diff --git a/.mcp.json b/.mcp.json index 9bf8ac1..bd98b4f 100644 --- a/.mcp.json +++ b/.mcp.json @@ -1,10 +1,8 @@ { "mcpServers": { "shadcn": { - "command": "cmd", + "command": "npx", "args": [ - "/c", - "npx", "shadcn@latest", "mcp" ] diff --git a/src/main/ai/orchestrator.ts b/src/main/ai/orchestrator.ts index 9e65e38..d13d71b 100644 --- a/src/main/ai/orchestrator.ts +++ b/src/main/ai/orchestrator.ts @@ -946,3 +946,27 @@ export async function orchestrate(input: OrchestrateInput): Promise { + return orchestrate({ + message: DAILY_BRIEF_PROMPT, + context: { type: 'global' }, + sender, + }); +} diff --git a/src/main/router/index.ts b/src/main/router/index.ts index f7ef537..85c2c48 100644 --- a/src/main/router/index.ts +++ b/src/main/router/index.ts @@ -6,7 +6,7 @@ import { getDb } from '../db'; import { clients, projects, tasks, checkpoints, notes, taskComments } from '../db/schema'; import { getStore } from '../store'; import { saveTokenAndInit, hasActiveToken } from '../ai/provider'; -import { orchestrate } from '../ai/orchestrator'; +import { orchestrate, dailyBrief } from '../ai/orchestrator'; import { upsertNoteEmbedding } from '../db/vectordb'; import type { TRPCContext } from '../ipc'; @@ -572,6 +572,15 @@ const aiRouter = router({ await saveTokenAndInit(input.token); return { success: true }; }), + dailyBrief: publicProcedure + .mutation(async ({ ctx }) => { + try { + return await dailyBrief(ctx.sender); + } catch (err) { + const msg = err instanceof Error ? err.message : 'Unknown error'; + return { response: '', error: msg }; + } + }), hasToken: publicProcedure.query(async () => { return hasActiveToken(); }), diff --git a/src/renderer/components/ai/AIChatPanel.tsx b/src/renderer/components/ai/AIChatPanel.tsx index d6aca60..ae57c10 100644 --- a/src/renderer/components/ai/AIChatPanel.tsx +++ b/src/renderer/components/ai/AIChatPanel.tsx @@ -61,6 +61,7 @@ export function AIChatPanel({ const streamingContentRef = useRef(''); const chatMutation = trpc.ai.chat.useMutation(); + const briefMutation = trpc.ai.dailyBrief.useMutation(); const scrollToBottom = useCallback(() => { const el = messagesContainerRef.current; @@ -98,28 +99,21 @@ export function AIChatPanel({ setDailyBrief(briefContentRef.current); }); - chatMutation.mutate( - { - message: - 'Give me a concise daily brief for today. Highlight tasks due today and this week, any overdue items, and recent project activity. Use **bold** for key phrases. Keep it to 3-5 sentences.', - context: { type: 'global' }, - }, - { - onSuccess: (data) => { - if (data.error) { - unsubscribe(); - setDailyBrief(null); - setBriefLoading(false); - } - }, - onError: () => { + briefMutation.mutate(undefined, { + onSuccess: (data) => { + if (data.error) { unsubscribe(); setDailyBrief(null); setBriefLoading(false); - }, + } }, - ); - }, [isHomePage, hasTokenQuery.data]); // chatMutation excluded — only fire once + onError: () => { + unsubscribe(); + setDailyBrief(null); + setBriefLoading(false); + }, + }); + }, [isHomePage, hasTokenQuery.data]); // briefMutation excluded — only fire once const handleSend = useCallback(() => { const trimmed = input.trim(); @@ -256,53 +250,78 @@ export function AIChatPanel({ viewportRef={messagesContainerRef} viewportClassName={ isHomePage && !hasMessages - ? undefined + ? '[&>div]:!flex [&>div]:!flex-col [&>div]:!min-h-full [&>div]:!justify-center' : '[&>div]:!flex [&>div]:!flex-col [&>div]:!min-h-full [&>div]:!justify-end' } onWheel={handleWheel} > {/* Home page initial state: greeting + brief */} {isHomePage && !hasMessages && ( -
-
- {/* Greeting + badge */} -
-

- ✦ Hello, {userName} -

- - {dueCount} Task{dueCount !== 1 ? 's' : ''} due - +
+
+ {/* Greeting + brief grouped closely */} +
+
+

+ ✦ Hello, {userName} +

+ + {dueCount} Task{dueCount !== 1 ? 's' : ''} due + +
+ + {/* Daily brief */} +
+ {hasTokenQuery.data === false ? ( +
+ +

+ Configure your AI provider in Settings to enable the daily brief. +

+ +
+ ) : briefLoading && !dailyBrief ? ( +
+ + + +
+ ) : dailyBrief ? ( + + ) : ( +

+ Your daily brief will appear here. +

+ )} +
- {/* Daily brief card */} - - - {hasTokenQuery.data === false ? ( -
- -

- Configure your AI provider in Settings to enable the daily brief. -

- -
- ) : briefLoading && !dailyBrief ? ( -
- - - -
- ) : dailyBrief ? ( - - ) : ( -

- Your daily brief will appear here. -

- )} -
-
+ {/* Inline input + suggestion chips */} +
+ +
+ {SUGGESTION_CHIPS.map((chip) => ( + + ))} +
+
)} @@ -311,13 +330,11 @@ export function AIChatPanel({ {isHomePage && hasMessages && (
- {/* Brief card persists */} + {/* Brief persists */} {dailyBrief && ( - - - - - +
+ +
)} {/* Chat messages */} @@ -440,37 +457,22 @@ export function AIChatPanel({ )} - {/* Fixed input — pinned to the bottom */} -
-
-
- - - {/* Suggestion chips (home page only, before first message) */} - {isHomePage && !hasMessages && ( -
- {SUGGESTION_CHIPS.map((chip) => ( - - ))} -
- )} + {/* Fixed input — pinned to the bottom (hidden on home initial state) */} + {!(isHomePage && !hasMessages) && ( +
+
+
+ +
-
+ )}
); }