← Commit history

0.28.1: the New panel remembers every field except the name

Drew Owens ·15be99f4c1 · ·parent 9f1f1a6
6 files changed +80−22
README.md+1−1
@@ -38,7 +38,7 @@ Interactive Gantt chart viewer for project timelines. Data-driven, dark by defau  **Legend filtering.** Click any phase, "Milestone", "Dependencies" or "Today" in the legend bar to toggle visibility. Dimmed items fade to 15% opacity; hiding "Today" removes the marker line. -**New items remember the last one.** After you create a task or milestone, the next New panel prefills the owner and both dates from it, so adding several items for the same person in the same week is just names. Name and notes always start empty. It lasts the page session only: a refresh clears it, and it is never saved to your preferences or the plan, so it is never handed to anyone else.+**New items remember the last one.** After you create a task or milestone, the next New panel prefills every field from it: type, lane, owner, status, both dates, notes and the provenance label. Only the **name** starts empty, so adding several items for the same person in the same lane in the same week is just names. Leave provenance at its default and it stays a default, following whoever is signed in rather than freezing a name into the session. It lasts the page session only: a refresh clears it, and it is never saved to your preferences or the plan, so it is never handed to anyone else. Cancel, Escape and a create rejected for a missing name all leave the memory exactly as it was.  **Undo.** Ctrl+Z undoes date edits, dependency changes, drag operations, row reorders, and table edits to owner, status and notes (100-level stack). 
SKILL.md+1−1
@@ -194,7 +194,7 @@ rather than a category.  ## Features -- **New** button (or Shift+N): create a task or milestone. After a successful create, the next New panel prefills the owner and both dates from the one you just made, so a run of tasks for the same person in the same week is names and nothing else. Name and notes always start empty; status, provenance and phase keep their defaults. It is in-memory for the page session only: a refresh clears it, and it is never written to localStorage, the preferences or the sidecar, so it is never carried to anyone else. Cancel does not update it, only a create that produced a row. Created rows live in the sidecar as `added: [...]`, keyed in a `#new-` namespace that cannot collide with the data file's `#row<n>`, and are merged into every export+- **New** button (or Shift+N): create a task or milestone. After a successful create, the next New panel prefills EVERY field from the one you just made (type, phase, owner, status, start, deadline or milestone date, notes, provenance label). Only the name starts empty, so a run of tasks for the same person in the same lane in the same week is names and nothing else. A provenance left at its default is not remembered as a value, so it keeps following the signed-in identity. It is in-memory for the page session only: a refresh clears it, and it is never written to localStorage, the preferences or the sidecar, so it is never carried to anyone else. Only a create that produced a row updates it: Cancel, Escape and a validation-rejected create all leave it untouched. Created rows live in the sidecar as `added: [...]`, keyed in a `#new-` namespace that cannot collide with the data file's `#row<n>`, and are merged into every export - **Text size** A-minus/A+ in the header: seven steps from 85% to 150%, applies to the chart and the table, persists in `localStorage` as `prefs.fs`. Row and bar geometry scale with the type - **Fit** button beside the cell-size picker: picks the finest grain whose whole span fits the pane, so the plan is fully visible with no horizontal scrolling. Chart-only - **Gantt / Table toggle** pinned first in the header, left of every conditional control so its position never shifts. Two lenses over the same items: the chart answers "when", the table answers "whose, and by when". See The table view below
docs/BUILD-NOTES.md+40
@@ -3,6 +3,46 @@ Decisions taken while building, and the places where the plan and the code disagreed. Newest first. +## Build notes, 2026-08-14 (0.28.1, the New panel remembers everything but the name)++0.18.1 carried the owner and the two dates from the last successful create. The+rest of the panel reset every time, so adding a run of milestones to one lane+meant re-picking the type, the lane, the status and the notes for each one. The+memory now covers every field except the name: type, phase, owner, status, start,+deadline (or the milestone's date), notes and the provenance label.++**The name is the exception, and it is the only one.** It is the field that makes+an item that item; carrying it would offer to create the same row twice.++**The semantics are 0.18.1's, unchanged.** In-memory for the page session, never+localStorage, never prefs, never the sidecar, so it is a fact about this tab in+this sitting and is never handed to anybody else. Only a create that produced a row updates it. Cancel, Escape and a create rejected for a missing name+all leave it exactly as it was, which is measured rather than asserted: the test+changes three fields, cancels, reopens and finds the old values; then blanks the+name, is refused, reopens and finds them still there.++**Provenance is remembered as a CHANGE, not as a value.** Left at its default it+is stored as absent, so the next panel recomputes "Added by <name>" from the+signed-in identity rather than freezing today's name into the session. Typed over,+the typed label is what comes back. Verified both ways.++**The three selects are set before `syncType()` runs**, not after. A carried+milestone has to open with its start field already hidden and its second date+already labelled "date"; setting the value afterwards would flip the panel a frame+late. A carried phase whose lane has since been deleted falls back to the first+rather than leaving the select on a value it cannot display.++**Evidence.** A pristine panel opens at today/today+7, not started, task, empty+name and notes, identity provenance. A create with non-default everything reopens+with all eight fields carried and an empty name. A milestone create reopens as a+milestone, hidden start, "date" label, its date intact. A second page load, with+the created rows still in the sidecar from the first, opens fully pristine, which+is the refresh case proven rather than argued, and `localStorage` is empty. The+uid create flow, `/uid` allocation, the 55-check keyboard sweep, the 35-step+column battery, the dependency and milestone-selection suites are all unchanged;+the fingerprint moves on `todayTag` alone, which is the documented clock-crossing+trap (the 0.28.0 capture was taken on the 12th and this one on the 14th).+ ## Build notes, 2026-08-12 (0.28.0, the export is your escape hatch)  Drew hit the save-conflict banner, went to Export to rescue his edits, and the
gantt.html+35−17
@@ -3621,11 +3621,12 @@ function openItemModal(r,carriedName){ // editing one feel like the same gesture. Everything typed here flows into the // same rendering paths as data-file content, which is where the escaping lives. // What the last successful Create left behind, so adding six tasks for the same-// person in the same week is six names typed and nothing else. In-memory on-// purpose: a refresh clears it, and it is never written to prefs, localStorage or-// the sidecar, so it is a fact about this tab in this sitting and not about the-// plan or about anyone else reading it. Cancel never touches it; only a create that-// actually produced a row does.+// person, in the same lane, in the same week, is six names typed and nothing else.+// EVERY field is carried except the name. In-memory on purpose: a refresh clears+// it, and it is never written to prefs, localStorage or the sidecar, so it is a+// fact about this tab in this sitting and not about the plan or about anyone else+// reading it. Cancel, Escape and a rejected create never touch it; only a create+// that actually produced a row does. let lastCreated=null; function openCreateEditor(){   closeEditor();@@ -3634,16 +3635,20 @@ function openCreateEditor(){   // week's work, and 14 days was never anything more than a round number. Carried   // values from the last creation still win over this, because the user set those.   const plus=addDays(today,7);-  // Name and notes always start empty: they are what makes an item that item, and-  // carrying them over would offer to create the same row twice. Status and-  // provenance keep their own defaults and phase is untouched.+  // The NAME always starts empty. It is the one field that makes an item that item,+  // so carrying it over would offer to create the same row twice; everything else+  // is context the user has already told us once.   const memo=lastCreated||{};   const ownerVal=memo.owner||'';   const startVal=memo.start||today;   const endVal=memo.end||plus;+  const descVal=memo.desc||'';   // Who added it, by name, when there is a name to use. Still an ordinary editable-  // field: this is a default, not a stamp.-  const provVal=USER_NAME?('Added by '+USER_NAME):'Added by hand';+  // field: this is a default, not a stamp. A provenance the user actually TYPED is+  // carried; one they left alone is not stored at all, so the default keeps+  // following the signed-in identity rather than freezing a name into the session.+  const provDefault=USER_NAME?('Added by '+USER_NAME):'Added by hand';+  const provVal=(memo.prov!==undefined&&memo.prov!==null)?memo.prov:provDefault;   editorEl.innerHTML=`<div class="drag-bar"></div>     <div class="batch-title">New item</div>     <div class="de-row">@@ -3659,7 +3664,7 @@ function openCreateEditor(){       <div id="ncStartWrap"><label>start</label><input type="date" id="ncStart" value="${startVal}"></div>       <div><label id="ncEndLabel">deadline</label><input type="date" id="ncEnd" value="${endVal}"></div>     </div>-    <div class="de-note-row"><label>notes</label><input type="text" id="ncDesc" placeholder="Shown on hover and in the table"></div>+    <div class="de-note-row"><label>notes</label><input type="text" id="ncDesc" placeholder="Shown on hover and in the table" value="${esc(descVal)}"></div>     <div class="de-note-row"><label>provenance <span class="de-hint">where this came from</span></label><input type="text" id="ncProv" value="${esc(provVal)}"></div>     <div class="de-actions"><button class="de-btn primary" id="ncCreate">Create</button>       <button class="de-btn secondary" id="ncCancel">Cancel</button></div>@@ -3668,7 +3673,14 @@ function openCreateEditor(){   editorEl.style.left=Math.max(8,Math.min(r.left-220,window.innerWidth-340))+'px';   editorEl.style.top=(r.bottom+8)+'px';   editorEl.classList.add('show');-  $('ncStatus').value='not-started';+  // The three selects carry too, and they are set BEFORE syncType runs: a carried+  // milestone has to open with its start field already hidden and its second date+  // already labelled "date", not flip a frame later. A carried phase that no longer+  // exists (its lane was deleted) falls back to the first, rather than leaving the+  // select on a value it cannot show.+  $('ncType').value=(memo.type==='milestone')?'milestone':'task';+  if(memo.phase!==undefined&&memo.phase!==null&&phases[memo.phase])$('ncPhase').value=String(memo.phase);+  $('ncStatus').value=(memo.status&&STATUS_ORDER.includes(memo.status))?memo.status:'not-started';   const syncType=()=>{     const ms=$('ncType').value==='milestone';     $('ncStartWrap').style.display=ms?'none':'';@@ -3718,11 +3730,17 @@ function openCreateEditor(){     if(st)r.status=st;     if(desc)r.description=desc;     if(prov)r.provenance=prov;-    // Only now, past validation and with a row actually being made. A milestone-    // hides the start field but the input keeps its value, so `start` is whatever-    // was carried in and the remembered start comes through a milestone unchanged,-    // which is what the panel looks like it is doing.-    lastCreated={owner:owner||'', start, end};+    // Only now, past validation and past the uid allocation, with a row actually+    // being made. A milestone hides the start field but the input keeps its value,+    // so `start` is whatever was carried in and the remembered start comes through+    // a milestone unchanged, which is what the panel looks like it is doing.+    //+    // `prov` is stored only when the user changed it. Left at the default, it is+    // recorded as absent so the next panel recomputes the default from the+    // signed-in identity instead of freezing today's name into the session.+    lastCreated={type:isMs?'milestone':'task', phase, owner:owner||'', start, end,+                 status:st||'', desc:desc||'',+                 prov:(prov===provDefault)?undefined:prov};     addedItems.push(r);     insertRow(r);     pushUndo({type:'create',r});
package.json+1−1
@@ -2,7 +2,7 @@   "slug": "adom-gantt",   "title": "Adom Gantt",   "type": "app",-  "version": "0.28.0",+  "version": "0.28.1",   "description": "Interactive Gantt chart viewer for project timelines, with a table view 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. Toggle to the table for every task and milestone as a row with owner, deadline and status, filtered per owner and edited in place. 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+2−2
@@ -2,7 +2,7 @@   "slug": "adom-gantt",   "title": "Adom Gantt",   "type": "app",-  "version": "0.28.0",+  "version": "0.28.1",   "description": "Interactive Gantt chart viewer for project timelines, with a table view 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. Toggle to the table for every task and milestone as a row with owner, deadline and status, filtered per owner and edited in place. 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": {@@ -63,4 +63,4 @@     "name": "Drew Owens",     "email": "[email protected]"   }-}\ No newline at end of file+}