refactor(contextual): drop 'floating' branch from useAIChat and useChatStream

UIChatContext is now 'global' | 'project' only. Floating domain
signal, scope field, and onDomainSignal callback removed. ChatInputBox
no longer defines floating variant. TaskBriefChat migrated to contextual mode.
This commit is contained in:
Roberto
2026-05-15 18:40:31 +02:00
parent e6357b0d61
commit 42a457f973
4 changed files with 7 additions and 58 deletions

View File

@@ -9,7 +9,7 @@ export interface ChatInputBoxHandle {
focus: () => void;
}
type ChatInputBoxVariant = 'panel' | 'floating' | 'comment';
type ChatInputBoxVariant = 'panel' | 'comment';
interface ChatInputBoxProps {
cacheKey: string;
@@ -27,12 +27,6 @@ const VARIANT_STYLES = {
button: 'flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-primary text-primary-foreground shadow-sm transition-all hover:bg-primary/90 active:scale-95 disabled:opacity-40 disabled:cursor-not-allowed disabled:active:scale-100',
iconSize: 16,
},
floating: {
container: 'flex items-center gap-2 px-3 py-2.5',
textarea: 'flex-1 resize-none bg-transparent text-sm placeholder:text-muted-foreground/60 outline-none max-h-20 overflow-y-auto',
button: 'flex h-7 w-7 shrink-0 items-center justify-center rounded-xl bg-primary text-primary-foreground shadow-sm transition-all hover:bg-primary/90 active:scale-95 disabled:opacity-30 disabled:cursor-not-allowed',
iconSize: 14,
},
comment: {
container: 'flex items-center gap-2 px-3 py-2',
textarea: 'flex-1 resize-none bg-transparent text-sm placeholder:text-muted-foreground outline-none max-h-32 overflow-y-auto',
@@ -49,7 +43,7 @@ export const ChatInputBox = forwardRef<ChatInputBoxHandle, ChatInputBoxProps>(
const valueRef = useRef(value);
valueRef.current = value;
// Re-init when the cache key changes (context switches in FloatingChat).
// Re-init when the cache key changes (context switches).
const prevKeyRef = useRef(cacheKey);
useEffect(() => {
if (prevKeyRef.current !== cacheKey) {

View File

@@ -177,7 +177,7 @@ export function TaskBriefChat({ taskId, projectId, initialBriefing, onBriefingRe
message: trimmed,
conversationHistory,
sessionId,
mode: 'floating',
mode: 'contextual',
scope: { type: 'task', id: taskId },
briefMode: true,
briefingContext: briefingText || undefined,

View File

@@ -1,33 +1,16 @@
import { useState, useCallback, useRef, useEffect, useMemo } from 'react';
import { trpc } from '@/lib/trpc';
export type FloatingDomainSignal =
| 'tasks'
| 'notes'
| 'timelines'
| 'projects'
| {
type: 'task' | 'timeline' | 'project' | 'note' | 'node';
id?: string | null;
section?: 'task' | 'timeline' | 'note' | null;
};
// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------
/**
* Renderer-only context describing where the user is in the UI.
* Retained for call-site compatibility; mode/scope fields support v3 routing.
*/
export interface UIChatContext {
type: 'global' | 'project' | 'floating';
type: 'global' | 'project';
projectId?: string;
/** For floating mode — the entity scope to pass to the backend. */
scope?: {
type: 'task' | 'project' | 'note' | 'timeline';
id?: string;
};
}
export interface ChatMessage {
@@ -47,10 +30,6 @@ interface UseAIChatReturn {
cacheKey: string;
}
interface UseAIChatOptions {
onDomainSignal?: (domain: FloatingDomainSignal) => void;
}
interface CachedChatState {
messages: ChatMessage[];
/** Written by ChatInputBox; read on mount to restore draft. Not written by this hook. */
@@ -62,11 +41,7 @@ const chatSessionCache = new Map<string, CachedChatState>();
function getContextCacheKey(ctx: UIChatContext): string {
if (ctx.type === 'global') return 'global';
if (ctx.type === 'project') return `project:${ctx.projectId ?? ''}`;
// Floating chat should keep a single continuous session while the panel is open,
// even when route/section context changes due floating-domain navigation.
return 'floating';
return `project:${ctx.projectId ?? ''}`;
}
// ---------------------------------------------------------------------------
@@ -121,10 +96,10 @@ export function parseMutationsToEntityTags(mutations: unknown[] | undefined): st
// Hook
// ---------------------------------------------------------------------------
export function useAIChat(defaultContext: UIChatContext, options?: UseAIChatOptions): UseAIChatReturn {
export function useAIChat(defaultContext: UIChatContext): UseAIChatReturn {
const contextCacheKey = useMemo(
() => getContextCacheKey(defaultContext),
[defaultContext.type, defaultContext.projectId, defaultContext.scope?.type, defaultContext.scope?.id],
[defaultContext.type, defaultContext.projectId],
);
const [messages, setMessages] = useState<ChatMessage[]>(
@@ -148,9 +123,6 @@ export function useAIChat(defaultContext: UIChatContext, options?: UseAIChatOpti
messagesRef.current = messages;
const sessionIdRef = useRef(sessionId);
sessionIdRef.current = sessionId;
const onDomainSignalRef = useRef(options?.onDomainSignal);
onDomainSignalRef.current = options?.onDomainSignal;
// Keep local state aligned when the chat context changes in-place.
useEffect(() => {
const cached = chatSessionCache.get(contextCacheKey);
@@ -234,9 +206,6 @@ export function useAIChat(defaultContext: UIChatContext, options?: UseAIChatOpti
break;
}
case 'floating_domain':
onDomainSignalRef.current?.(event.domain);
break;
}
});
@@ -246,17 +215,12 @@ export function useAIChat(defaultContext: UIChatContext, options?: UseAIChatOpti
content: m.content,
}));
const isFloating = ctx.type === 'floating';
chatMutationRef.current.mutate(
{
requestId,
message: trimmed,
conversationHistory,
sessionId: sessionIdRef.current,
...(isFloating && ctx.scope
? { mode: 'floating' as const, scope: ctx.scope }
: {}),
},
{
onSuccess: (data) => {

View File

@@ -14,15 +14,12 @@ export interface UseChatStreamArgs {
onAssistantMessage: (msg: ChatMessage) => void;
/** Called when the request fails. */
onError: (msg: ChatMessage) => void;
/** Optional: legacy floating_domain pivot signal. Kept until M6 removes floating. */
onDomainSignal?: (domain: unknown) => void;
}
export function useChatStream({
sessionId,
onAssistantMessage,
onError,
onDomainSignal,
}: UseChatStreamArgs) {
const [isStreaming, setIsStreaming] = useState(false);
const [streamingContent, setStreamingContent] = useState('');
@@ -30,9 +27,6 @@ export function useChatStream({
const mutation = trpc.ai.chat.useMutation();
const mutationRef = useRef(mutation);
mutationRef.current = mutation;
const domainRef = useRef(onDomainSignal);
domainRef.current = onDomainSignal;
const send = useCallback(
(args: {
message: string;
@@ -67,9 +61,6 @@ export function useChatStream({
unsubscribe();
break;
}
case 'floating_domain':
domainRef.current?.(event.domain);
break;
}
});