← Commit history

Docs: the what-lives-where diagram on the main page, updated for 0.77.1 (one Owner filter for both lenses, field-by-field saves, catch-ups, the order kept grouped by section), with its HTML source and a render script; docs/RELEASING.md

Drew Owens ·759bca6b05 · ·parent 2b9687b
7 files changed +176−3
README.md+4
@@ -417,6 +417,10 @@ with the edits if you choose to discard them. </tr> </table> +![What lives where: only this tab, this browser, and the plan's server](client-vs-server/where-it-lives.png)++*Your view stays in your browser; the plan is on the server and the same for everyone. [What lives where](client-vs-server/README.md) sorts every setting.*+ ## Keyboard shortcuts  | Key | Action |
client-vs-server/README.md+4−2
@@ -40,7 +40,7 @@ tab of the same plan in the same browser shares them; another computer starts fr | The lens: Gantt, Table, Analytics, Setup | Analytics and the owner cards on Setup are hidden for teammates | | Days, weeks or months; text size; light or dark; milestone style; Today line | | | Critical path highlight | Only the highlight. Auto-schedule, which moves dates, is a plan change |-| Name column width; table column widths, sort and owner pick | |+| Name column width; table column widths and sort | | | Which legend chips are folded away | The "18 tags" and "50 sections" chips |  A browser that has never opened the plan starts from the plan's own defaults: the@@ -68,7 +68,9 @@ Saved by the adom-gantt server and shared with everyone the plan is shared with. Edits save about half a second after you stop, as the fields you changed and nothing else (0.77.0), the way a Google Doc does. Two people editing different fields of one row both keep their change; the same field at the same moment goes to the later one, in every tab,-and the person whose value was replaced is told.+and the person whose value was replaced is told. The server keeps the row order grouped by+section, the way every screen shows it, and a tab that falls behind or finds it disagrees+with the server fetches the whole plan and puts itself right (0.77.1).  ## Live, never saved 
client-vs-server/where-it-lives.htmladded+119
@@ -0,0 +1,119 @@+<!doctype html>+<!--+  The "what lives where" diagram (client-vs-server/where-it-lives.png, shown on the main+  README and on this folder's sub-page). This file is its source: edit it when what lives+  where changes, then re-render the PNG with the test Chrome, at 1400 px wide and twice the+  pixels (see docs/RELEASING.md):++    node test/render-diagram.mjs++  Brand fonts: Familjen Grotesk (titles), Satoshi (text), JetBrains Mono (labels). They are+  used from the system when installed, and the first two from Google Fonts otherwise.+-->+<html lang="en">+<head>+<meta charset="utf-8">+<meta name="viewport" content="width=1400">+<title>What lives where</title>+<link rel="preconnect" href="https://fonts.googleapis.com">+<link href="https://fonts.googleapis.com/css2?family=Familjen+Grotesk:wght@500;600;700&family=JetBrains+Mono:wght@400&display=swap" rel="stylesheet">+<style>+  :root {+    --bg: #0d1117; --card: #161b22; --line: #262c36; --border: #30363d;+    --text: #e6edf3; --text2: #9aa4b2; --muted: #6e7681;+    --purple: #9b7bff; --purple-line: #7c5cff; --teal: #1fc7b6; --amber: #e3b341;+  }+  * { box-sizing: border-box; }+  html, body { margin: 0; background: var(--bg); }+  body { width: 1400px; font-family: 'Satoshi', Arial, sans-serif; color: var(--text); }+  .frame { padding: 44px 44px 40px; }+  h1 { font-family: 'Familjen Grotesk', sans-serif; font-weight: 700; font-size: 28px; margin: 0 0 6px; letter-spacing: -0.2px; }+  .lede { font-size: 16.5px; color: var(--text2); margin: 0 0 30px; }+  .lede b { color: var(--text); }+  .cols { display: grid; grid-template-columns: 300px 372px 1fr 440px; gap: 0 18px; align-items: stretch; }+  .card { background: var(--card); border: 1px solid var(--border); border-radius: 12px; padding: 22px 22px 20px; }+  .card.browser { border-color: var(--purple-line); }+  .card.server { border-color: var(--teal); box-shadow: 0 0 0 1px rgba(31,199,182,.12), 0 0 28px rgba(31,199,182,.10); }+  .kicker { font-family: 'JetBrains Mono', monospace; font-size: 12.5px; letter-spacing: 2.6px; color: var(--muted); text-transform: uppercase; margin-bottom: 10px; }+  .browser .kicker { color: var(--purple); }+  .server .kicker { color: var(--teal); }+  h2 { font-family: 'Familjen Grotesk', sans-serif; font-weight: 700; font-size: 21px; margin: 0 0 6px; }+  .sub { font-size: 14px; color: var(--text2); margin: 0 0 14px; line-height: 1.4; }+  ul { list-style: none; margin: 0; padding: 0; }+  li { position: relative; padding: 9px 0 9px 22px; border-top: 1px solid var(--line); font-size: 15px; line-height: 1.38; }+  li::before { content: ''; position: absolute; left: 3px; top: 15px; width: 7px; height: 7px; border-radius: 2px; background: #5b6370; }+  .browser li::before { background: var(--purple); }+  .server li::before { background: var(--teal); }+  li b { font-weight: 700; }+  .key { font-family: 'JetBrains Mono', monospace; font-size: 12px; color: var(--amber); border: 1px solid rgba(227,179,65,.55); border-radius: 4px; padding: 1px 5px; margin-left: 6px; }+  .foot { font-size: 12.5px; color: var(--text2); margin: 14px 0 0; line-height: 1.45; }+  .arrows { display: flex; flex-direction: column; justify-content: center; gap: 86px; text-align: center; color: var(--teal); font-size: 13px; line-height: 1.38; }+  .arrows > div { max-width: 150px; margin: 0 auto; }+  .arrow { display: block; margin: 6px auto; width: 74px; height: 12px; }+  .live { margin-top: 18px; border: 1px dashed rgba(31,199,182,.7); border-radius: 12px; padding: 16px 20px; display: flex; align-items: center; gap: 18px; }+  .live .kicker { color: var(--teal); margin: 0; white-space: nowrap; }+  .live p { margin: 0; font-size: 15px; line-height: 1.45; }+  .chip { display: inline-flex; align-items: center; justify-content: center; width: 21px; height: 21px; border-radius: 50%; font-family: 'Familjen Grotesk', sans-serif; font-size: 9.5px; font-weight: 700; color: #0d1117; vertical-align: -5px; margin: 0 1px; }+  .so { margin-top: 16px; background: #1b1830; border-left: 3px solid var(--purple-line); border-radius: 0 10px 10px 0; padding: 13px 16px; font-size: 15px; line-height: 1.45; }+</style>+</head>+<body>+<div class="frame" id="diagram">+  <h1>Where each thing lives in Adom Gantt</h1>+  <p class="lede">Two kinds of state: how <b>you</b> are looking at the plan stays in your browser; the <b>plan itself</b> lives on the server and is the same for everyone.</p>+  <div class="cols">+    <div class="card tab">+      <div class="kicker">Only this tab</div>+      <h2>Gone on reload</h2>+      <p class="sub">Nobody else sees it. Not even your other tabs.</p>+      <ul>+        <li>The section, Milestone and Dependencies chips in the legend (hide or dim)</li>+        <li>What you have selected, the open editor, where you have scrolled</li>+        <li>Undo history, your own changes only<span class="key">Ctrl+Z</span></li>+        <li>"See what the team sees" preview</li>+      </ul>+    </div>+    <div class="card browser">+      <div class="kicker">This browser</div>+      <h2>Remembered, never shared</h2>+      <p class="sub">Your view only. Kept for this plan in this browser, across reloads.</p>+      <ul>+        <li><b>Collapse all</b> and each section's fold</li>+        <li><b>Owner</b> (one filter for the chart and the table), Current, Overdue and tag filters</li>+        <li>Lens: Gantt, Table, Analytics, Setup</li>+        <li>Days / weeks / months, text size, light or dark, milestone style, Today line</li>+        <li>Critical path highlight</li>+        <li>Name column width; table columns and sort</li>+        <li>Which legend chips are folded away</li>+      </ul>+      <p class="foot">Every tab of this plan in this browser shares these. Another computer or browser starts from the plan's defaults.</p>+    </div>+    <div class="arrows">+      <div>your changes go out field by field, in about a second+        <svg class="arrow" viewBox="0 0 74 12"><path d="M2 6h62" stroke="currentColor" stroke-width="2" fill="none"/><path d="M62 1l10 5-10 5z" fill="currentColor"/></svg></div>+      <div><svg class="arrow" viewBox="0 0 74 12"><path d="M72 6H10" stroke="currentColor" stroke-width="2" fill="none"/><path d="M12 1L2 6l10 5z" fill="currentColor"/></svg>+        everyone else's arrive live; a tab that falls behind fetches the whole plan</div>+    </div>+    <div class="card server">+      <div class="kicker">The plan's server</div>+      <h2>The plan: same for everyone</h2>+      <p class="sub">Saved by the adom-gantt server, on every teammate's screen within a second.</p>+      <ul>+        <li><b>Every task and milestone:</b> name, dates, owner, status, notes, tags, effort, provenance link, dependencies</li>+        <li><b>Sections:</b> names, colours, leads, order; the row order, kept grouped by section; tag colours</li>+        <li>Anything that moves dates: dragging a bar, editing a row, <b>Auto-schedule</b> (owner only)</li>+        <li><b>Project dates and the working calendar</b> on Setup (owner only)</li>+        <li>Adding, deleting, pasting and importing rows</li>+        <li>The changelog (who changed what), daily backups</li>+        <li>Sharing and the Google Chat digest (owner only)</li>+      </ul>+    </div>+  </div>+  <div class="live">+    <div class="kicker">Live, never saved</div>+    <p>Who has the plan open <span class="chip" style="background:#1fc7b6">DO</span><span class="chip" style="background:#e3b341">AB</span>, and which rows each person has selected, the coloured rows with initials. The server passes it on and forgets it when they close the tab.</p>+  </div>+  <div class="so"><b>So:</b> when you press Collapse all, filter to Unassigned, or switch to the table, only your screen changes. When you drag a bar, change an owner or press Auto-schedule, everyone's does.</div>+</div>+</body>+</html>
client-vs-server/where-it-lives.png
⋯ 1 unchanged line ⋯
docs/BUILD-NOTES.md+1−1
@@ -1,7 +1,7 @@ # adom-gantt build notes  Decisions taken while building, and the places where the plan and the code-disagreed. Newest first.+disagreed. Newest first. The release checklist is [RELEASING.md](RELEASING.md).  ## Build notes, 2026-11-03 (0.77.1, sections and the row order, after an afternoon on the team plan) 
docs/RELEASING.mdadded+21
@@ -0,0 +1,21 @@+# Releasing adom-gantt++The list for every version. What changed and why goes in [BUILD-NOTES.md](BUILD-NOTES.md).++1. **Version.** Bump `version` in `package.json` and `page.json`.+2. **Gates, one after another.** `node test/server_test.mjs`, `node test/collab_test.mjs`,+   then `node test/ui_test.mjs`. Never two at once: they share test ports. When several+   people or agents run suites on one machine, wrap each run in the same `flock` lock file.+3. **What lives where.** If the release changes what stays in a tab, in the browser or on+   the server (a filter, a setting, how saving or catching up works), update+   `client-vs-server/where-it-lives.html` and the tables in `client-vs-server/README.md`,+   re-render the PNG with `node test/render-diagram.mjs`, and look at the PNG. The main+   README shows it under "Your view and the shared plan: what lives where"; check it still+   does.+4. **Build notes.** A new entry at the top of `docs/BUILD-NOTES.md`: what changed, why,+   and the test counts.+5. **Publish.** `adom-wiki pkg info adom/adom-gantt` first, in case another session+   published; then `adom-wiki pkg publish --org adom -y`, and `adom-wiki repo push` for+   anything the package leaves out. Check a fresh clone and the live page.+6. **Deploy.** Only with the plan owner's go. A changed `server.js` needs a restart; a+   changed page needs only the files. Either way, everybody with the plan open reloads once.
test/render-diagram.mjsadded+27
@@ -0,0 +1,27 @@+// Renders client-vs-server/where-it-lives.html to where-it-lives.png: the "what lives where"+// diagram on the main README and the client-vs-server sub-page. 1400 px wide at twice the+// pixels, in the suite's own Chrome. Run it whenever the HTML changes (docs/RELEASING.md).+//   node test/render-diagram.mjs+import puppeteer from './node_modules/puppeteer-core/lib/esm/puppeteer/puppeteer-core.js';+import { readdirSync } from 'node:fs';+import { join, dirname } from 'node:path';+import { fileURLToPath, pathToFileURL } from 'node:url';+const HERE = dirname(fileURLToPath(import.meta.url));+const CHROME = process.env.CHROME_PATH || (() => {+  const dir = join(process.env.HOME, '.cache/puppeteer/chrome');+  const dirs = readdirSync(dir).filter(d => d.startsWith('linux-')).sort((a, b) => a.split('-')[1].localeCompare(b.split('-')[1], undefined, { numeric: true }));+  return join(dir, dirs.at(-1), 'chrome-linux64', 'chrome');+})();+const src = join(HERE, '..', 'client-vs-server', 'where-it-lives.html');+const out = join(HERE, '..', 'client-vs-server', 'where-it-lives.png');+const browser = await puppeteer.launch({ executablePath: CHROME, headless: 'new', args: ['--no-sandbox', '--disable-dev-shm-usage', '--hide-scrollbars'] });+try {+  const page = await browser.newPage();+  await page.setViewport({ width: 1400, height: 900, deviceScaleFactor: 2 });+  await page.goto(pathToFileURL(src).href, { waitUntil: 'networkidle0', timeout: 30000 }).catch(() => {});+  await page.evaluate(() => document.fonts.ready);+  const el = await page.$('#diagram');+  await el.screenshot({ path: out });+  const box = await el.boundingBox();+  console.log(`OK: wrote ${out} (${Math.round(box.width * 2)}x${Math.round(box.height * 2)})`);+} finally { await browser.close(); }