Skip to content

Chat component

@open-matrix/chat-component is the embeddable variant of Chat. It registers the same browser elements as the Chat app plus a surface/ subpath that exports four conversation primitives for embedding in other apps.

Source tree (delta from chat)

The source layout is byte-identical to chat/src/ for the shared elements (browser/, components/, elements/, services/, app/services/, contracts/, runtime/, shims/, types/, utils/). The unique addition is:

chat-component/src/surface/
├── ConversationInput.ts
├── ConversationRenderer.ts
├── ConversationSessionList.ts
├── ConversationSurfaceActor.ts
└── register-surface.ts

These four classes plus the registration file are what makes chat-component an embeddable.

Exports

package.json:

json
"exports": {
  ".":           { "import": "./dist/runtime/index.js" },
  "./browser":   { "import": "./dist/browser/register-elements.js" },
  "./bootstrap": { "import": "./dist/browser/bootstrap.js" },
  "./surface":   {
    "types":  "./src/surface/register-surface.ts",
    "import": "./src/surface/register-surface.ts"
  },
  "./dist-path": { "import": "./dist-path.js" }
}

The ./surface export points at the TypeScript source rather than a built JS file. That's intentional: hosts (like Director) consume this as a Vite-resolved package specifier, and Vite is allowed to compile the TS source as part of the host's build graph. That keeps class identity unified — there's only one compiled copy of ConversationSurfaceActor, not two.

How Director consumes it

projects/matrix-3/packages/director/src/components/DirectorChatSurfaceHost.ts:

ts
await import('@open-matrix/chat-component/surface');

Vite resolves @open-matrix/chat-component/surface to chat-component/src/surface/register-surface.ts, compiles it, and emits a lazy chunk in Director's dist. The chunk has a side-effect that registers the four <conversation-*> custom elements.

The Vite alias trick is what makes this work. If we depended on the built dist/browser/surface/...js instead, the browser would receive bare-specifier imports it cannot resolve. By pointing the export at TS source, the host's Vite owns the compilation.

This is the M4-003 / HARVEST-005 architecture. Genuine cross-package plugin loading (HARVEST-040) is a separate, bigger effort.

Surfaces declaration

chat-component/matrix.json:

json
"surfaces": {
  "conversation": {
    "tag": "conversation-surface",
    "module": "./dist/browser/surface/register-surface.js",
    "route": "/apps/chat-component/surface/register-surface.js",
    "description": "Embeddable conversation surface plugin loaded by host shells (Director, etc.) via dynamic import"
  }
}

This is informational metadata for future host-side import-map work. Today, the module and route fields are not consumed by any code path; the actual loading uses the package specifier @open-matrix/chat-component/surface and Vite's resolver.

What chat-component does NOT include

  • The full Chat app shell. Chat-component does not register chat-app in a way that produces a usable user-facing app at /apps/chat-component/ — its webapp.appName is chat-component, not chat. If you want the Chat app, install @open-matrix/chat.
  • Slash commands. Slash commands belong to MatrixChatApp (in the chat app); the surface uses a slimmer input.
  • Inline-component browser. <mx-component-browser> is registered but not surfaced from the embeddable.

When to add a new surface to chat-component

If you want a new embeddable variant (say, a "session-only" surface or an "explorer-style" surface), follow the same pattern:

  1. Create src/surface/<NewSurface>Actor.ts extending MatrixActorHtmlElement.
  2. Add it to register-surface.ts (or create a parallel register-<name>.ts).
  3. Add a new entry under matrix.json surfaces.
  4. Add an export path under package.json exports pointing at the TS source.

The host (Director, smithers, etc.) then dynamic-imports it the same way DirectorChatSurfaceHost does.

See also

Source: projects/matrix-3/packages/chat-component/src/surface/, chat-component/package.json, chat-component/matrix.json.