@stephen-netu/design-system 0.2.2 → 0.5.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/dist/d0-data-viz/BarChart.test.js +53 -0
- package/dist/d0-data-viz/CheckpointBar.test.js +83 -0
- package/dist/d0-data-viz/CheckpointPill.svelte +1 -1
- package/dist/d0-data-viz/CheckpointPill.test.js +78 -0
- package/dist/d0-data-viz/ConnectionHealth.test.js +70 -0
- package/dist/d0-data-viz/MetricCard.test.js +57 -0
- package/dist/d0-data-viz/SparkLine.test.js +47 -0
- package/dist/d0-data-viz/StorageMap.test.js +63 -0
- package/dist/d0-data-viz/Timeline.test.js +62 -0
- package/dist/d0-data-viz/WorldLineTimeline.test.js +99 -0
- package/dist/d0-data-viz/checkpoint-types.js +1 -1
- package/dist/d0-data-viz/storage-map-types.d.ts.map +1 -1
- package/dist/d0-data-viz/storage-map-types.js +6 -3
- package/dist/d0-data-viz/theme-integration.js +1 -1
- package/dist/demos/AmbientPrimitivesDemo.svelte +103 -0
- package/dist/demos/AmbientPrimitivesDemo.svelte.d.ts +5 -0
- package/dist/demos/AmbientPrimitivesDemo.svelte.d.ts.map +1 -0
- package/dist/demos/NodepadBlocksShowcase.svelte +2 -1
- package/dist/demos/NodepadBlocksShowcase.svelte.d.ts.map +1 -1
- package/dist/demos/index.d.ts +1 -0
- package/dist/demos/index.d.ts.map +1 -1
- package/dist/demos/index.js +1 -0
- package/dist/editor/CodeEditor.svelte +83 -26
- package/dist/editor/CodeEditor.svelte.d.ts +0 -1
- package/dist/editor/CodeEditor.svelte.d.ts.map +1 -1
- package/dist/editor/ExternalChangeDialog.svelte +117 -0
- package/dist/editor/ExternalChangeDialog.svelte.d.ts +13 -0
- package/dist/editor/ExternalChangeDialog.svelte.d.ts.map +1 -0
- package/dist/editor/MarkdownEditor.svelte +104 -617
- package/dist/editor/MarkdownEditor.svelte.d.ts +1 -1
- package/dist/editor/MarkdownEditor.svelte.d.ts.map +1 -1
- package/dist/editor/codemirror-config.d.ts +36 -0
- package/dist/editor/codemirror-config.d.ts.map +1 -0
- package/dist/editor/codemirror-config.js +87 -0
- package/dist/editor/components/CorrectionTooltip.svelte +180 -159
- package/dist/editor/components/CorrectionTooltip.svelte.d.ts +7 -23
- package/dist/editor/components/CorrectionTooltip.svelte.d.ts.map +1 -1
- package/dist/editor/extensions/autocomplete.d.ts +2 -2
- package/dist/editor/extensions/autocomplete.d.ts.map +1 -1
- package/dist/editor/extensions/autocomplete.test.js +121 -0
- package/dist/editor/extensions/spellcheck.d.ts +21 -3
- package/dist/editor/extensions/spellcheck.d.ts.map +1 -1
- package/dist/editor/extensions/spellcheck.js +51 -9
- package/dist/editor/extensions/spellcheck.test.js +168 -0
- package/dist/editor/extensions/types.d.ts +2 -1
- package/dist/editor/extensions/types.d.ts.map +1 -1
- package/dist/editor/index.d.ts +2 -1
- package/dist/editor/index.d.ts.map +1 -1
- package/dist/editor/index.js +1 -0
- package/dist/editor/markdown-preview.d.ts +9 -0
- package/dist/editor/markdown-preview.d.ts.map +1 -0
- package/dist/editor/markdown-preview.js +19 -0
- package/dist/editor/markdown-preview.test.js +38 -0
- package/dist/editor/useCodeMirror.d.ts +23 -0
- package/dist/editor/useCodeMirror.d.ts.map +1 -0
- package/dist/editor/useCodeMirror.js +40 -0
- package/dist/editor/useFilePersistence.d.ts +32 -0
- package/dist/editor/useFilePersistence.d.ts.map +1 -0
- package/dist/editor/useFilePersistence.js +117 -0
- package/dist/fabric/ai/ChatPanel.svelte +61 -62
- package/dist/fabric/ai/ChatPanel.svelte.d.ts.map +1 -1
- package/dist/fabric/ai/ResearchPanel.svelte +31 -24
- package/dist/fabric/data/FileTree.svelte +195 -0
- package/dist/fabric/data/FileTree.svelte.d.ts +19 -0
- package/dist/fabric/data/FileTree.svelte.d.ts.map +1 -0
- package/dist/fabric/data/FileTree.test.js +127 -0
- package/dist/fabric/data/index.d.ts +5 -9
- package/dist/fabric/data/index.d.ts.map +1 -1
- package/dist/fabric/data/index.js +4 -1
- package/dist/fabric/index.d.ts +1 -0
- package/dist/fabric/index.d.ts.map +1 -1
- package/dist/fabric/index.js +2 -0
- package/dist/fabric/input/KeyboardShortcuts.svelte +23 -21
- package/dist/fabric/layout/CollapsiblePanel.svelte +8 -10
- package/dist/fabric/navigation/ActivityRail.svelte +3 -5
- package/dist/fabric/navigation/ActivityRail.svelte.d.ts.map +1 -1
- package/dist/g0-generative/components/Text.svelte +38 -0
- package/dist/g0-generative/components/Text.svelte.d.ts +9 -0
- package/dist/g0-generative/components/Text.svelte.d.ts.map +1 -0
- package/dist/g0-generative/index.d.ts +1 -0
- package/dist/g0-generative/index.d.ts.map +1 -1
- package/dist/g0-generative/index.js +1 -0
- package/dist/g0-generative/library/ComponentLibrary.d.ts +4 -0
- package/dist/g0-generative/library/ComponentLibrary.d.ts.map +1 -1
- package/dist/g0-generative/library/ComponentLibrary.js +10 -0
- package/dist/g0-generative/library/ComponentLibrary.test.js +135 -0
- package/dist/g0-generative/renderer/OpenUIRenderer.svelte +16 -56
- package/dist/g0-generative/renderer/OpenUIRenderer.svelte.d.ts.map +1 -1
- package/dist/g0-generative/renderer/OpenUIRenderer.test.js +143 -0
- package/dist/g0-geo/SovereignMap.svelte +13 -13
- package/dist/g0-geo/SovereignMap.svelte.d.ts.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/l0-layout/block-writer/BlockContent.svelte +23 -22
- package/dist/l0-layout/block-writer/BlockContent.svelte.d.ts.map +1 -1
- package/dist/l0-layout/block-writer/BlockContent.test.js +47 -0
- package/dist/l0-layout/block-writer/markdown.js +11 -11
- package/dist/l0-layout/block-writer/markdown.test.js +81 -0
- package/dist/l0-layout/card/StackedCard.svelte +87 -0
- package/dist/l0-layout/card/StackedCard.svelte.d.ts +9 -0
- package/dist/l0-layout/card/StackedCard.svelte.d.ts.map +1 -0
- package/dist/l0-layout/card/StackedCard.test.js +29 -0
- package/dist/l0-layout/index.d.ts +1 -0
- package/dist/l0-layout/index.d.ts.map +1 -1
- package/dist/l0-layout/index.js +1 -0
- package/dist/l0-layout/kanban/KanbanBoard.svelte +1 -1
- package/dist/l0-layout/kanban/KanbanColumn.svelte +1 -1
- package/dist/n1-flow-canvas/FlowCanvas.svelte +50 -6
- package/dist/n1-flow-canvas/FlowCanvas.svelte.d.ts.map +1 -1
- package/dist/n1-flow-canvas/index.d.ts +2 -0
- package/dist/n1-flow-canvas/index.d.ts.map +1 -1
- package/dist/n1-flow-canvas/index.js +1 -0
- package/dist/n1-flow-canvas/pipeline-adapter.d.ts +37 -0
- package/dist/n1-flow-canvas/pipeline-adapter.d.ts.map +1 -0
- package/dist/n1-flow-canvas/pipeline-adapter.js +160 -0
- package/dist/n2-manifold-disk/ManifoldDisk.svelte +62 -35
- package/dist/n2-manifold-disk/ManifoldDisk.svelte.d.ts.map +1 -1
- package/dist/n4-adaptive-manifold/AdaptiveManifold.svelte +23 -19
- package/dist/n4-adaptive-manifold/AdaptiveManifold.svelte.d.ts.map +1 -1
- package/dist/n4-adaptive-manifold/types.d.ts +3 -0
- package/dist/n4-adaptive-manifold/types.d.ts.map +1 -1
- package/dist/p0-primitives/ThemeProvider.svelte +28 -7
- package/dist/p0-primitives/ThemeProvider.svelte.d.ts +5 -0
- package/dist/p0-primitives/ThemeProvider.svelte.d.ts.map +1 -1
- package/dist/p0-primitives/canvas-theme.d.ts +21 -1
- package/dist/p0-primitives/canvas-theme.d.ts.map +1 -1
- package/dist/p0-primitives/canvas-theme.js +78 -23
- package/dist/p0-primitives/debug.d.ts +6 -0
- package/dist/p0-primitives/debug.d.ts.map +1 -0
- package/dist/p0-primitives/debug.js +21 -0
- package/dist/p0-primitives/theme.d.ts.map +1 -1
- package/dist/p0-primitives/theme.js +11 -31
- package/dist/p0-primitives/tokens/ambient.css +76 -0
- package/dist/p0-primitives/tokens/tokens.css +124 -29
- package/dist/p0-primitives/utils/index.d.ts +2 -0
- package/dist/p0-primitives/utils/index.d.ts.map +1 -1
- package/dist/p0-primitives/utils/index.js +1 -0
- package/dist/p0-primitives/utils/reveal.d.ts +15 -0
- package/dist/p0-primitives/utils/reveal.d.ts.map +1 -0
- package/dist/p0-primitives/utils/reveal.js +29 -0
- package/dist/p0-primitives/utils/reveal.test.js +95 -0
- package/dist/s0-state/SyncStatus.svelte +1 -1
- package/dist/s0-state/index.d.ts +2 -1
- package/dist/s0-state/index.d.ts.map +1 -1
- package/dist/s0-state/index.js +2 -0
- package/dist/s0-state/modal-stack-store.svelte.d.ts +3 -3
- package/dist/s0-state/modal-stack-store.svelte.js +2 -2
- package/dist/spatial/ForceSimulation.svelte.js +1 -1
- package/dist/spatial/ParticleAssembleMark.svelte +285 -0
- package/dist/spatial/ParticleAssembleMark.svelte.d.ts +24 -0
- package/dist/spatial/ParticleAssembleMark.svelte.d.ts.map +1 -0
- package/dist/spatial/ZoneTiler.test.js +90 -0
- package/dist/spatial/index.js +1 -0
- package/dist/spatial/particle-assemble-math.d.ts +22 -0
- package/dist/spatial/particle-assemble-math.d.ts.map +1 -0
- package/dist/spatial/particle-assemble-math.js +26 -0
- package/dist/spatial/particle-assemble-math.test.js +56 -0
- package/dist/t0-transport/substrate.svelte.d.ts.map +1 -1
- package/dist/t0-transport/substrate.svelte.js +10 -4
- package/dist/u0-primitives/LockIndicator.svelte +16 -14
- package/dist/u0-primitives/VerticalToolbar.svelte +24 -21
- package/dist/u0-primitives/accordion/Accordion.svelte +8 -7
- package/dist/u0-primitives/animated-icon/AnimatedIcon.svelte +3 -3
- package/dist/u0-primitives/animated-icon/AnimatedIcon.test.js +82 -0
- package/dist/u0-primitives/avatar/Avatar.svelte +14 -14
- package/dist/u0-primitives/avatar/Avatar.test.js +81 -0
- package/dist/u0-primitives/badge/Badge.svelte +6 -4
- package/dist/u0-primitives/badge/ValidityBadge.svelte +4 -27
- package/dist/u0-primitives/button/Button.svelte +15 -12
- package/dist/u0-primitives/card/Card.svelte +37 -15
- package/dist/u0-primitives/card/Card.test.js +86 -0
- package/dist/u0-primitives/command-palette/CommandPalette.svelte +48 -43
- package/dist/u0-primitives/command-palette/CommandPalette.test.js +202 -0
- package/dist/u0-primitives/dropdown/Dropdown.svelte +20 -25
- package/dist/u0-primitives/grant-badge/GrantStatusBadge.svelte +2 -2
- package/dist/u0-primitives/index.d.ts +6 -0
- package/dist/u0-primitives/index.d.ts.map +1 -1
- package/dist/u0-primitives/index.js +8 -0
- package/dist/u0-primitives/input/Input.svelte +77 -24
- package/dist/u0-primitives/input/Input.svelte.d.ts +2 -10
- package/dist/u0-primitives/input/Input.svelte.d.ts.map +1 -1
- package/dist/u0-primitives/input/Input.test.js +81 -1
- package/dist/u0-primitives/live-dot/LiveDot.svelte +56 -0
- package/dist/u0-primitives/live-dot/LiveDot.svelte.d.ts +11 -0
- package/dist/u0-primitives/live-dot/LiveDot.svelte.d.ts.map +1 -0
- package/dist/u0-primitives/live-dot/LiveDot.test.js +41 -0
- package/dist/u0-primitives/live-dot/index.d.ts +3 -0
- package/dist/u0-primitives/live-dot/index.d.ts.map +1 -0
- package/dist/u0-primitives/live-dot/index.js +1 -0
- package/dist/u0-primitives/modal/Modal.svelte +22 -19
- package/dist/u0-primitives/modal/Modal.test.js +1 -1
- package/dist/u0-primitives/spinner/Spinner.svelte +11 -79
- package/dist/u0-primitives/spinner/Spinner.svelte.d.ts.map +1 -1
- package/dist/u0-primitives/steps/Steps.svelte +18 -19
- package/dist/u0-primitives/surface-state/SurfaceState.svelte +7 -7
- package/dist/u0-primitives/tabs/Tabs.svelte +13 -11
- package/dist/u0-primitives/theme-swatch-group/ThemeSwatchGroup.svelte +94 -0
- package/dist/u0-primitives/theme-swatch-group/ThemeSwatchGroup.svelte.d.ts +16 -0
- package/dist/u0-primitives/theme-swatch-group/ThemeSwatchGroup.svelte.d.ts.map +1 -0
- package/dist/u0-primitives/theme-swatch-group/ThemeSwatchGroup.test.js +48 -0
- package/dist/u0-primitives/theme-swatch-group/index.d.ts +3 -0
- package/dist/u0-primitives/theme-swatch-group/index.d.ts.map +1 -0
- package/dist/u0-primitives/theme-swatch-group/index.js +1 -0
- package/dist/u0-primitives/toggle/Toggle.svelte +10 -11
- package/dist/u0-primitives/tooltip/Tooltip.svelte +9 -9
- package/dist/u0-primitives/web-components/button-component.d.ts +5 -1
- package/dist/u0-primitives/web-components/button-component.d.ts.map +1 -1
- package/dist/u0-primitives/web-components/button-component.js +72 -33
- package/dist/u0-primitives/web-components/index.d.ts +3 -0
- package/dist/u0-primitives/web-components/index.d.ts.map +1 -0
- package/dist/u0-primitives/web-components/index.js +2 -0
- package/dist/u0-primitives/web-components/input-component.d.ts +5 -1
- package/dist/u0-primitives/web-components/input-component.d.ts.map +1 -1
- package/dist/u0-primitives/web-components/input-component.js +56 -39
- package/dist/u0-primitives/web-components/web-components.test.js +96 -0
- package/dist/u0-primitives/window-frame/WindowFrame.svelte +87 -0
- package/dist/u0-primitives/window-frame/WindowFrame.svelte.d.ts +10 -0
- package/dist/u0-primitives/window-frame/WindowFrame.svelte.d.ts.map +1 -0
- package/dist/u0-primitives/window-frame/WindowFrame.test.js +31 -0
- package/dist/u0-primitives/window-frame/index.d.ts +3 -0
- package/dist/u0-primitives/window-frame/index.d.ts.map +1 -0
- package/dist/u0-primitives/window-frame/index.js +1 -0
- package/dist/x0-enchanted-blocks/components/NodepadBlock.svelte +11 -2
- package/dist/x0-enchanted-blocks/components/NodepadBlock.svelte.d.ts.map +1 -1
- package/package.json +44 -28
- package/src/p0-primitives/tokens/ambient.css +76 -0
- package/src/p0-primitives/tokens/tokens.css +124 -29
- package/src/p0-primitives/tokens/tokens.json +42 -24
|
@@ -1,31 +1,15 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
*
|
|
5
|
-
* CodeMirror 6 integration with markdown support.
|
|
6
|
-
* Includes external file change detection and find/replace.
|
|
7
|
-
*
|
|
8
|
-
* NOTE: This is a heavy stateful component (CodeMirror creates ~200+ DOM nodes).
|
|
9
|
-
* Prefer {#if} over visibility:hidden when toggling to avoid mounting hidden
|
|
10
|
-
* editor instances. Use visibility:hidden only for the outer chrome wrapper.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import { onMount, onDestroy, untrack } from 'svelte';
|
|
14
|
-
import { EditorView, keymap, lineNumbers, highlightActiveLineGutter, highlightSpecialChars,
|
|
15
|
-
drawSelection, dropCursor, rectangularSelection, crosshairCursor,
|
|
16
|
-
highlightActiveLine, type ViewUpdate } from '@codemirror/view';
|
|
17
|
-
import { EditorState, type Extension } from '@codemirror/state';
|
|
18
|
-
import { markdown } from '@codemirror/lang-markdown';
|
|
19
|
-
import { defaultKeymap, history, historyKeymap } from '@codemirror/commands';
|
|
20
|
-
import { search } from '@codemirror/search';
|
|
21
|
-
import { writeTextFile, readTextFile, getMtimeMs, isTauri } from '../t0-transport/fs';
|
|
2
|
+
import { untrack } from 'svelte';
|
|
3
|
+
import { EditorView } from '@codemirror/view';
|
|
22
4
|
import FindReplaceDialog from './FindReplaceDialog.svelte';
|
|
23
|
-
import
|
|
24
|
-
import
|
|
25
|
-
import
|
|
26
|
-
import {
|
|
5
|
+
import CorrectionTooltip from './components/CorrectionTooltip.svelte';
|
|
6
|
+
import ExternalChangeDialog from './ExternalChangeDialog.svelte';
|
|
7
|
+
import DOMPurify from 'dompurify';
|
|
8
|
+
import { markdownToHtml } from './markdown-preview';
|
|
9
|
+
import { useCodeMirror } from './useCodeMirror';
|
|
10
|
+
import { useFilePersistence } from './useFilePersistence';
|
|
27
11
|
import type { CompletionSource } from './extensions/autocomplete';
|
|
28
|
-
import type { SpellCheckDictionary } from './extensions/spellcheck';
|
|
12
|
+
import type { SpellCheckCorrection, SpellCheckDictionary } from './extensions/spellcheck';
|
|
29
13
|
|
|
30
14
|
interface Props {
|
|
31
15
|
filePath: string | null;
|
|
@@ -33,656 +17,159 @@
|
|
|
33
17
|
onChange?: (content: string) => void;
|
|
34
18
|
onSave?: () => void;
|
|
35
19
|
onClose?: () => void;
|
|
36
|
-
onExternalChange?: (
|
|
20
|
+
onExternalChange?: (content: string) => void;
|
|
37
21
|
completionSource?: CompletionSource;
|
|
38
22
|
spellCheckDictionary?: SpellCheckDictionary;
|
|
39
23
|
}
|
|
40
24
|
|
|
41
|
-
|
|
25
|
+
interface ActiveCorrection extends SpellCheckCorrection { from: number; to: number; }
|
|
42
26
|
|
|
27
|
+
let { filePath, initialContent, onChange, onSave, onClose, onExternalChange, completionSource, spellCheckDictionary }: Props = $props();
|
|
43
28
|
let content = $state(untrack(() => initialContent));
|
|
44
29
|
let isDirty = $state(false);
|
|
45
30
|
let isSaving = $state(false);
|
|
46
31
|
let saveError = $state<string | null>(null);
|
|
47
32
|
let showPreview = $state(false);
|
|
48
|
-
|
|
49
|
-
// Auto-save
|
|
50
|
-
let autoSaveTimeout: ReturnType<typeof setTimeout> | null = null;
|
|
51
|
-
|
|
52
|
-
// Persistence backend (T0 transport). Static per session: without a Tauri
|
|
53
|
-
// host there is no file backend, and the UI must say so instead of "Saved".
|
|
54
|
-
const persistenceAvailable = isTauri();
|
|
55
|
-
|
|
56
|
-
// External change detection
|
|
33
|
+
let showFindReplace = $state(false);
|
|
57
34
|
let showExternalChangeDialog = $state(false);
|
|
58
|
-
let
|
|
59
|
-
let isCheckingExternalChanges = $state(false);
|
|
60
|
-
let lastKnownMtimeMs: number | null = null;
|
|
61
|
-
|
|
62
|
-
// CodeMirror instance
|
|
35
|
+
let activeCorrection = $state<ActiveCorrection | null>(null);
|
|
63
36
|
let editorContainer: HTMLDivElement;
|
|
64
37
|
let editorView = $state<EditorView | null>(null);
|
|
38
|
+
let autoSaveTimeout: ReturnType<typeof setTimeout> | null = null;
|
|
39
|
+
let stopExternalChangeMonitoring: (() => void) | null = null;
|
|
40
|
+
let lastKnownMtimeMs = $state<number | null>(null);
|
|
41
|
+
let isCheckingExternalChanges = $state(false);
|
|
65
42
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
43
|
+
const filePersistence = useFilePersistence({
|
|
44
|
+
getFilePath: () => filePath,
|
|
45
|
+
getContent: () => content,
|
|
46
|
+
getIsDirty: () => isDirty,
|
|
47
|
+
getIsSaving: () => isSaving,
|
|
48
|
+
getIsCheckingExternalChanges: () => isCheckingExternalChanges,
|
|
49
|
+
getShowExternalChangeDialog: () => showExternalChangeDialog,
|
|
50
|
+
getLastKnownMtimeMs: () => lastKnownMtimeMs,
|
|
51
|
+
getEditorView: () => editorView,
|
|
52
|
+
setContent: (value) => { content = value; },
|
|
53
|
+
setIsDirty: (value) => { isDirty = value; },
|
|
54
|
+
setIsSaving: (value) => { isSaving = value; },
|
|
55
|
+
setSaveError: (value) => { saveError = value; },
|
|
56
|
+
setShowExternalChangeDialog: (value) => { showExternalChangeDialog = value; },
|
|
57
|
+
setIsCheckingExternalChanges: (value) => { isCheckingExternalChanges = value; },
|
|
58
|
+
setLastKnownMtimeMs: (value) => { lastKnownMtimeMs = value; },
|
|
59
|
+
onChange: (value) => onChange?.(value),
|
|
60
|
+
onSave: () => onSave?.(),
|
|
61
|
+
onExternalChange: (value) => onExternalChange?.(value)
|
|
62
|
+
});
|
|
77
63
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
openFindReplace(true);
|
|
98
|
-
return true;
|
|
64
|
+
const codeMirror = useCodeMirror({
|
|
65
|
+
getContainer: () => editorContainer,
|
|
66
|
+
getInitialContent: () => initialContent,
|
|
67
|
+
completionSource: () => completionSource,
|
|
68
|
+
spellCheckDictionary: () => spellCheckDictionary,
|
|
69
|
+
onCorrectionClick: (correction, _view, from, to) => { activeCorrection = { ...correction, from, to }; },
|
|
70
|
+
onSave: filePersistence.saveContent,
|
|
71
|
+
onFind: () => openFindReplace(),
|
|
72
|
+
onFindReplace: () => openFindReplace(),
|
|
73
|
+
onDocChanged: handleDocChanged,
|
|
74
|
+
onEditorReady: (view) => {
|
|
75
|
+
editorView = view;
|
|
76
|
+
stopExternalChangeMonitoring = filePersistence.startExternalChangeMonitoring();
|
|
77
|
+
},
|
|
78
|
+
onEditorDestroy: () => {
|
|
79
|
+
editorView = null;
|
|
80
|
+
if (autoSaveTimeout) { clearTimeout(autoSaveTimeout); autoSaveTimeout = null; }
|
|
81
|
+
stopExternalChangeMonitoring?.();
|
|
82
|
+
stopExternalChangeMonitoring = null;
|
|
99
83
|
}
|
|
100
|
-
}
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
let fileName = $derived(filePath?.split('/').pop() || filePath || 'Untitled');
|
|
87
|
+
let saveStatus = $derived(isSaving ? 'Saving...' : saveError ? 'Error!' : filePath && !filePersistence.persistenceAvailable ? 'Not persisted' : isDirty ? 'Unsaved' : 'Saved');
|
|
101
88
|
|
|
102
|
-
|
|
103
|
-
function openFindReplace(withReplace: boolean) {
|
|
89
|
+
function openFindReplace() {
|
|
104
90
|
showFindReplace = true;
|
|
105
|
-
// The dialog will handle toggling replace mode via the button
|
|
106
|
-
// We could pass this as a prop if we want to open directly to replace mode
|
|
107
91
|
}
|
|
108
92
|
|
|
109
|
-
// Close find/replace dialog
|
|
110
93
|
function closeFindReplace() {
|
|
111
94
|
showFindReplace = false;
|
|
112
|
-
|
|
113
|
-
editorView?.focus();
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Custom dark theme matching Atelier - uses CSS variables via style injection
|
|
117
|
-
const atelierTheme = EditorView.theme({
|
|
118
|
-
'&': {
|
|
119
|
-
backgroundColor: 'var(--color-bg-canvas)',
|
|
120
|
-
color: 'var(--color-text-primary)',
|
|
121
|
-
fontSize: '14px',
|
|
122
|
-
fontFamily: "var(--font-mono)"
|
|
123
|
-
},
|
|
124
|
-
'.cm-content': {
|
|
125
|
-
caretColor: 'var(--color-accent)',
|
|
126
|
-
padding: '16px'
|
|
127
|
-
},
|
|
128
|
-
'.cm-cursor': {
|
|
129
|
-
borderLeftColor: 'var(--color-accent)'
|
|
130
|
-
},
|
|
131
|
-
'.cm-activeLine': {
|
|
132
|
-
backgroundColor: 'var(--color-accent-subtle)'
|
|
133
|
-
},
|
|
134
|
-
'.cm-gutters': {
|
|
135
|
-
backgroundColor: 'var(--color-bg-app)',
|
|
136
|
-
borderRight: '1px solid var(--border-subtle)',
|
|
137
|
-
color: 'var(--color-text-tertiary)'
|
|
138
|
-
},
|
|
139
|
-
'.cm-activeLineGutter': {
|
|
140
|
-
backgroundColor: 'var(--color-accent-subtle)',
|
|
141
|
-
color: 'var(--color-accent)'
|
|
142
|
-
},
|
|
143
|
-
'.cm-selectionBackground': {
|
|
144
|
-
backgroundColor: 'var(--color-accent-subtle)'
|
|
145
|
-
}
|
|
146
|
-
}, { dark: true });
|
|
147
|
-
|
|
148
|
-
// Check for external file changes (mtime-based via T0 transport)
|
|
149
|
-
async function checkExternalChanges() {
|
|
150
|
-
if (!filePath || !persistenceAvailable || isCheckingExternalChanges || showExternalChangeDialog || !isDirty) return;
|
|
151
|
-
|
|
152
|
-
isCheckingExternalChanges = true;
|
|
153
|
-
try {
|
|
154
|
-
const mtime = await getMtimeMs(filePath);
|
|
155
|
-
if (mtime !== null && lastKnownMtimeMs !== null && mtime > lastKnownMtimeMs) {
|
|
156
|
-
showExternalChangeDialog = true;
|
|
157
|
-
}
|
|
158
|
-
} catch (err) {
|
|
159
|
-
// Silently fail - file might not exist
|
|
160
|
-
console.debug('External change check failed:', err);
|
|
161
|
-
} finally {
|
|
162
|
-
isCheckingExternalChanges = false;
|
|
163
|
-
}
|
|
95
|
+
codeMirror.focus();
|
|
164
96
|
}
|
|
165
97
|
|
|
166
|
-
|
|
167
|
-
|
|
98
|
+
function handleDocChanged(newContent: string) {
|
|
99
|
+
content = newContent;
|
|
100
|
+
activeCorrection = null;
|
|
101
|
+
isDirty = true;
|
|
102
|
+
saveError = null;
|
|
103
|
+
onChange?.(newContent);
|
|
168
104
|
if (!filePath) return;
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
// Update editor content
|
|
174
|
-
if (editorView) {
|
|
175
|
-
editorView.dispatch({
|
|
176
|
-
changes: {
|
|
177
|
-
from: 0,
|
|
178
|
-
to: editorView.state.doc.length,
|
|
179
|
-
insert: newContent
|
|
180
|
-
}
|
|
181
|
-
});
|
|
182
|
-
}
|
|
105
|
+
if (autoSaveTimeout) clearTimeout(autoSaveTimeout);
|
|
106
|
+
autoSaveTimeout = setTimeout(() => { void filePersistence.saveContent(); }, 800);
|
|
107
|
+
}
|
|
183
108
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
109
|
+
function handleAcceptCorrection(suggestion: string) {
|
|
110
|
+
if (!activeCorrection || !editorView) return;
|
|
111
|
+
editorView.dispatch({ changes: { from: activeCorrection.from, to: activeCorrection.to, insert: suggestion } });
|
|
112
|
+
activeCorrection = null;
|
|
113
|
+
}
|
|
187
114
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
115
|
+
function handleAddWordToDictionary() {
|
|
116
|
+
if (!activeCorrection) return;
|
|
117
|
+
spellCheckDictionary?.addWord(activeCorrection.original);
|
|
118
|
+
activeCorrection = null;
|
|
119
|
+
}
|
|
191
120
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
} catch (err) {
|
|
195
|
-
console.error('Failed to reload file:', err);
|
|
196
|
-
saveError = err instanceof Error ? err.message : 'Failed to reload file';
|
|
197
|
-
} finally {
|
|
198
|
-
showExternalChangeDialog = false;
|
|
199
|
-
}
|
|
121
|
+
function handleDismissCorrection() {
|
|
122
|
+
activeCorrection = null;
|
|
200
123
|
}
|
|
201
124
|
|
|
202
|
-
// Handle keep current (ignore external changes)
|
|
203
125
|
function handleKeepCurrent() {
|
|
204
|
-
|
|
205
|
-
// Mark as dirty to indicate divergence from disk
|
|
206
|
-
isDirty = true;
|
|
126
|
+
filePersistence.keepCurrent();
|
|
207
127
|
}
|
|
208
128
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
highlightActiveLineGutter(),
|
|
213
|
-
highlightSpecialChars(),
|
|
214
|
-
history(),
|
|
215
|
-
keymap.of(defaultKeymap),
|
|
216
|
-
keymap.of(historyKeymap),
|
|
217
|
-
drawSelection(),
|
|
218
|
-
dropCursor(),
|
|
219
|
-
rectangularSelection(),
|
|
220
|
-
crosshairCursor(),
|
|
221
|
-
highlightActiveLine(),
|
|
222
|
-
];
|
|
223
|
-
|
|
224
|
-
// Initialize CodeMirror
|
|
225
|
-
onMount(() => {
|
|
226
|
-
const extensions: Extension[] = [
|
|
227
|
-
minimalSetup,
|
|
228
|
-
markdown(),
|
|
229
|
-
atelierTheme,
|
|
230
|
-
saveKeymap,
|
|
231
|
-
autoFormat(),
|
|
232
|
-
expansion(),
|
|
233
|
-
...(completionSource ? [autocomplete({ source: completionSource })] : []),
|
|
234
|
-
...(spellCheckDictionary ? [spellcheck(spellCheckDictionary)] : []),
|
|
235
|
-
EditorView.updateListener.of((update: ViewUpdate) => {
|
|
236
|
-
if (update.docChanged) {
|
|
237
|
-
content = update.state.doc.toString();
|
|
238
|
-
isDirty = true;
|
|
239
|
-
saveError = null;
|
|
240
|
-
onChange?.(content);
|
|
241
|
-
// Auto-save: debounced 800ms after last keystroke
|
|
242
|
-
if (filePath) {
|
|
243
|
-
if (autoSaveTimeout) clearTimeout(autoSaveTimeout);
|
|
244
|
-
autoSaveTimeout = setTimeout(() => saveContent(), 800);
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
})
|
|
248
|
-
];
|
|
249
|
-
|
|
250
|
-
const state = EditorState.create({
|
|
251
|
-
doc: initialContent,
|
|
252
|
-
extensions
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
editorView = new EditorView({
|
|
256
|
-
state,
|
|
257
|
-
parent: editorContainer
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
// Seed the tracked modification time, then start external change
|
|
261
|
-
// detection polling (every 2 seconds). Skipped without a backend.
|
|
262
|
-
if (filePath && persistenceAvailable) {
|
|
263
|
-
getMtimeMs(filePath)
|
|
264
|
-
.then((m) => { lastKnownMtimeMs = m; })
|
|
265
|
-
.catch(() => { /* file may not exist yet */ });
|
|
266
|
-
}
|
|
267
|
-
externalChangeCheckInterval = setInterval(checkExternalChanges, 2000);
|
|
268
|
-
|
|
269
|
-
// Also check when window regains focus
|
|
270
|
-
const handleWindowFocus = () => {
|
|
271
|
-
checkExternalChanges();
|
|
272
|
-
};
|
|
273
|
-
window.addEventListener('focus', handleWindowFocus);
|
|
274
|
-
|
|
275
|
-
return () => {
|
|
276
|
-
window.removeEventListener('focus', handleWindowFocus);
|
|
277
|
-
if (externalChangeCheckInterval) {
|
|
278
|
-
clearInterval(externalChangeCheckInterval);
|
|
279
|
-
externalChangeCheckInterval = null;
|
|
280
|
-
}
|
|
281
|
-
};
|
|
282
|
-
});
|
|
283
|
-
|
|
284
|
-
// Cleanup
|
|
285
|
-
onDestroy(() => {
|
|
286
|
-
editorView?.destroy();
|
|
287
|
-
if (autoSaveTimeout) clearTimeout(autoSaveTimeout);
|
|
288
|
-
if (externalChangeCheckInterval) {
|
|
289
|
-
clearInterval(externalChangeCheckInterval);
|
|
290
|
-
externalChangeCheckInterval = null;
|
|
291
|
-
}
|
|
292
|
-
});
|
|
129
|
+
function togglePreview() {
|
|
130
|
+
showPreview = !showPreview;
|
|
131
|
+
}
|
|
293
132
|
|
|
294
|
-
// Update content when file changes
|
|
295
133
|
$effect(() => {
|
|
296
|
-
if (editorView
|
|
297
|
-
|
|
298
|
-
changes: {
|
|
299
|
-
from: 0,
|
|
300
|
-
to: editorView.state.doc.length,
|
|
301
|
-
insert: initialContent
|
|
302
|
-
}
|
|
303
|
-
});
|
|
134
|
+
if (editorView) {
|
|
135
|
+
codeMirror.syncContent(initialContent);
|
|
304
136
|
content = initialContent;
|
|
305
137
|
isDirty = false;
|
|
306
138
|
saveError = null;
|
|
307
139
|
}
|
|
308
140
|
});
|
|
309
|
-
|
|
310
|
-
// Save file
|
|
311
|
-
async function saveContent() {
|
|
312
|
-
if (!isDirty || isSaving || !filePath) return;
|
|
313
|
-
|
|
314
|
-
// No backend → no write. The status chip reads "Not persisted"; do not
|
|
315
|
-
// mark clean or fire onSave, and never pretend the save succeeded.
|
|
316
|
-
if (!persistenceAvailable) return;
|
|
317
|
-
|
|
318
|
-
isSaving = true;
|
|
319
|
-
saveError = null;
|
|
320
|
-
|
|
321
|
-
try {
|
|
322
|
-
await writeTextFile(filePath, content);
|
|
323
|
-
isDirty = false;
|
|
324
|
-
onSave?.();
|
|
325
|
-
|
|
326
|
-
// Update tracked modification time after save
|
|
327
|
-
try { lastKnownMtimeMs = await getMtimeMs(filePath); } catch { /* keep prior value */ }
|
|
328
|
-
} catch (err) {
|
|
329
|
-
saveError = err instanceof Error ? err.message : String(err);
|
|
330
|
-
console.error('Failed to save file:', err);
|
|
331
|
-
} finally {
|
|
332
|
-
isSaving = false;
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
function togglePreview() {
|
|
337
|
-
showPreview = !showPreview;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
// Simple markdown preview
|
|
341
|
-
function markdownToHtml(md: string): string {
|
|
342
|
-
return md
|
|
343
|
-
.replace(/^### (.*$)/gim, '<h3>$1</h3>')
|
|
344
|
-
.replace(/^## (.*$)/gim, '<h2>$1</h2>')
|
|
345
|
-
.replace(/^# (.*$)/gim, '<h1>$1</h1>')
|
|
346
|
-
.replace(/\*\*(.*?)\*\*/gim, '<strong>$1</strong>')
|
|
347
|
-
.replace(/\*(.*?)\*/gim, '<em>$1</em>')
|
|
348
|
-
.replace(/`([^`]+)`/gim, '<code>$1</code>')
|
|
349
|
-
.replace(/^- (.*$)/gim, '<li>$1</li>')
|
|
350
|
-
.replace(/(<li>.*<\/li>)/s, '<ul>$1</ul>')
|
|
351
|
-
.replace(/\n/gim, '<br>');
|
|
352
|
-
}
|
|
353
141
|
</script>
|
|
354
142
|
|
|
355
143
|
<div class="markdown-editor" data-testid="markdown-editor">
|
|
356
|
-
<!-- Header -->
|
|
357
144
|
<div class="editor-header" data-testid="editor-header">
|
|
358
145
|
<div class="file-info">
|
|
359
146
|
<span class="file-name" data-testid="editor-filename">{fileName}</span>
|
|
360
|
-
<span class="save-status" class:dirty={isDirty} class:error={saveError} data-testid="save-status">
|
|
361
|
-
{saveStatus}
|
|
362
|
-
</span>
|
|
147
|
+
<span class="save-status" class:dirty={isDirty} class:error={!!saveError} data-testid="save-status">{saveStatus}</span>
|
|
363
148
|
</div>
|
|
364
149
|
<div class="editor-actions">
|
|
365
|
-
<button
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
onclick={
|
|
369
|
-
>
|
|
370
|
-
Preview
|
|
371
|
-
</button>
|
|
372
|
-
{#if onClose}
|
|
373
|
-
<button
|
|
374
|
-
class="action-btn"
|
|
375
|
-
onclick={onClose}
|
|
376
|
-
>
|
|
377
|
-
Close
|
|
378
|
-
</button>
|
|
379
|
-
{/if}
|
|
380
|
-
{#if filePath && persistenceAvailable}
|
|
381
|
-
<button
|
|
382
|
-
class="action-btn primary"
|
|
383
|
-
onclick={saveContent}
|
|
384
|
-
disabled={!isDirty || isSaving}
|
|
385
|
-
>
|
|
386
|
-
{isSaving ? 'Saving...' : 'Save'}
|
|
387
|
-
</button>
|
|
150
|
+
<button class="action-btn" class:active={showPreview} onclick={togglePreview}>Preview</button>
|
|
151
|
+
{#if onClose}<button class="action-btn" onclick={onClose}>Close</button>{/if}
|
|
152
|
+
{#if filePath && filePersistence.persistenceAvailable}
|
|
153
|
+
<button class="action-btn primary" onclick={filePersistence.saveContent} disabled={!isDirty || isSaving}>{isSaving ? 'Saving...' : 'Save'}</button>
|
|
388
154
|
{/if}
|
|
389
155
|
</div>
|
|
390
156
|
</div>
|
|
391
|
-
|
|
392
|
-
<!-- Body -->
|
|
393
157
|
<div class="editor-body" class:split={showPreview}>
|
|
394
158
|
<div class="editor-pane" bind:this={editorContainer}>
|
|
395
|
-
|
|
396
|
-
<FindReplaceDialog
|
|
397
|
-
{editorView}
|
|
398
|
-
isOpen={showFindReplace}
|
|
399
|
-
onClose={closeFindReplace}
|
|
400
|
-
/>
|
|
159
|
+
<FindReplaceDialog {editorView} isOpen={showFindReplace} onClose={closeFindReplace} />
|
|
401
160
|
</div>
|
|
402
|
-
|
|
403
161
|
{#if showPreview}
|
|
404
|
-
<div class="preview-pane">
|
|
405
|
-
<div class="preview-content">
|
|
406
|
-
{@html markdownToHtml(content)}
|
|
407
|
-
</div>
|
|
408
|
-
</div>
|
|
162
|
+
<div class="preview-pane"><div class="preview-content">{@html DOMPurify.sanitize(markdownToHtml(content))}</div></div>
|
|
409
163
|
{/if}
|
|
410
164
|
</div>
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
<div class="error-banner">
|
|
414
|
-
Failed to save: {saveError}
|
|
415
|
-
</div>
|
|
165
|
+
{#if activeCorrection}
|
|
166
|
+
<CorrectionTooltip correction={{ original: activeCorrection.original, suggestions: activeCorrection.suggestions }} position={activeCorrection.position} onAccept={handleAcceptCorrection} onDismiss={handleDismissCorrection} onAddToDictionary={handleAddWordToDictionary} />
|
|
416
167
|
{/if}
|
|
168
|
+
{#if saveError}<div class="error-banner">Failed to save: {saveError}</div>{/if}
|
|
417
169
|
</div>
|
|
170
|
+
{#if showExternalChangeDialog}<ExternalChangeDialog onReload={filePersistence.reloadFromDisk} onKeepCurrent={handleKeepCurrent} />{/if}
|
|
418
171
|
|
|
419
|
-
<!--
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
<div class="dialog">
|
|
423
|
-
<div class="dialog-header">
|
|
424
|
-
<h3>File Changed Externally</h3>
|
|
425
|
-
</div>
|
|
426
|
-
<div class="dialog-body">
|
|
427
|
-
<p>This file has been modified outside of Atelier.</p>
|
|
428
|
-
<p>Would you like to reload it from disk, or keep your current changes?</p>
|
|
429
|
-
</div>
|
|
430
|
-
<div class="dialog-footer">
|
|
431
|
-
<button
|
|
432
|
-
class="action-btn"
|
|
433
|
-
onclick={handleKeepCurrent}
|
|
434
|
-
>
|
|
435
|
-
Keep Current
|
|
436
|
-
</button>
|
|
437
|
-
<button
|
|
438
|
-
class="action-btn primary"
|
|
439
|
-
onclick={handleReloadFromDisk}
|
|
440
|
-
>
|
|
441
|
-
Reload from Disk
|
|
442
|
-
</button>
|
|
443
|
-
</div>
|
|
444
|
-
</div>
|
|
445
|
-
</div>
|
|
446
|
-
{/if}
|
|
447
|
-
|
|
448
|
-
<style>
|
|
449
|
-
.markdown-editor {
|
|
450
|
-
display: flex;
|
|
451
|
-
flex-direction: column;
|
|
452
|
-
height: 100%;
|
|
453
|
-
background: var(--color-bg-canvas, #141414);
|
|
454
|
-
color: var(--color-text-primary, #f5f2eb);
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
.editor-header {
|
|
458
|
-
display: flex;
|
|
459
|
-
align-items: center;
|
|
460
|
-
justify-content: space-between;
|
|
461
|
-
padding: 12px 16px;
|
|
462
|
-
border-bottom: 1px solid var(--border-subtle, #2a2a2a);
|
|
463
|
-
background: var(--color-bg-panel, #1a1a1a);
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
.file-info {
|
|
467
|
-
display: flex;
|
|
468
|
-
align-items: center;
|
|
469
|
-
gap: var(--space-3);
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
.file-name {
|
|
473
|
-
font-weight: 500;
|
|
474
|
-
font-size: 14px;
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
.save-status {
|
|
478
|
-
font-size: 12px;
|
|
479
|
-
color: var(--color-text-tertiary, #6b6b6b);
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
.save-status.dirty {
|
|
483
|
-
color: var(--color-accent);
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
.save-status.error {
|
|
487
|
-
color: var(--color-error, #ef4444);
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
.editor-actions {
|
|
491
|
-
display: flex;
|
|
492
|
-
gap: var(--space-2);
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
.action-btn {
|
|
496
|
-
padding: 6px 12px;
|
|
497
|
-
border: 1px solid var(--border-subtle, #2a2a2a);
|
|
498
|
-
background: var(--color-bg-panel-elevated, #252525);
|
|
499
|
-
color: var(--color-text-secondary, #a0a0a0);
|
|
500
|
-
border-radius: var(--radius-sm, 4px);
|
|
501
|
-
cursor: pointer;
|
|
502
|
-
font-size: 13px;
|
|
503
|
-
transition: all 0.15s ease;
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
.action-btn:hover {
|
|
507
|
-
background: var(--border-subtle);
|
|
508
|
-
color: var(--color-text-primary, #f5f2eb);
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
.action-btn.active {
|
|
512
|
-
background: var(--color-accent-subtle);
|
|
513
|
-
color: var(--color-accent);
|
|
514
|
-
border-color: var(--color-accent);
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
.action-btn.primary {
|
|
518
|
-
background: var(--color-accent);
|
|
519
|
-
color: white;
|
|
520
|
-
border-color: var(--color-accent);
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
.action-btn.primary:hover {
|
|
524
|
-
background: var(--color-accent-hover, #c9843d);
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
.action-btn:disabled {
|
|
528
|
-
opacity: 0.5;
|
|
529
|
-
cursor: not-allowed;
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
.editor-body {
|
|
533
|
-
display: flex;
|
|
534
|
-
flex: 1;
|
|
535
|
-
overflow: hidden;
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
.editor-body.split .editor-pane,
|
|
539
|
-
.editor-body.split .preview-pane {
|
|
540
|
-
flex: 1;
|
|
541
|
-
width: 50%;
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
.editor-pane {
|
|
545
|
-
flex: 1;
|
|
546
|
-
overflow: hidden;
|
|
547
|
-
position: relative;
|
|
548
|
-
:global(.cm-editor) {
|
|
549
|
-
height: 100%;
|
|
550
|
-
}
|
|
551
|
-
:global(.cm-scroller) {
|
|
552
|
-
overflow: auto;
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
.preview-pane {
|
|
557
|
-
border-left: 1px solid var(--border-subtle, #2a2a2a);
|
|
558
|
-
overflow-y: auto;
|
|
559
|
-
background: var(--color-bg-panel, #1a1a1a);
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
.preview-content {
|
|
563
|
-
padding: 16px;
|
|
564
|
-
font-size: 14px;
|
|
565
|
-
line-height: 1.6;
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
.preview-content :global(h1),
|
|
569
|
-
.preview-content :global(h2),
|
|
570
|
-
.preview-content :global(h3) {
|
|
571
|
-
margin-top: 0;
|
|
572
|
-
margin-bottom: 16px;
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
.preview-content :global(p) {
|
|
576
|
-
margin-bottom: 12px;
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
.preview-content :global(code) {
|
|
580
|
-
background: var(--color-bg-panel-elevated, #252525);
|
|
581
|
-
padding: 2px 6px;
|
|
582
|
-
border-radius: var(--radius-sm, 3px);
|
|
583
|
-
font-family: var(--font-mono, 'JetBrains Mono', monospace);
|
|
584
|
-
font-size: 0.9em;
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
.preview-content :global(pre) {
|
|
588
|
-
background: var(--color-bg-panel-elevated, #252525);
|
|
589
|
-
padding: 12px;
|
|
590
|
-
border-radius: var(--radius-md, 6px);
|
|
591
|
-
overflow-x: auto;
|
|
592
|
-
margin-bottom: 12px;
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
.preview-content :global(pre code) {
|
|
596
|
-
background: none;
|
|
597
|
-
padding: 0;
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
.preview-content :global(ul),
|
|
601
|
-
.preview-content :global(ol) {
|
|
602
|
-
margin-bottom: 12px;
|
|
603
|
-
padding-left: 24px;
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
.preview-content :global(li) {
|
|
607
|
-
margin-bottom: 4px;
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
.preview-content :global(blockquote) {
|
|
611
|
-
border-left: 3px solid var(--color-accent);
|
|
612
|
-
padding-left: 12px;
|
|
613
|
-
margin-left: 0;
|
|
614
|
-
margin-bottom: 12px;
|
|
615
|
-
color: var(--color-text-secondary);
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
.preview-content :global(a) {
|
|
619
|
-
color: var(--color-accent);
|
|
620
|
-
text-decoration: none;
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
.preview-content :global(a:hover) {
|
|
624
|
-
text-decoration: underline;
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
.error-banner {
|
|
628
|
-
padding: 8px 16px;
|
|
629
|
-
background: var(--color-error, #ef4444);
|
|
630
|
-
color: white;
|
|
631
|
-
font-size: 13px;
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
/* Dialog Styles */
|
|
635
|
-
.dialog-overlay {
|
|
636
|
-
position: fixed;
|
|
637
|
-
inset: 0;
|
|
638
|
-
background: rgba(0, 0, 0, 0.7);
|
|
639
|
-
display: flex;
|
|
640
|
-
align-items: center;
|
|
641
|
-
justify-content: center;
|
|
642
|
-
z-index: 1000;
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
.dialog {
|
|
646
|
-
background: var(--color-bg-panel, #1a1a1a);
|
|
647
|
-
border: 1px solid var(--border-subtle, #2a2a2a);
|
|
648
|
-
border-radius: var(--radius-lg, 8px);
|
|
649
|
-
min-width: 400px;
|
|
650
|
-
max-width: 500px;
|
|
651
|
-
box-shadow: var(--shadow-lg, 0 10px 40px rgba(0,0,0,0.5));
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
.dialog-header {
|
|
655
|
-
padding: 16px 20px;
|
|
656
|
-
border-bottom: 1px solid var(--border-subtle, #2a2a2a);
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
.dialog-header h3 {
|
|
660
|
-
margin: 0;
|
|
661
|
-
font-size: 16px;
|
|
662
|
-
font-weight: 600;
|
|
663
|
-
color: var(--color-text-primary, #f5f2eb);
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
.dialog-body {
|
|
667
|
-
padding: 20px;
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
.dialog-body p {
|
|
671
|
-
margin: 0 0 12px 0;
|
|
672
|
-
color: var(--color-text-secondary, #a0a0a0);
|
|
673
|
-
font-size: 14px;
|
|
674
|
-
line-height: 1.5;
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
.dialog-body p:last-child {
|
|
678
|
-
margin-bottom: 0;
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
.dialog-footer {
|
|
682
|
-
display: flex;
|
|
683
|
-
justify-content: flex-end;
|
|
684
|
-
gap: var(--space-3);
|
|
685
|
-
padding: 16px 20px;
|
|
686
|
-
border-top: 1px solid var(--border-subtle, #2a2a2a);
|
|
687
|
-
}
|
|
172
|
+
<!-- svelte-ignore css_unused_selector -->
|
|
173
|
+
<style>
|
|
174
|
+
.markdown-editor{display:flex;flex-direction:column;height:100%;background:var(--color-bg-canvas,#141414);color:var(--color-text-primary,#f5f2eb)}.editor-header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;border-bottom:1px solid var(--border-subtle,#2a2a2a);background:var(--color-bg-panel,#1a1a1a)}.file-info{display:flex;align-items:center;gap:var(--space-3)}.file-name{font-weight:500;font-size:14px}.save-status{font-size:12px;color:var(--color-text-tertiary,#6b6b6b)}.save-status.dirty{color:var(--color-accent)}.save-status.error{color:var(--color-error,#ef4444)}.editor-actions{display:flex;gap:var(--space-2)}.action-btn{padding:6px 12px;border:1px solid var(--border-subtle,#2a2a2a);background:var(--color-bg-panel-elevated,#252525);color:var(--color-text-secondary,#a0a0a0);border-radius:var(--radius-sm,4px);cursor:pointer;font-size:13px;transition:all .15s ease}.action-btn:hover{background:var(--border-subtle);color:var(--color-text-primary,#f5f2eb)}.action-btn.active{background:var(--color-accent-subtle);color:var(--color-accent);border-color:var(--color-accent)}.action-btn.primary{background:var(--color-accent);color:white;border-color:var(--color-accent)}.action-btn.primary:hover{background:var(--color-accent-hover,#c9843d)}.action-btn:disabled{opacity:.5;cursor:not-allowed}.editor-body{display:flex;flex:1;overflow:hidden}.editor-body.split>:is(.editor-pane,.preview-pane){flex:1;width:50%}.editor-pane{flex:1;overflow:hidden;position:relative}:global(.cm-editor){height:100%}:global(.cm-scroller){overflow:auto}.preview-pane{border-left:1px solid var(--border-subtle,#2a2a2a);overflow-y:auto;background:var(--color-bg-panel,#1a1a1a)}.preview-content{padding:16px;font-size:14px;line-height:1.6}.preview-content :is(h1,h2,h3){margin-top:0;margin-bottom:16px}.preview-content p,.preview-content ul,.preview-content ol,.preview-content blockquote{margin-bottom:12px}.preview-content ul,.preview-content ol{padding-left:24px}.preview-content li{margin-bottom:4px}.preview-content code{background:var(--color-bg-panel-elevated,#252525);padding:2px 6px;border-radius:var(--radius-sm,3px);font-family:var(--font-mono,'JetBrains Mono',monospace);font-size:.9em}.preview-content pre{background:var(--color-bg-panel-elevated,#252525);padding:12px;border-radius:var(--radius-md,6px);overflow-x:auto;margin-bottom:12px}.preview-content pre code{background:none;padding:0}.preview-content blockquote{border-left:3px solid var(--color-accent);padding-left:12px;margin-left:0;color:var(--color-text-secondary)}.preview-content a{color:var(--color-accent);text-decoration:none}.preview-content a:hover{text-decoration:underline}.error-banner{padding:8px 16px;background:var(--color-error,#ef4444);color:white;font-size:13px}
|
|
688
175
|
</style>
|