GFM tables (config reference's Field/Type/Default/Notes tables) are
wider than a phone screen and don't wrap, so without their own scroll
container they forced the whole page to scroll horizontally instead.
Wrap rendered tables in an overflow-x-auto div, and let long unbroken
strings in table cells and inline code (env var names, paths) break
instead of forcing extra width.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Two boundaries share the same content: src/app/not-found.tsx catches
genuinely unmatched URLs (rendered bare in the root layout), and
(app)/not-found.tsx catches notFound() calls from within app routes
(already used by scripts/[scriptId] and runs/[runId]) so it renders
nested inside AppLayout, keeping nav and footer visible.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
CardAction's default row-span-2 reserved header column 2 across both
the title row and the metadata dl row below it, shrinking the dl's
available width (cramping the Run ID column) to fit around the
button. Scope the button to just the title row and let the dl span
the full header width on its own row.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Values containing spaces (e.g. an env-passed SCENE="Glitz and glam")
rendered as bare, space-separated words in the run detail view,
indistinguishable from separate argv/env entries. Now anything outside
a safe bareword character set is quoted and escaped for display only -
actual execution is unaffected, since values are always passed as
discrete argv elements/env vars, never through a shell.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The narrower max-w-2xl card left a lot of unused width on scripts with
several variables, forcing a long single-column scroll. Widening the
card and laying out variable inputs in a responsive grid (up to 3
columns) uses that space; textareas and checkbox groups still span the
full width since they don't shrink well into a column.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
h-[60vh] made the terminal's height track the viewport regardless of
its actual width, so it read as too tall on narrower layouts.
aspect-video keeps it at 16/9 relative to its own width instead.
redactedCommandLine only ever included argv (script.command + args),
so a passAs:env variable like a scene name was invisible in run
history even though it's the main thing that varied between runs.
Secrets still redact to *** instead of being omitted outright.
select/radio don't scale to enums with dozens of choices. Reuses the
same cmdk Command/Popover primitives multi-select already uses, just
single-valued instead of an array.
Prettier had never been run in --check mode here before, so this had
drifted across most files (markdown tables, long option() chains,
line wrapping). Purely formatting, no logic changes - needed so a CI
format:check gate can actually pass. Adds .prettierignore for
pnpm-lock.yaml specifically: prettier's YAML formatter rewrites every
quoted key (single -> double quotes) producing an ~8700-line diff of
pure noise on a file pnpm itself owns the formatting of.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
`run <scriptId>` auto-detects whether the web server is already
reachable (a quick /api/healthz check):
- If it is, the run goes through the existing POST
/api/scripts/:id/runs endpoint (token-authenticated, same as any
other API client) and the CLI subscribes over /ws/runs exactly like
a browser tab - so the run shows up live in Run History and any open
browser watching it, with zero server-side changes, since the
broadcast path has no idea a run was triggered by a click vs a CLI
invocation.
- If nothing's reachable, it calls startRun() directly in its own
process (after its own migrateOnBoot/reconcileOrphanedRuns, so a
from-scratch .triggershell/ works standalone) and streams output by
listening on the same in-process runEvents emitter a WS client would
otherwise be fed from - read-log-then-listen, the same ordering
ws/server.ts's subscribe() already uses, so a fast script finishing
before the listener attaches still gets its output printed.
Both modes support --var name=value (repeatable; repeat a name for
multiselect), --no-wait, and Ctrl-C cancellation through the same
mechanism the web UI's Cancel button uses (a WS cancel message
remotely, cancelRun() directly locally). `scripts list`/`scripts show`
are local-only, no network - same direct-config-read pattern as
`validate`/`doctor`.
Extracts defaultValuesForScript() out of dynamic-form.tsx into
src/lib/config/defaults.ts so the CLI's --var handling and the web
form fill in a script's configured defaults identically instead of
duplicating that logic.
Verified live end-to-end: a CLI-triggered remote run was observed
streaming to both the triggering CLI process and an independent WS
client (simulating a browser tab) simultaneously; local-mode Ctrl-C
confirmed to actually kill the spawned child process, not just the
CLI; token, wrong-token, and TRIGGERSHELL_API_TOKEN auth paths all
verified against a running auth-enabled server.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The nav's Terminal icon uses text-primary, which resolves to a
different brass shade in light vs dark mode - the favicon was
hardcoded to only the dark-mode shade. Uses prefers-color-scheme
so the favicon glyph always matches whichever primary the header
is actually showing.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Replaces the default shadcn grayscale/Geist look with a deliberate
identity built around the product's own subject - a control panel for
running scripts and watching their output live:
- Type: Bricolage Grotesque for page/card titles (via the existing
--font-heading token, used with restraint), IBM Plex Sans for UI body
text, IBM Plex Mono for technical data (run IDs, commands, timestamps,
status labels) - all self-hosted at build time via next/font/google,
no runtime CDN dependency for a self-hosted tool.
- Color: a cool graphite ink/paper base with a warm brass signal accent
in both themes. The brass tone doubles as the "running" status color,
so an active run literally lights the UI up with the brand color.
New --status-* tokens give queued/running/succeeded/failed/cancelled/
warn a single source of truth instead of ad-hoc Tailwind color classes.
- Structural language: small tracked-out uppercase mono labels mark
technical fields (Command, Variables, Triggered by...) consistently;
the live-output terminal gets an instrument-bezel frame (header bar +
panel) instead of floating loose above the xterm canvas.
Layout/IA is unchanged throughout - this is a token- and detail-level
pass, not a restructuring.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Now that the CLI and the Next.js app are one package, nesting it inside
app/ served no purpose - the repo root itself becomes the published
npm package. Merges app/.gitignore and app/README.md into the root
versions, drops the now-duplicate app/LICENSE, and updates path
references (README, docs/ARCHITECTURE.md, docs/CONFIG_REFERENCE.md,
package.json's repository.directory) that assumed the app/ nesting.
Also fixes a real bug this surfaced: the in-app docs viewer resolved
docs/ relative to process.cwd(), which only worked by accident when the
CLI happened to be invoked from app/'s parent directory. A first attempt
at fixing it with import.meta.dirname broke instead, for the same
cross-module-graph reason config-path resolution already documented -
Next compiles Route Handlers through a separate module graph that
doesn't preserve source-relative import.meta paths. Fixed by exposing
the app root via TRIGGERSHELL_APP_ROOT (set once in server.ts, where
import.meta *does* resolve correctly), the same pattern already used
for TRIGGERSHELL_CONFIG_PATH.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>