Files
DemonGatanjieuandClaude Opus 5.5 0de22f8f46 AI workbench: how to change a marked picture, a quicker task list, "AI edits a copy", lit source rows
- Marker: "make a repaint mask" becomes How to change it (leave it to the AI, only the
  marked areas, the whole picture anew); the AI reads the choice first as the user's, the
  timeline row says it, the repaint brush picks "only the marked areas".
- Task list: folded, it stays laid out out of sight and slides in at once from its button
  or the page's left edge; redraws make anew only the rows that changed (sync_rows.js).
  A picked task shows at once from the copy read before or read ahead on hover
  (ai_task_cache.js), then what changed; activity entries follow instead of holding it up.
  Stage pictures load as thumbnails first, the file itself only when shown larger.
- Header: "Own tab" is "AI edits a copy ▾", a two-way menu with what each means (kept open
  across redraws), and "Show the copy".
- Materials: the Source list lights the hovered row and ticks the one shown (a later rule
  had hidden both), tooltips only for cut names, arrows and Enter (ui_ai_facet_menu.js).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 13:08:46 +08:00

56 lines
1.8 KiB
JavaScript

/**
* The AI workbench's tasks as last read from the server (ui_ai_workbench.js), so picking one is
* quick: a task read before shows at once from here while the page reads it again, and the pointer
* resting on a task in the list reads that task ahead. One read per task at a time; the latest few
* tasks are kept.
*/
const KEEP = 8;
const AHEAD_MS = 90; // the pointer passing over the list reads nothing
const kept = new Map(); // task id -> its record as last read, the latest last
const reading = new Map(); // task id -> the read on its way
let aheadTimer = null;
function keep(id, task) {
kept.delete(id);
if (!task) return;
kept.set(id, task);
while (kept.size > KEEP) kept.delete(kept.keys().next().value);
}
/** Task `id` from the server (null when there is none); kept for keptTask. */
export function readTask(id) {
if (reading.has(id)) return reading.get(id);
const read = fetch(`/anomalous/ai_tasks/${encodeURIComponent(id)}`)
.then((response) => {
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
})
.then((data) => {
keep(id, data.task || null);
return data.task || null;
})
.finally(() => reading.delete(id));
reading.set(id, read);
return read;
}
/** Task `id` as last read, or null. */
export const keptTask = id => kept.get(id) || null;
/** The pointer rests on task `id` in the list: read it ahead (once it has stayed a moment). */
export function readAhead(id) {
clearTimeout(aheadTimer);
aheadTimer = setTimeout(() => { if (!reading.has(id)) readTask(id).catch(() => {}); }, AHEAD_MS);
}
export function stopReadAhead() {
clearTimeout(aheadTimer);
}
/** A task removed: not shown again from here. */
export function forgetTask(id) {
kept.delete(id);
}