@stevecastaneda/paseo-progress 0.1.5 → 0.1.6
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/client/panel.tsx +55 -5
- package/dist/shared/dashboard.js +10 -0
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/shared/dashboard.ts +11 -0
package/client/panel.tsx
CHANGED
|
@@ -10,7 +10,7 @@ import { raised } from "./surfaces";
|
|
|
10
10
|
import { When } from "./when";
|
|
11
11
|
import React, { useEffect, useRef, useState } from "react";
|
|
12
12
|
import { ActivityIndicator, Animated, Platform, Pressable, ScrollView, Text, View } from "react-native";
|
|
13
|
-
import { shortTitle, type Activity, type Dashboard, type FileIssue, type ProgressSegment, type StuckItem, type Ticket } from "../shared/dashboard";
|
|
13
|
+
import { finishedAtTop, shortTitle, type Activity, type Dashboard, type FileIssue, type ProgressSegment, type StuckItem, type Ticket } from "../shared/dashboard";
|
|
14
14
|
import { PROGRESS_FILE, type TicketStatus } from "../shared/events";
|
|
15
15
|
import { formatEstimate, formatMinutes, formatWorkDone, minutesSince } from "../shared/format";
|
|
16
16
|
import { markPanelOpened } from "../shared/rpc";
|
|
@@ -214,10 +214,7 @@ function DashboardView({ colors, dashboard, agentRunning, compact, workspaceId,
|
|
|
214
214
|
No {(dashboard.run?.itemLabel ?? "ticket").toLowerCase()}s yet.
|
|
215
215
|
</Text>
|
|
216
216
|
) : (
|
|
217
|
-
dashboard
|
|
218
|
-
<TicketRow key={ticket.id} colors={colors} ticket={ticket} live={live} now={now} onOpen={() => setOpenTicket(ticket.id)}
|
|
219
|
-
deliverables={dashboard.deliverables.filter((deliverable) => deliverable.ticketId === ticket.id).length} />
|
|
220
|
-
))
|
|
217
|
+
<TicketList colors={colors} dashboard={dashboard} live={live} now={now} workspaceId={workspaceId} onOpen={setOpenTicket} />
|
|
221
218
|
)}
|
|
222
219
|
<TicketDialogs colors={colors} dashboard={dashboard} openId={openTicket} setOpenId={setOpenTicket} now={now} live={live} context={attachmentContext} />
|
|
223
220
|
</Card>
|
|
@@ -415,6 +412,59 @@ function StuckSection({ colors, items, now }: { colors: Colors; items: StuckItem
|
|
|
415
412
|
);
|
|
416
413
|
}
|
|
417
414
|
|
|
415
|
+
// Whether the finished tickets at the top are shown, per workspace, kept for
|
|
416
|
+
// the app session like the history tab.
|
|
417
|
+
const finishedShown = new Map<string, boolean>();
|
|
418
|
+
|
|
419
|
+
// Finished tickets at the top fold into one row, so the work still ahead is
|
|
420
|
+
// near the top without scrolling (see finishedAtTop).
|
|
421
|
+
function TicketList({ colors, dashboard, live, now, workspaceId, onOpen }: { colors: Colors; dashboard: Dashboard; live: boolean; now: number; workspaceId: string; onOpen(id: string): void }) {
|
|
422
|
+
const [shown, setShown] = useState(() => finishedShown.get(workspaceId) ?? false);
|
|
423
|
+
const toggle = () => {
|
|
424
|
+
finishedShown.set(workspaceId, !shown);
|
|
425
|
+
setShown(!shown);
|
|
426
|
+
};
|
|
427
|
+
const folded = finishedAtTop(dashboard.tickets);
|
|
428
|
+
const row = (ticket: Ticket) => (
|
|
429
|
+
<TicketRow key={ticket.id} colors={colors} ticket={ticket} live={live} now={now} onOpen={() => onOpen(ticket.id)}
|
|
430
|
+
deliverables={dashboard.deliverables.filter((deliverable) => deliverable.ticketId === ticket.id).length} />
|
|
431
|
+
);
|
|
432
|
+
if (!folded) return <>{dashboard.tickets.map(row)}</>;
|
|
433
|
+
const finished = dashboard.tickets.slice(0, folded);
|
|
434
|
+
return (
|
|
435
|
+
<>
|
|
436
|
+
<FinishedRow colors={colors} tickets={finished} shown={shown} onPress={toggle} />
|
|
437
|
+
{shown ? finished.map(row) : null}
|
|
438
|
+
{dashboard.tickets.slice(folded).map(row)}
|
|
439
|
+
</>
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// Sits on the ticket rows' columns: status icon, label, estimate, then the
|
|
444
|
+
// chevron, which points the way the list will move, like "Show 10 older".
|
|
445
|
+
function FinishedRow({ colors, tickets, shown, onPress }: { colors: Colors; tickets: Ticket[]; shown: boolean; onPress(): void }) {
|
|
446
|
+
const done = tickets.filter((ticket) => ticket.status === "done").length;
|
|
447
|
+
const skipped = tickets.length - done;
|
|
448
|
+
const label = [done ? `${done} done` : "", skipped ? `${skipped} skipped` : ""].filter(Boolean).join(", ");
|
|
449
|
+
const estimateMin = tickets.reduce((sum, ticket) => sum + (ticket.status === "done" ? ticket.estimateMin : 0), 0);
|
|
450
|
+
const chevron = shown ? "ChevronUp" : "ChevronDown";
|
|
451
|
+
return (
|
|
452
|
+
<PressableRow colors={colors} accessibilityRole="button" accessibilityState={{ expanded: shown }}
|
|
453
|
+
accessibilityLabel={`${shown ? "Hide" : "Show"} ${label}`} onPress={onPress}
|
|
454
|
+
style={{ flexDirection: "row", alignItems: "flex-start", gap: 10, paddingHorizontal: 10, paddingVertical: 8, borderTopWidth: 1, borderTopColor: colors.border }}>
|
|
455
|
+
<View style={{ width: 16, paddingTop: 2, alignItems: "center" }}>
|
|
456
|
+
<Icon name={done ? "Check" : "CircleSlash"} size={14} color={done ? colors.statusSuccess : colors.foregroundMuted} />
|
|
457
|
+
</View>
|
|
458
|
+
<Text style={{ flex: 1, minWidth: 0, color: colors.foregroundMuted, fontSize: 12, lineHeight: 18, fontWeight: "600", fontVariant: ["tabular-nums"] }}>{label}</Text>
|
|
459
|
+
<View style={{ width: 12 }} />
|
|
460
|
+
<Text style={{ minWidth: 40, flexShrink: 0, textAlign: "right", color: colors.foregroundMuted, fontSize: 12, lineHeight: 18, fontVariant: ["tabular-nums"] }}>{estimateMin ? formatEstimate(estimateMin) : ""}</Text>
|
|
461
|
+
<View style={{ paddingTop: 2 }}>
|
|
462
|
+
<IconSwap swapKey={chevron} size={14}><Icon name={chevron} size={14} color={colors.foregroundMuted} /></IconSwap>
|
|
463
|
+
</View>
|
|
464
|
+
</PressableRow>
|
|
465
|
+
);
|
|
466
|
+
}
|
|
467
|
+
|
|
418
468
|
// Press for the ticket's story. Hover and press only tint the row: it's a list, pressed often.
|
|
419
469
|
function TicketRow({ colors, ticket, live, now, onOpen, deliverables }: { colors: Colors; ticket: Ticket; live: boolean; now: number; onOpen(): void; deliverables: number }) {
|
|
420
470
|
const muted = ticket.status === "skipped";
|
package/dist/shared/dashboard.js
CHANGED
|
@@ -328,6 +328,16 @@ export function shortTitle(title) {
|
|
|
328
328
|
const index = title.indexOf(":");
|
|
329
329
|
return index > 0 && index <= 24 ? title.slice(0, index) : title;
|
|
330
330
|
}
|
|
331
|
+
// How many tickets at the top of the list are finished (done or skipped), so
|
|
332
|
+
// the panel can fold them into one row and the work still ahead sits near the
|
|
333
|
+
// top. Only the leading run folds: a ticket finished out of order stays in
|
|
334
|
+
// place, so the plan's order never looks shuffled. A single one isn't worth a
|
|
335
|
+
// fold row. Once every ticket is finished nothing is ahead, and the whole list
|
|
336
|
+
// is the record of the run, so nothing folds.
|
|
337
|
+
export function finishedAtTop(tickets) {
|
|
338
|
+
const index = tickets.findIndex((ticket) => ticket.status !== "done" && ticket.status !== "skipped");
|
|
339
|
+
return index >= 2 ? index : 0;
|
|
340
|
+
}
|
|
331
341
|
function plural(count, noun) {
|
|
332
342
|
return `${count} ${count === 1 ? noun : `${noun}s`}`;
|
|
333
343
|
}
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stevecastaneda/paseo-progress",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@stevecastaneda/paseo-progress",
|
|
9
|
-
"version": "0.1.
|
|
9
|
+
"version": "0.1.6",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@getpaseo/client": "^0.9.2",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stevecastaneda/paseo-progress",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "A live progress dashboard per worktree for Paseo, drawn from a file agents write with the paseo-progress command.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Steve Castaneda",
|
package/shared/dashboard.ts
CHANGED
|
@@ -449,6 +449,17 @@ export function shortTitle(title: string): string {
|
|
|
449
449
|
return index > 0 && index <= 24 ? title.slice(0, index) : title;
|
|
450
450
|
}
|
|
451
451
|
|
|
452
|
+
// How many tickets at the top of the list are finished (done or skipped), so
|
|
453
|
+
// the panel can fold them into one row and the work still ahead sits near the
|
|
454
|
+
// top. Only the leading run folds: a ticket finished out of order stays in
|
|
455
|
+
// place, so the plan's order never looks shuffled. A single one isn't worth a
|
|
456
|
+
// fold row. Once every ticket is finished nothing is ahead, and the whole list
|
|
457
|
+
// is the record of the run, so nothing folds.
|
|
458
|
+
export function finishedAtTop(tickets: Ticket[]): number {
|
|
459
|
+
const index = tickets.findIndex((ticket) => ticket.status !== "done" && ticket.status !== "skipped");
|
|
460
|
+
return index >= 2 ? index : 0;
|
|
461
|
+
}
|
|
462
|
+
|
|
452
463
|
function plural(count: number, noun: string): string {
|
|
453
464
|
return `${count} ${count === 1 ? noun : `${noun}s`}`;
|
|
454
465
|
}
|