chore(tui): strip showroom chrome and beef up slash demo
- drop title bar, "real ink" tag, meta line — terminal box is the surface - single bottom controls row: ↻ · scale · speed · picker · progress - slash workflow now types each command, echoes a slash msg, then renders the panel - adds a /help scene (real Panel grouped by category) - README minus the "real ink" marketing
This commit is contained in:
+27
-44
@@ -1,59 +1,43 @@
|
||||
# TUI Showroom
|
||||
|
||||
Cinematic demos of the real `ui-tui`. Workflows are built from actual Ink-rendered ANSI captured from `MessageLine`, `Panel`, and friends — replayed in xterm.js with timeline overlays (captions, spotlights, fades, highlights).
|
||||
Scripted demos of `ui-tui`. Workflows snapshot real ui-tui components (`MessageLine`, `Panel`, `Box`, `Text`) into ANSI and replay them through xterm.js with cinematic overlays. Recorded once, played any number of times — built for screen capture.
|
||||
|
||||
```bash
|
||||
npm run showroom # dev server at http://127.0.0.1:4317
|
||||
npm run showroom:record # re-record all workflows (regenerates JSON)
|
||||
npm run showroom:build # builds dist/<name>.html for every workflow
|
||||
npm run showroom:record # regenerate every workflow JSON
|
||||
npm run showroom:build # dist/<name>.html for every workflow
|
||||
npm run showroom:type-check
|
||||
```
|
||||
|
||||
## Bundled workflows
|
||||
|
||||
| File | Demonstrates |
|
||||
| -------------------------------------- | ----------------------------------------------------- |
|
||||
| `workflows/feature-tour.json` | Plan → tool trail → result highlight |
|
||||
| `workflows/subagent-trail.json` | Parallel subagents, hot lanes, summary |
|
||||
| `workflows/slash-commands.json` | `/skills`, `/model`, `/agents` panels |
|
||||
| `workflows/voice-mode.json` | VAD capture, transcript, TTS ducking |
|
||||
| File | Shows |
|
||||
| -------------------------------------- | -------------------------------------------------------------- |
|
||||
| `workflows/feature-tour.json` | Plan → tool trail → result highlight |
|
||||
| `workflows/subagent-trail.json` | Parallel subagents, hot lanes, summary |
|
||||
| `workflows/slash-commands.json` | `/skills`, `/model`, `/agents`, `/help` typed → echoed → panel |
|
||||
| `workflows/voice-mode.json` | VAD capture, transcript, TTS ducking |
|
||||
|
||||
Use the dropdown in the top-right or pass `?w=<name>` to deep-link a workflow.
|
||||
Pick a workflow from the dropdown or deep-link with `?w=<name>`.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
record.tsx ─┐
|
||||
↳ MessageLine, │ Ink renders → custom Writable → ANSI string
|
||||
Panel, Box, Text │
|
||||
▼
|
||||
↳ MessageLine, │ Ink renders → Writable → ANSI string
|
||||
Panel, Box, Text │
|
||||
▼
|
||||
workflows/<name>.json
|
||||
│ served at /api/workflow/<name>
|
||||
▼
|
||||
showroom.js │ xterm.js writes ANSI; DOM overlays target frame ids
|
||||
▼
|
||||
│ served at /api/workflow/<name>
|
||||
▼
|
||||
showroom.js │ xterm.js writes ANSI; DOM overlays target frame ids
|
||||
▼
|
||||
browser
|
||||
```
|
||||
|
||||
Every `frame` action embeds the ANSI bytes from a real Ink render; the browser replays them via `@xterm/xterm` (loaded from jsDelivr) so the surface is the actual TUI, not a CSS approximation. Cinematic overlays (captions, spotlights, highlights, fades) are positioned by frame `id` and rendered via DOM.
|
||||
`frame` actions embed ANSI from an Ink render; the browser feeds them into `@xterm/xterm` (jsDelivr CDN) so the surface is the actual TUI. Captions, spotlights, highlights, and fades are DOM overlays anchored to frame `id`s.
|
||||
|
||||
## Workflow Shape
|
||||
|
||||
```json
|
||||
{
|
||||
"title": "Hermes TUI · Feature Tour",
|
||||
"viewport": { "cols": 80, "rows": 16 },
|
||||
"composer": "ask hermes anything",
|
||||
"timeline": [
|
||||
{ "at": 200, "type": "frame", "id": "user-row", "ansi": "..." },
|
||||
{ "at": 1500, "type": "frame", "id": "assistant", "ansi": "..." },
|
||||
{ "at": 1700, "type": "spotlight", "target": "assistant" },
|
||||
{ "at": 1900, "type": "caption", "target": "assistant", "text": "..." }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Timeline Actions
|
||||
## Timeline actions
|
||||
|
||||
| Action | Required | Optional |
|
||||
| ----------- | -------------------- | ----------------------------------------------------- |
|
||||
@@ -66,19 +50,18 @@ Every `frame` action embeds the ANSI bytes from a real Ink render; the browser r
|
||||
| `fade` | `target` | `to` (default `0`), `duration` |
|
||||
| `clear` | — | — |
|
||||
|
||||
`target` references the `id` of an earlier `frame`. `viewport.scale` (default = best-fit integer) controls the upscale factor; manual buttons offer 1x–4x for capture-ready output.
|
||||
`target` references the `id` of an earlier `frame`. `viewport.scale` (or the 1x–4x picker) controls the upscale factor for capture.
|
||||
|
||||
## Player
|
||||
|
||||
- Restart, Clear, 1x–4x scale, 0.5x/1x/2x speed.
|
||||
- Keyboard: `R` restart, `C` clear, `1`/`2`/`3` speed.
|
||||
- Progress bar tracks elapsed/total based on the slowest action's `at + duration`.
|
||||
- Restart (`R`), 1x–4x scale, 0.5x/1x/2x speed (`1`/`2`/`3`).
|
||||
- Progress bar reads `at + duration` from the slowest action.
|
||||
|
||||
## Adding a workflow
|
||||
|
||||
1. Add a scene fn to `record.tsx` that returns a `{ title, viewport, composer, timeline }` shape.
|
||||
2. Compose Ink primitives (`Box`, `Text`) or import real ui-tui components (`MessageLine`, `Panel`).
|
||||
3. Snap each scene with `await snap(<Component />)` to capture ANSI.
|
||||
4. Run `npm run showroom:record`.
|
||||
1. Add a scene fn to `record.tsx` returning `{ title, viewport, composer, timeline }`.
|
||||
2. Compose Ink primitives or pull `MessageLine` / `Panel` from `../src`.
|
||||
3. `await snap(<Component />)` for each frame.
|
||||
4. `npm run showroom:record`.
|
||||
|
||||
Components rendered to ANSI must be **state-free** at first paint — `useEffect` hooks usually haven't fired by the time the recorder unmounts. For accordions like the live `ToolTrail`, render an inline scene with `Box` + `Text` instead.
|
||||
Components must be state-free at first paint — `useEffect` hooks won't fire by the time the recorder unmounts. For accordions like the live `ToolTrail`, render a flat `Box` + `Text` scene instead.
|
||||
|
||||
+47
-21
@@ -245,6 +245,9 @@ const subagentTrail = async () => {
|
||||
}
|
||||
|
||||
const slashCommands = async () => {
|
||||
const slashEcho = (text: string) => snap(<Msg kind="slash" role="user" text={text} />)
|
||||
|
||||
const skillsEcho = await slashEcho('/skills search vibe')
|
||||
const skillsResults = await snap(
|
||||
<Panel
|
||||
sections={[
|
||||
@@ -257,11 +260,12 @@ const slashCommands = async () => {
|
||||
}
|
||||
]}
|
||||
t={t}
|
||||
title="/skills search vibe"
|
||||
title="skills · search vibe"
|
||||
/>,
|
||||
180
|
||||
)
|
||||
|
||||
const modelEcho = await slashEcho('/model claude-4.6-sonnet')
|
||||
const modelSwitch = await snap(
|
||||
<Panel
|
||||
sections={[
|
||||
@@ -279,6 +283,7 @@ const slashCommands = async () => {
|
||||
180
|
||||
)
|
||||
|
||||
const agentsEcho = await slashEcho('/agents pause')
|
||||
const agentsStatus = await snap(
|
||||
<Panel
|
||||
sections={[
|
||||
@@ -291,47 +296,68 @@ const slashCommands = async () => {
|
||||
}
|
||||
]}
|
||||
t={t}
|
||||
title="/agents · paused"
|
||||
title="agents · paused"
|
||||
/>,
|
||||
180
|
||||
)
|
||||
|
||||
const helpEcho = await slashEcho('/help')
|
||||
const helpPanel = await snap(
|
||||
<Panel
|
||||
sections={[
|
||||
{ items: ['/skills search · install · inspect', '/model switch model · pop picker'], title: 'Tools & Skills' },
|
||||
{ items: ['/agents spawn-tree dashboard', '/queue queue prompt for next turn', '/steer inject after next tool call'], title: 'Session' },
|
||||
{ items: ['/voice toggle voice mode', '/details thinking · tools · subagents · activity'], title: 'Configuration' }
|
||||
]}
|
||||
t={t}
|
||||
title="(^_^)? Commands"
|
||||
/>,
|
||||
220
|
||||
)
|
||||
|
||||
return {
|
||||
composer: 'press / to open the palette',
|
||||
composer: '',
|
||||
timeline: [
|
||||
{ at: 200, duration: 500, text: '/skills search vibe', type: 'compose' },
|
||||
{ ansi: skillsResults, at: 800, id: 'skills', type: 'frame' },
|
||||
{ at: 1100, duration: 1500, target: 'skills', type: 'spotlight' },
|
||||
{ at: 200, duration: 700, text: '/skills search vibe', type: 'compose' },
|
||||
{ ansi: skillsEcho, at: 1100, type: 'frame' },
|
||||
{ at: 1100, duration: 200, text: '', type: 'compose' },
|
||||
{ ansi: skillsResults, at: 1400, id: 'skills', type: 'frame' },
|
||||
{
|
||||
at: 1300,
|
||||
duration: 1700,
|
||||
at: 1700,
|
||||
duration: 2000,
|
||||
position: 'right',
|
||||
target: 'skills',
|
||||
text: 'Slash commands stream live results without blocking the composer.',
|
||||
text: 'Typed /skills, hit return — same Panel the live TUI renders.',
|
||||
type: 'caption'
|
||||
},
|
||||
{ at: 3300, duration: 600, text: '/model claude-4.6-sonnet', type: 'compose' },
|
||||
{ ansi: modelSwitch, at: 4100, id: 'model', type: 'frame' },
|
||||
{ at: 4000, duration: 700, text: '/model claude-4.6-sonnet', type: 'compose' },
|
||||
{ ansi: modelEcho, at: 4900, type: 'frame' },
|
||||
{ at: 4900, duration: 200, text: '', type: 'compose' },
|
||||
{ ansi: modelSwitch, at: 5200, id: 'model', type: 'frame' },
|
||||
{
|
||||
at: 4400,
|
||||
duration: 1700,
|
||||
at: 5500,
|
||||
duration: 1900,
|
||||
position: 'right',
|
||||
target: 'model',
|
||||
text: '/model also pops the inline picker when no arg is given.',
|
||||
text: '/model swaps mid-session; transcript and cache stay intact.',
|
||||
type: 'caption'
|
||||
},
|
||||
{ at: 6300, duration: 600, text: '/agents pause', type: 'compose' },
|
||||
{ ansi: agentsStatus, at: 7000, id: 'agents', type: 'frame' },
|
||||
{ at: 7300, duration: 1300, target: 'agents', type: 'highlight' },
|
||||
{ at: 7600, duration: 600, text: '/agents pause', type: 'compose' },
|
||||
{ ansi: agentsEcho, at: 8400, type: 'frame' },
|
||||
{ at: 8400, duration: 200, text: '', type: 'compose' },
|
||||
{ ansi: agentsStatus, at: 8700, id: 'agents', type: 'frame' },
|
||||
{
|
||||
at: 7500,
|
||||
duration: 1700,
|
||||
at: 9000,
|
||||
duration: 1800,
|
||||
position: 'right',
|
||||
target: 'agents',
|
||||
text: 'Same registry powers TUI, gateway, Telegram, Discord — one source of truth.',
|
||||
text: 'Same registry powers TUI, gateway, Telegram, Discord — one truth.',
|
||||
type: 'caption'
|
||||
},
|
||||
{ at: 9300, duration: 600, text: '/resume', type: 'compose' }
|
||||
{ at: 11000, duration: 400, text: '/help', type: 'compose' },
|
||||
{ ansi: helpEcho, at: 11500, type: 'frame' },
|
||||
{ at: 11500, duration: 200, text: '', type: 'compose' },
|
||||
{ ansi: helpPanel, at: 11800, id: 'help', type: 'frame' }
|
||||
],
|
||||
title: 'Hermes TUI · Slash Commands',
|
||||
viewport: { cols: COLS, rows: ROWS }
|
||||
|
||||
@@ -40,7 +40,7 @@ body {
|
||||
|
||||
.showroom-shell {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
gap: 14px;
|
||||
width: max-content;
|
||||
max-width: 100%;
|
||||
opacity: 0;
|
||||
@@ -55,39 +55,6 @@ body {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.showroom-title {
|
||||
display: flex;
|
||||
align-items: end;
|
||||
justify-content: space-between;
|
||||
gap: 24px;
|
||||
color: var(--cornsilk);
|
||||
font-size: 18px;
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
|
||||
.showroom-title-name {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.showroom-title-tag {
|
||||
color: var(--dim);
|
||||
font-family: 'JetBrains Mono', 'SFMono-Regular', Consolas, monospace;
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.16em;
|
||||
}
|
||||
|
||||
.showroom-meta {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
color: var(--dim);
|
||||
font-family: 'JetBrains Mono', 'SFMono-Regular', Consolas, monospace;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.showroom-picker {
|
||||
appearance: none;
|
||||
border: 1px solid rgba(205, 127, 50, 0.4);
|
||||
@@ -354,28 +321,38 @@ body {
|
||||
.showroom-controls {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
font-family: 'JetBrains Mono', 'SFMono-Regular', Consolas, monospace;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.showroom-controls button {
|
||||
border: 1px solid rgba(205, 127, 50, 0.35);
|
||||
border: 1px solid rgba(205, 127, 50, 0.25);
|
||||
border-radius: 999px;
|
||||
padding: 6px 14px;
|
||||
background: rgba(205, 127, 50, 0.06);
|
||||
color: var(--cornsilk);
|
||||
padding: 4px 10px;
|
||||
background: rgba(205, 127, 50, 0.04);
|
||||
color: var(--dim);
|
||||
cursor: pointer;
|
||||
font: inherit;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.showroom-controls button:hover {
|
||||
background: rgba(205, 127, 50, 0.14);
|
||||
background: rgba(205, 127, 50, 0.12);
|
||||
color: var(--cornsilk);
|
||||
}
|
||||
|
||||
.showroom-controls button[data-action="restart"] {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
padding: 0;
|
||||
font-size: 14px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.showroom-segmented {
|
||||
display: inline-flex;
|
||||
border: 1px solid rgba(205, 127, 50, 0.35);
|
||||
border: 1px solid rgba(205, 127, 50, 0.25);
|
||||
border-radius: 999px;
|
||||
padding: 2px;
|
||||
background: rgba(205, 127, 50, 0.04);
|
||||
@@ -384,12 +361,11 @@ body {
|
||||
.showroom-segmented button {
|
||||
border: 0;
|
||||
border-radius: 999px;
|
||||
padding: 4px 12px;
|
||||
padding: 3px 10px;
|
||||
background: transparent;
|
||||
color: var(--dim);
|
||||
cursor: pointer;
|
||||
font: inherit;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.showroom-segmented button.is-active {
|
||||
@@ -397,31 +373,19 @@ body {
|
||||
color: var(--cornsilk);
|
||||
}
|
||||
|
||||
.showroom-segmented-label {
|
||||
align-self: center;
|
||||
margin-right: 4px;
|
||||
color: var(--dim);
|
||||
font-family: 'JetBrains Mono', 'SFMono-Regular', Consolas, monospace;
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.12em;
|
||||
}
|
||||
|
||||
.showroom-progress {
|
||||
display: flex;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
width: 100%;
|
||||
margin-top: 4px;
|
||||
gap: 10px;
|
||||
flex: 1;
|
||||
min-width: 140px;
|
||||
color: var(--dim);
|
||||
font-family: 'JetBrains Mono', 'SFMono-Regular', Consolas, monospace;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.showroom-progress-track {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
height: 3px;
|
||||
height: 2px;
|
||||
border-radius: 999px;
|
||||
background: rgba(205, 127, 50, 0.1);
|
||||
overflow: hidden;
|
||||
|
||||
@@ -395,9 +395,6 @@ const setScale = next => {
|
||||
for (const button of state.shell.querySelectorAll('[data-segment="scale"] button')) {
|
||||
button.classList.toggle('is-active', Number(button.dataset.value) === next)
|
||||
}
|
||||
|
||||
state.shell.querySelector('[data-role="meta"]').textContent =
|
||||
`${state.viewport.cols}x${state.viewport.rows} · ${next}x`
|
||||
}
|
||||
|
||||
const fitScale = () => {
|
||||
@@ -475,16 +472,6 @@ const renderShell = () => {
|
||||
state.shell.style.setProperty('--term-h', `${state.viewport.rows * state.viewport.lineHeight}px`)
|
||||
|
||||
state.shell.innerHTML = `
|
||||
<header class="showroom-title">
|
||||
<span class="showroom-title-name">
|
||||
<span data-role="title">${escapeHtml(state.workflow.title ?? 'Hermes TUI Showroom')}</span>
|
||||
<span class="showroom-title-tag">${state.frameMode ? 'real ink' : 'showroom'}</span>
|
||||
</span>
|
||||
<span class="showroom-meta">
|
||||
<span data-role="meta">${state.viewport.cols}x${state.viewport.rows} · ${state.scale}x</span>
|
||||
${catalog.length > 1 ? `<select class="showroom-picker" data-action="picker">${buildOptions()}</select>` : ''}
|
||||
</span>
|
||||
</header>
|
||||
<div class="showroom-stage">
|
||||
<div class="showroom-terminal">
|
||||
<div class="showroom-status" data-target="status">
|
||||
@@ -496,17 +483,15 @@ const renderShell = () => {
|
||||
</div>
|
||||
<div class="showroom-overlays"></div>
|
||||
</div>
|
||||
<div class="showroom-progress">
|
||||
<span data-role="time">0.0s / 0.0s</span>
|
||||
<div class="showroom-progress-track"><div class="showroom-progress-fill"></div></div>
|
||||
</div>
|
||||
<footer class="showroom-controls">
|
||||
<button type="button" data-action="restart">Restart</button>
|
||||
<button type="button" data-action="clear">Clear</button>
|
||||
<span class="showroom-segmented-label">scale</span>
|
||||
<button type="button" data-action="restart" title="restart (R)">↻</button>
|
||||
<span class="showroom-segmented" data-segment="scale">${buildSegmented(SCALES, state.scale)}</span>
|
||||
<span class="showroom-segmented-label">speed</span>
|
||||
<span class="showroom-segmented" data-segment="speed">${buildSegmented(SPEEDS, state.speed)}</span>
|
||||
${catalog.length > 1 ? `<select class="showroom-picker" data-action="picker">${buildOptions()}</select>` : ''}
|
||||
<span class="showroom-progress">
|
||||
<span data-role="time">0.0s / 0.0s</span>
|
||||
<div class="showroom-progress-track"><div class="showroom-progress-fill"></div></div>
|
||||
</span>
|
||||
</footer>
|
||||
`
|
||||
|
||||
@@ -519,10 +504,6 @@ const renderShell = () => {
|
||||
state.progressLabel = state.shell.querySelector('[data-role="time"]')
|
||||
|
||||
state.shell.querySelector('[data-action="restart"]').addEventListener('click', play)
|
||||
state.shell.querySelector('[data-action="clear"]').addEventListener('click', () => {
|
||||
clearTimers()
|
||||
clearTranscript()
|
||||
})
|
||||
|
||||
for (const button of state.shell.querySelectorAll('[data-segment="speed"] button')) {
|
||||
button.addEventListener('click', () => setSpeed(Number(button.dataset.value)))
|
||||
@@ -574,9 +555,6 @@ const mount = () => {
|
||||
|
||||
if (key === 'r') {
|
||||
play()
|
||||
} else if (key === 'c') {
|
||||
clearTimers()
|
||||
clearTranscript()
|
||||
} else if (key === '1' || key === '2' || key === '3') {
|
||||
setSpeed(SPEEDS[Number(key) - 1])
|
||||
}
|
||||
|
||||
@@ -1,83 +1,121 @@
|
||||
{
|
||||
"composer": "press / to open the palette",
|
||||
"composer": "",
|
||||
"timeline": [
|
||||
{
|
||||
"at": 200,
|
||||
"duration": 500,
|
||||
"duration": 700,
|
||||
"text": "/skills search vibe",
|
||||
"type": "compose"
|
||||
},
|
||||
{
|
||||
"ansi": "\u001b[?25l\u001b[?2026h╭──────────────────────────────────────────────────────────────────────────────╮\r\n│\u001b[78C│\r\n│\u001b[29C/skills\u001b[1Csearch\u001b[1Cvibe\u001b[30C│\r\n│\u001b[78C│\r\n│\u001b[2Canthropics/skills/frontend-design★\u001b[1Ctrusted\u001b[34C│\r\n│\u001b[2Copenai/skills/skill-creator·\u001b[1Cofficial\u001b[39C│\r\n│\u001b[2Cskills.sh/community/vibe-coding⚙\u001b[1Ccommunity\u001b[33C│\r\n│\u001b[78C│\r\n╰──────────────────────────────────────────────────────────────────────────────╯\r\n\u001b[?2026l\u001b[?2026h\u001b[?25h\u001b[?2026l\u001b[?25h",
|
||||
"at": 800,
|
||||
"id": "skills",
|
||||
"ansi": "\u001b[?25l\u001b[?2026h\r\n❯\u001b[2C/skills\u001b[1Csearch\u001b[1Cvibe\r\n\r\n\u001b[?2026l\u001b[?2026h\u001b[?25h\u001b[?2026l\u001b[?25h",
|
||||
"at": 1100,
|
||||
"type": "frame"
|
||||
},
|
||||
{
|
||||
"at": 1100,
|
||||
"duration": 1500,
|
||||
"target": "skills",
|
||||
"type": "spotlight"
|
||||
"duration": 200,
|
||||
"text": "",
|
||||
"type": "compose"
|
||||
},
|
||||
{
|
||||
"at": 1300,
|
||||
"duration": 1700,
|
||||
"ansi": "\u001b[?25l\u001b[?2026h╭──────────────────────────────────────────────────────────────────────────────╮\r\n│\u001b[78C│\r\n│\u001b[29Cskills\u001b[1C·\u001b[1Csearch\u001b[1Cvibe\u001b[29C│\r\n│\u001b[78C│\r\n│\u001b[2Canthropics/skills/frontend-design★\u001b[1Ctrusted\u001b[34C│\r\n│\u001b[2Copenai/skills/skill-creator·\u001b[1Cofficial\u001b[39C│\r\n│\u001b[2Cskills.sh/community/vibe-coding⚙\u001b[1Ccommunity\u001b[33C│\r\n│\u001b[78C│\r\n╰──────────────────────────────────────────────────────────────────────────────╯\r\n\u001b[?2026l\u001b[?2026h\u001b[?25h\u001b[?2026l\u001b[?25h",
|
||||
"at": 1400,
|
||||
"id": "skills",
|
||||
"type": "frame"
|
||||
},
|
||||
{
|
||||
"at": 1700,
|
||||
"duration": 2000,
|
||||
"position": "right",
|
||||
"target": "skills",
|
||||
"text": "Slash commands stream live results without blocking the composer.",
|
||||
"text": "Typed /skills, hit return — same Panel the live TUI renders.",
|
||||
"type": "caption"
|
||||
},
|
||||
{
|
||||
"at": 3300,
|
||||
"duration": 600,
|
||||
"at": 4000,
|
||||
"duration": 700,
|
||||
"text": "/model claude-4.6-sonnet",
|
||||
"type": "compose"
|
||||
},
|
||||
{
|
||||
"ansi": "\u001b[?25l\u001b[?2026h\r\n❯\u001b[2C/model\u001b[1Cclaude-4.6-sonnet\r\n\r\n\u001b[?2026l\u001b[?2026h\u001b[?25h\u001b[?2026l\u001b[?25h",
|
||||
"at": 4900,
|
||||
"type": "frame"
|
||||
},
|
||||
{
|
||||
"at": 4900,
|
||||
"duration": 200,
|
||||
"text": "",
|
||||
"type": "compose"
|
||||
},
|
||||
{
|
||||
"ansi": "\u001b[?25l\u001b[?2026h╭──────────────────────────────────────────────────────────────────────────────╮\r\n│\u001b[78C│\r\n│\u001b[32Cmodel\u001b[1Cswitched\u001b[32C│\r\n│\u001b[78C│\r\n│\u001b[2Cfrom\u001b[16Cgpt-5-codex\u001b[45C│\r\n│\u001b[2Cto\u001b[18Cclaude-4.6-sonnet\u001b[39C│\r\n│\u001b[2Cscope\u001b[15Cthis\u001b[1Csession\u001b[44C│\r\n│\u001b[78C│\r\n╰──────────────────────────────────────────────────────────────────────────────╯\r\n\u001b[?2026l\u001b[?2026h\u001b[?25h\u001b[?2026l\u001b[?25h",
|
||||
"at": 4100,
|
||||
"at": 5200,
|
||||
"id": "model",
|
||||
"type": "frame"
|
||||
},
|
||||
{
|
||||
"at": 4400,
|
||||
"duration": 1700,
|
||||
"at": 5500,
|
||||
"duration": 1900,
|
||||
"position": "right",
|
||||
"target": "model",
|
||||
"text": "/model also pops the inline picker when no arg is given.",
|
||||
"text": "/model swaps mid-session; transcript and cache stay intact.",
|
||||
"type": "caption"
|
||||
},
|
||||
{
|
||||
"at": 6300,
|
||||
"at": 7600,
|
||||
"duration": 600,
|
||||
"text": "/agents pause",
|
||||
"type": "compose"
|
||||
},
|
||||
{
|
||||
"ansi": "\u001b[?25l\u001b[?2026h╭──────────────────────────────────────────────────────────────────────────────╮\r\n│\u001b[78C│\r\n│\u001b[31C/agents\u001b[1C·\u001b[1Cpaused\u001b[31C│\r\n│\u001b[78C│\r\n│\u001b[2Cdelegation\u001b[10Cpaused\u001b[50C│\r\n│\u001b[2Cmax\u001b[1Cchildren\u001b[8C4\u001b[55C│\r\n│\u001b[2Crunning\u001b[1Ctasks\u001b[7Cqueued\u001b[1Cfor\u001b[1Cresume\u001b[39C│\r\n│\u001b[78C│\r\n╰──────────────────────────────────────────────────────────────────────────────╯\r\n\u001b[?2026l\u001b[?2026h\u001b[?25h\u001b[?2026l\u001b[?25h",
|
||||
"at": 7000,
|
||||
"ansi": "\u001b[?25l\u001b[?2026h\r\n❯\u001b[2C/agents\u001b[1Cpause\r\n\r\n\u001b[?2026l\u001b[?2026h\u001b[?25h\u001b[?2026l\u001b[?25h",
|
||||
"at": 8400,
|
||||
"type": "frame"
|
||||
},
|
||||
{
|
||||
"at": 8400,
|
||||
"duration": 200,
|
||||
"text": "",
|
||||
"type": "compose"
|
||||
},
|
||||
{
|
||||
"ansi": "\u001b[?25l\u001b[?2026h╭──────────────────────────────────────────────────────────────────────────────╮\r\n│\u001b[78C│\r\n│\u001b[31Cagents\u001b[1C·\u001b[1Cpaused\u001b[32C│\r\n│\u001b[78C│\r\n│\u001b[2Cdelegation\u001b[10Cpaused\u001b[50C│\r\n│\u001b[2Cmax\u001b[1Cchildren\u001b[8C4\u001b[55C│\r\n│\u001b[2Crunning\u001b[1Ctasks\u001b[7Cqueued\u001b[1Cfor\u001b[1Cresume\u001b[39C│\r\n│\u001b[78C│\r\n╰──────────────────────────────────────────────────────────────────────────────╯\r\n\u001b[?2026l\u001b[?2026h\u001b[?25h\u001b[?2026l\u001b[?25h",
|
||||
"at": 8700,
|
||||
"id": "agents",
|
||||
"type": "frame"
|
||||
},
|
||||
{
|
||||
"at": 7300,
|
||||
"duration": 1300,
|
||||
"target": "agents",
|
||||
"type": "highlight"
|
||||
},
|
||||
{
|
||||
"at": 7500,
|
||||
"duration": 1700,
|
||||
"at": 9000,
|
||||
"duration": 1800,
|
||||
"position": "right",
|
||||
"target": "agents",
|
||||
"text": "Same registry powers TUI, gateway, Telegram, Discord — one source of truth.",
|
||||
"text": "Same registry powers TUI, gateway, Telegram, Discord — one truth.",
|
||||
"type": "caption"
|
||||
},
|
||||
{
|
||||
"at": 9300,
|
||||
"duration": 600,
|
||||
"text": "/resume",
|
||||
"at": 11000,
|
||||
"duration": 400,
|
||||
"text": "/help",
|
||||
"type": "compose"
|
||||
},
|
||||
{
|
||||
"ansi": "\u001b[?25l\u001b[?2026h\r\n❯\u001b[2C/help\r\n\r\n\u001b[?2026l\u001b[?2026h\u001b[?25h\u001b[?2026l\u001b[?25h",
|
||||
"at": 11500,
|
||||
"type": "frame"
|
||||
},
|
||||
{
|
||||
"at": 11500,
|
||||
"duration": 200,
|
||||
"text": "",
|
||||
"type": "compose"
|
||||
},
|
||||
{
|
||||
"ansi": "\u001b[?25l\u001b[?2026h╭──────────────────────────────────────────────────────────────────────────────╮\r\n│\u001b[78C│\r\n│\u001b[31C(^_^)?\u001b[1CCommands\u001b[32C│\r\n│\u001b[78C│\r\n│\u001b[2CTools\u001b[1C&\u001b[1CSkills\u001b[62C│\r\n│\u001b[2C/skills\u001b[4Csearch\u001b[1C·\u001b[1Cinstall\u001b[1C·\u001b[1Cinspect\u001b[39C│\r\n│\u001b[2C/model\u001b[5Cswitch\u001b[1Cmodel\u001b[1C·\u001b[1Cpop\u001b[1Cpicker\u001b[40C│\r\n│\u001b[78C│\r\n│\u001b[2CSession\u001b[69C│\r\n│\u001b[2C/agents\u001b[4Cspawn-tree\u001b[1Cdashboard\u001b[45C│\r\n│\u001b[2C/queue\u001b[5Cqueue\u001b[1Cprompt\u001b[1Cfor\u001b[1Cnext\u001b[1Cturn\u001b[39C│\r\n│\u001b[2C/steer\u001b[5Cinject\u001b[1Cafter\u001b[1Cnext\u001b[1Ctool\u001b[1Ccall\u001b[38C│\r\n│\u001b[78C│\r\n│\u001b[2CConfiguration\u001b[63C│\r\n│\u001b[2C/voice\u001b[5Ctoggle\u001b[1Cvoice\u001b[1Cmode\u001b[48C│\r\n│\u001b[2C/details\u001b[3Cthinking\u001b[1C·\u001b[1Ctools\u001b[1C·\u001b[1Csubagents\u001b[1C·\u001b[1Cactivity\u001b[26C│\r\n│\u001b[78C│\r\n╰──────────────────────────────────────────────────────────────────────────────╯\r\n\u001b[?2026l\u001b[?2026h\u001b[?25h\u001b[?2026l\u001b[?25h",
|
||||
"at": 11800,
|
||||
"id": "help",
|
||||
"type": "frame"
|
||||
}
|
||||
],
|
||||
"title": "Hermes TUI · Slash Commands",
|
||||
|
||||
Reference in New Issue
Block a user