Replace the Todo tab with a Project dashboard of tasks and TODO items
This commit is contained in:
Vendored
+2
-2
@@ -86,8 +86,8 @@ function openSwitcher() {
|
||||
const el = h("li", {}, h("span", { class: `dot ${sess?.state ?? "none"}`, title: sess?.state ?? "no session" }), h("span", { class: "task-title" }, h("span", { class: "project-name" }, project.name), " ", task.title), count > 0 && h("span", { class: "badge", title: "inbox items" }, count), h("span", { class: "task-state" }, sess?.state ?? "no agent"));
|
||||
return { el, pick: () => go(task.id) };
|
||||
});
|
||||
for (const p of state.projects.filter((p) => `${p.name} todo`.toLowerCase().includes(q))) {
|
||||
const el = h("li", {}, h("span", { class: "dot none" }), h("span", { class: "task-title" }, h("span", { class: "project-name" }, p.name), " TODO.md"), h("span", { class: "task-state" }, "project"));
|
||||
for (const p of state.projects.filter((p) => `${p.name} project`.toLowerCase().includes(q))) {
|
||||
const el = h("li", {}, h("span", { class: "dot none" }), h("span", { class: "task-title" }, h("span", { class: "project-name" }, p.name)), h("span", { class: "task-state" }, "project"));
|
||||
rows.push({
|
||||
el,
|
||||
pick: () => {
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+2
-2
@@ -5,14 +5,14 @@ import { mountFiles } from "./files.js";
|
||||
import { mountInbox } from "./inbox.js";
|
||||
import { mountLead } from "./lead.js";
|
||||
import { focusTab, on, state, tabs } from "./state.js";
|
||||
import { mountProject } from "./project.js";
|
||||
import { mountTranscript } from "./task.js";
|
||||
import { mountTodo } from "./todo.js";
|
||||
const views = {
|
||||
transcript: { label: "Transcript", mount: mountTranscript },
|
||||
changes: { label: "Changes", mount: (root) => mountChanges(root) },
|
||||
files: { label: "Files", mount: mountFiles },
|
||||
inbox: { label: "Inbox", mount: mountInbox },
|
||||
todo: { label: "Todo", mount: mountTodo },
|
||||
project: { label: "Project", mount: mountProject },
|
||||
};
|
||||
export function leadPane() {
|
||||
const root = h("div", { class: "view view-lead" });
|
||||
|
||||
Vendored
+1
-1
@@ -1 +1 @@
|
||||
{"version":3,"file":"panes.js","sourceRoot":"","sources":["../src/panes.ts"],"names":[],"mappings":"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"}
|
||||
{"version":3,"file":"panes.js","sourceRoot":"","sources":["../src/panes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAgB,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAE5C,MAAM,KAAK,GAAiF;IAC1F,UAAU,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,eAAe,EAAE;IAC3D,OAAO,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE;IAClE,KAAK,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE;IAC5C,KAAK,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE;IAC5C,OAAO,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,YAAY,EAAE;CACnD,CAAC;AAEF,MAAM,UAAU,QAAQ;IACtB,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC,CAAC;IACnD,SAAS,CAAC,IAAI,CAAC,CAAC;IAChB,OAAO,CAAC,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,CAAC,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC;AAC7J,CAAC;AAED,MAAM,UAAU,QAAQ;IACtB,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;IAC/C,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;IAC9C,IAAI,KAA0B,CAAC;IAC/B,IAAI,OAAO,GAAe,GAAG,EAAE,GAAE,CAAC,CAAC;IAEnC,MAAM,MAAM,GAAG,GAAG,EAAE;QAClB,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;QAC/F,MAAM,KAAK,GAAG,CAAC,GAAG,KAAK,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACpE,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;QAC5B,OAAO,CACL,KAAK,EACL,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;YACjB,MAAM,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;YAC5E,CAAC,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;YAC9B,OAAO,CAAC,CAAC;QACX,CAAC,CAAC,CACH,CAAC;QACF,IAAI,GAAG,KAAK,KAAK,EAAE,CAAC;YAClB,OAAO;QACT,CAAC;QACD,OAAO,EAAE,CAAC;QACV,KAAK,GAAG,GAAG,CAAC;QACZ,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,aAAa,GAAG,EAAE,EAAE,CAAC,CAAC;QACrD,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACpB,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC,CAAC;IAEF,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IACpB,MAAM,EAAE,CAAC;IACT,MAAM,IAAI,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;IACxE,cAAc,CAAC,IAAI,CAAC,CAAC;IACrB,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,cAAc,CAAC,IAAiB;IACvC,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,MAAM,GAAmB,IAAI,CAAC;IAClC,IAAI,OAAO,GAAqB,OAAO,CAAC,OAAO,EAAE,CAAC;IAClD,MAAM,IAAI,GAAG,GAAG,EAAE;QAChB,MAAM,IAAI,GAAG,EAAE,OAAO,EAAE,CAAC;QACzB,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,oBAAoB,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAC,CAAC;IAClI,CAAC,CAAC;IACF,MAAM,MAAM,GAAG,GAAG,EAAE;QAClB,UAAU,CAAC,GAAG,EAAE;YACd,IAAI,OAAO,IAAI,MAAM,IAAI,CAAC,MAAM,CAAC,WAAW,IAAI,QAAQ,CAAC,aAAa,KAAK,QAAQ,CAAC,IAAI,EAAE,CAAC;gBACzF,oGAAoG;gBACpG,IAAI,CAAC,KAAK,CAAC,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;YACtC,CAAC;YACD,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC;YACtC,MAAM,GAAG,GAAG,QAAQ,CAAC,QAAQ,EAAE,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YACzD,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;YAC7B,IAAI,GAAG,KAAK,OAAO,EAAE,CAAC;gBACpB,OAAO;YACT,CAAC;YACD,OAAO,GAAG,GAAG,CAAC;YACd,IAAI,EAAE,CAAC;QACT,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;IACF,EAAE,CAAC,YAAY,EAAE,GAAG,EAAE;QACpB,IAAI,KAAK,CAAC,SAAS,IAAI,OAAO,EAAE,CAAC;YAC/B,IAAI,EAAE,CAAC;QACT,CAAC;IACH,CAAC,CAAC,CAAC;IACH,QAAQ,CAAC,gBAAgB,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IAC7C,QAAQ,CAAC,gBAAgB,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IAC9C,gBAAgB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAClC,gBAAgB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,gBAAgB,CAAC,UAAU,EAAE,GAAG,EAAE;QAChC,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,GAAG,KAAK,CAAC;YAChB,KAAK,GAAG,CAAC,oBAAoB,EAAE,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAChF,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC"}
|
||||
+32
-14
@@ -4,15 +4,15 @@ import { markdown } from "./markdown.js";
|
||||
import { models } from "./task.js";
|
||||
import { currentSession, focusedProject, focusTask, on, onEach, state } from "./state.js";
|
||||
const refreshKinds = new Set(["task.merged", "project.created"]);
|
||||
export function mountTodo(root) {
|
||||
export function mountProject(root) {
|
||||
const todos = new Map();
|
||||
const starting = new Set();
|
||||
let disposed = false;
|
||||
const render = () => {
|
||||
const scrollTop = root.querySelector(".todo-view")?.scrollTop ?? 0;
|
||||
const scrollTop = root.querySelector(".project-view")?.scrollTop ?? 0;
|
||||
const p = focusedProject();
|
||||
replace(root, h("div", { class: "todo-view" }, p ? projectBlock(p, todos.get(p.id), starting, start) : h("div", { class: "empty" }, "No project selected.")));
|
||||
root.querySelector(".todo-view").scrollTop = scrollTop;
|
||||
replace(root, h("div", { class: "project-view" }, p ? projectBlock(p, todos.get(p.id), starting, start) : h("div", { class: "empty" }, "No project selected.")));
|
||||
root.querySelector(".project-view").scrollTop = scrollTop;
|
||||
};
|
||||
const load = async (projectId) => {
|
||||
try {
|
||||
@@ -71,7 +71,7 @@ export function mountTodo(root) {
|
||||
void load(p.id);
|
||||
}
|
||||
}),
|
||||
onEach(["tasks", "sessions"], render),
|
||||
onEach(["tasks", "sessions", "inbox"], render),
|
||||
];
|
||||
render();
|
||||
refresh();
|
||||
@@ -83,20 +83,38 @@ export function mountTodo(root) {
|
||||
};
|
||||
}
|
||||
function projectBlock(project, todo, starting, start) {
|
||||
const head = h("h2", {}, project.name, " ", h("span", { class: "repo" }, `TODO.md on ${project.default_branch}`));
|
||||
const active = (state.tasks.get(project.id) ?? []).filter((t) => t.status === "active");
|
||||
return h("section", { class: "project" }, h("h2", {}, project.name, " ", h("span", { class: "repo" }, project.default_branch)), h("h3", {}, "Tasks"), tasksBlock(active), h("h3", {}, "TODO"), ...todoBlock(project, todo, active, starting, start));
|
||||
}
|
||||
function tasksBlock(active) {
|
||||
if (active.length === 0) {
|
||||
return h("p", { class: "empty-row" }, "No active tasks.");
|
||||
}
|
||||
return h("ul", { class: "task-list" }, ...active.map((task) => {
|
||||
const sess = currentSession(task.id);
|
||||
const count = state.inbox.filter((item) => item.task_id === task.id).length;
|
||||
return h("li", { class: task.id === state.selectedTaskId ? "selected" : "" }, h("a", {
|
||||
href: `#/task/${task.id}`,
|
||||
onclick: (e) => {
|
||||
e.preventDefault();
|
||||
focusTask(task.id);
|
||||
},
|
||||
}, h("span", { class: `dot ${sess?.state ?? "none"}`, title: sess?.state ?? "no session" }), h("span", { class: "task-title" }, task.title), count > 0 && h("span", { class: "badge", title: "inbox items" }, count), h("span", { class: "task-state" }, sess?.state ?? "no agent")));
|
||||
}));
|
||||
}
|
||||
function todoBlock(project, todo, active, starting, start) {
|
||||
if (todo === undefined) {
|
||||
return h("section", { class: "project" }, head, h("p", { class: "empty-row" }, "Loading…"));
|
||||
return [h("p", { class: "empty-row" }, "Loading…")];
|
||||
}
|
||||
if (typeof todo === "string") {
|
||||
return h("section", { class: "project" }, head, h("p", { class: "empty-row error-text" }, todo));
|
||||
return [h("p", { class: "empty-row error-text" }, todo)];
|
||||
}
|
||||
if (!todo.exists) {
|
||||
return h("section", { class: "project" }, head, h("p", { class: "empty-row" }, `No TODO.md on ${project.default_branch}.`));
|
||||
return [h("p", { class: "empty-row" }, `No TODO.md on ${project.default_branch}.`)];
|
||||
}
|
||||
if (todo.items.length === 0) {
|
||||
return h("section", { class: "project" }, head, h("p", { class: "empty-row" }, "TODO.md has no items."));
|
||||
return [h("p", { class: "empty-row" }, "TODO.md has no items.")];
|
||||
}
|
||||
const active = (state.tasks.get(project.id) ?? []).filter((t) => t.status === "active" && t.todo_item);
|
||||
const children = [];
|
||||
let section = "";
|
||||
let list;
|
||||
@@ -104,7 +122,7 @@ function projectBlock(project, todo, starting, start) {
|
||||
if (!list || item.section !== section) {
|
||||
section = item.section;
|
||||
if (section) {
|
||||
children.push(h("h3", { class: "todo-section" }, section));
|
||||
children.push(h("h4", { class: "todo-section" }, section));
|
||||
}
|
||||
list = h("ul", { class: "todo-list" });
|
||||
children.push(list);
|
||||
@@ -130,6 +148,6 @@ function projectBlock(project, todo, starting, start) {
|
||||
}
|
||||
list.append(h("li", { class: task && task.id === state.selectedTaskId ? "selected" : "" }, h("div", { class: "todo-head" }, h("span", { class: "todo-title" }, item.title), action), item.body && markdown(item.body, "md todo-body")));
|
||||
}
|
||||
return h("section", { class: "project" }, head, ...children);
|
||||
return children;
|
||||
}
|
||||
//# sourceMappingURL=todo.js.map
|
||||
//# sourceMappingURL=project.js.map
|
||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+2
-2
@@ -1,5 +1,5 @@
|
||||
import { get, put, subscribe } from "./api.js";
|
||||
export const tabs = ["transcript", "changes", "files", "inbox", "todo"];
|
||||
export const tabs = ["transcript", "changes", "files", "inbox", "project"];
|
||||
export const state = {
|
||||
me: undefined,
|
||||
projects: [],
|
||||
@@ -120,7 +120,7 @@ export function focusTask(taskId, tab = "transcript") {
|
||||
setFocus({ task_id: taskId, tab }).catch((err) => console.error("set focus", err));
|
||||
}
|
||||
export function focusProject(projectId) {
|
||||
setFocus({ task_id: null, project_id: projectId, tab: "todo" }).catch((err) => console.error("set focus", err));
|
||||
setFocus({ task_id: null, project_id: projectId, tab: "project" }).catch((err) => console.error("set focus", err));
|
||||
}
|
||||
export function focusTab(tab) {
|
||||
setFocus({ tab }).catch((err) => console.error("set focus", err));
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
-1
File diff suppressed because one or more lines are too long
+1
-1
@@ -1019,7 +1019,7 @@ export interface components {
|
||||
focused: boolean;
|
||||
};
|
||||
/** @enum {string} */
|
||||
TaskTab: "transcript" | "changes" | "files" | "inbox" | "todo";
|
||||
TaskTab: "transcript" | "changes" | "files" | "inbox" | "project";
|
||||
Status: {
|
||||
/** @description Agent processes currently running. */
|
||||
live_agents: number;
|
||||
|
||||
+2
-2
@@ -106,8 +106,8 @@ function openSwitcher(): void {
|
||||
);
|
||||
return { el, pick: () => go(task.id) };
|
||||
});
|
||||
for (const p of state.projects.filter((p) => `${p.name} todo`.toLowerCase().includes(q))) {
|
||||
const el = h("li", {}, h("span", { class: "dot none" }), h("span", { class: "task-title" }, h("span", { class: "project-name" }, p.name), " TODO.md"), h("span", { class: "task-state" }, "project"));
|
||||
for (const p of state.projects.filter((p) => `${p.name} project`.toLowerCase().includes(q))) {
|
||||
const el = h("li", {}, h("span", { class: "dot none" }), h("span", { class: "task-title" }, h("span", { class: "project-name" }, p.name)), h("span", { class: "task-state" }, "project"));
|
||||
rows.push({
|
||||
el,
|
||||
pick: () => {
|
||||
|
||||
+2
-2
@@ -5,15 +5,15 @@ import { mountFiles } from "./files.js";
|
||||
import { mountInbox } from "./inbox.js";
|
||||
import { mountLead } from "./lead.js";
|
||||
import { focusTab, on, state, tabs } from "./state.js";
|
||||
import { mountProject } from "./project.js";
|
||||
import { mountTranscript } from "./task.js";
|
||||
import { mountTodo } from "./todo.js";
|
||||
|
||||
const views: Record<TaskTab, { label: string; mount: (root: HTMLElement) => () => void }> = {
|
||||
transcript: { label: "Transcript", mount: mountTranscript },
|
||||
changes: { label: "Changes", mount: (root) => mountChanges(root) },
|
||||
files: { label: "Files", mount: mountFiles },
|
||||
inbox: { label: "Inbox", mount: mountInbox },
|
||||
todo: { label: "Todo", mount: mountTodo },
|
||||
project: { label: "Project", mount: mountProject },
|
||||
};
|
||||
|
||||
export function leadPane(): HTMLElement {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { get, post, type ByreEvent, type Project, type Schemas } from "./api.js";
|
||||
import { get, post, type ByreEvent, type Project, type Schemas, type Task } from "./api.js";
|
||||
import { errorText, h, replace, showAlert } from "./dom.js";
|
||||
import { markdown } from "./markdown.js";
|
||||
import { models } from "./task.js";
|
||||
@@ -9,16 +9,16 @@ type TodoItem = Schemas["TodoItem"];
|
||||
|
||||
const refreshKinds = new Set(["task.merged", "project.created"]);
|
||||
|
||||
export function mountTodo(root: HTMLElement): () => void {
|
||||
export function mountProject(root: HTMLElement): () => void {
|
||||
const todos = new Map<string, Todo | string>();
|
||||
const starting = new Set<string>();
|
||||
let disposed = false;
|
||||
|
||||
const render = () => {
|
||||
const scrollTop = root.querySelector(".todo-view")?.scrollTop ?? 0;
|
||||
const scrollTop = root.querySelector(".project-view")?.scrollTop ?? 0;
|
||||
const p = focusedProject();
|
||||
replace(root, h("div", { class: "todo-view" }, p ? projectBlock(p, todos.get(p.id), starting, start) : h("div", { class: "empty" }, "No project selected.")));
|
||||
root.querySelector(".todo-view")!.scrollTop = scrollTop;
|
||||
replace(root, h("div", { class: "project-view" }, p ? projectBlock(p, todos.get(p.id), starting, start) : h("div", { class: "empty" }, "No project selected.")));
|
||||
root.querySelector(".project-view")!.scrollTop = scrollTop;
|
||||
};
|
||||
|
||||
const load = async (projectId: string) => {
|
||||
@@ -79,7 +79,7 @@ export function mountTodo(root: HTMLElement): () => void {
|
||||
void load(p.id);
|
||||
}
|
||||
}),
|
||||
onEach(["tasks", "sessions"], render),
|
||||
onEach(["tasks", "sessions", "inbox"], render),
|
||||
];
|
||||
|
||||
render();
|
||||
@@ -94,22 +94,64 @@ export function mountTodo(root: HTMLElement): () => void {
|
||||
}
|
||||
|
||||
function projectBlock(project: Project, todo: Todo | string | undefined, starting: Set<string>, start: (p: Project, item: TodoItem) => Promise<void>): HTMLElement {
|
||||
const head = h("h2", {}, project.name, " ", h("span", { class: "repo" }, `TODO.md on ${project.default_branch}`));
|
||||
const active = (state.tasks.get(project.id) ?? []).filter((t) => t.status === "active");
|
||||
return h(
|
||||
"section",
|
||||
{ class: "project" },
|
||||
h("h2", {}, project.name, " ", h("span", { class: "repo" }, project.default_branch)),
|
||||
h("h3", {}, "Tasks"),
|
||||
tasksBlock(active),
|
||||
h("h3", {}, "TODO"),
|
||||
...todoBlock(project, todo, active, starting, start),
|
||||
);
|
||||
}
|
||||
|
||||
function tasksBlock(active: Task[]): HTMLElement {
|
||||
if (active.length === 0) {
|
||||
return h("p", { class: "empty-row" }, "No active tasks.");
|
||||
}
|
||||
return h(
|
||||
"ul",
|
||||
{ class: "task-list" },
|
||||
...active.map((task) => {
|
||||
const sess = currentSession(task.id);
|
||||
const count = state.inbox.filter((item) => item.task_id === task.id).length;
|
||||
return h(
|
||||
"li",
|
||||
{ class: task.id === state.selectedTaskId ? "selected" : "" },
|
||||
h(
|
||||
"a",
|
||||
{
|
||||
href: `#/task/${task.id}`,
|
||||
onclick: (e: MouseEvent) => {
|
||||
e.preventDefault();
|
||||
focusTask(task.id);
|
||||
},
|
||||
},
|
||||
h("span", { class: `dot ${sess?.state ?? "none"}`, title: sess?.state ?? "no session" }),
|
||||
h("span", { class: "task-title" }, task.title),
|
||||
count > 0 && h("span", { class: "badge", title: "inbox items" }, count),
|
||||
h("span", { class: "task-state" }, sess?.state ?? "no agent"),
|
||||
),
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
function todoBlock(project: Project, todo: Todo | string | undefined, active: Task[], starting: Set<string>, start: (p: Project, item: TodoItem) => Promise<void>): HTMLElement[] {
|
||||
if (todo === undefined) {
|
||||
return h("section", { class: "project" }, head, h("p", { class: "empty-row" }, "Loading…"));
|
||||
return [h("p", { class: "empty-row" }, "Loading…")];
|
||||
}
|
||||
if (typeof todo === "string") {
|
||||
return h("section", { class: "project" }, head, h("p", { class: "empty-row error-text" }, todo));
|
||||
return [h("p", { class: "empty-row error-text" }, todo)];
|
||||
}
|
||||
if (!todo.exists) {
|
||||
return h("section", { class: "project" }, head, h("p", { class: "empty-row" }, `No TODO.md on ${project.default_branch}.`));
|
||||
return [h("p", { class: "empty-row" }, `No TODO.md on ${project.default_branch}.`)];
|
||||
}
|
||||
if (todo.items.length === 0) {
|
||||
return h("section", { class: "project" }, head, h("p", { class: "empty-row" }, "TODO.md has no items."));
|
||||
return [h("p", { class: "empty-row" }, "TODO.md has no items.")];
|
||||
}
|
||||
|
||||
const active = (state.tasks.get(project.id) ?? []).filter((t) => t.status === "active" && t.todo_item);
|
||||
const children: HTMLElement[] = [];
|
||||
let section = "";
|
||||
let list: HTMLElement | undefined;
|
||||
@@ -117,7 +159,7 @@ function projectBlock(project: Project, todo: Todo | string | undefined, startin
|
||||
if (!list || item.section !== section) {
|
||||
section = item.section;
|
||||
if (section) {
|
||||
children.push(h("h3", { class: "todo-section" }, section));
|
||||
children.push(h("h4", { class: "todo-section" }, section));
|
||||
}
|
||||
list = h("ul", { class: "todo-list" });
|
||||
children.push(list);
|
||||
@@ -156,6 +198,5 @@ function projectBlock(project: Project, todo: Todo | string | undefined, startin
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return h("section", { class: "project" }, head, ...children);
|
||||
return children;
|
||||
}
|
||||
+2
-2
@@ -17,7 +17,7 @@ export type Topic =
|
||||
| "lead.transcript"
|
||||
| "lead.stream";
|
||||
|
||||
export const tabs: TaskTab[] = ["transcript", "changes", "files", "inbox", "todo"];
|
||||
export const tabs: TaskTab[] = ["transcript", "changes", "files", "inbox", "project"];
|
||||
|
||||
export const state = {
|
||||
me: undefined as User | undefined,
|
||||
@@ -161,7 +161,7 @@ export function focusTask(taskId: string, tab: TaskTab = "transcript"): void {
|
||||
}
|
||||
|
||||
export function focusProject(projectId: string): void {
|
||||
setFocus({ task_id: null, project_id: projectId, tab: "todo" }).catch((err: unknown) => console.error("set focus", err));
|
||||
setFocus({ task_id: null, project_id: projectId, tab: "project" }).catch((err: unknown) => console.error("set focus", err));
|
||||
}
|
||||
|
||||
export function focusTab(tab: TaskTab): void {
|
||||
|
||||
+6
-1
@@ -313,11 +313,16 @@ textarea {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.todo-view {
|
||||
.project-view {
|
||||
overflow-y: auto;
|
||||
padding: 8px 16px 16px;
|
||||
}
|
||||
|
||||
.project h3 {
|
||||
font-size: 12px;
|
||||
margin: 14px 0 4px;
|
||||
}
|
||||
|
||||
.todo-section {
|
||||
font-size: 12px;
|
||||
color: var(--muted);
|
||||
|
||||
Reference in New Issue
Block a user