import { put } from "./api.js"; import { mountChanges } from "./changes.js"; import { h, replace } from "./dom.js"; import { mountFiles } from "./files.js"; import { mountInbox } from "./inbox.js"; import { mountLead } from "./lead.js"; import { openSwitcher } from "./navbar.js"; import { findTask, focusedProject, focusTab, on, onEach, state, tabs } from "./state.js"; import { mountProject } from "./project.js"; import { mountTranscript } from "./task.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 }, project: { label: "Project", mount: mountProject }, }; export function leadPane() { const root = h("div", { class: "view view-lead" }); mountLead(root); return h("section", { class: "pane" }, h("nav", { class: "pane-tabs" }, h("button", { class: "active" }, "Lead")), h("div", { class: "pane-body" }, root)); } export function taskPane() { const strip = h("nav", { class: "pane-tabs" }); const body = h("div", { class: "pane-body" }); const picker = h("button", { class: "task-picker", title: "Switch task (⌘K)" }); picker.onclick = openSwitcher; let shown; let dispose = () => { }; const renderPicker = () => { const task = state.selectedTaskId ? findTask(state.selectedTaskId) : undefined; const project = focusedProject(); replace(picker, task ? `${project?.name ?? ""} ▸ ${task.title}` : (project?.name ?? "No task"), " ▾"); }; const render = () => { const first = state.focus.tabs.filter((t, i, all) => tabs.includes(t) && all.indexOf(t) === i); const order = [...first, ...tabs.filter((t) => !first.includes(t))]; const tab = state.focus.tab; replace(strip, ...order.map((t) => { const b = h("button", { class: t === tab ? "active" : "" }, views[t].label); b.onclick = () => focusTab(t); return b; }), h("span", { class: "spacer" }), picker); if (tab === shown) { return; } dispose(); shown = tab; const root = h("div", { class: `view view-${tab}` }); replace(body, root); dispose = views[tab].mount(root); }; on("focus", render); onEach(["projects", "tasks", "selection", "focus"], renderPicker); render(); renderPicker(); const pane = h("section", { class: "pane", tabIndex: -1 }, strip, body); trackPaneFocus(pane); return pane; } function trackPaneFocus(pane) { let focused = false; let inside = null; let sending = Promise.resolve(); const send = () => { const body = { focused }; sending = sending.then(() => put("/api/me/pane-focus", { body })).catch((err) => console.error("set pane focus", err)); }; const update = () => { setTimeout(() => { if (focused && inside && !inside.isConnected && document.activeElement === document.body) { // A re-render removed the focused element (a tab button, a list row); that isn't the human leaving. pane.focus({ preventScroll: true }); } const active = document.activeElement; const now = document.hasFocus() && pane.contains(active); inside = now ? active : null; if (now === focused) { return; } focused = now; send(); }); }; on("connection", () => { if (state.connected && focused) { send(); } }); document.addEventListener("focusin", update); document.addEventListener("focusout", update); addEventListener("focus", update); addEventListener("blur", update); addEventListener("pagehide", () => { if (focused) { focused = false; void put("/api/me/pane-focus", { body: { focused: false }, keepalive: true }); } }); } //# sourceMappingURL=panes.js.map