@ethisyscore/plugin-ui 1.19.0 → 1.21.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.
@@ -35,6 +35,7 @@ function makeDefaultQueryClient() {
35
35
  });
36
36
  }
37
37
  var defaultQueryClient = makeDefaultQueryClient();
38
+ var EHX_PLUGIN_ROOT_CLASS = "ehx-plugin-root";
38
39
  function definePlatformReactPluginPage(Page, options) {
39
40
  const {
40
41
  manifest = {},
@@ -51,7 +52,11 @@ function definePlatformReactPluginPage(Page, options) {
51
52
  TemplateContext.Provider,
52
53
  { value: manifest },
53
54
  react.createElement(PluginStyleScope, { id: styleId, css }),
54
- react.createElement("div", { className: rootClassName }, react.createElement(Page, props))
55
+ react.createElement(
56
+ "div",
57
+ { className: `${EHX_PLUGIN_ROOT_CLASS} ${rootClassName}` },
58
+ react.createElement(Page, props)
59
+ )
55
60
  )
56
61
  );
57
62
  }
@@ -227,6 +232,7 @@ function createReactRouterShim(options = {}) {
227
232
  }
228
233
 
229
234
  exports.BaseMcpService = BaseMcpService;
235
+ exports.EHX_PLUGIN_ROOT_CLASS = EHX_PLUGIN_ROOT_CLASS;
230
236
  exports.MissingViewFallback = MissingViewFallback;
231
237
  exports.PluginStyleScope = PluginStyleScope;
232
238
  exports.TemplateContext = TemplateContext;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/platform-react/templateContext.ts","../../src/platform-react/injectStyles.ts","../../src/platform-react/definePlatformReactPluginPage.tsx","../../src/platform-react/MissingViewFallback.tsx","../../src/platform-react/useView.ts","../../src/platform-react/mcpService.ts","../../src/platform-react/useAuthenticatedQuery.ts","../../src/platform-react/reactRouterShim.tsx"],"names":["createContext","useEffect","QueryClient","createElement","QueryClientProvider","definePlatformReactPage","useView","useContext","useMcpTool","useMemo","useQuery","useQueries","emitNavigation"],"mappings":"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","file":"index.cjs","sourcesContent":["import { createContext, type ComponentType } from \"react\";\n\n/** A view component resolved from the template manifest. Props are open. */\nexport type ViewComponent = ComponentType<Record<string, unknown>>;\n\n/**\n * Static port of the monolith's gogo-ui adapter mechanism for PlatformReact.\n *\n * A monolith feature picks its adapter view at runtime via `import.meta.glob`;\n * a PlatformReact page is a single self-contained ESM bundle where that glob is\n * unavailable, so each page declares its view slice statically and provides it\n * through this context. The shape is `manifest[module][viewName] = Component`.\n */\nexport type TemplateManifest = Record<string, Partial<Record<string, ViewComponent>>>;\n\nexport const TemplateContext = createContext<TemplateManifest | null>(null);\n","import { useEffect } from \"react\";\n\n/**\n * Injects a plugin's compiled CSS into the host document once, keyed by a\n * stable element id (idempotent across pages and re-mounts).\n *\n * Each PlatformReact page is a separate ESM bundle with no companion CSS asset\n * the host would load, so the styles travel inside the bundle (typically via a\n * `*.css?inline` import) and are mounted at runtime. Scope the CSS to a plugin\n * root class (never `:root`) so it cannot override the host theme.\n */\nexport function injectPluginStyles(id: string, css: string): void {\n if (typeof document === \"undefined\") return;\n if (document.getElementById(id)) return;\n const el = document.createElement(\"style\");\n el.id = id;\n el.textContent = css;\n document.head.appendChild(el);\n}\n\n/** Component form of {@link injectPluginStyles}; renders nothing. */\nexport function PluginStyleScope({ id, css }: { id: string; css: string }): null {\n useEffect(() => {\n injectPluginStyles(id, css);\n }, [id, css]);\n return null;\n}\n","import { createElement, type ComponentType } from \"react\";\nimport { QueryClient, QueryClientProvider } from \"@tanstack/react-query\";\nimport {\n definePlatformReactPage,\n type PlatformReactPageProps,\n} from \"@ethisyscore/components-react\";\nimport { TemplateContext, type TemplateManifest, type ViewComponent } from \"./templateContext\";\nimport { PluginStyleScope } from \"./injectStyles\";\n\n/**\n * Default per-page `QueryClient`. Each PlatformReact page is an isolated ESM\n * bundle, so this module-level singleton is scoped to that page — exactly the\n * lifetime a page wants. Plugins needing different options pass their own.\n */\nfunction makeDefaultQueryClient(): QueryClient {\n return new QueryClient({\n defaultOptions: {\n queries: {\n retry: 1,\n refetchOnWindowFocus: false,\n staleTime: 30_000,\n },\n },\n });\n}\n\nconst defaultQueryClient = makeDefaultQueryClient();\n\nexport interface DefinePlatformReactPluginPageOptions {\n /** The page's static `useView` manifest slice (no `import.meta.glob` in a single-file bundle). */\n manifest?: TemplateManifest;\n /** Scoped style-root class wrapped around the page (e.g. `ethisys-<plugin>-root`). */\n rootClassName: string;\n /** Stable id for the injected `<style>` element (idempotent across pages). */\n styleId: string;\n /** The plugin's compiled CSS string (typically a `*.css?inline` import). */\n css: string;\n /** Override the per-page `QueryClient`. */\n queryClient?: QueryClient;\n}\n\n/**\n * Wraps a lifted page as a PlatformReact entry with the providers the monolith\n * took for granted: react-query, the static `useView` manifest, and the scoped\n * style root. The host already provides the MCP transport (it mounts pages\n * inside its own runtime provider), so this adds only the in-page providers.\n */\nexport function definePlatformReactPluginPage(\n Page: ComponentType<PlatformReactPageProps>,\n options: DefinePlatformReactPluginPageOptions,\n): ComponentType<PlatformReactPageProps> {\n const {\n manifest = {},\n rootClassName,\n styleId,\n css,\n queryClient = defaultQueryClient,\n } = options;\n\n function PluginPage(props: PlatformReactPageProps) {\n return createElement(\n QueryClientProvider,\n { client: queryClient },\n createElement(\n TemplateContext.Provider,\n { value: manifest },\n createElement(PluginStyleScope, { id: styleId, css }),\n createElement(\"div\", { className: rootClassName }, createElement(Page, props)),\n ),\n );\n }\n\n return definePlatformReactPage(PluginPage);\n}\n\nexport interface PluginPageDefinerConfig {\n /**\n * The plugin's `useView` module key (e.g. `\"timesheets\"`). Each page's `views`\n * are registered under it, so a page's `useView(module, name)` resolves them.\n */\n module: string;\n /** Scoped style-root class wrapped around every page (e.g. `ethisys-<plugin>-root`). */\n rootClassName: string;\n /** Stable id for the injected `<style>` element. */\n styleId: string;\n /** The plugin's compiled CSS string (typically a `*.css?inline` import). */\n css: string;\n /** Override the per-page `QueryClient`. */\n queryClient?: QueryClient;\n}\n\n/**\n * Builds a plugin-bound `definePluginPage(Page, views)` from one config, so a\n * plugin writes the four plugin-specific values (module key, root class, style\n * id, CSS) ONCE instead of in every page entry. Each surface entry then calls\n * `definePluginPage(Page, { ViewName })`; the flat `views` map is wrapped into\n * the `{ [module]: views }` manifest slice the page's `useView(module, name)`\n * reads. The generic provider/manifest/style wrapping stays in\n * {@link definePlatformReactPluginPage}.\n */\nexport function createPluginPageDefiner(config: PluginPageDefinerConfig) {\n const { module, ...rest } = config;\n return function definePluginPage(\n Page: ComponentType<PlatformReactPageProps>,\n views: Record<string, ViewComponent> = {},\n ): ComponentType<PlatformReactPageProps> {\n return definePlatformReactPluginPage(Page, { ...rest, manifest: { [module]: views } });\n };\n}\n","import { createElement, type ComponentType } from \"react\";\n\n/**\n * Dependency-light fallback rendered when a view is not present in the current\n * template manifest. Kept to plain elements + inline styles so the foundation\n * package carries no UI-library dependency; plugins that want a branded\n * fallback pass their own via {@link createUseView}.\n */\nexport const MissingViewFallback: ComponentType<Record<string, unknown>> = () =>\n createElement(\n \"div\",\n {\n style: {\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n padding: \"2rem\",\n },\n },\n createElement(\n \"div\",\n {\n style: {\n maxWidth: \"28rem\",\n padding: \"1.5rem\",\n border: \"1px solid rgba(0,0,0,0.12)\",\n borderRadius: \"0.5rem\",\n textAlign: \"center\",\n },\n },\n createElement(\"p\", { style: { margin: \"0 0 0.5rem\", fontWeight: 600 } }, \"View not available\"),\n createElement(\n \"p\",\n { style: { margin: 0, opacity: 0.7, fontSize: \"0.875rem\" } },\n \"This view has not been implemented by the current template.\",\n ),\n ),\n );\n","import { useContext, type ComponentType } from \"react\";\nimport { TemplateContext } from \"./templateContext\";\nimport { MissingViewFallback } from \"./MissingViewFallback\";\n\nconst REACT_LAZY_TYPE = Symbol.for(\"react.lazy\");\n\n/** Matches regular function components and `React.lazy` wrappers. */\nexport function isComponent(value: unknown): value is ComponentType {\n if (typeof value === \"function\") return true;\n if (typeof value === \"object\" && value !== null) {\n return (value as Record<string, unknown>).$$typeof === REACT_LAZY_TYPE;\n }\n return false;\n}\n\n/**\n * Builds a `useView` hook bound to a fallback component. The hook is a safe\n * view accessor that never throws: it returns `manifest[module][viewName]` when\n * present, or the supplied fallback when the manifest, module slice, or view is\n * missing or is not a component.\n */\nexport function createUseView(fallback: ComponentType<Record<string, unknown>>) {\n return function useView(\n module: string,\n viewName: string,\n ): ComponentType<Record<string, unknown>> {\n const manifest = useContext(TemplateContext);\n if (!manifest) {\n return fallback;\n }\n\n const slice = manifest[module];\n if (slice && typeof slice === \"object\" && viewName in (slice as Record<string, unknown>)) {\n const component = (slice as Record<string, unknown>)[viewName];\n if (isComponent(component)) {\n return component as ComponentType<Record<string, unknown>>;\n }\n }\n\n return fallback;\n };\n}\n\n/** Default `useView` bound to the built-in {@link MissingViewFallback}. */\nexport const useView = createUseView(MissingViewFallback);\n","import { useMemo } from \"react\";\nimport { useMcpTool } from \"@ethisyscore/extension-runtime/plugin\";\n\n/** Dispatches an MCP tool call by name. The seam a lifted REST service binds to. */\nexport type ToolInvoker = (toolName: string, args: unknown) => Promise<unknown>;\n\n/**\n * Base for service classes lifted from a monolith REST plane onto MCP.\n *\n * The monolith versions extend a REST base (`this.get`/`this.post` over axios).\n * The plugin runtime exposes no imperative MCP transport — only the\n * `useMcpTool` hook — so a `useXService()` hook composes per-tool invokers (see\n * {@link useToolInvokerMap}) into a single {@link ToolInvoker} and constructs\n * the class with it. Method bodies call `this.tool(\"<tool-name>\", args)`.\n */\nexport abstract class BaseMcpService {\n constructor(private readonly invoker: ToolInvoker) {}\n\n protected async tool<TRes>(name: string, args: unknown = {}): Promise<TRes> {\n return (await this.invoker(name, args)) as TRes;\n }\n}\n\n/**\n * Thrown by a lifted service method whose monolith REST route has no MCP tool\n * yet (typically because it needs client-side aggregation over a coarser tool).\n */\nexport function notViaMcp(serviceName: string, method: string): never {\n throw new Error(`${serviceName}.${method} has no MCP tool — pending per-surface aggregation.`);\n}\n\n/** Imperative invoker for a single MCP tool (the hook's `.invoke`, stable across renders). */\nexport function useToolInvoker<TReq, TRes>(toolName: string): (req: TReq) => Promise<TRes> {\n return useMcpTool<TReq, TRes>(toolName).invoke;\n}\n\n/**\n * Composes one {@link ToolInvoker} dispatching over every tool in `tools`, so a\n * service class can call tools by name.\n *\n * Rules of hooks: `tools` MUST be a frozen, constant-length list (e.g. a\n * module-level `as const` array) so the per-tool `useMcpTool` calls run in a\n * stable order on every render. Passing a list whose length varies between\n * renders is a violation and will break.\n */\nexport function useToolInvokerMap(tools: readonly string[]): ToolInvoker {\n const invokers: Record<string, (args: unknown) => Promise<unknown>> = {};\n for (const name of tools) {\n // eslint-disable-next-line react-hooks/rules-of-hooks -- `tools` is required to be frozen; call order is stable.\n invokers[name] = useMcpTool(name).invoke;\n }\n return useMemo<ToolInvoker>(\n () => (name, args) => {\n const fn = invokers[name];\n if (!fn) {\n throw new Error(`No MCP invoker registered for tool \"${name}\"`);\n }\n return fn(args);\n },\n // eslint-disable-next-line react-hooks/exhaustive-deps -- per-tool invoke refs are stable; rebuild only if one changes.\n tools.map((t) => invokers[t]),\n );\n}\n","import {\n useQueries,\n useQuery,\n type QueryKey,\n type UseQueryOptions,\n type UseQueryResult,\n} from \"@tanstack/react-query\";\n\n/**\n * Plugin shim for the monolith's `useAuthenticatedQuery`.\n *\n * In the host realm the MCP transport is already authenticated and scoped to\n * the active organisation, so there is no auth gate to apply. The hook keeps\n * the monolith's return shape (`isLoading` + `isAuthLoading`) so lifted data\n * hooks and pages consume it unchanged — `isAuthLoading` is always `false`.\n */\nexport interface AuthenticatedQueryResult<TData, TError>\n extends Omit<UseQueryResult<TData, TError>, \"isLoading\"> {\n isLoading: boolean;\n isAuthLoading: boolean;\n}\n\nexport function useAuthenticatedQuery<\n TQueryFnData = unknown,\n TError = Error,\n TData = TQueryFnData,\n TQueryKey extends QueryKey = QueryKey,\n>(\n options: UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>,\n): AuthenticatedQueryResult<TData, TError> {\n const result = useQuery(options);\n return {\n ...result,\n isLoading: result.isLoading,\n isAuthLoading: false,\n };\n}\n\nexport function useAuthenticatedQueries<TQueryFnData = unknown, TError = Error>(\n queries: UseQueryOptions<TQueryFnData, TError>[],\n) {\n return useQueries({ queries });\n}\n","import { createElement, type AnchorHTMLAttributes, type MouseEvent, type ReactNode } from \"react\";\nimport { emitNavigation } from \"@ethisyscore/components-react\";\n\n/**\n * Minimal `react-router-dom` compatibility shim for lifted monolith code.\n *\n * A PlatformReact plugin has no client router — navigation is brokered to the\n * host via {@link emitNavigation}, and route params come from the surface URL's\n * trailing segment(s). {@link createReactRouterShim} returns a module-shaped\n * object of the hooks/components lifted code expects; alias `react-router-dom`\n * to a tiny file that re-exports this object's members so call sites resolve\n * unchanged.\n *\n * `paramKeys` names the keys the single trailing path segment is exposed under,\n * so a route like `/…/<page>/<value>` satisfies `useParams<{ id }>()` /\n * `useParams<{ date }>()` regardless of which key the lifted code destructures.\n */\nexport interface ReactRouterShimOptions {\n /** Keys the trailing path segment is exposed under (default: `[\"id\"]`). */\n paramKeys?: readonly string[];\n}\n\ntype LinkProps = AnchorHTMLAttributes<HTMLAnchorElement> & {\n to: string;\n children?: ReactNode;\n};\n\nexport function createReactRouterShim(options: ReactRouterShimOptions = {}) {\n const paramKeys = options.paramKeys ?? [\"id\"];\n\n function useNavigate(): (to: string | number) => void {\n return (to) => {\n if (typeof to === \"number\") {\n if (typeof window !== \"undefined\") window.history.go(to);\n return;\n }\n emitNavigation(to);\n };\n }\n\n function useParams<\n T extends Record<string, string | undefined> = Record<string, string | undefined>,\n >(): T {\n const path = typeof window !== \"undefined\" ? window.location.pathname : \"\";\n const segments = path.split(\"/\").filter(Boolean);\n const last = segments[segments.length - 1];\n const params: Record<string, string | undefined> = {};\n if (last) {\n for (const key of paramKeys) {\n params[key] = last;\n }\n }\n return params as T;\n }\n\n function useSearchParams(): [\n URLSearchParams,\n (next: URLSearchParams | Record<string, string>) => void,\n ] {\n const search = typeof window !== \"undefined\" ? window.location.search : \"\";\n const params = new URLSearchParams(search);\n const setSearchParams = (next: URLSearchParams | Record<string, string>) => {\n const usp = next instanceof URLSearchParams ? next : new URLSearchParams(next);\n const qs = usp.toString();\n if (typeof window !== \"undefined\") {\n window.history.replaceState(\n {},\n \"\",\n qs ? `${window.location.pathname}?${qs}` : window.location.pathname,\n );\n }\n };\n return [params, setSearchParams];\n }\n\n function useMatch(\n _pattern: string,\n ): { params: Record<string, string | undefined>; pathname: string } | null {\n return null;\n }\n\n function useLocation() {\n const path = typeof window !== \"undefined\" ? window.location.pathname : \"/\";\n return { pathname: path, search: \"\", hash: \"\", state: null, key: \"default\" };\n }\n\n function Link({ to, children, onClick, ...rest }: LinkProps) {\n return createElement(\n \"a\",\n {\n href: to,\n onClick: (e: MouseEvent<HTMLAnchorElement>) => {\n e.preventDefault();\n onClick?.(e);\n emitNavigation(to);\n },\n ...rest,\n },\n children,\n );\n }\n\n return { useNavigate, useParams, useSearchParams, useMatch, useLocation, Link, NavLink: Link };\n}\n"]}
1
+ {"version":3,"sources":["../../src/platform-react/templateContext.ts","../../src/platform-react/injectStyles.ts","../../src/platform-react/definePlatformReactPluginPage.tsx","../../src/platform-react/MissingViewFallback.tsx","../../src/platform-react/useView.ts","../../src/platform-react/mcpService.ts","../../src/platform-react/useAuthenticatedQuery.ts","../../src/platform-react/reactRouterShim.tsx"],"names":["createContext","useEffect","QueryClient","createElement","QueryClientProvider","definePlatformReactPage","useView","useContext","useMcpTool","useMemo","useQuery","useQueries","emitNavigation"],"mappings":";;;;;;;;AAeO,IAAM,eAAA,GAAkBA,oBAAuC,IAAI;ACJnE,SAAS,kBAAA,CAAmB,IAAY,GAAA,EAAmB;AAChE,EAAA,IAAI,OAAO,aAAa,WAAA,EAAa;AACrC,EAAA,IAAI,QAAA,CAAS,cAAA,CAAe,EAAE,CAAA,EAAG;AACjC,EAAA,MAAM,EAAA,GAAK,QAAA,CAAS,aAAA,CAAc,OAAO,CAAA;AACzC,EAAA,EAAA,CAAG,EAAA,GAAK,EAAA;AACR,EAAA,EAAA,CAAG,WAAA,GAAc,GAAA;AACjB,EAAA,QAAA,CAAS,IAAA,CAAK,YAAY,EAAE,CAAA;AAC9B;AAGO,SAAS,gBAAA,CAAiB,EAAE,EAAA,EAAI,GAAA,EAAI,EAAsC;AAC/E,EAAAC,eAAA,CAAU,MAAM;AACd,IAAA,kBAAA,CAAmB,IAAI,GAAG,CAAA;AAAA,EAC5B,CAAA,EAAG,CAAC,EAAA,EAAI,GAAG,CAAC,CAAA;AACZ,EAAA,OAAO,IAAA;AACT;;;ACZA,SAAS,sBAAA,GAAsC;AAC7C,EAAA,OAAO,IAAIC,sBAAA,CAAY;AAAA,IACrB,cAAA,EAAgB;AAAA,MACd,OAAA,EAAS;AAAA,QACP,KAAA,EAAO,CAAA;AAAA,QACP,oBAAA,EAAsB,KAAA;AAAA,QACtB,SAAA,EAAW;AAAA;AACb;AACF,GACD,CAAA;AACH;AAEA,IAAM,qBAAqB,sBAAA,EAAuB;AAQ3C,IAAM,qBAAA,GAAwB;AAqB9B,SAAS,6BAAA,CACd,MACA,OAAA,EACuC;AACvC,EAAA,MAAM;AAAA,IACJ,WAAW,EAAC;AAAA,IACZ,aAAA;AAAA,IACA,OAAA;AAAA,IACA,GAAA;AAAA,IACA,WAAA,GAAc;AAAA,GAChB,GAAI,OAAA;AAEJ,EAAA,SAAS,WAAW,KAAA,EAA+B;AACjD,IAAA,OAAOC,mBAAA;AAAA,MACLC,8BAAA;AAAA,MACA,EAAE,QAAQ,WAAA,EAAY;AAAA,MACtBD,mBAAA;AAAA,QACE,eAAA,CAAgB,QAAA;AAAA,QAChB,EAAE,OAAO,QAAA,EAAS;AAAA,QAClBA,oBAAc,gBAAA,EAAkB,EAAE,EAAA,EAAI,OAAA,EAAS,KAAK,CAAA;AAAA,QACpDA,mBAAA;AAAA,UACE,KAAA;AAAA,UACA,EAAE,SAAA,EAAW,CAAA,EAAG,qBAAqB,CAAA,CAAA,EAAI,aAAa,CAAA,CAAA,EAAG;AAAA,UACzDA,mBAAA,CAAc,MAAM,KAAK;AAAA;AAC3B;AACF,KACF;AAAA,EACF;AAEA,EAAA,OAAOE,wCAAwB,UAAU,CAAA;AAC3C;AA2BO,SAAS,wBAAwB,MAAA,EAAiC;AACvE,EAAA,MAAM,EAAE,MAAA,EAAQ,GAAG,IAAA,EAAK,GAAI,MAAA;AAC5B,EAAA,OAAO,SAAS,gBAAA,CACd,IAAA,EACA,KAAA,GAAuC,EAAC,EACD;AACvC,IAAA,OAAO,6BAAA,CAA8B,IAAA,EAAM,EAAE,GAAG,IAAA,EAAM,QAAA,EAAU,EAAE,CAAC,MAAM,GAAG,KAAA,EAAM,EAAG,CAAA;AAAA,EACvF,CAAA;AACF;AChHO,IAAM,sBAA8D,MACzEF,mBAAAA;AAAA,EACE,KAAA;AAAA,EACA;AAAA,IACE,KAAA,EAAO;AAAA,MACL,OAAA,EAAS,MAAA;AAAA,MACT,UAAA,EAAY,QAAA;AAAA,MACZ,cAAA,EAAgB,QAAA;AAAA,MAChB,OAAA,EAAS;AAAA;AACX,GACF;AAAA,EACAA,mBAAAA;AAAA,IACE,KAAA;AAAA,IACA;AAAA,MACE,KAAA,EAAO;AAAA,QACL,QAAA,EAAU,OAAA;AAAA,QACV,OAAA,EAAS,QAAA;AAAA,QACT,MAAA,EAAQ,4BAAA;AAAA,QACR,YAAA,EAAc,QAAA;AAAA,QACd,SAAA,EAAW;AAAA;AACb,KACF;AAAA,IACAA,mBAAAA,CAAc,GAAA,EAAK,EAAE,KAAA,EAAO,EAAE,MAAA,EAAQ,YAAA,EAAc,UAAA,EAAY,GAAA,EAAI,EAAE,EAAG,oBAAoB,CAAA;AAAA,IAC7FA,mBAAAA;AAAA,MACE,GAAA;AAAA,MACA,EAAE,OAAO,EAAE,MAAA,EAAQ,GAAG,OAAA,EAAS,GAAA,EAAK,QAAA,EAAU,UAAA,EAAW,EAAE;AAAA,MAC3D;AAAA;AACF;AAEJ;;;ACjCF,IAAM,eAAA,mBAAkB,MAAA,CAAO,GAAA,CAAI,YAAY,CAAA;AAGxC,SAAS,YAAY,KAAA,EAAwC;AAClE,EAAA,IAAI,OAAO,KAAA,KAAU,UAAA,EAAY,OAAO,IAAA;AACxC,EAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,KAAU,IAAA,EAAM;AAC/C,IAAA,OAAQ,MAAkC,QAAA,KAAa,eAAA;AAAA,EACzD;AACA,EAAA,OAAO,KAAA;AACT;AAQO,SAAS,cAAc,QAAA,EAAkD;AAC9E,EAAA,OAAO,SAASG,QAAAA,CACd,MAAA,EACA,QAAA,EACwC;AACxC,IAAA,MAAM,QAAA,GAAWC,iBAAW,eAAe,CAAA;AAC3C,IAAA,IAAI,CAAC,QAAA,EAAU;AACb,MAAA,OAAO,QAAA;AAAA,IACT;AAEA,IAAA,MAAM,KAAA,GAAQ,SAAS,MAAM,CAAA;AAC7B,IAAA,IAAI,KAAA,IAAS,OAAO,KAAA,KAAU,QAAA,IAAY,YAAa,KAAA,EAAmC;AACxF,MAAA,MAAM,SAAA,GAAa,MAAkC,QAAQ,CAAA;AAC7D,MAAA,IAAI,WAAA,CAAY,SAAS,CAAA,EAAG;AAC1B,QAAA,OAAO,SAAA;AAAA,MACT;AAAA,IACF;AAEA,IAAA,OAAO,QAAA;AAAA,EACT,CAAA;AACF;AAGO,IAAM,OAAA,GAAU,cAAc,mBAAmB;AC7BjD,IAAe,iBAAf,MAA8B;AAAA,EACnC,YAA6B,OAAA,EAAsB;AAAtB,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA;AAAA,EAAuB;AAAA,EAAvB,OAAA;AAAA,EAE7B,MAAgB,IAAA,CAAW,IAAA,EAAc,IAAA,GAAgB,EAAC,EAAkB;AAC1E,IAAA,OAAQ,MAAM,IAAA,CAAK,OAAA,CAAQ,IAAA,EAAM,IAAI,CAAA;AAAA,EACvC;AACF;AAMO,SAAS,SAAA,CAAU,aAAqB,MAAA,EAAuB;AACpE,EAAA,MAAM,IAAI,KAAA,CAAM,CAAA,EAAG,WAAW,CAAA,CAAA,EAAI,MAAM,CAAA,wDAAA,CAAqD,CAAA;AAC/F;AAGO,SAAS,eAA2B,QAAA,EAAgD;AACzF,EAAA,OAAOC,iBAAA,CAAuB,QAAQ,CAAA,CAAE,MAAA;AAC1C;AAWO,SAAS,kBAAkB,KAAA,EAAuC;AACvE,EAAA,MAAM,WAAgE,EAAC;AACvE,EAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AAExB,IAAA,QAAA,CAAS,IAAI,CAAA,GAAIA,iBAAA,CAAW,IAAI,CAAA,CAAE,MAAA;AAAA,EACpC;AACA,EAAA,OAAOC,aAAA;AAAA,IACL,MAAM,CAAC,IAAA,EAAM,IAAA,KAAS;AACpB,MAAA,MAAM,EAAA,GAAK,SAAS,IAAI,CAAA;AACxB,MAAA,IAAI,CAAC,EAAA,EAAI;AACP,QAAA,MAAM,IAAI,KAAA,CAAM,CAAA,oCAAA,EAAuC,IAAI,CAAA,CAAA,CAAG,CAAA;AAAA,MAChE;AACA,MAAA,OAAO,GAAG,IAAI,CAAA;AAAA,IAChB,CAAA;AAAA;AAAA,IAEA,MAAM,GAAA,CAAI,CAAC,CAAA,KAAM,QAAA,CAAS,CAAC,CAAC;AAAA,GAC9B;AACF;ACxCO,SAAS,sBAMd,OAAA,EACyC;AACzC,EAAA,MAAM,MAAA,GAASC,oBAAS,OAAO,CAAA;AAC/B,EAAA,OAAO;AAAA,IACL,GAAG,MAAA;AAAA,IACH,WAAW,MAAA,CAAO,SAAA;AAAA,IAClB,aAAA,EAAe;AAAA,GACjB;AACF;AAEO,SAAS,wBACd,OAAA,EACA;AACA,EAAA,OAAOC,qBAAA,CAAW,EAAE,OAAA,EAAS,CAAA;AAC/B;ACfO,SAAS,qBAAA,CAAsB,OAAA,GAAkC,EAAC,EAAG;AAC1E,EAAA,MAAM,SAAA,GAAY,OAAA,CAAQ,SAAA,IAAa,CAAC,IAAI,CAAA;AAE5C,EAAA,SAAS,WAAA,GAA6C;AACpD,IAAA,OAAO,CAAC,EAAA,KAAO;AACb,MAAA,IAAI,OAAO,OAAO,QAAA,EAAU;AAC1B,QAAA,IAAI,OAAO,MAAA,KAAW,WAAA,EAAa,MAAA,CAAO,OAAA,CAAQ,GAAG,EAAE,CAAA;AACvD,QAAA;AAAA,MACF;AACA,MAAAC,8BAAA,CAAe,EAAE,CAAA;AAAA,IACnB,CAAA;AAAA,EACF;AAEA,EAAA,SAAS,SAAA,GAEF;AACL,IAAA,MAAM,OAAO,OAAO,MAAA,KAAW,WAAA,GAAc,MAAA,CAAO,SAAS,QAAA,GAAW,EAAA;AACxE,IAAA,MAAM,WAAW,IAAA,CAAK,KAAA,CAAM,GAAG,CAAA,CAAE,OAAO,OAAO,CAAA;AAC/C,IAAA,MAAM,IAAA,GAAO,QAAA,CAAS,QAAA,CAAS,MAAA,GAAS,CAAC,CAAA;AACzC,IAAA,MAAM,SAA6C,EAAC;AACpD,IAAA,IAAI,IAAA,EAAM;AACR,MAAA,KAAA,MAAW,OAAO,SAAA,EAAW;AAC3B,QAAA,MAAA,CAAO,GAAG,CAAA,GAAI,IAAA;AAAA,MAChB;AAAA,IACF;AACA,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,SAAS,eAAA,GAGP;AACA,IAAA,MAAM,SAAS,OAAO,MAAA,KAAW,WAAA,GAAc,MAAA,CAAO,SAAS,MAAA,GAAS,EAAA;AACxE,IAAA,MAAM,MAAA,GAAS,IAAI,eAAA,CAAgB,MAAM,CAAA;AACzC,IAAA,MAAM,eAAA,GAAkB,CAAC,IAAA,KAAmD;AAC1E,MAAA,MAAM,MAAM,IAAA,YAAgB,eAAA,GAAkB,IAAA,GAAO,IAAI,gBAAgB,IAAI,CAAA;AAC7E,MAAA,MAAM,EAAA,GAAK,IAAI,QAAA,EAAS;AACxB,MAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AACjC,QAAA,MAAA,CAAO,OAAA,CAAQ,YAAA;AAAA,UACb,EAAC;AAAA,UACD,EAAA;AAAA,UACA,EAAA,GAAK,GAAG,MAAA,CAAO,QAAA,CAAS,QAAQ,CAAA,CAAA,EAAI,EAAE,CAAA,CAAA,GAAK,MAAA,CAAO,QAAA,CAAS;AAAA,SAC7D;AAAA,MACF;AAAA,IACF,CAAA;AACA,IAAA,OAAO,CAAC,QAAQ,eAAe,CAAA;AAAA,EACjC;AAEA,EAAA,SAAS,SACP,QAAA,EACyE;AACzE,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,SAAS,WAAA,GAAc;AACrB,IAAA,MAAM,OAAO,OAAO,MAAA,KAAW,WAAA,GAAc,MAAA,CAAO,SAAS,QAAA,GAAW,GAAA;AACxE,IAAA,OAAO,EAAE,QAAA,EAAU,IAAA,EAAM,MAAA,EAAQ,EAAA,EAAI,MAAM,EAAA,EAAI,KAAA,EAAO,IAAA,EAAM,GAAA,EAAK,SAAA,EAAU;AAAA,EAC7E;AAEA,EAAA,SAAS,KAAK,EAAE,EAAA,EAAI,UAAU,OAAA,EAAS,GAAG,MAAK,EAAc;AAC3D,IAAA,OAAOT,mBAAAA;AAAA,MACL,GAAA;AAAA,MACA;AAAA,QACE,IAAA,EAAM,EAAA;AAAA,QACN,OAAA,EAAS,CAAC,CAAA,KAAqC;AAC7C,UAAA,CAAA,CAAE,cAAA,EAAe;AACjB,UAAA,OAAA,GAAU,CAAC,CAAA;AACX,UAAAS,8BAAA,CAAe,EAAE,CAAA;AAAA,QACnB,CAAA;AAAA,QACA,GAAG;AAAA,OACL;AAAA,MACA;AAAA,KACF;AAAA,EACF;AAEA,EAAA,OAAO,EAAE,aAAa,SAAA,EAAW,eAAA,EAAiB,UAAU,WAAA,EAAa,IAAA,EAAM,SAAS,IAAA,EAAK;AAC/F","file":"index.cjs","sourcesContent":["import { createContext, type ComponentType } from \"react\";\n\n/** A view component resolved from the template manifest. Props are open. */\nexport type ViewComponent = ComponentType<Record<string, unknown>>;\n\n/**\n * Static port of the monolith's gogo-ui adapter mechanism for PlatformReact.\n *\n * A monolith feature picks its adapter view at runtime via `import.meta.glob`;\n * a PlatformReact page is a single self-contained ESM bundle where that glob is\n * unavailable, so each page declares its view slice statically and provides it\n * through this context. The shape is `manifest[module][viewName] = Component`.\n */\nexport type TemplateManifest = Record<string, Partial<Record<string, ViewComponent>>>;\n\nexport const TemplateContext = createContext<TemplateManifest | null>(null);\n","import { useEffect } from \"react\";\n\n/**\n * Injects a plugin's compiled CSS into the host document once, keyed by a\n * stable element id (idempotent across pages and re-mounts).\n *\n * Each PlatformReact page is a separate ESM bundle with no companion CSS asset\n * the host would load, so the styles travel inside the bundle (typically via a\n * `*.css?inline` import) and are mounted at runtime. Scope the CSS to a plugin\n * root class (never `:root`) so it cannot override the host theme.\n */\nexport function injectPluginStyles(id: string, css: string): void {\n if (typeof document === \"undefined\") return;\n if (document.getElementById(id)) return;\n const el = document.createElement(\"style\");\n el.id = id;\n el.textContent = css;\n document.head.appendChild(el);\n}\n\n/** Component form of {@link injectPluginStyles}; renders nothing. */\nexport function PluginStyleScope({ id, css }: { id: string; css: string }): null {\n useEffect(() => {\n injectPluginStyles(id, css);\n }, [id, css]);\n return null;\n}\n","import { createElement, type ComponentType } from \"react\";\nimport { QueryClient, QueryClientProvider } from \"@tanstack/react-query\";\nimport {\n definePlatformReactPage,\n type PlatformReactPageProps,\n} from \"@ethisyscore/components-react\";\nimport { TemplateContext, type TemplateManifest, type ViewComponent } from \"./templateContext\";\nimport { PluginStyleScope } from \"./injectStyles\";\n\n/**\n * Default per-page `QueryClient`. Each PlatformReact page is an isolated ESM\n * bundle, so this module-level singleton is scoped to that page — exactly the\n * lifetime a page wants. Plugins needing different options pass their own.\n */\nfunction makeDefaultQueryClient(): QueryClient {\n return new QueryClient({\n defaultOptions: {\n queries: {\n retry: 1,\n refetchOnWindowFocus: false,\n staleTime: 30_000,\n },\n },\n });\n}\n\nconst defaultQueryClient = makeDefaultQueryClient();\n\n/**\n * Shared root class applied to every plugin page wrapper, in addition to the\n * plugin's own `rootClassName`. The SDK's `plugin-base.css` (scoped resets +\n * component overrides) targets this class, so host-parity styling reaches a\n * plugin's surface deterministically regardless of its plugin-specific class.\n */\nexport const EHX_PLUGIN_ROOT_CLASS = \"ehx-plugin-root\";\n\nexport interface DefinePlatformReactPluginPageOptions {\n /** The page's static `useView` manifest slice (no `import.meta.glob` in a single-file bundle). */\n manifest?: TemplateManifest;\n /** Scoped style-root class wrapped around the page (e.g. `ethisys-<plugin>-root`). */\n rootClassName: string;\n /** Stable id for the injected `<style>` element (idempotent across pages). */\n styleId: string;\n /** The plugin's compiled CSS string (typically a `*.css?inline` import). */\n css: string;\n /** Override the per-page `QueryClient`. */\n queryClient?: QueryClient;\n}\n\n/**\n * Wraps a lifted page as a PlatformReact entry with the providers the monolith\n * took for granted: react-query, the static `useView` manifest, and the scoped\n * style root. The host already provides the MCP transport (it mounts pages\n * inside its own runtime provider), so this adds only the in-page providers.\n */\nexport function definePlatformReactPluginPage(\n Page: ComponentType<PlatformReactPageProps>,\n options: DefinePlatformReactPluginPageOptions,\n): ComponentType<PlatformReactPageProps> {\n const {\n manifest = {},\n rootClassName,\n styleId,\n css,\n queryClient = defaultQueryClient,\n } = options;\n\n function PluginPage(props: PlatformReactPageProps) {\n return createElement(\n QueryClientProvider,\n { client: queryClient },\n createElement(\n TemplateContext.Provider,\n { value: manifest },\n createElement(PluginStyleScope, { id: styleId, css }),\n createElement(\n \"div\",\n { className: `${EHX_PLUGIN_ROOT_CLASS} ${rootClassName}` },\n createElement(Page, props),\n ),\n ),\n );\n }\n\n return definePlatformReactPage(PluginPage);\n}\n\nexport interface PluginPageDefinerConfig {\n /**\n * The plugin's `useView` module key (e.g. `\"timesheets\"`). Each page's `views`\n * are registered under it, so a page's `useView(module, name)` resolves them.\n */\n module: string;\n /** Scoped style-root class wrapped around every page (e.g. `ethisys-<plugin>-root`). */\n rootClassName: string;\n /** Stable id for the injected `<style>` element. */\n styleId: string;\n /** The plugin's compiled CSS string (typically a `*.css?inline` import). */\n css: string;\n /** Override the per-page `QueryClient`. */\n queryClient?: QueryClient;\n}\n\n/**\n * Builds a plugin-bound `definePluginPage(Page, views)` from one config, so a\n * plugin writes the four plugin-specific values (module key, root class, style\n * id, CSS) ONCE instead of in every page entry. Each surface entry then calls\n * `definePluginPage(Page, { ViewName })`; the flat `views` map is wrapped into\n * the `{ [module]: views }` manifest slice the page's `useView(module, name)`\n * reads. The generic provider/manifest/style wrapping stays in\n * {@link definePlatformReactPluginPage}.\n */\nexport function createPluginPageDefiner(config: PluginPageDefinerConfig) {\n const { module, ...rest } = config;\n return function definePluginPage(\n Page: ComponentType<PlatformReactPageProps>,\n views: Record<string, ViewComponent> = {},\n ): ComponentType<PlatformReactPageProps> {\n return definePlatformReactPluginPage(Page, { ...rest, manifest: { [module]: views } });\n };\n}\n","import { createElement, type ComponentType } from \"react\";\n\n/**\n * Dependency-light fallback rendered when a view is not present in the current\n * template manifest. Kept to plain elements + inline styles so the foundation\n * package carries no UI-library dependency; plugins that want a branded\n * fallback pass their own via {@link createUseView}.\n */\nexport const MissingViewFallback: ComponentType<Record<string, unknown>> = () =>\n createElement(\n \"div\",\n {\n style: {\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n padding: \"2rem\",\n },\n },\n createElement(\n \"div\",\n {\n style: {\n maxWidth: \"28rem\",\n padding: \"1.5rem\",\n border: \"1px solid rgba(0,0,0,0.12)\",\n borderRadius: \"0.5rem\",\n textAlign: \"center\",\n },\n },\n createElement(\"p\", { style: { margin: \"0 0 0.5rem\", fontWeight: 600 } }, \"View not available\"),\n createElement(\n \"p\",\n { style: { margin: 0, opacity: 0.7, fontSize: \"0.875rem\" } },\n \"This view has not been implemented by the current template.\",\n ),\n ),\n );\n","import { useContext, type ComponentType } from \"react\";\nimport { TemplateContext } from \"./templateContext\";\nimport { MissingViewFallback } from \"./MissingViewFallback\";\n\nconst REACT_LAZY_TYPE = Symbol.for(\"react.lazy\");\n\n/** Matches regular function components and `React.lazy` wrappers. */\nexport function isComponent(value: unknown): value is ComponentType {\n if (typeof value === \"function\") return true;\n if (typeof value === \"object\" && value !== null) {\n return (value as Record<string, unknown>).$$typeof === REACT_LAZY_TYPE;\n }\n return false;\n}\n\n/**\n * Builds a `useView` hook bound to a fallback component. The hook is a safe\n * view accessor that never throws: it returns `manifest[module][viewName]` when\n * present, or the supplied fallback when the manifest, module slice, or view is\n * missing or is not a component.\n */\nexport function createUseView(fallback: ComponentType<Record<string, unknown>>) {\n return function useView(\n module: string,\n viewName: string,\n ): ComponentType<Record<string, unknown>> {\n const manifest = useContext(TemplateContext);\n if (!manifest) {\n return fallback;\n }\n\n const slice = manifest[module];\n if (slice && typeof slice === \"object\" && viewName in (slice as Record<string, unknown>)) {\n const component = (slice as Record<string, unknown>)[viewName];\n if (isComponent(component)) {\n return component as ComponentType<Record<string, unknown>>;\n }\n }\n\n return fallback;\n };\n}\n\n/** Default `useView` bound to the built-in {@link MissingViewFallback}. */\nexport const useView = createUseView(MissingViewFallback);\n","import { useMemo } from \"react\";\nimport { useMcpTool } from \"@ethisyscore/extension-runtime/plugin\";\n\n/** Dispatches an MCP tool call by name. The seam a lifted REST service binds to. */\nexport type ToolInvoker = (toolName: string, args: unknown) => Promise<unknown>;\n\n/**\n * Base for service classes lifted from a monolith REST plane onto MCP.\n *\n * The monolith versions extend a REST base (`this.get`/`this.post` over axios).\n * The plugin runtime exposes no imperative MCP transport — only the\n * `useMcpTool` hook — so a `useXService()` hook composes per-tool invokers (see\n * {@link useToolInvokerMap}) into a single {@link ToolInvoker} and constructs\n * the class with it. Method bodies call `this.tool(\"<tool-name>\", args)`.\n */\nexport abstract class BaseMcpService {\n constructor(private readonly invoker: ToolInvoker) {}\n\n protected async tool<TRes>(name: string, args: unknown = {}): Promise<TRes> {\n return (await this.invoker(name, args)) as TRes;\n }\n}\n\n/**\n * Thrown by a lifted service method whose monolith REST route has no MCP tool\n * yet (typically because it needs client-side aggregation over a coarser tool).\n */\nexport function notViaMcp(serviceName: string, method: string): never {\n throw new Error(`${serviceName}.${method} has no MCP tool — pending per-surface aggregation.`);\n}\n\n/** Imperative invoker for a single MCP tool (the hook's `.invoke`, stable across renders). */\nexport function useToolInvoker<TReq, TRes>(toolName: string): (req: TReq) => Promise<TRes> {\n return useMcpTool<TReq, TRes>(toolName).invoke;\n}\n\n/**\n * Composes one {@link ToolInvoker} dispatching over every tool in `tools`, so a\n * service class can call tools by name.\n *\n * Rules of hooks: `tools` MUST be a frozen, constant-length list (e.g. a\n * module-level `as const` array) so the per-tool `useMcpTool` calls run in a\n * stable order on every render. Passing a list whose length varies between\n * renders is a violation and will break.\n */\nexport function useToolInvokerMap(tools: readonly string[]): ToolInvoker {\n const invokers: Record<string, (args: unknown) => Promise<unknown>> = {};\n for (const name of tools) {\n // eslint-disable-next-line react-hooks/rules-of-hooks -- `tools` is required to be frozen; call order is stable.\n invokers[name] = useMcpTool(name).invoke;\n }\n return useMemo<ToolInvoker>(\n () => (name, args) => {\n const fn = invokers[name];\n if (!fn) {\n throw new Error(`No MCP invoker registered for tool \"${name}\"`);\n }\n return fn(args);\n },\n // eslint-disable-next-line react-hooks/exhaustive-deps -- per-tool invoke refs are stable; rebuild only if one changes.\n tools.map((t) => invokers[t]),\n );\n}\n","import {\n useQueries,\n useQuery,\n type QueryKey,\n type UseQueryOptions,\n type UseQueryResult,\n} from \"@tanstack/react-query\";\n\n/**\n * Plugin shim for the monolith's `useAuthenticatedQuery`.\n *\n * In the host realm the MCP transport is already authenticated and scoped to\n * the active organisation, so there is no auth gate to apply. The hook keeps\n * the monolith's return shape (`isLoading` + `isAuthLoading`) so lifted data\n * hooks and pages consume it unchanged — `isAuthLoading` is always `false`.\n */\nexport interface AuthenticatedQueryResult<TData, TError>\n extends Omit<UseQueryResult<TData, TError>, \"isLoading\"> {\n isLoading: boolean;\n isAuthLoading: boolean;\n}\n\nexport function useAuthenticatedQuery<\n TQueryFnData = unknown,\n TError = Error,\n TData = TQueryFnData,\n TQueryKey extends QueryKey = QueryKey,\n>(\n options: UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>,\n): AuthenticatedQueryResult<TData, TError> {\n const result = useQuery(options);\n return {\n ...result,\n isLoading: result.isLoading,\n isAuthLoading: false,\n };\n}\n\nexport function useAuthenticatedQueries<TQueryFnData = unknown, TError = Error>(\n queries: UseQueryOptions<TQueryFnData, TError>[],\n) {\n return useQueries({ queries });\n}\n","import { createElement, type AnchorHTMLAttributes, type MouseEvent, type ReactNode } from \"react\";\nimport { emitNavigation } from \"@ethisyscore/components-react\";\n\n/**\n * Minimal `react-router-dom` compatibility shim for lifted monolith code.\n *\n * A PlatformReact plugin has no client router — navigation is brokered to the\n * host via {@link emitNavigation}, and route params come from the surface URL's\n * trailing segment(s). {@link createReactRouterShim} returns a module-shaped\n * object of the hooks/components lifted code expects; alias `react-router-dom`\n * to a tiny file that re-exports this object's members so call sites resolve\n * unchanged.\n *\n * `paramKeys` names the keys the single trailing path segment is exposed under,\n * so a route like `/…/<page>/<value>` satisfies `useParams<{ id }>()` /\n * `useParams<{ date }>()` regardless of which key the lifted code destructures.\n */\nexport interface ReactRouterShimOptions {\n /** Keys the trailing path segment is exposed under (default: `[\"id\"]`). */\n paramKeys?: readonly string[];\n}\n\ntype LinkProps = AnchorHTMLAttributes<HTMLAnchorElement> & {\n to: string;\n children?: ReactNode;\n};\n\nexport function createReactRouterShim(options: ReactRouterShimOptions = {}) {\n const paramKeys = options.paramKeys ?? [\"id\"];\n\n function useNavigate(): (to: string | number) => void {\n return (to) => {\n if (typeof to === \"number\") {\n if (typeof window !== \"undefined\") window.history.go(to);\n return;\n }\n emitNavigation(to);\n };\n }\n\n function useParams<\n T extends Record<string, string | undefined> = Record<string, string | undefined>,\n >(): T {\n const path = typeof window !== \"undefined\" ? window.location.pathname : \"\";\n const segments = path.split(\"/\").filter(Boolean);\n const last = segments[segments.length - 1];\n const params: Record<string, string | undefined> = {};\n if (last) {\n for (const key of paramKeys) {\n params[key] = last;\n }\n }\n return params as T;\n }\n\n function useSearchParams(): [\n URLSearchParams,\n (next: URLSearchParams | Record<string, string>) => void,\n ] {\n const search = typeof window !== \"undefined\" ? window.location.search : \"\";\n const params = new URLSearchParams(search);\n const setSearchParams = (next: URLSearchParams | Record<string, string>) => {\n const usp = next instanceof URLSearchParams ? next : new URLSearchParams(next);\n const qs = usp.toString();\n if (typeof window !== \"undefined\") {\n window.history.replaceState(\n {},\n \"\",\n qs ? `${window.location.pathname}?${qs}` : window.location.pathname,\n );\n }\n };\n return [params, setSearchParams];\n }\n\n function useMatch(\n _pattern: string,\n ): { params: Record<string, string | undefined>; pathname: string } | null {\n return null;\n }\n\n function useLocation() {\n const path = typeof window !== \"undefined\" ? window.location.pathname : \"/\";\n return { pathname: path, search: \"\", hash: \"\", state: null, key: \"default\" };\n }\n\n function Link({ to, children, onClick, ...rest }: LinkProps) {\n return createElement(\n \"a\",\n {\n href: to,\n onClick: (e: MouseEvent<HTMLAnchorElement>) => {\n e.preventDefault();\n onClick?.(e);\n emitNavigation(to);\n },\n ...rest,\n },\n children,\n );\n }\n\n return { useNavigate, useParams, useSearchParams, useMatch, useLocation, Link, NavLink: Link };\n}\n"]}
@@ -18,6 +18,13 @@ type ViewComponent = ComponentType<Record<string, unknown>>;
18
18
  type TemplateManifest = Record<string, Partial<Record<string, ViewComponent>>>;
19
19
  declare const TemplateContext: react.Context<TemplateManifest | null>;
20
20
 
21
+ /**
22
+ * Shared root class applied to every plugin page wrapper, in addition to the
23
+ * plugin's own `rootClassName`. The SDK's `plugin-base.css` (scoped resets +
24
+ * component overrides) targets this class, so host-parity styling reaches a
25
+ * plugin's surface deterministically regardless of its plugin-specific class.
26
+ */
27
+ declare const EHX_PLUGIN_ROOT_CLASS = "ehx-plugin-root";
21
28
  interface DefinePlatformReactPluginPageOptions {
22
29
  /** The page's static `useView` manifest slice (no `import.meta.glob` in a single-file bundle). */
23
30
  manifest?: TemplateManifest;
@@ -737,4 +744,4 @@ declare function createReactRouterShim(options?: ReactRouterShimOptions): {
737
744
  }, HTMLElement>;
738
745
  };
739
746
 
740
- export { type AuthenticatedQueryResult, BaseMcpService, type DefinePlatformReactPluginPageOptions, MissingViewFallback, type PluginPageDefinerConfig, PluginStyleScope, type ReactRouterShimOptions, TemplateContext, type TemplateManifest, type ToolInvoker, type ViewComponent, createPluginPageDefiner, createReactRouterShim, createUseView, definePlatformReactPluginPage, injectPluginStyles, isComponent, notViaMcp, useAuthenticatedQueries, useAuthenticatedQuery, useToolInvoker, useToolInvokerMap, useView };
747
+ export { type AuthenticatedQueryResult, BaseMcpService, type DefinePlatformReactPluginPageOptions, EHX_PLUGIN_ROOT_CLASS, MissingViewFallback, type PluginPageDefinerConfig, PluginStyleScope, type ReactRouterShimOptions, TemplateContext, type TemplateManifest, type ToolInvoker, type ViewComponent, createPluginPageDefiner, createReactRouterShim, createUseView, definePlatformReactPluginPage, injectPluginStyles, isComponent, notViaMcp, useAuthenticatedQueries, useAuthenticatedQuery, useToolInvoker, useToolInvokerMap, useView };
@@ -18,6 +18,13 @@ type ViewComponent = ComponentType<Record<string, unknown>>;
18
18
  type TemplateManifest = Record<string, Partial<Record<string, ViewComponent>>>;
19
19
  declare const TemplateContext: react.Context<TemplateManifest | null>;
20
20
 
21
+ /**
22
+ * Shared root class applied to every plugin page wrapper, in addition to the
23
+ * plugin's own `rootClassName`. The SDK's `plugin-base.css` (scoped resets +
24
+ * component overrides) targets this class, so host-parity styling reaches a
25
+ * plugin's surface deterministically regardless of its plugin-specific class.
26
+ */
27
+ declare const EHX_PLUGIN_ROOT_CLASS = "ehx-plugin-root";
21
28
  interface DefinePlatformReactPluginPageOptions {
22
29
  /** The page's static `useView` manifest slice (no `import.meta.glob` in a single-file bundle). */
23
30
  manifest?: TemplateManifest;
@@ -737,4 +744,4 @@ declare function createReactRouterShim(options?: ReactRouterShimOptions): {
737
744
  }, HTMLElement>;
738
745
  };
739
746
 
740
- export { type AuthenticatedQueryResult, BaseMcpService, type DefinePlatformReactPluginPageOptions, MissingViewFallback, type PluginPageDefinerConfig, PluginStyleScope, type ReactRouterShimOptions, TemplateContext, type TemplateManifest, type ToolInvoker, type ViewComponent, createPluginPageDefiner, createReactRouterShim, createUseView, definePlatformReactPluginPage, injectPluginStyles, isComponent, notViaMcp, useAuthenticatedQueries, useAuthenticatedQuery, useToolInvoker, useToolInvokerMap, useView };
747
+ export { type AuthenticatedQueryResult, BaseMcpService, type DefinePlatformReactPluginPageOptions, EHX_PLUGIN_ROOT_CLASS, MissingViewFallback, type PluginPageDefinerConfig, PluginStyleScope, type ReactRouterShimOptions, TemplateContext, type TemplateManifest, type ToolInvoker, type ViewComponent, createPluginPageDefiner, createReactRouterShim, createUseView, definePlatformReactPluginPage, injectPluginStyles, isComponent, notViaMcp, useAuthenticatedQueries, useAuthenticatedQuery, useToolInvoker, useToolInvokerMap, useView };
@@ -33,6 +33,7 @@ function makeDefaultQueryClient() {
33
33
  });
34
34
  }
35
35
  var defaultQueryClient = makeDefaultQueryClient();
36
+ var EHX_PLUGIN_ROOT_CLASS = "ehx-plugin-root";
36
37
  function definePlatformReactPluginPage(Page, options) {
37
38
  const {
38
39
  manifest = {},
@@ -49,7 +50,11 @@ function definePlatformReactPluginPage(Page, options) {
49
50
  TemplateContext.Provider,
50
51
  { value: manifest },
51
52
  createElement(PluginStyleScope, { id: styleId, css }),
52
- createElement("div", { className: rootClassName }, createElement(Page, props))
53
+ createElement(
54
+ "div",
55
+ { className: `${EHX_PLUGIN_ROOT_CLASS} ${rootClassName}` },
56
+ createElement(Page, props)
57
+ )
53
58
  )
54
59
  );
55
60
  }
@@ -224,6 +229,6 @@ function createReactRouterShim(options = {}) {
224
229
  return { useNavigate, useParams, useSearchParams, useMatch, useLocation, Link, NavLink: Link };
225
230
  }
226
231
 
227
- export { BaseMcpService, MissingViewFallback, PluginStyleScope, TemplateContext, createPluginPageDefiner, createReactRouterShim, createUseView, definePlatformReactPluginPage, injectPluginStyles, isComponent, notViaMcp, useAuthenticatedQueries, useAuthenticatedQuery, useToolInvoker, useToolInvokerMap, useView };
232
+ export { BaseMcpService, EHX_PLUGIN_ROOT_CLASS, MissingViewFallback, PluginStyleScope, TemplateContext, createPluginPageDefiner, createReactRouterShim, createUseView, definePlatformReactPluginPage, injectPluginStyles, isComponent, notViaMcp, useAuthenticatedQueries, useAuthenticatedQuery, useToolInvoker, useToolInvokerMap, useView };
228
233
  //# sourceMappingURL=index.js.map
229
234
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/platform-react/templateContext.ts","../../src/platform-react/injectStyles.ts","../../src/platform-react/definePlatformReactPluginPage.tsx","../../src/platform-react/MissingViewFallback.tsx","../../src/platform-react/useView.ts","../../src/platform-react/mcpService.ts","../../src/platform-react/useAuthenticatedQuery.ts","../../src/platform-react/reactRouterShim.tsx"],"names":["createElement","useView"],"mappings":"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","file":"index.js","sourcesContent":["import { createContext, type ComponentType } from \"react\";\n\n/** A view component resolved from the template manifest. Props are open. */\nexport type ViewComponent = ComponentType<Record<string, unknown>>;\n\n/**\n * Static port of the monolith's gogo-ui adapter mechanism for PlatformReact.\n *\n * A monolith feature picks its adapter view at runtime via `import.meta.glob`;\n * a PlatformReact page is a single self-contained ESM bundle where that glob is\n * unavailable, so each page declares its view slice statically and provides it\n * through this context. The shape is `manifest[module][viewName] = Component`.\n */\nexport type TemplateManifest = Record<string, Partial<Record<string, ViewComponent>>>;\n\nexport const TemplateContext = createContext<TemplateManifest | null>(null);\n","import { useEffect } from \"react\";\n\n/**\n * Injects a plugin's compiled CSS into the host document once, keyed by a\n * stable element id (idempotent across pages and re-mounts).\n *\n * Each PlatformReact page is a separate ESM bundle with no companion CSS asset\n * the host would load, so the styles travel inside the bundle (typically via a\n * `*.css?inline` import) and are mounted at runtime. Scope the CSS to a plugin\n * root class (never `:root`) so it cannot override the host theme.\n */\nexport function injectPluginStyles(id: string, css: string): void {\n if (typeof document === \"undefined\") return;\n if (document.getElementById(id)) return;\n const el = document.createElement(\"style\");\n el.id = id;\n el.textContent = css;\n document.head.appendChild(el);\n}\n\n/** Component form of {@link injectPluginStyles}; renders nothing. */\nexport function PluginStyleScope({ id, css }: { id: string; css: string }): null {\n useEffect(() => {\n injectPluginStyles(id, css);\n }, [id, css]);\n return null;\n}\n","import { createElement, type ComponentType } from \"react\";\nimport { QueryClient, QueryClientProvider } from \"@tanstack/react-query\";\nimport {\n definePlatformReactPage,\n type PlatformReactPageProps,\n} from \"@ethisyscore/components-react\";\nimport { TemplateContext, type TemplateManifest, type ViewComponent } from \"./templateContext\";\nimport { PluginStyleScope } from \"./injectStyles\";\n\n/**\n * Default per-page `QueryClient`. Each PlatformReact page is an isolated ESM\n * bundle, so this module-level singleton is scoped to that page — exactly the\n * lifetime a page wants. Plugins needing different options pass their own.\n */\nfunction makeDefaultQueryClient(): QueryClient {\n return new QueryClient({\n defaultOptions: {\n queries: {\n retry: 1,\n refetchOnWindowFocus: false,\n staleTime: 30_000,\n },\n },\n });\n}\n\nconst defaultQueryClient = makeDefaultQueryClient();\n\nexport interface DefinePlatformReactPluginPageOptions {\n /** The page's static `useView` manifest slice (no `import.meta.glob` in a single-file bundle). */\n manifest?: TemplateManifest;\n /** Scoped style-root class wrapped around the page (e.g. `ethisys-<plugin>-root`). */\n rootClassName: string;\n /** Stable id for the injected `<style>` element (idempotent across pages). */\n styleId: string;\n /** The plugin's compiled CSS string (typically a `*.css?inline` import). */\n css: string;\n /** Override the per-page `QueryClient`. */\n queryClient?: QueryClient;\n}\n\n/**\n * Wraps a lifted page as a PlatformReact entry with the providers the monolith\n * took for granted: react-query, the static `useView` manifest, and the scoped\n * style root. The host already provides the MCP transport (it mounts pages\n * inside its own runtime provider), so this adds only the in-page providers.\n */\nexport function definePlatformReactPluginPage(\n Page: ComponentType<PlatformReactPageProps>,\n options: DefinePlatformReactPluginPageOptions,\n): ComponentType<PlatformReactPageProps> {\n const {\n manifest = {},\n rootClassName,\n styleId,\n css,\n queryClient = defaultQueryClient,\n } = options;\n\n function PluginPage(props: PlatformReactPageProps) {\n return createElement(\n QueryClientProvider,\n { client: queryClient },\n createElement(\n TemplateContext.Provider,\n { value: manifest },\n createElement(PluginStyleScope, { id: styleId, css }),\n createElement(\"div\", { className: rootClassName }, createElement(Page, props)),\n ),\n );\n }\n\n return definePlatformReactPage(PluginPage);\n}\n\nexport interface PluginPageDefinerConfig {\n /**\n * The plugin's `useView` module key (e.g. `\"timesheets\"`). Each page's `views`\n * are registered under it, so a page's `useView(module, name)` resolves them.\n */\n module: string;\n /** Scoped style-root class wrapped around every page (e.g. `ethisys-<plugin>-root`). */\n rootClassName: string;\n /** Stable id for the injected `<style>` element. */\n styleId: string;\n /** The plugin's compiled CSS string (typically a `*.css?inline` import). */\n css: string;\n /** Override the per-page `QueryClient`. */\n queryClient?: QueryClient;\n}\n\n/**\n * Builds a plugin-bound `definePluginPage(Page, views)` from one config, so a\n * plugin writes the four plugin-specific values (module key, root class, style\n * id, CSS) ONCE instead of in every page entry. Each surface entry then calls\n * `definePluginPage(Page, { ViewName })`; the flat `views` map is wrapped into\n * the `{ [module]: views }` manifest slice the page's `useView(module, name)`\n * reads. The generic provider/manifest/style wrapping stays in\n * {@link definePlatformReactPluginPage}.\n */\nexport function createPluginPageDefiner(config: PluginPageDefinerConfig) {\n const { module, ...rest } = config;\n return function definePluginPage(\n Page: ComponentType<PlatformReactPageProps>,\n views: Record<string, ViewComponent> = {},\n ): ComponentType<PlatformReactPageProps> {\n return definePlatformReactPluginPage(Page, { ...rest, manifest: { [module]: views } });\n };\n}\n","import { createElement, type ComponentType } from \"react\";\n\n/**\n * Dependency-light fallback rendered when a view is not present in the current\n * template manifest. Kept to plain elements + inline styles so the foundation\n * package carries no UI-library dependency; plugins that want a branded\n * fallback pass their own via {@link createUseView}.\n */\nexport const MissingViewFallback: ComponentType<Record<string, unknown>> = () =>\n createElement(\n \"div\",\n {\n style: {\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n padding: \"2rem\",\n },\n },\n createElement(\n \"div\",\n {\n style: {\n maxWidth: \"28rem\",\n padding: \"1.5rem\",\n border: \"1px solid rgba(0,0,0,0.12)\",\n borderRadius: \"0.5rem\",\n textAlign: \"center\",\n },\n },\n createElement(\"p\", { style: { margin: \"0 0 0.5rem\", fontWeight: 600 } }, \"View not available\"),\n createElement(\n \"p\",\n { style: { margin: 0, opacity: 0.7, fontSize: \"0.875rem\" } },\n \"This view has not been implemented by the current template.\",\n ),\n ),\n );\n","import { useContext, type ComponentType } from \"react\";\nimport { TemplateContext } from \"./templateContext\";\nimport { MissingViewFallback } from \"./MissingViewFallback\";\n\nconst REACT_LAZY_TYPE = Symbol.for(\"react.lazy\");\n\n/** Matches regular function components and `React.lazy` wrappers. */\nexport function isComponent(value: unknown): value is ComponentType {\n if (typeof value === \"function\") return true;\n if (typeof value === \"object\" && value !== null) {\n return (value as Record<string, unknown>).$$typeof === REACT_LAZY_TYPE;\n }\n return false;\n}\n\n/**\n * Builds a `useView` hook bound to a fallback component. The hook is a safe\n * view accessor that never throws: it returns `manifest[module][viewName]` when\n * present, or the supplied fallback when the manifest, module slice, or view is\n * missing or is not a component.\n */\nexport function createUseView(fallback: ComponentType<Record<string, unknown>>) {\n return function useView(\n module: string,\n viewName: string,\n ): ComponentType<Record<string, unknown>> {\n const manifest = useContext(TemplateContext);\n if (!manifest) {\n return fallback;\n }\n\n const slice = manifest[module];\n if (slice && typeof slice === \"object\" && viewName in (slice as Record<string, unknown>)) {\n const component = (slice as Record<string, unknown>)[viewName];\n if (isComponent(component)) {\n return component as ComponentType<Record<string, unknown>>;\n }\n }\n\n return fallback;\n };\n}\n\n/** Default `useView` bound to the built-in {@link MissingViewFallback}. */\nexport const useView = createUseView(MissingViewFallback);\n","import { useMemo } from \"react\";\nimport { useMcpTool } from \"@ethisyscore/extension-runtime/plugin\";\n\n/** Dispatches an MCP tool call by name. The seam a lifted REST service binds to. */\nexport type ToolInvoker = (toolName: string, args: unknown) => Promise<unknown>;\n\n/**\n * Base for service classes lifted from a monolith REST plane onto MCP.\n *\n * The monolith versions extend a REST base (`this.get`/`this.post` over axios).\n * The plugin runtime exposes no imperative MCP transport — only the\n * `useMcpTool` hook — so a `useXService()` hook composes per-tool invokers (see\n * {@link useToolInvokerMap}) into a single {@link ToolInvoker} and constructs\n * the class with it. Method bodies call `this.tool(\"<tool-name>\", args)`.\n */\nexport abstract class BaseMcpService {\n constructor(private readonly invoker: ToolInvoker) {}\n\n protected async tool<TRes>(name: string, args: unknown = {}): Promise<TRes> {\n return (await this.invoker(name, args)) as TRes;\n }\n}\n\n/**\n * Thrown by a lifted service method whose monolith REST route has no MCP tool\n * yet (typically because it needs client-side aggregation over a coarser tool).\n */\nexport function notViaMcp(serviceName: string, method: string): never {\n throw new Error(`${serviceName}.${method} has no MCP tool — pending per-surface aggregation.`);\n}\n\n/** Imperative invoker for a single MCP tool (the hook's `.invoke`, stable across renders). */\nexport function useToolInvoker<TReq, TRes>(toolName: string): (req: TReq) => Promise<TRes> {\n return useMcpTool<TReq, TRes>(toolName).invoke;\n}\n\n/**\n * Composes one {@link ToolInvoker} dispatching over every tool in `tools`, so a\n * service class can call tools by name.\n *\n * Rules of hooks: `tools` MUST be a frozen, constant-length list (e.g. a\n * module-level `as const` array) so the per-tool `useMcpTool` calls run in a\n * stable order on every render. Passing a list whose length varies between\n * renders is a violation and will break.\n */\nexport function useToolInvokerMap(tools: readonly string[]): ToolInvoker {\n const invokers: Record<string, (args: unknown) => Promise<unknown>> = {};\n for (const name of tools) {\n // eslint-disable-next-line react-hooks/rules-of-hooks -- `tools` is required to be frozen; call order is stable.\n invokers[name] = useMcpTool(name).invoke;\n }\n return useMemo<ToolInvoker>(\n () => (name, args) => {\n const fn = invokers[name];\n if (!fn) {\n throw new Error(`No MCP invoker registered for tool \"${name}\"`);\n }\n return fn(args);\n },\n // eslint-disable-next-line react-hooks/exhaustive-deps -- per-tool invoke refs are stable; rebuild only if one changes.\n tools.map((t) => invokers[t]),\n );\n}\n","import {\n useQueries,\n useQuery,\n type QueryKey,\n type UseQueryOptions,\n type UseQueryResult,\n} from \"@tanstack/react-query\";\n\n/**\n * Plugin shim for the monolith's `useAuthenticatedQuery`.\n *\n * In the host realm the MCP transport is already authenticated and scoped to\n * the active organisation, so there is no auth gate to apply. The hook keeps\n * the monolith's return shape (`isLoading` + `isAuthLoading`) so lifted data\n * hooks and pages consume it unchanged — `isAuthLoading` is always `false`.\n */\nexport interface AuthenticatedQueryResult<TData, TError>\n extends Omit<UseQueryResult<TData, TError>, \"isLoading\"> {\n isLoading: boolean;\n isAuthLoading: boolean;\n}\n\nexport function useAuthenticatedQuery<\n TQueryFnData = unknown,\n TError = Error,\n TData = TQueryFnData,\n TQueryKey extends QueryKey = QueryKey,\n>(\n options: UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>,\n): AuthenticatedQueryResult<TData, TError> {\n const result = useQuery(options);\n return {\n ...result,\n isLoading: result.isLoading,\n isAuthLoading: false,\n };\n}\n\nexport function useAuthenticatedQueries<TQueryFnData = unknown, TError = Error>(\n queries: UseQueryOptions<TQueryFnData, TError>[],\n) {\n return useQueries({ queries });\n}\n","import { createElement, type AnchorHTMLAttributes, type MouseEvent, type ReactNode } from \"react\";\nimport { emitNavigation } from \"@ethisyscore/components-react\";\n\n/**\n * Minimal `react-router-dom` compatibility shim for lifted monolith code.\n *\n * A PlatformReact plugin has no client router — navigation is brokered to the\n * host via {@link emitNavigation}, and route params come from the surface URL's\n * trailing segment(s). {@link createReactRouterShim} returns a module-shaped\n * object of the hooks/components lifted code expects; alias `react-router-dom`\n * to a tiny file that re-exports this object's members so call sites resolve\n * unchanged.\n *\n * `paramKeys` names the keys the single trailing path segment is exposed under,\n * so a route like `/…/<page>/<value>` satisfies `useParams<{ id }>()` /\n * `useParams<{ date }>()` regardless of which key the lifted code destructures.\n */\nexport interface ReactRouterShimOptions {\n /** Keys the trailing path segment is exposed under (default: `[\"id\"]`). */\n paramKeys?: readonly string[];\n}\n\ntype LinkProps = AnchorHTMLAttributes<HTMLAnchorElement> & {\n to: string;\n children?: ReactNode;\n};\n\nexport function createReactRouterShim(options: ReactRouterShimOptions = {}) {\n const paramKeys = options.paramKeys ?? [\"id\"];\n\n function useNavigate(): (to: string | number) => void {\n return (to) => {\n if (typeof to === \"number\") {\n if (typeof window !== \"undefined\") window.history.go(to);\n return;\n }\n emitNavigation(to);\n };\n }\n\n function useParams<\n T extends Record<string, string | undefined> = Record<string, string | undefined>,\n >(): T {\n const path = typeof window !== \"undefined\" ? window.location.pathname : \"\";\n const segments = path.split(\"/\").filter(Boolean);\n const last = segments[segments.length - 1];\n const params: Record<string, string | undefined> = {};\n if (last) {\n for (const key of paramKeys) {\n params[key] = last;\n }\n }\n return params as T;\n }\n\n function useSearchParams(): [\n URLSearchParams,\n (next: URLSearchParams | Record<string, string>) => void,\n ] {\n const search = typeof window !== \"undefined\" ? window.location.search : \"\";\n const params = new URLSearchParams(search);\n const setSearchParams = (next: URLSearchParams | Record<string, string>) => {\n const usp = next instanceof URLSearchParams ? next : new URLSearchParams(next);\n const qs = usp.toString();\n if (typeof window !== \"undefined\") {\n window.history.replaceState(\n {},\n \"\",\n qs ? `${window.location.pathname}?${qs}` : window.location.pathname,\n );\n }\n };\n return [params, setSearchParams];\n }\n\n function useMatch(\n _pattern: string,\n ): { params: Record<string, string | undefined>; pathname: string } | null {\n return null;\n }\n\n function useLocation() {\n const path = typeof window !== \"undefined\" ? window.location.pathname : \"/\";\n return { pathname: path, search: \"\", hash: \"\", state: null, key: \"default\" };\n }\n\n function Link({ to, children, onClick, ...rest }: LinkProps) {\n return createElement(\n \"a\",\n {\n href: to,\n onClick: (e: MouseEvent<HTMLAnchorElement>) => {\n e.preventDefault();\n onClick?.(e);\n emitNavigation(to);\n },\n ...rest,\n },\n children,\n );\n }\n\n return { useNavigate, useParams, useSearchParams, useMatch, useLocation, Link, NavLink: Link };\n}\n"]}
1
+ {"version":3,"sources":["../../src/platform-react/templateContext.ts","../../src/platform-react/injectStyles.ts","../../src/platform-react/definePlatformReactPluginPage.tsx","../../src/platform-react/MissingViewFallback.tsx","../../src/platform-react/useView.ts","../../src/platform-react/mcpService.ts","../../src/platform-react/useAuthenticatedQuery.ts","../../src/platform-react/reactRouterShim.tsx"],"names":["createElement","useView"],"mappings":"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","file":"index.js","sourcesContent":["import { createContext, type ComponentType } from \"react\";\n\n/** A view component resolved from the template manifest. Props are open. */\nexport type ViewComponent = ComponentType<Record<string, unknown>>;\n\n/**\n * Static port of the monolith's gogo-ui adapter mechanism for PlatformReact.\n *\n * A monolith feature picks its adapter view at runtime via `import.meta.glob`;\n * a PlatformReact page is a single self-contained ESM bundle where that glob is\n * unavailable, so each page declares its view slice statically and provides it\n * through this context. The shape is `manifest[module][viewName] = Component`.\n */\nexport type TemplateManifest = Record<string, Partial<Record<string, ViewComponent>>>;\n\nexport const TemplateContext = createContext<TemplateManifest | null>(null);\n","import { useEffect } from \"react\";\n\n/**\n * Injects a plugin's compiled CSS into the host document once, keyed by a\n * stable element id (idempotent across pages and re-mounts).\n *\n * Each PlatformReact page is a separate ESM bundle with no companion CSS asset\n * the host would load, so the styles travel inside the bundle (typically via a\n * `*.css?inline` import) and are mounted at runtime. Scope the CSS to a plugin\n * root class (never `:root`) so it cannot override the host theme.\n */\nexport function injectPluginStyles(id: string, css: string): void {\n if (typeof document === \"undefined\") return;\n if (document.getElementById(id)) return;\n const el = document.createElement(\"style\");\n el.id = id;\n el.textContent = css;\n document.head.appendChild(el);\n}\n\n/** Component form of {@link injectPluginStyles}; renders nothing. */\nexport function PluginStyleScope({ id, css }: { id: string; css: string }): null {\n useEffect(() => {\n injectPluginStyles(id, css);\n }, [id, css]);\n return null;\n}\n","import { createElement, type ComponentType } from \"react\";\nimport { QueryClient, QueryClientProvider } from \"@tanstack/react-query\";\nimport {\n definePlatformReactPage,\n type PlatformReactPageProps,\n} from \"@ethisyscore/components-react\";\nimport { TemplateContext, type TemplateManifest, type ViewComponent } from \"./templateContext\";\nimport { PluginStyleScope } from \"./injectStyles\";\n\n/**\n * Default per-page `QueryClient`. Each PlatformReact page is an isolated ESM\n * bundle, so this module-level singleton is scoped to that page — exactly the\n * lifetime a page wants. Plugins needing different options pass their own.\n */\nfunction makeDefaultQueryClient(): QueryClient {\n return new QueryClient({\n defaultOptions: {\n queries: {\n retry: 1,\n refetchOnWindowFocus: false,\n staleTime: 30_000,\n },\n },\n });\n}\n\nconst defaultQueryClient = makeDefaultQueryClient();\n\n/**\n * Shared root class applied to every plugin page wrapper, in addition to the\n * plugin's own `rootClassName`. The SDK's `plugin-base.css` (scoped resets +\n * component overrides) targets this class, so host-parity styling reaches a\n * plugin's surface deterministically regardless of its plugin-specific class.\n */\nexport const EHX_PLUGIN_ROOT_CLASS = \"ehx-plugin-root\";\n\nexport interface DefinePlatformReactPluginPageOptions {\n /** The page's static `useView` manifest slice (no `import.meta.glob` in a single-file bundle). */\n manifest?: TemplateManifest;\n /** Scoped style-root class wrapped around the page (e.g. `ethisys-<plugin>-root`). */\n rootClassName: string;\n /** Stable id for the injected `<style>` element (idempotent across pages). */\n styleId: string;\n /** The plugin's compiled CSS string (typically a `*.css?inline` import). */\n css: string;\n /** Override the per-page `QueryClient`. */\n queryClient?: QueryClient;\n}\n\n/**\n * Wraps a lifted page as a PlatformReact entry with the providers the monolith\n * took for granted: react-query, the static `useView` manifest, and the scoped\n * style root. The host already provides the MCP transport (it mounts pages\n * inside its own runtime provider), so this adds only the in-page providers.\n */\nexport function definePlatformReactPluginPage(\n Page: ComponentType<PlatformReactPageProps>,\n options: DefinePlatformReactPluginPageOptions,\n): ComponentType<PlatformReactPageProps> {\n const {\n manifest = {},\n rootClassName,\n styleId,\n css,\n queryClient = defaultQueryClient,\n } = options;\n\n function PluginPage(props: PlatformReactPageProps) {\n return createElement(\n QueryClientProvider,\n { client: queryClient },\n createElement(\n TemplateContext.Provider,\n { value: manifest },\n createElement(PluginStyleScope, { id: styleId, css }),\n createElement(\n \"div\",\n { className: `${EHX_PLUGIN_ROOT_CLASS} ${rootClassName}` },\n createElement(Page, props),\n ),\n ),\n );\n }\n\n return definePlatformReactPage(PluginPage);\n}\n\nexport interface PluginPageDefinerConfig {\n /**\n * The plugin's `useView` module key (e.g. `\"timesheets\"`). Each page's `views`\n * are registered under it, so a page's `useView(module, name)` resolves them.\n */\n module: string;\n /** Scoped style-root class wrapped around every page (e.g. `ethisys-<plugin>-root`). */\n rootClassName: string;\n /** Stable id for the injected `<style>` element. */\n styleId: string;\n /** The plugin's compiled CSS string (typically a `*.css?inline` import). */\n css: string;\n /** Override the per-page `QueryClient`. */\n queryClient?: QueryClient;\n}\n\n/**\n * Builds a plugin-bound `definePluginPage(Page, views)` from one config, so a\n * plugin writes the four plugin-specific values (module key, root class, style\n * id, CSS) ONCE instead of in every page entry. Each surface entry then calls\n * `definePluginPage(Page, { ViewName })`; the flat `views` map is wrapped into\n * the `{ [module]: views }` manifest slice the page's `useView(module, name)`\n * reads. The generic provider/manifest/style wrapping stays in\n * {@link definePlatformReactPluginPage}.\n */\nexport function createPluginPageDefiner(config: PluginPageDefinerConfig) {\n const { module, ...rest } = config;\n return function definePluginPage(\n Page: ComponentType<PlatformReactPageProps>,\n views: Record<string, ViewComponent> = {},\n ): ComponentType<PlatformReactPageProps> {\n return definePlatformReactPluginPage(Page, { ...rest, manifest: { [module]: views } });\n };\n}\n","import { createElement, type ComponentType } from \"react\";\n\n/**\n * Dependency-light fallback rendered when a view is not present in the current\n * template manifest. Kept to plain elements + inline styles so the foundation\n * package carries no UI-library dependency; plugins that want a branded\n * fallback pass their own via {@link createUseView}.\n */\nexport const MissingViewFallback: ComponentType<Record<string, unknown>> = () =>\n createElement(\n \"div\",\n {\n style: {\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n padding: \"2rem\",\n },\n },\n createElement(\n \"div\",\n {\n style: {\n maxWidth: \"28rem\",\n padding: \"1.5rem\",\n border: \"1px solid rgba(0,0,0,0.12)\",\n borderRadius: \"0.5rem\",\n textAlign: \"center\",\n },\n },\n createElement(\"p\", { style: { margin: \"0 0 0.5rem\", fontWeight: 600 } }, \"View not available\"),\n createElement(\n \"p\",\n { style: { margin: 0, opacity: 0.7, fontSize: \"0.875rem\" } },\n \"This view has not been implemented by the current template.\",\n ),\n ),\n );\n","import { useContext, type ComponentType } from \"react\";\nimport { TemplateContext } from \"./templateContext\";\nimport { MissingViewFallback } from \"./MissingViewFallback\";\n\nconst REACT_LAZY_TYPE = Symbol.for(\"react.lazy\");\n\n/** Matches regular function components and `React.lazy` wrappers. */\nexport function isComponent(value: unknown): value is ComponentType {\n if (typeof value === \"function\") return true;\n if (typeof value === \"object\" && value !== null) {\n return (value as Record<string, unknown>).$$typeof === REACT_LAZY_TYPE;\n }\n return false;\n}\n\n/**\n * Builds a `useView` hook bound to a fallback component. The hook is a safe\n * view accessor that never throws: it returns `manifest[module][viewName]` when\n * present, or the supplied fallback when the manifest, module slice, or view is\n * missing or is not a component.\n */\nexport function createUseView(fallback: ComponentType<Record<string, unknown>>) {\n return function useView(\n module: string,\n viewName: string,\n ): ComponentType<Record<string, unknown>> {\n const manifest = useContext(TemplateContext);\n if (!manifest) {\n return fallback;\n }\n\n const slice = manifest[module];\n if (slice && typeof slice === \"object\" && viewName in (slice as Record<string, unknown>)) {\n const component = (slice as Record<string, unknown>)[viewName];\n if (isComponent(component)) {\n return component as ComponentType<Record<string, unknown>>;\n }\n }\n\n return fallback;\n };\n}\n\n/** Default `useView` bound to the built-in {@link MissingViewFallback}. */\nexport const useView = createUseView(MissingViewFallback);\n","import { useMemo } from \"react\";\nimport { useMcpTool } from \"@ethisyscore/extension-runtime/plugin\";\n\n/** Dispatches an MCP tool call by name. The seam a lifted REST service binds to. */\nexport type ToolInvoker = (toolName: string, args: unknown) => Promise<unknown>;\n\n/**\n * Base for service classes lifted from a monolith REST plane onto MCP.\n *\n * The monolith versions extend a REST base (`this.get`/`this.post` over axios).\n * The plugin runtime exposes no imperative MCP transport — only the\n * `useMcpTool` hook — so a `useXService()` hook composes per-tool invokers (see\n * {@link useToolInvokerMap}) into a single {@link ToolInvoker} and constructs\n * the class with it. Method bodies call `this.tool(\"<tool-name>\", args)`.\n */\nexport abstract class BaseMcpService {\n constructor(private readonly invoker: ToolInvoker) {}\n\n protected async tool<TRes>(name: string, args: unknown = {}): Promise<TRes> {\n return (await this.invoker(name, args)) as TRes;\n }\n}\n\n/**\n * Thrown by a lifted service method whose monolith REST route has no MCP tool\n * yet (typically because it needs client-side aggregation over a coarser tool).\n */\nexport function notViaMcp(serviceName: string, method: string): never {\n throw new Error(`${serviceName}.${method} has no MCP tool — pending per-surface aggregation.`);\n}\n\n/** Imperative invoker for a single MCP tool (the hook's `.invoke`, stable across renders). */\nexport function useToolInvoker<TReq, TRes>(toolName: string): (req: TReq) => Promise<TRes> {\n return useMcpTool<TReq, TRes>(toolName).invoke;\n}\n\n/**\n * Composes one {@link ToolInvoker} dispatching over every tool in `tools`, so a\n * service class can call tools by name.\n *\n * Rules of hooks: `tools` MUST be a frozen, constant-length list (e.g. a\n * module-level `as const` array) so the per-tool `useMcpTool` calls run in a\n * stable order on every render. Passing a list whose length varies between\n * renders is a violation and will break.\n */\nexport function useToolInvokerMap(tools: readonly string[]): ToolInvoker {\n const invokers: Record<string, (args: unknown) => Promise<unknown>> = {};\n for (const name of tools) {\n // eslint-disable-next-line react-hooks/rules-of-hooks -- `tools` is required to be frozen; call order is stable.\n invokers[name] = useMcpTool(name).invoke;\n }\n return useMemo<ToolInvoker>(\n () => (name, args) => {\n const fn = invokers[name];\n if (!fn) {\n throw new Error(`No MCP invoker registered for tool \"${name}\"`);\n }\n return fn(args);\n },\n // eslint-disable-next-line react-hooks/exhaustive-deps -- per-tool invoke refs are stable; rebuild only if one changes.\n tools.map((t) => invokers[t]),\n );\n}\n","import {\n useQueries,\n useQuery,\n type QueryKey,\n type UseQueryOptions,\n type UseQueryResult,\n} from \"@tanstack/react-query\";\n\n/**\n * Plugin shim for the monolith's `useAuthenticatedQuery`.\n *\n * In the host realm the MCP transport is already authenticated and scoped to\n * the active organisation, so there is no auth gate to apply. The hook keeps\n * the monolith's return shape (`isLoading` + `isAuthLoading`) so lifted data\n * hooks and pages consume it unchanged — `isAuthLoading` is always `false`.\n */\nexport interface AuthenticatedQueryResult<TData, TError>\n extends Omit<UseQueryResult<TData, TError>, \"isLoading\"> {\n isLoading: boolean;\n isAuthLoading: boolean;\n}\n\nexport function useAuthenticatedQuery<\n TQueryFnData = unknown,\n TError = Error,\n TData = TQueryFnData,\n TQueryKey extends QueryKey = QueryKey,\n>(\n options: UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>,\n): AuthenticatedQueryResult<TData, TError> {\n const result = useQuery(options);\n return {\n ...result,\n isLoading: result.isLoading,\n isAuthLoading: false,\n };\n}\n\nexport function useAuthenticatedQueries<TQueryFnData = unknown, TError = Error>(\n queries: UseQueryOptions<TQueryFnData, TError>[],\n) {\n return useQueries({ queries });\n}\n","import { createElement, type AnchorHTMLAttributes, type MouseEvent, type ReactNode } from \"react\";\nimport { emitNavigation } from \"@ethisyscore/components-react\";\n\n/**\n * Minimal `react-router-dom` compatibility shim for lifted monolith code.\n *\n * A PlatformReact plugin has no client router — navigation is brokered to the\n * host via {@link emitNavigation}, and route params come from the surface URL's\n * trailing segment(s). {@link createReactRouterShim} returns a module-shaped\n * object of the hooks/components lifted code expects; alias `react-router-dom`\n * to a tiny file that re-exports this object's members so call sites resolve\n * unchanged.\n *\n * `paramKeys` names the keys the single trailing path segment is exposed under,\n * so a route like `/…/<page>/<value>` satisfies `useParams<{ id }>()` /\n * `useParams<{ date }>()` regardless of which key the lifted code destructures.\n */\nexport interface ReactRouterShimOptions {\n /** Keys the trailing path segment is exposed under (default: `[\"id\"]`). */\n paramKeys?: readonly string[];\n}\n\ntype LinkProps = AnchorHTMLAttributes<HTMLAnchorElement> & {\n to: string;\n children?: ReactNode;\n};\n\nexport function createReactRouterShim(options: ReactRouterShimOptions = {}) {\n const paramKeys = options.paramKeys ?? [\"id\"];\n\n function useNavigate(): (to: string | number) => void {\n return (to) => {\n if (typeof to === \"number\") {\n if (typeof window !== \"undefined\") window.history.go(to);\n return;\n }\n emitNavigation(to);\n };\n }\n\n function useParams<\n T extends Record<string, string | undefined> = Record<string, string | undefined>,\n >(): T {\n const path = typeof window !== \"undefined\" ? window.location.pathname : \"\";\n const segments = path.split(\"/\").filter(Boolean);\n const last = segments[segments.length - 1];\n const params: Record<string, string | undefined> = {};\n if (last) {\n for (const key of paramKeys) {\n params[key] = last;\n }\n }\n return params as T;\n }\n\n function useSearchParams(): [\n URLSearchParams,\n (next: URLSearchParams | Record<string, string>) => void,\n ] {\n const search = typeof window !== \"undefined\" ? window.location.search : \"\";\n const params = new URLSearchParams(search);\n const setSearchParams = (next: URLSearchParams | Record<string, string>) => {\n const usp = next instanceof URLSearchParams ? next : new URLSearchParams(next);\n const qs = usp.toString();\n if (typeof window !== \"undefined\") {\n window.history.replaceState(\n {},\n \"\",\n qs ? `${window.location.pathname}?${qs}` : window.location.pathname,\n );\n }\n };\n return [params, setSearchParams];\n }\n\n function useMatch(\n _pattern: string,\n ): { params: Record<string, string | undefined>; pathname: string } | null {\n return null;\n }\n\n function useLocation() {\n const path = typeof window !== \"undefined\" ? window.location.pathname : \"/\";\n return { pathname: path, search: \"\", hash: \"\", state: null, key: \"default\" };\n }\n\n function Link({ to, children, onClick, ...rest }: LinkProps) {\n return createElement(\n \"a\",\n {\n href: to,\n onClick: (e: MouseEvent<HTMLAnchorElement>) => {\n e.preventDefault();\n onClick?.(e);\n emitNavigation(to);\n },\n ...rest,\n },\n children,\n );\n }\n\n return { useNavigate, useParams, useSearchParams, useMatch, useLocation, Link, NavLink: Link };\n}\n"]}
@@ -0,0 +1,192 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ // src/tailwind-preset/index.ts
6
+ var hsl = (token) => `hsl(var(--${token}))`;
7
+ var ehxTailwindPreset = {
8
+ theme: {
9
+ extend: {
10
+ // gogo-ui native breakpoints (LARGER than the stock scale). Adopted so a
11
+ // plugin's Tailwind `sm:`/`md:` reflow at the same widths as its MUI
12
+ // `<Grid>` (which uses the gogo scale via the host theme) — otherwise the
13
+ // two systems break at different widths inside one component.
14
+ screens: {
15
+ sm: "480px",
16
+ md: "960px",
17
+ lg: "1280px",
18
+ xl: "1440px",
19
+ "2xl": "1640px",
20
+ "3xl": "1900px"
21
+ },
22
+ // Host body/heading fonts (Mulish / Urbanist) via the host's `--font-*`.
23
+ fontFamily: {
24
+ sans: ["var(--font-body)", "ui-sans-serif"],
25
+ heading: ["var(--font-heading)", "ui-sans-serif"],
26
+ body: ["var(--font-body)", "ui-sans-serif"]
27
+ },
28
+ // gogo's custom type scale — SMALLER than stock (base 0.875rem/14px).
29
+ // Literal values: not variable-backed, baked at build time.
30
+ fontSize: {
31
+ xs: "0.6875rem",
32
+ sm: "0.75rem",
33
+ base: "0.875rem",
34
+ lg: "1rem",
35
+ xl: "1.125rem",
36
+ "2xl": "1.25rem",
37
+ "3xl": "1.375rem",
38
+ "4xl": "1.5rem",
39
+ "5xl": "1.625rem"
40
+ },
41
+ // gogo's custom radius scale — LARGER than stock — via `--border-radius-*`.
42
+ borderRadius: {
43
+ "2xs": "var(--border-radius-2xs)",
44
+ xs: "var(--border-radius-xs)",
45
+ sm: "var(--border-radius-sm)",
46
+ DEFAULT: "var(--border-radius-sm)",
47
+ md: "var(--border-radius-md)",
48
+ lg: "var(--border-radius-lg)",
49
+ xl: "var(--border-radius-xl)",
50
+ "2xl": "var(--border-radius-2xl)",
51
+ "3xl": "var(--border-radius-3xl)",
52
+ "4xl": "var(--border-radius-4xl)"
53
+ },
54
+ // Host shadow scale via `--shadow-*` (JS-config mode doesn't auto-map the
55
+ // v4 `--shadow-*` namespace, so it's declared explicitly here).
56
+ boxShadow: {
57
+ "2xs": "var(--shadow-2xs)",
58
+ xs: "var(--shadow-xs)",
59
+ sm: "var(--shadow-sm)",
60
+ md: "var(--shadow-md)",
61
+ lg: "var(--shadow-lg)",
62
+ xl: "var(--shadow-xl)",
63
+ "2xl": "var(--shadow-2xl)"
64
+ },
65
+ // Union of gogo's colour families (`light`/`dark`, accents, text-*, grey)
66
+ // and the shadcn-style aliases lifted plugin components rely on
67
+ // (`foreground`/`hover`/`active`, `card`, `popover`, `neutral`). Merged per
68
+ // colour so every utility resolves regardless of which vocabulary a
69
+ // component uses; overlapping names share the same token.
70
+ colors: {
71
+ border: hsl("border"),
72
+ input: hsl("input"),
73
+ ring: hsl("ring"),
74
+ line: "var(--line)",
75
+ background: {
76
+ DEFAULT: hsl("background"),
77
+ paper: hsl("background-paper")
78
+ },
79
+ foreground: hsl("foreground"),
80
+ surface: {
81
+ DEFAULT: hsl("surface"),
82
+ foreground: hsl("surface-foreground")
83
+ },
84
+ card: {
85
+ DEFAULT: hsl("card"),
86
+ foreground: hsl("card-foreground")
87
+ },
88
+ popover: {
89
+ DEFAULT: hsl("popover"),
90
+ foreground: hsl("popover-foreground")
91
+ },
92
+ muted: {
93
+ DEFAULT: hsl("muted"),
94
+ foreground: hsl("muted-foreground")
95
+ },
96
+ accent: {
97
+ DEFAULT: hsl("accent"),
98
+ foreground: hsl("accent-foreground"),
99
+ hover: hsl("accent-hover")
100
+ },
101
+ "text-primary": {
102
+ DEFAULT: hsl("text-primary"),
103
+ light: hsl("text-primary-light"),
104
+ dark: hsl("text-primary-dark")
105
+ },
106
+ "text-secondary": {
107
+ DEFAULT: hsl("text-secondary"),
108
+ light: hsl("text-secondary-light"),
109
+ dark: hsl("text-secondary-dark")
110
+ },
111
+ "text-disabled": {
112
+ DEFAULT: hsl("text-disabled"),
113
+ light: hsl("text-disabled-light"),
114
+ dark: hsl("text-disabled-dark")
115
+ },
116
+ "text-muted": {
117
+ DEFAULT: hsl("text-muted"),
118
+ light: hsl("text-muted-light"),
119
+ dark: hsl("text-muted-dark")
120
+ },
121
+ "text-contrast": {
122
+ DEFAULT: hsl("text-contrast"),
123
+ alternative: hsl("text-contrast-alternative")
124
+ },
125
+ primary: {
126
+ DEFAULT: hsl("primary"),
127
+ light: hsl("primary-light"),
128
+ dark: hsl("primary-dark"),
129
+ foreground: hsl("primary-foreground"),
130
+ hover: hsl("primary-hover"),
131
+ active: hsl("primary-active")
132
+ },
133
+ secondary: {
134
+ DEFAULT: hsl("secondary"),
135
+ light: hsl("secondary-light"),
136
+ dark: hsl("secondary-dark"),
137
+ foreground: hsl("secondary-foreground"),
138
+ hover: hsl("secondary-hover"),
139
+ active: hsl("secondary-active")
140
+ },
141
+ "accent-1": { DEFAULT: hsl("accent-1"), light: hsl("accent-1-light"), dark: hsl("accent-1-dark") },
142
+ "accent-2": { DEFAULT: hsl("accent-2"), light: hsl("accent-2-light"), dark: hsl("accent-2-dark") },
143
+ "accent-3": { DEFAULT: hsl("accent-3"), light: hsl("accent-3-light"), dark: hsl("accent-3-dark") },
144
+ "accent-4": { DEFAULT: hsl("accent-4"), light: hsl("accent-4-light"), dark: hsl("accent-4-dark") },
145
+ "accent-5": { DEFAULT: hsl("accent-5"), light: hsl("accent-5-light"), dark: hsl("accent-5-dark") },
146
+ "accent-6": { DEFAULT: hsl("accent-6"), light: hsl("accent-6-light"), dark: hsl("accent-6-dark") },
147
+ success: { DEFAULT: hsl("success"), light: hsl("success-light"), dark: hsl("success-dark"), foreground: hsl("success-foreground"), hover: hsl("success-hover") },
148
+ warning: { DEFAULT: hsl("warning"), light: hsl("warning-light"), dark: hsl("warning-dark"), foreground: hsl("warning-foreground"), hover: hsl("warning-hover") },
149
+ error: { DEFAULT: hsl("error"), light: hsl("error-light"), dark: hsl("error-dark"), foreground: hsl("error-foreground"), hover: hsl("error-hover") },
150
+ info: { DEFAULT: hsl("info"), light: hsl("info-light"), dark: hsl("info-dark"), foreground: hsl("info-foreground"), hover: hsl("info-hover") },
151
+ rating: { DEFAULT: hsl("rating"), light: hsl("rating-light"), dark: hsl("rating-dark") },
152
+ destructive: { DEFAULT: hsl("destructive"), foreground: hsl("destructive-foreground") },
153
+ grey: greyScale(),
154
+ gray: greyScale(),
155
+ neutral: {
156
+ 50: hsl("neutral-50"),
157
+ 100: hsl("neutral-100"),
158
+ 200: hsl("neutral-200"),
159
+ 300: hsl("neutral-300"),
160
+ 400: hsl("neutral-400"),
161
+ 500: hsl("neutral-500"),
162
+ 600: hsl("neutral-600"),
163
+ 700: hsl("neutral-700"),
164
+ 800: hsl("neutral-800"),
165
+ 900: hsl("neutral-900")
166
+ }
167
+ }
168
+ }
169
+ }
170
+ };
171
+ function greyScale() {
172
+ return {
173
+ 20: hsl("grey-20"),
174
+ 25: hsl("grey-25"),
175
+ 50: hsl("grey-50"),
176
+ 100: hsl("grey-100"),
177
+ 200: hsl("grey-200"),
178
+ 300: hsl("grey-300"),
179
+ 400: hsl("grey-400"),
180
+ 500: hsl("grey-500"),
181
+ 600: hsl("grey-600"),
182
+ 700: hsl("grey-700"),
183
+ 800: hsl("grey-800"),
184
+ 900: hsl("grey-900")
185
+ };
186
+ }
187
+ var tailwind_preset_default = ehxTailwindPreset;
188
+
189
+ exports.default = tailwind_preset_default;
190
+ exports.ehxTailwindPreset = ehxTailwindPreset;
191
+ //# sourceMappingURL=index.cjs.map
192
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/tailwind-preset/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Shared Tailwind **preset** for PlatformReact plugin surfaces.\n *\n * A plugin renders inside the host document and must look identical to it, but\n * it bundles its **own** Tailwind — and its compiled CSS is injected last, so\n * wherever the plugin's Tailwind config disagrees with the host's, the plugin's\n * value silently wins. The host (`gogo-ui`) customises several theme scales\n * (notably a larger `borderRadius` and a smaller `fontSize`, plus native\n * breakpoints); a plugin that omits them falls back to stock Tailwind and\n * renders `rounded-*`/`text-*`/responsive utilities at the wrong sizes.\n *\n * Extending this preset (`presets: [ehxTailwindPreset]`) makes a plugin's\n * compiled utilities pixel-identical to the host's.\n *\n * Values are **hard-copied** from `gogo-ui/tailwind.config.ts` +\n * `gogo-ui/src/style/theme/common.css` as of gogo-ui 1.x (2026-06). Variable-\n * backed scales (`borderRadius`, `boxShadow`, `fontFamily`, `colors`) resolve\n * from the host's `<html>` design-token variables at runtime by inheritance;\n * literal scales (`fontSize`, `screens`) are baked at the plugin's build time.\n * Re-sync these if the gogo theme scales change.\n *\n * @example\n * // plugin tailwind.config.ts\n * import { ehxTailwindPreset } from \"@ethisyscore/plugin-ui/tailwind-preset\";\n * export default {\n * presets: [ehxTailwindPreset],\n * content: [\"./src/**\\/*.{ts,tsx}\"],\n * // plugin-specific extras only\n * };\n */\n\n/** A Tailwind colour with the gogo (`light`/`dark`) + shadcn (`foreground`/`hover`/…) shades merged. */\ntype ColorShades = Record<string, string>;\n\n/** Minimal structural type — avoids a hard `tailwindcss` dependency in the SDK. */\nexport interface EhxTailwindPreset {\n theme: {\n extend: {\n screens: Record<string, string>;\n fontFamily: Record<string, string[]>;\n fontSize: Record<string, string>;\n borderRadius: Record<string, string>;\n boxShadow: Record<string, string>;\n colors: Record<string, string | ColorShades>;\n };\n };\n}\n\nconst hsl = (token: string): string => `hsl(var(--${token}))`;\n\nexport const ehxTailwindPreset: EhxTailwindPreset = {\n theme: {\n extend: {\n // gogo-ui native breakpoints (LARGER than the stock scale). Adopted so a\n // plugin's Tailwind `sm:`/`md:` reflow at the same widths as its MUI\n // `<Grid>` (which uses the gogo scale via the host theme) — otherwise the\n // two systems break at different widths inside one component.\n screens: {\n sm: \"480px\",\n md: \"960px\",\n lg: \"1280px\",\n xl: \"1440px\",\n \"2xl\": \"1640px\",\n \"3xl\": \"1900px\",\n },\n\n // Host body/heading fonts (Mulish / Urbanist) via the host's `--font-*`.\n fontFamily: {\n sans: [\"var(--font-body)\", \"ui-sans-serif\"],\n heading: [\"var(--font-heading)\", \"ui-sans-serif\"],\n body: [\"var(--font-body)\", \"ui-sans-serif\"],\n },\n\n // gogo's custom type scale — SMALLER than stock (base 0.875rem/14px).\n // Literal values: not variable-backed, baked at build time.\n fontSize: {\n xs: \"0.6875rem\",\n sm: \"0.75rem\",\n base: \"0.875rem\",\n lg: \"1rem\",\n xl: \"1.125rem\",\n \"2xl\": \"1.25rem\",\n \"3xl\": \"1.375rem\",\n \"4xl\": \"1.5rem\",\n \"5xl\": \"1.625rem\",\n },\n\n // gogo's custom radius scale — LARGER than stock — via `--border-radius-*`.\n borderRadius: {\n \"2xs\": \"var(--border-radius-2xs)\",\n xs: \"var(--border-radius-xs)\",\n sm: \"var(--border-radius-sm)\",\n DEFAULT: \"var(--border-radius-sm)\",\n md: \"var(--border-radius-md)\",\n lg: \"var(--border-radius-lg)\",\n xl: \"var(--border-radius-xl)\",\n \"2xl\": \"var(--border-radius-2xl)\",\n \"3xl\": \"var(--border-radius-3xl)\",\n \"4xl\": \"var(--border-radius-4xl)\",\n },\n\n // Host shadow scale via `--shadow-*` (JS-config mode doesn't auto-map the\n // v4 `--shadow-*` namespace, so it's declared explicitly here).\n boxShadow: {\n \"2xs\": \"var(--shadow-2xs)\",\n xs: \"var(--shadow-xs)\",\n sm: \"var(--shadow-sm)\",\n md: \"var(--shadow-md)\",\n lg: \"var(--shadow-lg)\",\n xl: \"var(--shadow-xl)\",\n \"2xl\": \"var(--shadow-2xl)\",\n },\n\n // Union of gogo's colour families (`light`/`dark`, accents, text-*, grey)\n // and the shadcn-style aliases lifted plugin components rely on\n // (`foreground`/`hover`/`active`, `card`, `popover`, `neutral`). Merged per\n // colour so every utility resolves regardless of which vocabulary a\n // component uses; overlapping names share the same token.\n colors: {\n border: hsl(\"border\"),\n input: hsl(\"input\"),\n ring: hsl(\"ring\"),\n line: \"var(--line)\",\n\n background: {\n DEFAULT: hsl(\"background\"),\n paper: hsl(\"background-paper\"),\n },\n foreground: hsl(\"foreground\"),\n surface: {\n DEFAULT: hsl(\"surface\"),\n foreground: hsl(\"surface-foreground\"),\n },\n card: {\n DEFAULT: hsl(\"card\"),\n foreground: hsl(\"card-foreground\"),\n },\n popover: {\n DEFAULT: hsl(\"popover\"),\n foreground: hsl(\"popover-foreground\"),\n },\n muted: {\n DEFAULT: hsl(\"muted\"),\n foreground: hsl(\"muted-foreground\"),\n },\n accent: {\n DEFAULT: hsl(\"accent\"),\n foreground: hsl(\"accent-foreground\"),\n hover: hsl(\"accent-hover\"),\n },\n\n \"text-primary\": {\n DEFAULT: hsl(\"text-primary\"),\n light: hsl(\"text-primary-light\"),\n dark: hsl(\"text-primary-dark\"),\n },\n \"text-secondary\": {\n DEFAULT: hsl(\"text-secondary\"),\n light: hsl(\"text-secondary-light\"),\n dark: hsl(\"text-secondary-dark\"),\n },\n \"text-disabled\": {\n DEFAULT: hsl(\"text-disabled\"),\n light: hsl(\"text-disabled-light\"),\n dark: hsl(\"text-disabled-dark\"),\n },\n \"text-muted\": {\n DEFAULT: hsl(\"text-muted\"),\n light: hsl(\"text-muted-light\"),\n dark: hsl(\"text-muted-dark\"),\n },\n \"text-contrast\": {\n DEFAULT: hsl(\"text-contrast\"),\n alternative: hsl(\"text-contrast-alternative\"),\n },\n\n primary: {\n DEFAULT: hsl(\"primary\"),\n light: hsl(\"primary-light\"),\n dark: hsl(\"primary-dark\"),\n foreground: hsl(\"primary-foreground\"),\n hover: hsl(\"primary-hover\"),\n active: hsl(\"primary-active\"),\n },\n secondary: {\n DEFAULT: hsl(\"secondary\"),\n light: hsl(\"secondary-light\"),\n dark: hsl(\"secondary-dark\"),\n foreground: hsl(\"secondary-foreground\"),\n hover: hsl(\"secondary-hover\"),\n active: hsl(\"secondary-active\"),\n },\n\n \"accent-1\": { DEFAULT: hsl(\"accent-1\"), light: hsl(\"accent-1-light\"), dark: hsl(\"accent-1-dark\") },\n \"accent-2\": { DEFAULT: hsl(\"accent-2\"), light: hsl(\"accent-2-light\"), dark: hsl(\"accent-2-dark\") },\n \"accent-3\": { DEFAULT: hsl(\"accent-3\"), light: hsl(\"accent-3-light\"), dark: hsl(\"accent-3-dark\") },\n \"accent-4\": { DEFAULT: hsl(\"accent-4\"), light: hsl(\"accent-4-light\"), dark: hsl(\"accent-4-dark\") },\n \"accent-5\": { DEFAULT: hsl(\"accent-5\"), light: hsl(\"accent-5-light\"), dark: hsl(\"accent-5-dark\") },\n \"accent-6\": { DEFAULT: hsl(\"accent-6\"), light: hsl(\"accent-6-light\"), dark: hsl(\"accent-6-dark\") },\n\n success: { DEFAULT: hsl(\"success\"), light: hsl(\"success-light\"), dark: hsl(\"success-dark\"), foreground: hsl(\"success-foreground\"), hover: hsl(\"success-hover\") },\n warning: { DEFAULT: hsl(\"warning\"), light: hsl(\"warning-light\"), dark: hsl(\"warning-dark\"), foreground: hsl(\"warning-foreground\"), hover: hsl(\"warning-hover\") },\n error: { DEFAULT: hsl(\"error\"), light: hsl(\"error-light\"), dark: hsl(\"error-dark\"), foreground: hsl(\"error-foreground\"), hover: hsl(\"error-hover\") },\n info: { DEFAULT: hsl(\"info\"), light: hsl(\"info-light\"), dark: hsl(\"info-dark\"), foreground: hsl(\"info-foreground\"), hover: hsl(\"info-hover\") },\n rating: { DEFAULT: hsl(\"rating\"), light: hsl(\"rating-light\"), dark: hsl(\"rating-dark\") },\n destructive: { DEFAULT: hsl(\"destructive\"), foreground: hsl(\"destructive-foreground\") },\n\n grey: greyScale(),\n gray: greyScale(),\n neutral: {\n 50: hsl(\"neutral-50\"),\n 100: hsl(\"neutral-100\"),\n 200: hsl(\"neutral-200\"),\n 300: hsl(\"neutral-300\"),\n 400: hsl(\"neutral-400\"),\n 500: hsl(\"neutral-500\"),\n 600: hsl(\"neutral-600\"),\n 700: hsl(\"neutral-700\"),\n 800: hsl(\"neutral-800\"),\n 900: hsl(\"neutral-900\"),\n },\n },\n },\n },\n};\n\n/** gogo grey scale, aliased to both `grey-*` (MUI naming) and `gray-*` (Tailwind naming). */\nfunction greyScale(): ColorShades {\n return {\n 20: hsl(\"grey-20\"),\n 25: hsl(\"grey-25\"),\n 50: hsl(\"grey-50\"),\n 100: hsl(\"grey-100\"),\n 200: hsl(\"grey-200\"),\n 300: hsl(\"grey-300\"),\n 400: hsl(\"grey-400\"),\n 500: hsl(\"grey-500\"),\n 600: hsl(\"grey-600\"),\n 700: hsl(\"grey-700\"),\n 800: hsl(\"grey-800\"),\n 900: hsl(\"grey-900\"),\n };\n}\n\nexport default ehxTailwindPreset;\n"]}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Shared Tailwind **preset** for PlatformReact plugin surfaces.
3
+ *
4
+ * A plugin renders inside the host document and must look identical to it, but
5
+ * it bundles its **own** Tailwind — and its compiled CSS is injected last, so
6
+ * wherever the plugin's Tailwind config disagrees with the host's, the plugin's
7
+ * value silently wins. The host (`gogo-ui`) customises several theme scales
8
+ * (notably a larger `borderRadius` and a smaller `fontSize`, plus native
9
+ * breakpoints); a plugin that omits them falls back to stock Tailwind and
10
+ * renders `rounded-*`/`text-*`/responsive utilities at the wrong sizes.
11
+ *
12
+ * Extending this preset (`presets: [ehxTailwindPreset]`) makes a plugin's
13
+ * compiled utilities pixel-identical to the host's.
14
+ *
15
+ * Values are **hard-copied** from `gogo-ui/tailwind.config.ts` +
16
+ * `gogo-ui/src/style/theme/common.css` as of gogo-ui 1.x (2026-06). Variable-
17
+ * backed scales (`borderRadius`, `boxShadow`, `fontFamily`, `colors`) resolve
18
+ * from the host's `<html>` design-token variables at runtime by inheritance;
19
+ * literal scales (`fontSize`, `screens`) are baked at the plugin's build time.
20
+ * Re-sync these if the gogo theme scales change.
21
+ *
22
+ * @example
23
+ * // plugin tailwind.config.ts
24
+ * import { ehxTailwindPreset } from "@ethisyscore/plugin-ui/tailwind-preset";
25
+ * export default {
26
+ * presets: [ehxTailwindPreset],
27
+ * content: ["./src/**\/*.{ts,tsx}"],
28
+ * // plugin-specific extras only
29
+ * };
30
+ */
31
+ /** A Tailwind colour with the gogo (`light`/`dark`) + shadcn (`foreground`/`hover`/…) shades merged. */
32
+ type ColorShades = Record<string, string>;
33
+ /** Minimal structural type — avoids a hard `tailwindcss` dependency in the SDK. */
34
+ interface EhxTailwindPreset {
35
+ theme: {
36
+ extend: {
37
+ screens: Record<string, string>;
38
+ fontFamily: Record<string, string[]>;
39
+ fontSize: Record<string, string>;
40
+ borderRadius: Record<string, string>;
41
+ boxShadow: Record<string, string>;
42
+ colors: Record<string, string | ColorShades>;
43
+ };
44
+ };
45
+ }
46
+ declare const ehxTailwindPreset: EhxTailwindPreset;
47
+
48
+ export { type EhxTailwindPreset, ehxTailwindPreset as default, ehxTailwindPreset };
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Shared Tailwind **preset** for PlatformReact plugin surfaces.
3
+ *
4
+ * A plugin renders inside the host document and must look identical to it, but
5
+ * it bundles its **own** Tailwind — and its compiled CSS is injected last, so
6
+ * wherever the plugin's Tailwind config disagrees with the host's, the plugin's
7
+ * value silently wins. The host (`gogo-ui`) customises several theme scales
8
+ * (notably a larger `borderRadius` and a smaller `fontSize`, plus native
9
+ * breakpoints); a plugin that omits them falls back to stock Tailwind and
10
+ * renders `rounded-*`/`text-*`/responsive utilities at the wrong sizes.
11
+ *
12
+ * Extending this preset (`presets: [ehxTailwindPreset]`) makes a plugin's
13
+ * compiled utilities pixel-identical to the host's.
14
+ *
15
+ * Values are **hard-copied** from `gogo-ui/tailwind.config.ts` +
16
+ * `gogo-ui/src/style/theme/common.css` as of gogo-ui 1.x (2026-06). Variable-
17
+ * backed scales (`borderRadius`, `boxShadow`, `fontFamily`, `colors`) resolve
18
+ * from the host's `<html>` design-token variables at runtime by inheritance;
19
+ * literal scales (`fontSize`, `screens`) are baked at the plugin's build time.
20
+ * Re-sync these if the gogo theme scales change.
21
+ *
22
+ * @example
23
+ * // plugin tailwind.config.ts
24
+ * import { ehxTailwindPreset } from "@ethisyscore/plugin-ui/tailwind-preset";
25
+ * export default {
26
+ * presets: [ehxTailwindPreset],
27
+ * content: ["./src/**\/*.{ts,tsx}"],
28
+ * // plugin-specific extras only
29
+ * };
30
+ */
31
+ /** A Tailwind colour with the gogo (`light`/`dark`) + shadcn (`foreground`/`hover`/…) shades merged. */
32
+ type ColorShades = Record<string, string>;
33
+ /** Minimal structural type — avoids a hard `tailwindcss` dependency in the SDK. */
34
+ interface EhxTailwindPreset {
35
+ theme: {
36
+ extend: {
37
+ screens: Record<string, string>;
38
+ fontFamily: Record<string, string[]>;
39
+ fontSize: Record<string, string>;
40
+ borderRadius: Record<string, string>;
41
+ boxShadow: Record<string, string>;
42
+ colors: Record<string, string | ColorShades>;
43
+ };
44
+ };
45
+ }
46
+ declare const ehxTailwindPreset: EhxTailwindPreset;
47
+
48
+ export { type EhxTailwindPreset, ehxTailwindPreset as default, ehxTailwindPreset };
@@ -0,0 +1,187 @@
1
+ // src/tailwind-preset/index.ts
2
+ var hsl = (token) => `hsl(var(--${token}))`;
3
+ var ehxTailwindPreset = {
4
+ theme: {
5
+ extend: {
6
+ // gogo-ui native breakpoints (LARGER than the stock scale). Adopted so a
7
+ // plugin's Tailwind `sm:`/`md:` reflow at the same widths as its MUI
8
+ // `<Grid>` (which uses the gogo scale via the host theme) — otherwise the
9
+ // two systems break at different widths inside one component.
10
+ screens: {
11
+ sm: "480px",
12
+ md: "960px",
13
+ lg: "1280px",
14
+ xl: "1440px",
15
+ "2xl": "1640px",
16
+ "3xl": "1900px"
17
+ },
18
+ // Host body/heading fonts (Mulish / Urbanist) via the host's `--font-*`.
19
+ fontFamily: {
20
+ sans: ["var(--font-body)", "ui-sans-serif"],
21
+ heading: ["var(--font-heading)", "ui-sans-serif"],
22
+ body: ["var(--font-body)", "ui-sans-serif"]
23
+ },
24
+ // gogo's custom type scale — SMALLER than stock (base 0.875rem/14px).
25
+ // Literal values: not variable-backed, baked at build time.
26
+ fontSize: {
27
+ xs: "0.6875rem",
28
+ sm: "0.75rem",
29
+ base: "0.875rem",
30
+ lg: "1rem",
31
+ xl: "1.125rem",
32
+ "2xl": "1.25rem",
33
+ "3xl": "1.375rem",
34
+ "4xl": "1.5rem",
35
+ "5xl": "1.625rem"
36
+ },
37
+ // gogo's custom radius scale — LARGER than stock — via `--border-radius-*`.
38
+ borderRadius: {
39
+ "2xs": "var(--border-radius-2xs)",
40
+ xs: "var(--border-radius-xs)",
41
+ sm: "var(--border-radius-sm)",
42
+ DEFAULT: "var(--border-radius-sm)",
43
+ md: "var(--border-radius-md)",
44
+ lg: "var(--border-radius-lg)",
45
+ xl: "var(--border-radius-xl)",
46
+ "2xl": "var(--border-radius-2xl)",
47
+ "3xl": "var(--border-radius-3xl)",
48
+ "4xl": "var(--border-radius-4xl)"
49
+ },
50
+ // Host shadow scale via `--shadow-*` (JS-config mode doesn't auto-map the
51
+ // v4 `--shadow-*` namespace, so it's declared explicitly here).
52
+ boxShadow: {
53
+ "2xs": "var(--shadow-2xs)",
54
+ xs: "var(--shadow-xs)",
55
+ sm: "var(--shadow-sm)",
56
+ md: "var(--shadow-md)",
57
+ lg: "var(--shadow-lg)",
58
+ xl: "var(--shadow-xl)",
59
+ "2xl": "var(--shadow-2xl)"
60
+ },
61
+ // Union of gogo's colour families (`light`/`dark`, accents, text-*, grey)
62
+ // and the shadcn-style aliases lifted plugin components rely on
63
+ // (`foreground`/`hover`/`active`, `card`, `popover`, `neutral`). Merged per
64
+ // colour so every utility resolves regardless of which vocabulary a
65
+ // component uses; overlapping names share the same token.
66
+ colors: {
67
+ border: hsl("border"),
68
+ input: hsl("input"),
69
+ ring: hsl("ring"),
70
+ line: "var(--line)",
71
+ background: {
72
+ DEFAULT: hsl("background"),
73
+ paper: hsl("background-paper")
74
+ },
75
+ foreground: hsl("foreground"),
76
+ surface: {
77
+ DEFAULT: hsl("surface"),
78
+ foreground: hsl("surface-foreground")
79
+ },
80
+ card: {
81
+ DEFAULT: hsl("card"),
82
+ foreground: hsl("card-foreground")
83
+ },
84
+ popover: {
85
+ DEFAULT: hsl("popover"),
86
+ foreground: hsl("popover-foreground")
87
+ },
88
+ muted: {
89
+ DEFAULT: hsl("muted"),
90
+ foreground: hsl("muted-foreground")
91
+ },
92
+ accent: {
93
+ DEFAULT: hsl("accent"),
94
+ foreground: hsl("accent-foreground"),
95
+ hover: hsl("accent-hover")
96
+ },
97
+ "text-primary": {
98
+ DEFAULT: hsl("text-primary"),
99
+ light: hsl("text-primary-light"),
100
+ dark: hsl("text-primary-dark")
101
+ },
102
+ "text-secondary": {
103
+ DEFAULT: hsl("text-secondary"),
104
+ light: hsl("text-secondary-light"),
105
+ dark: hsl("text-secondary-dark")
106
+ },
107
+ "text-disabled": {
108
+ DEFAULT: hsl("text-disabled"),
109
+ light: hsl("text-disabled-light"),
110
+ dark: hsl("text-disabled-dark")
111
+ },
112
+ "text-muted": {
113
+ DEFAULT: hsl("text-muted"),
114
+ light: hsl("text-muted-light"),
115
+ dark: hsl("text-muted-dark")
116
+ },
117
+ "text-contrast": {
118
+ DEFAULT: hsl("text-contrast"),
119
+ alternative: hsl("text-contrast-alternative")
120
+ },
121
+ primary: {
122
+ DEFAULT: hsl("primary"),
123
+ light: hsl("primary-light"),
124
+ dark: hsl("primary-dark"),
125
+ foreground: hsl("primary-foreground"),
126
+ hover: hsl("primary-hover"),
127
+ active: hsl("primary-active")
128
+ },
129
+ secondary: {
130
+ DEFAULT: hsl("secondary"),
131
+ light: hsl("secondary-light"),
132
+ dark: hsl("secondary-dark"),
133
+ foreground: hsl("secondary-foreground"),
134
+ hover: hsl("secondary-hover"),
135
+ active: hsl("secondary-active")
136
+ },
137
+ "accent-1": { DEFAULT: hsl("accent-1"), light: hsl("accent-1-light"), dark: hsl("accent-1-dark") },
138
+ "accent-2": { DEFAULT: hsl("accent-2"), light: hsl("accent-2-light"), dark: hsl("accent-2-dark") },
139
+ "accent-3": { DEFAULT: hsl("accent-3"), light: hsl("accent-3-light"), dark: hsl("accent-3-dark") },
140
+ "accent-4": { DEFAULT: hsl("accent-4"), light: hsl("accent-4-light"), dark: hsl("accent-4-dark") },
141
+ "accent-5": { DEFAULT: hsl("accent-5"), light: hsl("accent-5-light"), dark: hsl("accent-5-dark") },
142
+ "accent-6": { DEFAULT: hsl("accent-6"), light: hsl("accent-6-light"), dark: hsl("accent-6-dark") },
143
+ success: { DEFAULT: hsl("success"), light: hsl("success-light"), dark: hsl("success-dark"), foreground: hsl("success-foreground"), hover: hsl("success-hover") },
144
+ warning: { DEFAULT: hsl("warning"), light: hsl("warning-light"), dark: hsl("warning-dark"), foreground: hsl("warning-foreground"), hover: hsl("warning-hover") },
145
+ error: { DEFAULT: hsl("error"), light: hsl("error-light"), dark: hsl("error-dark"), foreground: hsl("error-foreground"), hover: hsl("error-hover") },
146
+ info: { DEFAULT: hsl("info"), light: hsl("info-light"), dark: hsl("info-dark"), foreground: hsl("info-foreground"), hover: hsl("info-hover") },
147
+ rating: { DEFAULT: hsl("rating"), light: hsl("rating-light"), dark: hsl("rating-dark") },
148
+ destructive: { DEFAULT: hsl("destructive"), foreground: hsl("destructive-foreground") },
149
+ grey: greyScale(),
150
+ gray: greyScale(),
151
+ neutral: {
152
+ 50: hsl("neutral-50"),
153
+ 100: hsl("neutral-100"),
154
+ 200: hsl("neutral-200"),
155
+ 300: hsl("neutral-300"),
156
+ 400: hsl("neutral-400"),
157
+ 500: hsl("neutral-500"),
158
+ 600: hsl("neutral-600"),
159
+ 700: hsl("neutral-700"),
160
+ 800: hsl("neutral-800"),
161
+ 900: hsl("neutral-900")
162
+ }
163
+ }
164
+ }
165
+ }
166
+ };
167
+ function greyScale() {
168
+ return {
169
+ 20: hsl("grey-20"),
170
+ 25: hsl("grey-25"),
171
+ 50: hsl("grey-50"),
172
+ 100: hsl("grey-100"),
173
+ 200: hsl("grey-200"),
174
+ 300: hsl("grey-300"),
175
+ 400: hsl("grey-400"),
176
+ 500: hsl("grey-500"),
177
+ 600: hsl("grey-600"),
178
+ 700: hsl("grey-700"),
179
+ 800: hsl("grey-800"),
180
+ 900: hsl("grey-900")
181
+ };
182
+ }
183
+ var tailwind_preset_default = ehxTailwindPreset;
184
+
185
+ export { tailwind_preset_default as default, ehxTailwindPreset };
186
+ //# sourceMappingURL=index.js.map
187
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/tailwind-preset/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Shared Tailwind **preset** for PlatformReact plugin surfaces.\n *\n * A plugin renders inside the host document and must look identical to it, but\n * it bundles its **own** Tailwind — and its compiled CSS is injected last, so\n * wherever the plugin's Tailwind config disagrees with the host's, the plugin's\n * value silently wins. The host (`gogo-ui`) customises several theme scales\n * (notably a larger `borderRadius` and a smaller `fontSize`, plus native\n * breakpoints); a plugin that omits them falls back to stock Tailwind and\n * renders `rounded-*`/`text-*`/responsive utilities at the wrong sizes.\n *\n * Extending this preset (`presets: [ehxTailwindPreset]`) makes a plugin's\n * compiled utilities pixel-identical to the host's.\n *\n * Values are **hard-copied** from `gogo-ui/tailwind.config.ts` +\n * `gogo-ui/src/style/theme/common.css` as of gogo-ui 1.x (2026-06). Variable-\n * backed scales (`borderRadius`, `boxShadow`, `fontFamily`, `colors`) resolve\n * from the host's `<html>` design-token variables at runtime by inheritance;\n * literal scales (`fontSize`, `screens`) are baked at the plugin's build time.\n * Re-sync these if the gogo theme scales change.\n *\n * @example\n * // plugin tailwind.config.ts\n * import { ehxTailwindPreset } from \"@ethisyscore/plugin-ui/tailwind-preset\";\n * export default {\n * presets: [ehxTailwindPreset],\n * content: [\"./src/**\\/*.{ts,tsx}\"],\n * // plugin-specific extras only\n * };\n */\n\n/** A Tailwind colour with the gogo (`light`/`dark`) + shadcn (`foreground`/`hover`/…) shades merged. */\ntype ColorShades = Record<string, string>;\n\n/** Minimal structural type — avoids a hard `tailwindcss` dependency in the SDK. */\nexport interface EhxTailwindPreset {\n theme: {\n extend: {\n screens: Record<string, string>;\n fontFamily: Record<string, string[]>;\n fontSize: Record<string, string>;\n borderRadius: Record<string, string>;\n boxShadow: Record<string, string>;\n colors: Record<string, string | ColorShades>;\n };\n };\n}\n\nconst hsl = (token: string): string => `hsl(var(--${token}))`;\n\nexport const ehxTailwindPreset: EhxTailwindPreset = {\n theme: {\n extend: {\n // gogo-ui native breakpoints (LARGER than the stock scale). Adopted so a\n // plugin's Tailwind `sm:`/`md:` reflow at the same widths as its MUI\n // `<Grid>` (which uses the gogo scale via the host theme) — otherwise the\n // two systems break at different widths inside one component.\n screens: {\n sm: \"480px\",\n md: \"960px\",\n lg: \"1280px\",\n xl: \"1440px\",\n \"2xl\": \"1640px\",\n \"3xl\": \"1900px\",\n },\n\n // Host body/heading fonts (Mulish / Urbanist) via the host's `--font-*`.\n fontFamily: {\n sans: [\"var(--font-body)\", \"ui-sans-serif\"],\n heading: [\"var(--font-heading)\", \"ui-sans-serif\"],\n body: [\"var(--font-body)\", \"ui-sans-serif\"],\n },\n\n // gogo's custom type scale — SMALLER than stock (base 0.875rem/14px).\n // Literal values: not variable-backed, baked at build time.\n fontSize: {\n xs: \"0.6875rem\",\n sm: \"0.75rem\",\n base: \"0.875rem\",\n lg: \"1rem\",\n xl: \"1.125rem\",\n \"2xl\": \"1.25rem\",\n \"3xl\": \"1.375rem\",\n \"4xl\": \"1.5rem\",\n \"5xl\": \"1.625rem\",\n },\n\n // gogo's custom radius scale — LARGER than stock — via `--border-radius-*`.\n borderRadius: {\n \"2xs\": \"var(--border-radius-2xs)\",\n xs: \"var(--border-radius-xs)\",\n sm: \"var(--border-radius-sm)\",\n DEFAULT: \"var(--border-radius-sm)\",\n md: \"var(--border-radius-md)\",\n lg: \"var(--border-radius-lg)\",\n xl: \"var(--border-radius-xl)\",\n \"2xl\": \"var(--border-radius-2xl)\",\n \"3xl\": \"var(--border-radius-3xl)\",\n \"4xl\": \"var(--border-radius-4xl)\",\n },\n\n // Host shadow scale via `--shadow-*` (JS-config mode doesn't auto-map the\n // v4 `--shadow-*` namespace, so it's declared explicitly here).\n boxShadow: {\n \"2xs\": \"var(--shadow-2xs)\",\n xs: \"var(--shadow-xs)\",\n sm: \"var(--shadow-sm)\",\n md: \"var(--shadow-md)\",\n lg: \"var(--shadow-lg)\",\n xl: \"var(--shadow-xl)\",\n \"2xl\": \"var(--shadow-2xl)\",\n },\n\n // Union of gogo's colour families (`light`/`dark`, accents, text-*, grey)\n // and the shadcn-style aliases lifted plugin components rely on\n // (`foreground`/`hover`/`active`, `card`, `popover`, `neutral`). Merged per\n // colour so every utility resolves regardless of which vocabulary a\n // component uses; overlapping names share the same token.\n colors: {\n border: hsl(\"border\"),\n input: hsl(\"input\"),\n ring: hsl(\"ring\"),\n line: \"var(--line)\",\n\n background: {\n DEFAULT: hsl(\"background\"),\n paper: hsl(\"background-paper\"),\n },\n foreground: hsl(\"foreground\"),\n surface: {\n DEFAULT: hsl(\"surface\"),\n foreground: hsl(\"surface-foreground\"),\n },\n card: {\n DEFAULT: hsl(\"card\"),\n foreground: hsl(\"card-foreground\"),\n },\n popover: {\n DEFAULT: hsl(\"popover\"),\n foreground: hsl(\"popover-foreground\"),\n },\n muted: {\n DEFAULT: hsl(\"muted\"),\n foreground: hsl(\"muted-foreground\"),\n },\n accent: {\n DEFAULT: hsl(\"accent\"),\n foreground: hsl(\"accent-foreground\"),\n hover: hsl(\"accent-hover\"),\n },\n\n \"text-primary\": {\n DEFAULT: hsl(\"text-primary\"),\n light: hsl(\"text-primary-light\"),\n dark: hsl(\"text-primary-dark\"),\n },\n \"text-secondary\": {\n DEFAULT: hsl(\"text-secondary\"),\n light: hsl(\"text-secondary-light\"),\n dark: hsl(\"text-secondary-dark\"),\n },\n \"text-disabled\": {\n DEFAULT: hsl(\"text-disabled\"),\n light: hsl(\"text-disabled-light\"),\n dark: hsl(\"text-disabled-dark\"),\n },\n \"text-muted\": {\n DEFAULT: hsl(\"text-muted\"),\n light: hsl(\"text-muted-light\"),\n dark: hsl(\"text-muted-dark\"),\n },\n \"text-contrast\": {\n DEFAULT: hsl(\"text-contrast\"),\n alternative: hsl(\"text-contrast-alternative\"),\n },\n\n primary: {\n DEFAULT: hsl(\"primary\"),\n light: hsl(\"primary-light\"),\n dark: hsl(\"primary-dark\"),\n foreground: hsl(\"primary-foreground\"),\n hover: hsl(\"primary-hover\"),\n active: hsl(\"primary-active\"),\n },\n secondary: {\n DEFAULT: hsl(\"secondary\"),\n light: hsl(\"secondary-light\"),\n dark: hsl(\"secondary-dark\"),\n foreground: hsl(\"secondary-foreground\"),\n hover: hsl(\"secondary-hover\"),\n active: hsl(\"secondary-active\"),\n },\n\n \"accent-1\": { DEFAULT: hsl(\"accent-1\"), light: hsl(\"accent-1-light\"), dark: hsl(\"accent-1-dark\") },\n \"accent-2\": { DEFAULT: hsl(\"accent-2\"), light: hsl(\"accent-2-light\"), dark: hsl(\"accent-2-dark\") },\n \"accent-3\": { DEFAULT: hsl(\"accent-3\"), light: hsl(\"accent-3-light\"), dark: hsl(\"accent-3-dark\") },\n \"accent-4\": { DEFAULT: hsl(\"accent-4\"), light: hsl(\"accent-4-light\"), dark: hsl(\"accent-4-dark\") },\n \"accent-5\": { DEFAULT: hsl(\"accent-5\"), light: hsl(\"accent-5-light\"), dark: hsl(\"accent-5-dark\") },\n \"accent-6\": { DEFAULT: hsl(\"accent-6\"), light: hsl(\"accent-6-light\"), dark: hsl(\"accent-6-dark\") },\n\n success: { DEFAULT: hsl(\"success\"), light: hsl(\"success-light\"), dark: hsl(\"success-dark\"), foreground: hsl(\"success-foreground\"), hover: hsl(\"success-hover\") },\n warning: { DEFAULT: hsl(\"warning\"), light: hsl(\"warning-light\"), dark: hsl(\"warning-dark\"), foreground: hsl(\"warning-foreground\"), hover: hsl(\"warning-hover\") },\n error: { DEFAULT: hsl(\"error\"), light: hsl(\"error-light\"), dark: hsl(\"error-dark\"), foreground: hsl(\"error-foreground\"), hover: hsl(\"error-hover\") },\n info: { DEFAULT: hsl(\"info\"), light: hsl(\"info-light\"), dark: hsl(\"info-dark\"), foreground: hsl(\"info-foreground\"), hover: hsl(\"info-hover\") },\n rating: { DEFAULT: hsl(\"rating\"), light: hsl(\"rating-light\"), dark: hsl(\"rating-dark\") },\n destructive: { DEFAULT: hsl(\"destructive\"), foreground: hsl(\"destructive-foreground\") },\n\n grey: greyScale(),\n gray: greyScale(),\n neutral: {\n 50: hsl(\"neutral-50\"),\n 100: hsl(\"neutral-100\"),\n 200: hsl(\"neutral-200\"),\n 300: hsl(\"neutral-300\"),\n 400: hsl(\"neutral-400\"),\n 500: hsl(\"neutral-500\"),\n 600: hsl(\"neutral-600\"),\n 700: hsl(\"neutral-700\"),\n 800: hsl(\"neutral-800\"),\n 900: hsl(\"neutral-900\"),\n },\n },\n },\n },\n};\n\n/** gogo grey scale, aliased to both `grey-*` (MUI naming) and `gray-*` (Tailwind naming). */\nfunction greyScale(): ColorShades {\n return {\n 20: hsl(\"grey-20\"),\n 25: hsl(\"grey-25\"),\n 50: hsl(\"grey-50\"),\n 100: hsl(\"grey-100\"),\n 200: hsl(\"grey-200\"),\n 300: hsl(\"grey-300\"),\n 400: hsl(\"grey-400\"),\n 500: hsl(\"grey-500\"),\n 600: hsl(\"grey-600\"),\n 700: hsl(\"grey-700\"),\n 800: hsl(\"grey-800\"),\n 900: hsl(\"grey-900\"),\n };\n}\n\nexport default ehxTailwindPreset;\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ethisyscore/plugin-ui",
3
- "version": "1.19.0",
3
+ "version": "1.21.0",
4
4
  "description": "Plugin-UI umbrella SDK: client bridge + a11y/l10n primitives + brokered-MCP client (WI 4858).",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -32,6 +32,12 @@
32
32
  "import": "./dist/platform-react/index.js",
33
33
  "require": "./dist/platform-react/index.cjs"
34
34
  },
35
+ "./tailwind-preset": {
36
+ "types": "./dist/tailwind-preset/index.d.ts",
37
+ "import": "./dist/tailwind-preset/index.js",
38
+ "require": "./dist/tailwind-preset/index.cjs"
39
+ },
40
+ "./plugin-base.css": "./styles/plugin-base.css",
35
41
  "./icons": {
36
42
  "types": "./dist/icons/index.d.ts",
37
43
  "import": "./dist/icons/index.js",
@@ -60,6 +66,7 @@
60
66
  },
61
67
  "files": [
62
68
  "dist",
69
+ "styles",
63
70
  "README.md",
64
71
  "LICENSE"
65
72
  ],
@@ -0,0 +1,61 @@
1
+ /*
2
+ * @ethisyscore/plugin-ui — shared base stylesheet for PlatformReact plugin surfaces.
3
+ *
4
+ * Reproduces, SCOPED to the plugin root (`.ehx-plugin-root`), the host globals a
5
+ * plugin can't safely inherit, so a plugin renders like a native host surface
6
+ * without (a) re-emitting Tailwind's global preflight (which re-resets borders to
7
+ * `currentColor` and leaks onto the host) or (b) relying on host CSS leaking in
8
+ * with the right cascade priority.
9
+ *
10
+ * Pair with the `@ethisyscore/plugin-ui/tailwind-preset` (theme-scale parity) and
11
+ * the shared root class applied by `createPluginPageDefiner`.
12
+ *
13
+ * USAGE — a plugin's entry CSS imports this FIRST, then pulls in Tailwind WITHOUT
14
+ * preflight (theme + utilities only) so the host's preflight governs shared
15
+ * resets and the plugin stops restyling the host:
16
+ *
17
+ * @import "@ethisyscore/plugin-ui/plugin-base.css";
18
+ * @import "tailwindcss/theme.css" layer(theme) source(none);
19
+ * @import "tailwindcss/utilities.css" layer(utilities) source(none);
20
+ * @source "../";
21
+ * @config "./tailwind.config.ts"; // presets: [ehxTailwindPreset]
22
+ *
23
+ * Design-token variables (`--border`, `--border-radius-*`) resolve from the
24
+ * host's `<html>` at runtime by inheritance; the dev mock seeds them itself.
25
+ */
26
+
27
+ /* Cascade-layer order — match the host so MUI's own defaults (`mui` layer) sit
28
+ below gogo's component/utilities overrides. Declared first so it's the order
29
+ of record for the document. */
30
+ @layer theme, base, mui, components, utilities;
31
+
32
+ /* Scoped resets — the host pairs Tailwind's preflight with a global
33
+ `*{border-color}` reset (Tailwind v4 defaults bare borders to `currentColor`)
34
+ plus `box-sizing`/focus resets. Reproduced here for the plugin subtree ONLY,
35
+ so a bare `border`/`border-t` is grey (not black) without the plugin touching
36
+ the host document. Explicit `border-<colour>` utilities still win (later
37
+ `utilities` layer). */
38
+ @layer base {
39
+ .ehx-plugin-root,
40
+ .ehx-plugin-root *,
41
+ .ehx-plugin-root *::before,
42
+ .ehx-plugin-root *::after {
43
+ box-sizing: border-box;
44
+ border-color: hsl(var(--border));
45
+ }
46
+
47
+ .ehx-plugin-root :focus {
48
+ outline: 0;
49
+ }
50
+ }
51
+
52
+ /* Scoped component overrides — gogo rounds `.MuiCard-root` / `.MuiPaper-rounded`
53
+ to `rounded-3xl` via CSS in its `components`/`utilities` layers, which can lose
54
+ to MUI's 4px default inside a plugin's cascade. Pin the gogo radius here,
55
+ unlayered + root-scoped (specificity 0,2,0) so it wins regardless of layer
56
+ placement. Portaled Paper (menus/dialogs/pickers) renders outside the root and
57
+ is unaffected — the host styles those. */
58
+ .ehx-plugin-root .MuiCard-root,
59
+ .ehx-plugin-root .MuiPaper-rounded {
60
+ border-radius: var(--border-radius-3xl);
61
+ }