@krtclcdy/tersio 2.9.0 → 2.11.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 +54 -36
- package/cli/banner.js +84 -0
- package/cli/common.js +3 -2
- package/cli/dashboard.js +37 -11
- package/cli/doctor.js +8 -1
- package/cli/install.js +27 -7
- package/cli/reset.js +51 -0
- package/cli/update.js +8 -0
- package/cli/usage.js +87 -22
- package/dashboard/app.js +922 -0
- package/dashboard/styles.css +194 -0
- package/dashboard/template.html +305 -0
- package/extensions/caveman-session/index.js +12 -1
- package/extensions/combo-toggle/index.js +3 -3
- package/extensions/rtk-session/index.js +8 -1
- package/extensions/shared/carbon.js +88 -0
- package/extensions/shared/pricing.js +121 -0
- package/extensions/shared/rtk-gain.js +61 -0
- package/extensions/shared/session-state.js +10 -3
- package/extensions/shared/usage-ledger.js +81 -37
- package/extensions/tersio-commands/index.js +5 -10
- package/package.json +2 -3
- package/tersio.js +8 -1
- package/cli/dashboard.html +0 -780
- /package/{cli → dashboard}/brand.webp +0 -0
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--bg: #f4f4f5; --panel: #ffffff; --ink: #18181b; --dim: #52525b;
|
|
3
|
+
--line: rgba(24, 24, 27, .12); --accent: #047857; --accent-soft: rgba(4, 120, 87, .12);
|
|
4
|
+
--track: rgba(24, 24, 27, .08); --grid: rgba(4, 120, 87, .07);
|
|
5
|
+
--cell-0: rgba(24, 24, 27, .07);
|
|
6
|
+
--cell-1: rgba(4, 120, 87, .3); --cell-2: rgba(4, 120, 87, .55);
|
|
7
|
+
--cell-3: rgba(4, 120, 87, .8); --cell-4: #047857;
|
|
8
|
+
--shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.07);
|
|
9
|
+
}
|
|
10
|
+
html[data-theme="light"] { color-scheme: light; }
|
|
11
|
+
html[data-theme="dark"] { color-scheme: dark; }
|
|
12
|
+
@media (prefers-color-scheme: dark) {
|
|
13
|
+
:root:not([data-theme="light"]) {
|
|
14
|
+
--bg: #09090b; --panel: #101014; --ink: #f4f4f5; --dim: #a1a1aa;
|
|
15
|
+
--line: rgba(255, 255, 255, .09); --accent: #34d399; --accent-soft: rgba(52, 211, 153, .1);
|
|
16
|
+
--track: rgba(255, 255, 255, .07); --grid: rgba(52, 211, 153, .05);
|
|
17
|
+
--cell-0: rgba(255, 255, 255, .07);
|
|
18
|
+
--cell-1: rgba(52, 211, 153, .3); --cell-2: rgba(52, 211, 153, .55);
|
|
19
|
+
--cell-3: rgba(52, 211, 153, .8); --cell-4: #34d399;
|
|
20
|
+
--shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.55);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
body { background: var(--bg); color: var(--ink); }
|
|
24
|
+
.hero-glow { position: absolute; left: 50%; top: 44%; width: min(720px, 90vw); height: 300px;
|
|
25
|
+
transform: translate(-50%, -50%); pointer-events: none;
|
|
26
|
+
background: radial-gradient(ellipse at center, var(--accent-soft) 0%, transparent 65%);
|
|
27
|
+
animation: breathe 5s ease-in-out infinite; }
|
|
28
|
+
@keyframes breathe { 0%, 100% { opacity: .55; transform: translate(-50%, -50%) scale(1); } 50% { opacity: 1; transform: translate(-50%, -50%) scale(1.12); } }
|
|
29
|
+
.herototal { background: linear-gradient(180deg, var(--ink) 55%, var(--accent) 130%);
|
|
30
|
+
-webkit-background-clip: text; background-clip: text;
|
|
31
|
+
-webkit-text-fill-color: transparent; color: transparent; }
|
|
32
|
+
html[data-theme="dark"] {
|
|
33
|
+
--bg: #09090b; --panel: #101014; --ink: #f4f4f5; --dim: #a1a1aa;
|
|
34
|
+
--line: rgba(255, 255, 255, .09); --accent: #34d399; --accent-soft: rgba(52, 211, 153, .1);
|
|
35
|
+
--track: rgba(255, 255, 255, .07); --grid: rgba(52, 211, 153, .05);
|
|
36
|
+
--cell-0: rgba(255, 255, 255, .07);
|
|
37
|
+
--cell-1: rgba(52, 211, 153, .3); --cell-2: rgba(52, 211, 153, .55);
|
|
38
|
+
--cell-3: rgba(52, 211, 153, .8); --cell-4: #34d399;
|
|
39
|
+
--shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.55);
|
|
40
|
+
}
|
|
41
|
+
.ticker { animation: tick 36s linear infinite; will-change: transform; }
|
|
42
|
+
.ticker:hover { animation-play-state: paused; }
|
|
43
|
+
@keyframes tick { from { transform: translateX(0); } to { transform: translateX(-50%); } }
|
|
44
|
+
.display { font-family: "Outfit", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
|
|
45
|
+
.bg-blueprint {
|
|
46
|
+
background-image: linear-gradient(var(--grid) 1px, transparent 1px),
|
|
47
|
+
linear-gradient(90deg, var(--grid) 1px, transparent 1px);
|
|
48
|
+
background-size: 44px 44px;
|
|
49
|
+
mask-image: radial-gradient(ellipse 90% 70% at 50% 0%, black 30%, transparent 75%);
|
|
50
|
+
}
|
|
51
|
+
.mono { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-variant-numeric: tabular-nums; }
|
|
52
|
+
.rowline { border-bottom: 1px solid var(--line); }
|
|
53
|
+
#recentTable, #cmdTable { table-layout: fixed; }
|
|
54
|
+
#recentTable td:first-child, #cmdTable td:nth-child(2) { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
55
|
+
.pill { display: inline-flex; align-items: center; gap: 4px; border-radius: 999px; padding: 3px 10px; font-size: 11px; white-space: nowrap; }
|
|
56
|
+
.pill.good { background: var(--accent-soft); color: var(--accent); }
|
|
57
|
+
.pill.bad { background: rgba(248, 113, 113, .12); color: #f87171; }
|
|
58
|
+
.pill.flat { background: var(--track); color: var(--dim); }
|
|
59
|
+
.fxbtn { background: var(--track); color: var(--ink); border: 0; cursor: pointer; gap: 6px; }
|
|
60
|
+
.fxbtn:focus-visible { outline: 1px solid var(--accent); }
|
|
61
|
+
.fxbtn svg { transition: transform .15s; }
|
|
62
|
+
.fxbtn[aria-expanded="true"] svg { transform: rotate(180deg); }
|
|
63
|
+
.fxpanel { position: absolute; right: 0; top: calc(100% + 6px); z-index: 50; min-width: 132px; padding: 4px; border-radius: 10px; border: 1px solid var(--line); background: var(--panel); box-shadow: 0 8px 28px rgba(0, 0, 0, .28); }
|
|
64
|
+
.fxopt { display: flex; align-items: center; gap: 8px; width: 100%; padding: 6px 8px; border-radius: 7px; font-size: 11px; cursor: pointer; color: var(--ink); background: transparent; border: 0; text-align: left; }
|
|
65
|
+
.fxopt:hover, .fxopt.active { background: var(--accent-soft); }
|
|
66
|
+
.fxopt:focus-visible { outline: 1px solid var(--accent); }
|
|
67
|
+
.fxopt .tick { margin-left: auto; color: var(--accent); visibility: hidden; }
|
|
68
|
+
.fxopt[aria-selected="true"] .tick { visibility: visible; }
|
|
69
|
+
.bar-fill { background: var(--accent); border-radius: 999px; transform-origin: left; transition: width 1s cubic-bezier(.16, 1, .3, 1); }
|
|
70
|
+
.bar-track { background: var(--track); border-radius: 999px; }
|
|
71
|
+
.live-dot { background: var(--accent); box-shadow: 0 0 0 0 var(--accent-soft); animation: ping 2s cubic-bezier(0, 0, .2, 1) infinite; }
|
|
72
|
+
.thsort { all: unset; cursor: pointer; display: inline-flex; align-items: center; gap: 5px; text-transform: inherit; letter-spacing: inherit; }
|
|
73
|
+
.thsort.num { float: right; }
|
|
74
|
+
.thsort:hover { color: var(--ink); }
|
|
75
|
+
.thsort:focus-visible { outline: 1px solid var(--accent); border-radius: 4px; }
|
|
76
|
+
.thsort .arr { font-size: 10px; color: var(--accent); }
|
|
77
|
+
th[aria-sort="none"] .arr { color: var(--dim); opacity: .55; }
|
|
78
|
+
.pgbtn { min-width: 26px; height: 26px; padding: 0 7px; display: inline-flex; align-items: center; justify-content: center; border-radius: 7px; border: 1px solid var(--line); background: transparent; color: var(--dim); cursor: pointer; }
|
|
79
|
+
.pgbtn:hover:not(:disabled) { color: var(--ink); border-color: var(--accent); }
|
|
80
|
+
.pgbtn.on { background: var(--accent-soft); color: var(--accent); border-color: transparent; font-weight: 700; }
|
|
81
|
+
.pgbtn:disabled { opacity: .35; cursor: default; }
|
|
82
|
+
.pgbtn:focus-visible { outline: 1px solid var(--accent); }
|
|
83
|
+
.rail { position: fixed; left: 14px; top: 50%; transform: translateY(-50%); z-index: 40; flex-direction: column; gap: 5px; }
|
|
84
|
+
.rail button { all: unset; width: 14px; height: 2px; border-radius: 2px; background: var(--track); cursor: pointer; transition: background .2s; }
|
|
85
|
+
.rail button.lit { background: var(--accent); }
|
|
86
|
+
.rail button:hover { background: var(--dim); }
|
|
87
|
+
.rail button:focus-visible { outline: 1px solid var(--accent); outline-offset: 2px; }
|
|
88
|
+
@keyframes ping { 75%, 100% { box-shadow: 0 0 0 9px transparent; } }
|
|
89
|
+
.rise { opacity: 0; transform: translateY(26px); }
|
|
90
|
+
.rise.in { opacity: 1; transform: none; transition: opacity .7s cubic-bezier(.16, 1, .3, 1), transform .7s cubic-bezier(.16, 1, .3, 1); }
|
|
91
|
+
.tilt { transition: transform .25s cubic-bezier(.16, 1, .3, 1), background .25s; }
|
|
92
|
+
.tilt:hover { transform: translateX(6px); box-shadow: var(--shadow-xs); }
|
|
93
|
+
.seg button { color: var(--dim); border-radius: 8px; transition: background .2s, color .2s; cursor: pointer; }
|
|
94
|
+
.seg button.on { background: var(--accent-soft); color: var(--ink); }
|
|
95
|
+
.seg button:not(.on):hover { color: var(--ink); }
|
|
96
|
+
.mrow { transition: background .25s; }
|
|
97
|
+
.mrow:hover { box-shadow: var(--shadow-xs); }
|
|
98
|
+
.stripcell { transition: background .25s; }
|
|
99
|
+
.stripcell:hover { box-shadow: var(--shadow-xs); }
|
|
100
|
+
button, .tilt, .cell, .stripcell { cursor: pointer; }
|
|
101
|
+
.btn-push { transition: transform .12s, background .2s; }
|
|
102
|
+
.btn-push:hover { background: var(--accent-soft); }
|
|
103
|
+
.btn-push:active { transform: scale(.96); }
|
|
104
|
+
a.social { color: var(--ink); }
|
|
105
|
+
a.social:hover { color: var(--accent); }
|
|
106
|
+
a.social img, a.social svg { width: 16px; height: 16px; display: block; }
|
|
107
|
+
html[data-theme="dark"] a.social img { filter: invert(1); }
|
|
108
|
+
@media (prefers-color-scheme: dark) {
|
|
109
|
+
:root:not([data-theme="light"]) a.social img { filter: invert(1); }
|
|
110
|
+
}
|
|
111
|
+
a.social:hover { outline: 1px solid var(--accent); outline-offset: 2px; }
|
|
112
|
+
.cell { width: 100%; aspect-ratio: 1 / 1; border-radius: 3px; background: var(--cell-0); min-width: 0; }
|
|
113
|
+
.cell.key { width: 11px; height: 11px; aspect-ratio: auto; flex-shrink: 0; }
|
|
114
|
+
.cell.l1 { background: var(--cell-1); } .cell.l2 { background: var(--cell-2); }
|
|
115
|
+
.cell.l3 { background: var(--cell-3); } .cell.l4 { background: var(--cell-4); }
|
|
116
|
+
.brandmark { width: 34px; height: 34px; border-radius: 10px; display: grid; place-items: center; flex-shrink: 0; color: #fff; }
|
|
117
|
+
.brandmark img { width: 18px; height: 18px; }
|
|
118
|
+
.brandmark.sm { width: 28px; height: 28px; border-radius: 8px; }
|
|
119
|
+
.brandmark.sm img { width: 14px; height: 14px; }
|
|
120
|
+
.brandmark .fb { display: none; width: 18px; height: 18px; }
|
|
121
|
+
.brandmark.sm .fb { width: 14px; height: 14px; }
|
|
122
|
+
.ghostimg { position: absolute; right: -6px; bottom: -10px; width: 88px; height: 88px;
|
|
123
|
+
opacity: .08; pointer-events: none; user-select: none; }
|
|
124
|
+
@media (prefers-color-scheme: light) {
|
|
125
|
+
:root:not([data-theme="dark"]) .ghostimg { opacity: .06; filter: brightness(0); }
|
|
126
|
+
}
|
|
127
|
+
html[data-theme="light"] .ghostimg { opacity: .06; filter: brightness(0); }
|
|
128
|
+
#tip { position: fixed; z-index: 100; pointer-events: none; min-width: 230px; max-width: 300px;
|
|
129
|
+
background: var(--panel);
|
|
130
|
+
border: 1px solid var(--line); border-radius: 12px; padding: 12px 14px;
|
|
131
|
+
box-shadow: var(--shadow-xs); opacity: 0; transition: opacity .15s; }
|
|
132
|
+
#tip.show { opacity: 1; }
|
|
133
|
+
#tip .tt { font-size: 11px; text-transform: uppercase; letter-spacing: .14em; font-weight: 700; }
|
|
134
|
+
#tip .tv { font-size: 13px; margin: 2px 0 8px; }
|
|
135
|
+
#tip .tr { display: flex; align-items: center; gap: 8px; font-size: 12px; padding: 2.5px 0; }
|
|
136
|
+
#tip .sw { width: 9px; height: 9px; border-radius: 2.5px; flex-shrink: 0; }
|
|
137
|
+
#tip .tn { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
138
|
+
.scrollarea { overflow-y: auto; overscroll-behavior: contain; scrollbar-width: thin; scrollbar-color: var(--line) transparent; }
|
|
139
|
+
.scrollarea::-webkit-scrollbar { width: 8px; }
|
|
140
|
+
.scrollarea::-webkit-scrollbar-thumb { background: var(--line); border-radius: 99px; }
|
|
141
|
+
.scrollarea::-webkit-scrollbar-track { background: transparent; }
|
|
142
|
+
.rrow { transition: background .2s; cursor: pointer; }
|
|
143
|
+
.rrow:hover { box-shadow: var(--shadow-xs); }
|
|
144
|
+
#recentTable thead th { position: sticky; top: 0; background: var(--panel); z-index: 1; }
|
|
145
|
+
#tip .tvr { flex-shrink: 0; }
|
|
146
|
+
.modelcard { transition: background .25s; }
|
|
147
|
+
.modelcard:hover { box-shadow: var(--shadow-xs); }
|
|
148
|
+
|
|
149
|
+
.heatmonth { font-size: 10px; }
|
|
150
|
+
#graphMonths span { overflow: visible; white-space: nowrap; }
|
|
151
|
+
.dock {
|
|
152
|
+
position: sticky; top: 12px; z-index: 50;
|
|
153
|
+
border: 1px solid var(--line); border-radius: 20px;
|
|
154
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, .08);
|
|
155
|
+
}
|
|
156
|
+
.dock img.brand { border-radius: 10px; }
|
|
157
|
+
.dock {
|
|
158
|
+
background:
|
|
159
|
+
linear-gradient(var(--panel), var(--panel)) padding-box,
|
|
160
|
+
linear-gradient(120deg, var(--accent-soft), var(--line) 30%, var(--line) 70%, var(--accent-soft)) border-box;
|
|
161
|
+
border: 1px solid transparent;
|
|
162
|
+
}
|
|
163
|
+
#reload:hover { box-shadow: 0 0 14px var(--accent-soft); border-color: var(--accent); }
|
|
164
|
+
.spark { display: flex; align-items: flex-end; gap: 3px; }
|
|
165
|
+
.spark > span { flex: 1; min-width: 3px; border-radius: 2px 2px 0 0; background: var(--accent); opacity: .45; }
|
|
166
|
+
.spark > span.top { opacity: 1; box-shadow: 0 0 8px var(--accent-soft); }
|
|
167
|
+
@media (prefers-reduced-motion: reduce) {
|
|
168
|
+
.rise { opacity: 1; transform: none; }
|
|
169
|
+
.live-dot { animation: none; }
|
|
170
|
+
.tilt, .btn-push, .mrow, .bar-fill { transition: none; }
|
|
171
|
+
.hero-glow, .ticker { animation: none; }
|
|
172
|
+
}
|
|
173
|
+
/* Empty states (shadcn pattern): dashed well, icon chip, title + hint. */
|
|
174
|
+
.empty { display: flex; flex-direction: column; align-items: center; text-align: center; gap: 2px; border: 1px dashed var(--line); border-radius: 12px; padding: 32px 16px; }
|
|
175
|
+
.empty-icon { display: grid; place-items: center; width: 40px; height: 40px; border-radius: 999px; background: var(--track); color: var(--dim); margin-bottom: 10px; }
|
|
176
|
+
.empty-title { font-weight: 600; font-size: 14px; color: var(--ink); margin: 0; }
|
|
177
|
+
.empty-desc { font-size: 12px; color: var(--dim); margin: 0; max-width: 340px; }
|
|
178
|
+
#reset:hover { box-shadow: 0 0 14px var(--accent-soft); border-color: var(--accent); }
|
|
179
|
+
/* Settings dialog (shadcn pattern over the native modal). */
|
|
180
|
+
.dlg { margin: auto; border: 1px solid var(--line); background: var(--panel); color: var(--ink); border-radius: 16px; padding: 20px; width: min(460px, calc(100vw - 32px)); box-shadow: 0 16px 48px rgba(0, 0, 0, .35); }
|
|
181
|
+
.dlg::backdrop { background: rgba(0, 0, 0, .8); }
|
|
182
|
+
.dlg-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
|
|
183
|
+
.dlg-title { font-weight: 700; font-size: 16px; letter-spacing: -0.01em; margin: 0; }
|
|
184
|
+
.dlg-desc { font-size: 12px; color: var(--dim); margin: 2px 0 0; }
|
|
185
|
+
.dlg-label { font-size: 11px; text-transform: uppercase; letter-spacing: .14em; color: var(--dim); margin: 18px 0 8px; }
|
|
186
|
+
.dlg-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
|
|
187
|
+
.dlg-name { font-size: 13px; font-weight: 600; margin: 0; }
|
|
188
|
+
.dlg-hint { font-size: 12px; color: var(--dim); margin: 2px 0 0; }
|
|
189
|
+
.dlg-own { font-weight: 400; font-size: 11px; color: var(--dim); }
|
|
190
|
+
.dlg-paths { margin-top: 12px; display: grid; gap: 8px; }
|
|
191
|
+
.dlg-path { border: 1px solid var(--line); border-radius: 10px; padding: 8px 12px; }
|
|
192
|
+
.dlg-file { font-size: 11px; color: var(--dim); margin: 4px 0 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
193
|
+
#settingsBtn:hover { box-shadow: 0 0 14px var(--accent-soft); border-color: var(--accent); }
|
|
194
|
+
.dlg .seg button { display: grid; place-items: center; }
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<meta name="color-scheme" content="dark light">
|
|
7
|
+
<script>(function(){try{var t=localStorage.getItem('tersio-theme');if(t==='light'||t==='dark')document.documentElement.setAttribute('data-theme',t);else document.documentElement.removeAttribute('data-theme');}catch(e){}})();</script>
|
|
8
|
+
<title>Tersio Dashboard</title>
|
|
9
|
+
<link rel="icon" type="image/png" href="brand.webp">
|
|
10
|
+
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
|
|
11
|
+
<script src="https://unpkg.com/lucide@latest"></script>
|
|
12
|
+
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js"></script>
|
|
13
|
+
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/ScrollTrigger.min.js"></script>
|
|
14
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource/outfit@5/index.css">
|
|
15
|
+
<link rel="stylesheet" href="styles.css">
|
|
16
|
+
</head>
|
|
17
|
+
<body class="min-h-[100dvh] antialiased">
|
|
18
|
+
<div class="bg-blueprint pointer-events-none fixed inset-0" aria-hidden="true"></div>
|
|
19
|
+
|
|
20
|
+
<main class="overflow-x-clip w-full max-w-full">
|
|
21
|
+
<div class="relative mx-auto max-w-7xl px-4 sm:px-6 pb-16">
|
|
22
|
+
<!-- Scroll progress rail: fixed left ticks, accent fill by scroll fraction -->
|
|
23
|
+
<nav id="rail" class="rail hidden md:flex" aria-label="Scroll progress"></nav>
|
|
24
|
+
|
|
25
|
+
<!-- Floating dock navbar -->
|
|
26
|
+
<header class="dock mt-4 flex w-full items-center gap-2 px-3 py-2">
|
|
27
|
+
<a href="#" class="flex shrink-0 items-center gap-2.5 pl-1 pr-2" aria-label="Tersio dashboard home">
|
|
28
|
+
<img src="brand.webp" alt="Tersio" width="32" height="32" class="brand size-8 rounded-full">
|
|
29
|
+
<span class="leading-tight text-left">
|
|
30
|
+
<span class="block font-bold tracking-tight text-sm">TERSIO DASHBOARD</span>
|
|
31
|
+
<span class="mono block text-[11px]" style="color: var(--dim)">live token number feed</span>
|
|
32
|
+
</span>
|
|
33
|
+
</a>
|
|
34
|
+
<span class="mono hidden shrink-0 sm:flex items-center gap-2 text-xs px-2 ml-auto" style="color: var(--dim)">
|
|
35
|
+
<span class="live-dot inline-block size-2 rounded-full"></span>
|
|
36
|
+
<span id="clock">--:--</span>
|
|
37
|
+
</span>
|
|
38
|
+
<span class="hidden sm:block w-px h-5 shrink-0" style="background: var(--line)" aria-hidden="true"></span>
|
|
39
|
+
<button id="reset" class="btn-push mono flex shrink-0 items-center gap-2 text-xs pl-2.5 pr-3 py-2 rounded-xl" style="border: 1px solid var(--line); color: var(--ink)" aria-label="Reset tersio statistics">
|
|
40
|
+
<i data-lucide="rotate-ccw" class="size-3.5"></i><span class="hidden md:inline" id="resetLabel">Reset</span>
|
|
41
|
+
</button>
|
|
42
|
+
<button id="settingsBtn" class="btn-push flex shrink-0 items-center p-2 rounded-xl" style="border: 1px solid var(--line); color: var(--ink)" aria-label="Open settings">
|
|
43
|
+
<i data-lucide="settings" class="size-4"></i>
|
|
44
|
+
</button>
|
|
45
|
+
</header>
|
|
46
|
+
|
|
47
|
+
<!-- Settings dialog (shadcn pattern, native modal) -->
|
|
48
|
+
<dialog id="settings" class="dlg" aria-labelledby="settingsTitle">
|
|
49
|
+
<div class="dlg-head">
|
|
50
|
+
<div class="min-w-0">
|
|
51
|
+
<p class="dlg-title" id="settingsTitle">Settings</p>
|
|
52
|
+
<p class="dlg-desc">Appearance, data refresh, and where statistics live.</p>
|
|
53
|
+
</div>
|
|
54
|
+
<button id="settingsClose" class="btn-push flex shrink-0 items-center p-2 rounded-xl" style="border: 1px solid var(--line); color: var(--ink)" aria-label="Close settings">
|
|
55
|
+
<i data-lucide="x" class="size-4"></i>
|
|
56
|
+
</button>
|
|
57
|
+
</div>
|
|
58
|
+
<p class="dlg-label">Appearance</p>
|
|
59
|
+
<div class="dlg-row">
|
|
60
|
+
<div class="min-w-0">
|
|
61
|
+
<p class="dlg-name">Theme</p>
|
|
62
|
+
<p class="dlg-hint">Light, dark, or follow the system.</p>
|
|
63
|
+
</div>
|
|
64
|
+
<div class="seg mono text-xs flex items-center gap-1 p-1 rounded-[10px]" style="border: 1px solid var(--line); background: var(--panel)" role="tablist" aria-label="Theme">
|
|
65
|
+
<button data-theme-val="light" class="px-2.5 py-1.5" role="tab" aria-label="Light" title="Light"><i data-lucide="sun" class="size-4"></i></button>
|
|
66
|
+
<button data-theme-val="dark" class="px-2.5 py-1.5" role="tab" aria-label="Dark" title="Dark"><i data-lucide="moon" class="size-4"></i></button>
|
|
67
|
+
<button data-theme-val="system" class="px-2.5 py-1.5" role="tab" aria-label="System" title="System"><i data-lucide="monitor" class="size-4"></i></button>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
<p class="dlg-label">Data</p>
|
|
71
|
+
<div class="dlg-row">
|
|
72
|
+
<div class="min-w-0">
|
|
73
|
+
<p class="dlg-name">Reload</p>
|
|
74
|
+
<p class="dlg-hint">Re-read statistics from disk.</p>
|
|
75
|
+
</div>
|
|
76
|
+
<button id="reload" class="btn-push mono flex shrink-0 items-center gap-2 text-xs pl-2.5 pr-3 py-2 rounded-xl" style="border: 1px solid var(--line); color: var(--ink)" aria-label="Reload data">
|
|
77
|
+
<i data-lucide="refresh-cw" class="size-3.5"></i><span>Reload</span>
|
|
78
|
+
</button>
|
|
79
|
+
</div>
|
|
80
|
+
<div class="dlg-paths">
|
|
81
|
+
<div class="dlg-path">
|
|
82
|
+
<p class="dlg-name">Usage ledger <span class="dlg-own">tersio-owned · reset clears</span></p>
|
|
83
|
+
<p class="mono dlg-file" id="pathLedger" title="">–</p>
|
|
84
|
+
</div>
|
|
85
|
+
<div class="dlg-path">
|
|
86
|
+
<p class="dlg-name">Session transcripts <span class="dlg-own">host-owned · never touched</span></p>
|
|
87
|
+
<p class="mono dlg-file" id="pathSessions" title="">–</p>
|
|
88
|
+
</div>
|
|
89
|
+
<div class="dlg-path">
|
|
90
|
+
<p class="dlg-name">RTK history <span class="dlg-own">rtk-owned · never touched</span></p>
|
|
91
|
+
<p class="mono dlg-file" id="pathRtk" title="">–</p>
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
</dialog>
|
|
95
|
+
|
|
96
|
+
<!-- Hero: centered total -->
|
|
97
|
+
<section class="mx-auto max-w-6xl text-center pt-14 pb-10 relative">
|
|
98
|
+
<div class="hero-glow" aria-hidden="true"></div>
|
|
99
|
+
<p class="hero-in mono text-[11px] uppercase tracking-[0.22em] mb-4 relative" style="color: var(--accent)">Live token feed</p>
|
|
100
|
+
<p class="hero-in mono font-bold tracking-tighter leading-none text-7xl md:text-8xl relative herototal" id="total">0</p>
|
|
101
|
+
<div class="hero-in relative mt-6 overflow-hidden" aria-hidden="true" style="mask-image: linear-gradient(90deg, transparent, black 12%, black 88%, transparent)">
|
|
102
|
+
<div class="ticker mono text-xs whitespace-nowrap inline-block" id="ticker" style="color: var(--dim)"></div>
|
|
103
|
+
</div>
|
|
104
|
+
</section>
|
|
105
|
+
|
|
106
|
+
<!-- Savings bento: big USD card + nested right rail -->
|
|
107
|
+
<section class="grid grid-cols-12 grid-flow-dense gap-3" aria-label="Savings">
|
|
108
|
+
<div class="rise col-span-12 lg:col-span-7 lg:row-span-2 rounded-xl p-6 flex flex-col justify-between" style="border: 1px solid var(--line); background: var(--panel)">
|
|
109
|
+
<div>
|
|
110
|
+
<div class="flex items-center justify-between gap-2">
|
|
111
|
+
<p class="mono text-[11px] uppercase tracking-[0.14em]" style="color: var(--dim)" id="costLabel">usd cost</p>
|
|
112
|
+
<span class="relative">
|
|
113
|
+
<button id="fxBtn" class="pill mono fxbtn" aria-haspopup="listbox" aria-expanded="false"><span id="fxCur">🇺🇸 USD</span><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"><path d="m6 9 6 6 6-6"/></svg></button>
|
|
114
|
+
<div id="fxPanel" class="fxpanel hidden" role="listbox" aria-label="Display currency"></div>
|
|
115
|
+
</span>
|
|
116
|
+
</div>
|
|
117
|
+
<p class="mono font-bold tracking-tighter leading-none mt-2 text-6xl lg:text-7xl" id="usd">-</p>
|
|
118
|
+
</div>
|
|
119
|
+
<div class="mt-4">
|
|
120
|
+
<div class="spark h-16" id="usdSpark"></div>
|
|
121
|
+
<div class="flex gap-4 mt-3 mono text-xs" style="color: var(--dim)">
|
|
122
|
+
<span>avg <span id="usdPerDay" style="color: var(--ink)">-</span>/day</span>
|
|
123
|
+
<span>top <span id="usdTopDay" style="color: var(--ink)">-</span></span>
|
|
124
|
+
<span title="Cache savings per $1 spent, vs full input price">x<span id="usdLeverage" style="color: var(--accent)">-</span> cache leverage</span>
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
127
|
+
<p class="mono text-[11px] mt-3" style="color: var(--dim)" id="usdNote">per-model price table</p>
|
|
128
|
+
</div>
|
|
129
|
+
<div class="col-span-12 lg:col-span-5 lg:row-span-2 grid grid-cols-2 grid-flow-dense gap-3 h-full grid-rows-[auto_1fr]">
|
|
130
|
+
<div class="rise col-span-2 rounded-xl p-5" style="border: 1px solid var(--line); background: var(--panel)">
|
|
131
|
+
<div class="flex items-center justify-between gap-2">
|
|
132
|
+
<div><p class="mono text-[11px] uppercase tracking-[0.14em]" style="color: var(--dim)">saved by cache</p>
|
|
133
|
+
<p class="mono font-bold text-3xl mt-1" style="color: var(--accent)" id="saved">-</p></div>
|
|
134
|
+
<span class="pill flat mono" title="Modeled estimate, not measured">est.</span>
|
|
135
|
+
</div>
|
|
136
|
+
<p class="mono text-[11px] mt-2" style="color: var(--dim)">est. vs full input price</p>
|
|
137
|
+
</div>
|
|
138
|
+
<div class="rise col-span-1 rounded-xl p-5" style="border: 1px solid var(--line); background: var(--panel)">
|
|
139
|
+
<div class="flex items-center justify-between gap-2">
|
|
140
|
+
<div><p class="mono text-[11px] uppercase tracking-[0.14em]" style="color: var(--dim)">co2</p>
|
|
141
|
+
<p class="mono font-bold text-3xl mt-1" id="co2">-</p>
|
|
142
|
+
<p class="mt-2"><span class="pill flat mono" title="Single-stream ceiling divided by 32-way served concurrency">served ÷32</span></p></div>
|
|
143
|
+
</div>
|
|
144
|
+
<p class="mono text-[11px] mt-2" style="color: var(--dim)">est. from output</p>
|
|
145
|
+
</div>
|
|
146
|
+
<div class="rise col-span-1 rounded-xl p-5" style="border: 1px solid var(--line); background: var(--panel)">
|
|
147
|
+
<div class="flex items-center justify-between gap-2">
|
|
148
|
+
<div><p class="mono text-[11px] uppercase tracking-[0.14em]" style="color: var(--dim)">cache share</p>
|
|
149
|
+
<p class="mono font-bold text-3xl mt-1" id="cacheShare">-</p>
|
|
150
|
+
<p class="mt-2"><span class="pill good mono" id="cacheDelta">-</span></p></div>
|
|
151
|
+
</div>
|
|
152
|
+
<p class="mono text-[11px] mt-2" style="color: var(--dim)">of tokens cached</p>
|
|
153
|
+
</div>
|
|
154
|
+
</div>
|
|
155
|
+
</section>
|
|
156
|
+
|
|
157
|
+
<!-- Token strip: nonzero buckets only -->
|
|
158
|
+
<section class="rise mt-3 flex rounded-xl overflow-hidden" style="border: 1px solid var(--line); background: var(--panel)" id="tokens" aria-label="Tokens by bucket"></section>
|
|
159
|
+
|
|
160
|
+
<!-- Activity: full-width year heatmap -->
|
|
161
|
+
<section id="activity" class="rise mt-8" style="scroll-margin-top: 90px" aria-label="Activity graph">
|
|
162
|
+
<div class="flex items-center gap-2 mb-1">
|
|
163
|
+
<i data-lucide="calendar-days" class="size-4" style="color: var(--accent)"></i>
|
|
164
|
+
<h2 class="display font-bold tracking-tight text-xl truncate min-w-0">Activity</h2>
|
|
165
|
+
<div class="seg mono text-xs flex items-center gap-1 ml-auto p-1 rounded-[10px]" style="border: 1px solid var(--line); background: var(--panel)" role="tablist" aria-label="Graph range">
|
|
166
|
+
<button data-gmode="daily" class="on px-3 py-1.5" role="tab">Daily</button>
|
|
167
|
+
<button data-gmode="weekly" class="px-3 py-1.5" role="tab">Weekly</button>
|
|
168
|
+
<button data-gmode="cumulative" class="px-3 py-1.5" role="tab">Cumulative</button>
|
|
169
|
+
</div>
|
|
170
|
+
</div>
|
|
171
|
+
<p class="mono text-xs mb-4 truncate" style="color: var(--dim)"><span id="graphRange"></span> · <span id="graphCap">daily values / trailing 12 months</span></p>
|
|
172
|
+
<div class="rounded-xl p-5 overflow-hidden" style="border: 1px solid var(--line); background: var(--panel)">
|
|
173
|
+
<div class="pb-1">
|
|
174
|
+
<div class="grid w-full" style="grid-template-columns: repeat(53, minmax(0, 1fr)); gap: 3px;" id="graph"></div>
|
|
175
|
+
</div>
|
|
176
|
+
<div class="mono text-[11px] mt-3 flex items-center gap-1.5" style="color: var(--dim)">
|
|
177
|
+
<span>less</span>
|
|
178
|
+
<span class="cell key"></span><span class="cell key l1"></span><span class="cell key l2"></span><span class="cell key l3"></span><span class="cell key l4"></span>
|
|
179
|
+
<span>more</span>
|
|
180
|
+
</div>
|
|
181
|
+
<div class="heatmonth mono mt-2 grid w-full" style="color: var(--dim); grid-template-columns: repeat(53, minmax(0, 1fr)); gap: 3px;" id="graphMonths"></div>
|
|
182
|
+
<div id="emptyGraph" class="empty hidden">
|
|
183
|
+
<span class="empty-icon"><i data-lucide="calendar-x" class="size-5"></i></span>
|
|
184
|
+
<p class="empty-title">No activity yet</p>
|
|
185
|
+
<p class="empty-desc">Daily token activity will chart here once sessions report tokens.</p>
|
|
186
|
+
</div>
|
|
187
|
+
</div>
|
|
188
|
+
</section>
|
|
189
|
+
|
|
190
|
+
<!-- Top models: 3 columns -->
|
|
191
|
+
<section class="mt-8" aria-label="Top models">
|
|
192
|
+
<div class="flex items-center gap-2 mb-1">
|
|
193
|
+
<i data-lucide="cpu" class="size-4" style="color: var(--accent)"></i>
|
|
194
|
+
<h2 class="display font-bold tracking-tight text-xl truncate min-w-0">Top models</h2>
|
|
195
|
+
<span class="mono text-xs ml-auto" style="color: var(--dim)">usage across sessions</span>
|
|
196
|
+
</div>
|
|
197
|
+
<p class="mono text-xs mb-4" style="color: var(--dim)">week-over-week vs prior 7 days</p>
|
|
198
|
+
<div class="grid grid-cols-1 md:grid-cols-3 gap-3" id="modelCards"></div>
|
|
199
|
+
</section>
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
<!-- Models + recent: twin cards -->
|
|
203
|
+
<div class="rise grid grid-cols-12 grid-flow-dense gap-3 mt-8">
|
|
204
|
+
<section class="col-span-12 lg:col-span-7 rounded-xl p-5 overflow-hidden" style="border: 1px solid var(--line); background: var(--panel)" aria-label="All models">
|
|
205
|
+
<div class="flex items-center gap-2 mb-1">
|
|
206
|
+
<i data-lucide="layers" class="size-4" style="color: var(--accent)"></i>
|
|
207
|
+
<h2 class="display font-bold tracking-tight text-xl truncate min-w-0">Models</h2>
|
|
208
|
+
<span class="mono text-xs ml-auto truncate shrink-0" style="color: var(--dim)" id="modelsCount"></span>
|
|
209
|
+
</div>
|
|
210
|
+
<p class="mono text-xs mb-4" style="color: var(--dim)">ranked by tokens / top 8</p>
|
|
211
|
+
<div class="scrollarea" style="max-height:410px"><ol id="models" class="overflow-hidden"></ol></div>
|
|
212
|
+
<div id="emptyModels" class="empty hidden">
|
|
213
|
+
<span class="empty-icon"><i data-lucide="boxes" class="size-5"></i></span>
|
|
214
|
+
<p class="empty-title">No models yet</p>
|
|
215
|
+
<p class="empty-desc">Model token totals will appear here once sessions report tokens.</p>
|
|
216
|
+
</div>
|
|
217
|
+
</section>
|
|
218
|
+
<section class="col-span-12 lg:col-span-5 rounded-xl p-5 overflow-hidden" style="border: 1px solid var(--line); background: var(--panel)" aria-label="Recent requests">
|
|
219
|
+
<div class="flex items-center gap-2 mb-1">
|
|
220
|
+
<i data-lucide="history" class="size-4" style="color: var(--accent)"></i>
|
|
221
|
+
<h2 class="display font-bold tracking-tight text-xl truncate min-w-0">Recent requests</h2>
|
|
222
|
+
<span class="mono text-xs ml-auto truncate shrink-0" style="color: var(--dim)" id="recentCount"></span>
|
|
223
|
+
</div>
|
|
224
|
+
<p class="mono text-xs mb-4 truncate" style="color: var(--dim)">latest assistant messages</p>
|
|
225
|
+
<div class="scrollarea overflow-x-auto" style="max-height:410px">
|
|
226
|
+
<table class="w-full mono text-[13px]" id="recentTable">
|
|
227
|
+
<colgroup><col><col style="width:150px"><col style="width:90px"></colgroup>
|
|
228
|
+
<thead>
|
|
229
|
+
<tr class="text-left text-[11px] uppercase tracking-[0.14em]" style="color: var(--dim)">
|
|
230
|
+
<th class="font-normal py-2 pr-3">Model</th>
|
|
231
|
+
<th class="font-normal text-right py-2 pr-3">In / Out</th>
|
|
232
|
+
<th class="font-normal text-right py-2">When</th>
|
|
233
|
+
</tr>
|
|
234
|
+
</thead>
|
|
235
|
+
<tbody id="recent"></tbody>
|
|
236
|
+
</table>
|
|
237
|
+
</div>
|
|
238
|
+
<div id="emptyRecent" class="empty hidden">
|
|
239
|
+
<span class="empty-icon"><i data-lucide="inbox" class="size-5"></i></span>
|
|
240
|
+
<p class="empty-title">No requests yet</p>
|
|
241
|
+
<p class="empty-desc">Recent assistant messages will show here once sessions report tokens.</p>
|
|
242
|
+
</div>
|
|
243
|
+
</section>
|
|
244
|
+
</div>
|
|
245
|
+
|
|
246
|
+
<!-- Command tools: own card, header outside like Models -->
|
|
247
|
+
<section id="tools" class="rise mt-8 rounded-xl p-5 overflow-hidden" style="border: 1px solid var(--line); background: var(--panel); scroll-margin-top: 90px" aria-label="Command tools">
|
|
248
|
+
<div class="flex items-center gap-2 mb-1">
|
|
249
|
+
<i data-lucide="terminal" class="size-4" style="color: var(--accent)"></i>
|
|
250
|
+
<h2 class="display font-bold tracking-tight text-xl truncate min-w-0">Command tools</h2>
|
|
251
|
+
<span class="mono text-xs ml-auto truncate min-w-0" style="color: var(--dim)" id="toolsScope">tool calls in sessions</span>
|
|
252
|
+
</div>
|
|
253
|
+
<p class="mono text-xs mb-4 truncate" style="color: var(--dim)">count · tokens saved · avg rate · avg time · share of executions</p>
|
|
254
|
+
<div class="overflow-x-auto">
|
|
255
|
+
<table class="w-full mono text-[13px]" id="cmdTable">
|
|
256
|
+
<colgroup><col style="width:40px"><col><col style="width:90px"><col style="width:90px"><col style="width:90px"><col style="width:90px"><col style="width:140px"></colgroup>
|
|
257
|
+
<thead>
|
|
258
|
+
<tr class="text-left text-[11px] uppercase tracking-[0.14em]" style="color: var(--dim)">
|
|
259
|
+
<th class="font-normal text-right pr-3 py-2 w-10">#</th>
|
|
260
|
+
<th class="font-normal py-2 pr-3" aria-sort="none"><button class="thsort" data-sort="name">Tool / Command <span class="arr">⇅</span></button></th>
|
|
261
|
+
<th class="font-normal text-right py-2 pr-3" aria-sort="descending"><button class="thsort num" data-sort="count">Count <span class="arr">↓</span></button></th>
|
|
262
|
+
<th class="font-normal text-right py-2 pr-3" aria-sort="none"><button class="thsort num" data-sort="saved">Saved <span class="arr">⇅</span></button></th>
|
|
263
|
+
<th class="font-normal text-right py-2 pr-3" aria-sort="none"><button class="thsort num" data-sort="avgPct">Avg% <span class="arr">⇅</span></button></th>
|
|
264
|
+
<th class="font-normal text-right py-2 pr-3" aria-sort="none"><button class="thsort num" data-sort="avgMs">Time <span class="arr">⇅</span></button></th>
|
|
265
|
+
<th class="font-normal py-2 min-w-32">Impact</th>
|
|
266
|
+
</tr>
|
|
267
|
+
</thead>
|
|
268
|
+
<tbody id="cmdBody"></tbody>
|
|
269
|
+
</table>
|
|
270
|
+
</div>
|
|
271
|
+
<div id="emptyCmd" class="empty hidden">
|
|
272
|
+
<span class="empty-icon"><i data-lucide="wrench" class="size-5"></i></span>
|
|
273
|
+
<p class="empty-title">No tool data yet</p>
|
|
274
|
+
<p class="empty-desc">Session tool calls and RTK measurements will appear here once recorded.</p>
|
|
275
|
+
</div>
|
|
276
|
+
<div id="cmdPager" class="mono text-xs mt-4 flex flex-wrap items-center gap-x-4 gap-y-3" style="color: var(--dim)">
|
|
277
|
+
<span id="cmdRange">Showing 0 of 0</span>
|
|
278
|
+
<span class="seg flex items-center gap-1 p-1 rounded-[10px]" style="border: 1px solid var(--line)" role="group" aria-label="Rows per page">
|
|
279
|
+
<button data-pp="10" class="px-2.5 py-1" role="button">10</button>
|
|
280
|
+
<button data-pp="15" class="on px-2.5 py-1" role="button">15</button>
|
|
281
|
+
<button data-pp="25" class="px-2.5 py-1" role="button">25</button>
|
|
282
|
+
<button data-pp="50" class="px-2.5 py-1" role="button">50</button>
|
|
283
|
+
</span>
|
|
284
|
+
<span class="ml-auto flex items-center gap-1" id="cmdPages" role="navigation" aria-label="Pages"></span>
|
|
285
|
+
</div>
|
|
286
|
+
<p class="mono text-[11px] mt-3" style="color: var(--dim)">saved / avg / time are RTK-metered per command — session tools without metering show –. caveman + ponytail gains are instruction-following, bench-measured in BENCHMARK.md.</p>
|
|
287
|
+
</section>
|
|
288
|
+
|
|
289
|
+
<footer class="mono text-xs mt-12 pt-4" style="color: var(--dim); border-top: 1px solid var(--line)">
|
|
290
|
+
<div class="flex flex-wrap items-center gap-x-4 gap-y-3">
|
|
291
|
+
<span>© 2026 Tersio. KurutoDenzeru. All rights reserved.</span>
|
|
292
|
+
<span class="pill flat" id="verChip">tersio</span>
|
|
293
|
+
<span class="ml-auto flex items-center gap-1" style="color: var(--ink)">
|
|
294
|
+
<a href="https://github.com/KurutoDenzeru/tersio" target="_blank" rel="noopener" aria-label="GitHub" class="btn-push social grid size-8 place-items-center rounded-lg"><img height="16" width="16" src="https://cdn.jsdelivr.net/npm/simple-icons@v16/icons/github.svg" alt="GitHub"></a>
|
|
295
|
+
<a href="https://linkedin.com/in/kurtcalacday/" target="_blank" rel="noopener" aria-label="LinkedIn" class="btn-push social grid size-8 place-items-center rounded-lg"><svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/></svg></a>
|
|
296
|
+
<a href="https://instagram.com/krtclcdy/" target="_blank" rel="noopener" aria-label="Instagram" class="btn-push social grid size-8 place-items-center rounded-lg"><img height="16" width="16" src="https://cdn.jsdelivr.net/npm/simple-icons@v16/icons/instagram.svg" alt="Instagram"></a>
|
|
297
|
+
</span>
|
|
298
|
+
</div>
|
|
299
|
+
</footer>
|
|
300
|
+
</div>
|
|
301
|
+
</main>
|
|
302
|
+
|
|
303
|
+
<script src="app.js" defer></script>
|
|
304
|
+
</body>
|
|
305
|
+
</html>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
2
|
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { activeModesSummary, asPromptArray, getSharedComboState, isComboPresetActive, isOmpSubagentPrompt, lastCustomValue, normalizeInputCommand, normalizeMode, paintStatusBar, reconcileSharedComboEntries, sessionEntries, setSharedComboListener, setSharedComboMode } from '../shared/session-state.js';
|
|
3
4
|
import { dirname, join } from 'node:path';
|
|
4
|
-
import { activeModesSummary, asPromptArray, getSharedComboState, isComboPresetActive, isOmpSubagentPrompt, lastCustomValue, normalizeInputCommand, normalizeMode, paintStatusBar, reconcileSharedComboEntries, sessionEntries, setSharedComboMode } from '../shared/session-state.js';
|
|
5
5
|
import { readCavemanDefault } from '../shared/plugin-settings.js';
|
|
6
6
|
const CAVERN_DIR = dirname(fileURLToPath(import.meta.url));
|
|
7
7
|
const RULE_PATH = join(CAVERN_DIR, 'rule.md');
|
|
@@ -74,6 +74,17 @@ export default function cavemanSessionExtension(pi) {
|
|
|
74
74
|
return true;
|
|
75
75
|
}
|
|
76
76
|
pi.setLabel?.('Caveman session toggle');
|
|
77
|
+
// Live mirror: a /tersio or /combo switch publishes shared state — adopt
|
|
78
|
+
// it at once so the next turn injects the new mode with no session reload.
|
|
79
|
+
// Stable identity, so the bridge set dedupes across re-inits.
|
|
80
|
+
function syncFromShared(state) {
|
|
81
|
+
const mode = normalizeMode('caveman', state.caveman);
|
|
82
|
+
if (mode) {
|
|
83
|
+
currentMode = mode;
|
|
84
|
+
syncStatus();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
setSharedComboListener(syncFromShared);
|
|
77
88
|
pi.registerCommand?.('caveman', {
|
|
78
89
|
description: 'Toggle terse caveman replies for this session',
|
|
79
90
|
handler: async (args, ctx) => {
|
|
@@ -78,8 +78,10 @@ export default function comboToggleExtension(pi) {
|
|
|
78
78
|
return useState(reconcileSharedComboEntries(sessionEntries(ctx)), ctx);
|
|
79
79
|
}
|
|
80
80
|
function listen(ctx) {
|
|
81
|
+
// Stable identity: the bridge set dedupes, so repeated track()/command
|
|
82
|
+
// calls register once instead of stacking duplicate listeners.
|
|
81
83
|
if (ctx?.hasUI)
|
|
82
|
-
setSharedComboListener(
|
|
84
|
+
setSharedComboListener(useState);
|
|
83
85
|
}
|
|
84
86
|
function track(ctx) {
|
|
85
87
|
listen(ctx);
|
|
@@ -112,8 +114,6 @@ export default function comboToggleExtension(pi) {
|
|
|
112
114
|
useState(setSharedComboLevel(level), ctx);
|
|
113
115
|
const active = activeModesSummary(getSharedComboState());
|
|
114
116
|
ctx?.ui?.notify?.(level === 'off' ? `Combo off — all tersio modes inactive for this session. Active: ${active}.` : `Combo ${level} on — ${active} active for this session.`, 'info');
|
|
115
|
-
if (ctx?.reload)
|
|
116
|
-
await ctx.reload();
|
|
117
117
|
},
|
|
118
118
|
});
|
|
119
119
|
pi.on('session_start', async (_event, ctx) => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import os from 'node:os';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { activeModesSummary, asPromptArray, getSharedComboState, isComboPresetActive, isOmpSubagentPrompt, lastCustomValue, normalizeInputCommand, paintStatusBar, reconcileSharedComboEntries, sessionEntries, setSharedComboMode } from '../shared/session-state.js';
|
|
3
|
+
import { activeModesSummary, asPromptArray, getSharedComboState, isComboPresetActive, isOmpSubagentPrompt, lastCustomValue, normalizeInputCommand, paintStatusBar, reconcileSharedComboEntries, sessionEntries, setSharedComboListener, setSharedComboMode } from '../shared/session-state.js';
|
|
4
4
|
import { readRtkDefault } from '../shared/plugin-settings.js';
|
|
5
5
|
const DEFAULT_ENABLED = false;
|
|
6
6
|
function asBoolean(value) {
|
|
@@ -48,6 +48,13 @@ export default function rtkSessionExtension(pi) {
|
|
|
48
48
|
ctx?.ui?.notify?.(enabled ? `RTK on — compact shell output for this session. Active: ${active}.` : `RTK off. Active: ${active}.`, 'info');
|
|
49
49
|
}
|
|
50
50
|
pi.setLabel?.('RTK session toggle');
|
|
51
|
+
// Live mirror: a /tersio or /combo switch publishes shared state — adopt
|
|
52
|
+
// it at once so the next turn and the rtk_run gate see it, no reload.
|
|
53
|
+
function syncFromShared(state) {
|
|
54
|
+
enabled = state.rtk === 'on';
|
|
55
|
+
syncStatus();
|
|
56
|
+
}
|
|
57
|
+
setSharedComboListener(syncFromShared);
|
|
51
58
|
pi.registerCommand?.('rtk', {
|
|
52
59
|
description: 'Toggle RTK compact shell-output guidance for this session',
|
|
53
60
|
handler: async (args, ctx) => {
|