From 00b4f01d2839dc55344e76c95bd634e306b77b85 Mon Sep 17 00:00:00 2001
From: Tim Disney
Date: Mon, 27 Jul 2026 17:06:19 -0700
Subject: [PATCH] ask bar
---
DESIGN.md | 16 ++++++
packages/ui/src/app.css | 12 +++++
.../ui/src/lib/components/RequestBar.svelte | 52 +++++++++++++++++++
.../ui/src/routes/g/[did]/[rkey]/+page.svelte | 8 ++-
.../routes/p/[project]/system/+page.svelte | 12 ++++-
5 files changed, 98 insertions(+), 2 deletions(-)
create mode 100644 packages/ui/src/lib/components/RequestBar.svelte
diff --git a/DESIGN.md b/DESIGN.md
index 4b633cd..2a4d86c 100644
--- a/DESIGN.md
+++ b/DESIGN.md
@@ -398,6 +398,22 @@ rather than inert". It was coherent and it cost the product its one authored rec
Logbook, on `/space`, there was no ⊕ and so no way to write down the thing you had just thought of. The
goal is now the constant and the list's own item is the variable.
+### The ask bar
+A label — **Ask an agent for** — and one default button per registry type of the page's own scope, each
+with the type's 15px glyph in `stroke` that goes accent on hover, sitting under the title where the
+compose card opens: goal-scoped types on a goal, project-scoped ones on System. It is the ⊕'s request
+section as a control in the page, generated from the same registry records, and it exists because those
+two surfaces were the only ways in: a floating disc in the corner, and a button in a drawer under an
+artifact that already landed — which is no help at all on the page that has none, the one place where
+asking is the whole of what there is to do. A human initiating is the first move of the product's loop,
+so it is a thing you can see rather than a menu you have to find.
+
+Nothing in it is primary and nothing is ordered by anything but the registry's own alphabet: which type
+to ask for next is the human's judgement, and an accent-filled "Request plan" would be the UI making it
+for them. It stays put while its card is open — that is where Escape hands focus back, and it is how the
+reader changes their mind about which type they are asking for — which is also the difference between it
+and a ⊕ menu item, since a menu is a surface and this is a place.
+
### The keyboard map
`/` puts the caret in quick find, `n` writes a goal from anywhere — in the project the view is of, where
it is of one — `⌘N` is the ⊕ of the view being shown, Escape closes one layer per press — and `?` draws
diff --git a/packages/ui/src/app.css b/packages/ui/src/app.css
index 022e786..1a19369 100644
--- a/packages/ui/src/app.css
+++ b/packages/ui/src/app.css
@@ -543,6 +543,18 @@ a.btn { text-decoration: none; color: var(--ink); display: inline-block; }
.btn-bad:hover { background: var(--bad-wash); border-color: var(--bad); }
.acts .gap { width: 1px; height: 20px; background: var(--line); margin: 0 3px; }
+/* ─── the ask bar ────────────────────────────────────────────────────────── */
+/* The registry's request buttons, standing on the goal they are asked against (`RequestBar.svelte`).
+ A plain row of controls with no box, no rule and no fill: the ask is the reader's next move, not a
+ call to action shouted at them, and one accent-filled button here would be the UI ranking the
+ registry's types on their behalf. It occupies the compose card's own slot, so pressing a button
+ swaps the row for the card in place rather than moving the page under the reader. */
+.askbar { display: flex; flex-wrap: wrap; align-items: center; gap: 7px; margin: 2px 0 16px; }
+.askbar .asklead { font-size: 12px; font-weight: 600; color: var(--ink-2); margin-right: 1px; }
+.askbtn { display: inline-flex; align-items: center; gap: 7px; }
+.askbtn .g { display: grid; place-items: center; color: var(--stroke); }
+.askbtn:hover .g, .askbtn:focus-visible .g { color: var(--accent); }
+
.softwarn {
display: flex; align-items: flex-start; gap: 8px;
font-size: 12.5px; line-height: 1.45; color: var(--warn);
diff --git a/packages/ui/src/lib/components/RequestBar.svelte b/packages/ui/src/lib/components/RequestBar.svelte
new file mode 100644
index 0000000..bb18a61
--- /dev/null
+++ b/packages/ui/src/lib/components/RequestBar.svelte
@@ -0,0 +1,52 @@
+
+
+
+{#if offered.length > 0}
+
+ Ask an agent for
+ {#each offered as { name, value } (name)}
+
+ {/each}
+
+{/if}
diff --git a/packages/ui/src/routes/g/[did]/[rkey]/+page.svelte b/packages/ui/src/routes/g/[did]/[rkey]/+page.svelte
index 5f26bab..0fad18d 100644
--- a/packages/ui/src/routes/g/[did]/[rkey]/+page.svelte
+++ b/packages/ui/src/routes/g/[did]/[rkey]/+page.svelte
@@ -7,6 +7,7 @@
import GoalActions from '$lib/components/GoalActions.svelte'
import NewRequest from '$lib/components/NewRequest.svelte'
import Pie from '$lib/components/Pie.svelte'
+ import RequestBar from '$lib/components/RequestBar.svelte'
import Thread from '$lib/components/Thread.svelte'
import UnitRow from '$lib/components/UnitRow.svelte'
import UriChip from '$lib/components/UriChip.svelte'
@@ -88,6 +89,12 @@
{/if}
+
+
+
{#if drafting}
@@ -99,7 +106,6 @@
{#if units.length === 0}
Nothing requested against this goal yet.
-
Press ⊕ to ask an agent for a plan.
{:else}
{#each days as day (day.key)}
diff --git a/packages/ui/src/routes/p/[project]/system/+page.svelte b/packages/ui/src/routes/p/[project]/system/+page.svelte
index abeb8bf..77b87e3 100644
--- a/packages/ui/src/routes/p/[project]/system/+page.svelte
+++ b/packages/ui/src/routes/p/[project]/system/+page.svelte
@@ -4,6 +4,7 @@
import Badge from '$lib/components/Badge.svelte'
import Glyph from '$lib/components/Glyph.svelte'
import NewRequest from '$lib/components/NewRequest.svelte'
+ import RequestBar from '$lib/components/RequestBar.svelte'
import UnitRow from '$lib/components/UnitRow.svelte'
import { draftFor } from '$lib/compose.svelte.js'
import { currentSpace } from '$lib/session.svelte.js'
@@ -67,6 +68,10 @@