From aa047fe94ce510f8a50cff78f6ab97ec130bb19e Mon Sep 17 00:00:00 2001 From: paoloredis Date: Wed, 4 Nov 2026 08:50:05 +0100 Subject: [PATCH 1/2] DOC-7256: Keep the Iris tour's Redis Search and Agent labels sharp in Chrome On the tour's first two steps, and on the Lens step's rows that dim every service, the stage blends three versions of the drawing with "darken". Chrome, and other Chromium browsers such as Brave, draw images stacked with mix-blend-mode at a lower resolution, so "darken" kept the dimmer pixels and the REDIS SEARCH and AGENT labels came out thin and faint. Firefox and Safari weren't affected. Each of the stage's two stacks is now one element with the versions as its background images, blended with background-blend-mode, which Chrome draws at full resolution. Every step blends the same versions as before. Co-Authored-By: Claude Opus 5.5 --- static/css/iris-demo.css | 7 +++---- static/js/iris-demo.js | 35 +++++++++++++++-------------------- 2 files changed, 18 insertions(+), 24 deletions(-) diff --git a/static/css/iris-demo.css b/static/css/iris-demo.css index bac94fa077..96e555ea8d 100644 --- a/static/css/iris-demo.css +++ b/static/css/iris-demo.css @@ -18,11 +18,10 @@ .iris:not(.iris-preview) .iris-stagewrap { max-width: 75%; margin-left: auto; margin-right: auto; } @media (max-width: 640px) { .iris:not(.iris-preview) .iris-stagewrap { max-width: 100%; } } .iris-stage { position: relative; aspect-ratio: 613.5 / 423.5; /* VIEW in iris-demo.js */ border-radius: 11px; overflow: hidden; background: #0D212C; } -/* The four published versions of the drawing, stacked twice. In a stack, a step shows the versions it needs and the - script sets how they blend; isolation keeps that blend inside the stack. The new stack fades in over the old one. */ +/* The published versions of the drawing, in two stacks. In a stack, a step shows the versions it needs as background + images, and the script sets how they blend. The new stack fades in over the old one. */ .iris-artset { position: absolute; inset: 0; isolation: isolate; opacity: 0; transition: opacity .45s; } -.iris .iris-art { position: absolute; inset: 0; width: 100%; height: 100%; max-width: none; margin: 0; opacity: 0; pointer-events: none; user-select: none; } -.iris .iris-art.is-on { opacity: 1; } +.iris .iris-art { position: absolute; margin: 0; background-size: 100% 100%; background-repeat: no-repeat; pointer-events: none; user-select: none; } .iris-over { position: absolute; inset: 0; width: 100%; height: 100%; display: block; } .iris-route { fill: none; stroke: none; } .iris-hit { fill: transparent; cursor: pointer; } diff --git a/static/js/iris-demo.js b/static/js/iris-demo.js index 3466b044c2..15bf114ca0 100644 --- a/static/js/iris-demo.js +++ b/static/js/iris-demo.js @@ -674,22 +674,19 @@ var box = el("div", "iris-stage"); box.setAttribute("role", "img"); /* Two stacks of the versions. A step blends its versions in the stack that isn't showing, then - fades that stack in over the other, so a half-faded version never blends the wrong way. */ + fades that stack in over the other, so a half-faded version never blends the wrong way. + Each stack is one element with the versions as its background images, blended with + background-blend-mode. Stacked images blended with mix-blend-mode would do the same, but Chrome + draws them at a lower resolution, and "darken" then thins the labels' text. */ var sets = [0, 1].map(function () { - var set = el("div", "iris-artset"), layers = {}; - ["retriever", "cache", "memory"].forEach(function (k) { - var img = el("img", "iris-art"); - img.style.left = vx(0); img.style.top = vy(0); img.style.width = vw(ART_W); img.style.height = vh(ART_H); - img.src = artBase + ART[k]; - img.alt = ""; - img.decoding = "async"; - img.draggable = false; - set.appendChild(img); - layers[k] = img; - }); + var set = el("div", "iris-artset"), art = el("div", "iris-art"); + art.style.left = vx(0); art.style.top = vy(0); art.style.width = vw(ART_W); art.style.height = vh(ART_H); + set.appendChild(art); box.appendChild(set); - return { el: set, layers: layers }; + return { el: set, art: art }; }); + /* Load every version up front, so the first step that uses one doesn't wait for it. */ + var preload = ["retriever", "cache", "memory"].map(function (k) { var i = new Image(); i.src = artBase + ART[k]; return i; }); var cur = sets[1]; var NS = "http://www.w3.org/2000/svg"; var over = document.createElementNS(NS, "svg"); @@ -750,13 +747,11 @@ /* Show some versions of the drawing: the first one as is, the rest blended onto it. */ function compose(keys, blend) { - var first = true, next = cur === sets[0] ? sets[1] : sets[0]; - ["retriever", "cache", "memory"].forEach(function (k) { - var on = keys.indexOf(k) >= 0, img = next.layers[k]; - img.classList.toggle("is-on", on); - img.style.mixBlendMode = on && !first ? (blend || "lighten") : "normal"; - if (on) first = false; - }); + var next = cur === sets[0] ? sets[1] : sets[0]; + var shown = ["retriever", "cache", "memory"].filter(function (k) { return keys.indexOf(k) >= 0; }); + next.art.style.backgroundImage = shown.map(function (k) { return 'url("' + artBase + ART[k] + '")'; }).join(", "); + next.art.style.backgroundBlendMode = shown.length > 1 ? (blend || "lighten") : "normal"; + next.art.dataset.versions = shown.join(" "); // what the stack shows, for the tests /* The showing stack stays put underneath; the new one starts clear on top and fades in. */ cur.el.style.transition = "none"; cur.el.style.opacity = "1"; From 05ac917a19805fb6d5e422489fa5db37376e9f48 Mon Sep 17 00:00:00 2001 From: paoloredis Date: Wed, 4 Nov 2026 11:06:16 +0100 Subject: [PATCH 2/2] DOC-7256: Draw the Iris tour's Redis Search and Agent labels as text, like Maya's The tour draws the labels it adds, such as Maya's, as text, but the REDIS SEARCH and AGENT labels came from the drawing, so they never matched exactly. The stage now draws those two as text too, in the same font, size, and color, over the drawing's own boxes. It covers those boxes 2 units larger all round, so their outlines can't show at the edges. Co-Authored-By: Claude Opus 5.5 --- static/js/iris-demo.js | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/static/js/iris-demo.js b/static/js/iris-demo.js index 15bf114ca0..1d5bb7936d 100644 --- a/static/js/iris-demo.js +++ b/static/js/iris-demo.js @@ -564,18 +564,26 @@ Each version of the drawing shows its lit service's label box, and data-integration.svg also shows the Context Retriever box. The stage draws the same box, with the current name, at the same place, and shows it whenever a version in `in` is lit (see compose()). Boxes are x, y, width, height, copied from - langcache.svg and context-retriever.svg. */ + langcache.svg and context-retriever.svg. + The Redis and agent labels are lit in every version, so the stage draws them as text too, always shown + (`always`), with their boxes copied from agent-memory.svg. They then look exactly like the labels the stage + adds, such as Maya's. The agent's box is as tall as Maya's, so its text sits as Maya's does (`base`). */ var RELABEL = { cache: { box: [471.578, 518.156, 99.2506, 24.4832], lines: ["AGENT PLAYBOOKS"], in: ["cache"] }, - retriever: { box: [498.461, 235.889, 99.2506, 31.2039], lines: ["CONTEXT", "GATEWAY"], in: ["retriever"] } + retriever: { box: [498.461, 235.889, 99.2506, 31.2039], lines: ["CONTEXT", "GATEWAY"], in: ["retriever"] }, + redis: { box: [370.684, 287.974, 84.6654, 32.7885], lines: ["REDIS SEARCH", "(IN REDIS DB)"], always: true }, + agent: { box: [632.932, 436.729, 43.1543, 24.751], lines: ["AGENT"], always: true, rx: 3.78037, stroke: 0.840082, base: 3.69 } }; function buildRelabels(NS) { var out = {}; Object.keys(RELABEL).forEach(function (k) { var R = RELABEL[k], b = R.box, g = document.createElementNS(NS, "g"); - var mid = b[1] + b[3] / 2, gap = 10, first = mid - (R.lines.length - 1) * gap / 2 + 2.95; // 2.95: half the glyphs' cap height - g.setAttribute("class", "iris-relabel"); - g.innerHTML = '' + + var mid = b[1] + b[3] / 2, gap = 10, first = mid - (R.lines.length - 1) * gap / 2 + (R.base || 2.95); // 2.95: half the glyphs' cap height + g.setAttribute("class", "iris-relabel" + (R.always ? " is-on" : "")); + /* An always-shown box first covers the drawing's own box, 2 units larger all round: the drawing can land up to + a device pixel away from the overlay, so its outline would otherwise show at the edges. */ + g.innerHTML = (R.always ? '' : "") + + '' + R.lines.map(function (t, i) { return '' + esc(t) + ""; }).join(""); @@ -766,8 +774,10 @@ cur = next; /* Agent Playbooks' disc is lit where its version is, as its label is. */ pbDisc.classList.toggle("is-lit", keys.indexOf("cache") >= 0 && (keys.length === 1 || blend !== "darken")); - /* A label shows where a version that has it is lit; "darken" keeps only what every version lights, which no label is. */ + /* A label shows where a version that has it is lit; "darken" keeps only what every version lights, which no service's + label is. The Redis and agent labels always show. */ Object.keys(relabels).forEach(function (k) { + if (RELABEL[k].always) return; var shown = RELABEL[k].in.some(function (v) { return keys.indexOf(v) >= 0; }); relabels[k].classList.toggle("is-on", shown && (keys.length === 1 || blend !== "darken")); });