Skip to content

Stop notes inventing a colour, and mark system prompts by shape - #390

Merged
dovvnloading merged 1 commit into
mainfrom
ux/note-color-defaults
Sep 1, 2026
Merged

dovvnloading merged 1 commit into
mainfrom
ux/note-color-defaults

Conversation

@dovvnloading

Copy link
Copy Markdown
Owner

Problem

Every note rendered a saturated green in an otherwise monochrome canvas, and a system-prompt note rendered green with a purple border.

The green was invented by the save path. SceneNode.color's own contract is "None means use the kind's own default colour, a rendering fallback that is entirely the frontend's job", and NoteNodeView already honours it (backgroundColor: data.color ?? undefined). But chat_library.py wrote str(note.get("color") or "#4a7c59") on every save, so:

  • a note nobody ever coloured came back from a save/reload permanently green, and
  • #4a7c59 is not one of GroupColorPicker's eight swatches (its Green is #3f8f5c), so that colour could never be deliberately chosen, nor re-chosen after changing it.

The notes.color column is TEXT NOT NULL, which is why a value was being invented at all.

The purple was a semantic marker borrowing a user-choice swatch. NOTE_SYSTEM_PROMPT_BORDER_COLOR was defined as GROUP_NAMED_COLORS[2].hex - the picker's own "Purple" - and applied as an inline borderColor. That put a semantic marker (this note governs the branch) and a user's colour choice in the same visual channel, indistinguishable the moment anyone picks Purple for a note body, and spent a hue in a palette whose whole design is monochrome. The meaning was already carried by shape: .note-node.system-prompt's dashed, heavier border plus the header's own badge.

Change

  • The save path stores "" - the storable spelling of "no colour chosen" under a NOT NULL column - instead of a hex, and _legacy_default_to_none maps both "" and the old forced default back to None on read.
  • Rows already carrying #4a7c59 are normalised on load, not rewritten, so existing databases render neutral with no destructive migration. A colour the user genuinely chose still round-trips as that hex.
  • The system-prompt border loses its inline override and takes a theme token in CSS; the dashed 2px border and the badge continue to carry the meaning. NOTE_SYSTEM_PROMPT_BORDER_COLOR is removed.

Test plan

  • Full backend suite on a clean checkout of this branch: 3095 passed, 19 skipped. ruff clean. npm run check green.
  • New round-trip coverage in test_session_format_adr009.py, all reloading through the notes table exactly as loadChat does: an uncoloured note stays None across a full DB round trip; a chosen #3f8f5c survives unchanged; and a legacy row carrying #4a7c59 loads as None.
  • NoteNodeView.test.tsx asserts a system-prompt note carries no inline borderColor, so the marker cannot drift back onto a palette hue.
  • Verified live against the built bundle at the app's real 1440x900 window: the note renders rgb(37, 37, 37) (the shared node surface) with a dashed rgb(148, 148, 148) border and no inline styles at all.

SceneNode.color's own contract is "None means use the kind's own default
colour, a rendering fallback that is entirely the frontend's job", and
NoteNodeView already honours it (backgroundColor: data.color ?? undefined).
The save path overrode that on every write with a hard-coded "#4a7c59", so
a note nobody ever coloured came back from a save/reload permanently
green - in the middle of an otherwise monochrome canvas, and in a colour
that is not one of the picker's eight swatches (its Green is #3f8f5c), so
it could not be deliberately chosen or re-chosen either.

The column is TEXT NOT NULL, which is why a value was being invented at
all; the empty string is the storable spelling of "no colour chosen" and
normalises back to None on read. Rows already carrying the old default are
normalised the same way on load rather than rewritten, so existing
databases render neutral with no destructive migration. A note whose
colour was genuinely chosen still round-trips as that hex.

Separately, a system-prompt note took the colour picker's own "Purple"
swatch as its border colour. That put a SEMANTIC marker (this note governs
the branch) and a USER CHOICE (I coloured this note purple) in the same
visual channel, indistinguishable the moment anyone picks Purple for a
note body - and spent a hue in a palette whose whole design is monochrome.
The meaning was already carried by shape: .note-node.system-prompt's
dashed, heavier border, plus the header badge. That border now takes a
theme token, and the inline colour override is gone.

Co-Authored-By: Claude <noreply@anthropic.com>
@dovvnloading
dovvnloading merged commit 22f0345 into main Sep 1, 2026
4 checks passed
@dovvnloading
dovvnloading deleted the ux/note-color-defaults branch September 1, 2026 16:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant