From a3732122a461c9aa654401f6e2c54f168eedaad5 Mon Sep 17 00:00:00 2001 From: Agando Date: Mon, 9 Nov 2026 15:36:11 +0100 Subject: fix(products): ship the reviewed 1.0.1 builds — the 1.0.0 pdf2txt never ran MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A review of all three products in Chromium found defects that only execution exposes. pdf2txt 1.0.0 DID NOT RUN. Both the packaged file and the live demo contained a duplicated const declaration inside fetchModels, so the inline script threw a SyntaxError at parse time and no handler was ever bound: no extraction, no translation, no config. Proven by extracting both inline scripts from the committed file and from spaces/chatstudio/pdf2txt.html and running node --check (SyntaxError: Identifier 'config' has already been declared). The two published versions of this product were dead on arrival. pdf2txt 1.0.1 also stops writing the API key and the whole extracted document to localStorage (sessionStorage for the key, nothing for the text), keeps paragraphs (hasEOL), splits chunks on word boundaries, reports a non-JSON 200 or a missing response field instead of writing "undefined" into the editor, re-enables Translate after a run, resets stopRequested, stops a workflow on a failed step, warns on a PDF with no text layer, recognises .PDF, fixes an unterminated mobile media query, stops the Fullscreen button covering line 1, and makes the drop zone and all 39 controls reachable and named. shiftplanner 1.0.1 fixes seven demonstrated defects: the settings modal recursed until the stack overflowed, "+ Worker" prefilled the last edited worker id and overwrote that person, date maths was off by one west of UTC (local-noon Date formatted with toISOString), same-day and overnight shift overlaps were never flagged, the generator sliders never reached state.settings, CSV export could execute a formula and broke on a quote, and the header never wrapped (390 px viewport gave a 1139 px document). Plus four data-integrity guards: unparseable saved plans are copied to a recovery key instead of silently overwritten, wrong-typed fields are normalised, unknown top-level fields survive a save, and a storage event syncs another tab. chat-widget 1.0.1 closes a critical attribute-injection XSS. escapeHtml() round-tripped through textContent, which does not escape a double quote, so a reply containing https://x.test/"onclick="alert(1)// produced a real and fired alert(1) when clicked. Proven both ways: the 1.0.0 file fired a dialog, the fixed one renders only href/target/rel and stays silent. The launcher also covered the Send button at 390 px, so the widget could not be used on a phone at all. Also in this commit: - Landing pages and demos are generated (build-product-pages.py). The "Try the demo" button used to point at spaces/chatstudio/pdf2txt.html (the dead copy above) and at a v2.1 shiftplanner with no AI panel: a demo that was a different build than the ZIP. Demos are now built into /demos// from products//, with the ../vendor/ prefix rewritten, and the publish wrapper regenerates them before every publish. - publish-public-repo.py refuses to publish a subtree with uncommitted changes. It reads git, so it silently republished the old tip while rc was 0 for every slug. --- CHANGELOG.md | 25 ++++ VERSION | 2 +- src/index.html | 430 ++++++++++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 409 insertions(+), 48 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3babe97..425418c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,31 @@ All notable changes to this product tree are recorded here. This file describes the packaged product, not the history of the five source copies (that history is documented in `docs/CANONICAL.md`). +## 1.0.1 — 2026-11-09 + +Seven defects found by driving the app in Chromium, plus three data-integrity guards. + +- **Settings never opened**: `openSettings()` called `openModal('settingsModal')`, whose hook called + `openSettings()` again — `RangeError: Maximum call stack size exceeded` on every click. The cycle is + broken at the source; the hook is the single caller. +- **"+ Worker" overwrote the last person you edited.** `openWorkerModal()` never cleared `#workerId`, + and the header button opened the modal directly, bypassing the reset. Adding a worker now starts blank. +- **Dates were off by one west of UTC**: `getPrevDay()` built a local-noon `Date` and formatted it with + `toISOString()`. Day arithmetic is done on the date string in local time; verified in Pacific/Auckland, + America/Los_Angeles and UTC, including a month boundary. +- **Overlaps were never detected.** The check only looked at the previous calendar day, so 22:00–06:00 + followed by 02:00–10:00 the same day passed. Shifts are compared as absolute intervals now, including + overnight spill into the next day. +- **The generator sliders did not persist**: conflict detection kept using 5/11 while the wizard showed + the new values. Both are written to `settings` on change and before generating. +- **CSV export could execute formulas.** Cells are quoted with doubled inner quotes, and a leading `'` + is added to any value starting with `= + - @`, tab or CR. +- **The header never wrapped**: at 390 px the document became 1139 px wide. It wraps now; 390 px stays 390 px. +- Data integrity: an unparseable saved plan is copied to `shiftplanner_v2_recovery_` instead of + being silently overwritten, wrong-typed fields are normalised instead of blanking the page, unknown + top-level fields survive a save, `saveToStorage()` is guarded, and a `storage` listener reacts to + another tab. + ## [1.0.0] - 2026-11-09 ### Added diff --git a/VERSION b/VERSION index 3eefcb9..7dea76e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.0 +1.0.1 diff --git a/src/index.html b/src/index.html index 86ff355..6a6d871 100644 --- a/src/index.html +++ b/src/index.html @@ -461,11 +461,22 @@ .toggle-sidebar { display: block !important; } .header-right .btn .btn-label { display: none; } .ai-panel { width: min(var(--ai-panel-w), 100vw); } + /* Header must wrap instead of pushing the document wider than the phone. */ + .header { flex-wrap: wrap; row-gap: 6px; } + .header-left, .nav-center, .header-right { flex-wrap: wrap; } + .header-right { justify-content: flex-end; } } @media (max-width: 600px) { .header { padding: 8px 12px; gap: 6px; } + .header-left, .nav-center, .header-right { gap: 5px; } + .logo { font-size: 1.05rem; } + .logo-ai { padding: 1px 5px; font-size: 0.65rem; margin-left: 4px; } + .stat-chip { font-size: 0.6rem; padding: 2px 5px; } + .header-divider { display: none; } + .btn { padding: 5px 9px; } + .ai-toggle-btn { padding: 5px 9px; font-size: 0.72rem; } .nav-btn { padding: 5px 10px; font-size: 0.8rem; } - .month-display { min-width: 140px; font-size: 1rem; } + .month-display { min-width: 120px; font-size: 0.9rem; } .calendar-grid { grid-template-columns: repeat(7, minmax(120px, 1fr)); } .day-cell { min-height: 130px; } } @@ -790,14 +801,14 @@ Max Consecutive Days 5 - +
Min Rest Hours 11
- +