@bendyline/squisq-editor-react 2.3.1 → 2.3.3

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.
@@ -75,6 +75,9 @@ var EXT_TO_LANGUAGE = {
75
75
  };
76
76
  var MARKDOWN_MODE_LANGUAGES = /* @__PURE__ */ new Set(["markdown", "plaintext"]);
77
77
  var IMAGE_MODE_LANGUAGES = /* @__PURE__ */ new Set(["image"]);
78
+ var MONACO_LANGUAGE_ALIASES = {
79
+ jsonc: "json"
80
+ };
78
81
  function extractExtension(fileName) {
79
82
  const trimmed = fileName.trim();
80
83
  if (!trimmed) return null;
@@ -93,7 +96,8 @@ function detectLanguageFromFileName(fileName) {
93
96
  return EXT_TO_LANGUAGE[ext] ?? null;
94
97
  }
95
98
  function resolveFileKind(fileName, language) {
96
- const resolvedLanguage = language ?? (fileName ? detectLanguageFromFileName(fileName) : null);
99
+ const requestedLanguage = language ?? (fileName ? detectLanguageFromFileName(fileName) : null);
100
+ const resolvedLanguage = requestedLanguage ? MONACO_LANGUAGE_ALIASES[requestedLanguage.toLowerCase()] ?? requestedLanguage : null;
97
101
  if (!resolvedLanguage) {
98
102
  return { mode: "markdown", language: "markdown" };
99
103
  }
@@ -2679,7 +2683,7 @@ var TEMPLATE_ENTRIES = [
2679
2683
  {
2680
2684
  name: "sectionHeader",
2681
2685
  label: "Section Header",
2682
- description: "A clean section break with a prominent title and optional subtitle.",
2686
+ description: "A clean section break with a prominent title and optional background image.",
2683
2687
  icon: /* @__PURE__ */ jsxs5(TemplateIcon, { children: [
2684
2688
  /* @__PURE__ */ jsx8("rect", { x: 4, y: 4, width: 3, height: 32, rx: 1, fill: FA }),
2685
2689
  /* @__PURE__ */ jsx8("rect", { x: 11, y: 8, width: 36, height: 6, rx: 1, fill: F2 }),
@@ -2687,6 +2691,18 @@ var TEMPLATE_ENTRIES = [
2687
2691
  /* @__PURE__ */ jsx8("rect", { x: 11, y: 24, width: 20, height: 2.5, rx: 1, fill: F1, opacity: 0.7 })
2688
2692
  ] })
2689
2693
  },
2694
+ {
2695
+ name: "content",
2696
+ label: "Content",
2697
+ description: "Shows a heading and the complete body in a loss-averse content-first layout.",
2698
+ icon: /* @__PURE__ */ jsxs5(TemplateIcon, { children: [
2699
+ /* @__PURE__ */ jsx8("rect", { x: 7, y: 7, width: 34, height: 5, rx: 1, fill: FA }),
2700
+ /* @__PURE__ */ jsx8("rect", { x: 7, y: 17, width: 42, height: 2.5, rx: 1, fill: F2 }),
2701
+ /* @__PURE__ */ jsx8("rect", { x: 7, y: 22, width: 38, height: 2.5, rx: 1, fill: F1 }),
2702
+ /* @__PURE__ */ jsx8("rect", { x: 7, y: 27, width: 41, height: 2.5, rx: 1, fill: F1 }),
2703
+ /* @__PURE__ */ jsx8("rect", { x: 7, y: 32, width: 30, height: 2.5, rx: 1, fill: F1, opacity: 0.7 })
2704
+ ] })
2705
+ },
2690
2706
  {
2691
2707
  name: "statHighlight",
2692
2708
  label: "Stat Highlight",
@@ -8423,6 +8439,14 @@ var CODE_SNIPPET_LANGUAGES = [
8423
8439
  monacoLanguage: "json",
8424
8440
  starter: '{\n "key": "value"\n}'
8425
8441
  },
8442
+ {
8443
+ fenceLanguage: "jsonc",
8444
+ label: "JSONC",
8445
+ // Monaco exposes JSON-with-comments through its `json` language service;
8446
+ // it does not register a separate rich-service `jsonc` language id.
8447
+ monacoLanguage: "json",
8448
+ starter: '{\n // Comments are allowed.\n "key": "value"\n}'
8449
+ },
8426
8450
  {
8427
8451
  fenceLanguage: "html",
8428
8452
  label: "HTML",
@@ -8540,7 +8564,7 @@ var SPECIAL_FENCE_LANGUAGES = /* @__PURE__ */ new Set([
8540
8564
  "timeline",
8541
8565
  "mermaid"
8542
8566
  ]);
8543
- var MONACO_LANGUAGE_ALIASES = {
8567
+ var MONACO_LANGUAGE_ALIASES2 = {
8544
8568
  c: "c",
8545
8569
  "c++": "cpp",
8546
8570
  "c#": "csharp",
@@ -8550,6 +8574,7 @@ var MONACO_LANGUAGE_ALIASES = {
8550
8574
  htm: "html",
8551
8575
  js: "javascript",
8552
8576
  jsx: "javascript",
8577
+ jsonc: "json",
8553
8578
  md: "markdown",
8554
8579
  py: "python",
8555
8580
  rb: "ruby",
@@ -8570,7 +8595,7 @@ function isCodeSnippetFenceLanguage(language) {
8570
8595
  function monacoLanguageForFence(language) {
8571
8596
  const token = codeSnippetFenceLanguageToken(language);
8572
8597
  const catalogEntry = BY_FENCE_LANGUAGE.get(token);
8573
- return (catalogEntry?.monacoLanguage ?? MONACO_LANGUAGE_ALIASES[token] ?? token) || "plaintext";
8598
+ return (catalogEntry?.monacoLanguage ?? MONACO_LANGUAGE_ALIASES2[token] ?? token) || "plaintext";
8574
8599
  }
8575
8600
  function codeSnippetLanguageLabel(language) {
8576
8601
  const token = codeSnippetFenceLanguageToken(language);
@@ -16653,9 +16678,19 @@ function ManagerContent({
16653
16678
  import { createPortal as createPortal8 } from "react-dom";
16654
16679
 
16655
16680
  // src/mediaEntries.ts
16681
+ var IMAGE_EDIT_STATE_FILENAME = "state.json";
16682
+ function pathSegments(path) {
16683
+ return path.split(/[\\/]+/).filter(Boolean);
16684
+ }
16656
16685
  function isVisibleMediaEntry(entry) {
16657
- const basename = entry.name.slice(entry.name.lastIndexOf("/") + 1);
16658
- return basename.toLowerCase() !== ".gitignore";
16686
+ const segments = pathSegments(entry.name);
16687
+ const lowerSegments = segments.map((segment) => segment.toLowerCase());
16688
+ const basename = lowerSegments[lowerSegments.length - 1] ?? "";
16689
+ if (basename === ".gitignore") return false;
16690
+ if (lowerSegments.includes(".versions")) return false;
16691
+ const parent = lowerSegments[lowerSegments.length - 2] ?? "";
16692
+ const isImageEditState = basename === IMAGE_EDIT_STATE_FILENAME && (lowerSegments.includes(".imageedits") || parent.endsWith("_files"));
16693
+ return !isImageEditState;
16659
16694
  }
16660
16695
  function filterVisibleMediaEntries(entries) {
16661
16696
  return entries.filter(isVisibleMediaEntry);
@@ -17111,6 +17146,16 @@ function markPreviewRanges(ranges, selectedIndex) {
17111
17146
  return marks;
17112
17147
  }
17113
17148
 
17149
+ // src/platformShortcuts.ts
17150
+ function resolvePlatform(explicitPlatform) {
17151
+ if (explicitPlatform !== void 0) return explicitPlatform;
17152
+ if (typeof navigator === "undefined") return "";
17153
+ return navigator.platform;
17154
+ }
17155
+ function platformShortcut(key, platform) {
17156
+ return /Mac|iPhone|iPad|iPod/i.test(resolvePlatform(platform)) ? `\u2318${key}` : `Ctrl+${key}`;
17157
+ }
17158
+
17114
17159
  // src/toolbar/toolbarButtons.tsx
17115
17160
  import { jsx as jsx38 } from "react/jsx-runtime";
17116
17161
  var BUTTONS = [
@@ -17500,9 +17545,9 @@ function insertLayoutBlock(editor) {
17500
17545
  // src/Toolbar.tsx
17501
17546
  import { Fragment as Fragment13, jsx as jsx39, jsxs as jsxs29 } from "react/jsx-runtime";
17502
17547
  var VIEWS = [
17503
- { id: "wysiwyg", label: "Write", shortcut: "\u23181" },
17504
- { id: "raw", label: "Source", shortcut: "\u23182" },
17505
- { id: "preview", label: "Use", shortcut: "\u23183" }
17548
+ { id: "wysiwyg", label: "Write", shortcutKey: "1" },
17549
+ { id: "raw", label: "Source", shortcutKey: "2" },
17550
+ { id: "preview", label: "Use", shortcutKey: "3" }
17506
17551
  ];
17507
17552
  var BLOCK_META_KEYS = new Set(Object.keys(KNOWN_BLOCK_META_KEYS));
17508
17553
  function findBlockBySourceLine(blocks, lineNumber) {
@@ -18716,7 +18761,7 @@ ${TASK_LIST_MARKDOWN}
18716
18761
  }
18717
18762
  setActiveView(view.id);
18718
18763
  },
18719
- "data-tooltip": `${viewLabel} (${view.shortcut})`,
18764
+ "data-tooltip": `${viewLabel} (${platformShortcut(view.shortcutKey)})`,
18720
18765
  children: [
18721
18766
  /* @__PURE__ */ jsx39(
18722
18767
  "span",
@@ -25431,8 +25476,24 @@ function useCodeSnippetData(editor, blockId) {
25431
25476
  }, [editor, blockId, version]);
25432
25477
  }
25433
25478
 
25479
+ // src/codeSnippet/codeSnippetSizing.ts
25480
+ var CODE_SNIPPET_HEADER_HEIGHT = 34;
25481
+ var CODE_SNIPPET_LINE_HEIGHT = 20;
25482
+ var CODE_SNIPPET_VERTICAL_PADDING = 20;
25483
+ var CODE_SNIPPET_SHELL_BORDER = 2;
25484
+ var CODE_SNIPPET_EXTRA_LINES = 1;
25485
+ function codeSnippetAutoHeight(source) {
25486
+ const sourceLineCount = source.split("\n").length;
25487
+ return CODE_SNIPPET_SHELL_BORDER + CODE_SNIPPET_HEADER_HEIGHT + CODE_SNIPPET_VERTICAL_PADDING + (sourceLineCount + CODE_SNIPPET_EXTRA_LINES) * CODE_SNIPPET_LINE_HEIGHT;
25488
+ }
25489
+
25434
25490
  // src/codeSnippet/CodeSnippetWidget.tsx
25435
25491
  import { jsx as jsx47, jsxs as jsxs36 } from "react/jsx-runtime";
25492
+ var nextCodeSnippetModelInstanceId = 0;
25493
+ function allocateCodeSnippetModelInstanceId() {
25494
+ nextCodeSnippetModelInstanceId += 1;
25495
+ return nextCodeSnippetModelInstanceId;
25496
+ }
25436
25497
  function schemeFromHost(host) {
25437
25498
  return host?.closest("[data-theme]")?.dataset.theme === "dark" ? "dark" : "light";
25438
25499
  }
@@ -25471,6 +25532,7 @@ function CodeSnippetWidget({
25471
25532
  const { ready } = useMonacoLoader();
25472
25533
  const colorScheme = useHostColorScheme(host);
25473
25534
  const editable = useEditorEditable(editor);
25535
+ const [modelInstanceId] = useState39(allocateCodeSnippetModelInstanceId);
25474
25536
  const handleChange = useCallback34(
25475
25537
  (value) => {
25476
25538
  if (!editable) return;
@@ -25489,37 +25551,46 @@ function CodeSnippetWidget({
25489
25551
  /[^a-z0-9_-]/g,
25490
25552
  "-"
25491
25553
  );
25492
- return /* @__PURE__ */ jsxs36("div", { className: "squisq-code-snippet-shell", "data-language": data.fenceLanguage, children: [
25493
- /* @__PURE__ */ jsx47("div", { className: "squisq-code-snippet-header", children: /* @__PURE__ */ jsxs36("span", { className: "squisq-code-snippet-title", children: [
25494
- /* @__PURE__ */ jsx47(Icon, { icon: "fa-solid fa-file-code" }),
25495
- /* @__PURE__ */ jsx47("span", { children: data.label })
25496
- ] }) }),
25497
- /* @__PURE__ */ jsx47("div", { className: "squisq-code-snippet-editor", "aria-label": `${data.label} code editor`, children: ready ? /* @__PURE__ */ jsx47(
25498
- Ft,
25499
- {
25500
- path: `inmemory://squisq/code-snippet/${blockId}.${modelSuffix || "txt"}`,
25501
- language: data.monacoLanguage,
25502
- theme: colorScheme === "dark" ? "vs-dark" : "vs",
25503
- value: data.source,
25504
- onChange: handleChange,
25505
- onMount: handleMount,
25506
- options: {
25507
- automaticLayout: true,
25508
- contextmenu: true,
25509
- folding: true,
25510
- fontSize: 13,
25511
- lineNumbers: "on",
25512
- minimap: { enabled: false },
25513
- padding: { top: 10, bottom: 10 },
25514
- readOnly: !editable,
25515
- renderLineHighlight: "line",
25516
- scrollBeyondLastLine: false,
25517
- tabSize: 2,
25518
- wordWrap: "off"
25519
- }
25520
- }
25521
- ) : /* @__PURE__ */ jsx47("div", { className: "squisq-code-snippet-loading", children: "Loading code editor\u2026" }) })
25522
- ] });
25554
+ return /* @__PURE__ */ jsxs36(
25555
+ "div",
25556
+ {
25557
+ className: "squisq-code-snippet-shell",
25558
+ "data-language": data.fenceLanguage,
25559
+ style: { height: codeSnippetAutoHeight(data.source) },
25560
+ children: [
25561
+ /* @__PURE__ */ jsx47("div", { className: "squisq-code-snippet-header", children: /* @__PURE__ */ jsxs36("span", { className: "squisq-code-snippet-title", children: [
25562
+ /* @__PURE__ */ jsx47(Icon, { icon: "fa-solid fa-file-code" }),
25563
+ /* @__PURE__ */ jsx47("span", { children: data.label })
25564
+ ] }) }),
25565
+ /* @__PURE__ */ jsx47("div", { className: "squisq-code-snippet-editor", "aria-label": `${data.label} code editor`, children: ready ? /* @__PURE__ */ jsx47(
25566
+ Ft,
25567
+ {
25568
+ path: `inmemory://squisq/code-snippet/${modelInstanceId}/${blockId}.${modelSuffix || "txt"}`,
25569
+ language: data.monacoLanguage,
25570
+ theme: colorScheme === "dark" ? "vs-dark" : "vs",
25571
+ value: data.source,
25572
+ onChange: handleChange,
25573
+ onMount: handleMount,
25574
+ options: {
25575
+ automaticLayout: true,
25576
+ contextmenu: true,
25577
+ folding: true,
25578
+ fontSize: 13,
25579
+ lineHeight: CODE_SNIPPET_LINE_HEIGHT,
25580
+ lineNumbers: "on",
25581
+ minimap: { enabled: false },
25582
+ padding: { top: 10, bottom: 10 },
25583
+ readOnly: !editable,
25584
+ renderLineHighlight: "line",
25585
+ scrollBeyondLastLine: false,
25586
+ tabSize: 2,
25587
+ wordWrap: "off"
25588
+ }
25589
+ }
25590
+ ) : /* @__PURE__ */ jsx47("div", { className: "squisq-code-snippet-loading", children: "Loading code editor\u2026" }) })
25591
+ ]
25592
+ }
25593
+ );
25523
25594
  }
25524
25595
 
25525
25596
  // src/codeSnippet/CodeSnippetExtension.ts
@@ -29945,6 +30016,7 @@ function InlinePreviewGutter({
29945
30016
  });
29946
30017
  }, [items, connectorTops]);
29947
30018
  const isEmpty2 = items.length === 0;
30019
+ if (isEmpty2) return null;
29948
30020
  const totalWidth = width + connectorWidth;
29949
30021
  const gutterStyle = {
29950
30022
  position: "relative",
@@ -30035,14 +30107,7 @@ function InlinePreviewGutter({
30035
30107
  })
30036
30108
  }
30037
30109
  ),
30038
- isEmpty2 ? /* @__PURE__ */ jsx55(
30039
- "div",
30040
- {
30041
- className: "squisq-inline-preview-empty",
30042
- style: { position: "absolute", top: 12, left: connectorWidth + 12, right: 12 },
30043
- children: /* @__PURE__ */ jsx55("p", { children: "Tag a heading with a template to see a preview here." })
30044
- }
30045
- ) : /* @__PURE__ */ jsx55(MediaContext3.Provider, { value: mediaProvider ?? null, children: items.map((item) => {
30110
+ /* @__PURE__ */ jsx55(MediaContext3.Provider, { value: mediaProvider ?? null, children: items.map((item) => {
30046
30111
  const top = positions.get(item.id);
30047
30112
  const hidden = top == null;
30048
30113
  return /* @__PURE__ */ jsxs42(
@@ -32450,7 +32515,12 @@ function preparePopupDocument(popup, source, title) {
32450
32515
  function presentationTitle(docTitle) {
32451
32516
  return typeof docTitle === "string" && docTitle.trim() ? `${docTitle.trim()} - Presentation` : "Squisq Presentation";
32452
32517
  }
32453
- function PresentationModeProvider({ rootRef, children }) {
32518
+ function PresentationModeProvider({
32519
+ rootRef,
32520
+ children,
32521
+ allowWindow = true,
32522
+ allowFullscreen = true
32523
+ }) {
32454
32524
  const { activeView, colorScheme, doc } = useEditorContext();
32455
32525
  const popupNameId = useId14().replace(/[^a-zA-Z0-9_-]/g, "");
32456
32526
  const [selectedTarget, setSelectedTarget] = useState55("control");
@@ -32464,6 +32534,14 @@ function PresentationModeProvider({ rootRef, children }) {
32464
32534
  const popupRef = useRef46(null);
32465
32535
  const popupCleanupRef = useRef46(null);
32466
32536
  const fullscreenSupported = typeof document !== "undefined" && typeof document.documentElement.requestFullscreen === "function";
32537
+ const availableTargets = useMemo41(
32538
+ () => [
32539
+ "control",
32540
+ ...allowWindow ? ["window"] : [],
32541
+ ...allowFullscreen ? ["fullscreen"] : []
32542
+ ],
32543
+ [allowFullscreen, allowWindow]
32544
+ );
32467
32545
  const releasePopup = useCallback45((closeWindow) => {
32468
32546
  const popup = popupRef.current;
32469
32547
  popupCleanupRef.current?.();
@@ -32515,6 +32593,7 @@ function PresentationModeProvider({ rootRef, children }) {
32515
32593
  return;
32516
32594
  }
32517
32595
  if (selectedTarget === "fullscreen") {
32596
+ if (!allowFullscreen) return;
32518
32597
  if (typeof root.requestFullscreen !== "function") {
32519
32598
  setError("Browser full screen is not available here.");
32520
32599
  return;
@@ -32527,6 +32606,7 @@ function PresentationModeProvider({ rootRef, children }) {
32527
32606
  }
32528
32607
  return;
32529
32608
  }
32609
+ if (!allowWindow) return;
32530
32610
  let popup = null;
32531
32611
  try {
32532
32612
  const screenWidth = window.screen?.availWidth || POPUP_WIDTH;
@@ -32567,15 +32647,29 @@ function PresentationModeProvider({ rootRef, children }) {
32567
32647
  else if (popup && !popup.closed) popup.close();
32568
32648
  setError("The presentation window was blocked. Allow pop-ups and try again.");
32569
32649
  }
32570
- }, [doc?.frontmatter?.title, popupNameId, releasePopup, rootRef, selectedTarget]);
32650
+ }, [
32651
+ allowFullscreen,
32652
+ allowWindow,
32653
+ doc?.frontmatter?.title,
32654
+ popupNameId,
32655
+ releasePopup,
32656
+ rootRef,
32657
+ selectedTarget
32658
+ ]);
32571
32659
  const selectTarget = useCallback45(
32572
32660
  (target) => {
32661
+ if (!availableTargets.includes(target)) return;
32573
32662
  if (target === selectedTarget) return;
32574
32663
  setSelectedTarget(target);
32575
32664
  if (activeTargetRef.current !== null) void stop();
32576
32665
  },
32577
- [selectedTarget, stop]
32666
+ [availableTargets, selectedTarget, stop]
32578
32667
  );
32668
+ useEffect45(() => {
32669
+ if (availableTargets.includes(selectedTarget)) return;
32670
+ setSelectedTarget("control");
32671
+ if (activeTargetRef.current !== null) void stop();
32672
+ }, [availableTargets, selectedTarget, stop]);
32579
32673
  useEffect45(() => {
32580
32674
  const root = rootRef.current;
32581
32675
  const ownerDocument = root?.ownerDocument;
@@ -32643,12 +32737,22 @@ function PresentationModeProvider({ rootRef, children }) {
32643
32737
  selectedTarget,
32644
32738
  activeTarget,
32645
32739
  popupRoot,
32740
+ availableTargets,
32646
32741
  fullscreenSupported,
32647
32742
  selectTarget,
32648
32743
  start,
32649
32744
  stop
32650
32745
  }),
32651
- [selectedTarget, activeTarget, popupRoot, fullscreenSupported, selectTarget, start, stop]
32746
+ [
32747
+ selectedTarget,
32748
+ activeTarget,
32749
+ popupRoot,
32750
+ availableTargets,
32751
+ fullscreenSupported,
32752
+ selectTarget,
32753
+ start,
32754
+ stop
32755
+ ]
32652
32756
  );
32653
32757
  const exitButton = activeTarget ? /* @__PURE__ */ jsxs47(
32654
32758
  "button",
@@ -32699,13 +32803,22 @@ var MENU_WIDTH = 340;
32699
32803
  var MENU_MARGIN = 8;
32700
32804
  var MENU_GAP = 4;
32701
32805
  function PresentationModeControl() {
32702
- const { selectedTarget, activeTarget, fullscreenSupported, selectTarget, start, stop } = usePresentationMode();
32806
+ const {
32807
+ selectedTarget,
32808
+ activeTarget,
32809
+ availableTargets,
32810
+ fullscreenSupported,
32811
+ selectTarget,
32812
+ start,
32813
+ stop
32814
+ } = usePresentationMode();
32703
32815
  const { colorScheme } = useEditorContext();
32704
32816
  const triggerRef = useRef46(null);
32705
32817
  const menuRef = useRef46(null);
32706
32818
  const [open, setOpen] = useState55(false);
32707
32819
  const [anchor, setAnchor] = useState55(null);
32708
- const selected = PRESENTATION_OPTIONS.find((option) => option.target === selectedTarget) ?? PRESENTATION_OPTIONS[0];
32820
+ const options = PRESENTATION_OPTIONS.filter((option) => availableTargets.includes(option.target));
32821
+ const selected = options.find((option) => option.target === selectedTarget) ?? PRESENTATION_OPTIONS[0];
32709
32822
  const updatePosition = useCallback45(() => {
32710
32823
  const trigger = triggerRef.current;
32711
32824
  if (!trigger) return;
@@ -32770,7 +32883,7 @@ function PresentationModeControl() {
32770
32883
  children: /* @__PURE__ */ jsx63(Icon, { icon: "fa-solid fa-display" })
32771
32884
  }
32772
32885
  ),
32773
- /* @__PURE__ */ jsx63(
32886
+ options.length > 1 && /* @__PURE__ */ jsx63(
32774
32887
  "button",
32775
32888
  {
32776
32889
  ref: triggerRef,
@@ -32817,7 +32930,7 @@ function PresentationModeControl() {
32817
32930
  const nextIndex = event.key === "Home" ? 0 : event.key === "End" ? items.length - 1 : event.key === "ArrowUp" ? (currentIndex - 1 + items.length) % items.length : (currentIndex + 1) % items.length;
32818
32931
  items[nextIndex]?.focus();
32819
32932
  },
32820
- children: PRESENTATION_OPTIONS.map((option) => {
32933
+ children: options.map((option) => {
32821
32934
  const isSelected = option.target === selectedTarget;
32822
32935
  const disabled = option.target === "fullscreen" && !fullscreenSupported;
32823
32936
  return /* @__PURE__ */ jsxs47(
@@ -34372,6 +34485,7 @@ function EditorShell({
34372
34485
  articleId = "untitled",
34373
34486
  basePath = "/",
34374
34487
  onChange,
34488
+ onLinkClick,
34375
34489
  colorScheme = "light",
34376
34490
  className,
34377
34491
  height = "100vh",
@@ -34390,6 +34504,9 @@ function EditorShell({
34390
34504
  toolbarSlotRight,
34391
34505
  statusBarSlotRight,
34392
34506
  showPlayTab = true,
34507
+ allowPresentationWindow = true,
34508
+ allowPresentationFullscreen = true,
34509
+ allowPrint = true,
34393
34510
  submitOnEnter,
34394
34511
  codeContext,
34395
34512
  fullWidth = false,
@@ -34470,6 +34587,7 @@ function EditorShell({
34470
34587
  {
34471
34588
  basePath,
34472
34589
  onChange,
34590
+ onLinkClick,
34473
34591
  className,
34474
34592
  height,
34475
34593
  minHeight,
@@ -34483,6 +34601,9 @@ function EditorShell({
34483
34601
  toolbarSlotRight,
34484
34602
  statusBarSlotRight,
34485
34603
  showPlayTab,
34604
+ allowPresentationWindow,
34605
+ allowPresentationFullscreen,
34606
+ allowPrint,
34486
34607
  submitOnEnter,
34487
34608
  codeContext,
34488
34609
  fullWidth,
@@ -34505,24 +34626,35 @@ function EditorShell({
34505
34626
  }
34506
34627
  ) });
34507
34628
  }
34508
- function UseModeToolbarControls() {
34629
+ function UseModeToolbarControls({ allowPrint }) {
34509
34630
  const printMode = usePrintMode();
34510
34631
  if (printMode.active) return /* @__PURE__ */ jsx71(PrintPreviewToolbar, {});
34511
34632
  return /* @__PURE__ */ jsxs53(Fragment21, { children: [
34512
34633
  /* @__PURE__ */ jsx71(PreviewToolbarControls, {}),
34513
34634
  /* @__PURE__ */ jsx71(PresentationModeControl, {}),
34514
- /* @__PURE__ */ jsx71(PrintModeControl, {})
34635
+ allowPrint && /* @__PURE__ */ jsx71(PrintModeControl, {})
34515
34636
  ] });
34516
34637
  }
34517
34638
  function UseModeProviders({
34518
34639
  rootRef,
34519
- children
34640
+ children,
34641
+ allowPresentationWindow,
34642
+ allowPresentationFullscreen
34520
34643
  }) {
34521
- return /* @__PURE__ */ jsx71(PresentationModeProvider, { rootRef, children: /* @__PURE__ */ jsx71(PrintModeProvider, { rootRef, children }) });
34644
+ return /* @__PURE__ */ jsx71(
34645
+ PresentationModeProvider,
34646
+ {
34647
+ rootRef,
34648
+ allowWindow: allowPresentationWindow,
34649
+ allowFullscreen: allowPresentationFullscreen,
34650
+ children: /* @__PURE__ */ jsx71(PrintModeProvider, { rootRef, children })
34651
+ }
34652
+ );
34522
34653
  }
34523
34654
  function EditorShellInner({
34524
34655
  basePath,
34525
34656
  onChange,
34657
+ onLinkClick,
34526
34658
  className,
34527
34659
  height,
34528
34660
  minHeight,
@@ -34536,6 +34668,9 @@ function EditorShellInner({
34536
34668
  toolbarSlotRight,
34537
34669
  statusBarSlotRight,
34538
34670
  showPlayTab,
34671
+ allowPresentationWindow,
34672
+ allowPresentationFullscreen,
34673
+ allowPrint,
34539
34674
  submitOnEnter,
34540
34675
  codeContext,
34541
34676
  fullWidth,
@@ -34723,14 +34858,51 @@ ${snippet}` : snippet);
34723
34858
  },
34724
34859
  [showPlayTab]
34725
34860
  );
34861
+ const handleShellLinkClick = useCallback50(
34862
+ (event) => {
34863
+ if (!onLinkClick || event.button !== 0) return;
34864
+ const anchor = event.target.closest?.("a[href]");
34865
+ if (!anchor || !event.currentTarget.contains(anchor)) return;
34866
+ const href = anchor.getAttribute("href");
34867
+ if (!href || onLinkClick(href) === false) return;
34868
+ event.preventDefault();
34869
+ event.stopPropagation();
34870
+ },
34871
+ [onLinkClick]
34872
+ );
34873
+ const handleShellLinkMouseOver = useCallback50(
34874
+ (event) => {
34875
+ if (!onLinkClick) return;
34876
+ const anchor = event.target.closest?.("a[href]");
34877
+ if (!anchor || !event.currentTarget.contains(anchor)) return;
34878
+ const href = anchor.getAttribute("href");
34879
+ if (!href || anchor.hasAttribute("title") && !anchor.hasAttribute("data-squisq-link-title")) {
34880
+ return;
34881
+ }
34882
+ anchor.setAttribute("title", href);
34883
+ anchor.setAttribute("data-squisq-link-title", "true");
34884
+ },
34885
+ [onLinkClick]
34886
+ );
34887
+ const handleShellLinkMouseOut = useCallback50((event) => {
34888
+ const anchor = event.target.closest?.("a[data-squisq-link-title]");
34889
+ if (!anchor || !event.currentTarget.contains(anchor)) return;
34890
+ if (event.relatedTarget instanceof Node && anchor.contains(event.relatedTarget)) return;
34891
+ anchor.removeAttribute("title");
34892
+ anchor.removeAttribute("data-squisq-link-title");
34893
+ }, []);
34726
34894
  const autoGrow = minHeight !== void 0 || maxHeight !== void 0;
34727
34895
  return /* @__PURE__ */ jsxs53(
34728
34896
  "div",
34729
34897
  {
34730
34898
  ref: shellRef,
34731
34899
  onKeyDown: handleShellKeyDown,
34900
+ onClickCapture: handleShellLinkClick,
34901
+ onMouseOverCapture: handleShellLinkMouseOver,
34902
+ onMouseOutCapture: handleShellLinkMouseOut,
34732
34903
  className: `squisq-editor-shell ${className || ""}`,
34733
34904
  "data-theme": colorScheme,
34905
+ "data-link-handler": onLinkClick ? "true" : void 0,
34734
34906
  "data-full-width": fullWidth ? "true" : void 0,
34735
34907
  "data-thin-margins": thinMargins ? "true" : void 0,
34736
34908
  "data-outline-visible": isMarkdownMode && outlineVisible ? "true" : void 0,
@@ -34757,109 +34929,126 @@ ${snippet}` : snippet);
34757
34929
  },
34758
34930
  ...containerProps,
34759
34931
  children: [
34760
- /* @__PURE__ */ jsx71(CustomThemeProvider, { docThemes, onDocThemesChange, children: /* @__PURE__ */ jsx71(PreviewSettingsProvider, { doc, themeOverride, children: /* @__PURE__ */ jsxs53(UseModeProviders, { rootRef: shellRef, children: [
34761
- isImageMode ? (toolbarSlotLeft || toolbarSlotRight) && /* @__PURE__ */ jsxs53("div", { className: "squisq-editor-header squisq-editor-header--image", children: [
34762
- toolbarSlotLeft,
34763
- /* @__PURE__ */ jsx71("div", { style: { flex: 1 } }),
34764
- toolbarSlotRight
34765
- ] }) : /* @__PURE__ */ jsx71("div", { className: "squisq-editor-header", children: /* @__PURE__ */ jsx71(
34766
- Toolbar,
34767
- {
34768
- showFiles,
34769
- fileCount: mediaCount,
34770
- onToggleFiles: !isCodeMode && filesToggleEnabled ? handleToggleFiles : void 0,
34771
- slotLeft: toolbarSlotLeft,
34772
- slotAfterTabs: !isCodeMode && isPreview && /* @__PURE__ */ jsx71(UseModeToolbarControls, {}),
34773
- slotAfterActions: toolbarSlotAfterActions,
34774
- slotRight: toolbarSlotRight,
34775
- showPlayTab
34776
- }
34777
- ) }),
34778
- /* @__PURE__ */ jsxs53(
34779
- "div",
34780
- {
34781
- className: "squisq-editor-content",
34782
- style: {
34783
- flex: autoGrow ? "1 1 auto" : 1,
34784
- overflowY: autoGrow ? "auto" : "hidden",
34785
- overflowX: "hidden",
34786
- minHeight: 0,
34787
- position: "relative",
34788
- display: "flex"
34789
- },
34790
- children: [
34791
- /* @__PURE__ */ jsxs53(
34792
- "div",
34793
- {
34794
- style: {
34795
- flex: autoGrow ? "1 1 auto" : 1,
34796
- overflow: autoGrow ? "visible" : "hidden",
34797
- minHeight: 0,
34798
- position: "relative"
34799
- },
34800
- children: [
34801
- isImageMode && imageSrc && (imageMode === "edit" && imageEditorContainer ? /* @__PURE__ */ jsx71(
34802
- ImageEditor,
34803
- {
34804
- filesContainer: imageEditorContainer,
34805
- initialSrc: imageSrc,
34806
- allowVersioning,
34807
- versioningAutoSaveIdleMs,
34808
- onExport: onImageExport,
34809
- surface: colorScheme === "dark" ? DARK_SURFACE : LIGHT_SURFACE
34810
- }
34811
- ) : /* @__PURE__ */ jsx71(ImageViewer, { src: imageSrc, alt: imageAlt, theme: colorScheme })),
34812
- !isImageMode && activeView === "raw" && /* @__PURE__ */ jsxs53("div", { className: "squisq-editor-with-gutter", children: [
34813
- isMarkdownMode && outlineVisible && /* @__PURE__ */ jsx71(OutlinePanel, { width: outlineWidth, readOnly }, "outline"),
34814
- isCardMode ? /* @__PURE__ */ jsx71(
34815
- BlockCardView,
34816
- {
34817
- blockCount,
34818
- activeBlockKey,
34819
- onPrev: prevBlock,
34820
- onNext: nextBlock,
34821
- onAdd: addBlock,
34822
- children: /* @__PURE__ */ jsx71("div", { className: "squisq-raw-editor-container", children: /* @__PURE__ */ jsx71(
34932
+ /* @__PURE__ */ jsx71(CustomThemeProvider, { docThemes, onDocThemesChange, children: /* @__PURE__ */ jsx71(PreviewSettingsProvider, { doc, themeOverride, children: /* @__PURE__ */ jsxs53(
34933
+ UseModeProviders,
34934
+ {
34935
+ rootRef: shellRef,
34936
+ allowPresentationWindow,
34937
+ allowPresentationFullscreen,
34938
+ children: [
34939
+ isImageMode ? (toolbarSlotLeft || toolbarSlotRight) && /* @__PURE__ */ jsxs53("div", { className: "squisq-editor-header squisq-editor-header--image", children: [
34940
+ toolbarSlotLeft,
34941
+ /* @__PURE__ */ jsx71("div", { style: { flex: 1 } }),
34942
+ toolbarSlotRight
34943
+ ] }) : /* @__PURE__ */ jsx71("div", { className: "squisq-editor-header", children: /* @__PURE__ */ jsx71(
34944
+ Toolbar,
34945
+ {
34946
+ showFiles,
34947
+ fileCount: mediaCount,
34948
+ onToggleFiles: !isCodeMode && filesToggleEnabled ? handleToggleFiles : void 0,
34949
+ slotLeft: toolbarSlotLeft,
34950
+ slotAfterTabs: !isCodeMode && isPreview && /* @__PURE__ */ jsx71(UseModeToolbarControls, { allowPrint }),
34951
+ slotAfterActions: toolbarSlotAfterActions,
34952
+ slotRight: toolbarSlotRight,
34953
+ showPlayTab
34954
+ }
34955
+ ) }),
34956
+ /* @__PURE__ */ jsxs53(
34957
+ "div",
34958
+ {
34959
+ className: "squisq-editor-content",
34960
+ style: {
34961
+ flex: autoGrow ? "1 1 auto" : 1,
34962
+ overflowY: autoGrow ? "auto" : "hidden",
34963
+ overflowX: "hidden",
34964
+ minHeight: 0,
34965
+ position: "relative",
34966
+ display: "flex"
34967
+ },
34968
+ children: [
34969
+ /* @__PURE__ */ jsxs53(
34970
+ "div",
34971
+ {
34972
+ style: {
34973
+ flex: autoGrow ? "1 1 auto" : 1,
34974
+ overflow: autoGrow ? "visible" : "hidden",
34975
+ minHeight: 0,
34976
+ position: "relative"
34977
+ },
34978
+ children: [
34979
+ isImageMode && imageSrc && (imageMode === "edit" && imageEditorContainer ? /* @__PURE__ */ jsx71(
34980
+ ImageEditor,
34981
+ {
34982
+ filesContainer: imageEditorContainer,
34983
+ initialSrc: imageSrc,
34984
+ allowVersioning,
34985
+ versioningAutoSaveIdleMs,
34986
+ onExport: onImageExport,
34987
+ surface: colorScheme === "dark" ? DARK_SURFACE : LIGHT_SURFACE
34988
+ }
34989
+ ) : /* @__PURE__ */ jsx71(ImageViewer, { src: imageSrc, alt: imageAlt, theme: colorScheme })),
34990
+ !isImageMode && activeView === "raw" && /* @__PURE__ */ jsxs53("div", { className: "squisq-editor-with-gutter", children: [
34991
+ isMarkdownMode && outlineVisible && /* @__PURE__ */ jsx71(OutlinePanel, { width: outlineWidth, readOnly }, "outline"),
34992
+ isCardMode ? /* @__PURE__ */ jsx71(
34993
+ BlockCardView,
34994
+ {
34995
+ blockCount,
34996
+ activeBlockKey,
34997
+ onPrev: prevBlock,
34998
+ onNext: nextBlock,
34999
+ onAdd: addBlock,
35000
+ children: /* @__PURE__ */ jsx71("div", { className: "squisq-raw-editor-container", children: /* @__PURE__ */ jsx71(
35001
+ RawEditor,
35002
+ {
35003
+ monacoTheme: colorScheme === "dark" ? "vs-dark" : "vs",
35004
+ submitOnEnter,
35005
+ readOnly
35006
+ }
35007
+ ) }, "raw-editor")
35008
+ },
35009
+ "raw-card"
35010
+ ) : /* @__PURE__ */ jsx71("div", { className: "squisq-raw-editor-container", children: /* @__PURE__ */ jsx71(
34823
35011
  RawEditor,
34824
35012
  {
34825
35013
  monacoTheme: colorScheme === "dark" ? "vs-dark" : "vs",
34826
35014
  submitOnEnter,
34827
35015
  readOnly
34828
35016
  }
34829
- ) }, "raw-editor")
34830
- },
34831
- "raw-card"
34832
- ) : /* @__PURE__ */ jsx71("div", { className: "squisq-raw-editor-container", children: /* @__PURE__ */ jsx71(
34833
- RawEditor,
34834
- {
34835
- monacoTheme: colorScheme === "dark" ? "vs-dark" : "vs",
34836
- submitOnEnter,
34837
- readOnly
34838
- }
34839
- ) }, "raw-editor"),
34840
- isCodeMode && codeContext && /* @__PURE__ */ jsx71(CodeContextZones, { options: codeContext }, "code-context"),
34841
- isMarkdownMode && isCardMode && inlinePreviewVisible && /* @__PURE__ */ jsx71(BlockPreviewPanel, { basePath }, "block-preview"),
34842
- isMarkdownMode && !isCardMode && inlinePreviewVisible && /* @__PURE__ */ jsx71(
34843
- InlinePreviewGutter,
34844
- {
34845
- width: inlinePreviewWidth,
34846
- basePath,
34847
- mediaProvider
34848
- },
34849
- "inline"
34850
- )
34851
- ] }, "raw-shell"),
34852
- isMarkdownMode && activeView === "wysiwyg" && /* @__PURE__ */ jsxs53("div", { className: "squisq-editor-with-gutter", children: [
34853
- outlineVisible && /* @__PURE__ */ jsx71(OutlinePanel, { width: outlineWidth, readOnly }, "outline"),
34854
- isCardMode ? /* @__PURE__ */ jsx71(
34855
- BlockCardView,
34856
- {
34857
- blockCount,
34858
- activeBlockKey,
34859
- onPrev: prevBlock,
34860
- onNext: nextBlock,
34861
- onAdd: addBlock,
34862
- children: /* @__PURE__ */ jsx71(
35017
+ ) }, "raw-editor"),
35018
+ isCodeMode && codeContext && /* @__PURE__ */ jsx71(CodeContextZones, { options: codeContext }, "code-context"),
35019
+ isMarkdownMode && isCardMode && inlinePreviewVisible && /* @__PURE__ */ jsx71(BlockPreviewPanel, { basePath }, "block-preview"),
35020
+ isMarkdownMode && !isCardMode && inlinePreviewVisible && /* @__PURE__ */ jsx71(
35021
+ InlinePreviewGutter,
35022
+ {
35023
+ width: inlinePreviewWidth,
35024
+ basePath,
35025
+ mediaProvider
35026
+ },
35027
+ "inline"
35028
+ )
35029
+ ] }, "raw-shell"),
35030
+ isMarkdownMode && activeView === "wysiwyg" && /* @__PURE__ */ jsxs53("div", { className: "squisq-editor-with-gutter", children: [
35031
+ outlineVisible && /* @__PURE__ */ jsx71(OutlinePanel, { width: outlineWidth, readOnly }, "outline"),
35032
+ isCardMode ? /* @__PURE__ */ jsx71(
35033
+ BlockCardView,
35034
+ {
35035
+ blockCount,
35036
+ activeBlockKey,
35037
+ onPrev: prevBlock,
35038
+ onNext: nextBlock,
35039
+ onAdd: addBlock,
35040
+ children: /* @__PURE__ */ jsx71(
35041
+ WysiwygEditor,
35042
+ {
35043
+ submitOnEnter,
35044
+ placeholder,
35045
+ readOnly
35046
+ },
35047
+ "wysiwyg-editor"
35048
+ )
35049
+ },
35050
+ "wysiwyg-card"
35051
+ ) : /* @__PURE__ */ jsx71(
34863
35052
  WysiwygEditor,
34864
35053
  {
34865
35054
  submitOnEnter,
@@ -34867,60 +35056,51 @@ ${snippet}` : snippet);
34867
35056
  readOnly
34868
35057
  },
34869
35058
  "wysiwyg-editor"
35059
+ ),
35060
+ isCardMode && inlinePreviewVisible && /* @__PURE__ */ jsx71(BlockPreviewPanel, { basePath }, "block-preview"),
35061
+ !isCardMode && inlinePreviewVisible && /* @__PURE__ */ jsx71(
35062
+ InlinePreviewGutter,
35063
+ {
35064
+ width: inlinePreviewWidth,
35065
+ basePath,
35066
+ mediaProvider
35067
+ },
35068
+ "inline"
34870
35069
  )
34871
- },
34872
- "wysiwyg-card"
34873
- ) : /* @__PURE__ */ jsx71(
34874
- WysiwygEditor,
34875
- {
34876
- submitOnEnter,
34877
- placeholder,
34878
- readOnly
34879
- },
34880
- "wysiwyg-editor"
34881
- ),
34882
- isCardMode && inlinePreviewVisible && /* @__PURE__ */ jsx71(BlockPreviewPanel, { basePath }, "block-preview"),
34883
- !isCardMode && inlinePreviewVisible && /* @__PURE__ */ jsx71(
34884
- InlinePreviewGutter,
34885
- {
34886
- width: inlinePreviewWidth,
34887
- basePath,
34888
- mediaProvider
34889
- },
34890
- "inline"
34891
- )
34892
- ] }, "wysiwyg-shell"),
34893
- isMarkdownMode && isPreview && /* @__PURE__ */ jsx71(PreviewPanel, { basePath, workspaceContainer })
34894
- ]
34895
- }
34896
- ),
34897
- isMarkdownMode && showFiles && /* @__PURE__ */ jsx71(
34898
- MediaBin,
34899
- {
34900
- mediaProvider,
34901
- isDark,
34902
- refreshKey: mediaListRefreshKey,
34903
- usedMediaPaths,
34904
- onMediaUploaded: handleMediaUploaded,
34905
- onMediaRemoved: handleMediaRemoved,
34906
- onCountChange: setMediaCount
34907
- }
34908
- ),
34909
- isMarkdownMode && /* @__PURE__ */ jsx71(ThemeDesignerDock, {}),
34910
- isMarkdownMode && isDragging && !(activeView === "wysiwyg" && dragContentType === "media") && /* @__PURE__ */ jsx71(
34911
- DropZoneOverlay,
34912
- {
34913
- dragContentType,
34914
- zoneProps,
34915
- hasMediaProvider: mediaProvider !== null
34916
- }
34917
- )
34918
- ]
34919
- }
34920
- ),
34921
- isTimelineMode && /* @__PURE__ */ jsx71(TimelineTrack, {}),
34922
- statusBarVisible && !isImageMode && /* @__PURE__ */ jsx71(StatusBar, { slotRight: statusBarSlotRight })
34923
- ] }) }) }),
35070
+ ] }, "wysiwyg-shell"),
35071
+ isMarkdownMode && isPreview && /* @__PURE__ */ jsx71(PreviewPanel, { basePath, workspaceContainer })
35072
+ ]
35073
+ }
35074
+ ),
35075
+ isMarkdownMode && showFiles && /* @__PURE__ */ jsx71(
35076
+ MediaBin,
35077
+ {
35078
+ mediaProvider,
35079
+ isDark,
35080
+ refreshKey: mediaListRefreshKey,
35081
+ usedMediaPaths,
35082
+ onMediaUploaded: handleMediaUploaded,
35083
+ onMediaRemoved: handleMediaRemoved,
35084
+ onCountChange: setMediaCount
35085
+ }
35086
+ ),
35087
+ isMarkdownMode && /* @__PURE__ */ jsx71(ThemeDesignerDock, {}),
35088
+ isMarkdownMode && isDragging && !(activeView === "wysiwyg" && dragContentType === "media") && /* @__PURE__ */ jsx71(
35089
+ DropZoneOverlay,
35090
+ {
35091
+ dragContentType,
35092
+ zoneProps,
35093
+ hasMediaProvider: mediaProvider !== null
35094
+ }
35095
+ )
35096
+ ]
35097
+ }
35098
+ ),
35099
+ isTimelineMode && /* @__PURE__ */ jsx71(TimelineTrack, {}),
35100
+ statusBarVisible && !isImageMode && /* @__PURE__ */ jsx71(StatusBar, { slotRight: statusBarSlotRight })
35101
+ ]
35102
+ }
35103
+ ) }) }),
34924
35104
  /* @__PURE__ */ jsx71(TooltipLayer, {}),
34925
35105
  imageEditTarget !== null && mediaProvider && /* @__PURE__ */ jsx71(
34926
35106
  ImageEditModal,
@@ -35125,6 +35305,7 @@ export {
35125
35305
  DEFAULT_MERMAID_DIAGRAM_TYPE,
35126
35306
  mermaidDiagramMarkdown,
35127
35307
  MermaidDiagramTypeThumbnail,
35308
+ platformShortcut,
35128
35309
  Toolbar,
35129
35310
  StatusBar,
35130
35311
  RawEditor,