@rightkit/document-workspace 0.0.0-stage → 0.1.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/CHANGELOG.md +5 -0
- package/LICENSE-APACHE +201 -0
- package/LICENSE-MIT +21 -0
- package/README.md +51 -2
- package/dist/app/App.d.ts +1 -0
- package/dist/app/App.js +6 -0
- package/dist/app/AppDialogs.d.ts +52 -0
- package/dist/app/AppDialogs.js +23 -0
- package/dist/app/AppDialogsHost.d.ts +31 -0
- package/dist/app/AppDialogsHost.js +30 -0
- package/dist/app/AppLifecycle.d.ts +19 -0
- package/dist/app/AppLifecycle.js +87 -0
- package/dist/app/AppProvider.d.ts +317 -0
- package/dist/app/AppProvider.js +217 -0
- package/dist/app/AppRoot.d.ts +8 -0
- package/dist/app/AppRoot.js +159 -0
- package/dist/app/AppShell.d.ts +2 -0
- package/dist/app/AppShell.js +81 -0
- package/dist/app/CommandPalette.d.ts +13 -0
- package/dist/app/CommandPalette.js +47 -0
- package/dist/app/DocActionBar.d.ts +36 -0
- package/dist/app/DocActionBar.js +300 -0
- package/dist/app/DocumentContextMenu.d.ts +29 -0
- package/dist/app/DocumentContextMenu.js +122 -0
- package/dist/app/DocumentPane.d.ts +52 -0
- package/dist/app/DocumentPane.js +30 -0
- package/dist/app/DocumentRouterTable.d.ts +58 -0
- package/dist/app/DocumentRouterTable.js +143 -0
- package/dist/app/ErrorBoundary.d.ts +12 -0
- package/dist/app/ErrorBoundary.js +16 -0
- package/dist/app/FindBar.d.ts +12 -0
- package/dist/app/FindBar.js +199 -0
- package/dist/app/LiveTitle.d.ts +14 -0
- package/dist/app/LiveTitle.js +14 -0
- package/dist/app/OpeningProgress.d.ts +13 -0
- package/dist/app/OpeningProgress.js +21 -0
- package/dist/app/PrintPreviewWindow.d.ts +2 -0
- package/dist/app/PrintPreviewWindow.js +57 -0
- package/dist/app/ShortcutHelp.d.ts +3 -0
- package/dist/app/ShortcutHelp.js +99 -0
- package/dist/app/Sidebar.d.ts +36 -0
- package/dist/app/Sidebar.js +121 -0
- package/dist/app/ToastViewport.d.ts +1 -0
- package/dist/app/ToastViewport.js +31 -0
- package/dist/app/Toolbar.d.ts +26 -0
- package/dist/app/Toolbar.js +63 -0
- package/dist/app/activeFormatSavers.d.ts +7 -0
- package/dist/app/activeFormatSavers.js +4 -0
- package/dist/app/clipboardPaths.d.ts +1 -0
- package/dist/app/clipboardPaths.js +31 -0
- package/dist/app/documentDrafts.d.ts +17 -0
- package/dist/app/documentDrafts.js +104 -0
- package/dist/app/draftResolution.d.ts +5 -0
- package/dist/app/draftResolution.js +25 -0
- package/dist/app/markdownPaletteActions.d.ts +8 -0
- package/dist/app/markdownPaletteActions.js +107 -0
- package/dist/app/resourceCensusDev.d.ts +41 -0
- package/dist/app/resourceCensusDev.js +518 -0
- package/dist/app/setupRecovery.d.ts +34 -0
- package/dist/app/setupRecovery.js +70 -0
- package/dist/app/shell/DocStatsPill.d.ts +18 -0
- package/dist/app/shell/DocStatsPill.js +32 -0
- package/dist/app/shell/DocumentTools.d.ts +9 -0
- package/dist/app/shell/DocumentTools.js +73 -0
- package/dist/app/shell/InfoInspector.d.ts +9 -0
- package/dist/app/shell/InfoInspector.js +67 -0
- package/dist/app/shell/StartPage.d.ts +9 -0
- package/dist/app/shell/StartPage.js +49 -0
- package/dist/app/shell/readEdit.d.ts +6 -0
- package/dist/app/shell/readEdit.js +6 -0
- package/dist/app/sidebar/SidebarDocumentMenu.d.ts +20 -0
- package/dist/app/sidebar/SidebarDocumentMenu.js +56 -0
- package/dist/app/sidebar/SidebarExpandedPanel.d.ts +9 -0
- package/dist/app/sidebar/SidebarExpandedPanel.js +78 -0
- package/dist/app/sidebar/SidebarIcons.d.ts +3 -0
- package/dist/app/sidebar/SidebarIcons.js +18 -0
- package/dist/app/sidebar/SidebarMenus.d.ts +19 -0
- package/dist/app/sidebar/SidebarMenus.js +64 -0
- package/dist/app/sidebar/SidebarPathUtils.d.ts +10 -0
- package/dist/app/sidebar/SidebarPathUtils.js +55 -0
- package/dist/app/sidebar/SidebarPrimitives.d.ts +12 -0
- package/dist/app/sidebar/SidebarPrimitives.js +45 -0
- package/dist/app/sidebar/SidebarRail.d.ts +5 -0
- package/dist/app/sidebar/SidebarRail.js +37 -0
- package/dist/app/sidebar/explorer/ExplorerRowMenu.d.ts +21 -0
- package/dist/app/sidebar/explorer/ExplorerRowMenu.js +37 -0
- package/dist/app/sidebar/explorer/ExplorerSections.d.ts +16 -0
- package/dist/app/sidebar/explorer/ExplorerSections.js +259 -0
- package/dist/app/sidebar/explorer/ExplorerTree.d.ts +46 -0
- package/dist/app/sidebar/explorer/ExplorerTree.js +387 -0
- package/dist/app/sidebar/explorer/explorerState.d.ts +37 -0
- package/dist/app/sidebar/explorer/explorerState.js +135 -0
- package/dist/app/tabMountPolicy.d.ts +45 -0
- package/dist/app/tabMountPolicy.js +104 -0
- package/dist/app/tabWarmOrder.d.ts +7 -0
- package/dist/app/tabWarmOrder.js +48 -0
- package/dist/app/tabs/TabFuseCurves.d.ts +3 -0
- package/dist/app/tabs/TabFuseCurves.js +15 -0
- package/dist/app/tabs/TabOverview.d.ts +24 -0
- package/dist/app/tabs/TabOverview.js +121 -0
- package/dist/app/tabs/TabStrip.d.ts +42 -0
- package/dist/app/tabs/TabStrip.js +380 -0
- package/dist/app/tabs/tabIds.d.ts +5 -0
- package/dist/app/tabs/tabIds.js +7 -0
- package/dist/app/tabs/useTabDirty.d.ts +3 -0
- package/dist/app/tabs/useTabDirty.js +11 -0
- package/dist/app/useAppChromeState.d.ts +21 -0
- package/dist/app/useAppChromeState.js +48 -0
- package/dist/app/useAppCommands.d.ts +66 -0
- package/dist/app/useAppCommands.js +71 -0
- package/dist/app/useAppDocumentCore.d.ts +210 -0
- package/dist/app/useAppDocumentCore.js +40 -0
- package/dist/app/useAppEffects.d.ts +18 -0
- package/dist/app/useAppEffects.js +52 -0
- package/dist/app/useAppPaneSearch.d.ts +37 -0
- package/dist/app/useAppPaneSearch.js +74 -0
- package/dist/app/useAppShellSetup.d.ts +40 -0
- package/dist/app/useAppShellSetup.js +52 -0
- package/dist/app/useAppSlots.d.ts +28 -0
- package/dist/app/useAppSlots.js +40 -0
- package/dist/app/useAppTabChrome.d.ts +51 -0
- package/dist/app/useAppTabChrome.js +66 -0
- package/dist/app/useAutoSave.d.ts +34 -0
- package/dist/app/useAutoSave.js +116 -0
- package/dist/app/useCloseAll.d.ts +52 -0
- package/dist/app/useCloseAll.js +295 -0
- package/dist/app/useClosedTabEviction.d.ts +10 -0
- package/dist/app/useClosedTabEviction.js +38 -0
- package/dist/app/useCommandPalette.d.ts +44 -0
- package/dist/app/useCommandPalette.js +254 -0
- package/dist/app/useCrossDocSearch.d.ts +7 -0
- package/dist/app/useCrossDocSearch.js +94 -0
- package/dist/app/useDistractionFree.d.ts +21 -0
- package/dist/app/useDistractionFree.js +73 -0
- package/dist/app/useDocumentCreation.d.ts +18 -0
- package/dist/app/useDocumentCreation.js +63 -0
- package/dist/app/useDocumentDirty.d.ts +31 -0
- package/dist/app/useDocumentDirty.js +46 -0
- package/dist/app/useDocumentLoader.d.ts +55 -0
- package/dist/app/useDocumentLoader.js +267 -0
- package/dist/app/useDocumentOutputActions.d.ts +6 -0
- package/dist/app/useDocumentOutputActions.js +45 -0
- package/dist/app/useDocumentRuntimeEffects.d.ts +36 -0
- package/dist/app/useDocumentRuntimeEffects.js +458 -0
- package/dist/app/useDocumentSaver.d.ts +58 -0
- package/dist/app/useDocumentSaver.js +511 -0
- package/dist/app/useDocumentTabs.d.ts +41 -0
- package/dist/app/useDocumentTabs.js +207 -0
- package/dist/app/useDocumentWatcher.d.ts +83 -0
- package/dist/app/useDocumentWatcher.js +393 -0
- package/dist/app/useDocumentZoom.d.ts +24 -0
- package/dist/app/useDocumentZoom.js +104 -0
- package/dist/app/useDocuments.d.ts +169 -0
- package/dist/app/useDocuments.js +455 -0
- package/dist/app/useDraftConfirmGate.d.ts +27 -0
- package/dist/app/useDraftConfirmGate.js +47 -0
- package/dist/app/useFileDragOverlay.d.ts +12 -0
- package/dist/app/useFileDragOverlay.js +49 -0
- package/dist/app/useFormatApis.d.ts +13 -0
- package/dist/app/useFormatApis.js +62 -0
- package/dist/app/useFormatDirtyState.d.ts +29 -0
- package/dist/app/useFormatDirtyState.js +81 -0
- package/dist/app/useGlobalShortcuts.d.ts +47 -0
- package/dist/app/useGlobalShortcuts.js +359 -0
- package/dist/app/useHtmlSession.d.ts +68 -0
- package/dist/app/useHtmlSession.js +463 -0
- package/dist/app/useHtmlSessionProps.d.ts +33 -0
- package/dist/app/useHtmlSessionProps.js +57 -0
- package/dist/app/useLibrary.d.ts +21 -0
- package/dist/app/useLibrary.js +81 -0
- package/dist/app/useOpenFromClipboard.d.ts +7 -0
- package/dist/app/useOpenFromClipboard.js +44 -0
- package/dist/app/usePaneLiveness.d.ts +29 -0
- package/dist/app/usePaneLiveness.js +199 -0
- package/dist/app/usePendingSavePaths.d.ts +19 -0
- package/dist/app/usePendingSavePaths.js +67 -0
- package/dist/app/useReadyShow.d.ts +12 -0
- package/dist/app/useReadyShow.js +142 -0
- package/dist/app/useSearchController.d.ts +22 -0
- package/dist/app/useSearchController.js +114 -0
- package/dist/app/useSetupRecovery.d.ts +41 -0
- package/dist/app/useSetupRecovery.js +153 -0
- package/dist/app/useTabChrome.d.ts +40 -0
- package/dist/app/useTabChrome.js +115 -0
- package/dist/app/useViewState.d.ts +27 -0
- package/dist/app/useViewState.js +160 -0
- package/dist/appName.d.ts +2 -0
- package/dist/appName.js +8 -0
- package/dist/documents/DocumentRouter.d.ts +102 -0
- package/dist/documents/DocumentRouter.js +153 -0
- package/dist/documents/FileTypeIcon.d.ts +6 -0
- package/dist/documents/FileTypeIcon.js +93 -0
- package/dist/documents/ImageDocument.d.ts +14 -0
- package/dist/documents/ImageDocument.js +61 -0
- package/dist/documents/capabilities.d.ts +17 -0
- package/dist/documents/capabilities.js +18 -0
- package/dist/documents/html/HtmlDocument.d.ts +15 -0
- package/dist/documents/html/HtmlDocument.js +584 -0
- package/dist/documents/html/HtmlDocumentLayout.d.ts +87 -0
- package/dist/documents/html/HtmlDocumentLayout.js +25 -0
- package/dist/documents/html/HtmlDocumentProps.d.ts +60 -0
- package/dist/documents/html/HtmlDocumentProps.js +1 -0
- package/dist/documents/html/HtmlDocumentSource.d.ts +34 -0
- package/dist/documents/html/HtmlDocumentSource.js +28 -0
- package/dist/documents/markdown/MarkdownSplitPane.d.ts +115 -0
- package/dist/documents/markdown/MarkdownSplitPane.js +352 -0
- package/dist/documents/mdx/MdxSplitPane.d.ts +32 -0
- package/dist/documents/mdx/MdxSplitPane.js +60 -0
- package/dist/documents/txt/LinkDialog.d.ts +8 -0
- package/dist/documents/txt/LinkDialog.js +34 -0
- package/dist/documents/txt/MarkdownSourceToolbar.d.ts +8 -0
- package/dist/documents/txt/MarkdownSourceToolbar.js +25 -0
- package/dist/documents/txt/SourceEditorToolbar.d.ts +12 -0
- package/dist/documents/txt/SourceEditorToolbar.js +4 -0
- package/dist/documents/txt/TxtEditor.d.ts +39 -0
- package/dist/documents/txt/TxtEditor.js +186 -0
- package/dist/documents/txt/sourceEditorTypes.d.ts +49 -0
- package/dist/documents/txt/sourceEditorTypes.js +1 -0
- package/dist/documents/txt/sourceExtensions.d.ts +38 -0
- package/dist/documents/txt/sourceExtensions.js +444 -0
- package/dist/documents/txt/sourceFormatting.d.ts +18 -0
- package/dist/documents/txt/sourceFormatting.js +183 -0
- package/dist/documents/txt/sourceTheme.d.ts +5 -0
- package/dist/documents/txt/sourceTheme.js +220 -0
- package/dist/documents/txt/useCodeMirrorSourceEditor.d.ts +69 -0
- package/dist/documents/txt/useCodeMirrorSourceEditor.js +546 -0
- package/dist/documents/txt/useSourceSearch.d.ts +39 -0
- package/dist/documents/txt/useSourceSearch.js +122 -0
- package/dist/documents/txt/useTextEditorCommands.d.ts +26 -0
- package/dist/documents/txt/useTextEditorCommands.js +151 -0
- package/dist/documents/web/LivePreviewPane.d.ts +11 -0
- package/dist/documents/web/LivePreviewPane.js +58 -0
- package/dist/env.d.ts +66 -0
- package/dist/index.d.ts +121 -0
- package/dist/index.js +123 -0
- package/dist/settings/AppearanceMenu.d.ts +9 -0
- package/dist/settings/AppearanceMenu.js +54 -0
- package/dist/settings/SettingsPanel.d.ts +7 -0
- package/dist/settings/SettingsPanel.js +104 -0
- package/dist/settings/publishedSections.d.ts +22 -0
- package/dist/settings/publishedSections.js +79 -0
- package/dist/settings/sections/AboutSection.d.ts +1 -0
- package/dist/settings/sections/AboutSection.js +13 -0
- package/dist/settings/sections/DiagnosticsSection.d.ts +1 -0
- package/dist/settings/sections/DiagnosticsSection.js +42 -0
- package/dist/settings/sections/PrivacySection.d.ts +3 -0
- package/dist/settings/sections/PrivacySection.js +51 -0
- package/dist/settings/sections/UpdateLicenseBlock.d.ts +10 -0
- package/dist/settings/sections/UpdateLicenseBlock.js +158 -0
- package/dist/state/explorer.d.ts +32 -0
- package/dist/state/explorer.js +111 -0
- package/dist/state/legal.d.ts +51 -0
- package/dist/state/legal.js +130 -0
- package/dist/state/library.d.ts +33 -0
- package/dist/state/library.js +110 -0
- package/dist/state/updateLicense.d.ts +31 -0
- package/dist/state/updateLicense.js +340 -0
- package/dist/styles/explorer.css +219 -0
- package/dist/styles/index.css +1477 -0
- package/dist/styles/tabs.css +467 -0
- package/dist/styles.css +6 -0
- package/package.json +88 -3
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { type Dispatch, type MutableRefObject, type SetStateAction } from "react";
|
|
2
|
+
import type { LoadedDocument, ViewMode } from "@rightkit/document-core/documents/types";
|
|
3
|
+
import type { ImageSaveFunction } from "@rightkit/media-ui";
|
|
4
|
+
import type { DocumentTabs } from "./useDocumentTabs.js";
|
|
5
|
+
import type { BlockDraftRegistry } from "@rightkit/document-core/app/blockDrafts";
|
|
6
|
+
interface SetupRecoveryDeps {
|
|
7
|
+
blockDrafts: BlockDraftRegistry;
|
|
8
|
+
setError: Dispatch<SetStateAction<string | null>>;
|
|
9
|
+
openDocsRef: MutableRefObject<LoadedDocument[]>;
|
|
10
|
+
activePathRef: MutableRefObject<string | null>;
|
|
11
|
+
viewsRef: MutableRefObject<Record<string, ViewMode>>;
|
|
12
|
+
isDirty: (doc: LoadedDocument) => boolean;
|
|
13
|
+
editsRef: DocumentTabs["editsRef"];
|
|
14
|
+
htmlPagesRef: DocumentTabs["htmlPagesRef"];
|
|
15
|
+
docxSaversRef: MutableRefObject<Map<string, () => Promise<ArrayBuffer | null>>>;
|
|
16
|
+
imageSaversRef: MutableRefObject<Map<string, ImageSaveFunction>>;
|
|
17
|
+
setOpenDocs: DocumentTabs["setOpenDocs"];
|
|
18
|
+
writeEdits: DocumentTabs["writeEdits"];
|
|
19
|
+
writeHtmlPages: DocumentTabs["writeHtmlPages"];
|
|
20
|
+
setViews: Dispatch<SetStateAction<Record<string, ViewMode>>>;
|
|
21
|
+
setDocxDirty: DocumentTabs["setDocxDirty"];
|
|
22
|
+
restoreImageDirty: (path: string) => void;
|
|
23
|
+
fingerprints: MutableRefObject<Map<string, {
|
|
24
|
+
mtimeMs: number;
|
|
25
|
+
size: number;
|
|
26
|
+
}>>;
|
|
27
|
+
withTabKey: <T extends LoadedDocument>(doc: T, existingKey?: string) => T;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Setup-replacement recovery: snapshot every open dirty document (committed sources only — open
|
|
31
|
+
* block drafts are resolved first so the snapshot never carries a half-typed draft) before the
|
|
32
|
+
* installer destroys the window, then rehydrate documents/edits/views/dirty flags on the next
|
|
33
|
+
* launch when the snapshot is present. The pure snapshot (de)serialization lives in
|
|
34
|
+
* setupRecovery.ts; this hook owns the refs/callbacks that feed it and the re-entrant guard.
|
|
35
|
+
* Extracted verbatim from useDocuments; bodies are unchanged.
|
|
36
|
+
*/
|
|
37
|
+
export declare function useSetupRecovery({ blockDrafts, setError, openDocsRef, activePathRef, viewsRef, isDirty, editsRef, htmlPagesRef, docxSaversRef, imageSaversRef, setOpenDocs, writeEdits, writeHtmlPages, setViews, setDocxDirty, restoreImageDirty, fingerprints, withTabKey, }: SetupRecoveryDeps): {
|
|
38
|
+
prepareSetupRecovery: () => Promise<void>;
|
|
39
|
+
restoreSetupRecovery: () => Promise<string | null>;
|
|
40
|
+
};
|
|
41
|
+
export {};
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { useCallback, useRef, } from "react";
|
|
2
|
+
import { getCurrentWindow } from "@tauri-apps/api/window";
|
|
3
|
+
import { errorMessage } from "@rightkit/document-core/state/errors";
|
|
4
|
+
import { captureSetupRecovery, clearSetupRecoverySnapshot, loadSetupRecoverySnapshot, saveSetupRecoverySnapshot, } from "./setupRecovery.js";
|
|
5
|
+
import { resolveAllDrafts } from "./draftResolution.js";
|
|
6
|
+
/**
|
|
7
|
+
* Setup-replacement recovery: snapshot every open dirty document (committed sources only — open
|
|
8
|
+
* block drafts are resolved first so the snapshot never carries a half-typed draft) before the
|
|
9
|
+
* installer destroys the window, then rehydrate documents/edits/views/dirty flags on the next
|
|
10
|
+
* launch when the snapshot is present. The pure snapshot (de)serialization lives in
|
|
11
|
+
* setupRecovery.ts; this hook owns the refs/callbacks that feed it and the re-entrant guard.
|
|
12
|
+
* Extracted verbatim from useDocuments; bodies are unchanged.
|
|
13
|
+
*/
|
|
14
|
+
export function useSetupRecovery({ blockDrafts, setError, openDocsRef, activePathRef, viewsRef, isDirty, editsRef, htmlPagesRef, docxSaversRef, imageSaversRef, setOpenDocs, writeEdits, writeHtmlPages, setViews, setDocxDirty, restoreImageDirty, fingerprints, withTabKey, }) {
|
|
15
|
+
const setupPreparingRef = useRef(false);
|
|
16
|
+
const prepareSetupRecovery = useCallback(async () => {
|
|
17
|
+
if (setupPreparingRef.current)
|
|
18
|
+
return;
|
|
19
|
+
setupPreparingRef.current = true;
|
|
20
|
+
try {
|
|
21
|
+
// Setup replacement resolves every open block draft before snapshotting, so the recovery
|
|
22
|
+
// snapshot carries committed source — never a half-typed draft.
|
|
23
|
+
const outcome = await resolveAllDrafts(blockDrafts, "setup-replacement", "done");
|
|
24
|
+
if (outcome !== "resolved") {
|
|
25
|
+
setupPreparingRef.current = false;
|
|
26
|
+
setError("Setup could not preserve an open block edit.");
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const snapshot = await captureSetupRecovery({
|
|
30
|
+
openDocs: openDocsRef.current,
|
|
31
|
+
activePath: activePathRef.current,
|
|
32
|
+
views: viewsRef.current,
|
|
33
|
+
isDirty,
|
|
34
|
+
editsRef,
|
|
35
|
+
htmlPagesRef,
|
|
36
|
+
docxSaversRef,
|
|
37
|
+
imageSaversRef,
|
|
38
|
+
});
|
|
39
|
+
await saveSetupRecoverySnapshot(snapshot);
|
|
40
|
+
await getCurrentWindow().destroy();
|
|
41
|
+
}
|
|
42
|
+
catch (e) {
|
|
43
|
+
setupPreparingRef.current = false;
|
|
44
|
+
setError(`Setup could not preserve unsaved changes: ${errorMessage(e)}`);
|
|
45
|
+
}
|
|
46
|
+
}, [
|
|
47
|
+
docxSaversRef,
|
|
48
|
+
editsRef,
|
|
49
|
+
htmlPagesRef,
|
|
50
|
+
imageSaversRef,
|
|
51
|
+
isDirty,
|
|
52
|
+
viewsRef,
|
|
53
|
+
blockDrafts,
|
|
54
|
+
openDocsRef,
|
|
55
|
+
activePathRef,
|
|
56
|
+
setError,
|
|
57
|
+
]);
|
|
58
|
+
const restoreSetupRecovery = useCallback(async () => {
|
|
59
|
+
const snapshot = await loadSetupRecoverySnapshot();
|
|
60
|
+
if (!snapshot)
|
|
61
|
+
return null;
|
|
62
|
+
try {
|
|
63
|
+
const restored = snapshot.documents.map((entry) => {
|
|
64
|
+
const document = { ...entry.document };
|
|
65
|
+
if (entry.document.format === "docx") {
|
|
66
|
+
if (!entry.binary?.base64)
|
|
67
|
+
throw new Error(`Recovery data is missing for ${entry.document.displayName}.`);
|
|
68
|
+
document.content = entry.binary.base64;
|
|
69
|
+
}
|
|
70
|
+
else if (entry.document.format === "image") {
|
|
71
|
+
if (!entry.binary?.base64 || !entry.binary.mime)
|
|
72
|
+
throw new Error(`Recovery data is missing for ${entry.document.displayName}.`);
|
|
73
|
+
document.content = `data:${entry.binary.mime};base64,${entry.binary.base64}`;
|
|
74
|
+
}
|
|
75
|
+
return { entry, document };
|
|
76
|
+
});
|
|
77
|
+
setOpenDocs((prev) => {
|
|
78
|
+
let next = prev;
|
|
79
|
+
for (const { document } of restored) {
|
|
80
|
+
const existing = next.find((candidate) => candidate.path === document.path);
|
|
81
|
+
const recovered = withTabKey(document, existing?.tabKey);
|
|
82
|
+
next = existing
|
|
83
|
+
? next.map((candidate) => (candidate.path === document.path ? recovered : candidate))
|
|
84
|
+
: [...next, recovered];
|
|
85
|
+
}
|
|
86
|
+
return next;
|
|
87
|
+
});
|
|
88
|
+
writeEdits((prev) => {
|
|
89
|
+
const next = { ...prev };
|
|
90
|
+
for (const { entry, document } of restored) {
|
|
91
|
+
if (entry.editedText !== undefined)
|
|
92
|
+
next[document.path] = entry.editedText;
|
|
93
|
+
}
|
|
94
|
+
return next;
|
|
95
|
+
});
|
|
96
|
+
writeHtmlPages((prev) => {
|
|
97
|
+
const next = { ...prev };
|
|
98
|
+
for (const { entry, document } of restored) {
|
|
99
|
+
if (entry.htmlPages)
|
|
100
|
+
next[document.path] = entry.htmlPages;
|
|
101
|
+
}
|
|
102
|
+
return next;
|
|
103
|
+
});
|
|
104
|
+
setViews((prev) => {
|
|
105
|
+
const next = { ...prev };
|
|
106
|
+
for (const { entry, document } of restored) {
|
|
107
|
+
if (entry.view)
|
|
108
|
+
next[document.path] = entry.view;
|
|
109
|
+
}
|
|
110
|
+
viewsRef.current = next;
|
|
111
|
+
return next;
|
|
112
|
+
});
|
|
113
|
+
setDocxDirty((prev) => {
|
|
114
|
+
const next = new Set(prev);
|
|
115
|
+
for (const { document } of restored)
|
|
116
|
+
if (document.format === "docx")
|
|
117
|
+
next.add(document.path);
|
|
118
|
+
return next;
|
|
119
|
+
});
|
|
120
|
+
for (const { document } of restored) {
|
|
121
|
+
if (document.format === "image")
|
|
122
|
+
restoreImageDirty(document.path);
|
|
123
|
+
if (document.fingerprint)
|
|
124
|
+
fingerprints.current.set(document.path, document.fingerprint);
|
|
125
|
+
}
|
|
126
|
+
const restoredPaths = new Set(restored.map(({ document }) => document.path));
|
|
127
|
+
const active = snapshot.activePath && restoredPaths.has(snapshot.activePath)
|
|
128
|
+
? snapshot.activePath
|
|
129
|
+
: activePathRef.current
|
|
130
|
+
? null
|
|
131
|
+
: (restored[0]?.document.path ?? null);
|
|
132
|
+
await clearSetupRecoverySnapshot();
|
|
133
|
+
return active;
|
|
134
|
+
}
|
|
135
|
+
catch (e) {
|
|
136
|
+
setError(`Setup recovery could not be restored: ${errorMessage(e)}`);
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
}, [
|
|
140
|
+
restoreImageDirty,
|
|
141
|
+
setDocxDirty,
|
|
142
|
+
setOpenDocs,
|
|
143
|
+
setViews,
|
|
144
|
+
viewsRef,
|
|
145
|
+
withTabKey,
|
|
146
|
+
writeEdits,
|
|
147
|
+
writeHtmlPages,
|
|
148
|
+
activePathRef,
|
|
149
|
+
fingerprints,
|
|
150
|
+
setError,
|
|
151
|
+
]);
|
|
152
|
+
return { prepareSetupRecovery, restoreSetupRecovery };
|
|
153
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { TabStripProps } from "./tabs/TabStrip.js";
|
|
2
|
+
import type { LoadedDocument } from "@rightkit/document-core/documents/types";
|
|
3
|
+
type DocsApi = {
|
|
4
|
+
activate: (path: string) => unknown;
|
|
5
|
+
open: (path: string, opts?: {
|
|
6
|
+
background?: boolean;
|
|
7
|
+
}) => unknown;
|
|
8
|
+
requestClose: TabStripProps["onRequestClose"];
|
|
9
|
+
closeAll: TabStripProps["onCloseAll"];
|
|
10
|
+
closeMany: TabStripProps["onClosePaths"];
|
|
11
|
+
newDocument: (format: "txt") => unknown;
|
|
12
|
+
isDirty: TabStripProps["isDirty"];
|
|
13
|
+
};
|
|
14
|
+
/** Tab-strip props + tab actions (reorder, bulk close, explorer open, cycle/jump), extracted from
|
|
15
|
+
* App.tsx. Tab order == openDocs order. */
|
|
16
|
+
export declare function useTabChrome({ tabs, docs, activePath, sidebarCollapsed, tabOverviewOpen, setTabOverviewOpen, pinnedPaths, onTogglePin, titleBarTitleFor, dirtyPaths, subscribeDirty, }: {
|
|
17
|
+
tabs: {
|
|
18
|
+
openDocs: LoadedDocument[];
|
|
19
|
+
setOpenDocs: React.Dispatch<React.SetStateAction<LoadedDocument[]>>;
|
|
20
|
+
subscribeEdits: TabStripProps["subscribeEdits"];
|
|
21
|
+
getEditSnapshot: TabStripProps["getEditSnapshot"];
|
|
22
|
+
};
|
|
23
|
+
docs: DocsApi;
|
|
24
|
+
activePath: string | null;
|
|
25
|
+
sidebarCollapsed: boolean;
|
|
26
|
+
tabOverviewOpen: boolean;
|
|
27
|
+
setTabOverviewOpen: (open: boolean) => void;
|
|
28
|
+
pinnedPaths: TabStripProps["pinnedPaths"];
|
|
29
|
+
onTogglePin: (path: string) => unknown;
|
|
30
|
+
titleBarTitleFor: (d: LoadedDocument) => string;
|
|
31
|
+
dirtyPaths: TabStripProps["dirtyPaths"];
|
|
32
|
+
subscribeDirty: TabStripProps["subscribeDirty"];
|
|
33
|
+
}): {
|
|
34
|
+
tabStripProps: TabStripProps;
|
|
35
|
+
openPathsSet: Set<string>;
|
|
36
|
+
openExplorerPath: (path: string) => void;
|
|
37
|
+
cycleTab: (dir: 1 | -1) => void;
|
|
38
|
+
jumpToTab: (index: number) => void;
|
|
39
|
+
};
|
|
40
|
+
export {};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { useCallback, useMemo } from "react";
|
|
2
|
+
import { isLocalFileDoc } from "./sidebar/SidebarPathUtils.js";
|
|
3
|
+
/** Tab-strip props + tab actions (reorder, bulk close, explorer open, cycle/jump), extracted from
|
|
4
|
+
* App.tsx. Tab order == openDocs order. */
|
|
5
|
+
export function useTabChrome({ tabs, docs, activePath, sidebarCollapsed, tabOverviewOpen, setTabOverviewOpen, pinnedPaths, onTogglePin, titleBarTitleFor, dirtyPaths, subscribeDirty, }) {
|
|
6
|
+
const { openDocs, subscribeEdits, getEditSnapshot } = tabs;
|
|
7
|
+
const { activate, open, requestClose, closeAll, closeMany, newDocument, isDirty } = docs;
|
|
8
|
+
const reorderTabs = useCallback((fromPath, toPath, position = "before") => {
|
|
9
|
+
tabs.setOpenDocs((prev) => {
|
|
10
|
+
const from = prev.findIndex((doc) => doc.path === fromPath);
|
|
11
|
+
const to = prev.findIndex((doc) => doc.path === toPath);
|
|
12
|
+
if (from < 0 || to < 0 || from === to)
|
|
13
|
+
return prev;
|
|
14
|
+
const next = [...prev];
|
|
15
|
+
const [moved] = next.splice(from, 1);
|
|
16
|
+
const targetAfterRemoval = from < to ? to - 1 : to;
|
|
17
|
+
const insertAt = position === "after" ? targetAfterRemoval + 1 : targetAfterRemoval;
|
|
18
|
+
next.splice(insertAt, 0, moved);
|
|
19
|
+
return next;
|
|
20
|
+
});
|
|
21
|
+
}, [tabs]);
|
|
22
|
+
// Tab-strip bulk closes: the same Save-All/Discard-All chooser Close All uses, driven by closeMany
|
|
23
|
+
// with the chosen subset. Order = openDocs order so "to the right" means spatial, not sorted.
|
|
24
|
+
const closeOthers = useCallback((path) => {
|
|
25
|
+
closeMany(openDocs.filter((d) => d.path !== path).map((d) => d.path));
|
|
26
|
+
}, [closeMany, openDocs]);
|
|
27
|
+
const closeToRight = useCallback((path) => {
|
|
28
|
+
const at = openDocs.findIndex((d) => d.path === path);
|
|
29
|
+
if (at < 0)
|
|
30
|
+
return;
|
|
31
|
+
closeMany(openDocs.slice(at + 1).map((d) => d.path));
|
|
32
|
+
}, [closeMany, openDocs]);
|
|
33
|
+
// Explorer row click: the entry's path is already broker-granted by explorer_list_dir, so this is
|
|
34
|
+
// the same open-or-activate flow recents use (minus the token grant step).
|
|
35
|
+
const openExplorerPath = useCallback((path) => {
|
|
36
|
+
if (openDocs.some((d) => d.path === path)) {
|
|
37
|
+
void activate(path);
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
void open(path);
|
|
41
|
+
}
|
|
42
|
+
}, [openDocs, activate, open]);
|
|
43
|
+
// Tab navigation operates on the visible open-tab order; Ctrl+9 maps to the last tab (browser-style).
|
|
44
|
+
const cycleTab = (dir) => {
|
|
45
|
+
if (openDocs.length < 2)
|
|
46
|
+
return;
|
|
47
|
+
const i = openDocs.findIndex((d) => d.path === activePath);
|
|
48
|
+
const n = openDocs.length;
|
|
49
|
+
activate(openDocs[((((i < 0 ? 0 : i) + dir) % n) + n) % n].path);
|
|
50
|
+
};
|
|
51
|
+
const jumpToTab = (index) => {
|
|
52
|
+
if (!openDocs.length)
|
|
53
|
+
return;
|
|
54
|
+
const target = index === 8 ? openDocs.length - 1 : index;
|
|
55
|
+
const doc = openDocs[target];
|
|
56
|
+
if (doc)
|
|
57
|
+
activate(doc.path);
|
|
58
|
+
};
|
|
59
|
+
// Tab-strip props for the titlebar — memoized so the memoized TabStrip skips per-keystroke App
|
|
60
|
+
// re-renders the same way Sidebar/Toolbar do. openDocs order IS the tab order (single source).
|
|
61
|
+
const toggleSidebarEvent = useCallback(() => window.dispatchEvent(new CustomEvent("viewright:toggle-sidebar")), []);
|
|
62
|
+
const openLocalPaths = useMemo(() => openDocs.filter(isLocalFileDoc).map((d) => d.path), [openDocs]);
|
|
63
|
+
const openPathsSet = useMemo(() => new Set(openDocs.map((d) => d.path)), [openDocs]);
|
|
64
|
+
const tabStripProps = useMemo(() => ({
|
|
65
|
+
docs: openDocs,
|
|
66
|
+
activePath,
|
|
67
|
+
sidebarCollapsed,
|
|
68
|
+
onToggleSidebar: toggleSidebarEvent,
|
|
69
|
+
onActivate: (path) => void activate(path),
|
|
70
|
+
onRequestClose: requestClose,
|
|
71
|
+
onCloseAll: closeAll,
|
|
72
|
+
onCloseOthers: closeOthers,
|
|
73
|
+
onCloseRight: closeToRight,
|
|
74
|
+
onClosePaths: closeMany,
|
|
75
|
+
onReorderTabs: reorderTabs,
|
|
76
|
+
onNewDocument: () => newDocument("txt"),
|
|
77
|
+
onTogglePin: (path) => void onTogglePin(path),
|
|
78
|
+
pinnedPaths,
|
|
79
|
+
openLocalPaths,
|
|
80
|
+
titleFor: titleBarTitleFor,
|
|
81
|
+
isDirty,
|
|
82
|
+
dirtyPaths,
|
|
83
|
+
subscribeDirty,
|
|
84
|
+
subscribeEdits,
|
|
85
|
+
getEditSnapshot,
|
|
86
|
+
computeTitle: titleBarTitleFor,
|
|
87
|
+
overviewOpen: tabOverviewOpen,
|
|
88
|
+
onOverviewOpenChange: setTabOverviewOpen,
|
|
89
|
+
}), [
|
|
90
|
+
openDocs,
|
|
91
|
+
activePath,
|
|
92
|
+
sidebarCollapsed,
|
|
93
|
+
toggleSidebarEvent,
|
|
94
|
+
activate,
|
|
95
|
+
requestClose,
|
|
96
|
+
closeAll,
|
|
97
|
+
closeOthers,
|
|
98
|
+
closeToRight,
|
|
99
|
+
closeMany,
|
|
100
|
+
reorderTabs,
|
|
101
|
+
newDocument,
|
|
102
|
+
onTogglePin,
|
|
103
|
+
pinnedPaths,
|
|
104
|
+
openLocalPaths,
|
|
105
|
+
titleBarTitleFor,
|
|
106
|
+
isDirty,
|
|
107
|
+
dirtyPaths,
|
|
108
|
+
subscribeDirty,
|
|
109
|
+
subscribeEdits,
|
|
110
|
+
getEditSnapshot,
|
|
111
|
+
tabOverviewOpen,
|
|
112
|
+
setTabOverviewOpen,
|
|
113
|
+
]);
|
|
114
|
+
return { tabStripProps, openPathsSet, openExplorerPath, cycleTab, jumpToTab };
|
|
115
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type LoadedDocument, type ViewMeta, type ViewMode } from "@rightkit/document-core/documents/types";
|
|
2
|
+
import type { BlockDraftRegistry } from "@rightkit/document-core/app/blockDrafts";
|
|
3
|
+
/**
|
|
4
|
+
* Per-format view state: preview/source mode per doc, plus the persisted visible location
|
|
5
|
+
* (text/html scroll via the document pane, Markdown via its editor scroller; PDF page/zoom/fit
|
|
6
|
+
* reported by the
|
|
7
|
+
* viewer). Persisted per file (debounced) so reopening restores where you were. Returns the state
|
|
8
|
+
* setter + refs so the document-IO flows can seed view state when a file opens/saves.
|
|
9
|
+
*/
|
|
10
|
+
export declare function useViewState(activePath: string | null, openDocs: LoadedDocument[], blockDrafts?: BlockDraftRegistry): {
|
|
11
|
+
views: Record<string, ViewMode>;
|
|
12
|
+
setViews: import("react").Dispatch<import("react").SetStateAction<Record<string, ViewMode>>>;
|
|
13
|
+
viewsRef: import("react").RefObject<Record<string, ViewMode>>;
|
|
14
|
+
viewMetaRef: import("react").RefObject<Record<string, ViewMeta>>;
|
|
15
|
+
persistViewMeta: (path: string) => void;
|
|
16
|
+
contentScrollRef: import("react").RefObject<HTMLDivElement | null>;
|
|
17
|
+
requestViewChange: (next: ViewMode) => Promise<void>;
|
|
18
|
+
confirmViewChange: (decision: "done" | "discard" | "stay") => Promise<void>;
|
|
19
|
+
cancelViewChange: () => void;
|
|
20
|
+
pendingViewChange: {
|
|
21
|
+
next: ViewMode;
|
|
22
|
+
drafts: ReturnType<BlockDraftRegistry["snapshotModified"]>;
|
|
23
|
+
} | null;
|
|
24
|
+
onContentScroll: () => void;
|
|
25
|
+
onPdfViewState: (m: ViewMeta) => void;
|
|
26
|
+
dropViewState: (path: string) => void;
|
|
27
|
+
};
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
2
|
+
import { saveViewState } from "../state/library.js";
|
|
3
|
+
import { isUntitled } from "@rightkit/document-core/documents/types";
|
|
4
|
+
import { resolveDocumentDrafts } from "./draftResolution.js";
|
|
5
|
+
/**
|
|
6
|
+
* Per-format view state: preview/source mode per doc, plus the persisted visible location
|
|
7
|
+
* (text/html scroll via the document pane, Markdown via its editor scroller; PDF page/zoom/fit
|
|
8
|
+
* reported by the
|
|
9
|
+
* viewer). Persisted per file (debounced) so reopening restores where you were. Returns the state
|
|
10
|
+
* setter + refs so the document-IO flows can seed view state when a file opens/saves.
|
|
11
|
+
*/
|
|
12
|
+
export function useViewState(activePath, openDocs, blockDrafts) {
|
|
13
|
+
const [views, setViews] = useState({});
|
|
14
|
+
// Points at the active non-PDF viewer's actual scroller. Markdown owns an inner scroller so its
|
|
15
|
+
// fixed editing hero never overlaps content; other formats use the document pane itself.
|
|
16
|
+
const contentScrollRef = useRef(null);
|
|
17
|
+
const viewMetaRef = useRef({});
|
|
18
|
+
const viewsRef = useRef({});
|
|
19
|
+
const viewSaveTimer = useRef(null);
|
|
20
|
+
const persistViewMeta = useCallback((path) => {
|
|
21
|
+
if (isUntitled(path))
|
|
22
|
+
return;
|
|
23
|
+
if (viewSaveTimer.current)
|
|
24
|
+
window.clearTimeout(viewSaveTimer.current);
|
|
25
|
+
viewSaveTimer.current = window.setTimeout(() => {
|
|
26
|
+
void saveViewState(path, viewsRef.current[path] ?? "preview", viewMetaRef.current[path]);
|
|
27
|
+
}, 400);
|
|
28
|
+
}, []);
|
|
29
|
+
// Restore the saved scroll position for the active non-PDF doc once its scroller is in the DOM.
|
|
30
|
+
// Best-effort for tall async-rendered content (e.g. Markdown); PDF restores page/zoom internally.
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
const el = contentScrollRef.current;
|
|
33
|
+
if (!el || !activePath)
|
|
34
|
+
return;
|
|
35
|
+
const doc = openDocs.find((d) => d.path === activePath);
|
|
36
|
+
if (!doc || doc.format === "pdf" || doc.format === "web")
|
|
37
|
+
return;
|
|
38
|
+
const target = viewMetaRef.current[activePath]?.scroll ?? 0;
|
|
39
|
+
const raf = requestAnimationFrame(() => {
|
|
40
|
+
el.scrollTop = target;
|
|
41
|
+
});
|
|
42
|
+
return () => cancelAnimationFrame(raf);
|
|
43
|
+
}, [activePath, openDocs]);
|
|
44
|
+
const [pendingViewChange, setPendingViewChange] = useState(null);
|
|
45
|
+
const applyViewFor = useCallback(async (path, next) => {
|
|
46
|
+
setViews((v) => ({ ...v, [path]: next }));
|
|
47
|
+
viewsRef.current[path] = next;
|
|
48
|
+
if (!isUntitled(path))
|
|
49
|
+
await saveViewState(path, next, viewMetaRef.current[path]);
|
|
50
|
+
}, []);
|
|
51
|
+
// requestViewChange is handed to every live DocumentRouter, whose memo comparator checks it by
|
|
52
|
+
// identity. Read the active path + draft registry through refs so the callback stays stable across
|
|
53
|
+
// tab switches; otherwise every warm pane (up to the live cap) re-renders on each switch.
|
|
54
|
+
const activePathRef = useRef(activePath);
|
|
55
|
+
const blockDraftsRef = useRef(blockDrafts);
|
|
56
|
+
useLayoutEffect(() => {
|
|
57
|
+
activePathRef.current = activePath;
|
|
58
|
+
blockDraftsRef.current = blockDrafts;
|
|
59
|
+
}, [activePath, blockDrafts]);
|
|
60
|
+
const requestViewChange = useCallback(async (next) => {
|
|
61
|
+
const path = activePathRef.current;
|
|
62
|
+
if (!path)
|
|
63
|
+
return;
|
|
64
|
+
const drafts = blockDraftsRef.current;
|
|
65
|
+
if (drafts) {
|
|
66
|
+
const modified = drafts.snapshotModified(path);
|
|
67
|
+
if (modified.length > 0) {
|
|
68
|
+
// Modified draft: offer Done / Discard / Stay instead of silently discarding.
|
|
69
|
+
setPendingViewChange({ next, drafts: modified });
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
// Clean draft (not in snapshotModified but exists): close silently per contract.
|
|
73
|
+
// snapshotAll returns all drafts for this document (clean only at this point, since
|
|
74
|
+
// modified/stale/committing are already handled above).
|
|
75
|
+
const allForDoc = drafts.snapshotAll(path);
|
|
76
|
+
if (allForDoc.length > 0) {
|
|
77
|
+
for (const d of allForDoc)
|
|
78
|
+
drafts.discard(d.identity);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
await applyViewFor(path, next);
|
|
82
|
+
}, [applyViewFor]);
|
|
83
|
+
const confirmViewChange = useCallback(async (decision) => {
|
|
84
|
+
if (!pendingViewChange || !activePath || !blockDrafts) {
|
|
85
|
+
setPendingViewChange(null);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (decision === "stay") {
|
|
89
|
+
setPendingViewChange(null);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const outcome = await resolveDocumentDrafts(blockDrafts, activePath, "view-change", decision);
|
|
93
|
+
if (outcome !== "resolved") {
|
|
94
|
+
// failed/cancelled: keep dialog open with fresh draft snapshot so error/reason stays visible
|
|
95
|
+
if (blockDrafts) {
|
|
96
|
+
setPendingViewChange({
|
|
97
|
+
next: pendingViewChange.next,
|
|
98
|
+
drafts: blockDrafts.snapshotModified(activePath),
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const next = pendingViewChange.next;
|
|
104
|
+
setPendingViewChange(null);
|
|
105
|
+
await applyViewFor(activePath, next);
|
|
106
|
+
}, [activePath, applyViewFor, blockDrafts, pendingViewChange]);
|
|
107
|
+
const cancelViewChange = useCallback(() => setPendingViewChange(null), []);
|
|
108
|
+
// Capture the non-PDF scroll position for the active doc (PDF reports its own page/zoom/fit).
|
|
109
|
+
const onContentScroll = useCallback(() => {
|
|
110
|
+
const el = contentScrollRef.current;
|
|
111
|
+
if (!el || !activePath || isUntitled(activePath))
|
|
112
|
+
return;
|
|
113
|
+
const doc = openDocs.find((d) => d.path === activePath);
|
|
114
|
+
if (!doc || doc.format === "pdf" || doc.format === "web")
|
|
115
|
+
return;
|
|
116
|
+
viewMetaRef.current[activePath] = {
|
|
117
|
+
...(viewMetaRef.current[activePath] ?? {}),
|
|
118
|
+
scroll: el.scrollTop,
|
|
119
|
+
};
|
|
120
|
+
persistViewMeta(activePath);
|
|
121
|
+
}, [activePath, openDocs, persistViewMeta]);
|
|
122
|
+
// Merge PDF view state (page/zoom/fit) reported by the viewer, and persist it.
|
|
123
|
+
const onPdfViewState = useCallback((m) => {
|
|
124
|
+
if (!activePath)
|
|
125
|
+
return;
|
|
126
|
+
viewMetaRef.current[activePath] = { ...(viewMetaRef.current[activePath] ?? {}), ...m };
|
|
127
|
+
persistViewMeta(activePath);
|
|
128
|
+
}, [activePath, persistViewMeta]);
|
|
129
|
+
// Drop a closed tab's view state (mode + scroll/page/zoom meta) from all three stores. Without
|
|
130
|
+
// this, `views`/`viewsRef`/`viewMetaRef` only ever grow — every path that was ever opened stays
|
|
131
|
+
// keyed in these Records for the life of the app. Exposed for useDocumentWatcher's teardownDoc to
|
|
132
|
+
// call alongside its other per-close pruning (fingerprints, editsRef, etc.); this hook doesn't call
|
|
133
|
+
// it itself since it has no visibility into tab close.
|
|
134
|
+
const dropViewState = useCallback((path) => {
|
|
135
|
+
setViews((v) => {
|
|
136
|
+
if (!(path in v))
|
|
137
|
+
return v;
|
|
138
|
+
const n = { ...v };
|
|
139
|
+
delete n[path];
|
|
140
|
+
return n;
|
|
141
|
+
});
|
|
142
|
+
delete viewsRef.current[path];
|
|
143
|
+
delete viewMetaRef.current[path];
|
|
144
|
+
}, []);
|
|
145
|
+
return {
|
|
146
|
+
views,
|
|
147
|
+
setViews,
|
|
148
|
+
viewsRef,
|
|
149
|
+
viewMetaRef,
|
|
150
|
+
persistViewMeta,
|
|
151
|
+
contentScrollRef,
|
|
152
|
+
requestViewChange,
|
|
153
|
+
confirmViewChange,
|
|
154
|
+
cancelViewChange,
|
|
155
|
+
pendingViewChange,
|
|
156
|
+
onContentScroll,
|
|
157
|
+
onPdfViewState,
|
|
158
|
+
dropViewState,
|
|
159
|
+
};
|
|
160
|
+
}
|
package/dist/appName.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { type ReactNode, type RefObject, type UIEventHandler } from "react";
|
|
2
|
+
import type { LoadedDocument, ReadEditMode, ViewMode, ViewMeta, SearchProvider } from "@rightkit/document-core/documents/types";
|
|
3
|
+
import type { DocxMenuApi } from "@rightkit/docx-editor/documents/docx/menuApi";
|
|
4
|
+
import type { ImageSaveFunction } from "@rightkit/media-ui";
|
|
5
|
+
import type { HtmlPageBuffer } from "@rightkit/html-editor/documents/html/sessionTypes";
|
|
6
|
+
import type { MarkdownExportApi } from "@rightkit/markdown-editor/documents/markdown/export/markdownExportTypes";
|
|
7
|
+
import type { SourceEditorViewState } from "./txt/TxtEditor.js";
|
|
8
|
+
import type { PdfSaveFunction } from "@rightkit/document-core/documents/pdf/pdfEditor";
|
|
9
|
+
import type { PdfSavePlan } from "@rightkit/document-core/documents/pdf/pdfEditModel";
|
|
10
|
+
export declare const MarkdownTextFallback: ({ text }: {
|
|
11
|
+
text: string;
|
|
12
|
+
}) => import("react").JSX.Element;
|
|
13
|
+
interface Props {
|
|
14
|
+
doc: LoadedDocument;
|
|
15
|
+
view: ViewMode;
|
|
16
|
+
/** Wave 2 per-document Read/Edit (PDF: Markup) mode — "read" keeps viewers view-first,
|
|
17
|
+
* "edit" exposes the format's existing editing surface as the floating format bar. */
|
|
18
|
+
readEdit?: ReadEditMode;
|
|
19
|
+
devMode?: boolean;
|
|
20
|
+
/** Live editable text (edits ?? content) + handlers, owned by App. */
|
|
21
|
+
text: string;
|
|
22
|
+
/** Per-document live edit subscription used by split panes without repainting App. */
|
|
23
|
+
subscribeEdits?: (path: string, cb: () => void) => () => void;
|
|
24
|
+
getEditSnapshot?: (path: string) => string | undefined;
|
|
25
|
+
getExternalSeq?: (path: string) => number;
|
|
26
|
+
/** App-level source selection/scroll store, retained when a dirty source pane shells. */
|
|
27
|
+
sourceViewStateRef?: RefObject<Record<string, SourceEditorViewState>>;
|
|
28
|
+
onChange: (text: string) => void;
|
|
29
|
+
onSave: () => void;
|
|
30
|
+
/** DOCX editor writes its bytes back to disk via its own File→Save. */
|
|
31
|
+
onDocxSave: (buffer: ArrayBuffer) => void;
|
|
32
|
+
/** DOCX edited (for unsaved-close warning). */
|
|
33
|
+
onDocxChange: () => void;
|
|
34
|
+
/** Register the DOCX editor's programmatic save (for the close dialog). */
|
|
35
|
+
registerDocxSave: (fn: (() => Promise<ArrayBuffer | null>) | null) => void;
|
|
36
|
+
/** Register the image editor export function for app-level Save / Save As / Ctrl+S. */
|
|
37
|
+
registerImageSave: (fn: ImageSaveFunction | null) => void;
|
|
38
|
+
/** Lift the DOCX command surface for the app's File/Format/Insert/Help menus. */
|
|
39
|
+
onDocxToolbar: (api: DocxMenuApi | null) => void;
|
|
40
|
+
onImageChange: () => void;
|
|
41
|
+
/** Page to a neighbouring image in the same folder, replacing this tab in place (image branch). */
|
|
42
|
+
onOpenImageSibling: (currentPath: string, targetPath: string) => void;
|
|
43
|
+
registerSearchProvider: (provider: SearchProvider | null) => void;
|
|
44
|
+
/** Keep-alive: whether this is the visible (active) pane. The real renderer doesn't branch on it
|
|
45
|
+
* (inactive panes stay mounted, just CSS-hidden by the parent); it's a hook for test harnesses. */
|
|
46
|
+
active?: boolean;
|
|
47
|
+
/** Restored PDF view state (page/zoom/fit) and a callback to persist changes. */
|
|
48
|
+
pdfInitialView?: ViewMeta;
|
|
49
|
+
onPdfViewState?: (m: ViewMeta) => void;
|
|
50
|
+
/** Register the active PDF pane's print function (uses its own already-loaded document — see
|
|
51
|
+
* documentPrinting.ts printPdfDocument for why re-fetching the stream URL is unreliable). */
|
|
52
|
+
registerPdfPrint?: (fn: (() => Promise<boolean>) | null) => void;
|
|
53
|
+
/** Register PDF annotation serialization for app-level Save / Save As / Ctrl+S. */
|
|
54
|
+
registerPdfSave?: (fn: PdfSaveFunction | null) => void;
|
|
55
|
+
/** Register PDF two-phase save plan for source/OCR edits plus annotations. */
|
|
56
|
+
registerPdfSavePlan?: (fn: (() => Promise<PdfSavePlan>) | null) => void;
|
|
57
|
+
/** Publish PDF annotation dirtiness to app-level close, quit, & autosave handling. */
|
|
58
|
+
onPdfDirtyChange?: (dirty: boolean) => void;
|
|
59
|
+
/** Register canonical rendered Markdown export for app-level print, export, & clipboard commands. */
|
|
60
|
+
registerMarkdownExport?: (api: MarkdownExportApi | null) => void;
|
|
61
|
+
/** Markdown live view owns its scroll below fixed authoring chrome. */
|
|
62
|
+
markdownScrollRef?: RefObject<HTMLDivElement | null>;
|
|
63
|
+
onMarkdownScroll?: UIEventHandler<HTMLDivElement>;
|
|
64
|
+
/** Shared FindBar element, placed by each searchable document toolbar. */
|
|
65
|
+
findBar?: ReactNode;
|
|
66
|
+
findMode?: "find" | "replace";
|
|
67
|
+
/** Shell-level preview/source toggle. */
|
|
68
|
+
onChangeView?: (view: ViewMode) => void;
|
|
69
|
+
/** Open the shared find UI — used by the HTML preview to intercept Ctrl+F inside its iframe. */
|
|
70
|
+
onRequestFind?: () => void;
|
|
71
|
+
/** Close the shared find bar — used when a surface that owns its own search goes away. */
|
|
72
|
+
onCloseFind?: () => void;
|
|
73
|
+
onRequestReplace?: () => void;
|
|
74
|
+
/** Open a brokered absolute path as a tab — used for HTML internal relative-link navigation.
|
|
75
|
+
* `background` (new-tab gesture) adds the tab without switching to it. */
|
|
76
|
+
onOpenPath?: (path: string, opts?: {
|
|
77
|
+
background?: boolean;
|
|
78
|
+
}) => void;
|
|
79
|
+
/** MDX branch only: reports every grant minted for the document's compile-time imports + in-preview
|
|
80
|
+
* link clicks, so the owning tab's close can revoke them. */
|
|
81
|
+
onMdxImportGranted?: (entryPath: string, grantedPath: string) => void;
|
|
82
|
+
/** HTML site-session state + handlers (consumed only by the html branch). */
|
|
83
|
+
html?: {
|
|
84
|
+
pageBuffers: Record<string, HtmlPageBuffer>;
|
|
85
|
+
dirtyPagePaths: string[];
|
|
86
|
+
onEnsurePage: (entryPath: string, pagePath: string) => void;
|
|
87
|
+
onPageChange: (entryPath: string, pagePath: string, text: string) => void;
|
|
88
|
+
onSetActivePage: (entryPath: string, pagePath: string) => void;
|
|
89
|
+
onSavePage: (entryPath: string, pagePath: string) => void;
|
|
90
|
+
onDiscardPage: (entryPath: string, pagePath: string) => void;
|
|
91
|
+
onReloadFromDisk: (path: string, options?: {
|
|
92
|
+
force?: boolean;
|
|
93
|
+
}) => void;
|
|
94
|
+
initialPreviewScrollY: number;
|
|
95
|
+
onPreviewScroll: (y: number) => void;
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
declare function DocumentRouterImpl({ doc, view, readEdit, devMode, text, subscribeEdits, getEditSnapshot, getExternalSeq, sourceViewStateRef, onChange, onSave, onDocxSave, onDocxChange, registerDocxSave, registerImageSave, onDocxToolbar, onImageChange, onOpenImageSibling, registerSearchProvider, active, pdfInitialView, onPdfViewState, registerPdfPrint, registerPdfSave, registerPdfSavePlan, onPdfDirtyChange, registerMarkdownExport, markdownScrollRef, onMarkdownScroll, findBar, findMode, onChangeView, onRequestFind, onCloseFind, onRequestReplace, onOpenPath, onMdxImportGranted, html, }: Props): import("react").JSX.Element;
|
|
99
|
+
type MemoProps = Pick<Props, "doc" | "active" | "view" | "readEdit" | "text" | "findBar" | "findMode" | "onRequestReplace" | "onChangeView" | "html">;
|
|
100
|
+
export declare function documentRouterPropsEqual(prev: MemoProps, next: MemoProps): boolean;
|
|
101
|
+
export declare const DocumentRouter: import("react").MemoExoticComponent<typeof DocumentRouterImpl>;
|
|
102
|
+
export {};
|