@health-samurai/react-components 0.0.0-alpha.18 → 0.0.0-alpha.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/bundle.css +51 -33
- package/dist/src/components/code-editor/fhir-autocomplete.d.ts +70 -0
- package/dist/src/components/code-editor/fhir-autocomplete.d.ts.map +1 -0
- package/dist/src/components/code-editor/fhir-autocomplete.js +1849 -0
- package/dist/src/components/code-editor/fhir-autocomplete.js.map +1 -0
- package/dist/src/components/code-editor/fhir-autocomplete.test.js +1099 -0
- package/dist/src/components/code-editor/fhir-autocomplete.test.js.map +1 -0
- package/dist/src/components/code-editor/http/index.d.ts +9 -1
- package/dist/src/components/code-editor/http/index.d.ts.map +1 -1
- package/dist/src/components/code-editor/http/index.js +423 -3
- package/dist/src/components/code-editor/http/index.js.map +1 -1
- package/dist/src/components/code-editor/index.d.ts +13 -4
- package/dist/src/components/code-editor/index.d.ts.map +1 -1
- package/dist/src/components/code-editor/index.js +505 -96
- package/dist/src/components/code-editor/index.js.map +1 -1
- package/dist/src/components/code-editor/json-ast.d.ts +46 -0
- package/dist/src/components/code-editor/json-ast.d.ts.map +1 -0
- package/dist/src/components/code-editor/json-ast.js +465 -0
- package/dist/src/components/code-editor/json-ast.js.map +1 -0
- package/dist/src/components/code-editor/json-ast.test.js +206 -0
- package/dist/src/components/code-editor/json-ast.test.js.map +1 -0
- package/dist/src/components/code-editor/sql-completion.d.ts +22 -0
- package/dist/src/components/code-editor/sql-completion.d.ts.map +1 -0
- package/dist/src/components/code-editor/sql-completion.js +895 -0
- package/dist/src/components/code-editor/sql-completion.js.map +1 -0
- package/dist/src/components/date-picker-input.d.ts +10 -0
- package/dist/src/components/date-picker-input.d.ts.map +1 -0
- package/dist/src/components/date-picker-input.js +90 -0
- package/dist/src/components/date-picker-input.js.map +1 -0
- package/dist/src/components/date-picker-input.stories.js +76 -0
- package/dist/src/components/date-picker-input.stories.js.map +1 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/shadcn/components/ui/alert-dialog.d.ts +1 -1
- package/dist/src/shadcn/components/ui/calendar.d.ts +1 -1
- package/dist/src/shadcn/components/ui/carousel.d.ts +1 -1
- package/dist/src/shadcn/components/ui/chart.d.ts +3 -3
- package/dist/src/shadcn/components/ui/chart.d.ts.map +1 -1
- package/dist/src/shadcn/components/ui/chart.js +1 -1
- package/dist/src/shadcn/components/ui/chart.js.map +1 -1
- package/dist/src/shadcn/components/ui/command.d.ts +1 -1
- package/dist/src/shadcn/components/ui/pagination.d.ts +1 -1
- package/dist/src/shadcn/components/ui/resizable.stories.js +2 -2
- package/dist/src/shadcn/components/ui/resizable.stories.js.map +1 -1
- package/dist/src/shadcn/components/ui/sidebar.d.ts +4 -4
- package/dist/src/shadcn/components/ui/tabs.d.ts +3 -1
- package/dist/src/shadcn/components/ui/tabs.d.ts.map +1 -1
- package/dist/src/shadcn/components/ui/tabs.js +129 -2
- package/dist/src/shadcn/components/ui/tabs.js.map +1 -1
- package/dist/src/shadcn/components/ui/tabs.stories.js +1 -1
- package/dist/src/shadcn/components/ui/tabs.stories.js.map +1 -1
- package/dist/src/shadcn/components/ui/toggle-group.d.ts +1 -1
- package/dist/src/typography.css +1 -1
- package/package.json +24 -19
- package/src/components/code-editor/fhir-autocomplete.test.ts +993 -0
- package/src/components/code-editor/fhir-autocomplete.ts +2321 -0
- package/src/components/code-editor/http/index.ts +339 -2
- package/src/components/code-editor/index.tsx +593 -102
- package/src/components/code-editor/json-ast.test.ts +230 -0
- package/src/components/code-editor/json-ast.ts +590 -0
- package/src/components/code-editor/sql-completion.ts +1105 -0
- package/src/components/date-picker-input.stories.tsx +79 -0
- package/src/components/date-picker-input.tsx +104 -0
- package/src/index.tsx +1 -0
- package/src/shadcn/components/ui/chart.tsx +6 -3
- package/src/shadcn/components/ui/resizable.stories.tsx +2 -2
- package/src/shadcn/components/ui/tabs.stories.tsx +1 -1
- package/src/shadcn/components/ui/tabs.tsx +160 -2
- package/src/typography.css +1 -1
- package/dist/src/components/code-editor/http/grammar/http.test.d.ts +0 -2
- package/dist/src/components/code-editor/http/grammar/http.test.d.ts.map +0 -1
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { acceptCompletion, autocompletion, closeBrackets, closeBracketsKeymap, completionKeymap } from "@codemirror/autocomplete";
|
|
2
|
+
import { acceptCompletion, autocompletion, closeBrackets, closeBracketsKeymap, completionKeymap, completionStatus, moveCompletionSelection } from "@codemirror/autocomplete";
|
|
3
3
|
import { defaultKeymap, history, historyKeymap } from "@codemirror/commands";
|
|
4
4
|
import { json, jsonParseLinter } from "@codemirror/lang-json";
|
|
5
5
|
import { SQLDialect, sql } from "@codemirror/lang-sql";
|
|
6
6
|
import { yaml } from "@codemirror/lang-yaml";
|
|
7
|
-
import { bracketMatching, foldGutter, foldKeymap, HighlightStyle, indentOnInput, syntaxHighlighting } from "@codemirror/language";
|
|
8
|
-
import { linter,
|
|
7
|
+
import { bracketMatching, foldGutter, foldKeymap, HighlightStyle, indentOnInput, syntaxHighlighting, syntaxTree } from "@codemirror/language";
|
|
8
|
+
import { linter, lintKeymap } from "@codemirror/lint";
|
|
9
9
|
import { closeSearchPanel, findNext, findPrevious, getSearchQuery, highlightSelectionMatches, SearchQuery, search, searchKeymap, setSearchQuery } from "@codemirror/search";
|
|
10
|
-
import { Compartment, EditorState, RangeSet, StateEffect, StateField } from "@codemirror/state";
|
|
11
|
-
import { crosshairCursor, Decoration, drawSelection, dropCursor, EditorView, GutterMarker, gutterLineClass,
|
|
10
|
+
import { Compartment, EditorState, Prec, RangeSet, StateEffect, StateField } from "@codemirror/state";
|
|
11
|
+
import { crosshairCursor, Decoration, drawSelection, dropCursor, EditorView, GutterMarker, gutterLineClass, highlightSpecialChars, keymap, lineNumbers, rectangularSelection } from "@codemirror/view";
|
|
12
12
|
import { tags } from "@lezer/highlight";
|
|
13
|
-
import {
|
|
13
|
+
import { vim } from "@replit/codemirror-vim";
|
|
14
|
+
import { ChevronDown, ChevronsRight, ChevronUp, Heading, Table2, Terminal, X } from "lucide-react";
|
|
14
15
|
import * as React from "react";
|
|
15
16
|
import { flushSync } from "react-dom";
|
|
16
17
|
import { createRoot } from "react-dom/client";
|
|
17
|
-
import { ComplexTypeIcon, SquareFunctionIcon, TypCodeIcon } from "../../icons.js";
|
|
18
|
+
import { ComplexTypeIcon, ResourceIcon, SquareFunctionIcon, TypCodeIcon } from "../../icons.js";
|
|
19
|
+
import { buildFhirCompletionExtension, fhirDiagnosticsField } from "./fhir-autocomplete.js";
|
|
18
20
|
import { http } from "./http/index.js";
|
|
21
|
+
import { buildSqlCompletionExtensions, fetchSqlMetadata } from "./sql-completion.js";
|
|
19
22
|
class ErrorLineGutterMarker extends GutterMarker {
|
|
20
23
|
elementClass = "cm-errorLineGutter";
|
|
21
24
|
}
|
|
@@ -25,33 +28,80 @@ const errorLineDecoration = Decoration.line({
|
|
|
25
28
|
});
|
|
26
29
|
const setIssueLinesEffect = StateEffect.define();
|
|
27
30
|
let errorTooltipEl = null;
|
|
28
|
-
function
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
function formatErrorTypeTitle(code) {
|
|
32
|
+
return code.split("-").map((w)=>w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
33
|
+
}
|
|
34
|
+
function renderErrorCard(msg) {
|
|
35
|
+
const card = document.createElement("div");
|
|
36
|
+
Object.assign(card.style, {
|
|
34
37
|
backgroundColor: "var(--color-bg-primary)",
|
|
35
38
|
border: "1px solid var(--color-border-primary)",
|
|
36
39
|
borderRadius: "var(--radius-md)",
|
|
37
40
|
padding: "6px 10px",
|
|
41
|
+
boxShadow: "0 2px 6px rgba(0, 0, 0, 0.08)"
|
|
42
|
+
});
|
|
43
|
+
const newlineIdx = msg.indexOf("\n");
|
|
44
|
+
if (newlineIdx !== -1) {
|
|
45
|
+
const title = msg.slice(0, newlineIdx);
|
|
46
|
+
const body = msg.slice(newlineIdx + 1);
|
|
47
|
+
const titleEl = document.createElement("div");
|
|
48
|
+
titleEl.textContent = formatErrorTypeTitle(title);
|
|
49
|
+
Object.assign(titleEl.style, {
|
|
50
|
+
fontWeight: "600"
|
|
51
|
+
});
|
|
52
|
+
const hr = document.createElement("div");
|
|
53
|
+
Object.assign(hr.style, {
|
|
54
|
+
borderTop: "1px solid var(--color-border-primary)",
|
|
55
|
+
margin: "4px 0"
|
|
56
|
+
});
|
|
57
|
+
const bodyEl = document.createElement("div");
|
|
58
|
+
bodyEl.textContent = body;
|
|
59
|
+
Object.assign(bodyEl.style, {
|
|
60
|
+
whiteSpace: "pre-wrap"
|
|
61
|
+
});
|
|
62
|
+
card.append(titleEl, hr, bodyEl);
|
|
63
|
+
} else {
|
|
64
|
+
card.textContent = msg;
|
|
65
|
+
card.style.whiteSpace = "pre-wrap";
|
|
66
|
+
}
|
|
67
|
+
return card;
|
|
68
|
+
}
|
|
69
|
+
function showErrorTooltip(message, x, y) {
|
|
70
|
+
hideErrorTooltip();
|
|
71
|
+
const tooltip = document.createElement("div");
|
|
72
|
+
Object.assign(tooltip.style, {
|
|
73
|
+
position: "fixed",
|
|
38
74
|
fontSize: "12px",
|
|
39
75
|
lineHeight: "1.4",
|
|
40
76
|
color: "var(--color-text-error-primary)",
|
|
41
77
|
fontFamily: "var(--font-family-sans)",
|
|
42
|
-
boxShadow: "0 4px 12px rgba(0, 0, 0, 0.1)",
|
|
43
78
|
zIndex: "1000",
|
|
44
79
|
pointerEvents: "none",
|
|
45
80
|
maxWidth: "400px",
|
|
46
|
-
|
|
81
|
+
display: "flex",
|
|
82
|
+
flexDirection: "column",
|
|
83
|
+
gap: "6px"
|
|
47
84
|
});
|
|
85
|
+
const parts = message.split("\n\x00\n");
|
|
86
|
+
for (const part of parts){
|
|
87
|
+
tooltip.append(renderErrorCard(part ?? ""));
|
|
88
|
+
}
|
|
48
89
|
document.body.appendChild(tooltip);
|
|
49
90
|
errorTooltipEl = tooltip;
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
91
|
+
const tooltipRect = tooltip.getBoundingClientRect();
|
|
92
|
+
let top = y - tooltipRect.height - 8;
|
|
93
|
+
// If tooltip goes above viewport, show below cursor instead
|
|
94
|
+
if (top < 4) {
|
|
95
|
+
top = y + 20;
|
|
96
|
+
}
|
|
97
|
+
// If it still goes below viewport, clamp to bottom
|
|
98
|
+
if (top + tooltipRect.height > window.innerHeight - 4) {
|
|
99
|
+
top = window.innerHeight - tooltipRect.height - 4;
|
|
100
|
+
}
|
|
101
|
+
// Final clamp to top
|
|
102
|
+
if (top < 4) top = 4;
|
|
103
|
+
tooltip.style.left = `${x}px`;
|
|
104
|
+
tooltip.style.top = `${top}px`;
|
|
55
105
|
}
|
|
56
106
|
function hideErrorTooltip() {
|
|
57
107
|
errorTooltipEl?.remove();
|
|
@@ -89,6 +139,21 @@ const issueLinesField = StateField.define({
|
|
|
89
139
|
};
|
|
90
140
|
}
|
|
91
141
|
}
|
|
142
|
+
if (tr.docChanged) {
|
|
143
|
+
try {
|
|
144
|
+
return {
|
|
145
|
+
gutterMarkers: state.gutterMarkers.map(tr.changes),
|
|
146
|
+
lineDecorations: state.lineDecorations.map(tr.changes),
|
|
147
|
+
messages: state.messages
|
|
148
|
+
};
|
|
149
|
+
} catch {
|
|
150
|
+
return {
|
|
151
|
+
gutterMarkers: RangeSet.empty,
|
|
152
|
+
lineDecorations: Decoration.none,
|
|
153
|
+
messages: new Map()
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
}
|
|
92
157
|
return state;
|
|
93
158
|
},
|
|
94
159
|
provide (field) {
|
|
@@ -98,28 +163,49 @@ const issueLinesField = StateField.define({
|
|
|
98
163
|
];
|
|
99
164
|
}
|
|
100
165
|
});
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
166
|
+
function getErrorMessageForLine(view, lineNo) {
|
|
167
|
+
const issueMsg = view.state.field(issueLinesField).messages.get(lineNo);
|
|
168
|
+
if (issueMsg) return issueMsg;
|
|
169
|
+
try {
|
|
170
|
+
return view.state.field(fhirDiagnosticsField).messages.get(lineNo);
|
|
171
|
+
} catch {
|
|
172
|
+
return undefined;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
function handleErrorTooltipMove(event, view) {
|
|
176
|
+
const target = event.target;
|
|
177
|
+
const mouseEvent = event;
|
|
178
|
+
// Check gutter line number
|
|
179
|
+
const gutterEl = target.closest(".cm-lineNumbers .cm-gutterElement");
|
|
180
|
+
if (gutterEl) {
|
|
109
181
|
const lineNo = Number.parseInt(gutterEl.textContent ?? "", 10);
|
|
110
|
-
if (Number.isNaN(lineNo)) {
|
|
111
|
-
|
|
112
|
-
|
|
182
|
+
if (!Number.isNaN(lineNo)) {
|
|
183
|
+
const message = getErrorMessageForLine(view, lineNo);
|
|
184
|
+
if (message) {
|
|
185
|
+
showErrorTooltip(message, mouseEvent.clientX, mouseEvent.clientY);
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
113
188
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
189
|
+
hideErrorTooltip();
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
// Check content line (cm-line) — follow cursor
|
|
193
|
+
const lineEl = target.closest(".cm-line");
|
|
194
|
+
if (lineEl) {
|
|
195
|
+
const pos = view.posAtDOM(lineEl);
|
|
196
|
+
const lineNo = view.state.doc.lineAt(pos).number;
|
|
197
|
+
const message = getErrorMessageForLine(view, lineNo);
|
|
198
|
+
if (message) {
|
|
199
|
+
showErrorTooltip(message, mouseEvent.clientX, mouseEvent.clientY);
|
|
118
200
|
return false;
|
|
119
201
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
202
|
+
}
|
|
203
|
+
hideErrorTooltip();
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
const errorTooltipHandler = EditorView.domEventHandlers({
|
|
207
|
+
mouseover: handleErrorTooltipMove,
|
|
208
|
+
mousemove: handleErrorTooltipMove,
|
|
123
209
|
mouseleave () {
|
|
124
210
|
hideErrorTooltip();
|
|
125
211
|
return false;
|
|
@@ -155,23 +241,38 @@ const baseTheme = EditorView.theme({
|
|
|
155
241
|
fontFamily: "var(--font-family-mono)"
|
|
156
242
|
},
|
|
157
243
|
".cm-gutters": {
|
|
158
|
-
backgroundColor: "
|
|
244
|
+
backgroundColor: "transparent",
|
|
159
245
|
border: "none"
|
|
160
246
|
},
|
|
161
247
|
".cm-lineNumbers": {
|
|
162
|
-
|
|
248
|
+
minWidth: "3.5ch"
|
|
163
249
|
},
|
|
164
|
-
".cm-
|
|
250
|
+
".cm-lineNumbers .cm-gutterElement": {
|
|
251
|
+
minWidth: "3.5ch",
|
|
252
|
+
paddingRight: "4px",
|
|
253
|
+
color: "var(--color-text-quaternary)"
|
|
254
|
+
},
|
|
255
|
+
".cm-lineNumbers .cm-gutterElement.cm-activeLineGutter": {
|
|
165
256
|
backgroundColor: "var(--color-bg-primary)",
|
|
166
|
-
color: "var(--color-text-
|
|
257
|
+
color: "var(--color-text-secondary)"
|
|
258
|
+
},
|
|
259
|
+
".cm-activeLineGutter": {
|
|
260
|
+
backgroundColor: "transparent !important"
|
|
167
261
|
},
|
|
168
262
|
".cm-activeLine": {
|
|
169
|
-
backgroundColor: "
|
|
263
|
+
backgroundColor: "transparent !important"
|
|
170
264
|
},
|
|
171
|
-
".cm-errorLineGutter": {
|
|
265
|
+
".cm-lineNumbers .cm-gutterElement.cm-errorLineGutter": {
|
|
172
266
|
color: "var(--color-text-error-primary)",
|
|
173
267
|
backgroundColor: "color-mix(in srgb, var(--color-text-error-primary) 7%, transparent)"
|
|
174
268
|
},
|
|
269
|
+
".cm-foldGutter .cm-gutterElement.cm-errorLineGutter": {
|
|
270
|
+
color: "var(--color-text-error-primary)",
|
|
271
|
+
backgroundColor: "color-mix(in srgb, var(--color-text-error-primary) 7%, transparent)",
|
|
272
|
+
display: "flex",
|
|
273
|
+
alignItems: "center",
|
|
274
|
+
justifyContent: "center"
|
|
275
|
+
},
|
|
175
276
|
".cm-errorLine": {
|
|
176
277
|
backgroundColor: "color-mix(in srgb, var(--color-text-error-primary) 7%, transparent)"
|
|
177
278
|
}
|
|
@@ -187,7 +288,15 @@ const completionTheme = EditorView.theme({
|
|
|
187
288
|
},
|
|
188
289
|
".cm-completionLabel": {
|
|
189
290
|
flex: "1",
|
|
190
|
-
minWidth: "0"
|
|
291
|
+
minWidth: "0",
|
|
292
|
+
fontFamily: "var(--font-family-mono)",
|
|
293
|
+
fontSize: "var(--font-size-sm)",
|
|
294
|
+
lineHeight: "var(--font-leading-5)"
|
|
295
|
+
},
|
|
296
|
+
".cm-completionMatchedText": {
|
|
297
|
+
textDecoration: "none",
|
|
298
|
+
fontWeight: "600",
|
|
299
|
+
color: "var(--color-text-link)"
|
|
191
300
|
},
|
|
192
301
|
".cm-completionDetail": {
|
|
193
302
|
color: "var(--color-text-tertiary)",
|
|
@@ -200,9 +309,10 @@ const completionTheme = EditorView.theme({
|
|
|
200
309
|
border: "1px solid var(--color-border-primary)",
|
|
201
310
|
borderRadius: "var(--radius-md)",
|
|
202
311
|
color: "var(--color-text-secondary)",
|
|
203
|
-
fontFamily: "var(--font-family-
|
|
204
|
-
fontSize: "
|
|
312
|
+
fontFamily: "var(--font-family-mono)",
|
|
313
|
+
fontSize: "14px",
|
|
205
314
|
padding: "8px 12px",
|
|
315
|
+
marginLeft: "8px",
|
|
206
316
|
lineHeight: "1.4",
|
|
207
317
|
whiteSpace: "normal",
|
|
208
318
|
maxWidth: "300px"
|
|
@@ -247,19 +357,34 @@ const readOnlyTheme = EditorView.theme({
|
|
|
247
357
|
border: "none"
|
|
248
358
|
},
|
|
249
359
|
".cm-lineNumbers": {
|
|
250
|
-
|
|
360
|
+
minWidth: "3.5ch"
|
|
251
361
|
},
|
|
252
|
-
".cm-
|
|
362
|
+
".cm-lineNumbers .cm-gutterElement": {
|
|
363
|
+
minWidth: "3.5ch",
|
|
364
|
+
paddingRight: "4px",
|
|
365
|
+
color: "var(--color-text-quaternary)"
|
|
366
|
+
},
|
|
367
|
+
".cm-lineNumbers .cm-gutterElement.cm-activeLineGutter": {
|
|
253
368
|
backgroundColor: "var(--color-bg-secondary)",
|
|
254
|
-
color: "var(--color-text-
|
|
369
|
+
color: "var(--color-text-secondary)"
|
|
370
|
+
},
|
|
371
|
+
".cm-activeLineGutter": {
|
|
372
|
+
backgroundColor: "transparent !important"
|
|
255
373
|
},
|
|
256
374
|
".cm-activeLine": {
|
|
257
|
-
backgroundColor: "
|
|
375
|
+
backgroundColor: "transparent !important"
|
|
258
376
|
},
|
|
259
|
-
".cm-errorLineGutter": {
|
|
377
|
+
".cm-lineNumbers .cm-gutterElement.cm-errorLineGutter": {
|
|
260
378
|
color: "var(--color-text-error-primary)",
|
|
261
379
|
backgroundColor: "color-mix(in srgb, var(--color-text-error-primary) 7%, transparent)"
|
|
262
380
|
},
|
|
381
|
+
".cm-foldGutter .cm-gutterElement.cm-errorLineGutter": {
|
|
382
|
+
color: "var(--color-text-error-primary)",
|
|
383
|
+
backgroundColor: "color-mix(in srgb, var(--color-text-error-primary) 7%, transparent)",
|
|
384
|
+
display: "flex",
|
|
385
|
+
alignItems: "center",
|
|
386
|
+
justifyContent: "center"
|
|
387
|
+
},
|
|
263
388
|
".cm-errorLine": {
|
|
264
389
|
backgroundColor: "color-mix(in srgb, var(--color-text-error-primary) 7%, transparent)"
|
|
265
390
|
}
|
|
@@ -336,6 +461,7 @@ function createSearchPanel(view) {
|
|
|
336
461
|
alignItems: "center",
|
|
337
462
|
gap: "2px",
|
|
338
463
|
padding: "6px 8px",
|
|
464
|
+
marginTop: "4px",
|
|
339
465
|
backgroundColor: "var(--color-bg-primary)",
|
|
340
466
|
border: "1px solid var(--color-border-primary)",
|
|
341
467
|
borderRadius: "var(--radius-md)",
|
|
@@ -460,10 +586,10 @@ function createSearchPanel(view) {
|
|
|
460
586
|
}
|
|
461
587
|
};
|
|
462
588
|
}
|
|
463
|
-
const searchPanelTheme = EditorView.
|
|
464
|
-
".cm-panels-top": {
|
|
589
|
+
const searchPanelTheme = EditorView.theme({
|
|
590
|
+
"& .cm-panels-top": {
|
|
465
591
|
position: "absolute",
|
|
466
|
-
top: "
|
|
592
|
+
top: "8px",
|
|
467
593
|
right: "4px",
|
|
468
594
|
left: "auto",
|
|
469
595
|
zIndex: "10",
|
|
@@ -471,10 +597,13 @@ const searchPanelTheme = EditorView.baseTheme({
|
|
|
471
597
|
border: "none"
|
|
472
598
|
},
|
|
473
599
|
".cm-searchMatch": {
|
|
474
|
-
backgroundColor: "
|
|
600
|
+
backgroundColor: "var(--color-blue-200) !important"
|
|
475
601
|
},
|
|
476
602
|
".cm-searchMatch-selected": {
|
|
477
|
-
backgroundColor: "
|
|
603
|
+
backgroundColor: "var(--color-blue-400) !important"
|
|
604
|
+
},
|
|
605
|
+
".cm-selectionMatch": {
|
|
606
|
+
backgroundColor: "var(--color-blue-100) !important"
|
|
478
607
|
}
|
|
479
608
|
});
|
|
480
609
|
const customSearchExtension = [
|
|
@@ -626,13 +755,89 @@ const customSQLDialect = SQLDialect.define({
|
|
|
626
755
|
keywords: SQL_KEYWORDS.join(" "),
|
|
627
756
|
builtin: SQL_BUILTIN.join(" ")
|
|
628
757
|
});
|
|
629
|
-
function
|
|
758
|
+
function computeYamlNewlineIndent(lineText) {
|
|
759
|
+
const indent = lineText.match(/^(\s*)/)?.[1] ?? "";
|
|
760
|
+
const trimmed = lineText.trimEnd();
|
|
761
|
+
if (trimmed.endsWith(":")) {
|
|
762
|
+
// After "key:" with no value — increase indent
|
|
763
|
+
// For " - key:", base indent is at the dash content level
|
|
764
|
+
const dashMatch = trimmed.match(/^(\s*-\s+)/);
|
|
765
|
+
const baseIndent = dashMatch?.[1] ? " ".repeat(dashMatch[1].length) : indent;
|
|
766
|
+
return `${baseIndent} `;
|
|
767
|
+
}
|
|
768
|
+
if (/^\s*-\s*$/.test(trimmed)) {
|
|
769
|
+
// After bare "- " (array item marker only) — align to content after dash
|
|
770
|
+
const dashMatch = trimmed.match(/^(\s*-\s*)/);
|
|
771
|
+
return dashMatch?.[1] ? " ".repeat(dashMatch[1].length) : indent;
|
|
772
|
+
}
|
|
773
|
+
// Preserve current indent; for " - key: val" align to key level
|
|
774
|
+
const dashKeyMatch = trimmed.match(/^(\s*-\s+)\S/);
|
|
775
|
+
return dashKeyMatch?.[1] ? " ".repeat(dashKeyMatch[1].length) : indent;
|
|
776
|
+
}
|
|
777
|
+
function yamlEnterKeymap() {
|
|
778
|
+
return keymap.of([
|
|
779
|
+
{
|
|
780
|
+
key: "Enter",
|
|
781
|
+
run: (view)=>{
|
|
782
|
+
const { state } = view;
|
|
783
|
+
const pos = state.selection.main.head;
|
|
784
|
+
const line = state.doc.lineAt(pos);
|
|
785
|
+
const newIndent = computeYamlNewlineIndent(line.text);
|
|
786
|
+
view.dispatch({
|
|
787
|
+
changes: {
|
|
788
|
+
from: pos,
|
|
789
|
+
insert: `\n${newIndent}`
|
|
790
|
+
},
|
|
791
|
+
selection: {
|
|
792
|
+
anchor: pos + 1 + newIndent.length
|
|
793
|
+
}
|
|
794
|
+
});
|
|
795
|
+
return true;
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
]);
|
|
799
|
+
}
|
|
800
|
+
function httpYamlEnterKeymap() {
|
|
801
|
+
return keymap.of([
|
|
802
|
+
{
|
|
803
|
+
key: "Enter",
|
|
804
|
+
run: (view)=>{
|
|
805
|
+
const { state } = view;
|
|
806
|
+
const pos = state.selection.main.head;
|
|
807
|
+
const doc = state.doc.toString();
|
|
808
|
+
// Only handle if cursor is in YAML body (after blank line separator)
|
|
809
|
+
const textBeforeCursor = doc.slice(0, pos);
|
|
810
|
+
const blankLineIdx = textBeforeCursor.indexOf("\n\n");
|
|
811
|
+
if (blankLineIdx === -1 || pos <= blankLineIdx + 1) return false;
|
|
812
|
+
// Check if the body looks like YAML (not JSON)
|
|
813
|
+
const bodyStart = blankLineIdx + 2;
|
|
814
|
+
const bodyPrefix = doc.slice(bodyStart, bodyStart + 20).trimStart();
|
|
815
|
+
if (bodyPrefix.startsWith("{") || bodyPrefix.startsWith("[")) return false;
|
|
816
|
+
const line = state.doc.lineAt(pos);
|
|
817
|
+
const newIndent = computeYamlNewlineIndent(line.text);
|
|
818
|
+
view.dispatch({
|
|
819
|
+
changes: {
|
|
820
|
+
from: pos,
|
|
821
|
+
insert: `\n${newIndent}`
|
|
822
|
+
},
|
|
823
|
+
selection: {
|
|
824
|
+
anchor: pos + 1 + newIndent.length
|
|
825
|
+
}
|
|
826
|
+
});
|
|
827
|
+
return true;
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
]);
|
|
831
|
+
}
|
|
832
|
+
function languageExtensions(mode, sqlExtraBuiltins, getUrlSuggestions) {
|
|
630
833
|
if (mode === "http") {
|
|
631
834
|
const jsonLang = json();
|
|
632
835
|
const yamlLang = yaml();
|
|
633
836
|
return [
|
|
634
|
-
http((ct)=>ct === "application/json" ? jsonLang.language : ct === "text/yaml" || ct === "application/yaml" || ct === "application/x-yaml" ? yamlLang.language : null),
|
|
635
|
-
syntaxHighlighting(customHighlightStyle)
|
|
837
|
+
http((ct)=>ct === "application/json" ? jsonLang.language : ct === "text/yaml" || ct === "application/yaml" || ct === "application/x-yaml" ? yamlLang.language : null, getUrlSuggestions),
|
|
838
|
+
syntaxHighlighting(customHighlightStyle),
|
|
839
|
+
jsonAutoExpandBraces(),
|
|
840
|
+
httpYamlEnterKeymap()
|
|
636
841
|
];
|
|
637
842
|
} else if (mode === "sql") {
|
|
638
843
|
let dialect = customSQLDialect;
|
|
@@ -654,21 +859,86 @@ function languageExtensions(mode, sqlExtraBuiltins) {
|
|
|
654
859
|
} else if (mode === "yaml") {
|
|
655
860
|
return [
|
|
656
861
|
yaml(),
|
|
657
|
-
syntaxHighlighting(customHighlightStyle)
|
|
862
|
+
syntaxHighlighting(customHighlightStyle),
|
|
863
|
+
yamlEnterKeymap()
|
|
658
864
|
];
|
|
659
865
|
} else {
|
|
660
866
|
return [
|
|
661
867
|
json(),
|
|
662
|
-
linter(
|
|
868
|
+
linter((view)=>{
|
|
869
|
+
if (!view.state.doc.toString().trim()) return [];
|
|
870
|
+
return jsonParseLinter()(view);
|
|
871
|
+
}, {
|
|
663
872
|
delay: 300
|
|
664
873
|
}),
|
|
665
|
-
syntaxHighlighting(customHighlightStyle)
|
|
874
|
+
syntaxHighlighting(customHighlightStyle),
|
|
875
|
+
jsonAutoExpandBraces()
|
|
666
876
|
];
|
|
667
877
|
}
|
|
668
878
|
}
|
|
669
|
-
|
|
879
|
+
function jsonAutoExpandBraces() {
|
|
880
|
+
return EditorState.transactionFilter.of((tr)=>{
|
|
881
|
+
if (!tr.docChanged) return tr;
|
|
882
|
+
let braceFrom = -1;
|
|
883
|
+
let braceTo = -1;
|
|
884
|
+
let changeCount = 0;
|
|
885
|
+
tr.changes.iterChanges((fromA, toA, _fromB, _toB, inserted)=>{
|
|
886
|
+
changeCount++;
|
|
887
|
+
if (inserted.toString() === "{}") {
|
|
888
|
+
braceFrom = fromA;
|
|
889
|
+
braceTo = toA;
|
|
890
|
+
}
|
|
891
|
+
});
|
|
892
|
+
if (changeCount !== 1 || braceFrom === -1) return tr;
|
|
893
|
+
const tree = syntaxTree(tr.startState);
|
|
894
|
+
const nodeBefore = tree.resolveInner(braceFrom, -1);
|
|
895
|
+
if (nodeBefore.name === "String" || nodeBefore.parent?.name === "String") {
|
|
896
|
+
return tr;
|
|
897
|
+
}
|
|
898
|
+
const line = tr.startState.doc.lineAt(braceFrom);
|
|
899
|
+
const indent = line.text.match(/^(\s*)/)?.[1] ?? "";
|
|
900
|
+
const inner = `${indent} `;
|
|
901
|
+
// Check if { is inside an extension array — insert {"url": ""} snippet
|
|
902
|
+
const docText = tr.startState.doc.toString();
|
|
903
|
+
const textBefore = docText.slice(0, braceFrom);
|
|
904
|
+
const isInExtArray = /"(?:extension|modifierExtension)"\s*:\s*\[\s*(?:\{[\s\S]*?\}\s*,?\s*)*$/s.test(textBefore);
|
|
905
|
+
if (isInExtArray) {
|
|
906
|
+
const insert = `{\n${inner}"url": ""\n${indent}}`;
|
|
907
|
+
return {
|
|
908
|
+
changes: {
|
|
909
|
+
from: braceFrom,
|
|
910
|
+
to: braceTo,
|
|
911
|
+
insert
|
|
912
|
+
},
|
|
913
|
+
selection: {
|
|
914
|
+
anchor: braceFrom + insert.lastIndexOf('""') + 1
|
|
915
|
+
}
|
|
916
|
+
};
|
|
917
|
+
}
|
|
918
|
+
return {
|
|
919
|
+
changes: {
|
|
920
|
+
from: braceFrom,
|
|
921
|
+
to: braceTo,
|
|
922
|
+
insert: `{\n${inner}\n${indent}}`
|
|
923
|
+
},
|
|
924
|
+
selection: {
|
|
925
|
+
anchor: braceFrom + 2 + inner.length
|
|
926
|
+
}
|
|
927
|
+
};
|
|
928
|
+
});
|
|
929
|
+
}
|
|
930
|
+
export function CodeEditor({ defaultValue, currentValue, onChange, onUpdate, viewCallback, readOnly = false, id, mode = "json", isReadOnlyTheme = false, additionalExtensions, issueLineNumbers, foldGutter: enableFoldGutter = true, lineNumbers: enableLineNumbers = true, sql, getStructureDefinitions, resourceTypeHint, expandValueSet, getUrlSuggestions, vimMode = false }) {
|
|
670
931
|
const domRef = React.useRef(null);
|
|
671
932
|
const [view, setView] = React.useState(null);
|
|
933
|
+
const safeDispatch = React.useCallback((spec)=>{
|
|
934
|
+
try {
|
|
935
|
+
view?.dispatch(spec);
|
|
936
|
+
} catch {
|
|
937
|
+
// Ignore RangeError from stale decoration positions during reconfigure
|
|
938
|
+
}
|
|
939
|
+
}, [
|
|
940
|
+
view
|
|
941
|
+
]);
|
|
672
942
|
const initialValue = React.useRef(defaultValue ?? "");
|
|
673
943
|
const onChangeComparment = React.useRef(new Compartment());
|
|
674
944
|
const onUpdateComparment = React.useRef(new Compartment());
|
|
@@ -676,6 +946,11 @@ export function CodeEditor({ defaultValue, currentValue, onChange, onUpdate, vie
|
|
|
676
946
|
const readOnlyCompartment = React.useRef(new Compartment());
|
|
677
947
|
const themeCompartment = React.useRef(new Compartment());
|
|
678
948
|
const additionalExtensionsCompartment = React.useRef(new Compartment());
|
|
949
|
+
const sqlCompletionCompartment = React.useRef(new Compartment());
|
|
950
|
+
const fhirCompletionCompartment = React.useRef(new Compartment());
|
|
951
|
+
const vimCompartment = React.useRef(new Compartment());
|
|
952
|
+
const [sqlFunctions, setSqlFunctions] = React.useState();
|
|
953
|
+
const executeSqlRef = React.useRef(sql?.executeSql);
|
|
679
954
|
React.useEffect(()=>{
|
|
680
955
|
if (!domRef.current) {
|
|
681
956
|
return;
|
|
@@ -685,6 +960,7 @@ export function CodeEditor({ defaultValue, currentValue, onChange, onUpdate, vie
|
|
|
685
960
|
state: EditorState.create({
|
|
686
961
|
doc: initialValue.current,
|
|
687
962
|
extensions: [
|
|
963
|
+
vimCompartment.current.of(vimMode ? vim() : []),
|
|
688
964
|
EditorView.contentAttributes.of({
|
|
689
965
|
"data-gramm": "false"
|
|
690
966
|
}),
|
|
@@ -693,7 +969,18 @@ export function CodeEditor({ defaultValue, currentValue, onChange, onUpdate, vie
|
|
|
693
969
|
lineNumbers()
|
|
694
970
|
] : [],
|
|
695
971
|
...enableFoldGutter ? [
|
|
696
|
-
foldGutter(
|
|
972
|
+
foldGutter({
|
|
973
|
+
markerDOM: (open)=>{
|
|
974
|
+
const el = document.createElement("span");
|
|
975
|
+
el.style.display = "flex";
|
|
976
|
+
el.style.alignItems = "center";
|
|
977
|
+
el.style.justifyContent = "center";
|
|
978
|
+
el.style.width = "100%";
|
|
979
|
+
el.style.height = "100%";
|
|
980
|
+
el.innerHTML = open ? '<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></svg>' : '<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>';
|
|
981
|
+
return el;
|
|
982
|
+
}
|
|
983
|
+
})
|
|
697
984
|
] : [],
|
|
698
985
|
highlightSpecialChars(),
|
|
699
986
|
history(),
|
|
@@ -707,6 +994,7 @@ export function CodeEditor({ defaultValue, currentValue, onChange, onUpdate, vie
|
|
|
707
994
|
autocompletion({
|
|
708
995
|
icons: false,
|
|
709
996
|
maxRenderedOptions: 1000,
|
|
997
|
+
defaultKeymap: false,
|
|
710
998
|
addToOptions: [
|
|
711
999
|
{
|
|
712
1000
|
render: renderCompletionIcon,
|
|
@@ -723,29 +1011,56 @@ export function CodeEditor({ defaultValue, currentValue, onChange, onUpdate, vie
|
|
|
723
1011
|
}),
|
|
724
1012
|
rectangularSelection(),
|
|
725
1013
|
crosshairCursor(),
|
|
726
|
-
highlightActiveLine(),
|
|
727
|
-
highlightActiveLineGutter(),
|
|
728
1014
|
highlightSelectionMatches(),
|
|
1015
|
+
Prec.highest(keymap.of([
|
|
1016
|
+
{
|
|
1017
|
+
key: "Tab",
|
|
1018
|
+
run: (v)=>{
|
|
1019
|
+
if (completionStatus(v.state) === "active") {
|
|
1020
|
+
return moveCompletionSelection(true)(v);
|
|
1021
|
+
}
|
|
1022
|
+
return false;
|
|
1023
|
+
}
|
|
1024
|
+
},
|
|
1025
|
+
{
|
|
1026
|
+
key: "Shift-Tab",
|
|
1027
|
+
run: (v)=>{
|
|
1028
|
+
if (completionStatus(v.state) === "active") {
|
|
1029
|
+
return moveCompletionSelection(false)(v);
|
|
1030
|
+
}
|
|
1031
|
+
return false;
|
|
1032
|
+
}
|
|
1033
|
+
},
|
|
1034
|
+
{
|
|
1035
|
+
key: "Enter",
|
|
1036
|
+
run: (v)=>{
|
|
1037
|
+
if (completionStatus(v.state) === "active") {
|
|
1038
|
+
return acceptCompletion(v);
|
|
1039
|
+
}
|
|
1040
|
+
return false;
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
])),
|
|
729
1044
|
themeCompartment.current.of(baseTheme),
|
|
730
1045
|
completionTheme,
|
|
731
1046
|
keymap.of([
|
|
732
1047
|
...closeBracketsKeymap,
|
|
1048
|
+
...completionKeymap.filter((b)=>b.key !== "Enter"),
|
|
733
1049
|
...defaultKeymap,
|
|
734
1050
|
...searchKeymap,
|
|
735
1051
|
...historyKeymap,
|
|
736
1052
|
...foldKeymap,
|
|
737
|
-
...completionKeymap,
|
|
738
1053
|
...lintKeymap
|
|
739
1054
|
]),
|
|
740
|
-
...enableLintGutter ? [
|
|
741
|
-
lintGutter()
|
|
742
|
-
] : [],
|
|
743
1055
|
issueLinesField,
|
|
744
1056
|
errorTooltipHandler,
|
|
1057
|
+
EditorView.exceptionSink.of(()=>{}),
|
|
745
1058
|
...customSearchExtension,
|
|
746
1059
|
onChangeComparment.current.of([]),
|
|
747
1060
|
onUpdateComparment.current.of([]),
|
|
748
|
-
additionalExtensionsCompartment.current.of([])
|
|
1061
|
+
additionalExtensionsCompartment.current.of([]),
|
|
1062
|
+
sqlCompletionCompartment.current.of([]),
|
|
1063
|
+
fhirCompletionCompartment.current.of([])
|
|
749
1064
|
]
|
|
750
1065
|
})
|
|
751
1066
|
});
|
|
@@ -757,7 +1072,55 @@ export function CodeEditor({ defaultValue, currentValue, onChange, onUpdate, vie
|
|
|
757
1072
|
}, [
|
|
758
1073
|
enableFoldGutter,
|
|
759
1074
|
enableLineNumbers,
|
|
760
|
-
|
|
1075
|
+
vimMode
|
|
1076
|
+
]);
|
|
1077
|
+
React.useEffect(()=>{
|
|
1078
|
+
executeSqlRef.current = sql?.executeSql;
|
|
1079
|
+
});
|
|
1080
|
+
React.useEffect(()=>{
|
|
1081
|
+
if (!view || !sql) {
|
|
1082
|
+
if (view) {
|
|
1083
|
+
safeDispatch({
|
|
1084
|
+
effects: sqlCompletionCompartment.current.reconfigure([])
|
|
1085
|
+
});
|
|
1086
|
+
}
|
|
1087
|
+
setSqlFunctions(undefined);
|
|
1088
|
+
return;
|
|
1089
|
+
}
|
|
1090
|
+
let cancelled = false;
|
|
1091
|
+
fetchSqlMetadata(sql.executeSql).then((metadata)=>{
|
|
1092
|
+
if (cancelled) return;
|
|
1093
|
+
setSqlFunctions(metadata.functions);
|
|
1094
|
+
const extensions = buildSqlCompletionExtensions(metadata, (query, type)=>executeSqlRef.current?.(query, type) ?? Promise.resolve([]));
|
|
1095
|
+
safeDispatch({
|
|
1096
|
+
effects: sqlCompletionCompartment.current.reconfigure(extensions)
|
|
1097
|
+
});
|
|
1098
|
+
}).catch(()=>{});
|
|
1099
|
+
return ()=>{
|
|
1100
|
+
cancelled = true;
|
|
1101
|
+
};
|
|
1102
|
+
}, [
|
|
1103
|
+
view,
|
|
1104
|
+
sql,
|
|
1105
|
+
safeDispatch
|
|
1106
|
+
]);
|
|
1107
|
+
React.useEffect(()=>{
|
|
1108
|
+
if (!view) return;
|
|
1109
|
+
if (getStructureDefinitions) {
|
|
1110
|
+
safeDispatch({
|
|
1111
|
+
effects: fhirCompletionCompartment.current.reconfigure(buildFhirCompletionExtension(getStructureDefinitions, resourceTypeHint, expandValueSet))
|
|
1112
|
+
});
|
|
1113
|
+
} else {
|
|
1114
|
+
safeDispatch({
|
|
1115
|
+
effects: fhirCompletionCompartment.current.reconfigure([])
|
|
1116
|
+
});
|
|
1117
|
+
}
|
|
1118
|
+
}, [
|
|
1119
|
+
view,
|
|
1120
|
+
getStructureDefinitions,
|
|
1121
|
+
resourceTypeHint,
|
|
1122
|
+
expandValueSet,
|
|
1123
|
+
safeDispatch
|
|
761
1124
|
]);
|
|
762
1125
|
React.useEffect(()=>{
|
|
763
1126
|
if (viewCallback && view) {
|
|
@@ -768,7 +1131,7 @@ export function CodeEditor({ defaultValue, currentValue, onChange, onUpdate, vie
|
|
|
768
1131
|
viewCallback
|
|
769
1132
|
]);
|
|
770
1133
|
React.useEffect(()=>{
|
|
771
|
-
|
|
1134
|
+
safeDispatch({
|
|
772
1135
|
effects: onChangeComparment.current.reconfigure([
|
|
773
1136
|
EditorView.updateListener.of((update)=>{
|
|
774
1137
|
if (update.docChanged && onChange) {
|
|
@@ -778,11 +1141,11 @@ export function CodeEditor({ defaultValue, currentValue, onChange, onUpdate, vie
|
|
|
778
1141
|
])
|
|
779
1142
|
});
|
|
780
1143
|
}, [
|
|
781
|
-
|
|
782
|
-
|
|
1144
|
+
onChange,
|
|
1145
|
+
safeDispatch
|
|
783
1146
|
]);
|
|
784
1147
|
React.useEffect(()=>{
|
|
785
|
-
|
|
1148
|
+
safeDispatch({
|
|
786
1149
|
effects: onUpdateComparment.current.reconfigure([
|
|
787
1150
|
EditorView.updateListener.of((update)=>{
|
|
788
1151
|
if (onUpdate) {
|
|
@@ -792,8 +1155,8 @@ export function CodeEditor({ defaultValue, currentValue, onChange, onUpdate, vie
|
|
|
792
1155
|
])
|
|
793
1156
|
});
|
|
794
1157
|
}, [
|
|
795
|
-
|
|
796
|
-
|
|
1158
|
+
onUpdate,
|
|
1159
|
+
safeDispatch
|
|
797
1160
|
]);
|
|
798
1161
|
// FIXME: it is probably better to have CM manage its state.
|
|
799
1162
|
React.useEffect(()=>{
|
|
@@ -802,7 +1165,7 @@ export function CodeEditor({ defaultValue, currentValue, onChange, onUpdate, vie
|
|
|
802
1165
|
}
|
|
803
1166
|
const currentDoc = view.state.doc.toString();
|
|
804
1167
|
if (currentDoc !== currentValue) {
|
|
805
|
-
|
|
1168
|
+
safeDispatch({
|
|
806
1169
|
changes: {
|
|
807
1170
|
from: 0,
|
|
808
1171
|
to: currentDoc.length,
|
|
@@ -812,69 +1175,98 @@ export function CodeEditor({ defaultValue, currentValue, onChange, onUpdate, vie
|
|
|
812
1175
|
}
|
|
813
1176
|
}, [
|
|
814
1177
|
currentValue,
|
|
815
|
-
view
|
|
1178
|
+
view,
|
|
1179
|
+
safeDispatch
|
|
1180
|
+
]);
|
|
1181
|
+
const getUrlSuggestionsRef = React.useRef(getUrlSuggestions);
|
|
1182
|
+
getUrlSuggestionsRef.current = getUrlSuggestions;
|
|
1183
|
+
const stableGetUrlSuggestions = React.useMemo(()=>{
|
|
1184
|
+
if (!getUrlSuggestions) return undefined;
|
|
1185
|
+
return (path, method)=>getUrlSuggestionsRef.current?.(path, method) ?? [];
|
|
1186
|
+
}, [
|
|
1187
|
+
getUrlSuggestions
|
|
816
1188
|
]);
|
|
817
1189
|
React.useEffect(()=>{
|
|
818
1190
|
if (view === null) {
|
|
819
1191
|
return;
|
|
820
1192
|
}
|
|
821
|
-
|
|
822
|
-
effects: languageCompartment.current.reconfigure(languageExtensions(mode,
|
|
1193
|
+
safeDispatch({
|
|
1194
|
+
effects: languageCompartment.current.reconfigure(languageExtensions(mode, sqlFunctions, stableGetUrlSuggestions))
|
|
823
1195
|
});
|
|
824
1196
|
}, [
|
|
825
1197
|
mode,
|
|
826
1198
|
view,
|
|
827
|
-
|
|
1199
|
+
sqlFunctions,
|
|
1200
|
+
stableGetUrlSuggestions,
|
|
1201
|
+
safeDispatch
|
|
828
1202
|
]);
|
|
829
1203
|
React.useEffect(()=>{
|
|
830
1204
|
if (view === null) {
|
|
831
1205
|
return;
|
|
832
1206
|
}
|
|
833
|
-
|
|
1207
|
+
safeDispatch({
|
|
834
1208
|
effects: [
|
|
835
1209
|
readOnlyCompartment.current.reconfigure(EditorState.readOnly.of(readOnly))
|
|
836
1210
|
]
|
|
837
1211
|
});
|
|
838
1212
|
}, [
|
|
839
1213
|
readOnly,
|
|
840
|
-
view
|
|
1214
|
+
view,
|
|
1215
|
+
safeDispatch
|
|
841
1216
|
]);
|
|
842
1217
|
React.useEffect(()=>{
|
|
843
1218
|
if (view === null) {
|
|
844
1219
|
return;
|
|
845
1220
|
}
|
|
846
|
-
|
|
1221
|
+
safeDispatch({
|
|
847
1222
|
effects: [
|
|
848
1223
|
themeCompartment.current.reconfigure(isReadOnlyTheme ? readOnlyTheme : baseTheme)
|
|
849
1224
|
]
|
|
850
1225
|
});
|
|
851
1226
|
}, [
|
|
852
1227
|
isReadOnlyTheme,
|
|
853
|
-
view
|
|
1228
|
+
view,
|
|
1229
|
+
safeDispatch
|
|
854
1230
|
]);
|
|
855
1231
|
React.useEffect(()=>{
|
|
856
1232
|
if (view === null) {
|
|
857
1233
|
return;
|
|
858
1234
|
}
|
|
859
|
-
|
|
1235
|
+
safeDispatch({
|
|
1236
|
+
effects: [
|
|
1237
|
+
vimCompartment.current.reconfigure(vimMode ? vim() : [])
|
|
1238
|
+
]
|
|
1239
|
+
});
|
|
1240
|
+
}, [
|
|
1241
|
+
vimMode,
|
|
1242
|
+
view,
|
|
1243
|
+
safeDispatch
|
|
1244
|
+
]);
|
|
1245
|
+
React.useEffect(()=>{
|
|
1246
|
+
if (view === null) {
|
|
1247
|
+
return;
|
|
1248
|
+
}
|
|
1249
|
+
safeDispatch({
|
|
860
1250
|
effects: [
|
|
861
1251
|
additionalExtensionsCompartment.current.reconfigure(additionalExtensions ?? [])
|
|
862
1252
|
]
|
|
863
1253
|
});
|
|
864
1254
|
}, [
|
|
865
1255
|
additionalExtensions,
|
|
866
|
-
view
|
|
1256
|
+
view,
|
|
1257
|
+
safeDispatch
|
|
867
1258
|
]);
|
|
868
1259
|
React.useEffect(()=>{
|
|
869
1260
|
if (view === null) {
|
|
870
1261
|
return;
|
|
871
1262
|
}
|
|
872
|
-
|
|
1263
|
+
safeDispatch({
|
|
873
1264
|
effects: setIssueLinesEffect.of(issueLineNumbers ?? [])
|
|
874
1265
|
});
|
|
875
1266
|
}, [
|
|
876
1267
|
issueLineNumbers,
|
|
877
|
-
view
|
|
1268
|
+
view,
|
|
1269
|
+
safeDispatch
|
|
878
1270
|
]);
|
|
879
1271
|
return /*#__PURE__*/ _jsx("div", {
|
|
880
1272
|
className: "h-full w-full",
|
|
@@ -937,7 +1329,15 @@ const KeywordIcon = ()=>/*#__PURE__*/ _jsx(Terminal, {
|
|
|
937
1329
|
size: 16,
|
|
938
1330
|
color: "#717684"
|
|
939
1331
|
});
|
|
940
|
-
const OperatorIcon = ()=>/*#__PURE__*/ _jsx(
|
|
1332
|
+
const OperatorIcon = ()=>/*#__PURE__*/ _jsx(ChevronsRight, {
|
|
1333
|
+
size: 16,
|
|
1334
|
+
color: "#717684"
|
|
1335
|
+
});
|
|
1336
|
+
const TableIcon = ()=>/*#__PURE__*/ _jsx(Table2, {
|
|
1337
|
+
size: 16,
|
|
1338
|
+
color: "#717684"
|
|
1339
|
+
});
|
|
1340
|
+
const HeaderIcon = ()=>/*#__PURE__*/ _jsx(Heading, {
|
|
941
1341
|
size: 16,
|
|
942
1342
|
color: "#717684"
|
|
943
1343
|
});
|
|
@@ -945,15 +1345,22 @@ function getCompletionIcon(completion) {
|
|
|
945
1345
|
if (completion.type === "function") return SquareFunctionIcon;
|
|
946
1346
|
if (completion.type === "keyword") return KeywordIcon;
|
|
947
1347
|
if (completion.type === "operator") return OperatorIcon;
|
|
1348
|
+
if (completion.type === "table") return TableIcon;
|
|
1349
|
+
if (completion.type === "header") return HeaderIcon;
|
|
1350
|
+
if (completion.type === "text") return TypCodeIcon;
|
|
1351
|
+
if (completion.type === "type") return ResourceIcon;
|
|
1352
|
+
if (completion.type === "search-param") return null;
|
|
948
1353
|
const detail = completion.detail;
|
|
949
1354
|
if (!detail) {
|
|
950
1355
|
if (completion.type === "variable") return SquareFunctionIcon;
|
|
951
|
-
return
|
|
1356
|
+
return TypCodeIcon;
|
|
952
1357
|
}
|
|
953
1358
|
const typeName = detail.replace(/\[\]$/, "");
|
|
954
|
-
if (!typeName) return
|
|
1359
|
+
if (!typeName) return TypCodeIcon;
|
|
1360
|
+
// Search param types (TOKEN, REFERENCE) — no icon
|
|
1361
|
+
if (typeName === typeName.toUpperCase()) return null;
|
|
955
1362
|
const firstChar = typeName[0];
|
|
956
|
-
if (!firstChar) return
|
|
1363
|
+
if (!firstChar) return TypCodeIcon;
|
|
957
1364
|
const isComplex = firstChar === firstChar.toUpperCase();
|
|
958
1365
|
return isComplex ? ComplexTypeIcon : TypCodeIcon;
|
|
959
1366
|
}
|
|
@@ -965,6 +1372,8 @@ function renderCompletionIcon(completion) {
|
|
|
965
1372
|
flushSync(()=>{
|
|
966
1373
|
createRoot(container).render(/*#__PURE__*/ _jsx(Icon, {}));
|
|
967
1374
|
});
|
|
1375
|
+
} else {
|
|
1376
|
+
container.style.display = "none";
|
|
968
1377
|
}
|
|
969
1378
|
return container;
|
|
970
1379
|
}
|