src/ui/mascot.js
Rama main · réplica de lectura del repositorio original.
// The guide mascot: the logo character drawn as inline SVG from the brand
// vectors (body, cream eyes, sleep cap from the night scene). Decorative only;
// guide.css styles it and hides it in forced colors.
const SVG = "http://www.w3.org/2000/svg";
export const MASCOT_POSES = Object.freeze(["idle", "look", "happy", "thinking", "worried", "waiting"]);
function draw(name, attributes, parent) {
const node = document.createElementNS(SVG, name);
for (const [key, value] of Object.entries(attributes)) node.setAttribute(key, value);
parent?.append(node);
return node;
}
export function createMascot() {
const svg = draw("svg", {
class: "rm-mascot", viewBox: "-8 -34 140 168", "aria-hidden": "true", focusable: "false", "data-pose": "idle",
});
const figure = draw("g", { class: "rm-mascot__figure" }, svg);
draw("path", { class: "rm-mascot__body", d: "M0 126V47C0 17 18 0 48 0s48 17 48 47v79z" }, figure);
const eyes = draw("g", { class: "rm-mascot__eyes" }, figure);
for (const cx of [30, 66]) draw("circle", { class: "rm-mascot__eye", cx, cy: 47, r: 10 }, eyes);
draw("path", { class: "rm-mascot__arcs rm-mascot__happy", d: "M20 51Q30 37 40 51M56 51Q66 37 76 51" }, figure);
draw("path", { class: "rm-mascot__arcs rm-mascot__closed", d: "M21 45Q30 53 39 45M57 45Q66 53 75 45" }, figure);
const cap = draw("g", { class: "rm-mascot__cap" }, figure);
draw("path", {
class: "rm-mascot__line rm-mascot__cloth",
d: "M4 12Q10-24 48-28Q86-31 98-5L111 10Q90 8 82-5Q73-13 66-6L91 12Z",
}, cap);
draw("path", { class: "rm-mascot__arcs", d: "M22 8Q26-12 43-23M48 8Q50-5 59-12" }, cap);
draw("rect", { class: "rm-mascot__cream rm-mascot__line", x: 1, y: 8, width: 94, height: 13, rx: 6.5 }, cap);
draw("circle", { class: "rm-mascot__cream rm-mascot__line", cx: 111, cy: 11, r: 7 }, cap);
const warning = draw("g", { class: "rm-mascot__warning" }, svg);
draw("path", { class: "rm-mascot__mark", d: "M114 62 131 92H97Z" }, warning);
draw("path", { class: "rm-mascot__mark-line", d: "M114 72v9M114 86v1" }, warning);
return svg;
}
// look shifts the eyes up to 4 units toward (dx, dy); thinking looks up.
export function setMascotPose(svg, pose, { dx = 0, dy = 0 } = {}) {
const name = MASCOT_POSES.includes(pose) ? pose : "idle";
const clamp = (value) => Math.round(Math.max(-4, Math.min(4, Number(value) || 0)));
const shift = name === "look" ? `${clamp(dx)} ${clamp(dy)}` : name === "thinking" ? "0 -4" : "0 0";
const eyes = svg.querySelector(".rm-mascot__eyes");
if (svg.getAttribute("data-pose") !== name) svg.setAttribute("data-pose", name);
if (eyes.getAttribute("transform") !== `translate(${shift})`) eyes.setAttribute("transform", `translate(${shift})`);
for (const eye of svg.querySelectorAll(".rm-mascot__eye")) eye.setAttribute("r", name === "worried" ? "7" : "10");
}