@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 CHANGED
@@ -1,317 +1,46 @@
1
1
  # @anvia/studio
2
2
 
3
- Studio UI and HTTP runtime for Anvia agents, pipelines, graphs, tools, MCPs, memory, status, and
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
- In this monorepo, the package is available through the workspace:
11
+ ## Quick start
67
12
 
68
- ```sh
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 client = new OpenAIClient({
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
- ## Browser sandbox views
31
+ ## What you get
281
32
 
282
- Sandbox registrations may include explicit noVNC views. Studio resolves the upstream only through the
283
- inspector's loopback-published port and exposes an authorized, same-origin WebSocket bridge. Studio's
284
- programmatic noVNC client renders the desktop directly, so the stock noVNC splash, toolbar, and password
285
- prompt are not part of the UI.
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
- ```ts
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
- Local mode requires a loopback connection and a same-origin browser request. Remotely reachable Studio
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
- When a registered agent emits a matching browser tool call, Playground replaces its Sessions sidebar
313
- with a larger resizable desktop panel. The embedded desktop remains view-only until the user explicitly
314
- takes control. That lease blocks Anvia browser tools, is renewed by the UI, and expires when abandoned.
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-BsVGFFv1.js.map
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