@gperezmz/bb-plugin-thread-glance 0.2.4 → 0.4.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.
Files changed (54) hide show
  1. package/README.md +1 -1
  2. package/app.tsx +3 -11
  3. package/dist/app.css +1 -1
  4. package/dist/app.js +31 -31
  5. package/dist/app.meta.json +1 -1
  6. package/dist/server.js +10 -10
  7. package/dist/server.js.map +3 -3
  8. package/dist/server.meta.json +1 -1
  9. package/features/thread-list/components/FoldRows.tsx +21 -30
  10. package/features/thread-list/components/GroupSection.tsx +7 -45
  11. package/features/thread-list/components/ListHeader.tsx +79 -0
  12. package/features/thread-list/components/ProviderBadge.tsx +0 -1
  13. package/features/thread-list/components/PullRequestProbes.tsx +27 -0
  14. package/features/thread-list/components/SettingsPanel.tsx +20 -34
  15. package/features/thread-list/components/ThreadDetails.tsx +3 -3
  16. package/features/thread-list/components/ThreadList.tsx +117 -83
  17. package/features/thread-list/components/ThreadRowView.tsx +203 -180
  18. package/features/thread-list/components/controller.ts +5 -10
  19. package/features/thread-list/components/glyphs.tsx +10 -16
  20. package/features/thread-list/data/useAutoExpand.ts +1 -1
  21. package/features/thread-list/data/useDefaultBranches.ts +35 -0
  22. package/features/thread-list/data/usePullRequestAnswers.ts +36 -0
  23. package/features/thread-list/data/useSystemFacts.ts +52 -0
  24. package/features/thread-list/icons.ts +1 -0
  25. package/features/thread-list/index.ts +0 -1
  26. package/features/thread-list/model/attention.ts +2 -66
  27. package/features/thread-list/model/branches.ts +27 -0
  28. package/features/thread-list/model/counters.ts +5 -5
  29. package/features/thread-list/model/details.ts +6 -6
  30. package/features/thread-list/model/expansion.ts +5 -5
  31. package/features/thread-list/model/groups.ts +2 -2
  32. package/features/thread-list/model/labels.ts +43 -18
  33. package/features/thread-list/model/menu.ts +1 -0
  34. package/features/thread-list/model/move.ts +1 -1
  35. package/features/thread-list/model/notes.ts +2 -28
  36. package/features/thread-list/model/settings.ts +1 -17
  37. package/features/thread-list/model/settled.ts +122 -0
  38. package/features/thread-list/model/share.ts +1 -7
  39. package/features/thread-list/model/sort.ts +5 -16
  40. package/features/thread-list/model/state.ts +17 -16
  41. package/features/thread-list/model/time.ts +7 -0
  42. package/features/thread-list/model/toggles.ts +35 -24
  43. package/features/thread-list/model/{families.ts → trees.ts} +40 -28
  44. package/features/thread-list/model/view.ts +202 -274
  45. package/features/thread-list/testing/browser.css +13 -0
  46. package/features/thread-list/testing/fixtures.ts +41 -12
  47. package/package.json +5 -1
  48. package/server/cli.ts +1 -1
  49. package/server/import.ts +36 -6
  50. package/server/preference-store.ts +2 -1
  51. package/server.ts +1 -1
  52. package/shared/preferences.ts +38 -27
  53. package/skills/thread-glance/SKILL.md +11 -12
  54. package/features/thread-list/data/useAttentionHold.ts +0 -19
@@ -3,7 +3,7 @@
3
3
  "sdkVersion": "0.5.9",
4
4
  "artifactFormatVersion": 1,
5
5
  "pluginId": "thread-glance",
6
- "pluginVersion": "0.2.4",
6
+ "pluginVersion": "0.4.0",
7
7
  "builtWith": {
8
8
  "bbVersion": "0.43.4",
9
9
  "pluginSdkVersion": "0.5.9"
@@ -1,5 +1,5 @@
1
- // The fold rows (`N older` on a group, `N more child threads` on a family),
2
- // the "+N more" line in Needs attention, and the environment folder row.
1
+ // The fold rows (a group's settled fold, `N more child threads` in a tree)
2
+ // and the environment folder row.
3
3
  import { memo, useState } from "react";
4
4
  import { experimental_Icon as Icon, experimental_ProviderIcon as ProviderIcon } from "@get-bb/plugin-sdk/app";
5
5
  import type { PluginEnvironmentProvider } from "@get-bb/plugin-sdk/app";
@@ -11,9 +11,9 @@ import {
11
11
  } from "@/components/ui/dropdown-menu";
12
12
  import { cn } from "@/lib/utils";
13
13
  import { ICONS } from "../icons";
14
- import { olderRowText } from "../model/labels";
14
+ import { olderRowText, settledRowText } from "../model/labels";
15
15
  import { rowIndent } from "../model/layout";
16
- import type { EnvironmentRow, LeftOutRow, OlderRow } from "../model/view";
16
+ import type { EnvironmentRow, OlderRow, SettledRow } from "../model/view";
17
17
  import type { RowController } from "./controller";
18
18
  import { FlagGlyph } from "./glyphs";
19
19
  import { RenameEditor } from "./RenameEditor";
@@ -21,48 +21,39 @@ import { ROW_ICON_BUTTON } from "./ThreadRowView";
21
21
 
22
22
  export const OlderRowView = memo(function OlderRowView({ row, controller }: { row: OlderRow; controller: RowController }) {
23
23
  const { label, ariaLabel } = olderRowText(row);
24
- // A family's fold sits where its children do, with a dots glyph in the
25
- // Status column; the group's fold keeps the chevron.
26
- const inFamily = row.scope !== "group";
24
+ // It sits where the children do, with a dots glyph in the Status column.
27
25
  return (
28
26
  <button
29
27
  type="button"
30
28
  aria-expanded={row.expanded}
31
29
  aria-label={ariaLabel}
32
30
  onClick={() => controller.onToggleOlder(row)}
33
- className={cn(
34
- "relative flex w-full items-center rounded-md pr-2 text-left text-xs text-muted-foreground outline-none hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 focus-visible:ring-sidebar-ring",
35
- inFamily ? "h-7 gap-1.5 max-md:pointer-coarse:h-9" : "h-6 gap-1",
36
- )}
37
- style={{ paddingLeft: rowIndent(row.depth) + (inFamily ? 0 : 2) }}
31
+ className="relative flex h-7 w-full items-center gap-1.5 rounded-md pr-2 text-left text-xs text-muted-foreground outline-none hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 focus-visible:ring-sidebar-ring max-md:pointer-coarse:h-9"
32
+ style={{ paddingLeft: rowIndent(row.depth) }}
38
33
  >
39
- {inFamily ? (
40
- <span aria-hidden className="inline-flex size-4 shrink-0 items-center justify-center">
41
- <Icon name={ICONS.more} className="size-3.5" />
42
- </span>
43
- ) : (
44
- <Icon name={ICONS.expand} aria-hidden className={cn("size-3 transition-transform", row.expanded && "-rotate-90")} />
45
- )}
34
+ <span aria-hidden className="inline-flex size-4 shrink-0 items-center justify-center">
35
+ <Icon name={ICONS.more} className="size-3.5" />
36
+ </span>
46
37
  {label}
47
38
  </button>
48
39
  );
49
40
  });
50
41
 
51
- /** The "+N more" line under a closed family in Needs attention: it opens the family, as the chip does. */
52
- export const LeftOutRowView = memo(function LeftOutRowView({ row, controller }: { row: LeftOutRow; controller: RowController }) {
53
- const noun = row.count === 1 ? "child thread" : "child threads";
42
+ /** The settled fold: a faint label, a hairline to the row's end, and a chevron. */
43
+ export const SettledRowView = memo(function SettledRowView({ row, controller }: { row: SettledRow; controller: RowController }) {
44
+ const { label, ariaLabel } = settledRowText(row);
54
45
  return (
55
46
  <button
56
47
  type="button"
57
- aria-expanded={false}
58
- aria-label={`Show ${row.count} more ${noun}`}
59
- title={`Show ${row.count} more ${noun}`}
60
- onClick={() => controller.onOpenChildren(row.scopeId)}
61
- className="relative flex h-6 w-full items-center gap-1.5 rounded-md pr-2 text-left text-xs text-muted-foreground outline-none hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 focus-visible:ring-sidebar-ring"
62
- style={{ paddingLeft: rowIndent(row.depth) }}
48
+ aria-expanded={row.expanded}
49
+ aria-label={ariaLabel}
50
+ onClick={() => controller.onToggleSettled(row)}
51
+ className="group/settled relative flex h-6 w-full items-center gap-2 rounded-md pr-1 text-left text-[11px] text-muted-foreground/70 outline-none hover:text-muted-foreground focus-visible:ring-2 focus-visible:ring-sidebar-ring max-md:pointer-coarse:h-9"
52
+ style={{ paddingLeft: rowIndent(0) }}
63
53
  >
64
- <span aria-hidden className="size-4 shrink-0" />
65
- +{row.count} more
54
+ <span className="shrink-0 tabular-nums">{label}</span>
55
+ <span aria-hidden className="h-px flex-1 bg-border/60" />
56
+ <Icon name={ICONS.expand} aria-hidden className={cn("size-3 shrink-0 transition-transform duration-150", row.expanded && "rotate-90")} />
66
57
  </button>
67
58
  );
68
59
  });
@@ -1,5 +1,5 @@
1
1
  // A top-level group: its header with counters, and its rows, windowed
2
- // in chunks; and the Needs attention section above every group.
2
+ // in chunks.
3
3
  import { memo, useEffect, useRef, useState } from "react";
4
4
  import { useDraggable, useDroppable } from "@dnd-kit/core";
5
5
  import { experimental_Icon as Icon } from "@get-bb/plugin-sdk/app";
@@ -14,10 +14,10 @@ import {
14
14
  } from "@/components/ui/dropdown-menu";
15
15
  import { cn } from "@/lib/utils";
16
16
  import { ICONS } from "../icons";
17
- import { ATTENTION_GROUP_ID, type GroupView, type AttentionView, type Row } from "../model/view";
17
+ import type { GroupView, Row } from "../model/view";
18
18
  import { chunk, windowedNavValue } from "../model/windowing";
19
19
  import type { RowController } from "./controller";
20
- import { EnvironmentRowView, LeftOutRowView, OlderRowView } from "./FoldRows";
20
+ import { EnvironmentRowView, OlderRowView, SettledRowView } from "./FoldRows";
21
21
  import { CounterStrip } from "./glyphs";
22
22
  import { visibleCounters } from "../model/counters";
23
23
  import { RenameEditor } from "./RenameEditor";
@@ -343,8 +343,8 @@ function Rows({
343
343
  />
344
344
  ) : row.type === "older" ? (
345
345
  <OlderRowView key={row.key} row={row} controller={rowController} />
346
- ) : row.type === "left-out" ? (
347
- <LeftOutRowView key={row.key} row={row} controller={rowController} />
346
+ ) : row.type === "settled" ? (
347
+ <SettledRowView key={row.key} row={row} controller={rowController} />
348
348
  ) : (
349
349
  <EnvironmentRowView
350
350
  key={row.key}
@@ -392,9 +392,8 @@ export const GroupSection = memo(function GroupSection({
392
392
  inOverflow={inOverflow}
393
393
  dropActive={dropTargetGroupId === group.descriptor.id}
394
394
  />
395
- {group.collapsed ? null : group.rows.length === 0 ? (
396
- // A group whose every family sits in Needs attention draws only its header.
397
- group.rootIds.length === 0 ? <p className="py-1 pl-8 text-xs text-muted-foreground">No threads</p> : null
395
+ {group.rows.length === 0 ? (
396
+ group.collapsed || group.rootIds.length > 0 ? null : <p className="py-1 pl-8 text-xs text-muted-foreground">No threads</p>
398
397
  ) : (
399
398
  <Rows
400
399
  {...rest}
@@ -407,40 +406,3 @@ export const GroupSection = memo(function GroupSection({
407
406
  </section>
408
407
  );
409
408
  });
410
-
411
- // bb's attention colour, thinned over the sidebar: the one colour the section
412
- // adds. Opaque, so the sticky header hides the rows scrolling under it. Mixed
413
- // in oklab: the sidebar's grey has hue 0 in oklch, which would pull the tint
414
- // toward pink. Set as the surface quiet titles mix toward; the band is
415
- // lighter than the sidebar in the dark theme, so they keep 70% of the
416
- // foreground there to stay above 4.5:1 (68% measured 4.44:1).
417
- const ATTENTION_BAND =
418
- "[--tg-surface:color-mix(in_oklab,var(--attention)_8%,var(--sidebar))] [--tg-quiet:70%] bg-[var(--tg-surface)]";
419
- const ATTENTION_COUNT =
420
- "bg-[color-mix(in_oklab,var(--attention)_20%,transparent)] text-[color:color-mix(in_oklab,var(--attention),var(--foreground)_55%)]";
421
-
422
- /**
423
- * The Needs attention section: a header with its family count (none when every
424
- * family in it is attended), then its rows, on one band of bb's attention
425
- * colour. It has no collapse, menu or drag, and no drop target of its own.
426
- */
427
- export const AttentionSection = memo(function AttentionSection({ view, ...rest }: SectionProps & { view: AttentionView }) {
428
- return (
429
- <section aria-label="Needs attention" className={cn("relative mb-1 flex w-full min-w-0 flex-col rounded-md pb-0.5", ATTENTION_BAND)}>
430
- <h2
431
- className="sticky top-0 z-20 flex h-7 items-center gap-1 rounded-t-md bg-[var(--tg-surface)] pl-2 pr-1 text-xs font-medium text-muted-foreground max-md:pointer-coarse:h-9"
432
- >
433
- <span className="min-w-0 flex-1 truncate">Needs attention</span>
434
- {view.familyCount > 0 ? (
435
- <span
436
- className={cn("rounded-full px-1.5 text-[11px] leading-4 tabular-nums", ATTENTION_COUNT)}
437
- aria-label={`${view.familyCount} ${view.familyCount === 1 ? "family" : "families"}`}
438
- >
439
- {view.familyCount}
440
- </span>
441
- ) : null}
442
- </h2>
443
- <Rows {...rest} rows={view.rows} groupId={ATTENTION_GROUP_ID} inPinned={false} forceMount={false} />
444
- </section>
445
- );
446
- });
@@ -0,0 +1,79 @@
1
+ // The list header: the grouping's name, the need-you filter, Mark all read
2
+ // and the settings button, above every group.
3
+ import { memo, useState } from "react";
4
+ import { experimental_Icon as Icon } from "@get-bb/plugin-sdk/app";
5
+ import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
6
+ import { cn } from "@/lib/utils";
7
+ import type { ClientPreferences, OrganizationMode, Preferences } from "@/shared/preferences";
8
+ import { ICONS } from "../icons";
9
+ import { groupingName } from "../model/labels";
10
+ import { SettingsPanel } from "./SettingsPanel";
11
+ import { ROW_ICON_BUTTON } from "./ThreadRowView";
12
+
13
+ // bb's attention colour, thinned, as the need-you filter's own: the list's
14
+ // one place to see what waits on you.
15
+ const NEED_YOU =
16
+ "bg-[color-mix(in_oklab,var(--attention)_14%,transparent)] text-[color:color-mix(in_oklab,var(--attention),var(--foreground)_55%)] hover:bg-[color-mix(in_oklab,var(--attention)_24%,transparent)]";
17
+ const NEED_YOU_ON = "bg-[color-mix(in_oklab,var(--attention)_30%,transparent)] text-foreground";
18
+
19
+ export interface ListHeaderProps {
20
+ mode: OrganizationMode;
21
+ /** Thread trees that need attention; the filter is not drawn at 0. */
22
+ needYouCount: number;
23
+ needYouOnly: boolean;
24
+ onToggleNeedYou(): void;
25
+ onMarkAllRead(): void;
26
+ prefs: Preferences;
27
+ client: ClientPreferences;
28
+ onPrefs(patch: Partial<Preferences>): void;
29
+ onClient(patch: Partial<ClientPreferences>): void;
30
+ }
31
+
32
+ export const ListHeader = memo(function ListHeader({
33
+ mode,
34
+ needYouCount,
35
+ needYouOnly,
36
+ onToggleNeedYou,
37
+ onMarkAllRead,
38
+ prefs,
39
+ client,
40
+ onPrefs,
41
+ onClient,
42
+ }: ListHeaderProps) {
43
+ const [settingsOpen, setSettingsOpen] = useState(false);
44
+ return (
45
+ <div
46
+ data-sidebar="list-header"
47
+ className="flex h-7 items-center gap-1 pl-2 pr-1 text-xs text-muted-foreground max-md:pointer-coarse:h-9"
48
+ >
49
+ <h2 className="min-w-0 flex-1 truncate font-medium select-none">{groupingName(mode)}</h2>
50
+ {needYouCount > 0 ? (
51
+ <button
52
+ type="button"
53
+ aria-pressed={needYouOnly}
54
+ title={needYouOnly ? "Show every thread" : "Show only what needs you"}
55
+ onClick={onToggleNeedYou}
56
+ className={cn(
57
+ "inline-flex h-5 shrink-0 items-center rounded-full px-1.5 text-[11px] leading-none tabular-nums outline-none focus-visible:ring-2 focus-visible:ring-sidebar-ring",
58
+ needYouOnly ? NEED_YOU_ON : NEED_YOU,
59
+ )}
60
+ >
61
+ {needYouCount} need you
62
+ </button>
63
+ ) : null}
64
+ <button type="button" aria-label="Mark all read" title="Mark all read" className={ROW_ICON_BUTTON} onClick={onMarkAllRead}>
65
+ <Icon name={ICONS.markRead} aria-hidden className="size-4" />
66
+ </button>
67
+ <Popover open={settingsOpen} onOpenChange={setSettingsOpen}>
68
+ <PopoverTrigger asChild>
69
+ <button type="button" aria-label="Thread Glance settings" title="Settings" className={ROW_ICON_BUTTON}>
70
+ <Icon name={ICONS.settings} aria-hidden className="size-4" />
71
+ </button>
72
+ </PopoverTrigger>
73
+ <PopoverContent side="bottom" align="end" className="w-80 max-w-[calc(100vw-1rem)] p-1">
74
+ <SettingsPanel prefs={prefs} client={client} onPrefs={onPrefs} onClient={onClient} />
75
+ </PopoverContent>
76
+ </Popover>
77
+ </div>
78
+ );
79
+ });
@@ -26,7 +26,6 @@ export function ProviderBadge({
26
26
  mode?: HarnessIcon;
27
27
  className?: string;
28
28
  }) {
29
- if (mode === "hidden") return null;
30
29
  const muted = mode === "muted";
31
30
  if (display.provider !== null) {
32
31
  // Without its tint the logo draws as a currentColor mask.
@@ -0,0 +1,27 @@
1
+ // Looks up the pull request of every thread settling has to know about and
2
+ // reports each answer. Draws nothing: the host shares one lookup per
3
+ // environment with the badges that ask for the same threads.
4
+ import { memo, useEffect } from "react";
5
+ import { experimental_useSidebarThreadPullRequest as usePullRequest } from "@get-bb/plugin-sdk/app";
6
+ import type { PullRequestState } from "../model/settled";
7
+
8
+ type Answer = (threadId: string, state: PullRequestState | null) => void;
9
+
10
+ const Probe = memo(function Probe({ threadId, onAnswer }: { threadId: string; onAnswer: Answer }) {
11
+ const { isLoading, pullRequest } = usePullRequest(threadId);
12
+ const state = isLoading ? undefined : (pullRequest?.state ?? null);
13
+ useEffect(() => {
14
+ if (state !== undefined) onAnswer(threadId, state);
15
+ }, [threadId, state, onAnswer]);
16
+ return null;
17
+ });
18
+
19
+ export function PullRequestProbes({ threadIds, onAnswer }: { threadIds: readonly string[]; onAnswer: Answer }) {
20
+ return (
21
+ <>
22
+ {threadIds.map((threadId) => (
23
+ <Probe key={threadId} threadId={threadId} onAnswer={onAnswer} />
24
+ ))}
25
+ </>
26
+ );
27
+ }
@@ -1,11 +1,9 @@
1
- // Thread Glance's settings, shown by its item in bb's sidebar footer.
2
- import { experimental_Icon as Icon, type ExperimentalSidebarFooterDisclosureProps } from "@get-bb/plugin-sdk/app";
1
+ // Thread Glance's settings, under the list header's settings button.
2
+ import { experimental_Icon as Icon } from "@get-bb/plugin-sdk/app";
3
3
  import { cn } from "@/lib/utils";
4
4
  import type { ClientPreferences, Preferences } from "@/shared/preferences";
5
- import { childAttentionFor, countsEveryChild, lifecyclesFor, sortArrow, sortFieldPatch, threadsShown } from "../model/settings";
5
+ import { childAttentionFor, countsEveryChild, sortArrow, sortFieldPatch } from "../model/settings";
6
6
  import { effectiveSortField } from "../model/sort";
7
- import { useClientPreferences } from "../data/useClientPreferences";
8
- import { usePreferences } from "../data/usePreferences";
9
7
  import { ICONS } from "../icons";
10
8
 
11
9
  // bb's own segmented controls (the Reasoning picker, the diff view toggle)
@@ -105,7 +103,8 @@ function Heading({ children }: { children: React.ReactNode }) {
105
103
  return <h3 className="px-2 pb-0.5 pt-3 text-xs font-medium text-muted-foreground first:pt-0">{children}</h3>;
106
104
  }
107
105
 
108
- function SettingsPanel({
106
+ /** Thread Glance's settings, as the list header's settings button opens them. */
107
+ export function SettingsPanel({
109
108
  prefs,
110
109
  client,
111
110
  onPrefs,
@@ -153,14 +152,26 @@ function SettingsPanel({
153
152
  {arrow.glyph}
154
153
  </button>
155
154
  </Line>
156
- <Toggle label="Working threads first" checked={prefs.workingFirst} onChange={(value) => onPrefs({ workingFirst: value })} />
157
155
  <Toggle
158
156
  label="Worktrees as folders"
159
157
  description="Threads sharing a worktree fold into one row"
160
158
  checked={prefs.environmentGrouping}
161
159
  onChange={(value) => onPrefs({ environmentGrouping: value })}
162
160
  />
163
- <Toggle label="Collapse older threads" checked={prefs.foldOlder} onChange={(value) => onPrefs({ foldOlder: value })} />
161
+ <Line label="Settle after">
162
+ <Segmented
163
+ label="Settle after"
164
+ options={[
165
+ ["12h", "12h"],
166
+ ["1d", "1d"],
167
+ ["3d", "3d"],
168
+ ["1w", "1w"],
169
+ ["never", "Never"],
170
+ ]}
171
+ current={prefs.settleAfter}
172
+ onSelect={(value) => onPrefs({ settleAfter: value })}
173
+ />
174
+ </Line>
164
175
  <Heading>Rows</Heading>
165
176
  <Line label="Density">
166
177
  <Segmented
@@ -179,26 +190,12 @@ function SettingsPanel({
179
190
  options={[
180
191
  ["muted", "Muted"],
181
192
  ["colour", "Colour"],
182
- ["hidden", "Hidden"],
183
193
  ]}
184
194
  current={prefs.harnessIcon}
185
195
  onSelect={(value) => onPrefs({ harnessIcon: value })}
186
196
  />
187
197
  </Line>
188
- <Toggle label="Pull request badge" checked={prefs.showPullRequests} onChange={(value) => onPrefs({ showPullRequests: value })} />
189
- <Heading>Show</Heading>
190
- <Line label="Threads">
191
- <Segmented
192
- label="Threads"
193
- options={[
194
- ["active", "Active"],
195
- ["archived", "Archived"],
196
- ["both", "Both"],
197
- ]}
198
- current={threadsShown(prefs.threadLifecycles)}
199
- onSelect={(value) => onPrefs({ threadLifecycles: lifecyclesFor(value) })}
200
- />
201
- </Line>
198
+ <Heading>Attention</Heading>
202
199
  <Toggle
203
200
  label="Needs attention counts every child"
204
201
  description="Every unread or failed child thread; otherwise only those blocked on you."
@@ -208,14 +205,3 @@ function SettingsPanel({
208
205
  </div>
209
206
  );
210
207
  }
211
-
212
- /** The footer item's panel, reading and writing the preferences the list reads. */
213
- export function SettingsDisclosure(_props: ExperimentalSidebarFooterDisclosureProps) {
214
- const { prefs, update } = usePreferences();
215
- const [client, updateClient] = useClientPreferences();
216
- return (
217
- <div className="p-1">
218
- <SettingsPanel prefs={prefs} client={client} onPrefs={update} onClient={updateClient} />
219
- </div>
220
- );
221
- }
@@ -2,7 +2,7 @@
2
2
  import { useContext, useEffect, useState } from "react";
3
3
  import { experimental_Icon as Icon, experimental_useSidebarThreadPullRequest as usePullRequest } from "@get-bb/plugin-sdk/app";
4
4
  import { Button } from "@/components/ui/button";
5
- import type { ThreadInfo } from "../model/families";
5
+ import type { ThreadInfo } from "../model/trees";
6
6
  import { childSummary, descendantsOf, formatDateTime, sinceLabel } from "../model/details";
7
7
  import { lastReply } from "../model/notes";
8
8
  import { finishedAtFor, stateSince } from "../model/time";
@@ -70,7 +70,7 @@ export function ThreadDetails({
70
70
  const model = useModel(controller, info);
71
71
  const live = useContext(ListLiveContext);
72
72
  const since = sinceLabel(info.state.kind, stateSince(info.state.kind, thread.id, live.stamps, live.now));
73
- const children = childSummary(descendantsOf(live.familyOf(thread.id), thread.id));
73
+ const children = childSummary(descendantsOf(live.treeOf(thread.id), thread.id));
74
74
  const environment = thread.environment;
75
75
  const finished = finishedAtFor(thread, live.stamps.finishedAt);
76
76
  const reply = lastReply(live.notes[thread.id]);
@@ -93,7 +93,7 @@ export function ThreadDetails({
93
93
  <dl className="flex flex-col gap-1">
94
94
  <Line label="Harness">
95
95
  <span className="inline-flex items-center gap-1.5">
96
- <ProviderBadge display={provider} mode={controller.harnessIcon === "hidden" ? "muted" : controller.harnessIcon} />
96
+ <ProviderBadge display={provider} mode={controller.harnessIcon} />
97
97
  {provider.name}
98
98
  </span>
99
99
  </Line>