Skip to content

feat(builder): wave 2, Ask AI keeps to the pick and says what it did - #925

Draft
mortondev wants to merge 11 commits into
feat/builder-7-wave1from
feat/builder-8-wave2
Draft

mortondev wants to merge 11 commits into
feat/builder-7-wave1from
feat/builder-8-wave2

Conversation

@mortondev

Copy link
Copy Markdown
Member

Wave 2 of the portal builder critique: Ask AI keeps to what you picked and says honestly what it did. Stacked on #924.

What changes

  • Changes stay within the pick (F01).
    • An AI change to element styles now lands as an override on the picked part, the page shown or the Home block. It's a site-wide style only when the pick is the whole site or every element of that kind.
    • Anything else is refused and listed in Show changes, not dropped silently.
    • The server enforces the reach, so a German request no longer lands site-wide. The English regexes are now hints only.
  • A reach line on the context chip (F01). It reads, for example, "Hero · every page (6)" or "Header and menu · Every page". Clicking it narrows the reach to this page or just this one, and that is sent as the request's within. When changes reach beyond the page shown, the reply ends ", on every page", and Show changes lists what else a change reaches again.
  • Website matching only for the whole site (F35). match_website is offered only when the scope is the whole site; scoped requests borrow with read_website.
  • A step that fails keeps what applied (F02). The reply says "…, but I stopped before checking everything." and gives the normal Undo. "Nothing changed" shows only when nothing did.
  • One-line replies (F30).
    • "Left alone", refusals and unmet checks move into Show changes.
    • Widen appears only when something outside the pick was actually left out.
    • The screenshots note that every brand match added is gone.
  • Stop (F29).
    • Send turns into Stop while a request runs; Escape in the composer also stops it.
    • Stopping aborts the client call, the server function (through the request signal) and the edit loop, and keeps what applied.
    • Your own edits made while the AI works are their own undo step.
  • Show changes rows in words (F31). One row per operation, a swatch only for real colours. This fixes the contradictory "Element styles: Automatic → Colour" rows, which came from a style object being read as words-by-language.
  • The canvas shows what changed (F32). A neutral grey outline flashes for about 1.5s on changed parts, shown without the fade for reduced motion. Each Show changes row gets a Show button that switches page, forces the state when needed and outlines the part.
  • Starters follow the pick and the page (F33). Up to 3 come from the picked part's examples, or from the page's sections when nothing is picked. Match my brand still leads until a match has run.

Notes

  • The chip's starting reach is each panel's default (every page for the hero, every element of that kind for an element). It doesn't yet follow a scope changed inside the panel; that's a follow-up.
  • A small race remains: a Stop that arrives after the server has saved but before the response can leave a revision mismatch on the next save. The server checks the signal just before saving, which narrows this but doesn't close it.

Testing

  • Typecheck and lint are clean.
  • 5,650 tests pass across the designer, appearance, design (shared, agent and domain), client, policy, AI and the design AI server functions. All model calls are mocked.
  • Regression tests reproduce both critique cases, in English and German. They fail on the old code.
  • Checked in a browser on a seeded database:
    • With the header picked, the chip reads "Every page".
    • A real request ("Make the header calmer", on Haiku) shows Stop. Stopping it replies "Stopped. Nothing changed."
    • No page errors.

The starters ignored the selection and the page, and the block registry's
example requests went unused. A picked hero, Home block or page section now
offers up to three of its own registry examples; with nothing picked the
starters keep to the page open (its sections' examples first, and Home-only
suggestions only on Home). Match my brand still leads until a match has run.
Show changes made a row per changed field and named each after the operation
that touched it, so one hero look read as "Tint: None → Tint" and "Tint: None →
Automatic". A style's { background: "#12893c" } was also read as words by
language, giving "Element styles: Automatic → Colour" with an empty swatch.

Rows now come one per operation, said as the operation is (codes stripped, a
style's property by its panel name), and changes no operation names still read
field by field. A swatch shows only beside an actual colour, never beside a role
or a colour worked out from another. Words-by-language detection now needs
every key to be a language.
…s undo step

A request could run several loop steps over about 24 seconds with no way to
stop it, and any change made by hand meanwhile was folded into the AI's open
undo group, so Undo took both back together.

While a request runs, Send becomes Stop (the same button, so focus stays) and
Escape in the composer stops too. Stop aborts the request: the signal reaches
the server functions and the model call (structuredChat now takes a signal),
the loop starts no further step, and a step stopped while the model answered
saves nothing. What already applied stays, with the usual Undo, and the reply
says "Stopped." with whether anything changed. Brand matches pass the signal
to their stream, which the route already honours.

The history now knows who opened a group. A change made by hand while the
AI's group is open closes the AI's step so far and is a step of its own; a
drag started meanwhile is one step of its own; what the AI changes after that
is one more step under its name.
…hip says

With the hero picked, "Make the main button darker when hovered" changed
every main button on the site: the hero's allowed element kinds included
button, and a style was checked by its kind alone, so it was written as a
site-wide token. How far a request reached was read from English word
patterns, so a German request landed site-wide, and nothing said so.

A request now carries `within` (one, page, every: ai-reach.ts), and the
server holds every change to it. A style is written for the whole site
only when the whole site, or every one of an element's kind, is the pick;
otherwise it lands as overrides on the pick's own parts (the hero's
buttons), the page shown or the Home block picked, or it is left out and
reported. Operations and overrides narrowed to one page or one part keep
to fields and parts drawn there. The word patterns stay as hints for the
model and the route.

The one-call reply is one sentence that ends with a reach clause when its
changes land beyond the page shown ("on every page"); what was left alone,
the language words were written in and what else a shared field changes
(alsoChanges, used again) go to notes for Show changes. Asking about the
whole site is offered only when the request names something outside the
pick. A scoped loop step never adopts a website match.
…eply in one line

A loop step that threw after an earlier step had applied left the draft
changed but showed "Something went wrong, so nothing changed" with no
Undo, as nothing caught the throw and the builder took it as a failure.
The loop now catches it, stops as failed with the merged changes, and the
reply says "..., but I stopped before checking everything." with the
usual Undo; it still throws when nothing changed.

The loop's reply is one sentence, ending with how far the changes reached
beyond the page shown. What did not come out as asked, what was refused
and what was left outside the pick (no longer dropped silently, and told
to the next step) are notes for Show changes. A website match says only
the match.

match_website runs only when the builder offers it, which it does for
the whole site alone; asked for anyway, the model is told to borrow with
read_website, so "make this button feel like stripe.com" cannot restyle
the site or spend a capture.
The chip said "Hero / On Home" while "Make this calmer" changed the hero
on every page. It now says how far a request reaches, starting where the
picked part's own panel starts ("Every page (6)" for the hero, "Hero ·
every main button" for an element in it, "On Home" for a Home block), and
a click takes the next reach (this page, just this one). The chosen reach
goes with the request as `within`, which the server holds every change to.

The builder offers match_website to the loop only for the whole site, and
a reply's notes (what was left alone, what else a change reaches) go to
the turn for Show changes rather than into the reply.
…ry match says

Every brand match added "Your screenshots do not show hover effects or
status labels, so those are as they were." whatever was matched, and a
placed hero photo added a second sentence to the reply. The always-on
note is gone; the photo to replace is a note for Show changes, beside the
ones that are specific to the match (a stage that failed, a font or logo
it could not use).
…gnal

The server functions now read the request's signal so Stop ends the model call;
the existing tests' mock of the server module had no getRequest.
…ach change from its row

After an AI step applied, nothing on the canvas said what had changed.

Each step the AI applies now flashes a neutral grey outline (never brand
coloured, edged in white) for about 1.5s on the parts it changed on the page
shown: the hero, a Home block, a page section, or each element a style
changed (a few of each), with a style changed in a state forced into that
state while outlined. With less motion asked for, the outline shows still
and goes, without fading. Changes the whole portal takes at once (the brand
colour, corners) have no one spot and are not outlined.

Each Show changes row the canvas can point at gets a Show button: it switches
to the page the change is on (staying on the page shown when it draws the
part), forces the hover or focus state for a state's change, outlines the
part, and says what it shows through the builder's status line.

change-spots.ts maps a change to where it shows, from the same operations the
AI applies; rows now carry the change they say, which Show uses. No server
field was added.
Stop now ends the edit loop through the same path that keeps a failed
step's changes: a step cut off by Stop, or a throw after Stop, stops with
what applied; the reach chip and the starters share one page prop.

This branch has not been deployed

No deployments
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