| author | |
| committer | |
| log | 2f4051ed308d8c10558e9be7a4b58f6fd7a936eb |
| tree | f20a730467f7ae8419f5df05c93eb89ade950cca |
| parent | f0ac59306842c0392b2e11ac35ebf24763131de4 |
| signature | Commit is signed but in an unrecognized format. |
4 files changed, 52 insertions(+), 52 deletions(-)
.gitignore+1| ... | @@ -10,3 +10,4 @@ packages/*/dist | ... | @@ -10,3 +10,4 @@ packages/*/dist |
| 10 | .env | 10 | .env |
| 11 | .env.local | 11 | .env.local |
| 12 | build/ | 12 | build/ |
| 13 | .logs/ |
apps/desktop/src/providerManager.ts+15-1| ... | @@ -1,4 +1,6 @@ | ... | @@ -1,4 +1,6 @@ |
| 1 | import { EventEmitter } from "node:events"; | 1 | import { EventEmitter } from "node:events"; |
| 2 | import fs from "node:fs"; | ||
| 3 | import path from "node:path"; | ||
| 2 | 4 | ||
| 3 | import { | 5 | import { |
| 4 | type ProviderEvent, | 6 | type ProviderEvent, |
| ... | @@ -21,10 +23,22 @@ export interface ProviderManagerEvents { | ... | @@ -21,10 +23,22 @@ export interface ProviderManagerEvents { |
| 21 | 23 | ||
| 22 | export class ProviderManager extends EventEmitter<ProviderManagerEvents> { | 24 | export class ProviderManager extends EventEmitter<ProviderManagerEvents> { |
| 23 | private readonly codex = new CodexAppServerManager(); | 25 | private readonly codex = new CodexAppServerManager(); |
| 26 | private readonly logStream: fs.WriteStream; | ||
| 24 | 27 | ||
| 25 | constructor() { | 28 | constructor() { |
| 26 | super(); | 29 | super(); |
| 27 | this.codex.on("event", (event) => this.emit("event", event)); | 30 | |
| 31 | const logsDir = path.resolve(process.cwd(), ".logs"); | ||
| 32 | fs.mkdirSync(logsDir, { recursive: true }); | ||
| 33 | this.logStream = fs.createWriteStream( | ||
| 34 | path.join(logsDir, "events.txt"), | ||
| 35 | { flags: "a" }, | ||
| 36 | ); | ||
| 37 | |||
| 38 | this.codex.on("event", (event) => { | ||
| 39 | this.logStream.write(`${JSON.stringify(event)}\n`); | ||
| 40 | this.emit("event", event); | ||
| 41 | }); | ||
| 28 | } | 42 | } |
| 29 | 43 | ||
| 30 | async startSession(raw: ProviderSessionStartInput): Promise<ProviderSession> { | 44 | async startSession(raw: ProviderSessionStartInput): Promise<ProviderSession> { |
apps/renderer/src/components/ChatView.tsx+33-51| ... | @@ -36,10 +36,10 @@ function statusLabel(phase: string): string { | ... | @@ -36,10 +36,10 @@ function statusLabel(phase: string): string { |
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | function workToneClass(tone: "thinking" | "tool" | "info" | "error"): string { | 38 | function workToneClass(tone: "thinking" | "tool" | "info" | "error"): string { |
| 39 | if (tone === "thinking") return "text-sky-100"; | 39 | if (tone === "error") return "text-rose-300/50"; |
| 40 | if (tone === "tool") return "text-emerald-100"; | 40 | if (tone === "tool") return "text-[#8a8a8a]"; |
| 41 | if (tone === "error") return "text-rose-100"; | 41 | if (tone === "thinking") return "text-[#707070]"; |
| 42 | return "text-[#d8d8d8]"; | 42 | return "text-[#606060]"; |
| 43 | } | 43 | } |
| 44 | 44 | ||
| 45 | export default function ChatView() { | 45 | export default function ChatView() { |
| ... | @@ -341,53 +341,6 @@ export default function ChatView() { | ... | @@ -341,53 +341,6 @@ export default function ChatView() { |
| 341 | </div> | 341 | </div> |
| 342 | ) : ( | 342 | ) : ( |
| 343 | <div className="mx-auto max-w-3xl space-y-4"> | 343 | <div className="mx-auto max-w-3xl space-y-4"> |
| 344 | {isWorking && ( | ||
| 345 | <div className="rounded-2xl border border-sky-400/20 bg-sky-500/[0.06] px-3 py-2.5"> | ||
| 346 | <div className="flex items-center justify-between gap-2"> | ||
| 347 | <div className="inline-flex items-center gap-2 text-xs text-sky-100"> | ||
| 348 | <span className="relative inline-flex h-2.5 w-2.5"> | ||
| 349 | <span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-sky-300/70" /> | ||
| 350 | <span className="relative inline-flex h-2.5 w-2.5 rounded-full bg-sky-200" /> | ||
| 351 | </span> | ||
| 352 | <span>Model is working</span> | ||
| 353 | </div> | ||
| 354 | <span className="text-[10px] text-sky-100/70"> | ||
| 355 | {recentWorkLogEntries.length} event | ||
| 356 | {recentWorkLogEntries.length === 1 ? "" : "s"} | ||
| 357 | </span> | ||
| 358 | </div> | ||
| 359 | {recentWorkLogEntries.length === 0 ? ( | ||
| 360 | <p className="pt-2 text-[11px] text-sky-100/70"> | ||
| 361 | Waiting for tool/preamble updates... | ||
| 362 | </p> | ||
| 363 | ) : ( | ||
| 364 | <div className="mt-2 max-h-48 space-y-1.5 overflow-y-auto pr-1"> | ||
| 365 | {recentWorkLogEntries.map((entry) => ( | ||
| 366 | <div | ||
| 367 | key={entry.id} | ||
| 368 | className="rounded-lg border border-white/[0.08] bg-black/20 px-2 py-1.5" | ||
| 369 | > | ||
| 370 | <div className="flex items-start justify-between gap-2"> | ||
| 371 | <p | ||
| 372 | className={`text-[11px] ${workToneClass(entry.tone)}`} | ||
| 373 | > | ||
| 374 | {entry.label} | ||
| 375 | </p> | ||
| 376 | <span className="shrink-0 text-[10px] text-[#a0a0a0]/60"> | ||
| 377 | {formatTimestamp(entry.createdAt)} | ||
| 378 | </span> | ||
| 379 | </div> | ||
| 380 | {entry.detail && ( | ||
| 381 | <p className="pt-0.5 font-mono text-[11px] text-[#d0d0d0]/75"> | ||
| 382 | {entry.detail} | ||
| 383 | </p> | ||
| 384 | )} | ||
| 385 | </div> | ||
| 386 | ))} | ||
| 387 | </div> | ||
| 388 | )} | ||
| 389 | </div> | ||
| 390 | )} | ||
| 391 | {activeThread.messages.map((msg) => ( | 344 | {activeThread.messages.map((msg) => ( |
| 392 | <div key={msg.id}> | 345 | <div key={msg.id}> |
| 393 | {msg.role === "user" ? ( | 346 | {msg.role === "user" ? ( |
| ... | @@ -433,6 +386,35 @@ export default function ChatView() { | ... | @@ -433,6 +386,35 @@ export default function ChatView() { |
| 433 | )} | 386 | )} |
| 434 | </div> | 387 | </div> |
| 435 | ))} | 388 | ))} |
| 389 | {/* Inline activity feed */} | ||
| 390 | {isWorking && ( | ||
| 391 | <div className="border-l-2 border-white/[0.05] pl-4 py-1"> | ||
| 392 | {recentWorkLogEntries.map((entry) => ( | ||
| 393 | <p | ||
| 394 | key={entry.id} | ||
| 395 | className={`py-[2px] text-[12px] leading-relaxed ${workToneClass(entry.tone)}`} | ||
| 396 | > | ||
| 397 | {entry.detail ? ( | ||
| 398 | <> | ||
| 399 | {entry.label} | ||
| 400 | <span className="ml-1.5 font-mono text-[11px] opacity-60"> | ||
| 401 | {entry.detail} | ||
| 402 | </span> | ||
| 403 | </> | ||
| 404 | ) : ( | ||
| 405 | entry.label | ||
| 406 | )} | ||
| 407 | </p> | ||
| 408 | ))} | ||
| 409 | <div className="flex items-center pt-1"> | ||
| 410 | <span className="inline-flex items-center gap-[3px]"> | ||
| 411 | <span className="h-1 w-1 rounded-full bg-white/20 animate-pulse" /> | ||
| 412 | <span className="h-1 w-1 rounded-full bg-white/20 animate-pulse [animation-delay:200ms]" /> | ||
| 413 | <span className="h-1 w-1 rounded-full bg-white/20 animate-pulse [animation-delay:400ms]" /> | ||
| 414 | </span> | ||
| 415 | </div> | ||
| 416 | </div> | ||
| 417 | )} | ||
| 436 | <div ref={messagesEndRef} /> | 418 | <div ref={messagesEndRef} /> |
| 437 | </div> | 419 | </div> |
| 438 | )} | 420 | )} |
bun.lock+3| ... | @@ -42,6 +42,7 @@ | ... | @@ -42,6 +42,7 @@ |
| 42 | "@types/react": "^19.0.0", | 42 | "@types/react": "^19.0.0", |
| 43 | "@types/react-dom": "^19.0.0", | 43 | "@types/react-dom": "^19.0.0", |
| 44 | "@vitejs/plugin-react": "^4.3.4", | 44 | "@vitejs/plugin-react": "^4.3.4", |
| 45 | "babel-plugin-react-compiler": "^19.0.0-beta-e552027-20250112", | ||
| 45 | "tailwindcss": "^4.0.0", | 46 | "tailwindcss": "^4.0.0", |
| 46 | "typescript": "^5.7.3", | 47 | "typescript": "^5.7.3", |
| 47 | "vite": "^6.0.5", | 48 | "vite": "^6.0.5", |
| ... | @@ -368,6 +369,8 @@ | ... | @@ -368,6 +369,8 @@ |
| 368 | 369 | ||
| 369 | "axios": ["axios@1.13.4", "", { "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.4", "proxy-from-env": "^1.1.0" } }, "sha512-1wVkUaAO6WyaYtCkcYCOx12ZgpGf9Zif+qXa4n+oYzK558YryKqiL6UWwd5DqiH3VRW0GYhTZQ/vlgJrCoNQlg=="], | 370 | "axios": ["axios@1.13.4", "", { "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.4", "proxy-from-env": "^1.1.0" } }, "sha512-1wVkUaAO6WyaYtCkcYCOx12ZgpGf9Zif+qXa4n+oYzK558YryKqiL6UWwd5DqiH3VRW0GYhTZQ/vlgJrCoNQlg=="], |
| 370 | 371 | ||
| 372 | "babel-plugin-react-compiler": ["babel-plugin-react-compiler@19.0.0-beta-ebf51a3-20250411", "", { "dependencies": { "@babel/types": "^7.26.0" } }, "sha512-q84bNR9JG1crykAlJUt5Ud0/5BUyMFuQww/mrwIQDFBaxsikqBDj3f/FNDsVd2iR26A1HvXKWPEIfgJDv8/V2g=="], | ||
| 373 | |||
| 371 | "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], | 374 | "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], |
| 372 | 375 | ||
| 373 | "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], | 376 | "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], |