@bendyline/docblocks-react 2.6.2 → 2.7.1
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 +3 -10
- package/THIRD_PARTY_NOTICES.txt +4284 -3651
- package/dist/AiDiagrams-LFV4YU6W.js +13 -0
- package/dist/AiEditorAssistant-GN2KXKZD.js +786 -0
- package/dist/AudioExportDialog-F23QXNPB.js +253 -0
- package/dist/DocumentConflictDialog-5LRVIGXS.js +111 -0
- package/dist/ExportDialog-IMBVGRYG.js +9 -0
- package/dist/{ExportToolbarControls-QYLBRWM7.js → ExportToolbarControls-MUM6HM3K.js} +117 -49
- package/dist/{GitToolbarControl-O5AXBC6T.js → GitToolbarControl-B7KG4MFP.js} +6 -6
- package/dist/{GitUI-L5T3XHZG.js → GitUI-RCWUZVS6.js} +6 -6
- package/dist/PresentationMaker-W2JIBHRN.js +9 -0
- package/dist/QuickOpenDialog-2KK4SEBU.js +356 -0
- package/dist/Settings-DuXk6baH.d.ts +144 -0
- package/dist/ShareDialog-XUA2OVGI.js +292 -0
- package/dist/SpeechToolbarControl-OSQS7NVY.js +434 -0
- package/dist/TextFromNarration-XYRAB6O2.js +598 -0
- package/dist/WorkspaceSearchPanel-BKZJRPMY.js +334 -0
- package/dist/chunk-2IYBBFQX.js +201 -0
- package/dist/{chunk-LG6HAWCK.js → chunk-56KO2LYN.js} +35 -4
- package/dist/chunk-6B7G33D5.js +88 -0
- package/dist/chunk-ARATARVJ.js +40 -0
- package/dist/{chunk-ODYNOPC3.js → chunk-BCQZTWE5.js} +57 -17
- package/dist/{chunk-FMDLZ4ZG.js → chunk-BD54Z5XP.js} +12 -3
- package/dist/chunk-EA4BO6E4.js +73 -0
- package/dist/chunk-EPOLZNFD.js +518 -0
- package/dist/chunk-G4MFB3IO.js +208 -0
- package/dist/{chunk-MRUK56JS.js → chunk-GMA4KGGO.js} +7 -3
- package/dist/chunk-I44HKDQX.js +314 -0
- package/dist/chunk-K4I3NJFL.js +49 -0
- package/dist/chunk-KBDZDFPU.js +61 -0
- package/dist/{chunk-FNSRCCBC.js → chunk-KWG6UP5O.js} +0 -10
- package/dist/chunk-NAH52736.js +1884 -0
- package/dist/chunk-RP3DVNTI.js +62 -0
- package/dist/chunk-SPUPWCSI.js +111 -0
- package/dist/chunk-TBLZBNQF.js +25 -0
- package/dist/{chunk-M5Y5WO7Z.js → chunk-UG4G7PJ4.js} +1 -1
- package/dist/chunk-UONFWKJ7.js +22 -0
- package/dist/chunk-VO2JYYQZ.js +40 -0
- package/dist/chunk-W55E6TE2.js +1714 -0
- package/dist/chunk-X3DDTJKR.js +574 -0
- package/dist/{chunk-EBWYGTN7.js → chunk-X437UAF7.js} +6 -0
- package/dist/editor.d.ts +36 -1
- package/dist/editor.js +16 -1
- package/dist/export/index.d.ts +27 -11
- package/dist/export/index.js +11 -7
- package/dist/index.d.ts +100 -13
- package/dist/index.js +3927 -1638
- package/dist/proofing/index.js +11 -1
- package/dist/{run-export-I2XOVGG4.js → run-export-47X5FTYZ.js} +3 -3
- package/dist/settings/index.d.ts +32 -101
- package/dist/settings/index.js +14 -2
- package/package.json +9 -9
- package/src/styles/docblocks.css +2344 -657
- package/dist/ExportDialog-Q4VWI43H.js +0 -9
- package/dist/ShareDialog-4OX3UPJ4.js +0 -359
- package/dist/chunk-JDJVRDOP.js +0 -92
- package/dist/chunk-Q6XNIKDG.js +0 -669
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// src/Export/video-export-doc.ts
|
|
2
|
+
import { parseMarkdown } from "@bendyline/squisq/markdown";
|
|
3
|
+
var DATA_PREVIEW_ROWS = 50;
|
|
4
|
+
var TRANSFORM_KEY = "squisq-transform";
|
|
5
|
+
var LEGACY_TRANSFORM_KEY = "transform-style";
|
|
6
|
+
function persistedTransformStyle(frontmatter, knownIds) {
|
|
7
|
+
if (!frontmatter) return null;
|
|
8
|
+
const value = Object.prototype.hasOwnProperty.call(frontmatter, TRANSFORM_KEY) ? frontmatter[TRANSFORM_KEY] : frontmatter[LEGACY_TRANSFORM_KEY];
|
|
9
|
+
if (typeof value !== "string") return null;
|
|
10
|
+
const normalized = value.trim().toLowerCase().replace(/\s+/g, "-");
|
|
11
|
+
const id = normalized === "datadriven" ? "data-driven" : normalized;
|
|
12
|
+
return knownIds.has(id) ? id : null;
|
|
13
|
+
}
|
|
14
|
+
async function withDataPreviews(doc, container, resolveDataReferences) {
|
|
15
|
+
try {
|
|
16
|
+
const { defaultDataReaders } = await import("@bendyline/squisq-formats/data");
|
|
17
|
+
const resolved = await resolveDataReferences(doc, container, {
|
|
18
|
+
readers: defaultDataReaders(),
|
|
19
|
+
maxPreviewRows: DATA_PREVIEW_ROWS
|
|
20
|
+
});
|
|
21
|
+
return resolved.doc;
|
|
22
|
+
} catch {
|
|
23
|
+
return doc;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
async function buildVideoExportDoc(markdownSource, options = {}) {
|
|
27
|
+
const [
|
|
28
|
+
{
|
|
29
|
+
buildPreviewDoc,
|
|
30
|
+
documentTitleFromFileName,
|
|
31
|
+
markdownToDoc,
|
|
32
|
+
resolveAudioMapping,
|
|
33
|
+
resolveDataReferences
|
|
34
|
+
},
|
|
35
|
+
{ applyTransform, getTransformStyleSummaries }
|
|
36
|
+
] = await Promise.all([import("@bendyline/squisq/doc"), import("@bendyline/squisq/transform")]);
|
|
37
|
+
let doc = markdownToDoc(parseMarkdown(markdownSource));
|
|
38
|
+
const container = options.workspaceContainer;
|
|
39
|
+
if (container) {
|
|
40
|
+
doc = await resolveAudioMapping(doc, container).catch(() => doc);
|
|
41
|
+
doc = await withDataPreviews(doc, container, resolveDataReferences);
|
|
42
|
+
}
|
|
43
|
+
const knownIds = new Set(getTransformStyleSummaries().map((summary) => summary.id));
|
|
44
|
+
const transformStyle = persistedTransformStyle(doc.frontmatter, knownIds);
|
|
45
|
+
const contentDoc = transformStyle ? applyTransform(doc, transformStyle).doc : doc;
|
|
46
|
+
const narrationOwnsTimeline = (contentDoc.documentMedia ?? []).some(
|
|
47
|
+
(clip) => clip.anchor === "document"
|
|
48
|
+
);
|
|
49
|
+
const projected = buildPreviewDoc(contentDoc, {
|
|
50
|
+
documentTitle: documentTitleFromFileName(options.fileName ?? void 0),
|
|
51
|
+
interleaveImages: !narrationOwnsTimeline
|
|
52
|
+
});
|
|
53
|
+
const invalidPresentation = projected.diagnostics?.find(
|
|
54
|
+
(diagnostic) => diagnostic.code === "presentation-invalid"
|
|
55
|
+
);
|
|
56
|
+
if (invalidPresentation) throw new Error(invalidPresentation.message);
|
|
57
|
+
return { ...projected, audio: contentDoc.audio };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export {
|
|
61
|
+
buildVideoExportDoc
|
|
62
|
+
};
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// src/Export/shared-document.ts
|
|
2
|
+
import {
|
|
3
|
+
SHARED_DOCUMENT_LIMITS,
|
|
4
|
+
createSharedDocumentUrl
|
|
5
|
+
} from "@bendyline/docblocks/share";
|
|
6
|
+
var PUBLIC_DOCBLOCKS_SHARE_URL = "https://docblocks.com/";
|
|
7
|
+
function resolveSharedDocumentBaseUrl(currentUrl) {
|
|
8
|
+
try {
|
|
9
|
+
const current = new URL(currentUrl);
|
|
10
|
+
if (current.protocol === "http:" || current.protocol === "https:") {
|
|
11
|
+
current.hash = "";
|
|
12
|
+
current.searchParams.delete("action");
|
|
13
|
+
return current.toString();
|
|
14
|
+
}
|
|
15
|
+
} catch {
|
|
16
|
+
}
|
|
17
|
+
return PUBLIC_DOCBLOCKS_SHARE_URL;
|
|
18
|
+
}
|
|
19
|
+
function sharedDocumentFilename(selectedFile) {
|
|
20
|
+
const basename = selectedFile?.replace(/\\/gu, "/").split("/").pop() ?? "shared.md";
|
|
21
|
+
const stem = basename.replace(/\.md$/iu, "").replace(/[^A-Za-z0-9._-]+/gu, "-");
|
|
22
|
+
const bounded = stem.replace(/^-+|-+$/gu, "").slice(0, 80) || "shared";
|
|
23
|
+
return `${bounded}.md`;
|
|
24
|
+
}
|
|
25
|
+
async function omitSharedImages(markdown) {
|
|
26
|
+
const { parseMarkdown, rewriteMarkdownReferences, splitFrontmatterBlock, walkMarkdownTree } = await import("@bendyline/squisq/markdown");
|
|
27
|
+
const { frontmatter, body } = splitFrontmatterBlock(markdown);
|
|
28
|
+
const imageReferences = /* @__PURE__ */ new Set();
|
|
29
|
+
const linkReferences = /* @__PURE__ */ new Set();
|
|
30
|
+
walkMarkdownTree(parseMarkdown(body, { frontmatter: false }), (node) => {
|
|
31
|
+
if (node.type === "imageReference") imageReferences.add(node.identifier);
|
|
32
|
+
if (node.type === "linkReference") linkReferences.add(node.identifier);
|
|
33
|
+
});
|
|
34
|
+
let sharedBody = rewriteMarkdownReferences(body, { images: "omit" });
|
|
35
|
+
let sharedDocument = parseMarkdown(sharedBody, { frontmatter: false });
|
|
36
|
+
let descriptionContainsImage = false;
|
|
37
|
+
walkMarkdownTree(sharedDocument, (node) => {
|
|
38
|
+
if (node.type === "image" || node.type === "imageReference") descriptionContainsImage = true;
|
|
39
|
+
if (node.type === "imageReference") imageReferences.add(node.identifier);
|
|
40
|
+
if (node.type === "linkReference") linkReferences.add(node.identifier);
|
|
41
|
+
});
|
|
42
|
+
if (descriptionContainsImage) {
|
|
43
|
+
sharedBody = rewriteMarkdownReferences(sharedBody, { images: "omit" });
|
|
44
|
+
sharedDocument = parseMarkdown(sharedBody, { frontmatter: false });
|
|
45
|
+
walkMarkdownTree(sharedDocument, (node) => {
|
|
46
|
+
if (node.type === "image" || node.type === "imageReference") {
|
|
47
|
+
throw new Error(
|
|
48
|
+
"An image description contains image markup that cannot be shared as text."
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
if (imageReferences.size > 0) {
|
|
54
|
+
const removals = [];
|
|
55
|
+
walkMarkdownTree(sharedDocument, (node) => {
|
|
56
|
+
if (node.type !== "definition" || !imageReferences.has(node.identifier) || linkReferences.has(node.identifier)) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const start = node.position?.start.offset;
|
|
60
|
+
const end = node.position?.end.offset;
|
|
61
|
+
if (start === void 0 || end === void 0) {
|
|
62
|
+
throw new Error("Cannot locate an image reference in the shared document.");
|
|
63
|
+
}
|
|
64
|
+
removals.push({ start, end });
|
|
65
|
+
});
|
|
66
|
+
for (const { start, end } of removals.sort((a, b) => b.start - a.start)) {
|
|
67
|
+
sharedBody = sharedBody.slice(0, start) + sharedBody.slice(end);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return (frontmatter ?? "") + sharedBody;
|
|
71
|
+
}
|
|
72
|
+
async function createSharedDocumentArchive(markdown, selectedFile) {
|
|
73
|
+
const sharedMarkdown = await omitSharedImages(markdown);
|
|
74
|
+
const encoded = new TextEncoder().encode(sharedMarkdown);
|
|
75
|
+
if (encoded.byteLength > SHARED_DOCUMENT_LIMITS.markdownBytes) {
|
|
76
|
+
throw new Error(
|
|
77
|
+
`This document is too large for a shared URL (maximum ${SHARED_DOCUMENT_LIMITS.markdownBytes.toLocaleString()} UTF-8 bytes).`
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
const [{ MemoryContentContainer }, { containerToZip }] = await Promise.all([
|
|
81
|
+
import("@bendyline/squisq/storage"),
|
|
82
|
+
import("@bendyline/squisq-formats/container")
|
|
83
|
+
]);
|
|
84
|
+
const container = new MemoryContentContainer();
|
|
85
|
+
await container.writeFile(sharedDocumentFilename(selectedFile), encoded, "text/markdown");
|
|
86
|
+
const blob = await containerToZip(container, { compression: "DEFLATE", compressionLevel: 9 });
|
|
87
|
+
return new Uint8Array(await blob.arrayBuffer());
|
|
88
|
+
}
|
|
89
|
+
function buildSharedDocumentUrl(baseUrl, archive, mode) {
|
|
90
|
+
return createSharedDocumentUrl(resolveSharedDocumentBaseUrl(baseUrl), archive, mode);
|
|
91
|
+
}
|
|
92
|
+
function buildSharedDocumentQrUrl(archive, mode) {
|
|
93
|
+
const url = createSharedDocumentUrl(PUBLIC_DOCBLOCKS_SHARE_URL, archive, mode);
|
|
94
|
+
if (url.length > SHARED_DOCUMENT_LIMITS.qrUrlCharacters) {
|
|
95
|
+
throw new Error(
|
|
96
|
+
"This document is too large for a reliable QR code (maximum " + SHARED_DOCUMENT_LIMITS.qrUrlCharacters.toLocaleString() + " URL characters). Copy the share link instead."
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
return url;
|
|
100
|
+
}
|
|
101
|
+
function sharedDocumentQrFilename(selectedFile) {
|
|
102
|
+
return sharedDocumentFilename(selectedFile).replace(/\.md$/iu, "-qr.png");
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export {
|
|
106
|
+
sharedDocumentFilename,
|
|
107
|
+
createSharedDocumentArchive,
|
|
108
|
+
buildSharedDocumentUrl,
|
|
109
|
+
buildSharedDocumentQrUrl,
|
|
110
|
+
sharedDocumentQrFilename
|
|
111
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// src/components/HighlightedText.tsx
|
|
2
|
+
import { Fragment } from "react";
|
|
3
|
+
import { Fragment as Fragment2, jsx } from "react/jsx-runtime";
|
|
4
|
+
function HighlightedText({ text, ranges }) {
|
|
5
|
+
const parts = [];
|
|
6
|
+
let cursor = 0;
|
|
7
|
+
for (const [rawStart, rawEnd] of ranges) {
|
|
8
|
+
const start = Math.max(cursor, Math.min(rawStart, text.length));
|
|
9
|
+
const end = Math.min(rawEnd, text.length);
|
|
10
|
+
if (end <= start) continue;
|
|
11
|
+
if (start > cursor)
|
|
12
|
+
parts.push(/* @__PURE__ */ jsx(Fragment, { children: text.slice(cursor, start) }, `t${cursor}`));
|
|
13
|
+
parts.push(
|
|
14
|
+
/* @__PURE__ */ jsx("mark", { className: "db-search-match", children: text.slice(start, end) }, `m${start}`)
|
|
15
|
+
);
|
|
16
|
+
cursor = end;
|
|
17
|
+
}
|
|
18
|
+
if (cursor < text.length)
|
|
19
|
+
parts.push(/* @__PURE__ */ jsx(Fragment, { children: text.slice(cursor) }, `t${cursor}`));
|
|
20
|
+
return /* @__PURE__ */ jsx(Fragment2, { children: parts });
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export {
|
|
24
|
+
HighlightedText
|
|
25
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// src/Export/host-error.ts
|
|
2
|
+
function hostErrorDetail(caught) {
|
|
3
|
+
return caught instanceof Error ? caught.message.replace(/^Error invoking remote method '[^']+': Error:\s*/, "").trim() : "";
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
// src/Export/image-save.ts
|
|
7
|
+
function createImageSaveOutput(adapter) {
|
|
8
|
+
return async (blob, filename) => {
|
|
9
|
+
try {
|
|
10
|
+
const target = await adapter.pickTarget(filename, null);
|
|
11
|
+
if (!target) return false;
|
|
12
|
+
return await adapter.saveBlob(blob, filename, target) !== null;
|
|
13
|
+
} catch (caught) {
|
|
14
|
+
throw new Error(hostErrorDetail(caught) || `Couldn't save "${filename}".`);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export {
|
|
20
|
+
hostErrorDetail,
|
|
21
|
+
createImageSaveOutput
|
|
22
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// src/Speech/useSpeechStatus.ts
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
function useSpeechStatus(speech) {
|
|
4
|
+
const [status, setStatus] = useState(null);
|
|
5
|
+
useEffect(() => {
|
|
6
|
+
let live = true;
|
|
7
|
+
const unsubscribe = speech.onStatus((next) => {
|
|
8
|
+
if (live) setStatus(next);
|
|
9
|
+
});
|
|
10
|
+
void speech.status().then(
|
|
11
|
+
(initial) => {
|
|
12
|
+
if (live) setStatus((current) => current ?? initial);
|
|
13
|
+
},
|
|
14
|
+
() => {
|
|
15
|
+
if (live)
|
|
16
|
+
setStatus(
|
|
17
|
+
(current) => current ?? {
|
|
18
|
+
stt: {
|
|
19
|
+
state: "unavailable",
|
|
20
|
+
reason: "Speech status is unavailable. Try opening Settings."
|
|
21
|
+
},
|
|
22
|
+
tts: {
|
|
23
|
+
state: "unavailable",
|
|
24
|
+
reason: "Speech status is unavailable. Try opening Settings."
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
);
|
|
30
|
+
return () => {
|
|
31
|
+
live = false;
|
|
32
|
+
unsubscribe();
|
|
33
|
+
};
|
|
34
|
+
}, [speech]);
|
|
35
|
+
return status;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export {
|
|
39
|
+
useSpeechStatus
|
|
40
|
+
};
|