@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,207 @@
|
|
|
1
|
+
import { useCallback, useEffect, useReducer, useRef, useState } from "react";
|
|
2
|
+
/**
|
|
3
|
+
* The shared open-document state: tabs (openDocs + activePath), per-path unsaved TXT buffers
|
|
4
|
+
* (edits, with a synchronous editsRef mirror so a save/close in the same tick as a keystroke reads
|
|
5
|
+
* the latest text), the DOCX dirty set, and the per-HTML-session sub-page buffers. Owned separately
|
|
6
|
+
* so useViewState/useLibrary can take activePath/openDocs while useDocuments takes the setters
|
|
7
|
+
* (breaks the otherwise-circular wiring).
|
|
8
|
+
*/
|
|
9
|
+
export function useDocumentTabs() {
|
|
10
|
+
const [openDocs, setOpenDocs] = useState([]);
|
|
11
|
+
const [activePath, setActivePath] = useState(null);
|
|
12
|
+
// editsRef + a per-path pub/sub ARE the unsaved-text store (the single source of truth — NOT React
|
|
13
|
+
// state). A keystroke mutates the ref and notifies ONLY that path's subscribers (the active editor's
|
|
14
|
+
// bottom-bar word-count/dirty and that tab's title), so App and the rest of the chrome never re-render
|
|
15
|
+
// on a keystroke. `bumpEdits` exists only to re-render App on the rare EXTERNAL write
|
|
16
|
+
// (reopen/conflict/restore/save) so the controlled editor's `text` prop re-reads the ref.
|
|
17
|
+
const editsRef = useRef({});
|
|
18
|
+
const [, bumpEdits] = useReducer((n) => n + 1, 0);
|
|
19
|
+
const editsSubsRef = useRef(new Map());
|
|
20
|
+
const liveNotifyTimersRef = useRef(new Map());
|
|
21
|
+
const notifyEdits = useCallback((path) => {
|
|
22
|
+
// Snapshot the Set before iterating: a subscriber that unsubscribes inside its own callback would
|
|
23
|
+
// otherwise make Set.forEach skip not-yet-visited callbacks in the same pass.
|
|
24
|
+
const cbs = editsSubsRef.current.get(path);
|
|
25
|
+
if (cbs)
|
|
26
|
+
for (const cb of [...cbs])
|
|
27
|
+
cb();
|
|
28
|
+
}, []);
|
|
29
|
+
const scheduleLiveNotify = useCallback((path) => {
|
|
30
|
+
const existing = liveNotifyTimersRef.current.get(path);
|
|
31
|
+
if (existing)
|
|
32
|
+
clearTimeout(existing);
|
|
33
|
+
liveNotifyTimersRef.current.set(path, setTimeout(() => {
|
|
34
|
+
liveNotifyTimersRef.current.delete(path);
|
|
35
|
+
notifyEdits(path);
|
|
36
|
+
}, 90));
|
|
37
|
+
}, [notifyEdits]);
|
|
38
|
+
const clearLiveNotify = useCallback((path) => {
|
|
39
|
+
const existing = liveNotifyTimersRef.current.get(path);
|
|
40
|
+
if (!existing)
|
|
41
|
+
return;
|
|
42
|
+
clearTimeout(existing);
|
|
43
|
+
liveNotifyTimersRef.current.delete(path);
|
|
44
|
+
}, []);
|
|
45
|
+
useEffect(() => () => {
|
|
46
|
+
for (const timer of liveNotifyTimersRef.current.values())
|
|
47
|
+
clearTimeout(timer);
|
|
48
|
+
liveNotifyTimersRef.current.clear();
|
|
49
|
+
}, []);
|
|
50
|
+
const subscribeEdits = useCallback((path, cb) => {
|
|
51
|
+
let set = editsSubsRef.current.get(path);
|
|
52
|
+
if (!set) {
|
|
53
|
+
set = new Set();
|
|
54
|
+
editsSubsRef.current.set(path, set);
|
|
55
|
+
}
|
|
56
|
+
set.add(cb);
|
|
57
|
+
return () => {
|
|
58
|
+
set.delete(cb);
|
|
59
|
+
if (set.size === 0)
|
|
60
|
+
editsSubsRef.current.delete(path);
|
|
61
|
+
};
|
|
62
|
+
}, []);
|
|
63
|
+
const getEditSnapshot = useCallback((path) => editsRef.current[path], []);
|
|
64
|
+
// Origin tag for external ingress: bumped ONLY by writeEdits (reopen/reload/restore/conflict/
|
|
65
|
+
// duplicate) when a path's value actually changes — never by writeEditsLive (core publishes).
|
|
66
|
+
// The Markdown core provider applies external text only when this advances past what it applied.
|
|
67
|
+
const externalSeqRef = useRef({});
|
|
68
|
+
const getExternalSeq = useCallback((path) => externalSeqRef.current[path] ?? 0, []);
|
|
69
|
+
// Keystroke path: mutate the ref synchronously + debounce this path's live chrome subscribers. No App
|
|
70
|
+
// re-render. Saves/closes read editsRef immediately, while title/word-count repaint once after a burst
|
|
71
|
+
// of typing instead of making a React commit for every key.
|
|
72
|
+
const writeEditsLive = useCallback((path, text) => {
|
|
73
|
+
editsRef.current[path] = text;
|
|
74
|
+
scheduleLiveNotify(path);
|
|
75
|
+
}, [scheduleLiveNotify]);
|
|
76
|
+
// External path (reopen/conflict/restore/save/duplicate): mutate the ref, notify any changed path's
|
|
77
|
+
// subscribers, and bump so App re-renders once and the controlled editor re-syncs from the ref.
|
|
78
|
+
const writeEdits = useCallback((updater) => {
|
|
79
|
+
const prev = editsRef.current;
|
|
80
|
+
editsRef.current = updater(prev);
|
|
81
|
+
for (const path of new Set([...Object.keys(prev), ...Object.keys(editsRef.current)])) {
|
|
82
|
+
if (editsRef.current[path] !== prev[path])
|
|
83
|
+
externalSeqRef.current[path] = (externalSeqRef.current[path] ?? 0) + 1;
|
|
84
|
+
}
|
|
85
|
+
for (const path of editsSubsRef.current.keys()) {
|
|
86
|
+
if (editsRef.current[path] !== prev[path]) {
|
|
87
|
+
clearLiveNotify(path);
|
|
88
|
+
notifyEdits(path);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
bumpEdits();
|
|
92
|
+
}, [clearLiveNotify, notifyEdits]);
|
|
93
|
+
const [docxDirty, setDocxDirtyState] = useState(new Set());
|
|
94
|
+
const docxDirtyRef = useRef(new Set());
|
|
95
|
+
const setDocxDirty = useCallback((next) => {
|
|
96
|
+
const value = typeof next === "function"
|
|
97
|
+
? next(docxDirtyRef.current)
|
|
98
|
+
: next;
|
|
99
|
+
docxDirtyRef.current = value;
|
|
100
|
+
setDocxDirtyState(value);
|
|
101
|
+
}, []);
|
|
102
|
+
const [pdfDirty, setPdfDirtyState] = useState(new Set());
|
|
103
|
+
const pdfDirtyRef = useRef(new Set());
|
|
104
|
+
const setPdfDirty = useCallback((next) => {
|
|
105
|
+
const value = typeof next === "function"
|
|
106
|
+
? next(pdfDirtyRef.current)
|
|
107
|
+
: next;
|
|
108
|
+
pdfDirtyRef.current = value;
|
|
109
|
+
setPdfDirtyState(value);
|
|
110
|
+
}, []);
|
|
111
|
+
// Text/HTML keep their buffers here; binary editors contribute path markers to the same tab store.
|
|
112
|
+
const [imageDirty, setImageDirtyState] = useState(new Set());
|
|
113
|
+
const imageDirtyRef = useRef(new Set());
|
|
114
|
+
const setImageDirty = useCallback((next) => {
|
|
115
|
+
const value = typeof next === "function"
|
|
116
|
+
? next(imageDirtyRef.current)
|
|
117
|
+
: next;
|
|
118
|
+
imageDirtyRef.current = value;
|
|
119
|
+
setImageDirtyState(value);
|
|
120
|
+
}, []);
|
|
121
|
+
// HTML site sessions. Sub-page buffers live here (App level), not inside HtmlDocument, because only
|
|
122
|
+
// the active document is mounted — component state would be lost on a tab switch and invisible to
|
|
123
|
+
// the window-quit guard. The synchronous htmlPagesRef mirror lets a save/close read the latest
|
|
124
|
+
// edits in the same tick as a keystroke (same pattern as editsRef).
|
|
125
|
+
const [htmlPages, setHtmlPages] = useState({});
|
|
126
|
+
const htmlPagesRef = useRef({});
|
|
127
|
+
// T2-3: the React-state publish below feeds App's per-path html props (htmlPropsFor), which cascade
|
|
128
|
+
// into a DocumentRouter re-render — debounce it for the per-keystroke edit path (typing in an HTML
|
|
129
|
+
// sub-page) so that doesn't storm the whole app. `htmlPagesRef` is updated SYNCHRONOUSLY on every
|
|
130
|
+
// call regardless of debounce, so save/dirty logic (which reads the ref, not this state) is never
|
|
131
|
+
// stale; `notifyEdits` also stays synchronous so the bottom-bar/tab dirty marker stays live. Every
|
|
132
|
+
// caller EXCEPT the live-typing path (useHtmlSession's setHtmlPageEdited) passes immediate=true
|
|
133
|
+
// (the default), which also FLUSHES any pending debounced publish — save/discard/reload always see
|
|
134
|
+
// the latest buffer, so the state is never stale when it actually matters.
|
|
135
|
+
const htmlPagesPublishTimerRef = useRef(null);
|
|
136
|
+
const writeHtmlPages = useCallback((updater, options = {}) => {
|
|
137
|
+
htmlPagesRef.current = updater(htmlPagesRef.current);
|
|
138
|
+
// Notify entry-path subscribers so the bottom bar's isDirty(htmlDoc) — which folds in sub-page
|
|
139
|
+
// dirtiness — re-evaluates live while editing a navigated sub-page (no tab switch needed).
|
|
140
|
+
for (const entryPath of Object.keys(htmlPagesRef.current))
|
|
141
|
+
notifyEdits(entryPath);
|
|
142
|
+
if (options.immediate === false) {
|
|
143
|
+
if (htmlPagesPublishTimerRef.current != null)
|
|
144
|
+
clearTimeout(htmlPagesPublishTimerRef.current);
|
|
145
|
+
htmlPagesPublishTimerRef.current = setTimeout(() => {
|
|
146
|
+
htmlPagesPublishTimerRef.current = null;
|
|
147
|
+
setHtmlPages(htmlPagesRef.current);
|
|
148
|
+
}, 300);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
if (htmlPagesPublishTimerRef.current != null) {
|
|
152
|
+
clearTimeout(htmlPagesPublishTimerRef.current);
|
|
153
|
+
htmlPagesPublishTimerRef.current = null;
|
|
154
|
+
}
|
|
155
|
+
setHtmlPages(htmlPagesRef.current);
|
|
156
|
+
}, [notifyEdits]);
|
|
157
|
+
useEffect(() => () => {
|
|
158
|
+
if (htmlPagesPublishTimerRef.current != null)
|
|
159
|
+
clearTimeout(htmlPagesPublishTimerRef.current);
|
|
160
|
+
}, []);
|
|
161
|
+
// entryPath → the in-site page currently shown in that HTML tab (roams as the user navigates).
|
|
162
|
+
// App needs it for Ctrl+S / Save targeting and the per-page dirty indicator.
|
|
163
|
+
const [htmlActivePage, setHtmlActivePage] = useState({});
|
|
164
|
+
// MDX component-import grants. Unlike HTML sub-pages, these have no editable buffer — the sandboxed
|
|
165
|
+
// iframe only reads them at compile time — so a plain ref (entryPath -> Set<grantedSubPath>) is
|
|
166
|
+
// enough bookkeeping. Tracked so the owning tab's close can revoke them (mirrors htmlPagesRef's
|
|
167
|
+
// shape one level down: Map<entryPath, Set<subPath>> instead of Map<entryPath, Map<subPath, buffer>>).
|
|
168
|
+
const mdxGrantsRef = useRef({});
|
|
169
|
+
const trackMdxGrant = useCallback((entryPath, grantedPath) => {
|
|
170
|
+
if (grantedPath === entryPath)
|
|
171
|
+
return;
|
|
172
|
+
let set = mdxGrantsRef.current[entryPath];
|
|
173
|
+
if (!set) {
|
|
174
|
+
set = new Set();
|
|
175
|
+
mdxGrantsRef.current[entryPath] = set;
|
|
176
|
+
}
|
|
177
|
+
set.add(grantedPath); // Set semantics: re-resolving the same import on HMR-style re-render is a no-op.
|
|
178
|
+
}, []);
|
|
179
|
+
return {
|
|
180
|
+
openDocs,
|
|
181
|
+
setOpenDocs,
|
|
182
|
+
activePath,
|
|
183
|
+
setActivePath,
|
|
184
|
+
editsRef,
|
|
185
|
+
writeEdits,
|
|
186
|
+
writeEditsLive,
|
|
187
|
+
subscribeEdits,
|
|
188
|
+
getEditSnapshot,
|
|
189
|
+
getExternalSeq,
|
|
190
|
+
docxDirty,
|
|
191
|
+
docxDirtyRef,
|
|
192
|
+
setDocxDirty,
|
|
193
|
+
pdfDirty,
|
|
194
|
+
pdfDirtyRef,
|
|
195
|
+
setPdfDirty,
|
|
196
|
+
imageDirty,
|
|
197
|
+
imageDirtyRef,
|
|
198
|
+
setImageDirty,
|
|
199
|
+
htmlPages,
|
|
200
|
+
htmlPagesRef,
|
|
201
|
+
writeHtmlPages,
|
|
202
|
+
htmlActivePage,
|
|
203
|
+
setHtmlActivePage,
|
|
204
|
+
mdxGrantsRef,
|
|
205
|
+
trackMdxGrant,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { type Dispatch, type MutableRefObject, type SetStateAction } from "react";
|
|
2
|
+
import { type LoadedDocument } from "@rightkit/document-core/documents/types";
|
|
3
|
+
import type { DocumentTabs } from "./useDocumentTabs.js";
|
|
4
|
+
import type { BlockDraftRegistry } from "@rightkit/document-core/app/blockDrafts";
|
|
5
|
+
export declare function pickNextActive(closedPath: string, prevDocs: LoadedDocument[], remaining: LoadedDocument[], activationOrder: string[]): string | null;
|
|
6
|
+
interface DocumentWatcherDeps {
|
|
7
|
+
openDocs: LoadedDocument[];
|
|
8
|
+
openDocsRef: MutableRefObject<LoadedDocument[]>;
|
|
9
|
+
activationOrderRef: MutableRefObject<string[]>;
|
|
10
|
+
setOpenDocs: DocumentTabs["setOpenDocs"];
|
|
11
|
+
setActivePath: DocumentTabs["setActivePath"];
|
|
12
|
+
activePath: string | null;
|
|
13
|
+
writeEdits: DocumentTabs["writeEdits"];
|
|
14
|
+
setDocxDirty: DocumentTabs["setDocxDirty"];
|
|
15
|
+
setPdfDirty: DocumentTabs["setPdfDirty"];
|
|
16
|
+
htmlPagesRef: DocumentTabs["htmlPagesRef"];
|
|
17
|
+
writeHtmlPages: DocumentTabs["writeHtmlPages"];
|
|
18
|
+
setHtmlActivePage: DocumentTabs["setHtmlActivePage"];
|
|
19
|
+
mdxGrantsRef: DocumentTabs["mdxGrantsRef"];
|
|
20
|
+
clearImageDirty: (path: string) => void;
|
|
21
|
+
isDirty: (doc: LoadedDocument) => boolean;
|
|
22
|
+
openLiveUrl: (input: string, opts?: {
|
|
23
|
+
background?: boolean;
|
|
24
|
+
}) => Promise<void>;
|
|
25
|
+
activate: (path: string) => Promise<void>;
|
|
26
|
+
open: (path: string) => Promise<void>;
|
|
27
|
+
fingerprints: MutableRefObject<Map<string, {
|
|
28
|
+
mtimeMs: number;
|
|
29
|
+
size: number;
|
|
30
|
+
}>>;
|
|
31
|
+
selfSaved: MutableRefObject<Set<string>>;
|
|
32
|
+
/** Save paths whose writes are still in flight; watcher events are replayed after completion. */
|
|
33
|
+
pendingSavePaths?: ReadonlySet<string>;
|
|
34
|
+
/** Synchronous saver mirror; covers the gap before pendingSavePaths state renders. */
|
|
35
|
+
synchronousPendingSavePathsRef?: MutableRefObject<ReadonlySet<string>>;
|
|
36
|
+
/** Synchronous mirrors for DOCX/HTML save wrappers. */
|
|
37
|
+
synchronousPendingSavePathsRefs?: ReadonlyArray<MutableRefObject<ReadonlySet<string>>>;
|
|
38
|
+
reloadHtmlSubpage: (path: string) => void;
|
|
39
|
+
setHtmlConfirm: Dispatch<SetStateAction<{
|
|
40
|
+
entryPath: string;
|
|
41
|
+
mode: "save" | "close";
|
|
42
|
+
} | null>>;
|
|
43
|
+
setConfirmClose: Dispatch<SetStateAction<string | null>>;
|
|
44
|
+
confirmClose: string | null;
|
|
45
|
+
setConflict: Dispatch<SetStateAction<string | null>>;
|
|
46
|
+
onSave: (doc: LoadedDocument) => Promise<string | null>;
|
|
47
|
+
onDocxSave: (path: string, buffer: ArrayBuffer) => Promise<void>;
|
|
48
|
+
docxSaver: MutableRefObject<(() => Promise<ArrayBuffer | null>) | null>;
|
|
49
|
+
docxSaversRef?: MutableRefObject<Map<string, () => Promise<ArrayBuffer | null>>>;
|
|
50
|
+
onDocxSaveAs: (doc: LoadedDocument, buffer?: ArrayBuffer) => Promise<string | null>;
|
|
51
|
+
withTabKey: <T extends LoadedDocument>(doc: T, existingKey?: string) => T;
|
|
52
|
+
/** Block-draft registry for draft-aware close/save/reload. Optional so this hook's signature stays
|
|
53
|
+
* backward-compatible with callers that haven't threaded it through yet. */
|
|
54
|
+
blockDrafts?: BlockDraftRegistry;
|
|
55
|
+
/** useViewState's teardown (see useViewState.ts) — deletes a closed path's views/viewsRef/
|
|
56
|
+
* viewMetaRef entries. Optional so this hook's signature stays backward-compatible with callers
|
|
57
|
+
* that haven't threaded it through yet; when absent, view-state pruning on close is simply
|
|
58
|
+
* skipped (same as before this dep existed). */
|
|
59
|
+
dropViewState?: (path: string) => void;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Tab lifecycle (close/teardown/reopen) and external-change detection (fingerprint-checked
|
|
63
|
+
* reload). Extracted verbatim from useDocuments — bodies, fingerprint suppression, and the reopen
|
|
64
|
+
* stack's bounded-eviction grant revocation are unchanged. Owns `closedTabs` (the Ctrl+Shift+T stack);
|
|
65
|
+
* `fingerprints` and `selfSaved` are owned by sibling hooks and threaded in so all three read/write the
|
|
66
|
+
* same single source of truth. `confirmClose`/`conflict` STATE stays with the orchestrator (it's part
|
|
67
|
+
* of the public return shape and `requestClose` needs to set it before this hook can read it back), so
|
|
68
|
+
* this hook only receives the current value + setter.
|
|
69
|
+
*/
|
|
70
|
+
export declare function useDocumentWatcher({ openDocs, openDocsRef, activationOrderRef, setOpenDocs, setActivePath, activePath, writeEdits, setDocxDirty, setPdfDirty, htmlPagesRef, writeHtmlPages, setHtmlActivePage, mdxGrantsRef, clearImageDirty, isDirty, openLiveUrl, activate, open, fingerprints, selfSaved, pendingSavePaths, synchronousPendingSavePathsRef, synchronousPendingSavePathsRefs, reloadHtmlSubpage, setHtmlConfirm, setConfirmClose, confirmClose, setConflict, onSave, onDocxSave, docxSaver, docxSaversRef, onDocxSaveAs, withTabKey, blockDrafts, dropViewState, }: DocumentWatcherDeps): {
|
|
71
|
+
teardownDoc: (path: string, closingDoc: LoadedDocument | undefined) => void;
|
|
72
|
+
doClose: (path: string) => void;
|
|
73
|
+
reopenLastClosed: () => void;
|
|
74
|
+
requestClose: (path: string) => void;
|
|
75
|
+
confirmSave: () => Promise<void>;
|
|
76
|
+
confirmDontSave: () => Promise<void>;
|
|
77
|
+
applyReload: (path: string) => void;
|
|
78
|
+
reload: (path: string, options?: {
|
|
79
|
+
force?: boolean;
|
|
80
|
+
replayDeferred?: boolean;
|
|
81
|
+
}) => void;
|
|
82
|
+
};
|
|
83
|
+
export {};
|