@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.
- package/CHANGELOG.md +11 -0
- package/LICENSE-APACHE +201 -0
- package/LICENSE-MIT +21 -0
- package/README.md +74 -2
- package/dist/app/App.d.ts +1 -0
- package/dist/app/App.js +6 -0
- package/dist/app/AppDialogs.d.ts +52 -0
- package/dist/app/AppDialogs.js +23 -0
- package/dist/app/AppDialogsHost.d.ts +31 -0
- package/dist/app/AppDialogsHost.js +30 -0
- package/dist/app/AppLifecycle.d.ts +19 -0
- package/dist/app/AppLifecycle.js +87 -0
- package/dist/app/AppProvider.d.ts +317 -0
- package/dist/app/AppProvider.js +217 -0
- package/dist/app/AppRoot.d.ts +8 -0
- package/dist/app/AppRoot.js +159 -0
- package/dist/app/AppShell.d.ts +2 -0
- package/dist/app/AppShell.js +81 -0
- package/dist/app/CommandPalette.d.ts +13 -0
- package/dist/app/CommandPalette.js +47 -0
- package/dist/app/DocActionBar.d.ts +36 -0
- package/dist/app/DocActionBar.js +300 -0
- package/dist/app/DocumentContextMenu.d.ts +29 -0
- package/dist/app/DocumentContextMenu.js +122 -0
- package/dist/app/DocumentPane.d.ts +52 -0
- package/dist/app/DocumentPane.js +30 -0
- package/dist/app/DocumentRouterTable.d.ts +58 -0
- package/dist/app/DocumentRouterTable.js +143 -0
- package/dist/app/ErrorBoundary.d.ts +12 -0
- package/dist/app/ErrorBoundary.js +16 -0
- package/dist/app/FindBar.d.ts +12 -0
- package/dist/app/FindBar.js +199 -0
- package/dist/app/LiveTitle.d.ts +14 -0
- package/dist/app/LiveTitle.js +14 -0
- package/dist/app/OpeningProgress.d.ts +13 -0
- package/dist/app/OpeningProgress.js +21 -0
- package/dist/app/PrintPreviewWindow.d.ts +2 -0
- package/dist/app/PrintPreviewWindow.js +57 -0
- package/dist/app/ShortcutHelp.d.ts +3 -0
- package/dist/app/ShortcutHelp.js +99 -0
- package/dist/app/Sidebar.d.ts +36 -0
- package/dist/app/Sidebar.js +121 -0
- package/dist/app/ToastViewport.d.ts +1 -0
- package/dist/app/ToastViewport.js +31 -0
- package/dist/app/Toolbar.d.ts +26 -0
- package/dist/app/Toolbar.js +63 -0
- package/dist/app/activeFormatSavers.d.ts +7 -0
- package/dist/app/activeFormatSavers.js +4 -0
- package/dist/app/clipboardPaths.d.ts +1 -0
- package/dist/app/clipboardPaths.js +31 -0
- package/dist/app/documentDrafts.d.ts +17 -0
- package/dist/app/documentDrafts.js +104 -0
- package/dist/app/draftResolution.d.ts +5 -0
- package/dist/app/draftResolution.js +25 -0
- package/dist/app/markdownPaletteActions.d.ts +8 -0
- package/dist/app/markdownPaletteActions.js +107 -0
- package/dist/app/resourceCensusDev.d.ts +41 -0
- package/dist/app/resourceCensusDev.js +518 -0
- package/dist/app/setupRecovery.d.ts +34 -0
- package/dist/app/setupRecovery.js +70 -0
- package/dist/app/shell/DocStatsPill.d.ts +18 -0
- package/dist/app/shell/DocStatsPill.js +32 -0
- package/dist/app/shell/DocumentTools.d.ts +9 -0
- package/dist/app/shell/DocumentTools.js +73 -0
- package/dist/app/shell/InfoInspector.d.ts +9 -0
- package/dist/app/shell/InfoInspector.js +67 -0
- package/dist/app/shell/StartPage.d.ts +9 -0
- package/dist/app/shell/StartPage.js +49 -0
- package/dist/app/shell/readEdit.d.ts +6 -0
- package/dist/app/shell/readEdit.js +6 -0
- package/dist/app/sidebar/SidebarDocumentMenu.d.ts +20 -0
- package/dist/app/sidebar/SidebarDocumentMenu.js +56 -0
- package/dist/app/sidebar/SidebarExpandedPanel.d.ts +9 -0
- package/dist/app/sidebar/SidebarExpandedPanel.js +78 -0
- package/dist/app/sidebar/SidebarIcons.d.ts +3 -0
- package/dist/app/sidebar/SidebarIcons.js +18 -0
- package/dist/app/sidebar/SidebarMenus.d.ts +19 -0
- package/dist/app/sidebar/SidebarMenus.js +64 -0
- package/dist/app/sidebar/SidebarPathUtils.d.ts +10 -0
- package/dist/app/sidebar/SidebarPathUtils.js +55 -0
- package/dist/app/sidebar/SidebarPrimitives.d.ts +12 -0
- package/dist/app/sidebar/SidebarPrimitives.js +45 -0
- package/dist/app/sidebar/SidebarRail.d.ts +5 -0
- package/dist/app/sidebar/SidebarRail.js +37 -0
- package/dist/app/sidebar/explorer/ExplorerRowMenu.d.ts +21 -0
- package/dist/app/sidebar/explorer/ExplorerRowMenu.js +37 -0
- package/dist/app/sidebar/explorer/ExplorerSections.d.ts +16 -0
- package/dist/app/sidebar/explorer/ExplorerSections.js +259 -0
- package/dist/app/sidebar/explorer/ExplorerTree.d.ts +46 -0
- package/dist/app/sidebar/explorer/ExplorerTree.js +387 -0
- package/dist/app/sidebar/explorer/explorerState.d.ts +37 -0
- package/dist/app/sidebar/explorer/explorerState.js +135 -0
- package/dist/app/tabMountPolicy.d.ts +45 -0
- package/dist/app/tabMountPolicy.js +104 -0
- package/dist/app/tabWarmOrder.d.ts +7 -0
- package/dist/app/tabWarmOrder.js +48 -0
- package/dist/app/tabs/TabFuseCurves.d.ts +3 -0
- package/dist/app/tabs/TabFuseCurves.js +15 -0
- package/dist/app/tabs/TabOverview.d.ts +24 -0
- package/dist/app/tabs/TabOverview.js +121 -0
- package/dist/app/tabs/TabStrip.d.ts +42 -0
- package/dist/app/tabs/TabStrip.js +380 -0
- package/dist/app/tabs/tabIds.d.ts +5 -0
- package/dist/app/tabs/tabIds.js +7 -0
- package/dist/app/tabs/useTabDirty.d.ts +3 -0
- package/dist/app/tabs/useTabDirty.js +11 -0
- package/dist/app/useAppChromeState.d.ts +21 -0
- package/dist/app/useAppChromeState.js +48 -0
- package/dist/app/useAppCommands.d.ts +66 -0
- package/dist/app/useAppCommands.js +71 -0
- package/dist/app/useAppDocumentCore.d.ts +210 -0
- package/dist/app/useAppDocumentCore.js +40 -0
- package/dist/app/useAppEffects.d.ts +18 -0
- package/dist/app/useAppEffects.js +52 -0
- package/dist/app/useAppPaneSearch.d.ts +37 -0
- package/dist/app/useAppPaneSearch.js +74 -0
- package/dist/app/useAppShellSetup.d.ts +40 -0
- package/dist/app/useAppShellSetup.js +52 -0
- package/dist/app/useAppSlots.d.ts +28 -0
- package/dist/app/useAppSlots.js +40 -0
- package/dist/app/useAppTabChrome.d.ts +51 -0
- package/dist/app/useAppTabChrome.js +66 -0
- package/dist/app/useAutoSave.d.ts +34 -0
- package/dist/app/useAutoSave.js +116 -0
- package/dist/app/useCloseAll.d.ts +52 -0
- package/dist/app/useCloseAll.js +295 -0
- package/dist/app/useClosedTabEviction.d.ts +10 -0
- package/dist/app/useClosedTabEviction.js +38 -0
- package/dist/app/useCommandPalette.d.ts +44 -0
- package/dist/app/useCommandPalette.js +254 -0
- package/dist/app/useCrossDocSearch.d.ts +7 -0
- package/dist/app/useCrossDocSearch.js +94 -0
- package/dist/app/useDistractionFree.d.ts +21 -0
- package/dist/app/useDistractionFree.js +73 -0
- package/dist/app/useDocumentCreation.d.ts +18 -0
- package/dist/app/useDocumentCreation.js +63 -0
- package/dist/app/useDocumentDirty.d.ts +31 -0
- package/dist/app/useDocumentDirty.js +46 -0
- package/dist/app/useDocumentLoader.d.ts +55 -0
- package/dist/app/useDocumentLoader.js +267 -0
- package/dist/app/useDocumentOutputActions.d.ts +6 -0
- package/dist/app/useDocumentOutputActions.js +45 -0
- package/dist/app/useDocumentRuntimeEffects.d.ts +36 -0
- package/dist/app/useDocumentRuntimeEffects.js +458 -0
- package/dist/app/useDocumentSaver.d.ts +58 -0
- package/dist/app/useDocumentSaver.js +511 -0
- package/dist/app/useDocumentTabs.d.ts +41 -0
- package/dist/app/useDocumentTabs.js +207 -0
- package/dist/app/useDocumentWatcher.d.ts +83 -0
- package/dist/app/useDocumentWatcher.js +393 -0
- package/dist/app/useDocumentZoom.d.ts +24 -0
- package/dist/app/useDocumentZoom.js +104 -0
- package/dist/app/useDocuments.d.ts +169 -0
- package/dist/app/useDocuments.js +455 -0
- package/dist/app/useDraftConfirmGate.d.ts +27 -0
- package/dist/app/useDraftConfirmGate.js +47 -0
- package/dist/app/useFileDragOverlay.d.ts +12 -0
- package/dist/app/useFileDragOverlay.js +49 -0
- package/dist/app/useFormatApis.d.ts +13 -0
- package/dist/app/useFormatApis.js +62 -0
- package/dist/app/useFormatDirtyState.d.ts +29 -0
- package/dist/app/useFormatDirtyState.js +81 -0
- package/dist/app/useGlobalShortcuts.d.ts +47 -0
- package/dist/app/useGlobalShortcuts.js +359 -0
- package/dist/app/useHtmlSession.d.ts +68 -0
- package/dist/app/useHtmlSession.js +463 -0
- package/dist/app/useHtmlSessionProps.d.ts +33 -0
- package/dist/app/useHtmlSessionProps.js +57 -0
- package/dist/app/useLibrary.d.ts +21 -0
- package/dist/app/useLibrary.js +81 -0
- package/dist/app/useOpenFromClipboard.d.ts +7 -0
- package/dist/app/useOpenFromClipboard.js +44 -0
- package/dist/app/usePaneLiveness.d.ts +29 -0
- package/dist/app/usePaneLiveness.js +199 -0
- package/dist/app/usePendingSavePaths.d.ts +19 -0
- package/dist/app/usePendingSavePaths.js +67 -0
- package/dist/app/useReadyShow.d.ts +12 -0
- package/dist/app/useReadyShow.js +142 -0
- package/dist/app/useSearchController.d.ts +22 -0
- package/dist/app/useSearchController.js +114 -0
- package/dist/app/useSetupRecovery.d.ts +41 -0
- package/dist/app/useSetupRecovery.js +153 -0
- package/dist/app/useTabChrome.d.ts +40 -0
- package/dist/app/useTabChrome.js +115 -0
- package/dist/app/useViewState.d.ts +27 -0
- package/dist/app/useViewState.js +160 -0
- package/dist/appName.d.ts +2 -0
- package/dist/appName.js +8 -0
- package/dist/documents/DocumentRouter.d.ts +102 -0
- package/dist/documents/DocumentRouter.js +153 -0
- package/dist/documents/FileTypeIcon.d.ts +6 -0
- package/dist/documents/FileTypeIcon.js +93 -0
- package/dist/documents/ImageDocument.d.ts +14 -0
- package/dist/documents/ImageDocument.js +61 -0
- package/dist/documents/capabilities.d.ts +17 -0
- package/dist/documents/capabilities.js +18 -0
- package/dist/documents/html/HtmlDocument.d.ts +15 -0
- package/dist/documents/html/HtmlDocument.js +584 -0
- package/dist/documents/html/HtmlDocumentLayout.d.ts +87 -0
- package/dist/documents/html/HtmlDocumentLayout.js +25 -0
- package/dist/documents/html/HtmlDocumentProps.d.ts +60 -0
- package/dist/documents/html/HtmlDocumentProps.js +1 -0
- package/dist/documents/html/HtmlDocumentSource.d.ts +34 -0
- package/dist/documents/html/HtmlDocumentSource.js +28 -0
- package/dist/documents/markdown/MarkdownSplitPane.d.ts +115 -0
- package/dist/documents/markdown/MarkdownSplitPane.js +352 -0
- package/dist/documents/mdx/MdxSplitPane.d.ts +32 -0
- package/dist/documents/mdx/MdxSplitPane.js +60 -0
- package/dist/documents/txt/LinkDialog.d.ts +8 -0
- package/dist/documents/txt/LinkDialog.js +34 -0
- package/dist/documents/txt/MarkdownSourceToolbar.d.ts +8 -0
- package/dist/documents/txt/MarkdownSourceToolbar.js +25 -0
- package/dist/documents/txt/SourceEditorToolbar.d.ts +12 -0
- package/dist/documents/txt/SourceEditorToolbar.js +4 -0
- package/dist/documents/txt/TxtEditor.d.ts +39 -0
- package/dist/documents/txt/TxtEditor.js +186 -0
- package/dist/documents/txt/sourceEditorTypes.d.ts +49 -0
- package/dist/documents/txt/sourceEditorTypes.js +1 -0
- package/dist/documents/txt/sourceExtensions.d.ts +38 -0
- package/dist/documents/txt/sourceExtensions.js +444 -0
- package/dist/documents/txt/sourceFormatting.d.ts +18 -0
- package/dist/documents/txt/sourceFormatting.js +183 -0
- package/dist/documents/txt/sourceTheme.d.ts +5 -0
- package/dist/documents/txt/sourceTheme.js +220 -0
- package/dist/documents/txt/useCodeMirrorSourceEditor.d.ts +69 -0
- package/dist/documents/txt/useCodeMirrorSourceEditor.js +546 -0
- package/dist/documents/txt/useSourceSearch.d.ts +39 -0
- package/dist/documents/txt/useSourceSearch.js +122 -0
- package/dist/documents/txt/useTextEditorCommands.d.ts +26 -0
- package/dist/documents/txt/useTextEditorCommands.js +151 -0
- package/dist/documents/web/LivePreviewPane.d.ts +11 -0
- package/dist/documents/web/LivePreviewPane.js +58 -0
- package/dist/env.d.ts +66 -0
- package/dist/index.d.ts +121 -0
- package/dist/index.js +123 -0
- package/dist/settings/AppearanceMenu.d.ts +9 -0
- package/dist/settings/AppearanceMenu.js +54 -0
- package/dist/settings/SettingsPanel.d.ts +7 -0
- package/dist/settings/SettingsPanel.js +104 -0
- package/dist/settings/publishedSections.d.ts +22 -0
- package/dist/settings/publishedSections.js +79 -0
- package/dist/settings/sections/AboutSection.d.ts +1 -0
- package/dist/settings/sections/AboutSection.js +13 -0
- package/dist/settings/sections/DiagnosticsSection.d.ts +1 -0
- package/dist/settings/sections/DiagnosticsSection.js +42 -0
- package/dist/settings/sections/PrivacySection.d.ts +3 -0
- package/dist/settings/sections/PrivacySection.js +51 -0
- package/dist/settings/sections/UpdateLicenseBlock.d.ts +10 -0
- package/dist/settings/sections/UpdateLicenseBlock.js +158 -0
- package/dist/state/explorer.d.ts +32 -0
- package/dist/state/explorer.js +111 -0
- package/dist/state/legal.d.ts +51 -0
- package/dist/state/legal.js +130 -0
- package/dist/state/library.d.ts +33 -0
- package/dist/state/library.js +110 -0
- package/dist/state/updateLicense.d.ts +31 -0
- package/dist/state/updateLicense.js +340 -0
- package/dist/styles/explorer.css +219 -0
- package/dist/styles/index.css +1477 -0
- package/dist/styles/tabs.css +467 -0
- package/dist/styles.css +6 -0
- package/package.json +88 -3
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import type { SearchProvider } from "@rightkit/document-core/documents/types";
|
|
3
|
+
import { type DocumentSearchMode } from "@rightkit/document-core/documents/DocumentToolbarFind";
|
|
4
|
+
interface Props {
|
|
5
|
+
text: string;
|
|
6
|
+
documentPath?: string;
|
|
7
|
+
subscribeEdits?: (path: string, cb: () => void) => () => void;
|
|
8
|
+
getEditSnapshot?: (path: string) => string | undefined;
|
|
9
|
+
devMode?: boolean;
|
|
10
|
+
active?: boolean;
|
|
11
|
+
showSource: boolean;
|
|
12
|
+
/** Wave 2 Read mode: the MDX preview is inherently read-only (sandboxed iframe); the flag only
|
|
13
|
+
* forces the source drawer closed — the drawer is the editing surface. */
|
|
14
|
+
readOnly?: boolean;
|
|
15
|
+
onChange: (text: string) => void;
|
|
16
|
+
onSave: () => void;
|
|
17
|
+
registerSearchProvider: (provider: SearchProvider | null) => void;
|
|
18
|
+
findBar?: ReactNode;
|
|
19
|
+
findMode?: DocumentSearchMode;
|
|
20
|
+
onRequestFind?: () => void;
|
|
21
|
+
onRequestReplace?: () => void;
|
|
22
|
+
/** Close the shared find bar when the rendered/source surface switches. */
|
|
23
|
+
onCloseFind?: () => void;
|
|
24
|
+
onOpenPath?: (path: string, opts?: {
|
|
25
|
+
background?: boolean;
|
|
26
|
+
}) => void;
|
|
27
|
+
/** Reports every grant minted for this document's compile-time imports + in-preview link clicks,
|
|
28
|
+
* so the owning tab can revoke them on close. */
|
|
29
|
+
onImportGranted?: (entryPath: string, grantedPath: string) => void;
|
|
30
|
+
}
|
|
31
|
+
export declare function MdxSplitPane({ text, documentPath, subscribeEdits, getEditSnapshot, showSource, onChange, onSave, registerSearchProvider, findBar, findMode, onRequestFind, onRequestReplace, onCloseFind, onOpenPath, onImportGranted, readOnly, active, }: Props): import("react").JSX.Element;
|
|
32
|
+
export {};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// MdxSplitPane: the MDX branch of the markdown/mdx split pane. Mirrors MarkdownSplitPane
|
|
3
|
+
// structurally — same source drawer (CodeMirror markdown mode + markdownToolbar), same
|
|
4
|
+
// find-provider-publish pattern, same source-fullscreen / focus / typewriter, same outline.
|
|
5
|
+
//
|
|
6
|
+
// The preview slot is the only difference: instead of mounting <MarkdownView> in the app's
|
|
7
|
+
// React tree, it mounts <MdxPreviewFrame> — a sandboxed iframe (sandbox="allow-scripts", never
|
|
8
|
+
// allow-same-origin) where the compiled MDX renders. The duplication vs MarkdownSplitPane is
|
|
9
|
+
// intentional: each format owns its own split pane so changes to one don't accidentally affect
|
|
10
|
+
// the other. The shared code (TextEditor, MarkdownSourceToolbar, MarkdownSourceActions, etc.)
|
|
11
|
+
// lives in the markdown/ folder; this file reuses them by import.
|
|
12
|
+
import { useCloseFindOnSurfaceChange } from "@rightkit/document-core/app/useCloseFindOnSurfaceChange";
|
|
13
|
+
import { useCallback, useEffect, useRef } from "react";
|
|
14
|
+
import { useEditText } from "@rightkit/document-core/app/useEditText";
|
|
15
|
+
import { useMirroredEditText } from "@rightkit/document-core/documents/useMirroredEditText";
|
|
16
|
+
import { useSplitResize } from "@rightkit/document-core/documents/useSplitResize";
|
|
17
|
+
import { MarkdownSourceDrawer, useMarkdownSourceDrawer } from "../markdown/MarkdownSplitPane.js";
|
|
18
|
+
import { MdxPreviewFrame } from "@rightkit/markdown-editor/documents/mdx/MdxPreviewFrame";
|
|
19
|
+
import { DocumentToolbarSearch } from "@rightkit/document-core/documents/DocumentToolbarFind";
|
|
20
|
+
const noopSubscribeEdits = (_path, _cb) => () => { };
|
|
21
|
+
const noopEditSnapshot = (_path) => undefined;
|
|
22
|
+
export function MdxSplitPane({ text, documentPath, subscribeEdits, getEditSnapshot, showSource, onChange, onSave, registerSearchProvider, findBar, findMode, onRequestFind, onRequestReplace, onCloseFind, onOpenPath, onImportGranted, readOnly = false, active = true, }) {
|
|
23
|
+
const liveText = useEditText(subscribeEdits ?? noopSubscribeEdits, getEditSnapshot ?? noopEditSnapshot, documentPath ?? "");
|
|
24
|
+
const [localText, setLocalText] = useMirroredEditText(liveText, text);
|
|
25
|
+
const handleChange = useCallback((next) => {
|
|
26
|
+
setLocalText(next);
|
|
27
|
+
onChange(next);
|
|
28
|
+
}, [onChange, setLocalText]);
|
|
29
|
+
const renderedText = localText;
|
|
30
|
+
const containerRef = useRef(null);
|
|
31
|
+
const { sourceWidth, setSourceWidth, sourceFullscreen, drawerProps } = useMarkdownSourceDrawer(renderedText, undefined, undefined, undefined, documentPath);
|
|
32
|
+
// The active surface owns find: the source editor while the drawer is open, else the MDX
|
|
33
|
+
// preview. publish() exposes whichever is active so neither child's register/unregister can
|
|
34
|
+
// clobber the other during the toggle. Same invariant as MarkdownSplitPane.tsx:54-64.
|
|
35
|
+
const previewProviderRef = useRef(null);
|
|
36
|
+
const sourceProviderRef = useRef(null);
|
|
37
|
+
// Read mode keeps the drawer closed even if a persisted view asks for "source" — the drawer is
|
|
38
|
+
// the editing surface, and Read never exposes one.
|
|
39
|
+
const drawer = showSource && !readOnly;
|
|
40
|
+
useCloseFindOnSurfaceChange(drawer, !!findMode, onCloseFind);
|
|
41
|
+
const showSourceRef = useRef(drawer);
|
|
42
|
+
showSourceRef.current = drawer;
|
|
43
|
+
const publish = useCallback(() => {
|
|
44
|
+
registerSearchProvider(showSourceRef.current ? sourceProviderRef.current : previewProviderRef.current);
|
|
45
|
+
}, [registerSearchProvider]);
|
|
46
|
+
const registerPreviewProvider = useCallback((p) => {
|
|
47
|
+
previewProviderRef.current = p;
|
|
48
|
+
publish();
|
|
49
|
+
}, [publish]);
|
|
50
|
+
const registerSourceProvider = useCallback((p) => {
|
|
51
|
+
sourceProviderRef.current = p;
|
|
52
|
+
publish();
|
|
53
|
+
}, [publish]);
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
publish();
|
|
56
|
+
}, [publish]);
|
|
57
|
+
useEffect(() => () => registerSearchProvider(null), [registerSearchProvider]);
|
|
58
|
+
const startResize = useSplitResize(containerRef, setSourceWidth);
|
|
59
|
+
return (_jsxs("div", { ref: containerRef, className: "flex h-full min-h-0 min-w-0 bg-ink", children: [!(drawer && sourceFullscreen) && (_jsxs("section", { className: "relative flex min-h-0 min-w-0 flex-1 flex-col", children: [!drawer && findBar ? (_jsx("div", { className: "no-print vr-chrome vr-document-toolbar vr-document-toolbar-search-layout relative shrink-0 border-b border-line bg-panel-2 text-muted", children: _jsx(DocumentToolbarSearch, { findBar: findBar, activeMode: findMode, onRequestFind: onRequestFind, onRequestReplace: undefined }) })) : null, _jsx("div", { "data-document-scroll": true, className: "min-h-0 flex-1 overflow-auto", children: _jsx(MdxPreviewFrame, { source: renderedText, documentPath: documentPath ?? "", onOpenPath: onOpenPath, registerSearchProvider: registerPreviewProvider, onImportGranted: onImportGranted, active: active, printPreview: !drawer }) })] })), drawer && !sourceFullscreen && (_jsx("div", { onPointerDown: startResize, role: "separator", tabIndex: 0, "aria-orientation": "vertical", "aria-valuemin": 30, "aria-valuenow": sourceWidth, "aria-label": "Resize MDX source drawer", className: "w-1.5 shrink-0 cursor-col-resize bg-line transition-colors hover:bg-copper/60" })), drawer && (_jsx(MarkdownSourceDrawer, { documentPath: documentPath, text: renderedText, onChange: handleChange, onSave: onSave, registerSourceProvider: registerSourceProvider, findBar: findBar, findMode: findMode, onRequestFind: onRequestFind, onRequestReplace: onRequestReplace, ...drawerProps, restoreTitle: "Restore MDX split", expandTitle: "Expand MDX source", className: `relative flex min-h-0 min-w-0 flex-col border-l border-line bg-panel ${sourceFullscreen ? "flex-1" : "shrink-0"}` }))] }));
|
|
60
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare function LinkDialog({ initialText, initialUrl, onInsert, onCancel, onRemove, }: {
|
|
2
|
+
initialText: string;
|
|
3
|
+
initialUrl?: string;
|
|
4
|
+
onInsert: (text: string, url: string) => void;
|
|
5
|
+
onCancel: () => void;
|
|
6
|
+
/** Provided when editing an existing link — shows a "Remove link" button. */
|
|
7
|
+
onRemove?: () => void;
|
|
8
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef, useState } from "react";
|
|
3
|
+
import { ActionDialog } from "@rightkit/react-shell/react";
|
|
4
|
+
import { DIALOG_SLOTS, cancelAction, dangerAction, primaryAction } from "@rightkit/document-core/app/chrome";
|
|
5
|
+
// Notepad/Word-style link dialog: Display text + Address, shared by source editors so link
|
|
6
|
+
// insertion is consistent across formats. Keyboard: autofocus, Enter inserts, Esc cancels. The
|
|
7
|
+
// caller restores the saved selection and performs the actual insert in onInsert.
|
|
8
|
+
export function LinkDialog({ initialText, initialUrl = "", onInsert, onCancel, onRemove, }) {
|
|
9
|
+
const [text, setText] = useState(initialText);
|
|
10
|
+
const [url, setUrl] = useState(initialUrl);
|
|
11
|
+
const textRef = useRef(null);
|
|
12
|
+
const urlRef = useRef(null);
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
// If text is already selected, jump straight to the address; otherwise start with display text.
|
|
15
|
+
(initialText ? urlRef.current : textRef.current)?.focus();
|
|
16
|
+
}, [initialText]);
|
|
17
|
+
const submit = () => {
|
|
18
|
+
const u = url.trim();
|
|
19
|
+
if (!u)
|
|
20
|
+
return;
|
|
21
|
+
onInsert(text.trim() || u, u); // insert exactly as typed — no rewriting
|
|
22
|
+
};
|
|
23
|
+
const onKey = (e) => {
|
|
24
|
+
if (e.key === "Enter") {
|
|
25
|
+
e.preventDefault();
|
|
26
|
+
submit();
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
return (_jsxs(ActionDialog, { title: "Link", ariaLabel: "Link", onClose: onCancel, width: 420, autoFocus: false, slots: { ...DIALOG_SLOTS, header: `mb-4 ${DIALOG_SLOTS.header}` }, actions: [
|
|
30
|
+
...(onRemove ? [dangerAction("Remove link", onRemove)] : []),
|
|
31
|
+
cancelAction(onCancel),
|
|
32
|
+
{ ...primaryAction(onRemove ? "Update" : "Insert", submit), disabled: !url.trim() },
|
|
33
|
+
], children: [_jsx("label", { htmlFor: "vr-link-text", className: "mb-1 block text-chrome-xs uppercase tracking-[0.15em] text-muted-2", children: "Display text" }), _jsx("input", { id: "vr-link-text", ref: textRef, value: text, onChange: (e) => setText(e.target.value), onKeyDown: onKey, placeholder: "Text to show", className: "mb-4 w-full rounded border border-line bg-ink px-3 py-1.5 text-sm text-paper outline-none focus:border-copper" }), _jsx("label", { htmlFor: "vr-link-url", className: "mb-1 block text-chrome-xs uppercase tracking-[0.15em] text-muted-2", children: "Address" }), _jsx("input", { id: "vr-link-url", ref: urlRef, value: url, onChange: (e) => setUrl(e.target.value), onKeyDown: onKey, placeholder: "https://example.com", className: "mb-5 w-full rounded border border-line bg-ink px-3 py-1.5 font-mono text-sm text-paper outline-none focus:border-copper" })] }));
|
|
34
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare function MarkdownSourceToolbar({ setHeading, wrapInline, linePrefix, openLink, insertBlock, formatTables, }: {
|
|
2
|
+
setHeading: (level: number) => void;
|
|
3
|
+
wrapInline: (before: string, after: string) => void;
|
|
4
|
+
linePrefix: (prefix: string) => void;
|
|
5
|
+
openLink: (prefix: "[" | "![") => void;
|
|
6
|
+
insertBlock: (block: string, caretOffset?: number) => void;
|
|
7
|
+
formatTables: () => void;
|
|
8
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { TBtn, Sep, Group, Dropdown, Item } from "@rightkit/document-core/documents/toolbarPrimitives";
|
|
3
|
+
import { DIAGRAM_TEMPLATES, insertTableTemplate, mermaidBlock } from "@rightkit/markdown-editor/documents/markdown/markdownTools";
|
|
4
|
+
import { shortcutLabel } from "@rightkit/document-core/platform";
|
|
5
|
+
const HEADINGS = [
|
|
6
|
+
{ label: "Paragraph", level: 0 },
|
|
7
|
+
{ label: "Heading 1", level: 1 },
|
|
8
|
+
{ label: "Heading 2", level: 2 },
|
|
9
|
+
{ label: "Heading 3", level: 3 },
|
|
10
|
+
{ label: "Heading 4", level: 4 },
|
|
11
|
+
{ label: "Heading 5", level: 5 },
|
|
12
|
+
{ label: "Heading 6", level: 6 },
|
|
13
|
+
];
|
|
14
|
+
export function MarkdownSourceToolbar({ setHeading, wrapInline, linePrefix, openLink, insertBlock, formatTables, }) {
|
|
15
|
+
return (_jsxs(_Fragment, { children: [_jsx(Dropdown, { label: "Heading", title: "Paragraph style", minWidth: 92, panelWidth: 130, children: (close) => HEADINGS.map((h) => (_jsx(Item, { onRun: () => {
|
|
16
|
+
setHeading(h.level);
|
|
17
|
+
close();
|
|
18
|
+
}, children: h.label }, h.level))) }), _jsx(Sep, {}), _jsxs(Group, { children: [_jsx(TBtn, { label: "B", title: `Bold (${shortcutLabel("mod+b")})`, bold: true, onRun: () => wrapInline("**", "**") }), _jsx(TBtn, { label: "I", title: `Italic (${shortcutLabel("mod+i")})`, italic: true, onRun: () => wrapInline("*", "*") }), _jsx(TBtn, { label: "S", title: "Strikethrough", strike: true, onRun: () => wrapInline("~~", "~~") }), _jsx(TBtn, { label: "H", title: "Highlight", onRun: () => wrapInline("==", "==") }), _jsx(TBtn, { label: "<", title: "Inline code", width: 32, onRun: () => wrapInline("`", "`") })] }), _jsx(Sep, {}), _jsxs(Group, { children: [_jsx(TBtn, { label: "\u2022", title: "Bulleted list", onRun: () => linePrefix("- ") }), _jsx(TBtn, { label: "1.", title: "Numbered list", onRun: () => linePrefix("1. ") }), _jsx(TBtn, { label: "\u2611", title: "Task list", onRun: () => linePrefix("- [ ] ") }), _jsx(TBtn, { label: "\u275D", title: "Blockquote", onRun: () => linePrefix("> ") })] }), _jsx(Sep, {}), _jsxs(Group, { children: [_jsx(TBtn, { label: "link", title: `Link (${shortcutLabel("mod+k")})`, width: 40, onRun: () => openLink("[") }), _jsx(TBtn, { label: "[[", title: "Wiki link", width: 32, onRun: () => wrapInline("[[", "]]") }), _jsx(TBtn, { label: "img", title: "Image", width: 36, onRun: () => openLink("![") })] }), _jsx(Sep, {}), _jsxs(Group, { children: [_jsx(TBtn, { label: "\u25A6", title: "Insert table", onRun: () => {
|
|
19
|
+
const table = insertTableTemplate(3, 3);
|
|
20
|
+
insertBlock(table, table.length);
|
|
21
|
+
} }), _jsx(TBtn, { label: "fmt|", title: "Format tables", width: 42, onRun: formatTables }), _jsx(TBtn, { label: "\u2015", title: "Horizontal rule", onRun: () => insertBlock("---\n") }), _jsx(TBtn, { label: "{ }", title: "Code block", width: 32, onRun: () => insertBlock("```\n\n```\n", 4) })] }), _jsx(Sep, {}), _jsxs(Group, { children: [_jsx(TBtn, { label: "toc", title: "Insert table of contents", width: 38, onRun: () => insertBlock("[toc]\n\n") }), _jsx(Dropdown, { label: "diagram", title: "Insert Mermaid diagram", minWidth: 74, panelWidth: 158, children: (close) => DIAGRAM_TEMPLATES.map((template) => (_jsx(Item, { onRun: () => {
|
|
22
|
+
insertBlock(mermaidBlock(template.code));
|
|
23
|
+
close();
|
|
24
|
+
}, children: template.label }, template.label))) })] })] }));
|
|
25
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
export declare function SourceEditorToolbar({ folded, showInvisibles, actions, markdownControls, onFormat, onToggleFold, onGoToLine, onToggleInvisibles, }: {
|
|
3
|
+
folded: boolean;
|
|
4
|
+
showInvisibles: boolean;
|
|
5
|
+
actions?: ReactNode;
|
|
6
|
+
/** Markdown formatting controls (heading, bold, lists, ...), flowed into the same row. */
|
|
7
|
+
markdownControls?: ReactNode;
|
|
8
|
+
onFormat: () => void;
|
|
9
|
+
onToggleFold: () => void;
|
|
10
|
+
onGoToLine: () => void;
|
|
11
|
+
onToggleInvisibles: () => void;
|
|
12
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
export function SourceEditorToolbar({ folded, showInvisibles, actions, markdownControls, onFormat, onToggleFold, onGoToLine, onToggleInvisibles, }) {
|
|
3
|
+
return (_jsxs("div", { className: "no-print vr-chrome vr-capsule-scroll flex min-h-9 min-w-0 flex-nowrap items-center gap-1 whitespace-nowrap px-1 text-chrome-xs text-muted", children: [_jsx("button", { type: "button", title: "Format source", "aria-label": "Format source", onClick: onFormat, className: "vr-press vr-focus rounded px-2 py-1 hover:bg-panel-2 hover:text-paper", children: "fmt" }), _jsx("span", { className: "mx-1 h-5 w-px bg-line" }), _jsx("button", { type: "button", title: folded ? "Unfold all" : "Fold all", "aria-label": folded ? "Unfold all" : "Fold all", "aria-pressed": folded, onClick: onToggleFold, className: `vr-press vr-focus rounded px-2 py-1 hover:bg-panel-2 hover:text-paper ${folded ? "text-copper-hi" : ""}`, children: folded ? "unfold" : "fold" }), _jsx("button", { type: "button", title: "Go to line", "aria-label": "Go to line", onClick: onGoToLine, className: "vr-press vr-focus rounded px-2 py-1 hover:bg-panel-2 hover:text-paper", children: "line" }), _jsx("span", { className: "mx-1 h-5 w-px bg-line" }), _jsx("button", { type: "button", title: showInvisibles ? "Hide invisible characters" : "Show invisible characters", "aria-label": showInvisibles ? "Hide invisible characters" : "Show invisible characters", "aria-pressed": showInvisibles, onClick: onToggleInvisibles, className: `vr-press vr-focus rounded px-2 py-1 hover:bg-panel-2 hover:text-paper ${showInvisibles ? "text-copper-hi" : ""}`, children: "marks" }), markdownControls && (_jsxs(_Fragment, { children: [_jsx("span", { className: "mx-1 h-5 w-px bg-line" }), markdownControls] })), actions && (_jsxs(_Fragment, { children: [_jsx("div", { className: "min-w-2 flex-1" }), _jsx("div", { className: "flex shrink-0 items-center gap-1", children: actions })] }))] }));
|
|
4
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import type { EditorView } from "@codemirror/view";
|
|
3
|
+
import type { SearchProvider } from "@rightkit/document-core/documents/types";
|
|
4
|
+
import type { SourceLanguage } from "./sourceFormatting.js";
|
|
5
|
+
export { applyInlineWrap, buildImageEmbed, buildMarkdownLink, formatHtmlSource, formatSource, sourceDiagnostics, } from "./sourceFormatting.js";
|
|
6
|
+
import { type DocumentSearchMode } from "@rightkit/document-core/documents/DocumentToolbarFind";
|
|
7
|
+
import type { SourceEditorViewState, TextEditorProps } from "./sourceEditorTypes.js";
|
|
8
|
+
export type { SourceEditorViewState } from "./sourceEditorTypes.js";
|
|
9
|
+
interface Props extends TextEditorProps {
|
|
10
|
+
text: string;
|
|
11
|
+
onChange: (text: string) => void;
|
|
12
|
+
onSave: () => void;
|
|
13
|
+
/** Mono for source/code editing; reading font for plain txt. */
|
|
14
|
+
mono?: boolean;
|
|
15
|
+
/** Show the markdown formatting bar (txt + md/mdx source). Parity with new Notepad / md editors. */
|
|
16
|
+
markdownToolbar?: boolean;
|
|
17
|
+
/** Read-only source view — no editing or replace (e.g. navigated HTML sub-pages that aren't the entry file). */
|
|
18
|
+
readOnly?: boolean;
|
|
19
|
+
/** Source language profile. Markdown gets toolbar-aware shortcuts; HTML gets HTML parsing; plain is for TXT. */
|
|
20
|
+
language?: SourceLanguage;
|
|
21
|
+
registerSearchProvider: (provider: SearchProvider | null) => void;
|
|
22
|
+
/** Shared FindBar element placed in source toolbar chrome. */
|
|
23
|
+
findBar?: ReactNode;
|
|
24
|
+
findMode?: DocumentSearchMode;
|
|
25
|
+
onRequestFind?: () => void;
|
|
26
|
+
onRequestReplace?: () => void;
|
|
27
|
+
/** Optional right-side controls rendered in the source toolbar. */
|
|
28
|
+
sourceToolbarActions?: ReactNode;
|
|
29
|
+
initialViewState?: SourceEditorViewState | null;
|
|
30
|
+
onViewStateChange?: (state: SourceEditorViewState) => void;
|
|
31
|
+
onEditorReady?: (view: EditorView | null) => void;
|
|
32
|
+
onSourceScrollLine?: (line: number) => void;
|
|
33
|
+
autoFocus?: boolean;
|
|
34
|
+
sourceFocusMode?: boolean;
|
|
35
|
+
sourceTypewriterMode?: boolean;
|
|
36
|
+
active?: boolean;
|
|
37
|
+
markdownCore?: import("@rightkit/markdown-editor/documents/markdown/core/MarkdownDocumentCore").MarkdownDocumentCore;
|
|
38
|
+
}
|
|
39
|
+
export declare function TextEditor({ documentPath, text, onChange, onSave, mono, markdownToolbar, readOnly, dockToolbar, language, registerSearchProvider, findBar, findMode, onRequestFind, onRequestReplace, sourceToolbarActions, initialViewState, onViewStateChange, onEditorReady, onSourceScrollLine, autoFocus, sourceFocusMode, sourceTypewriterMode, externalHistory, active, markdownCore: explicitMarkdownCore, }: Props): import("react").JSX.Element;
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { lazy, Suspense, useEffect, useMemo, useRef, useState } from "react";
|
|
3
|
+
import { useSettings } from "@rightkit/document-core/settings/settings";
|
|
4
|
+
import { LinkDialog } from "./LinkDialog.js";
|
|
5
|
+
export { applyInlineWrap, buildImageEmbed, buildMarkdownLink, formatHtmlSource, formatSource, sourceDiagnostics, } from "./sourceFormatting.js";
|
|
6
|
+
import { viewRightTheme } from "./sourceTheme.js";
|
|
7
|
+
import { SourceEditorToolbar } from "./SourceEditorToolbar.js";
|
|
8
|
+
import { DocumentToolbarSearch } from "@rightkit/document-core/documents/DocumentToolbarFind";
|
|
9
|
+
import { useSourceSearch } from "./useSourceSearch.js";
|
|
10
|
+
import { useTextEditorCommands } from "./useTextEditorCommands.js";
|
|
11
|
+
import { useCodeMirrorSourceEditor } from "./useCodeMirrorSourceEditor.js";
|
|
12
|
+
import { printDocumentContent } 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 { DocBar } from "@rightkit/document-core/app/shell/docBar";
|
|
16
|
+
const MarkdownSourceToolbar = lazy(() => import("./MarkdownSourceToolbar.js").then((m) => ({ default: m.MarkdownSourceToolbar })));
|
|
17
|
+
export function TextEditor({ documentPath, text, onChange, onSave, mono = false, markdownToolbar = false, readOnly = false, dockToolbar = true, language = markdownToolbar ? "markdown" : "plain", registerSearchProvider, findBar, findMode, onRequestFind, onRequestReplace, sourceToolbarActions, initialViewState = null, onViewStateChange, onEditorReady, onSourceScrollLine, autoFocus = false, sourceFocusMode = false, sourceTypewriterMode = false, externalHistory = false, active = false, markdownCore: explicitMarkdownCore, }) {
|
|
18
|
+
const markdownCore = explicitMarkdownCore ?? null;
|
|
19
|
+
const coreHistory = language === "markdown" ? markdownCore : null;
|
|
20
|
+
const { settings } = useSettings();
|
|
21
|
+
const hostRef = useRef(null);
|
|
22
|
+
const viewRef = useRef(null);
|
|
23
|
+
const activeRef = useRef(active);
|
|
24
|
+
activeRef.current = active;
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
if (!active || language !== "plain")
|
|
27
|
+
return;
|
|
28
|
+
const onPrint = (event) => {
|
|
29
|
+
event.preventDefault();
|
|
30
|
+
void printDocumentContent({ kind: "plain-text", text: textRef.current })
|
|
31
|
+
.then((printed) => {
|
|
32
|
+
if (printed)
|
|
33
|
+
return;
|
|
34
|
+
logError("print:failed:txt");
|
|
35
|
+
toast("Couldn't open print dialog", "error");
|
|
36
|
+
})
|
|
37
|
+
.catch(() => {
|
|
38
|
+
logError("print:failed:txt");
|
|
39
|
+
toast("Couldn't open print dialog", "error");
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
window.addEventListener("viewright:print", onPrint);
|
|
43
|
+
return () => window.removeEventListener("viewright:print", onPrint);
|
|
44
|
+
}, [active, language]);
|
|
45
|
+
const allowNextTabFocusRef = useRef(false);
|
|
46
|
+
const suppressChangeRef = useRef(false);
|
|
47
|
+
const textRef = useRef(text);
|
|
48
|
+
const onChangeRef = useRef(onChange);
|
|
49
|
+
const onSaveRef = useRef(onSave);
|
|
50
|
+
const formatOnSaveRef = useRef(false);
|
|
51
|
+
const onViewStateChangeRef = useRef(onViewStateChange);
|
|
52
|
+
const onEditorReadyRef = useRef(onEditorReady);
|
|
53
|
+
const onSourceScrollLineRef = useRef(onSourceScrollLine);
|
|
54
|
+
const readOnlyRef = useRef(readOnly);
|
|
55
|
+
const registerSearchProviderRef = useRef(registerSearchProvider);
|
|
56
|
+
const registeredProviderRef = useRef(null);
|
|
57
|
+
const sourceWrap = true;
|
|
58
|
+
const [sourceFolded, setSourceFolded] = useState(false);
|
|
59
|
+
const [showInvisibles, setShowInvisibles] = useState(false);
|
|
60
|
+
const [goLineOpen, setGoLineOpen] = useState(false);
|
|
61
|
+
const [goLineValue, setGoLineValue] = useState("");
|
|
62
|
+
const useReadingFont = language === "plain" && !mono;
|
|
63
|
+
// Memoize the theme so the SAME extension instance flows into both the initial `themeCompartment.of`
|
|
64
|
+
// and the reconfigure effect. Rebuilding it per render churned CodeMirror's StyleModule (each
|
|
65
|
+
// EditorView.theme() mints a new generated class, and the editor could end up tagged with a class
|
|
66
|
+
// whose styles were already unmounted) — which silently dropped the source font-size/padding, so the
|
|
67
|
+
// editor fell back to the inherited 16px instead of var(--source-size). That nondeterministic size is
|
|
68
|
+
// the "font shift" seen on opening source.
|
|
69
|
+
const themeExtension = useMemo(() => viewRightTheme(language, useReadingFont), [language, useReadingFont]);
|
|
70
|
+
// Link/image insertion via the shared Notepad-style dialog (Display text + Address).
|
|
71
|
+
const [linkState, setLinkState] = useState(null);
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
onChangeRef.current = onChange;
|
|
74
|
+
}, [onChange]);
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
onSaveRef.current = onSave;
|
|
77
|
+
}, [onSave]);
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
formatOnSaveRef.current = settings.formatOnSave;
|
|
80
|
+
}, [settings.formatOnSave]);
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
onViewStateChangeRef.current = onViewStateChange;
|
|
83
|
+
}, [onViewStateChange]);
|
|
84
|
+
useEffect(() => {
|
|
85
|
+
onEditorReadyRef.current = onEditorReady;
|
|
86
|
+
}, [onEditorReady]);
|
|
87
|
+
useEffect(() => {
|
|
88
|
+
onSourceScrollLineRef.current = onSourceScrollLine;
|
|
89
|
+
}, [onSourceScrollLine]);
|
|
90
|
+
const { dispatchSearchHighlights, refreshSearch, replaceAndFind, runFind, searchRef, stepSearch, } = useSourceSearch({ viewRef, readOnlyRef });
|
|
91
|
+
const { formatFromEditor, formatTablesFromEditor, goToLine, insertBlock, insertLinkFromDialog: applyLinkFromDialog, linePrefix, openLink, saveFromEditor, setHeading, toggleFoldAll, wrapInline, } = useTextEditorCommands({
|
|
92
|
+
formatOnSaveRef,
|
|
93
|
+
language,
|
|
94
|
+
onSaveRef,
|
|
95
|
+
readOnlyRef,
|
|
96
|
+
setLinkState,
|
|
97
|
+
setSourceFolded,
|
|
98
|
+
sourceFolded,
|
|
99
|
+
viewRef,
|
|
100
|
+
});
|
|
101
|
+
const insertLinkFromDialog = (label, url) => {
|
|
102
|
+
const st = linkState;
|
|
103
|
+
setLinkState(null);
|
|
104
|
+
applyLinkFromDialog(st, label, url);
|
|
105
|
+
};
|
|
106
|
+
useCodeMirrorSourceEditor({
|
|
107
|
+
activeRef,
|
|
108
|
+
allowNextTabFocusRef,
|
|
109
|
+
autoFocus,
|
|
110
|
+
dispatchSearchHighlights,
|
|
111
|
+
documentPath,
|
|
112
|
+
externalHistory: externalHistory || Boolean(coreHistory),
|
|
113
|
+
formatFromEditor,
|
|
114
|
+
hostRef,
|
|
115
|
+
initialViewState,
|
|
116
|
+
language,
|
|
117
|
+
markdownToolbar,
|
|
118
|
+
onChangeRef,
|
|
119
|
+
onEditorReadyRef,
|
|
120
|
+
onSourceScrollLineRef,
|
|
121
|
+
onViewStateChangeRef,
|
|
122
|
+
openLink,
|
|
123
|
+
readOnly,
|
|
124
|
+
readOnlyRef,
|
|
125
|
+
registerSearchProvider,
|
|
126
|
+
registerSearchProviderRef,
|
|
127
|
+
registeredProviderRef,
|
|
128
|
+
refreshSearch,
|
|
129
|
+
replaceAndFind,
|
|
130
|
+
runFind,
|
|
131
|
+
saveFromEditor,
|
|
132
|
+
searchRef,
|
|
133
|
+
settingsSpellcheck: settings.spellcheck,
|
|
134
|
+
settingsWordWrap: settings.wordWrap,
|
|
135
|
+
showInvisibles,
|
|
136
|
+
sourceTypewriterMode,
|
|
137
|
+
sourceWrap,
|
|
138
|
+
stepSearch,
|
|
139
|
+
suppressChangeRef,
|
|
140
|
+
text,
|
|
141
|
+
textRef,
|
|
142
|
+
themeExtension,
|
|
143
|
+
viewRef,
|
|
144
|
+
wrapInline,
|
|
145
|
+
markdownCore: coreHistory,
|
|
146
|
+
});
|
|
147
|
+
// Inactive panes are hidden with `content-visibility: hidden`; when one is shown again CodeMirror
|
|
148
|
+
// re-measures from estimated line heights and lands on the wrong offset. Remember the offset the
|
|
149
|
+
// user left while active and put it back after the pane is visible again.
|
|
150
|
+
const activeScrollRef = useRef(null);
|
|
151
|
+
useEffect(() => {
|
|
152
|
+
const scroller = viewRef.current?.scrollDOM;
|
|
153
|
+
if (!active || !scroller)
|
|
154
|
+
return;
|
|
155
|
+
const saved = activeScrollRef.current;
|
|
156
|
+
const restore = () => {
|
|
157
|
+
if (!saved)
|
|
158
|
+
return;
|
|
159
|
+
scroller.scrollTop = saved.top;
|
|
160
|
+
scroller.scrollLeft = saved.left;
|
|
161
|
+
};
|
|
162
|
+
let frame = requestAnimationFrame(() => {
|
|
163
|
+
restore();
|
|
164
|
+
frame = requestAnimationFrame(restore);
|
|
165
|
+
});
|
|
166
|
+
const onScroll = () => {
|
|
167
|
+
activeScrollRef.current = { top: scroller.scrollTop, left: scroller.scrollLeft };
|
|
168
|
+
};
|
|
169
|
+
scroller.addEventListener("scroll", onScroll, { passive: true });
|
|
170
|
+
return () => {
|
|
171
|
+
cancelAnimationFrame(frame);
|
|
172
|
+
scroller.removeEventListener("scroll", onScroll);
|
|
173
|
+
};
|
|
174
|
+
}, [active]);
|
|
175
|
+
return (_jsxs(_Fragment, { children: [linkState && (_jsx(LinkDialog, { initialText: linkState.text, onInsert: insertLinkFromDialog, onCancel: () => setLinkState(null) })), _jsxs("div", { className: "relative flex h-full min-w-0 flex-col overflow-hidden", children: [language !== "plain" && dockToolbar && (_jsx(DocBar, { children: _jsx("div", { className: "vr-capsule h-[42px] min-w-0 px-1", children: _jsxs("div", { className: "vr-document-toolbar-search-layout flex min-w-0 items-center", children: [_jsx("div", { className: "vr-document-toolbar-main min-w-0", children: _jsx(SourceEditorToolbar, { folded: sourceFolded, showInvisibles: showInvisibles, actions: sourceToolbarActions, markdownControls: markdownToolbar ? (_jsx(Suspense, { fallback: null, children: _jsx(MarkdownSourceToolbar, { setHeading: setHeading, wrapInline: wrapInline, linePrefix: linePrefix, openLink: openLink, insertBlock: insertBlock, formatTables: formatTablesFromEditor }) })) : undefined, onFormat: formatFromEditor, onToggleFold: toggleFoldAll, onGoToLine: () => setGoLineOpen(true), onToggleInvisibles: () => setShowInvisibles((value) => !value) }) }), _jsx(DocumentToolbarSearch, { findBar: findBar, activeMode: findMode, onRequestFind: onRequestFind, onRequestReplace: onRequestReplace })] }) }) })), language !== "plain" && !dockToolbar && (_jsx("div", { className: "no-print vr-document-toolbar flex shrink-0 flex-col", children: _jsxs("div", { className: "vr-document-toolbar-search-layout", children: [_jsx("div", { className: "vr-document-toolbar-main min-w-0", children: _jsx(SourceEditorToolbar, { folded: sourceFolded, showInvisibles: showInvisibles, actions: sourceToolbarActions, markdownControls: markdownToolbar ? (_jsx(Suspense, { fallback: null, children: _jsx(MarkdownSourceToolbar, { setHeading: setHeading, wrapInline: wrapInline, linePrefix: linePrefix, openLink: openLink, insertBlock: insertBlock, formatTables: formatTablesFromEditor }) })) : undefined, onFormat: formatFromEditor, onToggleFold: toggleFoldAll, onGoToLine: () => setGoLineOpen(true), onToggleInvisibles: () => setShowInvisibles((value) => !value) }) }), _jsx(DocumentToolbarSearch, { findBar: findBar, activeMode: findMode, onRequestFind: onRequestFind, onRequestReplace: onRequestReplace })] }) })), _jsxs("div", { className: "relative min-h-0 min-w-0 flex-1 overflow-hidden", children: [language === "plain" && findBar && (_jsx("div", { className: "no-print vr-chrome absolute right-3 top-3 z-50 max-w-[calc(100%-1.5rem)]", children: findBar })), goLineOpen && (_jsxs("form", { className: "absolute right-3 top-3 z-50 flex items-center gap-2 rounded border border-line bg-panel-2 px-2 py-1 text-chrome-xs text-muted shadow-xl", onSubmit: (e) => {
|
|
176
|
+
e.preventDefault();
|
|
177
|
+
const line = Number.parseInt(goLineValue, 10);
|
|
178
|
+
if (Number.isFinite(line))
|
|
179
|
+
goToLine(line);
|
|
180
|
+
setGoLineOpen(false);
|
|
181
|
+
setGoLineValue("");
|
|
182
|
+
}, children: [_jsx("label", { htmlFor: "vr-source-go-line", children: "line" }), _jsx("input", { id: "vr-source-go-line", value: goLineValue, onChange: (e) => setGoLineValue(e.target.value), className: "w-20 rounded border border-line bg-ink px-2 py-1 text-paper outline-none focus:border-copper", inputMode: "numeric" }), _jsx("button", { type: "submit", className: "vr-press vr-focus rounded px-2 py-1 text-copper-hi hover:bg-panel", children: "go" }), _jsx("button", { type: "button", className: "vr-press vr-focus rounded px-2 py-1 hover:bg-panel hover:text-paper", onClick: () => {
|
|
183
|
+
setGoLineOpen(false);
|
|
184
|
+
setGoLineValue("");
|
|
185
|
+
}, children: "close" })] })), _jsx("div", { ref: hostRef, "data-vr-density": useReadingFont ? "reading" : language === "plain" ? "plain" : "source", className: `h-full w-full min-w-0 ${sourceFocusMode ? "vr-source-focus" : ""}` })] })] })] }));
|
|
186
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { EditorView } from "@codemirror/view";
|
|
2
|
+
import type { ReactNode } from "react";
|
|
3
|
+
import type { SearchProvider } from "@rightkit/document-core/documents/types";
|
|
4
|
+
import type { SourceLanguage } from "./sourceFormatting.js";
|
|
5
|
+
export interface SourceEditorViewState {
|
|
6
|
+
anchor: number;
|
|
7
|
+
head: number;
|
|
8
|
+
scrollTop: number;
|
|
9
|
+
scrollLeft: number;
|
|
10
|
+
}
|
|
11
|
+
export type SourceLinkPrefix = "[" | "![";
|
|
12
|
+
export interface SourceLinkState {
|
|
13
|
+
start: number;
|
|
14
|
+
end: number;
|
|
15
|
+
text: string;
|
|
16
|
+
prefix: SourceLinkPrefix;
|
|
17
|
+
}
|
|
18
|
+
export interface TextEditorProps {
|
|
19
|
+
/** Exact owning tab path for resource census attribution. */
|
|
20
|
+
documentPath?: string;
|
|
21
|
+
text: string;
|
|
22
|
+
onChange: (text: string) => void;
|
|
23
|
+
onSave: () => void;
|
|
24
|
+
/** Mono for source/code editing; reading font for plain txt. */
|
|
25
|
+
mono?: boolean;
|
|
26
|
+
/** Show the markdown formatting bar (txt + md/mdx source). Parity with new Notepad / md editors. */
|
|
27
|
+
markdownToolbar?: boolean;
|
|
28
|
+
/** Read-only source view, e.g. navigated HTML sub-pages that are not the entry file. */
|
|
29
|
+
readOnly?: boolean;
|
|
30
|
+
/** Source language profile. Markdown gets toolbar-aware shortcuts; HTML gets HTML parsing; plain is for TXT. */
|
|
31
|
+
language?: SourceLanguage;
|
|
32
|
+
registerSearchProvider: (provider: SearchProvider | null) => void;
|
|
33
|
+
/** Shared FindBar element (or null), floated top-right inside the editor area. */
|
|
34
|
+
findBar?: ReactNode;
|
|
35
|
+
/** Put the source toolbar in the app's docked document bar (default). Embedded sources that
|
|
36
|
+
* share the bar with another capsule (HTML's source drawer) keep their own strip. */
|
|
37
|
+
dockToolbar?: boolean;
|
|
38
|
+
/** Optional right-side controls rendered in the source toolbar. */
|
|
39
|
+
sourceToolbarActions?: ReactNode;
|
|
40
|
+
initialViewState?: SourceEditorViewState | null;
|
|
41
|
+
onViewStateChange?: (state: SourceEditorViewState) => void;
|
|
42
|
+
onEditorReady?: (view: EditorView | null) => void;
|
|
43
|
+
onSourceScrollLine?: (line: number) => void;
|
|
44
|
+
autoFocus?: boolean;
|
|
45
|
+
sourceFocusMode?: boolean;
|
|
46
|
+
sourceTypewriterMode?: boolean;
|
|
47
|
+
/** Use document-level undo/redo instead of CodeMirror history. */
|
|
48
|
+
externalHistory?: boolean;
|
|
49
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { Completion, CompletionContext } from "@codemirror/autocomplete";
|
|
2
|
+
import { EditorView, type DecorationSet } from "@codemirror/view";
|
|
3
|
+
import { StateField, type Extension } from "@codemirror/state";
|
|
4
|
+
import type { MutableRefObject } from "react";
|
|
5
|
+
import type { MarkdownDocumentCore } from "@rightkit/markdown-editor/documents/markdown/core/MarkdownDocumentCore";
|
|
6
|
+
import type { TextMatch } from "@rightkit/document-core/documents/searchUtils";
|
|
7
|
+
import { type SourceLanguage } from "./sourceFormatting.js";
|
|
8
|
+
export declare const setSearchHighlights: import("@codemirror/state").StateEffectType<{
|
|
9
|
+
matches: TextMatch[];
|
|
10
|
+
activeIndex: number;
|
|
11
|
+
}>;
|
|
12
|
+
export declare const searchHighlightField: StateField<DecorationSet>;
|
|
13
|
+
export declare const searchHighlightClickExtension: Extension;
|
|
14
|
+
export declare function invisibleCharactersExtension(enabled: boolean): Extension[];
|
|
15
|
+
export declare function languageExtension(_language: SourceLanguage): Extension[];
|
|
16
|
+
export declare function loadLanguageExtension(language: SourceLanguage): Promise<Extension[]>;
|
|
17
|
+
export declare function visualSourceLineSelection(view: EditorView, pos: number): {
|
|
18
|
+
anchor: number;
|
|
19
|
+
head: number;
|
|
20
|
+
};
|
|
21
|
+
export declare function gutterExtension(language: SourceLanguage): Extension[];
|
|
22
|
+
export declare function readOnlyExtension(readOnly: boolean): Extension[];
|
|
23
|
+
export declare function contentAttributesExtension(spellcheck: boolean, readOnly: boolean): Extension;
|
|
24
|
+
export declare function sourceWrapExtension(wordWrap: boolean, language: SourceLanguage, sourceWrap: boolean): Extension[];
|
|
25
|
+
export declare function imagePasteExtension(language: SourceLanguage, onMarkdownHtmlPaste?: (event: ClipboardEvent, view: EditorView) => boolean | Promise<boolean>): Extension[];
|
|
26
|
+
export declare function diagnosticsExtension(language: SourceLanguage, _core?: MarkdownDocumentCore | null): Extension[];
|
|
27
|
+
export declare function loadDiagnosticsExtension(language: SourceLanguage, core?: MarkdownDocumentCore | null): Promise<Extension[]>;
|
|
28
|
+
/** Caret-anchored "/" insert menu for Markdown/MDX source: CodeMirror's own completion popup
|
|
29
|
+
* (filter-as-you-type, Esc closes, Enter inserts) over the shared insert templates. Triggers on
|
|
30
|
+
* "/" at line start or after whitespace; the "/query" text is replaced in one transaction. */
|
|
31
|
+
export declare function slashInsertCompletionSource(context: CompletionContext): {
|
|
32
|
+
from: number;
|
|
33
|
+
options: Completion[];
|
|
34
|
+
validFor: RegExp;
|
|
35
|
+
} | null;
|
|
36
|
+
export declare function completionExtension(language: SourceLanguage): Extension;
|
|
37
|
+
export declare function loadMarkdownCoreCompletionExtension(core: MarkdownDocumentCore | null): Promise<Extension[]>;
|
|
38
|
+
export declare function sourceKeymap(markdownToolbar: boolean, onSave: () => void, onFormat: () => void, allowNextTabFocusRef: MutableRefObject<boolean>, wrapInline: (before: string, after: string) => void, openLink: (prefix: "[" | "![") => void, externalHistory?: boolean, externalUndo?: () => void, externalRedo?: () => void): Extension;
|