Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/app/src/app/components/model-picker-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ export default function ModelPickerModal(props: ModelPickerModalProps) {
}

if (event.key === "Enter") {
if (event.isComposing || event.keyCode === 229) return;
const idx = activeIndex();
const opt = props.filteredOptions[idx];
if (!opt) return;
Expand Down
2 changes: 2 additions & 0 deletions packages/app/src/app/components/question-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ export default function QuestionModal(props: QuestionModalProps) {
e.preventDefault();
setFocusedOptionIndex((prev) => (prev - 1 + optionsCount) % optionsCount);
} else if (e.key === "Enter") {
if (e.isComposing || e.keyCode === 229) return;
e.preventDefault();
if (q.custom && document.activeElement?.tagName === "INPUT") {
handleNext();
Expand Down Expand Up @@ -195,6 +196,7 @@ export default function QuestionModal(props: QuestionModalProps) {
placeholder="Type your answer here..."
onKeyDown={(e) => {
if (e.key === "Enter") {
if (e.isComposing || e.keyCode === 229) return;
e.stopPropagation();
handleNext();
}
Expand Down
2 changes: 1 addition & 1 deletion packages/app/src/app/components/rename-session-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export default function RenameSessionModal(props: RenameSessionModalProps) {
placeholder={translate("session.rename_placeholder")}
class="bg-gray-3"
onKeyDown={(event) => {
if (event.key !== "Enter") return;
if (event.key !== "Enter" || event.isComposing || event.keyCode === 229) return;
event.preventDefault();
if (props.canSave) props.onSave();
}}
Expand Down
2 changes: 1 addition & 1 deletion packages/app/src/app/components/rename-workspace-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export default function RenameWorkspaceModal(props: RenameWorkspaceModalProps) {
placeholder={translate("workspace.rename_placeholder")}
class="bg-gray-3"
onKeyDown={(event) => {
if (event.key !== "Enter") return;
if (event.key !== "Enter" || event.isComposing || event.keyCode === 229) return;
event.preventDefault();
if (props.canSave) props.onSave();
}}
Expand Down
28 changes: 25 additions & 3 deletions packages/app/src/app/components/session/composer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,9 @@ export default function Composer(props: ComposerProps) {
let variantPickerRef: HTMLDivElement | undefined;
let mentionSearchRun = 0;
let suppressPromptSync = false;
// Track IME composition state so we can combine it with keyCode === 229 to
// reliably suppress Enter during CJK input across Chrome, Safari, and WebKit.
let imeComposing = false;
const [mentionIndex, setMentionIndex] = createSignal(0);
const [mentionQuery, setMentionQuery] = createSignal("");
const [mentionOpen, setMentionOpen] = createSignal(false);
Expand All @@ -355,6 +358,20 @@ export default function Composer(props: ComposerProps) {

onMount(() => {
queueMicrotask(() => focusEditorEnd());

// Bind composition events directly via addEventListener because SolidJS
// does not delegate compositionstart/compositionend — the camelCase JSX
// form (onCompositionStart) may silently fail to attach.
if (editorRef) {
editorRef.addEventListener("compositionstart", () => {
imeComposing = true;
});
editorRef.addEventListener("compositionend", () => {
requestAnimationFrame(() => {
imeComposing = false;
});
});
}
});

const mentionGroups = createMemo<MentionGroup[]>(() => {
Expand Down Expand Up @@ -832,12 +849,17 @@ export default function Composer(props: ComposerProps) {
emitDraftChange();
return;
}
if (event.key === "Enter" && event.isComposing) return;
// Block Enter while IME is composing. We check three signals:
// 1. event.isComposing — standard API (unreliable in some WebKit builds)
// 2. imeComposing — manual flag from compositionstart/end
// 3. event.keyCode === 229 — legacy but reliable IME indicator across all browsers
const imeActive = event.isComposing || imeComposing || event.keyCode === 229;
if (event.key === "Enter" && imeActive) return;

if (mentionOpen()) {
const options = mentionOptions();
const ctrl = event.ctrlKey && !event.metaKey && !event.altKey && !event.shiftKey;
if (event.key === "Enter" && !event.isComposing) {
if (event.key === "Enter" && !imeActive) {
event.preventDefault();
const active = options[mentionIndex()] ?? options[0];
if (active) insertMention(active);
Expand Down Expand Up @@ -873,7 +895,7 @@ export default function Composer(props: ComposerProps) {
if (slashOpen()) {
const options = slashFiltered();
const ctrl = event.ctrlKey && !event.metaKey && !event.altKey && !event.shiftKey;
if (event.key === "Enter" && !event.isComposing) {
if (event.key === "Enter" && !imeActive) {
event.preventDefault();
const active = options[slashIndex()] ?? options[0];
if (active) handleSlashSelect(active);
Expand Down
3 changes: 3 additions & 0 deletions packages/app/src/app/pages/dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -747,6 +747,7 @@ export default function DashboardView(props: DashboardViewProps) {
}}
onKeyDown={(event) => {
if (event.key !== "Enter" && event.key !== " ") return;
if (event.isComposing || event.keyCode === 229) return;
event.preventDefault();
expandWorkspace(workspace().id);
props.activateWorkspace(workspace().id);
Expand Down Expand Up @@ -889,6 +890,7 @@ export default function DashboardView(props: DashboardViewProps) {
onClick={() => openSessionFromList(workspace().id, session.id)}
onKeyDown={(event) => {
if (event.key !== "Enter" && event.key !== " ") return;
if (event.isComposing || event.keyCode === 229) return;
event.preventDefault();
openSessionFromList(workspace().id, session.id);
}}
Expand Down Expand Up @@ -937,6 +939,7 @@ export default function DashboardView(props: DashboardViewProps) {
onClick={() => openSessionFromList(workspace().id, session.id)}
onKeyDown={(event) => {
if (event.key !== "Enter" && event.key !== " ") return;
if (event.isComposing || event.keyCode === 229) return;
event.preventDefault();
openSessionFromList(workspace().id, session.id);
}}
Expand Down
1 change: 1 addition & 0 deletions packages/app/src/app/pages/onboarding.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ export default function OnboardingView(props: OnboardingViewProps) {
onInput={(e) => props.onSetAuthorizedDir(e.currentTarget.value)}
onKeyDown={(e) => {
if (e.key === "Enter") {
if (e.isComposing || e.keyCode === 229) return;
props.onAddAuthorizedDir();
}
}}
Expand Down
1 change: 1 addition & 0 deletions packages/app/src/app/pages/proto-v1-ux.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ const ProjectFolder = (props: { name: string; children: any }) => {
onClick={toggleExpanded}
onKeyDown={(event) => {
if (event.key !== "Enter" && event.key !== " ") return;
if (event.isComposing || event.keyCode === 229) return;
event.preventDefault();
toggleExpanded();
}}
Expand Down
3 changes: 3 additions & 0 deletions packages/app/src/app/pages/session.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1138,6 +1138,7 @@ export default function SessionView(props: SessionViewProps) {
}}
onKeyDown={(event) => {
if (event.key !== "Enter" && event.key !== " ") return;
if (event.isComposing || event.keyCode === 229) return;
event.preventDefault();
expandWorkspace(workspace().id);
props.activateWorkspace(workspace().id);
Expand Down Expand Up @@ -1284,6 +1285,7 @@ export default function SessionView(props: SessionViewProps) {
onClick={() => openSessionFromList(workspace().id, session.id)}
onKeyDown={(event) => {
if (event.key !== "Enter" && event.key !== " ") return;
if (event.isComposing || event.keyCode === 229) return;
event.preventDefault();
openSessionFromList(workspace().id, session.id);
}}
Expand Down Expand Up @@ -1334,6 +1336,7 @@ export default function SessionView(props: SessionViewProps) {
onClick={() => openSessionFromList(workspace().id, session.id)}
onKeyDown={(event) => {
if (event.key !== "Enter" && event.key !== " ") return;
if (event.isComposing || event.keyCode === 229) return;
event.preventDefault();
openSessionFromList(workspace().id, session.id);
}}
Expand Down
1 change: 1 addition & 0 deletions packages/app/src/app/pages/skills.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ export default function SkillsView(props: SkillsViewProps) {
onClick={() => void openSkill(skill)}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
if (e.isComposing || e.keyCode === 229) return;
e.preventDefault();
void openSkill(skill);
}
Expand Down
Loading