@xpufx/paseo-twofado 0.1.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/README.md +62 -0
- package/client/approvals.tsx +1525 -0
- package/client/error-boundary.tsx +28 -0
- package/client/vendor/paseo-plugin-helper/command-center.ts +43 -0
- package/client/vendor/paseo-plugin-helper/components/AboutSection.tsx +493 -0
- package/client/vendor/paseo-plugin-helper/components/AttentionBeacon.tsx +250 -0
- package/client/vendor/paseo-plugin-helper/components/Badge.tsx +156 -0
- package/client/vendor/paseo-plugin-helper/components/Button.tsx +178 -0
- package/client/vendor/paseo-plugin-helper/components/Card.tsx +225 -0
- package/client/vendor/paseo-plugin-helper/components/CodeBlock.tsx +196 -0
- package/client/vendor/paseo-plugin-helper/components/Collapsible.tsx +277 -0
- package/client/vendor/paseo-plugin-helper/components/CommandBox.tsx +172 -0
- package/client/vendor/paseo-plugin-helper/components/CopyButton.tsx +180 -0
- package/client/vendor/paseo-plugin-helper/components/DataTable.tsx +200 -0
- package/client/vendor/paseo-plugin-helper/components/EmptyState.tsx +97 -0
- package/client/vendor/paseo-plugin-helper/components/HighlightedText.tsx +70 -0
- package/client/vendor/paseo-plugin-helper/components/InlineButton.tsx +73 -0
- package/client/vendor/paseo-plugin-helper/components/KeyValue.tsx +446 -0
- package/client/vendor/paseo-plugin-helper/components/MetricGauge.tsx +247 -0
- package/client/vendor/paseo-plugin-helper/components/ProgressBar.tsx +117 -0
- package/client/vendor/paseo-plugin-helper/components/Responsive.tsx +53 -0
- package/client/vendor/paseo-plugin-helper/components/SearchInput.tsx +118 -0
- package/client/vendor/paseo-plugin-helper/components/SectionHeader.tsx +80 -0
- package/client/vendor/paseo-plugin-helper/components/Select.tsx +215 -0
- package/client/vendor/paseo-plugin-helper/components/StatusDot.tsx +80 -0
- package/client/vendor/paseo-plugin-helper/components/Tabs.tsx +319 -0
- package/client/vendor/paseo-plugin-helper/components/TextInput.tsx +150 -0
- package/client/vendor/paseo-plugin-helper/components/Toggle.tsx +163 -0
- package/client/vendor/paseo-plugin-helper/components/TruncatedText.tsx +157 -0
- package/client/vendor/paseo-plugin-helper/components/index.ts +25 -0
- package/client/vendor/paseo-plugin-helper/custom-pills.tsx +224 -0
- package/client/vendor/paseo-plugin-helper/forge-icon.tsx +79 -0
- package/client/vendor/paseo-plugin-helper/host.ts +277 -0
- package/client/vendor/paseo-plugin-helper/icon.tsx +39 -0
- package/client/vendor/paseo-plugin-helper/index.ts +28 -0
- package/client/vendor/paseo-plugin-helper/layout/ActionBar.tsx +49 -0
- package/client/vendor/paseo-plugin-helper/layout/FormRow.tsx +103 -0
- package/client/vendor/paseo-plugin-helper/layout/Grid.tsx +65 -0
- package/client/vendor/paseo-plugin-helper/layout/ModalBody.tsx +378 -0
- package/client/vendor/paseo-plugin-helper/layout/ModalContent.tsx +49 -0
- package/client/vendor/paseo-plugin-helper/layout/Row.tsx +39 -0
- package/client/vendor/paseo-plugin-helper/layout/Stack.tsx +39 -0
- package/client/vendor/paseo-plugin-helper/layout/index.ts +7 -0
- package/client/vendor/paseo-plugin-helper/panel.tsx +81 -0
- package/client/vendor/paseo-plugin-helper/pill.tsx +884 -0
- package/client/vendor/paseo-plugin-helper/query-refresh.ts +79 -0
- package/client/vendor/paseo-plugin-helper/query.ts +66 -0
- package/client/vendor/paseo-plugin-helper/settings-screen.tsx +372 -0
- package/client/vendor/paseo-plugin-helper/settings.ts +181 -0
- package/client/vendor/paseo-plugin-helper/shared-settings.ts +46 -0
- package/client/vendor/paseo-plugin-helper/snapshot.ts +68 -0
- package/client/vendor/paseo-plugin-helper/surface.tsx +80 -0
- package/client/vendor/paseo-plugin-helper/theme/color-utils.ts +118 -0
- package/client/vendor/paseo-plugin-helper/theme/flair.ts +76 -0
- package/client/vendor/paseo-plugin-helper/theme/host-variables.ts +121 -0
- package/client/vendor/paseo-plugin-helper/theme/index.ts +7 -0
- package/client/vendor/paseo-plugin-helper/theme/provider.tsx +214 -0
- package/client/vendor/paseo-plugin-helper/theme/responsive.ts +213 -0
- package/client/vendor/paseo-plugin-helper/theme/tokens.ts +161 -0
- package/client/vendor/paseo-plugin-helper/theme/useResponsive.ts +57 -0
- package/client/vendor/paseo-plugin-helper/utils/clipboard.ts +149 -0
- package/client/vendor/paseo-plugin-helper/utils/haptics.ts +34 -0
- package/package.json +36 -0
- package/paseo-plugin.json +6 -0
- package/server/twofado.ts +536 -0
- package/server/vendor/paseo-plugin-helper/agent.ts +85 -0
- package/server/vendor/paseo-plugin-helper/custom-pills.ts +344 -0
- package/server/vendor/paseo-plugin-helper/index.ts +18 -0
- package/server/vendor/paseo-plugin-helper/jsonc.ts +78 -0
- package/server/vendor/paseo-plugin-helper/logger.ts +210 -0
- package/server/vendor/paseo-plugin-helper/mcp-config.ts +367 -0
- package/server/vendor/paseo-plugin-helper/mcp-injection.ts +85 -0
- package/server/vendor/paseo-plugin-helper/network.ts +91 -0
- package/server/vendor/paseo-plugin-helper/plugins.ts +160 -0
- package/server/vendor/paseo-plugin-helper/process.ts +186 -0
- package/server/vendor/paseo-plugin-helper/redact.ts +86 -0
- package/server/vendor/paseo-plugin-helper/rpc-guard.ts +77 -0
- package/server/vendor/paseo-plugin-helper/settings.ts +97 -0
- package/server/vendor/paseo-plugin-helper/shared-settings.ts +243 -0
- package/server/vendor/paseo-plugin-helper/storage.ts +244 -0
- package/server/vendor/paseo-plugin-helper/system.ts +128 -0
- package/server/vendor/paseo-plugin-helper/task.ts +116 -0
- package/server/vendor/paseo-plugin-helper/version.ts +153 -0
- package/server/vendor/paseo-plugin-helper/workspace-beacon.ts +418 -0
- package/shared/approval.ts +254 -0
- package/shared/vendor/paseo-plugin-helper/async.ts +35 -0
- package/shared/vendor/paseo-plugin-helper/custom-pills.ts +169 -0
- package/shared/vendor/paseo-plugin-helper/forge.ts +110 -0
- package/shared/vendor/paseo-plugin-helper/formatters.ts +271 -0
- package/shared/vendor/paseo-plugin-helper/highlight.ts +184 -0
- package/shared/vendor/paseo-plugin-helper/index.ts +10 -0
- package/shared/vendor/paseo-plugin-helper/rpc.ts +72 -0
- package/shared/vendor/paseo-plugin-helper/settings.ts +138 -0
- package/shared/vendor/paseo-plugin-helper/suite-settings.ts +17 -0
- package/shared/vendor/paseo-plugin-helper/suppressed.ts +31 -0
- package/shared/vendor/paseo-plugin-helper/types.ts +36 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Component, type ErrorInfo, type ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
interface ErrorBoundaryProps {
|
|
4
|
+
label: string;
|
|
5
|
+
fallback: ReactNode;
|
|
6
|
+
children: ReactNode;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface ErrorBoundaryState {
|
|
10
|
+
error?: Error;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
|
|
14
|
+
state: ErrorBoundaryState = {};
|
|
15
|
+
|
|
16
|
+
static getDerivedStateFromError(error: Error): ErrorBoundaryState {
|
|
17
|
+
return { error };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
componentDidCatch(error: Error, info: ErrorInfo) {
|
|
21
|
+
console.warn(`[2fado] ${this.props.label} render failed:`, error, info.componentStack);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
render() {
|
|
25
|
+
if (this.state.error) return this.props.fallback;
|
|
26
|
+
return this.props.children;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { PluginCleanup } from "./host";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Structural registrar interface satisfied by both Paseo v0.7 PluginContext
|
|
5
|
+
* and Paseo v0.8 PluginClientContext.
|
|
6
|
+
*/
|
|
7
|
+
export interface CommandCenterItemRegistrar {
|
|
8
|
+
addCommandCenterItem(contribution: any): any;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type CommandCenterContext = "global" | "workspace" | "agent";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Capabilities the host passes to a command-center item's `onSelect`. Mirrors
|
|
15
|
+
* the subset of `PluginCommandCapabilities` the helper needs; the real host
|
|
16
|
+
* context carries additional fields (paseo, rpc, workspace, agent) that a
|
|
17
|
+
* handler may read off its own typed contribution.
|
|
18
|
+
*/
|
|
19
|
+
export interface CommandCenterCapabilities {
|
|
20
|
+
openSurface(id: string): void;
|
|
21
|
+
openSettings(id: string): void;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface CommandCenterItemContribution {
|
|
25
|
+
id: string;
|
|
26
|
+
title: string;
|
|
27
|
+
icon: string;
|
|
28
|
+
keywords?: readonly string[];
|
|
29
|
+
context: CommandCenterContext;
|
|
30
|
+
onSelect(context: CommandCenterCapabilities): void | Promise<void>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Registers a command-center palette item — the entry the host Ctrl+K command
|
|
35
|
+
* center lists. Thin pass-through that keeps plugins on the helper seam and
|
|
36
|
+
* works with both Paseo v0.7 PluginContext and Paseo v0.8 PluginClientContext.
|
|
37
|
+
*/
|
|
38
|
+
export function registerCommandCenterItem(
|
|
39
|
+
plugin: CommandCenterItemRegistrar,
|
|
40
|
+
contribution: CommandCenterItemContribution,
|
|
41
|
+
): PluginCleanup {
|
|
42
|
+
return plugin.addCommandCenterItem(contribution);
|
|
43
|
+
}
|
|
@@ -0,0 +1,493 @@
|
|
|
1
|
+
import React, { useState, type ReactNode } from "react";
|
|
2
|
+
import {
|
|
3
|
+
Image,
|
|
4
|
+
Linking,
|
|
5
|
+
StyleSheet,
|
|
6
|
+
Text,
|
|
7
|
+
View,
|
|
8
|
+
type ImageSourcePropType,
|
|
9
|
+
type StyleProp,
|
|
10
|
+
type ViewStyle,
|
|
11
|
+
} from "react-native";
|
|
12
|
+
import { getClientHost } from "../host";
|
|
13
|
+
import { usePluginTheme } from "../theme/provider";
|
|
14
|
+
import { useResponsive } from "../theme/useResponsive";
|
|
15
|
+
import { Card } from "./Card";
|
|
16
|
+
import { Badge } from "./Badge";
|
|
17
|
+
import { Button } from "./Button";
|
|
18
|
+
import { KeyValue, KeyValueGroup } from "./KeyValue";
|
|
19
|
+
import { copyToClipboard } from "../utils/clipboard";
|
|
20
|
+
import { triggerHaptic } from "../utils/haptics";
|
|
21
|
+
|
|
22
|
+
export interface AboutLink {
|
|
23
|
+
label: string;
|
|
24
|
+
url: string;
|
|
25
|
+
icon?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface AboutSectionProps {
|
|
29
|
+
/**
|
|
30
|
+
* Name of the plugin.
|
|
31
|
+
*/
|
|
32
|
+
name: string;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Short description or tagline.
|
|
36
|
+
*/
|
|
37
|
+
description?: string;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Semantic version string (e.g. from `PLUGIN_VERSION` or package.json).
|
|
41
|
+
*/
|
|
42
|
+
version: string;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Author or organization name.
|
|
46
|
+
*/
|
|
47
|
+
author?: string;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Logo or icon to display.
|
|
51
|
+
* - A React Native image source object: `{ uri: "https://..." }` or `require("./assets/logo.png")`
|
|
52
|
+
* - A string starting with "http" (e.g. avatar/logo URL)
|
|
53
|
+
* - A Paseo Lucide icon name (e.g. "Cpu", "Layers", "Sparkles")
|
|
54
|
+
* - If omitted and `repository` or `author` is a GitHub link/username, defaults to the GitHub avatar!
|
|
55
|
+
*/
|
|
56
|
+
logo?: ImageSourcePropType | string;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Repository URL (e.g. "https://github.com/xpufx/paseo-top").
|
|
60
|
+
*/
|
|
61
|
+
repository?: string;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Issue tracker URL (e.g. "https://github.com/xpufx/paseo-top/issues").
|
|
65
|
+
*/
|
|
66
|
+
issues?: string;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Documentation website or wiki URL.
|
|
70
|
+
*/
|
|
71
|
+
homepage?: string;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* License identifier (e.g. "MIT", "Apache-2.0").
|
|
75
|
+
*/
|
|
76
|
+
license?: string;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Custom additional links.
|
|
80
|
+
*/
|
|
81
|
+
links?: AboutLink[];
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Extra diagnostic or environmental items to display in the details section.
|
|
85
|
+
*/
|
|
86
|
+
extraItems?: Array<{
|
|
87
|
+
label: string;
|
|
88
|
+
value: string;
|
|
89
|
+
subValue?: string;
|
|
90
|
+
copyable?: boolean;
|
|
91
|
+
}>;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Whether to display the "Copy Diagnostics" button. Default: true.
|
|
95
|
+
*/
|
|
96
|
+
showDiagnosticsCopy?: boolean;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Optional custom container style.
|
|
100
|
+
*/
|
|
101
|
+
style?: StyleProp<ViewStyle>;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Visual density. `"tiny"` scales all fonts to the smallest readable
|
|
105
|
+
* size for dense About pages. Default: `"default"`.
|
|
106
|
+
*/
|
|
107
|
+
density?: "default" | "compact" | "tiny";
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Extracts a GitHub owner/organization from a repository URL or author name.
|
|
112
|
+
*/
|
|
113
|
+
function resolveGitHubAvatarUrl(repo?: string, author?: string): string | null {
|
|
114
|
+
if (repo) {
|
|
115
|
+
const match = repo.match(/github\.com[/:]([a-zA-Z0-9_-]+)/);
|
|
116
|
+
if (match?.[1]) {
|
|
117
|
+
return `https://github.com/${match[1]}.png?size=128`;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (author && !author.includes(" ") && !author.includes("@")) {
|
|
121
|
+
return `https://github.com/${author}.png?size=128`;
|
|
122
|
+
}
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* `<AboutSection>` provides a standardized, responsive plugin information and diagnostics view.
|
|
128
|
+
*
|
|
129
|
+
* Features:
|
|
130
|
+
* - Displays plugin branding, author, description, version, and license badges.
|
|
131
|
+
* - Supports custom logo images, local asset requires, Lucide icons, or auto-resolved GitHub avatars.
|
|
132
|
+
* - One-click "Copy Diagnostics" button formatting system info for GitHub issue triage.
|
|
133
|
+
* - Pre-styled external links with native browser launch via React Native `Linking`.
|
|
134
|
+
*/
|
|
135
|
+
export function AboutSection({
|
|
136
|
+
name,
|
|
137
|
+
description,
|
|
138
|
+
version,
|
|
139
|
+
author,
|
|
140
|
+
logo,
|
|
141
|
+
repository,
|
|
142
|
+
issues,
|
|
143
|
+
homepage,
|
|
144
|
+
license = "MIT",
|
|
145
|
+
links = [],
|
|
146
|
+
extraItems = [],
|
|
147
|
+
showDiagnosticsCopy = true,
|
|
148
|
+
style,
|
|
149
|
+
density = "default",
|
|
150
|
+
}: AboutSectionProps) {
|
|
151
|
+
const { Icon } = getClientHost();
|
|
152
|
+
const { colors, resolveRadius, typography } = usePluginTheme();
|
|
153
|
+
const { isCompact, platform } = useResponsive();
|
|
154
|
+
const [copied, setCopied] = useState(false);
|
|
155
|
+
const isTiny = density === "tiny";
|
|
156
|
+
|
|
157
|
+
// 1. Resolve Logo / Avatar
|
|
158
|
+
let resolvedLogoNode: ReactNode = null;
|
|
159
|
+
const radius = resolveRadius("md");
|
|
160
|
+
|
|
161
|
+
if (logo) {
|
|
162
|
+
if (typeof logo === "string") {
|
|
163
|
+
if (logo.startsWith("http://") || logo.startsWith("https://")) {
|
|
164
|
+
resolvedLogoNode = (
|
|
165
|
+
<Image
|
|
166
|
+
source={{ uri: logo }}
|
|
167
|
+
style={[styles.logoImage, { borderRadius: radius }]}
|
|
168
|
+
/>
|
|
169
|
+
);
|
|
170
|
+
} else {
|
|
171
|
+
resolvedLogoNode = (
|
|
172
|
+
<View
|
|
173
|
+
style={[
|
|
174
|
+
styles.logoIconFallback,
|
|
175
|
+
{
|
|
176
|
+
backgroundColor: colors.surface2,
|
|
177
|
+
borderRadius: radius,
|
|
178
|
+
borderColor: colors.border,
|
|
179
|
+
},
|
|
180
|
+
]}
|
|
181
|
+
>
|
|
182
|
+
<Icon name={logo} size={26} color={colors.accent} />
|
|
183
|
+
</View>
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
} else {
|
|
187
|
+
resolvedLogoNode = (
|
|
188
|
+
<Image
|
|
189
|
+
source={logo}
|
|
190
|
+
style={[styles.logoImage, { borderRadius: radius }]}
|
|
191
|
+
/>
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
} else {
|
|
195
|
+
// Attempt automatic GitHub avatar resolution
|
|
196
|
+
const githubAvatar = resolveGitHubAvatarUrl(repository, author);
|
|
197
|
+
if (githubAvatar) {
|
|
198
|
+
resolvedLogoNode = (
|
|
199
|
+
<Image
|
|
200
|
+
source={{ uri: githubAvatar }}
|
|
201
|
+
style={[styles.logoImage, { borderRadius: radius }]}
|
|
202
|
+
/>
|
|
203
|
+
);
|
|
204
|
+
} else {
|
|
205
|
+
resolvedLogoNode = (
|
|
206
|
+
<View
|
|
207
|
+
style={[
|
|
208
|
+
styles.logoIconFallback,
|
|
209
|
+
{
|
|
210
|
+
backgroundColor: colors.surface2,
|
|
211
|
+
borderRadius: radius,
|
|
212
|
+
borderColor: colors.border,
|
|
213
|
+
},
|
|
214
|
+
]}
|
|
215
|
+
>
|
|
216
|
+
<Icon name="Layers" size={26} color={colors.accent} />
|
|
217
|
+
</View>
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// 2. Open URL helper
|
|
223
|
+
const handleOpenUrl = async (url: string) => {
|
|
224
|
+
try {
|
|
225
|
+
triggerHaptic("light");
|
|
226
|
+
const canOpen = await Linking.canOpenURL(url);
|
|
227
|
+
if (canOpen) {
|
|
228
|
+
await Linking.openURL(url);
|
|
229
|
+
}
|
|
230
|
+
} catch {
|
|
231
|
+
// Ignore linking errors
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
// 3. Diagnostics copy helper
|
|
236
|
+
const handleCopyDiagnostics = async () => {
|
|
237
|
+
triggerHaptic("success");
|
|
238
|
+
const lines = [
|
|
239
|
+
`Plugin: ${name} v${version}`,
|
|
240
|
+
author ? `Author: ${author}` : null,
|
|
241
|
+
`License: ${license}`,
|
|
242
|
+
`Platform: ${platform} (${isCompact ? "compact" : "regular"})`,
|
|
243
|
+
repository ? `Repository: ${repository}` : null,
|
|
244
|
+
...extraItems.map((item) => `${item.label}: ${item.value}`),
|
|
245
|
+
].filter(Boolean);
|
|
246
|
+
|
|
247
|
+
await copyToClipboard(lines.join("\n"));
|
|
248
|
+
setCopied(true);
|
|
249
|
+
setTimeout(() => setCopied(false), 2500);
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
// 4. Assemble external links
|
|
253
|
+
const allLinks: AboutLink[] = [
|
|
254
|
+
...(repository ? [{ label: "Repository", url: repository, icon: "ExternalLink" }] : []),
|
|
255
|
+
...(issues ? [{ label: "Report Issue", url: issues, icon: "Bug" }] : []),
|
|
256
|
+
...(homepage ? [{ label: "Documentation", url: homepage, icon: "BookOpen" }] : []),
|
|
257
|
+
...links,
|
|
258
|
+
];
|
|
259
|
+
|
|
260
|
+
return (
|
|
261
|
+
<View style={[styles.container, isTiny && tinyStyles.container, style]}>
|
|
262
|
+
{/* Header Banner Card */}
|
|
263
|
+
<Card variant="elevated" style={isTiny ? tinyStyles.card : undefined}>
|
|
264
|
+
<View style={[styles.headerRow, isTiny && tinyStyles.headerRow]}>
|
|
265
|
+
{resolvedLogoNode}
|
|
266
|
+
|
|
267
|
+
<View style={[styles.metaColumn, isTiny && tinyStyles.metaColumn]}>
|
|
268
|
+
<View style={styles.identityTitleRow}>
|
|
269
|
+
<Text
|
|
270
|
+
numberOfLines={2}
|
|
271
|
+
style={[
|
|
272
|
+
styles.identityTitle,
|
|
273
|
+
{
|
|
274
|
+
color: colors.foreground,
|
|
275
|
+
...typography.heading,
|
|
276
|
+
},
|
|
277
|
+
isTiny && tinyStyles.title,
|
|
278
|
+
]}
|
|
279
|
+
>
|
|
280
|
+
{name}
|
|
281
|
+
</Text>
|
|
282
|
+
<Badge
|
|
283
|
+
variant="accent"
|
|
284
|
+
label={`v${version}`}
|
|
285
|
+
textStyle={isTiny ? tinyStyles.text9 : undefined}
|
|
286
|
+
/>
|
|
287
|
+
</View>
|
|
288
|
+
|
|
289
|
+
{description ? (
|
|
290
|
+
<Text
|
|
291
|
+
numberOfLines={3}
|
|
292
|
+
style={[
|
|
293
|
+
styles.identityDescription,
|
|
294
|
+
{ color: colors.foregroundMuted, ...typography.body },
|
|
295
|
+
isTiny && tinyStyles.text9,
|
|
296
|
+
]}
|
|
297
|
+
>
|
|
298
|
+
{description}
|
|
299
|
+
</Text>
|
|
300
|
+
) : null}
|
|
301
|
+
|
|
302
|
+
{author ? (
|
|
303
|
+
<Text
|
|
304
|
+
style={[
|
|
305
|
+
styles.authorText,
|
|
306
|
+
{ color: colors.foregroundMuted, ...typography.caption },
|
|
307
|
+
isTiny && tinyStyles.text9,
|
|
308
|
+
]}
|
|
309
|
+
>
|
|
310
|
+
by {author}
|
|
311
|
+
</Text>
|
|
312
|
+
) : null}
|
|
313
|
+
|
|
314
|
+
{license ? (
|
|
315
|
+
<View style={styles.titleRow}>
|
|
316
|
+
<Badge
|
|
317
|
+
variant="neutral"
|
|
318
|
+
label={license}
|
|
319
|
+
textStyle={isTiny ? tinyStyles.text9 : undefined}
|
|
320
|
+
/>
|
|
321
|
+
</View>
|
|
322
|
+
) : null}
|
|
323
|
+
</View>
|
|
324
|
+
</View>
|
|
325
|
+
|
|
326
|
+
{/* Action Buttons: External Links & Diagnostics */}
|
|
327
|
+
<View style={[styles.actionsRow, isTiny && tinyStyles.actionsRow]}>
|
|
328
|
+
{allLinks.map((link) => (
|
|
329
|
+
<Button
|
|
330
|
+
key={link.url}
|
|
331
|
+
size="sm"
|
|
332
|
+
variant="secondary"
|
|
333
|
+
icon={link.icon ?? "ExternalLink"}
|
|
334
|
+
label={link.label}
|
|
335
|
+
onPress={() => handleOpenUrl(link.url)}
|
|
336
|
+
textStyle={isTiny ? tinyStyles.text9 : undefined}
|
|
337
|
+
/>
|
|
338
|
+
))}
|
|
339
|
+
|
|
340
|
+
{showDiagnosticsCopy && (
|
|
341
|
+
<Button
|
|
342
|
+
size="sm"
|
|
343
|
+
variant={copied ? "primary" : "ghost"}
|
|
344
|
+
icon={copied ? "Check" : "Copy"}
|
|
345
|
+
label={copied ? "Diagnostics Copied!" : "Copy Diagnostics"}
|
|
346
|
+
onPress={handleCopyDiagnostics}
|
|
347
|
+
textStyle={isTiny ? tinyStyles.text9 : undefined}
|
|
348
|
+
/>
|
|
349
|
+
)}
|
|
350
|
+
</View>
|
|
351
|
+
</Card>
|
|
352
|
+
|
|
353
|
+
{/* Environment & Extra Diagnostics Details */}
|
|
354
|
+
<Card variant="elevated" style={isTiny ? tinyStyles.card : undefined}>
|
|
355
|
+
<Card.Header
|
|
356
|
+
title="Runtime Environment"
|
|
357
|
+
subtitle="Diagnostics for issue reports and system verification"
|
|
358
|
+
titleStyle={isTiny ? tinyStyles.title : undefined}
|
|
359
|
+
subtitleStyle={isTiny ? tinyStyles.text9 : undefined}
|
|
360
|
+
/>
|
|
361
|
+
<KeyValueGroup columns={isCompact ? 1 : 2} gap={isTiny ? 6 : undefined}>
|
|
362
|
+
<KeyValue
|
|
363
|
+
label="Plugin Version"
|
|
364
|
+
value={`v${version}`}
|
|
365
|
+
copyable
|
|
366
|
+
labelStyle={isTiny ? tinyStyles.text9 : undefined}
|
|
367
|
+
valueStyle={isTiny ? tinyStyles.text9 : undefined}
|
|
368
|
+
/>
|
|
369
|
+
<KeyValue
|
|
370
|
+
label="Client Platform"
|
|
371
|
+
value={platform}
|
|
372
|
+
labelStyle={isTiny ? tinyStyles.text9 : undefined}
|
|
373
|
+
valueStyle={isTiny ? tinyStyles.text9 : undefined}
|
|
374
|
+
/>
|
|
375
|
+
{author ? (
|
|
376
|
+
<KeyValue
|
|
377
|
+
label="Author"
|
|
378
|
+
value={author}
|
|
379
|
+
labelStyle={isTiny ? tinyStyles.text9 : undefined}
|
|
380
|
+
valueStyle={isTiny ? tinyStyles.text9 : undefined}
|
|
381
|
+
/>
|
|
382
|
+
) : null}
|
|
383
|
+
{license ? (
|
|
384
|
+
<KeyValue
|
|
385
|
+
label="License"
|
|
386
|
+
value={license}
|
|
387
|
+
labelStyle={isTiny ? tinyStyles.text9 : undefined}
|
|
388
|
+
valueStyle={isTiny ? tinyStyles.text9 : undefined}
|
|
389
|
+
/>
|
|
390
|
+
) : null}
|
|
391
|
+
{extraItems.map((item, idx) => (
|
|
392
|
+
<KeyValue
|
|
393
|
+
key={idx}
|
|
394
|
+
label={item.label}
|
|
395
|
+
value={item.value}
|
|
396
|
+
subValue={item.subValue}
|
|
397
|
+
copyable={item.copyable}
|
|
398
|
+
labelStyle={isTiny ? tinyStyles.text9 : undefined}
|
|
399
|
+
valueStyle={isTiny ? tinyStyles.text9 : undefined}
|
|
400
|
+
/>
|
|
401
|
+
))}
|
|
402
|
+
</KeyValueGroup>
|
|
403
|
+
</Card>
|
|
404
|
+
</View>
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
const styles = StyleSheet.create({
|
|
409
|
+
container: {
|
|
410
|
+
gap: 12,
|
|
411
|
+
},
|
|
412
|
+
headerRow: {
|
|
413
|
+
flexDirection: "row",
|
|
414
|
+
alignItems: "center",
|
|
415
|
+
gap: 14,
|
|
416
|
+
},
|
|
417
|
+
logoImage: {
|
|
418
|
+
width: 48,
|
|
419
|
+
height: 48,
|
|
420
|
+
resizeMode: "cover",
|
|
421
|
+
},
|
|
422
|
+
logoIconFallback: {
|
|
423
|
+
width: 48,
|
|
424
|
+
height: 48,
|
|
425
|
+
alignItems: "center",
|
|
426
|
+
justifyContent: "center",
|
|
427
|
+
borderWidth: 1,
|
|
428
|
+
},
|
|
429
|
+
metaColumn: {
|
|
430
|
+
flex: 1,
|
|
431
|
+
gap: 5,
|
|
432
|
+
minWidth: 0,
|
|
433
|
+
},
|
|
434
|
+
identityTitleRow: {
|
|
435
|
+
flexDirection: "row",
|
|
436
|
+
alignItems: "center",
|
|
437
|
+
flexWrap: "wrap",
|
|
438
|
+
gap: 6,
|
|
439
|
+
},
|
|
440
|
+
identityTitle: {
|
|
441
|
+
flexShrink: 1,
|
|
442
|
+
},
|
|
443
|
+
identityDescription: {
|
|
444
|
+
flexShrink: 1,
|
|
445
|
+
},
|
|
446
|
+
titleRow: {
|
|
447
|
+
flexDirection: "row",
|
|
448
|
+
alignItems: "center",
|
|
449
|
+
flexWrap: "wrap",
|
|
450
|
+
gap: 6,
|
|
451
|
+
},
|
|
452
|
+
authorText: {
|
|
453
|
+
flexShrink: 1,
|
|
454
|
+
},
|
|
455
|
+
actionsRow: {
|
|
456
|
+
flexDirection: "row",
|
|
457
|
+
alignItems: "center",
|
|
458
|
+
flexWrap: "wrap",
|
|
459
|
+
gap: 8,
|
|
460
|
+
marginTop: 12,
|
|
461
|
+
paddingTop: 10,
|
|
462
|
+
borderTopWidth: StyleSheet.hairlineWidth,
|
|
463
|
+
borderTopColor: "rgba(128, 128, 128, 0.2)",
|
|
464
|
+
},
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
const tinyStyles = StyleSheet.create({
|
|
468
|
+
container: {
|
|
469
|
+
gap: 6,
|
|
470
|
+
},
|
|
471
|
+
card: {
|
|
472
|
+
paddingHorizontal: 8,
|
|
473
|
+
paddingVertical: 8,
|
|
474
|
+
gap: 6,
|
|
475
|
+
},
|
|
476
|
+
title: {
|
|
477
|
+
fontSize: 11,
|
|
478
|
+
},
|
|
479
|
+
text9: {
|
|
480
|
+
fontSize: 9,
|
|
481
|
+
},
|
|
482
|
+
headerRow: {
|
|
483
|
+
gap: 8,
|
|
484
|
+
},
|
|
485
|
+
metaColumn: {
|
|
486
|
+
gap: 4,
|
|
487
|
+
},
|
|
488
|
+
actionsRow: {
|
|
489
|
+
gap: 6,
|
|
490
|
+
marginTop: 8,
|
|
491
|
+
paddingTop: 6,
|
|
492
|
+
},
|
|
493
|
+
});
|