The message box stays at the bottom of the task column however the timeline scrolls. Over the input, what the message carries (ui_ai_tray.js): pictures with ✎ to mark up (sent on their own) and ×, chips for what "@" names; gallery pictures are attached, not typed in as "@name". Beside it the gallery and materials buttons with their counts; the input grows (Enter sends, Shift+Enter breaks the line) and pictures alone may be sent. "New steps below" appears when the AI adds steps while the user reads further up; one's own message scrolls into view. Pictures a message carries show as small image cards with the usual tools. The ready page keeps the big gallery and what is ready; the box below takes its pictures. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
65 lines
2.4 KiB
JavaScript
65 lines
2.4 KiB
JavaScript
/**
|
||
* What a message to the AI carries, over its input (ui_ai_message.js; the refs are
|
||
* ui_ai_mention.js's): the pictures as small pictures, each with ✎ to mark it up for the AI
|
||
* (ui_ai_marker.js, sent on its own) and × to leave it out, then what "@" names (models, combos,
|
||
* nodes…) as chips with ×. Nothing shows while nothing is attached.
|
||
*/
|
||
|
||
import { translate as t } from './locales.js';
|
||
import { icon } from './ui_ai_icons.js';
|
||
import { thumbUrl } from './ui_ai_gallery_pick.js';
|
||
|
||
function el(tag, className, text) {
|
||
const node = document.createElement(tag);
|
||
if (className) node.className = className;
|
||
if (text !== undefined) node.textContent = String(text);
|
||
return node;
|
||
}
|
||
|
||
function tool(className, name, label, run) {
|
||
const node = el('button', className);
|
||
node.type = 'button';
|
||
node.title = label;
|
||
node.setAttribute('aria-label', label);
|
||
node.append(icon(name, 12));
|
||
node.onclick = (event) => {
|
||
event.stopPropagation();
|
||
run();
|
||
};
|
||
return node;
|
||
}
|
||
|
||
function picture(ref, ctx) {
|
||
const tile = el('div', 'anomalous-ai-tray-picture');
|
||
tile.title = ref.id;
|
||
const img = el('img');
|
||
img.src = thumbUrl(ref.item.image || {});
|
||
img.alt = '';
|
||
img.onerror = () => tile.classList.add('is-gone');
|
||
tile.append(img, tool('anomalous-ai-tray-x', 'close', t('aiTrayRemove'), () => ctx.remove(ref)));
|
||
if (ctx.mark) {
|
||
const mark = el('button', 'anomalous-ai-tray-mark');
|
||
mark.type = 'button';
|
||
mark.title = t('aiTrayMarkHint');
|
||
mark.append(icon('pencil', 11), el('span', '', t('aiTrayMark')));
|
||
mark.onclick = () => ctx.mark(ref);
|
||
tile.append(mark);
|
||
}
|
||
return tile;
|
||
}
|
||
|
||
function chip(ref, ctx) {
|
||
const node = el('span', 'anomalous-ai-mention-chip');
|
||
node.append(ctx.lead(ref.item), el('span', 'anomalous-ai-mention-chip-name', ref.label),
|
||
tool('anomalous-ai-mention-chip-x', 'close', t('aiMentionRemove'), () => ctx.remove(ref)));
|
||
return node;
|
||
}
|
||
|
||
/** `refs` into `tray`. `ctx`: {remove(ref), mark(ref) (or none), lead(item) (a chip's small picture or icon)}. */
|
||
export function drawTray(tray, refs, ctx) {
|
||
const pictures = refs.filter(ref => ref.kind === 'pictures');
|
||
const others = refs.filter(ref => ref.kind !== 'pictures');
|
||
tray.hidden = !refs.length;
|
||
tray.replaceChildren(...pictures.map(ref => picture(ref, ctx)), ...others.map(ref => chip(ref, ctx)));
|
||
}
|