@bendyline/docblocks-react 2.3.3 → 2.4.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 +42 -42
- 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 +73 -64
- package/dist/index.js +811 -180
- package/package.json +8 -8
package/dist/index.js
CHANGED
|
@@ -8,8 +8,9 @@ import {
|
|
|
8
8
|
useGitContext
|
|
9
9
|
} from "./chunk-HFYPTTCH.js";
|
|
10
10
|
import {
|
|
11
|
+
createImageSaveOutput,
|
|
11
12
|
updateExportTargetExtension
|
|
12
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-FNSRCCBC.js";
|
|
13
14
|
import {
|
|
14
15
|
AccentColorSettings,
|
|
15
16
|
DB_CHROME_COLORS,
|
|
@@ -658,6 +659,7 @@ function FileTreeNode({
|
|
|
658
659
|
selected,
|
|
659
660
|
badge,
|
|
660
661
|
gitActions,
|
|
662
|
+
actions = [],
|
|
661
663
|
focusable = true,
|
|
662
664
|
posInSet,
|
|
663
665
|
setSize,
|
|
@@ -829,6 +831,18 @@ function FileTreeNode({
|
|
|
829
831
|
);
|
|
830
832
|
}
|
|
831
833
|
}, [closeMenu, entry.path, onTogglePin, pinned]);
|
|
834
|
+
const handleCustomAction = useCallback2(
|
|
835
|
+
async (action) => {
|
|
836
|
+
closeMenu(false);
|
|
837
|
+
setActionError(null);
|
|
838
|
+
try {
|
|
839
|
+
await action.onSelect();
|
|
840
|
+
} catch (caught) {
|
|
841
|
+
setActionError(caught instanceof Error ? caught.message : `Unable to ${action.label}.`);
|
|
842
|
+
}
|
|
843
|
+
},
|
|
844
|
+
[closeMenu]
|
|
845
|
+
);
|
|
832
846
|
useEffect2(() => {
|
|
833
847
|
if (!showContext) return;
|
|
834
848
|
function handleClose(e) {
|
|
@@ -1009,6 +1023,22 @@ function FileTreeNode({
|
|
|
1009
1023
|
children: pinned ? "Unpin" : "Pin"
|
|
1010
1024
|
}
|
|
1011
1025
|
),
|
|
1026
|
+
actions.length > 0 && /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
1027
|
+
/* @__PURE__ */ jsx3("div", { className: "db-tree-context-divider", role: "separator" }),
|
|
1028
|
+
actions.map((action) => /* @__PURE__ */ jsx3(
|
|
1029
|
+
"button",
|
|
1030
|
+
{
|
|
1031
|
+
type: "button",
|
|
1032
|
+
role: "menuitem",
|
|
1033
|
+
tabIndex: -1,
|
|
1034
|
+
className: "db-tree-context-item",
|
|
1035
|
+
disabled: action.disabled,
|
|
1036
|
+
onClick: () => void handleCustomAction(action),
|
|
1037
|
+
children: action.label
|
|
1038
|
+
},
|
|
1039
|
+
action.label
|
|
1040
|
+
))
|
|
1041
|
+
] }),
|
|
1012
1042
|
gitActions && (gitActions.viewChanges || gitActions.fileHistory) && /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
1013
1043
|
/* @__PURE__ */ jsx3("div", { className: "db-tree-context-divider", role: "separator" }),
|
|
1014
1044
|
gitActions.viewChanges && /* @__PURE__ */ jsx3(
|
|
@@ -1565,7 +1595,18 @@ function sortFileEntries(entries, mode) {
|
|
|
1565
1595
|
|
|
1566
1596
|
// src/FileExplorer/FileExplorer.tsx
|
|
1567
1597
|
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1568
|
-
var SUPPORTED_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
1598
|
+
var SUPPORTED_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
1599
|
+
".txt",
|
|
1600
|
+
".md",
|
|
1601
|
+
".html",
|
|
1602
|
+
".htm",
|
|
1603
|
+
".docx",
|
|
1604
|
+
".pdf",
|
|
1605
|
+
".pptx",
|
|
1606
|
+
".xlsx",
|
|
1607
|
+
".dbk",
|
|
1608
|
+
".zip"
|
|
1609
|
+
]);
|
|
1569
1610
|
var INTERNAL_DRAG_TYPE = "application/x-docblocks-entry";
|
|
1570
1611
|
var NEW_ITEM_ERROR_ID = "db-new-item-error";
|
|
1571
1612
|
function normalisePath2(path) {
|
|
@@ -1616,6 +1657,7 @@ function FileExplorer({
|
|
|
1616
1657
|
onPinnedDocumentDelete,
|
|
1617
1658
|
onTogglePin,
|
|
1618
1659
|
onSelect,
|
|
1660
|
+
actionsForEntry,
|
|
1619
1661
|
onTreeMutation,
|
|
1620
1662
|
onTreeChange,
|
|
1621
1663
|
onImportFiles,
|
|
@@ -1975,6 +2017,7 @@ function FileExplorer({
|
|
|
1975
2017
|
selected: tree.selectedPath !== null && normalisePath2(tree.selectedPath) === normalisePath2(entry.path),
|
|
1976
2018
|
badge: badgeFor(entry),
|
|
1977
2019
|
gitActions: gitActionsFor(entry),
|
|
2020
|
+
actions: actionsForEntry?.(entry),
|
|
1978
2021
|
onToggle: tree.toggleExpand,
|
|
1979
2022
|
onSelect: handleSelect,
|
|
1980
2023
|
confirmDelete,
|
|
@@ -2029,6 +2072,7 @@ function FileExplorer({
|
|
|
2029
2072
|
childIssues,
|
|
2030
2073
|
badgeFor,
|
|
2031
2074
|
gitActionsFor,
|
|
2075
|
+
actionsForEntry,
|
|
2032
2076
|
draggedEntry,
|
|
2033
2077
|
dropTarget,
|
|
2034
2078
|
handleInternalDragStart,
|
|
@@ -2523,21 +2567,22 @@ import {
|
|
|
2523
2567
|
DocumentVersionManager
|
|
2524
2568
|
} from "@bendyline/squisq/versions";
|
|
2525
2569
|
import {
|
|
2526
|
-
FileSystemContentContainer,
|
|
2570
|
+
FileSystemContentContainer as FileSystemContentContainer2,
|
|
2527
2571
|
createFileMediaProvider,
|
|
2528
2572
|
decodeUtf8Text,
|
|
2529
|
-
FsError as
|
|
2530
|
-
getFileSystemProviderV2 as
|
|
2573
|
+
FsError as FsError6,
|
|
2574
|
+
getFileSystemProviderV2 as getFileSystemProviderV27,
|
|
2531
2575
|
isQuotaExceededError,
|
|
2532
2576
|
moveFileSystemEntry as moveFileSystemEntry2,
|
|
2533
|
-
parseWorkspacePath as
|
|
2577
|
+
parseWorkspacePath as parseWorkspacePath7,
|
|
2534
2578
|
workspacePathContains
|
|
2535
2579
|
} from "@bendyline/docblocks/filesystem";
|
|
2536
2580
|
import {
|
|
2537
|
-
createFileSystemDocumentTarget,
|
|
2581
|
+
createFileSystemDocumentTarget as createFileSystemDocumentTarget2,
|
|
2538
2582
|
DocumentCommitConflictError,
|
|
2539
2583
|
DocumentSessionConflictError
|
|
2540
2584
|
} from "@bendyline/docblocks/document";
|
|
2585
|
+
import { createMediaProviderFromContainer } from "@bendyline/squisq/storage";
|
|
2541
2586
|
import { isElectronHost as isElectronHost3, getDocBlocksHost } from "@bendyline/docblocks/host";
|
|
2542
2587
|
import {
|
|
2543
2588
|
parseSharedDocumentHash
|
|
@@ -3671,7 +3716,123 @@ function hasControlCharacter(value) {
|
|
|
3671
3716
|
}
|
|
3672
3717
|
|
|
3673
3718
|
// src/DocBlocksShell/import-files.ts
|
|
3674
|
-
import {
|
|
3719
|
+
import {
|
|
3720
|
+
FsError as FsError3,
|
|
3721
|
+
getFileSystemProviderV2 as getFileSystemProviderV23,
|
|
3722
|
+
parseWorkspacePath as parseWorkspacePath4
|
|
3723
|
+
} from "@bendyline/docblocks/filesystem";
|
|
3724
|
+
|
|
3725
|
+
// src/DocBlocksShell/outside-in-contract.ts
|
|
3726
|
+
var OUTSIDE_IN_FORMAT_IDS = ["html", "docx", "pdf", "pptx", "xlsx"];
|
|
3727
|
+
var FORMAT_IDS = new Set(OUTSIDE_IN_FORMAT_IDS);
|
|
3728
|
+
var UPDATE_FROM_MARKDOWN_KEY = "squisq-updatefrommarkdown";
|
|
3729
|
+
function normalizePath(path) {
|
|
3730
|
+
const leading = path.replace(/\\/g, "/").startsWith("/") ? "/" : "";
|
|
3731
|
+
const parts = path.replace(/\\/g, "/").split("/").filter(Boolean);
|
|
3732
|
+
if (parts.some((part) => part === "." || part === "..")) {
|
|
3733
|
+
throw new Error(`Outside-in paths must be canonical workspace paths: ${path}`);
|
|
3734
|
+
}
|
|
3735
|
+
return leading + parts.join("/");
|
|
3736
|
+
}
|
|
3737
|
+
function join(parent, child) {
|
|
3738
|
+
if (!parent || parent === "/") return parent === "/" ? `/${child}` : child;
|
|
3739
|
+
return `${parent}/${child}`;
|
|
3740
|
+
}
|
|
3741
|
+
function slug(stem) {
|
|
3742
|
+
return stem.normalize("NFKD").replace(/\p{Mark}+/gu, "").toLocaleLowerCase("en-US").replace(/[^\p{Letter}\p{Number}]+/gu, "-").replace(/^-+|-+$/g, "") || "document";
|
|
3743
|
+
}
|
|
3744
|
+
function resolveOutsideInLayout(path) {
|
|
3745
|
+
const targetPath = normalizePath(path);
|
|
3746
|
+
const slash = targetPath.lastIndexOf("/");
|
|
3747
|
+
const parentDirectory = slash < 0 ? "" : slash === 0 ? "/" : targetPath.slice(0, slash);
|
|
3748
|
+
const filename = slash < 0 ? targetPath : targetPath.slice(slash + 1);
|
|
3749
|
+
const dot = filename.lastIndexOf(".");
|
|
3750
|
+
if (dot <= 0) return null;
|
|
3751
|
+
const rawFormat = filename.slice(dot + 1).toLowerCase();
|
|
3752
|
+
const format = rawFormat === "htm" ? "html" : rawFormat;
|
|
3753
|
+
if (!FORMAT_IDS.has(format)) return null;
|
|
3754
|
+
const stem = filename.slice(0, dot);
|
|
3755
|
+
const companionName = `${stem}_files`;
|
|
3756
|
+
const companionDirectory = join(parentDirectory, companionName);
|
|
3757
|
+
const markdownFilename = `${slug(stem)}.md`;
|
|
3758
|
+
const backupDirectory = join(companionDirectory, ".original");
|
|
3759
|
+
const backupFilename = `original.${format}`;
|
|
3760
|
+
return {
|
|
3761
|
+
targetPath,
|
|
3762
|
+
format,
|
|
3763
|
+
parentDirectory,
|
|
3764
|
+
stem,
|
|
3765
|
+
companionName,
|
|
3766
|
+
companionDirectory,
|
|
3767
|
+
markdownFilename,
|
|
3768
|
+
markdownPath: join(companionDirectory, markdownFilename),
|
|
3769
|
+
relativeTargetPath: `../${filename}`,
|
|
3770
|
+
backupDirectory,
|
|
3771
|
+
backupFilename,
|
|
3772
|
+
backupPath: join(backupDirectory, backupFilename)
|
|
3773
|
+
};
|
|
3774
|
+
}
|
|
3775
|
+
function chooseOutsideInMarkdownPath(layout, paths) {
|
|
3776
|
+
const canonical = normalizePath(layout.markdownPath);
|
|
3777
|
+
const normalized = paths.map(normalizePath);
|
|
3778
|
+
const exact = normalized.find((path) => path === canonical);
|
|
3779
|
+
if (exact) return exact;
|
|
3780
|
+
const folded = normalized.find(
|
|
3781
|
+
(path) => path.toLocaleLowerCase("en-US") === canonical.toLocaleLowerCase("en-US")
|
|
3782
|
+
);
|
|
3783
|
+
if (folded) return folded;
|
|
3784
|
+
const prefix = `${normalizePath(layout.companionDirectory).replace(/\/$/, "")}/`;
|
|
3785
|
+
const markdown = normalized.filter(
|
|
3786
|
+
(path) => path.startsWith(prefix) && !path.slice(prefix.length).includes("/") && path.toLocaleLowerCase("en-US").endsWith(".md")
|
|
3787
|
+
);
|
|
3788
|
+
return markdown.length === 1 ? markdown[0] : null;
|
|
3789
|
+
}
|
|
3790
|
+
async function readOutsideInMetadata(source) {
|
|
3791
|
+
const { readOutsideInMetadata: readMetadata } = await import("@bendyline/squisq-formats/outside-in");
|
|
3792
|
+
const metadata = readMetadata(source);
|
|
3793
|
+
return metadata ? {
|
|
3794
|
+
...metadata,
|
|
3795
|
+
updateFromMarkdown: await isOutsideInMarkdownEditingEnabled(source)
|
|
3796
|
+
} : null;
|
|
3797
|
+
}
|
|
3798
|
+
async function withOutsideInMetadata(source, layout) {
|
|
3799
|
+
const { withOutsideInMetadata: addMetadata } = await import("@bendyline/squisq-formats/outside-in");
|
|
3800
|
+
return addMetadata(source, layout);
|
|
3801
|
+
}
|
|
3802
|
+
async function isOutsideInMarkdownEditingEnabled(source) {
|
|
3803
|
+
const module = await import("@bendyline/squisq-formats/outside-in");
|
|
3804
|
+
if (module.isOutsideInMarkdownEditingEnabled) {
|
|
3805
|
+
return module.isOutsideInMarkdownEditingEnabled(source);
|
|
3806
|
+
}
|
|
3807
|
+
const { parseFrontmatter, splitFrontmatterBlock } = await import("@bendyline/squisq/markdown");
|
|
3808
|
+
const block = splitFrontmatterBlock(source).frontmatter;
|
|
3809
|
+
if (!block) return false;
|
|
3810
|
+
const firstBreak = block.indexOf("\n");
|
|
3811
|
+
if (firstBreak < 0) return false;
|
|
3812
|
+
const yaml = block.slice(firstBreak + 1).replace(/\r?\n---(?:\r?\n)?$/, "");
|
|
3813
|
+
return parseFrontmatter(yaml)?.[UPDATE_FROM_MARKDOWN_KEY] === true;
|
|
3814
|
+
}
|
|
3815
|
+
async function withOutsideInMarkdownEditing(source, layout, enabled = true) {
|
|
3816
|
+
const module = await import("@bendyline/squisq-formats/outside-in");
|
|
3817
|
+
if (module.withOutsideInMarkdownEditing) {
|
|
3818
|
+
return module.withOutsideInMarkdownEditing(source, layout, enabled);
|
|
3819
|
+
}
|
|
3820
|
+
const { setFrontmatterValues } = await import("@bendyline/squisq/markdown");
|
|
3821
|
+
return setFrontmatterValues(await withOutsideInMetadata(source, layout), {
|
|
3822
|
+
[UPDATE_FROM_MARKDOWN_KEY]: enabled
|
|
3823
|
+
});
|
|
3824
|
+
}
|
|
3825
|
+
async function importOutsideInDocument(source, options = {}) {
|
|
3826
|
+
const { importOutsideInDocument: importDocument } = await import("@bendyline/squisq-formats/outside-in");
|
|
3827
|
+
const imported = await importDocument(source, options);
|
|
3828
|
+
const layout = resolveOutsideInLayout(source.targetPath);
|
|
3829
|
+
if (!layout) throw new Error(`Outside-in editing does not support "${source.targetPath}".`);
|
|
3830
|
+
return { ...imported, layout };
|
|
3831
|
+
}
|
|
3832
|
+
async function renderOutsideInDocument(source, options = {}) {
|
|
3833
|
+
const { renderOutsideInDocument: renderDocument } = await import("@bendyline/squisq-formats/outside-in");
|
|
3834
|
+
return renderDocument(source, options);
|
|
3835
|
+
}
|
|
3675
3836
|
|
|
3676
3837
|
// src/DocBlocksShell/provider-io.ts
|
|
3677
3838
|
import {
|
|
@@ -3730,23 +3891,68 @@ async function claimAvailablePath(provider, desiredPath) {
|
|
|
3730
3891
|
}
|
|
3731
3892
|
throw new Error(`Too many documents are already named like \u201C${desiredPath}\u201D.`);
|
|
3732
3893
|
}
|
|
3733
|
-
async function
|
|
3734
|
-
|
|
3735
|
-
|
|
3894
|
+
async function writeProviderBytes(provider, path, data, mode) {
|
|
3895
|
+
const providerV2 = getFileSystemProviderV23(provider);
|
|
3896
|
+
if (providerV2) {
|
|
3897
|
+
await providerV2.writeFile(parseWorkspacePath4(path), data, {
|
|
3898
|
+
mode,
|
|
3899
|
+
createParents: true,
|
|
3900
|
+
expectedVersion: mode === "create" ? null : void 0
|
|
3901
|
+
});
|
|
3902
|
+
return;
|
|
3736
3903
|
}
|
|
3737
|
-
if (
|
|
3738
|
-
|
|
3739
|
-
const container = await docxToContainer(await file.arrayBuffer());
|
|
3740
|
-
const markdown = await container.readDocument() ?? "";
|
|
3741
|
-
await deps.persistMedia(container, destPath);
|
|
3742
|
-
return markdown;
|
|
3904
|
+
if (mode === "create" && await providerEntryExists(provider, path)) {
|
|
3905
|
+
throw new FsError3("already-exists", "File already exists.", { operation: "write", path });
|
|
3743
3906
|
}
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3907
|
+
await provider.writeBinary(path, data);
|
|
3908
|
+
}
|
|
3909
|
+
var OUTSIDE_IN_EXTENSIONS = /* @__PURE__ */ new Set([".html", ".htm", ".docx", ".pdf", ".pptx", ".xlsx"]);
|
|
3910
|
+
async function claimOutsideInTarget(provider, desiredPath) {
|
|
3911
|
+
for (let attempt = 1; attempt <= MAX_NAME_ATTEMPTS; attempt++) {
|
|
3912
|
+
const candidate = attempt === 1 ? desiredPath : withCopySuffix(desiredPath, attempt);
|
|
3913
|
+
const layout = resolveOutsideInLayout(candidate);
|
|
3914
|
+
if (!layout) throw new Error(`Outside-in editing does not support \u201C${desiredPath}\u201D.`);
|
|
3915
|
+
if (await providerEntryExists(provider, layout.companionDirectory)) continue;
|
|
3916
|
+
try {
|
|
3917
|
+
await writeProviderBytes(provider, candidate, new Uint8Array(), "create");
|
|
3918
|
+
return { path: candidate, renamed: attempt > 1 };
|
|
3919
|
+
} catch (error) {
|
|
3920
|
+
if (!isAlreadyExists(error)) throw error;
|
|
3921
|
+
}
|
|
3922
|
+
}
|
|
3923
|
+
throw new Error(`Too many documents are already named like \u201C${desiredPath}\u201D.`);
|
|
3924
|
+
}
|
|
3925
|
+
async function importOutsideInFile(file, provider) {
|
|
3926
|
+
const claim = await claimOutsideInTarget(provider, file.name);
|
|
3927
|
+
const created = [claim.path];
|
|
3928
|
+
try {
|
|
3929
|
+
const sourceBytes = await file.arrayBuffer();
|
|
3930
|
+
const imported = await importOutsideInDocument({
|
|
3931
|
+
data: sourceBytes,
|
|
3932
|
+
targetPath: claim.path
|
|
3933
|
+
});
|
|
3934
|
+
for (const entry of await imported.container.listFiles()) {
|
|
3935
|
+
if (/\.md$/i.test(entry.path)) continue;
|
|
3936
|
+
const data = await imported.container.readFile(entry.path);
|
|
3937
|
+
if (!data) continue;
|
|
3938
|
+
const destination = `${imported.layout.companionDirectory}/${entry.path.replace(/^\/+/, "")}`;
|
|
3939
|
+
await writeProviderBytes(provider, destination, data, "create");
|
|
3940
|
+
created.push(destination);
|
|
3941
|
+
}
|
|
3942
|
+
await writeProviderText(provider, imported.layout.markdownPath, imported.markdown, "create");
|
|
3943
|
+
created.push(imported.layout.markdownPath);
|
|
3944
|
+
await writeProviderBytes(provider, claim.path, sourceBytes, "upsert");
|
|
3945
|
+
return claim;
|
|
3946
|
+
} catch (error) {
|
|
3947
|
+
for (const path of created.reverse()) {
|
|
3948
|
+
await removeProviderEntry(provider, path).catch(() => void 0);
|
|
3949
|
+
}
|
|
3950
|
+
throw error;
|
|
3951
|
+
}
|
|
3952
|
+
}
|
|
3953
|
+
async function readImportedMarkdown(file, ext, destPath, provider) {
|
|
3954
|
+
if (ext === ".md" || ext === ".txt") {
|
|
3955
|
+
return file.text();
|
|
3750
3956
|
}
|
|
3751
3957
|
const snapshot = await decodeDbkWorkspace(await file.arrayBuffer(), {
|
|
3752
3958
|
targetDocumentPath: destPath
|
|
@@ -3761,8 +3967,8 @@ async function writeDbkCompanions(snapshot, provider) {
|
|
|
3761
3967
|
await writeProviderText(provider, entry.path, entry.content, "create");
|
|
3762
3968
|
}
|
|
3763
3969
|
}
|
|
3764
|
-
var SUPPORTED_EXTENSIONS2 = /* @__PURE__ */ new Set([".md", ".txt",
|
|
3765
|
-
async function importDroppedFiles(files, provider
|
|
3970
|
+
var SUPPORTED_EXTENSIONS2 = /* @__PURE__ */ new Set([".md", ".txt", ...OUTSIDE_IN_EXTENSIONS, ".dbk", ".zip"]);
|
|
3971
|
+
async function importDroppedFiles(files, provider) {
|
|
3766
3972
|
const result = { imported: [], failed: [], unsupported: [] };
|
|
3767
3973
|
for (const file of files) {
|
|
3768
3974
|
const ext = file.name.slice(file.name.lastIndexOf(".")).toLowerCase();
|
|
@@ -3773,9 +3979,14 @@ async function importDroppedFiles(files, provider, deps) {
|
|
|
3773
3979
|
const baseName = file.name.replace(/\.[^.]+$/, "");
|
|
3774
3980
|
let claimed = null;
|
|
3775
3981
|
try {
|
|
3982
|
+
if (OUTSIDE_IN_EXTENSIONS.has(ext)) {
|
|
3983
|
+
const imported = await importOutsideInFile(file, provider);
|
|
3984
|
+
result.imported.push({ source: file.name, ...imported });
|
|
3985
|
+
continue;
|
|
3986
|
+
}
|
|
3776
3987
|
const claim = await claimAvailablePath(provider, `${baseName}.md`);
|
|
3777
3988
|
claimed = claim.path;
|
|
3778
|
-
const markdown = await readImportedMarkdown(file, ext, claim.path, provider
|
|
3989
|
+
const markdown = await readImportedMarkdown(file, ext, claim.path, provider);
|
|
3779
3990
|
await writeProviderText(provider, claim.path, markdown);
|
|
3780
3991
|
result.imported.push({ source: file.name, path: claim.path, renamed: claim.renamed });
|
|
3781
3992
|
} catch (error) {
|
|
@@ -3807,7 +4018,7 @@ function summariseImport(result) {
|
|
|
3807
4018
|
if (renamed.length === 1) {
|
|
3808
4019
|
return {
|
|
3809
4020
|
kind: "success",
|
|
3810
|
-
message: `Imported as ${renamed[0].path} \u2014 a document
|
|
4021
|
+
message: `Imported as ${renamed[0].path} \u2014 a document with that name or companion folder already exists.`
|
|
3811
4022
|
};
|
|
3812
4023
|
}
|
|
3813
4024
|
if (renamed.length > 1) {
|
|
@@ -3819,6 +4030,241 @@ function summariseImport(result) {
|
|
|
3819
4030
|
return null;
|
|
3820
4031
|
}
|
|
3821
4032
|
|
|
4033
|
+
// src/DocBlocksShell/outside-in.ts
|
|
4034
|
+
import {
|
|
4035
|
+
FileSystemContentContainer,
|
|
4036
|
+
FsError as FsError4,
|
|
4037
|
+
getFileSystemProviderV2 as getFileSystemProviderV24,
|
|
4038
|
+
parseWorkspacePath as parseWorkspacePath5
|
|
4039
|
+
} from "@bendyline/docblocks/filesystem";
|
|
4040
|
+
import {
|
|
4041
|
+
createFileSystemDocumentTarget
|
|
4042
|
+
} from "@bendyline/docblocks/document";
|
|
4043
|
+
var OUTSIDE_IN_EXTENSION = /\.(?:html?|docx|pdf|pptx|xlsx)$/i;
|
|
4044
|
+
var SQUISQ_RUNTIME_DIRECTORY = "_squisq";
|
|
4045
|
+
var SQUISQ_RUNTIME_FILENAME = "squisq-player.js";
|
|
4046
|
+
function withoutLeadingSlash(path) {
|
|
4047
|
+
return parseWorkspacePath5(path);
|
|
4048
|
+
}
|
|
4049
|
+
function withLegacySlash(path, like) {
|
|
4050
|
+
const canonical = withoutLeadingSlash(path);
|
|
4051
|
+
return like.startsWith("/") && canonical ? `/${canonical}` : canonical;
|
|
4052
|
+
}
|
|
4053
|
+
function dirname(path) {
|
|
4054
|
+
const canonical = withoutLeadingSlash(path);
|
|
4055
|
+
const slash = canonical.lastIndexOf("/");
|
|
4056
|
+
return slash < 0 ? "" : canonical.slice(0, slash);
|
|
4057
|
+
}
|
|
4058
|
+
function join2(parent, child) {
|
|
4059
|
+
return parent ? `${parent}/${child}` : child;
|
|
4060
|
+
}
|
|
4061
|
+
function relativePath(fromDirectory, targetPath) {
|
|
4062
|
+
const from = withoutLeadingSlash(fromDirectory).split("/").filter(Boolean);
|
|
4063
|
+
const target = withoutLeadingSlash(targetPath).split("/").filter(Boolean);
|
|
4064
|
+
let shared = 0;
|
|
4065
|
+
while (shared < from.length && shared < target.length && from[shared] === target[shared]) {
|
|
4066
|
+
shared++;
|
|
4067
|
+
}
|
|
4068
|
+
const segments = [...from.slice(shared).map(() => ".."), ...target.slice(shared)];
|
|
4069
|
+
return segments.join("/") || ".";
|
|
4070
|
+
}
|
|
4071
|
+
async function readText(provider, path) {
|
|
4072
|
+
const v2 = getFileSystemProviderV24(provider);
|
|
4073
|
+
if (!v2) return provider.readFile(path);
|
|
4074
|
+
const current = await v2.readFile(parseWorkspacePath5(path));
|
|
4075
|
+
if (!current) return null;
|
|
4076
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(current.data);
|
|
4077
|
+
}
|
|
4078
|
+
async function readBytes(provider, path) {
|
|
4079
|
+
const v2 = getFileSystemProviderV24(provider);
|
|
4080
|
+
if (v2) return (await v2.readFile(parseWorkspacePath5(path)))?.data ?? null;
|
|
4081
|
+
return provider.readBinary(path);
|
|
4082
|
+
}
|
|
4083
|
+
async function writeBytes(provider, path, data, mode = "upsert") {
|
|
4084
|
+
const v2 = getFileSystemProviderV24(provider);
|
|
4085
|
+
if (v2) {
|
|
4086
|
+
await v2.writeFile(parseWorkspacePath5(path), data, {
|
|
4087
|
+
mode,
|
|
4088
|
+
createParents: true,
|
|
4089
|
+
expectedVersion: mode === "create" ? null : void 0
|
|
4090
|
+
});
|
|
4091
|
+
return;
|
|
4092
|
+
}
|
|
4093
|
+
if (mode === "create" && await provider.exists(path)) {
|
|
4094
|
+
throw new FsError4("already-exists", "File already exists.", { operation: "write", path });
|
|
4095
|
+
}
|
|
4096
|
+
await provider.writeBinary(path, data);
|
|
4097
|
+
}
|
|
4098
|
+
async function listCompanionFiles(provider, layout) {
|
|
4099
|
+
try {
|
|
4100
|
+
const entries = await provider.readDirectory(layout.companionDirectory);
|
|
4101
|
+
return entries.filter((entry) => entry.kind === "file").map((entry) => entry.path);
|
|
4102
|
+
} catch (error) {
|
|
4103
|
+
if (error instanceof FsError4 && error.code === "not-found") return [];
|
|
4104
|
+
throw error;
|
|
4105
|
+
}
|
|
4106
|
+
}
|
|
4107
|
+
async function removeOutsideInCompanion(provider, layout) {
|
|
4108
|
+
const providerV2 = getFileSystemProviderV24(provider);
|
|
4109
|
+
if (providerV2) {
|
|
4110
|
+
const path = parseWorkspacePath5(layout.companionDirectory);
|
|
4111
|
+
if (await providerV2.stat(path) !== null) {
|
|
4112
|
+
await providerV2.remove(path, { recursive: true, missing: "ignore" });
|
|
4113
|
+
}
|
|
4114
|
+
return;
|
|
4115
|
+
}
|
|
4116
|
+
if (await provider.exists(layout.companionDirectory)) {
|
|
4117
|
+
await provider.delete(layout.companionDirectory);
|
|
4118
|
+
}
|
|
4119
|
+
}
|
|
4120
|
+
async function persistImportedMedia(provider, layout, container) {
|
|
4121
|
+
const entries = await container.listFiles();
|
|
4122
|
+
for (const entry of entries) {
|
|
4123
|
+
if (/\.md$/i.test(entry.path)) continue;
|
|
4124
|
+
const data = await container.readFile(entry.path);
|
|
4125
|
+
if (!data) continue;
|
|
4126
|
+
await writeBytes(provider, join2(layout.companionDirectory, entry.path), data, "create");
|
|
4127
|
+
}
|
|
4128
|
+
}
|
|
4129
|
+
async function loadEditableShellDocument(provider, selectedPath) {
|
|
4130
|
+
if (!OUTSIDE_IN_EXTENSION.test(selectedPath)) {
|
|
4131
|
+
const content = await readText(provider, selectedPath);
|
|
4132
|
+
return content === null ? null : {
|
|
4133
|
+
displayPath: selectedPath,
|
|
4134
|
+
sourcePath: selectedPath,
|
|
4135
|
+
content,
|
|
4136
|
+
outsideIn: null,
|
|
4137
|
+
outsideInEditingEnabled: true
|
|
4138
|
+
};
|
|
4139
|
+
}
|
|
4140
|
+
const resolved = resolveOutsideInLayout(selectedPath);
|
|
4141
|
+
if (!resolved) return null;
|
|
4142
|
+
const layout = {
|
|
4143
|
+
...resolved,
|
|
4144
|
+
targetPath: withLegacySlash(resolved.targetPath, selectedPath),
|
|
4145
|
+
companionDirectory: withLegacySlash(resolved.companionDirectory, selectedPath),
|
|
4146
|
+
markdownPath: withLegacySlash(resolved.markdownPath, selectedPath)
|
|
4147
|
+
};
|
|
4148
|
+
const candidates = (await listCompanionFiles(provider, layout)).map(
|
|
4149
|
+
(path) => withLegacySlash(path, selectedPath)
|
|
4150
|
+
);
|
|
4151
|
+
const chosen = chooseOutsideInMarkdownPath(layout, candidates);
|
|
4152
|
+
if (chosen) {
|
|
4153
|
+
const content = await readText(provider, chosen);
|
|
4154
|
+
if (content === null) return null;
|
|
4155
|
+
const metadata = await readOutsideInMetadata(content);
|
|
4156
|
+
if (metadata && metadata.format !== layout.format) {
|
|
4157
|
+
throw new Error(
|
|
4158
|
+
`${chosen} is configured for ${metadata.format}, not ${layout.format}. Rename the rendered file back or repair the companion frontmatter.`
|
|
4159
|
+
);
|
|
4160
|
+
}
|
|
4161
|
+
const linkedContent = await withOutsideInMetadata(content, layout);
|
|
4162
|
+
if (linkedContent !== content) await writeProviderText(provider, chosen, linkedContent);
|
|
4163
|
+
return {
|
|
4164
|
+
displayPath: selectedPath,
|
|
4165
|
+
sourcePath: chosen,
|
|
4166
|
+
content: linkedContent,
|
|
4167
|
+
outsideIn: { ...layout, markdownPath: chosen },
|
|
4168
|
+
outsideInEditingEnabled: await isOutsideInMarkdownEditingEnabled(linkedContent)
|
|
4169
|
+
};
|
|
4170
|
+
}
|
|
4171
|
+
const rendered = await readBytes(provider, selectedPath);
|
|
4172
|
+
if (!rendered) return null;
|
|
4173
|
+
const imported = await importOutsideInDocument({
|
|
4174
|
+
data: rendered,
|
|
4175
|
+
targetPath: selectedPath
|
|
4176
|
+
});
|
|
4177
|
+
const importedLayout = {
|
|
4178
|
+
...imported.layout,
|
|
4179
|
+
targetPath: withLegacySlash(imported.layout.targetPath, selectedPath),
|
|
4180
|
+
companionDirectory: withLegacySlash(imported.layout.companionDirectory, selectedPath),
|
|
4181
|
+
markdownPath: withLegacySlash(imported.layout.markdownPath, selectedPath)
|
|
4182
|
+
};
|
|
4183
|
+
await persistImportedMedia(provider, importedLayout, imported.container);
|
|
4184
|
+
await writeProviderText(provider, importedLayout.markdownPath, imported.markdown, "create");
|
|
4185
|
+
return {
|
|
4186
|
+
displayPath: selectedPath,
|
|
4187
|
+
sourcePath: importedLayout.markdownPath,
|
|
4188
|
+
content: imported.markdown,
|
|
4189
|
+
outsideIn: importedLayout,
|
|
4190
|
+
outsideInEditingEnabled: false
|
|
4191
|
+
};
|
|
4192
|
+
}
|
|
4193
|
+
async function enableOutsideInMarkdownEditing(provider, document2) {
|
|
4194
|
+
const layout = document2.outsideIn;
|
|
4195
|
+
if (!layout) throw new Error("This file does not support outside-in Markdown editing.");
|
|
4196
|
+
const original = await readBytes(provider, layout.targetPath);
|
|
4197
|
+
if (!original) throw new Error(`The rendered file "${layout.targetPath}" was not found.`);
|
|
4198
|
+
try {
|
|
4199
|
+
await writeBytes(provider, layout.backupPath, original, "create");
|
|
4200
|
+
} catch (error) {
|
|
4201
|
+
if (!(error instanceof FsError4 && error.code === "already-exists")) throw error;
|
|
4202
|
+
}
|
|
4203
|
+
const content = await withOutsideInMarkdownEditing(document2.content, layout);
|
|
4204
|
+
if (content !== document2.content) await writeProviderText(provider, document2.sourcePath, content);
|
|
4205
|
+
return { ...document2, content, outsideIn: layout, outsideInEditingEnabled: true };
|
|
4206
|
+
}
|
|
4207
|
+
async function findRuntimePath(provider, targetPath) {
|
|
4208
|
+
let directory = dirname(targetPath);
|
|
4209
|
+
for (; ; ) {
|
|
4210
|
+
const candidateDirectory = join2(directory, SQUISQ_RUNTIME_DIRECTORY);
|
|
4211
|
+
if (await providerEntryExists(provider, candidateDirectory)) {
|
|
4212
|
+
const providerV2 = getFileSystemProviderV24(provider);
|
|
4213
|
+
if (providerV2) {
|
|
4214
|
+
const entry = await providerV2.stat(parseWorkspacePath5(candidateDirectory));
|
|
4215
|
+
if (entry?.kind !== "directory") {
|
|
4216
|
+
throw new Error(`${candidateDirectory} must be a directory.`);
|
|
4217
|
+
}
|
|
4218
|
+
}
|
|
4219
|
+
return join2(candidateDirectory, SQUISQ_RUNTIME_FILENAME);
|
|
4220
|
+
}
|
|
4221
|
+
if (!directory) break;
|
|
4222
|
+
directory = dirname(directory);
|
|
4223
|
+
}
|
|
4224
|
+
return join2(SQUISQ_RUNTIME_DIRECTORY, SQUISQ_RUNTIME_FILENAME);
|
|
4225
|
+
}
|
|
4226
|
+
async function writeRuntimeIfNeeded(provider, runtimePath) {
|
|
4227
|
+
const { PLAYER_BUNDLE } = await import("@bendyline/squisq-react/standalone-source");
|
|
4228
|
+
const current = await readText(provider, runtimePath);
|
|
4229
|
+
if (current !== PLAYER_BUNDLE) await writeProviderText(provider, runtimePath, PLAYER_BUNDLE);
|
|
4230
|
+
}
|
|
4231
|
+
function createOutsideInDocumentTarget(provider, layout, onCommitted) {
|
|
4232
|
+
const sourceTarget = createFileSystemDocumentTarget(provider, layout.markdownPath);
|
|
4233
|
+
return {
|
|
4234
|
+
key: `${provider.id}:outside-in:${parseWorkspacePath5(layout.targetPath)}`,
|
|
4235
|
+
async commit(request) {
|
|
4236
|
+
if (!await isOutsideInMarkdownEditingEnabled(request.content)) {
|
|
4237
|
+
throw new Error(
|
|
4238
|
+
"Outside-in editing is read-only until squisq-updatefrommarkdown: true is set."
|
|
4239
|
+
);
|
|
4240
|
+
}
|
|
4241
|
+
const runtimePath = layout.format === "html" ? await findRuntimePath(provider, layout.targetPath) : null;
|
|
4242
|
+
const outputDirectory = dirname(layout.targetPath);
|
|
4243
|
+
const rendered = await renderOutsideInDocument(
|
|
4244
|
+
{
|
|
4245
|
+
markdown: request.content,
|
|
4246
|
+
targetPath: layout.targetPath,
|
|
4247
|
+
container: new FileSystemContentContainer(provider, layout.companionDirectory)
|
|
4248
|
+
},
|
|
4249
|
+
runtimePath ? {
|
|
4250
|
+
html: {
|
|
4251
|
+
playerScriptPath: relativePath(outputDirectory, runtimePath),
|
|
4252
|
+
basePath: relativePath(outputDirectory, layout.companionDirectory)
|
|
4253
|
+
}
|
|
4254
|
+
} : {}
|
|
4255
|
+
);
|
|
4256
|
+
await sourceTarget.commit({ ...request, targetKey: sourceTarget.key });
|
|
4257
|
+
if (runtimePath) await writeRuntimeIfNeeded(provider, runtimePath);
|
|
4258
|
+
await writeBytes(provider, layout.targetPath, rendered.bytes);
|
|
4259
|
+
onCommitted?.();
|
|
4260
|
+
return {};
|
|
4261
|
+
}
|
|
4262
|
+
};
|
|
4263
|
+
}
|
|
4264
|
+
function createOutsideInContentContainer(provider, layout) {
|
|
4265
|
+
return new FileSystemContentContainer(provider, layout.companionDirectory);
|
|
4266
|
+
}
|
|
4267
|
+
|
|
3822
4268
|
// src/components/usePromptDialog.ts
|
|
3823
4269
|
import React2, { useCallback as useCallback11, useEffect as useEffect12, useRef as useRef11, useState as useState12 } from "react";
|
|
3824
4270
|
|
|
@@ -4232,9 +4678,9 @@ import { useCallback as useCallback14, useEffect as useEffect14, useRef as useRe
|
|
|
4232
4678
|
|
|
4233
4679
|
// src/DocBlocksShell/document-links.ts
|
|
4234
4680
|
import {
|
|
4235
|
-
FsError as
|
|
4236
|
-
getFileSystemProviderV2 as
|
|
4237
|
-
parseWorkspacePath as
|
|
4681
|
+
FsError as FsError5,
|
|
4682
|
+
getFileSystemProviderV2 as getFileSystemProviderV25,
|
|
4683
|
+
parseWorkspacePath as parseWorkspacePath6
|
|
4238
4684
|
} from "@bendyline/docblocks/filesystem";
|
|
4239
4685
|
var DEFAULT_DOCUMENT_DISCOVERY_LIMITS = Object.freeze({
|
|
4240
4686
|
entries: 1e4,
|
|
@@ -4257,9 +4703,9 @@ function positiveLimit(value, fallback, label) {
|
|
|
4257
4703
|
return selected;
|
|
4258
4704
|
}
|
|
4259
4705
|
async function readDirectory(provider, path) {
|
|
4260
|
-
const providerV2 =
|
|
4706
|
+
const providerV2 = getFileSystemProviderV25(provider);
|
|
4261
4707
|
if (!providerV2) return provider.readDirectory(path);
|
|
4262
|
-
const entries = await providerV2.readDirectory(
|
|
4708
|
+
const entries = await providerV2.readDirectory(parseWorkspacePath6(path));
|
|
4263
4709
|
return entries.map((entry) => ({ kind: entry.kind, name: entry.name, path: entry.path }));
|
|
4264
4710
|
}
|
|
4265
4711
|
function cancellationError(signal) {
|
|
@@ -4326,7 +4772,7 @@ async function collectMarkdownFiles(provider, root, options = {}) {
|
|
|
4326
4772
|
while (pending.length > 0) {
|
|
4327
4773
|
if (options.signal?.aborted) throw cancellationError(options.signal);
|
|
4328
4774
|
const current = pending.pop();
|
|
4329
|
-
const canonical =
|
|
4775
|
+
const canonical = parseWorkspacePath6(current.path);
|
|
4330
4776
|
if (visited.has(canonical)) continue;
|
|
4331
4777
|
visited.add(canonical);
|
|
4332
4778
|
if (visited.size > maxDirectories) {
|
|
@@ -4339,7 +4785,7 @@ async function collectMarkdownFiles(provider, root, options = {}) {
|
|
|
4339
4785
|
try {
|
|
4340
4786
|
entries = await readWithBudget(provider, canonical, deadline, options.signal);
|
|
4341
4787
|
} catch (error) {
|
|
4342
|
-
if (error instanceof
|
|
4788
|
+
if (error instanceof FsError5 && error.code === "not-found") continue;
|
|
4343
4789
|
throw error;
|
|
4344
4790
|
}
|
|
4345
4791
|
entryCount += entries.length;
|
|
@@ -4368,7 +4814,7 @@ async function collectMarkdownFiles(provider, root, options = {}) {
|
|
|
4368
4814
|
}
|
|
4369
4815
|
return markdown;
|
|
4370
4816
|
}
|
|
4371
|
-
function
|
|
4817
|
+
function dirname2(path) {
|
|
4372
4818
|
const clean = path.replace(/^\/+/, "");
|
|
4373
4819
|
const index = clean.lastIndexOf("/");
|
|
4374
4820
|
return index < 0 ? "" : clean.slice(0, index);
|
|
@@ -4392,16 +4838,16 @@ function relativeMarkdownLink(fromFile, toFile) {
|
|
|
4392
4838
|
}
|
|
4393
4839
|
function createDocumentLinkCandidates(entries, selectedFile, query) {
|
|
4394
4840
|
const normalizedQuery = query.trim().toLowerCase();
|
|
4395
|
-
const selected =
|
|
4841
|
+
const selected = parseWorkspacePath6(selectedFile);
|
|
4396
4842
|
const candidates = [];
|
|
4397
4843
|
for (const entry of entries) {
|
|
4398
|
-
if (entry.kind !== "file" ||
|
|
4844
|
+
if (entry.kind !== "file" || parseWorkspacePath6(entry.path) === selected) continue;
|
|
4399
4845
|
const label = entry.name.replace(/\.md$/iu, "");
|
|
4400
4846
|
const path = relativeMarkdownLink(selectedFile, entry.path);
|
|
4401
4847
|
if (normalizedQuery && !label.toLowerCase().includes(normalizedQuery) && !path.toLowerCase().includes(normalizedQuery)) {
|
|
4402
4848
|
continue;
|
|
4403
4849
|
}
|
|
4404
|
-
const description =
|
|
4850
|
+
const description = dirname2(entry.path);
|
|
4405
4851
|
candidates.push(description ? { path, label, description } : { path, label });
|
|
4406
4852
|
}
|
|
4407
4853
|
candidates.sort((left, right) => left.label.localeCompare(right.label));
|
|
@@ -4465,14 +4911,14 @@ var WorkspaceAuthorityBarrier = class {
|
|
|
4465
4911
|
|
|
4466
4912
|
// src/DocBlocksShell/transient-workspace-move.ts
|
|
4467
4913
|
import {
|
|
4468
|
-
getFileSystemProviderV2 as
|
|
4914
|
+
getFileSystemProviderV2 as getFileSystemProviderV26
|
|
4469
4915
|
} from "@bendyline/docblocks/filesystem";
|
|
4470
4916
|
function pathDepth(path) {
|
|
4471
4917
|
return path ? path.split("/").length : 0;
|
|
4472
4918
|
}
|
|
4473
4919
|
async function copyTransientWorkspaceContents(source, destination) {
|
|
4474
|
-
const sourceV2 =
|
|
4475
|
-
const destinationV2 =
|
|
4920
|
+
const sourceV2 = getFileSystemProviderV26(source);
|
|
4921
|
+
const destinationV2 = getFileSystemProviderV26(destination);
|
|
4476
4922
|
if (!sourceV2 || !destinationV2) {
|
|
4477
4923
|
throw new Error("Moving a temporary document requires a current workspace provider.");
|
|
4478
4924
|
}
|
|
@@ -4542,7 +4988,7 @@ var WELCOME_DOCUMENT_CONTENT = [
|
|
|
4542
4988
|
"squisq-cover-slide: false",
|
|
4543
4989
|
"---",
|
|
4544
4990
|
"",
|
|
4545
|
-
'# DocBlocks
|
|
4991
|
+
'# DocBlocks {[title subtitle="Local-first writing for pages, documents, slideshows, and video"]}',
|
|
4546
4992
|
"",
|
|
4547
4993
|
"Write in plain Markdown. Shape it visually. Keep it portable.",
|
|
4548
4994
|
"",
|
|
@@ -4756,7 +5202,7 @@ function stripExtension(name) {
|
|
|
4756
5202
|
return name.replace(/\.[^.]+$/, "");
|
|
4757
5203
|
}
|
|
4758
5204
|
function normaliseProviderPath(p) {
|
|
4759
|
-
const canonical =
|
|
5205
|
+
const canonical = parseWorkspacePath7(p);
|
|
4760
5206
|
return canonical ? `/${canonical}` : "/";
|
|
4761
5207
|
}
|
|
4762
5208
|
function sameProviderPath(a, b) {
|
|
@@ -4769,32 +5215,32 @@ function relocateProviderPath(path, oldPath, newPath) {
|
|
|
4769
5215
|
return `${newPath.replace(/\/+$/, "")}${current.slice(oldNormalised.length)}`;
|
|
4770
5216
|
}
|
|
4771
5217
|
function pathContains(parentPath2, candidatePath) {
|
|
4772
|
-
return workspacePathContains(
|
|
5218
|
+
return workspacePathContains(parseWorkspacePath7(parentPath2), parseWorkspacePath7(candidatePath));
|
|
4773
5219
|
}
|
|
4774
5220
|
async function readProviderDirectory2(provider, path) {
|
|
4775
|
-
const providerV2 =
|
|
5221
|
+
const providerV2 = getFileSystemProviderV27(provider);
|
|
4776
5222
|
if (!providerV2) return provider.readDirectory(path);
|
|
4777
|
-
const entries = await providerV2.readDirectory(
|
|
5223
|
+
const entries = await providerV2.readDirectory(parseWorkspacePath7(path));
|
|
4778
5224
|
return entries.map((entry) => ({ kind: entry.kind, name: entry.name, path: entry.path }));
|
|
4779
5225
|
}
|
|
4780
5226
|
async function readProviderText(provider, path) {
|
|
4781
|
-
const providerV2 =
|
|
5227
|
+
const providerV2 = getFileSystemProviderV27(provider);
|
|
4782
5228
|
if (!providerV2) return provider.readFile(path);
|
|
4783
|
-
const file = await providerV2.readFile(
|
|
4784
|
-
return file ? decodeUtf8Text(file.data, { label: "The document", path:
|
|
5229
|
+
const file = await providerV2.readFile(parseWorkspacePath7(path));
|
|
5230
|
+
return file ? decodeUtf8Text(file.data, { label: "The document", path: parseWorkspacePath7(path) }) : null;
|
|
4785
5231
|
}
|
|
4786
5232
|
async function pinnedProviderFileExists(provider, path) {
|
|
4787
|
-
const providerV2 =
|
|
5233
|
+
const providerV2 = getFileSystemProviderV27(provider);
|
|
4788
5234
|
if (!providerV2) return provider.exists(path);
|
|
4789
|
-
const entry = await providerV2.stat(
|
|
5235
|
+
const entry = await providerV2.stat(parseWorkspacePath7(path));
|
|
4790
5236
|
if (!entry) return false;
|
|
4791
5237
|
if (entry.kind !== "file") throw new Error("The pinned path is no longer a document.");
|
|
4792
5238
|
return true;
|
|
4793
5239
|
}
|
|
4794
5240
|
async function removePinnedProviderFile(provider, path) {
|
|
4795
|
-
const providerV2 =
|
|
5241
|
+
const providerV2 = getFileSystemProviderV27(provider);
|
|
4796
5242
|
if (providerV2) {
|
|
4797
|
-
const canonical =
|
|
5243
|
+
const canonical = parseWorkspacePath7(path);
|
|
4798
5244
|
const entry = await providerV2.stat(canonical);
|
|
4799
5245
|
if (!entry) return false;
|
|
4800
5246
|
if (entry.kind !== "file") throw new Error("The pinned path is no longer a document.");
|
|
@@ -4810,13 +5256,13 @@ async function removePinnedProviderFile(provider, path) {
|
|
|
4810
5256
|
return true;
|
|
4811
5257
|
}
|
|
4812
5258
|
async function readStableFileSnapshot(provider, path) {
|
|
4813
|
-
const providerV2 =
|
|
5259
|
+
const providerV2 = getFileSystemProviderV27(provider);
|
|
4814
5260
|
if (providerV2) {
|
|
4815
|
-
const read = await providerV2.readFile(
|
|
5261
|
+
const read = await providerV2.readFile(parseWorkspacePath7(path));
|
|
4816
5262
|
return {
|
|
4817
5263
|
content: read ? decodeUtf8Text(read.data, {
|
|
4818
5264
|
label: "The document",
|
|
4819
|
-
path:
|
|
5265
|
+
path: parseWorkspacePath7(path)
|
|
4820
5266
|
}) : null,
|
|
4821
5267
|
version: read?.entry.version ?? null
|
|
4822
5268
|
};
|
|
@@ -4852,7 +5298,7 @@ async function ensureHandleWritePermission(handle) {
|
|
|
4852
5298
|
}
|
|
4853
5299
|
}
|
|
4854
5300
|
async function copyProviderToContainer(src, container, pathPrefix) {
|
|
4855
|
-
const providerV2 =
|
|
5301
|
+
const providerV2 = getFileSystemProviderV27(src);
|
|
4856
5302
|
if (providerV2) {
|
|
4857
5303
|
const snapshot = await providerV2.snapshot();
|
|
4858
5304
|
for (const entry of snapshot.entries) {
|
|
@@ -5099,7 +5545,7 @@ function DocBlocksShell({
|
|
|
5099
5545
|
const [activeWorkspaceDescriptor, setActiveWorkspaceDescriptor] = useState15(null);
|
|
5100
5546
|
useEffect15(() => {
|
|
5101
5547
|
if (!provider || getTransientWorkspace(provider.id)) return;
|
|
5102
|
-
const providerV2 =
|
|
5548
|
+
const providerV2 = getFileSystemProviderV27(provider);
|
|
5103
5549
|
return providerV2 ? retainFileSystemProvider(providerV2) : void 0;
|
|
5104
5550
|
}, [provider]);
|
|
5105
5551
|
const [descriptorRefreshKey, setDescriptorRefreshKey] = useState15(0);
|
|
@@ -5186,7 +5632,7 @@ function DocBlocksShell({
|
|
|
5186
5632
|
void scan();
|
|
5187
5633
|
const scanOnFocus = () => void scan();
|
|
5188
5634
|
window.addEventListener("focus", scanOnFocus);
|
|
5189
|
-
const providerV2 =
|
|
5635
|
+
const providerV2 = getFileSystemProviderV27(provider);
|
|
5190
5636
|
const subscription = providerV2?.capabilities.watch ? providerV2.watch(
|
|
5191
5637
|
(event) => {
|
|
5192
5638
|
if (event.type !== "modified") void scan();
|
|
@@ -5250,6 +5696,28 @@ function DocBlocksShell({
|
|
|
5250
5696
|
[activeWorkspaceDescriptor]
|
|
5251
5697
|
);
|
|
5252
5698
|
const [selectedFile, setSelectedFile] = useState15(null);
|
|
5699
|
+
const [selectedSourceFile, setSelectedSourceFile] = useState15(null);
|
|
5700
|
+
const [selectedOutsideIn, setSelectedOutsideIn] = useState15(null);
|
|
5701
|
+
const [selectedOutsideInEditingEnabled, setSelectedOutsideInEditingEnabled] = useState15(false);
|
|
5702
|
+
const adoptSelectedDocument = useCallback15((document2) => {
|
|
5703
|
+
setSelectedFile(document2?.displayPath ?? null);
|
|
5704
|
+
setSelectedSourceFile(document2?.sourcePath ?? null);
|
|
5705
|
+
setSelectedOutsideIn(document2?.outsideIn ?? null);
|
|
5706
|
+
setSelectedOutsideInEditingEnabled(document2?.outsideInEditingEnabled ?? false);
|
|
5707
|
+
}, []);
|
|
5708
|
+
const adoptRegularDocument = useCallback15((path) => {
|
|
5709
|
+
if (path === null) {
|
|
5710
|
+
setSelectedFile(null);
|
|
5711
|
+
setSelectedSourceFile(null);
|
|
5712
|
+
setSelectedOutsideIn(null);
|
|
5713
|
+
setSelectedOutsideInEditingEnabled(false);
|
|
5714
|
+
return;
|
|
5715
|
+
}
|
|
5716
|
+
setSelectedFile(path);
|
|
5717
|
+
setSelectedSourceFile(path);
|
|
5718
|
+
setSelectedOutsideIn(null);
|
|
5719
|
+
setSelectedOutsideInEditingEnabled(false);
|
|
5720
|
+
}, []);
|
|
5253
5721
|
useDocumentTitle(selectedFile, homeDocumentTitle, homeDocumentPath);
|
|
5254
5722
|
const exportDestinationAdapter = useMemo3(() => {
|
|
5255
5723
|
if (!selectedFile) return void 0;
|
|
@@ -5264,6 +5732,10 @@ function DocBlocksShell({
|
|
|
5264
5732
|
}
|
|
5265
5733
|
return createBrowserSaveAsAdapter();
|
|
5266
5734
|
}, [activeWorkspaceId, selectedFile]);
|
|
5735
|
+
const saveRenderedImageOutput = useMemo3(
|
|
5736
|
+
() => isElectronHost3() && exportDestinationAdapter ? createImageSaveOutput(exportDestinationAdapter) : void 0,
|
|
5737
|
+
[exportDestinationAdapter]
|
|
5738
|
+
);
|
|
5267
5739
|
const [selectedFolder, setSelectedFolder] = useState15(null);
|
|
5268
5740
|
const [folderEntries, setFolderEntries] = useState15([]);
|
|
5269
5741
|
const visibleFolderEntries = useMemo3(
|
|
@@ -5300,9 +5772,12 @@ function DocBlocksShell({
|
|
|
5300
5772
|
const preparedCloseRequestRef = useRef15(null);
|
|
5301
5773
|
const pendingDbkConflictsRef = useRef15(/* @__PURE__ */ new Map());
|
|
5302
5774
|
const createDocumentTarget = useCallback15(
|
|
5303
|
-
(fsProvider, workspaceId, filePath) => {
|
|
5775
|
+
(fsProvider, workspaceId, filePath, outsideIn = null) => {
|
|
5776
|
+
if (outsideIn) {
|
|
5777
|
+
return createOutsideInDocumentTarget(fsProvider, outsideIn, gitScheduleRefresh);
|
|
5778
|
+
}
|
|
5304
5779
|
const transient = getTransientWorkspace(workspaceId);
|
|
5305
|
-
const baseTarget =
|
|
5780
|
+
const baseTarget = createFileSystemDocumentTarget2(fsProvider, filePath);
|
|
5306
5781
|
const originKind = transient?.descriptor.origin?.kind;
|
|
5307
5782
|
const needsElectronHost = originKind === "loose-file" || originKind === "dbk";
|
|
5308
5783
|
if (!transient?.descriptor.origin || needsElectronHost && !isElectronHost3()) {
|
|
@@ -5506,52 +5981,58 @@ function DocBlocksShell({
|
|
|
5506
5981
|
try {
|
|
5507
5982
|
await touchWorkspace2(ws.id);
|
|
5508
5983
|
if (requestId !== navigationRequestRef.current) return null;
|
|
5509
|
-
let
|
|
5510
|
-
let openedContent = "";
|
|
5984
|
+
let openedDocument = null;
|
|
5511
5985
|
const transitioned = await documentSession.transitionWithLoad(async () => {
|
|
5512
5986
|
if (requestId !== navigationRequestRef.current) return null;
|
|
5513
5987
|
if (filePath) {
|
|
5514
|
-
const
|
|
5988
|
+
const document2 = await loadEditableShellDocument(fsProvider, filePath);
|
|
5515
5989
|
if (requestId !== navigationRequestRef.current) return null;
|
|
5516
|
-
if (
|
|
5517
|
-
openedFile = filePath;
|
|
5518
|
-
openedContent = content;
|
|
5519
|
-
}
|
|
5990
|
+
if (document2 !== null) openedDocument = document2;
|
|
5520
5991
|
}
|
|
5521
5992
|
return {
|
|
5522
|
-
target:
|
|
5523
|
-
|
|
5993
|
+
target: openedDocument ? createDocumentTarget(
|
|
5994
|
+
fsProvider,
|
|
5995
|
+
ws.id,
|
|
5996
|
+
openedDocument.sourcePath,
|
|
5997
|
+
openedDocument.outsideIn
|
|
5998
|
+
) : null,
|
|
5999
|
+
content: openedDocument?.content ?? ""
|
|
5524
6000
|
};
|
|
5525
6001
|
});
|
|
5526
6002
|
if (!transitioned) return null;
|
|
6003
|
+
const acceptedDocument = openedDocument;
|
|
5527
6004
|
setProvider(fsProvider);
|
|
5528
6005
|
adopted = true;
|
|
5529
6006
|
setActiveWorkspaceId(ws.id);
|
|
5530
|
-
if (
|
|
5531
|
-
|
|
6007
|
+
if (acceptedDocument) {
|
|
6008
|
+
adoptSelectedDocument(acceptedDocument);
|
|
5532
6009
|
setSelectedFolder(null);
|
|
5533
6010
|
setFolderEntries([]);
|
|
5534
6011
|
const effectiveView = view ?? "wysiwyg";
|
|
5535
6012
|
setInitialView(effectiveView);
|
|
5536
6013
|
setInitialSharedMode(sharedMode);
|
|
5537
6014
|
if (!transient) {
|
|
5538
|
-
saveLastState({
|
|
6015
|
+
saveLastState({
|
|
6016
|
+
workspaceId: wsId,
|
|
6017
|
+
filePath: acceptedDocument.displayPath,
|
|
6018
|
+
view: effectiveView
|
|
6019
|
+
});
|
|
5539
6020
|
}
|
|
5540
6021
|
} else {
|
|
5541
|
-
|
|
6022
|
+
adoptSelectedDocument(null);
|
|
5542
6023
|
setSelectedFolder(null);
|
|
5543
6024
|
setFolderEntries([]);
|
|
5544
6025
|
setInitialSharedMode(null);
|
|
5545
6026
|
}
|
|
5546
6027
|
if (push) {
|
|
5547
|
-
pushHash(ws.id,
|
|
6028
|
+
pushHash(ws.id, acceptedDocument?.displayPath ?? null);
|
|
5548
6029
|
}
|
|
5549
6030
|
return fsProvider;
|
|
5550
6031
|
} finally {
|
|
5551
|
-
if (!transient && !adopted) await
|
|
6032
|
+
if (!transient && !adopted) await getFileSystemProviderV27(fsProvider)?.dispose();
|
|
5552
6033
|
}
|
|
5553
6034
|
},
|
|
5554
|
-
[createDocumentTarget, documentSession, pushHash]
|
|
6035
|
+
[adoptSelectedDocument, createDocumentTarget, documentSession, pushHash]
|
|
5555
6036
|
);
|
|
5556
6037
|
const adoptTransientWorkspace = useCallback15(
|
|
5557
6038
|
async (options) => {
|
|
@@ -5594,7 +6075,7 @@ function DocBlocksShell({
|
|
|
5594
6075
|
});
|
|
5595
6076
|
mem.replaceContents(snapshot);
|
|
5596
6077
|
} catch (error) {
|
|
5597
|
-
await
|
|
6078
|
+
await getFileSystemProviderV27(mem)?.dispose();
|
|
5598
6079
|
throw error;
|
|
5599
6080
|
}
|
|
5600
6081
|
return adoptTransientWorkspace({
|
|
@@ -5622,7 +6103,7 @@ function DocBlocksShell({
|
|
|
5622
6103
|
if (content !== null && isCurrent()) {
|
|
5623
6104
|
await documentSession.transitionTo(createDocumentTarget(fs, fs.id, aboutPath), content);
|
|
5624
6105
|
if (!isCurrent()) return;
|
|
5625
|
-
|
|
6106
|
+
adoptRegularDocument(aboutPath);
|
|
5626
6107
|
setInitialView("preview");
|
|
5627
6108
|
setInitialSharedMode(null);
|
|
5628
6109
|
setExplorerKey((k) => k + 1);
|
|
@@ -5639,7 +6120,7 @@ function DocBlocksShell({
|
|
|
5639
6120
|
try {
|
|
5640
6121
|
await writeProviderText(fs, welcomePath, welcomeContent, "create");
|
|
5641
6122
|
} catch (error) {
|
|
5642
|
-
if (!(error instanceof
|
|
6123
|
+
if (!(error instanceof FsError6 && error.code === "already-exists")) throw error;
|
|
5643
6124
|
const existing = await readProviderText(fs, welcomePath);
|
|
5644
6125
|
if (existing === null) throw error;
|
|
5645
6126
|
seededContent = existing;
|
|
@@ -5650,7 +6131,7 @@ function DocBlocksShell({
|
|
|
5650
6131
|
seededContent
|
|
5651
6132
|
);
|
|
5652
6133
|
if (!isCurrent()) return;
|
|
5653
|
-
|
|
6134
|
+
adoptRegularDocument(welcomePath);
|
|
5654
6135
|
setInitialView("preview");
|
|
5655
6136
|
setInitialSharedMode(null);
|
|
5656
6137
|
setExplorerKey((k) => k + 1);
|
|
@@ -5658,7 +6139,7 @@ function DocBlocksShell({
|
|
|
5658
6139
|
saveLastState({ workspaceId: fs.id, filePath: welcomePath, view: "preview" });
|
|
5659
6140
|
if (!isWelcomeGatewayDismissed()) setShowWelcomeGateway(true);
|
|
5660
6141
|
},
|
|
5661
|
-
[createDocumentTarget, documentSession, pushHash]
|
|
6142
|
+
[adoptRegularDocument, createDocumentTarget, documentSession, pushHash]
|
|
5662
6143
|
);
|
|
5663
6144
|
const startupOpenFromIdsRef = useRef15(openFromIds);
|
|
5664
6145
|
startupOpenFromIdsRef.current = openFromIds;
|
|
@@ -5784,12 +6265,12 @@ function DocBlocksShell({
|
|
|
5784
6265
|
const p = await createElectronProviderFromWorkspace(ws);
|
|
5785
6266
|
if (!p) continue;
|
|
5786
6267
|
if (!isCurrent()) {
|
|
5787
|
-
await
|
|
6268
|
+
await getFileSystemProviderV27(p)?.dispose();
|
|
5788
6269
|
return;
|
|
5789
6270
|
}
|
|
5790
6271
|
await touchWorkspace2(ws.id);
|
|
5791
6272
|
if (!isCurrent()) {
|
|
5792
|
-
await
|
|
6273
|
+
await getFileSystemProviderV27(p)?.dispose();
|
|
5793
6274
|
return;
|
|
5794
6275
|
}
|
|
5795
6276
|
fsProvider = p;
|
|
@@ -5798,12 +6279,12 @@ function DocBlocksShell({
|
|
|
5798
6279
|
const restored = await (await loadNativeFileSystem()).restoreNativeFolder(ws.id);
|
|
5799
6280
|
if (restored) {
|
|
5800
6281
|
if (!isCurrent()) {
|
|
5801
|
-
await
|
|
6282
|
+
await getFileSystemProviderV27(restored)?.dispose();
|
|
5802
6283
|
return;
|
|
5803
6284
|
}
|
|
5804
6285
|
await touchWorkspace2(ws.id);
|
|
5805
6286
|
if (!isCurrent()) {
|
|
5806
|
-
await
|
|
6287
|
+
await getFileSystemProviderV27(restored)?.dispose();
|
|
5807
6288
|
return;
|
|
5808
6289
|
}
|
|
5809
6290
|
fsProvider = restored;
|
|
@@ -5812,12 +6293,12 @@ function DocBlocksShell({
|
|
|
5812
6293
|
} else {
|
|
5813
6294
|
const p = await createIndexedDbFileSystemProvider(ws.id, ws.name);
|
|
5814
6295
|
if (!isCurrent()) {
|
|
5815
|
-
await
|
|
6296
|
+
await getFileSystemProviderV27(p)?.dispose();
|
|
5816
6297
|
return;
|
|
5817
6298
|
}
|
|
5818
6299
|
await touchWorkspace2(ws.id);
|
|
5819
6300
|
if (!isCurrent()) {
|
|
5820
|
-
await
|
|
6301
|
+
await getFileSystemProviderV27(p)?.dispose();
|
|
5821
6302
|
return;
|
|
5822
6303
|
}
|
|
5823
6304
|
fsProvider = p;
|
|
@@ -5839,7 +6320,7 @@ function DocBlocksShell({
|
|
|
5839
6320
|
if (!isCurrent()) return;
|
|
5840
6321
|
const p = await createElectronFileSystemProvider(info.id, info.name, info.rootPath);
|
|
5841
6322
|
if (!isCurrent()) {
|
|
5842
|
-
await
|
|
6323
|
+
await getFileSystemProviderV27(p)?.dispose();
|
|
5843
6324
|
return;
|
|
5844
6325
|
}
|
|
5845
6326
|
fsProvider = p;
|
|
@@ -5848,7 +6329,7 @@ function DocBlocksShell({
|
|
|
5848
6329
|
if (!isCurrent()) return;
|
|
5849
6330
|
const p = await createIndexedDbFileSystemProvider(defaultWs.id, defaultWs.name);
|
|
5850
6331
|
if (!isCurrent()) {
|
|
5851
|
-
await
|
|
6332
|
+
await getFileSystemProviderV27(p)?.dispose();
|
|
5852
6333
|
return;
|
|
5853
6334
|
}
|
|
5854
6335
|
fsProvider = p;
|
|
@@ -5864,7 +6345,7 @@ function DocBlocksShell({
|
|
|
5864
6345
|
setActiveWorkspaceId(fsProvider.id);
|
|
5865
6346
|
adopted = true;
|
|
5866
6347
|
} finally {
|
|
5867
|
-
if (!adopted) await
|
|
6348
|
+
if (!adopted) await getFileSystemProviderV27(fsProvider)?.dispose();
|
|
5868
6349
|
}
|
|
5869
6350
|
})().catch(() => {
|
|
5870
6351
|
if (isCurrent()) {
|
|
@@ -6047,12 +6528,21 @@ function DocBlocksShell({
|
|
|
6047
6528
|
setVersionsContainer(null);
|
|
6048
6529
|
return;
|
|
6049
6530
|
}
|
|
6531
|
+
if (selectedOutsideIn) {
|
|
6532
|
+
const container2 = createOutsideInContentContainer(provider, selectedOutsideIn);
|
|
6533
|
+
const mp2 = createMediaProviderFromContainer(container2);
|
|
6534
|
+
mediaContainerRef.current = container2;
|
|
6535
|
+
versionsContainerRef.current = container2;
|
|
6536
|
+
setMediaProvider(mp2);
|
|
6537
|
+
setVersionsContainer(container2);
|
|
6538
|
+
return () => mp2.dispose();
|
|
6539
|
+
}
|
|
6050
6540
|
const parentDir = dirnameOf(selectedFile);
|
|
6051
6541
|
const base = basenameOf(selectedFile);
|
|
6052
6542
|
const baseNoExt = base.replace(/\.[^.]+$/, "");
|
|
6053
|
-
const container = new
|
|
6543
|
+
const container = new FileSystemContentContainer2(provider, parentDir);
|
|
6054
6544
|
const vPrefix = parentDir ? `${parentDir}/${baseNoExt}_files` : `${baseNoExt}_files`;
|
|
6055
|
-
const vContainer = new
|
|
6545
|
+
const vContainer = new FileSystemContentContainer2(provider, vPrefix);
|
|
6056
6546
|
const mp = createFileMediaProvider(container, base);
|
|
6057
6547
|
mediaContainerRef.current = container;
|
|
6058
6548
|
versionsContainerRef.current = vContainer;
|
|
@@ -6061,8 +6551,12 @@ function DocBlocksShell({
|
|
|
6061
6551
|
return () => {
|
|
6062
6552
|
mp.dispose();
|
|
6063
6553
|
};
|
|
6064
|
-
}, [provider, selectedFile, mediaEpoch]);
|
|
6065
|
-
const documentLinkProvider = useDocumentLinkProvider(
|
|
6554
|
+
}, [provider, selectedFile, selectedOutsideIn, mediaEpoch]);
|
|
6555
|
+
const documentLinkProvider = useDocumentLinkProvider(
|
|
6556
|
+
provider,
|
|
6557
|
+
selectedSourceFile ?? selectedFile,
|
|
6558
|
+
documentLinkEpoch
|
|
6559
|
+
);
|
|
6066
6560
|
useEffect15(() => {
|
|
6067
6561
|
const ref = versioningRef;
|
|
6068
6562
|
if (!ref) return;
|
|
@@ -6083,10 +6577,10 @@ function DocBlocksShell({
|
|
|
6083
6577
|
}, [versioningRef, effectiveVersioning, versionBasename, selectedFile, versionsContainer]);
|
|
6084
6578
|
useEffect15(() => {
|
|
6085
6579
|
if (!provider) return;
|
|
6086
|
-
const providerV2 =
|
|
6580
|
+
const providerV2 = getFileSystemProviderV27(provider);
|
|
6087
6581
|
if (!providerV2?.capabilities.watch) return;
|
|
6088
|
-
|
|
6089
|
-
|
|
6582
|
+
const watchedFile = selectedSourceFile ?? selectedFile;
|
|
6583
|
+
if (!watchedFile || !documentSnapshot.targetKey) return;
|
|
6090
6584
|
const targetKey = documentSnapshot.targetKey;
|
|
6091
6585
|
let disposed = false;
|
|
6092
6586
|
let reading = false;
|
|
@@ -6140,7 +6634,7 @@ function DocBlocksShell({
|
|
|
6140
6634
|
disposed = true;
|
|
6141
6635
|
void subscription.dispose();
|
|
6142
6636
|
};
|
|
6143
|
-
}, [provider, selectedFile, documentSession, documentSnapshot.targetKey]);
|
|
6637
|
+
}, [provider, selectedFile, selectedSourceFile, documentSession, documentSnapshot.targetKey]);
|
|
6144
6638
|
const transitionAwayFromDocument = useCallback15(
|
|
6145
6639
|
async (requestId) => {
|
|
6146
6640
|
if (requestId !== navigationRequestRef.current) return false;
|
|
@@ -6173,7 +6667,7 @@ function DocBlocksShell({
|
|
|
6173
6667
|
}
|
|
6174
6668
|
const snapshot = await documentSession.resolveConflict("use-external");
|
|
6175
6669
|
if (!snapshot.targetKey) {
|
|
6176
|
-
|
|
6670
|
+
adoptSelectedDocument(null);
|
|
6177
6671
|
if (activeWorkspaceId) pushHash(activeWorkspaceId, null);
|
|
6178
6672
|
}
|
|
6179
6673
|
} catch (error) {
|
|
@@ -6182,7 +6676,7 @@ function DocBlocksShell({
|
|
|
6182
6676
|
message: error instanceof Error ? error.message : "Could not reload the external document."
|
|
6183
6677
|
});
|
|
6184
6678
|
}
|
|
6185
|
-
}, [activeWorkspaceId, documentSession, pushHash]);
|
|
6679
|
+
}, [activeWorkspaceId, adoptSelectedDocument, documentSession, pushHash]);
|
|
6186
6680
|
const handleKeepLocalDocument = useCallback15(async () => {
|
|
6187
6681
|
const conflictKey = documentSession.getSnapshot().conflict?.targetKey;
|
|
6188
6682
|
try {
|
|
@@ -6274,12 +6768,12 @@ function DocBlocksShell({
|
|
|
6274
6768
|
if (!await transitionAwayFromDocument(requestId)) return;
|
|
6275
6769
|
setProvider(nextProvider);
|
|
6276
6770
|
setActiveWorkspaceId(ws.id);
|
|
6277
|
-
|
|
6771
|
+
adoptSelectedDocument(null);
|
|
6278
6772
|
setSelectedFolder(null);
|
|
6279
6773
|
setFolderEntries([]);
|
|
6280
6774
|
pushHash(ws.id, null);
|
|
6281
6775
|
},
|
|
6282
|
-
[pushHash, transitionAwayFromDocument]
|
|
6776
|
+
[adoptSelectedDocument, pushHash, transitionAwayFromDocument]
|
|
6283
6777
|
);
|
|
6284
6778
|
const unpinDocument = useCallback15((document2) => {
|
|
6285
6779
|
const key = pinnedDocumentKey(document2);
|
|
@@ -6307,7 +6801,7 @@ function DocBlocksShell({
|
|
|
6307
6801
|
const handleTogglePin = useCallback15(
|
|
6308
6802
|
(path) => {
|
|
6309
6803
|
if (!activeWorkspaceId || !provider) return;
|
|
6310
|
-
const canonicalPath =
|
|
6804
|
+
const canonicalPath = parseWorkspacePath7(path);
|
|
6311
6805
|
if (!canonicalPath) return;
|
|
6312
6806
|
const document2 = {
|
|
6313
6807
|
workspaceId: activeWorkspaceId,
|
|
@@ -6397,17 +6891,23 @@ function DocBlocksShell({
|
|
|
6397
6891
|
return;
|
|
6398
6892
|
}
|
|
6399
6893
|
let disappearedDuringRead = false;
|
|
6894
|
+
let openedDocument = null;
|
|
6400
6895
|
const transitioned = await documentSession.transitionWithLoad(async () => {
|
|
6401
6896
|
if (requestId !== navigationRequestRef.current) return null;
|
|
6402
|
-
|
|
6403
|
-
if (
|
|
6897
|
+
openedDocument = await loadEditableShellDocument(openedProvider, document2.path);
|
|
6898
|
+
if (openedDocument === null) {
|
|
6404
6899
|
disappearedDuringRead = true;
|
|
6405
6900
|
return null;
|
|
6406
6901
|
}
|
|
6407
6902
|
if (requestId !== navigationRequestRef.current) return null;
|
|
6408
6903
|
return {
|
|
6409
|
-
target: createDocumentTarget(
|
|
6410
|
-
|
|
6904
|
+
target: createDocumentTarget(
|
|
6905
|
+
openedProvider,
|
|
6906
|
+
workspace.id,
|
|
6907
|
+
openedDocument.sourcePath,
|
|
6908
|
+
openedDocument.outsideIn
|
|
6909
|
+
),
|
|
6910
|
+
content: openedDocument.content
|
|
6411
6911
|
};
|
|
6412
6912
|
});
|
|
6413
6913
|
if (!transitioned) {
|
|
@@ -6419,7 +6919,8 @@ function DocBlocksShell({
|
|
|
6419
6919
|
setProvider(openedProvider);
|
|
6420
6920
|
setActiveWorkspaceId(workspace.id);
|
|
6421
6921
|
setActiveWorkspaceDescriptor(workspace);
|
|
6422
|
-
|
|
6922
|
+
if (!openedDocument) return;
|
|
6923
|
+
adoptSelectedDocument(openedDocument);
|
|
6423
6924
|
setSelectedFolder(null);
|
|
6424
6925
|
setFolderEntries([]);
|
|
6425
6926
|
setInitialView("wysiwyg");
|
|
@@ -6439,7 +6940,7 @@ function DocBlocksShell({
|
|
|
6439
6940
|
} finally {
|
|
6440
6941
|
if (nextProvider && ownsNextProvider && !adoptedNextProvider) {
|
|
6441
6942
|
try {
|
|
6442
|
-
await
|
|
6943
|
+
await getFileSystemProviderV27(nextProvider)?.dispose();
|
|
6443
6944
|
} catch {
|
|
6444
6945
|
}
|
|
6445
6946
|
}
|
|
@@ -6447,6 +6948,7 @@ function DocBlocksShell({
|
|
|
6447
6948
|
},
|
|
6448
6949
|
[
|
|
6449
6950
|
activeWorkspaceId,
|
|
6951
|
+
adoptSelectedDocument,
|
|
6450
6952
|
closeWelcomeGateway,
|
|
6451
6953
|
confirmMissingPinnedDocument,
|
|
6452
6954
|
createDocumentTarget,
|
|
@@ -6513,7 +7015,7 @@ function DocBlocksShell({
|
|
|
6513
7015
|
setProvider(openedDestination);
|
|
6514
7016
|
setActiveWorkspaceId(destination.id);
|
|
6515
7017
|
setActiveWorkspaceDescriptor(destination);
|
|
6516
|
-
|
|
7018
|
+
adoptRegularDocument(selectedFile);
|
|
6517
7019
|
setPinnedDocuments(
|
|
6518
7020
|
(current) => movePinnedDocumentsToWorkspace(current, activeWorkspaceId, {
|
|
6519
7021
|
workspaceId: destination.id,
|
|
@@ -6550,7 +7052,7 @@ function DocBlocksShell({
|
|
|
6550
7052
|
} catch (error) {
|
|
6551
7053
|
if (destinationProvider && !adoptedDestination) {
|
|
6552
7054
|
try {
|
|
6553
|
-
await
|
|
7055
|
+
await getFileSystemProviderV27(destinationProvider)?.dispose();
|
|
6554
7056
|
} catch {
|
|
6555
7057
|
}
|
|
6556
7058
|
}
|
|
@@ -6559,6 +7061,7 @@ function DocBlocksShell({
|
|
|
6559
7061
|
},
|
|
6560
7062
|
[
|
|
6561
7063
|
activeWorkspaceId,
|
|
7064
|
+
adoptRegularDocument,
|
|
6562
7065
|
closeWelcomeGateway,
|
|
6563
7066
|
createDocumentTarget,
|
|
6564
7067
|
documentSession,
|
|
@@ -6590,7 +7093,7 @@ function DocBlocksShell({
|
|
|
6590
7093
|
if (!await transitionAwayFromDocument(requestId)) return;
|
|
6591
7094
|
setProvider(provider2);
|
|
6592
7095
|
setActiveWorkspaceId(descriptor2.id);
|
|
6593
|
-
|
|
7096
|
+
adoptSelectedDocument(null);
|
|
6594
7097
|
setSelectedFolder(null);
|
|
6595
7098
|
setFolderEntries([]);
|
|
6596
7099
|
pushHash(descriptor2.id, null);
|
|
@@ -6609,13 +7112,13 @@ function DocBlocksShell({
|
|
|
6609
7112
|
if (!await transitionAwayFromDocument(requestId)) return;
|
|
6610
7113
|
setProvider(nativeProvider);
|
|
6611
7114
|
setActiveWorkspaceId(descriptor.id);
|
|
6612
|
-
|
|
7115
|
+
adoptSelectedDocument(null);
|
|
6613
7116
|
setSelectedFolder(null);
|
|
6614
7117
|
setFolderEntries([]);
|
|
6615
7118
|
pushHash(descriptor.id, null);
|
|
6616
7119
|
} catch {
|
|
6617
7120
|
}
|
|
6618
|
-
}, [pushHash, transitionAwayFromDocument]);
|
|
7121
|
+
}, [adoptSelectedDocument, pushHash, transitionAwayFromDocument]);
|
|
6619
7122
|
const handleWorkspaceCloned = useCallback15(
|
|
6620
7123
|
(info) => {
|
|
6621
7124
|
const requestId = ++navigationRequestRef.current;
|
|
@@ -6637,13 +7140,13 @@ function DocBlocksShell({
|
|
|
6637
7140
|
if (!await transitionAwayFromDocument(requestId)) return;
|
|
6638
7141
|
setProvider(cloneProvider);
|
|
6639
7142
|
setActiveWorkspaceId(descriptor.id);
|
|
6640
|
-
|
|
7143
|
+
adoptSelectedDocument(null);
|
|
6641
7144
|
setSelectedFolder(null);
|
|
6642
7145
|
setFolderEntries([]);
|
|
6643
7146
|
pushHash(descriptor.id, null);
|
|
6644
7147
|
})();
|
|
6645
7148
|
},
|
|
6646
|
-
[pushHash, transitionAwayFromDocument]
|
|
7149
|
+
[adoptSelectedDocument, pushHash, transitionAwayFromDocument]
|
|
6647
7150
|
);
|
|
6648
7151
|
const handleNewFile = useCallback15(async () => {
|
|
6649
7152
|
if (!provider) return;
|
|
@@ -6661,12 +7164,12 @@ function DocBlocksShell({
|
|
|
6661
7164
|
try {
|
|
6662
7165
|
const transitioned = await documentSession.transitionWithLoad(async () => {
|
|
6663
7166
|
if (requestId !== navigationRequestRef.current) return null;
|
|
6664
|
-
const providerV2 =
|
|
7167
|
+
const providerV2 = getFileSystemProviderV27(provider);
|
|
6665
7168
|
if (providerV2) {
|
|
6666
7169
|
try {
|
|
6667
7170
|
await writeProviderText(provider, path, content, "create");
|
|
6668
7171
|
} catch (error) {
|
|
6669
|
-
if (error instanceof
|
|
7172
|
+
if (error instanceof FsError6 && error.code === "already-exists") {
|
|
6670
7173
|
throw new Error("A document with that name already exists.");
|
|
6671
7174
|
}
|
|
6672
7175
|
throw error;
|
|
@@ -6693,7 +7196,7 @@ function DocBlocksShell({
|
|
|
6693
7196
|
showToast("error", error instanceof Error ? error.message : "Could not create the document.");
|
|
6694
7197
|
return;
|
|
6695
7198
|
}
|
|
6696
|
-
|
|
7199
|
+
adoptRegularDocument(path);
|
|
6697
7200
|
setInitialView("wysiwyg");
|
|
6698
7201
|
setInitialSharedMode(null);
|
|
6699
7202
|
setExplorerKey((k) => k + 1);
|
|
@@ -6703,6 +7206,7 @@ function DocBlocksShell({
|
|
|
6703
7206
|
}
|
|
6704
7207
|
}, [
|
|
6705
7208
|
provider,
|
|
7209
|
+
adoptRegularDocument,
|
|
6706
7210
|
activeWorkspaceId,
|
|
6707
7211
|
pushHash,
|
|
6708
7212
|
closeWelcomeGateway,
|
|
@@ -6727,9 +7231,9 @@ function DocBlocksShell({
|
|
|
6727
7231
|
}
|
|
6728
7232
|
const path = `/${folderName}`;
|
|
6729
7233
|
try {
|
|
6730
|
-
const providerV2 =
|
|
7234
|
+
const providerV2 = getFileSystemProviderV27(provider);
|
|
6731
7235
|
if (providerV2) {
|
|
6732
|
-
await providerV2.createDirectory(
|
|
7236
|
+
await providerV2.createDirectory(parseWorkspacePath7(path), { mode: "create" });
|
|
6733
7237
|
} else {
|
|
6734
7238
|
if (await providerEntryExists(provider, path)) {
|
|
6735
7239
|
throw new Error("A file or folder with that name already exists.");
|
|
@@ -6737,11 +7241,11 @@ function DocBlocksShell({
|
|
|
6737
7241
|
await provider.createDirectory(path);
|
|
6738
7242
|
}
|
|
6739
7243
|
} catch (error) {
|
|
6740
|
-
const message = error instanceof
|
|
7244
|
+
const message = error instanceof FsError6 && error.code === "already-exists" ? "A file or folder with that name already exists." : error instanceof Error ? error.message : "Could not create the folder.";
|
|
6741
7245
|
showToast("error", message);
|
|
6742
7246
|
return;
|
|
6743
7247
|
}
|
|
6744
|
-
|
|
7248
|
+
adoptSelectedDocument(null);
|
|
6745
7249
|
setSelectedFolder(path);
|
|
6746
7250
|
setFolderEntries([]);
|
|
6747
7251
|
setExplorerKey((key) => key + 1);
|
|
@@ -6750,6 +7254,7 @@ function DocBlocksShell({
|
|
|
6750
7254
|
if (effectiveCompact) setMobileShowEditor(true);
|
|
6751
7255
|
}, [
|
|
6752
7256
|
provider,
|
|
7257
|
+
adoptSelectedDocument,
|
|
6753
7258
|
promptForText,
|
|
6754
7259
|
showToast,
|
|
6755
7260
|
closeWelcomeGateway,
|
|
@@ -6851,22 +7356,27 @@ function DocBlocksShell({
|
|
|
6851
7356
|
if (kind === "directory") {
|
|
6852
7357
|
if (!await transitionAwayFromDocument(requestId)) return;
|
|
6853
7358
|
if (requestId !== navigationRequestRef.current) return;
|
|
6854
|
-
|
|
7359
|
+
adoptSelectedDocument(null);
|
|
6855
7360
|
setSelectedFolder(path);
|
|
6856
7361
|
const entries = await readProviderDirectory2(provider, path);
|
|
6857
7362
|
if (requestId !== navigationRequestRef.current) return;
|
|
6858
7363
|
setFolderEntries(entries);
|
|
6859
7364
|
pushHash(activeWorkspaceId, null);
|
|
6860
7365
|
} else {
|
|
6861
|
-
let
|
|
7366
|
+
let openedDocument = null;
|
|
6862
7367
|
try {
|
|
6863
7368
|
const transitioned = await documentSession.transitionWithLoad(async () => {
|
|
6864
7369
|
if (requestId !== navigationRequestRef.current) return null;
|
|
6865
|
-
|
|
6866
|
-
if (requestId !== navigationRequestRef.current ||
|
|
7370
|
+
openedDocument = await loadEditableShellDocument(provider, path);
|
|
7371
|
+
if (requestId !== navigationRequestRef.current || openedDocument === null) return null;
|
|
6867
7372
|
return {
|
|
6868
|
-
target: createDocumentTarget(
|
|
6869
|
-
|
|
7373
|
+
target: createDocumentTarget(
|
|
7374
|
+
provider,
|
|
7375
|
+
activeWorkspaceId,
|
|
7376
|
+
openedDocument.sourcePath,
|
|
7377
|
+
openedDocument.outsideIn
|
|
7378
|
+
),
|
|
7379
|
+
content: openedDocument.content
|
|
6870
7380
|
};
|
|
6871
7381
|
});
|
|
6872
7382
|
if (!transitioned) return;
|
|
@@ -6878,7 +7388,8 @@ function DocBlocksShell({
|
|
|
6878
7388
|
return;
|
|
6879
7389
|
}
|
|
6880
7390
|
if (requestId !== navigationRequestRef.current) return;
|
|
6881
|
-
|
|
7391
|
+
if (!openedDocument) return;
|
|
7392
|
+
adoptSelectedDocument(openedDocument);
|
|
6882
7393
|
setSelectedFolder(null);
|
|
6883
7394
|
setFolderEntries([]);
|
|
6884
7395
|
setInitialView("wysiwyg");
|
|
@@ -6895,15 +7406,84 @@ function DocBlocksShell({
|
|
|
6895
7406
|
pushHash,
|
|
6896
7407
|
effectiveCompact,
|
|
6897
7408
|
closeWelcomeGateway,
|
|
7409
|
+
adoptSelectedDocument,
|
|
6898
7410
|
createDocumentTarget,
|
|
6899
7411
|
documentSession,
|
|
6900
7412
|
transitionAwayFromDocument,
|
|
6901
7413
|
showToast
|
|
6902
7414
|
]
|
|
6903
7415
|
);
|
|
7416
|
+
const handleEnableOutsideInEditing = useCallback15(
|
|
7417
|
+
async (path) => {
|
|
7418
|
+
if (!provider || !activeWorkspaceId) {
|
|
7419
|
+
throw new Error("Open the workspace before enabling Markdown editing.");
|
|
7420
|
+
}
|
|
7421
|
+
const requestId = ++navigationRequestRef.current;
|
|
7422
|
+
const editableRef = { current: null };
|
|
7423
|
+
const transitioned = await documentSession.transitionWithLoad(async () => {
|
|
7424
|
+
if (requestId !== navigationRequestRef.current) return null;
|
|
7425
|
+
const opened = await loadEditableShellDocument(provider, path);
|
|
7426
|
+
if (!opened?.outsideIn) {
|
|
7427
|
+
throw new Error("This file does not support outside-in Markdown editing.");
|
|
7428
|
+
}
|
|
7429
|
+
const editable2 = await enableOutsideInMarkdownEditing(provider, opened);
|
|
7430
|
+
editableRef.current = editable2;
|
|
7431
|
+
return {
|
|
7432
|
+
target: createDocumentTarget(
|
|
7433
|
+
provider,
|
|
7434
|
+
activeWorkspaceId,
|
|
7435
|
+
editable2.sourcePath,
|
|
7436
|
+
editable2.outsideIn
|
|
7437
|
+
),
|
|
7438
|
+
content: editable2.content
|
|
7439
|
+
};
|
|
7440
|
+
});
|
|
7441
|
+
const editable = editableRef.current;
|
|
7442
|
+
if (!transitioned || !editable || requestId !== navigationRequestRef.current) return;
|
|
7443
|
+
adoptSelectedDocument(editable);
|
|
7444
|
+
setSelectedFolder(null);
|
|
7445
|
+
setFolderEntries([]);
|
|
7446
|
+
setInitialView("wysiwyg");
|
|
7447
|
+
setInitialSharedMode(null);
|
|
7448
|
+
closeWelcomeGateway();
|
|
7449
|
+
pushHash(activeWorkspaceId, path);
|
|
7450
|
+
saveLastState({ workspaceId: activeWorkspaceId, filePath: path, view: "wysiwyg" });
|
|
7451
|
+
if (effectiveCompact) setMobileShowEditor(true);
|
|
7452
|
+
showToast(
|
|
7453
|
+
"success",
|
|
7454
|
+
`Markdown editing enabled. The original is backed up at ${editable.outsideIn.backupPath}.`
|
|
7455
|
+
);
|
|
7456
|
+
},
|
|
7457
|
+
[
|
|
7458
|
+
activeWorkspaceId,
|
|
7459
|
+
adoptSelectedDocument,
|
|
7460
|
+
closeWelcomeGateway,
|
|
7461
|
+
createDocumentTarget,
|
|
7462
|
+
documentSession,
|
|
7463
|
+
effectiveCompact,
|
|
7464
|
+
provider,
|
|
7465
|
+
pushHash,
|
|
7466
|
+
showToast
|
|
7467
|
+
]
|
|
7468
|
+
);
|
|
7469
|
+
const outsideInActionsForEntry = useCallback15(
|
|
7470
|
+
(entry) => {
|
|
7471
|
+
if (entry.kind !== "file" || resolveOutsideInLayout(entry.path) === null) return [];
|
|
7472
|
+
if (selectedOutsideInEditingEnabled && selectedFile !== null && sameProviderPath(selectedFile, entry.path)) {
|
|
7473
|
+
return [];
|
|
7474
|
+
}
|
|
7475
|
+
return [
|
|
7476
|
+
{
|
|
7477
|
+
label: "Allow editing via markdown",
|
|
7478
|
+
onSelect: () => handleEnableOutsideInEditing(entry.path)
|
|
7479
|
+
}
|
|
7480
|
+
];
|
|
7481
|
+
},
|
|
7482
|
+
[handleEnableOutsideInEditing, selectedFile, selectedOutsideInEditingEnabled]
|
|
7483
|
+
);
|
|
6904
7484
|
const handleEditorLinkClick = useCallback15(
|
|
6905
7485
|
(href) => {
|
|
6906
|
-
const target = resolveShellEditorLinkTarget(href, selectedFile);
|
|
7486
|
+
const target = resolveShellEditorLinkTarget(href, selectedSourceFile ?? selectedFile);
|
|
6907
7487
|
if (!target) {
|
|
6908
7488
|
showToast(
|
|
6909
7489
|
"error",
|
|
@@ -6949,31 +7529,93 @@ function DocBlocksShell({
|
|
|
6949
7529
|
})();
|
|
6950
7530
|
return true;
|
|
6951
7531
|
},
|
|
6952
|
-
[handleSelect, provider, selectedFile, showToast]
|
|
7532
|
+
[handleSelect, provider, selectedFile, selectedSourceFile, showToast]
|
|
6953
7533
|
);
|
|
6954
7534
|
const handleTreeMutation = useCallback15(
|
|
6955
7535
|
async (change, mutate) => {
|
|
6956
|
-
if (!provider
|
|
7536
|
+
if (!provider) {
|
|
6957
7537
|
await mutate();
|
|
6958
7538
|
return;
|
|
6959
7539
|
}
|
|
7540
|
+
let mutateDocument = mutate;
|
|
7541
|
+
if (change.kind === "file") {
|
|
7542
|
+
const oldLayout = resolveOutsideInLayout(
|
|
7543
|
+
change.type === "move" ? change.oldPath : change.path
|
|
7544
|
+
);
|
|
7545
|
+
if (oldLayout && change.type === "move") {
|
|
7546
|
+
const nextLayout = resolveOutsideInLayout(change.newPath);
|
|
7547
|
+
if (!nextLayout || nextLayout.format !== oldLayout.format) {
|
|
7548
|
+
throw new Error(
|
|
7549
|
+
`Keep the .${oldLayout.format} extension when renaming this outside-in document.`
|
|
7550
|
+
);
|
|
7551
|
+
}
|
|
7552
|
+
}
|
|
7553
|
+
if (oldLayout && change.type === "delete") {
|
|
7554
|
+
mutateDocument = async () => {
|
|
7555
|
+
await mutate();
|
|
7556
|
+
try {
|
|
7557
|
+
await removeOutsideInCompanion(provider, oldLayout);
|
|
7558
|
+
} catch (error) {
|
|
7559
|
+
showToast(
|
|
7560
|
+
"error",
|
|
7561
|
+
error instanceof Error ? `The rendered file was deleted, but its companion could not be removed: ${error.message}` : "The rendered file was deleted, but its companion could not be removed."
|
|
7562
|
+
);
|
|
7563
|
+
}
|
|
7564
|
+
};
|
|
7565
|
+
}
|
|
7566
|
+
}
|
|
7567
|
+
if (!activeWorkspaceId || !selectedFile) {
|
|
7568
|
+
await mutateDocument();
|
|
7569
|
+
return;
|
|
7570
|
+
}
|
|
6960
7571
|
if (change.type === "move") {
|
|
6961
7572
|
const nextFile = relocateProviderPath(selectedFile, change.oldPath, change.newPath);
|
|
6962
7573
|
if (nextFile !== selectedFile) {
|
|
6963
|
-
|
|
6964
|
-
|
|
6965
|
-
|
|
7574
|
+
const nextSource = relocateProviderPath(
|
|
7575
|
+
selectedSourceFile ?? selectedFile,
|
|
7576
|
+
change.oldPath,
|
|
7577
|
+
change.newPath
|
|
7578
|
+
);
|
|
7579
|
+
let nextOutsideIn = null;
|
|
7580
|
+
if (selectedOutsideIn) {
|
|
7581
|
+
const resolved = resolveOutsideInLayout(nextFile);
|
|
7582
|
+
if (!resolved)
|
|
7583
|
+
throw new Error("The outside-in target must keep a supported extension.");
|
|
7584
|
+
nextOutsideIn = { ...resolved, markdownPath: nextSource };
|
|
7585
|
+
}
|
|
7586
|
+
const snapshot = await documentSession.retarget(
|
|
7587
|
+
createDocumentTarget(provider, activeWorkspaceId, nextSource, nextOutsideIn),
|
|
7588
|
+
mutateDocument
|
|
6966
7589
|
);
|
|
7590
|
+
if (nextOutsideIn) {
|
|
7591
|
+
const linkedContent = await withOutsideInMetadata(snapshot.content, nextOutsideIn);
|
|
7592
|
+
if (linkedContent !== snapshot.content && snapshot.targetKey) {
|
|
7593
|
+
documentSession.edit(linkedContent, {
|
|
7594
|
+
targetKey: snapshot.targetKey,
|
|
7595
|
+
generation: snapshot.generation
|
|
7596
|
+
});
|
|
7597
|
+
await documentSession.flush("transition");
|
|
7598
|
+
}
|
|
7599
|
+
}
|
|
6967
7600
|
return;
|
|
6968
7601
|
}
|
|
6969
7602
|
}
|
|
6970
7603
|
if (change.type === "delete" && pathContains(change.path, selectedFile)) {
|
|
6971
|
-
await documentSession.delete(
|
|
7604
|
+
await documentSession.delete(mutateDocument);
|
|
6972
7605
|
return;
|
|
6973
7606
|
}
|
|
6974
|
-
await
|
|
7607
|
+
await mutateDocument();
|
|
6975
7608
|
},
|
|
6976
|
-
[
|
|
7609
|
+
[
|
|
7610
|
+
provider,
|
|
7611
|
+
activeWorkspaceId,
|
|
7612
|
+
selectedFile,
|
|
7613
|
+
selectedSourceFile,
|
|
7614
|
+
selectedOutsideIn,
|
|
7615
|
+
documentSession,
|
|
7616
|
+
createDocumentTarget,
|
|
7617
|
+
showToast
|
|
7618
|
+
]
|
|
6977
7619
|
);
|
|
6978
7620
|
const handleTreeChange = useCallback15(
|
|
6979
7621
|
async (change) => {
|
|
@@ -6988,7 +7630,12 @@ function DocBlocksShell({
|
|
|
6988
7630
|
const nextFile = selectedFile ? relocateProviderPath(selectedFile, change.oldPath, change.newPath) : null;
|
|
6989
7631
|
const nextFolder = selectedFolder ? relocateProviderPath(selectedFolder, change.oldPath, change.newPath) : null;
|
|
6990
7632
|
if (nextFile !== selectedFile) {
|
|
6991
|
-
|
|
7633
|
+
if (nextFile && selectedOutsideIn) {
|
|
7634
|
+
const opened = await loadEditableShellDocument(provider, nextFile);
|
|
7635
|
+
adoptSelectedDocument(opened);
|
|
7636
|
+
} else {
|
|
7637
|
+
adoptRegularDocument(nextFile);
|
|
7638
|
+
}
|
|
6992
7639
|
if (activeWorkspaceId) {
|
|
6993
7640
|
pushHash(activeWorkspaceId, nextFile);
|
|
6994
7641
|
if (nextFile) {
|
|
@@ -7014,7 +7661,7 @@ function DocBlocksShell({
|
|
|
7014
7661
|
if (selectedFile) {
|
|
7015
7662
|
const exists = await providerEntryExists(provider, selectedFile);
|
|
7016
7663
|
if (!exists) {
|
|
7017
|
-
|
|
7664
|
+
adoptSelectedDocument(null);
|
|
7018
7665
|
if (activeWorkspaceId) pushHash(activeWorkspaceId, null);
|
|
7019
7666
|
}
|
|
7020
7667
|
}
|
|
@@ -7025,7 +7672,10 @@ function DocBlocksShell({
|
|
|
7025
7672
|
},
|
|
7026
7673
|
[
|
|
7027
7674
|
provider,
|
|
7675
|
+
adoptRegularDocument,
|
|
7676
|
+
adoptSelectedDocument,
|
|
7028
7677
|
selectedFile,
|
|
7678
|
+
selectedOutsideIn,
|
|
7029
7679
|
selectedFolder,
|
|
7030
7680
|
activeWorkspaceId,
|
|
7031
7681
|
pinnedDocuments,
|
|
@@ -7097,7 +7747,7 @@ function DocBlocksShell({
|
|
|
7097
7747
|
const parent = dirnameOf(document2.path);
|
|
7098
7748
|
let newPath;
|
|
7099
7749
|
try {
|
|
7100
|
-
newPath =
|
|
7750
|
+
newPath = parseWorkspacePath7(parent ? `${parent}/${newName}` : newName);
|
|
7101
7751
|
} catch {
|
|
7102
7752
|
showToast("error", "Use a valid document name.");
|
|
7103
7753
|
return;
|
|
@@ -7139,7 +7789,7 @@ function DocBlocksShell({
|
|
|
7139
7789
|
} finally {
|
|
7140
7790
|
if (access.owned) {
|
|
7141
7791
|
try {
|
|
7142
|
-
await
|
|
7792
|
+
await getFileSystemProviderV27(access.provider)?.dispose();
|
|
7143
7793
|
} catch {
|
|
7144
7794
|
}
|
|
7145
7795
|
}
|
|
@@ -7208,7 +7858,7 @@ function DocBlocksShell({
|
|
|
7208
7858
|
} finally {
|
|
7209
7859
|
if (access.owned) {
|
|
7210
7860
|
try {
|
|
7211
|
-
await
|
|
7861
|
+
await getFileSystemProviderV27(access.provider)?.dispose();
|
|
7212
7862
|
} catch {
|
|
7213
7863
|
}
|
|
7214
7864
|
}
|
|
@@ -7226,42 +7876,15 @@ function DocBlocksShell({
|
|
|
7226
7876
|
showToast
|
|
7227
7877
|
]
|
|
7228
7878
|
);
|
|
7229
|
-
const persistImportedMedia = useCallback15(
|
|
7230
|
-
async (source, target, importedMarkdownPath) => {
|
|
7231
|
-
const parentDir = dirnameOf(importedMarkdownPath);
|
|
7232
|
-
const folder = basenameOf(importedMarkdownPath).replace(/\.[^.]+$/, "") + "_files";
|
|
7233
|
-
const mediaRoot = parentDir ? `${parentDir}/${folder}` : folder;
|
|
7234
|
-
const entries = await source.listFiles();
|
|
7235
|
-
const targetV2 = getFileSystemProviderV25(target);
|
|
7236
|
-
for (const entry of entries) {
|
|
7237
|
-
if (entry.path.endsWith(".md")) continue;
|
|
7238
|
-
const data = await source.readFile(entry.path);
|
|
7239
|
-
if (!data) continue;
|
|
7240
|
-
const cleanPath = entry.path.replace(/^\/+/, "");
|
|
7241
|
-
const destination = `${mediaRoot}/${cleanPath}`;
|
|
7242
|
-
if (targetV2) {
|
|
7243
|
-
await targetV2.writeFile(parseWorkspacePath5(destination), data, {
|
|
7244
|
-
mode: "upsert",
|
|
7245
|
-
createParents: true
|
|
7246
|
-
});
|
|
7247
|
-
} else {
|
|
7248
|
-
await target.writeBinary(destination, data);
|
|
7249
|
-
}
|
|
7250
|
-
}
|
|
7251
|
-
},
|
|
7252
|
-
[]
|
|
7253
|
-
);
|
|
7254
7879
|
const handleImportFiles = useCallback15(
|
|
7255
7880
|
async (files) => {
|
|
7256
7881
|
if (!provider) return;
|
|
7257
|
-
const result = await importDroppedFiles(files, provider
|
|
7258
|
-
persistMedia: (source, path) => persistImportedMedia(source, provider, path)
|
|
7259
|
-
});
|
|
7882
|
+
const result = await importDroppedFiles(files, provider);
|
|
7260
7883
|
setExplorerKey((k) => k + 1);
|
|
7261
7884
|
const summary = summariseImport(result);
|
|
7262
7885
|
if (summary) showToast(summary.kind, summary.message);
|
|
7263
7886
|
},
|
|
7264
|
-
[provider,
|
|
7887
|
+
[provider, showToast]
|
|
7265
7888
|
);
|
|
7266
7889
|
const handleEditorChange = useCallback15(
|
|
7267
7890
|
(source) => {
|
|
@@ -7308,7 +7931,7 @@ function DocBlocksShell({
|
|
|
7308
7931
|
throw error;
|
|
7309
7932
|
}
|
|
7310
7933
|
if (!isCurrent()) {
|
|
7311
|
-
await
|
|
7934
|
+
await getFileSystemProviderV27(mem)?.dispose();
|
|
7312
7935
|
await revokeAbandonedResource();
|
|
7313
7936
|
return;
|
|
7314
7937
|
}
|
|
@@ -7356,7 +7979,7 @@ function DocBlocksShell({
|
|
|
7356
7979
|
} finally {
|
|
7357
7980
|
try {
|
|
7358
7981
|
if (!adoptionStarted) {
|
|
7359
|
-
await
|
|
7982
|
+
await getFileSystemProviderV27(mem)?.dispose();
|
|
7360
7983
|
}
|
|
7361
7984
|
} finally {
|
|
7362
7985
|
if (!opened) await revokeAbandonedResource();
|
|
@@ -7426,7 +8049,7 @@ function DocBlocksShell({
|
|
|
7426
8049
|
push: true
|
|
7427
8050
|
});
|
|
7428
8051
|
} finally {
|
|
7429
|
-
if (!adoptionStarted) await
|
|
8052
|
+
if (!adoptionStarted) await getFileSystemProviderV27(mem)?.dispose();
|
|
7430
8053
|
}
|
|
7431
8054
|
},
|
|
7432
8055
|
[adoptTransientWorkspace]
|
|
@@ -7523,7 +8146,7 @@ function DocBlocksShell({
|
|
|
7523
8146
|
try {
|
|
7524
8147
|
await copyProviderToContainer(p, container, folder);
|
|
7525
8148
|
} finally {
|
|
7526
|
-
if (ownsProvider) await
|
|
8149
|
+
if (ownsProvider) await getFileSystemProviderV27(p)?.dispose();
|
|
7527
8150
|
}
|
|
7528
8151
|
}
|
|
7529
8152
|
if (usedFolders.size === 0) {
|
|
@@ -7657,7 +8280,7 @@ function DocBlocksShell({
|
|
|
7657
8280
|
const p = await createElectronFileSystemProvider(info.id, info.name, info.rootPath);
|
|
7658
8281
|
setProvider(p);
|
|
7659
8282
|
setActiveWorkspaceId(info.id);
|
|
7660
|
-
|
|
8283
|
+
adoptSelectedDocument(null);
|
|
7661
8284
|
setSelectedFolder(null);
|
|
7662
8285
|
setFolderEntries([]);
|
|
7663
8286
|
} else {
|
|
@@ -7665,12 +8288,13 @@ function DocBlocksShell({
|
|
|
7665
8288
|
const fsProvider = await createIndexedDbFileSystemProvider(defaultWs.id, defaultWs.name);
|
|
7666
8289
|
setProvider(fsProvider);
|
|
7667
8290
|
setActiveWorkspaceId(defaultWs.id);
|
|
7668
|
-
|
|
8291
|
+
adoptSelectedDocument(null);
|
|
7669
8292
|
setSelectedFolder(null);
|
|
7670
8293
|
setFolderEntries([]);
|
|
7671
8294
|
}
|
|
7672
8295
|
}, [
|
|
7673
8296
|
activeWorkspaceId,
|
|
8297
|
+
adoptSelectedDocument,
|
|
7674
8298
|
confirmAction,
|
|
7675
8299
|
handleWorkspaceSelect,
|
|
7676
8300
|
pinnedDocuments,
|
|
@@ -7807,6 +8431,7 @@ function DocBlocksShell({
|
|
|
7807
8431
|
onPinnedDocumentDelete: handlePinnedDocumentDelete,
|
|
7808
8432
|
onTogglePin: handleTogglePin,
|
|
7809
8433
|
onSelect: handleSelect,
|
|
8434
|
+
actionsForEntry: outsideInActionsForEntry,
|
|
7810
8435
|
onTreeMutation: handleTreeMutation,
|
|
7811
8436
|
onTreeChange: handleTreeChange,
|
|
7812
8437
|
onImportFiles: handleImportFiles,
|
|
@@ -7905,10 +8530,13 @@ function DocBlocksShell({
|
|
|
7905
8530
|
EditorShell,
|
|
7906
8531
|
{
|
|
7907
8532
|
initialMarkdown: editorContent,
|
|
8533
|
+
readOnly: selectedOutsideIn !== null && !selectedOutsideInEditingEnabled,
|
|
7908
8534
|
initialView,
|
|
7909
8535
|
defaultViewportPreset: defaultPreviewViewportPreset,
|
|
7910
8536
|
articleId: selectedFile,
|
|
7911
8537
|
fileName: selectedFile,
|
|
8538
|
+
saveCoverImageOutput: saveRenderedImageOutput,
|
|
8539
|
+
saveDashboardImageOutput: saveRenderedImageOutput,
|
|
7912
8540
|
onChange: handleEditorChange,
|
|
7913
8541
|
onLinkClick: handleEditorLinkClick,
|
|
7914
8542
|
colorScheme: resolvedTheme,
|
|
@@ -8079,6 +8707,9 @@ export {
|
|
|
8079
8707
|
WorkspacePicker,
|
|
8080
8708
|
WriteCanvasSettingsControls,
|
|
8081
8709
|
buildExportFilename,
|
|
8710
|
+
createImageSaveOutput as createCoverImageSaveOutput,
|
|
8711
|
+
createImageSaveOutput as createDashboardImageSaveOutput,
|
|
8712
|
+
createImageSaveOutput,
|
|
8082
8713
|
loadLastExportOptions,
|
|
8083
8714
|
resolveWriteCanvasFonts,
|
|
8084
8715
|
runExport,
|