@bendyline/squisq-editor-react 1.6.0 → 1.6.1

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 (37) hide show
  1. package/README.md +57 -10
  2. package/dist/index.d.ts +401 -76
  3. package/dist/index.js +1334 -930
  4. package/dist/index.js.map +1 -1
  5. package/dist/styles/fa-brands-400-AHOAZHCU.woff2 +0 -0
  6. package/dist/styles/fa-regular-400-VRZYIBIZ.woff2 +0 -0
  7. package/dist/styles/fa-solid-900-MDEYK55F.woff2 +0 -0
  8. package/dist/styles/fa-v4compatibility-ETEVP6IB.woff2 +0 -0
  9. package/dist/styles/index.css +13867 -0
  10. package/package.json +15 -7
  11. package/src/EditorContext.tsx +22 -16
  12. package/src/EditorShell.tsx +68 -27
  13. package/src/OutlinePanel.tsx +26 -4
  14. package/src/PreviewControls.tsx +338 -141
  15. package/src/PreviewPanel.tsx +15 -9
  16. package/src/RawEditor.tsx +10 -4
  17. package/src/Toolbar.tsx +21 -11
  18. package/src/VersionHistoryPanel.tsx +2 -2
  19. package/src/__tests__/codeContextSectionView.test.tsx +95 -0
  20. package/src/__tests__/codeContextZoneManager.test.ts +127 -0
  21. package/src/__tests__/diffContextSections.test.ts +39 -0
  22. package/src/__tests__/editorShellCodeContext.test.tsx +86 -0
  23. package/src/__tests__/editorShellProps.test.tsx +96 -0
  24. package/src/__tests__/previewControls.test.tsx +70 -0
  25. package/src/__tests__/useJsonEditorTokens.test.ts +59 -0
  26. package/src/__tests__/useMediaRecorder.test.ts +17 -0
  27. package/src/codeContext/CodeContextSectionView.tsx +124 -0
  28. package/src/codeContext/CodeContextZoneManager.ts +149 -0
  29. package/src/codeContext/CodeContextZones.tsx +121 -0
  30. package/src/codeContext/diffContextSections.ts +38 -0
  31. package/src/codeContext/types.ts +75 -0
  32. package/src/index.ts +32 -1
  33. package/src/jsonEditor/useJsonEditorTokens.ts +13 -43
  34. package/src/recorder/hooks/useMediaRecorder.ts +9 -10
  35. package/src/styles/code-context.css +155 -0
  36. package/src/styles/editor.css +149 -3
  37. package/src/styles/index.css +1 -0
package/dist/index.d.ts CHANGED
@@ -7,11 +7,12 @@ import { ContentContainer } from '@bendyline/squisq/storage';
7
7
  import { DocumentVersionManager, SaveVersionOptions, SaveVersionResult, PrunePolicy } from '@bendyline/squisq/versions';
8
8
  import * as _tiptap_core from '@tiptap/core';
9
9
  import { Editor, Extension } from '@tiptap/core';
10
+ import * as monaco_editor from 'monaco-editor';
10
11
  import { editor } from 'monaco-editor';
11
12
  import { IconFamily } from '@bendyline/squisq/icons';
12
13
  import { DisplayMode, CaptionStyle } from '@bendyline/squisq-react';
13
- import * as _tiptap_extension_heading from '@tiptap/extension-heading';
14
14
  import { Editor as Editor$1 } from '@tiptap/react';
15
+ import * as _tiptap_extension_heading from '@tiptap/extension-heading';
15
16
  import { Node } from '@tiptap/pm/model';
16
17
  import { SquisqAnnotatedSchema, JsonFormValidator, JsonFormValidationError } from '@bendyline/squisq/jsonForm';
17
18
  import { ImageEditExportFormat, ImageEditVersionManager } from '@bendyline/squisq/imageEdit';
@@ -90,7 +91,13 @@ interface DocumentLinkCandidate {
90
91
  */
91
92
  type DocumentLinkProvider = (query: string) => Promise<DocumentLinkCandidate[]>;
92
93
  type EditorView = 'raw' | 'wysiwyg' | 'preview';
93
- type EditorTheme = 'light' | 'dark';
94
+ /**
95
+ * Light/dark chrome mode for the editor shell (toolbar, tabs, status bar,
96
+ * side panes). This is the editor's *UI color scheme* — distinct from a
97
+ * Squisq `Theme` object, which styles the rendered document. Renamed from
98
+ * the former `EditorTheme` to remove that ambiguity.
99
+ */
100
+ type EditorColorScheme = 'light' | 'dark';
94
101
  /**
95
102
  * Document layout mode. `'document'` shows the whole markdown document in
96
103
  * the active view (the historical behavior). `'block'` is the
@@ -126,8 +133,8 @@ interface EditorState {
126
133
  parseError: string | null;
127
134
  /** Whether a parse is pending */
128
135
  isParsing: boolean;
129
- /** Current color theme */
130
- theme: EditorTheme;
136
+ /** Current light/dark chrome color scheme for the editor shell. */
137
+ colorScheme: EditorColorScheme;
131
138
  /** Operating mode — 'markdown' for the full shell, 'code' for Monaco-only. */
132
139
  editorMode: EditorMode;
133
140
  /** Monaco language ID for the Raw editor. */
@@ -237,8 +244,8 @@ interface EditorActions {
237
244
  setTiptapEditor: (editor: Editor | null) => void;
238
245
  /** Register / unregister the Monaco editor instance (called by RawEditor) */
239
246
  setMonacoEditor: (editor: MonacoEditor | null) => void;
240
- /** Set the color theme */
241
- setTheme: (theme: EditorTheme) => void;
247
+ /** Set the light/dark chrome color scheme for the editor shell. */
248
+ setColorScheme: (colorScheme: EditorColorScheme) => void;
242
249
  /** Show or hide the inline preview gutter at runtime (driven by the View menu). */
243
250
  setInlinePreviewVisible: (visible: boolean) => void;
244
251
  /** Show or hide the bottom status bar at runtime (driven by the View menu). */
@@ -340,8 +347,8 @@ interface EditorProviderProps {
340
347
  initialView?: EditorView;
341
348
  /** Article ID used when generating the Doc */
342
349
  articleId?: string;
343
- /** Color theme */
344
- theme?: EditorTheme;
350
+ /** Light/dark chrome color scheme for the editor shell. */
351
+ colorScheme?: EditorColorScheme;
345
352
  /**
346
353
  * Workspace-scoped `ContentContainer` for this document — the folder
347
354
  * holding the doc, its `_files/` sidecar, sibling documents, and any
@@ -476,7 +483,83 @@ interface ViewPreferences {
476
483
  /** Document vs. block-at-a-time layout. */
477
484
  layoutMode?: LayoutMode;
478
485
  }
479
- declare function EditorProvider({ initialMarkdown, initialView, articleId, theme: initialTheme, workspaceContainer, allowVersioning, versionBasename, versioningPrunePolicy, versioningAutoSaveIdleMs, onSaveVersion, mediaProvider, imageDisplayMode, mentionProvider, documentLinkProvider, allowRecording, fileName, language, inlinePreview, showStatusBar, outline, blockTags, themeInheritance, layoutMode, viewPreferences, onViewPreferencesChange, children, }: EditorProviderProps): react_jsx_runtime.JSX.Element;
486
+ declare function EditorProvider({ initialMarkdown, initialView, articleId, colorScheme: initialColorScheme, workspaceContainer, allowVersioning, versionBasename, versioningPrunePolicy, versioningAutoSaveIdleMs, onSaveVersion, mediaProvider, imageDisplayMode, mentionProvider, documentLinkProvider, allowRecording, fileName, language, inlinePreview, showStatusBar, outline, blockTags, themeInheritance, layoutMode, viewPreferences, onViewPreferencesChange, children, }: EditorProviderProps): react_jsx_runtime.JSX.Element;
487
+
488
+ /**
489
+ * Host-supplied context sections rendered INSIDE the Monaco (raw/code)
490
+ * surface: collapsible markdown blurbs injected above anchor lines, plus an
491
+ * optional file-top summary. Squisq stays host-agnostic — anchors are plain
492
+ * line numbers, ids are opaque strings, links are intercepted via callback.
493
+ *
494
+ * Accessibility note: Monaco marks its view-zone layer `aria-hidden`, so
495
+ * sections are invisible to screen readers in v1.
496
+ */
497
+ /**
498
+ * One host-supplied markdown blurb anchored above a line of the code buffer.
499
+ * Rendered as a compact one-line strip that expands in place to the full
500
+ * markdown body.
501
+ */
502
+ interface CodeContextSection {
503
+ /**
504
+ * Stable identity used to reconcile zones across prop updates — e.g. a
505
+ * symbol id like 'resolveImportEdges@60'. Zones are diffed by id: same id +
506
+ * new line moves the zone; same id + new markdown re-renders in place; a
507
+ * new id creates a new zone.
508
+ */
509
+ id: string;
510
+ /**
511
+ * 1-based line the section renders ABOVE. Out-of-range values are clamped
512
+ * by Monaco. In editable buffers the zone rides Monaco's whitespace
513
+ * semantics — it shifts as lines are inserted/deleted above it and
514
+ * collapses onto the previous line if its anchor lines are deleted. Squisq
515
+ * never re-derives anchors from edits; the host re-supplies lines when it
516
+ * re-analyzes the file.
517
+ */
518
+ line: number;
519
+ /**
520
+ * Compact markdown for the collapsed strip. Rendered on one line (block
521
+ * structure flattened, overflow ellipsized). Links work here too and go
522
+ * through `onLinkClick`.
523
+ */
524
+ summaryMarkdown: string;
525
+ /**
526
+ * Full markdown body shown when expanded. Omit while still loading — the
527
+ * expanded view shows a muted loading row and fills in when a later prop
528
+ * update supplies it.
529
+ */
530
+ markdown?: string;
531
+ /** Start expanded. The user's toggle wins after first interaction. Default false. */
532
+ defaultExpanded?: boolean;
533
+ }
534
+ /** The full context dictionary passed to `EditorShell.codeContext`. */
535
+ interface CodeContext {
536
+ /**
537
+ * Section pinned above line 1 (file summary). Rendered before any line-1
538
+ * `sections` entry. `line` is implicit.
539
+ */
540
+ fileTop?: Omit<CodeContextSection, 'line'>;
541
+ /** Line-anchored sections. Array order is preserved for equal lines. */
542
+ sections?: CodeContextSection[];
543
+ /**
544
+ * Extra URI schemes section links may use (e.g. `['gezel-nav']`).
545
+ * http/https/mailto/tel are always allowed; executable schemes
546
+ * (javascript:, data:) are never allowed regardless.
547
+ */
548
+ linkSchemes?: readonly string[];
549
+ /**
550
+ * Intercepts link clicks inside sections, receiving the href exactly as
551
+ * authored in the markdown. Return `false` to let the browser's default
552
+ * navigation proceed; any other return (or void) suppresses it. Fragment
553
+ * links of the form `#L<digits>` are handled natively by squisq (reveal
554
+ * that line in the editor) and never reach this callback. When omitted:
555
+ * http(s)/mailto links open normally, custom-scheme links do nothing.
556
+ */
557
+ onLinkClick?: (href: string, meta: {
558
+ sectionId: string;
559
+ }) => boolean | undefined;
560
+ /** Notified on expand/collapse — lets hosts lazy-load bodies on first expand. */
561
+ onToggleSection?: (sectionId: string, expanded: boolean) => void;
562
+ }
480
563
 
481
564
  interface EditorShellProps {
482
565
  /** Initial markdown content */
@@ -490,8 +573,13 @@ interface EditorShellProps {
490
573
  basePath?: string;
491
574
  /** Called when markdown source changes */
492
575
  onChange?: (source: string) => void;
493
- /** Color theme: 'light' or 'dark' (default: 'light') */
494
- theme?: 'light' | 'dark';
576
+ /**
577
+ * Light/dark chrome color scheme for the editor shell — toolbar, tabs,
578
+ * status bar, and side panes (default: `'light'`). This is the editor's
579
+ * UI mode, **not** a Squisq `Theme` object; the rendered document's
580
+ * styling is controlled separately via `themeOverride` / `Doc.themeId`.
581
+ */
582
+ colorScheme?: 'light' | 'dark';
495
583
  /** Additional class name */
496
584
  className?: string;
497
585
  /** CSS height for the shell container (default: '100vh') */
@@ -586,6 +674,13 @@ interface EditorShellProps {
586
674
  * (Slack, Discord). When omitted, the editor behaves normally.
587
675
  */
588
676
  submitOnEnter?: () => void;
677
+ /**
678
+ * Host-supplied context dictionary rendered inside the Monaco (raw / code)
679
+ * surface: collapsible markdown sections injected above anchor lines, plus
680
+ * an optional file-top summary. See {@link CodeContext}. Ignored in
681
+ * WYSIWYG / preview / image surfaces.
682
+ */
683
+ codeContext?: CodeContext;
589
684
  /**
590
685
  * Let the WYSIWYG editing surface fill its container instead of rendering
591
686
  * as a centered 800px "page" column. Useful when embedding in chat
@@ -744,9 +839,11 @@ interface EditorShellProps {
744
839
  */
745
840
  outline?: boolean;
746
841
  /**
747
- * Width in pixels for the outline pane. Defaults to 240. Only takes
748
- * effect when {@link EditorShellProps.outline} is true (or the View
749
- * menu has toggled it on).
842
+ * Fixed width in pixels for the outline pane. When omitted, the pane sizes
843
+ * responsively never narrower than 260px, growing with the window and
844
+ * capped at 460px — so it stretches out when there's horizontal space to
845
+ * spare. Only takes effect when {@link EditorShellProps.outline} is true (or
846
+ * the View menu has toggled it on).
750
847
  */
751
848
  outlineWidth?: number;
752
849
  /**
@@ -791,7 +888,18 @@ interface EditorShellProps {
791
888
  * Complete markdown editor shell with toolbar, view switcher, and three
792
889
  * editing modes: Raw (Monaco), WYSIWYG (Tiptap), and Preview.
793
890
  */
794
- declare function EditorShell({ initialMarkdown, initialView, articleId, basePath, onChange, theme, className, height, minHeight, maxHeight, mediaProvider, workspaceContainer, container, allowVersioning, versionBasename, versioningPrunePolicy, versioningAutoSaveIdleMs, onSaveVersion, showFilesToggle, toolbarSlotLeft, toolbarSlotAfterActions, toolbarSlotRight, showPlayTab, submitOnEnter, fullWidth, uxFont, thinMargins, showStatusBar, imageDisplayMode, fileName, language, mentionProvider, documentLinkProvider, allowRecording, placeholder, readOnly, imageSrc, imageAlt, imageMode, imageEditorContainer, onImageExport, inlinePreview, inlinePreviewWidth, outline, outlineWidth, blockTags, themeInheritance, viewPreferences, onViewPreferencesChange, themeOverride, }: EditorShellProps): react_jsx_runtime.JSX.Element;
891
+ declare function EditorShell({ initialMarkdown, initialView, articleId, basePath, onChange, colorScheme, className, height, minHeight, maxHeight, mediaProvider, workspaceContainer, container, allowVersioning, versionBasename, versioningPrunePolicy, versioningAutoSaveIdleMs, onSaveVersion, showFilesToggle, toolbarSlotLeft, toolbarSlotAfterActions, toolbarSlotRight, showPlayTab, submitOnEnter, codeContext, fullWidth, uxFont, thinMargins, showStatusBar, imageDisplayMode, fileName, language, mentionProvider, documentLinkProvider, allowRecording, placeholder, readOnly, imageSrc, imageAlt, imageMode, imageEditorContainer, onImageExport, inlinePreview, inlinePreviewWidth, outline, outlineWidth, blockTags, themeInheritance, viewPreferences, onViewPreferencesChange, themeOverride, }: EditorShellProps): react_jsx_runtime.JSX.Element;
892
+
893
+ /**
894
+ * Bridges a host-supplied {@link CodeContext} onto the live Monaco editor:
895
+ * owns a {@link CodeContextZoneManager} per editor instance, reconciles zones
896
+ * when the context prop changes, and portals a `CodeContextSectionView` into
897
+ * each zone's dom node. Mounted by EditorShell in code mode; also exported
898
+ * for hosts composing a custom shell around `RawEditor`.
899
+ */
900
+ declare function CodeContextZones({ options }: {
901
+ options: CodeContext;
902
+ }): react_jsx_runtime.JSX.Element | null;
795
903
 
796
904
  /**
797
905
  * One entry in a {@link FolderView} — a file or subfolder. Host-defined
@@ -1080,8 +1188,14 @@ declare function ImageViewer({ src, alt, className, theme }: ImageViewerProps):
1080
1188
  * Syncs changes back to EditorContext on every keystroke (debounced).
1081
1189
  */
1082
1190
  interface RawEditorProps {
1083
- /** Monaco editor theme (default: 'vs-dark') */
1084
- theme?: string;
1191
+ /**
1192
+ * Monaco editor theme name (default: `'vs'`). Accepts Monaco's built-in
1193
+ * theme ids (`'vs'`, `'vs-dark'`, `'hc-black'`) — which are transparently
1194
+ * mapped to the Squisq-tinted variants — or any custom theme registered
1195
+ * via `monaco.editor.defineTheme`. This is the *code editor* color
1196
+ * theme, distinct from the shell's light/dark `colorScheme`.
1197
+ */
1198
+ monacoTheme?: string;
1085
1199
  /** Show minimap (default: false) */
1086
1200
  minimap?: boolean;
1087
1201
  /** Font size in pixels (default: 14) */
@@ -1102,7 +1216,7 @@ interface RawEditorProps {
1102
1216
  * Raw markdown editor using Monaco Editor.
1103
1217
  * Binds to the shared EditorContext for source synchronization.
1104
1218
  */
1105
- declare function RawEditor({ theme, minimap, fontSize, wordWrap, className, submitOnEnter, readOnly, }: RawEditorProps): react_jsx_runtime.JSX.Element;
1219
+ declare function RawEditor({ monacoTheme, minimap, fontSize, wordWrap, className, submitOnEnter, readOnly, }: RawEditorProps): react_jsx_runtime.JSX.Element;
1106
1220
 
1107
1221
  /**
1108
1222
  * WysiwygEditor
@@ -1337,9 +1451,85 @@ interface ThemePickerProps {
1337
1451
  }
1338
1452
  declare function ThemePicker({ value, onChange, includeDefault, variant, ariaLabel, customThemes, onCreateCustom, onEditCustom, onDeleteCustom, }: ThemePickerProps): react_jsx_runtime.JSX.Element;
1339
1453
 
1454
+ interface CustomThemeContextValue {
1455
+ /** Themes inlined into the current doc's frontmatter. */
1456
+ docThemes: Theme[];
1457
+ /** Themes the user has saved to their browser-local library. */
1458
+ libraryThemes: Theme[];
1459
+ /**
1460
+ * Convenience: the union of doc + library, with library entries skipped
1461
+ * when a doc theme of the same id already exists. Used by the picker so a
1462
+ * single "Custom" section covers both sources without duplicates.
1463
+ */
1464
+ allThemes: Theme[];
1465
+ /**
1466
+ * Persist a theme into the current doc. Replaces any existing entry with
1467
+ * the same `id`. Triggers the host's `onDocThemesChange` so the doc's
1468
+ * frontmatter ends up updated.
1469
+ */
1470
+ upsertDocTheme: (theme: Theme) => void;
1471
+ /** Persist a theme into the user's library (replaces by id). */
1472
+ upsertLibraryTheme: (theme: Theme) => void;
1473
+ /** Remove a doc-level theme by id. */
1474
+ removeDocTheme: (id: string) => void;
1475
+ /** Remove a library theme by id. */
1476
+ removeLibraryTheme: (id: string) => void;
1477
+ /**
1478
+ * Apply a theme to the current doc. If the theme is already in the doc,
1479
+ * this is a no-op. If it's only in the library, it's copied into the doc
1480
+ * so SSR / export work. Returns the resolved definition so callers can
1481
+ * also write the `squisq-theme` selection.
1482
+ */
1483
+ applyTheme: (theme: Theme) => Theme;
1484
+ }
1485
+ interface CustomThemeProviderProps {
1486
+ /** The current doc's custom themes (from `Doc.customThemes`). */
1487
+ docThemes: Theme[];
1488
+ /**
1489
+ * Callback the host uses to persist a new doc-theme list back into the doc
1490
+ * (e.g. by writing the encoded payload into the markdown's
1491
+ * `squisq-custom-themes` frontmatter key).
1492
+ */
1493
+ onDocThemesChange: (next: Theme[]) => void;
1494
+ children: ReactNode;
1495
+ }
1496
+ declare function CustomThemeProvider({ docThemes, onDocThemesChange, children, }: CustomThemeProviderProps): react_jsx_runtime.JSX.Element;
1497
+ /**
1498
+ * Hook returning the current context. Returns null when no provider is
1499
+ * mounted — callers can degrade to "no custom themes" rather than throwing,
1500
+ * since the feature is optional.
1501
+ */
1502
+ declare function useCustomThemes(): CustomThemeContextValue | null;
1503
+
1504
+ /**
1505
+ * useDocCustomThemes — read + persist the active doc's custom themes (the
1506
+ * list inlined into the doc's frontmatter under `squisq-custom-themes`).
1507
+ *
1508
+ * The theme analog of `useDocCustomTemplates`. Both the CustomThemeProvider
1509
+ * and any manager surface need the same two things: the current
1510
+ * `Doc.customThemes` array and a way to write a new list back into the
1511
+ * markdown source. Centralizing the frontmatter encoding here keeps call
1512
+ * sites from drifting.
1513
+ */
1514
+
1515
+ interface DocCustomThemes {
1516
+ /** Custom themes inlined in the active doc's frontmatter. */
1517
+ docThemes: Theme[];
1518
+ /**
1519
+ * Persist a new custom-themes list back into the markdown source's
1520
+ * frontmatter so the doc round-trips through save/load. The whole list is
1521
+ * encoded as a single compact JSON string per the flat YAML frontmatter
1522
+ * parser's constraint.
1523
+ */
1524
+ onDocThemesChange: (next: Theme[]) => void;
1525
+ }
1526
+ declare function useDocCustomThemes(): DocCustomThemes;
1527
+
1340
1528
  /** Where a saved theme lands — mirrors `DesignerSaveTarget` for templates. */
1341
1529
  type ThemeSaveTarget = 'doc' | 'library';
1342
1530
 
1531
+ /** Caption selection: off, or one of the two enabled styles. */
1532
+ type CaptionMode = 'off' | CaptionStyle;
1343
1533
  interface PreviewSettings {
1344
1534
  activePreset: ViewportPreset;
1345
1535
  setSelectedPreset: (preset: ViewportPreset | null) => void;
@@ -1351,8 +1541,14 @@ interface PreviewSettings {
1351
1541
  activeTheme: Theme;
1352
1542
  activeTransformStyle: string;
1353
1543
  setSelectedTransformStyle: (id: string | null) => void;
1544
+ /** The caption style used when captions are enabled. */
1354
1545
  activeCaptionStyle: CaptionStyle;
1355
- setSelectedCaptionStyle: (style: CaptionStyle | null) => void;
1546
+ /** Whether captions are shown at all (the 'off' arm of the tri-state). */
1547
+ activeCaptionsEnabled: boolean;
1548
+ /** Set the caption mode: 'off' hides captions, 'standard'/'social' enable
1549
+ * that style. The single entry point so the toggle buttons persist in one
1550
+ * frontmatter write. */
1551
+ setCaptionMode: (mode: CaptionMode) => void;
1356
1552
  /** User-authored themes (doc + browser library) for the picker's "Custom" group. */
1357
1553
  customThemes: Theme[];
1358
1554
  /** Open the custom-theme designer for a theme (or null to create a new one). */
@@ -1385,9 +1581,31 @@ interface PreviewSettingsProviderProps {
1385
1581
  declare function PreviewSettingsProvider({ doc, children, themeOverride, }: PreviewSettingsProviderProps): react_jsx_runtime.JSX.Element;
1386
1582
  /**
1387
1583
  * Inline preview controls rendered in the main toolbar row.
1388
- * On narrow viewports, collapses into a single settings button with a dropdown.
1584
+ *
1585
+ * Collapse is *progressive* (a priority-plus pattern): rather than switch the
1586
+ * whole row in and out at a fixed window-width breakpoint, the controls
1587
+ * measure how many of them actually fit in the width the toolbar gives them
1588
+ * and keep that many inline, folding the rest — from the low-priority end of
1589
+ * {@link CONTROL_KEYS} — into a single settings (gear) button's popover. As
1590
+ * the toolbar widens or narrows, controls migrate one at a time between the
1591
+ * inline row and the menu, so the available space is always well used and the
1592
+ * row never wraps onto a second line.
1389
1593
  */
1390
1594
  declare function PreviewToolbarControls(): react_jsx_runtime.JSX.Element;
1595
+ /**
1596
+ * Segmented display-mode switch (Video / Slideshow / Document / Page) rendered
1597
+ * as four connected buttons on the left of the Play toolbar — the prominent,
1598
+ * one-click counterpart to the old "Mode:" dropdown. Reads and writes the same
1599
+ * `activeDisplayMode` in preview settings.
1600
+ */
1601
+ declare function PreviewModeSwitch(): react_jsx_runtime.JSX.Element;
1602
+ /**
1603
+ * Segmented aspect-ratio switch (16:9 / 1:1 / 9:16 / 4:3) rendered as connected
1604
+ * icon buttons on the left of the Play toolbar, next to the mode switch — the
1605
+ * one-click counterpart to the old "Format:" dropdown. Reads and writes the
1606
+ * same `activePreset` in preview settings.
1607
+ */
1608
+ declare function PreviewFormatSwitch(): react_jsx_runtime.JSX.Element;
1391
1609
 
1392
1610
  /**
1393
1611
  * ViewSwitcher
@@ -1412,6 +1630,10 @@ interface ToolbarProps {
1412
1630
  onToggleFiles?: () => void;
1413
1631
  /** Content rendered at the left edge of the toolbar, before the view tabs. */
1414
1632
  slotLeft?: ReactNode;
1633
+ /** Content rendered immediately after the view tabs, on the left side of the
1634
+ * toolbar (before the formatting controls). Used for the preview mode
1635
+ * switch in Play view. */
1636
+ slotAfterTabs?: ReactNode;
1415
1637
  /** Content rendered after the formatting controls (in the middle area). */
1416
1638
  slotAfterActions?: ReactNode;
1417
1639
  /** Content rendered at the rightmost end of the toolbar, after all other elements. */
@@ -1428,7 +1650,7 @@ interface ToolbarProps {
1428
1650
  * - WYSIWYG: calls Tiptap chain commands (toggleBold, etc.)
1429
1651
  * - Raw: appends markdown syntax to the source
1430
1652
  */
1431
- declare function Toolbar({ className, showFiles, onToggleFiles, slotLeft, slotAfterActions, slotRight, showPlayTab, }: ToolbarProps): react_jsx_runtime.JSX.Element;
1653
+ declare function Toolbar({ className, showFiles, onToggleFiles, slotLeft, slotAfterTabs, slotAfterActions, slotRight, showPlayTab, }: ToolbarProps): react_jsx_runtime.JSX.Element;
1432
1654
 
1433
1655
  /**
1434
1656
  * VersionHistoryPanel
@@ -1457,17 +1679,12 @@ declare function VersionHistoryPanel(): react_jsx_runtime.JSX.Element | null;
1457
1679
  */
1458
1680
  declare function ViewMenuPanel(): react_jsx_runtime.JSX.Element;
1459
1681
 
1460
- /**
1461
- * OutlinePanel
1462
- *
1463
- * Left-side companion to the InlinePreviewGutter. Renders a hierarchical
1464
- * tree of the document's headings (h1 → h2 → h3 …) so the structure is
1465
- * graspable at a glance and the user can jump to any section. Works in
1466
- * BOTH the WYSIWYG and Markdown editor views — view-specific positioning
1467
- * lives in `useHeadingLayout`.
1468
- */
1469
1682
  interface OutlinePanelProps {
1470
- /** Width of the pane in pixels (default: 240). */
1683
+ /**
1684
+ * Fixed width of the pane in pixels. When omitted, the pane sizes
1685
+ * responsively from `--squisq-outline-width` (falling back to
1686
+ * {@link OUTLINE_RESPONSIVE_WIDTH}) so it stretches on wider screens.
1687
+ */
1471
1688
  width?: number;
1472
1689
  /** Optional CSS class for the outer container. */
1473
1690
  className?: string;
@@ -1868,6 +2085,155 @@ declare function processTextFile(file: File): Promise<string>;
1868
2085
  */
1869
2086
  declare function processTextFiles(files: File[]): Promise<string>;
1870
2087
 
2088
+ /**
2089
+ * useMonacoLoader
2090
+ *
2091
+ * Idempotently dynamic-imports `monaco-editor` and points the
2092
+ * `@monaco-editor/react` singleton loader at the bundled copy. Replaces
2093
+ * the historical top-of-module `import * as monaco from 'monaco-editor';
2094
+ * loader.config({ monaco })` pattern, which forced every consumer of
2095
+ * `@bendyline/squisq-editor-react` — including ones that only import
2096
+ * `JsonEditor` or a type — to drag in monaco's ~9MB worth of language
2097
+ * services and workers at module evaluation time.
2098
+ *
2099
+ * Hosts that want the smallest possible bundle can keep aliasing
2100
+ * `monaco-editor` to a slim entry as before; the behavior is identical
2101
+ * once the dynamic import settles.
2102
+ *
2103
+ * The promise is cached at module scope so the first subscriber
2104
+ * anywhere in the app pays the import cost and every later subscriber
2105
+ * reuses the same settled value.
2106
+ */
2107
+ interface UseMonacoLoaderResult {
2108
+ /** The monaco namespace once loaded, or `null` while the import is in flight. */
2109
+ monaco: typeof monaco_editor | null;
2110
+ /** Flips to `true` after the import settles. Gate `<Editor>` / `<DiffEditor>` renders on this. */
2111
+ ready: boolean;
2112
+ }
2113
+ /**
2114
+ * Subscribe to the lazy-loaded monaco namespace. The first caller
2115
+ * triggers `import('monaco-editor')` and configures the
2116
+ * `@monaco-editor/react` loader; subsequent callers receive the same
2117
+ * cached value.
2118
+ */
2119
+ declare function useMonacoLoader(): UseMonacoLoaderResult;
2120
+
2121
+ interface CustomTemplateContextValue {
2122
+ /** Templates inlined into the current doc's frontmatter. */
2123
+ docTemplates: CustomTemplateDefinition[];
2124
+ /** Templates the user has saved to their browser-local library. */
2125
+ libraryTemplates: CustomTemplateDefinition[];
2126
+ /**
2127
+ * Convenience: the union of doc + library, with library entries
2128
+ * skipped when a doc template of the same name already exists.
2129
+ * Used by the picker so a single "Custom" section covers both
2130
+ * sources without duplicates.
2131
+ */
2132
+ allTemplates: CustomTemplateDefinition[];
2133
+ /**
2134
+ * Persist a template into the current doc. Replaces any existing
2135
+ * entry with the same `name`. Triggers the host's
2136
+ * `onDocTemplatesChange` so the doc's frontmatter ends up updated.
2137
+ */
2138
+ upsertDocTemplate: (def: CustomTemplateDefinition) => void;
2139
+ /** Persist a template into the user's library (replaces by name). */
2140
+ upsertLibraryTemplate: (def: CustomTemplateDefinition) => void;
2141
+ /** Remove a doc-level template by name. */
2142
+ removeDocTemplate: (name: string) => void;
2143
+ /** Remove a library template by name. */
2144
+ removeLibraryTemplate: (name: string) => void;
2145
+ /**
2146
+ * Apply a template to the current doc. If the template is already in
2147
+ * the doc, this is a no-op. If it's only in the library, it's
2148
+ * copied into the doc so SSR / export work. Returns the resolved
2149
+ * definition so callers can also write the heading annotation.
2150
+ */
2151
+ applyTemplate: (def: CustomTemplateDefinition) => CustomTemplateDefinition;
2152
+ }
2153
+ interface CustomTemplateProviderProps {
2154
+ /** The current doc's custom templates (from `Doc.customTemplates`). */
2155
+ docTemplates: CustomTemplateDefinition[];
2156
+ /**
2157
+ * Callback the host uses to persist a new doc-template list back
2158
+ * into the doc (e.g. by writing the encoded payload into the
2159
+ * markdown's `squisq-custom-templates` frontmatter key).
2160
+ */
2161
+ onDocTemplatesChange: (next: CustomTemplateDefinition[]) => void;
2162
+ children: ReactNode;
2163
+ }
2164
+ declare function CustomTemplateProvider({ docTemplates, onDocTemplatesChange, children, }: CustomTemplateProviderProps): react_jsx_runtime.JSX.Element;
2165
+ /**
2166
+ * Hook returning the current context. Returns null when no provider is
2167
+ * mounted — callers can degrade to "no custom templates" rather than
2168
+ * throwing, since the feature is optional.
2169
+ */
2170
+ declare function useCustomTemplates(): CustomTemplateContextValue | null;
2171
+
2172
+ /**
2173
+ * useDocCustomTemplates — read + persist the active doc's custom
2174
+ * templates (the list inlined into the doc's frontmatter under
2175
+ * `squisq-custom-templates`).
2176
+ *
2177
+ * Both the WYSIWYG editor and the standalone Custom Layout Manager need
2178
+ * the same two things: the current `Doc.customTemplates` array and a way
2179
+ * to write a new list back into the markdown source. Centralizing the
2180
+ * frontmatter encoding here keeps the two call sites from drifting.
2181
+ */
2182
+
2183
+ interface DocCustomTemplates {
2184
+ /** Custom templates inlined in the active doc's frontmatter. */
2185
+ docTemplates: CustomTemplateDefinition[];
2186
+ /**
2187
+ * Persist a new custom-templates list back into the markdown source's
2188
+ * frontmatter so the doc round-trips through save/load. The whole list
2189
+ * is encoded as a single base64-JSON string per the flat YAML
2190
+ * frontmatter parser's constraint.
2191
+ */
2192
+ onDocTemplatesChange: (next: CustomTemplateDefinition[]) => void;
2193
+ }
2194
+ declare function useDocCustomTemplates(): DocCustomTemplates;
2195
+
2196
+ /**
2197
+ * Read diagram nodes + edges from the live Tiptap state.
2198
+ *
2199
+ * For a given parent heading position, walks the diagram section's child
2200
+ * headings, builds synthetic `Block` objects from their text + Pandoc
2201
+ * attributes, runs `computeDiagramLayout` from core to fill in missing
2202
+ * positions, and returns the result in the shape React Flow consumes.
2203
+ *
2204
+ * The hook re-derives on every editor transaction — no caching layer
2205
+ * means there's nothing to invalidate when the user types or the markdown
2206
+ * is reloaded from disk.
2207
+ */
2208
+
2209
+ interface DiagramRFNode {
2210
+ id: string;
2211
+ position: {
2212
+ x: number;
2213
+ y: number;
2214
+ };
2215
+ data: {
2216
+ label: string;
2217
+ };
2218
+ type?: string;
2219
+ /** Per-node width override (from the heading's `w=` Pandoc param). */
2220
+ width?: number;
2221
+ /** Per-node height override (from the heading's `h=` Pandoc param). */
2222
+ height?: number;
2223
+ }
2224
+ interface DiagramRFEdge {
2225
+ id: string;
2226
+ source: string;
2227
+ target: string;
2228
+ label?: string;
2229
+ }
2230
+ interface DiagramData {
2231
+ nodes: DiagramRFNode[];
2232
+ edges: DiagramRFEdge[];
2233
+ warnings: string[];
2234
+ }
2235
+ declare function useDiagramData(editor: Editor$1, parentPos: number): DiagramData;
2236
+
1871
2237
  /**
1872
2238
  * Tiptap Bridge
1873
2239
  *
@@ -1955,47 +2321,6 @@ interface DiagramExtensionOptions {
1955
2321
  }
1956
2322
  declare const DiagramExtension: Extension<DiagramExtensionOptions, any>;
1957
2323
 
1958
- /**
1959
- * Read diagram nodes + edges from the live Tiptap state.
1960
- *
1961
- * For a given parent heading position, walks the diagram section's child
1962
- * headings, builds synthetic `Block` objects from their text + Pandoc
1963
- * attributes, runs `computeDiagramLayout` from core to fill in missing
1964
- * positions, and returns the result in the shape React Flow consumes.
1965
- *
1966
- * The hook re-derives on every editor transaction — no caching layer
1967
- * means there's nothing to invalidate when the user types or the markdown
1968
- * is reloaded from disk.
1969
- */
1970
-
1971
- interface DiagramRFNode {
1972
- id: string;
1973
- position: {
1974
- x: number;
1975
- y: number;
1976
- };
1977
- data: {
1978
- label: string;
1979
- };
1980
- type?: string;
1981
- /** Per-node width override (from the heading's `w=` Pandoc param). */
1982
- width?: number;
1983
- /** Per-node height override (from the heading's `h=` Pandoc param). */
1984
- height?: number;
1985
- }
1986
- interface DiagramRFEdge {
1987
- id: string;
1988
- source: string;
1989
- target: string;
1990
- label?: string;
1991
- }
1992
- interface DiagramData {
1993
- nodes: DiagramRFNode[];
1994
- edges: DiagramRFEdge[];
1995
- warnings: string[];
1996
- }
1997
- declare function useDiagramData(editor: Editor$1, parentPos: number): DiagramData;
1998
-
1999
2324
  type DiagramCommand = {
2000
2325
  kind: 'moveNode';
2001
2326
  nodeId: string;
@@ -2231,8 +2556,8 @@ type RecorderSource = 'mic' | 'camera' | 'screen' | 'screen+mic';
2231
2556
  /** Discriminated state describing what the recorder is currently doing. */
2232
2557
  type RecorderState = 'idle' | 'requesting' | 'ready' | 'recording' | 'stopping' | 'stopped' | 'error';
2233
2558
  interface UseMediaRecorderOptions {
2234
- /** Which capture pipeline to use. */
2235
- source: RecorderSource;
2559
+ /** Which capture pipeline to use (default: `'mic'`). */
2560
+ source?: RecorderSource;
2236
2561
  /**
2237
2562
  * Preferred MIME type override. When the browser supports it, this
2238
2563
  * wins over the default candidate list. When unset (or unsupported),
@@ -2309,7 +2634,7 @@ interface UseMediaRecorderResult {
2309
2634
  * (e.g. headless tests) can resolve a format up front.
2310
2635
  */
2311
2636
  declare function getCaptureKind(source: RecorderSource): CaptureKind;
2312
- declare function useMediaRecorder(options: UseMediaRecorderOptions): UseMediaRecorderResult;
2637
+ declare function useMediaRecorder(options?: UseMediaRecorderOptions): UseMediaRecorderResult;
2313
2638
 
2314
2639
  interface RecorderModalProps {
2315
2640
  /** Required — recordings are written here. */
@@ -2732,4 +3057,4 @@ interface UseImageEditorReturn {
2732
3057
  }
2733
3058
  declare function useImageEditor(options: UseImageEditorOptions): UseImageEditorReturn;
2734
3059
 
2735
- export { ALL_EMOJIS, BlockCardView, type BlockCardViewProps, type BlockNavigator, BlockPropertiesPopover, type BlockPropertiesPopoverProps, type BlockRange, type BlockSlice, type CameraStreamOptions, type CanvasRect, type CaptureKind, DiagramCanvas, type DiagramCommand, type DiagramData, DiagramExtension, type DiagramRFEdge, type DiagramRFNode, DiagramWidget, type DirectionModel, type DocumentLinkCandidate, type DocumentLinkProvider, DocumentSettingsDialog, type DocumentSettingsDialogProps, type DragContentType, type DropTarget, DropZoneOverlay, type DropZoneOverlayProps, EMOJI_CATEGORIES, EMPTY_TRANSITION, type EditorActions, type EditorContextValue, type EditorMode, EditorProvider, type EditorProviderProps, EditorShell, type EditorShellProps, type EditorState, type EditorTheme, type EditorView, type EmojiCategory, type EmojiEntry, EmojiPicker, type EmojiPickerProps, type FileCategory, type FileKind, type FolderEntry, FolderView, type FolderViewProps, HeadingWithTemplate, type ImageDisplayMode, ImageEditor, type ImageEditorAction, type ImageEditorProps, type ImageEditorState, type ImageEditorTool, ImageViewer, type ImageViewerProps, InlinePreviewGutter, type InlinePreviewGutterProps, JsonEditor, type JsonEditorProps, type LayoutMode, MediaBin, type MediaBinProps, type MediaClipPatch, type MentionCandidate, type MentionProvider, OutlinePanel, type OutlinePanelProps, PlainHtmlPreview, type PlainHtmlPreviewProps, PreviewPanel, type PreviewPanelProps, type PreviewSettings, PreviewSettingsProvider, PreviewToolbarControls, RawEditor, type RawEditorProps, type RecordedBookmark, RecorderButton, type RecorderButtonProps, RecorderModal, type RecorderModalProps, RecorderPanel, type RecorderPanelProps, type RecorderSaveResult, type RecorderSource, type RecorderState, type ResolvedFormat, type ScreenStreamHandle, type ScreenStreamOptions, StatusBar, type StatusBarProps, TRANSITION_ENTRIES, TRANSITION_GROUPS, TemplatePicker, ThemeCustomizerPanel, type ThemeCustomizerPanelProps, type ThemeInheritance, ThemePicker, type ThemePickerProps, TimelineTrack, type TimelineTrackProps, type TimingJson, Toolbar, type ToolbarProps, TooltipLayer, type TransitionCatalogEntry, type TransitionFields, type TransitionGroup, TransitionPicker, type TransitionPickerProps, type UseBlockNavigatorOptions, type UseFileDropOptions, type UseFileDropResult, type UseImageEditorOptions, type UseImageEditorReturn, type UseMediaRecorderOptions, type UseMediaRecorderResult, VersionHistoryPanel, ViewMenuPanel, type ViewPreferences, ViewSwitcher, type ViewSwitcherProps, WysiwygEditor, type WysiwygEditorProps, addConnection, addNode, buildFilename, buildPreviewDoc, buildTimingJson, classifyFile, detectLanguageFromFileName, encodeTimingJson, findTransitionEntry, formatSeconds, getBlockSlices, getCaptureKind, imageEditorReducer, initialImageEditorState, lineToOffset, listDiagramChildren, markdownToTiptap, moveNode, offsetToLine, partitionFiles, processMediaFiles, processTextFile, processTextFiles, readBlockAttrsParams, readBlockAttrsTransition, readBlockAttrsValue, readHeadingLineTransition, removeConnection, removeNode, renameNode, requestCameraStream, requestMicStream, requestScreenStream, resolveFileKind, resolveFormat, searchEmojis, setBlockAttrsTransition, setBlockAttrsValue, setBlockDurationInSource, setHeadingLineTransition, setMediaClipInSource, sliceIndexAtOffset, spliceBlock, summarizeBlockProps, supportsDisplayMedia, supportsMediaRecorder, supportsUserMedia, templateLabel, timingPathFor, tiptapToMarkdown, transitionLabel, useBlockNavigator, useDiagramData, useEditorContext, useFileDrop, useImageEditor, useMediaRecorder, usePreviewSettings, useStreamPreview };
3060
+ export { ALL_EMOJIS, BlockCardView, type BlockCardViewProps, type BlockNavigator, BlockPropertiesPopover, type BlockPropertiesPopoverProps, type BlockRange, type BlockSlice, type CameraStreamOptions, type CanvasRect, type CaptureKind, type CodeContext, type CodeContextSection, CodeContextZones, type CustomTemplateContextValue, CustomTemplateProvider, type CustomTemplateProviderProps, type CustomThemeContextValue, CustomThemeProvider, type CustomThemeProviderProps, DiagramCanvas, type DiagramCommand, type DiagramData, DiagramExtension, type DiagramRFEdge, type DiagramRFNode, DiagramWidget, type DirectionModel, type DocCustomTemplates, type DocCustomThemes, type DocumentLinkCandidate, type DocumentLinkProvider, DocumentSettingsDialog, type DocumentSettingsDialogProps, type DragContentType, type DropTarget, DropZoneOverlay, type DropZoneOverlayProps, EMOJI_CATEGORIES, EMPTY_TRANSITION, type EditorActions, type EditorColorScheme, type EditorContextValue, type EditorMode, EditorProvider, type EditorProviderProps, EditorShell, type EditorShellProps, type EditorState, type EditorView, type EmojiCategory, type EmojiEntry, EmojiPicker, type EmojiPickerProps, type FileCategory, type FileKind, type FolderEntry, FolderView, type FolderViewProps, HeadingWithTemplate, type ImageDisplayMode, ImageEditor, type ImageEditorAction, type ImageEditorProps, type ImageEditorState, type ImageEditorTool, ImageViewer, type ImageViewerProps, InlinePreviewGutter, type InlinePreviewGutterProps, JsonEditor, type JsonEditorProps, type LayoutMode, MediaBin, type MediaBinProps, type MediaClipPatch, type MentionCandidate, type MentionProvider, OutlinePanel, type OutlinePanelProps, PlainHtmlPreview, type PlainHtmlPreviewProps, PreviewFormatSwitch, PreviewModeSwitch, PreviewPanel, type PreviewPanelProps, type PreviewSettings, PreviewSettingsProvider, PreviewToolbarControls, RawEditor, type RawEditorProps, type RecordedBookmark, RecorderButton, type RecorderButtonProps, RecorderModal, type RecorderModalProps, RecorderPanel, type RecorderPanelProps, type RecorderSaveResult, type RecorderSource, type RecorderState, type ResolvedFormat, type ScreenStreamHandle, type ScreenStreamOptions, StatusBar, type StatusBarProps, TRANSITION_ENTRIES, TRANSITION_GROUPS, TemplatePicker, ThemeCustomizerPanel, type ThemeCustomizerPanelProps, type ThemeInheritance, ThemePicker, type ThemePickerProps, TimelineTrack, type TimelineTrackProps, type TimingJson, Toolbar, type ToolbarProps, TooltipLayer, type TransitionCatalogEntry, type TransitionFields, type TransitionGroup, TransitionPicker, type TransitionPickerProps, type UseBlockNavigatorOptions, type UseFileDropOptions, type UseFileDropResult, type UseImageEditorOptions, type UseImageEditorReturn, type UseMediaRecorderOptions, type UseMediaRecorderResult, type UseMonacoLoaderResult, VersionHistoryPanel, ViewMenuPanel, type ViewPreferences, ViewSwitcher, type ViewSwitcherProps, WysiwygEditor, type WysiwygEditorProps, addConnection, addNode, buildFilename, buildPreviewDoc, buildTimingJson, classifyFile, detectLanguageFromFileName, encodeTimingJson, findTransitionEntry, formatSeconds, getBlockSlices, getCaptureKind, imageEditorReducer, initialImageEditorState, lineToOffset, listDiagramChildren, markdownToTiptap, moveNode, offsetToLine, partitionFiles, processMediaFiles, processTextFile, processTextFiles, readBlockAttrsParams, readBlockAttrsTransition, readBlockAttrsValue, readHeadingLineTransition, removeConnection, removeNode, renameNode, requestCameraStream, requestMicStream, requestScreenStream, resolveFileKind, resolveFormat, searchEmojis, setBlockAttrsTransition, setBlockAttrsValue, setBlockDurationInSource, setHeadingLineTransition, setMediaClipInSource, sliceIndexAtOffset, spliceBlock, summarizeBlockProps, supportsDisplayMedia, supportsMediaRecorder, supportsUserMedia, templateLabel, timingPathFor, tiptapToMarkdown, transitionLabel, useBlockNavigator, useCustomTemplates, useCustomThemes, useDiagramData, useDocCustomTemplates, useDocCustomThemes, useEditorContext, useFileDrop, useImageEditor, useMediaRecorder, useMonacoLoader, usePreviewSettings, useStreamPreview };