Extract shared composer hook (useComposerArea)#283
Open
brsbl wants to merge 1 commit into
Open
Conversation
The prompt-composer wiring — useThreadCreationOptions + usePromptDraftStorage + usePromptMentions + useCommandSuggestions + useUploadPromptAttachment + buildProviderPromptActionProps, assembled into the execution / permission / typeahead / attachments / composer configs — was hand-rolled in three places. Extract it into one shared hook, apps/app/src/components/promptbox/ useComposerArea.ts, and migrate the three call sites onto it. Parity-first migration (no behavior change to any surface), in order of coupling: 1. SideChatTabContent (FollowUpPromptBox, read-only footer, submit creates the child thread) — lifted first as the most self-contained. 2. ThreadDetailPromptArea (FollowUpPromptBox, editable footer, submit steers / follows up). 3. RootComposeView (NewThreadPromptBox, submit creates a new thread) — most coupled; its project picker, branch/worktree selection, queued-message stack, and the box itself stay per-site. Command discovery follows the resolved environment selection, which the hook itself produces, so it is supplied as a resolver over the live selection value. The hook owns only the shared assembly. Each site keeps its box choice, submit handler, and chrome. Sites differ on a few explicit, typed parameters: execution interactivity (read-only / provider-switchable), permission shaping (editable / editable-gated / read-only), the attachment upload-error strategy (collect failed names / first error), and the mention/command scoping. The Loops/Skills inline composer that motivated this is a follow-up that will consume the hook; it is intentionally not built here. Validation: typecheck and the full @bb/app test suite pass (871 tests, including the SideChatTabContent render test that exercises the real component through the new hook); lint clean on the changed files. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Extract the duplicated prompt-composer wiring into one shared hook,
apps/app/src/components/promptbox/useComposerArea.ts, and migrate the threeexisting call sites onto it. Refactor only — no new feature, no behavior
change to any surface.
The composer assembly (
useThreadCreationOptions+usePromptDraftStorage+usePromptMentions+useCommandSuggestions+useUploadPromptAttachment+buildProviderPromptActionProps, built into the execution / permission /typeahead / attachments / composer configs) was hand-rolled in three places. We
refused to add a 4th copy for the upcoming Loops/Skills inline composer.
Parity-first migration
Each step was verified (typecheck + tests) before the next:
SideChatTabContent's assembly (the mostself-contained), parameterized.
SideChatTabContent(FollowUpPromptBox; submit creates the childthread; read-only footer).
ThreadDetailPromptArea(FollowUpPromptBox; submit steers / followsup; editable footer).
RootComposeView(NewThreadPromptBox; submit creates a new thread) —the most coupled. Its project picker, branch/worktree selection,
queued-message stack, and the box itself stay per-site; only the config
assembly comes from the hook. Command discovery follows the resolved
environment selection — which the hook itself produces — so
commands.environmentIdaccepts a resolver over the live selection value to break the cycle.
What stays per-site
The box choice (
NewThreadPromptBoxvsFollowUpPromptBox), the submithandler, and site chrome (project picker, branch/worktree pickers,
readOnly,trigger message, queued-message stack). Sites differ on a small set of explicit,
typed hook parameters: execution interactivity (read-only / provider-switchable),
permission shaping (
editable/editable-gated/read-only), the attachmentupload-error strategy (
collectfailed names /firsterror), and themention/command scoping.
Net: the three sites shed ~756 lines of duplicated assembly; the shared hook is
the single home for it.
Follow-up (not in this PR)
The Loops/Skills inline composer that motivated the extraction is a separate
follow-up that will consume
useComposerAreaas its 4th call site. It isintentionally not built here.
Validation
pnpm exec turbo run typecheck --filter=@bb/app— green.pnpm exec turbo run test --filter=@bb/app— 137 files / 871 tests pass,including
SideChatTabContent.test.tsx, which renders the real componentthrough the new hook (submit-creates-thread, mention/command typeahead,
attachments, steer).
eslintclean (0 errors) on the changed files.better-sqlite3is built for Node 22 (ABI 127) while the runtime is Node 23(ABI 131), so the dev host daemon won't boot — an environment/native-module
gap unrelated to this frontend-only change. Worth a manual pass after a native
rebuild before merge.
🤖 Generated with Claude Code