@stevecastaneda/paseo-progress 1.4.0 → 1.5.1
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 +18 -1
- package/client/attachments.tsx +9 -6
- package/client/deliverables.tsx +6 -4
- package/client/dialog-stack.tsx +3 -0
- package/client/history.tsx +23 -18
- package/client/i18n.ts +86 -0
- package/client/language.tsx +23 -0
- package/client/launcher.tsx +22 -17
- package/client/locales/en.ts +207 -0
- package/client/locales/zh-CN.ts +207 -0
- package/client/messages.ts +189 -0
- package/client/panel.tsx +48 -38
- package/client/pill-popover.tsx +8 -5
- package/client/pill-state.ts +4 -3
- package/client/pills.tsx +25 -11
- package/client/preview.tsx +18 -13
- package/client/questions.tsx +26 -18
- package/client/settings.tsx +46 -28
- package/client/show-more.tsx +4 -2
- package/client/spinner.tsx +7 -3
- package/client/status-badge.tsx +3 -9
- package/client/ticket-dialog.tsx +35 -29
- package/client/video.tsx +3 -1
- package/client/whats-new.tsx +11 -6
- package/client/when.tsx +5 -3
- package/client/write-up.tsx +5 -3
- package/images/5-settings.png +0 -0
- package/index.client.tsx +3 -2
- package/index.server.ts +2 -1
- package/package.json +1 -1
- package/shared/settings.ts +10 -0
- package/shared/ticket-story.ts +12 -10
package/client/whats-new.tsx
CHANGED
|
@@ -9,12 +9,14 @@ import { spans } from "../shared/write-up";
|
|
|
9
9
|
import { PressScale, Presence } from "./motion";
|
|
10
10
|
import { isLight, raised, tint } from "./surfaces";
|
|
11
11
|
import { BlockView, Spans } from "./write-up";
|
|
12
|
+
import { ProgressLanguageProvider, useProgressLanguage } from "./language";
|
|
12
13
|
|
|
13
14
|
type Colors = PluginWorkspacePanelProps["theme"]["colors"];
|
|
14
15
|
|
|
15
16
|
// Once after an update: this version's headline, with its full notes a press
|
|
16
17
|
// away. Closing either hides both for this version.
|
|
17
18
|
export function WhatsNewBanner({ colors, host }: { colors: Colors; host: PluginWorkspacePanelProps["host"] }) {
|
|
19
|
+
const { messages: m } = useProgressLanguage();
|
|
18
20
|
const fetchWhatsNew = useRpc(getWhatsNew);
|
|
19
21
|
const dismiss = useRpc(dismissWhatsNew);
|
|
20
22
|
const queryClient = useQueryClient();
|
|
@@ -40,31 +42,33 @@ export function WhatsNewBanner({ colors, host }: { colors: Colors; host: PluginW
|
|
|
40
42
|
<View style={{ flexDirection: "row", alignItems: "center", flexWrap: "wrap", gap: 10, margin: 12, marginBottom: 0, padding: 8, borderRadius: 12, ...raised(colors), backgroundColor: colors.surface1 }}>
|
|
41
43
|
<View style={{ alignSelf: "flex-start", height: 17, justifyContent: "center" }}><Icon name="Gift" size={14} color={colors.foregroundMuted} /></View>
|
|
42
44
|
<Text selectable style={{ flex: 1, minWidth: 200, color: colors.foregroundMuted, fontSize: 12, lineHeight: 17 }}>
|
|
43
|
-
<Text style={{ color: colors.foreground, fontWeight: "500" }}>
|
|
45
|
+
<Text style={{ color: colors.foreground, fontWeight: "500" }}>{m.newVersion(whatsNew.version)}</Text> {whatsNew.headline}
|
|
44
46
|
</Text>
|
|
45
47
|
{/* 8px keeps the close button's 6px hit slop off the button beside it. */}
|
|
46
48
|
<View style={{ flexDirection: "row", alignItems: "center", gap: 8 }}>
|
|
47
49
|
<PressScale accessibilityRole="button" onPress={() => { setShown(whatsNew); setOpen(true); }}
|
|
48
50
|
style={({ pressed, hovered }) => ({ paddingHorizontal: 10, paddingVertical: 5, borderRadius: 4, backgroundColor: colors.accent, opacity: pressed ? 0.7 : hovered ? 0.85 : 1 })}>
|
|
49
|
-
<Text style={{ color: colors.accentForeground, fontSize: 12, lineHeight: 16, fontWeight: "600" }}>
|
|
51
|
+
<Text style={{ color: colors.accentForeground, fontSize: 12, lineHeight: 16, fontWeight: "600" }}>{m.seeWhatsNew}</Text>
|
|
50
52
|
</PressScale>
|
|
51
|
-
<PressScale accessibilityRole="button" accessibilityLabel=
|
|
53
|
+
<PressScale accessibilityRole="button" accessibilityLabel={m.dismissWhatsNew} hitSlop={6} onPress={() => close(whatsNew.version)}
|
|
52
54
|
style={({ pressed, hovered }) => ({ padding: 5, borderRadius: 4, backgroundColor: pressed ? colors.border : hovered ? colors.surface2 : "transparent" })}>
|
|
53
55
|
<Icon name="X" size={16} color={colors.foregroundMuted} />
|
|
54
56
|
</PressScale>
|
|
55
57
|
</View>
|
|
56
58
|
</View>
|
|
57
59
|
) : null}</Presence>
|
|
58
|
-
<Modal title={shown ?
|
|
60
|
+
<Modal title={shown ? m.whatsNewTitle(shown.version) : ""} icon={<Icon name="Gift" size={16} color={colors.foregroundMuted} />}
|
|
59
61
|
open={open} onOpenChange={(next) => { if (!next) closeDialog(); }}>
|
|
60
62
|
<Modal.Content>
|
|
63
|
+
<ProgressLanguageProvider>
|
|
61
64
|
{shown ? <Notes colors={colors} notes={shown.notes} /> : null}
|
|
62
65
|
<View style={{ flexDirection: "row", justifyContent: "flex-end", marginTop: 8 }}>
|
|
63
66
|
<PressScale accessibilityRole="button" onPress={closeDialog}
|
|
64
67
|
style={({ pressed, hovered }) => ({ paddingHorizontal: 12, paddingVertical: 6, borderRadius: 6, backgroundColor: colors.accent, opacity: pressed ? 0.7 : hovered ? 0.85 : 1 })}>
|
|
65
|
-
<Text style={{ color: colors.accentForeground, fontSize: 12, lineHeight: 16, fontWeight: "600" }}>
|
|
68
|
+
<Text style={{ color: colors.accentForeground, fontSize: 12, lineHeight: 16, fontWeight: "600" }}>{m.gotIt}</Text>
|
|
66
69
|
</PressScale>
|
|
67
70
|
</View>
|
|
71
|
+
</ProgressLanguageProvider>
|
|
68
72
|
</Modal.Content>
|
|
69
73
|
</Modal>
|
|
70
74
|
</>
|
|
@@ -111,10 +115,11 @@ const TONE_COLOR: Record<NoteTone, (colors: Colors) => string> = {
|
|
|
111
115
|
// Sits on the title's first line: 18px tall, 1px down in its 20px line. As wide
|
|
112
116
|
// as "Improved" at least, so the titles beside the tags start at one edge.
|
|
113
117
|
function KindTag({ colors, kind }: { colors: Colors; kind: NoteKind }) {
|
|
118
|
+
const { messages: m } = useProgressLanguage();
|
|
114
119
|
const color = TONE_COLOR[NOTE_KINDS[kind].tone](colors);
|
|
115
120
|
return (
|
|
116
121
|
<View style={{ marginTop: 1, minWidth: 64, borderRadius: 4, paddingHorizontal: 7, backgroundColor: tint(color, colors) }}>
|
|
117
|
-
<Text numberOfLines={1} style={{ textAlign: "center", color, fontSize: 11, lineHeight: 18, fontWeight: "500" }}>{
|
|
122
|
+
<Text numberOfLines={1} style={{ textAlign: "center", color, fontSize: 11, lineHeight: 18, fontWeight: "500" }}>{m.noteKinds[kind]}</Text>
|
|
118
123
|
</View>
|
|
119
124
|
);
|
|
120
125
|
}
|
package/client/when.tsx
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { PluginWorkspacePanelProps } from "@getpaseo/plugin/client";
|
|
2
2
|
import React from "react";
|
|
3
3
|
import { Text } from "react-native";
|
|
4
|
-
import {
|
|
4
|
+
import { formatClientAgo, formatClientClock } from "./i18n";
|
|
5
5
|
import { useNarrow } from "./narrow";
|
|
6
|
+
import { useProgressLanguage } from "./language";
|
|
6
7
|
|
|
7
8
|
type Colors = PluginWorkspacePanelProps["theme"]["colors"];
|
|
8
9
|
|
|
@@ -15,11 +16,12 @@ export function unbroken(text: string): string {
|
|
|
15
16
|
// "Sep 27, 8:14 PM (10h 2m ago)", inline inside other text. A narrow
|
|
16
17
|
// panel shows only "10h 2m ago"; the ticket dialog keeps the clock time.
|
|
17
18
|
export function When({ colors, iso, now }: { colors: Colors; iso: string; now: number }) {
|
|
18
|
-
const
|
|
19
|
+
const { locale, messages: m } = useProgressLanguage();
|
|
20
|
+
const ago = unbroken(formatClientAgo(locale, iso, now));
|
|
19
21
|
if (useNarrow()) return <Text>{ago}</Text>;
|
|
20
22
|
return (
|
|
21
23
|
<Text>
|
|
22
|
-
{
|
|
24
|
+
{formatClientClock(locale, iso)}<Text style={{ color: colors.foregroundMuted, opacity: 0.7 }}>{m.parenthetical(ago)}</Text>
|
|
23
25
|
</Text>
|
|
24
26
|
);
|
|
25
27
|
}
|
package/client/write-up.tsx
CHANGED
|
@@ -9,6 +9,7 @@ import { type Block, parseWriteUp, type Span, withoutTitle, writeUpExcerpt } fro
|
|
|
9
9
|
import { type Attachment, attachmentIcon } from "./attachments";
|
|
10
10
|
import { PressableRow } from "./row";
|
|
11
11
|
import { errorText } from "./errors";
|
|
12
|
+
import { useProgressLanguage } from "./language";
|
|
12
13
|
|
|
13
14
|
type Colors = PluginWorkspacePanelProps["theme"]["colors"];
|
|
14
15
|
const MONO = Platform.OS === "ios" ? "Menlo" : "monospace";
|
|
@@ -22,6 +23,7 @@ export function WriteUp({ colors, source, workspaceId, workspaceDirectory, onOpe
|
|
|
22
23
|
workspaceDirectory: string;
|
|
23
24
|
onOpen(attachment: Attachment, group: Attachment[]): void;
|
|
24
25
|
}) {
|
|
26
|
+
const { messages: m } = useProgressLanguage();
|
|
25
27
|
const fetchPreview = useRpc(previewDeliverable);
|
|
26
28
|
const readable = Boolean(source.path && previewKind(source.path) === "text");
|
|
27
29
|
// The same query as the preview dialog, so opening the file after reading here is instant.
|
|
@@ -38,12 +40,12 @@ export function WriteUp({ colors, source, workspaceId, workspaceDirectory, onOpe
|
|
|
38
40
|
return (
|
|
39
41
|
<View style={{ gap: 12, padding: 14, borderRadius: 8, backgroundColor: colors.surface0 }}>
|
|
40
42
|
{shown.length ? <View style={{ gap: 8 }}>{shown.map((block, index) => <BlockView key={index} colors={colors} block={block} first={index === 0} />)}</View> : null}
|
|
41
|
-
{preview.error ? <Text style={{ color: colors.foregroundMuted, fontSize: 12, lineHeight: 18 }}>
|
|
43
|
+
{preview.error ? <Text style={{ color: colors.foregroundMuted, fontSize: 12, lineHeight: 18 }}>{m.readError(errorText(preview.error, m.unknownError))}</Text> : null}
|
|
42
44
|
{/* The file on the left, opening it whole; expanding in place on the right. */}
|
|
43
45
|
<View style={{ flexDirection: "row", alignItems: "center", gap: 12 }}>
|
|
44
|
-
<TextButton colors={colors} icon={attachmentIcon(source)} label={source.title} accessibilityLabel={
|
|
46
|
+
<TextButton colors={colors} icon={attachmentIcon(source)} label={source.title} accessibilityLabel={m.openItem(source.title)} onPress={() => onOpen(source, [source])} grow />
|
|
45
47
|
<View style={{ flex: 1 }} />
|
|
46
|
-
{excerpt.more ? <TextButton colors={colors} label={all ?
|
|
48
|
+
{excerpt.more ? <TextButton colors={colors} label={all ? m.showLess : m.readMore} onPress={() => setAll(!all)} expanded={all} /> : null}
|
|
47
49
|
</View>
|
|
48
50
|
</View>
|
|
49
51
|
);
|
package/images/5-settings.png
CHANGED
|
Binary file
|
package/index.client.tsx
CHANGED
|
@@ -2,6 +2,7 @@ import type { PluginClientContext } from "@getpaseo/plugin/client";
|
|
|
2
2
|
import { ProgressPanel } from "./client/panel";
|
|
3
3
|
import { contributePills, progressPanel } from "./client/pills";
|
|
4
4
|
import { QuestionSettings } from "./client/settings";
|
|
5
|
+
import { ProgressLanguageProvider } from "./client/language";
|
|
5
6
|
|
|
6
7
|
const SETTINGS_ID = "settings";
|
|
7
8
|
|
|
@@ -14,7 +15,7 @@ export default function contribute(client: PluginClientContext) {
|
|
|
14
15
|
icon: "ListChecks",
|
|
15
16
|
context: "workspace",
|
|
16
17
|
locations: [panel.location],
|
|
17
|
-
Component: (props) => <ProgressPanel {...props} openSettings={() => client.openSettings(SETTINGS_ID)}
|
|
18
|
+
Component: (props) => <ProgressLanguageProvider><ProgressPanel {...props} openSettings={() => client.openSettings(SETTINGS_ID)} /></ProgressLanguageProvider>,
|
|
18
19
|
});
|
|
19
20
|
const stopCommand = client.addCommandCenterItem({
|
|
20
21
|
id: "open-progress",
|
|
@@ -32,7 +33,7 @@ export default function contribute(client: PluginClientContext) {
|
|
|
32
33
|
id: SETTINGS_ID,
|
|
33
34
|
title: "Settings",
|
|
34
35
|
icon: "Settings",
|
|
35
|
-
Component: QuestionSettings
|
|
36
|
+
Component: (props) => <ProgressLanguageProvider><QuestionSettings {...props} /></ProgressLanguageProvider>,
|
|
36
37
|
});
|
|
37
38
|
|
|
38
39
|
return () => {
|
package/index.server.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { mirrorQuestionMode } from "./server/question-mode";
|
|
|
6
6
|
import { handleInstallSkill, handleSkillStatus } from "./server/skill";
|
|
7
7
|
import { handleDismissWhatsNew, handleGetWhatsNew } from "./server/whats-new";
|
|
8
8
|
import { canOpenOnHost, dismissWhatsNew, getAttention, getDashboard, getLauncherStatus, getSkillStatus, getWhatsNew, installLauncher, installSkill, listFolders, markPanelOpened, openDeliverable, previewDeliverable, readDeliverablePiece, setHistoryFolder } from "./shared/rpc";
|
|
9
|
-
import { questionSettings } from "./shared/settings";
|
|
9
|
+
import { displaySettings, questionSettings } from "./shared/settings";
|
|
10
10
|
|
|
11
11
|
export default function contribute(server: PluginServerContext) {
|
|
12
12
|
server.handle(getDashboard, handleGetDashboard);
|
|
@@ -27,6 +27,7 @@ export default function contribute(server: PluginServerContext) {
|
|
|
27
27
|
// Settles a new install before any panel opens. Opening one makes a
|
|
28
28
|
// worktree folder, which would look like an earlier version's.
|
|
29
29
|
void handleGetWhatsNew().catch(() => {});
|
|
30
|
+
server.registerSettings(displaySettings);
|
|
30
31
|
const mirror = mirrorQuestionMode(server.registerSettings(questionSettings));
|
|
31
32
|
return mirror.stop;
|
|
32
33
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stevecastaneda/paseo-progress",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.1",
|
|
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/settings.ts
CHANGED
|
@@ -8,3 +8,13 @@ export const questionSettings = defineSettings({
|
|
|
8
8
|
version: 1,
|
|
9
9
|
schema: z.object({ mode: z.enum(QUESTION_MODES).default(DEFAULT_QUESTION_MODE) }),
|
|
10
10
|
});
|
|
11
|
+
|
|
12
|
+
export const LANGUAGE_PREFERENCES = ["system", "en", "zh-CN"] as const;
|
|
13
|
+
export type LanguagePreference = (typeof LANGUAGE_PREFERENCES)[number];
|
|
14
|
+
|
|
15
|
+
export const displaySettings = defineSettings({
|
|
16
|
+
id: "display",
|
|
17
|
+
scope: "host",
|
|
18
|
+
version: 1,
|
|
19
|
+
schema: z.object({ language: z.enum(LANGUAGE_PREFERENCES).default("system") }),
|
|
20
|
+
});
|
package/shared/ticket-story.ts
CHANGED
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
import type { Activity, Deliverable, Question, Ticket } from "./dashboard.ts";
|
|
2
2
|
import type { TicketStatus } from "./events.ts";
|
|
3
3
|
|
|
4
|
+
export type TimelineLabel =
|
|
5
|
+
| { kind: "added" }
|
|
6
|
+
| { kind: "reset" }
|
|
7
|
+
| { kind: "stage"; stage: string }
|
|
8
|
+
| { kind: "status"; status: TicketStatus }
|
|
9
|
+
| { kind: "waiting"; id: string };
|
|
10
|
+
|
|
4
11
|
// One span in a ticket's life: from a status or stage change to the next one.
|
|
5
12
|
export interface TimelineStep {
|
|
6
13
|
at: string;
|
|
7
14
|
// When the next step began; null while this one is still going (or it's the end).
|
|
8
15
|
until: string | null;
|
|
9
|
-
label:
|
|
16
|
+
label: TimelineLabel;
|
|
10
17
|
status: TicketStatus;
|
|
11
18
|
notes: string[];
|
|
12
19
|
// Length of the step; null for a finished ticket's last step, which has no end.
|
|
@@ -27,15 +34,10 @@ export interface TicketStory {
|
|
|
27
34
|
|
|
28
35
|
const OPEN_ENDED: TicketStatus[] = ["working", "blocked", "not_started"];
|
|
29
36
|
|
|
30
|
-
function labelFor(status: TicketStatus, stage: string | undefined, first: boolean):
|
|
31
|
-
if (first && status === "not_started") return "
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
case "blocked": return "Blocked";
|
|
35
|
-
case "done": return "Done";
|
|
36
|
-
case "skipped": return "Skipped";
|
|
37
|
-
default: return "Back to not started";
|
|
38
|
-
}
|
|
37
|
+
function labelFor(status: TicketStatus, stage: string | undefined, first: boolean): TimelineLabel {
|
|
38
|
+
if (first && status === "not_started") return { kind: "added" };
|
|
39
|
+
if (status === "working" && stage) return { kind: "stage", stage };
|
|
40
|
+
return status === "not_started" ? { kind: "reset" } : { kind: "status", status };
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
export function ticketTimeline(ticket: Ticket, nowMs: number): TimelineStep[] {
|