@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.
Files changed (262) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/LICENSE-APACHE +201 -0
  3. package/LICENSE-MIT +21 -0
  4. package/README.md +51 -2
  5. package/dist/app/App.d.ts +1 -0
  6. package/dist/app/App.js +6 -0
  7. package/dist/app/AppDialogs.d.ts +52 -0
  8. package/dist/app/AppDialogs.js +23 -0
  9. package/dist/app/AppDialogsHost.d.ts +31 -0
  10. package/dist/app/AppDialogsHost.js +30 -0
  11. package/dist/app/AppLifecycle.d.ts +19 -0
  12. package/dist/app/AppLifecycle.js +87 -0
  13. package/dist/app/AppProvider.d.ts +317 -0
  14. package/dist/app/AppProvider.js +217 -0
  15. package/dist/app/AppRoot.d.ts +8 -0
  16. package/dist/app/AppRoot.js +159 -0
  17. package/dist/app/AppShell.d.ts +2 -0
  18. package/dist/app/AppShell.js +81 -0
  19. package/dist/app/CommandPalette.d.ts +13 -0
  20. package/dist/app/CommandPalette.js +47 -0
  21. package/dist/app/DocActionBar.d.ts +36 -0
  22. package/dist/app/DocActionBar.js +300 -0
  23. package/dist/app/DocumentContextMenu.d.ts +29 -0
  24. package/dist/app/DocumentContextMenu.js +122 -0
  25. package/dist/app/DocumentPane.d.ts +52 -0
  26. package/dist/app/DocumentPane.js +30 -0
  27. package/dist/app/DocumentRouterTable.d.ts +58 -0
  28. package/dist/app/DocumentRouterTable.js +143 -0
  29. package/dist/app/ErrorBoundary.d.ts +12 -0
  30. package/dist/app/ErrorBoundary.js +16 -0
  31. package/dist/app/FindBar.d.ts +12 -0
  32. package/dist/app/FindBar.js +199 -0
  33. package/dist/app/LiveTitle.d.ts +14 -0
  34. package/dist/app/LiveTitle.js +14 -0
  35. package/dist/app/OpeningProgress.d.ts +13 -0
  36. package/dist/app/OpeningProgress.js +21 -0
  37. package/dist/app/PrintPreviewWindow.d.ts +2 -0
  38. package/dist/app/PrintPreviewWindow.js +57 -0
  39. package/dist/app/ShortcutHelp.d.ts +3 -0
  40. package/dist/app/ShortcutHelp.js +99 -0
  41. package/dist/app/Sidebar.d.ts +36 -0
  42. package/dist/app/Sidebar.js +121 -0
  43. package/dist/app/ToastViewport.d.ts +1 -0
  44. package/dist/app/ToastViewport.js +31 -0
  45. package/dist/app/Toolbar.d.ts +26 -0
  46. package/dist/app/Toolbar.js +63 -0
  47. package/dist/app/activeFormatSavers.d.ts +7 -0
  48. package/dist/app/activeFormatSavers.js +4 -0
  49. package/dist/app/clipboardPaths.d.ts +1 -0
  50. package/dist/app/clipboardPaths.js +31 -0
  51. package/dist/app/documentDrafts.d.ts +17 -0
  52. package/dist/app/documentDrafts.js +104 -0
  53. package/dist/app/draftResolution.d.ts +5 -0
  54. package/dist/app/draftResolution.js +25 -0
  55. package/dist/app/markdownPaletteActions.d.ts +8 -0
  56. package/dist/app/markdownPaletteActions.js +107 -0
  57. package/dist/app/resourceCensusDev.d.ts +41 -0
  58. package/dist/app/resourceCensusDev.js +518 -0
  59. package/dist/app/setupRecovery.d.ts +34 -0
  60. package/dist/app/setupRecovery.js +70 -0
  61. package/dist/app/shell/DocStatsPill.d.ts +18 -0
  62. package/dist/app/shell/DocStatsPill.js +32 -0
  63. package/dist/app/shell/DocumentTools.d.ts +9 -0
  64. package/dist/app/shell/DocumentTools.js +73 -0
  65. package/dist/app/shell/InfoInspector.d.ts +9 -0
  66. package/dist/app/shell/InfoInspector.js +67 -0
  67. package/dist/app/shell/StartPage.d.ts +9 -0
  68. package/dist/app/shell/StartPage.js +49 -0
  69. package/dist/app/shell/readEdit.d.ts +6 -0
  70. package/dist/app/shell/readEdit.js +6 -0
  71. package/dist/app/sidebar/SidebarDocumentMenu.d.ts +20 -0
  72. package/dist/app/sidebar/SidebarDocumentMenu.js +56 -0
  73. package/dist/app/sidebar/SidebarExpandedPanel.d.ts +9 -0
  74. package/dist/app/sidebar/SidebarExpandedPanel.js +78 -0
  75. package/dist/app/sidebar/SidebarIcons.d.ts +3 -0
  76. package/dist/app/sidebar/SidebarIcons.js +18 -0
  77. package/dist/app/sidebar/SidebarMenus.d.ts +19 -0
  78. package/dist/app/sidebar/SidebarMenus.js +64 -0
  79. package/dist/app/sidebar/SidebarPathUtils.d.ts +10 -0
  80. package/dist/app/sidebar/SidebarPathUtils.js +55 -0
  81. package/dist/app/sidebar/SidebarPrimitives.d.ts +12 -0
  82. package/dist/app/sidebar/SidebarPrimitives.js +45 -0
  83. package/dist/app/sidebar/SidebarRail.d.ts +5 -0
  84. package/dist/app/sidebar/SidebarRail.js +37 -0
  85. package/dist/app/sidebar/explorer/ExplorerRowMenu.d.ts +21 -0
  86. package/dist/app/sidebar/explorer/ExplorerRowMenu.js +37 -0
  87. package/dist/app/sidebar/explorer/ExplorerSections.d.ts +16 -0
  88. package/dist/app/sidebar/explorer/ExplorerSections.js +259 -0
  89. package/dist/app/sidebar/explorer/ExplorerTree.d.ts +46 -0
  90. package/dist/app/sidebar/explorer/ExplorerTree.js +387 -0
  91. package/dist/app/sidebar/explorer/explorerState.d.ts +37 -0
  92. package/dist/app/sidebar/explorer/explorerState.js +135 -0
  93. package/dist/app/tabMountPolicy.d.ts +45 -0
  94. package/dist/app/tabMountPolicy.js +104 -0
  95. package/dist/app/tabWarmOrder.d.ts +7 -0
  96. package/dist/app/tabWarmOrder.js +48 -0
  97. package/dist/app/tabs/TabFuseCurves.d.ts +3 -0
  98. package/dist/app/tabs/TabFuseCurves.js +15 -0
  99. package/dist/app/tabs/TabOverview.d.ts +24 -0
  100. package/dist/app/tabs/TabOverview.js +121 -0
  101. package/dist/app/tabs/TabStrip.d.ts +42 -0
  102. package/dist/app/tabs/TabStrip.js +380 -0
  103. package/dist/app/tabs/tabIds.d.ts +5 -0
  104. package/dist/app/tabs/tabIds.js +7 -0
  105. package/dist/app/tabs/useTabDirty.d.ts +3 -0
  106. package/dist/app/tabs/useTabDirty.js +11 -0
  107. package/dist/app/useAppChromeState.d.ts +21 -0
  108. package/dist/app/useAppChromeState.js +48 -0
  109. package/dist/app/useAppCommands.d.ts +66 -0
  110. package/dist/app/useAppCommands.js +71 -0
  111. package/dist/app/useAppDocumentCore.d.ts +210 -0
  112. package/dist/app/useAppDocumentCore.js +40 -0
  113. package/dist/app/useAppEffects.d.ts +18 -0
  114. package/dist/app/useAppEffects.js +52 -0
  115. package/dist/app/useAppPaneSearch.d.ts +37 -0
  116. package/dist/app/useAppPaneSearch.js +74 -0
  117. package/dist/app/useAppShellSetup.d.ts +40 -0
  118. package/dist/app/useAppShellSetup.js +52 -0
  119. package/dist/app/useAppSlots.d.ts +28 -0
  120. package/dist/app/useAppSlots.js +40 -0
  121. package/dist/app/useAppTabChrome.d.ts +51 -0
  122. package/dist/app/useAppTabChrome.js +66 -0
  123. package/dist/app/useAutoSave.d.ts +34 -0
  124. package/dist/app/useAutoSave.js +116 -0
  125. package/dist/app/useCloseAll.d.ts +52 -0
  126. package/dist/app/useCloseAll.js +295 -0
  127. package/dist/app/useClosedTabEviction.d.ts +10 -0
  128. package/dist/app/useClosedTabEviction.js +38 -0
  129. package/dist/app/useCommandPalette.d.ts +44 -0
  130. package/dist/app/useCommandPalette.js +254 -0
  131. package/dist/app/useCrossDocSearch.d.ts +7 -0
  132. package/dist/app/useCrossDocSearch.js +94 -0
  133. package/dist/app/useDistractionFree.d.ts +21 -0
  134. package/dist/app/useDistractionFree.js +73 -0
  135. package/dist/app/useDocumentCreation.d.ts +18 -0
  136. package/dist/app/useDocumentCreation.js +63 -0
  137. package/dist/app/useDocumentDirty.d.ts +31 -0
  138. package/dist/app/useDocumentDirty.js +46 -0
  139. package/dist/app/useDocumentLoader.d.ts +55 -0
  140. package/dist/app/useDocumentLoader.js +267 -0
  141. package/dist/app/useDocumentOutputActions.d.ts +6 -0
  142. package/dist/app/useDocumentOutputActions.js +45 -0
  143. package/dist/app/useDocumentRuntimeEffects.d.ts +36 -0
  144. package/dist/app/useDocumentRuntimeEffects.js +458 -0
  145. package/dist/app/useDocumentSaver.d.ts +58 -0
  146. package/dist/app/useDocumentSaver.js +511 -0
  147. package/dist/app/useDocumentTabs.d.ts +41 -0
  148. package/dist/app/useDocumentTabs.js +207 -0
  149. package/dist/app/useDocumentWatcher.d.ts +83 -0
  150. package/dist/app/useDocumentWatcher.js +393 -0
  151. package/dist/app/useDocumentZoom.d.ts +24 -0
  152. package/dist/app/useDocumentZoom.js +104 -0
  153. package/dist/app/useDocuments.d.ts +169 -0
  154. package/dist/app/useDocuments.js +455 -0
  155. package/dist/app/useDraftConfirmGate.d.ts +27 -0
  156. package/dist/app/useDraftConfirmGate.js +47 -0
  157. package/dist/app/useFileDragOverlay.d.ts +12 -0
  158. package/dist/app/useFileDragOverlay.js +49 -0
  159. package/dist/app/useFormatApis.d.ts +13 -0
  160. package/dist/app/useFormatApis.js +62 -0
  161. package/dist/app/useFormatDirtyState.d.ts +29 -0
  162. package/dist/app/useFormatDirtyState.js +81 -0
  163. package/dist/app/useGlobalShortcuts.d.ts +47 -0
  164. package/dist/app/useGlobalShortcuts.js +359 -0
  165. package/dist/app/useHtmlSession.d.ts +68 -0
  166. package/dist/app/useHtmlSession.js +463 -0
  167. package/dist/app/useHtmlSessionProps.d.ts +33 -0
  168. package/dist/app/useHtmlSessionProps.js +57 -0
  169. package/dist/app/useLibrary.d.ts +21 -0
  170. package/dist/app/useLibrary.js +81 -0
  171. package/dist/app/useOpenFromClipboard.d.ts +7 -0
  172. package/dist/app/useOpenFromClipboard.js +44 -0
  173. package/dist/app/usePaneLiveness.d.ts +29 -0
  174. package/dist/app/usePaneLiveness.js +199 -0
  175. package/dist/app/usePendingSavePaths.d.ts +19 -0
  176. package/dist/app/usePendingSavePaths.js +67 -0
  177. package/dist/app/useReadyShow.d.ts +12 -0
  178. package/dist/app/useReadyShow.js +142 -0
  179. package/dist/app/useSearchController.d.ts +22 -0
  180. package/dist/app/useSearchController.js +114 -0
  181. package/dist/app/useSetupRecovery.d.ts +41 -0
  182. package/dist/app/useSetupRecovery.js +153 -0
  183. package/dist/app/useTabChrome.d.ts +40 -0
  184. package/dist/app/useTabChrome.js +115 -0
  185. package/dist/app/useViewState.d.ts +27 -0
  186. package/dist/app/useViewState.js +160 -0
  187. package/dist/appName.d.ts +2 -0
  188. package/dist/appName.js +8 -0
  189. package/dist/documents/DocumentRouter.d.ts +102 -0
  190. package/dist/documents/DocumentRouter.js +153 -0
  191. package/dist/documents/FileTypeIcon.d.ts +6 -0
  192. package/dist/documents/FileTypeIcon.js +93 -0
  193. package/dist/documents/ImageDocument.d.ts +14 -0
  194. package/dist/documents/ImageDocument.js +61 -0
  195. package/dist/documents/capabilities.d.ts +17 -0
  196. package/dist/documents/capabilities.js +18 -0
  197. package/dist/documents/html/HtmlDocument.d.ts +15 -0
  198. package/dist/documents/html/HtmlDocument.js +584 -0
  199. package/dist/documents/html/HtmlDocumentLayout.d.ts +87 -0
  200. package/dist/documents/html/HtmlDocumentLayout.js +25 -0
  201. package/dist/documents/html/HtmlDocumentProps.d.ts +60 -0
  202. package/dist/documents/html/HtmlDocumentProps.js +1 -0
  203. package/dist/documents/html/HtmlDocumentSource.d.ts +34 -0
  204. package/dist/documents/html/HtmlDocumentSource.js +28 -0
  205. package/dist/documents/markdown/MarkdownSplitPane.d.ts +115 -0
  206. package/dist/documents/markdown/MarkdownSplitPane.js +352 -0
  207. package/dist/documents/mdx/MdxSplitPane.d.ts +32 -0
  208. package/dist/documents/mdx/MdxSplitPane.js +60 -0
  209. package/dist/documents/txt/LinkDialog.d.ts +8 -0
  210. package/dist/documents/txt/LinkDialog.js +34 -0
  211. package/dist/documents/txt/MarkdownSourceToolbar.d.ts +8 -0
  212. package/dist/documents/txt/MarkdownSourceToolbar.js +25 -0
  213. package/dist/documents/txt/SourceEditorToolbar.d.ts +12 -0
  214. package/dist/documents/txt/SourceEditorToolbar.js +4 -0
  215. package/dist/documents/txt/TxtEditor.d.ts +39 -0
  216. package/dist/documents/txt/TxtEditor.js +186 -0
  217. package/dist/documents/txt/sourceEditorTypes.d.ts +49 -0
  218. package/dist/documents/txt/sourceEditorTypes.js +1 -0
  219. package/dist/documents/txt/sourceExtensions.d.ts +38 -0
  220. package/dist/documents/txt/sourceExtensions.js +444 -0
  221. package/dist/documents/txt/sourceFormatting.d.ts +18 -0
  222. package/dist/documents/txt/sourceFormatting.js +183 -0
  223. package/dist/documents/txt/sourceTheme.d.ts +5 -0
  224. package/dist/documents/txt/sourceTheme.js +220 -0
  225. package/dist/documents/txt/useCodeMirrorSourceEditor.d.ts +69 -0
  226. package/dist/documents/txt/useCodeMirrorSourceEditor.js +546 -0
  227. package/dist/documents/txt/useSourceSearch.d.ts +39 -0
  228. package/dist/documents/txt/useSourceSearch.js +122 -0
  229. package/dist/documents/txt/useTextEditorCommands.d.ts +26 -0
  230. package/dist/documents/txt/useTextEditorCommands.js +151 -0
  231. package/dist/documents/web/LivePreviewPane.d.ts +11 -0
  232. package/dist/documents/web/LivePreviewPane.js +58 -0
  233. package/dist/env.d.ts +66 -0
  234. package/dist/index.d.ts +121 -0
  235. package/dist/index.js +123 -0
  236. package/dist/settings/AppearanceMenu.d.ts +9 -0
  237. package/dist/settings/AppearanceMenu.js +54 -0
  238. package/dist/settings/SettingsPanel.d.ts +7 -0
  239. package/dist/settings/SettingsPanel.js +104 -0
  240. package/dist/settings/publishedSections.d.ts +22 -0
  241. package/dist/settings/publishedSections.js +79 -0
  242. package/dist/settings/sections/AboutSection.d.ts +1 -0
  243. package/dist/settings/sections/AboutSection.js +13 -0
  244. package/dist/settings/sections/DiagnosticsSection.d.ts +1 -0
  245. package/dist/settings/sections/DiagnosticsSection.js +42 -0
  246. package/dist/settings/sections/PrivacySection.d.ts +3 -0
  247. package/dist/settings/sections/PrivacySection.js +51 -0
  248. package/dist/settings/sections/UpdateLicenseBlock.d.ts +10 -0
  249. package/dist/settings/sections/UpdateLicenseBlock.js +158 -0
  250. package/dist/state/explorer.d.ts +32 -0
  251. package/dist/state/explorer.js +111 -0
  252. package/dist/state/legal.d.ts +51 -0
  253. package/dist/state/legal.js +130 -0
  254. package/dist/state/library.d.ts +33 -0
  255. package/dist/state/library.js +110 -0
  256. package/dist/state/updateLicense.d.ts +31 -0
  257. package/dist/state/updateLicense.js +340 -0
  258. package/dist/styles/explorer.css +219 -0
  259. package/dist/styles/index.css +1477 -0
  260. package/dist/styles/tabs.css +467 -0
  261. package/dist/styles.css +6 -0
  262. package/package.json +88 -3
@@ -0,0 +1,41 @@
1
+ import { type ResourceCensus } from "@rightkit/document-core/app/resourceCensus";
2
+ type ResourceMatrixFormat = "shell" | "txt" | "md" | "html" | "docx" | "image" | "pdf";
3
+ type ResourceMatrixPlanDocument = {
4
+ readonly format: Exclude<ResourceMatrixFormat, "shell">;
5
+ readonly path: string;
6
+ readonly fixture: {
7
+ readonly source_filename: string;
8
+ readonly bytes: number;
9
+ readonly sha256: string;
10
+ };
11
+ };
12
+ type ResourceMatrixPlan = {
13
+ readonly scenario: "C";
14
+ readonly a: ResourceMatrixPlanDocument;
15
+ readonly b: ResourceMatrixPlanDocument;
16
+ } | {
17
+ readonly scenario: "D";
18
+ readonly tab_count: 10 | 50;
19
+ readonly dirty_docx_path: string;
20
+ readonly fixtures: readonly ResourceMatrixPlanDocument[];
21
+ };
22
+ declare global {
23
+ interface Window {
24
+ /**
25
+ * Development/QA-only read surface for the cross-format resource matrix. It deliberately
26
+ * exposes a snapshot function, not mutable registry state or app controls.
27
+ */
28
+ __viewrightResourceCensus?: () => ResourceCensus;
29
+ /** Development-only one-shot matrix probe status, for native QA inspection. */
30
+ __viewrightResourceMatrixStatus?: () => {
31
+ readonly state: string;
32
+ readonly error: string | null;
33
+ };
34
+ }
35
+ }
36
+ /** Install only in dev or an explicitly-built browser-QA artifact; production has no global. */
37
+ export declare function installResourceCensusDevHook(): void;
38
+ /** Parse only an explicit native QA plan; malformed input leaves production/dev startup untouched. */
39
+ export declare function parseResourceMatrixPlan(raw: string | undefined): ResourceMatrixPlan | null;
40
+ export declare function resourceMatrixReady(format: ResourceMatrixFormat, census: ResourceCensus): boolean;
41
+ export {};
@@ -0,0 +1,518 @@
1
+ import { isWindows } from "@rightkit/document-core/platform";
2
+ import { getResourceCensus } from "@rightkit/document-core/app/resourceCensus";
3
+ const RESOURCE_MATRIX_FORMATS = new Set([
4
+ "shell",
5
+ "txt",
6
+ "md",
7
+ "html",
8
+ "docx",
9
+ "image",
10
+ "pdf",
11
+ ]);
12
+ const MATRIX_POLL_MS = 100;
13
+ const MATRIX_QUIET_MS = 1_000;
14
+ const MATRIX_TIMEOUT_MS = 45_000;
15
+ const HEAVY_RESOURCE_FORMATS = new Set(["html", "docx", "image", "pdf"]);
16
+ function matrixTimeoutMs() {
17
+ const requested = Number(import.meta.env?.VITE_RESOURCE_MATRIX_TIMEOUT_MS);
18
+ return Number.isSafeInteger(requested) && requested >= 5_000 && requested <= 180_000
19
+ ? requested
20
+ : MATRIX_TIMEOUT_MS;
21
+ }
22
+ /** Install only in dev or an explicitly-built browser-QA artifact; production has no global. */
23
+ export function installResourceCensusDevHook() {
24
+ if (typeof window === "undefined")
25
+ return;
26
+ if (!import.meta.env?.DEV && import.meta.env?.VITE_RIGHTKIT_BROWSER_QA !== "1")
27
+ return;
28
+ window.__viewrightResourceCensus = getResourceCensus;
29
+ installResourceMatrixAutopilot();
30
+ }
31
+ function resourceMatrixFormat() {
32
+ const raw = import.meta.env?.VITE_RESOURCE_MATRIX_FORMAT;
33
+ return RESOURCE_MATRIX_FORMATS.has(raw)
34
+ ? raw
35
+ : null;
36
+ }
37
+ function isPlanDocument(value) {
38
+ if (!value || typeof value !== "object")
39
+ return false;
40
+ const candidate = value;
41
+ const format = value.format;
42
+ return (typeof candidate.path === "string" &&
43
+ format !== "shell" &&
44
+ RESOURCE_MATRIX_FORMATS.has(format) &&
45
+ !!candidate.fixture &&
46
+ typeof candidate.fixture.source_filename === "string" &&
47
+ Number.isSafeInteger(candidate.fixture.bytes) &&
48
+ candidate.fixture.bytes > 0 &&
49
+ typeof candidate.fixture.sha256 === "string" &&
50
+ /^[0-9a-f]{64}$/i.test(candidate.fixture.sha256));
51
+ }
52
+ /** Parse only an explicit native QA plan; malformed input leaves production/dev startup untouched. */
53
+ export function parseResourceMatrixPlan(raw) {
54
+ if (!raw)
55
+ return null;
56
+ try {
57
+ const value = JSON.parse(raw);
58
+ if (value.scenario === "C" && isPlanDocument(value.a) && isPlanDocument(value.b)) {
59
+ if (value.a.path !== value.b.path &&
60
+ value.a.format !== value.b.format &&
61
+ HEAVY_RESOURCE_FORMATS.has(value.a.format) &&
62
+ HEAVY_RESOURCE_FORMATS.has(value.b.format))
63
+ return { scenario: "C", a: value.a, b: value.b };
64
+ }
65
+ if (value.scenario === "D" &&
66
+ (value.tab_count === 10 || value.tab_count === 50) &&
67
+ typeof value.dirty_docx_path === "string" &&
68
+ Array.isArray(value.fixtures) &&
69
+ value.fixtures.length === value.tab_count &&
70
+ value.fixtures.every(isPlanDocument) &&
71
+ value.fixtures.some((fixture) => fixture.path === value.dirty_docx_path && fixture.format === "docx"))
72
+ return {
73
+ scenario: "D",
74
+ tab_count: value.tab_count,
75
+ dirty_docx_path: value.dirty_docx_path,
76
+ fixtures: value.fixtures,
77
+ };
78
+ }
79
+ catch {
80
+ // QA configuration is external input. A malformed plan must never change normal app startup.
81
+ }
82
+ return null;
83
+ }
84
+ function resourceMatrixScenario(format) {
85
+ if (format === "shell")
86
+ return "A";
87
+ const raw = import.meta.env?.VITE_RESOURCE_MATRIX_SCENARIO;
88
+ return raw === "B" || raw === "E" ? raw : null;
89
+ }
90
+ function resourceMatrixRuntime() {
91
+ const memory = performance.memory;
92
+ return {
93
+ js_heap_used_bytes: memory?.usedJSHeapSize ?? null,
94
+ js_heap_total_bytes: memory?.totalJSHeapSize ?? null,
95
+ js_heap_limit_bytes: memory?.jsHeapSizeLimit ?? null,
96
+ };
97
+ }
98
+ function resourceMatrixTransfer() {
99
+ const entries = performance.getEntriesByType("resource");
100
+ return {
101
+ requests: entries.length,
102
+ transfer_bytes: entries.reduce((total, entry) => total + (entry.transferSize || 0), 0),
103
+ decoded_body_bytes: entries.reduce((total, entry) => total + (entry.decodedBodySize || 0), 0),
104
+ };
105
+ }
106
+ export function resourceMatrixReady(format, census) {
107
+ if (format === "shell")
108
+ return census.heavy === 0;
109
+ if (format === "pdf") {
110
+ return ((census.byKind["pdf-page-canvas"] ?? 0) > 0 || (census.byKind["pdf-native-reader"] ?? 0) > 0);
111
+ }
112
+ return (census.byFormat[format]?.live ?? 0) > 0;
113
+ }
114
+ async function resourceMatrixProcessMemory() {
115
+ if (!("__TAURI_INTERNALS__" in window))
116
+ return null;
117
+ try {
118
+ const { invoke } = await import("@tauri-apps/api/core");
119
+ return await invoke("pdf_bench_process_memory");
120
+ }
121
+ catch {
122
+ return null;
123
+ }
124
+ }
125
+ async function writeResourceMatrixResult(runId, result) {
126
+ if (!("__TAURI_INTERNALS__" in window))
127
+ return;
128
+ const { invoke } = await import("@tauri-apps/api/core");
129
+ await invoke("pdf_bench_write_result", { runId, result });
130
+ }
131
+ function platform() {
132
+ return isWindows() ? "windows" : "macos";
133
+ }
134
+ function paneStates(paths) {
135
+ const panes = [...document.querySelectorAll("[data-vr-document-path]")];
136
+ return Object.fromEntries(paths.map((path) => {
137
+ const pane = panes.find((candidate) => candidate.dataset.vrDocumentPath === path);
138
+ return [
139
+ path,
140
+ pane
141
+ ? {
142
+ pane_state: pane.dataset.vrPaneState ?? null,
143
+ router_state: pane.dataset.vrRouterState ?? null,
144
+ }
145
+ : null,
146
+ ];
147
+ }));
148
+ }
149
+ function clickTab(path) {
150
+ const tab = [...document.querySelectorAll("[data-vr-tab-path]")].find((candidate) => candidate.dataset.vrTabPath === path);
151
+ if (!tab)
152
+ return false;
153
+ // Expanded rows own activation on their container but expose a real title button; collapsed
154
+ // rows put the data attribute on that button. Clicking actual control matches user path on both.
155
+ const control = tab.matches("button") ? tab : tab.querySelector("button");
156
+ (control ?? tab).click();
157
+ return true;
158
+ }
159
+ async function waitForTab(path, deadline) {
160
+ while (performance.now() < deadline) {
161
+ if ([...document.querySelectorAll("[data-vr-tab-path]")].some((candidate) => candidate.dataset.vrTabPath === path))
162
+ return;
163
+ await new Promise((resolveWait) => window.setTimeout(resolveWait, MATRIX_POLL_MS));
164
+ }
165
+ throw new Error(`timed out waiting for startup tab: ${path}`);
166
+ }
167
+ function activePath() {
168
+ return ([...document.querySelectorAll("[data-vr-document-path]")].find((candidate) => candidate.dataset.vrPaneState === "active")?.dataset.vrDocumentPath ?? null);
169
+ }
170
+ async function waitForStablePath(format, path, deadline) {
171
+ let quietSince = null;
172
+ let lastSignature = "";
173
+ let lastCensus = getResourceCensus();
174
+ let lastActivationAttempt = 0;
175
+ while (performance.now() < deadline) {
176
+ if (activePath() !== path && performance.now() - lastActivationAttempt >= 500) {
177
+ clickTab(path);
178
+ lastActivationAttempt = performance.now();
179
+ }
180
+ const current = getResourceCensus();
181
+ lastCensus = current;
182
+ if (activePath() === path && resourceMatrixReady(format, current)) {
183
+ // Readiness belongs to active target, not unrelated background work. A paint-warm PDF may
184
+ // finish another canvas while DOCX activates; that is useful Scenario C evidence, not reason
185
+ // to prevent DOCX from ever reaching useful-ready.
186
+ const signature = JSON.stringify({
187
+ target: current.byFormat[format] ?? null,
188
+ pane: paneStates([path])[path],
189
+ });
190
+ if (signature !== lastSignature) {
191
+ lastSignature = signature;
192
+ quietSince = performance.now();
193
+ }
194
+ if (quietSince !== null && performance.now() - quietSince >= MATRIX_QUIET_MS)
195
+ return { census: current, readyAt: performance.now() };
196
+ }
197
+ else {
198
+ quietSince = null;
199
+ lastSignature = "";
200
+ }
201
+ await new Promise((resolveWait) => window.setTimeout(resolveWait, MATRIX_POLL_MS));
202
+ }
203
+ throw new Error(`timed out waiting for ${format} at ${path}; active=${activePath() ?? "none"}; target=${JSON.stringify(lastCensus.byFormat[format] ?? null)}; pane=${JSON.stringify(paneStates([path])[path])}`);
204
+ }
205
+ function dirtyDocxViaEditor() {
206
+ const editor = document.querySelector(".paged-editor__hidden-pm .ProseMirror[contenteditable='true']");
207
+ if (!editor || typeof document.execCommand !== "function")
208
+ return false;
209
+ editor.focus();
210
+ // This goes through EigenPal's real ProseMirror input pipeline. Do not set textContent or mutate
211
+ // census state: Scenario D is valid only when DocxPane's actual onChange marks it dirty.
212
+ return document.execCommand("insertText", false, "•");
213
+ }
214
+ function closeShortcut() {
215
+ const target = document.activeElement instanceof HTMLElement ? document.activeElement : document.body;
216
+ target.dispatchEvent(new KeyboardEvent("keydown", {
217
+ key: "w",
218
+ code: "KeyW",
219
+ ctrlKey: isWindows(),
220
+ metaKey: !isWindows(),
221
+ bubbles: true,
222
+ cancelable: true,
223
+ }));
224
+ }
225
+ function dismissDontSave() {
226
+ const button = [...document.querySelectorAll("button")].find((candidate) => candidate.textContent?.trim().toLowerCase() === "don't save");
227
+ if (!button)
228
+ return false;
229
+ button.click();
230
+ return true;
231
+ }
232
+ async function closeAllTabs(deadline) {
233
+ const before = getResourceCensus();
234
+ let priorCount = document.querySelectorAll("[data-vr-tab-path]").length;
235
+ while (priorCount > 0 && performance.now() < deadline) {
236
+ const countBeforeClose = priorCount;
237
+ closeShortcut();
238
+ const perCloseDeadline = Math.min(deadline, performance.now() + 4_000);
239
+ while (performance.now() < perCloseDeadline) {
240
+ dismissDontSave();
241
+ const currentCount = document.querySelectorAll("[data-vr-tab-path]").length;
242
+ if (currentCount < countBeforeClose) {
243
+ priorCount = currentCount;
244
+ break;
245
+ }
246
+ await new Promise((resolveWait) => window.setTimeout(resolveWait, MATRIX_POLL_MS));
247
+ }
248
+ if (priorCount >= countBeforeClose)
249
+ break;
250
+ }
251
+ if (priorCount !== 0)
252
+ throw new Error("timed out closing stress tabs");
253
+ const quietUntil = performance.now() + MATRIX_QUIET_MS;
254
+ while (performance.now() < quietUntil)
255
+ await new Promise((resolveWait) => window.setTimeout(resolveWait, MATRIX_POLL_MS));
256
+ const afterClose = getResourceCensus();
257
+ return {
258
+ afterClose,
259
+ released: afterClose.totals.released > before.totals.released,
260
+ };
261
+ }
262
+ async function runResourceMatrixPlan(plan, runId, baseline, startedAt) {
263
+ const deadline = startedAt + matrixTimeoutMs();
264
+ if (plan.scenario === "C") {
265
+ // Cold-open routing activates each startup argument in order. Do not begin C until both tabs
266
+ // exist, otherwise startup's final activation can overwrite our first A click with B.
267
+ await Promise.all([waitForTab(plan.a.path, deadline), waitForTab(plan.b.path, deadline)]);
268
+ const visits = [];
269
+ for (const [phase, item] of [
270
+ ["a-first", plan.a],
271
+ ["b", plan.b],
272
+ ["a-return", plan.a],
273
+ ]) {
274
+ if (!clickTab(item.path))
275
+ throw new Error(`tab not found for ${phase}: ${item.path}`);
276
+ const ready = await waitForStablePath(item.format, item.path, deadline);
277
+ visits.push({
278
+ phase,
279
+ census: ready.census,
280
+ panes: paneStates([plan.a.path, plan.b.path]),
281
+ });
282
+ }
283
+ const final = visits[visits.length - 1];
284
+ if (!final)
285
+ throw new Error("switch plan did not collect final A visit");
286
+ const finalPanes = final.panes;
287
+ const bIsOnlyWarm = finalPanes[plan.b.path]?.pane_state === "warm" &&
288
+ Object.values(finalPanes).filter((pane) => pane?.pane_state === "warm").length === 1;
289
+ const perFormatMax = Object.fromEntries([plan.a.format, plan.b.format].map((format) => [
290
+ format,
291
+ Math.max(...visits.map((visit) => visit.census.byFormat[format]?.live ?? 0)),
292
+ ]));
293
+ return {
294
+ schema_version: 1,
295
+ scenario: "C",
296
+ platform: platform(),
297
+ format: plan.a.format,
298
+ sample_id: runId,
299
+ method: "native-resource-matrix-autopilot",
300
+ fixture: plan.a.fixture,
301
+ useful_paint_ms: Math.max(0, performance.now() - startedAt),
302
+ peak_observed_ms: Math.max(0, performance.now() - startedAt),
303
+ resources: { before: baseline, steady: final.census, after_close: null },
304
+ runtime: resourceMatrixRuntime(),
305
+ process_memory: await resourceMatrixProcessMemory(),
306
+ transfer: resourceMatrixTransfer(),
307
+ close: { attempted: false },
308
+ tab_count: null,
309
+ dirty_formats: [],
310
+ switch: {
311
+ a: plan.a,
312
+ b: plan.b,
313
+ visits,
314
+ assertions: {
315
+ previous_pane_only_paint_warm: bIsOnlyWarm,
316
+ max_live_resources_per_format: perFormatMax,
317
+ },
318
+ },
319
+ error: bIsOnlyWarm ? null : "A→B→A did not leave only B paint-warm on A return",
320
+ };
321
+ }
322
+ while (performance.now() < deadline) {
323
+ if (document.querySelectorAll("[data-vr-tab-path]").length >= plan.tab_count)
324
+ break;
325
+ await new Promise((resolveWait) => window.setTimeout(resolveWait, MATRIX_POLL_MS));
326
+ }
327
+ if (document.querySelectorAll("[data-vr-tab-path]").length < plan.tab_count)
328
+ throw new Error(`timed out opening ${plan.tab_count} stress tabs`);
329
+ if (!clickTab(plan.dirty_docx_path))
330
+ throw new Error("dirty DOCX tab was not found");
331
+ const ready = await waitForStablePath("docx", plan.dirty_docx_path, deadline);
332
+ if (!dirtyDocxViaEditor())
333
+ throw new Error("could not enter text through the real DOCX editor");
334
+ let dirtyCensus = ready.census;
335
+ while (performance.now() < deadline) {
336
+ dirtyCensus = getResourceCensus();
337
+ if ((dirtyCensus.byFormat.docx?.pinnedDirty ?? 0) > 0)
338
+ break;
339
+ await new Promise((resolveWait) => window.setTimeout(resolveWait, MATRIX_POLL_MS));
340
+ }
341
+ if ((dirtyCensus.byFormat.docx?.pinnedDirty ?? 0) === 0)
342
+ throw new Error("DOCX input did not produce a pinned dirty resource");
343
+ const closed = await closeAllTabs(deadline);
344
+ return {
345
+ schema_version: 1,
346
+ scenario: "D",
347
+ platform: platform(),
348
+ format: null,
349
+ sample_id: runId,
350
+ method: "native-resource-matrix-autopilot",
351
+ fixture: null,
352
+ useful_paint_ms: Math.max(0, ready.readyAt - startedAt),
353
+ peak_observed_ms: Math.max(0, performance.now() - startedAt),
354
+ resources: { before: baseline, steady: dirtyCensus, after_close: closed.afterClose },
355
+ runtime: resourceMatrixRuntime(),
356
+ process_memory: await resourceMatrixProcessMemory(),
357
+ transfer: resourceMatrixTransfer(),
358
+ close: { attempted: true },
359
+ tab_count: plan.tab_count,
360
+ dirty_formats: ["docx"],
361
+ not_applicable: { pdf: "read-only" },
362
+ stress: {
363
+ fixtures: plan.fixtures,
364
+ dirty_docx_path: plan.dirty_docx_path,
365
+ close_released_resources: closed.released,
366
+ },
367
+ error: closed.released ? null : "closing stress tabs did not release a counted resource",
368
+ };
369
+ }
370
+ /**
371
+ * Native first-open collector. It is explicitly opt-in, uses existing debug-only PDF benchmark
372
+ * commands solely as an append-only local artifact sink, & leaves all production behavior alone.
373
+ */
374
+ function installResourceMatrixAutopilot() {
375
+ if (import.meta.env?.VITE_RESOURCE_MATRIX_AUTOPILOT !== "1")
376
+ return;
377
+ const plan = parseResourceMatrixPlan(import.meta.env?.VITE_RESOURCE_MATRIX_PLAN);
378
+ const planRunId = import.meta.env?.VITE_RESOURCE_MATRIX_RUN_ID;
379
+ if (plan && planRunId && "__TAURI_INTERNALS__" in window) {
380
+ const startedAt = performance.now();
381
+ const baseline = getResourceCensus();
382
+ const state = { value: "running", error: null };
383
+ window.__viewrightResourceMatrixStatus = () => ({ state: state.value, error: state.error });
384
+ void runResourceMatrixPlan(plan, planRunId, baseline, startedAt)
385
+ .then(async (result) => {
386
+ state.value = "writing";
387
+ await writeResourceMatrixResult(planRunId, result);
388
+ state.value = "written";
389
+ })
390
+ .catch(async (error) => {
391
+ state.value = "failed";
392
+ state.error = String(error);
393
+ void writeResourceMatrixResult(planRunId, {
394
+ schema_version: 1,
395
+ scenario: plan.scenario,
396
+ platform: platform(),
397
+ format: plan.scenario === "C" ? plan.a.format : null,
398
+ sample_id: planRunId,
399
+ method: "native-resource-matrix-autopilot",
400
+ fixture: plan.scenario === "C" ? plan.a.fixture : null,
401
+ useful_paint_ms: null,
402
+ peak_observed_ms: Math.max(0, performance.now() - startedAt),
403
+ resources: { before: baseline, steady: getResourceCensus(), after_close: null },
404
+ runtime: resourceMatrixRuntime(),
405
+ process_memory: await resourceMatrixProcessMemory(),
406
+ transfer: resourceMatrixTransfer(),
407
+ close: { attempted: false },
408
+ tab_count: plan.scenario === "D" ? plan.tab_count : null,
409
+ dirty_formats: [],
410
+ ...(plan.scenario === "D" ? { not_applicable: { pdf: "read-only" } } : {}),
411
+ error: state.error,
412
+ });
413
+ });
414
+ return;
415
+ }
416
+ const format = resourceMatrixFormat();
417
+ const runId = import.meta.env?.VITE_RESOURCE_MATRIX_RUN_ID;
418
+ const scenario = format ? resourceMatrixScenario(format) : null;
419
+ if (!format || !scenario || !runId || !("__TAURI_INTERNALS__" in window))
420
+ return;
421
+ const startedAt = performance.now();
422
+ const baseline = getResourceCensus();
423
+ const state = { value: "waiting", error: null };
424
+ window.__viewrightResourceMatrixStatus = () => ({ state: state.value, error: state.error });
425
+ let quietSince = null;
426
+ let lastSignature = "";
427
+ const deadline = startedAt + matrixTimeoutMs();
428
+ const complete = async (error, steady, afterClose, usefulReadyAt) => {
429
+ if (state.value === "writing" || state.value === "written" || state.value === "failed")
430
+ return;
431
+ state.value = "writing";
432
+ state.error = error;
433
+ const result = {
434
+ schema_version: 1,
435
+ scenario,
436
+ platform: isWindows() ? "windows" : "macos",
437
+ format: format === "shell" ? null : format,
438
+ sample_id: runId,
439
+ method: "native-resource-matrix-autopilot",
440
+ fixture: format === "shell"
441
+ ? null
442
+ : {
443
+ source_filename: import.meta.env?.VITE_RESOURCE_MATRIX_FIXTURE_FILENAME ?? null,
444
+ bytes: Number(import.meta.env?.VITE_RESOURCE_MATRIX_FIXTURE_BYTES) || null,
445
+ sha256: import.meta.env?.VITE_RESOURCE_MATRIX_FIXTURE_SHA256 ?? null,
446
+ },
447
+ useful_paint_ms: error ? null : Math.max(0, usefulReadyAt - startedAt),
448
+ peak_observed_ms: Math.max(0, performance.now() - startedAt),
449
+ resources: { before: baseline, steady, after_close: afterClose },
450
+ runtime: resourceMatrixRuntime(),
451
+ process_memory: await resourceMatrixProcessMemory(),
452
+ transfer: resourceMatrixTransfer(),
453
+ close: { attempted: scenario === "E" },
454
+ tab_count: null,
455
+ dirty_formats: [],
456
+ error,
457
+ };
458
+ try {
459
+ await writeResourceMatrixResult(runId, result);
460
+ state.value = "written";
461
+ }
462
+ catch (writeError) {
463
+ state.value = "failed";
464
+ state.error = `artifact write failed: ${String(writeError)}`;
465
+ }
466
+ };
467
+ const closeAndComplete = (steady, usefulReadyAt) => {
468
+ state.value = "closing";
469
+ window.dispatchEvent(new KeyboardEvent("keydown", {
470
+ key: "w",
471
+ code: "KeyW",
472
+ ctrlKey: isWindows(),
473
+ metaKey: !isWindows(),
474
+ bubbles: true,
475
+ cancelable: true,
476
+ }));
477
+ const pollClose = () => {
478
+ const current = getResourceCensus();
479
+ if (format !== "shell" && (current.byFormat[format]?.live ?? 0) === 0) {
480
+ void complete(null, steady, current, usefulReadyAt);
481
+ return;
482
+ }
483
+ if (performance.now() >= deadline) {
484
+ void complete(`timed out waiting for ${format} close release`, steady, current, usefulReadyAt);
485
+ return;
486
+ }
487
+ window.setTimeout(pollClose, MATRIX_POLL_MS);
488
+ };
489
+ window.setTimeout(pollClose, MATRIX_POLL_MS);
490
+ };
491
+ const poll = () => {
492
+ if (state.value !== "waiting")
493
+ return;
494
+ const now = performance.now();
495
+ const current = getResourceCensus();
496
+ if (!resourceMatrixReady(format, current)) {
497
+ if (now >= deadline)
498
+ void complete(`timed out waiting for ${format} useful readiness`, current, null, now);
499
+ else
500
+ window.setTimeout(poll, MATRIX_POLL_MS);
501
+ return;
502
+ }
503
+ const signature = JSON.stringify(current);
504
+ if (signature !== lastSignature) {
505
+ lastSignature = signature;
506
+ quietSince = now;
507
+ }
508
+ if (quietSince !== null && now - quietSince >= MATRIX_QUIET_MS) {
509
+ if (scenario === "E")
510
+ closeAndComplete(current, now);
511
+ else
512
+ void complete(null, current, null, now);
513
+ return;
514
+ }
515
+ window.setTimeout(poll, MATRIX_POLL_MS);
516
+ };
517
+ window.setTimeout(poll, MATRIX_POLL_MS);
518
+ }
@@ -0,0 +1,34 @@
1
+ import type { MutableRefObject } from "react";
2
+ import type { HtmlPageBuffer, HtmlSessionPages } from "@rightkit/html-editor/documents/html/sessionTypes";
3
+ import { type ImageSaveFunction } from "@rightkit/media-ui";
4
+ import type { LoadedDocument, ViewMode } from "@rightkit/document-core/documents/types";
5
+ export interface SetupRecoveryEntry {
6
+ document: Omit<LoadedDocument, "assetUrl" | "tabKey">;
7
+ view?: ViewMode;
8
+ editedText?: string;
9
+ htmlPages?: Record<string, HtmlPageBuffer>;
10
+ binary?: {
11
+ base64: string;
12
+ mime: string;
13
+ };
14
+ }
15
+ export interface SetupRecoverySnapshot {
16
+ version: 1;
17
+ activePath: string | null;
18
+ documents: SetupRecoveryEntry[];
19
+ }
20
+ interface CaptureSetupRecoveryDeps {
21
+ openDocs: LoadedDocument[];
22
+ activePath: string | null;
23
+ views: Record<string, ViewMode>;
24
+ isDirty: (doc: LoadedDocument) => boolean;
25
+ editsRef: MutableRefObject<Record<string, string>>;
26
+ htmlPagesRef: MutableRefObject<HtmlSessionPages>;
27
+ docxSaversRef: MutableRefObject<Map<string, () => Promise<ArrayBuffer | null>>>;
28
+ imageSaversRef: MutableRefObject<Map<string, ImageSaveFunction>>;
29
+ }
30
+ export declare function captureSetupRecovery({ openDocs, activePath, views, isDirty, editsRef, htmlPagesRef, docxSaversRef, imageSaversRef, }: CaptureSetupRecoveryDeps): Promise<SetupRecoverySnapshot>;
31
+ export declare function saveSetupRecoverySnapshot(snapshot: SetupRecoverySnapshot): Promise<void>;
32
+ export declare function loadSetupRecoverySnapshot(): Promise<SetupRecoverySnapshot | null>;
33
+ export declare function clearSetupRecoverySnapshot(): Promise<void>;
34
+ export {};
@@ -0,0 +1,70 @@
1
+ import { editableImageOutputForPath } from "@rightkit/media-ui";
2
+ import { bufferToBase64 } from "@rightkit/docx-editor/documents/docx/blankDocx";
3
+ import { clearSetupRecovery as clearNativeSetupRecovery, loadSetupRecovery as loadNativeSetupRecovery, saveSetupRecovery as saveNativeSetupRecovery, } from "@rightkit/document-core/state/tauriBridge";
4
+ function storableDocument(doc) {
5
+ const { assetUrl: _assetUrl, tabKey: _tabKey, ...stored } = doc;
6
+ return stored;
7
+ }
8
+ export async function captureSetupRecovery({ openDocs, activePath, views, isDirty, editsRef, htmlPagesRef, docxSaversRef, imageSaversRef, }) {
9
+ const dirtyDocs = openDocs.filter(isDirty);
10
+ const dirtyPdf = dirtyDocs.find((doc) => doc.format === "pdf");
11
+ if (dirtyPdf) {
12
+ // PDF source commands & annotations are not represented by the text snapshot channel.
13
+ // Keep the running window and edits intact instead of replacing it with incomplete recovery.
14
+ throw new Error(`Save or close ${dirtyPdf.displayName} before installing the update.`);
15
+ }
16
+ const documents = await Promise.all(dirtyDocs.map(async (doc) => {
17
+ const entry = {
18
+ document: storableDocument(doc),
19
+ view: views[doc.path],
20
+ };
21
+ if (doc.format === "docx") {
22
+ const buffer = await docxSaversRef.current.get(doc.path)?.();
23
+ if (!buffer)
24
+ throw new Error(`Could not preserve ${doc.displayName}.`);
25
+ entry.binary = {
26
+ base64: bufferToBase64(buffer),
27
+ mime: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
28
+ };
29
+ }
30
+ else if (doc.format === "image") {
31
+ const saver = imageSaversRef.current.get(doc.path);
32
+ const payload = await saver?.(editableImageOutputForPath(doc.path) ?? "png");
33
+ if (!payload)
34
+ throw new Error(`Could not preserve ${doc.displayName}.`);
35
+ entry.binary = { base64: bufferToBase64(payload.buffer), mime: payload.mime };
36
+ }
37
+ else {
38
+ entry.editedText = editsRef.current[doc.path] ?? doc.content;
39
+ if (doc.format === "html" && htmlPagesRef.current[doc.path]) {
40
+ entry.htmlPages = htmlPagesRef.current[doc.path];
41
+ }
42
+ }
43
+ return entry;
44
+ }));
45
+ return { version: 1, activePath, documents };
46
+ }
47
+ function isSetupRecoverySnapshot(value) {
48
+ if (!value || typeof value !== "object")
49
+ return false;
50
+ const candidate = value;
51
+ return candidate.version === 1 && Array.isArray(candidate.documents);
52
+ }
53
+ export async function saveSetupRecoverySnapshot(snapshot) {
54
+ await saveNativeSetupRecovery(JSON.stringify(snapshot));
55
+ }
56
+ export async function loadSetupRecoverySnapshot() {
57
+ const raw = await loadNativeSetupRecovery();
58
+ if (!raw)
59
+ return null;
60
+ try {
61
+ const parsed = JSON.parse(raw);
62
+ return isSetupRecoverySnapshot(parsed) ? parsed : null;
63
+ }
64
+ catch {
65
+ return null;
66
+ }
67
+ }
68
+ export async function clearSetupRecoverySnapshot() {
69
+ await clearNativeSetupRecovery();
70
+ }
@@ -0,0 +1,18 @@
1
+ import type { LoadedDocument } from "@rightkit/document-core/documents/types";
2
+ export declare const DocStatsPill: import("react").MemoExoticComponent<typeof DocStatsPillImpl>;
3
+ /** Save state + autosave, shown beside the stats (what 0.1.111's status bar used to say). */
4
+ export interface DocSaveStatus {
5
+ dirtyHint: boolean;
6
+ isDirty: (doc: LoadedDocument) => boolean;
7
+ subscribeDirty: (path: string, cb: () => void) => () => void;
8
+ onSave: () => void;
9
+ autoSave: boolean;
10
+ onToggleAutoSave: () => void;
11
+ }
12
+ declare function DocStatsPillImpl({ doc, subscribeEdits, getEditSnapshot, status, }: {
13
+ doc: LoadedDocument;
14
+ subscribeEdits: (path: string, cb: () => void) => () => void;
15
+ getEditSnapshot: (path: string) => string | undefined;
16
+ status?: DocSaveStatus;
17
+ }): import("react").JSX.Element | null;
18
+ export {};