Add a per-user lead agent and make the right pane task-focused
This commit is contained in:
Vendored
+11
-71
@@ -3,44 +3,17 @@ import { mountChanges } from "./changes.js";
|
||||
import { ago, errorText, h, replace, showAlert } from "./dom.js";
|
||||
import { image, inlineMarkdown, markdown } from "./markdown.js";
|
||||
import { diagram } from "./mermaid.js";
|
||||
import { findTask, onEach, state } from "./state.js";
|
||||
import { onEach, state } from "./state.js";
|
||||
const checked = new Map();
|
||||
const texts = new Map();
|
||||
let scope = "all";
|
||||
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) {
|
||||
return state.inbox.filter((i) => i.task_id === task.id);
|
||||
}
|
||||
if (scope === "project" && task) {
|
||||
return state.inbox.filter((i) => i.project_id === task.project_id);
|
||||
}
|
||||
return state.inbox;
|
||||
};
|
||||
const renderTabs = () => {
|
||||
const hasTask = state.selectedTaskId !== undefined;
|
||||
const tab = (s, label) => {
|
||||
const b = h("button", { class: s === scope ? "active" : "", disabled: s !== "all" && !hasTask }, label);
|
||||
b.onclick = () => {
|
||||
scope = s;
|
||||
render();
|
||||
};
|
||||
return b;
|
||||
};
|
||||
replace(tabs, tab("all", "All"), tab("project", "This project"), tab("task", "This task"));
|
||||
};
|
||||
const nothing = h("div", { class: "empty" }, "Nothing waiting on you in this task.");
|
||||
const noTask = h("div", { class: "empty" }, "No task selected.");
|
||||
const render = () => {
|
||||
if (scope !== "all" && !state.selectedTaskId) {
|
||||
scope = "all";
|
||||
}
|
||||
renderTabs();
|
||||
const items = visible();
|
||||
const taskId = state.selectedTaskId;
|
||||
const items = state.inbox.filter((i) => i.task_id === taskId).sort((a, b) => a.created_at.localeCompare(b.created_at));
|
||||
const keep = new Set(items.map((i) => i.id));
|
||||
for (const id of cards.keys()) {
|
||||
if (!keep.has(id)) {
|
||||
@@ -64,32 +37,15 @@ export function mountInbox(root) {
|
||||
cards.set(item.id, { sig, el });
|
||||
return el;
|
||||
};
|
||||
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);
|
||||
}
|
||||
if (!taskId) {
|
||||
reconcile(list, [noTask]);
|
||||
return;
|
||||
}
|
||||
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;
|
||||
});
|
||||
reconcile(list, els.length > 0 ? els : [empty]);
|
||||
reconcile(list, items.length > 0 ? items.map(cardFor) : [nothing]);
|
||||
};
|
||||
const off = onEach(["inbox", "selection", "tasks"], render);
|
||||
const off = onEach(["inbox", "selection"], render);
|
||||
const changes = h("section", { class: "inbox-changes" });
|
||||
replace(root, h("div", { class: "inbox-view" }, changes, tabs, list));
|
||||
replace(root, h("div", { class: "inbox-view" }, changes, list));
|
||||
const offChanges = mountChanges(changes, true);
|
||||
render();
|
||||
return () => {
|
||||
@@ -108,22 +64,6 @@ function reconcile(parent, nodes) {
|
||||
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))) {
|
||||
let g = groups.get(item.task_id);
|
||||
if (!g) {
|
||||
g = { taskId: item.task_id, projectId: item.project_id, blocked: false, items: [] };
|
||||
groups.set(item.task_id, g);
|
||||
}
|
||||
g.items.push(item);
|
||||
g.blocked ||= item.priority === "blocked";
|
||||
}
|
||||
const projectName = (id) => state.projects.find((p) => p.id === id)?.name ?? id;
|
||||
return [...groups.values()].sort((a, b) => Number(b.blocked) - Number(a.blocked) ||
|
||||
projectName(a.projectId).localeCompare(projectName(b.projectId)) ||
|
||||
a.items[0].created_at.localeCompare(b.items[0].created_at));
|
||||
}
|
||||
function clickable(el, go) {
|
||||
el.onclick = (e) => {
|
||||
if (!e.target.closest("a")) {
|
||||
|
||||
Reference in New Issue
Block a user