From 1bbb74500902aa83c41a12baecb4a88fb4d665f8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 24 Oct 2026 10:04:24 +0000 Subject: [PATCH 1/5] feat(muix): implement icicle-basic Adopted from 5b2bcb3e5e4dc5b3685a669c07cf3ab1b9918f69 (5b2bcb3e5e4dc5b3685a669c07cf3ab1b9918f69), written by gemini-4-argon outside the pipeline. --- .../implementations/javascript/muix.tsx | 686 ++++++++++-------- 1 file changed, 372 insertions(+), 314 deletions(-) diff --git a/plots/icicle-basic/implementations/javascript/muix.tsx b/plots/icicle-basic/implementations/javascript/muix.tsx index 4a97be8458c..6b13c8e6a02 100644 --- a/plots/icicle-basic/implementations/javascript/muix.tsx +++ b/plots/icicle-basic/implementations/javascript/muix.tsx @@ -2,362 +2,420 @@ // icicle-basic: Basic Icicle Chart // Library: muix 7.29.1 | JavaScript 22.23.2 // Quality: 86/100 | Created: 2026-09-05 -import { ChartContainer } from "@mui/x-charts/ChartContainer"; + +import { ChartsContainer } from "@mui/x-charts/ChartsContainer"; import { useDrawingArea } from "@mui/x-charts/hooks"; const t = window.ANYPLOT_TOKENS; -const title = "icicle-basic · javascript · muix · anyplot.ai"; -const titleFontSize = Math.round(30 * Math.min(1, 67 / title.length)); +// Constants +const FONT = '"Plus Jakarta Sans", "Inter", -apple-system, BlinkMacSystemFont, sans-serif'; +const MONO = '"JetBrains Mono", "SFMono-Regular", Menlo, monospace'; +const ROOT_FILL = "#6E6A61"; +const FIXED_DARK_TEXT = "#1A1A17"; +const FIXED_LIGHT_TEXT = "#FAF8F1"; +const TINT_STEP = 0.24; +const ROW_GUTTER = 5; +const COL_GUTTER = 2; +const LABEL_FONT_SIZE = [20, 16, 15, 14]; +const SUBLABEL_FONT_SIZE = 14; +const CHAR_WIDTH_RATIO = 0.56; -// --- Data: a file-system hierarchy (name/children/value in KB) -- one of the -// spec's listed applications. 4 top-level directories, 11 subdirectories, 25 -// files -- 41 nodes total, well inside the spec's 10-100 node range. Only -// leaves carry an explicit size; directory sizes are the sum of their -// contents, exactly like `du` on a real file system. ----------------------- -const TREE = { - name: "repo/", - children: [ - { - name: "node_modules/", - children: [ - { - name: "react/", - children: [ - { name: "index.js", value: 180 }, - { name: "package.json", value: 60 }, - { name: "README.md", value: 80 }, - ], - }, - { - name: "webpack/", - children: [ - { name: "webpack.js", value: 140 }, - { name: "loader.js", value: 90 }, - { name: "config.js", value: 50 }, - ], - }, - { - name: "typescript/", - children: [ - { name: "tsc.js", value: 120 }, - { name: "lib.d.ts", value: 70 }, - { name: "compiler.js", value: 60 }, - ], - }, - { - name: "eslint/", - children: [ - { name: "index.js", value: 55 }, - { name: "rules.js", value: 45 }, - ], - }, - ], - }, - { - name: "src/", - children: [ - { - name: "components/", - children: [ - { name: "Button.tsx", value: 90 }, - { name: "Modal.tsx", value: 110 }, - { name: "Chart.tsx", value: 80 }, - ], - }, - { - name: "utils/", - children: [ - { name: "format.ts", value: 100 }, - { name: "api.ts", value: 90 }, - ], - }, - { - name: "styles/", - children: [ - { name: "theme.css", value: 75 }, - { name: "globals.css", value: 55 }, - ], - }, - ], - }, - { - name: "tests/", - children: [ - { - name: "unit/", - children: [ - { name: "button.test.ts", value: 100 }, - { name: "utils.test.ts", value: 90 }, - ], - }, - { - name: "integration/", - children: [ - { name: "api.test.ts", value: 85 }, - { name: "flow.test.ts", value: 75 }, - ], - }, - ], - }, - { - name: "docs/", - children: [ - { - name: "guides/", - children: [ - { name: "getting-started.md", value: 70 }, - { name: "deployment.md", value: 60 }, - ], - }, - { - name: "reference/", - children: [{ name: "api-reference.md", value: 70 }], - }, - ], - }, - ], -}; +// Columns: set these to your data's column names +const ID = "Node path"; // role: id +const NAME = "Node name"; // role: name +const PARENT = "Parent path"; // role: parent +const VALUE = "Size (KB)"; // role: value -// Bottom-up: a directory's size is the sum of what it contains (leaves carry -// their own explicit `value`). -function computeValue(node) { - if (!node.children) return node.value; - node.value = node.children.reduce((sum, c) => sum + computeValue(c), 0); - return node.value; -} -computeValue(TREE); +// Data: replace this block with your own records +const data: Array<{ + [ID]: string; + [NAME]: string; + [PARENT]: string; + [VALUE]: number; +}> = [ + { [ID]: "repo/", [NAME]: "repo/", [PARENT]: "", [VALUE]: 0 }, + // Top-level directories + { [ID]: "repo/node_modules/", [NAME]: "node_modules/", [PARENT]: "repo/", [VALUE]: 0 }, + { [ID]: "repo/src/", [NAME]: "src/", [PARENT]: "repo/", [VALUE]: 0 }, + { [ID]: "repo/tests/", [NAME]: "tests/", [PARENT]: "repo/", [VALUE]: 0 }, + { [ID]: "repo/docs/", [NAME]: "docs/", [PARENT]: "repo/", [VALUE]: 0 }, + // Subdirectories under node_modules/ + { [ID]: "repo/node_modules/react/", [NAME]: "react/", [PARENT]: "repo/node_modules/", [VALUE]: 0 }, + { [ID]: "repo/node_modules/webpack/", [NAME]: "webpack/", [PARENT]: "repo/node_modules/", [VALUE]: 0 }, + { [ID]: "repo/node_modules/typescript/", [NAME]: "typescript/", [PARENT]: "repo/node_modules/", [VALUE]: 0 }, + { [ID]: "repo/node_modules/eslint/", [NAME]: "eslint/", [PARENT]: "repo/node_modules/", [VALUE]: 0 }, + // Subdirectories under src/ + { [ID]: "repo/src/components/", [NAME]: "components/", [PARENT]: "repo/src/", [VALUE]: 0 }, + { [ID]: "repo/src/utils/", [NAME]: "utils/", [PARENT]: "repo/src/", [VALUE]: 0 }, + { [ID]: "repo/src/styles/", [NAME]: "styles/", [PARENT]: "repo/src/", [VALUE]: 0 }, + // Subdirectories under tests/ + { [ID]: "repo/tests/unit/", [NAME]: "unit/", [PARENT]: "repo/tests/", [VALUE]: 0 }, + { [ID]: "repo/tests/integration/", [NAME]: "integration/", [PARENT]: "repo/tests/", [VALUE]: 0 }, + // Subdirectories under docs/ + { [ID]: "repo/docs/guides/", [NAME]: "guides/", [PARENT]: "repo/docs/", [VALUE]: 0 }, + { [ID]: "repo/docs/reference/", [NAME]: "reference/", [PARENT]: "repo/docs/", [VALUE]: 0 }, + // Files under react/ + { [ID]: "repo/node_modules/react/index.js", [NAME]: "index.js", [PARENT]: "repo/node_modules/react/", [VALUE]: 180 }, + { [ID]: "repo/node_modules/react/package.json", [NAME]: "package.json", [PARENT]: "repo/node_modules/react/", [VALUE]: 60 }, + { [ID]: "repo/node_modules/react/README.md", [NAME]: "README.md", [PARENT]: "repo/node_modules/react/", [VALUE]: 80 }, + // Files under webpack/ + { [ID]: "repo/node_modules/webpack/webpack.js", [NAME]: "webpack.js", [PARENT]: "repo/node_modules/webpack/", [VALUE]: 140 }, + { [ID]: "repo/node_modules/webpack/loader.js", [NAME]: "loader.js", [PARENT]: "repo/node_modules/webpack/", [VALUE]: 90 }, + { [ID]: "repo/node_modules/webpack/config.js", [NAME]: "config.js", [PARENT]: "repo/node_modules/webpack/", [VALUE]: 50 }, + // Files under typescript/ + { [ID]: "repo/node_modules/typescript/tsc.js", [NAME]: "tsc.js", [PARENT]: "repo/node_modules/typescript/", [VALUE]: 120 }, + { [ID]: "repo/node_modules/typescript/lib.d.ts", [NAME]: "lib.d.ts", [PARENT]: "repo/node_modules/typescript/", [VALUE]: 70 }, + { [ID]: "repo/node_modules/typescript/compiler.js", [NAME]: "compiler.js", [PARENT]: "repo/node_modules/typescript/", [VALUE]: 60 }, + // Files under eslint/ + { [ID]: "repo/node_modules/eslint/index.js", [NAME]: "index.js", [PARENT]: "repo/node_modules/eslint/", [VALUE]: 55 }, + { [ID]: "repo/node_modules/eslint/rules.js", [NAME]: "rules.js", [PARENT]: "repo/node_modules/eslint/", [VALUE]: 45 }, + // Files under components/ + { [ID]: "repo/src/components/Button.tsx", [NAME]: "Button.tsx", [PARENT]: "repo/src/components/", [VALUE]: 90 }, + { [ID]: "repo/src/components/Modal.tsx", [NAME]: "Modal.tsx", [PARENT]: "repo/src/components/", [VALUE]: 110 }, + { [ID]: "repo/src/components/Chart.tsx", [NAME]: "Chart.tsx", [PARENT]: "repo/src/components/", [VALUE]: 80 }, + // Files under utils/ + { [ID]: "repo/src/utils/format.ts", [NAME]: "format.ts", [PARENT]: "repo/src/utils/", [VALUE]: 100 }, + { [ID]: "repo/src/utils/api.ts", [NAME]: "api.ts", [PARENT]: "repo/src/utils/", [VALUE]: 90 }, + // Files under styles/ + { [ID]: "repo/src/styles/theme.css", [NAME]: "theme.css", [PARENT]: "repo/src/styles/", [VALUE]: 75 }, + { [ID]: "repo/src/styles/globals.css", [NAME]: "globals.css", [PARENT]: "repo/src/styles/", [VALUE]: 55 }, + // Files under unit/ + { [ID]: "repo/tests/unit/button.test.ts", [NAME]: "button.test.ts", [PARENT]: "repo/tests/unit/", [VALUE]: 100 }, + { [ID]: "repo/tests/unit/utils.test.ts", [NAME]: "utils.test.ts", [PARENT]: "repo/tests/unit/", [VALUE]: 90 }, + // Files under integration/ + { [ID]: "repo/tests/integration/api.test.ts", [NAME]: "api.test.ts", [PARENT]: "repo/tests/integration/", [VALUE]: 85 }, + { [ID]: "repo/tests/integration/flow.test.ts", [NAME]: "flow.test.ts", [PARENT]: "repo/tests/integration/", [VALUE]: 75 }, + // Files under guides/ + { [ID]: "repo/docs/guides/getting-started.md", [NAME]: "getting-started.md", [PARENT]: "repo/docs/guides/", [VALUE]: 70 }, + { [ID]: "repo/docs/guides/deployment.md", [NAME]: "deployment.md", [PARENT]: "repo/docs/guides/", [VALUE]: 60 }, + // Files under reference/ + { [ID]: "repo/docs/reference/api-reference.md", [NAME]: "api-reference.md", [PARENT]: "repo/docs/reference/", [VALUE]: 70 }, +]; -// Fixed neutral grey for the root/total band -- unlike `t.ink`, this stays -// pixel-identical across light and dark renders, so every data band -// (including root) is theme-stable, not just the top-level branches. -const ROOT_FILL = "#6B6A63"; +// Plot: reads only data and the constants above +const unitMatch = VALUE.match(/\(([^)]+)\)/); +const valueUnit = unitMatch ? unitMatch[1] : ""; +const unitSuffix = valueUnit ? ` ${valueUnit}` : ""; -// Each top-level directory owns one Imprint hue; every descendant inherits -// its ancestor's hue so a branch stays recognizable at any depth (the -// spec's "color by hierarchy level or category" note). -function assignBranch(node, color) { - node.branchColor = color; - node.children?.forEach((c) => assignBranch(c, color)); +interface IcicleNode { + id: string; + name: string; + parentId: string; + value: number; + depth: number; + childrenIds: string[]; + branchColor: string; + x0Norm: number; + x1Norm: number; + fill: string; + textFill: string; + parentValue: number; } -assignBranch(TREE, ROOT_FILL); // root itself reads as the neutral/total anchor -TREE.children.forEach((dir, i) => assignBranch(dir, t.palette[i % t.palette.length])); -// --- Icicle (partition) layout: each depth is a full-width horizontal band; -// within a band, a node's horizontal span is proportional to its value -// within its parent's span. Root at top, children stacked below -- the -// spec's "horizontal orientation, root at top" note. ------------------------ -function partition(node, x0, x1, depth) { - node.x0 = x0; - node.x1 = x1; - node.depth = depth; - if (!node.children) return; - let cx = x0; - const total = node.value; - node.children.forEach((child) => { - const w = total > 0 ? (child.value / total) * (x1 - x0) : 0; - partition(child, cx, cx + w, depth + 1); - cx += w; +const nodeById = new Map(); +for (let i = 0; i < data.length; i += 1) { + const row = data[i]; + nodeById.set(row[ID], { + id: row[ID], + name: row[NAME], + parentId: row[PARENT], + value: row[VALUE], + depth: 0, + childrenIds: [], + branchColor: ROOT_FILL, + x0Norm: 0, + x1Norm: 1, + fill: ROOT_FILL, + textFill: FIXED_LIGHT_TEXT, + parentValue: 0, }); } -function flatten(node, acc) { - acc.push(node); - node.children?.forEach((c) => flatten(c, acc)); - return acc; +let rootId = ""; +for (let i = 0; i < data.length; i += 1) { + const row = data[i]; + const node = nodeById.get(row[ID])!; + if (!node.parentId) { + rootId = node.id; + } else { + const parent = nodeById.get(node.parentId); + if (parent) parent.childrenIds.push(node.id); + } } -const DEPTH_COUNT = 4; // root, directory, subdirectory, file -const TINT_STEP = 0.28; // lighter per depth level below the branch's directory - -// --- Color: contrast-aware text over an arbitrary tinted fill, same -// relative-luminance technique used for every other custom-drawn anyplot -// chart so labels stay legible regardless of hue or tint. ------------------- -function hexToRgb(hex) { - const n = parseInt(hex.slice(1), 16); - return [(n >> 16) & 255, (n >> 8) & 255, n & 255]; -} -function rgbToHsl(r, g, b) { - r /= 255; - g /= 255; - b /= 255; - const max = Math.max(r, g, b); - const min = Math.min(r, g, b); - const l = (max + min) / 2; - if (max === min) return [0, 0, l]; - const d = max - min; - const s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - let h; - if (max === r) h = (g - b) / d + (g < b ? 6 : 0); - else if (max === g) h = (b - r) / d + 2; - else h = (r - g) / d + 4; - return [h / 6, s, l]; -} -function hueToRgb(p, q, tIn) { - let tt = tIn; - if (tt < 0) tt += 1; - if (tt > 1) tt -= 1; - if (tt < 1 / 6) return p + (q - p) * 6 * tt; - if (tt < 1 / 2) return q; - if (tt < 2 / 3) return p + (q - p) * (2 / 3 - tt) * 6; - return p; -} -function hslToRgb(h, s, l) { - if (s === 0) { - const v = Math.round(l * 255); - return [v, v, v]; +// Topological BFS order from root +const bfsOrder: IcicleNode[] = []; +const queue: string[] = [rootId]; +while (queue.length > 0) { + const curId = queue.shift()!; + const cur = nodeById.get(curId)!; + bfsOrder.push(cur); + for (let c = 0; c < cur.childrenIds.length; c += 1) { + const child = nodeById.get(cur.childrenIds[c])!; + child.depth = cur.depth + 1; + queue.push(child.id); } - const q = l < 0.5 ? l * (1 + s) : l + s - l * s; - const p = 2 * l - q; - return [Math.round(hueToRgb(p, q, h + 1 / 3) * 255), Math.round(hueToRgb(p, q, h) * 255), Math.round(hueToRgb(p, q, h - 1 / 3) * 255)]; -} -function tintRgb(hex, factor) { - const [r, g, b] = hexToRgb(hex); - const [h, s, l] = rgbToHsl(r, g, b); - return hslToRgb(h, s, l + (0.94 - l) * factor); -} -function relLuminanceRgb([r, g, b]) { - const srgb = [r, g, b].map((v) => v / 255).map((v) => (v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4))); - return 0.2126 * srgb[0] + 0.7152 * srgb[1] + 0.0722 * srgb[2]; } -// Fixed, theme-independent ink/paper pair -- the data fills themselves don't -// change between themes, so the contrast decision must depend only on the -// fill's own luminance, never on `t.ink`/`t.pageBg` (which flip meaning -// between light and dark theme and previously produced near-invisible text -// on the leaf-level tints in dark mode). -const FIXED_DARK_TEXT = "#1A1A17"; -const FIXED_LIGHT_TEXT = "#FFFDF6"; -function textColorForRgb(rgb) { - return relLuminanceRgb(rgb) > 0.45 ? FIXED_DARK_TEXT : FIXED_LIGHT_TEXT; + +// Bottom-up sum of leaf sizes +for (let i = bfsOrder.length - 1; i >= 0; i -= 1) { + const node = bfsOrder[i]; + if (node.childrenIds.length > 0) { + let sum = 0; + for (let c = 0; c < node.childrenIds.length; c += 1) { + sum += nodeById.get(node.childrenIds[c])!.value; + } + node.value = sum; + } } -// --- Sizing + label fitting ------------------------------------------------- -const MARGIN = { top: 96, right: 24, bottom: 20, left: 24 }; -const ROW_GUTTER = 5; -const COL_GUTTER = 2; -const LABEL_FONT_SIZE = [22, 17, 15, 13]; // by depth -- 15/13 for the two deepest levels (was 14/12) so labels hold up better at mobile thumbnail scale -const CHAR_WIDTH_RATIO = 0.56; +// Top-down branch color assignment and normalized [0, 1] partition +const rootNode = nodeById.get(rootId)!; +const topDirs = rootNode.childrenIds.map((id, idx) => { + const dir = nodeById.get(id)!; + dir.branchColor = t.palette[idx]; + return dir; +}); -function fmtKB(v) { - return `${v.toLocaleString()} KB`; -} -function fitsLabel(w, h, text, fontSize) { - return w - 10 >= text.length * fontSize * CHAR_WIDTH_RATIO && h >= fontSize + 4; -} +for (let i = 0; i < bfsOrder.length; i += 1) { + const node = bfsOrder[i]; + if (node.depth >= 2) { + const parent = nodeById.get(node.parentId)!; + node.branchColor = parent.branchColor; + } + if (node.parentId) { + node.parentValue = nodeById.get(node.parentId)!.value; + } else { + node.parentValue = node.value; + } -function IcicleRect({ x, y, w, h, fill, textFill, label, sublabel, fontSize, tooltip }) { - const showLabel = label && fitsLabel(w, h, label, fontSize); - const showSub = showLabel && sublabel && h >= fontSize * 2 + 8 && fitsLabel(w, h, sublabel, fontSize - 2); - return ( - - - {tooltip} - - {showLabel && ( - - {label} - - )} - {showSub && ( - - {sublabel} - - )} - - ); + let cx = node.x0Norm; + const span = node.x1Norm - node.x0Norm; + for (let c = 0; c < node.childrenIds.length; c += 1) { + const child = nodeById.get(node.childrenIds[c])!; + const w = node.value > 0 ? (child.value / node.value) * span : 0; + child.x0Norm = cx; + child.x1Norm = cx + w; + cx += w; + } + + // Compute tinted RGB fill and relative luminance contrast text + const hex = node.depth === 0 ? ROOT_FILL : node.branchColor; + const hexInt = parseInt(hex.slice(1), 16); + const baseR = (hexInt >> 16) & 255; + const baseG = (hexInt >> 8) & 255; + const baseB = hexInt & 255; + const tintFactor = node.depth === 0 ? 0 : (node.depth - 1) * TINT_STEP; + const r = Math.round(baseR + (248 - baseR) * tintFactor); + const g = Math.round(baseG + (246 - baseG) * tintFactor); + const b = Math.round(baseB + (238 - baseB) * tintFactor); + node.fill = `rgb(${r}, ${g}, ${b})`; + + const sR = + r / 255 <= 0.03928 + ? r / 255 / 12.92 + : Math.pow((r / 255 + 0.055) / 1.055, 2.4); + const sG = + g / 255 <= 0.03928 + ? g / 255 / 12.92 + : Math.pow((g / 255 + 0.055) / 1.055, 2.4); + const sB = + b / 255 <= 0.03928 + ? b / 255 / 12.92 + : Math.pow((b / 255 + 0.055) / 1.055, 2.4); + const lum = 0.2126 * sR + 0.7152 * sG + 0.0722 * sB; + node.textFill = lum > 0.18 ? FIXED_DARK_TEXT : FIXED_LIGHT_TEXT; } -function Icicle() { - // Drawing area comes from MUI X's own DrawingProvider (via ChartContainer's - // `margin` prop), the documented composition primitive for a custom mark -- - // MUI X community has no native Icicle/partition chart type. +const maxDepth = Math.max(...bfsOrder.map((n) => n.depth)); +const DEPTH_COUNT = maxDepth + 1; +const leafCount = bfsOrder.filter((n) => n.childrenIds.length === 0).length; + +function IcicleLayer() { const { left, top, width, height } = useDrawingArea(); - partition(TREE, left, left + width, 0); - const nodes = flatten(TREE, []); const rowH = height / DEPTH_COUNT; return ( - {nodes.map((node) => { - const x = node.x0 + COL_GUTTER / 2; - const w = Math.max(0, node.x1 - node.x0 - COL_GUTTER); + {bfsOrder.map((node) => { + const x = left + node.x0Norm * width + COL_GUTTER / 2; + const w = Math.max(0, (node.x1Norm - node.x0Norm) * width - COL_GUTTER); const y = top + node.depth * rowH + ROW_GUTTER / 2; const h = rowH - ROW_GUTTER; - const fontSize = LABEL_FONT_SIZE[node.depth]; + const fontSize = + LABEL_FONT_SIZE[ + Math.min(node.depth, LABEL_FONT_SIZE.length - 1) + ]; - if (node.depth === 0) { - return ( - - ); - } + const labelText = + node.depth === 0 + ? `${node.name} · ${node.value.toLocaleString()}${unitSuffix} total` + : node.name; + const subText = + node.depth === 0 ? "" : `${node.value.toLocaleString()}${unitSuffix}`; + const pct = + node.parentValue > 0 + ? ((node.value / node.parentValue) * 100).toFixed(0) + : "100"; + + const showLabel = + w - 12 >= labelText.length * fontSize * CHAR_WIDTH_RATIO && + h >= fontSize + 6; + const showSub = + showLabel && + subText.length > 0 && + h >= fontSize + SUBLABEL_FONT_SIZE + 12 && + w - 12 >= subText.length * SUBLABEL_FONT_SIZE * CHAR_WIDTH_RATIO; - const tintFactor = (node.depth - 1) * TINT_STEP; - const rgb = tintRgb(node.branchColor, tintFactor); - const fill = `rgb(${rgb[0]}, ${rgb[1]}, ${rgb[2]})`; - const parentValue = findParentValue(TREE, node); - const pct = parentValue > 0 ? ((node.value / parentValue) * 100).toFixed(0) : "0"; return ( - + + + + {node.id}: {node.value.toLocaleString()} + {unitSuffix} ({pct}% of parent) + + + {showLabel && ( + + {labelText} + + )} + {showSub && ( + + {subText} + + )} + ); })} ); } -// Walks the tree to find `target`'s parent value, used for the "% of parent" -// tooltip -- flatten() discards parent links, so this is the cheap way back. -function findParentValue(node, target) { - if (!node.children) return node.value; - if (node.children.includes(target)) return node.value; - for (const c of node.children) { - const found = findParentValue(c, target); - if (found !== undefined) return found; - } - return undefined; -} - -// --- Chart (default-exported component -- the harness mounts it) ---------- -// ChartContainer supplies the SVG root and theme context; its -// `margin` prop drives the DrawingProvider that Icicle() reads back via -// useDrawingArea(), so the title/plot split uses MUI X's own layout system -// rather than a parallel hand-rolled offset. The bands themselves are laid -// out in absolute pixel space via partition() above, so no axis/scale is -// needed -- xAxis/yAxis are omitted entirely. export default function Chart() { const { width, height } = window.ANYPLOT_SIZE; + const headerHeight = 84; + const chartHeight = height - headerHeight; return ( - - - {title} - - - Repository file sizes by directory depth · width ∝ size (KB) - - - +
+
+
+
+ icicle-basic · javascript · muix · anyplot.ai +
+
+ {DEPTH_COUNT} levels · {leafCount} leaves ·{" "} + {rootNode.value.toLocaleString()} + {unitSuffix} total · width ∝ {VALUE} +
+
+ +
+ {topDirs.map((dir) => ( +
+ + + {dir.name}{" "} + + ({dir.value.toLocaleString()} + {unitSuffix}) + + +
+ ))} +
+
+ + + + +
); } From 2d798a53dcd8590d7e45cdd9d3fddcc75e817c1d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 24 Oct 2026 10:04:27 +0000 Subject: [PATCH 2/5] chore(muix): add metadata for icicle-basic --- .../implementations/javascript/muix.tsx | 2 +- .../metadata/javascript/muix.yaml | 267 +----------------- 2 files changed, 13 insertions(+), 256 deletions(-) diff --git a/plots/icicle-basic/implementations/javascript/muix.tsx b/plots/icicle-basic/implementations/javascript/muix.tsx index 6b13c8e6a02..2a8bf247203 100644 --- a/plots/icicle-basic/implementations/javascript/muix.tsx +++ b/plots/icicle-basic/implementations/javascript/muix.tsx @@ -1,7 +1,7 @@ // anyplot.ai // icicle-basic: Basic Icicle Chart // Library: muix 7.29.1 | JavaScript 22.23.2 -// Quality: 86/100 | Created: 2026-09-05 +// Quality: pending | Created: 2026-09-05 import { ChartsContainer } from "@mui/x-charts/ChartsContainer"; import { useDrawingArea } from "@mui/x-charts/hooks"; diff --git a/plots/icicle-basic/metadata/javascript/muix.yaml b/plots/icicle-basic/metadata/javascript/muix.yaml index ccc951650bf..1ae35f08427 100644 --- a/plots/icicle-basic/metadata/javascript/muix.yaml +++ b/plots/icicle-basic/metadata/javascript/muix.yaml @@ -1,265 +1,22 @@ +# Per-library metadata for muix implementation of icicle-basic +# Auto-generated by impl-generate.yml + library: muix language: javascript specification_id: icicle-basic created: '2026-09-05T05:36:47Z' -updated: '2026-09-05T06:00:35Z' -generated_by: claude-sonnet -workflow_run: 33947337379 +updated: '2026-10-24T10:04:26Z' +generated_by: gemini-4-argon +adopted_from: 5b2bcb3e5e4dc5b3685a669c07cf3ab1b9918f69 (5b2bcb3e5e4dc5b3685a669c07cf3ab1b9918f69) +workflow_run: 40084393541 issue: 2497 -language_version: 22.23.2 -library_version: 7.29.1 +language_version: 22.23.3 +library_version: 9.16.0 preview_url_light: https://storage.googleapis.com/anyplot-images/plots/icicle-basic/javascript/muix/plot-light.png preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/icicle-basic/javascript/muix/plot-dark.png preview_html_light: https://storage.googleapis.com/anyplot-images/plots/icicle-basic/javascript/muix/plot-light.html preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/icicle-basic/javascript/muix/plot-dark.html -quality_score: 86 +quality_score: null review: - strengths: - - 'The Review-2 dark-mode text-legibility bug is fully fixed: textColorForRgb() - now branches on two fixed, theme-independent absolute colors (FIXED_DARK_TEXT - #1A1A17 / FIXED_LIGHT_TEXT #FFFDF6) chosen purely from the fill''s own relative - luminance, instead of the theme tokens t.ink/t.pageBg. Verified by pixel-cropping - the leaf-file row in plot-dark.png: labels like ''index.js / 180 KB'' render in - clear dark text on the mint tint, matching the light render.' - - 'Correct icicle/partition math: bottom-up value aggregation (computeValue) mirrors - du-style directory sizing, and partition() produces gapless, proportional bands - per the spec''s width-proportional-to-value requirement.' - - 'Palette compliance is excellent and theme-stable: node_modules/ uses Imprint - #009E73, remaining top-level branches follow canonical Imprint order (lavender, - blue, ochre), the root band uses a fixed neutral grey, and all data fills are - byte-identical between light and dark renders -- only chrome (title, subtitle, - page background) flips.' - - fitsLabel() correctly hides labels on boxes too small to hold them (both primary - label and KB sublabel gated independently), matching the spec's 'label rectangles - with sufficient space, hide labels for small nodes' note. - - 'Realistic, in-range dataset: 41-node file-system hierarchy (4 top-level directories, - 11 subdirectories, 25 files) sits well inside the spec''s 10-100 node recommendation, - with plausible KB sizes.' - - 'Solid harness compliance: single default export, sized from window.ANYPLOT_SIZE, - skipAnimation set, no AR-09 edge clipping on any border in either render.' - weaknesses: - - Library Mastery is thin beyond the required custom-mark composition primitive - -- no use of MUI X's own ChartsTooltip/ChartsLegend components; hover text relies - on a plain SVG element instead. - - Design Excellence is competent but generic -- the HSL tint ramp and consistent - gutters are clean, but there is no additional visual-hierarchy device (e.g. an - accent outline or subtle emphasis on the single largest branch/file) beyond the - color-by-branch scheme. - - The root band's own label ('repo/ · 2,100 KB total') is set at the same modest - fontsize step as other depth-0 content despite occupying by far the largest area - of the canvas -- a slightly larger or bolder treatment would let the root read - more clearly as the anchor of the whole chart. - image_description: |- - Light render (plot-light.png): - Background: warm off-white, matches #FAF8F1. - Chrome: bold dark title "icicle-basic · javascript · muix · anyplot.ai" and muted secondary-tone subtitle "Repository file sizes by directory depth · width ∝ size (KB)" -- both crisp and fully readable against the light background. - Data: 4-row icicle (partition) layout. Row 0 is a full-width neutral-grey "repo/ · 2,100 KB total" root band with white text. Row 1 has four Imprint-colored top-level directories in canonical order: node_modules/ = #009E73 brand green, src/ = lavender, tests/ = blue, docs/ = ochre, each with a white label. Row 2 (subdirectories) and row 3 (leaf files) use progressively lighter HSL tints of their ancestor's branch color, with automatically contrast-selected dark or light text; wide-enough leaf boxes (index.js, webpack.js, tsc.js, Modal.tsx, api.ts) show name + KB sublabel in dark ink, narrower leaf boxes correctly hide their labels. No overlapping text, no clipping at any canvas edge. - Legibility verdict: PASS - - Dark render (plot-dark.png): - Background: warm near-black, matches #1A1A17. - Chrome: light/cream title and subtitle, both fully readable against the dark background. - Data: confirmed pixel-identical fill colors to the light render (root grey, node_modules/ green, src/ lavender, tests/ blue, docs/ ochre, and all descendant tints) -- only the page chrome flips. Critically, the leaf-file row (previously the Review-2 rejection reason: near-invisible ~1.1:1 contrast text from using theme tokens instead of fixed colors) is now fully legible: a pixel crop of the leaf row shows clear dark text ("index.js" / "180 KB", "webpack.js" / "140 KB") on the mint-tinted fills, matching the light render's presentation. All rows at all depths pass legibility in this render. - Legibility verdict: PASS - criteria_checklist: - visual_quality: - score: 29 - max: 30 - items: - - id: VQ-01 - name: Text Legibility - score: 7 - max: 8 - passed: true - comment: Dark-mode leaf-label bug from Review 2 is fixed (verified via pixel - crop); all font sizes explicitly set per depth (22/17/15/13px); readable - in both themes with no overflow - - id: VQ-02 - name: No Overlap - score: 6 - max: 6 - passed: true - comment: No text/element collisions in either render - - id: VQ-03 - name: Element Visibility - score: 6 - max: 6 - passed: true - comment: All bands clearly visible with consistent gutters; labels correctly - hidden on boxes too small to hold them - - id: VQ-04 - name: Color Accessibility - score: 2 - max: 2 - passed: true - comment: Imprint palette is CVD-safe; contrast-aware text selection based - on fill luminance - - id: VQ-05 - name: Layout & Canvas - score: 4 - max: 4 - passed: true - comment: 3200x1800 canvas confirmed, no gate failure, no overflow/clipping, - sensible margins - - id: VQ-06 - name: Axis Labels & Title - score: 2 - max: 2 - passed: true - comment: Descriptive subtitle explains size encoding with units (KB); no traditional - axes needed for this chart type - - id: VQ-07 - name: Palette Compliance - score: 2 - max: 2 - passed: true - comment: 'First branch color #009E73 verified, canonical Imprint order for - remaining branches, data colors identical across themes, correct theme backgrounds' - design_excellence: - score: 13 - max: 20 - items: - - id: DE-01 - name: Aesthetic Sophistication - score: 5 - max: 8 - passed: false - comment: Contrast-aware HSL tint ramp shows real effort and is now bug-free, - but overall look stays close to a well-configured default - - id: DE-02 - name: Visual Refinement - score: 4 - max: 6 - passed: false - comment: Clean gutters and stroke separators, generous margins, but fairly - generic beyond the tint ramp - - id: DE-03 - name: Data Storytelling - score: 4 - max: 6 - passed: false - comment: Depth rows and branch coloring communicate hierarchy clearly, but - there is no additional focal point or emphasis on the largest contributor - spec_compliance: - score: 15 - max: 15 - items: - - id: SC-01 - name: Plot Type - score: 5 - max: 5 - passed: true - comment: Correct horizontal icicle/partition layout, root at top, children - below - - id: SC-02 - name: Required Features - score: 4 - max: 4 - passed: true - comment: Root at top with children below, color by hierarchy/branch, labels - hidden on small nodes -- all spec notes satisfied - - id: SC-03 - name: Data Mapping - score: 3 - max: 3 - passed: true - comment: Bottom-up aggregation, correct proportional widths within each parent - span - - id: SC-04 - name: Title & Legend - score: 3 - max: 3 - passed: true - comment: Title matches required format exactly; band labels substitute for - a legend appropriately - data_quality: - score: 14 - max: 15 - items: - - id: DQ-01 - name: Feature Coverage - score: 5 - max: 6 - passed: true - comment: 41 nodes across 4 depth levels, full hierarchy shown with varied - branch sizes - - id: DQ-02 - name: Realistic Context - score: 5 - max: 5 - passed: true - comment: Plausible, neutral file-system dataset (repo directories and files) - - id: DQ-03 - name: Appropriate Scale - score: 4 - max: 4 - passed: true - comment: Sensible KB values across the hierarchy, correctly summing bottom-up - code_quality: - score: 9 - max: 10 - items: - - id: CQ-01 - name: KISS Structure - score: 2 - max: 3 - passed: true - comment: Many helper functions, but justified by the custom SVG partition - layout MUI X has no native primitive for - - id: CQ-02 - name: Reproducibility - score: 2 - max: 2 - passed: true - comment: Fully deterministic, hard-coded tree data - - id: CQ-03 - name: Clean Imports - score: 2 - max: 2 - passed: true - comment: Only ChartContainer + useDrawingArea imported, both used - - id: CQ-04 - name: Code Elegance - score: 2 - max: 2 - passed: true - comment: No fake UI, appropriate complexity for a hand-rolled icicle mark - - id: CQ-05 - name: Output & API - score: 1 - max: 1 - passed: true - comment: Correct default export, sized from ANYPLOT_SIZE, skipAnimation set, - no clipping - library_mastery: - score: 6 - max: 10 - items: - - id: LM-01 - name: Idiomatic Usage - score: 4 - max: 5 - passed: true - comment: ChartContainer + useDrawingArea is exactly the documented custom-mark - composition primitive for a chart type MUI X community has no native component - for - - id: LM-02 - name: Distinctive Features - score: 2 - max: 5 - passed: false - comment: No use of MUI X's own ChartsTooltip/ChartsLegend components; relies - on a plain SVG <title> for hover text - verdict: APPROVED -impl_tags: - dependencies: [] - techniques: - - custom-mark - patterns: - - data-generation - - iteration-over-groups - - hierarchical-aggregation - dataprep: [] - styling: - - edge-highlighting - - alpha-blending + strengths: [] + weaknesses: [] From 1a12632ee533e8477f03529f49bdaaa08c46890d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com> Date: Sat, 24 Oct 2026 18:18:00 +0000 Subject: [PATCH 3/5] chore(muix): store the review for icicle-basic --- .../implementations/javascript/muix.tsx | 4 +- .../metadata/javascript/muix.yaml | 250 +++++++++++++++++- 2 files changed, 245 insertions(+), 9 deletions(-) diff --git a/plots/icicle-basic/implementations/javascript/muix.tsx b/plots/icicle-basic/implementations/javascript/muix.tsx index 2a8bf247203..86b9400d375 100644 --- a/plots/icicle-basic/implementations/javascript/muix.tsx +++ b/plots/icicle-basic/implementations/javascript/muix.tsx @@ -1,7 +1,7 @@ // anyplot.ai // icicle-basic: Basic Icicle Chart -// Library: muix 7.29.1 | JavaScript 22.23.2 -// Quality: pending | Created: 2026-09-05 +// Library: muix 9.16.0 | JavaScript 22.23.3 +// Quality: 86/100 | Updated: 2026-10-24 import { ChartsContainer } from "@mui/x-charts/ChartsContainer"; import { useDrawingArea } from "@mui/x-charts/hooks"; diff --git a/plots/icicle-basic/metadata/javascript/muix.yaml b/plots/icicle-basic/metadata/javascript/muix.yaml index 1ae35f08427..74dfaa8aad7 100644 --- a/plots/icicle-basic/metadata/javascript/muix.yaml +++ b/plots/icicle-basic/metadata/javascript/muix.yaml @@ -1,11 +1,8 @@ -# Per-library metadata for muix implementation of icicle-basic -# Auto-generated by impl-generate.yml - library: muix language: javascript specification_id: icicle-basic created: '2026-09-05T05:36:47Z' -updated: '2026-10-24T10:04:26Z' +updated: '2026-10-24T18:18:00Z' generated_by: gemini-4-argon adopted_from: 5b2bcb3e5e4dc5b3685a669c07cf3ab1b9918f69 (5b2bcb3e5e4dc5b3685a669c07cf3ab1b9918f69) workflow_run: 40084393541 @@ -16,7 +13,246 @@ preview_url_light: https://storage.googleapis.com/anyplot-images/plots/icicle-ba preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/icicle-basic/javascript/muix/plot-dark.png preview_html_light: https://storage.googleapis.com/anyplot-images/plots/icicle-basic/javascript/muix/plot-light.html preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/icicle-basic/javascript/muix/plot-dark.html -quality_score: null +quality_score: 86 review: - strengths: [] - weaknesses: [] + strengths: + - 'Clean four-row icicle: root on top, each child partition nested exactly within + its parent''s width, page-colored gutters separating siblings' + - Branch hue from the Imprint palette in canonical order with lighter tints per + depth keeps each subtree one color family in both themes + - Label ink chosen per fill by luminance, and labels are width-gated so small nodes + stay unlabeled as the spec asks + - 'Idiomatic MUI X composition: a custom SVG layer inside ChartsContainer driven + by useDrawingArea, with native SVG <title> tooltips for the HTML view' + weaknesses: + - 'DQ-03 (both): node_modules totals 950 KB with typescript/ at 250 KB and react/index.js + at 180 KB, implausibly small for these packages (real installs run to tens of + MB) → scale the node_modules branch to realistic sizes, e.g. in MB, keeping it + the largest branch. Likely cause: the hard-coded VALUE numbers in the node_modules + rows (lines 59–72).' + - 'CQ-04 (code): lines 215–227 write out the WCAG relative-luminance formula (13 + lines) → `getLuminance(node.fill)` (@mui/material/styles), which uses the same + 0.03928 / 2.4 transfer. Likely cause: hand-rolled contrast check.' + - 'Suggestion: use the theme-adaptive muted anchor (#6B6A63 light / #A8A79F dark) + for the root instead of the fixed #6E6A61.' + - 'Suggestion: most leaf rectangles are tall but too narrow for a horizontal label; + a vertical label would let more leaves be named.' + image_description: |- + Light render (plot-light.png): + Background: warm off-white #FAF8F1. + Chrome: title "icicle-basic · javascript · muix · anyplot.ai" top-left in dark ink, mono subtitle "4 levels · 25 leaves · 2,100 KB total · width ∝ Size (KB)"; top-right legend with swatches for node_modules/ (950 KB), src/ (600 KB), tests/ (350 KB), docs/ (200 KB). All readable. + Data: four full-width rows. Row 1 is the gray root "repo/ · 2,100 KB total" (white text). Row 2: node_modules/ in #009E73, src/ in lavender #C475FD, tests/ in blue #4467A3, docs/ in ochre #BD8233, each with name + mono size. Rows 3 and 4 are progressively lighter tints of the branch hue (react/, webpack/, typescript/, eslint/, components/, utils/, styles/, unit/, integration/, guides/ labelled; reference/ unlabeled sliver); leaf row labels only index.js, webpack.js, tsc.js, api.ts, the rest unlabeled. Thin page-colored gutters between all rectangles. + Legibility verdict: PASS + + Dark render (plot-dark.png): + Background: warm near-black #1A1A17. + Chrome: title, subtitle and legend switch to light ink and are fully readable; no dark-on-dark text. + Data: fills identical to the light render (same palette hues and tints, same gray root); in-rectangle label ink unchanged because it depends on the fill. Gutters now near-black. + Legibility verdict: PASS + criteria_checklist: + visual_quality: + score: 30 + max: 30 + items: + - id: VQ-01 + name: Text Legibility + score: 8 + max: 8 + passed: true + comment: All sizes explicit (title 22, labels 20/16/15/14, mono sublabels + 14 CSS px); readable in both themes, text color picked per fill by luminance + - id: VQ-02 + name: No Overlap + score: 6 + max: 6 + passed: true + comment: Labels are width-gated and sit inside their own rectangle; no collisions + - id: VQ-03 + name: Element Visibility + score: 6 + max: 6 + passed: true + comment: All 41 rectangles visible with page-colored gutters; legend swatches + match the depth-1 fills in both themes + - id: VQ-04 + name: Color Accessibility + score: 2 + max: 2 + passed: true + comment: Four distinct Imprint hue families, CVD-safe, labels carry the identity + too + - id: VQ-05 + name: Layout & Canvas + score: 4 + max: 4 + passed: true + comment: Canvas gate passed; four equal rows fill the canvas, header balanced, + no overflow + - id: VQ-06 + name: Axis Labels & Title + score: 2 + max: 2 + passed: true + comment: No axes by design; subtitle states the encoding 'width ∝ Size (KB)' + with units + - id: VQ-07 + name: Palette Compliance + score: 2 + max: 2 + passed: true + comment: 'Top-level branches in canonical Imprint order #009E73, #C475FD, + #4467A3, #BD8233 with depth tints; identical across themes; backgrounds + #FAF8F1 / #1A1A17' + design_excellence: + score: 12 + max: 20 + items: + - id: DE-01 + name: Aesthetic Sophistication + score: 5 + max: 8 + passed: true + comment: 'Typographic hierarchy beyond baseline: bold sans names over monospace + size sublabels, font size stepping down by depth, header legend with mono + sizes' + - id: DE-02 + name: Visual Refinement + score: 4 + max: 6 + passed: true + comment: Page-colored gutters (5 px rows, 2 px columns), 2 px rounded corners, + consistent 8 px label inset, luminance-picked label ink + - id: DE-03 + name: Data Storytelling + score: 3 + max: 6 + passed: true + comment: Branch hue plus depth tint makes each subtree read as one family; + legend ordered by size with totals + spec_compliance: + score: 15 + max: 15 + items: + - id: SC-01 + name: Plot Type + score: 5 + max: 5 + passed: true + comment: Correct horizontal-row icicle, root on top; no unasked layers + - id: SC-02 + name: Required Features + score: 4 + max: 4 + passed: true + comment: 'All Notes and ''A good version shows'' bullets met: one row per + level, widths proportional and nested, color by category, labels only where + they fit' + - id: SC-03 + name: Data Mapping + score: 3 + max: 3 + passed: true + comment: Widths are value/parent-value partitions; children lie within parent + extent + - id: SC-04 + name: Title & Legend + score: 3 + max: 3 + passed: true + comment: Title 'icicle-basic · javascript · muix · anyplot.ai'; legend names + the four top-level directories + data_quality: + score: 14 + max: 15 + items: + - id: DQ-01 + name: Feature Coverage + score: 6 + max: 6 + passed: true + comment: 4 levels, 41 nodes, uneven sibling sizes show proportional partitioning + well + - id: DQ-02 + name: Realistic Context + score: 5 + max: 5 + passed: true + comment: Repository file tree with realistic names, neutral + - id: DQ-03 + name: Appropriate Scale + score: 3 + max: 4 + passed: false + comment: 'node_modules sizes implausibly small for the named packages (defect + line); totals checked: 950+600+350+200 = 2,100 KB, sums correct' + code_quality: + score: 9 + max: 10 + items: + - id: CQ-01 + name: KISS Structure + score: 3 + max: 3 + passed: true + comment: The IcicleLayer component is the MUI X way to reach useDrawingArea; + otherwise flat + - id: CQ-02 + name: Reproducibility + score: 2 + max: 2 + passed: true + comment: Deterministic hard-coded data + - id: CQ-03 + name: Clean Imports + score: 2 + max: 2 + passed: true + comment: ChartsContainer and useDrawingArea both used + - id: CQ-04 + name: Code Elegance + score: 1 + max: 2 + passed: false + comment: Lines 215–227 write out relative luminance that getLuminance from + @mui/material/styles computes (defect line) + - id: CQ-05 + name: Output & API + score: 1 + max: 1 + passed: true + comment: Default export, v9 ChartsContainer path, skipAnimation, window.ANYPLOT_SIZE + library_mastery: + score: 6 + max: 10 + items: + - id: LM-01 + name: Idiomatic Usage + score: 4 + max: 5 + passed: true + comment: 'Custom SVG layer composed inside ChartsContainer via useDrawingArea, + axes hidden with position: ''none''' + - id: LM-02 + name: Distinctive Features + score: 2 + max: 5 + passed: true + comment: Uses the MUI X composition API (drawing-area hook) to build a chart + type the library lacks + verdict: REJECTED + model: claude-opus-5-5 + criteria_version: qc-899b86ffde.aqr-869a70087c.sg-c73b4641dd.lib-f7283eb626 + rendered_at: '2026-10-24T10:04:46Z' +impl_tags: + dependencies: [] + techniques: + - custom-layout + - html-export + - hover-tooltips + patterns: + - data-generation + - tree-traversal + dataprep: + - hierarchical-aggregation + styling: + - publication-ready From d6fd410fa6b9a61ba78c5d3232f48878f566831c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com> Date: Sat, 24 Oct 2026 18:21:26 +0000 Subject: [PATCH 4/5] fix(muix): address review feedback for icicle-basic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Attempt 1/4 - fixes based on AI review - DQ-03: replace the node_modules/ branch (implausible KB sizes for real packages) with a dist/ build-output branch (js/, assets/, fonts/, css/) whose KB sizes are realistic; values and layout unchanged. - CQ-04: use getLuminance from @mui/material/styles instead of the hand-written WCAG relative-luminance formula. Declined: - "Suggestion: theme-adaptive muted anchor for the root" — suggestion, not taken - "Suggestion: vertical labels on narrow leaves" — suggestion, not taken --- .../implementations/javascript/muix.tsx | 61 ++++++++----------- 1 file changed, 24 insertions(+), 37 deletions(-) diff --git a/plots/icicle-basic/implementations/javascript/muix.tsx b/plots/icicle-basic/implementations/javascript/muix.tsx index 86b9400d375..872c5b50dba 100644 --- a/plots/icicle-basic/implementations/javascript/muix.tsx +++ b/plots/icicle-basic/implementations/javascript/muix.tsx @@ -5,6 +5,7 @@ import { ChartsContainer } from "@mui/x-charts/ChartsContainer"; import { useDrawingArea } from "@mui/x-charts/hooks"; +import { getLuminance } from "@mui/material/styles"; const t = window.ANYPLOT_TOKENS; @@ -36,15 +37,15 @@ const data: Array<{ }> = [ { [ID]: "repo/", [NAME]: "repo/", [PARENT]: "", [VALUE]: 0 }, // Top-level directories - { [ID]: "repo/node_modules/", [NAME]: "node_modules/", [PARENT]: "repo/", [VALUE]: 0 }, + { [ID]: "repo/dist/", [NAME]: "dist/", [PARENT]: "repo/", [VALUE]: 0 }, { [ID]: "repo/src/", [NAME]: "src/", [PARENT]: "repo/", [VALUE]: 0 }, { [ID]: "repo/tests/", [NAME]: "tests/", [PARENT]: "repo/", [VALUE]: 0 }, { [ID]: "repo/docs/", [NAME]: "docs/", [PARENT]: "repo/", [VALUE]: 0 }, - // Subdirectories under node_modules/ - { [ID]: "repo/node_modules/react/", [NAME]: "react/", [PARENT]: "repo/node_modules/", [VALUE]: 0 }, - { [ID]: "repo/node_modules/webpack/", [NAME]: "webpack/", [PARENT]: "repo/node_modules/", [VALUE]: 0 }, - { [ID]: "repo/node_modules/typescript/", [NAME]: "typescript/", [PARENT]: "repo/node_modules/", [VALUE]: 0 }, - { [ID]: "repo/node_modules/eslint/", [NAME]: "eslint/", [PARENT]: "repo/node_modules/", [VALUE]: 0 }, + // Subdirectories under dist/ (build output) + { [ID]: "repo/dist/js/", [NAME]: "js/", [PARENT]: "repo/dist/", [VALUE]: 0 }, + { [ID]: "repo/dist/assets/", [NAME]: "assets/", [PARENT]: "repo/dist/", [VALUE]: 0 }, + { [ID]: "repo/dist/fonts/", [NAME]: "fonts/", [PARENT]: "repo/dist/", [VALUE]: 0 }, + { [ID]: "repo/dist/css/", [NAME]: "css/", [PARENT]: "repo/dist/", [VALUE]: 0 }, // Subdirectories under src/ { [ID]: "repo/src/components/", [NAME]: "components/", [PARENT]: "repo/src/", [VALUE]: 0 }, { [ID]: "repo/src/utils/", [NAME]: "utils/", [PARENT]: "repo/src/", [VALUE]: 0 }, @@ -55,21 +56,21 @@ const data: Array<{ // Subdirectories under docs/ { [ID]: "repo/docs/guides/", [NAME]: "guides/", [PARENT]: "repo/docs/", [VALUE]: 0 }, { [ID]: "repo/docs/reference/", [NAME]: "reference/", [PARENT]: "repo/docs/", [VALUE]: 0 }, - // Files under react/ - { [ID]: "repo/node_modules/react/index.js", [NAME]: "index.js", [PARENT]: "repo/node_modules/react/", [VALUE]: 180 }, - { [ID]: "repo/node_modules/react/package.json", [NAME]: "package.json", [PARENT]: "repo/node_modules/react/", [VALUE]: 60 }, - { [ID]: "repo/node_modules/react/README.md", [NAME]: "README.md", [PARENT]: "repo/node_modules/react/", [VALUE]: 80 }, - // Files under webpack/ - { [ID]: "repo/node_modules/webpack/webpack.js", [NAME]: "webpack.js", [PARENT]: "repo/node_modules/webpack/", [VALUE]: 140 }, - { [ID]: "repo/node_modules/webpack/loader.js", [NAME]: "loader.js", [PARENT]: "repo/node_modules/webpack/", [VALUE]: 90 }, - { [ID]: "repo/node_modules/webpack/config.js", [NAME]: "config.js", [PARENT]: "repo/node_modules/webpack/", [VALUE]: 50 }, - // Files under typescript/ - { [ID]: "repo/node_modules/typescript/tsc.js", [NAME]: "tsc.js", [PARENT]: "repo/node_modules/typescript/", [VALUE]: 120 }, - { [ID]: "repo/node_modules/typescript/lib.d.ts", [NAME]: "lib.d.ts", [PARENT]: "repo/node_modules/typescript/", [VALUE]: 70 }, - { [ID]: "repo/node_modules/typescript/compiler.js", [NAME]: "compiler.js", [PARENT]: "repo/node_modules/typescript/", [VALUE]: 60 }, - // Files under eslint/ - { [ID]: "repo/node_modules/eslint/index.js", [NAME]: "index.js", [PARENT]: "repo/node_modules/eslint/", [VALUE]: 55 }, - { [ID]: "repo/node_modules/eslint/rules.js", [NAME]: "rules.js", [PARENT]: "repo/node_modules/eslint/", [VALUE]: 45 }, + // Files under js/ + { [ID]: "repo/dist/js/vendor.js", [NAME]: "vendor.js", [PARENT]: "repo/dist/js/", [VALUE]: 180 }, + { [ID]: "repo/dist/js/polyfills.js", [NAME]: "polyfills.js", [PARENT]: "repo/dist/js/", [VALUE]: 60 }, + { [ID]: "repo/dist/js/main.js", [NAME]: "main.js", [PARENT]: "repo/dist/js/", [VALUE]: 80 }, + // Files under assets/ + { [ID]: "repo/dist/assets/hero.webp", [NAME]: "hero.webp", [PARENT]: "repo/dist/assets/", [VALUE]: 140 }, + { [ID]: "repo/dist/assets/logo.png", [NAME]: "logo.png", [PARENT]: "repo/dist/assets/", [VALUE]: 90 }, + { [ID]: "repo/dist/assets/icons.svg", [NAME]: "icons.svg", [PARENT]: "repo/dist/assets/", [VALUE]: 50 }, + // Files under fonts/ + { [ID]: "repo/dist/fonts/inter.woff2", [NAME]: "inter.woff2", [PARENT]: "repo/dist/fonts/", [VALUE]: 120 }, + { [ID]: "repo/dist/fonts/jakarta.woff2", [NAME]: "jakarta.woff2", [PARENT]: "repo/dist/fonts/", [VALUE]: 70 }, + { [ID]: "repo/dist/fonts/mono.woff2", [NAME]: "mono.woff2", [PARENT]: "repo/dist/fonts/", [VALUE]: 60 }, + // Files under css/ + { [ID]: "repo/dist/css/main.css", [NAME]: "main.css", [PARENT]: "repo/dist/css/", [VALUE]: 55 }, + { [ID]: "repo/dist/css/vendor.css", [NAME]: "vendor.css", [PARENT]: "repo/dist/css/", [VALUE]: 45 }, // Files under components/ { [ID]: "repo/src/components/Button.tsx", [NAME]: "Button.tsx", [PARENT]: "repo/src/components/", [VALUE]: 90 }, { [ID]: "repo/src/components/Modal.tsx", [NAME]: "Modal.tsx", [PARENT]: "repo/src/components/", [VALUE]: 110 }, @@ -200,7 +201,7 @@ for (let i = 0; i < bfsOrder.length; i += 1) { cx += w; } - // Compute tinted RGB fill and relative luminance contrast text + // Tint the branch hue toward the warm paper per depth; label ink by luminance const hex = node.depth === 0 ? ROOT_FILL : node.branchColor; const hexInt = parseInt(hex.slice(1), 16); const baseR = (hexInt >> 16) & 255; @@ -211,21 +212,7 @@ for (let i = 0; i < bfsOrder.length; i += 1) { const g = Math.round(baseG + (246 - baseG) * tintFactor); const b = Math.round(baseB + (238 - baseB) * tintFactor); node.fill = `rgb(${r}, ${g}, ${b})`; - - const sR = - r / 255 <= 0.03928 - ? r / 255 / 12.92 - : Math.pow((r / 255 + 0.055) / 1.055, 2.4); - const sG = - g / 255 <= 0.03928 - ? g / 255 / 12.92 - : Math.pow((g / 255 + 0.055) / 1.055, 2.4); - const sB = - b / 255 <= 0.03928 - ? b / 255 / 12.92 - : Math.pow((b / 255 + 0.055) / 1.055, 2.4); - const lum = 0.2126 * sR + 0.7152 * sG + 0.0722 * sB; - node.textFill = lum > 0.18 ? FIXED_DARK_TEXT : FIXED_LIGHT_TEXT; + node.textFill = getLuminance(node.fill) > 0.18 ? FIXED_DARK_TEXT : FIXED_LIGHT_TEXT; } const maxDepth = Math.max(...bfsOrder.map((n) => n.depth)); From 80c20c755c0bf7297c07fc28e244d4fdb4075b2b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com> Date: Sat, 24 Oct 2026 18:24:49 +0000 Subject: [PATCH 5/5] chore(muix): store the review for icicle-basic --- .../implementations/javascript/muix.tsx | 2 +- .../metadata/javascript/muix.yaml | 186 +++++++++--------- 2 files changed, 97 insertions(+), 91 deletions(-) diff --git a/plots/icicle-basic/implementations/javascript/muix.tsx b/plots/icicle-basic/implementations/javascript/muix.tsx index 872c5b50dba..67a36f11ce2 100644 --- a/plots/icicle-basic/implementations/javascript/muix.tsx +++ b/plots/icicle-basic/implementations/javascript/muix.tsx @@ -1,7 +1,7 @@ // anyplot.ai // icicle-basic: Basic Icicle Chart // Library: muix 9.16.0 | JavaScript 22.23.3 -// Quality: 86/100 | Updated: 2026-10-24 +// Quality: 83/100 | Updated: 2026-10-24 import { ChartsContainer } from "@mui/x-charts/ChartsContainer"; import { useDrawingArea } from "@mui/x-charts/hooks"; diff --git a/plots/icicle-basic/metadata/javascript/muix.yaml b/plots/icicle-basic/metadata/javascript/muix.yaml index 74dfaa8aad7..aa050bd7f43 100644 --- a/plots/icicle-basic/metadata/javascript/muix.yaml +++ b/plots/icicle-basic/metadata/javascript/muix.yaml @@ -2,7 +2,7 @@ library: muix language: javascript specification_id: icicle-basic created: '2026-09-05T05:36:47Z' -updated: '2026-10-24T18:18:00Z' +updated: '2026-10-24T18:24:49Z' generated_by: gemini-4-argon adopted_from: 5b2bcb3e5e4dc5b3685a669c07cf3ab1b9918f69 (5b2bcb3e5e4dc5b3685a669c07cf3ab1b9918f69) workflow_run: 40084393541 @@ -13,45 +13,53 @@ preview_url_light: https://storage.googleapis.com/anyplot-images/plots/icicle-ba preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/icicle-basic/javascript/muix/plot-dark.png preview_html_light: https://storage.googleapis.com/anyplot-images/plots/icicle-basic/javascript/muix/plot-light.html preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/icicle-basic/javascript/muix/plot-dark.html -quality_score: 86 +quality_score: 83 review: strengths: - - 'Clean four-row icicle: root on top, each child partition nested exactly within - its parent''s width, page-colored gutters separating siblings' - - Branch hue from the Imprint palette in canonical order with lighter tints per - depth keeps each subtree one color family in both themes - - Label ink chosen per fill by luminance, and labels are width-gated so small nodes - stay unlabeled as the spec asks - - 'Idiomatic MUI X composition: a custom SVG layer inside ChartsContainer driven - by useDrawingArea, with native SVG <title> tooltips for the HTML view' + - 'Correct icicle layout: one row per level with the root at the top, every child + within its parent''s horizontal extent, siblings adjacent with thin page-colored + gutters; leaf sums fall out of a bottom-up aggregation (dist 950, src 600, tests + 350, docs 200, total 2,100 KB all verified)' + - 'Color by top-level branch in canonical Imprint order (#009E73, #C475FD, #4467A3, + #BD8233), tinted lighter per depth, so branch membership and hierarchy level read + at once and identically in both themes' + - Label ink chosen per rectangle from the fill's luminance via MUI's getLuminance, + so every label is readable against its fill in both themes + - 'Two-tier label typography: sans-serif bold names over monospace values, with + font size stepping down by depth; labels are suppressed on rectangles too narrow + to hold them, as the Notes ask' + - Uses ChartsContainer composition with a custom SVG layer positioned by the useDrawingArea + hook, the community MUI X way to draw a chart type it has no component for; native + SVG <title> tooltips give path, size and share of parent in the HTML weaknesses: - - 'DQ-03 (both): node_modules totals 950 KB with typescript/ at 250 KB and react/index.js - at 180 KB, implausibly small for these packages (real installs run to tens of - MB) → scale the node_modules branch to realistic sizes, e.g. in MB, keeping it - the largest branch. Likely cause: the hard-coded VALUE numbers in the node_modules - rows (lines 59–72).' - - 'CQ-04 (code): lines 215–227 write out the WCAG relative-luminance formula (13 - lines) → `getLuminance(node.fill)` (@mui/material/styles), which uses the same - 0.03928 / 2.4 transfer. Likely cause: hand-rolled contrast check.' - - 'Suggestion: use the theme-adaptive muted anchor (#6B6A63 light / #A8A79F dark) - for the root instead of the fixed #6E6A61.' - - 'Suggestion: most leaf rectangles are tall but too narrow for a horizontal label; - a vertical label would let more leaves be named.' + - 'VQ-07 (both): the root block is filled with the custom hex #6E6A61, which is + neither an Imprint position nor the theme-adaptive muted anchor → the muted anchor + (#6B6A63 light / #A8A79F dark), with label ink still chosen by luminance. Likely + cause: the ROOT_FILL constant.' + - 'CQ-04 (code): lines 205–214 parse the hex and blend each channel toward the paper + by hand, which @mui/material/styles'' lighten(color, coefficient) computes in + one call → node.fill = lighten(hex, tintFactor). Likely cause: a written-out color + blend instead of the MUI color helper already imported alongside getLuminance.' + - 'Suggestion: 21 of the 25 leaf rectangles carry no label although each is about + 235 px tall; a vertical (rotated) name for the tall narrow leaves would identify + the files in the static PNG.' + - 'Suggestion: the root row spends a quarter of the chart height on one grey block; + a shorter root row would give the leaf row more room.' image_description: |- Light render (plot-light.png): - Background: warm off-white #FAF8F1. - Chrome: title "icicle-basic · javascript · muix · anyplot.ai" top-left in dark ink, mono subtitle "4 levels · 25 leaves · 2,100 KB total · width ∝ Size (KB)"; top-right legend with swatches for node_modules/ (950 KB), src/ (600 KB), tests/ (350 KB), docs/ (200 KB). All readable. - Data: four full-width rows. Row 1 is the gray root "repo/ · 2,100 KB total" (white text). Row 2: node_modules/ in #009E73, src/ in lavender #C475FD, tests/ in blue #4467A3, docs/ in ochre #BD8233, each with name + mono size. Rows 3 and 4 are progressively lighter tints of the branch hue (react/, webpack/, typescript/, eslint/, components/, utils/, styles/, unit/, integration/, guides/ labelled; reference/ unlabeled sliver); leaf row labels only index.js, webpack.js, tsc.js, api.ts, the rest unlabeled. Thin page-colored gutters between all rectangles. + Background: warm off-white #FAF8F1 (sampled 250,248,241). + Chrome: title "icicle-basic · javascript · muix · anyplot.ai" in dark bold sans at top left, a monospace subtitle "4 levels · 25 leaves · 2,100 KB total · width ∝ Size (KB)" in soft ink below it, and at top right a legend of four swatches (dist/ 950 KB, src/ 600 KB, tests/ 350 KB, docs/ 200 KB). All dark text, all readable. + Data: four equal-height rows. Row 1: the root "repo/ · 2,100 KB total" as one full-width grey (#6E6A61) block with white label. Row 2: dist/ (#009E73), src/ (#C475FD), tests/ (#4467A3), docs/ (#BD8233) with widths proportional to 950/600/350/200. Row 3: 11 subdirectories in lighter tints of their branch hue, labelled with name + size except the narrow docs/reference/. Row 4: 25 files in the lightest tints; only vendor.js, hero.webp and api.ts carry labels, the others are narrow and unlabelled. Thin page-colored gutters separate every rectangle; children sit within their parents. First series is #009E73. Legibility verdict: PASS Dark render (plot-dark.png): - Background: warm near-black #1A1A17. - Chrome: title, subtitle and legend switch to light ink and are fully readable; no dark-on-dark text. - Data: fills identical to the light render (same palette hues and tints, same gray root); in-rectangle label ink unchanged because it depends on the fill. Gutters now near-black. + Background: warm near-black #1A1A17 (sampled 26,26,23). + Chrome: title, subtitle and legend text flip to light ink and are clearly readable; no dark-on-dark text. Gutters are now near-black. + Data: rectangle fills are identical to the light render (sampled root 110,106,97 and a dist/ leaf 119,200,174 in both); label ink inside rectangles is chosen by fill luminance and stays readable. Brand green is clearly visible. Legibility verdict: PASS criteria_checklist: visual_quality: - score: 30 + score: 29 max: 30 items: - id: VQ-01 @@ -59,52 +67,51 @@ review: score: 8 max: 8 passed: true - comment: All sizes explicit (title 22, labels 20/16/15/14, mono sublabels - 14 CSS px); readable in both themes, text color picked per fill by luminance + comment: Title 22 px, legend and subtitle 14 px, labels 14–20 px by depth; + all readable in both themes, label ink picked per fill by luminance - id: VQ-02 name: No Overlap score: 6 max: 6 passed: true - comment: Labels are width-gated and sit inside their own rectangle; no collisions + comment: No text collisions; labels only where they fit their rectangle - id: VQ-03 name: Element Visibility score: 6 max: 6 passed: true - comment: All 41 rectangles visible with page-colored gutters; legend swatches - match the depth-1 fills in both themes + comment: All 39 rectangles visible with thin gutters; legend swatches match + the branch fills - id: VQ-04 name: Color Accessibility score: 2 max: 2 passed: true - comment: Four distinct Imprint hue families, CVD-safe, labels carry the identity - too + comment: Green, lavender, blue and ochre branches stay distinguishable at + every tint; no red-green sole signal - id: VQ-05 name: Layout & Canvas score: 4 max: 4 passed: true - comment: Canvas gate passed; four equal rows fill the canvas, header balanced, - no overflow + comment: 3200x1800, full-width layout with balanced header; root block as + one row is permitted by the spec - id: VQ-06 name: Axis Labels & Title score: 2 max: 2 passed: true - comment: No axes by design; subtitle states the encoding 'width ∝ Size (KB)' + comment: N/A for axes (hierarchy layout has none); title and subtitle descriptive with units - id: VQ-07 name: Palette Compliance - score: 2 + score: 1 max: 2 passed: true - comment: 'Top-level branches in canonical Imprint order #009E73, #C475FD, - #4467A3, #BD8233 with depth tints; identical across themes; backgrounds - #FAF8F1 / #1A1A17' + comment: 'Branches in canonical Imprint order and identical across themes, + backgrounds correct; root uses custom hex #6E6A61 (defect line)' design_excellence: - score: 12 + score: 10 max: 20 items: - id: DE-01 @@ -112,23 +119,21 @@ review: score: 5 max: 8 passed: true - comment: 'Typographic hierarchy beyond baseline: bold sans names over monospace - size sublabels, font size stepping down by depth, header legend with mono - sizes' + comment: Two-tier typography (bold sans names over monospace values) with + font size stepping down by depth, monospace subtitle; beyond the baseline - id: DE-02 name: Visual Refinement - score: 4 + score: 3 max: 6 passed: true - comment: Page-colored gutters (5 px rows, 2 px columns), 2 px rounded corners, - consistent 8 px label inset, luminance-picked label ink + comment: Depth tints toward the paper tone and page-colored gutters with consistent + 8 px inset labels - id: DE-03 name: Data Storytelling - score: 3 + score: 2 max: 6 - passed: true - comment: Branch hue plus depth tint makes each subtree read as one family; - legend ordered by size with totals + passed: false + comment: Color by branch is what the Notes ask; no further focal point chosen spec_compliance: score: 15 max: 15 @@ -138,31 +143,30 @@ review: score: 5 max: 5 passed: true - comment: Correct horizontal-row icicle, root on top; no unasked layers + comment: Horizontal icicle, root at top, no unasked layers - id: SC-02 name: Required Features score: 4 max: 4 passed: true - comment: 'All Notes and ''A good version shows'' bullets met: one row per - level, widths proportional and nested, color by category, labels only where - they fit' + comment: 'All Notes and A-good-version bullets met: one row per level, widths + proportional, children within parents, consistent color by branch, labels + only where room' - id: SC-03 name: Data Mapping score: 3 max: 3 passed: true - comment: Widths are value/parent-value partitions; children lie within parent - extent + comment: Widths proportional to aggregated sizes; children inside parent extent - id: SC-04 name: Title & Legend score: 3 max: 3 passed: true - comment: Title 'icicle-basic · javascript · muix · anyplot.ai'; legend names - the four top-level directories + comment: Title in mandated format; legend entries match the branch names and + fills data_quality: - score: 14 + score: 15 max: 15 items: - id: DQ-01 @@ -170,21 +174,22 @@ review: score: 6 max: 6 passed: true - comment: 4 levels, 41 nodes, uneven sibling sizes show proportional partitioning + comment: 4 levels, 39 nodes, uneven sibling sizes show proportional widths well - id: DQ-02 name: Realistic Context score: 5 max: 5 passed: true - comment: Repository file tree with realistic names, neutral + comment: Plausible front-end repository with dist/src/tests/docs and realistic + file names and sizes - id: DQ-03 name: Appropriate Scale - score: 3 + score: 4 max: 4 - passed: false - comment: 'node_modules sizes implausibly small for the named packages (defect - line); totals checked: 950+600+350+200 = 2,100 KB, sums correct' + passed: true + comment: 'Checked sums: js 320, assets 280, fonts 250, css 100 → dist 950; + src 600; tests 350; docs 200; total 2,100 KB, all computed bottom-up' code_quality: score: 9 max: 10 @@ -194,65 +199,66 @@ review: score: 3 max: 3 passed: true - comment: The IcicleLayer component is the MUI X way to reach useDrawingArea; - otherwise flat + comment: Default component plus one layer component required for the useDrawingArea + hook - id: CQ-02 name: Reproducibility score: 2 max: 2 passed: true - comment: Deterministic hard-coded data + comment: Hard-coded data, deterministic - id: CQ-03 name: Clean Imports score: 2 max: 2 passed: true - comment: ChartsContainer and useDrawingArea both used + comment: All three imports used - id: CQ-04 name: Code Elegance score: 1 max: 2 - passed: false - comment: Lines 215–227 write out relative luminance that getLuminance from - @mui/material/styles computes (defect line) + passed: true + comment: Lines 205–214 hand-blend hex channels where MUI lighten() computes + it (defect line) - id: CQ-05 name: Output & API score: 1 max: 1 passed: true - comment: Default export, v9 ChartsContainer path, skipAnimation, window.ANYPLOT_SIZE + comment: Default export, skipAnimation, sized to ANYPLOT_SIZE, v9 ChartsContainer + API library_mastery: - score: 6 + score: 5 max: 10 items: - id: LM-01 name: Idiomatic Usage - score: 4 + score: 3 max: 5 passed: true - comment: 'Custom SVG layer composed inside ChartsContainer via useDrawingArea, - axes hidden with position: ''none''' + comment: Community ChartsContainer with axes hidden via position none; mostly + hand-written SVG as the community surface has no icicle - id: LM-02 name: Distinctive Features score: 2 max: 5 - passed: true - comment: Uses the MUI X composition API (drawing-area hook) to build a chart - type the library lacks - verdict: REJECTED + passed: false + comment: Custom SVG layer composed into ChartsContainer via the useDrawingArea + hook; getLuminance from @mui/material for adaptive label ink + verdict: APPROVED model: claude-opus-5-5 criteria_version: qc-899b86ffde.aqr-869a70087c.sg-c73b4641dd.lib-f7283eb626 - rendered_at: '2026-10-24T10:04:46Z' + rendered_at: '2026-10-24T18:21:42Z' impl_tags: dependencies: [] techniques: - - custom-layout - - html-export + - custom-legend - hover-tooltips + - html-export patterns: - data-generation - - tree-traversal dataprep: - hierarchical-aggregation styling: - - publication-ready + - minimal-chrome + - edge-highlighting