984407f931637099f3e4fe594615ad849ed9641c
187
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
a6d6550483 |
fix(ui): walk the eleven dangling-style reports — two were real
CI & Build / Python lint (push) Successful in 4s
CI & Build / Plugin hooks (push) Successful in 10s
CI & Build / integration (push) Successful in 19s
CI & Build / TypeScript typecheck (push) Successful in 33s
CI & Build / Python tests (push) Successful in 57s
CI & Build / Build & push image (push) Successful in 45s
#2444. Each needed reading rather than a batch fix, and the split was 2 real losses, 4 false reports, 5 wrappers that are bare on purpose. REAL: .system-card was a flex row, and every child still says so — .system-swatch and .system-actions are flex-shrink: 0, .system-body and .system-form--inline are flex: 1. align-items: flex-start is why the swatch carries margin-top: 0.3rem: nudged onto the first line of text. .systems-list no rule AT ALL, so the systems list rendered with browser bullets and indent. Invisible to the check — see below. .graph-embed the panel is a flex column whose header is flex-shrink: 0, so this is the item that takes the remaining height. Without it the `height: 100%` on the line below resolves against auto and does nothing, which left the comment above it specifying a rule that could not work. FALSE REPORTS, and the checker was wrong rather than the code: `.pane.empty` and `td.num` are base rules for the element that carries those classes — the check read any compound with more than a lone class as a modifier. It now records a compound's whole class SET and clears an element carrying all of them, which is exact: recording the classes individually would have cleared `.pane` everywhere on the strength of a rule that only applies alongside `.empty`. Four reports gone, and a check with false reports is one that gets skimmed. BARE ON PURPOSE — .rb, .topic-group, .new-topic, .sub-list, .dash-head, and both .detail-row rows. Each namespaces descendant rules and assumes nothing about layout, which is the tell that separates them from a deleted base. All seven now carry a comment saying so, so the next reader doesn't re-litigate them and a NEW entry in the report means something actually changed. Also recorded in the script: it cannot see a class with no rule anywhere, since that is indistinguishable from a semantic-only hook. `.systems-list` was found by reading the file beside a class that WAS half-styled. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UaYUaouG9jjhATyuxCKrQs |
||
|
|
6ac821178f |
fix(design): declare --tp-fill so the token check can see it
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 9s
CI & Build / integration (push) Successful in 12s
CI & Build / TypeScript typecheck (push) Successful in 33s
CI & Build / Python tests (push) Successful in 54s
CI & Build / Build & push image (push) Canceled after 32s
The swatch set it inline only, and a custom property that exists nowhere in a stylesheet is exactly what check_design_tokens reports as unresolvable — it was right, and it caught this on the commit that introduced it. Declaring it on .tp-swatch is the real fix rather than a silencer: a token that resolves to nothing now renders as bare checks instead of an invalid gradient, which is what the inline value would produce when empty. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UaYUaouG9jjhATyuxCKrQs |
||
|
|
4a9744172f |
fix(ui): restore four base rules a CSS sweep deleted, and check for the rest
CI & Build / Python lint (push) Failing after 3s
CI & Build / Plugin hooks (push) Successful in 9s
CI & Build / TypeScript typecheck (push) Successful in 23s
CI & Build / integration (push) Successful in 27s
CI & Build / Python tests (push) Canceled after 38s
CI & Build / Build & push image (push) Canceled after 0s
Operator reported four things looking wrong. Two were the same bug, and it is
not a design drift — it is deleted CSS.
Removing a rule from a scoped stylesheet leaves its modifiers behind. The
selector still exists, so nothing reads as unused, and the element renders with
no base styling at all:
.btn-workspace base gone, :hover survived — the Workspace link rendered
as raw browser blue, underlined
.milestone-header base gone, .clickable and :hover survived. Every child is
written for a flex ROW (.ms-name { flex: 1 }, the progress
track, .ms-pct), so without the parent they stacked and a
one-line milestone became five. That is the "projects
section uses space poorly" — a deletion, not a redesign.
.milestone-group no rule at all; the card around each milestone
.ds-header only its h1 descendant survived
vue-tsc cannot see any of it. A dead style typechecks perfectly.
scripts/check_dangling_styles.py finds the shape: an element whose every static
class has no base rule anywhere, while at least one carries modifier rules. It
reports 11 more. Reported and not gated, because a genuinely bare wrapper is
legitimate — the signal is the count growing. Runs in the lint lane, stdlib
only, and knows no class name or convention (rule #115).
Also from the same report:
- The header pill bar was `position: absolute; left: 50%`, so it did not
participate in layout: out of room, it OVERLAPPED the brand and the utility
cluster instead of pushing them. A sixth link reached that at ~1270px, an
ordinary window. Now `1fr auto 1fr` — a 1fr track has an auto minimum, so
neither side can be squeezed under its content and the two stay equal, which
is what keeps the bar centred in the viewport rather than in the leftover
space. Overflow becomes the header growing, not two things sharing pixels.
- The token preview put its checkerboard on the whole specimen stage, so every
swatch sat in a frame of checks and the pattern read as the loudest thing on
the page. The checks now sit UNDER the colour as a second background layer:
an opaque value hides them, a 15% tint shows exactly as much as it should.
Text-bearing specimens lose the box entirely, and name/value/purpose are one
line each with the full text on hover — they wrapped freely before, so a card
was two lines tall or five depending on how long its color-mix() happened to
be, and the grid had no rhythm.
- .btn-cta joins the shared button family: the gradient-and-glow brand moment
the system carries tokens for, which had been living in one view's scoped
block. That is what made it deletable. The header actions are now one size
and one family instead of four sizes and two.
- The shared button shape gained inline-flex + gap, so a button carrying an
icon centres it without each caller rebuilding the row.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UaYUaouG9jjhATyuxCKrQs
|
||
|
|
8087ba4db0 |
feat(design): a project reports drift in its own recorded components
CI & Build / Python lint (push) Successful in 4s
CI & Build / Plugin hooks (push) Successful in 8s
CI & Build / integration (push) Successful in 21s
CI & Build / TypeScript typecheck (push) Successful in 35s
CI & Build / Python tests (push) Successful in 59s
CI & Build / Build & push image (push) Successful in 43s
The check has taken a project id since it was written — check_snippets_against_
system(user_id, design_system_id, project_id=0), and the route has always read
?project_id=. Nothing on the frontend ever passed one and no project-side
surface existed, so the capability shipped and stayed unreachable.
A Design tab on the project, beside Systems and Rules, reporting three things
per snippet:
no such token var(--x) the system doesn't declare. Renders as NOTHING —
no error, no failing test, just an element quietly unstyled.
Leads for that reason.
defines its own a component minting a custom property instead of reaching
for the shared one. This is the DRY finding and the reason
the surface exists: the codebase re-solving a solved
problem, one component at a time, visible only when someone
changes the shared value and half the components don't move.
write the token a literal the sheet says to stop writing, paired with what
to write instead.
Three empty states, kept distinct, because collapsing them is how a check comes
to sit dead: no design system bound, no snippets recorded (nothing was
checked), and checked-and-clean. The last one says how many were checked.
Bound to the SAVED pointer rather than the sidebar picker's draft, so an
unsaved change can't make the tab report against a system the project isn't
using.
Scope is recorded code, per the operator: snippets are what Scribe holds, and a
repository's own sources are checked where they live, by that project's CI.
Step 3 of milestone #274.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UaYUaouG9jjhATyuxCKrQs
|
||
|
|
7b0984579d |
feat(design): preview any design system, resolved from the record
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 8s
CI & Build / integration (push) Successful in 22s
CI & Build / TypeScript typecheck (push) Successful in 32s
CI & Build / Python tests (push) Successful in 53s
CI & Build / Build & push image (push) Successful in 44s
The record view listed values as text and drew a swatch only where the value looked like a colour. Two problems, one cause: a derived value such as color-mix(in srgb, var(--accent) 15%, transparent) was drawn by resolving --accent against THIS app, so previewing another project's system showed Scribe's palette. It looked right, which is why nobody noticed. TokenPreview draws the system from its own record. Every value is resolved on an offscreen probe carrying only that system's declarations, so a system whose app this browser has never loaded renders in its own colours — which is the difference between a tool and a mirror. Specimens are chosen by value SHAPE, never by name: colours become swatches, lengths become rules drawn to scale, gradients and shadows get a surface, font stacks are set in themselves. Nothing matches --fs-space-* or any other convention, because the convention belongs to the install (rule #115) — a system that calls its spacing --gap-N gets the same treatment. Translucent values sit on a checkerboard, or a 15% tint over a solid card reads as opaque and shows the wrong colour. Modes come from the system, not from the app: a system declaring base and light offers both, independent of the theme this page is in. The provenance list keeps its swatches only for self-contained colours — the ones needing no resolution, which it can therefore draw honestly. Everything with a var() inside is left to the preview built for it. Step 2 of milestone #274. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UaYUaouG9jjhATyuxCKrQs |
||
|
|
dcd4efcea0 |
refactor(design): retire /design — a surface that could only inspect itself
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 9s
CI & Build / integration (push) Successful in 29s
CI & Build / TypeScript typecheck (push) Successful in 33s
CI & Build / Python tests (push) Successful in 59s
CI & Build / Build & push image (push) Successful in 40s
The design surface is for the projects an install tracks. /design read the running app's own stylesheet — names out of a bundled theme.css, values out of getComputedStyle(document.documentElement) — so it could only ever describe the instance serving the page. Scribe is one project among the projects Scribe tracks; it gets no view hardcoded into every install. The mechanism that makes this a tool rather than a mirror already existed and already covers Scribe: scripts/check_design_tokens.py runs in CI against a sheet path it knows nothing about, using check_code_against_tokens — the same engine behind check_snippets_against_system. /design was redundant even here. Removed: DesignView, DesignTabs (nothing left to tab between), api/design.ts, routes/design.py and its blueprint, the /design route, ui_design_system() and its setting, and the Settings picker that designated "this app's UI". utils/designTokens.ts and utils/designDrift.ts go with it — between them they were the browser-reading half. What survives is utils/designValues.ts, which works on a record rather than a document: valueForMode, modesPresent, and resolveDeclared. resolveDeclared gained real isolation in the move. Custom properties inherit and `all: initial` does not reset them, so a probe sitting in this page would resolve any reference a record leaves undeclared against the SURROUNDING app's tokens — previewing another project's system would quietly borrow this one's palette wherever that system was incomplete, and a token already reported under unknown_refs would render as though it were fine. Undeclared references are now blanked on the probe first, so they resolve to nothing, which is what the record says they are. Migration 0075 absorbs ui_design_system_id alongside design_rulebook_id rather than an 0076 undoing it: 0075 has not run anywhere, since dev is unmerged and deploys come from main. Both keys named a design source for the running install, and a project already carries its own pointer. This retires the agreement panel shipped yesterday. It asked whether the sheet was actually loaded and applied — the one question a record cannot answer about itself — but only ever about the app you are already inside. Nothing replaces it; recorded in #2430 rather than quietly dropped. Step 1 of milestone #274. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UaYUaouG9jjhATyuxCKrQs |
||
|
|
4d2be27935 |
feat(nav): Design is primary navigation, not a utility icon
CI & Build / Plugin hooks (push) Successful in 12s
CI & Build / integration (push) Successful in 26s
CI & Build / TypeScript typecheck (push) Successful in 35s
CI & Build / Python tests (push) Successful in 1m1s
CI & Build / Python lint (push) Successful in 3s
CI & Build / Build & push image (push) Successful in 52s
It sat in the right-hand icon cluster with Trash and Settings, filed as a meta-surface. That was true when /design was a read-only gallery and false since: a design system is a record you author, with its own table, inheritance, sharing under the same ACL, and MCP tools. It is the content of the applications being built, which is the same rule that puts Snippets and Rulebooks in the bar. The pill bar is absolutely centred, so a sixth link doesn't push the brand and the utility cluster aside — it overlaps them. Added a 1150px breakpoint that drops the wordmark (the logo says the same thing and is still the link home) and tightens the link padding, rather than leaving Design out of the bar to avoid the collision. Mobile menu moves Design above the divider with the other content links, so both layouts sort it the same way. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UaYUaouG9jjhATyuxCKrQs |
||
|
|
c34454b840 |
refactor(theme): the accent was hand-written 16 times — now derived
CI & Build / Plugin hooks (push) Successful in 9s
CI & Build / integration (push) Successful in 29s
CI & Build / Python tests (push) Successful in 59s
CI & Build / Python lint (push) Successful in 4s
CI & Build / TypeScript typecheck (push) Successful in 33s
CI & Build / Build & push image (push) Successful in 43s
`rgba(91, 74, 138, …)` is Scribe's accent in decimal. It appears sixteen times across five files at ten different opacities, plus once as #5B4A8A. Change the accent in the design system and none of them would have moved — which is the precise failure the token system exists to prevent, hiding in a notation that does not look like a colour constant. Now `color-mix(in srgb, var(--color-primary) N%, transparent)`, so every one follows the accent. The design system already uses this form for its own tints (--fs-accent-soft, -faint, -wash), so this is the established idiom rather than a new one. The CI literal count barely moves (45 -> 44) because its regex matches #hex and fifteen of these were rgba(). Worth stating plainly: **the count was never the goal, and the check is blind to this whole class.** An rgba triple is a colour literal in every sense that matters and the report does not see it. Not touched: the badge palette in KnowledgeView (#7A6DA8, #fbbf24, #818cf8 for note/task/plan) and the remaining greys. Those are genuine unmade decisions — what colour IS a plan badge — not drift, and inventing tokens for them would be deciding by implementation. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UaYUaouG9jjhATyuxCKrQs |
||
|
|
bd60d679d9 |
refactor(theme): remove 184 var() fallbacks — every one was unreachable
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 16s
CI & Build / integration (push) Successful in 23s
CI & Build / TypeScript typecheck (push) Successful in 25s
CI & Build / Python tests (push) Successful in 57s
CI & Build / Build & push image (push) Successful in 43s
#2277 counted ~150 "raw colour literals bypassing the tokens". Measuring them told a different story: 184 sat in `var(--token, #fallback)` position, and a check against theme.css shows every one of those tokens IS declared. So the fallbacks could not render. Not drift — vestigial. They were also not this palette. The most common were Tailwind and Flat-UI defaults — #6366f1 indigo, #22c55e green, #f59e0b amber, #3b82f6 blue, #e74c3c and #27ae60 — a second, unsanctioned colour scheme sitting in the codebase looking like the app's colours to anyone reading it. Removing them is not tidying. #2319's lesson is that a fallback is WORSE than a missing token: a missing token renders as nothing and someone eventually notices, while a fallback renders something plausible forever. These 184 were one token rename away from silently repainting the app in Tailwind. The design token check would catch the rename — but the fallback is precisely the thing that would make it invisible if the check were ever bypassed. Literal count 152 -> 45, which matters beyond the number: a report that is mostly unreachable noise is one people stop reading, and then it stops working while still passing. What remains should be genuinely worth looking at. Done with a paren-aware transform, not a regex — `var(--x, rgba(0,0,0,.5))` nests parens and `[^)]+` would cut at the first one and leave `))` behind. Verified after: every changed line is a fallback strip and nothing else, and every var() reference still resolves. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UaYUaouG9jjhATyuxCKrQs |
||
|
|
174ec8af46 |
feat(design): the starter-role checklist, in the creation UI
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 9s
CI & Build / TypeScript typecheck (push) Successful in 25s
CI & Build / integration (push) Successful in 35s
CI & Build / Python tests (push) Successful in 1m2s
CI & Build / Build & push image (push) Successful in 44s
Rule #27 — the backend half shipped without a surface an operator can touch, so this is the other half of #2349. StarterRolePicker is a component rather than inline markup because DesignSystemsView has TWO creation forms: the empty state is a sibling branch of the body, not a parent, so a form written into one is unreachable from the other. Inlining the checklist would have made it the next thing in this codebase defined twice and free to drift — which is what the button migration spent nine commits undoing. What it offers is names and purposes, never values. "Named now, valued later": a role you haven't filled shows as to-be-decided, while a role that doesn't exist is what gets written as a literal instead. Every group unchecks individually, and the prefix is editable because `--fs-` is one family's convention, not the product's. Three deliberate details: - All groups checked by DEFAULT, and that default lives in the UI, not the service. create_design_system treats None and [] alike (seed nothing) so it can never write 40 rows into a system whose caller never asked; a UI default is visible and reversible before the click. Different layers, different safe answers. - A failed catalogue fetch is NOT fatal and does not read as an error. Starter roles are an accelerator, not a prerequisite — the form still creates, and the operator adds tokens by hand. - The refs are not cleared after a successful create. The picker owns them and re-seeds on mount; resetting here would race that and silently create the next system with no roles. props + defineEmits rather than defineModel, matching TagInput and the rest of components/. defineModel is available (Vue 3.5) and would be shorter, but being the only file in the codebase using a different binding idiom costs more than the lines it saves. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UaYUaouG9jjhATyuxCKrQs |
||
|
|
5f8b824523 |
refactor(ui): the remaining views migrate; 2 dead classes, 2 off-palette hovers
CI & Build / Python lint (push) Successful in 4s
CI & Build / Plugin hooks (push) Successful in 30s
CI & Build / TypeScript typecheck (push) Successful in 45s
CI & Build / integration (push) Successful in 1m50s
CI & Build / Python tests (push) Successful in 2m13s
CI & Build / Build & push image (push) Successful in 42s
Twelve more files onto the shared buttons. What the pass turned up: DEAD, verified not merely unnamed: - .btn-reconsolidate (TaskEditorView). A comment eleven hundred lines up in the same file says the feature was removed in Phase 8. The CSS outlived it. - .btn-remove-slot (SettingsView), style rules only, no template anywhere. OFF-PALETTE, the #2319 shape: TrashView's restore and purge hovers used `var(--color-primary, #6366f1)` and `var(--color-danger, #ef4444)` — Tailwind indigo and Tailwind red, from no palette in this system. The fallback is what renders if the token is ever absent, and it renders something plausible forever. Now the action and destructive colours, no fallback. A REAL BREAKAGE MY OWN CHECK COULD NOT SEE, worth recording. Deleting a rule whose selector was part of a comma-separated group left the leading selectors behind: .btn-log-edit, <nothing> .log-textarea { … } which silently swallows the next rule. Brace counting passed — there are no braces in a dangling fragment. Found by scanning for selector lines ending in `,` not followed by another selector; three instances across two files, one of them interleaved with comments so the first sweep missed it. The sweep is now part of the verification, not a one-off. Kept bespoke, deliberately: .btn-pin/.btn-unpin (pill-shaped history badges), .btn-add-share and .btn-new-note (gradient CTAs — brand moments, which the house style does sanction), .btn-icon/.btn-bell (icon buttons, a different component), .btn-add-system/.btn-add-milestone (dashed "add" affordances). These are not drift; they are other things wearing a btn- prefix. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UaYUaouG9jjhATyuxCKrQs |
||
|
|
c7cf07824a |
refactor(ui): the two workspace panels migrate onto the shared buttons
CI & Build / Python lint (push) Successful in 4s
CI & Build / Plugin hooks (push) Successful in 9s
CI & Build / TypeScript typecheck (push) Successful in 38s
CI & Build / Python tests (push) Canceled after 1m35s
CI & Build / integration (push) Canceled after 1m35s
CI & Build / Build & push image (push) Canceled after 0s
Eighteen bespoke button rules across the two densest components, replaced by
composition in the template. Net -279 lines.
These files are where the size taxonomy earns itself: almost every button here
is an inline affordance — a dismiss ×, a confirm tick, an add-chip — sitting
inside a card or a line of text. Forcing them to the standard 8/16px would have
broken the layouts, which is why the previous commit measured the clusters
before assuming a button is a button.
What the migration left behind is the useful signal. Each residual rule is now
one line stating only what the shared classes genuinely cannot:
.btn-add { font-size: 1rem; } a '+' glyph, not a label
.btn-search-clear { padding: 0; flex-shrink: 0; } sits in the field
.btn-suggest-tags { flex-shrink: 0; align-self: center; }
.btn-delete-task { margin-left: 0.25rem; }
Four residuals were deleted rather than kept, because the shared sheet already
said the same thing: a disabled opacity, two hover colours, and a danger-outline
hover fill. Keeping them would have recreated the drift in miniature.
Two accent hovers went with them. .btn-suggest-tags tinted its border and label
with the accent on hover, which is the same house-style violation corrected in
|
||
|
|
97b93bcaea |
refactor(ui): buttons stop using weights the system doesn't have
CI & Build / Python lint (push) Successful in 4s
CI & Build / Plugin hooks (push) Successful in 12s
CI & Build / integration (push) Successful in 21s
CI & Build / TypeScript typecheck (push) Successful in 35s
CI & Build / Python tests (push) Successful in 1m10s
CI & Build / Build & push image (push) Successful in 36s
Eleven buttons set font-weight 600. The house style permits exactly two weights, 400 and 500, and says so explicitly — 600 and 700 are not part of the system. Every auth Submit, plus Invite, Toggle, Confirm-delete, Add-share, the OAuth button and the assist Reject. Now var(--fs-weight-medium), which is 500. Buttons get very slightly lighter. Small on its own, but it is the third kind of drift the same five auth views have now produced: geometry that differed per file, an accent fill the style forbids, and a weight the system does not define. None of the three was a deliberate choice — each is what happens when a button is written by copying the nearest existing one. Weight declarations only. No geometry, no colour, no templates. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UaYUaouG9jjhATyuxCKrQs |
||
|
|
f491b6d7b9 |
refactor(ui): action buttons stop wearing the accent
CI & Build / Python lint (push) Successful in 4s
CI & Build / Plugin hooks (push) Successful in 7s
CI & Build / integration (push) Successful in 18s
CI & Build / TypeScript typecheck (push) Successful in 32s
CI & Build / Python tests (push) Successful in 46s
CI & Build / Build & push image (push) Successful in 47s
21 buttons filled with --color-primary, which resolves to Scribe's violet accent: every auth Submit (Login, Register, Invite, Forgot, Reset), plus Invite, Add, Confirm, Restore, Generate, Log-save, Subtask-confirm, Toggle-open, the modal primary, the version-restore, the inline-assist button, the milestone-plan actions, the task-advance hover, and both empty-state CTAs. The house style is explicit that the accent never appears on an action button: action colours are universal across the family precisely so a Save button looks identical in every app, while the accent carries identity. Doing both makes the accent mean two things and neither clearly. Operator's call, and the reasoning is worth keeping: the violet-on-Scribe- actions treatment was a deliberate early choice to give the web UI its own personality, made when much more of the app was user-facing. That is no longer true, so consistency is now worth more than the distinction it was buying. Found in two passes, which is the part worth noting. The first scan looked for `.btn-*` and found 13. Seven more were the same thing under different names — .modal-btn-primary, .vh-btn-restore, .inline-assist-btn, .empty-action, .task-advance-btn — plus .ms-plan-actions .btn-primary, a compound override flagged in the previous commit. Searching by naming convention finds what was named consistently, which is never the whole set. Deliberately NOT changed: progress-bar fills, active tab / page / selection states, tag-pill hover, the duration badge, the skip link, the assist pulse. Those are identity and active-state, which is exactly where the accent belongs. After this the accent appears only there, which is what makes it read as identity rather than as decoration. Colour swaps only — 25 lines changed, no geometry, no structure, no templates. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UaYUaouG9jjhATyuxCKrQs |
||
|
|
4c9a637507 |
fix(theme): text on a filled colour needs its own token — the old one inverts
CI & Build / Python lint (push) Successful in 4s
CI & Build / Plugin hooks (push) Successful in 13s
CI & Build / integration (push) Successful in 29s
CI & Build / TypeScript typecheck (push) Successful in 33s
CI & Build / Python tests (push) Successful in 55s
CI & Build / Build & push image (push) Successful in 41s
76 hardcoded `color: #fff` now resolve to --fs-text-on-action, a new token that is parchment in BOTH modes. The design system said they should supersede to --fs-text-primary, on the recorded reasoning that "there is no 'text on action' colour, there is just the text colour." That is true on dark and wrong on light. --fs-text-primary inverts to #14171A; the surfaces underneath it do not invert at all — every one of these 76 sits on an action colour, a semantic colour, the accent, or the CTA gradient, all of which hold a single value across modes. Sweeping as recorded would have put obsidian text on moss green: roughly 2.4:1, against a house style whose stated floor is WCAG AA. It would have looked correct to me, because I checked it in the mode where it was correct. --color-accent-fg had the same defect independently and is repointed too. The token check now reports zero superseded literals, down from 30 files, and raw colour literals drop 246 -> 169. Closes #2275. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UaYUaouG9jjhATyuxCKrQs |
||
|
|
8eef9e7845 |
fix(design-systems): the empty state's create button did nothing, and one surface
CI & Build / Python lint (push) Successful in 4s
CI & Build / Plugin hooks (push) Successful in 12s
CI & Build / integration (push) Successful in 28s
CI & Build / TypeScript typecheck (push) Successful in 33s
CI & Build / Python tests (push) Successful in 57s
CI & Build / Build & push image (push) Successful in 47s
Two reports, one root cause each. **The dead button.** "Create the first one" set `showCreate = true`, but the create form lived inside `<div v-else class="ds-body">` — the sibling branch of the empty state. The two are mutually exclusive, so on a fresh install the flag flipped and nothing rendered. The first action a new install can take was the one that didn't work, which is a poor way to honour "an install with zero design systems is the ordinary state". The first system now gets its own form outside the list layout, and it drops the parent picker entirely: there is nothing to inherit from yet, so it says so instead of offering an empty select. **Two surfaces, the wrong one first.** /design and /design-systems are halves of one thing — the record that decides the styling, and what the browser renders from it — and I had added them as two separate nav entries with the read-only diagnostic listed first. Backwards: the record is what you work with; the live view is the check on it. Now one nav entry pointing at the record, with a shared tab bar joining the two. The explorer is renamed "Live tokens", which is what it actually shows. The tab bar is a component rather than the same markup in both views. Two copies diverge the moment a third tab appears — and a design surface that ships duplicated markup would be arguing against itself. |
||
|
|
0937b1761e |
feat(design-systems): the editing surface — overrides, effective set, provenance
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 7s
CI & Build / TypeScript typecheck (push) Canceled after 15s
CI & Build / Python tests (push) Canceled after 15s
CI & Build / integration (push) Canceled after 15s
CI & Build / Build & push image (push) Canceled after 0s
Milestone #254 step 5 (#2294). /design-systems is the editable half of the surface /design already showed: that page is what the browser renders, this one is the record that ought to decide it. Each links to the other. The layout follows the model rather than decorating it. Two token lists, and they are deliberately different questions: Overrides — the system's own rows. Short by design, and EMPTY is the correct state for an app that hasn't departed from its family yet, so that empty state says so rather than looking unfinished. Effective — what it resolves to with inheritance applied, each row labelled with where its value came from. Provenance renders PER MODE when the modes disagree. A system can own `base` and inherit `dark` at once — that is the case the value column is a map for — and a single badge per row would have to lie about one of them. Rows whose modes agree (the common case) keep the single badge. "Defined here" and "overridden here" are distinct labels. Introducing a token and shadowing an ancestor's are different acts, and `is_overridden_in` is already false for the first. The parent picker filters out the selected system's descendants. The server refuses those anyway with a message naming the loop — but a refusal you cannot trigger beats a refusal explained well. Cycles that arrive some other way still render a truncated chain rather than freezing the tab: the client keeps the same defensive visited-set the server has. Three drift bugs caught while writing the styles, all of the shape this milestone exists to surface: - `--color-accent` does not exist. I had used it for every focus ring and active border; it would have rendered as nothing at all, silently. The brand token is `--color-primary`. - focus rings are ALREADY global in theme.css (`button:focus-visible` et al). My per-element rules would have overridden the house ring with a different one — the exact "bypassed abstraction" shape from #253. - every existing `.btn-primary` copy uses `color: #fff`, which is rule 52's prohibition and 67 live violations (#2275). This one uses Parchment and says why in a comment, rather than becoming the 68th. Also wires the project pointer into ProjectView's details panel, hidden entirely when no design systems exist (rule #115 — that is the ordinary state, not a degraded one) and saved through its own PUT, since clearing it is a real outcome rather than an omission. |
||
|
|
3c0192d749 |
feat(design-explorer): the gallery, including what isn't there
CI & Build / Plugin hooks (push) Successful in 7s
CI & Build / integration (push) Successful in 18s
CI & Build / TypeScript typecheck (push) Successful in 32s
CI & Build / Python lint (push) Successful in 2s
CI & Build / Python tests (push) Successful in 41s
CI & Build / Build & push image (push) Successful in 40s
Milestone #251 step 3 (#2260). New /design view, reachable from a Palette icon beside Trash and Settings — a meta-surface like /rules, so an icon rather than a sixth primary nav link, but not hidden either, since somewhere the design system is visible is the entire point. Renders three things, and refuses to render a fourth: - REAL components, imported not recreated: StatusBadge, PriorityBadge, TagPill. - REAL tokens, read at runtime via readTokens() so the page shows the live cascade rather than what the stylesheet claims. Grouped, swatched where the value is a colour, flagged where the token is mode-aware. - Rule 65's four button variants and rule 60's type scale, listed as SPEC and marked missing. That last part is the point of the step rather than a shortfall of it. Step 3's premise was "render the real components, not copies — a gallery of look-alikes drifts from the app within a month and then lies." Buttons have no shared implementation to import: .btn-primary is defined four separate times in four <style scoped> blocks, and 30 of 54 SFCs carry their own button CSS (#2273). Drawing a button here would have made this page the fifth copy — committing the exact drift the surface exists to catch. Same for the type scale: the three families load (rule 59) but rule 60's sizes and weights are not tokens, so there is nothing to read and a rendered specimen would be invented. So the gallery reports them as gaps. A design system nobody can point at is a design system that isn't there, and saying so is more useful than a page that looks complete. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UaYUaouG9jjhATyuxCKrQs |
||
|
|
d257c0fd67 |
feat(scribe): snippet management UI + REST routes; embed snippets on create
CI & Build / Python lint (push) Successful in 3s
CI & Build / integration (push) Successful in 23s
CI & Build / TypeScript typecheck (push) Successful in 33s
CI & Build / Python tests (push) Successful in 48s
CI & Build / Build & push image (push) Successful in 1m4s
Step 6 of the Drafter recall milestone (#227): a human-facing surface for the reusable-code snippets that agents record via MCP, plus the REST API behind it. Backend embeds snippets inline on create/update so they're recallable immediately, not only after a restart. Backend: - routes/snippets.py: GET/POST /api/snippets, GET/PATCH/DELETE /api/snippets/<id>. Share-aware per rule #78 (get_note_for_user + can_write_note), writes performed as the owner; list owner-scoped — mirrors routes/notes.py. Registered in app.py. - services/snippets.py: embed on create/update via a _embed_snippet fire-and-forget helper, covering BOTH the MCP tool and the REST route by construction. A snippet's value is immediate recall, so it can't wait for the startup-only backfill (see issue: MCP create path doesn't embed inline for notes/tasks generally). - tests/test_routes_snippets.py: structural registration + handler/service contract + PATCH-field ↔ update_snippet-kwarg parity (rule #33). Frontend (Vue 3 + TS): - api/snippets.ts: typed client, modeled on api/systems.ts. - views: SnippetListView (search, skeleton/empty/error states), SnippetDetailView (read + copy-to-clipboard, ConfirmDialog delete), SnippetEditorView (create/edit all fields, Ctrl/Cmd+S, Esc, autofocus, validation). v1 quality per rules #24/#27. - router: /snippets, /snippets/new, /snippets/:id, /snippets/:id/edit. - NoteType union widened to include 'snippet'; Snippets nav link added to AppHeader (desktop pill bar + mobile menu). - Design system: Moss --color-action-primary for action buttons, accent --color-primary reserved for tags/brand (Hybrid rule); focus rings; JetBrains Mono for code/name/signature/location. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pa2EsuB54BuWQ8GfJq9c7t |
||
|
|
12f71fabdf |
refactor(scribe): remove calendar + entity surfaces from web UI (frontend)
CI & Build / integration (push) Successful in 28s
CI & Build / Python lint (push) Successful in 4s
CI & Build / TypeScript typecheck (push) Successful in 33s
CI & Build / Python tests (push) Successful in 46s
CI & Build / Build & push image (push) Successful in 44s
Frontend half of the narrowing (milestone #194); matches backend
|
||
|
|
94d32c524a |
feat(issues): S4b frontend — open-issues lists + issue plumbing
- types/note.ts: Note gains systems? + arose_from_id?; TaskKind includes 'issue'. - stores/tasks.ts: create/update accept IssueFields (kind/system_ids/arose_from_id). - api/systems.ts: getProjectIssues + TaskLike. - DashboardView.vue: 'Open issues' rail section from dashboard.open_issues (links to /tasks/<id>, project + status). - SystemsSection.vue (project Systems tab): 'Open issues' list via getProjectIssues, with system chips, links to /tasks/<id>. Both reuse existing CSS tokens. Issue-editor controls (kind selector / system multi-select / arose-from picker in WorkspaceTaskPanel) are the remaining S4b piece. NEEDS operator browser verification (CI typechecks only). Refs plan 825 (S4b frontend — lists). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
4da29562bd |
feat(issues): S4a UI — Systems management section in project view
Frontend foundation for Issues + Systems (spec #825, S4a). - frontend/src/api/systems.ts: typed client (System + list/create/update/delete) over /api/projects/<id>/systems, matching the rulebooks api style. - frontend/src/stores/systems.ts: Pinia store keyed by project (fetch/create/ update/archive/unarchive/delete), toast-on-error. - frontend/src/components/SystemsSection.vue: a Systems management section — cards (color swatch, name, description, 'N open' issue-count badge) with inline create/edit, archive (hidden behind a 'show archived' toggle), and a delete-confirm modal. v1 quality: loading skeleton, empty state, error toasts, keyboard a11y, focus rings; reuses existing CSS tokens (no new colors). - ProjectView.vue: new 'Systems' tab (between Notes and Rules), rendering <SystemsSection :project-id>, wired like the existing rules tab. S4b (next) adds issue-editor controls (kind=issue/system multi-select/arose-from), open-issues lists, and the dashboard surface. NEEDS operator browser verification (CI typechecks but can't render). Refs plan 825 (S4a). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
70ab3f38c6 |
chore: remove pre-pivot dead code + finish Scribe rebrand (#599 t1-3)
- Header wordmark Fabled -> Scribe; fable:calendar-changed event -> scribe:calendar-changed; SettingsView CSS comment. - Drop dead Project.auto_summary + summary_updated_at columns (migration 0063) -- the Ollama-era summarizer is gone; model + 2 frontend types + projects test updated. - Remove pivot vestiges: diagnostics _curator_busy()/curator_busy heartbeat field, tz BRIEFING_DAY_START_HOUR/user_briefing_date dead aliases, the ignored 'model' param on get_embedding (+ its test). ruff src/ clean; CI is the gate. Part of scribe plan #599. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
65c85bab15 |
feat(dashboard): DashboardView landing + route + nav
Task 3 of #583. New DashboardView at /dashboard composes the approved layout: done-recently strip, Active-now project panels (project -> active milestones -> open tasks, in-progress flagged, + no-milestone group), and a rail with upcoming events / week stats / quick-create (Task/Note/Process). '/' now redirects to /dashboard; AppHeader gains a Dashboard link and relabels Knowledge -> Browse (route unchanged). Empty + loading states included. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
8b49ea896a |
fix(schedulers): wire recurring-task spawn + deliver event reminders
Drift-audit Group 2 (Phase-8 amputation — live wiring, no consumer): - Recurring tasks never recurred: spawn_recurring_tasks() had no caller. Register it as a 15-min interval job in the event scheduler (which app.py already starts/stops). Also add a deleted_at IS NULL guard to the spawn query in the same change, so a trashed recurring parent can never resurrect children once the sweep is live. - Event reminders were stamped reminder_sent_at but never delivered. _fire_reminders now creates an 'event_reminder' in-app notification before stamping, so a delivery failure stays retryable. Frontend NotificationsPanel renders the new type (⏰ + message); message logic pulled into a notifMessage() helper. - Remove the dead _fire_push_notif no-op stub (push left in Phase 8) and its three create_task call sites — no more throwaway tasks per share. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
7861607fb8 |
feat(rules): project rule + topic suppressions
Lets a project mute individual rules or whole topics from rulebooks it subscribes to, without unsubscribing the rulebook. Two new association tables (migration 0060), 4 MCP tools (suppress/unsuppress × rule/topic), 4 REST endpoints, and an inline "× skip" affordance plus collapsed "Suppressed (N)" section in the project's Rules tab. get_applicable_rules now emits suppressed_rules and suppressed_topics (detail objects with rulebook/topic context, not just IDs) so the UI can render the suppressed list without a follow-up lookup. The main rules projection grew topic_id and rulebook_id columns for the per-row suppress affordance. Project deletion cascades the suppression rows via hard DELETE — they are pure associations with no soft-delete column, and restoring a deleted project should start fresh, not inherit stale mutes. Project-scoped rules (Rule.project_id) are deliberately not suppressible — delete them with delete_rule instead. Implements plan-task #187. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
43a860c3ac |
feat(rules): project-scoped rules (S3)
Rules can now belong to either a rulebook topic OR a single project,
enforced by a CHECK constraint (exactly-one of topic_id/project_id).
Adds the create_project_rule MCP tool + REST endpoint, surfaces
project-scoped rules in get_project/get_task/start_planning under a
new project_rules field, and adds a project Rules tab section with an
inline create form so the operator can author project rules from the
UI without rulebook ceremony.
- migration 0059: rules.project_id (FK projects ON DELETE CASCADE),
topic_id now nullable, CHECK ck_rule_topic_xor_project, index on
project_id
- model: Rule gains project_id; to_dict exposes it
- service: create_project_rule with project-ownership guard; list_rules
with project_id filter UNIONs subscription-derived + project-scoped;
get_applicable_rules adds a project_rules field; get_rule / update_rule
/ delete_rule fetch via a shared _fetch_owned_rule that handles both
rulebook and project ownership paths
- trash: project delete cascades to project-scoped rules
- MCP: create_project_rule tool registered; _INSTRUCTIONS mentions both
create_rule and create_project_rule paths
- REST: POST /api/projects/<id>/rules (statement required, title derived
if omitted)
- frontend: Rule type gains nullable topic_id + project_id; createProjectRule
client; ProjectRulesTab.vue gains a "Project rules" section with inline
create form and per-rule expand/delete
- tests: register count → 18; create_project_rule unit tests (required
fields, title derivation, explicit-title pass-through); applicable_rules
shape tests now include project_rules; trash cascade test updated to
expect 5 executions
S1+S2 (always_on flag + Scribe-first prompt) shipped in
|
||
|
|
658348f208 |
feat(rules): always_on rulebook flag + Scribe-first prompt
Adds rulebooks.always_on (migration 0058) and a new list_always_on_rules MCP tool so a session-start eager pull can fetch standing rules without needing an active-project notion. Updates _INSTRUCTIONS so Claude calls the new tool at session start and codifies engineering rules in Scribe rather than CLAUDE.md / auto-memory. Seeds FabledSword family rulebook to always_on=true on migrate, matching its design role as the cross-project standards rulebook. Frontend: badge in RulebookListPane for always-on rulebooks; toggle in RulebookDetailPane header bound to a new toggleAlwaysOn store action. This is S1+S2 of the rules-consolidation plan (Scribe task #508). S3 (project-scoped rules) and S4 (enter_project handshake) follow. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
b579aa1c88 | feat(trash): TrashView page, nav links, and g+x shortcut | ||
|
|
75d3d40038 | feat(plan): plan-task viewer shows Applicable Rules panel | ||
|
|
9658e9a35c | feat(rulebook): Project Rules tab — applicable rules + subscription chips | ||
|
|
f2afb2a8bf | feat(rulebook): /rules route, nav entry, g+r shortcut | ||
|
|
447adf816c | feat(rulebook): subscription panel — toggle projects per rulebook | ||
|
|
75d8e7ab49 | feat(rulebook): RulesView three-pane shell + child panes + rule editor | ||
|
|
18eb1e7ab2 |
refactor(ui): Phase 7 — strip chat/voice/journal/workspace/home surfaces
Frontend deletion phase of the MCP-first pivot. All in-app
conversational surfaces are gone — Claude/MCP is the assistant now.
Deleted views:
ChatView, JournalView, WorkspaceView, HomeView
Deleted components:
ChatPanel, ChatInputBar, ChatMessage, ChatStreamingBubble,
ToolCallCard, ToolConfirmCard, WorkspaceChatWidget
Deleted composables + store:
useVoiceRecorder, useVoiceAudio, useStreamingTts, stores/chat
Router changes:
- / now redirects to /knowledge (was /journal)
- dropped /chat, /chat/:id, /journal, /workspace/:projectId
- /tasks still redirects to / (→ /knowledge)
- /notes still redirects to /knowledge
KnowledgeView:
- removed ChatPanel + ChatInputBar embeds
- removed minichat floating widget + state + handlers
- removed Chat link from today bar
- removed `chatStore` driven auto-refresh-on-tool-call watch
App.vue:
- removed useChatStore + startStatusPolling/stopStatusPolling
- removed VAD ONNX preloader (voice subsystem dead)
- removed visibilitychange listener (only did voice status re-check)
- removed `c` single-key shortcut (focus chat / goto chat)
- removed `g+c` two-key sequence (goto chat)
- removed Chat section from shortcuts overlay
- removed `.chat-page` / `.workspace-root` CSS overflow rule
AppHeader.vue:
- removed useChatStore + status indicator (Ollama model status)
- removed Chat / Journal nav links (desktop + mobile)
SettingsView.vue (4598 → 4079 lines):
- removed Voice tab entirely
- Notifications tab: dropped Push Notifications + Chat History
+ About sections (kept Email Notifications)
- General tab: dropped Assistant (name + model pickers) +
Model Management sections (kept Tasks + Timezone)
- Profile tab: dropped Journal + Observations sections
- VALID_TABS + tab list array no longer include "voice"
- removed `loadVoiceTab()` activation trigger
Service worker (frontend/public/sw.js):
- dropped push and notificationclick handlers (push subsystem
only fired on internal generation completion, which is gone)
- kept empty fetch handler as PWA installability shell
Script-level dead code (state refs, helper functions referencing
removed APIs) remains in SettingsView and stores/push.ts and
stores/settings.ts for now — Phase 8 backend deletion will clean
those up alongside the matching backend route removals.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
e6f2ee2b94 |
feat(frontend): pin/unpin/auto-pin UI in HistoryPanel
Version list rows now render a kind-aware badge: filled circle for manual pins (with the label inline), half-filled circle for auto-pinned versions. The right pane gains a control row above the diff: - Unpinned: 'Pin version' button → label input → Save creates a manual pin with that label. - Manual: 'Edit label' + 'Unpin' buttons. - Auto: 'Pin permanently' (promotes auto → manual with editable label). Local state is patched from the API response so the UI updates without reloading the panel. |
||
|
|
36cd08c236 |
feat(events): expose recurrence presets in EventSlideOver
Adds a "Repeat" select (None / Daily / Weekly / Monthly / Yearly) that reads/writes the existing Event.recurrence RRULE. CalDAV-imported rules with extra parts (e.g. FREQ=WEEKLY;BYDAY=MO,WE,FR) surface as a disabled "Custom" option with the raw rule shown read-only — visible but preserved unless the user explicitly picks a preset to replace it. EventUpdatePayload.recurrence is now string | null so we can clear via PATCH; backend service already treats null as "clear" (recurrence is in the nullable set in update_event). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
2db23cec7a |
refactor(events): turn EventSlideOver into a centered modal with auto-save
User feedback: the right-edge slide-over panel pinned its action buttons
to a thin floor band where the destructive ghost-style Delete button was
functionally invisible against the dark surface. Save / Cancel / Delete
all sat in the same floor strip, isolated from the form content.
This refactor changes the surface and the commit model.
## Centered modal, not slide-over
Backdrop dim covers the whole viewport; the panel sits centered with a
12px corner radius and a soft shadow. The form scrolls internally when
content overflows the viewport (max-height: calc(100vh - 2.5rem)).
File kept as `EventSlideOver.vue` to avoid touching the three consumers
(CalendarView, HomeView, ToolCallCard).
## Action buttons removed; close = save
- Save button: gone. Auto-save fires when the user closes via X, Esc,
backdrop click, or pressing Enter inside a text field.
- Cancel button: gone. Esc / X / backdrop click already cover dismiss;
a labeled "Cancel" was redundant.
- Delete button: moved to the header as a Trash2 icon (edit mode only).
Click → header swaps to inline confirm "Delete this event? [Yes,
delete] [No]" — same two-step flow, just relocated. Esc during the
confirm cancels back to edit mode rather than closing the modal,
giving the user a clear way out of the destructive prompt.
## Validity-aware close
All exit paths funnel through `attemptClose`:
- Form valid → save (POST or PATCH), then close.
- Form invalid in EDIT mode → discard the in-memory change and
close, with a toast naming the missing field
("Title required — change discarded"). Keeps the user from
silently corrupting an event.
- Form invalid in CREATE mode → close silently. Nothing was
committed; calling that out adds noise.
Emit signature unchanged (close / created / updated / deleted), so the
three consumers continue to work without edits.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
30dfbce426 |
fix(typography): strip chrome italic; reserve italic for emphasis
Per the design rule "italic is for emphasis, not for design", removed
every chrome `font-style: italic` declaration across the frontend.
42 declarations gone across 24 files: empty-state placeholder copy,
loading messages, "no results" hints, ghost text, voice/role labels,
field placeholder text, brand wordmark, et al.
Markdown content emphasis is unaffected — `<em>` and `<i>` tags from
`*emphasis*` markup still render italic via browser default styling
(prose.css doesn't override em behavior). User-typed emphasis in
notes, journal entries, and chat messages keeps its italic.
Specific spots that lost the decorative italic:
- KnowledgeView .filter-label, .empty-narrator
- NoteEditorView .ef-label, link-suggest related
- ChatPanel .empty-msg, .empty-greeting, .role-label
- ChatMessage .role-assistant .role-label (the "Fable" voice tag —
was italic per the doc's Illuminated Transcript spec, but per the
new typography rule the speaker tag stays regular and lets the
border-left + glow do the bubble framing on their own)
- AppHeader .brand-text ("Fabled" wordmark)
- editor-shared.css .title-input::placeholder
- ProjectView .project-title-input::placeholder
- HomeView .urgency-loading
- WorkspaceTaskPanel .empty-group
- WeatherCard .weather-unavailable
- SharedWithMeView .empty-msg
- DiffView empty-state spans
- ToolCallCard .tool-event-more
- SettingsView 7 spots (.you-label, .geo-pending, hint text, etc.)
- + several other empty-state / hint text spots
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
3f287d7417 |
fix(design): brighten status-dot indicator lights
The "ready" / "cold" / "unavailable" dots in the AppHeader status indicator were pulling --color-success / --color-warning / --color-danger which after the foundation pass became Moss / Warning gold-brown / Error terracotta — all visibly muted. The green-ready dot in particular read too dark to register as a "ready light", and the pulse glow (bright emerald) had nothing to glow off of. Status dots are indicator *lights*, not semantic-palette UI elements. Decouple them with hardcoded vital values: - ready: #4ade80 (matches the existing pulse glow) - cold: #facc15 - red: #ef4444 Orange already used a hardcoded bright #f97316; left as-is. The rest of the system continues to use --color-success / --color-warning / --color-danger semantically (toast-success, validation errors, etc.) — only the indicator-light contexts get the brighter palette. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
3c1ec4077f |
feat(design): surface phase PR 7 — edge surfaces polish
Per the spec, the final per-surface PR bundles the smaller / chrome-y / library-driven views (Header / Home / Calendar / Graph) together with EventSlideOver since it's the calendar's primary interaction surface. Button reclassification per Hybrid rule - CalendarView btn-new-event: accent gradient → Moss action-primary. Creating an event is a workflow action, not a brand moment. - EventSlideOver btn-primary (Save): accent gradient → Moss - EventSlideOver btn-secondary (Cancel/No): muted ghost → Bronze action-secondary - EventSlideOver btn-danger-ghost (Delete): hardcoded #ef4444 → --color-action-destructive ghost (with filled hover) - EventSlideOver btn-danger (confirm Delete): hardcoded #ef4444 → --color-action-destructive filled Brand-moment CTAs kept on accent - HomeView btn-workspace-hero: home-page hero CTA into the active project — central brand-moment surface, gradient stays - HomeView btn-workspace-sm: per-card workspace icon with the accent-tinted bg + hover-to-accent pattern, kept Two-weights-only - Snapped every font-weight 600/700 to 500 across the bundle (HomeView, GraphView, CalendarView, AppHeader, EventSlideOver). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
ff498ce1a4 |
feat(design): surface phase PR 5 — Project + Workspace polish
Per the surface-phase spec for the Project list, Project view, and
3-panel Workspace cluster:
Button reclassification per Hybrid rule
- ProjectView:
- btn-workspace ("Open Workspace"): kept on accent gradient — the
workspace is the project's central feature moment, a Scribe-
flavored action, brand moment per the doc.
- btn-share: muted-outline → Bronze action-secondary
- btn-danger-outline (Delete project): generic-border + danger-on-
hover → Oxblood outline + filled hover (proper destructive)
- btn-save-panel: accent gradient → Moss action-primary
- btn-ms-confirm (milestone confirm): flat accent → Moss
- btn-ms-cancel: ghost → Bronze action-secondary
- .modal-btn-danger: --color-danger → --color-action-destructive
- ProjectListView:
- btn-primary ("+ New Project"): flat accent → Moss action-primary.
Empty-state CTA (.empty-action) keeps accent — that *is* a
brand moment per Hybrid, "create your first project".
- WorkspaceTaskPanel + WorkspaceNoteEditor:
- btn-delete-task / btn-delete: hover --color-danger → Oxblood
- btn-delete-confirm / btn-confirm-delete: --color-danger pair →
--color-action-destructive pair, with filled hover for stronger
affordance
- btn-save (WorkspaceNoteEditor): flat accent → Moss
- WorkspaceChatWidget:
- History toggle: replaced unicode ▾ with Lucide ChevronDown
(PR 1 carry-over for emoji-as-icon)
Two-weights-only
- Snapped every font-weight 600/700 to 500 across the cluster.
Borders kept (per structural-not-decorative rule)
- Milestone group cards: standalone containers, keep
- Project cards in list: standalone containers, keep
- Task slide-over panel: drawer modal, keep
- Workspace 3-panel separators: major section dividers, keep
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
4192a64c0f |
feat(design): surface phase PR 3 — Chat surface polish
Per the surface-phase spec for the Chat cluster (ChatView, ChatPanel, ChatMessage, ChatInputBar, ToolCallCard): Long-form line-height - ChatMessage: assistant-bubble .message-content jumps from 1.55 to 1.7 — chat is a reading surface, not a snippet stream. User bubbles stay tighter. - JournalView: dropped its :deep(.role-assistant .message-content) override; ChatMessage handles it now and Journal inherits. ToolCallCard borders - Removed the outer 1px border. ToolCallCard always renders inside an assistant bubble; the bubble already contains it. Background tint differentiates without re-bordering. Per the structural-not-decorative rule. - Error state preserved as a 3px left-edge accent in --color-danger, mirroring the assistant bubble's own left-edge pattern. Button reclassification per Hybrid rule - ChatView .bulk-link "All"/"None": accent text → --color-text-secondary (these are tertiary list-control affordances, not brand moments) - ChatView .bulk-delete-btn: --color-danger (Error terracotta) → --color-action-destructive (Oxblood) per Hybrid; paired with a Trash2 icon since destructive should always be reinforced by an icon, not just color - ChatView .btn-delete-conv hover: same Error → Oxblood swap - .btn-new-conv stays accent (brand moment, correct already) - .btn-send stays accent gradient (primary brand moment, foundation) Two-weights-only - Snapped every font-weight: 600/700 to 500 across ChatView, ChatPanel, ChatMessage, ToolCallCard per the doc rule. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
93a3beb5d6 |
feat(design): surface phase PR 1 — Lucide icon migration + scale enforcement
Replaces every hand-inlined SVG in the chrome (60 across 15 files) with Lucide components. Snaps icon sizes to the doc's 16/24 scale — all icons in this pass land at 16. AppLogo wordmark stays as the legitimate "custom app icon" exception per the doc; GraphView's <svg> mount point for D3 stays as well. Replaces emoji-as-icons in clear button-affordance cases with their Lucide equivalents: ✕ → X (close/dismiss buttons across 8 files), ✓ → Check (ProjectView task-advance button), 🎤 → Mic (ChatPanel voice CTA), 📎 → Paperclip (ChatView context toggle), ↑ → ArrowUp (ChatInputBar Send), × → X (ChatPanel context-note remove), ☀/☾ → Sun/Moon (AppHeader theme toggle). Inline emoji punctuation in textual confirmation copy ("Saved ✓", "Created ✓", `done: "✓"` status maps, `'✓' : '📄'` interpolations) is left for surface-phase voice/tone touchups — replacing requires structural template changes and is best done per-component. Stroke weight stays at Lucide's default 2px; tightening to the doc's 1.5/1 is deferred per the surface-phase spec (option ii: drop-in + scale enforcement). Adds lucide-vue-next ^0.469.0 to frontend/package.json. Docker rebuild handles the install. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
7a9a8b7819 |
feat(design): foundation pass — dusty violet palette, warm parchment light, Inter + JetBrains Mono
Applies the FabledSword + Scribe iteration palette to theme.css end to end: indigo (#7c3aed) → dusty violet (#5B4A8A) accent, cool grey → Obsidian/Iron/Pewter dark surfaces, warm parchment (#F5F1E8) light mode, Inter body + JetBrains Mono code loaded alongside Fraunces, and neutral hairline scrollbars (chrome is structural, not branded). Adds the action token set (--color-action-primary Moss, --color-action-secondary Bronze, --color-action-destructive Oxblood, --color-action-ghost-border Pewter) but does not yet reclassify any buttons — surface-phase work. Buttons remain dusty-violet gradients in the meantime, by design. Removes deprecated --color-accent-warm; replaces concrete usages with --color-text-secondary (dates, flavor copy) or --color-warning (paused status). Sweeps hardcoded indigo literals in component scoped CSS so they don't bypass the token system. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
a3071a53cb |
feat(routing): land on Journal at root; move Knowledge to /knowledge
Routing changes: - / now redirects to /journal (Journal becomes the home view) - /knowledge becomes a real route pointing at KnowledgeView (was a redirect to /) - /notes redirect target updated from / to /knowledge (the Knowledge surface, which is where the notes-related dashboard lives) To revert (Knowledge as home): change the redirect target on the / route back to "/knowledge". Knowledge stays a real route either way, so the swap is a one-line edit. Nav: Knowledge link in AppHeader now points to /knowledge. The "active" check simplified to route.path === "/knowledge" since / is no longer Knowledge's home. The brand logo link stays at "/" — clicks still go "home", which is now Journal. Keyboard shortcuts in App.vue (Escape, g h, g t) still navigate to "/" and correctly land on Journal via the redirect — no change needed there. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
c9f2134ad4 |
feat(journal): conversational LLM-generated daily prep (replaces card)
The structured prep card was data-rich but voiceless. Replaced with an LLM-generated conversational opener — same shape the briefing's compilation slot had — that renders as a normal assistant chat bubble at the top of the day's conversation. Backend (services/journal_prep.py): - Renamed generate_daily_prep -> gather_daily_sections (still pure data fetching, no LLM); kept the old name as a backwards-compat alias. - New _generate_prep_prose: hands the gathered sections to generate_completion with a warm-conversational system prompt; returns prose. Falls back to a plain greeting if the LLM call fails or no model is configured. - ensure_daily_prep_message now persists the prep as role='assistant' with the prose as content. Structured sections stay on msg_metadata for provenance. Auto-upgrades legacy system-role preps in place on next call. Frontend: - Drop the <article class="daily-prep"> structured block from JournalView. The prep is now just the first chat bubble — picks up the existing Illuminated Transcript styling automatically. - Drop dayMessages / prepMessage / prepSections / asArray helpers — no longer needed. - ChatMessage hideMessage filter: comment refined to clarify it only catches LEGACY system-role prep rows. Current preps are assistant-role and render normally. Net effect: open /journal -> first thing you see is a warm assistant bubble that talks about your day -> input bar below to reply. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
cacfcac86a |
fix(journal): restore weather + events panels; hide daily-prep system msg in chat
The journal UI was over-stripped earlier — weather panel, current-conditions poll, and the upcoming-events sidebar were all dropped. Restored those (calls the new /api/journal/weather, /api/journal/weather/current, /api/journal/weather/refresh, /api/journal/weather/geocode endpoints). Also: the daily-prep system message was rendering as flat text inside ChatPanel because there's no .role-system bubble styling. Added a hideMessage guard in ChatMessage so daily-prep system messages don't render in the chat stream — they're already shown above as a structured prep card. News / RSS reactions / article-discuss are intentionally NOT in the journal (scoped out per user direction). The broader RSS infrastructure cleanup is a separate, larger task. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
873e70c7ea |
feat(journal): add JournalView UI with /journal route + nav link
Restores a working browser surface for the journal feature, which was left UI-less when Stage F of the original plan was deferred. JournalView is a fresh write (not a rename of BriefingView) — drops the briefing- specific weather panel, news cards, RSS reactions, article-discuss button, and setup wizard, since none of those have journal-backend equivalents. What it does: - Fetches /api/journal/today on mount; shows today's daily-prep card (rendered from msg_metadata.kind === 'daily_prep' sections) - Day picker via /api/journal/days lets you switch to past days - Refresh-prep button hits /api/journal/trigger-prep - Center is the existing ChatPanel pinned to today's journal conversation (so the chat input + SSE + tool-call cards inherit unchanged) - Right sidebar keeps the upcoming-events list (uses the generic /api/events endpoint, not a briefing-specific one) Also: - Replace the dead Briefing API client functions with Journal equivalents (getJournalConfig, saveJournalConfig, getJournalToday, getJournalDay, getJournalDays, triggerJournalPrep, list/update/deleteJournalMoment). - Remove NewsView.vue — it was orphaned (no route, no nav) and depended on the deleted /api/briefing/* endpoints. If a standalone news surface is wanted later it'll need to be rebuilt against new endpoints. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
7602bf2293 |
feat(briefing): hard-cut tear-down
Backend: - Delete briefing services (pipeline, scheduler, conversations, profile, tools) - Delete routes/briefing.py + remove blueprint registration - Move _get_temp_unit into services/weather.get_temp_unit (reads top-level temp_unit setting) - Rename briefing_preferences.py → rss_filtering.py (functions are RSS-specific) - Strip briefing scheduler hooks from app.py - Strip briefing scheduler call from routes/settings.py - Update test imports (test_rss_service, test_tz_helpers) Frontend: - Delete BriefingView, BriefingSetupWizard, BriefingToolStatusRow - Strip /briefing route + nav links (AppHeader, KnowledgeView) - Strip Settings → Briefing tab + state + functions + imports - Strip briefing-intermediate handling from ChatMessage - Hide /news route + nav links (NewsView depended on briefing endpoints; orphaned in tree) - Drop unused useSettingsStore from AppHeader The Android BriefingScreen lives in a separate repo and is not touched here. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |