@bendyline/squisq-react 2.6.0 → 2.7.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.
@@ -1,5 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { Theme } from '@bendyline/squisq/schemas';
3
+ import { FenceRendererMap } from '@bendyline/squisq/fence';
3
4
  import { MarkdownBlockNode, HtmlPolicy } from '@bendyline/squisq/markdown';
4
5
 
5
6
  interface CodeBlockCopyContext {
@@ -39,6 +40,14 @@ interface MarkdownRendererProps {
39
40
  * or hosts with their own clipboard permission/error handling.
40
41
  */
41
42
  onCopyCode?: CodeBlockCopyHandler;
43
+ /**
44
+ * Host fence-renderer registry (`@bendyline/squisq/fence`): fenced code
45
+ * blocks whose language is claimed render through the host's widget
46
+ * instead of the default code block. Wins over the built-in mermaid
47
+ * handling and over an ambient `FenceRendererContext`; a renderer that
48
+ * throws falls back to the plain code block via an error boundary.
49
+ */
50
+ fenceRenderers?: FenceRendererMap;
42
51
  }
43
52
  /**
44
53
  * Renders MarkdownBlockNode[] AST as React HTML elements.
@@ -48,6 +57,6 @@ interface MarkdownRendererProps {
48
57
  * <MarkdownRenderer nodes={block.contents} />
49
58
  * ```
50
59
  */
51
- declare function MarkdownRenderer({ nodes, className, htmlPolicy, linkSchemes, theme, showCodeCopyButton, onCopyCode, }: MarkdownRendererProps): react_jsx_runtime.JSX.Element | null;
60
+ declare function MarkdownRenderer({ nodes, className, htmlPolicy, linkSchemes, theme, showCodeCopyButton, onCopyCode, fenceRenderers, }: MarkdownRendererProps): react_jsx_runtime.JSX.Element | null;
52
61
 
53
62
  export { type CodeBlockCopyHandler as C, MarkdownRenderer as M, type CodeBlockCopyContext as a, type MarkdownRendererProps as b };
@@ -5,6 +5,13 @@ import {
5
5
  useMediaUrl
6
6
  } from "./chunk-LR3AIGDD.js";
7
7
 
8
+ // src/hooks/FenceRendererContext.tsx
9
+ import { createContext, useContext } from "react";
10
+ var FenceRendererContext = createContext(null);
11
+ function useFenceRenderers() {
12
+ return useContext(FenceRendererContext);
13
+ }
14
+
8
15
  // src/InlineVideoPlayer.tsx
9
16
  import { jsx } from "react/jsx-runtime";
10
17
  function InlineVideoPlayer({
@@ -50,7 +57,7 @@ function InlineAudioPlayer({
50
57
  }
51
58
 
52
59
  // src/MarkdownRenderer.tsx
53
- import { Fragment, useEffect, useRef, useState } from "react";
60
+ import { Component, Fragment, useEffect, useRef, useState } from "react";
54
61
  import {
55
62
  sanitizeHtmlNodes,
56
63
  sanitizeUrl
@@ -116,6 +123,33 @@ function CodeBlock({
116
123
  )
117
124
  ] });
118
125
  }
126
+ var FenceWidgetBoundary = class extends Component {
127
+ constructor() {
128
+ super(...arguments);
129
+ this.state = { failed: false };
130
+ }
131
+ static getDerivedStateFromError() {
132
+ return { failed: true };
133
+ }
134
+ render() {
135
+ return this.state.failed ? this.props.fallback : this.props.children;
136
+ }
137
+ };
138
+ function HostFence({
139
+ node,
140
+ lang,
141
+ ctx
142
+ }) {
143
+ const renderer = ctx.fenceRenderers?.[lang];
144
+ if (!renderer) return null;
145
+ return /* @__PURE__ */ jsx3("div", { className: `squisq-md-fence-widget squisq-md-fence-widget-${lang}`, "data-fence-lang": lang, children: renderer({
146
+ lang,
147
+ ...node.meta != null ? { meta: node.meta } : {},
148
+ value: node.value,
149
+ ...ctx.theme ? { theme: ctx.theme } : {},
150
+ mode: "read"
151
+ }) });
152
+ }
119
153
  function renderInline(nodes, keyPrefix = "", ctx = DEFAULT_CTX) {
120
154
  return nodes.map((node, i) => {
121
155
  const key = `${keyPrefix}i${i}`;
@@ -227,8 +261,19 @@ function renderBlock(node, key, ctx = DEFAULT_CTX) {
227
261
  return /* @__PURE__ */ jsx3("ol", { className: "squisq-md-list squisq-md-ol", start: node.start ?? void 0, children: node.children.map((item, i) => renderListItem(item, `${key}li${i}`, ctx)) }, key);
228
262
  }
229
263
  return /* @__PURE__ */ jsx3("ul", { className: "squisq-md-list squisq-md-ul", children: node.children.map((item, i) => renderListItem(item, `${key}li${i}`, ctx)) }, key);
230
- case "code":
231
- if (node.lang?.trim().toLowerCase() === "mermaid") {
264
+ case "code": {
265
+ const fenceLang = node.lang?.trim().toLowerCase();
266
+ if (fenceLang && ctx.fenceRenderers?.[fenceLang]) {
267
+ return /* @__PURE__ */ jsx3(
268
+ FenceWidgetBoundary,
269
+ {
270
+ fallback: /* @__PURE__ */ jsx3(CodeBlock, { value: node.value, language: node.lang, ctx }),
271
+ children: /* @__PURE__ */ jsx3(HostFence, { node, lang: fenceLang, ctx })
272
+ },
273
+ key
274
+ );
275
+ }
276
+ if (fenceLang === "mermaid") {
232
277
  return /* @__PURE__ */ jsx3(
233
278
  MermaidDiagram,
234
279
  {
@@ -241,6 +286,7 @@ function renderBlock(node, key, ctx = DEFAULT_CTX) {
241
286
  );
242
287
  }
243
288
  return /* @__PURE__ */ jsx3(CodeBlock, { value: node.value, language: node.lang, ctx }, key);
289
+ }
244
290
  case "thematicBreak":
245
291
  return /* @__PURE__ */ jsx3("hr", { className: "squisq-md-hr" }, key);
246
292
  case "table":
@@ -454,19 +500,24 @@ function MarkdownRenderer({
454
500
  linkSchemes,
455
501
  theme,
456
502
  showCodeCopyButton = false,
457
- onCopyCode
503
+ onCopyCode,
504
+ fenceRenderers
458
505
  }) {
506
+ const ambientFenceRenderers = useFenceRenderers();
459
507
  if (!nodes || nodes.length === 0) return null;
460
508
  return /* @__PURE__ */ jsx3("div", { className: `squisq-md ${className || ""}`, children: renderBlocks(nodes, "", {
461
509
  htmlPolicy,
462
510
  linkSchemes,
463
511
  theme,
464
512
  showCodeCopyButton,
465
- onCopyCode
513
+ onCopyCode,
514
+ fenceRenderers: fenceRenderers ?? ambientFenceRenderers ?? void 0
466
515
  }) });
467
516
  }
468
517
 
469
518
  export {
519
+ FenceRendererContext,
520
+ useFenceRenderers,
470
521
  InlineVideoPlayer,
471
522
  InlineAudioPlayer,
472
523
  MarkdownRenderer
@@ -16,7 +16,7 @@ import {
16
16
  import {
17
17
  InlineVideoPlayer,
18
18
  MarkdownRenderer
19
- } from "./chunk-TMCLQNLM.js";
19
+ } from "./chunk-ESI3P77P.js";
20
20
  import {
21
21
  useMediaUrl
22
22
  } from "./chunk-LR3AIGDD.js";
@@ -439,19 +439,22 @@ function CardGridSection({ section }) {
439
439
  ] });
440
440
  }
441
441
  function ItemListSection({ section }) {
442
- const { theme, showCodeCopyButton, onCopyCode } = usePageView();
442
+ const { theme, showCodeCopyButton, onCopyCode, fenceRenderers, htmlPolicy, linkSchemes } = usePageView();
443
443
  const items = section.slots.items ?? [];
444
444
  return /* @__PURE__ */ jsxs2(Fragment, { children: [
445
445
  section.slots.title && /* @__PURE__ */ jsx3("h2", { className: "squisq-page-section-title squisq-page-items-title", children: section.slots.title }),
446
- /* @__PURE__ */ jsx3("ol", { className: "squisq-page-items", children: items.map((item, i) => /* @__PURE__ */ jsx3("li", { children: item.markdown ? /* @__PURE__ */ jsx3(
446
+ /* @__PURE__ */ jsx3("ol", { className: "squisq-page-items", children: items.map((item, i) => /* @__PURE__ */ jsx3("li", { children: /* @__PURE__ */ jsx3("div", { className: "squisq-page-item-body", children: item.markdown ? /* @__PURE__ */ jsx3(
447
447
  MarkdownRenderer,
448
448
  {
449
449
  nodes: item.markdown,
450
450
  theme,
451
451
  showCodeCopyButton,
452
- onCopyCode
452
+ onCopyCode,
453
+ fenceRenderers,
454
+ htmlPolicy,
455
+ linkSchemes
453
456
  }
454
- ) : item.body }, i)) })
457
+ ) : item.body }) }, i)) })
455
458
  ] });
456
459
  }
457
460
  function TimelineRailSection({ section }) {
@@ -479,14 +482,16 @@ function TableSection({ section }) {
479
482
  ] });
480
483
  }
481
484
  function ProseSection({ section, block }) {
482
- const { theme, showCodeCopyButton, onCopyCode } = usePageView();
485
+ const { theme, showCodeCopyButton, onCopyCode, fenceRenderers, htmlPolicy, linkSchemes } = usePageView();
483
486
  const heading = section.slots.title !== void 0 && block?.sourceHeading ? /* @__PURE__ */ jsx3(
484
487
  MarkdownRenderer,
485
488
  {
486
489
  nodes: [block.sourceHeading],
487
490
  theme,
488
491
  showCodeCopyButton,
489
- onCopyCode
492
+ onCopyCode,
493
+ htmlPolicy,
494
+ linkSchemes
490
495
  }
491
496
  ) : null;
492
497
  const bodyNodes = section.slots.body?.markdown ?? block?.contents;
@@ -498,7 +503,10 @@ function ProseSection({ section, block }) {
498
503
  nodes: bodyNodes,
499
504
  theme,
500
505
  showCodeCopyButton,
501
- onCopyCode
506
+ onCopyCode,
507
+ fenceRenderers,
508
+ htmlPolicy,
509
+ linkSchemes
502
510
  }
503
511
  )
504
512
  ] });
@@ -562,7 +570,15 @@ function sectionBody(entry, featureFlip) {
562
570
  }
563
571
  }
564
572
  function PageSectionView({ entry, featureFlip, isLeadProse }) {
565
- const { pageStyle, theme, showCodeCopyButton, onCopyCode } = usePageView();
573
+ const {
574
+ pageStyle,
575
+ theme,
576
+ showCodeCopyButton,
577
+ onCopyCode,
578
+ fenceRenderers,
579
+ htmlPolicy,
580
+ linkSchemes
581
+ } = usePageView();
566
582
  const { section } = entry;
567
583
  const richContent = section.slots.richContent?.markdown;
568
584
  const classes = [
@@ -597,7 +613,10 @@ function PageSectionView({ entry, featureFlip, isLeadProse }) {
597
613
  nodes: richContent,
598
614
  theme,
599
615
  showCodeCopyButton,
600
- onCopyCode
616
+ onCopyCode,
617
+ fenceRenderers,
618
+ htmlPolicy,
619
+ linkSchemes
601
620
  }
602
621
  ) })
603
622
  ] })
@@ -622,6 +641,7 @@ import {
622
641
  DEFAULT_THEME as DEFAULT_THEME2
623
642
  } from "@bendyline/squisq/doc";
624
643
  import { parseMarkdown } from "@bendyline/squisq/markdown";
644
+ import { fenceRendererLangs } from "@bendyline/squisq/fence";
625
645
  import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
626
646
  function LinearDocView({
627
647
  doc,
@@ -638,7 +658,10 @@ function LinearDocView({
638
658
  showCover = true,
639
659
  transformPage,
640
660
  showCodeCopyButton = false,
641
- onCopyCode
661
+ onCopyCode,
662
+ fenceRenderers,
663
+ htmlPolicy,
664
+ linkSchemes
642
665
  }) {
643
666
  const scrollRef = useRef(null);
644
667
  const activeViewport = viewport ?? VIEWPORT_PRESETS2.landscape;
@@ -657,6 +680,7 @@ function LinearDocView({
657
680
  () => resolvePageStyle2(activeTheme, transformPage),
658
681
  [activeTheme, transformPage]
659
682
  );
683
+ const widgetFenceLangs = useMemo2(() => fenceRendererLangs(fenceRenderers), [fenceRenderers]);
660
684
  const sections = useMemo2(() => {
661
685
  if (!resolvedDoc) return [];
662
686
  return materializePageSections(resolvedDoc, {
@@ -664,9 +688,10 @@ function LinearDocView({
664
688
  viewport: activeViewport,
665
689
  customTemplates: resolvedDoc.customTemplates,
666
690
  cover: showCover !== false ? resolvedDoc.startBlock : false,
667
- transformPage
691
+ transformPage,
692
+ ...widgetFenceLangs.length > 0 ? { widgetFenceLangs } : {}
668
693
  });
669
- }, [resolvedDoc, activeTheme, activeViewport, showCover, transformPage]);
694
+ }, [resolvedDoc, activeTheme, activeViewport, showCover, transformPage, widgetFenceLangs]);
670
695
  const renderContext = useMemo2(
671
696
  () => ({
672
697
  theme: activeTheme,
@@ -687,7 +712,10 @@ function LinearDocView({
687
712
  animationsEnabled,
688
713
  imageDisplayMode,
689
714
  showCodeCopyButton,
690
- onCopyCode
715
+ onCopyCode,
716
+ fenceRenderers,
717
+ htmlPolicy,
718
+ linkSchemes
691
719
  }),
692
720
  [
693
721
  activeTheme,
@@ -698,7 +726,10 @@ function LinearDocView({
698
726
  animationsEnabled,
699
727
  imageDisplayMode,
700
728
  showCodeCopyButton,
701
- onCopyCode
729
+ onCopyCode,
730
+ fenceRenderers,
731
+ htmlPolicy,
732
+ linkSchemes
702
733
  ]
703
734
  );
704
735
  const { featureOrdinals, leadProseIndex } = useMemo2(() => {
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  BlockRenderer,
3
3
  LinearDocView
4
- } from "./chunk-U6P7HBUY.js";
4
+ } from "./chunk-IQGW6SA6.js";
5
5
  import {
6
6
  useAudioSync,
7
7
  useDocPlayback,
@@ -1861,7 +1861,8 @@ function DocPlayerContent({
1861
1861
  enableSwipe = true,
1862
1862
  globalKeyboardShortcuts = false,
1863
1863
  showCodeCopyButton = false,
1864
- onCopyCode
1864
+ onCopyCode,
1865
+ fenceRenderers
1865
1866
  }) {
1866
1867
  const isSlideshowMode = displayMode === "slideshow";
1867
1868
  const isLinearMode = displayMode === "linear";
@@ -2669,7 +2670,8 @@ function DocPlayerContent({
2669
2670
  surface,
2670
2671
  animationsEnabled,
2671
2672
  showCodeCopyButton,
2672
- onCopyCode
2673
+ onCopyCode,
2674
+ fenceRenderers
2673
2675
  }
2674
2676
  )
2675
2677
  }
@@ -2684,6 +2686,7 @@ function DocPlayerContent({
2684
2686
  "data-playback-state": isPlaying ? "playing" : "paused",
2685
2687
  "data-swipe-phase": swipe.phase,
2686
2688
  tabIndex: renderMode ? -1 : 0,
2689
+ role: "region",
2687
2690
  "aria-label": "Document player",
2688
2691
  onKeyDown: renderMode ? void 0 : handleKeyDown,
2689
2692
  className: `doc-player${swipeEnabled ? " doc-player--swipe" : ""}${swipe.phase === "dragging" ? " doc-player--grabbing" : ""}`,
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-TT6ENR6T.js";
4
4
  import {
5
5
  MarkdownRenderer
6
- } from "./chunk-TMCLQNLM.js";
6
+ } from "./chunk-ESI3P77P.js";
7
7
 
8
8
  // src/jsonView/useJsonViewTokens.ts
9
9
  import { useMemo } from "react";
@@ -325,6 +325,72 @@ function JsonView(props) {
325
325
  ) });
326
326
  }
327
327
 
328
+ // src/jsonView/JsonFormFence.tsx
329
+ import { inferSchema } from "@bendyline/squisq/jsonForm";
330
+ import { parseYamlSubset } from "@bendyline/squisq/doc";
331
+ import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
332
+ function parseFenceBody(value) {
333
+ const trimmed = value.trim();
334
+ if (trimmed.startsWith("{")) {
335
+ try {
336
+ const parsed = JSON.parse(trimmed);
337
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
338
+ return { error: "fence body must be a top-level object" };
339
+ }
340
+ return { data: parsed };
341
+ } catch (err) {
342
+ return { error: `invalid JSON: ${err instanceof Error ? err.message : String(err)}` };
343
+ }
344
+ }
345
+ try {
346
+ return { data: parseYamlSubset(value) };
347
+ } catch (err) {
348
+ return { error: `invalid YAML: ${err instanceof Error ? err.message : String(err)}` };
349
+ }
350
+ }
351
+ function JsonFormFence({ ctx, options }) {
352
+ const parsed = ctx.data !== void 0 ? { data: ctx.data } : parseFenceBody(ctx.value);
353
+ const data = parsed.data;
354
+ if (!data || typeof data !== "object" || Array.isArray(data)) {
355
+ return /* @__PURE__ */ jsxs2("div", { className: "squisq-json-fence squisq-json-fence--invalid", children: [
356
+ /* @__PURE__ */ jsx4("pre", { className: "squisq-md-code-block", children: /* @__PURE__ */ jsx4("code", { children: ctx.value }) }),
357
+ "error" in parsed && parsed.error && /* @__PURE__ */ jsx4("div", { className: "squisq-json-fence-error", children: parsed.error })
358
+ ] });
359
+ }
360
+ const schema = options.schema ?? inferSchema(data);
361
+ const actions = options.actions ?? [];
362
+ return /* @__PURE__ */ jsxs2("div", { className: `squisq-json-fence${options.className ? ` ${options.className}` : ""}`, children: [
363
+ /* @__PURE__ */ jsx4(
364
+ JsonView,
365
+ {
366
+ schema,
367
+ value: data,
368
+ theme: ctx.theme,
369
+ density: options.density ?? "compact"
370
+ }
371
+ ),
372
+ actions.length > 0 && options.onAction && /* @__PURE__ */ jsx4("div", { className: "squisq-json-fence-actions", role: "group", children: actions.map((action) => /* @__PURE__ */ jsx4(
373
+ "button",
374
+ {
375
+ type: "button",
376
+ className: `squisq-json-fence-action${action.variant === "primary" ? " squisq-json-fence-action--primary" : ""}`,
377
+ onClick: () => void options.onAction?.(action.id, data, ctx),
378
+ children: action.label
379
+ },
380
+ action.id
381
+ )) })
382
+ ] });
383
+ }
384
+
385
+ // src/jsonView/jsonFormFenceRenderer.tsx
386
+ import { jsx as jsx5 } from "react/jsx-runtime";
387
+ function createJsonFormFenceRenderer(options = {}) {
388
+ return function jsonFormFenceRenderer(ctx) {
389
+ return /* @__PURE__ */ jsx5(JsonFormFence, { ctx, options });
390
+ };
391
+ }
392
+
328
393
  export {
329
- JsonView
394
+ JsonView,
395
+ createJsonFormFenceRenderer
330
396
  };
package/dist/index.d.ts CHANGED
@@ -1,16 +1,17 @@
1
1
  import { SquisqRenderAPI, VideoPresentation as VideoPresentation$1, PipSize, PipShape, PipPosition } from './player/index.js';
2
2
  export { BlockMarker, BlockRenderer, CaptionMode, CaptionOverlay, CaptionStyle, ControlsLayout, DisplayMode, DocControlsBottom, DocControlsOverlay, DocControlsSidebar, DocControlsSlideshow, DocPlayer, DocPlayerProps, DocPlayerWithSidebar, DocProgressBar, InlineAudioPlayer, InlineAudioPlayerProps, InlineVideoPlayer, InlineVideoPlayerProps, MediaClipLayer, MediaClipLayerProps, PlaybackActions, PlaybackState, RenderAudioSegmentInfo, RenderBlockInfo, RenderCaptionInfo, RenderChapterInfo, SlideNavActions, SocialCaptionOverlay, formatTime } from './player/index.js';
3
3
  import { Theme, VideoPresentation, VideoPipSize, VideoPipShape, VideoPipPosition, Doc, ScheduledClip, MediaProvider } from '@bendyline/squisq/schemas';
4
- import { C as CodeBlockCopyHandler } from './MarkdownRenderer-CerBKw-c.js';
5
- export { a as CodeBlockCopyContext, M as MarkdownRenderer, b as MarkdownRendererProps } from './MarkdownRenderer-CerBKw-c.js';
6
- export { MermaidDiagram, MermaidDiagramProps } from './markdown/index.js';
4
+ import { C as CodeBlockCopyHandler } from './MarkdownRenderer-CC9dOc6b.js';
5
+ export { a as CodeBlockCopyContext, M as MarkdownRenderer, b as MarkdownRendererProps } from './MarkdownRenderer-CC9dOc6b.js';
6
+ export { FenceRendererContext, MermaidDiagram, MermaidDiagramProps, useFenceRenderers } from './markdown/index.js';
7
7
  export { CanvasSection, CanvasSectionProps, ImageDisplayMode, LinearDocView, LinearDocViewProps, PageSectionView, PageSectionViewProps, PageViewContext, PageViewContextValue, usePageView } from './page/index.js';
8
8
  export { ImageLayer, MapLayer, MermaidLayer, PathLayer, ShapeLayer, TableLayer, TextLayer, TreeLayer, VideoLayer } from './layers/index.js';
9
9
  import { CoverSlideTemplate, CoverSlidePlayback } from '@bendyline/squisq/doc';
10
10
  export { getAnimationStyle, getTransitionClass } from '@bendyline/squisq/doc';
11
11
  export { MediaContext, MediaScheduleController, ModalDialogOptions, ResourcePolicyContext, UseDocPlaybackOptions, useAudioSync, useAutoSurface, useDocPlayback, useMediaProvider, useMediaSchedule, useMediaUrl, useModalDialog, useResourcePolicy, useViewportOrientation } from './hooks/index.js';
12
+ export { FenceRenderContext, FenceRenderer, FenceRendererMap } from '@bendyline/squisq/fence';
12
13
  export { A as AudioActions, a as AudioController, b as AudioState } from './AudioController-DwMsPe38.js';
13
- export { JsonView, JsonViewProps } from './json-view/index.js';
14
+ export { JsonFormFenceAction, JsonFormFenceRendererOptions, JsonView, JsonViewProps, createJsonFormFenceRenderer } from './json-view/index.js';
14
15
  import 'react/jsx-runtime';
15
16
  import 'react';
16
17
  import '@bendyline/squisq/markdown';
package/dist/index.js CHANGED
@@ -12,7 +12,7 @@ import {
12
12
  formatTime,
13
13
  resolveDocPlayerAppearance,
14
14
  useMediaClipDurations
15
- } from "./chunk-GWD62KLD.js";
15
+ } from "./chunk-KXYE4AGL.js";
16
16
  import {
17
17
  BlockRenderer,
18
18
  CanvasSection,
@@ -20,7 +20,7 @@ import {
20
20
  PageSectionView,
21
21
  PageViewContext,
22
22
  usePageView
23
- } from "./chunk-U6P7HBUY.js";
23
+ } from "./chunk-IQGW6SA6.js";
24
24
  import {
25
25
  ImageLayer,
26
26
  MapLayer,
@@ -44,16 +44,19 @@ import {
44
44
  useViewportOrientation
45
45
  } from "./chunk-MVJQL2W2.js";
46
46
  import {
47
- JsonView
48
- } from "./chunk-YKBVTYU4.js";
47
+ JsonView,
48
+ createJsonFormFenceRenderer
49
+ } from "./chunk-QF74BODY.js";
49
50
  import {
50
51
  useAutoSurface
51
52
  } from "./chunk-TT6ENR6T.js";
52
53
  import {
54
+ FenceRendererContext,
53
55
  InlineAudioPlayer,
54
56
  InlineVideoPlayer,
55
- MarkdownRenderer
56
- } from "./chunk-TMCLQNLM.js";
57
+ MarkdownRenderer,
58
+ useFenceRenderers
59
+ } from "./chunk-ESI3P77P.js";
57
60
  import {
58
61
  MermaidDiagram
59
62
  } from "./chunk-WLUZTUNZ.js";
@@ -75,6 +78,7 @@ export {
75
78
  DocPlayer,
76
79
  DocPlayerWithSidebar,
77
80
  DocProgressBar,
81
+ FenceRendererContext,
78
82
  ImageLayer,
79
83
  InlineAudioPlayer,
80
84
  InlineVideoPlayer,
@@ -96,6 +100,7 @@ export {
96
100
  TextLayer,
97
101
  TreeLayer,
98
102
  VideoLayer,
103
+ createJsonFormFenceRenderer,
99
104
  formatTime,
100
105
  getAnimationStyle,
101
106
  getTransitionClass,
@@ -103,6 +108,7 @@ export {
103
108
  useAudioSync,
104
109
  useAutoSurface,
105
110
  useDocPlayback,
111
+ useFenceRenderers,
106
112
  useMediaClipDurations,
107
113
  useMediaProvider,
108
114
  useMediaSchedule,
@@ -1,6 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { SquisqAnnotatedSchema } from '@bendyline/squisq/jsonForm';
3
3
  import { Theme, SurfaceScheme } from '@bendyline/squisq/schemas';
4
+ import { FenceRenderContext, FenceRenderer } from '@bendyline/squisq/fence';
4
5
 
5
6
  interface JsonViewProps {
6
7
  /** Schema describing the value's shape (with optional `squisq` UI hints). */
@@ -18,4 +19,62 @@ interface JsonViewProps {
18
19
  }
19
20
  declare function JsonView(props: JsonViewProps): react_jsx_runtime.JSX.Element;
20
21
 
21
- export { JsonView, type JsonViewProps };
22
+ /**
23
+ * Built-in fence renderer: a fence's JSON / YAML-subset body rendered
24
+ * through the read-only JSON-form machinery (`<JsonView>`), with
25
+ * host-defined action buttons as chrome below the form.
26
+ *
27
+ * This is the form-driven flavor of the pluggable fence mechanism —
28
+ * register the result in a `FenceRendererMap` next to fully custom
29
+ * component renderers:
30
+ *
31
+ * ```tsx
32
+ * const fenceRenderers = {
33
+ * 'my-record': createJsonFormFenceRenderer({
34
+ * actions: [{ id: 'approve', label: 'Approve' }],
35
+ * onAction: (actionId, data) => host.handle(actionId, data),
36
+ * }),
37
+ * };
38
+ * ```
39
+ *
40
+ * Parsing: strict JSON first, then the documented YAML subset
41
+ * (`parseYamlSubset` — flat maps, inline arrays, one nesting level).
42
+ * Unparseable bodies render as a plain code block with a quiet
43
+ * diagnostic line, so a malformed fence stays visible and debuggable.
44
+ * The rendering itself lives in `JsonFormFence.tsx`; this module is the
45
+ * factory and its option surface.
46
+ */
47
+
48
+ /** One host action button rendered below the form. */
49
+ interface JsonFormFenceAction {
50
+ id: string;
51
+ label: string;
52
+ /** `primary` gets the filled treatment; default is quiet. */
53
+ variant?: 'primary' | 'default';
54
+ }
55
+ interface JsonFormFenceRendererOptions {
56
+ /**
57
+ * Schema for the fence body. Omitted → inferred from the parsed value
58
+ * (`inferSchema`), which renders sensible read-only fields for any
59
+ * well-formed object.
60
+ */
61
+ schema?: SquisqAnnotatedSchema;
62
+ /** Action buttons rendered below the form. */
63
+ actions?: readonly JsonFormFenceAction[];
64
+ /**
65
+ * Invoked from the button's click handler (user-gesture context is
66
+ * preserved, mirroring the `onCopyCode` contract).
67
+ */
68
+ onAction?: (actionId: string, data: Record<string, unknown>, ctx: FenceRenderContext) => void | Promise<void>;
69
+ /** Padding density passed to `<JsonView>` (default `'compact'`). */
70
+ density?: 'comfortable' | 'compact';
71
+ /** Extra CSS class on the wrapper. */
72
+ className?: string;
73
+ }
74
+ /**
75
+ * Build a `FenceRenderer` that renders the fence body through the
76
+ * read-only JSON form. See the module doc for usage.
77
+ */
78
+ declare function createJsonFormFenceRenderer(options?: JsonFormFenceRendererOptions): FenceRenderer;
79
+
80
+ export { type JsonFormFenceAction, type JsonFormFenceRendererOptions, JsonView, type JsonViewProps, createJsonFormFenceRenderer };
@@ -1,10 +1,12 @@
1
1
  import {
2
- JsonView
3
- } from "../chunk-YKBVTYU4.js";
2
+ JsonView,
3
+ createJsonFormFenceRenderer
4
+ } from "../chunk-QF74BODY.js";
4
5
  import "../chunk-TT6ENR6T.js";
5
- import "../chunk-TMCLQNLM.js";
6
+ import "../chunk-ESI3P77P.js";
6
7
  import "../chunk-WLUZTUNZ.js";
7
8
  import "../chunk-LR3AIGDD.js";
8
9
  export {
9
- JsonView
10
+ JsonView,
11
+ createJsonFormFenceRenderer
10
12
  };
@@ -1,6 +1,9 @@
1
- export { a as CodeBlockCopyContext, C as CodeBlockCopyHandler, M as MarkdownRenderer, b as MarkdownRendererProps } from '../MarkdownRenderer-CerBKw-c.js';
1
+ export { a as CodeBlockCopyContext, C as CodeBlockCopyHandler, M as MarkdownRenderer, b as MarkdownRendererProps } from '../MarkdownRenderer-CC9dOc6b.js';
2
2
  import * as react_jsx_runtime from 'react/jsx-runtime';
3
3
  import { Theme } from '@bendyline/squisq/schemas';
4
+ import * as react from 'react';
5
+ import { FenceRendererMap } from '@bendyline/squisq/fence';
6
+ export { FenceRenderContext, FenceRenderer, FenceRendererMap } from '@bendyline/squisq/fence';
4
7
  import '@bendyline/squisq/markdown';
5
8
 
6
9
  interface MermaidDiagramProps {
@@ -13,4 +16,8 @@ interface MermaidDiagramProps {
13
16
  /** Read-only Mermaid rendering for page bodies and slide layers. */
14
17
  declare function MermaidDiagram({ source, className, ariaLabel, theme, }: MermaidDiagramProps): react_jsx_runtime.JSX.Element;
15
18
 
16
- export { MermaidDiagram, type MermaidDiagramProps };
19
+ declare const FenceRendererContext: react.Context<FenceRendererMap | null>;
20
+ /** The ambient registry, or null when no provider wraps this subtree. */
21
+ declare function useFenceRenderers(): FenceRendererMap | null;
22
+
23
+ export { FenceRendererContext, MermaidDiagram, type MermaidDiagramProps, useFenceRenderers };
@@ -1,11 +1,15 @@
1
1
  import {
2
- MarkdownRenderer
3
- } from "../chunk-TMCLQNLM.js";
2
+ FenceRendererContext,
3
+ MarkdownRenderer,
4
+ useFenceRenderers
5
+ } from "../chunk-ESI3P77P.js";
4
6
  import {
5
7
  MermaidDiagram
6
8
  } from "../chunk-WLUZTUNZ.js";
7
9
  import "../chunk-LR3AIGDD.js";
8
10
  export {
11
+ FenceRendererContext,
9
12
  MarkdownRenderer,
10
- MermaidDiagram
13
+ MermaidDiagram,
14
+ useFenceRenderers
11
15
  };