mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-23 18:17:44 +02:00
State management - Pending task / live timeline are now Query-cache single source; Zustand mirror removed (fixes duplicate assistant render caused by the invalidate→refetch race window) - WS subscriptions moved from ChatWindow to global useRealtimeSync so pending state survives minimize and refresh - New GET /chat/sessions/:id/pending-task to recover live state on mount - Drafts persisted per-session (was per-workspace) Unread tracking - Migration 040: chat_session.unread_since (event-driven; old chats stay clean — no mass backfill) - POST /chat/sessions/:id/read clears unread; broadcasts chat:session_read so other devices sync - New GET /chat/pending-tasks aggregate for the FAB - ChatFab: brand-color impulse animation while running, brand-dot badge of unread session count - ChatWindow auto-marks read when user is viewing the session Header redesign - Two independent dropdowns: agent (avatar + name + My/Others grouping) at the input bottom-left; session (title + agent avatar) in the header - ⊕ new-chat button replaces the old + and history buttons - Session dropdown lists all sessions across agents with avatars - Empty state: 3 clickable starter prompts that send immediately - Mention link renderer falls through to default span on null — fixes @member/@agent/@all silently disappearing app-wide - User messages render through Markdown - Enter submits in chat input only (with IME guard + codeBlock skip); bubble menu hidden in chat Misc - Partial index on agent_task_queue for fast pending-task lookup - 2 new storage keys added to clearWorkspaceStorage - useMarkChatSessionRead has onError rollback - chat.* namespace logs across store, mutations, components, realtime Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
37 lines
1.3 KiB
TypeScript
37 lines
1.3 KiB
TypeScript
import { Extension } from "@tiptap/core";
|
|
|
|
/**
|
|
* `onSubmit` must return true when it actually handled the event and false
|
|
* when there's no submit handler wired up. That lets us fall through to the
|
|
* default Enter behaviour — inserting a newline — when appropriate.
|
|
*
|
|
* `submitOnEnter` — when true, bare Enter also submits (chat-style). When
|
|
* false, only Mod-Enter submits and bare Enter keeps its default (newline).
|
|
*/
|
|
export function createSubmitExtension(
|
|
onSubmit: () => boolean,
|
|
{ submitOnEnter }: { submitOnEnter: boolean },
|
|
) {
|
|
return Extension.create({
|
|
name: "submitShortcut",
|
|
addKeyboardShortcuts() {
|
|
const shortcuts: Record<string, () => boolean> = {
|
|
"Mod-Enter": () => onSubmit(),
|
|
};
|
|
if (submitOnEnter) {
|
|
shortcuts.Enter = () => {
|
|
const editor = this.editor;
|
|
// IME guard — never submit while composing a multi-key input
|
|
// (Chinese pinyin, Japanese kana, etc). `view.composing` is set
|
|
// by ProseMirror between compositionstart and compositionend.
|
|
if (editor.view.composing) return false;
|
|
// Let Enter insert a newline inside a code block.
|
|
if (editor.isActive("codeBlock")) return false;
|
|
return onSubmit();
|
|
};
|
|
}
|
|
return shortcuts;
|
|
},
|
|
});
|
|
}
|