Scope lead notes by project and share them with task agents

This commit is contained in:
Ian Gulliver
2026-10-16 23:12:14 -07:00
parent 165266f25d
commit 35184dc081
13 changed files with 395 additions and 251 deletions
+45 -21
View File
@@ -7,6 +7,7 @@ const kinds = [
{ key: "errors", label: "Session errors" },
{ key: "rate_limits", label: "Rate limits" },
];
let notesProject = "";
export function mountSettings(root) {
const device = h("div", { class: "push-device" });
const prefs = h("fieldset", { class: "push-prefs" });
@@ -52,12 +53,51 @@ export function mountSettings(root) {
replace(device, h("p", {}, "This browser doesn't show notifications."), button);
};
const notes = h("div", { class: "lead-notes" });
let list;
let loads = 0;
const noteItem = (note) => {
const remove = h("button", {}, "delete");
remove.onclick = async () => {
if (!(await showConfirm([h("p", {}, "Delete this note? Your lead stops applying it."), h("p", {}, note.text)], "Delete", true, remove))) {
return;
}
try {
await del("/api/me/lead/notes/{noteId}", { path: { noteId: note.id } });
}
catch (err) {
void showAlert(errorText(err));
}
};
return h("li", {}, h("span", {}, note.text), remove);
};
const renderNotes = () => {
if (!list) {
return;
}
if (list.length === 0) {
replace(notes, h("p", { class: "hint" }, "Your lead hasn't noted anything about how you work yet."));
return;
}
const all = list;
const scoped = (id) => all.filter((note) => (note.project_id ?? "") === id);
const groups = [{ id: "", name: "Every project" }, ...state.projects.map((p) => ({ id: p.id, name: p.name }))];
if (!groups.some((g) => g.id === notesProject)) {
notesProject = "";
}
const picker = h("select", { title: "Which notes to show" }, ...groups.map((g) => h("option", { value: g.id }, `${g.name} (${scoped(g.id).length})`)));
picker.value = notesProject;
picker.onchange = () => {
notesProject = picker.value;
renderNotes();
};
const shown = scoped(notesProject);
replace(notes, picker, shown.length === 0 ? h("p", { class: "hint" }, "No notes here yet.") : h("ul", {}, ...shown.map(noteItem)));
};
const loadNotes = async () => {
const n = ++loads;
let list;
let loaded;
try {
list = await get("/api/me/lead/notes");
loaded = await get("/api/me/lead/notes");
}
catch (err) {
replace(notes, h("p", { class: "hint" }, errorText(err)));
@@ -66,29 +106,13 @@ export function mountSettings(root) {
if (n !== loads) {
return;
}
if (list.length === 0) {
replace(notes, h("p", { class: "hint" }, "Your lead hasn't noted anything about how you work yet."));
return;
}
replace(notes, h("ul", {}, ...list.map((note) => {
const remove = h("button", {}, "delete");
remove.onclick = async () => {
if (!(await showConfirm([h("p", {}, "Delete this note? Your lead stops applying it."), h("p", {}, note.text)], "Delete", true, remove))) {
return;
}
try {
await del("/api/me/lead/notes/{noteId}", { path: { noteId: note.id } });
}
catch (err) {
void showAlert(errorText(err));
}
};
return h("li", {}, h("span", {}, note.text), remove);
})));
list = loaded;
renderNotes();
};
replace(root, h("div", { class: "settings-view" }, h("h2", {}, "Notifications"), device, prefs, h("h2", {}, "What your lead has learned"), h("p", { class: "hint" }, "Your lead notes your priorities and preferences as it works with you, and starts every session with them."), notes));
const offs = [
on("me", renderPrefs),
on("projects", renderNotes),
on("event", (detail) => {
const ev = detail;
if (ev.kind === "lead.note_saved" || ev.kind === "lead.note_deleted") {
+1 -1
View File
File diff suppressed because one or more lines are too long