@gperezmz/bb-plugin-thread-glance 0.2.1 → 0.2.2
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/README.md +2 -2
- package/dist/app.css +1 -1
- package/dist/app.js +29 -29
- package/dist/app.meta.json +1 -1
- package/dist/server.js +1 -1
- package/dist/server.js.map +2 -2
- package/dist/server.meta.json +1 -1
- package/features/thread-list/components/FoldRows.tsx +12 -11
- package/features/thread-list/components/GroupSection.tsx +131 -102
- package/features/thread-list/components/ThreadDetails.tsx +2 -3
- package/features/thread-list/components/ThreadList.tsx +10 -9
- package/features/thread-list/components/ThreadRowView.tsx +22 -18
- package/features/thread-list/components/Toolbar.tsx +1 -1
- package/features/thread-list/components/controller.ts +2 -0
- package/features/thread-list/components/glyphs.tsx +19 -1
- package/features/thread-list/data/{useNeedsYouHold.ts → useAttentionHold.ts} +5 -5
- package/features/thread-list/model/{needs-you.ts → attention.ts} +24 -24
- package/features/thread-list/model/counters.ts +3 -3
- package/features/thread-list/model/details.ts +1 -1
- package/features/thread-list/model/expansion.ts +10 -10
- package/features/thread-list/model/families.ts +26 -22
- package/features/thread-list/model/layout.ts +0 -43
- package/features/thread-list/model/notes.ts +37 -4
- package/features/thread-list/model/settings.ts +1 -1
- package/features/thread-list/model/share.ts +3 -3
- package/features/thread-list/model/state.ts +1 -1
- package/features/thread-list/model/toggles.ts +7 -5
- package/features/thread-list/model/view.ts +68 -47
- package/features/thread-list/testing/fixtures.ts +3 -3
- package/package.json +1 -1
- package/server/cli.ts +1 -1
- package/shared/preferences.ts +2 -2
- package/skills/thread-glance/SKILL.md +4 -4
- package/features/thread-list/components/Rails.tsx +0 -27
|
@@ -20,10 +20,10 @@ import {
|
|
|
20
20
|
} from "./groups";
|
|
21
21
|
import { addCounters, countFamilies, EMPTY_COUNTERS, type Counters } from "./counters";
|
|
22
22
|
import { comparePinned, effectiveSortField, makeComparator, type SortKey } from "./sort";
|
|
23
|
-
import {
|
|
23
|
+
import { inAttention } from "./attention";
|
|
24
24
|
import { mostUrgent, type Flag } from "./state";
|
|
25
25
|
import type { Targets } from "./expansion";
|
|
26
|
-
import {
|
|
26
|
+
import type { RowNote } from "./notes";
|
|
27
27
|
|
|
28
28
|
/** How many of a group's newest quiet roots stay out of its older fold. */
|
|
29
29
|
export const KEEP_QUIET = 5;
|
|
@@ -50,13 +50,18 @@ export interface ThreadRow {
|
|
|
50
50
|
/** Title of the thread this one attaches to, for tooltips and labels. */
|
|
51
51
|
parentTitle: string | null;
|
|
52
52
|
chip: Chip | null;
|
|
53
|
-
/**
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
/**
|
|
54
|
+
* The line under the title. In Needs attention, a row that itself needs
|
|
55
|
+
* attention says why; any other row says why it waits on you or failed.
|
|
56
|
+
*/
|
|
57
|
+
note: RowNote | null;
|
|
58
|
+
/** The title, and the chip with it, step back: see `isDimmed`. */
|
|
59
|
+
dimmed: boolean;
|
|
60
|
+
/** A hidden thread shown because it needs attention or failed. */
|
|
56
61
|
hiddenBadge: boolean;
|
|
57
62
|
/** "In project X" when the thread is outside its family's group. */
|
|
58
63
|
crossGroupLabel: string | null;
|
|
59
|
-
/** A root in Needs
|
|
64
|
+
/** A root in Needs attention: its home group's name, drawn where the age goes. */
|
|
60
65
|
homeGroupLabel: string | null;
|
|
61
66
|
projectId: string;
|
|
62
67
|
}
|
|
@@ -71,7 +76,6 @@ export interface OlderRow {
|
|
|
71
76
|
count: number;
|
|
72
77
|
expanded: boolean;
|
|
73
78
|
depth: number;
|
|
74
|
-
rails: (Rail | null)[];
|
|
75
79
|
}
|
|
76
80
|
|
|
77
81
|
export interface EnvironmentRow {
|
|
@@ -86,10 +90,9 @@ export interface EnvironmentRow {
|
|
|
86
90
|
collapsed: boolean;
|
|
87
91
|
flag: Flag | null;
|
|
88
92
|
depth: number;
|
|
89
|
-
rails: (Rail | null)[];
|
|
90
93
|
}
|
|
91
94
|
|
|
92
|
-
/** Under a family in Needs
|
|
95
|
+
/** Under a closed family in Needs attention: how many of its child threads it leaves out ("+N more"). Opens the family. */
|
|
93
96
|
export interface LeftOutRow {
|
|
94
97
|
type: "left-out";
|
|
95
98
|
key: string;
|
|
@@ -97,7 +100,6 @@ export interface LeftOutRow {
|
|
|
97
100
|
scopeId: string;
|
|
98
101
|
count: number;
|
|
99
102
|
depth: number;
|
|
100
|
-
rails: (Rail | null)[];
|
|
101
103
|
}
|
|
102
104
|
|
|
103
105
|
export type Row = ThreadRow | OlderRow | EnvironmentRow | LeftOutRow;
|
|
@@ -111,15 +113,15 @@ export interface GroupView {
|
|
|
111
113
|
collapsed: boolean;
|
|
112
114
|
hidden: boolean;
|
|
113
115
|
rows: Row[];
|
|
114
|
-
/** Every family root bucketed in the group, those in Needs
|
|
116
|
+
/** Every family root bucketed in the group, those in Needs attention and behind folds included. */
|
|
115
117
|
rootIds: string[];
|
|
116
118
|
}
|
|
117
119
|
|
|
118
|
-
/** The id Needs
|
|
119
|
-
export const
|
|
120
|
+
/** The id Needs attention's rows are drawn under; no group has it. */
|
|
121
|
+
export const ATTENTION_GROUP_ID = "attention";
|
|
120
122
|
|
|
121
|
-
/** The Needs
|
|
122
|
-
export interface
|
|
123
|
+
/** The Needs attention section: every family with a thread that needs attention, or held there while one of its threads is open. */
|
|
124
|
+
export interface AttentionView {
|
|
123
125
|
/** How many families the section holds, for its header. */
|
|
124
126
|
familyCount: number;
|
|
125
127
|
rows: Row[];
|
|
@@ -129,7 +131,7 @@ export interface NeedsYouView {
|
|
|
129
131
|
|
|
130
132
|
export interface ListView {
|
|
131
133
|
/** Null when no family is in the section. */
|
|
132
|
-
|
|
134
|
+
attention: AttentionView | null;
|
|
133
135
|
groups: GroupView[];
|
|
134
136
|
/** Hidden groups, shown in the More popover. */
|
|
135
137
|
more: GroupView[];
|
|
@@ -147,7 +149,7 @@ export interface ViewInputs {
|
|
|
147
149
|
sections: readonly PluginSidebarSection[];
|
|
148
150
|
prefs: Preferences;
|
|
149
151
|
activeThreadId: string | null;
|
|
150
|
-
/** The root of the family Needs
|
|
152
|
+
/** The root of the family Needs attention keeps while one of its threads is open. */
|
|
151
153
|
heldRootId: string | null;
|
|
152
154
|
targets: Targets;
|
|
153
155
|
}
|
|
@@ -164,6 +166,8 @@ interface Context extends ViewInputs {
|
|
|
164
166
|
activePath: ReadonlySet<string>;
|
|
165
167
|
projectNames: ReadonlyMap<string, string>;
|
|
166
168
|
sectionNames: ReadonlyMap<string, string>;
|
|
169
|
+
/** The rows are drawn in Needs attention. */
|
|
170
|
+
drawnInAttention: boolean;
|
|
167
171
|
}
|
|
168
172
|
|
|
169
173
|
function titleOf(context: Context, id: string | null): string | null {
|
|
@@ -190,12 +194,23 @@ function crossGroupLabel(context: Context, info: ThreadInfo, root: ThreadInfo):
|
|
|
190
194
|
}
|
|
191
195
|
}
|
|
192
196
|
|
|
197
|
+
/**
|
|
198
|
+
* Whether a row's title steps back: a quiet thread does, at any depth, so
|
|
199
|
+
* brightness shows state and depth is left to the indent and smaller text.
|
|
200
|
+
* A root with children stays bright until its whole family is quiet.
|
|
201
|
+
*/
|
|
202
|
+
function isDimmed(context: Context, info: ThreadInfo, chip: Chip | null): boolean {
|
|
203
|
+
if (info.parentId === null && chip !== null) return context.forest.familyOf.get(info.thread.id)?.quiet ?? info.quiet;
|
|
204
|
+
return info.quiet;
|
|
205
|
+
}
|
|
206
|
+
|
|
193
207
|
function threadRow(
|
|
194
208
|
context: Context,
|
|
195
209
|
info: ThreadInfo,
|
|
196
210
|
root: ThreadInfo,
|
|
197
211
|
options: { depth: number; nested: boolean; chip: Chip | null },
|
|
198
212
|
): ThreadRow {
|
|
213
|
+
const needsAttention = !info.thread.isArchived && info.attentionFlags.size > 0;
|
|
199
214
|
return {
|
|
200
215
|
type: "thread",
|
|
201
216
|
key: `thread:${info.thread.id}`,
|
|
@@ -204,7 +219,8 @@ function threadRow(
|
|
|
204
219
|
nested: options.nested,
|
|
205
220
|
parentTitle: titleOf(context, info.parentId),
|
|
206
221
|
chip: options.chip,
|
|
207
|
-
|
|
222
|
+
note: context.drawnInAttention && needsAttention ? info.attentionNote : info.note,
|
|
223
|
+
dimmed: isDimmed(context, info, options.chip),
|
|
208
224
|
hiddenBadge: info.thread.isHidden,
|
|
209
225
|
crossGroupLabel: crossGroupLabel(context, info, root),
|
|
210
226
|
homeGroupLabel: null,
|
|
@@ -212,11 +228,11 @@ function threadRow(
|
|
|
212
228
|
};
|
|
213
229
|
}
|
|
214
230
|
|
|
215
|
-
/** The children of `parent` that get a row: hidden ones only when they need
|
|
231
|
+
/** The children of `parent` that get a row: hidden ones only when they need attention or failed. */
|
|
216
232
|
function eligibleChildren(context: Context, parentId: string): ThreadInfo[] {
|
|
217
233
|
return (context.forest.children.get(parentId) ?? [])
|
|
218
234
|
.map((id) => context.forest.infos.get(id)!)
|
|
219
|
-
.filter((info) => !info.thread.isHidden || info.
|
|
235
|
+
.filter((info) => !info.thread.isHidden || info.attentionFlags.size > 0);
|
|
220
236
|
}
|
|
221
237
|
|
|
222
238
|
function subtreeOf(context: Context, id: string): Subtree {
|
|
@@ -248,7 +264,6 @@ function foldedChildren(
|
|
|
248
264
|
count,
|
|
249
265
|
expanded,
|
|
250
266
|
depth,
|
|
251
|
-
rails: [],
|
|
252
267
|
}
|
|
253
268
|
: null;
|
|
254
269
|
|
|
@@ -330,7 +345,6 @@ function clusterEnvironments(context: Context, units: Unit[], depth: number): Ro
|
|
|
330
345
|
collapsed,
|
|
331
346
|
flag: collapsed ? mostUrgent(flags) : null,
|
|
332
347
|
depth,
|
|
333
|
-
rails: [],
|
|
334
348
|
});
|
|
335
349
|
if (!collapsed) {
|
|
336
350
|
for (const member of members) {
|
|
@@ -357,7 +371,7 @@ function childProviders(parent: ThreadInfo, descendants: readonly ThreadInfo[]):
|
|
|
357
371
|
function chipOf(context: Context, info: ThreadInfo, expanded: boolean): Chip | null {
|
|
358
372
|
const subtree = subtreeOf(context, info.thread.id);
|
|
359
373
|
const hasChildren =
|
|
360
|
-
subtree.visibleCount > 0 || subtree.descendants.some((descendant) => descendant.thread.isHidden && descendant.
|
|
374
|
+
subtree.visibleCount > 0 || subtree.descendants.some((descendant) => descendant.thread.isHidden && descendant.attentionFlags.size > 0);
|
|
361
375
|
if (!hasChildren) return null;
|
|
362
376
|
return {
|
|
363
377
|
count: subtree.visibleCount,
|
|
@@ -369,7 +383,7 @@ function chipOf(context: Context, info: ThreadInfo, expanded: boolean): Chip | n
|
|
|
369
383
|
|
|
370
384
|
/** What a thread and everything under it carry, for an environment folder's glyph. */
|
|
371
385
|
function rollupFlags(context: Context, info: ThreadInfo): Set<Flag> {
|
|
372
|
-
const flags = new Set<Flag>([...info.
|
|
386
|
+
const flags = new Set<Flag>([...info.attentionFlags, ...subtreeOf(context, info.thread.id).flags]);
|
|
373
387
|
if (info.flags.has("working")) flags.add("working");
|
|
374
388
|
return flags;
|
|
375
389
|
}
|
|
@@ -408,7 +422,7 @@ function familyUnit(context: Context, family: Family): Unit {
|
|
|
408
422
|
|
|
409
423
|
/** Waiting on you, then failed, then offline, then unread, then the rest. */
|
|
410
424
|
export function urgency(family: Family): number {
|
|
411
|
-
const flags = family.
|
|
425
|
+
const flags = family.attentionFlags;
|
|
412
426
|
if (flags.has("waits-on-you")) return 0;
|
|
413
427
|
if (flags.has("unread-failed") || flags.has("queue-failed")) return 1;
|
|
414
428
|
if (flags.has("offline")) return 2;
|
|
@@ -417,20 +431,30 @@ export function urgency(family: Family): number {
|
|
|
417
431
|
}
|
|
418
432
|
|
|
419
433
|
/**
|
|
420
|
-
* One family's rows in Needs
|
|
421
|
-
* that needs
|
|
422
|
-
*
|
|
434
|
+
* One family's rows in Needs attention. It arrives with the path from the root
|
|
435
|
+
* down to each thread that needs attention or is open. Closed, the path is all
|
|
436
|
+
* it draws, with one "+N more" line for the child threads left out; opened
|
|
437
|
+
* with the root's chip, it draws as in its home group and keeps the path.
|
|
423
438
|
*/
|
|
424
|
-
function
|
|
439
|
+
function attentionFamilyRows(groupContext: Context, family: Family, homeGroupLabel: string): Row[] {
|
|
440
|
+
const context: Context = { ...groupContext, drawnInAttention: true };
|
|
425
441
|
const root = family.root;
|
|
426
442
|
const onPath = new Set<string>([root.thread.id]);
|
|
427
443
|
for (const info of family.descendants) {
|
|
428
|
-
const
|
|
429
|
-
if (!
|
|
444
|
+
const attention = !info.thread.isArchived && info.attentionFlags.size > 0;
|
|
445
|
+
if (!attention && !info.isActive) continue;
|
|
430
446
|
onPath.add(info.thread.id);
|
|
431
447
|
for (const ancestor of ancestorsOf(info.thread.id, context.forest.infos, root.thread.id)) onPath.add(ancestor);
|
|
432
448
|
}
|
|
433
|
-
const
|
|
449
|
+
const expanded = context.expandedChildren.has(root.thread.id);
|
|
450
|
+
if (expanded) {
|
|
451
|
+
// The path is kept the way a reveal target's is.
|
|
452
|
+
const kept: Context = { ...context, revealIds: new Set([...context.revealIds, ...onPath]) };
|
|
453
|
+
const [rootRow, ...rest] = foldedFamilyRows(kept, family);
|
|
454
|
+
return [{ ...(rootRow as ThreadRow), homeGroupLabel }, ...rest];
|
|
455
|
+
}
|
|
456
|
+
const chip = chipOf(context, root, false);
|
|
457
|
+
const rows: Row[] = [{ ...threadRow(context, root, root, { depth: 0, nested: false, chip }), homeGroupLabel }];
|
|
434
458
|
const walk = (parentId: string, depth: number) => {
|
|
435
459
|
for (const id of context.forest.children.get(parentId) ?? []) {
|
|
436
460
|
if (!onPath.has(id)) continue;
|
|
@@ -440,15 +464,15 @@ function needsYouFamilyRows(context: Context, family: Family, homeGroupLabel: st
|
|
|
440
464
|
};
|
|
441
465
|
walk(root.thread.id, 1);
|
|
442
466
|
const left = family.descendants.filter((info) => !info.thread.isHidden && !onPath.has(info.thread.id)).length;
|
|
443
|
-
if (left > 0) rows.push({ type: "left-out", key: `left-out:${root.thread.id}`, scopeId: root.thread.id, count: left, depth: 1
|
|
467
|
+
if (left > 0) rows.push({ type: "left-out", key: `left-out:${root.thread.id}`, scopeId: root.thread.id, count: left, depth: 1 });
|
|
444
468
|
return rows;
|
|
445
469
|
}
|
|
446
470
|
|
|
447
|
-
function
|
|
471
|
+
function buildAttention(
|
|
448
472
|
context: Context,
|
|
449
473
|
families: readonly Family[],
|
|
450
474
|
homeGroupOf: (family: Family) => GroupDescriptor,
|
|
451
|
-
):
|
|
475
|
+
): AttentionView | null {
|
|
452
476
|
if (families.length === 0) return null;
|
|
453
477
|
// Most urgent first, then the chosen field in its natural direction: the
|
|
454
478
|
// sort direction orders the groups only.
|
|
@@ -468,21 +492,20 @@ function buildNeedsYou(
|
|
|
468
492
|
const homeGroupIds: Record<string, string> = {};
|
|
469
493
|
const rows = sorted.flatMap((family) => {
|
|
470
494
|
const home = homeGroupOf(family);
|
|
471
|
-
const familyRows =
|
|
495
|
+
const familyRows = attentionFamilyRows(context, family, home.label);
|
|
472
496
|
for (const row of familyRows) if (row.type === "thread") homeGroupIds[row.info.thread.id] = home.id;
|
|
473
497
|
return familyRows;
|
|
474
498
|
});
|
|
475
|
-
const rails = railsFor(rows.map((row) => row.depth));
|
|
476
499
|
return {
|
|
477
500
|
familyCount: sorted.length,
|
|
478
|
-
rows
|
|
501
|
+
rows,
|
|
479
502
|
homeGroupIds,
|
|
480
503
|
};
|
|
481
504
|
}
|
|
482
505
|
|
|
483
506
|
/**
|
|
484
507
|
* One group. `families` is every family bucketed in it, for its counters and
|
|
485
|
-
* `rootIds`; `drawn` leaves out those in Needs
|
|
508
|
+
* `rootIds`; `drawn` leaves out those in Needs attention.
|
|
486
509
|
*/
|
|
487
510
|
function buildGroup(
|
|
488
511
|
context: Context,
|
|
@@ -544,15 +567,12 @@ function buildGroup(
|
|
|
544
567
|
count: foldedFamilies.length,
|
|
545
568
|
expanded: opened,
|
|
546
569
|
depth: 0,
|
|
547
|
-
rails: [],
|
|
548
570
|
};
|
|
549
571
|
}
|
|
550
572
|
}
|
|
551
573
|
|
|
552
574
|
const units = visible.map((family) => familyUnit(context, family));
|
|
553
|
-
const
|
|
554
|
-
const rails = railsFor(clustered.map((row) => row.depth));
|
|
555
|
-
const rows = clustered.map((row, index) => ({ ...row, rails: rails[index]! }));
|
|
575
|
+
const rows = clusterEnvironments(context, units, 0);
|
|
556
576
|
if (older !== null) rows.push(older);
|
|
557
577
|
return {
|
|
558
578
|
descriptor,
|
|
@@ -594,6 +614,7 @@ export function buildListView(inputs: ViewInputs): ListView {
|
|
|
594
614
|
activePath,
|
|
595
615
|
projectNames: new Map(inputs.projects.map((project) => [project.id, project.name])),
|
|
596
616
|
sectionNames: new Map(inputs.sections.map((section) => [section.id, section.name])),
|
|
617
|
+
drawnInAttention: false,
|
|
597
618
|
};
|
|
598
619
|
|
|
599
620
|
const byGroup = new Map<string, Family[]>();
|
|
@@ -609,7 +630,7 @@ export function buildListView(inputs: ViewInputs): ListView {
|
|
|
609
630
|
list.push(family);
|
|
610
631
|
byGroup.set(id, list);
|
|
611
632
|
homeOf.set(family, id);
|
|
612
|
-
if (
|
|
633
|
+
if (inAttention(family, inputs.heldRootId, openRootId)) section.push(family);
|
|
613
634
|
}
|
|
614
635
|
const inSection = new Set(section);
|
|
615
636
|
|
|
@@ -637,7 +658,7 @@ export function buildListView(inputs: ViewInputs): ListView {
|
|
|
637
658
|
const families = byGroup.get(id) ?? [];
|
|
638
659
|
const drawn = families.filter((family) => !inSection.has(family));
|
|
639
660
|
// Pinned and the loose bucket appear only when they hold threads outside
|
|
640
|
-
// Needs
|
|
661
|
+
// Needs attention, except the loose bucket in custom-section mode, as in bb.
|
|
641
662
|
if (drawn.length === 0) {
|
|
642
663
|
if (id === PINNED_GROUP_ID) continue;
|
|
643
664
|
if (id === THREADS_GROUP_ID && prefs.organizationMode !== "chronological") continue;
|
|
@@ -655,8 +676,8 @@ export function buildListView(inputs: ViewInputs): ListView {
|
|
|
655
676
|
const hosts = new Set<string>();
|
|
656
677
|
for (const thread of inputs.threads) if (thread.host !== null) hosts.add(thread.host.id);
|
|
657
678
|
// Every root's group id has a descriptor: entity groups include unknown sections and every host.
|
|
658
|
-
const
|
|
659
|
-
return {
|
|
679
|
+
const attention = buildAttention(context, section, (family) => descriptors.get(homeOf.get(family)!)!);
|
|
680
|
+
return { attention, groups, more, moreCounters, order, multiHost: hosts.size > 1 };
|
|
660
681
|
}
|
|
661
682
|
|
|
662
683
|
/** Every thread row in visual order, for keyboard and windowing. */
|
|
@@ -165,7 +165,7 @@ export function rowIds(view: ListView, groupId: string): string[] {
|
|
|
165
165
|
return idsOf(group.rows);
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
-
/** Thread ids in Needs
|
|
169
|
-
export function
|
|
170
|
-
return idsOf(view.
|
|
168
|
+
/** Thread ids in Needs attention, in order; "+N more" lines as `+N`. Empty when the section is absent. */
|
|
169
|
+
export function attentionIds(view: ListView): string[] {
|
|
170
|
+
return idsOf(view.attention?.rows ?? []);
|
|
171
171
|
}
|
package/package.json
CHANGED
package/server/cli.ts
CHANGED
|
@@ -38,7 +38,7 @@ export function createCli(store: PreferenceStore): PluginCliRegistration {
|
|
|
38
38
|
name: "thread-glance",
|
|
39
39
|
summary: "Inspect and change the Thread Glance sidebar's layout preferences",
|
|
40
40
|
description:
|
|
41
|
-
"Organization, sort, group order, hidden and collapsed groups, child-thread folding and which child threads Needs
|
|
41
|
+
"Organization, sort, group order, hidden and collapsed groups, child-thread folding and which child threads Needs attention counts, for the Thread Glance sidebar. Values are JSON; a bare word is read as a string.",
|
|
42
42
|
commands: {
|
|
43
43
|
"prefs list": cliCommand({
|
|
44
44
|
summary: "List every preference and its current value",
|
package/shared/preferences.ts
CHANGED
|
@@ -131,7 +131,7 @@ export const PREFERENCES = {
|
|
|
131
131
|
childAttention: define(
|
|
132
132
|
childAttentionSchema,
|
|
133
133
|
"blocked" as ChildAttention,
|
|
134
|
-
"Needs
|
|
134
|
+
"Needs attention counts every child: blocked counts a child thread that waits on you, is offline or has an orphaned failure; everything also counts every failed or finished-unread child. The same children stay out of a family's older fold, as running ones do.",
|
|
135
135
|
),
|
|
136
136
|
harnessIcon: define(
|
|
137
137
|
harnessIconSchema,
|
|
@@ -212,7 +212,7 @@ export function mapBbPreferences(raw: unknown): Partial<Preferences> {
|
|
|
212
212
|
return mapped as Partial<Preferences>;
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
-
/** Per-client preferences, kept in localStorage only. A saved `filter
|
|
215
|
+
/** Per-client preferences, kept in localStorage only. A saved `filter`, from the old All / Needs attention control, is dropped. */
|
|
216
216
|
export const clientPreferencesSchema = z.object({
|
|
217
217
|
density: z.enum(["compact", "comfortable"]).catch("compact"),
|
|
218
218
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: thread-glance
|
|
3
|
-
description: "Reads and changes the Thread Glance sidebar's layout preferences with `bb thread-glance prefs`: grouping, sort, group order, hidden and collapsed groups, child-thread folding, which child threads Needs
|
|
3
|
+
description: "Reads and changes the Thread Glance sidebar's layout preferences with `bb thread-glance prefs`: grouping, sort, group order, hidden and collapsed groups, child-thread folding, which child threads Needs attention counts, harness icons. Use when asked to change how the Thread Glance sidebar lists, groups, sorts, hides, collapses or folds threads or draws their harness icon."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Thread Glance preferences
|
|
@@ -57,12 +57,12 @@ and `bb environment list`; a thread id is a `thr_…` id.
|
|
|
57
57
|
| `expandedOlder` | Group ids and parent thread ids whose "N older" or "N more child threads" row is open | `[]` |
|
|
58
58
|
| `expandedChildren` | Parent thread ids whose chip is open | `[]` |
|
|
59
59
|
| `showPullRequests` | `true` shows a pull request badge on rows | `true` |
|
|
60
|
-
| `childAttention` | Needs
|
|
60
|
+
| `childAttention` | Needs attention counts every child. Which child threads need attention (the Needs attention section, counters, auto-reveal) and stay out of a family's "N more child threads" fold alongside running ones: `blocked` counts a child that waits on you, is offline, or has an orphaned failure (its parent thread idle since); `everything` also counts every failed or unread child | `blocked` |
|
|
61
61
|
| `harnessIcon` | How rows draw the harness logo: `muted` (monochrome), `colour` (the provider's tint) or `hidden` | `muted` |
|
|
62
62
|
|
|
63
63
|
Row density is kept per browser, and the CLI cannot read or change it. No
|
|
64
|
-
preference filters the list: families that need
|
|
65
|
-
Needs
|
|
64
|
+
preference filters the list: families that need attention always move into the
|
|
65
|
+
Needs attention section.
|
|
66
66
|
|
|
67
67
|
Sections themselves and the section a thread is in are bb core state: use
|
|
68
68
|
`bb thread section` and `bb thread update`.
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
// The guide rails of a nested row: one hairline per nesting level,
|
|
2
|
-
// drawn inside the row so they join up from row to row.
|
|
3
|
-
import { railLeft, type Rail } from "../model/layout";
|
|
4
|
-
|
|
5
|
-
// `start` begins just under the 16px status slot, which is centred in the row.
|
|
6
|
-
const SPAN: Record<Rail, { top: string; bottom: string }> = {
|
|
7
|
-
start: { top: "calc(50% + 10px)", bottom: "0px" },
|
|
8
|
-
full: { top: "0px", bottom: "0px" },
|
|
9
|
-
end: { top: "0px", bottom: "50%" },
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export function RowRails({ rails }: { rails: readonly (Rail | null)[] }) {
|
|
13
|
-
if (rails.every((rail) => rail === null)) return null;
|
|
14
|
-
return (
|
|
15
|
-
<span aria-hidden data-rails="" className="pointer-events-none absolute inset-0">
|
|
16
|
-
{rails.map((rail, level) =>
|
|
17
|
-
rail === null ? null : (
|
|
18
|
-
<span
|
|
19
|
-
key={level}
|
|
20
|
-
className="absolute w-px bg-border"
|
|
21
|
-
style={{ left: railLeft(level), ...SPAN[rail] }}
|
|
22
|
-
/>
|
|
23
|
-
),
|
|
24
|
-
)}
|
|
25
|
-
</span>
|
|
26
|
-
);
|
|
27
|
-
}
|