@particle-academy/fancy-slides 0.6.1 → 0.8.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,8 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ReactNode, CSSProperties } from 'react';
3
- import { h as Slide$1, o as Theme, j as SlideElement, D as Deck, d as DeckOp, g as ShapeKind, l as SlideTransition, i as SlideBackground, E as ElementAnimation, m as TextElement, I as ImageElement, S as ShapeElement, k as SlideLayout } from './types-9BbelJX1.cjs';
3
+ import { h as Slide$1, o as Theme, j as SlideElement, D as Deck, d as DeckOp, g as ShapeKind, l as SlideTransition, i as SlideBackground, k as SlideLayout, E as ElementAnimation, m as TextElement, I as ImageElement, S as ShapeElement } from './types-9BbelJX1.cjs';
4
4
  export { A as AnimationEffect, a as AnimationTrigger, C as ChartElement, b as CodeElement, c as DeckActivity, e as ElementBase, f as EmbedElement, T as TableElement, n as TextStyle, p as ThemeColors, q as ThemeFonts, r as TransitionKind } from './types-9BbelJX1.cjs';
5
+ import { EditorAction } from '@particle-academy/react-fancy';
5
6
 
6
7
  interface SlideProps {
7
8
  /** The slide to render. */
@@ -322,6 +323,8 @@ interface ElementInspectorProps {
322
323
  onSetTransition?: (transition?: SlideTransition) => void;
323
324
  /** Set the slide's background. */
324
325
  onSetBackground?: (background?: SlideBackground) => void;
326
+ /** Set the slide's layout preset. */
327
+ onSetLayout?: (layout: SlideLayout) => void;
325
328
  /** Set or clear the selected element's entrance build animation. */
326
329
  onSetAnimation?: (animation?: ElementAnimation) => void;
327
330
  /** Set a specific element's build animation by id — used by the slide-level build-order list. */
@@ -333,7 +336,7 @@ interface ElementInspectorProps {
333
336
  * react-fancy `Card`, `Tabs`, `Input`, `Select`, `Slider`, `ColorPicker`,
334
337
  * `Action`.
335
338
  */
336
- declare function ElementInspector({ element, onPatch, onDelete, onLockToggle, slide, onSetTransition, onSetBackground, onSetAnimation, onSetElementAnimation }: ElementInspectorProps): react_jsx_runtime.JSX.Element;
339
+ declare function ElementInspector({ element, onPatch, onDelete, onLockToggle, slide, onSetTransition, onSetBackground, onSetLayout, onSetAnimation, onSetElementAnimation }: ElementInspectorProps): react_jsx_runtime.JSX.Element;
337
340
 
338
341
  interface SpeakerNotesProps {
339
342
  notes?: string;
@@ -484,6 +487,29 @@ interface TextElementRendererProps {
484
487
  */
485
488
  declare function TextElementRenderer({ element, theme, slideWidthPx, editing, selected, onContentChange, paraReveal, }: TextElementRendererProps): react_jsx_runtime.JSX.Element;
486
489
 
490
+ /**
491
+ * Presentation-tuned toolbar preset for the react-fancy `Editor` used to edit
492
+ * slide text inline. Only commands that round-trip cleanly through the editor's
493
+ * `htmlToMarkdown` output are included — bold (`**`), italic (`*`), heading
494
+ * (`## `), and bullet list (`- `). Box-level typography (alignment, color, font
495
+ * size, line height) is NOT here: those are per-element `TextStyle` properties
496
+ * edited in the ElementInspector, and `text-align`/color spans don't survive the
497
+ * markdown the slide content commits to.
498
+ */
499
+ declare const PRESENTATION_EDITOR_ACTIONS: EditorAction[];
500
+ /**
501
+ * Normalize the `Editor`'s markdown output to the *line-based* paragraph model
502
+ * the slide content commits to. The editor emits a blank line (`\n\n`) between
503
+ * `<p>` blocks, but `splitParagraphs` (and the dark-slide pptx writer) treat a
504
+ * single `\n` as one paragraph / build unit, and a blank interior line as its
505
+ * own (phantom) build. Collapsing runs of newlines to a single `\n` keeps
506
+ * "by paragraph" reveals and per-paragraph pptx builds correct — bullets are
507
+ * already one-per-`\n`, so they're unaffected. The round-trip invariant
508
+ * (`content → Editor → normalize → content` preserves the `\n` paragraph count)
509
+ * is covered by a unit test.
510
+ */
511
+ declare function normalizeSlideMarkdown(md: string): string;
512
+
487
513
  interface ImageElementRendererProps {
488
514
  element: ImageElement;
489
515
  }
@@ -647,4 +673,4 @@ type ChartKind = "bar" | "line" | "pie" | "area" | "scatter";
647
673
  type Option = Record<string, unknown>;
648
674
  declare function chartStarterOption(kind: ChartKind): Option;
649
675
 
650
- export { type Build, type BuildStep, type ChartKind$1 as ChartKind, Deck, DeckEditor, type DeckEditorProps, DeckOp, type DeckStateApi, EditorToolbar, type EditorToolbarProps, ElementAnimation, ElementInspector, type ElementInspectorProps, ImageElement, ImageElementRenderer, type ImageElementRendererProps, type ParaReveal, 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, buildSteps, buildsForStep, builtinThemes, chartStarterOption, collectBuilds, darkTheme, deckId, defaultTheme, defineTheme, elementId, isByParagraph, nextId, paragraphReveals, reduce as reduceDeck, resolveTheme, slideId, splitParagraphs, stepDelays, totalBuildSteps, useDeckState, useIsDarkSlide, useSlideContext, useSlideKeyboard, useSlideTheme, visibleElementIds, vividTheme };
676
+ export { type Build, type BuildStep, type ChartKind$1 as ChartKind, Deck, DeckEditor, type DeckEditorProps, DeckOp, type DeckStateApi, EditorToolbar, type EditorToolbarProps, ElementAnimation, ElementInspector, type ElementInspectorProps, ImageElement, ImageElementRenderer, type ImageElementRendererProps, PRESENTATION_EDITOR_ACTIONS, type ParaReveal, 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, buildSteps, buildsForStep, builtinThemes, chartStarterOption, collectBuilds, darkTheme, deckId, defaultTheme, defineTheme, elementId, isByParagraph, nextId, normalizeSlideMarkdown, paragraphReveals, reduce as reduceDeck, resolveTheme, slideId, splitParagraphs, stepDelays, totalBuildSteps, useDeckState, useIsDarkSlide, useSlideContext, useSlideKeyboard, useSlideTheme, visibleElementIds, vividTheme };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ReactNode, CSSProperties } from 'react';
3
- import { h as Slide$1, o as Theme, j as SlideElement, D as Deck, d as DeckOp, g as ShapeKind, l as SlideTransition, i as SlideBackground, E as ElementAnimation, m as TextElement, I as ImageElement, S as ShapeElement, k as SlideLayout } from './types-9BbelJX1.js';
3
+ import { h as Slide$1, o as Theme, j as SlideElement, D as Deck, d as DeckOp, g as ShapeKind, l as SlideTransition, i as SlideBackground, k as SlideLayout, E as ElementAnimation, m as TextElement, I as ImageElement, S as ShapeElement } from './types-9BbelJX1.js';
4
4
  export { A as AnimationEffect, a as AnimationTrigger, C as ChartElement, b as CodeElement, c as DeckActivity, e as ElementBase, f as EmbedElement, T as TableElement, n as TextStyle, p as ThemeColors, q as ThemeFonts, r as TransitionKind } from './types-9BbelJX1.js';
5
+ import { EditorAction } from '@particle-academy/react-fancy';
5
6
 
6
7
  interface SlideProps {
7
8
  /** The slide to render. */
@@ -322,6 +323,8 @@ interface ElementInspectorProps {
322
323
  onSetTransition?: (transition?: SlideTransition) => void;
323
324
  /** Set the slide's background. */
324
325
  onSetBackground?: (background?: SlideBackground) => void;
326
+ /** Set the slide's layout preset. */
327
+ onSetLayout?: (layout: SlideLayout) => void;
325
328
  /** Set or clear the selected element's entrance build animation. */
326
329
  onSetAnimation?: (animation?: ElementAnimation) => void;
327
330
  /** Set a specific element's build animation by id — used by the slide-level build-order list. */
@@ -333,7 +336,7 @@ interface ElementInspectorProps {
333
336
  * react-fancy `Card`, `Tabs`, `Input`, `Select`, `Slider`, `ColorPicker`,
334
337
  * `Action`.
335
338
  */
336
- declare function ElementInspector({ element, onPatch, onDelete, onLockToggle, slide, onSetTransition, onSetBackground, onSetAnimation, onSetElementAnimation }: ElementInspectorProps): react_jsx_runtime.JSX.Element;
339
+ declare function ElementInspector({ element, onPatch, onDelete, onLockToggle, slide, onSetTransition, onSetBackground, onSetLayout, onSetAnimation, onSetElementAnimation }: ElementInspectorProps): react_jsx_runtime.JSX.Element;
337
340
 
338
341
  interface SpeakerNotesProps {
339
342
  notes?: string;
@@ -484,6 +487,29 @@ interface TextElementRendererProps {
484
487
  */
485
488
  declare function TextElementRenderer({ element, theme, slideWidthPx, editing, selected, onContentChange, paraReveal, }: TextElementRendererProps): react_jsx_runtime.JSX.Element;
486
489
 
490
+ /**
491
+ * Presentation-tuned toolbar preset for the react-fancy `Editor` used to edit
492
+ * slide text inline. Only commands that round-trip cleanly through the editor's
493
+ * `htmlToMarkdown` output are included — bold (`**`), italic (`*`), heading
494
+ * (`## `), and bullet list (`- `). Box-level typography (alignment, color, font
495
+ * size, line height) is NOT here: those are per-element `TextStyle` properties
496
+ * edited in the ElementInspector, and `text-align`/color spans don't survive the
497
+ * markdown the slide content commits to.
498
+ */
499
+ declare const PRESENTATION_EDITOR_ACTIONS: EditorAction[];
500
+ /**
501
+ * Normalize the `Editor`'s markdown output to the *line-based* paragraph model
502
+ * the slide content commits to. The editor emits a blank line (`\n\n`) between
503
+ * `<p>` blocks, but `splitParagraphs` (and the dark-slide pptx writer) treat a
504
+ * single `\n` as one paragraph / build unit, and a blank interior line as its
505
+ * own (phantom) build. Collapsing runs of newlines to a single `\n` keeps
506
+ * "by paragraph" reveals and per-paragraph pptx builds correct — bullets are
507
+ * already one-per-`\n`, so they're unaffected. The round-trip invariant
508
+ * (`content → Editor → normalize → content` preserves the `\n` paragraph count)
509
+ * is covered by a unit test.
510
+ */
511
+ declare function normalizeSlideMarkdown(md: string): string;
512
+
487
513
  interface ImageElementRendererProps {
488
514
  element: ImageElement;
489
515
  }
@@ -647,4 +673,4 @@ type ChartKind = "bar" | "line" | "pie" | "area" | "scatter";
647
673
  type Option = Record<string, unknown>;
648
674
  declare function chartStarterOption(kind: ChartKind): Option;
649
675
 
650
- export { type Build, type BuildStep, type ChartKind$1 as ChartKind, Deck, DeckEditor, type DeckEditorProps, DeckOp, type DeckStateApi, EditorToolbar, type EditorToolbarProps, ElementAnimation, ElementInspector, type ElementInspectorProps, ImageElement, ImageElementRenderer, type ImageElementRendererProps, type ParaReveal, 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, buildSteps, buildsForStep, builtinThemes, chartStarterOption, collectBuilds, darkTheme, deckId, defaultTheme, defineTheme, elementId, isByParagraph, nextId, paragraphReveals, reduce as reduceDeck, resolveTheme, slideId, splitParagraphs, stepDelays, totalBuildSteps, useDeckState, useIsDarkSlide, useSlideContext, useSlideKeyboard, useSlideTheme, visibleElementIds, vividTheme };
676
+ export { type Build, type BuildStep, type ChartKind$1 as ChartKind, Deck, DeckEditor, type DeckEditorProps, DeckOp, type DeckStateApi, EditorToolbar, type EditorToolbarProps, ElementAnimation, ElementInspector, type ElementInspectorProps, ImageElement, ImageElementRenderer, type ImageElementRendererProps, PRESENTATION_EDITOR_ACTIONS, type ParaReveal, 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, buildSteps, buildsForStep, builtinThemes, chartStarterOption, collectBuilds, darkTheme, deckId, defaultTheme, defineTheme, elementId, isByParagraph, nextId, normalizeSlideMarkdown, paragraphReveals, reduce as reduceDeck, resolveTheme, slideId, splitParagraphs, stepDelays, totalBuildSteps, useDeckState, useIsDarkSlide, useSlideContext, useSlideKeyboard, useSlideTheme, visibleElementIds, vividTheme };
package/dist/index.js CHANGED
@@ -2,8 +2,8 @@ import { defaultElementRegistry } from './chunk-YEJZYKVB.js';
2
2
  import { isDarkColor, SlideContext } from './chunk-WIUXPQAK.js';
3
3
  export { useIsDarkSlide, useSlideContext, useSlideTheme } from './chunk-WIUXPQAK.js';
4
4
  import { useId, useRef, useState, useEffect, useMemo, useCallback } from 'react';
5
- import { ContentRenderer, Text, Action, ContextMenu, Separator, Tooltip, Dropdown, Badge, Heading, Tabs, Card, Select, Input, ColorPicker, Slider, Switch, Textarea } from '@particle-academy/react-fancy';
6
- import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
+ import { Editor, ContentRenderer, Text, Action, ContextMenu, Separator, Tooltip, Dropdown, Badge, Heading, Tabs, Card, Select, Input, ColorPicker, Textarea, Slider, Switch } from '@particle-academy/react-fancy';
6
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
7
7
 
8
8
  // src/theme/default-theme.ts
9
9
  var defaultTheme = {
@@ -268,6 +268,18 @@ var BUILD_KEYFRAMES = `
268
268
  }
269
269
  }
270
270
  `;
271
+
272
+ // src/components/elements/TextElement/editor-preset.ts
273
+ var PRESENTATION_EDITOR_ACTIONS = [
274
+ { icon: "B", label: "Bold", command: "bold" },
275
+ { icon: "I", label: "Italic", command: "italic" },
276
+ { icon: "H", label: "Heading", command: "formatBlock", commandArg: "<h2>" },
277
+ { icon: "P", label: "Paragraph", command: "formatBlock", commandArg: "<p>" },
278
+ { icon: "\u2022", label: "Bullet list", command: "insertUnorderedList" }
279
+ ];
280
+ function normalizeSlideMarkdown(md) {
281
+ return md.replace(/\r\n/g, "\n").replace(/\n{2,}/g, "\n").replace(/[ \t]+$/gm, "").trim();
282
+ }
271
283
  function TextElementRenderer({
272
284
  element,
273
285
  theme,
@@ -306,19 +318,58 @@ function TextElementRenderer({
306
318
  overflow: "hidden"
307
319
  };
308
320
  if (editing && selected) {
309
- return /* @__PURE__ */ jsx(
310
- "textarea",
321
+ const fontPx = Math.round((style.fontSize ?? 28) * scale);
322
+ const lh = style.lineHeight ?? 1.4;
323
+ const editScope = scopeId;
324
+ return /* @__PURE__ */ jsxs(
325
+ "div",
311
326
  {
312
- value: element.content,
313
- onChange: (e) => onContentChange?.(e.target.value),
327
+ "data-fs-edit-scope": editScope,
314
328
  style: {
315
- ...css,
316
- whiteSpace: "pre-wrap",
317
- resize: "none",
318
- border: "none",
329
+ width: "100%",
330
+ height: "100%",
319
331
  pointerEvents: "auto",
320
- cursor: "text"
321
- }
332
+ cursor: "text",
333
+ textAlign: style.align ?? "left",
334
+ color: style.color ?? t.colors?.text,
335
+ fontFamily: style.fontFamily ?? t.fonts?.body
336
+ },
337
+ onPointerDown: (e) => e.stopPropagation(),
338
+ children: [
339
+ /* @__PURE__ */ jsx("style", { children: `
340
+ [data-fs-edit-scope="${editScope}"] [data-react-fancy-editor] {
341
+ border: none; background: transparent; border-radius: 0;
342
+ height: 100%; display: flex; flex-direction: column; overflow: hidden;
343
+ }
344
+ [data-fs-edit-scope="${editScope}"] [data-react-fancy-editor-toolbar] {
345
+ background: rgba(244,244,245,0.85); border-radius: 6px 6px 0 0;
346
+ padding: 2px 4px;
347
+ }
348
+ [data-fs-edit-scope="${editScope}"] [data-react-fancy-editor-content] {
349
+ flex: 1; min-height: 0; padding: 4px 2px; overflow: auto;
350
+ font-size: ${fontPx}px; line-height: ${lh};
351
+ }
352
+ [data-fs-edit-scope="${editScope}"] [data-react-fancy-editor-content] :is(p, ul, ol, li) { font-size: inherit; margin: 0; }
353
+ [data-fs-edit-scope="${editScope}"] [data-react-fancy-editor-content] :where(p, li) + :where(p, li, ul, ol) { margin-top: 0.4em; }
354
+ [data-fs-edit-scope="${editScope}"] [data-react-fancy-editor-content] h1 { font-size: 1.6em; font-weight: 700; margin: 0; }
355
+ [data-fs-edit-scope="${editScope}"] [data-react-fancy-editor-content] h2 { font-size: 1.35em; font-weight: 700; margin: 0; }
356
+ [data-fs-edit-scope="${editScope}"] [data-react-fancy-editor-content] h3 { font-size: 1.15em; font-weight: 600; margin: 0; }
357
+ ` }),
358
+ /* @__PURE__ */ jsxs(
359
+ Editor,
360
+ {
361
+ value: element.content,
362
+ onChange: (md) => onContentChange?.(normalizeSlideMarkdown(md)),
363
+ outputFormat: "markdown",
364
+ lineSpacing: lh,
365
+ children: [
366
+ /* @__PURE__ */ jsx(Editor.Toolbar, { actions: PRESENTATION_EDITOR_ACTIONS }),
367
+ /* @__PURE__ */ jsx(Editor.Content, {})
368
+ ]
369
+ },
370
+ element.id
371
+ )
372
+ ]
322
373
  }
323
374
  );
324
375
  }
@@ -1948,10 +1999,10 @@ function EditorToolbar({
1948
1999
  /* @__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" }) }) })
1949
2000
  ] });
1950
2001
  }
1951
- function ElementInspector({ element, onPatch, onDelete, onLockToggle, slide, onSetTransition, onSetBackground, onSetAnimation, onSetElementAnimation }) {
2002
+ function ElementInspector({ element, onPatch, onDelete, onLockToggle, slide, onSetTransition, onSetBackground, onSetLayout, onSetAnimation, onSetElementAnimation }) {
1952
2003
  if (!element) {
1953
2004
  if (slide) {
1954
- return /* @__PURE__ */ jsx(SlideSettings, { slide, onSetTransition, onSetBackground, onSetElementAnimation });
2005
+ return /* @__PURE__ */ jsx(SlideSettings, { slide, onSetTransition, onSetBackground, onSetLayout, onSetElementAnimation });
1955
2006
  }
1956
2007
  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: [
1957
2008
  /* @__PURE__ */ jsx(Heading, { as: "h3", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Inspector" }),
@@ -1988,10 +2039,26 @@ function ElementInspector({ element, onPatch, onDelete, onLockToggle, slide, onS
1988
2039
  ] }) })
1989
2040
  ] });
1990
2041
  }
2042
+ var SLIDE_LAYOUTS = [
2043
+ { value: "blank", label: "Blank" },
2044
+ { value: "title", label: "Title" },
2045
+ { value: "title-content", label: "Title + content" },
2046
+ { value: "two-column", label: "Two column" },
2047
+ { value: "section-divider", label: "Section divider" },
2048
+ { value: "image-text", label: "Image + text" },
2049
+ { value: "text-image", label: "Text + image" },
2050
+ { value: "quote", label: "Quote" }
2051
+ ];
2052
+ function backgroundMode(bg) {
2053
+ if (bg?.gradient) return "gradient";
2054
+ if (bg?.image) return "image";
2055
+ return "color";
2056
+ }
1991
2057
  function SlideSettings({
1992
2058
  slide,
1993
2059
  onSetTransition,
1994
2060
  onSetBackground,
2061
+ onSetLayout,
1995
2062
  onSetElementAnimation
1996
2063
  }) {
1997
2064
  const transition = slide.transition;
@@ -2000,6 +2067,7 @@ function SlideSettings({
2000
2067
  const merged = { kind, duration: transition?.duration, direction: transition?.direction, ...next };
2001
2068
  onSetTransition?.(merged.kind === "none" ? { kind: "none" } : merged);
2002
2069
  };
2070
+ const bgMode = backgroundMode(slide.background);
2003
2071
  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: [
2004
2072
  /* @__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: [
2005
2073
  /* @__PURE__ */ jsx(Heading, { as: "h3", size: "xs", className: "!font-mono !uppercase !tracking-wider !text-zinc-500", children: "slide" }),
@@ -2009,6 +2077,19 @@ function SlideSettings({
2009
2077
  ] })
2010
2078
  ] }) }),
2011
2079
  /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto p-3", children: [
2080
+ onSetLayout && /* @__PURE__ */ jsx(Card, { padding: "md", className: "mb-3 !bg-white dark:!bg-zinc-950", children: /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
2081
+ /* @__PURE__ */ jsx(Heading, { as: "h4", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Layout" }),
2082
+ /* @__PURE__ */ jsx(
2083
+ Select,
2084
+ {
2085
+ label: "Preset",
2086
+ list: SLIDE_LAYOUTS,
2087
+ value: slide.layout ?? "blank",
2088
+ onValueChange: (v) => onSetLayout(v)
2089
+ }
2090
+ ),
2091
+ /* @__PURE__ */ jsx(Text, { size: "xs", className: "!text-zinc-500", children: "The layout hint the deck commits to \u2014 carried through to the pptx export's slide layout." })
2092
+ ] }) }),
2012
2093
  /* @__PURE__ */ jsx(Card, { padding: "md", className: "!bg-white dark:!bg-zinc-950", children: /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
2013
2094
  /* @__PURE__ */ jsx(Heading, { as: "h4", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Transition" }),
2014
2095
  /* @__PURE__ */ jsx(
@@ -2052,13 +2133,63 @@ function SlideSettings({
2052
2133
  ] }) }),
2053
2134
  onSetBackground && /* @__PURE__ */ jsx(Card, { padding: "md", className: "mt-3 !bg-white dark:!bg-zinc-950", children: /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
2054
2135
  /* @__PURE__ */ jsx(Heading, { as: "h4", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Background" }),
2055
- /* @__PURE__ */ jsx(FieldLabel, { label: "Color", children: /* @__PURE__ */ jsx(
2136
+ /* @__PURE__ */ jsx(
2137
+ Select,
2138
+ {
2139
+ label: "Type",
2140
+ list: [
2141
+ { value: "color", label: "Solid color" },
2142
+ { value: "gradient", label: "Gradient" },
2143
+ { value: "image", label: "Image" }
2144
+ ],
2145
+ value: bgMode,
2146
+ onValueChange: (v) => {
2147
+ if (v === "color") onSetBackground({ color: slide.background?.color ?? "#ffffff" });
2148
+ else if (v === "gradient") onSetBackground({ gradient: slide.background?.gradient ?? "linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)" });
2149
+ else onSetBackground({ image: slide.background?.image ?? "", imageFit: slide.background?.imageFit ?? "cover", color: slide.background?.color });
2150
+ }
2151
+ }
2152
+ ),
2153
+ bgMode === "color" && /* @__PURE__ */ jsx(FieldLabel, { label: "Color", children: /* @__PURE__ */ jsx(
2056
2154
  ColorPicker,
2057
2155
  {
2058
2156
  value: slide.background?.color ?? "#ffffff",
2059
- onChange: (c) => onSetBackground({ ...slide.background, color: c })
2157
+ onChange: (c) => onSetBackground({ color: c })
2158
+ }
2159
+ ) }),
2160
+ bgMode === "gradient" && /* @__PURE__ */ jsx(
2161
+ Textarea,
2162
+ {
2163
+ label: "CSS gradient",
2164
+ value: slide.background?.gradient ?? "",
2165
+ onValueChange: (v) => onSetBackground({ gradient: v }),
2166
+ rows: 2
2060
2167
  }
2061
- ) })
2168
+ ),
2169
+ bgMode === "image" && /* @__PURE__ */ jsxs(Fragment, { children: [
2170
+ /* @__PURE__ */ jsx(
2171
+ Textarea,
2172
+ {
2173
+ label: "Image URL",
2174
+ value: slide.background?.image ?? "",
2175
+ onValueChange: (v) => onSetBackground({ ...slide.background, image: v }),
2176
+ rows: 2
2177
+ }
2178
+ ),
2179
+ /* @__PURE__ */ jsx(
2180
+ Select,
2181
+ {
2182
+ label: "Fit",
2183
+ list: [
2184
+ { value: "cover", label: "Cover" },
2185
+ { value: "contain", label: "Contain" },
2186
+ { value: "fill", label: "Fill (stretch)" }
2187
+ ],
2188
+ value: slide.background?.imageFit ?? "cover",
2189
+ onValueChange: (v) => onSetBackground({ ...slide.background, imageFit: v })
2190
+ }
2191
+ )
2192
+ ] })
2062
2193
  ] }) }),
2063
2194
  onSetElementAnimation && /* @__PURE__ */ jsx(Card, { padding: "md", className: "mt-3 !bg-white dark:!bg-zinc-950", children: /* @__PURE__ */ jsx(BuildOrderList, { slide, onSetElementAnimation }) })
2064
2195
  ] })
@@ -2299,6 +2430,24 @@ function TextStyleControls({ element, onPatch }) {
2299
2430
  onValueChange: (v) => setStyle({ align: v })
2300
2431
  }
2301
2432
  ),
2433
+ /* @__PURE__ */ jsx(
2434
+ Select,
2435
+ {
2436
+ label: "Vertical align",
2437
+ list: [
2438
+ { value: "top", label: "Top" },
2439
+ { value: "middle", label: "Middle" },
2440
+ { value: "bottom", label: "Bottom" }
2441
+ ],
2442
+ value: s.verticalAlign ?? "top",
2443
+ onValueChange: (v) => setStyle({ verticalAlign: v })
2444
+ }
2445
+ ),
2446
+ /* @__PURE__ */ jsx(Input, { label: "Line height", type: "number", value: String(s.lineHeight ?? 1.4), onChange: (e) => setStyle({ lineHeight: parseFloat(e.target.value) || 1.4 }) }),
2447
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-4", children: [
2448
+ /* @__PURE__ */ jsx(Switch, { label: "Italic", checked: !!s.italic, onCheckedChange: (v) => setStyle({ italic: v }) }),
2449
+ /* @__PURE__ */ jsx(Switch, { label: "Underline", checked: !!s.underline, onCheckedChange: (v) => setStyle({ underline: v }) })
2450
+ ] }),
2302
2451
  /* @__PURE__ */ jsx(FieldLabel, { label: "Color", children: /* @__PURE__ */ jsx(ColorPicker, { value: s.color ?? "#0f172a", onChange: (c) => setStyle({ color: c }) }) })
2303
2452
  ] });
2304
2453
  }
@@ -2381,6 +2530,7 @@ function ShapeStyleControls({ element, onPatch }) {
2381
2530
  /* @__PURE__ */ jsx(FieldLabel, { label: "Fill", children: /* @__PURE__ */ jsx(ColorPicker, { value: element.fill ?? "#ffffff", onChange: (c) => onPatch({ fill: c }) }) }),
2382
2531
  /* @__PURE__ */ jsx(FieldLabel, { label: "Stroke", children: /* @__PURE__ */ jsx(ColorPicker, { value: element.stroke ?? "#0f172a", onChange: (c) => onPatch({ stroke: c }) }) }),
2383
2532
  /* @__PURE__ */ jsx(Slider, { label: "Stroke width", value: element.strokeWidth ?? 2, onValueChange: (v) => onPatch({ strokeWidth: Number(v) }), min: 0, max: 20, step: 0.5 }),
2533
+ /* @__PURE__ */ jsx(Switch, { label: "Dashed stroke", checked: !!element.dashed, onCheckedChange: (v) => onPatch({ dashed: v }) }),
2384
2534
  (element.shape === "rounded-rect" || element.shape === "rect") && /* @__PURE__ */ jsx(Slider, { label: "Corner radius", value: element.radius ?? 0, onValueChange: (v) => onPatch({ radius: Number(v) }), min: 0, max: 40 })
2385
2535
  ] });
2386
2536
  }
@@ -2400,7 +2550,8 @@ function CodeStyleControls({ element, onPatch }) {
2400
2550
  value: element.codeTheme ?? "auto",
2401
2551
  onValueChange: (v) => onPatch({ codeTheme: v })
2402
2552
  }
2403
- )
2553
+ ),
2554
+ /* @__PURE__ */ jsx(Switch, { label: "Line numbers", checked: element.lineNumbers ?? true, onCheckedChange: (v) => onPatch({ lineNumbers: v }) })
2404
2555
  ] });
2405
2556
  }
2406
2557
  function ChartStyleControls({ element, onPatch }) {
@@ -2881,7 +3032,7 @@ function DeckEditor({
2881
3032
  slide,
2882
3033
  theme: deck.theme,
2883
3034
  editing: true,
2884
- onElementContentChange: (eid, content) => ops.updateElement(slide.id, eid, { content }),
3035
+ onElementContentChange: (eid, content) => ops.updateElement(slide.id, eid, { content, format: "markdown" }),
2885
3036
  onElementSelect: setElementIdSelected,
2886
3037
  selectedElementId: elementIdSelected,
2887
3038
  onElementMove: (eid, x, y) => ops.moveElement(slide.id, eid, x, y),
@@ -2907,6 +3058,7 @@ function DeckEditor({
2907
3058
  onLockToggle: (locked) => slide && elementIdSelected && ops.updateElement(slide.id, elementIdSelected, { locked }),
2908
3059
  onSetTransition: (transition) => slide && ops.setTransition(slide.id, transition),
2909
3060
  onSetBackground: (background) => slide && ops.setBackground(slide.id, background),
3061
+ onSetLayout: (layout) => slide && ops.setLayout(slide.id, layout),
2910
3062
  onSetAnimation: (animation) => slide && elementIdSelected && ops.setAnimation(slide.id, elementIdSelected, animation),
2911
3063
  onSetElementAnimation: (eid, animation) => slide && ops.setAnimation(slide.id, eid, animation)
2912
3064
  }
@@ -2918,6 +3070,6 @@ function DeckEditor({
2918
3070
  );
2919
3071
  }
2920
3072
 
2921
- export { DeckEditor, EditorToolbar, ElementInspector, ImageElementRenderer, PresenterView, ShapeElementRenderer, Slide, SlideRail, SlideThumbnail, SlideViewer, SpeakerNotes, TextElementRenderer, buildSteps, buildsForStep, builtinThemes, chartStarterOption, collectBuilds, darkTheme, deckId, defaultTheme, defineTheme, elementId, isByParagraph, nextId, paragraphReveals, reduce as reduceDeck, resolveTheme, slideId, splitParagraphs, stepDelays, totalBuildSteps, useDeckState, useSlideKeyboard, visibleElementIds, vividTheme };
3073
+ export { DeckEditor, EditorToolbar, ElementInspector, ImageElementRenderer, PRESENTATION_EDITOR_ACTIONS, PresenterView, ShapeElementRenderer, Slide, SlideRail, SlideThumbnail, SlideViewer, SpeakerNotes, TextElementRenderer, buildSteps, buildsForStep, builtinThemes, chartStarterOption, collectBuilds, darkTheme, deckId, defaultTheme, defineTheme, elementId, isByParagraph, nextId, normalizeSlideMarkdown, paragraphReveals, reduce as reduceDeck, resolveTheme, slideId, splitParagraphs, stepDelays, totalBuildSteps, useDeckState, useSlideKeyboard, visibleElementIds, vividTheme };
2922
3074
  //# sourceMappingURL=index.js.map
2923
3075
  //# sourceMappingURL=index.js.map