Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
},
"dependencies": {
"@tangent/shared": "workspace:*",
"@tangent/ui-extensions-sdk": "workspace:*",
"better-sqlite3": "^12.10.0",
"croner": "^10.0.1",
"drizzle-orm": "^0.45.2",
Expand Down
33 changes: 16 additions & 17 deletions apps/server/src/store/fileAgentBundleStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ import {
MANIFEST_FILENAME,
} from "@tangent/shared/configBundle.ts";
import type { AgentBundleMeta } from "@tangent/shared/contracts.ts";
import { build } from "esbuild";
import {
buildUiComponent,
UiComponentBuildError,
} from "@tangent/ui-extensions-sdk/build";
import { unzipSync } from "fflate";

import { AGENT_BUNDLES_ROOT } from "../config.ts";
Expand Down Expand Up @@ -129,21 +132,16 @@ async function compileComponent(
): Promise<void> {
let output: string;
try {
const result = await build({
entryPoints: [path.join(work, component.entry)],
bundle: true,
format: "esm",
platform: "browser",
jsx: "automatic",
packages: "external",
write: false,
logLevel: "silent",
});
output = result.outputFiles[0].text;
output = await buildUiComponent(path.join(work, component.entry));
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
const detail =
err instanceof UiComponentBuildError && err.cause instanceof Error
? err.cause.message
: err instanceof Error
? err.message
: String(err);
throw new AgentBundleValidationError(
`agent bundle: failed to compile ui entry "${component.entry}"\n${message}`,
`agent bundle: failed to compile ui entry "${component.entry}"\n${detail}`,
);
}
await writeFile(path.join(outDir, `${component.name}.js`), output);
Expand All @@ -153,9 +151,10 @@ async function compileComponent(
* Transpiles each declared UI component to ESM under `<dir>/ui/<name>.js`.
*
* The component sources are extracted to a temp working directory so esbuild
* can resolve relative sibling imports, then bundled transpile-only: bare
* (`packages: "external"`) imports such as `react`, `@tangent/bundle-ui`, and
* `@remote-dom/*` are left untouched for the Phase-5 worker import map to
* can resolve relative sibling imports, then bundled transpile-only via the
* shared {@link buildUiComponent} helper (also used by the `ui-extensions` CLI,
* so local builds match): bare imports such as `react` and
* `@tangent/ui-extensions-sdk` are left untouched for the worker import map to
* resolve, while relative imports within the bundle are inlined. The component
* never executes here. Throws {@link AgentBundleValidationError} when a declared
* entry is missing from the zip or fails to compile, so the upload route can
Expand Down
1 change: 1 addition & 0 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"@remote-dom/polyfill": "^1.5.1",
"@remote-dom/react": "^1.2.2",
"@tangent/shared": "workspace:*",
"@tangent/ui-extensions-sdk": "workspace:*",
"@tangent/ui-primitives": "workspace:*",
"@tangent/windows": "workspace:*",
"@tanstack/history": "1.162.0",
Expand Down
8 changes: 4 additions & 4 deletions apps/web/public/bundle-ui-harness/sample.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
// Dev harness fixture: a compiled bundle-UI "message" component.
//
// This mirrors what the Phase-3 server compiler emits for an author's
// This mirrors what the server compiler emits for an author's
// `ui/pipeline-progress.tsx`: plain ESM with *bare* imports (`react`,
// `@tangent/bundle-ui`) that the Phase-5 worker module loader resolves against
// `@tangent/ui-extensions-sdk`) that the worker module loader resolves against
// the worker's own module copies. It exercises the full loop: getProps ->
// host.fetch polling a real allowlisted endpoint -> render via the vocabulary ->
// host.sendPrompt on press.
//
// Equivalent source:
// import { useEffect, useState } from "react";
// import { host, Card, BlockStack, Heading, Text, Progress, Button } from "@tangent/bundle-ui";
// import { host, Card, BlockStack, Heading, Text, Progress, Button } from "@tangent/ui-extensions-sdk";
// export default function PipelineProgress() { ... }

import { createElement as h, useEffect, useState } from "react";
Expand All @@ -21,7 +21,7 @@ import {
host,
Progress,
Text,
} from "@tangent/bundle-ui";
} from "@tangent/ui-extensions-sdk";

export default function PipelineProgress() {
const [executionId, setExecutionId] = useState(null);
Expand Down
10 changes: 8 additions & 2 deletions apps/web/src/features/bundle-ui/bundle-ui.worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* Runs a compiled bundle component against a remote-dom DOM polyfill and streams
* the resulting tree of vocabulary elements to the host over `@quilted/threads`.
* The component reaches the outside world only through the host bridge exposed
* as `@tangent/bundle-ui`.
* as `@tangent/ui-extensions-sdk`.
*
* The polyfill imports MUST stay first: importing `./runtime/bridge` registers
* custom elements, which requires `customElements` to already exist.
Expand Down Expand Up @@ -33,6 +33,12 @@ import type { HostBridge, RenderOptions, WorkerApi } from "./types";
registerWorkerModules({
react: React as unknown as Record<string, unknown>,
"react/jsx-runtime": ReactJsxRuntime as unknown as Record<string, unknown>,
"@tangent/ui-extensions-sdk": BundleUiRuntime as unknown as Record<
string,
unknown
>,
// Legacy alias: bundles compiled before the SDK rename still import
// `@tangent/bundle-ui`. Resolve it to the same runtime so they keep working.
"@tangent/bundle-ui": BundleUiRuntime as unknown as Record<string, unknown>,
});

Expand All @@ -41,7 +47,7 @@ async function render(
options: RenderOptions,
): Promise<void> {
// The host functions become this worker's bridge; the component reaches them
// via `@tangent/bundle-ui`'s `host`.
// via `@tangent/ui-extensions-sdk`'s `host`.
globalThis.__TANGENT_BUNDLE_UI_HOST__ = thread.imports as HostBridge;

const Component = await loadComponent(options.moduleUrl);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@
*/

import { createRemoteComponent } from "@remote-dom/react";
import type {
EventPayload,
RemoteEvents,
} from "@tangent/ui-extensions-sdk/contracts/events";
import { type ComponentType, createElement, type ReactNode } from "react";

import type { AnyRemoteElementConstructor } from "./define-remote-element";
import type { EventPayload, RemoteEvents } from "./events";

type AnyProps = Record<string, unknown>;
type Handler = (...args: unknown[]) => void;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ import {
createRemoteElement,
type RemoteElementConstructor,
} from "@remote-dom/core/elements";
import type { RemoteEvents } from "@tangent/ui-extensions-sdk/contracts/events";
import type { z } from "zod";

import type { RemoteEvents } from "./events";
import { wireProperties } from "./wire-properties";

export type AnyRemoteElementConstructor = RemoteElementConstructor<
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { attributes } from "@tangent/ui-extensions-sdk/contracts/badge";
import { Badge } from "@tangent/ui-primitives/badge";

import { makeHostComponent } from "../_shared/make-host-component";
import { attributes } from "./badge.contract";

export const BadgeHost = makeHostComponent(Badge, attributes);
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import {
attributes,
events,
TAG,
} from "@tangent/ui-extensions-sdk/contracts/badge";

import { makeAuthorComponent } from "../_shared/author-component";
import { defineRemoteElement } from "../_shared/define-remote-element";
import { attributes, events, TAG } from "./badge.contract";

const BadgeElement = defineRemoteElement(TAG, attributes, events);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { attributes } from "@tangent/ui-extensions-sdk/contracts/block-stack";
import { BlockStack } from "@tangent/ui-primitives/layout";

import { makeHostComponent } from "../_shared/make-host-component";
import { attributes } from "./block-stack.contract";

export const BlockStackHost = makeHostComponent(BlockStack, attributes);
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import {
attributes,
events,
TAG,
} from "@tangent/ui-extensions-sdk/contracts/block-stack";

import { makeAuthorComponent } from "../_shared/author-component";
import { defineRemoteElement } from "../_shared/define-remote-element";
import { attributes, events, TAG } from "./block-stack.contract";

const BlockStackElement = defineRemoteElement(TAG, attributes, events);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { attributes } from "@tangent/ui-extensions-sdk/contracts/button";
import { Button } from "@tangent/ui-primitives/button";

import { makeHostComponent } from "../_shared/make-host-component";
import { attributes } from "./button.contract";

export const ButtonHost = makeHostComponent(Button, attributes, (props) => {
const onPress = props.onPress;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import {
attributes,
events,
TAG,
} from "@tangent/ui-extensions-sdk/contracts/button";

import { makeAuthorComponent } from "../_shared/author-component";
import { defineRemoteElement } from "../_shared/define-remote-element";
import { attributes, events, TAG } from "./button.contract";

const ButtonElement = defineRemoteElement(TAG, attributes, events);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { attributes } from "@tangent/ui-extensions-sdk/contracts/card-content";

import { CardContent } from "@/shared/ui/card";

import { makeHostComponent } from "../_shared/make-host-component";
import { attributes } from "./card-content.contract";

export const CardContentHost = makeHostComponent(CardContent, attributes);
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import {
attributes,
events,
TAG,
} from "@tangent/ui-extensions-sdk/contracts/card-content";

import { makeAuthorComponent } from "../_shared/author-component";
import { defineRemoteElement } from "../_shared/define-remote-element";
import { attributes, events, TAG } from "./card-content.contract";

const CardContentElement = defineRemoteElement(TAG, attributes, events);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { attributes } from "@tangent/ui-extensions-sdk/contracts/card-description";

import { CardDescription } from "@/shared/ui/card";

import { makeHostComponent } from "../_shared/make-host-component";
import { attributes } from "./card-description.contract";

export const CardDescriptionHost = makeHostComponent(
CardDescription,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import {
attributes,
events,
TAG,
} from "@tangent/ui-extensions-sdk/contracts/card-description";

import { makeAuthorComponent } from "../_shared/author-component";
import { defineRemoteElement } from "../_shared/define-remote-element";
import { attributes, events, TAG } from "./card-description.contract";

const CardDescriptionElement = defineRemoteElement(TAG, attributes, events);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { attributes } from "@tangent/ui-extensions-sdk/contracts/card-footer";

import { CardFooter } from "@/shared/ui/card";

import { makeHostComponent } from "../_shared/make-host-component";
import { attributes } from "./card-footer.contract";

export const CardFooterHost = makeHostComponent(CardFooter, attributes);
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import {
attributes,
events,
TAG,
} from "@tangent/ui-extensions-sdk/contracts/card-footer";

import { makeAuthorComponent } from "../_shared/author-component";
import { defineRemoteElement } from "../_shared/define-remote-element";
import { attributes, events, TAG } from "./card-footer.contract";

const CardFooterElement = defineRemoteElement(TAG, attributes, events);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { attributes } from "@tangent/ui-extensions-sdk/contracts/card-header";

import { CardHeader } from "@/shared/ui/card";

import { makeHostComponent } from "../_shared/make-host-component";
import { attributes } from "./card-header.contract";

export const CardHeaderHost = makeHostComponent(CardHeader, attributes);
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import {
attributes,
events,
TAG,
} from "@tangent/ui-extensions-sdk/contracts/card-header";

import { makeAuthorComponent } from "../_shared/author-component";
import { defineRemoteElement } from "../_shared/define-remote-element";
import { attributes, events, TAG } from "./card-header.contract";

const CardHeaderElement = defineRemoteElement(TAG, attributes, events);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { attributes } from "@tangent/ui-extensions-sdk/contracts/card-title";

import { CardTitle } from "@/shared/ui/card";

import { makeHostComponent } from "../_shared/make-host-component";
import { attributes } from "./card-title.contract";

export const CardTitleHost = makeHostComponent(CardTitle, attributes);
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import {
attributes,
events,
TAG,
} from "@tangent/ui-extensions-sdk/contracts/card-title";

import { makeAuthorComponent } from "../_shared/author-component";
import { defineRemoteElement } from "../_shared/define-remote-element";
import { attributes, events, TAG } from "./card-title.contract";

const CardTitleElement = defineRemoteElement(TAG, attributes, events);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { attributes } from "@tangent/ui-extensions-sdk/contracts/card";

import { Card } from "@/shared/ui/card";

import { makeHostComponent } from "../_shared/make-host-component";
import { attributes } from "./card.contract";

export const CardHost = makeHostComponent(Card, attributes);
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import {
attributes,
events,
TAG,
} from "@tangent/ui-extensions-sdk/contracts/card";

import { makeAuthorComponent } from "../_shared/author-component";
import { defineRemoteElement } from "../_shared/define-remote-element";
import { attributes, events, TAG } from "./card.contract";

const CardElement = defineRemoteElement(TAG, attributes, events);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { attributes } from "@tangent/ui-extensions-sdk/contracts/checkbox";
import { Checkbox } from "@tangent/ui-primitives/checkbox";

import { makeHostComponent } from "../_shared/make-host-component";
import { attributes } from "./checkbox.contract";

export const CheckboxHost = makeHostComponent(Checkbox, attributes, (props) => {
const onChange = props.onChange;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import {
attributes,
events,
TAG,
} from "@tangent/ui-extensions-sdk/contracts/checkbox";

import { makeAuthorComponent } from "../_shared/author-component";
import { defineRemoteElement } from "../_shared/define-remote-element";
import { attributes, events, TAG } from "./checkbox.contract";

const CheckboxElement = defineRemoteElement(TAG, attributes, events);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { attributes } from "@tangent/ui-extensions-sdk/contracts/heading";
import { Heading } from "@tangent/ui-primitives/typography";

import { makeHostComponent } from "../_shared/make-host-component";
import { attributes } from "./heading.contract";

export const HeadingHost = makeHostComponent(Heading, attributes);
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import {
attributes,
events,
TAG,
} from "@tangent/ui-extensions-sdk/contracts/heading";

import { makeAuthorComponent } from "../_shared/author-component";
import { defineRemoteElement } from "../_shared/define-remote-element";
import { attributes, events, TAG } from "./heading.contract";

const HeadingElement = defineRemoteElement(TAG, attributes, events);

Expand Down
Loading
Loading