@open-slide/core 0.0.14 → 0.0.16

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Yiwei Ho
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/dist/cli/bin.js CHANGED
File without changes
package/env.d.ts ADDED
@@ -0,0 +1,59 @@
1
+ // Ambient module declarations for assets imported from `slides/<id>/assets/`.
2
+ // Mirrors Vite's default asset handling (default export = resolved URL).
3
+ //
4
+ // Consumers opt in via tsconfig:
5
+ //
6
+ // { "compilerOptions": { "types": ["@open-slide/core/env"] } }
7
+
8
+ declare module '*.svg' {
9
+ const src: string;
10
+ export default src;
11
+ }
12
+ declare module '*.png' {
13
+ const src: string;
14
+ export default src;
15
+ }
16
+ declare module '*.jpg' {
17
+ const src: string;
18
+ export default src;
19
+ }
20
+ declare module '*.jpeg' {
21
+ const src: string;
22
+ export default src;
23
+ }
24
+ declare module '*.webp' {
25
+ const src: string;
26
+ export default src;
27
+ }
28
+ declare module '*.gif' {
29
+ const src: string;
30
+ export default src;
31
+ }
32
+ declare module '*.avif' {
33
+ const src: string;
34
+ export default src;
35
+ }
36
+ declare module '*.mp4' {
37
+ const src: string;
38
+ export default src;
39
+ }
40
+ declare module '*.webm' {
41
+ const src: string;
42
+ export default src;
43
+ }
44
+ declare module '*.woff' {
45
+ const src: string;
46
+ export default src;
47
+ }
48
+ declare module '*.woff2' {
49
+ const src: string;
50
+ export default src;
51
+ }
52
+ declare module '*.ttf' {
53
+ const src: string;
54
+ export default src;
55
+ }
56
+ declare module '*.otf' {
57
+ const src: string;
58
+ export default src;
59
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-slide/core",
3
- "version": "0.0.14",
3
+ "version": "0.0.16",
4
4
  "description": "Runtime and CLI for open-slide — write slides in slides/, we handle the rest.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -11,6 +11,9 @@
11
11
  "./vite": {
12
12
  "types": "./dist/vite/index.d.ts",
13
13
  "import": "./dist/vite/index.js"
14
+ },
15
+ "./env": {
16
+ "types": "./env.d.ts"
14
17
  }
15
18
  },
16
19
  "bin": {
@@ -19,15 +22,11 @@
19
22
  "files": [
20
23
  "bin.js",
21
24
  "dist",
25
+ "env.d.ts",
22
26
  "src/app",
23
27
  "skills",
24
28
  "README.md"
25
29
  ],
26
- "scripts": {
27
- "build": "tsdown",
28
- "typecheck": "tsc --noEmit",
29
- "prepack": "pnpm build"
30
- },
31
30
  "engines": {
32
31
  "node": ">=18"
33
32
  },
@@ -38,6 +37,11 @@
38
37
  "vite"
39
38
  ],
40
39
  "license": "MIT",
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "git+https://github.com/1weiho/open-slide.git",
43
+ "directory": "packages/core"
44
+ },
41
45
  "publishConfig": {
42
46
  "access": "public"
43
47
  },
@@ -72,5 +76,9 @@
72
76
  "@types/react-dom": "^18.3.1",
73
77
  "tsdown": "^0.9.9",
74
78
  "typescript": "^5.9.3"
79
+ },
80
+ "scripts": {
81
+ "build": "tsdown",
82
+ "typecheck": "tsc --noEmit"
75
83
  }
76
- }
84
+ }
@@ -72,64 +72,64 @@ export function PresentControlBar({
72
72
  >
73
73
  <TooltipProvider delayDuration={300}>
74
74
  <TooltipContainerCtx.Provider value={tooltipContainer ?? null}>
75
- <div className="pointer-events-auto flex h-11 items-center gap-1 rounded-full border border-white/10 bg-black/55 px-2 text-white/85 shadow-[0_8px_30px_-8px_oklch(0_0_0/0.6)] backdrop-blur-md">
76
- <BarButton label="Previous slide (←)" onClick={onPrev} disabled={index === 0}>
77
- <ChevronLeft className="size-4" />
78
- </BarButton>
79
- <BarButton label="Next slide (→)" onClick={onNext} disabled={index >= total - 1}>
80
- <ChevronRight className="size-4" />
81
- </BarButton>
75
+ <div className="pointer-events-auto flex h-11 items-center gap-1 rounded-full border border-white/10 bg-black/55 px-2 text-white/85 shadow-[0_8px_30px_-8px_oklch(0_0_0/0.6)] backdrop-blur-md">
76
+ <BarButton label="Previous slide (←)" onClick={onPrev} disabled={index === 0}>
77
+ <ChevronLeft className="size-4" />
78
+ </BarButton>
79
+ <BarButton label="Next slide (→)" onClick={onNext} disabled={index >= total - 1}>
80
+ <ChevronRight className="size-4" />
81
+ </BarButton>
82
82
 
83
- <Divider />
83
+ <Divider />
84
84
 
85
- <span className="px-2 font-mono text-[11.5px] tracking-[0.08em] tabular-nums uppercase select-none text-white/85">
86
- <span className="text-white">{(index + 1).toString().padStart(2, '0')}</span>
87
- <span className="text-white/35"> / </span>
88
- <span>{total.toString().padStart(2, '0')}</span>
89
- </span>
85
+ <span className="px-2 font-mono text-[11.5px] tracking-[0.08em] tabular-nums uppercase select-none text-white/85">
86
+ <span className="text-white">{(index + 1).toString().padStart(2, '0')}</span>
87
+ <span className="text-white/35"> / </span>
88
+ <span>{total.toString().padStart(2, '0')}</span>
89
+ </span>
90
90
 
91
- <Divider />
91
+ <Divider />
92
92
 
93
- <ElapsedClock startedAt={startedAt} />
93
+ <ElapsedClock startedAt={startedAt} />
94
94
 
95
- <Divider />
95
+ <Divider />
96
96
 
97
- <BarButton label="Slide overview (O)" onClick={onOverview}>
98
- <Grid2x2 className="size-4" />
99
- </BarButton>
100
- <BarButton
101
- label="Black screen (B)"
102
- onClick={() => onBlackout('black')}
103
- active={blackout === 'black'}
104
- >
105
- <Square className="size-4 fill-current" />
106
- </BarButton>
107
- <BarButton
108
- label="White screen (W)"
109
- onClick={() => onBlackout('white')}
110
- active={blackout === 'white'}
111
- >
112
- <Sun className="size-4" />
113
- </BarButton>
114
- <BarButton label="Laser pointer (L)" onClick={onLaser} active={laser}>
115
- <Crosshair className="size-4" />
116
- </BarButton>
117
- <BarButton label="Presenter view (P)" onClick={onPresenter}>
118
- <MonitorSpeaker className="size-4" />
119
- </BarButton>
120
- <BarButton label="Keyboard shortcuts (?)" onClick={onHelp}>
121
- <Keyboard className="size-4" />
122
- </BarButton>
97
+ <BarButton label="Slide overview (O)" onClick={onOverview}>
98
+ <Grid2x2 className="size-4" />
99
+ </BarButton>
100
+ <BarButton
101
+ label="Black screen (B)"
102
+ onClick={() => onBlackout('black')}
103
+ active={blackout === 'black'}
104
+ >
105
+ <Square className="size-4 fill-current" />
106
+ </BarButton>
107
+ <BarButton
108
+ label="White screen (W)"
109
+ onClick={() => onBlackout('white')}
110
+ active={blackout === 'white'}
111
+ >
112
+ <Sun className="size-4" />
113
+ </BarButton>
114
+ <BarButton label="Laser pointer (L)" onClick={onLaser} active={laser}>
115
+ <Crosshair className="size-4" />
116
+ </BarButton>
117
+ <BarButton label="Presenter view (P)" onClick={onPresenter}>
118
+ <MonitorSpeaker className="size-4" />
119
+ </BarButton>
120
+ <BarButton label="Keyboard shortcuts (?)" onClick={onHelp}>
121
+ <Keyboard className="size-4" />
122
+ </BarButton>
123
123
 
124
- {allowExit && (
125
- <>
126
- <Divider />
127
- <BarButton label="Exit (Esc)" onClick={onExit}>
128
- <LogOut className="size-4" />
129
- </BarButton>
130
- </>
131
- )}
132
- </div>
124
+ {allowExit && (
125
+ <>
126
+ <Divider />
127
+ <BarButton label="Exit (Esc)" onClick={onExit}>
128
+ <LogOut className="size-4" />
129
+ </BarButton>
130
+ </>
131
+ )}
132
+ </div>
133
133
  </TooltipContainerCtx.Provider>
134
134
  </TooltipProvider>
135
135
  </div>
@@ -22,14 +22,7 @@ type Props = {
22
22
  * so each preview is rendered with the slide's design tokens but with
23
23
  * motion frozen. Arrow keys move focus; Enter/click jumps and closes.
24
24
  */
25
- export function PresentOverviewGrid({
26
- pages,
27
- design,
28
- open,
29
- current,
30
- onClose,
31
- onSelect,
32
- }: Props) {
25
+ export function PresentOverviewGrid({ pages, design, open, current, onClose, onSelect }: Props) {
33
26
  const [focused, setFocused] = useState(current);
34
27
  const gridRef = useRef<HTMLDivElement>(null);
35
28
  const focusedRef = useRef<HTMLButtonElement | null>(null);
@@ -1,12 +1,4 @@
1
- import {
2
- FolderInput,
3
- FolderPlus,
4
- MoreHorizontal,
5
- Pencil,
6
- Search,
7
- Trash2,
8
- X,
9
- } from 'lucide-react';
1
+ import { FolderInput, FolderPlus, MoreHorizontal, Pencil, Search, Trash2, X } from 'lucide-react';
10
2
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
11
3
  import { Link, useSearchParams } from 'react-router-dom';
12
4
  import { Button } from '@/components/ui/button';
@@ -79,7 +71,9 @@ export function Home() {
79
71
  const [query, setQuery] = useState('');
80
72
  const [titleMap, setTitleMap] = useState<Record<string, string>>({});
81
73
  const reportTitle = useCallback((slideId: string, slideTitle: string) => {
82
- setTitleMap((prev) => (prev[slideId] === slideTitle ? prev : { ...prev, [slideId]: slideTitle }));
74
+ setTitleMap((prev) =>
75
+ prev[slideId] === slideTitle ? prev : { ...prev, [slideId]: slideTitle },
76
+ );
83
77
  }, []);
84
78
 
85
79
  const trimmedQuery = query.trim().toLowerCase();
@@ -152,7 +146,9 @@ export function Home() {
152
146
  .toString()
153
147
  .padStart(2, '0')}
154
148
  {isSearching && (
155
- <span className="opacity-40">/{visibleSlides.length.toString().padStart(2, '0')}</span>
149
+ <span className="opacity-40">
150
+ /{visibleSlides.length.toString().padStart(2, '0')}
151
+ </span>
156
152
  )}
157
153
  </span>
158
154
  <div className="ml-auto w-full md:w-auto">
@@ -219,13 +215,7 @@ function MobileFolderPill({
219
215
  );
220
216
  }
221
217
 
222
- function SearchInput({
223
- value,
224
- onChange,
225
- }: {
226
- value: string;
227
- onChange: (value: string) => void;
228
- }) {
218
+ function SearchInput({ value, onChange }: { value: string; onChange: (value: string) => void }) {
229
219
  return (
230
220
  <div className="relative w-full md:w-[240px]">
231
221
  <Search
@@ -262,8 +252,8 @@ function NoResultsState({ query, onClear }: { query: string; onClear: () => void
262
252
  </div>
263
253
  <p className="mt-4 font-heading text-[15px] font-semibold tracking-tight">No matches</p>
264
254
  <p className="mt-1.5 text-[13px] leading-relaxed text-muted-foreground">
265
- Nothing matches{' '}
266
- <span className="font-medium text-foreground">&ldquo;{query}&rdquo;</span> in this folder.
255
+ Nothing matches <span className="font-medium text-foreground">&ldquo;{query}&rdquo;</span>{' '}
256
+ in this folder.
267
257
  </p>
268
258
  <Button variant="ghost" size="sm" className="mt-4" onClick={onClear}>
269
259
  Clear search
@@ -413,10 +403,7 @@ function SlideCard({
413
403
  setDragging(true);
414
404
  }}
415
405
  onDragEnd={() => setDragging(false)}
416
- className={cn(
417
- 'group relative motion-safe:transition-opacity',
418
- dragging && 'opacity-40',
419
- )}
406
+ className={cn('group relative motion-safe:transition-opacity', dragging && 'opacity-40')}
420
407
  >
421
408
  <Link to={`/s/${id}`} className="block focus-visible:outline-none">
422
409
  {/* Slide thumb — tight border, grey baseboard, no shadcn rounded-xl */}
@@ -1,11 +1,4 @@
1
- import {
2
- ChevronLeft,
3
- ChevronRight,
4
- Loader2,
5
- RotateCcw,
6
- Square,
7
- Sun,
8
- } from 'lucide-react';
1
+ import { ChevronLeft, ChevronRight, Loader2, RotateCcw, Square, Sun } from 'lucide-react';
9
2
  import { useCallback, useEffect, useRef, useState } from 'react';
10
3
  import { useParams } from 'react-router-dom';
11
4
  import { Button } from '@/components/ui/button';
@@ -71,14 +64,8 @@ export function Presenter() {
71
64
  const goPrev = useCallback(() => send({ type: 'prev' }), [send]);
72
65
  const goNext = useCallback(() => send({ type: 'next' }), [send]);
73
66
  const goTo = useCallback((i: number) => send({ type: 'goto', index: i }), [send]);
74
- const toggleBlack = useCallback(
75
- () => send({ type: 'toggle-blackout', mode: 'black' }),
76
- [send],
77
- );
78
- const toggleWhite = useCallback(
79
- () => send({ type: 'toggle-blackout', mode: 'white' }),
80
- [send],
81
- );
67
+ const toggleBlack = useCallback(() => send({ type: 'toggle-blackout', mode: 'black' }), [send]);
68
+ const toggleWhite = useCallback(() => send({ type: 'toggle-blackout', mode: 'white' }), [send]);
82
69
 
83
70
  // Local-window key bindings mirror the projection's main shortcuts so the
84
71
  // presenter can drive without the mouse.
@@ -368,10 +355,7 @@ function Clock() {
368
355
  return () => clearInterval(id);
369
356
  }, []);
370
357
  return (
371
- <time
372
- title="Current time"
373
- className="font-mono text-[12px] tabular-nums text-white/55"
374
- >
358
+ <time title="Current time" className="font-mono text-[12px] tabular-nums text-white/55">
375
359
  {now.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
376
360
  </time>
377
361
  );
@@ -397,4 +381,3 @@ function ElapsedClock({ startedAt }: { startedAt: number }) {
397
381
  </time>
398
382
  );
399
383
  }
400
-