@stephen-netu/design-system 0.1.0

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 (204) hide show
  1. package/README.md +66 -0
  2. package/package.json +130 -0
  3. package/src/b0-app-shell/clarification-overlay/ClarificationOverlay.svelte +221 -0
  4. package/src/b0-app-shell/clarification-overlay/index.ts +1 -0
  5. package/src/b0-app-shell/command-bar/CommandBar.svelte +565 -0
  6. package/src/b0-app-shell/index.ts +14 -0
  7. package/src/base.css +152 -0
  8. package/src/d0-data-viz/BarChart.svelte +236 -0
  9. package/src/d0-data-viz/CheckpointBar.svelte +574 -0
  10. package/src/d0-data-viz/CheckpointPill.svelte +297 -0
  11. package/src/d0-data-viz/MetricCard.svelte +159 -0
  12. package/src/d0-data-viz/SparkLine.svelte +66 -0
  13. package/src/d0-data-viz/StorageMap.svelte +182 -0
  14. package/src/d0-data-viz/Timeline.svelte +188 -0
  15. package/src/d0-data-viz/checkpoint-types.ts +162 -0
  16. package/src/d0-data-viz/d0-tokens.css +32 -0
  17. package/src/d0-data-viz/index.ts +58 -0
  18. package/src/d0-data-viz/storage-map-types.ts +142 -0
  19. package/src/d0-data-viz/theme-integration.ts +134 -0
  20. package/src/d0-data-viz/types.ts +53 -0
  21. package/src/demos/BspSizingInfographic.svelte +386 -0
  22. package/src/demos/BspZoneDemo.svelte +255 -0
  23. package/src/demos/ControlRoomDemo.svelte +570 -0
  24. package/src/demos/NodepadBlocksShowcase.svelte +189 -0
  25. package/src/demos/index.ts +7 -0
  26. package/src/editor/CodeEditor.svelte +188 -0
  27. package/src/editor/FindReplaceDialog.svelte +516 -0
  28. package/src/editor/MarkdownEditor.svelte +660 -0
  29. package/src/editor/index.ts +2 -0
  30. package/src/entity-graph-view/WorldView.svelte +171 -0
  31. package/src/entity-graph-view/index.ts +6 -0
  32. package/src/entity-graph-view/types.ts +364 -0
  33. package/src/epistemic/index.ts +2 -0
  34. package/src/epistemic/types.ts +134 -0
  35. package/src/epistemic/utils.ts +32 -0
  36. package/src/f0-forms/checkbox/Checkbox.svelte +158 -0
  37. package/src/f0-forms/field/FormField.svelte +91 -0
  38. package/src/f0-forms/index.ts +11 -0
  39. package/src/f0-forms/radio/RadioGroup.svelte +192 -0
  40. package/src/f0-forms/search/SearchBar.svelte +99 -0
  41. package/src/f0-forms/select/Select.svelte +198 -0
  42. package/src/f0-forms/textarea/TextArea.svelte +170 -0
  43. package/src/f0-forms/toggle/FormToggle.svelte +147 -0
  44. package/src/f0-forms/validation/validate.ts +47 -0
  45. package/src/fabric/ai/ChatPanel.svelte +648 -0
  46. package/src/fabric/ai/index.ts +11 -0
  47. package/src/fabric/data/index.ts +21 -0
  48. package/src/fabric/feedback/StatusBar.svelte +274 -0
  49. package/src/fabric/feedback/ToastContainer.svelte +122 -0
  50. package/src/fabric/feedback/index.ts +14 -0
  51. package/src/fabric/index.ts +37 -0
  52. package/src/fabric/input/CommandBar.svelte +79 -0
  53. package/src/fabric/input/KeyboardShortcuts.svelte +388 -0
  54. package/src/fabric/input/index.ts +12 -0
  55. package/src/fabric/layout/GlassBox.svelte +193 -0
  56. package/src/fabric/layout/KanbanBoard.svelte +158 -0
  57. package/src/fabric/layout/KanbanCard.svelte +226 -0
  58. package/src/fabric/layout/KanbanColumn.svelte +278 -0
  59. package/src/fabric/layout/index.ts +17 -0
  60. package/src/fabric/navigation/ActivityRail.svelte +271 -0
  61. package/src/fabric/navigation/index.ts +11 -0
  62. package/src/g0-geo/SovereignMap.svelte +149 -0
  63. package/src/g0-geo/index.ts +2 -0
  64. package/src/index.ts +68 -0
  65. package/src/l0-layout/block-writer/BlockConnectionLines.svelte +98 -0
  66. package/src/l0-layout/block-writer/BlockContent.svelte +213 -0
  67. package/src/l0-layout/block-writer/BlockHeader.svelte +230 -0
  68. package/src/l0-layout/block-writer/BlockItem.svelte +177 -0
  69. package/src/l0-layout/block-writer/BlockWriter.svelte +219 -0
  70. package/src/l0-layout/block-writer/block-writer-types.ts +63 -0
  71. package/src/l0-layout/block-writer/index.ts +24 -0
  72. package/src/l0-layout/block-writer/markdown.ts +110 -0
  73. package/src/l0-layout/chat-view/ChatViewLayout.svelte +61 -0
  74. package/src/l0-layout/epistemic/AllayDetailPanel.svelte +113 -0
  75. package/src/l0-layout/epistemic/AssayDetailPanel.svelte +164 -0
  76. package/src/l0-layout/epistemic/AvenueTrace.svelte +253 -0
  77. package/src/l0-layout/epistemic/KnowledgeDetailPanel.svelte +204 -0
  78. package/src/l0-layout/epistemic/ParadigmLens.svelte +170 -0
  79. package/src/l0-layout/epistemic/ProvenanceChain.svelte +164 -0
  80. package/src/l0-layout/epistemic/TrustView.svelte +195 -0
  81. package/src/l0-layout/epistemic/index.ts +7 -0
  82. package/src/l0-layout/grove/AccordCard.svelte +156 -0
  83. package/src/l0-layout/grove/GroveCard.svelte +197 -0
  84. package/src/l0-layout/grove/MemberBadge.svelte +96 -0
  85. package/src/l0-layout/grove/MemberList.svelte +59 -0
  86. package/src/l0-layout/grove/grove-types.ts +29 -0
  87. package/src/l0-layout/grove/index.ts +14 -0
  88. package/src/l0-layout/index.ts +45 -0
  89. package/src/l0-layout/kanban/KanbanBoard.svelte +71 -0
  90. package/src/l0-layout/kanban/KanbanCard.svelte +276 -0
  91. package/src/l0-layout/kanban/KanbanColumn.svelte +198 -0
  92. package/src/l0-layout/kanban/KanbanViewLayout.svelte +61 -0
  93. package/src/l0-layout/workspace/WorkspaceLayout.svelte +93 -0
  94. package/src/n0-node-graph/GraphEditor.svelte +342 -0
  95. package/src/n0-node-graph/GraphHistory.svelte +122 -0
  96. package/src/n0-node-graph/Node.svelte +269 -0
  97. package/src/n0-node-graph/NodeCanvas.svelte +187 -0
  98. package/src/n0-node-graph/NodeEdge.svelte +152 -0
  99. package/src/n0-node-graph/NodePalette.svelte +318 -0
  100. package/src/n0-node-graph/NodePort.svelte +128 -0
  101. package/src/n0-node-graph/index.ts +33 -0
  102. package/src/n0-node-graph/n0-tokens.css +29 -0
  103. package/src/n0-node-graph/node-port-types.ts +19 -0
  104. package/src/n0-node-graph/types.ts +96 -0
  105. package/src/n0-node-graph/validate.test.ts +722 -0
  106. package/src/n0-node-graph/validate.ts +371 -0
  107. package/src/n1-flow-canvas/FlowCanvas.svelte +202 -0
  108. package/src/n1-flow-canvas/graph-state.svelte.ts +95 -0
  109. package/src/n1-flow-canvas/index.ts +21 -0
  110. package/src/n1-flow-canvas/layout.ts +118 -0
  111. package/src/n1-flow-canvas/layout.worker.ts +128 -0
  112. package/src/n1-flow-canvas/types.ts +102 -0
  113. package/src/n1-flow-canvas/utils.ts +146 -0
  114. package/src/n2-manifold-disk/ManifoldDisk.svelte +314 -0
  115. package/src/n2-manifold-disk/index.ts +6 -0
  116. package/src/n2-manifold-disk/poincare.ts +184 -0
  117. package/src/n3-force-graph/ForceGraph.svelte +180 -0
  118. package/src/n3-force-graph/GraphTooltip.svelte +99 -0
  119. package/src/n3-force-graph/animated-edge.ts +107 -0
  120. package/src/n3-force-graph/health-node.ts +128 -0
  121. package/src/n3-force-graph/index.ts +31 -0
  122. package/src/n3-force-graph/types.ts +94 -0
  123. package/src/n4-adaptive-manifold/AdaptiveManifold.svelte +1165 -0
  124. package/src/n4-adaptive-manifold/DetailCard.svelte +212 -0
  125. package/src/n4-adaptive-manifold/SuggestionIndicator.svelte +91 -0
  126. package/src/n4-adaptive-manifold/classifier.ts +139 -0
  127. package/src/n4-adaptive-manifold/index.ts +45 -0
  128. package/src/n4-adaptive-manifold/n4-tokens.css +155 -0
  129. package/src/n4-adaptive-manifold/orchestrator.ts +301 -0
  130. package/src/n4-adaptive-manifold/phase-theme.ts +92 -0
  131. package/src/n4-adaptive-manifold/pinnacle-events.ts +135 -0
  132. package/src/n4-adaptive-manifold/providers/gravitational-provider.ts +191 -0
  133. package/src/n4-adaptive-manifold/providers/manifold-provider.ts +276 -0
  134. package/src/n4-adaptive-manifold/transition.ts +90 -0
  135. package/src/n4-adaptive-manifold/types.ts +313 -0
  136. package/src/p0-primitives/index.ts +6 -0
  137. package/src/p0-primitives/tokens/control-room.css +309 -0
  138. package/src/p0-primitives/tokens/index.ts +6 -0
  139. package/src/p0-primitives/tokens/tokens.css +140 -0
  140. package/src/p0-primitives/tokens/tokens.json +132 -0
  141. package/src/p0-primitives/types/index.ts +123 -0
  142. package/src/p0-primitives/types/plugin-types.ts +74 -0
  143. package/src/p0-primitives/utils/index.ts +39 -0
  144. package/src/platform/hud-protocol.ts +20 -0
  145. package/src/platform/index.ts +3 -0
  146. package/src/s0-state/create-store.ts +84 -0
  147. package/src/s0-state/derived.ts +41 -0
  148. package/src/s0-state/index.ts +6 -0
  149. package/src/showcase/Showcase.svelte +401 -0
  150. package/src/spatial/BspTilingCanvas.svelte +232 -0
  151. package/src/spatial/ForceCanvas.svelte +289 -0
  152. package/src/spatial/LodRenderer.svelte +334 -0
  153. package/src/spatial/SnapZoomCamera.svelte +185 -0
  154. package/src/spatial/ZoneTiler.svelte +311 -0
  155. package/src/spatial/camera-types.ts +45 -0
  156. package/src/spatial/content-proportional-bsp.ts +253 -0
  157. package/src/spatial/force-canvas-types.ts +102 -0
  158. package/src/spatial/index.ts +15 -0
  159. package/src/spatial/lod-types.ts +70 -0
  160. package/src/spatial/zone-tiler-types.ts +177 -0
  161. package/src/stores/vaultStore.svelte +14 -0
  162. package/src/t0-transport/connection.ts +34 -0
  163. package/src/t0-transport/error.ts +37 -0
  164. package/src/t0-transport/events.ts +50 -0
  165. package/src/t0-transport/index.ts +6 -0
  166. package/src/t0-transport/invoke.ts +30 -0
  167. package/src/tauri/index.ts +13 -0
  168. package/src/u0-primitives/allay-gauge/AllayGauge.svelte +90 -0
  169. package/src/u0-primitives/allay-gauge/index.ts +1 -0
  170. package/src/u0-primitives/animated-icon/AnimatedIcon.svelte +273 -0
  171. package/src/u0-primitives/animated-icon/README.md +53 -0
  172. package/src/u0-primitives/animated-icon/index.ts +31 -0
  173. package/src/u0-primitives/assay-badge/AssayBadge.svelte +121 -0
  174. package/src/u0-primitives/assay-badge/AssaySignalBar.svelte +83 -0
  175. package/src/u0-primitives/assay-badge/index.ts +2 -0
  176. package/src/u0-primitives/avatar/Avatar.svelte +177 -0
  177. package/src/u0-primitives/badge/Badge.svelte +149 -0
  178. package/src/u0-primitives/button/Button.svelte +304 -0
  179. package/src/u0-primitives/card/Card.svelte +182 -0
  180. package/src/u0-primitives/dropdown/Dropdown.svelte +305 -0
  181. package/src/u0-primitives/expert-badge/ExpertBadge.svelte +89 -0
  182. package/src/u0-primitives/expert-badge/index.ts +1 -0
  183. package/src/u0-primitives/index.ts +35 -0
  184. package/src/u0-primitives/input/Input.svelte +237 -0
  185. package/src/u0-primitives/modal/Modal.svelte +331 -0
  186. package/src/u0-primitives/modal/index.ts +1 -0
  187. package/src/u0-primitives/spinner/Spinner.svelte +129 -0
  188. package/src/u0-primitives/tabs/Tabs.svelte +225 -0
  189. package/src/u0-primitives/tabs/tabs-types.ts +11 -0
  190. package/src/u0-primitives/toggle/Toggle.svelte +161 -0
  191. package/src/u0-primitives/tooltip/Tooltip.svelte +234 -0
  192. package/src/v0-diagrams/DiagramPanel.svelte +68 -0
  193. package/src/v0-diagrams/MarkmapDiagram.svelte +56 -0
  194. package/src/v0-diagrams/MermaidDiagram.svelte +53 -0
  195. package/src/v0-diagrams/index.ts +4 -0
  196. package/src/v0-diagrams/types.ts +28 -0
  197. package/src/v0-diagrams/v0-tokens.css +6 -0
  198. package/src/vite-env.d.ts +7 -0
  199. package/src/x0-enchanted-blocks/ENCHANTED_BLOCKS_SPEC.md +457 -0
  200. package/src/x0-enchanted-blocks/components/EnchantedBlock.svelte +485 -0
  201. package/src/x0-enchanted-blocks/components/NodepadBlock.svelte +262 -0
  202. package/src/x0-enchanted-blocks/core/block-layout.ts +211 -0
  203. package/src/x0-enchanted-blocks/core/block-types.ts +227 -0
  204. package/src/x0-enchanted-blocks/index.ts +46 -0
@@ -0,0 +1,189 @@
1
+ <script lang="ts">
2
+ // EnchantedShowcase — Flat Nodepad-style blocks (NO nested cards)
3
+ // Key improvements from failed first attempt:
4
+ // - Single flat block (no card-within-card)
5
+ // - Click-and-type editing (no mode toggle, no cursor reset)
6
+ // - NO collapse button (content flows naturally)
7
+
8
+ import { NodepadBlock, type EnchantedBlockData } from '@kos/design-system/x0-enchanted-blocks';
9
+
10
+ // Mock blocks as EnchantedBlockData (not wrapped in TaskView)
11
+ const mockBlocks: EnchantedBlockData[] = [
12
+ {
13
+ id: 'wt-001',
14
+ type: 'task',
15
+ content: 'Short task: Brief description.',
16
+ createdAt: Date.now(),
17
+ modifiedAt: Date.now(),
18
+ },
19
+ {
20
+ id: 'wt-002',
21
+ type: 'idea',
22
+ content: 'Medium complexity idea that should wrap to a few lines. This demonstrates how the block adapts to content length without truncation or collapse buttons.',
23
+ createdAt: Date.now(),
24
+ modifiedAt: Date.now(),
25
+ },
26
+ {
27
+ id: 'wt-003',
28
+ type: 'question',
29
+ content: `How do we integrate Pretext for DOM-free text measurement?
30
+
31
+ Key considerations:
32
+ - Canvas-based measurement (no DOM thrash)
33
+ - Adaptive sizing per content
34
+ - 14-type classification from Nodepad
35
+ - Inline editing without mode switches
36
+
37
+ The block should just flow naturally—no collapse buttons needed.`,
38
+ annotation: 'Research note from Chenglou/pretext investigation',
39
+ createdAt: Date.now(),
40
+ modifiedAt: Date.now(),
41
+ },
42
+ {
43
+ id: 'wt-004',
44
+ type: 'claim',
45
+ content: 'Flat blocks with inline editing are superior to nested card containers.',
46
+ createdAt: Date.now(),
47
+ modifiedAt: Date.now(),
48
+ },
49
+ {
50
+ id: 'wt-005',
51
+ type: 'reference',
52
+ content: 'Nodepad spatial canvas research — 14 block types, no collapse buttons, click-and-type editing.',
53
+ annotation: 'github.com/mskayyali/nodepad',
54
+ createdAt: Date.now(),
55
+ modifiedAt: Date.now(),
56
+ },
57
+ {
58
+ id: 'wt-006',
59
+ type: 'general',
60
+ content: 'Completed: Pretext dependency added to design-system. Build passes.',
61
+ createdAt: Date.now() - 86400000,
62
+ modifiedAt: Date.now(),
63
+ },
64
+ ];
65
+
66
+ // Local state for blocks (simulating store)
67
+ let blocks = $state<EnchantedBlockData[]>(mockBlocks);
68
+
69
+ function handleChange(id: string, newContent: string) {
70
+ const block = blocks.find(b => b.id === id);
71
+ if (block) {
72
+ block.content = newContent;
73
+ block.modifiedAt = Date.now();
74
+ }
75
+ console.log(`Changed ${id}: ${newContent.slice(0, 30)}...`);
76
+ }
77
+
78
+ function handleTypeChange(id: string, newType: string) {
79
+ const block = blocks.find(b => b.id === id);
80
+ if (block) {
81
+ (block as {type: string}).type = newType;
82
+ }
83
+ }
84
+ </script>
85
+
86
+ <div class="showcase">
87
+ <header class="showcase-header">
88
+ <h1>Nodepad-Style Blocks</h1>
89
+ <p class="subtitle">Flat layout • Click to edit • No collapse buttons • No nested cards</p>
90
+ <div class="features">
91
+ <span class="feature">✓ Single flat container</span>
92
+ <span class="feature">✓ Type in place (stable cursor)</span>
93
+ <span class="feature">✓ Auto-sized to content</span>
94
+ <span class="feature">✓ 14 types (click chip to cycle)</span>
95
+ </div>
96
+ </header>
97
+
98
+ <main class="showcase-grid">
99
+ {#each blocks as block (block.id)}
100
+ <NodepadBlock
101
+ {block}
102
+ maxWidth={320}
103
+ onChange={(content) => handleChange(block.id, content)}
104
+ onTypeChange={(type) => handleTypeChange(block.id, type)}
105
+ />
106
+ {/each}
107
+ </main>
108
+
109
+ <footer class="showcase-footer">
110
+ <p>Flat block architecture — no card-within-card nesting</p>
111
+ <p class="hint">Click any block to start typing. Click type chip to cycle types.</p>
112
+ </footer>
113
+ </div>
114
+
115
+ <style>
116
+ :global(body) {
117
+ margin: 0;
118
+ background: var(--color-bg-canvas, #1a1a1a);
119
+ font-family: "Sovereign Sans", system-ui, sans-serif;
120
+ }
121
+
122
+ .showcase {
123
+ min-height: 100vh;
124
+ padding: 40px 20px;
125
+ display: flex;
126
+ flex-direction: column;
127
+ gap: 32px;
128
+ }
129
+
130
+ .showcase-header {
131
+ text-align: center;
132
+ max-width: 800px;
133
+ margin: 0 auto;
134
+ }
135
+
136
+ h1 {
137
+ color: var(--color-text-primary, #e8e4dc);
138
+ font-size: 28px;
139
+ font-weight: 600;
140
+ margin: 0 0 8px 0;
141
+ }
142
+
143
+ .subtitle {
144
+ color: var(--color-text-secondary, #a09880);
145
+ font-size: 16px;
146
+ margin: 0 0 20px 0;
147
+ }
148
+
149
+ .features {
150
+ display: flex;
151
+ flex-wrap: wrap;
152
+ justify-content: center;
153
+ gap: 12px 24px;
154
+ font-size: 13px;
155
+ color: var(--color-accent, #b87333);
156
+ }
157
+
158
+ .feature {
159
+ white-space: nowrap;
160
+ }
161
+
162
+ .showcase-grid {
163
+ display: grid;
164
+ grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
165
+ align-items: start; /* KEY: Each card sizes to its own content, not row height */
166
+ gap: 20px;
167
+ max-width: 1400px;
168
+ margin: 0 auto;
169
+ width: 100%;
170
+ padding: 0 20px;
171
+ }
172
+
173
+ .showcase-footer {
174
+ text-align: center;
175
+ margin-top: auto;
176
+ padding-top: 40px;
177
+ color: var(--color-text-muted, #706858);
178
+ font-size: 13px;
179
+ }
180
+
181
+ .showcase-footer p {
182
+ margin: 4px 0;
183
+ }
184
+
185
+ .hint {
186
+ font-style: italic;
187
+ opacity: 0.8;
188
+ }
189
+ </style>
@@ -0,0 +1,7 @@
1
+ // Demos — Design system showcase and examples
2
+ // These are full-page demonstrations of design-system capabilities
3
+
4
+ export { default as NodepadBlocksShowcase } from './NodepadBlocksShowcase.svelte';
5
+ export { default as BspZoneDemo } from './BspZoneDemo.svelte';
6
+ export { default as BspSizingInfographic } from './BspSizingInfographic.svelte';
7
+ export { default as ControlRoomDemo } from './ControlRoomDemo.svelte';
@@ -0,0 +1,188 @@
1
+ <script lang="ts">
2
+ // wt-913: CodeEditor — CodeMirror 6 + language auto-detection
3
+ // Detects language from cell source extension or content heuristics.
4
+ // Falls back to plain text if no language matches.
5
+
6
+ import { onMount, onDestroy } from 'svelte';
7
+ import { EditorView, keymap } from '@codemirror/view';
8
+ import { EditorState } from '@codemirror/state';
9
+ import { defaultKeymap, history, historyKeymap, indentWithTab } from '@codemirror/commands';
10
+ import { LanguageDescription } from '@codemirror/language';
11
+ import { languages } from '@codemirror/language-data';
12
+ import type { Extension } from '@codemirror/state';
13
+
14
+ interface Props {
15
+ content: string;
16
+ /** Source hint — file path or URL, used for language detection */
17
+ source?: string;
18
+ isActive: boolean;
19
+ onContentChange?: (content: string) => void;
20
+ onBlur?: () => void;
21
+ }
22
+
23
+ let { content, source = '', isActive, onContentChange, onBlur }: Props = $props();
24
+
25
+ let container: HTMLElement | null = $state(null);
26
+ let view: EditorView | null = null;
27
+ let skipSync = false;
28
+ let detectedLang = $state('text');
29
+
30
+ // Detect language from source path/extension or content shebang
31
+ async function detectLanguageExtension(): Promise<Extension> {
32
+ // Try filename match
33
+ if (source) {
34
+ const filename = source.split('/').pop() ?? source;
35
+ const match = LanguageDescription.matchFilename(languages, filename);
36
+ if (match) {
37
+ detectedLang = match.name;
38
+ const lang = await match.load();
39
+ return lang;
40
+ }
41
+ }
42
+
43
+ // Try shebang heuristic
44
+ const firstLine = content.split('\n')[0] ?? '';
45
+ if (firstLine.startsWith('#!')) {
46
+ const shebangs: Record<string, string> = {
47
+ python: 'Python', node: 'JavaScript', ruby: 'Ruby',
48
+ bash: 'Shell', sh: 'Shell', zsh: 'Shell',
49
+ };
50
+ for (const [key, langName] of Object.entries(shebangs)) {
51
+ if (firstLine.includes(key)) {
52
+ const match = LanguageDescription.matchLanguageName(languages, langName);
53
+ if (match) {
54
+ detectedLang = match.name;
55
+ const lang = await match.load();
56
+ return lang;
57
+ }
58
+ }
59
+ }
60
+ }
61
+
62
+ // Try content-based heuristics
63
+ if (/^(fn |pub fn |use |mod |impl |struct |enum |trait )/m.test(content)) {
64
+ const rust = LanguageDescription.matchLanguageName(languages, 'Rust');
65
+ if (rust) { detectedLang = 'Rust'; return await rust.load(); }
66
+ }
67
+ if (/^(import |export |const |let |var |function |class |interface )/m.test(content)) {
68
+ const ts = LanguageDescription.matchLanguageName(languages, 'TypeScript');
69
+ if (ts) { detectedLang = 'TypeScript'; return await ts.load(); }
70
+ }
71
+ if (/^(def |class |import |from |if __name__)/m.test(content)) {
72
+ const py = LanguageDescription.matchLanguageName(languages, 'Python');
73
+ if (py) { detectedLang = 'Python'; return await py.load(); }
74
+ }
75
+
76
+ detectedLang = 'text';
77
+ return [];
78
+ }
79
+
80
+ // Loge dark theme (matches MarkdownEditor)
81
+ const logeTheme = EditorView.theme({
82
+ '&': { background: 'transparent', height: '100%', color: '#e8e0d0' },
83
+ '.cm-scroller': {
84
+ fontFamily: 'var(--font-mono, monospace)',
85
+ fontSize: '11px',
86
+ lineHeight: '1.6',
87
+ overflow: 'auto',
88
+ },
89
+ '.cm-content': { padding: '6px 8px', caretColor: '#b87333' },
90
+ '.cm-cursor': { borderLeftColor: '#b87333' },
91
+ '.cm-focused': { outline: 'none' },
92
+ '.cm-selectionBackground, ::selection': { background: 'rgba(184,115,51,0.2)' },
93
+ '.cm-gutters': {
94
+ background: '#1a1a1a',
95
+ borderRight: '1px solid #2a2a2a',
96
+ color: '#5a5248',
97
+ fontSize: '10px',
98
+ },
99
+ '.cm-lineNumbers .cm-gutterElement': { padding: '0 6px 0 4px' },
100
+ '.cm-activeLine': { background: 'rgba(184,115,51,0.05)' },
101
+ '.cm-activeLineGutter': { background: 'rgba(184,115,51,0.08)' },
102
+ }, { dark: true });
103
+
104
+ onMount(async () => {
105
+ if (!container) return;
106
+
107
+ const langExtension = await detectLanguageExtension();
108
+
109
+ const onEscape = keymap.of([{
110
+ key: 'Escape',
111
+ run: () => { onBlur?.(); return true; },
112
+ }]);
113
+
114
+ view = new EditorView({
115
+ state: EditorState.create({
116
+ doc: content,
117
+ extensions: [
118
+ history(),
119
+ keymap.of([...defaultKeymap, ...historyKeymap, indentWithTab]),
120
+ onEscape,
121
+ langExtension,
122
+ logeTheme,
123
+ EditorView.updateListener.of((update) => {
124
+ if (update.docChanged) {
125
+ skipSync = true;
126
+ onContentChange?.(update.state.doc.toString());
127
+ }
128
+ }),
129
+ EditorView.lineWrapping,
130
+ ],
131
+ }),
132
+ parent: container,
133
+ });
134
+
135
+ if (isActive) view.focus();
136
+ });
137
+
138
+ $effect(() => {
139
+ if (!view || skipSync) { skipSync = false; return; }
140
+ const current = view.state.doc.toString();
141
+ if (current !== content) {
142
+ view.dispatch({
143
+ changes: { from: 0, to: current.length, insert: content },
144
+ });
145
+ }
146
+ });
147
+
148
+ $effect(() => {
149
+ if (view && isActive) view.focus();
150
+ });
151
+
152
+ onDestroy(() => view?.destroy());
153
+ </script>
154
+
155
+ <div class="code-editor" bind:this={container}>
156
+ {#if detectedLang !== 'text'}
157
+ <div class="code-editor-lang-badge">{detectedLang}</div>
158
+ {/if}
159
+ </div>
160
+
161
+ <style>
162
+ .code-editor {
163
+ width: 100%;
164
+ height: 100%;
165
+ overflow: hidden;
166
+ position: relative;
167
+ background: #1a1a1a;
168
+ }
169
+
170
+ :global(.code-editor .cm-editor) {
171
+ width: 100%;
172
+ height: 100%;
173
+ }
174
+
175
+ .code-editor-lang-badge {
176
+ position: absolute;
177
+ top: 4px;
178
+ right: 6px;
179
+ font-family: var(--font-mono, monospace);
180
+ font-size: 8px;
181
+ color: var(--color-text-muted, #a09880);
182
+ opacity: 0.4;
183
+ text-transform: uppercase;
184
+ letter-spacing: 0.1em;
185
+ pointer-events: none;
186
+ z-index: 1;
187
+ }
188
+ </style>