← Commit history

Publish 0.71.1

Drew Owens ·e10bbcc1b4 · ·parent 1b921ea
5 files changed +54−2
docs/BUILD-NOTES.md+16
@@ -3,6 +3,22 @@ Decisions taken while building, and the places where the plan and the code disagreed. Newest first. +## Build notes, 2026-10-22 (0.71.1, a mouse pick in the editor's lists closed the editor)++Drew, with the milestone editor's status list open: "if I click on an item in this list,+the entire edit box disappears w/out applying". Reproduced on a task and a milestone, for+every list in the item editor. The app's own list (0.65.0) is drawn on the page body, not+inside the editor; a click on an option sets the select, the pick closes the list, and the+same click then bubbles to the document's "clicked outside the editor" handler, whose+target is now an option no longer on the page, so closest('.editor') is null and the+editor closed with the pick unapplied. The keyboard path (0.66.1) never clicks, and the+suite picked with page.select, which never clicks either, so neither saw it. The handler+now ignores a click inside one of our own lists or on a node no longer on the page.++Tests: a flow that picks a status and a section with the MOUSE in the editor, for a task+and a milestone, and checks the editor stays open holding both, nothing saves before Apply,+and Apply saves both.+ ## Build notes, 2026-10-22 (0.71.0, provenance links stay intact; presence counts real tabs)  Drew: "provenance links should not allow their text to be edited, they should remain
gantt.html+5
@@ -7002,6 +7002,11 @@ document.addEventListener('click',e=>{   // beside the bar rather than on it, and this handler was reading that as "clicked   // away" and shutting the panel that had just spent the whole drag showing the dates.   if(Date.now()-dragEndedAt<150)return;+  // A pick in one of our own lists is a click INSIDE whatever opened the list (0.71.1).+  // The list sits on the page body, not inside the editor, and by the time the click gets+  // here the pick has already closed it, so the option clicked is not even on the page+  // any more. Read as "clicked outside", it shut the editor with the pick unapplied.+  if(inOwnPopup(e)||!e.target.isConnected)return;   if(!e.target.closest('.editor')&&!e.target.closest('.bar[data-id]')&&!e.target.closest('.dropdown')&&!e.target.closest('.act-btn'))closeEditor();}); } 
package.json+1−1
@@ -2,7 +2,7 @@   "slug": "adom-gantt",   "title": "Adom Gantt",   "type": "app",-  "version": "0.71.0",+  "version": "0.71.1",   "description": "Interactive Gantt chart viewer for project timelines, with a table view and an analytics page over the same data. Data-driven, dark by default with a print-friendly light mode, and fully editable in the browser: drag bars to move or resize, multi-select and batch-shift dates, manage dependency arrows inline, reorder rows, filter by phase, and undo. One click auto-schedules the whole plan so every dependency lines up finish-to-start on working days, skipping weekends and US federal holidays, pushing work LATER only so a lead-time gap you set on purpose is never closed, and one more lights up the critical path, meaning the longest run of work inside every section at once. Cancelled work is left out of both: it never moves, it constrains nothing and it can never glow. Dependency arrows are coloured by the phase of the task they come out of, so where a chain starts reads at a glance. Toggle to the table for every task and milestone as a row with owner, deadline and status, filtered per owner and edited in place, or to analytics for total, remaining and average hours, one row per person carrying both their hours and their status counts, per-section totals, milestones, dependency stats and what is overdue. Copy and paste rows: Ctrl+C the selected tasks and milestones, Ctrl+V to drop clones directly below a selected row or at the end of the lane you are looking at, with dependencies AMONG the copied rows remapped onto the copies so a duplicated chain is still a chain, links pointing outside it left naming the originals, and the whole paste one undo and one changelog line. Five statuses in lifecycle order, not started, in progress, complete, blocked, cancelled, with cancelled keeping the row and its history on the plan while taking its hours out of every total. Every edit is recorded in an append-only changelog so an AI can read what a human changed and build on it. Imports JSON, CSV, MS Project XML and Jira CSV; exports JSON, CSV, SVG and a self-contained HTML snapshot.",   "main": "server.js",   "bin": {
page.json+1−1
@@ -2,7 +2,7 @@   "slug": "adom-gantt",   "title": "Adom Gantt",   "type": "app",-  "version": "0.71.0",+  "version": "0.71.1",   "description": "Interactive Gantt chart viewer for project timelines, with a table view and an analytics page over the same data. Data-driven, dark by default with a print-friendly light mode, and fully editable in the browser: drag bars to move or resize, multi-select and batch-shift dates, manage dependency arrows inline, reorder rows, filter by phase, and undo. One click auto-schedules the whole plan so every dependency lines up finish-to-start on working days, skipping weekends and US federal holidays, pushing work LATER only so a lead-time gap you set on purpose is never closed, and one more lights up the critical path, meaning the longest run of work inside every section at once. Cancelled work is left out of both: it never moves, it constrains nothing and it can never glow. Dependency arrows are coloured by the phase of the task they come out of, so where a chain starts reads at a glance. Toggle to the table for every task and milestone as a row with owner, deadline and status, filtered per owner and edited in place, or to analytics for total, remaining and average hours, one row per person carrying both their hours and their status counts, per-section totals, milestones, dependency stats and what is overdue. Copy and paste rows: Ctrl+C the selected tasks and milestones, Ctrl+V to drop clones directly below a selected row or at the end of the lane you are looking at, with dependencies AMONG the copied rows remapped onto the copies so a duplicated chain is still a chain, links pointing outside it left naming the originals, and the whole paste one undo and one changelog line. Five statuses in lifecycle order, not started, in progress, complete, blocked, cancelled, with cancelled keeping the row and its history on the plan while taking its hours out of every total. Every edit is recorded in an append-only changelog so an AI can read what a human changed and build on it. Imports JSON, CSV, MS Project XML and Jira CSV; exports JSON, CSV, SVG and a self-contained HTML snapshot.",   "main": "server.js",   "bin": {
test/ui_test.mjs+31
@@ -846,6 +846,7 @@ async function main() {     if (wantAny('notify')) await notifyFlow(browser);     if (wantAny('share')) await shareFlow(browser);     if (wantAny('provenance')) await provFlow(browser);+    if (wantAny('pick by mouse')) await pickFlow(browser);      if (wantAny('viewport')) {       // ── Viewport matrix ──@@ -3316,6 +3317,36 @@ async function notifyFlow(browser) {   check('the second click removes it', (await (await fetch(X.url + 'notify')).json()).configured === false && c.state === 'not set up', JSON.stringify(c));   await page.close(); fake.close(); }+// 0.71.1: a pick made with the MOUSE in one of the editor's lists keeps the editor open+// and holds the choice until Apply. It used to shut the editor unapplied: the click on+// the option reached the page's "clicked outside" handler after the list had closed.+async function pickFlow(browser) {+  section = 'pick by mouse';+  const X = await startServer({ project: { title: 'Pick Test', appName: 'pick-test' }, phases: [{ name: 'Build', start: '2026-10', end: '2026-11' }, { name: 'Ship', start: '2026-10', end: '2026-11' }],+    items: [{ type: 'task', id: 'k1', name: 'Pick task', start: '2026-10-05', end: '2026-10-09', phase: 0, status: 'not-started' },+      { type: 'milestone', name: 'Pick gate', date: '2026-10-20', phase: 0, status: 'not-started' }] }, 'pick');+  const page = await openPage(browser, X.url);+  const pickWith = async (sel, value) => { await page.click(sel); await settle(page, 150); await page.click(`.cell-list .cl-opt[data-value="${value}"]`); await settle(page, 250); };+  const state = () => page.evaluate(() => ({ open: !!document.querySelector('.editor.show'), status: (document.getElementById('imStatus') || {}).value, phase: (document.getElementById('imPhase') || {}).value }));+  const msUid = (await savedPlan(X.url, { minWait: 200 })).items.find(r => r.type === 'milestone').uid;+  for (const [name, find] of [['Pick task', r => r.type === 'task'], ['Pick gate', r => r.type === 'milestone']]) {+    // A milestone is a diamond on its section's row, not a label row of its own.+    if (name === 'Pick gate') await clickMilestone(page, msUid); else await openItemEditor(page, name);+    await settle(page, 200);+    await pickWith('#imStatus', 'blocked');+    let s = await state();+    check(`${name}: clicking a status in the list keeps the editor open, holding the choice`, s.open && s.status === 'blocked', JSON.stringify(s));+    await pickWith('#imPhase', '1');+    s = await state();+    check(`${name}: and so does clicking a section`, s.open && s.phase === '1', JSON.stringify(s));+    const before = (await savedPlan(X.url, { minWait: 300 })).items.find(find);+    check(`${name}: nothing is saved before Apply`, before.status === 'not-started' && before.phase === 0, JSON.stringify(before));+    await page.click('#imApply'); await settle(page, 300);+    const after = (await savedPlan(X.url)).items.find(find);+    check(`${name}: Apply saves both picks`, after.status === 'blocked' && after.phase === 1, JSON.stringify(after));+  }+  await page.close();+} // 0.71.0: a LINKED provenance is fixed (a link, never a text field, in the table and the // item editor), a plain one is still edited; and the table's count says what it hides. async function provFlow(browser) {