Skip to content

Fix/textarea confirm and tui polish#9

Open
superbusinesstools wants to merge 3 commits intooxgeneral:mainfrom
superbusinesstools:fix/textarea-confirm-and-tui-polish
Open

Fix/textarea confirm and tui polish#9
superbusinesstools wants to merge 3 commits intooxgeneral:mainfrom
superbusinesstools:fix/textarea-confirm-and-tui-polish

Conversation

@superbusinesstools
Copy link
Copy Markdown

Summary

Fixes a few TUI paper-cuts around the goal/task/agent wizards, found while using the editor on Linux.

  • Ctrl+S saves descriptions. Inside a wizard textarea (goal/task/agent description), Enter adds a newline and the existing Ctrl+Enter binding is indistinguishable from plain Enter in most Linux terminals — there was no working way to confirm the field. Ctrl+S now confirms any textarea step.
  • Visible hint. The bottom CommandBar switches to step-specific hints while a wizard is open, with a bold amber Ctrl+S save description for textareas so users can see the binding without hunting.
  • Cursor at input start. Empty single-line inputs rendered the cursor after the placeholder, making it look like it was floating mid-line. Rendered before the placeholder so it sits at column 0.
  • Saved agent shows on reopen. Reopening the edit-agent wizard immediately after saving used to show the pre-save values because refreshAll() is async. The returned agent is now merged into liveAgents synchronously in the save callback.
  • Dev scripts. scripts/dev-link.sh / scripts/dev-unlink.sh to build and link the fork against the globally-installed orch binary for local testing.

Test plan

  • npx vitest run test/unit/tui/form-wizard.test.tsx — 52/52 pass (2 new tests added for Ctrl+S)
  • npm run build — green
  • Manual: edit agent role, press Ctrl+S, reopen immediately — shows new value
  • Manual: open goal wizard — cursor sits before the placeholder
  • Manual: textarea hint visible in bold amber at bottom bar

superbusinesstools and others added 3 commits April 17, 2026 13:35
Pressing Enter inside wizard textareas (goal/task/agent descriptions) adds a
newline — there was no Linux-friendly way to confirm. Ctrl+Enter is
indistinguishable from Enter in most Linux terminals, so the only confirm path
was unusable.

Ctrl+S now confirms any textarea step in FormWizard. It is surfaced in the
bottom CommandBar, which switches to step-specific hints while a wizard is
open (bold amber Ctrl+S for textareas, step-appropriate hints for
text/select/multiselect).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two small UX fixes:

* Empty single-line inputs rendered the cursor after the placeholder, making
  it look like the cursor was floating mid-line. Render cursor before
  placeholder so it sits at column 0, matching user expectation.
* Reopening the edit-agent wizard immediately after saving showed the pre-save
  values because refreshAll() is async. Merge the returned agent into
  liveAgents synchronously in the save callback, and add liveAgents to
  launchEditAgentWizard's dependency array so the wizard uses the latest
  snapshot.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Convenience scripts for testing a local build against the globally-installed
orch binary. dev-link.sh builds and npm-links the fork; dev-unlink.sh
restores the global install.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Copy link
Copy Markdown

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

🎉 Thanks for your first PR to ORCH! We're excited to review your contribution.

Our CI will run automatically:

  • ✅ TypeScript strict mode check (tsc --noEmit)
  • ✅ Full test suite (1493+ tests via Vitest)
  • ✅ ESM build verification

A maintainer will review your changes shortly. In the meantime, check the Contributing Guide to make sure everything is in order.

⭐ If you enjoy working with ORCH, a star on the repo goes a long way!

@superbusinesstools superbusinesstools marked this pull request as ready for review April 17, 2026 14:12
@oxgeneral
Copy link
Copy Markdown
Owner

Thanks for this — the Ctrl+Enter → Ctrl+S swap and cursor-at-col-0 are exactly the kind of Linux polish the TUI needed. Clean commits, good tests 🙌

One blocker + a small ask before merge:

🔴 Blocker — Ctrl+S is already bound in the agent wizard

src/tui/App.tsx:1808 already intercepts Ctrl+S in agent wizards to open the Agent Shop:

if ((key.ctrl || key.meta) && input === 's' && inputMode === 'wizard' && wizardConfig?.kind === 'agent') {
  launchShopWizard();
  return;
}

Ink's useInput doesn't stop-propagate, so both handlers fire on the same key. In the agent edit wizard (where users most often want Ctrl+S = save), the shop opens and the wizard confirms at the same time.

Could you rebind the shop-launcher (e.g. Ctrl+Shift+S, or a plain key when wizard is idle) and keep Ctrl+S free for the save/confirm idiom you're introducing?

🟡 Should-fix — optimistic setLiveAgents silently drops the agent

setLiveAgents((prev) => prev.map((a) => a.id === agent.id ? agent : a));

If the agent was deleted by a concurrent CLI between open and save, prev.map returns the same array and the saved agent silently disappears from the UI. One-liner:

setLiveAgents((prev) =>
  prev.some((a) => a.id === agent.id)
    ? prev.map((a) => a.id === agent.id ? agent : a)
    : [...prev, agent],
);

🔵 Nit (optional)

TextInput.tsx empty-state now renders <cursor><placeholder> side-by-side, so on long placeholders the cursor block visually floats in front of the text. Hiding the placeholder when the cursor is at col 0 on empty input would polish it — fine either way.


Two context notes (not blockers):

  • I just enabled branch protection with the `test` job as a required status check. Your CI hasn't run yet because GitHub requires manual approval for first-time contributors' workflows — I'll click Approve and run so your PR gets a green check.
  • Tests use raw `\x13` (works with `ink-testing-library`). On a real terminal Ctrl+S may arrive via Kitty protocol (`\x1b[115;5u`). Ink decodes both, but a quick smoke-test on a terminal you actually use would be appreciated.

Really solid first PR — thanks for tackling this. Once the Ctrl+S conflict is resolved I'll merge 🚀

@oxgeneral
Copy link
Copy Markdown
Owner

One more follow-up after a deeper pass — a subtle render issue worth catching before merge:

onStepChange identity flicker

App.tsx:2528:

onStepChange={(step) => setWizardStepType(step ? step.type : null)}

This creates a new function identity every parent render. In FormWizard.tsx:105-108:

useEffect(() => {
  onStepChange?.(steps[currentStep] ?? null);
  return () => { onStepChange?.(null); };
}, [currentStep, steps, onStepChange]);

Because onStepChange is in the deps, every parent re-render re-runs the effect: cleanup fires onStepChange?.(null)wizardStepType momentarily becomes null → immediately restored. The CommandBar hint line can flicker on any App re-render (ticks, toasts, watch updates — and those are frequent).

Fix: wrap the prop in useCallback([]) in App.tsx, or drop onStepChange from the FormWizard useEffect deps (stable ref via useRef).

Minor nits

  • wizardStepType union 'text' | 'select' | 'textarea' | 'multiselect' is redeclared in App.tsx and CommandBar.tsx — could export WizardStep['type'] from FormWizard and reuse.
  • launchEditTaskWizard (App.tsx:985) and launchTeamWizard (:995) have the same liveAgents closure issue as launchEditAgentWizard — if the deps fix there was intentional, those two need the same treatment (or switch all three to liveAgentsRef.current to keep deps stable).
  • CommandBar hint strings duplicate FormWizard hints and have already drifted ("Ctrl+S save description" vs "Ctrl+S save"). Long-term better to render hints from a single source.

Not blockers on top of the earlier Ctrl+S conflict — just flagging while it's fresh.

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.

2 participants