@open-aippt/core 1.13.2

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 (142) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +98 -0
  3. package/bin.js +2 -0
  4. package/dist/build-DxTqmvsO.js +17 -0
  5. package/dist/cli/bin.d.ts +1 -0
  6. package/dist/cli/bin.js +86 -0
  7. package/dist/config-CjzqjrEA.js +4280 -0
  8. package/dist/config-DIC-yVPp.d.ts +23 -0
  9. package/dist/design-cpzS8aud.js +35 -0
  10. package/dist/dev-BYuTeJbA.js +20 -0
  11. package/dist/format-BCeKbTOM.js +1605 -0
  12. package/dist/index.d.ts +134 -0
  13. package/dist/index.js +467 -0
  14. package/dist/locale/index.d.ts +24 -0
  15. package/dist/locale/index.js +3 -0
  16. package/dist/preview-DlQvnJPq.js +18 -0
  17. package/dist/sync-BPZ0m27m.js +139 -0
  18. package/dist/sync-EsYusbbL.js +3 -0
  19. package/dist/types-CHmFPIG_.d.ts +430 -0
  20. package/dist/vite/index.d.ts +14 -0
  21. package/dist/vite/index.js +4 -0
  22. package/env.d.ts +59 -0
  23. package/package.json +103 -0
  24. package/skills/apply-comments/SKILL.md +83 -0
  25. package/skills/create-slide/SKILL.md +91 -0
  26. package/skills/create-theme/SKILL.md +250 -0
  27. package/skills/current-slide/SKILL.md +110 -0
  28. package/skills/slide-authoring/SKILL.md +625 -0
  29. package/src/app/app.tsx +47 -0
  30. package/src/app/components/asset-view.tsx +966 -0
  31. package/src/app/components/history-provider.tsx +120 -0
  32. package/src/app/components/image-placeholder.tsx +243 -0
  33. package/src/app/components/inspector/asset-picker-dialog.tsx +196 -0
  34. package/src/app/components/inspector/comment-widget.tsx +93 -0
  35. package/src/app/components/inspector/image-crop-dialog.tsx +212 -0
  36. package/src/app/components/inspector/inspect-overlay.tsx +387 -0
  37. package/src/app/components/inspector/inspector-panel.tsx +1115 -0
  38. package/src/app/components/inspector/inspector-provider.tsx +1218 -0
  39. package/src/app/components/inspector/save-bar.tsx +48 -0
  40. package/src/app/components/language-toggle.tsx +39 -0
  41. package/src/app/components/notes-drawer.tsx +120 -0
  42. package/src/app/components/overview-grid.tsx +363 -0
  43. package/src/app/components/panel/panel-fields.tsx +60 -0
  44. package/src/app/components/panel/panel-shell.tsx +80 -0
  45. package/src/app/components/panel/save-card.tsx +142 -0
  46. package/src/app/components/pdf-progress-toast.tsx +32 -0
  47. package/src/app/components/player.tsx +466 -0
  48. package/src/app/components/pptx-progress-toast.tsx +32 -0
  49. package/src/app/components/present/blackout-overlay.tsx +18 -0
  50. package/src/app/components/present/control-bar.tsx +315 -0
  51. package/src/app/components/present/help-overlay.tsx +57 -0
  52. package/src/app/components/present/jump-input.tsx +74 -0
  53. package/src/app/components/present/laser-pointer.tsx +39 -0
  54. package/src/app/components/present/progress-bar.tsx +26 -0
  55. package/src/app/components/present/use-idle.ts +46 -0
  56. package/src/app/components/present/use-pointer-near-bottom.ts +34 -0
  57. package/src/app/components/present/use-presenter-channel.ts +66 -0
  58. package/src/app/components/present/use-touch-swipe.ts +66 -0
  59. package/src/app/components/shared-element.tsx +48 -0
  60. package/src/app/components/sidebar/folder-item.tsx +258 -0
  61. package/src/app/components/sidebar/icon-picker.tsx +61 -0
  62. package/src/app/components/sidebar/mobile-pill.tsx +34 -0
  63. package/src/app/components/sidebar/sidebar-footer.tsx +105 -0
  64. package/src/app/components/sidebar/sidebar.tsx +284 -0
  65. package/src/app/components/slide-canvas.tsx +102 -0
  66. package/src/app/components/slide-transition-layer.tsx +844 -0
  67. package/src/app/components/style-panel/design-provider.tsx +148 -0
  68. package/src/app/components/style-panel/style-panel.tsx +349 -0
  69. package/src/app/components/style-panel/use-design.ts +112 -0
  70. package/src/app/components/theme-toggle.tsx +59 -0
  71. package/src/app/components/themes/theme-detail.tsx +305 -0
  72. package/src/app/components/themes/themes-gallery.tsx +149 -0
  73. package/src/app/components/thumbnail-rail.tsx +805 -0
  74. package/src/app/components/ui/badge.tsx +45 -0
  75. package/src/app/components/ui/button.tsx +99 -0
  76. package/src/app/components/ui/card.tsx +92 -0
  77. package/src/app/components/ui/context-menu.tsx +237 -0
  78. package/src/app/components/ui/dialog.tsx +157 -0
  79. package/src/app/components/ui/dropdown-menu.tsx +245 -0
  80. package/src/app/components/ui/input.tsx +25 -0
  81. package/src/app/components/ui/label.tsx +24 -0
  82. package/src/app/components/ui/popover.tsx +75 -0
  83. package/src/app/components/ui/progress.tsx +31 -0
  84. package/src/app/components/ui/scroll-area.tsx +53 -0
  85. package/src/app/components/ui/select.tsx +196 -0
  86. package/src/app/components/ui/separator.tsx +28 -0
  87. package/src/app/components/ui/slider.tsx +61 -0
  88. package/src/app/components/ui/sonner.tsx +48 -0
  89. package/src/app/components/ui/tabs.tsx +79 -0
  90. package/src/app/components/ui/textarea.tsx +22 -0
  91. package/src/app/components/ui/toggle-group.tsx +83 -0
  92. package/src/app/components/ui/toggle.tsx +45 -0
  93. package/src/app/components/ui/tooltip.tsx +58 -0
  94. package/src/app/favicon.ico +0 -0
  95. package/src/app/index.html +13 -0
  96. package/src/app/lib/assets.ts +242 -0
  97. package/src/app/lib/design-presets.ts +94 -0
  98. package/src/app/lib/design.ts +58 -0
  99. package/src/app/lib/export-html.ts +326 -0
  100. package/src/app/lib/export-pdf.ts +298 -0
  101. package/src/app/lib/export-pptx.ts +284 -0
  102. package/src/app/lib/folders.ts +239 -0
  103. package/src/app/lib/inspector/fiber.test.ts +154 -0
  104. package/src/app/lib/inspector/fiber.ts +85 -0
  105. package/src/app/lib/inspector/use-comments.ts +74 -0
  106. package/src/app/lib/inspector/use-editor.ts +73 -0
  107. package/src/app/lib/inspector/use-notes.ts +134 -0
  108. package/src/app/lib/locale-store.ts +67 -0
  109. package/src/app/lib/page-context.tsx +38 -0
  110. package/src/app/lib/print-ready.test.ts +32 -0
  111. package/src/app/lib/print-ready.ts +51 -0
  112. package/src/app/lib/sdk.test.ts +13 -0
  113. package/src/app/lib/sdk.ts +37 -0
  114. package/src/app/lib/slides.ts +26 -0
  115. package/src/app/lib/step-context.tsx +261 -0
  116. package/src/app/lib/themes.ts +22 -0
  117. package/src/app/lib/transition.ts +30 -0
  118. package/src/app/lib/use-agent-socket.ts +18 -0
  119. package/src/app/lib/use-click-page-navigation.ts +60 -0
  120. package/src/app/lib/use-is-mobile.ts +21 -0
  121. package/src/app/lib/use-locale.ts +8 -0
  122. package/src/app/lib/use-prefers-reduced-motion.ts +19 -0
  123. package/src/app/lib/use-slide-module.ts +48 -0
  124. package/src/app/lib/use-wheel-page-navigation.ts +99 -0
  125. package/src/app/lib/utils.test.ts +25 -0
  126. package/src/app/lib/utils.ts +6 -0
  127. package/src/app/main.tsx +14 -0
  128. package/src/app/routes/assets.tsx +9 -0
  129. package/src/app/routes/home-shell.tsx +213 -0
  130. package/src/app/routes/home.tsx +807 -0
  131. package/src/app/routes/presenter.tsx +418 -0
  132. package/src/app/routes/slide.tsx +1108 -0
  133. package/src/app/routes/themes.tsx +34 -0
  134. package/src/app/styles.css +429 -0
  135. package/src/app/virtual.d.ts +51 -0
  136. package/src/locale/en.ts +416 -0
  137. package/src/locale/format.ts +12 -0
  138. package/src/locale/index.ts +6 -0
  139. package/src/locale/ja.ts +422 -0
  140. package/src/locale/types.ts +443 -0
  141. package/src/locale/zh-cn.ts +414 -0
  142. package/src/locale/zh-tw.ts +414 -0
@@ -0,0 +1,284 @@
1
+ import { Plus } from 'lucide-react';
2
+ import { useEffect, useRef, useState } from 'react';
3
+ import { toast } from 'sonner';
4
+ import { LanguageToggle } from '@/components/language-toggle';
5
+ import { ThemeToggle } from '@/components/theme-toggle';
6
+ import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
7
+ import type { Folder, FolderIcon } from '@/lib/sdk';
8
+ import { format, useLocale } from '@/lib/use-locale';
9
+ import { cn } from '@/lib/utils';
10
+ import { FolderIconChip, FolderItem } from './folder-item';
11
+ import { IconPicker, PRESET_COLORS } from './icon-picker';
12
+ import { SidebarFooter } from './sidebar-footer';
13
+
14
+ export const DRAFT_ID = 'draft';
15
+ export const THEMES_ID = '__themes__';
16
+ export const ASSETS_ID = '__assets__';
17
+
18
+ export const FOLDER_DND_MIME = 'application/x-folder-id';
19
+
20
+ export function Sidebar({
21
+ folders,
22
+ countFor,
23
+ themesCount,
24
+ assetsCount,
25
+ selectedId,
26
+ onSelect,
27
+ onCreate,
28
+ onRename,
29
+ onChangeIcon,
30
+ onDelete,
31
+ onDropToFolder,
32
+ onDropToDraft,
33
+ onReorder,
34
+ }: {
35
+ folders: Folder[];
36
+ countFor: (folderId: string | null) => number;
37
+ themesCount: number;
38
+ assetsCount: number;
39
+ selectedId: string;
40
+ onSelect: (id: string) => void;
41
+ onCreate: (name: string, icon: FolderIcon) => Promise<Folder> | undefined;
42
+ onRename: (id: string, name: string) => void;
43
+ onChangeIcon: (id: string, icon: FolderIcon) => void;
44
+ onDelete: (id: string) => void;
45
+ onDropToFolder: (folderId: string, slideId: string) => void;
46
+ onDropToDraft: (slideId: string) => void;
47
+ onReorder: (ids: string[]) => void;
48
+ }) {
49
+ const [dragId, setDragId] = useState<string | null>(null);
50
+ const [dropTarget, setDropTarget] = useState<{ id: string; before: boolean } | null>(null);
51
+
52
+ const finishReorder = (toId: string, before: boolean) => {
53
+ const fromId = dragId;
54
+ setDragId(null);
55
+ setDropTarget(null);
56
+ if (!fromId || fromId === toId) return;
57
+ const ids = folders.map((f) => f.id);
58
+ if (!ids.includes(fromId) || !ids.includes(toId)) return;
59
+ const next = ids.filter((id) => id !== fromId);
60
+ next.splice(next.indexOf(toId) + (before ? 0 : 1), 0, fromId);
61
+ if (next.every((id, i) => id === ids[i])) return;
62
+ onReorder(next);
63
+ };
64
+ const [creating, setCreating] = useState(false);
65
+ const [newName, setNewName] = useState('');
66
+ const [newIcon, setNewIcon] = useState<FolderIcon>(() => ({
67
+ type: 'color',
68
+ value: PRESET_COLORS[0],
69
+ }));
70
+ const [iconOpen, setIconOpen] = useState(false);
71
+ const inputRef = useRef<HTMLInputElement>(null);
72
+ const t = useLocale();
73
+
74
+ const startCreating = () => {
75
+ const color = PRESET_COLORS[folders.length % PRESET_COLORS.length];
76
+ setNewIcon({ type: 'color', value: color });
77
+ setNewName('');
78
+ setCreating(true);
79
+ };
80
+
81
+ useEffect(() => {
82
+ if (creating) inputRef.current?.focus();
83
+ }, [creating]);
84
+
85
+ const stateRef = useRef({ name: newName, icon: newIcon, iconOpen });
86
+ stateRef.current = { name: newName, icon: newIcon, iconOpen };
87
+
88
+ const exitCreate = () => {
89
+ setCreating(false);
90
+ setNewName('');
91
+ setIconOpen(false);
92
+ };
93
+
94
+ const commitCreate = async () => {
95
+ const trimmed = stateRef.current.name.trim();
96
+ const icon = stateRef.current.icon;
97
+ if (!trimmed) {
98
+ exitCreate();
99
+ return;
100
+ }
101
+ exitCreate();
102
+ try {
103
+ const folder = await onCreate(trimmed, icon);
104
+ toast.success(format(t.home.toastFolderCreated, { name: folder?.name ?? trimmed }));
105
+ } catch {
106
+ toast.error(t.home.toastFolderCreateFailed);
107
+ }
108
+ };
109
+
110
+ // biome-ignore lint/correctness/useExhaustiveDependencies: commitCreate reads latest state via stateRef
111
+ useEffect(() => {
112
+ if (!creating) return;
113
+ const onDown = (e: MouseEvent) => {
114
+ if (stateRef.current.iconOpen) return;
115
+ const target = e.target as HTMLElement | null;
116
+ if (!target) return;
117
+ if (target.closest('[data-folder-create]')) return;
118
+ if (target.closest('[data-radix-popper-content-wrapper]')) return;
119
+ commitCreate();
120
+ };
121
+ document.addEventListener('mousedown', onDown);
122
+ return () => document.removeEventListener('mousedown', onDown);
123
+ }, [creating]);
124
+
125
+ return (
126
+ <aside className="paper relative flex h-full w-[16.5rem] shrink-0 flex-col border-r border-hairline bg-sidebar text-sidebar-foreground">
127
+ <div className="flex items-center justify-between px-4 pt-5 pb-4">
128
+ <h1 className="font-heading text-lg font-bold tracking-tight">{t.home.appTitle}</h1>
129
+ <div className="-mr-1.5 flex items-center">
130
+ <LanguageToggle />
131
+ <ThemeToggle />
132
+ </div>
133
+ </div>
134
+
135
+ <div className="px-2">
136
+ <FolderItem
137
+ row={{ kind: 'draft' }}
138
+ count={countFor(null)}
139
+ selected={selectedId === DRAFT_ID}
140
+ onSelect={() => onSelect(DRAFT_ID)}
141
+ onDropSlide={onDropToDraft}
142
+ />
143
+ <FolderItem
144
+ row={{ kind: 'themes' }}
145
+ count={themesCount}
146
+ selected={selectedId === THEMES_ID}
147
+ onSelect={() => onSelect(THEMES_ID)}
148
+ onDropSlide={() => {}}
149
+ />
150
+ <FolderItem
151
+ row={{ kind: 'assets' }}
152
+ count={assetsCount}
153
+ selected={selectedId === ASSETS_ID}
154
+ onSelect={() => onSelect(ASSETS_ID)}
155
+ onDropSlide={() => {}}
156
+ />
157
+ </div>
158
+
159
+ <div className="mt-5 flex items-center gap-2 px-4 pb-1.5">
160
+ <span className="eyebrow">{t.home.folders}</span>
161
+ <span className="h-px flex-1 bg-hairline" aria-hidden />
162
+ </div>
163
+
164
+ <div className="flex-1 overflow-y-auto px-2 pb-2">
165
+ {folders.map((folder) => {
166
+ const isDropTarget = dropTarget?.id === folder.id;
167
+ const before = isDropTarget && dropTarget.before;
168
+ const after = isDropTarget && !dropTarget.before;
169
+ return (
170
+ // biome-ignore lint/a11y/noStaticElementInteractions: drag-and-drop handle wraps the row
171
+ <div
172
+ key={folder.id}
173
+ className={cn(
174
+ 'relative',
175
+ before &&
176
+ 'before:absolute before:inset-x-2 before:-top-px before:h-[2px] before:rounded-full before:bg-brand',
177
+ after &&
178
+ 'after:absolute after:inset-x-2 after:-bottom-px after:h-[2px] after:rounded-full after:bg-brand',
179
+ dragId === folder.id && 'opacity-50',
180
+ )}
181
+ draggable={import.meta.env.DEV}
182
+ onDragStart={(e) => {
183
+ if (!import.meta.env.DEV) return;
184
+ e.dataTransfer.setData(FOLDER_DND_MIME, folder.id);
185
+ e.dataTransfer.effectAllowed = 'move';
186
+ setDragId(folder.id);
187
+ }}
188
+ onDragEnd={() => {
189
+ setDragId(null);
190
+ setDropTarget(null);
191
+ }}
192
+ onDragOver={(e) => {
193
+ if (!e.dataTransfer.types.includes(FOLDER_DND_MIME)) return;
194
+ e.preventDefault();
195
+ e.dataTransfer.dropEffect = 'move';
196
+ const rect = e.currentTarget.getBoundingClientRect();
197
+ const isBefore = e.clientY < rect.top + rect.height / 2;
198
+ if (!dropTarget || dropTarget.id !== folder.id || dropTarget.before !== isBefore) {
199
+ setDropTarget({ id: folder.id, before: isBefore });
200
+ }
201
+ }}
202
+ onDragLeave={(e) => {
203
+ if (e.currentTarget.contains(e.relatedTarget as Node | null)) return;
204
+ if (dropTarget?.id === folder.id) setDropTarget(null);
205
+ }}
206
+ onDrop={(e) => {
207
+ const fromId = e.dataTransfer.getData(FOLDER_DND_MIME);
208
+ if (!fromId) return;
209
+ e.preventDefault();
210
+ e.stopPropagation();
211
+ const rect = e.currentTarget.getBoundingClientRect();
212
+ const isBefore = e.clientY < rect.top + rect.height / 2;
213
+ finishReorder(folder.id, isBefore);
214
+ }}
215
+ >
216
+ <FolderItem
217
+ row={{
218
+ kind: 'folder',
219
+ folder,
220
+ onRename: (name) => onRename(folder.id, name),
221
+ onChangeIcon: (icon) => onChangeIcon(folder.id, icon),
222
+ onDelete: () => onDelete(folder.id),
223
+ }}
224
+ count={countFor(folder.id)}
225
+ selected={selectedId === folder.id}
226
+ onSelect={() => onSelect(folder.id)}
227
+ onDropSlide={(slideId) => onDropToFolder(folder.id, slideId)}
228
+ />
229
+ </div>
230
+ );
231
+ })}
232
+
233
+ {import.meta.env.DEV &&
234
+ (creating ? (
235
+ <div
236
+ data-folder-create
237
+ className="mt-1 flex items-center gap-2.5 rounded-[5px] border border-dashed border-foreground/30 bg-card px-2 py-[5px]"
238
+ >
239
+ <Popover open={iconOpen} onOpenChange={setIconOpen}>
240
+ <PopoverTrigger asChild>
241
+ <button
242
+ type="button"
243
+ className="flex size-5 shrink-0 items-center justify-center rounded transition-transform hover:scale-110"
244
+ aria-label={t.home.pickIcon}
245
+ >
246
+ <FolderIconChip icon={newIcon} />
247
+ </button>
248
+ </PopoverTrigger>
249
+ <PopoverContent side="right" align="start" className="w-auto p-2">
250
+ <IconPicker value={newIcon} onChange={setNewIcon} />
251
+ </PopoverContent>
252
+ </Popover>
253
+ <input
254
+ ref={inputRef}
255
+ value={newName}
256
+ onChange={(e) => setNewName(e.target.value)}
257
+ onKeyDown={(e) => {
258
+ if (e.nativeEvent.isComposing) return;
259
+ if (e.key === 'Enter') commitCreate();
260
+ if (e.key === 'Escape') exitCreate();
261
+ }}
262
+ placeholder={t.home.folderName}
263
+ maxLength={40}
264
+ className="min-w-0 flex-1 bg-transparent text-[12.5px] outline-none placeholder:text-muted-foreground/60"
265
+ />
266
+ </div>
267
+ ) : (
268
+ <button
269
+ type="button"
270
+ onClick={startCreating}
271
+ className="mt-1 flex w-full items-center gap-2 rounded-[5px] px-2 py-1.5 text-[12px] text-muted-foreground transition-colors hover:bg-muted/60 hover:text-foreground"
272
+ >
273
+ <Plus className="size-3.5" />
274
+ <span>{t.home.newFolder}</span>
275
+ </button>
276
+ ))}
277
+ </div>
278
+
279
+ <div className="border-t border-hairline">
280
+ <SidebarFooter />
281
+ </div>
282
+ </aside>
283
+ );
284
+ }
@@ -0,0 +1,102 @@
1
+ import { type CSSProperties, type ReactNode, useLayoutEffect, useRef, useState } from 'react';
2
+ import { cn } from '@/lib/utils';
3
+ import { type DesignSystem, designToCssVars } from '../lib/design';
4
+ import { CANVAS_HEIGHT, CANVAS_WIDTH } from '../lib/sdk';
5
+
6
+ type Props = {
7
+ children: ReactNode;
8
+ /** If set, use this scale directly. Otherwise fit to container. */
9
+ scale?: number;
10
+ center?: boolean;
11
+ flat?: boolean;
12
+ freezeMotion?: boolean;
13
+ className?: string;
14
+ design?: DesignSystem;
15
+ };
16
+
17
+ export function SlideCanvas({
18
+ children,
19
+ scale,
20
+ center = true,
21
+ flat = false,
22
+ freezeMotion = false,
23
+ className,
24
+ design,
25
+ }: Props) {
26
+ const containerRef = useRef<HTMLDivElement>(null);
27
+ const [fitScale, setFitScale] = useState<number | null>(null);
28
+
29
+ useLayoutEffect(() => {
30
+ if (scale !== undefined) return;
31
+ const el = containerRef.current;
32
+ if (!el) return;
33
+ const measure = () => {
34
+ const { width, height } = el.getBoundingClientRect();
35
+ if (width === 0 || height === 0) return;
36
+ setFitScale(Math.min(width / CANVAS_WIDTH, height / CANVAS_HEIGHT));
37
+ };
38
+ // Measure synchronously before paint so the fitted scale is applied on the
39
+ // first visible frame — otherwise the canvas flashes at full size.
40
+ measure();
41
+ const ro = new ResizeObserver(measure);
42
+ ro.observe(el);
43
+ return () => ro.disconnect();
44
+ }, [scale]);
45
+
46
+ const measured = scale ?? fitScale;
47
+ const s = measured ?? 1;
48
+ const scaledW = CANVAS_WIDTH * s;
49
+ const scaledH = CANVAS_HEIGHT * s;
50
+ const designVars = design ? designToCssVars(design) : undefined;
51
+
52
+ return (
53
+ <div ref={containerRef} className={cn('relative h-full w-full overflow-hidden', className)}>
54
+ <div
55
+ className={cn(
56
+ 'overflow-hidden bg-white text-black',
57
+ // Inset shadow keeps the 1px edge inside the canvas box so it
58
+ // can't be clipped by the parent's overflow-hidden.
59
+ !flat && 'rounded-[6px] shadow-[inset_0_0_0_1px_oklch(0_0_0/0.08)]',
60
+ )}
61
+ style={
62
+ {
63
+ width: scaledW,
64
+ height: scaledH,
65
+ visibility: measured === null ? 'hidden' : undefined,
66
+ ...(designVars
67
+ ? {
68
+ ...designVars,
69
+ background: 'var(--osd-bg)',
70
+ }
71
+ : {}),
72
+ ...(center
73
+ ? {
74
+ position: 'absolute',
75
+ left: '50%',
76
+ top: '50%',
77
+ transform: `translate(-50%, -50%)`,
78
+ }
79
+ : {}),
80
+ } as CSSProperties
81
+ }
82
+ >
83
+ <div
84
+ data-osd-canvas
85
+ data-osd-freeze-motion={freezeMotion ? '' : undefined}
86
+ style={
87
+ {
88
+ width: CANVAS_WIDTH,
89
+ height: CANVAS_HEIGHT,
90
+ transform: `scale(${s})`,
91
+ transformOrigin: 'top left',
92
+ ...(designVars ?? {}),
93
+ } as CSSProperties
94
+ }
95
+ >
96
+ {children}
97
+ </div>
98
+ </div>
99
+ {freezeMotion && <div aria-hidden className="absolute inset-0 z-10" />}
100
+ </div>
101
+ );
102
+ }