com um clique
tui-widgets
Author live widget apps for the Hermes TUI dock.
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Menu
Author live widget apps for the Hermes TUI dock.
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Baseado na classificação ocupacional SOC
Author a Hermes color theme that skins every surface.
Build auditable Excel workbooks headless with openpyxl — blue/black/green cell conventions, formulas over hardcodes, named ranges, balance checks, sensitivity tables. Use for financial models, audit outputs, reconciliations.
Create, read, edit Word .docx documents and templates.
Edit PDF text/typos/titles via nano-pdf CLI (NL prompts).
Extract text from PDFs/scans (pymupdf, marker-pdf).
Create, merge, split, fill, and secure PDF files.
| name | tui-widgets |
| description | Author live widget apps for the Hermes TUI dock. |
| version | 1.0.0 |
| author | Hermes Agent |
| license | MIT |
| metadata | {"hermes":{"tags":["tui","widgets","sdk","ui"],"category":"productivity"}} |
Author widget apps for the Hermes TUI (hermes --tui): glanceable ambient
panels docked above the status bar, or modal overlays that own the keyboard.
Widgets are plain ESM files the TUI loads at startup — no build step, no
repo changes. This skill does not cover desktop-app or web-dashboard
widgets.
hermes --tui). Widgets do not render in the
classic CLI or messaging platforms.write_file to create ~/.hermes/tui-widgets/<name>.mjs (see
templates/clock.mjs for a complete working widget)./widgets-reload forces a rescan./<id>), with
its help in the / completion popover. No other registration exists.register(sdk) with
sdk.openWidget(app, app.init('')) — the widget docks itself the moment
the file loads. Only do this when the user asked for it; note it re-docks
on every /widgets-reload.A widget file default-exports register(sdk):
export default function register(sdk) {
const { Box, Text, defineWidgetApp, h } = sdk
defineWidgetApp({
id: 'clock', // slash command name
help: 'live clock in the dock', // `/` completion metadata
mode: 'ambient', // 'ambient' docks; 'modal' takes input
init: arg => ({ label: arg.trim() || 'UTC' }), // null = print usage
reduce: (state, { ch, key }) => (key.escape || ch === 'q' ? null : state),
render: ({ state, t }) => h(sdk.Dialog, { width: 24 }, h(Text, { color: t.color.label }, state.label))
})
}
sdk contents: defineWidgetApp, openWidget, updateWidget, isCtrl,
React, h (createElement — no JSX in .mjs), components Box, Text,
Dialog, Overlay, WidgetGrid, GridAreas, and loaders Shimmer,
ShimmerRows, useShimmerPhase — use ShimmerRows for loading phases
instead of a bare "loading…" line.
Expand/collapse: sdk.Accordion — the same primitive the session panel's
tool/skill sections use. h(Accordion, { t, title: 'details', count: 3, defaultOpen: false }, body) toggles on CLICK (works in ambient widgets,
which receive no keys); modal apps may pass open + onToggle to drive it
from reducer state instead.
Stable sizing (cards must NEVER resize while ticking):
Dialog an explicit width; charts already return exactly the
width you ask for (short series pad-left while history warms up).String(v).padStart(6) — 51 ms → 112 ms must
not change the line length.Charts (pure string builders — color the result with theme tones):
sdk.sparkline(series, width?) → ▂▃▅▇█▆ one-row trendsdk.sparkRows(series, width, rows) → multi-row column chart (top line
first) — the mission-control panel look; taller cells gain resolutionsdk.gauge(ratio, width) → █████░░░ fill bar for a 0..1 valuesdk.hbars(values, width) → horizontal bar chart, one bar per value,
eighth-block tips, scaled to the maxKeep a rolling series in component state (push per tick, cap ~120 samples)
and render sparkRows for dashboard panels, sparkline for one-liners.
Contract essentials:
mode: 'ambient' — captures no input, the command toggles it; render
returns a CARD (usually Dialog), never Overlay. Placement via zone — every zone RESERVES real space (nothing ever
paints over the transcript):
dock-top (under the top status bar),
dock-bottom (default — above the bottom one).top-left, top-right, bottom-left, bottom-right — corner names
pick the rail side and its top/bottom anchor. Set width on the app
to the card's width (match your Dialog width; default 44) — the rail
reserves exactly that many columns.
Map the user's words to the nearest zone: "top right" → top-right,
"above/next to the status bar" → a dock. Rails suit narrow cards
(~30-46 cols); full-width or short-and-wide content belongs in a dock.mode: 'modal' (default) — owns every keypress; reduce returns next
state, the same reference to swallow a key, or null to close; render
wraps content in Overlay for placement.init, land results with
sdk.updateWidget(app, fn) — it no-ops if the widget was closed, so a
late reply can never resurrect it.React.useState +
React.useEffect (see the template); keep intervals ≥ 250ms.t.color.primary/label/muted/ok/error/…),
never hardcoded hexes — widgets must survive /skin and light/dark.id, mode, and the state shape; keep state serializable.init + updateWidget./<id> to launch (hot-loaded on write); relaunch /<id> to dismiss an
ambient widget./<id> to remount..mjs — everything comes from the sdk
parameter; h(...) builds elements.Esc/q returning null).register() is logged and skipped; check
~/.hermes/logs/tui_gateway_crash.log if a widget never appears.Run /widgets-reload — the transcript line must list the file under
loaded:. Then /<id>: an ambient widget appears docked right, above the
status bar, while the composer keeps accepting input; /<id> again removes
it.