@bendyline/docblocks-react 2.3.4 → 2.5.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.
- package/THIRD_PARTY_NOTICES.txt +353 -45
- package/dist/{ExportToolbarControls-N3TCL5DD.js → ExportToolbarControls-BUVJCSMV.js} +1 -1
- package/dist/{ShareDialog-PAMOQQFB.js → ShareDialog-4OX3UPJ4.js} +150 -3
- package/dist/{chunk-BI7NVU6T.js → chunk-FNSRCCBC.js} +10 -0
- package/dist/export/index.d.ts +23 -1
- package/dist/export/index.js +5 -1
- package/dist/index.d.ts +75 -64
- package/dist/index.js +891 -191
- package/package.json +10 -8
- package/src/styles/docblocks.css +79 -1
|
@@ -39,7 +39,7 @@ var ExportDialog = lazy(
|
|
|
39
39
|
() => import("./ExportDialog-VBWHFJ2R.js").then((module) => ({ default: module.ExportDialog }))
|
|
40
40
|
);
|
|
41
41
|
var ShareDialog = lazy(
|
|
42
|
-
() => import("./ShareDialog-
|
|
42
|
+
() => import("./ShareDialog-4OX3UPJ4.js").then((module) => ({ default: module.ShareDialog }))
|
|
43
43
|
);
|
|
44
44
|
var runExportModulePromise = null;
|
|
45
45
|
function loadRunExportModule() {
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
SHARED_DOCUMENT_LIMITS,
|
|
8
8
|
createSharedDocumentUrl
|
|
9
9
|
} from "@bendyline/docblocks/share";
|
|
10
|
-
var PUBLIC_DOCBLOCKS_SHARE_URL = "https://
|
|
10
|
+
var PUBLIC_DOCBLOCKS_SHARE_URL = "https://docblocks.com/";
|
|
11
11
|
function resolveSharedDocumentBaseUrl(currentUrl) {
|
|
12
12
|
try {
|
|
13
13
|
const current = new URL(currentUrl);
|
|
@@ -45,6 +45,45 @@ async function createSharedDocumentArchive(markdown, selectedFile) {
|
|
|
45
45
|
function buildSharedDocumentUrl(baseUrl, archive, mode) {
|
|
46
46
|
return createSharedDocumentUrl(resolveSharedDocumentBaseUrl(baseUrl), archive, mode);
|
|
47
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
|
+
}
|
|
60
|
+
|
|
61
|
+
// src/Export/share-qr.ts
|
|
62
|
+
import QRCode from "qrcode";
|
|
63
|
+
async function renderSharedDocumentQrPng(url) {
|
|
64
|
+
return QRCode.toDataURL(url, {
|
|
65
|
+
type: "image/png",
|
|
66
|
+
errorCorrectionLevel: "M",
|
|
67
|
+
margin: 4,
|
|
68
|
+
width: 1024,
|
|
69
|
+
color: {
|
|
70
|
+
dark: "#111111ff",
|
|
71
|
+
light: "#ffffffff"
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
function qrPngDataUrlToBlob(dataUrl) {
|
|
76
|
+
const prefix = "data:image/png;base64,";
|
|
77
|
+
if (!dataUrl.startsWith(prefix)) {
|
|
78
|
+
throw new Error("The QR code renderer returned an unsupported image format.");
|
|
79
|
+
}
|
|
80
|
+
const binary = atob(dataUrl.slice(prefix.length));
|
|
81
|
+
const bytes = new Uint8Array(binary.length);
|
|
82
|
+
for (let index = 0; index < binary.length; index += 1) {
|
|
83
|
+
bytes[index] = binary.charCodeAt(index);
|
|
84
|
+
}
|
|
85
|
+
return new Blob([bytes], { type: "image/png" });
|
|
86
|
+
}
|
|
48
87
|
|
|
49
88
|
// src/Export/ShareDialog.tsx
|
|
50
89
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -56,11 +95,19 @@ var MODES = [
|
|
|
56
95
|
{ value: "document", label: "Document" },
|
|
57
96
|
{ value: "narrate", label: "Narrate" }
|
|
58
97
|
];
|
|
59
|
-
function ShareDialog({
|
|
98
|
+
function ShareDialog({
|
|
99
|
+
markdown,
|
|
100
|
+
selectedFile,
|
|
101
|
+
baseUrl,
|
|
102
|
+
onClose
|
|
103
|
+
}) {
|
|
60
104
|
const [mode, setMode] = useState("");
|
|
61
105
|
const [archive, setArchive] = useState(null);
|
|
62
106
|
const [archiveError, setArchiveError] = useState(null);
|
|
63
107
|
const [copyStatus, setCopyStatus] = useState("idle");
|
|
108
|
+
const [qrDataUrl, setQrDataUrl] = useState(null);
|
|
109
|
+
const [qrRenderError, setQrRenderError] = useState(null);
|
|
110
|
+
const [qrCopyStatus, setQrCopyStatus] = useState("idle");
|
|
64
111
|
const linkRef = useRef(null);
|
|
65
112
|
useEffect(() => {
|
|
66
113
|
let cancelled = false;
|
|
@@ -90,6 +137,36 @@ function ShareDialog({ markdown, selectedFile, baseUrl, onClose }) {
|
|
|
90
137
|
};
|
|
91
138
|
}
|
|
92
139
|
}, [archive, archiveError, baseUrl, mode]);
|
|
140
|
+
const qrLinkResult = useMemo(() => {
|
|
141
|
+
if (!archive) return { url: "", error: archiveError };
|
|
142
|
+
try {
|
|
143
|
+
return { url: buildSharedDocumentQrUrl(archive, mode || null), error: null };
|
|
144
|
+
} catch (error) {
|
|
145
|
+
return {
|
|
146
|
+
url: "",
|
|
147
|
+
error: error instanceof Error ? error.message : "DocBlocks could not create the QR code."
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
}, [archive, archiveError, mode]);
|
|
151
|
+
useEffect(() => {
|
|
152
|
+
let cancelled = false;
|
|
153
|
+
setQrDataUrl(null);
|
|
154
|
+
setQrRenderError(null);
|
|
155
|
+
setQrCopyStatus("idle");
|
|
156
|
+
if (!qrLinkResult.url) return;
|
|
157
|
+
void renderSharedDocumentQrPng(qrLinkResult.url).then((dataUrl) => {
|
|
158
|
+
if (!cancelled) setQrDataUrl(dataUrl);
|
|
159
|
+
}).catch((error) => {
|
|
160
|
+
if (!cancelled) {
|
|
161
|
+
setQrRenderError(
|
|
162
|
+
error instanceof Error ? error.message : "DocBlocks could not render the QR code."
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
return () => {
|
|
167
|
+
cancelled = true;
|
|
168
|
+
};
|
|
169
|
+
}, [qrLinkResult.url]);
|
|
93
170
|
useEffect(() => setCopyStatus("idle"), [linkResult.url]);
|
|
94
171
|
useEffect(() => {
|
|
95
172
|
const handleKeyDown = (event) => {
|
|
@@ -117,7 +194,31 @@ function ShareDialog({ markdown, selectedFile, baseUrl, onClose }) {
|
|
|
117
194
|
setCopyStatus("failed");
|
|
118
195
|
}
|
|
119
196
|
}, [linkResult.url]);
|
|
197
|
+
const copyQrImage = useCallback(async () => {
|
|
198
|
+
if (!qrDataUrl) return;
|
|
199
|
+
try {
|
|
200
|
+
if (!navigator.clipboard?.write || typeof ClipboardItem === "undefined") {
|
|
201
|
+
throw new Error("Image clipboard access is unavailable.");
|
|
202
|
+
}
|
|
203
|
+
const png = qrPngDataUrlToBlob(qrDataUrl);
|
|
204
|
+
await navigator.clipboard.write([new ClipboardItem({ "image/png": png })]);
|
|
205
|
+
setQrCopyStatus("copied");
|
|
206
|
+
} catch {
|
|
207
|
+
setQrCopyStatus("failed");
|
|
208
|
+
}
|
|
209
|
+
}, [qrDataUrl]);
|
|
210
|
+
const saveQrImage = useCallback(() => {
|
|
211
|
+
if (!qrDataUrl) return;
|
|
212
|
+
const download = document.createElement("a");
|
|
213
|
+
download.href = qrDataUrl;
|
|
214
|
+
download.download = sharedDocumentQrFilename(selectedFile);
|
|
215
|
+
download.style.display = "none";
|
|
216
|
+
document.body.append(download);
|
|
217
|
+
download.click();
|
|
218
|
+
download.remove();
|
|
219
|
+
}, [qrDataUrl, selectedFile]);
|
|
120
220
|
const isLong = linkResult.url.length > SHARED_DOCUMENT_LIMITS2.portableUrlCharacters;
|
|
221
|
+
const qrError = qrLinkResult.error ?? qrRenderError;
|
|
121
222
|
return /* @__PURE__ */ jsx(
|
|
122
223
|
"div",
|
|
123
224
|
{
|
|
@@ -134,7 +235,7 @@ function ShareDialog({ markdown, selectedFile, baseUrl, onClose }) {
|
|
|
134
235
|
"aria-labelledby": "db-share-dialog-title",
|
|
135
236
|
children: [
|
|
136
237
|
/* @__PURE__ */ jsxs("div", { className: "db-dialog-header", children: [
|
|
137
|
-
/* @__PURE__ */ jsx("h2", { className: "db-dialog-title", id: "db-share-dialog-title", children: "Share document
|
|
238
|
+
/* @__PURE__ */ jsx("h2", { className: "db-dialog-title", id: "db-share-dialog-title", children: "Share document" }),
|
|
138
239
|
/* @__PURE__ */ jsx("button", { className: "db-dialog-close", type: "button", onClick: onClose, "aria-label": "Close", children: "\xD7" })
|
|
139
240
|
] }),
|
|
140
241
|
/* @__PURE__ */ jsxs("div", { className: "db-dialog-body", children: [
|
|
@@ -178,6 +279,52 @@ function ShareDialog({ markdown, selectedFile, baseUrl, onClose }) {
|
|
|
178
279
|
}
|
|
179
280
|
)
|
|
180
281
|
] }),
|
|
282
|
+
/* @__PURE__ */ jsxs("section", { className: "db-share-qr", "aria-labelledby": "db-share-qr-title", children: [
|
|
283
|
+
/* @__PURE__ */ jsxs("div", { className: "db-share-qr-heading", children: [
|
|
284
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
285
|
+
/* @__PURE__ */ jsx("h3", { id: "db-share-qr-title", children: "QR code" }),
|
|
286
|
+
/* @__PURE__ */ jsx("span", { className: "db-export-hint", children: "A self-contained docblocks.com link. No document content is uploaded." })
|
|
287
|
+
] }),
|
|
288
|
+
/* @__PURE__ */ jsxs("div", { className: "db-share-qr-actions", children: [
|
|
289
|
+
/* @__PURE__ */ jsx(
|
|
290
|
+
"button",
|
|
291
|
+
{
|
|
292
|
+
className: "db-export-btn db-export-btn--secondary",
|
|
293
|
+
type: "button",
|
|
294
|
+
onClick: () => void copyQrImage(),
|
|
295
|
+
disabled: !qrDataUrl,
|
|
296
|
+
children: qrCopyStatus === "copied" ? "Copied QR" : qrCopyStatus === "failed" ? "Copy unavailable" : "Copy QR image"
|
|
297
|
+
}
|
|
298
|
+
),
|
|
299
|
+
/* @__PURE__ */ jsx(
|
|
300
|
+
"button",
|
|
301
|
+
{
|
|
302
|
+
className: "db-export-btn db-export-btn--secondary",
|
|
303
|
+
type: "button",
|
|
304
|
+
onClick: saveQrImage,
|
|
305
|
+
disabled: !qrDataUrl,
|
|
306
|
+
children: "Save QR PNG"
|
|
307
|
+
}
|
|
308
|
+
)
|
|
309
|
+
] })
|
|
310
|
+
] }),
|
|
311
|
+
qrDataUrl ? /* @__PURE__ */ jsx(
|
|
312
|
+
"img",
|
|
313
|
+
{
|
|
314
|
+
className: "db-share-qr-image",
|
|
315
|
+
src: qrDataUrl,
|
|
316
|
+
alt: "QR code for this shared DocBlocks document"
|
|
317
|
+
}
|
|
318
|
+
) : /* @__PURE__ */ jsx(
|
|
319
|
+
"div",
|
|
320
|
+
{
|
|
321
|
+
className: `db-share-qr-placeholder${qrError ? " db-share-qr-placeholder--error" : ""}`,
|
|
322
|
+
role: qrError ? "note" : "status",
|
|
323
|
+
children: qrError ?? "Rendering QR code..."
|
|
324
|
+
}
|
|
325
|
+
),
|
|
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.` : "" })
|
|
327
|
+
] }),
|
|
181
328
|
/* @__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." })
|
|
182
329
|
] }),
|
|
183
330
|
/* @__PURE__ */ jsxs("div", { className: "db-export-footer", children: [
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
// src/Export/image-save.ts
|
|
2
|
+
function createImageSaveOutput(adapter) {
|
|
3
|
+
return async (blob, filename) => {
|
|
4
|
+
const target = await adapter.pickTarget(filename, null);
|
|
5
|
+
if (!target) return false;
|
|
6
|
+
return await adapter.saveBlob(blob, filename, target) !== null;
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
|
|
1
10
|
// src/Export/export-destination.ts
|
|
2
11
|
function updateExportTargetExtension(targetPath, suggestedFilename) {
|
|
3
12
|
const nextExtension = extensionOf(suggestedFilename);
|
|
@@ -27,5 +36,6 @@ function firstSuffixIndex(value) {
|
|
|
27
36
|
}
|
|
28
37
|
|
|
29
38
|
export {
|
|
39
|
+
createImageSaveOutput,
|
|
30
40
|
updateExportTargetExtension
|
|
31
41
|
};
|
package/dist/export/index.d.ts
CHANGED
|
@@ -101,4 +101,26 @@ declare function ExportDialog({ initial, exporting, error, destination, onExport
|
|
|
101
101
|
/** Keep a user-edited target name while switching the selected export format. */
|
|
102
102
|
declare function updateExportTargetExtension(targetPath: string, suggestedFilename: string): string;
|
|
103
103
|
|
|
104
|
-
|
|
104
|
+
/**
|
|
105
|
+
* Host persistence for the editor's raster-image exporters.
|
|
106
|
+
*
|
|
107
|
+
* Squisq renders both the cover image and the Dashboard image in the
|
|
108
|
+
* browser and hands the host a finished blob; DocBlocks owns where that
|
|
109
|
+
* blob lands. The two exporters share one contract upstream
|
|
110
|
+
* (`DashboardImageSaveOutput` is an alias of `CoverImageSaveOutput`), so
|
|
111
|
+
* they share one adapter here rather than two identical copies.
|
|
112
|
+
*/
|
|
113
|
+
/** Host persistence shape required by the editor's image exporters. */
|
|
114
|
+
interface ImageSaveAdapter<TTarget> {
|
|
115
|
+
pickTarget(filename: string, currentTarget?: TTarget | null): Promise<TTarget | null>;
|
|
116
|
+
saveBlob(blob: Blob, filename: string, target?: TTarget | null): Promise<TTarget | null>;
|
|
117
|
+
}
|
|
118
|
+
type ImageSaveOutput = (blob: Blob, filename: string) => Promise<boolean | void>;
|
|
119
|
+
/**
|
|
120
|
+
* Adapt DocBlocks' two-step host destination flow to Squisq's rendered image
|
|
121
|
+
* output callback. The picker stays host-owned, and cancellation is reported
|
|
122
|
+
* as `false` so the export dialog remains open without showing an error.
|
|
123
|
+
*/
|
|
124
|
+
declare function createImageSaveOutput<TTarget>(adapter: ImageSaveAdapter<TTarget>): ImageSaveOutput;
|
|
125
|
+
|
|
126
|
+
export { type ImageSaveAdapter as CoverImageSaveAdapter, type ImageSaveOutput as CoverImageSaveOutput, DEFAULT_OPTIONS, type ImageSaveAdapter as DashboardImageSaveAdapter, type ImageSaveOutput as DashboardImageSaveOutput, type ExportBlobSaver, type ExportConverterOverrides, type ExportDestinationControl, ExportDialog, type ExportDialogProps, type ExportFormat, type ExportOptions, FORMAT_EXTENSIONS, FORMAT_LABELS, type HtmlBundle, type HtmlStyle, type ImageSaveAdapter, type ImageSaveOutput, buildExportFilename, createImageSaveOutput as createCoverImageSaveOutput, createImageSaveOutput as createDashboardImageSaveOutput, createImageSaveOutput, loadLastExportOptions, runExport, saveExportOptions, updateExportTargetExtension };
|
package/dist/export/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
|
+
createImageSaveOutput,
|
|
2
3
|
updateExportTargetExtension
|
|
3
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-FNSRCCBC.js";
|
|
4
5
|
import {
|
|
5
6
|
ExportDialog
|
|
6
7
|
} from "../chunk-SIIEGOHY.js";
|
|
@@ -25,6 +26,9 @@ export {
|
|
|
25
26
|
FORMAT_EXTENSIONS,
|
|
26
27
|
FORMAT_LABELS,
|
|
27
28
|
buildExportFilename,
|
|
29
|
+
createImageSaveOutput as createCoverImageSaveOutput,
|
|
30
|
+
createImageSaveOutput as createDashboardImageSaveOutput,
|
|
31
|
+
createImageSaveOutput,
|
|
28
32
|
loadLastExportOptions,
|
|
29
33
|
runExport,
|
|
30
34
|
saveExportOptions,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import {
|
|
2
|
+
import { FileSystemEntry, FileSystemProvider, FsErrorCode } from '@bendyline/docblocks/filesystem';
|
|
3
3
|
import { WorkspaceDescriptor } from '@bendyline/docblocks/workspace';
|
|
4
4
|
import { EditorColorScheme } from '@bendyline/squisq-editor-react';
|
|
5
5
|
import { CodeBlockCopyHandler } from '@bendyline/squisq-react';
|
|
@@ -12,11 +12,79 @@ import { MediaProvider } from '@bendyline/squisq/schemas';
|
|
|
12
12
|
import { ContentContainer } from '@bendyline/squisq/storage';
|
|
13
13
|
import { VideoExportModalProps } from '@bendyline/squisq-video-react';
|
|
14
14
|
import { ExportBlobSaver } from './export/index.js';
|
|
15
|
-
export { DEFAULT_OPTIONS, ExportDestinationControl, ExportDialog, ExportDialogProps, ExportFormat, ExportOptions, HtmlBundle, HtmlStyle, buildExportFilename, loadLastExportOptions, runExport, updateExportTargetExtension } from './export/index.js';
|
|
15
|
+
export { CoverImageSaveAdapter, CoverImageSaveOutput, DEFAULT_OPTIONS, CoverImageSaveAdapter as DashboardImageSaveAdapter, CoverImageSaveOutput as DashboardImageSaveOutput, ExportDestinationControl, ExportDialog, ExportDialogProps, ExportFormat, ExportOptions, HtmlBundle, HtmlStyle, CoverImageSaveAdapter as ImageSaveAdapter, CoverImageSaveOutput as ImageSaveOutput, buildExportFilename, createCoverImageSaveOutput, createCoverImageSaveOutput as createDashboardImageSaveOutput, createCoverImageSaveOutput as createImageSaveOutput, loadLastExportOptions, runExport, updateExportTargetExtension } from './export/index.js';
|
|
16
16
|
import { DocumentSession, DocumentSessionSnapshot } from '@bendyline/docblocks/document';
|
|
17
17
|
import 'react';
|
|
18
18
|
import '@bendyline/squisq-formats/pptx';
|
|
19
19
|
|
|
20
|
+
/** Git decoration for a row — precomputed by FileExplorer so this node stays context-free. */
|
|
21
|
+
interface FileTreeNodeBadge {
|
|
22
|
+
kind: string;
|
|
23
|
+
glyph: string;
|
|
24
|
+
label: string;
|
|
25
|
+
}
|
|
26
|
+
/** Git context-menu actions, prebound to this entry's path. */
|
|
27
|
+
interface FileTreeNodeGitActions {
|
|
28
|
+
viewChanges?: () => void;
|
|
29
|
+
fileHistory?: () => void;
|
|
30
|
+
openOnRemote?: () => void;
|
|
31
|
+
}
|
|
32
|
+
/** Host-owned row action shown in both pointer and keyboard menus. */
|
|
33
|
+
interface FileTreeNodeAction {
|
|
34
|
+
label: string;
|
|
35
|
+
onSelect: () => void | Promise<void>;
|
|
36
|
+
disabled?: boolean;
|
|
37
|
+
}
|
|
38
|
+
interface FileTreeNodeProps {
|
|
39
|
+
entry: FileSystemEntry;
|
|
40
|
+
depth: number;
|
|
41
|
+
expanded: boolean;
|
|
42
|
+
selected: boolean;
|
|
43
|
+
badge?: FileTreeNodeBadge;
|
|
44
|
+
gitActions?: FileTreeNodeGitActions;
|
|
45
|
+
actions?: readonly FileTreeNodeAction[];
|
|
46
|
+
children?: FileSystemEntry[];
|
|
47
|
+
/**
|
|
48
|
+
* Roving tabindex: true only for the tree's single tab stop. Defaults to
|
|
49
|
+
* true so a node rendered on its own is still reachable.
|
|
50
|
+
*/
|
|
51
|
+
focusable?: boolean;
|
|
52
|
+
/** 1-based position among siblings, for aria-posinset. */
|
|
53
|
+
posInSet?: number;
|
|
54
|
+
/** Number of siblings, for aria-setsize. */
|
|
55
|
+
setSize?: number;
|
|
56
|
+
/** The row took focus (click or arrow key) — move the roving tab stop. */
|
|
57
|
+
onRowFocus?: (path: string) => void;
|
|
58
|
+
onToggle: (path: string) => void;
|
|
59
|
+
onSelect: (path: string) => void;
|
|
60
|
+
/**
|
|
61
|
+
* Ask the user to confirm an irreversible delete. Resolve `false` to abort.
|
|
62
|
+
*
|
|
63
|
+
* Optional so a standalone `<FileTreeNode>` still asks before destroying
|
|
64
|
+
* something; hosts that have their own dialog (as `<DocBlocksShell>` does)
|
|
65
|
+
* pass it here so the prompt matches the product instead of being a native
|
|
66
|
+
* browser/Electron dialog.
|
|
67
|
+
*/
|
|
68
|
+
confirmDelete?: (message: string) => boolean | Promise<boolean>;
|
|
69
|
+
onDelete: (path: string, kind: 'file' | 'directory') => Promise<void>;
|
|
70
|
+
onRename: (oldPath: string, newPath: string, kind: 'file' | 'directory') => Promise<void>;
|
|
71
|
+
/** Whether this document is present in the shell's cross-workspace pin list. */
|
|
72
|
+
pinned?: boolean;
|
|
73
|
+
/** Pin or unpin this file. Omitted for directories and standalone trees. */
|
|
74
|
+
onTogglePin?: (path: string) => void | Promise<void>;
|
|
75
|
+
draggable?: boolean;
|
|
76
|
+
dragging?: boolean;
|
|
77
|
+
dropTarget?: boolean;
|
|
78
|
+
onDragStart?: (event: React.DragEvent, entry: FileSystemEntry) => void;
|
|
79
|
+
onDragEnd?: () => void;
|
|
80
|
+
onDragOverEntry?: (event: React.DragEvent, entry: FileSystemEntry) => void;
|
|
81
|
+
onDropEntry?: (event: React.DragEvent, entry: FileSystemEntry) => void;
|
|
82
|
+
renderChildren?: (dirPath: string) => React.ReactNode;
|
|
83
|
+
/** Path-scoped directory loading failure, rendered outside the ARIA tree group. */
|
|
84
|
+
childError?: React.ReactNode;
|
|
85
|
+
}
|
|
86
|
+
declare function FileTreeNode({ entry, depth, expanded, selected, badge, gitActions, actions, focusable, posInSet, setSize, onRowFocus, onToggle, onSelect, confirmDelete, onDelete, onRename, pinned, onTogglePin, draggable, dragging, dropTarget, onDragStart, onDragEnd, onDragOverEntry, onDropEntry, renderChildren, childError, }: FileTreeNodeProps): react_jsx_runtime.JSX.Element;
|
|
87
|
+
|
|
20
88
|
interface PinnedDocument {
|
|
21
89
|
readonly workspaceId: string;
|
|
22
90
|
readonly workspaceName: string;
|
|
@@ -78,6 +146,10 @@ interface FileExplorerProps {
|
|
|
78
146
|
onTogglePin?: (path: string) => void | Promise<void>;
|
|
79
147
|
/** Called when any entry is selected (file or directory). */
|
|
80
148
|
onSelect?: (path: string, kind: 'file' | 'directory') => void;
|
|
149
|
+
/** Opens the active native workspace root in the platform file manager. */
|
|
150
|
+
onOpenWorkspaceFolder?: () => void;
|
|
151
|
+
/** Host-owned context actions for an entry. */
|
|
152
|
+
actionsForEntry?: (entry: FileSystemEntry) => readonly FileTreeNodeAction[];
|
|
81
153
|
/**
|
|
82
154
|
* Wraps destructive tree mutations so the active document session can
|
|
83
155
|
* flush, cancel, or retarget itself before storage changes.
|
|
@@ -100,68 +172,7 @@ interface FileExplorerProps {
|
|
|
100
172
|
/** Optional className for the root element. */
|
|
101
173
|
className?: string;
|
|
102
174
|
}
|
|
103
|
-
declare function FileExplorer({ provider, metadataRefreshKey, activeFilePath, sortMode, onSortModeChange, activeWorkspaceId, pinnedDocuments, pinnedPaths, onPinnedDocumentSelect, onPinnedDocumentUnpin, onPinnedDocumentRename, onPinnedDocumentDelete, onTogglePin, onSelect, onTreeMutation, onTreeChange, onImportFiles, confirmDelete, moveDestinations, onMoveToWorkspace, className, }: FileExplorerProps): react_jsx_runtime.JSX.Element;
|
|
104
|
-
|
|
105
|
-
/** Git decoration for a row — precomputed by FileExplorer so this node stays context-free. */
|
|
106
|
-
interface FileTreeNodeBadge {
|
|
107
|
-
kind: string;
|
|
108
|
-
glyph: string;
|
|
109
|
-
label: string;
|
|
110
|
-
}
|
|
111
|
-
/** Git context-menu actions, prebound to this entry's path. */
|
|
112
|
-
interface FileTreeNodeGitActions {
|
|
113
|
-
viewChanges?: () => void;
|
|
114
|
-
fileHistory?: () => void;
|
|
115
|
-
openOnRemote?: () => void;
|
|
116
|
-
}
|
|
117
|
-
interface FileTreeNodeProps {
|
|
118
|
-
entry: FileSystemEntry;
|
|
119
|
-
depth: number;
|
|
120
|
-
expanded: boolean;
|
|
121
|
-
selected: boolean;
|
|
122
|
-
badge?: FileTreeNodeBadge;
|
|
123
|
-
gitActions?: FileTreeNodeGitActions;
|
|
124
|
-
children?: FileSystemEntry[];
|
|
125
|
-
/**
|
|
126
|
-
* Roving tabindex: true only for the tree's single tab stop. Defaults to
|
|
127
|
-
* true so a node rendered on its own is still reachable.
|
|
128
|
-
*/
|
|
129
|
-
focusable?: boolean;
|
|
130
|
-
/** 1-based position among siblings, for aria-posinset. */
|
|
131
|
-
posInSet?: number;
|
|
132
|
-
/** Number of siblings, for aria-setsize. */
|
|
133
|
-
setSize?: number;
|
|
134
|
-
/** The row took focus (click or arrow key) — move the roving tab stop. */
|
|
135
|
-
onRowFocus?: (path: string) => void;
|
|
136
|
-
onToggle: (path: string) => void;
|
|
137
|
-
onSelect: (path: string) => void;
|
|
138
|
-
/**
|
|
139
|
-
* Ask the user to confirm an irreversible delete. Resolve `false` to abort.
|
|
140
|
-
*
|
|
141
|
-
* Optional so a standalone `<FileTreeNode>` still asks before destroying
|
|
142
|
-
* something; hosts that have their own dialog (as `<DocBlocksShell>` does)
|
|
143
|
-
* pass it here so the prompt matches the product instead of being a native
|
|
144
|
-
* browser/Electron dialog.
|
|
145
|
-
*/
|
|
146
|
-
confirmDelete?: (message: string) => boolean | Promise<boolean>;
|
|
147
|
-
onDelete: (path: string, kind: 'file' | 'directory') => Promise<void>;
|
|
148
|
-
onRename: (oldPath: string, newPath: string, kind: 'file' | 'directory') => Promise<void>;
|
|
149
|
-
/** Whether this document is present in the shell's cross-workspace pin list. */
|
|
150
|
-
pinned?: boolean;
|
|
151
|
-
/** Pin or unpin this file. Omitted for directories and standalone trees. */
|
|
152
|
-
onTogglePin?: (path: string) => void | Promise<void>;
|
|
153
|
-
draggable?: boolean;
|
|
154
|
-
dragging?: boolean;
|
|
155
|
-
dropTarget?: boolean;
|
|
156
|
-
onDragStart?: (event: React.DragEvent, entry: FileSystemEntry) => void;
|
|
157
|
-
onDragEnd?: () => void;
|
|
158
|
-
onDragOverEntry?: (event: React.DragEvent, entry: FileSystemEntry) => void;
|
|
159
|
-
onDropEntry?: (event: React.DragEvent, entry: FileSystemEntry) => void;
|
|
160
|
-
renderChildren?: (dirPath: string) => React.ReactNode;
|
|
161
|
-
/** Path-scoped directory loading failure, rendered outside the ARIA tree group. */
|
|
162
|
-
childError?: React.ReactNode;
|
|
163
|
-
}
|
|
164
|
-
declare function FileTreeNode({ entry, depth, expanded, selected, badge, gitActions, focusable, posInSet, setSize, onRowFocus, onToggle, onSelect, confirmDelete, onDelete, onRename, pinned, onTogglePin, draggable, dragging, dropTarget, onDragStart, onDragEnd, onDragOverEntry, onDropEntry, renderChildren, childError, }: FileTreeNodeProps): react_jsx_runtime.JSX.Element;
|
|
175
|
+
declare function FileExplorer({ provider, metadataRefreshKey, activeFilePath, sortMode, onSortModeChange, activeWorkspaceId, pinnedDocuments, pinnedPaths, onPinnedDocumentSelect, onPinnedDocumentUnpin, onPinnedDocumentRename, onPinnedDocumentDelete, onTogglePin, onSelect, onOpenWorkspaceFolder, actionsForEntry, onTreeMutation, onTreeChange, onImportFiles, confirmDelete, moveDestinations, onMoveToWorkspace, className, }: FileExplorerProps): react_jsx_runtime.JSX.Element;
|
|
165
176
|
|
|
166
177
|
/**
|
|
167
178
|
* useFileTree — hook that reads from a FileSystemProvider,
|