@workerdeck/ui 0.16.0 → 0.17.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/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { a as formatDuration, c as formatRelativeTime, d as rateLimitWindowSeconds, f as toolInputPreview, i as formatCountdown, l as formatTokens, n as formatBytes, o as formatRateLimitWindow, r as formatCost, s as formatRateLimitWindowLong, t as formatAgoPrecise, u as friendlyModel } from "./format-DfI_je9S.mjs";
2
- import { C as permissionModeChoices, S as PermissionModeSelectProps, _ as useAffordances, a as SessionVitals, b as PermissionModeMeta, c as TranscriptDensityProvider, d as TranscriptVariantProvider, f as useTranscriptDensity, g as WithActions, h as TerminalAffordances, i as SessionSurfacePanel, l as TranscriptFont, m as CopyAction, n as SessionPanel, o as TerminalMetrics, p as useTranscriptVariant, r as SessionPanelProps, s as TranscriptDensity, t as SessionControls, u as TranscriptVariant, v as PERMISSION_MODES, w as permissionModeMeta, x as PermissionModeSelect, y as PermissionModeChoice } from "./SessionPanel-B9CHoq8x.mjs";
2
+ import { C as permissionModeChoices, S as PermissionModeSelectProps, _ as useAffordances, a as SessionVitals, b as PermissionModeMeta, c as TranscriptDensityProvider, d as TranscriptVariantProvider, f as useTranscriptDensity, g as WithActions, h as TerminalAffordances, i as SessionSurfacePanel, l as TranscriptFont, m as CopyAction, n as SessionPanel, o as TerminalMetrics, p as useTranscriptVariant, r as SessionPanelProps, s as TranscriptDensity, t as SessionControls, u as TranscriptVariant, v as PERMISSION_MODES, w as permissionModeMeta, x as PermissionModeSelect, y as PermissionModeChoice } from "./SessionPanel-CnNYEX80.mjs";
3
3
  import * as _$react from "react";
4
4
  import { ButtonHTMLAttributes, FunctionComponent, HTMLAttributes, InputHTMLAttributes, ReactElement, ReactNode, Ref, RefObject, TextareaHTMLAttributes } from "react";
5
5
  import { VariantProps } from "class-variance-authority";
@@ -15,7 +15,7 @@ import { Dialog as Dialog$1 } from "@base-ui/react/dialog";
15
15
  import * as _$_base_ui_react_tooltip0 from "@base-ui/react/tooltip";
16
16
  import { Tooltip } from "@base-ui/react/tooltip";
17
17
  import { toast } from "sonner";
18
- import { ContextUsage, FilePatch, ModelOption, PermissionRequest, ProfileEngine, QuestionBehavior, RateLimitInfo, SessionInfo, SessionRow, SessionStatus, SkillInfo, SlashCommandInfo, UsageWindowRow, UserQuestion, ViewConfig, WorkspaceScope } from "@workerdeck/protocol";
18
+ import { ContextUsage, FilePatch, ModelOption, PermissionRequest, ProfileEngine, ProjectIcon as ProjectIcon$1, QuestionBehavior, RateLimitInfo, SessionInfo, SessionRow, SessionStatus, SkillInfo, SlashCommandInfo, UsageWindowRow, UserQuestion, ViewConfig, WorkspaceScope } from "@workerdeck/protocol";
19
19
  import { ConnectionState, TranscriptItem, TranscriptState, UseAttachmentsResult } from "@workerdeck/react";
20
20
  import * as _$class_variance_authority_types0 from "class-variance-authority/types";
21
21
  import { WorkerDeckClient } from "@workerdeck/client";
@@ -933,6 +933,22 @@ interface TranscriptProps {
933
933
  * message look like it did nothing at all.
934
934
  */
935
935
  repinRef?: RefObject<(() => void) | null>;
936
+ /**
937
+ * Scroll a tool call into view — bump `nonce` to ask again for the same one.
938
+ *
939
+ * The seam a *list* needs: sub-agent work is nested inside the `Task` call
940
+ * that spawned it, so "open that sub-agent" can only ever mean "take me to its
941
+ * row". A `parentToolUseId` works here as well as the Task's own id, since the
942
+ * lookup resolves an absorbed child to the row that folded it.
943
+ *
944
+ * A prop rather than a ref (the shape `jumpToRecapRef` uses) because the asker
945
+ * is outside this webview entirely and the request travels as data; a ref
946
+ * would need a live closure at the other end of a postMessage bridge.
947
+ */
948
+ reveal?: {
949
+ toolUseId: string;
950
+ nonce: number;
951
+ };
936
952
  className?: string;
937
953
  }
938
954
  declare function Transcript({
@@ -953,6 +969,7 @@ declare function Transcript({
953
969
  catchUp,
954
970
  jumpToRecapRef,
955
971
  repinRef,
972
+ reveal,
956
973
  className
957
974
  }: TranscriptProps): _$react.JSX.Element;
958
975
  //#endregion
@@ -1862,6 +1879,15 @@ interface SessionBrowserProps {
1862
1879
  * filtered by a control you can't currently see still says so.
1863
1880
  */
1864
1881
  showControls?: boolean;
1882
+ /**
1883
+ * Resolved project-icon bytes by content hash — `useProjectIcons`' output.
1884
+ *
1885
+ * Passed in rather than fetched here for the reason `ProjectIcon` states: the
1886
+ * wire carries an *address*, and who can fetch it differs per client. Absent,
1887
+ * or a hash not in it yet, simply draws no picture; the project's name is
1888
+ * already there.
1889
+ */
1890
+ projectIcons?: Record<string, string>;
1865
1891
  className?: string;
1866
1892
  }
1867
1893
  /**
@@ -1892,6 +1918,7 @@ declare function SessionBrowser({
1892
1918
  onRename,
1893
1919
  emptyState,
1894
1920
  showControls,
1921
+ projectIcons,
1895
1922
  className
1896
1923
  }: SessionBrowserProps): _$react.JSX.Element;
1897
1924
  /**
@@ -1907,6 +1934,15 @@ declare function SessionStatusIcon({
1907
1934
  }): _$react.JSX.Element;
1908
1935
  //#endregion
1909
1936
  //#region src/components/agent/EngineIcon.d.ts
1937
+ /**
1938
+ * Which mark a session wears.
1939
+ *
1940
+ * The two first-party engines are named directly. A `provider` session is
1941
+ * whatever the host wired up, so its *model* is the only thing that says whose
1942
+ * it is — sniffed loosely on purpose (`gemini-2.5-pro`, `deepseek-chat`,
1943
+ * `kimi-k2`), and falling through to no mark rather than guessing wrong.
1944
+ */
1945
+ declare function engineMark(engine: string, model?: string): string | undefined;
1910
1946
  declare function EngineIcon({
1911
1947
  engine,
1912
1948
  model,
@@ -1917,6 +1953,21 @@ declare function EngineIcon({
1917
1953
  className?: string;
1918
1954
  }): _$react.JSX.Element;
1919
1955
  //#endregion
1956
+ //#region src/components/agent/ProjectIcon.d.ts
1957
+ declare function ProjectIcon({
1958
+ icon,
1959
+ src,
1960
+ name,
1961
+ className
1962
+ }: {
1963
+ icon: ProjectIcon$1 | undefined;
1964
+ /** Resolved bytes for the `image` arm — an object URL or a data URL. The
1965
+ * caller fetches (see the note above); absent draws nothing. */
1966
+ src?: string; /** The project's name, for the alt text. */
1967
+ name?: string;
1968
+ className?: string;
1969
+ }): _$react.JSX.Element | null;
1970
+ //#endregion
1920
1971
  //#region src/components/agent/SessionEmptyState.d.ts
1921
1972
  interface SessionEmptyStateProps {
1922
1973
  cwd?: string;
@@ -2019,5 +2070,5 @@ declare function toolIcon(toolName: string): LucideIcon;
2019
2070
  */
2020
2071
  declare function isMutatingTool(toolName: string): boolean;
2021
2072
  //#endregion
2022
- export { AlertDialog, AlertDialogClose, AlertDialogContent, AlertDialogDescription, AlertDialogTitle, AlertDialogTrigger, Badge, type BadgeProps, Band, Blank, Button, type ButtonProps, Card, CardContent, CardHeader, CardTitle, type ChipSegment, CodeBlock, type CodeBlockProps, Composer, type ComposerFileMatch, type ComposerHandle, type ComposerProps, ContextDialog, type ContextDialogProps, Conversation, ConversationContent, type ConversationProps, ConversationScrollButton, CopyAction, CopyButton, type CopyButtonProps, Dialog, DialogBody, DialogClose, DialogContent, DialogHeader, DialogRow, DialogTrigger, Empty, EmptyKey, EngineIcon, FileCard, type FileCardProps, type FileDeliveredItem, HostFilesDialog, type HostFilesDialogProps, Ink, Input, Loader, McpDialog, type McpDialogProps, Menu, MenuContent, MenuItem, MenuSeparator, MenuTrigger, Message, MessageContent, type MessageProps, ModelSelect, type ModelSelectProps, PERMISSION_MODES, type PermissionModeChoice, type PermissionModeMeta, PermissionModeSelect, type PermissionModeSelectProps, PermissionPrompt, type PermissionPromptProps, ProgressRing, type ProgressRingProps, PromptArea, type PromptAreaHandle, type PromptAreaProps, PromptTokenText, QUESTION_BEHAVIORS, type QuestionBehaviorMeta, QuestionPrompt, type QuestionPromptProps, Reasoning, type ReasoningProps, Response, type ResponseProps, Row, type RowProps, STATUS_META, type Segment, Select, SelectContent, SelectItem, SelectItemText, SelectTrigger, SelectValue, SessionBrowser, type SessionBrowserProps, type SessionControls, SessionEmptyState, type SessionEmptyStateProps, SessionInfoDialog, type SessionInfoDialogProps, SessionList, SessionListItem, type SessionListItemProps, type SessionListProps, SessionPanel, type SessionPanelProps, SessionStatusIcon, type SessionSurfacePanel, type SessionVitals, SkillsDialog, type SkillsDialogProps, Spinner, Splitter, type SplitterProps, StatusBar, type StatusBarProps, type TerminalAffordances, TerminalDiff, TerminalItemView, TerminalMarkdown, type TerminalMarkdownProps, type TerminalMetrics, TerminalPermissionPrompt, type TerminalPermissionPromptProps, TerminalQuestionPrompt, type TerminalQuestionPromptProps, TerminalStatusLine, type TerminalStatusLineProps, TerminalSurface, type TerminalSurfaceProps, TerminalTranscript, type TerminalTranscriptProps, type TextSegment, Textarea, Tip, Toaster, type Tone, ToolCallCard, type ToolCallCardProps, type ToolCallItem, TooltipContent, TooltipProvider, Transcript, type TranscriptDensity, TranscriptDensityProvider, type TranscriptFont, type TranscriptProps, type TranscriptVariant, TranscriptVariantProvider, type TriggerConfig, type TriggerSuggestion, UsageDialog, type UsageDialogProps, UsageMeters, WithActions, badgeVariants, buttonVariants, cn, commandTrigger, copyText, formatAgoPrecise, formatBytes, formatCost, formatCountdown, formatDuration, formatRateLimitWindow, formatRateLimitWindowLong, formatRelativeTime, formatTokens, friendlyModel, getChipsByTrigger, hashtagTrigger, isMutatingTool, isSegmentsEmpty, mentionTrigger, parseUserQuestions, permissionModeChoices, permissionModeMeta, plainTextToSegments, previewPatch, rateLimitWindowSeconds, rowShapeClass, segmentsToPlainText, skillPrompt, toast, toolIcon, toolInputPreview, useAffordances, useMinuteClock, usePromptAreaState, useTranscriptDensity, useTranscriptVariant };
2073
+ export { AlertDialog, AlertDialogClose, AlertDialogContent, AlertDialogDescription, AlertDialogTitle, AlertDialogTrigger, Badge, type BadgeProps, Band, Blank, Button, type ButtonProps, Card, CardContent, CardHeader, CardTitle, type ChipSegment, CodeBlock, type CodeBlockProps, Composer, type ComposerFileMatch, type ComposerHandle, type ComposerProps, ContextDialog, type ContextDialogProps, Conversation, ConversationContent, type ConversationProps, ConversationScrollButton, CopyAction, CopyButton, type CopyButtonProps, Dialog, DialogBody, DialogClose, DialogContent, DialogHeader, DialogRow, DialogTrigger, Empty, EmptyKey, EngineIcon, FileCard, type FileCardProps, type FileDeliveredItem, HostFilesDialog, type HostFilesDialogProps, Ink, Input, Loader, McpDialog, type McpDialogProps, Menu, MenuContent, MenuItem, MenuSeparator, MenuTrigger, Message, MessageContent, type MessageProps, ModelSelect, type ModelSelectProps, PERMISSION_MODES, type PermissionModeChoice, type PermissionModeMeta, PermissionModeSelect, type PermissionModeSelectProps, PermissionPrompt, type PermissionPromptProps, ProgressRing, type ProgressRingProps, ProjectIcon, PromptArea, type PromptAreaHandle, type PromptAreaProps, PromptTokenText, QUESTION_BEHAVIORS, type QuestionBehaviorMeta, QuestionPrompt, type QuestionPromptProps, Reasoning, type ReasoningProps, Response, type ResponseProps, Row, type RowProps, STATUS_META, type Segment, Select, SelectContent, SelectItem, SelectItemText, SelectTrigger, SelectValue, SessionBrowser, type SessionBrowserProps, type SessionControls, SessionEmptyState, type SessionEmptyStateProps, SessionInfoDialog, type SessionInfoDialogProps, SessionList, SessionListItem, type SessionListItemProps, type SessionListProps, SessionPanel, type SessionPanelProps, SessionStatusIcon, type SessionSurfacePanel, type SessionVitals, SkillsDialog, type SkillsDialogProps, Spinner, Splitter, type SplitterProps, StatusBar, type StatusBarProps, type TerminalAffordances, TerminalDiff, TerminalItemView, TerminalMarkdown, type TerminalMarkdownProps, type TerminalMetrics, TerminalPermissionPrompt, type TerminalPermissionPromptProps, TerminalQuestionPrompt, type TerminalQuestionPromptProps, TerminalStatusLine, type TerminalStatusLineProps, TerminalSurface, type TerminalSurfaceProps, TerminalTranscript, type TerminalTranscriptProps, type TextSegment, Textarea, Tip, Toaster, type Tone, ToolCallCard, type ToolCallCardProps, type ToolCallItem, TooltipContent, TooltipProvider, Transcript, type TranscriptDensity, TranscriptDensityProvider, type TranscriptFont, type TranscriptProps, type TranscriptVariant, TranscriptVariantProvider, type TriggerConfig, type TriggerSuggestion, UsageDialog, type UsageDialogProps, 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, segmentsToPlainText, skillPrompt, toast, toolIcon, toolInputPreview, useAffordances, useMinuteClock, usePromptAreaState, useTranscriptDensity, useTranscriptVariant };
2023
2074
  //# sourceMappingURL=index.d.mts.map
package/build/index.mjs CHANGED
@@ -1,12 +1,12 @@
1
- import { $ as TranscriptDensityProvider, A as PermissionModeSelect, At as MenuTrigger, B as TerminalSurface, Bt as Button, C as TerminalQuestionPrompt, Ct as DialogHeader, D as parseUserQuestions, Dt as MenuContent, E as QuestionPrompt, Et as Menu, F as McpDialog, Ft as SelectTrigger, G as Blank, H as TerminalDiff, Ht as cn, I as HostFilesDialog, It as SelectValue, J as CopyAction, K as Ink, L as ContextDialog, Lt as Input, M as permissionModeMeta, Mt as SelectContent, N as ModelSelect, Nt as SelectItem, O as PermissionPrompt, Ot as MenuItem, P as SkillsDialog, Pt as SelectItemText, Q as toolIcon, R as Composer, Rt as Badge, S as SessionInfoDialog, St as DialogContent, T as QUESTION_BEHAVIORS, Tt as DialogTrigger, U as previewPatch, V as TerminalMarkdown, Vt as buttonVariants, W as Band, X as useAffordances, Y as WithActions, Z as isMutatingTool, _ as Conversation, _t as TooltipContent, a as Transcript, at as mentionTrigger, b as StatusBar, bt as DialogBody, c as ToolCallCard, ct as plainTextToSegments, d as Response, dt as Splitter, et as TranscriptVariantProvider, f as PromptTokenText, ft as CodeBlock, g as FileCard, gt as Tip, h as Loader, ht as copyText, i as useMinuteClock, it as hashtagTrigger, j as permissionModeChoices, jt as Select, k as PERMISSION_MODES, kt as MenuSeparator, l as SessionEmptyState, lt as segmentsToPlainText, m as MessageContent, mt as CopyButton, n as UsageDialog, nt as useTranscriptVariant, o as TerminalItemView, ot as usePromptAreaState, p as Message, pt as Spinner, q as Row, r as UsageMeters, rt as commandTrigger, s as TerminalTranscript, st as PromptArea, t as SessionPanel, tt as useTranscriptDensity, u as Reasoning, ut as ProgressRing, v as ConversationContent, vt as TooltipProvider, w as TerminalPermissionPrompt, wt as DialogRow, x as STATUS_META, xt as DialogClose, y as ConversationScrollButton, yt as Dialog, z as skillPrompt, zt as badgeVariants } from "./SessionPanel-DII9MmQ8.mjs";
1
+ import { $ as TranscriptDensityProvider, A as PermissionModeSelect, At as MenuTrigger, B as TerminalSurface, Bt as Button, C as TerminalQuestionPrompt, Ct as DialogHeader, D as parseUserQuestions, Dt as MenuContent, E as QuestionPrompt, Et as Menu, F as McpDialog, Ft as SelectTrigger, G as Blank, H as TerminalDiff, Ht as cn, I as HostFilesDialog, It as SelectValue, J as CopyAction, K as Ink, L as ContextDialog, Lt as Input, M as permissionModeMeta, Mt as SelectContent, N as ModelSelect, Nt as SelectItem, O as PermissionPrompt, Ot as MenuItem, P as SkillsDialog, Pt as SelectItemText, Q as toolIcon, R as Composer, Rt as Badge, S as SessionInfoDialog, St as DialogContent, T as QUESTION_BEHAVIORS, Tt as DialogTrigger, U as previewPatch, V as TerminalMarkdown, Vt as buttonVariants, W as Band, X as useAffordances, Y as WithActions, Z as isMutatingTool, _ as Conversation, _t as TooltipContent, a as Transcript, at as mentionTrigger, b as StatusBar, bt as DialogBody, c as ToolCallCard, ct as plainTextToSegments, d as Response, dt as Splitter, et as TranscriptVariantProvider, f as PromptTokenText, ft as CodeBlock, g as FileCard, gt as Tip, h as Loader, ht as copyText, i as useMinuteClock, it as hashtagTrigger, j as permissionModeChoices, jt as Select, k as PERMISSION_MODES, kt as MenuSeparator, l as SessionEmptyState, lt as segmentsToPlainText, m as MessageContent, mt as CopyButton, n as UsageDialog, nt as useTranscriptVariant, o as TerminalItemView, ot as usePromptAreaState, p as Message, pt as Spinner, q as Row, r as UsageMeters, rt as commandTrigger, s as TerminalTranscript, st as PromptArea, t as SessionPanel, tt as useTranscriptDensity, u as Reasoning, ut as ProgressRing, v as ConversationContent, vt as TooltipProvider, w as TerminalPermissionPrompt, wt as DialogRow, x as STATUS_META, xt as DialogClose, y as ConversationScrollButton, yt as Dialog, z as skillPrompt, zt as badgeVariants } from "./SessionPanel-DMPhsNlW.mjs";
2
2
  import { a as formatDuration, c as formatRelativeTime, d as rateLimitWindowSeconds, f as toolInputPreview, i as formatCountdown, l as formatTokens, n as formatBytes, o as formatRateLimitWindow, r as formatCost, s as formatRateLimitWindowLong, t as formatAgoPrecise, u as friendlyModel } from "./format-DqR56Y8l.mjs";
3
3
  import { a as statusPresentation, c as windowLabel, i as modelLabel, o as tightestWindow, r as meterSeverity, t as contextSeverity } from "./status-Ydzi7n6j.mjs";
4
4
  import { forwardRef, useEffect, useMemo, useRef, useState } from "react";
5
- import { jsx, jsxs } from "react/jsx-runtime";
6
- import { BellRing, CircleAlert, CircleSlash, Layers, Moon, PauseCircle, Pencil, Search, SearchX, Trash2, X } from "lucide-react";
5
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
6
+ import { Anchor, Atom, Beaker, BellRing, Bike, Binary, Blocks, Bot, Box, Brain, Briefcase, Brush, Bug, Building2, Cable, Camera, Car, Cat, CircleAlert, CircleSlash, CircuitBoard, Cloud, Code, Coffee, Cog, Compass, Container, Cpu, CreditCard, Database, Diamond, Dog, 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";
7
7
  import { AlertDialog as AlertDialog$1 } from "@base-ui/react/alert-dialog";
8
8
  import { Toaster as Toaster$1, toast } from "sonner";
9
- import { STATE_LABELS, STATE_ORDER, adaptersOf, clearFilters, filterRows, groupRows, hasFacetFilter, sessionLabel, subsetSummary } from "@workerdeck/protocol";
9
+ import { STATE_LABELS, STATE_ORDER, adaptersOf, clearFilters, filterRows, groupRows, hasFacetFilter, projectLabel, projectsOf, sessionLabel, subsetSummary } from "@workerdeck/protocol";
10
10
  //#region src/components/ui/Card.tsx
11
11
  function Card({ className, ...props }) {
12
12
  return /* @__PURE__ */ jsx("div", {
@@ -311,6 +311,179 @@ function SessionList({ sessions, activeId, onSelect, onDelete, emptyText = "No s
311
311
  });
312
312
  }
313
313
  //#endregion
314
+ //#region src/components/agent/ProjectIcon.tsx
315
+ /**
316
+ * A project's icon — the render side of protocol's `ProjectIcon`, for a list
317
+ * row or a group header.
318
+ *
319
+ * **The glyph arm is a curated set, and that is the design rather than a
320
+ * shortcut.** The gateway validates a glyph name by *shape* only (lucide's
321
+ * lowercase-kebab convention) and explicitly declines to grow an icon catalog,
322
+ * so the contract already says a client must fall back on a name it does not
323
+ * know.
324
+ *
325
+ * The alternative was measured rather than argued, against the VS Code
326
+ * sidebar's own bundle: `sidebar.js` is **31 KB** with one glyph, **77 KB**
327
+ * with the 110 below (~418 bytes each), and **927 KB** with a
328
+ * `import * as` over lucide's ~1,600 — which is what it costs, because a
329
+ * namespace import defeats tree-shaking by construction. (`DynamicIcon` is the
330
+ * third option and worse in a different currency: ~1,600 chunk files shipped
331
+ * inside the `.vsix`.) Twelve times the bundle to render a name nobody has
332
+ * declared yet is not a trade worth making; 46 KB to cover the names people
333
+ * actually use is.
334
+ *
335
+ * The set is chosen to cover what people call a project; an unlisted-but-valid
336
+ * name draws {@link Folder}, which is what the row drew before this feature
337
+ * existed. **Grow it freely** — each addition is ~400 bytes and the fallback
338
+ * means a missing one is a shrug, not a hole.
339
+ *
340
+ * **The image arm draws nothing until its bytes arrive**, and takes them as a
341
+ * resolved `src` rather than fetching: the wire carries only an address
342
+ * (media type, size, content hash), and *who* fetches it differs per client —
343
+ * a VS Code webview cannot reach a gateway at all and must be handed a data
344
+ * URL by its extension host, while a browser can fetch and `createObjectURL`.
345
+ * A component that fetched would be wrong on one of them. Absent `src` renders
346
+ * nothing rather than a placeholder box: an icon is decoration beside a name
347
+ * that is already there, and a box that becomes a picture a beat later is more
348
+ * movement than the picture is worth.
349
+ *
350
+ * One thing an image icon cannot do, and a glyph can: **take the row's
351
+ * colour.** An `<img>`-embedded SVG is its own document, so `currentColor` does
352
+ * not reach it and its own `prefers-color-scheme` resolves against the *OS*
353
+ * rather than the host's theme (measured: it does resolve, which is the
354
+ * surprise — so a monochrome mark tuned for both schemes is right on a dark OS
355
+ * in a dark editor and wrong on a light OS in a dark one). A repo that wants a
356
+ * mark that always matches its row should declare a glyph; one that wants its
357
+ * brand should declare the image and accept that it is a picture.
358
+ */
359
+ const GLYPHS = {
360
+ anchor: Anchor,
361
+ atom: Atom,
362
+ beaker: Beaker,
363
+ bike: Bike,
364
+ binary: Binary,
365
+ blocks: Blocks,
366
+ bot: Bot,
367
+ box: Box,
368
+ brain: Brain,
369
+ briefcase: Briefcase,
370
+ brush: Brush,
371
+ bug: Bug,
372
+ "building-2": Building2,
373
+ cable: Cable,
374
+ camera: Camera,
375
+ car: Car,
376
+ cat: Cat,
377
+ "circuit-board": CircuitBoard,
378
+ cloud: Cloud,
379
+ code: Code,
380
+ coffee: Coffee,
381
+ cog: Cog,
382
+ compass: Compass,
383
+ container: Container,
384
+ cpu: Cpu,
385
+ "credit-card": CreditCard,
386
+ database: Database,
387
+ diamond: Diamond,
388
+ dog: Dog,
389
+ feather: Feather,
390
+ film: Film,
391
+ flame: Flame,
392
+ "flask-conical": FlaskConical,
393
+ folder: Folder,
394
+ "gamepad-2": Gamepad2,
395
+ gauge: Gauge,
396
+ gem: Gem,
397
+ ghost: Ghost,
398
+ gift: Gift,
399
+ "git-branch": GitBranch,
400
+ globe: Globe,
401
+ "graduation-cap": GraduationCap,
402
+ hammer: Hammer,
403
+ hexagon: Hexagon,
404
+ house: House,
405
+ image: Image,
406
+ key: Key,
407
+ landmark: Landmark,
408
+ laptop: Laptop,
409
+ layers: Layers,
410
+ leaf: Leaf,
411
+ library: Library,
412
+ lightbulb: Lightbulb,
413
+ lock: Lock,
414
+ mail: Mail,
415
+ map: Map$1,
416
+ "message-circle": MessageCircle,
417
+ mic: Mic,
418
+ monitor: Monitor,
419
+ moon: Moon,
420
+ mountain: Mountain,
421
+ music: Music,
422
+ network: Network,
423
+ newspaper: Newspaper,
424
+ notebook: Notebook,
425
+ orbit: Orbit,
426
+ package: Package,
427
+ palette: Palette,
428
+ "pen-tool": PenTool,
429
+ phone: Phone,
430
+ "piggy-bank": PiggyBank,
431
+ plane: Plane,
432
+ plug: Plug,
433
+ puzzle: Puzzle,
434
+ radar: Radar,
435
+ radio: Radio,
436
+ receipt: Receipt,
437
+ rocket: Rocket,
438
+ ruler: Ruler,
439
+ satellite: Satellite,
440
+ scale: Scale,
441
+ scissors: Scissors,
442
+ server: Server,
443
+ shield: Shield,
444
+ ship: Ship,
445
+ "shopping-cart": ShoppingCart,
446
+ signal: Signal,
447
+ smartphone: Smartphone,
448
+ sparkles: Sparkles,
449
+ sprout: Sprout,
450
+ square: Square,
451
+ star: Star,
452
+ sun: Sun,
453
+ table: Table,
454
+ tag: Tag,
455
+ target: Target,
456
+ telescope: Telescope,
457
+ terminal: Terminal,
458
+ "test-tube": TestTube,
459
+ "tree-pine": TreePine,
460
+ trophy: Trophy,
461
+ truck: Truck,
462
+ umbrella: Umbrella,
463
+ users: Users,
464
+ wallet: Wallet,
465
+ wand: Wand,
466
+ waves: Waves,
467
+ webhook: Webhook,
468
+ wifi: Wifi,
469
+ wrench: Wrench,
470
+ zap: Zap
471
+ };
472
+ function ProjectIcon({ icon, src, name, className }) {
473
+ if (!icon) return null;
474
+ if (icon.type === "image") {
475
+ if (!src) return null;
476
+ return /* @__PURE__ */ jsx("img", {
477
+ src,
478
+ alt: "",
479
+ "aria-hidden": true,
480
+ title: name,
481
+ className: cn("inline-block size-3 shrink-0 object-contain", className)
482
+ });
483
+ }
484
+ return /* @__PURE__ */ jsx(GLYPHS[icon.name] ?? Folder, { className: cn("inline-block size-3 shrink-0", className) });
485
+ }
486
+ //#endregion
314
487
  //#region src/components/agent/SessionBrowser.tsx
315
488
  /**
316
489
  * How a list row is drawn, in one place, so `SidebarRow` in `web` matches this
@@ -331,7 +504,7 @@ function SessionList({ sessions, activeId, onSelect, onDelete, emptyText = "No s
331
504
  function rowShapeClass(active) {
332
505
  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");
333
506
  }
334
- function SessionBrowser({ rows, config, onConfigChange, scope, activeId, onSelect, onDelete, onRename, emptyState, showControls = true, className }) {
507
+ function SessionBrowser({ rows, config, onConfigChange, scope, activeId, onSelect, onDelete, onRename, emptyState, showControls = true, projectIcons, className }) {
335
508
  const visible = useMemo(() => filterRows(rows, config, scope), [
336
509
  rows,
337
510
  config,
@@ -340,6 +513,7 @@ function SessionBrowser({ rows, config, onConfigChange, scope, activeId, onSelec
340
513
  const groups = useMemo(() => groupRows(visible, config), [visible, config]);
341
514
  const subset = subsetSummary(config, scope, visible.length, rows.length);
342
515
  const adapters = useMemo(() => adaptersOf(rows), [rows]);
516
+ const projects = useMemo(() => projectsOf(rows), [rows]);
343
517
  const gateways = useMemo(() => {
344
518
  const seen = /* @__PURE__ */ new Map();
345
519
  for (const row of rows) seen.set(row.hostId, row.hostName);
@@ -405,6 +579,18 @@ function SessionBrowser({ rows, config, onConfigChange, scope, activeId, onSelec
405
579
  onChange: (gateways) => set({ gateways })
406
580
  })
407
581
  }) : null,
582
+ projects.length > 1 ? /* @__PURE__ */ jsx(FilterRow, {
583
+ label: "Project",
584
+ children: /* @__PURE__ */ jsx(FacetSelect, {
585
+ label: "Project",
586
+ value: config.projects ?? [],
587
+ options: projects.map((p) => ({
588
+ value: p.key,
589
+ label: p.label
590
+ })),
591
+ onChange: (next) => set({ projects: next })
592
+ })
593
+ }) : null,
408
594
  /* @__PURE__ */ jsx(FilterRow, {
409
595
  label: "Group",
410
596
  children: /* @__PURE__ */ jsx(OneOfSelect, {
@@ -423,6 +609,10 @@ function SessionBrowser({ rows, config, onConfigChange, scope, activeId, onSelec
423
609
  value: "adapter",
424
610
  label: "By engine"
425
611
  },
612
+ ...projects.length > 1 ? [{
613
+ value: "project",
614
+ label: "By project"
615
+ }] : [],
426
616
  ...gateways.length > 1 ? [{
427
617
  value: "gateway",
428
618
  label: "By gateway"
@@ -449,6 +639,10 @@ function SessionBrowser({ rows, config, onConfigChange, scope, activeId, onSelec
449
639
  value: "state",
450
640
  label: "State"
451
641
  },
642
+ ...projects.length > 1 ? [{
643
+ value: "project",
644
+ label: "Project"
645
+ }] : [],
452
646
  ...gateways.length > 1 ? [{
453
647
  value: "gateway",
454
648
  label: "Gateway"
@@ -491,18 +685,28 @@ function SessionBrowser({ rows, config, onConfigChange, scope, activeId, onSelec
491
685
  children: groups.map((group) => /* @__PURE__ */ jsxs("div", {
492
686
  className: "flex flex-col gap-1",
493
687
  children: [config.groupBy !== "none" && group.label ? /* @__PURE__ */ jsxs("div", {
494
- className: "flex items-baseline gap-2 px-3 text-label font-medium text-fg-4",
495
- children: [/* @__PURE__ */ jsx("span", {
496
- className: "uppercase tracking-wide",
497
- children: group.label
498
- }), /* @__PURE__ */ jsx("span", {
499
- className: "text-fg-4/70",
500
- children: group.rows.length
501
- })]
688
+ className: "flex items-center gap-2 px-3 text-label font-medium text-fg-4",
689
+ children: [
690
+ config.groupBy === "project" ? /* @__PURE__ */ jsx(ProjectIcon, {
691
+ icon: group.rows[0]?.info.project?.icon,
692
+ src: iconSrcOf(group.rows[0], projectIcons),
693
+ name: group.label
694
+ }) : null,
695
+ /* @__PURE__ */ jsx("span", {
696
+ className: "uppercase tracking-wide",
697
+ children: group.label
698
+ }),
699
+ /* @__PURE__ */ jsx("span", {
700
+ className: "text-fg-4/70",
701
+ children: group.rows.length
702
+ })
703
+ ]
502
704
  }) : null, group.rows.map((row) => /* @__PURE__ */ jsx(SessionRowItem, {
503
705
  row,
504
706
  active: row.info.id === activeId,
505
707
  showGateway: gateways.length > 1,
708
+ showProject: config.groupBy !== "project",
709
+ projectIcons,
506
710
  onSelect,
507
711
  onDelete,
508
712
  onRename
@@ -512,17 +716,27 @@ function SessionBrowser({ rows, config, onConfigChange, scope, activeId, onSelec
512
716
  ]
513
717
  });
514
718
  }
515
- function SessionRowItem({ row, active, showGateway, onSelect, onDelete, onRename }) {
719
+ /** The bytes for a row's project icon, if it has an image one and the caller has
720
+ * fetched it yet. Shared by the row and its group header so the two cannot draw
721
+ * different pictures for one project. */
722
+ function iconSrcOf(row, icons) {
723
+ const icon = row?.info.project?.icon;
724
+ return icon?.type === "image" ? icons?.[icon.hash] : void 0;
725
+ }
726
+ function SessionRowItem({ row, active, showGateway, showProject = true, projectIcons, onSelect, onDelete, onRename }) {
516
727
  const { info } = row;
517
728
  const [editing, setEditing] = useState(false);
518
729
  const folder = info.cwd.split("/").filter(Boolean).pop() ?? info.cwd;
730
+ const project = showProject ? projectLabel(row) : folder;
731
+ const projectIcon = showProject ? info.project?.icon : void 0;
519
732
  const details = [
520
733
  showGateway ? row.hostName : void 0,
521
734
  friendlyModel(info.model),
522
- folder,
735
+ project,
523
736
  info.profile ? `@${info.profile}` : void 0,
524
737
  formatCost(info.totalCostUsd)
525
738
  ].filter(Boolean);
739
+ const cut = details.indexOf(project);
526
740
  return /* @__PURE__ */ jsxs("div", {
527
741
  "data-slot": "session-row",
528
742
  "data-active": active || void 0,
@@ -561,7 +775,16 @@ function SessionRowItem({ row, active, showGateway, onSelect, onDelete, onRename
561
775
  type: "button",
562
776
  tabIndex: -1,
563
777
  className: "min-w-0 flex-1 truncate text-left font-mono outline-none",
564
- children: details.join(" · ")
778
+ children: cut < 0 ? details.join(" · ") : /* @__PURE__ */ jsxs(Fragment$1, { children: [
779
+ details.slice(0, cut).map((part) => `${part} · `),
780
+ /* @__PURE__ */ jsx(ProjectIcon, {
781
+ icon: projectIcon,
782
+ src: iconSrcOf(row, projectIcons),
783
+ name: project,
784
+ className: "mr-1 align-[-0.2em]"
785
+ }),
786
+ details.slice(cut).join(" · ")
787
+ ] })
565
788
  }),
566
789
  onRename && !editing ? /* @__PURE__ */ jsx(Button, {
567
790
  variant: "ghost",
@@ -770,6 +993,6 @@ function EngineIcon({ engine, model, className }) {
770
993
  });
771
994
  }
772
995
  //#endregion
773
- export { AlertDialog, AlertDialogClose, AlertDialogContent, AlertDialogDescription, AlertDialogTitle, AlertDialogTrigger, Badge, Band, Blank, Button, Card, CardContent, CardHeader, CardTitle, CodeBlock, Composer, ContextDialog, 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, 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, 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, formatAgoPrecise, formatBytes, formatCost, formatCountdown, formatDuration, formatRateLimitWindow, formatRateLimitWindowLong, formatRelativeTime, formatTokens, friendlyModel, getChipsByTrigger, hashtagTrigger, isMutatingTool, isSegmentsEmpty, mentionTrigger, parseUserQuestions, permissionModeChoices, permissionModeMeta, plainTextToSegments, previewPatch, rateLimitWindowSeconds, rowShapeClass, segmentsToPlainText, skillPrompt, toast, toolIcon, toolInputPreview, useAffordances, useMinuteClock, usePromptAreaState, useTranscriptDensity, useTranscriptVariant };
996
+ export { AlertDialog, AlertDialogClose, AlertDialogContent, AlertDialogDescription, AlertDialogTitle, AlertDialogTrigger, Badge, Band, Blank, Button, Card, CardContent, CardHeader, CardTitle, CodeBlock, Composer, ContextDialog, 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, 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, segmentsToPlainText, skillPrompt, toast, toolIcon, toolInputPreview, useAffordances, useMinuteClock, usePromptAreaState, useTranscriptDensity, useTranscriptVariant };
774
997
 
775
998
  //# sourceMappingURL=index.mjs.map