evOo-local/packages/core/src/composer-mention-picker.ts
cynarAI 6c7eed7580 Initial clean evOo-local from f00577b
Fresh history cut from cynarAI/evoo antigravity-handoff/project-timeline
(Voice First + project timeline). Brand docs/titles as evOo; keep @rakazo/*
packages as tech debt. Excludes node_modules, caches, secrets, voice blobs.
2026-09-20 17:15:16 +02:00

65 lines
2.0 KiB
TypeScript

/** Keyboard actions for the composer `@` mention picker. */
export type MentionPickerKeyAction =
| { type: "complete"; index: number }
| { type: "move"; index: number }
| { type: "dismiss" }
| { type: "send" }
| { type: "none" };
/** Wrap highlight within `[0, count)`. Returns `0` when the list is empty. */
export function wrapMentionHighlightIndex(index: number, count: number): number {
if (count <= 0) return 0;
return ((index % count) + count) % count;
}
/** Keep a stored highlight valid after the option list shrinks or grows. */
export function clampMentionHighlightIndex(index: number, count: number): number {
if (count <= 0) return 0;
if (index < 0) return 0;
if (index >= count) return count - 1;
return index;
}
/**
* Resolve composer key handling while (or after) the mention picker is open.
* When `optionCount` is 0 the picker is closed or empty: Enter sends (unless Shift).
* While IME composition is active, returns `none` so Enter confirms text instead.
*/
export function resolveMentionPickerKey(input: {
key: string;
shiftKey?: boolean;
isComposing?: boolean;
optionCount: number;
highlightedIndex: number;
}): MentionPickerKeyAction {
if (input.isComposing) return { type: "none" };
const { key, shiftKey = false, optionCount } = input;
const highlightedIndex = clampMentionHighlightIndex(input.highlightedIndex, optionCount);
if (optionCount > 0) {
if (key === "ArrowDown") {
return { type: "move", index: wrapMentionHighlightIndex(highlightedIndex + 1, optionCount) };
}
if (key === "ArrowUp") {
return { type: "move", index: wrapMentionHighlightIndex(highlightedIndex - 1, optionCount) };
}
if (key === "Enter" && !shiftKey) {
return { type: "complete", index: highlightedIndex };
}
if (key === "Tab") {
return { type: "complete", index: highlightedIndex };
}
if (key === "Escape") {
return { type: "dismiss" };
}
return { type: "none" };
}
if (key === "Enter" && !shiftKey) {
return { type: "send" };
}
return { type: "none" };
}