mirror of
https://github.com/stablyai/orca.git
synced 2026-10-16 16:03:26 +00:00
fix(sidebar): restore native hover title rename
This commit is contained in:
@@ -57,7 +57,6 @@ export function WorktreeCardHoverIdentityHeader({
|
||||
displayName={workspaceTitle}
|
||||
disabled={workspaceTitleRenameDisabled}
|
||||
editingPresentation="field"
|
||||
selectOnFocus={false}
|
||||
wrapTitle
|
||||
className={cn(
|
||||
'text-[13px] font-semibold leading-snug text-foreground',
|
||||
@@ -65,9 +64,7 @@ export function WorktreeCardHoverIdentityHeader({
|
||||
identityOrder === 'branch-first' && 'mt-1'
|
||||
)}
|
||||
editingClassName={cn(
|
||||
// Why: keep field padding on the wrapper so native input padding doesn't
|
||||
// expose arrow-cursor dead zones between glyphs and the field edge.
|
||||
'-mx-1.5 w-[calc(100%+0.75rem)] cursor-text px-1.5 text-[13px] leading-snug',
|
||||
'-mx-1.5 w-[calc(100%+0.75rem)] cursor-text text-[13px] leading-snug',
|
||||
identityOrder === 'branch-first' && 'mt-1'
|
||||
)}
|
||||
onEditingChange={handleWorkspaceTitleEditingChange}
|
||||
|
||||
@@ -220,10 +220,10 @@ describe('WorktreeCardDetailsHover interactions', () => {
|
||||
expect(identityHeader?.className).toContain('cursor-text')
|
||||
expect(input).not.toBeNull()
|
||||
expect(input?.className).toContain('cursor-text')
|
||||
expect(input?.className).toContain('select-none')
|
||||
expect(input?.className).not.toContain('select-text')
|
||||
expect(input?.className).toContain('select-text')
|
||||
expect(input?.className).not.toContain('select-none')
|
||||
expect(sizingTitle?.className).toContain('pointer-events-none')
|
||||
expect(input?.getAttribute('draggable')).toBe('false')
|
||||
expect(input?.getAttribute('draggable')).toBeNull()
|
||||
expect(input?.className).toContain('bg-input/40')
|
||||
expect(input?.className).toContain('rounded-sm')
|
||||
expect(input?.className).toContain('selection:bg-[Highlight]')
|
||||
@@ -235,7 +235,15 @@ describe('WorktreeCardDetailsHover interactions', () => {
|
||||
input?.dispatchEvent(dragStart)
|
||||
})
|
||||
|
||||
expect(dragStart.defaultPrevented).toBe(true)
|
||||
expect(dragStart.defaultPrevented).toBe(false)
|
||||
|
||||
const doubleClick = new MouseEvent('dblclick', { bubbles: true, cancelable: true })
|
||||
|
||||
act(() => {
|
||||
input?.dispatchEvent(doubleClick)
|
||||
})
|
||||
|
||||
expect(doubleClick.defaultPrevented).toBe(false)
|
||||
|
||||
act(() => {
|
||||
interactionMocks.onHoverOpenChange?.(false)
|
||||
|
||||
@@ -1,134 +0,0 @@
|
||||
import React from 'react'
|
||||
import { LoaderCircle } from 'lucide-react'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { translate } from '@/i18n/i18n'
|
||||
|
||||
type WorktreeTitleFieldRenameProps = {
|
||||
titleElementKey: string
|
||||
displayName: string
|
||||
value: string
|
||||
disabled: boolean
|
||||
editing: boolean
|
||||
saving: boolean
|
||||
showUnreadEmphasis: boolean
|
||||
className?: string
|
||||
editingClassName?: string
|
||||
inputClassName?: string
|
||||
wrapTitle: boolean
|
||||
suppressMouseSelection: boolean
|
||||
rootRef: React.RefCallback<HTMLSpanElement>
|
||||
inputRef: React.RefCallback<HTMLInputElement>
|
||||
onStartRename: (event: React.MouseEvent<HTMLElement>) => void
|
||||
onStopCardEvent: (event: React.SyntheticEvent) => void
|
||||
onInputDoubleClick: (event: React.MouseEvent<HTMLInputElement>) => void
|
||||
onInputDragStart: (event: React.DragEvent<HTMLInputElement>) => void
|
||||
onInputMouseDown: (event: React.MouseEvent<HTMLInputElement>) => void
|
||||
onInputSelect: (event: React.SyntheticEvent<HTMLInputElement>) => void
|
||||
onInputKeyDown: (event: React.KeyboardEvent<HTMLInputElement>) => void
|
||||
onValueChange: (value: string) => void
|
||||
onCommitRename: () => void
|
||||
}
|
||||
|
||||
const fieldEditingInputClassName =
|
||||
'h-6 rounded-sm border border-input bg-input/40 px-0 py-0 shadow-xs selection:bg-[Highlight] selection:text-[HighlightText] focus-visible:border-ring focus-visible:ring-[1px] focus-visible:ring-ring/50 dark:bg-input/30'
|
||||
const fieldReadInputClassName =
|
||||
'absolute inset-0 h-full min-h-[1lh] rounded-none border-0 !border-transparent !bg-transparent p-0 !text-transparent !shadow-none caret-transparent selection:bg-transparent selection:text-transparent focus-visible:border-transparent focus-visible:ring-0 focus-visible:outline-none dark:!bg-transparent'
|
||||
|
||||
export function WorktreeTitleFieldRename({
|
||||
titleElementKey,
|
||||
displayName,
|
||||
value,
|
||||
disabled,
|
||||
editing,
|
||||
saving,
|
||||
showUnreadEmphasis,
|
||||
className,
|
||||
editingClassName,
|
||||
inputClassName,
|
||||
wrapTitle,
|
||||
suppressMouseSelection,
|
||||
rootRef,
|
||||
inputRef,
|
||||
onStartRename,
|
||||
onStopCardEvent,
|
||||
onInputDoubleClick,
|
||||
onInputDragStart,
|
||||
onInputMouseDown,
|
||||
onInputSelect,
|
||||
onInputKeyDown,
|
||||
onValueChange,
|
||||
onCommitRename
|
||||
}: WorktreeTitleFieldRenameProps): React.JSX.Element {
|
||||
const fieldInputCursorClassName = disabled ? 'cursor-default' : 'cursor-text'
|
||||
|
||||
return (
|
||||
<span
|
||||
key={`field:${titleElementKey}`}
|
||||
ref={rootRef}
|
||||
className={cn(
|
||||
'relative grid min-w-0 leading-tight text-foreground',
|
||||
showUnreadEmphasis ? 'font-semibold' : 'font-normal',
|
||||
className,
|
||||
editing && editingClassName
|
||||
)}
|
||||
data-worktree-title-inline-rename={editing ? 'editing' : ''}
|
||||
onDoubleClick={editing ? undefined : onStartRename}
|
||||
>
|
||||
<span
|
||||
className={cn(
|
||||
'pointer-events-none col-start-1 row-start-1 min-w-0',
|
||||
editing
|
||||
? 'invisible truncate whitespace-pre'
|
||||
: wrapTitle
|
||||
? 'break-words whitespace-normal'
|
||||
: 'truncate whitespace-nowrap'
|
||||
)}
|
||||
aria-hidden="true"
|
||||
>
|
||||
{displayName}
|
||||
</span>
|
||||
<Input
|
||||
ref={inputRef}
|
||||
value={editing ? value : displayName}
|
||||
style={{ font: 'inherit' }}
|
||||
readOnly={!editing}
|
||||
disabled={saving}
|
||||
draggable={false}
|
||||
spellCheck={false}
|
||||
tabIndex={disabled ? -1 : 0}
|
||||
aria-label={translate(
|
||||
'auto.components.sidebar.WorktreeTitleInlineRename.bff3bdd00c',
|
||||
'Rename workspace'
|
||||
)}
|
||||
data-worktree-title-rename-input={editing ? 'true' : undefined}
|
||||
onChange={(event) => {
|
||||
if (editing) {
|
||||
onValueChange(event.target.value)
|
||||
}
|
||||
}}
|
||||
onBlur={editing ? onCommitRename : undefined}
|
||||
onClick={onStopCardEvent}
|
||||
onDoubleClick={editing ? onInputDoubleClick : onStartRename}
|
||||
onDragStart={onInputDragStart}
|
||||
onMouseDown={onInputMouseDown}
|
||||
onPointerDown={onStopCardEvent}
|
||||
onSelect={editing ? onInputSelect : undefined}
|
||||
onKeyDown={editing ? onInputKeyDown : onStopCardEvent}
|
||||
className={cn(
|
||||
// Why: the hovercard title keeps the same native text field before
|
||||
// and during rename so the OS cursor never has to refresh a new node.
|
||||
'col-start-1 row-start-1 min-w-0 truncate text-foreground outline-none',
|
||||
fieldInputCursorClassName,
|
||||
suppressMouseSelection ? 'select-none' : 'select-text',
|
||||
editing ? fieldEditingInputClassName : fieldReadInputClassName,
|
||||
saving && 'pr-6',
|
||||
inputClassName
|
||||
)}
|
||||
/>
|
||||
{saving ? (
|
||||
<LoaderCircle className="pointer-events-none absolute right-1.5 top-1/2 size-3 -translate-y-1/2 animate-spin text-muted-foreground" />
|
||||
) : null}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
@@ -26,6 +26,8 @@ describe('WorktreeTitleInlineRename lifecycle', () => {
|
||||
root = null
|
||||
container?.remove()
|
||||
container = null
|
||||
document.documentElement.removeAttribute('data-worktree-title-rename-cursor-lock')
|
||||
document.getElementById('worktree-title-rename-cursor-lock-style')?.remove()
|
||||
})
|
||||
|
||||
it('reports editing=false if it unmounts while renaming', () => {
|
||||
@@ -115,7 +117,9 @@ describe('WorktreeTitleInlineRename lifecycle', () => {
|
||||
setSelectionRange.mockRestore()
|
||||
})
|
||||
|
||||
it('suppresses mouse selection in hovercard field mode', () => {
|
||||
it('selects the full title when hovercard field mode starts editing', () => {
|
||||
const select = vi.spyOn(HTMLInputElement.prototype, 'select')
|
||||
const setSelectionRange = vi.spyOn(HTMLInputElement.prototype, 'setSelectionRange')
|
||||
const nextContainer = document.createElement('div')
|
||||
container = nextContainer
|
||||
root = createRoot(nextContainer)
|
||||
@@ -125,7 +129,6 @@ describe('WorktreeTitleInlineRename lifecycle', () => {
|
||||
<WorktreeTitleInlineRename
|
||||
displayName="Feature workspace"
|
||||
editingPresentation="field"
|
||||
selectOnFocus={false}
|
||||
onRename={vi.fn()}
|
||||
/>
|
||||
)
|
||||
@@ -138,11 +141,62 @@ describe('WorktreeTitleInlineRename lifecycle', () => {
|
||||
})
|
||||
|
||||
const input = nextContainer.querySelector('[data-worktree-title-rename-input]')
|
||||
expect(input?.className).toContain('select-none')
|
||||
expect(input?.className).not.toContain('select-text')
|
||||
expect(select).toHaveBeenCalledTimes(1)
|
||||
expect(setSelectionRange).not.toHaveBeenCalled()
|
||||
expect(input?.className).toContain('select-text')
|
||||
expect(input?.className).not.toContain('select-none')
|
||||
|
||||
select.mockRestore()
|
||||
setSelectionRange.mockRestore()
|
||||
})
|
||||
|
||||
it('keeps the hovercard field input mounted while entering edit mode', () => {
|
||||
it('locks the document cursor only while the hovercard field editor is active', () => {
|
||||
const nextContainer = document.createElement('div')
|
||||
container = nextContainer
|
||||
root = createRoot(nextContainer)
|
||||
|
||||
act(() => {
|
||||
root?.render(
|
||||
<WorktreeTitleInlineRename
|
||||
displayName="Feature workspace"
|
||||
editingPresentation="field"
|
||||
onRename={vi.fn()}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
expect(document.documentElement.hasAttribute('data-worktree-title-rename-cursor-lock')).toBe(
|
||||
false
|
||||
)
|
||||
|
||||
act(() => {
|
||||
nextContainer
|
||||
.querySelector('[data-worktree-title-inline-rename]')
|
||||
?.dispatchEvent(new MouseEvent('dblclick', { bubbles: true, cancelable: true }))
|
||||
})
|
||||
|
||||
expect(document.documentElement.hasAttribute('data-worktree-title-rename-cursor-lock')).toBe(
|
||||
true
|
||||
)
|
||||
expect(
|
||||
document.getElementById('worktree-title-rename-cursor-lock-style')?.textContent
|
||||
).toContain('cursor: text !important')
|
||||
|
||||
act(() => {
|
||||
nextContainer
|
||||
.querySelector('[data-worktree-title-rename-input]')
|
||||
?.dispatchEvent(
|
||||
new KeyboardEvent('keydown', { key: 'Escape', bubbles: true, cancelable: true })
|
||||
)
|
||||
})
|
||||
|
||||
expect(document.documentElement.hasAttribute('data-worktree-title-rename-cursor-lock')).toBe(
|
||||
false
|
||||
)
|
||||
expect(document.getElementById('worktree-title-rename-cursor-lock-style')).toBeNull()
|
||||
})
|
||||
|
||||
it('renders wrapped read text before mounting the hovercard field input', () => {
|
||||
const nextContainer = document.createElement('div')
|
||||
container = nextContainer
|
||||
root = createRoot(nextContainer)
|
||||
@@ -152,22 +206,16 @@ describe('WorktreeTitleInlineRename lifecycle', () => {
|
||||
<WorktreeTitleInlineRename
|
||||
displayName="Feature workspace"
|
||||
editingPresentation="field"
|
||||
selectOnFocus={false}
|
||||
wrapTitle
|
||||
onRename={vi.fn()}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
const readInput = nextContainer.querySelector('input')
|
||||
const readSizingTitle = nextContainer.querySelector(
|
||||
'[data-worktree-title-inline-rename] [aria-hidden="true"]'
|
||||
)
|
||||
expect(readInput?.readOnly).toBe(true)
|
||||
expect(readInput?.className).toContain('!text-transparent')
|
||||
expect(readSizingTitle?.className).toContain('break-words')
|
||||
expect(readSizingTitle?.className).toContain('whitespace-normal')
|
||||
expect(readInput?.getAttribute('data-worktree-title-rename-input')).toBeNull()
|
||||
const readTitle = nextContainer.querySelector('[data-worktree-title-inline-rename]')
|
||||
expect(nextContainer.querySelector('input')).toBeNull()
|
||||
expect(readTitle?.className).toContain('break-words')
|
||||
expect(readTitle?.className).toContain('whitespace-normal')
|
||||
|
||||
act(() => {
|
||||
nextContainer
|
||||
@@ -176,8 +224,10 @@ describe('WorktreeTitleInlineRename lifecycle', () => {
|
||||
})
|
||||
|
||||
const editingInput = nextContainer.querySelector('[data-worktree-title-rename-input]')
|
||||
expect(editingInput).toBe(readInput)
|
||||
expect(editingInput).not.toBeNull()
|
||||
expect((editingInput as HTMLInputElement | null)?.readOnly).toBe(false)
|
||||
expect(editingInput?.className).toContain('bg-input/40')
|
||||
expect(editingInput?.className).toContain('select-text')
|
||||
|
||||
act(() => {
|
||||
root?.unmount()
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import React, { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react'
|
||||
import { LoaderCircle } from 'lucide-react'
|
||||
import { toast } from 'sonner'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { translate } from '@/i18n/i18n'
|
||||
import { WorktreeTitleFieldRename } from './WorktreeTitleFieldRename'
|
||||
|
||||
export type WorktreeTitleRenameCommit = { kind: 'cancel' } | { kind: 'save'; displayName: string }
|
||||
|
||||
@@ -26,6 +25,50 @@ export function isWorktreeTitleTruncated(
|
||||
return element.scrollWidth > element.clientWidth
|
||||
}
|
||||
|
||||
const FIELD_RENAME_CURSOR_LOCK_ATTR = 'data-worktree-title-rename-cursor-lock'
|
||||
const FIELD_RENAME_CURSOR_LOCK_STYLE_ID = 'worktree-title-rename-cursor-lock-style'
|
||||
const useCursorLockEffect = typeof window === 'undefined' ? useEffect : useLayoutEffect
|
||||
|
||||
let fieldRenameCursorLockCount = 0
|
||||
|
||||
function acquireFieldRenameCursorLock(): () => void {
|
||||
if (typeof document === 'undefined') {
|
||||
return () => {}
|
||||
}
|
||||
|
||||
fieldRenameCursorLockCount += 1
|
||||
document.documentElement.setAttribute(FIELD_RENAME_CURSOR_LOCK_ATTR, '')
|
||||
|
||||
if (!document.getElementById(FIELD_RENAME_CURSOR_LOCK_STYLE_ID)) {
|
||||
const style = document.createElement('style')
|
||||
style.id = FIELD_RENAME_CURSOR_LOCK_STYLE_ID
|
||||
// Why: Chromium can leave the native cursor stale after a stationary pointer
|
||||
// swaps from wrapped text to an input. Lock cursor only while field editing.
|
||||
style.textContent = `
|
||||
html[${FIELD_RENAME_CURSOR_LOCK_ATTR}],
|
||||
html[${FIELD_RENAME_CURSOR_LOCK_ATTR}] body,
|
||||
html[${FIELD_RENAME_CURSOR_LOCK_ATTR}] body * {
|
||||
cursor: text !important;
|
||||
}
|
||||
`
|
||||
document.head.append(style)
|
||||
}
|
||||
|
||||
let released = false
|
||||
return () => {
|
||||
if (released) {
|
||||
return
|
||||
}
|
||||
released = true
|
||||
fieldRenameCursorLockCount = Math.max(0, fieldRenameCursorLockCount - 1)
|
||||
if (fieldRenameCursorLockCount > 0) {
|
||||
return
|
||||
}
|
||||
document.documentElement.removeAttribute(FIELD_RENAME_CURSOR_LOCK_ATTR)
|
||||
document.getElementById(FIELD_RENAME_CURSOR_LOCK_STYLE_ID)?.remove()
|
||||
}
|
||||
}
|
||||
|
||||
type WorktreeTitleInlineRenameProps = {
|
||||
displayName: string
|
||||
disabled?: boolean
|
||||
@@ -44,8 +87,8 @@ type WorktreeTitleInlineRenameProps = {
|
||||
// onBeginEditingConsumed so the request fires exactly once.
|
||||
beginEditing?: boolean
|
||||
onBeginEditingConsumed?: () => void
|
||||
// Why: fully selected rename text makes native inputs flip the OS cursor between
|
||||
// I-beam and arrow while moving over the hovercard field editor.
|
||||
// Why: parent-triggered rename can place the caret without selecting, while
|
||||
// double-click rename keeps the native one-keystroke replacement behavior.
|
||||
selectOnFocus?: boolean
|
||||
}
|
||||
|
||||
@@ -136,8 +179,18 @@ export function WorktreeTitleInlineRename({
|
||||
// Why: the sidebar row needs a text-only editor to avoid layout jumps; the
|
||||
// hovercard can use a compact field that reads more like native rename UI.
|
||||
const editingInputClassName =
|
||||
'h-[1lh] rounded-none border-0 !border-transparent !bg-transparent p-0 !shadow-none focus-visible:border-transparent focus-visible:ring-0 focus-visible:outline-none dark:!bg-transparent'
|
||||
const suppressMouseSelection = editingPresentation === 'field' && !selectOnFocus
|
||||
editingPresentation === 'field'
|
||||
? 'h-6 rounded-sm border border-input bg-input/40 px-1.5 py-0 shadow-xs selection:bg-[Highlight] selection:text-[HighlightText] focus-visible:border-ring focus-visible:ring-[1px] focus-visible:ring-ring/50 dark:bg-input/30'
|
||||
: 'h-[1lh] rounded-none border-0 !border-transparent !bg-transparent p-0 !shadow-none focus-visible:border-transparent focus-visible:ring-0 focus-visible:outline-none dark:!bg-transparent'
|
||||
const savingInputClassName = editingPresentation === 'field' ? 'pr-6' : 'pr-4'
|
||||
const savingSpinnerClassName = editingPresentation === 'field' ? 'right-1.5' : 'right-0'
|
||||
|
||||
useCursorLockEffect(() => {
|
||||
if (!editing || editingPresentation !== 'field') {
|
||||
return
|
||||
}
|
||||
return acquireFieldRenameCursorLock()
|
||||
}, [editing, editingPresentation])
|
||||
|
||||
const setEditingMode = useCallback(
|
||||
(nextEditing: boolean) => {
|
||||
@@ -171,8 +224,8 @@ export function WorktreeTitleInlineRename({
|
||||
if (!input || !editing) {
|
||||
return
|
||||
}
|
||||
// Why: defer focus until after the opening double-click finishes so the
|
||||
// browser doesn't leave a word/line selection that flips the OS cursor.
|
||||
// Why: defer focus until after the opening double-click finishes so
|
||||
// intentional select-all wins over the browser's initiating click target.
|
||||
renameFocusFrameRef.current = requestAnimationFrame(() => {
|
||||
renameFocusFrameRef.current = null
|
||||
input.focus()
|
||||
@@ -207,48 +260,18 @@ export function WorktreeTitleInlineRename({
|
||||
event.stopPropagation()
|
||||
}, [])
|
||||
|
||||
const stopInputDragEvent = useCallback((event: React.DragEvent<HTMLInputElement>) => {
|
||||
// Why: selected rename text can become a drag target, which flips the cursor
|
||||
// away from the I-beam while the title editor is active.
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
}, [])
|
||||
|
||||
const handleInputMouseDown = useCallback(
|
||||
(event: React.MouseEvent<HTMLInputElement>) => {
|
||||
if (suppressMouseSelection && event.detail > 1) {
|
||||
// Why: native inputs select words/lines on multi-click, which makes macOS
|
||||
// alternate arrow and I-beam cursors over the hovercard field editor.
|
||||
event.preventDefault()
|
||||
}
|
||||
stopCardEvent(event)
|
||||
},
|
||||
[stopCardEvent, suppressMouseSelection]
|
||||
[stopCardEvent]
|
||||
)
|
||||
|
||||
const handleInputDoubleClick = useCallback(
|
||||
(event: React.MouseEvent<HTMLInputElement>) => {
|
||||
if (suppressMouseSelection) {
|
||||
event.preventDefault()
|
||||
}
|
||||
stopCardEvent(event)
|
||||
},
|
||||
[stopCardEvent, suppressMouseSelection]
|
||||
)
|
||||
|
||||
const handleInputSelect = useCallback(
|
||||
(event: React.SyntheticEvent<HTMLInputElement>) => {
|
||||
if (!suppressMouseSelection) {
|
||||
return
|
||||
}
|
||||
const input = event.currentTarget
|
||||
if (input.selectionStart === input.selectionEnd) {
|
||||
return
|
||||
}
|
||||
const caret = input.value.length
|
||||
input.setSelectionRange(caret, caret)
|
||||
},
|
||||
[suppressMouseSelection]
|
||||
[stopCardEvent]
|
||||
)
|
||||
|
||||
const startRename = useCallback(
|
||||
@@ -320,36 +343,6 @@ export function WorktreeTitleInlineRename({
|
||||
[cancelRename, commitRename]
|
||||
)
|
||||
|
||||
if (editingPresentation === 'field') {
|
||||
return (
|
||||
<WorktreeTitleFieldRename
|
||||
titleElementKey={titleElementKey}
|
||||
displayName={displayName}
|
||||
value={value}
|
||||
disabled={disabled}
|
||||
editing={editing}
|
||||
saving={saving}
|
||||
showUnreadEmphasis={showUnreadEmphasis}
|
||||
className={className}
|
||||
editingClassName={editingClassName}
|
||||
inputClassName={inputClassName}
|
||||
wrapTitle={wrapTitle}
|
||||
suppressMouseSelection={suppressMouseSelection}
|
||||
rootRef={handleRootRef}
|
||||
inputRef={handleInputRef}
|
||||
onStartRename={startRename}
|
||||
onStopCardEvent={stopCardEvent}
|
||||
onInputDoubleClick={handleInputDoubleClick}
|
||||
onInputDragStart={stopInputDragEvent}
|
||||
onInputMouseDown={handleInputMouseDown}
|
||||
onInputSelect={handleInputSelect}
|
||||
onInputKeyDown={handleKeyDown}
|
||||
onValueChange={setValue}
|
||||
onCommitRename={() => void commitRename()}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
if (editing) {
|
||||
return (
|
||||
<span
|
||||
@@ -374,7 +367,6 @@ export function WorktreeTitleInlineRename({
|
||||
value={value}
|
||||
style={{ font: 'inherit' }}
|
||||
disabled={saving}
|
||||
draggable={false}
|
||||
spellCheck={false}
|
||||
aria-label={translate(
|
||||
'auto.components.sidebar.WorktreeTitleInlineRename.bff3bdd00c',
|
||||
@@ -385,16 +377,14 @@ export function WorktreeTitleInlineRename({
|
||||
onBlur={() => void commitRename()}
|
||||
onClick={stopCardEvent}
|
||||
onDoubleClick={handleInputDoubleClick}
|
||||
onDragStart={stopInputDragEvent}
|
||||
onMouseDown={handleInputMouseDown}
|
||||
onPointerDown={stopCardEvent}
|
||||
onSelect={handleInputSelect}
|
||||
onKeyDown={handleKeyDown}
|
||||
className={cn(
|
||||
'col-start-1 row-start-1 min-w-0 cursor-text truncate text-foreground outline-none',
|
||||
suppressMouseSelection ? 'select-none' : 'select-text',
|
||||
'select-text',
|
||||
editingInputClassName,
|
||||
saving && 'pr-4',
|
||||
saving && savingInputClassName,
|
||||
inputClassName
|
||||
)}
|
||||
/>
|
||||
@@ -402,7 +392,7 @@ export function WorktreeTitleInlineRename({
|
||||
<LoaderCircle
|
||||
className={cn(
|
||||
'pointer-events-none absolute top-1/2 size-3 -translate-y-1/2 animate-spin text-muted-foreground',
|
||||
'right-0'
|
||||
savingSpinnerClassName
|
||||
)}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
Reference in New Issue
Block a user