Rebar UI

1.0

opinion

FloatAssistant

A button that opens an assistant panel over the page.

Open panelPress Ask; the panel, the composer and the greeting
Brand

Harbour rebrand

Offline sync for tasks

Eng

JW

Harbour homepage

Design

MC

Open the assistant and ask about the project.

Closed
Dark
Bottom left
Accent

In context

On a phoneThe button over the project, and the conversation as a screen
Harbour rebrand

Offline sync for tasks

Jonas · Eng

Harbour homepage

Maya · Design

Assistant
What slipped on the mobile release this week?
Two things. Offline sync is in review and the crew-interview notes are late.
Task · Offline sync
Can you draft a note for the crew?

Ask about Harbour rebrand

Props

PropTypeRequiredDefault
namestring | undefinedNoAssistant
greetingstring | undefinedNoHi! How can I help you today?
voiceGreetingsstring[] | undefinedNo—
position"bottom-right" | "bottom-left" | "top-right" | "top-left" | undefinedNobottom-right
accentColorstring | undefinedNovar(--rebar-color-primary, #0066cc)
personaOrbPersonaId | undefinedNo—
personaLabelstring | undefinedNo—
onSendMessage((message: string, screenshot?: string | undefined, pageContext?: string[] | undefined) => void) | undefinedNo—
onVoiceRecord((recording: boolean) => void) | undefinedNo—
onAttachmentPress(() => void) | undefinedNo—
screenshotEnabledboolean | undefinedNotrue
onCaptureScreenshot(() => Promise<string>) | undefinedNo—
screenshotAcknowledgmentstring | undefinedNoYep, I'm looking at the screen — what would you like me to analyze?
contextAwareboolean | undefinedNofalse
voiceEnabledboolean | undefinedNotrue
mode"text" | "voice" | undefinedNo—
onModeChange((mode: "text" | "voice") => void) | undefinedNo—
draggableboolean | undefinedNotrue
minimizableboolean | undefinedNotrue
sidebarDockableboolean | undefinedNofalse
sidebarWidthnumber | undefinedNo380
dockMode"floating" | "sidebar" | undefinedNo—
onDockModeChange((dockMode: "floating" | "sidebar") => void) | undefinedNo—
onSlashCommand(() => void) | undefinedNo—
onHistoryPress(() => void) | undefinedNo—
modelLabelstring | undefinedNo—
onModelPress(() => void) | undefinedNo—
apiEndpointstring | undefinedNo—
apiAuthTokenstring | undefinedNo—
getHostContext(() => string | Promise<string | undefined> | undefined) | undefinedNo—
voicesFloatAssistantVoiceOption[] | undefinedNo—
voiceIdstring | undefinedNo—
onVoiceChange((voiceId: string) => void) | undefinedNo—
theme"light" | "dark" | undefinedNolight
markdownboolean | undefinedNotrue
openboolean | undefinedNo—
defaultOpenboolean | undefinedNo—
onOpenChange((open: boolean) => void) | undefinedNo—
classNamestring | undefinedNo—
bionicboolean | undefinedNo—
bionicOptionsBionicOptions | undefinedNo—

API integration

The construct never sees an API key: apiEndpoint is a server-side proxy you implement. It receives { message, history, screenshot?, pageContext?, hostContext? } and returns { reply }. getHostContext is called fresh before every send to describe the real on-screen state (the open project, an active filter).

POST /api/assistant/chat
{ "message": "What is due this week?", "history": [...], "hostContext": "Project: Harbour rebrand, 5 open tasks" }

{ "reply": "Seven tasks, three today. Offline sync is in review." }

Features

Drag the button anywhere (draggable), minimise it to a dot (minimizable), dock the panel to a full-height sidebar (sidebarDockable), switch to voice (voiceEnabled, voices), render replies as Markdown (markdown), and pick a WebGL orb persona (persona: Spark, Strato or Chorus). The footer always says it is an AI.

Accessibility

Real role="dialog" when expanded, aria-label and aria-expanded on the trigger, keyboard-navigable controls and visible focus. The floating button is always reachable with Tab.

data-rebar-* attributes

data-rebar-component="float-assistant", data-rebar-part="trigger" on the button, "panel" on the expanded panel, "messages" on the message list.