Skip to main content

UI kit

Import shared components from @timon/ui so your plugin uses the same React instance, controls and theme tokens as Timon.

ExportUse
Slot, SlotKeyedRender extension points your component declares
TileFrame for a widget using frame: 'none'
BrandMarkBackend brand mark using a catalogue brand or model slug; unknown brands get a fallback
Button, Input, Label, Switch, SeparatorForm primitives
DropdownMenu*, Tooltip*Menus and tooltips
Dialog, DialogPortal, DialogOverlay, DialogTrigger, DialogClose, DialogContent, DialogHeader, DialogFooter, DialogTitle, DialogDescriptionDialog composition
Select, SelectGroup, SelectValue, SelectTrigger, SelectContent, SelectLabel, SelectItem, SelectSeparator, SelectScrollUpButton, SelectScrollDownButtonSelect composition
HUD_SURFACETheme-aware cockpit surface classes; opaque string for cn(HUD_SURFACE, className)
cnMerge class names
t, getLocaleProduct translations and current locale
createTranslatorTranslations shipped by your own plugin

Plugin-owned translations avoid collisions with product keys. A missing translation key is displayed as its key rather than empty text. Ship your interface icons yourself; provider logos should use BrandMark so the app can keep them consistent.

Plugin source is not scanned by Tailwind

The app's stylesheet is compiled before a third-party package is installed. Arbitrary classes such as text-[10px] or a generated conic-gradient class may not exist and silently do nothing. Use inline styles for precise values and SVG for drawings. Do not assume every standard Tailwind utility is present either; only compiled classes are available.

Theme custom properties remain usable in inline styles, for example hsl(var(--muted)) or hsl(var(--primary)). The supplied quota monitor demonstrates SVG usage rings without depending on plugin-specific stylesheet compilation.

Use the existing WidgetChromeProps type from @timon-ai/sdk for a custom widget frame (widget: { frame: 'none' }). Forward className to the root, render poignee in the header actions, and use editing to show configuration controls. The SDK keeps poignee typed as unknown to avoid a React dependency; narrow it to React.ReactNode when rendering.

DialogContent includes its portal, overlay and close button. Set hideClose only when supplying your own accessible close control. SelectContent includes its portal and scroll buttons. Compose it with SelectItem children and give the trigger an accessible label. HUD_SURFACE reuses compiled theme classes; do not depend on its literal value or copy arbitrary Tailwind values into your plugin.

Reading usage

Both host and client contexts expose ctx.usage.list() with usage:read. Render all returned provider-defined windows rather than a fixed list of window names.

EntryDisplay
available: trueReal measurements
available: false, no errorNo probe available; omit the gauge
available: false, with errorUnavailable state; never substitute zero

A host plugin can register a probe with usage:collect. It is removed when the plugin unloads, cannot displace a native probe and cannot duplicate another plugin's collector for the same backend.

Backend extensions

With backends:register, prefer a profile on an existing dialect when its protocol matches. Write a dialect only for a genuinely different CLI protocol. The daemon owns process launch and checks plugin-chosen binaries against the operator policy.

Resolve credentials through secret settings or the vault and pass values to the profile. Plugins cannot use envFrom or requiredEnv to read the daemon's ambient environment. Unloading unregisters the backend and updates the catalogue. See the real provider template for both registration paths.