@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.
Files changed (130) hide show
  1. package/OVERVIEW.md +17 -0
  2. package/client/host-ui.tsx +184 -0
  3. package/client/multi-host-view.tsx +57 -43
  4. package/client/multi-host.ts +36 -13
  5. package/client/pill-labels.ts +124 -7
  6. package/client/pill.tsx +724 -669
  7. package/client/resources-query.ts +1 -1
  8. package/client/settings-events.ts +33 -0
  9. package/client/settings-ui.tsx +86 -9
  10. package/client/surface.tsx +310 -274
  11. package/client/telemetry-copy.ts +8 -2
  12. package/client/telemetry.tsx +372 -321
  13. package/client/turn-panel.tsx +126 -104
  14. package/client/vendor/paseo-plugin-helper/{host.ts → core/host.ts} +21 -61
  15. package/client/vendor/paseo-plugin-helper/core/index.ts +76 -0
  16. package/client/vendor/paseo-plugin-helper/core/plugin-load-count.ts +89 -0
  17. package/client/vendor/paseo-plugin-helper/{query-refresh.ts → core/query-refresh.ts} +2 -2
  18. package/client/vendor/paseo-plugin-helper/{query.ts → core/query.ts} +2 -2
  19. package/client/vendor/paseo-plugin-helper/{settings.ts → core/settings.ts} +1 -1
  20. package/client/vendor/paseo-plugin-helper/{shared-settings.ts → core/shared-settings.ts} +2 -2
  21. package/client/vendor/paseo-plugin-helper/{utils → lifecycle}/clipboard.ts +2 -2
  22. package/client/vendor/paseo-plugin-helper/lifecycle/flair.ts +26 -0
  23. package/client/vendor/paseo-plugin-helper/{theme/color-utils.ts → lifecycle/host-color.ts} +95 -38
  24. package/client/vendor/paseo-plugin-helper/lifecycle/host-theme.tsx +109 -0
  25. package/client/vendor/paseo-plugin-helper/lifecycle/index.ts +23 -0
  26. package/client/vendor/paseo-plugin-helper/lifecycle/multi-host.ts +66 -0
  27. package/client/vendor/paseo-plugin-helper/{panel.tsx → lifecycle/panel.tsx} +7 -7
  28. package/client/vendor/paseo-plugin-helper/{pill.tsx → lifecycle/pill.tsx} +118 -338
  29. package/client/vendor/paseo-plugin-helper/lifecycle/providers.tsx +46 -0
  30. package/client/vendor/paseo-plugin-helper/lifecycle/scroll-owner.ts +26 -0
  31. package/client/vendor/paseo-plugin-helper/lifecycle/surface.tsx +140 -0
  32. package/client/vendor/paseo-plugin-helper/ui/color.ts +1 -0
  33. package/client/vendor/paseo-plugin-helper/ui/content.tsx +1326 -0
  34. package/client/vendor/paseo-plugin-helper/ui/controls.tsx +1018 -0
  35. package/client/vendor/paseo-plugin-helper/{components/DataTable.tsx → ui/data.tsx} +55 -47
  36. package/client/vendor/paseo-plugin-helper/ui/index.ts +44 -0
  37. package/client/vendor/paseo-plugin-helper/ui/layout.tsx +263 -0
  38. package/client/vendor/paseo-plugin-helper/ui/modal.tsx +111 -0
  39. package/client/vendor/paseo-plugin-helper/ui/plugin-reload-banner.tsx +119 -0
  40. package/client/vendor/paseo-plugin-helper/{settings-screen.tsx → ui/settings-screen.tsx} +3 -3
  41. package/client/vendor/paseo-plugin-helper/ui/settings.ts +36 -0
  42. package/client/vendor/paseo-plugin-helper/ui/text.tsx +821 -0
  43. package/client/vendor/paseo-plugin-helper/ui/theme.tsx +1 -0
  44. package/client/vendor/permission-audit/client.tsx +706 -0
  45. package/client/vendor/permission-audit/index.tsx +1 -0
  46. package/index.client.tsx +58 -37
  47. package/index.server.ts +256 -227
  48. package/package.json +14 -2
  49. package/paseo-plugin.json +2 -0
  50. package/screenshots/1-paseo-top-modal-system.jpg +0 -0
  51. package/screenshots/2-paseo-top-modal-workspace.jpg +0 -0
  52. package/screenshots/3-paseo-top-modal-settings.jpg +0 -0
  53. package/screenshots/4-paseo-top-modal-about.jpg +0 -0
  54. package/screenshots/paseo-top-dashboard.jpg +0 -0
  55. package/screenshots/paseo-top-general-view.jpg +0 -0
  56. package/server/resources.ts +98 -0
  57. package/server/vendor/paseo-plugin-helper/custom-pills.ts +70 -0
  58. package/server/vendor/paseo-plugin-helper/index.ts +4 -0
  59. package/server/vendor/paseo-plugin-helper/logger.ts +5 -16
  60. package/server/vendor/paseo-plugin-helper/mcp-injection.ts +20 -2
  61. package/server/vendor/paseo-plugin-helper/plugins.ts +184 -19
  62. package/server/vendor/paseo-plugin-helper/rpc-guard.ts +2 -2
  63. package/server/vendor/paseo-plugin-helper/storage-guard.ts +294 -0
  64. package/server/vendor/paseo-plugin-helper/storage.ts +36 -2
  65. package/server/vendor/paseo-plugin-helper/telemetry.ts +486 -0
  66. package/server/vendor/paseo-plugin-helper/tickets.ts +1232 -0
  67. package/server/vendor/permission-audit/index.ts +1 -0
  68. package/server/vendor/permission-audit/server.ts +839 -0
  69. package/shared/helper-version.ts +20 -0
  70. package/shared/multi-host.ts +156 -155
  71. package/shared/resources.ts +146 -1
  72. package/shared/vendor/paseo-plugin-helper/async.ts +14 -7
  73. package/shared/vendor/paseo-plugin-helper/features.ts +244 -0
  74. package/shared/vendor/paseo-plugin-helper/forge.ts +27 -0
  75. package/shared/vendor/paseo-plugin-helper/index.ts +2 -0
  76. package/shared/vendor/paseo-plugin-helper/tickets.ts +1104 -0
  77. package/shared/vendor/permission-audit/filter.ts +61 -0
  78. package/shared/vendor/permission-audit/index.ts +2 -0
  79. package/shared/vendor/permission-audit/shared.ts +119 -0
  80. package/shared/version.ts +1 -1
  81. package/client/vendor/paseo-plugin-helper/components/AboutSection.tsx +0 -493
  82. package/client/vendor/paseo-plugin-helper/components/AttentionBeacon.tsx +0 -250
  83. package/client/vendor/paseo-plugin-helper/components/Badge.tsx +0 -156
  84. package/client/vendor/paseo-plugin-helper/components/Button.tsx +0 -184
  85. package/client/vendor/paseo-plugin-helper/components/Card.tsx +0 -225
  86. package/client/vendor/paseo-plugin-helper/components/CodeBlock.tsx +0 -196
  87. package/client/vendor/paseo-plugin-helper/components/Collapsible.tsx +0 -277
  88. package/client/vendor/paseo-plugin-helper/components/CommandBox.tsx +0 -172
  89. package/client/vendor/paseo-plugin-helper/components/CopyButton.tsx +0 -180
  90. package/client/vendor/paseo-plugin-helper/components/EmptyState.tsx +0 -97
  91. package/client/vendor/paseo-plugin-helper/components/HighlightedText.tsx +0 -70
  92. package/client/vendor/paseo-plugin-helper/components/InlineButton.tsx +0 -75
  93. package/client/vendor/paseo-plugin-helper/components/KeyValue.tsx +0 -446
  94. package/client/vendor/paseo-plugin-helper/components/MetricGauge.tsx +0 -247
  95. package/client/vendor/paseo-plugin-helper/components/ProgressBar.tsx +0 -117
  96. package/client/vendor/paseo-plugin-helper/components/Responsive.tsx +0 -53
  97. package/client/vendor/paseo-plugin-helper/components/SearchInput.tsx +0 -118
  98. package/client/vendor/paseo-plugin-helper/components/SectionHeader.tsx +0 -80
  99. package/client/vendor/paseo-plugin-helper/components/Select.tsx +0 -215
  100. package/client/vendor/paseo-plugin-helper/components/StatusDot.tsx +0 -80
  101. package/client/vendor/paseo-plugin-helper/components/Tabs.tsx +0 -319
  102. package/client/vendor/paseo-plugin-helper/components/TextInput.tsx +0 -150
  103. package/client/vendor/paseo-plugin-helper/components/Toggle.tsx +0 -163
  104. package/client/vendor/paseo-plugin-helper/components/TruncatedText.tsx +0 -157
  105. package/client/vendor/paseo-plugin-helper/components/index.ts +0 -25
  106. package/client/vendor/paseo-plugin-helper/custom-pills.tsx +0 -224
  107. package/client/vendor/paseo-plugin-helper/forge-icon.tsx +0 -79
  108. package/client/vendor/paseo-plugin-helper/icon.tsx +0 -39
  109. package/client/vendor/paseo-plugin-helper/index.ts +0 -29
  110. package/client/vendor/paseo-plugin-helper/layout/ActionBar.tsx +0 -49
  111. package/client/vendor/paseo-plugin-helper/layout/FormRow.tsx +0 -103
  112. package/client/vendor/paseo-plugin-helper/layout/Grid.tsx +0 -65
  113. package/client/vendor/paseo-plugin-helper/layout/ModalBody.tsx +0 -378
  114. package/client/vendor/paseo-plugin-helper/layout/ModalContent.tsx +0 -51
  115. package/client/vendor/paseo-plugin-helper/layout/Row.tsx +0 -39
  116. package/client/vendor/paseo-plugin-helper/layout/Stack.tsx +0 -39
  117. package/client/vendor/paseo-plugin-helper/layout/index.ts +0 -7
  118. package/client/vendor/paseo-plugin-helper/styles/index.ts +0 -1
  119. package/client/vendor/paseo-plugin-helper/styles/recipes.ts +0 -550
  120. package/client/vendor/paseo-plugin-helper/surface.tsx +0 -80
  121. package/client/vendor/paseo-plugin-helper/theme/flair.ts +0 -76
  122. package/client/vendor/paseo-plugin-helper/theme/host-variables.ts +0 -121
  123. package/client/vendor/paseo-plugin-helper/theme/index.ts +0 -7
  124. package/client/vendor/paseo-plugin-helper/theme/provider.tsx +0 -178
  125. package/client/vendor/paseo-plugin-helper/theme/responsive.ts +0 -213
  126. package/client/vendor/paseo-plugin-helper/theme/tokens.ts +0 -200
  127. package/client/vendor/paseo-plugin-helper/theme/useResponsive.ts +0 -57
  128. /package/client/vendor/paseo-plugin-helper/{command-center.ts → core/command-center.ts} +0 -0
  129. /package/client/vendor/paseo-plugin-helper/{snapshot.ts → core/snapshot.ts} +0 -0
  130. /package/client/vendor/paseo-plugin-helper/{utils → lifecycle}/haptics.ts +0 -0
@@ -1,172 +0,0 @@
1
- import React, { useState } from "react";
2
- import {
3
- Platform,
4
- Pressable,
5
- StyleSheet,
6
- Text,
7
- View,
8
- type StyleProp,
9
- type TextStyle,
10
- type ViewStyle,
11
- } from "react-native";
12
- import { getClientHost } from "../host";
13
- import { usePluginTheme } from "../theme/provider";
14
- import { copyToClipboard } from "../utils/clipboard";
15
-
16
- export interface CommandBoxProps {
17
- /** argv array — program is rendered bold, args muted */
18
- argv?: string[];
19
- /** Optional override to display a pre-joined string instead of argv */
20
- command?: string;
21
- style?: StyleProp<ViewStyle>;
22
- textStyle?: StyleProp<TextStyle>;
23
- /** Accessibility label override for the copy button */
24
- copyLabel?: string;
25
- }
26
-
27
- export function formatCommandLine(argv: string[]): string {
28
- return argv.map((arg) => (arg.includes(" ") ? JSON.stringify(arg) : arg)).join(" ");
29
- }
30
-
31
- export function CommandBox({
32
- argv = [],
33
- command,
34
- style,
35
- textStyle,
36
- copyLabel = "Copy command",
37
- }: CommandBoxProps): React.ReactElement | null {
38
- const { colors, resolveRadius } = usePluginTheme();
39
- const { Icon, useToast } = getClientHost();
40
- const toast = useToast();
41
- const [copied, setCopied] = useState(false);
42
-
43
- const fullCommand = command ?? formatCommandLine(argv);
44
- const [prog, ...rest] = argv;
45
-
46
- const handleCopy = async () => {
47
- if (!fullCommand) return;
48
- const ok = await copyToClipboard(fullCommand, {
49
- toast,
50
- toastMessage: "Command",
51
- });
52
- if (ok) {
53
- setCopied(true);
54
- setTimeout(() => setCopied(false), 2000);
55
- }
56
- };
57
-
58
- const fontFamily = Platform.select({
59
- ios: "Menlo",
60
- android: "monospace",
61
- default: "monospace",
62
- });
63
-
64
- const radius = resolveRadius("sm");
65
-
66
- return (
67
- <View
68
- style={[
69
- styles.container,
70
- {
71
- backgroundColor: colors.surface2,
72
- borderColor: colors.border,
73
- borderRadius: radius,
74
- },
75
- style,
76
- ]}
77
- >
78
- <View style={styles.textContainer}>
79
- <Text
80
- style={[
81
- styles.prompt,
82
- {
83
- color: colors.statusWarning,
84
- fontFamily,
85
- },
86
- ]}
87
- >
88
- $
89
- </Text>
90
- <Text
91
- selectable
92
- numberOfLines={2}
93
- style={[
94
- styles.commandText,
95
- {
96
- color: colors.foreground,
97
- fontFamily,
98
- },
99
- textStyle,
100
- ]}
101
- >
102
- {prog ? (
103
- <>
104
- <Text style={styles.bold}>{prog}</Text>
105
- {rest.length > 0 ? (
106
- <Text style={{ color: colors.foregroundMuted }}>
107
- {" " + rest.map((a) => (a.includes(" ") ? JSON.stringify(a) : a)).join(" ")}
108
- </Text>
109
- ) : null}
110
- </>
111
- ) : (
112
- command ?? ""
113
- )}
114
- </Text>
115
- </View>
116
- <Pressable
117
- onPress={handleCopy}
118
- accessibilityRole="button"
119
- accessibilityLabel={copyLabel}
120
- hitSlop={8}
121
- style={({ pressed }) => [
122
- styles.copyButton,
123
- {
124
- backgroundColor: pressed ? colors.surface1 : "transparent",
125
- borderRadius: Math.max(2, radius - 2),
126
- },
127
- ]}
128
- >
129
- <Icon
130
- name={copied ? "Check" : "Copy"}
131
- size={13}
132
- color={copied ? colors.statusSuccess : colors.foregroundMuted}
133
- />
134
- </Pressable>
135
- </View>
136
- );
137
- }
138
-
139
- const styles = StyleSheet.create({
140
- container: {
141
- flexDirection: "row",
142
- alignItems: "center",
143
- justifyContent: "space-between",
144
- borderWidth: 1,
145
- paddingHorizontal: 10,
146
- paddingVertical: 6,
147
- gap: 8,
148
- },
149
- textContainer: {
150
- flexDirection: "row",
151
- alignItems: "center",
152
- flex: 1,
153
- overflow: "hidden",
154
- gap: 6,
155
- },
156
- prompt: {
157
- fontWeight: "700",
158
- fontSize: 12,
159
- },
160
- commandText: {
161
- fontSize: 12,
162
- flex: 1,
163
- },
164
- bold: {
165
- fontWeight: "700",
166
- },
167
- copyButton: {
168
- padding: 4,
169
- alignItems: "center",
170
- justifyContent: "center",
171
- },
172
- });
@@ -1,180 +0,0 @@
1
- import React, { useEffect, useRef, useState } from "react";
2
- import {
3
- Pressable,
4
- StyleSheet,
5
- Text,
6
- type StyleProp,
7
- type TextStyle,
8
- type ViewStyle,
9
- } from "react-native";
10
- import { getClientHost } from "../host";
11
- import { usePluginTheme } from "../theme/provider";
12
- import { copyToClipboard } from "../utils/clipboard";
13
-
14
- export type CopyButtonSize = "sm" | "md";
15
- export type CopyButtonVariant = "ghost" | "secondary";
16
-
17
- export interface CopyButtonFeedback {
18
- icon: string;
19
- label: string;
20
- }
21
-
22
- /**
23
- * Pure idle/copied visual state, split out so the feedback contract is
24
- * testable without a renderer or a clipboard. Callers may override the idle
25
- * `icon`/`label`; the copied state is always the Check/"Copied!" affordance.
26
- */
27
- export function resolveCopyButtonFeedback(
28
- copied: boolean,
29
- options: { icon?: string; label?: string; copiedLabel?: string } = {},
30
- ): CopyButtonFeedback {
31
- const idleIcon = options.icon ?? "Copy";
32
- const idleLabel = options.label ?? "Copy";
33
- const copiedLabel = options.copiedLabel ?? "Copied!";
34
- return copied ? { icon: "Check", label: copiedLabel } : { icon: idleIcon, label: idleLabel };
35
- }
36
-
37
- export interface CopyButtonProps {
38
- /** Literal text to copy. Ignored when `getText` is provided. */
39
- text?: string;
40
- /** Lazy/Promise text source, resolved at press time. Takes precedence over `text`. */
41
- getText?: () => string | Promise<string>;
42
- /** Idle button label. Defaults to "Copy". */
43
- label?: string;
44
- /** Label shown after a successful copy. Defaults to "Copied!". */
45
- copiedLabel?: string;
46
- /** Icon name for the idle state. Defaults to "Copy". */
47
- icon?: string;
48
- size?: CopyButtonSize;
49
- variant?: CopyButtonVariant;
50
- accessibilityLabel?: string;
51
- /** Forwarded to the clipboard toast (e.g. "timeline card"). */
52
- toastMessage?: string;
53
- /** How long the Check/"Copied!" feedback stays up. Defaults to 2000ms. */
54
- feedbackDurationMs?: number;
55
- disabled?: boolean;
56
- style?: StyleProp<ViewStyle>;
57
- textStyle?: StyleProp<TextStyle>;
58
- }
59
-
60
- /**
61
- * Explicit copy affordance for styled/plugin surfaces.
62
- *
63
- * Web's selection-copy handler only rebuilds clipboard content for
64
- * `[data-testid="assistant-message"]` selections, so styled timeline, telemetry,
65
- * and panel content copies nothing (xpufx-org/paseo#278). This bypasses that
66
- * gate with the helper's host `copyText` / `copyToClipboard` path and shows
67
- * Check/"Copied!" feedback, matching CodeBlock's pattern.
68
- */
69
- export function CopyButton({
70
- text,
71
- getText,
72
- label,
73
- copiedLabel,
74
- icon,
75
- size = "sm",
76
- variant = "ghost",
77
- accessibilityLabel,
78
- toastMessage,
79
- feedbackDurationMs = 2000,
80
- disabled = false,
81
- style,
82
- textStyle,
83
- }: CopyButtonProps): React.ReactElement | null {
84
- const { Icon, useToast } = getClientHost();
85
- const { colors, resolveRadius, touchTargetMin, alpha } = usePluginTheme();
86
- const toast = useToast();
87
- const [copied, setCopied] = useState(false);
88
- const timer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
89
-
90
- useEffect(() => {
91
- return () => {
92
- if (timer.current) clearTimeout(timer.current);
93
- };
94
- }, []);
95
-
96
- const hasSource = getText !== undefined || text !== undefined;
97
- if (!hasSource) return null;
98
-
99
- const feedback = resolveCopyButtonFeedback(copied, { icon, label, copiedLabel });
100
- const iconNode = (
101
- <Icon
102
- name={feedback.icon}
103
- size={size === "sm" ? 12 : 14}
104
- color={copied ? colors.statusSuccess : colors.foregroundMuted}
105
- />
106
- );
107
-
108
- const secondary = variant === "secondary";
109
- const bg = secondary ? colors.surface1 : "transparent";
110
- const border = secondary ? colors.border : "transparent";
111
- const radius = resolveRadius(size === "sm" ? "sm" : "md");
112
-
113
- const handleCopy = async () => {
114
- if (disabled) return;
115
- let value: string | undefined;
116
- try {
117
- value = getText ? await getText() : text;
118
- } catch {
119
- return;
120
- }
121
- if (value === undefined || value === null) return;
122
-
123
- const ok = await copyToClipboard(String(value), { toast, toastMessage });
124
- if (!ok) return;
125
-
126
- setCopied(true);
127
- if (timer.current) clearTimeout(timer.current);
128
- timer.current = setTimeout(() => setCopied(false), feedbackDurationMs);
129
- };
130
-
131
- return (
132
- <Pressable
133
- onPress={handleCopy}
134
- disabled={disabled}
135
- accessibilityRole="button"
136
- accessibilityLabel={accessibilityLabel || label || "Copy"}
137
- hitSlop={Math.max(0, (touchTargetMin - 28) / 2)}
138
- style={({ pressed }) => [
139
- styles.base,
140
- {
141
- backgroundColor: pressed && !disabled ? alpha(colors.surface2, secondary ? 1 : 0.7) : bg,
142
- borderColor: border,
143
- borderWidth: border !== "transparent" ? 1 : 0,
144
- borderRadius: radius,
145
- paddingVertical: size === "sm" ? 3 : 5,
146
- paddingHorizontal: size === "sm" ? 8 : 10,
147
- opacity: disabled ? 0.45 : 1,
148
- },
149
- style,
150
- ]}
151
- >
152
- {iconNode}
153
- {feedback.label !== "" ? (
154
- <Text
155
- style={[
156
- styles.text,
157
- {
158
- fontSize: size === "sm" ? 11 : 12,
159
- color: copied ? colors.statusSuccess : colors.foregroundMuted,
160
- },
161
- textStyle,
162
- ]}
163
- >
164
- {feedback.label}
165
- </Text>
166
- ) : null}
167
- </Pressable>
168
- );
169
- }
170
-
171
- const styles = StyleSheet.create({
172
- base: {
173
- flexDirection: "row",
174
- alignItems: "center",
175
- gap: 4,
176
- },
177
- text: {
178
- fontWeight: "500",
179
- },
180
- });
@@ -1,97 +0,0 @@
1
- import React, { type ReactNode } from "react";
2
- import { StyleSheet, Text, View, type StyleProp, type ViewStyle } from "react-native";
3
- import { getClientHost } from "../host";
4
- import { usePluginTheme } from "../theme/provider";
5
- import { Button, type ButtonProps } from "./Button";
6
-
7
- export interface EmptyStateProps {
8
- icon?: string | ReactNode;
9
- title: string;
10
- description?: string;
11
- action?: ButtonProps;
12
- actionLabel?: string;
13
- onAction?: () => void;
14
- style?: StyleProp<ViewStyle>;
15
- }
16
-
17
- export function EmptyState({
18
- icon = "Inbox",
19
- title,
20
- description,
21
- action,
22
- actionLabel,
23
- onAction,
24
- style,
25
- }: EmptyStateProps) {
26
- const { Icon } = getClientHost();
27
- const { colors, isCompact, typography, padding } = usePluginTheme();
28
-
29
- const resolvedAction: ButtonProps | undefined = action
30
- ? action
31
- : actionLabel && onAction
32
- ? { label: actionLabel, onPress: onAction, variant: "secondary" }
33
- : undefined;
34
-
35
- return (
36
- <View style={[styles.container, { padding: isCompact ? padding.horizontal + 8 : padding.horizontal * 2 }, style]}>
37
- {icon ? (
38
- typeof icon === "string" ? (
39
- <View style={[styles.iconWrapper, { backgroundColor: colors.surface1 }]}>
40
- <Icon name={icon} size={isCompact ? 24 : 32} color={colors.foregroundMuted} />
41
- </View>
42
- ) : (
43
- icon
44
- )
45
- ) : null}
46
-
47
- <Text style={[styles.title, { color: colors.foreground, ...typography.title }]}>
48
- {title}
49
- </Text>
50
-
51
- {description ? (
52
- <Text
53
- style={[
54
- styles.description,
55
- { color: colors.foregroundMuted, ...typography.body },
56
- ]}
57
- >
58
- {description}
59
- </Text>
60
- ) : null}
61
-
62
- {resolvedAction ? (
63
- <View style={styles.actionRow}>
64
- <Button size={isCompact ? "sm" : "md"} {...resolvedAction} />
65
- </View>
66
- ) : null}
67
- </View>
68
- );
69
- }
70
-
71
- const styles = StyleSheet.create({
72
- container: {
73
- alignItems: "center",
74
- justifyContent: "center",
75
- gap: 8,
76
- },
77
- iconWrapper: {
78
- width: 56,
79
- height: 56,
80
- borderRadius: 28,
81
- alignItems: "center",
82
- justifyContent: "center",
83
- marginBottom: 4,
84
- },
85
- title: {
86
- fontWeight: "600",
87
- textAlign: "center",
88
- },
89
- description: {
90
- textAlign: "center",
91
- maxWidth: 280,
92
- lineHeight: 18,
93
- },
94
- actionRow: {
95
- marginTop: 8,
96
- },
97
- });
@@ -1,70 +0,0 @@
1
- import React, { useMemo } from "react";
2
- import { Text, type StyleProp, type TextStyle } from "react-native";
3
- import { splitHighlightParts } from "../../../../shared/vendor/paseo-plugin-helper/highlight";
4
- import { usePluginTheme } from "../theme/provider";
5
-
6
- export interface HighlightedTextProps {
7
- /** Source text rendered as-is when no query is active. */
8
- text: string;
9
- /**
10
- * Active search query; matched case-insensitively and never as a regex. When
11
- * the literal query is absent the query's tokens are highlighted, so a fuzzy
12
- * result still reads.
13
- */
14
- query: string;
15
- style?: StyleProp<TextStyle>;
16
- /**
17
- * Overrides the matched-run style. Defaults to the theme accent background
18
- * with `accentForeground` text so a match reads as selected.
19
- */
20
- highlightStyle?: StyleProp<TextStyle>;
21
- numberOfLines?: number;
22
- selectable?: boolean;
23
- /**
24
- * When true, a query with no literal or token hit marks the whole text, so a
25
- * fuzzy search result still reads as matched. Use it on the primary label
26
- * (row/detail title) rather than every small field.
27
- */
28
- fuzzyFallback?: boolean;
29
- }
30
-
31
- /**
32
- * `<Text>` that paints every case-insensitive occurrence of `query` with the
33
- * accent background/foreground. The query is matched literally, so user input
34
- * is never evaluated as a regular expression. When the literal query is absent
35
- * the splitter highlights the query's tokens; `fuzzyFallback` additionally
36
- * marks the whole text when even a token misses, so a fuzzy result is not left
37
- * silently unmarked. Renders the plain text when the query is empty or absent.
38
- */
39
- export function HighlightedText({
40
- text,
41
- query,
42
- style,
43
- highlightStyle,
44
- numberOfLines,
45
- selectable,
46
- fuzzyFallback = false,
47
- }: HighlightedTextProps) {
48
- const { colors } = usePluginTheme();
49
- const parts = useMemo(
50
- () => splitHighlightParts(text, query, { fallbackToWholeField: fuzzyFallback }),
51
- [text, query, fuzzyFallback],
52
- );
53
- const matchStyle: StyleProp<TextStyle> = [
54
- { backgroundColor: colors.accent, color: colors.accentForeground },
55
- highlightStyle,
56
- ];
57
- return (
58
- <Text style={style} numberOfLines={numberOfLines} selectable={selectable}>
59
- {parts.map((part, index) =>
60
- part.matched ? (
61
- <Text key={`m${index}`} style={matchStyle}>
62
- {part.text}
63
- </Text>
64
- ) : (
65
- part.text
66
- ),
67
- )}
68
- </Text>
69
- );
70
- }
@@ -1,75 +0,0 @@
1
- import React, { type ReactNode } from "react";
2
- import {
3
- Pressable,
4
- StyleSheet,
5
- Text,
6
- type StyleProp,
7
- type TextStyle,
8
- type ViewStyle,
9
- } from "react-native";
10
- import { getClientHost } from "../host";
11
- import { usePluginTheme } from "../theme/provider";
12
-
13
- export interface InlineButtonProps {
14
- label: string;
15
- onPress?: () => void | Promise<void>;
16
- icon?: string | ReactNode;
17
- disabled?: boolean;
18
- accessibilityLabel?: string;
19
- accessibilityRole?: "button" | "link";
20
- style?: StyleProp<ViewStyle>;
21
- textStyle?: StyleProp<TextStyle>;
22
- }
23
-
24
- /** Compact text/link action for inline cards and timeline content. */
25
- export function InlineButton({
26
- label,
27
- onPress,
28
- icon,
29
- disabled = false,
30
- accessibilityLabel,
31
- accessibilityRole = "button",
32
- style,
33
- textStyle,
34
- }: InlineButtonProps) {
35
- const { Icon } = getClientHost();
36
- const { colors, touchTargetMin, alpha } = usePluginTheme();
37
-
38
- return (
39
- <Pressable
40
- onPress={onPress}
41
- disabled={disabled}
42
- accessibilityRole={accessibilityRole}
43
- accessibilityLabel={accessibilityLabel || label}
44
- hitSlop={Math.max(0, (touchTargetMin - 24) / 2)}
45
- style={({ pressed }) => [
46
- styles.base,
47
- {
48
- opacity: disabled ? 0.45 : 1,
49
- backgroundColor: pressed && !disabled ? alpha(colors.accent, 0.12) : "transparent",
50
- },
51
- style,
52
- ]}
53
- >
54
- {typeof icon === "string" ? <Icon name={icon} size={13} color={colors.accent} /> : icon}
55
- <Text style={[styles.text, { color: colors.accent }, textStyle]}>{label}</Text>
56
- </Pressable>
57
- );
58
- }
59
-
60
- const styles = StyleSheet.create({
61
- base: {
62
- alignSelf: "flex-start",
63
- flexDirection: "row",
64
- alignItems: "center",
65
- gap: 4,
66
- minHeight: 24,
67
- paddingHorizontal: 3,
68
- paddingVertical: 2,
69
- borderRadius: 4,
70
- },
71
- text: {
72
- fontSize: 12,
73
- fontWeight: "600",
74
- },
75
- });