@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,546 @@
1
+ import { useEffect, useMemo, useRef } from "react";
2
+ import { closeBrackets } from "@codemirror/autocomplete";
3
+ import { history, redo, undo } from "@codemirror/commands";
4
+ import { bracketMatching, indentOnInput, syntaxHighlighting } from "@codemirror/language";
5
+ import { highlightSelectionMatches } from "@codemirror/search";
6
+ import { Compartment, EditorState } from "@codemirror/state";
7
+ import { dropCursor, EditorView, highlightActiveLine, rectangularSelection, } from "@codemirror/view";
8
+ import { bytesToBase64 } from "@rightkit/document-core/documents/base64";
9
+ import { completionExtension, contentAttributesExtension, diagnosticsExtension, gutterExtension, imagePasteExtension, invisibleCharactersExtension, languageExtension, loadDiagnosticsExtension, loadLanguageExtension, loadMarkdownCoreCompletionExtension, readOnlyExtension, searchHighlightClickExtension, searchHighlightField, sourceKeymap, sourceWrapExtension, } from "./sourceExtensions.js";
10
+ import { sourceHighlightStyle } from "./sourceTheme.js";
11
+ import { acquireResource } from "@rightkit/document-core/app/resourceCensus";
12
+ export function useCodeMirrorSourceEditor(args) {
13
+ // The EditorView construction effect is mount-only. Runtime settings live in compartments below;
14
+ // putting every changing argument in its dependency list destroys/recreates the view on a simple
15
+ // read-only or spellcheck update. Keep the initial construction snapshot separate from live refs.
16
+ const constructionArgsRef = useRef(args);
17
+ constructionArgsRef.current = args;
18
+ const languageCompartment = useMemo(() => new Compartment(), []);
19
+ const readOnlyCompartment = useMemo(() => new Compartment(), []);
20
+ const contentAttributesCompartment = useMemo(() => new Compartment(), []);
21
+ const wordWrapCompartment = useMemo(() => new Compartment(), []);
22
+ const gutterCompartment = useMemo(() => new Compartment(), []);
23
+ const themeCompartment = useMemo(() => new Compartment(), []);
24
+ const keymapCompartment = useMemo(() => new Compartment(), []);
25
+ const completionCompartment = useMemo(() => new Compartment(), []);
26
+ const diagnosticsCompartment = useMemo(() => new Compartment(), []);
27
+ const invisiblesCompartment = useMemo(() => new Compartment(), []);
28
+ useEffect(() => {
29
+ const previousRegister = args.registerSearchProviderRef.current;
30
+ if (previousRegister === args.registerSearchProvider)
31
+ return;
32
+ args.registerSearchProviderRef.current = args.registerSearchProvider;
33
+ const provider = args.registeredProviderRef.current;
34
+ if (!provider)
35
+ return;
36
+ previousRegister(null);
37
+ args.registerSearchProvider(provider);
38
+ }, [args.registerSearchProvider, args.registerSearchProviderRef, args.registeredProviderRef]);
39
+ useEffect(() => {
40
+ const args = constructionArgsRef.current;
41
+ const host = args.hostRef.current;
42
+ if (!host)
43
+ return;
44
+ let applyingFromCore = false;
45
+ const handleSmartPaste = async (event, view) => {
46
+ const core = args.markdownCore;
47
+ if (!core || args.language !== "markdown")
48
+ return false;
49
+ const html = event.clipboardData?.getData("text/html") ?? "";
50
+ const text = event.clipboardData?.getData("text/plain") ?? "";
51
+ if (!html)
52
+ return false;
53
+ event.preventDefault();
54
+ const [{ smartPaste }, { DEFAULT_MARKDOWN_ASSET_SETTINGS }, { NativeMarkdownAssetClient, validateMarkdownAsset },] = await Promise.all([
55
+ import("@rightkit/markdown-editor/documents/markdown/assets/smartPaste"),
56
+ import("@rightkit/markdown-editor/documents/markdown/assets/markdownAssetSettings"),
57
+ import("@rightkit/markdown-editor/documents/markdown/assets/markdownAsset"),
58
+ ]);
59
+ const selection = view.state.selection.main;
60
+ const snapshot = core.snapshot();
61
+ const client = new NativeMarkdownAssetClient();
62
+ const saved = [];
63
+ try {
64
+ const result = await smartPaste({
65
+ html,
66
+ text,
67
+ documentBasename: core.state.documentId.split(/[\\/]/).pop() ?? "document",
68
+ settings: DEFAULT_MARKDOWN_ASSET_SETTINGS,
69
+ insertAt: selection.from,
70
+ resolveAssetPath: async (payload) => {
71
+ const error = validateMarkdownAsset(payload);
72
+ if (error)
73
+ throw error;
74
+ const source = assetPayloadBase64(payload.source);
75
+ const savedAsset = await client.save({
76
+ payloadBase64: source,
77
+ hintName: payload.hintName,
78
+ mimeKind: payload.mimeKind,
79
+ documentPath: core.state.documentId,
80
+ });
81
+ if (savedAsset.rollbackToken)
82
+ saved.push(savedAsset.rollbackToken);
83
+ return savedAsset.relativePath;
84
+ },
85
+ });
86
+ const edits = result.edits.map((edit) => ({ ...edit, to: selection.to }));
87
+ core.applyCommand({
88
+ edits,
89
+ expectedRevision: snapshot.revision,
90
+ origin: "source",
91
+ label: "Paste",
92
+ isolateHistory: true,
93
+ });
94
+ await Promise.all(saved.map((token) => client.commit(token)));
95
+ }
96
+ catch {
97
+ await Promise.all(saved.map((token) => client.rollback(token)));
98
+ }
99
+ return true;
100
+ };
101
+ const extensions = [
102
+ ...(args.externalHistory ? [] : [history()]),
103
+ dropCursor(),
104
+ rectangularSelection(),
105
+ highlightActiveLine(),
106
+ indentOnInput(),
107
+ bracketMatching(),
108
+ closeBrackets(),
109
+ completionCompartment.of(completionExtension(args.language)),
110
+ syntaxHighlighting(sourceHighlightStyle, { fallback: true }),
111
+ highlightSelectionMatches(),
112
+ searchHighlightField,
113
+ searchHighlightClickExtension,
114
+ gutterCompartment.of(gutterExtension(args.language)),
115
+ diagnosticsCompartment.of(diagnosticsExtension(args.language, null)),
116
+ themeCompartment.of(args.themeExtension),
117
+ readOnlyCompartment.of(readOnlyExtension(args.readOnly)),
118
+ contentAttributesCompartment.of(contentAttributesExtension(args.settingsSpellcheck, args.readOnly)),
119
+ wordWrapCompartment.of(sourceWrapExtension(args.settingsWordWrap, args.language, args.sourceWrap)),
120
+ invisiblesCompartment.of(invisibleCharactersExtension(args.showInvisibles)),
121
+ imagePasteExtension(args.language, args.markdownCore ? handleSmartPaste : undefined),
122
+ EditorView.updateListener.of((update) => {
123
+ const core = args.markdownCore;
124
+ if (update.selectionSet && core && args.language === "markdown") {
125
+ const selection = update.state.selection.main;
126
+ core.postSelectionHint({
127
+ surface: "source",
128
+ nodeId: null,
129
+ anchor: selection.anchor,
130
+ head: selection.head,
131
+ assoc: selection.head >= selection.anchor ? -1 : 1,
132
+ });
133
+ }
134
+ if (update.selectionSet && args.sourceTypewriterMode) {
135
+ const head = update.state.selection.main.head;
136
+ update.view.dispatch({ effects: EditorView.scrollIntoView(head, { y: "center" }) });
137
+ }
138
+ if (!update.docChanged)
139
+ return;
140
+ // Feed CodeMirror's change set directly to the core to avoid
141
+ // the whole-string LCS diff on every keystroke. This keeps the
142
+ // hot path O(edits) instead of O(n*m) for large documents.
143
+ const next = update.state.doc.toString();
144
+ args.textRef.current = next;
145
+ if (args.suppressChangeRef.current || applyingFromCore)
146
+ return;
147
+ if (!core) {
148
+ args.onChangeRef.current(next);
149
+ return;
150
+ }
151
+ const snapshot = core.snapshot();
152
+ if (snapshot.logicalSource === next)
153
+ return;
154
+ // Prefer the incremental change set; fall back to whole-doc diff
155
+ // only for external reconciliation where the guard applies.
156
+ const edits = [];
157
+ let hasChanges = false;
158
+ try {
159
+ update.changes.iterChanges((fromA, toA, _fromB, _toB, inserted) => {
160
+ hasChanges = true;
161
+ edits.push({
162
+ from: fromA,
163
+ to: toA,
164
+ insert: inserted.toString(),
165
+ inheritMode: "inherit",
166
+ });
167
+ });
168
+ }
169
+ catch {
170
+ // If iterating fails, fall back to bounded diff (external path).
171
+ hasChanges = false;
172
+ }
173
+ if (hasChanges) {
174
+ try {
175
+ core.applyCommand({
176
+ edits,
177
+ expectedRevision: snapshot.revision,
178
+ origin: "source",
179
+ label: "Typing",
180
+ coalesceKey: "source:typing",
181
+ });
182
+ return;
183
+ }
184
+ catch {
185
+ // fall through to recovery — stale revision or validation
186
+ }
187
+ }
188
+ else {
189
+ // CodeMirror normally yields at least one range for docChanged. If
190
+ // it does not, keep source typing incremental without invoking LCS.
191
+ try {
192
+ core.applyCommand({
193
+ edits: [
194
+ {
195
+ from: 0,
196
+ to: snapshot.logicalSource.length,
197
+ insert: next,
198
+ inheritMode: "preserve",
199
+ },
200
+ ],
201
+ expectedRevision: snapshot.revision,
202
+ origin: "source",
203
+ label: "Typing",
204
+ coalesceKey: "source:typing",
205
+ });
206
+ return;
207
+ }
208
+ catch {
209
+ // fall through to recovery — stale revision or validation
210
+ }
211
+ }
212
+ // Recovery: resync editor from core.
213
+ applyingFromCore = true;
214
+ try {
215
+ const current = core.snapshot().logicalSource;
216
+ view.dispatch({ changes: { from: 0, to: view.state.doc.length, insert: current } });
217
+ args.textRef.current = current;
218
+ }
219
+ finally {
220
+ applyingFromCore = false;
221
+ }
222
+ }),
223
+ keymapCompartment.of(sourceKeymap(args.markdownToolbar, args.saveFromEditor, args.formatFromEditor, args.allowNextTabFocusRef, args.wrapInline, args.openLink, args.externalHistory, () => args.markdownCore?.undo(), () => args.markdownCore?.redo())),
224
+ languageCompartment.of(languageExtension(args.language)),
225
+ ];
226
+ const view = new EditorView({
227
+ state: EditorState.create({
228
+ doc: args.textRef.current,
229
+ selection: args.initialViewState
230
+ ? {
231
+ anchor: Math.min(args.initialViewState.anchor, args.textRef.current.length),
232
+ head: Math.min(args.initialViewState.head, args.textRef.current.length),
233
+ }
234
+ : undefined,
235
+ extensions,
236
+ }),
237
+ parent: host,
238
+ });
239
+ args.viewRef.current = view;
240
+ const releaseCensus = acquireResource({
241
+ kind: "codemirror-view",
242
+ documentPath: args.markdownCore?.state.documentId ?? args.documentPath,
243
+ format: args.language === "markdown" ? "md" : args.language === "html" ? "html" : "txt",
244
+ });
245
+ // MarkdownSurfaceId.CodeMirrorSource === 1 << 1 === 2. Avoid static import of markdown core enum to keep TXT isolated.
246
+ const MARKDOWN_SURFACE_ID_CODEMIRROR = 2;
247
+ const searchResultListeners = new Set();
248
+ const unsubscribeCore = args.markdownCore?.subscribe(MARKDOWN_SURFACE_ID_CODEMIRROR, (summary) => {
249
+ const next = args.markdownCore?.snapshot().logicalSource;
250
+ if (next !== undefined && view.state.doc.toString() !== next) {
251
+ applyingFromCore = true;
252
+ try {
253
+ const changes = summary.forwardEdits.map((edit) => ({
254
+ from: edit.from,
255
+ to: edit.to,
256
+ insert: edit.insert,
257
+ }));
258
+ const changeSet = view.state.changes(changes);
259
+ const transaction = view.state.update({
260
+ changes: changeSet,
261
+ selection: mapSelectionThroughChanges(view.state.selection.main, changeSet),
262
+ });
263
+ if (changes.length && transaction.newDoc.toString() === next) {
264
+ view.dispatch(transaction);
265
+ }
266
+ else {
267
+ const { from, to, insert } = boundedSourceTransition(view.state.doc.toString(), next);
268
+ const fallbackChanges = view.state.changes({ from, to, insert });
269
+ view.dispatch({
270
+ changes: fallbackChanges,
271
+ selection: mapSelectionThroughChanges(view.state.selection.main, fallbackChanges),
272
+ });
273
+ }
274
+ args.textRef.current = next;
275
+ }
276
+ finally {
277
+ applyingFromCore = false;
278
+ }
279
+ if (args.searchRef.current.query) {
280
+ const result = args.refreshSearch();
281
+ const query = args.searchRef.current.query;
282
+ for (const listener of searchResultListeners)
283
+ listener(query, result);
284
+ }
285
+ }
286
+ const hint = summary.selectionHint;
287
+ if (hint?.surface === "prosemirror") {
288
+ const anchor = Math.max(0, Math.min(hint.anchor, view.state.doc.length));
289
+ const head = Math.max(0, Math.min(hint.head, view.state.doc.length));
290
+ view.dispatch({ selection: { anchor, head } });
291
+ }
292
+ });
293
+ args.onEditorReadyRef.current?.(view);
294
+ if (args.autoFocus && !args.readOnlyRef.current) {
295
+ requestAnimationFrame(() => {
296
+ if (args.viewRef.current === view)
297
+ view.focus();
298
+ });
299
+ }
300
+ const onSourceScroll = () => {
301
+ const block = view.lineBlockAtHeight(view.scrollDOM.scrollTop + 12);
302
+ args.onSourceScrollLineRef.current?.(view.state.doc.lineAt(block.from).number);
303
+ };
304
+ view.scrollDOM.addEventListener("scroll", onSourceScroll, { passive: true });
305
+ const onUndo = () => {
306
+ if (args.activeRef.current)
307
+ undo(view);
308
+ };
309
+ const onRedo = () => {
310
+ if (args.activeRef.current)
311
+ redo(view);
312
+ };
313
+ if (!args.externalHistory) {
314
+ window.addEventListener("viewright:undo", onUndo);
315
+ window.addEventListener("viewright:redo", onRedo);
316
+ }
317
+ requestAnimationFrame(() => {
318
+ if (args.viewRef.current !== view)
319
+ return;
320
+ view.requestMeasure();
321
+ requestAnimationFrame(() => {
322
+ if (args.viewRef.current === view)
323
+ view.requestMeasure();
324
+ });
325
+ });
326
+ if (args.initialViewState) {
327
+ requestAnimationFrame(() => {
328
+ if (args.viewRef.current === view && args.initialViewState) {
329
+ view.scrollDOM.scrollTop = args.initialViewState.scrollTop;
330
+ view.scrollDOM.scrollLeft = args.initialViewState.scrollLeft;
331
+ }
332
+ });
333
+ }
334
+ const provider = {
335
+ kind: "source",
336
+ canReplace: !args.readOnlyRef.current,
337
+ supportsMatchCase: true,
338
+ supportsWholeWord: true,
339
+ supportsRegex: true,
340
+ find: (query, options) => args.runFind(query, options),
341
+ next: () => args.stepSearch(1),
342
+ prev: () => args.stepSearch(-1),
343
+ replace: (replacement) => args.replaceAndFind(replacement, false),
344
+ replaceAll: (replacement) => args.replaceAndFind(replacement, true),
345
+ subscribe: (listener) => {
346
+ searchResultListeners.add(listener);
347
+ return () => searchResultListeners.delete(listener);
348
+ },
349
+ close: () => {
350
+ args.searchRef.current = {
351
+ query: "",
352
+ options: args.searchRef.current.options,
353
+ matches: [],
354
+ activeIndex: -1,
355
+ };
356
+ args.dispatchSearchHighlights([], -1);
357
+ },
358
+ };
359
+ args.registeredProviderRef.current = provider;
360
+ args.registerSearchProviderRef.current(provider);
361
+ return () => {
362
+ unsubscribeCore?.();
363
+ if (!args.externalHistory) {
364
+ window.removeEventListener("viewright:undo", onUndo);
365
+ window.removeEventListener("viewright:redo", onRedo);
366
+ }
367
+ view.scrollDOM.removeEventListener("scroll", onSourceScroll);
368
+ const sel = view.state.selection.main;
369
+ args.onViewStateChangeRef.current?.({
370
+ anchor: sel.anchor,
371
+ head: sel.head,
372
+ scrollTop: view.scrollDOM.scrollTop,
373
+ scrollLeft: view.scrollDOM.scrollLeft,
374
+ });
375
+ args.registerSearchProviderRef.current(null);
376
+ args.registeredProviderRef.current = null;
377
+ args.onEditorReadyRef.current?.(null);
378
+ view.destroy();
379
+ releaseCensus();
380
+ if (args.viewRef.current === view)
381
+ args.viewRef.current = null;
382
+ };
383
+ }, [
384
+ completionCompartment,
385
+ contentAttributesCompartment,
386
+ diagnosticsCompartment,
387
+ gutterCompartment,
388
+ invisiblesCompartment,
389
+ keymapCompartment,
390
+ languageCompartment,
391
+ readOnlyCompartment,
392
+ themeCompartment,
393
+ wordWrapCompartment,
394
+ ]);
395
+ useEffect(() => {
396
+ const view = args.viewRef.current;
397
+ if (!view)
398
+ return;
399
+ if (args.language === "plain") {
400
+ view.dispatch({
401
+ effects: [
402
+ languageCompartment.reconfigure(languageExtension(args.language)),
403
+ gutterCompartment.reconfigure(gutterExtension(args.language)),
404
+ completionCompartment.reconfigure(completionExtension(args.language)),
405
+ diagnosticsCompartment.reconfigure(diagnosticsExtension(args.language, null)),
406
+ ],
407
+ });
408
+ return;
409
+ }
410
+ let cancelled = false;
411
+ void (async () => {
412
+ const [langExt, diagExt] = await Promise.all([
413
+ loadLanguageExtension(args.language),
414
+ loadDiagnosticsExtension(args.language, args.markdownCore),
415
+ ]);
416
+ if (cancelled || args.viewRef.current !== view)
417
+ return;
418
+ let combinedCompletion = completionExtension(args.language);
419
+ if (args.language === "markdown" && args.markdownCore) {
420
+ const mdCompletion = await loadMarkdownCoreCompletionExtension(args.markdownCore);
421
+ if (cancelled || args.viewRef.current !== view)
422
+ return;
423
+ combinedCompletion = mdCompletion.length
424
+ ? mdCompletion // includes the slash insert source; a second override would conflict
425
+ : combinedCompletion;
426
+ }
427
+ view.dispatch({
428
+ effects: [
429
+ languageCompartment.reconfigure(langExt),
430
+ gutterCompartment.reconfigure(gutterExtension(args.language)),
431
+ completionCompartment.reconfigure(combinedCompletion),
432
+ diagnosticsCompartment.reconfigure(diagExt),
433
+ ],
434
+ });
435
+ })();
436
+ return () => {
437
+ cancelled = true;
438
+ };
439
+ }, [
440
+ args.language,
441
+ args.markdownCore,
442
+ args.viewRef,
443
+ completionCompartment,
444
+ diagnosticsCompartment,
445
+ gutterCompartment,
446
+ languageCompartment,
447
+ ]);
448
+ useEffect(() => {
449
+ args.viewRef.current?.dispatch({ effects: themeCompartment.reconfigure(args.themeExtension) });
450
+ }, [args.themeExtension, args.viewRef, themeCompartment]);
451
+ useEffect(() => {
452
+ args.readOnlyRef.current = args.readOnly;
453
+ args.registeredProviderRef.current = args.registeredProviderRef.current
454
+ ? { ...args.registeredProviderRef.current, canReplace: !args.readOnly }
455
+ : null;
456
+ if (args.registeredProviderRef.current)
457
+ args.registerSearchProviderRef.current(args.registeredProviderRef.current);
458
+ const view = args.viewRef.current;
459
+ if (!view)
460
+ return;
461
+ view.dispatch({
462
+ effects: [
463
+ readOnlyCompartment.reconfigure(readOnlyExtension(args.readOnly)),
464
+ contentAttributesCompartment.reconfigure(contentAttributesExtension(args.settingsSpellcheck, args.readOnly)),
465
+ ],
466
+ });
467
+ }, [
468
+ args.readOnly,
469
+ args.readOnlyRef,
470
+ args.registerSearchProviderRef,
471
+ args.registeredProviderRef,
472
+ args.settingsSpellcheck,
473
+ args.viewRef,
474
+ contentAttributesCompartment,
475
+ readOnlyCompartment,
476
+ ]);
477
+ useEffect(() => {
478
+ args.viewRef.current?.dispatch({
479
+ effects: wordWrapCompartment.reconfigure(sourceWrapExtension(args.settingsWordWrap, args.language, args.sourceWrap)),
480
+ });
481
+ }, [args.language, args.settingsWordWrap, args.sourceWrap, args.viewRef, wordWrapCompartment]);
482
+ useEffect(() => {
483
+ args.viewRef.current?.dispatch({
484
+ effects: invisiblesCompartment.reconfigure(invisibleCharactersExtension(args.showInvisibles)),
485
+ });
486
+ }, [args.showInvisibles, args.viewRef, invisiblesCompartment]);
487
+ useEffect(() => {
488
+ args.viewRef.current?.dispatch({
489
+ effects: keymapCompartment.reconfigure(sourceKeymap(args.markdownToolbar, args.saveFromEditor, args.formatFromEditor, args.allowNextTabFocusRef, args.wrapInline, args.openLink, args.externalHistory, () => args.markdownCore?.undo(), () => args.markdownCore?.redo())),
490
+ });
491
+ }, [
492
+ args.allowNextTabFocusRef,
493
+ args.externalHistory,
494
+ args.formatFromEditor,
495
+ args.markdownToolbar,
496
+ args.markdownCore,
497
+ args.openLink,
498
+ args.saveFromEditor,
499
+ args.viewRef,
500
+ args.wrapInline,
501
+ keymapCompartment,
502
+ ]);
503
+ useEffect(() => {
504
+ const view = args.viewRef.current;
505
+ if (!view)
506
+ return;
507
+ if (args.textRef.current === args.text && view.state.doc.toString() === args.text)
508
+ return;
509
+ args.suppressChangeRef.current = true;
510
+ view.dispatch({ changes: { from: 0, to: view.state.doc.length, insert: args.text } });
511
+ args.suppressChangeRef.current = false;
512
+ args.textRef.current = args.text;
513
+ if (args.searchRef.current.query)
514
+ args.runFind(args.searchRef.current.query, args.searchRef.current.options);
515
+ }, [args.runFind, args.searchRef, args.suppressChangeRef, args.text, args.textRef, args.viewRef]);
516
+ }
517
+ function mapSelectionThroughChanges(selection, changes) {
518
+ const forward = selection.anchor <= selection.head;
519
+ const anchorAssoc = selection.empty ? 1 : forward ? -1 : 1;
520
+ const headAssoc = selection.empty ? 1 : forward ? 1 : -1;
521
+ return {
522
+ anchor: changes.mapPos(selection.anchor, anchorAssoc),
523
+ head: changes.mapPos(selection.head, headAssoc),
524
+ };
525
+ }
526
+ function boundedSourceTransition(previous, next) {
527
+ let from = 0;
528
+ const commonLimit = Math.min(previous.length, next.length);
529
+ while (from < commonLimit && previous.charCodeAt(from) === next.charCodeAt(from))
530
+ from++;
531
+ let previousEnd = previous.length;
532
+ let nextEnd = next.length;
533
+ while (previousEnd > from &&
534
+ nextEnd > from &&
535
+ previous.charCodeAt(previousEnd - 1) === next.charCodeAt(nextEnd - 1)) {
536
+ previousEnd--;
537
+ nextEnd--;
538
+ }
539
+ return { from, to: previousEnd, insert: next.slice(from, nextEnd) };
540
+ }
541
+ function assetPayloadBase64(source) {
542
+ if (typeof source !== "string")
543
+ return bytesToBase64(source);
544
+ const comma = source.indexOf(",");
545
+ return source.startsWith("data:") && comma >= 0 ? source.slice(comma + 1) : source;
546
+ }
@@ -0,0 +1,39 @@
1
+ import { type MutableRefObject } from "react";
2
+ import type { EditorView } from "@codemirror/view";
3
+ import { type TextMatch } from "@rightkit/document-core/documents/searchUtils";
4
+ type SearchOptions = {
5
+ matchCase: boolean;
6
+ wholeWord: boolean;
7
+ regex?: boolean;
8
+ };
9
+ interface SourceSearchSnapshot {
10
+ query: string;
11
+ options: SearchOptions;
12
+ matches: TextMatch[];
13
+ activeIndex: number;
14
+ }
15
+ export declare function useSourceSearch({ viewRef, readOnlyRef, }: {
16
+ viewRef: MutableRefObject<EditorView | null>;
17
+ readOnlyRef: MutableRefObject<boolean>;
18
+ }): {
19
+ clearSearch: () => void;
20
+ dispatchSearchHighlights: (matches: TextMatch[], activeIndex: number) => void;
21
+ replaceAndFind: (replacement: string, all: boolean) => {
22
+ count: number;
23
+ activeIndex: number;
24
+ };
25
+ refreshSearch: () => {
26
+ count: number;
27
+ activeIndex: number;
28
+ };
29
+ runFind: (query: string, options: SearchOptions) => {
30
+ count: number;
31
+ activeIndex: number;
32
+ };
33
+ searchRef: import("react").RefObject<SourceSearchSnapshot>;
34
+ stepSearch: (delta: number) => {
35
+ count: number;
36
+ activeIndex: number;
37
+ };
38
+ };
39
+ export {};