@digilogiclabs/saas-factory-ui 1.28.1 → 1.29.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -5257,15 +5257,28 @@ interface FlowTreeProps {
5257
5257
  hideControls?: boolean;
5258
5258
  /** Hide the breadcrumb legend at the bottom-left. */
5259
5259
  hideLegend?: boolean;
5260
+ /**
5261
+ * When > 0, cycles the focused path through the root's direct children
5262
+ * every N milliseconds until the user interacts with the tree (clicks a
5263
+ * card, pans, zooms, toggles a detail, or clicks a legend chip). Honors
5264
+ * `prefers-reduced-motion` (disabled if reduced motion is requested).
5265
+ * Default: 0 (disabled).
5266
+ */
5267
+ autoCycleMs?: number;
5268
+ /**
5269
+ * Explicit paths to cycle through when `autoCycleMs` is set. If omitted,
5270
+ * the tree auto-cycles through the root's direct children.
5271
+ */
5272
+ autoCyclePaths?: string[][];
5260
5273
  }
5261
- declare function FlowTree({ data, title, eyebrow, tagline, renderIcon: renderIconProp, tierColors: tierColorsProp, resolveHref: resolveHrefProp, layoutOptions, onNodeOpen, defaultFocusedPath, focusedPath: controlledPath, onFocusedPathChange, className, style, hideHeader, hideControls, hideLegend, }: FlowTreeProps): react_jsx_runtime.JSX.Element;
5274
+ declare function FlowTree({ data, title, eyebrow, tagline, renderIcon: renderIconProp, tierColors: tierColorsProp, resolveHref: resolveHrefProp, layoutOptions, onNodeOpen, defaultFocusedPath, focusedPath: controlledPath, onFocusedPathChange, className, style, hideHeader, hideControls, hideLegend, autoCycleMs, autoCyclePaths, }: FlowTreeProps): react_jsx_runtime.JSX.Element;
5262
5275
 
5263
5276
  /**
5264
5277
  * FlowTree CSS — injected once per page at runtime on the first mount.
5265
5278
  * Self-themed via `--ft-*` CSS variables, all with sensible fallbacks so the
5266
5279
  * component works standalone in any light or dark design system.
5267
5280
  */
5268
- declare const FLOW_TREE_CSS = "\n.ft-shell {\n --ft-bg: var(--ft-bg, oklch(0.185 0.008 80));\n --ft-fg: var(--ft-fg, oklch(0.96 0.005 85));\n --ft-muted-fg: var(--ft-muted-fg, oklch(0.68 0.008 80));\n --ft-dim-fg: var(--ft-dim-fg, oklch(0.5 0.008 80));\n --ft-line: var(--ft-line, oklch(0.28 0.008 80));\n --ft-line-strong: var(--ft-line-strong, oklch(0.4 0.008 80));\n --ft-grid-strong: var(--ft-grid-strong, oklch(0.24 0.008 80));\n --ft-card-bg: var(--ft-card-bg, oklch(0.21 0.008 80));\n --ft-card-active-bg: var(--ft-card-active-bg, oklch(0.25 0.015 60));\n --ft-card-hover-bg: var(--ft-card-hover-bg, oklch(0.23 0.01 80 / 0.6));\n --ft-rail-bg: var(--ft-rail-bg, oklch(0.19 0.008 80));\n --ft-icon-bg: var(--ft-icon-bg, oklch(0.17 0.008 80));\n --ft-detail-bg: var(--ft-detail-bg, oklch(0.16 0.008 80));\n --ft-header-bg: var(--ft-header-bg, linear-gradient(180deg, oklch(0.21 0.01 60) 0%, oklch(0.19 0.008 70) 100%));\n --ft-tier-orange: var(--ft-tier-orange, #f16c25);\n --ft-tier-teal: var(--ft-tier-teal, #5fb7b8);\n --ft-tier-blue: var(--ft-tier-blue, oklch(0.72 0.12 240));\n --ft-tier-amber: var(--ft-tier-amber, oklch(0.8 0.14 75));\n --ft-tier-green: var(--ft-tier-green, oklch(0.74 0.14 155));\n --ft-accent: var(--ft-accent, var(--ft-tier-orange));\n --ft-serif: var(--ft-serif, \"Instrument Serif\", Georgia, serif);\n --ft-sans: var(--ft-sans, \"Inter\", system-ui, sans-serif);\n --ft-mono: var(--ft-mono, \"JetBrains Mono\", ui-monospace, Menlo, monospace);\n\n display: flex;\n flex-direction: column;\n min-height: 0;\n background: var(--ft-bg);\n border: 1px solid var(--ft-line);\n border-radius: 2px;\n overflow: hidden;\n color: var(--ft-fg);\n font-family: var(--ft-sans);\n font-size: 14px;\n line-height: 1.5;\n -webkit-font-smoothing: antialiased;\n box-shadow: 0 30px 60px -30px oklch(0 0 0 / 0.5);\n}\n.ft-shell * { box-sizing: border-box; }\n.ft-shell .ft-mono {\n font-family: var(--ft-mono);\n font-size: 11px;\n letter-spacing: 0.02em;\n}\n\n.ft-header {\n padding: 22px 28px 20px;\n border-bottom: 1px solid var(--ft-line);\n background: var(--ft-header-bg);\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n.ft-header__eyebrow {\n font-family: var(--ft-mono);\n font-size: 10.5px;\n letter-spacing: 0.22em;\n text-transform: uppercase;\n color: var(--ft-accent);\n}\n.ft-header__title {\n margin: 0;\n font-family: var(--ft-serif);\n font-weight: 400;\n font-size: 44px;\n line-height: 1;\n letter-spacing: -0.015em;\n color: var(--ft-fg);\n}\n.ft-header__tagline {\n margin: 6px 0 0;\n max-width: 76ch;\n color: var(--ft-muted-fg);\n font-size: 13px;\n}\n\n.ft-stage {\n position: relative;\n flex: 1;\n min-height: 0;\n overflow: hidden;\n}\n\n.ft-svg {\n display: block;\n width: 100%;\n height: 100%;\n background: transparent;\n cursor: grab;\n touch-action: none;\n user-select: none;\n}\n.ft-svg.is-dragging { cursor: grabbing; }\n\n.ft-controls {\n position: absolute;\n top: 14px;\n right: 14px;\n z-index: 5;\n display: flex;\n align-items: center;\n gap: 6px;\n background: oklch(0.2 0.008 80 / 0.94);\n border: 1px solid var(--ft-line-strong);\n padding: 5px 8px;\n backdrop-filter: blur(8px);\n border-radius: 2px;\n}\n.ft-controls button {\n font-family: var(--ft-mono);\n font-size: 12px;\n width: 26px;\n height: 26px;\n background: transparent;\n border: 1px solid var(--ft-line);\n color: var(--ft-fg);\n cursor: pointer;\n padding: 0;\n}\n.ft-controls button:hover { border-color: var(--ft-accent); }\n.ft-controls__reset {\n width: auto !important;\n padding: 0 10px !important;\n font-size: 9.5px !important;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n}\n.ft-controls__hint {\n font-family: var(--ft-mono);\n font-size: 9.5px;\n color: var(--ft-muted-fg);\n letter-spacing: 0.04em;\n padding-left: 8px;\n margin-left: 2px;\n border-left: 1px solid var(--ft-line);\n}\n\n.ft-legend {\n position: absolute;\n left: 18px;\n bottom: 14px;\n z-index: 5;\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 6px;\n font-family: var(--ft-mono);\n font-size: 10px;\n color: var(--ft-muted-fg);\n background: oklch(0.2 0.008 80 / 0.88);\n border: 1px solid var(--ft-line);\n padding: 6px 10px;\n backdrop-filter: blur(6px);\n max-width: calc(100% - 220px);\n}\n.ft-legend__chip { display: inline-flex; align-items: center; gap: 6px; }\n.ft-legend__dot {\n width: 5px;\n height: 5px;\n border-radius: 50%;\n background: var(--ft-accent);\n}\n.ft-legend__sep { color: var(--ft-line-strong); margin: 0 2px; }\n\n.ft-edge { fill: none; transition: stroke 260ms, opacity 260ms; }\n.ft-edge--off {\n stroke: var(--ft-line);\n stroke-width: 1;\n opacity: 0.55;\n}\n.ft-edge--on {\n stroke: var(--ft-accent);\n stroke-width: 1.6;\n stroke-dasharray: 4 5;\n opacity: 1;\n animation: ftDashFlow 1.4s linear infinite;\n}\n@keyframes ftDashFlow { to { stroke-dashoffset: -36; } }\n\n.ft-card {\n --tier-local: var(--tier, var(--ft-line-strong));\n width: 100%;\n height: 100%;\n display: grid;\n grid-template-columns: 1fr auto;\n align-items: stretch;\n background: var(--ft-card-bg);\n border: 1px solid var(--ft-line);\n border-left: 3px solid var(--tier-local);\n border-radius: 2px;\n transition:\n background 260ms cubic-bezier(.2,.8,.2,1),\n border-color 260ms, box-shadow 260ms,\n transform 260ms cubic-bezier(.2,.8,.2,1),\n filter 260ms, opacity 260ms, color 260ms;\n overflow: visible;\n animation: ftCardIn 320ms cubic-bezier(.2,.8,.2,1) backwards;\n}\n@keyframes ftCardIn {\n from { opacity: 0; transform: translateX(-6px); }\n to { opacity: 1; transform: translateX(0); }\n}\n.ft-card__main {\n display: grid;\n grid-template-columns: auto 1fr auto;\n gap: 10px;\n align-items: center;\n padding: 0 10px 0 12px;\n background: transparent;\n border: none;\n color: inherit;\n font-family: var(--ft-sans);\n font-size: 13px;\n text-align: left;\n cursor: pointer;\n min-width: 0;\n}\n.ft-card__main:hover { background: var(--ft-card-hover-bg); }\n.ft-card__icon {\n width: 24px;\n height: 24px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n color: var(--tier-local);\n border: 1px solid var(--ft-line-strong);\n background: var(--ft-icon-bg);\n flex-shrink: 0;\n}\n.ft-card__body { display: flex; flex-direction: column; min-width: 0; gap: 1px; }\n.ft-card__label {\n font-family: var(--ft-serif);\n font-size: 15px;\n line-height: 1.15;\n color: var(--ft-fg);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n display: flex;\n align-items: center;\n gap: 5px;\n}\n.ft-card__sub {\n font-family: var(--ft-mono);\n font-size: 9.5px;\n letter-spacing: 0.02em;\n color: var(--ft-muted-fg);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.ft-card__lock { color: var(--ft-muted-fg); display: inline-flex; }\n.ft-card__caret {\n width: 18px;\n height: 18px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n font-family: var(--ft-mono);\n font-size: 12px;\n color: var(--ft-muted-fg);\n border: 1px solid var(--ft-line);\n}\n.ft-card--active .ft-card__caret {\n color: var(--tier-local);\n border-color: var(--tier-local);\n}\n\n.ft-card__rail {\n display: flex;\n flex-direction: column;\n border-left: 1px solid var(--ft-line);\n background: var(--ft-rail-bg);\n}\n.ft-card__mini {\n flex: 1;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n min-height: 20px;\n background: transparent;\n border: none;\n color: var(--ft-muted-fg);\n cursor: pointer;\n transition: background 150ms, color 150ms;\n border-bottom: 1px solid var(--ft-line);\n padding: 0;\n text-decoration: none;\n}\n.ft-card__mini:last-child { border-bottom: none; }\n.ft-card__mini:hover {\n background: oklch(0.23 0.01 80);\n color: var(--tier-local);\n}\n.ft-card__mini.is-on {\n color: var(--tier-local);\n background: oklch(0.23 0.01 80);\n}\n\n.ft-card--active {\n background: var(--ft-card-active-bg);\n box-shadow: 0 0 0 1px var(--tier-local), 0 14px 34px -16px oklch(0.5 0.18 50 / 0.55);\n transform: scale(1.02);\n}\n.ft-card--dim {\n filter: grayscale(0.85) brightness(0.82);\n opacity: 0.7;\n transform: scale(0.88);\n transform-origin: center left;\n}\n.ft-card--dim .ft-card__label { color: var(--ft-dim-fg); }\n.ft-card--dim:hover {\n filter: grayscale(0.3) brightness(0.96);\n opacity: 1;\n transform: scale(0.94);\n}\n.ft-card--cta {\n border-left-color: var(--ft-accent);\n background: linear-gradient(180deg, oklch(0.23 0.02 55) 0%, oklch(0.2 0.015 60) 100%);\n}\n.ft-card--root {\n border-left-color: var(--ft-accent);\n background: oklch(0.26 0.015 55);\n}\n.ft-card--root .ft-card__icon {\n color: var(--ft-accent);\n border-color: var(--ft-accent);\n}\n\n.ft-detail {\n position: relative;\n margin-top: 0;\n padding: 16px 18px 14px;\n background: var(--ft-detail-bg);\n border: 1px solid var(--ft-line-strong);\n border-left: 2px solid var(--ft-accent);\n border-radius: 3px;\n animation: ftDetailIn 220ms cubic-bezier(.2,.8,.2,1) backwards;\n color: var(--ft-fg);\n width: 320px;\n box-shadow: 0 18px 40px -8px rgba(0,0,0,0.6), 0 2px 8px rgba(0,0,0,0.4);\n}\n.ft-detail__head {\n display: flex;\n align-items: center;\n margin: -2px 28px 10px 0;\n}\n.ft-detail__title {\n font-family: var(--ft-mono);\n font-size: 10px;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n color: var(--ft-muted-fg);\n}\n.ft-detail__close {\n position: absolute;\n top: 8px;\n right: 8px;\n width: 22px;\n height: 22px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: 1px solid var(--ft-line);\n border-radius: 2px;\n color: var(--ft-muted-fg);\n cursor: pointer;\n padding: 0;\n transition: color 120ms, border-color 120ms, background 120ms;\n}\n.ft-detail__close:hover {\n color: var(--ft-fg);\n border-color: var(--ft-line-strong);\n background: oklch(0.22 0.008 80);\n}\n@keyframes ftDetailIn {\n from { opacity: 0; transform: translateY(-4px); }\n to { opacity: 1; transform: translateY(0); }\n}\n.ft-detail__lead {\n font-family: var(--ft-serif);\n font-size: 15px;\n line-height: 1.35;\n font-style: italic;\n padding-left: 10px;\n border-left: 2px solid var(--ft-accent);\n margin-bottom: 10px;\n}\n.ft-detail__body {\n margin: 0 0 10px;\n font-size: 12px;\n color: var(--ft-muted-fg);\n line-height: 1.5;\n}\n.ft-detail__bullets {\n margin: 0 0 10px;\n padding-left: 16px;\n font-size: 12px;\n color: var(--ft-fg);\n}\n.ft-detail__bullets li { margin-bottom: 3px; }\n.ft-detail__meta {\n margin: 0 0 10px;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n.ft-detail__meta-row {\n display: grid;\n grid-template-columns: 96px 1fr;\n gap: 8px;\n font-size: 11px;\n}\n.ft-detail__meta-row dt {\n font-family: var(--ft-mono);\n font-size: 9.5px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n color: var(--ft-muted-fg);\n padding-top: 1px;\n}\n.ft-detail__meta-row dd { margin: 0; color: var(--ft-fg); }\n.ft-detail__actions {\n display: flex;\n gap: 6px;\n flex-wrap: wrap;\n margin-top: 8px;\n}\n\n.ft-btn {\n font-family: var(--ft-mono);\n font-size: 10px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n padding: 7px 10px;\n background: transparent;\n color: var(--ft-fg);\n border: 1px solid var(--ft-line-strong);\n text-decoration: none;\n cursor: pointer;\n display: inline-flex;\n gap: 5px;\n align-items: center;\n transition: border-color 150ms, background 150ms;\n}\n.ft-btn:hover { border-color: var(--ft-accent); }\n.ft-btn--primary {\n background: var(--ft-accent);\n color: oklch(0.18 0.01 50);\n border-color: var(--ft-accent);\n}\n\n@media (max-width: 760px) {\n .ft-header { padding: 16px 18px 14px; }\n .ft-header__title { font-size: 32px; }\n .ft-controls__hint { display: none; }\n .ft-legend { max-width: calc(100% - 24px); left: 10px; bottom: 10px; }\n}\n";
5281
+ declare const FLOW_TREE_CSS = "\n.ft-shell {\n --ft-bg: var(--ft-bg, oklch(0.185 0.008 80));\n --ft-fg: var(--ft-fg, oklch(0.96 0.005 85));\n --ft-muted-fg: var(--ft-muted-fg, oklch(0.68 0.008 80));\n --ft-dim-fg: var(--ft-dim-fg, oklch(0.5 0.008 80));\n --ft-line: var(--ft-line, oklch(0.28 0.008 80));\n --ft-line-strong: var(--ft-line-strong, oklch(0.4 0.008 80));\n --ft-grid-strong: var(--ft-grid-strong, oklch(0.24 0.008 80));\n --ft-card-bg: var(--ft-card-bg, oklch(0.21 0.008 80));\n --ft-card-active-bg: var(--ft-card-active-bg, oklch(0.25 0.015 60));\n --ft-card-hover-bg: var(--ft-card-hover-bg, oklch(0.23 0.01 80 / 0.6));\n --ft-rail-bg: var(--ft-rail-bg, oklch(0.19 0.008 80));\n --ft-icon-bg: var(--ft-icon-bg, oklch(0.17 0.008 80));\n --ft-detail-bg: var(--ft-detail-bg, oklch(0.16 0.008 80));\n --ft-header-bg: var(--ft-header-bg, linear-gradient(180deg, oklch(0.21 0.01 60) 0%, oklch(0.19 0.008 70) 100%));\n --ft-tier-orange: var(--ft-tier-orange, #f16c25);\n --ft-tier-teal: var(--ft-tier-teal, #5fb7b8);\n --ft-tier-blue: var(--ft-tier-blue, oklch(0.72 0.12 240));\n --ft-tier-amber: var(--ft-tier-amber, oklch(0.8 0.14 75));\n --ft-tier-green: var(--ft-tier-green, oklch(0.74 0.14 155));\n --ft-accent: var(--ft-accent, var(--ft-tier-orange));\n --ft-serif: var(--ft-serif, \"Instrument Serif\", Georgia, serif);\n --ft-sans: var(--ft-sans, \"Inter\", system-ui, sans-serif);\n --ft-mono: var(--ft-mono, \"JetBrains Mono\", ui-monospace, Menlo, monospace);\n\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n min-height: 420px;\n background: var(--ft-bg);\n border: 1px solid var(--ft-line);\n border-radius: 2px;\n overflow: hidden;\n color: var(--ft-fg);\n font-family: var(--ft-sans);\n font-size: 14px;\n line-height: 1.5;\n -webkit-font-smoothing: antialiased;\n box-shadow: 0 30px 60px -30px oklch(0 0 0 / 0.5);\n}\n.ft-shell * { box-sizing: border-box; }\n.ft-shell .ft-mono {\n font-family: var(--ft-mono);\n font-size: 11px;\n letter-spacing: 0.02em;\n}\n\n.ft-header {\n padding: 22px 28px 20px;\n border-bottom: 1px solid var(--ft-line);\n background: var(--ft-header-bg);\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n.ft-header__eyebrow {\n font-family: var(--ft-mono);\n font-size: 10.5px;\n letter-spacing: 0.22em;\n text-transform: uppercase;\n color: var(--ft-accent);\n}\n.ft-header__title {\n margin: 0;\n font-family: var(--ft-serif);\n font-weight: 400;\n font-size: 44px;\n line-height: 1;\n letter-spacing: -0.015em;\n color: var(--ft-fg);\n}\n.ft-header__tagline {\n margin: 6px 0 0;\n max-width: 76ch;\n color: var(--ft-muted-fg);\n font-size: 13px;\n}\n\n.ft-stage {\n position: relative;\n flex: 1;\n min-height: 0;\n overflow: hidden;\n background:\n radial-gradient(1000px 600px at 15% -10%, oklch(0.22 0.012 60 / 0.7), transparent 60%),\n radial-gradient(800px 500px at 110% 110%, oklch(0.2 0.02 200 / 0.35), transparent 65%);\n}\n\n.ft-svg {\n display: block;\n width: 100%;\n height: 100%;\n background: transparent;\n cursor: grab;\n touch-action: none;\n user-select: none;\n}\n.ft-svg.is-dragging { cursor: grabbing; }\n\n.ft-controls {\n position: absolute;\n top: 14px;\n right: 14px;\n z-index: 5;\n display: flex;\n align-items: center;\n gap: 6px;\n background: oklch(0.2 0.008 80 / 0.94);\n border: 1px solid var(--ft-line-strong);\n padding: 5px 8px;\n backdrop-filter: blur(8px);\n border-radius: 2px;\n}\n.ft-controls button {\n font-family: var(--ft-mono);\n font-size: 12px;\n width: 26px;\n height: 26px;\n background: transparent;\n border: 1px solid var(--ft-line);\n color: var(--ft-fg);\n cursor: pointer;\n padding: 0;\n}\n.ft-controls button:hover { border-color: var(--ft-accent); }\n.ft-controls__reset {\n width: auto !important;\n padding: 0 10px !important;\n font-size: 9.5px !important;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n}\n.ft-controls__hint {\n font-family: var(--ft-mono);\n font-size: 9.5px;\n color: var(--ft-muted-fg);\n letter-spacing: 0.04em;\n padding-left: 8px;\n margin-left: 2px;\n border-left: 1px solid var(--ft-line);\n}\n\n.ft-legend {\n position: absolute;\n left: 18px;\n bottom: 14px;\n z-index: 5;\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 6px;\n font-family: var(--ft-mono);\n font-size: 10px;\n color: var(--ft-muted-fg);\n background: oklch(0.2 0.008 80 / 0.88);\n border: 1px solid var(--ft-line);\n padding: 6px 10px;\n backdrop-filter: blur(6px);\n max-width: calc(100% - 220px);\n}\n.ft-legend__chip {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 2px 6px;\n margin: -2px 0;\n background: transparent;\n border: none;\n color: inherit;\n font: inherit;\n letter-spacing: inherit;\n cursor: pointer;\n border-radius: 2px;\n transition: background 120ms, color 120ms;\n}\n.ft-legend__chip:hover {\n background: oklch(0.24 0.01 80);\n color: var(--ft-fg);\n}\n.ft-legend__chip:disabled {\n cursor: default;\n color: var(--ft-fg);\n}\n.ft-legend__chip:disabled:hover { background: transparent; }\n.ft-legend__dot {\n width: 5px;\n height: 5px;\n border-radius: 50%;\n background: var(--ft-accent);\n flex-shrink: 0;\n}\n.ft-legend__sep {\n color: var(--ft-line-strong);\n margin: 0 2px;\n pointer-events: none;\n user-select: none;\n}\n\n.ft-edge {\n fill: none;\n /* Critical: keep strokes the same screen size regardless of the tree's\n scale transform. Without this, fit-to-viewport (z \u2248 0.6-0.9) makes\n the edges sub-pixel wide and they visually disappear. */\n vector-effect: non-scaling-stroke;\n transition: stroke 260ms, opacity 260ms, stroke-width 260ms;\n}\n.ft-edge--off {\n /* Use the accent color at low opacity instead of the dim line color \u2014\n the accent (orange by default) reads far better on a dark stage. */\n stroke: var(--ft-accent);\n stroke-width: 1.5;\n stroke-dasharray: 3 5;\n opacity: 0.35;\n}\n.ft-edge--on {\n stroke: var(--ft-accent);\n stroke-width: 2.2;\n stroke-dasharray: 6 5;\n opacity: 1;\n filter: drop-shadow(0 0 6px var(--ft-accent));\n animation: ftDashFlow 1.4s linear infinite;\n}\n@keyframes ftDashFlow { to { stroke-dashoffset: -44; } }\n@media (prefers-reduced-motion: reduce) {\n .ft-edge--on { animation: none; }\n}\n\n.ft-card {\n --tier-local: var(--tier, var(--ft-line-strong));\n width: 100%;\n height: 100%;\n display: grid;\n grid-template-columns: 1fr auto;\n align-items: stretch;\n background: var(--ft-card-bg);\n border: 1px solid var(--ft-line);\n border-left: 3px solid var(--tier-local);\n border-radius: 2px;\n transition:\n background 260ms cubic-bezier(.2,.8,.2,1),\n border-color 260ms, box-shadow 260ms,\n transform 260ms cubic-bezier(.2,.8,.2,1),\n filter 260ms, opacity 260ms, color 260ms;\n overflow: visible;\n animation: ftCardIn 320ms cubic-bezier(.2,.8,.2,1) backwards;\n}\n@keyframes ftCardIn {\n from { opacity: 0; transform: translateX(-6px); }\n to { opacity: 1; transform: translateX(0); }\n}\n.ft-card__main {\n display: grid;\n grid-template-columns: auto 1fr auto;\n gap: 10px;\n align-items: center;\n padding: 0 10px 0 12px;\n background: transparent;\n border: none;\n color: inherit;\n font-family: var(--ft-sans);\n font-size: 13px;\n text-align: left;\n cursor: pointer;\n min-width: 0;\n}\n.ft-card__main:hover { background: var(--ft-card-hover-bg); }\n.ft-card__icon {\n width: 24px;\n height: 24px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n color: var(--tier-local);\n border: 1px solid var(--ft-line-strong);\n background: var(--ft-icon-bg);\n flex-shrink: 0;\n}\n.ft-card__body { display: flex; flex-direction: column; min-width: 0; gap: 1px; }\n.ft-card__label {\n font-family: var(--ft-serif);\n font-size: 15px;\n line-height: 1.15;\n color: var(--ft-fg);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n display: flex;\n align-items: center;\n gap: 5px;\n}\n.ft-card__sub {\n font-family: var(--ft-mono);\n font-size: 9.5px;\n letter-spacing: 0.02em;\n color: var(--ft-muted-fg);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.ft-card__lock { color: var(--ft-muted-fg); display: inline-flex; }\n.ft-card__caret {\n width: 18px;\n height: 18px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n font-family: var(--ft-mono);\n font-size: 12px;\n color: var(--ft-muted-fg);\n border: 1px solid var(--ft-line);\n}\n.ft-card--active .ft-card__caret {\n color: var(--tier-local);\n border-color: var(--tier-local);\n}\n\n.ft-card__rail {\n display: flex;\n flex-direction: column;\n border-left: 1px solid var(--ft-line);\n background: var(--ft-rail-bg);\n}\n.ft-card__mini {\n flex: 1;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n min-height: 20px;\n background: transparent;\n border: none;\n color: var(--ft-muted-fg);\n cursor: pointer;\n transition: background 150ms, color 150ms;\n border-bottom: 1px solid var(--ft-line);\n padding: 0;\n text-decoration: none;\n}\n.ft-card__mini:last-child { border-bottom: none; }\n.ft-card__mini:hover {\n background: oklch(0.23 0.01 80);\n color: var(--tier-local);\n}\n.ft-card__mini.is-on {\n color: var(--tier-local);\n background: oklch(0.23 0.01 80);\n}\n\n.ft-card--active {\n background: var(--ft-card-active-bg);\n box-shadow: 0 0 0 1px var(--tier-local), 0 14px 34px -16px oklch(0.5 0.18 50 / 0.55);\n transform: scale(1.02);\n}\n.ft-card--dim {\n filter: grayscale(0.85) brightness(0.82);\n opacity: 0.7;\n transform: scale(0.88);\n transform-origin: center left;\n}\n.ft-card--dim .ft-card__label { color: var(--ft-dim-fg); }\n.ft-card--dim:hover {\n filter: grayscale(0.3) brightness(0.96);\n opacity: 1;\n transform: scale(0.94);\n}\n.ft-card--cta {\n border-left-color: var(--ft-accent);\n background: linear-gradient(180deg, oklch(0.23 0.02 55) 0%, oklch(0.2 0.015 60) 100%);\n}\n.ft-card--root {\n border-left-color: var(--ft-accent);\n background: oklch(0.26 0.015 55);\n}\n.ft-card--root .ft-card__icon {\n color: var(--ft-accent);\n border-color: var(--ft-accent);\n}\n\n.ft-detail {\n position: relative;\n margin-top: 0;\n padding: 16px 18px 14px;\n background: var(--ft-detail-bg);\n border: 1px solid var(--ft-line-strong);\n border-left: 2px solid var(--ft-accent);\n border-radius: 3px;\n animation: ftDetailIn 220ms cubic-bezier(.2,.8,.2,1) backwards;\n color: var(--ft-fg);\n width: 320px;\n box-shadow: 0 18px 40px -8px rgba(0,0,0,0.6), 0 2px 8px rgba(0,0,0,0.4);\n}\n.ft-detail__head {\n display: flex;\n align-items: center;\n margin: -2px 28px 10px 0;\n}\n.ft-detail__title {\n font-family: var(--ft-mono);\n font-size: 10px;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n color: var(--ft-muted-fg);\n}\n.ft-detail__close {\n position: absolute;\n top: 8px;\n right: 8px;\n width: 22px;\n height: 22px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: 1px solid var(--ft-line);\n border-radius: 2px;\n color: var(--ft-muted-fg);\n cursor: pointer;\n padding: 0;\n transition: color 120ms, border-color 120ms, background 120ms;\n}\n.ft-detail__close:hover {\n color: var(--ft-fg);\n border-color: var(--ft-line-strong);\n background: oklch(0.22 0.008 80);\n}\n@keyframes ftDetailIn {\n from { opacity: 0; transform: translateY(-4px); }\n to { opacity: 1; transform: translateY(0); }\n}\n.ft-detail__lead {\n font-family: var(--ft-serif);\n font-size: 15px;\n line-height: 1.35;\n font-style: italic;\n padding-left: 10px;\n border-left: 2px solid var(--ft-accent);\n margin-bottom: 10px;\n}\n.ft-detail__body {\n margin: 0 0 10px;\n font-size: 12px;\n color: var(--ft-muted-fg);\n line-height: 1.5;\n}\n.ft-detail__bullets {\n margin: 0 0 10px;\n padding-left: 16px;\n font-size: 12px;\n color: var(--ft-fg);\n}\n.ft-detail__bullets li { margin-bottom: 3px; }\n.ft-detail__meta {\n margin: 0 0 10px;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n.ft-detail__meta-row {\n display: grid;\n grid-template-columns: 96px 1fr;\n gap: 8px;\n font-size: 11px;\n}\n.ft-detail__meta-row dt {\n font-family: var(--ft-mono);\n font-size: 9.5px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n color: var(--ft-muted-fg);\n padding-top: 1px;\n}\n.ft-detail__meta-row dd { margin: 0; color: var(--ft-fg); }\n.ft-detail__actions {\n display: flex;\n gap: 6px;\n flex-wrap: wrap;\n margin-top: 8px;\n}\n\n.ft-btn {\n font-family: var(--ft-mono);\n font-size: 10px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n padding: 7px 10px;\n background: transparent;\n color: var(--ft-fg);\n border: 1px solid var(--ft-line-strong);\n text-decoration: none;\n cursor: pointer;\n display: inline-flex;\n gap: 5px;\n align-items: center;\n transition: border-color 150ms, background 150ms;\n}\n.ft-btn:hover { border-color: var(--ft-accent); }\n.ft-btn--primary {\n background: var(--ft-accent);\n color: oklch(0.18 0.01 50);\n border-color: var(--ft-accent);\n}\n\n@media (max-width: 760px) {\n .ft-header { padding: 16px 18px 14px; }\n .ft-header__title { font-size: 32px; }\n .ft-controls__hint { display: none; }\n .ft-legend { max-width: calc(100% - 24px); left: 10px; bottom: 10px; }\n}\n";
5269
5282
 
5270
5283
  type GameTimerMode = "countdown" | "countup";
5271
5284
  type GameTimerThemePreset = "green" | "red" | "blue" | "amber" | "purple" | "pink" | "teal";
package/dist/index.d.ts CHANGED
@@ -5257,15 +5257,28 @@ interface FlowTreeProps {
5257
5257
  hideControls?: boolean;
5258
5258
  /** Hide the breadcrumb legend at the bottom-left. */
5259
5259
  hideLegend?: boolean;
5260
+ /**
5261
+ * When > 0, cycles the focused path through the root's direct children
5262
+ * every N milliseconds until the user interacts with the tree (clicks a
5263
+ * card, pans, zooms, toggles a detail, or clicks a legend chip). Honors
5264
+ * `prefers-reduced-motion` (disabled if reduced motion is requested).
5265
+ * Default: 0 (disabled).
5266
+ */
5267
+ autoCycleMs?: number;
5268
+ /**
5269
+ * Explicit paths to cycle through when `autoCycleMs` is set. If omitted,
5270
+ * the tree auto-cycles through the root's direct children.
5271
+ */
5272
+ autoCyclePaths?: string[][];
5260
5273
  }
5261
- declare function FlowTree({ data, title, eyebrow, tagline, renderIcon: renderIconProp, tierColors: tierColorsProp, resolveHref: resolveHrefProp, layoutOptions, onNodeOpen, defaultFocusedPath, focusedPath: controlledPath, onFocusedPathChange, className, style, hideHeader, hideControls, hideLegend, }: FlowTreeProps): react_jsx_runtime.JSX.Element;
5274
+ declare function FlowTree({ data, title, eyebrow, tagline, renderIcon: renderIconProp, tierColors: tierColorsProp, resolveHref: resolveHrefProp, layoutOptions, onNodeOpen, defaultFocusedPath, focusedPath: controlledPath, onFocusedPathChange, className, style, hideHeader, hideControls, hideLegend, autoCycleMs, autoCyclePaths, }: FlowTreeProps): react_jsx_runtime.JSX.Element;
5262
5275
 
5263
5276
  /**
5264
5277
  * FlowTree CSS — injected once per page at runtime on the first mount.
5265
5278
  * Self-themed via `--ft-*` CSS variables, all with sensible fallbacks so the
5266
5279
  * component works standalone in any light or dark design system.
5267
5280
  */
5268
- declare const FLOW_TREE_CSS = "\n.ft-shell {\n --ft-bg: var(--ft-bg, oklch(0.185 0.008 80));\n --ft-fg: var(--ft-fg, oklch(0.96 0.005 85));\n --ft-muted-fg: var(--ft-muted-fg, oklch(0.68 0.008 80));\n --ft-dim-fg: var(--ft-dim-fg, oklch(0.5 0.008 80));\n --ft-line: var(--ft-line, oklch(0.28 0.008 80));\n --ft-line-strong: var(--ft-line-strong, oklch(0.4 0.008 80));\n --ft-grid-strong: var(--ft-grid-strong, oklch(0.24 0.008 80));\n --ft-card-bg: var(--ft-card-bg, oklch(0.21 0.008 80));\n --ft-card-active-bg: var(--ft-card-active-bg, oklch(0.25 0.015 60));\n --ft-card-hover-bg: var(--ft-card-hover-bg, oklch(0.23 0.01 80 / 0.6));\n --ft-rail-bg: var(--ft-rail-bg, oklch(0.19 0.008 80));\n --ft-icon-bg: var(--ft-icon-bg, oklch(0.17 0.008 80));\n --ft-detail-bg: var(--ft-detail-bg, oklch(0.16 0.008 80));\n --ft-header-bg: var(--ft-header-bg, linear-gradient(180deg, oklch(0.21 0.01 60) 0%, oklch(0.19 0.008 70) 100%));\n --ft-tier-orange: var(--ft-tier-orange, #f16c25);\n --ft-tier-teal: var(--ft-tier-teal, #5fb7b8);\n --ft-tier-blue: var(--ft-tier-blue, oklch(0.72 0.12 240));\n --ft-tier-amber: var(--ft-tier-amber, oklch(0.8 0.14 75));\n --ft-tier-green: var(--ft-tier-green, oklch(0.74 0.14 155));\n --ft-accent: var(--ft-accent, var(--ft-tier-orange));\n --ft-serif: var(--ft-serif, \"Instrument Serif\", Georgia, serif);\n --ft-sans: var(--ft-sans, \"Inter\", system-ui, sans-serif);\n --ft-mono: var(--ft-mono, \"JetBrains Mono\", ui-monospace, Menlo, monospace);\n\n display: flex;\n flex-direction: column;\n min-height: 0;\n background: var(--ft-bg);\n border: 1px solid var(--ft-line);\n border-radius: 2px;\n overflow: hidden;\n color: var(--ft-fg);\n font-family: var(--ft-sans);\n font-size: 14px;\n line-height: 1.5;\n -webkit-font-smoothing: antialiased;\n box-shadow: 0 30px 60px -30px oklch(0 0 0 / 0.5);\n}\n.ft-shell * { box-sizing: border-box; }\n.ft-shell .ft-mono {\n font-family: var(--ft-mono);\n font-size: 11px;\n letter-spacing: 0.02em;\n}\n\n.ft-header {\n padding: 22px 28px 20px;\n border-bottom: 1px solid var(--ft-line);\n background: var(--ft-header-bg);\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n.ft-header__eyebrow {\n font-family: var(--ft-mono);\n font-size: 10.5px;\n letter-spacing: 0.22em;\n text-transform: uppercase;\n color: var(--ft-accent);\n}\n.ft-header__title {\n margin: 0;\n font-family: var(--ft-serif);\n font-weight: 400;\n font-size: 44px;\n line-height: 1;\n letter-spacing: -0.015em;\n color: var(--ft-fg);\n}\n.ft-header__tagline {\n margin: 6px 0 0;\n max-width: 76ch;\n color: var(--ft-muted-fg);\n font-size: 13px;\n}\n\n.ft-stage {\n position: relative;\n flex: 1;\n min-height: 0;\n overflow: hidden;\n}\n\n.ft-svg {\n display: block;\n width: 100%;\n height: 100%;\n background: transparent;\n cursor: grab;\n touch-action: none;\n user-select: none;\n}\n.ft-svg.is-dragging { cursor: grabbing; }\n\n.ft-controls {\n position: absolute;\n top: 14px;\n right: 14px;\n z-index: 5;\n display: flex;\n align-items: center;\n gap: 6px;\n background: oklch(0.2 0.008 80 / 0.94);\n border: 1px solid var(--ft-line-strong);\n padding: 5px 8px;\n backdrop-filter: blur(8px);\n border-radius: 2px;\n}\n.ft-controls button {\n font-family: var(--ft-mono);\n font-size: 12px;\n width: 26px;\n height: 26px;\n background: transparent;\n border: 1px solid var(--ft-line);\n color: var(--ft-fg);\n cursor: pointer;\n padding: 0;\n}\n.ft-controls button:hover { border-color: var(--ft-accent); }\n.ft-controls__reset {\n width: auto !important;\n padding: 0 10px !important;\n font-size: 9.5px !important;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n}\n.ft-controls__hint {\n font-family: var(--ft-mono);\n font-size: 9.5px;\n color: var(--ft-muted-fg);\n letter-spacing: 0.04em;\n padding-left: 8px;\n margin-left: 2px;\n border-left: 1px solid var(--ft-line);\n}\n\n.ft-legend {\n position: absolute;\n left: 18px;\n bottom: 14px;\n z-index: 5;\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 6px;\n font-family: var(--ft-mono);\n font-size: 10px;\n color: var(--ft-muted-fg);\n background: oklch(0.2 0.008 80 / 0.88);\n border: 1px solid var(--ft-line);\n padding: 6px 10px;\n backdrop-filter: blur(6px);\n max-width: calc(100% - 220px);\n}\n.ft-legend__chip { display: inline-flex; align-items: center; gap: 6px; }\n.ft-legend__dot {\n width: 5px;\n height: 5px;\n border-radius: 50%;\n background: var(--ft-accent);\n}\n.ft-legend__sep { color: var(--ft-line-strong); margin: 0 2px; }\n\n.ft-edge { fill: none; transition: stroke 260ms, opacity 260ms; }\n.ft-edge--off {\n stroke: var(--ft-line);\n stroke-width: 1;\n opacity: 0.55;\n}\n.ft-edge--on {\n stroke: var(--ft-accent);\n stroke-width: 1.6;\n stroke-dasharray: 4 5;\n opacity: 1;\n animation: ftDashFlow 1.4s linear infinite;\n}\n@keyframes ftDashFlow { to { stroke-dashoffset: -36; } }\n\n.ft-card {\n --tier-local: var(--tier, var(--ft-line-strong));\n width: 100%;\n height: 100%;\n display: grid;\n grid-template-columns: 1fr auto;\n align-items: stretch;\n background: var(--ft-card-bg);\n border: 1px solid var(--ft-line);\n border-left: 3px solid var(--tier-local);\n border-radius: 2px;\n transition:\n background 260ms cubic-bezier(.2,.8,.2,1),\n border-color 260ms, box-shadow 260ms,\n transform 260ms cubic-bezier(.2,.8,.2,1),\n filter 260ms, opacity 260ms, color 260ms;\n overflow: visible;\n animation: ftCardIn 320ms cubic-bezier(.2,.8,.2,1) backwards;\n}\n@keyframes ftCardIn {\n from { opacity: 0; transform: translateX(-6px); }\n to { opacity: 1; transform: translateX(0); }\n}\n.ft-card__main {\n display: grid;\n grid-template-columns: auto 1fr auto;\n gap: 10px;\n align-items: center;\n padding: 0 10px 0 12px;\n background: transparent;\n border: none;\n color: inherit;\n font-family: var(--ft-sans);\n font-size: 13px;\n text-align: left;\n cursor: pointer;\n min-width: 0;\n}\n.ft-card__main:hover { background: var(--ft-card-hover-bg); }\n.ft-card__icon {\n width: 24px;\n height: 24px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n color: var(--tier-local);\n border: 1px solid var(--ft-line-strong);\n background: var(--ft-icon-bg);\n flex-shrink: 0;\n}\n.ft-card__body { display: flex; flex-direction: column; min-width: 0; gap: 1px; }\n.ft-card__label {\n font-family: var(--ft-serif);\n font-size: 15px;\n line-height: 1.15;\n color: var(--ft-fg);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n display: flex;\n align-items: center;\n gap: 5px;\n}\n.ft-card__sub {\n font-family: var(--ft-mono);\n font-size: 9.5px;\n letter-spacing: 0.02em;\n color: var(--ft-muted-fg);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.ft-card__lock { color: var(--ft-muted-fg); display: inline-flex; }\n.ft-card__caret {\n width: 18px;\n height: 18px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n font-family: var(--ft-mono);\n font-size: 12px;\n color: var(--ft-muted-fg);\n border: 1px solid var(--ft-line);\n}\n.ft-card--active .ft-card__caret {\n color: var(--tier-local);\n border-color: var(--tier-local);\n}\n\n.ft-card__rail {\n display: flex;\n flex-direction: column;\n border-left: 1px solid var(--ft-line);\n background: var(--ft-rail-bg);\n}\n.ft-card__mini {\n flex: 1;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n min-height: 20px;\n background: transparent;\n border: none;\n color: var(--ft-muted-fg);\n cursor: pointer;\n transition: background 150ms, color 150ms;\n border-bottom: 1px solid var(--ft-line);\n padding: 0;\n text-decoration: none;\n}\n.ft-card__mini:last-child { border-bottom: none; }\n.ft-card__mini:hover {\n background: oklch(0.23 0.01 80);\n color: var(--tier-local);\n}\n.ft-card__mini.is-on {\n color: var(--tier-local);\n background: oklch(0.23 0.01 80);\n}\n\n.ft-card--active {\n background: var(--ft-card-active-bg);\n box-shadow: 0 0 0 1px var(--tier-local), 0 14px 34px -16px oklch(0.5 0.18 50 / 0.55);\n transform: scale(1.02);\n}\n.ft-card--dim {\n filter: grayscale(0.85) brightness(0.82);\n opacity: 0.7;\n transform: scale(0.88);\n transform-origin: center left;\n}\n.ft-card--dim .ft-card__label { color: var(--ft-dim-fg); }\n.ft-card--dim:hover {\n filter: grayscale(0.3) brightness(0.96);\n opacity: 1;\n transform: scale(0.94);\n}\n.ft-card--cta {\n border-left-color: var(--ft-accent);\n background: linear-gradient(180deg, oklch(0.23 0.02 55) 0%, oklch(0.2 0.015 60) 100%);\n}\n.ft-card--root {\n border-left-color: var(--ft-accent);\n background: oklch(0.26 0.015 55);\n}\n.ft-card--root .ft-card__icon {\n color: var(--ft-accent);\n border-color: var(--ft-accent);\n}\n\n.ft-detail {\n position: relative;\n margin-top: 0;\n padding: 16px 18px 14px;\n background: var(--ft-detail-bg);\n border: 1px solid var(--ft-line-strong);\n border-left: 2px solid var(--ft-accent);\n border-radius: 3px;\n animation: ftDetailIn 220ms cubic-bezier(.2,.8,.2,1) backwards;\n color: var(--ft-fg);\n width: 320px;\n box-shadow: 0 18px 40px -8px rgba(0,0,0,0.6), 0 2px 8px rgba(0,0,0,0.4);\n}\n.ft-detail__head {\n display: flex;\n align-items: center;\n margin: -2px 28px 10px 0;\n}\n.ft-detail__title {\n font-family: var(--ft-mono);\n font-size: 10px;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n color: var(--ft-muted-fg);\n}\n.ft-detail__close {\n position: absolute;\n top: 8px;\n right: 8px;\n width: 22px;\n height: 22px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: 1px solid var(--ft-line);\n border-radius: 2px;\n color: var(--ft-muted-fg);\n cursor: pointer;\n padding: 0;\n transition: color 120ms, border-color 120ms, background 120ms;\n}\n.ft-detail__close:hover {\n color: var(--ft-fg);\n border-color: var(--ft-line-strong);\n background: oklch(0.22 0.008 80);\n}\n@keyframes ftDetailIn {\n from { opacity: 0; transform: translateY(-4px); }\n to { opacity: 1; transform: translateY(0); }\n}\n.ft-detail__lead {\n font-family: var(--ft-serif);\n font-size: 15px;\n line-height: 1.35;\n font-style: italic;\n padding-left: 10px;\n border-left: 2px solid var(--ft-accent);\n margin-bottom: 10px;\n}\n.ft-detail__body {\n margin: 0 0 10px;\n font-size: 12px;\n color: var(--ft-muted-fg);\n line-height: 1.5;\n}\n.ft-detail__bullets {\n margin: 0 0 10px;\n padding-left: 16px;\n font-size: 12px;\n color: var(--ft-fg);\n}\n.ft-detail__bullets li { margin-bottom: 3px; }\n.ft-detail__meta {\n margin: 0 0 10px;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n.ft-detail__meta-row {\n display: grid;\n grid-template-columns: 96px 1fr;\n gap: 8px;\n font-size: 11px;\n}\n.ft-detail__meta-row dt {\n font-family: var(--ft-mono);\n font-size: 9.5px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n color: var(--ft-muted-fg);\n padding-top: 1px;\n}\n.ft-detail__meta-row dd { margin: 0; color: var(--ft-fg); }\n.ft-detail__actions {\n display: flex;\n gap: 6px;\n flex-wrap: wrap;\n margin-top: 8px;\n}\n\n.ft-btn {\n font-family: var(--ft-mono);\n font-size: 10px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n padding: 7px 10px;\n background: transparent;\n color: var(--ft-fg);\n border: 1px solid var(--ft-line-strong);\n text-decoration: none;\n cursor: pointer;\n display: inline-flex;\n gap: 5px;\n align-items: center;\n transition: border-color 150ms, background 150ms;\n}\n.ft-btn:hover { border-color: var(--ft-accent); }\n.ft-btn--primary {\n background: var(--ft-accent);\n color: oklch(0.18 0.01 50);\n border-color: var(--ft-accent);\n}\n\n@media (max-width: 760px) {\n .ft-header { padding: 16px 18px 14px; }\n .ft-header__title { font-size: 32px; }\n .ft-controls__hint { display: none; }\n .ft-legend { max-width: calc(100% - 24px); left: 10px; bottom: 10px; }\n}\n";
5281
+ declare const FLOW_TREE_CSS = "\n.ft-shell {\n --ft-bg: var(--ft-bg, oklch(0.185 0.008 80));\n --ft-fg: var(--ft-fg, oklch(0.96 0.005 85));\n --ft-muted-fg: var(--ft-muted-fg, oklch(0.68 0.008 80));\n --ft-dim-fg: var(--ft-dim-fg, oklch(0.5 0.008 80));\n --ft-line: var(--ft-line, oklch(0.28 0.008 80));\n --ft-line-strong: var(--ft-line-strong, oklch(0.4 0.008 80));\n --ft-grid-strong: var(--ft-grid-strong, oklch(0.24 0.008 80));\n --ft-card-bg: var(--ft-card-bg, oklch(0.21 0.008 80));\n --ft-card-active-bg: var(--ft-card-active-bg, oklch(0.25 0.015 60));\n --ft-card-hover-bg: var(--ft-card-hover-bg, oklch(0.23 0.01 80 / 0.6));\n --ft-rail-bg: var(--ft-rail-bg, oklch(0.19 0.008 80));\n --ft-icon-bg: var(--ft-icon-bg, oklch(0.17 0.008 80));\n --ft-detail-bg: var(--ft-detail-bg, oklch(0.16 0.008 80));\n --ft-header-bg: var(--ft-header-bg, linear-gradient(180deg, oklch(0.21 0.01 60) 0%, oklch(0.19 0.008 70) 100%));\n --ft-tier-orange: var(--ft-tier-orange, #f16c25);\n --ft-tier-teal: var(--ft-tier-teal, #5fb7b8);\n --ft-tier-blue: var(--ft-tier-blue, oklch(0.72 0.12 240));\n --ft-tier-amber: var(--ft-tier-amber, oklch(0.8 0.14 75));\n --ft-tier-green: var(--ft-tier-green, oklch(0.74 0.14 155));\n --ft-accent: var(--ft-accent, var(--ft-tier-orange));\n --ft-serif: var(--ft-serif, \"Instrument Serif\", Georgia, serif);\n --ft-sans: var(--ft-sans, \"Inter\", system-ui, sans-serif);\n --ft-mono: var(--ft-mono, \"JetBrains Mono\", ui-monospace, Menlo, monospace);\n\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n min-height: 420px;\n background: var(--ft-bg);\n border: 1px solid var(--ft-line);\n border-radius: 2px;\n overflow: hidden;\n color: var(--ft-fg);\n font-family: var(--ft-sans);\n font-size: 14px;\n line-height: 1.5;\n -webkit-font-smoothing: antialiased;\n box-shadow: 0 30px 60px -30px oklch(0 0 0 / 0.5);\n}\n.ft-shell * { box-sizing: border-box; }\n.ft-shell .ft-mono {\n font-family: var(--ft-mono);\n font-size: 11px;\n letter-spacing: 0.02em;\n}\n\n.ft-header {\n padding: 22px 28px 20px;\n border-bottom: 1px solid var(--ft-line);\n background: var(--ft-header-bg);\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n.ft-header__eyebrow {\n font-family: var(--ft-mono);\n font-size: 10.5px;\n letter-spacing: 0.22em;\n text-transform: uppercase;\n color: var(--ft-accent);\n}\n.ft-header__title {\n margin: 0;\n font-family: var(--ft-serif);\n font-weight: 400;\n font-size: 44px;\n line-height: 1;\n letter-spacing: -0.015em;\n color: var(--ft-fg);\n}\n.ft-header__tagline {\n margin: 6px 0 0;\n max-width: 76ch;\n color: var(--ft-muted-fg);\n font-size: 13px;\n}\n\n.ft-stage {\n position: relative;\n flex: 1;\n min-height: 0;\n overflow: hidden;\n background:\n radial-gradient(1000px 600px at 15% -10%, oklch(0.22 0.012 60 / 0.7), transparent 60%),\n radial-gradient(800px 500px at 110% 110%, oklch(0.2 0.02 200 / 0.35), transparent 65%);\n}\n\n.ft-svg {\n display: block;\n width: 100%;\n height: 100%;\n background: transparent;\n cursor: grab;\n touch-action: none;\n user-select: none;\n}\n.ft-svg.is-dragging { cursor: grabbing; }\n\n.ft-controls {\n position: absolute;\n top: 14px;\n right: 14px;\n z-index: 5;\n display: flex;\n align-items: center;\n gap: 6px;\n background: oklch(0.2 0.008 80 / 0.94);\n border: 1px solid var(--ft-line-strong);\n padding: 5px 8px;\n backdrop-filter: blur(8px);\n border-radius: 2px;\n}\n.ft-controls button {\n font-family: var(--ft-mono);\n font-size: 12px;\n width: 26px;\n height: 26px;\n background: transparent;\n border: 1px solid var(--ft-line);\n color: var(--ft-fg);\n cursor: pointer;\n padding: 0;\n}\n.ft-controls button:hover { border-color: var(--ft-accent); }\n.ft-controls__reset {\n width: auto !important;\n padding: 0 10px !important;\n font-size: 9.5px !important;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n}\n.ft-controls__hint {\n font-family: var(--ft-mono);\n font-size: 9.5px;\n color: var(--ft-muted-fg);\n letter-spacing: 0.04em;\n padding-left: 8px;\n margin-left: 2px;\n border-left: 1px solid var(--ft-line);\n}\n\n.ft-legend {\n position: absolute;\n left: 18px;\n bottom: 14px;\n z-index: 5;\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 6px;\n font-family: var(--ft-mono);\n font-size: 10px;\n color: var(--ft-muted-fg);\n background: oklch(0.2 0.008 80 / 0.88);\n border: 1px solid var(--ft-line);\n padding: 6px 10px;\n backdrop-filter: blur(6px);\n max-width: calc(100% - 220px);\n}\n.ft-legend__chip {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 2px 6px;\n margin: -2px 0;\n background: transparent;\n border: none;\n color: inherit;\n font: inherit;\n letter-spacing: inherit;\n cursor: pointer;\n border-radius: 2px;\n transition: background 120ms, color 120ms;\n}\n.ft-legend__chip:hover {\n background: oklch(0.24 0.01 80);\n color: var(--ft-fg);\n}\n.ft-legend__chip:disabled {\n cursor: default;\n color: var(--ft-fg);\n}\n.ft-legend__chip:disabled:hover { background: transparent; }\n.ft-legend__dot {\n width: 5px;\n height: 5px;\n border-radius: 50%;\n background: var(--ft-accent);\n flex-shrink: 0;\n}\n.ft-legend__sep {\n color: var(--ft-line-strong);\n margin: 0 2px;\n pointer-events: none;\n user-select: none;\n}\n\n.ft-edge {\n fill: none;\n /* Critical: keep strokes the same screen size regardless of the tree's\n scale transform. Without this, fit-to-viewport (z \u2248 0.6-0.9) makes\n the edges sub-pixel wide and they visually disappear. */\n vector-effect: non-scaling-stroke;\n transition: stroke 260ms, opacity 260ms, stroke-width 260ms;\n}\n.ft-edge--off {\n /* Use the accent color at low opacity instead of the dim line color \u2014\n the accent (orange by default) reads far better on a dark stage. */\n stroke: var(--ft-accent);\n stroke-width: 1.5;\n stroke-dasharray: 3 5;\n opacity: 0.35;\n}\n.ft-edge--on {\n stroke: var(--ft-accent);\n stroke-width: 2.2;\n stroke-dasharray: 6 5;\n opacity: 1;\n filter: drop-shadow(0 0 6px var(--ft-accent));\n animation: ftDashFlow 1.4s linear infinite;\n}\n@keyframes ftDashFlow { to { stroke-dashoffset: -44; } }\n@media (prefers-reduced-motion: reduce) {\n .ft-edge--on { animation: none; }\n}\n\n.ft-card {\n --tier-local: var(--tier, var(--ft-line-strong));\n width: 100%;\n height: 100%;\n display: grid;\n grid-template-columns: 1fr auto;\n align-items: stretch;\n background: var(--ft-card-bg);\n border: 1px solid var(--ft-line);\n border-left: 3px solid var(--tier-local);\n border-radius: 2px;\n transition:\n background 260ms cubic-bezier(.2,.8,.2,1),\n border-color 260ms, box-shadow 260ms,\n transform 260ms cubic-bezier(.2,.8,.2,1),\n filter 260ms, opacity 260ms, color 260ms;\n overflow: visible;\n animation: ftCardIn 320ms cubic-bezier(.2,.8,.2,1) backwards;\n}\n@keyframes ftCardIn {\n from { opacity: 0; transform: translateX(-6px); }\n to { opacity: 1; transform: translateX(0); }\n}\n.ft-card__main {\n display: grid;\n grid-template-columns: auto 1fr auto;\n gap: 10px;\n align-items: center;\n padding: 0 10px 0 12px;\n background: transparent;\n border: none;\n color: inherit;\n font-family: var(--ft-sans);\n font-size: 13px;\n text-align: left;\n cursor: pointer;\n min-width: 0;\n}\n.ft-card__main:hover { background: var(--ft-card-hover-bg); }\n.ft-card__icon {\n width: 24px;\n height: 24px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n color: var(--tier-local);\n border: 1px solid var(--ft-line-strong);\n background: var(--ft-icon-bg);\n flex-shrink: 0;\n}\n.ft-card__body { display: flex; flex-direction: column; min-width: 0; gap: 1px; }\n.ft-card__label {\n font-family: var(--ft-serif);\n font-size: 15px;\n line-height: 1.15;\n color: var(--ft-fg);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n display: flex;\n align-items: center;\n gap: 5px;\n}\n.ft-card__sub {\n font-family: var(--ft-mono);\n font-size: 9.5px;\n letter-spacing: 0.02em;\n color: var(--ft-muted-fg);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.ft-card__lock { color: var(--ft-muted-fg); display: inline-flex; }\n.ft-card__caret {\n width: 18px;\n height: 18px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n font-family: var(--ft-mono);\n font-size: 12px;\n color: var(--ft-muted-fg);\n border: 1px solid var(--ft-line);\n}\n.ft-card--active .ft-card__caret {\n color: var(--tier-local);\n border-color: var(--tier-local);\n}\n\n.ft-card__rail {\n display: flex;\n flex-direction: column;\n border-left: 1px solid var(--ft-line);\n background: var(--ft-rail-bg);\n}\n.ft-card__mini {\n flex: 1;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n min-height: 20px;\n background: transparent;\n border: none;\n color: var(--ft-muted-fg);\n cursor: pointer;\n transition: background 150ms, color 150ms;\n border-bottom: 1px solid var(--ft-line);\n padding: 0;\n text-decoration: none;\n}\n.ft-card__mini:last-child { border-bottom: none; }\n.ft-card__mini:hover {\n background: oklch(0.23 0.01 80);\n color: var(--tier-local);\n}\n.ft-card__mini.is-on {\n color: var(--tier-local);\n background: oklch(0.23 0.01 80);\n}\n\n.ft-card--active {\n background: var(--ft-card-active-bg);\n box-shadow: 0 0 0 1px var(--tier-local), 0 14px 34px -16px oklch(0.5 0.18 50 / 0.55);\n transform: scale(1.02);\n}\n.ft-card--dim {\n filter: grayscale(0.85) brightness(0.82);\n opacity: 0.7;\n transform: scale(0.88);\n transform-origin: center left;\n}\n.ft-card--dim .ft-card__label { color: var(--ft-dim-fg); }\n.ft-card--dim:hover {\n filter: grayscale(0.3) brightness(0.96);\n opacity: 1;\n transform: scale(0.94);\n}\n.ft-card--cta {\n border-left-color: var(--ft-accent);\n background: linear-gradient(180deg, oklch(0.23 0.02 55) 0%, oklch(0.2 0.015 60) 100%);\n}\n.ft-card--root {\n border-left-color: var(--ft-accent);\n background: oklch(0.26 0.015 55);\n}\n.ft-card--root .ft-card__icon {\n color: var(--ft-accent);\n border-color: var(--ft-accent);\n}\n\n.ft-detail {\n position: relative;\n margin-top: 0;\n padding: 16px 18px 14px;\n background: var(--ft-detail-bg);\n border: 1px solid var(--ft-line-strong);\n border-left: 2px solid var(--ft-accent);\n border-radius: 3px;\n animation: ftDetailIn 220ms cubic-bezier(.2,.8,.2,1) backwards;\n color: var(--ft-fg);\n width: 320px;\n box-shadow: 0 18px 40px -8px rgba(0,0,0,0.6), 0 2px 8px rgba(0,0,0,0.4);\n}\n.ft-detail__head {\n display: flex;\n align-items: center;\n margin: -2px 28px 10px 0;\n}\n.ft-detail__title {\n font-family: var(--ft-mono);\n font-size: 10px;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n color: var(--ft-muted-fg);\n}\n.ft-detail__close {\n position: absolute;\n top: 8px;\n right: 8px;\n width: 22px;\n height: 22px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: 1px solid var(--ft-line);\n border-radius: 2px;\n color: var(--ft-muted-fg);\n cursor: pointer;\n padding: 0;\n transition: color 120ms, border-color 120ms, background 120ms;\n}\n.ft-detail__close:hover {\n color: var(--ft-fg);\n border-color: var(--ft-line-strong);\n background: oklch(0.22 0.008 80);\n}\n@keyframes ftDetailIn {\n from { opacity: 0; transform: translateY(-4px); }\n to { opacity: 1; transform: translateY(0); }\n}\n.ft-detail__lead {\n font-family: var(--ft-serif);\n font-size: 15px;\n line-height: 1.35;\n font-style: italic;\n padding-left: 10px;\n border-left: 2px solid var(--ft-accent);\n margin-bottom: 10px;\n}\n.ft-detail__body {\n margin: 0 0 10px;\n font-size: 12px;\n color: var(--ft-muted-fg);\n line-height: 1.5;\n}\n.ft-detail__bullets {\n margin: 0 0 10px;\n padding-left: 16px;\n font-size: 12px;\n color: var(--ft-fg);\n}\n.ft-detail__bullets li { margin-bottom: 3px; }\n.ft-detail__meta {\n margin: 0 0 10px;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n.ft-detail__meta-row {\n display: grid;\n grid-template-columns: 96px 1fr;\n gap: 8px;\n font-size: 11px;\n}\n.ft-detail__meta-row dt {\n font-family: var(--ft-mono);\n font-size: 9.5px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n color: var(--ft-muted-fg);\n padding-top: 1px;\n}\n.ft-detail__meta-row dd { margin: 0; color: var(--ft-fg); }\n.ft-detail__actions {\n display: flex;\n gap: 6px;\n flex-wrap: wrap;\n margin-top: 8px;\n}\n\n.ft-btn {\n font-family: var(--ft-mono);\n font-size: 10px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n padding: 7px 10px;\n background: transparent;\n color: var(--ft-fg);\n border: 1px solid var(--ft-line-strong);\n text-decoration: none;\n cursor: pointer;\n display: inline-flex;\n gap: 5px;\n align-items: center;\n transition: border-color 150ms, background 150ms;\n}\n.ft-btn:hover { border-color: var(--ft-accent); }\n.ft-btn--primary {\n background: var(--ft-accent);\n color: oklch(0.18 0.01 50);\n border-color: var(--ft-accent);\n}\n\n@media (max-width: 760px) {\n .ft-header { padding: 16px 18px 14px; }\n .ft-header__title { font-size: 32px; }\n .ft-controls__hint { display: none; }\n .ft-legend { max-width: calc(100% - 24px); left: 10px; bottom: 10px; }\n}\n";
5269
5282
 
5270
5283
  type GameTimerMode = "countdown" | "countup";
5271
5284
  type GameTimerThemePreset = "green" | "red" | "blue" | "amber" | "purple" | "pink" | "teal";
package/dist/index.js CHANGED
@@ -35940,7 +35940,9 @@ var FLOW_TREE_CSS = `
35940
35940
 
35941
35941
  display: flex;
35942
35942
  flex-direction: column;
35943
- min-height: 0;
35943
+ width: 100%;
35944
+ height: 100%;
35945
+ min-height: 420px;
35944
35946
  background: var(--ft-bg);
35945
35947
  border: 1px solid var(--ft-line);
35946
35948
  border-radius: 2px;
@@ -35995,6 +35997,9 @@ var FLOW_TREE_CSS = `
35995
35997
  flex: 1;
35996
35998
  min-height: 0;
35997
35999
  overflow: hidden;
36000
+ background:
36001
+ radial-gradient(1000px 600px at 15% -10%, oklch(0.22 0.012 60 / 0.7), transparent 60%),
36002
+ radial-gradient(800px 500px at 110% 110%, oklch(0.2 0.02 200 / 0.35), transparent 65%);
35998
36003
  }
35999
36004
 
36000
36005
  .ft-svg {
@@ -36069,29 +36074,72 @@ var FLOW_TREE_CSS = `
36069
36074
  backdrop-filter: blur(6px);
36070
36075
  max-width: calc(100% - 220px);
36071
36076
  }
36072
- .ft-legend__chip { display: inline-flex; align-items: center; gap: 6px; }
36077
+ .ft-legend__chip {
36078
+ display: inline-flex;
36079
+ align-items: center;
36080
+ gap: 6px;
36081
+ padding: 2px 6px;
36082
+ margin: -2px 0;
36083
+ background: transparent;
36084
+ border: none;
36085
+ color: inherit;
36086
+ font: inherit;
36087
+ letter-spacing: inherit;
36088
+ cursor: pointer;
36089
+ border-radius: 2px;
36090
+ transition: background 120ms, color 120ms;
36091
+ }
36092
+ .ft-legend__chip:hover {
36093
+ background: oklch(0.24 0.01 80);
36094
+ color: var(--ft-fg);
36095
+ }
36096
+ .ft-legend__chip:disabled {
36097
+ cursor: default;
36098
+ color: var(--ft-fg);
36099
+ }
36100
+ .ft-legend__chip:disabled:hover { background: transparent; }
36073
36101
  .ft-legend__dot {
36074
36102
  width: 5px;
36075
36103
  height: 5px;
36076
36104
  border-radius: 50%;
36077
36105
  background: var(--ft-accent);
36106
+ flex-shrink: 0;
36107
+ }
36108
+ .ft-legend__sep {
36109
+ color: var(--ft-line-strong);
36110
+ margin: 0 2px;
36111
+ pointer-events: none;
36112
+ user-select: none;
36078
36113
  }
36079
- .ft-legend__sep { color: var(--ft-line-strong); margin: 0 2px; }
36080
36114
 
36081
- .ft-edge { fill: none; transition: stroke 260ms, opacity 260ms; }
36115
+ .ft-edge {
36116
+ fill: none;
36117
+ /* Critical: keep strokes the same screen size regardless of the tree's
36118
+ scale transform. Without this, fit-to-viewport (z \u2248 0.6-0.9) makes
36119
+ the edges sub-pixel wide and they visually disappear. */
36120
+ vector-effect: non-scaling-stroke;
36121
+ transition: stroke 260ms, opacity 260ms, stroke-width 260ms;
36122
+ }
36082
36123
  .ft-edge--off {
36083
- stroke: var(--ft-line);
36084
- stroke-width: 1;
36085
- opacity: 0.55;
36124
+ /* Use the accent color at low opacity instead of the dim line color \u2014
36125
+ the accent (orange by default) reads far better on a dark stage. */
36126
+ stroke: var(--ft-accent);
36127
+ stroke-width: 1.5;
36128
+ stroke-dasharray: 3 5;
36129
+ opacity: 0.35;
36086
36130
  }
36087
36131
  .ft-edge--on {
36088
36132
  stroke: var(--ft-accent);
36089
- stroke-width: 1.6;
36090
- stroke-dasharray: 4 5;
36133
+ stroke-width: 2.2;
36134
+ stroke-dasharray: 6 5;
36091
36135
  opacity: 1;
36136
+ filter: drop-shadow(0 0 6px var(--ft-accent));
36092
36137
  animation: ftDashFlow 1.4s linear infinite;
36093
36138
  }
36094
- @keyframes ftDashFlow { to { stroke-dashoffset: -36; } }
36139
+ @keyframes ftDashFlow { to { stroke-dashoffset: -44; } }
36140
+ @media (prefers-reduced-motion: reduce) {
36141
+ .ft-edge--on { animation: none; }
36142
+ }
36095
36143
 
36096
36144
  .ft-card {
36097
36145
  --tier-local: var(--tier, var(--ft-line-strong));
@@ -36388,11 +36436,11 @@ var DEFAULT_TIER_COLORS = {
36388
36436
  gray: "var(--ft-line-strong)"
36389
36437
  };
36390
36438
  var DEFAULT_LAYOUT = {
36391
- col: 280,
36392
- row: 64,
36393
- compactRow: 42,
36394
- cardW: 244,
36395
- cardH: 56,
36439
+ col: 290,
36440
+ row: 76,
36441
+ compactRow: 48,
36442
+ cardW: 248,
36443
+ cardH: 60,
36396
36444
  detailMaxW: 320
36397
36445
  };
36398
36446
  var STYLE_ID4 = "dll-flow-tree-styles";
@@ -36745,7 +36793,9 @@ function FlowTree({
36745
36793
  style,
36746
36794
  hideHeader = false,
36747
36795
  hideControls = false,
36748
- hideLegend = false
36796
+ hideLegend = false,
36797
+ autoCycleMs = 0,
36798
+ autoCyclePaths
36749
36799
  }) {
36750
36800
  React87.useEffect(() => {
36751
36801
  ensureStylesInjected4();
@@ -36789,31 +36839,84 @@ function FlowTree({
36789
36839
  },
36790
36840
  [controlledPath, onFocusedPathChange]
36791
36841
  );
36842
+ const [hasUserInteracted, setHasUserInteracted] = React87.useState(false);
36843
+ const markInteracted = React87.useCallback(() => {
36844
+ setHasUserInteracted(true);
36845
+ }, []);
36792
36846
  const [expandedDetails, setExpandedDetails] = React87.useState(
36793
36847
  () => /* @__PURE__ */ new Set()
36794
36848
  );
36795
- const toggleDetail = React87.useCallback((n) => {
36796
- setExpandedDetails((prev) => {
36797
- const next = new Set(prev);
36798
- if (next.has(n.data.id)) next.delete(n.data.id);
36799
- else next.add(n.data.id);
36800
- return next;
36801
- });
36802
- }, []);
36803
- const closeDetail = React87.useCallback((id) => {
36804
- setExpandedDetails((prev) => {
36805
- const next = new Set(prev);
36806
- next.delete(id);
36807
- return next;
36808
- });
36809
- }, []);
36849
+ const toggleDetail = React87.useCallback(
36850
+ (n) => {
36851
+ markInteracted();
36852
+ setExpandedDetails((prev) => {
36853
+ const next = new Set(prev);
36854
+ if (next.has(n.data.id)) next.delete(n.data.id);
36855
+ else next.add(n.data.id);
36856
+ return next;
36857
+ });
36858
+ },
36859
+ [markInteracted]
36860
+ );
36861
+ const closeDetail = React87.useCallback(
36862
+ (id) => {
36863
+ markInteracted();
36864
+ setExpandedDetails((prev) => {
36865
+ const next = new Set(prev);
36866
+ next.delete(id);
36867
+ return next;
36868
+ });
36869
+ },
36870
+ [markInteracted]
36871
+ );
36810
36872
  const activate = React87.useCallback(
36811
36873
  (n) => {
36874
+ markInteracted();
36812
36875
  setFocusedPath(n.pathArr);
36813
36876
  setExpandedDetails(/* @__PURE__ */ new Set());
36814
36877
  },
36815
- [setFocusedPath]
36878
+ [setFocusedPath, markInteracted]
36879
+ );
36880
+ const focusPathPrefix = React87.useCallback(
36881
+ (prefixLen) => {
36882
+ if (prefixLen < 1 || prefixLen >= focusedPath.length) return;
36883
+ markInteracted();
36884
+ setFocusedPath(focusedPath.slice(0, prefixLen));
36885
+ setExpandedDetails(/* @__PURE__ */ new Set());
36886
+ },
36887
+ [focusedPath, setFocusedPath, markInteracted]
36816
36888
  );
36889
+ const resolvedCyclePaths = React87.useMemo(() => {
36890
+ if (!autoCycleMs || autoCycleMs <= 0) return [];
36891
+ if (autoCyclePaths && autoCyclePaths.length) return autoCyclePaths;
36892
+ return (data.children ?? []).map((c) => [data.id, c.id]);
36893
+ }, [autoCycleMs, autoCyclePaths, data]);
36894
+ React87.useEffect(() => {
36895
+ if (!autoCycleMs || autoCycleMs <= 0) return;
36896
+ if (hasUserInteracted) return;
36897
+ if (controlledPath) return;
36898
+ if (resolvedCyclePaths.length === 0) return;
36899
+ if (typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
36900
+ return;
36901
+ }
36902
+ const currentKey = focusedPath.join("/");
36903
+ let i = Math.max(
36904
+ 0,
36905
+ resolvedCyclePaths.findIndex((p) => p.join("/") === currentKey)
36906
+ );
36907
+ const id = window.setInterval(() => {
36908
+ i = (i + 1) % resolvedCyclePaths.length;
36909
+ const next = resolvedCyclePaths[i];
36910
+ if (next) setFocusedPath(next);
36911
+ }, autoCycleMs);
36912
+ return () => window.clearInterval(id);
36913
+ }, [
36914
+ autoCycleMs,
36915
+ hasUserInteracted,
36916
+ controlledPath,
36917
+ resolvedCyclePaths,
36918
+ setFocusedPath
36919
+ ]);
36817
36920
  const { nodes, edges, width, height } = React87.useMemo(
36818
36921
  () => layoutTree(data, focusedPath, opts, expandedDetails),
36819
36922
  [data, focusedPath.join("/"), opts, expandedDetails]
@@ -36873,6 +36976,7 @@ function FlowTree({
36873
36976
  const onPointerDown = (e) => {
36874
36977
  if (e.target.closest("button, a")) return;
36875
36978
  userInteractedRef.current = true;
36979
+ markInteracted();
36876
36980
  dragRef.current = { sx: e.clientX, sy: e.clientY, px: pan.x, py: pan.y };
36877
36981
  e.currentTarget.setPointerCapture?.(e.pointerId);
36878
36982
  e.currentTarget.classList.add("is-dragging");
@@ -36892,6 +36996,7 @@ function FlowTree({
36892
36996
  if (!e.ctrlKey && !e.metaKey) return;
36893
36997
  e.preventDefault();
36894
36998
  userInteractedRef.current = true;
36999
+ markInteracted();
36895
37000
  setZoom((z) => Math.max(0.35, Math.min(2, z + -e.deltaY * 15e-4)));
36896
37001
  };
36897
37002
  const resetView = () => {
@@ -37067,10 +37172,23 @@ function FlowTree({
37067
37172
  ),
37068
37173
  !hideLegend ? /* @__PURE__ */ (0, import_jsx_runtime120.jsx)("div", { className: "ft-legend ft-mono", children: focusedPath.map((id, i) => {
37069
37174
  const n = nodeById[id];
37070
- return /* @__PURE__ */ (0, import_jsx_runtime120.jsxs)("span", { className: "ft-legend__chip", children: [
37071
- /* @__PURE__ */ (0, import_jsx_runtime120.jsx)("span", { className: "ft-legend__dot" }),
37072
- n ? n.data.label : id,
37073
- i < focusedPath.length - 1 ? /* @__PURE__ */ (0, import_jsx_runtime120.jsx)("span", { className: "ft-legend__sep", children: "\u203A" }) : null
37175
+ const isLast = i === focusedPath.length - 1;
37176
+ return /* @__PURE__ */ (0, import_jsx_runtime120.jsxs)(React87.Fragment, { children: [
37177
+ /* @__PURE__ */ (0, import_jsx_runtime120.jsxs)(
37178
+ "button",
37179
+ {
37180
+ type: "button",
37181
+ className: "ft-legend__chip",
37182
+ onClick: () => focusPathPrefix(i + 1),
37183
+ disabled: isLast,
37184
+ title: isLast ? "Current focus" : `Jump back to ${n ? n.data.label : id}`,
37185
+ children: [
37186
+ /* @__PURE__ */ (0, import_jsx_runtime120.jsx)("span", { className: "ft-legend__dot" }),
37187
+ n ? n.data.label : id
37188
+ ]
37189
+ }
37190
+ ),
37191
+ !isLast ? /* @__PURE__ */ (0, import_jsx_runtime120.jsx)("span", { className: "ft-legend__sep", "aria-hidden": true, children: "\u203A" }) : null
37074
37192
  ] }, id);
37075
37193
  }) }) : null
37076
37194
  ] })