@wallarm-org/design-system 1.30.0 → 1.31.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 (123) hide show
  1. package/dist/components/CodeEditor/CodeEditorContent.d.ts +17 -0
  2. package/dist/components/CodeEditor/CodeEditorContent.js +197 -0
  3. package/dist/components/CodeEditor/CodeEditorContext.d.ts +17 -0
  4. package/dist/components/CodeEditor/CodeEditorContext.js +8 -0
  5. package/dist/components/CodeEditor/CodeEditorRoot.d.ts +77 -0
  6. package/dist/components/CodeEditor/CodeEditorRoot.js +203 -0
  7. package/dist/components/CodeEditor/classes.d.ts +21 -0
  8. package/dist/components/CodeEditor/classes.js +41 -0
  9. package/dist/components/CodeEditor/engine/SearchPanel.d.ts +13 -0
  10. package/dist/components/CodeEditor/engine/SearchPanel.js +201 -0
  11. package/dist/components/CodeEditor/engine/adapterPainter.d.ts +23 -0
  12. package/dist/components/CodeEditor/engine/adapterPainter.js +110 -0
  13. package/dist/components/CodeEditor/engine/babelSyntax.d.ts +15 -0
  14. package/dist/components/CodeEditor/engine/babelSyntax.js +54 -0
  15. package/dist/components/CodeEditor/engine/completion.d.ts +17 -0
  16. package/dist/components/CodeEditor/engine/completion.js +129 -0
  17. package/dist/components/CodeEditor/engine/contentAttributes.d.ts +12 -0
  18. package/dist/components/CodeEditor/engine/contentAttributes.js +24 -0
  19. package/dist/components/CodeEditor/engine/diagnostics.d.ts +73 -0
  20. package/dist/components/CodeEditor/engine/diagnostics.js +327 -0
  21. package/dist/components/CodeEditor/engine/diff.d.ts +37 -0
  22. package/dist/components/CodeEditor/engine/diff.js +145 -0
  23. package/dist/components/CodeEditor/engine/folds.d.ts +25 -0
  24. package/dist/components/CodeEditor/engine/folds.js +458 -0
  25. package/dist/components/CodeEditor/engine/gutters.d.ts +22 -0
  26. package/dist/components/CodeEditor/engine/gutters.js +160 -0
  27. package/dist/components/CodeEditor/engine/index.d.ts +12 -0
  28. package/dist/components/CodeEditor/engine/index.js +387 -0
  29. package/dist/components/CodeEditor/engine/languages/http/index.d.ts +20 -0
  30. package/dist/components/CodeEditor/engine/languages/http/index.js +69 -0
  31. package/dist/components/CodeEditor/engine/languages/http/parser.d.ts +2 -0
  32. package/dist/components/CodeEditor/engine/languages/http/parser.js +31 -0
  33. package/dist/components/CodeEditor/engine/languages/http/parser.terms.d.ts +1 -0
  34. package/dist/components/CodeEditor/engine/languages/http/parser.terms.js +2 -0
  35. package/dist/components/CodeEditor/engine/languages/index.d.ts +8 -0
  36. package/dist/components/CodeEditor/engine/languages/index.js +41 -0
  37. package/dist/components/CodeEditor/engine/languages/json.d.ts +8 -0
  38. package/dist/components/CodeEditor/engine/languages/json.js +11 -0
  39. package/dist/components/CodeEditor/engine/languages/jsonPointers.d.ts +47 -0
  40. package/dist/components/CodeEditor/engine/languages/jsonPointers.js +125 -0
  41. package/dist/components/CodeEditor/engine/languages/yaml.d.ts +4 -0
  42. package/dist/components/CodeEditor/engine/languages/yaml.js +11 -0
  43. package/dist/components/CodeEditor/engine/lines.d.ts +22 -0
  44. package/dist/components/CodeEditor/engine/lines.js +131 -0
  45. package/dist/components/CodeEditor/engine/luaSyntax.d.ts +14 -0
  46. package/dist/components/CodeEditor/engine/luaSyntax.js +36 -0
  47. package/dist/components/CodeEditor/engine/positions.d.ts +19 -0
  48. package/dist/components/CodeEditor/engine/positions.js +22 -0
  49. package/dist/components/CodeEditor/engine/schema/complete.d.ts +12 -0
  50. package/dist/components/CodeEditor/engine/schema/complete.js +211 -0
  51. package/dist/components/CodeEditor/engine/schema/hover.d.ts +12 -0
  52. package/dist/components/CodeEditor/engine/schema/hover.js +73 -0
  53. package/dist/components/CodeEditor/engine/schema/loadSchema.d.ts +19 -0
  54. package/dist/components/CodeEditor/engine/schema/loadSchema.js +32 -0
  55. package/dist/components/CodeEditor/engine/schema/validate.d.ts +9 -0
  56. package/dist/components/CodeEditor/engine/schema/validate.js +67 -0
  57. package/dist/components/CodeEditor/engine/search.d.ts +13 -0
  58. package/dist/components/CodeEditor/engine/search.js +84 -0
  59. package/dist/components/CodeEditor/engine/searchMatches.d.ts +8 -0
  60. package/dist/components/CodeEditor/engine/searchMatches.js +15 -0
  61. package/dist/components/CodeEditor/engine/theme.d.ts +8 -0
  62. package/dist/components/CodeEditor/engine/theme.js +89 -0
  63. package/dist/components/CodeEditor/engine/types.d.ts +44 -0
  64. package/dist/components/CodeEditor/engine/types.js +0 -0
  65. package/dist/components/CodeEditor/hooks/useCodeEditor.d.ts +6 -0
  66. package/dist/components/CodeEditor/hooks/useCodeEditor.js +3 -0
  67. package/dist/components/CodeEditor/index.d.ts +6 -0
  68. package/dist/components/CodeEditor/index.js +5 -0
  69. package/dist/components/CodeEditor/lib/PortalOutlet.d.ts +11 -0
  70. package/dist/components/CodeEditor/lib/PortalOutlet.js +11 -0
  71. package/dist/components/CodeEditor/lib/httpCompletions.d.ts +7 -0
  72. package/dist/components/CodeEditor/lib/httpCompletions.js +102 -0
  73. package/dist/components/CodeEditor/lib/httpCompletionsData.d.ts +29 -0
  74. package/dist/components/CodeEditor/lib/httpCompletionsData.js +650 -0
  75. package/dist/components/CodeEditor/lib/keyboardHint.d.ts +5 -0
  76. package/dist/components/CodeEditor/lib/keyboardHint.js +2 -0
  77. package/dist/components/CodeEditor/lib/loadEngine.d.ts +7 -0
  78. package/dist/components/CodeEditor/lib/loadEngine.js +9 -0
  79. package/dist/components/CodeEditor/lib/portalRegistry.d.ts +20 -0
  80. package/dist/components/CodeEditor/lib/portalRegistry.js +47 -0
  81. package/dist/components/CodeEditor/lib/splitContentProps.d.ts +12 -0
  82. package/dist/components/CodeEditor/lib/splitContentProps.js +28 -0
  83. package/dist/components/CodeEditor/types.d.ts +72 -0
  84. package/dist/components/CodeEditor/types.js +0 -0
  85. package/dist/components/CodeSnippet/CodeSnippetCode.js +6 -23
  86. package/dist/components/CodeSnippet/CodeSnippetCopyButton.js +4 -3
  87. package/dist/components/CodeSnippet/CodeSnippetFrameContext.d.ts +25 -0
  88. package/dist/components/CodeSnippet/CodeSnippetFrameContext.js +3 -0
  89. package/dist/components/CodeSnippet/CodeSnippetFullscreenButton.js +2 -2
  90. package/dist/components/CodeSnippet/CodeSnippetRoot.d.ts +3 -5
  91. package/dist/components/CodeSnippet/CodeSnippetRoot.js +56 -50
  92. package/dist/components/CodeSnippet/CodeSnippetShowMoreButton.js +7 -5
  93. package/dist/components/CodeSnippet/CodeSnippetWrapButton.js +2 -2
  94. package/dist/components/CodeSnippet/adapters/highlightjs.js +1 -0
  95. package/dist/components/CodeSnippet/adapters/prism.js +3 -1
  96. package/dist/components/CodeSnippet/adapters/shiki.js +1 -0
  97. package/dist/components/CodeSnippet/adapters/types.d.ts +3 -3
  98. package/dist/components/CodeSnippet/classes.d.ts +3 -0
  99. package/dist/components/CodeSnippet/classes.js +14 -0
  100. package/dist/components/CodeSnippet/hooks/index.d.ts +1 -0
  101. package/dist/components/CodeSnippet/hooks/index.js +1 -0
  102. package/dist/components/CodeSnippet/hooks/useCodeSnippetFrame.d.ts +2 -0
  103. package/dist/components/CodeSnippet/hooks/useCodeSnippetFrame.js +8 -0
  104. package/dist/components/CodeSnippet/index.d.ts +2 -1
  105. package/dist/components/CodeSnippet/index.js +1 -1
  106. package/dist/components/CodeSnippet/internal/CodeSnippetFrame.d.ts +22 -0
  107. package/dist/components/CodeSnippet/internal/CodeSnippetFrame.js +81 -0
  108. package/dist/components/CodeSnippet/internal/CodeToken.d.ts +2 -1
  109. package/dist/components/CodeSnippet/internal/CodeToken.js +1 -1
  110. package/dist/components/CodeSnippet/internal/FoldSummary.d.ts +11 -0
  111. package/dist/components/CodeSnippet/internal/FoldSummary.js +27 -0
  112. package/dist/components/CodeSnippet/internal/FoldToggle.d.ts +2 -0
  113. package/dist/components/CodeSnippet/internal/FoldToggle.js +2 -1
  114. package/dist/components/CodeSnippet/internal/index.d.ts +1 -0
  115. package/dist/components/CodeSnippet/internal/index.js +1 -0
  116. package/dist/components/CodeSnippet/lib/foldUtils.d.ts +9 -2
  117. package/dist/components/CodeSnippet/lib/foldUtils.js +2 -2
  118. package/dist/components/CodeSnippet/lib/showMore.d.ts +7 -0
  119. package/dist/components/CodeSnippet/lib/showMore.js +7 -0
  120. package/dist/components/Copyable/Copyable.d.ts +5 -2
  121. package/dist/components/Copyable/Copyable.js +1 -1
  122. package/dist/metadata/components.json +1235 -5
  123. package/package.json +24 -1
@@ -0,0 +1,327 @@
1
+ import { ensureSyntaxTree, syntaxTree } from "@codemirror/language";
2
+ import { linter, setDiagnosticsEffect } from "@codemirror/lint";
3
+ import { EditorView } from "@codemirror/view";
4
+ import { babelSyntaxDiagnostics } from "./babelSyntax.js";
5
+ import { findJsonBodyRange } from "./languages/index.js";
6
+ import { luaSyntaxDiagnostics } from "./luaSyntax.js";
7
+ import { offsetToPosition, positionToOffset } from "./positions.js";
8
+ const LINT_DELAY = 300;
9
+ const SYNTAX_SOURCE = 'syntax';
10
+ const SCHEMA_SOURCE = 'schema';
11
+ const ENSURE_TREE_TIMEOUT = 200;
12
+ const jsonRegion = (state, language)=>{
13
+ if ('json' === language) return {
14
+ from: 0,
15
+ to: state.doc.length
16
+ };
17
+ if ('http' === language) return findJsonBodyRange(state);
18
+ return null;
19
+ };
20
+ const POSITION = /at position (\d+)/;
21
+ const LINE_COLUMN = /line (\d+) column (\d+)/;
22
+ const END_OF_INPUT = /end of (?:JSON )?(?:input|data)/i;
23
+ const offsetOfLineColumn = (text, line, column)=>{
24
+ let offset = 0;
25
+ for(let current = 1; current < line; current++){
26
+ const newline = text.indexOf('\n', offset);
27
+ if (newline < 0) return text.length;
28
+ offset = newline + 1;
29
+ }
30
+ return Math.min(offset + column - 1, text.length);
31
+ };
32
+ const cleanMessage = (message)=>{
33
+ const cleaned = message.replace(/^JSON\.parse: /, '').replace(/^JSON Parse error: /, '').replace(/ (in|after) JSON at position \d+(?: \(line \d+ column \d+\))?$/, (_, word)=>'after' === word ? ' after JSON' : '').replace(/ at line \d+ column \d+ of the JSON data$/, '').replace(/, (?:\.\.\.)?".*"(?:\.\.\.)? is not valid JSON$/s, '').trim();
34
+ if ('' === cleaned) return 'Invalid JSON';
35
+ return cleaned.charAt(0).toUpperCase() + cleaned.slice(1);
36
+ };
37
+ const locateJsonSyntaxError = (message, text)=>{
38
+ const clean = cleanMessage(message);
39
+ const position = POSITION.exec(message);
40
+ if (position) return {
41
+ offset: Number(position[1]),
42
+ message: clean
43
+ };
44
+ const lineColumn = LINE_COLUMN.exec(message);
45
+ if (lineColumn) return {
46
+ offset: offsetOfLineColumn(text, Number(lineColumn[1]), Number(lineColumn[2])),
47
+ message: clean
48
+ };
49
+ if (END_OF_INPUT.test(message)) return {
50
+ offset: text.trimEnd().length,
51
+ message: clean
52
+ };
53
+ return {
54
+ offset: null,
55
+ message: clean
56
+ };
57
+ };
58
+ const rangeAt = (text, relative, base)=>{
59
+ const contentEnd = text.trimEnd().length;
60
+ if (relative >= contentEnd) return {
61
+ from: base + contentEnd - 1,
62
+ to: base + contentEnd
63
+ };
64
+ const from = Math.max(relative, 0);
65
+ return '\n' === text.charAt(from) ? {
66
+ from: base + from,
67
+ to: base + from
68
+ } : {
69
+ from: base + from,
70
+ to: base + from + 1
71
+ };
72
+ };
73
+ const parsedTree = (state, upto, timeout)=>{
74
+ const ensured = ensureSyntaxTree(state, upto, timeout);
75
+ if (ensured) return {
76
+ tree: ensured,
77
+ parsedTo: 1 / 0
78
+ };
79
+ const tree = syntaxTree(state);
80
+ return {
81
+ tree,
82
+ parsedTo: tree.length
83
+ };
84
+ };
85
+ const firstErrorNode = (state, region, timeout)=>{
86
+ const { tree, parsedTo } = parsedTree(state, region.to, timeout);
87
+ let found = null;
88
+ tree.iterate({
89
+ from: region.from,
90
+ to: region.to,
91
+ enter: (node)=>{
92
+ if (found) return false;
93
+ if (!node.type.isError || node.from < region.from) return true;
94
+ if (node.from >= parsedTo) return false;
95
+ found = {
96
+ from: node.from,
97
+ to: node.to
98
+ };
99
+ return false;
100
+ }
101
+ });
102
+ return found;
103
+ };
104
+ const syntaxErrorDiagnostics = (state, region, timeout = ENSURE_TREE_TIMEOUT)=>{
105
+ const length = state.doc.length;
106
+ const { tree, parsedTo } = parsedTree(state, length, timeout);
107
+ const out = [];
108
+ tree.iterate({
109
+ from: region?.from ?? 0,
110
+ to: region?.to ?? length,
111
+ enter: (node)=>{
112
+ if (!node.type.isError) return true;
113
+ if (region && (node.from < region.from || node.from > region.to)) return false;
114
+ if (node.from >= parsedTo) return false;
115
+ const from = node.from;
116
+ const to = Math.min(Math.max(node.to, from + 1), length);
117
+ const last = out.at(-1);
118
+ if (last && from <= last.to) {
119
+ last.to = Math.max(last.to, to);
120
+ return false;
121
+ }
122
+ out.push({
123
+ from,
124
+ to,
125
+ severity: 'error',
126
+ source: SYNTAX_SOURCE,
127
+ message: from < length ? `Unexpected "${state.doc.sliceString(from, from + 1)}"` : 'Unexpected end of input'
128
+ });
129
+ return false;
130
+ }
131
+ });
132
+ return out;
133
+ };
134
+ const ERROR_NODE_LANGUAGES = new Set([
135
+ 'yaml',
136
+ 'python'
137
+ ]);
138
+ const isBabelLanguage = (language)=>"javascript" === language || "typescript" === language;
139
+ const jsonSyntaxDiagnostics = (state, region, timeout = ENSURE_TREE_TIMEOUT)=>{
140
+ const text = state.sliceDoc(region.from, region.to);
141
+ if ('' === text.trim()) return [];
142
+ try {
143
+ JSON.parse(text);
144
+ return [];
145
+ } catch (error) {
146
+ const raw = error instanceof Error ? error.message : String(error);
147
+ const located = locateJsonSyntaxError(raw, text);
148
+ let range;
149
+ if (null !== located.offset) range = rangeAt(text, located.offset, region.from);
150
+ else {
151
+ const node = firstErrorNode(state, region, timeout);
152
+ range = node && node.to > node.from ? node : node ? rangeAt(text, node.from - region.from, region.from) : rangeAt(text, text.length - text.trimStart().length, region.from);
153
+ }
154
+ return [
155
+ {
156
+ from: range.from,
157
+ to: range.to,
158
+ severity: 'error',
159
+ message: located.message,
160
+ source: SYNTAX_SOURCE
161
+ }
162
+ ];
163
+ }
164
+ };
165
+ const defaultEnd = (doc, from)=>from < doc.lineAt(from).to ? from + 1 : from;
166
+ const toCmDiagnostic = (doc, diagnostic, startingLineNumber)=>{
167
+ const from = positionToOffset(doc, diagnostic.from, startingLineNumber);
168
+ if (null === from) return null;
169
+ const to = diagnostic.to ? Math.max(from, positionToOffset(doc, diagnostic.to, startingLineNumber) ?? doc.length) : defaultEnd(doc, from);
170
+ return {
171
+ from,
172
+ to,
173
+ severity: diagnostic.severity,
174
+ message: diagnostic.message,
175
+ ...void 0 === diagnostic.source ? {} : {
176
+ source: diagnostic.source
177
+ }
178
+ };
179
+ };
180
+ const toPublicDiagnostic = (doc, diagnostic, startingLineNumber)=>({
181
+ from: offsetToPosition(doc, diagnostic.from, startingLineNumber),
182
+ to: offsetToPosition(doc, diagnostic.to, startingLineNumber),
183
+ severity: 'hint' === diagnostic.severity ? 'info' : diagnostic.severity,
184
+ message: diagnostic.message,
185
+ ...void 0 === diagnostic.source ? {} : {
186
+ source: diagnostic.source
187
+ }
188
+ });
189
+ const externalDiagnostics = (doc, external, startingLineNumber)=>external.flatMap((diagnostic)=>{
190
+ const converted = toCmDiagnostic(doc, diagnostic, startingLineNumber);
191
+ return converted ? [
192
+ converted
193
+ ] : [];
194
+ });
195
+ const latestLintState = new WeakMap();
196
+ const superseded = ()=>new Promise(()=>void 0);
197
+ const lintSource = (config)=>(view)=>{
198
+ const { state } = view;
199
+ latestLintState.set(view, state);
200
+ const isCurrent = ()=>latestLintState.get(view) === state;
201
+ const region = jsonRegion(state, config.language);
202
+ const external = externalDiagnostics(state.doc, config.external, config.startingLineNumber);
203
+ if (isBabelLanguage(config.language)) return babelSyntaxDiagnostics(state.doc.toString(), config.language).then((syntax)=>isCurrent() ? [
204
+ ...syntax,
205
+ ...external
206
+ ] : superseded(), (error)=>{
207
+ if (!isCurrent()) return superseded();
208
+ console.error('[CodeEditor] failed to load the JavaScript syntax checker', error);
209
+ return external;
210
+ });
211
+ if ('lua' === config.language) return luaSyntaxDiagnostics(state.doc.toString()).then((syntax)=>isCurrent() ? [
212
+ ...syntax,
213
+ ...external
214
+ ] : superseded(), (error)=>{
215
+ if (!isCurrent()) return superseded();
216
+ console.error('[CodeEditor] failed to load the Lua syntax checker', error);
217
+ return external;
218
+ });
219
+ const syntax = region ? jsonSyntaxDiagnostics(state, region) : ERROR_NODE_LANGUAGES.has(config.language) ? syntaxErrorDiagnostics(state) : [];
220
+ const { schemaSource, schema } = config;
221
+ if (!region || syntax.length > 0 || void 0 === schema || !schemaSource || '' === state.sliceDoc(region.from, region.to).trim()) return [
222
+ ...syntax,
223
+ ...external
224
+ ];
225
+ return schemaSource(state, region).then((schemaDiagnostics)=>isCurrent() ? [
226
+ ...schemaDiagnostics.map((d)=>({
227
+ ...d,
228
+ source: d.source ?? SCHEMA_SOURCE
229
+ })),
230
+ ...external
231
+ ] : superseded(), (error)=>{
232
+ if (!isCurrent()) return superseded();
233
+ console.error('[CodeEditor] JSON Schema validation failed', error);
234
+ return external;
235
+ });
236
+ };
237
+ const reported = new WeakMap();
238
+ const EMPTY_LIST = '[]';
239
+ const reportChanges = (config)=>EditorView.updateListener.of((update)=>{
240
+ for (const tr of update.transactions)for (const effect of tr.effects){
241
+ if (!effect.is(setDiagnosticsEffect)) continue;
242
+ const list = effect.value.map((d)=>toPublicDiagnostic(tr.state.doc, d, config.startingLineNumber));
243
+ const key = JSON.stringify(list);
244
+ if ((reported.get(update.view) ?? EMPTY_LIST) !== key) {
245
+ reported.set(update.view, key);
246
+ config.onChange(list);
247
+ }
248
+ }
249
+ });
250
+ const wavyUnderline = (token)=>({
251
+ backgroundImage: 'none',
252
+ textDecorationLine: 'underline',
253
+ textDecorationStyle: 'wavy',
254
+ textDecorationColor: `var(${token})`,
255
+ textDecorationSkipInk: 'none',
256
+ textUnderlineOffset: '3px'
257
+ });
258
+ const ERROR_INDICATOR = '--color-syntax-highlight-error-indicator';
259
+ const WARNING_INDICATOR = '--color-syntax-highlight-warning-indicator';
260
+ const INFO_INDICATOR = '--color-syntax-highlight-info-indicator';
261
+ const diagnosticsTheme = EditorView.theme({
262
+ '.cm-lintRange': {
263
+ paddingBottom: '0'
264
+ },
265
+ '.cm-lintRange-error': wavyUnderline(ERROR_INDICATOR),
266
+ '.cm-lintRange-warning': wavyUnderline(WARNING_INDICATOR),
267
+ '.cm-lintRange-info': wavyUnderline(INFO_INDICATOR),
268
+ '.cm-lintRange-hint': wavyUnderline(INFO_INDICATOR),
269
+ '.cm-lintRange-active': {
270
+ backgroundColor: 'transparent'
271
+ },
272
+ '.cm-lintPoint:after': {
273
+ borderBottomColor: `var(${ERROR_INDICATOR})`
274
+ },
275
+ '.cm-lintPoint-warning:after': {
276
+ borderBottomColor: `var(${WARNING_INDICATOR})`
277
+ },
278
+ '.cm-lintPoint-info:after': {
279
+ borderBottomColor: `var(${INFO_INDICATOR})`
280
+ },
281
+ '.cm-lintPoint-hint:after': {
282
+ borderBottomColor: `var(${INFO_INDICATOR})`
283
+ },
284
+ '.cm-tooltip.cm-tooltip-hover': {
285
+ border: 'none',
286
+ borderRadius: 'var(--radius-8)',
287
+ backgroundColor: 'var(--color-component-tooltip-bg)',
288
+ color: 'var(--color-text-primary-alt)',
289
+ fontFamily: 'var(--font-sans)',
290
+ fontSize: 'var(--text-xs)',
291
+ lineHeight: 'var(--text-xs--line-height)',
292
+ fontWeight: 'var(--font-weight-medium)',
293
+ overflow: 'hidden',
294
+ zIndex: 'var(--tooltip-z-index)'
295
+ },
296
+ '.cm-tooltip-lint': {
297
+ padding: '4px 0'
298
+ },
299
+ '.cm-diagnostic': {
300
+ padding: '0 8px 0 6px',
301
+ marginLeft: '0',
302
+ borderLeft: `2px solid var(${ERROR_INDICATOR})`
303
+ },
304
+ '.cm-diagnostic-warning': {
305
+ borderLeft: `2px solid var(${WARNING_INDICATOR})`
306
+ },
307
+ '.cm-diagnostic-info': {
308
+ borderLeft: `2px solid var(${INFO_INDICATOR})`
309
+ },
310
+ '.cm-diagnostic-hint': {
311
+ borderLeft: `2px solid var(${INFO_INDICATOR})`
312
+ },
313
+ '.cm-diagnosticSource': {
314
+ fontSize: 'var(--text-xs)',
315
+ opacity: '0.7'
316
+ }
317
+ });
318
+ const diagnosticsExtension = (config)=>[
319
+ linter(lintSource(config), {
320
+ delay: LINT_DELAY,
321
+ autoPanel: false,
322
+ needsRefresh: (update)=>update.transactions.some((tr)=>tr.reconfigured)
323
+ }),
324
+ reportChanges(config),
325
+ diagnosticsTheme
326
+ ];
327
+ export { LINT_DELAY, SCHEMA_SOURCE, SYNTAX_SOURCE, diagnosticsExtension, diagnosticsTheme, jsonRegion, jsonSyntaxDiagnostics, locateJsonSyntaxError, syntaxErrorDiagnostics, toCmDiagnostic, toPublicDiagnostic };
@@ -0,0 +1,37 @@
1
+ import { type EditorState, type Extension } from '@codemirror/state';
2
+ import { type BlockInfo, type ViewUpdate } from '@codemirror/view';
3
+ import type { PortalRegistry } from '../lib/portalRegistry';
4
+ export interface DiffExtensionConfig {
5
+ original: string;
6
+ /** Reserved for ReactNode content in diff rows; the `+` / `-` markers are plain text. */
7
+ portals: PortalRegistry;
8
+ }
9
+ /** Semantic hooks for the theme below (never used as test selectors in RTL tests). */
10
+ export declare const DIFF_INSERTED_CLASS = "cm-ds-diff-inserted";
11
+ export declare const DIFF_CHANGED_CLASS = "cm-ds-diff-changed";
12
+ export declare const DIFF_DELETED_CLASS = "cm-ds-diff-deleted";
13
+ /** `true` when `lineFrom` is the start of an inserted or changed line of the current document. */
14
+ export declare const isDiffInsertedLine: (state: EditorState, lineFrom: number) => boolean;
15
+ /**
16
+ * Number of original lines shown by the merge view's deleted-chunk widget for this gutter
17
+ * block, or 0 when the block is not a (non-empty) deleted chunk. The merge view places
18
+ * that widget as a block widget *before* `chunk.fromB`.
19
+ */
20
+ export declare const deletedRowCount: (state: EditorState, block: BlockInfo) => number;
21
+ /**
22
+ * Rows the merge view's deleted-chunk widgets add in diff mode (one per deleted original line),
23
+ * skipping chunks whose widget sits strictly inside a `hidden` (collapsed fold) range — the fold's
24
+ * replace decoration hides those widgets. 0 outside diff mode.
25
+ */
26
+ export declare const diffDeletedRowCount: (state: EditorState, hidden: readonly {
27
+ from: number;
28
+ to: number;
29
+ }[]) => number;
30
+ /** For gutter `lineMarkerChange`: the diff changed although the document may not have. */
31
+ export declare const diffChunksChanged: (update: ViewUpdate) => boolean;
32
+ /**
33
+ * Diff mode (spec §7.15, D7): CodeMirror's unified merge view against `original`, restyled as
34
+ * CodeSnippet `success` / `danger` lines. The `+` / `-` prefixes, colour sticks and blank line
35
+ * numbers of deleted rows are drawn by `guttersExtension({ diff: true })`.
36
+ */
37
+ export declare const diffExtension: (config: DiffExtensionConfig) => Extension;
@@ -0,0 +1,145 @@
1
+ import { getChunks, getOriginalDoc, unifiedMergeView } from "@codemirror/merge";
2
+ import { Prec, RangeSet, StateField } from "@codemirror/state";
3
+ import { BlockType, Decoration, EditorView, GutterMarker, gutterLineClass, gutterWidgetClass, lineNumberMarkers } from "@codemirror/view";
4
+ import { cn } from "../../../utils/cn.js";
5
+ import { LINE_COLOR_STYLES } from "../../CodeSnippet/lib/lineStyles.js";
6
+ const DIFF_INSERTED_CLASS = 'cm-ds-diff-inserted';
7
+ const DIFF_CHANGED_CLASS = 'cm-ds-diff-changed';
8
+ const DIFF_DELETED_CLASS = 'cm-ds-diff-deleted';
9
+ const success = LINE_COLOR_STYLES.success;
10
+ class DiffClassMarker extends GutterMarker {
11
+ elementClass;
12
+ constructor(elementClass){
13
+ super(), this.elementClass = elementClass;
14
+ }
15
+ eq(other) {
16
+ return other instanceof DiffClassMarker && other.elementClass === this.elementClass;
17
+ }
18
+ }
19
+ const insertedGutterBackground = new DiffClassMarker(cn(DIFF_INSERTED_CLASS, success.bg));
20
+ const insertedNumberText = new DiffClassMarker(cn(DIFF_INSERTED_CLASS, success.text));
21
+ const deletedGutterBackground = new DiffClassMarker(cn(DIFF_DELETED_CLASS, LINE_COLOR_STYLES.danger.bg));
22
+ const EMPTY = {
23
+ chunks: null,
24
+ insertedLines: new Set(),
25
+ decorations: Decoration.none,
26
+ gutterBackground: RangeSet.empty,
27
+ numberText: RangeSet.empty
28
+ };
29
+ const buildDiffDecorations = (state)=>{
30
+ const chunks = getChunks(state)?.chunks ?? null;
31
+ if (!chunks) return EMPTY;
32
+ const insertedLines = new Set();
33
+ const decorations = [];
34
+ const gutterBackground = [];
35
+ const numberText = [];
36
+ for (const chunk of chunks){
37
+ if (chunk.fromB >= chunk.toB) continue;
38
+ const changed = chunk.fromA < chunk.toA;
39
+ const lineDecoration = Decoration.line({
40
+ class: cn(DIFF_INSERTED_CLASS, changed && DIFF_CHANGED_CLASS, success.bg)
41
+ });
42
+ const textMark = Decoration.mark({
43
+ class: success.text
44
+ });
45
+ const first = state.doc.lineAt(chunk.fromB).number;
46
+ const last = state.doc.lineAt(chunk.endB).number;
47
+ for(let number = first; number <= last; number++){
48
+ const line = state.doc.line(number);
49
+ insertedLines.add(line.from);
50
+ decorations.push(lineDecoration.range(line.from));
51
+ if (line.length > 0) decorations.push(textMark.range(line.from, line.to));
52
+ gutterBackground.push(insertedGutterBackground.range(line.from));
53
+ numberText.push(insertedNumberText.range(line.from));
54
+ }
55
+ }
56
+ return {
57
+ chunks,
58
+ insertedLines,
59
+ decorations: Decoration.set(decorations, true),
60
+ gutterBackground: RangeSet.of(gutterBackground, true),
61
+ numberText: RangeSet.of(numberText, true)
62
+ };
63
+ };
64
+ const diffDecorationsField = StateField.define({
65
+ create: buildDiffDecorations,
66
+ update: (value, tr)=>getChunks(tr.state)?.chunks === value.chunks ? value : buildDiffDecorations(tr.state),
67
+ provide: (field)=>[
68
+ EditorView.decorations.from(field, (value)=>value.decorations),
69
+ gutterLineClass.from(field, (value)=>value.gutterBackground),
70
+ lineNumberMarkers.from(field, (value)=>value.numberText)
71
+ ]
72
+ });
73
+ const isDiffInsertedLine = (state, lineFrom)=>state.field(diffDecorationsField, false)?.insertedLines.has(lineFrom) ?? false;
74
+ const deletedRowCount = (state, block)=>{
75
+ if (block.type !== BlockType.WidgetBefore) return 0;
76
+ const chunks = getChunks(state)?.chunks;
77
+ if (!chunks) return 0;
78
+ const chunk = chunks.find((candidate)=>candidate.fromB === block.from);
79
+ if (!chunk || chunk.fromA >= chunk.toA) return 0;
80
+ const original = getOriginalDoc(state);
81
+ return original.lineAt(chunk.endA).number - original.lineAt(chunk.fromA).number + 1;
82
+ };
83
+ const diffDeletedRowCount = (state, hidden)=>{
84
+ const chunks = getChunks(state)?.chunks;
85
+ if (!chunks) return 0;
86
+ const original = getOriginalDoc(state);
87
+ let rows = 0;
88
+ for (const chunk of chunks)if (!(chunk.fromA >= chunk.toA)) {
89
+ if (!hidden.some(({ from, to })=>from < chunk.fromB && chunk.fromB <= to)) rows += original.lineAt(chunk.endA).number - original.lineAt(chunk.fromA).number + 1;
90
+ }
91
+ return rows;
92
+ };
93
+ const diffChunksChanged = (update)=>getChunks(update.startState)?.chunks !== getChunks(update.state)?.chunks;
94
+ const deletedGutterClass = gutterWidgetClass.of((view, _widget, block)=>deletedRowCount(view.state, block) > 0 ? deletedGutterBackground : null);
95
+ const diffTheme = EditorView.theme({
96
+ [`.cm-line.${DIFF_INSERTED_CLASS}`]: {
97
+ backgroundColor: 'var(--color-syntax-highlight-success-highlight)'
98
+ },
99
+ [`.cm-gutterElement.${DIFF_INSERTED_CLASS}`]: {
100
+ backgroundColor: 'var(--color-syntax-highlight-success-highlight)'
101
+ },
102
+ [`.cm-lineNumbers .cm-gutterElement.${DIFF_INSERTED_CLASS}`]: {
103
+ color: 'var(--color-syntax-highlight-success-code)'
104
+ },
105
+ [`.cm-gutterElement.${DIFF_DELETED_CLASS}`]: {
106
+ backgroundColor: 'var(--color-syntax-highlight-error-highlight)'
107
+ },
108
+ '& .cm-deletedChunk': {
109
+ backgroundColor: 'var(--color-syntax-highlight-error-highlight)',
110
+ color: 'var(--color-syntax-highlight-error-code)',
111
+ fontWeight: 'var(--font-weight-medium, 450)',
112
+ paddingLeft: '0'
113
+ },
114
+ '& .cm-deletedChunk .cm-deletedLine': {
115
+ padding: '0 12px'
116
+ },
117
+ '&:has(.cm-gutters) .cm-deletedChunk .cm-deletedLine': {
118
+ paddingLeft: '8px'
119
+ },
120
+ '&.cm-merge-b .cm-changedText, & .cm-deletedChunk .cm-deletedText, &.cm-merge-b .cm-deletedText': {
121
+ background: 'none'
122
+ },
123
+ [`.${DIFF_CHANGED_CLASS} .cm-changedText, .${DIFF_CHANGED_CLASS} .cm-changedText *`]: {
124
+ fontWeight: 'var(--font-weight-bold, 700)',
125
+ color: 'var(--color-syntax-highlight-success-code)'
126
+ },
127
+ [`.cm-deletedChunk:has(+ .${DIFF_CHANGED_CLASS}) .cm-deletedText`]: {
128
+ fontWeight: 'var(--font-weight-bold, 700)',
129
+ color: 'var(--color-syntax-highlight-error-code)'
130
+ }
131
+ });
132
+ const diffExtension = (config)=>[
133
+ unifiedMergeView({
134
+ original: config.original,
135
+ highlightChanges: true,
136
+ gutter: false,
137
+ mergeControls: false,
138
+ syntaxHighlightDeletions: false,
139
+ allowInlineDiffs: false
140
+ }),
141
+ Prec.highest(diffDecorationsField.init(buildDiffDecorations)),
142
+ deletedGutterClass,
143
+ diffTheme
144
+ ];
145
+ export { DIFF_CHANGED_CLASS, DIFF_DELETED_CLASS, DIFF_INSERTED_CLASS, deletedRowCount, diffChunksChanged, diffDeletedRowCount, diffExtension, isDiffInsertedLine };
@@ -0,0 +1,25 @@
1
+ import type { EditorState, Extension } from '@codemirror/state';
2
+ import { EditorView } from '@codemirror/view';
3
+ import type { PortalRegistry } from '../lib/portalRegistry';
4
+ import type { CodeEditorFolds } from '../types';
5
+ /** Debounce for re-running function-form `folds` after edits (spec §4). */
6
+ export declare const FOLDS_DEBOUNCE_MS = 150;
7
+ export declare const getCollapsedFoldIds: (state: EditorState) => ReadonlySet<string>;
8
+ export declare const getVisibleRowCount: (state: EditorState) => number;
9
+ /** Document ranges of the collapsed regions (what the fold replace decorations hide). */
10
+ export declare const getCollapsedRanges: (state: EditorState) => {
11
+ from: number;
12
+ to: number;
13
+ }[];
14
+ export declare const toggleFoldRegion: (view: EditorView, id: string) => boolean;
15
+ export declare const foldAllRegions: (view: EditorView) => boolean;
16
+ export declare const unfoldAllRegions: (view: EditorView) => boolean;
17
+ export declare const foldsExtension: (config: {
18
+ folds: CodeEditorFolds | undefined;
19
+ startingLineNumber: number;
20
+ portals: PortalRegistry;
21
+ testId: string | undefined;
22
+ }) => {
23
+ extension: Extension;
24
+ gutter: Extension | null;
25
+ };