Extending Oda's interface

DataMind Oda's interface is built from slots: named places that render the components a plugin contributes. Everything a plugin adds to the interface goes through the slots API, and this page is its contract: the registration call, the shape of a slot, the client services your code can use, and the rules your styles, routes and copy must follow.

How a plugin's code reaches the interface is on How client plugins load; the running side of a package is on The browser half of a dynamic package.

The registration contract

A minimal entry looks like this:

js
return {
  inject: ['slots'],
  apply(ctx) {
    ctx.slots.inject('<slot key>', () =>
      ctx.slots.register({ name: 'my-entry' }, () =>
        React.createElement('div', null, 'Hello'),
      ),
    );
  },
};

The catalog entry for the slot you choose states exactly which options the call must pass.

Slot kinds and scopes

The client services your code can use

ServiceMethodsWhat it covers
slotsregister, registerFactory, injectThe registration surface on this page.
layoutselectPanel, beginNavigation, toggleSidebar, openRightbar, closeRightbarWhat the app shows around your UI: the panel in view, navigation, the sidebar and the right bar.
sessionsretain, using, retainInfo, refreshProjections, search, fork, scope, bindingThe chats: searching, forking and following their state.
uiWorkspaceopenSession, openWorkspace, forkSession, connectWorkspace, startSession, archiveSession, unarchiveSession, pickDirectory, listDirectory, createDirectoryOpening and moving around: workspaces and chats, starting, forking, archiving and restoring them, and picking, listing or creating folders.
workspacescreate, rename, delete, insertSessionBefore, archiveSession, unarchiveSessionThe workspace list: creating, renaming, deleting, ordering and archiving.
themegetTheme, setTheme, setFontSize, register, overrideTokensReading and setting the theme and text size, and registering or overriding theme tokens.
localegetLocale, getSnapshot, subscribe, setLocale, addLanguage, register, bindThe running language: reading it, following changes, and registering a language and its dictionaries.

The notes are orientation only; query a service through the inspection tools for its exact contract. Read the directory in your installation instead of assuming a name is there, since availability can differ between installations. A browser half can also declare timer for timer helpers; The browser half of a dynamic package covers that service.

Styling and theme tokens

Routes from browser code

Code that runs in the interface addresses the app's own routes under three prefixes: api, plugins and open-in-app. Three rules keep a route resolvable:

Copy and language

Questions, answered

Which client services can my interface use?

The set includes slots, layout, sessions, uiWorkspace, workspaces, theme and locale, and a browser half can also declare timer. Declare what you use in inject, and query the inspection tools for the exact contract, since availability can differ between installations.

What do a slot's kind and scope mean?

A slot's kind is one of single, list, keyed and chain: a single or keyed slot renders one entry, and a list slot renders several, sorted by their order. A slot's scope (root, session or session-maybe) names the context its component runs in: components receive the framework hooks of their slot's scope, and useSessions(state => state.current) reads the current chat.

How do I control where my entry sits among the others?

Pass the order the slot documents: it is the entry's position among the slot's entries, ascending, with a default of 0.

What does shadows-shipped-ui mean in the catalog?

It is the catalog's mark on a single or keyed slot that already ships an occupant, such as the root slot, which carries the application frame. In such a slot your entry is the one that renders, so a registration there replaces the shipped interface; a priority is assigned for you, lower than every shipped entry, and you never pass one yourself.

Can I hard-code a string in my component?

No. The locale dictionaries are the only files allowed to own translated text, and copy hard-coded in component code is rejected, including text in copy-bearing attributes such as alt, aria-label, placeholder and title. Put the string in a dictionary and reference it from the component.

Which language does the interface run in?

English. Every string resolves through the locale service, and a language pack can add another language; the calls and rules are on Language packs.

How do I reference one of the app's own routes?

Write it document-relative under the api, plugins or open-in-app prefix, as in api/x; resolve it against the document's base address, and strip the leading slash before the browser uses a shared route key. A root-absolute path is rejected.

Should my UI use fixed colours?

No. Base the look on the theme's CSS variables, var(--dsw-alias-bg-layer-1) and var(--dsw-alias-label-primary), and override tokens through the theme service where your UI needs its own colours; an override is yours and goes away when your plugin unloads.

Where do I find the slot catalog?

The client side of the inspection tools serves it: list the providers first, then query the client platform, and the answer carries the slots with their kinds, scopes and the options each registration must pass. Registrations that failed can be read back there too; see Inspect and manage extensions.