@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.
@@ -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-PAMOQQFB.js").then((module) => ({ default: module.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://bendyline.github.io/docblocks/";
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({ markdown, selectedFile, baseUrl, onClose }) {
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 via URL" }),
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
  };
@@ -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
- export { DEFAULT_OPTIONS, type ExportBlobSaver, type ExportConverterOverrides, type ExportDestinationControl, ExportDialog, type ExportDialogProps, type ExportFormat, type ExportOptions, FORMAT_EXTENSIONS, FORMAT_LABELS, type HtmlBundle, type HtmlStyle, buildExportFilename, loadLastExportOptions, runExport, saveExportOptions, updateExportTargetExtension };
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 };
@@ -1,6 +1,7 @@
1
1
  import {
2
+ createImageSaveOutput,
2
3
  updateExportTargetExtension
3
- } from "../chunk-BI7NVU6T.js";
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 { FileSystemProvider, FileSystemEntry, FsErrorCode } from '@bendyline/docblocks/filesystem';
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,