@open-slide/core 0.0.11 → 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.
- package/dist/{build-DHiRlpjn.js → build-aiY_8kwE.js} +2 -1
- package/dist/cli/bin.js +43 -4
- package/dist/{config-LZM903FE.js → config-CVqRAagl.js} +592 -63
- package/dist/design-CROQh0AA.js +35 -0
- package/dist/{dev-B3JzCYn7.js → dev-R2we2iaF.js} +2 -1
- package/dist/index.d.ts +55 -4
- package/dist/index.js +110 -1
- package/dist/{preview-UikovHEt.js → preview-CU4zSyGp.js} +2 -1
- package/dist/sync-3oqN1WyK.js +139 -0
- package/dist/sync-B4eLo2H6.js +3 -0
- package/dist/vite/index.d.ts +1 -1
- package/dist/vite/index.js +2 -1
- package/package.json +2 -1
- package/skills/apply-comments/SKILL.md +83 -0
- package/skills/create-slide/SKILL.md +81 -0
- package/skills/create-theme/SKILL.md +194 -0
- package/skills/slide-authoring/SKILL.md +288 -0
- package/src/app/{App.tsx → app.tsx} +8 -6
- package/src/app/components/{AssetView.tsx → asset-view.tsx} +41 -33
- package/src/app/components/{ClickNavZones.tsx → click-nav-zones.tsx} +1 -1
- package/src/app/components/history-provider.tsx +120 -0
- package/src/app/components/image-placeholder.tsx +121 -0
- package/src/app/components/inspector/{CommentWidget.tsx → comment-widget.tsx} +1 -1
- package/src/app/components/inspector/{InspectOverlay.tsx → inspect-overlay.tsx} +1 -1
- package/src/app/components/inspector/{InspectorPanel.tsx → inspector-panel.tsx} +164 -212
- package/src/app/components/inspector/{InspectorProvider.tsx → inspector-provider.tsx} +186 -18
- package/src/app/components/inspector/save-bar.tsx +47 -0
- package/src/app/components/panel/panel-fields.tsx +60 -0
- package/src/app/components/panel/panel-shell.tsx +78 -0
- package/src/app/components/panel/save-card.tsx +139 -0
- package/src/app/components/pdf-progress-toast.tsx +25 -0
- package/src/app/components/player.tsx +341 -0
- package/src/app/components/present/blackout-overlay.tsx +18 -0
- package/src/app/components/present/control-bar.tsx +204 -0
- package/src/app/components/present/help-overlay.tsx +56 -0
- package/src/app/components/present/jump-input.tsx +74 -0
- package/src/app/components/present/laser-pointer.tsx +40 -0
- package/src/app/components/present/overview-grid.tsx +184 -0
- package/src/app/components/present/progress-bar.tsx +26 -0
- package/src/app/components/present/use-idle.ts +44 -0
- package/src/app/components/present/use-pointer-near-bottom.ts +34 -0
- package/src/app/components/present/use-presenter-channel.ts +71 -0
- package/src/app/components/present/use-touch-swipe.ts +63 -0
- package/src/app/components/sidebar/{FolderItem.tsx → folder-item.tsx} +62 -27
- package/src/app/components/sidebar/{IconPicker.tsx → icon-picker.tsx} +13 -10
- package/src/app/components/sidebar/{Sidebar.tsx → sidebar.tsx} +40 -34
- package/src/app/components/{SlideCanvas.tsx → slide-canvas.tsx} +35 -10
- package/src/app/components/style-panel/design-provider.tsx +139 -0
- package/src/app/components/style-panel/style-panel.tsx +326 -0
- package/src/app/components/style-panel/use-design.ts +112 -0
- package/src/app/components/theme-toggle.tsx +57 -0
- package/src/app/components/thumbnail-rail.tsx +151 -0
- package/src/app/components/ui/button.tsx +51 -19
- package/src/app/components/ui/card.tsx +1 -1
- package/src/app/components/ui/dialog.tsx +25 -9
- package/src/app/components/ui/dropdown-menu.tsx +29 -12
- package/src/app/components/ui/input.tsx +13 -9
- package/src/app/components/ui/popover.tsx +5 -2
- package/src/app/components/ui/progress.tsx +2 -2
- package/src/app/components/ui/select.tsx +11 -5
- package/src/app/components/ui/separator.tsx +1 -1
- package/src/app/components/ui/slider.tsx +4 -4
- package/src/app/components/ui/sonner.tsx +11 -1
- package/src/app/components/ui/tabs.tsx +6 -6
- package/src/app/components/ui/textarea.tsx +11 -7
- package/src/app/components/ui/toggle-group.tsx +2 -2
- package/src/app/components/ui/toggle.tsx +6 -6
- package/src/app/components/ui/tooltip.tsx +5 -2
- package/src/app/lib/export-html.ts +10 -1
- package/src/app/lib/export-pdf.ts +7 -0
- package/src/app/lib/folders.ts +1 -1
- package/src/app/lib/inspector/{useEditor.ts → use-editor.ts} +2 -1
- package/src/app/lib/sdk.ts +5 -0
- package/src/app/lib/slides.ts +1 -1
- package/src/app/lib/utils.ts +1 -1
- package/src/app/main.tsx +5 -2
- package/src/app/routes/{Home.tsx → home.tsx} +266 -97
- package/src/app/routes/presenter.tsx +400 -0
- package/src/app/routes/slide.tsx +519 -0
- package/src/app/styles.css +338 -67
- package/src/app/components/PdfProgressToast.tsx +0 -23
- package/src/app/components/Player.tsx +0 -100
- package/src/app/components/ThumbnailRail.tsx +0 -68
- package/src/app/components/inspector/SaveBar.tsx +0 -77
- package/src/app/routes/Slide.tsx +0 -478
- /package/dist/{config-SXL5qIl6.d.ts → config-DweCbRkQ.d.ts} +0 -0
- /package/src/app/lib/inspector/{useComments.ts → use-comments.ts} +0 -0
- /package/src/app/lib/{useWheelPageNavigation.ts → use-wheel-page-navigation.ts} +0 -0
package/src/app/styles.css
CHANGED
|
@@ -6,8 +6,13 @@
|
|
|
6
6
|
@custom-variant dark (&:is(.dark *));
|
|
7
7
|
|
|
8
8
|
@theme inline {
|
|
9
|
-
--font-
|
|
10
|
-
--font-
|
|
9
|
+
--font-sans: "Geist Variable", -apple-system, BlinkMacSystemFont, sans-serif;
|
|
10
|
+
--font-heading: "Geist Variable", -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
|
|
11
|
+
--font-serif:
|
|
12
|
+
ui-serif, "New York", "Iowan Old Style", "Apple Garamond", "Big Caslon", "Charter", Georgia,
|
|
13
|
+
serif;
|
|
14
|
+
--font-mono: ui-monospace, "JetBrains Mono", "SF Mono", "Menlo", "Cascadia Code", monospace;
|
|
15
|
+
|
|
11
16
|
--color-sidebar-ring: var(--sidebar-ring);
|
|
12
17
|
--color-sidebar-border: var(--sidebar-border);
|
|
13
18
|
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
@@ -24,6 +29,7 @@
|
|
|
24
29
|
--color-ring: var(--ring);
|
|
25
30
|
--color-input: var(--input);
|
|
26
31
|
--color-border: var(--border);
|
|
32
|
+
--color-hairline: var(--hairline);
|
|
27
33
|
--color-destructive: var(--destructive);
|
|
28
34
|
--color-accent-foreground: var(--accent-foreground);
|
|
29
35
|
--color-accent: var(--accent);
|
|
@@ -37,94 +43,359 @@
|
|
|
37
43
|
--color-popover: var(--popover);
|
|
38
44
|
--color-card-foreground: var(--card-foreground);
|
|
39
45
|
--color-card: var(--card);
|
|
46
|
+
--color-canvas: var(--canvas);
|
|
40
47
|
--color-foreground: var(--foreground);
|
|
41
48
|
--color-background: var(--background);
|
|
49
|
+
--color-brand: var(--brand);
|
|
50
|
+
--color-brand-foreground: var(--brand-foreground);
|
|
51
|
+
--color-brand-soft: var(--brand-soft);
|
|
52
|
+
|
|
42
53
|
--radius-sm: calc(var(--radius) * 0.6);
|
|
43
|
-
--radius-md: calc(var(--radius) * 0.
|
|
54
|
+
--radius-md: calc(var(--radius) * 0.85);
|
|
44
55
|
--radius-lg: var(--radius);
|
|
45
56
|
--radius-xl: calc(var(--radius) * 1.4);
|
|
46
57
|
--radius-2xl: calc(var(--radius) * 1.8);
|
|
47
58
|
--radius-3xl: calc(var(--radius) * 2.2);
|
|
48
59
|
--radius-4xl: calc(var(--radius) * 2.6);
|
|
60
|
+
|
|
61
|
+
--shadow-edge: 0 0 0 0.5px oklch(0 0 0 / 0.06), 0 1px 0 oklch(0 0 0 / 0.025);
|
|
62
|
+
--shadow-floating:
|
|
63
|
+
0 0 0 0.5px oklch(0 0 0 / 0.08), 0 1px 1px oklch(0 0 0 / 0.04),
|
|
64
|
+
0 4px 16px -2px oklch(0 0 0 / 0.08);
|
|
65
|
+
--shadow-overlay:
|
|
66
|
+
0 0 0 0.5px oklch(0 0 0 / 0.1), 0 8px 28px -4px oklch(0 0 0 / 0.18),
|
|
67
|
+
0 24px 64px -12px oklch(0 0 0 / 0.2);
|
|
49
68
|
}
|
|
50
69
|
|
|
51
70
|
:root {
|
|
52
|
-
|
|
53
|
-
|
|
71
|
+
/* Warm paper, ink foreground. Not pure white / pure black — those read as
|
|
72
|
+
"default browser"; the slight warmth gives the surface a quiet voice. */
|
|
73
|
+
--background: oklch(0.985 0.004 75);
|
|
74
|
+
--foreground: oklch(0.2 0.012 60);
|
|
75
|
+
--canvas: oklch(0.974 0.005 75);
|
|
76
|
+
|
|
54
77
|
--card: oklch(1 0 0);
|
|
55
|
-
--card-foreground: oklch(0.
|
|
56
|
-
--popover: oklch(
|
|
57
|
-
--popover-foreground: oklch(0.
|
|
58
|
-
|
|
59
|
-
--primary
|
|
60
|
-
--
|
|
61
|
-
--secondary
|
|
62
|
-
--
|
|
63
|
-
--muted
|
|
64
|
-
--
|
|
65
|
-
--accent
|
|
66
|
-
--
|
|
67
|
-
--
|
|
68
|
-
|
|
69
|
-
--
|
|
70
|
-
--
|
|
71
|
-
--
|
|
72
|
-
--
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
--
|
|
77
|
-
--
|
|
78
|
-
--
|
|
79
|
-
|
|
80
|
-
--
|
|
81
|
-
--
|
|
82
|
-
--
|
|
83
|
-
--
|
|
78
|
+
--card-foreground: oklch(0.2 0.012 60);
|
|
79
|
+
--popover: oklch(0.998 0.002 75);
|
|
80
|
+
--popover-foreground: oklch(0.2 0.012 60);
|
|
81
|
+
|
|
82
|
+
--primary: oklch(0.215 0.012 60);
|
|
83
|
+
--primary-foreground: oklch(0.985 0.004 75);
|
|
84
|
+
--secondary: oklch(0.96 0.006 75);
|
|
85
|
+
--secondary-foreground: oklch(0.215 0.012 60);
|
|
86
|
+
--muted: oklch(0.955 0.008 75);
|
|
87
|
+
--muted-foreground: oklch(0.485 0.012 60);
|
|
88
|
+
--accent: oklch(0.945 0.012 75);
|
|
89
|
+
--accent-foreground: oklch(0.215 0.012 60);
|
|
90
|
+
--destructive: oklch(0.55 0.21 27);
|
|
91
|
+
|
|
92
|
+
--border: oklch(0.895 0.008 70);
|
|
93
|
+
--hairline: oklch(0.86 0.008 70);
|
|
94
|
+
--input: oklch(0.91 0.008 70);
|
|
95
|
+
--ring: oklch(0.55 0.18 28);
|
|
96
|
+
|
|
97
|
+
/* Vermillion — refined editorial red. Single accent across the app;
|
|
98
|
+
restraint is the point. */
|
|
99
|
+
--brand: oklch(0.555 0.185 28);
|
|
100
|
+
--brand-foreground: oklch(0.985 0.004 75);
|
|
101
|
+
--brand-soft: oklch(0.555 0.185 28 / 0.1);
|
|
102
|
+
|
|
103
|
+
--chart-1: oklch(0.555 0.185 28);
|
|
104
|
+
--chart-2: oklch(0.55 0.1 60);
|
|
105
|
+
--chart-3: oklch(0.45 0.06 245);
|
|
106
|
+
--chart-4: oklch(0.55 0.12 160);
|
|
107
|
+
--chart-5: oklch(0.4 0.04 60);
|
|
108
|
+
|
|
109
|
+
/* Tight, considered radius — drops the trendy 10px squish for 6px crisp. */
|
|
110
|
+
--radius: 0.375rem;
|
|
111
|
+
|
|
112
|
+
--sidebar: oklch(0.972 0.005 75);
|
|
113
|
+
--sidebar-foreground: oklch(0.215 0.012 60);
|
|
114
|
+
--sidebar-primary: oklch(0.215 0.012 60);
|
|
115
|
+
--sidebar-primary-foreground: oklch(0.985 0.004 75);
|
|
116
|
+
--sidebar-accent: oklch(0.94 0.01 75);
|
|
117
|
+
--sidebar-accent-foreground: oklch(0.215 0.012 60);
|
|
118
|
+
--sidebar-border: oklch(0.895 0.008 70);
|
|
119
|
+
--sidebar-ring: oklch(0.55 0.18 28);
|
|
84
120
|
}
|
|
85
121
|
|
|
86
122
|
.dark {
|
|
87
|
-
--background: oklch(0.
|
|
88
|
-
--foreground: oklch(0.
|
|
89
|
-
--
|
|
90
|
-
|
|
91
|
-
--
|
|
92
|
-
--
|
|
93
|
-
--
|
|
94
|
-
--
|
|
95
|
-
|
|
96
|
-
--
|
|
97
|
-
--
|
|
98
|
-
--
|
|
99
|
-
--
|
|
100
|
-
--
|
|
101
|
-
--
|
|
102
|
-
--
|
|
103
|
-
--
|
|
104
|
-
--
|
|
105
|
-
|
|
106
|
-
--
|
|
107
|
-
--
|
|
108
|
-
--
|
|
109
|
-
--
|
|
110
|
-
|
|
111
|
-
--
|
|
112
|
-
--
|
|
113
|
-
--
|
|
114
|
-
|
|
115
|
-
--
|
|
116
|
-
--
|
|
117
|
-
--
|
|
123
|
+
--background: oklch(0.155 0.005 70);
|
|
124
|
+
--foreground: oklch(0.945 0.005 80);
|
|
125
|
+
--canvas: oklch(0.135 0.005 70);
|
|
126
|
+
|
|
127
|
+
--card: oklch(0.195 0.006 70);
|
|
128
|
+
--card-foreground: oklch(0.945 0.005 80);
|
|
129
|
+
--popover: oklch(0.21 0.006 70);
|
|
130
|
+
--popover-foreground: oklch(0.945 0.005 80);
|
|
131
|
+
|
|
132
|
+
--primary: oklch(0.94 0.005 80);
|
|
133
|
+
--primary-foreground: oklch(0.18 0.008 70);
|
|
134
|
+
--secondary: oklch(0.245 0.006 70);
|
|
135
|
+
--secondary-foreground: oklch(0.945 0.005 80);
|
|
136
|
+
--muted: oklch(0.235 0.006 70);
|
|
137
|
+
--muted-foreground: oklch(0.68 0.008 75);
|
|
138
|
+
--accent: oklch(0.27 0.008 70);
|
|
139
|
+
--accent-foreground: oklch(0.945 0.005 80);
|
|
140
|
+
--destructive: oklch(0.68 0.2 25);
|
|
141
|
+
|
|
142
|
+
--border: oklch(1 0 0 / 0.1);
|
|
143
|
+
--hairline: oklch(1 0 0 / 0.06);
|
|
144
|
+
--input: oklch(1 0 0 / 0.13);
|
|
145
|
+
--ring: oklch(0.62 0.18 28);
|
|
146
|
+
|
|
147
|
+
--brand: oklch(0.66 0.19 28);
|
|
148
|
+
--brand-foreground: oklch(0.155 0.005 70);
|
|
149
|
+
--brand-soft: oklch(0.66 0.19 28 / 0.18);
|
|
150
|
+
|
|
151
|
+
--chart-1: oklch(0.66 0.19 28);
|
|
152
|
+
--chart-2: oklch(0.7 0.1 60);
|
|
153
|
+
--chart-3: oklch(0.65 0.08 245);
|
|
154
|
+
--chart-4: oklch(0.65 0.12 160);
|
|
155
|
+
--chart-5: oklch(0.6 0.04 60);
|
|
156
|
+
|
|
157
|
+
--sidebar: oklch(0.18 0.006 70);
|
|
158
|
+
--sidebar-foreground: oklch(0.945 0.005 80);
|
|
159
|
+
--sidebar-primary: oklch(0.94 0.005 80);
|
|
160
|
+
--sidebar-primary-foreground: oklch(0.18 0.008 70);
|
|
161
|
+
--sidebar-accent: oklch(0.265 0.008 70);
|
|
162
|
+
--sidebar-accent-foreground: oklch(0.945 0.005 80);
|
|
163
|
+
--sidebar-border: oklch(1 0 0 / 0.1);
|
|
164
|
+
--sidebar-ring: oklch(0.62 0.18 28);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/* Tabular numerics — pages, counts, hex values, slider readouts.
|
|
168
|
+
Declared as a Tailwind v4 `@utility` so it can be `@apply`d (e.g. by
|
|
169
|
+
.folio below) and used as a className token. */
|
|
170
|
+
@utility nums {
|
|
171
|
+
font-feature-settings:
|
|
172
|
+
"tnum" on,
|
|
173
|
+
"cv11" on;
|
|
174
|
+
font-variant-numeric: tabular-nums;
|
|
118
175
|
}
|
|
119
176
|
|
|
120
177
|
@layer base {
|
|
121
178
|
* {
|
|
122
179
|
@apply border-border outline-ring/50;
|
|
123
180
|
}
|
|
181
|
+
html {
|
|
182
|
+
@apply font-sans;
|
|
183
|
+
text-rendering: optimizeLegibility;
|
|
184
|
+
-webkit-font-smoothing: antialiased;
|
|
185
|
+
-moz-osx-font-smoothing: grayscale;
|
|
186
|
+
font-feature-settings:
|
|
187
|
+
"ss01" on,
|
|
188
|
+
"cv11" on,
|
|
189
|
+
"calt" on;
|
|
190
|
+
}
|
|
124
191
|
body {
|
|
125
192
|
@apply bg-background text-foreground;
|
|
193
|
+
font-feature-settings:
|
|
194
|
+
"ss01" on,
|
|
195
|
+
"cv11" on,
|
|
196
|
+
"calt" on,
|
|
197
|
+
"tnum" off;
|
|
198
|
+
letter-spacing: -0.005em;
|
|
126
199
|
}
|
|
127
|
-
|
|
128
|
-
|
|
200
|
+
::selection {
|
|
201
|
+
background: var(--brand-soft);
|
|
202
|
+
color: var(--foreground);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
@layer components {
|
|
207
|
+
/*
|
|
208
|
+
* Eyebrow label — the single typographic gesture used for *every* section
|
|
209
|
+
* label across the app. Tracked-out small caps in muted ink.
|
|
210
|
+
*/
|
|
211
|
+
.eyebrow {
|
|
212
|
+
@apply font-sans text-[10px] font-semibold uppercase text-muted-foreground;
|
|
213
|
+
letter-spacing: 0.14em;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/* Hairline divider — 0.5px on retina, snaps to 1px elsewhere. */
|
|
217
|
+
.hairline {
|
|
218
|
+
background: var(--hairline);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/* Subtle paper texture for "studio" surfaces (sidebar, gallery bg).
|
|
222
|
+
Pure-CSS noise via SVG; cheap, dark-mode aware via mix-blend. */
|
|
223
|
+
.paper {
|
|
224
|
+
position: relative;
|
|
225
|
+
isolation: isolate;
|
|
226
|
+
}
|
|
227
|
+
.paper::before {
|
|
228
|
+
content: "";
|
|
229
|
+
position: absolute;
|
|
230
|
+
inset: 0;
|
|
231
|
+
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' seed='3'/><feColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.55 0'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");
|
|
232
|
+
opacity: 0.025;
|
|
233
|
+
pointer-events: none;
|
|
234
|
+
z-index: 0;
|
|
235
|
+
mix-blend-mode: multiply;
|
|
236
|
+
}
|
|
237
|
+
.dark .paper::before {
|
|
238
|
+
opacity: 0.05;
|
|
239
|
+
mix-blend-mode: screen;
|
|
240
|
+
}
|
|
241
|
+
.paper > * {
|
|
242
|
+
position: relative;
|
|
243
|
+
z-index: 1;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/* Numeric folio-style page count. */
|
|
247
|
+
.folio {
|
|
248
|
+
@apply nums font-mono text-[10px] uppercase tracking-[0.16em] text-muted-foreground;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/*
|
|
253
|
+
* Comment input cue — kept from the original design but retoned to match
|
|
254
|
+
* the new palette (warm vermillion + amber instead of the AI-classic
|
|
255
|
+
* blue/violet halo). Plays once on first open, then settles.
|
|
256
|
+
*/
|
|
257
|
+
@property --comment-cue-angle {
|
|
258
|
+
syntax: "<angle>";
|
|
259
|
+
initial-value: 0deg;
|
|
260
|
+
inherits: false;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.comment-cue {
|
|
264
|
+
position: relative;
|
|
265
|
+
isolation: isolate;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
.comment-cue::before {
|
|
269
|
+
content: "";
|
|
270
|
+
position: absolute;
|
|
271
|
+
inset: -1px;
|
|
272
|
+
border-radius: calc(var(--radius-md) + 1px);
|
|
273
|
+
padding: 1.25px;
|
|
274
|
+
background: conic-gradient(
|
|
275
|
+
from var(--comment-cue-angle),
|
|
276
|
+
transparent 0deg,
|
|
277
|
+
transparent 250deg,
|
|
278
|
+
oklch(0.78 0.14 60 / 0.55) 295deg,
|
|
279
|
+
oklch(0.62 0.2 28 / 0.85) 325deg,
|
|
280
|
+
oklch(0.78 0.14 60 / 0.55) 350deg,
|
|
281
|
+
transparent 360deg
|
|
282
|
+
);
|
|
283
|
+
-webkit-mask:
|
|
284
|
+
linear-gradient(#000 0 0) content-box,
|
|
285
|
+
linear-gradient(#000 0 0);
|
|
286
|
+
-webkit-mask-composite: xor;
|
|
287
|
+
mask:
|
|
288
|
+
linear-gradient(#000 0 0) content-box,
|
|
289
|
+
linear-gradient(#000 0 0);
|
|
290
|
+
mask-composite: exclude;
|
|
291
|
+
pointer-events: none;
|
|
292
|
+
opacity: 0;
|
|
293
|
+
z-index: 1;
|
|
294
|
+
animation: comment-cue-trace 1500ms cubic-bezier(0.22, 0.61, 0.36, 1) 220ms 1 both;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
.comment-cue::after {
|
|
298
|
+
content: "";
|
|
299
|
+
position: absolute;
|
|
300
|
+
inset: -10px;
|
|
301
|
+
border-radius: calc(var(--radius-md) + 10px);
|
|
302
|
+
background:
|
|
303
|
+
radial-gradient(80% 60% at 30% 50%, oklch(0.62 0.2 28 / 0.18), transparent 70%),
|
|
304
|
+
radial-gradient(70% 60% at 75% 60%, oklch(0.78 0.14 60 / 0.16), transparent 70%);
|
|
305
|
+
filter: blur(14px);
|
|
306
|
+
opacity: 0;
|
|
307
|
+
z-index: -1;
|
|
308
|
+
pointer-events: none;
|
|
309
|
+
animation: comment-cue-halo 1700ms ease-out 180ms 1 both;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
@keyframes comment-cue-trace {
|
|
313
|
+
0% {
|
|
314
|
+
--comment-cue-angle: 0deg;
|
|
315
|
+
opacity: 0;
|
|
316
|
+
}
|
|
317
|
+
18% {
|
|
318
|
+
opacity: 1;
|
|
319
|
+
}
|
|
320
|
+
82% {
|
|
321
|
+
opacity: 1;
|
|
322
|
+
}
|
|
323
|
+
100% {
|
|
324
|
+
--comment-cue-angle: 360deg;
|
|
325
|
+
opacity: 0;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
@keyframes comment-cue-halo {
|
|
330
|
+
0% {
|
|
331
|
+
opacity: 0;
|
|
332
|
+
transform: scale(0.97);
|
|
333
|
+
}
|
|
334
|
+
40% {
|
|
335
|
+
opacity: 1;
|
|
336
|
+
transform: scale(1);
|
|
337
|
+
}
|
|
338
|
+
100% {
|
|
339
|
+
opacity: 0;
|
|
340
|
+
transform: scale(1.015);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
@media (prefers-reduced-motion: reduce) {
|
|
345
|
+
.comment-cue::before,
|
|
346
|
+
.comment-cue::after {
|
|
347
|
+
animation: none;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
[data-osd-freeze-motion],
|
|
352
|
+
[data-osd-freeze-motion] *,
|
|
353
|
+
[data-osd-freeze-motion] *::before,
|
|
354
|
+
[data-osd-freeze-motion] *::after {
|
|
355
|
+
/* biome-ignore lint/complexity/noImportantStyles: Thumbnail previews must override arbitrary slide-authored motion, including inline styles. */
|
|
356
|
+
animation-duration: 1ms !important;
|
|
357
|
+
/* biome-ignore lint/complexity/noImportantStyles: Thumbnail previews must override arbitrary slide-authored motion, including inline styles. */
|
|
358
|
+
animation-delay: 0s !important;
|
|
359
|
+
/* biome-ignore lint/complexity/noImportantStyles: Thumbnail previews must override arbitrary slide-authored motion, including inline styles. */
|
|
360
|
+
animation-iteration-count: 1 !important;
|
|
361
|
+
/* biome-ignore lint/complexity/noImportantStyles: Thumbnail previews must override arbitrary slide-authored motion, including inline styles. */
|
|
362
|
+
animation-fill-mode: forwards !important;
|
|
363
|
+
/* biome-ignore lint/complexity/noImportantStyles: Thumbnail previews must override arbitrary slide-authored motion, including inline styles. */
|
|
364
|
+
transition: none !important;
|
|
365
|
+
/* biome-ignore lint/complexity/noImportantStyles: Thumbnail previews must override arbitrary slide-authored motion, including inline styles. */
|
|
366
|
+
scroll-behavior: auto !important;
|
|
367
|
+
/* biome-ignore lint/complexity/noImportantStyles: Thumbnail previews must override arbitrary slide-authored motion, including inline styles. */
|
|
368
|
+
view-transition-name: none !important;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/*
|
|
372
|
+
* Custom scrollbar — slim, ink-on-paper. Removes the chunky default that
|
|
373
|
+
* fights the calm surfaces.
|
|
374
|
+
*/
|
|
375
|
+
@media (pointer: fine) {
|
|
376
|
+
*::-webkit-scrollbar {
|
|
377
|
+
width: 10px;
|
|
378
|
+
height: 10px;
|
|
379
|
+
}
|
|
380
|
+
*::-webkit-scrollbar-track {
|
|
381
|
+
background: transparent;
|
|
382
|
+
}
|
|
383
|
+
*::-webkit-scrollbar-thumb {
|
|
384
|
+
background: oklch(0 0 0 / 0.1);
|
|
385
|
+
border: 3px solid transparent;
|
|
386
|
+
background-clip: content-box;
|
|
387
|
+
border-radius: 999px;
|
|
388
|
+
}
|
|
389
|
+
*::-webkit-scrollbar-thumb:hover {
|
|
390
|
+
background: oklch(0 0 0 / 0.18);
|
|
391
|
+
background-clip: content-box;
|
|
392
|
+
}
|
|
393
|
+
.dark *::-webkit-scrollbar-thumb {
|
|
394
|
+
background: oklch(1 0 0 / 0.1);
|
|
395
|
+
background-clip: content-box;
|
|
396
|
+
}
|
|
397
|
+
.dark *::-webkit-scrollbar-thumb:hover {
|
|
398
|
+
background: oklch(1 0 0 / 0.18);
|
|
399
|
+
background-clip: content-box;
|
|
129
400
|
}
|
|
130
401
|
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { Loader2 } from 'lucide-react';
|
|
2
|
-
import type { PdfExportProgress } from '../lib/export-pdf';
|
|
3
|
-
import { Progress } from './ui/progress';
|
|
4
|
-
|
|
5
|
-
export function PdfProgressToast({ progress }: { progress: PdfExportProgress }) {
|
|
6
|
-
const text =
|
|
7
|
-
progress.phase === 'processing'
|
|
8
|
-
? `Processing slide ${progress.current} / ${progress.total}`
|
|
9
|
-
: progress.phase === 'printing'
|
|
10
|
-
? 'Opening print dialog…'
|
|
11
|
-
: 'Done';
|
|
12
|
-
|
|
13
|
-
return (
|
|
14
|
-
<div className="flex w-80 items-start gap-3 rounded-md border bg-popover px-4 py-3 text-popover-foreground shadow-lg">
|
|
15
|
-
<Loader2 className="mt-0.5 size-4 shrink-0 animate-spin text-primary" />
|
|
16
|
-
<div className="min-w-0 flex-1">
|
|
17
|
-
<p className="text-sm font-medium">Exporting PDF</p>
|
|
18
|
-
<p className="truncate text-xs text-muted-foreground">{text}</p>
|
|
19
|
-
<Progress value={Math.round(progress.percent)} className="mt-2 h-1.5" />
|
|
20
|
-
</div>
|
|
21
|
-
</div>
|
|
22
|
-
);
|
|
23
|
-
}
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import { useCallback, useEffect, useRef } from 'react';
|
|
2
|
-
import { useWheelPageNavigation } from '@/lib/useWheelPageNavigation';
|
|
3
|
-
import type { Page } from '../lib/sdk';
|
|
4
|
-
import { SlideCanvas } from './SlideCanvas';
|
|
5
|
-
|
|
6
|
-
type Props = {
|
|
7
|
-
pages: Page[];
|
|
8
|
-
index: number;
|
|
9
|
-
onIndexChange: (index: number) => void;
|
|
10
|
-
onExit: () => void;
|
|
11
|
-
allowExit?: boolean;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
export function Player({ pages, index, onIndexChange, onExit, allowExit = true }: Props) {
|
|
15
|
-
const rootRef = useRef<HTMLDivElement>(null);
|
|
16
|
-
const goPrev = useCallback(() => {
|
|
17
|
-
if (index > 0) onIndexChange(index - 1);
|
|
18
|
-
}, [index, onIndexChange]);
|
|
19
|
-
const goNext = useCallback(() => {
|
|
20
|
-
if (index < pages.length - 1) onIndexChange(index + 1);
|
|
21
|
-
}, [index, pages.length, onIndexChange]);
|
|
22
|
-
|
|
23
|
-
useWheelPageNavigation({
|
|
24
|
-
ref: rootRef,
|
|
25
|
-
canPrev: index > 0,
|
|
26
|
-
canNext: index < pages.length - 1,
|
|
27
|
-
onPrev: goPrev,
|
|
28
|
-
onNext: goNext,
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
useEffect(() => {
|
|
32
|
-
const el = rootRef.current;
|
|
33
|
-
if (!el) return;
|
|
34
|
-
if (document.fullscreenElement !== el) {
|
|
35
|
-
el.requestFullscreen?.().catch(() => {});
|
|
36
|
-
}
|
|
37
|
-
return () => {
|
|
38
|
-
if (document.fullscreenElement) document.exitFullscreen?.().catch(() => {});
|
|
39
|
-
};
|
|
40
|
-
}, []);
|
|
41
|
-
|
|
42
|
-
useEffect(() => {
|
|
43
|
-
if (!allowExit) return;
|
|
44
|
-
const onFsChange = () => {
|
|
45
|
-
if (!document.fullscreenElement) onExit();
|
|
46
|
-
};
|
|
47
|
-
document.addEventListener('fullscreenchange', onFsChange);
|
|
48
|
-
return () => document.removeEventListener('fullscreenchange', onFsChange);
|
|
49
|
-
}, [onExit, allowExit]);
|
|
50
|
-
|
|
51
|
-
useEffect(() => {
|
|
52
|
-
const onKey = (e: KeyboardEvent) => {
|
|
53
|
-
if (
|
|
54
|
-
e.key === 'ArrowRight' ||
|
|
55
|
-
e.key === 'ArrowDown' ||
|
|
56
|
-
e.key === ' ' ||
|
|
57
|
-
e.key === 'PageDown'
|
|
58
|
-
) {
|
|
59
|
-
e.preventDefault();
|
|
60
|
-
if (index < pages.length - 1) onIndexChange(index + 1);
|
|
61
|
-
} else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp' || e.key === 'PageUp') {
|
|
62
|
-
e.preventDefault();
|
|
63
|
-
if (index > 0) onIndexChange(index - 1);
|
|
64
|
-
} else if (e.key === 'Escape') {
|
|
65
|
-
if (allowExit) onExit();
|
|
66
|
-
} else if (e.key === 'Home') {
|
|
67
|
-
onIndexChange(0);
|
|
68
|
-
} else if (e.key === 'End') {
|
|
69
|
-
onIndexChange(pages.length - 1);
|
|
70
|
-
}
|
|
71
|
-
};
|
|
72
|
-
window.addEventListener('keydown', onKey);
|
|
73
|
-
return () => window.removeEventListener('keydown', onKey);
|
|
74
|
-
}, [index, pages.length, onIndexChange, onExit, allowExit]);
|
|
75
|
-
|
|
76
|
-
const PageComp = pages[index];
|
|
77
|
-
|
|
78
|
-
return (
|
|
79
|
-
<div
|
|
80
|
-
ref={rootRef}
|
|
81
|
-
className="relative flex h-screen w-screen items-center justify-center bg-black"
|
|
82
|
-
>
|
|
83
|
-
<SlideCanvas flat>{PageComp ? <PageComp /> : null}</SlideCanvas>
|
|
84
|
-
<button
|
|
85
|
-
type="button"
|
|
86
|
-
aria-label="Previous page"
|
|
87
|
-
onClick={goPrev}
|
|
88
|
-
disabled={index === 0}
|
|
89
|
-
className="absolute inset-y-0 left-0 z-10 w-[30%]"
|
|
90
|
-
/>
|
|
91
|
-
<button
|
|
92
|
-
type="button"
|
|
93
|
-
aria-label="Next page"
|
|
94
|
-
onClick={goNext}
|
|
95
|
-
disabled={index === pages.length - 1}
|
|
96
|
-
className="absolute inset-y-0 right-0 z-10 w-[30%]"
|
|
97
|
-
/>
|
|
98
|
-
</div>
|
|
99
|
-
);
|
|
100
|
-
}
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import { useEffect, useRef } from 'react';
|
|
2
|
-
import { ScrollArea } from '@/components/ui/scroll-area';
|
|
3
|
-
import { cn } from '@/lib/utils';
|
|
4
|
-
import type { Page } from '../lib/sdk';
|
|
5
|
-
import { CANVAS_HEIGHT, CANVAS_WIDTH } from '../lib/sdk';
|
|
6
|
-
import { SlideCanvas } from './SlideCanvas';
|
|
7
|
-
|
|
8
|
-
type Props = {
|
|
9
|
-
pages: Page[];
|
|
10
|
-
current: number;
|
|
11
|
-
onSelect: (index: number) => void;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
const THUMB_WIDTH = 200;
|
|
15
|
-
const THUMB_SCALE = THUMB_WIDTH / CANVAS_WIDTH;
|
|
16
|
-
const THUMB_HEIGHT = CANVAS_HEIGHT * THUMB_SCALE;
|
|
17
|
-
|
|
18
|
-
export function ThumbnailRail({ pages, current, onSelect }: Props) {
|
|
19
|
-
const activeRef = useRef<HTMLButtonElement | null>(null);
|
|
20
|
-
|
|
21
|
-
// biome-ignore lint/correctness/useExhaustiveDependencies: `current` triggers re-scroll on selection change
|
|
22
|
-
useEffect(() => {
|
|
23
|
-
activeRef.current?.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
|
|
24
|
-
}, [current]);
|
|
25
|
-
|
|
26
|
-
return (
|
|
27
|
-
<ScrollArea className="h-full border-r bg-card">
|
|
28
|
-
<aside className="flex flex-col gap-2.5 p-3">
|
|
29
|
-
{pages.map((PageComp, i) => {
|
|
30
|
-
const active = i === current;
|
|
31
|
-
return (
|
|
32
|
-
<button
|
|
33
|
-
// biome-ignore lint/suspicious/noArrayIndexKey: pages list is render-stable
|
|
34
|
-
key={i}
|
|
35
|
-
type="button"
|
|
36
|
-
ref={active ? activeRef : undefined}
|
|
37
|
-
onClick={() => onSelect(i)}
|
|
38
|
-
aria-label={`Go to page ${i + 1}`}
|
|
39
|
-
aria-current={active ? 'true' : undefined}
|
|
40
|
-
className={cn(
|
|
41
|
-
'flex items-center gap-2.5 rounded-lg border-2 border-transparent p-1.5 text-left transition-colors',
|
|
42
|
-
'hover:bg-muted',
|
|
43
|
-
active && 'border-primary bg-primary/5',
|
|
44
|
-
)}
|
|
45
|
-
>
|
|
46
|
-
<span
|
|
47
|
-
className={cn(
|
|
48
|
-
'w-5 shrink-0 text-right text-xs tabular-nums text-muted-foreground',
|
|
49
|
-
active && 'font-semibold text-primary',
|
|
50
|
-
)}
|
|
51
|
-
>
|
|
52
|
-
{i + 1}
|
|
53
|
-
</span>
|
|
54
|
-
<div
|
|
55
|
-
className="relative shrink-0 overflow-hidden rounded border bg-white shadow-sm"
|
|
56
|
-
style={{ width: THUMB_WIDTH, height: THUMB_HEIGHT }}
|
|
57
|
-
>
|
|
58
|
-
<SlideCanvas scale={THUMB_SCALE} center={false} flat>
|
|
59
|
-
<PageComp />
|
|
60
|
-
</SlideCanvas>
|
|
61
|
-
</div>
|
|
62
|
-
</button>
|
|
63
|
-
);
|
|
64
|
-
})}
|
|
65
|
-
</aside>
|
|
66
|
-
</ScrollArea>
|
|
67
|
-
);
|
|
68
|
-
}
|