fix(desktop): one IME-aware Enter helper for plugins too; guard the sites main added since
- The Kanban plugin cannot import `@/lib/ime` (plugin fence), so its `./ime-enter` twin duplicated the predicate. Export `isSubmitEnter` from `@hermes/plugin-sdk` and drop the twin so one helper owns the policy. - `BoardNameField` in kanban/board-switcher.tsx (main's refactor of the create/rename board dialogs) submitted on composition Enter; guarded. - Telegram allowed-ID input and the clarify-card textarea submitted on the post-compositionend keyCode-229 Enter; both now use `isSubmitEnter`.
This commit is contained in:
@@ -16,6 +16,7 @@ import {
|
||||
import { useI18n } from '@/i18n'
|
||||
import { openExternalLink } from '@/lib/external-link'
|
||||
import { Check, ExternalLink, QrCode, Save, X } from '@/lib/icons'
|
||||
import { isSubmitEnter } from '@/lib/ime'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
import { CREDENTIAL_CONTROL_CLASS } from '../settings/credential-key-ui'
|
||||
@@ -326,7 +327,7 @@ export function TelegramQrSetup({ onApplied, platform, scopeProfile }: TelegramQ
|
||||
className={CREDENTIAL_CONTROL_CLASS}
|
||||
onChange={event => setNewAllowedId(event.target.value)}
|
||||
onKeyDown={event => {
|
||||
if (event.key === 'Enter') {
|
||||
if (isSubmitEnter(event)) {
|
||||
event.preventDefault()
|
||||
addAllowedId()
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import { Tip } from '@/components/ui/tooltip'
|
||||
import { useI18n } from '@/i18n'
|
||||
import { triggerHaptic } from '@/lib/haptics'
|
||||
import { CircleLetterA, Loader2, MessageQuestion } from '@/lib/icons'
|
||||
import { isSubmitEnter } from '@/lib/ime'
|
||||
import { visibleClarifyCard } from '@/lib/keybinds/composer-focus-keys'
|
||||
import { cn } from '@/lib/utils'
|
||||
import {
|
||||
@@ -592,11 +593,7 @@ function ClarifyToolSinglePending({
|
||||
|
||||
const handleTextareaKey = useCallback(
|
||||
(event: KeyboardEvent<HTMLTextAreaElement>) => {
|
||||
if (event.nativeEvent.isComposing) {
|
||||
return
|
||||
}
|
||||
|
||||
if (event.key === 'Enter' && !event.shiftKey) {
|
||||
if (isSubmitEnter(event) && !event.shiftKey) {
|
||||
event.preventDefault()
|
||||
submitAnswer()
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
DropdownMenuTrigger,
|
||||
host,
|
||||
Input,
|
||||
isSubmitEnter,
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
@@ -160,7 +161,7 @@ function BoardNameField({
|
||||
<Input
|
||||
autoFocus
|
||||
onChange={event => onChange(event.target.value)}
|
||||
onKeyDown={event => event.key === 'Enter' && onEnter()}
|
||||
onKeyDown={event => isSubmitEnter(event) && onEnter()}
|
||||
placeholder={k.boardNamePlaceholder}
|
||||
value={value}
|
||||
/>
|
||||
|
||||
@@ -33,6 +33,7 @@ import {
|
||||
formatModifierToken,
|
||||
host,
|
||||
Input,
|
||||
isSubmitEnter,
|
||||
Loader,
|
||||
SearchField,
|
||||
Select,
|
||||
@@ -79,7 +80,6 @@ import {
|
||||
} from './api'
|
||||
import { BoardSwitcher } from './board-switcher'
|
||||
import { TaskDrawer } from './drawer'
|
||||
import { shouldSubmitOnEnter } from './ime-enter'
|
||||
import { EMPTY_OVERRIDE, ModelOverrideField, overrideCreateFields, type TaskModelOverride } from './model-override'
|
||||
import { OrchestrationPanel } from './orchestration'
|
||||
import { columnMeta, type KanbanBoard, type KanbanTask, type TaskEstimate } from './types'
|
||||
@@ -684,7 +684,7 @@ function NewTaskDialog({
|
||||
autoFocus
|
||||
onChange={event => setTitle(event.target.value)}
|
||||
onKeyDown={event => {
|
||||
if (shouldSubmitOnEnter(event)) {
|
||||
if (isSubmitEnter(event)) {
|
||||
event.preventDefault()
|
||||
void submit()
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
DropdownMenuTrigger,
|
||||
ErrorState,
|
||||
host,
|
||||
isSubmitEnter,
|
||||
Loader,
|
||||
LogView,
|
||||
Textarea,
|
||||
@@ -45,7 +46,6 @@ import {
|
||||
taskKey,
|
||||
uploadAttachment
|
||||
} from './api'
|
||||
import { shouldSubmitOnEnter } from './ime-enter'
|
||||
import { ModelOverrideField, overridePatch } from './model-override'
|
||||
import {
|
||||
type Diagnostic,
|
||||
@@ -323,7 +323,7 @@ function CommentComposer({
|
||||
className={cn('field-sizing-content max-h-40 min-h-0 resize-none', running ? 'pr-[3.5rem]' : 'pr-[5rem]')}
|
||||
onChange={event => setBody(event.target.value)}
|
||||
onKeyDown={event => {
|
||||
if (shouldSubmitOnEnter(event) && !event.shiftKey) {
|
||||
if (isSubmitEnter(event) && !event.shiftKey) {
|
||||
event.preventDefault()
|
||||
submit()
|
||||
}
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { shouldSubmitOnEnter } from './ime-enter'
|
||||
|
||||
describe('shouldSubmitOnEnter', () => {
|
||||
it('submits a normal Enter keypress', () => {
|
||||
expect(shouldSubmitOnEnter({ key: 'Enter', nativeEvent: {} })).toBe(true)
|
||||
})
|
||||
|
||||
it('does not submit while IME composition is active', () => {
|
||||
expect(shouldSubmitOnEnter({ key: 'Enter', nativeEvent: { isComposing: true } })).toBe(false)
|
||||
})
|
||||
|
||||
it('does not submit Chromium composition-boundary keyCode 229', () => {
|
||||
expect(shouldSubmitOnEnter({ key: 'Enter', nativeEvent: { keyCode: 229 } })).toBe(false)
|
||||
})
|
||||
|
||||
it('does not submit non-Enter keys', () => {
|
||||
expect(shouldSubmitOnEnter({ key: 'Escape', nativeEvent: {} })).toBe(false)
|
||||
})
|
||||
})
|
||||
@@ -1,16 +0,0 @@
|
||||
export interface ImeKeyEvent {
|
||||
key: string
|
||||
nativeEvent: {
|
||||
isComposing?: boolean
|
||||
keyCode?: number
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enter confirms an IME conversion before it should act as a submit shortcut.
|
||||
* Chromium can report the legacy 229 keyCode around composition boundaries,
|
||||
* so keep that fallback in addition to the standard isComposing signal.
|
||||
*/
|
||||
export function shouldSubmitOnEnter(event: ImeKeyEvent): boolean {
|
||||
return event.key === 'Enter' && !event.nativeEvent.isComposing && event.nativeEvent.keyCode !== 229
|
||||
}
|
||||
@@ -1683,6 +1683,10 @@ export { triggerHaptic as haptic } from '@/lib/haptics'
|
||||
export type { HermesOpenTarget } from '@/lib/hermes-open-target'
|
||||
/** The app's lucide icon set (RefreshCw, LayoutDashboard, Activity, …). */
|
||||
export * as icons from '@/lib/icons'
|
||||
/** IME-aware Enter: true only for a real submit Enter, never a CJK composition
|
||||
* commit (`isComposing` or the legacy keyCode 229). Use it on every plugin
|
||||
* text field whose bare Enter performs an action. */
|
||||
export { isSubmitEnter } from '@/lib/ime'
|
||||
export { type KeybindContribution, KEYBINDS_AREA } from '@/lib/keybinds/actions'
|
||||
export { formatModifierToken } from '@/lib/keybinds/combo'
|
||||
/** A `Map` with a ceiling, for the module-level caches a plugin keeps across
|
||||
|
||||
Reference in New Issue
Block a user