Files
ausboss-ComfyUI-AusBoss/js/shared/script_colours_math.mjs
T
ausboss 9ba82c45b4 H3 Prompt Writer: timeline, colored words, Sound and Music boxes
- Words light up as you type (@1, times, cuts, quotes, labels), with @
  autocomplete and a ? cheat sheet
- Timeline bar shows every picture a line names; drag a block to reorder,
  drag its edge to set its time, click an edge for a cut, click a block to
  jump to its line
- Sound and Music boxes (new optional inputs, appended) fill H3's
  overall_soundscape and non_diegetic_music
- Pictures and video as chips with their jobs; Length, Camera and Look stay
  plain rows so they can still be wired
- Help page and changelog updated; tests for the colors, autocomplete,
  timeline edits and the new inputs
2026-11-03 11:09:26 -06:00

52 lines
2.3 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Pure decisions behind js/shared/script_colours.mjs: which parts of a script
// get which colour. No DOM, so tests run it under node:test.
const escape = (text) => text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
const span = (kind, text) => `<span class="${kind}">${escape(text)}</span>`;
const TOKENS = /(@\d)|("[^"\n]*"?)/g;
const LABEL = /^\s*(sound|music|camera|look):/i;
function lineMarkup(line, wired) {
let head = "";
let rest = line;
const time = rest.match(/^(\s*)(\d+(?:\.\d+)?s:)/);
const key = rest.match(/^(\s*)(sound|music|camera|look):/i);
if (time) { head = escape(time[1]) + span("time", time[2]); rest = rest.slice(time[0].length); }
else if (key) { head = escape(key[1]) + span("key", `${key[2]}:`); rest = rest.slice(key[0].length); }
let out = head;
let at = 0;
for (const match of rest.matchAll(TOKENS)) {
out += escape(rest.slice(at, match.index));
if (match[1]) {
const number = Number(match[1].slice(1));
out += span(number >= 1 && number <= 4 && wired(number) ? "at" : "at unknown", match[1]);
} else {
out += span("say", match[2]);
}
at = match.index + match[0].length;
}
return out + escape(rest.slice(at));
}
// The coloured copy of a script, as HTML. wired(number) says whether a
// picture is plugged in under that @ number; one that isn't shows amber.
// A blank line between two lines is a cut, unless the script says no cuts or
// what follows is a label line.
export function scriptMarkup(text, wired = () => true) {
const lines = String(text ?? "").split("\n");
const cuts = !/\bno cuts\b/i.test(String(text ?? ""));
const filled = lines.map((line) => line.trim() !== "");
// A sound:, music:, camera: or look: line on its own is for the whole clip, not a new shot.
const label = lines.map((line) => LABEL.test(line));
const html = lines.map((line, index) => {
if (filled[index]) return lineMarkup(line, wired);
const before = filled.slice(0, index).some(Boolean);
const next = filled.indexOf(true, index + 1);
const after = next >= 0 && !label[next];
const firstBlank = index > 0 && filled[index - 1];
return cuts && before && after && firstBlank ? '<span class="cut">​</span>' : escape(line);
});
// A box that ends on a new line shows one more empty line.
return html.join("\n") + (String(text ?? "").endsWith("\n") ? " " : "");
}