Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
666c1ff
feat(theme): follow the system's dark mode, with an Appearance setting
jerelvelarde Oct 28, 2026
3306a54
feat(ui): a visual system of cool greys, one accent and five type sizes
jerelvelarde Oct 28, 2026
c4d5ff3
feat(orb): an orb drawn on a canvas, in a colour for each Bot
jerelvelarde Oct 28, 2026
867dd0e
feat(orb): one animation clock for every orb, and eyes that follow th…
jerelvelarde Oct 28, 2026
7a8f072
feat(orb): every Bot and OpenBot's own mark draw the orb
jerelvelarde Oct 28, 2026
a901367
feat(orb): conversation faces and onboarding's example Bots draw the orb
jerelvelarde Oct 28, 2026
291277b
docs: changelog for OpenBot's new look
jerelvelarde Oct 28, 2026
2f423ec
fix(orb): keep small faces in their circles, sharp on any screen, sti…
jerelvelarde Oct 28, 2026
cb8f677
fix(orb): the skins draw the orb too
jerelvelarde Oct 28, 2026
f5df1e1
fix(orb): OpenMuse's header draws its face at the size of its slot
jerelvelarde Oct 29, 2026
851afa1
fix(orb): a face at rest draws nothing until it changes
jerelvelarde Oct 29, 2026
cfd681d
fix(orb): two Bots in one colour no longer share a face, and none is …
jerelvelarde Oct 29, 2026
c962106
feat(orb): a Bot's seed can name its colour
jerelvelarde Oct 30, 2026
09e7a1b
feat(bots): an edit can change a Bot's look
jerelvelarde Oct 30, 2026
cc69dc4
feat(bots): a Bot's owner can choose how it looks
jerelvelarde Oct 30, 2026
bac3254
docs: a Bot's look, and seeds that name a colour
jerelvelarde Oct 30, 2026
1b6ec7a
test(settings): the preferences test names its requests without an un…
jerelvelarde Oct 30, 2026
b821e6c
fix(bots): an edited Bot keeps its place in the list
jerelvelarde Oct 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .claude/skills/openbot-screen-layout/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,11 +188,11 @@ itself when the dialog changes something.

## Units

The root is the browser's 16px, so Tailwind's spacing, sizes and widths are their documented values
(`p-4` is 16px, `max-w-3xl` is 768px) and land on its 4px grid. Text is the exception: `styles.css`
sets every `--text-*` to Tailwind's default times 15/16, so `text-sm` is 13.125px and body text is
15px. That is the scale this app has always read at; do not size text in px or rem by hand to match
the docs.
The root is the browser's 16px, so Tailwind's spacing, sizes, widths and text sizes are their
documented values (`p-4` is 16px, `max-w-3xl` is 768px, `text-sm` is 14px) and land on its 4px grid.
Text used to be the exception, on a 15px scale of its own; it is not any more. Where a screen needs one
of the named sizes, use the five `type-*` utilities in `theme.css`; do not size text in px or rem by
hand.

Even so, never hardcode a pixel constant derived from a Tailwind token, and never compute a ratio
from one. Measure the element — `offsetWidth`, `clientWidth`, a `ResizeObserver` — and scale from what
Expand Down
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,19 @@ Newest first. `Unreleased` is what is on `main` and not yet tagged.
`COMPUTER_MEMORY_HIGH_PERCENT=0` and `COMPUTER_TAB_IDLE_MS=0` (Helm: `computers.memoryHighPercent`
and `computers.tabIdleMs`) keep the old behaviour.

### Choose how a Bot looks

A Bot's Setup page has a Look section for whoever may edit the Bot: its face, large, the eight
colours to pick from, and New face, which keeps the colour and draws a different face. A choice
saves at once and shows wherever the Bot appears, the conversation list and a conversation's header
included. The choice is the Bot's avatar seed, so nothing is migrated: `PATCH /api/agents/:agentId`
takes an optional `avatarSeed`, and a seed may start with a colour, as `moss:knowledge`. A seed that
names no colour, which is every Bot's until somebody chooses, draws exactly as before. Violet stays
OpenBot's own, and a `violet:` seed or a colour no Bot has is refused. A tenant package's
`avatar_seed` may name a colour the same way. A new look is named on the Bot's `bot.updated` audit
row. `GET /api/agents` now lists Bots in the order they were made, so restyling or renaming a Bot no
longer moves it to the end of the list.

### A computer closes browsers nobody is using when its memory runs high

A computer runs one browser for each Bot that is browsing, and the only limits were a count and half
Expand Down Expand Up @@ -133,6 +146,18 @@ again when they come back to the end or press the arrow. A rail beside a convers
per turn: hovering one previews what was asked and answered, and choosing it goes there. Nothing
changes for a deployment.

### OpenBot has a new look

- Every screen has new colours: cool neutral greys, a near-black primary button that turns light
in dark mode, a purple focus ring, slightly rounder corners and quicker easing. Text now follows
the browser's own size, so it is a little larger than before.
- Dark mode follows the system by default. Settings → General → Appearance offers System, Light and
Dark, kept in this browser. Someone who had turned the old dark switch on stays dark.
- Every Bot, and OpenBot's own mark, is an orb with eyes that blinks and follows the pointer. It
holds still with reduced motion.
- Each Bot's orb takes its colour and pattern from the Bot, so two Bots in one colour still look
different. Violet is kept for OpenBot's own mark.

### Generated interfaces use A2UI 1.0

With generative UI on, a Bot builds an interface from the A2UI 1.0 basic components instead of the
Expand Down
24 changes: 18 additions & 6 deletions app/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,27 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>OpenBot</title>
<!-- Sets the theme before the first paint. Must stay inline and classic: `defer` or `type="module"` runs too late. -->
<!-- The rule in src/lib/theme.ts (parseThemePreference, then resolveTheme): the person's choice, else the system's setting. -->
<script>
// localStorage throws in some privacy modes; a theme is not worth taking the app down for.
try {
const dark = window.localStorage.getItem("openbot-theme") === "dark";
(() => {
let choice = null;
// localStorage throws in some privacy modes; the system's setting still applies.
try {
choice = window.localStorage.getItem("openbot-appearance");
if (choice !== "system" && choice !== "light" && choice !== "dark")
choice = window.localStorage.getItem("openbot-theme") === "dark" ? "dark" : "system";
} catch {}
const dark =
choice === "dark" ||
(choice !== "light" && !!window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches);
document.documentElement.classList.toggle("dark", dark);
document.documentElement.style.colorScheme = dark ? "dark" : "light";
const skin = window.localStorage.getItem("openbot-skin");
if (skin && skin !== "openbot") document.documentElement.dataset.skin = skin;
} catch {}
// The person's skin, painted before the bundle loads, the same way.
try {
const skin = window.localStorage.getItem("openbot-skin");
if (skin && skin !== "openbot") document.documentElement.dataset.skin = skin;
} catch {}
})();
</script>
</head>
<body>
Expand Down
1 change: 0 additions & 1 deletion app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
"@tanstack/react-router": "^1.170.27",
"@tanstack/react-table": "^9.3.1",
"better-auth": "^1.6.27",
"boring-avatars": "^2.0.4",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"embla-carousel-react": "^8.6.0",
Expand Down
16 changes: 11 additions & 5 deletions app/src/components/agents/abstract-avatar.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
import Avatar from "boring-avatars";
import { BotOrb } from "@/components/agents/orb/bot-orb";

/**
* A Bot's face: its orb, with the look its seed picks. The seed is the Bot's id unless a tenant
* package set one or the Bot was copied from another, so `ChannelAvatar`, which goes by id, matches it
* for every other Bot.
* The orb hides itself from assistive tech, so the Bot is announced once, by name.
*
* Clipped to its circle, so its glow never spills into the row beside it or makes a list scroll.
* At rest the glow is too faint that far out to be missed.
*/
export function AbstractAvatar({
name,
seed,
Expand All @@ -16,10 +25,7 @@ export function AbstractAvatar({
className="inline-flex shrink-0 overflow-hidden rounded-full"
style={{ height: size, width: size }}
>
{/* The drawing carries its own role; hidden so the coworker is announced once, by name. */}
<span aria-hidden="true" className="contents">
<Avatar name={seed} size={size} />
</span>
<BotOrb seed={seed} size={size} />
</span>
);
}
Loading
Loading