@particle-academy/fancy-slides 0.6.1 → 0.7.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
@@ -2,6 +2,7 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ReactNode, CSSProperties } from 'react';
3
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';
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. */
@@ -484,6 +485,29 @@ interface TextElementRendererProps {
484
485
  */
485
486
  declare function TextElementRenderer({ element, theme, slideWidthPx, editing, selected, onContentChange, paraReveal, }: TextElementRendererProps): react_jsx_runtime.JSX.Element;
486
487
 
488
+ /**
489
+ * Presentation-tuned toolbar preset for the react-fancy `Editor` used to edit
490
+ * slide text inline. Only commands that round-trip cleanly through the editor's
491
+ * `htmlToMarkdown` output are included — bold (`**`), italic (`*`), heading
492
+ * (`## `), and bullet list (`- `). Box-level typography (alignment, color, font
493
+ * size, line height) is NOT here: those are per-element `TextStyle` properties
494
+ * edited in the ElementInspector, and `text-align`/color spans don't survive the
495
+ * markdown the slide content commits to.
496
+ */
497
+ declare const PRESENTATION_EDITOR_ACTIONS: EditorAction[];
498
+ /**
499
+ * Normalize the `Editor`'s markdown output to the *line-based* paragraph model
500
+ * the slide content commits to. The editor emits a blank line (`\n\n`) between
501
+ * `<p>` blocks, but `splitParagraphs` (and the dark-slide pptx writer) treat a
502
+ * single `\n` as one paragraph / build unit, and a blank interior line as its
503
+ * own (phantom) build. Collapsing runs of newlines to a single `\n` keeps
504
+ * "by paragraph" reveals and per-paragraph pptx builds correct — bullets are
505
+ * already one-per-`\n`, so they're unaffected. The round-trip invariant
506
+ * (`content → Editor → normalize → content` preserves the `\n` paragraph count)
507
+ * is covered by a unit test.
508
+ */
509
+ declare function normalizeSlideMarkdown(md: string): string;
510
+
487
511
  interface ImageElementRendererProps {
488
512
  element: ImageElement;
489
513
  }
@@ -647,4 +671,4 @@ type ChartKind = "bar" | "line" | "pie" | "area" | "scatter";
647
671
  type Option = Record<string, unknown>;
648
672
  declare function chartStarterOption(kind: ChartKind): Option;
649
673
 
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 };
674
+ 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
@@ -2,6 +2,7 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ReactNode, CSSProperties } from 'react';
3
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';
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. */
@@ -484,6 +485,29 @@ interface TextElementRendererProps {
484
485
  */
485
486
  declare function TextElementRenderer({ element, theme, slideWidthPx, editing, selected, onContentChange, paraReveal, }: TextElementRendererProps): react_jsx_runtime.JSX.Element;
486
487
 
488
+ /**
489
+ * Presentation-tuned toolbar preset for the react-fancy `Editor` used to edit
490
+ * slide text inline. Only commands that round-trip cleanly through the editor's
491
+ * `htmlToMarkdown` output are included — bold (`**`), italic (`*`), heading
492
+ * (`## `), and bullet list (`- `). Box-level typography (alignment, color, font
493
+ * size, line height) is NOT here: those are per-element `TextStyle` properties
494
+ * edited in the ElementInspector, and `text-align`/color spans don't survive the
495
+ * markdown the slide content commits to.
496
+ */
497
+ declare const PRESENTATION_EDITOR_ACTIONS: EditorAction[];
498
+ /**
499
+ * Normalize the `Editor`'s markdown output to the *line-based* paragraph model
500
+ * the slide content commits to. The editor emits a blank line (`\n\n`) between
501
+ * `<p>` blocks, but `splitParagraphs` (and the dark-slide pptx writer) treat a
502
+ * single `\n` as one paragraph / build unit, and a blank interior line as its
503
+ * own (phantom) build. Collapsing runs of newlines to a single `\n` keeps
504
+ * "by paragraph" reveals and per-paragraph pptx builds correct — bullets are
505
+ * already one-per-`\n`, so they're unaffected. The round-trip invariant
506
+ * (`content → Editor → normalize → content` preserves the `\n` paragraph count)
507
+ * is covered by a unit test.
508
+ */
509
+ declare function normalizeSlideMarkdown(md: string): string;
510
+
487
511
  interface ImageElementRendererProps {
488
512
  element: ImageElement;
489
513
  }
@@ -647,4 +671,4 @@ type ChartKind = "bar" | "line" | "pie" | "area" | "scatter";
647
671
  type Option = Record<string, unknown>;
648
672
  declare function chartStarterOption(kind: ChartKind): Option;
649
673
 
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 };
674
+ 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, Slider, Switch, Textarea } 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
  }
@@ -2881,7 +2932,7 @@ function DeckEditor({
2881
2932
  slide,
2882
2933
  theme: deck.theme,
2883
2934
  editing: true,
2884
- onElementContentChange: (eid, content) => ops.updateElement(slide.id, eid, { content }),
2935
+ onElementContentChange: (eid, content) => ops.updateElement(slide.id, eid, { content, format: "markdown" }),
2885
2936
  onElementSelect: setElementIdSelected,
2886
2937
  selectedElementId: elementIdSelected,
2887
2938
  onElementMove: (eid, x, y) => ops.moveElement(slide.id, eid, x, y),
@@ -2918,6 +2969,6 @@ function DeckEditor({
2918
2969
  );
2919
2970
  }
2920
2971
 
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 };
2972
+ 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
2973
  //# sourceMappingURL=index.js.map
2923
2974
  //# sourceMappingURL=index.js.map