Files
ersatztv/design-system/_ds_bundle.js
T
2026-07-02 07:37:00 +02:00

3261 lines
98 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* @ds-bundle: {"format":3,"namespace":"ChicoryTVDesignSystem_eb3b61","components":[{"name":"Badge","sourcePath":"components/data-display/Badge.jsx"},{"name":"Card","sourcePath":"components/data-display/Card.jsx"},{"name":"ChannelLogo","sourcePath":"components/data-display/ChannelLogo.jsx"},{"name":"ProgressBar","sourcePath":"components/data-display/ProgressBar.jsx"},{"name":"Stat","sourcePath":"components/data-display/Stat.jsx"},{"name":"StatusDot","sourcePath":"components/data-display/StatusDot.jsx"},{"name":"Tag","sourcePath":"components/data-display/Tag.jsx"},{"name":"Spinner","sourcePath":"components/feedback/Spinner.jsx"},{"name":"Toast","sourcePath":"components/feedback/Toast.jsx"},{"name":"Tooltip","sourcePath":"components/feedback/Tooltip.jsx"},{"name":"Button","sourcePath":"components/forms/Button.jsx"},{"name":"Checkbox","sourcePath":"components/forms/Checkbox.jsx"},{"name":"IconButton","sourcePath":"components/forms/IconButton.jsx"},{"name":"Input","sourcePath":"components/forms/Input.jsx"},{"name":"Select","sourcePath":"components/forms/Select.jsx"},{"name":"Switch","sourcePath":"components/forms/Switch.jsx"},{"name":"NavItem","sourcePath":"components/navigation/NavItem.jsx"},{"name":"NavSection","sourcePath":"components/navigation/NavItem.jsx"},{"name":"Tabs","sourcePath":"components/navigation/Tabs.jsx"}],"sourceHashes":{"components/data-display/Badge.jsx":"a2d5da49e43f","components/data-display/Card.jsx":"b6e830c9f9f0","components/data-display/ChannelLogo.jsx":"af406aecdc6a","components/data-display/ProgressBar.jsx":"54ab9452240b","components/data-display/Stat.jsx":"695d12eae330","components/data-display/StatusDot.jsx":"a924e27b2a1c","components/data-display/Tag.jsx":"bdf96d4cdf7f","components/feedback/Spinner.jsx":"13341131fe7d","components/feedback/Toast.jsx":"f18ffea8c6ce","components/feedback/Tooltip.jsx":"f17876ac005e","components/forms/Button.jsx":"fa14c86210e0","components/forms/Checkbox.jsx":"81750526a791","components/forms/IconButton.jsx":"1ea757879b1c","components/forms/Input.jsx":"8759cd8e610f","components/forms/Select.jsx":"2a3af2c7bce8","components/forms/Switch.jsx":"1fce47dca064","components/navigation/NavItem.jsx":"8b46c82d5c25","components/navigation/Tabs.jsx":"95d81be93d0f","design_handoff_channel_builder/ChannelBuilder.jsx":"904a465a69fd"},"inlinedExternals":[],"unexposedExports":[]} */
(() => {
const __ds_ns = (window.ChicoryTVDesignSystem_eb3b61 = window.ChicoryTVDesignSystem_eb3b61 || {});
const __ds_scope = {};
(__ds_ns.__errors = __ds_ns.__errors || []);
// components/data-display/Badge.jsx
try { (() => {
const tones = {
neutral: {
fg: "var(--text-secondary)",
bg: "var(--ctv-surface-3)",
bd: "var(--border-hairline)"
},
accent: {
fg: "var(--ctv-accent)",
bg: "var(--ctv-accent-soft)",
bd: "rgba(91,124,250,0.35)"
},
ok: {
fg: "var(--status-ok)",
bg: "var(--ctv-ok-soft)",
bd: "rgba(63,185,132,0.35)"
},
warn: {
fg: "var(--status-warn)",
bg: "var(--ctv-warn-soft)",
bd: "rgba(224,168,61,0.35)"
},
error: {
fg: "var(--status-error)",
bg: "var(--ctv-error-soft)",
bd: "rgba(229,72,77,0.35)"
}
};
/**
* Small status/label badge. Soft (tinted) or solid.
*/
function Badge({
children,
tone = "neutral",
solid = false,
dot = false,
style = {}
}) {
const t = tones[tone] || tones.neutral;
const solidBg = {
neutral: "var(--ctv-surface-3)",
accent: "var(--ctv-accent)",
ok: "var(--status-ok)",
warn: "var(--status-warn)",
error: "var(--status-error)"
};
return /*#__PURE__*/React.createElement("span", {
style: {
display: "inline-flex",
alignItems: "center",
gap: "5px",
height: "20px",
padding: dot ? "0 8px 0 6px" : "0 8px",
borderRadius: "var(--radius-xs)",
font: `var(--weight-medium) var(--text-2xs)/1 var(--font-sans)`,
letterSpacing: "0.02em",
color: solid ? tone === "warn" ? "#1A1206" : "#fff" : t.fg,
background: solid ? solidBg[tone] : t.bg,
border: solid ? "1px solid transparent" : `1px solid ${t.bd}`,
whiteSpace: "nowrap",
...style
}
}, dot && /*#__PURE__*/React.createElement("span", {
style: {
width: 6,
height: 6,
borderRadius: "50%",
background: solid ? "rgba(255,255,255,0.9)" : t.fg
}
}), children);
}
Object.assign(__ds_scope, { Badge });
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/data-display/Badge.jsx", error: String((e && e.message) || e) }); }
// components/data-display/Card.jsx
try { (() => {
/**
* Surface container. Optional header (title + actions) and body padding.
*/
function Card({
title,
subtitle,
actions = null,
children,
padded = true,
inset = false,
style = {}
}) {
return /*#__PURE__*/React.createElement("div", {
style: {
background: inset ? "var(--ctv-bg-sunken)" : "var(--surface-card)",
border: "1px solid var(--border-hairline)",
borderRadius: "var(--radius-md)",
overflow: "hidden",
...style
}
}, (title || actions) && /*#__PURE__*/React.createElement("div", {
style: {
display: "flex",
alignItems: "center",
justifyContent: "space-between",
gap: "12px",
padding: "13px 16px",
borderBottom: "1px solid var(--border-hairline)"
}
}, /*#__PURE__*/React.createElement("div", {
style: {
minWidth: 0
}
}, title && /*#__PURE__*/React.createElement("div", {
style: {
font: "var(--weight-semibold) var(--text-md)/1.2 var(--font-sans)",
color: "var(--text-primary)"
}
}, title), subtitle && /*#__PURE__*/React.createElement("div", {
style: {
marginTop: 3,
font: "var(--text-xs)/1.3 var(--font-sans)",
color: "var(--text-secondary)"
}
}, subtitle)), actions && /*#__PURE__*/React.createElement("div", {
style: {
display: "flex",
alignItems: "center",
gap: "8px",
flex: "0 0 auto"
}
}, actions)), /*#__PURE__*/React.createElement("div", {
style: {
padding: padded ? "16px" : 0
}
}, children));
}
Object.assign(__ds_scope, { Card });
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/data-display/Card.jsx", error: String((e && e.message) || e) }); }
// components/data-display/ChannelLogo.jsx
try { (() => {
const palette = [["#5B7CFA", "#2E3A66"], ["#3FB984", "#1E4536"], ["#E0A83D", "#4A3818"], ["#E5484D", "#4A1F21"], ["#B06CF0", "#38235A"], ["#48B0C8", "#193E47"]];
function hashIndex(str) {
let h = 0;
for (let i = 0; i < str.length; i++) h = h * 31 + str.charCodeAt(i) >>> 0;
return h % palette.length;
}
/**
* Channel logo tile. Renders an image when given, else a generated
* initials chip keyed to the channel name — mirrors ErsatzTV's logo/gen.
*/
function ChannelLogo({
name = "",
src = null,
size = 40,
radius = "var(--radius-sm)",
style = {}
}) {
const [fg, bg] = palette[hashIndex(name)];
const initials = name.split(/[\s\-|:]+/).filter(Boolean).slice(0, 2).map(w => w[0]).join("").toUpperCase() || "?";
return /*#__PURE__*/React.createElement("span", {
style: {
display: "inline-flex",
alignItems: "center",
justifyContent: "center",
width: size,
height: size,
flex: "0 0 auto",
overflow: "hidden",
borderRadius: radius,
background: src ? "var(--ctv-bg-sunken)" : bg,
border: "1px solid var(--border-hairline)",
...style
}
}, src ? /*#__PURE__*/React.createElement("img", {
src: src,
alt: name,
style: {
maxWidth: "100%",
maxHeight: "100%",
objectFit: "contain"
}
}) : /*#__PURE__*/React.createElement("span", {
style: {
font: `var(--weight-semibold) ${Math.round(size * 0.34)}px/1 var(--font-mono)`,
color: fg,
letterSpacing: "0.02em"
}
}, initials));
}
Object.assign(__ds_scope, { ChannelLogo });
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/data-display/ChannelLogo.jsx", error: String((e && e.message) || e) }); }
// components/data-display/ProgressBar.jsx
try { (() => {
const toneColor = {
accent: "var(--action-primary)",
ok: "var(--status-ok)",
warn: "var(--status-warn)",
error: "var(--status-error)"
};
/**
* Thin determinate/indeterminate progress bar — library scans, transcodes.
*/
function ProgressBar({
value = null,
tone = "accent",
height = 6,
showLabel = false,
style = {}
}) {
const c = toneColor[tone] || toneColor.accent;
const indeterminate = value == null;
const pct = Math.max(0, Math.min(100, value ?? 0));
return /*#__PURE__*/React.createElement("div", {
style: {
display: "flex",
alignItems: "center",
gap: "10px",
...style
}
}, /*#__PURE__*/React.createElement("div", {
style: {
position: "relative",
flex: 1,
height,
borderRadius: "var(--radius-pill)",
background: "var(--ctv-surface-3)",
overflow: "hidden"
}
}, /*#__PURE__*/React.createElement("style", null, `@keyframes ctv-indet{0%{left:-40%}100%{left:100%}}`), indeterminate ? /*#__PURE__*/React.createElement("span", {
style: {
position: "absolute",
top: 0,
bottom: 0,
width: "40%",
borderRadius: "inherit",
background: c,
animation: "ctv-indet 1.1s var(--ease-standard) infinite"
}
}) : /*#__PURE__*/React.createElement("span", {
style: {
position: "absolute",
top: 0,
left: 0,
bottom: 0,
width: `${pct}%`,
borderRadius: "inherit",
background: c,
transition: "width var(--dur-slow) var(--ease-out)"
}
})), showLabel && !indeterminate && /*#__PURE__*/React.createElement("span", {
style: {
font: "var(--weight-medium) var(--text-xs)/1 var(--font-mono)",
color: "var(--text-secondary)",
minWidth: 34,
textAlign: "right",
fontVariantNumeric: "tabular-nums"
}
}, Math.round(pct), "%"));
}
Object.assign(__ds_scope, { ProgressBar });
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/data-display/ProgressBar.jsx", error: String((e && e.message) || e) }); }
// components/data-display/Stat.jsx
try { (() => {
/**
* Dashboard metric. Big mono value, label, optional delta + icon.
*/
function Stat({
label,
value,
unit,
delta = null,
deltaTone = "neutral",
icon = null,
style = {}
}) {
const dc = {
up: "var(--status-ok)",
down: "var(--status-error)",
neutral: "var(--text-secondary)"
};
return /*#__PURE__*/React.createElement("div", {
style: {
display: "flex",
flexDirection: "column",
gap: "8px",
...style
}
}, /*#__PURE__*/React.createElement("div", {
style: {
display: "flex",
alignItems: "center",
gap: "7px"
}
}, icon && /*#__PURE__*/React.createElement("span", {
style: {
display: "inline-flex",
color: "var(--text-disabled)"
}
}, icon), /*#__PURE__*/React.createElement("span", {
style: {
font: "var(--weight-medium) var(--text-xs)/1 var(--font-sans)",
letterSpacing: "0.03em",
color: "var(--text-secondary)"
}
}, label)), /*#__PURE__*/React.createElement("div", {
style: {
display: "flex",
alignItems: "baseline",
gap: "6px"
}
}, /*#__PURE__*/React.createElement("span", {
style: {
font: "var(--weight-semibold) var(--text-2xl)/1 var(--font-mono)",
color: "var(--text-primary)",
letterSpacing: "-0.01em",
fontVariantNumeric: "tabular-nums"
}
}, value), unit && /*#__PURE__*/React.createElement("span", {
style: {
font: "var(--text-sm)/1 var(--font-sans)",
color: "var(--text-disabled)"
}
}, unit), delta != null && /*#__PURE__*/React.createElement("span", {
style: {
marginLeft: 2,
font: "var(--weight-medium) var(--text-xs)/1 var(--font-mono)",
color: dc[deltaTone]
}
}, delta)));
}
Object.assign(__ds_scope, { Stat });
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/data-display/Stat.jsx", error: String((e && e.message) || e) }); }
// components/data-display/StatusDot.jsx
try { (() => {
const colors = {
live: "var(--status-live)",
ok: "var(--status-ok)",
warn: "var(--status-warn)",
error: "var(--status-error)",
idle: "var(--text-disabled)"
};
/**
* Status dot with optional pulsing ring — on-air / live indicators.
*/
function StatusDot({
status = "idle",
pulse = false,
size = 8,
label,
style = {}
}) {
const c = colors[status] || colors.idle;
const doPulse = pulse || status === "live";
return /*#__PURE__*/React.createElement("span", {
style: {
display: "inline-flex",
alignItems: "center",
gap: "7px",
...style
}
}, /*#__PURE__*/React.createElement("span", {
style: {
position: "relative",
width: size,
height: size,
flex: "0 0 auto"
}
}, doPulse && /*#__PURE__*/React.createElement("span", {
style: {
position: "absolute",
inset: 0,
borderRadius: "50%",
background: c,
animation: "ctv-ping 1.6s cubic-bezier(0,0,0.2,1) infinite"
}
}), /*#__PURE__*/React.createElement("span", {
style: {
position: "absolute",
inset: 0,
borderRadius: "50%",
background: c,
boxShadow: doPulse ? `0 0 6px ${c}` : "none"
}
}), /*#__PURE__*/React.createElement("style", null, `@keyframes ctv-ping{0%{transform:scale(1);opacity:.55}70%,100%{transform:scale(2.4);opacity:0}}`)), label && /*#__PURE__*/React.createElement("span", {
style: {
font: "var(--text-xs)/1 var(--font-sans)",
color: "var(--text-secondary)"
}
}, label));
}
Object.assign(__ds_scope, { StatusDot });
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/data-display/StatusDot.jsx", error: String((e && e.message) || e) }); }
// components/data-display/Tag.jsx
try { (() => {
/**
* Removable chip/tag — smart-collection filters, tags on media.
*/
function Tag({
children,
onRemove,
icon = null,
tone = "neutral",
style = {}
}) {
const [hover, setHover] = React.useState(false);
const tint = tone === "accent";
return /*#__PURE__*/React.createElement("span", {
style: {
display: "inline-flex",
alignItems: "center",
gap: "6px",
height: "22px",
padding: onRemove ? "0 5px 0 8px" : "0 9px",
borderRadius: "var(--radius-xs)",
font: "var(--weight-medium) var(--text-xs)/1 var(--font-sans)",
color: tint ? "var(--ctv-accent)" : "var(--text-primary)",
background: tint ? "var(--ctv-accent-soft)" : "var(--ctv-surface-3)",
border: `1px solid ${tint ? "rgba(91,124,250,0.35)" : "var(--border-hairline)"}`,
whiteSpace: "nowrap",
...style
}
}, icon && /*#__PURE__*/React.createElement("span", {
style: {
display: "inline-flex",
color: "var(--text-disabled)"
}
}, icon), children, onRemove && /*#__PURE__*/React.createElement("button", {
type: "button",
onClick: onRemove,
onMouseEnter: () => setHover(true),
onMouseLeave: () => setHover(false),
"aria-label": "Remove",
style: {
display: "inline-flex",
alignItems: "center",
justifyContent: "center",
width: 15,
height: 15,
padding: 0,
border: "none",
borderRadius: "3px",
cursor: "pointer",
color: hover ? "var(--text-primary)" : "var(--text-disabled)",
background: hover ? "rgba(255,255,255,0.08)" : "transparent"
}
}, /*#__PURE__*/React.createElement("svg", {
width: "10",
height: "10",
viewBox: "0 0 24 24",
fill: "none"
}, /*#__PURE__*/React.createElement("path", {
d: "M6 6l12 12M18 6L6 18",
stroke: "currentColor",
strokeWidth: "2.4",
strokeLinecap: "round"
}))));
}
Object.assign(__ds_scope, { Tag });
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/data-display/Tag.jsx", error: String((e && e.message) || e) }); }
// components/feedback/Spinner.jsx
try { (() => {
/**
* Indeterminate ring spinner.
*/
function Spinner({
size = 18,
tone = "accent",
stroke = 2.4,
style = {}
}) {
const c = {
accent: "var(--action-primary)",
muted: "var(--text-disabled)",
ok: "var(--status-ok)"
}[tone] || "var(--action-primary)";
return /*#__PURE__*/React.createElement("span", {
style: {
display: "inline-flex",
...style
}
}, /*#__PURE__*/React.createElement("style", null, `@keyframes ctv-spin{to{transform:rotate(360deg)}}`), /*#__PURE__*/React.createElement("svg", {
width: size,
height: size,
viewBox: "0 0 24 24",
fill: "none",
style: {
animation: "ctv-spin 0.7s linear infinite"
}
}, /*#__PURE__*/React.createElement("circle", {
cx: "12",
cy: "12",
r: "9",
stroke: c,
strokeOpacity: "0.2",
strokeWidth: stroke
}), /*#__PURE__*/React.createElement("path", {
d: "M21 12a9 9 0 0 0-9-9",
stroke: c,
strokeWidth: stroke,
strokeLinecap: "round"
})));
}
Object.assign(__ds_scope, { Spinner });
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/feedback/Spinner.jsx", error: String((e && e.message) || e) }); }
// components/feedback/Toast.jsx
try { (() => {
const map = {
ok: {
c: "var(--status-ok)",
bg: "var(--ctv-ok-soft)",
icon: "M5 12.5l4.5 4.5L19 7"
},
warn: {
c: "var(--status-warn)",
bg: "var(--ctv-warn-soft)",
icon: "M12 8v5M12 17h.01"
},
error: {
c: "var(--status-error)",
bg: "var(--ctv-error-soft)",
icon: "M12 8v5M12 17h.01"
},
info: {
c: "var(--ctv-accent)",
bg: "var(--ctv-accent-soft)",
icon: "M12 11v5M12 8h.01"
}
};
/**
* Toast / inline alert. Icon, message, optional close.
*/
function Toast({
tone = "info",
title,
message,
onClose,
style = {}
}) {
const t = map[tone] || map.info;
return /*#__PURE__*/React.createElement("div", {
role: "status",
style: {
display: "flex",
alignItems: "flex-start",
gap: "11px",
width: "min(380px, 100%)",
padding: "12px 13px",
background: "var(--surface-raised)",
border: "1px solid var(--border-hairline)",
borderLeft: `2px solid ${t.c}`,
borderRadius: "var(--radius-md)",
boxShadow: "var(--shadow-md)",
...style
}
}, /*#__PURE__*/React.createElement("span", {
style: {
display: "inline-flex",
alignItems: "center",
justifyContent: "center",
width: 22,
height: 22,
borderRadius: "var(--radius-xs)",
background: t.bg,
flex: "0 0 auto",
marginTop: 1
}
}, /*#__PURE__*/React.createElement("svg", {
width: "14",
height: "14",
viewBox: "0 0 24 24",
fill: "none"
}, /*#__PURE__*/React.createElement("circle", {
cx: "12",
cy: "12",
r: "9",
stroke: t.c,
strokeWidth: "0",
fill: "none"
}), /*#__PURE__*/React.createElement("path", {
d: t.icon,
stroke: t.c,
strokeWidth: "2.2",
strokeLinecap: "round",
strokeLinejoin: "round"
}))), /*#__PURE__*/React.createElement("div", {
style: {
flex: 1,
minWidth: 0
}
}, title && /*#__PURE__*/React.createElement("div", {
style: {
font: "var(--weight-semibold) var(--text-sm)/1.3 var(--font-sans)",
color: "var(--text-primary)"
}
}, title), message && /*#__PURE__*/React.createElement("div", {
style: {
marginTop: title ? 2 : 0,
font: "var(--text-xs)/1.45 var(--font-sans)",
color: "var(--text-secondary)"
}
}, message)), onClose && /*#__PURE__*/React.createElement("button", {
type: "button",
onClick: onClose,
"aria-label": "Dismiss",
style: {
display: "inline-flex",
padding: 3,
border: "none",
background: "transparent",
color: "var(--text-disabled)",
cursor: "pointer",
flex: "0 0 auto"
}
}, /*#__PURE__*/React.createElement("svg", {
width: "14",
height: "14",
viewBox: "0 0 24 24",
fill: "none"
}, /*#__PURE__*/React.createElement("path", {
d: "M6 6l12 12M18 6L6 18",
stroke: "currentColor",
strokeWidth: "2",
strokeLinecap: "round"
}))));
}
Object.assign(__ds_scope, { Toast });
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/feedback/Toast.jsx", error: String((e && e.message) || e) }); }
// components/feedback/Tooltip.jsx
try { (() => {
/**
* Hover tooltip. Wraps a trigger; shows a small dark label.
*/
function Tooltip({
label,
placement = "top",
children,
style = {}
}) {
const [show, setShow] = React.useState(false);
const pos = {
top: {
bottom: "calc(100% + 7px)",
left: "50%",
transform: "translateX(-50%)"
},
bottom: {
top: "calc(100% + 7px)",
left: "50%",
transform: "translateX(-50%)"
},
left: {
right: "calc(100% + 7px)",
top: "50%",
transform: "translateY(-50%)"
},
right: {
left: "calc(100% + 7px)",
top: "50%",
transform: "translateY(-50%)"
}
}[placement];
return /*#__PURE__*/React.createElement("span", {
onMouseEnter: () => setShow(true),
onMouseLeave: () => setShow(false),
style: {
position: "relative",
display: "inline-flex",
...style
}
}, children, show && label && /*#__PURE__*/React.createElement("span", {
role: "tooltip",
style: {
position: "absolute",
zIndex: 50,
...pos,
padding: "5px 9px",
borderRadius: "var(--radius-sm)",
background: "var(--ctv-surface-3)",
border: "1px solid var(--border-control)",
boxShadow: "var(--shadow-pop)",
color: "var(--text-primary)",
font: "var(--weight-medium) var(--text-xs)/1.2 var(--font-sans)",
whiteSpace: "nowrap",
pointerEvents: "none"
}
}, label));
}
Object.assign(__ds_scope, { Tooltip });
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/feedback/Tooltip.jsx", error: String((e && e.message) || e) }); }
// components/forms/Button.jsx
try { (() => {
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
const sizeMap = {
sm: {
height: "var(--control-h-sm)",
padding: "0 12px",
font: "var(--text-xs)"
},
md: {
height: "var(--control-h)",
padding: "0 14px",
font: "var(--text-sm)"
}
};
const variantStyle = {
primary: {
background: "var(--action-primary)",
color: "var(--text-on-accent)",
border: "1px solid transparent"
},
secondary: {
background: "var(--ctv-surface-2)",
color: "var(--text-primary)",
border: "1px solid var(--border-control)"
},
ghost: {
background: "transparent",
color: "var(--text-secondary)",
border: "1px solid transparent"
},
danger: {
background: "var(--ctv-error-soft)",
color: "var(--status-error)",
border: "1px solid rgba(229,72,77,0.35)"
}
};
const hoverBg = {
primary: "var(--action-primary-hover)",
secondary: "var(--ctv-surface-3)",
ghost: "var(--ctv-surface-2)",
danger: "rgba(229,72,77,0.22)"
};
/**
* ChicoryTV primary action button.
*/
function Button({
children,
variant = "primary",
size = "md",
startIcon = null,
endIcon = null,
disabled = false,
loading = false,
fullWidth = false,
onClick,
type = "button",
style = {},
...rest
}) {
const [hover, setHover] = React.useState(false);
const [active, setActive] = React.useState(false);
const s = sizeMap[size] || sizeMap.md;
const v = variantStyle[variant] || variantStyle.primary;
const isDisabled = disabled || loading;
return /*#__PURE__*/React.createElement("button", _extends({
type: type,
disabled: isDisabled,
onClick: onClick,
onMouseEnter: () => setHover(true),
onMouseLeave: () => {
setHover(false);
setActive(false);
},
onMouseDown: () => setActive(true),
onMouseUp: () => setActive(false),
style: {
display: "inline-flex",
alignItems: "center",
justifyContent: "center",
gap: "7px",
height: s.height,
padding: s.padding,
width: fullWidth ? "100%" : "auto",
font: `var(--weight-medium) ${s.font}/1 var(--font-sans)`,
letterSpacing: "0.01em",
borderRadius: "var(--radius-sm)",
cursor: isDisabled ? "not-allowed" : "pointer",
opacity: isDisabled ? 0.45 : 1,
whiteSpace: "nowrap",
transition: "background var(--dur-fast) var(--ease-standard), transform var(--dur-fast) var(--ease-standard)",
transform: active && !isDisabled ? "translateY(0.5px)" : "none",
...v,
background: !isDisabled && hover ? hoverBg[variant] : v.background,
...style
}
}, rest), loading ? /*#__PURE__*/React.createElement(Spinner16, null) : startIcon, children, !loading && endIcon);
}
function Spinner16() {
return /*#__PURE__*/React.createElement("svg", {
width: "14",
height: "14",
viewBox: "0 0 24 24",
fill: "none",
style: {
animation: "ctv-spin 0.7s linear infinite"
}
}, /*#__PURE__*/React.createElement("style", null, `@keyframes ctv-spin{to{transform:rotate(360deg)}}`), /*#__PURE__*/React.createElement("circle", {
cx: "12",
cy: "12",
r: "9",
stroke: "currentColor",
strokeOpacity: "0.25",
strokeWidth: "3"
}), /*#__PURE__*/React.createElement("path", {
d: "M21 12a9 9 0 0 0-9-9",
stroke: "currentColor",
strokeWidth: "3",
strokeLinecap: "round"
}));
}
Object.assign(__ds_scope, { Button });
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/forms/Button.jsx", error: String((e && e.message) || e) }); }
// components/forms/Checkbox.jsx
try { (() => {
/**
* Checkbox with label. Controlled via checked/onChange.
*/
function Checkbox({
checked = false,
onChange,
disabled = false,
label,
indeterminate = false,
style = {}
}) {
const toggle = () => {
if (!disabled && onChange) onChange(!checked);
};
const on = checked || indeterminate;
return /*#__PURE__*/React.createElement("label", {
style: {
display: "inline-flex",
alignItems: "center",
gap: "8px",
cursor: disabled ? "not-allowed" : "pointer",
opacity: disabled ? 0.5 : 1,
...style
}
}, /*#__PURE__*/React.createElement("span", {
role: "checkbox",
"aria-checked": indeterminate ? "mixed" : checked,
onClick: toggle,
style: {
display: "inline-flex",
alignItems: "center",
justifyContent: "center",
width: 17,
height: 17,
flex: "0 0 auto",
borderRadius: "var(--radius-xs)",
background: on ? "var(--action-primary)" : "var(--ctv-bg-sunken)",
border: `1px solid ${on ? "transparent" : "var(--border-control)"}`,
transition: "background var(--dur-fast), border-color var(--dur-fast)"
}
}, indeterminate ? /*#__PURE__*/React.createElement("svg", {
width: "11",
height: "11",
viewBox: "0 0 24 24"
}, /*#__PURE__*/React.createElement("path", {
d: "M6 12h12",
stroke: "#fff",
strokeWidth: "3",
strokeLinecap: "round"
})) : checked ? /*#__PURE__*/React.createElement("svg", {
width: "12",
height: "12",
viewBox: "0 0 24 24",
fill: "none"
}, /*#__PURE__*/React.createElement("path", {
d: "M5 12.5l4.5 4.5L19 7",
stroke: "#fff",
strokeWidth: "3",
strokeLinecap: "round",
strokeLinejoin: "round"
})) : null), label && /*#__PURE__*/React.createElement("span", {
style: {
font: "var(--text-sm)/1 var(--font-sans)",
color: "var(--text-primary)"
}
}, label));
}
Object.assign(__ds_scope, { Checkbox });
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/forms/Checkbox.jsx", error: String((e && e.message) || e) }); }
// components/forms/IconButton.jsx
try { (() => {
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
/**
* Compact square icon button — table row actions, toolbars.
*/
function IconButton({
children,
size = "md",
variant = "ghost",
disabled = false,
active = false,
title,
onClick,
style = {},
...rest
}) {
const [hover, setHover] = React.useState(false);
const dim = size === "sm" ? 28 : 32;
const base = variant === "solid" ? {
background: "var(--action-primary)",
color: "var(--text-on-accent)"
} : {
background: active ? "var(--ctv-surface-3)" : "transparent",
color: active ? "var(--text-primary)" : "var(--text-secondary)"
};
const hoverBg = variant === "solid" ? "var(--action-primary-hover)" : "var(--ctv-surface-2)";
return /*#__PURE__*/React.createElement("button", _extends({
type: "button",
title: title,
"aria-label": title,
disabled: disabled,
onClick: onClick,
onMouseEnter: () => setHover(true),
onMouseLeave: () => setHover(false),
style: {
display: "inline-flex",
alignItems: "center",
justifyContent: "center",
width: dim,
height: dim,
flex: "0 0 auto",
borderRadius: "var(--radius-sm)",
border: "1px solid transparent",
cursor: disabled ? "not-allowed" : "pointer",
opacity: disabled ? 0.4 : 1,
color: base.color,
background: !disabled && hover ? hoverBg : base.background,
transition: "background var(--dur-fast) var(--ease-standard), color var(--dur-fast) var(--ease-standard)",
...(!disabled && hover && variant !== "solid" ? {
color: "var(--text-primary)"
} : {}),
...style
}
}, rest), children);
}
Object.assign(__ds_scope, { IconButton });
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/forms/IconButton.jsx", error: String((e && e.message) || e) }); }
// components/forms/Input.jsx
try { (() => {
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
/**
* Text input with optional label, leading icon and error state.
*/
function Input({
label,
value,
onChange,
placeholder,
leadingIcon = null,
trailing = null,
error = null,
disabled = false,
size = "md",
type = "text",
fullWidth = true,
style = {},
...rest
}) {
const [focus, setFocus] = React.useState(false);
const height = size === "sm" ? "var(--control-h-sm)" : "var(--control-h)";
const borderColor = error ? "var(--status-error)" : focus ? "var(--action-primary)" : "var(--border-control)";
return /*#__PURE__*/React.createElement("label", {
style: {
display: "block",
width: fullWidth ? "100%" : "auto"
}
}, label && /*#__PURE__*/React.createElement("span", {
style: {
display: "block",
marginBottom: "6px",
font: "var(--weight-medium) var(--text-xs)/1 var(--font-sans)",
color: "var(--text-secondary)"
}
}, label), /*#__PURE__*/React.createElement("span", {
style: {
display: "flex",
alignItems: "center",
gap: "8px",
height,
padding: "0 10px",
background: "var(--ctv-bg-sunken)",
border: `1px solid ${borderColor}`,
borderRadius: "var(--radius-sm)",
boxShadow: focus && !error ? "0 0 0 3px var(--focus-ring)" : "none",
transition: "border-color var(--dur-fast) var(--ease-standard), box-shadow var(--dur-fast) var(--ease-standard)",
opacity: disabled ? 0.5 : 1,
...style
}
}, leadingIcon && /*#__PURE__*/React.createElement("span", {
style: {
display: "flex",
color: "var(--text-disabled)",
flex: "0 0 auto"
}
}, leadingIcon), /*#__PURE__*/React.createElement("input", _extends({
type: type,
value: value,
onChange: onChange,
placeholder: placeholder,
disabled: disabled,
onFocus: () => setFocus(true),
onBlur: () => setFocus(false),
style: {
flex: 1,
minWidth: 0,
background: "transparent",
border: "none",
outline: "none",
color: "var(--text-primary)",
font: "var(--weight-normal) var(--text-sm)/1 var(--font-sans)"
}
}, rest)), trailing && /*#__PURE__*/React.createElement("span", {
style: {
display: "flex",
flex: "0 0 auto"
}
}, trailing)), error && /*#__PURE__*/React.createElement("span", {
style: {
display: "block",
marginTop: "5px",
font: "var(--text-xs)/1.3 var(--font-sans)",
color: "var(--status-error)"
}
}, error));
}
Object.assign(__ds_scope, { Input });
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/forms/Input.jsx", error: String((e && e.message) || e) }); }
// components/forms/Select.jsx
try { (() => {
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
/**
* Styled select built on a native <select> for accessibility.
*/
function Select({
label,
value,
onChange,
options = [],
disabled = false,
size = "md",
fullWidth = true,
style = {},
...rest
}) {
const [focus, setFocus] = React.useState(false);
const height = size === "sm" ? "var(--control-h-sm)" : "var(--control-h)";
const opts = options.map(o => typeof o === "string" ? {
value: o,
label: o
} : o);
return /*#__PURE__*/React.createElement("label", {
style: {
display: "block",
width: fullWidth ? "100%" : "auto"
}
}, label && /*#__PURE__*/React.createElement("span", {
style: {
display: "block",
marginBottom: "6px",
font: "var(--weight-medium) var(--text-xs)/1 var(--font-sans)",
color: "var(--text-secondary)"
}
}, label), /*#__PURE__*/React.createElement("span", {
style: {
position: "relative",
display: "flex",
alignItems: "center",
height,
background: "var(--ctv-bg-sunken)",
border: `1px solid ${focus ? "var(--action-primary)" : "var(--border-control)"}`,
borderRadius: "var(--radius-sm)",
boxShadow: focus ? "0 0 0 3px var(--focus-ring)" : "none",
transition: "border-color var(--dur-fast), box-shadow var(--dur-fast)",
opacity: disabled ? 0.5 : 1,
...style
}
}, /*#__PURE__*/React.createElement("select", _extends({
value: value,
onChange: onChange,
disabled: disabled,
onFocus: () => setFocus(true),
onBlur: () => setFocus(false),
style: {
appearance: "none",
WebkitAppearance: "none",
flex: 1,
height: "100%",
padding: "0 30px 0 10px",
background: "transparent",
border: "none",
outline: "none",
color: "var(--text-primary)",
font: "var(--weight-normal) var(--text-sm)/1 var(--font-sans)",
cursor: disabled ? "not-allowed" : "pointer"
}
}, rest), opts.map(o => /*#__PURE__*/React.createElement("option", {
key: o.value,
value: o.value,
style: {
background: "var(--ctv-surface-2)",
color: "var(--text-primary)"
}
}, o.label))), /*#__PURE__*/React.createElement("svg", {
width: "14",
height: "14",
viewBox: "0 0 24 24",
fill: "none",
style: {
position: "absolute",
right: 9,
pointerEvents: "none",
color: "var(--text-disabled)"
}
}, /*#__PURE__*/React.createElement("path", {
d: "M6 9l6 6 6-6",
stroke: "currentColor",
strokeWidth: "2",
strokeLinecap: "round",
strokeLinejoin: "round"
}))));
}
Object.assign(__ds_scope, { Select });
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/forms/Select.jsx", error: String((e && e.message) || e) }); }
// components/forms/Switch.jsx
try { (() => {
/**
* On/off toggle switch.
*/
function Switch({
checked = false,
onChange,
disabled = false,
label,
size = "md",
style = {}
}) {
const w = size === "sm" ? 30 : 36;
const h = size === "sm" ? 17 : 20;
const knob = h - 5;
const toggle = () => {
if (!disabled && onChange) onChange(!checked);
};
return /*#__PURE__*/React.createElement("label", {
style: {
display: "inline-flex",
alignItems: "center",
gap: "9px",
cursor: disabled ? "not-allowed" : "pointer",
opacity: disabled ? 0.5 : 1,
...style
}
}, /*#__PURE__*/React.createElement("span", {
role: "switch",
"aria-checked": checked,
onClick: toggle,
style: {
position: "relative",
width: w,
height: h,
flex: "0 0 auto",
borderRadius: "var(--radius-pill)",
background: checked ? "var(--action-primary)" : "var(--ctv-surface-3)",
border: `1px solid ${checked ? "transparent" : "var(--border-control)"}`,
transition: "background var(--dur-base) var(--ease-standard)"
}
}, /*#__PURE__*/React.createElement("span", {
style: {
position: "absolute",
top: "50%",
left: checked ? `calc(100% - ${knob + 2}px)` : "2px",
transform: "translateY(-50%)",
width: knob,
height: knob,
borderRadius: "50%",
background: "#fff",
boxShadow: "0 1px 2px rgba(0,0,0,0.5)",
transition: "left var(--dur-base) var(--ease-out)"
}
})), label && /*#__PURE__*/React.createElement("span", {
style: {
font: "var(--text-sm)/1 var(--font-sans)",
color: "var(--text-primary)"
}
}, label));
}
Object.assign(__ds_scope, { Switch });
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/forms/Switch.jsx", error: String((e && e.message) || e) }); }
// components/navigation/NavItem.jsx
try { (() => {
/**
* Single left-sidebar navigation item. Compose a list of these.
*/
function NavItem({
icon = null,
label,
active = false,
badge = null,
badgeTone = "warn",
onClick,
href,
style = {}
}) {
const [hover, setHover] = React.useState(false);
const Comp = href ? "a" : "button";
const bt = {
warn: "var(--status-warn)",
error: "var(--status-error)",
accent: "var(--action-primary)"
}[badgeTone];
return /*#__PURE__*/React.createElement(Comp, {
href: href,
onClick: onClick,
onMouseEnter: () => setHover(true),
onMouseLeave: () => setHover(false),
style: {
position: "relative",
display: "flex",
alignItems: "center",
gap: "10px",
width: "100%",
height: "34px",
padding: "0 10px",
border: "none",
borderRadius: "var(--radius-sm)",
textDecoration: "none",
cursor: "pointer",
textAlign: "left",
font: `${active ? "var(--weight-medium)" : "var(--weight-normal)"} var(--text-sm)/1 var(--font-sans)`,
color: active ? "var(--text-primary)" : hover ? "var(--text-primary)" : "var(--text-secondary)",
background: active ? "var(--ctv-accent-soft)" : hover ? "var(--ctv-surface-2)" : "transparent",
transition: "background var(--dur-fast) var(--ease-standard), color var(--dur-fast)",
...style
}
}, active && /*#__PURE__*/React.createElement("span", {
style: {
position: "absolute",
left: 0,
top: 7,
bottom: 7,
width: 2.5,
borderRadius: 2,
background: "var(--action-primary)"
}
}), icon && /*#__PURE__*/React.createElement("span", {
style: {
display: "inline-flex",
color: active ? "var(--action-primary)" : "currentColor",
flex: "0 0 auto"
}
}, icon), /*#__PURE__*/React.createElement("span", {
style: {
flex: 1,
minWidth: 0,
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap"
}
}, label), badge != null && /*#__PURE__*/React.createElement("span", {
style: {
minWidth: 17,
height: 17,
padding: "0 5px",
display: "inline-flex",
alignItems: "center",
justifyContent: "center",
borderRadius: "var(--radius-pill)",
background: bt,
color: badgeTone === "warn" ? "#1A1206" : "#fff",
font: "var(--weight-semibold) var(--text-2xs)/1 var(--font-mono)"
}
}, badge));
}
/**
* Sidebar section label (eyebrow) that groups NavItems.
*/
function NavSection({
children,
style = {}
}) {
return /*#__PURE__*/React.createElement("div", {
style: {
padding: "14px 10px 6px",
font: "var(--weight-semibold) var(--text-2xs)/1 var(--font-sans)",
letterSpacing: "var(--tracking-caps)",
textTransform: "uppercase",
color: "var(--text-disabled)",
...style
}
}, children);
}
Object.assign(__ds_scope, { NavItem, NavSection });
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/navigation/NavItem.jsx", error: String((e && e.message) || e) }); }
// components/navigation/Tabs.jsx
try { (() => {
/**
* Underlined tab bar. Controlled via value/onChange.
*/
function Tabs({
tabs = [],
value,
onChange,
style = {}
}) {
const items = tabs.map(t => typeof t === "string" ? {
value: t,
label: t
} : t);
const active = value ?? items[0]?.value;
return /*#__PURE__*/React.createElement("div", {
style: {
display: "flex",
gap: "2px",
borderBottom: "1px solid var(--border-hairline)",
...style
}
}, items.map(t => {
const on = t.value === active;
return /*#__PURE__*/React.createElement(Tab, {
key: t.value,
tab: t,
on: on,
onChange: onChange
});
}));
}
function Tab({
tab,
on,
onChange
}) {
const [hover, setHover] = React.useState(false);
return /*#__PURE__*/React.createElement("button", {
type: "button",
onClick: () => onChange && onChange(tab.value),
onMouseEnter: () => setHover(true),
onMouseLeave: () => setHover(false),
style: {
position: "relative",
display: "inline-flex",
alignItems: "center",
gap: "7px",
height: "34px",
padding: "0 12px",
border: "none",
background: "transparent",
cursor: "pointer",
font: `${on ? "var(--weight-medium)" : "var(--weight-normal)"} var(--text-sm)/1 var(--font-sans)`,
color: on ? "var(--text-primary)" : hover ? "var(--text-primary)" : "var(--text-secondary)",
transition: "color var(--dur-fast)"
}
}, tab.icon && /*#__PURE__*/React.createElement("span", {
style: {
display: "inline-flex",
color: on ? "var(--action-primary)" : "currentColor"
}
}, tab.icon), tab.label, tab.count != null && /*#__PURE__*/React.createElement("span", {
style: {
font: "var(--text-2xs)/1 var(--font-mono)",
color: "var(--text-disabled)",
background: "var(--ctv-surface-3)",
padding: "2px 5px",
borderRadius: "var(--radius-xs)"
}
}, tab.count), /*#__PURE__*/React.createElement("span", {
style: {
position: "absolute",
left: 6,
right: 6,
bottom: -1,
height: 2,
borderRadius: 2,
background: on ? "var(--action-primary)" : "transparent",
transition: "background var(--dur-fast)"
}
}));
}
Object.assign(__ds_scope, { Tabs });
})(); } catch (e) { __ds_ns.__errors.push({ path: "components/navigation/Tabs.jsx", error: String((e && e.message) || e) }); }
// design_handoff_channel_builder/ChannelBuilder.jsx
try { (() => {
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
// Channel Builder — GROUND-UP replacement for the four-editor create chain
// (Channel -> Schedule -> Schedule Items -> Playout). One library-to-lineup screen.
// Left: searchable library browser. Center: the channel lineup (add by double-click
// or drag; drag to reorder). Right: friendly options + Channel Template + Advanced.
// Behind the scenes "Create" builds Channel + Collection + Schedule + Items + Playout.
(function () {
const NS = window.ChicoryTVDesignSystem_eb3b61;
const {
Button,
IconButton,
Input,
Select,
Switch,
Badge,
ChannelLogo,
Tooltip
} = NS;
const Ico = window.Ico;
const mono = {
fontFamily: "var(--font-mono)",
fontVariantNumeric: "tabular-nums"
};
const eyebrow = {
font: "var(--weight-semibold) var(--text-2xs)/1 var(--font-sans)",
letterSpacing: "0.06em",
textTransform: "uppercase",
color: "var(--text-disabled)"
};
// ---- Mock library (shows / movies / music / docs across libraries) --------
const LIB = [{
id: "s1",
title: "Looney Tunes",
type: "Show",
library: "Cartoons",
sub: "247 shorts",
min: 96
}, {
id: "s2",
title: "Tom & Jerry",
type: "Show",
library: "Cartoons",
sub: "164 shorts",
min: 72
}, {
id: "s3",
title: "Popeye",
type: "Show",
library: "Cartoons",
sub: "108 shorts",
min: 54
}, {
id: "s4",
title: "The Flintstones",
type: "Show",
library: "Cartoons",
sub: "166 episodes",
min: 84
}, {
id: "s5",
title: "Scooby-Doo",
type: "Show",
library: "Cartoons",
sub: "97 episodes",
min: 60
}, {
id: "s6",
title: "Pink Panther",
type: "Show",
library: "Cartoons",
sub: "124 shorts",
min: 48
}, {
id: "m1",
title: "Blade Runner",
type: "Movie",
library: "Movies",
sub: "1982 · Sci-Fi",
min: 117
}, {
id: "m2",
title: "Alien",
type: "Movie",
library: "Movies",
sub: "1979 · Horror",
min: 117
}, {
id: "m3",
title: "The Thing",
type: "Movie",
library: "Movies",
sub: "1982 · Horror",
min: 109
}, {
id: "m4",
title: "Metropolis",
type: "Movie",
library: "Movies",
sub: "1927 · Sci-Fi",
min: 153
}, {
id: "m5",
title: "Nosferatu",
type: "Movie",
library: "Movies",
sub: "1922 · Horror",
min: 94
}, {
id: "t1",
title: "The Twilight Zone",
type: "Show",
library: "TV Shows",
sub: "156 episodes",
min: 51
}, {
id: "t2",
title: "Star Trek",
type: "Show",
library: "TV Shows",
sub: "79 episodes",
min: 50
}, {
id: "t3",
title: "Columbo",
type: "Show",
library: "TV Shows",
sub: "69 episodes",
min: 94
}, {
id: "t4",
title: "Cheers",
type: "Show",
library: "TV Shows",
sub: "275 episodes",
min: 24
}, {
id: "u1",
title: "80s Synthpop",
type: "Music",
library: "Music Videos",
sub: "212 videos",
min: 45
}, {
id: "u2",
title: "90s Alt Rock",
type: "Music",
library: "Music Videos",
sub: "188 videos",
min: 45
}, {
id: "u3",
title: "Chart Countdown",
type: "Music",
library: "Music Videos",
sub: "Top 40",
min: 60
}, {
id: "d1",
title: "Blue Planet",
type: "Doc",
library: "Documentaries",
sub: "8 episodes",
min: 50
}, {
id: "d2",
title: "Planet Earth",
type: "Doc",
library: "Documentaries",
sub: "11 episodes",
min: 50
}, {
id: "d3",
title: "Cosmos",
type: "Doc",
library: "Documentaries",
sub: "13 episodes",
min: 60
}];
const LIBRARIES = ["All", "Cartoons", "Movies", "TV Shows", "Music Videos", "Documentaries"];
const TYPE_ICON = {
Show: "Tv",
Movie: "Film",
Music: "Music",
Doc: "Radio",
Multi: "Layers",
Smart: "Sparkles",
Manual: "FolderTree",
Rerun: "Repeat"
};
// Collections — curated groups (Manual / Smart / Multi / Rerun) addable as a
// single lineup entry, so the builder isn't limited to raw library items.
const COLLECTIONS = [{
id: "c1",
title: "Prime Time Cartoons",
type: "Multi",
library: "Collection",
sub: "Multi · 62 items",
min: 180,
coll: true
}, {
id: "c2",
title: "Classic Sci-Fi",
type: "Smart",
library: "Collection",
sub: "Smart · rating \u2265 8",
min: 240,
coll: true
}, {
id: "c3",
title: "Retro Station IDs",
type: "Manual",
library: "Collection",
sub: "Manual · 24 items",
min: 12,
coll: true
}, {
id: "c4",
title: "Saturday Morning",
type: "Rerun",
library: "Collection",
sub: "Rerun block · weekly",
min: 120,
coll: true
}, {
id: "c5",
title: "Halloween Marathon",
type: "Manual",
library: "Collection",
sub: "Manual · 9 films",
min: 900,
coll: true
}];
// Channel Templates — bundle the clunky technical config; one is pre-selected.
const TEMPLATES = [{
id: "std",
name: "Standard",
builtin: true,
desc: "General-purpose 1080p H.264, retro bumpers.",
shuffle: false,
always: true,
sets: ["1080p H.264", "HLS Segmenter", "Sequential", "Always on", "Pre + post filler"]
}, {
id: "music",
name: "Music videos",
builtin: true,
desc: "Continuous rotation, no fillers, direct stream.",
shuffle: true,
always: true,
sets: ["720p H.264", "HLS Direct", "Shuffle", "Always on", "No filler"]
}, {
id: "film",
name: "Movie night",
builtin: true,
desc: "Film-grain HEVC, mid-roll ad breaks.",
shuffle: false,
always: false,
sets: ["1080p HEVC", "MPEG-TS", "Sequential", "On-demand", "Mid-roll ads"]
}, {
id: "retro",
name: "Retro Saturday AM",
builtin: false,
desc: "Your saved cartoon-block template.",
shuffle: true,
always: true,
sets: ["1080p H.264", "HLS Segmenter", "Shuffle", "Always on", "Bumpers + ads"]
}];
// Advanced (~30-field) override set, grouped. Values shown as template-inherited.
const ADV = [{
group: "Streaming",
fields: [["Streaming mode", "HLS Segmenter"], ["FFmpeg profile", "1080p H.264"], ["Resolution", "1920×1080"], ["Video bitrate", "8000 kbps"], ["Audio bitrate", "192 kbps"], ["Buffer size", "16000 kb"]]
}, {
group: "Filler",
fields: [["Pre-roll", "Retro Bumpers"], ["Mid-roll", "Ad Break"], ["Post-roll", "Outro"], ["Tail filler", "None"], ["Fallback", "Test Pattern"], ["Filler kind", "Pad to :00"]]
}, {
group: "Playback",
fields: [["Interleave", "On"], ["Keep multi-part together", "On"], ["Watermark", "Channel logo"], ["Subtitle mode", "Any"], ["Preferred audio", "English"], ["Preferred subtitle", "None"]]
}, {
group: "Behavior",
fields: [["Guide mode default", "Normal"], ["Song video mode", "Off"], ["On-demand", "Off"], ["Idle behavior", "Offline image"], ["Transcode audio", "Normalize"], ["Number scheme", "Auto"]]
}];
// deterministic low-chroma poster hue from a title
function hueOf(s) {
let h = 0;
for (let i = 0; i < s.length; i++) h = (h * 31 + s.charCodeAt(i)) % 360;
return h;
}
function fmtDur(totalMin) {
const h = Math.floor(totalMin / 60),
m = totalMin % 60;
if (h && m) return h + "h " + String(m).padStart(2, "0") + "m";
if (h) return h + "h";
return m + "m";
}
// ---- Overlay scroll region — hides native bar (no reflow when it appears),
// shows a subtle thumb only while scrolling, fading out when idle. ------
function ScrollArea({
children,
innerStyle,
scrollerRef
}) {
const local = React.useRef(null);
const timer = React.useRef(null);
const [thumb, setThumb] = React.useState(null);
const setRef = n => {
local.current = n;
if (scrollerRef) scrollerRef.current = n;
};
const update = () => {
const el = local.current;
if (!el) return;
const {
scrollTop,
scrollHeight,
clientHeight
} = el;
if (scrollHeight <= clientHeight + 2) {
setThumb(null);
return;
}
const track = clientHeight - 8;
const h = Math.max(28, track * clientHeight / scrollHeight);
const top = 4 + (track - h) * (scrollTop / (scrollHeight - clientHeight));
setThumb({
top,
h
});
clearTimeout(timer.current);
timer.current = setTimeout(() => setThumb(t => t ? {
...t,
hidden: true
} : null), 850);
};
React.useEffect(() => {
const id = setTimeout(update, 60);
return () => {
clearTimeout(id);
clearTimeout(timer.current);
};
}, []);
return /*#__PURE__*/React.createElement("div", {
style: {
position: "relative",
flex: 1,
minHeight: 0,
minWidth: 0
}
}, /*#__PURE__*/React.createElement("div", {
ref: setRef,
onScroll: update,
className: "ctv-hide-sb",
style: {
height: "100%",
overflowY: "auto",
overflowX: "hidden",
scrollbarWidth: "none",
scrollBehavior: "smooth",
...innerStyle
}
}, children), thumb && /*#__PURE__*/React.createElement("div", {
style: {
position: "absolute",
top: thumb.top,
right: 2,
width: 4,
height: thumb.h,
borderRadius: 3,
background: "var(--text-secondary)",
opacity: thumb.hidden ? 0 : 0.5,
transition: "opacity 260ms var(--ease-standard, ease)",
pointerEvents: "none"
}
}));
}
// ---- Segmented control — iOS-style sliding active highlight ---------------
function Segmented({
value,
options,
onChange,
style
}) {
const idx = Math.max(0, options.findIndex(o => o.value === value));
return /*#__PURE__*/React.createElement("div", {
style: {
position: "relative",
display: "grid",
gridAutoFlow: "column",
gridAutoColumns: "1fr",
padding: 2,
borderRadius: "var(--radius-sm)",
background: "var(--ctv-bg-sunken)",
border: "1px solid var(--border-hairline)",
...style
}
}, /*#__PURE__*/React.createElement("div", {
style: {
position: "absolute",
top: 2,
bottom: 2,
left: 2,
width: `calc((100% - 4px) / ${options.length})`,
transform: `translateX(${idx * 100}%)`,
background: "var(--ctv-surface-3)",
borderRadius: "var(--radius-xs)",
boxShadow: "var(--shadow-sm)",
transition: "transform 300ms cubic-bezier(0.16,1,0.3,1)",
pointerEvents: "none"
}
}), options.map(o => {
const on = o.value === value;
return /*#__PURE__*/React.createElement("button", {
key: o.value,
type: "button",
title: o.title || o.label,
onClick: () => onChange(o.value),
style: {
position: "relative",
zIndex: 1,
display: "inline-flex",
alignItems: "center",
justifyContent: "center",
gap: 6,
padding: "5px 10px",
border: "none",
background: "transparent",
cursor: "pointer",
color: on ? "var(--text-primary)" : "var(--text-secondary)",
transition: "color 220ms cubic-bezier(0.16,1,0.3,1)",
font: "var(--weight-medium) var(--text-2xs)/1 var(--font-sans)"
}
}, o.icon && /*#__PURE__*/React.createElement(Ico, {
n: o.icon,
s: 13
}), o.label);
}));
}
// ---- Poster placeholder (striped, tinted, mono meta) ----------------------
function Poster({
item,
w,
h,
mini
}) {
const hue = hueOf(item.title);
return /*#__PURE__*/React.createElement("div", {
style: {
position: "relative",
width: w,
height: h,
flex: "0 0 auto",
overflow: "hidden",
borderRadius: "var(--radius-sm)",
border: "1px solid var(--border-hairline)",
background: `linear-gradient(160deg, hsl(${hue} 24% 17%), hsl(${hue} 26% 12%))`
}
}, /*#__PURE__*/React.createElement("div", {
style: {
position: "absolute",
inset: 0,
backgroundImage: `repeating-linear-gradient(135deg, hsl(${hue} 22% 10% / .55) 0 1px, transparent 1px 9px)`
}
}), /*#__PURE__*/React.createElement("div", {
style: {
position: "absolute",
top: mini ? 4 : 7,
left: mini ? 4 : 7,
display: "inline-flex",
color: `hsl(${hue} 30% 68%)`
}
}, /*#__PURE__*/React.createElement(Ico, {
n: TYPE_ICON[item.type] || "Film",
s: mini ? 12 : 15
})), !mini && /*#__PURE__*/React.createElement("div", {
style: {
position: "absolute",
left: 0,
right: 0,
bottom: 0,
padding: "18px 8px 8px",
background: "linear-gradient(to top, rgba(11,13,17,.92), transparent)"
}
}, /*#__PURE__*/React.createElement("div", {
style: {
font: "var(--weight-semibold) var(--text-xs)/1.15 var(--font-sans)",
color: "var(--text-primary)",
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap"
}
}, item.title), /*#__PURE__*/React.createElement("div", {
style: {
marginTop: 3,
...mono,
font: "var(--text-2xs)/1 var(--font-mono)",
color: "var(--text-secondary)"
}
}, item.sub)));
}
// ---- Library card ----------------------------------------------------------
function LibCard({
item,
added,
compact,
onAdd,
onDragStart,
onDragEnd
}) {
if (compact) {
return /*#__PURE__*/React.createElement("div", {
draggable: true,
onDragStart: onDragStart,
onDragEnd: onDragEnd,
onDoubleClick: onAdd,
title: "Double-click or drag to add",
className: "ctv-press",
style: {
display: "flex",
alignItems: "center",
gap: 10,
padding: "6px 8px",
borderRadius: "var(--radius-sm)",
cursor: "grab",
background: added ? "color-mix(in srgb, var(--action-primary) 14%, transparent)" : "var(--surface-card)",
border: `1px solid ${added ? "color-mix(in srgb, var(--action-primary) 35%, transparent)" : "var(--border-hairline)"}`
}
}, /*#__PURE__*/React.createElement(Poster, {
item: item,
w: 30,
h: 40,
mini: true
}), /*#__PURE__*/React.createElement("div", {
style: {
flex: 1,
minWidth: 0
}
}, /*#__PURE__*/React.createElement("div", {
style: {
font: "var(--weight-medium) var(--text-xs)/1.15 var(--font-sans)",
color: "var(--text-primary)",
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap"
}
}, item.title), /*#__PURE__*/React.createElement("div", {
style: {
marginTop: 2,
font: "var(--text-2xs)/1 var(--font-sans)",
color: "var(--text-secondary)"
}
}, item.type, " \xB7 ", /*#__PURE__*/React.createElement("span", {
style: mono
}, item.sub))), /*#__PURE__*/React.createElement(IconButton, {
size: "sm",
title: added ? "Added" : "Add to lineup",
onClick: onAdd
}, /*#__PURE__*/React.createElement(Ico, {
n: added ? "Check" : "Plus",
s: 15,
color: added ? "var(--action-primary)" : undefined
})));
}
return /*#__PURE__*/React.createElement("div", {
draggable: true,
onDragStart: onDragStart,
onDragEnd: onDragEnd,
onDoubleClick: onAdd,
title: "Double-click or drag to add",
className: "ctv-lift",
style: {
position: "relative",
cursor: "grab"
}
}, /*#__PURE__*/React.createElement("div", {
style: {
position: "relative",
borderRadius: "var(--radius-sm)",
boxShadow: added ? "0 0 0 2px var(--action-primary)" : "none"
}
}, /*#__PURE__*/React.createElement(Poster, {
item: item,
w: "100%",
h: 150
}), added && /*#__PURE__*/React.createElement("div", {
style: {
position: "absolute",
top: 6,
right: 6,
width: 22,
height: 22,
borderRadius: "50%",
background: "var(--action-primary)",
color: "var(--text-on-accent)",
display: "flex",
alignItems: "center",
justifyContent: "center",
boxShadow: "var(--shadow-sm)",
animation: "ctv-pop 260ms cubic-bezier(0.16,1,0.3,1)"
}
}, /*#__PURE__*/React.createElement(Ico, {
n: "Check",
s: 14
}))));
}
// ---- Lineup row ------------------------------------------------------------
function LineupRow({
item,
index,
shuffle,
dragging,
isOver,
onRemove,
dragProps
}) {
return /*#__PURE__*/React.createElement("div", _extends({}, dragProps, {
className: "ctv-lift",
style: {
display: "flex",
alignItems: "center",
gap: 11,
padding: "9px 10px",
borderRadius: "var(--radius-md)",
background: "var(--surface-card)",
border: "1px solid var(--border-hairline)",
boxShadow: isOver ? "0 -2px 0 var(--action-primary)" : "none",
opacity: dragging ? 0.35 : 1,
transition: "opacity var(--dur-fast)"
}
}), /*#__PURE__*/React.createElement("span", {
style: {
display: "inline-flex",
cursor: "grab",
color: "var(--text-faint)",
flex: "0 0 auto"
},
title: "Drag to reorder"
}, /*#__PURE__*/React.createElement(Ico, {
n: "GripVertical",
s: 16
})), /*#__PURE__*/React.createElement("span", {
style: {
width: 22,
flex: "0 0 auto",
textAlign: "center"
}
}, shuffle ? /*#__PURE__*/React.createElement(Ico, {
n: "Shuffle",
s: 13,
color: "var(--text-disabled)"
}) : /*#__PURE__*/React.createElement("span", {
style: {
...mono,
font: "var(--weight-medium) var(--text-xs)/1 var(--font-mono)",
color: "var(--text-secondary)"
}
}, index + 1)), /*#__PURE__*/React.createElement(Poster, {
item: item,
w: 34,
h: 46,
mini: true
}), /*#__PURE__*/React.createElement("div", {
style: {
flex: 1,
minWidth: 0
}
}, /*#__PURE__*/React.createElement("div", {
style: {
font: "var(--weight-semibold) var(--text-sm)/1.2 var(--font-sans)",
color: "var(--text-primary)",
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap"
}
}, item.title), /*#__PURE__*/React.createElement("div", {
style: {
marginTop: 2,
font: "var(--text-2xs)/1 var(--font-sans)",
color: "var(--text-secondary)"
}
}, item.type, " \xB7 ", item.library)), /*#__PURE__*/React.createElement("span", {
style: {
...mono,
font: "var(--text-2xs)/1 var(--font-mono)",
color: "var(--text-disabled)",
flex: "0 0 auto"
}
}, fmtDur(item.min)), /*#__PURE__*/React.createElement(IconButton, {
size: "sm",
title: "Remove",
onClick: onRemove
}, /*#__PURE__*/React.createElement(Ico, {
n: "X",
s: 15
})));
}
// ---- Channel image dropzone + live logo/bug preview -----------------------
function ImagePanel({
logoSrc,
setLogoSrc,
name
}) {
const [sep, setSep] = React.useState(false);
const [over, setOver] = React.useState(false);
const fileRef = React.useRef(null);
const read = file => {
if (!file) return;
const r = new FileReader();
r.onload = () => setLogoSrc(r.result);
r.readAsDataURL(file);
};
return /*#__PURE__*/React.createElement("div", {
style: {
display: "flex",
flexDirection: "column",
gap: 10
}
}, /*#__PURE__*/React.createElement("div", {
onDragOver: e => {
e.preventDefault();
setOver(true);
},
onDragLeave: () => setOver(false),
onDrop: e => {
e.preventDefault();
setOver(false);
read(e.dataTransfer.files && e.dataTransfer.files[0]);
},
onClick: () => fileRef.current && fileRef.current.click(),
style: {
display: "flex",
gap: 12,
padding: 12,
borderRadius: "var(--radius-sm)",
cursor: "pointer",
background: "var(--ctv-bg-sunken)",
border: `1px dashed ${over ? "var(--ctv-accent)" : "var(--border-control)"}`
}
}, /*#__PURE__*/React.createElement(ChannelLogo, {
name: name || "New Channel",
src: logoSrc,
size: 52,
radius: "var(--radius-sm)"
}), /*#__PURE__*/React.createElement("div", {
style: {
flex: 1,
minWidth: 0,
display: "flex",
flexDirection: "column",
justifyContent: "center",
gap: 3
}
}, /*#__PURE__*/React.createElement("div", {
style: {
font: "var(--weight-medium) var(--text-xs)/1 var(--font-sans)",
color: "var(--text-primary)"
}
}, logoSrc ? "Channel image set" : "Drop channel image"), /*#__PURE__*/React.createElement("div", {
style: {
font: "var(--text-2xs)/1.35 var(--font-sans)",
color: "var(--text-secondary)"
}
}, "Used as both the guide logo and the on-screen bug.")), /*#__PURE__*/React.createElement("input", {
ref: fileRef,
type: "file",
accept: "image/*",
style: {
display: "none"
},
onChange: e => read(e.target.files && e.target.files[0])
})), /*#__PURE__*/React.createElement("div", {
style: {
display: "grid",
gridTemplateColumns: "1fr 1fr",
gap: 10
}
}, /*#__PURE__*/React.createElement("div", {
style: {
padding: 10,
borderRadius: "var(--radius-sm)",
background: "var(--surface-card)",
border: "1px solid var(--border-hairline)"
}
}, /*#__PURE__*/React.createElement("div", {
style: {
...eyebrow,
marginBottom: 8
}
}, "Guide logo"), /*#__PURE__*/React.createElement("div", {
style: {
display: "flex",
alignItems: "center",
gap: 9
}
}, /*#__PURE__*/React.createElement(ChannelLogo, {
name: name || "New Channel",
src: logoSrc,
size: 30,
radius: "var(--radius-xs)"
}), /*#__PURE__*/React.createElement("div", {
style: {
minWidth: 0
}
}, /*#__PURE__*/React.createElement("div", {
style: {
font: "var(--weight-medium) var(--text-2xs)/1.1 var(--font-sans)",
color: "var(--text-primary)",
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap"
}
}, name || "New Channel"), /*#__PURE__*/React.createElement("div", {
style: {
...mono,
font: "9px/1 var(--font-mono)",
color: "var(--text-disabled)"
}
}, "listing")))), /*#__PURE__*/React.createElement("div", {
style: {
padding: 10,
borderRadius: "var(--radius-sm)",
background: "var(--surface-card)",
border: "1px solid var(--border-hairline)"
}
}, /*#__PURE__*/React.createElement("div", {
style: {
...eyebrow,
marginBottom: 8
}
}, "On-screen bug"), /*#__PURE__*/React.createElement("div", {
style: {
position: "relative",
height: 42,
borderRadius: "var(--radius-xs)",
overflow: "hidden",
background: "linear-gradient(160deg,#1a1d24,#0e1014)",
backgroundImage: "repeating-linear-gradient(135deg, rgba(255,255,255,.03) 0 1px, transparent 1px 7px)"
}
}, /*#__PURE__*/React.createElement("div", {
style: {
position: "absolute",
bottom: 5,
right: 5,
opacity: 0.9
}
}, /*#__PURE__*/React.createElement(ChannelLogo, {
name: name || "New Channel",
src: logoSrc,
size: 22,
radius: "3px"
}))))), /*#__PURE__*/React.createElement("button", {
type: "button",
onClick: () => setSep(s => !s),
style: {
alignSelf: "flex-start",
display: "inline-flex",
alignItems: "center",
gap: 5,
background: "none",
border: "none",
cursor: "pointer",
padding: "2px 0",
font: "var(--text-2xs)/1 var(--font-sans)",
color: "var(--text-secondary)"
}
}, /*#__PURE__*/React.createElement(Ico, {
n: sep ? "ChevronDown" : "ChevronRight",
s: 13
}), "Set logo & bug separately"), sep && /*#__PURE__*/React.createElement("div", {
style: {
display: "grid",
gridTemplateColumns: "1fr 1fr",
gap: 10,
padding: 10,
borderRadius: "var(--radius-sm)",
background: "var(--ctv-bg-sunken)",
border: "1px solid var(--border-hairline)"
}
}, /*#__PURE__*/React.createElement(Button, {
size: "sm",
variant: "secondary",
startIcon: /*#__PURE__*/React.createElement(Ico, {
n: "Image",
s: 14
}),
onClick: () => fileRef.current && fileRef.current.click()
}, "Logo image"), /*#__PURE__*/React.createElement(Button, {
size: "sm",
variant: "secondary",
startIcon: /*#__PURE__*/React.createElement(Ico, {
n: "Image",
s: 14
}),
onClick: () => fileRef.current && fileRef.current.click()
}, "Bug image")));
}
// ---- Advanced disclosure — one-click Override (edit) or View defaults ------
function Advanced({
templateName,
scrollerRef
}) {
const [mode, setMode] = React.useState("closed"); // closed | view | override
const [vals, setVals] = React.useState(() => {
const o = {};
ADV.forEach(g => g.fields.forEach(([k, v]) => {
o[k] = v;
}));
return o;
});
const override = mode === "override";
const count = ADV.reduce((n, g) => n + g.fields.length, 0);
const fieldsRef = React.useRef(null);
const rootRef = React.useRef(null);
const [maxH, setMaxH] = React.useState(0);
React.useLayoutEffect(() => {
if (mode === "closed") setMaxH(0);else if (fieldsRef.current) setMaxH(fieldsRef.current.scrollHeight);
}, [mode]);
const toggle = m => {
const next = mode === m ? "closed" : m;
setMode(next);
if (next !== "closed" && scrollerRef && scrollerRef.current && rootRef.current) {
const sc = scrollerRef.current;
requestAnimationFrame(() => {
const target = sc.scrollTop + rootRef.current.getBoundingClientRect().top - sc.getBoundingClientRect().top - 20;
sc.scrollTo({
top: Math.max(0, target),
behavior: "smooth"
});
});
}
};
const tabBtn = active => ({
display: "inline-flex",
alignItems: "center",
justifyContent: "center",
gap: 6,
padding: "8px 10px",
borderRadius: "var(--radius-sm)",
cursor: "pointer",
font: "var(--weight-medium) var(--text-xs)/1 var(--font-sans)",
background: active ? "color-mix(in srgb, var(--action-primary) 14%, transparent)" : "var(--ctv-surface-2)",
color: active ? "var(--action-primary)" : "var(--text-secondary)",
border: `1px solid ${active ? "color-mix(in srgb, var(--action-primary) 38%, transparent)" : "var(--border-hairline)"}`,
transition: "background 200ms cubic-bezier(0.16,1,0.3,1), color 200ms cubic-bezier(0.16,1,0.3,1), border-color 200ms cubic-bezier(0.16,1,0.3,1)"
});
return /*#__PURE__*/React.createElement("div", {
ref: rootRef,
style: {
borderTop: "1px solid var(--border-hairline)",
paddingTop: 14
}
}, /*#__PURE__*/React.createElement("div", {
style: {
display: "flex",
alignItems: "center",
gap: 9,
marginBottom: 10
}
}, /*#__PURE__*/React.createElement(Ico, {
n: "SlidersHorizontal",
s: 16,
color: "var(--text-secondary)"
}), /*#__PURE__*/React.createElement("span", {
style: {
font: "var(--weight-medium) var(--text-sm)/1 var(--font-sans)",
color: "var(--text-primary)"
}
}, "Advanced"), /*#__PURE__*/React.createElement("span", {
style: {
flex: 1
}
}), /*#__PURE__*/React.createElement("span", {
style: {
...mono,
font: "var(--text-2xs)/1 var(--font-mono)",
color: "var(--text-disabled)"
}
}, count, " fields \xB7 ", templateName)), /*#__PURE__*/React.createElement("div", {
style: {
display: "grid",
gridTemplateColumns: "1fr 1fr",
gap: 8
}
}, /*#__PURE__*/React.createElement("button", {
type: "button",
onClick: () => toggle("override"),
style: tabBtn(override)
}, /*#__PURE__*/React.createElement(Ico, {
n: "SquarePen",
s: 14
}), override ? "Overriding" : "Override settings"), /*#__PURE__*/React.createElement("button", {
type: "button",
onClick: () => toggle("view"),
style: tabBtn(mode === "view")
}, /*#__PURE__*/React.createElement(Ico, {
n: "Eye",
s: 14
}), "View defaults")), /*#__PURE__*/React.createElement("div", {
style: {
overflow: "hidden",
maxHeight: maxH,
transition: "max-height 340ms cubic-bezier(0.16,1,0.3,1)"
}
}, /*#__PURE__*/React.createElement("div", {
ref: fieldsRef,
style: {
marginTop: 12,
display: "flex",
flexDirection: "column",
gap: 14
}
}, /*#__PURE__*/React.createElement("div", {
style: {
display: "flex",
alignItems: "center",
gap: 8,
font: "var(--text-2xs)/1.35 var(--font-sans)",
color: "var(--text-secondary)"
}
}, /*#__PURE__*/React.createElement(Ico, {
n: "Info",
s: 13,
color: override ? "var(--action-primary)" : "var(--text-disabled)"
}), override ? "Editing these overrides the template for this channel only." : `Read-only — inherited from the ${templateName} template. Turn on Override to edit.`), ADV.map(g => /*#__PURE__*/React.createElement("div", {
key: g.group
}, /*#__PURE__*/React.createElement("div", {
style: {
...eyebrow,
marginBottom: 8
}
}, g.group), override ? /*#__PURE__*/React.createElement("div", {
style: {
display: "grid",
gridTemplateColumns: "1fr 1fr",
gap: 8
}
}, g.fields.map(([k]) => /*#__PURE__*/React.createElement(Input, {
key: k,
size: "sm",
label: k,
value: vals[k],
onChange: e => setVals(s => ({
...s,
[k]: e.target.value
}))
}))) : /*#__PURE__*/React.createElement("div", {
style: {
display: "grid",
gridTemplateColumns: "1fr 1fr",
gap: 8
}
}, g.fields.map(([k, v]) => /*#__PURE__*/React.createElement("div", {
key: k,
style: {
padding: "7px 9px",
borderRadius: "var(--radius-xs)",
background: "var(--ctv-bg-sunken)",
border: "1px solid var(--border-hairline)",
opacity: 0.72
}
}, /*#__PURE__*/React.createElement("div", {
style: {
font: "var(--text-2xs)/1 var(--font-sans)",
color: "var(--text-disabled)"
}
}, k), /*#__PURE__*/React.createElement("div", {
style: {
marginTop: 3,
font: "var(--weight-medium) var(--text-xs)/1 var(--font-sans)",
color: "var(--text-primary)",
...(/\d/.test(v) ? mono : {})
}
}, v)))))))));
}
// ---- Main ------------------------------------------------------------------
function ChannelBuilder() {
const [q, setQ] = React.useState("");
const [libFilter, setLibFilter] = React.useState("All");
const [compact, setCompact] = React.useState(false);
const [lineup, setLineup] = React.useState(() => ["s1", "s2", "s3", "s4"].map(id => LIB.find(x => x.id === id)));
const [name, setName] = React.useState("Retro Cartoons");
const [number, setNumber] = React.useState("13.1");
const [shuffle, setShuffle] = React.useState(false);
const [always, setAlways] = React.useState(true);
const [logoSrc, setLogoSrc] = React.useState(null);
const [tpl, setTpl] = React.useState("std");
const [tplOpen, setTplOpen] = React.useState(false);
const [source, setSource] = React.useState("library"); // library | collections
// drag state
const [dragLib, setDragLib] = React.useState(null); // library item id being dragged
const [dragRow, setDragRow] = React.useState(null); // lineup index being dragged
const [overRow, setOverRow] = React.useState(null);
const railScrollerRef = React.useRef(null);
const addedIds = new Set(lineup.map(i => i.id));
const pool = source === "collections" ? COLLECTIONS : LIB;
const filtered = pool.filter(i => (source === "collections" || libFilter === "All" || i.library === libFilter) && (!q || i.title.toLowerCase().includes(q.toLowerCase())));
const add = item => setLineup(l => l.some(x => x.id === item.id) ? l : [...l, item]);
const removeAt = idx => setLineup(l => l.filter((_, i) => i !== idx));
const totalMin = lineup.reduce((n, i) => n + i.min, 0);
const template = TEMPLATES.find(t => t.id === tpl);
const canCreate = name.trim() && lineup.length > 0;
// reorder / insert on drop at target index
const dropAt = targetIdx => {
if (dragRow != null) {
setLineup(l => {
const n = l.slice();
const [m] = n.splice(dragRow, 1);
n.splice(targetIdx, 0, m);
return n;
});
} else if (dragLib != null) {
const item = [...LIB, ...COLLECTIONS].find(x => x.id === dragLib);
if (item && !addedIds.has(item.id)) setLineup(l => {
const n = l.slice();
n.splice(targetIdx, 0, item);
return n;
});
}
setDragLib(null);
setDragRow(null);
setOverRow(null);
};
const dropEnd = () => {
if (dragLib != null) {
const item = [...LIB, ...COLLECTIONS].find(x => x.id === dragLib);
if (item) add(item);
}
setDragLib(null);
setDragRow(null);
setOverRow(null);
};
return /*#__PURE__*/React.createElement("div", {
style: {
display: "flex",
flexDirection: "column",
height: "100%"
}
}, /*#__PURE__*/React.createElement("div", {
style: {
display: "flex",
alignItems: "center",
gap: 12,
padding: "12px 20px",
borderBottom: "1px solid var(--border-hairline)",
flex: "0 0 auto"
}
}, /*#__PURE__*/React.createElement("span", {
style: {
display: "inline-flex",
alignItems: "center",
justifyContent: "center",
width: 34,
height: 34,
borderRadius: "var(--radius-sm)",
background: "var(--ctv-accent-soft)",
color: "var(--ctv-accent)",
flex: "0 0 auto"
}
}, /*#__PURE__*/React.createElement(Ico, {
n: "Plus",
s: 19
})), /*#__PURE__*/React.createElement("div", {
style: {
flex: 1,
minWidth: 0
}
}, /*#__PURE__*/React.createElement("div", {
style: {
font: "var(--weight-semibold) var(--text-md)/1 var(--font-sans)",
color: "var(--text-primary)"
}
}, "New Channel"), /*#__PURE__*/React.createElement("div", {
style: {
marginTop: 3,
font: "var(--text-xs)/1 var(--font-sans)",
color: "var(--text-secondary)"
}
}, "Build from your library \u2014 ", /*#__PURE__*/React.createElement("span", {
style: mono
}, lineup.length), " in lineup \xB7 ", /*#__PURE__*/React.createElement("span", {
style: mono
}, fmtDur(totalMin)))), /*#__PURE__*/React.createElement(Button, {
variant: "ghost"
}, "Cancel"), /*#__PURE__*/React.createElement(Tooltip, {
placement: "bottom",
label: canCreate ? "Creates the channel, schedule & playout" : "Name the channel and add at least one item"
}, /*#__PURE__*/React.createElement(Button, {
variant: "primary",
disabled: !canCreate,
startIcon: /*#__PURE__*/React.createElement(Ico, {
n: "Check",
s: 15
})
}, "Create Channel"))), /*#__PURE__*/React.createElement("div", {
style: {
flex: 1,
minHeight: 0,
display: "grid",
gridTemplateColumns: "minmax(0,1.2fr) minmax(0,1fr) 304px",
gap: 0
}
}, /*#__PURE__*/React.createElement("section", {
style: {
display: "flex",
flexDirection: "column",
minHeight: 0,
minWidth: 0,
overflow: "hidden",
borderRight: "1px solid var(--border-hairline)"
}
}, /*#__PURE__*/React.createElement("div", {
style: {
padding: "14px 16px 10px",
display: "flex",
flexDirection: "column",
gap: 10
}
}, /*#__PURE__*/React.createElement("div", {
style: {
display: "flex",
alignItems: "center",
gap: 10,
flexWrap: "wrap",
rowGap: 8
}
}, /*#__PURE__*/React.createElement(Segmented, {
value: source,
onChange: setSource,
options: [{
value: "library",
label: "Library",
icon: "Library"
}, {
value: "collections",
label: "Collections",
icon: "FolderTree"
}]
}), /*#__PURE__*/React.createElement("span", {
style: {
flex: 1
}
}), /*#__PURE__*/React.createElement(Segmented, {
value: compact ? "list" : "grid",
onChange: v => setCompact(v === "list"),
options: [{
value: "grid",
icon: "LayoutGrid",
title: "Poster grid"
}, {
value: "list",
icon: "Rows3",
title: "Compact list"
}]
})), /*#__PURE__*/React.createElement(Input, {
size: "sm",
value: q,
onChange: e => setQ(e.target.value),
leadingIcon: /*#__PURE__*/React.createElement(Ico, {
n: "Search",
s: 15
}),
placeholder: source === "collections" ? "Search collections\u2026" : "Search shows & movies\u2026"
}), source === "library" && /*#__PURE__*/React.createElement("div", {
style: {
display: "flex",
gap: 6,
flexWrap: "wrap"
}
}, LIBRARIES.map(l => {
const on = libFilter === l;
return /*#__PURE__*/React.createElement("button", {
key: l,
type: "button",
onClick: () => setLibFilter(l),
className: "ctv-press",
style: {
padding: "4px 10px",
borderRadius: "var(--radius-pill)",
cursor: "pointer",
font: "var(--weight-medium) var(--text-2xs)/1.4 var(--font-sans)",
background: on ? "var(--ctv-accent-soft)" : "var(--ctv-surface-2)",
color: on ? "var(--ctv-accent)" : "var(--text-secondary)",
border: `1px solid ${on ? "rgba(224,138,60,.35)" : "var(--border-hairline)"}`
}
}, l);
}))), /*#__PURE__*/React.createElement(ScrollArea, {
innerStyle: {
padding: "4px 16px 16px"
}
}, filtered.length === 0 ? /*#__PURE__*/React.createElement("div", {
style: {
padding: "40px 0",
textAlign: "center",
font: "var(--text-xs)/1.5 var(--font-sans)",
color: "var(--text-disabled)"
}
}, "No titles match.") : compact ? /*#__PURE__*/React.createElement("div", {
style: {
display: "flex",
flexDirection: "column",
gap: 6
}
}, filtered.map(item => /*#__PURE__*/React.createElement(LibCard, {
key: item.id,
item: item,
compact: true,
added: addedIds.has(item.id),
onAdd: () => add(item),
onDragStart: () => setDragLib(item.id),
onDragEnd: dropEnd
}))) : /*#__PURE__*/React.createElement("div", {
style: {
display: "grid",
gridTemplateColumns: "repeat(auto-fill, minmax(104px,1fr))",
gap: 10
}
}, filtered.map(item => /*#__PURE__*/React.createElement(LibCard, {
key: item.id,
item: item,
added: addedIds.has(item.id),
onAdd: () => add(item),
onDragStart: () => setDragLib(item.id),
onDragEnd: dropEnd
}))))), /*#__PURE__*/React.createElement("section", {
style: {
display: "flex",
flexDirection: "column",
minHeight: 0,
minWidth: 0,
overflow: "hidden",
borderRight: "1px solid var(--border-hairline)"
},
onDragOver: e => {
if (dragLib != null) e.preventDefault();
},
onDrop: () => {
if (dragLib != null) dropEnd();
}
}, /*#__PURE__*/React.createElement("div", {
style: {
padding: "14px 16px 10px",
display: "flex",
alignItems: "center",
gap: 10
}
}, /*#__PURE__*/React.createElement("span", {
style: eyebrow
}, "Lineup"), /*#__PURE__*/React.createElement(Badge, {
tone: shuffle ? "accent" : "neutral"
}, /*#__PURE__*/React.createElement("span", {
style: {
display: "inline-flex",
alignItems: "center",
gap: 4
}
}, /*#__PURE__*/React.createElement(Ico, {
n: shuffle ? "Shuffle" : "ListOrdered",
s: 11
}), shuffle ? "Shuffled" : "Sequential")), /*#__PURE__*/React.createElement("span", {
style: {
flex: 1
}
}), lineup.length > 0 && /*#__PURE__*/React.createElement("button", {
type: "button",
onClick: () => setLineup([]),
style: {
background: "none",
border: "none",
cursor: "pointer",
font: "var(--text-2xs)/1 var(--font-sans)",
color: "var(--text-secondary)"
}
}, "Clear")), /*#__PURE__*/React.createElement(ScrollArea, {
innerStyle: {
padding: "4px 16px 16px"
}
}, lineup.length === 0 ? /*#__PURE__*/React.createElement("div", {
style: {
height: "100%",
minHeight: 220,
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
gap: 12,
textAlign: "center",
border: `1px dashed ${dragLib != null ? "var(--ctv-accent)" : "var(--border-control)"}`,
borderRadius: "var(--radius-md)",
background: dragLib != null ? "var(--ctv-accent-soft)" : "transparent",
color: "var(--text-disabled)",
padding: 24
}
}, /*#__PURE__*/React.createElement(Ico, {
n: "ListVideo",
s: 26
}), /*#__PURE__*/React.createElement("div", {
style: {
font: "var(--text-sm)/1.5 var(--font-sans)",
maxWidth: 240
}
}, "This channel has no content yet. Double-click or drag titles from the library to build the lineup.")) : /*#__PURE__*/React.createElement("div", {
style: {
display: "flex",
flexDirection: "column",
gap: 7
}
}, lineup.map((item, idx) => /*#__PURE__*/React.createElement(LineupRow, {
key: item.id + "-" + idx,
item: item,
index: idx,
shuffle: shuffle,
dragging: dragRow === idx,
isOver: overRow === idx && dragRow !== idx,
onRemove: () => removeAt(idx),
dragProps: {
draggable: true,
onDragStart: () => setDragRow(idx),
onDragEnd: () => {
setDragRow(null);
setOverRow(null);
},
onDragOver: e => {
e.preventDefault();
setOverRow(idx);
},
onDrop: e => {
e.stopPropagation();
dropAt(idx);
}
}
})), /*#__PURE__*/React.createElement("div", {
style: {
marginTop: 4,
padding: "10px 12px",
borderRadius: "var(--radius-md)",
border: "1px dashed var(--border-hairline)",
display: "flex",
alignItems: "center",
gap: 8,
color: "var(--text-disabled)",
font: "var(--text-2xs)/1 var(--font-sans)"
}
}, /*#__PURE__*/React.createElement(Ico, {
n: "Plus",
s: 14
}), "Double-click or drag more titles to add")))), /*#__PURE__*/React.createElement("aside", {
style: {
position: "relative",
minWidth: 0,
overflow: "hidden",
display: "flex",
flexDirection: "column",
background: "var(--surface-card)"
}
}, /*#__PURE__*/React.createElement(ScrollArea, {
scrollerRef: railScrollerRef,
innerStyle: {
padding: "14px 16px 24px",
display: "flex",
flexDirection: "column",
gap: 16
}
}, /*#__PURE__*/React.createElement("div", {
style: {
display: "flex",
flexDirection: "column",
gap: 10
}
}, /*#__PURE__*/React.createElement(Input, {
label: "Channel name",
value: name,
onChange: e => setName(e.target.value),
placeholder: "e.g. Retro Cartoons"
}), /*#__PURE__*/React.createElement(Input, {
label: "Number",
value: number,
onChange: e => setNumber(e.target.value),
trailing: /*#__PURE__*/React.createElement("span", {
style: {
...mono,
font: "9px/1 var(--font-mono)",
padding: "2px 5px",
borderRadius: "3px",
background: "var(--ctv-accent-soft)",
color: "var(--ctv-accent)"
}
}, "AUTO")
})), /*#__PURE__*/React.createElement(ImagePanel, {
logoSrc: logoSrc,
setLogoSrc: setLogoSrc,
name: name
}), /*#__PURE__*/React.createElement("div", {
style: {
display: "flex",
flexDirection: "column",
gap: 4
}
}, /*#__PURE__*/React.createElement("label", {
onClick: () => setShuffle(s => !s),
style: {
display: "flex",
alignItems: "flex-start",
gap: 11,
padding: "10px 0",
cursor: "pointer"
}
}, /*#__PURE__*/React.createElement("div", {
style: {
flex: 1
}
}, /*#__PURE__*/React.createElement("div", {
style: {
display: "flex",
alignItems: "center",
gap: 7
}
}, /*#__PURE__*/React.createElement("span", {
style: {
font: "var(--weight-medium) var(--text-sm)/1.2 var(--font-sans)",
color: "var(--text-primary)"
}
}, "Shuffle"), shuffle !== template.shuffle && /*#__PURE__*/React.createElement("span", {
style: {
font: "var(--text-2xs)/1 var(--font-sans)",
color: "var(--text-disabled)",
padding: "2px 6px",
borderRadius: "var(--radius-xs)",
background: "var(--ctv-surface-2)",
border: "1px solid var(--border-hairline)"
}
}, "overrides template")), /*#__PURE__*/React.createElement("div", {
style: {
marginTop: 2,
font: "var(--text-2xs)/1.35 var(--font-sans)",
color: "var(--text-secondary)"
}
}, shuffle ? "Plays in a random order." : "Plays in sequence (chronological).")), /*#__PURE__*/React.createElement(Switch, {
checked: shuffle,
onChange: setShuffle
})), /*#__PURE__*/React.createElement("div", {
style: {
height: 1,
background: "var(--border-hairline)"
}
}), /*#__PURE__*/React.createElement("label", {
onClick: () => setAlways(s => !s),
style: {
display: "flex",
alignItems: "flex-start",
gap: 10,
padding: "10px 11px",
borderRadius: "var(--radius-sm)",
cursor: "pointer",
background: always ? "var(--ctv-live-soft)" : "transparent",
border: `1px solid ${always ? "var(--ctv-live)" : "transparent"}`,
transition: "background 240ms cubic-bezier(0.16,1,0.3,1), border-color 240ms cubic-bezier(0.16,1,0.3,1)"
}
}, /*#__PURE__*/React.createElement("span", {
style: {
display: "inline-flex",
marginTop: 1,
color: "var(--ctv-live)",
flex: "0 0 auto"
}
}, /*#__PURE__*/React.createElement(Ico, {
n: "Radio",
s: 16
})), /*#__PURE__*/React.createElement("div", {
style: {
flex: 1
}
}, /*#__PURE__*/React.createElement("div", {
style: {
display: "flex",
alignItems: "center",
gap: 7
}
}, /*#__PURE__*/React.createElement("span", {
style: {
font: "var(--weight-medium) var(--text-sm)/1.2 var(--font-sans)",
color: "var(--text-primary)"
}
}, "Always playing"), always !== template.always && /*#__PURE__*/React.createElement("span", {
style: {
font: "var(--text-2xs)/1 var(--font-sans)",
color: "var(--text-disabled)",
padding: "2px 6px",
borderRadius: "var(--radius-xs)",
background: "var(--ctv-surface-2)",
border: "1px solid var(--border-hairline)"
}
}, "overrides template"), always && /*#__PURE__*/React.createElement("span", {
style: {
display: "inline-flex",
alignItems: "center",
gap: 4,
padding: "2px 7px",
borderRadius: "var(--radius-pill)",
background: "var(--ctv-live-soft)",
color: "var(--ctv-live)",
font: "var(--weight-semibold) 9px/1.4 var(--font-sans)",
letterSpacing: ".06em"
}
}, /*#__PURE__*/React.createElement("span", {
style: {
width: 5,
height: 5,
borderRadius: "50%",
background: "var(--ctv-live)"
}
}), "LIVE")), /*#__PURE__*/React.createElement("div", {
style: {
marginTop: 3,
font: "var(--text-2xs)/1.35 var(--font-sans)",
color: "var(--text-secondary)"
}
}, "Like live TV \u2014 advances on schedule even when nobody is watching.")), /*#__PURE__*/React.createElement(Switch, {
checked: always,
onChange: setAlways
}))), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", {
style: {
...eyebrow,
marginBottom: 8
}
}, "Channel Template"), /*#__PURE__*/React.createElement("button", {
type: "button",
onClick: () => setTplOpen(o => !o),
className: "ctv-press",
style: {
width: "100%",
display: "flex",
alignItems: "center",
gap: 10,
padding: "10px 11px",
borderRadius: "var(--radius-sm)",
cursor: "pointer",
textAlign: "left",
background: "var(--ctv-bg-sunken)",
border: "1px solid var(--border-control)"
}
}, /*#__PURE__*/React.createElement("span", {
style: {
display: "inline-flex",
alignItems: "center",
justifyContent: "center",
width: 30,
height: 30,
borderRadius: "var(--radius-xs)",
background: "var(--ctv-surface-3)",
color: "var(--ctv-accent)",
flex: "0 0 auto"
}
}, /*#__PURE__*/React.createElement(Ico, {
n: "SlidersHorizontal",
s: 15
})), /*#__PURE__*/React.createElement("div", {
style: {
flex: 1,
minWidth: 0
}
}, /*#__PURE__*/React.createElement("div", {
style: {
display: "flex",
alignItems: "center",
gap: 6
}
}, /*#__PURE__*/React.createElement("span", {
style: {
font: "var(--weight-medium) var(--text-sm)/1 var(--font-sans)",
color: "var(--text-primary)"
}
}, template.name), !template.builtin && /*#__PURE__*/React.createElement(Badge, {
tone: "neutral"
}, "Custom")), /*#__PURE__*/React.createElement("div", {
style: {
marginTop: 3,
font: "var(--text-2xs)/1.3 var(--font-sans)",
color: "var(--text-secondary)",
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap"
}
}, template.desc)), /*#__PURE__*/React.createElement(Ico, {
n: tplOpen ? "ChevronUp" : "ChevronDown",
s: 15,
color: "var(--text-disabled)"
})), tplOpen && /*#__PURE__*/React.createElement("div", {
style: {
marginTop: 6,
display: "flex",
flexDirection: "column",
gap: 4,
padding: 4,
borderRadius: "var(--radius-sm)",
background: "var(--ctv-bg-sunken)",
border: "1px solid var(--border-hairline)"
}
}, TEMPLATES.map(t => {
const on = t.id === tpl;
return /*#__PURE__*/React.createElement("button", {
key: t.id,
type: "button",
className: "ctv-press",
onClick: () => {
setTpl(t.id);
setTplOpen(false);
setShuffle(t.shuffle);
setAlways(t.always);
},
style: {
display: "flex",
alignItems: "center",
gap: 9,
padding: "8px 9px",
borderRadius: "var(--radius-xs)",
cursor: "pointer",
textAlign: "left",
background: on ? "color-mix(in srgb, var(--action-primary) 14%, transparent)" : "transparent",
border: "none"
}
}, /*#__PURE__*/React.createElement(Ico, {
n: on ? "CircleCheck" : "Circle",
s: 15,
color: on ? "var(--action-primary)" : "var(--text-disabled)"
}), /*#__PURE__*/React.createElement("div", {
style: {
flex: 1,
minWidth: 0
}
}, /*#__PURE__*/React.createElement("div", {
style: {
display: "flex",
alignItems: "center",
gap: 6
}
}, /*#__PURE__*/React.createElement("span", {
style: {
font: "var(--weight-medium) var(--text-xs)/1 var(--font-sans)",
color: "var(--text-primary)"
}
}, t.name), !t.builtin && /*#__PURE__*/React.createElement(Badge, {
tone: "neutral"
}, "Custom")), /*#__PURE__*/React.createElement("div", {
style: {
marginTop: 2,
font: "var(--text-2xs)/1.3 var(--font-sans)",
color: "var(--text-secondary)"
}
}, t.desc)));
}), /*#__PURE__*/React.createElement("button", {
type: "button",
style: {
display: "flex",
alignItems: "center",
gap: 7,
padding: "8px 9px",
borderRadius: "var(--radius-xs)",
cursor: "pointer",
background: "none",
border: "none",
color: "var(--text-secondary)",
font: "var(--weight-medium) var(--text-xs)/1 var(--font-sans)"
}
}, /*#__PURE__*/React.createElement(Ico, {
n: "Save",
s: 14
}), "Save current settings as template\u2026")), /*#__PURE__*/React.createElement("div", {
style: {
marginTop: 8,
display: "flex",
flexWrap: "wrap",
gap: 5
}
}, template.sets.map(s => /*#__PURE__*/React.createElement("span", {
key: s,
style: {
padding: "3px 8px",
borderRadius: "var(--radius-xs)",
background: "var(--ctv-surface-2)",
border: "1px solid var(--border-hairline)",
font: "var(--text-2xs)/1.4 var(--font-sans)",
color: "var(--text-secondary)",
...(/\d/.test(s) ? mono : {})
}
}, s)))), /*#__PURE__*/React.createElement(Advanced, {
templateName: template.name,
scrollerRef: railScrollerRef
})))));
}
window.CTVChannelBuilder = ChannelBuilder;
})();
})(); } catch (e) { __ds_ns.__errors.push({ path: "design_handoff_channel_builder/ChannelBuilder.jsx", error: String((e && e.message) || e) }); }
__ds_ns.Badge = __ds_scope.Badge;
__ds_ns.Card = __ds_scope.Card;
__ds_ns.ChannelLogo = __ds_scope.ChannelLogo;
__ds_ns.ProgressBar = __ds_scope.ProgressBar;
__ds_ns.Stat = __ds_scope.Stat;
__ds_ns.StatusDot = __ds_scope.StatusDot;
__ds_ns.Tag = __ds_scope.Tag;
__ds_ns.Spinner = __ds_scope.Spinner;
__ds_ns.Toast = __ds_scope.Toast;
__ds_ns.Tooltip = __ds_scope.Tooltip;
__ds_ns.Button = __ds_scope.Button;
__ds_ns.Checkbox = __ds_scope.Checkbox;
__ds_ns.IconButton = __ds_scope.IconButton;
__ds_ns.Input = __ds_scope.Input;
__ds_ns.Select = __ds_scope.Select;
__ds_ns.Switch = __ds_scope.Switch;
__ds_ns.NavItem = __ds_scope.NavItem;
__ds_ns.NavSection = __ds_scope.NavSection;
__ds_ns.Tabs = __ds_scope.Tabs;
})();