@bendyline/docblocks-react 2.6.2 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +3 -10
  2. package/THIRD_PARTY_NOTICES.txt +4284 -3651
  3. package/dist/AiDiagrams-XWOM3YZX.js +13 -0
  4. package/dist/AiEditorAssistant-M4LI2CRY.js +771 -0
  5. package/dist/AudioExportDialog-F23QXNPB.js +253 -0
  6. package/dist/DocumentConflictDialog-5LRVIGXS.js +111 -0
  7. package/dist/ExportDialog-L6FG6W3H.js +9 -0
  8. package/dist/{ExportToolbarControls-QYLBRWM7.js → ExportToolbarControls-BRCPZ7H3.js} +108 -42
  9. package/dist/{GitUI-L5T3XHZG.js → GitUI-LZW3IUXO.js} +5 -5
  10. package/dist/PresentationMaker-622NHMIN.js +9 -0
  11. package/dist/QuickOpenDialog-2KK4SEBU.js +356 -0
  12. package/dist/Settings-DuXk6baH.d.ts +144 -0
  13. package/dist/{ShareDialog-4OX3UPJ4.js → ShareDialog-N7GPM4SB.js} +10 -59
  14. package/dist/SpeechToolbarControl-UZQKGQWD.js +434 -0
  15. package/dist/TextFromNarration-XYRAB6O2.js +598 -0
  16. package/dist/WorkspaceSearchPanel-OUQQ5DOM.js +328 -0
  17. package/dist/chunk-2IYBBFQX.js +201 -0
  18. package/dist/{chunk-LG6HAWCK.js → chunk-56KO2LYN.js} +35 -4
  19. package/dist/chunk-5OZBGIIT.js +1826 -0
  20. package/dist/chunk-6B7G33D5.js +88 -0
  21. package/dist/chunk-ARATARVJ.js +40 -0
  22. package/dist/{chunk-ODYNOPC3.js → chunk-BCQZTWE5.js} +57 -17
  23. package/dist/chunk-EA4BO6E4.js +73 -0
  24. package/dist/chunk-G4MFB3IO.js +208 -0
  25. package/dist/chunk-I44HKDQX.js +314 -0
  26. package/dist/chunk-K4I3NJFL.js +49 -0
  27. package/dist/chunk-KBDZDFPU.js +61 -0
  28. package/dist/{chunk-FNSRCCBC.js → chunk-KWG6UP5O.js} +0 -10
  29. package/dist/chunk-MMOTEQK6.js +518 -0
  30. package/dist/chunk-OINKNX26.js +574 -0
  31. package/dist/chunk-Q5ZVP5LV.js +1711 -0
  32. package/dist/chunk-RP3DVNTI.js +62 -0
  33. package/dist/{chunk-FMDLZ4ZG.js → chunk-SGQKTKDX.js} +2 -2
  34. package/dist/chunk-TBLZBNQF.js +25 -0
  35. package/dist/{chunk-M5Y5WO7Z.js → chunk-UG4G7PJ4.js} +1 -1
  36. package/dist/chunk-UONFWKJ7.js +22 -0
  37. package/dist/chunk-VO2JYYQZ.js +40 -0
  38. package/dist/{chunk-EBWYGTN7.js → chunk-X437UAF7.js} +6 -0
  39. package/dist/chunk-YS4HVWIO.js +63 -0
  40. package/dist/editor.d.ts +36 -1
  41. package/dist/editor.js +16 -1
  42. package/dist/export/index.d.ts +27 -11
  43. package/dist/export/index.js +11 -7
  44. package/dist/index.d.ts +100 -13
  45. package/dist/index.js +3916 -1633
  46. package/dist/{run-export-I2XOVGG4.js → run-export-47X5FTYZ.js} +3 -3
  47. package/dist/settings/index.d.ts +32 -101
  48. package/dist/settings/index.js +14 -2
  49. package/package.json +9 -9
  50. package/src/styles/docblocks.css +2332 -656
  51. package/dist/ExportDialog-Q4VWI43H.js +0 -9
  52. package/dist/chunk-JDJVRDOP.js +0 -92
  53. package/dist/chunk-Q6XNIKDG.js +0 -669
  54. package/dist/{GitToolbarControl-O5AXBC6T.js → GitToolbarControl-J4UYZDV6.js} +3 -3
@@ -0,0 +1,88 @@
1
+ import {
2
+ Dialog
3
+ } from "./chunk-56KO2LYN.js";
4
+
5
+ // src/components/useConfirmDialog.ts
6
+ import React2, { useCallback as useCallback2, useEffect, useRef as useRef2, useState } from "react";
7
+
8
+ // src/components/ConfirmDialog.tsx
9
+ import { useCallback, useRef } from "react";
10
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
11
+ function ConfirmDialog({ request, onSettle }) {
12
+ const acknowledge = request.kind === "acknowledge";
13
+ const destructive = request.kind === "confirm" && request.destructive === true;
14
+ const cancelRef = useRef(null);
15
+ const confirmRef = useRef(null);
16
+ const accept = useCallback(() => onSettle(true), [onSettle]);
17
+ const reject = useCallback(() => onSettle(false), [onSettle]);
18
+ const close = acknowledge ? accept : reject;
19
+ return /* @__PURE__ */ jsx(
20
+ Dialog,
21
+ {
22
+ title: request.title,
23
+ onClose: close,
24
+ initialFocusRef: destructive ? cancelRef : confirmRef,
25
+ footer: /* @__PURE__ */ jsxs(Fragment, { children: [
26
+ !acknowledge && /* @__PURE__ */ jsx("button", { ref: cancelRef, type: "button", className: "db-git-secondary-btn", onClick: reject, children: request.cancelLabel ?? "Cancel" }),
27
+ /* @__PURE__ */ jsx(
28
+ "button",
29
+ {
30
+ ref: confirmRef,
31
+ type: "button",
32
+ className: destructive ? "db-git-primary-btn db-git-primary-btn--danger" : "db-git-primary-btn",
33
+ onClick: accept,
34
+ children: request.confirmLabel ?? "OK"
35
+ }
36
+ )
37
+ ] }),
38
+ children: /* @__PURE__ */ jsx("p", { className: "db-dialog-message", children: request.message })
39
+ }
40
+ );
41
+ }
42
+
43
+ // src/components/useConfirmDialog.ts
44
+ function useConfirmDialog() {
45
+ const [pending, setPending] = useState(null);
46
+ const pendingRef = useRef2(null);
47
+ const nextIdRef = useRef2(0);
48
+ const settlePending = useCallback2((value) => {
49
+ const current = pendingRef.current;
50
+ if (!current) return;
51
+ pendingRef.current = null;
52
+ setPending((open2) => open2?.id === current.id ? null : open2);
53
+ current.settle(value);
54
+ }, []);
55
+ const open = useCallback2(
56
+ (request) => new Promise((resolve) => {
57
+ settlePending(false);
58
+ const next = { id: ++nextIdRef.current, request, settle: resolve };
59
+ pendingRef.current = next;
60
+ setPending(next);
61
+ }),
62
+ [settlePending]
63
+ );
64
+ const confirm = useCallback2(
65
+ (request) => open({ kind: "confirm", ...request }),
66
+ [open]
67
+ );
68
+ const acknowledge = useCallback2(
69
+ async (request) => {
70
+ await open({ kind: "acknowledge", ...request });
71
+ },
72
+ [open]
73
+ );
74
+ useEffect(() => {
75
+ return () => settlePending(false);
76
+ }, [settlePending]);
77
+ const confirmDialog = pending ? React2.createElement(ConfirmDialog, {
78
+ // Remount on a superseding request so focus re-seeds from it.
79
+ key: pending.id,
80
+ request: pending.request,
81
+ onSettle: settlePending
82
+ }) : null;
83
+ return { confirm, acknowledge, confirmDialog };
84
+ }
85
+
86
+ export {
87
+ useConfirmDialog
88
+ };
@@ -0,0 +1,40 @@
1
+ // src/FileExplorer/file-names.ts
2
+ var HIDDEN_EXTENSION = ".md";
3
+ var EXTENSION = /\.(?=[A-Za-z0-9]*[A-Za-z])[A-Za-z0-9]{1,10}$/;
4
+ function hidesExtension(name) {
5
+ return name.endsWith(HIDDEN_EXTENSION) && name.length > HIDDEN_EXTENSION.length;
6
+ }
7
+ function extensionStart(name) {
8
+ const match = EXTENSION.exec(name);
9
+ return match && match.index > 0 ? match.index : -1;
10
+ }
11
+ function displayFileName(name) {
12
+ return hidesExtension(name) ? name.slice(0, -HIDDEN_EXTENSION.length) : name;
13
+ }
14
+ function renameDraft(name, kind) {
15
+ if (kind === "file" && hidesExtension(name)) {
16
+ const value = displayFileName(name);
17
+ return { value, selectionEnd: value.length };
18
+ }
19
+ const start = kind === "file" ? extensionStart(name) : -1;
20
+ return { value: name, selectionEnd: start >= 0 ? start : name.length };
21
+ }
22
+ function renamedFileName(name, typed, kind) {
23
+ let next = typed.trim();
24
+ if (!next) return null;
25
+ if (kind === "file") {
26
+ if (hidesExtension(name)) {
27
+ if (!next.toLowerCase().endsWith(HIDDEN_EXTENSION)) next += HIDDEN_EXTENSION;
28
+ } else {
29
+ const start = extensionStart(name);
30
+ if (start >= 0 && extensionStart(next) < 0) next += name.slice(start);
31
+ }
32
+ }
33
+ return next === name ? null : next;
34
+ }
35
+
36
+ export {
37
+ displayFileName,
38
+ renameDraft,
39
+ renamedFileName
40
+ };
@@ -1,9 +1,22 @@
1
1
  import {
2
2
  FORMAT_EXTENSIONS
3
- } from "./chunk-EBWYGTN7.js";
3
+ } from "./chunk-X437UAF7.js";
4
4
 
5
5
  // src/Export/run-export.ts
6
6
  import { parseMarkdown } from "@bendyline/squisq/markdown";
7
+ async function pictureDiagrams(doc, overrides, kinds) {
8
+ try {
9
+ const [{ rasterizeDiagrams }, render] = await Promise.all([
10
+ import("@bendyline/squisq-formats/diagrams"),
11
+ overrides.diagramRenderer ?? import("@bendyline/squisq-react/diagram-pictures").then(
12
+ (module) => module.createDiagramPictureRenderer()
13
+ )
14
+ ]);
15
+ return await rasterizeDiagrams(doc, render, { kinds, urlPrefix: "docblocks-diagram" });
16
+ } catch {
17
+ return { markdownDoc: doc, images: /* @__PURE__ */ new Map(), mermaid: /* @__PURE__ */ new Map() };
18
+ }
19
+ }
7
20
  var MIME_TYPES = {
8
21
  docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
9
22
  pdf: "application/pdf",
@@ -45,24 +58,40 @@ async function runExport(markdown, selectedFile, options, mediaContainer, saveBl
45
58
  const doc = parseMarkdown(markdown);
46
59
  if (options.format === "docx") {
47
60
  const { markdownDocToDocx } = await import("@bendyline/squisq-formats/docx");
48
- const images = mediaContainer ? await resolveImages(doc, mediaContainer) : void 0;
49
- const buf = await markdownDocToDocx(doc, { themeId, images });
61
+ const pictured = await pictureDiagrams(doc, converterOverrides);
62
+ const images = new Map([
63
+ ...mediaContainer ? await resolveImages(doc, mediaContainer) : [],
64
+ ...pictured.images
65
+ ]);
66
+ const buf = await markdownDocToDocx(pictured.markdownDoc, { themeId, images });
50
67
  await saveExportBlob(new Blob([buf], { type: MIME_TYPES.docx }), filename, saveBlob);
51
68
  return;
52
69
  }
53
70
  if (options.format === "pdf") {
54
71
  const { markdownDocToPdf } = await import("@bendyline/squisq-formats/pdf");
55
- const buf = await markdownDocToPdf(doc, {
72
+ const pictured = await pictureDiagrams(doc, converterOverrides);
73
+ const images = new Map([
74
+ ...mediaContainer ? await resolveImages(doc, mediaContainer) : [],
75
+ ...pictured.images
76
+ ]);
77
+ const buf = await markdownDocToPdf(pictured.markdownDoc, {
56
78
  themeId,
57
- pageSize: options.pageSize
79
+ pageSize: options.pageSize,
80
+ images
58
81
  });
59
82
  await saveExportBlob(new Blob([buf], { type: MIME_TYPES.pdf }), filename, saveBlob);
60
83
  return;
61
84
  }
62
85
  if (options.format === "epub") {
63
86
  const { markdownDocToEpub } = await import("@bendyline/squisq-formats/epub");
64
- const images = mediaContainer ? await resolveImageData(doc, mediaContainer) : void 0;
65
- const buf = await markdownDocToEpub(doc, { themeId, images });
87
+ const pictured = await pictureDiagrams(doc, converterOverrides);
88
+ const images = new Map([
89
+ ...mediaContainer ? await resolveImageData(doc, mediaContainer) : [],
90
+ ...[...pictured.images].map(
91
+ ([url, picture]) => [url, picture.data.slice().buffer]
92
+ )
93
+ ]);
94
+ const buf = await markdownDocToEpub(pictured.markdownDoc, { themeId, images });
66
95
  await saveExportBlob(new Blob([buf], { type: MIME_TYPES.epub }), filename, saveBlob);
67
96
  return;
68
97
  }
@@ -81,11 +110,21 @@ async function runExport(markdown, selectedFile, options, mediaContainer, saveBl
81
110
  enrichedDoc.themeId = themeId;
82
111
  }
83
112
  const images = mediaContainer ? await resolveImageData(doc, mediaContainer) : void 0;
84
- const buf = await docToPptx(enrichedDoc, { themeId, images });
113
+ const { mermaid } = await pictureDiagrams(doc, converterOverrides, ["mermaid"]);
114
+ const buf = await docToPptx(enrichedDoc, { themeId, images, diagramImages: mermaid });
85
115
  await saveExportBlob(new Blob([buf], { type: MIME_TYPES.pptx }), filename, saveBlob);
86
116
  return;
87
117
  }
88
118
  }
119
+ var MERMAID_FENCE = /^ {0,3}(?:```|~~~)[ \t]*mermaid\b/imu;
120
+ async function playerScriptFor(markdown) {
121
+ if (MERMAID_FENCE.test(markdown)) {
122
+ const { PLAYER_BUNDLE_FULL } = await import("@bendyline/squisq-react/standalone-source/full");
123
+ return PLAYER_BUNDLE_FULL;
124
+ }
125
+ const { PLAYER_BUNDLE } = await import("@bendyline/squisq-react/standalone-source");
126
+ return PLAYER_BUNDLE;
127
+ }
89
128
  async function runHtmlExport(markdown, htmlFilename, options, mediaContainer, selectedFile, saveBlob) {
90
129
  const baseName = htmlFilename.replace(/\.html$/, "");
91
130
  const zipName = `${baseName}.zip`;
@@ -93,16 +132,17 @@ async function runHtmlExport(markdown, htmlFilename, options, mediaContainer, se
93
132
  const themeId = options.themeId !== "standard" ? options.themeId : void 0;
94
133
  if (options.includeLinkedDocs && mediaContainer && selectedFile) {
95
134
  const entryPath = basenameForBundle(selectedFile);
135
+ const readDocument = (path) => path === entryPath ? Promise.resolve(markdown) : readDocumentFromContainer(mediaContainer, path);
96
136
  if (options.htmlStyle === "rendered") {
97
- const [{ markdownDocsToHtmlBundle }, { PLAYER_BUNDLE }] = await Promise.all([
137
+ const [{ markdownDocsToHtmlBundle }, playerScript] = await Promise.all([
98
138
  import("@bendyline/squisq-formats/html"),
99
- import("@bendyline/squisq-react/standalone-source")
139
+ playerScriptFor(markdown)
100
140
  ]);
101
141
  const blob2 = await markdownDocsToHtmlBundle({
102
142
  entryPath,
103
- readDocument: (path) => readDocumentFromContainer(mediaContainer, path),
143
+ readDocument,
104
144
  readBinary: (path) => mediaContainer.readFile(path),
105
- playerScript: PLAYER_BUNDLE,
145
+ playerScript,
106
146
  title: baseName,
107
147
  themeId,
108
148
  mode: "static",
@@ -115,7 +155,7 @@ async function runHtmlExport(markdown, htmlFilename, options, mediaContainer, se
115
155
  const { markdownDocsToPlainHtmlBundle } = await import("@bendyline/squisq-formats/html");
116
156
  const blob = await markdownDocsToPlainHtmlBundle({
117
157
  entryPath,
118
- readDocument: (path) => readDocumentFromContainer(mediaContainer, path),
158
+ readDocument,
119
159
  readBinary: (path) => mediaContainer.readFile(path),
120
160
  title: baseName,
121
161
  themeId,
@@ -125,10 +165,10 @@ async function runHtmlExport(markdown, htmlFilename, options, mediaContainer, se
125
165
  return;
126
166
  }
127
167
  if (options.htmlStyle === "rendered") {
128
- const [{ markdownToDoc }, { docToHtml, docToHtmlZip, collectImagePaths }, { PLAYER_BUNDLE }] = await Promise.all([
168
+ const [{ markdownToDoc }, { docToHtml, docToHtmlZip, collectImagePaths }, playerScript] = await Promise.all([
129
169
  import("@bendyline/squisq/doc"),
130
170
  import("@bendyline/squisq-formats/html"),
131
- import("@bendyline/squisq-react/standalone-source")
171
+ playerScriptFor(markdown)
132
172
  ]);
133
173
  const mdDoc2 = parseMarkdown(markdown);
134
174
  const baseDoc = markdownToDoc(mdDoc2);
@@ -136,7 +176,7 @@ async function runHtmlExport(markdown, htmlFilename, options, mediaContainer, se
136
176
  const images = mediaContainer ? await resolveDocImages(baseDoc, mediaContainer, collectImagePaths) : void 0;
137
177
  if (options.htmlBundle === "zip") {
138
178
  const blob = await docToHtmlZip(baseDoc, {
139
- playerScript: PLAYER_BUNDLE,
179
+ playerScript,
140
180
  images,
141
181
  mode: "static",
142
182
  title: baseName,
@@ -146,7 +186,7 @@ async function runHtmlExport(markdown, htmlFilename, options, mediaContainer, se
146
186
  return;
147
187
  }
148
188
  const html2 = docToHtml(baseDoc, {
149
- playerScript: PLAYER_BUNDLE,
189
+ playerScript,
150
190
  images,
151
191
  mode: "static",
152
192
  title: baseName,
@@ -0,0 +1,73 @@
1
+ // src/icons.tsx
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ function FontAwesomeIcon({ icon }) {
4
+ return /* @__PURE__ */ jsx("i", { className: icon, "aria-hidden": "true" });
5
+ }
6
+ function NewFileIcon() {
7
+ return /* @__PURE__ */ jsx(FontAwesomeIcon, { icon: "fa-solid fa-file-circle-plus" });
8
+ }
9
+ function NewFolderIcon() {
10
+ return /* @__PURE__ */ jsx(FontAwesomeIcon, { icon: "fa-solid fa-folder-plus" });
11
+ }
12
+ function OpenFolderIcon() {
13
+ return /* @__PURE__ */ jsx(FontAwesomeIcon, { icon: "fa-solid fa-folder-open" });
14
+ }
15
+ function SortByNameIcon() {
16
+ return /* @__PURE__ */ jsx(FontAwesomeIcon, { icon: "fa-solid fa-arrow-down-a-z" });
17
+ }
18
+ function SortByLastModifiedIcon() {
19
+ return /* @__PURE__ */ jsx(FontAwesomeIcon, { icon: "fa-solid fa-clock" });
20
+ }
21
+ function FolderIcon() {
22
+ return /* @__PURE__ */ jsx(FontAwesomeIcon, { icon: "fa-solid fa-folder" });
23
+ }
24
+ function MoreIcon() {
25
+ return /* @__PURE__ */ jsx(FontAwesomeIcon, { icon: "fa-solid fa-ellipsis" });
26
+ }
27
+ function PinIcon() {
28
+ return /* @__PURE__ */ jsx(FontAwesomeIcon, { icon: "fa-solid fa-thumbtack" });
29
+ }
30
+ function WorkspaceIcon() {
31
+ return /* @__PURE__ */ jsx(FontAwesomeIcon, { icon: "fa-solid fa-gear" });
32
+ }
33
+ function SplitViewIcon() {
34
+ return /* @__PURE__ */ jsxs(
35
+ "svg",
36
+ {
37
+ width: "16",
38
+ height: "16",
39
+ viewBox: "0 0 16 16",
40
+ fill: "none",
41
+ stroke: "currentColor",
42
+ strokeWidth: "1.5",
43
+ strokeLinecap: "round",
44
+ strokeLinejoin: "round",
45
+ "aria-hidden": "true",
46
+ children: [
47
+ /* @__PURE__ */ jsx("rect", { x: "1.5", y: "2.5", width: "13", height: "11", rx: "1" }),
48
+ /* @__PURE__ */ jsx("line", { x1: "6", y1: "2.5", x2: "6", y2: "13.5" })
49
+ ]
50
+ }
51
+ );
52
+ }
53
+ function SearchIcon() {
54
+ return /* @__PURE__ */ jsx(FontAwesomeIcon, { icon: "fa-solid fa-magnifying-glass" });
55
+ }
56
+ function FilesIcon() {
57
+ return /* @__PURE__ */ jsx(FontAwesomeIcon, { icon: "fa-solid fa-folder-tree" });
58
+ }
59
+
60
+ export {
61
+ NewFileIcon,
62
+ NewFolderIcon,
63
+ OpenFolderIcon,
64
+ SortByNameIcon,
65
+ SortByLastModifiedIcon,
66
+ FolderIcon,
67
+ MoreIcon,
68
+ PinIcon,
69
+ WorkspaceIcon,
70
+ SplitViewIcon,
71
+ SearchIcon,
72
+ FilesIcon
73
+ };
@@ -0,0 +1,208 @@
1
+ // src/editor.ts
2
+ import { useEffect, useState } from "react";
3
+
4
+ // src/components/FindButton.tsx
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+ function FindButton({ active, onActiveChange, shortcutLabel: shortcutLabel2 }) {
7
+ const tooltip = shortcutLabel2 ? `Find in document (${shortcutLabel2})` : "Find in document";
8
+ return /* @__PURE__ */ jsx(
9
+ "button",
10
+ {
11
+ type: "button",
12
+ className: `squisq-toolbar-button${active ? " squisq-toolbar-button--active" : ""}`,
13
+ onClick: () => onActiveChange(!active),
14
+ "aria-label": "Find in document",
15
+ "aria-pressed": active,
16
+ "data-tooltip": tooltip,
17
+ children: /* @__PURE__ */ jsx(FindGlyph, {})
18
+ }
19
+ );
20
+ }
21
+ function FindGlyph() {
22
+ return /* @__PURE__ */ jsxs(
23
+ "svg",
24
+ {
25
+ width: "16",
26
+ height: "16",
27
+ viewBox: "0 0 16 16",
28
+ fill: "none",
29
+ stroke: "currentColor",
30
+ strokeWidth: "1.5",
31
+ strokeLinecap: "round",
32
+ "aria-hidden": "true",
33
+ children: [
34
+ /* @__PURE__ */ jsx("circle", { cx: "6.75", cy: "6.75", r: "4.25" }),
35
+ /* @__PURE__ */ jsx("path", { d: "m10 10 3.5 3.5" })
36
+ ]
37
+ }
38
+ );
39
+ }
40
+
41
+ // src/components/find-input.ts
42
+ function focusFindInput(scope = document) {
43
+ const input = scope.querySelector(".squisq-find-input");
44
+ input?.focus();
45
+ input?.select();
46
+ }
47
+
48
+ // src/DocBlocksShell/keyboard-shortcuts.ts
49
+ var SHELL_SHORTCUTS = Object.freeze([
50
+ // Must work mid-sentence; that is the entire point of a save shortcut.
51
+ { id: "save", key: "s", accel: true, label: "Ctrl+S", whileTyping: true },
52
+ // Find is mostly invoked from inside the document being searched.
53
+ { id: "find", key: "f", accel: true, label: "Ctrl+F", whileTyping: true },
54
+ // Jumping elsewhere is as likely mid-sentence as from the tree.
55
+ { id: "quick-open", key: "p", accel: true, label: "Ctrl+P", whileTyping: true },
56
+ {
57
+ id: "workspace-search",
58
+ key: "f",
59
+ accel: true,
60
+ shift: true,
61
+ label: "Ctrl+Shift+F",
62
+ whileTyping: true
63
+ },
64
+ // Deliberately NOT Cmd+B — Squisq owns that for Bold. Cmd+\ is the VS Code
65
+ // convention for toggling the side bar.
66
+ { id: "toggle-sidebar", key: "\\", accel: true, label: "Ctrl+\\", whileTyping: true },
67
+ { id: "focus-files", key: "e", accel: true, shift: true, label: "Ctrl+Shift+E" },
68
+ { id: "new-document", key: "n", accel: true, label: "Ctrl+N" },
69
+ { id: "new-folder", key: "n", accel: true, shift: true, label: "Ctrl+Shift+N" },
70
+ // Ctrl+, (settings) is deliberately absent: AppMenu owns that dialog's state
71
+ // internally and exposes no way to open it. Binding the key without first
72
+ // giving AppMenu a controlled `settingsOpen` prop would ship a dead shortcut.
73
+ { id: "close-overlay", key: "Escape", accel: false, label: "Esc", whileTyping: true }
74
+ ]);
75
+ function isTypingTarget(target) {
76
+ if (!(target instanceof HTMLElement)) return false;
77
+ if (target.isContentEditable) return true;
78
+ const tag = target.tagName;
79
+ if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
80
+ return target.closest(".monaco-editor") !== null;
81
+ }
82
+ function matchShortcut(event, _layoutMode) {
83
+ return SHELL_SHORTCUTS.find((binding) => bindingMatches(binding, event))?.id ?? null;
84
+ }
85
+ function bindingMatches(binding, event) {
86
+ if (event.altKey) return false;
87
+ const accel = event.ctrlKey || event.metaKey;
88
+ const key = event.key.length === 1 ? event.key.toLowerCase() : event.key;
89
+ if (binding.accel !== accel) return false;
90
+ if ((binding.shift ?? false) !== event.shiftKey) return false;
91
+ if (binding.key !== key) return false;
92
+ return binding.whileTyping === true || !isTypingTarget(event.target);
93
+ }
94
+ function isFindShortcut(event) {
95
+ const binding = SHELL_SHORTCUTS.find((candidate) => candidate.id === "find");
96
+ return binding !== void 0 && bindingMatches(binding, event);
97
+ }
98
+ function findShortcutLabel() {
99
+ return shortcutLabel("find", isApplePlatform());
100
+ }
101
+ function isApplePlatform() {
102
+ if (typeof navigator === "undefined") return false;
103
+ return /Mac|iPhone|iPad|iPod/u.test(navigator.platform || navigator.userAgent);
104
+ }
105
+ function shortcutLabel(id, isApple) {
106
+ const binding = SHELL_SHORTCUTS.find((candidate) => candidate.id === id);
107
+ if (!binding) return "";
108
+ return isApple ? binding.label.replace("Ctrl+", "\u2318") : binding.label;
109
+ }
110
+
111
+ // src/editor.ts
112
+ var PORTRAIT_FORM_FACTOR_QUERY = "(orientation: portrait)";
113
+ function previewViewportPresetForOrientation(isPortrait) {
114
+ return isPortrait ? "portrait" : "landscape";
115
+ }
116
+ function useResponsivePreviewViewportPreset() {
117
+ const [isPortrait, setIsPortrait] = useState(() => {
118
+ if (typeof globalThis.matchMedia !== "function") return false;
119
+ return globalThis.matchMedia(PORTRAIT_FORM_FACTOR_QUERY).matches;
120
+ });
121
+ useEffect(() => {
122
+ if (typeof globalThis.matchMedia !== "function") return;
123
+ const query = globalThis.matchMedia(PORTRAIT_FORM_FACTOR_QUERY);
124
+ const update = () => setIsPortrait(query.matches);
125
+ update();
126
+ query.addEventListener("change", update);
127
+ return () => query.removeEventListener("change", update);
128
+ }, []);
129
+ return previewViewportPresetForOrientation(isPortrait);
130
+ }
131
+ var EMPTY_DOCUMENT_PROMPTS = [
132
+ "Start typing your content, or drop images on top of me...",
133
+ "Write anything -- paste markdown, drag in images, or just start typing...",
134
+ "Type away. Markdown syntax works too...",
135
+ "Chapter 1 begins here...",
136
+ "Once upon a time...",
137
+ "A blank page. Exciting, isn't it?",
138
+ "The first word is always the hardest...",
139
+ "Plot twist: this is where it all starts...",
140
+ "Write something the future you will thank you for...",
141
+ "Begin at the beginning...",
142
+ "Let's begin.",
143
+ "We can be heroes...",
144
+ "Here comes the sun...",
145
+ "And so it begins...",
146
+ "Let it be, let it be...",
147
+ "Don't stop believing...",
148
+ "Started from the bottom...",
149
+ "This is the start of something new...",
150
+ "Ground control to Major Tom...",
151
+ "Come as you are...",
152
+ "Every new beginning...",
153
+ "Hello, is it me you're looking for?",
154
+ "Imagine all the people...",
155
+ "Anything you want, you got it...",
156
+ "Rise above...",
157
+ "Do you have the time...",
158
+ "Here we are now, entertain us...",
159
+ "All the small things...",
160
+ "Should I stay or should I go?",
161
+ "I am a patient boy -- I wait, I wait, I wait...",
162
+ "Destination unknown...",
163
+ "Easy like Sunday morning...",
164
+ "Dance, dance, dance to the radio...",
165
+ "New day rising...",
166
+ "Visit the world...",
167
+ "Positive mental attitude...",
168
+ "Better I sing than I cry...",
169
+ "Disconnect for a minute...",
170
+ "We need new noise...",
171
+ "Shoot the moon...",
172
+ "I'll surprise you sometime -- I'll come around...",
173
+ "Coming out of my cage, and doing just fine...",
174
+ "Throw me to the wolves -- I'll come back, leading the pack...",
175
+ "There comes a time -- no excuses...",
176
+ "It's up to me now, turn on the bright lights...",
177
+ "Do what you want...",
178
+ "Fingers crossed...",
179
+ "This is the start of how it all ends...",
180
+ "It has to start somewhere, it has to start sometime -- what better place than here? what better time than now?",
181
+ "I know myself, and I'll begin again...",
182
+ "Dig me out, dig me in...",
183
+ "Everybody's full of sugar, honey, ice and tea...",
184
+ "Take time with a wounded hand, 'cause it likes to heal...",
185
+ "Grab a brush and put on a little makeup...",
186
+ "Amber is the color of your energy...",
187
+ "Wait -- they don't love it like I love it...",
188
+ "A world's been buried, where love is the law; a you-topia..."
189
+ ];
190
+ function pickEmptyDocumentPrompt(random = Math.random) {
191
+ const index = Math.floor(random() * EMPTY_DOCUMENT_PROMPTS.length);
192
+ return EMPTY_DOCUMENT_PROMPTS[index] ?? EMPTY_DOCUMENT_PROMPTS[0];
193
+ }
194
+
195
+ export {
196
+ FindButton,
197
+ focusFindInput,
198
+ matchShortcut,
199
+ isFindShortcut,
200
+ findShortcutLabel,
201
+ isApplePlatform,
202
+ shortcutLabel,
203
+ PORTRAIT_FORM_FACTOR_QUERY,
204
+ previewViewportPresetForOrientation,
205
+ useResponsivePreviewViewportPreset,
206
+ EMPTY_DOCUMENT_PROMPTS,
207
+ pickEmptyDocumentPrompt
208
+ };