@bendyline/squisq-editor-react 2.7.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,6 +1,6 @@
1
1
  import {
2
2
  RecorderModal
3
- } from "./chunk-JUAQQTJE.js";
3
+ } from "./chunk-V3J5S7X6.js";
4
4
 
5
5
  // src/recorder/RecorderButton.tsx
6
6
  import { useCallback, useState } from "react";
@@ -15,7 +15,7 @@ import {
15
15
  import {
16
16
  RecorderPanel,
17
17
  useModalDialog
18
- } from "./chunk-JUAQQTJE.js";
18
+ } from "./chunk-V3J5S7X6.js";
19
19
  import {
20
20
  Icon
21
21
  } from "./chunk-GS7QWYFT.js";
@@ -25170,27 +25170,16 @@ function replaceCodeSnippetText(editor, blockId, nextText) {
25170
25170
  return true;
25171
25171
  }
25172
25172
 
25173
- // src/fenceWidgets/HostFenceExtension.tsx
25173
+ // src/fenceWidgets/HostFenceExtension.ts
25174
25174
  import { Extension as Extension7 } from "@tiptap/core";
25175
25175
  import { Plugin as Plugin7, PluginKey as PluginKey7 } from "@tiptap/pm/state";
25176
25176
  import { Decoration as Decoration7, DecorationSet as DecorationSet7 } from "@tiptap/pm/view";
25177
25177
  import { createRoot as createRoot5 } from "react-dom/client";
25178
- import { Component, createElement as createElement11, useEffect as useEffect33, useMemo as useMemo32, useState as useState42 } from "react";
25178
+ import { createElement as createElement11 } from "react";
25179
+
25180
+ // src/fenceWidgets/HostFenceWidget.tsx
25181
+ import { Component, useEffect as useEffect33, useMemo as useMemo32, useState as useState42 } from "react";
25179
25182
  import { jsx as jsx50 } from "react/jsx-runtime";
25180
- var HOST_FENCE_KEY = new PluginKey7("squisq-host-fence");
25181
- function fenceLangToken(node2) {
25182
- const lang = node2.attrs.language;
25183
- if (typeof lang !== "string") return "";
25184
- return lang.trim().split(/\s+/, 1)[0]?.toLowerCase() ?? "";
25185
- }
25186
- function findHostFenceBlockPos(editor, blockId) {
25187
- return HOST_FENCE_KEY.getState(editor.state)?.entries.find((e2) => e2.id === blockId)?.pos ?? null;
25188
- }
25189
- function replaceHostFenceText(editor, blockId, next) {
25190
- const pos = findHostFenceBlockPos(editor, blockId);
25191
- if (pos === null) return false;
25192
- return replaceAsciiFenceText(editor, pos, next);
25193
- }
25194
25183
  var HostFenceErrorBoundary = class extends Component {
25195
25184
  constructor() {
25196
25185
  super(...arguments);
@@ -25203,12 +25192,7 @@ var HostFenceErrorBoundary = class extends Component {
25203
25192
  return this.state.failed ? this.props.fallback : this.props.children;
25204
25193
  }
25205
25194
  };
25206
- function HostFenceWidget({
25207
- editor,
25208
- blockId,
25209
- getRenderers,
25210
- getTheme
25211
- }) {
25195
+ function HostFenceWidget({ editor, blockId, getRenderers, getTheme }) {
25212
25196
  const [version, setVersion] = useState42(0);
25213
25197
  useEffect33(() => {
25214
25198
  const onUpdate = () => setVersion((v2) => v2 + 1);
@@ -25239,6 +25223,22 @@ function HostFenceWidget({
25239
25223
  }
25240
25224
  }) });
25241
25225
  }
25226
+
25227
+ // src/fenceWidgets/HostFenceExtension.ts
25228
+ var HOST_FENCE_KEY = new PluginKey7("squisq-host-fence");
25229
+ function fenceLangToken(node2) {
25230
+ const lang = node2.attrs.language;
25231
+ if (typeof lang !== "string") return "";
25232
+ return lang.trim().split(/\s+/, 1)[0]?.toLowerCase() ?? "";
25233
+ }
25234
+ function findHostFenceBlockPos(editor, blockId) {
25235
+ return HOST_FENCE_KEY.getState(editor.state)?.entries.find((e2) => e2.id === blockId)?.pos ?? null;
25236
+ }
25237
+ function replaceHostFenceText(editor, blockId, next) {
25238
+ const pos = findHostFenceBlockPos(editor, blockId);
25239
+ if (pos === null) return false;
25240
+ return replaceAsciiFenceText(editor, pos, next);
25241
+ }
25242
25242
  function buildDecorations6(doc, entries, editor, options) {
25243
25243
  const decos = [];
25244
25244
  for (const entry of entries) {
@@ -30023,335 +30023,9 @@ function InlinePreviewGutter({
30023
30023
 
30024
30024
  // src/buildPreviewDoc.ts
30025
30025
  import {
30026
- coerceTemplateParams,
30027
- deriveTemplateInputs as deriveTemplateInputs3,
30028
- flattenRenderableBlocks,
30029
- hasTemplate as hasTemplate2
30026
+ buildPreviewDoc,
30027
+ documentTitleFromFileName
30030
30028
  } from "@bendyline/squisq/doc";
30031
- import { extractPlainText as extractPlainText4, KNOWN_BLOCK_META_KEYS as KNOWN_BLOCK_META_KEYS2 } from "@bendyline/squisq/markdown";
30032
- import { getChildren as getChildren2 } from "@bendyline/squisq/markdown";
30033
- import { iconMarker } from "@bendyline/squisq/icon-marker";
30034
- function extractRichText(node2) {
30035
- if (node2.type === "inlineIcon") {
30036
- const icon = node2;
30037
- return iconMarker(icon.family, icon.name);
30038
- }
30039
- if ("value" in node2 && typeof node2.value === "string") {
30040
- return node2.value;
30041
- }
30042
- const children = getChildren2(node2);
30043
- const separator = node2.type === "list" || node2.type === "listItem" ? "\n" : "";
30044
- return children.map(extractRichText).join(separator);
30045
- }
30046
- function extractBodyText(contents) {
30047
- if (!contents || contents.length === 0) return "";
30048
- const parts = [];
30049
- for (const node2 of contents) {
30050
- if (node2.type === "code" && node2.lang?.trim().toLowerCase() === "mermaid") continue;
30051
- parts.push(extractRichText(node2));
30052
- }
30053
- return parts.join("\n").trim();
30054
- }
30055
- function parseDim2(raw) {
30056
- if (raw === void 0) return void 0;
30057
- const n = parseFloat(raw);
30058
- return Number.isFinite(n) && n > 0 ? n : void 0;
30059
- }
30060
- function extractBlockImages2(contents) {
30061
- if (!contents || contents.length === 0) return [];
30062
- const images = [];
30063
- function walkHtml(node2) {
30064
- if (!node2 || typeof node2 !== "object") return;
30065
- const n = node2;
30066
- if (n.type === "htmlElement" && n.tagName.toLowerCase() === "img") {
30067
- const attrs = n.attributes;
30068
- const src = attrs?.src;
30069
- if (typeof src === "string" && src) {
30070
- images.push({
30071
- src,
30072
- alt: typeof attrs?.alt === "string" ? attrs.alt : "",
30073
- width: parseDim2(attrs?.width),
30074
- height: parseDim2(attrs?.height)
30075
- });
30076
- }
30077
- }
30078
- if (Array.isArray(n.children)) {
30079
- for (const child of n.children) walkHtml(child);
30080
- }
30081
- }
30082
- function walk(node2) {
30083
- if ("type" in node2 && node2.type === "image" && "url" in node2) {
30084
- const img = node2;
30085
- if (img.url) {
30086
- images.push({ src: img.url, alt: img.alt ?? "" });
30087
- }
30088
- }
30089
- if ("type" in node2 && (node2.type === "htmlBlock" || node2.type === "htmlInline")) {
30090
- const html = node2;
30091
- for (const child of html.htmlChildren ?? []) walkHtml(child);
30092
- }
30093
- for (const child of getChildren2(node2)) {
30094
- walk(child);
30095
- }
30096
- }
30097
- for (const node2 of contents) {
30098
- walk(node2);
30099
- }
30100
- return images;
30101
- }
30102
- function collectAllDocImages(blocks) {
30103
- const seen = /* @__PURE__ */ new Set();
30104
- const images = [];
30105
- function walkBlocks(blockList) {
30106
- for (const block of blockList) {
30107
- for (const img of extractBlockImages2(block.contents)) {
30108
- if (!seen.has(img.src)) {
30109
- seen.add(img.src);
30110
- images.push(img);
30111
- }
30112
- }
30113
- if (block.children) {
30114
- walkBlocks(block.children);
30115
- }
30116
- }
30117
- }
30118
- walkBlocks(blocks);
30119
- return images;
30120
- }
30121
- function extractListItems3(contents) {
30122
- if (!contents) return [];
30123
- const items = [];
30124
- for (const node2 of contents) {
30125
- if (node2.type === "list") {
30126
- for (const item of node2.children) {
30127
- const text = extractPlainText4(item).trim();
30128
- if (text) items.push(text);
30129
- }
30130
- }
30131
- }
30132
- return items;
30133
- }
30134
- function getTemplateDefaults2(templateName, headingText2, block) {
30135
- const body = extractBodyText(block.contents);
30136
- switch (templateName) {
30137
- case "statHighlight":
30138
- return deriveTemplateInputs3(templateName, headingText2, block.contents) ?? {
30139
- stat: headingText2,
30140
- description: body || headingText2
30141
- };
30142
- case "quote":
30143
- case "fullBleedQuote":
30144
- case "pullQuote":
30145
- return { quote: body || headingText2 };
30146
- case "factCard":
30147
- return { fact: headingText2, explanation: body || headingText2 };
30148
- case "comparisonBar":
30149
- return { leftLabel: "A", leftValue: 60, rightLabel: "B", rightValue: 40 };
30150
- case "list": {
30151
- const items = extractListItems3(block.contents);
30152
- return { items: items.length > 0 ? items : ["Item 1", "Item 2", "Item 3"] };
30153
- }
30154
- case "definitionCard":
30155
- return { term: headingText2, definition: body || headingText2 };
30156
- case "dateEvent":
30157
- return { date: headingText2, description: body || headingText2 };
30158
- case "leftFeature":
30159
- case "rightFeature": {
30160
- const images = extractBlockImages2(block.contents);
30161
- const img = images[0];
30162
- return {
30163
- imageSrc: img?.src ?? "",
30164
- imageAlt: img?.alt || headingText2,
30165
- imageWidth: img?.width,
30166
- imageHeight: img?.height,
30167
- title: headingText2,
30168
- body: body || headingText2
30169
- };
30170
- }
30171
- default:
30172
- return {};
30173
- }
30174
- }
30175
- function blockToSlide(block, index2, knownTemplates, documentTitle2) {
30176
- const headingText2 = block.sourceHeading ? extractPlainText4(block.sourceHeading) : block.title || documentTitle2 || "";
30177
- const implicitSectionHeader = block.template === "sectionHeader" && block.autoTemplate !== true && !!block.sourceHeading && !block.sourceHeading.templateAnnotation?.template;
30178
- const requestedTemplate = implicitSectionHeader ? "content" : block.template ?? "content";
30179
- const isCustomTemplate = knownTemplates?.has(requestedTemplate) ?? false;
30180
- const recognized = hasTemplate2(requestedTemplate) || isCustomTemplate;
30181
- const template = recognized ? requestedTemplate : "sectionHeader";
30182
- const defaults = getTemplateDefaults2(template, headingText2, block);
30183
- const templateOverrides = omitStringBlockMeta(block.templateOverrides);
30184
- const coercedTemplateOverrides = templateOverrides ? coerceTemplateParams(template, templateOverrides).input : void 0;
30185
- const {
30186
- id: _id,
30187
- startTime: _st,
30188
- duration: _d,
30189
- audioSegment: _as,
30190
- layers: _l,
30191
- transition: _tr,
30192
- template: _t,
30193
- title: _ti,
30194
- children: _c,
30195
- contents: _co,
30196
- sourceHeading: _sh,
30197
- templateOverrides: _to,
30198
- templateData: _td,
30199
- ...extraFields
30200
- } = block;
30201
- return {
30202
- id: block.id,
30203
- template,
30204
- duration: block.duration,
30205
- audioSegment: 0,
30206
- // Respect the block's authored transition (set via the toolbar / on-canvas
30207
- // properties palette → `{…}` block attrs). Only fall back to a default fade
30208
- // for blocks past the first when the author hasn't chosen one; the first
30209
- // block has no previous slide to transition in from.
30210
- transition: block.transition ?? (index2 > 0 ? { type: "fade", duration: 0.5 } : void 0),
30211
- title: headingText2,
30212
- // Preserve body nodes on every slide. Built-in templates ignore this
30213
- // structural field, while the canonical materializer uses it to retain
30214
- // authored rich elements (Mermaid fences today; other media can follow)
30215
- // independently of the selected visual template.
30216
- ...block.contents ? { contents: block.contents } : {},
30217
- // Custom templates additionally consume child blocks through tokens.
30218
- ...isCustomTemplate && block.children ? { children: block.children } : {},
30219
- ...defaults,
30220
- ...extraFields,
30221
- // Structured body data (```json data fences, GFM tables for dataTable)
30222
- // carries typed values; `{[…]}` string overrides win last so an explicit
30223
- // annotation param can still pin any field.
30224
- //
30225
- // Block-meta keys (transition, startTime, duration, …) are the exception:
30226
- // they were already coerced to typed block fields above (e.g.
30227
- // `block.transition` → `{ type, duration, direction }`). Their raw string
30228
- // form also rides along in `templateData`/`templateOverrides` because the
30229
- // author wrote them inside `{[…]}`; left un-stripped, that string would
30230
- // spread back over the typed value here and clobber it — turning
30231
- // `transition=vortex` into the string `"vortex"`, which the player can't
30232
- // animate. Omit them from the content spreads so the typed fields win.
30233
- ...omitBlockMeta(block.templateData),
30234
- ...coercedTemplateOverrides
30235
- };
30236
- }
30237
- var BLOCK_META_KEYS2 = new Set(Object.keys(KNOWN_BLOCK_META_KEYS2));
30238
- function omitBlockMeta(data) {
30239
- if (!data) return data;
30240
- let hit = false;
30241
- const out = {};
30242
- for (const key of Object.keys(data)) {
30243
- if (BLOCK_META_KEYS2.has(key)) {
30244
- hit = true;
30245
- continue;
30246
- }
30247
- out[key] = data[key];
30248
- }
30249
- return hit ? out : data;
30250
- }
30251
- function omitStringBlockMeta(data) {
30252
- if (!data) return data;
30253
- let hit = false;
30254
- const out = {};
30255
- for (const key of Object.keys(data)) {
30256
- if (BLOCK_META_KEYS2.has(key)) {
30257
- hit = true;
30258
- continue;
30259
- }
30260
- out[key] = data[key];
30261
- }
30262
- return hit ? out : data;
30263
- }
30264
- var IMAGE_MOTIONS = [
30265
- "zoomIn",
30266
- "zoomOut",
30267
- "panLeft",
30268
- "panRight"
30269
- ];
30270
- function documentTitleFromFileName(fileName) {
30271
- if (!fileName) return "";
30272
- const base = fileName.split(/[\\/]/).pop() ?? "";
30273
- return base.replace(/\.[^.]+$/, "").trim();
30274
- }
30275
- function resolveDocumentTitle(doc, provided) {
30276
- const frontmatterTitle = doc.frontmatter?.title;
30277
- if (typeof frontmatterTitle === "string" && frontmatterTitle.trim()) {
30278
- return frontmatterTitle.trim();
30279
- }
30280
- return provided?.trim() ?? "";
30281
- }
30282
- function buildPreviewDoc(doc, options) {
30283
- const flat = flattenRenderableBlocks(doc.blocks);
30284
- const allImages = collectAllDocImages(doc.blocks);
30285
- const usedImageSrcs = /* @__PURE__ */ new Set();
30286
- const knownTemplates = doc.customTemplates ? new Set(doc.customTemplates.map((d) => d.name)) : void 0;
30287
- const documentTitle2 = resolveDocumentTitle(doc, options?.documentTitle);
30288
- const slides = [];
30289
- let motionIndex = 0;
30290
- for (let i = 0; i < flat.length; i++) {
30291
- const block = flat[i];
30292
- const blockImages = extractBlockImages2(block.contents);
30293
- const slide = blockToSlide(block, i, knownTemplates, documentTitle2);
30294
- if (blockImages.length > 0 && slide.template === "sectionHeader") {
30295
- const img = blockImages[0];
30296
- usedImageSrcs.add(img.src);
30297
- slide.template = "imageWithCaption";
30298
- slide.imageSrc = img.src;
30299
- slide.imageAlt = img.alt;
30300
- slide.caption = slide.title;
30301
- slide.captionPosition = "bottom";
30302
- slide.ambientMotion = IMAGE_MOTIONS[motionIndex++ % IMAGE_MOTIONS.length];
30303
- } else if (blockImages.length > 0) {
30304
- const img = blockImages[0];
30305
- usedImageSrcs.add(img.src);
30306
- if (!slide.accentImage) {
30307
- slide.accentImage = {
30308
- src: img.src,
30309
- alt: img.alt,
30310
- position: "left-strip",
30311
- ambientMotion: IMAGE_MOTIONS[motionIndex++ % IMAGE_MOTIONS.length]
30312
- };
30313
- }
30314
- }
30315
- slides.push(slide);
30316
- }
30317
- const unusedImages = allImages.filter((img) => !usedImageSrcs.has(img.src));
30318
- if (unusedImages.length > 0 && slides.length > 0) {
30319
- const interval = Math.max(2, Math.floor(slides.length / (unusedImages.length + 1)));
30320
- let insertOffset = 0;
30321
- for (let imgIdx = 0; imgIdx < unusedImages.length; imgIdx++) {
30322
- const insertAt = Math.min((imgIdx + 1) * interval + insertOffset, slides.length);
30323
- const img = unusedImages[imgIdx];
30324
- slides.splice(insertAt, 0, {
30325
- id: `img-interleave-${imgIdx}`,
30326
- template: "imageWithCaption",
30327
- duration: 5,
30328
- audioSegment: 0,
30329
- imageSrc: img.src,
30330
- imageAlt: img.alt,
30331
- ambientMotion: IMAGE_MOTIONS[motionIndex++ % IMAGE_MOTIONS.length],
30332
- transition: { type: "fade", duration: 0.5 }
30333
- });
30334
- insertOffset++;
30335
- }
30336
- }
30337
- let t = 0;
30338
- for (const slide of slides) {
30339
- slide.startTime = t;
30340
- t += slide.duration;
30341
- }
30342
- const audio = doc.audio?.segments?.length > 0 ? doc.audio : {
30343
- segments: t > 0 ? [{ src: "", name: "preview", duration: t, startTime: 0 }] : []
30344
- };
30345
- return {
30346
- // Preserve document-wide capabilities (custom themes, persistent layers,
30347
- // scheduled media, frontmatter, captions, and future schema fields).
30348
- // Preview preparation should replace only the slide/timing projection.
30349
- ...doc,
30350
- duration: t,
30351
- blocks: slides,
30352
- audio
30353
- };
30354
- }
30355
30029
 
30356
30030
  // src/OutlinePanel.tsx
30357
30031
  import {
@@ -30361,8 +30035,8 @@ import {
30361
30035
  useRef as useRef42,
30362
30036
  useState as useState54
30363
30037
  } from "react";
30364
- import { flattenBlocks as flattenBlocks5, hasTemplate as hasTemplate3 } from "@bendyline/squisq/doc";
30365
- import { extractPlainText as extractPlainText5 } from "@bendyline/squisq/markdown";
30038
+ import { flattenBlocks as flattenBlocks5, hasTemplate as hasTemplate2 } from "@bendyline/squisq/doc";
30039
+ import { extractPlainText as extractPlainText4 } from "@bendyline/squisq/markdown";
30366
30040
 
30367
30041
  // src/outlineSource.ts
30368
30042
  import { parseMarkdown as parseMarkdown7 } from "@bendyline/squisq/markdown";
@@ -30625,10 +30299,10 @@ function OutlineNode({
30625
30299
  const heading = block.sourceHeading;
30626
30300
  const depth = heading?.depth ?? 1;
30627
30301
  const headingLine2 = heading?.position?.start.line;
30628
- const text = heading ? extractPlainText5(heading).trim() : "";
30302
+ const text = heading ? extractPlainText4(heading).trim() : "";
30629
30303
  const annotation = heading?.templateAnnotation;
30630
30304
  const tplName = annotation?.template;
30631
- const showChip = tplName && hasTemplate3(tplName);
30305
+ const showChip = tplName && hasTemplate2(tplName);
30632
30306
  const isActive = block.id === activeBlockId;
30633
30307
  const canPromote = !!heading && depth > 1;
30634
30308
  const canDemote = !!heading && depth < 6;
@@ -33752,6 +33426,25 @@ function usePresentationModeOptional() {
33752
33426
  }
33753
33427
  var POPUP_WIDTH = 1280;
33754
33428
  var POPUP_HEIGHT = 720;
33429
+ var FULLSCREEN_RELEASE_TIMEOUT_MS = 4e3;
33430
+ function waitForFullscreenRelease(ownerDocument, root, timeoutMs) {
33431
+ if (ownerDocument.fullscreenElement !== root) return Promise.resolve(true);
33432
+ return new Promise((resolve) => {
33433
+ const settle = (released) => {
33434
+ ownerDocument.removeEventListener("fullscreenchange", handleChange);
33435
+ window.clearTimeout(timer);
33436
+ resolve(released);
33437
+ };
33438
+ const handleChange = () => {
33439
+ if (ownerDocument.fullscreenElement !== root) settle(true);
33440
+ };
33441
+ const timer = window.setTimeout(
33442
+ () => settle(ownerDocument.fullscreenElement !== root),
33443
+ timeoutMs
33444
+ );
33445
+ ownerDocument.addEventListener("fullscreenchange", handleChange);
33446
+ });
33447
+ }
33755
33448
  function copyDocumentStyles(source, target) {
33756
33449
  for (const sheet of Array.from(source.styleSheets)) {
33757
33450
  if (sheet.href) {
@@ -33869,7 +33562,12 @@ function PresentationModeProvider({
33869
33562
  return;
33870
33563
  }
33871
33564
  }
33872
- if (ownerDocument.fullscreenElement === root) {
33565
+ const released = await waitForFullscreenRelease(
33566
+ ownerDocument,
33567
+ root,
33568
+ FULLSCREEN_RELEASE_TIMEOUT_MS
33569
+ );
33570
+ if (!released) {
33873
33571
  setError("Still in full screen. Press Escape to leave it.");
33874
33572
  return;
33875
33573
  }
@@ -36941,8 +36639,8 @@ export {
36941
36639
  persistFromWrite,
36942
36640
  WysiwygEditor,
36943
36641
  InlinePreviewGutter,
36944
- documentTitleFromFileName,
36945
36642
  buildPreviewDoc,
36643
+ documentTitleFromFileName,
36946
36644
  OutlinePanel,
36947
36645
  CodeContextZones,
36948
36646
  BlockCardView,