@stevecastaneda/paseo-progress 1.8.0 → 1.10.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/README.md CHANGED
@@ -20,11 +20,11 @@ The dashboard shows:
20
20
 
21
21
  <img src="images/5-settings.png" alt="Progress settings in Paseo: Questions set to Work around, Language set to System, and the Agent Skills section with View skill" width="600">
22
22
 
23
- The panel lives in Explorer, beside the agent chat, so you can watch both. Open it from Command Center with **Open Progress**. The cog at the top right of the panel opens the plugin's Settings. After an update, the panel shows what's new once, including any versions you skipped. When questions are waiting or something is stuck, a pill above the message box says so, for example `3 questions`. With something stuck too, it shortens to `3Q · 1 stuck` to fit. Pressing it opens a popover with each open question, its choices and their Copy buttons, and what is stuck, so you can answer without leaving the chat. **Open Progress** at the bottom opens the panel in Explorer.
23
+ The panel lives in Explorer, beside the agent chat, so you can watch both. Open it from Command Center with **Open Progress**. The cog at the top right of the panel opens the plugin's Settings. After an update, the panel shows what's new once, including any versions you skipped. When questions are waiting or something is stuck, a pill above the message box says so, for example `3 questions`. With something stuck too, it shortens to `3Q · 1 stuck` to fit. Pressing it opens a popover with an open question, its choices and their Copy buttons, and what is stuck, so you can answer without leaving the chat. With more than one question, the arrows beside **Question 1 of 3** step through them. After you copy an answer, the popover opens on the next question. **Open Progress** at the bottom opens the panel in Explorer.
24
24
 
25
25
  <img src="images/4-pill.png" alt="The pill above the message box, showing two questions waiting" width="600">
26
26
 
27
- On phones, where Paseo has no Explorer pane, Progress opens as a tab of its own, and the pill stays above the message box while a run is open so there's always a way in. SVG files and videos preview on desktop; on phones they open on the daemon host.
27
+ On phones with Paseo 0.13 or later, Progress opens in Explorer too. Older phone apps have no Explorer, so there it opens as a tab of its own. On any phone, the pill stays above the message box while a run is open, so there's always a way in. SVG files and videos preview on desktop; on phones they open on the daemon host.
28
28
 
29
29
  The first time a run starts in a worktree whose Progress panel has never been opened, the pill shows a spinner and reads **Progress** instead; pressing it opens the panel. Once the panel has been opened there, the plugin writes an empty `progress-panel-opened` file next to the progress file and the nudge never comes back, even for later runs. It doesn't open the panel by itself because Paseo switches to a workspace to open its panel, which would pull you away from whatever you're looking at.
30
30
 
@@ -145,6 +145,15 @@ The progress file, `progress.jsonl`, holds one JSON event per line, each with a
145
145
 
146
146
  ## Changelog
147
147
 
148
+ ### 1.10.0
149
+
150
+ - [Improved] **Progress opens in Explorer on phones.** On Paseo 0.13 or later, the phone app opens Progress in Explorer, as the desktop app does, and lists it in Explorer's New tab menu. If a Progress tab from an earlier version is still open on your phone, you can close it.
151
+
152
+ ### 1.9.0
153
+
154
+ - [Improved] **The pill's popover shows one question at a time.** You used to scroll past every open question to reach the one you wanted. Now **Question 2 of 3** sits at the top with back and next arrows. Copy an answer and the popover opens on the next question the next time you press the pill.
155
+ - [Improved] **A long question shows there's more below.** When a question runs past the bottom of the popover, its last lines fade out until you scroll to the end. Before, it stopped at the edge and looked finished.
156
+
148
157
  ### 1.8.0
149
158
 
150
159
  - [New] **HTML reports open on a server with no screen.** An HTML report on a computer that can't open it, like a Linux server, used to fail and copy the file's path. In the desktop app it now opens across Paseo's main area, with its pictures, styles and scripts. Esc or Close takes you back. Where the computer can open it, it still opens in your browser.
@@ -67,6 +67,9 @@ export const messages: ProgressMessages = {
67
67
  openProgress: "Open Progress",
68
68
  needsYou: "Progress needs you",
69
69
  nothingNeedsYou: "Nothing needs you right now.",
70
+ questionPosition: (total: number) => ["Question ", ` of ${total}`],
71
+ previousQuestion: "Previous question",
72
+ nextQuestion: "Next question",
70
73
  overdueAside: " (no update past its estimate)",
71
74
  writeUp: "Write-up",
72
75
  timeline: "Timeline",
@@ -67,6 +67,9 @@ export const messages: ProgressMessages = {
67
67
  openProgress: "打开 Progress",
68
68
  needsYou: "Progress 需要你处理",
69
69
  nothingNeedsYou: "目前没有需要你处理的事项。",
70
+ questionPosition: (total: number) => ["第 ", ` 个问题,共 ${total} 个`],
71
+ previousQuestion: "上一个问题",
72
+ nextQuestion: "下一个问题",
70
73
  overdueAside: "(超过预计时长未更新)",
71
74
  writeUp: "总结",
72
75
  timeline: "时间线",
@@ -49,6 +49,9 @@ export interface ProgressMessages {
49
49
  openProgress: string;
50
50
  needsYou: string;
51
51
  nothingNeedsYou: string;
52
+ questionPosition: (total: number) => Around;
53
+ previousQuestion: string;
54
+ nextQuestion: string;
52
55
  overdueAside: string;
53
56
  writeUp: string;
54
57
  timeline: string;
@@ -4,21 +4,26 @@ import React, { useRef, useState } from "react";
4
4
  import { Text, View } from "react-native";
5
5
  import type { Dashboard } from "../shared/dashboard";
6
6
  import { useDashboard } from "./dashboard-query";
7
- import { progressPanel } from "./pills";
8
7
  import { PressScale } from "./motion";
9
8
  import { type Attachment, isPreviewable, useAttachmentOpener } from "./attachments";
10
9
  import { StackedDialog } from "./dialog-stack";
11
10
  import { galleryFiles } from "./preview";
12
11
  import { QuestionDetail, QuestionState, useCopy } from "./questions";
13
12
  import { errorText } from "./errors";
14
- import { raised } from "./surfaces";
13
+ import { raised, withAlpha } from "./surfaces";
15
14
  import { useProgressLanguage } from "./language";
16
15
 
17
16
  type Colors = PluginButtonContentProps["theme"]["colors"];
17
+ type Question = Dashboard["questions"]["open"][number];
18
18
 
19
19
  // On desktop, Paseo caps the popover at 440px and scrolls all of it, padding included. The
20
20
  // list scrolls on its own below that cap so Open Progress stays in view.
21
21
  const LIST_MAX_HEIGHT = 340;
22
+ const HEADER_GAP = 12;
23
+ const TOUCH_TARGET = 44;
24
+
25
+ // Paseo unmounts the popover when it closes, so reopening comes back to where the user was.
26
+ const shownQuestions = new Map<string, string>();
22
27
 
23
28
  // What the pill opens: the open questions with their Copy buttons, and anything
24
29
  // stuck, so the user can answer without leaving the chat.
@@ -27,8 +32,8 @@ export function AttentionPopover({ theme, host, layout, workspaceId, close, open
27
32
  const colors = theme.colors;
28
33
  // Paseo draws the desktop popover on surface1 and the phone sheet on surface0.
29
34
  const onSurface1 = !layout.compact;
30
- // Phones open the panel as its own tab, and are pressed with a finger.
31
- const touch = progressPanel().location === "workspace";
35
+ // The phone apps are pressed with a finger.
36
+ const touch = layout.platform !== "web";
32
37
  const directory = useWorkspace(workspaceId, (workspace) => workspace.directory);
33
38
  const query = useDashboard(host.id, workspaceId, directory);
34
39
  const dashboard = query.data?.configured ? query.data.dashboard : null;
@@ -43,8 +48,8 @@ export function AttentionPopover({ theme, host, layout, workspaceId, close, open
43
48
  return (
44
49
  <View style={{ width: 360, maxWidth: "100%", gap: 8 }}>
45
50
  {dashboard ? (
46
- <List compact={layout.compact}>
47
- <AttentionList colors={colors} dashboard={dashboard} onSurface1={onSurface1} onCopy={async (question, letter) => {
51
+ <AttentionList colors={colors} dashboard={dashboard} workspaceId={workspaceId} compact={layout.compact} touch={touch} onSurface1={onSurface1}
52
+ onCopy={async (question, letter) => {
48
53
  await copy(question, letter);
49
54
  close();
50
55
  }} onOpenAttachment={(attachment, from) => {
@@ -52,7 +57,6 @@ export function AttentionPopover({ theme, host, layout, workspaceId, close, open
52
57
  if (!isPreviewable(attachment)) close();
53
58
  void opener.open(attachment);
54
59
  }} />
55
- </List>
56
60
  ) : (
57
61
  <Text style={{ color: colors.foregroundMuted, fontSize: 12, lineHeight: 18 }}>
58
62
  {query.error ? m.readProgressError(errorText(query.error, m.unknownError)) : m.loading}
@@ -63,7 +67,7 @@ export function AttentionPopover({ theme, host, layout, workspaceId, close, open
63
67
  <View style={{ paddingTop: touch ? 12 : 8, borderTopWidth: 1, borderTopColor: colors.border }}>
64
68
  {/* A full 44pt touch target on phones; desktop keeps the compact button. */}
65
69
  <PressScale accessibilityRole="button" onPress={() => { close(); openPanel(); }}
66
- style={({ pressed }) => ({ flexDirection: "row", justifyContent: "center", alignItems: "center", gap: touch ? 8 : 6, paddingVertical: 6, minHeight: touch ? 44 : undefined, borderRadius: touch ? 8 : 4, ...raised(colors),
70
+ style={({ pressed }) => ({ flexDirection: "row", justifyContent: "center", alignItems: "center", gap: touch ? 8 : 6, paddingVertical: 6, minHeight: touch ? TOUCH_TARGET : undefined, borderRadius: touch ? 8 : 4, ...raised(colors),
67
71
  backgroundColor: onSurface1 ? (pressed ? colors.surface1 : colors.surface2) : pressed ? colors.surface2 : colors.surface1 })}>
68
72
  {/* Beside the chat on desktop; on a phone it opens as its own tab, shown with the tab's icon. */}
69
73
  <Icon name={touch ? "ListChecks" : "PanelRight"} size={touch ? 16 : 12} color={colors.foreground} />
@@ -77,54 +81,140 @@ export function AttentionPopover({ theme, host, layout, workspaceId, close, open
77
81
  // On a phone the popover is a sheet that already scrolls, and sizes itself to
78
82
  // whichever scroll view reports last. Paseo 0.10.1 turns a ScrollView inside it
79
83
  // into one of those, so a short list would shrink the sheet to its title.
80
- function List({ compact, children }: { compact: boolean; children: React.ReactNode }) {
84
+ function List({ compact, maxHeight, surface, children }: { compact: boolean; maxHeight: number; surface: string; children: React.ReactNode }) {
85
+ const [moreBelow, setMoreBelow] = useState(false);
86
+ const size = useRef({ viewport: 0, content: 0, offset: 0 });
81
87
  if (compact) return <View style={{ gap: 12 }}>{children}</View>;
82
- return <ScrollView style={{ maxHeight: LIST_MAX_HEIGHT }} contentContainerStyle={{ gap: 12 }}>{children}</ScrollView>;
88
+ const measure = (next: Partial<typeof size.current>) => {
89
+ const s = Object.assign(size.current, next);
90
+ const below = s.offset + s.viewport < s.content - 1;
91
+ if (below !== moreBelow) setMoreBelow(below);
92
+ };
93
+ return (
94
+ <View style={{ position: "relative" }}>
95
+ <ScrollView style={{ maxHeight }} contentContainerStyle={{ gap: 12 }} scrollEventThrottle={16}
96
+ onLayout={(event) => measure({ viewport: event.nativeEvent.layout.height })}
97
+ onContentSizeChange={(_, height) => measure({ content: height })}
98
+ onScroll={(event) => measure({ offset: event.nativeEvent.contentOffset.y })}>
99
+ {children}
100
+ </ScrollView>
101
+ {moreBelow ? <BottomFade color={surface} /> : null}
102
+ </View>
103
+ );
104
+ }
105
+
106
+ const FADE_SLICES = 8;
107
+
108
+ // React Native Web 0.21 has no experimental_backgroundImage, so no single gradient style draws on both desktop and iPad.
109
+ function BottomFade({ color }: { color: string }) {
110
+ return (
111
+ <View pointerEvents="none" style={{ position: "absolute", left: 0, right: 0, bottom: 0 }}>
112
+ {Array.from({ length: FADE_SLICES }, (_, i) => (
113
+ <View key={i} style={{ height: 3, backgroundColor: withAlpha(color, (0.9 * i) / (FADE_SLICES - 1)) ?? undefined }} />
114
+ ))}
115
+ </View>
116
+ );
83
117
  }
84
118
 
85
- export function AttentionList({ colors, dashboard, onSurface1, onCopy, onOpenAttachment }: {
119
+ export function AttentionList({ colors, dashboard, workspaceId, compact, touch, onSurface1, onCopy, onOpenAttachment }: {
86
120
  colors: Colors;
87
121
  dashboard: Dashboard;
122
+ workspaceId: string;
123
+ compact: boolean;
124
+ touch: boolean;
88
125
  onSurface1: boolean;
89
- onCopy(question: Dashboard["questions"]["open"][number], letter: string): void;
126
+ onCopy(question: Question, letter: string): void;
90
127
  onOpenAttachment(attachment: Attachment, group: Attachment[]): void;
91
128
  }) {
92
129
  const { messages: m } = useProgressLanguage();
130
+ const [shownId, setShownId] = useState(() => shownQuestions.get(workspaceId));
93
131
  const now = Date.now();
132
+ const open = dashboard.questions.open;
133
+ const found = open.findIndex((question) => question.id === shownId);
134
+ const index = found === -1 ? 0 : found;
135
+ const question: Question | undefined = open[index];
94
136
  // The last question can be answered while the popover is open.
95
- if (!dashboard.questions.open.length && !dashboard.stuck.length) {
137
+ if (!question && !dashboard.stuck.length) {
96
138
  return <Text style={{ color: colors.foregroundMuted, fontSize: 12, lineHeight: 18 }}>{m.nothingNeedsYou}</Text>;
97
139
  }
140
+ const show = (target: Question) => {
141
+ shownQuestions.set(workspaceId, target.id);
142
+ setShownId(target.id);
143
+ };
144
+ const advancePast = (copied: Question) => shownQuestions.set(workspaceId, (open[index + 1] ?? copied).id);
145
+ const step = { colors, touch, onSurface1, onPress: show };
146
+ const stepping = open.length > 1;
147
+ const position = m.questionPosition(open.length);
98
148
  return (
99
- <>
100
- {dashboard.questions.open.map((question) => (
101
- <View key={question.id} style={{ gap: 6 }}>
102
- <View style={{ flexDirection: "row", alignItems: "flex-start", gap: 8 }}>
103
- <Text style={{ color: colors.foreground, fontSize: 13, lineHeight: 19, fontWeight: "600" }}>{question.id}</Text>
104
- <View style={{ flex: 1, minWidth: 0, gap: 2 }}>
105
- <Text style={{ color: colors.foreground, fontSize: 13, lineHeight: 19 }}>{question.question}</Text>
106
- <QuestionState colors={colors} question={question} />
107
- </View>
149
+ <View style={{ gap: HEADER_GAP }}>
150
+ {stepping ? (
151
+ <View style={{ flexDirection: "row", alignItems: "center", justifyContent: "space-between", gap: 8 }}>
152
+ <Text style={{ color: colors.foregroundMuted, fontSize: 12, lineHeight: 17, fontVariant: ["tabular-nums"] }}>
153
+ {position[0]}<Text style={{ color: colors.foreground, fontWeight: "600" }}>{index + 1}</Text>{position[1]}
154
+ </Text>
155
+ <View style={{ flexDirection: "row", gap: touch ? stepSlop * 2 : 4 }}>
156
+ <StepButton {...step} icon="ChevronLeft" label={m.previousQuestion} target={open[index - 1]} />
157
+ <StepButton {...step} icon="ChevronRight" label={m.nextQuestion} target={open[index + 1]} />
108
158
  </View>
109
- <QuestionDetail colors={colors} question={question} now={now} onSurface1={onSurface1} onCopy={(letter) => onCopy(question, letter)} onOpenAttachment={onOpenAttachment} />
110
159
  </View>
111
- ))}
112
- {dashboard.stuck.length ? (
113
- <View style={{ gap: 4 }}>
114
- <View style={{ flexDirection: "row", alignItems: "center", gap: 6 }}>
115
- <Icon name="OctagonAlert" size={12} color={colors.statusDanger} />
116
- <Text style={{ color: colors.statusDanger, fontSize: 12, lineHeight: 17, fontWeight: "600" }}>{m.stuck}</Text>
160
+ ) : null}
161
+ <List key={question?.id} compact={compact} surface={onSurface1 ? colors.surface1 : colors.surface0} maxHeight={stepping ? LIST_MAX_HEIGHT - stepSize(touch) - HEADER_GAP : LIST_MAX_HEIGHT}>
162
+ {question ? (
163
+ <View style={{ gap: 6 }}>
164
+ <View style={{ flexDirection: "row", alignItems: "flex-start", gap: 8 }}>
165
+ <Text style={{ color: colors.foreground, fontSize: 13, lineHeight: 19, fontWeight: "600" }}>{question.id}</Text>
166
+ <View style={{ flex: 1, minWidth: 0, gap: 2 }}>
167
+ <Text style={{ color: colors.foreground, fontSize: 13, lineHeight: 19 }}>{question.question}</Text>
168
+ <QuestionState colors={colors} question={question} />
169
+ </View>
170
+ </View>
171
+ <QuestionDetail colors={colors} question={question} now={now} onSurface1={onSurface1} onOpenAttachment={onOpenAttachment} onCopy={(letter) => {
172
+ advancePast(question);
173
+ onCopy(question, letter);
174
+ }} />
117
175
  </View>
118
- {dashboard.stuck.map((item) => (
119
- <Text key={item.key} style={{ color: colors.foreground, fontSize: 12, lineHeight: 17 }}>
120
- {item.title}
121
- <Text style={{ color: colors.foregroundMuted }}>
122
- {item.kind === "overdue" ? m.overdueAside : item.kind === "blocked" ? m.blockedAside(item.note) : ""}
176
+ ) : null}
177
+ {dashboard.stuck.length ? (
178
+ <View style={{ gap: 4 }}>
179
+ <View style={{ flexDirection: "row", alignItems: "center", gap: 6 }}>
180
+ <Icon name="OctagonAlert" size={12} color={colors.statusDanger} />
181
+ <Text style={{ color: colors.statusDanger, fontSize: 12, lineHeight: 17, fontWeight: "600" }}>{m.stuck}</Text>
182
+ </View>
183
+ {dashboard.stuck.map((item) => (
184
+ <Text key={item.key} style={{ color: colors.foreground, fontSize: 12, lineHeight: 17 }}>
185
+ {item.title}
186
+ <Text style={{ color: colors.foregroundMuted }}>
187
+ {item.kind === "overdue" ? m.overdueAside : item.kind === "blocked" ? m.blockedAside(item.note) : ""}
188
+ </Text>
123
189
  </Text>
124
- </Text>
125
- ))}
126
- </View>
127
- ) : null}
128
- </>
190
+ ))}
191
+ </View>
192
+ ) : null}
193
+ </List>
194
+ </View>
195
+ );
196
+ }
197
+
198
+ const stepSize = (touch: boolean) => (touch ? 36 : 24);
199
+ const stepSlop = (TOUCH_TARGET - stepSize(true)) / 2;
200
+
201
+ function StepButton({ colors, touch, onSurface1, icon, label, target, onPress }: {
202
+ colors: Colors;
203
+ touch: boolean;
204
+ onSurface1: boolean;
205
+ icon: "ChevronLeft" | "ChevronRight";
206
+ label: string;
207
+ target: Question | undefined;
208
+ onPress(target: Question): void;
209
+ }) {
210
+ const button = onSurface1 ? { rest: colors.surface2, pressed: colors.surface1 } : { rest: colors.surface1, pressed: colors.surface2 };
211
+ const size = stepSize(touch);
212
+ return (
213
+ <PressScale accessibilityRole="button" accessibilityLabel={label} accessibilityState={{ disabled: !target }} disabled={!target}
214
+ onPress={target ? () => onPress(target) : undefined} hitSlop={touch ? stepSlop : undefined}
215
+ style={({ pressed }) => ({ width: size, height: size, alignItems: "center", justifyContent: "center", borderRadius: touch ? 8 : 4, opacity: target ? 1 : 0.35, ...raised(colors),
216
+ backgroundColor: pressed ? button.pressed : button.rest })}>
217
+ <Icon name={icon} size={touch ? 16 : 14} color={colors.foreground} />
218
+ </PressScale>
129
219
  );
130
220
  }
@@ -5,8 +5,7 @@ import type { AttentionResult } from "../shared/rpc";
5
5
  // icon and what pressing it does can't disagree.
6
6
  // - "attention": questions waiting or something stuck; opens a popover.
7
7
  // - "started": a run whose panel has never been opened; a one-time nudge.
8
- // - "open": on a phone, the way into the panel while a run is open, since
9
- // phones have no Explorer to open it from.
8
+ // - "open": on a phone, a one-tap way into the panel while a run is open.
10
9
  export type Pill =
11
10
  | { mode: "attention"; label: string; icon: "stuck" | "questions" }
12
11
  | { mode: "started"; label: "Progress"; icon: "started" }
package/client/pills.tsx CHANGED
@@ -18,13 +18,17 @@ export const PILL_POLL_MS = 5_000;
18
18
  // still trigger a check, at most once a poll interval.
19
19
  export const IDLE_POLL_EVERY = 6;
20
20
 
21
- // Paseo's desktop app shows the panel in Explorer, beside the chat. The phone
22
- // apps never draw Explorer's pane, so there the panel is a tab of its own.
23
- // It has its own id on phones: Paseo 0.10.1 reopens an existing tab where it
21
+ // Phones on Paseo before 0.13 have no Explorer, so there the panel is a tab of
22
+ // its own. addFilePanel arrived in 0.13, the release that gave phones Explorer.
23
+ // The tab has its own id: Paseo 0.10.1 reopens an existing tab where it
24
24
  // already is, and earlier versions of this plugin left a "progress" tab in the
25
25
  // phone's hidden Explorer pane, which a plugin can't move or close.
26
- export function progressPanel(): { id: string; location: "explorer" | "workspace" } {
27
- return Platform.OS === "web" ? { id: "progress", location: "explorer" } : { id: "progress-tab", location: "workspace" };
26
+ type ProgressPanelPlacement = { id: "progress"; location: "explorer" } | { id: "progress-tab"; location: "workspace" };
27
+
28
+ export function progressPanel(client: PluginClientContext): ProgressPanelPlacement {
29
+ return Platform.OS === "web" || "addFilePanel" in client
30
+ ? { id: "progress", location: "explorer" }
31
+ : { id: "progress-tab", location: "workspace" };
28
32
  }
29
33
 
30
34
  type Workspace = { id: string; workspaceDirectory?: string; projectRootPath: string };
@@ -44,7 +48,7 @@ export function contributePills(client: PluginClientContext) {
44
48
  // The last report per workspace; pillFor turns it into what the pill shows.
45
49
  const reports = new Map<string, AttentionResult>();
46
50
  let locale = resolveLocale("system");
47
- const where = { phone: progressPanel().location === "workspace" };
51
+ const where = { phone: Platform.OS !== "web" };
48
52
  const pillOf = (workspaceId: string) => pillFor(reports.get(workspaceId), where, messagesFor(locale));
49
53
  // Bumped on each report so a poll that was already in flight can't bring the pill back.
50
54
  let openedReports = 0;
@@ -83,9 +87,9 @@ export function contributePills(client: PluginClientContext) {
83
87
  );
84
88
  }
85
89
 
90
+ const panel = progressPanel(client);
86
91
  const openPanel = (workspaceId: string) => {
87
- const { id, location } = progressPanel();
88
- client.openPanel(id, { workspaceId, location });
92
+ client.openPanel(panel.id, { workspaceId, location: panel.location });
89
93
  };
90
94
 
91
95
  const buttonFor = (workspaceId: string, mode: Pill["mode"], label: string) => mode === "attention"
@@ -51,7 +51,10 @@ export function raised(colors: Colors): ViewStyle {
51
51
  // drifting second color. Paseo's own status badges use 12% in light themes and
52
52
  // 16% in dark ones, where the surface swallows more of it.
53
53
  export function tint(color: string, colors: Colors): string {
54
+ return withAlpha(color, isLight(colors) ? 0.12 : 0.16) ?? colors.surface2;
55
+ }
56
+
57
+ export function withAlpha(color: string, alpha: number): string | null {
54
58
  const rgb = channels(color);
55
- if (!rgb) return colors.surface2;
56
- return `rgba(${rgb.join(", ")}, ${isLight(colors) ? 0.12 : 0.16})`;
59
+ return rgb ? `rgba(${rgb.join(", ")}, ${alpha})` : null;
57
60
  }
package/index.client.tsx CHANGED
@@ -9,7 +9,7 @@ const SETTINGS_ID = "settings";
9
9
 
10
10
  export default function contribute(client: PluginClientContext) {
11
11
  const stopPills = contributePills(client);
12
- const panel = progressPanel();
12
+ const panel = progressPanel(client);
13
13
  const stopPanel = client.addWorkspacePanel({
14
14
  id: panel.id,
15
15
  title: "Progress",
@@ -24,7 +24,6 @@ export default function contribute(client: PluginClientContext) {
24
24
  icon: "ListChecks",
25
25
  keywords: ["dashboard", "tickets", "status", "questions"],
26
26
  context: "workspace",
27
- // Beside the agent chat where Paseo can show both; a tab of its own on phones.
28
27
  onSelect({ openPanel }) {
29
28
  openPanel(panel.id, { location: panel.location });
30
29
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stevecastaneda/paseo-progress",
3
- "version": "1.8.0",
3
+ "version": "1.10.0",
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",