@kwirthmagnify/kwirth-homepage-avicii 0.1.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.
Files changed (2) hide show
  1. package/front.js +312 -0
  2. package/package.json +7 -0
package/front.js ADDED
@@ -0,0 +1,312 @@
1
+ (() => {
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __commonJS = (cb, mod) => function __require() {
9
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
+ // If the importer is in node compatibility mode or this is not an ESM
21
+ // file that has been converted to a CommonJS file using a Babel-
22
+ // compatible transform (i.e. "__esModule" has not been set), then set
23
+ // "default" to the CommonJS "module.exports" for node compatibility.
24
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
+ mod
26
+ ));
27
+
28
+ // kwirth-globals:react
29
+ var require_react = __commonJS({
30
+ "kwirth-globals:react"(exports, module) {
31
+ var _m = window.__kwirth__.React;
32
+ var _d = _m != null && "default" in Object(_m) ? _m.default : _m;
33
+ if (typeof _d !== "function" && _d != null && typeof _d.default !== "undefined") _d = _d.default;
34
+ module.exports = Object.assign({}, typeof _m === "object" && _m !== null ? _m : {}, { default: _d, __esModule: true });
35
+ }
36
+ });
37
+
38
+ // kwirth-globals:@mui/material
39
+ var require_material = __commonJS({
40
+ "kwirth-globals:@mui/material"(exports, module) {
41
+ var _m = window.__kwirth__.MUI.material;
42
+ var _d = _m != null && "default" in Object(_m) ? _m.default : _m;
43
+ if (typeof _d !== "function" && _d != null && typeof _d.default !== "undefined") _d = _d.default;
44
+ module.exports = Object.assign({}, typeof _m === "object" && _m !== null ? _m : {}, { default: _d, __esModule: true });
45
+ }
46
+ });
47
+
48
+ // kwirth-globals:@mui/icons-material
49
+ var require_icons_material = __commonJS({
50
+ "kwirth-globals:@mui/icons-material"(exports, module) {
51
+ var _m = window.__kwirth__.MUI.icons;
52
+ var _d = _m != null && "default" in Object(_m) ? _m.default : _m;
53
+ if (typeof _d !== "function" && _d != null && typeof _d.default !== "undefined") _d = _d.default;
54
+ module.exports = Object.assign({}, typeof _m === "object" && _m !== null ? _m : {}, { default: _d, __esModule: true });
55
+ }
56
+ });
57
+
58
+ // src/front/Avicii.tsx
59
+ var import_react = __toESM(require_react(), 1);
60
+ var import_material = __toESM(require_material(), 1);
61
+ var import_icons_material = __toESM(require_icons_material(), 1);
62
+ var AV_GOLD = "#c9a227";
63
+ var AV_GOLD_DIM = "#5c4810";
64
+ var AV_GOLD_GLOW = "rgba(201,162,39,0.45)";
65
+ var AV_BG = "#050505";
66
+ var AV_CARD = "#0b0907";
67
+ var AV_TEXT = "#f0ede6";
68
+ var AV_MUTED = "#4a443c";
69
+ var AV_FONT = "'Oswald', 'Barlow Condensed', 'Arial Narrow', sans-serif";
70
+ var POLL_MS = 1e4;
71
+ var EVENTS_LIMIT = 25;
72
+ var TRIANGLE_BG = `url("data:image/svg+xml,%3Csvg width='160' height='60' viewBox='0 0 160 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg transform='translate(15,15)' opacity='0.045'%3E%3Cg transform='translate(0,30) scale(0.1,-0.1)' fill='%23c9a227' stroke='none'%3E%3Cpath d='M132 167 l-132 -132 0 -18 0 -17 17 0 18 0 112 112 113 113 0 -113 0 -112 25 0 25 0 0 150 0 150 -23 0 -22 0 -133 -133z'/%3E%3Cpath d='M360 150 l0 -150 38 0 37 0 113 113 112 112 0 35 0 35 -125 -125 -125 -125 0 128 0 127 -25 0 -25 0 0 -150z'/%3E%3Cpath d='M710 150 l0 -150 25 0 25 0 0 150 0 150 -25 0 -25 0 0 -150z'/%3E%3Cpath d='M1150 150 l0 -150 25 0 25 0 0 150 0 150 -25 0 -25 0 0 -150z'/%3E%3Cpath d='M1250 150 l0 -150 25 0 25 0 0 150 0 150 -25 0 -25 0 0 -150z'/%3E%3C/g%3E%3Cpath d='M106.64,17.40 A12.0,12.0 0 1 1 106.88,12.83' fill='none' stroke='%23c9a227' stroke-width='5' stroke-linecap='butt'/%3E%3C/g%3E%3C/svg%3E")`;
73
+ var AviciiLogo = ({ size = 48 }) => /* @__PURE__ */ import_react.default.createElement(
74
+ "svg",
75
+ {
76
+ xmlns: "http://www.w3.org/2000/svg",
77
+ viewBox: "0 0 37 18",
78
+ fill: AV_TEXT,
79
+ width: size,
80
+ height: Math.round(size * 18 / 37)
81
+ },
82
+ /* @__PURE__ */ import_react.default.createElement("polygon", { points: "17,0 17,18 0,18" }),
83
+ /* @__PURE__ */ import_react.default.createElement("polygon", { points: "19,0 37,0 19,18" })
84
+ );
85
+ var AviciiWordmark = () => /* @__PURE__ */ import_react.default.createElement(
86
+ "svg",
87
+ {
88
+ xmlns: "http://www.w3.org/2000/svg",
89
+ viewBox: "0 0 308.0 30",
90
+ height: "19",
91
+ style: { display: "block" }
92
+ },
93
+ /* @__PURE__ */ import_react.default.createElement("g", { fill: AV_TEXT, stroke: "none" }, /* @__PURE__ */ import_react.default.createElement("g", { transform: "translate(37.0,30) scale(0.1,-0.1)" }, /* @__PURE__ */ import_react.default.createElement("path", { d: "M132 167 l-132 -132 0 -18 0 -17 17 0 18 0 112 112 113 113 0 -113 0 -112 25 0 25 0 0 150 0 150 -23 0 -22 0 -133 -133z" })), /* @__PURE__ */ import_react.default.createElement("g", { transform: "translate(206.0,30) scale(0.1,-0.1)" }, /* @__PURE__ */ import_react.default.createElement("path", { d: "M132 167 l-132 -132 0 -18 0 -17 17 0 18 0 112 112 113 113 0 -113 0 -112 25 0 25 0 0 150 0 150 -23 0 -22 0 -133 -133z" }))),
94
+ /* @__PURE__ */ import_react.default.createElement("path", { d: "M 5.5 27.5 L 5.5 2.5 L 20.5 2.5 A 11 12.5 0 0 1 20.5 27.5 L 5.5 27.5", fill: "none", stroke: AV_TEXT, strokeWidth: "5", strokeLinecap: "butt", strokeLinejoin: "miter" }),
95
+ /* @__PURE__ */ import_react.default.createElement("path", { d: "M 99.5 2.5 L 79.75 2.5 A 6.25 6.25 0 0 0 79.75 15 L 93.25 15 A 6.25 6.25 0 0 1 93.25 27.5 L 73.5 27.5", fill: "none", stroke: AV_TEXT, strokeWidth: "5", strokeLinecap: "butt", strokeLinejoin: "miter" }),
96
+ /* @__PURE__ */ import_react.default.createElement("path", { d: "M 107.5 0 L 107.5 30 M 133.5 0 L 133.5 30 M 107.5 15.0 L 133.5 15.0", fill: "none", stroke: AV_TEXT, strokeWidth: "5", strokeLinecap: "butt", strokeLinejoin: "miter" }),
97
+ /* @__PURE__ */ import_react.default.createElement("path", { d: "M 141.5 0 L 141.5 30 M 141.5 2.5 L 161.5 2.5 A 6 6.25 0 0 1 161.5 15.0 L 141.5 15.0 M 141.5 15.0 L 160.0 15.0 A 7.5 6.25 0 0 1 160.0 27.5 L 141.5 27.5", fill: "none", stroke: AV_TEXT, strokeWidth: "5", strokeLinecap: "butt", strokeLinejoin: "miter" }),
98
+ /* @__PURE__ */ import_react.default.createElement("path", { d: "M 188.0 2.5 A 12.5 12.5 0 0 1 188.0 27.5 A 12.5 12.5 0 0 1 188.0 2.5", fill: "none", stroke: AV_TEXT, strokeWidth: "5", strokeLinecap: "butt", strokeLinejoin: "miter" }),
99
+ /* @__PURE__ */ import_react.default.createElement("path", { d: "M 242.5 30 L 242.5 2.5 L 259.5 2.5 A 9 6.25 0 0 1 259.5 15.0 L 242.5 15.0", fill: "none", stroke: AV_TEXT, strokeWidth: "5", strokeLinecap: "butt", strokeLinejoin: "miter" }),
100
+ /* @__PURE__ */ import_react.default.createElement("path", { d: "M 251.42 16.94 L 254.58 13.06 L 271.00 26.45 L 271.00 30.00 L 267.44 30.00 Z", fill: AV_TEXT, stroke: "none" }),
101
+ /* @__PURE__ */ import_react.default.createElement("path", { d: "M 276.5 27.5 L 276.5 2.5 L 291.5 2.5 A 11 12.5 0 0 1 291.5 27.5 L 276.5 27.5", fill: "none", stroke: AV_TEXT, strokeWidth: "5", strokeLinecap: "butt", strokeLinejoin: "miter" })
102
+ );
103
+ var DiamondIndicator = ({ online, delay = "0s" }) => /* @__PURE__ */ import_react.default.createElement(import_material.Tooltip, { title: online ? "Online" : "Offline" }, /* @__PURE__ */ import_react.default.createElement(import_material.Box, { sx: {
104
+ width: 10,
105
+ height: 10,
106
+ flexShrink: 0,
107
+ bgcolor: online ? AV_GOLD : AV_MUTED,
108
+ transform: "rotate(45deg)",
109
+ boxShadow: online ? `0 0 8px 2px ${AV_GOLD_GLOW}` : "none",
110
+ transition: "all 0.3s",
111
+ animation: online ? "avicii-pulse 2.8s ease-in-out infinite" : "none",
112
+ animationDelay: delay,
113
+ cursor: "default"
114
+ } }));
115
+ var MetricBar = ({ label, value }) => {
116
+ const barRef = (0, import_react.useRef)(null);
117
+ const [cols, setCols] = (0, import_react.useState)(20);
118
+ (0, import_react.useEffect)(() => {
119
+ if (!barRef.current) return;
120
+ const obs = new ResizeObserver(() => {
121
+ if (barRef.current) setCols(Math.max(5, Math.floor(barRef.current.offsetWidth / 8) - 1));
122
+ });
123
+ obs.observe(barRef.current);
124
+ return () => obs.disconnect();
125
+ }, []);
126
+ const filled = Math.round(value * cols / 100);
127
+ const bar = "\u25B0".repeat(filled) + "\u25B1".repeat(cols - filled);
128
+ const color = value > 90 ? "#e84040" : AV_GOLD;
129
+ return /* @__PURE__ */ import_react.default.createElement(import_material.Box, { sx: { display: "grid", gridTemplateColumns: "30px 1fr 36px", alignItems: "center", paddingRight: "8px" } }, /* @__PURE__ */ import_react.default.createElement(import_material.Typography, { sx: { fontFamily: AV_FONT, fontSize: "0.62rem", fontWeight: 600, color: AV_MUTED, letterSpacing: "1px", textTransform: "uppercase" } }, label), /* @__PURE__ */ import_react.default.createElement(import_material.Typography, { ref: barRef, sx: { fontFamily: "monospace", fontSize: "0.62rem", color, overflow: "hidden", whiteSpace: "nowrap" } }, bar), /* @__PURE__ */ import_react.default.createElement(import_material.Typography, { sx: { fontFamily: AV_FONT, fontSize: "0.62rem", fontWeight: 600, color, pl: 0.5, textAlign: "right" } }, Math.round(value), "%"));
130
+ };
131
+ var MiniCard = ({ label, value }) => /* @__PURE__ */ import_react.default.createElement(import_material.Box, { sx: {
132
+ border: `1px solid ${AV_GOLD_DIM}`,
133
+ borderTop: `2px solid ${AV_GOLD}`,
134
+ px: 1.5,
135
+ py: 0.75,
136
+ minWidth: 70,
137
+ display: "flex",
138
+ flexDirection: "column",
139
+ alignItems: "center"
140
+ } }, /* @__PURE__ */ import_react.default.createElement(import_material.Typography, { sx: { fontFamily: AV_FONT, fontSize: "1rem", fontWeight: 700, color: AV_TEXT, lineHeight: 1.2 } }, value), /* @__PURE__ */ import_react.default.createElement(import_material.Typography, { sx: { fontFamily: AV_FONT, fontSize: "0.58rem", fontWeight: 600, color: AV_MUTED, letterSpacing: "1.5px", textTransform: "uppercase", lineHeight: 1.3 } }, label));
141
+ var Avicii = (props) => {
142
+ (0, import_react.useEffect)(() => {
143
+ const fontId = "avicii-oswald-font";
144
+ if (!document.getElementById(fontId)) {
145
+ const link = document.createElement("link");
146
+ link.id = fontId;
147
+ link.rel = "stylesheet";
148
+ link.href = "https://fonts.googleapis.com/css2?family=Oswald:wght@300;400;500;600;700&display=swap";
149
+ document.head.appendChild(link);
150
+ }
151
+ const kfId = "avicii-keyframes";
152
+ if (!document.getElementById(kfId)) {
153
+ const style = document.createElement("style");
154
+ style.id = kfId;
155
+ style.textContent = `@keyframes avicii-pulse {
156
+ 0%, 100% { opacity: 0.25; box-shadow: none; }
157
+ 50% { opacity: 1; box-shadow: 0 0 10px 3px rgba(201,162,39,0.5); }
158
+ }`;
159
+ document.head.appendChild(style);
160
+ }
161
+ return () => {
162
+ document.getElementById("avicii-keyframes")?.remove();
163
+ };
164
+ }, []);
165
+ const containerRef = (0, import_react.useRef)(null);
166
+ const [containerHeight, setContainerHeight] = (0, import_react.useState)(0);
167
+ (0, import_react.useEffect)(() => {
168
+ const obs = new ResizeObserver(() => {
169
+ if (!containerRef.current) return;
170
+ const { top } = containerRef.current.getBoundingClientRect();
171
+ setContainerHeight(window.innerHeight - top);
172
+ });
173
+ obs.observe(document.body);
174
+ return () => obs.disconnect();
175
+ }, [containerRef.current]);
176
+ const [clusterMetrics, setClusterMetrics] = (0, import_react.useState)({});
177
+ (0, import_react.useEffect)(() => {
178
+ if (!props.getClusterMetrics) return;
179
+ const fetchAll = () => props.clusters.forEach(
180
+ (c) => props.getClusterMetrics(c.name).then((m) => {
181
+ if (m) setClusterMetrics((p) => ({ ...p, [c.name]: m }));
182
+ })
183
+ );
184
+ fetchAll();
185
+ const t = setInterval(fetchAll, POLL_MS);
186
+ return () => clearInterval(t);
187
+ }, [props.clusters, props.getClusterMetrics]);
188
+ const [clusterEvents, setClusterEvents] = (0, import_react.useState)({});
189
+ (0, import_react.useEffect)(() => {
190
+ if (!props.getClusterEvents) return;
191
+ const fetchAll = () => props.clusters.forEach(
192
+ (c) => props.getClusterEvents(c.name, EVENTS_LIMIT).then((evts) => setClusterEvents((p) => ({ ...p, [c.name]: evts })))
193
+ );
194
+ fetchAll();
195
+ const t = setInterval(fetchAll, POLL_MS);
196
+ return () => clearInterval(t);
197
+ }, [props.clusters, props.getClusterEvents]);
198
+ const launchMagnify = (name) => props.onHomepageSelectTab({ name, description: "", channel: "magnify", channelObject: { clusterName: name, view: "cluster", namespace: "", group: "", pod: "", container: "" } });
199
+ const launchTopology = (name) => props.onHomepageSelectTab({ name, description: "", channel: "topology", channelObject: { clusterName: name, view: "cluster", namespace: "", group: "", pod: "", container: "" } });
200
+ const hasMagnify = props.frontChannels.has("magnify");
201
+ const hasTopology = props.frontChannels.has("topology");
202
+ const magnifyClass = props.frontChannels.get("magnify");
203
+ const magnifyIcon = magnifyClass ? new magnifyClass().getChannelIcon() : null;
204
+ const topologyClass = props.frontChannels.get("topology");
205
+ const topologyIcon = topologyClass ? new topologyClass().getChannelIcon() : /* @__PURE__ */ import_react.default.createElement(import_icons_material.AccountTree, null);
206
+ const cardHeight = containerHeight > 0 ? Math.floor((containerHeight - 72 - 24 * 2 - 24) / 2) - 4 : 280;
207
+ const avBtnSx = {
208
+ borderRadius: 0,
209
+ fontFamily: AV_FONT,
210
+ fontWeight: 700,
211
+ fontSize: "0.65rem",
212
+ letterSpacing: "2px",
213
+ textTransform: "uppercase",
214
+ borderColor: AV_GOLD_DIM,
215
+ color: AV_MUTED,
216
+ "&:not(.Mui-disabled)": { borderColor: AV_GOLD_DIM, color: AV_TEXT },
217
+ "&:not(.Mui-disabled):hover": {
218
+ borderColor: AV_GOLD,
219
+ color: AV_GOLD,
220
+ bgcolor: "rgba(201,162,39,0.06)",
221
+ boxShadow: `0 0 10px ${AV_GOLD_GLOW}`
222
+ }
223
+ };
224
+ return /* @__PURE__ */ import_react.default.createElement(import_material.Box, { ref: containerRef, sx: {
225
+ position: "relative",
226
+ width: "100%",
227
+ height: `${containerHeight}px`,
228
+ overflow: "hidden",
229
+ bgcolor: AV_BG,
230
+ backgroundImage: TRIANGLE_BG,
231
+ backgroundSize: "160px 60px"
232
+ } }, /* @__PURE__ */ import_react.default.createElement(import_material.Box, { sx: { position: "absolute", inset: 0, overflowY: "auto" } }, /* @__PURE__ */ import_react.default.createElement(
233
+ import_material.Stack,
234
+ {
235
+ direction: "row",
236
+ alignItems: "center",
237
+ justifyContent: "flex-start",
238
+ spacing: 2.5,
239
+ sx: { pt: 2, pb: 1.5, pl: 3, borderBottom: `1px solid ${AV_GOLD_DIM}`, bgcolor: AV_BG }
240
+ },
241
+ /* @__PURE__ */ import_react.default.createElement(AviciiLogo, { size: 40 }),
242
+ /* @__PURE__ */ import_react.default.createElement(AviciiWordmark, null)
243
+ ), /* @__PURE__ */ import_react.default.createElement(import_material.Box, { sx: { p: 3, display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: 3 } }, props.clusters.map((cluster, idx) => {
244
+ const metrics = clusterMetrics[cluster.name];
245
+ const clusterChannelIds = new Set((cluster.kwirthData?.channels ?? []).map((ch) => ch.id));
246
+ const clusterHasMagnify = hasMagnify && clusterChannelIds.has("magnify");
247
+ const clusterHasTopology = hasTopology && clusterChannelIds.has("topology");
248
+ return /* @__PURE__ */ import_react.default.createElement(import_material.Card, { key: cluster.name, sx: {
249
+ bgcolor: AV_CARD,
250
+ borderRadius: 0,
251
+ border: `1px solid ${AV_GOLD_DIM}`,
252
+ borderTop: `2px solid ${AV_GOLD}`,
253
+ height: cardHeight,
254
+ overflow: "hidden",
255
+ backgroundImage: "none",
256
+ position: "relative",
257
+ "&::after": {
258
+ content: '""',
259
+ position: "absolute",
260
+ top: 0,
261
+ right: 0,
262
+ width: 0,
263
+ height: 0,
264
+ borderStyle: "solid",
265
+ borderWidth: "0 22px 22px 0",
266
+ borderColor: `transparent ${AV_GOLD} transparent transparent`
267
+ }
268
+ } }, /* @__PURE__ */ import_react.default.createElement(import_material.CardContent, { sx: { height: "100%", p: "12px !important" } }, /* @__PURE__ */ import_react.default.createElement(import_material.Stack, { direction: "column", justifyContent: "space-between", sx: { height: "100%" } }, /* @__PURE__ */ import_react.default.createElement(import_material.Stack, { direction: "column", spacing: 0.75 }, /* @__PURE__ */ import_react.default.createElement(import_material.Stack, { direction: "row", alignItems: "center", spacing: 1 }, /* @__PURE__ */ import_react.default.createElement(import_material.Typography, { sx: {
269
+ fontFamily: AV_FONT,
270
+ fontWeight: 700,
271
+ fontSize: "0.9rem",
272
+ letterSpacing: "3px",
273
+ textTransform: "uppercase",
274
+ color: AV_TEXT,
275
+ overflow: "hidden",
276
+ textOverflow: "ellipsis",
277
+ whiteSpace: "nowrap",
278
+ flex: 1
279
+ } }, cluster.name), /* @__PURE__ */ import_react.default.createElement(DiamondIndicator, { online: !!metrics, delay: `${idx * 0.7 % 2.5}s` })), /* @__PURE__ */ import_react.default.createElement(import_material.Typography, { sx: {
280
+ fontFamily: AV_FONT,
281
+ fontSize: "0.7rem",
282
+ color: AV_MUTED,
283
+ overflow: "hidden",
284
+ textOverflow: "ellipsis",
285
+ whiteSpace: "nowrap"
286
+ } }, cluster.url), metrics && /* @__PURE__ */ import_react.default.createElement(import_material.Stack, { direction: "column", spacing: 0.25 }, /* @__PURE__ */ import_react.default.createElement(MetricBar, { label: "CPU", value: metrics.cpu }), /* @__PURE__ */ import_react.default.createElement(MetricBar, { label: "MEM", value: metrics.memory }), /* @__PURE__ */ import_react.default.createElement(MetricBar, { label: "POD", value: metrics.maxPods > 0 ? metrics.pods / metrics.maxPods * 100 : 0 }))), metrics && /* @__PURE__ */ import_react.default.createElement(import_material.Stack, { direction: "row", spacing: 1, sx: { justifyContent: "center" } }, /* @__PURE__ */ import_react.default.createElement(MiniCard, { label: "vCPUs", value: metrics.vcpus != null ? String(metrics.vcpus) : "--" }), /* @__PURE__ */ import_react.default.createElement(MiniCard, { label: "RAM", value: metrics.totalMemoryBytes != null ? `${(metrics.totalMemoryBytes / 1073741824).toFixed(0)}G` : "--" }), /* @__PURE__ */ import_react.default.createElement(MiniCard, { label: "Pods", value: metrics.pods ? String(metrics.pods) : "--" })), /* @__PURE__ */ import_react.default.createElement(import_material.Stack, { direction: "row", alignItems: "center", justifyContent: "space-between" }, /* @__PURE__ */ import_react.default.createElement(import_material.Stack, { direction: "row", spacing: 0.5, alignItems: "center" }, (cluster.kwirthData?.channels ?? []).map((ch) => {
287
+ const cls = props.frontChannels.get(ch.id);
288
+ if (!cls) return null;
289
+ const icon = new cls().getChannelIcon();
290
+ return /* @__PURE__ */ import_react.default.createElement(import_material.Tooltip, { key: ch.id, title: ch.id }, import_react.default.cloneElement(icon, { fontSize: "small", sx: { color: AV_GOLD, opacity: 0.55 } }));
291
+ })), /* @__PURE__ */ import_react.default.createElement(
292
+ import_material.Button,
293
+ {
294
+ variant: "outlined",
295
+ size: "small",
296
+ disabled: !clusterHasMagnify,
297
+ startIcon: magnifyIcon,
298
+ onClick: () => launchMagnify(cluster.name),
299
+ sx: avBtnSx
300
+ },
301
+ "EXPLORE"
302
+ )))));
303
+ }), props.clusters.length === 0 && /* @__PURE__ */ import_react.default.createElement(import_material.Typography, { sx: { fontFamily: AV_FONT, fontSize: "1rem", fontWeight: 600, color: AV_GOLD, letterSpacing: "4px", textTransform: "uppercase", p: 2, opacity: 0.6 } }, "\u25B2 No clusters connected"))));
304
+ };
305
+
306
+ // src/front/index.ts
307
+ window.__kwirth_homepages__["avicii"] = {
308
+ homepageId: "avicii",
309
+ displayName: "Avicii",
310
+ Component: Avicii
311
+ };
312
+ })();
package/package.json ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "id": "avicii",
3
+ "name": "@kwirthmagnify/kwirth-homepage-avicii",
4
+ "displayName": "Avicii",
5
+ "version": "0.1.1",
6
+ "description": "Avicii-inspired homepage — warm gold on black, geometric triangle motifs, sharp Oswald typography, and live cluster metrics."
7
+ }