@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.
Files changed (191) hide show
  1. package/LICENSE +4 -0
  2. package/README.md +11 -0
  3. package/dist/compatibility/CompatibilityEdge.d.ts +106 -0
  4. package/dist/compatibility/CompatibilityEdge.d.ts.map +1 -0
  5. package/dist/compatibility/CompatibilityEdge.js +233 -0
  6. package/dist/compatibility/CompatibilityEdge.js.map +1 -0
  7. package/dist/compatibility/IncompatibilityTooltip.d.ts +44 -0
  8. package/dist/compatibility/IncompatibilityTooltip.d.ts.map +1 -0
  9. package/dist/compatibility/IncompatibilityTooltip.js +145 -0
  10. package/dist/compatibility/IncompatibilityTooltip.js.map +1 -0
  11. package/dist/compatibility/index.d.ts +10 -0
  12. package/dist/compatibility/index.d.ts.map +1 -0
  13. package/dist/compatibility/index.js +10 -0
  14. package/dist/compatibility/index.js.map +1 -0
  15. package/dist/compatibility/useEdgeCompatibility.d.ts +82 -0
  16. package/dist/compatibility/useEdgeCompatibility.d.ts.map +1 -0
  17. package/dist/compatibility/useEdgeCompatibility.js +170 -0
  18. package/dist/compatibility/useEdgeCompatibility.js.map +1 -0
  19. package/dist/compatibility/validateConnection.d.ts +56 -0
  20. package/dist/compatibility/validateConnection.d.ts.map +1 -0
  21. package/dist/compatibility/validateConnection.js +382 -0
  22. package/dist/compatibility/validateConnection.js.map +1 -0
  23. package/dist/components/EditContext.d.ts +45 -0
  24. package/dist/components/EditContext.d.ts.map +1 -0
  25. package/dist/components/EditContext.js +41 -0
  26. package/dist/components/EditContext.js.map +1 -0
  27. package/dist/components/ShapeRenderer.d.ts +35 -0
  28. package/dist/components/ShapeRenderer.d.ts.map +1 -0
  29. package/dist/components/ShapeRenderer.js +77 -0
  30. package/dist/components/ShapeRenderer.js.map +1 -0
  31. package/dist/components/SymbolNode.d.ts +53 -0
  32. package/dist/components/SymbolNode.d.ts.map +1 -0
  33. package/dist/components/SymbolNode.js +333 -0
  34. package/dist/components/SymbolNode.js.map +1 -0
  35. package/dist/components/index.d.ts +8 -0
  36. package/dist/components/index.d.ts.map +1 -0
  37. package/dist/components/index.js +10 -0
  38. package/dist/components/index.js.map +1 -0
  39. package/dist/components/nodes.d.ts +43 -0
  40. package/dist/components/nodes.d.ts.map +1 -0
  41. package/dist/components/nodes.js +111 -0
  42. package/dist/components/nodes.js.map +1 -0
  43. package/dist/editor/CoralEditor.d.ts +29 -0
  44. package/dist/editor/CoralEditor.d.ts.map +1 -0
  45. package/dist/editor/CoralEditor.js +112 -0
  46. package/dist/editor/CoralEditor.js.map +1 -0
  47. package/dist/editor/FileControls.d.ts +34 -0
  48. package/dist/editor/FileControls.d.ts.map +1 -0
  49. package/dist/editor/FileControls.js +139 -0
  50. package/dist/editor/FileControls.js.map +1 -0
  51. package/dist/editor/LayoutControls.d.ts +41 -0
  52. package/dist/editor/LayoutControls.d.ts.map +1 -0
  53. package/dist/editor/LayoutControls.js +124 -0
  54. package/dist/editor/LayoutControls.js.map +1 -0
  55. package/dist/editor/converter.d.ts +37 -0
  56. package/dist/editor/converter.d.ts.map +1 -0
  57. package/dist/editor/converter.js +256 -0
  58. package/dist/editor/converter.js.map +1 -0
  59. package/dist/editor/index.d.ts +8 -0
  60. package/dist/editor/index.d.ts.map +1 -0
  61. package/dist/editor/index.js +8 -0
  62. package/dist/editor/index.js.map +1 -0
  63. package/dist/file/deserialize.d.ts +15 -0
  64. package/dist/file/deserialize.d.ts.map +1 -0
  65. package/dist/file/deserialize.js +145 -0
  66. package/dist/file/deserialize.js.map +1 -0
  67. package/dist/file/index.d.ts +15 -0
  68. package/dist/file/index.d.ts.map +1 -0
  69. package/dist/file/index.js +20 -0
  70. package/dist/file/index.js.map +1 -0
  71. package/dist/file/migrate.d.ts +29 -0
  72. package/dist/file/migrate.d.ts.map +1 -0
  73. package/dist/file/migrate.js +103 -0
  74. package/dist/file/migrate.js.map +1 -0
  75. package/dist/file/schema.d.ts +385 -0
  76. package/dist/file/schema.d.ts.map +1 -0
  77. package/dist/file/schema.js +180 -0
  78. package/dist/file/schema.js.map +1 -0
  79. package/dist/file/serialize.d.ts +30 -0
  80. package/dist/file/serialize.d.ts.map +1 -0
  81. package/dist/file/serialize.js +218 -0
  82. package/dist/file/serialize.js.map +1 -0
  83. package/dist/file/validate.d.ts +21 -0
  84. package/dist/file/validate.d.ts.map +1 -0
  85. package/dist/file/validate.js +165 -0
  86. package/dist/file/validate.js.map +1 -0
  87. package/dist/index.d.ts +24 -0
  88. package/dist/index.d.ts.map +1 -0
  89. package/dist/index.js +42 -0
  90. package/dist/index.js.map +1 -0
  91. package/dist/layout/elk.d.ts +77 -0
  92. package/dist/layout/elk.d.ts.map +1 -0
  93. package/dist/layout/elk.js +379 -0
  94. package/dist/layout/elk.js.map +1 -0
  95. package/dist/layout/index.d.ts +12 -0
  96. package/dist/layout/index.d.ts.map +1 -0
  97. package/dist/layout/index.js +12 -0
  98. package/dist/layout/index.js.map +1 -0
  99. package/dist/layout/nodeSizing.d.ts +65 -0
  100. package/dist/layout/nodeSizing.d.ts.map +1 -0
  101. package/dist/layout/nodeSizing.js +319 -0
  102. package/dist/layout/nodeSizing.js.map +1 -0
  103. package/dist/layout/positionStability.d.ts +73 -0
  104. package/dist/layout/positionStability.d.ts.map +1 -0
  105. package/dist/layout/positionStability.js +199 -0
  106. package/dist/layout/positionStability.js.map +1 -0
  107. package/dist/layout/useAxisConstrain.d.ts +20 -0
  108. package/dist/layout/useAxisConstrain.d.ts.map +1 -0
  109. package/dist/layout/useAxisConstrain.js +106 -0
  110. package/dist/layout/useAxisConstrain.js.map +1 -0
  111. package/dist/layout/useDiagramState.d.ts +92 -0
  112. package/dist/layout/useDiagramState.d.ts.map +1 -0
  113. package/dist/layout/useDiagramState.js +256 -0
  114. package/dist/layout/useDiagramState.js.map +1 -0
  115. package/dist/layout/useLayoutHistory.d.ts +53 -0
  116. package/dist/layout/useLayoutHistory.d.ts.map +1 -0
  117. package/dist/layout/useLayoutHistory.js +124 -0
  118. package/dist/layout/useLayoutHistory.js.map +1 -0
  119. package/dist/properties/NodePropertiesPanel.d.ts +47 -0
  120. package/dist/properties/NodePropertiesPanel.d.ts.map +1 -0
  121. package/dist/properties/NodePropertiesPanel.js +199 -0
  122. package/dist/properties/NodePropertiesPanel.js.map +1 -0
  123. package/dist/properties/index.d.ts +10 -0
  124. package/dist/properties/index.d.ts.map +1 -0
  125. package/dist/properties/index.js +10 -0
  126. package/dist/properties/index.js.map +1 -0
  127. package/dist/properties/propertyUtils.d.ts +60 -0
  128. package/dist/properties/propertyUtils.d.ts.map +1 -0
  129. package/dist/properties/propertyUtils.js +89 -0
  130. package/dist/properties/propertyUtils.js.map +1 -0
  131. package/dist/settings/FontSettingsForm.d.ts +28 -0
  132. package/dist/settings/FontSettingsForm.d.ts.map +1 -0
  133. package/dist/settings/FontSettingsForm.js +129 -0
  134. package/dist/settings/FontSettingsForm.js.map +1 -0
  135. package/dist/settings/LayoutSettingsForm.d.ts +37 -0
  136. package/dist/settings/LayoutSettingsForm.d.ts.map +1 -0
  137. package/dist/settings/LayoutSettingsForm.js +165 -0
  138. package/dist/settings/LayoutSettingsForm.js.map +1 -0
  139. package/dist/settings/SettingsPanel.d.ts +55 -0
  140. package/dist/settings/SettingsPanel.d.ts.map +1 -0
  141. package/dist/settings/SettingsPanel.js +84 -0
  142. package/dist/settings/SettingsPanel.js.map +1 -0
  143. package/dist/settings/index.d.ts +13 -0
  144. package/dist/settings/index.d.ts.map +1 -0
  145. package/dist/settings/index.js +13 -0
  146. package/dist/settings/index.js.map +1 -0
  147. package/dist/settings/useSettings.d.ts +51 -0
  148. package/dist/settings/useSettings.d.ts.map +1 -0
  149. package/dist/settings/useSettings.js +154 -0
  150. package/dist/settings/useSettings.js.map +1 -0
  151. package/dist/text-editor/SplitEditor.d.ts +72 -0
  152. package/dist/text-editor/SplitEditor.d.ts.map +1 -0
  153. package/dist/text-editor/SplitEditor.js +172 -0
  154. package/dist/text-editor/SplitEditor.js.map +1 -0
  155. package/dist/text-editor/TextEditor.d.ts +53 -0
  156. package/dist/text-editor/TextEditor.d.ts.map +1 -0
  157. package/dist/text-editor/TextEditor.js +181 -0
  158. package/dist/text-editor/TextEditor.js.map +1 -0
  159. package/dist/text-editor/hooks/useBidirectionalSync.d.ts +106 -0
  160. package/dist/text-editor/hooks/useBidirectionalSync.d.ts.map +1 -0
  161. package/dist/text-editor/hooks/useBidirectionalSync.js +151 -0
  162. package/dist/text-editor/hooks/useBidirectionalSync.js.map +1 -0
  163. package/dist/text-editor/index.d.ts +9 -0
  164. package/dist/text-editor/index.d.ts.map +1 -0
  165. package/dist/text-editor/index.js +11 -0
  166. package/dist/text-editor/index.js.map +1 -0
  167. package/dist/types.d.ts +242 -0
  168. package/dist/types.d.ts.map +1 -0
  169. package/dist/types.js +5 -0
  170. package/dist/types.js.map +1 -0
  171. package/dist/waypoints/index.d.ts +8 -0
  172. package/dist/waypoints/index.d.ts.map +1 -0
  173. package/dist/waypoints/index.js +7 -0
  174. package/dist/waypoints/index.js.map +1 -0
  175. package/dist/waypoints/operations.d.ts +29 -0
  176. package/dist/waypoints/operations.d.ts.map +1 -0
  177. package/dist/waypoints/operations.js +85 -0
  178. package/dist/waypoints/operations.js.map +1 -0
  179. package/dist/waypoints/router.d.ts +23 -0
  180. package/dist/waypoints/router.d.ts.map +1 -0
  181. package/dist/waypoints/router.js +109 -0
  182. package/dist/waypoints/router.js.map +1 -0
  183. package/dist/waypoints/types.d.ts +15 -0
  184. package/dist/waypoints/types.d.ts.map +1 -0
  185. package/dist/waypoints/types.js +5 -0
  186. package/dist/waypoints/types.js.map +1 -0
  187. package/dist/waypoints/useWaypointManager.d.ts +38 -0
  188. package/dist/waypoints/useWaypointManager.d.ts.map +1 -0
  189. package/dist/waypoints/useWaypointManager.js +106 -0
  190. package/dist/waypoints/useWaypointManager.js.map +1 -0
  191. 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