mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-26 11:12:53 +00:00
chore: sync website translation parent
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@getpaseo/plugin",
|
||||
"version": "0.12.0-beta.3",
|
||||
"version": "0.12.0",
|
||||
"description": "Paseo plugin SDK package",
|
||||
"files": [
|
||||
"dist",
|
||||
@@ -71,20 +71,20 @@
|
||||
"use-sync-external-store": "^1.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@getpaseo/client": "0.12.0-beta.3",
|
||||
"@getpaseo/protocol": "0.12.0-beta.3",
|
||||
"@getpaseo/client": "0.12.0",
|
||||
"@getpaseo/protocol": "0.12.0",
|
||||
"@types/node": "^20.9.0",
|
||||
"@types/react": "~19.2.0",
|
||||
"react": "19.1.0",
|
||||
"react-native": "0.81.5",
|
||||
"react": "19.2.3",
|
||||
"react-native": "0.86.3",
|
||||
"typescript": "^5.9.3",
|
||||
"vitest": "^4.1.6",
|
||||
"zod": "^4.4.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@getpaseo/client": "0.12.0-beta.3",
|
||||
"@getpaseo/protocol": "0.12.0-beta.3",
|
||||
"react": "~19.1.0",
|
||||
"@getpaseo/client": "0.12.0",
|
||||
"@getpaseo/protocol": "0.12.0",
|
||||
"react": ">=19.1.0 <20",
|
||||
"react-native": ">=0.81.5",
|
||||
"zod": "^4.4.3"
|
||||
},
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
import type { PluginText } from "../text.js";
|
||||
import type { ComponentType } from "react";
|
||||
import type { PluginHostProps } from "./contracts.js";
|
||||
import type { PluginIconProps } from "../icons.js";
|
||||
import type { PluginHostProps, PluginIcon } from "./contracts.js";
|
||||
|
||||
export type PluginButtonContext =
|
||||
| { context: "workspace"; workspaceId: string }
|
||||
| { context: "agent"; workspaceId: string; agentId: string };
|
||||
export type PluginButtonIconProps = PluginHostProps &
|
||||
PluginButtonContext & { size: number; color: string };
|
||||
|
||||
export type PluginButtonIconProps = PluginHostProps & PluginButtonContext & PluginIconProps;
|
||||
|
||||
export type PluginButtonContentProps = PluginHostProps & PluginButtonContext & { close(): void };
|
||||
export type PluginButtonIcon = string | ComponentType<PluginButtonIconProps>;
|
||||
|
||||
export type PluginButtonIcon = PluginIcon<PluginButtonIconProps>;
|
||||
|
||||
export type PluginButtonBehavior<Text extends PluginText = string> =
|
||||
| { kind: "action"; onPress(): void | Promise<void> }
|
||||
|
||||
@@ -18,6 +18,7 @@ import type {
|
||||
PluginTimelineTransformResult,
|
||||
PluginCleanup,
|
||||
} from "../contracts.js";
|
||||
import type { PluginIconProps, PluginSerializableIcon } from "../icons.js";
|
||||
|
||||
export interface PluginHostProps {
|
||||
theme: PluginTheme;
|
||||
@@ -88,11 +89,13 @@ export interface PluginSidebarItemContribution<Text extends PluginText = string>
|
||||
Component: ComponentType<PluginSidebarItemProps>;
|
||||
}
|
||||
|
||||
export interface PluginIconProps {
|
||||
name: string;
|
||||
size?: number;
|
||||
color?: string;
|
||||
}
|
||||
/**
|
||||
* A Lucide icon name, inline SVG markup, or a component. Components receive the surface's size
|
||||
* and color; buttons widen the props with their host and target context.
|
||||
*/
|
||||
export type PluginIcon<Props extends PluginIconProps = PluginIconProps> =
|
||||
| PluginSerializableIcon
|
||||
| ComponentType<Props>;
|
||||
|
||||
export type PluginPanelLocation = "workspace" | "explorer";
|
||||
|
||||
@@ -103,7 +106,7 @@ export interface PluginOpenPanelOptions {
|
||||
interface PluginWorkspacePanelBase<Text extends PluginText = string> {
|
||||
id: string;
|
||||
title: Text;
|
||||
icon: string;
|
||||
icon: PluginIcon;
|
||||
locations?: readonly PluginPanelLocation[];
|
||||
}
|
||||
|
||||
@@ -176,7 +179,7 @@ export type PluginWorkspacePanelContribution<Text extends PluginText = string> =
|
||||
export interface PluginSettingsScreenContribution<Text extends PluginText = string> {
|
||||
id: string;
|
||||
title: Text;
|
||||
icon: string;
|
||||
icon: PluginIcon;
|
||||
Component: ComponentType<PluginSurfaceProps>;
|
||||
}
|
||||
|
||||
@@ -200,7 +203,7 @@ export interface PluginSurfaceContribution {
|
||||
export interface PluginSidebarContribution<Text extends PluginText = string> {
|
||||
id: string;
|
||||
title: Text;
|
||||
icon: string;
|
||||
icon: PluginIcon;
|
||||
surface: string;
|
||||
}
|
||||
|
||||
@@ -269,7 +272,7 @@ export interface PluginAgentCommandContext extends PluginCommandCapabilities {
|
||||
interface PluginCommandCenterItemBase<Text extends PluginText = string> {
|
||||
id: string;
|
||||
title: Text;
|
||||
icon: string;
|
||||
icon: PluginIcon;
|
||||
keywords?: readonly string[];
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ export type {
|
||||
PluginScreenLocation,
|
||||
PluginSidebarItemProps,
|
||||
PluginSidebarItemContribution,
|
||||
PluginIconProps,
|
||||
PluginIcon,
|
||||
PluginPanelLocation,
|
||||
PluginOpenPanelOptions,
|
||||
PluginWorkspacePanelProps,
|
||||
@@ -45,6 +45,7 @@ export type {
|
||||
PluginComposerPillContribution,
|
||||
PluginHeaderButtonContribution,
|
||||
} from "./buttons.js";
|
||||
export type { PluginIconProps, PluginSerializableIcon, PluginSvgIcon } from "../icons.js";
|
||||
export { usePaseo } from "./paseo-context.js";
|
||||
export { useAgent, useWorkspace } from "./client-state.js";
|
||||
export { useRpc } from "./rpc-context.js";
|
||||
|
||||
@@ -17,7 +17,8 @@ import type {
|
||||
TextInput as NativeTextInput,
|
||||
TextInputProps,
|
||||
} from "react-native";
|
||||
import type { PluginIconProps } from "./contracts.js";
|
||||
import type { PluginIconProps } from "../icons.js";
|
||||
import type { PluginIcon } from "./contracts.js";
|
||||
|
||||
export interface ModalProps {
|
||||
title: string;
|
||||
@@ -53,12 +54,16 @@ export interface ToastApi {
|
||||
error(message: string): void;
|
||||
}
|
||||
|
||||
export declare const Icon: ComponentType<PluginIconProps>;
|
||||
/** Renders a Lucide icon by `name`, or any plugin icon by `icon`. An unusable icon renders nothing. */
|
||||
export type IconProps = Partial<PluginIconProps> &
|
||||
({ name: string; icon?: never } | { icon: PluginIcon; name?: never });
|
||||
|
||||
export declare const Icon: ComponentType<IconProps>;
|
||||
export declare const Modal: ModalComponent;
|
||||
export declare function useToast(): ToastApi;
|
||||
export declare function useRevealedText(text: string, phase: "streaming" | "complete"): string;
|
||||
|
||||
export type { PluginIconProps } from "./contracts.js";
|
||||
export type { PluginIconProps } from "../icons.js";
|
||||
|
||||
/** React Native scrolling with the host's sheet gestures when rendered inside a sheet. */
|
||||
export declare const ScrollView: ForwardRefExoticComponent<
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { ComponentType, ReactNode, Ref } from "react";
|
||||
import type { PluginIcon } from "./contracts.js";
|
||||
|
||||
export interface SettingsSectionProps {
|
||||
title: string;
|
||||
@@ -64,14 +65,14 @@ export interface ExternalLinkProps {
|
||||
}
|
||||
export declare const ExternalLink: ComponentType<ExternalLinkProps>;
|
||||
|
||||
export type SidebarIcon = string | ComponentType<{ size: number; color: string }>;
|
||||
export type SidebarIcon = PluginIcon;
|
||||
export interface SidebarRowProps {
|
||||
/**
|
||||
* Tells rows of one item apart when the item renders several: "bot-2". Unique within the item.
|
||||
* Omit it when the item renders one row.
|
||||
*/
|
||||
id?: string;
|
||||
/** A Lucide icon name or a component. */
|
||||
/** A Lucide icon name, inline SVG, or a component. */
|
||||
icon?: SidebarIcon;
|
||||
/** Defaults to the item's registered title. */
|
||||
label?: string;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { PluginText } from "./text.js";
|
||||
import type { JsonValue } from "@getpaseo/protocol/agent-types";
|
||||
import type { PluginSerializableIcon } from "./icons.js";
|
||||
import type { PluginRpcContract } from "./rpc.js";
|
||||
|
||||
export interface PluginTheme {
|
||||
@@ -74,7 +75,7 @@ export interface PluginThemeContribution<Text extends PluginText = string> {
|
||||
export interface PluginAttachmentSourceContribution<Text extends PluginText = string> {
|
||||
id: string;
|
||||
title: Text;
|
||||
icon: string;
|
||||
icon: PluginSerializableIcon;
|
||||
pickerTitle: Text;
|
||||
searchPlaceholder: Text;
|
||||
search: PluginRpcContract;
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { findSvgIconProblem } from "./icons.js";
|
||||
|
||||
describe("findSvgIconProblem", () => {
|
||||
it.each([
|
||||
["a plain SVG", '<svg viewBox="0 0 24 24"><path d="M0 0h24v24z" /></svg>'],
|
||||
["leading whitespace", '\n <svg viewBox="0 0 24 24"></svg>'],
|
||||
[
|
||||
"a fragment reference",
|
||||
'<svg><defs><path id="mark" d="M0 0h1v1z" /></defs><use href="#mark" /></svg>',
|
||||
],
|
||||
["currentColor fills", '<svg fill="currentColor"><circle r="4" /></svg>'],
|
||||
])("accepts %s", (_name, svg) => {
|
||||
expect(findSvgIconProblem(svg)).toBeNull();
|
||||
});
|
||||
|
||||
it.each([
|
||||
["a non-SVG document", "hello", "not an SVG document"],
|
||||
["an <svgx> element", "<svgx></svgx>", "not an SVG document"],
|
||||
["a script", "<svg><script /></svg>", "script elements are not allowed"],
|
||||
["a foreign object", "<svg><foreignObject /></svg>", "foreignObject elements are not allowed"],
|
||||
["a style", "<svg><style /></svg>", "style elements are not allowed"],
|
||||
[
|
||||
"an event handler",
|
||||
'<svg><path onclick="run()" /></svg>',
|
||||
"event-handler attributes are not allowed",
|
||||
],
|
||||
[
|
||||
"a javascript URL",
|
||||
'<svg><a href="javascript:run()" /></svg>',
|
||||
"javascript URLs are not allowed",
|
||||
],
|
||||
[
|
||||
"an external href",
|
||||
'<svg><use href="https://example.com/icon.svg" /></svg>',
|
||||
"external href references are not allowed",
|
||||
],
|
||||
[
|
||||
"an external xlink:href",
|
||||
"<svg><image xlink:href=icon.png /></svg>",
|
||||
"external href references are not allowed",
|
||||
],
|
||||
["markup over 64 KiB", `<svg>${" ".repeat(64 * 1024)}</svg>`, "exceeds 64 KiB"],
|
||||
])("rejects %s", (_name, svg, reason) => {
|
||||
expect(findSvgIconProblem(svg)).toBe(reason);
|
||||
});
|
||||
|
||||
it("measures the size in UTF-8 bytes", () => {
|
||||
const multibyte = "é".repeat(32 * 1024);
|
||||
|
||||
expect(findSvgIconProblem(`<svg>${multibyte}</svg>`)).toBe("exceeds 64 KiB");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,38 @@
|
||||
/** What a component icon receives from the surface that draws it. */
|
||||
export interface PluginIconProps {
|
||||
size: number;
|
||||
color: string;
|
||||
}
|
||||
|
||||
/** Inline SVG markup. Paseo validates it when the contribution registers. */
|
||||
export interface PluginSvgIcon {
|
||||
svg: string;
|
||||
}
|
||||
|
||||
/** An icon that can be stored or sent: a Lucide icon name, or inline SVG. */
|
||||
export type PluginSerializableIcon = string | PluginSvgIcon;
|
||||
|
||||
export const PLUGIN_SVG_ICON_MAX_BYTES = 64 * 1024;
|
||||
|
||||
const EXTERNAL_HREF = /\s(?:href|xlink:href)\s*=\s*(?:(["'])(.*?)\1|([^\s>]+))/gi;
|
||||
|
||||
/**
|
||||
* Why Paseo refuses to render this SVG, or null when it is a self-contained document.
|
||||
* The daemon applies it to provider and usage-source icon files; the app to inline plugin icons.
|
||||
*/
|
||||
export function findSvgIconProblem(svg: string): string | null {
|
||||
if (new TextEncoder().encode(svg).byteLength > PLUGIN_SVG_ICON_MAX_BYTES) {
|
||||
return "exceeds 64 KiB";
|
||||
}
|
||||
if (!/^\s*<svg(?:\s|>)/i.test(svg)) return "not an SVG document";
|
||||
if (/<script(?:\s|>)/i.test(svg)) return "script elements are not allowed";
|
||||
if (/<foreignObject(?:\s|>)/i.test(svg)) return "foreignObject elements are not allowed";
|
||||
if (/<style(?:\s|>)/i.test(svg)) return "style elements are not allowed";
|
||||
if (/\son[a-z0-9_-]*\s*=/i.test(svg)) return "event-handler attributes are not allowed";
|
||||
if (/javascript\s*:/i.test(svg)) return "javascript URLs are not allowed";
|
||||
for (const match of svg.matchAll(EXTERNAL_HREF)) {
|
||||
const href = match[2] ?? match[3] ?? "";
|
||||
if (!href.startsWith("#")) return "external href references are not allowed";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -22,3 +22,10 @@ export {
|
||||
export { defineRpc, type PluginRpcContract, type RpcInput, type RpcOutput } from "./rpc.js";
|
||||
|
||||
export type { PluginText, PluginMessage } from "./text.js";
|
||||
export {
|
||||
findSvgIconProblem,
|
||||
PLUGIN_SVG_ICON_MAX_BYTES,
|
||||
type PluginIconProps,
|
||||
type PluginSerializableIcon,
|
||||
type PluginSvgIcon,
|
||||
} from "./icons.js";
|
||||
|
||||
Reference in New Issue
Block a user