@yuku123/z-graph-component 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/pages.js ADDED
@@ -0,0 +1,2176 @@
1
+ import { j as e, a as W, s as Y, f as ae, G as be, I as Ne } from "./GraphApp-C6N6khIq.js";
2
+ import { c as ft } from "./GraphApp-C6N6khIq.js";
3
+ import { UserOutlined as Se, LockOutlined as ve, HomeOutlined as Te, DashboardOutlined as Ee, PartitionOutlined as Ce, BranchesOutlined as Ae, HistoryOutlined as we, CodeOutlined as ke, DatabaseOutlined as Ie, ProfileOutlined as Le, FileTextOutlined as Re } from "@ant-design/icons";
4
+ import { useState as N, useEffect as $, useRef as te, useCallback as K, useMemo as X } from "react";
5
+ import { Typography as Oe, Card as re, Space as oe, Tag as Me, Row as De, Col as He, Form as Z, Input as le, Button as Pe, message as ie } from "antd";
6
+ import { useNavigate as ze } from "react-router-dom";
7
+ function We(s) {
8
+ const [a, o] = N({
9
+ status: "unknown",
10
+ head: null,
11
+ nodeCount: 0,
12
+ edgeCount: 0,
13
+ error: null
14
+ }), [r, d] = N(null);
15
+ return $(() => {
16
+ let p = !1;
17
+ async function S() {
18
+ try {
19
+ const [x, l] = await Promise.all([
20
+ W.health(),
21
+ W.metrics().catch(() => null)
22
+ ]);
23
+ if (p) return;
24
+ o({
25
+ status: "up",
26
+ head: x.head,
27
+ nodeCount: x.nodeCount || 0,
28
+ edgeCount: x.edgeCount || 0,
29
+ error: null
30
+ }), d(l || null);
31
+ } catch (x) {
32
+ if (p) return;
33
+ o({ status: "down", head: null, nodeCount: 0, edgeCount: 0, error: x.message }), d(null);
34
+ }
35
+ }
36
+ S();
37
+ const f = setInterval(S, 5e3);
38
+ return () => {
39
+ p = !0, clearInterval(f);
40
+ };
41
+ }, [s]), { server: a, metrics: r };
42
+ }
43
+ function F(s) {
44
+ return function() {
45
+ const { server: o, metrics: r } = We();
46
+ return /* @__PURE__ */ e.jsx(s, { server: o, metrics: r });
47
+ };
48
+ }
49
+ function Ge({ server: s }) {
50
+ const [a, o] = N([]), [r, d] = N([]), [p, S] = N(null), [f, x] = N(!0), [l, C] = N(null), [j, v] = N([]), A = te(null);
51
+ $(() => {
52
+ let t = !1;
53
+ async function m() {
54
+ try {
55
+ x(!0);
56
+ const [i, c, b] = await Promise.all([
57
+ W.branches().catch(() => []),
58
+ W.commits().catch(() => []),
59
+ W.schema().catch(() => null)
60
+ ]);
61
+ o(Array.isArray(i) ? i : []), d(Array.isArray(c) ? c : []), S(b), C(null);
62
+ } catch (i) {
63
+ C(i.message);
64
+ } finally {
65
+ x(!1);
66
+ }
67
+ }
68
+ m();
69
+ }, [s.head]), $(() => {
70
+ let t = !1;
71
+ async function m() {
72
+ try {
73
+ const c = await W.metrics().catch(() => null);
74
+ if (t || !c) return;
75
+ const b = Date.now();
76
+ let E = 0;
77
+ if (A.current) {
78
+ const T = (b - A.current.ts) / 1e3;
79
+ T > 0 && (E = Math.round((c.totalRequests - A.current.total) / T * 10) / 10);
80
+ }
81
+ A.current = { ts: b, total: c.totalRequests }, v((T) => {
82
+ var y;
83
+ return [...T, { ts: b, rps: E, requests: c.totalRequests, errors: c.errorResponses, memory: ((y = c.jvmMemory) == null ? void 0 : y.usedBytes) || 0 }].slice(-60);
84
+ });
85
+ } catch {
86
+ }
87
+ }
88
+ m();
89
+ const i = setInterval(m, 5e3);
90
+ return () => {
91
+ t = !0, clearInterval(i);
92
+ };
93
+ }, []);
94
+ const k = r.slice(-5).reverse(), g = (p == null ? void 0 : p.tags) || [], h = (p == null ? void 0 : p.edges) || [], n = (p == null ? void 0 : p.indexes) || [];
95
+ return /* @__PURE__ */ e.jsxs(e.Fragment, { children: [
96
+ /* @__PURE__ */ e.jsxs("div", { className: "kpi-grid", children: [
97
+ /* @__PURE__ */ e.jsx(Q, { label: "节点数", value: s.nodeCount, icon: "N", color: "#60a5fa" }),
98
+ /* @__PURE__ */ e.jsx(Q, { label: "边数", value: s.edgeCount, icon: "E", color: "#34d399" }),
99
+ /* @__PURE__ */ e.jsx(Q, { label: "分支数", value: a.length, icon: "B", color: "#a78bfa" }),
100
+ /* @__PURE__ */ e.jsx(Q, { label: "总提交", value: r.length, icon: "C", color: "#fbbf24" })
101
+ ] }),
102
+ j.length > 1 && /* @__PURE__ */ e.jsxs("div", { className: "card", children: [
103
+ /* @__PURE__ */ e.jsx("h2", { children: "实时指标" }),
104
+ /* @__PURE__ */ e.jsxs("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 16 }, children: [
105
+ /* @__PURE__ */ e.jsx(
106
+ ce,
107
+ {
108
+ data: j,
109
+ dataKey: "rps",
110
+ title: "请求速率 (req/s)",
111
+ color: "#60a5fa",
112
+ unit: "req/s"
113
+ }
114
+ ),
115
+ /* @__PURE__ */ e.jsx(
116
+ ce,
117
+ {
118
+ data: j,
119
+ dataKey: "memory",
120
+ title: "JVM 内存",
121
+ color: "#a78bfa",
122
+ unit: "MB",
123
+ format: (t) => Math.round(t / 1024 / 1024)
124
+ }
125
+ )
126
+ ] })
127
+ ] }),
128
+ /* @__PURE__ */ e.jsxs("div", { className: "row-2", children: [
129
+ /* @__PURE__ */ e.jsxs("div", { className: "card", children: [
130
+ /* @__PURE__ */ e.jsx("h2", { children: "Schema 概览" }),
131
+ f && /* @__PURE__ */ e.jsx("div", { className: "muted", children: "加载中…" }),
132
+ !f && g.length === 0 && h.length === 0 && /* @__PURE__ */ e.jsx("div", { className: "empty", children: "暂无 Schema,请先 CREATE TAG / CREATE EDGE" }),
133
+ g.length > 0 && /* @__PURE__ */ e.jsxs(e.Fragment, { children: [
134
+ /* @__PURE__ */ e.jsxs("h3", { style: { marginTop: 12 }, children: [
135
+ "TAG (",
136
+ g.length,
137
+ ")"
138
+ ] }),
139
+ /* @__PURE__ */ e.jsx("div", { className: "chip-list", children: g.map((t, m) => /* @__PURE__ */ e.jsx("span", { className: "chip tag-chip", children: t.Name || t.name || JSON.stringify(t) }, m)) })
140
+ ] }),
141
+ h.length > 0 && /* @__PURE__ */ e.jsxs(e.Fragment, { children: [
142
+ /* @__PURE__ */ e.jsxs("h3", { style: { marginTop: 12 }, children: [
143
+ "EDGE (",
144
+ h.length,
145
+ ")"
146
+ ] }),
147
+ /* @__PURE__ */ e.jsx("div", { className: "chip-list", children: h.map((t, m) => /* @__PURE__ */ e.jsx("span", { className: "chip edge-chip", children: t.Name || t.name || JSON.stringify(t) }, m)) })
148
+ ] }),
149
+ n.length > 0 && /* @__PURE__ */ e.jsxs(e.Fragment, { children: [
150
+ /* @__PURE__ */ e.jsxs("h3", { style: { marginTop: 12 }, children: [
151
+ "INDEX (",
152
+ n.length,
153
+ ")"
154
+ ] }),
155
+ /* @__PURE__ */ e.jsxs("table", { children: [
156
+ /* @__PURE__ */ e.jsx("thead", { children: /* @__PURE__ */ e.jsxs("tr", { children: [
157
+ /* @__PURE__ */ e.jsx("th", { children: "名称" }),
158
+ /* @__PURE__ */ e.jsx("th", { children: "类型" }),
159
+ /* @__PURE__ */ e.jsx("th", { children: "标签" })
160
+ ] }) }),
161
+ /* @__PURE__ */ e.jsx("tbody", { children: n.map((t, m) => /* @__PURE__ */ e.jsxs("tr", { children: [
162
+ /* @__PURE__ */ e.jsx("td", { className: "mono", children: t.Name || t.name || "-" }),
163
+ /* @__PURE__ */ e.jsx("td", { children: t.Type || t.type || "-" }),
164
+ /* @__PURE__ */ e.jsx("td", { children: t.Label || t.label || "-" })
165
+ ] }, m)) })
166
+ ] })
167
+ ] })
168
+ ] }),
169
+ /* @__PURE__ */ e.jsxs("div", { className: "card", children: [
170
+ /* @__PURE__ */ e.jsx("h2", { children: "快捷操作" }),
171
+ /* @__PURE__ */ e.jsxs("div", { className: "action-grid", children: [
172
+ /* @__PURE__ */ e.jsx(
173
+ ee,
174
+ {
175
+ title: "创建节点",
176
+ desc: "CREATE (n:Person {name:'Tom'})",
177
+ color: "#60a5fa"
178
+ }
179
+ ),
180
+ /* @__PURE__ */ e.jsx(
181
+ ee,
182
+ {
183
+ title: "查询数据",
184
+ desc: "MATCH (n) RETURN n LIMIT 10",
185
+ color: "#34d399"
186
+ }
187
+ ),
188
+ /* @__PURE__ */ e.jsx(
189
+ ee,
190
+ {
191
+ title: "Schema 管理",
192
+ desc: "CREATE TAG / CREATE EDGE",
193
+ color: "#a78bfa"
194
+ }
195
+ ),
196
+ /* @__PURE__ */ e.jsx(
197
+ ee,
198
+ {
199
+ title: "版本管理",
200
+ desc: "CALL db.branches() / commits",
201
+ color: "#fbbf24"
202
+ }
203
+ )
204
+ ] })
205
+ ] })
206
+ ] }),
207
+ /* @__PURE__ */ e.jsxs("div", { className: "card", children: [
208
+ /* @__PURE__ */ e.jsx("h2", { children: "最近提交" }),
209
+ f && /* @__PURE__ */ e.jsx("div", { className: "muted", children: "加载中…" }),
210
+ l && /* @__PURE__ */ e.jsx("div", { className: "error", children: l }),
211
+ !f && k.length === 0 && /* @__PURE__ */ e.jsx("div", { className: "empty", children: "还没有任何 commit" }),
212
+ !f && k.length > 0 && /* @__PURE__ */ e.jsxs("table", { children: [
213
+ /* @__PURE__ */ e.jsx("thead", { children: /* @__PURE__ */ e.jsxs("tr", { children: [
214
+ /* @__PURE__ */ e.jsx("th", { children: "Commit" }),
215
+ /* @__PURE__ */ e.jsx("th", { children: "分支" }),
216
+ /* @__PURE__ */ e.jsx("th", { children: "作者" }),
217
+ /* @__PURE__ */ e.jsx("th", { children: "说明" }),
218
+ /* @__PURE__ */ e.jsx("th", { children: "节点" }),
219
+ /* @__PURE__ */ e.jsx("th", { children: "边" }),
220
+ /* @__PURE__ */ e.jsx("th", { children: "时间" })
221
+ ] }) }),
222
+ /* @__PURE__ */ e.jsx("tbody", { children: k.map((t) => /* @__PURE__ */ e.jsxs("tr", { children: [
223
+ /* @__PURE__ */ e.jsx("td", { className: "mono", children: Y(t.id, 10) }),
224
+ /* @__PURE__ */ e.jsx("td", { children: /* @__PURE__ */ e.jsx("span", { className: "tag", children: t.branch }) }),
225
+ /* @__PURE__ */ e.jsx("td", { children: t.author }),
226
+ /* @__PURE__ */ e.jsx("td", { children: t.message }),
227
+ /* @__PURE__ */ e.jsx("td", { children: t.nodeCount }),
228
+ /* @__PURE__ */ e.jsx("td", { children: t.edgeCount }),
229
+ /* @__PURE__ */ e.jsx("td", { children: ae(t.timestamp) })
230
+ ] }, t.id)) })
231
+ ] })
232
+ ] })
233
+ ] });
234
+ }
235
+ function Q({ label: s, value: a, icon: o, color: r }) {
236
+ return /* @__PURE__ */ e.jsxs("div", { className: "kpi", children: [
237
+ /* @__PURE__ */ e.jsx("div", { className: "kpi-icon", style: { background: r + "20", color: r }, children: o }),
238
+ /* @__PURE__ */ e.jsxs("div", { className: "kpi-content", children: [
239
+ /* @__PURE__ */ e.jsx("div", { className: "kpi-value", children: a ?? "-" }),
240
+ /* @__PURE__ */ e.jsx("div", { className: "kpi-label", children: s })
241
+ ] })
242
+ ] });
243
+ }
244
+ function ee({ title: s, desc: a, color: o }) {
245
+ return /* @__PURE__ */ e.jsxs("div", { className: "action-card", style: { borderTopColor: o }, children: [
246
+ /* @__PURE__ */ e.jsx("div", { className: "action-title", children: s }),
247
+ /* @__PURE__ */ e.jsx("div", { className: "action-desc mono", children: a })
248
+ ] });
249
+ }
250
+ function ce({ data: s, dataKey: a, title: o, color: r, unit: d, format: p }) {
251
+ if (!s || s.length < 2) return null;
252
+ const S = 400, f = 120, x = 30, l = s.map((t) => p ? p(t[a]) : t[a]), C = Math.max(...l, 1), j = 0, v = C - j || 1, A = l.map((t, m) => {
253
+ const i = x + m / (l.length - 1) * (S - x * 2), c = f - x - (t - j) / v * (f - x * 2);
254
+ return { x: i, y: c, v: t };
255
+ }), k = A.map((t, m) => `${m === 0 ? "M" : "L"}${t.x},${t.y}`).join(" "), g = k + ` L${A[A.length - 1].x},${f - x} L${A[0].x},${f - x} Z`, h = l[l.length - 1], n = [0, 0.5, 1].map((t) => ({
256
+ value: Math.round(j + t * v),
257
+ y: f - x - t * (f - x * 2)
258
+ }));
259
+ return /* @__PURE__ */ e.jsxs("div", { className: "metrics-chart", children: [
260
+ /* @__PURE__ */ e.jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "baseline", marginBottom: 8 }, children: [
261
+ /* @__PURE__ */ e.jsx("span", { style: { fontSize: 13, color: "#94a3b8" }, children: o }),
262
+ /* @__PURE__ */ e.jsxs("span", { className: "mono", style: { fontSize: 18, fontWeight: 600, color: r }, children: [
263
+ h,
264
+ d ? " " + d : ""
265
+ ] })
266
+ ] }),
267
+ /* @__PURE__ */ e.jsxs("svg", { viewBox: `0 0 ${S} ${f}`, style: { width: "100%", height: f }, children: [
268
+ n.map((t, m) => /* @__PURE__ */ e.jsxs("g", { children: [
269
+ /* @__PURE__ */ e.jsx("line", { x1: x, y1: t.y, x2: S - x, y2: t.y, stroke: "#1e293b", strokeWidth: "1" }),
270
+ /* @__PURE__ */ e.jsx("text", { x: x - 4, y: t.y + 4, textAnchor: "end", fontSize: "9", fill: "#475569", children: t.value })
271
+ ] }, m)),
272
+ /* @__PURE__ */ e.jsx("path", { d: g, fill: r, fillOpacity: "0.1" }),
273
+ /* @__PURE__ */ e.jsx("path", { d: k, fill: "none", stroke: r, strokeWidth: "2", strokeLinejoin: "round" }),
274
+ /* @__PURE__ */ e.jsx("circle", { cx: A[A.length - 1].x, cy: A[A.length - 1].y, r: "3", fill: r })
275
+ ] })
276
+ ] });
277
+ }
278
+ const de = ["#3b82f6", "#10b981", "#f59e0b", "#ef4444", "#8b5cf6", "#06b6d4", "#ec4899", "#84cc16"], B = 24;
279
+ function qe({ server: s }) {
280
+ const [a, o] = N(null), [r, d] = N(!1), [p, S] = N(null), [f, x] = N(null), [l, C] = N(null), j = te(null), [v, A] = N({ width: 800, height: 500 });
281
+ $(() => {
282
+ var c;
283
+ const i = (c = j.current) == null ? void 0 : c.parentElement;
284
+ if (i) {
285
+ const b = new ResizeObserver((E) => {
286
+ for (const T of E)
287
+ A({ width: T.contentRect.width, height: Math.max(400, T.contentRect.height) });
288
+ });
289
+ return b.observe(i), () => b.disconnect();
290
+ }
291
+ }, []);
292
+ const k = K(async () => {
293
+ d(!0), S(null);
294
+ try {
295
+ const c = (await W.query("MATCH (n) RETURN n", "main") || []).map((E, T) => {
296
+ const w = E.n || E, y = typeof w == "string" ? V(w) : w;
297
+ return { id: y._id ?? T, label: y._label || "Node", props: y, x: 0, y: 0 };
298
+ });
299
+ let b = [];
300
+ try {
301
+ const E = await W.query("SHOW EDGES", "main"), T = Array.isArray(E) ? E : [];
302
+ for (const w of T) {
303
+ const y = w.Name || w.name || "";
304
+ if (y)
305
+ try {
306
+ const L = await W.query(`MATCH (a)-[r:${y}]->(b) RETURN a, r, b`, "main");
307
+ for (const R of L || []) {
308
+ const O = typeof R.a == "string" ? V(R.a) : R.a, z = typeof R.b == "string" ? V(R.b) : R.b, D = typeof R.r == "string" ? $e(R.r) : R.r;
309
+ b.push({
310
+ source: O._id,
311
+ target: z._id,
312
+ type: y,
313
+ props: D
314
+ });
315
+ }
316
+ } catch {
317
+ }
318
+ }
319
+ if (T.length === 0 && c.length > 0)
320
+ for (const w of ["KNOWS", "WORKS_AT", "LIKES", "FOLLOWS"])
321
+ try {
322
+ const y = await W.query(`MATCH (a)-[r:${w}]->(b) RETURN a, r, b`, "main");
323
+ if (Array.isArray(y) && y.length > 0)
324
+ for (const L of y) {
325
+ const R = typeof L.a == "string" ? V(L.a) : L.a, O = typeof L.b == "string" ? V(L.b) : L.b;
326
+ b.push({ source: R._id, target: O._id, type: w, props: {} });
327
+ }
328
+ } catch {
329
+ }
330
+ } catch {
331
+ }
332
+ o({ nodes: c, edges: b });
333
+ } catch (i) {
334
+ S(i.message);
335
+ } finally {
336
+ d(!1);
337
+ }
338
+ }, []);
339
+ $(() => {
340
+ k();
341
+ }, [s.head]), $(() => {
342
+ if (!a || a.nodes.length === 0) {
343
+ C(null);
344
+ return;
345
+ }
346
+ const i = a.nodes.map((T, w) => ({
347
+ ...T,
348
+ x: v.width / 2 + (Math.random() - 0.5) * 200,
349
+ y: v.height / 2 + (Math.random() - 0.5) * 200,
350
+ vx: 0,
351
+ vy: 0
352
+ })), c = a.edges.map((T) => ({
353
+ ...T,
354
+ sourceIdx: i.findIndex((w) => w.id === T.source),
355
+ targetIdx: i.findIndex((w) => w.id === T.target)
356
+ })).filter((T) => T.sourceIdx >= 0 && T.targetIdx >= 0), b = v.width / 2, E = v.height / 2;
357
+ for (let T = 0; T < 120; T++) {
358
+ const w = 1 - T / 120;
359
+ for (let y = 0; y < i.length; y++)
360
+ for (let L = y + 1; L < i.length; L++) {
361
+ let R = i[L].x - i[y].x, O = i[L].y - i[y].y, z = Math.sqrt(R * R + O * O) || 1, D = 800 * w / z;
362
+ i[y].vx -= R / z * D, i[y].vy -= O / z * D, i[L].vx += R / z * D, i[L].vy += O / z * D;
363
+ }
364
+ for (const y of c) {
365
+ const L = i[y.sourceIdx], R = i[y.targetIdx];
366
+ let O = R.x - L.x, z = R.y - L.y, D = Math.sqrt(O * O + z * z) || 1, G = (D - 120) * 0.05 * w;
367
+ L.vx += O / D * G, L.vy += z / D * G, R.vx -= O / D * G, R.vy -= z / D * G;
368
+ }
369
+ for (const y of i)
370
+ y.vx += (b - y.x) * 0.01 * w, y.vy += (E - y.y) * 0.01 * w;
371
+ for (const y of i)
372
+ y.vx *= 0.6, y.vy *= 0.6, y.x += y.vx, y.y += y.vy, y.x = Math.max(B, Math.min(v.width - B, y.x)), y.y = Math.max(B, Math.min(v.height - B, y.y));
373
+ }
374
+ C({ nodes: i, edges: c });
375
+ }, [a, v]);
376
+ const g = {};
377
+ let h = 0;
378
+ function n(i) {
379
+ return g[i] || (g[i] = de[h++ % de.length]), g[i];
380
+ }
381
+ function t(i, c) {
382
+ const b = n(i.label), E = f === c, T = (l == null ? void 0 : l.edges.filter((y) => y.sourceIdx === c || y.targetIdx === c)) || [], w = f !== null && !E && !T.some((y) => y.sourceIdx === f || y.targetIdx === f);
383
+ return /* @__PURE__ */ e.jsxs(
384
+ "g",
385
+ {
386
+ transform: `translate(${i.x},${i.y})`,
387
+ style: { cursor: "pointer", opacity: w ? 0.25 : 1 },
388
+ onClick: () => x(E ? null : c),
389
+ children: [
390
+ /* @__PURE__ */ e.jsx("circle", { r: B, fill: b, stroke: E ? "#fff" : "#0f172a", strokeWidth: E ? 3 : 2 }),
391
+ /* @__PURE__ */ e.jsx("text", { textAnchor: "middle", dy: "0.35em", fill: "#fff", fontSize: "11", fontWeight: "600", style: { pointerEvents: "none" }, children: i.label.substring(0, 4) }),
392
+ /* @__PURE__ */ e.jsx("text", { textAnchor: "middle", dy: B + 14, fill: "#94a3b8", fontSize: "10", style: { pointerEvents: "none" }, children: i.props.name || i.props._id || c })
393
+ ]
394
+ },
395
+ c
396
+ );
397
+ }
398
+ function m(i, c) {
399
+ const b = l.nodes[i.sourceIdx], E = l.nodes[i.targetIdx];
400
+ if (!b || !E) return null;
401
+ const T = E.x - b.x, w = E.y - b.y, y = Math.sqrt(T * T + w * w) || 1, L = T / y, R = w / y, O = b.x + L * B, z = b.y + R * B, D = E.x - L * B, G = E.y - R * B, u = f !== null && (i.sourceIdx === f || i.targetIdx === f), P = f !== null && !u, I = 10, M = Math.atan2(G - z, D - O), H = D - I * Math.cos(M - 0.35), q = G - I * Math.sin(M - 0.35), se = D - I * Math.cos(M + 0.35), je = G - I * Math.sin(M + 0.35);
402
+ return /* @__PURE__ */ e.jsxs("g", { style: { opacity: P ? 0.15 : 1 }, children: [
403
+ /* @__PURE__ */ e.jsx("line", { x1: O, y1: z, x2: D, y2: G, stroke: u ? "#60a5fa" : "#475569", strokeWidth: u ? 2.5 : 1.5 }),
404
+ /* @__PURE__ */ e.jsx("polygon", { points: `${D},${G} ${H},${q} ${se},${je}`, fill: u ? "#60a5fa" : "#475569" }),
405
+ i.type && /* @__PURE__ */ e.jsx("text", { x: (O + D) / 2, y: (z + G) / 2 - 6, textAnchor: "middle", fill: "#64748b", fontSize: "9", style: { pointerEvents: "none" }, children: i.type })
406
+ ] }, c);
407
+ }
408
+ return /* @__PURE__ */ e.jsxs(e.Fragment, { children: [
409
+ /* @__PURE__ */ e.jsxs("div", { className: "card", children: [
410
+ /* @__PURE__ */ e.jsxs("div", { className: "toolbar", children: [
411
+ /* @__PURE__ */ e.jsx("button", { className: "primary", onClick: k, disabled: r, children: r ? "加载中…" : "刷新图" }),
412
+ /* @__PURE__ */ e.jsx("span", { className: "muted", children: l ? `${l.nodes.length} 节点 · ${l.edges.length} 边` : "无数据" }),
413
+ f !== null && (l == null ? void 0 : l.nodes[f]) && /* @__PURE__ */ e.jsxs("span", { className: "tag", style: { marginLeft: 8 }, children: [
414
+ "选中: ",
415
+ l.nodes[f].label,
416
+ " (",
417
+ l.nodes[f].props.name || l.nodes[f].props._id,
418
+ ")"
419
+ ] })
420
+ ] }),
421
+ p && /* @__PURE__ */ e.jsx("div", { className: "error", style: { marginTop: 8 }, children: p })
422
+ ] }),
423
+ /* @__PURE__ */ e.jsxs("div", { className: "card", style: { padding: 0, overflow: "hidden" }, children: [
424
+ (!l || l.nodes.length === 0) && !r && /* @__PURE__ */ e.jsx("div", { className: "empty", children: "暂无图数据。请先在 Cypher 查询页创建节点和边。" }),
425
+ /* @__PURE__ */ e.jsxs(
426
+ "svg",
427
+ {
428
+ ref: j,
429
+ width: "100%",
430
+ height: v.height,
431
+ style: { background: "#0f172a", display: l != null && l.nodes.length ? "block" : "none" },
432
+ children: [
433
+ /* @__PURE__ */ e.jsx("defs", { children: /* @__PURE__ */ e.jsx("pattern", { id: "grid", width: "40", height: "40", patternUnits: "userSpaceOnUse", children: /* @__PURE__ */ e.jsx("path", { d: "M 40 0 L 0 0 0 40", fill: "none", stroke: "#1e293b", strokeWidth: "0.5" }) }) }),
434
+ /* @__PURE__ */ e.jsx("rect", { width: "100%", height: "100%", fill: "url(#grid)" }),
435
+ /* @__PURE__ */ e.jsx("g", { children: l == null ? void 0 : l.edges.map((i, c) => m(i, c)) }),
436
+ /* @__PURE__ */ e.jsx("g", { children: l == null ? void 0 : l.nodes.map((i, c) => t(i, c)) })
437
+ ]
438
+ }
439
+ )
440
+ ] }),
441
+ l && l.nodes.length > 0 && /* @__PURE__ */ e.jsxs("div", { className: "card", children: [
442
+ /* @__PURE__ */ e.jsx("h2", { children: "图例" }),
443
+ /* @__PURE__ */ e.jsx("div", { className: "chip-list", children: [...new Set(l.nodes.map((i) => i.label))].map((i) => /* @__PURE__ */ e.jsxs("span", { className: "chip", style: { borderColor: n(i), color: n(i) }, children: [
444
+ /* @__PURE__ */ e.jsx("span", { style: { width: 8, height: 8, borderRadius: "50%", background: n(i), display: "inline-block", marginRight: 4 } }),
445
+ i
446
+ ] }, i)) })
447
+ ] })
448
+ ] });
449
+ }
450
+ function V(s) {
451
+ const a = s.match(/\((\d+):/), o = s.match(/:\[(\w+)\]/), r = s.match(/\{(.+)\}/), d = { _id: a ? parseInt(a[1]) : 0, _label: o ? o[1] : "Node" };
452
+ return r && r[1].split(",").forEach((p) => {
453
+ const [S, f] = p.split("=").map((x) => x.trim());
454
+ S && f && (d[S] = isNaN(f) ? f.replace(/^'|'$/g, "") : Number(f));
455
+ }), d;
456
+ }
457
+ function $e(s) {
458
+ const a = s.match(/\}\s*\{(.*?)\}/) || s.match(/\{(.*)\}/), o = {};
459
+ return a && a[1] && a[1].split(",").forEach((r) => {
460
+ const [d, p] = r.split("=").map((S) => S.trim());
461
+ d && p && (o[d] = isNaN(p) ? p.replace(/^'|'$/g, "") : Number(p));
462
+ }), o;
463
+ }
464
+ function Ue({ server: s }) {
465
+ const [a, o] = N([]), [r, d] = N({}), [p, S] = N([]), [f, x] = N(!0), [l, C] = N(null);
466
+ $(() => {
467
+ let v = !1;
468
+ async function A() {
469
+ try {
470
+ x(!0);
471
+ const [k, g, h] = await Promise.all([
472
+ W.branches(),
473
+ W.commits(),
474
+ W.query("CALL db.branches()").catch(() => [])
475
+ ]), n = Array.isArray(k) ? k : k && k.Name ? [k.Name] : [];
476
+ o(n), S(Array.isArray(g) ? g : []);
477
+ const t = {};
478
+ (Array.isArray(h) ? h : []).forEach((m) => {
479
+ m && m.Name && (t[m.Name] = m);
480
+ }), d(t), C(null);
481
+ } catch (k) {
482
+ C(k.message);
483
+ } finally {
484
+ x(!1);
485
+ }
486
+ }
487
+ A();
488
+ }, [s.head]);
489
+ const j = p.reduce((v, A) => (v.has(A.branch) || v.set(A.branch, []), v.get(A.branch).push(A), v), /* @__PURE__ */ new Map());
490
+ return /* @__PURE__ */ e.jsxs(e.Fragment, { children: [
491
+ /* @__PURE__ */ e.jsxs("div", { className: "card", children: [
492
+ /* @__PURE__ */ e.jsx("h2", { children: "分支总览" }),
493
+ /* @__PURE__ */ e.jsx("div", { className: "muted", children: "每个分支指向一个 commit head;切换分支不会丢历史。" }),
494
+ f && /* @__PURE__ */ e.jsx("div", { className: "muted", children: "加载中…" }),
495
+ l && /* @__PURE__ */ e.jsx("div", { className: "error", children: l }),
496
+ !f && a.length === 0 && /* @__PURE__ */ e.jsx("div", { className: "empty", children: "尚无分支" }),
497
+ !f && a.length > 0 && /* @__PURE__ */ e.jsxs("table", { children: [
498
+ /* @__PURE__ */ e.jsx("thead", { children: /* @__PURE__ */ e.jsxs("tr", { children: [
499
+ /* @__PURE__ */ e.jsx("th", { children: "分支" }),
500
+ /* @__PURE__ */ e.jsx("th", { children: "Head commit" }),
501
+ /* @__PURE__ */ e.jsx("th", { children: "提交数" }),
502
+ /* @__PURE__ */ e.jsx("th", { children: "节点" }),
503
+ /* @__PURE__ */ e.jsx("th", { children: "边" })
504
+ ] }) }),
505
+ /* @__PURE__ */ e.jsx("tbody", { children: a.map((v) => {
506
+ var g;
507
+ const A = (g = r[v]) == null ? void 0 : g.Head, k = p.find((h) => h.id === A);
508
+ return /* @__PURE__ */ e.jsxs("tr", { children: [
509
+ /* @__PURE__ */ e.jsx("td", { children: /* @__PURE__ */ e.jsx("span", { className: "tag", children: v }) }),
510
+ /* @__PURE__ */ e.jsx("td", { className: "mono", children: Y(A, 12) || "-" }),
511
+ /* @__PURE__ */ e.jsx("td", { children: (j.get(v) || []).length }),
512
+ /* @__PURE__ */ e.jsx("td", { children: k ? k.nodeCount : "-" }),
513
+ /* @__PURE__ */ e.jsx("td", { children: k ? k.edgeCount : "-" })
514
+ ] }, v);
515
+ }) })
516
+ ] })
517
+ ] }),
518
+ /* @__PURE__ */ e.jsxs("div", { className: "card", children: [
519
+ /* @__PURE__ */ e.jsx("h2", { children: "操作提示" }),
520
+ /* @__PURE__ */ e.jsxs("ul", { className: "muted", style: { lineHeight: 1.8 }, children: [
521
+ /* @__PURE__ */ e.jsxs("li", { children: [
522
+ "在 Cypher 查询页执行 ",
523
+ /* @__PURE__ */ e.jsx("code", { className: "mono", children: "CALL db.branches()" }),
524
+ " / ",
525
+ /* @__PURE__ */ e.jsx("code", { className: "mono", children: "CALL db.commits()" }),
526
+ " 可以拿到结构化版本元数据。"
527
+ ] }),
528
+ /* @__PURE__ */ e.jsx("li", { children: "创建/合并/切换分支通过 Java API 或扩展控制面 endpoint(后续版本)。" }),
529
+ /* @__PURE__ */ e.jsxs("li", { children: [
530
+ 'HEAD 提交 ID 可在"提交历史"页找到,搭配 ',
531
+ /* @__PURE__ */ e.jsx("code", { className: "mono", children: "?commit=<id>" }),
532
+ " 即可对历史快照做只读查询。"
533
+ ] })
534
+ ] })
535
+ ] })
536
+ ] });
537
+ }
538
+ const he = [
539
+ "#60a5fa",
540
+ // blue
541
+ "#34d399",
542
+ // green
543
+ "#fbbf24",
544
+ // amber
545
+ "#a78bfa",
546
+ // purple
547
+ "#f87171",
548
+ // red
549
+ "#67e8f9",
550
+ // cyan
551
+ "#fb923c",
552
+ // orange
553
+ "#f472b6"
554
+ // pink
555
+ ];
556
+ function Be({ server: s }) {
557
+ const [a, o] = N([]), [r, d] = N(!0), [p, S] = N(null), [f, x] = N(""), [l, C] = N(""), [j, v] = N(null);
558
+ $(() => {
559
+ let g = !1;
560
+ async function h() {
561
+ try {
562
+ d(!0);
563
+ const n = await W.commits();
564
+ o(Array.isArray(n) ? n : []), S(null);
565
+ } catch (n) {
566
+ S(n.message);
567
+ } finally {
568
+ d(!1);
569
+ }
570
+ }
571
+ h();
572
+ }, [s.head]);
573
+ const A = X(
574
+ () => Array.from(new Set(a.map((g) => g.branch))),
575
+ [a]
576
+ ), k = X(
577
+ () => a.filter((g) => !f || g.branch === f).filter((g) => !l || (g.message || "").toLowerCase().includes(l.toLowerCase()) || (g.author || "").toLowerCase().includes(l.toLowerCase()) || (g.id || "").startsWith(l)),
578
+ [a, f, l]
579
+ );
580
+ return /* @__PURE__ */ e.jsxs(e.Fragment, { children: [
581
+ /* @__PURE__ */ e.jsxs("div", { className: "card", children: [
582
+ /* @__PURE__ */ e.jsxs("div", { className: "toolbar", style: { marginBottom: 12 }, children: [
583
+ /* @__PURE__ */ e.jsxs("select", { value: f, onChange: (g) => x(g.target.value), children: [
584
+ /* @__PURE__ */ e.jsx("option", { value: "", children: "所有分支" }),
585
+ A.map((g) => /* @__PURE__ */ e.jsx("option", { value: g, children: g }, g))
586
+ ] }),
587
+ /* @__PURE__ */ e.jsx(
588
+ "input",
589
+ {
590
+ placeholder: "搜索 commit message / author / id",
591
+ value: l,
592
+ onChange: (g) => C(g.target.value),
593
+ style: { flex: 1 }
594
+ }
595
+ ),
596
+ /* @__PURE__ */ e.jsx("button", { onClick: () => {
597
+ x(""), C(""), v(null);
598
+ }, children: "清空" })
599
+ ] }),
600
+ r && /* @__PURE__ */ e.jsx("div", { className: "muted", children: "加载中…" }),
601
+ p && /* @__PURE__ */ e.jsx("div", { className: "error-banner", children: p }),
602
+ !r && k.length === 0 && /* @__PURE__ */ e.jsx("div", { className: "empty", children: "没有匹配的 commit" }),
603
+ !r && k.length > 0 && /* @__PURE__ */ e.jsx(
604
+ Fe,
605
+ {
606
+ commits: k,
607
+ branches: A,
608
+ selected: j,
609
+ onSelect: v
610
+ }
611
+ )
612
+ ] }),
613
+ j && /* @__PURE__ */ e.jsxs("div", { className: "card", children: [
614
+ /* @__PURE__ */ e.jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "flex-start" }, children: [
615
+ /* @__PURE__ */ e.jsxs("div", { style: { flex: 1 }, children: [
616
+ /* @__PURE__ */ e.jsxs("h2", { style: { margin: "0 0 8px" }, children: [
617
+ /* @__PURE__ */ e.jsx("span", { className: "mono", style: { color: _(A, j.branch) }, children: Y(j.id, 12) }),
618
+ " ",
619
+ "提交详情"
620
+ ] }),
621
+ /* @__PURE__ */ e.jsxs("div", { style: { marginBottom: 8 }, children: [
622
+ /* @__PURE__ */ e.jsx("span", { className: "tag", style: { marginRight: 8 }, children: j.branch }),
623
+ /* @__PURE__ */ e.jsx("span", { className: "muted", children: ae(j.timestamp) })
624
+ ] }),
625
+ /* @__PURE__ */ e.jsxs("div", { style: { marginBottom: 4 }, children: [
626
+ /* @__PURE__ */ e.jsx("strong", { children: "作者:" }),
627
+ " ",
628
+ j.author
629
+ ] }),
630
+ /* @__PURE__ */ e.jsxs("div", { style: { marginBottom: 4 }, children: [
631
+ /* @__PURE__ */ e.jsx("strong", { children: "说明:" }),
632
+ " ",
633
+ j.message
634
+ ] }),
635
+ /* @__PURE__ */ e.jsxs("div", { style: { marginBottom: 4 }, children: [
636
+ /* @__PURE__ */ e.jsx("strong", { children: "父节点:" }),
637
+ " ",
638
+ (j.parents || []).map((g, h) => /* @__PURE__ */ e.jsx("span", { className: "mono", style: { marginRight: 8 }, children: Y(g, 12) }, h)) || "-"
639
+ ] }),
640
+ /* @__PURE__ */ e.jsxs("div", { style: { marginBottom: 8 }, children: [
641
+ /* @__PURE__ */ e.jsx("strong", { children: "变更:" }),
642
+ /* @__PURE__ */ e.jsxs("span", { className: "tag ok", style: { marginLeft: 4 }, children: [
643
+ j.nodeCount,
644
+ " 节点"
645
+ ] }),
646
+ /* @__PURE__ */ e.jsxs("span", { className: "tag alt", style: { marginLeft: 4 }, children: [
647
+ j.edgeCount,
648
+ " 边"
649
+ ] })
650
+ ] }),
651
+ /* @__PURE__ */ e.jsxs("div", { style: { marginBottom: 8 }, children: [
652
+ /* @__PURE__ */ e.jsx("strong", { children: "完整 ID:" }),
653
+ /* @__PURE__ */ e.jsx("code", { className: "mono", style: { fontSize: 11, color: "#94a3b8", wordBreak: "break-all" }, children: j.id })
654
+ ] })
655
+ ] }),
656
+ /* @__PURE__ */ e.jsx("button", { onClick: () => v(null), children: "✕" })
657
+ ] }),
658
+ /* @__PURE__ */ e.jsxs("div", { style: { marginTop: 12 }, children: [
659
+ /* @__PURE__ */ e.jsx("strong", { children: "按此 commit 查询:" }),
660
+ /* @__PURE__ */ e.jsx("pre", { className: "results mono", style: { marginTop: 4, fontSize: 12 }, children: `GET /query?commit=${j.id}&cypher=MATCH%20(n)%20RETURN%20n%20LIMIT%2010
661
+ -- 或者在 Cypher 查询页切到 "指定 commit" 模式` })
662
+ ] })
663
+ ] }),
664
+ /* @__PURE__ */ e.jsxs("div", { className: "card", children: [
665
+ /* @__PURE__ */ e.jsx("h2", { children: "如何按 commit 查询" }),
666
+ /* @__PURE__ */ e.jsx("pre", { className: "results", children: `GET /query?commit=<commitId>&cypher=MATCH%20(n)%20RETURN%20n%20LIMIT%2010
667
+ -- 或者在 Cypher 查询页切到 "指定 commit" 模式` })
668
+ ] })
669
+ ] });
670
+ }
671
+ function _(s, a) {
672
+ const o = s.indexOf(a);
673
+ return he[o % he.length];
674
+ }
675
+ function Fe({ commits: s, branches: a, selected: o, onSelect: r }) {
676
+ const d = X(
677
+ () => [...s].sort((h, n) => (n.timestamp || 0) - (h.timestamp || 0)),
678
+ [s]
679
+ ), p = 130, S = 70, f = 7, x = 30, l = 50, C = 140, j = X(() => {
680
+ const h = /* @__PURE__ */ new Map(), n = [...a].sort((t, m) => t === "main" ? -1 : m === "main" ? 1 : a.indexOf(t) - a.indexOf(m));
681
+ return n.forEach((t, m) => h.set(t, m)), { lanes: h, totalLanes: n.length };
682
+ }, [a]), v = X(() => {
683
+ const h = /* @__PURE__ */ new Map();
684
+ return d.forEach((n, t) => {
685
+ const m = j.lanes.get(n.branch) || 0;
686
+ h.set(n.id, {
687
+ col: t,
688
+ x: x + t * p + p / 2,
689
+ y: l + m * S + S / 2,
690
+ lane: m,
691
+ branch: n.branch
692
+ });
693
+ }), h;
694
+ }, [d, j]), A = X(() => {
695
+ const h = [];
696
+ for (const n of d) {
697
+ const t = v.get(n.id);
698
+ if (!t) continue;
699
+ const m = n.parents || [];
700
+ m.forEach((i, c) => {
701
+ const b = v.get(i);
702
+ b && h.push({
703
+ from: t,
704
+ to: b,
705
+ color: _(a, n.branch),
706
+ isMainParent: c === 0,
707
+ isMerge: m.length > 1
708
+ });
709
+ });
710
+ }
711
+ return h;
712
+ }, [d, v, j, a]), k = x + d.length * p + C, g = l + j.totalLanes * S + 30;
713
+ return /* @__PURE__ */ e.jsxs("div", { className: "commit-flow-graph", style: { overflowX: "auto", overflowY: "hidden", padding: "12px 0" }, children: [
714
+ /* @__PURE__ */ e.jsxs("div", { style: { minWidth: k, position: "relative" }, children: [
715
+ /* @__PURE__ */ e.jsxs(
716
+ "svg",
717
+ {
718
+ width: k,
719
+ height: g,
720
+ style: { position: "absolute", top: 0, left: 0 },
721
+ children: [
722
+ /* @__PURE__ */ e.jsxs("defs", { children: [
723
+ /* @__PURE__ */ e.jsx(
724
+ "marker",
725
+ {
726
+ id: "arrowhead",
727
+ markerWidth: "10",
728
+ markerHeight: "10",
729
+ refX: "8",
730
+ refY: "3",
731
+ orient: "auto",
732
+ markerUnits: "strokeWidth",
733
+ children: /* @__PURE__ */ e.jsx("path", { d: "M0,0 L0,6 L9,3 z", fill: "#475569" })
734
+ }
735
+ ),
736
+ /* @__PURE__ */ e.jsx(
737
+ "marker",
738
+ {
739
+ id: "arrowhead-active",
740
+ markerWidth: "10",
741
+ markerHeight: "10",
742
+ refX: "8",
743
+ refY: "3",
744
+ orient: "auto",
745
+ markerUnits: "strokeWidth",
746
+ children: /* @__PURE__ */ e.jsx("path", { d: "M0,0 L0,6 L9,3 z", fill: "#60a5fa" })
747
+ }
748
+ )
749
+ ] }),
750
+ [...j.lanes.entries()].map(([h, n]) => {
751
+ const t = l + n * S + S / 2;
752
+ return /* @__PURE__ */ e.jsx("g", { children: /* @__PURE__ */ e.jsx(
753
+ "line",
754
+ {
755
+ x1: x - 10,
756
+ y1: t,
757
+ x2: x + d.length * p + 10,
758
+ y2: t,
759
+ stroke: _(a, h),
760
+ strokeWidth: 1.5,
761
+ strokeDasharray: "6,4",
762
+ opacity: 0.4
763
+ }
764
+ ) }, h);
765
+ }),
766
+ /* @__PURE__ */ e.jsx(
767
+ "line",
768
+ {
769
+ x1: x - 10,
770
+ y1: l - 20,
771
+ x2: x + d.length * p - 10,
772
+ y2: l - 20,
773
+ stroke: "#475569",
774
+ strokeWidth: 1,
775
+ markerEnd: "url(#arrowhead)"
776
+ }
777
+ ),
778
+ /* @__PURE__ */ e.jsx(
779
+ "text",
780
+ {
781
+ x: x - 5,
782
+ y: l - 25,
783
+ fontSize: 11,
784
+ fill: "#64748b",
785
+ children: "旧"
786
+ }
787
+ ),
788
+ A.map((h, n) => {
789
+ var P, I;
790
+ const { from: t, to: m, color: i, isMerge: c, isMainParent: b } = h, E = (o == null ? void 0 : o.id) === ((P = t.commit) == null ? void 0 : P.id) || (o == null ? void 0 : o.id) === ((I = m.commit) == null ? void 0 : I.id), T = t.x - f - 2, w = t.y, y = m.x + f + 2, L = m.y;
791
+ if (t.lane === m.lane)
792
+ return /* @__PURE__ */ e.jsx(
793
+ "line",
794
+ {
795
+ x1: T,
796
+ y1: w,
797
+ x2: y,
798
+ y2: L,
799
+ stroke: E ? "#60a5fa" : i,
800
+ strokeWidth: E ? 2.5 : 2,
801
+ markerEnd: E ? "url(#arrowhead-active)" : "url(#arrowhead)"
802
+ },
803
+ n
804
+ );
805
+ const R = y - T, O = T + R * 0.3, z = w, D = y - R * 0.3, u = `M ${T},${w} C ${O},${z} ${D},${L} ${y},${L}`;
806
+ return /* @__PURE__ */ e.jsx(
807
+ "path",
808
+ {
809
+ d: u,
810
+ stroke: E ? "#60a5fa" : i,
811
+ strokeWidth: E ? 2.5 : c && !b ? 1.5 : 2,
812
+ strokeDasharray: c && !b ? "4,3" : "0",
813
+ fill: "none",
814
+ opacity: c && !b ? 0.6 : 1,
815
+ markerEnd: E ? "url(#arrowhead-active)" : "url(#arrowhead)"
816
+ },
817
+ n
818
+ );
819
+ }),
820
+ d.map((h) => {
821
+ const n = v.get(h.id);
822
+ if (!n) return null;
823
+ const t = (h.parents || []).length > 1, m = (o == null ? void 0 : o.id) === h.id, i = _(a, h.branch), c = m ? f + 2 : f;
824
+ return t ? /* @__PURE__ */ e.jsxs("g", { children: [
825
+ /* @__PURE__ */ e.jsx(
826
+ "polygon",
827
+ {
828
+ points: `${n.x},${n.y - c} ${n.x + c},${n.y} ${n.x},${n.y + c} ${n.x - c},${n.y}`,
829
+ fill: i,
830
+ stroke: m ? "#fff" : "#0b1220",
831
+ strokeWidth: m ? 2 : 1.5,
832
+ style: { cursor: "pointer" },
833
+ onClick: () => r(m ? null : h)
834
+ }
835
+ ),
836
+ m && /* @__PURE__ */ e.jsx(
837
+ "circle",
838
+ {
839
+ cx: n.x,
840
+ cy: n.y,
841
+ r: c + 4,
842
+ fill: "none",
843
+ stroke: i,
844
+ strokeWidth: 1,
845
+ opacity: 0.5
846
+ }
847
+ )
848
+ ] }, h.id) : /* @__PURE__ */ e.jsxs("g", { children: [
849
+ /* @__PURE__ */ e.jsx(
850
+ "circle",
851
+ {
852
+ cx: n.x,
853
+ cy: n.y,
854
+ r: c,
855
+ fill: i,
856
+ stroke: m ? "#fff" : "#0b1220",
857
+ strokeWidth: m ? 2 : 1.5,
858
+ style: { cursor: "pointer" },
859
+ onClick: () => r(m ? null : h)
860
+ }
861
+ ),
862
+ m && /* @__PURE__ */ e.jsx(
863
+ "circle",
864
+ {
865
+ cx: n.x,
866
+ cy: n.y,
867
+ r: c + 4,
868
+ fill: "none",
869
+ stroke: i,
870
+ strokeWidth: 1,
871
+ opacity: 0.5
872
+ }
873
+ )
874
+ ] }, h.id);
875
+ }),
876
+ [...j.lanes.entries()].map(([h, n]) => {
877
+ const t = l + n * S + S / 2, m = x + d.length * p + 10, i = _(a, h), b = d.filter((E) => E.branch === h)[0];
878
+ return /* @__PURE__ */ e.jsxs("g", { children: [
879
+ /* @__PURE__ */ e.jsx(
880
+ "line",
881
+ {
882
+ x1: m - 10,
883
+ y1: t,
884
+ x2: m + 10,
885
+ y2: t,
886
+ stroke: i,
887
+ strokeWidth: 2
888
+ }
889
+ ),
890
+ /* @__PURE__ */ e.jsx(
891
+ "rect",
892
+ {
893
+ x: m + 12,
894
+ y: t - 11,
895
+ width: Math.max(60, h.length * 8 + 16),
896
+ height: 22,
897
+ rx: 4,
898
+ fill: i + "20",
899
+ stroke: i,
900
+ strokeWidth: 1.5,
901
+ style: { cursor: "pointer" },
902
+ onClick: () => b && r(b)
903
+ }
904
+ ),
905
+ /* @__PURE__ */ e.jsx(
906
+ "text",
907
+ {
908
+ x: m + 22,
909
+ y: t + 4,
910
+ fontSize: 12,
911
+ fill: i,
912
+ fontFamily: "'SFMono-Regular', Menlo, Consolas, monospace",
913
+ fontWeight: 600,
914
+ style: { cursor: "pointer" },
915
+ onClick: () => b && r(b),
916
+ children: h
917
+ }
918
+ )
919
+ ] }, h);
920
+ }),
921
+ [...j.lanes.entries()].map(([h, n]) => {
922
+ const t = l + n * S + S / 2, m = _(a, h);
923
+ return /* @__PURE__ */ e.jsx(
924
+ "text",
925
+ {
926
+ x: 5,
927
+ y: t + 4,
928
+ fontSize: 11,
929
+ fill: m,
930
+ fontFamily: "'SFMono-Regular', Menlo, Consolas, monospace",
931
+ children: h
932
+ },
933
+ `${h}-label`
934
+ );
935
+ })
936
+ ]
937
+ }
938
+ ),
939
+ /* @__PURE__ */ e.jsx("div", { style: { position: "relative", pointerEvents: "none" }, children: d.map((h, n) => {
940
+ const t = v.get(h.id);
941
+ if (!t) return null;
942
+ const m = (o == null ? void 0 : o.id) === h.id;
943
+ return /* @__PURE__ */ e.jsxs(
944
+ "div",
945
+ {
946
+ onClick: () => r(m ? null : h),
947
+ style: {
948
+ position: "absolute",
949
+ left: t.x - p / 2 + 12,
950
+ top: t.y - 26,
951
+ width: p - 14,
952
+ fontSize: 11,
953
+ color: m ? "#f8fafc" : "#cbd5e1",
954
+ pointerEvents: "auto",
955
+ cursor: "pointer",
956
+ textAlign: "left"
957
+ },
958
+ children: [
959
+ /* @__PURE__ */ e.jsx(
960
+ "div",
961
+ {
962
+ className: "mono",
963
+ style: {
964
+ fontSize: 10,
965
+ color: _(a, h.branch),
966
+ fontWeight: 600,
967
+ whiteSpace: "nowrap",
968
+ overflow: "hidden",
969
+ textOverflow: "ellipsis"
970
+ },
971
+ title: h.id,
972
+ children: Y(h.id, 7)
973
+ }
974
+ ),
975
+ /* @__PURE__ */ e.jsx(
976
+ "div",
977
+ {
978
+ style: {
979
+ whiteSpace: "nowrap",
980
+ overflow: "hidden",
981
+ textOverflow: "ellipsis"
982
+ },
983
+ title: h.message,
984
+ children: h.message
985
+ }
986
+ ),
987
+ /* @__PURE__ */ e.jsx("div", { style: { fontSize: 9, color: "#64748b", whiteSpace: "nowrap" }, children: ae(h.timestamp) })
988
+ ]
989
+ },
990
+ h.id
991
+ );
992
+ }) })
993
+ ] }),
994
+ /* @__PURE__ */ e.jsxs("div", { style: { marginTop: 16, display: "flex", flexWrap: "wrap", gap: 12, padding: "8px 0", borderTop: "1px solid #1e293b" }, children: [
995
+ a.map((h) => /* @__PURE__ */ e.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 6, fontSize: 12 }, children: [
996
+ /* @__PURE__ */ e.jsx("span", { style: {
997
+ display: "inline-block",
998
+ width: 14,
999
+ height: 3,
1000
+ background: _(a, h)
1001
+ } }),
1002
+ /* @__PURE__ */ e.jsx("span", { className: "mono", children: h }),
1003
+ /* @__PURE__ */ e.jsxs("span", { className: "muted", children: [
1004
+ "(",
1005
+ d.filter((n) => n.branch === h).length,
1006
+ " commits)"
1007
+ ] })
1008
+ ] }, h)),
1009
+ /* @__PURE__ */ e.jsx("div", { style: { marginLeft: "auto", fontSize: 11, color: "#64748b" }, children: "💡 点击 commit 或分支标签查看详情 · 时间轴 ←" })
1010
+ ] })
1011
+ ] });
1012
+ }
1013
+ const _e = /* @__PURE__ */ new Set([
1014
+ "MATCH",
1015
+ "RETURN",
1016
+ "CREATE",
1017
+ "DELETE",
1018
+ "DETACH",
1019
+ "SET",
1020
+ "MERGE",
1021
+ "WHERE",
1022
+ "AND",
1023
+ "OR",
1024
+ "NOT",
1025
+ "IN",
1026
+ "AS",
1027
+ "ORDER",
1028
+ "BY",
1029
+ "ASC",
1030
+ "DESC",
1031
+ "LIMIT",
1032
+ "SKIP",
1033
+ "WITH",
1034
+ "UNWIND",
1035
+ "OPTIONAL",
1036
+ "CALL",
1037
+ "YIELD",
1038
+ "SHOW",
1039
+ "TAGS",
1040
+ "EDGES",
1041
+ "INDEXES",
1042
+ "EXPLAIN",
1043
+ "PROFILE",
1044
+ "DISTINCT",
1045
+ "COUNT",
1046
+ "SUM",
1047
+ "AVG",
1048
+ "MIN",
1049
+ "MAX",
1050
+ "COLLECT",
1051
+ "LENGTH",
1052
+ "SIZE",
1053
+ "TO_INT",
1054
+ "TO_FLOAT",
1055
+ "ToString",
1056
+ "HAS",
1057
+ "STARTS",
1058
+ "ENDS",
1059
+ "CONTAINS",
1060
+ "IS",
1061
+ "NULL",
1062
+ "TRUE",
1063
+ "FALSE",
1064
+ "CASE",
1065
+ "WHEN",
1066
+ "THEN",
1067
+ "ELSE",
1068
+ "END",
1069
+ "FOREACH",
1070
+ "REMOVE",
1071
+ "ADD",
1072
+ "DROP",
1073
+ "REBUILD",
1074
+ "ALTER",
1075
+ "IF",
1076
+ "NOT",
1077
+ "EXISTS"
1078
+ ]), me = /* @__PURE__ */ new Set([
1079
+ "db.version",
1080
+ "db.branches",
1081
+ "db.commits",
1082
+ "db.head",
1083
+ "db.stats",
1084
+ "db.tags",
1085
+ "db.edges",
1086
+ "db.indexes"
1087
+ ]), Ke = [
1088
+ // 关键字
1089
+ { text: "MATCH", desc: "模式匹配", category: "keyword" },
1090
+ { text: "RETURN", desc: "返回结果", category: "keyword" },
1091
+ { text: "CREATE", desc: "创建节点/边", category: "keyword" },
1092
+ { text: "DELETE", desc: "删除节点/边", category: "keyword" },
1093
+ { text: "DETACH DELETE", desc: "删除节点及关联边", category: "keyword" },
1094
+ { text: "MERGE", desc: "查找或创建", category: "keyword" },
1095
+ { text: "WHERE", desc: "过滤条件", category: "keyword" },
1096
+ { text: "SET", desc: "设置属性", category: "keyword" },
1097
+ { text: "ORDER BY", desc: "排序", category: "keyword" },
1098
+ { text: "LIMIT", desc: "限制数量", category: "keyword" },
1099
+ { text: "SKIP", desc: "跳过记录", category: "keyword" },
1100
+ { text: "WITH", desc: "传递中间结果", category: "keyword" },
1101
+ { text: "OPTIONAL MATCH", desc: "可选匹配", category: "keyword" },
1102
+ { text: "UNWIND", desc: "展开列表", category: "keyword" },
1103
+ { text: "DISTINCT", desc: "去重", category: "keyword" },
1104
+ { text: "AS", desc: "别名", category: "keyword" },
1105
+ { text: "AND", desc: "逻辑与", category: "keyword" },
1106
+ { text: "OR", desc: "逻辑或", category: "keyword" },
1107
+ { text: "NOT", desc: "逻辑非", category: "keyword" },
1108
+ { text: "IS NULL", desc: "判空", category: "keyword" },
1109
+ { text: "IS NOT NULL", desc: "非空判断", category: "keyword" },
1110
+ { text: "EXISTS", desc: "存在性检查", category: "keyword" },
1111
+ { text: "CONTAINS", desc: "包含匹配", category: "keyword" },
1112
+ { text: "STARTS WITH", desc: "前缀匹配", category: "keyword" },
1113
+ { text: "ENDS WITH", desc: "后缀匹配", category: "keyword" },
1114
+ // 内置过程
1115
+ { text: "CALL db.version()", desc: "数据库版本", category: "builtin" },
1116
+ { text: "CALL db.branches()", desc: "分支列表", category: "builtin" },
1117
+ { text: "CALL db.commits()", desc: "提交历史", category: "builtin" },
1118
+ { text: "CALL db.stats()", desc: "统计信息", category: "builtin" },
1119
+ { text: "CALL db.tags()", desc: "标签列表", category: "builtin" },
1120
+ { text: "CALL db.edges()", desc: "边类型列表", category: "builtin" },
1121
+ { text: "CALL db.indexes()", desc: "索引列表", category: "builtin" },
1122
+ { text: "CALL db.head('main')", desc: "当前 head", category: "builtin" },
1123
+ { text: "SHOW TAGS", desc: "显示所有标签", category: "builtin" },
1124
+ { text: "SHOW EDGES", desc: "显示所有边类型", category: "builtin" },
1125
+ { text: "SHOW INDEXES", desc: "显示所有索引", category: "builtin" },
1126
+ // 聚合函数
1127
+ { text: "count(*)", desc: "计数", category: "function" },
1128
+ { text: "count(n)", desc: "节点计数", category: "function" },
1129
+ { text: "sum(n.age)", desc: "求和", category: "function" },
1130
+ { text: "avg(n.age)", desc: "平均值", category: "function" },
1131
+ { text: "min(n.age)", desc: "最小值", category: "function" },
1132
+ { text: "max(n.age)", desc: "最大值", category: "function" },
1133
+ { text: "collect(n.name)", desc: "收集为列表", category: "function" },
1134
+ // 常用模式
1135
+ { text: "MATCH (n) RETURN n LIMIT 10", desc: "查询所有节点", category: "pattern" },
1136
+ { text: "MATCH (n:Person) RETURN n", desc: "按标签查询", category: "pattern" },
1137
+ { text: "MATCH (n:Person {name: 'Alice'}) RETURN n", desc: "按属性查询", category: "pattern" },
1138
+ { text: "MATCH (a)-[r]->(b) RETURN a, r, b LIMIT 10", desc: "查询关系", category: "pattern" },
1139
+ { text: "CREATE (n:Person {name: 'Alice', age: 30})", desc: "创建节点", category: "pattern" },
1140
+ { text: "CREATE (a)-[:KNOWS]->(b)", desc: "创建关系", category: "pattern" },
1141
+ { text: "MERGE (n:Person {name: 'Alice'})", desc: " Upsert 节点", category: "pattern" },
1142
+ // DDL
1143
+ { text: "CREATE TAG Person (name STRING, age INT)", desc: "创建标签", category: "ddl" },
1144
+ { text: "CREATE EDGE KNOWS ()", desc: "创建边类型", category: "ddl" },
1145
+ { text: "CREATE TAG INDEX idx_name ON Person(name)", desc: "创建索引", category: "ddl" }
1146
+ ], pe = {
1147
+ keyword: "#c084fc",
1148
+ builtin: "#67e8f9",
1149
+ function: "#93c5fd",
1150
+ pattern: "#86efac",
1151
+ ddl: "#fbbf24"
1152
+ };
1153
+ function Je({ value: s, onChange: a, onKeyDown: o, rows: r = 10 }) {
1154
+ const d = te(null), p = te(null), [S, f] = N([]), [x, l] = N(0), [C, j] = N(!1), [v, A] = N({ top: 0, left: 0 }), k = K(() => {
1155
+ d.current && p.current && (d.current.scrollTop = p.current.scrollTop, d.current.scrollLeft = p.current.scrollLeft);
1156
+ }, []);
1157
+ function g(c, b) {
1158
+ const T = c.substring(0, b).match(/[a-zA-Z_*.]+$/);
1159
+ return T ? T[0] : "";
1160
+ }
1161
+ function h(c, b) {
1162
+ const T = c.value.substring(0, b).split(`
1163
+ `), w = T.length - 1, y = T[T.length - 1].length;
1164
+ return {
1165
+ top: (w + 1) * 22 - c.scrollTop + 4,
1166
+ left: Math.min(y * 7.8 + 10, c.clientWidth - 200)
1167
+ };
1168
+ }
1169
+ const n = K((c) => {
1170
+ const b = c.target.value;
1171
+ a(b);
1172
+ const E = c.target.selectionStart, T = g(b, E);
1173
+ if (T.length >= 1) {
1174
+ const w = Ke.filter(
1175
+ (y) => y.text.toLowerCase().startsWith(T.toLowerCase()) || y.desc.includes(T)
1176
+ ).slice(0, 12);
1177
+ w.length > 0 ? (f(w), l(0), j(!0), A(h(c.target, E))) : j(!1);
1178
+ } else
1179
+ j(!1);
1180
+ }, [a]), t = K((c) => {
1181
+ if (C && S.length > 0) {
1182
+ if (c.key === "ArrowDown") {
1183
+ c.preventDefault(), l((b) => (b + 1) % S.length);
1184
+ return;
1185
+ }
1186
+ if (c.key === "ArrowUp") {
1187
+ c.preventDefault(), l((b) => (b - 1 + S.length) % S.length);
1188
+ return;
1189
+ }
1190
+ if (c.key === "Tab" || c.key === "Enter") {
1191
+ c.preventDefault(), m(S[x]);
1192
+ return;
1193
+ }
1194
+ if (c.key === "Escape") {
1195
+ j(!1);
1196
+ return;
1197
+ }
1198
+ }
1199
+ o && o(c);
1200
+ }, [C, S, x, o]);
1201
+ function m(c) {
1202
+ const b = p.current;
1203
+ if (!b) return;
1204
+ const E = b.selectionStart, T = b.value, w = g(T, E), y = T.substring(0, E - w.length), L = T.substring(E), R = y + c.text + L;
1205
+ a(R), j(!1), setTimeout(() => {
1206
+ const O = E - w.length + c.text.length;
1207
+ b.setSelectionRange(O, O), b.focus();
1208
+ }, 0);
1209
+ }
1210
+ $(() => {
1211
+ if (!C) return;
1212
+ function c(b) {
1213
+ p.current && !p.current.contains(b.target) && j(!1);
1214
+ }
1215
+ return document.addEventListener("mousedown", c), () => document.removeEventListener("mousedown", c);
1216
+ }, [C]);
1217
+ const i = Xe(s || "");
1218
+ return /* @__PURE__ */ e.jsxs("div", { className: "cypher-editor-wrap", style: { position: "relative" }, children: [
1219
+ /* @__PURE__ */ e.jsx(
1220
+ "pre",
1221
+ {
1222
+ ref: d,
1223
+ className: "cypher-editor-highlight",
1224
+ "aria-hidden": "true",
1225
+ style: {
1226
+ position: "absolute",
1227
+ top: 0,
1228
+ left: 0,
1229
+ right: 0,
1230
+ bottom: 0,
1231
+ margin: 0,
1232
+ padding: "8px 10px",
1233
+ border: "1px solid #334155",
1234
+ borderRadius: 6,
1235
+ background: "#1e293b",
1236
+ color: "#e2e8f0",
1237
+ fontFamily: "'SFMono-Regular', Menlo, Consolas, monospace",
1238
+ fontSize: 13,
1239
+ lineHeight: 1.5,
1240
+ overflow: "auto",
1241
+ whiteSpace: "pre-wrap",
1242
+ wordBreak: "break-all",
1243
+ pointerEvents: "none",
1244
+ zIndex: 1
1245
+ },
1246
+ dangerouslySetInnerHTML: { __html: i }
1247
+ }
1248
+ ),
1249
+ /* @__PURE__ */ e.jsx(
1250
+ "textarea",
1251
+ {
1252
+ ref: p,
1253
+ value: s,
1254
+ onChange: n,
1255
+ onScroll: k,
1256
+ onKeyDown: t,
1257
+ spellCheck: !1,
1258
+ rows: r,
1259
+ placeholder: "MATCH (n:Person) RETURN n LIMIT 10",
1260
+ style: {
1261
+ width: "100%",
1262
+ marginTop: 12,
1263
+ background: "transparent",
1264
+ color: "transparent",
1265
+ caretColor: "#e2e8f0",
1266
+ border: "1px solid #334155",
1267
+ borderRadius: 6,
1268
+ padding: "8px 10px",
1269
+ fontFamily: "'SFMono-Regular', Menlo, Consolas, monospace",
1270
+ fontSize: 13,
1271
+ lineHeight: 1.5,
1272
+ resize: "vertical",
1273
+ position: "relative",
1274
+ zIndex: 2
1275
+ }
1276
+ }
1277
+ ),
1278
+ C && S.length > 0 && /* @__PURE__ */ e.jsxs(
1279
+ "div",
1280
+ {
1281
+ className: "cypher-autocomplete",
1282
+ style: {
1283
+ position: "absolute",
1284
+ top: v.top,
1285
+ left: v.left,
1286
+ zIndex: 100,
1287
+ background: "#1e293b",
1288
+ border: "1px solid #334155",
1289
+ borderRadius: 6,
1290
+ boxShadow: "0 8px 24px rgba(0,0,0,0.4)",
1291
+ maxHeight: 240,
1292
+ overflowY: "auto",
1293
+ minWidth: 260
1294
+ },
1295
+ children: [
1296
+ S.map((c, b) => /* @__PURE__ */ e.jsxs(
1297
+ "div",
1298
+ {
1299
+ className: `autocomplete-item ${b === x ? "selected" : ""}`,
1300
+ style: {
1301
+ padding: "6px 10px",
1302
+ cursor: "pointer",
1303
+ display: "flex",
1304
+ alignItems: "center",
1305
+ gap: 8,
1306
+ background: b === x ? "#334155" : "transparent",
1307
+ borderBottom: b < S.length - 1 ? "1px solid #1e293b" : "none"
1308
+ },
1309
+ onMouseEnter: () => l(b),
1310
+ onMouseDown: (E) => {
1311
+ E.preventDefault(), m(c);
1312
+ },
1313
+ children: [
1314
+ /* @__PURE__ */ e.jsx("span", { style: {
1315
+ fontSize: 10,
1316
+ fontWeight: 600,
1317
+ padding: "1px 4px",
1318
+ borderRadius: 3,
1319
+ background: pe[c.category] + "20",
1320
+ color: pe[c.category],
1321
+ minWidth: 48,
1322
+ textAlign: "center"
1323
+ }, children: c.category }),
1324
+ /* @__PURE__ */ e.jsx("span", { className: "mono", style: { fontSize: 12, color: "#e2e8f0", flex: 1 }, children: c.text }),
1325
+ /* @__PURE__ */ e.jsx("span", { style: { fontSize: 11, color: "#64748b", whiteSpace: "nowrap" }, children: c.desc })
1326
+ ]
1327
+ },
1328
+ b
1329
+ )),
1330
+ /* @__PURE__ */ e.jsx("div", { style: { padding: "4px 10px", fontSize: 10, color: "#475569", borderTop: "1px solid #334155" }, children: "Tab/Enter 选择 · ↑↓ 导航 · Esc 关闭" })
1331
+ ]
1332
+ }
1333
+ )
1334
+ ] });
1335
+ }
1336
+ function Xe(s) {
1337
+ const a = [];
1338
+ let o = 0;
1339
+ for (; o < s.length; ) {
1340
+ if (s[o] === "-" && s[o + 1] === "-") {
1341
+ const r = s.indexOf(`
1342
+ `, o), d = r === -1 ? s.substring(o) : s.substring(o, r);
1343
+ a.push(`<span class="hl-comment">${U(d)}</span>`), o = r === -1 ? s.length : r;
1344
+ continue;
1345
+ }
1346
+ if (s[o] === "'") {
1347
+ let r = o + 1;
1348
+ for (; r < s.length && s[r] !== "'"; )
1349
+ s[r] === "\\" && r++, r++;
1350
+ r = Math.min(r + 1, s.length), a.push(`<span class="hl-string">${U(s.substring(o, r))}</span>`), o = r;
1351
+ continue;
1352
+ }
1353
+ if (s[o] === '"') {
1354
+ let r = o + 1;
1355
+ for (; r < s.length && s[r] !== '"'; )
1356
+ s[r] === "\\" && r++, r++;
1357
+ r = Math.min(r + 1, s.length), a.push(`<span class="hl-string">${U(s.substring(o, r))}</span>`), o = r;
1358
+ continue;
1359
+ }
1360
+ if (/[0-9]/.test(s[o]) && (o === 0 || /[\s,({\-:[=<>!]/.test(s[o - 1]))) {
1361
+ let r = o;
1362
+ for (; r < s.length && /[0-9.]/.test(s[r]); ) r++;
1363
+ a.push(`<span class="hl-number">${U(s.substring(o, r))}</span>`), o = r;
1364
+ continue;
1365
+ }
1366
+ if (/[a-zA-Z_]/.test(s[o])) {
1367
+ let r = o;
1368
+ for (; r < s.length && /[a-zA-Z0-9_]/.test(s[r]); ) r++;
1369
+ const d = s.substring(o, r), p = d.toUpperCase();
1370
+ _e.has(p) ? a.push(`<span class="hl-keyword">${U(d)}</span>`) : me.has(p) || me.has(d.toLowerCase()) ? a.push(`<span class="hl-builtin">${U(d)}</span>`) : r < s.length && s[r] === "(" ? a.push(`<span class="hl-function">${U(d)}</span>`) : p === "ASC" || p === "DESC" ? a.push(`<span class="hl-keyword">${U(d)}</span>`) : a.push(U(d)), o = r;
1371
+ continue;
1372
+ }
1373
+ if ("-><-[]{}(),:.;=<>!+".includes(s[o])) {
1374
+ a.push(`<span class="hl-op">${U(s[o])}</span>`), o++;
1375
+ continue;
1376
+ }
1377
+ a.push(U(s[o])), o++;
1378
+ }
1379
+ return a.join("");
1380
+ }
1381
+ function U(s) {
1382
+ return s === "&" ? "&amp;" : s === "<" ? "&lt;" : s === ">" ? "&gt;" : s === '"' ? "&quot;" : s;
1383
+ }
1384
+ const ne = {
1385
+ 节点与边: `CREATE (a:Person {name: 'Alice', age: 30}),
1386
+ (b:Person {name: 'Bob', age: 25}),
1387
+ (c:Person {name: 'Carol', age: 28}),
1388
+ (a)-[:KNOWS]->(b),
1389
+ (b)-[:KNOWS]->(c)`,
1390
+ "查询所有 Person": "MATCH (n:Person) RETURN n.name AS name, n.age AS age ORDER BY n.age DESC",
1391
+ 变长路径: "MATCH (a:Person {name: 'Alice'})-[:KNOWS*1..3]->(b:Person) RETURN b.name AS friend",
1392
+ 反向关系: "MATCH (b:Person)<-[:KNOWS]-(a:Person) RETURN a.name AS friendOf, b.name AS who",
1393
+ 聚合分组: "MATCH (n:Person) RETURN n.age AS age, count(n) AS total ORDER BY age",
1394
+ "CALL db.version": "CALL db.version()",
1395
+ "CALL db.branches": "CALL db.branches()",
1396
+ "CALL db.commits": "CALL db.commits() LIMIT 10",
1397
+ "CALL db.head": "CALL db.head('main')",
1398
+ "SHOW TAGS": "SHOW TAGS",
1399
+ "SHOW EDGES": "SHOW EDGES",
1400
+ "SHOW STATS": "CALL db.stats()",
1401
+ EXPLAIN: "EXPLAIN MATCH (n:Person) WHERE n.age > 20 RETURN n"
1402
+ };
1403
+ function Ve({ server: s }) {
1404
+ const [a, o] = N(ne.节点与边), [r, d] = N("main"), [p, S] = N(""), [f, x] = N([]), [l, C] = N([]), [j, v] = N("branch"), [A, k] = N(!1), [g, h] = N(null), [n, t] = N(null), [m, i] = N(null), [c, b] = N(() => {
1405
+ try {
1406
+ return JSON.parse(localStorage.getItem("z-graph-history") || "[]");
1407
+ } catch {
1408
+ return [];
1409
+ }
1410
+ }), [E, T] = N(!1);
1411
+ $(() => {
1412
+ let u = !1;
1413
+ async function P() {
1414
+ try {
1415
+ const [I, M] = await Promise.all([W.branches(), W.commits()]), H = Array.isArray(I) ? I : I && I.Name ? [I.Name] : [], q = Array.isArray(M) ? M : [];
1416
+ x(H), C(q), H.length && !H.find((se) => se === r) && d(H[0]);
1417
+ } catch {
1418
+ }
1419
+ }
1420
+ P();
1421
+ }, [s.head]);
1422
+ function w(u, P) {
1423
+ const I = { cypher: u.substring(0, 200), time: Date.now(), elapsed: P, branch: r }, M = [I, ...c.filter((H) => H.cypher !== I.cypher)].slice(0, 50);
1424
+ b(M);
1425
+ try {
1426
+ localStorage.setItem("z-graph-history", JSON.stringify(M));
1427
+ } catch {
1428
+ }
1429
+ }
1430
+ const y = K(async () => {
1431
+ if (!a.trim()) return;
1432
+ k(!0), t(null), h(null), i(null);
1433
+ const u = performance.now();
1434
+ try {
1435
+ const P = await W.query(
1436
+ a,
1437
+ j === "branch" ? r : null,
1438
+ j === "commit" ? p : null
1439
+ ), I = Math.round(performance.now() - u) + "ms";
1440
+ h(P), i(I), w(a, I);
1441
+ } catch (P) {
1442
+ t(P.message), w(a, "error");
1443
+ } finally {
1444
+ k(!1);
1445
+ }
1446
+ }, [a, r, p, j]);
1447
+ function L(u) {
1448
+ o(ne[u]);
1449
+ }
1450
+ function R(u) {
1451
+ o(u.cypher), u.branch && d(u.branch), T(!1);
1452
+ }
1453
+ function O(u) {
1454
+ if (u == null) return /* @__PURE__ */ e.jsx("div", { className: "empty", children: "无返回" });
1455
+ if (typeof u == "string") return /* @__PURE__ */ e.jsx("pre", { children: u });
1456
+ if (Array.isArray(u)) {
1457
+ if (u.length === 0) return /* @__PURE__ */ e.jsx("div", { className: "empty", children: "0 行结果" });
1458
+ const P = Array.from(u.reduce((I, M) => (M && typeof M == "object" && Object.keys(M).forEach((H) => I.add(H)), I), /* @__PURE__ */ new Set()));
1459
+ return /* @__PURE__ */ e.jsxs("table", { children: [
1460
+ /* @__PURE__ */ e.jsx("thead", { children: /* @__PURE__ */ e.jsx("tr", { children: P.map((I) => /* @__PURE__ */ e.jsx("th", { children: I }, I)) }) }),
1461
+ /* @__PURE__ */ e.jsx("tbody", { children: u.map((I, M) => /* @__PURE__ */ e.jsx("tr", { children: P.map((H) => /* @__PURE__ */ e.jsx("td", { className: "mono", children: Ye(I[H]) }, H)) }, M)) })
1462
+ ] });
1463
+ }
1464
+ return /* @__PURE__ */ e.jsx("pre", { children: JSON.stringify(u, null, 2) });
1465
+ }
1466
+ function z() {
1467
+ if (!g || !Array.isArray(g) || g.length === 0) return;
1468
+ const u = Array.from(g.reduce((I, M) => (M && typeof M == "object" && Object.keys(M).forEach((H) => I.add(H)), I), /* @__PURE__ */ new Set())), P = [u.join(",")];
1469
+ for (const I of g)
1470
+ P.push(u.map((M) => {
1471
+ const H = I[M];
1472
+ if (H == null) return "";
1473
+ const q = typeof H == "object" ? JSON.stringify(H) : String(H);
1474
+ return q.includes(",") || q.includes('"') || q.includes(`
1475
+ `) ? '"' + q.replace(/"/g, '""') + '"' : q;
1476
+ }).join(","));
1477
+ G(P.join(`
1478
+ `), "z-graph-query.csv", "text/csv;charset=utf-8");
1479
+ }
1480
+ function D() {
1481
+ if (!g) return;
1482
+ const u = JSON.stringify(g, null, 2);
1483
+ G(u, "z-graph-query.json", "application/json;charset=utf-8");
1484
+ }
1485
+ function G(u, P, I) {
1486
+ const M = new Blob([u], { type: I }), H = URL.createObjectURL(M), q = document.createElement("a");
1487
+ q.href = H, q.download = P, document.body.appendChild(q), q.click(), document.body.removeChild(q), URL.revokeObjectURL(H);
1488
+ }
1489
+ return /* @__PURE__ */ e.jsxs(e.Fragment, { children: [
1490
+ /* @__PURE__ */ e.jsxs("div", { className: "card", children: [
1491
+ /* @__PURE__ */ e.jsxs("div", { className: "toolbar", children: [
1492
+ /* @__PURE__ */ e.jsx("button", { className: "primary", disabled: A || !a.trim(), onClick: y, children: A ? "执行中…" : "执行" }),
1493
+ /* @__PURE__ */ e.jsx("button", { onClick: () => o(""), disabled: A, children: "清空" }),
1494
+ /* @__PURE__ */ e.jsxs("select", { onChange: (u) => L(u.target.value), value: "", children: [
1495
+ /* @__PURE__ */ e.jsx("option", { value: "", disabled: !0, children: "载入示例…" }),
1496
+ Object.keys(ne).map((u) => /* @__PURE__ */ e.jsx("option", { value: u, children: u }, u))
1497
+ ] }),
1498
+ /* @__PURE__ */ e.jsxs("button", { onClick: () => T(!E), disabled: A, children: [
1499
+ "历史 (",
1500
+ c.length,
1501
+ ")"
1502
+ ] }),
1503
+ /* @__PURE__ */ e.jsx("span", { style: { flex: 1 } }),
1504
+ /* @__PURE__ */ e.jsxs("div", { className: "tabs", style: { margin: 0, borderBottom: "none" }, children: [
1505
+ /* @__PURE__ */ e.jsx("button", { className: j === "branch" ? "active" : "", onClick: () => v("branch"), children: "分支 head" }),
1506
+ /* @__PURE__ */ e.jsx("button", { className: j === "commit" ? "active" : "", onClick: () => v("commit"), children: "指定 commit" })
1507
+ ] })
1508
+ ] }),
1509
+ E && /* @__PURE__ */ e.jsxs("div", { className: "history-panel", children: [
1510
+ c.length === 0 && /* @__PURE__ */ e.jsx("div", { className: "muted", children: "暂无历史" }),
1511
+ c.slice(0, 20).map((u, P) => /* @__PURE__ */ e.jsxs("div", { className: "history-item", onClick: () => R(u), children: [
1512
+ /* @__PURE__ */ e.jsxs("span", { className: "history-cypher mono", children: [
1513
+ u.cypher.substring(0, 80),
1514
+ u.cypher.length > 80 ? "…" : ""
1515
+ ] }),
1516
+ /* @__PURE__ */ e.jsxs("span", { className: "history-meta", children: [
1517
+ u.elapsed && /* @__PURE__ */ e.jsx("span", { className: "tag ok", children: u.elapsed }),
1518
+ /* @__PURE__ */ e.jsx("span", { className: "muted", children: new Date(u.time).toLocaleTimeString() })
1519
+ ] })
1520
+ ] }, P))
1521
+ ] }),
1522
+ /* @__PURE__ */ e.jsx("div", { style: { marginTop: 12 }, children: j === "branch" ? /* @__PURE__ */ e.jsx("select", { value: r, onChange: (u) => d(u.target.value), children: f.map((u) => /* @__PURE__ */ e.jsx("option", { value: u, children: u }, u)) }) : /* @__PURE__ */ e.jsxs("select", { value: p, onChange: (u) => S(u.target.value), children: [
1523
+ /* @__PURE__ */ e.jsx("option", { value: "", children: "选择一个 commit" }),
1524
+ l.slice().reverse().map((u) => /* @__PURE__ */ e.jsxs("option", { value: u.id, children: [
1525
+ u.id.substring(0, 12),
1526
+ " · ",
1527
+ u.branch,
1528
+ " · ",
1529
+ u.message
1530
+ ] }, u.id))
1531
+ ] }) }),
1532
+ /* @__PURE__ */ e.jsx(
1533
+ Je,
1534
+ {
1535
+ value: a,
1536
+ onChange: o,
1537
+ onKeyDown: (u) => {
1538
+ (u.ctrlKey || u.metaKey) && u.key === "Enter" && (u.preventDefault(), y());
1539
+ },
1540
+ rows: 10
1541
+ }
1542
+ ),
1543
+ /* @__PURE__ */ e.jsx("div", { className: "help", children: "支持 ; 分隔多语句; Ctrl+Enter 快捷执行;写查询在 main 上产生新 commit。" })
1544
+ ] }),
1545
+ /* @__PURE__ */ e.jsxs("div", { className: "card", children: [
1546
+ /* @__PURE__ */ e.jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [
1547
+ /* @__PURE__ */ e.jsxs("h2", { style: { margin: 0 }, children: [
1548
+ "结果",
1549
+ m && /* @__PURE__ */ e.jsx("span", { className: "tag ok", style: { marginLeft: 8, fontSize: 11 }, children: m }),
1550
+ g && Array.isArray(g) && /* @__PURE__ */ e.jsxs("span", { className: "muted", style: { marginLeft: 8, fontSize: 13 }, children: [
1551
+ g.length,
1552
+ " 行"
1553
+ ] })
1554
+ ] }),
1555
+ g && Array.isArray(g) && g.length > 0 && /* @__PURE__ */ e.jsxs("div", { className: "toolbar", children: [
1556
+ /* @__PURE__ */ e.jsx("button", { onClick: z, children: "📥 CSV" }),
1557
+ /* @__PURE__ */ e.jsx("button", { onClick: D, children: "📥 JSON" })
1558
+ ] })
1559
+ ] }),
1560
+ n && /* @__PURE__ */ e.jsx("div", { className: "results", children: /* @__PURE__ */ e.jsx("pre", { className: "error", children: n }) }),
1561
+ !n && !g && /* @__PURE__ */ e.jsx("div", { className: "empty", children: "尚未执行" }),
1562
+ !n && g && /* @__PURE__ */ e.jsx("div", { className: "results", children: O(g) })
1563
+ ] })
1564
+ ] });
1565
+ }
1566
+ function Ye(s) {
1567
+ return s == null ? "" : typeof s == "object" ? JSON.stringify(s) : String(s);
1568
+ }
1569
+ const ue = [
1570
+ { label: "创建 TAG", cypher: "CREATE TAG IF NOT EXISTS Person (name string, age int)", category: "DDL" },
1571
+ { label: "创建 TAG (Company)", cypher: "CREATE TAG IF NOT EXISTS Company (name string, founded int)", category: "DDL" },
1572
+ { label: "创建 EDGE", cypher: "CREATE EDGE IF NOT EXISTS KNOWS (since int)", category: "DDL" },
1573
+ { label: "创建 EDGE (WORKS_AT)", cypher: "CREATE EDGE IF NOT EXISTS WORKS_AT (role string)", category: "DDL" },
1574
+ { label: "创建索引", cypher: "CREATE TAG INDEX IF NOT EXISTS idx_person_name ON Person(name)", category: "DDL" },
1575
+ { label: "添加属性", cypher: "ALTER TAG Person ADD (email string)", category: "DDL" },
1576
+ { label: "创建节点", cypher: "CREATE (n:Person {name: 'Alice', age: 30})", category: "写入" },
1577
+ { label: "批量创建", cypher: `CREATE (a:Person {name: 'Alice', age: 30}),
1578
+ (b:Person {name: 'Bob', age: 25}),
1579
+ (a)-[:KNOWS {since: 2020}]->(b)`, category: "写入" },
1580
+ { label: "查询所有节点", cypher: "MATCH (n) RETURN labels(n) AS labels, n.name AS name LIMIT 50", category: "查询" },
1581
+ { label: "变长路径", cypher: `MATCH (a:Person)-[:KNOWS*1..3]->(b:Person)
1582
+ RETURN a.name AS from, b.name AS to LIMIT 100`, category: "查询" },
1583
+ { label: "聚合统计", cypher: `MATCH (n:Person)
1584
+ RETURN n.age AS age, count(n) AS count ORDER BY age`, category: "查询" },
1585
+ { label: "OPTIONAL MATCH", cypher: `MATCH (a:Person)-[r?:KNOWS]->(b:Person)
1586
+ RETURN a.name AS from, b.name AS to`, category: "查询" }
1587
+ ];
1588
+ function Ze({ server: s }) {
1589
+ const [a, o] = N("TAGS"), [r, d] = N([]), [p, S] = N(!1), [f, x] = N(null), [l, C] = N(null), [j, v] = N(null), [A, k] = N(!1), g = K(async () => {
1590
+ S(!0), x(null);
1591
+ try {
1592
+ const t = await W.query(xe(a), "main", null);
1593
+ d(Array.isArray(t) ? t : []);
1594
+ } catch (t) {
1595
+ x(t.message), d([]);
1596
+ } finally {
1597
+ S(!1);
1598
+ }
1599
+ }, [a]);
1600
+ $(() => {
1601
+ g();
1602
+ }, [a, s.head]);
1603
+ async function h(t) {
1604
+ k(!0), v(null), C(null);
1605
+ try {
1606
+ const m = await W.query(t);
1607
+ C(m), setTimeout(g, 500);
1608
+ } catch (m) {
1609
+ v(m.message);
1610
+ } finally {
1611
+ k(!1);
1612
+ }
1613
+ }
1614
+ const n = [...new Set(ue.map((t) => t.category))];
1615
+ return /* @__PURE__ */ e.jsxs(e.Fragment, { children: [
1616
+ /* @__PURE__ */ e.jsxs("div", { className: "card", children: [
1617
+ /* @__PURE__ */ e.jsx("h2", { children: "DDL 快捷操作" }),
1618
+ /* @__PURE__ */ e.jsx("div", { className: "muted", style: { marginBottom: 12 }, children: "点击按钮直接执行 DDL 语句,写入操作会在 main 分支产生新 commit。" }),
1619
+ n.map((t) => /* @__PURE__ */ e.jsxs("div", { children: [
1620
+ /* @__PURE__ */ e.jsx("h3", { style: { marginTop: 12, marginBottom: 6 }, children: t }),
1621
+ /* @__PURE__ */ e.jsx("div", { className: "chip-list", children: ue.filter((m) => m.category === t).map((m, i) => /* @__PURE__ */ e.jsx("button", { className: "chip ddl-chip", onClick: () => h(m.cypher), disabled: A, children: m.label }, i)) })
1622
+ ] }, t)),
1623
+ A && /* @__PURE__ */ e.jsx("div", { className: "muted", style: { marginTop: 8 }, children: "执行中…" }),
1624
+ j && /* @__PURE__ */ e.jsx("pre", { className: "error", style: { marginTop: 8 }, children: j }),
1625
+ l && /* @__PURE__ */ e.jsx("pre", { className: "results", style: { marginTop: 8 }, children: JSON.stringify(l, null, 2) })
1626
+ ] }),
1627
+ /* @__PURE__ */ e.jsxs("div", { className: "card", children: [
1628
+ /* @__PURE__ */ e.jsxs("div", { className: "toolbar", children: [
1629
+ ["TAGS", "EDGES", "INDEXES"].map((t) => /* @__PURE__ */ e.jsxs(
1630
+ "button",
1631
+ {
1632
+ className: a === t ? "primary" : "",
1633
+ onClick: () => o(t),
1634
+ children: [
1635
+ "SHOW ",
1636
+ t
1637
+ ]
1638
+ },
1639
+ t
1640
+ )),
1641
+ /* @__PURE__ */ e.jsx("button", { onClick: g, disabled: p, children: p ? "刷新中…" : "刷新" })
1642
+ ] }),
1643
+ /* @__PURE__ */ e.jsxs("div", { className: "muted", style: { marginTop: 8 }, children: [
1644
+ "执行:",
1645
+ xe(a)
1646
+ ] })
1647
+ ] }),
1648
+ /* @__PURE__ */ e.jsxs("div", { className: "card", children: [
1649
+ f && /* @__PURE__ */ e.jsx("div", { className: "error", children: f }),
1650
+ !f && r.length === 0 && /* @__PURE__ */ e.jsxs("div", { className: "empty", children: [
1651
+ "尚无 ",
1652
+ a.toLowerCase()
1653
+ ] }),
1654
+ !f && r.length > 0 && /* @__PURE__ */ e.jsx(Qe, { rows: r })
1655
+ ] })
1656
+ ] });
1657
+ }
1658
+ function xe(s) {
1659
+ return s === "TAGS" ? "SHOW TAGS" : s === "EDGES" ? "SHOW EDGES" : "SHOW INDEXES";
1660
+ }
1661
+ function Qe({ rows: s }) {
1662
+ const a = Array.from(s.reduce((o, r) => (Object.keys(r || {}).forEach((d) => o.add(d)), o), /* @__PURE__ */ new Set()));
1663
+ return /* @__PURE__ */ e.jsxs("table", { children: [
1664
+ /* @__PURE__ */ e.jsx("thead", { children: /* @__PURE__ */ e.jsx("tr", { children: a.map((o) => /* @__PURE__ */ e.jsx("th", { children: o }, o)) }) }),
1665
+ /* @__PURE__ */ e.jsx("tbody", { children: s.map((o, r) => /* @__PURE__ */ e.jsx("tr", { children: a.map((d) => /* @__PURE__ */ e.jsx("td", { className: "mono", children: et(o[d]) }, d)) }, r)) })
1666
+ ] });
1667
+ }
1668
+ function et(s) {
1669
+ return s == null ? "" : typeof s == "object" ? JSON.stringify(s) : String(s);
1670
+ }
1671
+ const tt = {
1672
+ GET: "#86efac",
1673
+ POST: "#93c5fd",
1674
+ OPTIONS: "#94a3b8"
1675
+ };
1676
+ function st() {
1677
+ var g, h;
1678
+ const [s, a] = N(null), [o, r] = N(!0), [d, p] = N(null), [S, f] = N(!0), [x, l] = N({ method: "", status: "", path: "", requestId: "" }), [C, j] = N(null), v = K(async () => {
1679
+ try {
1680
+ const n = {};
1681
+ x.method && (n.method = x.method), x.status && (n.status = x.status), x.path && (n.path = x.path), x.requestId && (n.requestId = x.requestId), n.limit = 200;
1682
+ const t = await W.logs(n);
1683
+ a(t), p(null);
1684
+ } catch (n) {
1685
+ p(n.message);
1686
+ } finally {
1687
+ r(!1);
1688
+ }
1689
+ }, [x]);
1690
+ $(() => {
1691
+ if (v(), !S) return;
1692
+ const n = setInterval(v, 3e3);
1693
+ return () => clearInterval(n);
1694
+ }, [v, S]);
1695
+ function A(n) {
1696
+ if (!n) return "";
1697
+ const t = new Date(n);
1698
+ return t.toLocaleTimeString("zh-CN", { hour12: !1 }) + "." + String(t.getMilliseconds()).padStart(3, "0");
1699
+ }
1700
+ function k(n) {
1701
+ return n >= 500 ? "log-status-error" : n >= 400 ? "log-status-warn" : "log-status-ok";
1702
+ }
1703
+ return /* @__PURE__ */ e.jsxs(e.Fragment, { children: [
1704
+ /* @__PURE__ */ e.jsx("div", { className: "card", children: /* @__PURE__ */ e.jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [
1705
+ /* @__PURE__ */ e.jsxs("div", { children: [
1706
+ /* @__PURE__ */ e.jsx("h2", { children: "请求日志" }),
1707
+ /* @__PURE__ */ e.jsxs("div", { className: "muted", children: [
1708
+ "环形缓冲区最近 ",
1709
+ (s == null ? void 0 : s.bufferSize) || 500,
1710
+ " 条请求记录,自动刷新。"
1711
+ ] })
1712
+ ] }),
1713
+ /* @__PURE__ */ e.jsxs("div", { className: "toolbar", children: [
1714
+ /* @__PURE__ */ e.jsx(
1715
+ "button",
1716
+ {
1717
+ className: S ? "primary" : "",
1718
+ onClick: () => f(!S),
1719
+ children: S ? "⏸ 暂停" : "▶ 自动刷新"
1720
+ }
1721
+ ),
1722
+ /* @__PURE__ */ e.jsx("button", { onClick: v, children: "🔄 刷新" })
1723
+ ] })
1724
+ ] }) }),
1725
+ /* @__PURE__ */ e.jsx("div", { className: "card", children: /* @__PURE__ */ e.jsxs("div", { className: "toolbar", children: [
1726
+ /* @__PURE__ */ e.jsxs("select", { value: x.method, onChange: (n) => l((t) => ({ ...t, method: n.target.value })), children: [
1727
+ /* @__PURE__ */ e.jsx("option", { value: "", children: "全部方法" }),
1728
+ /* @__PURE__ */ e.jsx("option", { value: "GET", children: "GET" }),
1729
+ /* @__PURE__ */ e.jsx("option", { value: "POST", children: "POST" }),
1730
+ /* @__PURE__ */ e.jsx("option", { value: "OPTIONS", children: "OPTIONS" })
1731
+ ] }),
1732
+ /* @__PURE__ */ e.jsxs("select", { value: x.status, onChange: (n) => l((t) => ({ ...t, status: n.target.value })), children: [
1733
+ /* @__PURE__ */ e.jsx("option", { value: "", children: "全部状态" }),
1734
+ /* @__PURE__ */ e.jsx("option", { value: "2xx", children: "2xx 成功" }),
1735
+ /* @__PURE__ */ e.jsx("option", { value: "4xx", children: "4xx 客户端错误" }),
1736
+ /* @__PURE__ */ e.jsx("option", { value: "5xx", children: "5xx 服务器错误" }),
1737
+ /* @__PURE__ */ e.jsx("option", { value: "200", children: "200" }),
1738
+ /* @__PURE__ */ e.jsx("option", { value: "400", children: "400" }),
1739
+ /* @__PURE__ */ e.jsx("option", { value: "401", children: "401" }),
1740
+ /* @__PURE__ */ e.jsx("option", { value: "429", children: "429" }),
1741
+ /* @__PURE__ */ e.jsx("option", { value: "500", children: "500" })
1742
+ ] }),
1743
+ /* @__PURE__ */ e.jsx(
1744
+ "input",
1745
+ {
1746
+ placeholder: "路径过滤…",
1747
+ value: x.path,
1748
+ onChange: (n) => l((t) => ({ ...t, path: n.target.value })),
1749
+ style: { width: 200 }
1750
+ }
1751
+ ),
1752
+ /* @__PURE__ */ e.jsx(
1753
+ "input",
1754
+ {
1755
+ placeholder: "Request ID…",
1756
+ value: x.requestId,
1757
+ onChange: (n) => l((t) => ({ ...t, requestId: n.target.value })),
1758
+ style: { width: 160 }
1759
+ }
1760
+ ),
1761
+ s && /* @__PURE__ */ e.jsxs("span", { style: { fontSize: 12, color: "#64748b" }, children: [
1762
+ "共 ",
1763
+ s.total,
1764
+ " 条记录,显示 ",
1765
+ ((g = s.entries) == null ? void 0 : g.length) || 0,
1766
+ " 条"
1767
+ ] })
1768
+ ] }) }),
1769
+ o && /* @__PURE__ */ e.jsx("div", { className: "card", children: /* @__PURE__ */ e.jsx("div", { className: "empty", children: "加载中…" }) }),
1770
+ d && /* @__PURE__ */ e.jsxs("div", { className: "error-banner", children: [
1771
+ "加载失败: ",
1772
+ d
1773
+ ] }),
1774
+ s && s.entries && /* @__PURE__ */ e.jsx("div", { className: "card", style: { padding: 0, overflow: "hidden" }, children: /* @__PURE__ */ e.jsx("div", { className: "log-table-wrap", children: /* @__PURE__ */ e.jsxs("table", { className: "log-table", children: [
1775
+ /* @__PURE__ */ e.jsx("thead", { children: /* @__PURE__ */ e.jsxs("tr", { children: [
1776
+ /* @__PURE__ */ e.jsx("th", { style: { width: 110 }, children: "时间" }),
1777
+ /* @__PURE__ */ e.jsx("th", { style: { width: 90 }, children: "Request ID" }),
1778
+ /* @__PURE__ */ e.jsx("th", { style: { width: 50 }, children: "方法" }),
1779
+ /* @__PURE__ */ e.jsx("th", { style: { width: 45 }, children: "状态" }),
1780
+ /* @__PURE__ */ e.jsx("th", { children: "路径" }),
1781
+ /* @__PURE__ */ e.jsx("th", { style: { width: 60 }, children: "耗时" }),
1782
+ /* @__PURE__ */ e.jsx("th", { style: { width: 100 }, children: "客户端" })
1783
+ ] }) }),
1784
+ /* @__PURE__ */ e.jsxs("tbody", { children: [
1785
+ s.entries.map((n, t) => /* @__PURE__ */ e.jsxs(
1786
+ "tr",
1787
+ {
1788
+ className: `log-row ${C === t ? "selected" : ""}`,
1789
+ onClick: () => j(C === t ? null : t),
1790
+ children: [
1791
+ /* @__PURE__ */ e.jsx("td", { className: "mono", style: { fontSize: 11, color: "#64748b" }, children: A(n.timestamp) }),
1792
+ /* @__PURE__ */ e.jsx(
1793
+ "td",
1794
+ {
1795
+ className: "mono",
1796
+ style: { fontSize: 10, color: "#64748b", cursor: "pointer" },
1797
+ title: n.requestId,
1798
+ onClick: (m) => {
1799
+ m.stopPropagation(), l((i) => ({ ...i, requestId: n.requestId }));
1800
+ },
1801
+ children: n.requestId ? n.requestId.substring(0, 12) + "…" : "-"
1802
+ }
1803
+ ),
1804
+ /* @__PURE__ */ e.jsx("td", { children: /* @__PURE__ */ e.jsx(
1805
+ "span",
1806
+ {
1807
+ className: "log-method",
1808
+ style: { color: tt[n.method] || "#94a3b8" },
1809
+ children: n.method
1810
+ }
1811
+ ) }),
1812
+ /* @__PURE__ */ e.jsx("td", { children: /* @__PURE__ */ e.jsx("span", { className: `log-status ${k(n.status)}`, children: n.status }) }),
1813
+ /* @__PURE__ */ e.jsx("td", { className: "mono", style: { fontSize: 12, color: "#e2e8f0", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: n.path }),
1814
+ /* @__PURE__ */ e.jsxs("td", { className: "mono", style: { fontSize: 12, color: n.elapsedMs > 100 ? "#fbbf24" : "#94a3b8" }, children: [
1815
+ n.elapsedMs,
1816
+ "ms"
1817
+ ] }),
1818
+ /* @__PURE__ */ e.jsx("td", { className: "mono", style: { fontSize: 11, color: "#64748b" }, children: n.clientIp })
1819
+ ]
1820
+ },
1821
+ t
1822
+ )),
1823
+ s.entries.length === 0 && /* @__PURE__ */ e.jsx("tr", { children: /* @__PURE__ */ e.jsx("td", { colSpan: 7, style: { textAlign: "center", padding: 30, color: "#64748b" }, children: "暂无日志记录" }) })
1824
+ ] })
1825
+ ] }) }) }),
1826
+ C !== null && ((h = s == null ? void 0 : s.entries) == null ? void 0 : h[C]) && /* @__PURE__ */ e.jsxs("div", { className: "card", children: [
1827
+ /* @__PURE__ */ e.jsx("h3", { style: { margin: "0 0 8px", fontSize: 14, color: "#94a3b8" }, children: "请求详情" }),
1828
+ /* @__PURE__ */ e.jsx("pre", { className: "results", style: { fontSize: 12, maxHeight: 200 }, children: JSON.stringify(s.entries[C], null, 2) })
1829
+ ] })
1830
+ ] });
1831
+ }
1832
+ const ge = [
1833
+ {
1834
+ method: "GET",
1835
+ path: "/health",
1836
+ name: "健康检查",
1837
+ desc: "返回服务状态、当前 head、节点数和边数。",
1838
+ params: [],
1839
+ response: "{ status, head, nodeCount, edgeCount }"
1840
+ },
1841
+ {
1842
+ method: "POST",
1843
+ path: "/query",
1844
+ name: "执行 Cypher",
1845
+ desc: "执行单条 Cypher 查询。写查询会在分支上产生新 commit。",
1846
+ params: [
1847
+ { name: "cypher", type: "string", required: !0, example: "MATCH (n) RETURN n LIMIT 10" },
1848
+ { name: "branch", type: "string", required: !1, example: "main" },
1849
+ { name: "commit", type: "string", required: !1, example: "" }
1850
+ ],
1851
+ response: "[{ ... }, ...]"
1852
+ },
1853
+ {
1854
+ method: "POST",
1855
+ path: "/query/batch",
1856
+ name: "批量查询",
1857
+ desc: "一次执行多条 Cypher 语句,返回每条的结果和总耗时。",
1858
+ params: [
1859
+ { name: "statements", type: "array", required: !0, example: '[{"cypher":"MATCH (n) RETURN n"}]' }
1860
+ ],
1861
+ response: "{ results: [...], elapsedMs: 123 }"
1862
+ },
1863
+ {
1864
+ method: "POST",
1865
+ path: "/query/explain",
1866
+ name: "查询计划",
1867
+ desc: "返回查询的执行计划(不实际执行写操作),包括操作步骤和复杂度估算。",
1868
+ params: [
1869
+ { name: "cypher", type: "string", required: !0, example: "MATCH (n:Person) WHERE n.age > 20 RETURN n" },
1870
+ { name: "branch", type: "string", required: !1, example: "main" }
1871
+ ],
1872
+ response: "{ plan: { queryType, steps, estimatedComplexity }, cypher, branch }"
1873
+ },
1874
+ {
1875
+ method: "GET",
1876
+ path: "/meta/branches",
1877
+ name: "分支列表",
1878
+ desc: "返回所有分支的名称和当前 head。",
1879
+ params: [],
1880
+ response: '[{ Name: "main", Head: "..." }]'
1881
+ },
1882
+ {
1883
+ method: "GET",
1884
+ path: "/meta/commits",
1885
+ name: "提交历史",
1886
+ desc: "返回所有 commit 的完整历史。",
1887
+ params: [],
1888
+ response: "[{ id, parents, branch, author, message, timestamp, nodeCount, edgeCount }]"
1889
+ },
1890
+ {
1891
+ method: "GET",
1892
+ path: "/meta/schema",
1893
+ name: "Schema 信息",
1894
+ desc: "返回当前分支的 TAG、EDGE 和 INDEX 定义。",
1895
+ params: [
1896
+ { name: "branch", type: "string", required: !1, example: "main" }
1897
+ ],
1898
+ response: "{ branch, tags, edges, indexes }"
1899
+ },
1900
+ {
1901
+ method: "GET",
1902
+ path: "/meta/stats",
1903
+ name: "统计摘要",
1904
+ desc: "返回节点数、边数和 db.stats() 信息。",
1905
+ params: [
1906
+ { name: "branch", type: "string", required: !1, example: "main" }
1907
+ ],
1908
+ response: "{ branch, head, nodeCount, edgeCount, stats }"
1909
+ },
1910
+ {
1911
+ method: "GET",
1912
+ path: "/meta/metrics",
1913
+ name: "运行指标",
1914
+ desc: "返回服务运行指标:请求数、错误率、JVM 内存、运行时间等。",
1915
+ params: [],
1916
+ response: "{ uptimeMs, totalRequests, errorResponses, errorRate, jvmMemory, ... }"
1917
+ },
1918
+ {
1919
+ method: "GET",
1920
+ path: "/meta/logs",
1921
+ name: "请求日志",
1922
+ desc: "返回最近的请求日志(环形缓冲区,最多 500 条),支持过滤。",
1923
+ params: [
1924
+ { name: "limit", type: "number", required: !1, example: "100" },
1925
+ { name: "offset", type: "number", required: !1, example: "0" },
1926
+ { name: "method", type: "string", required: !1, example: "GET" },
1927
+ { name: "status", type: "string", required: !1, example: "4xx" },
1928
+ { name: "path", type: "string", required: !1, example: "/query" }
1929
+ ],
1930
+ response: "{ total, bufferSize, offset, limit, entries: [{ timestamp, method, path, status, elapsedMs, clientIp }] }"
1931
+ },
1932
+ {
1933
+ method: "GET",
1934
+ path: "/meta/export",
1935
+ name: "导出图数据",
1936
+ desc: "导出当前分支的完整图数据(节点 + 边 + schema)。",
1937
+ params: [
1938
+ { name: "branch", type: "string", required: !1, example: "main" }
1939
+ ],
1940
+ response: "{ version, branch, head, schema, nodes, edges, exportedAt }"
1941
+ },
1942
+ {
1943
+ method: "POST",
1944
+ path: "/meta/import",
1945
+ name: "导入节点",
1946
+ desc: "通过 JSON 数组导入节点数据。",
1947
+ params: [
1948
+ { name: "nodes", type: "array", required: !0, example: '[{"label":"Person","name":"Alice","age":30}]' },
1949
+ { name: "branch", type: "string", required: !1, example: "main" }
1950
+ ],
1951
+ response: "{ imported, branch, elapsedMs }"
1952
+ }
1953
+ ];
1954
+ function nt() {
1955
+ const [s, a] = N(null), [o, r] = N(null), [d, p] = N(!1), [S, f] = N(null);
1956
+ async function x(l) {
1957
+ p(!0), r(null), f(null);
1958
+ try {
1959
+ let C;
1960
+ if (l.method === "GET")
1961
+ C = await fe(`http://${ye()}${l.path}`);
1962
+ else {
1963
+ const j = {};
1964
+ for (const v of l.params)
1965
+ v.example && (j[v.name] = v.example);
1966
+ C = await fe(`http://${ye()}${l.path}`, {
1967
+ method: "POST",
1968
+ headers: { "Content-Type": "application/json" },
1969
+ body: JSON.stringify(j)
1970
+ });
1971
+ }
1972
+ r(C);
1973
+ } catch (C) {
1974
+ f(C.message);
1975
+ } finally {
1976
+ p(!1);
1977
+ }
1978
+ }
1979
+ return /* @__PURE__ */ e.jsxs(e.Fragment, { children: [
1980
+ /* @__PURE__ */ e.jsxs("div", { className: "card", children: [
1981
+ /* @__PURE__ */ e.jsx("h2", { children: "API 文档" }),
1982
+ /* @__PURE__ */ e.jsxs("div", { className: "muted", children: [
1983
+ "z-graph HTTP 控制面共 ",
1984
+ ge.length,
1985
+ " 个端点。点击端点展开详情并可交互测试。"
1986
+ ] })
1987
+ ] }),
1988
+ ge.map((l, C) => /* @__PURE__ */ e.jsxs("div", { className: "card api-endpoint-card", children: [
1989
+ /* @__PURE__ */ e.jsxs("div", { className: "api-endpoint-header", onClick: () => a(s === C ? null : C), children: [
1990
+ /* @__PURE__ */ e.jsx("span", { className: `api-method ${l.method.toLowerCase()}`, children: l.method }),
1991
+ /* @__PURE__ */ e.jsx("code", { className: "api-path", children: l.path }),
1992
+ /* @__PURE__ */ e.jsx("span", { className: "api-name", children: l.name }),
1993
+ /* @__PURE__ */ e.jsx("span", { style: { flex: 1 } }),
1994
+ /* @__PURE__ */ e.jsx("span", { className: "api-expand", children: s === C ? "▲" : "▼" })
1995
+ ] }),
1996
+ s === C && /* @__PURE__ */ e.jsxs("div", { className: "api-endpoint-detail", children: [
1997
+ /* @__PURE__ */ e.jsx("p", { children: l.desc }),
1998
+ l.params.length > 0 && /* @__PURE__ */ e.jsxs(e.Fragment, { children: [
1999
+ /* @__PURE__ */ e.jsx("h3", { children: "参数" }),
2000
+ /* @__PURE__ */ e.jsxs("table", { children: [
2001
+ /* @__PURE__ */ e.jsx("thead", { children: /* @__PURE__ */ e.jsxs("tr", { children: [
2002
+ /* @__PURE__ */ e.jsx("th", { children: "名称" }),
2003
+ /* @__PURE__ */ e.jsx("th", { children: "类型" }),
2004
+ /* @__PURE__ */ e.jsx("th", { children: "必填" }),
2005
+ /* @__PURE__ */ e.jsx("th", { children: "示例" })
2006
+ ] }) }),
2007
+ /* @__PURE__ */ e.jsx("tbody", { children: l.params.map((j, v) => /* @__PURE__ */ e.jsxs("tr", { children: [
2008
+ /* @__PURE__ */ e.jsx("td", { className: "mono", children: j.name }),
2009
+ /* @__PURE__ */ e.jsx("td", { children: j.type }),
2010
+ /* @__PURE__ */ e.jsx("td", { children: j.required ? /* @__PURE__ */ e.jsx("span", { className: "tag ok", children: "必填" }) : "可选" }),
2011
+ /* @__PURE__ */ e.jsx("td", { className: "mono", style: { fontSize: 11, color: "#94a3b8" }, children: j.example || "-" })
2012
+ ] }, v)) })
2013
+ ] })
2014
+ ] }),
2015
+ /* @__PURE__ */ e.jsx("h3", { children: "返回值" }),
2016
+ /* @__PURE__ */ e.jsx("pre", { style: { fontSize: 12, color: "#86efac" }, children: l.response }),
2017
+ /* @__PURE__ */ e.jsx("div", { style: { marginTop: 12 }, children: /* @__PURE__ */ e.jsx("button", { className: "primary", onClick: () => x(l), disabled: d, children: d ? "测试中…" : "交互测试" }) }),
2018
+ o && /* @__PURE__ */ e.jsxs("div", { style: { marginTop: 8 }, children: [
2019
+ /* @__PURE__ */ e.jsx("h3", { children: "响应" }),
2020
+ /* @__PURE__ */ e.jsx("pre", { className: "results", style: { fontSize: 11, maxHeight: 300 }, children: typeof o == "string" ? o : JSON.stringify(o, null, 2) })
2021
+ ] }),
2022
+ S && /* @__PURE__ */ e.jsx("div", { className: "error", style: { marginTop: 8 }, children: S })
2023
+ ] })
2024
+ ] }, C))
2025
+ ] });
2026
+ }
2027
+ function ye() {
2028
+ const s = typeof window < "u" && window.__Z_GRAPH_API__ || "/api";
2029
+ return s === "/api" ? window.location.host : s.replace(/^https?:\/\//, "");
2030
+ }
2031
+ async function fe(s, a = {}) {
2032
+ const r = await (await fetch(s, {
2033
+ headers: { Accept: "application/json", ...a.headers || {} },
2034
+ ...a
2035
+ })).text();
2036
+ if (!r) return null;
2037
+ try {
2038
+ return JSON.parse(r);
2039
+ } catch {
2040
+ return r;
2041
+ }
2042
+ }
2043
+ const { Title: rt, Paragraph: at } = Oe;
2044
+ function ot() {
2045
+ const s = ze(), [a, o] = N(null);
2046
+ $(() => {
2047
+ const d = localStorage.getItem("userInfo");
2048
+ if (d)
2049
+ try {
2050
+ o(JSON.parse(d));
2051
+ } catch {
2052
+ o({ name: d });
2053
+ }
2054
+ }, []);
2055
+ const r = lt.filter((d) => d.key !== "/z-graph/home");
2056
+ return /* @__PURE__ */ e.jsxs("div", { children: [
2057
+ /* @__PURE__ */ e.jsx(re, { style: { marginBottom: 16, background: "linear-gradient(135deg, #7c3aed 0%, #6d28d9 100%)", border: "none" }, children: /* @__PURE__ */ e.jsxs(oe, { direction: "vertical", size: 4, style: { color: "#fff" }, children: [
2058
+ /* @__PURE__ */ e.jsxs(rt, { level: 3, style: { color: "#fff", margin: 0 }, children: [
2059
+ "欢迎",
2060
+ a != null && a.name ? `,${a.name}` : ""
2061
+ ] }),
2062
+ /* @__PURE__ */ e.jsx(at, { style: { color: "rgba(255,255,255,0.85)", margin: 0 }, children: "z-graph 图存储控制台 管理台" }),
2063
+ (a == null ? void 0 : a.role) && /* @__PURE__ */ e.jsx(Me, { style: { marginTop: 8, background: "rgba(255,255,255,0.2)", color: "#fff", border: "none" }, children: a.role })
2064
+ ] }) }),
2065
+ /* @__PURE__ */ e.jsx(De, { gutter: [16, 16], children: r.map((d) => /* @__PURE__ */ e.jsx(He, { xs: 24, sm: 12, md: 12, lg: 8, children: /* @__PURE__ */ e.jsx(re, { hoverable: !0, onClick: () => s(d.key), style: { borderTop: "3px solid #7c3aed" }, children: /* @__PURE__ */ e.jsxs(oe, { align: "start", size: 12, children: [
2066
+ /* @__PURE__ */ e.jsx("div", { style: {
2067
+ width: 44,
2068
+ height: 44,
2069
+ borderRadius: 8,
2070
+ flexShrink: 0,
2071
+ background: "rgba(124,58,237,0.08)",
2072
+ color: "#7c3aed",
2073
+ display: "flex",
2074
+ alignItems: "center",
2075
+ justifyContent: "center",
2076
+ fontSize: 20
2077
+ }, children: d.icon }),
2078
+ /* @__PURE__ */ e.jsxs("div", { style: { minWidth: 0 }, children: [
2079
+ /* @__PURE__ */ e.jsx("div", { style: { fontSize: 15, fontWeight: 600, color: "#0f172a" }, children: d.label }),
2080
+ /* @__PURE__ */ e.jsx("div", { style: { fontSize: 12, color: "#94a3b8", marginTop: 2 }, children: d.key })
2081
+ ] })
2082
+ ] }) }) }, d.key)) })
2083
+ ] });
2084
+ }
2085
+ const J = { username: "admin", password: "123456", user: { name: "admin", role: "管理员" } };
2086
+ function pt() {
2087
+ const [s, a] = N(!1), o = async (r) => {
2088
+ a(!0);
2089
+ try {
2090
+ r.username === J.username && r.password === J.password ? (localStorage.setItem("token", "mock-" + J.username), localStorage.setItem("userInfo", JSON.stringify(J.user)), ie.success("登录成功(mock 模式,待 CTC/SSO 接入)"), window.location.href = "/z-graph/home") : ie.error("用户名或密码错误(默认 admin/123456)");
2091
+ } finally {
2092
+ a(!1);
2093
+ }
2094
+ };
2095
+ return /* @__PURE__ */ e.jsx("div", { style: {
2096
+ height: "100vh",
2097
+ display: "flex",
2098
+ justifyContent: "center",
2099
+ alignItems: "center",
2100
+ background: "linear-gradient(135deg, #eef2ff 0%, #f5f3ff 50%, #eff6ff 100%)"
2101
+ }, children: /* @__PURE__ */ e.jsxs(re, { style: { width: 400, boxShadow: "0 10px 40px rgba(124,58,237,0.12)" }, children: [
2102
+ /* @__PURE__ */ e.jsxs("div", { style: { textAlign: "center", marginBottom: 20 }, children: [
2103
+ /* @__PURE__ */ e.jsx("div", { style: {
2104
+ width: 48,
2105
+ height: 48,
2106
+ borderRadius: 12,
2107
+ margin: "0 auto 12px",
2108
+ background: "linear-gradient(135deg, #7c3aed, #6d28d9)",
2109
+ display: "flex",
2110
+ alignItems: "center",
2111
+ justifyContent: "center",
2112
+ color: "#fff",
2113
+ fontSize: 24,
2114
+ fontWeight: 700
2115
+ }, children: "z" }),
2116
+ /* @__PURE__ */ e.jsx("div", { style: { fontSize: 18, fontWeight: 600, color: "#0f172a" }, children: "z-graph 管理台" })
2117
+ ] }),
2118
+ /* @__PURE__ */ e.jsxs(Z, { onFinish: o, size: "large", children: [
2119
+ /* @__PURE__ */ e.jsx(Z.Item, { name: "username", rules: [{ required: !0, message: "请输入用户名" }], children: /* @__PURE__ */ e.jsx(le, { prefix: /* @__PURE__ */ e.jsx(Se, {}), placeholder: "用户名" }) }),
2120
+ /* @__PURE__ */ e.jsx(Z.Item, { name: "password", rules: [{ required: !0, message: "请输入密码" }], children: /* @__PURE__ */ e.jsx(le.Password, { prefix: /* @__PURE__ */ e.jsx(ve, {}), placeholder: "密码" }) }),
2121
+ /* @__PURE__ */ e.jsx(Z.Item, { style: { marginBottom: 8 }, children: /* @__PURE__ */ e.jsx(
2122
+ Pe,
2123
+ {
2124
+ type: "primary",
2125
+ htmlType: "submit",
2126
+ loading: s,
2127
+ block: !0,
2128
+ style: { background: "linear-gradient(135deg, #7c3aed 0%, #6d28d9 100%)", border: "none" },
2129
+ children: "登 录"
2130
+ }
2131
+ ) }),
2132
+ /* @__PURE__ */ e.jsxs("div", { style: { textAlign: "center", color: "#94a3b8", fontSize: 12 }, children: [
2133
+ "Mock 模式:用户名 ",
2134
+ /* @__PURE__ */ e.jsx("b", { children: J.username }),
2135
+ " / 密码 ",
2136
+ /* @__PURE__ */ e.jsx("b", { children: J.password })
2137
+ ] })
2138
+ ] })
2139
+ ] }) });
2140
+ }
2141
+ const ut = { title: "z-graph 图存储控制台", short: "z-graph" }, lt = [
2142
+ { key: "/z-graph/home", label: "首页", icon: /* @__PURE__ */ e.jsx(Te, {}) },
2143
+ { key: "/z-graph/dashboard", label: "总览", icon: /* @__PURE__ */ e.jsx(Ee, {}) },
2144
+ { key: "/z-graph/graph", label: "图视图", icon: /* @__PURE__ */ e.jsx(Ce, {}) },
2145
+ { key: "/z-graph/branches", label: "分支", icon: /* @__PURE__ */ e.jsx(Ae, {}) },
2146
+ { key: "/z-graph/commits", label: "提交历史", icon: /* @__PURE__ */ e.jsx(we, {}) },
2147
+ { key: "/z-graph/query", label: "Cypher 查询", icon: /* @__PURE__ */ e.jsx(ke, {}) },
2148
+ { key: "/z-graph/schema", label: "Schema", icon: /* @__PURE__ */ e.jsx(Ie, {}) },
2149
+ { key: "/z-graph/logs", label: "请求日志", icon: /* @__PURE__ */ e.jsx(Le, {}) },
2150
+ { key: "/z-graph/api-docs", label: "API 文档", icon: /* @__PURE__ */ e.jsx(Re, {}) }
2151
+ ], xt = [
2152
+ { path: "/z-graph/home", Component: ot },
2153
+ { path: "/z-graph/dashboard", Component: F(Ge) },
2154
+ { path: "/z-graph/graph", Component: F(qe) },
2155
+ { path: "/z-graph/branches", Component: F(Ue) },
2156
+ { path: "/z-graph/commits", Component: F(Be) },
2157
+ { path: "/z-graph/query", Component: F(Ve) },
2158
+ { path: "/z-graph/schema", Component: F(Ze) },
2159
+ { path: "/z-graph/logs", Component: F(st) },
2160
+ { path: "/z-graph/api-docs", Component: F(nt) },
2161
+ { path: "/z-graph/instance", Component: F(Ne) },
2162
+ // catchall 放最后 —— 否则 `:rest*` 会先于具体路由匹配,导致 /z-graph/query
2163
+ // 全部跳进 GraphApp 的内层 /schema 路由(URL 变成 /z-graph/query/schema)。
2164
+ { path: "/z-graph/:rest*", Component: be }
2165
+ ];
2166
+ export {
2167
+ ot as HomePage,
2168
+ pt as LoginPage,
2169
+ W as api,
2170
+ ut as appMeta,
2171
+ ft as configureGraph,
2172
+ lt as menuItems,
2173
+ xt as routes,
2174
+ We as useGraphServer,
2175
+ F as withGraphServer
2176
+ };