@nbt-dev/components 0.0.8 → 0.0.10
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/dist/{chunk-WBZS7I6V.js → chunk-AI2LKTA4.js} +30 -2
- package/dist/chunk-AI2LKTA4.js.map +7 -0
- package/dist/{chunk-ZOGIPYYM.js → chunk-BNA2MLFE.js} +153 -18
- package/dist/chunk-BNA2MLFE.js.map +7 -0
- package/dist/{chunk-VDIHQ2NS.js → chunk-HTAPMIK6.js} +11 -3
- package/dist/chunk-HTAPMIK6.js.map +7 -0
- package/dist/{chunk-S7VBQE6Y.js → chunk-TNIHD62H.js} +115 -31
- package/dist/chunk-TNIHD62H.js.map +7 -0
- package/dist/core/auth.d.ts +2 -0
- package/dist/core/index.d.ts +1 -1
- package/dist/core/use-cartridge-info.d.ts +1 -0
- package/dist/editor/editor-themes.d.ts +8 -0
- package/dist/editor/index.d.ts +2 -0
- package/dist/editor/index.js +7 -3
- package/dist/editor/nbt-editor.d.ts +4 -0
- package/dist/graph/index.js +2 -2
- package/dist/index.js +20 -8
- package/dist/index.js.map +2 -2
- package/dist/styles.css +1 -1
- package/dist/table/column-widths.d.ts +2 -0
- package/dist/table/index.js +2 -2
- package/package.json +3 -2
- package/src/core/auth.ts +40 -0
- package/src/core/data-store.ts +5 -1
- package/src/core/index.ts +2 -0
- package/src/core/use-cartridge-info.ts +15 -4
- package/src/editor/editor-themes.ts +77 -0
- package/src/editor/index.ts +2 -0
- package/src/editor/nbt-editor.tsx +60 -26
- package/src/graph/diagram.tsx +16 -0
- package/src/table/column-widths.ts +34 -0
- package/src/table/data-table.tsx +148 -16
- package/dist/chunk-S7VBQE6Y.js.map +0 -7
- package/dist/chunk-VDIHQ2NS.js.map +0 -7
- package/dist/chunk-WBZS7I6V.js.map +0 -7
- package/dist/chunk-ZOGIPYYM.js.map +0 -7
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
// CodeMirror 6 React wrapper for one NBT buffer. Recreated per uri (keyed by
|
|
2
2
|
// the host); owns the EditorView lifecycle, Ctrl+S save, and the LSP wiring.
|
|
3
|
+
// Theme / font size / line-wrap / tab size are live-tunable via compartments so
|
|
4
|
+
// changing a setting doesn't blow away cursor + scroll.
|
|
3
5
|
|
|
4
6
|
import React from "react";
|
|
5
|
-
import { EditorState } from "@codemirror/state";
|
|
7
|
+
import { Compartment, EditorState, Prec } from "@codemirror/state";
|
|
6
8
|
import {
|
|
7
9
|
EditorView,
|
|
8
10
|
keymap,
|
|
@@ -20,40 +22,34 @@ import {
|
|
|
20
22
|
syntaxHighlighting,
|
|
21
23
|
defaultHighlightStyle,
|
|
22
24
|
bracketMatching,
|
|
25
|
+
indentUnit,
|
|
23
26
|
} from "@codemirror/language";
|
|
24
27
|
import { lintGutter } from "@codemirror/lint";
|
|
25
28
|
import { searchKeymap } from "@codemirror/search";
|
|
26
29
|
import { completionKeymap, closeBrackets } from "@codemirror/autocomplete";
|
|
27
30
|
import { nbtLanguageSupport } from "./nbt-language";
|
|
31
|
+
import { themeExtension } from "./editor-themes";
|
|
28
32
|
import { lspExtensions, type GotoDefHandler } from "./lsp-extensions";
|
|
29
33
|
import { NbtLspClient } from "./lsp-client";
|
|
30
34
|
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
|
|
34
|
-
{
|
|
35
|
-
"&": {
|
|
36
|
-
height: "100%",
|
|
37
|
-
fontSize: "12px",
|
|
38
|
-
backgroundColor: "var(--background)",
|
|
39
|
-
color: "var(--foreground)",
|
|
40
|
-
},
|
|
35
|
+
// Sizing only — the selected color theme owns the rest. Built per font-size so
|
|
36
|
+
// the theme compartment can carry both.
|
|
37
|
+
function sizingTheme(fontSize: number) {
|
|
38
|
+
return EditorView.theme({
|
|
39
|
+
"&": { height: "100%", fontSize: `${fontSize}px` },
|
|
41
40
|
".cm-content": { fontFamily: "ui-monospace, monospace" },
|
|
42
|
-
".cm-gutters": {
|
|
43
|
-
backgroundColor: "var(--background)",
|
|
44
|
-
color: "var(--muted-foreground)",
|
|
45
|
-
borderRight: "1px solid var(--border)",
|
|
46
|
-
},
|
|
47
|
-
".cm-activeLine": { backgroundColor: "color-mix(in srgb, var(--accent) 35%, transparent)" },
|
|
48
41
|
"&.cm-focused": { outline: "none" },
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
)
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// `Prec.high` so this wins the indentUnit/tabSize facets over the
|
|
46
|
+
// `indentUnit.of(" ")` baked into nbtLanguageSupport().
|
|
47
|
+
function indentConfig(tabSize: number) {
|
|
48
|
+
return Prec.high([
|
|
49
|
+
indentUnit.of(" ".repeat(tabSize)),
|
|
50
|
+
EditorState.tabSize.of(tabSize),
|
|
51
|
+
]);
|
|
52
|
+
}
|
|
57
53
|
|
|
58
54
|
export type NbtEditorProps = {
|
|
59
55
|
uri: string;
|
|
@@ -63,6 +59,10 @@ export type NbtEditorProps = {
|
|
|
63
59
|
onChange?: (text: string) => void;
|
|
64
60
|
onSave?: (text: string) => void;
|
|
65
61
|
onGotoDefinition?: GotoDefHandler;
|
|
62
|
+
theme?: string;
|
|
63
|
+
fontSize?: number;
|
|
64
|
+
lineWrap?: boolean;
|
|
65
|
+
tabSize?: number;
|
|
66
66
|
};
|
|
67
67
|
|
|
68
68
|
export const NbtEditor: React.FC<NbtEditorProps> = ({
|
|
@@ -73,6 +73,10 @@ export const NbtEditor: React.FC<NbtEditorProps> = ({
|
|
|
73
73
|
onChange,
|
|
74
74
|
onSave,
|
|
75
75
|
onGotoDefinition,
|
|
76
|
+
theme = "default",
|
|
77
|
+
fontSize = 12,
|
|
78
|
+
lineWrap = false,
|
|
79
|
+
tabSize = 2,
|
|
76
80
|
}) => {
|
|
77
81
|
const hostRef = React.useRef<HTMLDivElement | null>(null);
|
|
78
82
|
const viewRef = React.useRef<EditorView | null>(null);
|
|
@@ -85,10 +89,20 @@ export const NbtEditor: React.FC<NbtEditorProps> = ({
|
|
|
85
89
|
const onGotoRef = React.useRef(onGotoDefinition);
|
|
86
90
|
onGotoRef.current = onGotoDefinition;
|
|
87
91
|
|
|
92
|
+
// Live-tunable settings live in compartments. Keep the latest values in a ref
|
|
93
|
+
// so a view rebuild (new uri/mode) seeds the right config without listing the
|
|
94
|
+
// settings in the create-effect deps.
|
|
95
|
+
const themeComp = React.useRef(new Compartment());
|
|
96
|
+
const wrapComp = React.useRef(new Compartment());
|
|
97
|
+
const indentComp = React.useRef(new Compartment());
|
|
98
|
+
const settingsRef = React.useRef({ theme, fontSize, lineWrap, tabSize });
|
|
99
|
+
settingsRef.current = { theme, fontSize, lineWrap, tabSize };
|
|
100
|
+
|
|
88
101
|
React.useEffect(() => {
|
|
89
102
|
const host = hostRef.current;
|
|
90
103
|
if (!host) return;
|
|
91
104
|
|
|
105
|
+
const s = settingsRef.current;
|
|
92
106
|
const extensions = [
|
|
93
107
|
lineNumbers(),
|
|
94
108
|
history(),
|
|
@@ -99,7 +113,9 @@ export const NbtEditor: React.FC<NbtEditorProps> = ({
|
|
|
99
113
|
lintGutter(),
|
|
100
114
|
syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
|
|
101
115
|
nbtLanguageSupport(),
|
|
102
|
-
|
|
116
|
+
indentComp.current.of(indentConfig(s.tabSize)),
|
|
117
|
+
themeComp.current.of([sizingTheme(s.fontSize), themeExtension(s.theme)]),
|
|
118
|
+
wrapComp.current.of(s.lineWrap ? EditorView.lineWrapping : []),
|
|
103
119
|
keymap.of([
|
|
104
120
|
{
|
|
105
121
|
key: "Mod-s",
|
|
@@ -135,8 +151,26 @@ export const NbtEditor: React.FC<NbtEditorProps> = ({
|
|
|
135
151
|
viewRef.current = null;
|
|
136
152
|
};
|
|
137
153
|
// Recreate per buffer identity / mode; `value` is only the initial doc.
|
|
154
|
+
// Settings changes are handled by the reconfigure effect below.
|
|
138
155
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
139
156
|
}, [uri, readOnly, lsp]);
|
|
140
157
|
|
|
158
|
+
// Retune the live view when a setting changes — no destroy, so cursor/scroll
|
|
159
|
+
// survive.
|
|
160
|
+
React.useEffect(() => {
|
|
161
|
+
const view = viewRef.current;
|
|
162
|
+
if (!view) return;
|
|
163
|
+
view.dispatch({
|
|
164
|
+
effects: [
|
|
165
|
+
themeComp.current.reconfigure([
|
|
166
|
+
sizingTheme(fontSize),
|
|
167
|
+
themeExtension(theme),
|
|
168
|
+
]),
|
|
169
|
+
wrapComp.current.reconfigure(lineWrap ? EditorView.lineWrapping : []),
|
|
170
|
+
indentComp.current.reconfigure(indentConfig(tabSize)),
|
|
171
|
+
],
|
|
172
|
+
});
|
|
173
|
+
}, [theme, fontSize, lineWrap, tabSize]);
|
|
174
|
+
|
|
141
175
|
return <div ref={hostRef} className="h-full min-h-0 overflow-hidden" />;
|
|
142
176
|
};
|
package/src/graph/diagram.tsx
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
ReactFlowProvider,
|
|
9
9
|
useNodesInitialized,
|
|
10
10
|
useReactFlow,
|
|
11
|
+
useStore,
|
|
11
12
|
type Edge,
|
|
12
13
|
type Node,
|
|
13
14
|
} from "@xyflow/react";
|
|
@@ -35,9 +36,24 @@ const nodeTypes = { entity: EntityNode };
|
|
|
35
36
|
const FitOnReady: React.FC<{ fitKey: string }> = ({ fitKey }) => {
|
|
36
37
|
const initialized = useNodesInitialized();
|
|
37
38
|
const { fitView } = useReactFlow();
|
|
39
|
+
// Container dimensions tracked by ReactFlow's store; change when the canvas is
|
|
40
|
+
// resized (window, sidebar, or the selection rail opening on the right).
|
|
41
|
+
const width = useStore((s) => s.width);
|
|
42
|
+
const height = useStore((s) => s.height);
|
|
43
|
+
|
|
38
44
|
React.useEffect(() => {
|
|
39
45
|
if (initialized) fitView({ padding: 0.25, duration: 200 });
|
|
40
46
|
}, [initialized, fitKey, fitView]);
|
|
47
|
+
|
|
48
|
+
// When the selection rail opens it shrinks the canvas; without a refit a node
|
|
49
|
+
// near the right edge ends up hidden behind the rail. Refit on any resize,
|
|
50
|
+
// debounced so dragging the rail's resize handle stays smooth.
|
|
51
|
+
React.useEffect(() => {
|
|
52
|
+
if (!initialized || width === 0 || height === 0) return;
|
|
53
|
+
const t = setTimeout(() => fitView({ padding: 0.25, duration: 200 }), 120);
|
|
54
|
+
return () => clearTimeout(t);
|
|
55
|
+
}, [width, height, initialized, fitView]);
|
|
56
|
+
|
|
41
57
|
return null;
|
|
42
58
|
};
|
|
43
59
|
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// Per-table column-width overrides, persisted to localStorage. Keyed by
|
|
2
|
+
// `<cart>:<entity>` so each table keeps its own widths; the inner map is
|
|
3
|
+
// fieldName -> pixel width. A missing entry means "use the default heuristic".
|
|
4
|
+
|
|
5
|
+
const KEY = "nbt_devtools_colw:v1";
|
|
6
|
+
|
|
7
|
+
type Blob = Record<string, Record<string, number>>;
|
|
8
|
+
|
|
9
|
+
function readBlob(): Blob {
|
|
10
|
+
try {
|
|
11
|
+
const raw = localStorage.getItem(KEY);
|
|
12
|
+
if (!raw) return {};
|
|
13
|
+
const parsed = JSON.parse(raw);
|
|
14
|
+
return typeof parsed === "object" && parsed ? (parsed as Blob) : {};
|
|
15
|
+
} catch {
|
|
16
|
+
return {};
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function loadColWidths(cart: string, entity: string): Record<string, number> {
|
|
21
|
+
const blob = readBlob();
|
|
22
|
+
return blob[`${cart}:${entity}`] ?? {};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function saveColWidth(cart: string, entity: string, col: string, w: number): void {
|
|
26
|
+
try {
|
|
27
|
+
const blob = readBlob();
|
|
28
|
+
const k = `${cart}:${entity}`;
|
|
29
|
+
(blob[k] ??= {})[col] = w;
|
|
30
|
+
localStorage.setItem(KEY, JSON.stringify(blob));
|
|
31
|
+
} catch {
|
|
32
|
+
/* storage unavailable (private mode / SSR) — widths stay in-memory only */
|
|
33
|
+
}
|
|
34
|
+
}
|
package/src/table/data-table.tsx
CHANGED
|
@@ -3,10 +3,12 @@ import { useBulkSubscription } from "../core/use-bulk-stream";
|
|
|
3
3
|
import { cn } from "../core/utils";
|
|
4
4
|
import { TYPE_DATETIME, TYPE_DOCUMENT } from "../generated/bulk-protocol";
|
|
5
5
|
import ValuePopover, { type ValuePopoverData } from "./value-popover";
|
|
6
|
+
import { loadColWidths, saveColWidth } from "./column-widths";
|
|
6
7
|
|
|
7
8
|
const ROW_H = 22;
|
|
8
9
|
const OVERSCAN = 6;
|
|
9
10
|
const MIN_COL_W = 80;
|
|
11
|
+
const MAX_COL_W = 600;
|
|
10
12
|
const GUTTER_W = 32;
|
|
11
13
|
|
|
12
14
|
const READONLY_FIELDS = new Set(["id", "createdAt", "updatedAt"]);
|
|
@@ -19,7 +21,7 @@ type DataTableProps = {
|
|
|
19
21
|
onSelectRow: (rowJson: Record<string, string>) => void;
|
|
20
22
|
};
|
|
21
23
|
|
|
22
|
-
function
|
|
24
|
+
function defaultColWidth(name: string): number {
|
|
23
25
|
if (name === "id") return 220;
|
|
24
26
|
if (name === "createdAt" || name === "updatedAt") return 180;
|
|
25
27
|
if (name === "email" || name === "phone") return 180;
|
|
@@ -27,6 +29,85 @@ function colWidth(name: string): number {
|
|
|
27
29
|
return Math.max(MIN_COL_W, Math.min(240, name.length * 9 + 24));
|
|
28
30
|
}
|
|
29
31
|
|
|
32
|
+
// Shared offscreen canvas for measuring text width during auto-fit (no DOM reflow).
|
|
33
|
+
let measureCanvas: HTMLCanvasElement | null = null;
|
|
34
|
+
function measureCtx(): CanvasRenderingContext2D | null {
|
|
35
|
+
if (!measureCanvas) measureCanvas = document.createElement("canvas");
|
|
36
|
+
return measureCanvas.getContext("2d");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Drag handle pinned to a header cell's right edge. Resizes by mutating the
|
|
40
|
+
// column's CSS var on the scroll root each frame (no React churn across the
|
|
41
|
+
// virtualized rows), committing the final width on release. Double-click
|
|
42
|
+
// auto-fits the column to its loaded content.
|
|
43
|
+
const ColResizeHandle: React.FC<{
|
|
44
|
+
index: number;
|
|
45
|
+
startWidth: number;
|
|
46
|
+
scrollerRef: React.RefObject<HTMLDivElement | null>;
|
|
47
|
+
onCommit: (w: number) => void;
|
|
48
|
+
onAutoFit: () => void;
|
|
49
|
+
}> = ({ index, startWidth, scrollerRef, onCommit, onAutoFit }) => {
|
|
50
|
+
const onPointerDown = (e: React.PointerEvent) => {
|
|
51
|
+
e.preventDefault();
|
|
52
|
+
e.stopPropagation();
|
|
53
|
+
const root = scrollerRef.current;
|
|
54
|
+
if (!root) return;
|
|
55
|
+
const handle = e.currentTarget as HTMLElement;
|
|
56
|
+
handle.setPointerCapture(e.pointerId);
|
|
57
|
+
const startX = e.clientX;
|
|
58
|
+
let last = startWidth;
|
|
59
|
+
let moved = false;
|
|
60
|
+
let raf = 0;
|
|
61
|
+
let pending: number | null = null;
|
|
62
|
+
const flush = () => {
|
|
63
|
+
raf = 0;
|
|
64
|
+
if (pending == null) return;
|
|
65
|
+
last = pending;
|
|
66
|
+
root.style.setProperty(`--cw-${index}`, `${pending}px`);
|
|
67
|
+
pending = null;
|
|
68
|
+
};
|
|
69
|
+
const onMove = (ev: PointerEvent) => {
|
|
70
|
+
ev.preventDefault();
|
|
71
|
+
moved = true;
|
|
72
|
+
pending = Math.max(MIN_COL_W, Math.min(MAX_COL_W, startWidth + (ev.clientX - startX)));
|
|
73
|
+
if (!raf) raf = requestAnimationFrame(flush);
|
|
74
|
+
};
|
|
75
|
+
const onUp = () => {
|
|
76
|
+
if (raf) cancelAnimationFrame(raf);
|
|
77
|
+
flush();
|
|
78
|
+
if (moved) onCommit(last);
|
|
79
|
+
handle.removeEventListener("pointermove", onMove);
|
|
80
|
+
handle.removeEventListener("pointerup", onUp);
|
|
81
|
+
handle.removeEventListener("pointercancel", onUp);
|
|
82
|
+
try {
|
|
83
|
+
handle.releasePointerCapture(e.pointerId);
|
|
84
|
+
} catch {
|
|
85
|
+
/* already released */
|
|
86
|
+
}
|
|
87
|
+
document.body.style.userSelect = "";
|
|
88
|
+
document.body.style.cursor = "";
|
|
89
|
+
};
|
|
90
|
+
document.body.style.userSelect = "none";
|
|
91
|
+
document.body.style.cursor = "ew-resize";
|
|
92
|
+
handle.addEventListener("pointermove", onMove);
|
|
93
|
+
handle.addEventListener("pointerup", onUp);
|
|
94
|
+
handle.addEventListener("pointercancel", onUp);
|
|
95
|
+
};
|
|
96
|
+
return (
|
|
97
|
+
<div
|
|
98
|
+
onPointerDown={onPointerDown}
|
|
99
|
+
onDoubleClick={(e) => {
|
|
100
|
+
e.preventDefault();
|
|
101
|
+
e.stopPropagation();
|
|
102
|
+
onAutoFit();
|
|
103
|
+
}}
|
|
104
|
+
title="Drag to resize · double-click to fit"
|
|
105
|
+
style={{ touchAction: "none" }}
|
|
106
|
+
className="absolute right-0 top-0 bottom-0 z-20 w-1.5 cursor-ew-resize hover:bg-accent/40"
|
|
107
|
+
/>
|
|
108
|
+
);
|
|
109
|
+
};
|
|
110
|
+
|
|
30
111
|
const DataTable: React.FC<DataTableProps> = ({ cart, entity, searchFields, onSelectRow }) => {
|
|
31
112
|
const stream = useBulkSubscription(cart, entity);
|
|
32
113
|
const scrollerRef = React.useRef<HTMLDivElement | null>(null);
|
|
@@ -37,12 +118,24 @@ const DataTable: React.FC<DataTableProps> = ({ cart, entity, searchFields, onSel
|
|
|
37
118
|
const [popover, setPopover] = React.useState<ValuePopoverData | null>(null);
|
|
38
119
|
// Bump on every chunk arrival without re-rendering each FRAME_DATA.
|
|
39
120
|
const [tick, setTick] = React.useState(0);
|
|
121
|
+
// Per-column width overrides (px), keyed by field name. Seeded from
|
|
122
|
+
// localStorage; remounts on cart/entity change (keyed at the call site).
|
|
123
|
+
const [overrides, setOverrides] = React.useState<Record<string, number>>(
|
|
124
|
+
() => loadColWidths(cart, entity),
|
|
125
|
+
);
|
|
126
|
+
const widthFor = (name: string) => overrides[name] ?? defaultColWidth(name);
|
|
40
127
|
|
|
41
128
|
React.useEffect(() => {
|
|
42
129
|
stream.setOnRender(() => setTick((n) => n + 1));
|
|
43
130
|
return () => stream.setOnRender(null);
|
|
44
131
|
}, [stream]);
|
|
45
132
|
|
|
133
|
+
// Re-run once the scroller actually mounts. On first load columns are empty,
|
|
134
|
+
// so the "Waiting for schema…" placeholder renders instead of the scroller —
|
|
135
|
+
// scrollerRef is null and the observer never attaches, leaving viewportH at 0
|
|
136
|
+
// (only OVERSCAN rows windowed). Keying on columns-present re-attaches the
|
|
137
|
+
// moment the schema arrives and the scroller appears.
|
|
138
|
+
const hasScroller = stream.columns.length > 0;
|
|
46
139
|
React.useEffect(() => {
|
|
47
140
|
const el = scrollerRef.current;
|
|
48
141
|
if (!el) return;
|
|
@@ -50,7 +143,7 @@ const DataTable: React.FC<DataTableProps> = ({ cart, entity, searchFields, onSel
|
|
|
50
143
|
ro.observe(el);
|
|
51
144
|
setViewportH(el.clientHeight);
|
|
52
145
|
return () => ro.disconnect();
|
|
53
|
-
}, []);
|
|
146
|
+
}, [hasScroller]);
|
|
54
147
|
|
|
55
148
|
const rows = stream.store.rows;
|
|
56
149
|
const columns = stream.columns;
|
|
@@ -78,9 +171,41 @@ const DataTable: React.FC<DataTableProps> = ({ cart, entity, searchFields, onSel
|
|
|
78
171
|
};
|
|
79
172
|
|
|
80
173
|
const searchDisabled = searchFields.length === 0;
|
|
81
|
-
const totalColW = GUTTER_W + columns.reduce((s, c) => s + colWidth(c.name), 0);
|
|
82
174
|
const idColIndex = columns.findIndex((c) => c.name === "id");
|
|
83
175
|
|
|
176
|
+
// Drive every width off CSS vars on the scroll root so a drag mutates one
|
|
177
|
+
// property and resizes the whole virtualized column with no React re-render.
|
|
178
|
+
const widthVars = React.useMemo(() => {
|
|
179
|
+
const style: React.CSSProperties & Record<string, string> = { ["--gw"]: `${GUTTER_W}px` };
|
|
180
|
+
const parts = ["var(--gw)"];
|
|
181
|
+
columns.forEach((c, i) => {
|
|
182
|
+
style[`--cw-${i}`] = `${widthFor(c.name)}px`;
|
|
183
|
+
parts.push(`var(--cw-${i})`);
|
|
184
|
+
});
|
|
185
|
+
style["--total-w"] = `calc(${parts.join(" + ")})`;
|
|
186
|
+
return style;
|
|
187
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
188
|
+
}, [columns, overrides]);
|
|
189
|
+
|
|
190
|
+
const commitWidth = (name: string, w: number) => {
|
|
191
|
+
setOverrides((o) => ({ ...o, [name]: w }));
|
|
192
|
+
saveColWidth(cart, entity, name, w);
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
const autoFit = (colIndex: number, name: string) => {
|
|
196
|
+
const ctx = measureCtx();
|
|
197
|
+
const root = scrollerRef.current;
|
|
198
|
+
if (!ctx || !root) return;
|
|
199
|
+
ctx.font = `12px ${getComputedStyle(root).fontFamily}`;
|
|
200
|
+
let w = ctx.measureText(name).width;
|
|
201
|
+
const cap = Math.min(rows.length, 1000);
|
|
202
|
+
for (let r = 0; r < cap; r++) w = Math.max(w, ctx.measureText(rows[r]?.[colIndex] ?? "").width);
|
|
203
|
+
// px-2 padding (16) + border slack (2).
|
|
204
|
+
const fitted = Math.max(MIN_COL_W, Math.min(MAX_COL_W, Math.ceil(w) + 18));
|
|
205
|
+
root.style.setProperty(`--cw-${colIndex}`, `${fitted}px`);
|
|
206
|
+
commitWidth(name, fitted);
|
|
207
|
+
};
|
|
208
|
+
|
|
84
209
|
const scrollRowIntoView = (r: number) => {
|
|
85
210
|
const el = scrollerRef.current;
|
|
86
211
|
if (!el) return;
|
|
@@ -137,8 +262,8 @@ const DataTable: React.FC<DataTableProps> = ({ cart, entity, searchFields, onSel
|
|
|
137
262
|
<div className="shrink-0 text-[11px] text-muted-foreground tabular-nums">
|
|
138
263
|
{stream.error ? (
|
|
139
264
|
<span className="text-red-400">{stream.error}</span>
|
|
140
|
-
) :
|
|
141
|
-
<span>connecting
|
|
265
|
+
) : columns.length === 0 ? (
|
|
266
|
+
<span>{stream.connected ? "loading…" : "connecting…"}</span>
|
|
142
267
|
) : (
|
|
143
268
|
<span>
|
|
144
269
|
{stream.loadedRows.toLocaleString()} / {stream.totalRows.toLocaleString()} rows
|
|
@@ -163,29 +288,36 @@ const DataTable: React.FC<DataTableProps> = ({ cart, entity, searchFields, onSel
|
|
|
163
288
|
}}
|
|
164
289
|
className="group/table relative min-h-0 flex-1 overflow-auto font-mono outline-none"
|
|
165
290
|
>
|
|
166
|
-
<div style={{ width:
|
|
291
|
+
<div style={{ ...widthVars, width: "var(--total-w)" }}>
|
|
167
292
|
{/* Sticky header lives inside the scroller so it shares the native
|
|
168
293
|
horizontal scroll with the rows — no JS transform sync, no jitter. */}
|
|
169
294
|
<div
|
|
170
|
-
className="sticky top-0 z-10 flex h-6 select-none border-b border-border bg-background text-[11px]
|
|
171
|
-
style={{ width:
|
|
295
|
+
className="sticky top-0 z-10 flex h-6 select-none border-b border-border bg-background text-[11px] text-muted-foreground"
|
|
296
|
+
style={{ width: "var(--total-w)" }}
|
|
172
297
|
>
|
|
173
298
|
<div
|
|
174
|
-
style={{ width:
|
|
299
|
+
style={{ width: "var(--gw)" }}
|
|
175
300
|
className="shrink-0 border-r border-border"
|
|
176
301
|
/>
|
|
177
|
-
{columns.map((c) => (
|
|
302
|
+
{columns.map((c, ci) => (
|
|
178
303
|
<div
|
|
179
304
|
key={c.name}
|
|
180
|
-
style={{ width:
|
|
181
|
-
className="flex items-center overflow-hidden border-r border-border px-2"
|
|
305
|
+
style={{ width: `var(--cw-${ci})` }}
|
|
306
|
+
className="relative flex items-center overflow-hidden border-r border-border px-2"
|
|
182
307
|
>
|
|
183
308
|
<span className="truncate">{c.name}</span>
|
|
309
|
+
<ColResizeHandle
|
|
310
|
+
index={ci}
|
|
311
|
+
startWidth={widthFor(c.name)}
|
|
312
|
+
scrollerRef={scrollerRef}
|
|
313
|
+
onCommit={(w) => commitWidth(c.name, w)}
|
|
314
|
+
onAutoFit={() => autoFit(ci, c.name)}
|
|
315
|
+
/>
|
|
184
316
|
</div>
|
|
185
317
|
))}
|
|
186
318
|
</div>
|
|
187
319
|
|
|
188
|
-
<div style={{ height: totalH, width:
|
|
320
|
+
<div style={{ height: totalH, width: "var(--total-w)", position: "relative" }}>
|
|
189
321
|
{rows.slice(start, end).map((row, i) => {
|
|
190
322
|
const rowIdx = start + i;
|
|
191
323
|
return (
|
|
@@ -194,7 +326,7 @@ const DataTable: React.FC<DataTableProps> = ({ cart, entity, searchFields, onSel
|
|
|
194
326
|
style={{
|
|
195
327
|
top: rowIdx * ROW_H,
|
|
196
328
|
height: ROW_H,
|
|
197
|
-
width:
|
|
329
|
+
width: "var(--total-w)",
|
|
198
330
|
}}
|
|
199
331
|
className={cn(
|
|
200
332
|
"absolute left-0 flex text-[12px] leading-none",
|
|
@@ -205,7 +337,7 @@ const DataTable: React.FC<DataTableProps> = ({ cart, entity, searchFields, onSel
|
|
|
205
337
|
type="button"
|
|
206
338
|
title="Open row detail"
|
|
207
339
|
onClick={() => handleRowClick(rowIdx)}
|
|
208
|
-
style={{ width:
|
|
340
|
+
style={{ width: "var(--gw)" }}
|
|
209
341
|
className={cn(
|
|
210
342
|
"flex shrink-0 items-center justify-center border-r border-border/60",
|
|
211
343
|
"cursor-pointer text-[10px] tabular-nums text-muted-foreground/60",
|
|
@@ -245,7 +377,7 @@ const DataTable: React.FC<DataTableProps> = ({ cart, entity, searchFields, onSel
|
|
|
245
377
|
editable,
|
|
246
378
|
});
|
|
247
379
|
}}
|
|
248
|
-
style={{ width:
|
|
380
|
+
style={{ width: `var(--cw-${ci})` }}
|
|
249
381
|
className={cn(
|
|
250
382
|
"flex cursor-pointer items-center overflow-hidden border-r border-border/60 px-2",
|
|
251
383
|
"data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground",
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/editor/nbt-editor.tsx", "../src/editor/nbt-language.ts", "../src/editor/lsp-extensions.ts", "../src/editor/lsp-client.ts"],
|
|
4
|
-
"sourcesContent": ["// CodeMirror 6 React wrapper for one NBT buffer. Recreated per uri (keyed by\n// the host); owns the EditorView lifecycle, Ctrl+S save, and the LSP wiring.\n\nimport React from \"react\";\nimport { EditorState } from \"@codemirror/state\";\nimport {\n EditorView,\n keymap,\n lineNumbers,\n drawSelection,\n highlightActiveLine,\n} from \"@codemirror/view\";\nimport {\n defaultKeymap,\n history,\n historyKeymap,\n indentWithTab,\n} from \"@codemirror/commands\";\nimport {\n syntaxHighlighting,\n defaultHighlightStyle,\n bracketMatching,\n} from \"@codemirror/language\";\nimport { lintGutter } from \"@codemirror/lint\";\nimport { searchKeymap } from \"@codemirror/search\";\nimport { completionKeymap, closeBrackets } from \"@codemirror/autocomplete\";\nimport { nbtLanguageSupport } from \"./nbt-language\";\nimport { lspExtensions, type GotoDefHandler } from \"./lsp-extensions\";\nimport { NbtLspClient } from \"./lsp-client\";\n\n// Match the devtools panel chrome via its CSS variables (we render inside the\n// .nimbit-devtools subtree, so these resolve to the scoped theme).\nconst nbtTheme = EditorView.theme(\n {\n \"&\": {\n height: \"100%\",\n fontSize: \"12px\",\n backgroundColor: \"var(--background)\",\n color: \"var(--foreground)\",\n },\n \".cm-content\": { fontFamily: \"ui-monospace, monospace\" },\n \".cm-gutters\": {\n backgroundColor: \"var(--background)\",\n color: \"var(--muted-foreground)\",\n borderRight: \"1px solid var(--border)\",\n },\n \".cm-activeLine\": { backgroundColor: \"color-mix(in srgb, var(--accent) 35%, transparent)\" },\n \"&.cm-focused\": { outline: \"none\" },\n \".cm-tooltip\": {\n backgroundColor: \"var(--popover)\",\n color: \"var(--popover-foreground)\",\n border: \"1px solid var(--border)\",\n },\n },\n { dark: true },\n);\n\nexport type NbtEditorProps = {\n uri: string;\n value: string;\n readOnly?: boolean;\n lsp?: NbtLspClient | null;\n onChange?: (text: string) => void;\n onSave?: (text: string) => void;\n onGotoDefinition?: GotoDefHandler;\n};\n\nexport const NbtEditor: React.FC<NbtEditorProps> = ({\n uri,\n value,\n readOnly = false,\n lsp,\n onChange,\n onSave,\n onGotoDefinition,\n}) => {\n const hostRef = React.useRef<HTMLDivElement | null>(null);\n const viewRef = React.useRef<EditorView | null>(null);\n\n // Latest callbacks without recreating the view.\n const onChangeRef = React.useRef(onChange);\n onChangeRef.current = onChange;\n const onSaveRef = React.useRef(onSave);\n onSaveRef.current = onSave;\n const onGotoRef = React.useRef(onGotoDefinition);\n onGotoRef.current = onGotoDefinition;\n\n React.useEffect(() => {\n const host = hostRef.current;\n if (!host) return;\n\n const extensions = [\n lineNumbers(),\n history(),\n drawSelection(),\n highlightActiveLine(),\n bracketMatching(),\n closeBrackets(),\n lintGutter(),\n syntaxHighlighting(defaultHighlightStyle, { fallback: true }),\n nbtLanguageSupport(),\n nbtTheme,\n keymap.of([\n {\n key: \"Mod-s\",\n run: (view) => {\n onSaveRef.current?.(view.state.doc.toString());\n return true;\n },\n },\n ...defaultKeymap,\n ...historyKeymap,\n ...searchKeymap,\n ...completionKeymap,\n indentWithTab,\n ]),\n EditorView.updateListener.of((u) => {\n if (u.docChanged) onChangeRef.current?.(u.state.doc.toString());\n }),\n EditorState.readOnly.of(readOnly),\n ];\n if (lsp && !readOnly) {\n extensions.push(\n lspExtensions(lsp, uri, (defUri, line) => onGotoRef.current?.(defUri, line)),\n );\n }\n\n const view = new EditorView({\n state: EditorState.create({ doc: value, extensions }),\n parent: host,\n });\n viewRef.current = view;\n return () => {\n view.destroy();\n viewRef.current = null;\n };\n // Recreate per buffer identity / mode; `value` is only the initial doc.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [uri, readOnly, lsp]);\n\n return <div ref={hostRef} className=\"h-full min-h-0 overflow-hidden\" />;\n};\n", "// NBT syntax highlighting for CodeMirror 6 \u2014 a StreamLanguage tokenizer ported\n// from the console tokenizer (modules/lang_common/scan.jai) and the VSCode\n// grammar (editors/vscode/nbt/syntaxes/nbt.tmLanguage.json). Highlighting is\n// local; everything semantic (diagnostics/completion/hover) comes via the LSP.\n\nimport {\n StreamLanguage,\n LanguageSupport,\n indentUnit,\n type StreamParser,\n} from \"@codemirror/language\";\n\nconst KEYWORDS = new Set([\n \"entity\", \"enum\", \"struct\", \"const\", \"export\", \"extends\",\n \"fn\", \"on\", \"action\", \"activity\", \"task\", \"variant\", \"workflow\", \"command\",\n \"middleware\", \"schedule\", \"every\", \"jai\",\n \"migration\", \"test\", \"mock\", \"assert\",\n \"cartridge\", \"service\", \"component\", \"app\", \"route\", \"layout\",\n \"import\", \"from\",\n \"if\", \"elif\", \"else\", \"while\", \"for\", \"in\", \"return\", \"break\", \"continue\",\n \"delete\", \"defer\", \"print\", \"sleep\", \"fail\",\n]);\n\nconst TYPES = new Set([\n \"string\", \"bool\", \"ulid\", \"document\", \"dict\", \"blob\", \"DateTime\",\n \"u8\", \"u16\", \"u32\", \"u64\", \"s8\", \"s16\", \"s32\", \"s64\",\n \"int\", \"integer\", \"float\", \"float64\", \"f32\", \"f64\", \"double\",\n \"time\", \"bytes\", \"any\", \"name\", \"ref\",\n]);\n\ntype NbtState = {\n tripleString: boolean;\n};\n\n// NBT strings carry NO escapes (scan.jai consumes to the bare closing quote),\n// so a backslash before a quote does not extend the string.\nfunction eatSingleLineString(stream: StringStream, quote: string): string {\n while (!stream.eol()) {\n if (stream.next() === quote) break;\n }\n return \"string\";\n}\n\ntype StringStream = Parameters<StreamParser<NbtState>[\"token\"]>[0];\n\nconst nbtParser: StreamParser<NbtState> = {\n name: \"nbt\",\n startState: () => ({ tripleString: false }),\n token(stream, state) {\n if (state.tripleString) {\n while (!stream.eol()) {\n if (stream.match('\"\"\"')) {\n state.tripleString = false;\n return \"string\";\n }\n stream.next();\n }\n return \"string\";\n }\n\n if (stream.eatSpace()) return null;\n\n const ch = stream.peek();\n if (ch == null) return null;\n\n if (ch === \"#\") {\n stream.skipToEnd();\n return \"comment\";\n }\n\n if (stream.match('\"\"\"')) {\n state.tripleString = true;\n // Rest of line is string body unless it closes on the same line.\n while (!stream.eol()) {\n if (stream.match('\"\"\"')) {\n state.tripleString = false;\n return \"string\";\n }\n stream.next();\n }\n return \"string\";\n }\n\n if (ch === '\"') {\n stream.next();\n return eatSingleLineString(stream, '\"');\n }\n if (ch === \"'\") {\n // Like the scanner: only a string opener when not glued to an identifier\n // (lets contractions inside docs pass).\n const before = stream.string.charAt(stream.pos - 1);\n if (!/[A-Za-z0-9_]/.test(before)) {\n stream.next();\n return eatSingleLineString(stream, \"'\");\n }\n stream.next();\n return null;\n }\n\n // f-strings: f\"...\" \u2014 body highlighted as a string (interpolation holes\n // are cosmetic only).\n if (ch === \"f\" && stream.string.charAt(stream.pos + 1) === '\"') {\n stream.next();\n stream.next();\n return eatSingleLineString(stream, '\"');\n }\n\n // @attr / @@attr\n if (ch === \"@\") {\n stream.next();\n stream.eat(\"@\");\n stream.eatWhile(/[A-Za-z0-9_]/);\n return \"meta\";\n }\n\n // Numbers. The optional fraction backtracks on `0..9` ranges (matches \"0\",\n // not \"0.\").\n if (/\\d/.test(ch)) {\n stream.match(/^\\d+(\\.\\d+)?/);\n return \"number\";\n }\n\n if (/[A-Za-z_]/.test(ch)) {\n stream.eatWhile(/[A-Za-z0-9_]/);\n const word = stream.current();\n if (word === \"true\" || word === \"false\") return \"atom\";\n if (KEYWORDS.has(word)) return \"keyword\";\n if (TYPES.has(word)) return \"typeName\";\n if (/^[A-Z]/.test(word)) return \"typeName\"; // entity/enum references\n return \"variableName\";\n }\n\n if (/[+\\-*/=<>!&|?.,:;]/.test(ch)) {\n stream.next();\n stream.eatWhile(/[+\\-*/=<>!&|.]/);\n return \"operator\";\n }\n\n stream.next();\n return null;\n },\n languageData: {\n commentTokens: { line: \"#\" },\n },\n};\n\nexport const nbtLanguage = StreamLanguage.define(nbtParser);\n\nexport function nbtLanguageSupport(): LanguageSupport {\n return new LanguageSupport(nbtLanguage, [indentUnit.of(\" \")]);\n}\n", "// CodeMirror extensions wiring an editor buffer to the NbtLspClient:\n// full-text didChange sync (debounced), pushed publishDiagnostics \u2192 lint,\n// completion, hover, and F12 go-to-definition.\n\nimport { type Extension, type Text } from \"@codemirror/state\";\nimport {\n EditorView,\n ViewPlugin,\n type ViewUpdate,\n hoverTooltip,\n keymap,\n} from \"@codemirror/view\";\nimport {\n autocompletion,\n type CompletionContext,\n type CompletionResult,\n} from \"@codemirror/autocomplete\";\nimport { setDiagnostics, type Diagnostic } from \"@codemirror/lint\";\nimport {\n NbtLspClient,\n type LspDiagnostic,\n type LspPosition,\n type LspCompletionItem,\n type LspLocation,\n} from \"./lsp-client\";\n\nfunction toLspPos(doc: Text, offset: number): LspPosition {\n const line = doc.lineAt(offset);\n return { line: line.number - 1, character: offset - line.from };\n}\n\nfunction fromLspPos(doc: Text, pos: LspPosition): number {\n const lineNo = Math.min(Math.max(pos.line + 1, 1), doc.lines);\n const line = doc.line(lineNo);\n return Math.min(line.from + Math.max(pos.character, 0), line.to);\n}\n\nfunction toCmDiagnostics(doc: Text, diags: LspDiagnostic[]): Diagnostic[] {\n return diags.map((d) => {\n const from = fromLspPos(doc, d.range.start);\n let to = fromLspPos(doc, d.range.end);\n if (to <= from) to = Math.min(from + 1, doc.length);\n const severity =\n d.severity === 2 ? \"warning\" : d.severity === 3 || d.severity === 4 ? \"info\" : \"error\";\n return { from, to, severity, message: d.message, source: d.source ?? \"nbt\" };\n });\n}\n\n// LSP CompletionItemKind \u2192 CM completion type (drives the icon).\nfunction completionType(kind?: number): string {\n switch (kind) {\n case 3: return \"function\";\n case 5: return \"property\";\n case 13: return \"enum\";\n case 14: return \"keyword\";\n case 22: return \"class\";\n default: return \"variable\";\n }\n}\n\nexport type GotoDefHandler = (uri: string, line: number) => void;\n\nexport function lspExtensions(\n client: NbtLspClient,\n uri: string,\n onGotoDefinition?: GotoDefHandler,\n): Extension[] {\n // Sync + diagnostics plugin. didOpen on create, debounced didChange,\n // diagnostics pushed by the server are dispatched into the lint state.\n const syncPlugin = ViewPlugin.fromClass(\n class {\n private timer: ReturnType<typeof setTimeout> | null = null;\n private unsubscribe: () => void;\n\n constructor(readonly view: EditorView) {\n client.didOpen(uri, view.state.doc.toString());\n this.unsubscribe = client.onDiagnostics(uri, (diags) => {\n const cm = toCmDiagnostics(this.view.state.doc, diags);\n this.view.dispatch(setDiagnostics(this.view.state, cm));\n });\n }\n\n update(u: ViewUpdate) {\n if (!u.docChanged) return;\n if (this.timer) clearTimeout(this.timer);\n this.timer = setTimeout(() => {\n this.timer = null;\n client.didChange(uri, this.view.state.doc.toString());\n }, 200);\n }\n\n destroy() {\n if (this.timer) {\n clearTimeout(this.timer);\n client.didChange(uri, this.view.state.doc.toString());\n }\n this.unsubscribe();\n }\n },\n );\n\n const completionSource = async (\n ctx: CompletionContext,\n ): Promise<CompletionResult | null> => {\n const word = ctx.matchBefore(/\\w*/);\n if (!word) return null;\n if (word.from === word.to && !ctx.explicit) {\n // Only fire automatically right after a trigger character.\n const before = ctx.state.doc.sliceString(Math.max(0, ctx.pos - 1), ctx.pos);\n if (before !== \".\" && before !== \":\" && before !== '\"') return null;\n }\n // Flush any pending edit so the server completes against the live buffer.\n client.didChange(uri, ctx.state.doc.toString());\n let result;\n try {\n result = await client.completion(uri, toLspPos(ctx.state.doc, ctx.pos));\n } catch {\n return null;\n }\n const items: LspCompletionItem[] = Array.isArray(result)\n ? result\n : (result?.items ?? []);\n if (items.length === 0) return null;\n return {\n from: word.from,\n options: items.map((it) => ({\n label: it.label,\n type: completionType(it.kind),\n detail: it.detail,\n })),\n };\n };\n\n const hover = hoverTooltip(async (view, pos) => {\n let result;\n try {\n result = await client.hover(uri, toLspPos(view.state.doc, pos));\n } catch {\n return null;\n }\n const value = result?.contents?.value;\n if (!value) return null;\n return {\n pos,\n create: () => {\n const dom = document.createElement(\"pre\");\n dom.className = \"nbt-hover-tooltip\";\n dom.style.cssText =\n \"margin:0;padding:6px 8px;max-width:480px;white-space:pre-wrap;font-size:11px;\";\n // Server emits fenced markdown code blocks; strip the fences, show text.\n dom.textContent = value.replace(/```\\w*\\n?/g, \"\");\n return { dom };\n },\n };\n });\n\n const gotoDef = keymap.of([\n {\n key: \"F12\",\n run: (view) => {\n const pos = toLspPos(view.state.doc, view.state.selection.main.head);\n client\n .definition(uri, pos)\n .then((result) => {\n const loc: LspLocation | null = Array.isArray(result)\n ? (result[0] ?? null)\n : result;\n if (!loc?.uri) return;\n if (loc.uri === uri) {\n const off = fromLspPos(view.state.doc, loc.range.start);\n view.dispatch({\n selection: { anchor: off },\n scrollIntoView: true,\n });\n } else {\n onGotoDefinition?.(loc.uri, loc.range.start.line);\n }\n })\n .catch(() => {});\n return true;\n },\n },\n ]);\n\n return [\n syncPlugin,\n autocompletion({ override: [completionSource] }),\n hover,\n gotoDef,\n ];\n}\n", "// Minimal LSP client over WebSocket for the console's dev-mode nbt_lsp bridge\n// (/_console/dev/lsp). One JSON-RPC message per WS text frame \u2014 no\n// Content-Length framing (the bridge's write_proc emits bare JSON).\n//\n// Hand-rolled on purpose: the server speaks ~10 methods with full-text sync,\n// and a library client (codemirror-languageserver) would drag transitive\n// runtime deps into every host app.\n\nexport type LspPosition = { line: number; character: number };\nexport type LspRange = { start: LspPosition; end: LspPosition };\nexport type LspDiagnostic = {\n range: LspRange;\n severity?: number; // 1=error 2=warning 3=info 4=hint\n message: string;\n source?: string;\n};\nexport type LspCompletionItem = {\n label: string;\n kind?: number;\n detail?: string;\n};\nexport type LspLocation = { uri: string; range: LspRange };\n\ntype Pending = {\n resolve: (v: unknown) => void;\n reject: (e: Error) => void;\n};\n\nexport class NbtLspClient {\n private url: string;\n private rootUri: string | null = null;\n private ws: WebSocket | null = null;\n private nextId = 1;\n private pending = new Map<number, Pending>();\n private diagHandlers = new Map<string, (d: LspDiagnostic[]) => void>();\n // Open buffers, kept for replay across reconnects.\n private docs = new Map<string, { text: string; version: number }>();\n private ready = false;\n private queue: string[] = [];\n private closed = false;\n private retryMs = 500;\n\n constructor(url: string) {\n this.url = url;\n this.connect();\n }\n\n dispose() {\n this.closed = true;\n this.ws?.close();\n this.pending.forEach((p) => p.reject(new Error(\"lsp client disposed\")));\n this.pending.clear();\n }\n\n private connect() {\n if (this.closed) return;\n const ws = new WebSocket(this.url);\n this.ws = ws;\n ws.onopen = () => {\n this.retryMs = 500;\n if (this.rootUri) this.sendInitialize(this.rootUri);\n };\n ws.onmessage = (ev) => {\n if (typeof ev.data !== \"string\") return;\n this.onMessage(ev.data);\n };\n ws.onclose = () => {\n this.ready = false;\n this.ws = null;\n for (const p of this.pending.values())\n p.reject(new Error(\"lsp connection closed\"));\n this.pending.clear();\n if (!this.closed) {\n setTimeout(() => this.connect(), this.retryMs);\n this.retryMs = Math.min(this.retryMs * 2, 10_000);\n }\n };\n }\n\n // Called once by the host with file://<projectRoot>; also re-sent after every\n // reconnect, followed by didOpen replays for tracked buffers.\n initialize(rootUri: string) {\n this.rootUri = rootUri;\n if (this.ws?.readyState === WebSocket.OPEN) this.sendInitialize(rootUri);\n }\n\n private sendInitialize(rootUri: string) {\n const id = this.nextId++;\n this.pending.set(id, {\n resolve: () => {\n this.sendRaw({ jsonrpc: \"2.0\", method: \"initialized\", params: {} });\n this.ready = true;\n for (const [uri, d] of this.docs) {\n this.sendRaw({\n jsonrpc: \"2.0\",\n method: \"textDocument/didOpen\",\n params: {\n textDocument: {\n uri,\n languageId: \"nbt\",\n version: d.version,\n text: d.text,\n },\n },\n });\n }\n const q = this.queue;\n this.queue = [];\n for (const m of q) this.ws?.send(m);\n },\n reject: () => {},\n });\n this.ws?.send(\n JSON.stringify({\n jsonrpc: \"2.0\",\n id,\n method: \"initialize\",\n params: { rootUri },\n }),\n );\n }\n\n private sendRaw(msg: object) {\n const s = JSON.stringify(msg);\n if (this.ready && this.ws?.readyState === WebSocket.OPEN) this.ws.send(s);\n else this.queue.push(s);\n }\n\n private onMessage(data: string) {\n let msg: any;\n try {\n msg = JSON.parse(data);\n } catch {\n return;\n }\n if (msg.id != null && (msg.result !== undefined || msg.error)) {\n const p = this.pending.get(msg.id);\n if (!p) return;\n this.pending.delete(msg.id);\n if (msg.error) p.reject(new Error(msg.error.message ?? \"lsp error\"));\n else p.resolve(msg.result);\n return;\n }\n if (msg.method === \"textDocument/publishDiagnostics\") {\n const uri = msg.params?.uri as string;\n const handler = this.diagHandlers.get(uri);\n if (handler) handler((msg.params?.diagnostics ?? []) as LspDiagnostic[]);\n }\n }\n\n request<T>(method: string, params: object): Promise<T> {\n const id = this.nextId++;\n const p = new Promise<T>((resolve, reject) => {\n this.pending.set(id, { resolve: resolve as Pending[\"resolve\"], reject });\n });\n this.sendRaw({ jsonrpc: \"2.0\", id, method, params });\n return p;\n }\n\n onDiagnostics(uri: string, handler: (d: LspDiagnostic[]) => void): () => void {\n this.diagHandlers.set(uri, handler);\n return () => {\n if (this.diagHandlers.get(uri) === handler) this.diagHandlers.delete(uri);\n };\n }\n\n didOpen(uri: string, text: string) {\n const existing = this.docs.get(uri);\n if (existing) {\n // Re-opening an already-tracked buffer (tab switch) \u2014 just resync.\n this.didChange(uri, text);\n return;\n }\n this.docs.set(uri, { text, version: 1 });\n this.sendRaw({\n jsonrpc: \"2.0\",\n method: \"textDocument/didOpen\",\n params: { textDocument: { uri, languageId: \"nbt\", version: 1, text } },\n });\n }\n\n didChange(uri: string, text: string) {\n const d = this.docs.get(uri);\n if (!d) return this.didOpen(uri, text);\n if (d.text === text) return;\n d.text = text;\n d.version += 1;\n this.sendRaw({\n jsonrpc: \"2.0\",\n method: \"textDocument/didChange\",\n params: {\n textDocument: { uri, version: d.version },\n contentChanges: [{ text }],\n },\n });\n }\n\n didClose(uri: string) {\n if (!this.docs.delete(uri)) return;\n this.sendRaw({\n jsonrpc: \"2.0\",\n method: \"textDocument/didClose\",\n params: { textDocument: { uri } },\n });\n }\n\n completion(uri: string, pos: LspPosition): Promise<{ items: LspCompletionItem[] } | LspCompletionItem[] | null> {\n return this.request(\"textDocument/completion\", {\n textDocument: { uri },\n position: pos,\n });\n }\n\n hover(uri: string, pos: LspPosition): Promise<{ contents?: { value?: string } } | null> {\n return this.request(\"textDocument/hover\", {\n textDocument: { uri },\n position: pos,\n });\n }\n\n definition(uri: string, pos: LspPosition): Promise<LspLocation | LspLocation[] | null> {\n return this.request(\"textDocument/definition\", {\n textDocument: { uri },\n position: pos,\n });\n }\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["EditorView", "keymap", "EditorView", "keymap", "view"]
|
|
7
|
-
}
|