@deckbuilderkit/editor 0.0.0-stage → 0.1.1

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 (56) hide show
  1. package/package.json +53 -4
  2. package/src/ArchiveView.tsx +111 -0
  3. package/src/CodeEditor.tsx +265 -0
  4. package/src/CodeView.tsx +67 -0
  5. package/src/DiffView.tsx +139 -0
  6. package/src/EnvView.tsx +42 -0
  7. package/src/FilePreview.tsx +108 -0
  8. package/src/FontView.tsx +48 -0
  9. package/src/HarView.tsx +70 -0
  10. package/src/HexView.tsx +28 -0
  11. package/src/HtmlPreview.tsx +23 -0
  12. package/src/MarkdownEditor.tsx +90 -0
  13. package/src/MarkdownToc.tsx +135 -0
  14. package/src/MarkdownView.tsx +84 -0
  15. package/src/MediaView.tsx +17 -0
  16. package/src/NotebookView.tsx +74 -0
  17. package/src/PdfView.tsx +289 -0
  18. package/src/RequestEditor.tsx +419 -0
  19. package/src/ResponseView.tsx +89 -0
  20. package/src/SqlGraph.tsx +322 -0
  21. package/src/SqlSteps.tsx +487 -0
  22. package/src/SqliteView.tsx +68 -0
  23. package/src/SvgView.tsx +231 -0
  24. package/src/TableView.tsx +85 -0
  25. package/src/VarSuggest.tsx +125 -0
  26. package/src/curl-file.ts +70 -0
  27. package/src/editorOptions.ts +83 -0
  28. package/src/editorOptionsContext.ts +4 -0
  29. package/src/fileKinds.ts +69 -0
  30. package/src/http-client.ts +9 -0
  31. package/src/http-import.ts +594 -0
  32. package/src/http.ts +251 -0
  33. package/src/import-warning.ts +8 -0
  34. package/src/index.ts +56 -0
  35. package/src/languages.ts +107 -0
  36. package/src/markdown.ts +123 -0
  37. package/src/markdownLive.ts +289 -0
  38. package/src/options.ts +3 -0
  39. package/src/pdf-url.d.ts +4 -0
  40. package/src/sql/format.ts +637 -0
  41. package/src/sql/formatOptions.ts +71 -0
  42. package/src/sql/lineage.ts +365 -0
  43. package/src/sql/lint.ts +951 -0
  44. package/src/sql/markerHighlight.ts +50 -0
  45. package/src/sql/markers.ts +138 -0
  46. package/src/sql/outline.ts +662 -0
  47. package/src/sql/statements.ts +83 -0
  48. package/src/sql/tokens.ts +156 -0
  49. package/src/sql/xmlQuery.ts +249 -0
  50. package/src/sqlProblems.ts +40 -0
  51. package/src/table.ts +89 -0
  52. package/src/text-save.ts +28 -0
  53. package/src/theme.ts +86 -0
  54. package/src/toc.ts +11 -0
  55. package/src/variables.ts +123 -0
  56. package/README.md +0 -3
package/src/theme.ts ADDED
@@ -0,0 +1,86 @@
1
+ import { HighlightStyle, syntaxHighlighting } from '@codemirror/language';
2
+ import { EditorView } from '@codemirror/view';
3
+ import { tags } from '@lezer/highlight';
4
+
5
+ // Only the CSS tokens from @deckbuilderkit/ui's styles.css, so every palette (and a palette switch
6
+ // while the editor is open) works with no per-palette code.
7
+
8
+ export const editorTheme = EditorView.theme({
9
+ // Fills its host when the host has a height, else grows with the content.
10
+ '&': { height: '100%', backgroundColor: 'var(--bg-sunken)', color: 'var(--text)', fontSize: 'var(--editor-font-size, 12.5px)' },
11
+ '&.cm-focused': { outline: 'none' },
12
+ '.cm-scroller': { overflow: 'auto', fontFamily: 'var(--font-mono)', lineHeight: 'var(--editor-line-height, 1.55)' },
13
+ '.cm-content': { caretColor: 'var(--accent)', fontVariantLigatures: 'var(--editor-ligatures, normal)' },
14
+ '.cm-cursor, .cm-dropCursor': { borderLeftColor: 'var(--accent)' },
15
+ '&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection':
16
+ { backgroundColor: 'color-mix(in srgb, var(--accent) 35%, transparent)' },
17
+ '.cm-gutters': { backgroundColor: 'var(--bg-elev)', color: 'var(--text-3)', borderRight: '1px solid var(--border-soft)' },
18
+ // Translucent: the selection layer is drawn behind the lines, so an opaque one would hide it.
19
+ '.cm-activeLine': { backgroundColor: 'color-mix(in srgb, var(--hover) 60%, transparent)' },
20
+ '.cm-activeLineGutter': { backgroundColor: 'var(--hover)', color: 'var(--text-2)' },
21
+ '.cm-searchMatch': { backgroundColor: 'var(--mark)' },
22
+ '.cm-searchMatch.cm-searchMatch-selected': { backgroundColor: 'var(--mark)', outline: '1px solid var(--warn)' },
23
+ '.cm-selectionMatch': { backgroundColor: 'var(--accent-soft)', outline: '1px solid color-mix(in srgb, var(--accent) 50%, transparent)' },
24
+ '&.cm-focused .cm-matchingBracket': { backgroundColor: 'var(--accent-soft)', outline: '1px solid var(--accent)' },
25
+ '&.cm-focused .cm-nonmatchingBracket': { backgroundColor: 'var(--err-soft)' },
26
+ '.cm-placeholder': { color: 'var(--text-3)' },
27
+ '.cm-panels': { backgroundColor: 'var(--bg-elev)', color: 'var(--text)' },
28
+ '.cm-panels.cm-panels-top': { borderBottom: '1px solid var(--border)' },
29
+ '.cm-panels.cm-panels-bottom': { borderTop: '1px solid var(--border)' },
30
+ '.cm-panel input, .cm-panel button': { fontSize: '12px' },
31
+ '.cm-panel.cm-search input, .cm-panel.cm-search button': { fontFamily: 'inherit' },
32
+ '.cm-panel.cm-search input.cm-textfield': {
33
+ backgroundColor: 'var(--bg-sunken)', color: 'var(--text)', border: '1px solid var(--border)', borderRadius: '4px',
34
+ },
35
+ '.cm-panel.cm-search input.cm-textfield:focus': { outline: 'none', borderColor: 'var(--accent)' },
36
+ '.cm-panel.cm-search button.cm-button': {
37
+ backgroundImage: 'none', backgroundColor: 'var(--panel-2)', color: 'var(--text)', border: '1px solid var(--border)', borderRadius: '4px',
38
+ },
39
+ '.cm-panel.cm-search button.cm-button:hover': { backgroundColor: 'var(--hover)' },
40
+ '.cm-panel.cm-search button.cm-button:active': { backgroundImage: 'none', backgroundColor: 'var(--selected)' },
41
+ '.cm-panel.cm-search label': { color: 'var(--text-2)' },
42
+ '.cm-panel.cm-search input[type=checkbox]': { accentColor: 'var(--accent)' },
43
+ '.cm-panel.cm-search button[name=close]': { backgroundColor: 'transparent', border: 'none', color: 'var(--text-2)' },
44
+ '.cm-tooltip': { backgroundColor: 'var(--panel-2)', color: 'var(--text)', border: '1px solid var(--border)' },
45
+ '.cm-tooltip-autocomplete > ul > li[aria-selected]': { backgroundColor: 'var(--selected)', color: 'var(--text)' },
46
+ '.cm-completionDetail': { color: 'var(--text-3)' },
47
+ '.cm-completionMatchedText': { color: 'var(--accent)', textDecoration: 'none' },
48
+ // Lint: theme colours instead of @codemirror/lint's fixed SVG squiggles and icons.
49
+ '.cm-lintRange': { backgroundImage: 'none', textDecorationLine: 'underline', textDecorationStyle: 'wavy', textDecorationSkipInk: 'none', textUnderlineOffset: '3px' },
50
+ '.cm-lintRange-error': { textDecorationColor: 'var(--err)' },
51
+ '.cm-lintRange-warning': { textDecorationColor: 'var(--warn)' },
52
+ '.cm-lintRange-info, .cm-lintRange-hint': { textDecorationColor: 'var(--info)' },
53
+ '.cm-lintRange-active': { backgroundColor: 'var(--mark)' },
54
+ '.cm-lintPoint:after': { borderBottomColor: 'var(--warn)' },
55
+ '.cm-lintPoint-error:after': { borderBottomColor: 'var(--err)' },
56
+ '.cm-lintPoint-info:after': { borderBottomColor: 'var(--info)' },
57
+ '.cm-tooltip-lint': { padding: '0' },
58
+ '.cm-diagnostic': { padding: '4px 8px', marginLeft: '0', borderLeft: '3px solid var(--border)', color: 'var(--text)', fontFamily: 'var(--font-sans, inherit)', fontSize: '12px' },
59
+ '.cm-diagnostic-error': { borderLeftColor: 'var(--err)' },
60
+ '.cm-diagnostic-warning': { borderLeftColor: 'var(--warn)' },
61
+ '.cm-diagnostic-info, .cm-diagnostic-hint': { borderLeftColor: 'var(--info)' },
62
+ '.cm-diagnosticSource': { color: 'var(--text-3)', opacity: '1' },
63
+ '.cm-gutter-lint': { width: '12px' },
64
+ '.cm-gutter-lint .cm-gutterElement': { padding: '0', display: 'flex', alignItems: 'center', justifyContent: 'center' },
65
+ '.cm-lint-marker': { content: 'normal', width: '7px', height: '7px', borderRadius: '50%', backgroundColor: 'var(--info)' },
66
+ '.cm-lint-marker-error': { content: 'normal', backgroundColor: 'var(--err)' },
67
+ '.cm-lint-marker-warning': { content: 'normal', backgroundColor: 'var(--warn)' },
68
+ '.cm-lint-marker-info': { content: 'normal', backgroundColor: 'var(--info)' },
69
+ '.cm-panel.cm-panel-lint ul [aria-selected]': { backgroundColor: 'var(--selected)', backgroundImage: 'none' },
70
+ });
71
+
72
+ export const highlightStyle = HighlightStyle.define([
73
+ { tag: [tags.keyword, tags.operatorKeyword, tags.modifier, tags.controlKeyword], color: 'var(--y-key)' },
74
+ { tag: [tags.propertyName, tags.attributeName, tags.heading], color: 'var(--y-key)' },
75
+ { tag: [tags.string, tags.special(tags.string), tags.regexp, tags.link, tags.url], color: 'var(--y-str)' },
76
+ { tag: [tags.number, tags.integer, tags.float], color: 'var(--y-num)' },
77
+ { tag: [tags.bool, tags.atom, tags.null, tags.constant(tags.name)], color: 'var(--y-bool)' },
78
+ { tag: [tags.comment, tags.lineComment, tags.blockComment], color: 'var(--y-comment)', fontStyle: 'italic' },
79
+ { tag: tags.invalid, color: 'var(--err)' },
80
+ { tag: tags.strong, fontWeight: '600' },
81
+ { tag: tags.emphasis, fontStyle: 'italic' },
82
+ { tag: tags.heading, fontWeight: '600' },
83
+ ]);
84
+
85
+ /** Theme + syntax colours, for every EditorView in this package. */
86
+ export const themeExtension = [editorTheme, syntaxHighlighting(highlightStyle)];
package/src/toc.ts ADDED
@@ -0,0 +1,11 @@
1
+ /** Index of the section being read: the last heading whose top (relative to the container's top) is at or above `offset`; the last heading when scrolled to the bottom, 0 before the first one. */
2
+ export function activeHeading(tops: readonly number[], offset: number, atBottom: boolean): number {
3
+ if (tops.length === 0) return -1;
4
+ if (atBottom) return tops.length - 1;
5
+ let active = 0;
6
+ for (let i = 0; i < tops.length; i++) {
7
+ if (tops[i]! <= offset) active = i;
8
+ else break;
9
+ }
10
+ return active;
11
+ }
@@ -0,0 +1,123 @@
1
+ import { REST_BUILTIN_VARIABLES, type RestEnvVar, restVarRefs } from '@deckbuilderkit/server-core/protocol';
2
+ import type { Completion, CompletionContext, CompletionResult, CompletionSource } from '@codemirror/autocomplete';
3
+ import { EditorState, type Extension } from '@codemirror/state';
4
+ import { Decoration, type DecorationSet, type EditorView, ViewPlugin, type ViewUpdate } from '@codemirror/view';
5
+ import { t } from '@deckbuilderkit/ui/i18n';
6
+
7
+ export interface VariableInfo {
8
+ defined: boolean;
9
+ /** Hover text: the value of a plain variable, a label for secret, built-in or undefined ones. */
10
+ title: string;
11
+ }
12
+
13
+ /** Whether `name` resolves against `vars` (enabled ones; a secret one only when set) or the built-ins. */
14
+ export function variableInfo(name: string, vars: readonly RestEnvVar[]): VariableInfo {
15
+ if ((REST_BUILTIN_VARIABLES as readonly string[]).includes(name)) return { defined: true, title: t('http.varBuiltin', { name }) };
16
+ const v = vars.find((x) => x.enabled && x.name === name && (!x.secret || x.set));
17
+ if (!v) return { defined: false, title: t('http.varUndefined', { name }) };
18
+ return { defined: true, title: v.secret ? t('http.varSecret', { name }) : `${name} = ${v.value}` };
19
+ }
20
+
21
+ export const variableClass = (defined: boolean) => `req-var ${defined ? 'req-var-ok' : 'req-var-bad'}`;
22
+
23
+ function decorate(view: EditorView, vars: readonly RestEnvVar[]): DecorationSet {
24
+ const marks = restVarRefs(view.state.doc.toString()).map(({ name, from, to }) => {
25
+ const info = variableInfo(name, vars);
26
+ return Decoration.mark({ class: variableClass(info.defined), attributes: { title: info.title } }).range(from, to);
27
+ });
28
+ return Decoration.set(marks);
29
+ }
30
+
31
+ /** Marks `{{name}}` green when defined, red when not; the title shows the value (never a secret one). */
32
+ export function variableHighlight(vars: readonly RestEnvVar[]): Extension {
33
+ return ViewPlugin.fromClass(class {
34
+ decorations: DecorationSet;
35
+ constructor(view: EditorView) {
36
+ this.decorations = decorate(view, vars);
37
+ }
38
+ update(u: ViewUpdate) {
39
+ if (u.docChanged) this.decorations = decorate(u.view, vars);
40
+ }
41
+ }, { decorations: (p) => p.decorations });
42
+ }
43
+
44
+ /** A variable `{{` can complete to: its name and what the list shows beside it. */
45
+ export interface VariableOption {
46
+ name: string;
47
+ /** The value, or a label for a secret or built-in one. */
48
+ detail: string;
49
+ }
50
+
51
+ /** The names `{{` offers: enabled variables (a secret one only when set), then the built-ins. */
52
+ export function variableOptions(vars: readonly RestEnvVar[]): VariableOption[] {
53
+ const out: VariableOption[] = [];
54
+ const seen = new Set<string>();
55
+ for (const v of vars) {
56
+ if (!v.enabled || (v.secret && !v.set) || seen.has(v.name)) continue;
57
+ seen.add(v.name);
58
+ out.push({ name: v.name, detail: v.secret ? t('http.varTagSecret') : v.value });
59
+ }
60
+ for (const name of REST_BUILTIN_VARIABLES) out.push({ name, detail: t('http.varTagBuiltin') });
61
+ return out;
62
+ }
63
+
64
+ /** The options whose name contains `partial` (ignoring case), those starting with it first. */
65
+ export function filterVariableOptions(options: readonly VariableOption[], partial: string): VariableOption[] {
66
+ const p = partial.toLowerCase();
67
+ const hits = options.filter((o) => o.name.toLowerCase().includes(p));
68
+ return [...hits.filter((o) => o.name.toLowerCase().startsWith(p)), ...hits.filter((o) => !o.name.toLowerCase().startsWith(p))];
69
+ }
70
+
71
+ /** An unfinished `{{name` ending at the caret: where the `{{` starts and the name typed so far. */
72
+ export interface VariableQuery {
73
+ from: number;
74
+ partial: string;
75
+ }
76
+
77
+ const OPEN_REF = /\{\{\s*(\$?[\w.-]*)$/;
78
+
79
+ /** The `{{partial` right before `caret` in `text`, or null. */
80
+ export function variableQueryAt(text: string, caret: number): VariableQuery | null {
81
+ const m = OPEN_REF.exec(text.slice(0, caret));
82
+ return m ? { from: m.index, partial: m[1]! } : null;
83
+ }
84
+
85
+ /** `text` with the `{{partial` before `caret` (and the rest of the name and a `}}` after it) replaced by `{{name}}`; the caret goes after it. */
86
+ export function insertVariable(text: string, caret: number, name: string): { text: string; caret: number } {
87
+ const q = variableQueryAt(text, caret);
88
+ const from = q ? q.from : caret;
89
+ const tail = /^[\w.-]*(\s*\}\})?/.exec(text.slice(caret))![0];
90
+ const ref = `{{${name}}}`;
91
+ return { text: text.slice(0, from) + ref + text.slice(caret + tail.length), caret: from + ref.length };
92
+ }
93
+
94
+ /** What completing `name` inserts when `after` follows the completed range: `name}}`, or only `name` when `}}` is already there. */
95
+ export function variableCompletionText(name: string, after: string): string {
96
+ return /^\s*\}\}/.test(after) ? name : `${name}}}`;
97
+ }
98
+
99
+ /** Completes `{{name` in a CodeMirror editor with the variables and built-ins. */
100
+ export function variableCompletion(vars: readonly RestEnvVar[]): CompletionSource {
101
+ const options: Completion[] = variableOptions(vars).map(({ name, detail }) => ({
102
+ label: name,
103
+ detail,
104
+ type: 'variable',
105
+ apply: (view, _c, from, to) => {
106
+ const after = view.state.sliceDoc(to, to + 40);
107
+ const insert = variableCompletionText(name, after);
108
+ const skip = insert === name ? (/^\s*\}\}/.exec(after)![0].length) : 0;
109
+ view.dispatch({ changes: { from, to, insert }, selection: { anchor: from + insert.length + skip }, userEvent: 'input.complete' });
110
+ },
111
+ }));
112
+ return (ctx: CompletionContext): CompletionResult | null => {
113
+ const m = ctx.matchBefore(/\{\{\s*\$?[\w.-]*$/);
114
+ if (!m) return null;
115
+ const q = variableQueryAt(m.text, m.text.length)!;
116
+ return { from: m.to - q.partial.length, options, validFor: /^\$?[\w.-]*$/ };
117
+ };
118
+ }
119
+
120
+ /** `variableCompletion` as language data, so it adds to the language's own completions. */
121
+ export function variableAutocomplete(vars: readonly RestEnvVar[]): Extension {
122
+ return EditorState.languageData.of(() => [{ autocomplete: variableCompletion(vars) }]);
123
+ }
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.