@bendyline/docblocks-react 2.5.0 → 2.6.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/README.md +18 -0
- package/THIRD_PARTY_NOTICES.txt +154 -55
- package/dist/{ExportDialog-VBWHFJ2R.js → ExportDialog-Q4VWI43H.js} +2 -2
- package/dist/{ExportToolbarControls-BUVJCSMV.js → ExportToolbarControls-QYLBRWM7.js} +7 -7
- package/dist/GitGrantNotice-I2VNKPLH.js +29 -0
- package/dist/{GitToolbarControl-VJVK2B4X.js → GitToolbarControl-O5AXBC6T.js} +5 -3
- package/dist/{GitUI-CAKFVKWY.js → GitUI-L5T3XHZG.js} +68 -14
- package/dist/calculation/index.d.ts +27 -0
- package/dist/calculation/index.js +16 -0
- package/dist/chunk-6E635KCJ.js +11 -0
- package/dist/chunk-DUKTAKB4.js +83 -0
- package/dist/{chunk-VRRL6VTD.js → chunk-Q6XNIKDG.js} +63 -0
- package/dist/{chunk-HFYPTTCH.js → chunk-V2ENBGUA.js} +0 -9
- package/dist/export/index.js +5 -5
- package/dist/index.d.ts +42 -7
- package/dist/index.js +881 -509
- package/dist/proofing/index.d.ts +112 -0
- package/dist/proofing/index.js +50 -0
- package/dist/settings/index.d.ts +37 -1
- package/dist/settings/index.js +11 -3
- package/package.json +20 -9
- package/src/styles/docblocks.css +364 -96
- package/dist/{chunk-SIIEGOHY.js → chunk-FMDLZ4ZG.js} +3 -3
package/dist/index.js
CHANGED
|
@@ -1,66 +1,75 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createBrowserSaveAsAdapter
|
|
3
|
+
} from "./chunk-UMEDHMT7.js";
|
|
1
4
|
import {
|
|
2
5
|
BADGE_GLYPHS,
|
|
3
6
|
BADGE_LABELS,
|
|
4
|
-
GitContext,
|
|
5
7
|
buildBadgeMap,
|
|
6
8
|
conflictedPaths,
|
|
7
|
-
isFileDirty
|
|
9
|
+
isFileDirty
|
|
10
|
+
} from "./chunk-V2ENBGUA.js";
|
|
11
|
+
import {
|
|
12
|
+
useMenuKeyboard
|
|
13
|
+
} from "./chunk-MRUK56JS.js";
|
|
14
|
+
import {
|
|
15
|
+
GitContext,
|
|
8
16
|
useGitContext
|
|
9
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-6E635KCJ.js";
|
|
10
18
|
import {
|
|
11
19
|
createImageSaveOutput,
|
|
12
20
|
updateExportTargetExtension
|
|
13
21
|
} from "./chunk-FNSRCCBC.js";
|
|
22
|
+
import {
|
|
23
|
+
runExport
|
|
24
|
+
} from "./chunk-ODYNOPC3.js";
|
|
25
|
+
import {
|
|
26
|
+
buildExportFilename
|
|
27
|
+
} from "./chunk-M5Y5WO7Z.js";
|
|
28
|
+
import {
|
|
29
|
+
createLocalProofingIgnoreStore
|
|
30
|
+
} from "./chunk-DUKTAKB4.js";
|
|
14
31
|
import {
|
|
15
32
|
AccentColorSettings,
|
|
16
33
|
DB_CHROME_COLORS,
|
|
34
|
+
DEFAULT_PROOFING_PREFERENCES,
|
|
17
35
|
DEFAULT_WRITE_CANVAS_FONT_SCHEME,
|
|
18
36
|
DEFAULT_WRITE_CANVAS_PREFERENCES,
|
|
37
|
+
ProofingSettingsControls,
|
|
19
38
|
SettingsDialog,
|
|
20
39
|
ThemeSettings,
|
|
21
40
|
WRITE_CANVAS_FONT_SCHEMES,
|
|
22
41
|
WriteCanvasSettingsControls,
|
|
23
42
|
loadAccentColor,
|
|
43
|
+
loadProofingPreferences,
|
|
24
44
|
loadThemePreference,
|
|
25
45
|
loadWriteCanvasPreferences,
|
|
26
46
|
resolveWriteCanvasFonts,
|
|
27
47
|
saveAccentColor,
|
|
48
|
+
saveProofingPreferences,
|
|
28
49
|
saveThemePreference,
|
|
29
50
|
saveWriteCanvasPreferences
|
|
30
|
-
} from "./chunk-
|
|
51
|
+
} from "./chunk-Q6XNIKDG.js";
|
|
31
52
|
import {
|
|
32
53
|
pickEmptyDocumentPrompt,
|
|
33
54
|
useResponsivePreviewViewportPreset
|
|
34
55
|
} from "./chunk-JDJVRDOP.js";
|
|
35
56
|
import {
|
|
36
57
|
ExportDialog
|
|
37
|
-
} from "./chunk-
|
|
38
|
-
import {
|
|
39
|
-
runExport
|
|
40
|
-
} from "./chunk-ODYNOPC3.js";
|
|
41
|
-
import {
|
|
42
|
-
createBrowserSaveAsAdapter
|
|
43
|
-
} from "./chunk-UMEDHMT7.js";
|
|
44
|
-
import {
|
|
45
|
-
useMenuKeyboard
|
|
46
|
-
} from "./chunk-MRUK56JS.js";
|
|
58
|
+
} from "./chunk-FMDLZ4ZG.js";
|
|
47
59
|
import "./chunk-YBEYTVU2.js";
|
|
48
|
-
import {
|
|
49
|
-
Dialog
|
|
50
|
-
} from "./chunk-LG6HAWCK.js";
|
|
51
|
-
import {
|
|
52
|
-
buildExportFilename
|
|
53
|
-
} from "./chunk-M5Y5WO7Z.js";
|
|
54
60
|
import {
|
|
55
61
|
DEFAULT_OPTIONS,
|
|
56
62
|
loadLastExportOptions
|
|
57
63
|
} from "./chunk-EBWYGTN7.js";
|
|
64
|
+
import {
|
|
65
|
+
Dialog
|
|
66
|
+
} from "./chunk-LG6HAWCK.js";
|
|
58
67
|
|
|
59
68
|
// src/FileExplorer/FileExplorer.tsx
|
|
60
69
|
import { useCallback as useCallback4, useEffect as useEffect4, useMemo, useRef as useRef4, useState as useState4 } from "react";
|
|
61
70
|
import {
|
|
62
71
|
isFileSystemMoveStateError,
|
|
63
|
-
parseWorkspacePath as
|
|
72
|
+
parseWorkspacePath as parseWorkspacePath4
|
|
64
73
|
} from "@bendyline/docblocks/filesystem";
|
|
65
74
|
|
|
66
75
|
// src/FileExplorer/useFileTree.ts
|
|
@@ -1588,54 +1597,617 @@ function isHiddenFileEntry(entry) {
|
|
|
1588
1597
|
if (name.startsWith(".")) return true;
|
|
1589
1598
|
return entry.kind === "directory" && name.endsWith("_files");
|
|
1590
1599
|
}
|
|
1591
|
-
function filterVisibleFileEntries(entries) {
|
|
1592
|
-
return entries.filter((entry) => !isHiddenFileEntry(entry));
|
|
1600
|
+
function filterVisibleFileEntries(entries) {
|
|
1601
|
+
return entries.filter((entry) => !isHiddenFileEntry(entry));
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
// src/FileExplorer/entry-sort.ts
|
|
1605
|
+
function compareText(left, right) {
|
|
1606
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
1607
|
+
}
|
|
1608
|
+
function modifiedTime(entry) {
|
|
1609
|
+
if (entry.kind !== "file" || !entry.lastModified) return null;
|
|
1610
|
+
const timestamp = Date.parse(entry.lastModified);
|
|
1611
|
+
return Number.isNaN(timestamp) ? null : timestamp;
|
|
1612
|
+
}
|
|
1613
|
+
function sortFileEntries(entries, mode) {
|
|
1614
|
+
return [...entries].sort((left, right) => {
|
|
1615
|
+
if (left.kind !== right.kind) return left.kind === "directory" ? -1 : 1;
|
|
1616
|
+
if (left.kind === "directory" || right.kind === "directory" || mode === "name") {
|
|
1617
|
+
return compareText(left.name, right.name);
|
|
1618
|
+
}
|
|
1619
|
+
const leftTime = modifiedTime(left);
|
|
1620
|
+
const rightTime = modifiedTime(right);
|
|
1621
|
+
if (leftTime !== null && rightTime !== null && leftTime !== rightTime) {
|
|
1622
|
+
return rightTime - leftTime;
|
|
1623
|
+
}
|
|
1624
|
+
if (leftTime === null && rightTime !== null) return 1;
|
|
1625
|
+
if (leftTime !== null && rightTime === null) return -1;
|
|
1626
|
+
return compareText(left.name, right.name);
|
|
1627
|
+
});
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
// src/DocBlocksShell/outside-in.ts
|
|
1631
|
+
import {
|
|
1632
|
+
FileSystemContentContainer,
|
|
1633
|
+
FsError as FsError3,
|
|
1634
|
+
getFileSystemProviderV2 as getFileSystemProviderV23,
|
|
1635
|
+
parseWorkspacePath as parseWorkspacePath3
|
|
1636
|
+
} from "@bendyline/docblocks/filesystem";
|
|
1637
|
+
import {
|
|
1638
|
+
createFileSystemDocumentTarget
|
|
1639
|
+
} from "@bendyline/docblocks/document";
|
|
1640
|
+
|
|
1641
|
+
// src/DocBlocksShell/provider-io.ts
|
|
1642
|
+
import {
|
|
1643
|
+
FsError as FsError2,
|
|
1644
|
+
getFileSystemProviderV2 as getFileSystemProviderV22,
|
|
1645
|
+
parseWorkspacePath as parseWorkspacePath2
|
|
1646
|
+
} from "@bendyline/docblocks/filesystem";
|
|
1647
|
+
async function providerEntryExists(provider, path) {
|
|
1648
|
+
const providerV2 = getFileSystemProviderV22(provider);
|
|
1649
|
+
return providerV2 ? await providerV2.stat(parseWorkspacePath2(path)) !== null : provider.exists(path);
|
|
1650
|
+
}
|
|
1651
|
+
async function writeProviderText(provider, path, content, mode = "upsert") {
|
|
1652
|
+
const providerV2 = getFileSystemProviderV22(provider);
|
|
1653
|
+
if (providerV2) {
|
|
1654
|
+
await providerV2.writeFile(parseWorkspacePath2(path), new TextEncoder().encode(content), {
|
|
1655
|
+
mode,
|
|
1656
|
+
createParents: true,
|
|
1657
|
+
expectedVersion: mode === "create" ? null : void 0
|
|
1658
|
+
});
|
|
1659
|
+
return;
|
|
1660
|
+
}
|
|
1661
|
+
if (mode === "create" && await provider.exists(path)) {
|
|
1662
|
+
throw new FsError2("already-exists", "File already exists.", { operation: "write", path });
|
|
1663
|
+
}
|
|
1664
|
+
await provider.writeFile(path, content);
|
|
1665
|
+
}
|
|
1666
|
+
async function removeProviderEntry(provider, path) {
|
|
1667
|
+
const providerV2 = getFileSystemProviderV22(provider);
|
|
1668
|
+
if (providerV2) {
|
|
1669
|
+
await providerV2.remove(parseWorkspacePath2(path), { recursive: true, missing: "ignore" });
|
|
1670
|
+
return;
|
|
1671
|
+
}
|
|
1672
|
+
await provider.delete(path);
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1675
|
+
// src/DocBlocksShell/outside-in-contract.ts
|
|
1676
|
+
var OUTSIDE_IN_FORMAT_IDS = ["html", "docx", "pdf", "pptx", "xlsx", "csv"];
|
|
1677
|
+
var FORMAT_IDS = new Set(OUTSIDE_IN_FORMAT_IDS);
|
|
1678
|
+
var UPDATE_FROM_MARKDOWN_KEY = "squisq-updatefrommarkdown";
|
|
1679
|
+
var HTML_OUTPUT_KEY = "squisq-html-output";
|
|
1680
|
+
async function loadOutsideInModule() {
|
|
1681
|
+
return await import("@bendyline/squisq-formats/outside-in");
|
|
1682
|
+
}
|
|
1683
|
+
function normalizePath(path) {
|
|
1684
|
+
const leading = path.replace(/\\/g, "/").startsWith("/") ? "/" : "";
|
|
1685
|
+
const parts = path.replace(/\\/g, "/").split("/").filter(Boolean);
|
|
1686
|
+
if (parts.some((part) => part === "." || part === "..")) {
|
|
1687
|
+
throw new Error(`Outside-in paths must be canonical workspace paths: ${path}`);
|
|
1688
|
+
}
|
|
1689
|
+
return leading + parts.join("/");
|
|
1690
|
+
}
|
|
1691
|
+
function join(parent, child) {
|
|
1692
|
+
if (!parent || parent === "/") return parent === "/" ? `/${child}` : child;
|
|
1693
|
+
return `${parent}/${child}`;
|
|
1694
|
+
}
|
|
1695
|
+
function slug(stem) {
|
|
1696
|
+
return stem.normalize("NFKD").replace(/\p{Mark}+/gu, "").toLocaleLowerCase("en-US").replace(/[^\p{Letter}\p{Number}]+/gu, "-").replace(/^-+|-+$/g, "") || "document";
|
|
1697
|
+
}
|
|
1698
|
+
function resolveOutsideInLayout(path) {
|
|
1699
|
+
const targetPath = normalizePath(path);
|
|
1700
|
+
const slash = targetPath.lastIndexOf("/");
|
|
1701
|
+
const parentDirectory = slash < 0 ? "" : slash === 0 ? "/" : targetPath.slice(0, slash);
|
|
1702
|
+
const filename = slash < 0 ? targetPath : targetPath.slice(slash + 1);
|
|
1703
|
+
const dot = filename.lastIndexOf(".");
|
|
1704
|
+
if (dot <= 0) return null;
|
|
1705
|
+
const rawFormat = filename.slice(dot + 1).toLowerCase();
|
|
1706
|
+
const format = rawFormat === "htm" ? "html" : rawFormat;
|
|
1707
|
+
if (!FORMAT_IDS.has(format)) return null;
|
|
1708
|
+
const stem = filename.slice(0, dot);
|
|
1709
|
+
const companionName = `${stem}_files`;
|
|
1710
|
+
const companionDirectory = join(parentDirectory, companionName);
|
|
1711
|
+
const markdownFilename = `${slug(stem)}.md`;
|
|
1712
|
+
const backupDirectory = join(companionDirectory, ".original");
|
|
1713
|
+
const backupFilename = `original.${format}`;
|
|
1714
|
+
return {
|
|
1715
|
+
targetPath,
|
|
1716
|
+
format,
|
|
1717
|
+
parentDirectory,
|
|
1718
|
+
stem,
|
|
1719
|
+
companionName,
|
|
1720
|
+
companionDirectory,
|
|
1721
|
+
markdownFilename,
|
|
1722
|
+
markdownPath: join(companionDirectory, markdownFilename),
|
|
1723
|
+
relativeTargetPath: `../${filename}`,
|
|
1724
|
+
backupDirectory,
|
|
1725
|
+
backupFilename,
|
|
1726
|
+
backupPath: join(backupDirectory, backupFilename)
|
|
1727
|
+
};
|
|
1728
|
+
}
|
|
1729
|
+
function chooseOutsideInMarkdownPath(layout, paths) {
|
|
1730
|
+
const canonical = normalizePath(layout.markdownPath);
|
|
1731
|
+
const normalized = paths.map(normalizePath);
|
|
1732
|
+
const exact = normalized.find((path) => path === canonical);
|
|
1733
|
+
if (exact) return exact;
|
|
1734
|
+
const folded = normalized.find(
|
|
1735
|
+
(path) => path.toLocaleLowerCase("en-US") === canonical.toLocaleLowerCase("en-US")
|
|
1736
|
+
);
|
|
1737
|
+
if (folded) return folded;
|
|
1738
|
+
const prefix = `${normalizePath(layout.companionDirectory).replace(/\/$/, "")}/`;
|
|
1739
|
+
const markdown = normalized.filter(
|
|
1740
|
+
(path) => path.startsWith(prefix) && !path.slice(prefix.length).includes("/") && path.toLocaleLowerCase("en-US").endsWith(".md")
|
|
1741
|
+
);
|
|
1742
|
+
return markdown.length === 1 ? markdown[0] : null;
|
|
1743
|
+
}
|
|
1744
|
+
async function readOutsideInMetadata(source) {
|
|
1745
|
+
const { readOutsideInMetadata: readMetadata } = await loadOutsideInModule();
|
|
1746
|
+
const metadata = readMetadata(source);
|
|
1747
|
+
return metadata ? {
|
|
1748
|
+
...metadata,
|
|
1749
|
+
updateFromMarkdown: await isOutsideInMarkdownEditingEnabled(source)
|
|
1750
|
+
} : null;
|
|
1751
|
+
}
|
|
1752
|
+
async function withOutsideInMetadata(source, layout) {
|
|
1753
|
+
const { withOutsideInMetadata: addMetadata } = await loadOutsideInModule();
|
|
1754
|
+
return addMetadata(source, layout);
|
|
1755
|
+
}
|
|
1756
|
+
async function isOutsideInMarkdownEditingEnabled(source) {
|
|
1757
|
+
const module = await loadOutsideInModule();
|
|
1758
|
+
if (module.isOutsideInMarkdownEditingEnabled) {
|
|
1759
|
+
return module.isOutsideInMarkdownEditingEnabled(source);
|
|
1760
|
+
}
|
|
1761
|
+
const { parseFrontmatter, splitFrontmatterBlock } = await import("@bendyline/squisq/markdown");
|
|
1762
|
+
const block = splitFrontmatterBlock(source).frontmatter;
|
|
1763
|
+
if (!block) return false;
|
|
1764
|
+
const firstBreak = block.indexOf("\n");
|
|
1765
|
+
if (firstBreak < 0) return false;
|
|
1766
|
+
const yaml = block.slice(firstBreak + 1).replace(/\r?\n---(?:\r?\n)?$/, "");
|
|
1767
|
+
return parseFrontmatter(yaml)?.[UPDATE_FROM_MARKDOWN_KEY] === true;
|
|
1768
|
+
}
|
|
1769
|
+
async function withOutsideInMarkdownEditing(source, layout, enabled = true) {
|
|
1770
|
+
const module = await loadOutsideInModule();
|
|
1771
|
+
if (module.withOutsideInMarkdownEditing) {
|
|
1772
|
+
return module.withOutsideInMarkdownEditing(source, layout, enabled);
|
|
1773
|
+
}
|
|
1774
|
+
const { setFrontmatterValues } = await import("@bendyline/squisq/markdown");
|
|
1775
|
+
return setFrontmatterValues(await withOutsideInMetadata(source, layout), {
|
|
1776
|
+
[UPDATE_FROM_MARKDOWN_KEY]: enabled
|
|
1777
|
+
});
|
|
1778
|
+
}
|
|
1779
|
+
async function readOutsideInHtmlOutput(source) {
|
|
1780
|
+
const { parseFrontmatter, splitFrontmatterBlock } = await import("@bendyline/squisq/markdown");
|
|
1781
|
+
const block = splitFrontmatterBlock(source).frontmatter;
|
|
1782
|
+
if (!block) return null;
|
|
1783
|
+
const firstBreak = block.indexOf("\n");
|
|
1784
|
+
if (firstBreak < 0) return null;
|
|
1785
|
+
const yaml = block.slice(firstBreak + 1).replace(/\r?\n---(?:\r?\n)?$/, "");
|
|
1786
|
+
const value = parseFrontmatter(yaml)?.[HTML_OUTPUT_KEY];
|
|
1787
|
+
return value === "interactive" || value === "static" ? value : null;
|
|
1788
|
+
}
|
|
1789
|
+
async function withOutsideInHtmlOutput(source, output) {
|
|
1790
|
+
const { setFrontmatterValues } = await import("@bendyline/squisq/markdown");
|
|
1791
|
+
return setFrontmatterValues(source, { [HTML_OUTPUT_KEY]: output });
|
|
1792
|
+
}
|
|
1793
|
+
async function importOutsideInDocument(source, options = {}) {
|
|
1794
|
+
const { importOutsideInDocument: importDocument } = await loadOutsideInModule();
|
|
1795
|
+
const imported = await importDocument(source, options);
|
|
1796
|
+
const layout = resolveOutsideInLayout(source.targetPath);
|
|
1797
|
+
if (!layout) throw new Error(`Outside-in editing does not support "${source.targetPath}".`);
|
|
1798
|
+
return { ...imported, layout };
|
|
1799
|
+
}
|
|
1800
|
+
async function renderOutsideInDocument(source, options = {}) {
|
|
1801
|
+
const { renderOutsideInDocument: renderDocument } = await loadOutsideInModule();
|
|
1802
|
+
return renderDocument(source, options);
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1805
|
+
// src/DocBlocksShell/outside-in.ts
|
|
1806
|
+
var OUTSIDE_IN_EXTENSION = /\.(?:html?|docx|pdf|pptx|xlsx|csv)$/i;
|
|
1807
|
+
var IMAGE_MIME_TYPES = {
|
|
1808
|
+
avif: "image/avif",
|
|
1809
|
+
bmp: "image/bmp",
|
|
1810
|
+
gif: "image/gif",
|
|
1811
|
+
ico: "image/x-icon",
|
|
1812
|
+
jpeg: "image/jpeg",
|
|
1813
|
+
jpg: "image/jpeg",
|
|
1814
|
+
png: "image/png",
|
|
1815
|
+
webp: "image/webp"
|
|
1816
|
+
};
|
|
1817
|
+
var SQUISQ_RUNTIME_DIRECTORY = "_squisq";
|
|
1818
|
+
var SQUISQ_RUNTIME_FILENAME = "squisq-player.js";
|
|
1819
|
+
function withoutLeadingSlash(path) {
|
|
1820
|
+
return parseWorkspacePath3(path);
|
|
1821
|
+
}
|
|
1822
|
+
function withLegacySlash(path, like) {
|
|
1823
|
+
const canonical = withoutLeadingSlash(path);
|
|
1824
|
+
return like.startsWith("/") && canonical ? `/${canonical}` : canonical;
|
|
1825
|
+
}
|
|
1826
|
+
function dirname(path) {
|
|
1827
|
+
const canonical = withoutLeadingSlash(path);
|
|
1828
|
+
const slash = canonical.lastIndexOf("/");
|
|
1829
|
+
return slash < 0 ? "" : canonical.slice(0, slash);
|
|
1830
|
+
}
|
|
1831
|
+
function join2(parent, child) {
|
|
1832
|
+
return parent ? `${parent}/${child}` : child;
|
|
1833
|
+
}
|
|
1834
|
+
function relativePath(fromDirectory, targetPath) {
|
|
1835
|
+
const from = withoutLeadingSlash(fromDirectory).split("/").filter(Boolean);
|
|
1836
|
+
const target = withoutLeadingSlash(targetPath).split("/").filter(Boolean);
|
|
1837
|
+
let shared = 0;
|
|
1838
|
+
while (shared < from.length && shared < target.length && from[shared] === target[shared]) {
|
|
1839
|
+
shared++;
|
|
1840
|
+
}
|
|
1841
|
+
const segments = [...from.slice(shared).map(() => ".."), ...target.slice(shared)];
|
|
1842
|
+
return segments.join("/") || ".";
|
|
1843
|
+
}
|
|
1844
|
+
async function readText(provider, path) {
|
|
1845
|
+
const v2 = getFileSystemProviderV23(provider);
|
|
1846
|
+
if (!v2) return provider.readFile(path);
|
|
1847
|
+
const current = await v2.readFile(parseWorkspacePath3(path));
|
|
1848
|
+
if (!current) return null;
|
|
1849
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(current.data);
|
|
1850
|
+
}
|
|
1851
|
+
async function readBytes(provider, path) {
|
|
1852
|
+
const v2 = getFileSystemProviderV23(provider);
|
|
1853
|
+
if (v2) return (await v2.readFile(parseWorkspacePath3(path)))?.data ?? null;
|
|
1854
|
+
return provider.readBinary(path);
|
|
1855
|
+
}
|
|
1856
|
+
async function writeBytes(provider, path, data, mode = "upsert") {
|
|
1857
|
+
const v2 = getFileSystemProviderV23(provider);
|
|
1858
|
+
if (v2) {
|
|
1859
|
+
await v2.writeFile(parseWorkspacePath3(path), data, {
|
|
1860
|
+
mode,
|
|
1861
|
+
createParents: true,
|
|
1862
|
+
expectedVersion: mode === "create" ? null : void 0
|
|
1863
|
+
});
|
|
1864
|
+
return;
|
|
1865
|
+
}
|
|
1866
|
+
if (mode === "create" && await provider.exists(path)) {
|
|
1867
|
+
throw new FsError3("already-exists", "File already exists.", { operation: "write", path });
|
|
1868
|
+
}
|
|
1869
|
+
await provider.writeBinary(path, data);
|
|
1870
|
+
}
|
|
1871
|
+
async function listCompanionFiles(provider, layout) {
|
|
1872
|
+
try {
|
|
1873
|
+
const entries = await provider.readDirectory(layout.companionDirectory);
|
|
1874
|
+
return entries.filter((entry) => entry.kind === "file").map((entry) => entry.path);
|
|
1875
|
+
} catch (error) {
|
|
1876
|
+
if (error instanceof FsError3 && error.code === "not-found") return [];
|
|
1877
|
+
throw error;
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
async function removeOutsideInCompanion(provider, layout) {
|
|
1881
|
+
const providerV2 = getFileSystemProviderV23(provider);
|
|
1882
|
+
if (providerV2) {
|
|
1883
|
+
const path = parseWorkspacePath3(layout.companionDirectory);
|
|
1884
|
+
if (await providerV2.stat(path) !== null) {
|
|
1885
|
+
await providerV2.remove(path, { recursive: true, missing: "ignore" });
|
|
1886
|
+
}
|
|
1887
|
+
return;
|
|
1888
|
+
}
|
|
1889
|
+
if (await provider.exists(layout.companionDirectory)) {
|
|
1890
|
+
await provider.delete(layout.companionDirectory);
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
async function persistImportedMedia(provider, layout, container) {
|
|
1894
|
+
const entries = await container.listFiles();
|
|
1895
|
+
for (const entry of entries) {
|
|
1896
|
+
if (/\.md$/i.test(entry.path)) continue;
|
|
1897
|
+
const data = await container.readFile(entry.path);
|
|
1898
|
+
if (!data) continue;
|
|
1899
|
+
await writeBytes(provider, join2(layout.companionDirectory, entry.path), data, "create");
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1902
|
+
async function loadEditableShellDocument(provider, selectedPath) {
|
|
1903
|
+
const imageExtension = selectedPath.slice(selectedPath.lastIndexOf(".") + 1).toLowerCase();
|
|
1904
|
+
const imageMimeType = IMAGE_MIME_TYPES[imageExtension];
|
|
1905
|
+
if (imageMimeType) {
|
|
1906
|
+
const data = await readBytes(provider, selectedPath);
|
|
1907
|
+
return data === null ? null : {
|
|
1908
|
+
displayPath: selectedPath,
|
|
1909
|
+
sourcePath: selectedPath,
|
|
1910
|
+
content: "",
|
|
1911
|
+
outsideIn: null,
|
|
1912
|
+
outsideInEditingEnabled: false,
|
|
1913
|
+
image: { data, mimeType: imageMimeType }
|
|
1914
|
+
};
|
|
1915
|
+
}
|
|
1916
|
+
if (!OUTSIDE_IN_EXTENSION.test(selectedPath)) {
|
|
1917
|
+
const content = await readText(provider, selectedPath);
|
|
1918
|
+
return content === null ? null : {
|
|
1919
|
+
displayPath: selectedPath,
|
|
1920
|
+
sourcePath: selectedPath,
|
|
1921
|
+
content,
|
|
1922
|
+
outsideIn: null,
|
|
1923
|
+
outsideInEditingEnabled: true
|
|
1924
|
+
};
|
|
1925
|
+
}
|
|
1926
|
+
const resolved = resolveOutsideInLayout(selectedPath);
|
|
1927
|
+
if (!resolved) return null;
|
|
1928
|
+
const layout = {
|
|
1929
|
+
...resolved,
|
|
1930
|
+
targetPath: withLegacySlash(resolved.targetPath, selectedPath),
|
|
1931
|
+
companionDirectory: withLegacySlash(resolved.companionDirectory, selectedPath),
|
|
1932
|
+
markdownPath: withLegacySlash(resolved.markdownPath, selectedPath)
|
|
1933
|
+
};
|
|
1934
|
+
const candidates = (await listCompanionFiles(provider, layout)).map(
|
|
1935
|
+
(path) => withLegacySlash(path, selectedPath)
|
|
1936
|
+
);
|
|
1937
|
+
const chosen = chooseOutsideInMarkdownPath(layout, candidates);
|
|
1938
|
+
if (chosen) {
|
|
1939
|
+
const content = await readText(provider, chosen);
|
|
1940
|
+
if (content === null) return null;
|
|
1941
|
+
const metadata = await readOutsideInMetadata(content);
|
|
1942
|
+
if (metadata && metadata.format !== layout.format) {
|
|
1943
|
+
throw new Error(
|
|
1944
|
+
`${chosen} is configured for ${metadata.format}, not ${layout.format}. Rename the rendered file back or repair the companion frontmatter.`
|
|
1945
|
+
);
|
|
1946
|
+
}
|
|
1947
|
+
const linkedContent = await withOutsideInMetadata(content, layout);
|
|
1948
|
+
if (linkedContent !== content) await writeProviderText(provider, chosen, linkedContent);
|
|
1949
|
+
return {
|
|
1950
|
+
displayPath: selectedPath,
|
|
1951
|
+
sourcePath: chosen,
|
|
1952
|
+
content: linkedContent,
|
|
1953
|
+
outsideIn: { ...layout, markdownPath: chosen },
|
|
1954
|
+
outsideInEditingEnabled: await isOutsideInMarkdownEditingEnabled(linkedContent)
|
|
1955
|
+
};
|
|
1956
|
+
}
|
|
1957
|
+
const rendered = await readBytes(provider, selectedPath);
|
|
1958
|
+
if (!rendered) return null;
|
|
1959
|
+
const imported = await importOutsideInDocument({
|
|
1960
|
+
data: rendered,
|
|
1961
|
+
targetPath: selectedPath
|
|
1962
|
+
});
|
|
1963
|
+
const importedLayout = {
|
|
1964
|
+
...imported.layout,
|
|
1965
|
+
targetPath: withLegacySlash(imported.layout.targetPath, selectedPath),
|
|
1966
|
+
companionDirectory: withLegacySlash(imported.layout.companionDirectory, selectedPath),
|
|
1967
|
+
markdownPath: withLegacySlash(imported.layout.markdownPath, selectedPath)
|
|
1968
|
+
};
|
|
1969
|
+
await persistImportedMedia(provider, importedLayout, imported.container);
|
|
1970
|
+
await writeProviderText(provider, importedLayout.markdownPath, imported.markdown, "create");
|
|
1971
|
+
return {
|
|
1972
|
+
displayPath: selectedPath,
|
|
1973
|
+
sourcePath: importedLayout.markdownPath,
|
|
1974
|
+
content: imported.markdown,
|
|
1975
|
+
outsideIn: importedLayout,
|
|
1976
|
+
outsideInEditingEnabled: false
|
|
1977
|
+
};
|
|
1978
|
+
}
|
|
1979
|
+
async function enableOutsideInMarkdownEditing(provider, document2) {
|
|
1980
|
+
const layout = document2.outsideIn;
|
|
1981
|
+
if (!layout) throw new Error("This file does not support outside-in Markdown editing.");
|
|
1982
|
+
const original = await readBytes(provider, layout.targetPath);
|
|
1983
|
+
if (!original) throw new Error(`The rendered file "${layout.targetPath}" was not found.`);
|
|
1984
|
+
try {
|
|
1985
|
+
await writeBytes(provider, layout.backupPath, original, "create");
|
|
1986
|
+
} catch (error) {
|
|
1987
|
+
if (!(error instanceof FsError3 && error.code === "already-exists")) throw error;
|
|
1988
|
+
}
|
|
1989
|
+
const content = await withOutsideInMarkdownEditing(document2.content, layout);
|
|
1990
|
+
if (content !== document2.content) await writeProviderText(provider, document2.sourcePath, content);
|
|
1991
|
+
return { ...document2, content, outsideIn: layout, outsideInEditingEnabled: true };
|
|
1992
|
+
}
|
|
1993
|
+
async function findRuntimePath(provider, targetPath) {
|
|
1994
|
+
let directory = dirname(targetPath);
|
|
1995
|
+
for (; ; ) {
|
|
1996
|
+
const candidateDirectory = join2(directory, SQUISQ_RUNTIME_DIRECTORY);
|
|
1997
|
+
if (await providerEntryExists(provider, candidateDirectory)) {
|
|
1998
|
+
const providerV2 = getFileSystemProviderV23(provider);
|
|
1999
|
+
if (providerV2) {
|
|
2000
|
+
const entry = await providerV2.stat(parseWorkspacePath3(candidateDirectory));
|
|
2001
|
+
if (entry?.kind !== "directory") {
|
|
2002
|
+
throw new Error(`${candidateDirectory} must be a directory.`);
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
return join2(candidateDirectory, SQUISQ_RUNTIME_FILENAME);
|
|
2006
|
+
}
|
|
2007
|
+
if (!directory) break;
|
|
2008
|
+
directory = dirname(directory);
|
|
2009
|
+
}
|
|
2010
|
+
return join2(SQUISQ_RUNTIME_DIRECTORY, SQUISQ_RUNTIME_FILENAME);
|
|
2011
|
+
}
|
|
2012
|
+
async function writeRuntimeIfNeeded(provider, runtimePath) {
|
|
2013
|
+
const { PLAYER_BUNDLE } = await import("@bendyline/squisq-react/standalone-source");
|
|
2014
|
+
const current = await readText(provider, runtimePath);
|
|
2015
|
+
if (current !== PLAYER_BUNDLE) await writeProviderText(provider, runtimePath, PLAYER_BUNDLE);
|
|
1593
2016
|
}
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
2017
|
+
async function renderPlainOutsideInHtml(provider, layout, markdown) {
|
|
2018
|
+
const [{ parseMarkdown }, { markdownDocToPlainHtml }] = await Promise.all([
|
|
2019
|
+
import("@bendyline/squisq/markdown"),
|
|
2020
|
+
import("@bendyline/squisq-formats/html")
|
|
2021
|
+
]);
|
|
2022
|
+
const container = new FileSystemContentContainer(provider, layout.companionDirectory);
|
|
2023
|
+
const images = /* @__PURE__ */ new Map();
|
|
2024
|
+
for (const entry of await container.listFiles()) {
|
|
2025
|
+
const outputPath = `${layout.companionName}/${entry.path}`;
|
|
2026
|
+
images.set(entry.path, outputPath);
|
|
2027
|
+
images.set(`./${entry.path}`, outputPath);
|
|
2028
|
+
}
|
|
2029
|
+
return new TextEncoder().encode(
|
|
2030
|
+
markdownDocToPlainHtml(parseMarkdown(markdown), {
|
|
2031
|
+
title: layout.stem,
|
|
2032
|
+
images: images.size > 0 ? images : void 0
|
|
2033
|
+
})
|
|
2034
|
+
);
|
|
1598
2035
|
}
|
|
1599
|
-
function
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
2036
|
+
async function prepareOutsideInRender(provider, layout, markdown) {
|
|
2037
|
+
const htmlOutput = layout.format === "html" ? await readOutsideInHtmlOutput(markdown) : null;
|
|
2038
|
+
if (htmlOutput === "static") {
|
|
2039
|
+
return {
|
|
2040
|
+
bytes: await renderPlainOutsideInHtml(provider, layout, markdown),
|
|
2041
|
+
runtimePath: null
|
|
2042
|
+
};
|
|
2043
|
+
}
|
|
2044
|
+
const runtimePath = layout.format === "html" ? await findRuntimePath(provider, layout.targetPath) : null;
|
|
2045
|
+
const outputDirectory = dirname(layout.targetPath);
|
|
2046
|
+
const rendered = await renderOutsideInDocument(
|
|
2047
|
+
{
|
|
2048
|
+
markdown,
|
|
2049
|
+
targetPath: layout.targetPath,
|
|
2050
|
+
container: new FileSystemContentContainer(provider, layout.companionDirectory)
|
|
2051
|
+
},
|
|
2052
|
+
runtimePath ? {
|
|
2053
|
+
html: {
|
|
2054
|
+
playerScriptPath: relativePath(outputDirectory, runtimePath),
|
|
2055
|
+
basePath: relativePath(outputDirectory, layout.companionDirectory)
|
|
2056
|
+
},
|
|
2057
|
+
...htmlOutput === "interactive" ? { formatOptions: { html: { mode: "slideshow" } } } : {}
|
|
2058
|
+
} : {}
|
|
2059
|
+
);
|
|
2060
|
+
return { bytes: rendered.bytes, runtimePath };
|
|
1603
2061
|
}
|
|
1604
|
-
function
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
2062
|
+
async function createNewOutsideInDocument(provider, targetPath, htmlOutput) {
|
|
2063
|
+
const layout = resolveOutsideInLayout(targetPath);
|
|
2064
|
+
if (!layout) throw new Error(`Outside-in editing does not support "${targetPath}".`);
|
|
2065
|
+
if (layout.format !== "html" && htmlOutput !== void 0) {
|
|
2066
|
+
throw new Error("Only Web pages have an HTML output style.");
|
|
2067
|
+
}
|
|
2068
|
+
if (await providerEntryExists(provider, layout.targetPath) || await providerEntryExists(provider, layout.companionDirectory)) {
|
|
2069
|
+
throw new FsError3(
|
|
2070
|
+
"already-exists",
|
|
2071
|
+
"A file or companion folder with that name already exists.",
|
|
2072
|
+
{
|
|
2073
|
+
operation: "write",
|
|
2074
|
+
path: layout.targetPath
|
|
2075
|
+
}
|
|
2076
|
+
);
|
|
2077
|
+
}
|
|
2078
|
+
let content = await withOutsideInMarkdownEditing(`# ${layout.stem}
|
|
2079
|
+
`, layout);
|
|
2080
|
+
if (layout.format === "html") {
|
|
2081
|
+
content = await withOutsideInHtmlOutput(content, htmlOutput ?? "interactive");
|
|
2082
|
+
}
|
|
2083
|
+
const prepared = await prepareOutsideInRender(provider, layout, content);
|
|
2084
|
+
if (prepared.runtimePath) await writeRuntimeIfNeeded(provider, prepared.runtimePath);
|
|
2085
|
+
await writeBytes(provider, layout.targetPath, prepared.bytes, "create");
|
|
2086
|
+
await writeProviderText(provider, layout.markdownPath, content, "create");
|
|
2087
|
+
return {
|
|
2088
|
+
displayPath: layout.targetPath,
|
|
2089
|
+
sourcePath: layout.markdownPath,
|
|
2090
|
+
content,
|
|
2091
|
+
outsideIn: layout,
|
|
2092
|
+
outsideInEditingEnabled: true
|
|
2093
|
+
};
|
|
2094
|
+
}
|
|
2095
|
+
function createOutsideInDocumentTarget(provider, layout, onCommitted) {
|
|
2096
|
+
const sourceTarget = createFileSystemDocumentTarget(provider, layout.markdownPath);
|
|
2097
|
+
return {
|
|
2098
|
+
key: `${provider.id}:outside-in:${parseWorkspacePath3(layout.targetPath)}`,
|
|
2099
|
+
async commit(request) {
|
|
2100
|
+
if (!await isOutsideInMarkdownEditingEnabled(request.content)) {
|
|
2101
|
+
throw new Error(
|
|
2102
|
+
"Outside-in editing is read-only until squisq-updatefrommarkdown: true is set."
|
|
2103
|
+
);
|
|
2104
|
+
}
|
|
2105
|
+
const rendered = await prepareOutsideInRender(provider, layout, request.content);
|
|
2106
|
+
await sourceTarget.commit({ ...request, targetKey: sourceTarget.key });
|
|
2107
|
+
if (rendered.runtimePath) await writeRuntimeIfNeeded(provider, rendered.runtimePath);
|
|
2108
|
+
await writeBytes(provider, layout.targetPath, rendered.bytes);
|
|
2109
|
+
onCommitted?.();
|
|
2110
|
+
return {};
|
|
1614
2111
|
}
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
2112
|
+
};
|
|
2113
|
+
}
|
|
2114
|
+
function createOutsideInContentContainer(provider, layout) {
|
|
2115
|
+
return new FileSystemContentContainer(provider, layout.companionDirectory);
|
|
1619
2116
|
}
|
|
1620
2117
|
|
|
1621
|
-
// src/
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
".
|
|
1625
|
-
".md",
|
|
2118
|
+
// src/DocBlocksShell/import-file-types.ts
|
|
2119
|
+
var OUTSIDE_IN_IMPORT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
2120
|
+
".csv",
|
|
2121
|
+
".docx",
|
|
1626
2122
|
".html",
|
|
1627
2123
|
".htm",
|
|
1628
|
-
".docx",
|
|
1629
2124
|
".pdf",
|
|
1630
2125
|
".pptx",
|
|
1631
|
-
".xlsx"
|
|
1632
|
-
|
|
1633
|
-
|
|
2126
|
+
".xlsx"
|
|
2127
|
+
]);
|
|
2128
|
+
var BUNDLE_IMPORT_EXTENSIONS = /* @__PURE__ */ new Set([".dbk", ".zip"]);
|
|
2129
|
+
var DIRECT_IMPORT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
2130
|
+
".avif",
|
|
2131
|
+
".bash",
|
|
2132
|
+
".bmp",
|
|
2133
|
+
".c",
|
|
2134
|
+
".cc",
|
|
2135
|
+
".cpp",
|
|
2136
|
+
".cs",
|
|
2137
|
+
".css",
|
|
2138
|
+
".gif",
|
|
2139
|
+
".go",
|
|
2140
|
+
".h",
|
|
2141
|
+
".hpp",
|
|
2142
|
+
".ico",
|
|
2143
|
+
".ini",
|
|
2144
|
+
".java",
|
|
2145
|
+
".jpeg",
|
|
2146
|
+
".jpg",
|
|
2147
|
+
".js",
|
|
2148
|
+
".json",
|
|
2149
|
+
".jsonc",
|
|
2150
|
+
".jsx",
|
|
2151
|
+
".kt",
|
|
2152
|
+
".kts",
|
|
2153
|
+
".less",
|
|
2154
|
+
".log",
|
|
2155
|
+
".lua",
|
|
2156
|
+
".markdown",
|
|
2157
|
+
".md",
|
|
2158
|
+
".mdown",
|
|
2159
|
+
".mjs",
|
|
2160
|
+
".php",
|
|
2161
|
+
".png",
|
|
2162
|
+
".py",
|
|
2163
|
+
".rb",
|
|
2164
|
+
".rs",
|
|
2165
|
+
".scss",
|
|
2166
|
+
".sh",
|
|
2167
|
+
".sql",
|
|
2168
|
+
".svg",
|
|
2169
|
+
".swift",
|
|
2170
|
+
".toml",
|
|
2171
|
+
".ts",
|
|
2172
|
+
".tsv",
|
|
2173
|
+
".tsx",
|
|
2174
|
+
".txt",
|
|
2175
|
+
".webp",
|
|
2176
|
+
".xml",
|
|
2177
|
+
".yaml",
|
|
2178
|
+
".yml",
|
|
2179
|
+
".zsh"
|
|
1634
2180
|
]);
|
|
2181
|
+
var DIRECT_IMPORT_BASENAMES = /* @__PURE__ */ new Set(["dockerfile", "license", "makefile", "readme"]);
|
|
2182
|
+
function extensionOfFileName(name) {
|
|
2183
|
+
const dot = name.lastIndexOf(".");
|
|
2184
|
+
return dot <= 0 ? "" : name.slice(dot).toLowerCase();
|
|
2185
|
+
}
|
|
2186
|
+
function isSupportedImportFile(file) {
|
|
2187
|
+
const extension = extensionOfFileName(file.name);
|
|
2188
|
+
if (OUTSIDE_IN_IMPORT_EXTENSIONS.has(extension) || BUNDLE_IMPORT_EXTENSIONS.has(extension) || DIRECT_IMPORT_EXTENSIONS.has(extension)) {
|
|
2189
|
+
return true;
|
|
2190
|
+
}
|
|
2191
|
+
const mimeType = file.type.toLowerCase();
|
|
2192
|
+
if (mimeType.startsWith("text/") || mimeType.startsWith("image/")) return true;
|
|
2193
|
+
return !extension && DIRECT_IMPORT_BASENAMES.has(file.name.toLowerCase());
|
|
2194
|
+
}
|
|
2195
|
+
|
|
2196
|
+
// src/FileExplorer/FileExplorer.tsx
|
|
2197
|
+
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1635
2198
|
var INTERNAL_DRAG_TYPE = "application/x-docblocks-entry";
|
|
1636
2199
|
var NEW_ITEM_ERROR_ID = "db-new-item-error";
|
|
2200
|
+
var NEW_FILE_EXTENSIONS = {
|
|
2201
|
+
markdown: ".md",
|
|
2202
|
+
docx: ".docx",
|
|
2203
|
+
xlsx: ".xlsx",
|
|
2204
|
+
pdf: ".pdf",
|
|
2205
|
+
"web-interactive": ".html",
|
|
2206
|
+
"web-static": ".html"
|
|
2207
|
+
};
|
|
2208
|
+
var SELECTABLE_FILE_EXTENSION = /\.(?:md|docx|xlsx|pdf|html?)$/i;
|
|
1637
2209
|
function normalisePath2(path) {
|
|
1638
|
-
return
|
|
2210
|
+
return parseWorkspacePath4(path);
|
|
1639
2211
|
}
|
|
1640
2212
|
function hasEquivalentPath2(paths, path) {
|
|
1641
2213
|
const canonical = normalisePath2(path);
|
|
@@ -1701,6 +2273,7 @@ function FileExplorer({
|
|
|
1701
2273
|
[pinnedPaths]
|
|
1702
2274
|
);
|
|
1703
2275
|
const [newItemName, setNewItemName] = useState4("");
|
|
2276
|
+
const [newFileFormat, setNewFileFormat] = useState4("markdown");
|
|
1704
2277
|
const [uncontrolledSortMode, setUncontrolledSortMode] = useState4("name");
|
|
1705
2278
|
const selectedSortMode = sortMode ?? uncontrolledSortMode;
|
|
1706
2279
|
const selectSortMode = useCallback4(
|
|
@@ -1735,12 +2308,11 @@ function FileExplorer({
|
|
|
1735
2308
|
const hasSupported = useCallback4((dt) => {
|
|
1736
2309
|
for (const item of Array.from(dt.items)) {
|
|
1737
2310
|
if (item.kind !== "file") continue;
|
|
1738
|
-
const
|
|
1739
|
-
if (!
|
|
1740
|
-
|
|
1741
|
-
if (SUPPORTED_EXTENSIONS.has(ext)) return true;
|
|
2311
|
+
const file = item.getAsFile();
|
|
2312
|
+
if (!file) return true;
|
|
2313
|
+
if (isSupportedImportFile(file)) return true;
|
|
1742
2314
|
}
|
|
1743
|
-
return dt.
|
|
2315
|
+
return Array.from(dt.files).some(isSupportedImportFile);
|
|
1744
2316
|
}, []);
|
|
1745
2317
|
const handleDragEnter = useCallback4(
|
|
1746
2318
|
(e) => {
|
|
@@ -1774,11 +2346,8 @@ function FileExplorer({
|
|
|
1774
2346
|
e.preventDefault();
|
|
1775
2347
|
dragCounter.current = 0;
|
|
1776
2348
|
setDragOver(false);
|
|
1777
|
-
const
|
|
1778
|
-
|
|
1779
|
-
return SUPPORTED_EXTENSIONS.has(ext);
|
|
1780
|
-
});
|
|
1781
|
-
if (supported.length > 0) onImportFiles?.(supported);
|
|
2349
|
+
const files = Array.from(e.dataTransfer.files);
|
|
2350
|
+
if (files.length > 0) onImportFiles?.(files);
|
|
1782
2351
|
},
|
|
1783
2352
|
[onImportFiles]
|
|
1784
2353
|
);
|
|
@@ -1817,9 +2386,24 @@ function FileExplorer({
|
|
|
1817
2386
|
setNewItemCreationPending(true);
|
|
1818
2387
|
try {
|
|
1819
2388
|
if (itemType === "file") {
|
|
1820
|
-
const
|
|
2389
|
+
const stem = name.replace(SELECTABLE_FILE_EXTENSION, "");
|
|
2390
|
+
if (!stem) {
|
|
2391
|
+
setNewItemError("Enter a file name before the extension.");
|
|
2392
|
+
return;
|
|
2393
|
+
}
|
|
2394
|
+
const filename = `${stem}${NEW_FILE_EXTENSIONS[newFileFormat]}`;
|
|
1821
2395
|
createdPath = `${prefix}${filename}`;
|
|
1822
|
-
|
|
2396
|
+
if (newFileFormat === "markdown") {
|
|
2397
|
+
await tree.createFile(createdPath, "");
|
|
2398
|
+
} else {
|
|
2399
|
+
if (!provider) throw new Error("Open a workspace before creating a document.");
|
|
2400
|
+
await createNewOutsideInDocument(
|
|
2401
|
+
provider,
|
|
2402
|
+
createdPath,
|
|
2403
|
+
newFileFormat === "web-static" ? "static" : newFileFormat === "web-interactive" ? "interactive" : void 0
|
|
2404
|
+
);
|
|
2405
|
+
await tree.refresh();
|
|
2406
|
+
}
|
|
1823
2407
|
handleSelect(createdPath);
|
|
1824
2408
|
} else if (itemType === "directory") {
|
|
1825
2409
|
await tree.createDirectory(createdPath);
|
|
@@ -1834,7 +2418,7 @@ function FileExplorer({
|
|
|
1834
2418
|
setNewItemName("");
|
|
1835
2419
|
setNewItemType(null);
|
|
1836
2420
|
onTreeChange?.({ type: "create", path: createdPath });
|
|
1837
|
-
}, [newItemName, newItemType, tree, handleSelect
|
|
2421
|
+
}, [newFileFormat, newItemName, newItemType, onTreeChange, provider, tree, handleSelect]);
|
|
1838
2422
|
const handleMoveToWorkspace = useCallback4(async () => {
|
|
1839
2423
|
if (!onMoveToWorkspace || !moveDestinationId || movingToWorkspace) return;
|
|
1840
2424
|
setMovingToWorkspace(true);
|
|
@@ -2196,6 +2780,7 @@ function FileExplorer({
|
|
|
2196
2780
|
disabled: newItemCreationPending,
|
|
2197
2781
|
onClick: () => {
|
|
2198
2782
|
setNewItemError(null);
|
|
2783
|
+
setNewFileFormat("markdown");
|
|
2199
2784
|
setNewItemType("file");
|
|
2200
2785
|
},
|
|
2201
2786
|
title: "New File",
|
|
@@ -2274,7 +2859,7 @@ function FileExplorer({
|
|
|
2274
2859
|
/* @__PURE__ */ jsxs4(
|
|
2275
2860
|
"form",
|
|
2276
2861
|
{
|
|
2277
|
-
className:
|
|
2862
|
+
className: `db-new-item-row db-new-item-row--${newItemType}`,
|
|
2278
2863
|
"aria-busy": newItemCreationPending,
|
|
2279
2864
|
onSubmit: (e) => {
|
|
2280
2865
|
e.preventDefault();
|
|
@@ -2299,14 +2884,41 @@ function FileExplorer({
|
|
|
2299
2884
|
if (e.key === "Escape") {
|
|
2300
2885
|
setNewItemType(null);
|
|
2301
2886
|
setNewItemName("");
|
|
2887
|
+
setNewFileFormat("markdown");
|
|
2302
2888
|
setNewItemError(null);
|
|
2303
2889
|
}
|
|
2304
2890
|
},
|
|
2305
2891
|
autoFocus: true
|
|
2306
2892
|
}
|
|
2307
2893
|
),
|
|
2308
|
-
newItemType === "file"
|
|
2309
|
-
|
|
2894
|
+
newItemType === "file" ? /* @__PURE__ */ jsxs4("div", { className: "db-new-item-file-controls", children: [
|
|
2895
|
+
/* @__PURE__ */ jsxs4(
|
|
2896
|
+
"select",
|
|
2897
|
+
{
|
|
2898
|
+
className: "db-new-item-format",
|
|
2899
|
+
"aria-label": "New file type",
|
|
2900
|
+
value: newFileFormat,
|
|
2901
|
+
disabled: newItemCreationPending,
|
|
2902
|
+
onChange: (event) => {
|
|
2903
|
+
setNewFileFormat(event.currentTarget.value);
|
|
2904
|
+
setNewItemError(null);
|
|
2905
|
+
},
|
|
2906
|
+
children: [
|
|
2907
|
+
/* @__PURE__ */ jsxs4("optgroup", { label: "Document", children: [
|
|
2908
|
+
/* @__PURE__ */ jsx5("option", { value: "markdown", children: "Markdown (.md)" }),
|
|
2909
|
+
/* @__PURE__ */ jsx5("option", { value: "docx", children: "Word document (.docx)" }),
|
|
2910
|
+
/* @__PURE__ */ jsx5("option", { value: "xlsx", children: "Excel workbook (.xlsx)" }),
|
|
2911
|
+
/* @__PURE__ */ jsx5("option", { value: "pdf", children: "PDF document (.pdf)" })
|
|
2912
|
+
] }),
|
|
2913
|
+
/* @__PURE__ */ jsxs4("optgroup", { label: "Web page", children: [
|
|
2914
|
+
/* @__PURE__ */ jsx5("option", { value: "web-interactive", children: "Web page \u2014 Interactive (.html)" }),
|
|
2915
|
+
/* @__PURE__ */ jsx5("option", { value: "web-static", children: "Web page \u2014 Static (.html)" })
|
|
2916
|
+
] })
|
|
2917
|
+
]
|
|
2918
|
+
}
|
|
2919
|
+
),
|
|
2920
|
+
/* @__PURE__ */ jsx5("button", { type: "submit", className: "db-new-item-add", disabled: newItemCreationPending, children: newItemCreationPending ? "Adding\u2026" : "Add" })
|
|
2921
|
+
] }) : /* @__PURE__ */ jsx5("button", { type: "submit", className: "db-new-item-add", disabled: newItemCreationPending, children: newItemCreationPending ? "Adding\u2026" : "Add" })
|
|
2310
2922
|
]
|
|
2311
2923
|
}
|
|
2312
2924
|
),
|
|
@@ -2660,6 +3272,8 @@ function AppMenu({
|
|
|
2660
3272
|
onAccentColorChange,
|
|
2661
3273
|
writeCanvasSettings = DEFAULT_WRITE_CANVAS_PREFERENCES,
|
|
2662
3274
|
onWriteCanvasSettingsChange,
|
|
3275
|
+
proofingPreferences = DEFAULT_PROOFING_PREFERENCES,
|
|
3276
|
+
onProofingPreferencesChange,
|
|
2663
3277
|
versioningPreference = "browser-only",
|
|
2664
3278
|
onVersioningPreferenceChange,
|
|
2665
3279
|
onDownloadAllWorkspaces,
|
|
@@ -2834,6 +3448,13 @@ function AppMenu({
|
|
|
2834
3448
|
onChange: (settings) => onWriteCanvasSettingsChange?.(settings)
|
|
2835
3449
|
}
|
|
2836
3450
|
),
|
|
3451
|
+
/* @__PURE__ */ jsx7(
|
|
3452
|
+
ProofingSettingsControls,
|
|
3453
|
+
{
|
|
3454
|
+
value: proofingPreferences,
|
|
3455
|
+
onChange: (settings) => onProofingPreferencesChange?.(settings)
|
|
3456
|
+
}
|
|
3457
|
+
),
|
|
2837
3458
|
getStorageEstimate && /* @__PURE__ */ jsxs6("fieldset", { className: "db-settings-fieldset", children: [
|
|
2838
3459
|
/* @__PURE__ */ jsx7("legend", { className: "db-settings-legend", children: "Storage" }),
|
|
2839
3460
|
/* @__PURE__ */ jsx7("p", { className: "db-settings-hint", children: storageEstimate ? `DocBlocks documents and app data are using ${formatBytes(
|
|
@@ -3236,7 +3857,7 @@ function useDocumentSession(autoSaveDelayMs = 500) {
|
|
|
3236
3857
|
import { lazy, Suspense } from "react";
|
|
3237
3858
|
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
3238
3859
|
var ExportToolbarControlsImplementation = lazy(
|
|
3239
|
-
() => import("./ExportToolbarControls-
|
|
3860
|
+
() => import("./ExportToolbarControls-QYLBRWM7.js").then((module) => ({
|
|
3240
3861
|
default: module.ExportToolbarControls
|
|
3241
3862
|
}))
|
|
3242
3863
|
);
|
|
@@ -3359,27 +3980,39 @@ function useGit(provider, requestedWorkspaceId, theme) {
|
|
|
3359
3980
|
const available = gitApi !== null && capabilities?.gitAvailable === true;
|
|
3360
3981
|
const [repo, setRepo] = useState10(null);
|
|
3361
3982
|
const [remoteWeb, setRemoteWeb] = useState10(null);
|
|
3983
|
+
const [pendingGrant, setPendingGrant] = useState10(null);
|
|
3984
|
+
const loadRemote = useCallback9(
|
|
3985
|
+
(repositoryId2, isCancelled) => {
|
|
3986
|
+
if (!gitApi) return;
|
|
3987
|
+
void gitApi.listRemotes(repositoryId2).then(
|
|
3988
|
+
(remotes) => {
|
|
3989
|
+
if (isCancelled() || !remotes.ok) return;
|
|
3990
|
+
const first = remotes.value[0];
|
|
3991
|
+
setRemoteWeb(first?.web ?? null);
|
|
3992
|
+
},
|
|
3993
|
+
() => void 0
|
|
3994
|
+
);
|
|
3995
|
+
},
|
|
3996
|
+
[gitApi]
|
|
3997
|
+
);
|
|
3362
3998
|
useEffect9(() => {
|
|
3363
3999
|
setRepo(null);
|
|
3364
4000
|
setRemoteWeb(null);
|
|
4001
|
+
setPendingGrant(null);
|
|
3365
4002
|
if (!gitApi || !workspaceId || !available) return;
|
|
3366
4003
|
let cancelled = false;
|
|
4004
|
+
const isCancelled = () => cancelled;
|
|
3367
4005
|
void gitApi.detectRepo(workspaceId).then(
|
|
3368
4006
|
(result) => {
|
|
3369
|
-
if (cancelled || !result.ok || !result.value.isRepo
|
|
4007
|
+
if (cancelled || !result.ok || !result.value.isRepo) return;
|
|
4008
|
+
if (!result.value.repositoryId) {
|
|
4009
|
+
if (result.value.requiresExpandedGrant) {
|
|
4010
|
+
setPendingGrant({ repositoryRoot: result.value.repositoryRoot ?? null });
|
|
4011
|
+
}
|
|
3370
4012
|
return;
|
|
3371
4013
|
}
|
|
3372
4014
|
setRepo(result.value);
|
|
3373
|
-
|
|
3374
|
-
(remotes) => {
|
|
3375
|
-
if (cancelled || !remotes.ok) return;
|
|
3376
|
-
const first = remotes.value[0];
|
|
3377
|
-
setRemoteWeb(first?.web ?? null);
|
|
3378
|
-
},
|
|
3379
|
-
// No remote is a supported state; a failed probe degrades to it
|
|
3380
|
-
// rather than surfacing an unhandled rejection.
|
|
3381
|
-
() => void 0
|
|
3382
|
-
);
|
|
4015
|
+
loadRemote(result.value.repositoryId, isCancelled);
|
|
3383
4016
|
},
|
|
3384
4017
|
/* detection failure → not a repo, which is the default state */
|
|
3385
4018
|
() => void 0
|
|
@@ -3387,7 +4020,25 @@ function useGit(provider, requestedWorkspaceId, theme) {
|
|
|
3387
4020
|
return () => {
|
|
3388
4021
|
cancelled = true;
|
|
3389
4022
|
};
|
|
3390
|
-
}, [gitApi, workspaceId, available]);
|
|
4023
|
+
}, [gitApi, workspaceId, available, loadRemote]);
|
|
4024
|
+
const [grantBusy, setGrantBusy] = useState10(false);
|
|
4025
|
+
const enableExpandedRepo = useCallback9(
|
|
4026
|
+
async (opts) => {
|
|
4027
|
+
if (!gitApi || !workspaceId) return false;
|
|
4028
|
+
setGrantBusy(true);
|
|
4029
|
+
try {
|
|
4030
|
+
const result = await gitApi.grantExpandedRepo(workspaceId, opts);
|
|
4031
|
+
if (!result.ok || !result.value.repositoryId) return false;
|
|
4032
|
+
setRepo(result.value);
|
|
4033
|
+
setPendingGrant(null);
|
|
4034
|
+
loadRemote(result.value.repositoryId, () => false);
|
|
4035
|
+
return true;
|
|
4036
|
+
} finally {
|
|
4037
|
+
setGrantBusy(false);
|
|
4038
|
+
}
|
|
4039
|
+
},
|
|
4040
|
+
[gitApi, workspaceId, loadRemote]
|
|
4041
|
+
);
|
|
3391
4042
|
const repositoryId = repo?.repositoryId ?? null;
|
|
3392
4043
|
const isRepo = repositoryId !== null;
|
|
3393
4044
|
const { status, refresh, scheduleRefresh } = useGitStatus(gitApi, repositoryId, isRepo);
|
|
@@ -3536,6 +4187,9 @@ function useGit(provider, requestedWorkspaceId, theme) {
|
|
|
3536
4187
|
capabilities,
|
|
3537
4188
|
repo,
|
|
3538
4189
|
repositoryId,
|
|
4190
|
+
pendingGrant,
|
|
4191
|
+
grantBusy,
|
|
4192
|
+
enableExpandedRepo,
|
|
3539
4193
|
remoteWeb,
|
|
3540
4194
|
gitApi,
|
|
3541
4195
|
provider,
|
|
@@ -3563,6 +4217,9 @@ function useGit(provider, requestedWorkspaceId, theme) {
|
|
|
3563
4217
|
capabilities,
|
|
3564
4218
|
repo,
|
|
3565
4219
|
repositoryId,
|
|
4220
|
+
pendingGrant,
|
|
4221
|
+
grantBusy,
|
|
4222
|
+
enableExpandedRepo,
|
|
3566
4223
|
remoteWeb,
|
|
3567
4224
|
gitApi,
|
|
3568
4225
|
provider,
|
|
@@ -3749,163 +4406,15 @@ function hasControlCharacter(value) {
|
|
|
3749
4406
|
const code = value.charCodeAt(index);
|
|
3750
4407
|
if (code <= 31 || code === 127) return true;
|
|
3751
4408
|
}
|
|
3752
|
-
return false;
|
|
3753
|
-
}
|
|
3754
|
-
|
|
3755
|
-
// src/DocBlocksShell/import-files.ts
|
|
3756
|
-
import {
|
|
3757
|
-
FsError as FsError3,
|
|
3758
|
-
getFileSystemProviderV2 as getFileSystemProviderV23,
|
|
3759
|
-
parseWorkspacePath as parseWorkspacePath4
|
|
3760
|
-
} from "@bendyline/docblocks/filesystem";
|
|
3761
|
-
|
|
3762
|
-
// src/DocBlocksShell/outside-in-contract.ts
|
|
3763
|
-
var OUTSIDE_IN_FORMAT_IDS = ["html", "docx", "pdf", "pptx", "xlsx"];
|
|
3764
|
-
var FORMAT_IDS = new Set(OUTSIDE_IN_FORMAT_IDS);
|
|
3765
|
-
var UPDATE_FROM_MARKDOWN_KEY = "squisq-updatefrommarkdown";
|
|
3766
|
-
function normalizePath(path) {
|
|
3767
|
-
const leading = path.replace(/\\/g, "/").startsWith("/") ? "/" : "";
|
|
3768
|
-
const parts = path.replace(/\\/g, "/").split("/").filter(Boolean);
|
|
3769
|
-
if (parts.some((part) => part === "." || part === "..")) {
|
|
3770
|
-
throw new Error(`Outside-in paths must be canonical workspace paths: ${path}`);
|
|
3771
|
-
}
|
|
3772
|
-
return leading + parts.join("/");
|
|
3773
|
-
}
|
|
3774
|
-
function join(parent, child) {
|
|
3775
|
-
if (!parent || parent === "/") return parent === "/" ? `/${child}` : child;
|
|
3776
|
-
return `${parent}/${child}`;
|
|
3777
|
-
}
|
|
3778
|
-
function slug(stem) {
|
|
3779
|
-
return stem.normalize("NFKD").replace(/\p{Mark}+/gu, "").toLocaleLowerCase("en-US").replace(/[^\p{Letter}\p{Number}]+/gu, "-").replace(/^-+|-+$/g, "") || "document";
|
|
3780
|
-
}
|
|
3781
|
-
function resolveOutsideInLayout(path) {
|
|
3782
|
-
const targetPath = normalizePath(path);
|
|
3783
|
-
const slash = targetPath.lastIndexOf("/");
|
|
3784
|
-
const parentDirectory = slash < 0 ? "" : slash === 0 ? "/" : targetPath.slice(0, slash);
|
|
3785
|
-
const filename = slash < 0 ? targetPath : targetPath.slice(slash + 1);
|
|
3786
|
-
const dot = filename.lastIndexOf(".");
|
|
3787
|
-
if (dot <= 0) return null;
|
|
3788
|
-
const rawFormat = filename.slice(dot + 1).toLowerCase();
|
|
3789
|
-
const format = rawFormat === "htm" ? "html" : rawFormat;
|
|
3790
|
-
if (!FORMAT_IDS.has(format)) return null;
|
|
3791
|
-
const stem = filename.slice(0, dot);
|
|
3792
|
-
const companionName = `${stem}_files`;
|
|
3793
|
-
const companionDirectory = join(parentDirectory, companionName);
|
|
3794
|
-
const markdownFilename = `${slug(stem)}.md`;
|
|
3795
|
-
const backupDirectory = join(companionDirectory, ".original");
|
|
3796
|
-
const backupFilename = `original.${format}`;
|
|
3797
|
-
return {
|
|
3798
|
-
targetPath,
|
|
3799
|
-
format,
|
|
3800
|
-
parentDirectory,
|
|
3801
|
-
stem,
|
|
3802
|
-
companionName,
|
|
3803
|
-
companionDirectory,
|
|
3804
|
-
markdownFilename,
|
|
3805
|
-
markdownPath: join(companionDirectory, markdownFilename),
|
|
3806
|
-
relativeTargetPath: `../${filename}`,
|
|
3807
|
-
backupDirectory,
|
|
3808
|
-
backupFilename,
|
|
3809
|
-
backupPath: join(backupDirectory, backupFilename)
|
|
3810
|
-
};
|
|
3811
|
-
}
|
|
3812
|
-
function chooseOutsideInMarkdownPath(layout, paths) {
|
|
3813
|
-
const canonical = normalizePath(layout.markdownPath);
|
|
3814
|
-
const normalized = paths.map(normalizePath);
|
|
3815
|
-
const exact = normalized.find((path) => path === canonical);
|
|
3816
|
-
if (exact) return exact;
|
|
3817
|
-
const folded = normalized.find(
|
|
3818
|
-
(path) => path.toLocaleLowerCase("en-US") === canonical.toLocaleLowerCase("en-US")
|
|
3819
|
-
);
|
|
3820
|
-
if (folded) return folded;
|
|
3821
|
-
const prefix = `${normalizePath(layout.companionDirectory).replace(/\/$/, "")}/`;
|
|
3822
|
-
const markdown = normalized.filter(
|
|
3823
|
-
(path) => path.startsWith(prefix) && !path.slice(prefix.length).includes("/") && path.toLocaleLowerCase("en-US").endsWith(".md")
|
|
3824
|
-
);
|
|
3825
|
-
return markdown.length === 1 ? markdown[0] : null;
|
|
3826
|
-
}
|
|
3827
|
-
async function readOutsideInMetadata(source) {
|
|
3828
|
-
const { readOutsideInMetadata: readMetadata } = await import("@bendyline/squisq-formats/outside-in");
|
|
3829
|
-
const metadata = readMetadata(source);
|
|
3830
|
-
return metadata ? {
|
|
3831
|
-
...metadata,
|
|
3832
|
-
updateFromMarkdown: await isOutsideInMarkdownEditingEnabled(source)
|
|
3833
|
-
} : null;
|
|
3834
|
-
}
|
|
3835
|
-
async function withOutsideInMetadata(source, layout) {
|
|
3836
|
-
const { withOutsideInMetadata: addMetadata } = await import("@bendyline/squisq-formats/outside-in");
|
|
3837
|
-
return addMetadata(source, layout);
|
|
3838
|
-
}
|
|
3839
|
-
async function isOutsideInMarkdownEditingEnabled(source) {
|
|
3840
|
-
const module = await import("@bendyline/squisq-formats/outside-in");
|
|
3841
|
-
if (module.isOutsideInMarkdownEditingEnabled) {
|
|
3842
|
-
return module.isOutsideInMarkdownEditingEnabled(source);
|
|
3843
|
-
}
|
|
3844
|
-
const { parseFrontmatter, splitFrontmatterBlock } = await import("@bendyline/squisq/markdown");
|
|
3845
|
-
const block = splitFrontmatterBlock(source).frontmatter;
|
|
3846
|
-
if (!block) return false;
|
|
3847
|
-
const firstBreak = block.indexOf("\n");
|
|
3848
|
-
if (firstBreak < 0) return false;
|
|
3849
|
-
const yaml = block.slice(firstBreak + 1).replace(/\r?\n---(?:\r?\n)?$/, "");
|
|
3850
|
-
return parseFrontmatter(yaml)?.[UPDATE_FROM_MARKDOWN_KEY] === true;
|
|
3851
|
-
}
|
|
3852
|
-
async function withOutsideInMarkdownEditing(source, layout, enabled = true) {
|
|
3853
|
-
const module = await import("@bendyline/squisq-formats/outside-in");
|
|
3854
|
-
if (module.withOutsideInMarkdownEditing) {
|
|
3855
|
-
return module.withOutsideInMarkdownEditing(source, layout, enabled);
|
|
3856
|
-
}
|
|
3857
|
-
const { setFrontmatterValues } = await import("@bendyline/squisq/markdown");
|
|
3858
|
-
return setFrontmatterValues(await withOutsideInMetadata(source, layout), {
|
|
3859
|
-
[UPDATE_FROM_MARKDOWN_KEY]: enabled
|
|
3860
|
-
});
|
|
3861
|
-
}
|
|
3862
|
-
async function importOutsideInDocument(source, options = {}) {
|
|
3863
|
-
const { importOutsideInDocument: importDocument } = await import("@bendyline/squisq-formats/outside-in");
|
|
3864
|
-
const imported = await importDocument(source, options);
|
|
3865
|
-
const layout = resolveOutsideInLayout(source.targetPath);
|
|
3866
|
-
if (!layout) throw new Error(`Outside-in editing does not support "${source.targetPath}".`);
|
|
3867
|
-
return { ...imported, layout };
|
|
3868
|
-
}
|
|
3869
|
-
async function renderOutsideInDocument(source, options = {}) {
|
|
3870
|
-
const { renderOutsideInDocument: renderDocument } = await import("@bendyline/squisq-formats/outside-in");
|
|
3871
|
-
return renderDocument(source, options);
|
|
3872
|
-
}
|
|
3873
|
-
|
|
3874
|
-
// src/DocBlocksShell/provider-io.ts
|
|
3875
|
-
import {
|
|
3876
|
-
FsError as FsError2,
|
|
3877
|
-
getFileSystemProviderV2 as getFileSystemProviderV22,
|
|
3878
|
-
parseWorkspacePath as parseWorkspacePath3
|
|
3879
|
-
} from "@bendyline/docblocks/filesystem";
|
|
3880
|
-
async function providerEntryExists(provider, path) {
|
|
3881
|
-
const providerV2 = getFileSystemProviderV22(provider);
|
|
3882
|
-
return providerV2 ? await providerV2.stat(parseWorkspacePath3(path)) !== null : provider.exists(path);
|
|
3883
|
-
}
|
|
3884
|
-
async function writeProviderText(provider, path, content, mode = "upsert") {
|
|
3885
|
-
const providerV2 = getFileSystemProviderV22(provider);
|
|
3886
|
-
if (providerV2) {
|
|
3887
|
-
await providerV2.writeFile(parseWorkspacePath3(path), new TextEncoder().encode(content), {
|
|
3888
|
-
mode,
|
|
3889
|
-
createParents: true,
|
|
3890
|
-
expectedVersion: mode === "create" ? null : void 0
|
|
3891
|
-
});
|
|
3892
|
-
return;
|
|
3893
|
-
}
|
|
3894
|
-
if (mode === "create" && await provider.exists(path)) {
|
|
3895
|
-
throw new FsError2("already-exists", "File already exists.", { operation: "write", path });
|
|
3896
|
-
}
|
|
3897
|
-
await provider.writeFile(path, content);
|
|
3898
|
-
}
|
|
3899
|
-
async function removeProviderEntry(provider, path) {
|
|
3900
|
-
const providerV2 = getFileSystemProviderV22(provider);
|
|
3901
|
-
if (providerV2) {
|
|
3902
|
-
await providerV2.remove(parseWorkspacePath3(path), { recursive: true, missing: "ignore" });
|
|
3903
|
-
return;
|
|
3904
|
-
}
|
|
3905
|
-
await provider.delete(path);
|
|
4409
|
+
return false;
|
|
3906
4410
|
}
|
|
3907
4411
|
|
|
3908
4412
|
// src/DocBlocksShell/import-files.ts
|
|
4413
|
+
import {
|
|
4414
|
+
FsError as FsError4,
|
|
4415
|
+
getFileSystemProviderV2 as getFileSystemProviderV24,
|
|
4416
|
+
parseWorkspacePath as parseWorkspacePath5
|
|
4417
|
+
} from "@bendyline/docblocks/filesystem";
|
|
3909
4418
|
var MAX_NAME_ATTEMPTS = 100;
|
|
3910
4419
|
function withCopySuffix(path, n) {
|
|
3911
4420
|
const dot = path.lastIndexOf(".");
|
|
@@ -3914,7 +4423,7 @@ function withCopySuffix(path, n) {
|
|
|
3914
4423
|
return `${path.slice(0, dot)} (${n})${path.slice(dot)}`;
|
|
3915
4424
|
}
|
|
3916
4425
|
function isAlreadyExists(error) {
|
|
3917
|
-
return error instanceof
|
|
4426
|
+
return error instanceof FsError4 && error.code === "already-exists";
|
|
3918
4427
|
}
|
|
3919
4428
|
async function claimAvailablePath(provider, desiredPath) {
|
|
3920
4429
|
for (let attempt = 1; attempt <= MAX_NAME_ATTEMPTS; attempt++) {
|
|
@@ -3929,9 +4438,9 @@ async function claimAvailablePath(provider, desiredPath) {
|
|
|
3929
4438
|
throw new Error(`Too many documents are already named like \u201C${desiredPath}\u201D.`);
|
|
3930
4439
|
}
|
|
3931
4440
|
async function writeProviderBytes(provider, path, data, mode) {
|
|
3932
|
-
const providerV2 =
|
|
4441
|
+
const providerV2 = getFileSystemProviderV24(provider);
|
|
3933
4442
|
if (providerV2) {
|
|
3934
|
-
await providerV2.writeFile(
|
|
4443
|
+
await providerV2.writeFile(parseWorkspacePath5(path), data, {
|
|
3935
4444
|
mode,
|
|
3936
4445
|
createParents: true,
|
|
3937
4446
|
expectedVersion: mode === "create" ? null : void 0
|
|
@@ -3939,11 +4448,10 @@ async function writeProviderBytes(provider, path, data, mode) {
|
|
|
3939
4448
|
return;
|
|
3940
4449
|
}
|
|
3941
4450
|
if (mode === "create" && await providerEntryExists(provider, path)) {
|
|
3942
|
-
throw new
|
|
4451
|
+
throw new FsError4("already-exists", "File already exists.", { operation: "write", path });
|
|
3943
4452
|
}
|
|
3944
4453
|
await provider.writeBinary(path, data);
|
|
3945
4454
|
}
|
|
3946
|
-
var OUTSIDE_IN_EXTENSIONS = /* @__PURE__ */ new Set([".html", ".htm", ".docx", ".pdf", ".pptx", ".xlsx"]);
|
|
3947
4455
|
async function claimOutsideInTarget(provider, desiredPath) {
|
|
3948
4456
|
for (let attempt = 1; attempt <= MAX_NAME_ATTEMPTS; attempt++) {
|
|
3949
4457
|
const candidate = attempt === 1 ? desiredPath : withCopySuffix(desiredPath, attempt);
|
|
@@ -3987,10 +4495,7 @@ async function importOutsideInFile(file, provider) {
|
|
|
3987
4495
|
throw error;
|
|
3988
4496
|
}
|
|
3989
4497
|
}
|
|
3990
|
-
async function readImportedMarkdown(file,
|
|
3991
|
-
if (ext === ".md" || ext === ".txt") {
|
|
3992
|
-
return file.text();
|
|
3993
|
-
}
|
|
4498
|
+
async function readImportedMarkdown(file, destPath, provider) {
|
|
3994
4499
|
const snapshot = await decodeDbkWorkspace(await file.arrayBuffer(), {
|
|
3995
4500
|
targetDocumentPath: destPath
|
|
3996
4501
|
});
|
|
@@ -4004,27 +4509,31 @@ async function writeDbkCompanions(snapshot, provider) {
|
|
|
4004
4509
|
await writeProviderText(provider, entry.path, entry.content, "create");
|
|
4005
4510
|
}
|
|
4006
4511
|
}
|
|
4007
|
-
var SUPPORTED_EXTENSIONS2 = /* @__PURE__ */ new Set([".md", ".txt", ...OUTSIDE_IN_EXTENSIONS, ".dbk", ".zip"]);
|
|
4008
4512
|
async function importDroppedFiles(files, provider) {
|
|
4009
4513
|
const result = { imported: [], failed: [], unsupported: [] };
|
|
4010
4514
|
for (const file of files) {
|
|
4011
|
-
const ext =
|
|
4012
|
-
if (!
|
|
4515
|
+
const ext = extensionOfFileName(file.name);
|
|
4516
|
+
if (!isSupportedImportFile(file)) {
|
|
4013
4517
|
result.unsupported.push(file.name);
|
|
4014
4518
|
continue;
|
|
4015
4519
|
}
|
|
4016
|
-
const baseName = file.name.replace(/\.[^.]+$/, "");
|
|
4017
4520
|
let claimed = null;
|
|
4018
4521
|
try {
|
|
4019
|
-
if (
|
|
4522
|
+
if (OUTSIDE_IN_IMPORT_EXTENSIONS.has(ext)) {
|
|
4020
4523
|
const imported = await importOutsideInFile(file, provider);
|
|
4021
4524
|
result.imported.push({ source: file.name, ...imported });
|
|
4022
4525
|
continue;
|
|
4023
4526
|
}
|
|
4024
|
-
const
|
|
4527
|
+
const isBundle = BUNDLE_IMPORT_EXTENSIONS.has(ext);
|
|
4528
|
+
const desiredPath = isBundle ? `${file.name.replace(/\.[^.]+$/, "")}.md` : file.name;
|
|
4529
|
+
const claim = await claimAvailablePath(provider, desiredPath);
|
|
4025
4530
|
claimed = claim.path;
|
|
4026
|
-
|
|
4027
|
-
|
|
4531
|
+
if (isBundle) {
|
|
4532
|
+
const markdown = await readImportedMarkdown(file, claim.path, provider);
|
|
4533
|
+
await writeProviderText(provider, claim.path, markdown);
|
|
4534
|
+
} else {
|
|
4535
|
+
await writeProviderBytes(provider, claim.path, await file.arrayBuffer(), "upsert");
|
|
4536
|
+
}
|
|
4028
4537
|
result.imported.push({ source: file.name, path: claim.path, renamed: claim.renamed });
|
|
4029
4538
|
} catch (error) {
|
|
4030
4539
|
if (claimed) {
|
|
@@ -4040,16 +4549,13 @@ async function importDroppedFiles(files, provider) {
|
|
|
4040
4549
|
}
|
|
4041
4550
|
function summariseImport(result) {
|
|
4042
4551
|
const { imported, failed, unsupported } = result;
|
|
4043
|
-
|
|
4044
|
-
|
|
4552
|
+
const rejectedCount = failed.length + unsupported.length;
|
|
4553
|
+
if (rejectedCount > 0) {
|
|
4554
|
+
const detail = failed.length === 1 && unsupported.length === 0 ? `Could not import ${failed[0].source} \u2014 ${failed[0].message}` : unsupported.length === 1 && failed.length === 0 ? `${unsupported[0]} is not a file type DocBlocks can import.` : `Could not import ${rejectedCount} of ${rejectedCount + imported.length} files.`;
|
|
4045
4555
|
return { kind: "error", message: detail };
|
|
4046
4556
|
}
|
|
4047
4557
|
if (imported.length === 0) {
|
|
4048
|
-
|
|
4049
|
-
return {
|
|
4050
|
-
kind: "error",
|
|
4051
|
-
message: unsupported.length === 1 ? `${unsupported[0]} is not a file type DocBlocks can import.` : `${unsupported.length} dropped files are not file types DocBlocks can import.`
|
|
4052
|
-
};
|
|
4558
|
+
return null;
|
|
4053
4559
|
}
|
|
4054
4560
|
const renamed = imported.filter((entry) => entry.renamed);
|
|
4055
4561
|
if (renamed.length === 1) {
|
|
@@ -4067,241 +4573,6 @@ function summariseImport(result) {
|
|
|
4067
4573
|
return null;
|
|
4068
4574
|
}
|
|
4069
4575
|
|
|
4070
|
-
// src/DocBlocksShell/outside-in.ts
|
|
4071
|
-
import {
|
|
4072
|
-
FileSystemContentContainer,
|
|
4073
|
-
FsError as FsError4,
|
|
4074
|
-
getFileSystemProviderV2 as getFileSystemProviderV24,
|
|
4075
|
-
parseWorkspacePath as parseWorkspacePath5
|
|
4076
|
-
} from "@bendyline/docblocks/filesystem";
|
|
4077
|
-
import {
|
|
4078
|
-
createFileSystemDocumentTarget
|
|
4079
|
-
} from "@bendyline/docblocks/document";
|
|
4080
|
-
var OUTSIDE_IN_EXTENSION = /\.(?:html?|docx|pdf|pptx|xlsx)$/i;
|
|
4081
|
-
var SQUISQ_RUNTIME_DIRECTORY = "_squisq";
|
|
4082
|
-
var SQUISQ_RUNTIME_FILENAME = "squisq-player.js";
|
|
4083
|
-
function withoutLeadingSlash(path) {
|
|
4084
|
-
return parseWorkspacePath5(path);
|
|
4085
|
-
}
|
|
4086
|
-
function withLegacySlash(path, like) {
|
|
4087
|
-
const canonical = withoutLeadingSlash(path);
|
|
4088
|
-
return like.startsWith("/") && canonical ? `/${canonical}` : canonical;
|
|
4089
|
-
}
|
|
4090
|
-
function dirname(path) {
|
|
4091
|
-
const canonical = withoutLeadingSlash(path);
|
|
4092
|
-
const slash = canonical.lastIndexOf("/");
|
|
4093
|
-
return slash < 0 ? "" : canonical.slice(0, slash);
|
|
4094
|
-
}
|
|
4095
|
-
function join2(parent, child) {
|
|
4096
|
-
return parent ? `${parent}/${child}` : child;
|
|
4097
|
-
}
|
|
4098
|
-
function relativePath(fromDirectory, targetPath) {
|
|
4099
|
-
const from = withoutLeadingSlash(fromDirectory).split("/").filter(Boolean);
|
|
4100
|
-
const target = withoutLeadingSlash(targetPath).split("/").filter(Boolean);
|
|
4101
|
-
let shared = 0;
|
|
4102
|
-
while (shared < from.length && shared < target.length && from[shared] === target[shared]) {
|
|
4103
|
-
shared++;
|
|
4104
|
-
}
|
|
4105
|
-
const segments = [...from.slice(shared).map(() => ".."), ...target.slice(shared)];
|
|
4106
|
-
return segments.join("/") || ".";
|
|
4107
|
-
}
|
|
4108
|
-
async function readText(provider, path) {
|
|
4109
|
-
const v2 = getFileSystemProviderV24(provider);
|
|
4110
|
-
if (!v2) return provider.readFile(path);
|
|
4111
|
-
const current = await v2.readFile(parseWorkspacePath5(path));
|
|
4112
|
-
if (!current) return null;
|
|
4113
|
-
return new TextDecoder("utf-8", { fatal: true }).decode(current.data);
|
|
4114
|
-
}
|
|
4115
|
-
async function readBytes(provider, path) {
|
|
4116
|
-
const v2 = getFileSystemProviderV24(provider);
|
|
4117
|
-
if (v2) return (await v2.readFile(parseWorkspacePath5(path)))?.data ?? null;
|
|
4118
|
-
return provider.readBinary(path);
|
|
4119
|
-
}
|
|
4120
|
-
async function writeBytes(provider, path, data, mode = "upsert") {
|
|
4121
|
-
const v2 = getFileSystemProviderV24(provider);
|
|
4122
|
-
if (v2) {
|
|
4123
|
-
await v2.writeFile(parseWorkspacePath5(path), data, {
|
|
4124
|
-
mode,
|
|
4125
|
-
createParents: true,
|
|
4126
|
-
expectedVersion: mode === "create" ? null : void 0
|
|
4127
|
-
});
|
|
4128
|
-
return;
|
|
4129
|
-
}
|
|
4130
|
-
if (mode === "create" && await provider.exists(path)) {
|
|
4131
|
-
throw new FsError4("already-exists", "File already exists.", { operation: "write", path });
|
|
4132
|
-
}
|
|
4133
|
-
await provider.writeBinary(path, data);
|
|
4134
|
-
}
|
|
4135
|
-
async function listCompanionFiles(provider, layout) {
|
|
4136
|
-
try {
|
|
4137
|
-
const entries = await provider.readDirectory(layout.companionDirectory);
|
|
4138
|
-
return entries.filter((entry) => entry.kind === "file").map((entry) => entry.path);
|
|
4139
|
-
} catch (error) {
|
|
4140
|
-
if (error instanceof FsError4 && error.code === "not-found") return [];
|
|
4141
|
-
throw error;
|
|
4142
|
-
}
|
|
4143
|
-
}
|
|
4144
|
-
async function removeOutsideInCompanion(provider, layout) {
|
|
4145
|
-
const providerV2 = getFileSystemProviderV24(provider);
|
|
4146
|
-
if (providerV2) {
|
|
4147
|
-
const path = parseWorkspacePath5(layout.companionDirectory);
|
|
4148
|
-
if (await providerV2.stat(path) !== null) {
|
|
4149
|
-
await providerV2.remove(path, { recursive: true, missing: "ignore" });
|
|
4150
|
-
}
|
|
4151
|
-
return;
|
|
4152
|
-
}
|
|
4153
|
-
if (await provider.exists(layout.companionDirectory)) {
|
|
4154
|
-
await provider.delete(layout.companionDirectory);
|
|
4155
|
-
}
|
|
4156
|
-
}
|
|
4157
|
-
async function persistImportedMedia(provider, layout, container) {
|
|
4158
|
-
const entries = await container.listFiles();
|
|
4159
|
-
for (const entry of entries) {
|
|
4160
|
-
if (/\.md$/i.test(entry.path)) continue;
|
|
4161
|
-
const data = await container.readFile(entry.path);
|
|
4162
|
-
if (!data) continue;
|
|
4163
|
-
await writeBytes(provider, join2(layout.companionDirectory, entry.path), data, "create");
|
|
4164
|
-
}
|
|
4165
|
-
}
|
|
4166
|
-
async function loadEditableShellDocument(provider, selectedPath) {
|
|
4167
|
-
if (!OUTSIDE_IN_EXTENSION.test(selectedPath)) {
|
|
4168
|
-
const content = await readText(provider, selectedPath);
|
|
4169
|
-
return content === null ? null : {
|
|
4170
|
-
displayPath: selectedPath,
|
|
4171
|
-
sourcePath: selectedPath,
|
|
4172
|
-
content,
|
|
4173
|
-
outsideIn: null,
|
|
4174
|
-
outsideInEditingEnabled: true
|
|
4175
|
-
};
|
|
4176
|
-
}
|
|
4177
|
-
const resolved = resolveOutsideInLayout(selectedPath);
|
|
4178
|
-
if (!resolved) return null;
|
|
4179
|
-
const layout = {
|
|
4180
|
-
...resolved,
|
|
4181
|
-
targetPath: withLegacySlash(resolved.targetPath, selectedPath),
|
|
4182
|
-
companionDirectory: withLegacySlash(resolved.companionDirectory, selectedPath),
|
|
4183
|
-
markdownPath: withLegacySlash(resolved.markdownPath, selectedPath)
|
|
4184
|
-
};
|
|
4185
|
-
const candidates = (await listCompanionFiles(provider, layout)).map(
|
|
4186
|
-
(path) => withLegacySlash(path, selectedPath)
|
|
4187
|
-
);
|
|
4188
|
-
const chosen = chooseOutsideInMarkdownPath(layout, candidates);
|
|
4189
|
-
if (chosen) {
|
|
4190
|
-
const content = await readText(provider, chosen);
|
|
4191
|
-
if (content === null) return null;
|
|
4192
|
-
const metadata = await readOutsideInMetadata(content);
|
|
4193
|
-
if (metadata && metadata.format !== layout.format) {
|
|
4194
|
-
throw new Error(
|
|
4195
|
-
`${chosen} is configured for ${metadata.format}, not ${layout.format}. Rename the rendered file back or repair the companion frontmatter.`
|
|
4196
|
-
);
|
|
4197
|
-
}
|
|
4198
|
-
const linkedContent = await withOutsideInMetadata(content, layout);
|
|
4199
|
-
if (linkedContent !== content) await writeProviderText(provider, chosen, linkedContent);
|
|
4200
|
-
return {
|
|
4201
|
-
displayPath: selectedPath,
|
|
4202
|
-
sourcePath: chosen,
|
|
4203
|
-
content: linkedContent,
|
|
4204
|
-
outsideIn: { ...layout, markdownPath: chosen },
|
|
4205
|
-
outsideInEditingEnabled: await isOutsideInMarkdownEditingEnabled(linkedContent)
|
|
4206
|
-
};
|
|
4207
|
-
}
|
|
4208
|
-
const rendered = await readBytes(provider, selectedPath);
|
|
4209
|
-
if (!rendered) return null;
|
|
4210
|
-
const imported = await importOutsideInDocument({
|
|
4211
|
-
data: rendered,
|
|
4212
|
-
targetPath: selectedPath
|
|
4213
|
-
});
|
|
4214
|
-
const importedLayout = {
|
|
4215
|
-
...imported.layout,
|
|
4216
|
-
targetPath: withLegacySlash(imported.layout.targetPath, selectedPath),
|
|
4217
|
-
companionDirectory: withLegacySlash(imported.layout.companionDirectory, selectedPath),
|
|
4218
|
-
markdownPath: withLegacySlash(imported.layout.markdownPath, selectedPath)
|
|
4219
|
-
};
|
|
4220
|
-
await persistImportedMedia(provider, importedLayout, imported.container);
|
|
4221
|
-
await writeProviderText(provider, importedLayout.markdownPath, imported.markdown, "create");
|
|
4222
|
-
return {
|
|
4223
|
-
displayPath: selectedPath,
|
|
4224
|
-
sourcePath: importedLayout.markdownPath,
|
|
4225
|
-
content: imported.markdown,
|
|
4226
|
-
outsideIn: importedLayout,
|
|
4227
|
-
outsideInEditingEnabled: false
|
|
4228
|
-
};
|
|
4229
|
-
}
|
|
4230
|
-
async function enableOutsideInMarkdownEditing(provider, document2) {
|
|
4231
|
-
const layout = document2.outsideIn;
|
|
4232
|
-
if (!layout) throw new Error("This file does not support outside-in Markdown editing.");
|
|
4233
|
-
const original = await readBytes(provider, layout.targetPath);
|
|
4234
|
-
if (!original) throw new Error(`The rendered file "${layout.targetPath}" was not found.`);
|
|
4235
|
-
try {
|
|
4236
|
-
await writeBytes(provider, layout.backupPath, original, "create");
|
|
4237
|
-
} catch (error) {
|
|
4238
|
-
if (!(error instanceof FsError4 && error.code === "already-exists")) throw error;
|
|
4239
|
-
}
|
|
4240
|
-
const content = await withOutsideInMarkdownEditing(document2.content, layout);
|
|
4241
|
-
if (content !== document2.content) await writeProviderText(provider, document2.sourcePath, content);
|
|
4242
|
-
return { ...document2, content, outsideIn: layout, outsideInEditingEnabled: true };
|
|
4243
|
-
}
|
|
4244
|
-
async function findRuntimePath(provider, targetPath) {
|
|
4245
|
-
let directory = dirname(targetPath);
|
|
4246
|
-
for (; ; ) {
|
|
4247
|
-
const candidateDirectory = join2(directory, SQUISQ_RUNTIME_DIRECTORY);
|
|
4248
|
-
if (await providerEntryExists(provider, candidateDirectory)) {
|
|
4249
|
-
const providerV2 = getFileSystemProviderV24(provider);
|
|
4250
|
-
if (providerV2) {
|
|
4251
|
-
const entry = await providerV2.stat(parseWorkspacePath5(candidateDirectory));
|
|
4252
|
-
if (entry?.kind !== "directory") {
|
|
4253
|
-
throw new Error(`${candidateDirectory} must be a directory.`);
|
|
4254
|
-
}
|
|
4255
|
-
}
|
|
4256
|
-
return join2(candidateDirectory, SQUISQ_RUNTIME_FILENAME);
|
|
4257
|
-
}
|
|
4258
|
-
if (!directory) break;
|
|
4259
|
-
directory = dirname(directory);
|
|
4260
|
-
}
|
|
4261
|
-
return join2(SQUISQ_RUNTIME_DIRECTORY, SQUISQ_RUNTIME_FILENAME);
|
|
4262
|
-
}
|
|
4263
|
-
async function writeRuntimeIfNeeded(provider, runtimePath) {
|
|
4264
|
-
const { PLAYER_BUNDLE } = await import("@bendyline/squisq-react/standalone-source");
|
|
4265
|
-
const current = await readText(provider, runtimePath);
|
|
4266
|
-
if (current !== PLAYER_BUNDLE) await writeProviderText(provider, runtimePath, PLAYER_BUNDLE);
|
|
4267
|
-
}
|
|
4268
|
-
function createOutsideInDocumentTarget(provider, layout, onCommitted) {
|
|
4269
|
-
const sourceTarget = createFileSystemDocumentTarget(provider, layout.markdownPath);
|
|
4270
|
-
return {
|
|
4271
|
-
key: `${provider.id}:outside-in:${parseWorkspacePath5(layout.targetPath)}`,
|
|
4272
|
-
async commit(request) {
|
|
4273
|
-
if (!await isOutsideInMarkdownEditingEnabled(request.content)) {
|
|
4274
|
-
throw new Error(
|
|
4275
|
-
"Outside-in editing is read-only until squisq-updatefrommarkdown: true is set."
|
|
4276
|
-
);
|
|
4277
|
-
}
|
|
4278
|
-
const runtimePath = layout.format === "html" ? await findRuntimePath(provider, layout.targetPath) : null;
|
|
4279
|
-
const outputDirectory = dirname(layout.targetPath);
|
|
4280
|
-
const rendered = await renderOutsideInDocument(
|
|
4281
|
-
{
|
|
4282
|
-
markdown: request.content,
|
|
4283
|
-
targetPath: layout.targetPath,
|
|
4284
|
-
container: new FileSystemContentContainer(provider, layout.companionDirectory)
|
|
4285
|
-
},
|
|
4286
|
-
runtimePath ? {
|
|
4287
|
-
html: {
|
|
4288
|
-
playerScriptPath: relativePath(outputDirectory, runtimePath),
|
|
4289
|
-
basePath: relativePath(outputDirectory, layout.companionDirectory)
|
|
4290
|
-
}
|
|
4291
|
-
} : {}
|
|
4292
|
-
);
|
|
4293
|
-
await sourceTarget.commit({ ...request, targetKey: sourceTarget.key });
|
|
4294
|
-
if (runtimePath) await writeRuntimeIfNeeded(provider, runtimePath);
|
|
4295
|
-
await writeBytes(provider, layout.targetPath, rendered.bytes);
|
|
4296
|
-
onCommitted?.();
|
|
4297
|
-
return {};
|
|
4298
|
-
}
|
|
4299
|
-
};
|
|
4300
|
-
}
|
|
4301
|
-
function createOutsideInContentContainer(provider, layout) {
|
|
4302
|
-
return new FileSystemContentContainer(provider, layout.companionDirectory);
|
|
4303
|
-
}
|
|
4304
|
-
|
|
4305
4576
|
// src/components/usePromptDialog.ts
|
|
4306
4577
|
import React2, { useCallback as useCallback11, useEffect as useEffect12, useRef as useRef11, useState as useState12 } from "react";
|
|
4307
4578
|
|
|
@@ -5130,9 +5401,12 @@ function loadEditorShell() {
|
|
|
5130
5401
|
return editorShellModulePromise;
|
|
5131
5402
|
}
|
|
5132
5403
|
var EditorShell = lazy2(loadEditorShell);
|
|
5133
|
-
var GitUI = lazy2(() => import("./GitUI-
|
|
5404
|
+
var GitUI = lazy2(() => import("./GitUI-L5T3XHZG.js").then((m) => ({ default: m.GitUI })));
|
|
5405
|
+
var GitGrantNotice = lazy2(
|
|
5406
|
+
() => import("./GitGrantNotice-I2VNKPLH.js").then((m) => ({ default: m.GitGrantNotice }))
|
|
5407
|
+
);
|
|
5134
5408
|
var GitToolbarControl = lazy2(
|
|
5135
|
-
() => import("./GitToolbarControl-
|
|
5409
|
+
() => import("./GitToolbarControl-O5AXBC6T.js").then((m) => ({ default: m.GitToolbarControl }))
|
|
5136
5410
|
);
|
|
5137
5411
|
var DOCBLOCKS_VIDEO_EXPORT_PALETTE = Object.freeze({
|
|
5138
5412
|
overlay: "rgba(0, 0, 0, 0.72)",
|
|
@@ -5403,6 +5677,8 @@ function useIsMobile(breakpoint = 768) {
|
|
|
5403
5677
|
}, [breakpoint]);
|
|
5404
5678
|
return isMobile;
|
|
5405
5679
|
}
|
|
5680
|
+
var DESKTOP_TOOLBAR_WRAP_WIDTH = 900;
|
|
5681
|
+
var WEB_TOOLBAR_WRAP_WIDTH = 700;
|
|
5406
5682
|
function FolderGlyph() {
|
|
5407
5683
|
return /* @__PURE__ */ jsx14(
|
|
5408
5684
|
"svg",
|
|
@@ -5434,12 +5710,35 @@ function FileGlyph() {
|
|
|
5434
5710
|
}
|
|
5435
5711
|
);
|
|
5436
5712
|
}
|
|
5713
|
+
function CollapseSidebarGlyph() {
|
|
5714
|
+
return /* @__PURE__ */ jsxs12(
|
|
5715
|
+
"svg",
|
|
5716
|
+
{
|
|
5717
|
+
viewBox: "0 0 64 64",
|
|
5718
|
+
fill: "none",
|
|
5719
|
+
stroke: "currentColor",
|
|
5720
|
+
strokeWidth: "3",
|
|
5721
|
+
strokeLinecap: "round",
|
|
5722
|
+
strokeLinejoin: "round",
|
|
5723
|
+
"aria-hidden": "true",
|
|
5724
|
+
children: [
|
|
5725
|
+
/* @__PURE__ */ jsx14("rect", { x: "7", y: "10", width: "50", height: "44", rx: "6" }),
|
|
5726
|
+
/* @__PURE__ */ jsx14("path", { d: "M24 10v44" }),
|
|
5727
|
+
/* @__PURE__ */ jsx14("path", { d: "m17 25-7 7 7 7" })
|
|
5728
|
+
]
|
|
5729
|
+
}
|
|
5730
|
+
);
|
|
5731
|
+
}
|
|
5437
5732
|
function DocBlocksShell({
|
|
5438
5733
|
theme: hostTheme = "auto",
|
|
5439
5734
|
logoUrl,
|
|
5440
5735
|
issueReportVersion,
|
|
5441
5736
|
appBuildDate,
|
|
5442
5737
|
ffmpegWasm,
|
|
5738
|
+
calcEngineFactory,
|
|
5739
|
+
proofing,
|
|
5740
|
+
proofingDefaultEnabled,
|
|
5741
|
+
proofingIgnoreStore,
|
|
5443
5742
|
homeDocumentTitle,
|
|
5444
5743
|
homeDocumentPath,
|
|
5445
5744
|
allowVersioning = true,
|
|
@@ -5492,6 +5791,11 @@ function DocBlocksShell({
|
|
|
5492
5791
|
setWriteCanvasSettings(settings);
|
|
5493
5792
|
saveWriteCanvasPreferences(settings);
|
|
5494
5793
|
}, []);
|
|
5794
|
+
const [proofingPreferences, setProofingPreferences] = useState15(loadProofingPreferences);
|
|
5795
|
+
const handleProofingPreferencesChange = useCallback15((settings) => {
|
|
5796
|
+
setProofingPreferences(settings);
|
|
5797
|
+
saveProofingPreferences(settings);
|
|
5798
|
+
}, []);
|
|
5495
5799
|
const [viewPreferences, setViewPreferences] = useState15(loadViewPreferences);
|
|
5496
5800
|
const handleViewPreferencesChange = useCallback15((prefs) => {
|
|
5497
5801
|
setViewPreferences(prefs);
|
|
@@ -5508,6 +5812,27 @@ function DocBlocksShell({
|
|
|
5508
5812
|
const [sidebarWidth, setSidebarWidth] = useState15(loadSidebarWidth);
|
|
5509
5813
|
const [compactLayout, setCompactLayout] = useState15(false);
|
|
5510
5814
|
const effectiveCompact = isMobile || compactLayout;
|
|
5815
|
+
const editorAreaRef = useRef15(null);
|
|
5816
|
+
const [desktopToolbarWrapped, setDesktopToolbarWrapped] = useState15(false);
|
|
5817
|
+
useEffect15(() => {
|
|
5818
|
+
const editorArea = editorAreaRef.current;
|
|
5819
|
+
if (!editorArea || effectiveCompact) {
|
|
5820
|
+
setDesktopToolbarWrapped(false);
|
|
5821
|
+
return;
|
|
5822
|
+
}
|
|
5823
|
+
const wrapWidth = isElectronHost3() ? DESKTOP_TOOLBAR_WRAP_WIDTH : WEB_TOOLBAR_WRAP_WIDTH;
|
|
5824
|
+
const updateWrappedState = () => {
|
|
5825
|
+
setDesktopToolbarWrapped(editorArea.getBoundingClientRect().width <= wrapWidth);
|
|
5826
|
+
};
|
|
5827
|
+
updateWrappedState();
|
|
5828
|
+
if (typeof ResizeObserver === "undefined") {
|
|
5829
|
+
window.addEventListener("resize", updateWrappedState);
|
|
5830
|
+
return () => window.removeEventListener("resize", updateWrappedState);
|
|
5831
|
+
}
|
|
5832
|
+
const observer = new ResizeObserver(updateWrappedState);
|
|
5833
|
+
observer.observe(editorArea);
|
|
5834
|
+
return () => observer.disconnect();
|
|
5835
|
+
}, [effectiveCompact]);
|
|
5511
5836
|
const showBrowserStorageWarning = !isElectronHost3();
|
|
5512
5837
|
const appVersion = isElectronHost3() ? `${getDocBlocksHost().env.appVersion} desktop` : issueReportVersion ?? "web";
|
|
5513
5838
|
const issueReportUrl = buildIssueReportUrl({
|
|
@@ -5553,13 +5878,13 @@ function DocBlocksShell({
|
|
|
5553
5878
|
lastRaw = drag.startWidth + (ev.clientX - drag.startX);
|
|
5554
5879
|
if (lastRaw < SIDEBAR_COLLAPSE_THRESHOLD && sidebarRef.current) {
|
|
5555
5880
|
sidebarRef.current.style.width = `${SIDEBAR_WIDTH_MIN}px`;
|
|
5556
|
-
sidebarRef.current.
|
|
5881
|
+
sidebarRef.current.classList.add("db-shell-sidebar--collapse-preview");
|
|
5557
5882
|
return;
|
|
5558
5883
|
}
|
|
5559
5884
|
const clamped = Math.min(SIDEBAR_WIDTH_MAX, Math.max(SIDEBAR_WIDTH_MIN, lastRaw));
|
|
5560
5885
|
if (sidebarRef.current) {
|
|
5561
5886
|
sidebarRef.current.style.width = `${clamped}px`;
|
|
5562
|
-
sidebarRef.current.
|
|
5887
|
+
sidebarRef.current.classList.remove("db-shell-sidebar--collapse-preview");
|
|
5563
5888
|
}
|
|
5564
5889
|
};
|
|
5565
5890
|
const onUp = () => {
|
|
@@ -5568,7 +5893,7 @@ function DocBlocksShell({
|
|
|
5568
5893
|
document.body.style.userSelect = "";
|
|
5569
5894
|
document.body.classList.remove("db-resizing-sidebar");
|
|
5570
5895
|
if (sidebarRef.current) {
|
|
5571
|
-
sidebarRef.current.
|
|
5896
|
+
sidebarRef.current.classList.remove("db-shell-sidebar--collapse-preview");
|
|
5572
5897
|
}
|
|
5573
5898
|
if (lastRaw < SIDEBAR_COLLAPSE_THRESHOLD) {
|
|
5574
5899
|
setCompactLayout(true);
|
|
@@ -5595,6 +5920,11 @@ function DocBlocksShell({
|
|
|
5595
5920
|
const [workspaceStartupError, setWorkspaceStartupError] = useState15(null);
|
|
5596
5921
|
const [activeWorkspaceId, setActiveWorkspaceId] = useState15(null);
|
|
5597
5922
|
const [activeWorkspaceDescriptor, setActiveWorkspaceDescriptor] = useState15(null);
|
|
5923
|
+
const defaultProofingIgnoreStore = useMemo3(
|
|
5924
|
+
() => createLocalProofingIgnoreStore(() => activeWorkspaceId),
|
|
5925
|
+
[activeWorkspaceId]
|
|
5926
|
+
);
|
|
5927
|
+
const effectiveProofingIgnoreStore = proofingIgnoreStore === void 0 ? defaultProofingIgnoreStore : proofingIgnoreStore;
|
|
5598
5928
|
useEffect15(() => {
|
|
5599
5929
|
if (!provider || getTransientWorkspace(provider.id)) return;
|
|
5600
5930
|
const providerV2 = getFileSystemProviderV27(provider);
|
|
@@ -5751,11 +6081,13 @@ function DocBlocksShell({
|
|
|
5751
6081
|
const [selectedSourceFile, setSelectedSourceFile] = useState15(null);
|
|
5752
6082
|
const [selectedOutsideIn, setSelectedOutsideIn] = useState15(null);
|
|
5753
6083
|
const [selectedOutsideInEditingEnabled, setSelectedOutsideInEditingEnabled] = useState15(false);
|
|
6084
|
+
const [selectedImage, setSelectedImage] = useState15(void 0);
|
|
5754
6085
|
const adoptSelectedDocument = useCallback15((document2) => {
|
|
5755
6086
|
setSelectedFile(document2?.displayPath ?? null);
|
|
5756
6087
|
setSelectedSourceFile(document2?.sourcePath ?? null);
|
|
5757
6088
|
setSelectedOutsideIn(document2?.outsideIn ?? null);
|
|
5758
6089
|
setSelectedOutsideInEditingEnabled(document2?.outsideInEditingEnabled ?? false);
|
|
6090
|
+
setSelectedImage(document2?.image);
|
|
5759
6091
|
}, []);
|
|
5760
6092
|
const adoptRegularDocument = useCallback15((path) => {
|
|
5761
6093
|
if (path === null) {
|
|
@@ -5763,13 +6095,27 @@ function DocBlocksShell({
|
|
|
5763
6095
|
setSelectedSourceFile(null);
|
|
5764
6096
|
setSelectedOutsideIn(null);
|
|
5765
6097
|
setSelectedOutsideInEditingEnabled(false);
|
|
6098
|
+
setSelectedImage(void 0);
|
|
5766
6099
|
return;
|
|
5767
6100
|
}
|
|
5768
6101
|
setSelectedFile(path);
|
|
5769
6102
|
setSelectedSourceFile(path);
|
|
5770
6103
|
setSelectedOutsideIn(null);
|
|
5771
6104
|
setSelectedOutsideInEditingEnabled(false);
|
|
6105
|
+
setSelectedImage(void 0);
|
|
5772
6106
|
}, []);
|
|
6107
|
+
const selectedImageUrl = useMemo3(() => {
|
|
6108
|
+
if (!selectedImage || typeof URL === "undefined" || typeof URL.createObjectURL !== "function") {
|
|
6109
|
+
return void 0;
|
|
6110
|
+
}
|
|
6111
|
+
return URL.createObjectURL(new Blob([selectedImage.data], { type: selectedImage.mimeType }));
|
|
6112
|
+
}, [selectedImage]);
|
|
6113
|
+
useEffect15(
|
|
6114
|
+
() => () => {
|
|
6115
|
+
if (selectedImageUrl) URL.revokeObjectURL(selectedImageUrl);
|
|
6116
|
+
},
|
|
6117
|
+
[selectedImageUrl]
|
|
6118
|
+
);
|
|
5773
6119
|
useDocumentTitle(selectedFile, homeDocumentTitle, homeDocumentPath);
|
|
5774
6120
|
const exportDestinationAdapter = useMemo3(() => {
|
|
5775
6121
|
if (!selectedFile) return void 0;
|
|
@@ -6632,7 +6978,7 @@ function DocBlocksShell({
|
|
|
6632
6978
|
const providerV2 = getFileSystemProviderV27(provider);
|
|
6633
6979
|
if (!providerV2?.capabilities.watch) return;
|
|
6634
6980
|
const watchedFile = selectedSourceFile ?? selectedFile;
|
|
6635
|
-
if (!watchedFile || !documentSnapshot.targetKey) return;
|
|
6981
|
+
if (!watchedFile || !documentSnapshot.targetKey || selectedImage) return;
|
|
6636
6982
|
const targetKey = documentSnapshot.targetKey;
|
|
6637
6983
|
let disposed = false;
|
|
6638
6984
|
let reading = false;
|
|
@@ -6686,7 +7032,14 @@ function DocBlocksShell({
|
|
|
6686
7032
|
disposed = true;
|
|
6687
7033
|
void subscription.dispose();
|
|
6688
7034
|
};
|
|
6689
|
-
}, [
|
|
7035
|
+
}, [
|
|
7036
|
+
provider,
|
|
7037
|
+
selectedFile,
|
|
7038
|
+
selectedSourceFile,
|
|
7039
|
+
selectedImage,
|
|
7040
|
+
documentSession,
|
|
7041
|
+
documentSnapshot.targetKey
|
|
7042
|
+
]);
|
|
6690
7043
|
const transitionAwayFromDocument = useCallback15(
|
|
6691
7044
|
async (requestId) => {
|
|
6692
7045
|
if (requestId !== navigationRequestRef.current) return false;
|
|
@@ -7698,7 +8051,7 @@ function DocBlocksShell({
|
|
|
7698
8051
|
const nextFile = selectedFile ? relocateProviderPath(selectedFile, change.oldPath, change.newPath) : null;
|
|
7699
8052
|
const nextFolder = selectedFolder ? relocateProviderPath(selectedFolder, change.oldPath, change.newPath) : null;
|
|
7700
8053
|
if (nextFile !== selectedFile) {
|
|
7701
|
-
if (nextFile
|
|
8054
|
+
if (nextFile) {
|
|
7702
8055
|
const opened = await loadEditableShellDocument(provider, nextFile);
|
|
7703
8056
|
adoptSelectedDocument(opened);
|
|
7704
8057
|
} else {
|
|
@@ -7743,7 +8096,6 @@ function DocBlocksShell({
|
|
|
7743
8096
|
adoptRegularDocument,
|
|
7744
8097
|
adoptSelectedDocument,
|
|
7745
8098
|
selectedFile,
|
|
7746
|
-
selectedOutsideIn,
|
|
7747
8099
|
selectedFolder,
|
|
7748
8100
|
activeWorkspaceId,
|
|
7749
8101
|
pinnedDocuments,
|
|
@@ -8372,7 +8724,7 @@ function DocBlocksShell({
|
|
|
8372
8724
|
return /* @__PURE__ */ jsx14(
|
|
8373
8725
|
"div",
|
|
8374
8726
|
{
|
|
8375
|
-
className: `db-shell${effectiveCompact ? " db-shell--mobile" : ""}`,
|
|
8727
|
+
className: `db-shell${effectiveCompact ? " db-shell--mobile" : ""}${desktopToolbarWrapped ? " db-shell--desktop-toolbar-wrapped" : ""}`,
|
|
8376
8728
|
"data-theme": resolvedTheme,
|
|
8377
8729
|
"data-accent": accentColor,
|
|
8378
8730
|
"data-document-status": documentSnapshot.status,
|
|
@@ -8433,6 +8785,8 @@ function DocBlocksShell({
|
|
|
8433
8785
|
onAccentColorChange: handleAccentColorChange,
|
|
8434
8786
|
writeCanvasSettings,
|
|
8435
8787
|
onWriteCanvasSettingsChange: handleWriteCanvasSettingsChange,
|
|
8788
|
+
proofingPreferences,
|
|
8789
|
+
onProofingPreferencesChange: handleProofingPreferencesChange,
|
|
8436
8790
|
versioningPreference,
|
|
8437
8791
|
onVersioningPreferenceChange: handleVersioningPreferenceChange,
|
|
8438
8792
|
onDownloadAllWorkspaces: handleDownloadAllWorkspaces,
|
|
@@ -8544,6 +8898,7 @@ function DocBlocksShell({
|
|
|
8544
8898
|
]
|
|
8545
8899
|
}
|
|
8546
8900
|
),
|
|
8901
|
+
git.available && git.pendingGrant && /* @__PURE__ */ jsx14(Suspense2, { fallback: null, children: /* @__PURE__ */ jsx14(GitGrantNotice, {}) }),
|
|
8547
8902
|
/* @__PURE__ */ jsxs12("div", { className: "db-shell-sidebar-footer", children: [
|
|
8548
8903
|
/* @__PURE__ */ jsx14("a", { href: "https://docblocks.com/docs/", target: "_blank", rel: "noopener noreferrer", children: "Docs" }),
|
|
8549
8904
|
/* @__PURE__ */ jsx14("span", { className: "db-shell-sidebar-footer-separator", "aria-hidden": "true", children: "\u2022" }),
|
|
@@ -8563,6 +8918,10 @@ function DocBlocksShell({
|
|
|
8563
8918
|
}
|
|
8564
8919
|
)
|
|
8565
8920
|
] })
|
|
8921
|
+
] }),
|
|
8922
|
+
/* @__PURE__ */ jsxs12("div", { className: "db-sidebar-collapse-preview", "aria-hidden": "true", children: [
|
|
8923
|
+
/* @__PURE__ */ jsx14("span", { className: "db-sidebar-collapse-preview-icon", children: /* @__PURE__ */ jsx14(CollapseSidebarGlyph, {}) }),
|
|
8924
|
+
/* @__PURE__ */ jsx14("span", { className: "db-sidebar-collapse-preview-label", children: "Release to hide files" })
|
|
8566
8925
|
] })
|
|
8567
8926
|
]
|
|
8568
8927
|
}
|
|
@@ -8580,6 +8939,7 @@ function DocBlocksShell({
|
|
|
8580
8939
|
(!effectiveCompact || mobileShowEditor) && /* @__PURE__ */ jsxs12(
|
|
8581
8940
|
"main",
|
|
8582
8941
|
{
|
|
8942
|
+
ref: editorAreaRef,
|
|
8583
8943
|
"aria-label": "Document editor",
|
|
8584
8944
|
className: updateAvailable && onApplyUpdate && updateStatusBarVisible ? "db-shell-editor-area db-shell-editor-area--has-update" : "db-shell-editor-area",
|
|
8585
8945
|
style: {
|
|
@@ -8599,11 +8959,13 @@ function DocBlocksShell({
|
|
|
8599
8959
|
EditorShell,
|
|
8600
8960
|
{
|
|
8601
8961
|
initialMarkdown: editorContent,
|
|
8602
|
-
readOnly: selectedOutsideIn !== null && !selectedOutsideInEditingEnabled,
|
|
8962
|
+
readOnly: selectedImage !== void 0 || selectedOutsideIn !== null && !selectedOutsideInEditingEnabled,
|
|
8603
8963
|
initialView,
|
|
8604
8964
|
defaultViewportPreset: defaultPreviewViewportPreset,
|
|
8605
8965
|
articleId: selectedFile,
|
|
8606
8966
|
fileName: selectedFile,
|
|
8967
|
+
imageSrc: selectedImageUrl,
|
|
8968
|
+
imageAlt: basenameOf(selectedFile),
|
|
8607
8969
|
saveCoverImageOutput: saveRenderedImageOutput,
|
|
8608
8970
|
saveDashboardImageOutput: saveRenderedImageOutput,
|
|
8609
8971
|
onChange: handleEditorChange,
|
|
@@ -8614,6 +8976,12 @@ function DocBlocksShell({
|
|
|
8614
8976
|
placeholder: editorPlaceholder,
|
|
8615
8977
|
outlineWidth: 280,
|
|
8616
8978
|
mediaProvider,
|
|
8979
|
+
calcEngineFactory,
|
|
8980
|
+
proofing,
|
|
8981
|
+
proofingDefaultEnabled,
|
|
8982
|
+
proofingSpellingEnabled: proofingPreferences.spelling,
|
|
8983
|
+
proofingGrammarEnabled: proofingPreferences.grammar,
|
|
8984
|
+
proofingIgnoreStore: effectiveProofingIgnoreStore,
|
|
8617
8985
|
showCodeCopyButton,
|
|
8618
8986
|
onCopyCode,
|
|
8619
8987
|
allowRecording,
|
|
@@ -8621,7 +8989,7 @@ function DocBlocksShell({
|
|
|
8621
8989
|
allowPresentationFullscreen,
|
|
8622
8990
|
documentLinkProvider,
|
|
8623
8991
|
workspaceContainer: versionsContainer ?? void 0,
|
|
8624
|
-
allowVersioning: effectiveVersioning,
|
|
8992
|
+
allowVersioning: selectedImage === void 0 && effectiveVersioning,
|
|
8625
8993
|
viewPreferences,
|
|
8626
8994
|
onViewPreferencesChange: handleViewPreferencesChange,
|
|
8627
8995
|
versionBasename: versionBasename ?? stripExtension(basenameOf(selectedFile)),
|
|
@@ -8764,12 +9132,14 @@ export {
|
|
|
8764
9132
|
AccentColorSettings,
|
|
8765
9133
|
AppMenu,
|
|
8766
9134
|
DEFAULT_OPTIONS,
|
|
9135
|
+
DEFAULT_PROOFING_PREFERENCES,
|
|
8767
9136
|
DEFAULT_WRITE_CANVAS_FONT_SCHEME,
|
|
8768
9137
|
DocBlocksShell,
|
|
8769
9138
|
ExportDialog,
|
|
8770
9139
|
ExportToolbarControls,
|
|
8771
9140
|
FileExplorer,
|
|
8772
9141
|
FileTreeNode,
|
|
9142
|
+
ProofingSettingsControls,
|
|
8773
9143
|
SettingsDialog,
|
|
8774
9144
|
ThemeSettings,
|
|
8775
9145
|
WRITE_CANVAS_FONT_SCHEMES,
|
|
@@ -8780,8 +9150,10 @@ export {
|
|
|
8780
9150
|
createImageSaveOutput as createDashboardImageSaveOutput,
|
|
8781
9151
|
createImageSaveOutput,
|
|
8782
9152
|
loadLastExportOptions,
|
|
9153
|
+
loadProofingPreferences,
|
|
8783
9154
|
resolveWriteCanvasFonts,
|
|
8784
9155
|
runExport,
|
|
9156
|
+
saveProofingPreferences,
|
|
8785
9157
|
updateExportTargetExtension,
|
|
8786
9158
|
useDocumentSession,
|
|
8787
9159
|
useFileTree
|