Send pick-any checks with Other text; keep inbox focus across updates
This commit is contained in:
+1
-1
@@ -813,7 +813,7 @@ components:
|
||||
type: array
|
||||
items: { type: string }
|
||||
description: "option: the chosen options; exactly one for a pick-one question, one or more for a pick-any question."
|
||||
text: { type: string, description: "other: the text sent to the agent." }
|
||||
text: { type: string, description: "other: the text sent to the agent. option on a pick-any question: optional text sent along with the checked options." }
|
||||
attachments:
|
||||
type: array
|
||||
items: { $ref: "#/components/schemas/Attachment" }
|
||||
|
||||
+2
-2
@@ -110,7 +110,7 @@ Agents ask questions with the `ask_human` MCP tool. Each question carries a `con
|
||||
|
||||
| type | allowed on | item becomes |
|
||||
|---|---|---|
|
||||
| `option` (the chosen choices' text in `options`: exactly one for pick-one, one or more for pick-any) | questions | `answered` |
|
||||
| `option` (the chosen choices' text in `options`: exactly one for pick-one, one or more for pick-any; a pick-any one may also carry `text`, delivered after the checked list) | questions | `answered` |
|
||||
| `other` (free text in `text`: an answer, a question back, or new instructions) | questions, errors | `answered` |
|
||||
| `dismiss` | reports, errors | `dismissed` |
|
||||
|
||||
@@ -126,7 +126,7 @@ New inbox items are sent as web push notifications to every browser a user has t
|
||||
|
||||
The **Settings** tab turns notifications on or off for the current browser (asking for permission on the way) and edits the preferences. Opening it re-registers the browser's existing subscription, which restores one byre deleted as expired.
|
||||
|
||||
In the UI each card is answered on its own; there is no batch submission. A pick-one question's choices are clickable blocks, and clicking one sends that answer at once. A pick-any question's choices are checkboxes, sent with the Submit button at the bottom of the card. Below the choices, an "Other…" block in the same style opens the only text field. A card has at most one Submit button: on a pick-any question it sends the Other text if any, otherwise the checked choices, and opening Other clears the checkboxes while checking one closes Other. A report has only an OK button, which dismisses it at once; an error has a "Reply…" block (`other`) and a Dismiss button. The Other and Reply text boxes send with the Submit button under them or on Enter (Shift+Enter for a newline), like the message box, and fold back into their block when left empty.
|
||||
In the UI each card is answered on its own; there is no batch submission. A pick-one question's choices are clickable blocks, and clicking one sends that answer at once. A pick-any question's choices are checkboxes, sent with the Submit button at the bottom of the card. Below the choices, an "Other…" block in the same style opens the only text field. A card has at most one Submit button: on a pick-any question it sends the checked choices with the Other text alongside (one `option` response), or the Other text alone as `other` when nothing is checked. A report has only an OK button, which dismisses it at once; an error has a "Reply…" block (`other`) and a Dismiss button. The Other and Reply text boxes send with the Submit button under them or on Enter (Shift+Enter for a newline), like the message box, and fold back into their block when left empty. The list updates in place: a card stays attached while its item is unchanged, so task and inbox updates from other agents don't pull focus out of a text box being typed in.
|
||||
|
||||
There is no snoozing. The `snoozed_until` and `snooze_until` columns are still in the schema, always empty, so builds from older branches keep working against the same database; nothing reads them.
|
||||
|
||||
|
||||
@@ -372,6 +372,9 @@ func deliveryMessage(pending []store.Pending) string {
|
||||
for _, o := range *r.Options {
|
||||
fmt.Fprintf(b, "- %s\n", o)
|
||||
}
|
||||
if r.Text != nil && strings.TrimSpace(*r.Text) != "" {
|
||||
fmt.Fprintf(b, "%s added: %s\n", r.User, *r.Text)
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintf(b, "%s chose: %s\n", r.User, (*r.Options)[0])
|
||||
}
|
||||
|
||||
@@ -219,7 +219,7 @@ func TestDeliveryMessage(t *testing.T) {
|
||||
|
||||
msg := deliveryMessage([]store.Pending{
|
||||
{Item: question, Response: api.InboxResponse{User: "alice", Type: api.Option, Options: opts("blue")}},
|
||||
{Item: checklist, Response: api.InboxResponse{User: "alice", Type: api.Option, Options: opts("tests", "docs")}},
|
||||
{Item: checklist, Response: api.InboxResponse{User: "alice", Type: api.Option, Options: opts("tests", "docs"), Text: str("also run go vet")}},
|
||||
{Item: question, Response: api.InboxResponse{User: "bob", Type: api.Other, Text: str("what is it for?"),
|
||||
Attachments: []api.Attachment{{Kind: api.AttachmentKindUrl, Url: str("https://example.com")}}}},
|
||||
{Item: failure, Response: api.InboxResponse{User: "alice", Type: api.Other, Text: str("stop and write tests")}},
|
||||
@@ -228,7 +228,7 @@ func TestDeliveryMessage(t *testing.T) {
|
||||
for _, want := range []string{
|
||||
"Your question q1: Which color?",
|
||||
"alice chose: blue",
|
||||
"Your question q2: Which chores?\nalice checked:\n- tests\n- docs\n",
|
||||
"Your question q2: Which chores?\nalice checked:\n- tests\n- docs\nalice added: also run go vet\n",
|
||||
"bob answered (none of your options): what is it for?",
|
||||
`calling ask_human with thread_id "q1"`,
|
||||
`"url":"https://example.com"`,
|
||||
|
||||
+74
-73
@@ -490,7 +490,7 @@ type InboxResponseCreate struct {
|
||||
// Options option: the chosen options; exactly one for a pick-one question, one or more for a pick-any question.
|
||||
Options *[]string `json:"options,omitempty"`
|
||||
|
||||
// Text other: the text sent to the agent.
|
||||
// Text other: the text sent to the agent. option on a pick-any question: optional text sent along with the checked options.
|
||||
Text *string `json:"text,omitempty"`
|
||||
Type InboxResponseType `json:"type"`
|
||||
}
|
||||
@@ -2024,78 +2024,79 @@ var swaggerSpec = []string{
|
||||
"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",
|
||||
"m/8NAAD//w==",
|
||||
}
|
||||
|
||||
// decodeSpec returns the embedded OpenAPI spec as raw JSON bytes,
|
||||
|
||||
@@ -167,8 +167,8 @@ func checkResponse(item api.InboxItem, c api.InboxResponseCreate) (api.InboxStat
|
||||
}
|
||||
seen[o] = true
|
||||
}
|
||||
if text != "" {
|
||||
return bad("option takes no text; use other")
|
||||
if text != "" && item.Payload["multiple"] != true {
|
||||
return bad("a pick-one option takes no text; use other")
|
||||
}
|
||||
return api.Answered, nil
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ func TestCheckResponse(t *testing.T) {
|
||||
{"two picks on pick-one", question, api.InboxResponseCreate{Type: api.Option, Options: opts("red", "blue")}, "", http.StatusBadRequest},
|
||||
{"checked", checklist, api.InboxResponseCreate{Type: api.Option, Options: opts("tests", "lint")}, api.Answered, 0},
|
||||
{"checked one", checklist, api.InboxResponseCreate{Type: api.Option, Options: opts("docs")}, api.Answered, 0},
|
||||
{"checked with text", checklist, api.InboxResponseCreate{Type: api.Option, Options: opts("tests", "lint"), Text: str("also run go vet")}, api.Answered, 0},
|
||||
{"checked twice", checklist, api.InboxResponseCreate{Type: api.Option, Options: opts("docs", "docs")}, "", http.StatusBadRequest},
|
||||
{"checked unknown", checklist, api.InboxResponseCreate{Type: api.Option, Options: opts("docs", "deploy")}, "", http.StatusBadRequest},
|
||||
{"other", question, api.InboxResponseCreate{Type: api.Other, Text: str("green")}, api.Answered, 0},
|
||||
|
||||
Vendored
+47
-23
@@ -10,6 +10,8 @@ export function mountInbox(root) {
|
||||
const tabs = h("nav", { class: "scope-tabs" });
|
||||
const list = h("div", { class: "inbox-list" });
|
||||
const cards = new Map();
|
||||
const groups = new Map();
|
||||
const empty = h("div", { class: "empty" }, "Nothing waiting on you.");
|
||||
const visible = () => {
|
||||
const task = state.selectedTaskId ? findTask(state.selectedTaskId) : undefined;
|
||||
if (scope === "task" && task) {
|
||||
@@ -61,12 +63,28 @@ export function mountInbox(root) {
|
||||
cards.set(item.id, { sig, el });
|
||||
return el;
|
||||
};
|
||||
const els = groupByTask(items).map((g) => {
|
||||
const task = findTask(g.taskId);
|
||||
const project = state.projects.find((p) => p.id === g.projectId);
|
||||
return h("section", { class: `task-group ${g.blocked ? "blocked" : ""}` }, h("h3", {}, h("span", { class: "project-name" }, project?.name ?? g.projectId, " › "), h("a", { href: `#/task/${g.taskId}` }, task?.title ?? g.taskId)), ...g.items.map(cardFor));
|
||||
const shown = groupByTask(items);
|
||||
const keepGroups = new Set(shown.map((g) => g.taskId));
|
||||
for (const id of groups.keys()) {
|
||||
if (!keepGroups.has(id)) {
|
||||
groups.delete(id);
|
||||
}
|
||||
}
|
||||
const els = shown.map((g) => {
|
||||
const projectName = state.projects.find((p) => p.id === g.projectId)?.name ?? g.projectId;
|
||||
const title = findTask(g.taskId)?.title ?? g.taskId;
|
||||
const head = JSON.stringify([projectName, title]);
|
||||
let group = groups.get(g.taskId);
|
||||
if (!group || group.head !== head) {
|
||||
const h3 = h("h3", {}, h("span", { class: "project-name" }, projectName, " › "), h("a", { href: `#/task/${g.taskId}` }, title));
|
||||
group = { head, h3, el: group?.el ?? h("section", {}) };
|
||||
groups.set(g.taskId, group);
|
||||
}
|
||||
group.el.className = `task-group ${g.blocked ? "blocked" : ""}`;
|
||||
reconcile(group.el, [group.h3, ...g.items.map(cardFor)]);
|
||||
return group.el;
|
||||
});
|
||||
replace(list, ...(els.length > 0 ? els : [h("div", { class: "empty" }, "Nothing waiting on you.")]));
|
||||
reconcile(list, els.length > 0 ? els : [empty]);
|
||||
};
|
||||
const off = onEach(["inbox", "selection", "tasks"], render);
|
||||
const changes = h("section", { class: "inbox-changes" });
|
||||
@@ -78,6 +96,17 @@ export function mountInbox(root) {
|
||||
offChanges();
|
||||
};
|
||||
}
|
||||
function reconcile(parent, nodes) {
|
||||
nodes.forEach((node, i) => {
|
||||
const at = parent.childNodes[i];
|
||||
if (at !== node) {
|
||||
parent.insertBefore(node, at ?? null);
|
||||
}
|
||||
});
|
||||
while (parent.childNodes.length > nodes.length) {
|
||||
parent.lastChild.remove();
|
||||
}
|
||||
}
|
||||
function groupByTask(items) {
|
||||
const groups = new Map();
|
||||
for (const item of [...items].sort((a, b) => a.created_at.localeCompare(b.created_at))) {
|
||||
@@ -156,15 +185,12 @@ function card(item) {
|
||||
const now = (checked.get(item.id) ?? []).filter((o) => o !== text);
|
||||
if (input.checked) {
|
||||
now.push(text);
|
||||
texts.delete(item.id);
|
||||
}
|
||||
checked.set(item.id, options.filter((o) => now.includes(o)));
|
||||
renderActions();
|
||||
};
|
||||
boxes.push(input);
|
||||
return h("label", { class: "option" }, input, " ", inlineMarkdown(text), tag);
|
||||
};
|
||||
const boxes = [];
|
||||
const textEntry = (label, placeholder, shared) => {
|
||||
const open = texts.has(item.id);
|
||||
const text = h("textarea", { rows: 3, placeholder, value: texts.get(item.id) ?? "" });
|
||||
@@ -175,9 +201,12 @@ function card(item) {
|
||||
block.hidden = true;
|
||||
editor.hidden = false;
|
||||
text.focus();
|
||||
shared?.onOpen();
|
||||
});
|
||||
const go = () => {
|
||||
if (shared) {
|
||||
shared.onSubmit();
|
||||
return;
|
||||
}
|
||||
if (text.value.trim()) {
|
||||
void submit({ item_id: item.id, type: "other", text: text.value });
|
||||
}
|
||||
@@ -223,23 +252,18 @@ function card(item) {
|
||||
const refresh = () => {
|
||||
send.disabled = !typed() && (checked.get(item.id) ?? []).length === 0;
|
||||
};
|
||||
const other = textEntry("Other…", placeholder, {
|
||||
onOpen: () => {
|
||||
checked.delete(item.id);
|
||||
for (const box of boxes) {
|
||||
box.checked = false;
|
||||
}
|
||||
refresh();
|
||||
},
|
||||
onInput: refresh,
|
||||
});
|
||||
send.onclick = () => {
|
||||
if (typed()) {
|
||||
void submit({ item_id: item.id, type: "other", text: texts.get(item.id) });
|
||||
const go = () => {
|
||||
const options = checked.get(item.id) ?? [];
|
||||
if (options.length > 0) {
|
||||
void submit({ item_id: item.id, type: "option", options, text: typed() ? texts.get(item.id) : undefined });
|
||||
return;
|
||||
}
|
||||
void submit({ item_id: item.id, type: "option", options: checked.get(item.id) ?? [] });
|
||||
if (typed()) {
|
||||
void submit({ item_id: item.id, type: "other", text: texts.get(item.id) });
|
||||
}
|
||||
};
|
||||
const other = textEntry("Other…", placeholder, { onInput: refresh, onSubmit: go });
|
||||
send.onclick = go;
|
||||
refresh();
|
||||
replace(actions, other, h("div", { class: "submit-row" }, send));
|
||||
return;
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
@@ -739,7 +739,7 @@ export interface components {
|
||||
type: components["schemas"]["InboxResponseType"];
|
||||
/** @description option: the chosen options; exactly one for a pick-one question, one or more for a pick-any question. */
|
||||
options?: string[];
|
||||
/** @description other: the text sent to the agent. */
|
||||
/** @description other: the text sent to the agent. option on a pick-any question: optional text sent along with the checked options. */
|
||||
text?: string;
|
||||
attachments?: components["schemas"]["Attachment"][];
|
||||
};
|
||||
|
||||
+50
-29
@@ -14,6 +14,8 @@ export function mountInbox(root: HTMLElement): () => void {
|
||||
const tabs = h("nav", { class: "scope-tabs" });
|
||||
const list = h("div", { class: "inbox-list" });
|
||||
const cards = new Map<string, { sig: string; el: HTMLElement }>();
|
||||
const groups = new Map<string, { head: string; h3: HTMLElement; el: HTMLElement }>();
|
||||
const empty = h("div", { class: "empty" }, "Nothing waiting on you.");
|
||||
|
||||
const visible = (): InboxItem[] => {
|
||||
const task = state.selectedTaskId ? findTask(state.selectedTaskId) : undefined;
|
||||
@@ -70,18 +72,30 @@ export function mountInbox(root: HTMLElement): () => void {
|
||||
return el;
|
||||
};
|
||||
|
||||
const els = groupByTask(items).map((g) => {
|
||||
const task = findTask(g.taskId);
|
||||
const project = state.projects.find((p) => p.id === g.projectId);
|
||||
return h(
|
||||
"section",
|
||||
{ class: `task-group ${g.blocked ? "blocked" : ""}` },
|
||||
h("h3", {}, h("span", { class: "project-name" }, project?.name ?? g.projectId, " › "), h("a", { href: `#/task/${g.taskId}` }, task?.title ?? g.taskId)),
|
||||
...g.items.map(cardFor),
|
||||
);
|
||||
const shown = groupByTask(items);
|
||||
const keepGroups = new Set(shown.map((g) => g.taskId));
|
||||
for (const id of groups.keys()) {
|
||||
if (!keepGroups.has(id)) {
|
||||
groups.delete(id);
|
||||
}
|
||||
}
|
||||
|
||||
const els = shown.map((g) => {
|
||||
const projectName = state.projects.find((p) => p.id === g.projectId)?.name ?? g.projectId;
|
||||
const title = findTask(g.taskId)?.title ?? g.taskId;
|
||||
const head = JSON.stringify([projectName, title]);
|
||||
let group = groups.get(g.taskId);
|
||||
if (!group || group.head !== head) {
|
||||
const h3 = h("h3", {}, h("span", { class: "project-name" }, projectName, " › "), h("a", { href: `#/task/${g.taskId}` }, title));
|
||||
group = { head, h3, el: group?.el ?? h("section", {}) };
|
||||
groups.set(g.taskId, group);
|
||||
}
|
||||
group.el.className = `task-group ${g.blocked ? "blocked" : ""}`;
|
||||
reconcile(group.el, [group.h3, ...g.items.map(cardFor)]);
|
||||
return group.el;
|
||||
});
|
||||
|
||||
replace(list, ...(els.length > 0 ? els : [h("div", { class: "empty" }, "Nothing waiting on you.")]));
|
||||
reconcile(list, els.length > 0 ? els : [empty]);
|
||||
};
|
||||
|
||||
const off = onEach(["inbox", "selection", "tasks"], render);
|
||||
@@ -95,6 +109,18 @@ export function mountInbox(root: HTMLElement): () => void {
|
||||
};
|
||||
}
|
||||
|
||||
function reconcile(parent: Element, nodes: Node[]): void {
|
||||
nodes.forEach((node, i) => {
|
||||
const at = parent.childNodes[i];
|
||||
if (at !== node) {
|
||||
parent.insertBefore(node, at ?? null);
|
||||
}
|
||||
});
|
||||
while (parent.childNodes.length > nodes.length) {
|
||||
parent.lastChild!.remove();
|
||||
}
|
||||
}
|
||||
|
||||
type Group = { taskId: string; projectId: string; blocked: boolean; items: InboxItem[] };
|
||||
|
||||
function groupByTask(items: InboxItem[]): Group[] {
|
||||
@@ -194,7 +220,6 @@ function card(item: InboxItem): HTMLElement {
|
||||
const now = (checked.get(item.id) ?? []).filter((o) => o !== text);
|
||||
if (input.checked) {
|
||||
now.push(text);
|
||||
texts.delete(item.id);
|
||||
}
|
||||
checked.set(
|
||||
item.id,
|
||||
@@ -202,12 +227,10 @@ function card(item: InboxItem): HTMLElement {
|
||||
);
|
||||
renderActions();
|
||||
};
|
||||
boxes.push(input);
|
||||
return h("label", { class: "option" }, input, " ", inlineMarkdown(text), tag);
|
||||
};
|
||||
const boxes: HTMLInputElement[] = [];
|
||||
|
||||
const textEntry = (label: string, placeholder: string, shared?: { onOpen: () => void; onInput: () => void }) => {
|
||||
const textEntry = (label: string, placeholder: string, shared?: { onInput: () => void; onSubmit: () => void }) => {
|
||||
const open = texts.has(item.id);
|
||||
const text = h("textarea", { rows: 3, placeholder, value: texts.get(item.id) ?? "" });
|
||||
const send = h("button", { type: "button", class: "primary", disabled: !text.value.trim() }, "Submit");
|
||||
@@ -217,9 +240,12 @@ function card(item: InboxItem): HTMLElement {
|
||||
block.hidden = true;
|
||||
editor.hidden = false;
|
||||
text.focus();
|
||||
shared?.onOpen();
|
||||
});
|
||||
const go = () => {
|
||||
if (shared) {
|
||||
shared.onSubmit();
|
||||
return;
|
||||
}
|
||||
if (text.value.trim()) {
|
||||
void submit({ item_id: item.id, type: "other", text: text.value });
|
||||
}
|
||||
@@ -266,23 +292,18 @@ function card(item: InboxItem): HTMLElement {
|
||||
const refresh = () => {
|
||||
send.disabled = !typed() && (checked.get(item.id) ?? []).length === 0;
|
||||
};
|
||||
const other = textEntry("Other…", placeholder, {
|
||||
onOpen: () => {
|
||||
checked.delete(item.id);
|
||||
for (const box of boxes) {
|
||||
box.checked = false;
|
||||
}
|
||||
refresh();
|
||||
},
|
||||
onInput: refresh,
|
||||
});
|
||||
send.onclick = () => {
|
||||
if (typed()) {
|
||||
void submit({ item_id: item.id, type: "other", text: texts.get(item.id)! });
|
||||
const go = () => {
|
||||
const options = checked.get(item.id) ?? [];
|
||||
if (options.length > 0) {
|
||||
void submit({ item_id: item.id, type: "option", options, text: typed() ? texts.get(item.id) : undefined });
|
||||
return;
|
||||
}
|
||||
void submit({ item_id: item.id, type: "option", options: checked.get(item.id) ?? [] });
|
||||
if (typed()) {
|
||||
void submit({ item_id: item.id, type: "other", text: texts.get(item.id)! });
|
||||
}
|
||||
};
|
||||
const other = textEntry("Other…", placeholder, { onInput: refresh, onSubmit: go });
|
||||
send.onclick = go;
|
||||
refresh();
|
||||
replace(actions, other, h("div", { class: "submit-row" }, send));
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user