@omercnet/paseo-context-mode 0.0.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/LICENSE +21 -0
- package/NOTICE +7 -0
- package/README.md +74 -0
- package/client/actions-section.tsx +621 -0
- package/client/analytics-dashboard.tsx +721 -0
- package/client/context-mode-pill.tsx +106 -0
- package/client/context-mode-surface.tsx +414 -0
- package/client/knowledge-section.tsx +780 -0
- package/client/settings-screen.tsx +226 -0
- package/index.client.tsx +43 -0
- package/index.server.ts +54 -0
- package/package.json +72 -0
- package/paseo-plugin.json +5 -0
- package/server/actions.ts +146 -0
- package/server/analytics.ts +424 -0
- package/server/binary.ts +182 -0
- package/server/context-mode-db.ts +481 -0
- package/server/handlers.ts +200 -0
- package/server/integration.ts +441 -0
- package/server/knowledge.ts +187 -0
- package/server/mcp-process.ts +357 -0
- package/server/storage-discovery.ts +225 -0
- package/shared/actions.ts +57 -0
- package/shared/analytics.ts +134 -0
- package/shared/contracts.ts +95 -0
- package/shared/index.ts +5 -0
- package/shared/knowledge.ts +150 -0
- package/shared/settings.ts +51 -0
|
@@ -0,0 +1,621 @@
|
|
|
1
|
+
import { type PluginSurfaceProps, useRpc } from "@getpaseo/plugin/client";
|
|
2
|
+
import { useMutation } from "@tanstack/react-query";
|
|
3
|
+
import { useMemo, useState } from "react";
|
|
4
|
+
import {
|
|
5
|
+
ActivityIndicator,
|
|
6
|
+
Pressable,
|
|
7
|
+
ScrollView,
|
|
8
|
+
Text,
|
|
9
|
+
type TextStyle,
|
|
10
|
+
View,
|
|
11
|
+
type ViewStyle,
|
|
12
|
+
} from "react-native";
|
|
13
|
+
import {
|
|
14
|
+
CONTEXT_MODE_TARGET_VERSION,
|
|
15
|
+
type ContextModeAction,
|
|
16
|
+
type DoctorReport,
|
|
17
|
+
getContextModeDoctorReport,
|
|
18
|
+
getContextModeInstallAction,
|
|
19
|
+
getContextModeUpgradeAction,
|
|
20
|
+
} from "../shared/actions";
|
|
21
|
+
import type { ContextModeProvider } from "../shared/knowledge";
|
|
22
|
+
|
|
23
|
+
const PROVIDERS = [
|
|
24
|
+
{ value: "claude", label: "Claude" },
|
|
25
|
+
{ value: "codex", label: "Codex" },
|
|
26
|
+
{ value: "cursor", label: "Cursor" },
|
|
27
|
+
{ value: "copilot", label: "Copilot" },
|
|
28
|
+
{ value: "opencode", label: "OpenCode" },
|
|
29
|
+
{ value: "pi", label: "Pi" },
|
|
30
|
+
{ value: "omp", label: "OMP" },
|
|
31
|
+
{ value: "omp-plugin", label: "OMP plugin" },
|
|
32
|
+
] as const satisfies ReadonlyArray<{ value: ContextModeProvider; label: string }>;
|
|
33
|
+
|
|
34
|
+
export type ActionsSectionProps = Pick<PluginSurfaceProps, "theme" | "layout">;
|
|
35
|
+
|
|
36
|
+
interface ActionsStyles {
|
|
37
|
+
section: ViewStyle;
|
|
38
|
+
sectionHeader: ViewStyle;
|
|
39
|
+
sectionTitle: TextStyle;
|
|
40
|
+
card: ViewStyle;
|
|
41
|
+
cardHeader: ViewStyle;
|
|
42
|
+
cardTitle: TextStyle;
|
|
43
|
+
muted: TextStyle;
|
|
44
|
+
choiceRow: ViewStyle;
|
|
45
|
+
choice: ViewStyle;
|
|
46
|
+
choiceSelected: ViewStyle;
|
|
47
|
+
choiceText: TextStyle;
|
|
48
|
+
choiceTextSelected: TextStyle;
|
|
49
|
+
scopeNote: TextStyle;
|
|
50
|
+
actions: ViewStyle;
|
|
51
|
+
button: ViewStyle;
|
|
52
|
+
primaryButton: ViewStyle;
|
|
53
|
+
disabled: ViewStyle;
|
|
54
|
+
buttonText: TextStyle;
|
|
55
|
+
primaryButtonText: TextStyle;
|
|
56
|
+
pendingRow: ViewStyle;
|
|
57
|
+
activity: { color: string };
|
|
58
|
+
error: TextStyle;
|
|
59
|
+
doctorRows: ViewStyle;
|
|
60
|
+
doctorRow: ViewStyle;
|
|
61
|
+
doctorMarker: ViewStyle;
|
|
62
|
+
doctorMarkerOk: ViewStyle;
|
|
63
|
+
doctorMarkerWarning: ViewStyle;
|
|
64
|
+
doctorMarkerError: ViewStyle;
|
|
65
|
+
doctorCopy: ViewStyle;
|
|
66
|
+
doctorTopline: ViewStyle;
|
|
67
|
+
doctorCheck: TextStyle;
|
|
68
|
+
doctorStatus: TextStyle;
|
|
69
|
+
doctorStatusOk: TextStyle;
|
|
70
|
+
doctorStatusWarning: TextStyle;
|
|
71
|
+
doctorStatusError: TextStyle;
|
|
72
|
+
doctorDetail: TextStyle;
|
|
73
|
+
rawFrame: ViewStyle;
|
|
74
|
+
rawOutput: TextStyle;
|
|
75
|
+
commandCard: ViewStyle;
|
|
76
|
+
commandTitle: TextStyle;
|
|
77
|
+
property: ViewStyle;
|
|
78
|
+
propertyLabel: TextStyle;
|
|
79
|
+
propertyValue: TextStyle;
|
|
80
|
+
argumentFrame: ViewStyle;
|
|
81
|
+
argumentRow: ViewStyle;
|
|
82
|
+
argumentIndex: TextStyle;
|
|
83
|
+
argumentValue: TextStyle;
|
|
84
|
+
metaRow: ViewStyle;
|
|
85
|
+
badge: ViewStyle;
|
|
86
|
+
badgeText: TextStyle;
|
|
87
|
+
warningBadge: ViewStyle;
|
|
88
|
+
warningBadgeText: TextStyle;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function ActionButton({
|
|
92
|
+
label,
|
|
93
|
+
onPress,
|
|
94
|
+
disabled = false,
|
|
95
|
+
primary = false,
|
|
96
|
+
styles,
|
|
97
|
+
}: {
|
|
98
|
+
label: string;
|
|
99
|
+
onPress(): void;
|
|
100
|
+
disabled?: boolean;
|
|
101
|
+
primary?: boolean;
|
|
102
|
+
styles: ActionsStyles;
|
|
103
|
+
}) {
|
|
104
|
+
return (
|
|
105
|
+
<Pressable
|
|
106
|
+
accessibilityLabel={label}
|
|
107
|
+
accessibilityRole="button"
|
|
108
|
+
accessibilityState={{ disabled }}
|
|
109
|
+
disabled={disabled}
|
|
110
|
+
onPress={onPress}
|
|
111
|
+
style={[
|
|
112
|
+
styles.button,
|
|
113
|
+
primary ? styles.primaryButton : null,
|
|
114
|
+
disabled ? styles.disabled : null,
|
|
115
|
+
]}
|
|
116
|
+
>
|
|
117
|
+
<Text style={[styles.buttonText, primary ? styles.primaryButtonText : null]}>{label}</Text>
|
|
118
|
+
</Pressable>
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function DoctorResult({
|
|
123
|
+
report,
|
|
124
|
+
pending,
|
|
125
|
+
error,
|
|
126
|
+
styles,
|
|
127
|
+
}: {
|
|
128
|
+
report: DoctorReport | undefined;
|
|
129
|
+
pending: boolean;
|
|
130
|
+
error: unknown;
|
|
131
|
+
styles: ActionsStyles;
|
|
132
|
+
}) {
|
|
133
|
+
const [showRaw, setShowRaw] = useState(false);
|
|
134
|
+
if (pending) {
|
|
135
|
+
return (
|
|
136
|
+
<View accessibilityLiveRegion="polite" style={styles.pendingRow}>
|
|
137
|
+
<ActivityIndicator size="small" color={styles.activity.color} />
|
|
138
|
+
<Text style={styles.muted}>Running diagnostics…</Text>
|
|
139
|
+
</View>
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
if (error) {
|
|
143
|
+
return (
|
|
144
|
+
<Text accessibilityRole="alert" style={styles.error}>
|
|
145
|
+
{error instanceof Error ? error.message : String(error)}
|
|
146
|
+
</Text>
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
if (!report) return null;
|
|
150
|
+
|
|
151
|
+
const hasRows = report.rows.length > 0;
|
|
152
|
+
const rawVisible = !hasRows || showRaw;
|
|
153
|
+
return (
|
|
154
|
+
<View accessibilityLiveRegion="polite" style={styles.doctorRows}>
|
|
155
|
+
<View style={styles.metaRow}>
|
|
156
|
+
<View style={styles.badge}>
|
|
157
|
+
<Text style={styles.badgeText}>{report.platform}</Text>
|
|
158
|
+
</View>
|
|
159
|
+
{hasRows && report.rawOutput.trim() ? (
|
|
160
|
+
<ActionButton
|
|
161
|
+
label={showRaw ? "Hide raw diagnostics" : "Show raw diagnostics"}
|
|
162
|
+
onPress={() => setShowRaw((visible) => !visible)}
|
|
163
|
+
styles={styles}
|
|
164
|
+
/>
|
|
165
|
+
) : null}
|
|
166
|
+
</View>
|
|
167
|
+
{report.rows.map((row) => {
|
|
168
|
+
const ok = row.status === "ok";
|
|
169
|
+
const warning = row.status === "warning";
|
|
170
|
+
const statusLabel = ok ? "OK" : warning ? "Warning" : "Error";
|
|
171
|
+
return (
|
|
172
|
+
<View
|
|
173
|
+
key={`${row.status}:${row.check}:${row.detail}`}
|
|
174
|
+
accessibilityLabel={`${row.check}: ${statusLabel}. ${row.detail}`}
|
|
175
|
+
style={styles.doctorRow}
|
|
176
|
+
>
|
|
177
|
+
<View
|
|
178
|
+
style={[
|
|
179
|
+
styles.doctorMarker,
|
|
180
|
+
ok
|
|
181
|
+
? styles.doctorMarkerOk
|
|
182
|
+
: warning
|
|
183
|
+
? styles.doctorMarkerWarning
|
|
184
|
+
: styles.doctorMarkerError,
|
|
185
|
+
]}
|
|
186
|
+
/>
|
|
187
|
+
<View style={styles.doctorCopy}>
|
|
188
|
+
<View style={styles.doctorTopline}>
|
|
189
|
+
<Text style={styles.doctorCheck}>{row.check}</Text>
|
|
190
|
+
<Text
|
|
191
|
+
style={[
|
|
192
|
+
styles.doctorStatus,
|
|
193
|
+
ok
|
|
194
|
+
? styles.doctorStatusOk
|
|
195
|
+
: warning
|
|
196
|
+
? styles.doctorStatusWarning
|
|
197
|
+
: styles.doctorStatusError,
|
|
198
|
+
]}
|
|
199
|
+
>
|
|
200
|
+
{statusLabel}
|
|
201
|
+
</Text>
|
|
202
|
+
</View>
|
|
203
|
+
{row.detail ? (
|
|
204
|
+
<Text selectable style={styles.doctorDetail}>
|
|
205
|
+
{row.detail}
|
|
206
|
+
</Text>
|
|
207
|
+
) : null}
|
|
208
|
+
</View>
|
|
209
|
+
</View>
|
|
210
|
+
);
|
|
211
|
+
})}
|
|
212
|
+
{rawVisible ? (
|
|
213
|
+
<View style={styles.rawFrame}>
|
|
214
|
+
<Text style={styles.propertyLabel}>Raw diagnostic output</Text>
|
|
215
|
+
<ScrollView nestedScrollEnabled style={styles.argumentFrame}>
|
|
216
|
+
<Text selectable style={styles.rawOutput}>
|
|
217
|
+
{report.rawOutput || "Context Mode returned no diagnostic text."}
|
|
218
|
+
</Text>
|
|
219
|
+
</ScrollView>
|
|
220
|
+
</View>
|
|
221
|
+
) : null}
|
|
222
|
+
</View>
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function CommandResult({
|
|
227
|
+
title,
|
|
228
|
+
action,
|
|
229
|
+
pending,
|
|
230
|
+
error,
|
|
231
|
+
styles,
|
|
232
|
+
}: {
|
|
233
|
+
title: string;
|
|
234
|
+
action: ContextModeAction | undefined;
|
|
235
|
+
pending: boolean;
|
|
236
|
+
error: unknown;
|
|
237
|
+
styles: ActionsStyles;
|
|
238
|
+
}) {
|
|
239
|
+
if (pending) {
|
|
240
|
+
return (
|
|
241
|
+
<View accessibilityLiveRegion="polite" style={styles.pendingRow}>
|
|
242
|
+
<ActivityIndicator size="small" color={styles.activity.color} />
|
|
243
|
+
<Text style={styles.muted}>Preparing {title.toLowerCase()}…</Text>
|
|
244
|
+
</View>
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
if (error) {
|
|
248
|
+
return (
|
|
249
|
+
<Text accessibilityRole="alert" style={styles.error}>
|
|
250
|
+
{error instanceof Error ? error.message : String(error)}
|
|
251
|
+
</Text>
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
if (!action) return null;
|
|
255
|
+
|
|
256
|
+
const seenArguments = new Map<string, number>();
|
|
257
|
+
const argumentsWithKeys = action.args.map((argument, index) => {
|
|
258
|
+
const occurrence = seenArguments.get(argument) ?? 0;
|
|
259
|
+
seenArguments.set(argument, occurrence + 1);
|
|
260
|
+
return { argument, position: index + 1, key: `${argument}:${occurrence}` };
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
return (
|
|
264
|
+
<View accessibilityLiveRegion="polite" style={styles.commandCard}>
|
|
265
|
+
<View style={styles.cardHeader}>
|
|
266
|
+
<Text style={styles.commandTitle}>{title}</Text>
|
|
267
|
+
<Text style={styles.muted}>
|
|
268
|
+
Review this executable and argument list before running it outside Paseo.
|
|
269
|
+
</Text>
|
|
270
|
+
</View>
|
|
271
|
+
<View style={styles.property}>
|
|
272
|
+
<Text style={styles.propertyLabel}>Program</Text>
|
|
273
|
+
<Text selectable style={styles.propertyValue}>
|
|
274
|
+
{action.program}
|
|
275
|
+
</Text>
|
|
276
|
+
</View>
|
|
277
|
+
<View style={styles.property}>
|
|
278
|
+
<Text style={styles.propertyLabel}>Arguments ({action.args.length})</Text>
|
|
279
|
+
<ScrollView nestedScrollEnabled style={styles.argumentFrame}>
|
|
280
|
+
{action.args.length ? (
|
|
281
|
+
argumentsWithKeys.map(({ argument, key, position }) => (
|
|
282
|
+
<View key={key} style={styles.argumentRow}>
|
|
283
|
+
<Text style={styles.argumentIndex}>{position}</Text>
|
|
284
|
+
<Text selectable style={styles.argumentValue}>
|
|
285
|
+
{argument}
|
|
286
|
+
</Text>
|
|
287
|
+
</View>
|
|
288
|
+
))
|
|
289
|
+
) : (
|
|
290
|
+
<Text style={styles.muted}>No arguments.</Text>
|
|
291
|
+
)}
|
|
292
|
+
</ScrollView>
|
|
293
|
+
</View>
|
|
294
|
+
<View style={styles.metaRow}>
|
|
295
|
+
<View style={styles.badge}>
|
|
296
|
+
<Text style={styles.badgeText}>{action.platform}</Text>
|
|
297
|
+
</View>
|
|
298
|
+
<View style={styles.badge}>
|
|
299
|
+
<Text style={styles.badgeText}>
|
|
300
|
+
{action.currentVersion ? `${action.currentVersion} → ` : "Install "}
|
|
301
|
+
{action.targetVersion}
|
|
302
|
+
</Text>
|
|
303
|
+
</View>
|
|
304
|
+
{action.requiresRestart ? (
|
|
305
|
+
<View style={styles.warningBadge}>
|
|
306
|
+
<Text style={styles.warningBadgeText}>Restart required</Text>
|
|
307
|
+
</View>
|
|
308
|
+
) : null}
|
|
309
|
+
</View>
|
|
310
|
+
</View>
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
export function ActionsSection({ theme, layout }: ActionsSectionProps) {
|
|
315
|
+
const styles = useMemo(() => createStyles(theme, layout.compact), [layout.compact, theme]);
|
|
316
|
+
const loadDoctor = useRpc(getContextModeDoctorReport);
|
|
317
|
+
const loadInstall = useRpc(getContextModeInstallAction);
|
|
318
|
+
const loadUpgrade = useRpc(getContextModeUpgradeAction);
|
|
319
|
+
const [provider, setProvider] = useState<ContextModeProvider | null>("claude");
|
|
320
|
+
const doctor = useMutation({
|
|
321
|
+
mutationFn: () => {
|
|
322
|
+
if (!provider) throw new Error("Select a provider before running diagnostics.");
|
|
323
|
+
return loadDoctor({ provider });
|
|
324
|
+
},
|
|
325
|
+
});
|
|
326
|
+
const install = useMutation({
|
|
327
|
+
mutationFn: () => {
|
|
328
|
+
if (!provider) throw new Error("Select a provider before preparing an install command.");
|
|
329
|
+
return loadInstall({ provider });
|
|
330
|
+
},
|
|
331
|
+
});
|
|
332
|
+
const upgrade = useMutation({
|
|
333
|
+
mutationFn: () => {
|
|
334
|
+
if (!provider) throw new Error("Select a provider before preparing an upgrade command.");
|
|
335
|
+
return loadUpgrade({ provider });
|
|
336
|
+
},
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
const changeProvider = (next: ContextModeProvider) => {
|
|
340
|
+
setProvider(next);
|
|
341
|
+
doctor.reset();
|
|
342
|
+
install.reset();
|
|
343
|
+
upgrade.reset();
|
|
344
|
+
};
|
|
345
|
+
const busy = doctor.isPending || install.isPending || upgrade.isPending;
|
|
346
|
+
|
|
347
|
+
return (
|
|
348
|
+
<View style={styles.section}>
|
|
349
|
+
<View style={styles.sectionHeader}>
|
|
350
|
+
<Text accessibilityRole="header" style={styles.sectionTitle}>
|
|
351
|
+
Diagnostics and setup
|
|
352
|
+
</Text>
|
|
353
|
+
<Text style={styles.muted}>Health checks, installation, and upgrades.</Text>
|
|
354
|
+
</View>
|
|
355
|
+
|
|
356
|
+
<View style={styles.card}>
|
|
357
|
+
<Text style={styles.cardTitle}>Target provider</Text>
|
|
358
|
+
<View accessibilityRole="radiogroup" style={styles.choiceRow}>
|
|
359
|
+
{PROVIDERS.map((option) => {
|
|
360
|
+
const selected = provider === option.value;
|
|
361
|
+
return (
|
|
362
|
+
<Pressable
|
|
363
|
+
key={option.value}
|
|
364
|
+
accessibilityLabel={`${option.label} action target`}
|
|
365
|
+
accessibilityRole="radio"
|
|
366
|
+
accessibilityState={{ checked: selected, disabled: busy }}
|
|
367
|
+
disabled={busy}
|
|
368
|
+
onPress={() => changeProvider(option.value)}
|
|
369
|
+
style={[
|
|
370
|
+
styles.choice,
|
|
371
|
+
selected ? styles.choiceSelected : null,
|
|
372
|
+
busy ? styles.disabled : null,
|
|
373
|
+
]}
|
|
374
|
+
>
|
|
375
|
+
<Text style={[styles.choiceText, selected ? styles.choiceTextSelected : null]}>
|
|
376
|
+
{option.label}
|
|
377
|
+
</Text>
|
|
378
|
+
</Pressable>
|
|
379
|
+
);
|
|
380
|
+
})}
|
|
381
|
+
</View>
|
|
382
|
+
<Text accessibilityLiveRegion="polite" style={styles.scopeNote}>
|
|
383
|
+
{provider
|
|
384
|
+
? `Diagnostics and commands will target ${PROVIDERS.find((item) => item.value === provider)?.label}.`
|
|
385
|
+
: "Select a provider before requesting a diagnostic or command."}
|
|
386
|
+
</Text>
|
|
387
|
+
</View>
|
|
388
|
+
|
|
389
|
+
<View style={styles.card}>
|
|
390
|
+
<View style={styles.cardHeader}>
|
|
391
|
+
<Text accessibilityRole="header" style={styles.cardTitle}>
|
|
392
|
+
Doctor
|
|
393
|
+
</Text>
|
|
394
|
+
<Text style={styles.muted}>
|
|
395
|
+
Run read-only checks. Structured rows are shown first; the original diagnostic text
|
|
396
|
+
remains available as a fallback.
|
|
397
|
+
</Text>
|
|
398
|
+
</View>
|
|
399
|
+
<View style={styles.actions}>
|
|
400
|
+
<ActionButton
|
|
401
|
+
disabled={!provider || busy}
|
|
402
|
+
label={doctor.isPending ? "Running doctor…" : "Run doctor"}
|
|
403
|
+
onPress={() => doctor.mutate()}
|
|
404
|
+
primary
|
|
405
|
+
styles={styles}
|
|
406
|
+
/>
|
|
407
|
+
</View>
|
|
408
|
+
<DoctorResult
|
|
409
|
+
error={doctor.error}
|
|
410
|
+
pending={doctor.isPending}
|
|
411
|
+
report={doctor.data}
|
|
412
|
+
styles={styles}
|
|
413
|
+
/>
|
|
414
|
+
</View>
|
|
415
|
+
|
|
416
|
+
<View style={styles.card}>
|
|
417
|
+
<View style={styles.cardHeader}>
|
|
418
|
+
<Text accessibilityRole="header" style={styles.cardTitle}>
|
|
419
|
+
Install or upgrade
|
|
420
|
+
</Text>
|
|
421
|
+
<Text style={styles.muted}>
|
|
422
|
+
Install requests pin Context Mode {CONTEXT_MODE_TARGET_VERSION}; upgrades target the
|
|
423
|
+
latest release. Paseo shows the exact command; run it yourself.
|
|
424
|
+
</Text>
|
|
425
|
+
</View>
|
|
426
|
+
<View style={styles.actions}>
|
|
427
|
+
<ActionButton
|
|
428
|
+
disabled={!provider || busy}
|
|
429
|
+
label={install.isPending ? "Preparing install…" : "Prepare install"}
|
|
430
|
+
onPress={() => install.mutate()}
|
|
431
|
+
styles={styles}
|
|
432
|
+
/>
|
|
433
|
+
<ActionButton
|
|
434
|
+
disabled={!provider || busy}
|
|
435
|
+
label={upgrade.isPending ? "Preparing upgrade…" : "Prepare upgrade"}
|
|
436
|
+
onPress={() => upgrade.mutate()}
|
|
437
|
+
styles={styles}
|
|
438
|
+
/>
|
|
439
|
+
</View>
|
|
440
|
+
<CommandResult
|
|
441
|
+
action={install.data}
|
|
442
|
+
error={install.error}
|
|
443
|
+
pending={install.isPending}
|
|
444
|
+
styles={styles}
|
|
445
|
+
title="Install command"
|
|
446
|
+
/>
|
|
447
|
+
<CommandResult
|
|
448
|
+
action={upgrade.data}
|
|
449
|
+
error={upgrade.error}
|
|
450
|
+
pending={upgrade.isPending}
|
|
451
|
+
styles={styles}
|
|
452
|
+
title="Upgrade command"
|
|
453
|
+
/>
|
|
454
|
+
</View>
|
|
455
|
+
</View>
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
function createStyles(theme: PluginSurfaceProps["theme"], compact: boolean): ActionsStyles {
|
|
460
|
+
return {
|
|
461
|
+
section: { width: "100%", gap: compact ? 12 : 16 },
|
|
462
|
+
sectionHeader: { gap: 4 },
|
|
463
|
+
sectionTitle: {
|
|
464
|
+
color: theme.colors.foreground,
|
|
465
|
+
fontSize: compact ? 18 : 20,
|
|
466
|
+
fontWeight: "700",
|
|
467
|
+
},
|
|
468
|
+
card: {
|
|
469
|
+
width: "100%",
|
|
470
|
+
gap: 12,
|
|
471
|
+
padding: compact ? 12 : 16,
|
|
472
|
+
borderWidth: 1,
|
|
473
|
+
borderColor: theme.colors.border,
|
|
474
|
+
borderRadius: 12,
|
|
475
|
+
backgroundColor: theme.colors.surface1,
|
|
476
|
+
},
|
|
477
|
+
cardHeader: { gap: 3 },
|
|
478
|
+
cardTitle: { color: theme.colors.foreground, fontSize: 15, fontWeight: "700" },
|
|
479
|
+
muted: { color: theme.colors.foregroundMuted, fontSize: 12, lineHeight: 17 },
|
|
480
|
+
choiceRow: { flexDirection: "row", flexWrap: "wrap", gap: 7 },
|
|
481
|
+
choice: {
|
|
482
|
+
minHeight: 36,
|
|
483
|
+
justifyContent: "center",
|
|
484
|
+
paddingHorizontal: 11,
|
|
485
|
+
paddingVertical: 7,
|
|
486
|
+
borderWidth: 1,
|
|
487
|
+
borderColor: theme.colors.border,
|
|
488
|
+
borderRadius: 999,
|
|
489
|
+
backgroundColor: theme.colors.surface0,
|
|
490
|
+
},
|
|
491
|
+
choiceSelected: { borderColor: theme.colors.accent, backgroundColor: theme.colors.accent },
|
|
492
|
+
choiceText: { color: theme.colors.foreground, fontSize: 12, fontWeight: "600" },
|
|
493
|
+
choiceTextSelected: { color: theme.colors.accentForeground },
|
|
494
|
+
scopeNote: { color: theme.colors.foregroundMuted, fontSize: 12, lineHeight: 17 },
|
|
495
|
+
actions: { flexDirection: "row", flexWrap: "wrap", gap: 8 },
|
|
496
|
+
button: {
|
|
497
|
+
minHeight: 38,
|
|
498
|
+
alignItems: "center",
|
|
499
|
+
justifyContent: "center",
|
|
500
|
+
paddingHorizontal: 13,
|
|
501
|
+
paddingVertical: 8,
|
|
502
|
+
borderWidth: 1,
|
|
503
|
+
borderColor: theme.colors.border,
|
|
504
|
+
borderRadius: 8,
|
|
505
|
+
backgroundColor: theme.colors.surface2,
|
|
506
|
+
},
|
|
507
|
+
primaryButton: { borderColor: theme.colors.accent, backgroundColor: theme.colors.accent },
|
|
508
|
+
disabled: { opacity: 0.5 },
|
|
509
|
+
buttonText: { color: theme.colors.foreground, fontSize: 13, fontWeight: "600" },
|
|
510
|
+
primaryButtonText: { color: theme.colors.accentForeground },
|
|
511
|
+
pendingRow: { flexDirection: "row", alignItems: "center", gap: 8 },
|
|
512
|
+
activity: { color: theme.colors.accent },
|
|
513
|
+
error: { color: theme.colors.statusDanger, fontSize: 12, lineHeight: 17 },
|
|
514
|
+
doctorRows: { gap: 8 },
|
|
515
|
+
doctorRow: {
|
|
516
|
+
flexDirection: "row",
|
|
517
|
+
gap: 9,
|
|
518
|
+
padding: 10,
|
|
519
|
+
borderWidth: 1,
|
|
520
|
+
borderColor: theme.colors.border,
|
|
521
|
+
borderRadius: 8,
|
|
522
|
+
backgroundColor: theme.colors.surface0,
|
|
523
|
+
},
|
|
524
|
+
doctorMarker: { width: 4, minHeight: 32, borderRadius: 999 },
|
|
525
|
+
doctorMarkerOk: { backgroundColor: theme.colors.statusSuccess },
|
|
526
|
+
doctorMarkerWarning: { backgroundColor: theme.colors.statusWarning },
|
|
527
|
+
doctorMarkerError: { backgroundColor: theme.colors.statusDanger },
|
|
528
|
+
doctorCopy: { flex: 1, minWidth: 0, gap: 3 },
|
|
529
|
+
doctorTopline: {
|
|
530
|
+
flexDirection: compact ? "column" : "row",
|
|
531
|
+
alignItems: compact ? "flex-start" : "center",
|
|
532
|
+
gap: 6,
|
|
533
|
+
},
|
|
534
|
+
doctorCheck: {
|
|
535
|
+
flex: compact ? undefined : 1,
|
|
536
|
+
color: theme.colors.foreground,
|
|
537
|
+
fontWeight: "600",
|
|
538
|
+
},
|
|
539
|
+
doctorStatus: { fontSize: 11, fontWeight: "700" },
|
|
540
|
+
doctorStatusOk: { color: theme.colors.statusSuccess },
|
|
541
|
+
doctorStatusWarning: { color: theme.colors.statusWarning },
|
|
542
|
+
doctorStatusError: { color: theme.colors.statusDanger },
|
|
543
|
+
doctorDetail: { color: theme.colors.foregroundMuted, fontSize: 12, lineHeight: 17 },
|
|
544
|
+
rawFrame: {
|
|
545
|
+
gap: 6,
|
|
546
|
+
padding: 10,
|
|
547
|
+
borderWidth: 1,
|
|
548
|
+
borderColor: theme.colors.border,
|
|
549
|
+
borderRadius: 8,
|
|
550
|
+
backgroundColor: theme.colors.surface0,
|
|
551
|
+
},
|
|
552
|
+
rawOutput: {
|
|
553
|
+
color: theme.colors.foreground,
|
|
554
|
+
fontFamily: "monospace",
|
|
555
|
+
fontSize: compact ? 11 : 12,
|
|
556
|
+
lineHeight: compact ? 16 : 18,
|
|
557
|
+
},
|
|
558
|
+
commandCard: {
|
|
559
|
+
gap: 10,
|
|
560
|
+
padding: 11,
|
|
561
|
+
borderWidth: 1,
|
|
562
|
+
borderColor: theme.colors.border,
|
|
563
|
+
borderRadius: 8,
|
|
564
|
+
backgroundColor: theme.colors.surface0,
|
|
565
|
+
},
|
|
566
|
+
commandTitle: { color: theme.colors.foreground, fontSize: 14, fontWeight: "700" },
|
|
567
|
+
property: { gap: 4 },
|
|
568
|
+
propertyLabel: {
|
|
569
|
+
color: theme.colors.foregroundMuted,
|
|
570
|
+
fontSize: 11,
|
|
571
|
+
fontWeight: "600",
|
|
572
|
+
},
|
|
573
|
+
propertyValue: {
|
|
574
|
+
color: theme.colors.foreground,
|
|
575
|
+
fontFamily: "monospace",
|
|
576
|
+
fontSize: compact ? 11 : 12,
|
|
577
|
+
lineHeight: compact ? 16 : 18,
|
|
578
|
+
},
|
|
579
|
+
argumentFrame: { maxHeight: compact ? 160 : 220 },
|
|
580
|
+
argumentRow: {
|
|
581
|
+
flexDirection: "row",
|
|
582
|
+
alignItems: "flex-start",
|
|
583
|
+
gap: 8,
|
|
584
|
+
paddingVertical: 4,
|
|
585
|
+
borderBottomWidth: 1,
|
|
586
|
+
borderBottomColor: theme.colors.border,
|
|
587
|
+
},
|
|
588
|
+
argumentIndex: {
|
|
589
|
+
width: 22,
|
|
590
|
+
color: theme.colors.foregroundMuted,
|
|
591
|
+
fontFamily: "monospace",
|
|
592
|
+
fontSize: 11,
|
|
593
|
+
},
|
|
594
|
+
argumentValue: {
|
|
595
|
+
flex: 1,
|
|
596
|
+
color: theme.colors.foreground,
|
|
597
|
+
fontFamily: "monospace",
|
|
598
|
+
fontSize: compact ? 11 : 12,
|
|
599
|
+
lineHeight: compact ? 16 : 18,
|
|
600
|
+
},
|
|
601
|
+
metaRow: { flexDirection: "row", flexWrap: "wrap", alignItems: "center", gap: 7 },
|
|
602
|
+
badge: {
|
|
603
|
+
paddingHorizontal: 8,
|
|
604
|
+
paddingVertical: 4,
|
|
605
|
+
borderWidth: 1,
|
|
606
|
+
borderColor: theme.colors.border,
|
|
607
|
+
borderRadius: 999,
|
|
608
|
+
backgroundColor: theme.colors.surface2,
|
|
609
|
+
},
|
|
610
|
+
badgeText: { color: theme.colors.foreground, fontSize: 11, fontWeight: "600" },
|
|
611
|
+
warningBadge: {
|
|
612
|
+
paddingHorizontal: 8,
|
|
613
|
+
paddingVertical: 4,
|
|
614
|
+
borderWidth: 1,
|
|
615
|
+
borderColor: theme.colors.statusWarning,
|
|
616
|
+
borderRadius: 999,
|
|
617
|
+
backgroundColor: theme.colors.surface1,
|
|
618
|
+
},
|
|
619
|
+
warningBadgeText: { color: theme.colors.statusWarning, fontSize: 11, fontWeight: "600" },
|
|
620
|
+
};
|
|
621
|
+
}
|