@xpufx/paseo-top 0.4.3 → 0.4.5
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/OVERVIEW.md +17 -0
- package/client/host-ui.tsx +184 -0
- package/client/multi-host-view.tsx +57 -43
- package/client/multi-host.ts +36 -13
- package/client/pill-labels.ts +124 -7
- package/client/pill.tsx +724 -669
- package/client/resources-query.ts +1 -1
- package/client/settings-events.ts +33 -0
- package/client/settings-ui.tsx +86 -9
- package/client/surface.tsx +310 -274
- package/client/telemetry-copy.ts +8 -2
- package/client/telemetry.tsx +372 -321
- package/client/turn-panel.tsx +126 -104
- package/client/vendor/paseo-plugin-helper/{host.ts → core/host.ts} +21 -61
- package/client/vendor/paseo-plugin-helper/core/index.ts +76 -0
- package/client/vendor/paseo-plugin-helper/core/plugin-load-count.ts +89 -0
- package/client/vendor/paseo-plugin-helper/{query-refresh.ts → core/query-refresh.ts} +2 -2
- package/client/vendor/paseo-plugin-helper/{query.ts → core/query.ts} +2 -2
- package/client/vendor/paseo-plugin-helper/{settings.ts → core/settings.ts} +1 -1
- package/client/vendor/paseo-plugin-helper/{shared-settings.ts → core/shared-settings.ts} +2 -2
- package/client/vendor/paseo-plugin-helper/{utils → lifecycle}/clipboard.ts +2 -2
- package/client/vendor/paseo-plugin-helper/lifecycle/flair.ts +26 -0
- package/client/vendor/paseo-plugin-helper/{theme/color-utils.ts → lifecycle/host-color.ts} +95 -38
- package/client/vendor/paseo-plugin-helper/lifecycle/host-theme.tsx +109 -0
- package/client/vendor/paseo-plugin-helper/lifecycle/index.ts +23 -0
- package/client/vendor/paseo-plugin-helper/lifecycle/multi-host.ts +66 -0
- package/client/vendor/paseo-plugin-helper/{panel.tsx → lifecycle/panel.tsx} +7 -7
- package/client/vendor/paseo-plugin-helper/{pill.tsx → lifecycle/pill.tsx} +118 -338
- package/client/vendor/paseo-plugin-helper/lifecycle/providers.tsx +46 -0
- package/client/vendor/paseo-plugin-helper/lifecycle/scroll-owner.ts +26 -0
- package/client/vendor/paseo-plugin-helper/lifecycle/surface.tsx +140 -0
- package/client/vendor/paseo-plugin-helper/ui/color.ts +1 -0
- package/client/vendor/paseo-plugin-helper/ui/content.tsx +1326 -0
- package/client/vendor/paseo-plugin-helper/ui/controls.tsx +1018 -0
- package/client/vendor/paseo-plugin-helper/{components/DataTable.tsx → ui/data.tsx} +55 -47
- package/client/vendor/paseo-plugin-helper/ui/index.ts +44 -0
- package/client/vendor/paseo-plugin-helper/ui/layout.tsx +263 -0
- package/client/vendor/paseo-plugin-helper/ui/modal.tsx +111 -0
- package/client/vendor/paseo-plugin-helper/ui/plugin-reload-banner.tsx +119 -0
- package/client/vendor/paseo-plugin-helper/{settings-screen.tsx → ui/settings-screen.tsx} +3 -3
- package/client/vendor/paseo-plugin-helper/ui/settings.ts +36 -0
- package/client/vendor/paseo-plugin-helper/ui/text.tsx +821 -0
- package/client/vendor/paseo-plugin-helper/ui/theme.tsx +1 -0
- package/client/vendor/permission-audit/client.tsx +706 -0
- package/client/vendor/permission-audit/index.tsx +1 -0
- package/index.client.tsx +58 -37
- package/index.server.ts +256 -227
- package/package.json +14 -2
- package/paseo-plugin.json +2 -0
- package/screenshots/1-paseo-top-modal-system.jpg +0 -0
- package/screenshots/2-paseo-top-modal-workspace.jpg +0 -0
- package/screenshots/3-paseo-top-modal-settings.jpg +0 -0
- package/screenshots/4-paseo-top-modal-about.jpg +0 -0
- package/screenshots/paseo-top-dashboard.jpg +0 -0
- package/screenshots/paseo-top-general-view.jpg +0 -0
- package/server/resources.ts +98 -0
- package/server/vendor/paseo-plugin-helper/custom-pills.ts +70 -0
- package/server/vendor/paseo-plugin-helper/index.ts +4 -0
- package/server/vendor/paseo-plugin-helper/logger.ts +5 -16
- package/server/vendor/paseo-plugin-helper/mcp-injection.ts +20 -2
- package/server/vendor/paseo-plugin-helper/plugins.ts +184 -19
- package/server/vendor/paseo-plugin-helper/rpc-guard.ts +2 -2
- package/server/vendor/paseo-plugin-helper/storage-guard.ts +294 -0
- package/server/vendor/paseo-plugin-helper/storage.ts +36 -2
- package/server/vendor/paseo-plugin-helper/telemetry.ts +486 -0
- package/server/vendor/paseo-plugin-helper/tickets.ts +1232 -0
- package/server/vendor/permission-audit/index.ts +1 -0
- package/server/vendor/permission-audit/server.ts +839 -0
- package/shared/helper-version.ts +20 -0
- package/shared/multi-host.ts +156 -155
- package/shared/resources.ts +146 -1
- package/shared/vendor/paseo-plugin-helper/async.ts +14 -7
- package/shared/vendor/paseo-plugin-helper/features.ts +244 -0
- package/shared/vendor/paseo-plugin-helper/forge.ts +27 -0
- package/shared/vendor/paseo-plugin-helper/index.ts +2 -0
- package/shared/vendor/paseo-plugin-helper/tickets.ts +1104 -0
- package/shared/vendor/permission-audit/filter.ts +61 -0
- package/shared/vendor/permission-audit/index.ts +2 -0
- package/shared/vendor/permission-audit/shared.ts +119 -0
- package/shared/version.ts +1 -1
- package/client/vendor/paseo-plugin-helper/components/AboutSection.tsx +0 -493
- package/client/vendor/paseo-plugin-helper/components/AttentionBeacon.tsx +0 -250
- package/client/vendor/paseo-plugin-helper/components/Badge.tsx +0 -156
- package/client/vendor/paseo-plugin-helper/components/Button.tsx +0 -184
- package/client/vendor/paseo-plugin-helper/components/Card.tsx +0 -225
- package/client/vendor/paseo-plugin-helper/components/CodeBlock.tsx +0 -196
- package/client/vendor/paseo-plugin-helper/components/Collapsible.tsx +0 -277
- package/client/vendor/paseo-plugin-helper/components/CommandBox.tsx +0 -172
- package/client/vendor/paseo-plugin-helper/components/CopyButton.tsx +0 -180
- package/client/vendor/paseo-plugin-helper/components/EmptyState.tsx +0 -97
- package/client/vendor/paseo-plugin-helper/components/HighlightedText.tsx +0 -70
- package/client/vendor/paseo-plugin-helper/components/InlineButton.tsx +0 -75
- package/client/vendor/paseo-plugin-helper/components/KeyValue.tsx +0 -446
- package/client/vendor/paseo-plugin-helper/components/MetricGauge.tsx +0 -247
- package/client/vendor/paseo-plugin-helper/components/ProgressBar.tsx +0 -117
- package/client/vendor/paseo-plugin-helper/components/Responsive.tsx +0 -53
- package/client/vendor/paseo-plugin-helper/components/SearchInput.tsx +0 -118
- package/client/vendor/paseo-plugin-helper/components/SectionHeader.tsx +0 -80
- package/client/vendor/paseo-plugin-helper/components/Select.tsx +0 -215
- package/client/vendor/paseo-plugin-helper/components/StatusDot.tsx +0 -80
- package/client/vendor/paseo-plugin-helper/components/Tabs.tsx +0 -319
- package/client/vendor/paseo-plugin-helper/components/TextInput.tsx +0 -150
- package/client/vendor/paseo-plugin-helper/components/Toggle.tsx +0 -163
- package/client/vendor/paseo-plugin-helper/components/TruncatedText.tsx +0 -157
- package/client/vendor/paseo-plugin-helper/components/index.ts +0 -25
- package/client/vendor/paseo-plugin-helper/custom-pills.tsx +0 -224
- package/client/vendor/paseo-plugin-helper/forge-icon.tsx +0 -79
- package/client/vendor/paseo-plugin-helper/icon.tsx +0 -39
- package/client/vendor/paseo-plugin-helper/index.ts +0 -29
- package/client/vendor/paseo-plugin-helper/layout/ActionBar.tsx +0 -49
- package/client/vendor/paseo-plugin-helper/layout/FormRow.tsx +0 -103
- package/client/vendor/paseo-plugin-helper/layout/Grid.tsx +0 -65
- package/client/vendor/paseo-plugin-helper/layout/ModalBody.tsx +0 -378
- package/client/vendor/paseo-plugin-helper/layout/ModalContent.tsx +0 -51
- package/client/vendor/paseo-plugin-helper/layout/Row.tsx +0 -39
- package/client/vendor/paseo-plugin-helper/layout/Stack.tsx +0 -39
- package/client/vendor/paseo-plugin-helper/layout/index.ts +0 -7
- package/client/vendor/paseo-plugin-helper/styles/index.ts +0 -1
- package/client/vendor/paseo-plugin-helper/styles/recipes.ts +0 -550
- package/client/vendor/paseo-plugin-helper/surface.tsx +0 -80
- package/client/vendor/paseo-plugin-helper/theme/flair.ts +0 -76
- package/client/vendor/paseo-plugin-helper/theme/host-variables.ts +0 -121
- package/client/vendor/paseo-plugin-helper/theme/index.ts +0 -7
- package/client/vendor/paseo-plugin-helper/theme/provider.tsx +0 -178
- package/client/vendor/paseo-plugin-helper/theme/responsive.ts +0 -213
- package/client/vendor/paseo-plugin-helper/theme/tokens.ts +0 -200
- package/client/vendor/paseo-plugin-helper/theme/useResponsive.ts +0 -57
- /package/client/vendor/paseo-plugin-helper/{command-center.ts → core/command-center.ts} +0 -0
- /package/client/vendor/paseo-plugin-helper/{snapshot.ts → core/snapshot.ts} +0 -0
- /package/client/vendor/paseo-plugin-helper/{utils → lifecycle}/haptics.ts +0 -0
|
@@ -1,550 +0,0 @@
|
|
|
1
|
-
import type { TextStyle, ViewStyle } from "react-native";
|
|
2
|
-
import type { PluginTheme, StatusVariant, ThemeColors } from "../../../../shared/vendor/paseo-plugin-helper/types";
|
|
3
|
-
import { alpha, getStatusColor, getVariantPalette, getContrastColor } from "../theme/color-utils";
|
|
4
|
-
import { defaultDarkTheme, FALLBACK_ACCENT_FOREGROUND } from "../theme/tokens";
|
|
5
|
-
import type { PluginThemeContextValue } from "../theme/provider";
|
|
6
|
-
import type { SurfaceStyle } from "../theme/flair";
|
|
7
|
-
import type { ButtonSize, ButtonVariant } from "../components/Button";
|
|
8
|
-
import type { BadgeSize, BadgeStyle } from "../components/Badge";
|
|
9
|
-
|
|
10
|
-
export type ThemeInput =
|
|
11
|
-
| PluginTheme
|
|
12
|
-
| PluginThemeContextValue
|
|
13
|
-
| ThemeColors
|
|
14
|
-
| { colors: ThemeColors }
|
|
15
|
-
| undefined;
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Resolves ThemeColors from a PluginTheme, PluginThemeContextValue, ThemeColors, or undefined.
|
|
19
|
-
*/
|
|
20
|
-
export function resolveThemeColors(themeInput?: ThemeInput): ThemeColors {
|
|
21
|
-
if (!themeInput || typeof themeInput !== "object") return defaultDarkTheme.colors;
|
|
22
|
-
if ("colors" in themeInput && themeInput.colors && typeof themeInput.colors === "object") {
|
|
23
|
-
return { ...defaultDarkTheme.colors, ...themeInput.colors };
|
|
24
|
-
}
|
|
25
|
-
if ("surface0" in themeInput && typeof (themeInput as ThemeColors).surface0 === "string") {
|
|
26
|
-
return { ...defaultDarkTheme.colors, ...(themeInput as ThemeColors) };
|
|
27
|
-
}
|
|
28
|
-
return defaultDarkTheme.colors;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// ---------------------------------------------------------------------------
|
|
32
|
-
// 1. INPUT RECIPE
|
|
33
|
-
// ---------------------------------------------------------------------------
|
|
34
|
-
|
|
35
|
-
export interface InputRecipeOptions {
|
|
36
|
-
focused?: boolean;
|
|
37
|
-
isFocused?: boolean;
|
|
38
|
-
error?: boolean;
|
|
39
|
-
hasError?: boolean;
|
|
40
|
-
disabled?: boolean;
|
|
41
|
-
multiline?: boolean;
|
|
42
|
-
mono?: boolean;
|
|
43
|
-
compact?: boolean;
|
|
44
|
-
size?: "sm" | "md" | "lg";
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export interface InputRecipeResult extends TextStyle, ViewStyle {
|
|
48
|
-
input: TextStyle & ViewStyle;
|
|
49
|
-
container: ViewStyle;
|
|
50
|
-
label: TextStyle;
|
|
51
|
-
hint: TextStyle;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Generates standard React Native styles for text inputs and surrounding container/label/hint elements.
|
|
56
|
-
*/
|
|
57
|
-
export function inputRecipe(
|
|
58
|
-
themeInput?: ThemeInput,
|
|
59
|
-
options?: InputRecipeOptions,
|
|
60
|
-
): InputRecipeResult {
|
|
61
|
-
const colors = resolveThemeColors(themeInput);
|
|
62
|
-
const isFocused = Boolean(options?.focused ?? options?.isFocused);
|
|
63
|
-
const hasError = Boolean(options?.error ?? options?.hasError);
|
|
64
|
-
const disabled = Boolean(options?.disabled);
|
|
65
|
-
const multiline = Boolean(options?.multiline);
|
|
66
|
-
const mono = Boolean(options?.mono);
|
|
67
|
-
const compact = Boolean(options?.compact);
|
|
68
|
-
|
|
69
|
-
const borderColor = hasError
|
|
70
|
-
? colors.statusDanger
|
|
71
|
-
: isFocused
|
|
72
|
-
? colors.accent
|
|
73
|
-
: colors.border;
|
|
74
|
-
|
|
75
|
-
const minHeight = multiline
|
|
76
|
-
? 64
|
|
77
|
-
: options?.size === "sm"
|
|
78
|
-
? 30
|
|
79
|
-
: options?.size === "lg"
|
|
80
|
-
? 42
|
|
81
|
-
: compact
|
|
82
|
-
? 32
|
|
83
|
-
: 36;
|
|
84
|
-
const fontSize =
|
|
85
|
-
options?.size === "sm" ? 12 : options?.size === "lg" ? 15 : compact ? 13 : 14;
|
|
86
|
-
const paddingVertical = multiline
|
|
87
|
-
? 8
|
|
88
|
-
: options?.size === "sm"
|
|
89
|
-
? 4
|
|
90
|
-
: options?.size === "lg"
|
|
91
|
-
? 8
|
|
92
|
-
: 6;
|
|
93
|
-
const paddingHorizontal =
|
|
94
|
-
options?.size === "sm" ? 8 : options?.size === "lg" ? 12 : 10;
|
|
95
|
-
|
|
96
|
-
const baseInput: TextStyle & ViewStyle = {
|
|
97
|
-
backgroundColor: disabled ? alpha(colors.surface1, 0.5) : colors.surface0,
|
|
98
|
-
borderColor,
|
|
99
|
-
borderWidth: 1,
|
|
100
|
-
borderRadius: 8,
|
|
101
|
-
color: disabled ? colors.foregroundMuted : colors.foreground,
|
|
102
|
-
fontSize,
|
|
103
|
-
fontFamily: mono ? "monospace" : undefined,
|
|
104
|
-
minHeight,
|
|
105
|
-
paddingHorizontal,
|
|
106
|
-
paddingVertical,
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
const container: ViewStyle = {
|
|
110
|
-
gap: 4,
|
|
111
|
-
width: "100%",
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
const label: TextStyle = {
|
|
115
|
-
color: hasError ? colors.statusDanger : colors.foreground,
|
|
116
|
-
fontSize: compact ? 12 : 13,
|
|
117
|
-
fontWeight: "600",
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
const hint: TextStyle = {
|
|
121
|
-
color: hasError ? colors.statusDanger : colors.foregroundMuted,
|
|
122
|
-
fontSize: 11,
|
|
123
|
-
marginTop: 2,
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
const input = { ...baseInput };
|
|
127
|
-
return Object.assign(baseInput, {
|
|
128
|
-
input,
|
|
129
|
-
container,
|
|
130
|
-
label,
|
|
131
|
-
hint,
|
|
132
|
-
placeholderColor: colors.foregroundMuted,
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// ---------------------------------------------------------------------------
|
|
137
|
-
// 2. CARD RECIPE
|
|
138
|
-
// ---------------------------------------------------------------------------
|
|
139
|
-
|
|
140
|
-
export interface CardRecipeOptions {
|
|
141
|
-
variant?: SurfaceStyle;
|
|
142
|
-
noPadding?: boolean;
|
|
143
|
-
radius?: number;
|
|
144
|
-
borderWidth?: number;
|
|
145
|
-
compact?: boolean;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
export interface CardRecipeResult extends ViewStyle {
|
|
149
|
-
card: ViewStyle;
|
|
150
|
-
header: ViewStyle;
|
|
151
|
-
headerTitle: TextStyle;
|
|
152
|
-
headerSubtitle: TextStyle;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Generates standard React Native styles for cards and card headers.
|
|
157
|
-
*/
|
|
158
|
-
export function cardRecipe(
|
|
159
|
-
themeInput?: ThemeInput,
|
|
160
|
-
optionsOrVariant?: CardRecipeOptions | SurfaceStyle,
|
|
161
|
-
): CardRecipeResult {
|
|
162
|
-
const colors = resolveThemeColors(themeInput);
|
|
163
|
-
const options: CardRecipeOptions =
|
|
164
|
-
typeof optionsOrVariant === "string" ? { variant: optionsOrVariant } : (optionsOrVariant ?? {});
|
|
165
|
-
|
|
166
|
-
const variant = options.variant ?? "flat";
|
|
167
|
-
const noPadding = Boolean(options.noPadding);
|
|
168
|
-
const compact = Boolean(options.compact);
|
|
169
|
-
const radius = options.radius ?? (compact ? 6 : 8);
|
|
170
|
-
const borderWidth = options.borderWidth ?? 1;
|
|
171
|
-
|
|
172
|
-
let bg = colors.surface0;
|
|
173
|
-
let border = colors.border;
|
|
174
|
-
|
|
175
|
-
if (variant === "tinted") {
|
|
176
|
-
bg = alpha(colors.accent, 0.04);
|
|
177
|
-
border = alpha(colors.accent, 0.2);
|
|
178
|
-
} else if (variant === "elevated") {
|
|
179
|
-
bg = colors.surface1;
|
|
180
|
-
border = colors.border;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
const baseCard: ViewStyle = {
|
|
184
|
-
backgroundColor: bg,
|
|
185
|
-
borderColor: border,
|
|
186
|
-
borderWidth,
|
|
187
|
-
borderRadius: radius,
|
|
188
|
-
paddingHorizontal: noPadding ? 0 : compact ? 8 : 12,
|
|
189
|
-
paddingVertical: noPadding ? 0 : compact ? 6 : 10,
|
|
190
|
-
width: "100%",
|
|
191
|
-
overflow: "hidden",
|
|
192
|
-
};
|
|
193
|
-
|
|
194
|
-
const header: ViewStyle = {
|
|
195
|
-
flexDirection: "row",
|
|
196
|
-
alignItems: "center",
|
|
197
|
-
justifyContent: "space-between",
|
|
198
|
-
flexWrap: "wrap",
|
|
199
|
-
gap: compact ? 4 : 8,
|
|
200
|
-
width: "100%",
|
|
201
|
-
marginBottom: compact ? 4 : 8,
|
|
202
|
-
};
|
|
203
|
-
|
|
204
|
-
const headerTitle: TextStyle = {
|
|
205
|
-
color: colors.foreground,
|
|
206
|
-
fontSize: compact ? 13 : 14,
|
|
207
|
-
fontWeight: "600",
|
|
208
|
-
lineHeight: compact ? 18 : 20,
|
|
209
|
-
};
|
|
210
|
-
|
|
211
|
-
const headerSubtitle: TextStyle = {
|
|
212
|
-
color: colors.foregroundMuted,
|
|
213
|
-
fontSize: compact ? 10 : 11,
|
|
214
|
-
lineHeight: compact ? 14 : 15,
|
|
215
|
-
fontWeight: "400",
|
|
216
|
-
};
|
|
217
|
-
|
|
218
|
-
const card = { ...baseCard };
|
|
219
|
-
return Object.assign(baseCard, {
|
|
220
|
-
card,
|
|
221
|
-
header,
|
|
222
|
-
headerTitle,
|
|
223
|
-
headerSubtitle,
|
|
224
|
-
});
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// ---------------------------------------------------------------------------
|
|
228
|
-
// 3. BUTTON RECIPE
|
|
229
|
-
// ---------------------------------------------------------------------------
|
|
230
|
-
|
|
231
|
-
export interface ButtonRecipeOptions {
|
|
232
|
-
variant?: ButtonVariant;
|
|
233
|
-
size?: ButtonSize;
|
|
234
|
-
pressed?: boolean;
|
|
235
|
-
disabled?: boolean;
|
|
236
|
-
loading?: boolean;
|
|
237
|
-
compact?: boolean;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
export interface ButtonRecipeResult extends ViewStyle {
|
|
241
|
-
container: ViewStyle;
|
|
242
|
-
text: TextStyle;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
/**
|
|
246
|
-
* Generates standard React Native styles for interactive buttons and text labels.
|
|
247
|
-
*/
|
|
248
|
-
export function buttonRecipe(
|
|
249
|
-
themeInput?: ThemeInput,
|
|
250
|
-
optionsOrVariant?: ButtonRecipeOptions | ButtonVariant,
|
|
251
|
-
): ButtonRecipeResult {
|
|
252
|
-
const colors = resolveThemeColors(themeInput);
|
|
253
|
-
const options: ButtonRecipeOptions =
|
|
254
|
-
typeof optionsOrVariant === "string" ? { variant: optionsOrVariant } : (optionsOrVariant ?? {});
|
|
255
|
-
|
|
256
|
-
const variant = options.variant ?? "secondary";
|
|
257
|
-
const size = options.size ?? "md";
|
|
258
|
-
const pressed = Boolean(options.pressed);
|
|
259
|
-
const disabled = Boolean(options.disabled || options.loading);
|
|
260
|
-
const compact = Boolean(options.compact);
|
|
261
|
-
|
|
262
|
-
const radius = size === "sm" ? 6 : size === "lg" ? 10 : 8;
|
|
263
|
-
const py = size === "sm" ? (compact ? 5 : 6) : size === "lg" ? 12 : compact ? 8 : 10;
|
|
264
|
-
const px = size === "sm" ? (compact ? 8 : 10) : size === "lg" ? 18 : compact ? 12 : 14;
|
|
265
|
-
const fontSize = size === "sm" ? 12 : size === "lg" ? 15 : 13;
|
|
266
|
-
|
|
267
|
-
let bg = "transparent";
|
|
268
|
-
let border = "transparent";
|
|
269
|
-
let textColor = colors.foreground;
|
|
270
|
-
|
|
271
|
-
switch (variant) {
|
|
272
|
-
case "primary":
|
|
273
|
-
bg = colors.accent;
|
|
274
|
-
textColor = colors.accentForeground || FALLBACK_ACCENT_FOREGROUND;
|
|
275
|
-
break;
|
|
276
|
-
case "danger":
|
|
277
|
-
bg = alpha(colors.statusDanger, 0.15);
|
|
278
|
-
border = alpha(colors.statusDanger, 0.4);
|
|
279
|
-
textColor = colors.statusDanger;
|
|
280
|
-
break;
|
|
281
|
-
case "ghost":
|
|
282
|
-
bg = "transparent";
|
|
283
|
-
textColor = colors.foregroundMuted;
|
|
284
|
-
break;
|
|
285
|
-
case "secondary":
|
|
286
|
-
default:
|
|
287
|
-
bg = colors.surface1;
|
|
288
|
-
border = colors.border;
|
|
289
|
-
textColor = colors.foreground;
|
|
290
|
-
break;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
const effectiveBg = pressed && !disabled ? alpha(bg === "transparent" ? colors.surface2 : bg, 0.8) : bg;
|
|
294
|
-
|
|
295
|
-
const container: ViewStyle = {
|
|
296
|
-
flexDirection: "row",
|
|
297
|
-
alignItems: "center",
|
|
298
|
-
justifyContent: "center",
|
|
299
|
-
gap: 6,
|
|
300
|
-
backgroundColor: effectiveBg,
|
|
301
|
-
borderColor: border,
|
|
302
|
-
borderWidth: border !== "transparent" ? 1 : 0,
|
|
303
|
-
borderRadius: radius,
|
|
304
|
-
paddingVertical: py,
|
|
305
|
-
paddingHorizontal: px,
|
|
306
|
-
minHeight: size === "sm" ? 28 : size === "lg" ? 42 : 34,
|
|
307
|
-
opacity: disabled ? 0.45 : 1,
|
|
308
|
-
};
|
|
309
|
-
|
|
310
|
-
const text: TextStyle = {
|
|
311
|
-
color: textColor,
|
|
312
|
-
fontSize,
|
|
313
|
-
fontWeight: "600",
|
|
314
|
-
textAlign: "center",
|
|
315
|
-
};
|
|
316
|
-
|
|
317
|
-
const subContainer = { ...container };
|
|
318
|
-
return Object.assign(container, {
|
|
319
|
-
container: subContainer,
|
|
320
|
-
text,
|
|
321
|
-
});
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
// ---------------------------------------------------------------------------
|
|
325
|
-
// 4. TAB STRIP & TAB ITEM RECIPES
|
|
326
|
-
// ---------------------------------------------------------------------------
|
|
327
|
-
|
|
328
|
-
export interface TabStripRecipeOptions {
|
|
329
|
-
radius?: number;
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
export interface TabStripRecipeResult extends ViewStyle {
|
|
333
|
-
frame: ViewStyle;
|
|
334
|
-
track: ViewStyle;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
/**
|
|
338
|
-
* Generates standard React Native styles for tab tracks and bounding frames.
|
|
339
|
-
*/
|
|
340
|
-
export function tabStripRecipe(
|
|
341
|
-
themeInput?: ThemeInput,
|
|
342
|
-
options?: TabStripRecipeOptions,
|
|
343
|
-
): TabStripRecipeResult {
|
|
344
|
-
const colors = resolveThemeColors(themeInput);
|
|
345
|
-
const radius = options?.radius ?? 8;
|
|
346
|
-
|
|
347
|
-
const frame: ViewStyle = {
|
|
348
|
-
width: "100%",
|
|
349
|
-
backgroundColor: colors.surface1,
|
|
350
|
-
borderRadius: radius,
|
|
351
|
-
borderColor: colors.border,
|
|
352
|
-
borderWidth: 1,
|
|
353
|
-
overflow: "hidden",
|
|
354
|
-
justifyContent: "center",
|
|
355
|
-
};
|
|
356
|
-
|
|
357
|
-
const track: ViewStyle = {
|
|
358
|
-
flexDirection: "row",
|
|
359
|
-
alignItems: "center",
|
|
360
|
-
width: "100%",
|
|
361
|
-
padding: 3,
|
|
362
|
-
gap: 2,
|
|
363
|
-
};
|
|
364
|
-
|
|
365
|
-
const subFrame = { ...frame };
|
|
366
|
-
return Object.assign(frame, {
|
|
367
|
-
frame: subFrame,
|
|
368
|
-
track,
|
|
369
|
-
});
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
export interface TabItemRecipeOptions {
|
|
373
|
-
active?: boolean;
|
|
374
|
-
pressed?: boolean;
|
|
375
|
-
compact?: boolean;
|
|
376
|
-
fit?: boolean;
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
export interface TabItemRecipeResult extends ViewStyle {
|
|
380
|
-
container: ViewStyle;
|
|
381
|
-
text: TextStyle;
|
|
382
|
-
badge: ViewStyle;
|
|
383
|
-
badgeText: TextStyle;
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
/**
|
|
387
|
-
* Generates standard React Native styles for individual tab buttons and inner badges.
|
|
388
|
-
*/
|
|
389
|
-
export function tabItemRecipe(
|
|
390
|
-
themeInput?: ThemeInput,
|
|
391
|
-
activeOrOptions?: boolean | TabItemRecipeOptions,
|
|
392
|
-
options?: TabItemRecipeOptions,
|
|
393
|
-
): TabItemRecipeResult {
|
|
394
|
-
const colors = resolveThemeColors(themeInput);
|
|
395
|
-
const mergedOptions: TabItemRecipeOptions =
|
|
396
|
-
typeof activeOrOptions === "boolean"
|
|
397
|
-
? { active: activeOrOptions, ...options }
|
|
398
|
-
: (activeOrOptions ?? {});
|
|
399
|
-
|
|
400
|
-
const isActive = Boolean(mergedOptions.active);
|
|
401
|
-
const pressed = Boolean(mergedOptions.pressed);
|
|
402
|
-
const compact = Boolean(mergedOptions.compact);
|
|
403
|
-
const fit = mergedOptions.fit ?? true;
|
|
404
|
-
|
|
405
|
-
const bg = isActive
|
|
406
|
-
? colors.surface2
|
|
407
|
-
: pressed
|
|
408
|
-
? alpha(colors.surface2, 0.5)
|
|
409
|
-
: "transparent";
|
|
410
|
-
|
|
411
|
-
const container: ViewStyle = {
|
|
412
|
-
flexDirection: "row",
|
|
413
|
-
alignItems: "center",
|
|
414
|
-
justifyContent: "center",
|
|
415
|
-
gap: 4,
|
|
416
|
-
overflow: "hidden",
|
|
417
|
-
borderRadius: 6,
|
|
418
|
-
minHeight: compact ? 26 : 30,
|
|
419
|
-
backgroundColor: bg,
|
|
420
|
-
paddingHorizontal: compact ? 8 : 12,
|
|
421
|
-
paddingVertical: compact ? 4 : 6,
|
|
422
|
-
...(fit ? { flex: 1, flexShrink: 1, minWidth: 0 } : { flexShrink: 0 }),
|
|
423
|
-
};
|
|
424
|
-
|
|
425
|
-
const text: TextStyle = {
|
|
426
|
-
color: isActive ? colors.foreground : colors.foregroundMuted,
|
|
427
|
-
fontSize: compact ? 11 : 12,
|
|
428
|
-
fontWeight: isActive ? "600" : "500",
|
|
429
|
-
textAlign: "center",
|
|
430
|
-
};
|
|
431
|
-
|
|
432
|
-
const badge: ViewStyle = {
|
|
433
|
-
borderRadius: 9999,
|
|
434
|
-
paddingHorizontal: 5,
|
|
435
|
-
paddingVertical: 1,
|
|
436
|
-
minWidth: 16,
|
|
437
|
-
alignItems: "center",
|
|
438
|
-
justifyContent: "center",
|
|
439
|
-
backgroundColor: isActive ? colors.accent : alpha(colors.foregroundMuted, 0.2),
|
|
440
|
-
};
|
|
441
|
-
|
|
442
|
-
const badgeText: TextStyle = {
|
|
443
|
-
fontSize: 10,
|
|
444
|
-
fontWeight: "700",
|
|
445
|
-
color: isActive ? colors.accentForeground || FALLBACK_ACCENT_FOREGROUND : colors.foregroundMuted,
|
|
446
|
-
};
|
|
447
|
-
|
|
448
|
-
const subContainer = { ...container };
|
|
449
|
-
return Object.assign(container, {
|
|
450
|
-
container: subContainer,
|
|
451
|
-
text,
|
|
452
|
-
badge,
|
|
453
|
-
badgeText,
|
|
454
|
-
});
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
// ---------------------------------------------------------------------------
|
|
458
|
-
// 5. BADGE RECIPE
|
|
459
|
-
// ---------------------------------------------------------------------------
|
|
460
|
-
|
|
461
|
-
export interface BadgeRecipeOptions {
|
|
462
|
-
variant?: StatusVariant;
|
|
463
|
-
styleVariant?: BadgeStyle;
|
|
464
|
-
size?: BadgeSize;
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
export interface BadgeRecipeResult extends ViewStyle {
|
|
468
|
-
container: ViewStyle;
|
|
469
|
-
text: TextStyle;
|
|
470
|
-
dot: ViewStyle;
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
/**
|
|
474
|
-
* Generates standard React Native styles for pill badges, indicators, and labels.
|
|
475
|
-
*/
|
|
476
|
-
export function badgeRecipe(
|
|
477
|
-
themeInput?: ThemeInput,
|
|
478
|
-
variantOrOptions?: StatusVariant | BadgeRecipeOptions,
|
|
479
|
-
options?: BadgeRecipeOptions,
|
|
480
|
-
): BadgeRecipeResult {
|
|
481
|
-
const colors = resolveThemeColors(themeInput);
|
|
482
|
-
const mergedOptions: BadgeRecipeOptions =
|
|
483
|
-
typeof variantOrOptions === "string"
|
|
484
|
-
? { variant: variantOrOptions, ...options }
|
|
485
|
-
: (variantOrOptions ?? {});
|
|
486
|
-
|
|
487
|
-
const variant = mergedOptions.variant ?? "neutral";
|
|
488
|
-
const styleVariant = mergedOptions.styleVariant ?? "tinted";
|
|
489
|
-
const size = mergedOptions.size ?? "md";
|
|
490
|
-
|
|
491
|
-
const palette = getVariantPalette(variant, colors);
|
|
492
|
-
const solidColor = getStatusColor(variant, colors);
|
|
493
|
-
|
|
494
|
-
let bg = palette.bg;
|
|
495
|
-
let border = palette.border;
|
|
496
|
-
let textColor = palette.text;
|
|
497
|
-
|
|
498
|
-
if (styleVariant === "outline") {
|
|
499
|
-
bg = "transparent";
|
|
500
|
-
border = palette.border;
|
|
501
|
-
textColor = palette.text;
|
|
502
|
-
} else if (styleVariant === "solid") {
|
|
503
|
-
bg = solidColor;
|
|
504
|
-
border = "transparent";
|
|
505
|
-
textColor = getContrastColor(
|
|
506
|
-
solidColor,
|
|
507
|
-
colors.accentForeground || FALLBACK_ACCENT_FOREGROUND,
|
|
508
|
-
colors.foreground,
|
|
509
|
-
);
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
const fontSize = size === "sm" ? 10 : 11;
|
|
513
|
-
const lineHeight = size === "sm" ? 12 : 15;
|
|
514
|
-
const paddingVertical = size === "sm" ? 1 : 2;
|
|
515
|
-
const paddingHorizontal = size === "sm" ? 5 : 8;
|
|
516
|
-
|
|
517
|
-
const container: ViewStyle = {
|
|
518
|
-
flexDirection: "row",
|
|
519
|
-
alignItems: "center",
|
|
520
|
-
alignSelf: "flex-start",
|
|
521
|
-
backgroundColor: bg,
|
|
522
|
-
borderColor: border,
|
|
523
|
-
borderWidth: 1,
|
|
524
|
-
borderRadius: 9999,
|
|
525
|
-
paddingVertical,
|
|
526
|
-
paddingHorizontal,
|
|
527
|
-
gap: 4,
|
|
528
|
-
};
|
|
529
|
-
|
|
530
|
-
const text: TextStyle = {
|
|
531
|
-
color: textColor,
|
|
532
|
-
fontSize,
|
|
533
|
-
lineHeight,
|
|
534
|
-
fontWeight: "600",
|
|
535
|
-
};
|
|
536
|
-
|
|
537
|
-
const dot: ViewStyle = {
|
|
538
|
-
width: 6,
|
|
539
|
-
height: 6,
|
|
540
|
-
borderRadius: 3,
|
|
541
|
-
backgroundColor: textColor,
|
|
542
|
-
};
|
|
543
|
-
|
|
544
|
-
const subContainer = { ...container };
|
|
545
|
-
return Object.assign(container, {
|
|
546
|
-
container: subContainer,
|
|
547
|
-
text,
|
|
548
|
-
dot,
|
|
549
|
-
});
|
|
550
|
-
}
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import React, { type ComponentType } from "react";
|
|
2
|
-
import type { HostSurfaceProps as PluginSurfaceProps } from "./host";
|
|
3
|
-
import { PluginThemeProvider } from "./theme/provider";
|
|
4
|
-
import { ModalBodyScrollOwnerContext } from "./layout/ModalBody";
|
|
5
|
-
import type { VisualFlair } from "./theme/flair";
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Structural registrar interface satisfied by both Paseo v0.7 PluginContext
|
|
9
|
-
* and Paseo v0.8 PluginClientContext.
|
|
10
|
-
*/
|
|
11
|
-
export interface SidebarSurfaceRegistrar {
|
|
12
|
-
addSurface(surfaceId: string, Component: ComponentType<PluginSurfaceProps>): any;
|
|
13
|
-
addSidebarItem(contribution: any): any;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface RegisterSidebarSurfaceOptions {
|
|
17
|
-
id: string;
|
|
18
|
-
title: string;
|
|
19
|
-
icon: string;
|
|
20
|
-
Component: ComponentType<PluginSurfaceProps>;
|
|
21
|
-
flair?: VisualFlair;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Registers a sidebar icon and corresponding full-page surface in a single call,
|
|
26
|
-
* automatically injecting `<PluginThemeProvider>` with custom visual flair.
|
|
27
|
-
* Works with both Paseo v0.7 PluginContext and Paseo v0.8 PluginClientContext.
|
|
28
|
-
*
|
|
29
|
-
* A sidebar surface is a full host page: Paseo routes to it directly and does
|
|
30
|
-
* NOT wrap the surface body in a host scroller (unlike `<Modal.Content>`, which
|
|
31
|
-
* bounds the dialog and supplies the outer scroll). Any `ModalBody` in the
|
|
32
|
-
* subtree would therefore pick the plain, non-scrolling branch on a
|
|
33
|
-
* non-compact desktop and clip overflowing content — the recurring
|
|
34
|
-
* "plugin page does not scroll" bug. Marking the subtree with the
|
|
35
|
-
* `"required"` scroll-owner context makes every `ModalBody` inside own the
|
|
36
|
-
* scroll on every surface, so plugins inherit working scroll with no
|
|
37
|
-
* per-plugin workaround.
|
|
38
|
-
*
|
|
39
|
-
* Returns an idempotent disposer that removes both the surface and the sidebar
|
|
40
|
-
* item, matching every other helper `add*` registration. Existing callers that
|
|
41
|
-
* ignore the return value are unaffected.
|
|
42
|
-
*/
|
|
43
|
-
export function registerSidebarSurface(
|
|
44
|
-
plugin: SidebarSurfaceRegistrar,
|
|
45
|
-
options: RegisterSidebarSurfaceOptions,
|
|
46
|
-
): () => void {
|
|
47
|
-
const { id, title, icon, Component, flair } = options;
|
|
48
|
-
|
|
49
|
-
const WrappedComponent: ComponentType<PluginSurfaceProps> = (props) => (
|
|
50
|
-
<PluginThemeProvider theme={props.theme} layout={props.layout} flair={flair}>
|
|
51
|
-
<ModalBodyScrollOwnerContext.Provider value="required">
|
|
52
|
-
<Component {...props} />
|
|
53
|
-
</ModalBodyScrollOwnerContext.Provider>
|
|
54
|
-
</PluginThemeProvider>
|
|
55
|
-
);
|
|
56
|
-
|
|
57
|
-
const surfaceRegistration = plugin.addSurface(id, WrappedComponent);
|
|
58
|
-
const itemRegistration = plugin.addSidebarItem({
|
|
59
|
-
id,
|
|
60
|
-
title,
|
|
61
|
-
icon,
|
|
62
|
-
surface: id,
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
let disposed = false;
|
|
66
|
-
return () => {
|
|
67
|
-
if (disposed) return;
|
|
68
|
-
disposed = true;
|
|
69
|
-
// Host registrations expose `remove()`; tolerate hosts that return a bare
|
|
70
|
-
// function or nothing so cleanup never throws.
|
|
71
|
-
for (const registration of [itemRegistration, surfaceRegistration]) {
|
|
72
|
-
if (!registration) continue;
|
|
73
|
-
if (typeof registration === "function") {
|
|
74
|
-
(registration as () => void)();
|
|
75
|
-
} else if (typeof registration.remove === "function") {
|
|
76
|
-
registration.remove();
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
};
|
|
80
|
-
}
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
export type RadiusStyle = "sharp" | "rounded" | "pill";
|
|
2
|
-
export type DensityStyle = "compact" | "comfortable" | "spacious";
|
|
3
|
-
export type SurfaceStyle = "flat" | "tinted" | "elevated";
|
|
4
|
-
export type HeadingTransform = "none" | "uppercase";
|
|
5
|
-
|
|
6
|
-
export interface VisualFlair {
|
|
7
|
-
/**
|
|
8
|
-
* Corner radius preset for interactive elements and containers.
|
|
9
|
-
* - "sharp": 2-3px (terminal / technical flair)
|
|
10
|
-
* - "rounded": 6-8px (default Paseo native flair)
|
|
11
|
-
* - "pill": 9999px (soft / playful flair)
|
|
12
|
-
*/
|
|
13
|
-
radius: RadiusStyle;
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Spacing and typography density.
|
|
17
|
-
* - "compact": tight padding and smaller fonts (the default — plugin UI is
|
|
18
|
-
* dense by nature and generous padding wastes vertical space)
|
|
19
|
-
* - "comfortable": balanced, roomier defaults (opt in per plugin)
|
|
20
|
-
* - "spacious": generous breathing room
|
|
21
|
-
*/
|
|
22
|
-
density: DensityStyle;
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Surface background styling for cards, panels, and modal boxes.
|
|
26
|
-
* - "flat": pure surface0 with border
|
|
27
|
-
* - "tinted": subtle tinted foreground / accent wash
|
|
28
|
-
* - "elevated": uses surface1 / surface2 hierarchy
|
|
29
|
-
*/
|
|
30
|
-
surfaceStyle: SurfaceStyle;
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Optional custom brand accent color (e.g. "#10b981", "#3b82f6").
|
|
34
|
-
* Overrides Paseo's theme.colors.accent within this plugin.
|
|
35
|
-
*/
|
|
36
|
-
accentColor?: string;
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Default border width for cards and bordered elements (default: 1).
|
|
40
|
-
*/
|
|
41
|
-
borderWidth: number;
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Text transform for section headers and meta labels.
|
|
45
|
-
*/
|
|
46
|
-
headingTransform: HeadingTransform;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export const defaultFlair: VisualFlair = {
|
|
50
|
-
radius: "rounded",
|
|
51
|
-
// Compact by default: plugin surfaces are information-dense, and the
|
|
52
|
-
// comfortable/spacious presets are opt-in per plugin. See xpufx-org/paseo#213.
|
|
53
|
-
density: "compact",
|
|
54
|
-
surfaceStyle: "flat",
|
|
55
|
-
borderWidth: 1,
|
|
56
|
-
headingTransform: "none",
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
export function resolveRadius(radius: RadiusStyle, size: "xs" | "sm" | "md" | "lg" | "pill" = "md"): number {
|
|
60
|
-
if (size === "pill" || radius === "pill") return 9999;
|
|
61
|
-
if (radius === "sharp") {
|
|
62
|
-
switch (size) {
|
|
63
|
-
case "xs": return 1;
|
|
64
|
-
case "sm": return 2;
|
|
65
|
-
case "md": return 3;
|
|
66
|
-
case "lg": return 4;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
// "rounded"
|
|
70
|
-
switch (size) {
|
|
71
|
-
case "xs": return 4;
|
|
72
|
-
case "sm": return 6;
|
|
73
|
-
case "md": return 8;
|
|
74
|
-
case "lg": return 12;
|
|
75
|
-
}
|
|
76
|
-
}
|