Files
DemonGatanjieuandClaude Opus 5.5 6ff438751c Lay the blueprint out in the task's flow, done steps lit and coming ones dashed
Once an AI handed a blueprint, the flow over the task gets a second line: the
steps kept with their models and picking a base, read from the card and the runs
after it (bases run, pick, whole run), so the AI does nothing more for it. It
shows whether the user was asked or not; a click goes to the step. A job sheet's
run shows how far it is.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 15:45:31 +08:00

75 lines
3.2 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* The flow over an AI task: the stages of the work its steps went through, in the order they
* came (models, combo, prompt, canvas, run, pictures, voice, library: action_catalog.py's), each
* saying whether it is done, running, waiting for the user or failed; a job sheet's run says how far
* it is. A stage leads to its first step in the timeline (`goTo(stepId)`). The stages come from
* ai_task_view.js flowStages. With a blueprint (a plan card) a second line lays out what the AI means
* to do, done steps lit, the coming ones dashed (planFlow), whether the user was asked or not.
*/
import { resolveLocale, translate as t } from './locales.js';
import { flowStages, planFlow } from './ai_task_view.js';
function el(tag, className, text) {
const node = document.createElement(tag);
if (className) node.className = className;
if (text !== undefined) node.textContent = text;
return node;
}
function chip(state, words, first, goTo) {
const node = el('button', `anomalous-ai-flow-stage is-${state}`);
node.type = 'button';
node.title = t(`aiFlowState_${state}`);
node.append(el('span', 'anomalous-ai-flow-mark'), el('span', 'anomalous-ai-flow-words', words));
node.onclick = () => goTo(first);
return node;
}
function row(className, parts) {
const line = el('div', className);
parts.forEach((part, at) => {
if (at) line.append(el('span', 'anomalous-ai-flow-arrow', '›'));
line.append(part);
});
return line;
}
const MODEL_CHARS = 22; // a model's name on a blueprint step, the rest on hover
// A block's title is in both languages ({zh, en}, api/blocks): the one the window shows.
const words = value => (typeof value === 'string' ? value : (resolveLocale() === 'zh' ? value?.zh : value?.en) || value?.en || value?.zh || '');
/** The blueprint's line: its steps as chips, ahead of time too. */
function planRow(plan, goTo) {
const parts = plan.items.map((item) => {
const model = item.model && item.model.length > MODEL_CHARS ? `${item.model.slice(0, MODEL_CHARS - 1)}…` : item.model;
const node = chip(item.state, item.key ? t(item.key) : [words(item.title), model].filter(Boolean).join(' · '), item.first, goTo);
if (model !== item.model) node.title += `
${item.model}`; // the whole name where it was cut
return node;
});
const line = row('anomalous-ai-flow-plan', parts);
const label = el('button', 'anomalous-ai-flow-label', t(`aiFlowPlan_${plan.state}`));
label.type = 'button';
label.onclick = () => goTo(plan.card);
line.prepend(label);
return line;
}
export function flowMap(task, goTo) {
const stages = flowStages(task);
const plan = planFlow(task);
if (!stages.length && !plan) return null;
const box = el('div', 'anomalous-ai-flow');
box.setAttribute('aria-label', t('aiFlow'));
const done = stages.map((item) => {
const node = chip(item.state, item.sheet ? t('aiFlowSheet', item.sheet) : t(`aiStage_${item.stage}`), item.first, goTo);
if (item.count > 1 && !item.sheet) node.append(el('span', 'anomalous-ai-flow-count', `×${item.count}`));
return node;
});
if (done.length) box.append(row('anomalous-ai-flow-done', done));
if (plan) box.append(planRow(plan, goTo));
return box;
}