@anvia/studio 1.3.0 → 1.3.2
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/README.md +18 -289
- package/dist/ui/assets/{graphs-page-BsVGFFv1.js → graphs-page-2rKZQJAl.js} +2 -2
- package/dist/ui/assets/{graphs-page-BsVGFFv1.js.map → graphs-page-2rKZQJAl.js.map} +1 -1
- package/dist/ui/assets/index-DQo99AOj.js +67 -0
- package/dist/ui/assets/index-DQo99AOj.js.map +1 -0
- package/dist/ui/assets/{pipelines-page-Dqcq7IGL.js → pipelines-page-BeE6_VuK.js} +2 -2
- package/dist/ui/assets/{pipelines-page-Dqcq7IGL.js.map → pipelines-page-BeE6_VuK.js.map} +1 -1
- package/dist/ui/assets/{style-2z5K_w5A.js → style-C_1A5pxg.js} +2 -2
- package/dist/ui/assets/style-C_1A5pxg.js.map +1 -0
- package/dist/ui/index.html +1 -1
- package/package.json +12 -12
- package/dist/ui/assets/index-CM07xxYU.js +0 -67
- package/dist/ui/assets/index-CM07xxYU.js.map +0 -1
- package/dist/ui/assets/style-2z5K_w5A.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,317 +1,46 @@
|
|
|
1
1
|
# @anvia/studio
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
knowledge inspection.
|
|
5
|
-
|
|
6
|
-
Use this package to serve local agents and pipelines over HTTP, inspect sessions, traces, tools, MCPs, Memory, Status, and Knowledge in the browser UI, and exercise tool approval workflows during development.
|
|
7
|
-
|
|
8
|
-
## Agent teams
|
|
9
|
-
|
|
10
|
-
Register existing team definitions alongside agents and pipelines:
|
|
11
|
-
|
|
12
|
-
```ts
|
|
13
|
-
import { Agent, AgentTeam } from "@anvia/core/agent";
|
|
14
|
-
import { Studio } from "@anvia/studio";
|
|
15
|
-
|
|
16
|
-
const researcher = new Agent({ id: "researcher", model });
|
|
17
|
-
const team = new AgentTeam({ id: "research-team", model, members: [researcher] });
|
|
18
|
-
await new Studio([researcher, team]).serve({ port: 4021 });
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
Select a team from the playground's agent/team selector. A Studio configured with only teams
|
|
22
|
-
opens its first team automatically. Submit a task to the coordinator, send follow-ups while it
|
|
23
|
-
runs, or stop the whole team. The Members panel shows queued and active instances, including
|
|
24
|
-
recursive children; selecting an instance reveals its output, tool activity, run messages, and
|
|
25
|
-
usage. The Messages panel shows sender, recipient, and delivery status (the latest 500 messages).
|
|
26
|
-
Approvals and questions appear as independent cards labeled with the requesting member.
|
|
27
|
-
|
|
28
|
-
Team tasks are local to the current page. Stopping or leaving a task requests server cancellation
|
|
29
|
-
independently of closing its stream. Completed tasks remain visible until you choose **New task**. They do not appear in saved agent sessions.
|
|
30
|
-
|
|
31
|
-
`GET /teams` and `/config` expose team IDs, registered member definitions, and limits.
|
|
32
|
-
`GET /teams/:teamId` returns one definition. Duplicate team IDs are rejected; agent and team
|
|
33
|
-
IDs occupy separate namespaces. Members are not automatically registered as standalone agents.
|
|
34
|
-
|
|
35
|
-
| Endpoint | Request | Behavior |
|
|
36
|
-
| ------------------------------------------------------------- | ---------------------------------------- | ---------------------------------------- |
|
|
37
|
-
| `POST /teams/:teamId/runs` | `{ prompt }` or `{ messages }` | Start an attributed JSONL event stream |
|
|
38
|
-
| `POST /teams/:teamId/runs/:runId/steer` | `{ prompt }` or user-only `{ messages }` | Queue user input for the coordinator |
|
|
39
|
-
| `POST /teams/:teamId/runs/:runId/cancel` | `{}` | Cancel the entire team |
|
|
40
|
-
| `POST /teams/:teamId/runs/:runId/interactions/:interactionId` | `AgentInteractionResponse` | Resolve one pending approval or question |
|
|
41
|
-
|
|
42
|
-
The stream begins with `{ type: "team_run_started", teamId, runId }`; the same Studio control
|
|
43
|
-
ID is available in the `x-anvia-team-run-id` response header. Subsequent events follow
|
|
44
|
-
`AgentTeamEvent`, with core's separate `teamRunId`, instance IDs, parent IDs, and depth.
|
|
45
|
-
Internal agent continuation events are omitted; attributed `interaction` events contain everything
|
|
46
|
-
needed for the application's approval/question UI. Terminal failures emit an `error` event.
|
|
47
|
-
Team requests accept at most 1 MiB of JSON and 256 messages; oversized bodies or message counts return 413.
|
|
48
|
-
`StudioTeamRunRequest`, `StudioTeamRunEvent`, and `StudioTeamConfig` are exported for clients.
|
|
49
|
-
|
|
50
|
-
Runs and pending interactions are scoped to the team and Studio run ID. Invalid replies leave
|
|
51
|
-
the interaction pending; repeated replies are rejected. Several members may request approval
|
|
52
|
-
simultaneously, and only the HTTP application can answer them. Apply authentication/authorization
|
|
53
|
-
middleware to these routes when exposing Studio remotely, as with other Studio execution routes.
|
|
54
|
-
|
|
55
|
-
Disconnecting the stream or shutting down Studio cancels the team and its pending interactions.
|
|
56
|
-
Completed runs leave the live registry; later control requests return 404. Core's configured event
|
|
57
|
-
buffer limit bounds unread events. Team runs currently use ephemeral conversations and are not
|
|
58
|
-
stored in Studio's agent session history; this API does not provide reconnection or durable resume.
|
|
3
|
+
A local workspace for building and inspecting Anvia agents. Chat with the agents your application actually runs, follow tool calls and traces, and inspect their memory without creating a separate demo implementation.
|
|
59
4
|
|
|
60
5
|
## Installation
|
|
61
6
|
|
|
62
7
|
```sh
|
|
63
|
-
pnpm add @anvia/studio @anvia/core
|
|
8
|
+
pnpm add @anvia/studio @anvia/core @anvia/openai
|
|
64
9
|
```
|
|
65
10
|
|
|
66
|
-
|
|
11
|
+
## Quick start
|
|
67
12
|
|
|
68
|
-
|
|
69
|
-
pnpm --filter @anvia/studio build
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
## Usage
|
|
13
|
+
Set `OPENAI_API_KEY`, start the server, then open [localhost:4021/ui/playground](http://localhost:4021/ui/playground).
|
|
73
14
|
|
|
74
15
|
```ts
|
|
75
16
|
import { Agent } from "@anvia/core";
|
|
76
17
|
import { OpenAIClient } from "@anvia/openai";
|
|
77
18
|
import { Studio } from "@anvia/studio";
|
|
78
19
|
|
|
79
|
-
const
|
|
80
|
-
apiKey,
|
|
81
|
-
});
|
|
82
|
-
|
|
20
|
+
const openai = new OpenAIClient({ apiKey: process.env.OPENAI_API_KEY! });
|
|
83
21
|
const agent = new Agent({
|
|
84
22
|
id: "support",
|
|
85
|
-
model: client.completionModel({ modelId: "gpt-5", api: "responses" }),
|
|
86
23
|
name: "Support",
|
|
87
|
-
description: "Answers support questions.",
|
|
88
|
-
instructions: "Answer support questions clearly.",
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
await new Studio([agent]).serve({
|
|
92
|
-
port: 4021,
|
|
93
|
-
});
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
Then open:
|
|
97
|
-
|
|
98
|
-
```txt
|
|
99
|
-
http://localhost:4021/ui/playground
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
## Graceful shutdown
|
|
103
|
-
|
|
104
|
-
`serve()` handles both `SIGINT` and `SIGTERM`. Studio stops accepting work, aborts active Agent and
|
|
105
|
-
Pipeline runs, waits for their cancellation observers, and then runs `onShutdown`. Use that callback
|
|
106
|
-
to close observability clients or other caller-owned resources:
|
|
107
|
-
|
|
108
|
-
```ts
|
|
109
|
-
await new Studio([agent]).serve({
|
|
110
|
-
port: 4021,
|
|
111
|
-
shutdownTimeoutMs: 30_000,
|
|
112
|
-
onShutdown: async () => {
|
|
113
|
-
await Promise.all([lens.close(), langfuse.close(), otelSdk.shutdown()]);
|
|
114
|
-
},
|
|
115
|
-
});
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
`shutdown()` provides the same draining behavior for application-managed lifecycles. `close()`
|
|
119
|
-
remains synchronous for compatibility: it aborts active work but does not wait for cleanup.
|
|
120
|
-
|
|
121
|
-
## Multi-Provider Models
|
|
122
|
-
|
|
123
|
-
Studio can expose a shared model catalog and let each agent choose from registered providers:
|
|
124
|
-
|
|
125
|
-
```ts
|
|
126
|
-
import { Agent } from "@anvia/core";
|
|
127
|
-
import { AnthropicClient } from "@anvia/anthropic";
|
|
128
|
-
import { OpenAIClient } from "@anvia/openai";
|
|
129
|
-
import { Studio } from "@anvia/studio";
|
|
130
|
-
|
|
131
|
-
const openai = new OpenAIClient({ apiKey: process.env.OPENAI_API_KEY });
|
|
132
|
-
const anthropic = new AnthropicClient({ apiKey: process.env.ANTHROPIC_API_KEY });
|
|
133
|
-
|
|
134
|
-
const agent = new Agent({
|
|
135
|
-
id: "support",
|
|
136
24
|
model: openai.completionModel({ modelId: "gpt-5", api: "responses" }),
|
|
137
|
-
name: "Support",
|
|
138
25
|
instructions: "Answer support questions clearly.",
|
|
139
26
|
});
|
|
140
27
|
|
|
141
|
-
new Studio([agent]
|
|
142
|
-
models: {
|
|
143
|
-
providers: [
|
|
144
|
-
{
|
|
145
|
-
id: "openai",
|
|
146
|
-
name: "OpenAI",
|
|
147
|
-
defaultModelId: "gpt-5",
|
|
148
|
-
createCompletionModel: ({ modelId }) =>
|
|
149
|
-
openai.completionModel({ modelId, api: "responses" }),
|
|
150
|
-
listModels: () => openai.listModels(),
|
|
151
|
-
models: [
|
|
152
|
-
{
|
|
153
|
-
id: "gpt-5",
|
|
154
|
-
modalities: { input: ["text", "image", "document"], output: ["text"] },
|
|
155
|
-
},
|
|
156
|
-
],
|
|
157
|
-
},
|
|
158
|
-
{
|
|
159
|
-
id: "anthropic",
|
|
160
|
-
name: "Anthropic",
|
|
161
|
-
defaultModelId: "claude-sonnet-4-20250514",
|
|
162
|
-
createCompletionModel: ({ modelId }) => anthropic.completionModel({ modelId }),
|
|
163
|
-
},
|
|
164
|
-
],
|
|
165
|
-
agents: {
|
|
166
|
-
support: {
|
|
167
|
-
defaultModelRef: { providerId: "openai", modelId: "gpt-5" },
|
|
168
|
-
allowed: ["openai:*", { providerId: "anthropic", modelId: "claude-sonnet-4-20250514" }],
|
|
169
|
-
},
|
|
170
|
-
},
|
|
171
|
-
},
|
|
172
|
-
}).start();
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
The playground message composer shows the allowed models for the selected agent. It also renders
|
|
176
|
-
generic selectors for controls advertised by each completion model and restores explicit choices
|
|
177
|
-
from session metadata. “Default” omits the control, preserving Agent and provider defaults. API
|
|
178
|
-
callers can select a model and controls per run:
|
|
179
|
-
|
|
180
|
-
```json
|
|
181
|
-
{
|
|
182
|
-
"type": "messages",
|
|
183
|
-
"messages": [{ "role": "user", "content": "Summarize this ticket" }],
|
|
184
|
-
"model": {
|
|
185
|
-
"providerId": "anthropic",
|
|
186
|
-
"modelId": "claude-sonnet-4-20250514"
|
|
187
|
-
},
|
|
188
|
-
"controls": {
|
|
189
|
-
"reasoningEffort": "high"
|
|
190
|
-
},
|
|
191
|
-
"stream": true
|
|
192
|
-
}
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
## Browser UI
|
|
196
|
-
|
|
197
|
-
Studio exposes:
|
|
198
|
-
|
|
199
|
-
- Chat playground and persisted sessions
|
|
200
|
-
- Trace browser and session logs
|
|
201
|
-
- Realtime observability stream for session logs, pipeline logs, and completed traces
|
|
202
|
-
- Eval suite runner for registered `runEvalSuite` configurations
|
|
203
|
-
- Pipeline graph, logs, run history, and replay-from-history controls
|
|
204
|
-
- Knowledge-graph explorer with type filters, search, node details, and bounded neighborhood expansion
|
|
205
|
-
- Rich agent runtime details, direct tool invocation, static tool, dynamic tool, and MCP inspectors
|
|
206
|
-
- Memory explorer for users, conversations, messages, and transcript steps backed by the session store
|
|
207
|
-
- Status dashboard for storage adapters, record counts, and enabled capabilities
|
|
208
|
-
- Knowledge tabs for static context, dynamic context, dynamic tools, and retrieval log
|
|
209
|
-
|
|
210
|
-
Studio reads MCP provenance directly from `Agent.mcpServers`. Prefixes configured by an
|
|
211
|
-
`McpClient` remain visible in both the MCP inspector and direct tool runner, while the remote tool
|
|
212
|
-
name stays available on the typed MCP registration.
|
|
213
|
-
|
|
214
|
-
### Graph explorer
|
|
215
|
-
|
|
216
|
-
Register any graph that implements the provider-neutral `GraphExplorer` contract. Both
|
|
217
|
-
`@anvia/neo4j` and `@anvia/memgraph` graph registrations can be passed directly:
|
|
218
|
-
|
|
219
|
-
```ts
|
|
220
|
-
const studio = new Studio([agent], {
|
|
221
|
-
graphs: [
|
|
222
|
-
{
|
|
223
|
-
id: "support",
|
|
224
|
-
name: "Support knowledge graph",
|
|
225
|
-
graph,
|
|
226
|
-
},
|
|
227
|
-
],
|
|
228
|
-
});
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
Open `/graphs` and select nodes to inspect their public properties or expand their one-hop
|
|
232
|
-
neighborhood. Studio exposes only bounded overview and expansion requests; it does not accept raw
|
|
233
|
-
Cypher. Adapter explorer responses also omit stored embeddings and reserved `__anvia_*` properties.
|
|
234
|
-
|
|
235
|
-
## Session Storage
|
|
236
|
-
|
|
237
|
-
Studio uses an in-memory store by default. Sessions, traces, and pipeline run history are available while the process is running, but they do not create local files unless you pass an explicit SQLite store. If you omit the port, Studio uses `RUNNER_PORT` and then falls back to `4021`.
|
|
238
|
-
|
|
239
|
-
Pass `createSqliteSessionStore` to persist Studio data in SQLite:
|
|
240
|
-
|
|
241
|
-
```ts
|
|
242
|
-
import { Studio, createSqliteSessionStore } from "@anvia/studio";
|
|
243
|
-
|
|
244
|
-
new Studio([agent], {
|
|
245
|
-
stores: {
|
|
246
|
-
sessions: createSqliteSessionStore({ path: ".anvia/studio.sqlite" }),
|
|
247
|
-
},
|
|
248
|
-
}).start();
|
|
249
|
-
```
|
|
250
|
-
|
|
251
|
-
The store is caller-owned, so release it when the application shuts down. `close()` releases the SQLite handle and the store reopens lazily on the next call:
|
|
252
|
-
|
|
253
|
-
```ts
|
|
254
|
-
const store = createSqliteSessionStore({ path: ".anvia/studio.sqlite" });
|
|
255
|
-
|
|
256
|
-
await new Studio([agent], { stores: { sessions: store } }).serve({
|
|
257
|
-
onShutdown: () => store.close(),
|
|
258
|
-
});
|
|
259
|
-
```
|
|
260
|
-
|
|
261
|
-
Closing matters on Windows, where an open handle keeps the database file locked and blocks removing or moving its directory. If schema setup fails while opening the database, the store closes the handle before reporting the error, so deleting or recreating the file works as the error message suggests.
|
|
262
|
-
|
|
263
|
-
SQLite storage uses dedicated `anvia_studio_*` tables so it can share an application database without writing into product tables.
|
|
264
|
-
|
|
265
|
-
## Exports
|
|
266
|
-
|
|
267
|
-
- `Studio`
|
|
268
|
-
- `createInMemoryStudioStore`
|
|
269
|
-
- `createSqliteSessionStore`
|
|
270
|
-
- Studio session, trace, approval, pipeline, graph, memory, status, knowledge, tool, MCP, and runtime types
|
|
271
|
-
|
|
272
|
-
## Development
|
|
273
|
-
|
|
274
|
-
```sh
|
|
275
|
-
pnpm --filter @anvia/studio typecheck
|
|
276
|
-
pnpm --filter @anvia/studio test
|
|
277
|
-
pnpm --filter @anvia/studio build
|
|
28
|
+
await new Studio([agent]).serve({ port: 4021 });
|
|
278
29
|
```
|
|
279
30
|
|
|
280
|
-
##
|
|
31
|
+
## What you get
|
|
281
32
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
33
|
+
- Chat playground with model selection, approvals, and saved sessions.
|
|
34
|
+
- Agent teams with member activity and human interactions.
|
|
35
|
+
- Traces, tool and MCP inspectors, memory, and retrieval views.
|
|
36
|
+
- Pipeline execution, history, graphs, and evaluation runs.
|
|
37
|
+
- Knowledge-graph exploration and browser sandbox viewing with human takeover.
|
|
38
|
+
- Optional SQLite session storage and graceful server shutdown.
|
|
286
39
|
|
|
287
|
-
|
|
288
|
-
const studio = new Studio([agent], {
|
|
289
|
-
sandboxes: [
|
|
290
|
-
{
|
|
291
|
-
inspector: browser.inspector({ files: true, ports: true, processes: true }),
|
|
292
|
-
views: [
|
|
293
|
-
{
|
|
294
|
-
id: "desktop",
|
|
295
|
-
label: "Browser",
|
|
296
|
-
source: browser.desktop,
|
|
297
|
-
access: { mode: "local" },
|
|
298
|
-
authentication: { type: "password", password },
|
|
299
|
-
},
|
|
300
|
-
],
|
|
301
|
-
},
|
|
302
|
-
],
|
|
303
|
-
});
|
|
304
|
-
```
|
|
40
|
+
Session storage is in memory by default; configure SQLite to persist it across restarts. Studio is intended for local development. Add application authentication and authorization when exposing it remotely.
|
|
305
41
|
|
|
306
|
-
|
|
307
|
-
instances must instead provide `{ mode: "authorize", authorize }`; the application callback runs for
|
|
308
|
-
the viewer connection, WebSocket upgrade, and every takeover operation. Studio does not provide or infer
|
|
309
|
-
an application authentication system. View credentials are omitted from sandbox discovery metadata and
|
|
310
|
-
URLs and returned only by the authorized, non-cacheable viewer-connection endpoint.
|
|
42
|
+
## Learn more
|
|
311
43
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
It is a coordination boundary rather than a replacement for application authorization. The Sandboxes
|
|
316
|
-
page continues to expose the registered view as inspectable runtime metadata. Closing the Playground
|
|
317
|
-
panel restores Sessions without forgetting the current browser; use **Open browser** to show it again.
|
|
44
|
+
- [Usage guide](https://github.com/anvia-hq/anvia/blob/main/docs/packages/tool-studio.md)
|
|
45
|
+
- [Anvia](https://github.com/anvia-hq/anvia/blob/main/README.md)
|
|
46
|
+
- [Contributing](https://github.com/anvia-hq/anvia/blob/main/CONTRIBUTING.md)
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{i as e,r as t,s as n}from"./dist-xrWIzsxw.js";import{c as r,t as i}from"./button-jjWFI6nO.js";import{t as a}from"./request-CpxtfF0E.js";import{t as o}from"./ArrowClockwise.es-DwsUsBcf.js";import{t as s}from"./MagnifyingGlass.es-DP3xtOg0.js";import{n as c}from"./icon-BivhXy0Y.js";import{a as l,i as u,n as d,r as f,t as p}from"./select-Bt5d0Z3n.js";import{a as m,o as h,s as g,t as _}from"./studio-DeO7ueA2.js";import{a as v,i as y,n as b,o as x,r as S,s as C,t as w}from"./style-2z5K_w5A.js";import{a as T,t as E}from"./renderers-BakHuLSK.js";import{t as D}from"./input-C35vs5wD.js";var O=n(e(),1),k=new Map([[`bold`,O.createElement(O.Fragment,null,O.createElement(`path`,{d:`M228,128a12,12,0,0,1-12,12H140v76a12,12,0,0,1-24,0V140H40a12,12,0,0,1,0-24h76V40a12,12,0,0,1,24,0v76h76A12,12,0,0,1,228,128Z`}))],[`duotone`,O.createElement(O.Fragment,null,O.createElement(`path`,{d:`M216,56V200a16,16,0,0,1-16,16H56a16,16,0,0,1-16-16V56A16,16,0,0,1,56,40H200A16,16,0,0,1,216,56Z`,opacity:`0.2`}),O.createElement(`path`,{d:`M224,128a8,8,0,0,1-8,8H136v80a8,8,0,0,1-16,0V136H40a8,8,0,0,1,0-16h80V40a8,8,0,0,1,16,0v80h80A8,8,0,0,1,224,128Z`}))],[`fill`,O.createElement(O.Fragment,null,O.createElement(`path`,{d:`M208,32H48A16,16,0,0,0,32,48V208a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V48A16,16,0,0,0,208,32ZM184,136H136v48a8,8,0,0,1-16,0V136H72a8,8,0,0,1,0-16h48V72a8,8,0,0,1,16,0v48h48a8,8,0,0,1,0,16Z`}))],[`light`,O.createElement(O.Fragment,null,O.createElement(`path`,{d:`M222,128a6,6,0,0,1-6,6H134v82a6,6,0,0,1-12,0V134H40a6,6,0,0,1,0-12h82V40a6,6,0,0,1,12,0v82h82A6,6,0,0,1,222,128Z`}))],[`regular`,O.createElement(O.Fragment,null,O.createElement(`path`,{d:`M224,128a8,8,0,0,1-8,8H136v80a8,8,0,0,1-16,0V136H40a8,8,0,0,1,0-16h80V40a8,8,0,0,1,16,0v80h80A8,8,0,0,1,224,128Z`}))],[`thin`,O.createElement(O.Fragment,null,O.createElement(`path`,{d:`M220,128a4,4,0,0,1-4,4H132v84a4,4,0,0,1-8,0V132H40a4,4,0,0,1,0-8h84V40a4,4,0,0,1,8,0v84h84A4,4,0,0,1,220,128Z`}))]]),A=O.forwardRef((e,t)=>O.createElement(c,{ref:t,...e,weights:k}));A.displayName=`PlusIcon`;var j=A,M={nodes:[],relationships:[],truncated:{nodes:!1,relationships:!1}};function N(e){let{explore:t,searchText:n}=e,[r,i]=(0,O.useState)(e.initialResult??M),[a,o]=(0,O.useState)(),[s,c]=(0,O.useState)(e.initialQuery??``),[l,u]=(0,O.useState)(e.initialResult===void 0?`idle`:`ready`),[d,f]=(0,O.useState)(),p=(0,O.useRef)(void 0),m=(0,O.useRef)(0),h=(0,O.useRef)({mode:`overview`}),g=(0,O.useRef)({});(0,O.useEffect)(()=>()=>p.current?.abort(),[]);let _=(0,O.useCallback)(async e=>{p.current?.abort();let n=new AbortController;p.current=n;let r=++m.current,a=e.abortSignal,s=()=>{n.abort(),r===m.current&&(p.current=void 0,u(`ready`))};if(a?.aborted===!0){s(),f(void 0);return}a?.addEventListener(`abort`,s,{once:!0});let c={...e,abortSignal:n.signal};e.mode===`overview`&&(h.current=L(e)),u(`loading`),f(void 0);try{let a=await t(c);return n.signal.aborted||r!==m.current?void 0:(i(t=>e.mode===`expand`?P(t,a):a),e.mode===`overview`&&(g.current=R(e),o(e=>a.nodes.some(t=>t.id===e)?e:void 0)),u(`ready`),a)}catch(e){if(n.signal.aborted||r!==m.current)return;throw f(e),u(`error`),e}finally{a?.removeEventListener(`abort`,s),p.current===n&&(p.current=void 0)}},[t]),v=(0,O.useCallback)((e,t={})=>{if(e.length===0)throw TypeError(`Graph explorer node id must be non-empty.`);return _({...g.current,...t,mode:`expand`,nodeIds:[e]})},[_]),y=(0,O.useCallback)(()=>_(h.current),[_]),b=(0,O.useCallback)(()=>{m.current+=1,p.current?.abort(),p.current=void 0,f(void 0),u(`ready`)},[]),x=(0,O.useCallback)(()=>{m.current+=1,p.current?.abort(),p.current=void 0,h.current={mode:`overview`},g.current={},i(M),o(void 0),c(``),f(void 0),u(`idle`)},[]),S=(0,O.useMemo)(()=>new Map(r.nodes.map(e=>[e.id,e])),[r.nodes]),C=a===void 0?void 0:S.get(a),w=(0,O.useMemo)(()=>new Set(r.nodes.filter(e=>F(e,s,n)).map(e=>e.id)),[s,r.nodes,n]);return(0,O.useMemo)(()=>({nodes:r.nodes,nodeById:S,relationships:r.relationships,truncated:r.truncated,selectedNodeId:a,selectedNode:C,query:s,matchedNodeIds:w,status:l,error:d,explore:_,expandNode:v,refresh:y,selectNode:o,setQuery:c,stop:b,reset:x}),[d,v,_,w,S,s,y,x,r,C,a,l,b])}function P(e,t){let n=new Map(e.nodes.map(e=>[e.id,e])),r=new Map(e.relationships.map(e=>[e.id,e]));for(let e of t.nodes)n.set(e.id,e);for(let e of t.relationships)r.set(e.id,e);return{nodes:[...n.values()],relationships:[...r.values()],truncated:{nodes:e.truncated.nodes||t.truncated.nodes,relationships:e.truncated.relationships||t.truncated.relationships}}}function F(e,t,n){let r=t.trim().toLocaleLowerCase();return r.length===0?!0:n===void 0?I(e).some(e=>String(e??``).toLocaleLowerCase().includes(r)):n(e).toLocaleLowerCase().includes(r)}function I(e){let t=Object.entries(e.properties).flatMap(([e,t])=>[e,Array.isArray(t)?t.join(` `):String(t)]);return[e.type,e.key,...Object.values(e.identity),...t]}function L(e){let{abortSignal:t,...n}=e;return n}function R(e){return{nodeTypes:e.nodeTypes,relationships:e.relationships,includeProvenance:e.includeProvenance,maxNodes:e.maxNodes,maxRelationships:e.maxRelationships}}(0,O.createContext)(void 0),(0,O.createContext)(void 0),(0,O.createContext)(void 0);var z=(0,O.createContext)(void 0),B=(0,O.createContext)(void 0);function V({controller:e,children:t}){return(0,O.createElement)(z.Provider,{value:e},t)}function H({value:e,children:t}){return(0,O.createElement)(B.Provider,{value:e},t)}function U(){let e=(0,O.useContext)(z);if(e===void 0)throw Error(`Graph explorer primitives must be used inside GraphExplorerProvider.`);return e}function W(){let e=(0,O.useContext)(B);if(e===void 0)throw Error(`Graph explorer node primitives must be used inside GraphExplorerNodePrimitive.Root or GraphExplorerPrimitive.Nodes.`);return e}function G(){return(0,O.useContext)(B)}(0,O.createContext)(void 0),(0,O.createContext)(void 0),(0,O.createContext)(void 0),(0,O.createContext)(void 0),(0,O.createContext)(void 0),(0,O.createContext)(void 0),(0,O.createContext)(void 0),(0,O.createContext)(void 0),(0,O.createContext)(void 0),(0,O.createContext)(void 0),(0,O.createContext)(void 0),(0,O.createContext)(void 0),(0,O.createContext)(void 0);var K=t();function q(e){for(let t of[`name`,`title`,`label`]){let n=e.properties[t];if(typeof n==`string`&&n.trim().length>0)return n}return e.key===void 0?`${e.type} ${e.id}`:e.key}(0,O.forwardRef)(function(e,t){let n=U(),r=n.truncated.nodes||n.truncated.relationships;return T(`div`,{...e,"aria-busy":n.status===`loading`,"data-role":`graph-explorer`,"data-state":n.status,"data-truncated":r?`true`:`false`},t)}),(0,O.forwardRef)(function({onChange:e,...t},n){let r=U(),i=(0,O.useCallback)(t=>{e?.(t),t.defaultPrevented||r.setQuery(t.currentTarget.value)},[r,e]);return T(`input`,{...t,"aria-label":t[`aria-label`]??`Search graph`,"data-state":r.query.length===0?`empty`:`populated`,onChange:i,type:t.type??`search`,value:r.query},n)}),(0,O.forwardRef)(function(e,t){return T(`div`,{...e,"data-role":`graph-explorer-viewport`},t)}),(0,O.forwardRef)(function({children:e,keepMounted:t=!1,...n},r){let i=U(),a=i.nodes.length===0;return a&&!t?null:T(`div`,{...n,children:i.nodes.map(t=>{let n={node:t,selected:t.id===i.selectedNodeId,matched:i.matchedNodeIds.has(t.id)};return(0,K.jsx)(H,{value:n,children:typeof e==`function`?e(t,n):e??(0,K.jsx)(J,{children:(0,K.jsx)(Y,{})})},t.id)}),"data-state":a?`empty`:`populated`,role:n.role??`list`},r)}),(0,O.forwardRef)(function(e,t){let n=U();return n.nodes.length>0||n.status===`loading`?null:T(`div`,{...e,children:e.children??`No graph nodes.`},t)}),(0,O.forwardRef)(function({children:e,...t},n){let r=U(),i=typeof e==`function`?e(r):e??ee(r);return T(`div`,{...t,children:i,"data-state":r.status,role:t.role??`status`},n)}),(0,O.forwardRef)(function({onClick:e,...t},n){let r=U(),i=t.disabled??r.status===`loading`,a=Z(e,i,r.refresh);return T(`button`,{...t,children:t.children??`Refresh graph`,disabled:i,onClick:a,type:t.type??`button`},n)});var J=(0,O.forwardRef)(function({nodeId:e,...t},n){let r=U(),i=G(),a=e===void 0?i?.node:r.nodeById.get(e);if(a===void 0)throw Error(e===void 0?`GraphExplorerNodePrimitive.Root requires a nodeId outside GraphExplorerPrimitive.Nodes.`:`Graph explorer node ${e} is not loaded.`);let o={node:a,selected:a.id===r.selectedNodeId,matched:r.matchedNodeIds.has(a.id)};return(0,K.jsx)(H,{value:o,children:T(`div`,{...t,"data-match":o.matched?`matched`:`unmatched`,"data-node-id":o.node.id,"data-node-type":o.node.type,"data-state":o.selected?`selected`:`unselected`,role:t.role??(i===void 0?void 0:`listitem`)},n)})}),Y=(0,O.forwardRef)(function({onClick:e,...t},n){let r=U(),i=W(),a=(0,O.useCallback)(n=>{e?.(n),!n.defaultPrevented&&!t.disabled&&r.selectNode(i.node.id)},[r,e,t.disabled,i.node.id]);return T(`button`,{...t,children:t.children??q(i.node),onClick:a,type:t.type??`button`,"aria-pressed":i.selected},n)}),X=(0,O.forwardRef)(function({onClick:e,options:t,...n},r){let i=U(),a=W(),o=n.disabled??i.status===`loading`,s=Z(e,o,(0,O.useCallback)(()=>i.expandNode(a.node.id,t),[i,t,a.node.id]));return T(`button`,{...n,children:n.children??`Expand`,disabled:o,onClick:s,type:n.type??`button`},r)});function Z(e,t,n){return(0,O.useCallback)(r=>{e?.(r),!(r.defaultPrevented||t)&&n().catch(()=>void 0)},[n,t,e])}function ee(e){return e.status===`loading`?`Loading graph.`:e.status===`error`?`Graph exploration failed.`:`${e.nodes.length} nodes, ${e.relationships.length} relationships.`}var te={Root:J,Trigger:Y,Expand:X},ne={graphEntity:ie};function re(e,t){let n=e.nodes.filter(e=>t.has(e.id)),r=new Set(n.map(e=>e.id));return{nodes:ae(n),edges:e.relationships.filter(e=>r.has(e.from)&&r.has(e.to)).map(e=>oe(e))}}function ie(e){return(0,K.jsx)(te.Root,{nodeId:e.id,asChild:!0,children:(0,K.jsxs)(`article`,{className:[`relative w-[180px] rounded-xl border bg-card px-3 py-2.5 text-left shadow-sm transition`,e.selected?`border-foreground shadow-md`:`border-border/80`].join(` `),children:[(0,K.jsx)(y,{type:`target`,position:C.Top,className:`!size-1.5 !border-0 !bg-muted-foreground/50`}),(0,K.jsxs)(`div`,{className:`flex min-w-0 items-center gap-2.5`,children:[(0,K.jsx)(`span`,{className:[`size-2.5 shrink-0 rounded-full`,e.data.color].join(` `)}),(0,K.jsxs)(`div`,{className:`min-w-0`,children:[(0,K.jsx)(`div`,{className:`truncate text-sm font-semibold text-foreground`,children:e.data.label}),(0,K.jsx)(`div`,{className:`mt-0.5 truncate text-[10px] font-semibold uppercase tracking-[0.14em] text-muted-foreground`,children:e.data.type})]})]}),(0,K.jsx)(y,{type:`source`,position:C.Bottom,className:`!size-1.5 !border-0 !bg-muted-foreground/50`})]})})}function ae(e){return e.map((e,t)=>{let n=t*2.399963,r=180*Math.sqrt(t);return{id:e.id,type:`graphEntity`,position:{x:Math.cos(n)*r,y:Math.sin(n)*r},data:{label:q(e),type:e.type,color:se(e.type)}}})}function oe(e){return{id:e.id,source:e.from,target:e.to,label:e.type,type:`smoothstep`,markerEnd:{type:x.ArrowClosed,width:14,height:14},labelStyle:{fontSize:10,fontWeight:600},style:{strokeWidth:1.5}}}function se(e){let t=[`bg-emerald-500`,`bg-sky-500`,`bg-violet-500`,`bg-amber-500`,`bg-rose-500`,`bg-cyan-500`,`bg-fuchsia-500`],n=0;for(let t of e)n=n*31+t.charCodeAt(0)>>>0;return t[n%t.length]??`bg-muted-foreground`}function ce(e){let{onError:t,onStatus:n}=e,[i,o]=(0,O.useState)(e.graphs[0]?.id??``),[c,l]=(0,O.useState)([]),[u,d]=(0,O.useState)([]),f=e.graphs.find(e=>e.id===i)??e.graphs[0],p=N({explore:(0,O.useCallback)(async e=>{if(f===void 0)throw Error(`Graph explorer unavailable`);let{abortSignal:i,...o}=e;n(`Loading graph`);try{let e=await a(`/graphs/${encodeURIComponent(f.id)}/explore`,`Graph explorer`,i,`no-store`,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify(o)});return i?.aborted!==!0&&n(`Connected`),e}catch(e){throw i?.aborted!==!0&&(t(r(e)),n(`Graph error`)),e}},[f,t,n])}),y=(0,O.useMemo)(()=>({nodes:p.nodes,relationships:p.relationships,truncated:p.truncated}),[p.nodes,p.relationships,p.truncated]),x=(0,O.useMemo)(()=>re(y,p.matchedNodeIds),[p.matchedNodeIds,y]),C=p.status===`loading`,T=p.selectedNode,E=p.explore,k=p.reset,A=p.selectNode;if((0,O.useEffect)(()=>{if(f===void 0)return;let e=f.nodeTypes.map(e=>e.name),t=f.relationshipTypes.map(e=>e.name);o(f.id),l(e),d(t),k(),E({mode:`overview`,nodeTypes:e,relationships:t}).then(e=>{e!==void 0&&A(e.nodes[0]?.id)}).catch(()=>void 0)},[E,f,k,A]),!e.enabled||e.graphs.length===0)return(0,K.jsx)(m,{"aria-label":`Graphs`,children:(0,K.jsx)(_,{title:e.enabled?`No graphs`:`Graph explorer unavailable`,text:e.enabled?`Register a graph in Studio to inspect its nodes and relationships.`:`This Studio runtime does not expose a graph explorer.`})});function j(){E({mode:`overview`,nodeTypes:c,relationships:u}).then(e=>{e!==void 0&&A(e.nodes[0]?.id)}).catch(()=>void 0)}function M(){T!==void 0&&p.expandNode(T.id,{nodeTypes:c,relationships:u,direction:`both`,maxDepth:1}).catch(()=>void 0)}return(0,K.jsx)(V,{controller:p,children:(0,K.jsx)(m,{"aria-label":`Graphs`,children:(0,K.jsx)(`div`,{className:`grid min-h-0 min-w-0 pb-6 pr-6`,children:(0,K.jsxs)(g,{className:`grid grid-cols-[minmax(0,2fr)_340px] max-lg:grid-cols-1`,children:[(0,K.jsxs)(`div`,{className:`relative min-h-0 min-w-0 overflow-hidden border-r border-border/80 bg-card/25 max-lg:min-h-96 max-lg:border-b max-lg:border-r-0`,children:[(0,K.jsxs)(`div`,{className:`absolute left-4 top-4 z-10 flex max-w-[calc(100%-2rem)] items-center gap-2 rounded-xl border border-border/80 bg-background/90 p-2 shadow-sm backdrop-blur`,children:[(0,K.jsxs)(`div`,{className:`relative min-w-0 flex-1`,children:[(0,K.jsx)(s,{className:`pointer-events-none absolute left-2.5 top-1/2 size-4 -translate-y-1/2 text-muted-foreground`}),(0,K.jsx)(D,{"aria-label":`Search loaded nodes`,className:`w-64 pl-8`,placeholder:`Search loaded nodes`,value:p.query,onChange:e=>p.setQuery(e.currentTarget.value)})]}),(0,K.jsxs)(h,{children:[x.nodes.length,` nodes`]}),(0,K.jsxs)(h,{children:[x.edges.length,` relations`]})]}),C&&y.nodes.length===0?(0,K.jsx)(`div`,{className:`grid h-full min-h-96 place-items-center text-sm text-muted-foreground`,children:`Loading graph…`}):null,!C&&y.nodes.length===0?(0,K.jsx)(_,{className:`h-full border-0`,title:`No nodes found`,text:`Try another graph or broaden the selected node and relationship types.`}):null,y.nodes.length>0?(0,K.jsxs)(v,{nodes:x.nodes,edges:x.edges,className:`graph-explorer-flow`,colorMode:e.theme,nodeTypes:ne,fitView:!0,fitViewOptions:{padding:.2,maxZoom:1},minZoom:.2,maxZoom:1.7,proOptions:{hideAttribution:!0},defaultEdgeOptions:{focusable:!0},style:{"--xy-edge-stroke":`var(--muted-foreground)`,"--xy-edge-stroke-width":`1.4`,"--xy-edge-stroke-selected":`var(--foreground)`},onNodeClick:(e,t)=>p.selectNode(t.id),children:[(0,K.jsx)(w,{variant:b.Dots,gap:16,size:1.5,color:`var(--muted-foreground)`,className:`opacity-30`}),(0,K.jsx)(S,{showInteractive:!1})]},`${f?.id}:${y.nodes.length}:${y.relationships.length}`):null]}),(0,K.jsx)(le,{graph:f,graphs:e.graphs,loading:C,result:y,selectedGraphId:i,selectedNode:T,selectedNodeTypes:c,selectedRelationships:u,onExpand:M,onReload:j,onSelectGraph:o,onToggleNodeType:e=>l(t=>$(t,e)),onToggleRelationship:e=>d(t=>$(t,e))})]})})})})}function le(e){let t=e.result.truncated.nodes||e.result.truncated.relationships;return(0,K.jsxs)(`aside`,{className:`grid min-h-0 grid-rows-[auto_minmax(0,1fr)] overflow-hidden bg-background/70`,children:[(0,K.jsxs)(`header`,{className:`grid gap-3 border-b border-border/80 bg-card/35 p-4`,children:[(0,K.jsxs)(`div`,{className:`flex items-start justify-between gap-3`,children:[(0,K.jsxs)(`div`,{className:`min-w-0`,children:[(0,K.jsx)(`h1`,{className:`truncate text-base font-semibold`,children:e.graph?.name??`Graph explorer`}),(0,K.jsx)(`p`,{className:`mt-1 line-clamp-2 text-xs leading-5 text-muted-foreground`,children:e.graph?.description??`Explore a bounded view of nodes and relationships.`})]}),t?(0,K.jsx)(h,{title:`The configured result limit was reached`,children:`Limited`}):null]}),(0,K.jsxs)(p,{value:e.selectedGraphId,onValueChange:e.onSelectGraph,children:[(0,K.jsx)(u,{className:`h-8 w-full text-xs`,children:(0,K.jsx)(l,{placeholder:`Select graph`})}),(0,K.jsx)(d,{children:e.graphs.map(e=>(0,K.jsx)(f,{value:e.id,children:e.name??e.id},e.id))})]}),(0,K.jsxs)(`div`,{className:`flex gap-2`,children:[(0,K.jsxs)(i,{className:`flex-1`,variant:`secondary`,disabled:e.loading||e.selectedNodeTypes.length===0,onClick:e.onReload,children:[(0,K.jsx)(o,{}),` Refresh`]}),(0,K.jsxs)(i,{className:`flex-1`,disabled:e.loading||e.selectedNode===void 0,onClick:e.onExpand,children:[(0,K.jsx)(j,{}),` Expand`]})]})]}),(0,K.jsxs)(`div`,{className:`min-h-0 overflow-y-auto p-4`,children:[(0,K.jsx)(Q,{label:`Node types`,items:e.graph?.nodeTypes.map(e=>e.name)??[],selected:e.selectedNodeTypes,onToggle:e.onToggleNodeType}),(0,K.jsx)(Q,{label:`Relationships`,items:e.graph?.relationshipTypes.map(e=>e.name)??[],selected:e.selectedRelationships,onToggle:e.onToggleRelationship}),(0,K.jsx)(`div`,{className:`mt-5 border-t border-border/80 pt-4`,children:e.selectedNode===void 0?(0,K.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:`Select a node to inspect its properties.`}):(0,K.jsx)(ue,{node:e.selectedNode})})]})]})}function Q(e){return(0,K.jsxs)(`section`,{className:`mb-4`,children:[(0,K.jsx)(`h2`,{className:`mb-2 text-[10px] font-semibold uppercase tracking-[0.16em] text-muted-foreground`,children:e.label}),(0,K.jsx)(`div`,{className:`flex flex-wrap gap-1.5`,children:e.items.map(t=>{let n=e.selected.includes(t);return(0,K.jsx)(`button`,{type:`button`,"aria-pressed":n,className:[`rounded-full border px-2 py-1 text-xs transition-colors`,n?`border-foreground/30 bg-foreground text-background`:`border-border bg-card text-muted-foreground hover:text-foreground`].join(` `),onClick:()=>e.onToggle(t),children:t},t)})})]})}function ue(e){let t=JSON.stringify({identity:e.node.identity,properties:e.node.properties},null,2);return(0,K.jsxs)(`section`,{children:[(0,K.jsx)(h,{children:e.node.type}),(0,K.jsx)(`h2`,{className:`mt-2 break-words text-base font-semibold`,children:q(e.node)}),(0,K.jsxs)(`p`,{className:`mt-1 break-all text-xs text-muted-foreground`,children:[`ID `,e.node.id]}),(0,K.jsx)(`pre`,{className:`mt-4 overflow-x-auto rounded-xl border border-border/80 bg-card p-3 text-xs leading-5`,children:(0,K.jsx)(`code`,{children:(0,K.jsx)(E,{text:t})})})]})}function $(e,t){return e.includes(t)?e.filter(e=>e!==t):[...e,t]}export{ce as GraphsPage};
|
|
2
|
-
//# sourceMappingURL=graphs-page-
|
|
1
|
+
import{i as e,r as t,s as n}from"./dist-xrWIzsxw.js";import{c as r,t as i}from"./button-jjWFI6nO.js";import{t as a}from"./request-CpxtfF0E.js";import{t as o}from"./ArrowClockwise.es-DwsUsBcf.js";import{t as s}from"./MagnifyingGlass.es-DP3xtOg0.js";import{n as c}from"./icon-BivhXy0Y.js";import{a as l,i as u,n as d,r as f,t as p}from"./select-Bt5d0Z3n.js";import{a as m,o as h,s as g,t as _}from"./studio-DeO7ueA2.js";import{a as v,i as y,n as b,o as x,r as S,s as C,t as w}from"./style-C_1A5pxg.js";import{a as T,t as E}from"./renderers-BakHuLSK.js";import{t as D}from"./input-C35vs5wD.js";var O=n(e(),1),k=new Map([[`bold`,O.createElement(O.Fragment,null,O.createElement(`path`,{d:`M228,128a12,12,0,0,1-12,12H140v76a12,12,0,0,1-24,0V140H40a12,12,0,0,1,0-24h76V40a12,12,0,0,1,24,0v76h76A12,12,0,0,1,228,128Z`}))],[`duotone`,O.createElement(O.Fragment,null,O.createElement(`path`,{d:`M216,56V200a16,16,0,0,1-16,16H56a16,16,0,0,1-16-16V56A16,16,0,0,1,56,40H200A16,16,0,0,1,216,56Z`,opacity:`0.2`}),O.createElement(`path`,{d:`M224,128a8,8,0,0,1-8,8H136v80a8,8,0,0,1-16,0V136H40a8,8,0,0,1,0-16h80V40a8,8,0,0,1,16,0v80h80A8,8,0,0,1,224,128Z`}))],[`fill`,O.createElement(O.Fragment,null,O.createElement(`path`,{d:`M208,32H48A16,16,0,0,0,32,48V208a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V48A16,16,0,0,0,208,32ZM184,136H136v48a8,8,0,0,1-16,0V136H72a8,8,0,0,1,0-16h48V72a8,8,0,0,1,16,0v48h48a8,8,0,0,1,0,16Z`}))],[`light`,O.createElement(O.Fragment,null,O.createElement(`path`,{d:`M222,128a6,6,0,0,1-6,6H134v82a6,6,0,0,1-12,0V134H40a6,6,0,0,1,0-12h82V40a6,6,0,0,1,12,0v82h82A6,6,0,0,1,222,128Z`}))],[`regular`,O.createElement(O.Fragment,null,O.createElement(`path`,{d:`M224,128a8,8,0,0,1-8,8H136v80a8,8,0,0,1-16,0V136H40a8,8,0,0,1,0-16h80V40a8,8,0,0,1,16,0v80h80A8,8,0,0,1,224,128Z`}))],[`thin`,O.createElement(O.Fragment,null,O.createElement(`path`,{d:`M220,128a4,4,0,0,1-4,4H132v84a4,4,0,0,1-8,0V132H40a4,4,0,0,1,0-8h84V40a4,4,0,0,1,8,0v84h84A4,4,0,0,1,220,128Z`}))]]),A=O.forwardRef((e,t)=>O.createElement(c,{ref:t,...e,weights:k}));A.displayName=`PlusIcon`;var j=A,M={nodes:[],relationships:[],truncated:{nodes:!1,relationships:!1}};function N(e){let{explore:t,searchText:n}=e,[r,i]=(0,O.useState)(e.initialResult??M),[a,o]=(0,O.useState)(),[s,c]=(0,O.useState)(e.initialQuery??``),[l,u]=(0,O.useState)(e.initialResult===void 0?`idle`:`ready`),[d,f]=(0,O.useState)(),p=(0,O.useRef)(void 0),m=(0,O.useRef)(0),h=(0,O.useRef)({mode:`overview`}),g=(0,O.useRef)({});(0,O.useEffect)(()=>()=>p.current?.abort(),[]);let _=(0,O.useCallback)(async e=>{p.current?.abort();let n=new AbortController;p.current=n;let r=++m.current,a=e.abortSignal,s=()=>{n.abort(),r===m.current&&(p.current=void 0,u(`ready`))};if(a?.aborted===!0){s(),f(void 0);return}a?.addEventListener(`abort`,s,{once:!0});let c={...e,abortSignal:n.signal};e.mode===`overview`&&(h.current=L(e)),u(`loading`),f(void 0);try{let a=await t(c);return n.signal.aborted||r!==m.current?void 0:(i(t=>e.mode===`expand`?P(t,a):a),e.mode===`overview`&&(g.current=R(e),o(e=>a.nodes.some(t=>t.id===e)?e:void 0)),u(`ready`),a)}catch(e){if(n.signal.aborted||r!==m.current)return;throw f(e),u(`error`),e}finally{a?.removeEventListener(`abort`,s),p.current===n&&(p.current=void 0)}},[t]),v=(0,O.useCallback)((e,t={})=>{if(e.length===0)throw TypeError(`Graph explorer node id must be non-empty.`);return _({...g.current,...t,mode:`expand`,nodeIds:[e]})},[_]),y=(0,O.useCallback)(()=>_(h.current),[_]),b=(0,O.useCallback)(()=>{m.current+=1,p.current?.abort(),p.current=void 0,f(void 0),u(`ready`)},[]),x=(0,O.useCallback)(()=>{m.current+=1,p.current?.abort(),p.current=void 0,h.current={mode:`overview`},g.current={},i(M),o(void 0),c(``),f(void 0),u(`idle`)},[]),S=(0,O.useMemo)(()=>new Map(r.nodes.map(e=>[e.id,e])),[r.nodes]),C=a===void 0?void 0:S.get(a),w=(0,O.useMemo)(()=>new Set(r.nodes.filter(e=>F(e,s,n)).map(e=>e.id)),[s,r.nodes,n]);return(0,O.useMemo)(()=>({nodes:r.nodes,nodeById:S,relationships:r.relationships,truncated:r.truncated,selectedNodeId:a,selectedNode:C,query:s,matchedNodeIds:w,status:l,error:d,explore:_,expandNode:v,refresh:y,selectNode:o,setQuery:c,stop:b,reset:x}),[d,v,_,w,S,s,y,x,r,C,a,l,b])}function P(e,t){let n=new Map(e.nodes.map(e=>[e.id,e])),r=new Map(e.relationships.map(e=>[e.id,e]));for(let e of t.nodes)n.set(e.id,e);for(let e of t.relationships)r.set(e.id,e);return{nodes:[...n.values()],relationships:[...r.values()],truncated:{nodes:e.truncated.nodes||t.truncated.nodes,relationships:e.truncated.relationships||t.truncated.relationships}}}function F(e,t,n){let r=t.trim().toLocaleLowerCase();return r.length===0?!0:n===void 0?I(e).some(e=>String(e??``).toLocaleLowerCase().includes(r)):n(e).toLocaleLowerCase().includes(r)}function I(e){let t=Object.entries(e.properties).flatMap(([e,t])=>[e,Array.isArray(t)?t.join(` `):String(t)]);return[e.type,e.key,...Object.values(e.identity),...t]}function L(e){let{abortSignal:t,...n}=e;return n}function R(e){return{nodeTypes:e.nodeTypes,relationships:e.relationships,includeProvenance:e.includeProvenance,maxNodes:e.maxNodes,maxRelationships:e.maxRelationships}}(0,O.createContext)(void 0),(0,O.createContext)(void 0),(0,O.createContext)(void 0);var z=(0,O.createContext)(void 0),B=(0,O.createContext)(void 0);function V({controller:e,children:t}){return(0,O.createElement)(z.Provider,{value:e},t)}function H({value:e,children:t}){return(0,O.createElement)(B.Provider,{value:e},t)}function U(){let e=(0,O.useContext)(z);if(e===void 0)throw Error(`Graph explorer primitives must be used inside GraphExplorerProvider.`);return e}function W(){let e=(0,O.useContext)(B);if(e===void 0)throw Error(`Graph explorer node primitives must be used inside GraphExplorerNodePrimitive.Root or GraphExplorerPrimitive.Nodes.`);return e}function G(){return(0,O.useContext)(B)}(0,O.createContext)(void 0),(0,O.createContext)(void 0),(0,O.createContext)(void 0),(0,O.createContext)(void 0),(0,O.createContext)(void 0),(0,O.createContext)(void 0),(0,O.createContext)(void 0),(0,O.createContext)(void 0),(0,O.createContext)(void 0),(0,O.createContext)(void 0),(0,O.createContext)(void 0),(0,O.createContext)(void 0),(0,O.createContext)(void 0);var K=t();function q(e){for(let t of[`name`,`title`,`label`]){let n=e.properties[t];if(typeof n==`string`&&n.trim().length>0)return n}return e.key===void 0?`${e.type} ${e.id}`:e.key}(0,O.forwardRef)(function(e,t){let n=U(),r=n.truncated.nodes||n.truncated.relationships;return T(`div`,{...e,"aria-busy":n.status===`loading`,"data-role":`graph-explorer`,"data-state":n.status,"data-truncated":r?`true`:`false`},t)}),(0,O.forwardRef)(function({onChange:e,...t},n){let r=U(),i=(0,O.useCallback)(t=>{e?.(t),t.defaultPrevented||r.setQuery(t.currentTarget.value)},[r,e]);return T(`input`,{...t,"aria-label":t[`aria-label`]??`Search graph`,"data-state":r.query.length===0?`empty`:`populated`,onChange:i,type:t.type??`search`,value:r.query},n)}),(0,O.forwardRef)(function(e,t){return T(`div`,{...e,"data-role":`graph-explorer-viewport`},t)}),(0,O.forwardRef)(function({children:e,keepMounted:t=!1,...n},r){let i=U(),a=i.nodes.length===0;return a&&!t?null:T(`div`,{...n,children:i.nodes.map(t=>{let n={node:t,selected:t.id===i.selectedNodeId,matched:i.matchedNodeIds.has(t.id)};return(0,K.jsx)(H,{value:n,children:typeof e==`function`?e(t,n):e??(0,K.jsx)(J,{children:(0,K.jsx)(Y,{})})},t.id)}),"data-state":a?`empty`:`populated`,role:n.role??`list`},r)}),(0,O.forwardRef)(function(e,t){let n=U();return n.nodes.length>0||n.status===`loading`?null:T(`div`,{...e,children:e.children??`No graph nodes.`},t)}),(0,O.forwardRef)(function({children:e,...t},n){let r=U(),i=typeof e==`function`?e(r):e??ee(r);return T(`div`,{...t,children:i,"data-state":r.status,role:t.role??`status`},n)}),(0,O.forwardRef)(function({onClick:e,...t},n){let r=U(),i=t.disabled??r.status===`loading`,a=Z(e,i,r.refresh);return T(`button`,{...t,children:t.children??`Refresh graph`,disabled:i,onClick:a,type:t.type??`button`},n)});var J=(0,O.forwardRef)(function({nodeId:e,...t},n){let r=U(),i=G(),a=e===void 0?i?.node:r.nodeById.get(e);if(a===void 0)throw Error(e===void 0?`GraphExplorerNodePrimitive.Root requires a nodeId outside GraphExplorerPrimitive.Nodes.`:`Graph explorer node ${e} is not loaded.`);let o={node:a,selected:a.id===r.selectedNodeId,matched:r.matchedNodeIds.has(a.id)};return(0,K.jsx)(H,{value:o,children:T(`div`,{...t,"data-match":o.matched?`matched`:`unmatched`,"data-node-id":o.node.id,"data-node-type":o.node.type,"data-state":o.selected?`selected`:`unselected`,role:t.role??(i===void 0?void 0:`listitem`)},n)})}),Y=(0,O.forwardRef)(function({onClick:e,...t},n){let r=U(),i=W(),a=(0,O.useCallback)(n=>{e?.(n),!n.defaultPrevented&&!t.disabled&&r.selectNode(i.node.id)},[r,e,t.disabled,i.node.id]);return T(`button`,{...t,children:t.children??q(i.node),onClick:a,type:t.type??`button`,"aria-pressed":i.selected},n)}),X=(0,O.forwardRef)(function({onClick:e,options:t,...n},r){let i=U(),a=W(),o=n.disabled??i.status===`loading`,s=Z(e,o,(0,O.useCallback)(()=>i.expandNode(a.node.id,t),[i,t,a.node.id]));return T(`button`,{...n,children:n.children??`Expand`,disabled:o,onClick:s,type:n.type??`button`},r)});function Z(e,t,n){return(0,O.useCallback)(r=>{e?.(r),!(r.defaultPrevented||t)&&n().catch(()=>void 0)},[n,t,e])}function ee(e){return e.status===`loading`?`Loading graph.`:e.status===`error`?`Graph exploration failed.`:`${e.nodes.length} nodes, ${e.relationships.length} relationships.`}var te={Root:J,Trigger:Y,Expand:X},ne={graphEntity:ie};function re(e,t){let n=e.nodes.filter(e=>t.has(e.id)),r=new Set(n.map(e=>e.id));return{nodes:ae(n),edges:e.relationships.filter(e=>r.has(e.from)&&r.has(e.to)).map(e=>oe(e))}}function ie(e){return(0,K.jsx)(te.Root,{nodeId:e.id,asChild:!0,children:(0,K.jsxs)(`article`,{className:[`relative w-[180px] rounded-xl border bg-card px-3 py-2.5 text-left shadow-sm transition`,e.selected?`border-foreground shadow-md`:`border-border/80`].join(` `),children:[(0,K.jsx)(y,{type:`target`,position:C.Top,className:`!size-1.5 !border-0 !bg-muted-foreground/50`}),(0,K.jsxs)(`div`,{className:`flex min-w-0 items-center gap-2.5`,children:[(0,K.jsx)(`span`,{className:[`size-2.5 shrink-0 rounded-full`,e.data.color].join(` `)}),(0,K.jsxs)(`div`,{className:`min-w-0`,children:[(0,K.jsx)(`div`,{className:`truncate text-sm font-semibold text-foreground`,children:e.data.label}),(0,K.jsx)(`div`,{className:`mt-0.5 truncate text-[10px] font-semibold uppercase tracking-[0.14em] text-muted-foreground`,children:e.data.type})]})]}),(0,K.jsx)(y,{type:`source`,position:C.Bottom,className:`!size-1.5 !border-0 !bg-muted-foreground/50`})]})})}function ae(e){return e.map((e,t)=>{let n=t*2.399963,r=180*Math.sqrt(t);return{id:e.id,type:`graphEntity`,position:{x:Math.cos(n)*r,y:Math.sin(n)*r},data:{label:q(e),type:e.type,color:se(e.type)}}})}function oe(e){return{id:e.id,source:e.from,target:e.to,label:e.type,type:`smoothstep`,markerEnd:{type:x.ArrowClosed,width:14,height:14},labelStyle:{fontSize:10,fontWeight:600},style:{strokeWidth:1.5}}}function se(e){let t=[`bg-emerald-500`,`bg-sky-500`,`bg-violet-500`,`bg-amber-500`,`bg-rose-500`,`bg-cyan-500`,`bg-fuchsia-500`],n=0;for(let t of e)n=n*31+t.charCodeAt(0)>>>0;return t[n%t.length]??`bg-muted-foreground`}function ce(e){let{onError:t,onStatus:n}=e,[i,o]=(0,O.useState)(e.graphs[0]?.id??``),[c,l]=(0,O.useState)([]),[u,d]=(0,O.useState)([]),f=e.graphs.find(e=>e.id===i)??e.graphs[0],p=N({explore:(0,O.useCallback)(async e=>{if(f===void 0)throw Error(`Graph explorer unavailable`);let{abortSignal:i,...o}=e;n(`Loading graph`);try{let e=await a(`/graphs/${encodeURIComponent(f.id)}/explore`,`Graph explorer`,i,`no-store`,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify(o)});return i?.aborted!==!0&&n(`Connected`),e}catch(e){throw i?.aborted!==!0&&(t(r(e)),n(`Graph error`)),e}},[f,t,n])}),y=(0,O.useMemo)(()=>({nodes:p.nodes,relationships:p.relationships,truncated:p.truncated}),[p.nodes,p.relationships,p.truncated]),x=(0,O.useMemo)(()=>re(y,p.matchedNodeIds),[p.matchedNodeIds,y]),C=p.status===`loading`,T=p.selectedNode,E=p.explore,k=p.reset,A=p.selectNode;if((0,O.useEffect)(()=>{if(f===void 0)return;let e=f.nodeTypes.map(e=>e.name),t=f.relationshipTypes.map(e=>e.name);o(f.id),l(e),d(t),k(),E({mode:`overview`,nodeTypes:e,relationships:t}).then(e=>{e!==void 0&&A(e.nodes[0]?.id)}).catch(()=>void 0)},[E,f,k,A]),!e.enabled||e.graphs.length===0)return(0,K.jsx)(m,{"aria-label":`Graphs`,children:(0,K.jsx)(_,{title:e.enabled?`No graphs`:`Graph explorer unavailable`,text:e.enabled?`Register a graph in Studio to inspect its nodes and relationships.`:`This Studio runtime does not expose a graph explorer.`})});function j(){E({mode:`overview`,nodeTypes:c,relationships:u}).then(e=>{e!==void 0&&A(e.nodes[0]?.id)}).catch(()=>void 0)}function M(){T!==void 0&&p.expandNode(T.id,{nodeTypes:c,relationships:u,direction:`both`,maxDepth:1}).catch(()=>void 0)}return(0,K.jsx)(V,{controller:p,children:(0,K.jsx)(m,{"aria-label":`Graphs`,children:(0,K.jsx)(`div`,{className:`grid min-h-0 min-w-0 pb-6 pr-6`,children:(0,K.jsxs)(g,{className:`grid grid-cols-[minmax(0,2fr)_340px] max-lg:grid-cols-1`,children:[(0,K.jsxs)(`div`,{className:`relative min-h-0 min-w-0 overflow-hidden border-r border-border/80 bg-card/25 max-lg:min-h-96 max-lg:border-b max-lg:border-r-0`,children:[(0,K.jsxs)(`div`,{className:`absolute left-4 top-4 z-10 flex max-w-[calc(100%-2rem)] items-center gap-2 rounded-xl border border-border/80 bg-background/90 p-2 shadow-sm backdrop-blur`,children:[(0,K.jsxs)(`div`,{className:`relative min-w-0 flex-1`,children:[(0,K.jsx)(s,{className:`pointer-events-none absolute left-2.5 top-1/2 size-4 -translate-y-1/2 text-muted-foreground`}),(0,K.jsx)(D,{"aria-label":`Search loaded nodes`,className:`w-64 pl-8`,placeholder:`Search loaded nodes`,value:p.query,onChange:e=>p.setQuery(e.currentTarget.value)})]}),(0,K.jsxs)(h,{children:[x.nodes.length,` nodes`]}),(0,K.jsxs)(h,{children:[x.edges.length,` relations`]})]}),C&&y.nodes.length===0?(0,K.jsx)(`div`,{className:`grid h-full min-h-96 place-items-center text-sm text-muted-foreground`,children:`Loading graph…`}):null,!C&&y.nodes.length===0?(0,K.jsx)(_,{className:`h-full border-0`,title:`No nodes found`,text:`Try another graph or broaden the selected node and relationship types.`}):null,y.nodes.length>0?(0,K.jsxs)(v,{nodes:x.nodes,edges:x.edges,className:`graph-explorer-flow`,colorMode:e.theme,nodeTypes:ne,fitView:!0,fitViewOptions:{padding:.2,maxZoom:1},minZoom:.2,maxZoom:1.7,proOptions:{hideAttribution:!0},defaultEdgeOptions:{focusable:!0},style:{"--xy-edge-stroke":`var(--muted-foreground)`,"--xy-edge-stroke-width":`1.4`,"--xy-edge-stroke-selected":`var(--foreground)`},onNodeClick:(e,t)=>p.selectNode(t.id),children:[(0,K.jsx)(w,{variant:b.Dots,gap:16,size:1.5,color:`var(--muted-foreground)`,className:`opacity-30`}),(0,K.jsx)(S,{showInteractive:!1})]},`${f?.id}:${y.nodes.length}:${y.relationships.length}`):null]}),(0,K.jsx)(le,{graph:f,graphs:e.graphs,loading:C,result:y,selectedGraphId:i,selectedNode:T,selectedNodeTypes:c,selectedRelationships:u,onExpand:M,onReload:j,onSelectGraph:o,onToggleNodeType:e=>l(t=>$(t,e)),onToggleRelationship:e=>d(t=>$(t,e))})]})})})})}function le(e){let t=e.result.truncated.nodes||e.result.truncated.relationships;return(0,K.jsxs)(`aside`,{className:`grid min-h-0 grid-rows-[auto_minmax(0,1fr)] overflow-hidden bg-background/70`,children:[(0,K.jsxs)(`header`,{className:`grid gap-3 border-b border-border/80 bg-card/35 p-4`,children:[(0,K.jsxs)(`div`,{className:`flex items-start justify-between gap-3`,children:[(0,K.jsxs)(`div`,{className:`min-w-0`,children:[(0,K.jsx)(`h1`,{className:`truncate text-base font-semibold`,children:e.graph?.name??`Graph explorer`}),(0,K.jsx)(`p`,{className:`mt-1 line-clamp-2 text-xs leading-5 text-muted-foreground`,children:e.graph?.description??`Explore a bounded view of nodes and relationships.`})]}),t?(0,K.jsx)(h,{title:`The configured result limit was reached`,children:`Limited`}):null]}),(0,K.jsxs)(p,{value:e.selectedGraphId,onValueChange:e.onSelectGraph,children:[(0,K.jsx)(u,{className:`h-8 w-full text-xs`,children:(0,K.jsx)(l,{placeholder:`Select graph`})}),(0,K.jsx)(d,{children:e.graphs.map(e=>(0,K.jsx)(f,{value:e.id,children:e.name??e.id},e.id))})]}),(0,K.jsxs)(`div`,{className:`flex gap-2`,children:[(0,K.jsxs)(i,{className:`flex-1`,variant:`secondary`,disabled:e.loading||e.selectedNodeTypes.length===0,onClick:e.onReload,children:[(0,K.jsx)(o,{}),` Refresh`]}),(0,K.jsxs)(i,{className:`flex-1`,disabled:e.loading||e.selectedNode===void 0,onClick:e.onExpand,children:[(0,K.jsx)(j,{}),` Expand`]})]})]}),(0,K.jsxs)(`div`,{className:`min-h-0 overflow-y-auto p-4`,children:[(0,K.jsx)(Q,{label:`Node types`,items:e.graph?.nodeTypes.map(e=>e.name)??[],selected:e.selectedNodeTypes,onToggle:e.onToggleNodeType}),(0,K.jsx)(Q,{label:`Relationships`,items:e.graph?.relationshipTypes.map(e=>e.name)??[],selected:e.selectedRelationships,onToggle:e.onToggleRelationship}),(0,K.jsx)(`div`,{className:`mt-5 border-t border-border/80 pt-4`,children:e.selectedNode===void 0?(0,K.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:`Select a node to inspect its properties.`}):(0,K.jsx)(ue,{node:e.selectedNode})})]})]})}function Q(e){return(0,K.jsxs)(`section`,{className:`mb-4`,children:[(0,K.jsx)(`h2`,{className:`mb-2 text-[10px] font-semibold uppercase tracking-[0.16em] text-muted-foreground`,children:e.label}),(0,K.jsx)(`div`,{className:`flex flex-wrap gap-1.5`,children:e.items.map(t=>{let n=e.selected.includes(t);return(0,K.jsx)(`button`,{type:`button`,"aria-pressed":n,className:[`rounded-full border px-2 py-1 text-xs transition-colors`,n?`border-foreground/30 bg-foreground text-background`:`border-border bg-card text-muted-foreground hover:text-foreground`].join(` `),onClick:()=>e.onToggle(t),children:t},t)})})]})}function ue(e){let t=JSON.stringify({identity:e.node.identity,properties:e.node.properties},null,2);return(0,K.jsxs)(`section`,{children:[(0,K.jsx)(h,{children:e.node.type}),(0,K.jsx)(`h2`,{className:`mt-2 break-words text-base font-semibold`,children:q(e.node)}),(0,K.jsxs)(`p`,{className:`mt-1 break-all text-xs text-muted-foreground`,children:[`ID `,e.node.id]}),(0,K.jsx)(`pre`,{className:`mt-4 overflow-x-auto rounded-xl border border-border/80 bg-card p-3 text-xs leading-5`,children:(0,K.jsx)(`code`,{children:(0,K.jsx)(E,{text:t})})})]})}function $(e,t){return e.includes(t)?e.filter(e=>e!==t):[...e,t]}export{ce as GraphsPage};
|
|
2
|
+
//# sourceMappingURL=graphs-page-2rKZQJAl.js.map
|