@bendyline/docblocks-react 2.7.1 → 2.7.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.
Files changed (38) hide show
  1. package/README.md +1 -1
  2. package/THIRD_PARTY_NOTICES.txt +290 -48
  3. package/dist/{AiDiagrams-LFV4YU6W.js → AiDiagrams-QE3LVYFK.js} +2 -2
  4. package/dist/{AiEditorAssistant-GN2KXKZD.js → AiEditorAssistant-YLJ5OZK2.js} +203 -164
  5. package/dist/AiStudyDialog-4TQZZGU3.js +1150 -0
  6. package/dist/{ExportDialog-IMBVGRYG.js → ExportDialog-JRVZCG3J.js} +2 -2
  7. package/dist/{ExportToolbarControls-MUM6HM3K.js → ExportToolbarControls-A2YBKUUD.js} +46 -9
  8. package/dist/KeyboardShortcutsDialog-G5OBDTNR.js +145 -0
  9. package/dist/{PresentationMaker-W2JIBHRN.js → PresentationMaker-P2JP6UUX.js} +2 -2
  10. package/dist/{Settings-DuXk6baH.d.ts → Settings-ByNImjk5.d.ts} +13 -13
  11. package/dist/{SpeechToolbarControl-OSQS7NVY.js → SpeechToolbarControl-CLU7RB2G.js} +1 -1
  12. package/dist/{TextFromNarration-XYRAB6O2.js → TextFromNarration-NITJQ4IB.js} +1 -1
  13. package/dist/{WorkspaceSearchPanel-BKZJRPMY.js → WorkspaceSearchPanel-J6BKEZQJ.js} +1 -1
  14. package/dist/{chunk-X3DDTJKR.js → chunk-3CE5RH2B.js} +255 -17
  15. package/dist/{chunk-UG4G7PJ4.js → chunk-5O5YTOXG.js} +1 -1
  16. package/dist/{chunk-VO2JYYQZ.js → chunk-6JJOGV6M.js} +5 -13
  17. package/dist/{chunk-X437UAF7.js → chunk-6MSHXQNB.js} +7 -2
  18. package/dist/{chunk-BCQZTWE5.js → chunk-6PA7QBZT.js} +107 -24
  19. package/dist/chunk-ACQF63SB.js +136 -0
  20. package/dist/chunk-DICE7VBJ.js +77 -0
  21. package/dist/{chunk-EPOLZNFD.js → chunk-EOU2JWAQ.js} +186 -71
  22. package/dist/{chunk-G4MFB3IO.js → chunk-VG434562.js} +2 -70
  23. package/dist/chunk-XNT66JC2.js +168 -0
  24. package/dist/{chunk-BD54Z5XP.js → chunk-Y5OE3HJC.js} +4 -3
  25. package/dist/{chunk-NAH52736.js → chunk-YPIVCCTB.js} +969 -98
  26. package/dist/{chunk-W55E6TE2.js → chunk-ZMCQO57Q.js} +28 -23
  27. package/dist/editor.js +7 -5
  28. package/dist/export/index.d.ts +27 -8
  29. package/dist/export/index.js +4 -4
  30. package/dist/index.d.ts +36 -8
  31. package/dist/index.js +629 -446
  32. package/dist/run-export-OHRNT2TP.js +13 -0
  33. package/dist/settings/index.d.ts +1 -1
  34. package/dist/settings/index.js +1 -1
  35. package/package.json +9 -8
  36. package/src/styles/docblocks.css +968 -329
  37. package/dist/chunk-EA4BO6E4.js +0 -73
  38. package/dist/run-export-47X5FTYZ.js +0 -11
@@ -3,8 +3,8 @@ import { useEffect, useState } from "react";
3
3
 
4
4
  // src/components/FindButton.tsx
5
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";
6
+ function FindButton({ active, onActiveChange, shortcutLabel }) {
7
+ const tooltip = shortcutLabel ? `Find in document (${shortcutLabel})` : "Find in document";
8
8
  return /* @__PURE__ */ jsx(
9
9
  "button",
10
10
  {
@@ -45,69 +45,6 @@ function focusFindInput(scope = document) {
45
45
  input?.select();
46
46
  }
47
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
48
  // src/editor.ts
112
49
  var PORTRAIT_FORM_FACTOR_QUERY = "(orientation: portrait)";
113
50
  function previewViewportPresetForOrientation(isPortrait) {
@@ -195,11 +132,6 @@ function pickEmptyDocumentPrompt(random = Math.random) {
195
132
  export {
196
133
  FindButton,
197
134
  focusFindInput,
198
- matchShortcut,
199
- isFindShortcut,
200
- findShortcutLabel,
201
- isApplePlatform,
202
- shortcutLabel,
203
135
  PORTRAIT_FORM_FACTOR_QUERY,
204
136
  previewViewportPresetForOrientation,
205
137
  useResponsivePreviewViewportPreset,
@@ -0,0 +1,168 @@
1
+ // src/Ai/AiGenerationStatus.tsx
2
+ import { useEffect, useState } from "react";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ var LABELS = {
5
+ starting: "Waiting for model\u2026",
6
+ queued: "Waiting for an available model\u2026",
7
+ loading_model: "Loading model\u2026",
8
+ prefill: "Processing input (prefill)\u2026",
9
+ reasoning: "Thinking\u2026",
10
+ generating: "Writing\u2026"
11
+ };
12
+ function duration(milliseconds) {
13
+ const seconds = Math.max(0, Math.floor(milliseconds / 1e3));
14
+ return seconds < 60 ? `${seconds}s` : `${Math.floor(seconds / 60)}m ${String(seconds % 60).padStart(2, "0")}s`;
15
+ }
16
+ function AiGenerationStatus({
17
+ state,
18
+ characters
19
+ }) {
20
+ const [now, setNow] = useState(Date.now);
21
+ useEffect(() => {
22
+ const timer = setInterval(() => setNow(Date.now()), 1e3);
23
+ return () => clearInterval(timer);
24
+ }, []);
25
+ const progress = state.progress;
26
+ const label = progress ? LABELS[progress.phase] : "Waiting for response\u2026";
27
+ const percent = progress?.phase === "prefill" ? progress.percent : null;
28
+ const details = [`${duration(now - state.startedAt)} elapsed`];
29
+ if (progress?.outputTokens != null) {
30
+ details.push(`${progress.outputTokens.toLocaleString()} tokens generated`);
31
+ } else if (characters > 0) {
32
+ details.push(`${characters.toLocaleString()} characters received`);
33
+ }
34
+ if (progress?.tokensPerSecond != null) {
35
+ details.push(`${progress.tokensPerSecond.toFixed(1)} tokens/s`);
36
+ }
37
+ if (now - state.lastActivityAt >= 15e3) {
38
+ details.push(
39
+ progress || characters > 0 ? `Last update ${duration(now - state.lastActivityAt)} ago` : "No progress updates received"
40
+ );
41
+ }
42
+ return /* @__PURE__ */ jsxs("div", { className: "db-ai-generation-status", children: [
43
+ /* @__PURE__ */ jsxs("p", { className: "db-ai-progress", role: "status", children: [
44
+ /* @__PURE__ */ jsx("span", { className: "db-ai-spinner", "aria-hidden": "true" }),
45
+ label,
46
+ percent != null ? ` ${Math.floor(percent)}%` : ""
47
+ ] }),
48
+ percent != null && /* @__PURE__ */ jsx("progress", { "aria-label": label.replace("\u2026", ""), max: 100, value: percent }),
49
+ /* @__PURE__ */ jsx("p", { className: "db-ai-note", "aria-live": "off", children: details.join(" \xB7 ") })
50
+ ] });
51
+ }
52
+
53
+ // src/Ai/study-aids.ts
54
+ import {
55
+ parseFrontmatter,
56
+ setFrontmatterValues,
57
+ splitFrontmatterBlock
58
+ } from "@bendyline/squisq/markdown";
59
+ var STUDY_AID_KINDS = ["summary", "quiz", "flashcards"];
60
+ var STUDY_AID_LABELS = {
61
+ summary: {
62
+ menu: "Summarize document\u2026",
63
+ dialogTitle: "Summarize with AI",
64
+ title: "Summary",
65
+ noun: "summary",
66
+ feature: "Summarize document"
67
+ },
68
+ quiz: {
69
+ menu: "Create quiz\u2026",
70
+ dialogTitle: "Create a quiz with AI",
71
+ title: "Quiz",
72
+ noun: "quiz",
73
+ feature: "Create quiz"
74
+ },
75
+ flashcards: {
76
+ menu: "Create flashcards\u2026",
77
+ dialogTitle: "Create flashcards with AI",
78
+ title: "Flashcards",
79
+ noun: "flashcards",
80
+ feature: "Create flashcards"
81
+ }
82
+ };
83
+ var STUDY_AID_FOLDER = "ai";
84
+ function studyAidPath(kind) {
85
+ return `${STUDY_AID_FOLDER}/${kind}.md`;
86
+ }
87
+ var KEY_KIND = "docblocks-generated";
88
+ var KEY_SOURCE = "docblocks-source";
89
+ var KEY_SOURCE_SHA256 = "docblocks-source-sha256";
90
+ var KEY_GENERATED_AT = "docblocks-generated-at";
91
+ var KEY_MODEL = "docblocks-model";
92
+ function isStudyAidKind(value) {
93
+ return value === "summary" || value === "quiz" || value === "flashcards";
94
+ }
95
+ function withStudyAidFrontmatter(body, title, provenance) {
96
+ return setFrontmatterValues(`
97
+ ${body.replace(/^\n+/u, "")}`, {
98
+ title,
99
+ // Squisq opens a document carrying this hint in its Study view.
100
+ "display-mode": provenance.kind === "summary" ? null : "flashcards",
101
+ [KEY_KIND]: provenance.kind,
102
+ [KEY_SOURCE]: provenance.source,
103
+ [KEY_SOURCE_SHA256]: provenance.sourceSha256,
104
+ [KEY_GENERATED_AT]: provenance.generatedAt,
105
+ [KEY_MODEL]: provenance.model
106
+ });
107
+ }
108
+ function readStudyAidProvenance(markdown) {
109
+ const { frontmatter } = splitFrontmatterBlock(markdown);
110
+ if (frontmatter === null) return null;
111
+ const yaml = frontmatter.replace(/^---\r?\n/u, "").replace(/\r?\n---\s*$/u, "");
112
+ const values = parseFrontmatter(yaml);
113
+ if (!values) return null;
114
+ const kind = values[KEY_KIND];
115
+ const generatedAt = values[KEY_GENERATED_AT];
116
+ if (!isStudyAidKind(kind) || typeof generatedAt !== "string") return null;
117
+ const text = (value) => typeof value === "string" && value.trim() ? value : null;
118
+ return {
119
+ kind,
120
+ source: text(values[KEY_SOURCE]) ?? "",
121
+ sourceSha256: text(values[KEY_SOURCE_SHA256]),
122
+ generatedAt,
123
+ model: text(values[KEY_MODEL])
124
+ };
125
+ }
126
+ function studyAidBody(markdown) {
127
+ return splitFrontmatterBlock(markdown).body.replace(/^\s+/u, "");
128
+ }
129
+ function sourceBody(markdown) {
130
+ return splitFrontmatterBlock(markdown).body.replace(/\r\n?/gu, "\n").replace(/[ \t]+$/gmu, "").trim();
131
+ }
132
+ async function studySourceDigest(markdown) {
133
+ const subtle = globalThis.crypto?.subtle;
134
+ if (!subtle) return null;
135
+ const digest = await subtle.digest("SHA-256", new TextEncoder().encode(sourceBody(markdown)));
136
+ return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
137
+ }
138
+ async function studyAidIsStale(provenance, markdown) {
139
+ if (!provenance?.sourceSha256) return null;
140
+ const digest = await studySourceDigest(markdown);
141
+ return digest === null ? null : digest !== provenance.sourceSha256;
142
+ }
143
+ async function readStudyAid(container, kind) {
144
+ const data = await container.readFile(studyAidPath(kind));
145
+ if (data === null) return null;
146
+ const markdown = new TextDecoder().decode(data);
147
+ return { markdown, provenance: readStudyAidProvenance(markdown) };
148
+ }
149
+ async function writeStudyAid(container, kind, markdown) {
150
+ await container.writeFile(
151
+ studyAidPath(kind),
152
+ new TextEncoder().encode(markdown),
153
+ "text/markdown"
154
+ );
155
+ }
156
+
157
+ export {
158
+ AiGenerationStatus,
159
+ STUDY_AID_KINDS,
160
+ STUDY_AID_LABELS,
161
+ withStudyAidFrontmatter,
162
+ readStudyAidProvenance,
163
+ studyAidBody,
164
+ studySourceDigest,
165
+ studyAidIsStale,
166
+ readStudyAid,
167
+ writeStudyAid
168
+ };
@@ -3,15 +3,15 @@ import {
3
3
  } from "./chunk-YBEYTVU2.js";
4
4
  import {
5
5
  FORMAT_LABELS,
6
+ exportThemeChoices,
6
7
  saveExportOptions
7
- } from "./chunk-X437UAF7.js";
8
+ } from "./chunk-6MSHXQNB.js";
8
9
  import {
9
10
  Dialog
10
11
  } from "./chunk-56KO2LYN.js";
11
12
 
12
13
  // src/Export/ExportDialog.tsx
13
14
  import { useState, useCallback, useEffect, useMemo } from "react";
14
- import { getThemeSummaries } from "@bendyline/squisq/schemas";
15
15
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
16
16
  var FORMATS = ["pdf", "docx", "pptx", "epub", "html", "md"];
17
17
  var FORMAT_CHIP_LABELS = {
@@ -80,6 +80,7 @@ function ChipRadioGroup({
80
80
  }
81
81
  function ExportDialog({
82
82
  initial,
83
+ documentThemes,
83
84
  exporting,
84
85
  error,
85
86
  destination,
@@ -97,7 +98,7 @@ function ExportDialog({
97
98
  const [includeLinkedDocs, setIncludeLinkedDocs] = useState(initial.includeLinkedDocs);
98
99
  const [entryAsIndex, setEntryAsIndex] = useState(initial.entryAsIndex);
99
100
  const [transforms, setTransforms] = useState([]);
100
- const themes = getThemeSummaries();
101
+ const themes = useMemo(() => exportThemeChoices(documentThemes), [documentThemes]);
101
102
  const showTheme = format === "docx" || format === "pdf" || format === "pptx" || format === "epub" || format === "html";
102
103
  const showTransform = format === "pptx";
103
104
  const showPageSize = format === "pdf";