@stevecastaneda/paseo-progress 1.14.1 → 1.14.3
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 +28 -17
- package/client/attachments.tsx +122 -28
- package/client/blocks.tsx +33 -8
- package/client/chat-row-view.tsx +19 -6
- package/client/chat-row.ts +13 -2
- package/client/dashboard-query.ts +9 -4
- package/client/deliverables.tsx +82 -17
- package/client/dialog-stack.tsx +104 -42
- package/client/directory.ts +1 -3
- package/client/errors.ts +6 -1
- package/client/history.tsx +233 -79
- package/client/i18n.ts +10 -5
- package/client/launcher.tsx +57 -15
- package/client/locales/en.ts +28 -22
- package/client/locales/zh-CN.ts +18 -17
- package/client/messages.ts +1 -0
- package/client/motion.tsx +38 -14
- package/client/panel-opened.ts +3 -1
- package/client/panel.tsx +442 -160
- package/client/pdf.tsx +18 -4
- package/client/pieces.ts +6 -1
- package/client/pill-popover.tsx +108 -23
- package/client/pills.tsx +35 -12
- package/client/preview.tsx +265 -79
- package/client/questions.tsx +176 -55
- package/client/row.tsx +14 -3
- package/client/section-title.tsx +11 -2
- package/client/settings.tsx +76 -24
- package/client/show-more.tsx +23 -6
- package/client/spinner.tsx +11 -7
- package/client/status-badge.tsx +9 -7
- package/client/tabs.tsx +98 -20
- package/client/ticket-dialog.tsx +241 -73
- package/client/time-bar.tsx +5 -2
- package/client/video.tsx +15 -6
- package/client/whats-new.tsx +94 -36
- package/client/when.tsx +2 -1
- package/client/write-up.tsx +79 -12
- package/dist/server/cli.js +199 -101
- package/index.client.tsx +28 -13
- package/index.server.ts +19 -1
- package/package.json +15 -14
- package/server/cli.ts +72 -32
- package/server/dashboard.ts +9 -13
- package/server/launcher.ts +33 -17
- package/server/lock.ts +14 -7
- package/server/open.ts +32 -98
- package/server/progress-file.ts +6 -6
- package/server/question-mode.ts +12 -6
- package/server/skill.ts +22 -16
- package/server/store.ts +26 -41
- package/server/system.ts +113 -0
- package/server/tool.ts +89 -44
- package/server/whats-new.ts +25 -13
- package/shared/attachments.ts +40 -8
- package/shared/blocks.ts +9 -6
- package/shared/dashboard.ts +46 -29
- package/shared/events.ts +12 -10
- package/shared/markdown.ts +16 -8
- package/shared/question-modes.ts +8 -5
- package/shared/rpc.ts +9 -3
- package/shared/show.ts +20 -8
- package/shared/ticket-story.ts +1 -3
- package/skills/paseo-progress/SKILL.md +1 -0
- package/tsconfig.json +3 -10
- package/client/page-opener.ts +0 -9
- package/client/page-screen.tsx +0 -97
package/client/video.tsx
CHANGED
|
@@ -7,8 +7,8 @@ interface VideoElement {
|
|
|
7
7
|
src: string;
|
|
8
8
|
videoWidth: number;
|
|
9
9
|
videoHeight: number;
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
addEventListener(type: "loadedmetadata" | "error", listener: () => void): void;
|
|
11
|
+
removeEventListener(type: "loadedmetadata" | "error", listener: () => void): void;
|
|
12
12
|
}
|
|
13
13
|
const page = globalThis as unknown as { document: { createElement(tag: "video"): VideoElement } };
|
|
14
14
|
|
|
@@ -17,14 +17,21 @@ const page = globalThis as unknown as { document: { createElement(tag: "video"):
|
|
|
17
17
|
export function videoRatio(blob: FileBlob): Promise<number | null> {
|
|
18
18
|
const url = browser.URL.createObjectURL(blob);
|
|
19
19
|
const video = page.document.createElement("video");
|
|
20
|
+
let stopListening: (() => void) | undefined;
|
|
20
21
|
return new Promise<number | null>((resolve) => {
|
|
21
22
|
setTimeout(() => resolve(null), 5_000);
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
const onMetadata = () => resolve(video.videoWidth && video.videoHeight ? video.videoWidth / video.videoHeight : null);
|
|
24
|
+
const onError = () => resolve(null);
|
|
25
|
+
video.addEventListener("loadedmetadata", onMetadata);
|
|
26
|
+
video.addEventListener("error", onError);
|
|
27
|
+
stopListening = () => {
|
|
28
|
+
video.removeEventListener("loadedmetadata", onMetadata);
|
|
29
|
+
video.removeEventListener("error", onError);
|
|
30
|
+
};
|
|
24
31
|
video.preload = "metadata";
|
|
25
32
|
video.src = url;
|
|
26
33
|
}).finally(() => {
|
|
27
|
-
|
|
34
|
+
stopListening?.();
|
|
28
35
|
video.src = "";
|
|
29
36
|
browser.URL.revokeObjectURL(url);
|
|
30
37
|
});
|
|
@@ -35,7 +42,9 @@ export function VideoPlayer({ blob, label, style }: { blob: FileBlob; label: str
|
|
|
35
42
|
const url = useObjectUrl(blob);
|
|
36
43
|
return (
|
|
37
44
|
<View style={[style, { overflow: "hidden" }]}>
|
|
38
|
-
{url ?
|
|
45
|
+
{url ? (
|
|
46
|
+
<video src={url} controls playsInline aria-label={label} style={{ width: "100%", height: "100%", display: "block", objectFit: "contain" }} />
|
|
47
|
+
) : null}
|
|
39
48
|
</View>
|
|
40
49
|
);
|
|
41
50
|
}
|
package/client/whats-new.tsx
CHANGED
|
@@ -38,38 +38,84 @@ export function WhatsNewBanner({ colors, host }: { colors: Colors; host: PluginW
|
|
|
38
38
|
|
|
39
39
|
return (
|
|
40
40
|
<>
|
|
41
|
-
<Presence show={Boolean(whatsNew)}>
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
<View style={{
|
|
45
|
-
<View style={{
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
41
|
+
<Presence show={Boolean(whatsNew)}>
|
|
42
|
+
{whatsNew ? (
|
|
43
|
+
// The headline gets the full width, with the button under it on the text's edge.
|
|
44
|
+
<View style={{ gap: 8, margin: 12, marginBottom: 0, padding: 8, borderRadius: 12, ...raised(colors), backgroundColor: colors.surface1 }}>
|
|
45
|
+
<View style={{ flexDirection: "row", alignItems: "flex-start", gap: 10 }}>
|
|
46
|
+
<View style={{ height: 17, justifyContent: "center" }}>
|
|
47
|
+
<Icon name="Gift" size={14} color={colors.foregroundMuted} />
|
|
48
|
+
</View>
|
|
49
|
+
<Text selectable style={{ flex: 1, color: colors.foregroundMuted, fontSize: 12, lineHeight: 17 }}>
|
|
50
|
+
<Text style={{ color: colors.foreground, fontWeight: "500" }}>
|
|
51
|
+
{whatsNew.releases.length > 1 ? m.updatedTo(whatsNew.version) : m.newVersion(whatsNew.version)}
|
|
52
|
+
</Text>{" "}
|
|
53
|
+
{whatsNew.headline}
|
|
54
|
+
</Text>
|
|
55
|
+
{/* Centered on the first line without making it taller. */}
|
|
56
|
+
<PressScale
|
|
57
|
+
accessibilityRole="button"
|
|
58
|
+
accessibilityLabel={m.dismissWhatsNew}
|
|
59
|
+
hitSlop={6}
|
|
60
|
+
onPress={() => close(whatsNew.version)}
|
|
61
|
+
style={({ pressed, hovered }) => ({
|
|
62
|
+
marginVertical: -4.5,
|
|
63
|
+
padding: 5,
|
|
64
|
+
borderRadius: 4,
|
|
65
|
+
backgroundColor: pressed ? colors.border : hovered ? colors.surface2 : "transparent",
|
|
66
|
+
})}
|
|
67
|
+
>
|
|
68
|
+
<Icon name="X" size={16} color={colors.foregroundMuted} />
|
|
69
|
+
</PressScale>
|
|
70
|
+
</View>
|
|
71
|
+
<PressScale
|
|
72
|
+
accessibilityRole="button"
|
|
73
|
+
onPress={() => {
|
|
74
|
+
setShown(whatsNew);
|
|
75
|
+
setOpen(true);
|
|
76
|
+
}}
|
|
77
|
+
style={({ pressed, hovered }) => ({
|
|
78
|
+
alignSelf: "flex-start",
|
|
79
|
+
marginStart: 24,
|
|
80
|
+
paddingHorizontal: 10,
|
|
81
|
+
paddingVertical: 5,
|
|
82
|
+
borderRadius: 4,
|
|
83
|
+
backgroundColor: colors.accent,
|
|
84
|
+
opacity: pressed ? 0.7 : hovered ? 0.85 : 1,
|
|
85
|
+
})}
|
|
86
|
+
>
|
|
87
|
+
<Text style={{ color: colors.accentForeground, fontSize: 12, lineHeight: 16, fontWeight: "600" }}>{m.seeWhatsNew}</Text>
|
|
53
88
|
</PressScale>
|
|
54
89
|
</View>
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
90
|
+
) : null}
|
|
91
|
+
</Presence>
|
|
92
|
+
<Modal
|
|
93
|
+
title={!shown ? "" : shown.releases.length > 1 ? m.whatsNewAll : m.whatsNewTitle(shown.version)}
|
|
94
|
+
icon={<Icon name="Gift" size={16} color={colors.foregroundMuted} />}
|
|
95
|
+
open={open}
|
|
96
|
+
onOpenChange={(next) => {
|
|
97
|
+
if (!next) closeDialog();
|
|
98
|
+
}}
|
|
99
|
+
>
|
|
63
100
|
<Modal.Content>
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
101
|
+
<ProgressLanguageProvider>
|
|
102
|
+
{shown ? <Releases colors={colors} releases={shown.releases} /> : null}
|
|
103
|
+
<View style={{ flexDirection: "row", justifyContent: "flex-end", marginTop: 8 }}>
|
|
104
|
+
<PressScale
|
|
105
|
+
accessibilityRole="button"
|
|
106
|
+
onPress={closeDialog}
|
|
107
|
+
style={({ pressed, hovered }) => ({
|
|
108
|
+
paddingHorizontal: 12,
|
|
109
|
+
paddingVertical: 6,
|
|
110
|
+
borderRadius: 6,
|
|
111
|
+
backgroundColor: colors.accent,
|
|
112
|
+
opacity: pressed ? 0.7 : hovered ? 0.85 : 1,
|
|
113
|
+
})}
|
|
114
|
+
>
|
|
115
|
+
<Text style={{ color: colors.accentForeground, fontSize: 12, lineHeight: 16, fontWeight: "600" }}>{m.gotIt}</Text>
|
|
116
|
+
</PressScale>
|
|
117
|
+
</View>
|
|
118
|
+
</ProgressLanguageProvider>
|
|
73
119
|
</Modal.Content>
|
|
74
120
|
</Modal>
|
|
75
121
|
</>
|
|
@@ -84,7 +130,9 @@ function Releases({ colors, releases }: { colors: Colors; releases: WhatsNewRele
|
|
|
84
130
|
<View style={{ gap: 40 }}>
|
|
85
131
|
{releases.map((release) => (
|
|
86
132
|
<View key={release.version} style={{ gap: 12 }}>
|
|
87
|
-
<Text accessibilityRole="header" selectable style={{ color: colors.foreground, fontSize: 12, lineHeight: 16, fontWeight: "600" }}>
|
|
133
|
+
<Text accessibilityRole="header" selectable style={{ color: colors.foreground, fontSize: 12, lineHeight: 16, fontWeight: "600" }}>
|
|
134
|
+
{release.version}
|
|
135
|
+
</Text>
|
|
88
136
|
<Notes colors={colors} notes={release.notes} />
|
|
89
137
|
</View>
|
|
90
138
|
))}
|
|
@@ -96,9 +144,13 @@ function Releases({ colors, releases }: { colors: Colors; releases: WhatsNewRele
|
|
|
96
144
|
// written without titles stays a bulleted list.
|
|
97
145
|
function Notes({ colors, notes }: { colors: Colors; notes: WhatsNewNote[] }) {
|
|
98
146
|
if (!notes.some((note) => note.title || note.kind)) {
|
|
99
|
-
return
|
|
100
|
-
<
|
|
101
|
-
|
|
147
|
+
return (
|
|
148
|
+
<View style={{ gap: 12 }}>
|
|
149
|
+
{notes.map((note, index) => (
|
|
150
|
+
<BlockView key={index} colors={colors} block={{ kind: "item", marker: "•", depth: 0, spans: spans(note.body) }} first={index === 0} />
|
|
151
|
+
))}
|
|
152
|
+
</View>
|
|
153
|
+
);
|
|
102
154
|
}
|
|
103
155
|
return (
|
|
104
156
|
<View style={{ gap: 20 }}>
|
|
@@ -107,7 +159,11 @@ function Notes({ colors, notes }: { colors: Colors; notes: WhatsNewNote[] }) {
|
|
|
107
159
|
{note.title || note.kind ? (
|
|
108
160
|
<View style={{ flexDirection: "row", alignItems: "flex-start", gap: 8 }}>
|
|
109
161
|
{note.kind ? <KindTag colors={colors} kind={note.kind} /> : null}
|
|
110
|
-
{note.title ?
|
|
162
|
+
{note.title ? (
|
|
163
|
+
<Text accessibilityRole="header" selectable style={{ flex: 1, color: colors.foreground, fontSize: 14, lineHeight: 20, fontWeight: "600" }}>
|
|
164
|
+
{note.title}
|
|
165
|
+
</Text>
|
|
166
|
+
) : null}
|
|
111
167
|
</View>
|
|
112
168
|
) : null}
|
|
113
169
|
{note.body ? (
|
|
@@ -124,7 +180,7 @@ function Notes({ colors, notes }: { colors: Colors; notes: WhatsNewNote[] }) {
|
|
|
124
180
|
// Paseo has no violet for plugins, so it's Paseo's own "merged" purple, which
|
|
125
181
|
// shares the status colors' lightness and chroma.
|
|
126
182
|
const TONE_COLOR: Record<NoteTone, (colors: Colors) => string> = {
|
|
127
|
-
violet: (colors) => isLight(colors) ? "#7347af" : "#a890d5",
|
|
183
|
+
violet: (colors) => (isLight(colors) ? "#7347af" : "#a890d5"),
|
|
128
184
|
green: (colors) => colors.statusSuccess,
|
|
129
185
|
amber: (colors) => colors.statusWarning,
|
|
130
186
|
};
|
|
@@ -136,7 +192,9 @@ function KindTag({ colors, kind }: { colors: Colors; kind: NoteKind }) {
|
|
|
136
192
|
const color = TONE_COLOR[NOTE_KINDS[kind].tone](colors);
|
|
137
193
|
return (
|
|
138
194
|
<View style={{ marginTop: 1, minWidth: 64, borderRadius: 4, paddingHorizontal: 7, backgroundColor: tint(color, colors) }}>
|
|
139
|
-
<Text numberOfLines={1} style={{ textAlign: "center", color, fontSize: 11, lineHeight: 18, fontWeight: "500" }}>
|
|
195
|
+
<Text numberOfLines={1} style={{ textAlign: "center", color, fontSize: 11, lineHeight: 18, fontWeight: "500" }}>
|
|
196
|
+
{m.noteKinds[kind]}
|
|
197
|
+
</Text>
|
|
140
198
|
</View>
|
|
141
199
|
);
|
|
142
200
|
}
|
package/client/when.tsx
CHANGED
|
@@ -21,7 +21,8 @@ export function When({ colors, iso, now }: { colors: Colors; iso: string; now: n
|
|
|
21
21
|
if (useNarrow()) return <Text>{ago}</Text>;
|
|
22
22
|
return (
|
|
23
23
|
<Text>
|
|
24
|
-
{formatClientClock(locale, iso)}
|
|
24
|
+
{formatClientClock(locale, iso)}
|
|
25
|
+
<Text style={{ color: colors.foregroundMuted, opacity: 0.7 }}>{m.parenthetical(ago)}</Text>
|
|
25
26
|
</Text>
|
|
26
27
|
);
|
|
27
28
|
}
|
package/client/write-up.tsx
CHANGED
|
@@ -2,11 +2,11 @@ import { type PluginWorkspacePanelProps, useRpc } from "@getpaseo/plugin/client"
|
|
|
2
2
|
import { Icon, Markdown } from "@getpaseo/plugin/client/ui";
|
|
3
3
|
import { useQuery } from "@tanstack/react-query";
|
|
4
4
|
import React, { useMemo, useState } from "react";
|
|
5
|
-
import {
|
|
5
|
+
import { Text, View } from "react-native";
|
|
6
6
|
import { previewKind } from "../shared/attachments";
|
|
7
7
|
import { previewDeliverable } from "../shared/rpc";
|
|
8
8
|
import { flattenHeadings, dropTitle, markdownExcerpt, textExcerpt } from "../shared/markdown";
|
|
9
|
-
import { type Attachment, attachmentIcon } from "./attachments";
|
|
9
|
+
import { type Attachment, absolutePath, attachmentIcon, followPageLink, PageLink, pageLinkRef } from "./attachments";
|
|
10
10
|
import { PressableRow } from "./row";
|
|
11
11
|
import { errorText } from "./errors";
|
|
12
12
|
import { useProgressLanguage } from "./language";
|
|
@@ -18,7 +18,13 @@ const EXCERPT_CHARS = 280;
|
|
|
18
18
|
|
|
19
19
|
// What the ticket is: the start of its write-up in a quiet inset, with the
|
|
20
20
|
// rest a press away. A link or a file that isn't text shows only its name.
|
|
21
|
-
export function WriteUp({
|
|
21
|
+
export function WriteUp({
|
|
22
|
+
colors,
|
|
23
|
+
source,
|
|
24
|
+
workspaceId,
|
|
25
|
+
workspaceDirectory,
|
|
26
|
+
onOpen,
|
|
27
|
+
}: {
|
|
22
28
|
colors: Colors;
|
|
23
29
|
source: Attachment;
|
|
24
30
|
workspaceId: string;
|
|
@@ -40,7 +46,13 @@ export function WriteUp({ colors, source, workspaceId, workspaceDirectory, onOpe
|
|
|
40
46
|
const parts = useMemo(() => {
|
|
41
47
|
if (data?.kind === "markdown") {
|
|
42
48
|
const excerpt = markdownExcerpt(data.markdown, EXCERPT_CHARS);
|
|
43
|
-
return {
|
|
49
|
+
return {
|
|
50
|
+
markdown: true,
|
|
51
|
+
excerpt: flattenHeadings(excerpt.text, WRITE_UP_HEADING),
|
|
52
|
+
whole: flattenHeadings(dropTitle(data.markdown), WRITE_UP_HEADING),
|
|
53
|
+
more: excerpt.more,
|
|
54
|
+
truncated: data.truncated,
|
|
55
|
+
};
|
|
44
56
|
}
|
|
45
57
|
if (data?.kind === "text") {
|
|
46
58
|
const excerpt = textExcerpt(data.text, EXCERPT_CHARS);
|
|
@@ -49,16 +61,36 @@ export function WriteUp({ colors, source, workspaceId, workspaceDirectory, onOpe
|
|
|
49
61
|
return null;
|
|
50
62
|
}, [data]);
|
|
51
63
|
const shown = parts ? (all ? parts.whole : parts.excerpt) : "";
|
|
64
|
+
const link = pageLinkRef(source);
|
|
52
65
|
const muted = { color: colors.foregroundMuted, fontSize: 12, lineHeight: 18 } as const;
|
|
53
66
|
return (
|
|
54
67
|
<View style={{ gap: 12, padding: 14, borderRadius: 8, backgroundColor: colors.surface0 }}>
|
|
55
|
-
{shown.trim() ? (
|
|
56
|
-
|
|
68
|
+
{shown.trim() ? (
|
|
69
|
+
parts?.markdown ? (
|
|
70
|
+
<Markdown>{shown}</Markdown>
|
|
71
|
+
) : (
|
|
72
|
+
<Text selectable style={{ color: colors.foreground, fontSize: 13, lineHeight: 19 }}>
|
|
73
|
+
{shown}
|
|
74
|
+
</Text>
|
|
75
|
+
)
|
|
76
|
+
) : null}
|
|
57
77
|
{all && parts?.truncated ? <Text style={muted}>{m.truncatedText}</Text> : null}
|
|
58
78
|
{preview.error ? <Text style={muted}>{m.readError(errorText(preview.error, m.unknownError))}</Text> : null}
|
|
59
79
|
{/* The file on the left, opening it whole; expanding in place on the right. */}
|
|
60
80
|
<View style={{ flexDirection: "row", alignItems: "center", gap: 12 }}>
|
|
61
|
-
<TextButton
|
|
81
|
+
<TextButton
|
|
82
|
+
colors={colors}
|
|
83
|
+
icon={attachmentIcon(source)}
|
|
84
|
+
label={source.title}
|
|
85
|
+
accessibilityLabel={m.openItem(source.title)}
|
|
86
|
+
onPress={() => {
|
|
87
|
+
if (link && !followPageLink(link)) return;
|
|
88
|
+
onOpen(source, [source]);
|
|
89
|
+
}}
|
|
90
|
+
grow
|
|
91
|
+
>
|
|
92
|
+
{link ? <PageLink link={link} path={absolutePath(source.path!, workspaceDirectory)} /> : null}
|
|
93
|
+
</TextButton>
|
|
62
94
|
<View style={{ flex: 1 }} />
|
|
63
95
|
{parts?.more ? <TextButton colors={colors} label={all ? m.showLess : m.readMore} onPress={() => setAll(!all)} expanded={all} /> : null}
|
|
64
96
|
</View>
|
|
@@ -67,14 +99,49 @@ export function WriteUp({ colors, source, workspaceId, workspaceDirectory, onOpe
|
|
|
67
99
|
}
|
|
68
100
|
|
|
69
101
|
// A quiet text button for the inset's footer: muted, tinted on hover.
|
|
70
|
-
function TextButton({
|
|
71
|
-
colors
|
|
102
|
+
function TextButton({
|
|
103
|
+
colors,
|
|
104
|
+
icon,
|
|
105
|
+
label,
|
|
106
|
+
accessibilityLabel,
|
|
107
|
+
onPress,
|
|
108
|
+
grow = false,
|
|
109
|
+
expanded,
|
|
110
|
+
children,
|
|
111
|
+
}: {
|
|
112
|
+
colors: Colors;
|
|
113
|
+
icon?: string;
|
|
114
|
+
label: string;
|
|
115
|
+
accessibilityLabel?: string;
|
|
116
|
+
onPress(): void;
|
|
117
|
+
grow?: boolean;
|
|
118
|
+
expanded?: boolean;
|
|
119
|
+
children?: React.ReactNode;
|
|
72
120
|
}) {
|
|
73
121
|
return (
|
|
74
|
-
<PressableRow
|
|
75
|
-
|
|
122
|
+
<PressableRow
|
|
123
|
+
colors={colors}
|
|
124
|
+
accessibilityRole="button"
|
|
125
|
+
accessibilityLabel={accessibilityLabel ?? label}
|
|
126
|
+
aria-expanded={expanded}
|
|
127
|
+
onPress={onPress}
|
|
128
|
+
style={{
|
|
129
|
+
flexDirection: "row",
|
|
130
|
+
alignItems: "center",
|
|
131
|
+
gap: 6,
|
|
132
|
+
paddingVertical: 3,
|
|
133
|
+
paddingHorizontal: 6,
|
|
134
|
+
marginHorizontal: -6,
|
|
135
|
+
borderRadius: 4,
|
|
136
|
+
flexShrink: grow ? 1 : 0,
|
|
137
|
+
minWidth: 0,
|
|
138
|
+
}}
|
|
139
|
+
>
|
|
76
140
|
{icon ? <Icon name={icon} size={12} color={colors.foregroundMuted} /> : null}
|
|
77
|
-
<Text numberOfLines={1} style={{ flexShrink: 1, color: colors.foregroundMuted, fontSize: 12, lineHeight: 17, fontWeight: "500" }}>
|
|
141
|
+
<Text numberOfLines={1} style={{ flexShrink: 1, color: colors.foregroundMuted, fontSize: 12, lineHeight: 17, fontWeight: "500" }}>
|
|
142
|
+
{label}
|
|
143
|
+
</Text>
|
|
144
|
+
{children}
|
|
78
145
|
</PressableRow>
|
|
79
146
|
);
|
|
80
147
|
}
|