@open-slide/core 0.0.10 → 0.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/dist/{build-DHiRlpjn.js → build-aiY_8kwE.js} +2 -1
  2. package/dist/cli/bin.js +43 -4
  3. package/dist/{config-LZM903FE.js → config-CVqRAagl.js} +592 -63
  4. package/dist/design-CROQh0AA.js +35 -0
  5. package/dist/{dev-B3JzCYn7.js → dev-R2we2iaF.js} +2 -1
  6. package/dist/index.d.ts +55 -4
  7. package/dist/index.js +110 -1
  8. package/dist/{preview-UikovHEt.js → preview-CU4zSyGp.js} +2 -1
  9. package/dist/sync-3oqN1WyK.js +139 -0
  10. package/dist/sync-B4eLo2H6.js +3 -0
  11. package/dist/vite/index.d.ts +1 -1
  12. package/dist/vite/index.js +2 -1
  13. package/package.json +2 -1
  14. package/skills/apply-comments/SKILL.md +83 -0
  15. package/skills/create-slide/SKILL.md +81 -0
  16. package/skills/create-theme/SKILL.md +194 -0
  17. package/skills/slide-authoring/SKILL.md +288 -0
  18. package/src/app/{App.tsx → app.tsx} +8 -6
  19. package/src/app/components/{AssetView.tsx → asset-view.tsx} +41 -33
  20. package/src/app/components/{ClickNavZones.tsx → click-nav-zones.tsx} +1 -1
  21. package/src/app/components/history-provider.tsx +120 -0
  22. package/src/app/components/image-placeholder.tsx +121 -0
  23. package/src/app/components/inspector/{CommentWidget.tsx → comment-widget.tsx} +1 -1
  24. package/src/app/components/inspector/{InspectOverlay.tsx → inspect-overlay.tsx} +1 -1
  25. package/src/app/components/inspector/{InspectorPanel.tsx → inspector-panel.tsx} +164 -212
  26. package/src/app/components/inspector/{InspectorProvider.tsx → inspector-provider.tsx} +186 -18
  27. package/src/app/components/inspector/save-bar.tsx +47 -0
  28. package/src/app/components/panel/panel-fields.tsx +60 -0
  29. package/src/app/components/panel/panel-shell.tsx +78 -0
  30. package/src/app/components/panel/save-card.tsx +139 -0
  31. package/src/app/components/pdf-progress-toast.tsx +25 -0
  32. package/src/app/components/player.tsx +341 -0
  33. package/src/app/components/present/blackout-overlay.tsx +18 -0
  34. package/src/app/components/present/control-bar.tsx +204 -0
  35. package/src/app/components/present/help-overlay.tsx +56 -0
  36. package/src/app/components/present/jump-input.tsx +74 -0
  37. package/src/app/components/present/laser-pointer.tsx +40 -0
  38. package/src/app/components/present/overview-grid.tsx +184 -0
  39. package/src/app/components/present/progress-bar.tsx +26 -0
  40. package/src/app/components/present/use-idle.ts +44 -0
  41. package/src/app/components/present/use-pointer-near-bottom.ts +34 -0
  42. package/src/app/components/present/use-presenter-channel.ts +71 -0
  43. package/src/app/components/present/use-touch-swipe.ts +63 -0
  44. package/src/app/components/sidebar/{FolderItem.tsx → folder-item.tsx} +62 -27
  45. package/src/app/components/sidebar/{IconPicker.tsx → icon-picker.tsx} +13 -10
  46. package/src/app/components/sidebar/{Sidebar.tsx → sidebar.tsx} +40 -34
  47. package/src/app/components/{SlideCanvas.tsx → slide-canvas.tsx} +35 -10
  48. package/src/app/components/style-panel/design-provider.tsx +139 -0
  49. package/src/app/components/style-panel/style-panel.tsx +326 -0
  50. package/src/app/components/style-panel/use-design.ts +112 -0
  51. package/src/app/components/theme-toggle.tsx +57 -0
  52. package/src/app/components/thumbnail-rail.tsx +151 -0
  53. package/src/app/components/ui/button.tsx +51 -19
  54. package/src/app/components/ui/card.tsx +1 -1
  55. package/src/app/components/ui/dialog.tsx +25 -9
  56. package/src/app/components/ui/dropdown-menu.tsx +29 -12
  57. package/src/app/components/ui/input.tsx +13 -9
  58. package/src/app/components/ui/popover.tsx +5 -2
  59. package/src/app/components/ui/progress.tsx +2 -2
  60. package/src/app/components/ui/select.tsx +11 -5
  61. package/src/app/components/ui/separator.tsx +1 -1
  62. package/src/app/components/ui/slider.tsx +4 -4
  63. package/src/app/components/ui/sonner.tsx +11 -1
  64. package/src/app/components/ui/tabs.tsx +6 -6
  65. package/src/app/components/ui/textarea.tsx +11 -7
  66. package/src/app/components/ui/toggle-group.tsx +2 -2
  67. package/src/app/components/ui/toggle.tsx +6 -6
  68. package/src/app/components/ui/tooltip.tsx +5 -2
  69. package/src/app/lib/export-html.ts +10 -1
  70. package/src/app/lib/export-pdf.ts +7 -0
  71. package/src/app/lib/folders.ts +1 -1
  72. package/src/app/lib/inspector/{useEditor.ts → use-editor.ts} +2 -1
  73. package/src/app/lib/sdk.ts +5 -0
  74. package/src/app/lib/slides.ts +1 -1
  75. package/src/app/lib/utils.ts +1 -1
  76. package/src/app/main.tsx +5 -2
  77. package/src/app/routes/{Home.tsx → home.tsx} +266 -97
  78. package/src/app/routes/presenter.tsx +400 -0
  79. package/src/app/routes/slide.tsx +519 -0
  80. package/src/app/styles.css +338 -67
  81. package/src/app/components/PdfProgressToast.tsx +0 -23
  82. package/src/app/components/Player.tsx +0 -100
  83. package/src/app/components/ThumbnailRail.tsx +0 -68
  84. package/src/app/components/inspector/SaveBar.tsx +0 -77
  85. package/src/app/routes/Slide.tsx +0 -478
  86. /package/dist/{config-SXL5qIl6.d.ts → config-DweCbRkQ.d.ts} +0 -0
  87. /package/src/app/lib/inspector/{useComments.ts → use-comments.ts} +0 -0
  88. /package/src/app/lib/{useWheelPageNavigation.ts → use-wheel-page-navigation.ts} +0 -0
@@ -129,39 +129,44 @@ export function AssetView({ slideId }: Props) {
129
129
  }
130
130
  }}
131
131
  >
132
- <div className="flex shrink-0 items-center justify-between gap-3 border-b bg-card px-6 py-3">
132
+ <div className="flex shrink-0 items-center justify-between gap-3 border-b border-hairline bg-sidebar px-6 py-3">
133
133
  <div className="min-w-0">
134
- <h2 className="truncate text-base font-semibold tracking-tight">Assets</h2>
135
- <p className="truncate text-xs text-muted-foreground">
136
- <span className="font-mono">slides/{slideId}/assets/</span>
134
+ <span className="eyebrow">Assets</span>
135
+ <p className="mt-0.5 truncate text-[12px] text-muted-foreground">
136
+ <span className="font-mono text-[11.5px]">slides/{slideId}/assets/</span>
137
137
  {!loading && (
138
138
  <>
139
- <span className="mx-1.5">·</span>
140
- <span>{assets.length === 1 ? '1 file' : `${assets.length} files`}</span>
139
+ <span className="mx-2 opacity-50">·</span>
140
+ <span className="folio">
141
+ {assets.length.toString().padStart(2, '0')}
142
+ <span className="opacity-40"> </span>
143
+ {assets.length === 1 ? 'file' : 'files'}
144
+ </span>
141
145
  </>
142
146
  )}
143
147
  </p>
144
148
  </div>
145
- <div className="flex shrink-0 items-center gap-2">
149
+ <div className="flex shrink-0 items-center gap-1.5">
146
150
  <button
147
151
  type="button"
148
152
  onClick={() => setLogoSearchOpen(true)}
149
153
  className={cn(
150
- 'inline-flex h-8 cursor-pointer items-center gap-1.5 rounded-lg border bg-background px-3 text-sm font-medium transition-colors',
151
- 'hover:bg-muted active:translate-y-px',
154
+ 'inline-flex h-8 cursor-pointer items-center gap-1.5 rounded-[5px] border border-border bg-card px-2.5 text-[12.5px] font-medium transition-colors',
155
+ 'hover:bg-muted/60 hover:border-foreground/20 active:translate-y-px',
152
156
  )}
153
157
  >
154
- <Search className="size-4" />
158
+ <Search className="size-3.5" />
155
159
  <span>Search logos</span>
156
160
  </button>
157
161
  <label
158
162
  htmlFor={inputId}
159
163
  className={cn(
160
- 'inline-flex h-8 cursor-pointer items-center gap-1.5 rounded-lg bg-primary px-3 text-sm font-medium text-primary-foreground transition-opacity',
161
- 'hover:opacity-90 active:translate-y-px',
164
+ 'inline-flex h-8 cursor-pointer items-center gap-1.5 rounded-[5px] bg-foreground px-3 text-[12.5px] font-medium text-background transition-colors',
165
+ 'shadow-[inset_0_1px_0_oklch(1_0_0/0.12),0_1px_0_oklch(0_0_0/0.12)]',
166
+ 'hover:bg-foreground/90 active:translate-y-px',
162
167
  )}
163
168
  >
164
- <Upload className="size-4" />
169
+ <Upload className="size-3.5" />
165
170
  <span>Upload</span>
166
171
  </label>
167
172
  <input
@@ -231,11 +236,11 @@ export function AssetView({ slideId }: Props) {
231
236
  className="pointer-events-none absolute inset-0 z-30 animate-in fade-in-0 duration-200"
232
237
  aria-hidden="true"
233
238
  >
234
- <div className="absolute inset-0 bg-foreground/[0.03]" />
235
- <div className="absolute inset-2 rounded-xl border border-dashed border-foreground/25" />
239
+ <div className="absolute inset-0 bg-brand/5" />
240
+ <div className="absolute inset-2 rounded-[10px] border border-dashed border-brand/40" />
236
241
  <div className="absolute inset-x-0 bottom-8 flex justify-center">
237
- <div className="flex animate-in items-center gap-2 rounded-full border bg-background px-3.5 py-1.5 text-xs font-medium shadow-sm fade-in-0 slide-in-from-bottom-1 duration-300">
238
- <ArrowDownToLine className="size-3.5 text-muted-foreground" />
242
+ <div className="flex animate-in items-center gap-2 rounded-[6px] border border-border bg-card px-3 py-1.5 text-[12px] font-medium shadow-floating fade-in-0 slide-in-from-bottom-1 duration-300">
243
+ <ArrowDownToLine className="size-3.5 text-brand" />
239
244
  <span>Drop to upload</span>
240
245
  </div>
241
246
  </div>
@@ -280,14 +285,15 @@ export function AssetView({ slideId }: Props) {
280
285
 
281
286
  function EmptyState() {
282
287
  return (
283
- <div className="flex h-full flex-col items-center justify-center gap-3 px-6 py-16 text-center">
284
- <div className="flex size-14 items-center justify-center rounded-full bg-muted">
285
- <ImageIcon className="size-6 text-muted-foreground" />
288
+ <div className="flex h-full flex-col items-center justify-center gap-4 px-6 py-16 text-center">
289
+ <div className="flex size-12 items-center justify-center rounded-full border border-hairline bg-card text-muted-foreground">
290
+ <ImageIcon className="size-5" />
286
291
  </div>
287
292
  <div>
288
- <p className="text-sm font-medium">No assets yet</p>
289
- <p className="mt-1 text-xs text-muted-foreground">
290
- Drop files anywhere here or click Upload to add them.
293
+ <p className="font-heading text-[14px] font-semibold tracking-tight">No assets yet</p>
294
+ <p className="mt-1 max-w-xs text-[12.5px] leading-relaxed text-muted-foreground">
295
+ Drop files anywhere here, or use <span className="font-mono text-foreground">Upload</span>
296
+ .
291
297
  </p>
292
298
  </div>
293
299
  </div>
@@ -329,12 +335,12 @@ function AssetCard({
329
335
  }) {
330
336
  const isImage = asset.mime.startsWith('image/');
331
337
  return (
332
- <div className="group relative flex flex-col overflow-hidden rounded-xl border bg-card shadow-sm transition-shadow hover:shadow-md focus-within:ring-2 focus-within:ring-ring/50">
338
+ <div className="group relative flex flex-col overflow-hidden rounded-[6px] border border-border bg-card shadow-edge transition-shadow hover:shadow-floating focus-within:ring-2 focus-within:ring-ring/30">
333
339
  <button
334
340
  type="button"
335
341
  onClick={onPreview}
336
342
  aria-label={`Preview ${asset.name}`}
337
- className="relative flex aspect-square w-full items-center justify-center overflow-hidden bg-[repeating-conic-gradient(theme(colors.muted)_0_25%,transparent_0_50%)] bg-[length:16px_16px]"
343
+ className="relative flex aspect-square w-full items-center justify-center overflow-hidden border-b border-hairline bg-[repeating-conic-gradient(theme(colors.muted)_0_25%,transparent_0_50%)] bg-[length:14px_14px]"
338
344
  >
339
345
  {isImage ? (
340
346
  <img
@@ -347,16 +353,16 @@ function AssetCard({
347
353
  }}
348
354
  />
349
355
  ) : (
350
- <FileIcon className="size-10 text-muted-foreground" />
356
+ <FileIcon className="size-9 text-muted-foreground" />
351
357
  )}
352
358
  </button>
353
359
 
354
- <div className="flex items-center gap-1 border-t bg-card px-3 py-2">
360
+ <div className="flex items-center gap-1 px-2.5 py-2">
355
361
  <div className="min-w-0 flex-1">
356
- <div className="truncate text-sm font-medium" title={asset.name}>
362
+ <div className="truncate text-[12.5px] font-medium" title={asset.name}>
357
363
  {asset.name}
358
364
  </div>
359
- <div className="truncate text-[11px] text-muted-foreground">{formatSize(asset.size)}</div>
365
+ <div className="folio truncate">{formatSize(asset.size)}</div>
360
366
  </div>
361
367
  <DropdownMenu>
362
368
  <DropdownMenuTrigger
@@ -549,8 +555,10 @@ function PreviewDialog({ asset, onClose }: { asset: AssetEntry; onClose: () => v
549
555
  <span className="text-sm">No preview available</span>
550
556
  </div>
551
557
  )}
552
- <div className="rounded-md border bg-muted/40 p-2 font-mono text-xs">
553
- import asset from '<span className="font-semibold">{importPath}</span>';
558
+ <div className="rounded-[5px] border border-hairline bg-muted/50 px-3 py-2 font-mono text-[11.5px] leading-relaxed">
559
+ <span className="text-muted-foreground">import asset from </span>
560
+ <span className="text-brand">'{importPath}'</span>
561
+ <span className="text-muted-foreground">;</span>
554
562
  </div>
555
563
  </DialogContent>
556
564
  </Dialog>
@@ -621,13 +629,13 @@ function LogoSearchDialog({
621
629
  </DialogHeader>
622
630
 
623
631
  <div className="relative">
624
- <Search className="pointer-events-none absolute left-2.5 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" />
632
+ <Search className="pointer-events-none absolute left-2.5 top-1/2 size-3.5 -translate-y-1/2 text-muted-foreground" />
625
633
  <input
626
634
  ref={inputRef}
627
635
  value={query}
628
636
  onChange={(e) => setQuery(e.target.value)}
629
637
  placeholder="Search by brand…"
630
- className="w-full rounded-md border bg-background py-2 pl-8 pr-3 text-sm outline-none ring-ring/40 focus:ring-2"
638
+ className="h-9 w-full rounded-[6px] border border-border bg-background py-2 pl-8 pr-3 text-[13px] outline-none focus-visible:border-foreground/40 focus-visible:ring-2 focus-visible:ring-ring/30"
631
639
  />
632
640
  </div>
633
641
 
@@ -1,4 +1,4 @@
1
- import { useInspector } from './inspector/InspectorProvider';
1
+ import { useInspector } from './inspector/inspector-provider';
2
2
 
3
3
  type Props = {
4
4
  onPrev: () => void;
@@ -0,0 +1,120 @@
1
+ import {
2
+ createContext,
3
+ type ReactNode,
4
+ useCallback,
5
+ useContext,
6
+ useMemo,
7
+ useRef,
8
+ useState,
9
+ } from 'react';
10
+
11
+ export type HistoryEntry = {
12
+ undo: () => void;
13
+ redo: () => void;
14
+ coalesceKey?: string;
15
+ ts: number;
16
+ };
17
+
18
+ type HistoryCtx = {
19
+ canUndo: boolean;
20
+ canRedo: boolean;
21
+ record: (entry: Omit<HistoryEntry, 'ts'>) => void;
22
+ undo: () => void;
23
+ redo: () => void;
24
+ clear: () => void;
25
+ isSuppressed: () => boolean;
26
+ };
27
+
28
+ const COALESCE_WINDOW_MS = 500;
29
+
30
+ const Ctx = createContext<HistoryCtx | null>(null);
31
+
32
+ export function useHistory(): HistoryCtx {
33
+ const v = useContext(Ctx);
34
+ if (!v) throw new Error('useHistory must be used inside <HistoryProvider>');
35
+ return v;
36
+ }
37
+
38
+ export function HistoryProvider({ children }: { children: ReactNode }) {
39
+ const [past, setPast] = useState<HistoryEntry[]>([]);
40
+ const [future, setFuture] = useState<HistoryEntry[]>([]);
41
+ // Set while invoking an entry's undo/redo so providers can skip
42
+ // re-recording the resulting state mutation.
43
+ const suppressedRef = useRef(false);
44
+
45
+ const record = useCallback((entry: Omit<HistoryEntry, 'ts'>) => {
46
+ if (suppressedRef.current) return;
47
+ const ts = Date.now();
48
+ setPast((prev) => {
49
+ const top = prev.at(-1);
50
+ if (
51
+ top &&
52
+ entry.coalesceKey !== undefined &&
53
+ top.coalesceKey === entry.coalesceKey &&
54
+ ts - top.ts < COALESCE_WINDOW_MS
55
+ ) {
56
+ const merged: HistoryEntry = {
57
+ undo: top.undo,
58
+ redo: entry.redo,
59
+ coalesceKey: entry.coalesceKey,
60
+ ts,
61
+ };
62
+ return [...prev.slice(0, -1), merged];
63
+ }
64
+ return [...prev, { ...entry, ts }];
65
+ });
66
+ setFuture([]);
67
+ }, []);
68
+
69
+ const undo = useCallback(() => {
70
+ setPast((prev) => {
71
+ const top = prev.at(-1);
72
+ if (!top) return prev;
73
+ suppressedRef.current = true;
74
+ try {
75
+ top.undo();
76
+ } finally {
77
+ suppressedRef.current = false;
78
+ }
79
+ setFuture((f) => [...f, top]);
80
+ return prev.slice(0, -1);
81
+ });
82
+ }, []);
83
+
84
+ const redo = useCallback(() => {
85
+ setFuture((prev) => {
86
+ const top = prev.at(-1);
87
+ if (!top) return prev;
88
+ suppressedRef.current = true;
89
+ try {
90
+ top.redo();
91
+ } finally {
92
+ suppressedRef.current = false;
93
+ }
94
+ setPast((p) => [...p, top]);
95
+ return prev.slice(0, -1);
96
+ });
97
+ }, []);
98
+
99
+ const clear = useCallback(() => {
100
+ setPast([]);
101
+ setFuture([]);
102
+ }, []);
103
+
104
+ const isSuppressed = useCallback(() => suppressedRef.current, []);
105
+
106
+ const value = useMemo<HistoryCtx>(
107
+ () => ({
108
+ canUndo: past.length > 0,
109
+ canRedo: future.length > 0,
110
+ record,
111
+ undo,
112
+ redo,
113
+ clear,
114
+ isSuppressed,
115
+ }),
116
+ [past.length, future.length, record, undo, redo, clear, isSuppressed],
117
+ );
118
+
119
+ return <Ctx.Provider value={value}>{children}</Ctx.Provider>;
120
+ }
@@ -0,0 +1,121 @@
1
+ import type { CSSProperties, HTMLAttributes } from 'react';
2
+
3
+ export type ImagePlaceholderProps = {
4
+ hint: string;
5
+ width?: number;
6
+ height?: number;
7
+ style?: CSSProperties;
8
+ className?: string;
9
+ } & Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'style' | 'className'>;
10
+
11
+ export function ImagePlaceholder({
12
+ hint,
13
+ width,
14
+ height,
15
+ style,
16
+ className,
17
+ ...rest
18
+ }: ImagePlaceholderProps) {
19
+ const dims = width && height ? `${width} × ${height}` : null;
20
+ return (
21
+ <div
22
+ {...rest}
23
+ data-slide-placeholder={hint}
24
+ data-placeholder-w={width}
25
+ data-placeholder-h={height}
26
+ role="img"
27
+ aria-label={hint}
28
+ style={{
29
+ position: 'relative',
30
+ width: width ?? '100%',
31
+ height: height ?? '100%',
32
+ display: 'flex',
33
+ alignItems: 'center',
34
+ justifyContent: 'center',
35
+ flexDirection: 'column',
36
+ gap: 14,
37
+ border: '1px dashed rgba(120, 120, 130, 0.35)',
38
+ borderRadius: 12,
39
+ background:
40
+ 'linear-gradient(135deg, rgba(120,120,130,0.06) 0%, rgba(120,120,130,0.02) 50%, rgba(120,120,130,0.06) 100%)',
41
+ color: 'rgba(90, 90, 100, 0.7)',
42
+ fontFamily: '-apple-system, BlinkMacSystemFont, "Inter", "Segoe UI", system-ui, sans-serif',
43
+ textAlign: 'center',
44
+ padding: 24,
45
+ boxSizing: 'border-box',
46
+ overflow: 'hidden',
47
+ ...style,
48
+ }}
49
+ className={className}
50
+ >
51
+ <PlaceholderIcon />
52
+ <div
53
+ style={{
54
+ display: 'flex',
55
+ flexDirection: 'column',
56
+ alignItems: 'center',
57
+ gap: 6,
58
+ maxWidth: '85%',
59
+ }}
60
+ >
61
+ <span
62
+ style={{
63
+ fontSize: 11,
64
+ fontWeight: 600,
65
+ letterSpacing: '0.14em',
66
+ textTransform: 'uppercase',
67
+ opacity: 0.55,
68
+ }}
69
+ >
70
+ Image
71
+ </span>
72
+ <span
73
+ style={{
74
+ fontSize: 16,
75
+ fontWeight: 500,
76
+ lineHeight: 1.4,
77
+ color: 'rgba(60, 60, 70, 0.85)',
78
+ }}
79
+ >
80
+ {hint}
81
+ </span>
82
+ {dims && (
83
+ <span
84
+ style={{
85
+ fontSize: 11,
86
+ fontVariantNumeric: 'tabular-nums',
87
+ fontFamily: 'ui-monospace, "SF Mono", Menlo, Consolas, monospace',
88
+ opacity: 0.5,
89
+ marginTop: 2,
90
+ }}
91
+ >
92
+ {dims}
93
+ </span>
94
+ )}
95
+ </div>
96
+ </div>
97
+ );
98
+ }
99
+
100
+ function PlaceholderIcon() {
101
+ return (
102
+ <svg
103
+ width="32"
104
+ height="32"
105
+ viewBox="0 0 32 32"
106
+ fill="none"
107
+ stroke="currentColor"
108
+ strokeWidth="1.5"
109
+ strokeLinecap="round"
110
+ strokeLinejoin="round"
111
+ style={{ opacity: 0.55 }}
112
+ role="img"
113
+ aria-label="image placeholder"
114
+ >
115
+ <title>image placeholder</title>
116
+ <rect x="4" y="6" width="24" height="20" rx="2.5" />
117
+ <circle cx="11" cy="13" r="2" />
118
+ <path d="M4 22l7-7 6 6 4-4 7 7" />
119
+ </svg>
120
+ );
121
+ }
@@ -1,6 +1,6 @@
1
1
  import { MessageSquare, Trash2, X } from 'lucide-react';
2
2
  import { useState } from 'react';
3
- import { useInspector } from './InspectorProvider';
3
+ import { useInspector } from './inspector-provider';
4
4
 
5
5
  export function CommentWidget() {
6
6
  const { comments, remove, error } = useInspector();
@@ -1,6 +1,6 @@
1
1
  import { useEffect, useLayoutEffect, useRef, useState } from 'react';
2
2
  import { findSlideSource, type SlideSourceHit } from '@/lib/inspector/fiber';
3
- import { useInspector } from './InspectorProvider';
3
+ import { useInspector } from './inspector-provider';
4
4
 
5
5
  type Highlight = { rect: DOMRect; hit: SlideSourceHit };
6
6