Skip to content

feat(tui): state-tinted tool cards, session status bar, mode borders, cosine shimmer - #58

Merged
elkaix merged 11 commits into
mainfrom
feat/tui-signature-design
Aug 12, 2026
Merged

feat(tui): state-tinted tool cards, session status bar, mode borders, cosine shimmer#58
elkaix merged 11 commits into
mainfrom
feat/tui-signature-design

Conversation

@elkaix

@elkaix elkaix commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Related Issue

No linked issue — directly requested design work; problem explained below.

Problem

Tool-call blocks, the input chrome, and the working shimmer are visually flat: tool state (running / succeeded / failed) is only distinguishable by a small glyph, parallel sessions look identical, and permission modes are invisible while typing.

What changed

Four signature visual elements ported from an MIT-licensed terminal-agent design:

  • State-tinted tool cards — every tool-call block paints a full-width background tint: subtle blue-gray while running, near-invisible dark on success, dark red on error (pale equivalents in the light theme). Three new theme tokens (toolPendingBg, toolSuccessBg, toolErrorBg) added to both palettes, the custom-theme JSON schema, the theme docs, and the custom-theme skill table.
  • Status bar above the input — one line showing a model+effort chip and mode badges, joined to a right-aligned cwd chip by a rule painted in a per-session accent color (stable hash of the session title/id), so parallel sessions are visually distinct. Mounted in both fixed and inline layouts.
  • Editor border mode colors — the input border now also reflects yolo and auto permission modes (plan mode and thinking-level colors already existed).
  • Cosine shimmer — the working-label shimmer sweeps a cosine brightness band at constant velocity with three intensity tiers, replacing the fixed sliding window; same API, callers unchanged.

Three existing layout/shimmer expectation tests were updated to match the intended new visuals; all other tests pass unchanged.

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked a related issue, or explained the problem above.
  • I have added tests that prove my feature works.
  • Ran gen-changesets skill, or this PR needs no changeset.
  • Ran gen-docs skill, or this PR needs no doc update.

Summary by CodeRabbit

  • New Features

    • Added a responsive status bar showing model, thinking level, token speed, active modes, extras, and working directory.
    • Added session-specific status-bar accent colors.
    • Added state-based background colors for pending, successful, and failed tool cards.
    • Improved permission and plan mode visual indicators.
    • Added smoother, multi-tone shimmer animation and updated workflow progress indicators.
  • Documentation

    • Documented new tool-card theme color options for dark and light themes.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: dfff884c-59c3-446c-bdc7-3133a4d95995

📥 Commits

Reviewing files that changed from the base of the PR and between ebfb674 and 66d0076.

📒 Files selected for processing (5)
  • apps/pythinker-code/test/tui/activity-pane.test.ts
  • apps/pythinker-code/test/tui/components/messages/thinking.test.ts
  • apps/pythinker-code/test/tui/components/messages/tool-call.test.ts
  • apps/pythinker-code/test/tui/components/panels/footer-bg-agents.test.ts
  • apps/pythinker-code/test/tui/components/status-bar.test.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • apps/pythinker-code/test/tui/components/messages/tool-call.test.ts
  • apps/pythinker-code/test/tui/components/status-bar.test.ts
  • apps/pythinker-code/test/tui/components/messages/thinking.test.ts
  • apps/pythinker-code/test/tui/components/panels/footer-bg-agents.test.ts

📝 Walkthrough

Walkthrough

The TUI adds state-specific tool-card backgrounds, a responsive session status bar, deterministic session accents, permission-mode editor borders, and time-based shimmer animation. Tests, theme schemas, documentation, and a changeset cover these updates.

Changes

TUI visual surfaces

Layer / File(s) Summary
Tool-state theming
apps/pythinker-code/src/tui/theme/*, apps/pythinker-code/src/tui/components/messages/tool-call.ts, docs/customization/themes.md, packages/agent-core/src/skill/builtin/custom-theme.md
Tool cards use pending, success, and error background tokens. Palette definitions, schema validation, tests, and documentation define these tokens.
Status bar and session chrome
apps/pythinker-code/src/tui/components/chrome/status-bar.ts, apps/pythinker-code/src/tui/tui-state.ts, apps/pythinker-code/src/tui/pythinker-tui.ts, apps/pythinker-code/src/tui/utils/session-accent.ts, apps/pythinker-code/test/tui/components/status-bar.test.ts, apps/pythinker-code/test/tui/utils/session-accent.test.ts
The TUI mounts and updates a responsive status bar with a stable session accent. Editor borders reflect permission modes, and layout tests verify status-bar placement.
Footer status projection
apps/pythinker-code/src/tui/runtime/footer/footer-model.ts, apps/pythinker-code/src/tui/components/chrome/footer.ts, apps/pythinker-code/test/tui/components/panels/*, apps/pythinker-code/test/tui/controllers/*
Footer status data is split into status-bar extras. Badge, goal, context, cost, and throughput tests now validate the footer model instead of legacy footer rendering.
Time-based shimmer and workflow motion
apps/pythinker-code/src/tui/utils/shimmer.ts, apps/pythinker-code/src/tui/components/messages/*, apps/pythinker-code/src/tui/constant/rendering.ts, apps/pythinker-code/test/tui/utils/shimmer.test.ts, apps/pythinker-code/test/tui/components/messages/*
shimmerText uses elapsed time and three color tiers. Dynamic Workflow uses rotating Braille frames at a 300 ms cadence. Animation tests cover timing, styling, lifecycle, and cancellation.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title uses a valid conventional prefix and describes the changes, but it is 84 characters and does not use imperative mood. Rewrite the title in imperative mood and limit it to 72 characters or fewer while preserving the main change.
Docstring Coverage ⚠️ Warning Docstring coverage is 3.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The description includes the required sections, explains the problem and changes, and completes all checklist items.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Comment @coderabbitai help to get the list of available commands.

@pkg-pr-new

pkg-pr-new Bot commented Aug 11, 2026

Copy link
Copy Markdown
pnpm dlx https://pkg.pr.new/@pythoughts/pythinker-code@66d0076
npx https://pkg.pr.new/@pythoughts/pythinker-code@66d0076

commit: 66d0076

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/pythinker-code/src/tui/components/chrome/status-bar.ts`:
- Around line 12-21: Update StatusBarStatus to include FooterStatus.fastMode,
then adjust renderModesChip so fastMode renders the ↯ fast badge using modeFast
and workflow renders with the appropriate workflow token instead of modeFast.
Add a status-bar test covering fast-mode rendering.

In `@apps/pythinker-code/src/tui/components/messages/tool-call.ts`:
- Around line 668-675: Update the line-mapping condition in the tool-card
rendering path so the first two rendered rows—the leading Spacer and
headerText—remain unchanged; apply the background only from index 2 onward,
preserving the existing padding behavior for body lines.

In `@apps/pythinker-code/test/tui/components/messages/tool-call.test.ts`:
- Around line 65-85: Strengthen the assertions in the tool-call tint test around
component.render(40): for pending, successful, and error results, require at
least one body row before checking every row’s background color. Also assert
that the header row lacks each state-specific background in all three states,
not only while pending, using the existing rendered output for each state.

In `@apps/pythinker-code/test/tui/components/status-bar.test.ts`:
- Around line 63-65: Update the width loop in the status-bar rendering test to
first assert that component.render(width) returns exactly one row, then validate
that row’s visible width does not exceed width. Reuse the rendered result so an
empty array cannot satisfy the width assertion vacuously.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 22ce2320-0f3a-4458-8a92-e8d3fc6f0d0c

📥 Commits

Reviewing files that changed from the base of the PR and between 45887dc and 86ceb3c.

📒 Files selected for processing (18)
  • .changeset/tui-signature-design.md
  • apps/pythinker-code/src/tui/components/chrome/status-bar.ts
  • apps/pythinker-code/src/tui/components/messages/tool-call.ts
  • apps/pythinker-code/src/tui/pythinker-tui.ts
  • apps/pythinker-code/src/tui/theme/colors.ts
  • apps/pythinker-code/src/tui/theme/theme-schema.json
  • apps/pythinker-code/src/tui/tui-state.ts
  • apps/pythinker-code/src/tui/utils/session-accent.ts
  • apps/pythinker-code/src/tui/utils/shimmer.ts
  • apps/pythinker-code/test/tui/components/messages/dynamic-workflow-mission-control.test.ts
  • apps/pythinker-code/test/tui/components/messages/tool-call.test.ts
  • apps/pythinker-code/test/tui/components/status-bar.test.ts
  • apps/pythinker-code/test/tui/pythinker-tui-message-flow.test.ts
  • apps/pythinker-code/test/tui/pythinker-tui-startup.test.ts
  • apps/pythinker-code/test/tui/theme/palette.test.ts
  • apps/pythinker-code/test/tui/utils/session-accent.test.ts
  • docs/customization/themes.md
  • packages/agent-core/src/skill/builtin/custom-theme.md

Comment thread apps/pythinker-code/src/tui/components/chrome/status-bar.ts Outdated
Comment thread apps/pythinker-code/src/tui/components/messages/tool-call.ts
Comment thread apps/pythinker-code/test/tui/components/messages/tool-call.test.ts
Comment thread apps/pythinker-code/test/tui/components/status-bar.test.ts
@elkaix
elkaix changed the base branch from main to feat/tool-intent August 12, 2026 00:57
@elkaix
elkaix force-pushed the feat/tui-signature-design branch from 86ceb3c to a68cb74 Compare August 12, 2026 01:01
@elkaix
elkaix force-pushed the feat/tui-signature-design branch from a68cb74 to f495c80 Compare August 12, 2026 03:00
@elkaix
elkaix force-pushed the feat/tui-signature-design branch 2 times, most recently from 5d80ea7 to 525b3db Compare August 12, 2026 04:39
@elkaix
elkaix force-pushed the feat/tui-signature-design branch from 525b3db to de67ad5 Compare August 12, 2026 04:45
@elkaix
elkaix force-pushed the feat/tui-signature-design branch from de67ad5 to eade981 Compare August 12, 2026 05:15
@elkaix
elkaix force-pushed the feat/tui-signature-design branch from eade981 to 4b47377 Compare August 12, 2026 05:36
@elkaix
elkaix force-pushed the feat/tui-signature-design branch from 4b47377 to 98a175d Compare August 12, 2026 05:59
@elkaix
elkaix force-pushed the feat/tui-signature-design branch from 98a175d to bf66a7c Compare August 12, 2026 06:07
Base automatically changed from feat/tool-intent to main August 12, 2026 06:30
elkaix added 7 commits August 12, 2026 02:30
… cosine shimmer

Port four signature visual elements: tool-call blocks carry a
full-width background tint keyed to their state; a one-line status bar
above the editor shows model, effort, and mode chips joined by a
session-accent rule; the editor border colors yolo and auto permission
modes; and the shimmer sweep uses a cosine band at constant velocity.
elkaix added 3 commits August 12, 2026 02:30
Gate the status bar model, effort, and mode chips on their status_line
settings, drop the effort suffix when thinking is off, and paint the
yolo badge with the error token. Slow the shimmer sweep and alternate
the mission-control peak with a warning highlight; remove the dead
frame option in favor of a documented bandHalfWidth.
The prompt box no longer tints by permission mode or thinking effort;
it uses the neutral border color, and yolo stays visible as a status
bar badge. Token speed returns to the model chip, matching the footer
composition, instead of sitting mid-ladder in the extras.
@elkaix
elkaix force-pushed the feat/tui-signature-design branch from bf66a7c to ebfb674 Compare August 12, 2026 06:30

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Nitpick comments (1)
apps/pythinker-code/src/tui/components/messages/tool-call.ts (1)

662-669: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Avoid the negated condition.

Line 667 triggers Oxlint's unicorn(no-negated-condition) rule. Check the error case first and keep success as the fallback. This preserves the current behavior, including treating is_error === undefined as success.

Proposed fix
 const background =
   this.result === undefined
     ? this.toolCall.truncated === true
       ? undefined
       : 'toolPendingBg'
-    : this.result.is_error !== true
-      ? 'toolSuccessBg'
-      : 'toolErrorBg';
+    : this.result.is_error === true
+      ? 'toolErrorBg'
+      : 'toolSuccessBg';
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/pythinker-code/src/tui/components/messages/tool-call.ts` around lines
662 - 669, Update the background selection expression in the tool-call component
to check the error case first, using toolErrorBg when result.is_error === true
and toolSuccessBg as the fallback; preserve the existing pending/truncated
behavior and treat undefined is_error as success.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/pythinker-code/src/tui/pythinker-tui.ts`:
- Line 1339: Update the editor border-color selection logic near the
permission-mode handling and the corresponding theme-token selection around the
`2130-2132` path to explicitly handle `permissionMode` values `auto` and `yolo`
before falling back to the default `border` token, while preserving the
highlighted `primary` behavior.

In `@apps/pythinker-code/test/tui/activity-pane.test.ts`:
- Line 291: Update both assertions in
apps/pythinker-code/test/tui/activity-pane.test.ts at lines 291-291 and 379-381
to match the BRAILLE_SPINNER_FRAMES character set before “Orchestrating” instead
of only quarter-circle frames, ensuring the tests fail when an aggregate Braille
spinner remains after completion or cleanup.
- Line 223: Add the Unicode (u) flag to both Braille-frame regular expressions
in activity-pane.test.ts: the matcher at lines 223-223 and the matcher at lines
372-374.

In `@apps/pythinker-code/test/tui/components/messages/thinking.test.ts`:
- Around line 92-103: Update the thinking component test assertions around
firstHeader and fullCycleHeader to validate each rendered header exists before
comparison, then compare the headers directly without any `?? ''` fallbacks.
Remove the conditional fallbacks from the mapped equality assertion as well,
preserving the existing shimmer and frame-cycle checks.

In `@apps/pythinker-code/test/tui/components/messages/tool-call.test.ts`:
- Around line 106-108: Update the truncated-call assertion in the test around
component.render(40) to first store the rendered lines and assert their length
is greater than zero, then verify none of those lines contain the background
codes. Keep the existing backgrounds check unchanged after establishing a
non-empty render result.

In `@apps/pythinker-code/test/tui/components/panels/footer-bg-agents.test.ts`:
- Around line 56-57: Add the Unicode flag to every affected regular expression:
both badge-match expressions at lines 56-57, 64-65, and 82-83 in
apps/pythinker-code/test/tui/components/panels/footer-bg-agents.test.ts; the NaN
expressions at lines 66 and 75 in
apps/pythinker-code/test/tui/components/panels/footer-context.test.ts; and the
goal expressions at lines 86 and 137 in
apps/pythinker-code/test/tui/components/panels/footer-goal-badge.test.ts.
Preserve each existing pattern and assertions.

In `@apps/pythinker-code/test/tui/components/status-bar.test.ts`:
- Line 62: Update the status-bar tests around component.render(80), including
the assertions at lines 62 and 73-75, to first assert that the render result
contains exactly one row before checking row contents. Keep the existing content
assertions unchanged after establishing the non-empty render result.

---

Nitpick comments:
In `@apps/pythinker-code/src/tui/components/messages/tool-call.ts`:
- Around line 662-669: Update the background selection expression in the
tool-call component to check the error case first, using toolErrorBg when
result.is_error === true and toolSuccessBg as the fallback; preserve the
existing pending/truncated behavior and treat undefined is_error as success.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 749f2919-60a2-4133-895c-7e07beca7695

📥 Commits

Reviewing files that changed from the base of the PR and between 86ceb3c and ebfb674.

📒 Files selected for processing (30)
  • .changeset/tui-signature-design.md
  • apps/pythinker-code/src/tui/components/chrome/activity-loader.ts
  • apps/pythinker-code/src/tui/components/chrome/footer.ts
  • apps/pythinker-code/src/tui/components/chrome/status-bar.ts
  • apps/pythinker-code/src/tui/components/dialogs/compaction.ts
  • apps/pythinker-code/src/tui/components/messages/dynamic-workflow-mission-control.ts
  • apps/pythinker-code/src/tui/components/messages/thinking.ts
  • apps/pythinker-code/src/tui/components/messages/tool-call.ts
  • apps/pythinker-code/src/tui/constant/rendering.ts
  • apps/pythinker-code/src/tui/pythinker-tui.ts
  • apps/pythinker-code/src/tui/runtime/footer/footer-model.ts
  • apps/pythinker-code/src/tui/tui-state.ts
  • apps/pythinker-code/src/tui/utils/session-accent.ts
  • apps/pythinker-code/src/tui/utils/shimmer.ts
  • apps/pythinker-code/test/tui/activity-pane.test.ts
  • apps/pythinker-code/test/tui/components/chrome/footer.test.ts
  • apps/pythinker-code/test/tui/components/dialogs/compaction.test.ts
  • apps/pythinker-code/test/tui/components/messages/dynamic-workflow-mission-control.test.ts
  • apps/pythinker-code/test/tui/components/messages/thinking.test.ts
  • apps/pythinker-code/test/tui/components/messages/tool-call.test.ts
  • apps/pythinker-code/test/tui/components/panels/footer-bg-agents.test.ts
  • apps/pythinker-code/test/tui/components/panels/footer-context.test.ts
  • apps/pythinker-code/test/tui/components/panels/footer-goal-badge.test.ts
  • apps/pythinker-code/test/tui/components/status-bar.test.ts
  • apps/pythinker-code/test/tui/controllers/session-event-handler-goal-queue.test.ts
  • apps/pythinker-code/test/tui/pythinker-tui-message-flow.test.ts
  • apps/pythinker-code/test/tui/pythinker-tui-startup.test.ts
  • apps/pythinker-code/test/tui/runtime/footer-model.test.ts
  • apps/pythinker-code/test/tui/utils/session-accent.test.ts
  • apps/pythinker-code/test/tui/utils/shimmer.test.ts
💤 Files with no reviewable changes (2)
  • apps/pythinker-code/src/tui/components/chrome/activity-loader.ts
  • apps/pythinker-code/src/tui/components/dialogs/compaction.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • apps/pythinker-code/test/tui/pythinker-tui-startup.test.ts
  • .changeset/tui-signature-design.md
  • apps/pythinker-code/src/tui/tui-state.ts
  • apps/pythinker-code/test/tui/pythinker-tui-message-flow.test.ts

Comment thread apps/pythinker-code/src/tui/pythinker-tui.ts
Comment thread apps/pythinker-code/test/tui/activity-pane.test.ts Outdated
Comment thread apps/pythinker-code/test/tui/activity-pane.test.ts
Comment thread apps/pythinker-code/test/tui/components/messages/thinking.test.ts
Comment thread apps/pythinker-code/test/tui/components/messages/tool-call.test.ts
Comment thread apps/pythinker-code/test/tui/components/panels/footer-bg-agents.test.ts Outdated
Comment thread apps/pythinker-code/test/tui/components/status-bar.test.ts Outdated
Assertions that indexed or folded over a render result treated a
missing row as an empty string or an empty array, so they passed
when the component rendered nothing. They now assert a row exists
first. Also match braille loader frames when checking that a
terminal workflow stopped animating.
@elkaix
elkaix merged commit 065bf2e into main Aug 12, 2026
12 checks passed
@elkaix
elkaix deleted the feat/tui-signature-design branch August 12, 2026 07:13
elkaix pushed a commit that referenced this pull request Aug 12, 2026
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## @pythoughts/pythinker-code@0.16.0

### Minor Changes

- [#59](#59)
[`6999b68`](6999b68)
- Add an opt-in advisor: a second model reviews the conversation after a
completed user turn unless another review is already running, and its
notes appear as an `<advisory>` block in the agent's next turn; enable
with `[advisor] enabled = true` plus an advisor model (the `advisor`
model role or `[advisor] model`), and it runs only when the advisor
shares the session model's provider.

- [#56](#56)
[`b71f094`](b71f094)
- Add model roles: lock a model alias to the small, implementer, or
advisor slot with `/model <role>`, list assignments with `/model roles`,
and reference roles as `@small`, `@implementer`, or `@advisor` wherever
a subagent model can be set; an assigned implementer role becomes the
default model for subagents.

- [#57](#57)
[`99c427c`](99c427c)
- Show what the agent is doing in the working indicator: eligible tool
calls whose input schema accepts the injected field now carry a short
model-written intent, streamed live into the spinner label (for example
"check failing test…") instead of a rotating placeholder; disable with
`PYTHINKER_CODE_EXPERIMENTAL_TOOL_INTENT=0`.

- [#58](#58)
[`065bf2e`](065bf2e)
- Redesign core TUI surfaces: tool cards get state-tinted backgrounds
with three new theme tokens, a status bar with a per-session accent
color appears between the input box and footer, and the prompt box uses
a neutral border while permission mode appears in the status bar. The
working-label shimmer uses a calmer constant-velocity sweep with
alternating mission-control highlights.

### Patch Changes

- [#62](#62)
[`7fc36fd`](7fc36fd)
- Repair invalid escape sequences and unescaped quotes in model-written
tool arguments instead of failing the tool call.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant