@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
|
@@ -1,21 +1,25 @@
|
|
|
1
|
-
import * as React from
|
|
1
|
+
import * as React from 'react';
|
|
2
2
|
|
|
3
|
-
import { cn } from
|
|
3
|
+
import { cn } from '@/lib/utils';
|
|
4
4
|
|
|
5
|
-
function Input({ className, type, ...props }: React.ComponentProps<
|
|
5
|
+
function Input({ className, type, ...props }: React.ComponentProps<'input'>) {
|
|
6
6
|
return (
|
|
7
7
|
<input
|
|
8
8
|
type={type}
|
|
9
9
|
data-slot="input"
|
|
10
10
|
className={cn(
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
'h-8 w-full min-w-0 rounded-[5px] border border-border bg-background px-2.5 text-[13px] outline-none',
|
|
12
|
+
'transition-colors selection:bg-brand-soft selection:text-foreground',
|
|
13
|
+
'placeholder:text-muted-foreground/70',
|
|
14
|
+
'focus-visible:border-foreground/40 focus-visible:ring-2 focus-visible:ring-ring/30',
|
|
15
|
+
'disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50',
|
|
16
|
+
'aria-invalid:border-destructive aria-invalid:ring-2 aria-invalid:ring-destructive/25',
|
|
17
|
+
'file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground',
|
|
18
|
+
className,
|
|
15
19
|
)}
|
|
16
20
|
{...props}
|
|
17
21
|
/>
|
|
18
|
-
)
|
|
22
|
+
);
|
|
19
23
|
}
|
|
20
24
|
|
|
21
|
-
export { Input }
|
|
25
|
+
export { Input };
|
|
@@ -14,7 +14,7 @@ function PopoverTrigger({ ...props }: React.ComponentProps<typeof PopoverPrimiti
|
|
|
14
14
|
function PopoverContent({
|
|
15
15
|
className,
|
|
16
16
|
align = 'center',
|
|
17
|
-
sideOffset =
|
|
17
|
+
sideOffset = 6,
|
|
18
18
|
...props
|
|
19
19
|
}: React.ComponentProps<typeof PopoverPrimitive.Content>) {
|
|
20
20
|
return (
|
|
@@ -24,7 +24,10 @@ function PopoverContent({
|
|
|
24
24
|
align={align}
|
|
25
25
|
sideOffset={sideOffset}
|
|
26
26
|
className={cn(
|
|
27
|
-
'z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-
|
|
27
|
+
'z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-[8px] border border-border bg-popover p-3 text-popover-foreground shadow-floating outline-hidden',
|
|
28
|
+
'data-[side=bottom]:slide-in-from-top-1 data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1 data-[side=top]:slide-in-from-bottom-1',
|
|
29
|
+
'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95',
|
|
30
|
+
'data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95',
|
|
28
31
|
className,
|
|
29
32
|
)}
|
|
30
33
|
{...props}
|
|
@@ -14,14 +14,14 @@ function Progress({
|
|
|
14
14
|
<ProgressPrimitive.Root
|
|
15
15
|
data-slot="progress"
|
|
16
16
|
className={cn(
|
|
17
|
-
"relative h-
|
|
17
|
+
"relative h-[3px] w-full overflow-hidden rounded-full bg-muted",
|
|
18
18
|
className
|
|
19
19
|
)}
|
|
20
20
|
{...props}
|
|
21
21
|
>
|
|
22
22
|
<ProgressPrimitive.Indicator
|
|
23
23
|
data-slot="progress-indicator"
|
|
24
|
-
className="h-full w-full flex-1 bg-
|
|
24
|
+
className="h-full w-full flex-1 bg-brand transition-all"
|
|
25
25
|
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
|
|
26
26
|
/>
|
|
27
27
|
</ProgressPrimitive.Root>
|
|
@@ -37,14 +37,20 @@ function SelectTrigger({
|
|
|
37
37
|
data-slot="select-trigger"
|
|
38
38
|
data-size={size}
|
|
39
39
|
className={cn(
|
|
40
|
-
"flex w-fit items-center justify-between gap-2 rounded-
|
|
40
|
+
"flex w-fit items-center justify-between gap-2 rounded-[5px] border border-border bg-background px-2.5 text-[13px] whitespace-nowrap outline-none transition-colors",
|
|
41
|
+
"hover:border-foreground/25 focus-visible:border-foreground/40 focus-visible:ring-2 focus-visible:ring-ring/30",
|
|
42
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
43
|
+
"aria-invalid:border-destructive aria-invalid:ring-2 aria-invalid:ring-destructive/25",
|
|
44
|
+
"data-[placeholder]:text-muted-foreground/70 data-[size=default]:h-8 data-[size=sm]:h-7",
|
|
45
|
+
"*:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2",
|
|
46
|
+
"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5 [&_svg:not([class*='text-'])]:text-muted-foreground",
|
|
41
47
|
className
|
|
42
48
|
)}
|
|
43
49
|
{...props}
|
|
44
50
|
>
|
|
45
51
|
{children}
|
|
46
52
|
<SelectPrimitive.Icon asChild>
|
|
47
|
-
<ChevronDownIcon className="size-
|
|
53
|
+
<ChevronDownIcon className="size-3.5 opacity-50" />
|
|
48
54
|
</SelectPrimitive.Icon>
|
|
49
55
|
</SelectPrimitive.Trigger>
|
|
50
56
|
)
|
|
@@ -62,7 +68,7 @@ function SelectContent({
|
|
|
62
68
|
<SelectPrimitive.Content
|
|
63
69
|
data-slot="select-content"
|
|
64
70
|
className={cn(
|
|
65
|
-
"relative z-50 max-h-(--radix-select-content-available-height) min-w-[
|
|
71
|
+
"relative z-50 max-h-(--radix-select-content-available-height) min-w-[9rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-[8px] border border-border bg-popover text-popover-foreground shadow-floating data-[side=bottom]:slide-in-from-top-1 data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1 data-[side=top]:slide-in-from-bottom-1 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
|
|
66
72
|
position === "popper" &&
|
|
67
73
|
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
|
68
74
|
className
|
|
@@ -109,7 +115,7 @@ function SelectItem({
|
|
|
109
115
|
<SelectPrimitive.Item
|
|
110
116
|
data-slot="select-item"
|
|
111
117
|
className={cn(
|
|
112
|
-
"relative flex w-full cursor-default items-center gap-2 rounded-
|
|
118
|
+
"relative flex w-full cursor-default items-center gap-2 rounded-[5px] py-1.5 pr-8 pl-2 text-[12.5px] outline-hidden select-none focus:bg-foreground focus:text-background data-[disabled]:pointer-events-none data-[disabled]:opacity-45 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5 [&_svg:not([class*='text-'])]:text-current *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
|
|
113
119
|
className
|
|
114
120
|
)}
|
|
115
121
|
{...props}
|
|
@@ -119,7 +125,7 @@ function SelectItem({
|
|
|
119
125
|
className="absolute right-2 flex size-3.5 items-center justify-center"
|
|
120
126
|
>
|
|
121
127
|
<SelectPrimitive.ItemIndicator>
|
|
122
|
-
<CheckIcon className="size-
|
|
128
|
+
<CheckIcon className="size-3.5" />
|
|
123
129
|
</SelectPrimitive.ItemIndicator>
|
|
124
130
|
</span>
|
|
125
131
|
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
|
@@ -17,7 +17,7 @@ function Separator({
|
|
|
17
17
|
decorative={decorative}
|
|
18
18
|
orientation={orientation}
|
|
19
19
|
className={cn(
|
|
20
|
-
'shrink-0 bg-
|
|
20
|
+
'shrink-0 bg-hairline data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch',
|
|
21
21
|
className,
|
|
22
22
|
)}
|
|
23
23
|
{...props}
|
|
@@ -29,7 +29,7 @@ function Slider({
|
|
|
29
29
|
min={min}
|
|
30
30
|
max={max}
|
|
31
31
|
className={cn(
|
|
32
|
-
"relative flex w-full touch-none items-center select-none data-[disabled]:opacity-50 data-[orientation=vertical]:h-full data-[orientation=vertical]:min-h-44 data-[orientation=vertical]:w-auto data-[orientation=vertical]:flex-col",
|
|
32
|
+
"group/slider relative flex w-full touch-none items-center select-none data-[disabled]:opacity-50 data-[orientation=vertical]:h-full data-[orientation=vertical]:min-h-44 data-[orientation=vertical]:w-auto data-[orientation=vertical]:flex-col",
|
|
33
33
|
className
|
|
34
34
|
)}
|
|
35
35
|
{...props}
|
|
@@ -37,13 +37,13 @@ function Slider({
|
|
|
37
37
|
<SliderPrimitive.Track
|
|
38
38
|
data-slot="slider-track"
|
|
39
39
|
className={cn(
|
|
40
|
-
"relative grow overflow-hidden rounded-full bg-muted data-[orientation=horizontal]:h-
|
|
40
|
+
"relative grow overflow-hidden rounded-full bg-muted data-[orientation=horizontal]:h-[3px] data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-[3px]"
|
|
41
41
|
)}
|
|
42
42
|
>
|
|
43
43
|
<SliderPrimitive.Range
|
|
44
44
|
data-slot="slider-range"
|
|
45
45
|
className={cn(
|
|
46
|
-
"absolute bg-
|
|
46
|
+
"absolute bg-foreground data-[orientation=horizontal]:h-full data-[orientation=vertical]:w-full"
|
|
47
47
|
)}
|
|
48
48
|
/>
|
|
49
49
|
</SliderPrimitive.Track>
|
|
@@ -51,7 +51,7 @@ function Slider({
|
|
|
51
51
|
<SliderPrimitive.Thumb
|
|
52
52
|
data-slot="slider-thumb"
|
|
53
53
|
key={index}
|
|
54
|
-
className="block size-
|
|
54
|
+
className="block size-3.5 shrink-0 rounded-full border border-foreground bg-card shadow-edge transition-transform hover:scale-110 focus-visible:scale-110 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring/50 disabled:pointer-events-none disabled:opacity-50"
|
|
55
55
|
/>
|
|
56
56
|
))}
|
|
57
57
|
</SliderPrimitive.Root>
|
|
@@ -27,9 +27,19 @@ const Toaster = ({ ...props }: ToasterProps) => {
|
|
|
27
27
|
"--normal-bg": "var(--popover)",
|
|
28
28
|
"--normal-text": "var(--popover-foreground)",
|
|
29
29
|
"--normal-border": "var(--border)",
|
|
30
|
-
"--border-radius": "
|
|
30
|
+
"--border-radius": "8px",
|
|
31
|
+
"--font-family":
|
|
32
|
+
"Geist Variable, -apple-system, BlinkMacSystemFont, sans-serif",
|
|
31
33
|
} as React.CSSProperties
|
|
32
34
|
}
|
|
35
|
+
toastOptions={{
|
|
36
|
+
classNames: {
|
|
37
|
+
toast:
|
|
38
|
+
"!font-sans !text-[12.5px] !shadow-floating !border-border !rounded-[8px]",
|
|
39
|
+
title: "!font-medium !text-[12.5px] !tracking-tight",
|
|
40
|
+
description: "!text-[12px] !text-muted-foreground",
|
|
41
|
+
},
|
|
42
|
+
}}
|
|
33
43
|
{...props}
|
|
34
44
|
/>
|
|
35
45
|
)
|
|
@@ -21,11 +21,11 @@ function Tabs({
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
const tabsListVariants = cva(
|
|
24
|
-
'group/tabs-list inline-flex w-fit items-center justify-center rounded-
|
|
24
|
+
'group/tabs-list inline-flex w-fit items-center justify-center rounded-[6px] p-[2px] text-muted-foreground group-data-[orientation=horizontal]/tabs:h-7 group-data-[orientation=vertical]/tabs:h-fit group-data-[orientation=vertical]/tabs:flex-col data-[variant=line]:rounded-none',
|
|
25
25
|
{
|
|
26
26
|
variants: {
|
|
27
27
|
variant: {
|
|
28
|
-
default: 'bg-muted',
|
|
28
|
+
default: 'bg-muted/70 ring-1 ring-inset ring-border/60',
|
|
29
29
|
line: 'gap-1 bg-transparent',
|
|
30
30
|
},
|
|
31
31
|
},
|
|
@@ -55,10 +55,10 @@ function TabsTrigger({ className, ...props }: React.ComponentProps<typeof TabsPr
|
|
|
55
55
|
<TabsPrimitive.Trigger
|
|
56
56
|
data-slot="tabs-trigger"
|
|
57
57
|
className={cn(
|
|
58
|
-
"relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-
|
|
59
|
-
'group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent
|
|
60
|
-
'data-[state=active]:bg-
|
|
61
|
-
'after:absolute after:bg-
|
|
58
|
+
"relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-[5px] border border-transparent px-2.5 text-[12px] font-medium whitespace-nowrap text-foreground/55 transition-colors group-data-[orientation=vertical]/tabs:w-full group-data-[orientation=vertical]/tabs:justify-start hover:text-foreground focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-ring/50 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5",
|
|
59
|
+
'group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent',
|
|
60
|
+
'data-[state=active]:bg-card data-[state=active]:text-foreground data-[state=active]:shadow-edge dark:data-[state=active]:bg-foreground/10',
|
|
61
|
+
'after:absolute after:bg-brand after:opacity-0 after:transition-opacity group-data-[orientation=horizontal]/tabs:after:inset-x-0 group-data-[orientation=horizontal]/tabs:after:-bottom-[6px] group-data-[orientation=horizontal]/tabs:after:h-[2px] group-data-[orientation=vertical]/tabs:after:inset-y-0 group-data-[orientation=vertical]/tabs:after:-right-1 group-data-[orientation=vertical]/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-[state=active]:after:opacity-100',
|
|
62
62
|
className,
|
|
63
63
|
)}
|
|
64
64
|
{...props}
|
|
@@ -1,18 +1,22 @@
|
|
|
1
|
-
import * as React from
|
|
1
|
+
import * as React from 'react';
|
|
2
2
|
|
|
3
|
-
import { cn } from
|
|
3
|
+
import { cn } from '@/lib/utils';
|
|
4
4
|
|
|
5
|
-
function Textarea({ className, ...props }: React.ComponentProps<
|
|
5
|
+
function Textarea({ className, ...props }: React.ComponentProps<'textarea'>) {
|
|
6
6
|
return (
|
|
7
7
|
<textarea
|
|
8
8
|
data-slot="textarea"
|
|
9
9
|
className={cn(
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
'flex field-sizing-content min-h-16 w-full rounded-[6px] border border-border bg-background px-2.5 py-2 text-[13px] leading-relaxed outline-none',
|
|
11
|
+
'transition-colors placeholder:text-muted-foreground/70',
|
|
12
|
+
'focus-visible:border-foreground/40 focus-visible:ring-2 focus-visible:ring-ring/30',
|
|
13
|
+
'disabled:cursor-not-allowed disabled:opacity-50',
|
|
14
|
+
'aria-invalid:border-destructive aria-invalid:ring-2 aria-invalid:ring-destructive/25',
|
|
15
|
+
className,
|
|
12
16
|
)}
|
|
13
17
|
{...props}
|
|
14
18
|
/>
|
|
15
|
-
)
|
|
19
|
+
);
|
|
16
20
|
}
|
|
17
21
|
|
|
18
|
-
export { Textarea }
|
|
22
|
+
export { Textarea };
|
|
@@ -36,7 +36,7 @@ function ToggleGroup({
|
|
|
36
36
|
data-spacing={spacing}
|
|
37
37
|
style={{ "--gap": spacing } as React.CSSProperties}
|
|
38
38
|
className={cn(
|
|
39
|
-
"group/toggle-group flex w-fit items-center gap-[--spacing(var(--gap))] rounded-
|
|
39
|
+
"group/toggle-group flex w-fit items-center gap-[--spacing(var(--gap))] rounded-[5px]",
|
|
40
40
|
className
|
|
41
41
|
)}
|
|
42
42
|
{...props}
|
|
@@ -70,7 +70,7 @@ function ToggleGroupItem({
|
|
|
70
70
|
size: context.size || size,
|
|
71
71
|
}),
|
|
72
72
|
"w-auto min-w-0 shrink-0 px-3 focus:z-10 focus-visible:z-10",
|
|
73
|
-
"data-[spacing=0]:rounded-none data-[spacing=0]:shadow-none data-[spacing=0]:first:rounded-l-
|
|
73
|
+
"data-[spacing=0]:rounded-none data-[spacing=0]:shadow-none data-[spacing=0]:first:rounded-l-[5px] data-[spacing=0]:last:rounded-r-[5px] data-[spacing=0]:data-[variant=outline]:border-l-0 data-[spacing=0]:data-[variant=outline]:first:border-l",
|
|
74
74
|
className
|
|
75
75
|
)}
|
|
76
76
|
{...props}
|
|
@@ -5,18 +5,18 @@ import { Toggle as TogglePrimitive } from "radix-ui"
|
|
|
5
5
|
import { cn } from "@/lib/utils"
|
|
6
6
|
|
|
7
7
|
const toggleVariants = cva(
|
|
8
|
-
"inline-flex items-center justify-center gap-2 rounded-
|
|
8
|
+
"inline-flex items-center justify-center gap-2 rounded-[5px] text-[12px] font-medium whitespace-nowrap outline-none transition-colors hover:bg-muted hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring/30 disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-foreground data-[state=on]:text-background [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5",
|
|
9
9
|
{
|
|
10
10
|
variants: {
|
|
11
11
|
variant: {
|
|
12
|
-
default: "bg-transparent",
|
|
12
|
+
default: "bg-transparent text-foreground/70",
|
|
13
13
|
outline:
|
|
14
|
-
"border border-
|
|
14
|
+
"border border-border bg-card text-foreground/75 hover:border-foreground/20 data-[state=on]:border-foreground",
|
|
15
15
|
},
|
|
16
16
|
size: {
|
|
17
|
-
default: "h-
|
|
18
|
-
sm: "h-
|
|
19
|
-
lg: "h-
|
|
17
|
+
default: "h-8 min-w-8 px-2",
|
|
18
|
+
sm: "h-7 min-w-7 px-1.5",
|
|
19
|
+
lg: "h-9 min-w-9 px-2.5",
|
|
20
20
|
},
|
|
21
21
|
},
|
|
22
22
|
defaultVariants: {
|
|
@@ -32,10 +32,13 @@ function TooltipContent({
|
|
|
32
32
|
className,
|
|
33
33
|
sideOffset = 0,
|
|
34
34
|
children,
|
|
35
|
+
container,
|
|
35
36
|
...props
|
|
36
|
-
}: React.ComponentProps<typeof TooltipPrimitive.Content>
|
|
37
|
+
}: React.ComponentProps<typeof TooltipPrimitive.Content> & {
|
|
38
|
+
container?: React.ComponentProps<typeof TooltipPrimitive.Portal>['container']
|
|
39
|
+
}) {
|
|
37
40
|
return (
|
|
38
|
-
<TooltipPrimitive.Portal>
|
|
41
|
+
<TooltipPrimitive.Portal container={container}>
|
|
39
42
|
<TooltipPrimitive.Content
|
|
40
43
|
data-slot="tooltip-content"
|
|
41
44
|
sideOffset={sideOffset}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { createElement } from 'react';
|
|
7
7
|
import { createRoot } from 'react-dom/client';
|
|
8
|
+
import { designToCssVars } from '../../design';
|
|
8
9
|
import type { SlideModule } from './sdk';
|
|
9
10
|
|
|
10
11
|
type AssetEntry = { name: string; bytes: Uint8Array };
|
|
@@ -51,6 +52,7 @@ export async function exportSlideAsHtml(slide: SlideModule, slideId: string): Pr
|
|
|
51
52
|
pagesHtml: rewrittenPages,
|
|
52
53
|
bundledCss: rewrittenCss,
|
|
53
54
|
externalLinks,
|
|
55
|
+
design: slide.design,
|
|
54
56
|
});
|
|
55
57
|
|
|
56
58
|
const htmlBytes = new TextEncoder().encode(html);
|
|
@@ -237,6 +239,7 @@ function buildHtml(opts: {
|
|
|
237
239
|
pagesHtml: string[];
|
|
238
240
|
bundledCss: string;
|
|
239
241
|
externalLinks: string;
|
|
242
|
+
design: SlideModule['design'];
|
|
240
243
|
}): string {
|
|
241
244
|
const pagesMarkup = opts.pagesHtml
|
|
242
245
|
.map(
|
|
@@ -244,6 +247,12 @@ function buildHtml(opts: {
|
|
|
244
247
|
)
|
|
245
248
|
.join('');
|
|
246
249
|
|
|
250
|
+
const frameStyle = opts.design
|
|
251
|
+
? Object.entries(designToCssVars(opts.design))
|
|
252
|
+
.map(([k, v]) => `${k}: ${v};`)
|
|
253
|
+
.join(' ')
|
|
254
|
+
: '';
|
|
255
|
+
|
|
247
256
|
return `<!doctype html>
|
|
248
257
|
<html lang="en">
|
|
249
258
|
<head>
|
|
@@ -262,7 +271,7 @@ html, body { margin: 0; height: 100%; background: #000; overflow: hidden; font-f
|
|
|
262
271
|
<style>${opts.bundledCss}</style>
|
|
263
272
|
</head>
|
|
264
273
|
<body>
|
|
265
|
-
<div class="os-stage"><div class="os-frame" id="os-frame">${pagesMarkup}</div></div>
|
|
274
|
+
<div class="os-stage"><div class="os-frame" id="os-frame" data-osd-canvas${frameStyle ? ` style="${escapeAttr(frameStyle)}"` : ''}>${pagesMarkup}</div></div>
|
|
266
275
|
<div class="os-counter"><span id="os-cur">1</span> / <span id="os-total">${opts.pagesHtml.length}</span></div>
|
|
267
276
|
<script>
|
|
268
277
|
(function () {
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { createElement } from 'react';
|
|
7
7
|
import { createRoot, type Root } from 'react-dom/client';
|
|
8
|
+
import { designToCssVars } from '../../design';
|
|
8
9
|
import { isFrameAnimationSettled, waitForDataWaitfor, waitForFonts } from './print-ready';
|
|
9
10
|
import type { SlideModule } from './sdk';
|
|
10
11
|
|
|
@@ -103,13 +104,19 @@ export async function exportSlideAsPdf(
|
|
|
103
104
|
|
|
104
105
|
onProgress?.({ phase: 'processing', current: 0, total, percent: 0 });
|
|
105
106
|
|
|
107
|
+
const designVars = slide.design ? designToCssVars(slide.design) : null;
|
|
108
|
+
|
|
106
109
|
const reactRoots: Root[] = [];
|
|
107
110
|
const frames: HTMLElement[] = [];
|
|
108
111
|
for (const Page of pages) {
|
|
109
112
|
const host = document.createElement('div');
|
|
110
113
|
host.className = 'os-print-frame';
|
|
114
|
+
host.setAttribute('data-osd-canvas', '');
|
|
111
115
|
host.style.width = '1920px';
|
|
112
116
|
host.style.height = '1080px';
|
|
117
|
+
if (designVars) {
|
|
118
|
+
for (const [k, v] of Object.entries(designVars)) host.style.setProperty(k, v);
|
|
119
|
+
}
|
|
113
120
|
const inner = document.createElement('div');
|
|
114
121
|
inner.className = 'os-print-supersample';
|
|
115
122
|
inner.style.width = '1920px';
|
package/src/app/lib/folders.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { useCallback, useEffect, useState } from 'react';
|
|
2
1
|
import buildManifest from 'virtual:open-slide/folders';
|
|
2
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
3
3
|
import type { Folder, FolderIcon, FoldersManifest } from './sdk';
|
|
4
4
|
|
|
5
5
|
const EMPTY: FoldersManifest = { folders: [], assignments: {} };
|
|
@@ -3,7 +3,8 @@ import { useCallback } from 'react';
|
|
|
3
3
|
export type EditOp =
|
|
4
4
|
| { kind: 'set-style'; key: string; value: string | null }
|
|
5
5
|
| { kind: 'set-text'; value: string }
|
|
6
|
-
| { kind: 'set-attr-asset'; attr: string; assetPath: string; previewUrl: string }
|
|
6
|
+
| { kind: 'set-attr-asset'; attr: string; assetPath: string; previewUrl: string }
|
|
7
|
+
| { kind: 'replace-placeholder-with-image'; assetPath: string };
|
|
7
8
|
|
|
8
9
|
export type Edit = { line: number; column: number; ops: EditOp[] };
|
|
9
10
|
|
package/src/app/lib/sdk.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ComponentType } from 'react';
|
|
2
|
+
import type { DesignSystem } from '../../design.ts';
|
|
2
3
|
|
|
3
4
|
export type Page = ComponentType;
|
|
4
5
|
|
|
@@ -9,6 +10,10 @@ export type SlideMeta = {
|
|
|
9
10
|
export type SlideModule = {
|
|
10
11
|
default: Page[];
|
|
11
12
|
meta?: SlideMeta;
|
|
13
|
+
design?: DesignSystem;
|
|
14
|
+
// Index-aligned with `default`. Each entry is the speaker note for the
|
|
15
|
+
// page at the same position. Used by Presenter View only.
|
|
16
|
+
notes?: (string | undefined)[];
|
|
12
17
|
};
|
|
13
18
|
|
|
14
19
|
export type FolderIcon = { type: 'emoji'; value: string } | { type: 'color'; value: string };
|
package/src/app/lib/slides.ts
CHANGED
package/src/app/lib/utils.ts
CHANGED
package/src/app/main.tsx
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
|
+
import { ThemeProvider } from 'next-themes';
|
|
1
2
|
import { StrictMode } from 'react';
|
|
2
3
|
import { createRoot } from 'react-dom/client';
|
|
3
|
-
import { App } from './
|
|
4
|
+
import { App } from './app';
|
|
4
5
|
import './styles.css';
|
|
5
6
|
|
|
6
7
|
// biome-ignore lint/style/noNonNullAssertion: #root is guaranteed by index.html
|
|
7
8
|
createRoot(document.getElementById('root')!).render(
|
|
8
9
|
<StrictMode>
|
|
9
|
-
<
|
|
10
|
+
<ThemeProvider attribute="class" defaultTheme="system" enableSystem disableTransitionOnChange>
|
|
11
|
+
<App />
|
|
12
|
+
</ThemeProvider>
|
|
10
13
|
</StrictMode>,
|
|
11
14
|
);
|