yoyo-pi 0.1.4

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.
@@ -0,0 +1,379 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en"><head>
3
+ <meta charset="utf-8">
4
+ <title>pi-TUI · agent status variations</title>
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <style>
7
+ html, body { margin: 0; padding: 0; }
8
+ body {
9
+ font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
10
+ background: #1a1916;
11
+ color: #ECE7DC;
12
+ min-height: 100vh;
13
+ padding: 56px 40px 80px;
14
+ -webkit-font-smoothing: antialiased;
15
+ }
16
+ .page-h, .theme-switch, .grid, .jump { max-width: 980px; margin-left: auto; margin-right: auto; }
17
+ .page-h { margin-bottom: 22px; }
18
+ h1 { margin: 0 0 8px; font-size: 40px; letter-spacing: -0.02em; }
19
+ .sub { color: #9A9385; font-size: 15px; max-width: 74ch; line-height: 1.6; }
20
+ .jump { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 18px; }
21
+ .jump a, .theme-switch button {
22
+ font: inherit;
23
+ color: #B0A99A;
24
+ background: transparent;
25
+ border: 1px solid rgba(236,231,220,0.18);
26
+ border-radius: 999px;
27
+ padding: 5px 12px;
28
+ text-decoration: none;
29
+ cursor: pointer;
30
+ }
31
+ .jump a:hover, .theme-switch button:hover { background: rgba(236,231,220,0.06); color: #ECE7DC; }
32
+ .theme-switch { margin-bottom: 28px; display: flex; gap: 8px; align-items: center; color: #9A9385; font-size: 13px; }
33
+ .theme-switch button[aria-pressed="true"] { background: rgba(216,155,126,0.15); color: #D89B7E; border-color: rgba(216,155,126,0.42); }
34
+ .grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 28px; }
35
+ @media (max-width: 760px) { .grid { grid-template-columns: 1fr; } body { padding: 34px 18px 56px; } }
36
+ .card { display: flex; flex-direction: column; gap: 10px; scroll-margin-top: 28px; }
37
+ .card:target .tui-screen { border-color: rgba(216,155,126,0.62); box-shadow: 0 0 0 2px rgba(216,155,126,0.22), 0 14px 30px rgba(0,0,0,0.28); }
38
+ .label { display: flex; flex-wrap: wrap; gap: 10px; color: #777768; font-size: 13px; align-items: baseline; }
39
+ .label .n { color: #D89B7E; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
40
+ .label .name { color: #ECE7DC; font-weight: 600; }
41
+ .label a { color: #8FB4CE; text-decoration: none; }
42
+ .label a:hover { text-decoration: underline; }
43
+
44
+ .tui-screen {
45
+ --tui-bg: #EFE7D4;
46
+ --tui-bg-edge: #E5DBC2;
47
+ --tui-ink: #1D1916;
48
+ --tui-ink-2: #5B524A;
49
+ --tui-ink-3: #8C8175;
50
+ --tui-rule: rgba(29,25,22,0.18);
51
+ --tui-red: #8C2A1F;
52
+ --tui-red-bg: rgba(140,42,31,0.10);
53
+ --tui-yellow: #8A6A1F;
54
+ --tui-yellow-bg: rgba(138,106,31,0.12);
55
+ --tui-green: #3F5C45;
56
+ --tui-green-bg: rgba(63,92,69,0.12);
57
+ --tui-blue: #2F5A78;
58
+ --tui-blue-bg: rgba(47,90,120,0.10);
59
+ --tui-purple: #6E4A78;
60
+ --tui-paper-tex: 0.6;
61
+
62
+ position: relative;
63
+ overflow: hidden;
64
+ background: var(--tui-bg);
65
+ color: var(--tui-ink);
66
+ border: 1px solid var(--tui-rule);
67
+ border-radius: 10px;
68
+ padding: 22px 24px;
69
+ box-shadow: 0 1px 0 rgba(0,0,0,0.35), 0 14px 30px rgba(0,0,0,0.28);
70
+ font: 13px/1.65 ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
71
+ white-space: pre;
72
+ }
73
+ .tui-screen::before {
74
+ content: '';
75
+ position: absolute;
76
+ inset: 0;
77
+ pointer-events: none;
78
+ background:
79
+ radial-gradient(circle at 11% 17%, rgba(0,0,0,0.025) 0.5px, transparent 1px),
80
+ radial-gradient(circle at 71% 39%, rgba(255,255,255,0.06) 0.5px, transparent 1px),
81
+ radial-gradient(circle at 33% 67%, rgba(0,0,0,0.02) 0.5px, transparent 1px);
82
+ background-size: 7px 7px, 13px 13px, 19px 19px;
83
+ opacity: var(--tui-paper-tex);
84
+ }
85
+ .tui-screen > * { position: relative; z-index: 1; }
86
+ body[data-theme="dark"] .tui-screen {
87
+ --tui-bg: #2E322E;
88
+ --tui-bg-edge: #272A27;
89
+ --tui-ink: #ECE7DC;
90
+ --tui-ink-2: #B0B0A5;
91
+ --tui-ink-3: #777768;
92
+ --tui-rule: rgba(236,231,220,0.18);
93
+ --tui-red: #D89B7E;
94
+ --tui-red-bg: rgba(216,155,126,0.14);
95
+ --tui-yellow: #D9B670;
96
+ --tui-yellow-bg: rgba(217,182,112,0.16);
97
+ --tui-green: #ADC79A;
98
+ --tui-green-bg: rgba(173,199,154,0.14);
99
+ --tui-blue: #8FB4CE;
100
+ --tui-blue-bg: rgba(143,180,206,0.12);
101
+ --tui-purple: #BCA0C5;
102
+ --tui-paper-tex: 0;
103
+ }
104
+ body[data-theme="dark"] .tui-screen::before { display: none; }
105
+ body[data-theme="light"] .tui-screen {
106
+ --tui-bg: #F5F1EA;
107
+ --tui-bg-edge: #ECE7DC;
108
+ --tui-ink: #2A2D28;
109
+ --tui-ink-2: #6B6B60;
110
+ --tui-ink-3: #9A9A8E;
111
+ --tui-rule: rgba(42,45,40,0.14);
112
+ --tui-red: #A8734F;
113
+ --tui-yellow: #A88A50;
114
+ --tui-green: #6F8A68;
115
+ --tui-blue: #5F8880;
116
+ --tui-purple: #8A6E9C;
117
+ --tui-paper-tex: 0;
118
+ }
119
+ body[data-theme="light"] .tui-screen::before { display: none; }
120
+
121
+ .tui-line { display: block; white-space: pre; font-feature-settings: 'liga' 0, 'calt' 0; }
122
+ .tui-red { color: var(--tui-red); }
123
+ .tui-yel { color: var(--tui-yellow); }
124
+ .tui-grn { color: var(--tui-green); }
125
+ .tui-blu { color: var(--tui-blue); }
126
+ .tui-pur { color: var(--tui-purple); }
127
+ .tui-dim { color: var(--tui-ink-2); }
128
+ .tui-faint { color: var(--tui-ink-3); }
129
+ .tui-bold { font-weight: 700; }
130
+ .tui-italic { font-style: italic; }
131
+ .tui-fill-red { background: var(--tui-red-bg); color: var(--tui-red); }
132
+ .tui-fill-yel { background: var(--tui-yellow-bg); color: var(--tui-yellow); }
133
+ .tui-fill-grn { background: var(--tui-green-bg); color: var(--tui-green); }
134
+ .tui-fill-blu { background: var(--tui-blue-bg); color: var(--tui-blue); }
135
+ .tui-fill-dim { background: rgba(0,0,0,0.05); }
136
+ .tui-spin { display: inline-block; width: 1ch; text-align: center; }
137
+ .tui-spin::before { content: attr(data-frame); }
138
+ .tui-dots { display: inline-block; min-width: 3ch; white-space: pre; }
139
+ </style>
140
+ </head>
141
+ <body data-theme="dark">
142
+ <header class="page-h">
143
+ <h1>Agent status variations</h1>
144
+ <p class="sub">gr0k-hack replaces pi's default streaming loader with selectable terminal-native status readouts. These previews mirror <code>/switch-agentStatus 1</code> through <code>9</code>: phase, latest visible assistant text, and the current/read/changed file. Edit and write renderers show file names only—no diffs or file contents.</p>
145
+ </header>
146
+
147
+ <nav class="jump" aria-label="Jump to variant">
148
+ <a href="#v1">V1 hairline</a>
149
+ <a href="#v2">V2 phase</a>
150
+ <a href="#v3">V3 rail</a>
151
+ <a href="#v4">V4 card</a>
152
+ <a href="#v5">V5 margin</a>
153
+ <a href="#v6">V6 sigil</a>
154
+ <a href="#v7">V7 split</a>
155
+ <a href="#v8">V8 micro</a>
156
+ <a href="#v9">V9 stamp</a>
157
+ </nav>
158
+
159
+ <div class="theme-switch" role="group" aria-label="Theme">
160
+ <span>Theme</span>
161
+ <button type="button" data-theme-btn="paper">paper</button>
162
+ <button type="button" data-theme-btn="light">light</button>
163
+ <button type="button" data-theme-btn="dark" aria-pressed="true">dark</button>
164
+ </div>
165
+
166
+ <main id="grid" class="grid"></main>
167
+
168
+ <script>
169
+ function tuiRender(line) {
170
+ const out = [];
171
+ const stack = [];
172
+ let buf = '';
173
+ let i = 0;
174
+ const flush = () => {
175
+ if (!buf) return;
176
+ const text = escapeHtml(buf);
177
+ if (stack.length) {
178
+ const cls = stack.map(mapClass).join(' ');
179
+ out.push(`<span class="${cls}">${text}</span>`);
180
+ } else {
181
+ out.push(text);
182
+ }
183
+ buf = '';
184
+ };
185
+ while (i < line.length) {
186
+ if (line[i] === '«' && line.charAt(i + 1) === '/' && line.charAt(i + 2) === '»') {
187
+ flush();
188
+ stack.pop();
189
+ i += 3;
190
+ } else if (line[i] === '«') {
191
+ const close = line.indexOf('»', i + 1);
192
+ if (close === -1) { buf += line[i]; i++; continue; }
193
+ flush();
194
+ const tok = line.slice(i + 1, close);
195
+ if (tok === 'spin') {
196
+ out.push('<span class="tui-spin" data-frame="⠋"></span>');
197
+ } else if (tok === 'dots') {
198
+ out.push('<span class="tui-dots"></span>');
199
+ } else {
200
+ stack.push(tok);
201
+ }
202
+ i = close + 1;
203
+ } else {
204
+ buf += line[i];
205
+ i++;
206
+ }
207
+ }
208
+ flush();
209
+ return out.join('');
210
+ }
211
+ function escapeHtml(s) {
212
+ return s.replace(/[&<>]/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;'}[c]));
213
+ }
214
+ function mapClass(k) {
215
+ return ({
216
+ red:'tui-red', yel:'tui-yel', grn:'tui-grn', blu:'tui-blu', pur:'tui-pur',
217
+ dim:'tui-dim', faint:'tui-faint', b:'tui-bold', it:'tui-italic',
218
+ fred:'tui-fill-red', fyel:'tui-fill-yel', fgrn:'tui-fill-grn',
219
+ fblu:'tui-fill-blu', fdim:'tui-fill-dim',
220
+ }[k] || k);
221
+ }
222
+ function tuiLen(line) {
223
+ return line.replace(/«spin»|«dots»|«pulse»/g, ' ').replace(/«[^»]*»/g, '').length;
224
+ }
225
+ function tuiPad(line, width, fill = ' ') {
226
+ const n = tuiLen(line);
227
+ if (n >= width) return line;
228
+ return line + fill.repeat(width - n);
229
+ }
230
+ function trunc(s, w) {
231
+ if (s.length <= w) return s;
232
+ return '…' + s.slice(s.length - w + 1);
233
+ }
234
+
235
+ const S = {
236
+ thinking: { label: 'thinking', tone: 'yel', tonefill: 'fyel', glyph: '✱', thought: 'planning the refactor — extract picker state into a provider', file: '~/sage-app/src/components/FilePicker.tsx' },
237
+ executing: { label: 'executing', tone: 'red', tonefill: 'fred', glyph: '▶', thought: 'applying edit · move usePickerState → PickerContext', file: '~/sage-app/src/components/PickerContext.tsx' },
238
+ reading: { label: 'reading', tone: 'blu', tonefill: 'fblu', glyph: '◐', thought: 'scanning callers of usePickerState (4 files)', file: '~/sage-app/src/hooks/usePickerState.ts' },
239
+ writing: { label: 'writing', tone: 'grn', tonefill: 'fgrn', glyph: '✎', thought: 'committing the new provider boundary', file: '~/sage-app/src/components/FilePicker.tsx' },
240
+ };
241
+
242
+ function v1_hairline(scn) {
243
+ const W = 86, L = [];
244
+ const badge = `«${scn.tonefill}» ${scn.label.toUpperCase()} «/»`;
245
+ L.push(' ' + badge + ' «spin» «' + scn.tone + '»' + scn.glyph + '«/» «faint»14:23:09«/»');
246
+ L.push(' «dim»' + tuiPad(scn.thought + '«dots»', W - 2) + '«/»');
247
+ L.push(' «faint»↳«/» «' + scn.tone + '»' + trunc(scn.file, W - 6) + '«/»');
248
+ return L;
249
+ }
250
+ function v2_step(scn) {
251
+ const W = 86;
252
+ return [
253
+ ' «faint»[agent] «/»«' + scn.tone + '»' + scn.label.toUpperCase() + '«/»«dots»',
254
+ ' «dim»❝ ' + scn.thought + ' ❞«/»',
255
+ ' «faint»file «/»«' + scn.tone + '»' + trunc(scn.file, W - 8) + '«/»',
256
+ ];
257
+ }
258
+ function v3_rail(scn) {
259
+ const W = 86;
260
+ return [
261
+ '«' + scn.tone + '»▌«/» «b»' + scn.label.toUpperCase() + '«/» «spin» «faint»agent · turn 7«/»',
262
+ '«' + scn.tone + '»▌«/» «dim»' + scn.thought + '«/»«dots»',
263
+ '«' + scn.tone + '»▌«/» «faint»↳«/» «' + scn.tone + '»' + trunc(scn.file, W - 6) + '«/»',
264
+ ];
265
+ }
266
+ function v4_card(scn) {
267
+ const W = 86, inner = W - 2;
268
+ const head = ' «spin» «b»' + scn.label.toUpperCase() + '«/»«dots»' + ' «faint»· agent · turn 7«/»';
269
+ const mid = ' «dim»' + scn.thought + '«/»';
270
+ const foot = ' «faint»↳«/» «' + scn.tone + '»' + trunc(scn.file, inner - 4) + '«/»';
271
+ return [
272
+ '╭' + '─'.repeat(W - 2) + '╮',
273
+ '│' + tuiPad(head, inner) + '│',
274
+ '├' + '┄'.repeat(W - 2) + '┤',
275
+ '│' + tuiPad(mid, inner) + '│',
276
+ '│' + tuiPad(foot, inner) + '│',
277
+ '╰' + '─'.repeat(W - 2) + '╯',
278
+ ];
279
+ }
280
+ function v5_margin(scn) {
281
+ const W = 86, G = 12;
282
+ const m = s => tuiPad(s, G);
283
+ return [
284
+ m('«faint»state«/»') + '«' + scn.tonefill + '» ' + scn.label.toUpperCase() + ' «/»' + ' «spin» «faint»turn 7«/»',
285
+ m('«faint»thought«/»') + '«dim»' + scn.thought + '«/»«dots»',
286
+ m('«faint»file«/»') + '«' + scn.tone + '»' + trunc(scn.file, W - G) + '«/»',
287
+ ];
288
+ }
289
+ function v6_sigil(scn) {
290
+ const W = 86;
291
+ return [
292
+ '«faint»::«/» «' + scn.tone + '»' + scn.label + '«/»«dots»' + ' «faint»turn 7 · 1.4s«/»',
293
+ '«faint» ›«/» «dim»' + scn.thought + '«/»',
294
+ '«faint» ⌁«/» «' + scn.tone + '»' + trunc(scn.file, W - 6) + '«/»',
295
+ ];
296
+ }
297
+ function v7_split(scn) {
298
+ const W = 86;
299
+ const topTab = `╴ «${scn.tonefill}» ${scn.label.toUpperCase()} «/» «faint»turn 7«/» ╴`;
300
+ const topPad = W - tuiLen(topTab);
301
+ const file = trunc(scn.file, W - 14);
302
+ const botTab = `«faint»╴«/» «faint»file«/» «${scn.tone}»${file}«/» «faint»╴«/»`;
303
+ const botPad = W - tuiLen(botTab);
304
+ return [
305
+ topTab + '«faint»' + '─'.repeat(Math.max(2, topPad)) + '«/»',
306
+ '',
307
+ ' «spin» «dim»' + scn.thought + '«/»«dots»',
308
+ '',
309
+ botTab + '«faint»' + '─'.repeat(Math.max(2, botPad)) + '«/»',
310
+ ];
311
+ }
312
+ function v8_micro(scn) {
313
+ const W = 64;
314
+ return [
315
+ '«' + scn.tone + '»●«/» «b»' + scn.label + '«/»' + '«dots»',
316
+ '«faint»│«/» «dim»' + scn.thought + '«/»',
317
+ '«faint»╰─«/»«faint»file «/»«' + scn.tone + '»' + trunc(scn.file, W - 12) + '«/»',
318
+ ];
319
+ }
320
+ function v9_stamp(scn) {
321
+ const W = 86;
322
+ return [
323
+ '«' + scn.tonefill + '» ' + scn.label.toUpperCase().padEnd(9, ' ') + '«/» «spin» «dim»' + scn.thought + '«/»«dots»',
324
+ ' '.repeat(11) + '«faint»' + '─'.repeat(W - 12) + '«/»',
325
+ ' '.repeat(11) + '«faint»↳ file«/» «' + scn.tone + '»' + trunc(scn.file, W - 22) + '«/»',
326
+ ];
327
+ }
328
+
329
+ const VARIANTS = [
330
+ { n: 'V1', id: 'v1', name: 'hairline readout', note: 'three indented lines, ochre badge, dim thought', state: 'thinking', build: v1_hairline },
331
+ { n: 'V2', id: 'v2', name: 'phase tag', note: '[agent] header · quoted thought · file label', state: 'executing', build: v2_step },
332
+ { n: 'V3', id: 'v3', name: 'left-rail accent', note: 'colored bar runs vertically through all three rows', state: 'reading', build: v3_rail },
333
+ { n: 'V4', id: 'v4', name: 'soft card', note: 'rounded box · status row, divider, thought + file', state: 'writing', build: v4_card },
334
+ { n: 'V5', id: 'v5', name: 'margin labels', note: 'editorial state / thought / file gutter', state: 'thinking', build: v5_margin },
335
+ { n: 'V6', id: 'v6', name: 'sigil prefix', note: 'each line begins with a different terminal sigil', state: 'executing', build: v6_sigil },
336
+ { n: 'V7', id: 'v7', name: 'split-bar', note: 'status tabs into top rule, file into bottom', state: 'reading', build: v7_split },
337
+ { n: 'V8', id: 'v8', name: 'micro-log', note: 'compact 3-row entry for narrow rails', state: 'thinking', build: v8_micro },
338
+ { n: 'V9', id: 'v9', name: 'stamp head', note: 'tinted stamp block at the head of the row', state: 'executing', build: v9_stamp },
339
+ ];
340
+
341
+ const grid = document.getElementById('grid');
342
+ for (const v of VARIANTS) {
343
+ const scn = S[v.state];
344
+ const card = document.createElement('section');
345
+ card.id = v.id;
346
+ card.className = 'card';
347
+ card.innerHTML = `
348
+ <div class="label">
349
+ <span class="n">${v.n}</span>
350
+ <span class="name">${v.name}</span>
351
+ <span>${v.note}</span>
352
+ <a href="#${v.id}">/switch-agentStatus ${v.n.slice(1)}</a>
353
+ </div>
354
+ <div class="tui-screen">${v.build(scn).map(l => `<span class="tui-line">${tuiRender(l) || '&nbsp;'}</span>`).join('')}</div>
355
+ `;
356
+ grid.appendChild(card);
357
+ }
358
+
359
+ const FRAMES = ['⠋','⠙','⠹','⠸','⠼','⠴','⠦','⠧','⠇','⠏'];
360
+ const DOTS = [' ', '. ', '.. ', '...'];
361
+ let f = 0;
362
+ setInterval(() => {
363
+ f = (f + 1) % FRAMES.length;
364
+ document.querySelectorAll('.tui-spin').forEach(el => el.setAttribute('data-frame', FRAMES[f]));
365
+ document.querySelectorAll('.tui-dots').forEach(el => el.style.setProperty('--dots', JSON.stringify(DOTS[Math.floor(f / 2) % DOTS.length])));
366
+ document.querySelectorAll('.tui-dots').forEach(el => el.textContent = DOTS[Math.floor(f / 2) % DOTS.length]);
367
+ }, 90);
368
+
369
+ document.querySelectorAll('[data-theme-btn]').forEach(btn => {
370
+ btn.addEventListener('click', () => {
371
+ const theme = btn.dataset.themeBtn;
372
+ document.body.dataset.theme = theme;
373
+ document.querySelectorAll('[data-theme-btn]').forEach(b => {
374
+ b.setAttribute('aria-pressed', b === btn ? 'true' : 'false');
375
+ });
376
+ });
377
+ });
378
+ </script>
379
+ </body></html>