mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-14 14:12:44 +00:00
refactor(desktop): extract composer branch/worktree engine into useComposerBranch
Moves the CodingStatusRow hand-offs (openInWorktree + branch-off / convert / list / switch) out of ChatBar into hooks/use-composer-branch.ts, verbatim. The hook depends only on cwd + draftRef + clearDraft (backend coupling via the projects store); nothing about ChatBar's render. Dead projects/composer-store imports drop out of index.tsx.
This commit is contained in:
parent
0b3752eede
commit
5fdc2acedc
3 changed files with 101 additions and 622 deletions
|
|
@ -0,0 +1,95 @@
|
|||
import { type MutableRefObject, useCallback } from 'react'
|
||||
|
||||
import { clearComposerAttachments } from '@/store/composer'
|
||||
import { listRepoBranches, requestStartWorkSession, startWorkInRepo, switchBranchInRepo } from '@/store/projects'
|
||||
|
||||
interface UseComposerBranchOptions {
|
||||
clearDraft: () => void
|
||||
cwd: null | string | undefined
|
||||
draftRef: MutableRefObject<string>
|
||||
}
|
||||
|
||||
/**
|
||||
* Branch / worktree engine — the `CodingStatusRow` hand-offs. Each action opens
|
||||
* a fresh session anchored in a worktree carrying the current composer draft as
|
||||
* its first turn; clearing here means the draft travels to the new session
|
||||
* instead of getting stashed under this one. Backend coupling (cwd + the
|
||||
* projects store) is the only dependency; nothing about ChatBar's render.
|
||||
*/
|
||||
export function useComposerBranch({ clearDraft, cwd, draftRef }: UseComposerBranchOptions) {
|
||||
// Hand a worktree off to the controller: open a fresh session anchored there,
|
||||
// carrying the composer draft as its first turn. Clearing here means the draft
|
||||
// travels to the new session instead of getting stashed under this one.
|
||||
const openInWorktree = useCallback(
|
||||
(path: string) => {
|
||||
const text = draftRef.current
|
||||
clearDraft()
|
||||
clearComposerAttachments()
|
||||
requestStartWorkSession(path, text)
|
||||
},
|
||||
[clearDraft, draftRef]
|
||||
)
|
||||
|
||||
// Branch off into a NEW worktree (base = branch name, or current HEAD). A
|
||||
// create failure throws back to the row (which toasts) before we touch the
|
||||
// draft; a missing cwd / remote backend no-ops (the row hides the affordance).
|
||||
const handleBranchOff = useCallback(
|
||||
async (branch: string, base?: string) => {
|
||||
const repoPath = cwd?.trim()
|
||||
const result = repoPath && (await startWorkInRepo(repoPath, { base, branch, name: branch }))
|
||||
|
||||
if (result) {
|
||||
openInWorktree(result.path)
|
||||
}
|
||||
},
|
||||
[cwd, openInWorktree]
|
||||
)
|
||||
|
||||
// Convert an EXISTING branch into a fresh worktree + session (no new branch).
|
||||
// Mirrors handleBranchOff's hand-off: create the worktree, then open a session
|
||||
// anchored there carrying the draft.
|
||||
const handleConvertBranch = useCallback(
|
||||
async (branch: string, path?: null | string, isDefault?: boolean) => {
|
||||
if (path?.trim()) {
|
||||
openInWorktree(path)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const repoPath = cwd?.trim()
|
||||
|
||||
if (repoPath && isDefault) {
|
||||
await switchBranchInRepo(repoPath, branch)
|
||||
openInWorktree(repoPath)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const result = repoPath && (await startWorkInRepo(repoPath, { existingBranch: branch }))
|
||||
|
||||
if (result) {
|
||||
openInWorktree(result.path)
|
||||
}
|
||||
},
|
||||
[cwd, openInWorktree]
|
||||
)
|
||||
|
||||
const handleListBranches = useCallback(async () => {
|
||||
const repoPath = cwd?.trim()
|
||||
|
||||
return repoPath ? listRepoBranches(repoPath) : []
|
||||
}, [cwd])
|
||||
|
||||
const handleSwitchBranch = useCallback(
|
||||
async (branch: string) => {
|
||||
const repoPath = cwd?.trim()
|
||||
|
||||
if (repoPath) {
|
||||
await switchBranchInRepo(repoPath, branch)
|
||||
}
|
||||
},
|
||||
[cwd]
|
||||
)
|
||||
|
||||
return { handleBranchOff, handleConvertBranch, handleListBranches, handleSwitchBranch, openInWorktree }
|
||||
}
|
||||
|
|
@ -20,7 +20,7 @@ import { desktopSlashCommandTakesArgs } from '@/lib/desktop-slash-commands'
|
|||
import { DATA_IMAGE_URL_RE } from '@/lib/embedded-images'
|
||||
import { triggerHaptic } from '@/lib/haptics'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { $composerAttachments, clearComposerAttachments } from '@/store/composer'
|
||||
import { $composerAttachments } from '@/store/composer'
|
||||
import {
|
||||
browseBackward,
|
||||
browseForward,
|
||||
|
|
@ -37,7 +37,6 @@ import {
|
|||
setComposerPoppedOut
|
||||
} from '@/store/composer-popout'
|
||||
import { removeQueuedPrompt } from '@/store/composer-queue'
|
||||
import { listRepoBranches, requestStartWorkSession, startWorkInRepo, switchBranchInRepo } from '@/store/projects'
|
||||
import { $activeSessionAwaitingInput } from '@/store/prompts'
|
||||
import { toggleReview } from '@/store/review'
|
||||
import { $gatewayState, $messages } from '@/store/session'
|
||||
|
|
@ -62,6 +61,7 @@ import { COMPOSER_DROP_ACTIVE_CLASS, COMPOSER_DROP_FADE_CLASS } from './drop-aff
|
|||
import { markActiveComposer } from './focus'
|
||||
import { HelpHint } from './help-hint'
|
||||
import { useAtCompletions } from './hooks/use-at-completions'
|
||||
import { useComposerBranch } from './hooks/use-composer-branch'
|
||||
import { useComposerDraft } from './hooks/use-composer-draft'
|
||||
import { useComposerDrop } from './hooks/use-composer-drop'
|
||||
import { useComposerMetrics } from './hooks/use-composer-metrics'
|
||||
|
|
@ -966,79 +966,10 @@ export function ChatBar({
|
|||
handleInputDrop
|
||||
} = useComposerDrop({ cwd, insertInlineRefs, onAttachDroppedItems, requestMainFocus })
|
||||
|
||||
// Hand a worktree off to the controller: open a fresh session anchored there,
|
||||
// carrying the composer draft as its first turn. Clearing here means the draft
|
||||
// travels to the new session instead of getting stashed under this one.
|
||||
const openInWorktree = useCallback(
|
||||
(path: string) => {
|
||||
const text = draftRef.current
|
||||
clearDraft()
|
||||
clearComposerAttachments()
|
||||
requestStartWorkSession(path, text)
|
||||
},
|
||||
[clearDraft]
|
||||
)
|
||||
|
||||
// Branch off into a NEW worktree (base = branch name, or current HEAD). A
|
||||
// create failure throws back to the row (which toasts) before we touch the
|
||||
// draft; a missing cwd / remote backend no-ops (the row hides the affordance).
|
||||
const handleBranchOff = useCallback(
|
||||
async (branch: string, base?: string) => {
|
||||
const repoPath = cwd?.trim()
|
||||
const result = repoPath && (await startWorkInRepo(repoPath, { base, branch, name: branch }))
|
||||
|
||||
if (result) {
|
||||
openInWorktree(result.path)
|
||||
}
|
||||
},
|
||||
[cwd, openInWorktree]
|
||||
)
|
||||
|
||||
// Convert an EXISTING branch into a fresh worktree + session (no new branch).
|
||||
// Mirrors handleBranchOff's hand-off: create the worktree, then open a session
|
||||
// anchored there carrying the draft.
|
||||
const handleConvertBranch = useCallback(
|
||||
async (branch: string, path?: null | string, isDefault?: boolean) => {
|
||||
if (path?.trim()) {
|
||||
openInWorktree(path)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const repoPath = cwd?.trim()
|
||||
|
||||
if (repoPath && isDefault) {
|
||||
await switchBranchInRepo(repoPath, branch)
|
||||
openInWorktree(repoPath)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const result = repoPath && (await startWorkInRepo(repoPath, { existingBranch: branch }))
|
||||
|
||||
if (result) {
|
||||
openInWorktree(result.path)
|
||||
}
|
||||
},
|
||||
[cwd, openInWorktree]
|
||||
)
|
||||
|
||||
const handleListBranches = useCallback(async () => {
|
||||
const repoPath = cwd?.trim()
|
||||
|
||||
return repoPath ? listRepoBranches(repoPath) : []
|
||||
}, [cwd])
|
||||
|
||||
const handleSwitchBranch = useCallback(
|
||||
async (branch: string) => {
|
||||
const repoPath = cwd?.trim()
|
||||
|
||||
if (repoPath) {
|
||||
await switchBranchInRepo(repoPath, branch)
|
||||
}
|
||||
},
|
||||
[cwd]
|
||||
)
|
||||
// Branch / worktree hand-offs (CodingStatusRow). Owns the worktree open +
|
||||
// branch-off/convert/list/switch actions; draft travels into the new session.
|
||||
const { handleBranchOff, handleConvertBranch, handleListBranches, handleSwitchBranch, openInWorktree } =
|
||||
useComposerBranch({ clearDraft, cwd, draftRef })
|
||||
|
||||
// Esc cancels the in-flight turn when the CHAT has focus — not just the
|
||||
// composer input (which has its own handler above). Clicking into the
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue