@rightkit/document-workspace 0.0.0-stage → 0.1.1

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 +11 -0
  2. package/LICENSE-APACHE +201 -0
  3. package/LICENSE-MIT +21 -0
  4. package/README.md +74 -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,153 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { lazy, memo, Suspense, useEffect, } from "react";
3
+ import { capabilitiesFor } from "./capabilities.js";
4
+ import { ErrorBoundary } from "../app/ErrorBoundary.js";
5
+ import { logEvent } from "@rightkit/document-core/state/diagnostics";
6
+ // Heavy viewers — load only when their format is actually opened (keeps cold launch fast).
7
+ // MarkdownSplitPane pulls the unified/remark engine (its preview) + lazies the CodeMirror source;
8
+ // HtmlViewer pulls DOMPurify — all lazy so a cold launch or opening a plain TXT never pays for them.
9
+ const MarkdownSplitPane = lazy(() => import("./markdown/MarkdownSplitPane.js").then((m) => ({ default: m.MarkdownSplitPane })));
10
+ const MarkdownCoreProvider = lazy(() => import("@rightkit/markdown-editor/documents/markdown/MarkdownCoreProvider").then((m) => ({ default: m.MarkdownCoreProvider })));
11
+ const MdxSplitPane = lazy(() => import("./mdx/MdxSplitPane.js").then((m) => ({ default: m.MdxSplitPane })));
12
+ const HtmlDocument = lazy(() => import("./html/HtmlDocument.js").then((m) => ({ default: m.HtmlDocument })));
13
+ const DocxPane = lazy(() => import("@rightkit/docx-editor/documents/docx/DocxPane").then((m) => ({ default: m.DocxPane })));
14
+ const PdfViewer = lazy(() => import("@rightkit/document-core/documents/pdf/NativePdfDocument").then((m) => ({ default: m.NativePdfDocument })));
15
+ const LivePreviewPane = lazy(() => import("./web/LivePreviewPane.js").then((m) => ({ default: m.LivePreviewPane })));
16
+ const ImageViewer = lazy(() => import("./ImageDocument.js").then((m) => ({ default: m.ImageViewer })));
17
+ const TextEditor = lazy(() => import("./txt/TxtEditor.js").then((m) => ({ default: m.TextEditor })));
18
+ // The image viewer's React key: the containing folder, so same-folder paging reuses one instance
19
+ // (seamless swap) while a different folder remounts. Falls back to the full path if it has no parent.
20
+ const VIEWER_ROUTE_EVENTS = {
21
+ preview: "viewer:route:preview",
22
+ source: "viewer:route:source",
23
+ interactive: "viewer:route:interactive",
24
+ };
25
+ const imageInstanceKey = (path) => path.replace(/[\\/][^\\/]*$/, "") || path;
26
+ const Loading = () => (_jsx("div", { className: "h-full bg-ink p-4", children: _jsxs("div", { className: "h-full animate-pulse rounded border border-line bg-panel/40 p-5", children: [_jsx("div", { className: "mb-5 h-6 w-40 rounded bg-panel-2" }), _jsxs("div", { className: "space-y-3", children: [_jsx("div", { className: "h-4 w-3/4 rounded bg-panel-2" }), _jsx("div", { className: "h-4 w-2/3 rounded bg-panel-2" }), _jsx("div", { className: "h-4 w-5/6 rounded bg-panel-2" })] })] }) }));
27
+ export const MarkdownTextFallback = ({ text }) => (_jsx("div", { "data-vr-markdown-first-paint": "", className: "h-full overflow-auto bg-ink px-8 py-6", children: _jsx("pre", { className: "vr-markdown-document-geometry min-h-full whitespace-pre-wrap rounded-lg px-8 py-7 font-serif text-[length:var(--doc-size)] leading-[var(--doc-leading)]", style: { background: "var(--reader-surface)", color: "var(--doc-fg)" }, children: text }) }));
28
+ function ViewerShell({ doc, children }) {
29
+ return (_jsx(ErrorBoundary, { label: `${doc.displayName || doc.format} viewer`, resetKey: `${doc.path}:${doc.format}`, closeTabPath: doc.path, children: _jsx(Suspense, { fallback: _jsx(Loading, {}), children: children }) }));
30
+ }
31
+ function DocumentRouterImpl({ doc, view, readEdit = "read", devMode, text, subscribeEdits, getEditSnapshot, getExternalSeq, sourceViewStateRef, onChange, onSave, onDocxSave, onDocxChange, registerDocxSave, registerImageSave, onDocxToolbar, onImageChange, onOpenImageSibling, registerSearchProvider, active = false, pdfInitialView, onPdfViewState, registerPdfPrint, registerPdfSave, registerPdfSavePlan, onPdfDirtyChange, registerMarkdownExport, markdownScrollRef, onMarkdownScroll, findBar, findMode, onChangeView, onRequestFind, onCloseFind, onRequestReplace, onOpenPath, onMdxImportGranted, html, }) {
32
+ useEffect(() => {
33
+ logEvent(VIEWER_ROUTE_EVENTS[view]);
34
+ }, [view]);
35
+ useEffect(() => {
36
+ if (doc.format === "md") {
37
+ void import("./markdown/MarkdownSplitPane.js");
38
+ void import("@rightkit/markdown-editor/documents/markdown/MarkdownCoreProvider");
39
+ // The source drawer (CodeMirror) is not needed for first paint: warm it after the preview
40
+ // has painted so it stays off the cold-mount critical path but is ready when opened.
41
+ const warmSource = () => {
42
+ void import("./txt/TxtEditor.js");
43
+ };
44
+ if (typeof window.requestIdleCallback === "function") {
45
+ window.requestIdleCallback(warmSource, { timeout: 2000 });
46
+ }
47
+ else {
48
+ window.setTimeout(warmSource, 300);
49
+ }
50
+ }
51
+ else if (doc.format === "mdx") {
52
+ // MDX preview is a separate subtree (sandboxed iframe). The compile path lives in mdxCompiler
53
+ // and the iframe runtime is bundled in mdxBridgeScript; the splitter + source editor are
54
+ // shared with the markdown side, so we prefetch both formats' chunks on first open.
55
+ void import("./mdx/MdxSplitPane.js");
56
+ void import("@rightkit/markdown-editor/documents/mdx/mdxCompiler");
57
+ void import("./markdown/MarkdownSplitPane.js"); // shared TxtEditor + outline code
58
+ void import("./txt/TxtEditor.js");
59
+ }
60
+ else if (doc.format === "html") {
61
+ void import("./txt/TxtEditor.js");
62
+ }
63
+ }, [doc.format]);
64
+ // Every viewer EXCEPT image is keyed by `doc.path` so switching documents REMOUNTS it. That makes
65
+ // the search provider register on mount and unregister on unmount through the normal lifecycle — no
66
+ // separate reset effect (which raced the lazy child's registration), and no "re-register on doc
67
+ // change" hacks inside the viewers. Same-format switches (html→html, pdf→pdf, txt→txt) previously
68
+ // reused one instance whose stable-deps registration effect never re-ran, so a leaving-doc reset
69
+ // left the provider null and Ctrl+F couldn't open. Keying fixes that whole class of bug. The key is
70
+ // stable while editing one doc (only switches remount), so the caret/scroll within a doc is intact.
71
+ // The IMAGE viewer is the deliberate exception: it's keyed by its FOLDER (imageInstanceKey), not the
72
+ // file, so same-folder paging reuses one mounted instance (seamless swap). That's safe because the
73
+ // image viewer registers NO search provider and resets its per-image edit/view state internally —
74
+ // do NOT "fix" it back to doc.path or you'll reintroduce the remount → blank → resize on paging.
75
+ switch (doc.format) {
76
+ case "web":
77
+ return (_jsx(ViewerShell, { doc: doc, children: _jsx(LivePreviewPane, { doc: doc }, doc.path) }));
78
+ case "pdf":
79
+ return (_jsx(ViewerShell, { doc: doc, children: _jsx(PdfViewer, { documentPath: doc.path, url: doc.assetUrl, active: active, readOnly: readEdit !== "edit", initialView: pdfInitialView, onViewState: onPdfViewState, registerPrint: registerPdfPrint, registerPdfSave: registerPdfSave, registerPdfSavePlan: registerPdfSavePlan, onDirtyChange: onPdfDirtyChange, registerSearchProvider: registerSearchProvider, findBar: findBar, findMode: findMode, onRequestFind: onRequestFind, onRequestReplace: onRequestReplace }, doc.path) }));
80
+ case "docx":
81
+ return (_jsx(ViewerShell, { doc: doc, children: _jsx(DocxPane, { documentPath: doc.path, url: doc.assetUrl, readOnly: readEdit !== "edit", initialBase64: doc.assetUrl ? undefined : doc.content, onSave: onDocxSave, onChange: onDocxChange, registerSave: registerDocxSave, registerMenu: onDocxToolbar, registerSearchProvider: registerSearchProvider, findBar: findBar, findMode: findMode, onRequestFind: onRequestFind, onRequestReplace: onRequestReplace }, doc.path) }));
82
+ case "image":
83
+ return (_jsx(ViewerShell, { doc: doc, children: _jsx(ImageViewer, { doc: doc, active: active, readOnly: readEdit !== "edit", onChange: onImageChange, registerSave: registerImageSave, onOpenSibling: onOpenImageSibling }, imageInstanceKey(doc.path)) }));
84
+ case "txt":
85
+ return (_jsx(ViewerShell, { doc: doc, children: _jsx(TextEditor, { documentPath: doc.path, language: "plain", text: text, onChange: onChange, onSave: onSave, registerSearchProvider: registerSearchProvider, findBar: findBar, findMode: findMode, onRequestFind: onRequestFind, onRequestReplace: onRequestReplace, active: active, readOnly: readEdit !== "edit", initialViewState: sourceViewStateRef?.current[doc.path] ?? null, onViewStateChange: (state) => {
86
+ if (sourceViewStateRef)
87
+ sourceViewStateRef.current[doc.path] = state;
88
+ } }, doc.path) }));
89
+ case "html":
90
+ if (!html)
91
+ throw new Error("HTML router props are missing");
92
+ return (_jsx(ViewerShell, { doc: doc, children: _jsx(HtmlDocument, { entryPath: doc.path, view: view, text: text, onChange: onChange, registerSearchProvider: registerSearchProvider, findBar: findBar, findMode: findMode, onRequestFind: onRequestFind, onRequestReplace: onRequestReplace, onCloseFind: onCloseFind, onOpenPath: onOpenPath, onChangeView: onChangeView, active: active, readOnly: readEdit !== "edit", pageBuffers: html.pageBuffers, dirtyPagePaths: html.dirtyPagePaths, onEnsurePage: html.onEnsurePage, onPageChange: html.onPageChange, onSetActivePage: html.onSetActivePage, onSavePage: html.onSavePage, onDiscardPage: html.onDiscardPage, onReloadFromDisk: html.onReloadFromDisk, initialPreviewScrollY: html.initialPreviewScrollY, onPreviewScroll: html.onPreviewScroll, sourceViewStateRef: sourceViewStateRef }, doc.path) }));
93
+ case "mdx":
94
+ // MDX is JSX-execution content — it can NEVER mount in the app's React tree (it would
95
+ // share window/Tauri/localStorage with the shell). The MdxSplitPane owns a sandboxed
96
+ // iframe (`sandbox="allow-scripts"`, never `allow-same-origin`) and keeps the markdown
97
+ // source drawer verbatim. Compile + postMessage live in MdxPreviewFrame.
98
+ return (_jsx(ViewerShell, { doc: doc, children: _jsx(MdxSplitPane, { text: text, documentPath: doc.path, subscribeEdits: subscribeEdits, getEditSnapshot: getEditSnapshot, showSource: view === "source", onCloseFind: onCloseFind, devMode: devMode, readOnly: readEdit !== "edit", onChange: onChange, onSave: onSave, registerSearchProvider: registerSearchProvider, findBar: findBar, findMode: findMode, onRequestFind: onRequestFind, onRequestReplace: onRequestReplace, onOpenPath: onOpenPath, onImportGranted: onMdxImportGranted, active: active }, doc.path) }));
99
+ default:
100
+ // md — preview is ALWAYS mounted; the source drawer toggles via `showSource` (no
101
+ // component swap, so opening source no longer remounts/refreshes the preview, like HTML's drawer).
102
+ return (_jsx(ViewerShell, { doc: doc, children: _jsx(Suspense, { fallback: _jsx(MarkdownTextFallback, { text: text }), children: _jsx(MarkdownCoreProvider, { documentId: doc.path, initialText: text, onChange: onChange, subscribeEdits: subscribeEdits, getEditSnapshot: getEditSnapshot, getExternalSeq: getExternalSeq, children: _jsx(MarkdownSplitPane, { text: text, documentPath: doc.path, subscribeEdits: subscribeEdits, getEditSnapshot: getEditSnapshot, showSource: view === "source", onCloseFind: onCloseFind, devMode: devMode, readOnly: readEdit !== "edit", onChange: onChange, onSave: onSave, registerSearchProvider: registerSearchProvider, findBar: findBar, findMode: findMode, onRequestFind: onRequestFind, onRequestReplace: onRequestReplace, onOpenPath: onOpenPath, onChangeView: onChangeView, active: active, registerMarkdownExport: registerMarkdownExport, scrollContainerRef: markdownScrollRef, onScroll: onMarkdownScroll, sourceViewStateRef: sourceViewStateRef }, doc.path) }) }) }));
103
+ }
104
+ }
105
+ // Active + MRU + protected tabs may stay mounted (see App.tsx). Without this memo, shell updates
106
+ // cascade into every live pane; with many tabs open that can make the main thread drop keystrokes.
107
+ // Format-aware: PDF, Image, HTML, and DOCX viewers consume active ownership for side effects
108
+ // (shortcut/scroll state, save state, active print ownership, or the global DOCX toolbar). Web can
109
+ // stay isolated from an active-only wrapper change. Find placement is viewer-owned, so it is compared too.
110
+ const formatUsesActive = (doc) => !!doc && capabilitiesFor(doc.format).canEdit;
111
+ const formatUsesFindBar = (doc) => doc?.format !== "image" && doc?.format !== "web";
112
+ // T5-1: `html` used to be entirely omitted from the memo comparison, so an HTML tab's session props
113
+ // (pageBuffers/dirtyPagePaths) could change underneath the memo without ever re-rendering
114
+ // the pane. Compare the fields
115
+ // that actually drive its rendering. App.tsx's htmlPropsFor (T2-3) keeps this prop referentially
116
+ // STABLE per entryPath unless that path's own slice changed, so the `prev === next` fast path is the
117
+ // common case for every OTHER open html tab — this only forces a render when html truly changed for
118
+ // THIS doc, it never widens re-renders beyond that.
119
+ function htmlPropsEqual(prev, next) {
120
+ if (prev === next)
121
+ return true;
122
+ if (!prev || !next)
123
+ return false;
124
+ if (prev.pageBuffers !== next.pageBuffers)
125
+ return false;
126
+ if (prev.dirtyPagePaths !== next.dirtyPagePaths) {
127
+ if (prev.dirtyPagePaths.length !== next.dirtyPagePaths.length)
128
+ return false;
129
+ for (let i = 0; i < prev.dirtyPagePaths.length; i++) {
130
+ if (prev.dirtyPagePaths[i] !== next.dirtyPagePaths[i])
131
+ return false;
132
+ }
133
+ }
134
+ return true;
135
+ }
136
+ export function documentRouterPropsEqual(prev, next) {
137
+ if ((formatUsesActive(prev.doc) || formatUsesActive(next.doc)) && prev.active !== next.active) {
138
+ return false;
139
+ }
140
+ if ((prev.doc.format === "html" || next.doc.format === "html") &&
141
+ !htmlPropsEqual(prev.html, next.html)) {
142
+ return false;
143
+ }
144
+ return (prev.doc === next.doc &&
145
+ prev.view === next.view &&
146
+ prev.readEdit === next.readEdit &&
147
+ prev.text === next.text &&
148
+ prev.findMode === next.findMode &&
149
+ prev.onChangeView === next.onChangeView &&
150
+ !!prev.onRequestReplace === !!next.onRequestReplace &&
151
+ (!(formatUsesFindBar(prev.doc) || formatUsesFindBar(next.doc)) || prev.findBar === next.findBar));
152
+ }
153
+ export const DocumentRouter = memo(DocumentRouterImpl, documentRouterPropsEqual);
@@ -0,0 +1,6 @@
1
+ import type { DocFormat } from "@rightkit/document-core/documents/types";
2
+ export declare function FileTypeIcon({ format, path, size, }: {
3
+ format: DocFormat;
4
+ path?: string;
5
+ size?: number;
6
+ }): import("react").JSX.Element;
@@ -0,0 +1,93 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ // Format badge — a solid colored file chip with the visible extension stacked vertically, so it
3
+ // stays legible at small sidebar sizes. Four-character labels use a deliberate 2x2 stack.
4
+ // Colors are distinct per format and dark enough for white badge text at small task/sidebar sizes.
5
+ const STYLE = {
6
+ pdf: { label: "PDF", color: "#B83A2F" },
7
+ md: { label: "MD", color: "#2F7D45" },
8
+ mdx: { label: "MDX", color: "#7A5CAE" },
9
+ html: { label: "HTML", color: "#B45424" },
10
+ docx: { label: "DOCX", color: "#1F5FAE" },
11
+ image: { label: "IMG", color: "#8A3B8E" },
12
+ web: { label: "WEB", color: "#0F737A" },
13
+ txt: { label: "TXT", color: "#68707A" },
14
+ unknown: { label: "?", color: "#68707A" },
15
+ };
16
+ const EXTENSION_LABEL_ALIASES = {
17
+ jpeg: "JPG",
18
+ markdown: "MD",
19
+ text: "TXT",
20
+ };
21
+ const EXTENSIONLESS_LABELS = {
22
+ dockerfile: "DOCK",
23
+ makefile: "MAKE",
24
+ };
25
+ const DOTFILE_LABELS = {
26
+ ".env": "ENV",
27
+ ".npmrc": "NPM",
28
+ ".gitignore": "GIT",
29
+ ".gitattributes": "GIT",
30
+ ".editorconfig": "EDIT",
31
+ };
32
+ function badgeLabelFor(format, path) {
33
+ if (!path || format === "web" || path.startsWith("untitled:"))
34
+ return STYLE[format]?.label ?? STYLE.txt.label;
35
+ const name = path.split(/[\\/]/).pop()?.toLowerCase() ?? "";
36
+ const dotfile = DOTFILE_LABELS[name];
37
+ if (dotfile)
38
+ return dotfile;
39
+ const extensionless = EXTENSIONLESS_LABELS[name];
40
+ if (extensionless)
41
+ return extensionless;
42
+ const ext = name.includes(".") ? (name.split(".").pop() ?? "") : "";
43
+ if (!ext)
44
+ return STYLE[format]?.label ?? STYLE.txt.label;
45
+ return (EXTENSION_LABEL_ALIASES[ext] ?? ext.toUpperCase()).slice(0, 4);
46
+ }
47
+ export function FileTypeIcon({ format, path, size = 18, }) {
48
+ const s = STYLE[format] ?? STYLE.txt;
49
+ const label = badgeLabelFor(format, path);
50
+ const letters = label.split("");
51
+ const w = size;
52
+ const h = Math.round(size * 1.3); // a touch taller than wide — reads as a file/spine
53
+ if (letters.length === 4) {
54
+ const fontSize = Math.max(5, Math.min(size * 0.42, 8));
55
+ return (_jsx("span", { "aria-label": `${label} file`, role: "img", style: {
56
+ display: "inline-grid",
57
+ gridTemplateColumns: "1fr 1fr",
58
+ alignItems: "center",
59
+ justifyItems: "center",
60
+ width: w,
61
+ height: h,
62
+ background: s.color,
63
+ borderRadius: Math.max(2, size * 0.16),
64
+ color: "#fff",
65
+ fontFamily: '"JetBrains Mono", ui-monospace, monospace',
66
+ fontWeight: 800,
67
+ lineHeight: 1,
68
+ letterSpacing: 0,
69
+ userSelect: "none",
70
+ flexShrink: 0,
71
+ padding: Math.max(1, Math.round(size * 0.12)),
72
+ }, children: letters.map((c, i) => (_jsx("span", { style: { fontSize }, children: c }, i))) }));
73
+ }
74
+ // Fit the stacked letters to the chip height, but never so big they crowd the width.
75
+ const fontSize = Math.max(5, Math.min(((h - 3) / letters.length) * 0.86, w * 0.66));
76
+ return (_jsx("span", { "aria-label": `${label} file`, role: "img", style: {
77
+ display: "inline-flex",
78
+ flexDirection: "column",
79
+ alignItems: "center",
80
+ justifyContent: "center",
81
+ width: w,
82
+ height: h,
83
+ background: s.color,
84
+ borderRadius: Math.max(2, size * 0.16),
85
+ color: "#fff",
86
+ fontFamily: '"JetBrains Mono", ui-monospace, monospace',
87
+ fontWeight: 700,
88
+ lineHeight: 1.02,
89
+ letterSpacing: 0,
90
+ userSelect: "none",
91
+ flexShrink: 0,
92
+ }, children: letters.map((c, i) => (_jsx("span", { style: { fontSize }, children: c }, i))) }));
93
+ }
@@ -0,0 +1,14 @@
1
+ import type { ImageSaveFunction } from "@rightkit/media-ui";
2
+ import type { LoadedDocument } from "@rightkit/document-core/documents/types";
3
+ interface Props {
4
+ doc: LoadedDocument;
5
+ active?: boolean;
6
+ onChange: () => void;
7
+ registerSave: (fn: ImageSaveFunction | null) => void;
8
+ /** Page to the previous/next image in the same folder, replacing this tab in place. */
9
+ onOpenSibling?: (currentPath: string, targetPath: string) => void;
10
+ /** Read mode: no edit tools — zoom/fit/paging stay, the stage is pure view. */
11
+ readOnly?: boolean;
12
+ }
13
+ export declare function ImageViewer({ doc, ...props }: Props): import("react").JSX.Element;
14
+ export {};
@@ -0,0 +1,61 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // the host app host for @rightkit/media-ui's image viewer/editor. Reproduces the former local
3
+ // layout: tool panels in-flow above the stage inside the chrome toolbar, and only the capsule
4
+ // docked into App's single document bar (via DocBar, which renders nothing for inactive panes).
5
+ import { useState } from "react";
6
+ import { CaretLeft } from "@phosphor-icons/react/dist/icons/CaretLeft";
7
+ import { CaretRight } from "@phosphor-icons/react/dist/icons/CaretRight";
8
+ import { ImageViewer as RightKitImageViewer } from "@rightkit/media-ui/image";
9
+ import { DocBar } from "@rightkit/document-core/app/shell/docBar";
10
+ import { BUNDLED_FONTS } from "@rightkit/document-core/settings/fonts";
11
+ import { listSiblingImages, listSystemFonts } from "@rightkit/document-core/state/tauriBridge";
12
+ import { printImageDataUrl } from "@rightkit/document-core/app/documentPrinting";
13
+ import { logError } from "@rightkit/document-core/state/diagnostics";
14
+ import { toast } from "@rightkit/platform-ui/headless";
15
+ import { acquireResource, updateResource } from "@rightkit/document-core/app/resourceCensus";
16
+ import { hostAppName } from "../appName.js";
17
+ const ADAPTERS = {
18
+ icons: {
19
+ caretLeft: _jsx(CaretLeft, { size: 18, weight: "regular", "aria-hidden": "true" }),
20
+ caretRight: _jsx(CaretRight, { size: 18, weight: "regular", "aria-hidden": "true" }),
21
+ },
22
+ listImageSiblings: listSiblingImages,
23
+ listSystemFonts,
24
+ bundledFonts: BUNDLED_FONTS,
25
+ bundledFontsLabel: `${hostAppName()} fonts`,
26
+ bundledFontsEmptyOption: hostAppName(),
27
+ defaultFontId: "inter",
28
+ fallbackFontStack: "'Inter', system-ui, sans-serif",
29
+ printImage: printImageDataUrl,
30
+ acquireResource: ({ kind, documentPath, estimatedBytes, dirty, stateExternalized }) => {
31
+ const release = acquireResource({
32
+ kind: kind,
33
+ documentPath,
34
+ estimatedBytes,
35
+ dirty,
36
+ stateExternalized,
37
+ format: "image",
38
+ });
39
+ return {
40
+ release,
41
+ update: (next) => updateResource(release, {
42
+ ...(next.dirty === undefined ? {} : { dirty: next.dirty }),
43
+ ...(next.stateExternalized === undefined
44
+ ? {}
45
+ : { stateExternalized: next.stateExternalized }),
46
+ }),
47
+ };
48
+ },
49
+ notify: (message, severity) => toast(message, severity),
50
+ // media-ui reports only the "print:failed" tag; any other tag has no allowlisted label.
51
+ logError: (tag) => {
52
+ if (tag === "print:failed")
53
+ logError(tag);
54
+ },
55
+ };
56
+ export function ImageViewer({ doc, ...props }) {
57
+ const [panelsHost, setPanelsHost] = useState(null);
58
+ const [capsuleHost, setCapsuleHost] = useState(null);
59
+ const displaySrc = doc.assetUrl || doc.content;
60
+ return (_jsxs("div", { "data-vr-image-host": "", className: "flex h-full min-h-0 flex-col bg-ink", children: [_jsxs("div", { className: "no-print vr-chrome vr-document-toolbar relative flex flex-shrink-0 flex-col text-chrome-xs text-muted", children: [_jsx("div", { ref: setPanelsHost, className: "contents" }), _jsx("div", { className: "relative z-30 shrink-0", children: _jsx(DocBar, { children: _jsx("div", { ref: setCapsuleHost, className: "vr-capsule vr-image-capsule h-[42px] px-1" }) }) })] }), _jsx(RightKitImageViewer, { ...props, path: doc.path, displayName: doc.displayName, src: doc.content || displaySrc, displaySrc: displaySrc, printEventName: "viewright:print", toolbarContainer: capsuleHost, panelsContainer: panelsHost, adapters: ADAPTERS })] }));
61
+ }
@@ -0,0 +1,17 @@
1
+ import type { DocFormat } from "@rightkit/document-core/documents/types";
2
+ export interface DocumentCapabilities {
3
+ /** Edited inside the host app, either through a source surface, the DOCX bridge, or the image shell. */
4
+ canEdit: boolean;
5
+ /** Offers Save As for editable local formats. */
6
+ canSaveAs: boolean;
7
+ /** Printable via the shell's print path. DOCX prints through its own editor bridge instead. */
8
+ canPrintInApp: boolean;
9
+ /** Has a shell-level preview/source view toggle. */
10
+ canSwitchView: boolean;
11
+ /** Find supports replace (text formats). */
12
+ canReplace: boolean;
13
+ }
14
+ /** Single entry point for format capabilities. Callers read `capabilitiesFor(format).canReplace`. */
15
+ export declare function capabilitiesFor(format: DocFormat): DocumentCapabilities;
16
+ /** md and mdx share one Markdown pipeline; use this instead of `format === "md" || format === "mdx"`. */
17
+ export declare function isMarkdownFormat(format: DocFormat | undefined): boolean;
@@ -0,0 +1,18 @@
1
+ const EDITABLE = new Set(["txt", "md", "mdx", "html", "docx", "pdf", "image"]);
2
+ // md/mdx (preview/source), html (interactive/safe + source drawer). Images edit inside the viewer shell.
3
+ const VIEW_SWITCH = new Set(["md", "mdx", "html"]);
4
+ const TEXT_REPLACE = new Set(["txt", "md", "mdx", "html"]);
5
+ /** Single entry point for format capabilities. Callers read `capabilitiesFor(format).canReplace`. */
6
+ export function capabilitiesFor(format) {
7
+ return {
8
+ canEdit: EDITABLE.has(format),
9
+ canSaveAs: EDITABLE.has(format),
10
+ canPrintInApp: format !== "docx" && format !== "web" && format !== "unknown",
11
+ canSwitchView: VIEW_SWITCH.has(format),
12
+ canReplace: TEXT_REPLACE.has(format),
13
+ };
14
+ }
15
+ /** md and mdx share one Markdown pipeline; use this instead of `format === "md" || format === "mdx"`. */
16
+ export function isMarkdownFormat(format) {
17
+ return format === "md" || format === "mdx";
18
+ }
@@ -0,0 +1,15 @@
1
+ import type { HtmlDocumentProps } from "./HtmlDocumentProps.js";
2
+ export type { HtmlDocumentProps } from "./HtmlDocumentProps.js";
3
+ /**
4
+ * One tab = one local-site session. The tab is identified by `entryPath`; `activePath` is the page
5
+ * currently shown and may roam to sub-pages as the user navigates (always under the entry folder —
6
+ * Rust enforces containment). Switching modes/opening the source drawer keeps this component mounted,
7
+ * so the nav stack and every page's edit buffer survive.
8
+ *
9
+ * This file is the mode-router shell: it owns the preview-build lifecycle (applied snapshot → srcDoc),
10
+ * the source drawer state, and layout. Behavior lives in focused hooks (useHtmlPreviewControls,
11
+ * useHtmlInspect, useHtmlBlockDrafts, useHtmlAutoViewport, plus the existing navigation/bridge/block
12
+ * hooks); rendering is delegated to HtmlDocumentView (preview surface), HtmlDocumentSource (source
13
+ * drawer), and HtmlDocumentEdit (inspect context menu).
14
+ */
15
+ export declare function HtmlDocument({ entryPath, view, text, onChange, registerSearchProvider, findBar, findMode, onRequestFind, onRequestReplace, onCloseFind, onOpenPath, onChangeView, readOnly, active, pageBuffers, dirtyPagePaths, onEnsurePage, onPageChange, onSetActivePage, onSavePage, onDiscardPage, onReloadFromDisk, initialPreviewScrollY, onPreviewScroll, sourceViewStateRef: externalSourceViewStateRef, }: HtmlDocumentProps): import("react").JSX.Element;