Add Contextual chat
This commit is contained in:
2
adiuvAI
2
adiuvAI
Submodule adiuvAI updated: 6aa7cb3d22...c1b1b289c1
@@ -0,0 +1,967 @@
|
||||
# Floating Chat Deprecation Sweep Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Delete every floating-chat code path across the adiuvAI Electron app — components, context, hooks, DOM attributes, stream branches, main-process methods, and shared-type artefacts — in five clean commits.
|
||||
|
||||
**Architecture:** The contextual sidebar (M4) fully replaces floating chat; floating is dead code with no live consumers. Each task targets one layer: (1) renderer components/context/hooks, (2) DOM data-attributes, (3) renderer hook logic, (4) main-process IPC/orchestrator, (5) store/localStorage keys sweep.
|
||||
|
||||
**Tech Stack:** TypeScript, React 19, Electron (main + preload + renderer), tRPC v11, Zod, electron-store.
|
||||
|
||||
---
|
||||
|
||||
## Pre-flight: baseline tsc check
|
||||
|
||||
- [ ] **Run tsc before touching anything**
|
||||
|
||||
```bash
|
||||
cd /c/Users/PC-Roby/Documents/_adiuvai_workspace/adiuvAI
|
||||
source ~/.nvm/nvm.sh && npx tsc --noEmit 2>&1 | tail -20
|
||||
```
|
||||
|
||||
Record the number of pre-existing errors (expected: 0). Any errors here are pre-existing and not your fault.
|
||||
|
||||
---
|
||||
|
||||
## Task M6.1 — Delete FloatingChat component, context, hook + AppShell cleanup
|
||||
|
||||
**Files:**
|
||||
- Delete: `src/renderer/components/ai/FloatingChat.tsx`
|
||||
- Delete: `src/renderer/context/FloatingChatContext.tsx`
|
||||
- Delete: `src/renderer/hooks/useDoubleClickAI.ts`
|
||||
- Modify: `src/renderer/components/layout/AppShell.tsx`
|
||||
- Modify: `src/renderer/components/projects/ProjectDetail.tsx`
|
||||
- Modify: `src/renderer/components/timeline/TimelineGanttView.tsx`
|
||||
- Modify: `src/renderer/routes/notes.$noteId.tsx`
|
||||
- Modify: `src/renderer/routes/tasks.tsx`
|
||||
|
||||
### Context
|
||||
|
||||
Six files import from `FloatingChatContext` or `FloatingChat`. After deleting the three source files, all six must be cleaned up.
|
||||
|
||||
`ProjectDetail.tsx`, `TimelineGanttView.tsx`, `notes.$noteId.tsx`, and `tasks.tsx` import `useFloatingChat()` and call `registerSection`/`unregisterSection`. These are pure floating-chat wiring — they register DOM regions with the floating panel so it could anchor itself. With floating chat gone, these calls become dead code and must be removed entirely (no replacement needed; the contextual sidebar uses `useContextualScope` for scope awareness, which is already imported in tasks.tsx and ProjectDetail.tsx).
|
||||
|
||||
`AppShell.tsx` wraps the tree in `<FloatingChatProvider>` (line 151) and calls `useDoubleClickAI()` inside `AppShellInner` (line 166).
|
||||
|
||||
### Steps
|
||||
|
||||
- [ ] **Step 1: Confirm all consumers before deletion**
|
||||
|
||||
```bash
|
||||
grep -rn "FloatingChat\|useFloatingChat\|FloatingChatProvider\|useDoubleClickAI" src/renderer/
|
||||
```
|
||||
|
||||
Expected output matches: `AppShell.tsx`, `FloatingChat.tsx`, `FloatingChatContext.tsx`, `useDoubleClickAI.ts`, `ProjectDetail.tsx`, `TimelineGanttView.tsx`, `notes.$noteId.tsx`, `tasks.tsx`. If any unexpected file appears, stop and report before proceeding.
|
||||
|
||||
- [ ] **Step 2: Delete the three source files**
|
||||
|
||||
```bash
|
||||
git rm src/renderer/components/ai/FloatingChat.tsx
|
||||
git rm src/renderer/context/FloatingChatContext.tsx
|
||||
git rm src/renderer/hooks/useDoubleClickAI.ts
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Clean AppShell.tsx**
|
||||
|
||||
Open `src/renderer/components/layout/AppShell.tsx`.
|
||||
|
||||
Remove line 28:
|
||||
```ts
|
||||
import { useDoubleClickAI } from '@/hooks/useDoubleClickAI';
|
||||
```
|
||||
|
||||
Remove lines 70–71:
|
||||
```ts
|
||||
import { FloatingChatPortal } from '@/components/ai/FloatingChat';
|
||||
import { FloatingChatProvider } from '@/context/FloatingChatContext';
|
||||
```
|
||||
|
||||
In `AppShell` (around line 149–163), change:
|
||||
```tsx
|
||||
export function AppShell({ children }: AppShellProps) {
|
||||
return (
|
||||
<FloatingChatProvider>
|
||||
<ExpandedClientsProvider>
|
||||
<TaskBriefingProvider>
|
||||
<HeaderProvider>
|
||||
<div className="flex w-full h-full">
|
||||
<AppShellInner>{children}</AppShellInner>
|
||||
</div>
|
||||
</HeaderProvider>
|
||||
</TaskBriefingProvider>
|
||||
</ExpandedClientsProvider>
|
||||
</FloatingChatProvider>
|
||||
);
|
||||
}
|
||||
```
|
||||
to:
|
||||
```tsx
|
||||
export function AppShell({ children }: AppShellProps) {
|
||||
return (
|
||||
<ExpandedClientsProvider>
|
||||
<TaskBriefingProvider>
|
||||
<HeaderProvider>
|
||||
<div className="flex w-full h-full">
|
||||
<AppShellInner>{children}</AppShellInner>
|
||||
</div>
|
||||
</HeaderProvider>
|
||||
</TaskBriefingProvider>
|
||||
</ExpandedClientsProvider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
In `AppShellInner` (around line 166), remove:
|
||||
```ts
|
||||
useDoubleClickAI();
|
||||
```
|
||||
|
||||
Search for `<FloatingChatPortal />` in the file (around line 288) and delete that line.
|
||||
|
||||
- [ ] **Step 4: Clean ProjectDetail.tsx**
|
||||
|
||||
Open `src/renderer/components/projects/ProjectDetail.tsx`.
|
||||
|
||||
Remove line 24:
|
||||
```ts
|
||||
import { useFloatingChat } from '@/context/FloatingChatContext';
|
||||
```
|
||||
|
||||
Remove line 63:
|
||||
```ts
|
||||
const { registerSection, unregisterSection } = useFloatingChat();
|
||||
```
|
||||
|
||||
Remove the `useEffect` block that calls `registerSection`/`unregisterSection` (lines ~91–101):
|
||||
```ts
|
||||
useEffect(() => {
|
||||
if (isLoading || !project) return;
|
||||
registerSection({ id: 'project-summary', label: 'Project Summary', ref: summaryRef, projectId });
|
||||
registerSection({ id: 'project-tasks', label: 'Tasks', ref: tasksRef, projectId });
|
||||
registerSection({ id: 'project-notes', label: 'Notes', ref: notesRef, projectId });
|
||||
return () => {
|
||||
unregisterSection('project-summary');
|
||||
unregisterSection('project-tasks');
|
||||
unregisterSection('project-notes');
|
||||
};
|
||||
}, [projectId, isLoading, project, registerSection, unregisterSection]);
|
||||
```
|
||||
|
||||
- [ ] **Step 5: Clean TimelineGanttView.tsx**
|
||||
|
||||
Open `src/renderer/components/timeline/TimelineGanttView.tsx`.
|
||||
|
||||
Remove line 31:
|
||||
```ts
|
||||
import { useFloatingChat } from '@/context/FloatingChatContext';
|
||||
```
|
||||
|
||||
Remove line 95:
|
||||
```ts
|
||||
const { registerSection, unregisterSection } = useFloatingChat();
|
||||
```
|
||||
|
||||
Remove the `useEffect` that calls `registerSection`/`unregisterSection` (lines ~107–110):
|
||||
```ts
|
||||
useEffect(() => {
|
||||
registerSection({ id: sectionId, label: sectionLabel, ref: sectionRef, projectId });
|
||||
return () => unregisterSection(sectionId);
|
||||
}, [sectionId, sectionLabel, projectId, registerSection, unregisterSection]);
|
||||
```
|
||||
|
||||
- [ ] **Step 6: Clean notes.$noteId.tsx**
|
||||
|
||||
Open `src/renderer/routes/notes.$noteId.tsx`.
|
||||
|
||||
Remove line 29:
|
||||
```ts
|
||||
import { useFloatingChat } from '@/context/FloatingChatContext';
|
||||
```
|
||||
|
||||
Remove line 117:
|
||||
```ts
|
||||
const { registerSection, unregisterSection } = useFloatingChat();
|
||||
```
|
||||
|
||||
Remove the `useEffect` that calls `registerSection`/`unregisterSection` (lines ~119–128):
|
||||
```ts
|
||||
useEffect(() => {
|
||||
registerSection({
|
||||
id: 'note-editor',
|
||||
label: 'Note Editor',
|
||||
ref: editorRef,
|
||||
projectId: noteProjectId,
|
||||
anchorMode: 'right-margin',
|
||||
});
|
||||
return () => unregisterSection('note-editor');
|
||||
}, [noteId, noteProjectId, registerSection, unregisterSection]);
|
||||
```
|
||||
|
||||
- [ ] **Step 7: Clean tasks.tsx**
|
||||
|
||||
Open `src/renderer/routes/tasks.tsx`.
|
||||
|
||||
Remove line 5:
|
||||
```ts
|
||||
import { useFloatingChat } from '@/context/FloatingChatContext';
|
||||
```
|
||||
|
||||
Remove line 18:
|
||||
```ts
|
||||
const { registerSection, unregisterSection } = useFloatingChat();
|
||||
```
|
||||
|
||||
Remove the `useEffect` that calls `registerSection`/`unregisterSection` (lines ~20–27):
|
||||
```ts
|
||||
useEffect(() => {
|
||||
registerSection({ id: 'tasks-overview', label: 'Tasks Overview', ref: overviewRef });
|
||||
registerSection({ id: 'tasks-list', label: 'Task List', ref: listRef });
|
||||
return () => {
|
||||
unregisterSection('tasks-overview');
|
||||
unregisterSection('tasks-list');
|
||||
};
|
||||
}, [registerSection, unregisterSection]);
|
||||
```
|
||||
|
||||
Also remove the `overviewRef` and `listRef` ref declarations if they are now unused (check whether they are still referenced by the JSX `ref=` attributes after M6.2 strips `data-ai-section`):
|
||||
```ts
|
||||
const overviewRef = useRef<HTMLDivElement>(null);
|
||||
const listRef = useRef<HTMLDivElement>(null);
|
||||
```
|
||||
|
||||
Note: After M6.2 removes `data-ai-section` attributes, `ref={overviewRef}` and `ref={listRef}` in the JSX will also be gone, making the refs fully dead. Remove them here (or in M6.2 — just do it in whichever task you're in when you notice them).
|
||||
|
||||
- [ ] **Step 8: Type check**
|
||||
|
||||
```bash
|
||||
source ~/.nvm/nvm.sh && npx tsc --noEmit 2>&1 | grep -i "error" | head -30
|
||||
```
|
||||
|
||||
Expected: zero errors related to FloatingChat, FloatingChatContext, useDoubleClickAI. If any appear, locate the offending file and remove the import/usage.
|
||||
|
||||
- [ ] **Step 9: Commit**
|
||||
|
||||
```bash
|
||||
git add src/renderer/components/layout/AppShell.tsx \
|
||||
src/renderer/components/projects/ProjectDetail.tsx \
|
||||
src/renderer/components/timeline/TimelineGanttView.tsx \
|
||||
src/renderer/routes/notes.\$noteId.tsx \
|
||||
src/renderer/routes/tasks.tsx
|
||||
git commit -m "$(cat <<'EOF'
|
||||
refactor(contextual): delete FloatingChat, FloatingChatContext, useDoubleClickAI
|
||||
|
||||
Replaced by ContextualChatProvider + AdiuvaTriggerButton in M4.
|
||||
Pre-1.0 clean removal — no deprecation period.
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task M6.2 — Strip all `data-ai-section` attributes
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/renderer/components/projects/ProjectDetail.tsx`
|
||||
- Modify: `src/renderer/components/timeline/TimelineGanttView.tsx`
|
||||
- Modify: `src/renderer/routes/notes.$noteId.tsx`
|
||||
- Modify: `src/renderer/routes/tasks.tsx`
|
||||
|
||||
### Context
|
||||
|
||||
`data-ai-section` was used by `useDoubleClickAI` to walk the DOM and find the nearest section anchor to open floating chat. Now that floating chat is gone, these attributes are purely dead markup. The contextual sidebar uses `scope` payloads, not DOM attributes.
|
||||
|
||||
Current occurrences (7 total):
|
||||
- `ProjectDetail.tsx:538` — `data-ai-section="project-summary"`
|
||||
- `ProjectDetail.tsx:620` — `data-ai-section="project-tasks"`
|
||||
- `ProjectDetail.tsx:631` — `data-ai-section="project-notes"`
|
||||
- `TimelineGanttView.tsx:239` — `data-ai-section={sectionId}`
|
||||
- `notes.$noteId.tsx:291` — `data-ai-section="note-editor"`
|
||||
- `tasks.tsx:42` — `data-ai-section="tasks-overview"`
|
||||
- `tasks.tsx:61` — `data-ai-section="tasks-list"`
|
||||
|
||||
### Steps
|
||||
|
||||
- [ ] **Step 1: List all occurrences**
|
||||
|
||||
```bash
|
||||
grep -rn "data-ai-section" src/renderer/
|
||||
```
|
||||
|
||||
Confirm you see exactly the 7 occurrences listed above. If more appear, handle them too.
|
||||
|
||||
- [ ] **Step 2: Remove from ProjectDetail.tsx**
|
||||
|
||||
For each JSX element with a `data-ai-section` prop, delete only that prop (keep `className` and all others). Example:
|
||||
|
||||
Find:
|
||||
```tsx
|
||||
<div
|
||||
ref={summaryRef}
|
||||
data-ai-section="project-summary"
|
||||
className="..."
|
||||
>
|
||||
```
|
||||
Change to:
|
||||
```tsx
|
||||
<div
|
||||
ref={summaryRef}
|
||||
className="..."
|
||||
>
|
||||
```
|
||||
|
||||
Repeat for `project-tasks` and `project-notes`.
|
||||
|
||||
- [ ] **Step 3: Remove from TimelineGanttView.tsx**
|
||||
|
||||
Find the element at line ~239 and remove only the `data-ai-section={sectionId}` prop. Keep `ref={sectionRef}` and any other props.
|
||||
|
||||
Also check: after M6.1 removed `registerSection`/`unregisterSection`, there may now be `sectionRef`, `sectionId`, `sectionLabel` variables declared but unused. Remove any that are now dead. Check with:
|
||||
|
||||
```bash
|
||||
grep -n "sectionRef\|sectionId\|sectionLabel" src/renderer/components/timeline/TimelineGanttView.tsx
|
||||
```
|
||||
|
||||
If these variables are only used by the removed `useEffect` and the now-removed `data-ai-section` attribute, delete their declarations too.
|
||||
|
||||
- [ ] **Step 4: Remove from notes.$noteId.tsx**
|
||||
|
||||
Find the `<ScrollArea>` at line ~291:
|
||||
```tsx
|
||||
<ScrollArea ref={editorRef} data-ai-section="note-editor" className="flex-1 min-h-0">
|
||||
```
|
||||
Change to:
|
||||
```tsx
|
||||
<ScrollArea ref={editorRef} className="flex-1 min-h-0">
|
||||
```
|
||||
|
||||
Note: `editorRef` is still used by the existing scroll area ref, so keep it.
|
||||
|
||||
- [ ] **Step 5: Remove from tasks.tsx**
|
||||
|
||||
Remove `data-ai-section="tasks-overview"` from the div at line ~42 and `data-ai-section="tasks-list"` from the div at line ~61. Also remove `ref={overviewRef}` and `ref={listRef}` from those elements if the refs were removed in M6.1.
|
||||
|
||||
If `overviewRef` and `listRef` are now unused (no JSX `ref=` and no other use), delete their `useRef` declarations too.
|
||||
|
||||
- [ ] **Step 6: Verify clean**
|
||||
|
||||
```bash
|
||||
grep -rn "data-ai-section" src/renderer/
|
||||
echo "exit=$?"
|
||||
```
|
||||
|
||||
Expected: no output, `exit=1` (grep found nothing).
|
||||
|
||||
- [ ] **Step 7: Type check + commit**
|
||||
|
||||
```bash
|
||||
source ~/.nvm/nvm.sh && npx tsc --noEmit 2>&1 | grep -i "error" | head -20
|
||||
```
|
||||
|
||||
Expected: zero errors.
|
||||
|
||||
```bash
|
||||
git add src/renderer/components/projects/ProjectDetail.tsx \
|
||||
src/renderer/components/timeline/TimelineGanttView.tsx \
|
||||
src/renderer/routes/notes.\$noteId.tsx \
|
||||
src/renderer/routes/tasks.tsx
|
||||
git commit -m "$(cat <<'EOF'
|
||||
refactor(contextual): strip all data-ai-section attributes
|
||||
|
||||
Section-anchoring obsolete now that there is no floating chat.
|
||||
The contextual sidebar uses scope payload, not DOM attributes.
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task M6.3 — Drop `'floating'` from `useAIChat` + `ChatInputBox` + `useChatStream`
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/renderer/hooks/useAIChat.ts`
|
||||
- Modify: `src/renderer/hooks/useChatStream.ts`
|
||||
- Modify: `src/renderer/components/ai/ChatInputBox.tsx`
|
||||
- Modify: `src/renderer/components/brief/TaskBriefChat.tsx`
|
||||
|
||||
### Context
|
||||
|
||||
`useAIChat.ts` has `FloatingDomainSignal` type, `'floating'` in `UIChatContext.type` union, a `scope` field, a `'floating'` cache-key branch, `isFloating` logic in `handleSend`, `floating_domain` switch case, and `onDomainSignalRef`. All dead after M6.1 deleted `FloatingChat.tsx` which was the only consumer.
|
||||
|
||||
`useChatStream.ts` has a `floating_domain` case and the `onDomainSignal` option — kept during M2.1 explicitly "until M6 removes floating." Now is the time.
|
||||
|
||||
`ChatInputBox.tsx` has `'floating'` as a variant in `ChatInputBoxVariant` with its own style entry. This is a visual variant only — the type can be removed if nothing still passes `variant="floating"`. After `FloatingChat.tsx` is gone, nothing does.
|
||||
|
||||
`TaskBriefChat.tsx` passes `mode: 'floating'` to `chatMutation.mutate(...)`. After removing the floating mode from the tRPC schema (M6.4), this becomes a type error. Fix it here by removing `mode: 'floating'` and `scope` from the mutation call — task briefs will fall through to the default home orchestrator, or better: use `mode: 'contextual'` with the scope. Since task brief already passes `scope: { type: 'task', id: taskId }`, change `mode: 'floating'` to `mode: 'contextual'`.
|
||||
|
||||
`parseMutationsToEntityTags` and `TABLE_TO_ENTITY` in `useAIChat.ts` are still needed — `useChatStream.ts` imports `parseMutationsToEntityTags` from `useAIChat.ts`. Keep both exports.
|
||||
|
||||
### Steps
|
||||
|
||||
- [ ] **Step 1: Edit useAIChat.ts**
|
||||
|
||||
Open `src/renderer/hooks/useAIChat.ts`.
|
||||
|
||||
**Delete lines 4–13** (the `FloatingDomainSignal` type):
|
||||
```ts
|
||||
export type FloatingDomainSignal =
|
||||
| 'tasks'
|
||||
| 'notes'
|
||||
| 'timelines'
|
||||
| 'projects'
|
||||
| {
|
||||
type: 'task' | 'timeline' | 'project' | 'note' | 'node';
|
||||
id?: string | null;
|
||||
section?: 'task' | 'timeline' | 'note' | null;
|
||||
};
|
||||
```
|
||||
|
||||
**Replace the `UIChatContext` interface** (lines 23–31):
|
||||
```ts
|
||||
export interface UIChatContext {
|
||||
type: 'global' | 'project' | 'floating';
|
||||
projectId?: string;
|
||||
/** For floating mode — the entity scope to pass to the backend. */
|
||||
scope?: {
|
||||
type: 'task' | 'project' | 'note' | 'timeline';
|
||||
id?: string;
|
||||
};
|
||||
}
|
||||
```
|
||||
with:
|
||||
```ts
|
||||
export interface UIChatContext {
|
||||
type: 'global' | 'project';
|
||||
projectId?: string;
|
||||
}
|
||||
```
|
||||
|
||||
**Delete the `UseAIChatOptions` interface** (lines 50–52):
|
||||
```ts
|
||||
interface UseAIChatOptions {
|
||||
onDomainSignal?: (domain: FloatingDomainSignal) => void;
|
||||
}
|
||||
```
|
||||
|
||||
**Update the `useAIChat` function signature** — remove the `options` parameter:
|
||||
```ts
|
||||
export function useAIChat(defaultContext: UIChatContext, options?: UseAIChatOptions): UseAIChatReturn {
|
||||
```
|
||||
becomes:
|
||||
```ts
|
||||
export function useAIChat(defaultContext: UIChatContext): UseAIChatReturn {
|
||||
```
|
||||
|
||||
**Update `getContextCacheKey`** — replace the entire function body:
|
||||
```ts
|
||||
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';
|
||||
}
|
||||
```
|
||||
with:
|
||||
```ts
|
||||
function getContextCacheKey(ctx: UIChatContext): string {
|
||||
if (ctx.type === 'global') return 'global';
|
||||
return `project:${ctx.projectId ?? ''}`;
|
||||
}
|
||||
```
|
||||
|
||||
**Update the `useMemo` for `contextCacheKey`** — remove `defaultContext.scope?.type` and `defaultContext.scope?.id` from deps:
|
||||
```ts
|
||||
const contextCacheKey = useMemo(
|
||||
() => getContextCacheKey(defaultContext),
|
||||
[defaultContext.type, defaultContext.projectId, defaultContext.scope?.type, defaultContext.scope?.id],
|
||||
);
|
||||
```
|
||||
becomes:
|
||||
```ts
|
||||
const contextCacheKey = useMemo(
|
||||
() => getContextCacheKey(defaultContext),
|
||||
[defaultContext.type, defaultContext.projectId],
|
||||
);
|
||||
```
|
||||
|
||||
**Remove `onDomainSignalRef`** (lines 151–152):
|
||||
```ts
|
||||
const onDomainSignalRef = useRef(options?.onDomainSignal);
|
||||
onDomainSignalRef.current = options?.onDomainSignal;
|
||||
```
|
||||
|
||||
**Remove the `floating_domain` case** from the `switch (event.type)` block (lines 237–239):
|
||||
```ts
|
||||
case 'floating_domain':
|
||||
onDomainSignalRef.current?.(event.domain);
|
||||
break;
|
||||
```
|
||||
|
||||
**Remove `isFloating` and the conditional spread** in `handleSend` (lines 249–259):
|
||||
```ts
|
||||
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 }
|
||||
: {}),
|
||||
},
|
||||
```
|
||||
becomes:
|
||||
```ts
|
||||
chatMutationRef.current.mutate(
|
||||
{
|
||||
requestId,
|
||||
message: trimmed,
|
||||
conversationHistory,
|
||||
sessionId: sessionIdRef.current,
|
||||
},
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Edit useChatStream.ts**
|
||||
|
||||
Open `src/renderer/hooks/useChatStream.ts`.
|
||||
|
||||
Remove `onDomainSignal` from `UseChatStreamArgs` interface (lines 17–18):
|
||||
```ts
|
||||
/** Optional: legacy floating_domain pivot signal. Kept until M6 removes floating. */
|
||||
onDomainSignal?: (domain: unknown) => void;
|
||||
```
|
||||
|
||||
Remove `onDomainSignal` from the destructuring of `useChatStream`'s argument (line 25):
|
||||
```ts
|
||||
onDomainSignal,
|
||||
```
|
||||
|
||||
Remove `domainRef` declarations (lines 33–34):
|
||||
```ts
|
||||
const domainRef = useRef(onDomainSignal);
|
||||
domainRef.current = onDomainSignal;
|
||||
```
|
||||
|
||||
Remove the `floating_domain` case from the switch block (lines 70–72):
|
||||
```ts
|
||||
case 'floating_domain':
|
||||
domainRef.current?.(event.domain);
|
||||
break;
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Edit ChatInputBox.tsx**
|
||||
|
||||
Open `src/renderer/components/ai/ChatInputBox.tsx`.
|
||||
|
||||
The `'floating'` variant in `ChatInputBoxVariant` (line 12) and its entry in `VARIANT_STYLES` (lines 30–35) should be removed since no live code passes `variant="floating"` any more.
|
||||
|
||||
Change:
|
||||
```ts
|
||||
type ChatInputBoxVariant = 'panel' | 'floating' | 'comment';
|
||||
```
|
||||
to:
|
||||
```ts
|
||||
type ChatInputBoxVariant = 'panel' | 'comment';
|
||||
```
|
||||
|
||||
Remove the `floating` entry from `VARIANT_STYLES` (lines 30–35):
|
||||
```ts
|
||||
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,
|
||||
},
|
||||
```
|
||||
|
||||
Also remove the comment about FloatingChat on line 52:
|
||||
```ts
|
||||
// Re-init when the cache key changes (context switches in FloatingChat).
|
||||
```
|
||||
Replace with a neutral comment or just remove the comment line.
|
||||
|
||||
- [ ] **Step 4: Fix TaskBriefChat.tsx**
|
||||
|
||||
Open `src/renderer/components/brief/TaskBriefChat.tsx`.
|
||||
|
||||
At line ~174–184, change:
|
||||
```ts
|
||||
chatMutation.mutate(
|
||||
{
|
||||
requestId,
|
||||
message: trimmed,
|
||||
conversationHistory,
|
||||
sessionId,
|
||||
mode: 'floating',
|
||||
scope: { type: 'task', id: taskId },
|
||||
briefMode: true,
|
||||
briefingContext: briefingText || undefined,
|
||||
},
|
||||
```
|
||||
to:
|
||||
```ts
|
||||
chatMutation.mutate(
|
||||
{
|
||||
requestId,
|
||||
message: trimmed,
|
||||
conversationHistory,
|
||||
sessionId,
|
||||
mode: 'contextual',
|
||||
scope: { type: 'task', id: taskId },
|
||||
briefMode: true,
|
||||
briefingContext: briefingText || undefined,
|
||||
},
|
||||
```
|
||||
|
||||
- [ ] **Step 5: Type check**
|
||||
|
||||
```bash
|
||||
source ~/.nvm/nvm.sh && npx tsc --noEmit 2>&1 | grep -i "error" | head -30
|
||||
```
|
||||
|
||||
Expected: zero errors. If tsc reports that `'floating'` is no longer valid for some type (from M6.4 not yet done), note it — the router schema still allows `'floating'` until M6.4. If tsc reports that `useAIChat` call sites are broken, check them:
|
||||
|
||||
```bash
|
||||
grep -rn "useAIChat" src/renderer/
|
||||
```
|
||||
|
||||
All call sites should be passing only `type: 'global'` or `type: 'project'` contexts after FloatingChat.tsx was deleted.
|
||||
|
||||
- [ ] **Step 6: Commit**
|
||||
|
||||
```bash
|
||||
git add src/renderer/hooks/useAIChat.ts \
|
||||
src/renderer/hooks/useChatStream.ts \
|
||||
src/renderer/components/ai/ChatInputBox.tsx \
|
||||
src/renderer/components/brief/TaskBriefChat.tsx
|
||||
git commit -m "$(cat <<'EOF'
|
||||
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.
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task M6.4 — Main process: drop `sendFloatingRequest`, `orchestrateFloating`, floating mode
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/main/api/backend-client.ts`
|
||||
- Modify: `src/main/ai/orchestrator.ts`
|
||||
- Modify: `src/main/router/index.ts`
|
||||
- Modify: `src/preload/trpc.ts`
|
||||
- Modify: `src/renderer/lib/ipcLink.ts`
|
||||
- Modify: `src/shared/api-types.ts`
|
||||
|
||||
### Context
|
||||
|
||||
`backend-client.ts` has `sendFloatingRequest` (lines 398–458) and a `floating_domain` case in its WS message handler (lines 1060–1063).
|
||||
|
||||
`orchestrator.ts` has `OrchestrateFloatingInput` interface (lines 40–49) and `orchestrateFloating` function (lines 142–173). It also imports `WsFloatingRequest` from shared types.
|
||||
|
||||
`router/index.ts` imports `orchestrateFloating` (line 16) and uses it at lines 950–960. The `mode` enum must change from `z.enum(['home', 'floating', 'contextual'])` to `z.enum(['contextual'])`.
|
||||
|
||||
`preload/trpc.ts` defines the `V3StreamEvent` union which includes `floating_domain` (lines 29–42).
|
||||
|
||||
`renderer/lib/ipcLink.ts` defines a duplicate `V3StreamEvent` type which also includes `floating_domain` (lines 16–33).
|
||||
|
||||
`shared/api-types.ts` defines `WsFloatingDomainSchema` (lines 217–228) and `WsFloatingDomain` type.
|
||||
|
||||
Check also whether `WsFloatingRequest` type is still needed by anything after removing `orchestrateFloating`:
|
||||
```bash
|
||||
grep -rn "WsFloatingRequest\|WsFloatingDomain" src/
|
||||
```
|
||||
|
||||
### Steps
|
||||
|
||||
- [ ] **Step 1: Delete sendFloatingRequest from backend-client.ts**
|
||||
|
||||
Open `src/main/api/backend-client.ts`.
|
||||
|
||||
Delete the entire `sendFloatingRequest` method (lines ~394–458), from the JSDoc comment through the closing `}`.
|
||||
|
||||
Also delete the `floating_domain` case in the WS message handler (lines ~1060–1063):
|
||||
```ts
|
||||
case 'floating_domain': {
|
||||
const listener = this.streamListeners.get(frame.data.requestId);
|
||||
listener?.onDomain(frame.data.domain);
|
||||
break;
|
||||
}
|
||||
```
|
||||
|
||||
Also check whether `onDomain` is still used in `StreamListener` type or elsewhere. If `onDomain` callback is only referenced by `sendFloatingRequest` and the `floating_domain` case, delete the `onDomain` field from `StreamListener` too (search for `onDomain` to confirm all uses).
|
||||
|
||||
- [ ] **Step 2: Delete orchestrateFloating from orchestrator.ts**
|
||||
|
||||
Open `src/main/ai/orchestrator.ts`.
|
||||
|
||||
Delete lines 40–49 (`OrchestrateFloatingInput` interface):
|
||||
```ts
|
||||
interface OrchestrateFloatingInput {
|
||||
message: string;
|
||||
requestId?: string;
|
||||
sessionId?: string;
|
||||
scope: WsFloatingRequest['scope'];
|
||||
conversationHistory?: WsFloatingRequest['conversationHistory'];
|
||||
briefMode?: boolean;
|
||||
briefingContext?: string;
|
||||
sender?: Electron.WebContents;
|
||||
}
|
||||
```
|
||||
|
||||
Delete lines 1–16 of the import section's `WsFloatingRequest` import. Change:
|
||||
```ts
|
||||
import type { WsFloatingRequest } from '../../shared/api-types';
|
||||
```
|
||||
to nothing (remove the line entirely), since `WsFloatingRequest` is only used by `OrchestrateFloatingInput`.
|
||||
|
||||
Delete the entire `orchestrateFloating` function (lines 142–173):
|
||||
```ts
|
||||
export async function orchestrateFloating(input: OrchestrateFloatingInput): Promise<OrchestrateResult> {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Update the docstring at the top of the file (lines 1–10) — remove the reference to `sendFloatingRequest()`:
|
||||
```
|
||||
* 2. Delegates to BackendClient.sendHomeRequest() / sendFloatingRequest()
|
||||
```
|
||||
becomes:
|
||||
```
|
||||
* 2. Delegates to BackendClient.sendHomeRequest() / sendContextualRequest()
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Update router/index.ts**
|
||||
|
||||
Open `src/main/router/index.ts`.
|
||||
|
||||
Change line 16 — remove `orchestrateFloating` from the import:
|
||||
```ts
|
||||
import { orchestrate, orchestrateFloating, orchestrateContextual, orchestrateTaskBriefResearch, dailyBrief, getCachedBrief, invalidateBriefCache } from '../ai/orchestrator';
|
||||
```
|
||||
becomes:
|
||||
```ts
|
||||
import { orchestrate, orchestrateContextual, orchestrateTaskBriefResearch, dailyBrief, getCachedBrief, invalidateBriefCache } from '../ai/orchestrator';
|
||||
```
|
||||
|
||||
Change line 933 — update the `mode` enum:
|
||||
```ts
|
||||
mode: z.enum(['home', 'floating', 'contextual']).optional(),
|
||||
```
|
||||
becomes:
|
||||
```ts
|
||||
mode: z.enum(['contextual']).optional(),
|
||||
```
|
||||
|
||||
Delete the floating branch in the mutation handler (lines 950–960):
|
||||
```ts
|
||||
if (input.mode === 'floating' && input.scope) {
|
||||
return await orchestrateFloating({
|
||||
message: input.message,
|
||||
requestId: input.requestId,
|
||||
sessionId: input.sessionId,
|
||||
scope: input.scope as Parameters<typeof orchestrateFloating>[0]['scope'],
|
||||
conversationHistory: input.conversationHistory,
|
||||
briefMode: input.briefMode,
|
||||
briefingContext: input.briefingContext,
|
||||
sender: ctx.sender,
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
The resulting mutation handler should flow: `if contextual → orchestrateContextual`, else `→ orchestrate`.
|
||||
|
||||
- [ ] **Step 4: Update preload/trpc.ts**
|
||||
|
||||
Open `src/preload/trpc.ts`.
|
||||
|
||||
Remove the `floating_domain` case from the `V3StreamEvent` union (lines 29–42):
|
||||
```ts
|
||||
| {
|
||||
type: 'floating_domain';
|
||||
requestId: string;
|
||||
domain:
|
||||
| 'tasks'
|
||||
| 'notes'
|
||||
| 'timelines'
|
||||
| 'projects'
|
||||
| {
|
||||
type: 'task' | 'timeline' | 'project' | 'note' | 'node';
|
||||
id?: string | null;
|
||||
section?: 'task' | 'timeline' | 'note' | null;
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
The `V3StreamEvent` union becomes:
|
||||
```ts
|
||||
type V3StreamEvent =
|
||||
| { type: 'stream_start'; requestId: string }
|
||||
| { type: 'stream_text'; requestId: string; chunk: string }
|
||||
| { type: 'stream_end'; requestId: string; mutations?: unknown[] };
|
||||
```
|
||||
|
||||
- [ ] **Step 5: Update renderer/lib/ipcLink.ts**
|
||||
|
||||
Open `src/renderer/lib/ipcLink.ts`.
|
||||
|
||||
Remove the `floating_domain` case from the `V3StreamEvent` union (lines 16–33) the same way as done in preload. The type becomes:
|
||||
```ts
|
||||
type V3StreamEvent =
|
||||
| { type: 'stream_start'; requestId: string }
|
||||
| { type: 'stream_text'; requestId: string; chunk: string }
|
||||
| { type: 'stream_end'; requestId: string; mutations?: unknown[] };
|
||||
```
|
||||
|
||||
- [ ] **Step 6: Update shared/api-types.ts**
|
||||
|
||||
Open `src/shared/api-types.ts`.
|
||||
|
||||
Check whether `WsFloatingDomainSchema` / `WsFloatingDomain` are imported anywhere:
|
||||
```bash
|
||||
grep -rn "WsFloatingDomain\|WsFloatingRequest" src/
|
||||
```
|
||||
|
||||
If `WsFloatingRequest` is imported only by the now-deleted orchestrator import, and `WsFloatingDomain` is imported nowhere, delete both from `shared/api-types.ts`:
|
||||
- Delete `WsFloatingDomainSchema` Zod object (lines 217–228)
|
||||
- Delete `export type WsFloatingDomain = z.infer<typeof WsFloatingDomainSchema>;` (line 229)
|
||||
- If `WsFloatingRequest` type/schema exists, delete it too (search for it in the file)
|
||||
|
||||
- [ ] **Step 7: Sweep for any remaining floating references in main**
|
||||
|
||||
```bash
|
||||
grep -rn "floating\|Floating" src/main/ src/preload/ | grep -v node_modules | grep -v "\.md:"
|
||||
```
|
||||
|
||||
Any remaining references to floating in these directories should be removed. Common residuals:
|
||||
- `onDomain` callback type in `StreamListener` (if `sendFloatingRequest` was its only consumer)
|
||||
- Stale comments in `backend-client.ts` mentioning floating
|
||||
|
||||
- [ ] **Step 8: Type check**
|
||||
|
||||
```bash
|
||||
source ~/.nvm/nvm.sh && npx tsc --noEmit 2>&1 | grep -i "error" | head -30
|
||||
```
|
||||
|
||||
Expected: zero errors. The `TaskBriefChat.tsx` now sends `mode: 'contextual'` which is valid per the updated schema.
|
||||
|
||||
- [ ] **Step 9: Commit**
|
||||
|
||||
```bash
|
||||
git add src/main/api/backend-client.ts \
|
||||
src/main/ai/orchestrator.ts \
|
||||
src/main/router/index.ts \
|
||||
src/preload/trpc.ts \
|
||||
src/renderer/lib/ipcLink.ts \
|
||||
src/shared/api-types.ts
|
||||
git commit -m "$(cat <<'EOF'
|
||||
refactor(contextual): main process drops sendFloatingRequest and floating mode
|
||||
|
||||
ai.chat tRPC procedure now accepts mode='contextual' (or unset for home).
|
||||
Orchestrator loses the floating delegation branch. Backend client method
|
||||
and WsFloatingDomain shared type removed.
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task M6.8 — Sweep electron-store and localStorage `'floating'` keys
|
||||
|
||||
**Files:**
|
||||
- Inspect: `src/main/store.ts`
|
||||
- Inspect: `src/renderer/` (any localStorage usage)
|
||||
|
||||
### Context
|
||||
|
||||
`src/main/store.ts` defines the electron-store schema — no `floating.*` keys exist in the current schema (verified: `AppSettings` has `sidebarCollapsed`, `encryptedTokens`, `backendUrl`, `deviceId`, `dailyBriefCache`, `localAgents`, `formatPrefs`, `uiLanguage`, `timelineZoom`). No floating keys.
|
||||
|
||||
This task is a verification sweep. If nothing is found, the commit is skipped.
|
||||
|
||||
### Steps
|
||||
|
||||
- [ ] **Step 1: Search for any floating.* key usage**
|
||||
|
||||
```bash
|
||||
grep -rn "floating\." src/main/ src/preload/ src/renderer/ | grep -v node_modules | grep -v "\.md:"
|
||||
```
|
||||
|
||||
Also check localStorage:
|
||||
```bash
|
||||
grep -rn "localStorage" src/renderer/ | grep -v node_modules | grep "float"
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Decision point**
|
||||
|
||||
If Step 1 returns NO results (or only results already cleaned by M6.1–M6.4), this task is a no-op. Skip the commit and note in the final report: "M6.8: no floating.* store or localStorage keys found — no commit needed."
|
||||
|
||||
If Step 1 returns results with actual floating key reads/writes in store.ts or localStorage calls:
|
||||
- Delete the key from `AppSettings` interface and the `defaults` object in `store.ts`
|
||||
- Delete any `localStorage.getItem('floating.*')` or `setItem('floating.*', ...)` calls
|
||||
|
||||
- [ ] **Step 3: Conditional commit**
|
||||
|
||||
Only run this if changes were made in Step 2:
|
||||
|
||||
```bash
|
||||
git add -A
|
||||
git status --short # confirm what's staged
|
||||
git commit -m "$(cat <<'EOF'
|
||||
chore(contextual): purge residual 'floating' keys from store and renderer
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Final Self-Review Checklist
|
||||
|
||||
After all commits, run these verification checks and paste the output into your report:
|
||||
|
||||
- [ ] **FloatingChat imports gone**
|
||||
```bash
|
||||
grep -rn "FloatingChat\|useFloatingChat\|FloatingChatProvider\|useDoubleClickAI" src/renderer/
|
||||
```
|
||||
Expected: no output.
|
||||
|
||||
- [ ] **data-ai-section gone**
|
||||
```bash
|
||||
grep -rn "data-ai-section" src/renderer/
|
||||
```
|
||||
Expected: no output.
|
||||
|
||||
- [ ] **floating string in renderer hooks**
|
||||
```bash
|
||||
grep -rn "'floating'" src/renderer/hooks/
|
||||
```
|
||||
Expected: no output (sidebar.tsx uses `"floating"` as a layout variant — that is unrelated and harmless).
|
||||
|
||||
- [ ] **UIChatContext type**
|
||||
```bash
|
||||
grep -n "type:" src/renderer/hooks/useAIChat.ts | head -5
|
||||
```
|
||||
Expected: `'global' | 'project'` only.
|
||||
|
||||
- [ ] **tRPC schema**
|
||||
```bash
|
||||
grep -n "floating" src/main/router/index.ts
|
||||
```
|
||||
Expected: no output.
|
||||
|
||||
- [ ] **sendFloatingRequest gone**
|
||||
```bash
|
||||
grep -rn "sendFloatingRequest\|orchestrateFloating" src/main/
|
||||
```
|
||||
Expected: no output.
|
||||
|
||||
- [ ] **Final tsc**
|
||||
```bash
|
||||
source ~/.nvm/nvm.sh && npx tsc --noEmit 2>&1 | grep -c "error TS"
|
||||
```
|
||||
Expected: `0`
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
19681
graphify-out/graph.json
19681
graphify-out/graph.json
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user