IDE UI surfaces

These are contributes.* entries that hang UI (or a command) off the Glixo Code IDE shell. Production marketplace modules and teaching samples are separate: the runnable reference implementations live under glixo-extensions-docs/samples/ide/, while glixo-community-modules/catalog/ contains end-user integrations only.

A cross-cutting fact that matters for every type here: a contribution's component string only renders in-process if the host has build-time-registered that exact name and bound it to the contributing extension's id (glixo-code/sources/extensions/renderTier.ts, ownership check via getRegisteredComponentOwner). Third-party authors must ship a2ui or a coded entry; a bare component name is only valid for first-party code compiled into the host. The IDE examples below use real A2UI or runtime entries so they do not depend on an imaginary host component.

Sidebar view — contributes.sidebarViews

What it is. A panel mounted into a named sidebar slot (session rail or workspace panel). Use it for always-visible, glanceable state tied to a session, workspace, or machine.

Manifest shape. See the working glixo-extensions-docs/samples/ide/a2ui-sidebar/glixo.module.json example:

"contributes": {
  "sidebarViews": [
    {
      "id": "com.example.status.sidebar",
      "slot": "session.sidebar",
      "title": "Status",
      "a2ui": {
        "root": "root",
        "components": [
          { "id": "root", "type": "text", "props": { "text": "Ready" } }
        ]
      }
    }
  ]
}

The same contribution shape may target workspace.sidebar; the host must actually publish and render the chosen slot. Required fields (glixo-platform/catalog/schema/glixo.module.schema.json, sidebarViewContribution): id, slot, title. A third party must also provide a2ui or an entry to be renderable.

Code you write. Ship a declarative a2ui surface (root + components) or a coded entry artifact rendered sandboxed in an iframe — not a bare component name. Keep A2UI examples read-only for now: extension-owned command handlers and data binding are not public contracts yet.

Host consumption. glixo-code/sources/code/RightRail.tsx:222-227 calls getInstalledSidebarViews(extensionState, 'session.sidebar'), filters to renderable tiers, and mounts each via ExtensionComponentHost at RightRail.tsx:757-767; workspace.sidebar views render through the same getInstalledSidebarViews selector (glixo-code/sources/extensions/store.ts:247).

Sample to study. glixo-extensions-docs/samples/ide/a2ui-sidebar.


Settings panel — contributes.settingsPanels

What it is. A custom surface listed under Code's Extension Settings tab. The generic config.fields form is the supported choice for user-editable extension settings today.

Manifest shape. The host accepts this contribution shape:

"contributes": {
  "settingsPanels": [
    {
      "id": "glixo.samples.hello.settings",
      "slot": "settings",
      "title": "Hello Extension",
      "component": "HelloExtensionSettings"
    }
  ]
}

Required fields (settingsPanelContribution): id, title; slot is optional and defaults to the settings surface. A renderable third-party panel also needs a2ui or an iframe entry.

Code you write. A third-party panel needs a2ui or an iframe entry, not an unregistered component string. Do not use a custom panel for editable settings yet: A2UI has no public persistence/data-binding bridge and extension-owned command handlers are not available. Use config.fields instead.

Host consumption. glixo-code/sources/settings/GlobalCodeSettings.tsx:405-410 calls getInstalledSettingsPanels(extensionState), sorts by priority/title, and renders the list inside the ExtensionsSettingsPanel tab, wrapped by <ExtensionSlot slotId={HOST_SLOTS.settingsPanel}> (GlobalCodeSettings.tsx:428).

Sample to study. None until a third-party panel can persist settings end to end.


Command / action — contributes.actions

What it is. A command surfaced in the Agxos toolbar and dispatched through the host command registry. Use it for a one-shot user-triggered operation backed by a command the host already exposes.

Manifest shape. See glixo-extensions-docs/samples/ide/command-action/glixo.module.json:

"contributes": {
  "actions": [
    {
      "id": "com.example.quickNotes.show",
      "slot": "agxos.toolbar",
      "title": "Show Quick Notes",
      "command": "glixo.agxos.windowAction",
      "input": { "appId": "quick.notes", "command": "show" }
    }
  ]
}

Required fields (commandContribution, schema lines 623-639): id, title, command, slot. Real slot values seen in code (glixo-sdk/packages/nodejs/src/extensionRegistry.ts:119-133, ExtensionContributionSlot): session.toolbar, agxos.toolbar, machine.contextMenu, message.contextMenu, command.palette, plus the open-ended agxos.* family.

Code you write. command must name a handler already registered by the host. Extensions cannot register novel command handlers yet. input carries command-specific arguments.

Host consumption. agxos.toolbar is the confirmed render path: glixo-code/sources/agxos/AgxosIframeHost.tsx calls getInstalledActions(extensionState, 'agxos.toolbar'). Do not target command-palette or context-menu slots until the host mounts them.

Sample to study. glixo-extensions-docs/samples/ide/command-action.


HTTP route — contributes.routes

What it is. A full-screen route the extension registers, resolved by a host route resolver to a screen component. Use it for a dedicated full-page experience (not a sidebar panel or dialog).

Manifest shape. The schema accepts this shape, but Glixo Code does not currently mount extension routes:

"contributes": {
  "routes": [
    {
      "id": "com.example.status.route",
      "path": "/status",
      "title": "Status",
      "component": "StatusRoute"
    }
  ]
}

Required field (routeContribution, schema lines 692-705): path only. The schema documents two equivalent field names for the screen reference: component (used by every first-party manifest, including this one) or screen (the glixo-sdk ExtensionRouteDefinition field name, glixo-sdk/packages/nodejs/src/extensionRegistry.ts:135-140) — at least one is expected.

Code you write. Do not build a product around this contribution yet. A server can implement the URL, but Glixo Code has no route-render call site connecting contributes.routes to its router. A bare component name is not sufficient.

Host consumption. glixo-sdk's Node ExtensionRegistry.listRoutes() (glixo-sdk/packages/nodejs/src/extensionRegistry.ts:791-793) is the generic accessor extension hosts call; no route-render call site (router/screen resolver) was found inside glixo-code's own sources for this contribution family — treat routes as declared-and-collected today, wired per-host.

Sample to study. None until the host mounts this contribution end to end.


Context-menu item — contributes.contextMenuItems

What it is. Context-menu commands surfaced in a host-published slot. It uses the same command contribution shape as actions.

Manifest shape (derived from $defs/commandContribution, the same definition actions uses, schema lines 623-639 — required: id, title, command, slot):

"contributes": {
  "contextMenuItems": [
    {
      "id": "your.extension.contextAction",
      "slot": "machine.contextMenu",
      "title": "Your action",
      "command": "your.extension.contextAction"
    }
  ]
}

Code you write. Same as actions: command is a string dispatched through the host command registry; no manifest-side handler code.

Host consumption. glixo-code/sources/extensions/store.ts:243 defines getInstalledContextMenuItems(state, slot?) and it's wired into the kind-dispatch helper at store.ts:311, but — like routes — no render site in glixo-code was found calling it to actually paint a context menu. Declared and collected, not yet mounted anywhere in the IDE shell.

Sample to study. None until a Glixo Code context-menu surface consumes this contribution. Use a proven agxos.toolbar action when it fits the interaction.


View patch — contributes.viewPatches

What it is. A patch that injects into, replaces, or decorates a host-published slot — the mechanism used to extend Code's own built-in UI (settings list, session rail) rather than add a brand-new panel.

Manifest shape (derived from $defs/viewPatchContribution, schema lines 675-691 — required: id, target, operation):

"contributes": {
  "viewPatches": [
    {
      "id": "your.extension.settingsBanner",
      "target": "settings.panel.header",
      "operation": "prepend",
      "component": "YourBannerComponent"
    }
  ]
}

operation must be one of "append" | "prepend" | "replace" | "decorate" (schema enum). target must be one of the actual host-published slot ids, not an arbitrary string — the real, currently-published set is (glixo-code/sources/extensions/slots.ts:24-33, HOST_SLOTS):

Code you write. Nothing manifest-side beyond the patch declaration. As with the other component-bearing kinds, only a first-party registered component renders in-process; a third party ships a2ui or entry to actually render. For operation: "decorate", only an in-process component can wrap the existing content — it receives it as input.children (glixo-code/sources/extensions/ExtensionSlot.tsx:106-111, 165-173); an a2ui/iframe-tier decorator is skipped and the original content passes through unchanged.

Host consumption. glixo-code/sources/extensions/ExtensionSlot.tsx:137-182 (<ExtensionSlot slotId={...}>) reads getInstalledViewPatches(state, slotId), sorts by priority (ties broken by id), and applies prepend/append/replace/decorate around the host's default children. It's mounted today at session.sidebar and session.sidebar.footer in glixo-code/sources/code/RightRail.tsx:704,737, and at settings.panel.header/settings.panel in glixo-code/sources/settings/GlobalCodeSettings.tsx:426,428.

Sample to study. glixo-extensions-docs/samples/ide/view-patch. It is deliberately read-only and targets session.sidebar.footer, a real published slot.

Client context in UI components

In-process extension UI receives a small gated context, not the full host runtime. Use it for commands, events, UI actions, and capability calls. Every facade is checked against the extension's granted capabilities.

export async function refreshStats(context) {
  if (!context.capabilities.has('capability.glixo.memory.read')) return;

  await context.commands.execute('com.example.sidebar-stats.refresh', {
    sessionId: context.descriptor.sessionId,
  });
}

The descriptor contains identity only: extensionId, optional componentId / viewId, and optional sessionId, workspaceId, projectId, or machineId. Keep host-only runtime state out of third-party UI code.