fix: the last run's illustration no longer sits under an empty form
Some checks failed
Forgejo Docker Build / Build Docker image (push) Blocked by required conditions
Forgejo Docker Build / Deploy to the host (push) Blocked by required conditions
Forgejo Android APK / Root app tests (push) Successful in 56s
Forgejo Docker Build / Root app tests (push) Successful in 49s
Forgejo Android APK / Build signed APK (push) Has been cancelled
Some checks failed
Forgejo Docker Build / Build Docker image (push) Blocked by required conditions
Forgejo Docker Build / Deploy to the host (push) Blocked by required conditions
Forgejo Android APK / Root app tests (push) Successful in 56s
Forgejo Docker Build / Root app tests (push) Successful in 49s
Forgejo Android APK / Build signed APK (push) Has been cancelled
app.js loads a tab's component once and marks it data-loaded, so the DOM survives leaving and returning. Nothing cleared the result area, and an illustration from a previous generation stayed on screen under a blank form as though it were output for a topic nobody had typed. A full page refresh rebuilt the component and cleared it, which is why it looked like a leak that fixed itself. Cleared at the start of a generation, at the start of a modification, and on re-entering the tab — not on the first visit, where there is nothing to clear. Covers the illustration area, the searches line, the image-failure line and the status text. Also documented what a modification can actually change. The deck vocabulary is structural — bullets, compare, table, callout, figure, image, section, title — and none of those carries a colour; the palette is fixed in render_pptx.py and the model never sees it. So "make it yellow" lands on the only field that takes a colour, image_prompt, and yellow figures appear on an otherwise blue deck. That is not modify reaching only the images; it is the model using the one lever the schema gives it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dv6sqaY6Vq3ChZHMem3cnU
This commit is contained in:
parent
31e634e0ce
commit
7305443243
3 changed files with 79 additions and 0 deletions
|
|
@ -253,6 +253,26 @@ download that works without it.
|
|||
of Python. Both pip packages are pinned: unpinned, a rebuild from the same commit
|
||||
could produce different documents.
|
||||
|
||||
## What a modification can and cannot change
|
||||
|
||||
The deck vocabulary describes *structure*, not *style*: `bullets`, `compare`,
|
||||
`table`, `callout`, `figure`, `image`, `section`, `title`. None of them carries a
|
||||
colour. The palette lives in `scripts/render_pptx.py` as fixed constants —
|
||||
`ACCENT` (#2563EB), `INK`, `MUTED`, `RULE`, `PAPER` — and the model never sees
|
||||
them.
|
||||
|
||||
So an instruction like "make it yellow" has nowhere to land. The model applies it
|
||||
to the only colour lever it has: the `image_prompt` text, which produces yellow
|
||||
*figures* on an otherwise blue deck. That is not the modification going only to
|
||||
the images — it is the model using the one field that accepts a colour at all.
|
||||
|
||||
The exception is a `custom` slide, whose shapes take `fill` and `color` (see
|
||||
`slideShapes.js`). A model can restyle those, but it will rarely rebuild an
|
||||
ordinary slide as a custom one just to change a colour.
|
||||
|
||||
If deck styling should be changeable, the honest fix is a theme — an accent
|
||||
colour on the resource, passed to the renderer — not a wider slide vocabulary.
|
||||
|
||||
## The image library
|
||||
|
||||
Library → **Images** is every picture the account has generated, across all
|
||||
|
|
|
|||
|
|
@ -14,6 +14,10 @@
|
|||
document.addEventListener('tabChanged', function (e) {
|
||||
if (!e.detail || e.detail.tab !== 'myresources') return;
|
||||
if (!inited) { init(); inited = true; }
|
||||
// Reopening the tab is a fresh start, not a resumed one. The DOM survives
|
||||
// the visit, so without this the previous run's illustration and status sit
|
||||
// under an empty form as though they belonged to it.
|
||||
else clearResults();
|
||||
loadLibrary();
|
||||
});
|
||||
|
||||
|
|
@ -162,10 +166,25 @@
|
|||
el.style.color = tone === 'bad' ? 'var(--red)' : tone === 'good' ? 'var(--green)' : 'var(--g600)';
|
||||
}
|
||||
|
||||
// Everything the last run left on screen. The tab keeps its DOM between
|
||||
// visits — app.js loads a component once and marks it data-loaded — so an
|
||||
// illustration from a previous generation stayed visible under an empty form
|
||||
// when the tab was reopened, looking like output for a topic nobody had
|
||||
// typed. It cleared on a full page refresh and only then, which is why it
|
||||
// read as a leak.
|
||||
function clearResults() {
|
||||
['mr-images', 'mr-searches', 'mr-image-failures'].forEach(function (id) {
|
||||
var el = document.getElementById(id);
|
||||
if (el) el.textContent = '';
|
||||
});
|
||||
status('');
|
||||
}
|
||||
|
||||
function runGenerate() {
|
||||
var topic = (document.getElementById('mr-topic') || {}).value || '';
|
||||
if (!topic.trim()) { status('Enter a topic first.', 'bad'); return; }
|
||||
|
||||
clearResults();
|
||||
var btn = document.getElementById('btn-mr-generate');
|
||||
if (btn) { btn.disabled = true; btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Generating...'; }
|
||||
status('Searching the library and writing. This takes a moment.');
|
||||
|
|
@ -605,6 +624,7 @@
|
|||
if (!instructions) return say('Say what to change.', 'bad');
|
||||
|
||||
if (btn) { btn.disabled = true; btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Applying'; }
|
||||
clearResults();
|
||||
say('Rewriting…');
|
||||
fetch('/api/my-resources/' + encodeURIComponent(id) + '/refine', {
|
||||
method: 'POST',
|
||||
|
|
|
|||
39
test/my-resources-stale-results.test.js
Normal file
39
test/my-resources-stale-results.test.js
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
// The tab keeps its DOM between visits (app.js loads a component once and marks
|
||||
// it data-loaded), so whatever the last run rendered stays on screen. An
|
||||
// illustration from a previous generation sat under an empty form as though it
|
||||
// were output for a topic nobody had typed, and only a full page refresh
|
||||
// cleared it.
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const ui = fs.readFileSync(path.join(__dirname, '..', 'public/js/myResources.js'), 'utf8');
|
||||
|
||||
test('a new generation starts from a clean result area', () => {
|
||||
assert.match(ui, /function clearResults\(\)/);
|
||||
const gen = ui.slice(ui.indexOf('function runGenerate'), ui.indexOf('function reportSearches'));
|
||||
assert.match(gen, /clearResults\(\);/, 'generate clears before it runs');
|
||||
});
|
||||
|
||||
test('a modification clears it too', () => {
|
||||
const start = ui.indexOf('function runModify');
|
||||
const mod = ui.slice(start, ui.indexOf('function renderRow', start));
|
||||
assert.match(mod, /clearResults\(\);/);
|
||||
});
|
||||
|
||||
test('reopening the tab clears it, but the first visit does not need to', () => {
|
||||
const tab = ui.slice(ui.indexOf("e.detail.tab !== 'myresources'"), ui.indexOf('var available'));
|
||||
assert.match(tab, /else clearResults\(\);/,
|
||||
'only on a revisit — on the first visit there is nothing to clear and init() has not run');
|
||||
});
|
||||
|
||||
test('clearing covers every container a run writes into', () => {
|
||||
const fn = ui.slice(ui.indexOf('function clearResults'), ui.indexOf('function runGenerate'));
|
||||
for (const id of ['mr-images', 'mr-searches', 'mr-image-failures']) {
|
||||
assert.ok(fn.includes(id), id + ' is not cleared');
|
||||
}
|
||||
assert.match(fn, /status\(''\)/, 'the status line is part of the debris');
|
||||
// textContent, not innerHTML: these hold model-derived text and image nodes.
|
||||
assert.match(fn, /el\.textContent = ''/);
|
||||
});
|
||||
Loading…
Reference in a new issue