claude-memory-admin 1.6.0 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -1
- package/package.json +1 -1
- package/public/state.mjs +3 -2
- package/public/store.mjs +2 -1
- package/public/styles.css +1 -1
- package/public/ui.mjs +1 -1
- package/public/views/environment.mjs +2 -0
- package/public/views/header.mjs +2 -0
- package/public/views/rtk.mjs +148 -0
- package/public/views/tools.mjs +76 -0
- package/server.mjs +9 -0
- package/src/rtk.mjs +179 -0
package/README.md
CHANGED
|
@@ -83,7 +83,7 @@ Everything lives under three tabs, each answering one question:
|
|
|
83
83
|
| --- | --- | --- |
|
|
84
84
|
| **Memory** | what is in here? | the memory list, `MEMORY.md`, the graph |
|
|
85
85
|
| **Cleanup** | what should I fix? | the load meter and one worst-first list of fixable things |
|
|
86
|
-
| **Environment** | what else does Claude load? | instructions, settings, sessions - all read-only |
|
|
86
|
+
| **Environment** | what else does Claude load? | instructions, settings, sessions, tools - all read-only |
|
|
87
87
|
|
|
88
88
|
Undo is a button in the project header rather than a fourth tab, because it is a
|
|
89
89
|
safety net and not a place you browse.
|
|
@@ -305,6 +305,33 @@ It also names the failures that are otherwise silent:
|
|
|
305
305
|
Every segment of Environment is read-only. It reports what is configured; it never
|
|
306
306
|
writes a setting.
|
|
307
307
|
|
|
308
|
+
## Tools: what a session saved, next to what it cost
|
|
309
|
+
|
|
310
|
+
Every other meter here counts what a session *costs*. [rtk](https://github.com/rtk-ai/rtk)
|
|
311
|
+
counts the other half: it proxies commands like `grep`, `git` and `cargo test`,
|
|
312
|
+
strips their output down before it reaches the model, and keeps a ledger of what
|
|
313
|
+
it removed.
|
|
314
|
+
|
|
315
|
+
If `rtk` is on the PATH the server was started with, Environment grows a fourth
|
|
316
|
+
segment, **Tools**, showing three things:
|
|
317
|
+
|
|
318
|
+
- **This project** - what share of everything rtk read here never reached the
|
|
319
|
+
model. rtk scopes its ledger by working directory, so this is exactly the
|
|
320
|
+
commands run from this project's path.
|
|
321
|
+
- **Every project** - the same figure for the whole machine, with the last
|
|
322
|
+
thirty days called out separately, because a lifetime average hides a habit
|
|
323
|
+
that changed last week.
|
|
324
|
+
- **Left on the table** - commands that ran raw when rtk has a filter for them,
|
|
325
|
+
worst first, with rtk's own estimate of what each would have saved. The
|
|
326
|
+
estimates count what the filter would have stripped, not what the model would
|
|
327
|
+
have ignored.
|
|
328
|
+
|
|
329
|
+
The segment is the one place this app runs a program that is not itself, so it
|
|
330
|
+
stays off until you press **Read rtk stats**, and the answer is remembered. rtk
|
|
331
|
+
is only ever handed a working directory the app resolved on its own, never one
|
|
332
|
+
named in a request, and only its read-only `gain` and `discover` subcommands are
|
|
333
|
+
called. If rtk is not installed, the segment does not appear at all.
|
|
334
|
+
|
|
308
335
|
## Everything it writes is reversible
|
|
309
336
|
|
|
310
337
|
Delete shows a preview first: the exact lines that will go, the prose mentions it
|
package/package.json
CHANGED
package/public/state.mjs
CHANGED
|
@@ -5,7 +5,7 @@ export const SEGMENT_STORAGE = {
|
|
|
5
5
|
|
|
6
6
|
const SEGMENTS = {
|
|
7
7
|
memory: ['list', 'index', 'graph'],
|
|
8
|
-
environment: ['instructions', 'settings', 'sessions'],
|
|
8
|
+
environment: ['instructions', 'settings', 'sessions', 'tools'],
|
|
9
9
|
};
|
|
10
10
|
|
|
11
11
|
const storedSegment = (tab) => {
|
|
@@ -21,6 +21,7 @@ const storedListSort = () => {
|
|
|
21
21
|
|
|
22
22
|
export const state = {
|
|
23
23
|
stores: [],
|
|
24
|
+
tools: [],
|
|
24
25
|
storeId: null,
|
|
25
26
|
store: null,
|
|
26
27
|
tab: 'memory',
|
|
@@ -39,7 +40,7 @@ export const state = {
|
|
|
39
40
|
listSelection: new Set(),
|
|
40
41
|
selecting: false,
|
|
41
42
|
indexView: localStorage.getItem('memoryIndexView') === 'source' ? 'source' : 'rendered',
|
|
42
|
-
aux: { instructions: null, settings: null, sessions: null, sessionDay: null },
|
|
43
|
+
aux: { instructions: null, settings: null, sessions: null, sessionDay: null, rtk: null },
|
|
43
44
|
pathCheck: null,
|
|
44
45
|
};
|
|
45
46
|
|
package/public/store.mjs
CHANGED
|
@@ -10,7 +10,7 @@ export async function openStore(id, { keepTab = false } = {}) {
|
|
|
10
10
|
state.selecting = false;
|
|
11
11
|
}
|
|
12
12
|
state.storeId = id;
|
|
13
|
-
state.aux = { instructions: null, settings: null, sessions: null, sessionDay: null };
|
|
13
|
+
state.aux = { instructions: null, settings: null, sessions: null, sessionDay: null, rtk: null };
|
|
14
14
|
state.pathCheck = null;
|
|
15
15
|
if (!keepTab) {
|
|
16
16
|
const opening = state.stores.find((store) => store.id === id);
|
|
@@ -64,6 +64,7 @@ export async function openStore(id, { keepTab = false } = {}) {
|
|
|
64
64
|
export async function reloadStores() {
|
|
65
65
|
const data = await api('/api/stores');
|
|
66
66
|
state.stores = data.stores;
|
|
67
|
+
state.tools = data.tools || [];
|
|
67
68
|
paint('stores');
|
|
68
69
|
sweepIssues();
|
|
69
70
|
return data;
|
package/public/styles.css
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
|
|
2
|
-
@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-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-border-style:solid;--tw-leading:initial;--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-outline-style:solid;--tw-content:"";--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:ui-sans-serif, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;--font-mono:ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;--color-red-50:oklch(97.1% .013 17.4);--color-red-300:oklch(80.8% .114 19.6);--color-red-400:oklch(70.4% .16 22.2);--color-red-500:oklch(63.7% .208 25.3);--color-red-600:oklch(57.7% .215 27.3);--color-red-950:oklch(25.8% .092 26);--color-amber-50:oklch(97.7% .021 84.6);--color-amber-300:oklch(87.9% .153 91.6);--color-amber-400:oklch(82.8% .171 84.4);--color-amber-600:oklch(66.6% .15 58.3);--color-amber-700:oklch(55.5% .13 49);--color-amber-950:oklch(27.9% .077 45.6);--color-green-50:oklch(98.2% .018 155.8);--color-green-300:oklch(87.1% .12 154.4);--color-green-400:oklch(79.2% .15 151.7);--color-green-600:oklch(58.5% .145 149.2);--color-green-700:oklch(52.7% .132 150.1);--color-green-950:oklch(26.6% .065 152.9);--color-blue-50:oklch(97% .014 253.3);--color-blue-200:oklch(88% .06 253.3);--color-blue-300:oklch(78.4% .124 239.1);--color-blue-400:oklch(75.2% .138 243.7);--color-blue-500:oklch(65.8% .152 248.5);--color-blue-600:oklch(56.5% .163 253.3);--color-blue-700:oklch(48.5% .15 253.3);--color-blue-800:oklch(41% .125 253.3);--color-blue-900:oklch(35.5% .1 253.3);--color-blue-950:oklch(24% .07 253.3);--color-violet-50:oklch(96.9% .016 300);--color-violet-300:oklch(82.7% .104 300);--color-violet-700:oklch(49.6% .19 300);--color-violet-950:oklch(28.3% .109 300);--color-slate-50:oklch(98.4% .003 253.3);--color-slate-100:oklch(96.4% .005 253.3);--color-slate-200:oklch(92.6% .009 253.3);--color-slate-300:oklch(86.6% .014 253.3);--color-slate-400:oklch(70.4% .021 253.3);--color-slate-500:oklch(55.6% .026 253.3);--color-slate-600:oklch(44.8% .028 253.3);--color-slate-700:oklch(36.8% .028 253.3);--color-slate-800:oklch(26.4% .026 253.3);--color-slate-900:oklch(20.4% .024 253.3);--color-slate-950:oklch(14.2% .022 253.3);--spacing:.25rem;--container-2xl:42rem;--text-xs:.6875rem;--text-xs--line-height:1.45;--text-sm:.8125rem;--text-sm--line-height:1.5;--text-base:.875rem;--text-base--line-height:1.6;--text-lg:1rem;--text-lg--line-height:1.5;--text-xl:1.25rem;--text-xl--line-height:1.3;--text-2xl:1.5rem;--text-2xl--line-height:1.2;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-widest:.12em;--leading-relaxed:1.625;--radius-xs:.125rem;--radius-sm:.25rem;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--default-transition-duration:.12s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-slate-0:oklch(100% 0 253.3);--spacing-control-x-sm:calc(var(--spacing) * 2);--spacing-control-y-sm:calc(var(--spacing) * 1);--spacing-control-x-md:calc(var(--spacing) * 3);--spacing-control-y-md:calc(var(--spacing) * 2);--spacing-control-x-lg:calc(var(--spacing) * 3.5);--spacing-control-y-lg:calc(var(--spacing) * 2.5);--spacing-control-h-sm:calc(var(--spacing) * 6.5);--spacing-control-h-md:calc(var(--spacing) * 9.5);--spacing-control-h-lg:calc(var(--spacing) * 9.5);--text-2xs:.625rem;--text-2xs--line-height:1.4;--tracking-tightest:-.03em}}@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;-webkit-text-decoration: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}:root{color-scheme:light}:root[data-theme=dark]{color-scheme:dark}html,body{height:100%}body{background-color:var(--ui-canvas);color:var(--ui-fg);font-family:var(--font-sans);font-size:var(--text-base);line-height:var(--text-base--line-height)}::selection{background-color:var(--ui-accent-surface);color:var(--ui-accent)}:focus-visible{outline:2px solid var(--ui-accent);outline-offset:1px}}@layer components{.prose{overflow-wrap:anywhere}.prose>:first-child{margin-top:0}.prose>:last-child{margin-bottom:0}.prose h1,.prose h2,.prose h3,.prose h4{margin:calc(var(--spacing) * 6) 0 calc(var(--spacing) * 2);letter-spacing:var(--tracking-tightest);font-weight:650;line-height:1.25}.prose h1{font-size:var(--text-lg)}.prose h2{font-size:var(--text-base);padding-bottom:calc(var(--spacing) * 1.5);border-bottom:1px solid var(--ui-line)}.prose h3,.prose h4{font-size:var(--text-base);color:var(--ui-fg-muted)}.prose p{margin:calc(var(--spacing) * 3) 0}.prose ul,.prose ol{margin:calc(var(--spacing) * 3) 0;padding-left:calc(var(--spacing) * 5)}.prose ul{list-style-type:"– "}.prose ol{list-style-type:decimal}.prose li{margin:calc(var(--spacing) * 1) 0}.prose li::marker{color:var(--ui-accent);font-family:var(--font-mono)}.prose li>ul,.prose li>ol{margin:calc(var(--spacing) * 1) 0}.prose a{color:var(--ui-accent);text-underline-offset:2px;text-decoration:underline}.prose a:hover{color:var(--ui-accent-hover)}.prose strong{color:var(--ui-fg);font-weight:650}.prose pre{margin:calc(var(--spacing) * 3) 0;padding:calc(var(--spacing) * 3);border:1px solid var(--ui-line);border-radius:var(--radius-md);background-color:var(--ui-surface-sunken);font-size:var(--text-xs);line-height:1.6;overflow-x:auto}.prose code{font-family:var(--font-mono);font-size:.9em}.prose :not(pre)>code{border:1px solid var(--ui-line);border-radius:var(--radius-sm);background-color:var(--ui-surface-sunken);color:var(--ui-accent);padding:1px 4px}.prose blockquote{margin:calc(var(--spacing) * 3) 0;padding-left:calc(var(--spacing) * 3);border-left:2px solid var(--ui-accent-line);color:var(--ui-fg-muted)}.prose hr{margin:calc(var(--spacing) * 5) 0;border:0;border-top:1px dashed var(--ui-line-strong)}.prose table{margin:calc(var(--spacing) * 3) 0;border-collapse:collapse;font-size:var(--text-sm);display:block;overflow-x:auto}.prose th,.prose td{padding:calc(var(--spacing) * 1.5) calc(var(--spacing) * 2.5);border:1px solid var(--ui-line);text-align:left}.prose th{background-color:var(--ui-surface-sunken);font-weight:600}.graph svg{cursor:grab;width:100%;display:block}.graph .node circle{cursor:pointer;stroke-width:1.5px}.graph .node circle.hit{fill:#0000;stroke:none}.graph .node text{fill:var(--ui-fg-muted);font-family:var(--font-mono);pointer-events:none;font-size:9px}.graph .node.hi circle{stroke:var(--ui-accent);stroke-width:2.5px}.graph .node.hi text{fill:var(--ui-fg);font-weight:600}.graph .node.dim{opacity:.18}.graph .node.dim text{display:none}.graph .edge{stroke:var(--ui-line-strong)}.graph .edge.dim{opacity:.05}}@layer utilities{.collapse{visibility:collapse}.invisible{visibility:hidden}.visible{visibility:visible}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.inset-y-0{inset-block:0}.top-0{top:0}.top-2{top:calc(var(--spacing) * 2)}.top-3{top:calc(var(--spacing) * 3)}.top-3\.5{top:calc(var(--spacing) * 3.5)}.right-3\.5{right:calc(var(--spacing) * 3.5)}.bottom-2{bottom:calc(var(--spacing) * 2)}.bottom-3{bottom:calc(var(--spacing) * 3)}.bottom-5{bottom:calc(var(--spacing) * 5)}.left-0{left:0}.left-1\/2{left:50%}.left-3\.5{left:calc(var(--spacing) * 3.5)}.z-10{z-index:10}.z-20{z-index:20}.z-200{z-index:200}.float-right{float:right}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.m-0{margin:0}.m-auto{margin:auto}.my-1{margin-block:var(--spacing)}.my-2\.5{margin-block:calc(var(--spacing) * 2.5)}.mt-0{margin-top:0}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:var(--spacing)}.mt-1\.5{margin-top:calc(var(--spacing) * 1.5)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-2\.5{margin-top:calc(var(--spacing) * 2.5)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mt-5{margin-top:calc(var(--spacing) * 5)}.mr-1\.5{margin-right:calc(var(--spacing) * 1.5)}.-mb-px{margin-bottom:-1px}.mb-0\.5{margin-bottom:calc(var(--spacing) * .5)}.mb-1{margin-bottom:var(--spacing)}.mb-1\.5{margin-bottom:calc(var(--spacing) * 1.5)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-2\.5{margin-bottom:calc(var(--spacing) * 2.5)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-3\.5{margin-bottom:calc(var(--spacing) * 3.5)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-5{margin-bottom:calc(var(--spacing) * 5)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.ml-1\.5{margin-left:calc(var(--spacing) * 1.5)}.ml-2{margin-left:calc(var(--spacing) * 2)}.line-clamp-2{-webkit-line-clamp:2;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.block{display:block}.contents{display:contents}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.size-1\.5{width:calc(var(--spacing) * 1.5);height:calc(var(--spacing) * 1.5)}.size-2{width:calc(var(--spacing) * 2);height:calc(var(--spacing) * 2)}.size-3{width:calc(var(--spacing) * 3);height:calc(var(--spacing) * 3)}.h-2{height:calc(var(--spacing) * 2)}.h-3{height:calc(var(--spacing) * 3)}.h-9{height:calc(var(--spacing) * 9)}.h-full{height:100%}.h-screen{height:100vh}.max-h-\[28rem\]{max-height:28rem}.max-h-\[82vh\]{max-height:82vh}.min-h-20{min-height:calc(var(--spacing) * 20)}.min-h-control-h-lg{min-height:var(--spacing-control-h-lg)}.min-h-control-h-md{min-height:var(--spacing-control-h-md)}.min-h-control-h-sm{min-height:var(--spacing-control-h-sm)}.w-0\.5{width:calc(var(--spacing) * .5)}.w-3{width:calc(var(--spacing) * 3)}.w-20{width:calc(var(--spacing) * 20)}.w-24{width:calc(var(--spacing) * 24)}.w-\[calc\(100\%-3rem\)\]{width:calc(100% - 3rem)}.w-full{width:100%}.max-w-2xl{max-width:var(--container-2xl)}.min-w-0{min-width:0}.min-w-6{min-width:calc(var(--spacing) * 6)}.min-w-control-h-sm{min-width:var(--spacing-control-h-sm)}.flex-1{flex:1}.flex-none{flex:none}.grow{flex-grow:1}.-translate-x-1\/2{--tw-translate-x:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.cursor-default{cursor:default}.cursor-help{cursor:help}.cursor-pointer{cursor:pointer}.resize-y{resize:vertical}.grid-flow-col{grid-auto-flow:column}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-\[3rem_1fr\]{grid-template-columns:3rem 1fr}.grid-cols-\[280px_1fr\]{grid-template-columns:280px 1fr}.grid-cols-\[auto_1fr\]{grid-template-columns:auto 1fr}.grid-rows-7{grid-template-rows:repeat(7,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-baseline{align-items:baseline}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-start{justify-content:flex-start}.gap-0\.5{gap:calc(var(--spacing) * .5)}.gap-1{gap:var(--spacing)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-2\.5{gap:calc(var(--spacing) * 2.5)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-3\.5{gap:calc(var(--spacing) * 3.5)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-5{gap:calc(var(--spacing) * 5)}.gap-6{gap:calc(var(--spacing) * 6)}.gap-px{gap:1px}.gap-x-3\.5{column-gap:calc(var(--spacing) * 3.5)}.gap-y-1{row-gap:var(--spacing)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-sm{border-radius:var(--radius-sm)}.rounded-xl{border-radius:var(--radius-xl)}.rounded-xs{border-radius:var(--radius-xs)}.rounded-r-md{border-top-right-radius:var(--radius-md);border-bottom-right-radius:var(--radius-md)}.border{border-style:var(--tw-border-style);border-width:1px}.border-0{border-style:var(--tw-border-style);border-width:0}.border-y{border-block-style:var(--tw-border-style);border-block-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-b-2{border-bottom-style:var(--tw-border-style);border-bottom-width:2px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-l-0{border-left-style:var(--tw-border-style);border-left-width:0}.border-l-2{border-left-style:var(--tw-border-style);border-left-width:2px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-none{--tw-border-style:none;border-style:none}.border-accent{border-color:var(--ui-accent)}.border-accent-line{border-color:var(--ui-accent-line)}.border-current{border-color:currentColor}.border-danger{border-color:var(--ui-danger)}.border-danger-line{border-color:var(--ui-danger-line)}.border-line{border-color:var(--ui-line)}.border-transparent{border-color:#0000}.border-l-danger{border-left-color:var(--ui-danger)}.border-l-warn{border-left-color:var(--ui-warn)}.bg-accent{background-color:var(--ui-accent)}.bg-accent-surface{background-color:var(--ui-accent-surface)}.bg-canvas{background-color:var(--ui-canvas)}.bg-danger{background-color:var(--ui-danger)}.bg-danger-surface{background-color:var(--ui-danger-surface)}.bg-fg{background-color:var(--ui-fg)}.bg-heat-0{background-color:var(--ui-heat-0)}.bg-heat-1{background-color:var(--ui-heat-1)}.bg-heat-2{background-color:var(--ui-heat-2)}.bg-heat-3{background-color:var(--ui-heat-3)}.bg-heat-4{background-color:var(--ui-heat-4)}.bg-line-strong{background-color:var(--ui-line-strong)}.bg-ok{background-color:var(--ui-ok)}.bg-ok-surface{background-color:var(--ui-ok-surface)}.bg-scope-local-surface{background-color:var(--ui-scope-local-surface)}.bg-scope-managed-surface{background-color:var(--ui-scope-managed-surface)}.bg-scope-project-surface{background-color:var(--ui-scope-project-surface)}.bg-scope-user-surface{background-color:var(--ui-scope-user-surface)}.bg-surface{background-color:var(--ui-surface)}.bg-surface-sunken{background-color:var(--ui-surface-sunken)}.bg-transparent{background-color:#0000}.bg-type-feedback-surface{background-color:var(--ui-type-feedback-surface)}.bg-type-project-surface{background-color:var(--ui-type-project-surface)}.bg-type-reference-surface{background-color:var(--ui-type-reference-surface)}.bg-type-user-surface{background-color:var(--ui-type-user-surface)}.bg-warn{background-color:var(--ui-warn)}.bg-warn-surface{background-color:var(--ui-warn-surface)}.p-0{padding:0}.p-0\.5{padding:calc(var(--spacing) * .5)}.p-2{padding:calc(var(--spacing) * 2)}.p-4{padding:calc(var(--spacing) * 4)}.p-5{padding:calc(var(--spacing) * 5)}.p-10{padding:calc(var(--spacing) * 10)}.px-1{padding-inline:var(--spacing)}.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-3\.5{padding-inline:calc(var(--spacing) * 3.5)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-5{padding-inline:calc(var(--spacing) * 5)}.px-6{padding-inline:calc(var(--spacing) * 6)}.px-7{padding-inline:calc(var(--spacing) * 7)}.px-10{padding-inline:calc(var(--spacing) * 10)}.px-control-x-lg{padding-inline:var(--spacing-control-x-lg)}.px-control-x-md{padding-inline:var(--spacing-control-x-md)}.px-control-x-sm{padding-inline:var(--spacing-control-x-sm)}.px-px{padding-inline:1px}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.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)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-3\.5{padding-block:calc(var(--spacing) * 3.5)}.py-4{padding-block:calc(var(--spacing) * 4)}.py-\[15vh\]{padding-block:15vh}.py-control-y-lg{padding-block:var(--spacing-control-y-lg)}.py-control-y-md{padding-block:var(--spacing-control-y-md)}.py-control-y-sm{padding-block:var(--spacing-control-y-sm)}.py-px{padding-block:1px}.pt-3{padding-top:calc(var(--spacing) * 3)}.pt-3\.5{padding-top:calc(var(--spacing) * 3.5)}.pt-4{padding-top:calc(var(--spacing) * 4)}.pt-5{padding-top:calc(var(--spacing) * 5)}.pt-6{padding-top:calc(var(--spacing) * 6)}.pb-1{padding-bottom:var(--spacing)}.pb-3{padding-bottom:calc(var(--spacing) * 3)}.pb-3\.5{padding-bottom:calc(var(--spacing) * 3.5)}.pb-4\.5{padding-bottom:calc(var(--spacing) * 4.5)}.pb-16{padding-bottom:calc(var(--spacing) * 16)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.font-mono{font-family:var(--font-mono)}.font-sans{font-family:var(--font-sans)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-2xs{font-size:var(--text-2xs);line-height:var(--tw-leading,var(--text-2xs--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))}.leading-3{--tw-leading:calc(var(--spacing) * 3);line-height:calc(var(--spacing) * 3)}.leading-none{--tw-leading:1;line-height:1}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.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-tightest{--tw-tracking:var(--tracking-tightest);letter-spacing:var(--tracking-tightest)}.tracking-widest{--tw-tracking:var(--tracking-widest);letter-spacing:var(--tracking-widest)}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.text-accent{color:var(--ui-accent)}.text-canvas{color:var(--ui-canvas)}.text-danger{color:var(--ui-danger)}.text-fg{color:var(--ui-fg)}.text-fg-muted{color:var(--ui-fg-muted)}.text-fg-subtle{color:var(--ui-fg-subtle)}.text-ok{color:var(--ui-ok)}.text-on-accent{color:var(--ui-on-accent)}.text-scope-local{color:var(--ui-scope-local)}.text-scope-managed{color:var(--ui-scope-managed)}.text-scope-project{color:var(--ui-scope-project)}.text-scope-user{color:var(--ui-scope-user)}.text-type-feedback{color:var(--ui-type-feedback)}.text-type-project{color:var(--ui-type-project)}.text-type-reference{color:var(--ui-type-reference)}.text-type-user{color:var(--ui-type-user)}.text-warn{color:var(--ui-warn)}.lowercase{text-transform:lowercase}.uppercase{text-transform:uppercase}.line-through{text-decoration-line:line-through}.no-underline{text-decoration-line:none}.underline{text-decoration-line:underline}.decoration-danger{-webkit-text-decoration-color:var(--ui-danger);-webkit-text-decoration-color:var(--ui-danger);text-decoration-color:var(--ui-danger)}.decoration-line-strong{-webkit-text-decoration-color:var(--ui-line-strong);-webkit-text-decoration-color:var(--ui-line-strong);text-decoration-color:var(--ui-line-strong)}.accent-accent{accent-color:var(--ui-accent)}.opacity-45{opacity:.45}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.shadow-card{--tw-shadow:var(--ui-shadow-card);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-pop{--tw-shadow:var(--ui-shadow-pop);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.outline-2{outline-style:var(--tw-outline-style);outline-width:2px}.outline-offset-1{outline-offset:1px}.outline-fg{outline-color:var(--ui-fg)}.outline-transparent{outline-color:#0000}.transition-\[outline-color\]{transition-property:outline-color;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[width\]{transition-property:width;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.select-none{-webkit-user-select:none;user-select:none}.placeholder\:text-fg-subtle::placeholder{color:var(--ui-fg-subtle)}.backdrop\:bg-scrim::backdrop{background-color:var(--ui-scrim)}.before\:flex-1:before{content:var(--tw-content);flex:1}.before\:border-t:before{content:var(--tw-content);border-top-style:var(--tw-border-style);border-top-width:1px}.before\:border-dashed:before{content:var(--tw-content);--tw-border-style:dashed;border-style:dashed}.before\:border-danger:before{content:var(--tw-content);border-color:var(--ui-danger)}.after\:flex-1:after{content:var(--tw-content);flex:1}.after\:border-t:after{content:var(--tw-content);border-top-style:var(--tw-border-style);border-top-width:1px}.after\:border-dashed:after{content:var(--tw-content);--tw-border-style:dashed;border-style:dashed}.after\:border-danger:after{content:var(--tw-content);border-color:var(--ui-danger)}.first\:mt-0:first-child{margin-top:0}.last\:border-b-0:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.open\:flex:is([open],:popover-open,:open){display:flex}@media (hover:hover){.hover\:border-accent:hover{border-color:var(--ui-accent)}.hover\:border-accent-hover:hover{border-color:var(--ui-accent-hover)}.hover\:border-accent-line:hover{border-color:var(--ui-accent-line)}.hover\:border-line-strong:hover{border-color:var(--ui-line-strong)}.hover\:bg-accent-hover:hover{background-color:var(--ui-accent-hover)}.hover\:bg-danger:hover{background-color:var(--ui-danger)}.hover\:bg-surface-sunken:hover{background-color:var(--ui-surface-sunken)}.hover\:text-accent-hover:hover{color:var(--ui-accent-hover)}.hover\:text-fg:hover{color:var(--ui-fg)}.hover\:text-on-accent:hover{color:var(--ui-on-accent)}.hover\:underline:hover{text-decoration-line:underline}.hover\:outline-fg-subtle:hover{outline-color:var(--ui-fg-subtle)}}.focus\:border-accent:focus{border-color:var(--ui-accent)}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-45:disabled{opacity:.45}@media (min-width:48rem){.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (min-width:64rem){.lg\:grid-cols-\[minmax\(0\,1fr\)_auto\]{grid-template-columns:minmax(0,1fr) auto}.lg\:gap-14{gap:calc(var(--spacing) * 14)}}@media (min-width:80rem){.xl\:sticky{position:sticky}.xl\:top-4{top:calc(var(--spacing) * 4)}.xl\:max-h-\[calc\(100vh-9rem\)\]{max-height:calc(100vh - 9rem)}.xl\:grid-cols-\[minmax\(280px\,380px\)_1fr\]{grid-template-columns:minmax(280px,380px) 1fr}.xl\:overflow-y-auto{overflow-y:auto}}.dark\:invert:where([data-theme=dark],[data-theme=dark] *){--tw-invert:invert(100%);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,)}.\[\&\>span\]\:block>span{display:block}}:root{--ui-canvas:var(--color-slate-50);--ui-surface:var(--color-slate-0);--ui-surface-sunken:var(--color-slate-100);--ui-surface-raised:var(--color-slate-0);--ui-line:var(--color-slate-200);--ui-line-strong:var(--color-slate-300);--ui-fg:var(--color-slate-900);--ui-fg-muted:var(--color-slate-500);--ui-fg-subtle:var(--color-slate-400);--ui-accent:var(--color-blue-600);--ui-accent-hover:var(--color-blue-700);--ui-accent-surface:var(--color-blue-50);--ui-accent-line:var(--color-blue-200);--ui-on-accent:var(--color-slate-0);--ui-danger:var(--color-red-600);--ui-danger-surface:var(--color-red-50);--ui-danger-line:var(--color-red-300);--ui-warn:var(--color-amber-700);--ui-warn-surface:var(--color-amber-50);--ui-warn-line:var(--color-amber-300);--ui-ok:var(--color-green-600);--ui-ok-surface:var(--color-green-50);--ui-ok-line:var(--color-green-300);--ui-heat-0:var(--color-slate-200);--ui-heat-1:var(--color-blue-200);--ui-heat-2:var(--color-blue-400);--ui-heat-3:var(--color-blue-600);--ui-heat-4:var(--color-blue-800);--ui-type-project:var(--color-blue-700);--ui-type-project-surface:var(--color-blue-50);--ui-type-feedback:var(--color-amber-700);--ui-type-feedback-surface:var(--color-amber-50);--ui-type-user:var(--color-violet-700);--ui-type-user-surface:var(--color-violet-50);--ui-type-reference:var(--color-green-700);--ui-type-reference-surface:var(--color-green-50);--ui-scope-managed:var(--color-red-600);--ui-scope-managed-surface:var(--color-red-50);--ui-scope-user:var(--color-blue-700);--ui-scope-user-surface:var(--color-blue-50);--ui-scope-project:var(--color-amber-700);--ui-scope-project-surface:var(--color-amber-50);--ui-scope-local:var(--color-slate-600);--ui-scope-local-surface:var(--color-slate-100);--ui-shadow-card:0 1px 2px #0000000d;--ui-shadow-pop:0 1px 2px #0000000f, 0 12px 32px #0000001a;--ui-scrim:#0f172a73}:root[data-theme=dark]{--ui-canvas:var(--color-slate-950);--ui-surface:var(--color-slate-900);--ui-surface-sunken:var(--color-slate-950);--ui-surface-raised:var(--color-slate-800);--ui-line:var(--color-slate-800);--ui-line-strong:var(--color-slate-700);--ui-fg:var(--color-slate-100);--ui-fg-muted:var(--color-slate-400);--ui-fg-subtle:var(--color-slate-500);--ui-accent:var(--color-blue-400);--ui-accent-hover:var(--color-blue-300);--ui-accent-surface:var(--color-blue-950);--ui-accent-line:var(--color-blue-800);--ui-on-accent:var(--color-slate-950);--ui-danger:var(--color-red-400);--ui-danger-surface:var(--color-red-950);--ui-danger-line:var(--color-red-500);--ui-warn:var(--color-amber-400);--ui-warn-surface:var(--color-amber-950);--ui-warn-line:var(--color-amber-600);--ui-ok:var(--color-green-400);--ui-ok-surface:var(--color-green-950);--ui-ok-line:var(--color-green-600);--ui-heat-0:var(--color-slate-800);--ui-heat-1:var(--color-blue-900);--ui-heat-2:var(--color-blue-700);--ui-heat-3:var(--color-blue-500);--ui-heat-4:var(--color-blue-300);--ui-type-project:var(--color-blue-300);--ui-type-project-surface:var(--color-blue-950);--ui-type-feedback:var(--color-amber-300);--ui-type-feedback-surface:var(--color-amber-950);--ui-type-user:var(--color-violet-300);--ui-type-user-surface:var(--color-violet-950);--ui-type-reference:var(--color-green-300);--ui-type-reference-surface:var(--color-green-950);--ui-scope-managed:var(--color-red-400);--ui-scope-managed-surface:var(--color-red-950);--ui-scope-user:var(--color-blue-300);--ui-scope-user-surface:var(--color-blue-950);--ui-scope-project:var(--color-amber-300);--ui-scope-project-surface:var(--color-amber-950);--ui-scope-local:var(--color-slate-400);--ui-scope-local-surface:var(--color-slate-800);--ui-shadow-card:0 1px 2px #0006;--ui-shadow-pop:0 1px 2px #00000080, 0 12px 32px #00000073;--ui-scrim:#020610b3}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@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-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-content{syntax:"*";inherits:false;initial-value:""}@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}
|
|
2
|
+
@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-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-border-style:solid;--tw-leading:initial;--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-outline-style:solid;--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;--tw-content:""}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;--font-mono:ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;--color-red-50:oklch(97.1% .013 17.4);--color-red-300:oklch(80.8% .114 19.6);--color-red-400:oklch(70.4% .16 22.2);--color-red-500:oklch(63.7% .208 25.3);--color-red-600:oklch(57.7% .215 27.3);--color-red-950:oklch(25.8% .092 26);--color-amber-50:oklch(97.7% .021 84.6);--color-amber-300:oklch(87.9% .153 91.6);--color-amber-400:oklch(82.8% .171 84.4);--color-amber-600:oklch(66.6% .15 58.3);--color-amber-700:oklch(55.5% .13 49);--color-amber-950:oklch(27.9% .077 45.6);--color-green-50:oklch(98.2% .018 155.8);--color-green-300:oklch(87.1% .12 154.4);--color-green-400:oklch(79.2% .15 151.7);--color-green-600:oklch(58.5% .145 149.2);--color-green-700:oklch(52.7% .132 150.1);--color-green-950:oklch(26.6% .065 152.9);--color-blue-50:oklch(97% .014 253.3);--color-blue-200:oklch(88% .06 253.3);--color-blue-300:oklch(78.4% .124 239.1);--color-blue-400:oklch(75.2% .138 243.7);--color-blue-500:oklch(65.8% .152 248.5);--color-blue-600:oklch(56.5% .163 253.3);--color-blue-700:oklch(48.5% .15 253.3);--color-blue-800:oklch(41% .125 253.3);--color-blue-900:oklch(35.5% .1 253.3);--color-blue-950:oklch(24% .07 253.3);--color-violet-50:oklch(96.9% .016 300);--color-violet-300:oklch(82.7% .104 300);--color-violet-700:oklch(49.6% .19 300);--color-violet-950:oklch(28.3% .109 300);--color-slate-50:oklch(98.4% .003 253.3);--color-slate-100:oklch(96.4% .005 253.3);--color-slate-200:oklch(92.6% .009 253.3);--color-slate-300:oklch(86.6% .014 253.3);--color-slate-400:oklch(70.4% .021 253.3);--color-slate-500:oklch(55.6% .026 253.3);--color-slate-600:oklch(44.8% .028 253.3);--color-slate-700:oklch(36.8% .028 253.3);--color-slate-800:oklch(26.4% .026 253.3);--color-slate-900:oklch(20.4% .024 253.3);--color-slate-950:oklch(14.2% .022 253.3);--spacing:.25rem;--container-2xl:42rem;--text-xs:.6875rem;--text-xs--line-height:1.45;--text-sm:.8125rem;--text-sm--line-height:1.5;--text-base:.875rem;--text-base--line-height:1.6;--text-lg:1rem;--text-lg--line-height:1.5;--text-xl:1.25rem;--text-xl--line-height:1.3;--text-2xl:1.5rem;--text-2xl--line-height:1.2;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-widest:.12em;--leading-relaxed:1.625;--radius-xs:.125rem;--radius-sm:.25rem;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--default-transition-duration:.12s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-slate-0:oklch(100% 0 253.3);--spacing-control-x-sm:calc(var(--spacing) * 2);--spacing-control-y-sm:calc(var(--spacing) * 1);--spacing-control-x-md:calc(var(--spacing) * 3);--spacing-control-y-md:calc(var(--spacing) * 2);--spacing-control-x-lg:calc(var(--spacing) * 3.5);--spacing-control-y-lg:calc(var(--spacing) * 2.5);--spacing-control-h-sm:calc(var(--spacing) * 6.5);--spacing-control-h-md:calc(var(--spacing) * 9.5);--spacing-control-h-lg:calc(var(--spacing) * 9.5);--text-2xs:.625rem;--text-2xs--line-height:1.4;--tracking-tightest:-.03em}}@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;-webkit-text-decoration: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}:root{color-scheme:light}:root[data-theme=dark]{color-scheme:dark}html,body{height:100%}body{background-color:var(--ui-canvas);color:var(--ui-fg);font-family:var(--font-sans);font-size:var(--text-base);line-height:var(--text-base--line-height)}::selection{background-color:var(--ui-accent-surface);color:var(--ui-accent)}:focus-visible{outline:2px solid var(--ui-accent);outline-offset:1px}}@layer components{.prose{overflow-wrap:anywhere}.prose>:first-child{margin-top:0}.prose>:last-child{margin-bottom:0}.prose h1,.prose h2,.prose h3,.prose h4{margin:calc(var(--spacing) * 6) 0 calc(var(--spacing) * 2);letter-spacing:var(--tracking-tightest);font-weight:650;line-height:1.25}.prose h1{font-size:var(--text-lg)}.prose h2{font-size:var(--text-base);padding-bottom:calc(var(--spacing) * 1.5);border-bottom:1px solid var(--ui-line)}.prose h3,.prose h4{font-size:var(--text-base);color:var(--ui-fg-muted)}.prose p{margin:calc(var(--spacing) * 3) 0}.prose ul,.prose ol{margin:calc(var(--spacing) * 3) 0;padding-left:calc(var(--spacing) * 5)}.prose ul{list-style-type:"– "}.prose ol{list-style-type:decimal}.prose li{margin:calc(var(--spacing) * 1) 0}.prose li::marker{color:var(--ui-accent);font-family:var(--font-mono)}.prose li>ul,.prose li>ol{margin:calc(var(--spacing) * 1) 0}.prose a{color:var(--ui-accent);text-underline-offset:2px;text-decoration:underline}.prose a:hover{color:var(--ui-accent-hover)}.prose strong{color:var(--ui-fg);font-weight:650}.prose pre{margin:calc(var(--spacing) * 3) 0;padding:calc(var(--spacing) * 3);border:1px solid var(--ui-line);border-radius:var(--radius-md);background-color:var(--ui-surface-sunken);font-size:var(--text-xs);line-height:1.6;overflow-x:auto}.prose code{font-family:var(--font-mono);font-size:.9em}.prose :not(pre)>code{border:1px solid var(--ui-line);border-radius:var(--radius-sm);background-color:var(--ui-surface-sunken);color:var(--ui-accent);padding:1px 4px}.prose blockquote{margin:calc(var(--spacing) * 3) 0;padding-left:calc(var(--spacing) * 3);border-left:2px solid var(--ui-accent-line);color:var(--ui-fg-muted)}.prose hr{margin:calc(var(--spacing) * 5) 0;border:0;border-top:1px dashed var(--ui-line-strong)}.prose table{margin:calc(var(--spacing) * 3) 0;border-collapse:collapse;font-size:var(--text-sm);display:block;overflow-x:auto}.prose th,.prose td{padding:calc(var(--spacing) * 1.5) calc(var(--spacing) * 2.5);border:1px solid var(--ui-line);text-align:left}.prose th{background-color:var(--ui-surface-sunken);font-weight:600}.graph svg{cursor:grab;width:100%;display:block}.graph .node circle{cursor:pointer;stroke-width:1.5px}.graph .node circle.hit{fill:#0000;stroke:none}.graph .node text{fill:var(--ui-fg-muted);font-family:var(--font-mono);pointer-events:none;font-size:9px}.graph .node.hi circle{stroke:var(--ui-accent);stroke-width:2.5px}.graph .node.hi text{fill:var(--ui-fg);font-weight:600}.graph .node.dim{opacity:.18}.graph .node.dim text{display:none}.graph .edge{stroke:var(--ui-line-strong)}.graph .edge.dim{opacity:.05}}@layer utilities{.collapse{visibility:collapse}.invisible{visibility:hidden}.visible{visibility:visible}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.inset-y-0{inset-block:0}.top-0{top:0}.top-2{top:calc(var(--spacing) * 2)}.top-3{top:calc(var(--spacing) * 3)}.top-3\.5{top:calc(var(--spacing) * 3.5)}.right-3\.5{right:calc(var(--spacing) * 3.5)}.bottom-2{bottom:calc(var(--spacing) * 2)}.bottom-3{bottom:calc(var(--spacing) * 3)}.bottom-5{bottom:calc(var(--spacing) * 5)}.left-0{left:0}.left-1\/2{left:50%}.left-3\.5{left:calc(var(--spacing) * 3.5)}.z-10{z-index:10}.z-20{z-index:20}.z-200{z-index:200}.float-right{float:right}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.m-0{margin:0}.m-auto{margin:auto}.my-1{margin-block:var(--spacing)}.my-2\.5{margin-block:calc(var(--spacing) * 2.5)}.mt-0{margin-top:0}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:var(--spacing)}.mt-1\.5{margin-top:calc(var(--spacing) * 1.5)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-2\.5{margin-top:calc(var(--spacing) * 2.5)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mt-5{margin-top:calc(var(--spacing) * 5)}.mr-1\.5{margin-right:calc(var(--spacing) * 1.5)}.-mb-px{margin-bottom:-1px}.mb-0\.5{margin-bottom:calc(var(--spacing) * .5)}.mb-1{margin-bottom:var(--spacing)}.mb-1\.5{margin-bottom:calc(var(--spacing) * 1.5)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-2\.5{margin-bottom:calc(var(--spacing) * 2.5)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-3\.5{margin-bottom:calc(var(--spacing) * 3.5)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-5{margin-bottom:calc(var(--spacing) * 5)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.ml-1\.5{margin-left:calc(var(--spacing) * 1.5)}.ml-2{margin-left:calc(var(--spacing) * 2)}.line-clamp-2{-webkit-line-clamp:2;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.block{display:block}.contents{display:contents}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.size-1\.5{width:calc(var(--spacing) * 1.5);height:calc(var(--spacing) * 1.5)}.size-2{width:calc(var(--spacing) * 2);height:calc(var(--spacing) * 2)}.size-3{width:calc(var(--spacing) * 3);height:calc(var(--spacing) * 3)}.h-2{height:calc(var(--spacing) * 2)}.h-3{height:calc(var(--spacing) * 3)}.h-9{height:calc(var(--spacing) * 9)}.h-full{height:100%}.h-screen{height:100vh}.max-h-\[28rem\]{max-height:28rem}.max-h-\[82vh\]{max-height:82vh}.min-h-20{min-height:calc(var(--spacing) * 20)}.min-h-control-h-lg{min-height:var(--spacing-control-h-lg)}.min-h-control-h-md{min-height:var(--spacing-control-h-md)}.min-h-control-h-sm{min-height:var(--spacing-control-h-sm)}.w-0\.5{width:calc(var(--spacing) * .5)}.w-3{width:calc(var(--spacing) * 3)}.w-20{width:calc(var(--spacing) * 20)}.w-24{width:calc(var(--spacing) * 24)}.w-\[calc\(100\%-3rem\)\]{width:calc(100% - 3rem)}.w-full{width:100%}.max-w-2xl{max-width:var(--container-2xl)}.min-w-0{min-width:0}.min-w-6{min-width:calc(var(--spacing) * 6)}.min-w-control-h-sm{min-width:var(--spacing-control-h-sm)}.flex-1{flex:1}.flex-none{flex:none}.grow{flex-grow:1}.-translate-x-1\/2{--tw-translate-x:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.cursor-default{cursor:default}.cursor-help{cursor:help}.cursor-pointer{cursor:pointer}.resize-y{resize:vertical}.grid-flow-col{grid-auto-flow:column}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-\[3rem_1fr\]{grid-template-columns:3rem 1fr}.grid-cols-\[280px_1fr\]{grid-template-columns:280px 1fr}.grid-cols-\[auto_1fr\]{grid-template-columns:auto 1fr}.grid-rows-7{grid-template-rows:repeat(7,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-baseline{align-items:baseline}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-start{justify-content:flex-start}.gap-0\.5{gap:calc(var(--spacing) * .5)}.gap-1{gap:var(--spacing)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-2\.5{gap:calc(var(--spacing) * 2.5)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-3\.5{gap:calc(var(--spacing) * 3.5)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-5{gap:calc(var(--spacing) * 5)}.gap-6{gap:calc(var(--spacing) * 6)}.gap-px{gap:1px}.gap-x-3\.5{column-gap:calc(var(--spacing) * 3.5)}.gap-y-1{row-gap:var(--spacing)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-sm{border-radius:var(--radius-sm)}.rounded-xl{border-radius:var(--radius-xl)}.rounded-xs{border-radius:var(--radius-xs)}.rounded-r-md{border-top-right-radius:var(--radius-md);border-bottom-right-radius:var(--radius-md)}.border{border-style:var(--tw-border-style);border-width:1px}.border-0{border-style:var(--tw-border-style);border-width:0}.border-y{border-block-style:var(--tw-border-style);border-block-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-b-2{border-bottom-style:var(--tw-border-style);border-bottom-width:2px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-l-0{border-left-style:var(--tw-border-style);border-left-width:0}.border-l-2{border-left-style:var(--tw-border-style);border-left-width:2px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-none{--tw-border-style:none;border-style:none}.border-accent{border-color:var(--ui-accent)}.border-accent-line{border-color:var(--ui-accent-line)}.border-current{border-color:currentColor}.border-danger{border-color:var(--ui-danger)}.border-danger-line{border-color:var(--ui-danger-line)}.border-line{border-color:var(--ui-line)}.border-transparent{border-color:#0000}.border-l-danger{border-left-color:var(--ui-danger)}.border-l-warn{border-left-color:var(--ui-warn)}.bg-accent{background-color:var(--ui-accent)}.bg-accent-surface{background-color:var(--ui-accent-surface)}.bg-canvas{background-color:var(--ui-canvas)}.bg-danger{background-color:var(--ui-danger)}.bg-danger-surface{background-color:var(--ui-danger-surface)}.bg-fg{background-color:var(--ui-fg)}.bg-heat-0{background-color:var(--ui-heat-0)}.bg-heat-1{background-color:var(--ui-heat-1)}.bg-heat-2{background-color:var(--ui-heat-2)}.bg-heat-3{background-color:var(--ui-heat-3)}.bg-heat-4{background-color:var(--ui-heat-4)}.bg-line-strong{background-color:var(--ui-line-strong)}.bg-ok{background-color:var(--ui-ok)}.bg-ok-surface{background-color:var(--ui-ok-surface)}.bg-scope-local-surface{background-color:var(--ui-scope-local-surface)}.bg-scope-managed-surface{background-color:var(--ui-scope-managed-surface)}.bg-scope-project-surface{background-color:var(--ui-scope-project-surface)}.bg-scope-user-surface{background-color:var(--ui-scope-user-surface)}.bg-surface{background-color:var(--ui-surface)}.bg-surface-sunken{background-color:var(--ui-surface-sunken)}.bg-transparent{background-color:#0000}.bg-type-feedback-surface{background-color:var(--ui-type-feedback-surface)}.bg-type-project-surface{background-color:var(--ui-type-project-surface)}.bg-type-reference-surface{background-color:var(--ui-type-reference-surface)}.bg-type-user-surface{background-color:var(--ui-type-user-surface)}.bg-warn{background-color:var(--ui-warn)}.bg-warn-surface{background-color:var(--ui-warn-surface)}.p-0{padding:0}.p-0\.5{padding:calc(var(--spacing) * .5)}.p-2{padding:calc(var(--spacing) * 2)}.p-4{padding:calc(var(--spacing) * 4)}.p-5{padding:calc(var(--spacing) * 5)}.p-10{padding:calc(var(--spacing) * 10)}.px-1{padding-inline:var(--spacing)}.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-3\.5{padding-inline:calc(var(--spacing) * 3.5)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-5{padding-inline:calc(var(--spacing) * 5)}.px-6{padding-inline:calc(var(--spacing) * 6)}.px-7{padding-inline:calc(var(--spacing) * 7)}.px-10{padding-inline:calc(var(--spacing) * 10)}.px-control-x-lg{padding-inline:var(--spacing-control-x-lg)}.px-control-x-md{padding-inline:var(--spacing-control-x-md)}.px-control-x-sm{padding-inline:var(--spacing-control-x-sm)}.px-px{padding-inline:1px}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.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)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-3\.5{padding-block:calc(var(--spacing) * 3.5)}.py-4{padding-block:calc(var(--spacing) * 4)}.py-\[15vh\]{padding-block:15vh}.py-control-y-lg{padding-block:var(--spacing-control-y-lg)}.py-control-y-md{padding-block:var(--spacing-control-y-md)}.py-control-y-sm{padding-block:var(--spacing-control-y-sm)}.py-px{padding-block:1px}.pt-3{padding-top:calc(var(--spacing) * 3)}.pt-3\.5{padding-top:calc(var(--spacing) * 3.5)}.pt-4{padding-top:calc(var(--spacing) * 4)}.pt-5{padding-top:calc(var(--spacing) * 5)}.pt-6{padding-top:calc(var(--spacing) * 6)}.pb-1{padding-bottom:var(--spacing)}.pb-3{padding-bottom:calc(var(--spacing) * 3)}.pb-3\.5{padding-bottom:calc(var(--spacing) * 3.5)}.pb-4\.5{padding-bottom:calc(var(--spacing) * 4.5)}.pb-16{padding-bottom:calc(var(--spacing) * 16)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.font-mono{font-family:var(--font-mono)}.font-sans{font-family:var(--font-sans)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-2xs{font-size:var(--text-2xs);line-height:var(--tw-leading,var(--text-2xs--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))}.leading-3{--tw-leading:calc(var(--spacing) * 3);line-height:calc(var(--spacing) * 3)}.leading-none{--tw-leading:1;line-height:1}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.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-tightest{--tw-tracking:var(--tracking-tightest);letter-spacing:var(--tracking-tightest)}.tracking-widest{--tw-tracking:var(--tracking-widest);letter-spacing:var(--tracking-widest)}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.text-accent{color:var(--ui-accent)}.text-canvas{color:var(--ui-canvas)}.text-danger{color:var(--ui-danger)}.text-fg{color:var(--ui-fg)}.text-fg-muted{color:var(--ui-fg-muted)}.text-fg-subtle{color:var(--ui-fg-subtle)}.text-ok{color:var(--ui-ok)}.text-on-accent{color:var(--ui-on-accent)}.text-scope-local{color:var(--ui-scope-local)}.text-scope-managed{color:var(--ui-scope-managed)}.text-scope-project{color:var(--ui-scope-project)}.text-scope-user{color:var(--ui-scope-user)}.text-type-feedback{color:var(--ui-type-feedback)}.text-type-project{color:var(--ui-type-project)}.text-type-reference{color:var(--ui-type-reference)}.text-type-user{color:var(--ui-type-user)}.text-warn{color:var(--ui-warn)}.lowercase{text-transform:lowercase}.uppercase{text-transform:uppercase}.line-through{text-decoration-line:line-through}.no-underline{text-decoration-line:none}.underline{text-decoration-line:underline}.decoration-danger{-webkit-text-decoration-color:var(--ui-danger);-webkit-text-decoration-color:var(--ui-danger);text-decoration-color:var(--ui-danger)}.decoration-line-strong{-webkit-text-decoration-color:var(--ui-line-strong);-webkit-text-decoration-color:var(--ui-line-strong);text-decoration-color:var(--ui-line-strong)}.accent-accent{accent-color:var(--ui-accent)}.opacity-45{opacity:.45}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.shadow-card{--tw-shadow:var(--ui-shadow-card);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-pop{--tw-shadow:var(--ui-shadow-pop);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.outline-2{outline-style:var(--tw-outline-style);outline-width:2px}.outline-offset-1{outline-offset:1px}.outline-fg{outline-color:var(--ui-fg)}.outline-transparent{outline-color:#0000}.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-\[outline-color\]{transition-property:outline-color;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[width\]{transition-property:width;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.select-none{-webkit-user-select:none;user-select:none}.placeholder\:text-fg-subtle::placeholder{color:var(--ui-fg-subtle)}.backdrop\:bg-scrim::backdrop{background-color:var(--ui-scrim)}.before\:flex-1:before{content:var(--tw-content);flex:1}.before\:border-t:before{content:var(--tw-content);border-top-style:var(--tw-border-style);border-top-width:1px}.before\:border-dashed:before{content:var(--tw-content);--tw-border-style:dashed;border-style:dashed}.before\:border-danger:before{content:var(--tw-content);border-color:var(--ui-danger)}.after\:flex-1:after{content:var(--tw-content);flex:1}.after\:border-t:after{content:var(--tw-content);border-top-style:var(--tw-border-style);border-top-width:1px}.after\:border-dashed:after{content:var(--tw-content);--tw-border-style:dashed;border-style:dashed}.after\:border-danger:after{content:var(--tw-content);border-color:var(--ui-danger)}.first\:mt-0:first-child{margin-top:0}.last\:border-b-0:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.open\:flex:is([open],:popover-open,:open){display:flex}@media (hover:hover){.hover\:border-accent:hover{border-color:var(--ui-accent)}.hover\:border-accent-hover:hover{border-color:var(--ui-accent-hover)}.hover\:border-accent-line:hover{border-color:var(--ui-accent-line)}.hover\:border-line-strong:hover{border-color:var(--ui-line-strong)}.hover\:bg-accent-hover:hover{background-color:var(--ui-accent-hover)}.hover\:bg-danger:hover{background-color:var(--ui-danger)}.hover\:bg-surface-sunken:hover{background-color:var(--ui-surface-sunken)}.hover\:text-accent-hover:hover{color:var(--ui-accent-hover)}.hover\:text-fg:hover{color:var(--ui-fg)}.hover\:text-on-accent:hover{color:var(--ui-on-accent)}.hover\:underline:hover{text-decoration-line:underline}.hover\:outline-fg-subtle:hover{outline-color:var(--ui-fg-subtle)}}.focus\:border-accent:focus{border-color:var(--ui-accent)}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-45:disabled{opacity:.45}@media (min-width:48rem){.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (min-width:64rem){.lg\:grid-cols-\[minmax\(0\,1fr\)_auto\]{grid-template-columns:minmax(0,1fr) auto}.lg\:gap-14{gap:calc(var(--spacing) * 14)}}@media (min-width:80rem){.xl\:sticky{position:sticky}.xl\:top-4{top:calc(var(--spacing) * 4)}.xl\:max-h-\[calc\(100vh-9rem\)\]{max-height:calc(100vh - 9rem)}.xl\:grid-cols-\[minmax\(280px\,380px\)_1fr\]{grid-template-columns:minmax(280px,380px) 1fr}.xl\:overflow-y-auto{overflow-y:auto}}.dark\:invert:where([data-theme=dark],[data-theme=dark] *){--tw-invert:invert(100%);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,)}.\[\&\>span\]\:block>span{display:block}}:root{--ui-canvas:var(--color-slate-50);--ui-surface:var(--color-slate-0);--ui-surface-sunken:var(--color-slate-100);--ui-surface-raised:var(--color-slate-0);--ui-line:var(--color-slate-200);--ui-line-strong:var(--color-slate-300);--ui-fg:var(--color-slate-900);--ui-fg-muted:var(--color-slate-500);--ui-fg-subtle:var(--color-slate-400);--ui-accent:var(--color-blue-600);--ui-accent-hover:var(--color-blue-700);--ui-accent-surface:var(--color-blue-50);--ui-accent-line:var(--color-blue-200);--ui-on-accent:var(--color-slate-0);--ui-danger:var(--color-red-600);--ui-danger-surface:var(--color-red-50);--ui-danger-line:var(--color-red-300);--ui-warn:var(--color-amber-700);--ui-warn-surface:var(--color-amber-50);--ui-warn-line:var(--color-amber-300);--ui-ok:var(--color-green-600);--ui-ok-surface:var(--color-green-50);--ui-ok-line:var(--color-green-300);--ui-heat-0:var(--color-slate-200);--ui-heat-1:var(--color-blue-200);--ui-heat-2:var(--color-blue-400);--ui-heat-3:var(--color-blue-600);--ui-heat-4:var(--color-blue-800);--ui-type-project:var(--color-blue-700);--ui-type-project-surface:var(--color-blue-50);--ui-type-feedback:var(--color-amber-700);--ui-type-feedback-surface:var(--color-amber-50);--ui-type-user:var(--color-violet-700);--ui-type-user-surface:var(--color-violet-50);--ui-type-reference:var(--color-green-700);--ui-type-reference-surface:var(--color-green-50);--ui-scope-managed:var(--color-red-600);--ui-scope-managed-surface:var(--color-red-50);--ui-scope-user:var(--color-blue-700);--ui-scope-user-surface:var(--color-blue-50);--ui-scope-project:var(--color-amber-700);--ui-scope-project-surface:var(--color-amber-50);--ui-scope-local:var(--color-slate-600);--ui-scope-local-surface:var(--color-slate-100);--ui-shadow-card:0 1px 2px #0000000d;--ui-shadow-pop:0 1px 2px #0000000f, 0 12px 32px #0000001a;--ui-scrim:#0f172a73}:root[data-theme=dark]{--ui-canvas:var(--color-slate-950);--ui-surface:var(--color-slate-900);--ui-surface-sunken:var(--color-slate-950);--ui-surface-raised:var(--color-slate-800);--ui-line:var(--color-slate-800);--ui-line-strong:var(--color-slate-700);--ui-fg:var(--color-slate-100);--ui-fg-muted:var(--color-slate-400);--ui-fg-subtle:var(--color-slate-500);--ui-accent:var(--color-blue-400);--ui-accent-hover:var(--color-blue-300);--ui-accent-surface:var(--color-blue-950);--ui-accent-line:var(--color-blue-800);--ui-on-accent:var(--color-slate-950);--ui-danger:var(--color-red-400);--ui-danger-surface:var(--color-red-950);--ui-danger-line:var(--color-red-500);--ui-warn:var(--color-amber-400);--ui-warn-surface:var(--color-amber-950);--ui-warn-line:var(--color-amber-600);--ui-ok:var(--color-green-400);--ui-ok-surface:var(--color-green-950);--ui-ok-line:var(--color-green-600);--ui-heat-0:var(--color-slate-800);--ui-heat-1:var(--color-blue-900);--ui-heat-2:var(--color-blue-700);--ui-heat-3:var(--color-blue-500);--ui-heat-4:var(--color-blue-300);--ui-type-project:var(--color-blue-300);--ui-type-project-surface:var(--color-blue-950);--ui-type-feedback:var(--color-amber-300);--ui-type-feedback-surface:var(--color-amber-950);--ui-type-user:var(--color-violet-300);--ui-type-user-surface:var(--color-violet-950);--ui-type-reference:var(--color-green-300);--ui-type-reference-surface:var(--color-green-950);--ui-scope-managed:var(--color-red-400);--ui-scope-managed-surface:var(--color-red-950);--ui-scope-user:var(--color-blue-300);--ui-scope-user-surface:var(--color-blue-950);--ui-scope-project:var(--color-amber-300);--ui-scope-project-surface:var(--color-amber-950);--ui-scope-local:var(--color-slate-400);--ui-scope-local-surface:var(--color-slate-800);--ui-shadow-card:0 1px 2px #0006;--ui-shadow-pop:0 1px 2px #00000080, 0 12px 32px #00000073;--ui-scrim:#020610b3}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@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-outline-style{syntax:"*";inherits:false;initial-value:solid}@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}@property --tw-content{syntax:"*";inherits:false;initial-value:""}
|
package/public/ui.mjs
CHANGED
|
@@ -251,7 +251,7 @@ export const meterTop = 'mb-2.5 flex items-baseline gap-2.5';
|
|
|
251
251
|
export const meterValue = 'font-mono text-2xl font-semibold tracking-tightest text-fg';
|
|
252
252
|
export const meterUnit = 'flex-1 text-sm text-fg-muted';
|
|
253
253
|
export const meterBar = 'h-2 overflow-hidden rounded-full border border-line bg-surface-sunken';
|
|
254
|
-
const meterFillTone = { ok: 'bg-ok', near: 'bg-warn', over: 'bg-danger' };
|
|
254
|
+
const meterFillTone = { ok: 'bg-ok', near: 'bg-warn', over: 'bg-danger', accent: 'bg-accent' };
|
|
255
255
|
export const meterFill = (level) => cx('h-full transition-[width]', pick(meterFillTone, level, 'ok'));
|
|
256
256
|
export const meterNote = 'mt-2.5 text-xs text-fg-muted';
|
|
257
257
|
export const meterFacts = 'mt-3 flex flex-wrap gap-4 border-t border-line pt-3 font-mono text-2xs text-fg-muted';
|
|
@@ -2,11 +2,13 @@ import { renderSegmented } from '/views/segments.mjs';
|
|
|
2
2
|
import { renderContext } from '/views/context.mjs';
|
|
3
3
|
import { renderSettings } from '/views/settings.mjs';
|
|
4
4
|
import { renderSessions } from '/views/sessions.mjs';
|
|
5
|
+
import { renderTools } from '/views/tools.mjs';
|
|
5
6
|
|
|
6
7
|
export function renderEnvironment(container) {
|
|
7
8
|
return renderSegmented(container, 'environment', {
|
|
8
9
|
instructions: renderContext,
|
|
9
10
|
settings: renderSettings,
|
|
10
11
|
sessions: renderSessions,
|
|
12
|
+
tools: renderTools,
|
|
11
13
|
});
|
|
12
14
|
}
|
package/public/views/header.mjs
CHANGED
|
@@ -23,6 +23,7 @@ const NAVIGATION = [
|
|
|
23
23
|
{ id: 'instructions', label: 'Instructions' },
|
|
24
24
|
{ id: 'settings', label: 'Settings' },
|
|
25
25
|
{ id: 'sessions', label: 'Sessions' },
|
|
26
|
+
{ id: 'tools', label: 'Tools' },
|
|
26
27
|
],
|
|
27
28
|
},
|
|
28
29
|
];
|
|
@@ -38,6 +39,7 @@ function segmentVisible(tab, segment, store) {
|
|
|
38
39
|
return segment === 'index' ? Boolean(store.hasIndex) : true;
|
|
39
40
|
}
|
|
40
41
|
if (segment === 'instructions') return global || hasProjectDir(store);
|
|
42
|
+
if (segment === 'tools') return state.tools.some((tool) => tool.found);
|
|
41
43
|
if (global) return false;
|
|
42
44
|
if (segment === 'sessions') return Boolean(store.sessions?.count);
|
|
43
45
|
return true;
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import * as ui from '/ui.mjs';
|
|
2
|
+
import { node } from '/dom.mjs';
|
|
3
|
+
|
|
4
|
+
const compact = (value) => {
|
|
5
|
+
const rounded = Math.round(value);
|
|
6
|
+
if (Math.abs(rounded) >= 1000000) return `${(rounded / 1000000).toFixed(1)}M`;
|
|
7
|
+
if (Math.abs(rounded) >= 1000) return `${(rounded / 1000).toFixed(1)}K`;
|
|
8
|
+
return String(rounded);
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const clamp = (percent) => Math.min(100, Math.max(0, percent));
|
|
12
|
+
|
|
13
|
+
const fact = (value, label) => node('span', {}, [
|
|
14
|
+
node('b', { class: ui.meterFactValue, text: value }),
|
|
15
|
+
document.createTextNode(` ${label}`),
|
|
16
|
+
]);
|
|
17
|
+
|
|
18
|
+
function meter(percent, unit, note, facts) {
|
|
19
|
+
return node('div', { class: ui.meter }, [
|
|
20
|
+
node('div', { class: ui.meterTop }, [
|
|
21
|
+
node('span', { class: ui.meterValue, text: `${percent.toFixed(1)}%` }),
|
|
22
|
+
node('span', { class: ui.meterUnit, text: unit }),
|
|
23
|
+
]),
|
|
24
|
+
node('div', { class: ui.meterBar }, [
|
|
25
|
+
node('div', { class: ui.meterFill('accent'), style: `width:${clamp(percent).toFixed(1)}%` }),
|
|
26
|
+
]),
|
|
27
|
+
note ? node('p', { class: ui.meterNote, text: note }) : null,
|
|
28
|
+
facts.length ? node('div', { class: ui.meterFacts }, facts) : null,
|
|
29
|
+
]);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function head(report, actions) {
|
|
33
|
+
return node('div', { class: ui.sectionLabelRow }, [
|
|
34
|
+
node('span', { class: ui.sectionLabelInline, text: report.version ? `rtk ${report.version}` : 'rtk' }),
|
|
35
|
+
node('span', { class: ui.note, text: report.path || '' }),
|
|
36
|
+
node('span', { class: ui.listSpacer }),
|
|
37
|
+
node('button', { class: ui.buttonSmall, text: 'Re-read', onclick: actions.reread }),
|
|
38
|
+
node('button', { class: ui.buttonSmall, text: 'Turn off', onclick: actions.turnOff }),
|
|
39
|
+
]);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function projectSection(report) {
|
|
43
|
+
const project = report.project;
|
|
44
|
+
if (!project) return [];
|
|
45
|
+
if (!project.commands) {
|
|
46
|
+
return [
|
|
47
|
+
node('div', { class: ui.sectionLabel, text: 'This project' }),
|
|
48
|
+
node('p', { class: ui.noteTight, text: `rtk has filtered nothing in ${report.projectDir} yet.` }),
|
|
49
|
+
];
|
|
50
|
+
}
|
|
51
|
+
return [node('div', { class: ui.sectionLabel, text: 'This project' }), meter(
|
|
52
|
+
project.savedPct,
|
|
53
|
+
'of what rtk read in this project never reached the model',
|
|
54
|
+
'rtk scopes its ledger by working directory, so this covers every command run from this project path.',
|
|
55
|
+
[
|
|
56
|
+
fact(compact(project.saved), 'tokens saved'),
|
|
57
|
+
fact(compact(project.commands), 'commands filtered'),
|
|
58
|
+
fact(`${compact(project.input)} → ${compact(project.output)}`, 'in / out'),
|
|
59
|
+
fact(`${compact(project.avgMs)}ms`, 'average'),
|
|
60
|
+
],
|
|
61
|
+
)];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function machineMeter(report) {
|
|
65
|
+
const machine = report.machine;
|
|
66
|
+
if (!machine) return null;
|
|
67
|
+
const window = machine.window;
|
|
68
|
+
const facts = [
|
|
69
|
+
fact(compact(machine.saved), 'tokens saved'),
|
|
70
|
+
fact(compact(machine.commands), 'commands filtered'),
|
|
71
|
+
fact(`${compact(machine.avgMs)}ms`, 'average'),
|
|
72
|
+
];
|
|
73
|
+
if (window && window.commands) {
|
|
74
|
+
facts.push(fact(`${compact(window.saved)} (${window.savedPct.toFixed(1)}%)`, `saved in the last ${window.days} days`));
|
|
75
|
+
}
|
|
76
|
+
return meter(
|
|
77
|
+
machine.savedPct,
|
|
78
|
+
'of everything rtk has ever filtered on this machine',
|
|
79
|
+
null,
|
|
80
|
+
facts,
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function missedRow(item) {
|
|
85
|
+
const saving = `${compact(item.savedTokens)} tokens (${item.savedPct.toFixed(0)}%)`;
|
|
86
|
+
return node('div', { class: ui.issue(false) }, [
|
|
87
|
+
node('div', { class: ui.issueBody }, [
|
|
88
|
+
node('div', { class: ui.issueTitle, text: `${item.command} ran ${item.count.toLocaleString()} times unfiltered` }),
|
|
89
|
+
node('div', { class: ui.issueDetail, text: `${item.equivalent} would have saved about ${saving}` }),
|
|
90
|
+
]),
|
|
91
|
+
node('span', { class: ui.badge('neutral'), text: item.category || 'Other' }),
|
|
92
|
+
]);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function discoverSection(report) {
|
|
96
|
+
const discover = report.discover;
|
|
97
|
+
if (!discover || !discover.commands) return [];
|
|
98
|
+
|
|
99
|
+
const parts = [
|
|
100
|
+
node('div', { class: ui.sectionLabel, text: 'Left on the table' }),
|
|
101
|
+
meter(
|
|
102
|
+
discover.adoptionPct,
|
|
103
|
+
`of the commands in the last ${discover.sinceDays} days already went through rtk`,
|
|
104
|
+
`Counted across ${discover.sessionsScanned.toLocaleString()} session transcripts for this project.`,
|
|
105
|
+
[
|
|
106
|
+
fact(discover.alreadyRtk.toLocaleString(), 'already filtered'),
|
|
107
|
+
fact(discover.commands.toLocaleString(), 'commands seen'),
|
|
108
|
+
fact(discover.sessionsScanned.toLocaleString(), 'sessions scanned'),
|
|
109
|
+
],
|
|
110
|
+
),
|
|
111
|
+
];
|
|
112
|
+
|
|
113
|
+
if (!discover.missed.length) {
|
|
114
|
+
parts.push(node('p', { class: ui.okLine, text: 'Nothing rtk knows how to filter was run raw.' }));
|
|
115
|
+
return parts;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
for (const item of discover.missed) parts.push(missedRow(item));
|
|
119
|
+
const hidden = discover.missedTotal - discover.missed.length;
|
|
120
|
+
const left = hidden === 1 ? '1 smaller one is not shown' : `${hidden} smaller ones are not shown`;
|
|
121
|
+
parts.push(node('p', { class: ui.note, text: hidden > 0
|
|
122
|
+
? `Estimates from rtk itself, worst first; ${left}. They count what the filter would have stripped, not what the model would have ignored.`
|
|
123
|
+
: 'Estimates from rtk itself, worst first. They count what the filter would have stripped, not what the model would have ignored.' }));
|
|
124
|
+
return parts;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function errorNotes(report) {
|
|
128
|
+
if (!report.errors.length) return [];
|
|
129
|
+
return [
|
|
130
|
+
node('div', { class: ui.sectionLabel, text: 'Not read' }),
|
|
131
|
+
...report.errors.map((error) => node('p', { class: ui.noteTight, text: `${error.step}: ${error.message}` })),
|
|
132
|
+
];
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function renderRtk(container, report, actions) {
|
|
136
|
+
container.append(head(report, actions));
|
|
137
|
+
for (const part of projectSection(report)) container.append(part);
|
|
138
|
+
|
|
139
|
+
container.append(node('div', { class: ui.sectionLabel, text: report.project ? 'Every project' : 'This machine' }));
|
|
140
|
+
const machine = machineMeter(report);
|
|
141
|
+
container.append(machine || node('p', { class: ui.noteTight, text: 'The machine-wide ledger could not be read.' }));
|
|
142
|
+
if (!report.project && !report.projectDir) {
|
|
143
|
+
container.append(node('p', { class: ui.note, text: 'This entry is the user scope rather than a project, so rtk has no working directory to slice its ledger by.' }));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
for (const part of discoverSection(report)) container.append(part);
|
|
147
|
+
for (const part of errorNotes(report)) container.append(part);
|
|
148
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import * as ui from '/ui.mjs';
|
|
2
|
+
import { node } from '/dom.mjs';
|
|
3
|
+
import { api } from '/api.mjs';
|
|
4
|
+
import { state } from '/state.mjs';
|
|
5
|
+
import { isAt } from '/parts.mjs';
|
|
6
|
+
import { paint } from '/bus.mjs';
|
|
7
|
+
import { renderRtk } from '/views/rtk.mjs';
|
|
8
|
+
|
|
9
|
+
const OPT_IN = 'toolsOptIn';
|
|
10
|
+
|
|
11
|
+
const installedRtk = () => state.tools.find((tool) => tool.id === 'rtk' && tool.found) || null;
|
|
12
|
+
|
|
13
|
+
async function fetchRtk() {
|
|
14
|
+
const id = state.storeId;
|
|
15
|
+
let next;
|
|
16
|
+
try {
|
|
17
|
+
next = { data: await api(`/api/stores/${encodeURIComponent(id)}/tools/rtk`) };
|
|
18
|
+
} catch (err) {
|
|
19
|
+
next = { error: err.message };
|
|
20
|
+
}
|
|
21
|
+
if (state.storeId !== id) return;
|
|
22
|
+
state.aux.rtk = next;
|
|
23
|
+
if (isAt('environment', 'tools')) paint('tab');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function startRtk({ remember = true } = {}) {
|
|
27
|
+
if (remember) localStorage.setItem(OPT_IN, '1');
|
|
28
|
+
state.aux.rtk = { running: true };
|
|
29
|
+
fetchRtk();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function readRtk() {
|
|
33
|
+
startRtk();
|
|
34
|
+
paint('tab');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function turnOff() {
|
|
38
|
+
localStorage.removeItem(OPT_IN);
|
|
39
|
+
state.aux.rtk = null;
|
|
40
|
+
paint('tab');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function gate(container, tool) {
|
|
44
|
+
container.append(node('div', { class: ui.issue(false) }, [
|
|
45
|
+
node('div', { class: ui.issueBody }, [
|
|
46
|
+
node('div', { class: ui.issueTitle, text: 'rtk is installed, and is not being read' }),
|
|
47
|
+
node('div', { class: ui.issueDetail, text: `${tool.path} keeps a ledger of what its filters stripped before it reached the model. Reading it runs rtk on this machine, so it stays off until you ask.` }),
|
|
48
|
+
]),
|
|
49
|
+
node('button', { class: ui.buttonSmall, text: 'Read rtk stats', onclick: readRtk }),
|
|
50
|
+
]));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function failure(container, message) {
|
|
54
|
+
container.append(node('div', { class: ui.issue(true) }, [
|
|
55
|
+
node('div', { class: ui.issueBody }, [
|
|
56
|
+
node('div', { class: ui.issueTitle, text: 'rtk could not be read' }),
|
|
57
|
+
node('div', { class: ui.issueDetail, text: message }),
|
|
58
|
+
]),
|
|
59
|
+
node('button', { class: ui.buttonSmall, text: 'Turn off', onclick: turnOff }),
|
|
60
|
+
node('button', { class: ui.buttonSmall, text: 'Try again', onclick: readRtk }),
|
|
61
|
+
]));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function renderTools(container) {
|
|
65
|
+
const tool = installedRtk();
|
|
66
|
+
if (!tool) return;
|
|
67
|
+
|
|
68
|
+
if (!state.aux.rtk && localStorage.getItem(OPT_IN) === '1') startRtk({ remember: false });
|
|
69
|
+
|
|
70
|
+
const report = state.aux.rtk;
|
|
71
|
+
if (!report) return gate(container, tool);
|
|
72
|
+
if (report.running) return container.append(node('p', { class: ui.note, text: 'Reading the rtk ledger…' }));
|
|
73
|
+
if (report.error) return failure(container, report.error);
|
|
74
|
+
|
|
75
|
+
renderRtk(container, report.data, { reread: readRtk, turnOff });
|
|
76
|
+
}
|
package/server.mjs
CHANGED
|
@@ -16,6 +16,7 @@ import { listStores } from './src/stores.mjs';
|
|
|
16
16
|
import { forgetPath, rememberPath } from './src/pathcache.mjs';
|
|
17
17
|
import { searchAll } from './src/search.mjs';
|
|
18
18
|
import { verifyPaths } from './src/pathcheck.mjs';
|
|
19
|
+
import { findRtk, rtkReport } from './src/rtk.mjs';
|
|
19
20
|
import { sessionsWithSummaries, transcriptDir } from './src/sessions.mjs';
|
|
20
21
|
import {
|
|
21
22
|
addIndexEntry,
|
|
@@ -217,6 +218,7 @@ async function handleApi(req, res, url) {
|
|
|
217
218
|
rootSource: origin.path === ROOT ? origin.source : 'flag',
|
|
218
219
|
rootFile: origin.path === ROOT ? origin.file : null,
|
|
219
220
|
rootWarning: origin.invalid,
|
|
221
|
+
tools: [findRtk()],
|
|
220
222
|
stores: listStores(ROOT),
|
|
221
223
|
});
|
|
222
224
|
}
|
|
@@ -276,6 +278,13 @@ async function handleApi(req, res, url) {
|
|
|
276
278
|
return sendJson(res, 200, sessionsWithSummaries(slugDir, { projectDir: storeProjectDir(store) }));
|
|
277
279
|
}
|
|
278
280
|
|
|
281
|
+
if (action === 'tools/rtk' && req.method === 'GET') {
|
|
282
|
+
const rtk = findRtk();
|
|
283
|
+
if (!rtk.found) throw new Error('rtk is not installed, or not on the PATH this server was started with.');
|
|
284
|
+
const report = await rtkReport(storeProjectDir(store));
|
|
285
|
+
return sendJson(res, 200, { ...report, path: rtk.path });
|
|
286
|
+
}
|
|
287
|
+
|
|
279
288
|
if (action === 'settings' && req.method === 'GET') {
|
|
280
289
|
return sendJson(res, 200, settingsReport({ projectDir: storeProjectDir(store) }));
|
|
281
290
|
}
|
package/src/rtk.mjs
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
|
|
6
|
+
const BINARY = 'rtk';
|
|
7
|
+
const TIMEOUT_MS = 15000;
|
|
8
|
+
const MAX_BUFFER = 4 * 1024 * 1024;
|
|
9
|
+
const DISCOVER_DAYS = 30;
|
|
10
|
+
const DISCOVER_LIMIT = 10;
|
|
11
|
+
const MISSED_LIMIT = 12;
|
|
12
|
+
const TREND_DAYS = 30;
|
|
13
|
+
|
|
14
|
+
const num = (value) => (typeof value === 'number' && Number.isFinite(value) ? value : 0);
|
|
15
|
+
const text = (value) => (typeof value === 'string' ? value : '');
|
|
16
|
+
const pct = (part, whole) => (whole > 0 ? (part / whole) * 100 : 0);
|
|
17
|
+
|
|
18
|
+
function candidateNames(env) {
|
|
19
|
+
if (process.platform !== 'win32') return [BINARY];
|
|
20
|
+
const exts = text(env.PATHEXT || '.EXE').split(';').filter(Boolean);
|
|
21
|
+
return exts.map((ext) => BINARY + ext.toLowerCase());
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function findRtk(env = process.env) {
|
|
25
|
+
const dirs = text(env.PATH).split(path.delimiter).filter(Boolean);
|
|
26
|
+
const names = candidateNames(env);
|
|
27
|
+
for (const dir of dirs) {
|
|
28
|
+
for (const name of names) {
|
|
29
|
+
const candidate = path.join(dir, name);
|
|
30
|
+
try {
|
|
31
|
+
if (!fs.statSync(candidate).isFile()) continue;
|
|
32
|
+
fs.accessSync(candidate, fs.constants.X_OK);
|
|
33
|
+
return { id: BINARY, found: true, path: candidate };
|
|
34
|
+
} catch {
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return { id: BINARY, found: false, path: null };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function runRtk(args, { cwd, timeout = TIMEOUT_MS } = {}) {
|
|
43
|
+
return new Promise((resolve, reject) => {
|
|
44
|
+
const options = { cwd, timeout, maxBuffer: MAX_BUFFER, windowsHide: true, encoding: 'utf8' };
|
|
45
|
+
execFile(BINARY, args, options, (err, stdout, stderr) => {
|
|
46
|
+
if (!err) return resolve(stdout);
|
|
47
|
+
if (err.code === 'ENOENT') return reject(new Error('rtk is not on this process PATH'));
|
|
48
|
+
if (err.killed) return reject(new Error(`rtk ${args[0]} was still running after ${timeout}ms and was stopped`));
|
|
49
|
+
const detail = text(stderr || stdout).trim().split('\n')[0];
|
|
50
|
+
return reject(new Error(detail ? `rtk ${args[0]} failed: ${detail}` : `rtk ${args[0]} exited ${err.code}`));
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function parseJson(raw, label) {
|
|
56
|
+
try {
|
|
57
|
+
const value = JSON.parse(raw);
|
|
58
|
+
if (!value || typeof value !== 'object') throw new Error('not an object');
|
|
59
|
+
return value;
|
|
60
|
+
} catch {
|
|
61
|
+
throw new Error(`rtk ${label} did not answer with JSON`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function summaryOf(raw) {
|
|
66
|
+
const summary = raw && typeof raw.summary === 'object' && raw.summary ? raw.summary : {};
|
|
67
|
+
return {
|
|
68
|
+
commands: num(summary.total_commands),
|
|
69
|
+
input: num(summary.total_input),
|
|
70
|
+
output: num(summary.total_output),
|
|
71
|
+
saved: num(summary.total_saved),
|
|
72
|
+
savedPct: num(summary.avg_savings_pct),
|
|
73
|
+
totalMs: num(summary.total_time_ms),
|
|
74
|
+
avgMs: num(summary.avg_time_ms),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function daysOf(raw) {
|
|
79
|
+
const list = raw && Array.isArray(raw.daily) ? raw.daily : [];
|
|
80
|
+
return list
|
|
81
|
+
.filter((day) => day && typeof day === 'object')
|
|
82
|
+
.map((day) => ({
|
|
83
|
+
date: text(day.date),
|
|
84
|
+
commands: num(day.commands),
|
|
85
|
+
input: num(day.input_tokens),
|
|
86
|
+
saved: num(day.saved_tokens),
|
|
87
|
+
savedPct: num(day.savings_pct),
|
|
88
|
+
}))
|
|
89
|
+
.filter((day) => day.date);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function windowOf(days, count) {
|
|
93
|
+
const recent = days.slice(-count);
|
|
94
|
+
const totals = recent.reduce((sum, day) => ({
|
|
95
|
+
commands: sum.commands + day.commands,
|
|
96
|
+
input: sum.input + day.input,
|
|
97
|
+
saved: sum.saved + day.saved,
|
|
98
|
+
}), { commands: 0, input: 0, saved: 0 });
|
|
99
|
+
return {
|
|
100
|
+
days: count,
|
|
101
|
+
active: recent.length,
|
|
102
|
+
commands: totals.commands,
|
|
103
|
+
saved: totals.saved,
|
|
104
|
+
savedPct: pct(totals.saved, totals.input),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function discoverOf(raw) {
|
|
109
|
+
const commands = num(raw.total_commands);
|
|
110
|
+
const alreadyRtk = num(raw.already_rtk);
|
|
111
|
+
const supported = Array.isArray(raw.supported) ? raw.supported : [];
|
|
112
|
+
const missed = supported
|
|
113
|
+
.filter((item) => item && typeof item === 'object')
|
|
114
|
+
.map((item) => ({
|
|
115
|
+
command: text(item.command),
|
|
116
|
+
count: num(item.count),
|
|
117
|
+
equivalent: text(item.rtk_equivalent),
|
|
118
|
+
category: text(item.category),
|
|
119
|
+
savedTokens: num(item.estimated_savings_tokens),
|
|
120
|
+
savedPct: num(item.estimated_savings_pct),
|
|
121
|
+
}))
|
|
122
|
+
.filter((item) => item.command)
|
|
123
|
+
.sort((a, b) => b.savedTokens - a.savedTokens);
|
|
124
|
+
|
|
125
|
+
return {
|
|
126
|
+
sessionsScanned: num(raw.sessions_scanned),
|
|
127
|
+
commands,
|
|
128
|
+
alreadyRtk,
|
|
129
|
+
adoptionPct: pct(alreadyRtk, commands),
|
|
130
|
+
sinceDays: num(raw.since_days) || DISCOVER_DAYS,
|
|
131
|
+
missedTotal: missed.length,
|
|
132
|
+
missed: missed.slice(0, MISSED_LIMIT),
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function usableDir(dir) {
|
|
137
|
+
if (!dir) return null;
|
|
138
|
+
try {
|
|
139
|
+
return fs.statSync(dir).isDirectory() ? dir : null;
|
|
140
|
+
} catch {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export async function rtkReport(projectDir, { run = runRtk, home = os.homedir() } = {}) {
|
|
146
|
+
const errors = [];
|
|
147
|
+
const step = async (label, work) => {
|
|
148
|
+
try {
|
|
149
|
+
return await work();
|
|
150
|
+
} catch (err) {
|
|
151
|
+
errors.push({ step: label, message: err.message });
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const dir = usableDir(projectDir);
|
|
157
|
+
|
|
158
|
+
const [version, machine, project, discover] = await Promise.all([
|
|
159
|
+
step('version', async () => text(await run(['--version'], { cwd: home })).trim().replace(/^rtk\s+/, '')),
|
|
160
|
+
step('gain', async () => parseJson(await run(['gain', '-a', '--format', 'json'], { cwd: home }), 'gain')),
|
|
161
|
+
dir ? step('gain --project', async () => parseJson(await run(['gain', '-p', '--format', 'json'], { cwd: dir }), 'gain --project')) : null,
|
|
162
|
+
dir ? step('discover', async () => parseJson(
|
|
163
|
+
await run(['discover', '--format', 'json', '-s', String(DISCOVER_DAYS), '-l', String(DISCOVER_LIMIT)], { cwd: dir }),
|
|
164
|
+
'discover',
|
|
165
|
+
)) : null,
|
|
166
|
+
]);
|
|
167
|
+
|
|
168
|
+
const days = daysOf(machine);
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
tool: BINARY,
|
|
172
|
+
version: version || null,
|
|
173
|
+
projectDir: dir,
|
|
174
|
+
machine: machine ? { ...summaryOf(machine), days, window: windowOf(days, TREND_DAYS) } : null,
|
|
175
|
+
project: project ? summaryOf(project) : null,
|
|
176
|
+
discover: discover ? discoverOf(discover) : null,
|
|
177
|
+
errors,
|
|
178
|
+
};
|
|
179
|
+
}
|