Files
paseo/packages/plugin/src/host.ts
T
Mohamed Boudra deb846eed6 Let plugins use Paseo modals, icons, and toasts (#3967)
* feat(plugins): add React Native UI primitives

Give client plugins host-owned adaptive UI without exposing app internals. Preserve plugin runtime contexts when modal content moves through the compact portal host.

* docs(plugins): clarify host UI reference

* refactor(plugins): harden host UI boundary

Replace mocked pass-through coverage with a real browser plugin journey, keep host runtime ownership inside the evaluator, preserve toast context across compact portals, and expose modal content as a compound component.

* fix(plugins): defer native UI presentation runtime

Loading the plugin evaluator initialized the modal and toast presentation stacks even when no plugin requested host UI. Keep the capability stable while delaying platform-heavy presentation code until Modal.Content renders.

* refactor(plugins): move modal metadata to root

Keep Modal.Content body-only while the top-level Modal owns the host-rendered title and leading icon. This leaves presentation metadata stable as the compound API gains future regions.
2026-08-28 11:43:30 +02:00

104 lines
3.5 KiB
TypeScript

import { PluginAttachmentSearchPayloadSchema } from "./attachments.js";
export { PluginClientStateProvider, type PluginClientStateSource } from "./client-state.js";
export {
usePluginRuntimeContextBridge,
type PluginRuntimeContextBridge,
} from "./runtime-context-bridge.js";
import type {
PluginAttachmentSourceContribution,
PluginCommandCenterItemContribution,
PluginClientContribution,
PluginContext,
PluginSidebarContribution,
PluginSurfaceContribution,
PluginSurfaceProps,
PluginThemeContribution,
PluginTimelineRendererContribution,
PluginTimelineTransformerContribution,
PluginWorkspacePanelContribution,
} from "./contracts.js";
import { PluginRpcProvider } from "./rpc-context.js";
import { PaseoApiProvider } from "./paseo-context.js";
import { callPluginRpc } from "./rpc.js";
import type { ComponentType } from "react";
interface PluginCollector {
addSurface(id: string, Component: ComponentType<PluginSurfaceProps>): void;
addSidebarItem(contribution: PluginSidebarContribution): void;
addWorkspacePanel(contribution: PluginWorkspacePanelContribution): void;
addCommandCenterItem(contribution: PluginCommandCenterItemContribution): void;
addClientSide(contribution: PluginClientContribution): void;
addAttachmentSource(contribution: PluginAttachmentSourceContribution): void;
addTheme(contribution: PluginThemeContribution): void;
addTimelineTransformer(contribution: PluginTimelineTransformerContribution): void;
addTimelineRenderer(contribution: PluginTimelineRendererContribution): void;
}
export interface PluginRegistrationCollector {
surfaces: PluginSurfaceContribution[];
sidebarItems: PluginSidebarContribution[];
workspacePanels: PluginWorkspacePanelContribution[];
commandCenterItems: PluginCommandCenterItemContribution[];
clientSide: PluginClientContribution | null;
attachmentSources: PluginAttachmentSourceContribution[];
themes: PluginThemeContribution[];
timelineTransformers: PluginTimelineTransformerContribution[];
timelineRenderers: PluginTimelineRendererContribution[];
}
export function createPluginContext(
collector: PluginCollector,
): Pick<
PluginContext,
| "addSurface"
| "addSidebarItem"
| "addWorkspacePanel"
| "addCommandCenterItem"
| "addClientSide"
| "addAttachmentSource"
| "addTheme"
| "addTimelineTransformer"
| "addTimelineRenderer"
> {
return {
addSurface(id, Component) {
collector.addSurface(id, Component);
},
addSidebarItem(contribution) {
collector.addSidebarItem(contribution);
},
addWorkspacePanel(contribution) {
collector.addWorkspacePanel(contribution);
},
addCommandCenterItem(contribution) {
collector.addCommandCenterItem(contribution);
},
addClientSide(contribution) {
collector.addClientSide(contribution);
},
addAttachmentSource(contribution) {
collector.addAttachmentSource(contribution);
},
addTheme(contribution) {
collector.addTheme(contribution);
},
addTimelineTransformer(contribution) {
collector.addTimelineTransformer(contribution);
},
addTimelineRenderer(contribution) {
collector.addTimelineRenderer(contribution);
},
};
}
export async function searchPluginAttachments(
source: PluginAttachmentSourceContribution,
invoke: (method: string, input: unknown) => Promise<unknown>,
query: string,
) {
const output = await callPluginRpc(source.search, invoke, { query });
return PluginAttachmentSearchPayloadSchema.parseAsync(output);
}
export { callPluginRpc, PaseoApiProvider, PluginRpcProvider };