@coral-viz/viz 0.2.3
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/LICENSE +4 -0
- package/README.md +11 -0
- package/dist/compatibility/CompatibilityEdge.d.ts +106 -0
- package/dist/compatibility/CompatibilityEdge.d.ts.map +1 -0
- package/dist/compatibility/CompatibilityEdge.js +233 -0
- package/dist/compatibility/CompatibilityEdge.js.map +1 -0
- package/dist/compatibility/IncompatibilityTooltip.d.ts +44 -0
- package/dist/compatibility/IncompatibilityTooltip.d.ts.map +1 -0
- package/dist/compatibility/IncompatibilityTooltip.js +145 -0
- package/dist/compatibility/IncompatibilityTooltip.js.map +1 -0
- package/dist/compatibility/index.d.ts +10 -0
- package/dist/compatibility/index.d.ts.map +1 -0
- package/dist/compatibility/index.js +10 -0
- package/dist/compatibility/index.js.map +1 -0
- package/dist/compatibility/useEdgeCompatibility.d.ts +82 -0
- package/dist/compatibility/useEdgeCompatibility.d.ts.map +1 -0
- package/dist/compatibility/useEdgeCompatibility.js +170 -0
- package/dist/compatibility/useEdgeCompatibility.js.map +1 -0
- package/dist/compatibility/validateConnection.d.ts +56 -0
- package/dist/compatibility/validateConnection.d.ts.map +1 -0
- package/dist/compatibility/validateConnection.js +382 -0
- package/dist/compatibility/validateConnection.js.map +1 -0
- package/dist/components/EditContext.d.ts +45 -0
- package/dist/components/EditContext.d.ts.map +1 -0
- package/dist/components/EditContext.js +41 -0
- package/dist/components/EditContext.js.map +1 -0
- package/dist/components/ShapeRenderer.d.ts +35 -0
- package/dist/components/ShapeRenderer.d.ts.map +1 -0
- package/dist/components/ShapeRenderer.js +77 -0
- package/dist/components/ShapeRenderer.js.map +1 -0
- package/dist/components/SymbolNode.d.ts +53 -0
- package/dist/components/SymbolNode.d.ts.map +1 -0
- package/dist/components/SymbolNode.js +333 -0
- package/dist/components/SymbolNode.js.map +1 -0
- package/dist/components/index.d.ts +8 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +10 -0
- package/dist/components/index.js.map +1 -0
- package/dist/components/nodes.d.ts +43 -0
- package/dist/components/nodes.d.ts.map +1 -0
- package/dist/components/nodes.js +111 -0
- package/dist/components/nodes.js.map +1 -0
- package/dist/editor/CoralEditor.d.ts +29 -0
- package/dist/editor/CoralEditor.d.ts.map +1 -0
- package/dist/editor/CoralEditor.js +112 -0
- package/dist/editor/CoralEditor.js.map +1 -0
- package/dist/editor/FileControls.d.ts +34 -0
- package/dist/editor/FileControls.d.ts.map +1 -0
- package/dist/editor/FileControls.js +139 -0
- package/dist/editor/FileControls.js.map +1 -0
- package/dist/editor/LayoutControls.d.ts +41 -0
- package/dist/editor/LayoutControls.d.ts.map +1 -0
- package/dist/editor/LayoutControls.js +124 -0
- package/dist/editor/LayoutControls.js.map +1 -0
- package/dist/editor/converter.d.ts +37 -0
- package/dist/editor/converter.d.ts.map +1 -0
- package/dist/editor/converter.js +256 -0
- package/dist/editor/converter.js.map +1 -0
- package/dist/editor/index.d.ts +8 -0
- package/dist/editor/index.d.ts.map +1 -0
- package/dist/editor/index.js +8 -0
- package/dist/editor/index.js.map +1 -0
- package/dist/file/deserialize.d.ts +15 -0
- package/dist/file/deserialize.d.ts.map +1 -0
- package/dist/file/deserialize.js +145 -0
- package/dist/file/deserialize.js.map +1 -0
- package/dist/file/index.d.ts +15 -0
- package/dist/file/index.d.ts.map +1 -0
- package/dist/file/index.js +20 -0
- package/dist/file/index.js.map +1 -0
- package/dist/file/migrate.d.ts +29 -0
- package/dist/file/migrate.d.ts.map +1 -0
- package/dist/file/migrate.js +103 -0
- package/dist/file/migrate.js.map +1 -0
- package/dist/file/schema.d.ts +385 -0
- package/dist/file/schema.d.ts.map +1 -0
- package/dist/file/schema.js +180 -0
- package/dist/file/schema.js.map +1 -0
- package/dist/file/serialize.d.ts +30 -0
- package/dist/file/serialize.d.ts.map +1 -0
- package/dist/file/serialize.js +218 -0
- package/dist/file/serialize.js.map +1 -0
- package/dist/file/validate.d.ts +21 -0
- package/dist/file/validate.d.ts.map +1 -0
- package/dist/file/validate.js +165 -0
- package/dist/file/validate.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +42 -0
- package/dist/index.js.map +1 -0
- package/dist/layout/elk.d.ts +77 -0
- package/dist/layout/elk.d.ts.map +1 -0
- package/dist/layout/elk.js +379 -0
- package/dist/layout/elk.js.map +1 -0
- package/dist/layout/index.d.ts +12 -0
- package/dist/layout/index.d.ts.map +1 -0
- package/dist/layout/index.js +12 -0
- package/dist/layout/index.js.map +1 -0
- package/dist/layout/nodeSizing.d.ts +65 -0
- package/dist/layout/nodeSizing.d.ts.map +1 -0
- package/dist/layout/nodeSizing.js +319 -0
- package/dist/layout/nodeSizing.js.map +1 -0
- package/dist/layout/positionStability.d.ts +73 -0
- package/dist/layout/positionStability.d.ts.map +1 -0
- package/dist/layout/positionStability.js +199 -0
- package/dist/layout/positionStability.js.map +1 -0
- package/dist/layout/useAxisConstrain.d.ts +20 -0
- package/dist/layout/useAxisConstrain.d.ts.map +1 -0
- package/dist/layout/useAxisConstrain.js +106 -0
- package/dist/layout/useAxisConstrain.js.map +1 -0
- package/dist/layout/useDiagramState.d.ts +92 -0
- package/dist/layout/useDiagramState.d.ts.map +1 -0
- package/dist/layout/useDiagramState.js +256 -0
- package/dist/layout/useDiagramState.js.map +1 -0
- package/dist/layout/useLayoutHistory.d.ts +53 -0
- package/dist/layout/useLayoutHistory.d.ts.map +1 -0
- package/dist/layout/useLayoutHistory.js +124 -0
- package/dist/layout/useLayoutHistory.js.map +1 -0
- package/dist/properties/NodePropertiesPanel.d.ts +47 -0
- package/dist/properties/NodePropertiesPanel.d.ts.map +1 -0
- package/dist/properties/NodePropertiesPanel.js +199 -0
- package/dist/properties/NodePropertiesPanel.js.map +1 -0
- package/dist/properties/index.d.ts +10 -0
- package/dist/properties/index.d.ts.map +1 -0
- package/dist/properties/index.js +10 -0
- package/dist/properties/index.js.map +1 -0
- package/dist/properties/propertyUtils.d.ts +60 -0
- package/dist/properties/propertyUtils.d.ts.map +1 -0
- package/dist/properties/propertyUtils.js +89 -0
- package/dist/properties/propertyUtils.js.map +1 -0
- package/dist/settings/FontSettingsForm.d.ts +28 -0
- package/dist/settings/FontSettingsForm.d.ts.map +1 -0
- package/dist/settings/FontSettingsForm.js +129 -0
- package/dist/settings/FontSettingsForm.js.map +1 -0
- package/dist/settings/LayoutSettingsForm.d.ts +37 -0
- package/dist/settings/LayoutSettingsForm.d.ts.map +1 -0
- package/dist/settings/LayoutSettingsForm.js +165 -0
- package/dist/settings/LayoutSettingsForm.js.map +1 -0
- package/dist/settings/SettingsPanel.d.ts +55 -0
- package/dist/settings/SettingsPanel.d.ts.map +1 -0
- package/dist/settings/SettingsPanel.js +84 -0
- package/dist/settings/SettingsPanel.js.map +1 -0
- package/dist/settings/index.d.ts +13 -0
- package/dist/settings/index.d.ts.map +1 -0
- package/dist/settings/index.js +13 -0
- package/dist/settings/index.js.map +1 -0
- package/dist/settings/useSettings.d.ts +51 -0
- package/dist/settings/useSettings.d.ts.map +1 -0
- package/dist/settings/useSettings.js +154 -0
- package/dist/settings/useSettings.js.map +1 -0
- package/dist/text-editor/SplitEditor.d.ts +72 -0
- package/dist/text-editor/SplitEditor.d.ts.map +1 -0
- package/dist/text-editor/SplitEditor.js +172 -0
- package/dist/text-editor/SplitEditor.js.map +1 -0
- package/dist/text-editor/TextEditor.d.ts +53 -0
- package/dist/text-editor/TextEditor.d.ts.map +1 -0
- package/dist/text-editor/TextEditor.js +181 -0
- package/dist/text-editor/TextEditor.js.map +1 -0
- package/dist/text-editor/hooks/useBidirectionalSync.d.ts +106 -0
- package/dist/text-editor/hooks/useBidirectionalSync.d.ts.map +1 -0
- package/dist/text-editor/hooks/useBidirectionalSync.js +151 -0
- package/dist/text-editor/hooks/useBidirectionalSync.js.map +1 -0
- package/dist/text-editor/index.d.ts +9 -0
- package/dist/text-editor/index.d.ts.map +1 -0
- package/dist/text-editor/index.js +11 -0
- package/dist/text-editor/index.js.map +1 -0
- package/dist/types.d.ts +242 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/dist/waypoints/index.d.ts +8 -0
- package/dist/waypoints/index.d.ts.map +1 -0
- package/dist/waypoints/index.js +7 -0
- package/dist/waypoints/index.js.map +1 -0
- package/dist/waypoints/operations.d.ts +29 -0
- package/dist/waypoints/operations.d.ts.map +1 -0
- package/dist/waypoints/operations.js +85 -0
- package/dist/waypoints/operations.js.map +1 -0
- package/dist/waypoints/router.d.ts +23 -0
- package/dist/waypoints/router.d.ts.map +1 -0
- package/dist/waypoints/router.js +109 -0
- package/dist/waypoints/router.js.map +1 -0
- package/dist/waypoints/types.d.ts +15 -0
- package/dist/waypoints/types.d.ts.map +1 -0
- package/dist/waypoints/types.js +5 -0
- package/dist/waypoints/types.js.map +1 -0
- package/dist/waypoints/useWaypointManager.d.ts +38 -0
- package/dist/waypoints/useWaypointManager.d.ts.map +1 -0
- package/dist/waypoints/useWaypointManager.js +106 -0
- package/dist/waypoints/useWaypointManager.js.map +1 -0
- package/package.json +76 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useSettings Hook (CORAL-REQ-009)
|
|
3
|
+
*
|
|
4
|
+
* Manages settings state with two tiers:
|
|
5
|
+
* - Document Settings: Saved with the diagram file
|
|
6
|
+
* - User Preferences: Saved to localStorage
|
|
7
|
+
*/
|
|
8
|
+
import { useState, useCallback, useEffect, useMemo } from 'react';
|
|
9
|
+
import { DEFAULT_USER_PREFERENCES, DEFAULT_LAYOUT_SETTINGS, LAYOUT_PRESETS, getLayoutPreset, } from '../file/schema.js';
|
|
10
|
+
const STORAGE_KEY = 'coral:user-preferences';
|
|
11
|
+
/**
|
|
12
|
+
* Load user preferences from localStorage
|
|
13
|
+
*/
|
|
14
|
+
function loadUserPreferences() {
|
|
15
|
+
if (typeof window === 'undefined' || !window.localStorage) {
|
|
16
|
+
return DEFAULT_USER_PREFERENCES;
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
const stored = localStorage.getItem(STORAGE_KEY);
|
|
20
|
+
if (!stored) {
|
|
21
|
+
return DEFAULT_USER_PREFERENCES;
|
|
22
|
+
}
|
|
23
|
+
const parsed = JSON.parse(stored);
|
|
24
|
+
// Merge with defaults to handle missing fields
|
|
25
|
+
return { ...DEFAULT_USER_PREFERENCES, ...parsed };
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
// Invalid JSON or other error - use defaults
|
|
29
|
+
return DEFAULT_USER_PREFERENCES;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Save user preferences to localStorage
|
|
34
|
+
*/
|
|
35
|
+
function saveUserPreferences(prefs) {
|
|
36
|
+
if (typeof window === 'undefined' || !window.localStorage) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify(prefs));
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// Storage full or other error - ignore
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Detect which preset matches the current layout settings
|
|
48
|
+
*/
|
|
49
|
+
function detectPreset(layout) {
|
|
50
|
+
// Check each preset (except custom) to see if settings match
|
|
51
|
+
for (const preset of LAYOUT_PRESETS) {
|
|
52
|
+
if (preset.id === 'custom')
|
|
53
|
+
continue;
|
|
54
|
+
const presetLayout = preset.settings;
|
|
55
|
+
// Compare key fields
|
|
56
|
+
if (layout.algorithm === presetLayout.algorithm &&
|
|
57
|
+
layout.direction === presetLayout.direction) {
|
|
58
|
+
return preset.id;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return 'custom';
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Hook for managing settings with localStorage persistence for user preferences
|
|
65
|
+
*/
|
|
66
|
+
export function useSettings(options = {}) {
|
|
67
|
+
const { initialDocumentSettings, initialUserPreferences, onDocumentSettingsChange, onLayoutChange, } = options;
|
|
68
|
+
// User preferences state (persisted to localStorage)
|
|
69
|
+
const [userPreferences, setUserPreferences] = useState(() => {
|
|
70
|
+
if (initialUserPreferences) {
|
|
71
|
+
return { ...DEFAULT_USER_PREFERENCES, ...initialUserPreferences };
|
|
72
|
+
}
|
|
73
|
+
return loadUserPreferences();
|
|
74
|
+
});
|
|
75
|
+
// Document settings state (NOT persisted - saved with document)
|
|
76
|
+
const [documentSettings, setDocumentSettings] = useState(() => {
|
|
77
|
+
if (initialDocumentSettings) {
|
|
78
|
+
return initialDocumentSettings;
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
notation: userPreferences.defaultNotation,
|
|
82
|
+
layout: DEFAULT_LAYOUT_SETTINGS,
|
|
83
|
+
};
|
|
84
|
+
});
|
|
85
|
+
// Detect current preset based on layout settings
|
|
86
|
+
const selectedPreset = useMemo(() => detectPreset(documentSettings.layout), [documentSettings.layout]);
|
|
87
|
+
// Persist user preferences to localStorage when they change
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
saveUserPreferences(userPreferences);
|
|
90
|
+
}, [userPreferences]);
|
|
91
|
+
// Update user preferences
|
|
92
|
+
const updateUserPreferences = useCallback((updates) => {
|
|
93
|
+
setUserPreferences((prev) => ({ ...prev, ...updates }));
|
|
94
|
+
}, []);
|
|
95
|
+
// Reset user preferences to defaults
|
|
96
|
+
const resetUserPreferences = useCallback(() => {
|
|
97
|
+
setUserPreferences(DEFAULT_USER_PREFERENCES);
|
|
98
|
+
}, []);
|
|
99
|
+
// Update document settings
|
|
100
|
+
const updateDocumentSettings = useCallback((settings) => {
|
|
101
|
+
setDocumentSettings(settings);
|
|
102
|
+
onDocumentSettingsChange?.(settings);
|
|
103
|
+
}, [onDocumentSettingsChange]);
|
|
104
|
+
// Apply a layout preset
|
|
105
|
+
const applyLayoutPreset = useCallback((presetId) => {
|
|
106
|
+
const preset = getLayoutPreset(presetId);
|
|
107
|
+
if (!preset)
|
|
108
|
+
return;
|
|
109
|
+
const newSettings = {
|
|
110
|
+
...documentSettings,
|
|
111
|
+
layout: { ...preset.settings },
|
|
112
|
+
};
|
|
113
|
+
setDocumentSettings(newSettings);
|
|
114
|
+
onDocumentSettingsChange?.(newSettings);
|
|
115
|
+
onLayoutChange?.(preset.settings);
|
|
116
|
+
}, [documentSettings, onDocumentSettingsChange, onLayoutChange]);
|
|
117
|
+
// Add file to recent files
|
|
118
|
+
const addRecentFile = useCallback((filePath) => {
|
|
119
|
+
setUserPreferences((prev) => {
|
|
120
|
+
// Remove if already exists
|
|
121
|
+
const filtered = prev.recentFiles.filter((f) => f !== filePath);
|
|
122
|
+
// Add to front
|
|
123
|
+
const updated = [filePath, ...filtered];
|
|
124
|
+
// Limit to max
|
|
125
|
+
const limited = updated.slice(0, prev.maxRecentFiles);
|
|
126
|
+
return { ...prev, recentFiles: limited };
|
|
127
|
+
});
|
|
128
|
+
}, []);
|
|
129
|
+
// Clear recent files
|
|
130
|
+
const clearRecentFiles = useCallback(() => {
|
|
131
|
+
setUserPreferences((prev) => ({ ...prev, recentFiles: [] }));
|
|
132
|
+
}, []);
|
|
133
|
+
// Get combined settings panel state
|
|
134
|
+
const getSettingsPanelState = useCallback(() => {
|
|
135
|
+
return {
|
|
136
|
+
document: documentSettings,
|
|
137
|
+
user: userPreferences,
|
|
138
|
+
selectedPreset,
|
|
139
|
+
};
|
|
140
|
+
}, [documentSettings, userPreferences, selectedPreset]);
|
|
141
|
+
return {
|
|
142
|
+
userPreferences,
|
|
143
|
+
documentSettings,
|
|
144
|
+
selectedPreset,
|
|
145
|
+
updateUserPreferences,
|
|
146
|
+
resetUserPreferences,
|
|
147
|
+
updateDocumentSettings,
|
|
148
|
+
applyLayoutPreset,
|
|
149
|
+
addRecentFile,
|
|
150
|
+
clearRecentFiles,
|
|
151
|
+
getSettingsPanelState,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=useSettings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useSettings.js","sourceRoot":"","sources":["../../src/settings/useSettings.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SplitEditor - Combined text and visual editor with bidirectional sync
|
|
3
|
+
*
|
|
4
|
+
* Renders a split view with text editor on one side and visual diagram
|
|
5
|
+
* on the other, keeping them in sync through Graph-IR.
|
|
6
|
+
*/
|
|
7
|
+
import React from 'react';
|
|
8
|
+
import { type SyntaxLanguage } from './TextEditor.js';
|
|
9
|
+
import { type ParseFunction, type PrintFunction, type ParseError } from './hooks/useBidirectionalSync.js';
|
|
10
|
+
import type { GraphIR, EditorConfig } from '../types.js';
|
|
11
|
+
/**
|
|
12
|
+
* Layout orientation for the split view
|
|
13
|
+
*/
|
|
14
|
+
export type SplitLayout = 'horizontal' | 'vertical';
|
|
15
|
+
/**
|
|
16
|
+
* Which pane is on the left/top
|
|
17
|
+
*/
|
|
18
|
+
export type PrimaryPane = 'text' | 'visual';
|
|
19
|
+
/**
|
|
20
|
+
* SplitEditor props
|
|
21
|
+
*/
|
|
22
|
+
export interface SplitEditorProps {
|
|
23
|
+
/** Initial text content */
|
|
24
|
+
initialText?: string;
|
|
25
|
+
/** Initial Graph-IR (if provided, takes precedence over text) */
|
|
26
|
+
initialGraph?: GraphIR;
|
|
27
|
+
/** Parser function (text → Graph-IR) */
|
|
28
|
+
parse: ParseFunction;
|
|
29
|
+
/** Printer function (Graph-IR → text) */
|
|
30
|
+
print: PrintFunction;
|
|
31
|
+
/** Syntax language for text editor */
|
|
32
|
+
language?: SyntaxLanguage;
|
|
33
|
+
/** Layout orientation */
|
|
34
|
+
layout?: SplitLayout;
|
|
35
|
+
/** Which pane is primary (left/top) */
|
|
36
|
+
primaryPane?: PrimaryPane;
|
|
37
|
+
/** Split ratio (0-1, proportion of primary pane) */
|
|
38
|
+
splitRatio?: number;
|
|
39
|
+
/** Show resize handle between panes */
|
|
40
|
+
resizable?: boolean;
|
|
41
|
+
/** Visual editor configuration */
|
|
42
|
+
editorConfig?: EditorConfig;
|
|
43
|
+
/** Debounce delay for sync (ms) */
|
|
44
|
+
debounceMs?: number;
|
|
45
|
+
/** Callback when graph changes */
|
|
46
|
+
onGraphChange?: (graph: GraphIR | null) => void;
|
|
47
|
+
/** Callback when text changes */
|
|
48
|
+
onTextChange?: (text: string) => void;
|
|
49
|
+
/** Callback when errors change */
|
|
50
|
+
onErrorsChange?: (errors: ParseError[]) => void;
|
|
51
|
+
/** CSS class name */
|
|
52
|
+
className?: string;
|
|
53
|
+
/** Inline styles */
|
|
54
|
+
style?: React.CSSProperties;
|
|
55
|
+
/** Minimum pane size (px) */
|
|
56
|
+
minPaneSize?: number;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* SplitEditor component
|
|
60
|
+
*
|
|
61
|
+
* A split-pane editor that shows both text DSL and visual diagram,
|
|
62
|
+
* keeping them synchronized through Graph-IR.
|
|
63
|
+
*
|
|
64
|
+
* Features:
|
|
65
|
+
* - Bidirectional sync between text and visual
|
|
66
|
+
* - Configurable layout (horizontal/vertical)
|
|
67
|
+
* - Resizable panes
|
|
68
|
+
* - Parse error display
|
|
69
|
+
*/
|
|
70
|
+
export declare const SplitEditor: React.FC<SplitEditorProps>;
|
|
71
|
+
export default SplitEditor;
|
|
72
|
+
//# sourceMappingURL=SplitEditor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SplitEditor.d.ts","sourceRoot":"","sources":["../../src/text-editor/SplitEditor.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAyC,MAAM,OAAO,CAAC;AAE9D,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAClE,OAAO,EAEL,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,UAAU,EAChB,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EAAE,OAAO,EAAwB,YAAY,EAAmB,MAAM,aAAa,CAAC;AAEhG;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,YAAY,GAAG,UAAU,CAAC;AAEpD;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE5C;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,2BAA2B;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iEAAiE;IACjE,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,wCAAwC;IACxC,KAAK,EAAE,aAAa,CAAC;IACrB,yCAAyC;IACzC,KAAK,EAAE,aAAa,CAAC;IACrB,sCAAsC;IACtC,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,yBAAyB;IACzB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,uCAAuC;IACvC,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,oDAAoD;IACpD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uCAAuC;IACvC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,kCAAkC;IAClC,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,mCAAmC;IACnC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kCAAkC;IAClC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,KAAK,IAAI,CAAC;IAChD,iCAAiC;IACjC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,kCAAkC;IAClC,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,IAAI,CAAC;IAChD,qBAAqB;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oBAAoB;IACpB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5B,6BAA6B;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAgCD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAqNlD,CAAC;AAEF,eAAe,WAAW,CAAC"}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* SplitEditor - Combined text and visual editor with bidirectional sync
|
|
4
|
+
*
|
|
5
|
+
* Renders a split view with text editor on one side and visual diagram
|
|
6
|
+
* on the other, keeping them in sync through Graph-IR.
|
|
7
|
+
*/
|
|
8
|
+
import React, { useCallback, useMemo, useState } from 'react';
|
|
9
|
+
import { CoralEditor } from '../editor/CoralEditor.js';
|
|
10
|
+
import { TextEditor } from './TextEditor.js';
|
|
11
|
+
import { useBidirectionalSync, } from './hooks/useBidirectionalSync.js';
|
|
12
|
+
/**
|
|
13
|
+
* Container styles for split layout
|
|
14
|
+
*/
|
|
15
|
+
const containerStyles = {
|
|
16
|
+
display: 'flex',
|
|
17
|
+
width: '100%',
|
|
18
|
+
height: '100%',
|
|
19
|
+
minHeight: '400px',
|
|
20
|
+
overflow: 'hidden',
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Pane styles
|
|
24
|
+
*/
|
|
25
|
+
const paneStyles = {
|
|
26
|
+
overflow: 'auto',
|
|
27
|
+
display: 'flex',
|
|
28
|
+
flexDirection: 'column',
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Resize handle styles
|
|
32
|
+
*/
|
|
33
|
+
const resizeHandleStyles = {
|
|
34
|
+
backgroundColor: '#e0e0e0',
|
|
35
|
+
cursor: 'col-resize',
|
|
36
|
+
transition: 'background-color 0.2s',
|
|
37
|
+
flexShrink: 0,
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* SplitEditor component
|
|
41
|
+
*
|
|
42
|
+
* A split-pane editor that shows both text DSL and visual diagram,
|
|
43
|
+
* keeping them synchronized through Graph-IR.
|
|
44
|
+
*
|
|
45
|
+
* Features:
|
|
46
|
+
* - Bidirectional sync between text and visual
|
|
47
|
+
* - Configurable layout (horizontal/vertical)
|
|
48
|
+
* - Resizable panes
|
|
49
|
+
* - Parse error display
|
|
50
|
+
*/
|
|
51
|
+
export const SplitEditor = ({ initialText = '', initialGraph, parse, print, language = 'coral', layout = 'horizontal', primaryPane = 'text', splitRatio: initialSplitRatio = 0.5, resizable = true, editorConfig, debounceMs = 100, onGraphChange, onTextChange, onErrorsChange, className, style, minPaneSize = 200, }) => {
|
|
52
|
+
// Split ratio state for resizing
|
|
53
|
+
const [splitRatio, setSplitRatio] = useState(initialSplitRatio);
|
|
54
|
+
const [isResizing, setIsResizing] = useState(false);
|
|
55
|
+
// Bidirectional sync hook
|
|
56
|
+
const [syncState, syncActions] = useBidirectionalSync({
|
|
57
|
+
initialText,
|
|
58
|
+
initialGraph,
|
|
59
|
+
parse,
|
|
60
|
+
print,
|
|
61
|
+
debounceMs,
|
|
62
|
+
onGraphChange,
|
|
63
|
+
onTextChange,
|
|
64
|
+
onErrorsChange,
|
|
65
|
+
});
|
|
66
|
+
// Handle text editor changes
|
|
67
|
+
const handleTextChange = useCallback((newText) => {
|
|
68
|
+
syncActions.setText(newText);
|
|
69
|
+
}, [syncActions]);
|
|
70
|
+
// Handle visual editor node changes
|
|
71
|
+
const handleNodesChange = useCallback((nodes) => {
|
|
72
|
+
if (syncState.graph) {
|
|
73
|
+
// Get current edges from the visual editor state
|
|
74
|
+
// For now, just update nodes - full implementation would track edges too
|
|
75
|
+
const updatedGraph = {
|
|
76
|
+
...syncState.graph,
|
|
77
|
+
nodes: nodes.map((node) => ({
|
|
78
|
+
id: node.id,
|
|
79
|
+
type: node.data.nodeType,
|
|
80
|
+
label: node.data.label,
|
|
81
|
+
description: node.data.description,
|
|
82
|
+
properties: node.data.properties,
|
|
83
|
+
position: node.position,
|
|
84
|
+
dimensions: node.measured
|
|
85
|
+
? { width: node.measured.width ?? 0, height: node.measured.height ?? 0 }
|
|
86
|
+
: undefined,
|
|
87
|
+
})),
|
|
88
|
+
};
|
|
89
|
+
syncActions.setGraph(updatedGraph);
|
|
90
|
+
}
|
|
91
|
+
}, [syncState.graph, syncActions]);
|
|
92
|
+
// Editor callbacks for visual editor
|
|
93
|
+
const editorCallbacks = useMemo(() => ({
|
|
94
|
+
onNodesChange: handleNodesChange,
|
|
95
|
+
}), [handleNodesChange]);
|
|
96
|
+
// Handle resize drag
|
|
97
|
+
const handleResizeStart = useCallback((e) => {
|
|
98
|
+
e.preventDefault();
|
|
99
|
+
setIsResizing(true);
|
|
100
|
+
}, []);
|
|
101
|
+
// Resize mouse move handler
|
|
102
|
+
React.useEffect(() => {
|
|
103
|
+
if (!isResizing)
|
|
104
|
+
return;
|
|
105
|
+
const handleMouseMove = (e) => {
|
|
106
|
+
const container = document.querySelector('.coral-split-editor');
|
|
107
|
+
if (!container)
|
|
108
|
+
return;
|
|
109
|
+
const rect = container.getBoundingClientRect();
|
|
110
|
+
const isHorizontal = layout === 'horizontal';
|
|
111
|
+
const position = isHorizontal ? e.clientX - rect.left : e.clientY - rect.top;
|
|
112
|
+
const total = isHorizontal ? rect.width : rect.height;
|
|
113
|
+
const newRatio = Math.max(minPaneSize / total, Math.min(1 - minPaneSize / total, position / total));
|
|
114
|
+
setSplitRatio(newRatio);
|
|
115
|
+
};
|
|
116
|
+
const handleMouseUp = () => {
|
|
117
|
+
setIsResizing(false);
|
|
118
|
+
};
|
|
119
|
+
document.addEventListener('mousemove', handleMouseMove);
|
|
120
|
+
document.addEventListener('mouseup', handleMouseUp);
|
|
121
|
+
return () => {
|
|
122
|
+
document.removeEventListener('mousemove', handleMouseMove);
|
|
123
|
+
document.removeEventListener('mouseup', handleMouseUp);
|
|
124
|
+
};
|
|
125
|
+
}, [isResizing, layout, minPaneSize]);
|
|
126
|
+
// Compute pane dimensions
|
|
127
|
+
const primarySize = `${splitRatio * 100}%`;
|
|
128
|
+
const secondarySize = `${(1 - splitRatio) * 100}%`;
|
|
129
|
+
// Build pane content
|
|
130
|
+
const textPane = (_jsx("div", { className: "coral-split-editor-text-pane", style: {
|
|
131
|
+
...paneStyles,
|
|
132
|
+
width: layout === 'horizontal' ? primaryPane === 'text' ? primarySize : secondarySize : '100%',
|
|
133
|
+
height: layout === 'vertical' ? primaryPane === 'text' ? primarySize : secondarySize : '100%',
|
|
134
|
+
flexShrink: 0,
|
|
135
|
+
}, children: _jsx(TextEditor, { value: syncState.text, onChange: handleTextChange, language: language, errors: syncState.errors, showLineNumbers: true, style: { flex: 1, minHeight: 0 } }) }));
|
|
136
|
+
const visualPane = (_jsx("div", { className: "coral-split-editor-visual-pane", style: {
|
|
137
|
+
...paneStyles,
|
|
138
|
+
width: layout === 'horizontal' ? primaryPane === 'visual' ? primarySize : secondarySize : '100%',
|
|
139
|
+
height: layout === 'vertical' ? primaryPane === 'visual' ? primarySize : secondarySize : '100%',
|
|
140
|
+
flex: 1,
|
|
141
|
+
minWidth: 0,
|
|
142
|
+
minHeight: 0,
|
|
143
|
+
}, children: syncState.graph ? (_jsx(CoralEditor, { graph: syncState.graph, config: editorConfig, callbacks: editorCallbacks, style: { width: '100%', height: '100%' } })) : (_jsx("div", { style: {
|
|
144
|
+
display: 'flex',
|
|
145
|
+
alignItems: 'center',
|
|
146
|
+
justifyContent: 'center',
|
|
147
|
+
height: '100%',
|
|
148
|
+
color: '#999',
|
|
149
|
+
fontStyle: 'italic',
|
|
150
|
+
}, children: syncState.errors.length > 0
|
|
151
|
+
? 'Fix syntax errors to see the diagram'
|
|
152
|
+
: 'Enter diagram code to see the visualization' })) }));
|
|
153
|
+
// Resize handle
|
|
154
|
+
const resizeHandle = resizable ? (_jsx("div", { className: "coral-split-editor-resize-handle", onMouseDown: handleResizeStart, style: {
|
|
155
|
+
...resizeHandleStyles,
|
|
156
|
+
width: layout === 'horizontal' ? '6px' : '100%',
|
|
157
|
+
height: layout === 'horizontal' ? '100%' : '6px',
|
|
158
|
+
cursor: layout === 'horizontal' ? 'col-resize' : 'row-resize',
|
|
159
|
+
backgroundColor: isResizing ? '#bdbdbd' : '#e0e0e0',
|
|
160
|
+
} })) : null;
|
|
161
|
+
// Order panes based on primary
|
|
162
|
+
const firstPane = primaryPane === 'text' ? textPane : visualPane;
|
|
163
|
+
const secondPane = primaryPane === 'text' ? visualPane : textPane;
|
|
164
|
+
return (_jsxs("div", { className: `coral-split-editor ${className || ''}`, style: {
|
|
165
|
+
...containerStyles,
|
|
166
|
+
...style,
|
|
167
|
+
flexDirection: layout === 'horizontal' ? 'row' : 'column',
|
|
168
|
+
userSelect: isResizing ? 'none' : undefined,
|
|
169
|
+
}, children: [firstPane, resizeHandle, secondPane] }));
|
|
170
|
+
};
|
|
171
|
+
export default SplitEditor;
|
|
172
|
+
//# sourceMappingURL=SplitEditor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SplitEditor.js","sourceRoot":"","sources":["../../src/text-editor/SplitEditor.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TextEditor - DSL text editor component
|
|
3
|
+
*
|
|
4
|
+
* A code editor for Coral DSL, Mermaid, and DOT syntax with
|
|
5
|
+
* inline error display and syntax highlighting.
|
|
6
|
+
*/
|
|
7
|
+
import React from 'react';
|
|
8
|
+
import type { ParseError } from './hooks/useBidirectionalSync.js';
|
|
9
|
+
/**
|
|
10
|
+
* Supported syntax languages
|
|
11
|
+
*/
|
|
12
|
+
export type SyntaxLanguage = 'coral' | 'mermaid' | 'dot' | 'plain';
|
|
13
|
+
/**
|
|
14
|
+
* TextEditor props
|
|
15
|
+
*/
|
|
16
|
+
export interface TextEditorProps {
|
|
17
|
+
/** Current text content */
|
|
18
|
+
value: string;
|
|
19
|
+
/** Callback when text changes */
|
|
20
|
+
onChange: (value: string) => void;
|
|
21
|
+
/** Syntax language for highlighting */
|
|
22
|
+
language?: SyntaxLanguage;
|
|
23
|
+
/** Parse errors to display inline */
|
|
24
|
+
errors?: ParseError[];
|
|
25
|
+
/** Placeholder text */
|
|
26
|
+
placeholder?: string;
|
|
27
|
+
/** Read-only mode */
|
|
28
|
+
readOnly?: boolean;
|
|
29
|
+
/** CSS class name */
|
|
30
|
+
className?: string;
|
|
31
|
+
/** Inline styles */
|
|
32
|
+
style?: React.CSSProperties;
|
|
33
|
+
/** Line numbers */
|
|
34
|
+
showLineNumbers?: boolean;
|
|
35
|
+
/** Auto-focus on mount */
|
|
36
|
+
autoFocus?: boolean;
|
|
37
|
+
/** Tab size */
|
|
38
|
+
tabSize?: number;
|
|
39
|
+
/** Minimum height */
|
|
40
|
+
minHeight?: string;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* TextEditor component
|
|
44
|
+
*
|
|
45
|
+
* Provides a code editing experience for diagram DSLs with:
|
|
46
|
+
* - Syntax-aware editing
|
|
47
|
+
* - Inline error display
|
|
48
|
+
* - Optional line numbers
|
|
49
|
+
* - Keyboard shortcuts (Tab indentation)
|
|
50
|
+
*/
|
|
51
|
+
export declare const TextEditor: React.FC<TextEditorProps>;
|
|
52
|
+
export default TextEditor;
|
|
53
|
+
//# sourceMappingURL=TextEditor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TextEditor.d.ts","sourceRoot":"","sources":["../../src/text-editor/TextEditor.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAkD,MAAM,OAAO,CAAC;AACvE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAElE;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,SAAS,GAAG,KAAK,GAAG,OAAO,CAAC;AAEnE;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,2BAA2B;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,iCAAiC;IACjC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,uCAAuC;IACvC,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,qCAAqC;IACrC,MAAM,CAAC,EAAE,UAAU,EAAE,CAAC;IACtB,uBAAuB;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qBAAqB;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oBAAoB;IACpB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5B,mBAAmB;IACnB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,0BAA0B;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,eAAe;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qBAAqB;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAwFD;;;;;;;;GAQG;AACH,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CAoLhD,CAAC;AAEF,eAAe,UAAU,CAAC"}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* TextEditor - DSL text editor component
|
|
4
|
+
*
|
|
5
|
+
* A code editor for Coral DSL, Mermaid, and DOT syntax with
|
|
6
|
+
* inline error display and syntax highlighting.
|
|
7
|
+
*/
|
|
8
|
+
import { useCallback, useRef, useEffect, useMemo } from 'react';
|
|
9
|
+
/**
|
|
10
|
+
* Get line and column from text offset
|
|
11
|
+
*/
|
|
12
|
+
function getPositionFromOffset(text, offset) {
|
|
13
|
+
const lines = text.slice(0, offset).split('\n');
|
|
14
|
+
return {
|
|
15
|
+
line: lines.length,
|
|
16
|
+
column: lines[lines.length - 1].length + 1,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Get offset from line and column
|
|
21
|
+
*/
|
|
22
|
+
function getOffsetFromPosition(text, line, column) {
|
|
23
|
+
const lines = text.split('\n');
|
|
24
|
+
let offset = 0;
|
|
25
|
+
for (let i = 0; i < line - 1 && i < lines.length; i++) {
|
|
26
|
+
offset += lines[i].length + 1; // +1 for newline
|
|
27
|
+
}
|
|
28
|
+
return offset + column - 1;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Default styles for the editor
|
|
32
|
+
*/
|
|
33
|
+
const defaultStyles = {
|
|
34
|
+
fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Monaco, Consolas, monospace',
|
|
35
|
+
fontSize: '14px',
|
|
36
|
+
lineHeight: '1.5',
|
|
37
|
+
padding: '12px',
|
|
38
|
+
border: '1px solid #e0e0e0',
|
|
39
|
+
borderRadius: '4px',
|
|
40
|
+
outline: 'none',
|
|
41
|
+
resize: 'vertical',
|
|
42
|
+
width: '100%',
|
|
43
|
+
boxSizing: 'border-box',
|
|
44
|
+
backgroundColor: '#fafafa',
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Error marker styles
|
|
48
|
+
*/
|
|
49
|
+
const errorMarkerStyles = {
|
|
50
|
+
position: 'absolute',
|
|
51
|
+
backgroundColor: 'rgba(255, 0, 0, 0.1)',
|
|
52
|
+
borderBottom: '2px wavy red',
|
|
53
|
+
pointerEvents: 'none',
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Error tooltip styles
|
|
57
|
+
*/
|
|
58
|
+
const errorTooltipStyles = {
|
|
59
|
+
position: 'absolute',
|
|
60
|
+
backgroundColor: '#ffebee',
|
|
61
|
+
color: '#c62828',
|
|
62
|
+
padding: '4px 8px',
|
|
63
|
+
borderRadius: '4px',
|
|
64
|
+
fontSize: '12px',
|
|
65
|
+
boxShadow: '0 2px 4px rgba(0,0,0,0.2)',
|
|
66
|
+
zIndex: 1000,
|
|
67
|
+
maxWidth: '300px',
|
|
68
|
+
whiteSpace: 'pre-wrap',
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Line number gutter styles
|
|
72
|
+
*/
|
|
73
|
+
const lineNumberStyles = {
|
|
74
|
+
position: 'absolute',
|
|
75
|
+
left: 0,
|
|
76
|
+
top: 0,
|
|
77
|
+
width: '40px',
|
|
78
|
+
backgroundColor: '#f5f5f5',
|
|
79
|
+
borderRight: '1px solid #e0e0e0',
|
|
80
|
+
textAlign: 'right',
|
|
81
|
+
paddingRight: '8px',
|
|
82
|
+
paddingTop: '12px',
|
|
83
|
+
fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Monaco, Consolas, monospace',
|
|
84
|
+
fontSize: '12px',
|
|
85
|
+
color: '#999',
|
|
86
|
+
userSelect: 'none',
|
|
87
|
+
lineHeight: '1.5',
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* TextEditor component
|
|
91
|
+
*
|
|
92
|
+
* Provides a code editing experience for diagram DSLs with:
|
|
93
|
+
* - Syntax-aware editing
|
|
94
|
+
* - Inline error display
|
|
95
|
+
* - Optional line numbers
|
|
96
|
+
* - Keyboard shortcuts (Tab indentation)
|
|
97
|
+
*/
|
|
98
|
+
export const TextEditor = ({ value, onChange, language = 'coral', errors = [], placeholder = 'Enter diagram code...', readOnly = false, className, style, showLineNumbers = true, autoFocus = false, tabSize = 2, minHeight = '200px', }) => {
|
|
99
|
+
const textareaRef = useRef(null);
|
|
100
|
+
const containerRef = useRef(null);
|
|
101
|
+
// Calculate line numbers
|
|
102
|
+
const lineCount = useMemo(() => value.split('\n').length, [value]);
|
|
103
|
+
const lineNumbers = useMemo(() => Array.from({ length: lineCount }, (_, i) => i + 1), [lineCount]);
|
|
104
|
+
// Handle text changes
|
|
105
|
+
const handleChange = useCallback((e) => {
|
|
106
|
+
onChange(e.target.value);
|
|
107
|
+
}, [onChange]);
|
|
108
|
+
// Handle tab key for indentation
|
|
109
|
+
const handleKeyDown = useCallback((e) => {
|
|
110
|
+
if (e.key === 'Tab') {
|
|
111
|
+
e.preventDefault();
|
|
112
|
+
const textarea = e.currentTarget;
|
|
113
|
+
const start = textarea.selectionStart;
|
|
114
|
+
const end = textarea.selectionEnd;
|
|
115
|
+
const spaces = ' '.repeat(tabSize);
|
|
116
|
+
// Insert spaces at cursor
|
|
117
|
+
const newValue = value.slice(0, start) + spaces + value.slice(end);
|
|
118
|
+
onChange(newValue);
|
|
119
|
+
// Move cursor after inserted spaces
|
|
120
|
+
requestAnimationFrame(() => {
|
|
121
|
+
textarea.selectionStart = textarea.selectionEnd = start + tabSize;
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}, [value, onChange, tabSize]);
|
|
125
|
+
// Auto-focus
|
|
126
|
+
useEffect(() => {
|
|
127
|
+
if (autoFocus && textareaRef.current) {
|
|
128
|
+
textareaRef.current.focus();
|
|
129
|
+
}
|
|
130
|
+
}, [autoFocus]);
|
|
131
|
+
// Find errors for display
|
|
132
|
+
const displayErrors = useMemo(() => {
|
|
133
|
+
return errors.map((error) => ({
|
|
134
|
+
...error,
|
|
135
|
+
// Ensure we have line/column
|
|
136
|
+
line: error.line || 1,
|
|
137
|
+
column: error.column || 1,
|
|
138
|
+
}));
|
|
139
|
+
}, [errors]);
|
|
140
|
+
// Group errors by line for inline display
|
|
141
|
+
const errorsByLine = useMemo(() => {
|
|
142
|
+
const byLine = new Map();
|
|
143
|
+
for (const error of displayErrors) {
|
|
144
|
+
const existing = byLine.get(error.line) || [];
|
|
145
|
+
existing.push(error);
|
|
146
|
+
byLine.set(error.line, existing);
|
|
147
|
+
}
|
|
148
|
+
return byLine;
|
|
149
|
+
}, [displayErrors]);
|
|
150
|
+
// Compute textarea styles with line number offset
|
|
151
|
+
const textareaStyles = useMemo(() => ({
|
|
152
|
+
...defaultStyles,
|
|
153
|
+
...style,
|
|
154
|
+
minHeight,
|
|
155
|
+
paddingLeft: showLineNumbers ? '52px' : '12px',
|
|
156
|
+
}), [style, minHeight, showLineNumbers]);
|
|
157
|
+
return (_jsxs("div", { ref: containerRef, className: `coral-text-editor ${className || ''}`, style: {
|
|
158
|
+
position: 'relative',
|
|
159
|
+
width: '100%',
|
|
160
|
+
}, children: [showLineNumbers && (_jsx("div", { className: "coral-text-editor-line-numbers", style: {
|
|
161
|
+
...lineNumberStyles,
|
|
162
|
+
minHeight,
|
|
163
|
+
}, children: lineNumbers.map((num) => (_jsx("div", { style: {
|
|
164
|
+
height: '21px', // Match line height
|
|
165
|
+
color: errorsByLine.has(num) ? '#c62828' : '#999',
|
|
166
|
+
fontWeight: errorsByLine.has(num) ? 'bold' : 'normal',
|
|
167
|
+
}, children: num }, num))) })), _jsx("textarea", { ref: textareaRef, value: value, onChange: handleChange, onKeyDown: handleKeyDown, placeholder: placeholder, readOnly: readOnly, spellCheck: false, autoComplete: "off", autoCorrect: "off", autoCapitalize: "off", "data-language": language, style: textareaStyles }), displayErrors.length > 0 && (_jsxs("div", { className: "coral-text-editor-errors", style: {
|
|
168
|
+
marginTop: '8px',
|
|
169
|
+
padding: '8px 12px',
|
|
170
|
+
backgroundColor: '#ffebee',
|
|
171
|
+
borderRadius: '4px',
|
|
172
|
+
fontSize: '13px',
|
|
173
|
+
}, children: [_jsxs("div", { style: { color: '#c62828', fontWeight: 500, marginBottom: '4px' }, children: [displayErrors.length, " error", displayErrors.length !== 1 ? 's' : ''] }), displayErrors.slice(0, 5).map((error, i) => (_jsxs("div", { style: {
|
|
174
|
+
color: '#b71c1c',
|
|
175
|
+
marginTop: '4px',
|
|
176
|
+
fontFamily: 'monospace',
|
|
177
|
+
fontSize: '12px',
|
|
178
|
+
}, children: ["Line ", error.line, ", Col ", error.column, ": ", error.message] }, i))), displayErrors.length > 5 && (_jsxs("div", { style: { color: '#999', marginTop: '4px', fontSize: '12px' }, children: ["...and ", displayErrors.length - 5, " more"] }))] }))] }));
|
|
179
|
+
};
|
|
180
|
+
export default TextEditor;
|
|
181
|
+
//# sourceMappingURL=TextEditor.js.map
|