feat(ui): add draft target shortcut sequences
This commit is contained in:
@@ -101,6 +101,9 @@ and the send path reading the same grammar.
|
|||||||
- `state/useDraftTarget.ts` — the draft can target a directory that does not
|
- `state/useDraftTarget.ts` — the draft can target a directory that does not
|
||||||
exist yet (a worktree being created). It must survive not appearing in the
|
exist yet (a worktree being created). It must survive not appearing in the
|
||||||
branch list, or the selector snaps back to the project root mid-creation.
|
branch list, or the selector snaps back to the project root mid-creation.
|
||||||
|
- `ui/DraftTargetSelectors.tsx` owns the controlled project/worktree picker
|
||||||
|
state and registers its application shortcuts locally. The selectors only
|
||||||
|
consume their shared prefix while the draft target UI is mounted.
|
||||||
|
|
||||||
## Mobile
|
## Mobile
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import {
|
|||||||
import { useI18n } from '@/lib/i18n';
|
import { useI18n } from '@/lib/i18n';
|
||||||
import { PROJECT_COLOR_MAP, PROJECT_ICON_MAP, ProjectIconImage } from '@/lib/projectMeta';
|
import { PROJECT_COLOR_MAP, PROJECT_ICON_MAP, ProjectIconImage } from '@/lib/projectMeta';
|
||||||
import { createWorktreeDraft } from '@/lib/worktreeSessionCreator';
|
import { createWorktreeDraft } from '@/lib/worktreeSessionCreator';
|
||||||
|
import { useKeybind } from '@/hooks/useKeybind';
|
||||||
import type { Theme } from '@/types/theme';
|
import type { Theme } from '@/types/theme';
|
||||||
import { normalizePath } from '../attachments/filePaths';
|
import { normalizePath } from '../attachments/filePaths';
|
||||||
import { getProjectDisplayLabel, type DraftTargetProject } from '../state/useDraftTarget';
|
import { getProjectDisplayLabel, type DraftTargetProject } from '../state/useDraftTarget';
|
||||||
@@ -103,14 +104,40 @@ export function DraftTargetSelectors(props: DraftTargetProps) {
|
|||||||
onDirectoryChange,
|
onDirectoryChange,
|
||||||
theme,
|
theme,
|
||||||
} = props;
|
} = props;
|
||||||
|
const [openPicker, setOpenPicker] = React.useState<'project' | 'worktree' | null>(null);
|
||||||
|
const projectTriggerRef = React.useRef<HTMLButtonElement>(null);
|
||||||
|
const worktreeTriggerRef = React.useRef<HTMLButtonElement>(null);
|
||||||
|
|
||||||
|
useKeybind('open_draft_project_picker', () => {
|
||||||
|
projectTriggerRef.current?.focus();
|
||||||
|
setOpenPicker('project');
|
||||||
|
});
|
||||||
|
useKeybind('open_draft_worktree_picker', () => {
|
||||||
|
if (!showBranchSelector) return false;
|
||||||
|
worktreeTriggerRef.current?.focus();
|
||||||
|
setOpenPicker('worktree');
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleProjectChange = (projectId: string) => {
|
||||||
|
onProjectChange(projectId);
|
||||||
|
setOpenPicker(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDirectoryChange = (directory: string) => {
|
||||||
|
onDirectoryChange(directory);
|
||||||
|
setOpenPicker(null);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mb-1.5 flex min-w-0 items-center gap-1.5 px-0.5">
|
<div className="mb-1.5 flex min-w-0 items-center gap-1.5 px-0.5">
|
||||||
<Select
|
<Select
|
||||||
value={selectedProject.id}
|
value={selectedProject.id}
|
||||||
onValueChange={onProjectChange}
|
open={openPicker === 'project'}
|
||||||
|
onOpenChange={(open) => setOpenPicker(open ? 'project' : null)}
|
||||||
|
onValueChange={handleProjectChange}
|
||||||
>
|
>
|
||||||
<SelectTrigger
|
<SelectTrigger
|
||||||
|
ref={projectTriggerRef}
|
||||||
size="sm"
|
size="sm"
|
||||||
className="h-7 min-w-0 w-fit max-w-[42vw] sm:max-w-[18rem] border-transparent bg-transparent px-1.5 hover:bg-transparent data-[popup-open]:bg-transparent"
|
className="h-7 min-w-0 w-fit max-w-[42vw] sm:max-w-[18rem] border-transparent bg-transparent px-1.5 hover:bg-transparent data-[popup-open]:bg-transparent"
|
||||||
>
|
>
|
||||||
@@ -130,9 +157,12 @@ export function DraftTargetSelectors(props: DraftTargetProps) {
|
|||||||
{showBranchSelector ? (
|
{showBranchSelector ? (
|
||||||
<Select
|
<Select
|
||||||
value={selectedDirectory ?? branchItems[0]?.value ?? normalizePath(selectedProject.path) ?? ''}
|
value={selectedDirectory ?? branchItems[0]?.value ?? normalizePath(selectedProject.path) ?? ''}
|
||||||
onValueChange={onDirectoryChange}
|
open={openPicker === 'worktree'}
|
||||||
|
onOpenChange={(open) => setOpenPicker(open ? 'worktree' : null)}
|
||||||
|
onValueChange={handleDirectoryChange}
|
||||||
>
|
>
|
||||||
<SelectTrigger
|
<SelectTrigger
|
||||||
|
ref={worktreeTriggerRef}
|
||||||
size="sm"
|
size="sm"
|
||||||
className="h-7 min-w-0 w-fit max-w-[48vw] sm:max-w-[20rem] border-transparent bg-transparent px-1.5 hover:bg-transparent data-[popup-open]:bg-transparent"
|
className="h-7 min-w-0 w-fit max-w-[48vw] sm:max-w-[20rem] border-transparent bg-transparent px-1.5 hover:bg-transparent data-[popup-open]:bg-transparent"
|
||||||
>
|
>
|
||||||
|
|||||||
Reference in New Issue
Block a user