Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
f1c4d35
feat(ui): establish shadcn workspace system
robinbraemer Aug 11, 2026
f9ec499
feat(ui): adopt shadcn chat primitives
robinbraemer Aug 11, 2026
249bde1
feat(ui): adopt shadcn AI Elements
robinbraemer Aug 11, 2026
2da045c
perf(ui): defer reasoning primitives
robinbraemer Aug 11, 2026
4511688
perf(ui): preserve protected workspace budget
robinbraemer Aug 12, 2026
db0c8c0
docs: record AI Elements verification
robinbraemer Aug 12, 2026
d5d8f02
fix(ui): complete live handoff verification
robinbraemer Aug 12, 2026
134e8c2
feat(ui): open settings in the workspace
robinbraemer Aug 12, 2026
68301c4
fix(ui): clarify the starter workspace
robinbraemer Aug 12, 2026
6e1f1c5
fix(ui): protect recovery action contrast
robinbraemer Aug 12, 2026
08c024e
fix(desktop): restore native window dragging
robinbraemer Aug 12, 2026
275dbff
refactor(desktop): route window drag through Effect
robinbraemer Aug 12, 2026
99d7970
fix(shell): keep role workspaces disposable
robinbraemer Aug 12, 2026
fc4a1f9
feat(shell): present Flect as floating overlays
robinbraemer Aug 12, 2026
3e53a2a
feat(onboarding): clarify the first Flect prompt
robinbraemer Aug 13, 2026
c1913c8
fix(shell): center an untouched starter workspace
robinbraemer Aug 13, 2026
321d4de
fix(shell): preserve starter canvas beneath conversation
robinbraemer Aug 13, 2026
307a0a6
feat: add contextual canvas editing overlay
robinbraemer Aug 13, 2026
bb6b9f9
fix: complete contextual overlay quality gates
robinbraemer Aug 13, 2026
6e10e18
fix: remove protruding chat divider
robinbraemer Aug 13, 2026
b0d8655
test: stabilize throttled activation gate
robinbraemer Aug 13, 2026
10a5547
feat: refine agent conversation timeline
robinbraemer Aug 13, 2026
6698b9a
fix: keep model picker above the canvas
robinbraemer Aug 13, 2026
fcc98d6
fix: activate first native workspace reliably
robinbraemer Aug 13, 2026
428601d
fix: keep extension patterns browser compatible
robinbraemer Aug 13, 2026
23583e9
fix: keep submitted turns visible
robinbraemer Aug 13, 2026
a402a9f
fix: teach shaper the interface contract
robinbraemer Aug 13, 2026
82fdd97
test visible shaping responsiveness
robinbraemer Aug 13, 2026
fb20e26
refine agent chat work log
robinbraemer Aug 13, 2026
db06c19
feat: group chat history by agent turn
robinbraemer Aug 13, 2026
36c3729
test: stabilize shared runner quality gates
robinbraemer Aug 13, 2026
b8ca0af
Fix conversational shaping reliability
robinbraemer Aug 14, 2026
ed9a52b
feat: author real web apps from conversation
robinbraemer Aug 27, 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
76 changes: 76 additions & 0 deletions .agents/skills/flect-ui-state/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
---
name: flect-ui-state
description: Use this skill when building or changing any Flect UI part - React components, hooks, workspace state wiring, or reactive bridges between the Effect kernel and rendering. It owns the recommended practices for UI state in this repository, including the current SubscriptionRef-to-React bridge pattern and the adoption plan for Effect Atom.
---

# Flect UI state practices

Flect's UI is a thin rendering layer over an Effect application kernel. These
rules keep it that way.

## Non-negotiable rules

1. **Effect is the architecture.** Every workflow, data access, transport, and
long-lived state lives behind Effect services and Layers. Follow
`.agents/skills/effect-ts/SKILL.md` and the repository `AGENTS.md` before
this skill.
2. **React renders; it does not own application state.** Components may hold
ephemeral interaction state only (open/closed, focus, in-progress text).
Everything else is a projection of `FlectWorkspaceController` snapshots or
another Effect-owned `SubscriptionRef`.
3. **One state authority per concern.** Never mirror controller state into
React state, contexts, or module singletons. Subscribe and project.
4. **Event handlers are adapters.** A handler converts a DOM event into one
typed controller command (or one Effect run through the managed runtime)
and nothing else. No orchestration, retries, or fan-out in components.
5. **No direct platform imports in components.** `@tauri-apps/api/*`, storage,
and network enter only through named Effect capabilities and Layers.
6. **Budgets are part of the definition of done.** New UI must keep
`bun run check:bundle` green; large optional surfaces load lazily behind
the existing dynamic boundaries.

## Current bridge pattern (SubscriptionRef -> React)

Until the Effect Atom adoption below is possible, bridge Effect state into
React exactly the way `src/hooks/use-agent-session.ts` and `src/app.tsx` do:

- the kernel exposes `snapshot: Effect<...>` plus `changes: Stream<...>` from a
`SubscriptionRef`;
- one hook subscribes through the managed runtime, stores the latest snapshot
with `useState`, and cleans up via fiber interruption on unmount;
- derived controller objects are memoized from the snapshot; and
- commands go back through `controller.dispatch` / typed service methods,
never by mutating the snapshot.

Do not hand-roll additional subscription mechanisms, polling, or event
emitters beside this pattern.

## Effect Atom: direction and current blocker

[`@effect-atom/atom` and `@effect-atom/atom-react`](https://github.com/tim-smart/effect-atom)
provide exactly the missing piece between the Effect kernel and React:
reactive atoms that wrap Effects, Streams, and services, with `useAtomValue` /
`useAtomSet` hooks, built-in pending/error result states, and a registry that
composes with Layers at the runtime edge.

**Decision (2026-08-27):** adopt Effect Atom as the standard UI-state bridge
and make it the strict rule for new React state wiring **once it supports the
Effect major that Flect pins**. As of this writing `@effect-atom/atom-react`
0.7.0 peer-depends on `effect ^3.22.1` while Flect pins `effect
4.0.0-beta.102`, so adoption is blocked upstream. Do not add the dependency
while the peer range conflicts.

When the blocker clears:

1. add the exact-pinned packages beside the other pinned Effect packages;
2. build atoms over the existing `FlectWorkspaceController` snapshot stream
(one atom per bounded projection, not one giant atom);
3. replace the hand-rolled subscription code in `use-agent-session.ts` first,
behind the same exported controller interface, so components do not churn;
4. keep commands flowing through the controller - atoms are read projections
plus thin writable adapters, never a second state authority; and
5. update this skill to make Atom the mandatory pattern and retire the manual
bridge section above.

Until then, treat "should we use Atom here?" as answered: not yet, for the
compatibility reason above, and the manual bridge pattern is the rule.
1 change: 1 addition & 0 deletions .agents/skills/flect/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ Use the public `flect` command. Treat it as the authoritative command surface fo
| `flect product invoke <operation-id> [--input <json>]` | Invoke a registered product operation without raw HTTP access. | outside agent, App Agent |
| `flect permissions list\|revoke <decision-id>` | Inspect product permission lifecycle or revoke a visible decision; grants remain protected UI decisions. | outside agent, App Agent, Shaper |
| `flect interface inspect\|schema\|validate\|propose` | Inspect or propose interface documents inside Shaper's sandbox. | Shaper |
| `flect app validate\|propose <sandbox-dir> [--name <text>]` | Package authored web app source from Shaper's sandbox and propose it as the running canvas. | Shaper |
| `flect proposal accept\|reject` | Resolve the current validated preview as a protected user decision. | outside agent, App Agent |
| `flect revision list\|rollback` | Inspect revision state or request deterministic rollback. | outside agent, App Agent |
| `flect repository status` | Inspect canonical Git refs, isolation, and conflict state. | outside agent, App Agent, Shaper |
Expand Down
7 changes: 7 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ Before changing Flect:
`AGENTS.md` covering the files in scope.
- Read `.agents/skills/effect-ts/SKILL.md` and the relevant references it
routes to before changing application architecture or Effect code.
- Read `.agents/skills/flect-ui-state/SKILL.md` before adding or changing
React components, hooks, or any Effect-to-React state wiring.
- Run `bun run prepare` if `.repos/effect` is missing, then use that pinned
checkout as the primary Effect API reference.
- Inspect the worktree and preserve unrelated or unfinished work.
Expand Down Expand Up @@ -149,6 +151,11 @@ Repository-wide constraints:
- Keep platform behavior behind Effect services and Layers. The browser,
Tauri host, and macOS Swift code are adapters to shared application
capabilities, not alternate homes for product workflows or interface state.
- Every user-triggered native operation, including window chrome such as
dragging or resizing, must enter through a named Effect capability and its
provided runtime. React components and page scripts must not import or call
`@tauri-apps/api/*` directly; only the named platform Layer may adapt that
API. Tests provide that capability through a test Layer.
- Test observable behavior through exported contracts, HTTP requests, and the
rendered interface. Do not assert that source files contain selected text.
- Treat documentation as guidance, not proof that a boundary or lifecycle is
Expand Down
36 changes: 30 additions & 6 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,12 @@ model responses. The static view-only route fetches neither the Preact renderer
nor Effect. Compiler, package, shell, Worker, and Wasm implementations are
separate dynamic boundaries after workspace activation. Astro production uses
Preact compatibility for the protected components; the direct Vite SPA remains
a React fallback.
a React fallback. UI primitives follow the official Shadcn v4 composition
model with its Radix primitives and the official AI Elements registry. Radix
and Tailwind are workspace implementation details behind the Astro island
boundary; they do not
own workflows, persistence, or the static view-only route, and each added
primitive must preserve the browser budgets.

## Effect application kernel

Expand Down Expand Up @@ -161,9 +166,10 @@ runtime. Trusted browser and native clients schema-encode validated documents
before JSON transport. An `InterfaceDocument` contains no generated HTML, CSS,
JSX, or executable code path.

Shaper must terminate a proposal turn through its role-bound browser shell:
it writes `/workspace/interface.json`, runs `flect interface validate`, then
runs `flect interface propose` as its final action. The reserved command reads
Shaper must terminate a proposal turn through its role-bound browser shell
using exactly one of two reserved paths. For a schema interface it writes
`/workspace/interface.json`, runs `flect interface validate`, then runs
`flect interface propose` as its final action. The reserved command reads
only that disposable workspace, decodes unknown JSON through the closed
`InterfaceDocument` Effect Schema, and sends the validated value over the
bounded `AgentCommandBus`; the controller never receives a sandbox path.
Expand All @@ -173,6 +179,23 @@ one bounded corrective retry in the same Pi session. A second missing or
invalid proposal ends without creating a revision and leaves field-level tool
evidence in the activity UI and journal.

For an authored web app it writes complete self-contained source under
`/workspace/project`, runs `flect app validate`, then runs
`flect app propose` as its final action. The reserved Shaper-only command
packages that sandbox directory through the same bounded pre-capsule adapter
as project import, so path validation, secret and ignore rules, file and byte
limits, the single root `index.html` requirement, and capability findings all
apply before an archive can latch on the turn. The controller stages the
authored archive through the existing capsule pipeline: guarded
`flect/authoring` source checkpoint, portable compilation for framework
entrypoints, and the isolated compiled presentation. Because the authorship
trust domain equals a declarative proposal while the runtime confinement is
strictly stronger (the opaque-origin frame instead of the trusted shell DOM),
a review without blocked activation accepts automatically as one local Git
transition; a review with required ungranted capabilities falls back to the
explicit candidate ceremony. The rationale is recorded in
[`docs/decisions/0005-conversational-authored-apps.md`](docs/decisions/0005-conversational-authored-apps.md).

The `ShapingKernel` owns the active, proposed, previewed, superseded, accepted,
last-known-good, rejected, and recovered revision transitions. A Shaper result
is decoded as an unknown value and fully validated. A valid local conversation
Expand Down Expand Up @@ -216,8 +239,9 @@ Its primary session set contains three isolated agent sessions:
- **Shaper** receives the current validated document and a shaping instruction.
It has no ambient host resources. Its only Pi tool is custom `bash`, which
runs in a disposable browser workspace and returns through a typed
request/result bridge. The reserved `flect interface validate/propose`
commands are the terminating proposal path.
request/result bridge. The reserved `flect interface validate/propose` and
`flect app validate/propose` commands are the two terminating proposal
paths.
Configured external Pi extensions remain disabled unless the user explicitly
enables them for Shaper. A shaped document still returns as an untrusted
candidate for Flect to validate.
Expand Down
84 changes: 59 additions & 25 deletions DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ colors:
muted: "oklch(0.690 0.012 340)"
quiet: "oklch(0.500 0.012 340)"
line: "oklch(0.300 0.010 340)"
flect-rose: "oklch(0.630 0.180 340)"
flect-rose-hover: "oklch(0.690 0.170 340)"
primary: "oklch(0.922 0 0)"
primary-hover: "oklch(0.870 0 0)"
ready: "oklch(0.780 0.120 158)"
danger: "oklch(0.660 0.170 27)"
typography:
Expand Down Expand Up @@ -53,7 +53,7 @@ components:
rounded: "{rounded.pill}"
size: "40px"
button-primary-hover:
backgroundColor: "{colors.flect-rose-hover}"
backgroundColor: "{colors.primary-hover}"
textColor: "{colors.void}"
rounded: "{rounded.pill}"
size: "40px"
Expand All @@ -72,9 +72,9 @@ components:

Flect feels like working at a black anodized drafting desk after everything
unnecessary has been cleared away. The interface recedes so the person's intent
and the surface being shaped remain central. One restrained rose signal carries
identity; it appears as a precise indication of agency, never as ambient
decoration.
and the surface being shaped remain central. A neutral hierarchy carries the
interface; state is communicated with contrast, type, icons, and copy rather
than a decorative product accent.

The system is familiar enough to trust immediately and exact enough to feel
first-party. It rejects widget-dashboard density, chatbot sidecars,
Expand All @@ -83,12 +83,28 @@ affordances, short labels, and restrained state motion.

**Key Characteristics:**

- Near-black neutral architecture with one precise rose signal.
- Shadcn's neutral light and dark defaults, with no product accent color yet.
- System typography tuned for calm density and high legibility.
- Tonal layering before shadows; boundaries appear only when useful.
- One centered agent composer that expands naturally into a conversation rail.
- Protected recovery controls that remain quiet but always reachable.

### Component implementation

Flect vendors the official Shadcn v4 component source. The protected workspace
uses Shadcn's Radix primitives and the official AI Elements registry for the
conversation, messages, composer, reasoning, and tool activity. The initial
visual baseline is Shadcn's neutral default; product-specific styling is a
later, deliberate layer instead of a parallel component system.

Tailwind v4 compiles only the vendored workspace component sources. It does not
enter the static Astro activation shell, does not replace the Effect workflow
kernel, and does not authorize large registry-wide installs. Add primitives one
at a time, keep them behind the island or feature boundary that needs them, and
measure both the initial protected workspace and the on-demand chunk. A native
host control still wins whenever a WebView primitive fails the platform-native
quality contract.

## 2. Platform-native quality contract

Flect must feel like first-party software on every platform it claims to
Expand Down Expand Up @@ -174,15 +190,15 @@ keyboard behavior, or a design that only works at the demo viewport.

## 3. Colors

The palette is monochrome at rest and reveals color only when state or agency
needs to be communicated.
The palette is neutral by default. Color appears only for semantic success,
warning, or failure states.

### Primary

- **Flect Rose** (`oklch(0.630 0.180 340)`): focus, active agent state,
selected controls, and the rare brand signal.
- **Flect Rose Hover** (`oklch(0.690 0.170 340)`): interactive emphasis when a
primary action needs more presence.
- **Primary** (`oklch(0.922 0 0)` dark, `oklch(0.205 0 0)` light): actions,
focus, and selected controls using Shadcn's neutral defaults.
- **Primary Hover**: a neighboring neutral tone that preserves contrast without
introducing a product accent.

### Secondary

Expand All @@ -207,8 +223,8 @@ needs to be communicated.

### Named Rules

**The One Signal Rule.** Flect Rose occupies less than ten percent of a screen.
Its rarity makes agency unmistakable.
**The Semantic Color Rule.** Non-neutral color must communicate a named state
and must never be the only carrier of meaning.

## 4. Typography

Expand Down Expand Up @@ -260,17 +276,18 @@ to communicate focus, movement, or temporary hierarchy.
### Buttons

- **Shape:** circular for icon-only controls; 8px for labeled controls.
- **Primary:** Ink fill with Void content, reversing to Flect Rose when active.
- **Hover / Focus:** 180ms tonal transition and a visible 2px Flect Rose focus
- **Primary:** Ink fill with Void content, using the neighboring neutral when
active.
- **Hover / Focus:** 180ms tonal transition and a visible 2px primary focus
ring with offset.
- **Ghost:** transparent at rest, Raised Surface on hover, never a faint outline
box.

### Chips

- **Style:** tonal Surface or Raised Surface background with Ink or Muted text.
- **State:** selected chips use a low-chroma rose tint plus an explicit icon or
label change.
- **State:** selected chips use neutral contrast plus an explicit icon or label
change.

### Cards / Containers

Expand All @@ -285,8 +302,8 @@ to communicate focus, movement, or temporary hierarchy.

- **Style:** a 16px prompt surface without a resting outline. The text area and
its action rail read as one instrument.
- **Focus:** Flect Rose appears as a precise inner seam; Prompt Lift signals the
active surface.
- **Focus:** Primary contrast appears as a precise inner seam; Prompt Lift
signals the active surface.
- **Error / Disabled:** errors use Failure Red plus direct recovery copy;
disabled controls retain readable text and expose their reason.

Expand Down Expand Up @@ -356,19 +373,36 @@ existing Surface, Raised Surface, Line, Ink, and Muted tokens; code is
`0.875rem` mono and table content is `0.8125rem`. Their own viewports own
horizontal overflow, while copy, wrap, and expand actions stay dense on
desktop and reach `44px` at compact widths. Details remain native disclosures,
links use Flect Rose only as an interaction cue, and footnotes remain subdued.
links use underline and neutral contrast as interaction cues, and footnotes
remain subdued.
The complete rendering and trust contract lives in
[`docs/superpowers/specs/2026-07-31-flect-chat-markdown-design.md`](docs/superpowers/specs/2026-07-31-flect-chat-markdown-design.md).

### Activity, Follow, and Diagnostics

Tool use is a compact instrument in the conversation timeline, not a generic assistant
sentence and not a developer-console dump. A card always names the tool and
shows queued, running, completed, or failed state. Duration stays visible;
sentence and not a developer-console dump. An expanded row names the action
and shows queued, running, completed, or failed state. Duration stays visible;
bounded commands, output, exit status, preview links, validation paths, and
operation identifiers live in a native disclosure. Ready Mint and Failure Red
support the label but never carry meaning alone.

Completed work is collapsed by default into one quiet turn-level disclosure
such as “Worked for 1.4 s.” The final assistant message remains visible. A
running turn uses the same line-level treatment, and individual commands appear
as borderless rows only after disclosure; command output uses a subtle inset
rule rather than nested cards. Failed work that ends the turn opens for
attention. Raw tool names, repeated completion badges, and one duration column
per command must not dominate the ordinary conversation.

Every submitted request, its tool activity, and its final response share one
typed turn identity. Once a newer turn begins, the older request and work fold
behind one quiet `Asked …` line while that turn's final assistant response
remains visible. Expanding the line restores the complete request and work in
place. Historical user prompts must not remain as a stack of dominant cards,
and legacy records without turn identity remain readable rather than being
guessed into the wrong turn.

Conversation follow respects the reader. Content follows while the viewport is
within 48px of its bottom. Once the person scrolls away, streaming and tool
updates preserve that position and surface a quiet, keyboard-operable **Jump
Expand All @@ -386,7 +420,7 @@ ordinary product surface into infrastructure chrome.
### Do:

- **Do** keep the person's work and current prompt visually central.
- **Do** reserve Flect Rose for focus, selection, and active shaping.
- **Do** use neutral contrast for focus, selection, and active shaping.
- **Do** use known platform affordances with visible keyboard focus.
- **Do** use host-native surfaces when the WebView cannot meet the platform
contract.
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,12 @@ until that stylesheet has loaded and the island is ready. Compiler, package,
shell, Worker, and Wasm substrates remain on demand until a typed operation
needs them. Production Astro uses Preact's React compatibility renderer for the
existing workspace components; the direct Vite SPA remains a React fallback.
Official Shadcn v4 components, its Radix primitives, and the official AI
Elements conversation, message, composer, reasoning, and tool components
compile inside that deferred workspace stylesheet/chunk only.
The product intentionally starts from Shadcn's neutral defaults; Flect-specific
styling comes later, without forking interaction behavior. The static Astro
route carries none of the component runtime.
The measured production gates and exact budgets are documented below.

Flect remains under active development. The published v0.2.0 macOS preview is
Expand Down
Loading
Loading