@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.
- package/README.md +1 -1
- package/THIRD_PARTY_NOTICES.txt +290 -48
- package/dist/{AiDiagrams-LFV4YU6W.js → AiDiagrams-QE3LVYFK.js} +2 -2
- package/dist/{AiEditorAssistant-GN2KXKZD.js → AiEditorAssistant-YLJ5OZK2.js} +203 -164
- package/dist/AiStudyDialog-4TQZZGU3.js +1150 -0
- package/dist/{ExportDialog-IMBVGRYG.js → ExportDialog-JRVZCG3J.js} +2 -2
- package/dist/{ExportToolbarControls-MUM6HM3K.js → ExportToolbarControls-A2YBKUUD.js} +46 -9
- package/dist/KeyboardShortcutsDialog-G5OBDTNR.js +145 -0
- package/dist/{PresentationMaker-W2JIBHRN.js → PresentationMaker-P2JP6UUX.js} +2 -2
- package/dist/{Settings-DuXk6baH.d.ts → Settings-ByNImjk5.d.ts} +13 -13
- package/dist/{SpeechToolbarControl-OSQS7NVY.js → SpeechToolbarControl-CLU7RB2G.js} +1 -1
- package/dist/{TextFromNarration-XYRAB6O2.js → TextFromNarration-NITJQ4IB.js} +1 -1
- package/dist/{WorkspaceSearchPanel-BKZJRPMY.js → WorkspaceSearchPanel-J6BKEZQJ.js} +1 -1
- package/dist/{chunk-X3DDTJKR.js → chunk-3CE5RH2B.js} +255 -17
- package/dist/{chunk-UG4G7PJ4.js → chunk-5O5YTOXG.js} +1 -1
- package/dist/{chunk-VO2JYYQZ.js → chunk-6JJOGV6M.js} +5 -13
- package/dist/{chunk-X437UAF7.js → chunk-6MSHXQNB.js} +7 -2
- package/dist/{chunk-BCQZTWE5.js → chunk-6PA7QBZT.js} +107 -24
- package/dist/chunk-ACQF63SB.js +136 -0
- package/dist/chunk-DICE7VBJ.js +77 -0
- package/dist/{chunk-EPOLZNFD.js → chunk-EOU2JWAQ.js} +186 -71
- package/dist/{chunk-G4MFB3IO.js → chunk-VG434562.js} +2 -70
- package/dist/chunk-XNT66JC2.js +168 -0
- package/dist/{chunk-BD54Z5XP.js → chunk-Y5OE3HJC.js} +4 -3
- package/dist/{chunk-NAH52736.js → chunk-YPIVCCTB.js} +969 -98
- package/dist/{chunk-W55E6TE2.js → chunk-ZMCQO57Q.js} +28 -23
- package/dist/editor.js +7 -5
- package/dist/export/index.d.ts +27 -8
- package/dist/export/index.js +4 -4
- package/dist/index.d.ts +36 -8
- package/dist/index.js +629 -446
- package/dist/run-export-OHRNT2TP.js +13 -0
- package/dist/settings/index.d.ts +1 -1
- package/dist/settings/index.js +1 -1
- package/package.json +9 -8
- package/src/styles/docblocks.css +968 -329
- package/dist/chunk-EA4BO6E4.js +0 -73
- 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
|
|
7
|
-
const tooltip =
|
|
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-
|
|
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 =
|
|
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";
|