@xpufx/paseo-top 0.4.4 → 0.4.7
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 +18 -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 +94 -1
- package/client/pill.tsx +692 -616
- package/client/resources-query.ts +1 -1
- package/client/settings-events.ts +1 -1
- package/client/settings-ui.tsx +86 -9
- package/client/surface.tsx +308 -276
- 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} +9 -3
- 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} +51 -18
- 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/{surface.tsx → lifecycle/surface.tsx} +6 -6
- 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} +50 -58
- 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 -234
- package/npm-shrinkwrap.json +44 -0
- package/package.json +8 -4
- 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 +2 -0
- package/server/vendor/paseo-plugin-helper/logger.ts +5 -16
- 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 +18 -67
- package/server/vendor/permission-audit/index.ts +1 -0
- package/server/vendor/permission-audit/server.ts +839 -0
- package/shared/helper-version.ts +1 -1
- package/shared/multi-host.ts +153 -159
- package/shared/resources.ts +145 -0
- 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 +1 -0
- package/shared/vendor/paseo-plugin-helper/tickets.ts +122 -28
- 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 -168
- package/client/vendor/paseo-plugin-helper/components/Button.tsx +0 -204
- package/client/vendor/paseo-plugin-helper/components/Card.tsx +0 -257
- 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 -88
- package/client/vendor/paseo-plugin-helper/components/InteractiveRow.tsx +0 -142
- 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 -132
- package/client/vendor/paseo-plugin-helper/components/SectionHeader.tsx +0 -87
- package/client/vendor/paseo-plugin-helper/components/Select.tsx +0 -283
- 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 -26
- package/client/vendor/paseo-plugin-helper/custom-pills.tsx +0 -225
- 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 -30
- 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 -368
- 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/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/tickets.tsx +0 -766
- /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,121 +0,0 @@
|
|
|
1
|
-
import type { ThemeColors } from "../../../../shared/vendor/paseo-plugin-helper/types";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Paseo 0.8 host theme variables. On web hosts the live theme is exposed as
|
|
5
|
-
* CSS custom properties on the document root; this map binds each variable
|
|
6
|
-
* to the semantic ThemeColors slot it feeds.
|
|
7
|
-
*/
|
|
8
|
-
export const PASEO_HOST_CSS_VARIABLES = {
|
|
9
|
-
"--background": "surface0",
|
|
10
|
-
"--foreground": "foreground",
|
|
11
|
-
"--muted": "foregroundMuted",
|
|
12
|
-
"--accent": "accent",
|
|
13
|
-
"--accent-foreground": "accentForeground",
|
|
14
|
-
"--border": "border",
|
|
15
|
-
} as const satisfies Record<string, keyof ThemeColors>;
|
|
16
|
-
|
|
17
|
-
export type PaseoHostCssVariable = keyof typeof PASEO_HOST_CSS_VARIABLES;
|
|
18
|
-
|
|
19
|
-
export interface HostFontVariables {
|
|
20
|
-
sans?: string;
|
|
21
|
-
mono?: string;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface HostThemeVariables {
|
|
25
|
-
colors: Partial<ThemeColors>;
|
|
26
|
-
fonts: HostFontVariables;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
interface StyleReader {
|
|
30
|
-
getPropertyValue(name: string): string;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
interface DomRuntime {
|
|
34
|
-
document?: {
|
|
35
|
-
documentElement?: object;
|
|
36
|
-
defaultView?: {
|
|
37
|
-
getComputedStyle(target: object): StyleReader;
|
|
38
|
-
};
|
|
39
|
-
};
|
|
40
|
-
getComputedStyle?: (target: object) => StyleReader;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
type MutableColors = { -readonly [K in keyof ThemeColors]: ThemeColors[K] };
|
|
44
|
-
|
|
45
|
-
const HOST_FONT_VARIABLES: Record<"sans" | "mono", string[]> = {
|
|
46
|
-
sans: ["--font-sans", "--font-family"],
|
|
47
|
-
mono: ["--font-mono", "--font-family-monospace"],
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
function readVariable(names: string[]): string | undefined {
|
|
51
|
-
const runtime = globalThis as unknown as DomRuntime;
|
|
52
|
-
const doc = runtime.document;
|
|
53
|
-
if (!doc?.documentElement) return undefined;
|
|
54
|
-
const getStyle =
|
|
55
|
-
runtime.getComputedStyle ?? doc.defaultView?.getComputedStyle.bind(doc.defaultView);
|
|
56
|
-
if (!getStyle) return undefined;
|
|
57
|
-
try {
|
|
58
|
-
const computed = getStyle(doc.documentElement);
|
|
59
|
-
for (const name of names) {
|
|
60
|
-
const value = computed
|
|
61
|
-
.getPropertyValue(name)
|
|
62
|
-
.trim()
|
|
63
|
-
.replace(/^["']|["']$/g, "");
|
|
64
|
-
if (value) return value;
|
|
65
|
-
}
|
|
66
|
-
} catch {
|
|
67
|
-
// Non-DOM runtimes (native, tests) have no computed style to read.
|
|
68
|
-
}
|
|
69
|
-
return undefined;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Reads the live Paseo 0.8 host variables. Returns empty slots outside a DOM
|
|
74
|
-
* runtime so native callers merge to static defaults untouched.
|
|
75
|
-
*/
|
|
76
|
-
export function readHostThemeVariables(): HostThemeVariables {
|
|
77
|
-
const colors: Partial<MutableColors> = {};
|
|
78
|
-
for (const [variable, slot] of Object.entries(PASEO_HOST_CSS_VARIABLES)) {
|
|
79
|
-
const value = readVariable([variable]);
|
|
80
|
-
if (value) {
|
|
81
|
-
colors[slot] = value;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
const fonts: HostFontVariables = {};
|
|
85
|
-
const sans = readVariable(HOST_FONT_VARIABLES.sans);
|
|
86
|
-
if (sans) fonts.sans = sans;
|
|
87
|
-
const mono = readVariable(HOST_FONT_VARIABLES.mono);
|
|
88
|
-
if (mono) fonts.mono = mono;
|
|
89
|
-
return { colors, fonts };
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function definedValues<T extends object>(obj: T): Partial<T> {
|
|
93
|
-
const out: Partial<T> = {};
|
|
94
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
95
|
-
if (value !== undefined) {
|
|
96
|
-
(out as Record<string, unknown>)[key] = value;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
return out;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Pure merge for the provider: static defaults lose to live host variables,
|
|
104
|
-
* which lose to the injected host theme, which loses to the flair accent.
|
|
105
|
-
* Runtime `undefined` slots are skipped so a partial host theme falls back
|
|
106
|
-
* instead of blanking a slot. Exported for tests; the provider applies it
|
|
107
|
-
* inside useMemo.
|
|
108
|
-
*/
|
|
109
|
-
export function mergeThemeColors(
|
|
110
|
-
defaults: ThemeColors,
|
|
111
|
-
hostVariables: Partial<ThemeColors>,
|
|
112
|
-
injected: ThemeColors,
|
|
113
|
-
accentOverride?: string,
|
|
114
|
-
): ThemeColors {
|
|
115
|
-
return {
|
|
116
|
-
...defaults,
|
|
117
|
-
...definedValues(hostVariables),
|
|
118
|
-
...definedValues(injected),
|
|
119
|
-
...(accentOverride ? { accent: accentOverride } : {}),
|
|
120
|
-
};
|
|
121
|
-
}
|
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
createContext,
|
|
3
|
-
useCallback,
|
|
4
|
-
useContext,
|
|
5
|
-
useMemo,
|
|
6
|
-
useState,
|
|
7
|
-
useEffect,
|
|
8
|
-
type ReactNode,
|
|
9
|
-
} from "react";
|
|
10
|
-
import { StyleSheet, View, type LayoutChangeEvent } from "react-native";
|
|
11
|
-
import type { PluginTheme } from "../../../../shared/vendor/paseo-plugin-helper/types";
|
|
12
|
-
import type { HostLayout } from "../host";
|
|
13
|
-
import { Appearance } from "react-native";
|
|
14
|
-
import { defaultFlair, resolveRadius, type VisualFlair } from "./flair";
|
|
15
|
-
import { alpha, getContrastColor, getStatusColor, getVariantPalette } from "./color-utils";
|
|
16
|
-
import {
|
|
17
|
-
getTouchTargetMin,
|
|
18
|
-
isMobilePlatform,
|
|
19
|
-
resolveEffectiveCompact,
|
|
20
|
-
resolvePadding,
|
|
21
|
-
} from "./responsive";
|
|
22
|
-
import {
|
|
23
|
-
mergeThemeColors,
|
|
24
|
-
readHostThemeVariables,
|
|
25
|
-
type HostFontVariables,
|
|
26
|
-
} from "./host-variables";
|
|
27
|
-
import { defaultDarkTheme, defaultLightTheme, getDefaultTheme, resolveTypography, type TypographyScale } from "./tokens";
|
|
28
|
-
export { defaultDarkTheme, defaultLightTheme, getDefaultTheme };
|
|
29
|
-
import type { ResponsiveLayout, StatusVariant, ThemeColors } from "../../../../shared/vendor/paseo-plugin-helper/types";
|
|
30
|
-
|
|
31
|
-
export interface PluginThemeContextValue {
|
|
32
|
-
theme: PluginTheme;
|
|
33
|
-
colors: ThemeColors;
|
|
34
|
-
fonts: HostFontVariables;
|
|
35
|
-
layout: ResponsiveLayout;
|
|
36
|
-
flair: VisualFlair;
|
|
37
|
-
isCompact: boolean;
|
|
38
|
-
isMobile: boolean;
|
|
39
|
-
touchTargetMin: number;
|
|
40
|
-
alpha: (color: string, opacity: number) => string;
|
|
41
|
-
getContrastColor: (bgHex: string, light?: string, dark?: string) => string;
|
|
42
|
-
getStatusColor: (variant: StatusVariant) => string;
|
|
43
|
-
getVariantPalette: (variant: StatusVariant) => { bg: string; text: string; border: string };
|
|
44
|
-
resolveRadius: (size?: "xs" | "sm" | "md" | "lg" | "pill") => number;
|
|
45
|
-
padding: { horizontal: number; vertical: number; gap: number };
|
|
46
|
-
typography: TypographyScale;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const defaultLayout: ResponsiveLayout = {
|
|
50
|
-
compact: false,
|
|
51
|
-
platform: "web",
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
// default themes re-exported from tokens.js
|
|
55
|
-
|
|
56
|
-
export function useAppearanceScheme(): [string | undefined, (s: string | undefined) => void] {
|
|
57
|
-
const [scheme, setScheme] = useState<string | undefined>(Appearance.getColorScheme?.() ?? undefined);
|
|
58
|
-
useEffect(() => {
|
|
59
|
-
const sub = Appearance.addChangeListener?.(({colorScheme}) => {
|
|
60
|
-
setScheme(colorScheme ?? undefined);
|
|
61
|
-
});
|
|
62
|
-
return () => {
|
|
63
|
-
// remove listener if possible
|
|
64
|
-
// Appearance.addChangeListener returns an object with remove method in RN
|
|
65
|
-
// but in web we may not have it; ignore safely
|
|
66
|
-
if (sub && typeof (sub as any).remove === "function") {
|
|
67
|
-
(sub as any).remove();
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
}, []);
|
|
71
|
-
return [scheme, setScheme];
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const initialDefaultTheme = getDefaultTheme();
|
|
75
|
-
const PluginThemeContext = createContext<PluginThemeContextValue>({
|
|
76
|
-
theme: initialDefaultTheme,
|
|
77
|
-
colors: initialDefaultTheme.colors,
|
|
78
|
-
fonts: {},
|
|
79
|
-
layout: defaultLayout,
|
|
80
|
-
flair: defaultFlair,
|
|
81
|
-
isCompact: false,
|
|
82
|
-
isMobile: false,
|
|
83
|
-
touchTargetMin: 28,
|
|
84
|
-
alpha: (color, op) => alpha(color, op),
|
|
85
|
-
getContrastColor: (bg, l, d) => getContrastColor(bg, l, d),
|
|
86
|
-
getStatusColor: (v) => getStatusColor(v, initialDefaultTheme.colors),
|
|
87
|
-
getVariantPalette: (v) => getVariantPalette(v, initialDefaultTheme.colors),
|
|
88
|
-
resolveRadius: (s) => resolveRadius("rounded", s),
|
|
89
|
-
padding: resolvePadding(defaultLayout, "comfortable"),
|
|
90
|
-
typography: resolveTypography(defaultLayout, "comfortable"),
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
export interface PluginThemeProviderProps {
|
|
94
|
-
theme: PluginTheme;
|
|
95
|
-
layout?: HostLayout;
|
|
96
|
-
flair?: Partial<VisualFlair>;
|
|
97
|
-
children: ReactNode;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
export function PluginThemeProvider({
|
|
101
|
-
theme,
|
|
102
|
-
layout = defaultLayout,
|
|
103
|
-
flair: userFlair,
|
|
104
|
-
children,
|
|
105
|
-
}: PluginThemeProviderProps) {
|
|
106
|
-
const hostWidth =
|
|
107
|
-
typeof layout.width === "number" && layout.width > 0 ? layout.width : undefined;
|
|
108
|
-
const needsMeasurement = hostWidth === undefined;
|
|
109
|
-
const [measuredWidth, setMeasuredWidth] = useState<number | undefined>(undefined);
|
|
110
|
-
const [appearanceScheme] = useAppearanceScheme();
|
|
111
|
-
|
|
112
|
-
const handleLayout = useCallback((event: LayoutChangeEvent) => {
|
|
113
|
-
const width = event.nativeEvent?.layout?.width;
|
|
114
|
-
if (typeof width !== "number" || width <= 0) return;
|
|
115
|
-
setMeasuredWidth((prev) => (prev === width ? prev : width));
|
|
116
|
-
}, []);
|
|
117
|
-
|
|
118
|
-
const effectiveWidth = hostWidth ?? measuredWidth;
|
|
119
|
-
|
|
120
|
-
const value = useMemo<PluginThemeContextValue>(() => {
|
|
121
|
-
const flair: VisualFlair = { ...defaultFlair, ...userFlair };
|
|
122
|
-
const hostVariables = readHostThemeVariables();
|
|
123
|
-
const effectiveColors = mergeThemeColors(
|
|
124
|
-
getDefaultTheme(appearanceScheme).colors,
|
|
125
|
-
hostVariables.colors,
|
|
126
|
-
theme.colors,
|
|
127
|
-
flair.accentColor,
|
|
128
|
-
);
|
|
129
|
-
|
|
130
|
-
const isCompact = resolveEffectiveCompact(layout, effectiveWidth);
|
|
131
|
-
const effectiveLayout: ResponsiveLayout =
|
|
132
|
-
effectiveWidth !== undefined
|
|
133
|
-
? { ...layout, width: effectiveWidth, compact: isCompact }
|
|
134
|
-
: { ...layout, compact: isCompact };
|
|
135
|
-
const isMobile = isMobilePlatform(effectiveLayout.platform);
|
|
136
|
-
const touchTargetMin = getTouchTargetMin(effectiveLayout);
|
|
137
|
-
const padding = resolvePadding(effectiveLayout, flair.density);
|
|
138
|
-
const typography = resolveTypography(effectiveLayout, flair.density);
|
|
139
|
-
|
|
140
|
-
return {
|
|
141
|
-
theme,
|
|
142
|
-
colors: effectiveColors,
|
|
143
|
-
fonts: hostVariables.fonts,
|
|
144
|
-
layout: effectiveLayout,
|
|
145
|
-
flair,
|
|
146
|
-
isCompact,
|
|
147
|
-
isMobile,
|
|
148
|
-
touchTargetMin,
|
|
149
|
-
alpha: (c, o) => alpha(c, o),
|
|
150
|
-
getContrastColor: (bg, l, d) => getContrastColor(bg, l, d),
|
|
151
|
-
getStatusColor: (v) => getStatusColor(v, effectiveColors, flair.accentColor),
|
|
152
|
-
getVariantPalette: (v) => getVariantPalette(v, effectiveColors, flair.accentColor),
|
|
153
|
-
resolveRadius: (size = "md") => resolveRadius(flair.radius, size),
|
|
154
|
-
padding,
|
|
155
|
-
typography,
|
|
156
|
-
};
|
|
157
|
-
}, [theme, layout, userFlair, effectiveWidth, appearanceScheme]);
|
|
158
|
-
|
|
159
|
-
return (
|
|
160
|
-
<PluginThemeContext.Provider value={value}>
|
|
161
|
-
{needsMeasurement ? (
|
|
162
|
-
<View style={styles.measureContainer} onLayout={handleLayout}>
|
|
163
|
-
{children}
|
|
164
|
-
</View>
|
|
165
|
-
) : (
|
|
166
|
-
children
|
|
167
|
-
)}
|
|
168
|
-
</PluginThemeContext.Provider>
|
|
169
|
-
);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const styles = StyleSheet.create({
|
|
173
|
-
measureContainer: { flex: 1 },
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
export function usePluginTheme(): PluginThemeContextValue {
|
|
177
|
-
return useContext(PluginThemeContext);
|
|
178
|
-
}
|
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
import type { DensityStyle } from "./flair";
|
|
2
|
-
import type { PlatformType, ResponsiveLayout } from "../../../../shared/vendor/paseo-plugin-helper/types";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Container width (px) at or below which a surface steps down to the compact
|
|
6
|
-
* scale. Mirrors the host's `COMPACT_FORM_FACTOR_WIDTH` so plugin typography
|
|
7
|
-
* in narrow popovers matches full-screen mobile surfaces.
|
|
8
|
-
*/
|
|
9
|
-
export const COMPACT_FORM_FACTOR_WIDTH = 500;
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Resolves compact mode from the actual container width when it is known,
|
|
13
|
-
* falling back to the host's viewport-derived `compact` flag otherwise.
|
|
14
|
-
*
|
|
15
|
-
* A host-declared compact surface stays compact at any width; a known width at
|
|
16
|
-
* or below {@link COMPACT_FORM_FACTOR_WIDTH} forces compact even when the host
|
|
17
|
-
* viewport is wide (e.g. a narrow header-button popover on desktop).
|
|
18
|
-
*/
|
|
19
|
-
export function resolveEffectiveCompact(
|
|
20
|
-
layout: ResponsiveLayout,
|
|
21
|
-
widthOverride?: number,
|
|
22
|
-
): boolean {
|
|
23
|
-
const width = widthOverride ?? layout.width;
|
|
24
|
-
if (typeof width === "number" && width > 0) {
|
|
25
|
-
return width <= COMPACT_FORM_FACTOR_WIDTH || Boolean(layout.compact);
|
|
26
|
-
}
|
|
27
|
-
return Boolean(layout.compact);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Checks if the current platform is mobile (iOS or Android).
|
|
32
|
-
*/
|
|
33
|
-
export function isMobilePlatform(platform: PlatformType): boolean {
|
|
34
|
-
return platform === "ios" || platform === "android";
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Interactive target floor for a compact surface on a non-mobile platform
|
|
39
|
-
* (e.g. a narrow desktop popover). Sits between the full-desktop floor and the
|
|
40
|
-
* 44pt touch target: a mouse pointer needs a little more room than a wide
|
|
41
|
-
* panel, but nothing like a finger-sized hit area.
|
|
42
|
-
*/
|
|
43
|
-
export const COMPACT_DESKTOP_TOUCH_TARGET = 36;
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Returns the recommended minimum interactive target size (in pt/px).
|
|
47
|
-
* Real touch platforms follow Apple HIG / Android Material (min 44pt); a
|
|
48
|
-
* compact desktop surface gets a modest bump over the 28pt desktop floor.
|
|
49
|
-
*/
|
|
50
|
-
export function getTouchTargetMin(layout: ResponsiveLayout): number {
|
|
51
|
-
if (isMobilePlatform(layout.platform)) return 44;
|
|
52
|
-
return resolveEffectiveCompact(layout) ? COMPACT_DESKTOP_TOUCH_TARGET : 28;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Selects a value based on compact/mobile vs desktop screen constraints.
|
|
57
|
-
*/
|
|
58
|
-
export function responsiveValue<T>(layout: ResponsiveLayout, desktopVal: T, compactVal: T): T {
|
|
59
|
-
return resolveEffectiveCompact(layout) ? compactVal : desktopVal;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Calculates adaptive padding based on compact mode and density preset.
|
|
64
|
-
*/
|
|
65
|
-
export function resolvePadding(
|
|
66
|
-
layout: ResponsiveLayout,
|
|
67
|
-
density: DensityStyle,
|
|
68
|
-
): { horizontal: number; vertical: number; gap: number } {
|
|
69
|
-
const isCompact = resolveEffectiveCompact(layout);
|
|
70
|
-
|
|
71
|
-
switch (density) {
|
|
72
|
-
case "compact":
|
|
73
|
-
return {
|
|
74
|
-
horizontal: isCompact ? 10 : 12,
|
|
75
|
-
vertical: isCompact ? 6 : 8,
|
|
76
|
-
gap: isCompact ? 6 : 8,
|
|
77
|
-
};
|
|
78
|
-
case "spacious":
|
|
79
|
-
return {
|
|
80
|
-
horizontal: isCompact ? 16 : 24,
|
|
81
|
-
vertical: isCompact ? 14 : 20,
|
|
82
|
-
gap: isCompact ? 12 : 16,
|
|
83
|
-
};
|
|
84
|
-
case "comfortable":
|
|
85
|
-
default:
|
|
86
|
-
return {
|
|
87
|
-
horizontal: isCompact ? 12 : 16,
|
|
88
|
-
vertical: isCompact ? 10 : 14,
|
|
89
|
-
gap: isCompact ? 8 : 12,
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export interface GridColumnOptions {
|
|
95
|
-
/** Requested (and maximum) column count. */
|
|
96
|
-
columns: number;
|
|
97
|
-
/** Horizontal gap between cells, in px. Default: 0. */
|
|
98
|
-
gap?: number;
|
|
99
|
-
/** Measured container width, when the host reports one. */
|
|
100
|
-
width?: number;
|
|
101
|
-
/**
|
|
102
|
-
* Minimum width each column should keep before wrapping to fewer columns.
|
|
103
|
-
* Only applied when a positive container width is known; `columns` stays the
|
|
104
|
-
* upper bound.
|
|
105
|
-
*/
|
|
106
|
-
minColumnWidth?: number;
|
|
107
|
-
/**
|
|
108
|
-
* How a compact surface treats the requested column count.
|
|
109
|
-
* - "never" (default): keep the requested columns; flexbox wraps as needed.
|
|
110
|
-
* - "compact": collapse to a single column on a compact surface.
|
|
111
|
-
*/
|
|
112
|
-
collapse?: "compact" | "never";
|
|
113
|
-
/** Whether the current surface is compact. */
|
|
114
|
-
isCompact?: boolean;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Resolves the effective column count for a responsive grid.
|
|
119
|
-
*
|
|
120
|
-
* A `minColumnWidth` plus a known container width yields as many columns as
|
|
121
|
-
* fit, capped at the requested count — so a narrow container wraps down to
|
|
122
|
-
* fewer columns instead of the requested count being forced through.
|
|
123
|
-
*/
|
|
124
|
-
export function resolveGridColumns(options: GridColumnOptions): number {
|
|
125
|
-
const requested = Math.max(1, Math.floor(options.columns));
|
|
126
|
-
if ((options.collapse ?? "never") === "compact" && options.isCompact) return 1;
|
|
127
|
-
|
|
128
|
-
const { minColumnWidth, width } = options;
|
|
129
|
-
const gap = options.gap ?? 0;
|
|
130
|
-
if (
|
|
131
|
-
typeof minColumnWidth === "number" &&
|
|
132
|
-
minColumnWidth > 0 &&
|
|
133
|
-
typeof width === "number" &&
|
|
134
|
-
width > 0
|
|
135
|
-
) {
|
|
136
|
-
const fit = Math.floor((width + gap) / (minColumnWidth + gap));
|
|
137
|
-
return Math.max(1, Math.min(requested, fit));
|
|
138
|
-
}
|
|
139
|
-
return requested;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
export interface ResponsiveSelectOptions<T> {
|
|
143
|
-
/**
|
|
144
|
-
* Default fallback value, used on desktop/wide viewports if no more specific option matches.
|
|
145
|
-
*/
|
|
146
|
-
desktop?: T;
|
|
147
|
-
/**
|
|
148
|
-
* Value to use on mobile platforms ('ios' | 'android').
|
|
149
|
-
*/
|
|
150
|
-
mobile?: T;
|
|
151
|
-
/**
|
|
152
|
-
* Value to use when in compact mode (layout.compact === true), such as on mobile or narrow split panes.
|
|
153
|
-
*/
|
|
154
|
-
compact?: T;
|
|
155
|
-
/**
|
|
156
|
-
* Value to use when not in compact mode (layout.compact === false).
|
|
157
|
-
*/
|
|
158
|
-
wide?: T;
|
|
159
|
-
/**
|
|
160
|
-
* Platform-specific overrides ('web', 'desktop', 'ios', 'android', 'macos', 'windows', 'linux').
|
|
161
|
-
*/
|
|
162
|
-
platform?: Partial<Record<PlatformType, T>>;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* Selects a value based on responsive criteria with priority:
|
|
167
|
-
* 1. Platform-specific override (`options.platform?.[platform]`)
|
|
168
|
-
* 2. Mobile platform match (`options.mobile` if mobile OS)
|
|
169
|
-
* 3. Compact mode match (`options.compact` if layout.compact)
|
|
170
|
-
* 4. Wide mode match (`options.wide` if !layout.compact)
|
|
171
|
-
* 5. Desktop / Default (`options.desktop`)
|
|
172
|
-
*/
|
|
173
|
-
export function responsiveSelect<T>(
|
|
174
|
-
layout: ResponsiveLayout,
|
|
175
|
-
options: ResponsiveSelectOptions<T>,
|
|
176
|
-
): T | undefined {
|
|
177
|
-
const isMobile = isMobilePlatform(layout.platform);
|
|
178
|
-
const isCompact = resolveEffectiveCompact(layout);
|
|
179
|
-
|
|
180
|
-
// 1. Specific platform override
|
|
181
|
-
if (options.platform && layout.platform in options.platform) {
|
|
182
|
-
const val = options.platform[layout.platform];
|
|
183
|
-
if (val !== undefined) return val;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// 2. Mobile platform match
|
|
187
|
-
if (isMobile && options.mobile !== undefined) {
|
|
188
|
-
return options.mobile;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// 3. Compact layout match
|
|
192
|
-
if (isCompact && options.compact !== undefined) {
|
|
193
|
-
return options.compact;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// 4. Wide (non-compact) layout match
|
|
197
|
-
if (!isCompact && options.wide !== undefined) {
|
|
198
|
-
return options.wide;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// 5. Desktop fallback
|
|
202
|
-
if (options.desktop !== undefined) {
|
|
203
|
-
return options.desktop;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// 6. Secondary fallbacks
|
|
207
|
-
if (options.wide !== undefined) return options.wide;
|
|
208
|
-
if (options.compact !== undefined) return options.compact;
|
|
209
|
-
if (options.mobile !== undefined) return options.mobile;
|
|
210
|
-
|
|
211
|
-
return undefined;
|
|
212
|
-
}
|
|
213
|
-
|