crewdesk 0.1.0 → 0.1.1
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 +82 -7
- package/apps/web/dist/assets/index-PSq5yw7N.css +1 -0
- package/apps/web/dist/assets/index-mDYf6j1e.js +18 -0
- package/apps/web/dist/index.html +2 -2
- package/docs/DEVELOPMENT.md +1 -0
- package/package.json +1 -1
- package/apps/web/dist/assets/index-hoPwNvfD.js +0 -18
- package/apps/web/dist/assets/index-lYVPS1ZE.css +0 -1
package/README.md
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
# Crewdesk
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/crewdesk)
|
|
4
|
+
[](https://github.com/HusseinTaha/crewdesk/actions/workflows/ci.yml)
|
|
5
|
+

|
|
6
|
+

|
|
7
|
+
[](LICENSE)
|
|
8
|
+
|
|
3
9
|
One local dashboard for all your Claude Code sessions. When any session asks a question, wants permission, or finishes a turn, it shows up in one attention queue at **http://127.0.0.1:7777**. Your answer goes back to the exact session that asked. Routing uses the event id and the session id, never the name or terminal.
|
|
4
10
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
Claude
|
|
8
|
-
|
|
9
|
-
Claude #4 ─┘ ◀──────────│ SQLite + API │ REST └───────────┘
|
|
10
|
-
decisions └──────────────┘
|
|
11
|
-
```
|
|
11
|
+
<picture>
|
|
12
|
+
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/HusseinTaha/crewdesk/main/docs/images/dashboard-light.png">
|
|
13
|
+
<img alt="Crewdesk dashboard: five Claude Code sessions, a destructive permission request, a question and recent activity" src="https://raw.githubusercontent.com/HusseinTaha/crewdesk/main/docs/images/dashboard-dark.png">
|
|
14
|
+
</picture>
|
|
12
15
|
|
|
13
16
|
## Quick start
|
|
14
17
|
|
|
@@ -23,6 +26,46 @@ Install it globally rather than running it through `npx`. The hooks point at the
|
|
|
23
26
|
|
|
24
27
|
Start Claude Code sessions normally. Sessions that were already running need a restart, or run `/hooks`, to pick up the hooks.
|
|
25
28
|
|
|
29
|
+
## How it works
|
|
30
|
+
|
|
31
|
+
```mermaid
|
|
32
|
+
flowchart LR
|
|
33
|
+
subgraph sessions["Claude Code sessions (any project, any account)"]
|
|
34
|
+
C1["payments-api #1"]
|
|
35
|
+
C2["storefront-web #1"]
|
|
36
|
+
C3["mobile-app #1"]
|
|
37
|
+
end
|
|
38
|
+
H["crewdesk-hook<br/>(one short process per hook call)"]
|
|
39
|
+
HUB[("Crewdesk hub<br/>Fastify + SQLite<br/>127.0.0.1:7777")]
|
|
40
|
+
UI["Dashboard<br/>(browser)"]
|
|
41
|
+
|
|
42
|
+
C1 & C2 & C3 -- "hook JSON on stdin" --> H
|
|
43
|
+
H -- "POST event, then long-poll" --> HUB
|
|
44
|
+
HUB -- "WebSocket push" --> UI
|
|
45
|
+
UI -- "Allow / Deny / answer / next prompt" --> HUB
|
|
46
|
+
HUB -- "decision" --> H
|
|
47
|
+
H -- "hook output" --> C1 & C2 & C3
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Each hook call is routed by **event id → the session id stored with that event**, so an answer can only ever reach the session that asked. The terminal stays live the whole time. Whichever answers first, the dashboard or the terminal, wins.
|
|
51
|
+
|
|
52
|
+
```mermaid
|
|
53
|
+
sequenceDiagram
|
|
54
|
+
autonumber
|
|
55
|
+
participant CC as Claude Code
|
|
56
|
+
participant HK as crewdesk-hook
|
|
57
|
+
participant HUB as Crewdesk hub
|
|
58
|
+
participant UI as Dashboard
|
|
59
|
+
CC->>HK: PermissionRequest (Bash: rm -rf dist && pnpm build)
|
|
60
|
+
HK->>HUB: POST /api/events
|
|
61
|
+
HUB-->>UI: WebSocket: new request (desktop notification + chime)
|
|
62
|
+
HK->>HUB: GET /api/events/:id/wait (long-poll)
|
|
63
|
+
UI->>HUB: Allow
|
|
64
|
+
HUB-->>HK: { action: "allow" }
|
|
65
|
+
HK-->>CC: allow → the tool runs
|
|
66
|
+
Note over HK,HUB: Hub down or unreachable? The hook exits 0 and Claude Code carries on as if no hook existed.
|
|
67
|
+
```
|
|
68
|
+
|
|
26
69
|
## What reaches the dashboard
|
|
27
70
|
|
|
28
71
|
| Claude Code does… | Hook | Dashboard shows | Terminal meanwhile |
|
|
@@ -35,6 +78,37 @@ Start Claude Code sessions normally. Sessions that were already running need a r
|
|
|
35
78
|
|
|
36
79
|
If the hub is down, every hook fails open: Claude Code behaves exactly as if no hook existed.
|
|
37
80
|
|
|
81
|
+
## Screenshots
|
|
82
|
+
|
|
83
|
+
**Permission requests.** Destructive commands are flagged and need a second confirm.
|
|
84
|
+
|
|
85
|
+

|
|
86
|
+
|
|
87
|
+
**Questions.** `AskUserQuestion` options become buttons, and you can also type a custom answer.
|
|
88
|
+
|
|
89
|
+

|
|
90
|
+
|
|
91
|
+
**Idle sessions.** When Claude finishes a turn, send the next instruction straight from the dashboard.
|
|
92
|
+
|
|
93
|
+

|
|
94
|
+
|
|
95
|
+
**Desktop notifications.** Opt in with **Notify**. They fire while the dashboard tab is in the background.
|
|
96
|
+
|
|
97
|
+

|
|
98
|
+
|
|
99
|
+
<table>
|
|
100
|
+
<tr>
|
|
101
|
+
<td width="50%"><b>History</b>: every event, filterable by type, status, project and agent.<br><img alt="Event history table" src="https://raw.githubusercontent.com/HusseinTaha/crewdesk/main/docs/images/history.png"></td>
|
|
102
|
+
<td width="50%"><b>Agent detail</b>: project, directory, pid, account, current task and pending requests.<br><img alt="Agent detail page" src="https://raw.githubusercontent.com/HusseinTaha/crewdesk/main/docs/images/agent-detail.png"></td>
|
|
103
|
+
</tr>
|
|
104
|
+
<tr>
|
|
105
|
+
<td width="50%"><b>Keyboard-first</b>: <code>J</code>/<code>K</code> to move, <code>A</code>/<code>D</code> to allow or deny, <code>?</code> for help.<br><img alt="Keyboard shortcuts dialog" src="https://raw.githubusercontent.com/HusseinTaha/crewdesk/main/docs/images/shortcuts.png"></td>
|
|
106
|
+
<td width="50%" align="center"><b>Responsive</b>: fits a narrow window or a split screen next to your terminal.<br><img alt="Dashboard at phone width" src="https://raw.githubusercontent.com/HusseinTaha/crewdesk/main/docs/images/mobile.png" width="260"></td>
|
|
107
|
+
</tr>
|
|
108
|
+
</table>
|
|
109
|
+
|
|
110
|
+
<sub>Screenshots use made-up sessions and are regenerated with <code>node scripts/screenshots.mjs</code>.</sub>
|
|
111
|
+
|
|
38
112
|
## Features
|
|
39
113
|
|
|
40
114
|
- **Agents:** multiple sessions per project, multiple accounts, named `<project> #N` and renamable. Session reconnects are detected. A session goes OFFLINE when its Claude process (`CLAUDE_PID`) dies.
|
|
@@ -44,6 +118,7 @@ If the hub is down, every hook fails open: Claude Code behaves exactly as if no
|
|
|
44
118
|
- **Notifications:** desktop notifications (opt-in) and sound. Dark and light themes.
|
|
45
119
|
- **Permission policies:** in `~/.crewdesk/policies.yaml`. Destructive or chained shell commands are never auto-allowed.
|
|
46
120
|
- **Persistence:** everything is persisted in SQLite, so a browser refresh or hub restart loses nothing.
|
|
121
|
+
- **Cross-platform:** tested in CI on macOS, Linux and Windows with Node 22 and 24.
|
|
47
122
|
|
|
48
123
|
## Commands
|
|
49
124
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial}}}@layer theme{:root,:host{--font-sans:"Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;--font-mono:"JetBrains Mono", ui-monospace, "Cascadia Code", Consolas, monospace;--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-sm:24rem;--container-4xl:56rem;--container-5xl:64rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height: 1.5 ;--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--font-weight-medium:500;--font-weight-semibold:600;--tracking-tight:-.025em;--tracking-wide:.025em;--tracking-wider:.05em;--radius-md:.375rem;--radius-xl:.75rem;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-card:var(--card);--color-card-2:var(--card-2);--color-line:var(--line);--color-fg:var(--fg);--color-muted:var(--muted);--color-st-blue:#3b82f6;--color-st-green:#22c55e;--color-st-yellow:#eab308;--color-st-orange:#f97316;--color-st-red:#ef4444;--color-st-purple:#a855f7;--color-st-gray:#6b7280}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}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;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.visible{visibility:visible}.fixed{position:fixed}.sticky{position:sticky}.inset-0{inset:0}.top-0{top:0}.z-50{z-index:50}.mx-auto{margin-inline:auto}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:var(--spacing)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mr-2{margin-right:calc(var(--spacing) * 2)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.ml-1{margin-left:var(--spacing)}.ml-2{margin-left:calc(var(--spacing) * 2)}.ml-auto{margin-left:auto}.line-clamp-2{-webkit-line-clamp:2;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline-flex{display:inline-flex}.table{display:table}.h-2{height:calc(var(--spacing) * 2)}.h-full{height:100%}.max-h-48{max-height:calc(var(--spacing) * 48)}.max-h-60{max-height:calc(var(--spacing) * 60)}.max-h-72{max-height:calc(var(--spacing) * 72)}.min-h-0{min-height:0}.w-2{width:calc(var(--spacing) * 2)}.w-16{width:calc(var(--spacing) * 16)}.w-36{width:calc(var(--spacing) * 36)}.w-44{width:calc(var(--spacing) * 44)}.w-56{width:calc(var(--spacing) * 56)}.w-full{width:100%}.max-w-4xl{max-width:var(--container-4xl)}.max-w-5xl{max-width:var(--container-5xl)}.max-w-full{max-width:100%}.max-w-sm{max-width:var(--container-sm)}.min-w-0{min-width:0}.min-w-32{min-width:calc(var(--spacing) * 32)}.flex-1{flex:1}.shrink-0{flex-shrink:0}.basis-full{flex-basis:100%}.resize-y{resize:vertical}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.content-start{align-content:flex-start}.items-baseline{align-items:baseline}.items-center{align-items:center}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.gap-1{gap:var(--spacing)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}:where(.space-y-1\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1.5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1.5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 5) * calc(1 - var(--tw-space-y-reverse)))}.gap-x-3{column-gap:calc(var(--spacing) * 3)}.gap-x-4{column-gap:calc(var(--spacing) * 4)}.gap-y-2{row-gap:calc(var(--spacing) * 2)}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-line>:not(:last-child)){border-color:var(--color-line)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-hidden{overflow:hidden}.overflow-x-hidden{overflow-x:hidden}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-full{border-radius:3.40282e38px}.rounded-md{border-radius:var(--radius-md)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l-4{border-left-style:var(--tw-border-style);border-left-width:4px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-line{border-color:var(--color-line)}.border-st-blue{border-color:var(--color-st-blue)}.border-st-orange{border-color:var(--color-st-orange)}.border-st-orange\/60{border-color:#f9731699}@supports (color:color-mix(in lab,red,red)){.border-st-orange\/60{border-color:color-mix(in oklab,var(--color-st-orange) 60%,transparent)}}.border-st-purple{border-color:var(--color-st-purple)}.border-st-red{border-color:var(--color-st-red)}.border-st-yellow{border-color:var(--color-st-yellow)}.bg-black\/60{background-color:#0009}@supports (color:color-mix(in lab,red,red)){.bg-black\/60{background-color:color-mix(in oklab,var(--color-black) 60%,transparent)}}.bg-card{background-color:var(--color-card)}.bg-card-2{background-color:var(--color-card-2)}.bg-st-blue{background-color:var(--color-st-blue)}.bg-st-blue\/15{background-color:#3b82f626}@supports (color:color-mix(in lab,red,red)){.bg-st-blue\/15{background-color:color-mix(in oklab,var(--color-st-blue) 15%,transparent)}}.bg-st-gray{background-color:var(--color-st-gray)}.bg-st-green{background-color:var(--color-st-green)}.bg-st-orange{background-color:var(--color-st-orange)}.bg-st-orange\/15{background-color:#f9731626}@supports (color:color-mix(in lab,red,red)){.bg-st-orange\/15{background-color:color-mix(in oklab,var(--color-st-orange) 15%,transparent)}}.bg-st-orange\/20{background-color:#f9731633}@supports (color:color-mix(in lab,red,red)){.bg-st-orange\/20{background-color:color-mix(in oklab,var(--color-st-orange) 20%,transparent)}}.bg-st-purple{background-color:var(--color-st-purple)}.bg-st-purple\/15{background-color:#a855f726}@supports (color:color-mix(in lab,red,red)){.bg-st-purple\/15{background-color:color-mix(in oklab,var(--color-st-purple) 15%,transparent)}}.bg-st-red{background-color:var(--color-st-red)}.bg-st-red\/10{background-color:#ef44441a}@supports (color:color-mix(in lab,red,red)){.bg-st-red\/10{background-color:color-mix(in oklab,var(--color-st-red) 10%,transparent)}}.bg-st-red\/15{background-color:#ef444426}@supports (color:color-mix(in lab,red,red)){.bg-st-red\/15{background-color:color-mix(in oklab,var(--color-st-red) 15%,transparent)}}.bg-st-yellow{background-color:var(--color-st-yellow)}.bg-st-yellow\/15{background-color:#eab30826}@supports (color:color-mix(in lab,red,red)){.bg-st-yellow\/15{background-color:color-mix(in oklab,var(--color-st-yellow) 15%,transparent)}}.p-2{padding:calc(var(--spacing) * 2)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-5{padding:calc(var(--spacing) * 5)}.p-6{padding:calc(var(--spacing) * 6)}.p-8{padding:calc(var(--spacing) * 8)}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:var(--spacing)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pr-1{padding-right:var(--spacing)}.text-center{text-align:center}.text-left{text-align:left}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.\[overflow-wrap\:normal\]{overflow-wrap:normal}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.text-black{color:var(--color-black)}.text-fg{color:var(--color-fg)}.text-muted{color:var(--color-muted)}.text-st-blue{color:var(--color-st-blue)}.text-st-gray{color:var(--color-st-gray)}.text-st-green{color:var(--color-st-green)}.text-st-orange{color:var(--color-st-orange)}.text-st-purple{color:var(--color-st-purple)}.text-st-red{color:var(--color-st-red)}.text-st-yellow{color:var(--color-st-yellow)}.text-white{color:var(--color-white)}.uppercase{text-transform:uppercase}.line-through{text-decoration-line:line-through}.underline{text-decoration-line:underline}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-2{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-st-blue\/60{--tw-ring-color:#3b82f699}@supports (color:color-mix(in lab,red,red)){.ring-st-blue\/60{--tw-ring-color:color-mix(in oklab, var(--color-st-blue) 60%, transparent)}}.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,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.placeholder\:text-muted::placeholder{color:var(--color-muted)}@media(hover:hover){.hover\:border-st-blue\/60:hover{border-color:#3b82f699}@supports (color:color-mix(in lab,red,red)){.hover\:border-st-blue\/60:hover{border-color:color-mix(in oklab,var(--color-st-blue) 60%,transparent)}}.hover\:border-st-orange:hover{border-color:var(--color-st-orange)}.hover\:bg-card-2:hover{background-color:var(--color-card-2)}.hover\:bg-st-green\/85:hover{background-color:#22c55ed9}@supports (color:color-mix(in lab,red,red)){.hover\:bg-st-green\/85:hover{background-color:color-mix(in oklab,var(--color-st-green) 85%,transparent)}}.hover\:bg-st-orange\/85:hover{background-color:#f97316d9}@supports (color:color-mix(in lab,red,red)){.hover\:bg-st-orange\/85:hover{background-color:color-mix(in oklab,var(--color-st-orange) 85%,transparent)}}.hover\:bg-st-red\/85:hover{background-color:#ef4444d9}@supports (color:color-mix(in lab,red,red)){.hover\:bg-st-red\/85:hover{background-color:color-mix(in oklab,var(--color-st-red) 85%,transparent)}}.hover\:bg-st-yellow\/85:hover{background-color:#eab308d9}@supports (color:color-mix(in lab,red,red)){.hover\:bg-st-yellow\/85:hover{background-color:color-mix(in oklab,var(--color-st-yellow) 85%,transparent)}}.hover\:text-fg:hover{color:var(--color-fg)}.hover\:underline:hover{text-decoration-line:underline}}.focus\:border-st-blue:focus{border-color:var(--color-st-blue)}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}@media(min-width:40rem){.sm\:table-cell{display:table-cell}}@media(min-width:48rem){.md\:line-clamp-none{-webkit-line-clamp:unset;-webkit-box-orient:horizontal;display:block;overflow:visible}.md\:table-cell{display:table-cell}.md\:flex-1{flex:1}.md\:basis-auto{flex-basis:auto}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.md\:flex-nowrap{flex-wrap:nowrap}.md\:truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}}@media(min-width:64rem){.lg\:inline{display:inline}.lg\:min-h-0{min-height:0}.lg\:flex-1{flex:1}.lg\:grid-cols-\[320px_minmax\(0\,1fr\)\]{grid-template-columns:320px minmax(0,1fr)}.lg\:content-stretch{align-content:stretch}.lg\:overflow-hidden{overflow:hidden}.lg\:overflow-y-auto{overflow-y:auto}}}:root{--bg:#f5f7fa;--card:#fff;--card-2:#eef1f5;--line:#d9dee6;--fg:#111827;--muted:#5b6574;color-scheme:light}:root.dark{--bg:#0b0d10;--card:#151922;--card-2:#1b202b;--line:#252b36;--fg:#f4f7fa;--muted:#8b95a5;color-scheme:dark}html,body,#root{height:100%}body{background:var(--bg);color:var(--fg);font-family:var(--font-sans);-webkit-font-smoothing:antialiased;overflow-wrap:anywhere}:focus-visible{outline-offset:2px;outline:2px solid #3b82f6}.pulse-dot{animation:1.6s ease-in-out infinite pulse-dot}@keyframes pulse-dot{0%,to{opacity:1}50%{opacity:.35}}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}
|