@adonis-agora/durable-dashboard 0.2.1 → 0.2.3
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/CHANGELOG.md +31 -0
- package/dist/spa/assets/index-97QpkU1f.css +1 -0
- package/dist/spa/assets/index-CS0tc60i.js +16 -0
- package/dist/spa/assets/index-CS0tc60i.js.map +1 -0
- package/dist/spa/index.html +2 -2
- package/package.json +21 -19
- package/skills/durable-observability/SKILL.md +289 -0
- package/dist/spa/assets/index-CTsYsi6p.css +0 -1
- package/dist/spa/assets/index-CV232q_B.js +0 -67
- package/dist/spa/assets/index-CV232q_B.js.map +0 -1
package/dist/spa/index.html
CHANGED
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
rel="stylesheet"
|
|
11
11
|
/>
|
|
12
12
|
<title>durable · control plane</title>
|
|
13
|
-
<script type="module" crossorigin src="/__DURABLE_DASHBOARD__/assets/index-
|
|
14
|
-
<link rel="stylesheet" crossorigin href="/__DURABLE_DASHBOARD__/assets/index-
|
|
13
|
+
<script type="module" crossorigin src="/__DURABLE_DASHBOARD__/assets/index-CS0tc60i.js"></script>
|
|
14
|
+
<link rel="stylesheet" crossorigin href="/__DURABLE_DASHBOARD__/assets/index-97QpkU1f.css">
|
|
15
15
|
</head>
|
|
16
16
|
<body>
|
|
17
17
|
<div id="root"></div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adonis-agora/durable-dashboard",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "Control-plane console (runs, step timeline, workflow graph, retry/cancel/fix-and-replay) for @adonis-agora/durable — a React SPA served by a thin AdonisJS provider. Part of the Agora ecosystem.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"sideEffects": false,
|
|
14
14
|
"files": [
|
|
15
15
|
"dist/",
|
|
16
|
+
"skills",
|
|
16
17
|
"README.md",
|
|
17
18
|
"CHANGELOG.md"
|
|
18
19
|
],
|
|
@@ -22,28 +23,28 @@
|
|
|
22
23
|
},
|
|
23
24
|
"devDependencies": {
|
|
24
25
|
"@base-ui/react": "1.7.0",
|
|
25
|
-
"@
|
|
26
|
-
"@tanstack/
|
|
26
|
+
"@tailwindcss/postcss": "4.3.3",
|
|
27
|
+
"@tanstack/intent": "^0.3.7",
|
|
28
|
+
"@tanstack/react-query": "5.102.8",
|
|
29
|
+
"@tanstack/react-virtual": "3.14.10",
|
|
27
30
|
"@testing-library/dom": "10.4.1",
|
|
28
|
-
"@testing-library/react": "16.3.
|
|
29
|
-
"@types/node": "
|
|
30
|
-
"@types/react": "
|
|
31
|
-
"@types/react-dom": "
|
|
32
|
-
"@vitejs/plugin-react": "
|
|
33
|
-
"@xyflow/react": "12.11.
|
|
34
|
-
"autoprefixer": "10.5.4",
|
|
31
|
+
"@testing-library/react": "16.3.3",
|
|
32
|
+
"@types/node": "26.4.0",
|
|
33
|
+
"@types/react": "19.2.18",
|
|
34
|
+
"@types/react-dom": "19.2.5",
|
|
35
|
+
"@vitejs/plugin-react": "6.1.1",
|
|
36
|
+
"@xyflow/react": "12.11.5",
|
|
35
37
|
"class-variance-authority": "0.7.1",
|
|
36
38
|
"clsx": "2.1.1",
|
|
37
|
-
"happy-dom": "20.
|
|
38
|
-
"jsdom": "
|
|
39
|
-
"
|
|
40
|
-
"react": "
|
|
41
|
-
"react-dom": "18.3.1",
|
|
39
|
+
"happy-dom": "20.12.0",
|
|
40
|
+
"jsdom": "30.0.1",
|
|
41
|
+
"react": "19.2.8",
|
|
42
|
+
"react-dom": "19.2.8",
|
|
42
43
|
"tailwind-merge": "3.6.0",
|
|
43
|
-
"tailwindcss": "3.
|
|
44
|
-
"typescript": "
|
|
45
|
-
"vite": "
|
|
46
|
-
"vitest": "
|
|
44
|
+
"tailwindcss": "4.3.3",
|
|
45
|
+
"typescript": "7.0.2",
|
|
46
|
+
"vite": "8.2.2",
|
|
47
|
+
"vitest": "4.1.11"
|
|
47
48
|
},
|
|
48
49
|
"engines": {
|
|
49
50
|
"node": ">=20.6.0"
|
|
@@ -56,6 +57,7 @@
|
|
|
56
57
|
"dashboard",
|
|
57
58
|
"admin",
|
|
58
59
|
"console",
|
|
60
|
+
"tanstack-intent",
|
|
59
61
|
"agora"
|
|
60
62
|
],
|
|
61
63
|
"scripts": {
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: durable-observability
|
|
3
|
+
description: >-
|
|
4
|
+
Operate and observe @adonis-agora/durable runs: the embedded dashboard
|
|
5
|
+
(config/durable_dashboard.ts, enabled/path/authorize, dashboardAuth session
|
|
6
|
+
layer with session/login hooks, JSON API for listRuns/retry/retry-with-input/
|
|
7
|
+
cancel/redispatch/bulk, SSE live tail via EventSource), engine.subscribe
|
|
8
|
+
lifecycle events (run.started/completed/failed/suspended, step.*,
|
|
9
|
+
capability.unavailable), engine.use local-step interceptors, collectMetrics
|
|
10
|
+
Prometheus counters, OpenTelemetry tracing (attachDurableOtel,
|
|
11
|
+
otelTraceparent cross-process propagation), and the Telescope Workflows view
|
|
12
|
+
(durableTelescopeExtension). Use when mounting/securing the console,
|
|
13
|
+
scripting run actions, or wiring logs/metrics/traces.
|
|
14
|
+
license: MIT
|
|
15
|
+
metadata:
|
|
16
|
+
type: core
|
|
17
|
+
library: "@adonis-agora/durable-dashboard"
|
|
18
|
+
library_version: "0.2.1"
|
|
19
|
+
framework: adonisjs
|
|
20
|
+
sources:
|
|
21
|
+
- 'DavideCarvalho/adonis-durable:docs/observability/dashboard.mdx'
|
|
22
|
+
- 'DavideCarvalho/adonis-durable:docs/observability/dashboard-auth.mdx'
|
|
23
|
+
- 'DavideCarvalho/adonis-durable:docs/observability/events-and-interceptors.mdx'
|
|
24
|
+
- 'DavideCarvalho/adonis-durable:docs/observability/otel.mdx'
|
|
25
|
+
- 'DavideCarvalho/adonis-durable:docs/observability/telescope.mdx'
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
# Observing & operating runs
|
|
29
|
+
|
|
30
|
+
The dashboard mounts an operations console into your AdonisJS app: a React SPA
|
|
31
|
+
plus the JSON API it runs on, reading straight from the state store. The SPA is
|
|
32
|
+
bundled from `@adonis-agora/durable-dashboard`; its provider and config ship in
|
|
33
|
+
the main package.
|
|
34
|
+
|
|
35
|
+
## Setup
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
node ace configure @adonis-agora/durable
|
|
39
|
+
# registers @adonis-agora/durable/dashboard_provider + publishes config/durable_dashboard.ts
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
```ts title="config/durable_dashboard.ts"
|
|
43
|
+
import { defineConfig } from '@adonis-agora/durable/dashboard'
|
|
44
|
+
|
|
45
|
+
export default defineConfig({
|
|
46
|
+
// enabled: true, // default true; false registers no routes at all
|
|
47
|
+
path: '/durable',
|
|
48
|
+
authorize: async (ctx) => {
|
|
49
|
+
await ctx.auth.check()
|
|
50
|
+
return ctx.auth.user?.isAdmin === true // denied requests get 403
|
|
51
|
+
},
|
|
52
|
+
})
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The default `authorize` is open outside production and requires a bearer token
|
|
56
|
+
equal to `DURABLE_DASHBOARD_TOKEN` in production (**fails closed if unset**).
|
|
57
|
+
The console mutates runs — always front it with a real guard in production.
|
|
58
|
+
|
|
59
|
+
## Core patterns
|
|
60
|
+
|
|
61
|
+
### Pattern 1 — operator sign-in with `dashboardAuth`
|
|
62
|
+
|
|
63
|
+
Opt-in session layer, additive to `authorize` (both must pass). `secret` plus at
|
|
64
|
+
least one of `session` / `login`; a mis-wired gate throws at boot:
|
|
65
|
+
|
|
66
|
+
```ts title="config/durable_dashboard.ts"
|
|
67
|
+
import { defineConfig } from '@adonis-agora/durable/dashboard'
|
|
68
|
+
import env from '#start/env'
|
|
69
|
+
import User from '#models/user'
|
|
70
|
+
|
|
71
|
+
export default defineConfig({
|
|
72
|
+
path: '/durable',
|
|
73
|
+
dashboardAuth: {
|
|
74
|
+
secret: env.get('DURABLE_DASHBOARD_SECRET'), // HMAC-SHA256 key, 32+ bytes
|
|
75
|
+
ttl: '8h',
|
|
76
|
+
login: async (username, password) => {
|
|
77
|
+
const user = await User.verifyCredentials(username, password).catch(() => null)
|
|
78
|
+
if (!user || !user.isAdmin) return null // null denies
|
|
79
|
+
return { id: String(user.id), name: user.fullName, roles: ['admin'] }
|
|
80
|
+
},
|
|
81
|
+
// or Mode A — mint a session from your app's own cookie via
|
|
82
|
+
// POST <path>/session with credentials: 'include' and redirect: 'manual'.
|
|
83
|
+
},
|
|
84
|
+
})
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Adds `GET/POST <path>/login`, `POST <path>/session`, `GET <path>/logout`. Pages
|
|
88
|
+
redirect 302 to login without a valid cookie; API requests get
|
|
89
|
+
`401 { error, auth: { modes } }`. Source: `docs/observability/dashboard-auth.mdx`.
|
|
90
|
+
|
|
91
|
+
### Pattern 2 — fix-and-replay and bulk actions
|
|
92
|
+
|
|
93
|
+
A run that failed on bad input cannot be retried into success — retry replays
|
|
94
|
+
the same input. Fix-and-replay starts a NEW run (`<originalRunId>~retry~<uuid>`,
|
|
95
|
+
clean history, original left intact as the record):
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
const res = await fetch(`/durable/api/runs/${runId}/retry-with-input`, {
|
|
99
|
+
method: 'POST',
|
|
100
|
+
headers: { 'content-type': 'application/json', ...headers },
|
|
101
|
+
body: JSON.stringify({ input: { orderId: 'ord_42', amount: 1999 } }),
|
|
102
|
+
})
|
|
103
|
+
const { result } = await res.json()
|
|
104
|
+
result.runId // 'run-abc~retry~1f4c9e02'
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Apply an action to everything a filter matches — same filters as `GET /api/runs`
|
|
108
|
+
(`status`, `workflow`, `tag`, `namespace`, repeatable `attr=key:op:value`):
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
curl -X POST '/durable/api/bulk/retry?status=failed&workflow=charge-order&namespace=eu-west'
|
|
112
|
+
curl -X POST '/durable/api/bulk/cancel?status=suspended&tag=stuck&compensate=true'
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Bulk acts on at most **500 runs per call**, no paging — compare `matched` to 500
|
|
116
|
+
and narrow + repeat. A compensating cancel answers with the PRE-cancel status;
|
|
117
|
+
watch the SSE stream until it reaches `cancelled`.
|
|
118
|
+
Source: `docs/observability/dashboard.mdx`.
|
|
119
|
+
|
|
120
|
+
### Pattern 3 — lifecycle events → logs, metrics, traces
|
|
121
|
+
|
|
122
|
+
Everything observable is one event stream; subscribe directly or use the
|
|
123
|
+
shipped bridges:
|
|
124
|
+
|
|
125
|
+
```ts title="start/durable.ts"
|
|
126
|
+
import router from '@adonisjs/core/services/router'
|
|
127
|
+
import engine from '@adonis-agora/durable/services/main'
|
|
128
|
+
import { attachDurableOtel } from '@adonis-agora/durable/otel'
|
|
129
|
+
import { collectMetrics } from '@adonis-agora/durable'
|
|
130
|
+
|
|
131
|
+
engine.subscribe((event) => {
|
|
132
|
+
if (event.type !== 'run.failed') return
|
|
133
|
+
logger.error({ runId: event.runId, workflow: event.workflow }, 'durable run failed')
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
const metrics = collectMetrics(engine)
|
|
137
|
+
router.get('/metrics/durable', ({ response }) => {
|
|
138
|
+
response.header('content-type', 'text/plain; version=0.0.4')
|
|
139
|
+
return metrics.prometheus()
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
const detach = attachDurableOtel(engine) // one root span per run, one per step
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Event types: `run.started|completed|failed|suspended`, `step.started|
|
|
146
|
+
completed|failed` (each retry emits its own `step.failed`), plus
|
|
147
|
+
`capability.unavailable` / `protocol.incompatible` carrying full diagnostics.
|
|
148
|
+
For distributed tracing across workers, stamp tasks with a provider:
|
|
149
|
+
|
|
150
|
+
```ts title="config/durable.ts"
|
|
151
|
+
import { defineConfig } from '@adonis-agora/durable'
|
|
152
|
+
import { otelTraceparent } from '@adonis-agora/durable/otel'
|
|
153
|
+
|
|
154
|
+
export default defineConfig({
|
|
155
|
+
traceparent: () => otelTraceparent(), // W3C context continues on the worker
|
|
156
|
+
})
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Live-tail one run over SSE:
|
|
160
|
+
`new EventSource('/durable/api/runs/${runId}/stream')`.
|
|
161
|
+
Source: `docs/observability/events-and-interceptors.mdx`,
|
|
162
|
+
`docs/observability/otel.mdx`, `docs/observability/dashboard.mdx`.
|
|
163
|
+
|
|
164
|
+
### Pattern 4 — golden signals in Telescope
|
|
165
|
+
|
|
166
|
+
Register the extension once and workflows appear next to requests/queries/jobs:
|
|
167
|
+
|
|
168
|
+
```ts title="config/telescope.ts"
|
|
169
|
+
import { durableTelescopeExtension } from '@adonis-agora/durable/telescope'
|
|
170
|
+
|
|
171
|
+
export default defineConfig({
|
|
172
|
+
extensions: [durableTelescopeExtension({ runHref: '/durable/runs/{runId}' })],
|
|
173
|
+
})
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Rollups (success rate, throughput, duration percentiles, top failures) come from
|
|
177
|
+
captured entries bounded by Telescope's prune window; current-state gauges
|
|
178
|
+
(dead/suspended/running/pending NOW) read live from the store via `listRuns`.
|
|
179
|
+
Per-run actions stay in the durable dashboard. Source:
|
|
180
|
+
`docs/observability/telescope.mdx`.
|
|
181
|
+
|
|
182
|
+
## Common mistakes
|
|
183
|
+
|
|
184
|
+
### HIGH trusting an "empty" dashboard config in production
|
|
185
|
+
|
|
186
|
+
Wrong:
|
|
187
|
+
|
|
188
|
+
```ts
|
|
189
|
+
export default defineConfig({}) // prod, DURABLE_DASHBOARD_TOKEN unset
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Correct:
|
|
193
|
+
|
|
194
|
+
```ts
|
|
195
|
+
export default defineConfig({
|
|
196
|
+
authorize: async (ctx) => {
|
|
197
|
+
await ctx.auth.check()
|
|
198
|
+
return ctx.auth.user?.isAdmin === true
|
|
199
|
+
},
|
|
200
|
+
})
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Mechanism: the default authorize fails CLOSED in production when the token env
|
|
204
|
+
var is unset, so every console route 403s and the console looks broken rather
|
|
205
|
+
than leaking — either set the token deliberately or override authorize.
|
|
206
|
+
Source: `docs/observability/dashboard.mdx` ("Authorization").
|
|
207
|
+
|
|
208
|
+
### HIGH making interceptor outputs depend on time, randomness or external state
|
|
209
|
+
|
|
210
|
+
Wrong:
|
|
211
|
+
|
|
212
|
+
```ts
|
|
213
|
+
engine.use(async (invocation, next) => next().then((r) => ({ ...r, at: Date.now() })))
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Correct:
|
|
217
|
+
|
|
218
|
+
```ts
|
|
219
|
+
engine.use(async (invocation, next) => {
|
|
220
|
+
const result = await next()
|
|
221
|
+
logger.info({ workflow: invocation.workflow, step: invocation.stepName })
|
|
222
|
+
return result
|
|
223
|
+
})
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Mechanism: interceptors wrap the REAL execution of local steps inside the
|
|
227
|
+
deterministic execution path — a returned value that differs between runs makes
|
|
228
|
+
replay disagree with the recorded checkpoint. Read, time, log, classify; don't
|
|
229
|
+
mutate results nondeterministically. Source:
|
|
230
|
+
`docs/observability/events-and-interceptors.mdx` (warning callout).
|
|
231
|
+
|
|
232
|
+
### MEDIUM expecting interceptors to fire on replay or dispatched steps
|
|
233
|
+
|
|
234
|
+
Wrong:
|
|
235
|
+
|
|
236
|
+
```ts
|
|
237
|
+
engine.use(enforceInvariant) // "runs on every turn" — it does not
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Correct: enforce invariants inside the workflow body or step handlers; register
|
|
241
|
+
interceptors on the worker's engine to observe dispatched steps there.
|
|
242
|
+
|
|
243
|
+
Mechanism: a replayed step returns its recorded output without executing (so
|
|
244
|
+
timings here are real-execution timings), and dispatched steps execute on
|
|
245
|
+
workers in another process — there is no local body to wrap. Source:
|
|
246
|
+
`docs/observability/events-and-interceptors.mdx`.
|
|
247
|
+
|
|
248
|
+
### MEDIUM treating a compensating cancel's response as the final state
|
|
249
|
+
|
|
250
|
+
Wrong:
|
|
251
|
+
|
|
252
|
+
```ts
|
|
253
|
+
const { run } = await cancelRun(runId, { compensate: true })
|
|
254
|
+
if (run.status !== 'cancelled') throw new Error('cancel failed') // premature
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
Correct:
|
|
258
|
+
|
|
259
|
+
```ts
|
|
260
|
+
await cancelRun(runId, { compensate: true })
|
|
261
|
+
// poll getRun / subscribe to the stream until status === 'cancelled'
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
Mechanism: cancel-with-compensate answers immediately with the pre-cancel status
|
|
265
|
+
while the saga undo replays in the background; only a plain cancel is terminal in
|
|
266
|
+
the response itself. Source: `docs/observability/dashboard.mdx` (info callout).
|
|
267
|
+
|
|
268
|
+
### MEDIUM scraping one pod's collectMetrics counters as fleet totals
|
|
269
|
+
|
|
270
|
+
Wrong:
|
|
271
|
+
|
|
272
|
+
```yaml
|
|
273
|
+
# one Prometheus target pointed at a single web pod
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
Correct:
|
|
277
|
+
|
|
278
|
+
```yaml
|
|
279
|
+
# scrape /metrics/durable on EVERY engine-bearing pod; aggregate in PromQL
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Mechanism: counters are per process and reset on restart, so one target both
|
|
283
|
+
undercounts the fleet and zeroes on every deploy of that pod. Source:
|
|
284
|
+
`docs/observability/events-and-interceptors.mdx` ("Counters are per process").
|
|
285
|
+
|
|
286
|
+
See also: `durable-reliability` — what the events mean operationally; bulk
|
|
287
|
+
cancel + compensate semantics.
|
|
288
|
+
See also: `durable-cluster` — which verbs proxy over the wire on store-less pods
|
|
289
|
+
(`retry-with-input` / `continue` answer 404 there).
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
.react-flow{direction:ltr;--xy-edge-stroke-default: #b1b1b7;--xy-edge-stroke-width-default: 1;--xy-edge-stroke-selected-default: #555;--xy-connectionline-stroke-default: #b1b1b7;--xy-connectionline-stroke-width-default: 1;--xy-attribution-background-color-default: rgba(255, 255, 255, .5);--xy-minimap-background-color-default: #fff;--xy-minimap-mask-background-color-default: rgba(240, 240, 240, .6);--xy-minimap-mask-stroke-color-default: transparent;--xy-minimap-mask-stroke-width-default: 1;--xy-minimap-node-background-color-default: #e2e2e2;--xy-minimap-node-stroke-color-default: transparent;--xy-minimap-node-stroke-width-default: 2;--xy-background-color-default: transparent;--xy-background-pattern-dots-color-default: #91919a;--xy-background-pattern-lines-color-default: #eee;--xy-background-pattern-cross-color-default: #e2e2e2;background-color:var(--xy-background-color, var(--xy-background-color-default));--xy-node-color-default: inherit;--xy-node-border-default: 1px solid #1a192b;--xy-node-background-color-default: #fff;--xy-node-group-background-color-default: rgba(240, 240, 240, .25);--xy-node-boxshadow-hover-default: 0 1px 4px 1px rgba(0, 0, 0, .08);--xy-node-boxshadow-selected-default: 0 0 0 .5px #1a192b;--xy-node-border-radius-default: 3px;--xy-handle-background-color-default: #1a192b;--xy-handle-border-color-default: #fff;--xy-selection-background-color-default: rgba(0, 89, 220, .08);--xy-selection-border-default: 1px dotted rgba(0, 89, 220, .8);--xy-controls-button-background-color-default: #fefefe;--xy-controls-button-background-color-hover-default: #f4f4f4;--xy-controls-button-color-default: inherit;--xy-controls-button-color-hover-default: inherit;--xy-controls-button-border-color-default: #eee;--xy-controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, .08);--xy-edge-label-background-color-default: #ffffff;--xy-edge-label-color-default: inherit;--xy-resize-background-color-default: #3367d9}.react-flow.dark{--xy-edge-stroke-default: #3e3e3e;--xy-edge-stroke-width-default: 1;--xy-edge-stroke-selected-default: #727272;--xy-connectionline-stroke-default: #b1b1b7;--xy-connectionline-stroke-width-default: 1;--xy-attribution-background-color-default: rgba(150, 150, 150, .25);--xy-minimap-background-color-default: #141414;--xy-minimap-mask-background-color-default: rgba(60, 60, 60, .6);--xy-minimap-mask-stroke-color-default: transparent;--xy-minimap-mask-stroke-width-default: 1;--xy-minimap-node-background-color-default: #2b2b2b;--xy-minimap-node-stroke-color-default: transparent;--xy-minimap-node-stroke-width-default: 2;--xy-background-color-default: #141414;--xy-background-pattern-dots-color-default: #555;--xy-background-pattern-lines-color-default: #333;--xy-background-pattern-cross-color-default: #333;--xy-node-color-default: #f8f8f8;--xy-node-border-default: 1px solid #3c3c3c;--xy-node-background-color-default: #1e1e1e;--xy-node-group-background-color-default: rgba(240, 240, 240, .25);--xy-node-boxshadow-hover-default: 0 1px 4px 1px rgba(255, 255, 255, .08);--xy-node-boxshadow-selected-default: 0 0 0 .5px #999;--xy-handle-background-color-default: #bebebe;--xy-handle-border-color-default: #1e1e1e;--xy-selection-background-color-default: rgba(200, 200, 220, .08);--xy-selection-border-default: 1px dotted rgba(200, 200, 220, .8);--xy-controls-button-background-color-default: #2b2b2b;--xy-controls-button-background-color-hover-default: #3e3e3e;--xy-controls-button-color-default: #f8f8f8;--xy-controls-button-color-hover-default: #fff;--xy-controls-button-border-color-default: #5b5b5b;--xy-controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, .08);--xy-edge-label-background-color-default: #141414;--xy-edge-label-color-default: #f8f8f8}.react-flow__background{background-color:var(--xy-background-color-props, var(--xy-background-color, var(--xy-background-color-default)));pointer-events:none;z-index:-1}.react-flow__container{position:absolute;width:100%;height:100%;top:0;left:0}.react-flow__pane{z-index:1;touch-action:none}.react-flow__pane.draggable{cursor:grab}.react-flow__pane.dragging{cursor:grabbing}.react-flow__pane.selection{cursor:pointer}.react-flow__viewport{transform-origin:0 0;z-index:2;pointer-events:none}.react-flow__renderer{z-index:4}.react-flow__selection{z-index:6}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible{outline:none}.react-flow__edge-path{stroke:var(--xy-edge-stroke, var(--xy-edge-stroke-default));stroke-width:var(--xy-edge-stroke-width, var(--xy-edge-stroke-width-default));fill:none}.react-flow__connection-path{stroke:var(--xy-connectionline-stroke, var(--xy-connectionline-stroke-default));stroke-width:var(--xy-connectionline-stroke-width, var(--xy-connectionline-stroke-width-default));fill:none}.react-flow .react-flow__edges{position:absolute}.react-flow .react-flow__edges svg{overflow:visible;position:absolute;pointer-events:none}.react-flow__edge{pointer-events:visibleStroke}.react-flow__edge.selectable{cursor:pointer}.react-flow__edge.animated path{stroke-dasharray:5;animation:dashdraw .5s linear infinite}.react-flow__edge.animated path.react-flow__edge-interaction{stroke-dasharray:none;animation:none}.react-flow__edge.inactive{pointer-events:none}.react-flow__edge.selected,.react-flow__edge:focus,.react-flow__edge:focus-visible{outline:none}.react-flow__edge.selected .react-flow__edge-path,.react-flow__edge.selectable:focus .react-flow__edge-path,.react-flow__edge.selectable:focus-visible .react-flow__edge-path{stroke:var(--xy-edge-stroke-selected, var(--xy-edge-stroke-selected-default))}.react-flow__edge-textwrapper{pointer-events:all}.react-flow__edge .react-flow__edge-text{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__arrowhead polyline{stroke:var(--xy-edge-stroke, var(--xy-edge-stroke-default))}.react-flow__arrowhead polyline.arrowclosed{fill:var(--xy-edge-stroke, var(--xy-edge-stroke-default))}.react-flow__connection{pointer-events:none}.react-flow__connection .animated{stroke-dasharray:5;animation:dashdraw .5s linear infinite}svg.react-flow__connectionline{z-index:1001;overflow:visible;position:absolute}.react-flow__nodes{pointer-events:none;transform-origin:0 0}.react-flow__node{position:absolute;-webkit-user-select:none;-moz-user-select:none;user-select:none;pointer-events:all;transform-origin:0 0;box-sizing:border-box;cursor:default}.react-flow__node.selectable{cursor:pointer}.react-flow__node.draggable{cursor:grab;pointer-events:all}.react-flow__node.draggable.dragging{cursor:grabbing}.react-flow__nodesselection{z-index:3;transform-origin:left top;pointer-events:none}.react-flow__nodesselection-rect{position:absolute;pointer-events:all;cursor:grab}.react-flow__handle{position:absolute;pointer-events:none;min-width:5px;min-height:5px;width:6px;height:6px;background-color:var(--xy-handle-background-color, var(--xy-handle-background-color-default));border:1px solid var(--xy-handle-border-color, var(--xy-handle-border-color-default));border-radius:100%}.react-flow__handle.connectingfrom{pointer-events:all}.react-flow__handle.connectionindicator{pointer-events:all;cursor:crosshair}.react-flow__handle-bottom{top:auto;left:50%;bottom:0;transform:translate(-50%,50%)}.react-flow__handle-top{top:0;left:50%;transform:translate(-50%,-50%)}.react-flow__handle-left{top:50%;left:0;transform:translate(-50%,-50%)}.react-flow__handle-right{top:50%;right:0;transform:translate(50%,-50%)}.react-flow__edgeupdater{cursor:move;pointer-events:all}.react-flow__pane.selection .react-flow__panel{pointer-events:none}.react-flow__panel{position:absolute;z-index:5;margin:15px}.react-flow__panel.top{top:0}.react-flow__panel.bottom{bottom:0}.react-flow__panel.top.center,.react-flow__panel.bottom.center{left:50%;transform:translate(-15px) translate(-50%)}.react-flow__panel.left{left:0}.react-flow__panel.right{right:0}.react-flow__panel.left.center,.react-flow__panel.right.center{top:50%;transform:translateY(-15px) translateY(-50%)}.react-flow__attribution{font-size:10px;background:var(--xy-attribution-background-color, var(--xy-attribution-background-color-default));padding:2px 3px;margin:0}.react-flow__attribution a{text-decoration:none;color:#999}@keyframes dashdraw{0%{stroke-dashoffset:10}}.react-flow__edgelabel-renderer{position:absolute;width:100%;height:100%;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;left:0;top:0}.react-flow__viewport-portal{position:absolute;width:100%;height:100%;left:0;top:0;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__minimap{background:var( --xy-minimap-background-color-props, var(--xy-minimap-background-color, var(--xy-minimap-background-color-default)) )}.react-flow__minimap-svg{display:block}.react-flow__minimap-mask{fill:var( --xy-minimap-mask-background-color-props, var(--xy-minimap-mask-background-color, var(--xy-minimap-mask-background-color-default)) );stroke:var( --xy-minimap-mask-stroke-color-props, var(--xy-minimap-mask-stroke-color, var(--xy-minimap-mask-stroke-color-default)) );stroke-width:var( --xy-minimap-mask-stroke-width-props, var(--xy-minimap-mask-stroke-width, var(--xy-minimap-mask-stroke-width-default)) )}.react-flow__minimap-node{fill:var( --xy-minimap-node-background-color-props, var(--xy-minimap-node-background-color, var(--xy-minimap-node-background-color-default)) );stroke:var( --xy-minimap-node-stroke-color-props, var(--xy-minimap-node-stroke-color, var(--xy-minimap-node-stroke-color-default)) );stroke-width:var( --xy-minimap-node-stroke-width-props, var(--xy-minimap-node-stroke-width, var(--xy-minimap-node-stroke-width-default)) )}.react-flow__background-pattern.dots{fill:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-dots-color-default)) )}.react-flow__background-pattern.lines{stroke:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-lines-color-default)) )}.react-flow__background-pattern.cross{stroke:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-cross-color-default)) )}.react-flow__controls{display:flex;flex-direction:column;box-shadow:var(--xy-controls-box-shadow, var(--xy-controls-box-shadow-default))}.react-flow__controls.horizontal{flex-direction:row}.react-flow__controls-button{display:flex;justify-content:center;align-items:center;height:26px;width:26px;padding:4px;border:none;background:var(--xy-controls-button-background-color, var(--xy-controls-button-background-color-default));border-bottom:1px solid var( --xy-controls-button-border-color-props, var(--xy-controls-button-border-color, var(--xy-controls-button-border-color-default)) );color:var( --xy-controls-button-color-props, var(--xy-controls-button-color, var(--xy-controls-button-color-default)) );cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__controls-button svg{width:100%;max-width:12px;max-height:12px;fill:currentColor}.react-flow__edge.updating .react-flow__edge-path{stroke:#777}.react-flow__edge-text{font-size:10px}.react-flow__node.selectable:focus,.react-flow__node.selectable:focus-visible{outline:none}.react-flow__node-input,.react-flow__node-default,.react-flow__node-output,.react-flow__node-group{padding:10px;border-radius:var(--xy-node-border-radius, var(--xy-node-border-radius-default));width:150px;font-size:12px;color:var(--xy-node-color, var(--xy-node-color-default));text-align:center;border:var(--xy-node-border, var(--xy-node-border-default));background-color:var(--xy-node-background-color, var(--xy-node-background-color-default))}.react-flow__node-input.selectable:hover,.react-flow__node-default.selectable:hover,.react-flow__node-output.selectable:hover,.react-flow__node-group.selectable:hover{box-shadow:var(--xy-node-boxshadow-hover, var(--xy-node-boxshadow-hover-default))}.react-flow__node-input.selectable.selected,.react-flow__node-input.selectable:focus,.react-flow__node-input.selectable:focus-visible,.react-flow__node-default.selectable.selected,.react-flow__node-default.selectable:focus,.react-flow__node-default.selectable:focus-visible,.react-flow__node-output.selectable.selected,.react-flow__node-output.selectable:focus,.react-flow__node-output.selectable:focus-visible,.react-flow__node-group.selectable.selected,.react-flow__node-group.selectable:focus,.react-flow__node-group.selectable:focus-visible{box-shadow:var(--xy-node-boxshadow-selected, var(--xy-node-boxshadow-selected-default))}.react-flow__node-group{background-color:var(--xy-node-group-background-color, var(--xy-node-group-background-color-default))}.react-flow__nodesselection-rect,.react-flow__selection{background:var(--xy-selection-background-color, var(--xy-selection-background-color-default));border:var(--xy-selection-border, var(--xy-selection-border-default))}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible,.react-flow__selection:focus,.react-flow__selection:focus-visible{outline:none}.react-flow__controls-button:hover{background:var( --xy-controls-button-background-color-hover-props, var(--xy-controls-button-background-color-hover, var(--xy-controls-button-background-color-hover-default)) );color:var( --xy-controls-button-color-hover-props, var(--xy-controls-button-color-hover, var(--xy-controls-button-color-hover-default)) )}.react-flow__controls-button:disabled{pointer-events:none}.react-flow__controls-button:disabled svg{fill-opacity:.4}.react-flow__controls-button:last-child{border-bottom:none}.react-flow__controls.horizontal .react-flow__controls-button{border-bottom:none;border-right:1px solid var( --xy-controls-button-border-color-props, var(--xy-controls-button-border-color, var(--xy-controls-button-border-color-default)) )}.react-flow__controls.horizontal .react-flow__controls-button:last-child{border-right:none}.react-flow__resize-control{position:absolute}.react-flow__resize-control.left,.react-flow__resize-control.right{cursor:ew-resize}.react-flow__resize-control.top,.react-flow__resize-control.bottom{cursor:ns-resize}.react-flow__resize-control.top.left,.react-flow__resize-control.bottom.right{cursor:nwse-resize}.react-flow__resize-control.bottom.left,.react-flow__resize-control.top.right{cursor:nesw-resize}.react-flow__resize-control.handle{width:5px;height:5px;border:1px solid #fff;border-radius:1px;background-color:var(--xy-resize-background-color, var(--xy-resize-background-color-default));translate:-50% -50%}.react-flow__resize-control.handle.left{left:0;top:50%}.react-flow__resize-control.handle.right{left:100%;top:50%}.react-flow__resize-control.handle.top{left:50%;top:0}.react-flow__resize-control.handle.bottom{left:50%;top:100%}.react-flow__resize-control.handle.top.left,.react-flow__resize-control.handle.bottom.left{left:0}.react-flow__resize-control.handle.top.right,.react-flow__resize-control.handle.bottom.right{left:100%}.react-flow__resize-control.line{border-color:var(--xy-resize-background-color, var(--xy-resize-background-color-default));border-width:0;border-style:solid}.react-flow__resize-control.line.left,.react-flow__resize-control.line.right{width:1px;transform:translate(-50%);top:0;height:100%}.react-flow__resize-control.line.left{left:0;border-left-width:1px}.react-flow__resize-control.line.right{left:100%;border-right-width:1px}.react-flow__resize-control.line.top,.react-flow__resize-control.line.bottom{height:1px;transform:translateY(-50%);left:0;width:100%}.react-flow__resize-control.line.top{top:0;border-top-width:1px}.react-flow__resize-control.line.bottom{border-bottom-width:1px;top:100%}.react-flow__edge-textbg{fill:var(--xy-edge-label-background-color, var(--xy-edge-label-background-color-default))}.react-flow__edge-text{fill:var(--xy-edge-label-color, var(--xy-edge-label-color-default))}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:JetBrains Mono,ui-monospace,SFMono-Regular,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.container{width:100%}@media (min-width: 640px){.container{max-width:640px}}@media (min-width: 768px){.container{max-width:768px}}@media (min-width: 1024px){.container{max-width:1024px}}@media (min-width: 1280px){.container{max-width:1280px}}@media (min-width: 1536px){.container{max-width:1536px}}.visible{visibility:visible}.collapse{visibility:collapse}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-0{top:0;right:0;bottom:0;left:0}.inset-x-0{left:0;right:0}.inset-x-6{left:1.5rem;right:1.5rem}.inset-y-0{top:0;bottom:0}.bottom-6{bottom:1.5rem}.left-0{left:0}.left-1\/2{left:50%}.right-0{right:0}.top-0{top:0}.top-1\/2{top:50%}.top-full{top:100%}.z-10{z-index:10}.z-20{z-index:20}.z-30{z-index:30}.z-40{z-index:40}.z-50{z-index:50}.mx-auto{margin-left:auto;margin-right:auto}.my-1{margin-top:.25rem;margin-bottom:.25rem}.mb-1{margin-bottom:.25rem}.mb-1\.5{margin-bottom:.375rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.ml-\[10px\]{margin-left:10px}.ml-\[18px\]{margin-left:18px}.ml-auto{margin-left:auto}.mr-1{margin-right:.25rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-1\.5{margin-top:.375rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-1\.5{height:.375rem}.h-10{height:2.5rem}.h-2{height:.5rem}.h-2\.5{height:.625rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-64{height:16rem}.h-7{height:1.75rem}.h-full{height:100%}.h-px{height:1px}.max-h-48{max-height:12rem}.max-h-64{max-height:16rem}.max-h-80{max-height:20rem}.min-h-0{min-height:0px}.w-1\.5{width:.375rem}.w-10{width:2.5rem}.w-16{width:4rem}.w-32{width:8rem}.w-4{width:1rem}.w-48{width:12rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-7{width:1.75rem}.w-72{width:18rem}.w-\[208px\]{width:208px}.w-\[300px\]{width:300px}.w-\[380px\]{width:380px}.w-\[3px\]{width:3px}.w-\[min\(560px\,92vw\)\]{width:min(560px,92vw)}.w-full{width:100%}.min-w-0{min-width:0px}.max-w-3xl{max-width:48rem}.max-w-\[120px\]{max-width:120px}.max-w-\[180px\]{max-width:180px}.max-w-\[280px\]{max-width:280px}.max-w-\[90\%\]{max-width:90%}.max-w-full{max-width:100%}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.grow{flex-grow:1}.-translate-x-1\/2{--tw-translate-x: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-y-1\/2{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-rotate-90{--tw-rotate: -90deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.cursor-default{cursor:default}.cursor-pointer{cursor:pointer}.cursor-row-resize{cursor:row-resize}.resize-y{resize:vertical}.resize{resize:both}.grid-cols-\[130px_1fr\]{grid-template-columns:130px 1fr}.grid-cols-\[150px_1fr\]{grid-template-columns:150px 1fr}.grid-cols-\[minmax\(300px\,360px\)_1fr\]{grid-template-columns:minmax(300px,360px) 1fr}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.flex-nowrap{flex-wrap:nowrap}.place-items-center{place-items:center}.items-start{align-items:flex-start}.items-center{align-items:center}.items-baseline{align-items:baseline}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-2\.5{gap:.625rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-x-3{-moz-column-gap:.75rem;column-gap:.75rem}.gap-y-0\.5{row-gap:.125rem}.space-y-0\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.125rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.125rem * var(--tw-space-y-reverse))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse: 0;border-top-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px * var(--tw-divide-y-reverse))}.divide-line-soft>:not([hidden])~:not([hidden]){--tw-divide-opacity: 1;border-color:color-mix(in srgb,var(--line-soft) calc(var(--tw-divide-opacity, 1) * 100%),transparent)}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-line{white-space:pre-line}.break-words{overflow-wrap:break-word}.rounded{border-radius:.25rem}.rounded-\[2px\]{border-radius:2px}.rounded-\[3px\]{border-radius:3px}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-xl{border-radius:.75rem}.\!border-0{border-width:0px!important}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-l{border-left-width:1px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-amber-500\/20{border-color:#f59e0b33}.border-amber-500\/25{border-color:#f59e0b40}.border-amber-500\/30{border-color:#f59e0b4d}.border-amber-500\/40{border-color:#f59e0b66}.border-amber-500\/50{border-color:#f59e0b80}.border-bad\/25{border-color:color-mix(in srgb,var(--bad) 25%,transparent)}.border-brand\/30{border-color:color-mix(in srgb,var(--accent) 30%,transparent)}.border-brand\/60{border-color:color-mix(in srgb,var(--accent) 60%,transparent)}.border-emerald-500\/25{border-color:#10b98140}.border-indigo-500\/30{border-color:#6366f14d}.border-indigo-500\/40{border-color:#6366f166}.border-line{--tw-border-opacity: 1;border-color:color-mix(in srgb,var(--line) calc(var(--tw-border-opacity, 1) * 100%),transparent)}.border-line-soft{--tw-border-opacity: 1;border-color:color-mix(in srgb,var(--line-soft) calc(var(--tw-border-opacity, 1) * 100%),transparent)}.border-line\/60{border-color:color-mix(in srgb,var(--line) 60%,transparent)}.border-red-500\/20{border-color:#ef444433}.border-red-500\/25{border-color:#ef444440}.border-red-500\/40{border-color:#ef444466}.border-rose-500\/30{border-color:#f43f5e4d}.border-rose-500\/40{border-color:#f43f5e66}.border-rose-500\/50{border-color:#f43f5e80}.border-sky-500\/30{border-color:#0ea5e94d}.border-sky-500\/40{border-color:#0ea5e966}.border-teal-500\/30{border-color:#14b8a64d}.border-transparent{border-color:transparent}.border-warn\/40{border-color:color-mix(in srgb,var(--warn) 40%,transparent)}.border-zinc-500{--tw-border-opacity: 1;border-color:rgb(113 113 122 / var(--tw-border-opacity, 1))}.border-zinc-600{--tw-border-opacity: 1;border-color:rgb(82 82 91 / var(--tw-border-opacity, 1))}.border-zinc-600\/50{border-color:#52525b80}.border-zinc-700{--tw-border-opacity: 1;border-color:rgb(63 63 70 / var(--tw-border-opacity, 1))}.\!bg-zinc-600{--tw-bg-opacity: 1 !important;background-color:rgb(82 82 91 / var(--tw-bg-opacity, 1))!important}.bg-amber-400{--tw-bg-opacity: 1;background-color:rgb(251 191 36 / var(--tw-bg-opacity, 1))}.bg-amber-500\/10{background-color:#f59e0b1a}.bg-amber-500\/15{background-color:#f59e0b26}.bg-amber-500\/35{background-color:#f59e0b59}.bg-amber-500\/5{background-color:#f59e0b0d}.bg-bad\/10{background-color:color-mix(in srgb,var(--bad) 10%,transparent)}.bg-black\/15{background-color:#00000026}.bg-black\/20{background-color:#0003}.bg-black\/40{background-color:#0006}.bg-black\/60{background-color:#0009}.bg-brand\/10{background-color:color-mix(in srgb,var(--accent) 10%,transparent)}.bg-current{background-color:currentColor}.bg-emerald-400{--tw-bg-opacity: 1;background-color:rgb(52 211 153 / var(--tw-bg-opacity, 1))}.bg-emerald-400\/45{background-color:#34d39973}.bg-emerald-500\/10{background-color:#10b9811a}.bg-emerald-500\/15{background-color:#10b98126}.bg-emerald-500\/20{background-color:#10b98133}.bg-emerald-500\/30{background-color:#10b9814d}.bg-emerald-500\/40{background-color:#10b98166}.bg-indigo-500\/10{background-color:#6366f11a}.bg-line-soft{--tw-bg-opacity: 1;background-color:color-mix(in srgb,var(--line-soft) calc(var(--tw-bg-opacity, 1) * 100%),transparent)}.bg-live{--tw-bg-opacity: 1;background-color:color-mix(in srgb,var(--live) calc(var(--tw-bg-opacity, 1) * 100%),transparent)}.bg-live\/15{background-color:color-mix(in srgb,var(--live) 15%,transparent)}.bg-live\/20{background-color:color-mix(in srgb,var(--live) 20%,transparent)}.bg-panel{--tw-bg-opacity: 1;background-color:color-mix(in srgb,var(--panel) calc(var(--tw-bg-opacity, 1) * 100%),transparent)}.bg-panel-2{--tw-bg-opacity: 1;background-color:color-mix(in srgb,var(--panel-2) calc(var(--tw-bg-opacity, 1) * 100%),transparent)}.bg-panel\/95{background-color:color-mix(in srgb,var(--panel) 95%,transparent)}.bg-popover\/95{background-color:color-mix(in srgb,var(--panel-2) 95%,transparent)}.bg-red-400{--tw-bg-opacity: 1;background-color:rgb(248 113 113 / var(--tw-bg-opacity, 1))}.bg-red-500\/10{background-color:#ef44441a}.bg-red-500\/15{background-color:#ef444426}.bg-red-500\/20{background-color:#ef444433}.bg-red-500\/35{background-color:#ef444459}.bg-red-500\/40{background-color:#ef444466}.bg-rose-500\/10{background-color:#f43f5e1a}.bg-rose-500\/15{background-color:#f43f5e26}.bg-rose-500\/80{background-color:#f43f5ecc}.bg-sky-500\/10{background-color:#0ea5e91a}.bg-teal-500\/10{background-color:#14b8a61a}.bg-transparent{background-color:transparent}.bg-warn\/10{background-color:color-mix(in srgb,var(--warn) 10%,transparent)}.bg-zinc-800{--tw-bg-opacity: 1;background-color:rgb(39 39 42 / var(--tw-bg-opacity, 1))}.bg-zinc-800\/40{background-color:#27272a66}.bg-zinc-800\/50{background-color:#27272a80}.bg-zinc-800\/60{background-color:#27272a99}.bg-zinc-800\/70{background-color:#27272ab3}.bg-zinc-900{--tw-bg-opacity: 1;background-color:rgb(24 24 27 / var(--tw-bg-opacity, 1))}.bg-zinc-900\/30{background-color:#18181b4d}.bg-zinc-900\/40{background-color:#18181b66}.bg-zinc-900\/60{background-color:#18181b99}.p-0{padding:0}.p-0\.5{padding:.125rem}.p-1\.5{padding:.375rem}.p-2{padding:.5rem}.p-2\.5{padding:.625rem}.p-3{padding:.75rem}.p-6{padding:1.5rem}.p-8{padding:2rem}.px-0{padding-left:0;padding-right:0}.px-1{padding-left:.25rem;padding-right:.25rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-3\.5{padding-left:.875rem;padding-right:.875rem}.px-4{padding-left:1rem;padding-right:1rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.px-7{padding-left:1.75rem;padding-right:1.75rem}.py-0{padding-top:0;padding-bottom:0}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-5{padding-top:1.25rem;padding-bottom:1.25rem}.pb-2{padding-bottom:.5rem}.pb-3{padding-bottom:.75rem}.pl-1{padding-left:.25rem}.pl-2{padding-left:.5rem}.pl-3\.5{padding-left:.875rem}.pr-3{padding-right:.75rem}.pt-1\.5{padding-top:.375rem}.pt-3{padding-top:.75rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.text-\[10\.5px\]{font-size:10.5px}.text-\[10px\]{font-size:10px}.text-\[11\.5px\]{font-size:11.5px}.text-\[11px\]{font-size:11px}.text-\[12px\]{font-size:12px}.text-\[13px\]{font-size:13px}.text-\[15px\]{font-size:15px}.text-\[9px\]{font-size:9px}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.normal-case{text-transform:none}.italic{font-style:italic}.leading-none{line-height:1}.leading-relaxed{line-height:1.625}.tracking-\[0\.15em\]{letter-spacing:.15em}.tracking-\[0\.16em\]{letter-spacing:.16em}.tracking-\[0\.18em\]{letter-spacing:.18em}.tracking-\[0\.2em\]{letter-spacing:.2em}.tracking-normal{letter-spacing:0em}.tracking-tight{letter-spacing:-.025em}.tracking-wide{letter-spacing:.025em}.tracking-wider{letter-spacing:.05em}.text-amber-100{--tw-text-opacity: 1;color:rgb(254 243 199 / var(--tw-text-opacity, 1))}.text-amber-200{--tw-text-opacity: 1;color:rgb(253 230 138 / var(--tw-text-opacity, 1))}.text-amber-300{--tw-text-opacity: 1;color:rgb(252 211 77 / var(--tw-text-opacity, 1))}.text-amber-300\/70{color:#fcd34db3}.text-amber-400\/70{color:#fbbf24b3}.text-amber-400\/80{color:#fbbf24cc}.text-bad{--tw-text-opacity: 1;color:color-mix(in srgb,var(--bad) calc(var(--tw-text-opacity, 1) * 100%),transparent)}.text-brand{--tw-text-opacity: 1;color:color-mix(in srgb,var(--accent) calc(var(--tw-text-opacity, 1) * 100%),transparent)}.text-emerald-300{--tw-text-opacity: 1;color:rgb(110 231 183 / var(--tw-text-opacity, 1))}.text-emerald-400{--tw-text-opacity: 1;color:rgb(52 211 153 / var(--tw-text-opacity, 1))}.text-emerald-500\/80{color:#10b981cc}.text-foreground{--tw-text-opacity: 1;color:color-mix(in srgb,var(--text) calc(var(--tw-text-opacity, 1) * 100%),transparent)}.text-good{--tw-text-opacity: 1;color:color-mix(in srgb,var(--good) calc(var(--tw-text-opacity, 1) * 100%),transparent)}.text-indigo-300{--tw-text-opacity: 1;color:rgb(165 180 252 / var(--tw-text-opacity, 1))}.text-indigo-300\/80{color:#a5b4fccc}.text-indigo-400{--tw-text-opacity: 1;color:rgb(129 140 248 / var(--tw-text-opacity, 1))}.text-live{--tw-text-opacity: 1;color:color-mix(in srgb,var(--live) calc(var(--tw-text-opacity, 1) * 100%),transparent)}.text-red-200{--tw-text-opacity: 1;color:rgb(254 202 202 / var(--tw-text-opacity, 1))}.text-red-300{--tw-text-opacity: 1;color:rgb(252 165 165 / var(--tw-text-opacity, 1))}.text-red-400{--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity, 1))}.text-red-400\/80{color:#f87171cc}.text-rose-200{--tw-text-opacity: 1;color:rgb(254 205 211 / var(--tw-text-opacity, 1))}.text-rose-300{--tw-text-opacity: 1;color:rgb(253 164 175 / var(--tw-text-opacity, 1))}.text-rose-300\/70{color:#fda4afb3}.text-sky-300{--tw-text-opacity: 1;color:rgb(125 211 252 / var(--tw-text-opacity, 1))}.text-teal-300{--tw-text-opacity: 1;color:rgb(94 234 212 / var(--tw-text-opacity, 1))}.text-warn{--tw-text-opacity: 1;color:color-mix(in srgb,var(--warn) calc(var(--tw-text-opacity, 1) * 100%),transparent)}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.text-zinc-100{--tw-text-opacity: 1;color:rgb(244 244 245 / var(--tw-text-opacity, 1))}.text-zinc-200{--tw-text-opacity: 1;color:rgb(228 228 231 / var(--tw-text-opacity, 1))}.text-zinc-300{--tw-text-opacity: 1;color:rgb(212 212 216 / var(--tw-text-opacity, 1))}.text-zinc-400{--tw-text-opacity: 1;color:rgb(161 161 170 / var(--tw-text-opacity, 1))}.text-zinc-50{--tw-text-opacity: 1;color:rgb(250 250 250 / var(--tw-text-opacity, 1))}.text-zinc-500{--tw-text-opacity: 1;color:rgb(113 113 122 / var(--tw-text-opacity, 1))}.text-zinc-600{--tw-text-opacity: 1;color:rgb(82 82 91 / var(--tw-text-opacity, 1))}.text-zinc-800{--tw-text-opacity: 1;color:rgb(39 39 42 / var(--tw-text-opacity, 1))}.shadow-2xl{--tw-shadow: 0 25px 50px -12px rgb(0 0 0 / .25);--tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.outline{outline-style:solid}.ring{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-1{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-2{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-amber-500\/45{--tw-ring-color: rgb(245 158 11 / .45)}.ring-brand\/30{--tw-ring-color: color-mix(in srgb, var(--accent) 30% , transparent)}.ring-emerald-400\/60{--tw-ring-color: rgb(52 211 153 / .6)}.ring-emerald-500\/40{--tw-ring-color: rgb(16 185 129 / .4)}.ring-emerald-500\/50{--tw-ring-color: rgb(16 185 129 / .5)}.ring-line-soft{--tw-ring-opacity: 1;--tw-ring-color: color-mix(in srgb, var(--line-soft) calc(var(--tw-ring-opacity, 1) * 100%), transparent)}.ring-red-500\/50{--tw-ring-color: rgb(239 68 68 / .5)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur{--tw-backdrop-blur: blur(8px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.backdrop-blur-md{--tw-backdrop-blur: blur(12px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.backdrop-blur-sm{--tw-backdrop-blur: blur(4px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-150{transition-duration:.15s}.duration-500{transition-duration:.5s}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}:root{--bg: #09090b;--panel: #0c0c0f;--panel-2: #101017;--line: #1c1c22;--line-soft: #16161a;--text: #e7e7ea;--muted: #76767f;--good: #34d399;--warn: #fbbf24;--bad: #f87171;--live: #60a5fa;--accent: #a3e635}*{box-sizing:border-box}html,body,#root{height:100%}body{margin:0;background:var(--bg);color:var(--text);font-family:Space Grotesk,ui-sans-serif,system-ui,sans-serif;-webkit-font-smoothing:antialiased}.app-bg{position:fixed;top:0;right:0;bottom:0;left:0;z-index:0;pointer-events:none;background-image:radial-gradient(900px 400px at 78% -8%,color-mix(in srgb,var(--accent) 8%,transparent),transparent 70%),linear-gradient(to right,var(--line-soft) 1px,transparent 1px),linear-gradient(to bottom,var(--line-soft) 1px,transparent 1px);background-size:100% 100%,44px 44px,44px 44px;-webkit-mask-image:linear-gradient(to bottom,black,black 60%,transparent);mask-image:linear-gradient(to bottom,black,black 60%,transparent);opacity:.5}.mono{font-family:JetBrains Mono,ui-monospace,monospace}.tnum{font-variant-numeric:tabular-nums}.s-running{color:var(--live)}.s-suspended{color:var(--warn)}.s-sleeping{color:#818cf8}.s-awaiting{color:var(--warn)}.s-completed{color:var(--good)}.s-failed{color:var(--bad)}.s-cancelling{color:#f59e0b}.s-cancelled{color:#71717a}.s-dead{color:#e11d48}.s-pending{color:#94a3b8}.s-no-worker{color:#fb923c}.s-queued{color:#a78bfa}.s-stale{color:#fb923c}.dot{width:7px;height:7px;border-radius:999px;background:currentColor;box-shadow:0 0 0 3px color-mix(in srgb,currentColor 18%,transparent)}.pulse{animation:pulse 1.6s ease-in-out infinite}@keyframes pulse{0%,to{opacity:1}50%{opacity:.35}}@keyframes rise{0%{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}.rise{animation:rise .32s cubic-bezier(.2,.7,.2,1) both}.react-flow{--xy-background-color: transparent;--xy-edge-stroke-default: var(--line);--xy-attribution-background-color: transparent}.react-flow__edge-path{stroke-width:1.5}.react-flow__controls{border:1px solid var(--line);border-radius:8px;overflow:hidden;box-shadow:none}.react-flow__controls-button{background:var(--panel);border-bottom:1px solid var(--line);color:var(--muted);fill:var(--muted)}.react-flow__controls-button:hover{background:#18181b}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-thumb{background:#26262c;border:3px solid var(--bg);border-radius:999px}.placeholder\:text-zinc-600::-moz-placeholder{--tw-text-opacity: 1;color:rgb(82 82 91 / var(--tw-text-opacity, 1))}.placeholder\:text-zinc-600::placeholder{--tw-text-opacity: 1;color:rgb(82 82 91 / var(--tw-text-opacity, 1))}.focus-within\:border-zinc-600:focus-within{--tw-border-opacity: 1;border-color:rgb(82 82 91 / var(--tw-border-opacity, 1))}.hover\:-translate-y-0\.5:hover{--tw-translate-y: -.125rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hover\:border-amber-500\/40:hover{border-color:#f59e0b66}.hover\:border-indigo-400\/60:hover{border-color:#818cf899}.hover\:border-red-400\/60:hover{border-color:#f8717199}.hover\:border-sky-400\/60:hover{border-color:#38bdf899}.hover\:border-teal-400\/60:hover{border-color:#2dd4bf99}.hover\:border-zinc-500:hover{--tw-border-opacity: 1;border-color:rgb(113 113 122 / var(--tw-border-opacity, 1))}.hover\:border-zinc-600:hover{--tw-border-opacity: 1;border-color:rgb(82 82 91 / var(--tw-border-opacity, 1))}.hover\:bg-brand\/20:hover{background-color:color-mix(in srgb,var(--accent) 20%,transparent)}.hover\:bg-brand\/40:hover{background-color:color-mix(in srgb,var(--accent) 40%,transparent)}.hover\:bg-indigo-500\/20:hover{background-color:#6366f133}.hover\:bg-rose-500\/20:hover{background-color:#f43f5e33}.hover\:bg-sky-500\/20:hover{background-color:#0ea5e933}.hover\:bg-warn\/20:hover{background-color:color-mix(in srgb,var(--warn) 20%,transparent)}.hover\:bg-zinc-800:hover{--tw-bg-opacity: 1;background-color:rgb(39 39 42 / var(--tw-bg-opacity, 1))}.hover\:bg-zinc-800\/50:hover{background-color:#27272a80}.hover\:bg-zinc-900\/50:hover{background-color:#18181b80}.hover\:bg-zinc-900\/60:hover{background-color:#18181b99}.hover\:text-indigo-100:hover{--tw-text-opacity: 1;color:rgb(224 231 255 / var(--tw-text-opacity, 1))}.hover\:text-indigo-200:hover{--tw-text-opacity: 1;color:rgb(199 210 254 / var(--tw-text-opacity, 1))}.hover\:text-sky-200:hover{--tw-text-opacity: 1;color:rgb(186 230 253 / var(--tw-text-opacity, 1))}.hover\:text-teal-200:hover{--tw-text-opacity: 1;color:rgb(153 246 228 / var(--tw-text-opacity, 1))}.hover\:text-zinc-100:hover{--tw-text-opacity: 1;color:rgb(244 244 245 / var(--tw-text-opacity, 1))}.hover\:text-zinc-200:hover{--tw-text-opacity: 1;color:rgb(228 228 231 / var(--tw-text-opacity, 1))}.hover\:text-zinc-300:hover{--tw-text-opacity: 1;color:rgb(212 212 216 / var(--tw-text-opacity, 1))}.focus\:border-zinc-600:focus{--tw-border-opacity: 1;border-color:rgb(82 82 91 / var(--tw-border-opacity, 1))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus-visible\:outline-none:focus-visible{outline:2px solid transparent;outline-offset:2px}.focus-visible\:ring-1:focus-visible{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus-visible\:ring-ring:focus-visible{--tw-ring-opacity: 1;--tw-ring-color: color-mix(in srgb, var(--accent) calc(var(--tw-ring-opacity, 1) * 100%), transparent)}.enabled\:hover\:bg-rose-500\/25:hover:enabled{background-color:#f43f5e40}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:cursor-default:disabled{cursor:default}.disabled\:opacity-30:disabled{opacity:.3}.disabled\:opacity-50:disabled{opacity:.5}.data-\[active\]\:bg-zinc-700[data-active]{--tw-bg-opacity: 1;background-color:rgb(63 63 70 / var(--tw-bg-opacity, 1))}.data-\[active\]\:text-zinc-100[data-active]{--tw-text-opacity: 1;color:rgb(244 244 245 / var(--tw-text-opacity, 1))}
|