{
  "version": 3,
  "sources": ["../../../src/diagrams/common/colorThemeGate.ts"],
  "sourcesContent": ["/**\n * The per-item colour palette is opt-in per diagram. A shape or container stamps a\n * `data-color-id` slot on its rendered element, and the diagram's stylesheet maps that\n * slot to a border and fill. Both halves need the same answers to the same three\n * questions \u2014 is this a colour theme, does it actually carry a palette, and which slot\n * does this item get \u2014 so they live here rather than being restated per diagram.\n *\n * Before this module the `COLOR_THEMES` list existed in five copies and the stamping\n * block was duplicated verbatim between `clusters.js` and `classBox.ts`. Two idioms for\n * the same gate had already appeared: `er/styles.ts` keys off the theme name while\n * `requirement/styles.js` keys off the array being non-empty. Anything added here is\n * added once.\n */\nimport type { D3Selection } from '../../types.js';\n\n/** Themes that carry a categorical palette for per-item colouring. */\nexport const COLOR_THEMES = new Set(['redux-color', 'redux-dark-color']);\n\n/** How many palette slots to emit when the theme does not say. */\nexport const DEFAULT_COLOR_SLOTS = 12;\n\n/**\n * Upper bound on slots. Every shipped palette has 12 entries, so this is generous -- it\n * exists to bound the loop, not to express a design limit. See `colorSlotCount`.\n */\nexport const MAX_COLOR_SLOTS = 64;\n\n/**\n * A palette array is usable only if it is genuinely a non-empty array. A truthy `.length`\n * check passes for a plain string too, which would yield a per-character \"palette\" and\n * declarations like `stroke: r;`.\n */\nexport const hasPalette = (palette: unknown): palette is string[] =>\n  Array.isArray(palette) && palette.length > 0;\n\n/** Whether `theme` should render per-item colour at all. */\nexport const isColorTheme = (theme: string | undefined, palette: unknown): boolean =>\n  theme != null && COLOR_THEMES.has(theme) && hasPalette(palette);\n\n/**\n * `look` is interpolated into a CSS selector by every stylesheet that emits palette\n * rules, and it is a top-level config key \u2014 so it is settable from diagram text via\n * frontmatter or an init directive, and `config.sanitize` only removes values containing\n * `<`, `>` or `url(data:`. Braces and quotes survive, which is enough to close the\n * attribute selector early and open a rule block of the caller's choosing, escaping the\n * `#svgId` scoping stylis applies.\n *\n * Every real look is a bare word, so anything else is rejected outright rather than\n * escaped. Validate here, at the point of interpolation, so no caller has to remember.\n */\nconst SAFE_LOOK = /^[\\w-]+$/;\n\nexport const safeLook = (look: unknown): string => {\n  // Only stringify types that actually describe themselves -- `String()` on a plain object\n  // or array would pass through as the meaningless `[object Object]`, which happens to\n  // still fail SAFE_LOOK today but would be a silent bug waiting for a future look-alike\n  // regex, and is exactly the kind of default-stringification mistake this function exists\n  // to guard against elsewhere.\n  const s = typeof look === 'string' || typeof look === 'number' ? String(look) : '';\n  return SAFE_LOOK.test(s) ? s : 'classic';\n};\n\n/**\n * Number of palette slots a stylesheet should emit.\n *\n * With a palette, this is exactly `palette.length` -- not the limit, not a cap. That is not\n * a policy choice: `stampColorSlot` assigns `colorIndex % palette.length`, so the ids it\n * can produce are precisely `0 .. palette.length - 1`. Emitting fewer leaves items stamped\n * with no rule to match, and emitting more is dead CSS. Deriving both from the same length\n * is what makes the two impossible to disagree, rather than something a bound has to keep\n * lined up.\n *\n * Three separate bugs came out of letting these drift apart -- a limit longer than the\n * palette, a palette longer than the limit, and then a palette longer than the cap that was\n * added to bound the limit. `paletteSlotCount` below is the single source of truth for both\n * sides, and `slotsAgree` in the spec pins that they never diverge again.\n *\n * `THEME_COLOR_LIMIT` still governs callers that emit slots *without* stamping -- timeline\n * numbers `.section-N` classes rather than palette slots -- and is bounded there, because a\n * loop runs on it directly and `Infinity` is reachable from front matter\n * (`THEME_COLOR_LIMIT: .inf` parses to it under the `JSON_SCHEMA` mermaid uses).\n */\nexport const paletteSlotCount = (palette: unknown): number =>\n  hasPalette(palette) ? palette.length : 0;\n\nexport const colorSlotCount = (themeColorLimit: unknown, palette?: unknown): number => {\n  if (hasPalette(palette)) {\n    return paletteSlotCount(palette);\n  }\n  return typeof themeColorLimit === 'number' &&\n    Number.isInteger(themeColorLimit) &&\n    themeColorLimit > 0 &&\n    themeColorLimit <= MAX_COLOR_SLOTS\n    ? themeColorLimit\n    : DEFAULT_COLOR_SLOTS;\n};\n\n/**\n * Stamp the element with its palette slot, so the diagram's `[data-color-id]` rules can\n * find it. A no-op for every theme without a palette, which is what keeps this safe to\n * call from shared rendering code used by diagrams that never opt in.\n *\n * The slot wraps at the palette length rather than indexing raw, so a palette shorter\n * than the emitted slot count cannot produce `stroke: undefined`.\n *\n * An absent `colorIndex` means \"this element is not part of the cycle\", so nothing is\n * stamped. It used to fall back to `colorIndex ?? 0`, which says the opposite -- an\n * unnumbered element was stamped `color-0` and painted in the first palette colour. That\n * was invisible while the only unnumbered containers were ones whose diagram emits no\n * matching rules (class namespaces, block containers), and it stops being invisible the\n * moment such a diagram gains palette rules: state's composites opt out of the cycle when\n * the author has styled them, and would otherwise all come back as `color-0`.\n */\nexport const stampColorSlot = <T extends SVGGraphicsElement>(\n  shapeSvg: D3Selection<T>,\n  colorIndex: number | undefined,\n  theme: string | undefined,\n  palette: unknown\n): void => {\n  if (colorIndex === undefined || !isColorTheme(theme, palette)) {\n    return;\n  }\n  const slot = colorIndex % paletteSlotCount(palette);\n  shapeSvg.attr('data-color-id', `color-${slot}`);\n};\n"],
  "mappings": "yCAgBO,IAAMA,EAAe,IAAI,IAAI,CAAC,cAAe,kBAAkB,CAAC,EAG1DC,EAAsB,GAMtBC,EAAkB,GAOlBC,EAAaC,EAACC,GACzB,MAAM,QAAQA,CAAO,GAAKA,EAAQ,OAAS,EADnB,cAIbC,EAAeF,EAAA,CAACG,EAA2BF,IACtDE,GAAS,MAAQP,EAAa,IAAIO,CAAK,GAAKJ,EAAWE,CAAO,EADpC,gBActBG,EAAY,WAELC,EAAWL,EAACM,GAA0B,CAMjD,IAAMC,EAAI,OAAOD,GAAS,UAAY,OAAOA,GAAS,SAAW,OAAOA,CAAI,EAAI,GAChF,OAAOF,EAAU,KAAKG,CAAC,EAAIA,EAAI,SACjC,EARwB,YA8BXC,EAAmBR,EAACC,GAC/BF,EAAWE,CAAO,EAAIA,EAAQ,OAAS,EADT,oBAGnBQ,EAAiBT,EAAA,CAACU,EAA0BT,IACnDF,EAAWE,CAAO,EACbO,EAAiBP,CAAO,EAE1B,OAAOS,GAAoB,UAChC,OAAO,UAAUA,CAAe,GAChCA,EAAkB,GAClBA,GAAmBZ,EACjBY,EACAb,EATwB,kBA4BjBc,EAAiBX,EAAA,CAC5BY,EACAC,EACAV,EACAF,IACS,CACT,GAAIY,IAAe,QAAa,CAACX,EAAaC,EAAOF,CAAO,EAC1D,OAEF,IAAMa,EAAOD,EAAaL,EAAiBP,CAAO,EAClDW,EAAS,KAAK,gBAAiB,SAASE,CAAI,EAAE,CAChD,EAX8B",
  "names": ["COLOR_THEMES", "DEFAULT_COLOR_SLOTS", "MAX_COLOR_SLOTS", "hasPalette", "__name", "palette", "isColorTheme", "theme", "SAFE_LOOK", "safeLook", "look", "s", "paletteSlotCount", "colorSlotCount", "themeColorLimit", "stampColorSlot", "shapeSvg", "colorIndex", "slot"]
}
