@particle-academy/fancy-slides 0.1.7 → 0.2.0

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/index.d.cts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ReactNode, CSSProperties } from 'react';
3
- import { f as Slide$1, m as Theme, h as SlideElement, D as Deck, c as DeckOp, e as ShapeKind, k as TextElement, I as ImageElement, S as ShapeElement, i as SlideLayout, g as SlideBackground } from './types-Bc-psiRF.cjs';
4
- export { C as ChartElement, a as CodeElement, b as DeckActivity, E as ElementBase, d as EmbedElement, j as SlideTransition, T as TableElement, l as TextStyle, n as ThemeColors, o as ThemeFonts, p as TransitionKind } from './types-Bc-psiRF.cjs';
3
+ import { f as Slide$1, m as Theme, h as SlideElement, D as Deck, c as DeckOp, e as ShapeKind, j as SlideTransition, g as SlideBackground, k as TextElement, I as ImageElement, S as ShapeElement, i as SlideLayout } from './types-B2ecrEAz.cjs';
4
+ export { C as ChartElement, a as CodeElement, b as DeckActivity, E as ElementBase, d as EmbedElement, T as TableElement, l as TextStyle, n as ThemeColors, o as ThemeFonts, p as TransitionKind } from './types-B2ecrEAz.cjs';
5
5
 
6
6
  interface SlideProps {
7
7
  /** The slide to render. */
@@ -279,7 +279,7 @@ interface EditorToolbarProps {
279
279
  declare function EditorToolbar({ title, onTitleChange, themeName, onApplyTheme, onInsertText, onInsertImage, onInsertShape, onInsertChart, onInsertCode, onInsertTable, onPresent, disabled, }: EditorToolbarProps): react_jsx_runtime.JSX.Element;
280
280
 
281
281
  interface ElementInspectorProps {
282
- /** Element being inspected. `null` shows the empty state. */
282
+ /** Element being inspected. `null` falls back to slide settings (or the empty state). */
283
283
  element: SlideElement | null;
284
284
  /** Patch a property on the element. */
285
285
  onPatch: (patch: Partial<SlideElement>) => void;
@@ -287,6 +287,12 @@ interface ElementInspectorProps {
287
287
  onDelete?: () => void;
288
288
  /** Lock toggle. */
289
289
  onLockToggle?: (locked: boolean) => void;
290
+ /** Selected slide — shown when no element is selected so the user can edit slide-level settings. */
291
+ slide?: Slide$1 | null;
292
+ /** Set the slide's entrance transition. */
293
+ onSetTransition?: (transition?: SlideTransition) => void;
294
+ /** Set the slide's background. */
295
+ onSetBackground?: (background?: SlideBackground) => void;
290
296
  }
291
297
  /**
292
298
  * Right-hand inspector. Tabs split position + style + advanced properties.
@@ -294,7 +300,7 @@ interface ElementInspectorProps {
294
300
  * react-fancy `Card`, `Tabs`, `Input`, `Select`, `Slider`, `ColorPicker`,
295
301
  * `Action`.
296
302
  */
297
- declare function ElementInspector({ element, onPatch, onDelete, onLockToggle }: ElementInspectorProps): react_jsx_runtime.JSX.Element;
303
+ declare function ElementInspector({ element, onPatch, onDelete, onLockToggle, slide, onSetTransition, onSetBackground }: ElementInspectorProps): react_jsx_runtime.JSX.Element;
298
304
 
299
305
  interface SpeakerNotesProps {
300
306
  notes?: string;
@@ -413,6 +419,7 @@ interface DeckStateApi {
413
419
  setLayout: (id: string, layout: SlideLayout) => void;
414
420
  setNotes: (id: string, notes: string) => void;
415
421
  setBackground: (id: string, bg?: SlideBackground) => void;
422
+ setTransition: (id: string, transition?: SlideTransition) => void;
416
423
  /** Element-level helpers. */
417
424
  addElement: (slideId: string, element: Omit<SlideElement, "id"> & {
418
425
  id?: string;
@@ -481,4 +488,4 @@ type ChartKind = "bar" | "line" | "pie" | "area" | "scatter";
481
488
  type Option = Record<string, unknown>;
482
489
  declare function chartStarterOption(kind: ChartKind): Option;
483
490
 
484
- export { type ChartKind$1 as ChartKind, Deck, DeckEditor, type DeckEditorProps, DeckOp, type DeckStateApi, EditorToolbar, type EditorToolbarProps, ElementInspector, type ElementInspectorProps, ImageElement, ImageElementRenderer, type ImageElementRendererProps, PresenterView, type PresenterViewProps, ShapeElement, ShapeElementRenderer, type ShapeElementRendererProps, ShapeKind, Slide, SlideBackground, type SlideContextValue, Slide$1 as SlideData, SlideElement, type SlideKeyboardOptions, SlideLayout, type SlideProps, SlideRail, type SlideRailProps, SlideThumbnail, type SlideThumbnailProps, SlideViewer, type SlideViewerProps, SpeakerNotes, type SpeakerNotesProps, TextElement, TextElementRenderer, type TextElementRendererProps, Theme, type UseDeckStateOptions, builtinThemes, chartStarterOption, darkTheme, deckId, defaultTheme, defineTheme, elementId, nextId, reduce as reduceDeck, resolveTheme, slideId, useDeckState, useIsDarkSlide, useSlideContext, useSlideKeyboard, useSlideTheme, vividTheme };
491
+ export { type ChartKind$1 as ChartKind, Deck, DeckEditor, type DeckEditorProps, DeckOp, type DeckStateApi, EditorToolbar, type EditorToolbarProps, ElementInspector, type ElementInspectorProps, ImageElement, ImageElementRenderer, type ImageElementRendererProps, PresenterView, type PresenterViewProps, ShapeElement, ShapeElementRenderer, type ShapeElementRendererProps, ShapeKind, Slide, SlideBackground, type SlideContextValue, Slide$1 as SlideData, SlideElement, type SlideKeyboardOptions, SlideLayout, type SlideProps, SlideRail, type SlideRailProps, SlideThumbnail, type SlideThumbnailProps, SlideTransition, SlideViewer, type SlideViewerProps, SpeakerNotes, type SpeakerNotesProps, TextElement, TextElementRenderer, type TextElementRendererProps, Theme, type UseDeckStateOptions, builtinThemes, chartStarterOption, darkTheme, deckId, defaultTheme, defineTheme, elementId, nextId, reduce as reduceDeck, resolveTheme, slideId, useDeckState, useIsDarkSlide, useSlideContext, useSlideKeyboard, useSlideTheme, vividTheme };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ReactNode, CSSProperties } from 'react';
3
- import { f as Slide$1, m as Theme, h as SlideElement, D as Deck, c as DeckOp, e as ShapeKind, k as TextElement, I as ImageElement, S as ShapeElement, i as SlideLayout, g as SlideBackground } from './types-Bc-psiRF.js';
4
- export { C as ChartElement, a as CodeElement, b as DeckActivity, E as ElementBase, d as EmbedElement, j as SlideTransition, T as TableElement, l as TextStyle, n as ThemeColors, o as ThemeFonts, p as TransitionKind } from './types-Bc-psiRF.js';
3
+ import { f as Slide$1, m as Theme, h as SlideElement, D as Deck, c as DeckOp, e as ShapeKind, j as SlideTransition, g as SlideBackground, k as TextElement, I as ImageElement, S as ShapeElement, i as SlideLayout } from './types-B2ecrEAz.js';
4
+ export { C as ChartElement, a as CodeElement, b as DeckActivity, E as ElementBase, d as EmbedElement, T as TableElement, l as TextStyle, n as ThemeColors, o as ThemeFonts, p as TransitionKind } from './types-B2ecrEAz.js';
5
5
 
6
6
  interface SlideProps {
7
7
  /** The slide to render. */
@@ -279,7 +279,7 @@ interface EditorToolbarProps {
279
279
  declare function EditorToolbar({ title, onTitleChange, themeName, onApplyTheme, onInsertText, onInsertImage, onInsertShape, onInsertChart, onInsertCode, onInsertTable, onPresent, disabled, }: EditorToolbarProps): react_jsx_runtime.JSX.Element;
280
280
 
281
281
  interface ElementInspectorProps {
282
- /** Element being inspected. `null` shows the empty state. */
282
+ /** Element being inspected. `null` falls back to slide settings (or the empty state). */
283
283
  element: SlideElement | null;
284
284
  /** Patch a property on the element. */
285
285
  onPatch: (patch: Partial<SlideElement>) => void;
@@ -287,6 +287,12 @@ interface ElementInspectorProps {
287
287
  onDelete?: () => void;
288
288
  /** Lock toggle. */
289
289
  onLockToggle?: (locked: boolean) => void;
290
+ /** Selected slide — shown when no element is selected so the user can edit slide-level settings. */
291
+ slide?: Slide$1 | null;
292
+ /** Set the slide's entrance transition. */
293
+ onSetTransition?: (transition?: SlideTransition) => void;
294
+ /** Set the slide's background. */
295
+ onSetBackground?: (background?: SlideBackground) => void;
290
296
  }
291
297
  /**
292
298
  * Right-hand inspector. Tabs split position + style + advanced properties.
@@ -294,7 +300,7 @@ interface ElementInspectorProps {
294
300
  * react-fancy `Card`, `Tabs`, `Input`, `Select`, `Slider`, `ColorPicker`,
295
301
  * `Action`.
296
302
  */
297
- declare function ElementInspector({ element, onPatch, onDelete, onLockToggle }: ElementInspectorProps): react_jsx_runtime.JSX.Element;
303
+ declare function ElementInspector({ element, onPatch, onDelete, onLockToggle, slide, onSetTransition, onSetBackground }: ElementInspectorProps): react_jsx_runtime.JSX.Element;
298
304
 
299
305
  interface SpeakerNotesProps {
300
306
  notes?: string;
@@ -413,6 +419,7 @@ interface DeckStateApi {
413
419
  setLayout: (id: string, layout: SlideLayout) => void;
414
420
  setNotes: (id: string, notes: string) => void;
415
421
  setBackground: (id: string, bg?: SlideBackground) => void;
422
+ setTransition: (id: string, transition?: SlideTransition) => void;
416
423
  /** Element-level helpers. */
417
424
  addElement: (slideId: string, element: Omit<SlideElement, "id"> & {
418
425
  id?: string;
@@ -481,4 +488,4 @@ type ChartKind = "bar" | "line" | "pie" | "area" | "scatter";
481
488
  type Option = Record<string, unknown>;
482
489
  declare function chartStarterOption(kind: ChartKind): Option;
483
490
 
484
- export { type ChartKind$1 as ChartKind, Deck, DeckEditor, type DeckEditorProps, DeckOp, type DeckStateApi, EditorToolbar, type EditorToolbarProps, ElementInspector, type ElementInspectorProps, ImageElement, ImageElementRenderer, type ImageElementRendererProps, PresenterView, type PresenterViewProps, ShapeElement, ShapeElementRenderer, type ShapeElementRendererProps, ShapeKind, Slide, SlideBackground, type SlideContextValue, Slide$1 as SlideData, SlideElement, type SlideKeyboardOptions, SlideLayout, type SlideProps, SlideRail, type SlideRailProps, SlideThumbnail, type SlideThumbnailProps, SlideViewer, type SlideViewerProps, SpeakerNotes, type SpeakerNotesProps, TextElement, TextElementRenderer, type TextElementRendererProps, Theme, type UseDeckStateOptions, builtinThemes, chartStarterOption, darkTheme, deckId, defaultTheme, defineTheme, elementId, nextId, reduce as reduceDeck, resolveTheme, slideId, useDeckState, useIsDarkSlide, useSlideContext, useSlideKeyboard, useSlideTheme, vividTheme };
491
+ export { type ChartKind$1 as ChartKind, Deck, DeckEditor, type DeckEditorProps, DeckOp, type DeckStateApi, EditorToolbar, type EditorToolbarProps, ElementInspector, type ElementInspectorProps, ImageElement, ImageElementRenderer, type ImageElementRendererProps, PresenterView, type PresenterViewProps, ShapeElement, ShapeElementRenderer, type ShapeElementRendererProps, ShapeKind, Slide, SlideBackground, type SlideContextValue, Slide$1 as SlideData, SlideElement, type SlideKeyboardOptions, SlideLayout, type SlideProps, SlideRail, type SlideRailProps, SlideThumbnail, type SlideThumbnailProps, SlideTransition, SlideViewer, type SlideViewerProps, SpeakerNotes, type SpeakerNotesProps, TextElement, TextElementRenderer, type TextElementRendererProps, Theme, type UseDeckStateOptions, builtinThemes, chartStarterOption, darkTheme, deckId, defaultTheme, defineTheme, elementId, nextId, reduce as reduceDeck, resolveTheme, slideId, useDeckState, useIsDarkSlide, useSlideContext, useSlideKeyboard, useSlideTheme, vividTheme };
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { isDarkColor, SlideContext } from './chunk-WIUXPQAK.js';
2
2
  export { useIsDarkSlide, useSlideContext, useSlideTheme } from './chunk-WIUXPQAK.js';
3
3
  import { useId, useRef, useState, useEffect, useMemo, useCallback } from 'react';
4
- import { ContentRenderer, Text, Action, ContextMenu, Separator, Tooltip, Dropdown, Badge, Heading, Tabs, Card, Input, Slider, Textarea, Select, ColorPicker } from '@particle-academy/react-fancy';
4
+ import { ContentRenderer, Text, Action, ContextMenu, Separator, Tooltip, Dropdown, Badge, Heading, Tabs, Card, Select, Input, ColorPicker, Slider, Textarea } from '@particle-academy/react-fancy';
5
5
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
6
6
 
7
7
  // src/theme/default-theme.ts
@@ -611,6 +611,11 @@ function SlideViewer({
611
611
  );
612
612
  const [blanked, setBlanked] = useState(false);
613
613
  const containerRef = useRef(null);
614
+ const prevIndexRef = useRef(index);
615
+ const forward = index >= prevIndexRef.current;
616
+ useEffect(() => {
617
+ prevIndexRef.current = index;
618
+ }, [index]);
614
619
  useSlideKeyboard({
615
620
  total: deck.slides.length,
616
621
  index,
@@ -634,6 +639,8 @@ function SlideViewer({
634
639
  const slide = deck.slides[index];
635
640
  const theme = resolveTheme(deck.theme);
636
641
  const aspectRatio = theme.aspectRatio ?? 16 / 9;
642
+ const transition = slide?.transition ?? theme.defaultTransition;
643
+ const enterStyle = transitionEnterStyle(transition, forward);
637
644
  return /* @__PURE__ */ jsxs(
638
645
  "div",
639
646
  {
@@ -652,6 +659,7 @@ function SlideViewer({
652
659
  tabIndex: 0,
653
660
  "data-fancy-slides-viewer": deck.id,
654
661
  children: [
662
+ /* @__PURE__ */ jsx("style", { children: TRANSITION_KEYFRAMES }),
655
663
  !blanked && slide && /* @__PURE__ */ jsx(
656
664
  "div",
657
665
  {
@@ -663,7 +671,7 @@ function SlideViewer({
663
671
  ["--fs-ratio"]: aspectRatio.toString(),
664
672
  boxShadow: "0 8px 30px rgba(0,0,0,0.35)"
665
673
  },
666
- children: /* @__PURE__ */ jsx(Slide, { slide, theme, renderElement })
674
+ children: /* @__PURE__ */ jsx("div", { className: "fs-slide-enter", style: enterStyle, children: /* @__PURE__ */ jsx(Slide, { slide, theme, renderElement }) }, index)
667
675
  }
668
676
  ),
669
677
  !hideChrome && !blanked && /* @__PURE__ */ jsxs(
@@ -693,6 +701,68 @@ function SlideViewer({
693
701
  }
694
702
  );
695
703
  }
704
+ var DEFAULT_DURATION = 400;
705
+ var EASE = "cubic-bezier(0.16, 1, 0.3, 1)";
706
+ function transitionEnterStyle(transition, forward) {
707
+ const kind = transition?.kind ?? "none";
708
+ if (kind === "none") return { width: "100%", height: "100%" };
709
+ const duration = transition?.duration ?? DEFAULT_DURATION;
710
+ let name;
711
+ switch (kind) {
712
+ case "fade":
713
+ name = "fs-fade-in";
714
+ break;
715
+ case "zoom":
716
+ name = "fs-zoom-in";
717
+ break;
718
+ case "slide": {
719
+ const dir = transition?.direction ?? (forward ? "right" : "left");
720
+ name = `fs-slide-in-${dir}`;
721
+ break;
722
+ }
723
+ default:
724
+ return { width: "100%", height: "100%" };
725
+ }
726
+ return {
727
+ width: "100%",
728
+ height: "100%",
729
+ animationName: name,
730
+ animationDuration: `${duration}ms`,
731
+ animationTimingFunction: EASE,
732
+ animationFillMode: "both"
733
+ };
734
+ }
735
+ var TRANSITION_KEYFRAMES = `
736
+ @media (prefers-reduced-motion: reduce) {
737
+ .fs-slide-enter { animation: none !important; }
738
+ }
739
+ @media (prefers-reduced-motion: no-preference) {
740
+ @keyframes fs-fade-in {
741
+ from { opacity: 0; }
742
+ to { opacity: 1; }
743
+ }
744
+ @keyframes fs-zoom-in {
745
+ from { opacity: 0; transform: scale(0.92); }
746
+ to { opacity: 1; transform: scale(1); }
747
+ }
748
+ @keyframes fs-slide-in-right {
749
+ from { opacity: 0; transform: translateX(8%); }
750
+ to { opacity: 1; transform: translateX(0); }
751
+ }
752
+ @keyframes fs-slide-in-left {
753
+ from { opacity: 0; transform: translateX(-8%); }
754
+ to { opacity: 1; transform: translateX(0); }
755
+ }
756
+ @keyframes fs-slide-in-up {
757
+ from { opacity: 0; transform: translateY(8%); }
758
+ to { opacity: 1; transform: translateY(0); }
759
+ }
760
+ @keyframes fs-slide-in-down {
761
+ from { opacity: 0; transform: translateY(-8%); }
762
+ to { opacity: 1; transform: translateY(0); }
763
+ }
764
+ }
765
+ `;
696
766
  function PresenterView({
697
767
  deck,
698
768
  index: controlledIndex,
@@ -1046,6 +1116,7 @@ function useDeckState({ value, onChange, onOp }) {
1046
1116
  setLayout: (id, layout) => apply({ kind: "slide_set_layout", id, layout }),
1047
1117
  setNotes: (id, notes) => apply({ kind: "slide_set_notes", id, notes }),
1048
1118
  setBackground: (id, background) => apply({ kind: "slide_set_background", id, background }),
1119
+ setTransition: (id, transition) => apply({ kind: "slide_set_transition", id, transition }),
1049
1120
  addElement: (slideId2, element) => {
1050
1121
  const id = element.id ?? elementId();
1051
1122
  apply({ kind: "element_add", slideId: slideId2, element: { ...element, id } });
@@ -1087,6 +1158,8 @@ function reduce(deck, op) {
1087
1158
  return { ...deck, slides: deck.slides.map((s) => s.id === op.id ? { ...s, notes: op.notes } : s) };
1088
1159
  case "slide_set_background":
1089
1160
  return { ...deck, slides: deck.slides.map((s) => s.id === op.id ? { ...s, background: op.background } : s) };
1161
+ case "slide_set_transition":
1162
+ return { ...deck, slides: deck.slides.map((s) => s.id === op.id ? { ...s, transition: op.transition } : s) };
1090
1163
  case "element_add":
1091
1164
  return {
1092
1165
  ...deck,
@@ -1354,8 +1427,11 @@ function EditorToolbar({
1354
1427
  /* @__PURE__ */ jsx("div", { className: "ml-auto flex items-center gap-2", children: /* @__PURE__ */ jsx(Tooltip, { content: "Present (F)", children: /* @__PURE__ */ jsx(Action, { color: "violet", size: "sm", icon: "play", onClick: onPresent, children: "Present" }) }) })
1355
1428
  ] });
1356
1429
  }
1357
- function ElementInspector({ element, onPatch, onDelete, onLockToggle }) {
1430
+ function ElementInspector({ element, onPatch, onDelete, onLockToggle, slide, onSetTransition, onSetBackground }) {
1358
1431
  if (!element) {
1432
+ if (slide) {
1433
+ return /* @__PURE__ */ jsx(SlideSettings, { slide, onSetTransition, onSetBackground });
1434
+ }
1359
1435
  return /* @__PURE__ */ jsxs("div", { className: "fs-inspector flex h-full flex-col border-l border-zinc-200 bg-zinc-50 p-4 dark:border-zinc-800 dark:bg-zinc-900", children: [
1360
1436
  /* @__PURE__ */ jsx(Heading, { as: "h3", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Inspector" }),
1361
1437
  /* @__PURE__ */ jsx(Text, { size: "sm", className: "mt-2 !text-zinc-500", children: "Select an element to edit its properties." })
@@ -1389,6 +1465,80 @@ function ElementInspector({ element, onPatch, onDelete, onLockToggle }) {
1389
1465
  ] }) })
1390
1466
  ] });
1391
1467
  }
1468
+ function SlideSettings({
1469
+ slide,
1470
+ onSetTransition,
1471
+ onSetBackground
1472
+ }) {
1473
+ const transition = slide.transition;
1474
+ const kind = transition?.kind ?? "none";
1475
+ const setTransition = (next) => {
1476
+ const merged = { kind, duration: transition?.duration, direction: transition?.direction, ...next };
1477
+ onSetTransition?.(merged.kind === "none" ? { kind: "none" } : merged);
1478
+ };
1479
+ return /* @__PURE__ */ jsxs("div", { className: "fs-inspector flex h-full w-full flex-col border-l border-zinc-200 bg-zinc-50 dark:border-zinc-800 dark:bg-zinc-900", children: [
1480
+ /* @__PURE__ */ jsx("div", { className: "flex items-center justify-between border-b border-zinc-200 px-3 py-2 dark:border-zinc-800", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1481
+ /* @__PURE__ */ jsx(Heading, { as: "h3", size: "xs", className: "!font-mono !uppercase !tracking-wider !text-zinc-500", children: "slide" }),
1482
+ /* @__PURE__ */ jsxs(Text, { size: "xs", className: "!font-mono !text-zinc-400", children: [
1483
+ "#",
1484
+ slide.id.slice(-6)
1485
+ ] })
1486
+ ] }) }),
1487
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto p-3", children: [
1488
+ /* @__PURE__ */ jsx(Card, { padding: "md", className: "!bg-white dark:!bg-zinc-950", children: /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
1489
+ /* @__PURE__ */ jsx(Heading, { as: "h4", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Transition" }),
1490
+ /* @__PURE__ */ jsx(
1491
+ Select,
1492
+ {
1493
+ label: "Kind",
1494
+ list: [
1495
+ { value: "none", label: "None" },
1496
+ { value: "fade", label: "Fade" },
1497
+ { value: "slide", label: "Slide" },
1498
+ { value: "zoom", label: "Zoom" }
1499
+ ],
1500
+ value: kind,
1501
+ onValueChange: (v) => setTransition({ kind: v })
1502
+ }
1503
+ ),
1504
+ kind === "slide" && /* @__PURE__ */ jsx(
1505
+ Select,
1506
+ {
1507
+ label: "Direction",
1508
+ list: [
1509
+ { value: "left", label: "From left" },
1510
+ { value: "right", label: "From right" },
1511
+ { value: "up", label: "From bottom" },
1512
+ { value: "down", label: "From top" }
1513
+ ],
1514
+ value: transition?.direction ?? "right",
1515
+ onValueChange: (v) => setTransition({ direction: v })
1516
+ }
1517
+ ),
1518
+ kind !== "none" && /* @__PURE__ */ jsx(
1519
+ Input,
1520
+ {
1521
+ label: "Duration (ms)",
1522
+ type: "number",
1523
+ value: String(transition?.duration ?? 400),
1524
+ onChange: (e) => setTransition({ duration: parseInt(e.target.value, 10) || 400 })
1525
+ }
1526
+ ),
1527
+ /* @__PURE__ */ jsx(Text, { size: "xs", className: "!text-zinc-500", children: "Entrance animation played when this slide appears in the viewer. Falls back to the theme default. Honors prefers-reduced-motion." })
1528
+ ] }) }),
1529
+ onSetBackground && /* @__PURE__ */ jsx(Card, { padding: "md", className: "mt-3 !bg-white dark:!bg-zinc-950", children: /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
1530
+ /* @__PURE__ */ jsx(Heading, { as: "h4", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Background" }),
1531
+ /* @__PURE__ */ jsx(FieldLabel, { label: "Color", children: /* @__PURE__ */ jsx(
1532
+ ColorPicker,
1533
+ {
1534
+ value: slide.background?.color ?? "#ffffff",
1535
+ onChange: (c) => onSetBackground({ ...slide.background, color: c })
1536
+ }
1537
+ ) })
1538
+ ] }) })
1539
+ ] })
1540
+ ] });
1541
+ }
1392
1542
  function LayoutSection({ element, onPatch }) {
1393
1543
  return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
1394
1544
  /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
@@ -1838,13 +1988,16 @@ function DeckEditor({
1838
1988
  ElementInspector,
1839
1989
  {
1840
1990
  element: selectedElement,
1991
+ slide: slide ?? null,
1841
1992
  onPatch: (patch) => slide && elementIdSelected && ops.updateElement(slide.id, elementIdSelected, patch),
1842
1993
  onDelete: () => {
1843
1994
  if (!slide || !elementIdSelected) return;
1844
1995
  ops.removeElement(slide.id, elementIdSelected);
1845
1996
  setElementIdSelected(null);
1846
1997
  },
1847
- onLockToggle: (locked) => slide && elementIdSelected && ops.updateElement(slide.id, elementIdSelected, { locked })
1998
+ onLockToggle: (locked) => slide && elementIdSelected && ops.updateElement(slide.id, elementIdSelected, { locked }),
1999
+ onSetTransition: (transition) => slide && ops.setTransition(slide.id, transition),
2000
+ onSetBackground: (background) => slide && ops.setBackground(slide.id, background)
1848
2001
  }
1849
2002
  ) })
1850
2003
  ] }),