@savvifi/meridian-web-react 0.1.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/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@savvifi/meridian-web-react",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "React adapter for the meridian WebRenderer seam: a kit-agnostic core (MeridianProvider, PanelRenderer, ComponentKit, reactWebRenderer) that renders meridian.ui.v1 PanelDescriptors through a swappable ComponentKit (MUI, shadcn, …). The premium web renderer tier of the meridian renderer family.",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/meridian-ux/meridian-web-react.git"
12
+ },
13
+ "main": "./src/index.js",
14
+ "types": "./src/index.d.ts",
15
+ "exports": {
16
+ ".": {
17
+ "types": "./src/index.d.ts",
18
+ "import": "./src/index.js"
19
+ }
20
+ },
21
+ "files": [
22
+ "src/**/*.js",
23
+ "src/**/*.d.ts"
24
+ ],
25
+ "dependencies": {
26
+ "@savvifi/meridian-proto-ts": "^0.1.0",
27
+ "@savvifi/meridian-schemas": "^0.1.0",
28
+ "@bufbuild/protobuf": "2.12.1"
29
+ },
30
+ "peerDependencies": {
31
+ "react": "^19.0.0",
32
+ "react-dom": "^19.0.0"
33
+ },
34
+ "devDependencies": {
35
+ "react": "19.2.7",
36
+ "react-dom": "19.2.7",
37
+ "@types/react": "19.2.17",
38
+ "@types/react-dom": "19.2.3",
39
+ "typescript": "5.6.2",
40
+ "vitest": "2.1.4"
41
+ },
42
+ "pnpm": {
43
+ "onlyBuiltDependencies": ["esbuild"]
44
+ }
45
+ }
@@ -0,0 +1,46 @@
1
+ import type { ComponentType, CSSProperties, ReactNode } from "react";
2
+ import type { GalleryPanel } from "@savvifi/meridian-proto-ts/proto/gallery_pb.js";
3
+ import type { LlmPromptPanel } from "@savvifi/meridian-proto-ts/proto/llm_prompt_pb.js";
4
+ import type { LroPanel } from "@savvifi/meridian-proto-ts/proto/lro_pb.js";
5
+ import type { PanelDescriptor } from "@savvifi/meridian-proto-ts/proto/panel_pb.js";
6
+ import type { PromptPanel } from "@savvifi/meridian-proto-ts/proto/prompt_pb.js";
7
+ import type { TablePanel } from "@savvifi/meridian-proto-ts/proto/table_pb.js";
8
+ import type { Theme } from "@savvifi/meridian-proto-ts/proto/theme_pb.js";
9
+ import type { RpcInvoker } from "@savvifi/meridian-schemas/uiview";
10
+ /** Props every shape component receives: its panel, the parent descriptor, transport. */
11
+ export interface ShapeProps<P> {
12
+ panel: P;
13
+ descriptor: PanelDescriptor;
14
+ invoker: RpcInvoker;
15
+ }
16
+ export type TablePanelProps = ShapeProps<TablePanel>;
17
+ export type PromptPanelProps = ShapeProps<PromptPanel>;
18
+ export type LroPanelProps = ShapeProps<LroPanel>;
19
+ export type GalleryPanelProps = ShapeProps<GalleryPanel>;
20
+ export type LlmPromptPanelProps = ShapeProps<LlmPromptPanel>;
21
+ /** A set of components + a theme binding that paints meridian panels. */
22
+ export interface ComponentKit {
23
+ /** Stable id, e.g. "mui" / "shadcn" / "html". Used in the renderer id. */
24
+ readonly id: string;
25
+ /**
26
+ * Bind a meridian.theme.v1.Theme to this kit's style primitive (MUI theme,
27
+ * CSS custom properties, Tailwind config, …). Returned style is applied to the
28
+ * panel container. Optional — a kit may bind the theme its own way.
29
+ */
30
+ themeToStyle?(theme: Theme | undefined): CSSProperties;
31
+ /** Optional wrapper around every panel (title bar, surface, padding). */
32
+ Chrome?: ComponentType<{
33
+ descriptor: PanelDescriptor;
34
+ children: ReactNode;
35
+ }>;
36
+ Table: ComponentType<TablePanelProps>;
37
+ Prompt: ComponentType<PromptPanelProps>;
38
+ Lro: ComponentType<LroPanelProps>;
39
+ /** Optional richer shapes; PanelRenderer falls back when a kit omits them. */
40
+ Gallery?: ComponentType<GalleryPanelProps>;
41
+ LlmPrompt?: ComponentType<LlmPromptPanelProps>;
42
+ /** Rendered for unknown / unset / unsupported shapes. */
43
+ Fallback: ComponentType<{
44
+ descriptor: PanelDescriptor;
45
+ }>;
46
+ }
@@ -0,0 +1,9 @@
1
+ // ComponentKit — the swap point *within* the React renderer.
2
+ //
3
+ // The React adapter core (provider + PanelRenderer + reactWebRenderer) is
4
+ // kit-agnostic: it dispatches a meridian.ui.v1 PanelDescriptor to the kit's
5
+ // per-shape components and binds the theme through the kit. Swapping the kit
6
+ // (e.g. MUI -> shadcn) changes the look and the concrete components, not the
7
+ // dispatch logic. The savvi `mui-kit` (wrapping @aion/ui) and a future
8
+ // `shadcn-kit` are both implementations of this one interface.
9
+ export {};
@@ -0,0 +1,2 @@
1
+ import type { ComponentKit } from "./component_kit.js";
2
+ export declare const htmlKit: ComponentKit;
@@ -0,0 +1,30 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ function themeToStyle(theme) {
3
+ if (!theme)
4
+ return {};
5
+ const pal = theme.dark ?? theme.light;
6
+ if (!pal)
7
+ return {};
8
+ // Expose the palette as the same --mer-* custom properties the web-components
9
+ // renderer uses, so one skin styles both web renderers identically.
10
+ return {
11
+ ["--mer-bg"]: pal.bg,
12
+ ["--mer-surface"]: pal.surface,
13
+ ["--mer-fg"]: pal.fg,
14
+ ["--mer-accent"]: pal.accent,
15
+ ["--mer-border"]: pal.border,
16
+ background: "var(--mer-bg)",
17
+ color: "var(--mer-fg)",
18
+ };
19
+ }
20
+ export const htmlKit = {
21
+ id: "html",
22
+ themeToStyle,
23
+ Chrome: ({ descriptor, children }) => (_jsxs("section", { className: "mer-panel", style: themeToStyle(undefined), children: [_jsx("h2", { className: "mer-panel-title", children: descriptor.title || descriptor.panelId }), children] })),
24
+ Table: ({ panel }) => (_jsxs("table", { className: "mer-table", children: [_jsx("thead", { children: _jsx("tr", { children: panel.columns.map((col, i) => (_jsx("th", { children: col.header }, i))) }) }), _jsx("tbody", { children: _jsx("tr", { children: _jsx("td", { className: "mer-empty", colSpan: panel.columns.length || 1, children: panel.placeholder || "(load to populate)" }) }) })] })),
25
+ Prompt: ({ panel }) => (_jsx("form", { className: "mer-prompt", children: panel.fields.map((field) => (_jsx("label", { className: "mer-field", children: _jsx("span", { children: field.label }) }, field.fieldId))) })),
26
+ Lro: ({ panel }) => (_jsx("div", { className: "mer-lro", children: _jsx("button", { type: "button", children: panel.runButtonLabel || "Run" }) })),
27
+ Fallback: ({ descriptor }) => (_jsx("pre", { className: "mer-fallback", children: descriptor.body.case
28
+ ? `unsupported panel shape: ${descriptor.body.case}`
29
+ : "(empty panel)" })),
30
+ };
package/src/index.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ export type { ComponentKit, ShapeProps, TablePanelProps, PromptPanelProps, LroPanelProps, GalleryPanelProps, LlmPromptPanelProps, } from "./component_kit.js";
2
+ export { MeridianProvider, useMeridian, useMeridianTheme, useRpcInvoker, useComponentKit, useAdhocHandler, type MeridianContextValue, type MeridianProviderProps, type ReactAdhocFactory, } from "./provider.js";
3
+ export { PanelRenderer } from "./panel_renderer.js";
4
+ export { reactWebRenderer } from "./react_web_renderer.js";
5
+ export { htmlKit } from "./html_kit.js";
package/src/index.js ADDED
@@ -0,0 +1,10 @@
1
+ // @savvifi/meridian-web-react — the React renderer tier of the meridian renderer family.
2
+ //
3
+ // A kit-agnostic React adapter over the framework-neutral WebRenderer seam
4
+ // (@savvifi/meridian-schemas/uiview): it renders meridian.ui.v1 PanelDescriptors through a
5
+ // swappable ComponentKit. `reactWebRenderer(kit)` yields a WebRenderer a host
6
+ // can mount, a peer of the web-components / TUI / native renderers.
7
+ export { MeridianProvider, useMeridian, useMeridianTheme, useRpcInvoker, useComponentKit, useAdhocHandler, } from "./provider.js";
8
+ export { PanelRenderer } from "./panel_renderer.js";
9
+ export { reactWebRenderer } from "./react_web_renderer.js";
10
+ export { htmlKit } from "./html_kit.js";
@@ -0,0 +1,5 @@
1
+ import type { ReactNode } from "react";
2
+ import type { PanelDescriptor } from "@savvifi/meridian-proto-ts/proto/panel_pb.js";
3
+ export declare function PanelRenderer({ descriptor, }: {
4
+ descriptor: PanelDescriptor;
5
+ }): ReactNode;
@@ -0,0 +1,33 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useMeridian } from "./provider.js";
3
+ export function PanelRenderer({ descriptor, }) {
4
+ const { kit, invoker, adhoc } = useMeridian();
5
+ const body = descriptor.body;
6
+ let inner;
7
+ switch (body.case) {
8
+ case "table":
9
+ inner = (_jsx(kit.Table, { panel: body.value, descriptor: descriptor, invoker: invoker }));
10
+ break;
11
+ case "prompt":
12
+ inner = (_jsx(kit.Prompt, { panel: body.value, descriptor: descriptor, invoker: invoker }));
13
+ break;
14
+ case "lro":
15
+ inner = (_jsx(kit.Lro, { panel: body.value, descriptor: descriptor, invoker: invoker }));
16
+ break;
17
+ case "gallery":
18
+ inner = kit.Gallery ? (_jsx(kit.Gallery, { panel: body.value, descriptor: descriptor, invoker: invoker })) : (_jsx(kit.Fallback, { descriptor: descriptor }));
19
+ break;
20
+ case "llmPrompt":
21
+ inner = kit.LlmPrompt ? (_jsx(kit.LlmPrompt, { panel: body.value, descriptor: descriptor, invoker: invoker })) : (_jsx(kit.Fallback, { descriptor: descriptor }));
22
+ break;
23
+ case "adhoc": {
24
+ const Adhoc = adhoc[body.value.handlerId];
25
+ inner = Adhoc ? (_jsx(Adhoc, { descriptor: descriptor })) : (_jsx(kit.Fallback, { descriptor: descriptor }));
26
+ break;
27
+ }
28
+ default:
29
+ inner = _jsx(kit.Fallback, { descriptor: descriptor });
30
+ }
31
+ const Chrome = kit.Chrome;
32
+ return Chrome ? _jsx(Chrome, { descriptor: descriptor, children: inner }) : inner;
33
+ }
@@ -0,0 +1,24 @@
1
+ import type { ComponentType, ReactNode } from "react";
2
+ import type { PanelDescriptor } from "@savvifi/meridian-proto-ts/proto/panel_pb.js";
3
+ import type { Theme } from "@savvifi/meridian-proto-ts/proto/theme_pb.js";
4
+ import type { RpcInvoker } from "@savvifi/meridian-schemas/uiview";
5
+ import type { ComponentKit } from "./component_kit.js";
6
+ /** The React renderer's AdhocPanel factory: a component rendered for a handler_id. */
7
+ export type ReactAdhocFactory = ComponentType<{
8
+ descriptor: PanelDescriptor;
9
+ }>;
10
+ export interface MeridianContextValue {
11
+ theme?: Theme;
12
+ invoker: RpcInvoker;
13
+ kit: ComponentKit;
14
+ adhoc: Record<string, ReactAdhocFactory>;
15
+ }
16
+ export declare function useMeridian(): MeridianContextValue;
17
+ export declare const useMeridianTheme: () => Theme | undefined;
18
+ export declare const useRpcInvoker: () => RpcInvoker;
19
+ export declare const useComponentKit: () => ComponentKit;
20
+ export declare const useAdhocHandler: (handlerId: string) => ReactAdhocFactory | undefined;
21
+ export interface MeridianProviderProps extends MeridianContextValue {
22
+ children: ReactNode;
23
+ }
24
+ export declare function MeridianProvider({ children, ...value }: MeridianProviderProps): ReactNode;
@@ -0,0 +1,21 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ // MeridianProvider — the React context that carries the cross-cutting inputs a
3
+ // PanelRenderer needs: the active Theme (skin), the RpcInvoker (transport), the
4
+ // chosen ComponentKit, and the adhoc-handler registry. It composes with a host's
5
+ // other providers; it does not replace them.
6
+ import { createContext, useContext } from "react";
7
+ const MeridianContext = createContext(null);
8
+ export function useMeridian() {
9
+ const value = useContext(MeridianContext);
10
+ if (!value) {
11
+ throw new Error("useMeridian must be used within a <MeridianProvider>");
12
+ }
13
+ return value;
14
+ }
15
+ export const useMeridianTheme = () => useMeridian().theme;
16
+ export const useRpcInvoker = () => useMeridian().invoker;
17
+ export const useComponentKit = () => useMeridian().kit;
18
+ export const useAdhocHandler = (handlerId) => useMeridian().adhoc[handlerId];
19
+ export function MeridianProvider({ children, ...value }) {
20
+ return (_jsx(MeridianContext.Provider, { value: value, children: children }));
21
+ }
@@ -0,0 +1,5 @@
1
+ import type { Theme } from "@savvifi/meridian-proto-ts/proto/theme_pb.js";
2
+ import type { WebRenderer } from "@savvifi/meridian-schemas/uiview";
3
+ import type { ComponentKit } from "./component_kit.js";
4
+ import { type ReactAdhocFactory } from "./provider.js";
5
+ export declare function reactWebRenderer(kit: ComponentKit): WebRenderer<Theme, ReactAdhocFactory>;
@@ -0,0 +1,23 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ // reactWebRenderer — turns a ComponentKit into a meridian `WebRenderer` (the
3
+ // framework-neutral seam from @savvifi/meridian-schemas/uiview). This is the React branch
4
+ // of the web swap point: `reactWebRenderer(muiKit)` and
5
+ // `reactWebRenderer(shadcnKit)` are both WebRenderers a host can mount, peers of
6
+ // the web-components renderer — all consuming the same PanelDescriptor + Theme.
7
+ import { createRoot } from "react-dom/client";
8
+ import { PanelRenderer } from "./panel_renderer.js";
9
+ import { MeridianProvider } from "./provider.js";
10
+ export function reactWebRenderer(kit) {
11
+ return {
12
+ id: `react:${kit.id}`,
13
+ mount(opts) {
14
+ const root = createRoot(opts.container);
15
+ const draw = (descriptor) => root.render(_jsx(MeridianProvider, { theme: opts.theme, invoker: opts.invoker, kit: kit, adhoc: opts.adhoc ?? {}, children: _jsx(PanelRenderer, { descriptor: descriptor }) }));
16
+ draw(opts.descriptor);
17
+ return {
18
+ update: (descriptor) => draw(descriptor),
19
+ unmount: () => root.unmount(),
20
+ };
21
+ },
22
+ };
23
+ }