@workerdeck/ui 0.20.0 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/{SessionPanel-CnU_IJ3-.d.mts → SessionPanel-BjNAnWMF.d.mts} +41 -3
- package/build/{SessionPanel-CHktI2CF.mjs → SessionPanel-DNuPKg5c.mjs} +20 -12
- package/build/{SessionPanel-CHktI2CF.mjs.map → SessionPanel-DNuPKg5c.mjs.map} +1 -1
- package/build/index.d.mts +256 -33
- package/build/index.mjs +528 -392
- package/build/index.mjs.map +1 -1
- package/build/workspace.d.mts +10 -1
- package/build/workspace.mjs +4 -2
- package/build/workspace.mjs.map +1 -1
- package/package.json +9 -5
- package/src/components/agent/SessionBrowser.tsx +113 -293
- package/src/components/agent/SessionItem.tsx +538 -0
- package/src/components/agent/SessionPanel.tsx +92 -14
- package/src/components/agent/SessionStatusIcon.tsx +43 -0
- package/src/components/agent/SessionSteps.tsx +108 -57
- package/src/components/agent/SessionWorkspace.tsx +11 -0
- package/src/index.ts +5 -1
- package/src/styles/theme.css +63 -8
package/build/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { $ as toolIcon, A as PERMISSION_MODES, At as MenuSeparator, B as skillPrompt, Bt as badgeVariants, C as TerminalQuestionPrompt, Ct as DialogContent, D as parseUserQuestions, Dt as Menu, E as QuestionPrompt, Et as DialogTrigger, F as SkillsDialog, Ft as SelectItemText, G as Band, H as TerminalMarkdown, Ht as buttonVariants, I as McpDialog, It as SelectTrigger, J as Row, K as Blank, L as HostFilesDialog, Lt as SelectValue, M as permissionModeChoices, Mt as Select, N as permissionModeMeta, Nt as SelectContent, O as SubagentStrip, Ot as MenuContent, P as ModelSelect, Pt as SelectItem, Q as isMutatingTool, R as ContextDialog, Rt as Input, S as SessionInfoDialog, St as DialogClose, T as QUESTION_BEHAVIORS, Tt as DialogRow, U as TerminalDiff, Ut as cn, V as TerminalSurface, Vt as Button, W as previewPatch, X as WithActions, Y as CopyAction, Z as useAffordances, _ as Conversation, _t as Tip, a as Transcript, at as hashtagTrigger, b as StatusBar, bt as Dialog, c as ToolCallCard, ct as PromptArea, d as Response, dt as ProgressRing, et as TranscriptDensityProvider, f as PromptTokenText, ft as Splitter, g as FileCard, gt as copyText, h as Loader, ht as CopyButton, i as useMinuteClock, it as commandTrigger, j as PermissionModeSelect, jt as MenuTrigger, k as PermissionPrompt, kt as MenuItem, l as SessionEmptyState, lt as plainTextToSegments, m as MessageContent, mt as Spinner, n as UsageDialog, nt as useTranscriptDensity, o as TerminalItemView, ot as mentionTrigger, p as Message, pt as CodeBlock, q as Ink, r as UsageMeters, rt as useTranscriptVariant, s as TerminalTranscript, st as usePromptAreaState, t as SessionPanel, tt as TranscriptVariantProvider, u as Reasoning, ut as segmentsToPlainText, v as ConversationContent, vt as TooltipContent, w as TerminalPermissionPrompt, wt as DialogHeader, x as STATUS_META, xt as DialogBody, y as ConversationScrollButton, yt as TooltipProvider, z as Composer, zt as Badge } from "./SessionPanel-
|
|
1
|
+
import { $ as toolIcon, A as PERMISSION_MODES, At as MenuSeparator, B as skillPrompt, Bt as badgeVariants, C as TerminalQuestionPrompt, Ct as DialogContent, D as parseUserQuestions, Dt as Menu, E as QuestionPrompt, Et as DialogTrigger, F as SkillsDialog, Ft as SelectItemText, G as Band, H as TerminalMarkdown, Ht as buttonVariants, I as McpDialog, It as SelectTrigger, J as Row, K as Blank, L as HostFilesDialog, Lt as SelectValue, M as permissionModeChoices, Mt as Select, N as permissionModeMeta, Nt as SelectContent, O as SubagentStrip, Ot as MenuContent, P as ModelSelect, Pt as SelectItem, Q as isMutatingTool, R as ContextDialog, Rt as Input, S as SessionInfoDialog, St as DialogClose, T as QUESTION_BEHAVIORS, Tt as DialogRow, U as TerminalDiff, Ut as cn, V as TerminalSurface, Vt as Button, W as previewPatch, X as WithActions, Y as CopyAction, Z as useAffordances, _ as Conversation, _t as Tip, a as Transcript, at as hashtagTrigger, b as StatusBar, bt as Dialog, c as ToolCallCard, ct as PromptArea, d as Response, dt as ProgressRing, et as TranscriptDensityProvider, f as PromptTokenText, ft as Splitter, g as FileCard, gt as copyText, h as Loader, ht as CopyButton, i as useMinuteClock, it as commandTrigger, j as PermissionModeSelect, jt as MenuTrigger, k as PermissionPrompt, kt as MenuItem, l as SessionEmptyState, lt as plainTextToSegments, m as MessageContent, mt as Spinner, n as UsageDialog, nt as useTranscriptDensity, o as TerminalItemView, ot as mentionTrigger, p as Message, pt as CodeBlock, q as Ink, r as UsageMeters, rt as useTranscriptVariant, s as TerminalTranscript, st as usePromptAreaState, t as SessionPanel, tt as TranscriptVariantProvider, u as Reasoning, ut as segmentsToPlainText, v as ConversationContent, vt as TooltipContent, w as TerminalPermissionPrompt, wt as DialogHeader, x as STATUS_META, xt as DialogBody, y as ConversationScrollButton, yt as TooltipProvider, z as Composer, zt as Badge } from "./SessionPanel-DNuPKg5c.mjs";
|
|
2
2
|
import { _ as formatRelativeTime, a as modelLabel, b as rateLimitWindowSeconds, d as formatBytes, f as formatCost, g as formatRateLimitWindowLong, h as formatRateLimitWindow, i as meterSeverity, l as windowLabel, m as formatDuration, o as statusPresentation, p as formatCountdown, r as meterColorClass, s as tightestWindow, t as contextSeverity, u as formatAgoPrecise, v as formatTokens, x as toolInputPreview, y as friendlyModel } from "./status-BE-zg88x.mjs";
|
|
3
|
-
import { forwardRef, useEffect, useMemo, useRef, useState } from "react";
|
|
3
|
+
import { Fragment, forwardRef, useEffect, useMemo, useRef, useState } from "react";
|
|
4
4
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
import { Anchor, ArrowRight, Atom, Beaker, BellRing, Bike, Binary, Blocks, Bot, Box, Brain, Briefcase, Brush, Bug, Building2, Cable, Camera, Car, Cat, Check, ChevronDown, ChevronRight, CircleAlert, CircleSlash, CircuitBoard, Cloud, Code, Coffee, Cog, Compass, Container, Cpu, CreditCard, Database, Diamond, Dog, Dot, Eraser, Feather, Film, Flame, FlaskConical, Folder, Gamepad2, Gauge, Gem, Ghost, Gift, GitBranch, Globe, GraduationCap, Hammer, Hexagon, House, Image, Key, Landmark, Laptop, Layers, Leaf, Library, Lightbulb, Lock, Mail, Map as Map$1, MessageCircle, Mic, Monitor, Moon, Mountain, Music, Network, Newspaper, Notebook, Orbit, Package, Palette, PauseCircle, PenTool, Pencil, Phone, PiggyBank, Plane, Plug, Puzzle, Radar, Radio, Receipt, Rocket, Ruler, Satellite, Scale, Scissors, Search, SearchX, Server, Shield, Ship, ShoppingCart, Signal, Smartphone, Sparkles, Sprout, Square, Star, Sun, Table, Tag, Target, Telescope, Terminal, TestTube, Trash2, TreePine, Trophy, Truck, Umbrella, Users, Wallet, Wand, Waves, Webhook, Wifi, Wrench, X, Zap } from "lucide-react";
|
|
6
6
|
import { AlertDialog as AlertDialog$1 } from "@base-ui/react/alert-dialog";
|
|
@@ -310,150 +310,6 @@ function SessionList({ sessions, activeId, onSelect, onDelete, emptyText = "No s
|
|
|
310
310
|
});
|
|
311
311
|
}
|
|
312
312
|
//#endregion
|
|
313
|
-
//#region src/components/agent/ContextRing.tsx
|
|
314
|
-
/**
|
|
315
|
-
* How full a session's context window is, sized for a **list row**.
|
|
316
|
-
*
|
|
317
|
-
* The reading the session screen has always drawn, moved somewhere it answers a
|
|
318
|
-
* question the session screen cannot: *which* of these thirty sessions is
|
|
319
|
-
* bloating. That is also why it is a ring and not a number — across a list, a
|
|
320
|
-
* ring is read at a glance and `71%` has to be read one row at a time.
|
|
321
|
-
*
|
|
322
|
-
* **Absent draws nothing, and absent is not zero.** A promptless session, a
|
|
323
|
-
* parked one from before the field existed, or an engine that reports no window
|
|
324
|
-
* has no reading; an empty ring would claim its context is empty rather than
|
|
325
|
-
* unknown. The title carries the exact numbers for anyone who wants them.
|
|
326
|
-
*
|
|
327
|
-
* Shared rather than spelled per client: the dashboard's row and the VS Code
|
|
328
|
-
* sidebar's card both draw it, and two copies would be two sets of thresholds
|
|
329
|
-
* the day one of them is nudged.
|
|
330
|
-
*/
|
|
331
|
-
function ContextRing({ usage, size = 11, className }) {
|
|
332
|
-
if (usage === void 0) return null;
|
|
333
|
-
return /* @__PURE__ */ jsx("span", {
|
|
334
|
-
title: `Context ${usage.percentage.toFixed(0)}% · ${formatTokens(usage.totalTokens)} / ${formatTokens(usage.maxTokens)}`,
|
|
335
|
-
className: cn("shrink-0 leading-none", meterColorClass(usage.percentage), className),
|
|
336
|
-
children: /* @__PURE__ */ jsx(ProgressRing, {
|
|
337
|
-
value: usage.percentage,
|
|
338
|
-
size,
|
|
339
|
-
strokeWidth: 2
|
|
340
|
-
})
|
|
341
|
-
});
|
|
342
|
-
}
|
|
343
|
-
//#endregion
|
|
344
|
-
//#region src/components/agent/EngineIcon.tsx
|
|
345
|
-
/**
|
|
346
|
-
* The engine's own mark, monochrome.
|
|
347
|
-
*
|
|
348
|
-
* Inlined rather than depended on: the React package these come from
|
|
349
|
-
* (`@lobehub/icons`) pulls antd, `@lobehub/ui` and a second copy of
|
|
350
|
-
* lucide-react — an absurd amount of bundle for a few glyphs in a sidebar.
|
|
351
|
-
* These are paths from `@lobehub/icons-static-svg` (MIT, © LobeHub), already
|
|
352
|
-
* `fill="currentColor"` single-path marks, so they take the row's colour like
|
|
353
|
-
* any other glyph and need no light/dark variants.
|
|
354
|
-
*
|
|
355
|
-
* The marks are trademarks of their owners (Anthropic, OpenAI, Google,
|
|
356
|
-
* DeepSeek, Moonshot), used here only to identify which engine a session runs
|
|
357
|
-
* on.
|
|
358
|
-
*
|
|
359
|
-
* Codex is drawn as OpenAI: the session's engine *is* OpenAI's, and OpenAI's
|
|
360
|
-
* mark is the one a person recognises at 12px.
|
|
361
|
-
*/
|
|
362
|
-
const PATHS = {
|
|
363
|
-
claude: {
|
|
364
|
-
title: "Claude",
|
|
365
|
-
d: "M4.709 15.955l4.72-2.647.08-.23-.08-.128H9.2l-.79-.048-2.698-.073-2.339-.097-2.266-.122-.571-.121L0 11.784l.055-.352.48-.321.686.06 1.52.103 2.278.158 1.652.097 2.449.255h.389l.055-.157-.134-.098-.103-.097-2.358-1.596-2.552-1.688-1.336-.972-.724-.491-.364-.462-.158-1.008.656-.722.881.06.225.061.893.686 1.908 1.476 2.491 1.833.365.304.145-.103.019-.073-.164-.274-1.355-2.446-1.446-2.49-.644-1.032-.17-.619a2.97 2.97 0 01-.104-.729L6.283.134 6.696 0l.996.134.42.364.62 1.414 1.002 2.229 1.555 3.03.456.898.243.832.091.255h.158V9.01l.128-1.706.237-2.095.23-2.695.08-.76.376-.91.747-.492.584.28.48.685-.067.444-.286 1.851-.559 2.903-.364 1.942h.212l.243-.242.985-1.306 1.652-2.064.73-.82.85-.904.547-.431h1.033l.76 1.129-.34 1.166-1.064 1.347-.881 1.142-1.264 1.7-.79 1.36.073.11.188-.02 2.856-.606 1.543-.28 1.841-.315.833.388.091.395-.328.807-1.969.486-2.309.462-3.439.813-.042.03.049.061 1.549.146.662.036h1.622l3.02.225.79.522.474.638-.079.485-1.215.62-1.64-.389-3.829-.91-1.312-.329h-.182v.11l1.093 1.068 2.006 1.81 2.509 2.33.127.578-.322.455-.34-.049-2.205-1.657-.851-.747-1.926-1.62h-.128v.17l.444.649 2.345 3.521.122 1.08-.17.353-.608.213-.668-.122-1.374-1.925-1.415-2.167-1.143-1.943-.14.08-.674 7.254-.316.37-.729.28-.607-.461-.322-.747.322-1.476.389-1.924.315-1.53.286-1.9.17-.632-.012-.042-.14.018-1.434 1.967-2.18 2.945-1.726 1.845-.414.164-.717-.37.067-.662.401-.589 2.388-3.036 1.44-1.882.93-1.086-.006-.158h-.055L4.132 18.56l-1.13.146-.487-.456.061-.746.231-.243 1.908-1.312-.006.006z"
|
|
366
|
-
},
|
|
367
|
-
codex: {
|
|
368
|
-
title: "OpenAI",
|
|
369
|
-
d: "M9.205 8.658v-2.26c0-.19.072-.333.238-.428l4.543-2.616c.619-.357 1.356-.523 2.117-.523 2.854 0 4.662 2.212 4.662 4.566 0 .167 0 .357-.024.547l-4.71-2.759a.797.797 0 00-.856 0l-5.97 3.473zm10.609 8.8V12.06c0-.333-.143-.57-.429-.737l-5.97-3.473 1.95-1.118a.433.433 0 01.476 0l4.543 2.617c1.309.76 2.189 2.378 2.189 3.948 0 1.808-1.07 3.473-2.76 4.163zM7.802 12.703l-1.95-1.142c-.167-.095-.239-.238-.239-.428V5.899c0-2.545 1.95-4.472 4.591-4.472 1 0 1.927.333 2.712.928L8.23 5.067c-.285.166-.428.404-.428.737v6.898zM12 15.128l-2.795-1.57v-3.33L12 8.658l2.795 1.57v3.33L12 15.128zm1.796 7.23c-1 0-1.927-.332-2.712-.927l4.686-2.712c.285-.166.428-.404.428-.737v-6.898l1.974 1.142c.167.095.238.238.238.428v5.233c0 2.545-1.974 4.472-4.614 4.472zm-5.637-5.303l-4.544-2.617c-1.308-.761-2.188-2.378-2.188-3.948A4.482 4.482 0 014.21 6.327v5.423c0 .333.143.571.428.738l5.947 3.449-1.95 1.118a.432.432 0 01-.476 0zm-.262 3.9c-2.688 0-4.662-2.021-4.662-4.519 0-.19.024-.38.047-.57l4.686 2.71c.286.167.571.167.856 0l5.97-3.448v2.26c0 .19-.07.333-.237.428l-4.543 2.616c-.619.357-1.356.523-2.117.523zm5.899 2.83a5.947 5.947 0 005.827-4.756C22.287 18.339 24 15.84 24 13.296c0-1.665-.713-3.282-1.998-4.448.119-.5.19-.999.19-1.498 0-3.401-2.759-5.947-5.946-5.947-.642 0-1.26.095-1.88.31A5.962 5.962 0 0010.205 0a5.947 5.947 0 00-5.827 4.757C1.713 5.447 0 7.945 0 10.49c0 1.666.713 3.283 1.998 4.448-.119.5-.19 1-.19 1.499 0 3.401 2.759 5.946 5.946 5.946.642 0 1.26-.095 1.88-.309a5.96 5.96 0 004.162 1.713z"
|
|
370
|
-
},
|
|
371
|
-
gemini: {
|
|
372
|
-
title: "Gemini",
|
|
373
|
-
d: "M20.616 10.835a14.147 14.147 0 01-4.45-3.001 14.111 14.111 0 01-3.678-6.452.503.503 0 00-.975 0 14.134 14.134 0 01-3.679 6.452 14.155 14.155 0 01-4.45 3.001c-.65.28-1.318.505-2.002.678a.502.502 0 000 .975c.684.172 1.35.397 2.002.677a14.147 14.147 0 014.45 3.001 14.112 14.112 0 013.679 6.453.502.502 0 00.975 0c.172-.685.397-1.351.677-2.003a14.145 14.145 0 013.001-4.45 14.113 14.113 0 016.453-3.678.503.503 0 000-.975 13.245 13.245 0 01-2.003-.678z"
|
|
374
|
-
},
|
|
375
|
-
deepseek: {
|
|
376
|
-
title: "DeepSeek",
|
|
377
|
-
d: "M23.748 4.482c-.254-.124-.364.113-.512.234-.051.039-.094.09-.137.136-.372.397-.806.657-1.373.626-.829-.046-1.537.214-2.163.848-.133-.782-.575-1.248-1.247-1.548-.352-.156-.708-.311-.955-.65-.172-.241-.219-.51-.305-.774-.055-.16-.11-.323-.293-.35-.2-.031-.278.136-.356.276-.313.572-.434 1.202-.422 1.84.027 1.436.633 2.58 1.838 3.393.137.093.172.187.129.323-.082.28-.18.552-.266.833-.055.179-.137.217-.329.14a5.526 5.526 0 01-1.736-1.18c-.857-.828-1.631-1.742-2.597-2.458a11.365 11.365 0 00-.689-.471c-.985-.957.13-1.743.388-1.836.27-.098.093-.432-.779-.428-.872.004-1.67.295-2.687.684a3.055 3.055 0 01-.465.137 9.597 9.597 0 00-2.883-.102c-1.885.21-3.39 1.102-4.497 2.623C.082 8.606-.231 10.684.152 12.85c.403 2.284 1.569 4.175 3.36 5.653 1.858 1.533 3.997 2.284 6.438 2.14 1.482-.085 3.133-.284 4.994-1.86.47.234.962.327 1.78.397.63.059 1.236-.03 1.705-.128.735-.156.684-.837.419-.961-2.155-1.004-1.682-.595-2.113-.926 1.096-1.296 2.746-2.642 3.392-7.003.05-.347.007-.565 0-.845-.004-.17.035-.237.23-.256a4.173 4.173 0 001.545-.475c1.396-.763 1.96-2.015 2.093-3.517.02-.23-.004-.467-.247-.588zM11.581 18c-2.089-1.642-3.102-2.183-3.52-2.16-.392.024-.321.471-.235.763.09.288.207.486.371.739.114.167.192.416-.113.603-.673.416-1.842-.14-1.897-.167-1.361-.802-2.5-1.86-3.301-3.307-.774-1.393-1.224-2.887-1.298-4.482-.02-.386.093-.522.477-.592a4.696 4.696 0 011.529-.039c2.132.312 3.946 1.265 5.468 2.774.868.86 1.525 1.887 2.202 2.891.72 1.066 1.494 2.082 2.48 2.914.348.292.625.514.891.677-.802.09-2.14.11-3.054-.614zm1-6.44a.306.306 0 01.415-.287.302.302 0 01.2.288.306.306 0 01-.31.307.303.303 0 01-.304-.308zm3.11 1.596c-.2.081-.399.151-.59.16a1.245 1.245 0 01-.798-.254c-.274-.23-.47-.358-.552-.758a1.73 1.73 0 01.016-.588c.07-.327-.008-.537-.239-.727-.187-.156-.426-.199-.688-.199a.559.559 0 01-.254-.078c-.11-.054-.2-.19-.114-.358.028-.054.16-.186.192-.21.356-.202.767-.136 1.146.016.352.144.618.408 1.001.782.391.451.462.576.685.914.176.265.336.537.445.848.067.195-.019.354-.25.452z"
|
|
378
|
-
},
|
|
379
|
-
moonshot: {
|
|
380
|
-
title: "Moonshot",
|
|
381
|
-
d: "M1.052 16.916l9.539 2.552a21.007 21.007 0 00.06 2.033l5.956 1.593a11.997 11.997 0 01-5.586.865l-.18-.016-.044-.004-.084-.009-.094-.01a11.605 11.605 0 01-.157-.02l-.107-.014-.11-.016a11.962 11.962 0 01-.32-.051l-.042-.008-.075-.013-.107-.02-.07-.015-.093-.019-.075-.016-.095-.02-.097-.023-.094-.022-.068-.017-.088-.022-.09-.024-.095-.025-.082-.023-.109-.03-.062-.02-.084-.025-.093-.028-.105-.034-.058-.019-.08-.026-.09-.031-.066-.024a6.293 6.293 0 01-.044-.015l-.068-.025-.101-.037-.057-.022-.08-.03-.087-.035-.088-.035-.079-.032-.095-.04-.063-.028-.063-.027a5.655 5.655 0 01-.041-.018l-.066-.03-.103-.047-.052-.024-.096-.046-.062-.03-.084-.04-.086-.044-.093-.047-.052-.027-.103-.055-.057-.03-.058-.032a6.49 6.49 0 01-.046-.026l-.094-.053-.06-.034-.051-.03-.072-.041-.082-.05-.093-.056-.052-.032-.084-.053-.061-.039-.079-.05-.07-.047-.053-.035a7.785 7.785 0 01-.054-.036l-.044-.03-.044-.03a6.066 6.066 0 01-.04-.028l-.057-.04-.076-.054-.069-.05-.074-.054-.056-.042-.076-.057-.076-.059-.086-.067-.045-.035-.064-.052-.074-.06-.089-.073-.046-.039-.046-.039a7.516 7.516 0 01-.043-.037l-.045-.04-.061-.053-.07-.062-.068-.06-.062-.058-.067-.062-.053-.05-.088-.084a13.28 13.28 0 01-.099-.097l-.029-.028-.041-.042-.069-.07-.05-.051-.05-.053a6.457 6.457 0 01-.168-.179l-.08-.088-.062-.07-.071-.08-.042-.049-.053-.062-.058-.068-.046-.056a7.175 7.175 0 01-.027-.033l-.045-.055-.066-.082-.041-.052-.05-.064-.02-.025a11.99 11.99 0 01-1.44-2.402zm-1.02-5.794l11.353 3.037a20.468 20.468 0 00-.469 2.011l10.817 2.894a12.076 12.076 0 01-1.845 2.005L.657 15.923l-.016-.046-.035-.104a11.965 11.965 0 01-.05-.153l-.007-.023a11.896 11.896 0 01-.207-.741l-.03-.126-.018-.08-.021-.097-.018-.081-.018-.09-.017-.084-.018-.094c-.026-.141-.05-.283-.071-.426l-.017-.118-.011-.083-.013-.102a12.01 12.01 0 01-.019-.161l-.005-.047a12.12 12.12 0 01-.034-2.145zm1.593-5.15l11.948 3.196c-.368.605-.705 1.231-1.01 1.875l11.295 3.022c-.142.82-.368 1.612-.668 2.365l-11.55-3.09L.124 10.26l.015-.1.008-.049.01-.067.015-.087.018-.098c.026-.148.056-.295.088-.442l.028-.124.02-.085.024-.097c.022-.09.045-.18.07-.268l.028-.102.023-.083.03-.1.025-.082.03-.096.026-.082.031-.095a11.896 11.896 0 011.01-2.232zm4.442-4.4L17.352 4.59a20.77 20.77 0 00-1.688 1.721l7.823 2.093c.267.852.442 1.744.513 2.665L2.106 5.213l.045-.065.027-.04.04-.055.046-.065.055-.076.054-.072.064-.086.05-.065.057-.073.055-.07.06-.074.055-.069.065-.077.054-.066.066-.077.053-.06.072-.082.053-.06.067-.074.054-.058.073-.078.058-.06.063-.067.168-.17.1-.098.059-.056.076-.071a12.084 12.084 0 012.272-1.677zM12.017 0h.097l.082.001.069.001.054.002.068.002.046.001.076.003.047.002.06.003.054.002.087.005.105.007.144.011.088.007.044.004.077.008.082.008.047.005.102.012.05.006.108.014.081.01.042.006.065.01.207.032.07.012.065.011.14.026.092.018.11.022.046.01.075.016.041.01L14.7.3l.042.01.065.015.049.012.071.017.096.024.112.03.113.03.113.032.05.015.07.02.078.024.073.023.05.016.05.016.076.025.099.033.102.036.048.017.064.023.093.034.11.041.116.045.1.04.047.02.06.024.041.018.063.026.04.018.057.025.11.048.1.046.074.035.075.036.06.028.092.046.091.045.102.052.053.028.049.026.046.024.06.033.041.022.052.029.088.05.106.06.087.051.057.034.053.032.096.059.088.055.098.062.036.024.064.041.084.056.04.027.062.042.062.043.023.017c.054.037.108.075.161.114l.083.06.065.048.056.043.086.065.082.064.04.03.05.041.086.069.079.065.085.071c.712.6 1.353 1.283 1.909 2.031L7.222.994l.062-.027.065-.028.081-.034.086-.035c.113-.045.227-.09.341-.131l.096-.035.093-.033.084-.03.096-.031c.087-.03.176-.058.264-.085l.091-.027.086-.025.102-.03.085-.023.1-.026L9.04.37l.09-.023.091-.022.095-.022.09-.02.098-.021.091-.02.095-.018.092-.018.1-.018.091-.016.098-.017.092-.014.097-.015.092-.013.102-.013.091-.012.105-.012.09-.01.105-.01c.093-.01.186-.018.28-.024l.106-.008.09-.005.11-.006.093-.004.1-.004.097-.002.099-.002.197-.002z"
|
|
382
|
-
}
|
|
383
|
-
};
|
|
384
|
-
/**
|
|
385
|
-
* Which mark a session wears.
|
|
386
|
-
*
|
|
387
|
-
* The two first-party engines are named directly. A `provider` session is
|
|
388
|
-
* whatever the host wired up, so its *model* is the only thing that says whose
|
|
389
|
-
* it is — sniffed loosely on purpose (`gemini-2.5-pro`, `deepseek-chat`,
|
|
390
|
-
* `kimi-k2`), and falling through to no mark rather than guessing wrong.
|
|
391
|
-
*/
|
|
392
|
-
function engineMark(engine, model) {
|
|
393
|
-
if (engine === "claude") return "claude";
|
|
394
|
-
if (engine === "codex") return "codex";
|
|
395
|
-
const id = model?.toLowerCase() ?? "";
|
|
396
|
-
if (!id) return void 0;
|
|
397
|
-
if (id.includes("gemini")) return "gemini";
|
|
398
|
-
if (id.includes("deepseek")) return "deepseek";
|
|
399
|
-
if (id.includes("moonshot") || id.includes("kimi")) return "moonshot";
|
|
400
|
-
if (id.includes("claude")) return "claude";
|
|
401
|
-
if (id.startsWith("gpt") || id.startsWith("o1") || id.startsWith("o3") || id.includes("openai")) return "codex";
|
|
402
|
-
}
|
|
403
|
-
/**
|
|
404
|
-
* Which engines wear their vendor's colour, and **how far it reaches**.
|
|
405
|
-
*
|
|
406
|
-
* `MARK` is the glyph, `TEXT` is the model name beside it. They are the same for
|
|
407
|
-
* Anthropic and deliberately not for OpenAI, because the two brands differ in
|
|
408
|
-
* kind rather than in hue: coral is an accent, and OpenAI's guidelines forbid
|
|
409
|
-
* adding colour to the mark at all, so theirs is pure white/black. At full
|
|
410
|
-
* contrast that is right on a 12px glyph and wrong on an 11px label — a
|
|
411
|
-
* pure-white model name is *brighter than the session title above it*, which
|
|
412
|
-
* inverts the row's whole hierarchy to say something the mark has already said.
|
|
413
|
-
* So OpenAI's name keeps the muted line it always had, which is also the most
|
|
414
|
-
* literal reading of "don't add any colors".
|
|
415
|
-
*
|
|
416
|
-
* Anything absent falls through to muted on both counts.
|
|
417
|
-
*/
|
|
418
|
-
const VENDOR_MARK = {
|
|
419
|
-
claude: "text-vendor-claude",
|
|
420
|
-
codex: "text-vendor-openai"
|
|
421
|
-
};
|
|
422
|
-
const VENDOR_TEXT = { claude: "text-vendor-claude" };
|
|
423
|
-
/** The colour a session's engine mark wears. Pass it to `EngineIcon`'s
|
|
424
|
-
* `className` rather than to a parent: the svg carries its own `text-fg-3`,
|
|
425
|
-
* and only a class on the element itself is merged over it. */
|
|
426
|
-
function vendorMarkClass(engine, model) {
|
|
427
|
-
return VENDOR_MARK[engineMark(engine, model) ?? ""] ?? "text-fg-3";
|
|
428
|
-
}
|
|
429
|
-
/** The colour the model *name* wears beside that mark — muted unless the vendor
|
|
430
|
-
* is one whose accent survives at label size. See `VENDOR_TEXT`.
|
|
431
|
-
*
|
|
432
|
-
* The fallback is `text-fg-3` rather than the metadata line's own `text-fg-4`,
|
|
433
|
-
* so an unbranded model still sits one step above the project and gateway
|
|
434
|
-
* beside it — the order the spec lists them in, held without a colour. */
|
|
435
|
-
function vendorTextClass(engine, model) {
|
|
436
|
-
return VENDOR_TEXT[engineMark(engine, model) ?? ""] ?? "text-fg-3";
|
|
437
|
-
}
|
|
438
|
-
function EngineIcon({ engine, model, className }) {
|
|
439
|
-
const mark = PATHS[engineMark(engine, model) ?? ""];
|
|
440
|
-
if (!mark) return /* @__PURE__ */ jsx("span", {
|
|
441
|
-
"aria-hidden": true,
|
|
442
|
-
title: engine,
|
|
443
|
-
className: cn("shrink-0 text-fg-3", className),
|
|
444
|
-
children: "·"
|
|
445
|
-
});
|
|
446
|
-
return /* @__PURE__ */ jsxs("svg", {
|
|
447
|
-
viewBox: "0 0 24 24",
|
|
448
|
-
fill: "currentColor",
|
|
449
|
-
fillRule: "evenodd",
|
|
450
|
-
role: "img",
|
|
451
|
-
"aria-label": mark.title,
|
|
452
|
-
className: cn("size-3 shrink-0 text-fg-3", className),
|
|
453
|
-
children: [/* @__PURE__ */ jsx("title", { children: mark.title }), /* @__PURE__ */ jsx("path", { d: mark.d })]
|
|
454
|
-
});
|
|
455
|
-
}
|
|
456
|
-
//#endregion
|
|
457
313
|
//#region src/components/agent/ProjectIcon.tsx
|
|
458
314
|
/**
|
|
459
315
|
* A project's icon — the render side of protocol's `ProjectIcon`, for a list
|
|
@@ -624,21 +480,224 @@ function ProjectIcon({ icon, src, name, className }) {
|
|
|
624
480
|
className: cn("inline-block size-3 shrink-0 object-contain", className)
|
|
625
481
|
});
|
|
626
482
|
}
|
|
627
|
-
return /* @__PURE__ */ jsx(GLYPHS[icon.name] ?? Folder, { className: cn("inline-block size-3 shrink-0", className) });
|
|
483
|
+
return /* @__PURE__ */ jsx(GLYPHS[icon.name] ?? Folder, { className: cn("inline-block size-3 shrink-0", className) });
|
|
484
|
+
}
|
|
485
|
+
//#endregion
|
|
486
|
+
//#region src/components/agent/ContextRing.tsx
|
|
487
|
+
/**
|
|
488
|
+
* How full a session's context window is, sized for a **list row**.
|
|
489
|
+
*
|
|
490
|
+
* The reading the session screen has always drawn, moved somewhere it answers a
|
|
491
|
+
* question the session screen cannot: *which* of these thirty sessions is
|
|
492
|
+
* bloating. That is also why it is a ring and not a number — across a list, a
|
|
493
|
+
* ring is read at a glance and `71%` has to be read one row at a time.
|
|
494
|
+
*
|
|
495
|
+
* **Absent draws nothing, and absent is not zero.** A promptless session, a
|
|
496
|
+
* parked one from before the field existed, or an engine that reports no window
|
|
497
|
+
* has no reading; an empty ring would claim its context is empty rather than
|
|
498
|
+
* unknown. The title carries the exact numbers for anyone who wants them.
|
|
499
|
+
*
|
|
500
|
+
* Shared rather than spelled per client: the dashboard's row and the VS Code
|
|
501
|
+
* sidebar's card both draw it, and two copies would be two sets of thresholds
|
|
502
|
+
* the day one of them is nudged.
|
|
503
|
+
*/
|
|
504
|
+
function ContextRing({ usage, size = 11, className }) {
|
|
505
|
+
if (usage === void 0) return null;
|
|
506
|
+
return /* @__PURE__ */ jsx("span", {
|
|
507
|
+
title: `Context ${usage.percentage.toFixed(0)}% · ${formatTokens(usage.totalTokens)} / ${formatTokens(usage.maxTokens)}`,
|
|
508
|
+
className: cn("shrink-0 leading-none", meterColorClass(usage.percentage), className),
|
|
509
|
+
children: /* @__PURE__ */ jsx(ProgressRing, {
|
|
510
|
+
value: usage.percentage,
|
|
511
|
+
size,
|
|
512
|
+
strokeWidth: 2
|
|
513
|
+
})
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
//#endregion
|
|
517
|
+
//#region src/components/agent/EngineIcon.tsx
|
|
518
|
+
/**
|
|
519
|
+
* The engine's own mark, monochrome.
|
|
520
|
+
*
|
|
521
|
+
* Inlined rather than depended on: the React package these come from
|
|
522
|
+
* (`@lobehub/icons`) pulls antd, `@lobehub/ui` and a second copy of
|
|
523
|
+
* lucide-react — an absurd amount of bundle for a few glyphs in a sidebar.
|
|
524
|
+
* These are paths from `@lobehub/icons-static-svg` (MIT, © LobeHub), already
|
|
525
|
+
* `fill="currentColor"` single-path marks, so they take the row's colour like
|
|
526
|
+
* any other glyph and need no light/dark variants.
|
|
527
|
+
*
|
|
528
|
+
* The marks are trademarks of their owners (Anthropic, OpenAI, Google,
|
|
529
|
+
* DeepSeek, Moonshot), used here only to identify which engine a session runs
|
|
530
|
+
* on.
|
|
531
|
+
*
|
|
532
|
+
* Codex is drawn as OpenAI: the session's engine *is* OpenAI's, and OpenAI's
|
|
533
|
+
* mark is the one a person recognises at 12px.
|
|
534
|
+
*/
|
|
535
|
+
const PATHS = {
|
|
536
|
+
claude: {
|
|
537
|
+
title: "Claude",
|
|
538
|
+
d: "M4.709 15.955l4.72-2.647.08-.23-.08-.128H9.2l-.79-.048-2.698-.073-2.339-.097-2.266-.122-.571-.121L0 11.784l.055-.352.48-.321.686.06 1.52.103 2.278.158 1.652.097 2.449.255h.389l.055-.157-.134-.098-.103-.097-2.358-1.596-2.552-1.688-1.336-.972-.724-.491-.364-.462-.158-1.008.656-.722.881.06.225.061.893.686 1.908 1.476 2.491 1.833.365.304.145-.103.019-.073-.164-.274-1.355-2.446-1.446-2.49-.644-1.032-.17-.619a2.97 2.97 0 01-.104-.729L6.283.134 6.696 0l.996.134.42.364.62 1.414 1.002 2.229 1.555 3.03.456.898.243.832.091.255h.158V9.01l.128-1.706.237-2.095.23-2.695.08-.76.376-.91.747-.492.584.28.48.685-.067.444-.286 1.851-.559 2.903-.364 1.942h.212l.243-.242.985-1.306 1.652-2.064.73-.82.85-.904.547-.431h1.033l.76 1.129-.34 1.166-1.064 1.347-.881 1.142-1.264 1.7-.79 1.36.073.11.188-.02 2.856-.606 1.543-.28 1.841-.315.833.388.091.395-.328.807-1.969.486-2.309.462-3.439.813-.042.03.049.061 1.549.146.662.036h1.622l3.02.225.79.522.474.638-.079.485-1.215.62-1.64-.389-3.829-.91-1.312-.329h-.182v.11l1.093 1.068 2.006 1.81 2.509 2.33.127.578-.322.455-.34-.049-2.205-1.657-.851-.747-1.926-1.62h-.128v.17l.444.649 2.345 3.521.122 1.08-.17.353-.608.213-.668-.122-1.374-1.925-1.415-2.167-1.143-1.943-.14.08-.674 7.254-.316.37-.729.28-.607-.461-.322-.747.322-1.476.389-1.924.315-1.53.286-1.9.17-.632-.012-.042-.14.018-1.434 1.967-2.18 2.945-1.726 1.845-.414.164-.717-.37.067-.662.401-.589 2.388-3.036 1.44-1.882.93-1.086-.006-.158h-.055L4.132 18.56l-1.13.146-.487-.456.061-.746.231-.243 1.908-1.312-.006.006z"
|
|
539
|
+
},
|
|
540
|
+
codex: {
|
|
541
|
+
title: "OpenAI",
|
|
542
|
+
d: "M9.205 8.658v-2.26c0-.19.072-.333.238-.428l4.543-2.616c.619-.357 1.356-.523 2.117-.523 2.854 0 4.662 2.212 4.662 4.566 0 .167 0 .357-.024.547l-4.71-2.759a.797.797 0 00-.856 0l-5.97 3.473zm10.609 8.8V12.06c0-.333-.143-.57-.429-.737l-5.97-3.473 1.95-1.118a.433.433 0 01.476 0l4.543 2.617c1.309.76 2.189 2.378 2.189 3.948 0 1.808-1.07 3.473-2.76 4.163zM7.802 12.703l-1.95-1.142c-.167-.095-.239-.238-.239-.428V5.899c0-2.545 1.95-4.472 4.591-4.472 1 0 1.927.333 2.712.928L8.23 5.067c-.285.166-.428.404-.428.737v6.898zM12 15.128l-2.795-1.57v-3.33L12 8.658l2.795 1.57v3.33L12 15.128zm1.796 7.23c-1 0-1.927-.332-2.712-.927l4.686-2.712c.285-.166.428-.404.428-.737v-6.898l1.974 1.142c.167.095.238.238.238.428v5.233c0 2.545-1.974 4.472-4.614 4.472zm-5.637-5.303l-4.544-2.617c-1.308-.761-2.188-2.378-2.188-3.948A4.482 4.482 0 014.21 6.327v5.423c0 .333.143.571.428.738l5.947 3.449-1.95 1.118a.432.432 0 01-.476 0zm-.262 3.9c-2.688 0-4.662-2.021-4.662-4.519 0-.19.024-.38.047-.57l4.686 2.71c.286.167.571.167.856 0l5.97-3.448v2.26c0 .19-.07.333-.237.428l-4.543 2.616c-.619.357-1.356.523-2.117.523zm5.899 2.83a5.947 5.947 0 005.827-4.756C22.287 18.339 24 15.84 24 13.296c0-1.665-.713-3.282-1.998-4.448.119-.5.19-.999.19-1.498 0-3.401-2.759-5.947-5.946-5.947-.642 0-1.26.095-1.88.31A5.962 5.962 0 0010.205 0a5.947 5.947 0 00-5.827 4.757C1.713 5.447 0 7.945 0 10.49c0 1.666.713 3.283 1.998 4.448-.119.5-.19 1-.19 1.499 0 3.401 2.759 5.946 5.946 5.946.642 0 1.26-.095 1.88-.309a5.96 5.96 0 004.162 1.713z"
|
|
543
|
+
},
|
|
544
|
+
gemini: {
|
|
545
|
+
title: "Gemini",
|
|
546
|
+
d: "M20.616 10.835a14.147 14.147 0 01-4.45-3.001 14.111 14.111 0 01-3.678-6.452.503.503 0 00-.975 0 14.134 14.134 0 01-3.679 6.452 14.155 14.155 0 01-4.45 3.001c-.65.28-1.318.505-2.002.678a.502.502 0 000 .975c.684.172 1.35.397 2.002.677a14.147 14.147 0 014.45 3.001 14.112 14.112 0 013.679 6.453.502.502 0 00.975 0c.172-.685.397-1.351.677-2.003a14.145 14.145 0 013.001-4.45 14.113 14.113 0 016.453-3.678.503.503 0 000-.975 13.245 13.245 0 01-2.003-.678z"
|
|
547
|
+
},
|
|
548
|
+
deepseek: {
|
|
549
|
+
title: "DeepSeek",
|
|
550
|
+
d: "M23.748 4.482c-.254-.124-.364.113-.512.234-.051.039-.094.09-.137.136-.372.397-.806.657-1.373.626-.829-.046-1.537.214-2.163.848-.133-.782-.575-1.248-1.247-1.548-.352-.156-.708-.311-.955-.65-.172-.241-.219-.51-.305-.774-.055-.16-.11-.323-.293-.35-.2-.031-.278.136-.356.276-.313.572-.434 1.202-.422 1.84.027 1.436.633 2.58 1.838 3.393.137.093.172.187.129.323-.082.28-.18.552-.266.833-.055.179-.137.217-.329.14a5.526 5.526 0 01-1.736-1.18c-.857-.828-1.631-1.742-2.597-2.458a11.365 11.365 0 00-.689-.471c-.985-.957.13-1.743.388-1.836.27-.098.093-.432-.779-.428-.872.004-1.67.295-2.687.684a3.055 3.055 0 01-.465.137 9.597 9.597 0 00-2.883-.102c-1.885.21-3.39 1.102-4.497 2.623C.082 8.606-.231 10.684.152 12.85c.403 2.284 1.569 4.175 3.36 5.653 1.858 1.533 3.997 2.284 6.438 2.14 1.482-.085 3.133-.284 4.994-1.86.47.234.962.327 1.78.397.63.059 1.236-.03 1.705-.128.735-.156.684-.837.419-.961-2.155-1.004-1.682-.595-2.113-.926 1.096-1.296 2.746-2.642 3.392-7.003.05-.347.007-.565 0-.845-.004-.17.035-.237.23-.256a4.173 4.173 0 001.545-.475c1.396-.763 1.96-2.015 2.093-3.517.02-.23-.004-.467-.247-.588zM11.581 18c-2.089-1.642-3.102-2.183-3.52-2.16-.392.024-.321.471-.235.763.09.288.207.486.371.739.114.167.192.416-.113.603-.673.416-1.842-.14-1.897-.167-1.361-.802-2.5-1.86-3.301-3.307-.774-1.393-1.224-2.887-1.298-4.482-.02-.386.093-.522.477-.592a4.696 4.696 0 011.529-.039c2.132.312 3.946 1.265 5.468 2.774.868.86 1.525 1.887 2.202 2.891.72 1.066 1.494 2.082 2.48 2.914.348.292.625.514.891.677-.802.09-2.14.11-3.054-.614zm1-6.44a.306.306 0 01.415-.287.302.302 0 01.2.288.306.306 0 01-.31.307.303.303 0 01-.304-.308zm3.11 1.596c-.2.081-.399.151-.59.16a1.245 1.245 0 01-.798-.254c-.274-.23-.47-.358-.552-.758a1.73 1.73 0 01.016-.588c.07-.327-.008-.537-.239-.727-.187-.156-.426-.199-.688-.199a.559.559 0 01-.254-.078c-.11-.054-.2-.19-.114-.358.028-.054.16-.186.192-.21.356-.202.767-.136 1.146.016.352.144.618.408 1.001.782.391.451.462.576.685.914.176.265.336.537.445.848.067.195-.019.354-.25.452z"
|
|
551
|
+
},
|
|
552
|
+
moonshot: {
|
|
553
|
+
title: "Moonshot",
|
|
554
|
+
d: "M1.052 16.916l9.539 2.552a21.007 21.007 0 00.06 2.033l5.956 1.593a11.997 11.997 0 01-5.586.865l-.18-.016-.044-.004-.084-.009-.094-.01a11.605 11.605 0 01-.157-.02l-.107-.014-.11-.016a11.962 11.962 0 01-.32-.051l-.042-.008-.075-.013-.107-.02-.07-.015-.093-.019-.075-.016-.095-.02-.097-.023-.094-.022-.068-.017-.088-.022-.09-.024-.095-.025-.082-.023-.109-.03-.062-.02-.084-.025-.093-.028-.105-.034-.058-.019-.08-.026-.09-.031-.066-.024a6.293 6.293 0 01-.044-.015l-.068-.025-.101-.037-.057-.022-.08-.03-.087-.035-.088-.035-.079-.032-.095-.04-.063-.028-.063-.027a5.655 5.655 0 01-.041-.018l-.066-.03-.103-.047-.052-.024-.096-.046-.062-.03-.084-.04-.086-.044-.093-.047-.052-.027-.103-.055-.057-.03-.058-.032a6.49 6.49 0 01-.046-.026l-.094-.053-.06-.034-.051-.03-.072-.041-.082-.05-.093-.056-.052-.032-.084-.053-.061-.039-.079-.05-.07-.047-.053-.035a7.785 7.785 0 01-.054-.036l-.044-.03-.044-.03a6.066 6.066 0 01-.04-.028l-.057-.04-.076-.054-.069-.05-.074-.054-.056-.042-.076-.057-.076-.059-.086-.067-.045-.035-.064-.052-.074-.06-.089-.073-.046-.039-.046-.039a7.516 7.516 0 01-.043-.037l-.045-.04-.061-.053-.07-.062-.068-.06-.062-.058-.067-.062-.053-.05-.088-.084a13.28 13.28 0 01-.099-.097l-.029-.028-.041-.042-.069-.07-.05-.051-.05-.053a6.457 6.457 0 01-.168-.179l-.08-.088-.062-.07-.071-.08-.042-.049-.053-.062-.058-.068-.046-.056a7.175 7.175 0 01-.027-.033l-.045-.055-.066-.082-.041-.052-.05-.064-.02-.025a11.99 11.99 0 01-1.44-2.402zm-1.02-5.794l11.353 3.037a20.468 20.468 0 00-.469 2.011l10.817 2.894a12.076 12.076 0 01-1.845 2.005L.657 15.923l-.016-.046-.035-.104a11.965 11.965 0 01-.05-.153l-.007-.023a11.896 11.896 0 01-.207-.741l-.03-.126-.018-.08-.021-.097-.018-.081-.018-.09-.017-.084-.018-.094c-.026-.141-.05-.283-.071-.426l-.017-.118-.011-.083-.013-.102a12.01 12.01 0 01-.019-.161l-.005-.047a12.12 12.12 0 01-.034-2.145zm1.593-5.15l11.948 3.196c-.368.605-.705 1.231-1.01 1.875l11.295 3.022c-.142.82-.368 1.612-.668 2.365l-11.55-3.09L.124 10.26l.015-.1.008-.049.01-.067.015-.087.018-.098c.026-.148.056-.295.088-.442l.028-.124.02-.085.024-.097c.022-.09.045-.18.07-.268l.028-.102.023-.083.03-.1.025-.082.03-.096.026-.082.031-.095a11.896 11.896 0 011.01-2.232zm4.442-4.4L17.352 4.59a20.77 20.77 0 00-1.688 1.721l7.823 2.093c.267.852.442 1.744.513 2.665L2.106 5.213l.045-.065.027-.04.04-.055.046-.065.055-.076.054-.072.064-.086.05-.065.057-.073.055-.07.06-.074.055-.069.065-.077.054-.066.066-.077.053-.06.072-.082.053-.06.067-.074.054-.058.073-.078.058-.06.063-.067.168-.17.1-.098.059-.056.076-.071a12.084 12.084 0 012.272-1.677zM12.017 0h.097l.082.001.069.001.054.002.068.002.046.001.076.003.047.002.06.003.054.002.087.005.105.007.144.011.088.007.044.004.077.008.082.008.047.005.102.012.05.006.108.014.081.01.042.006.065.01.207.032.07.012.065.011.14.026.092.018.11.022.046.01.075.016.041.01L14.7.3l.042.01.065.015.049.012.071.017.096.024.112.03.113.03.113.032.05.015.07.02.078.024.073.023.05.016.05.016.076.025.099.033.102.036.048.017.064.023.093.034.11.041.116.045.1.04.047.02.06.024.041.018.063.026.04.018.057.025.11.048.1.046.074.035.075.036.06.028.092.046.091.045.102.052.053.028.049.026.046.024.06.033.041.022.052.029.088.05.106.06.087.051.057.034.053.032.096.059.088.055.098.062.036.024.064.041.084.056.04.027.062.042.062.043.023.017c.054.037.108.075.161.114l.083.06.065.048.056.043.086.065.082.064.04.03.05.041.086.069.079.065.085.071c.712.6 1.353 1.283 1.909 2.031L7.222.994l.062-.027.065-.028.081-.034.086-.035c.113-.045.227-.09.341-.131l.096-.035.093-.033.084-.03.096-.031c.087-.03.176-.058.264-.085l.091-.027.086-.025.102-.03.085-.023.1-.026L9.04.37l.09-.023.091-.022.095-.022.09-.02.098-.021.091-.02.095-.018.092-.018.1-.018.091-.016.098-.017.092-.014.097-.015.092-.013.102-.013.091-.012.105-.012.09-.01.105-.01c.093-.01.186-.018.28-.024l.106-.008.09-.005.11-.006.093-.004.1-.004.097-.002.099-.002.197-.002z"
|
|
555
|
+
}
|
|
556
|
+
};
|
|
557
|
+
/**
|
|
558
|
+
* Which mark a session wears.
|
|
559
|
+
*
|
|
560
|
+
* The two first-party engines are named directly. A `provider` session is
|
|
561
|
+
* whatever the host wired up, so its *model* is the only thing that says whose
|
|
562
|
+
* it is — sniffed loosely on purpose (`gemini-2.5-pro`, `deepseek-chat`,
|
|
563
|
+
* `kimi-k2`), and falling through to no mark rather than guessing wrong.
|
|
564
|
+
*/
|
|
565
|
+
function engineMark(engine, model) {
|
|
566
|
+
if (engine === "claude") return "claude";
|
|
567
|
+
if (engine === "codex") return "codex";
|
|
568
|
+
const id = model?.toLowerCase() ?? "";
|
|
569
|
+
if (!id) return void 0;
|
|
570
|
+
if (id.includes("gemini")) return "gemini";
|
|
571
|
+
if (id.includes("deepseek")) return "deepseek";
|
|
572
|
+
if (id.includes("moonshot") || id.includes("kimi")) return "moonshot";
|
|
573
|
+
if (id.includes("claude")) return "claude";
|
|
574
|
+
if (id.startsWith("gpt") || id.startsWith("o1") || id.startsWith("o3") || id.includes("openai")) return "codex";
|
|
575
|
+
}
|
|
576
|
+
/**
|
|
577
|
+
* Which engines wear their vendor's colour, and **how far it reaches**.
|
|
578
|
+
*
|
|
579
|
+
* `MARK` is the glyph, `TEXT` is the model name beside it. They are the same for
|
|
580
|
+
* Anthropic and deliberately not for OpenAI, because the two brands differ in
|
|
581
|
+
* kind rather than in hue: coral is an accent, and OpenAI's guidelines forbid
|
|
582
|
+
* adding colour to the mark at all, so theirs is pure white/black. At full
|
|
583
|
+
* contrast that is right on a 12px glyph and wrong on an 11px label — a
|
|
584
|
+
* pure-white model name is *brighter than the session title above it*, which
|
|
585
|
+
* inverts the row's whole hierarchy to say something the mark has already said.
|
|
586
|
+
* So OpenAI's name keeps the muted line it always had, which is also the most
|
|
587
|
+
* literal reading of "don't add any colors".
|
|
588
|
+
*
|
|
589
|
+
* Anything absent falls through to muted on both counts.
|
|
590
|
+
*/
|
|
591
|
+
const VENDOR_MARK = {
|
|
592
|
+
claude: "text-vendor-claude",
|
|
593
|
+
codex: "text-vendor-openai"
|
|
594
|
+
};
|
|
595
|
+
const VENDOR_TEXT = { claude: "text-vendor-claude" };
|
|
596
|
+
/** The colour a session's engine mark wears. Pass it to `EngineIcon`'s
|
|
597
|
+
* `className` rather than to a parent: the svg carries its own `text-fg-3`,
|
|
598
|
+
* and only a class on the element itself is merged over it. */
|
|
599
|
+
function vendorMarkClass(engine, model) {
|
|
600
|
+
return VENDOR_MARK[engineMark(engine, model) ?? ""] ?? "text-fg-3";
|
|
601
|
+
}
|
|
602
|
+
/** The colour the model *name* wears beside that mark — muted unless the vendor
|
|
603
|
+
* is one whose accent survives at label size. See `VENDOR_TEXT`.
|
|
604
|
+
*
|
|
605
|
+
* The fallback is `text-fg-3` rather than the metadata line's own `text-fg-4`,
|
|
606
|
+
* so an unbranded model still sits one step above the project and gateway
|
|
607
|
+
* beside it — the order the spec lists them in, held without a colour. */
|
|
608
|
+
function vendorTextClass(engine, model) {
|
|
609
|
+
return VENDOR_TEXT[engineMark(engine, model) ?? ""] ?? "text-fg-3";
|
|
610
|
+
}
|
|
611
|
+
function EngineIcon({ engine, model, className }) {
|
|
612
|
+
const mark = PATHS[engineMark(engine, model) ?? ""];
|
|
613
|
+
if (!mark) return /* @__PURE__ */ jsx("span", {
|
|
614
|
+
"aria-hidden": true,
|
|
615
|
+
title: engine,
|
|
616
|
+
className: cn("shrink-0 text-fg-3", className),
|
|
617
|
+
children: "·"
|
|
618
|
+
});
|
|
619
|
+
return /* @__PURE__ */ jsxs("svg", {
|
|
620
|
+
viewBox: "0 0 24 24",
|
|
621
|
+
fill: "currentColor",
|
|
622
|
+
fillRule: "evenodd",
|
|
623
|
+
role: "img",
|
|
624
|
+
"aria-label": mark.title,
|
|
625
|
+
className: cn("size-3 shrink-0 text-fg-3", className),
|
|
626
|
+
children: [/* @__PURE__ */ jsx("title", { children: mark.title }), /* @__PURE__ */ jsx("path", { d: mark.d })]
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
//#endregion
|
|
630
|
+
//#region src/components/agent/SessionStatusIcon.tsx
|
|
631
|
+
/**
|
|
632
|
+
* State as one glyph — a ringing bell when it wants a human, a spinner while it
|
|
633
|
+
* works, a moon when it is only sleeping. Replaces the text badge: in a sidebar
|
|
634
|
+
* the word costs more room than it earns, and the states that matter are the two
|
|
635
|
+
* you can recognise without reading.
|
|
636
|
+
*
|
|
637
|
+
* **It reads `row.state`, not `info.status`, and that distinction is the whole
|
|
638
|
+
* point of the row model.** `sessionState` already folds in the arm this glyph
|
|
639
|
+
* cannot see for itself: a *background* sub-agent outlives its turn by design,
|
|
640
|
+
* so the turn ends, `status` comes to rest at `idle`, and the agent keeps
|
|
641
|
+
* working. Reading the raw status drew a **moon on a row filed under the
|
|
642
|
+
* "Working" header** — the list contradicting itself on one line, which is
|
|
643
|
+
* exactly what a derived view model exists to prevent. The value was in scope
|
|
644
|
+
* and unread.
|
|
645
|
+
*
|
|
646
|
+
* The terminal statuses still come off `info.status`, because `ended` collapses
|
|
647
|
+
* `failed` and `closed` into one bucket and those are worth telling apart here.
|
|
648
|
+
*
|
|
649
|
+
* It lives in its own file rather than inside the dashboard's browser because
|
|
650
|
+
* the extension's cards draw it too, and a second copy there is how the two
|
|
651
|
+
* lists last disagreed about what a parked session looks like.
|
|
652
|
+
*/
|
|
653
|
+
function SessionStatusIcon({ row, className }) {
|
|
654
|
+
const { info } = row;
|
|
655
|
+
const size = cn("size-4 shrink-0", className);
|
|
656
|
+
if (row.state === "attention") return /* @__PURE__ */ jsx(BellRing, { className: cn(size, "animate-pulse text-warning") });
|
|
657
|
+
if (row.state === "working") return /* @__PURE__ */ jsx(Spinner, { className: cn(size, "text-info") });
|
|
658
|
+
switch (info.status) {
|
|
659
|
+
case "failed": return /* @__PURE__ */ jsx(CircleAlert, { className: cn(size, "text-danger") });
|
|
660
|
+
case "closed": return /* @__PURE__ */ jsx(CircleSlash, { className: cn(size, "text-fg-4") });
|
|
661
|
+
case "parked": return /* @__PURE__ */ jsx(PauseCircle, { className: cn(size, "text-fg-3") });
|
|
662
|
+
default: return /* @__PURE__ */ jsx(Moon, { className: cn(size, "text-fg-4") });
|
|
663
|
+
}
|
|
628
664
|
}
|
|
629
665
|
//#endregion
|
|
630
666
|
//#region src/components/agent/SessionSteps.tsx
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
667
|
+
/**
|
|
668
|
+
* The steps under one session, **agents first**.
|
|
669
|
+
*
|
|
670
|
+
* The two kinds do different things when pressed and are worth different
|
|
671
|
+
* amounts of attention: an agent has work of its own to go and read, a task is a
|
|
672
|
+
* marker in a transcript. Interleaved in dispatch order they read as one
|
|
673
|
+
* undifferentiated list, and the rows you can actually open are scattered
|
|
674
|
+
* through it. Grouped, the openable ones are a block at the top and the markers
|
|
675
|
+
* are a tail you can skip.
|
|
676
|
+
*
|
|
677
|
+
* Stable **within** each group, deliberately: dispatch order is the only order
|
|
678
|
+
* these records have that means anything (it is the order the work was started
|
|
679
|
+
* in), so the sort partitions and never reorders inside a partition.
|
|
680
|
+
*
|
|
681
|
+
* `onSelect` is handed the **kind** as well as the id, because the two kinds go
|
|
682
|
+
* to different places — see `SessionItem`, which is what routes them. Passing
|
|
683
|
+
* only the id is what let a task be opened as if it were an agent, and a framed
|
|
684
|
+
* task id matches no items, so the panel drew an **empty agent view**.
|
|
685
|
+
*/
|
|
686
|
+
function sessionSteps(info, onSelect) {
|
|
687
|
+
const steps = (info.subagents ?? []).map((sub) => {
|
|
688
|
+
const kind = isAgentRecord(sub) ? "agent" : "task";
|
|
689
|
+
return {
|
|
690
|
+
key: sub.toolUseId,
|
|
691
|
+
label: subagentLabel(sub),
|
|
692
|
+
noun: "agent",
|
|
693
|
+
kind,
|
|
694
|
+
state: stepState(sub.status),
|
|
695
|
+
detail: sub.toolCount > 0 ? String(sub.toolCount) : void 0,
|
|
696
|
+
title: `${subagentLabel(sub)} · ${sub.toolCount} tool${sub.toolCount === 1 ? "" : "s"}`,
|
|
697
|
+
onSelect: () => onSelect(sub.toolUseId, kind)
|
|
698
|
+
};
|
|
699
|
+
});
|
|
700
|
+
return [...steps.filter((s) => s.kind === "agent"), ...steps.filter((s) => s.kind === "task")];
|
|
642
701
|
}
|
|
643
702
|
function stepState(status) {
|
|
644
703
|
switch (status) {
|
|
@@ -674,7 +733,7 @@ function StepToggle({ expanded, running, total, noun, onToggle }) {
|
|
|
674
733
|
e.stopPropagation();
|
|
675
734
|
onToggle();
|
|
676
735
|
},
|
|
677
|
-
className: cn("flex shrink-0 items-center gap-0.5 rounded
|
|
736
|
+
className: cn("flex shrink-0 items-center gap-0.5 rounded-[4px] py-0.5 pr-1 pl-0.5 outline-none", "text-[0.75rem] leading-3 hover:bg-row-hover hover:text-fg-2", running > 0 ? "text-info" : "text-fg-4"),
|
|
678
737
|
children: [/* @__PURE__ */ jsx(Chevron, { className: "size-3" }), /* @__PURE__ */ jsx("span", {
|
|
679
738
|
className: "tabular-nums",
|
|
680
739
|
children: label
|
|
@@ -682,64 +741,276 @@ function StepToggle({ expanded, running, total, noun, onToggle }) {
|
|
|
682
741
|
});
|
|
683
742
|
}
|
|
684
743
|
/**
|
|
685
|
-
* One step under its session
|
|
686
|
-
*
|
|
687
|
-
*
|
|
688
|
-
*
|
|
689
|
-
*
|
|
690
|
-
*
|
|
744
|
+
* One step under its session — **pressable, all of them**, and what a press
|
|
745
|
+
* means is what tells the two kinds apart.
|
|
746
|
+
*
|
|
747
|
+
* Pressing an **agent** hands the panel over to that agent's own work
|
|
748
|
+
* (`SessionPanel.openSubagent`): it is not a session and never becomes one, but
|
|
749
|
+
* it has a surface, so it can be the selected thing. Pressing a **task** selects
|
|
750
|
+
* the *session* and travels to that task's marker inside it — a task is a
|
|
751
|
+
* reference to a place in a transcript, not a thing with a screen, so it can be
|
|
752
|
+
* followed but never held.
|
|
691
753
|
*
|
|
692
|
-
*
|
|
693
|
-
*
|
|
694
|
-
*
|
|
695
|
-
*
|
|
754
|
+
* That is a reversal, and a deliberate one. A task used to be inert markup on
|
|
755
|
+
* the argument that "a disabled-looking button still announces itself as one" —
|
|
756
|
+
* correct about the markup, wrong about the premise, because there *was* always
|
|
757
|
+
* somewhere to go and the row simply swallowed the click on its way there. A row
|
|
758
|
+
* that looks like a list item, sits in a list, and does nothing when pressed is
|
|
759
|
+
* the worse lie. So every step answers the pointer and every step answers a
|
|
760
|
+
* press; only what the press does differs.
|
|
761
|
+
*
|
|
762
|
+
* Divided from the card's header by **indentation and its own hit shape**, not
|
|
763
|
+
* by a rule. The rules came first, on the argument that at 11px an indent is not
|
|
764
|
+
* enough to say "list inside a row" — and they were right about the reading and
|
|
765
|
+
* wrong about the cost: a stack of hairlines across every open card turned the
|
|
766
|
+
* list into a ledger, and a rule cannot answer a pointer. A step that lights up
|
|
767
|
+
* under the cursor and fills when it is the one on screen says *list* far more
|
|
768
|
+
* plainly than a line between two of them, and it says it while doing the job
|
|
769
|
+
* the rule could not.
|
|
770
|
+
*
|
|
771
|
+
* **Only an agent can wear the selection**, and `active` is guarded on that here
|
|
772
|
+
* rather than trusted from the caller: a host that hands back a task's key is
|
|
773
|
+
* describing where it navigated, not what it selected, and the row must not
|
|
774
|
+
* paint itself blue for it.
|
|
775
|
+
*
|
|
776
|
+
* The hover is `--row-active` — **alpha, not a flat fill** — because this row
|
|
777
|
+
* has to answer the pointer on three different grounds: a transparent card, a
|
|
778
|
+
* blue one (its session is selected), and a grey one (a sibling agent is). A
|
|
779
|
+
* flat value tuned for any of those is wrong on the other two; a tint darkens or
|
|
780
|
+
* lifts whatever it lands on. It is the one place in the list where the alpha
|
|
781
|
+
* token earns its keep.
|
|
696
782
|
*/
|
|
697
|
-
function StepRow({ step, onSelect }) {
|
|
783
|
+
function StepRow({ step, active = false, onSelect }) {
|
|
698
784
|
const agent = step.kind === "agent";
|
|
785
|
+
const selected = active && agent;
|
|
699
786
|
const body = step.state === "failed" ? "text-danger" : agent ? "text-success" : "text-fg-4";
|
|
700
|
-
|
|
701
|
-
/* @__PURE__ */ jsx(StepIcon, {
|
|
702
|
-
state: step.state,
|
|
703
|
-
kind: step.kind
|
|
704
|
-
}),
|
|
705
|
-
/* @__PURE__ */ jsx("span", {
|
|
706
|
-
className: "min-w-0 flex-1 truncate",
|
|
707
|
-
children: step.label
|
|
708
|
-
}),
|
|
709
|
-
step.detail ? /* @__PURE__ */ jsx("span", {
|
|
710
|
-
className: "shrink-0 tabular-nums text-fg-4",
|
|
711
|
-
children: step.detail
|
|
712
|
-
}) : null,
|
|
713
|
-
agent ? /* @__PURE__ */ jsx(ArrowRight, { className: "size-3 shrink-0 opacity-60" }) : null
|
|
714
|
-
] });
|
|
715
|
-
const shape = "flex w-full items-center gap-2 border-t border-black/25 py-1 pl-3 pr-2 text-left text-label outline-none";
|
|
716
|
-
if (!agent) return /* @__PURE__ */ jsx("div", {
|
|
717
|
-
title: step.title,
|
|
718
|
-
onClick: (e) => e.stopPropagation(),
|
|
719
|
-
className: cn(shape, body),
|
|
720
|
-
children: content
|
|
721
|
-
});
|
|
722
|
-
return /* @__PURE__ */ jsx("button", {
|
|
787
|
+
return /* @__PURE__ */ jsxs("button", {
|
|
723
788
|
type: "button",
|
|
724
789
|
title: step.title,
|
|
790
|
+
"aria-current": selected || void 0,
|
|
725
791
|
onClick: (e) => {
|
|
726
792
|
e.stopPropagation();
|
|
727
793
|
onSelect();
|
|
728
794
|
},
|
|
729
|
-
className: cn(
|
|
730
|
-
children:
|
|
795
|
+
className: cn("flex w-full items-center gap-1.5 rounded-[4px] py-1 pr-2.5 pl-3.5", "text-left text-micro outline-none", selected ? "bg-row-selected" : "hover:bg-row-active", body),
|
|
796
|
+
children: [
|
|
797
|
+
/* @__PURE__ */ jsx(StepIcon, {
|
|
798
|
+
state: step.state,
|
|
799
|
+
kind: step.kind
|
|
800
|
+
}),
|
|
801
|
+
/* @__PURE__ */ jsx("span", {
|
|
802
|
+
className: "min-w-0 flex-1 truncate",
|
|
803
|
+
children: step.label
|
|
804
|
+
}),
|
|
805
|
+
step.detail ? /* @__PURE__ */ jsx("span", {
|
|
806
|
+
className: "shrink-0 tabular-nums text-fg-4",
|
|
807
|
+
children: step.detail
|
|
808
|
+
}) : null,
|
|
809
|
+
agent ? /* @__PURE__ */ jsx(ArrowRight, { className: "size-3.5 shrink-0 text-fg-4" }) : null
|
|
810
|
+
]
|
|
731
811
|
});
|
|
732
812
|
}
|
|
733
813
|
function StepIcon({ state, kind }) {
|
|
734
|
-
if (kind === "task" && state !== "running" && state !== "failed") return /* @__PURE__ */ jsx(Dot, { className: "size-
|
|
814
|
+
if (kind === "task" && state !== "running" && state !== "failed") return /* @__PURE__ */ jsx(Dot, { className: "size-[11px] shrink-0" });
|
|
735
815
|
switch (state) {
|
|
736
|
-
case "running": return /* @__PURE__ */ jsx(Spinner, { className: "size-
|
|
737
|
-
case "failed": return /* @__PURE__ */ jsx(CircleAlert, { className: "size-
|
|
738
|
-
case "pending": return /* @__PURE__ */ jsx(PauseCircle, { className: "size-
|
|
739
|
-
default: return /* @__PURE__ */ jsx(Check, { className: "size-
|
|
816
|
+
case "running": return /* @__PURE__ */ jsx(Spinner, { className: "size-[11px] shrink-0" });
|
|
817
|
+
case "failed": return /* @__PURE__ */ jsx(CircleAlert, { className: "size-[11px] shrink-0" });
|
|
818
|
+
case "pending": return /* @__PURE__ */ jsx(PauseCircle, { className: "size-[11px] shrink-0" });
|
|
819
|
+
default: return /* @__PURE__ */ jsx(Check, { className: "size-[11px] shrink-0" });
|
|
740
820
|
}
|
|
741
821
|
}
|
|
742
822
|
//#endregion
|
|
823
|
+
//#region src/components/agent/SessionItem.tsx
|
|
824
|
+
function SessionItem({ row, active = false, activeStepKey, showGateway, showProject = true, projectIcons, expanded, onExpandedChange, onSelect, onSelectSubagent, onRevealStep, onRename, renameOn = "doubleClick", editing, onEditingChange, actions, className }) {
|
|
825
|
+
const { info } = row;
|
|
826
|
+
const [ownExpanded, setOwnExpanded] = useState(false);
|
|
827
|
+
const open = expanded ?? ownExpanded;
|
|
828
|
+
const setOpen = (next) => {
|
|
829
|
+
setOwnExpanded(next);
|
|
830
|
+
onExpandedChange?.(next);
|
|
831
|
+
};
|
|
832
|
+
const [ownEditing, setOwnEditing] = useState(false);
|
|
833
|
+
const isEditing = editing ?? ownEditing;
|
|
834
|
+
const setEditing = (next) => {
|
|
835
|
+
setOwnEditing(next);
|
|
836
|
+
onEditingChange?.(next);
|
|
837
|
+
};
|
|
838
|
+
const engine = info.engine ?? "claude";
|
|
839
|
+
const project = showProject ? projectLabel(row) : projectSubpath(row);
|
|
840
|
+
const projectIcon = showProject ? info.project?.icon : void 0;
|
|
841
|
+
const iconSrc = projectIcon?.type === "image" ? projectIcons?.[projectIcon.hash] : void 0;
|
|
842
|
+
const cost = formatCost(info.totalCostUsd);
|
|
843
|
+
const extras = [
|
|
844
|
+
showGateway ? row.hostName : void 0,
|
|
845
|
+
info.profile ? `@${info.profile}` : void 0,
|
|
846
|
+
cost === "—" ? void 0 : cost
|
|
847
|
+
].filter((part) => Boolean(part));
|
|
848
|
+
const model = friendlyModel(info.model);
|
|
849
|
+
const parts = [];
|
|
850
|
+
if (model) parts.push(/* @__PURE__ */ jsx("span", {
|
|
851
|
+
className: vendorTextClass(engine, info.model),
|
|
852
|
+
children: model
|
|
853
|
+
}, "model"));
|
|
854
|
+
if (project !== void 0) parts.push(/* @__PURE__ */ jsxs("span", { children: [/* @__PURE__ */ jsx(ProjectIcon, {
|
|
855
|
+
icon: projectIcon,
|
|
856
|
+
src: iconSrc,
|
|
857
|
+
name: project,
|
|
858
|
+
className: "mr-1.5 size-4 align-[-0.3em]"
|
|
859
|
+
}), project] }, "project"));
|
|
860
|
+
for (const extra of extras) parts.push(/* @__PURE__ */ jsx("span", { children: extra }, extra));
|
|
861
|
+
const steps = sessionSteps(info, (toolUseId, kind) => {
|
|
862
|
+
if (kind === "agent") return onSelectSubagent ? onSelectSubagent(toolUseId) : onSelect?.();
|
|
863
|
+
return onRevealStep ? onRevealStep(toolUseId) : onSelect?.();
|
|
864
|
+
});
|
|
865
|
+
const holdsOpenAgent = steps.some((s) => s.kind === "agent" && s.key === activeStepKey);
|
|
866
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
867
|
+
"data-slot": "session-item",
|
|
868
|
+
"data-active": active || void 0,
|
|
869
|
+
role: "button",
|
|
870
|
+
tabIndex: 0,
|
|
871
|
+
onClick: (e) => {
|
|
872
|
+
if (e.detail > 1 || isEditing) return;
|
|
873
|
+
onSelect?.();
|
|
874
|
+
},
|
|
875
|
+
onKeyDown: (e) => {
|
|
876
|
+
if (e.target !== e.currentTarget) return;
|
|
877
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
878
|
+
e.preventDefault();
|
|
879
|
+
onSelect?.();
|
|
880
|
+
}
|
|
881
|
+
},
|
|
882
|
+
className: cn("group flex w-full cursor-pointer flex-col p-1 text-left outline-none", "rounded-[4px] transition-colors focus-visible:ring-2 focus-visible:ring-ring", holdsOpenAgent ? "bg-row-selected-weak" : active ? "bg-row-selected" : "hover:bg-row-hover", className),
|
|
883
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
884
|
+
className: "flex flex-col gap-1 py-0.5 pr-0.5 pl-1.5",
|
|
885
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
886
|
+
className: "flex h-5 items-center gap-1.5 overflow-hidden",
|
|
887
|
+
children: [
|
|
888
|
+
/* @__PURE__ */ jsx(Gutter, { children: /* @__PURE__ */ jsx(SessionStatusIcon, { row }) }),
|
|
889
|
+
isEditing && onRename ? /* @__PURE__ */ jsx(NameEditor, {
|
|
890
|
+
initial: info.title ?? "",
|
|
891
|
+
onCommit: (title) => {
|
|
892
|
+
setEditing(false);
|
|
893
|
+
if (title !== (info.title ?? "")) onRename(title);
|
|
894
|
+
},
|
|
895
|
+
onCancel: () => setEditing(false)
|
|
896
|
+
}) : /* @__PURE__ */ jsx("span", {
|
|
897
|
+
onDoubleClick: onRename && renameOn === "doubleClick" ? (e) => {
|
|
898
|
+
e.stopPropagation();
|
|
899
|
+
setEditing(true);
|
|
900
|
+
} : void 0,
|
|
901
|
+
className: "min-w-0 flex-1 truncate text-body-sm font-medium tracking-[-0.005em] text-fg-1",
|
|
902
|
+
children: sessionLabel(info)
|
|
903
|
+
}),
|
|
904
|
+
row.unseen > 0 ? /* @__PURE__ */ jsx("span", {
|
|
905
|
+
title: `${row.unseen} new`,
|
|
906
|
+
className: cn("flex h-4 min-w-6 shrink-0 items-center justify-center rounded-full px-2", "text-[0.75rem] leading-none tracking-[-0.005em] tabular-nums", row.state === "working" || row.state === "attention" ? "bg-accent text-accent-fg" : "bg-badge text-badge-fg"),
|
|
907
|
+
children: row.unseen
|
|
908
|
+
}) : null,
|
|
909
|
+
/* @__PURE__ */ jsx(ContextRing, {
|
|
910
|
+
usage: info.contextUsage,
|
|
911
|
+
size: 16,
|
|
912
|
+
className: "p-0.5"
|
|
913
|
+
})
|
|
914
|
+
]
|
|
915
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
916
|
+
className: "flex h-5 items-center gap-1.5 overflow-hidden text-body-sm tracking-[-0.005em]",
|
|
917
|
+
children: [
|
|
918
|
+
/* @__PURE__ */ jsx(Gutter, { children: /* @__PURE__ */ jsx(EngineIcon, {
|
|
919
|
+
engine,
|
|
920
|
+
model: info.model,
|
|
921
|
+
className: cn("size-4", vendorMarkClass(engine, info.model))
|
|
922
|
+
}) }),
|
|
923
|
+
/* @__PURE__ */ jsx("span", {
|
|
924
|
+
className: "min-w-0 truncate text-fg-4",
|
|
925
|
+
children: parts.map((part, i) => /* @__PURE__ */ jsxs(Fragment, { children: [i > 0 ? " · " : "", part] }, i))
|
|
926
|
+
}),
|
|
927
|
+
/* @__PURE__ */ jsxs("span", {
|
|
928
|
+
className: "shrink-0 text-fg-4",
|
|
929
|
+
children: [parts.length > 0 ? "· " : "", formatRelativeTime(info.lastActivityAt ?? info.createdAt)]
|
|
930
|
+
}),
|
|
931
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1" }),
|
|
932
|
+
steps.length > 0 ? /* @__PURE__ */ jsx(StepToggle, {
|
|
933
|
+
expanded: open,
|
|
934
|
+
running: runningSteps(steps),
|
|
935
|
+
total: steps.length,
|
|
936
|
+
noun: steps[0].noun,
|
|
937
|
+
onToggle: () => setOpen(!open)
|
|
938
|
+
}) : null,
|
|
939
|
+
actions
|
|
940
|
+
]
|
|
941
|
+
})]
|
|
942
|
+
}), open && steps.length > 0 ? /* @__PURE__ */ jsx("div", {
|
|
943
|
+
className: "flex flex-col",
|
|
944
|
+
children: steps.map((step) => /* @__PURE__ */ jsx(StepRow, {
|
|
945
|
+
step,
|
|
946
|
+
active: step.key === activeStepKey,
|
|
947
|
+
onSelect: step.onSelect
|
|
948
|
+
}, step.key))
|
|
949
|
+
}) : null]
|
|
950
|
+
});
|
|
951
|
+
}
|
|
952
|
+
/**
|
|
953
|
+
* The one 16px cell both lines hang their glyph in.
|
|
954
|
+
*
|
|
955
|
+
* A shared column rather than a leading character: the state and the engine mark
|
|
956
|
+
* are different widths and a bare glyph would let the two text columns start at
|
|
957
|
+
* different x. Centring inside a fixed cell is what makes them agree.
|
|
958
|
+
*/
|
|
959
|
+
function Gutter({ children }) {
|
|
960
|
+
return /* @__PURE__ */ jsx("span", {
|
|
961
|
+
className: "flex size-4 shrink-0 items-center justify-center",
|
|
962
|
+
children
|
|
963
|
+
});
|
|
964
|
+
}
|
|
965
|
+
/**
|
|
966
|
+
* Enter commits, Escape cancels, blur commits — the editor's inline-rename feel.
|
|
967
|
+
* An empty value is a deliberate "clear the name", which the gateway answers by
|
|
968
|
+
* handing back the derived title.
|
|
969
|
+
*
|
|
970
|
+
* The blur rule needs one guard. Selecting a session focuses a different surface
|
|
971
|
+
* — in the extension, a different VS Code view — so this document can lose focus
|
|
972
|
+
* a tick after a double-click opened the editor, and an unguarded blur reads
|
|
973
|
+
* that as "the user clicked away" and closes the editor in the frame it
|
|
974
|
+
* appeared. `document.hasFocus()` tells the two apart: focus moving WITHIN this
|
|
975
|
+
* document is the user leaving the field; the whole view losing focus is not.
|
|
976
|
+
* When the window comes back, so does the caret.
|
|
977
|
+
*/
|
|
978
|
+
function NameEditor({ initial, onCommit, onCancel }) {
|
|
979
|
+
const [value, setValue] = useState(initial);
|
|
980
|
+
const ref = useRef(null);
|
|
981
|
+
useEffect(() => {
|
|
982
|
+
ref.current?.focus();
|
|
983
|
+
ref.current?.select();
|
|
984
|
+
}, []);
|
|
985
|
+
useEffect(() => {
|
|
986
|
+
const onWindowFocus = () => {
|
|
987
|
+
ref.current?.focus();
|
|
988
|
+
ref.current?.select();
|
|
989
|
+
};
|
|
990
|
+
window.addEventListener("focus", onWindowFocus);
|
|
991
|
+
return () => window.removeEventListener("focus", onWindowFocus);
|
|
992
|
+
}, []);
|
|
993
|
+
return /* @__PURE__ */ jsx("input", {
|
|
994
|
+
ref,
|
|
995
|
+
value,
|
|
996
|
+
spellCheck: false,
|
|
997
|
+
placeholder: "Session name",
|
|
998
|
+
"aria-label": "Session name",
|
|
999
|
+
onClick: (e) => e.stopPropagation(),
|
|
1000
|
+
onDoubleClick: (e) => e.stopPropagation(),
|
|
1001
|
+
onChange: (e) => setValue(e.target.value),
|
|
1002
|
+
onBlur: () => {
|
|
1003
|
+
if (document.hasFocus()) onCommit(value.trim());
|
|
1004
|
+
},
|
|
1005
|
+
onKeyDown: (e) => {
|
|
1006
|
+
e.stopPropagation();
|
|
1007
|
+
if (e.key === "Enter") onCommit(value.trim());
|
|
1008
|
+
else if (e.key === "Escape") onCancel();
|
|
1009
|
+
},
|
|
1010
|
+
className: cn("-my-0.5 min-w-0 flex-1 rounded-sm border border-ring bg-bg px-1 py-px", "text-body-sm leading-5 text-fg-1 outline-none")
|
|
1011
|
+
});
|
|
1012
|
+
}
|
|
1013
|
+
//#endregion
|
|
743
1014
|
//#region src/components/agent/SessionBrowser.tsx
|
|
744
1015
|
/**
|
|
745
1016
|
* How a list row is drawn, in one place, so `SidebarRow` in `web` matches this
|
|
@@ -760,7 +1031,7 @@ function StepIcon({ state, kind }) {
|
|
|
760
1031
|
function rowShapeClass(active) {
|
|
761
1032
|
return cn("px-2 py-1.5 hover:bg-row-hover", active ? "mr-1 ml-0 rounded-r-md border-l-4 border-l-accent" : "mx-1 rounded-md");
|
|
762
1033
|
}
|
|
763
|
-
function SessionBrowser({ rows, config, onConfigChange, scope, activeId, onSelect, onDelete, onRename, onClearContext, onSelectSubagent, emptyState, showControls = true, projectIcons, className }) {
|
|
1034
|
+
function SessionBrowser({ rows, config, onConfigChange, scope, activeId, activeSubagentId, onSelect, onDelete, onRename, onClearContext, onSelectSubagent, onRevealStep, emptyState, showControls = true, projectIcons, className }) {
|
|
764
1035
|
const visible = useMemo(() => filterRows(rows, config, scope), [
|
|
765
1036
|
rows,
|
|
766
1037
|
config,
|
|
@@ -937,11 +1208,11 @@ function SessionBrowser({ rows, config, onConfigChange, scope, activeId, onSelec
|
|
|
937
1208
|
title: "Nothing here",
|
|
938
1209
|
description: "No session to show."
|
|
939
1210
|
}) : /* @__PURE__ */ jsx("div", {
|
|
940
|
-
className: "flex flex-col gap-4",
|
|
1211
|
+
className: "flex flex-col gap-4 px-1",
|
|
941
1212
|
children: groups.map((group) => /* @__PURE__ */ jsxs("div", {
|
|
942
1213
|
className: "flex flex-col gap-1",
|
|
943
1214
|
children: [config.groupBy !== "none" && group.label ? /* @__PURE__ */ jsxs("div", {
|
|
944
|
-
className: "flex items-center gap-2 px-
|
|
1215
|
+
className: "flex items-center gap-2 px-2 text-label font-medium text-fg-4",
|
|
945
1216
|
children: [
|
|
946
1217
|
config.groupBy === "project" ? /* @__PURE__ */ jsx(ProjectIcon, {
|
|
947
1218
|
icon: group.rows[0]?.info.project?.icon,
|
|
@@ -960,6 +1231,7 @@ function SessionBrowser({ rows, config, onConfigChange, scope, activeId, onSelec
|
|
|
960
1231
|
}) : null, group.rows.map((row) => /* @__PURE__ */ jsx(SessionRowItem, {
|
|
961
1232
|
row,
|
|
962
1233
|
active: row.info.id === activeId,
|
|
1234
|
+
activeSubagentId,
|
|
963
1235
|
showGateway: gateways.length > 1 && config.groupBy !== "gateway",
|
|
964
1236
|
showProject: config.groupBy !== "project",
|
|
965
1237
|
projectIcons,
|
|
@@ -967,7 +1239,8 @@ function SessionBrowser({ rows, config, onConfigChange, scope, activeId, onSelec
|
|
|
967
1239
|
onDelete,
|
|
968
1240
|
onRename,
|
|
969
1241
|
onClearContext,
|
|
970
|
-
onSelectSubagent
|
|
1242
|
+
onSelectSubagent,
|
|
1243
|
+
onRevealStep
|
|
971
1244
|
}, `${row.hostId}:${row.info.id}`))]
|
|
972
1245
|
}, group.key))
|
|
973
1246
|
})
|
|
@@ -981,202 +1254,65 @@ function iconSrcOf(row, icons) {
|
|
|
981
1254
|
const icon = row?.info.project?.icon;
|
|
982
1255
|
return icon?.type === "image" ? icons?.[icon.hash] : void 0;
|
|
983
1256
|
}
|
|
984
|
-
function SessionRowItem({ row, active, showGateway, showProject = true, projectIcons, onSelect, onDelete, onRename, onClearContext, onSelectSubagent }) {
|
|
1257
|
+
function SessionRowItem({ row, active, activeSubagentId, showGateway, showProject = true, projectIcons, onSelect, onDelete, onRename, onClearContext, onSelectSubagent, onRevealStep }) {
|
|
985
1258
|
const { info } = row;
|
|
986
1259
|
const [editing, setEditing] = useState(false);
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
children: sessionLabel(info)
|
|
1020
|
-
}),
|
|
1021
|
-
row.unseen > 0 ? /* @__PURE__ */ jsx("span", {
|
|
1022
|
-
title: `${row.unseen} new`,
|
|
1023
|
-
className: "shrink-0 rounded-full bg-accent px-1.5 text-label text-accent-fg",
|
|
1024
|
-
children: row.unseen
|
|
1025
|
-
}) : null,
|
|
1026
|
-
/* @__PURE__ */ jsx("span", {
|
|
1027
|
-
className: "shrink-0 text-label text-fg-4",
|
|
1028
|
-
children: formatRelativeTime(info.lastActivityAt ?? info.createdAt)
|
|
1029
|
-
}),
|
|
1030
|
-
/* @__PURE__ */ jsx(ContextRing, { usage: info.contextUsage })
|
|
1031
|
-
]
|
|
1032
|
-
}),
|
|
1033
|
-
/* @__PURE__ */ jsxs("div", {
|
|
1034
|
-
className: "flex items-center gap-1 text-label text-fg-4",
|
|
1035
|
-
children: [
|
|
1036
|
-
/* @__PURE__ */ jsx(Gutter, { children: /* @__PURE__ */ jsx(EngineIcon, {
|
|
1037
|
-
engine,
|
|
1038
|
-
model: info.model,
|
|
1039
|
-
className: vendorMarkClass(engine, info.model)
|
|
1040
|
-
}) }),
|
|
1041
|
-
/* @__PURE__ */ jsxs("button", {
|
|
1042
|
-
type: "button",
|
|
1043
|
-
tabIndex: -1,
|
|
1044
|
-
className: "min-w-0 flex-1 truncate text-left font-mono outline-none",
|
|
1045
|
-
children: [
|
|
1046
|
-
/* @__PURE__ */ jsx("span", {
|
|
1047
|
-
className: vendorTextClass(engine, info.model),
|
|
1048
|
-
children: friendlyModel(info.model)
|
|
1049
|
-
}),
|
|
1050
|
-
details.length > 0 ? " · " : "",
|
|
1051
|
-
cut < 0 ? details.join(" · ") : /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
1052
|
-
details.slice(0, cut).map((part) => `${part} · `),
|
|
1053
|
-
/* @__PURE__ */ jsx(ProjectIcon, {
|
|
1054
|
-
icon: projectIcon,
|
|
1055
|
-
src: iconSrcOf(row, projectIcons),
|
|
1056
|
-
name: project,
|
|
1057
|
-
className: "mr-1 align-[-0.2em]"
|
|
1058
|
-
}),
|
|
1059
|
-
details.slice(cut).join(" · ")
|
|
1060
|
-
] })
|
|
1061
|
-
]
|
|
1062
|
-
}),
|
|
1063
|
-
steps.length > 0 ? /* @__PURE__ */ jsx(StepToggle, {
|
|
1064
|
-
expanded,
|
|
1065
|
-
running: runningSteps(steps),
|
|
1066
|
-
total: steps.length,
|
|
1067
|
-
noun: steps[0].noun,
|
|
1068
|
-
onToggle: () => setExpanded((v) => !v)
|
|
1069
|
-
}) : null,
|
|
1070
|
-
onRename && !editing ? /* @__PURE__ */ jsx(Button, {
|
|
1071
|
-
variant: "ghost",
|
|
1072
|
-
size: "icon-sm",
|
|
1073
|
-
"aria-label": "Rename session",
|
|
1074
|
-
className: "size-5 shrink-0 opacity-0 transition-opacity group-hover:opacity-100",
|
|
1075
|
-
onClick: (e) => {
|
|
1076
|
-
e.stopPropagation();
|
|
1077
|
-
setEditing(true);
|
|
1078
|
-
},
|
|
1079
|
-
children: /* @__PURE__ */ jsx(Pencil, { className: "size-3 text-fg-3" })
|
|
1080
|
-
}) : null,
|
|
1081
|
-
onClearContext && info.capabilities?.clearContext ? /* @__PURE__ */ jsx(Button, {
|
|
1082
|
-
variant: "ghost",
|
|
1083
|
-
size: "icon-sm",
|
|
1084
|
-
"aria-label": "Clear context",
|
|
1085
|
-
title: "Clear the conversation — the session keeps running and the old conversation stays resumable",
|
|
1086
|
-
className: "size-5 shrink-0 opacity-0 transition-opacity group-hover:opacity-100",
|
|
1087
|
-
onClick: (e) => {
|
|
1088
|
-
e.stopPropagation();
|
|
1089
|
-
onClearContext(row);
|
|
1090
|
-
},
|
|
1091
|
-
children: /* @__PURE__ */ jsx(Eraser, { className: "size-3 text-fg-3" })
|
|
1092
|
-
}) : null,
|
|
1093
|
-
onDelete ? /* @__PURE__ */ jsx(Button, {
|
|
1094
|
-
variant: "ghost",
|
|
1095
|
-
size: "icon-sm",
|
|
1096
|
-
"aria-label": "Close session",
|
|
1097
|
-
className: "size-5 shrink-0 opacity-0 transition-opacity group-hover:opacity-100",
|
|
1098
|
-
onClick: (e) => {
|
|
1099
|
-
e.stopPropagation();
|
|
1100
|
-
onDelete(row);
|
|
1101
|
-
},
|
|
1102
|
-
children: /* @__PURE__ */ jsx(Trash2, { className: "size-3 text-fg-3" })
|
|
1103
|
-
}) : null
|
|
1104
|
-
]
|
|
1105
|
-
}),
|
|
1106
|
-
expanded ? steps.map((step) => /* @__PURE__ */ jsx(StepRow, {
|
|
1107
|
-
step,
|
|
1108
|
-
onSelect: step.onSelect
|
|
1109
|
-
}, step.key)) : null
|
|
1110
|
-
]
|
|
1111
|
-
});
|
|
1112
|
-
}
|
|
1113
|
-
/**
|
|
1114
|
-
* The one 14px cell both lines hang their glyph in.
|
|
1115
|
-
*
|
|
1116
|
-
* A shared column rather than a leading character: the state and the engine mark
|
|
1117
|
-
* are different widths and a bare glyph would let the two text columns start at
|
|
1118
|
-
* different x. Centring inside a fixed cell is what makes them agree —
|
|
1119
|
-
* `packages/ui`'s terminal-gutter argument at row scale.
|
|
1120
|
-
*/
|
|
1121
|
-
function Gutter({ children }) {
|
|
1122
|
-
return /* @__PURE__ */ jsx("span", {
|
|
1123
|
-
className: "flex w-3.5 shrink-0 items-center justify-center",
|
|
1124
|
-
children
|
|
1260
|
+
return /* @__PURE__ */ jsx(SessionItem, {
|
|
1261
|
+
row,
|
|
1262
|
+
active: active === true,
|
|
1263
|
+
activeStepKey: activeSubagentId,
|
|
1264
|
+
showGateway,
|
|
1265
|
+
showProject,
|
|
1266
|
+
projectIcons,
|
|
1267
|
+
onSelect: () => onSelect?.(row),
|
|
1268
|
+
onSelectSubagent: onSelectSubagent ? (id) => onSelectSubagent(row, id) : void 0,
|
|
1269
|
+
onRevealStep: onRevealStep ? (id) => onRevealStep(row, id) : void 0,
|
|
1270
|
+
onRename: onRename ? (title) => onRename(row, title) : void 0,
|
|
1271
|
+
renameOn: "external",
|
|
1272
|
+
editing,
|
|
1273
|
+
onEditingChange: setEditing,
|
|
1274
|
+
actions: /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
1275
|
+
onRename && !editing ? /* @__PURE__ */ jsx(RowAction, {
|
|
1276
|
+
label: "Rename session",
|
|
1277
|
+
onClick: () => setEditing(true),
|
|
1278
|
+
children: /* @__PURE__ */ jsx(Pencil, { className: "size-3 text-fg-3" })
|
|
1279
|
+
}) : null,
|
|
1280
|
+
onClearContext && info.capabilities?.clearContext ? /* @__PURE__ */ jsx(RowAction, {
|
|
1281
|
+
label: "Clear context",
|
|
1282
|
+
title: "Clear the conversation — the session keeps running and the old conversation stays resumable",
|
|
1283
|
+
onClick: () => onClearContext(row),
|
|
1284
|
+
children: /* @__PURE__ */ jsx(Eraser, { className: "size-3 text-fg-3" })
|
|
1285
|
+
}) : null,
|
|
1286
|
+
onDelete ? /* @__PURE__ */ jsx(RowAction, {
|
|
1287
|
+
label: "Close session",
|
|
1288
|
+
onClick: () => onDelete(row),
|
|
1289
|
+
children: /* @__PURE__ */ jsx(Trash2, { className: "size-3 text-fg-3" })
|
|
1290
|
+
}) : null
|
|
1291
|
+
] })
|
|
1125
1292
|
});
|
|
1126
1293
|
}
|
|
1127
1294
|
/**
|
|
1128
|
-
*
|
|
1129
|
-
* works, a moon when it is only sleeping. Replaces the text badge: in a sidebar
|
|
1130
|
-
* the word costs more room than it earns, and the states that matter are the two
|
|
1131
|
-
* you can recognise without reading.
|
|
1295
|
+
* One of the dashboard's hover actions.
|
|
1132
1296
|
*
|
|
1133
|
-
*
|
|
1134
|
-
*
|
|
1135
|
-
*
|
|
1136
|
-
*
|
|
1137
|
-
*
|
|
1138
|
-
*
|
|
1139
|
-
* derived view model exists to prevent. The value was in scope and unread.
|
|
1140
|
-
*
|
|
1141
|
-
* The terminal statuses still come off `info.status`, because `ended` collapses
|
|
1142
|
-
* `failed` and `closed` into one bucket and those are worth telling apart here.
|
|
1143
|
-
*/
|
|
1144
|
-
function SessionStatusIcon({ row }) {
|
|
1145
|
-
const { info } = row;
|
|
1146
|
-
if (row.state === "attention") return /* @__PURE__ */ jsx(BellRing, { className: "size-3.5 shrink-0 animate-pulse text-warning" });
|
|
1147
|
-
if (row.state === "working") return /* @__PURE__ */ jsx(Spinner, { className: "size-3.5 shrink-0 text-info" });
|
|
1148
|
-
switch (info.status) {
|
|
1149
|
-
case "failed": return /* @__PURE__ */ jsx(CircleAlert, { className: "size-3.5 shrink-0 text-danger" });
|
|
1150
|
-
case "closed": return /* @__PURE__ */ jsx(CircleSlash, { className: "size-3.5 shrink-0 text-fg-4" });
|
|
1151
|
-
case "parked": return /* @__PURE__ */ jsx(PauseCircle, { className: "size-3.5 shrink-0 text-fg-3" });
|
|
1152
|
-
default: return /* @__PURE__ */ jsx(Moon, { className: "size-3.5 shrink-0 text-fg-4" });
|
|
1153
|
-
}
|
|
1154
|
-
}
|
|
1155
|
-
/**
|
|
1156
|
-
* Inline rename. Enter commits, Escape cancels, blur commits — but only a blur
|
|
1157
|
-
* that is still inside this document: switching windows must not silently commit,
|
|
1158
|
-
* nor kill the editor in the frame it opened.
|
|
1297
|
+
* Hover-revealed rather than always on, which is the opposite of the
|
|
1298
|
+
* extension's single overflow glyph and the right call for each: there are
|
|
1299
|
+
* three of them here and a sidebar card has room for one, so the extension
|
|
1300
|
+
* spends that room on a menu and this spends the row's hover on the actions
|
|
1301
|
+
* themselves. Both stop the click — the whole card is pressable underneath, and
|
|
1302
|
+
* an action that also selected the session would do two things per press.
|
|
1159
1303
|
*/
|
|
1160
|
-
function
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
value,
|
|
1169
|
-
autoFocus: true,
|
|
1170
|
-
"aria-label": "Session name",
|
|
1171
|
-
onClick: (e) => e.stopPropagation(),
|
|
1172
|
-
onChange: (e) => setValue(e.target.value),
|
|
1173
|
-
onBlur: () => document.hasFocus() ? onCommit(value.trim()) : void 0,
|
|
1174
|
-
onKeyDown: (e) => {
|
|
1175
|
-
if (e.key === "Enter") onCommit(value.trim());
|
|
1176
|
-
else if (e.key === "Escape") onCancel();
|
|
1304
|
+
function RowAction({ label, title, onClick, children }) {
|
|
1305
|
+
return /* @__PURE__ */ jsx(Button, {
|
|
1306
|
+
variant: "ghost",
|
|
1307
|
+
size: "icon-sm",
|
|
1308
|
+
"aria-label": label,
|
|
1309
|
+
title: title ?? label,
|
|
1310
|
+
className: "size-5 shrink-0 opacity-0 transition-opacity group-hover:opacity-100",
|
|
1311
|
+
onClick: (e) => {
|
|
1177
1312
|
e.stopPropagation();
|
|
1313
|
+
onClick();
|
|
1178
1314
|
},
|
|
1179
|
-
|
|
1315
|
+
children
|
|
1180
1316
|
});
|
|
1181
1317
|
}
|
|
1182
1318
|
/** A multi-select facet. Empty = no filter, which is why the trigger reads the
|
|
@@ -1238,6 +1374,6 @@ function OneOfSelect({ label, value, options, onChange }) {
|
|
|
1238
1374
|
});
|
|
1239
1375
|
}
|
|
1240
1376
|
//#endregion
|
|
1241
|
-
export { AlertDialog, AlertDialogClose, AlertDialogContent, AlertDialogDescription, AlertDialogTitle, AlertDialogTrigger, Badge, Band, Blank, Button, Card, CardContent, CardHeader, CardTitle, CodeBlock, Composer, ContextDialog, ContextRing, Conversation, ConversationContent, ConversationScrollButton, CopyAction, CopyButton, Dialog, DialogBody, DialogClose, DialogContent, DialogHeader, DialogRow, DialogTrigger, Empty, EmptyKey, EngineIcon, FileCard, HostFilesDialog, Ink, Input, Loader, McpDialog, Menu, MenuContent, MenuItem, MenuSeparator, MenuTrigger, Message, MessageContent, ModelSelect, PERMISSION_MODES, PermissionModeSelect, PermissionPrompt, ProgressRing, ProjectIcon, PromptArea, PromptTokenText, QUESTION_BEHAVIORS, QuestionPrompt, Reasoning, Response, Row, STATUS_META, Select, SelectContent, SelectItem, SelectItemText, SelectTrigger, SelectValue, SessionBrowser, SessionEmptyState, SessionInfoDialog, SessionList, SessionListItem, SessionPanel, SessionStatusIcon, SkillsDialog, Spinner, Splitter, StatusBar, StepRow, StepToggle, SubagentStrip, TerminalDiff, TerminalItemView, TerminalMarkdown, TerminalPermissionPrompt, TerminalQuestionPrompt, TerminalStatusLine, TerminalSurface, TerminalTranscript, Textarea, Tip, Toaster, ToolCallCard, TooltipContent, TooltipProvider, Transcript, TranscriptDensityProvider, TranscriptVariantProvider, UsageDialog, UsageMeters, WithActions, badgeVariants, buttonVariants, cn, commandTrigger, copyText, engineMark, formatAgoPrecise, formatBytes, formatCost, formatCountdown, formatDuration, formatRateLimitWindow, formatRateLimitWindowLong, formatRelativeTime, formatTokens, friendlyModel, getChipsByTrigger, hashtagTrigger, isMutatingTool, isSegmentsEmpty, mentionTrigger, parseUserQuestions, permissionModeChoices, permissionModeMeta, plainTextToSegments, previewPatch, rateLimitWindowSeconds, rowShapeClass, runningSteps, segmentsToPlainText, sessionSteps, skillPrompt, toast, toolIcon, toolInputPreview, useAffordances, useMinuteClock, usePromptAreaState, useTranscriptDensity, useTranscriptVariant, vendorMarkClass, vendorTextClass };
|
|
1377
|
+
export { AlertDialog, AlertDialogClose, AlertDialogContent, AlertDialogDescription, AlertDialogTitle, AlertDialogTrigger, Badge, Band, Blank, Button, Card, CardContent, CardHeader, CardTitle, CodeBlock, Composer, ContextDialog, ContextRing, Conversation, ConversationContent, ConversationScrollButton, CopyAction, CopyButton, Dialog, DialogBody, DialogClose, DialogContent, DialogHeader, DialogRow, DialogTrigger, Empty, EmptyKey, EngineIcon, FileCard, HostFilesDialog, Ink, Input, Loader, McpDialog, Menu, MenuContent, MenuItem, MenuSeparator, MenuTrigger, Message, MessageContent, ModelSelect, PERMISSION_MODES, PermissionModeSelect, PermissionPrompt, ProgressRing, ProjectIcon, PromptArea, PromptTokenText, QUESTION_BEHAVIORS, QuestionPrompt, Reasoning, Response, Row, STATUS_META, Select, SelectContent, SelectItem, SelectItemText, SelectTrigger, SelectValue, SessionBrowser, SessionEmptyState, SessionInfoDialog, SessionItem, SessionList, SessionListItem, SessionPanel, SessionStatusIcon, SkillsDialog, Spinner, Splitter, StatusBar, StepRow, StepToggle, SubagentStrip, TerminalDiff, TerminalItemView, TerminalMarkdown, TerminalPermissionPrompt, TerminalQuestionPrompt, TerminalStatusLine, TerminalSurface, TerminalTranscript, Textarea, Tip, Toaster, ToolCallCard, TooltipContent, TooltipProvider, Transcript, TranscriptDensityProvider, TranscriptVariantProvider, UsageDialog, UsageMeters, WithActions, badgeVariants, buttonVariants, cn, commandTrigger, copyText, engineMark, formatAgoPrecise, formatBytes, formatCost, formatCountdown, formatDuration, formatRateLimitWindow, formatRateLimitWindowLong, formatRelativeTime, formatTokens, friendlyModel, getChipsByTrigger, hashtagTrigger, isMutatingTool, isSegmentsEmpty, mentionTrigger, parseUserQuestions, permissionModeChoices, permissionModeMeta, plainTextToSegments, previewPatch, rateLimitWindowSeconds, rowShapeClass, runningSteps, segmentsToPlainText, sessionSteps, skillPrompt, toast, toolIcon, toolInputPreview, useAffordances, useMinuteClock, usePromptAreaState, useTranscriptDensity, useTranscriptVariant, vendorMarkClass, vendorTextClass };
|
|
1242
1378
|
|
|
1243
1379
|
//# sourceMappingURL=index.mjs.map
|