@xpufx/paseo-x-comms 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +149 -0
- package/client/attribution.ts +7 -0
- package/client/conversations.ts +229 -0
- package/client/main.tsx +885 -0
- package/client/peer-label.ts +98 -0
- package/client/peer-status.tsx +201 -0
- package/client/settings-prototype.tsx +395 -0
- package/client/tool-call.ts +115 -0
- package/client/vendor/paseo-plugin-helper/command-center.ts +43 -0
- package/client/vendor/paseo-plugin-helper/components/AboutSection.tsx +493 -0
- package/client/vendor/paseo-plugin-helper/components/AttentionBeacon.tsx +250 -0
- package/client/vendor/paseo-plugin-helper/components/Badge.tsx +156 -0
- package/client/vendor/paseo-plugin-helper/components/Button.tsx +178 -0
- package/client/vendor/paseo-plugin-helper/components/Card.tsx +225 -0
- package/client/vendor/paseo-plugin-helper/components/CodeBlock.tsx +196 -0
- package/client/vendor/paseo-plugin-helper/components/Collapsible.tsx +277 -0
- package/client/vendor/paseo-plugin-helper/components/CommandBox.tsx +172 -0
- package/client/vendor/paseo-plugin-helper/components/CopyButton.tsx +180 -0
- package/client/vendor/paseo-plugin-helper/components/DataTable.tsx +200 -0
- package/client/vendor/paseo-plugin-helper/components/EmptyState.tsx +97 -0
- package/client/vendor/paseo-plugin-helper/components/HighlightedText.tsx +70 -0
- package/client/vendor/paseo-plugin-helper/components/InlineButton.tsx +73 -0
- package/client/vendor/paseo-plugin-helper/components/KeyValue.tsx +446 -0
- package/client/vendor/paseo-plugin-helper/components/MetricGauge.tsx +247 -0
- package/client/vendor/paseo-plugin-helper/components/ProgressBar.tsx +117 -0
- package/client/vendor/paseo-plugin-helper/components/Responsive.tsx +53 -0
- package/client/vendor/paseo-plugin-helper/components/SearchInput.tsx +118 -0
- package/client/vendor/paseo-plugin-helper/components/SectionHeader.tsx +80 -0
- package/client/vendor/paseo-plugin-helper/components/Select.tsx +215 -0
- package/client/vendor/paseo-plugin-helper/components/StatusDot.tsx +80 -0
- package/client/vendor/paseo-plugin-helper/components/Tabs.tsx +319 -0
- package/client/vendor/paseo-plugin-helper/components/TextInput.tsx +150 -0
- package/client/vendor/paseo-plugin-helper/components/Toggle.tsx +163 -0
- package/client/vendor/paseo-plugin-helper/components/TruncatedText.tsx +157 -0
- package/client/vendor/paseo-plugin-helper/components/index.ts +25 -0
- package/client/vendor/paseo-plugin-helper/custom-pills.tsx +224 -0
- package/client/vendor/paseo-plugin-helper/forge-icon.tsx +79 -0
- package/client/vendor/paseo-plugin-helper/host.ts +277 -0
- package/client/vendor/paseo-plugin-helper/icon.tsx +39 -0
- package/client/vendor/paseo-plugin-helper/index.ts +28 -0
- package/client/vendor/paseo-plugin-helper/layout/ActionBar.tsx +49 -0
- package/client/vendor/paseo-plugin-helper/layout/FormRow.tsx +103 -0
- package/client/vendor/paseo-plugin-helper/layout/Grid.tsx +65 -0
- package/client/vendor/paseo-plugin-helper/layout/ModalBody.tsx +378 -0
- package/client/vendor/paseo-plugin-helper/layout/ModalContent.tsx +49 -0
- package/client/vendor/paseo-plugin-helper/layout/Row.tsx +39 -0
- package/client/vendor/paseo-plugin-helper/layout/Stack.tsx +39 -0
- package/client/vendor/paseo-plugin-helper/layout/index.ts +7 -0
- package/client/vendor/paseo-plugin-helper/panel.tsx +81 -0
- package/client/vendor/paseo-plugin-helper/pill.tsx +884 -0
- package/client/vendor/paseo-plugin-helper/query-refresh.ts +79 -0
- package/client/vendor/paseo-plugin-helper/query.ts +66 -0
- package/client/vendor/paseo-plugin-helper/settings-screen.tsx +372 -0
- package/client/vendor/paseo-plugin-helper/settings.ts +181 -0
- package/client/vendor/paseo-plugin-helper/shared-settings.ts +46 -0
- package/client/vendor/paseo-plugin-helper/snapshot.ts +68 -0
- package/client/vendor/paseo-plugin-helper/surface.tsx +80 -0
- package/client/vendor/paseo-plugin-helper/theme/color-utils.ts +118 -0
- package/client/vendor/paseo-plugin-helper/theme/flair.ts +76 -0
- package/client/vendor/paseo-plugin-helper/theme/host-variables.ts +121 -0
- package/client/vendor/paseo-plugin-helper/theme/index.ts +7 -0
- package/client/vendor/paseo-plugin-helper/theme/provider.tsx +214 -0
- package/client/vendor/paseo-plugin-helper/theme/responsive.ts +213 -0
- package/client/vendor/paseo-plugin-helper/theme/tokens.ts +161 -0
- package/client/vendor/paseo-plugin-helper/theme/useResponsive.ts +57 -0
- package/client/vendor/paseo-plugin-helper/utils/clipboard.ts +149 -0
- package/client/vendor/paseo-plugin-helper/utils/haptics.ts +34 -0
- package/client/via-x-comms.tsx +15 -0
- package/client/x-comms-conversation.tsx +449 -0
- package/client/x-comms-panel.tsx +6 -0
- package/client/x-comms-pill.tsx +193 -0
- package/client/x-comms-timeline.tsx +176 -0
- package/client/x-comms-tool-call.tsx +85 -0
- package/docs/mesh.md +115 -0
- package/mcp/LICENSE +202 -0
- package/mcp/README.md +287 -0
- package/mcp/mcp-config.example.json +8 -0
- package/mcp/package-lock.json +1186 -0
- package/mcp/package.json +43 -0
- package/mcp/paseo-cross-daemon-comms.example.json +5 -0
- package/mcp/paseo-x-comms.bundled.mjs +36964 -0
- package/mcp/paseo-x-comms.mjs +630 -0
- package/mcp/test/fixtures/extensions/block/10-block.mjs +4 -0
- package/mcp/test/fixtures/extensions/broken-hook/10-thrower.mjs +6 -0
- package/mcp/test/fixtures/extensions/broken-hook/20-good.mjs +4 -0
- package/mcp/test/fixtures/extensions/broken-load/10-thrower.mjs +4 -0
- package/mcp/test/fixtures/extensions/broken-load/20-good.mjs +4 -0
- package/mcp/test/fixtures/extensions/custom-tool/10-tool.mjs +12 -0
- package/mcp/test/fixtures/extensions/tool-block/10-block.mjs +8 -0
- package/mcp/test/fixtures/extensions/transform/10-transform.mjs +15 -0
- package/mcp/test/fixtures/fake-paseo.mjs +92 -0
- package/mcp/test/register-ts-hooks.mjs +6 -0
- package/mcp/test/resolve-ts-hooks.mjs +46 -0
- package/package.json +58 -0
- package/paseo-plugin.json +7 -0
- package/server/conversations-snapshot.ts +249 -0
- package/server/handlers.ts +1081 -0
- package/server/injection.ts +152 -0
- package/server/local-send.ts +153 -0
- package/server/mcp-client.ts +47 -0
- package/server/outbox.ts +233 -0
- package/server/peer-channel.ts +151 -0
- package/server/peer-status.ts +233 -0
- package/server/presence.ts +204 -0
- package/server/registry.ts +215 -0
- package/server/relay-status.ts +9 -0
- package/server/server-status.ts +138 -0
- package/server/settings.ts +81 -0
- package/server/snapshot.ts +176 -0
- package/server/vendor/paseo-plugin-helper/agent.ts +85 -0
- package/server/vendor/paseo-plugin-helper/custom-pills.ts +344 -0
- package/server/vendor/paseo-plugin-helper/index.ts +18 -0
- package/server/vendor/paseo-plugin-helper/jsonc.ts +78 -0
- package/server/vendor/paseo-plugin-helper/logger.ts +210 -0
- package/server/vendor/paseo-plugin-helper/mcp/client.ts +349 -0
- package/server/vendor/paseo-plugin-helper/mcp/http-client.ts +399 -0
- package/server/vendor/paseo-plugin-helper/mcp/index.ts +5 -0
- package/server/vendor/paseo-plugin-helper/mcp/process-killer.ts +65 -0
- package/server/vendor/paseo-plugin-helper/mcp/ring-buffer.ts +29 -0
- package/server/vendor/paseo-plugin-helper/mcp/types.ts +41 -0
- package/server/vendor/paseo-plugin-helper/mcp-config.ts +367 -0
- package/server/vendor/paseo-plugin-helper/mcp-injection.ts +85 -0
- package/server/vendor/paseo-plugin-helper/network.ts +91 -0
- package/server/vendor/paseo-plugin-helper/plugins.ts +160 -0
- package/server/vendor/paseo-plugin-helper/process.ts +186 -0
- package/server/vendor/paseo-plugin-helper/redact.ts +86 -0
- package/server/vendor/paseo-plugin-helper/rpc-guard.ts +77 -0
- package/server/vendor/paseo-plugin-helper/settings.ts +97 -0
- package/server/vendor/paseo-plugin-helper/shared-settings.ts +243 -0
- package/server/vendor/paseo-plugin-helper/storage.ts +244 -0
- package/server/vendor/paseo-plugin-helper/system.ts +128 -0
- package/server/vendor/paseo-plugin-helper/task.ts +116 -0
- package/server/vendor/paseo-plugin-helper/version.ts +153 -0
- package/server/vendor/paseo-plugin-helper/workspace-beacon.ts +418 -0
- package/shared/conversations-snapshot.ts +39 -0
- package/shared/envelope.ts +92 -0
- package/shared/outbox.ts +21 -0
- package/shared/registry.ts +366 -0
- package/shared/vendor/paseo-plugin-helper/README.md +11 -0
- package/shared/vendor/paseo-plugin-helper/async.ts +35 -0
- package/shared/vendor/paseo-plugin-helper/custom-pills.ts +169 -0
- package/shared/vendor/paseo-plugin-helper/forge.ts +110 -0
- package/shared/vendor/paseo-plugin-helper/formatters.ts +271 -0
- package/shared/vendor/paseo-plugin-helper/highlight.ts +184 -0
- package/shared/vendor/paseo-plugin-helper/index.ts +10 -0
- package/shared/vendor/paseo-plugin-helper/rpc.ts +72 -0
- package/shared/vendor/paseo-plugin-helper/settings.ts +138 -0
- package/shared/vendor/paseo-plugin-helper/suite-settings.ts +17 -0
- package/shared/vendor/paseo-plugin-helper/suppressed.ts +31 -0
- package/shared/vendor/paseo-plugin-helper/types.ts +36 -0
- package/shared/version.ts +2 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Image, Platform, type ImageStyle, type StyleProp } from "react-native";
|
|
3
|
+
import { Icon } from "./icon";
|
|
4
|
+
import { usePluginTheme } from "./theme/provider";
|
|
5
|
+
import {
|
|
6
|
+
resolveForgeMark,
|
|
7
|
+
type ForgeKind,
|
|
8
|
+
type ForgeMarkInput,
|
|
9
|
+
} from "../../../shared/vendor/paseo-plugin-helper/forge";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Official monochrome forge marks (Simple Icons, CC0 / project trademarks),
|
|
13
|
+
* authored on a 24x24 viewBox. Rendered as inline SVG through a data URI so
|
|
14
|
+
* the helper needs no SVG dependency: exact vector marks on web/Electron,
|
|
15
|
+
* where Paseo renders React Native Web.
|
|
16
|
+
*/
|
|
17
|
+
const FORGE_MARK_PATHS: Partial<Record<ForgeKind, string>> = {
|
|
18
|
+
codeberg:
|
|
19
|
+
"M11.999.747A11.974 11.974 0 0 0 0 12.75c0 2.254.635 4.465 1.833 6.376L11.837 6.19c.072-.092.251-.092.323 0l4.178 5.402h-2.992l.065.239h3.113l.882 1.138h-3.674l.103.374h3.86l.777 1.003h-4.358l.135.483h4.593l.695.894h-5.038l.165.589h5.326l.609.785h-5.717l.182.65h6.038l.562.727h-6.397l.183.65h6.717A12.003 12.003 0 0 0 24 12.75 11.977 11.977 0 0 0 11.999.747zm3.654 19.104.182.65h5.326c.173-.204.353-.433.513-.65zm.385 1.377.18.65h3.563c.233-.198.485-.428.712-.65zm.383 1.377.182.648h1.203c.356-.204.685-.412 1.042-.648zz",
|
|
20
|
+
forgejo:
|
|
21
|
+
"M16.7773 0c1.6018 0 2.9004 1.2986 2.9004 2.9005s-1.2986 2.9004-2.9004 2.9004c-1.0854 0-2.0315-.596-2.5288-1.4787H12.91c-2.3322 0-4.2272 1.8718-4.2649 4.195l-.0007 2.1175a7.0759 7.0759 0 0 1 4.148-1.4205l.1176-.001 1.3385.0002c.4973-.8827 1.4434-1.4788 2.5288-1.4788 1.6018 0 2.9004 1.2986 2.9004 2.9005s-1.2986 2.9004-2.9004 2.9004c-1.0854 0-2.0315-.596-2.5288-1.4787H12.91c-2.3322 0-4.2272 1.8718-4.2649 4.195l-.0007 2.319c.8827.4973 1.4788 1.4434 1.4788 2.5287 0 1.602-1.2986 2.9005-2.9005 2.9005-1.6018 0-2.9004-1.2986-2.9004-2.9005 0-1.0853.596-2.0314 1.4788-2.5287l-.0002-9.9831c0-3.887 3.1195-7.0453 6.9915-7.108l.1176-.001h1.3385C14.7458.5962 15.692 0 16.7773 0ZM7.2227 19.9052c-.6596 0-1.1943.5347-1.1943 1.1943s.5347 1.1943 1.1943 1.1943 1.1944-.5347 1.1944-1.1943-.5348-1.1943-1.1944-1.1943Zm9.5546-10.4644c-.6596 0-1.1944.5347-1.1944 1.1943s.5348 1.1943 1.1944 1.1943c.6596 0 1.1943-.5347 1.1943-1.1943s-.5347-1.1943-1.1943-1.1943Zm0-7.7346c-.6596 0-1.1944.5347-1.1944 1.1943s.5348 1.1943 1.1944 1.1943c.6596 0 1.1943-.5347 1.1943-1.1943s-.5347-1.1943-1.1943-1.1943Z",
|
|
22
|
+
gitea:
|
|
23
|
+
"M4.209 4.603c-.247 0-.525.02-.84.088-.333.07-1.28.283-2.054 1.027C-.403 7.25.035 9.685.089 10.052c.065.446.263 1.687 1.21 2.768 1.749 2.141 5.513 2.092 5.513 2.092s.462 1.103 1.168 2.119c.955 1.263 1.936 2.248 2.89 2.367 2.406 0 7.212-.004 7.212-.004s.458.004 1.08-.394c.535-.324 1.013-.893 1.013-.893s.492-.527 1.18-1.73c.21-.37.385-.729.538-1.068 0 0 2.107-4.471 2.107-8.823-.042-1.318-.367-1.55-.443-1.627-.156-.156-.366-.153-.366-.153s-4.475.252-6.792.306c-.508.011-1.012.023-1.512.027v4.474l-.634-.301c0-1.39-.004-4.17-.004-4.17-1.107.016-3.405-.084-3.405-.084s-5.399-.27-5.987-.324c-.187-.011-.401-.032-.648-.032zm.354 1.832h.111s.271 2.269.6 3.597C5.549 11.147 6.22 13 6.22 13s-.996-.119-1.641-.348c-.99-.324-1.409-.714-1.409-.714s-.73-.511-1.096-1.52C1.444 8.73 2.021 7.7 2.021 7.7s.32-.859 1.47-1.145c.395-.106.863-.12 1.072-.12zm8.33 2.554c.26.003.509.127.509.127l.868.422-.529 1.075a.686.686 0 0 0-.614.359.685.685 0 0 0 .072.756l-.939 1.924a.69.69 0 0 0-.66.527.687.687 0 0 0 .347.763.686.686 0 0 0 .867-.206.688.688 0 0 0-.069-.882l.916-1.874a.667.667 0 0 0 .237-.02.657.657 0 0 0 .271-.137 8.826 8.826 0 0 1 1.016.512.761.761 0 0 1 .286.282c.073.21-.073.569-.073.569-.087.29-.702 1.55-.702 1.55a.692.692 0 0 0-.676.477.681.681 0 1 0 1.157-.252c.073-.141.141-.282.214-.431.19-.397.515-1.16.515-1.16.035-.066.218-.394.103-.814-.095-.435-.48-.638-.48-.638-.467-.301-1.116-.58-1.116-.58s0-.156-.042-.27a.688.688 0 0 0-.148-.241l.516-1.062 2.89 1.401s.48.218.583.619c.073.282-.019.534-.069.657-.24.587-2.1 4.317-2.1 4.317s-.232.554-.748.588a1.065 1.065 0 0 1-.393-.045l-.202-.08-4.31-2.1s-.417-.218-.49-.596c-.083-.31.104-.691.104-.691l2.073-4.272s.183-.37.466-.497a.855.855 0 0 1 .35-.077z",
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/** Inline SVG data URI for a custom mark, tinted with the resolved color. */
|
|
27
|
+
export function forgeMarkSource(kind: ForgeKind, color: string): { uri: string } | null {
|
|
28
|
+
const path = FORGE_MARK_PATHS[kind];
|
|
29
|
+
if (!path) return null;
|
|
30
|
+
const svg =
|
|
31
|
+
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">` +
|
|
32
|
+
`<path fill="${color}" d="${path}"/></svg>`;
|
|
33
|
+
return { uri: `data:image/svg+xml,${encodeURIComponent(svg)}` };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface ForgeIconProps extends ForgeMarkInput {
|
|
37
|
+
/** Icon box in points; defaults to 16 to match the host Lucide default. */
|
|
38
|
+
size?: number;
|
|
39
|
+
/** Mark color; defaults to the active theme foreground. */
|
|
40
|
+
color?: string;
|
|
41
|
+
style?: StyleProp<ImageStyle>;
|
|
42
|
+
/** Overrides the default forge-name accessibility label. */
|
|
43
|
+
accessibilityLabel?: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Shared forge brand mark. Resolves a host/kind to one mark and renders it:
|
|
48
|
+
* GitHub/GitLab/unknown delegate to the host Lucide set, while Codeberg,
|
|
49
|
+
* Forgejo and Gitea draw their official mono marks inline. On native, where
|
|
50
|
+
* Paseo plugin bundles cannot render SVG, custom marks fall back to their
|
|
51
|
+
* closest Lucide glyph so forges stay distinct.
|
|
52
|
+
*/
|
|
53
|
+
export function ForgeIcon({
|
|
54
|
+
host,
|
|
55
|
+
kind,
|
|
56
|
+
size = 16,
|
|
57
|
+
color,
|
|
58
|
+
style,
|
|
59
|
+
accessibilityLabel,
|
|
60
|
+
}: ForgeIconProps) {
|
|
61
|
+
const { colors } = usePluginTheme();
|
|
62
|
+
const markColor = color ?? colors.foreground;
|
|
63
|
+
const mark = resolveForgeMark({ host, kind });
|
|
64
|
+
const source = mark.custom && Platform.OS === "web" ? forgeMarkSource(mark.kind, markColor) : null;
|
|
65
|
+
|
|
66
|
+
if (!source) {
|
|
67
|
+
return <Icon name={mark.lucideName} size={size} color={markColor} />;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<Image
|
|
72
|
+
accessibilityLabel={accessibilityLabel ?? mark.label}
|
|
73
|
+
accessibilityRole="image"
|
|
74
|
+
accessible
|
|
75
|
+
source={source}
|
|
76
|
+
style={[{ width: size, height: size }, style]}
|
|
77
|
+
/>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ComponentType,
|
|
3
|
+
ForwardRefExoticComponent,
|
|
4
|
+
ReactElement,
|
|
5
|
+
ReactNode,
|
|
6
|
+
Ref,
|
|
7
|
+
RefAttributes,
|
|
8
|
+
} from "react";
|
|
9
|
+
import type {
|
|
10
|
+
FlatList as RNFlatList,
|
|
11
|
+
FlatListProps,
|
|
12
|
+
ScrollView as RNScrollView,
|
|
13
|
+
ScrollViewProps,
|
|
14
|
+
TextInput as RNTextInput,
|
|
15
|
+
TextInputProps,
|
|
16
|
+
} from "react-native";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Structural host types for Paseo client integration.
|
|
20
|
+
*
|
|
21
|
+
* These interfaces describe the shapes `paseo-plugin-helper/client` needs
|
|
22
|
+
* from the Paseo host app. They are intentionally decoupled from any
|
|
23
|
+
* Paseo SDK version: this module (and every client module built on
|
|
24
|
+
* it) contains zero Paseo SDK imports, so a single published helper
|
|
25
|
+
* bundle satisfies both the Paseo v0.7 SDK entry points and the Paseo
|
|
26
|
+
* v0.8 runtime-owned entry points.
|
|
27
|
+
*
|
|
28
|
+
* The plugin author provides the real host implementations once, in the
|
|
29
|
+
* client entry, using whichever specifiers match their installed SDK.
|
|
30
|
+
* See docs/client.md for the per-version import paths.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
export interface HostThemeColors {
|
|
34
|
+
surface0: string;
|
|
35
|
+
surface1: string;
|
|
36
|
+
surface2: string;
|
|
37
|
+
border: string;
|
|
38
|
+
foreground: string;
|
|
39
|
+
foregroundMuted: string;
|
|
40
|
+
accent: string;
|
|
41
|
+
accentForeground: string;
|
|
42
|
+
statusSuccess: string;
|
|
43
|
+
statusWarning: string;
|
|
44
|
+
statusDanger: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface HostTheme {
|
|
48
|
+
colors: HostThemeColors;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface HostLayout {
|
|
52
|
+
compact: boolean;
|
|
53
|
+
platform: "ios" | "android" | "web";
|
|
54
|
+
width?: number;
|
|
55
|
+
height?: number;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface HostIconProps {
|
|
59
|
+
name: string;
|
|
60
|
+
size?: number;
|
|
61
|
+
color?: string;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export type HostIcon = ComponentType<HostIconProps>;
|
|
65
|
+
|
|
66
|
+
export interface HostModalContentProps {
|
|
67
|
+
children: ReactNode;
|
|
68
|
+
scrollable?: boolean;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface HostModalProps {
|
|
72
|
+
title: string;
|
|
73
|
+
icon?: ReactNode;
|
|
74
|
+
open: boolean;
|
|
75
|
+
onOpenChange(open: boolean): void;
|
|
76
|
+
children: ReactNode;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export type HostModal = ComponentType<HostModalProps> & {
|
|
80
|
+
Content: ComponentType<HostModalContentProps>;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export interface HostToast {
|
|
84
|
+
show?: (message: string, options?: any) => void;
|
|
85
|
+
copied?: (label?: string) => void;
|
|
86
|
+
error?: (message: string) => void;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export type HostUseToast = () => HostToast;
|
|
90
|
+
|
|
91
|
+
export interface HostRpcContract {
|
|
92
|
+
name: string;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Takes `any` contract deliberately: both SDK generations type their own
|
|
96
|
+
// contract shapes, and structural acceptance in both directions only holds
|
|
97
|
+
// for `any`. Call sites in useRpcQuery/usePluginSettings keep full typing
|
|
98
|
+
// through their own generics.
|
|
99
|
+
export type HostUseRpc = (contract: any) => (input: any) => Promise<any>;
|
|
100
|
+
|
|
101
|
+
export interface HostAgentRef {
|
|
102
|
+
id: string;
|
|
103
|
+
workspaceId?: string | null;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export type HostAgentUpdate =
|
|
107
|
+
| { kind: "remove"; agentId: string }
|
|
108
|
+
| { kind: string; agent: HostAgentRef };
|
|
109
|
+
|
|
110
|
+
export interface HostAgentsApi {
|
|
111
|
+
subscribe(cb: (update: HostAgentUpdate) => void): () => void;
|
|
112
|
+
list(): Promise<{ entries: Array<{ agent: HostAgentRef }> }>;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export type PluginCleanup = () => void;
|
|
116
|
+
|
|
117
|
+
export type HostCopyText = (text: string) => Promise<void>;
|
|
118
|
+
|
|
119
|
+
export type HostScrollView = ForwardRefExoticComponent<
|
|
120
|
+
ScrollViewProps & RefAttributes<RNScrollView>
|
|
121
|
+
>;
|
|
122
|
+
|
|
123
|
+
export type HostFlatList = <ItemT>(
|
|
124
|
+
props: FlatListProps<ItemT> & { ref?: Ref<RNFlatList<ItemT>> },
|
|
125
|
+
) => ReactElement;
|
|
126
|
+
|
|
127
|
+
export type HostTextInput = ForwardRefExoticComponent<
|
|
128
|
+
TextInputProps & RefAttributes<RNTextInput>
|
|
129
|
+
>;
|
|
130
|
+
|
|
131
|
+
export interface ClientHostDeps {
|
|
132
|
+
Icon: HostIcon;
|
|
133
|
+
Modal: HostModal;
|
|
134
|
+
useRpc: HostUseRpc;
|
|
135
|
+
useToast: HostUseToast;
|
|
136
|
+
copyText?: HostCopyText;
|
|
137
|
+
ScrollView?: HostScrollView;
|
|
138
|
+
FlatList?: HostFlatList;
|
|
139
|
+
TextInput?: HostTextInput;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
let deps: ClientHostDeps | undefined;
|
|
143
|
+
|
|
144
|
+
export function initClientHelpers(host: ClientHostDeps): void {
|
|
145
|
+
deps = host;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function getClientHost(): ClientHostDeps {
|
|
149
|
+
if (!deps) {
|
|
150
|
+
throw new Error(
|
|
151
|
+
"paseo-plugin-helper/client used before initClientHelpers(). " +
|
|
152
|
+
"Call initClientHelpers({ Icon, Modal, useRpc, useToast }) in the plugin client entry.",
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
return deps;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export function getOptionalClientHost(): ClientHostDeps | undefined {
|
|
159
|
+
return deps;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Single precedence rule for scrollable surfaces: the host-injected
|
|
164
|
+
* ScrollView (sheet-aware on Paseo v0.8, cooperates with bottom-sheet
|
|
165
|
+
* gestures) wins whenever the plugin supplied one to initClientHelpers;
|
|
166
|
+
* otherwise plain React Native ScrollView (pre-0.8 fallback, where the
|
|
167
|
+
* caller avoids nesting inside host scrollers itself).
|
|
168
|
+
*/
|
|
169
|
+
export function selectHostScrollView(
|
|
170
|
+
host: Pick<ClientHostDeps, "ScrollView"> | undefined,
|
|
171
|
+
fallback: HostScrollView,
|
|
172
|
+
): HostScrollView {
|
|
173
|
+
return host?.ScrollView ?? fallback;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export interface HostPillProps {
|
|
177
|
+
agentId: string;
|
|
178
|
+
workspaceId: string;
|
|
179
|
+
theme: HostTheme;
|
|
180
|
+
layout: HostLayout;
|
|
181
|
+
host: {
|
|
182
|
+
id: string;
|
|
183
|
+
label: string;
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export interface HostWorkspacePanelProps {
|
|
188
|
+
context: "workspace";
|
|
189
|
+
workspaceId: string;
|
|
190
|
+
theme: HostTheme;
|
|
191
|
+
layout: HostLayout;
|
|
192
|
+
host: {
|
|
193
|
+
id: string;
|
|
194
|
+
label: string;
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export interface HostAgentPanelProps {
|
|
199
|
+
context: "agent";
|
|
200
|
+
workspaceId: string;
|
|
201
|
+
agentId: string;
|
|
202
|
+
theme: HostTheme;
|
|
203
|
+
layout: HostLayout;
|
|
204
|
+
host: {
|
|
205
|
+
id: string;
|
|
206
|
+
label: string;
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export interface HostSurfaceProps {
|
|
211
|
+
theme: HostTheme;
|
|
212
|
+
layout: HostLayout;
|
|
213
|
+
host: {
|
|
214
|
+
id: string;
|
|
215
|
+
label: string;
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export interface ComposerPillContribution {
|
|
220
|
+
id: string;
|
|
221
|
+
title: string;
|
|
222
|
+
workspaceId: string;
|
|
223
|
+
agentId: string;
|
|
224
|
+
Component: ComponentType<HostPillProps>;
|
|
225
|
+
onPress(): void | Promise<void>;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export type ComposerPillButtonIcon = string | ComponentType<any>;
|
|
229
|
+
|
|
230
|
+
export interface ComposerPillButtonDescriptor {
|
|
231
|
+
title: string;
|
|
232
|
+
icon: ComposerPillButtonIcon;
|
|
233
|
+
label?: string;
|
|
234
|
+
visible?: boolean;
|
|
235
|
+
disabled?: boolean;
|
|
236
|
+
behavior:
|
|
237
|
+
| { kind: "action"; onPress(): void | Promise<void> }
|
|
238
|
+
| { kind: "popover"; Content: ComponentType<any> };
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export interface ComposerPillButtonContribution {
|
|
242
|
+
id: string;
|
|
243
|
+
workspaceId: string;
|
|
244
|
+
agentId: string;
|
|
245
|
+
button: ComposerPillButtonDescriptor;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export interface ComposerPillSdkContribution {
|
|
249
|
+
id: string;
|
|
250
|
+
workspaceId: string;
|
|
251
|
+
agentId: string;
|
|
252
|
+
button: Record<string, any>;
|
|
253
|
+
[key: string]: any;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export interface ComposerPillRegistrationHandle {
|
|
257
|
+
update(patch: Record<string, any>): void;
|
|
258
|
+
remove(): void;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export type ComposerPillRegistration = PluginCleanup | ComposerPillRegistrationHandle;
|
|
262
|
+
|
|
263
|
+
export interface ComposerPillRegistrar {
|
|
264
|
+
addComposerPill(
|
|
265
|
+
contribution:
|
|
266
|
+
| ComposerPillContribution
|
|
267
|
+
| ComposerPillButtonContribution
|
|
268
|
+
| ComposerPillSdkContribution,
|
|
269
|
+
): ComposerPillRegistration;
|
|
270
|
+
paseo: {
|
|
271
|
+
agents: HostAgentsApi;
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
export function isClientHostInitialized(): boolean {
|
|
276
|
+
return deps !== undefined;
|
|
277
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { getClientHost, type HostIconProps } from "./host";
|
|
3
|
+
|
|
4
|
+
let knownIconNames: Set<string> | undefined;
|
|
5
|
+
const warnedIconNames = new Set<string>();
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Optionally registers the set of valid icon names (e.g. Lucide names
|
|
9
|
+
* supported by the host). When set, Icon warns once per unknown name
|
|
10
|
+
* instead of rendering nothing silently.
|
|
11
|
+
*/
|
|
12
|
+
export function setKnownIconNames(names: Iterable<string>): void {
|
|
13
|
+
knownIconNames = new Set(names);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function clearKnownIconNames(): void {
|
|
17
|
+
knownIconNames = undefined;
|
|
18
|
+
warnedIconNames.clear();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function reportUnknownIcon(name: string): void {
|
|
22
|
+
const key = name.trim();
|
|
23
|
+
if (warnedIconNames.has(key)) return;
|
|
24
|
+
warnedIconNames.add(key);
|
|
25
|
+
console.warn(
|
|
26
|
+
`[paseo-plugin-helper Icon] unknown icon name "${name}" — renders nothing; check the Lucide name`,
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function Icon(props: HostIconProps) {
|
|
31
|
+
const rawName = typeof props.name === "string" ? props.name : "";
|
|
32
|
+
if (!rawName.trim()) {
|
|
33
|
+
reportUnknownIcon(String(props.name));
|
|
34
|
+
} else if (knownIconNames && !knownIconNames.has(rawName.trim())) {
|
|
35
|
+
reportUnknownIcon(rawName);
|
|
36
|
+
}
|
|
37
|
+
const { Icon: HostIconComponent } = getClientHost();
|
|
38
|
+
return <HostIconComponent {...props} />;
|
|
39
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export * from "./theme/index";
|
|
2
|
+
export * from "./components/index";
|
|
3
|
+
export * from "./layout/index";
|
|
4
|
+
export * from "./pill";
|
|
5
|
+
export * from "./surface";
|
|
6
|
+
export * from "./command-center";
|
|
7
|
+
export * from "./panel";
|
|
8
|
+
export * from "./query";
|
|
9
|
+
export * from "./query-refresh";
|
|
10
|
+
export * from "./snapshot";
|
|
11
|
+
export * from "./settings";
|
|
12
|
+
export * from "./shared-settings";
|
|
13
|
+
export * from "./settings-screen";
|
|
14
|
+
export * from "./utils/clipboard";
|
|
15
|
+
export * from "./utils/haptics";
|
|
16
|
+
export * from "./custom-pills";
|
|
17
|
+
export * from "./host";
|
|
18
|
+
export { Icon } from "./icon";
|
|
19
|
+
export { ForgeIcon, forgeMarkSource, type ForgeIconProps } from "./forge-icon";
|
|
20
|
+
export {
|
|
21
|
+
forgeKindFromHost,
|
|
22
|
+
isForgeKind,
|
|
23
|
+
normalizeForgeHost,
|
|
24
|
+
resolveForgeMark,
|
|
25
|
+
type ForgeKind,
|
|
26
|
+
type ForgeMarkInput,
|
|
27
|
+
type ResolvedForgeMark,
|
|
28
|
+
} from "../../../shared/vendor/paseo-plugin-helper/forge";
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import React, { type ReactNode } from "react";
|
|
2
|
+
import { StyleSheet, View, type StyleProp, type ViewStyle } from "react-native";
|
|
3
|
+
import { usePluginTheme } from "../theme/provider";
|
|
4
|
+
|
|
5
|
+
export interface ActionBarProps {
|
|
6
|
+
children: ReactNode;
|
|
7
|
+
align?: "flex-start" | "flex-end" | "center" | "space-between";
|
|
8
|
+
direction?: "row" | "column" | "auto";
|
|
9
|
+
style?: StyleProp<ViewStyle>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Responsive action toolbar for modals and surfaces.
|
|
14
|
+
* Automatically wraps or stacks on compact/mobile layouts.
|
|
15
|
+
*/
|
|
16
|
+
export function ActionBar({
|
|
17
|
+
children,
|
|
18
|
+
align = "flex-end",
|
|
19
|
+
direction = "auto",
|
|
20
|
+
style,
|
|
21
|
+
}: ActionBarProps) {
|
|
22
|
+
const { isCompact, padding } = usePluginTheme();
|
|
23
|
+
|
|
24
|
+
const isColumn = direction === "column" || (direction === "auto" && isCompact);
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<View
|
|
28
|
+
style={[
|
|
29
|
+
styles.container,
|
|
30
|
+
{
|
|
31
|
+
flexDirection: isColumn ? "column" : "row",
|
|
32
|
+
justifyContent: isColumn ? "flex-start" : align,
|
|
33
|
+
alignItems: isColumn ? "stretch" : "center",
|
|
34
|
+
gap: isCompact ? 8 : 10,
|
|
35
|
+
marginTop: padding.vertical,
|
|
36
|
+
},
|
|
37
|
+
style,
|
|
38
|
+
]}
|
|
39
|
+
>
|
|
40
|
+
{children}
|
|
41
|
+
</View>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const styles = StyleSheet.create({
|
|
46
|
+
container: {
|
|
47
|
+
flexWrap: "wrap",
|
|
48
|
+
},
|
|
49
|
+
});
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import React, { type ReactNode } from "react";
|
|
2
|
+
import { StyleSheet, Text, View, type StyleProp, type ViewStyle } from "react-native";
|
|
3
|
+
import { usePluginTheme } from "../theme/provider";
|
|
4
|
+
|
|
5
|
+
export interface FormRowProps {
|
|
6
|
+
label: string;
|
|
7
|
+
description?: string;
|
|
8
|
+
children: ReactNode;
|
|
9
|
+
/**
|
|
10
|
+
* "stacked" (default) keeps the historical label/description above the
|
|
11
|
+
* control. "inline" puts the label + description in a left column and the
|
|
12
|
+
* control on the right of the SAME line, which is what a short control
|
|
13
|
+
* (Toggle, StatusDot, Badge, small Button) wants. Stacking a one-line
|
|
14
|
+
* control under its label doubles a settings row's height for no gain
|
|
15
|
+
* (xpufx-org/paseo#213).
|
|
16
|
+
*/
|
|
17
|
+
layout?: "stacked" | "inline";
|
|
18
|
+
style?: StyleProp<ViewStyle>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function FormRow({
|
|
22
|
+
label,
|
|
23
|
+
description,
|
|
24
|
+
children,
|
|
25
|
+
layout = "stacked",
|
|
26
|
+
style,
|
|
27
|
+
}: FormRowProps) {
|
|
28
|
+
const { colors, flair, typography } = usePluginTheme();
|
|
29
|
+
|
|
30
|
+
const labelBlock = (
|
|
31
|
+
<View style={styles.labelBlock}>
|
|
32
|
+
<Text
|
|
33
|
+
style={[
|
|
34
|
+
styles.label,
|
|
35
|
+
{
|
|
36
|
+
color: colors.foreground,
|
|
37
|
+
...typography.label,
|
|
38
|
+
textTransform: flair.headingTransform === "uppercase" ? "uppercase" : "none",
|
|
39
|
+
},
|
|
40
|
+
]}
|
|
41
|
+
>
|
|
42
|
+
{label}
|
|
43
|
+
</Text>
|
|
44
|
+
{description && (
|
|
45
|
+
<Text
|
|
46
|
+
style={[
|
|
47
|
+
styles.description,
|
|
48
|
+
{ color: colors.foregroundMuted, ...typography.caption },
|
|
49
|
+
]}
|
|
50
|
+
>
|
|
51
|
+
{description}
|
|
52
|
+
</Text>
|
|
53
|
+
)}
|
|
54
|
+
</View>
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
if (layout === "inline") {
|
|
58
|
+
return (
|
|
59
|
+
<View style={[styles.container, styles.inlineContainer, style]}>
|
|
60
|
+
{labelBlock}
|
|
61
|
+
<View style={styles.inlineContent}>{children}</View>
|
|
62
|
+
</View>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<View style={[styles.container, style]}>
|
|
68
|
+
{labelBlock}
|
|
69
|
+
<View style={styles.content}>{children}</View>
|
|
70
|
+
</View>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const styles = StyleSheet.create({
|
|
75
|
+
container: {
|
|
76
|
+
gap: 4,
|
|
77
|
+
width: "100%",
|
|
78
|
+
},
|
|
79
|
+
inlineContainer: {
|
|
80
|
+
flexDirection: "row",
|
|
81
|
+
alignItems: "center",
|
|
82
|
+
justifyContent: "space-between",
|
|
83
|
+
gap: 8,
|
|
84
|
+
},
|
|
85
|
+
labelBlock: {
|
|
86
|
+
gap: 2,
|
|
87
|
+
flexShrink: 1,
|
|
88
|
+
minWidth: 0,
|
|
89
|
+
},
|
|
90
|
+
inlineContent: {
|
|
91
|
+
flexShrink: 0,
|
|
92
|
+
alignItems: "flex-end",
|
|
93
|
+
},
|
|
94
|
+
label: {
|
|
95
|
+
fontWeight: "600",
|
|
96
|
+
},
|
|
97
|
+
description: {
|
|
98
|
+
lineHeight: 16,
|
|
99
|
+
},
|
|
100
|
+
content: {
|
|
101
|
+
marginTop: 2,
|
|
102
|
+
},
|
|
103
|
+
});
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import React, { type ReactNode } from "react";
|
|
2
|
+
import { StyleSheet, View, type StyleProp, type ViewStyle } from "react-native";
|
|
3
|
+
import { usePluginTheme } from "../theme/provider";
|
|
4
|
+
import { resolveSpacing, type SpacingValue } from "../theme/tokens";
|
|
5
|
+
import { resolveGridColumns } from "../theme/responsive";
|
|
6
|
+
|
|
7
|
+
export interface GridProps {
|
|
8
|
+
children?: ReactNode;
|
|
9
|
+
/** Maximum column count. Default: 2. */
|
|
10
|
+
columns?: number;
|
|
11
|
+
/**
|
|
12
|
+
* Minimum width a column should keep. When set and the container width is
|
|
13
|
+
* known, the grid uses as many columns as fit (up to `columns`) and wraps
|
|
14
|
+
* down instead of collapsing to one.
|
|
15
|
+
*/
|
|
16
|
+
minColumnWidth?: number;
|
|
17
|
+
/** Gap between cells: a spacing token or raw px. Defaults to the theme gap. */
|
|
18
|
+
gap?: SpacingValue;
|
|
19
|
+
style?: StyleProp<ViewStyle>;
|
|
20
|
+
testID?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Width-aware wrapping grid. Cells keep a percentage basis driven by the
|
|
25
|
+
* effective column count, so they reflow across rows rather than stacking
|
|
26
|
+
* one-per-line or collapsing to a single column.
|
|
27
|
+
*/
|
|
28
|
+
export function Grid({ children, columns = 2, minColumnWidth, gap, style, testID }: GridProps) {
|
|
29
|
+
const { padding, isCompact, layout } = usePluginTheme();
|
|
30
|
+
const resolvedGap = resolveSpacing(gap, padding.gap);
|
|
31
|
+
const effectiveColumns = resolveGridColumns({
|
|
32
|
+
columns,
|
|
33
|
+
minColumnWidth,
|
|
34
|
+
gap: resolvedGap,
|
|
35
|
+
width: layout.width,
|
|
36
|
+
isCompact,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const cells = React.Children.toArray(children).filter(Boolean);
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<View testID={testID} style={[styles.container, { gap: resolvedGap }, style]}>
|
|
43
|
+
{cells.map((child, index) => (
|
|
44
|
+
<View
|
|
45
|
+
key={index}
|
|
46
|
+
style={[styles.cell, { flexBasis: `${Math.floor(100 / effectiveColumns) - 2}%` }]}
|
|
47
|
+
>
|
|
48
|
+
{child}
|
|
49
|
+
</View>
|
|
50
|
+
))}
|
|
51
|
+
</View>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const styles = StyleSheet.create({
|
|
56
|
+
container: {
|
|
57
|
+
flexDirection: "row",
|
|
58
|
+
flexWrap: "wrap",
|
|
59
|
+
width: "100%",
|
|
60
|
+
},
|
|
61
|
+
cell: {
|
|
62
|
+
flexGrow: 1,
|
|
63
|
+
flexShrink: 1,
|
|
64
|
+
},
|
|
65
|
+
});
|