@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,356 @@
1
+ import {
2
+ HighlightedText
3
+ } from "./chunk-TBLZBNQF.js";
4
+ import {
5
+ displayFileName
6
+ } from "./chunk-ARATARVJ.js";
7
+ import {
8
+ Dialog
9
+ } from "./chunk-56KO2LYN.js";
10
+
11
+ // src/QuickOpen/QuickOpenDialog.tsx
12
+ import { useEffect, useId, useMemo, useRef, useState } from "react";
13
+ import { tryParseWorkspacePath } from "@bendyline/docblocks/filesystem";
14
+
15
+ // src/QuickOpen/quick-open-ranking.ts
16
+ import { fuzzyMatch, fuzzyTokens } from "@bendyline/docblocks/workspace-index";
17
+ var DEFAULT_LIMIT = 50;
18
+ var EMPTY_QUERY_LIMIT = 20;
19
+ var RECENCY_HORIZON = 50;
20
+ var RECENCY_WEIGHT = 0.25;
21
+ var PINNED_WEIGHT = 0.05;
22
+ var DOCUMENT_WEIGHT = 0.03;
23
+ function splitPath(path) {
24
+ const slash = path.lastIndexOf("/");
25
+ return slash < 0 ? { folder: "", base: path } : { folder: path.slice(0, slash), base: path.slice(slash + 1) };
26
+ }
27
+ function stemOf(name) {
28
+ const dot = name.lastIndexOf(".");
29
+ return dot > 0 ? name.slice(0, dot) : name;
30
+ }
31
+ function compareCodeUnits(left, right) {
32
+ return left < right ? -1 : left > right ? 1 : 0;
33
+ }
34
+ function item(candidate, ranges) {
35
+ return {
36
+ key: `${candidate.workspaceId}\0${candidate.path}`,
37
+ workspaceId: candidate.workspaceId,
38
+ workspaceName: candidate.workspaceName,
39
+ path: candidate.path,
40
+ name: candidate.name,
41
+ folder: candidate.folder,
42
+ title: candidate.title,
43
+ nameRanges: ranges?.nameRanges ?? [],
44
+ folderRanges: ranges?.folderRanges ?? [],
45
+ titleRanges: ranges?.titleRanges ?? [],
46
+ recent: candidate.recentRank >= 0,
47
+ pinned: candidate.pinned
48
+ };
49
+ }
50
+ function buildCandidates(input) {
51
+ const titles = new Map(
52
+ input.documents.map((document2) => [document2.path, document2.title])
53
+ );
54
+ const recentRank = new Map(input.recent.map((path, rank) => [path, rank]));
55
+ const pinnedHere = new Set(
56
+ input.pins.filter((pin) => pin.workspaceId === input.workspaceId).map((pin) => pin.path)
57
+ );
58
+ const candidates = input.files.map((file) => {
59
+ const { folder } = splitPath(file.path);
60
+ const name = displayFileName(file.name);
61
+ const title = titles.get(file.path) ?? null;
62
+ return {
63
+ workspaceId: input.workspaceId,
64
+ workspaceName: null,
65
+ path: file.path,
66
+ name,
67
+ folder,
68
+ title: title && title !== stemOf(file.name) && title !== name ? title : null,
69
+ isDocument: file.kind !== "other",
70
+ recentRank: recentRank.get(file.path) ?? -1,
71
+ pinned: pinnedHere.has(file.path)
72
+ };
73
+ });
74
+ for (const pin of input.pins) {
75
+ if (pin.workspaceId === input.workspaceId) continue;
76
+ const { folder, base } = splitPath(pin.path);
77
+ candidates.push({
78
+ workspaceId: pin.workspaceId,
79
+ workspaceName: pin.workspaceName,
80
+ path: pin.path,
81
+ name: displayFileName(base),
82
+ folder,
83
+ title: null,
84
+ isDocument: true,
85
+ recentRank: -1,
86
+ pinned: true
87
+ });
88
+ }
89
+ return candidates;
90
+ }
91
+ function emptyQuery(input, candidates) {
92
+ const byPath = new Map(
93
+ candidates.filter((candidate) => candidate.workspaceId === input.workspaceId).map((candidate) => [candidate.path, candidate])
94
+ );
95
+ const shown = /* @__PURE__ */ new Set();
96
+ const items = [];
97
+ const add = (candidate) => {
98
+ if (!candidate) return;
99
+ const key = `${candidate.workspaceId}\0${candidate.path}`;
100
+ const active = candidate.workspaceId === input.workspaceId && candidate.path === input.activePath;
101
+ if (shown.has(key) || active) return;
102
+ shown.add(key);
103
+ items.push(item(candidate));
104
+ };
105
+ for (const path of input.recent) add(byPath.get(path));
106
+ for (const candidate of candidates) if (candidate.pinned) add(candidate);
107
+ const lastModified = new Map(input.files.map((file) => [file.path, file.lastModified]));
108
+ const modified = [...byPath.values()].sort(
109
+ (left, right) => compareCodeUnits(lastModified.get(right.path) ?? "", lastModified.get(left.path) ?? "") || compareCodeUnits(left.path, right.path)
110
+ );
111
+ for (const candidate of modified) {
112
+ if (items.length >= EMPTY_QUERY_LIMIT) break;
113
+ add(candidate);
114
+ }
115
+ return items;
116
+ }
117
+ var FIELD_WEIGHTS = { name: 1, title: 0.9, folder: 0.6, path: 1 };
118
+ function bestField(token, candidate) {
119
+ const fields = token.includes("/") ? [["path", candidate.folder ? `${candidate.folder}/${candidate.name}` : candidate.name]] : [
120
+ ["name", candidate.name],
121
+ ["title", candidate.title],
122
+ ["folder", candidate.folder]
123
+ ];
124
+ let best = null;
125
+ for (const [field, text] of fields) {
126
+ if (!text) continue;
127
+ const match = fuzzyMatch(token, text);
128
+ if (!match) continue;
129
+ const score = match.score * FIELD_WEIGHTS[field];
130
+ if (!best || score > best.score) best = { score, field, ranges: match.ranges };
131
+ }
132
+ return best;
133
+ }
134
+ function mergeRanges(ranges) {
135
+ const sorted = [...ranges].sort((left, right) => left[0] - right[0]);
136
+ const merged = [];
137
+ for (const [start, end] of sorted) {
138
+ const last = merged[merged.length - 1];
139
+ if (last && start <= last[1]) last[1] = Math.max(last[1], end);
140
+ else merged.push([start, end]);
141
+ }
142
+ return merged;
143
+ }
144
+ function rankQuickOpen(input) {
145
+ const candidates = buildCandidates(input);
146
+ const tokens = fuzzyTokens(input.query);
147
+ if (tokens.length === 0) return emptyQuery(input, candidates);
148
+ const perfect = tokens.reduce(
149
+ (total, token) => total + (fuzzyMatch(token, token)?.score ?? 0),
150
+ 0
151
+ );
152
+ const scored = [];
153
+ for (const candidate of candidates) {
154
+ let score = 0;
155
+ const ranges = {
156
+ name: [],
157
+ title: [],
158
+ folder: []
159
+ };
160
+ let matched = true;
161
+ for (const token of tokens) {
162
+ const best = bestField(token, candidate);
163
+ if (!best) {
164
+ matched = false;
165
+ break;
166
+ }
167
+ score += best.score;
168
+ if (best.field === "path") {
169
+ const offset = candidate.folder ? candidate.folder.length + 1 : 0;
170
+ for (const [start, end] of best.ranges) {
171
+ if (end <= candidate.folder.length) ranges.folder.push([start, end]);
172
+ else if (start >= offset) ranges.name.push([start - offset, end - offset]);
173
+ else {
174
+ ranges.folder.push([start, candidate.folder.length]);
175
+ ranges.name.push([0, end - offset]);
176
+ }
177
+ }
178
+ } else {
179
+ ranges[best.field].push(...best.ranges);
180
+ }
181
+ }
182
+ if (!matched) continue;
183
+ if (candidate.recentRank >= 0 && candidate.recentRank < RECENCY_HORIZON) {
184
+ score += perfect * RECENCY_WEIGHT * (1 - candidate.recentRank / RECENCY_HORIZON);
185
+ }
186
+ if (candidate.pinned) score += perfect * PINNED_WEIGHT;
187
+ if (candidate.isDocument) score += perfect * DOCUMENT_WEIGHT;
188
+ scored.push({
189
+ score,
190
+ item: item(candidate, {
191
+ nameRanges: mergeRanges(ranges.name),
192
+ titleRanges: mergeRanges(ranges.title),
193
+ folderRanges: mergeRanges(ranges.folder)
194
+ })
195
+ });
196
+ }
197
+ scored.sort(
198
+ (left, right) => right.score - left.score || left.item.path.length - right.item.path.length || compareCodeUnits(left.item.path, right.item.path)
199
+ );
200
+ return scored.slice(0, input.limit ?? DEFAULT_LIMIT).map((entry) => entry.item);
201
+ }
202
+
203
+ // src/QuickOpen/QuickOpenDialog.tsx
204
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
205
+ var PAGE = 10;
206
+ function statusMessage(status, query, count) {
207
+ if (status.error) return status.error;
208
+ if (!status.listed) return "Listing workspace\u2026";
209
+ if (query.trim() && count === 0) return "No matching documents";
210
+ if (status.truncated) return "Large workspace: some files are not listed.";
211
+ return null;
212
+ }
213
+ function QuickOpenDialog({
214
+ index,
215
+ status,
216
+ workspaceId,
217
+ activePath,
218
+ recent,
219
+ pins,
220
+ focusRequest,
221
+ onOpen,
222
+ onClose
223
+ }) {
224
+ const inputRef = useRef(null);
225
+ const listId = useId();
226
+ const optionPrefix = useId();
227
+ const [query, setQuery] = useState("");
228
+ const [active, setActive] = useState(0);
229
+ const canonicalActive = activePath === null ? null : tryParseWorkspacePath(activePath);
230
+ const items = useMemo(
231
+ () => rankQuickOpen({
232
+ query,
233
+ workspaceId,
234
+ files: index?.getFiles() ?? [],
235
+ documents: index?.getDocuments() ?? [],
236
+ recent,
237
+ pins,
238
+ activePath: canonicalActive
239
+ }),
240
+ // `status.generation` stands in for the index's contents.
241
+ // eslint-disable-next-line react-hooks/exhaustive-deps
242
+ [query, workspaceId, index, status.generation, recent, pins, canonicalActive]
243
+ );
244
+ useEffect(() => {
245
+ const first = items[0];
246
+ const skipActive = items.length > 1 && first?.workspaceId === workspaceId && first.path === canonicalActive;
247
+ setActive(skipActive ? 1 : 0);
248
+ }, [query]);
249
+ useEffect(() => {
250
+ inputRef.current?.focus();
251
+ inputRef.current?.select();
252
+ }, [focusRequest]);
253
+ const activeIndex = Math.min(active, Math.max(0, items.length - 1));
254
+ const activeId = items.length > 0 ? `${optionPrefix}-${activeIndex}` : void 0;
255
+ useEffect(() => {
256
+ if (activeId) document.getElementById(activeId)?.scrollIntoView({ block: "nearest" });
257
+ }, [activeId]);
258
+ const move = (delta) => {
259
+ if (items.length === 0) return;
260
+ setActive(Math.max(0, Math.min(items.length - 1, activeIndex + delta)));
261
+ };
262
+ const onKeyDown = (event) => {
263
+ switch (event.key) {
264
+ case "ArrowDown":
265
+ event.preventDefault();
266
+ move(1);
267
+ break;
268
+ case "ArrowUp":
269
+ event.preventDefault();
270
+ move(-1);
271
+ break;
272
+ case "PageDown":
273
+ event.preventDefault();
274
+ move(PAGE);
275
+ break;
276
+ case "PageUp":
277
+ event.preventDefault();
278
+ move(-PAGE);
279
+ break;
280
+ case "Enter": {
281
+ event.preventDefault();
282
+ const chosen = items[activeIndex];
283
+ if (chosen) onOpen(chosen);
284
+ break;
285
+ }
286
+ default:
287
+ break;
288
+ }
289
+ };
290
+ const message = statusMessage(status, query, items.length);
291
+ return /* @__PURE__ */ jsxs(
292
+ Dialog,
293
+ {
294
+ title: "Go to document",
295
+ onClose,
296
+ size: "wide",
297
+ placement: "top",
298
+ titleHidden: true,
299
+ initialFocusRef: inputRef,
300
+ className: "db-quick-open",
301
+ bodyClassName: "db-quick-open-body",
302
+ children: [
303
+ /* @__PURE__ */ jsx(
304
+ "input",
305
+ {
306
+ ref: inputRef,
307
+ className: "db-quick-open-input",
308
+ type: "text",
309
+ role: "combobox",
310
+ "aria-label": "Go to document",
311
+ "aria-expanded": items.length > 0,
312
+ "aria-controls": listId,
313
+ "aria-activedescendant": activeId,
314
+ "aria-autocomplete": "list",
315
+ autoComplete: "off",
316
+ spellCheck: false,
317
+ placeholder: "Search documents by name or title",
318
+ value: query,
319
+ onChange: (event) => setQuery(event.target.value),
320
+ onKeyDown
321
+ }
322
+ ),
323
+ /* @__PURE__ */ jsx("ul", { id: listId, role: "listbox", "aria-label": "Documents", className: "db-quick-open-list", children: items.map((item2, position) => /* @__PURE__ */ jsxs(
324
+ "li",
325
+ {
326
+ id: `${optionPrefix}-${position}`,
327
+ role: "option",
328
+ "aria-selected": position === activeIndex,
329
+ className: position === activeIndex ? "db-quick-open-option db-quick-open-option--active" : "db-quick-open-option",
330
+ onMouseDown: (event) => event.preventDefault(),
331
+ onMouseMove: () => {
332
+ if (position !== activeIndex) setActive(position);
333
+ },
334
+ onClick: () => onOpen(item2),
335
+ children: [
336
+ /* @__PURE__ */ jsx("span", { className: "db-quick-open-name", children: /* @__PURE__ */ jsx(HighlightedText, { text: item2.name, ranges: item2.nameRanges }) }),
337
+ item2.title && /* @__PURE__ */ jsx("span", { className: "db-quick-open-title", children: /* @__PURE__ */ jsx(HighlightedText, { text: item2.title, ranges: item2.titleRanges }) }),
338
+ /* @__PURE__ */ jsxs("span", { className: "db-quick-open-folder", children: [
339
+ item2.workspaceName && /* @__PURE__ */ jsxs(Fragment, { children: [
340
+ item2.workspaceName,
341
+ " \xB7 "
342
+ ] }),
343
+ /* @__PURE__ */ jsx(HighlightedText, { text: item2.folder, ranges: item2.folderRanges })
344
+ ] })
345
+ ]
346
+ },
347
+ item2.key
348
+ )) }),
349
+ /* @__PURE__ */ jsx("p", { className: "db-quick-open-status", role: "status", children: message })
350
+ ]
351
+ }
352
+ );
353
+ }
354
+ export {
355
+ QuickOpenDialog
356
+ };
@@ -0,0 +1,144 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { DocBlocksHostAiAPI } from '@bendyline/docblocks/host';
3
+ import { ReactNode } from 'react';
4
+ import { WriteCanvasSettings } from '@bendyline/squisq-editor-react';
5
+
6
+ /** Appearance preferences shared by the shell and settings UI. */
7
+ type ThemePreference = 'auto' | 'light' | 'dark';
8
+ declare const ACCENT_COLORS: readonly ["brown", "green", "blue", "purple", "maroon", "orange", "gray"];
9
+ type AccentColor = (typeof ACCENT_COLORS)[number];
10
+ /**
11
+ * Which typeface the shell chrome uses.
12
+ *
13
+ * `system` resolves `system-ui`, so DocBlocks looks native on each platform —
14
+ * the right default, and what almost everyone should keep. `fixed` pins the
15
+ * chrome to the bundled `DocBlocks Fixed UI` face (the Roboto variable binary
16
+ * already shipped for the document theme of that name) so the interface renders
17
+ * identically everywhere.
18
+ *
19
+ * Two things want that. A user working across machines gets typography that
20
+ * does not shift between them. And the visual-regression suite gets a rendering
21
+ * it can compare across operating systems at all: under `system` the same
22
+ * screenshot differs on every platform, which is why those baselines are
23
+ * otherwise captured on one OS and compared only there.
24
+ */
25
+ type InterfaceFontPreference = 'system' | 'fixed';
26
+ declare const DEFAULT_INTERFACE_FONT: InterfaceFontPreference;
27
+ declare function loadInterfaceFont(): InterfaceFontPreference;
28
+ declare function saveInterfaceFont(value: InterfaceFontPreference): void;
29
+
30
+ /**
31
+ * Stable ids for the Write-canvas font schemes offered in Settings. This list
32
+ * mirrors `DOCBLOCKS_WRITE_CANVAS_FONT_SCHEMES` in `@bendyline/docblocks/vscode`
33
+ * (the wire/persisted contract) and the `docblocks.writeCanvasFontScheme` enum
34
+ * in the VS Code `package.json`; a guard test keeps the three in sync.
35
+ */
36
+ type WriteCanvasFontScheme = 'theme' | 'serif-sans' | 'sans-sans' | 'serif-serif' | 'pt-serif' | 'hanken' | 'playfair-pt-serif' | 'hanken-lora' | 'dm-serif-dm-sans' | 'inter';
37
+ type WriteCanvasFontSchemeGroup = 'theme' | 'system' | 'curated';
38
+ interface WriteCanvasFontSchemeOption {
39
+ id: WriteCanvasFontScheme;
40
+ label: string;
41
+ group: WriteCanvasFontSchemeGroup;
42
+ /** CSS font-family for headings. Omitted for `'theme'` (inherit). */
43
+ headerFont?: string;
44
+ /** CSS font-family for body text. Omitted for `'theme'` (inherit). */
45
+ bodyFont?: string;
46
+ }
47
+ /**
48
+ * Curated Write-canvas font schemes, in picker order. A named scheme is an
49
+ * explicit user preference and overrides the active theme in Write mode through
50
+ * the DocBlocks host stylesheet. `'theme'` sets no fonts, i.e. inherits the
51
+ * active theme / host as before.
52
+ */
53
+ declare const WRITE_CANVAS_FONT_SCHEMES: readonly WriteCanvasFontSchemeOption[];
54
+ declare const DEFAULT_WRITE_CANVAS_FONT_SCHEME: WriteCanvasFontScheme;
55
+ /** Resolve a scheme id to the header/body CSS font-family strings (empty for `'theme'`). */
56
+ declare function resolveWriteCanvasFonts(scheme: WriteCanvasFontScheme): Pick<WriteCanvasSettings, 'headerFont' | 'bodyFont'>;
57
+ /** Persisted Write-canvas preferences. Stores the font scheme id, not resolved fonts. */
58
+ interface WriteCanvasPreferences {
59
+ textSize: number;
60
+ lineSpacing: number;
61
+ fontScheme: WriteCanvasFontScheme;
62
+ }
63
+
64
+ /**
65
+ * Inline proofing preferences — which squiggles the editor draws.
66
+ *
67
+ * Two independent switches rather than one, because they fail
68
+ * differently: spell checking is language-agnostic enough to leave on
69
+ * everywhere, while the grammar rules harper ships are English-only and
70
+ * are noise in a document written in anything else. The engine itself is
71
+ * still all-or-nothing (harper lints once and reports a category per
72
+ * finding), so these filter what surfaces; turning both off is what
73
+ * turns proofing — and the ~30 MiB engine download — off entirely.
74
+ *
75
+ * Stored per browser/profile, never in the document: a reader's tolerance
76
+ * for green underlines is not content. The per-document opt-out lives in
77
+ * `squisq-proofing` frontmatter and is separate from this.
78
+ */
79
+ interface ProofingPreferences {
80
+ /** Draw red squiggles under misspellings. */
81
+ spelling: boolean;
82
+ /** Draw green/blue squiggles under grammar and style findings. */
83
+ grammar: boolean;
84
+ }
85
+ declare const DEFAULT_PROOFING_PREFERENCES: Readonly<ProofingPreferences>;
86
+ declare function loadProofingPreferences(): ProofingPreferences;
87
+ declare function saveProofingPreferences(value: ProofingPreferences): void;
88
+
89
+ interface AiSettingsControlsProps {
90
+ ai: DocBlocksHostAiAPI;
91
+ }
92
+ declare function AiSettingsControls({ ai }: AiSettingsControlsProps): react_jsx_runtime.JSX.Element | null;
93
+
94
+ interface SettingsDialogProps {
95
+ title?: string;
96
+ onClose: () => void;
97
+ children: ReactNode;
98
+ }
99
+ declare function SettingsDialog({ title, onClose, children }: SettingsDialogProps): react_jsx_runtime.JSX.Element;
100
+ interface ThemeSettingsProps {
101
+ value: ThemePreference;
102
+ onChange: (theme: ThemePreference) => void;
103
+ name?: string;
104
+ }
105
+ declare function ThemeSettings({ value, onChange, name }: ThemeSettingsProps): react_jsx_runtime.JSX.Element;
106
+ interface InterfaceFontSettingsProps {
107
+ value: InterfaceFontPreference;
108
+ onChange: (font: InterfaceFontPreference) => void;
109
+ name?: string;
110
+ }
111
+ /**
112
+ * Choose the typeface for the app's own chrome, not for documents.
113
+ *
114
+ * The default follows the operating system, which is what almost everyone
115
+ * should keep. The bundled option exists for two reasons worth stating in the
116
+ * UI: identical typography across machines, and a rendering that is the same
117
+ * everywhere — which is what lets the visual-regression suite compare one set
118
+ * of baselines across operating systems at all.
119
+ */
120
+ declare function InterfaceFontSettings({ value, onChange, name, }: InterfaceFontSettingsProps): react_jsx_runtime.JSX.Element;
121
+ interface AccentColorSettingsProps {
122
+ value: AccentColor;
123
+ onChange: (color: AccentColor) => void;
124
+ name?: string;
125
+ }
126
+ declare function AccentColorSettings({ value, onChange, name, }: AccentColorSettingsProps): react_jsx_runtime.JSX.Element;
127
+ interface ProofingSettingsControlsProps {
128
+ value: ProofingPreferences;
129
+ onChange: (settings: ProofingPreferences) => void;
130
+ }
131
+ /**
132
+ * Spelling and grammar squiggles, as two switches. Grammar is separable
133
+ * because harper's grammar rules are English-only — a writer working in
134
+ * another language wants the spell checker and none of the green
135
+ * underlines. With both off no checking engine is ever downloaded.
136
+ */
137
+ declare function ProofingSettingsControls({ value, onChange }: ProofingSettingsControlsProps): react_jsx_runtime.JSX.Element;
138
+ interface WriteCanvasSettingsControlsProps {
139
+ value: WriteCanvasPreferences;
140
+ onChange: (settings: WriteCanvasPreferences) => void;
141
+ }
142
+ declare function WriteCanvasSettingsControls({ value, onChange }: WriteCanvasSettingsControlsProps): react_jsx_runtime.JSX.Element;
143
+
144
+ export { type AccentColor as A, DEFAULT_INTERFACE_FONT as D, type InterfaceFontPreference as I, type ProofingPreferences as P, SettingsDialog as S, type ThemePreference as T, WRITE_CANVAS_FONT_SCHEMES as W, AccentColorSettings as a, type AccentColorSettingsProps as b, AiSettingsControls as c, type AiSettingsControlsProps as d, DEFAULT_PROOFING_PREFERENCES as e, DEFAULT_WRITE_CANVAS_FONT_SCHEME as f, InterfaceFontSettings as g, type InterfaceFontSettingsProps as h, ProofingSettingsControls as i, type ProofingSettingsControlsProps as j, type SettingsDialogProps as k, ThemeSettings as l, type ThemeSettingsProps as m, type WriteCanvasFontScheme as n, type WriteCanvasFontSchemeGroup as o, type WriteCanvasFontSchemeOption as p, type WriteCanvasPreferences as q, WriteCanvasSettingsControls as r, type WriteCanvasSettingsControlsProps as s, loadInterfaceFont as t, loadProofingPreferences as u, resolveWriteCanvasFonts as v, saveInterfaceFont as w, saveProofingPreferences as x };
@@ -1,62 +1,13 @@
1
+ import {
2
+ buildSharedDocumentQrUrl,
3
+ buildSharedDocumentUrl,
4
+ createSharedDocumentArchive,
5
+ sharedDocumentQrFilename
6
+ } from "./chunk-YS4HVWIO.js";
7
+
1
8
  // src/Export/ShareDialog.tsx
2
9
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
3
- import { SHARED_DOCUMENT_LIMITS as SHARED_DOCUMENT_LIMITS2 } from "@bendyline/docblocks/share";
4
-
5
- // src/Export/shared-document.ts
6
- import {
7
- SHARED_DOCUMENT_LIMITS,
8
- createSharedDocumentUrl
9
- } from "@bendyline/docblocks/share";
10
- var PUBLIC_DOCBLOCKS_SHARE_URL = "https://docblocks.com/";
11
- function resolveSharedDocumentBaseUrl(currentUrl) {
12
- try {
13
- const current = new URL(currentUrl);
14
- if (current.protocol === "http:" || current.protocol === "https:") {
15
- current.hash = "";
16
- current.searchParams.delete("action");
17
- return current.toString();
18
- }
19
- } catch {
20
- }
21
- return PUBLIC_DOCBLOCKS_SHARE_URL;
22
- }
23
- function sharedDocumentFilename(selectedFile) {
24
- const basename = selectedFile?.replace(/\\/gu, "/").split("/").pop() ?? "shared.md";
25
- const stem = basename.replace(/\.md$/iu, "").replace(/[^A-Za-z0-9._-]+/gu, "-");
26
- const bounded = stem.replace(/^-+|-+$/gu, "").slice(0, 80) || "shared";
27
- return `${bounded}.md`;
28
- }
29
- async function createSharedDocumentArchive(markdown, selectedFile) {
30
- const encoded = new TextEncoder().encode(markdown);
31
- if (encoded.byteLength > SHARED_DOCUMENT_LIMITS.markdownBytes) {
32
- throw new Error(
33
- `This document is too large for a shared URL (maximum ${SHARED_DOCUMENT_LIMITS.markdownBytes.toLocaleString()} UTF-8 bytes).`
34
- );
35
- }
36
- const [{ MemoryContentContainer }, { containerToZip }] = await Promise.all([
37
- import("@bendyline/squisq/storage"),
38
- import("@bendyline/squisq-formats/container")
39
- ]);
40
- const container = new MemoryContentContainer();
41
- await container.writeFile(sharedDocumentFilename(selectedFile), encoded, "text/markdown");
42
- const blob = await containerToZip(container, { compression: "DEFLATE", compressionLevel: 9 });
43
- return new Uint8Array(await blob.arrayBuffer());
44
- }
45
- function buildSharedDocumentUrl(baseUrl, archive, mode) {
46
- return createSharedDocumentUrl(resolveSharedDocumentBaseUrl(baseUrl), archive, mode);
47
- }
48
- function buildSharedDocumentQrUrl(archive, mode) {
49
- const url = createSharedDocumentUrl(PUBLIC_DOCBLOCKS_SHARE_URL, archive, mode);
50
- if (url.length > SHARED_DOCUMENT_LIMITS.qrUrlCharacters) {
51
- throw new Error(
52
- "This document is too large for a reliable QR code (maximum " + SHARED_DOCUMENT_LIMITS.qrUrlCharacters.toLocaleString() + " URL characters). Copy the share link instead."
53
- );
54
- }
55
- return url;
56
- }
57
- function sharedDocumentQrFilename(selectedFile) {
58
- return sharedDocumentFilename(selectedFile).replace(/\.md$/iu, "-qr.png");
59
- }
10
+ import { SHARED_DOCUMENT_LIMITS } from "@bendyline/docblocks/share";
60
11
 
61
12
  // src/Export/share-qr.ts
62
13
  import QRCode from "qrcode";
@@ -217,7 +168,7 @@ function ShareDialog({
217
168
  download.click();
218
169
  download.remove();
219
170
  }, [qrDataUrl, selectedFile]);
220
- const isLong = linkResult.url.length > SHARED_DOCUMENT_LIMITS2.portableUrlCharacters;
171
+ const isLong = linkResult.url.length > SHARED_DOCUMENT_LIMITS.portableUrlCharacters;
221
172
  const qrError = qrLinkResult.error ?? qrRenderError;
222
173
  return /* @__PURE__ */ jsx(
223
174
  "div",
@@ -323,7 +274,7 @@ function ShareDialog({
323
274
  children: qrError ?? "Rendering QR code..."
324
275
  }
325
276
  ),
326
- /* @__PURE__ */ jsx("span", { className: "db-export-hint db-share-qr-status", "aria-live": "polite", children: qrCopyStatus === "failed" ? "This browser could not copy the PNG. Save it instead." : qrDataUrl ? `${qrLinkResult.url.length.toLocaleString()} of ${SHARED_DOCUMENT_LIMITS2.qrUrlCharacters.toLocaleString()} supported QR characters.` : "" })
277
+ /* @__PURE__ */ jsx("span", { className: "db-export-hint db-share-qr-status", "aria-live": "polite", children: qrCopyStatus === "failed" ? "This browser could not copy the PNG. Save it instead." : qrDataUrl ? `${qrLinkResult.url.length.toLocaleString()} of ${SHARED_DOCUMENT_LIMITS.qrUrlCharacters.toLocaleString()} supported QR characters.` : "" })
327
278
  ] }),
328
279
  /* @__PURE__ */ jsx("p", { className: "db-share-temporary-note", children: "Opening the link creates a temporary workspace. Changes there do not alter this document or the link." })
329
280
  ] }),