@camstack/ui-library 0.1.25 → 0.1.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1886 -124
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +304 -3
- package/dist/index.d.ts +304 -3
- package/dist/index.js +1826 -92
- package/dist/index.js.map +1 -1
- package/package.json +10 -1
package/dist/index.js
CHANGED
|
@@ -1273,8 +1273,30 @@ function ProviderBadge({
|
|
|
1273
1273
|
] });
|
|
1274
1274
|
}
|
|
1275
1275
|
|
|
1276
|
+
// src/composites/version-badge.tsx
|
|
1277
|
+
import { jsx as jsx22 } from "react/jsx-runtime";
|
|
1278
|
+
var VARIANT_STYLES = {
|
|
1279
|
+
success: "bg-emerald-400 text-emerald-950",
|
|
1280
|
+
warning: "bg-amber-400 text-amber-950",
|
|
1281
|
+
danger: "bg-red-400 text-red-950",
|
|
1282
|
+
info: "bg-blue-400 text-blue-950",
|
|
1283
|
+
neutral: "bg-foreground-subtle/20 text-foreground"
|
|
1284
|
+
};
|
|
1285
|
+
function SemanticBadge({ children, variant = "neutral", mono, className }) {
|
|
1286
|
+
return /* @__PURE__ */ jsx22("span", { className: cn(
|
|
1287
|
+
"inline-flex items-center rounded-md px-2 py-0.5 text-[11px] font-bold leading-tight",
|
|
1288
|
+
mono && "font-mono",
|
|
1289
|
+
VARIANT_STYLES[variant],
|
|
1290
|
+
className
|
|
1291
|
+
), children });
|
|
1292
|
+
}
|
|
1293
|
+
function VersionBadge({ version, preRelease, className }) {
|
|
1294
|
+
const isPreRelease = preRelease ?? /-(alpha|beta|rc|dev|canary|next)/i.test(version);
|
|
1295
|
+
return /* @__PURE__ */ jsx22(SemanticBadge, { variant: isPreRelease ? "warning" : "success", mono: true, className, children: version });
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1276
1298
|
// src/composites/form-field.tsx
|
|
1277
|
-
import { jsx as
|
|
1299
|
+
import { jsx as jsx23, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1278
1300
|
function FormField({
|
|
1279
1301
|
label,
|
|
1280
1302
|
description,
|
|
@@ -1297,31 +1319,31 @@ function FormField({
|
|
|
1297
1319
|
/* @__PURE__ */ jsxs8("div", { className: cn(isHorizontal ? "flex-1" : ""), children: [
|
|
1298
1320
|
/* @__PURE__ */ jsxs8(Label, { children: [
|
|
1299
1321
|
label,
|
|
1300
|
-
required && /* @__PURE__ */
|
|
1322
|
+
required && /* @__PURE__ */ jsx23("span", { className: "text-danger ml-0.5", children: "*" })
|
|
1301
1323
|
] }),
|
|
1302
|
-
description && /* @__PURE__ */
|
|
1324
|
+
description && /* @__PURE__ */ jsx23("p", { className: "text-foreground-subtle text-xs mt-0.5", children: description })
|
|
1303
1325
|
] }),
|
|
1304
|
-
/* @__PURE__ */
|
|
1305
|
-
error && /* @__PURE__ */
|
|
1326
|
+
/* @__PURE__ */ jsx23("div", { className: cn(isHorizontal ? "shrink-0" : ""), children }),
|
|
1327
|
+
error && /* @__PURE__ */ jsx23("p", { className: "text-danger text-xs", children: error })
|
|
1306
1328
|
]
|
|
1307
1329
|
}
|
|
1308
1330
|
);
|
|
1309
1331
|
}
|
|
1310
1332
|
|
|
1311
1333
|
// src/composites/page-header.tsx
|
|
1312
|
-
import { jsx as
|
|
1334
|
+
import { jsx as jsx24, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1313
1335
|
function PageHeader({ title, subtitle, actions, className }) {
|
|
1314
1336
|
return /* @__PURE__ */ jsxs9("div", { className: cn("flex items-center justify-between mb-3", className), children: [
|
|
1315
1337
|
/* @__PURE__ */ jsxs9("div", { children: [
|
|
1316
|
-
/* @__PURE__ */
|
|
1317
|
-
subtitle && /* @__PURE__ */
|
|
1338
|
+
/* @__PURE__ */ jsx24("h1", { className: "text-sm font-semibold text-foreground", children: title }),
|
|
1339
|
+
subtitle && /* @__PURE__ */ jsx24("p", { className: "text-foreground-subtle text-xs", children: subtitle })
|
|
1318
1340
|
] }),
|
|
1319
|
-
actions && /* @__PURE__ */
|
|
1341
|
+
actions && /* @__PURE__ */ jsx24("div", { className: "flex items-center gap-2", children: actions })
|
|
1320
1342
|
] });
|
|
1321
1343
|
}
|
|
1322
1344
|
|
|
1323
1345
|
// src/composites/empty-state.tsx
|
|
1324
|
-
import { jsx as
|
|
1346
|
+
import { jsx as jsx25, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1325
1347
|
function EmptyState({
|
|
1326
1348
|
icon: Icon,
|
|
1327
1349
|
title,
|
|
@@ -1330,17 +1352,17 @@ function EmptyState({
|
|
|
1330
1352
|
className
|
|
1331
1353
|
}) {
|
|
1332
1354
|
return /* @__PURE__ */ jsxs10("div", { className: cn("flex flex-col items-center justify-center gap-3 py-12", className), children: [
|
|
1333
|
-
Icon && /* @__PURE__ */
|
|
1355
|
+
Icon && /* @__PURE__ */ jsx25(Icon, { className: "h-12 w-12 text-foreground-subtle", "aria-hidden": "true" }),
|
|
1334
1356
|
/* @__PURE__ */ jsxs10("div", { className: "flex flex-col items-center gap-1 text-center", children: [
|
|
1335
|
-
/* @__PURE__ */
|
|
1336
|
-
description && /* @__PURE__ */
|
|
1357
|
+
/* @__PURE__ */ jsx25("p", { className: "text-foreground-muted text-sm font-medium", children: title }),
|
|
1358
|
+
description && /* @__PURE__ */ jsx25("p", { className: "text-foreground-subtle text-xs max-w-xs", children: description })
|
|
1337
1359
|
] }),
|
|
1338
|
-
action && /* @__PURE__ */
|
|
1360
|
+
action && /* @__PURE__ */ jsx25("div", { className: "mt-1", children: action })
|
|
1339
1361
|
] });
|
|
1340
1362
|
}
|
|
1341
1363
|
|
|
1342
1364
|
// src/composites/confirm-dialog.tsx
|
|
1343
|
-
import { jsx as
|
|
1365
|
+
import { jsx as jsx26, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1344
1366
|
function ConfirmDialog({
|
|
1345
1367
|
title,
|
|
1346
1368
|
message,
|
|
@@ -1352,14 +1374,14 @@ function ConfirmDialog({
|
|
|
1352
1374
|
open,
|
|
1353
1375
|
onOpenChange
|
|
1354
1376
|
}) {
|
|
1355
|
-
return /* @__PURE__ */
|
|
1377
|
+
return /* @__PURE__ */ jsx26(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs11(DialogContent, { children: [
|
|
1356
1378
|
/* @__PURE__ */ jsxs11(DialogHeader, { children: [
|
|
1357
|
-
/* @__PURE__ */
|
|
1358
|
-
/* @__PURE__ */
|
|
1379
|
+
/* @__PURE__ */ jsx26(DialogTitle, { children: title }),
|
|
1380
|
+
/* @__PURE__ */ jsx26(DialogDescription, { children: message })
|
|
1359
1381
|
] }),
|
|
1360
1382
|
/* @__PURE__ */ jsxs11(DialogFooter, { children: [
|
|
1361
|
-
/* @__PURE__ */
|
|
1362
|
-
/* @__PURE__ */
|
|
1383
|
+
/* @__PURE__ */ jsx26(Button, { variant: "ghost", onClick: onCancel, children: cancelLabel }),
|
|
1384
|
+
/* @__PURE__ */ jsx26(
|
|
1363
1385
|
Button,
|
|
1364
1386
|
{
|
|
1365
1387
|
variant: variant === "danger" ? "danger" : "primary",
|
|
@@ -1373,11 +1395,11 @@ function ConfirmDialog({
|
|
|
1373
1395
|
|
|
1374
1396
|
// src/composites/stat-card.tsx
|
|
1375
1397
|
import { TrendingUp, TrendingDown } from "lucide-react";
|
|
1376
|
-
import { jsx as
|
|
1398
|
+
import { jsx as jsx27, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1377
1399
|
function StatCard({ value, label, trend, className }) {
|
|
1378
1400
|
return /* @__PURE__ */ jsxs12(Card, { className: cn("flex flex-col gap-1", className), children: [
|
|
1379
1401
|
/* @__PURE__ */ jsxs12("div", { className: "flex items-baseline gap-2", children: [
|
|
1380
|
-
/* @__PURE__ */
|
|
1402
|
+
/* @__PURE__ */ jsx27("span", { className: "text-2xl font-semibold text-foreground", children: value }),
|
|
1381
1403
|
trend && /* @__PURE__ */ jsxs12(
|
|
1382
1404
|
"span",
|
|
1383
1405
|
{
|
|
@@ -1386,27 +1408,27 @@ function StatCard({ value, label, trend, className }) {
|
|
|
1386
1408
|
trend.direction === "up" ? "text-success" : "text-danger"
|
|
1387
1409
|
),
|
|
1388
1410
|
children: [
|
|
1389
|
-
trend.direction === "up" ? /* @__PURE__ */
|
|
1411
|
+
trend.direction === "up" ? /* @__PURE__ */ jsx27(TrendingUp, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx27(TrendingDown, { className: "h-3 w-3" }),
|
|
1390
1412
|
trend.value,
|
|
1391
1413
|
"%"
|
|
1392
1414
|
]
|
|
1393
1415
|
}
|
|
1394
1416
|
)
|
|
1395
1417
|
] }),
|
|
1396
|
-
/* @__PURE__ */
|
|
1418
|
+
/* @__PURE__ */ jsx27("span", { className: "text-xs text-foreground-muted", children: label })
|
|
1397
1419
|
] });
|
|
1398
1420
|
}
|
|
1399
1421
|
|
|
1400
1422
|
// src/composites/key-value-list.tsx
|
|
1401
|
-
import { jsx as
|
|
1423
|
+
import { jsx as jsx28, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1402
1424
|
function KeyValueList({ items, className }) {
|
|
1403
|
-
return /* @__PURE__ */
|
|
1425
|
+
return /* @__PURE__ */ jsx28("dl", { className: cn("flex flex-col", className), children: items.map((item) => /* @__PURE__ */ jsxs13(
|
|
1404
1426
|
"div",
|
|
1405
1427
|
{
|
|
1406
1428
|
className: "flex items-center h-7",
|
|
1407
1429
|
children: [
|
|
1408
|
-
/* @__PURE__ */
|
|
1409
|
-
/* @__PURE__ */
|
|
1430
|
+
/* @__PURE__ */ jsx28("dt", { className: "text-foreground-subtle text-xs w-1/3 shrink-0", children: item.key }),
|
|
1431
|
+
/* @__PURE__ */ jsx28("dd", { className: "text-foreground text-xs", children: item.value })
|
|
1410
1432
|
]
|
|
1411
1433
|
},
|
|
1412
1434
|
item.key
|
|
@@ -1416,7 +1438,7 @@ function KeyValueList({ items, className }) {
|
|
|
1416
1438
|
// src/composites/code-block.tsx
|
|
1417
1439
|
import { useCallback as useCallback7, useState as useState8 } from "react";
|
|
1418
1440
|
import { Copy, Check } from "lucide-react";
|
|
1419
|
-
import { jsx as
|
|
1441
|
+
import { jsx as jsx29, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1420
1442
|
function CodeBlock({ children, maxHeight = 300, className }) {
|
|
1421
1443
|
const [copied, setCopied] = useState8(false);
|
|
1422
1444
|
const handleCopy = useCallback7(() => {
|
|
@@ -1426,8 +1448,8 @@ function CodeBlock({ children, maxHeight = 300, className }) {
|
|
|
1426
1448
|
});
|
|
1427
1449
|
}, [children]);
|
|
1428
1450
|
return /* @__PURE__ */ jsxs14("div", { className: cn("relative group", className), children: [
|
|
1429
|
-
/* @__PURE__ */
|
|
1430
|
-
/* @__PURE__ */
|
|
1451
|
+
/* @__PURE__ */ jsx29(ScrollArea, { style: { maxHeight }, children: /* @__PURE__ */ jsx29("pre", { className: "font-mono text-xs bg-surface p-3 rounded-md border border-border-subtle", children: /* @__PURE__ */ jsx29("code", { children }) }) }),
|
|
1452
|
+
/* @__PURE__ */ jsx29("div", { className: "absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity", children: /* @__PURE__ */ jsx29(
|
|
1431
1453
|
IconButton,
|
|
1432
1454
|
{
|
|
1433
1455
|
icon: copied ? Check : Copy,
|
|
@@ -1442,27 +1464,27 @@ function CodeBlock({ children, maxHeight = 300, className }) {
|
|
|
1442
1464
|
|
|
1443
1465
|
// src/composites/filter-bar.tsx
|
|
1444
1466
|
import { Search } from "lucide-react";
|
|
1445
|
-
import { jsx as
|
|
1467
|
+
import { jsx as jsx30 } from "react/jsx-runtime";
|
|
1446
1468
|
function FilterBar({ filters, values, onChange, className }) {
|
|
1447
1469
|
const handleChange = (key, value) => {
|
|
1448
1470
|
onChange({ ...values, [key]: value });
|
|
1449
1471
|
};
|
|
1450
|
-
return /* @__PURE__ */
|
|
1472
|
+
return /* @__PURE__ */ jsx30("div", { className: cn("flex items-center gap-2 flex-wrap", className), children: filters.map((filter) => {
|
|
1451
1473
|
switch (filter.type) {
|
|
1452
1474
|
case "search":
|
|
1453
|
-
return /* @__PURE__ */
|
|
1475
|
+
return /* @__PURE__ */ jsx30(
|
|
1454
1476
|
Input,
|
|
1455
1477
|
{
|
|
1456
1478
|
placeholder: filter.placeholder ?? "Search...",
|
|
1457
1479
|
value: values[filter.key] ?? "",
|
|
1458
1480
|
onChange: (e) => handleChange(filter.key, e.target.value),
|
|
1459
|
-
leftSlot: /* @__PURE__ */
|
|
1481
|
+
leftSlot: /* @__PURE__ */ jsx30(Search, { className: "h-3 w-3 text-foreground-subtle" }),
|
|
1460
1482
|
className: "w-48"
|
|
1461
1483
|
},
|
|
1462
1484
|
filter.key
|
|
1463
1485
|
);
|
|
1464
1486
|
case "select":
|
|
1465
|
-
return /* @__PURE__ */
|
|
1487
|
+
return /* @__PURE__ */ jsx30(
|
|
1466
1488
|
Select,
|
|
1467
1489
|
{
|
|
1468
1490
|
options: filter.options,
|
|
@@ -1473,10 +1495,10 @@ function FilterBar({ filters, values, onChange, className }) {
|
|
|
1473
1495
|
filter.key
|
|
1474
1496
|
);
|
|
1475
1497
|
case "badge-toggle":
|
|
1476
|
-
return /* @__PURE__ */
|
|
1498
|
+
return /* @__PURE__ */ jsx30("div", { className: "flex items-center gap-1", children: filter.options.map((option) => {
|
|
1477
1499
|
const currentValue = values[filter.key];
|
|
1478
1500
|
const isActive = currentValue === option.value;
|
|
1479
|
-
return /* @__PURE__ */
|
|
1501
|
+
return /* @__PURE__ */ jsx30(
|
|
1480
1502
|
"button",
|
|
1481
1503
|
{
|
|
1482
1504
|
type: "button",
|
|
@@ -1484,7 +1506,7 @@ function FilterBar({ filters, values, onChange, className }) {
|
|
|
1484
1506
|
filter.key,
|
|
1485
1507
|
isActive ? void 0 : option.value
|
|
1486
1508
|
),
|
|
1487
|
-
children: /* @__PURE__ */
|
|
1509
|
+
children: /* @__PURE__ */ jsx30(
|
|
1488
1510
|
Badge,
|
|
1489
1511
|
{
|
|
1490
1512
|
variant: isActive ? "info" : "default",
|
|
@@ -1503,7 +1525,7 @@ function FilterBar({ filters, values, onChange, className }) {
|
|
|
1503
1525
|
}
|
|
1504
1526
|
|
|
1505
1527
|
// src/composites/app-shell/sidebar-item.tsx
|
|
1506
|
-
import { jsx as
|
|
1528
|
+
import { jsx as jsx31, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1507
1529
|
function SidebarItem({
|
|
1508
1530
|
label,
|
|
1509
1531
|
icon: Icon,
|
|
@@ -1522,16 +1544,16 @@ function SidebarItem({
|
|
|
1522
1544
|
className
|
|
1523
1545
|
),
|
|
1524
1546
|
children: [
|
|
1525
|
-
/* @__PURE__ */
|
|
1526
|
-
/* @__PURE__ */
|
|
1527
|
-
badge !== void 0 && /* @__PURE__ */
|
|
1547
|
+
/* @__PURE__ */ jsx31(Icon, { className: "h-3.5 w-3.5 shrink-0" }),
|
|
1548
|
+
/* @__PURE__ */ jsx31("span", { className: "truncate flex-1", children: label }),
|
|
1549
|
+
badge !== void 0 && /* @__PURE__ */ jsx31(Badge, { className: "ml-auto text-[10px] px-1.5 py-0", children: badge })
|
|
1528
1550
|
]
|
|
1529
1551
|
}
|
|
1530
1552
|
);
|
|
1531
1553
|
}
|
|
1532
1554
|
|
|
1533
1555
|
// src/composites/app-shell/sidebar.tsx
|
|
1534
|
-
import { jsx as
|
|
1556
|
+
import { jsx as jsx32, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1535
1557
|
function Sidebar({ logo, sections, footer, className }) {
|
|
1536
1558
|
return /* @__PURE__ */ jsxs16(
|
|
1537
1559
|
"nav",
|
|
@@ -1541,14 +1563,14 @@ function Sidebar({ logo, sections, footer, className }) {
|
|
|
1541
1563
|
className
|
|
1542
1564
|
),
|
|
1543
1565
|
children: [
|
|
1544
|
-
logo && /* @__PURE__ */
|
|
1545
|
-
/* @__PURE__ */
|
|
1546
|
-
section.label && /* @__PURE__ */
|
|
1547
|
-
/* @__PURE__ */
|
|
1566
|
+
logo && /* @__PURE__ */ jsx32("div", { className: "px-3 py-2 shrink-0", children: logo }),
|
|
1567
|
+
/* @__PURE__ */ jsx32("div", { className: "flex-1 overflow-auto px-1 py-1", children: sections.map((section, sectionIndex) => /* @__PURE__ */ jsxs16("div", { className: cn(sectionIndex > 0 ? "mt-3" : ""), children: [
|
|
1568
|
+
section.label && /* @__PURE__ */ jsx32("span", { className: "text-[10px] text-foreground-disabled uppercase tracking-wider px-2 mb-1 block", children: section.label }),
|
|
1569
|
+
/* @__PURE__ */ jsx32("div", { className: "flex flex-col gap-0.5", children: section.items.map((item) => /* @__PURE__ */ jsx32(SidebarItem, { ...item }, item.href)) })
|
|
1548
1570
|
] }, sectionIndex)) }),
|
|
1549
1571
|
footer && footer.length > 0 && /* @__PURE__ */ jsxs16("div", { className: "shrink-0 px-1 pb-1", children: [
|
|
1550
|
-
/* @__PURE__ */
|
|
1551
|
-
/* @__PURE__ */
|
|
1572
|
+
/* @__PURE__ */ jsx32(Separator, { className: "mb-1" }),
|
|
1573
|
+
/* @__PURE__ */ jsx32("div", { className: "flex flex-col gap-0.5", children: footer.map((item) => /* @__PURE__ */ jsx32(SidebarItem, { ...item }, item.href)) })
|
|
1552
1574
|
] })
|
|
1553
1575
|
]
|
|
1554
1576
|
}
|
|
@@ -1557,29 +1579,29 @@ function Sidebar({ logo, sections, footer, className }) {
|
|
|
1557
1579
|
|
|
1558
1580
|
// src/composites/app-shell/app-shell.tsx
|
|
1559
1581
|
import { ChevronRight } from "lucide-react";
|
|
1560
|
-
import { jsx as
|
|
1582
|
+
import { jsx as jsx33, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
1561
1583
|
function AppShell({ sidebar, header, children, className }) {
|
|
1562
1584
|
return /* @__PURE__ */ jsxs17("div", { className: cn("flex h-screen", className), children: [
|
|
1563
|
-
/* @__PURE__ */
|
|
1585
|
+
/* @__PURE__ */ jsx33(Sidebar, { ...sidebar }),
|
|
1564
1586
|
/* @__PURE__ */ jsxs17("div", { className: "flex flex-1 flex-col min-w-0", children: [
|
|
1565
1587
|
header && /* @__PURE__ */ jsxs17("header", { className: "flex items-center h-10 border-b border-border px-4 shrink-0", children: [
|
|
1566
|
-
header.breadcrumbs && header.breadcrumbs.length > 0 && /* @__PURE__ */
|
|
1588
|
+
header.breadcrumbs && header.breadcrumbs.length > 0 && /* @__PURE__ */ jsx33("nav", { className: "flex items-center gap-1 text-xs flex-1 min-w-0", children: header.breadcrumbs.map((crumb, index) => {
|
|
1567
1589
|
const isLast = index === header.breadcrumbs.length - 1;
|
|
1568
1590
|
return /* @__PURE__ */ jsxs17("span", { className: "flex items-center gap-1", children: [
|
|
1569
|
-
index > 0 && /* @__PURE__ */
|
|
1570
|
-
crumb.href && !isLast ? /* @__PURE__ */
|
|
1591
|
+
index > 0 && /* @__PURE__ */ jsx33(ChevronRight, { className: "h-3 w-3 text-foreground-subtle shrink-0" }),
|
|
1592
|
+
crumb.href && !isLast ? /* @__PURE__ */ jsx33(
|
|
1571
1593
|
"a",
|
|
1572
1594
|
{
|
|
1573
1595
|
href: crumb.href,
|
|
1574
1596
|
className: "text-foreground-subtle hover:text-foreground transition-colors truncate",
|
|
1575
1597
|
children: crumb.label
|
|
1576
1598
|
}
|
|
1577
|
-
) : /* @__PURE__ */
|
|
1599
|
+
) : /* @__PURE__ */ jsx33("span", { className: "text-foreground truncate", children: crumb.label })
|
|
1578
1600
|
] }, index);
|
|
1579
1601
|
}) }),
|
|
1580
|
-
header.actions && /* @__PURE__ */
|
|
1602
|
+
header.actions && /* @__PURE__ */ jsx33("div", { className: "flex items-center gap-2 ml-auto shrink-0", children: header.actions })
|
|
1581
1603
|
] }),
|
|
1582
|
-
/* @__PURE__ */
|
|
1604
|
+
/* @__PURE__ */ jsx33("main", { className: "flex-1 overflow-auto p-4", children })
|
|
1583
1605
|
] })
|
|
1584
1606
|
] });
|
|
1585
1607
|
}
|
|
@@ -1597,20 +1619,20 @@ import {
|
|
|
1597
1619
|
|
|
1598
1620
|
// src/composites/data-table/data-table-header.tsx
|
|
1599
1621
|
import { ArrowUpDown, ArrowUp, ArrowDown } from "lucide-react";
|
|
1600
|
-
import { jsx as
|
|
1622
|
+
import { jsx as jsx34, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
1601
1623
|
function DataTableHeader({
|
|
1602
1624
|
headerGroups,
|
|
1603
1625
|
onSortingChange,
|
|
1604
1626
|
stickyHeader,
|
|
1605
1627
|
flexRender: render
|
|
1606
1628
|
}) {
|
|
1607
|
-
return /* @__PURE__ */
|
|
1629
|
+
return /* @__PURE__ */ jsx34(
|
|
1608
1630
|
"thead",
|
|
1609
1631
|
{
|
|
1610
1632
|
className: cn(
|
|
1611
1633
|
stickyHeader && "sticky top-0 z-10 bg-background"
|
|
1612
1634
|
),
|
|
1613
|
-
children: headerGroups.map((headerGroup) => /* @__PURE__ */
|
|
1635
|
+
children: headerGroups.map((headerGroup) => /* @__PURE__ */ jsx34("tr", { className: "h-6", children: headerGroup.headers.map((header) => /* @__PURE__ */ jsx34(
|
|
1614
1636
|
HeaderCell,
|
|
1615
1637
|
{
|
|
1616
1638
|
header,
|
|
@@ -1625,7 +1647,7 @@ function DataTableHeader({
|
|
|
1625
1647
|
function HeaderCell({ header, sortable, flexRender: render }) {
|
|
1626
1648
|
const sorted = header.column.getIsSorted();
|
|
1627
1649
|
const SortIcon = sorted === "asc" ? ArrowUp : sorted === "desc" ? ArrowDown : ArrowUpDown;
|
|
1628
|
-
return /* @__PURE__ */
|
|
1650
|
+
return /* @__PURE__ */ jsx34(
|
|
1629
1651
|
"th",
|
|
1630
1652
|
{
|
|
1631
1653
|
className: cn(
|
|
@@ -1635,7 +1657,7 @@ function HeaderCell({ header, sortable, flexRender: render }) {
|
|
|
1635
1657
|
onClick: sortable ? header.column.getToggleSortingHandler() : void 0,
|
|
1636
1658
|
children: /* @__PURE__ */ jsxs18("span", { className: "inline-flex items-center gap-1", children: [
|
|
1637
1659
|
header.isPlaceholder ? null : render(header.column.columnDef.header, header.getContext()),
|
|
1638
|
-
sortable && /* @__PURE__ */
|
|
1660
|
+
sortable && /* @__PURE__ */ jsx34(SortIcon, { className: "h-3 w-3" })
|
|
1639
1661
|
] })
|
|
1640
1662
|
}
|
|
1641
1663
|
);
|
|
@@ -1643,7 +1665,7 @@ function HeaderCell({ header, sortable, flexRender: render }) {
|
|
|
1643
1665
|
|
|
1644
1666
|
// src/composites/data-table/data-table-row.tsx
|
|
1645
1667
|
import { MoreHorizontal } from "lucide-react";
|
|
1646
|
-
import { jsx as
|
|
1668
|
+
import { jsx as jsx35, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
1647
1669
|
function DataTableRow({
|
|
1648
1670
|
row,
|
|
1649
1671
|
onRowClick,
|
|
@@ -1661,17 +1683,17 @@ function DataTableRow({
|
|
|
1661
1683
|
),
|
|
1662
1684
|
onClick: onRowClick ? () => onRowClick(row.original) : void 0,
|
|
1663
1685
|
children: [
|
|
1664
|
-
row.getVisibleCells().map((cell) => /* @__PURE__ */
|
|
1665
|
-
actions.length > 0 && /* @__PURE__ */
|
|
1666
|
-
/* @__PURE__ */
|
|
1686
|
+
row.getVisibleCells().map((cell) => /* @__PURE__ */ jsx35(DataTableCell, { cell, flexRender: render }, cell.id)),
|
|
1687
|
+
actions.length > 0 && /* @__PURE__ */ jsx35("td", { className: "px-2 py-1.5 w-8", children: /* @__PURE__ */ jsxs19(Dropdown, { children: [
|
|
1688
|
+
/* @__PURE__ */ jsx35(
|
|
1667
1689
|
DropdownTrigger,
|
|
1668
1690
|
{
|
|
1669
1691
|
className: "p-0.5 rounded hover:bg-surface-hover",
|
|
1670
1692
|
onClick: (e) => e.stopPropagation(),
|
|
1671
|
-
children: /* @__PURE__ */
|
|
1693
|
+
children: /* @__PURE__ */ jsx35(MoreHorizontal, { className: "h-3.5 w-3.5 text-foreground-muted" })
|
|
1672
1694
|
}
|
|
1673
1695
|
),
|
|
1674
|
-
/* @__PURE__ */
|
|
1696
|
+
/* @__PURE__ */ jsx35(DropdownContent, { className: "right-0 left-auto", children: actions.map((action) => /* @__PURE__ */ jsx35(
|
|
1675
1697
|
DropdownItem,
|
|
1676
1698
|
{
|
|
1677
1699
|
icon: action.icon,
|
|
@@ -1690,12 +1712,12 @@ function DataTableRow({
|
|
|
1690
1712
|
);
|
|
1691
1713
|
}
|
|
1692
1714
|
function DataTableCell({ cell, flexRender: render }) {
|
|
1693
|
-
return /* @__PURE__ */
|
|
1715
|
+
return /* @__PURE__ */ jsx35("td", { className: "px-2 py-1.5 text-xs text-foreground", children: render(cell.column.columnDef.cell, cell.getContext()) });
|
|
1694
1716
|
}
|
|
1695
1717
|
|
|
1696
1718
|
// src/composites/data-table/data-table-pagination.tsx
|
|
1697
1719
|
import { ChevronLeft, ChevronRight as ChevronRight2 } from "lucide-react";
|
|
1698
|
-
import { jsx as
|
|
1720
|
+
import { jsx as jsx36, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
1699
1721
|
var PAGE_SIZE_OPTIONS = [
|
|
1700
1722
|
{ value: "10", label: "10" },
|
|
1701
1723
|
{ value: "25", label: "25" },
|
|
@@ -1712,8 +1734,8 @@ function DataTablePagination({
|
|
|
1712
1734
|
const currentPage = page + 1;
|
|
1713
1735
|
return /* @__PURE__ */ jsxs20("div", { className: "flex items-center justify-between px-2 py-2 text-xs text-foreground-muted", children: [
|
|
1714
1736
|
/* @__PURE__ */ jsxs20("div", { className: "flex items-center gap-2", children: [
|
|
1715
|
-
/* @__PURE__ */
|
|
1716
|
-
/* @__PURE__ */
|
|
1737
|
+
/* @__PURE__ */ jsx36("span", { children: "Rows per page" }),
|
|
1738
|
+
/* @__PURE__ */ jsx36("div", { className: "w-16", children: /* @__PURE__ */ jsx36(
|
|
1717
1739
|
Select,
|
|
1718
1740
|
{
|
|
1719
1741
|
options: PAGE_SIZE_OPTIONS,
|
|
@@ -1732,7 +1754,7 @@ function DataTablePagination({
|
|
|
1732
1754
|
" of ",
|
|
1733
1755
|
totalPages
|
|
1734
1756
|
] }),
|
|
1735
|
-
/* @__PURE__ */
|
|
1757
|
+
/* @__PURE__ */ jsx36(
|
|
1736
1758
|
IconButton,
|
|
1737
1759
|
{
|
|
1738
1760
|
icon: ChevronLeft,
|
|
@@ -1743,7 +1765,7 @@ function DataTablePagination({
|
|
|
1743
1765
|
onClick: () => onPaginationChange?.({ pageIndex: page - 1, pageSize })
|
|
1744
1766
|
}
|
|
1745
1767
|
),
|
|
1746
|
-
/* @__PURE__ */
|
|
1768
|
+
/* @__PURE__ */ jsx36(
|
|
1747
1769
|
IconButton,
|
|
1748
1770
|
{
|
|
1749
1771
|
icon: ChevronRight2,
|
|
@@ -1759,7 +1781,7 @@ function DataTablePagination({
|
|
|
1759
1781
|
}
|
|
1760
1782
|
|
|
1761
1783
|
// src/composites/data-table/data-table.tsx
|
|
1762
|
-
import { Fragment, jsx as
|
|
1784
|
+
import { Fragment, jsx as jsx37, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
1763
1785
|
function DataTable({
|
|
1764
1786
|
data,
|
|
1765
1787
|
columns: userColumns,
|
|
@@ -1782,7 +1804,7 @@ function DataTable({
|
|
|
1782
1804
|
if (!selectable) return userColumns;
|
|
1783
1805
|
const selectColumn = {
|
|
1784
1806
|
id: "__select",
|
|
1785
|
-
header: ({ table: table2 }) => /* @__PURE__ */
|
|
1807
|
+
header: ({ table: table2 }) => /* @__PURE__ */ jsx37(
|
|
1786
1808
|
Checkbox,
|
|
1787
1809
|
{
|
|
1788
1810
|
checked: table2.getIsAllPageRowsSelected(),
|
|
@@ -1790,7 +1812,7 @@ function DataTable({
|
|
|
1790
1812
|
"aria-label": "Select all"
|
|
1791
1813
|
}
|
|
1792
1814
|
),
|
|
1793
|
-
cell: ({ row }) => /* @__PURE__ */
|
|
1815
|
+
cell: ({ row }) => /* @__PURE__ */ jsx37(
|
|
1794
1816
|
Checkbox,
|
|
1795
1817
|
{
|
|
1796
1818
|
checked: row.getIsSelected(),
|
|
@@ -1830,7 +1852,7 @@ function DataTable({
|
|
|
1830
1852
|
const hasActions = !!rowActions;
|
|
1831
1853
|
return /* @__PURE__ */ jsxs21("div", { className: cn("overflow-auto", className), children: [
|
|
1832
1854
|
/* @__PURE__ */ jsxs21("table", { className: "w-full border-collapse", children: [
|
|
1833
|
-
/* @__PURE__ */
|
|
1855
|
+
/* @__PURE__ */ jsx37(
|
|
1834
1856
|
DataTableHeader,
|
|
1835
1857
|
{
|
|
1836
1858
|
headerGroups: table.getHeaderGroups(),
|
|
@@ -1839,14 +1861,14 @@ function DataTable({
|
|
|
1839
1861
|
flexRender
|
|
1840
1862
|
}
|
|
1841
1863
|
),
|
|
1842
|
-
/* @__PURE__ */
|
|
1864
|
+
/* @__PURE__ */ jsx37("tbody", { children: loading ? /* @__PURE__ */ jsx37(LoadingRows, { colSpan: columns.length + (hasActions ? 1 : 0), compact }) : table.getRowModel().rows.length === 0 ? /* @__PURE__ */ jsx37("tr", { children: /* @__PURE__ */ jsx37(
|
|
1843
1865
|
"td",
|
|
1844
1866
|
{
|
|
1845
1867
|
colSpan: columns.length + (hasActions ? 1 : 0),
|
|
1846
1868
|
className: "text-center py-8 text-xs text-foreground-muted",
|
|
1847
1869
|
children: emptyState ?? "No data"
|
|
1848
1870
|
}
|
|
1849
|
-
) }) : table.getRowModel().rows.map((row) => /* @__PURE__ */
|
|
1871
|
+
) }) : table.getRowModel().rows.map((row) => /* @__PURE__ */ jsx37(
|
|
1850
1872
|
DataTableRow,
|
|
1851
1873
|
{
|
|
1852
1874
|
row,
|
|
@@ -1857,7 +1879,7 @@ function DataTable({
|
|
|
1857
1879
|
row.id
|
|
1858
1880
|
)) })
|
|
1859
1881
|
] }),
|
|
1860
|
-
pagination && /* @__PURE__ */
|
|
1882
|
+
pagination && /* @__PURE__ */ jsx37(
|
|
1861
1883
|
DataTablePagination,
|
|
1862
1884
|
{
|
|
1863
1885
|
page: pagination.page,
|
|
@@ -1869,11 +1891,11 @@ function DataTable({
|
|
|
1869
1891
|
] });
|
|
1870
1892
|
}
|
|
1871
1893
|
function LoadingRows({ colSpan, compact }) {
|
|
1872
|
-
return /* @__PURE__ */
|
|
1894
|
+
return /* @__PURE__ */ jsx37(Fragment, { children: Array.from({ length: 5 }).map((_, rowIdx) => /* @__PURE__ */ jsx37("tr", { className: compact ? "h-7" : "h-9", children: Array.from({ length: colSpan }).map((_2, colIdx) => /* @__PURE__ */ jsx37("td", { className: "px-2 py-1.5", children: /* @__PURE__ */ jsx37(Skeleton, { className: "h-3 w-full" }) }, colIdx)) }, rowIdx)) });
|
|
1873
1895
|
}
|
|
1874
1896
|
|
|
1875
1897
|
// src/composites/device-card.tsx
|
|
1876
|
-
import { jsx as
|
|
1898
|
+
import { jsx as jsx38, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
1877
1899
|
var STATUS_COLORS = {
|
|
1878
1900
|
online: "bg-success",
|
|
1879
1901
|
offline: "bg-danger",
|
|
@@ -1905,11 +1927,11 @@ function DeviceCard({
|
|
|
1905
1927
|
),
|
|
1906
1928
|
children: [
|
|
1907
1929
|
/* @__PURE__ */ jsxs22("div", { className: "flex items-center justify-between mb-2", children: [
|
|
1908
|
-
/* @__PURE__ */
|
|
1909
|
-
status && /* @__PURE__ */
|
|
1930
|
+
/* @__PURE__ */ jsx38("span", { className: "text-sm font-medium truncate", children: title }),
|
|
1931
|
+
status && /* @__PURE__ */ jsx38("span", { className: cn("h-2 w-2 rounded-full shrink-0", STATUS_COLORS[status]) })
|
|
1910
1932
|
] }),
|
|
1911
|
-
subtitle && /* @__PURE__ */
|
|
1912
|
-
badges && badges.length > 0 && /* @__PURE__ */
|
|
1933
|
+
subtitle && /* @__PURE__ */ jsx38("div", { className: "text-[11px] text-foreground-muted", children: subtitle }),
|
|
1934
|
+
badges && badges.length > 0 && /* @__PURE__ */ jsx38("div", { className: "flex flex-wrap gap-1 mt-2", children: badges.map((badge, i) => {
|
|
1913
1935
|
const cls = cn(
|
|
1914
1936
|
"rounded px-1.5 py-0.5 text-[10px] flex items-center gap-0.5",
|
|
1915
1937
|
selected ? "bg-primary/20" : "bg-surface-hover",
|
|
@@ -1934,7 +1956,7 @@ function DeviceCard({
|
|
|
1934
1956
|
badge.label
|
|
1935
1957
|
] }, i);
|
|
1936
1958
|
}) }),
|
|
1937
|
-
!isOffline && actions && actions.length > 0 && /* @__PURE__ */
|
|
1959
|
+
!isOffline && actions && actions.length > 0 && /* @__PURE__ */ jsx38("div", { className: "flex items-center gap-0.5 mt-2 -mb-1", children: actions.map((action, i) => /* @__PURE__ */ jsx38(
|
|
1938
1960
|
"button",
|
|
1939
1961
|
{
|
|
1940
1962
|
onClick: (e) => {
|
|
@@ -1948,21 +1970,21 @@ function DeviceCard({
|
|
|
1948
1970
|
},
|
|
1949
1971
|
i
|
|
1950
1972
|
)) }),
|
|
1951
|
-
isOffline && offlineAction && /* @__PURE__ */
|
|
1973
|
+
isOffline && offlineAction && /* @__PURE__ */ jsx38("div", { className: "mt-2", onClick: (e) => e.stopPropagation(), children: offlineAction })
|
|
1952
1974
|
]
|
|
1953
1975
|
}
|
|
1954
1976
|
);
|
|
1955
1977
|
}
|
|
1956
1978
|
|
|
1957
1979
|
// src/composites/device-grid.tsx
|
|
1958
|
-
import { jsx as
|
|
1980
|
+
import { jsx as jsx39 } from "react/jsx-runtime";
|
|
1959
1981
|
function DeviceGrid({
|
|
1960
1982
|
children,
|
|
1961
1983
|
minCardWidth = 220,
|
|
1962
1984
|
gap = 3,
|
|
1963
1985
|
className
|
|
1964
1986
|
}) {
|
|
1965
|
-
return /* @__PURE__ */
|
|
1987
|
+
return /* @__PURE__ */ jsx39(
|
|
1966
1988
|
"div",
|
|
1967
1989
|
{
|
|
1968
1990
|
className: cn(
|
|
@@ -1978,15 +2000,1715 @@ function DeviceGrid({
|
|
|
1978
2000
|
}
|
|
1979
2001
|
);
|
|
1980
2002
|
}
|
|
2003
|
+
|
|
2004
|
+
// src/composites/pipeline-step.tsx
|
|
2005
|
+
import { useState as useState9 } from "react";
|
|
2006
|
+
import { ChevronRight as ChevronRight3, ChevronDown as ChevronDown2 } from "lucide-react";
|
|
2007
|
+
import { jsx as jsx40, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
2008
|
+
var ADDON_COLORS = {
|
|
2009
|
+
"object-detection": "border-l-blue-500",
|
|
2010
|
+
"motion-detection": "border-l-amber-500",
|
|
2011
|
+
"face-detection": "border-l-purple-500",
|
|
2012
|
+
"face-recognition": "border-l-violet-500",
|
|
2013
|
+
"plate-detection": "border-l-pink-500",
|
|
2014
|
+
"plate-recognition": "border-l-rose-500",
|
|
2015
|
+
"animal-classifier": "border-l-lime-500",
|
|
2016
|
+
"bird-nabirds-classifier": "border-l-emerald-500",
|
|
2017
|
+
"bird-global-classifier": "border-l-teal-500",
|
|
2018
|
+
"audio-classification": "border-l-green-500",
|
|
2019
|
+
"segmentation-refiner": "border-l-cyan-500"
|
|
2020
|
+
};
|
|
2021
|
+
var SLOT_FALLBACK = {
|
|
2022
|
+
detector: "border-l-blue-500",
|
|
2023
|
+
cropper: "border-l-purple-500",
|
|
2024
|
+
classifier: "border-l-lime-500",
|
|
2025
|
+
refiner: "border-l-cyan-500"
|
|
2026
|
+
};
|
|
2027
|
+
function borderColor(addonId, slot) {
|
|
2028
|
+
return ADDON_COLORS[addonId] ?? SLOT_FALLBACK[slot] ?? "border-l-primary";
|
|
2029
|
+
}
|
|
2030
|
+
var BACKEND_FORMAT = {
|
|
2031
|
+
cpu: "onnx",
|
|
2032
|
+
coreml: "coreml",
|
|
2033
|
+
openvino: "openvino",
|
|
2034
|
+
cuda: "onnx",
|
|
2035
|
+
tensorrt: "onnx",
|
|
2036
|
+
"onnx-py": "onnx",
|
|
2037
|
+
pytorch: "pt"
|
|
2038
|
+
};
|
|
2039
|
+
function backendsForRuntime(runtime, caps, schema) {
|
|
2040
|
+
const allBackends = runtime === "node" ? caps.runtimes.node.backends : caps.runtimes.python.backends;
|
|
2041
|
+
if (!schema) return allBackends;
|
|
2042
|
+
const availableFormats = /* @__PURE__ */ new Set();
|
|
2043
|
+
for (const m of schema.models) {
|
|
2044
|
+
for (const fmt of Object.keys(m.formats)) {
|
|
2045
|
+
availableFormats.add(fmt);
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
if (runtime === "node") availableFormats.add("onnx");
|
|
2049
|
+
return allBackends.map((b) => {
|
|
2050
|
+
const neededFormat = BACKEND_FORMAT[b.id] ?? "onnx";
|
|
2051
|
+
const hasFormat = availableFormats.has(neededFormat);
|
|
2052
|
+
return { ...b, available: b.available && hasFormat };
|
|
2053
|
+
});
|
|
2054
|
+
}
|
|
2055
|
+
function modelsForStep(schema, runtime, backend, caps) {
|
|
2056
|
+
if (!schema) return [];
|
|
2057
|
+
const fmt = runtime === "node" ? "onnx" : caps.runtimes.python.backends.find((b) => b.id === backend)?.modelFormat ?? "onnx";
|
|
2058
|
+
return schema.models.filter((m) => m.formats[fmt]).map((m) => ({
|
|
2059
|
+
id: m.id,
|
|
2060
|
+
name: m.name,
|
|
2061
|
+
downloaded: m.formats[fmt]?.downloaded ?? false
|
|
2062
|
+
}));
|
|
2063
|
+
}
|
|
2064
|
+
function runtimeOptions(caps) {
|
|
2065
|
+
const opts = [
|
|
2066
|
+
{ id: "node", label: "Node.js (ONNX)", available: true }
|
|
2067
|
+
];
|
|
2068
|
+
if (caps.runtimes.python.available && caps.runtimes.python.backends.some((b) => b.available)) {
|
|
2069
|
+
opts.unshift({ id: "python", label: "Python", available: true });
|
|
2070
|
+
}
|
|
2071
|
+
return opts;
|
|
2072
|
+
}
|
|
2073
|
+
function PipelineStep({
|
|
2074
|
+
step,
|
|
2075
|
+
schema,
|
|
2076
|
+
allSchemas,
|
|
2077
|
+
capabilities,
|
|
2078
|
+
depth = 0,
|
|
2079
|
+
onChange,
|
|
2080
|
+
onDelete,
|
|
2081
|
+
readOnly = false
|
|
2082
|
+
}) {
|
|
2083
|
+
const [expanded, setExpanded] = useState9(false);
|
|
2084
|
+
const color = borderColor(step.addonId, step.slot);
|
|
2085
|
+
const backends = backendsForRuntime(step.runtime, capabilities, schema);
|
|
2086
|
+
const rtOptions = runtimeOptions(capabilities);
|
|
2087
|
+
const currentBackendAvailable = backends.find((b) => b.id === step.backend)?.available ?? false;
|
|
2088
|
+
if (!currentBackendAvailable && !readOnly) {
|
|
2089
|
+
const firstAvailable = backends.find((b) => b.available);
|
|
2090
|
+
if (firstAvailable && firstAvailable.id !== step.backend) {
|
|
2091
|
+
queueMicrotask(() => onChange({ ...step, backend: firstAvailable.id }));
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
const models = modelsForStep(schema, step.runtime, step.backend, capabilities);
|
|
2095
|
+
if (models.length > 0 && !models.some((m) => m.id === step.modelId) && !readOnly) {
|
|
2096
|
+
const defaultModel = schema?.defaultModelId ? models.find((m) => m.id === schema.defaultModelId) : null;
|
|
2097
|
+
const fallback = defaultModel ?? models[0];
|
|
2098
|
+
if (fallback && fallback.id !== step.modelId) {
|
|
2099
|
+
queueMicrotask(() => onChange({ ...step, modelId: fallback.id }));
|
|
2100
|
+
}
|
|
2101
|
+
}
|
|
2102
|
+
function handleClick(e) {
|
|
2103
|
+
if (e.target.closest(".step-config")) return;
|
|
2104
|
+
setExpanded((v) => !v);
|
|
2105
|
+
}
|
|
2106
|
+
return /* @__PURE__ */ jsx40("div", { className: "space-y-2", children: /* @__PURE__ */ jsxs23(
|
|
2107
|
+
"div",
|
|
2108
|
+
{
|
|
2109
|
+
className: cn(
|
|
2110
|
+
"rounded-xl border border-border bg-surface overflow-hidden border-l-4 shadow-sm",
|
|
2111
|
+
color,
|
|
2112
|
+
!step.enabled && "opacity-[0.45]"
|
|
2113
|
+
),
|
|
2114
|
+
children: [
|
|
2115
|
+
/* @__PURE__ */ jsxs23("div", { className: "flex items-center gap-2.5 px-3 py-2.5 cursor-pointer select-none", onClick: handleClick, children: [
|
|
2116
|
+
/* @__PURE__ */ jsx40("span", { className: "text-foreground-subtle", children: expanded ? /* @__PURE__ */ jsx40(ChevronDown2, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx40(ChevronRight3, { className: "h-4 w-4" }) }),
|
|
2117
|
+
/* @__PURE__ */ jsxs23("div", { className: "flex-1 min-w-0", children: [
|
|
2118
|
+
/* @__PURE__ */ jsx40("span", { className: "text-[10px] uppercase tracking-wider font-medium text-foreground-subtle/60 block leading-none", children: step.slot }),
|
|
2119
|
+
/* @__PURE__ */ jsx40("span", { className: "text-sm font-semibold text-foreground truncate block leading-tight", children: step.addonName }),
|
|
2120
|
+
/* @__PURE__ */ jsxs23("div", { className: "flex items-center gap-1 mt-0.5 flex-wrap", children: [
|
|
2121
|
+
step.inputClasses.map((c) => /* @__PURE__ */ jsx40("span", { className: "text-[9px] uppercase font-semibold tracking-wide px-1.5 py-0.5 rounded bg-blue-500/12 text-blue-400", children: c }, c)),
|
|
2122
|
+
step.inputClasses.length > 0 && step.outputClasses.length > 0 && /* @__PURE__ */ jsx40("span", { className: "text-foreground-subtle/40 text-[10px]", children: "\u2192" }),
|
|
2123
|
+
step.outputClasses.map((c) => /* @__PURE__ */ jsx40("span", { className: "text-[9px] uppercase font-semibold tracking-wide px-1.5 py-0.5 rounded bg-green-500/12 text-green-400", children: c }, c))
|
|
2124
|
+
] })
|
|
2125
|
+
] }),
|
|
2126
|
+
/* @__PURE__ */ jsx40(
|
|
2127
|
+
"button",
|
|
2128
|
+
{
|
|
2129
|
+
onClick: (e) => {
|
|
2130
|
+
e.stopPropagation();
|
|
2131
|
+
onChange({ ...step, enabled: !step.enabled });
|
|
2132
|
+
},
|
|
2133
|
+
className: cn(
|
|
2134
|
+
"relative inline-flex h-6 w-11 shrink-0 items-center rounded-full transition-colors",
|
|
2135
|
+
step.enabled ? "bg-success" : "bg-foreground-subtle/30"
|
|
2136
|
+
),
|
|
2137
|
+
children: /* @__PURE__ */ jsx40("span", { className: cn(
|
|
2138
|
+
"inline-block h-4 w-4 rounded-full bg-white shadow transition-transform",
|
|
2139
|
+
step.enabled ? "translate-x-6" : "translate-x-1"
|
|
2140
|
+
) })
|
|
2141
|
+
}
|
|
2142
|
+
)
|
|
2143
|
+
] }),
|
|
2144
|
+
expanded && /* @__PURE__ */ jsxs23("div", { className: "step-config border-t border-border bg-background px-4 py-4 space-y-3", children: [
|
|
2145
|
+
/* @__PURE__ */ jsxs23("div", { className: "grid grid-cols-2 gap-3", children: [
|
|
2146
|
+
/* @__PURE__ */ jsx40(
|
|
2147
|
+
ConfigSelect,
|
|
2148
|
+
{
|
|
2149
|
+
label: "Agent",
|
|
2150
|
+
value: step.agentId,
|
|
2151
|
+
disabled: readOnly,
|
|
2152
|
+
options: [{ value: step.agentId || "hub", label: step.agentId || "Hub (local)" }],
|
|
2153
|
+
onChange: (v) => onChange({ ...step, agentId: v })
|
|
2154
|
+
}
|
|
2155
|
+
),
|
|
2156
|
+
/* @__PURE__ */ jsx40(
|
|
2157
|
+
ConfigSelect,
|
|
2158
|
+
{
|
|
2159
|
+
label: "Runtime",
|
|
2160
|
+
value: step.runtime,
|
|
2161
|
+
disabled: readOnly,
|
|
2162
|
+
options: rtOptions.map((r) => ({ value: r.id, label: r.label, disabled: !r.available })),
|
|
2163
|
+
onChange: (v) => onChange({ ...step, runtime: v })
|
|
2164
|
+
}
|
|
2165
|
+
),
|
|
2166
|
+
/* @__PURE__ */ jsx40(
|
|
2167
|
+
ConfigSelect,
|
|
2168
|
+
{
|
|
2169
|
+
label: "Backend",
|
|
2170
|
+
value: step.backend,
|
|
2171
|
+
disabled: readOnly,
|
|
2172
|
+
options: backends.map((b) => ({ value: b.id, label: b.label, disabled: !b.available })),
|
|
2173
|
+
onChange: (v) => onChange({ ...step, backend: v })
|
|
2174
|
+
}
|
|
2175
|
+
),
|
|
2176
|
+
/* @__PURE__ */ jsx40(
|
|
2177
|
+
ConfigSelect,
|
|
2178
|
+
{
|
|
2179
|
+
label: "Model",
|
|
2180
|
+
value: step.modelId,
|
|
2181
|
+
disabled: readOnly,
|
|
2182
|
+
options: models.map((m) => ({ value: m.id, label: `${m.name}${m.downloaded ? " \u2713" : ""}` })),
|
|
2183
|
+
onChange: (v) => onChange({ ...step, modelId: v })
|
|
2184
|
+
}
|
|
2185
|
+
)
|
|
2186
|
+
] }),
|
|
2187
|
+
/* @__PURE__ */ jsxs23("div", { children: [
|
|
2188
|
+
/* @__PURE__ */ jsxs23("div", { className: "flex items-center justify-between mb-1", children: [
|
|
2189
|
+
/* @__PURE__ */ jsx40("span", { className: "text-[10px] font-medium text-foreground-subtle uppercase tracking-wide", children: "Confidence" }),
|
|
2190
|
+
/* @__PURE__ */ jsxs23("span", { className: "text-xs font-medium text-foreground tabular-nums", children: [
|
|
2191
|
+
(step.confidence * 100).toFixed(0),
|
|
2192
|
+
"%"
|
|
2193
|
+
] })
|
|
2194
|
+
] }),
|
|
2195
|
+
/* @__PURE__ */ jsx40(
|
|
2196
|
+
"input",
|
|
2197
|
+
{
|
|
2198
|
+
type: "range",
|
|
2199
|
+
min: 0,
|
|
2200
|
+
max: 1,
|
|
2201
|
+
step: 0.01,
|
|
2202
|
+
value: step.confidence,
|
|
2203
|
+
disabled: readOnly,
|
|
2204
|
+
onChange: (e) => onChange({ ...step, confidence: Number(e.target.value) }),
|
|
2205
|
+
className: "w-full accent-primary h-1.5"
|
|
2206
|
+
}
|
|
2207
|
+
)
|
|
2208
|
+
] })
|
|
2209
|
+
] })
|
|
2210
|
+
]
|
|
2211
|
+
}
|
|
2212
|
+
) });
|
|
2213
|
+
}
|
|
2214
|
+
function ConfigSelect({ label, value, options, disabled, onChange }) {
|
|
2215
|
+
return /* @__PURE__ */ jsxs23("div", { children: [
|
|
2216
|
+
/* @__PURE__ */ jsx40("label", { className: "block text-[10px] font-medium text-foreground-subtle uppercase tracking-wide mb-1.5", children: label }),
|
|
2217
|
+
/* @__PURE__ */ jsxs23(
|
|
2218
|
+
"select",
|
|
2219
|
+
{
|
|
2220
|
+
value,
|
|
2221
|
+
onChange: (e) => onChange(e.target.value),
|
|
2222
|
+
disabled,
|
|
2223
|
+
className: "w-full rounded-lg border border-border bg-surface px-3 py-2 text-xs text-foreground focus:outline-none focus:border-primary/50",
|
|
2224
|
+
children: [
|
|
2225
|
+
options.length === 0 && /* @__PURE__ */ jsx40("option", { value, children: value || "default" }),
|
|
2226
|
+
options.map((o) => /* @__PURE__ */ jsx40("option", { value: o.value, disabled: o.disabled, children: o.label }, o.value))
|
|
2227
|
+
]
|
|
2228
|
+
}
|
|
2229
|
+
)
|
|
2230
|
+
] });
|
|
2231
|
+
}
|
|
2232
|
+
|
|
2233
|
+
// src/composites/pipeline-runtime-selector.tsx
|
|
2234
|
+
import { Cpu, Star } from "lucide-react";
|
|
2235
|
+
import { jsx as jsx41, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
2236
|
+
function PipelineRuntimeSelector({ options, value, onChange }) {
|
|
2237
|
+
return /* @__PURE__ */ jsx41("div", { className: "flex flex-wrap gap-2", children: options.map((opt) => {
|
|
2238
|
+
const active = opt.id === value;
|
|
2239
|
+
return /* @__PURE__ */ jsxs24(
|
|
2240
|
+
"button",
|
|
2241
|
+
{
|
|
2242
|
+
onClick: () => opt.available && onChange(opt.id),
|
|
2243
|
+
disabled: !opt.available,
|
|
2244
|
+
className: `flex items-center gap-2 rounded-lg border px-3 py-2 text-xs font-medium transition-all ${active ? "border-primary/40 bg-primary/10 text-primary" : opt.available ? "border-border bg-surface text-foreground-subtle hover:bg-surface-hover hover:text-foreground" : "border-border/40 bg-surface/40 text-foreground-subtle/40 cursor-not-allowed"}`,
|
|
2245
|
+
children: [
|
|
2246
|
+
/* @__PURE__ */ jsx41(Cpu, { className: "h-3.5 w-3.5 shrink-0" }),
|
|
2247
|
+
opt.label,
|
|
2248
|
+
opt.isBest && /* @__PURE__ */ jsxs24("span", { className: "inline-flex items-center gap-0.5 rounded-full bg-amber-500/15 px-1.5 py-0.5 text-[10px] font-semibold text-amber-400", children: [
|
|
2249
|
+
/* @__PURE__ */ jsx41(Star, { className: "h-2.5 w-2.5" }),
|
|
2250
|
+
"Best"
|
|
2251
|
+
] }),
|
|
2252
|
+
opt.platformScore != null && /* @__PURE__ */ jsxs24("span", { className: "text-[10px] text-foreground-subtle/60", children: [
|
|
2253
|
+
"(",
|
|
2254
|
+
opt.platformScore,
|
|
2255
|
+
")"
|
|
2256
|
+
] }),
|
|
2257
|
+
/* @__PURE__ */ jsx41(
|
|
2258
|
+
"span",
|
|
2259
|
+
{
|
|
2260
|
+
className: `h-1.5 w-1.5 rounded-full ${opt.available ? "bg-success" : "bg-danger"}`
|
|
2261
|
+
}
|
|
2262
|
+
)
|
|
2263
|
+
]
|
|
2264
|
+
},
|
|
2265
|
+
opt.id
|
|
2266
|
+
);
|
|
2267
|
+
}) });
|
|
2268
|
+
}
|
|
2269
|
+
|
|
2270
|
+
// src/composites/pipeline-builder.tsx
|
|
2271
|
+
import { useMemo as useMemo3, useState as useState10 } from "react";
|
|
2272
|
+
import { Save, CopyPlus, Trash2, PlusCircle, X as X2 } from "lucide-react";
|
|
2273
|
+
|
|
2274
|
+
// src/lib/validate-template.ts
|
|
2275
|
+
function validateTemplate(steps, schema) {
|
|
2276
|
+
const availableAddonIds = new Set(
|
|
2277
|
+
schema.slots.flatMap((s) => s.addons.map((a) => a.id))
|
|
2278
|
+
);
|
|
2279
|
+
const warnings = [];
|
|
2280
|
+
function validateStep(step) {
|
|
2281
|
+
if (!availableAddonIds.has(step.addonId)) {
|
|
2282
|
+
warnings.push(`Addon "${step.addonId}" is no longer available \u2014 step removed`);
|
|
2283
|
+
return null;
|
|
2284
|
+
}
|
|
2285
|
+
const addon = schema.slots.flatMap((s) => s.addons).find((a) => a.id === step.addonId);
|
|
2286
|
+
let modelId = step.modelId;
|
|
2287
|
+
if (addon && !addon.models.some((m) => m.id === modelId)) {
|
|
2288
|
+
const fallback = addon.defaultModelId;
|
|
2289
|
+
warnings.push(`Model "${modelId}" not available for ${step.addonId} \u2014 using "${fallback}"`);
|
|
2290
|
+
modelId = fallback;
|
|
2291
|
+
}
|
|
2292
|
+
const validChildren = step.children.map((c) => validateStep(c)).filter((c) => c !== null);
|
|
2293
|
+
return { ...step, modelId, children: validChildren };
|
|
2294
|
+
}
|
|
2295
|
+
const validSteps = steps.map((s) => validateStep(s)).filter((s) => s !== null);
|
|
2296
|
+
return {
|
|
2297
|
+
valid: warnings.length === 0,
|
|
2298
|
+
steps: validSteps,
|
|
2299
|
+
warnings
|
|
2300
|
+
};
|
|
2301
|
+
}
|
|
2302
|
+
|
|
2303
|
+
// src/composites/pipeline-builder.tsx
|
|
2304
|
+
import { jsx as jsx42, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
2305
|
+
function buildSchemaMap(schema) {
|
|
2306
|
+
const map = /* @__PURE__ */ new Map();
|
|
2307
|
+
for (const slot of schema.slots) {
|
|
2308
|
+
for (const addon of slot.addons) {
|
|
2309
|
+
map.set(addon.id, addon);
|
|
2310
|
+
}
|
|
2311
|
+
}
|
|
2312
|
+
return map;
|
|
2313
|
+
}
|
|
2314
|
+
function createDefaultStep(addon, fallbackRuntime, fallbackBackend) {
|
|
2315
|
+
return {
|
|
2316
|
+
addonId: addon.id,
|
|
2317
|
+
addonName: addon.name,
|
|
2318
|
+
slot: addon.slot,
|
|
2319
|
+
inputClasses: [...addon.inputClasses],
|
|
2320
|
+
outputClasses: [...addon.outputClasses],
|
|
2321
|
+
enabled: true,
|
|
2322
|
+
agentId: "hub",
|
|
2323
|
+
// Use per-addon defaults from backend PlatformScorer, fallback to provided
|
|
2324
|
+
runtime: addon.defaultRuntime ?? fallbackRuntime ?? "node",
|
|
2325
|
+
backend: addon.defaultBackend ?? fallbackBackend ?? "cpu",
|
|
2326
|
+
modelId: addon.defaultModelId,
|
|
2327
|
+
confidence: addon.defaultConfidence,
|
|
2328
|
+
classFilters: [...addon.inputClasses],
|
|
2329
|
+
children: []
|
|
2330
|
+
};
|
|
2331
|
+
}
|
|
2332
|
+
function PlaceholderStep({ addon, onClick }) {
|
|
2333
|
+
return /* @__PURE__ */ jsx42(
|
|
2334
|
+
"button",
|
|
2335
|
+
{
|
|
2336
|
+
type: "button",
|
|
2337
|
+
onClick,
|
|
2338
|
+
className: "w-full rounded-xl border-2 border-dashed border-border/60 px-4 py-3 text-left transition-all hover:border-primary/30 hover:bg-surface/60 group",
|
|
2339
|
+
children: /* @__PURE__ */ jsxs25("div", { className: "flex items-center gap-3", children: [
|
|
2340
|
+
/* @__PURE__ */ jsx42(PlusCircle, { className: "h-[18px] w-[18px] text-foreground-subtle/30 group-hover:text-primary/60 shrink-0" }),
|
|
2341
|
+
/* @__PURE__ */ jsxs25("div", { className: "flex-1 min-w-0", children: [
|
|
2342
|
+
/* @__PURE__ */ jsx42("span", { className: "text-[13px] font-medium text-foreground-subtle/50 group-hover:text-foreground-subtle block truncate", children: addon.name }),
|
|
2343
|
+
/* @__PURE__ */ jsxs25("div", { className: "flex items-center gap-1 mt-0.5 flex-wrap", children: [
|
|
2344
|
+
addon.inputClasses.map((c) => /* @__PURE__ */ jsx42("span", { className: "text-[9px] uppercase font-semibold tracking-wide px-1.5 py-0.5 rounded bg-blue-500/8 text-blue-400/50", children: c }, c)),
|
|
2345
|
+
addon.inputClasses.length > 0 && addon.outputClasses.length > 0 && /* @__PURE__ */ jsx42("span", { className: "text-foreground-subtle/25 text-[10px]", children: "\u2192" }),
|
|
2346
|
+
addon.outputClasses.map((c) => /* @__PURE__ */ jsx42("span", { className: "text-[9px] uppercase font-semibold tracking-wide px-1.5 py-0.5 rounded bg-green-500/8 text-green-400/50", children: c }, c))
|
|
2347
|
+
] })
|
|
2348
|
+
] })
|
|
2349
|
+
] })
|
|
2350
|
+
}
|
|
2351
|
+
);
|
|
2352
|
+
}
|
|
2353
|
+
function PipelineBuilder({
|
|
2354
|
+
schema,
|
|
2355
|
+
capabilities,
|
|
2356
|
+
steps,
|
|
2357
|
+
onChange,
|
|
2358
|
+
templates,
|
|
2359
|
+
selectedTemplateId,
|
|
2360
|
+
onSelectTemplate,
|
|
2361
|
+
onSaveTemplate,
|
|
2362
|
+
onUpdateTemplate,
|
|
2363
|
+
onDeleteTemplate,
|
|
2364
|
+
readOnly = false,
|
|
2365
|
+
excludeAddons = []
|
|
2366
|
+
}) {
|
|
2367
|
+
const excluded = useMemo3(() => new Set(excludeAddons), [excludeAddons]);
|
|
2368
|
+
const schemaMap = useMemo3(() => buildSchemaMap(schema), [schema]);
|
|
2369
|
+
const [warnings, setWarnings] = useState10([]);
|
|
2370
|
+
const bestPlatformScore = capabilities.platformScores?.find((s) => s.available);
|
|
2371
|
+
const hasPython = capabilities.runtimes.python.available && capabilities.runtimes.python.backends.some((b) => b.available);
|
|
2372
|
+
const defaultRuntime = bestPlatformScore?.runtime ?? (hasPython ? "python" : "node");
|
|
2373
|
+
const defaultBackend = bestPlatformScore?.backend ?? (hasPython ? capabilities.runtimes.python.backends.find((b) => b.available)?.id ?? "cpu" : capabilities.runtimes.node.backends.find((b) => b.available && b.id !== "cpu")?.id ?? "cpu");
|
|
2374
|
+
const dirty = selectedTemplateId ? JSON.stringify(steps) !== JSON.stringify(templates.find((t) => t.id === selectedTemplateId)?.steps) : false;
|
|
2375
|
+
function handleSelectTemplate(e) {
|
|
2376
|
+
const id = e.target.value || null;
|
|
2377
|
+
if (id) {
|
|
2378
|
+
const tpl = templates.find((t) => t.id === id);
|
|
2379
|
+
if (tpl) {
|
|
2380
|
+
const result = validateTemplate(tpl.steps, schema);
|
|
2381
|
+
setWarnings([...result.warnings]);
|
|
2382
|
+
}
|
|
2383
|
+
} else {
|
|
2384
|
+
setWarnings([]);
|
|
2385
|
+
}
|
|
2386
|
+
onSelectTemplate(id);
|
|
2387
|
+
}
|
|
2388
|
+
function handleSave() {
|
|
2389
|
+
if (selectedTemplateId) onUpdateTemplate(selectedTemplateId, steps);
|
|
2390
|
+
}
|
|
2391
|
+
function handleSaveAs() {
|
|
2392
|
+
const name = window.prompt("Template name:");
|
|
2393
|
+
if (name?.trim()) onSaveTemplate(name.trim(), steps);
|
|
2394
|
+
}
|
|
2395
|
+
function handleDelete() {
|
|
2396
|
+
if (!selectedTemplateId) return;
|
|
2397
|
+
const tpl = templates.find((t) => t.id === selectedTemplateId);
|
|
2398
|
+
if (tpl && window.confirm(`Delete template "${tpl.name}"?`)) {
|
|
2399
|
+
onDeleteTemplate(selectedTemplateId);
|
|
2400
|
+
}
|
|
2401
|
+
}
|
|
2402
|
+
function handleStepChange(updated) {
|
|
2403
|
+
onChange(steps.map((s) => {
|
|
2404
|
+
if (s.addonId !== updated.addonId) return s;
|
|
2405
|
+
return autoEnableAncestors(updated);
|
|
2406
|
+
}));
|
|
2407
|
+
}
|
|
2408
|
+
function autoEnableAncestors(step) {
|
|
2409
|
+
const hasEnabledChild = step.children.some((c) => c.enabled || c.children.some((gc) => gc.enabled));
|
|
2410
|
+
return {
|
|
2411
|
+
...step,
|
|
2412
|
+
enabled: step.enabled || hasEnabledChild,
|
|
2413
|
+
children: step.children.map((c) => {
|
|
2414
|
+
const hasEnabledGrandchild = c.children.some((gc) => gc.enabled);
|
|
2415
|
+
return {
|
|
2416
|
+
...c,
|
|
2417
|
+
enabled: c.enabled || hasEnabledGrandchild
|
|
2418
|
+
};
|
|
2419
|
+
})
|
|
2420
|
+
};
|
|
2421
|
+
}
|
|
2422
|
+
function handleAddChildToStep(parentAddonId, addon) {
|
|
2423
|
+
const child = createDefaultStep(addon, defaultRuntime, defaultBackend);
|
|
2424
|
+
onChange(steps.map((s) => {
|
|
2425
|
+
if (s.addonId !== parentAddonId) return s;
|
|
2426
|
+
return { ...s, children: [...s.children, child] };
|
|
2427
|
+
}));
|
|
2428
|
+
}
|
|
2429
|
+
function collectIds(list) {
|
|
2430
|
+
const ids = /* @__PURE__ */ new Set();
|
|
2431
|
+
for (const s of list) {
|
|
2432
|
+
ids.add(s.addonId);
|
|
2433
|
+
for (const c of s.children) ids.add(c.addonId);
|
|
2434
|
+
const childIds = collectIds(s.children);
|
|
2435
|
+
for (const id of childIds) ids.add(id);
|
|
2436
|
+
}
|
|
2437
|
+
return ids;
|
|
2438
|
+
}
|
|
2439
|
+
const existingIds = collectIds(steps);
|
|
2440
|
+
function getChildPlaceholders(step) {
|
|
2441
|
+
const stepSchema = schemaMap.get(step.addonId);
|
|
2442
|
+
if (!stepSchema) return [];
|
|
2443
|
+
const childSlotIds = stepSchema.childSlots;
|
|
2444
|
+
const childIds = new Set(step.children.map((c) => c.addonId));
|
|
2445
|
+
const placeholders = [];
|
|
2446
|
+
for (const slot of schema.slots) {
|
|
2447
|
+
if (!childSlotIds.includes(slot.id)) continue;
|
|
2448
|
+
for (const addon of slot.addons) {
|
|
2449
|
+
if (childIds.has(addon.id) || excluded.has(addon.id)) continue;
|
|
2450
|
+
const compatible = addon.inputClasses.some((ic) => step.outputClasses.includes(ic));
|
|
2451
|
+
if (compatible) placeholders.push(addon);
|
|
2452
|
+
}
|
|
2453
|
+
}
|
|
2454
|
+
return placeholders;
|
|
2455
|
+
}
|
|
2456
|
+
function renderStep(step) {
|
|
2457
|
+
const childPlaceholders = getChildPlaceholders(step);
|
|
2458
|
+
return /* @__PURE__ */ jsxs25("div", { className: "space-y-1.5", children: [
|
|
2459
|
+
/* @__PURE__ */ jsx42(
|
|
2460
|
+
PipelineStep,
|
|
2461
|
+
{
|
|
2462
|
+
step,
|
|
2463
|
+
schema: schemaMap.get(step.addonId) ?? null,
|
|
2464
|
+
allSchemas: schemaMap,
|
|
2465
|
+
capabilities,
|
|
2466
|
+
onChange: handleStepChange,
|
|
2467
|
+
onDelete: readOnly ? void 0 : (id) => onChange(steps.filter((s) => s.addonId !== id)),
|
|
2468
|
+
readOnly
|
|
2469
|
+
}
|
|
2470
|
+
),
|
|
2471
|
+
(step.children.length > 0 || childPlaceholders.length > 0) && /* @__PURE__ */ jsxs25("div", { className: "ml-6 pl-4 border-l-2 border-dashed border-border/40 space-y-1.5", children: [
|
|
2472
|
+
/* @__PURE__ */ jsx42("span", { className: "text-[10px] font-semibold uppercase tracking-widest text-foreground-subtle/40", children: "Slot: Cropper / Classifier" }),
|
|
2473
|
+
step.children.map((child) => {
|
|
2474
|
+
const childChildPlaceholders = getChildPlaceholders(child);
|
|
2475
|
+
return /* @__PURE__ */ jsxs25("div", { className: "space-y-1.5", children: [
|
|
2476
|
+
/* @__PURE__ */ jsx42(
|
|
2477
|
+
PipelineStep,
|
|
2478
|
+
{
|
|
2479
|
+
step: child,
|
|
2480
|
+
schema: schemaMap.get(child.addonId) ?? null,
|
|
2481
|
+
allSchemas: schemaMap,
|
|
2482
|
+
capabilities,
|
|
2483
|
+
depth: 1,
|
|
2484
|
+
onChange: (updated) => {
|
|
2485
|
+
handleStepChange({
|
|
2486
|
+
...step,
|
|
2487
|
+
children: step.children.map((c) => c.addonId === updated.addonId ? updated : c)
|
|
2488
|
+
});
|
|
2489
|
+
},
|
|
2490
|
+
onDelete: readOnly ? void 0 : (id) => {
|
|
2491
|
+
handleStepChange({
|
|
2492
|
+
...step,
|
|
2493
|
+
children: step.children.filter((c) => c.addonId !== id)
|
|
2494
|
+
});
|
|
2495
|
+
},
|
|
2496
|
+
readOnly
|
|
2497
|
+
}
|
|
2498
|
+
),
|
|
2499
|
+
(child.children.length > 0 || childChildPlaceholders.length > 0) && /* @__PURE__ */ jsxs25("div", { className: "ml-6 pl-4 border-l-2 border-dashed border-border/30 space-y-1.5", children: [
|
|
2500
|
+
/* @__PURE__ */ jsx42("span", { className: "text-[10px] font-semibold uppercase tracking-widest text-foreground-subtle/30", children: "Slot: Recognizer" }),
|
|
2501
|
+
child.children.map((grandchild) => /* @__PURE__ */ jsx42(
|
|
2502
|
+
PipelineStep,
|
|
2503
|
+
{
|
|
2504
|
+
step: grandchild,
|
|
2505
|
+
schema: schemaMap.get(grandchild.addonId) ?? null,
|
|
2506
|
+
allSchemas: schemaMap,
|
|
2507
|
+
capabilities,
|
|
2508
|
+
depth: 2,
|
|
2509
|
+
onChange: (updatedGrandchild) => {
|
|
2510
|
+
handleStepChange({
|
|
2511
|
+
...step,
|
|
2512
|
+
children: step.children.map(
|
|
2513
|
+
(c) => c.addonId === child.addonId ? { ...c, children: c.children.map((gc) => gc.addonId === updatedGrandchild.addonId ? updatedGrandchild : gc) } : c
|
|
2514
|
+
)
|
|
2515
|
+
});
|
|
2516
|
+
},
|
|
2517
|
+
onDelete: readOnly ? void 0 : (id) => {
|
|
2518
|
+
handleStepChange({
|
|
2519
|
+
...step,
|
|
2520
|
+
children: step.children.map(
|
|
2521
|
+
(c) => c.addonId === child.addonId ? { ...c, children: c.children.filter((gc) => gc.addonId !== id) } : c
|
|
2522
|
+
)
|
|
2523
|
+
});
|
|
2524
|
+
},
|
|
2525
|
+
readOnly
|
|
2526
|
+
},
|
|
2527
|
+
grandchild.addonId
|
|
2528
|
+
)),
|
|
2529
|
+
!readOnly && childChildPlaceholders.map((addon) => /* @__PURE__ */ jsx42(
|
|
2530
|
+
PlaceholderStep,
|
|
2531
|
+
{
|
|
2532
|
+
addon,
|
|
2533
|
+
onClick: () => {
|
|
2534
|
+
const newChild = createDefaultStep(addon, defaultRuntime, defaultBackend);
|
|
2535
|
+
handleStepChange({
|
|
2536
|
+
...step,
|
|
2537
|
+
children: step.children.map(
|
|
2538
|
+
(c) => c.addonId === child.addonId ? { ...c, children: [...c.children, newChild] } : c
|
|
2539
|
+
)
|
|
2540
|
+
});
|
|
2541
|
+
}
|
|
2542
|
+
},
|
|
2543
|
+
addon.id
|
|
2544
|
+
))
|
|
2545
|
+
] })
|
|
2546
|
+
] }, child.addonId);
|
|
2547
|
+
}),
|
|
2548
|
+
!readOnly && childPlaceholders.map((addon) => /* @__PURE__ */ jsx42(
|
|
2549
|
+
PlaceholderStep,
|
|
2550
|
+
{
|
|
2551
|
+
addon,
|
|
2552
|
+
onClick: () => handleAddChildToStep(step.addonId, addon)
|
|
2553
|
+
},
|
|
2554
|
+
addon.id
|
|
2555
|
+
))
|
|
2556
|
+
] })
|
|
2557
|
+
] }, step.addonId);
|
|
2558
|
+
}
|
|
2559
|
+
const rootSlots = schema.slots.filter((s) => s.parentSlot === null).sort((a, b) => a.priority - b.priority);
|
|
2560
|
+
return /* @__PURE__ */ jsxs25("div", { className: "space-y-4", children: [
|
|
2561
|
+
/* @__PURE__ */ jsx42("div", { className: "rounded-xl border border-border bg-surface p-3", children: /* @__PURE__ */ jsxs25("div", { className: "flex items-center gap-2", children: [
|
|
2562
|
+
/* @__PURE__ */ jsx42("div", { className: "relative flex-1 min-w-0", children: /* @__PURE__ */ jsxs25(
|
|
2563
|
+
"select",
|
|
2564
|
+
{
|
|
2565
|
+
value: selectedTemplateId ?? "",
|
|
2566
|
+
onChange: handleSelectTemplate,
|
|
2567
|
+
className: "w-full rounded-lg border border-border bg-background px-3 py-2 text-sm text-foreground focus:outline-none focus:border-primary/50",
|
|
2568
|
+
children: [
|
|
2569
|
+
/* @__PURE__ */ jsx42("option", { value: "", children: "No template" }),
|
|
2570
|
+
templates.map((t) => /* @__PURE__ */ jsx42("option", { value: t.id, children: t.name }, t.id))
|
|
2571
|
+
]
|
|
2572
|
+
}
|
|
2573
|
+
) }),
|
|
2574
|
+
dirty && /* @__PURE__ */ jsx42("span", { className: "h-1.5 w-1.5 rounded-full bg-amber-500 shrink-0" }),
|
|
2575
|
+
/* @__PURE__ */ jsx42(
|
|
2576
|
+
"button",
|
|
2577
|
+
{
|
|
2578
|
+
onClick: handleSave,
|
|
2579
|
+
disabled: !selectedTemplateId || readOnly,
|
|
2580
|
+
title: "Save",
|
|
2581
|
+
className: cn(
|
|
2582
|
+
"p-2 rounded-lg border border-border transition-colors",
|
|
2583
|
+
selectedTemplateId && !readOnly ? "text-foreground-subtle hover:bg-surface-hover" : "text-foreground-subtle/30 cursor-not-allowed"
|
|
2584
|
+
),
|
|
2585
|
+
children: /* @__PURE__ */ jsx42(Save, { className: "h-4 w-4" })
|
|
2586
|
+
}
|
|
2587
|
+
),
|
|
2588
|
+
/* @__PURE__ */ jsx42(
|
|
2589
|
+
"button",
|
|
2590
|
+
{
|
|
2591
|
+
onClick: handleSaveAs,
|
|
2592
|
+
disabled: readOnly,
|
|
2593
|
+
title: "Save As",
|
|
2594
|
+
className: cn(
|
|
2595
|
+
"p-2 rounded-lg border border-border transition-colors",
|
|
2596
|
+
!readOnly ? "text-foreground-subtle hover:bg-surface-hover" : "text-foreground-subtle/30 cursor-not-allowed"
|
|
2597
|
+
),
|
|
2598
|
+
children: /* @__PURE__ */ jsx42(CopyPlus, { className: "h-4 w-4" })
|
|
2599
|
+
}
|
|
2600
|
+
),
|
|
2601
|
+
/* @__PURE__ */ jsx42(
|
|
2602
|
+
"button",
|
|
2603
|
+
{
|
|
2604
|
+
onClick: handleDelete,
|
|
2605
|
+
disabled: !selectedTemplateId || readOnly,
|
|
2606
|
+
title: "Delete",
|
|
2607
|
+
className: cn(
|
|
2608
|
+
"p-2 rounded-lg border border-border transition-colors",
|
|
2609
|
+
selectedTemplateId && !readOnly ? "text-foreground-subtle hover:text-danger" : "text-foreground-subtle/30 cursor-not-allowed"
|
|
2610
|
+
),
|
|
2611
|
+
children: /* @__PURE__ */ jsx42(Trash2, { className: "h-4 w-4" })
|
|
2612
|
+
}
|
|
2613
|
+
)
|
|
2614
|
+
] }) }),
|
|
2615
|
+
warnings.length > 0 && /* @__PURE__ */ jsxs25("div", { className: "rounded-lg border border-amber-500/30 bg-amber-500/5 p-3 text-xs text-amber-400 space-y-1", children: [
|
|
2616
|
+
/* @__PURE__ */ jsxs25("div", { className: "flex items-center justify-between", children: [
|
|
2617
|
+
/* @__PURE__ */ jsx42("span", { className: "font-medium", children: "Template loaded with warnings:" }),
|
|
2618
|
+
/* @__PURE__ */ jsx42("button", { onClick: () => setWarnings([]), className: "text-amber-400/60 hover:text-amber-400", children: /* @__PURE__ */ jsx42(X2, { className: "h-3.5 w-3.5" }) })
|
|
2619
|
+
] }),
|
|
2620
|
+
warnings.map((w, i) => /* @__PURE__ */ jsxs25("div", { children: [
|
|
2621
|
+
"\u2022 ",
|
|
2622
|
+
w
|
|
2623
|
+
] }, i))
|
|
2624
|
+
] }),
|
|
2625
|
+
rootSlots.map((slot) => {
|
|
2626
|
+
const slotSteps = steps.filter((s) => s.slot === slot.id && !excluded.has(s.addonId));
|
|
2627
|
+
const missingRootAddons = slot.addons.filter((a) => !existingIds.has(a.id) && !excluded.has(a.id));
|
|
2628
|
+
return /* @__PURE__ */ jsxs25("div", { className: "space-y-2", children: [
|
|
2629
|
+
/* @__PURE__ */ jsxs25("span", { className: "text-[10px] font-semibold uppercase tracking-widest text-foreground-subtle/50", children: [
|
|
2630
|
+
"Slot: ",
|
|
2631
|
+
slot.label
|
|
2632
|
+
] }),
|
|
2633
|
+
slotSteps.map((step) => renderStep(step)),
|
|
2634
|
+
!readOnly && missingRootAddons.map((addon) => /* @__PURE__ */ jsx42(PlaceholderStep, { addon, onClick: () => {
|
|
2635
|
+
onChange([...steps, createDefaultStep(addon, defaultRuntime, defaultBackend)]);
|
|
2636
|
+
} }, addon.id))
|
|
2637
|
+
] }, slot.id);
|
|
2638
|
+
})
|
|
2639
|
+
] });
|
|
2640
|
+
}
|
|
2641
|
+
|
|
2642
|
+
// src/composites/detection-colors.ts
|
|
2643
|
+
var CLASS_COLORS = {
|
|
2644
|
+
// Primary detection classes
|
|
2645
|
+
person: "#3b82f6",
|
|
2646
|
+
// blue-500
|
|
2647
|
+
vehicle: "#f59e0b",
|
|
2648
|
+
// amber-500
|
|
2649
|
+
animal: "#22c55e",
|
|
2650
|
+
// green-500
|
|
2651
|
+
// Sub-detection classes
|
|
2652
|
+
face: "#a855f7",
|
|
2653
|
+
// purple-500
|
|
2654
|
+
plate: "#ec4899",
|
|
2655
|
+
// pink-500
|
|
2656
|
+
// Specific animal types
|
|
2657
|
+
bird: "#14b8a6",
|
|
2658
|
+
// teal-500
|
|
2659
|
+
dog: "#84cc16",
|
|
2660
|
+
// lime-500
|
|
2661
|
+
cat: "#f97316",
|
|
2662
|
+
// orange-500
|
|
2663
|
+
// Specific vehicle types
|
|
2664
|
+
car: "#f59e0b",
|
|
2665
|
+
// amber-500
|
|
2666
|
+
truck: "#d97706",
|
|
2667
|
+
// amber-600
|
|
2668
|
+
bus: "#b45309",
|
|
2669
|
+
// amber-700
|
|
2670
|
+
motorcycle: "#eab308",
|
|
2671
|
+
// yellow-500
|
|
2672
|
+
bicycle: "#ca8a04",
|
|
2673
|
+
// yellow-600
|
|
2674
|
+
// Other
|
|
2675
|
+
motion: "#facc15"
|
|
2676
|
+
// yellow-400
|
|
2677
|
+
};
|
|
2678
|
+
var FALLBACK_PALETTE = [
|
|
2679
|
+
"#ef4444",
|
|
2680
|
+
// red-500
|
|
2681
|
+
"#8b5cf6",
|
|
2682
|
+
// violet-500
|
|
2683
|
+
"#06b6d4",
|
|
2684
|
+
// cyan-500
|
|
2685
|
+
"#f97316",
|
|
2686
|
+
// orange-500
|
|
2687
|
+
"#10b981",
|
|
2688
|
+
// emerald-500
|
|
2689
|
+
"#6366f1",
|
|
2690
|
+
// indigo-500
|
|
2691
|
+
"#e11d48",
|
|
2692
|
+
// rose-600
|
|
2693
|
+
"#0891b2",
|
|
2694
|
+
// cyan-600
|
|
2695
|
+
"#7c3aed",
|
|
2696
|
+
// violet-600
|
|
2697
|
+
"#059669"
|
|
2698
|
+
// emerald-600
|
|
2699
|
+
];
|
|
2700
|
+
var DEFAULT_COLOR = "#f59e42";
|
|
2701
|
+
function getClassColor(className, customColors) {
|
|
2702
|
+
if (customColors?.[className]) return customColors[className];
|
|
2703
|
+
if (customColors?.[className.toLowerCase()]) return customColors[className.toLowerCase()];
|
|
2704
|
+
if (CLASS_COLORS[className]) return CLASS_COLORS[className];
|
|
2705
|
+
if (CLASS_COLORS[className.toLowerCase()]) return CLASS_COLORS[className.toLowerCase()];
|
|
2706
|
+
let hash = 0;
|
|
2707
|
+
for (let i = 0; i < className.length; i++) {
|
|
2708
|
+
hash = hash * 31 + (className.codePointAt(i) ?? 0) >>> 0;
|
|
2709
|
+
}
|
|
2710
|
+
return FALLBACK_PALETTE[hash % FALLBACK_PALETTE.length] ?? DEFAULT_COLOR;
|
|
2711
|
+
}
|
|
2712
|
+
|
|
2713
|
+
// src/composites/detection-canvas.tsx
|
|
2714
|
+
import { useRef as useRef6, useEffect as useEffect6 } from "react";
|
|
2715
|
+
import { Fragment as Fragment2, jsx as jsx43, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
2716
|
+
var DEFAULT_CLASS_COLORS = CLASS_COLORS;
|
|
2717
|
+
function DetectionCanvas({
|
|
2718
|
+
src,
|
|
2719
|
+
imageWidth,
|
|
2720
|
+
imageHeight,
|
|
2721
|
+
detections = [],
|
|
2722
|
+
classColors,
|
|
2723
|
+
aspectRatio,
|
|
2724
|
+
className,
|
|
2725
|
+
placeholder,
|
|
2726
|
+
showConfidence = true,
|
|
2727
|
+
minConfidence = 0,
|
|
2728
|
+
borderWidth = 2
|
|
2729
|
+
}) {
|
|
2730
|
+
function getColor(className2) {
|
|
2731
|
+
return getClassColor(className2, classColors);
|
|
2732
|
+
}
|
|
2733
|
+
const ratio = aspectRatio ?? (imageWidth && imageHeight ? `${imageWidth}/${imageHeight}` : "16/9");
|
|
2734
|
+
const filteredDetections = detections.filter((d) => d.confidence >= minConfidence);
|
|
2735
|
+
return /* @__PURE__ */ jsx43(
|
|
2736
|
+
"div",
|
|
2737
|
+
{
|
|
2738
|
+
className: cn(
|
|
2739
|
+
"rounded-lg border border-border bg-surface overflow-hidden relative",
|
|
2740
|
+
className
|
|
2741
|
+
),
|
|
2742
|
+
style: { aspectRatio: ratio },
|
|
2743
|
+
children: src ? /* @__PURE__ */ jsxs26(Fragment2, { children: [
|
|
2744
|
+
/* @__PURE__ */ jsx43("img", { src, className: "absolute inset-0 w-full h-full object-fill", alt: "" }),
|
|
2745
|
+
filteredDetections.map(
|
|
2746
|
+
(d, i) => d.mask && d.maskWidth && d.maskHeight ? /* @__PURE__ */ jsx43(
|
|
2747
|
+
MaskOverlay,
|
|
2748
|
+
{
|
|
2749
|
+
mask: d.mask,
|
|
2750
|
+
maskWidth: d.maskWidth,
|
|
2751
|
+
maskHeight: d.maskHeight,
|
|
2752
|
+
bbox: d.bbox,
|
|
2753
|
+
imageWidth,
|
|
2754
|
+
imageHeight,
|
|
2755
|
+
color: getColor(d.className)
|
|
2756
|
+
},
|
|
2757
|
+
`mask-${i}`
|
|
2758
|
+
) : null
|
|
2759
|
+
),
|
|
2760
|
+
filteredDetections.map((d, i) => /* @__PURE__ */ jsx43(
|
|
2761
|
+
BoundingBox,
|
|
2762
|
+
{
|
|
2763
|
+
detection: d,
|
|
2764
|
+
imageWidth,
|
|
2765
|
+
imageHeight,
|
|
2766
|
+
color: getColor(d.className),
|
|
2767
|
+
showConfidence,
|
|
2768
|
+
borderWidth: d.mask ? 1 : borderWidth,
|
|
2769
|
+
children: d.children?.filter((c) => {
|
|
2770
|
+
if (c.confidence < minConfidence) return false;
|
|
2771
|
+
const [cx1, cy1, cx2, cy2] = c.bbox;
|
|
2772
|
+
const cw = cx2 - cx1;
|
|
2773
|
+
const ch = cy2 - cy1;
|
|
2774
|
+
if (cw <= 0 || ch <= 0) return false;
|
|
2775
|
+
const [px1, py1, px2, py2] = d.bbox;
|
|
2776
|
+
const pw = px2 - px1;
|
|
2777
|
+
const ph = py2 - py1;
|
|
2778
|
+
if (pw > 0 && ph > 0 && cw * ch / (pw * ph) > 0.8) return false;
|
|
2779
|
+
return true;
|
|
2780
|
+
}).map((child, j) => /* @__PURE__ */ jsx43(
|
|
2781
|
+
ChildBoundingBox,
|
|
2782
|
+
{
|
|
2783
|
+
child,
|
|
2784
|
+
parentBbox: d.bbox,
|
|
2785
|
+
color: getColor(child.className),
|
|
2786
|
+
showConfidence
|
|
2787
|
+
},
|
|
2788
|
+
`child-${j}`
|
|
2789
|
+
))
|
|
2790
|
+
},
|
|
2791
|
+
`det-${i}`
|
|
2792
|
+
))
|
|
2793
|
+
] }) : /* @__PURE__ */ jsx43("div", { className: "w-full h-full flex items-center justify-center text-foreground-subtle text-sm", children: placeholder ?? "No image loaded" })
|
|
2794
|
+
}
|
|
2795
|
+
);
|
|
2796
|
+
}
|
|
2797
|
+
function BoundingBox({
|
|
2798
|
+
detection,
|
|
2799
|
+
imageWidth,
|
|
2800
|
+
imageHeight,
|
|
2801
|
+
color,
|
|
2802
|
+
showConfidence,
|
|
2803
|
+
borderWidth,
|
|
2804
|
+
children
|
|
2805
|
+
}) {
|
|
2806
|
+
const [x1, y1, x2, y2] = detection.bbox;
|
|
2807
|
+
const labelCount = 1 + (detection.labelsData?.length ?? 0);
|
|
2808
|
+
const labelHeightPx = labelCount * 16 + 4;
|
|
2809
|
+
const topPct = y1 / imageHeight * 100;
|
|
2810
|
+
const containerRef = useRef6(null);
|
|
2811
|
+
const showBelow = topPct < labelHeightPx / imageHeight * 100 * 1.5;
|
|
2812
|
+
const labelsElement = /* @__PURE__ */ jsxs26(
|
|
2813
|
+
"div",
|
|
2814
|
+
{
|
|
2815
|
+
className: `absolute left-0 flex flex-col items-start gap-px ${showBelow ? "" : ""}`,
|
|
2816
|
+
style: showBelow ? { top: "100%", marginTop: "2px" } : { bottom: "100%", marginBottom: "2px" },
|
|
2817
|
+
children: [
|
|
2818
|
+
/* @__PURE__ */ jsxs26(
|
|
2819
|
+
"span",
|
|
2820
|
+
{
|
|
2821
|
+
className: "text-[10px] px-1 rounded-sm whitespace-nowrap text-white",
|
|
2822
|
+
style: { backgroundColor: color },
|
|
2823
|
+
children: [
|
|
2824
|
+
detection.className,
|
|
2825
|
+
showConfidence && ` ${(detection.confidence * 100).toFixed(0)}%`
|
|
2826
|
+
]
|
|
2827
|
+
}
|
|
2828
|
+
),
|
|
2829
|
+
detection.labelsData?.map((l, k) => /* @__PURE__ */ jsxs26(
|
|
2830
|
+
"span",
|
|
2831
|
+
{
|
|
2832
|
+
className: "text-[9px] font-semibold px-1 rounded-sm whitespace-nowrap text-white",
|
|
2833
|
+
style: { backgroundColor: getClassColor(l.addonId ?? l.label) },
|
|
2834
|
+
children: [
|
|
2835
|
+
l.label,
|
|
2836
|
+
" ",
|
|
2837
|
+
(l.score * 100).toFixed(0),
|
|
2838
|
+
"%"
|
|
2839
|
+
]
|
|
2840
|
+
},
|
|
2841
|
+
k
|
|
2842
|
+
))
|
|
2843
|
+
]
|
|
2844
|
+
}
|
|
2845
|
+
);
|
|
2846
|
+
return /* @__PURE__ */ jsxs26(
|
|
2847
|
+
"div",
|
|
2848
|
+
{
|
|
2849
|
+
ref: containerRef,
|
|
2850
|
+
className: "absolute rounded-sm",
|
|
2851
|
+
style: {
|
|
2852
|
+
left: `${x1 / imageWidth * 100}%`,
|
|
2853
|
+
top: `${y1 / imageHeight * 100}%`,
|
|
2854
|
+
width: `${(x2 - x1) / imageWidth * 100}%`,
|
|
2855
|
+
height: `${(y2 - y1) / imageHeight * 100}%`,
|
|
2856
|
+
borderWidth: `${borderWidth}px`,
|
|
2857
|
+
borderStyle: "solid",
|
|
2858
|
+
borderColor: color
|
|
2859
|
+
},
|
|
2860
|
+
children: [
|
|
2861
|
+
labelsElement,
|
|
2862
|
+
children
|
|
2863
|
+
]
|
|
2864
|
+
}
|
|
2865
|
+
);
|
|
2866
|
+
}
|
|
2867
|
+
function MaskOverlay({
|
|
2868
|
+
mask,
|
|
2869
|
+
maskWidth,
|
|
2870
|
+
maskHeight,
|
|
2871
|
+
bbox,
|
|
2872
|
+
imageWidth,
|
|
2873
|
+
imageHeight,
|
|
2874
|
+
color
|
|
2875
|
+
}) {
|
|
2876
|
+
const canvasRef = useRef6(null);
|
|
2877
|
+
useEffect6(() => {
|
|
2878
|
+
const canvas = canvasRef.current;
|
|
2879
|
+
if (!canvas) return;
|
|
2880
|
+
const ctx = canvas.getContext("2d");
|
|
2881
|
+
if (!ctx) return;
|
|
2882
|
+
const binary = atob(mask);
|
|
2883
|
+
const bytes = new Uint8Array(binary.length);
|
|
2884
|
+
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
2885
|
+
const r = parseInt(color.slice(1, 3), 16);
|
|
2886
|
+
const g = parseInt(color.slice(3, 5), 16);
|
|
2887
|
+
const b = parseInt(color.slice(5, 7), 16);
|
|
2888
|
+
canvas.width = maskWidth;
|
|
2889
|
+
canvas.height = maskHeight;
|
|
2890
|
+
const imageData = ctx.createImageData(maskWidth, maskHeight);
|
|
2891
|
+
const totalPixels = maskWidth * maskHeight;
|
|
2892
|
+
for (let i = 0; i < totalPixels; i++) {
|
|
2893
|
+
const val = i < bytes.length ? bytes[i] : 0;
|
|
2894
|
+
const px = i * 4;
|
|
2895
|
+
if (val > 0) {
|
|
2896
|
+
imageData.data[px] = r;
|
|
2897
|
+
imageData.data[px + 1] = g;
|
|
2898
|
+
imageData.data[px + 2] = b;
|
|
2899
|
+
imageData.data[px + 3] = 120;
|
|
2900
|
+
}
|
|
2901
|
+
}
|
|
2902
|
+
ctx.putImageData(imageData, 0, 0);
|
|
2903
|
+
}, [mask, maskWidth, maskHeight, color]);
|
|
2904
|
+
const [x1, y1, x2, y2] = bbox;
|
|
2905
|
+
return /* @__PURE__ */ jsx43(
|
|
2906
|
+
"canvas",
|
|
2907
|
+
{
|
|
2908
|
+
ref: canvasRef,
|
|
2909
|
+
className: "absolute pointer-events-none",
|
|
2910
|
+
style: {
|
|
2911
|
+
left: `${x1 / imageWidth * 100}%`,
|
|
2912
|
+
top: `${y1 / imageHeight * 100}%`,
|
|
2913
|
+
width: `${(x2 - x1) / imageWidth * 100}%`,
|
|
2914
|
+
height: `${(y2 - y1) / imageHeight * 100}%`,
|
|
2915
|
+
imageRendering: "pixelated"
|
|
2916
|
+
}
|
|
2917
|
+
}
|
|
2918
|
+
);
|
|
2919
|
+
}
|
|
2920
|
+
function ChildBoundingBox({
|
|
2921
|
+
child,
|
|
2922
|
+
parentBbox,
|
|
2923
|
+
color,
|
|
2924
|
+
showConfidence
|
|
2925
|
+
}) {
|
|
2926
|
+
const [px1, py1, px2, py2] = parentBbox;
|
|
2927
|
+
const [cx1, cy1, cx2, cy2] = child.bbox;
|
|
2928
|
+
const pw = px2 - px1;
|
|
2929
|
+
const ph = py2 - py1;
|
|
2930
|
+
if (pw <= 0 || ph <= 0) return null;
|
|
2931
|
+
return /* @__PURE__ */ jsx43(
|
|
2932
|
+
"div",
|
|
2933
|
+
{
|
|
2934
|
+
className: "absolute rounded-sm",
|
|
2935
|
+
style: {
|
|
2936
|
+
left: `${Math.max(0, (cx1 - px1) / pw * 100)}%`,
|
|
2937
|
+
top: `${Math.max(0, (cy1 - py1) / ph * 100)}%`,
|
|
2938
|
+
width: `${Math.min(100, (cx2 - cx1) / pw * 100)}%`,
|
|
2939
|
+
height: `${Math.min(100, (cy2 - cy1) / ph * 100)}%`,
|
|
2940
|
+
borderWidth: "1px",
|
|
2941
|
+
borderStyle: "solid",
|
|
2942
|
+
borderColor: color
|
|
2943
|
+
},
|
|
2944
|
+
children: (() => {
|
|
2945
|
+
const labelCount = 1 + (child.labelsData?.length ?? 0);
|
|
2946
|
+
const relTop = (cy1 - py1) / ph * 100;
|
|
2947
|
+
const showBelow = relTop < labelCount * 6;
|
|
2948
|
+
return /* @__PURE__ */ jsxs26(
|
|
2949
|
+
"div",
|
|
2950
|
+
{
|
|
2951
|
+
className: "absolute left-0 flex flex-col items-start gap-px",
|
|
2952
|
+
style: showBelow ? { top: "100%", marginTop: "1px" } : { bottom: "100%", marginBottom: "1px" },
|
|
2953
|
+
children: [
|
|
2954
|
+
/* @__PURE__ */ jsxs26(
|
|
2955
|
+
"span",
|
|
2956
|
+
{
|
|
2957
|
+
className: "text-[9px] px-0.5 rounded-sm whitespace-nowrap text-white",
|
|
2958
|
+
style: { backgroundColor: color },
|
|
2959
|
+
children: [
|
|
2960
|
+
child.className,
|
|
2961
|
+
showConfidence && ` ${(child.confidence * 100).toFixed(0)}%`
|
|
2962
|
+
]
|
|
2963
|
+
}
|
|
2964
|
+
),
|
|
2965
|
+
child.labelsData?.map((l, k) => /* @__PURE__ */ jsxs26(
|
|
2966
|
+
"span",
|
|
2967
|
+
{
|
|
2968
|
+
className: "text-[8px] font-semibold px-0.5 rounded-sm whitespace-nowrap text-white",
|
|
2969
|
+
style: { backgroundColor: getClassColor(l.addonId ?? l.label) },
|
|
2970
|
+
children: [
|
|
2971
|
+
l.label,
|
|
2972
|
+
" ",
|
|
2973
|
+
(l.score * 100).toFixed(0),
|
|
2974
|
+
"%"
|
|
2975
|
+
]
|
|
2976
|
+
},
|
|
2977
|
+
k
|
|
2978
|
+
))
|
|
2979
|
+
]
|
|
2980
|
+
}
|
|
2981
|
+
);
|
|
2982
|
+
})()
|
|
2983
|
+
}
|
|
2984
|
+
);
|
|
2985
|
+
}
|
|
2986
|
+
|
|
2987
|
+
// src/composites/detection-result-tree.tsx
|
|
2988
|
+
import { jsx as jsx44, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
2989
|
+
function DetectionResultTree({
|
|
2990
|
+
detections,
|
|
2991
|
+
classColors,
|
|
2992
|
+
className,
|
|
2993
|
+
hiddenKeys,
|
|
2994
|
+
onToggleVisibility
|
|
2995
|
+
}) {
|
|
2996
|
+
const colors = classColors;
|
|
2997
|
+
if (detections.length === 0) {
|
|
2998
|
+
return /* @__PURE__ */ jsx44("div", { className: "text-sm text-foreground-subtle italic text-center py-4", children: "No detections" });
|
|
2999
|
+
}
|
|
3000
|
+
return /* @__PURE__ */ jsxs27("div", { className, children: [
|
|
3001
|
+
/* @__PURE__ */ jsxs27("div", { className: "text-xs font-medium text-foreground-subtle uppercase tracking-wide mb-2", children: [
|
|
3002
|
+
"Detections (",
|
|
3003
|
+
detections.length,
|
|
3004
|
+
")"
|
|
3005
|
+
] }),
|
|
3006
|
+
/* @__PURE__ */ jsx44("div", { className: "space-y-2", children: detections.map((d, i) => /* @__PURE__ */ jsx44(
|
|
3007
|
+
DetectionNode,
|
|
3008
|
+
{
|
|
3009
|
+
detection: d,
|
|
3010
|
+
path: String(i),
|
|
3011
|
+
colors,
|
|
3012
|
+
hiddenKeys,
|
|
3013
|
+
onToggleVisibility
|
|
3014
|
+
},
|
|
3015
|
+
i
|
|
3016
|
+
)) })
|
|
3017
|
+
] });
|
|
3018
|
+
}
|
|
3019
|
+
function DetectionNode({
|
|
3020
|
+
detection,
|
|
3021
|
+
path,
|
|
3022
|
+
colors,
|
|
3023
|
+
hiddenKeys,
|
|
3024
|
+
onToggleVisibility
|
|
3025
|
+
}) {
|
|
3026
|
+
const color = getClassColor(detection.className, colors);
|
|
3027
|
+
const isVisible = !hiddenKeys?.has(path);
|
|
3028
|
+
return /* @__PURE__ */ jsxs27("div", { className: `rounded-md border border-border bg-surface p-3 space-y-1 ${isVisible ? "" : "opacity-40"}`, children: [
|
|
3029
|
+
/* @__PURE__ */ jsxs27("div", { className: "flex justify-between items-center", children: [
|
|
3030
|
+
/* @__PURE__ */ jsxs27("div", { className: "flex items-center gap-2", children: [
|
|
3031
|
+
onToggleVisibility && /* @__PURE__ */ jsx44(
|
|
3032
|
+
"input",
|
|
3033
|
+
{
|
|
3034
|
+
type: "checkbox",
|
|
3035
|
+
checked: isVisible,
|
|
3036
|
+
onChange: () => onToggleVisibility(path, !isVisible),
|
|
3037
|
+
className: "h-3.5 w-3.5 rounded border-border accent-primary cursor-pointer shrink-0"
|
|
3038
|
+
}
|
|
3039
|
+
),
|
|
3040
|
+
/* @__PURE__ */ jsx44(
|
|
3041
|
+
"span",
|
|
3042
|
+
{
|
|
3043
|
+
className: "h-2.5 w-2.5 rounded-full shrink-0",
|
|
3044
|
+
style: { backgroundColor: color }
|
|
3045
|
+
}
|
|
3046
|
+
),
|
|
3047
|
+
/* @__PURE__ */ jsx44("span", { className: "text-sm font-medium text-foreground", children: detection.className }),
|
|
3048
|
+
detection.mask && detection.maskWidth && detection.maskHeight && /* @__PURE__ */ jsxs27("span", { className: "text-[9px] font-mono px-1 py-0.5 rounded bg-primary/10 text-primary", children: [
|
|
3049
|
+
"mask ",
|
|
3050
|
+
detection.maskWidth,
|
|
3051
|
+
"x",
|
|
3052
|
+
detection.maskHeight
|
|
3053
|
+
] })
|
|
3054
|
+
] }),
|
|
3055
|
+
/* @__PURE__ */ jsx44(ConfidenceBadge, { confidence: detection.confidence })
|
|
3056
|
+
] }),
|
|
3057
|
+
/* @__PURE__ */ jsxs27("div", { className: "text-[10px] text-foreground-subtle font-mono", children: [
|
|
3058
|
+
"bbox: [",
|
|
3059
|
+
detection.bbox.map((v) => Math.round(v)).join(", "),
|
|
3060
|
+
"]"
|
|
3061
|
+
] }),
|
|
3062
|
+
detection.labelsData && detection.labelsData.length > 0 && /* @__PURE__ */ jsx44("div", { className: "flex flex-wrap gap-1 mt-1", children: detection.labelsData.map((l, k) => /* @__PURE__ */ jsxs27(
|
|
3063
|
+
"span",
|
|
3064
|
+
{
|
|
3065
|
+
className: "inline-flex items-center gap-1 text-[10px] font-medium px-1.5 py-0.5 rounded-full",
|
|
3066
|
+
style: { backgroundColor: getClassColor(l.addonId ?? l.label, colors) + "20", color: getClassColor(l.addonId ?? l.label, colors) },
|
|
3067
|
+
children: [
|
|
3068
|
+
l.label,
|
|
3069
|
+
/* @__PURE__ */ jsxs27("span", { className: "opacity-60", children: [
|
|
3070
|
+
(l.score * 100).toFixed(0),
|
|
3071
|
+
"%"
|
|
3072
|
+
] }),
|
|
3073
|
+
l.addonId && /* @__PURE__ */ jsx44("span", { className: "opacity-40 text-[8px]", children: l.addonId })
|
|
3074
|
+
]
|
|
3075
|
+
},
|
|
3076
|
+
k
|
|
3077
|
+
)) }),
|
|
3078
|
+
detection.children && detection.children.length > 0 && /* @__PURE__ */ jsx44(
|
|
3079
|
+
ChildrenTree,
|
|
3080
|
+
{
|
|
3081
|
+
children: detection.children,
|
|
3082
|
+
parentPath: path,
|
|
3083
|
+
colors,
|
|
3084
|
+
hiddenKeys,
|
|
3085
|
+
onToggleVisibility
|
|
3086
|
+
}
|
|
3087
|
+
)
|
|
3088
|
+
] });
|
|
3089
|
+
}
|
|
3090
|
+
function ChildrenTree({
|
|
3091
|
+
children,
|
|
3092
|
+
parentPath,
|
|
3093
|
+
colors,
|
|
3094
|
+
hiddenKeys,
|
|
3095
|
+
onToggleVisibility
|
|
3096
|
+
}) {
|
|
3097
|
+
return /* @__PURE__ */ jsx44("div", { className: "ml-4 mt-1.5 space-y-1.5 border-l-2 border-border pl-3", children: children.map((child, j) => {
|
|
3098
|
+
const childPath = `${parentPath}.${j}`;
|
|
3099
|
+
const childColor = getClassColor(child.className, colors);
|
|
3100
|
+
const isVisible = !hiddenKeys?.has(childPath);
|
|
3101
|
+
return /* @__PURE__ */ jsxs27("div", { className: `text-xs space-y-0.5 ${isVisible ? "" : "opacity-40"}`, children: [
|
|
3102
|
+
/* @__PURE__ */ jsxs27("div", { className: "flex items-center gap-1.5", children: [
|
|
3103
|
+
onToggleVisibility && /* @__PURE__ */ jsx44(
|
|
3104
|
+
"input",
|
|
3105
|
+
{
|
|
3106
|
+
type: "checkbox",
|
|
3107
|
+
checked: isVisible,
|
|
3108
|
+
onChange: () => onToggleVisibility(childPath, !isVisible),
|
|
3109
|
+
className: "h-3 w-3 rounded border-border accent-primary cursor-pointer shrink-0"
|
|
3110
|
+
}
|
|
3111
|
+
),
|
|
3112
|
+
/* @__PURE__ */ jsx44(
|
|
3113
|
+
"span",
|
|
3114
|
+
{
|
|
3115
|
+
className: "h-1.5 w-1.5 rounded-full shrink-0",
|
|
3116
|
+
style: { backgroundColor: childColor }
|
|
3117
|
+
}
|
|
3118
|
+
),
|
|
3119
|
+
/* @__PURE__ */ jsx44("span", { className: "font-medium", style: { color: childColor }, children: child.className }),
|
|
3120
|
+
/* @__PURE__ */ jsxs27("span", { className: "text-foreground-subtle", children: [
|
|
3121
|
+
(child.confidence * 100).toFixed(0),
|
|
3122
|
+
"%"
|
|
3123
|
+
] }),
|
|
3124
|
+
child.mask && child.maskWidth && child.maskHeight && /* @__PURE__ */ jsxs27("span", { className: "text-[9px] font-mono px-1 py-0.5 rounded bg-primary/10 text-primary", children: [
|
|
3125
|
+
"mask ",
|
|
3126
|
+
child.maskWidth,
|
|
3127
|
+
"x",
|
|
3128
|
+
child.maskHeight
|
|
3129
|
+
] })
|
|
3130
|
+
] }),
|
|
3131
|
+
child.labelsData && child.labelsData.length > 0 && /* @__PURE__ */ jsx44("div", { className: "flex flex-wrap gap-1 ml-5 mt-0.5", children: child.labelsData.map((l, k) => /* @__PURE__ */ jsxs27(
|
|
3132
|
+
"span",
|
|
3133
|
+
{
|
|
3134
|
+
className: "inline-flex items-center gap-0.5 text-[9px] font-medium px-1 py-0.5 rounded-full",
|
|
3135
|
+
style: { backgroundColor: getClassColor(l.addonId ?? l.label, colors) + "20", color: getClassColor(l.addonId ?? l.label, colors) },
|
|
3136
|
+
children: [
|
|
3137
|
+
l.label,
|
|
3138
|
+
" ",
|
|
3139
|
+
/* @__PURE__ */ jsxs27("span", { className: "opacity-60", children: [
|
|
3140
|
+
(l.score * 100).toFixed(0),
|
|
3141
|
+
"%"
|
|
3142
|
+
] })
|
|
3143
|
+
]
|
|
3144
|
+
},
|
|
3145
|
+
k
|
|
3146
|
+
)) }),
|
|
3147
|
+
child.children && child.children.length > 0 && /* @__PURE__ */ jsx44(
|
|
3148
|
+
ChildrenTree,
|
|
3149
|
+
{
|
|
3150
|
+
children: child.children,
|
|
3151
|
+
parentPath: childPath,
|
|
3152
|
+
colors,
|
|
3153
|
+
hiddenKeys,
|
|
3154
|
+
onToggleVisibility
|
|
3155
|
+
}
|
|
3156
|
+
)
|
|
3157
|
+
] }, j);
|
|
3158
|
+
}) });
|
|
3159
|
+
}
|
|
3160
|
+
function ConfidenceBadge({ confidence }) {
|
|
3161
|
+
const level = confidence >= 0.8 ? "bg-success/10 text-success" : confidence >= 0.5 ? "bg-warning/10 text-warning" : "bg-danger/10 text-danger";
|
|
3162
|
+
return /* @__PURE__ */ jsxs27("span", { className: `text-xs font-medium px-2 py-0.5 rounded-full ${level}`, children: [
|
|
3163
|
+
(confidence * 100).toFixed(1),
|
|
3164
|
+
"%"
|
|
3165
|
+
] });
|
|
3166
|
+
}
|
|
3167
|
+
|
|
3168
|
+
// src/composites/step-timings.tsx
|
|
3169
|
+
import { jsx as jsx45, jsxs as jsxs28 } from "react/jsx-runtime";
|
|
3170
|
+
function StepTimings({ timings, totalMs, className }) {
|
|
3171
|
+
const entries = Object.entries(timings);
|
|
3172
|
+
if (entries.length === 0 && totalMs === void 0) return null;
|
|
3173
|
+
return /* @__PURE__ */ jsxs28("div", { className: `rounded-lg border border-border bg-surface p-3 space-y-2 ${className ?? ""}`, children: [
|
|
3174
|
+
/* @__PURE__ */ jsx45("div", { className: "text-xs font-medium text-foreground-subtle uppercase tracking-wide", children: "Timings" }),
|
|
3175
|
+
/* @__PURE__ */ jsxs28("div", { className: "space-y-1 text-xs", children: [
|
|
3176
|
+
entries.map(([step, ms]) => /* @__PURE__ */ jsxs28("div", { className: "flex justify-between", children: [
|
|
3177
|
+
/* @__PURE__ */ jsx45("span", { className: "text-foreground-subtle", children: step }),
|
|
3178
|
+
/* @__PURE__ */ jsxs28("span", { className: "font-mono text-foreground", children: [
|
|
3179
|
+
ms.toFixed(1),
|
|
3180
|
+
"ms"
|
|
3181
|
+
] })
|
|
3182
|
+
] }, step)),
|
|
3183
|
+
totalMs !== void 0 && /* @__PURE__ */ jsxs28("div", { className: "flex justify-between pt-1 border-t border-border font-medium text-foreground", children: [
|
|
3184
|
+
/* @__PURE__ */ jsx45("span", { children: "Total" }),
|
|
3185
|
+
/* @__PURE__ */ jsxs28("span", { className: "font-mono", children: [
|
|
3186
|
+
totalMs.toFixed(1),
|
|
3187
|
+
"ms"
|
|
3188
|
+
] })
|
|
3189
|
+
] })
|
|
3190
|
+
] })
|
|
3191
|
+
] });
|
|
3192
|
+
}
|
|
3193
|
+
|
|
3194
|
+
// src/composites/image-selector.tsx
|
|
3195
|
+
import { jsx as jsx46, jsxs as jsxs29 } from "react/jsx-runtime";
|
|
3196
|
+
function ImageSelector({
|
|
3197
|
+
images,
|
|
3198
|
+
selectedFilename,
|
|
3199
|
+
uploadedName,
|
|
3200
|
+
onSelect,
|
|
3201
|
+
onUpload,
|
|
3202
|
+
className
|
|
3203
|
+
}) {
|
|
3204
|
+
const handleUploadClick = () => {
|
|
3205
|
+
const input = document.createElement("input");
|
|
3206
|
+
input.type = "file";
|
|
3207
|
+
input.accept = "image/*";
|
|
3208
|
+
input.onchange = (ev) => {
|
|
3209
|
+
const file = ev.target.files?.[0];
|
|
3210
|
+
if (!file) return;
|
|
3211
|
+
const reader = new FileReader();
|
|
3212
|
+
reader.onload = (e) => {
|
|
3213
|
+
const dataUrl = e.target?.result;
|
|
3214
|
+
const b64 = dataUrl.split(",")[1];
|
|
3215
|
+
if (b64) onUpload(b64, file.name, dataUrl);
|
|
3216
|
+
};
|
|
3217
|
+
reader.readAsDataURL(file);
|
|
3218
|
+
};
|
|
3219
|
+
input.click();
|
|
3220
|
+
};
|
|
3221
|
+
return /* @__PURE__ */ jsxs29("div", { className: `flex flex-wrap items-center gap-2 ${className ?? ""}`, children: [
|
|
3222
|
+
images.map((img) => /* @__PURE__ */ jsx46(
|
|
3223
|
+
"button",
|
|
3224
|
+
{
|
|
3225
|
+
onClick: () => onSelect(img.filename),
|
|
3226
|
+
className: `px-3 py-1.5 text-xs rounded-md border transition-colors ${selectedFilename === img.filename ? "bg-primary text-primary-foreground border-primary" : "bg-surface border-border text-foreground hover:border-primary/50"}`,
|
|
3227
|
+
children: img.id
|
|
3228
|
+
},
|
|
3229
|
+
img.filename
|
|
3230
|
+
)),
|
|
3231
|
+
/* @__PURE__ */ jsx46(
|
|
3232
|
+
"button",
|
|
3233
|
+
{
|
|
3234
|
+
onClick: handleUploadClick,
|
|
3235
|
+
className: "px-3 py-1.5 text-xs rounded-md border border-border bg-surface text-foreground hover:bg-surface-hover transition-colors",
|
|
3236
|
+
children: "Upload..."
|
|
3237
|
+
}
|
|
3238
|
+
),
|
|
3239
|
+
uploadedName && /* @__PURE__ */ jsx46("span", { className: "text-xs text-foreground-subtle", children: uploadedName })
|
|
3240
|
+
] });
|
|
3241
|
+
}
|
|
3242
|
+
|
|
3243
|
+
// src/composites/inference-config-selector.tsx
|
|
3244
|
+
import { jsx as jsx47, jsxs as jsxs30 } from "react/jsx-runtime";
|
|
3245
|
+
var SELECT_CLASS = "w-full px-3 py-2 text-sm rounded-md border border-border bg-surface text-foreground focus:outline-none focus:ring-2 focus:ring-primary/50";
|
|
3246
|
+
function InferenceConfigSelector({
|
|
3247
|
+
runtime,
|
|
3248
|
+
backend,
|
|
3249
|
+
modelId,
|
|
3250
|
+
agentId = "hub",
|
|
3251
|
+
runtimes,
|
|
3252
|
+
backends,
|
|
3253
|
+
models,
|
|
3254
|
+
agents = [],
|
|
3255
|
+
onRuntimeChange,
|
|
3256
|
+
onBackendChange,
|
|
3257
|
+
onModelChange,
|
|
3258
|
+
onAgentChange,
|
|
3259
|
+
layout = "grid",
|
|
3260
|
+
className,
|
|
3261
|
+
showAgent = false
|
|
3262
|
+
}) {
|
|
3263
|
+
const containerClass = layout === "grid" ? "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4" : layout === "horizontal" ? "flex flex-wrap items-end gap-4" : "space-y-3";
|
|
3264
|
+
return /* @__PURE__ */ jsxs30("div", { className: `${containerClass} ${className ?? ""}`, children: [
|
|
3265
|
+
showAgent && agents.length > 0 && /* @__PURE__ */ jsxs30("label", { className: "space-y-1", children: [
|
|
3266
|
+
/* @__PURE__ */ jsx47("span", { className: "text-xs font-medium text-foreground-subtle", children: "Agent" }),
|
|
3267
|
+
/* @__PURE__ */ jsx47(
|
|
3268
|
+
"select",
|
|
3269
|
+
{
|
|
3270
|
+
value: agentId,
|
|
3271
|
+
onChange: (e) => onAgentChange?.(e.target.value),
|
|
3272
|
+
className: SELECT_CLASS,
|
|
3273
|
+
children: agents.map((a) => /* @__PURE__ */ jsxs30("option", { value: a.id, children: [
|
|
3274
|
+
a.name,
|
|
3275
|
+
" (",
|
|
3276
|
+
a.status,
|
|
3277
|
+
")"
|
|
3278
|
+
] }, a.id))
|
|
3279
|
+
}
|
|
3280
|
+
)
|
|
3281
|
+
] }),
|
|
3282
|
+
/* @__PURE__ */ jsxs30("label", { className: "space-y-1", children: [
|
|
3283
|
+
/* @__PURE__ */ jsx47("span", { className: "text-xs font-medium text-foreground-subtle", children: "Runtime" }),
|
|
3284
|
+
/* @__PURE__ */ jsx47(
|
|
3285
|
+
"select",
|
|
3286
|
+
{
|
|
3287
|
+
value: runtime,
|
|
3288
|
+
onChange: (e) => onRuntimeChange(e.target.value),
|
|
3289
|
+
className: SELECT_CLASS,
|
|
3290
|
+
children: runtimes.map((r) => /* @__PURE__ */ jsxs30("option", { value: r.value, disabled: !r.available, children: [
|
|
3291
|
+
r.label,
|
|
3292
|
+
!r.available ? " (unavailable)" : ""
|
|
3293
|
+
] }, r.value))
|
|
3294
|
+
}
|
|
3295
|
+
)
|
|
3296
|
+
] }),
|
|
3297
|
+
/* @__PURE__ */ jsxs30("label", { className: "space-y-1", children: [
|
|
3298
|
+
/* @__PURE__ */ jsx47("span", { className: "text-xs font-medium text-foreground-subtle", children: "Backend" }),
|
|
3299
|
+
/* @__PURE__ */ jsx47(
|
|
3300
|
+
"select",
|
|
3301
|
+
{
|
|
3302
|
+
value: backend,
|
|
3303
|
+
onChange: (e) => onBackendChange(e.target.value),
|
|
3304
|
+
className: SELECT_CLASS,
|
|
3305
|
+
children: backends.map((b) => /* @__PURE__ */ jsxs30("option", { value: b.id, disabled: !b.available, children: [
|
|
3306
|
+
b.label,
|
|
3307
|
+
!b.available ? " (unavailable)" : ""
|
|
3308
|
+
] }, b.id))
|
|
3309
|
+
}
|
|
3310
|
+
)
|
|
3311
|
+
] }),
|
|
3312
|
+
/* @__PURE__ */ jsxs30("label", { className: "space-y-1", children: [
|
|
3313
|
+
/* @__PURE__ */ jsx47("span", { className: "text-xs font-medium text-foreground-subtle", children: "Model" }),
|
|
3314
|
+
/* @__PURE__ */ jsx47(
|
|
3315
|
+
"select",
|
|
3316
|
+
{
|
|
3317
|
+
value: modelId,
|
|
3318
|
+
onChange: (e) => onModelChange(e.target.value),
|
|
3319
|
+
className: SELECT_CLASS,
|
|
3320
|
+
children: models.length === 0 ? /* @__PURE__ */ jsx47("option", { value: "", children: "No compatible models" }) : models.map((m) => /* @__PURE__ */ jsxs30("option", { value: m.id, children: [
|
|
3321
|
+
m.name,
|
|
3322
|
+
m.downloaded ? " \u2713" : ""
|
|
3323
|
+
] }, m.id))
|
|
3324
|
+
}
|
|
3325
|
+
)
|
|
3326
|
+
] })
|
|
3327
|
+
] });
|
|
3328
|
+
}
|
|
3329
|
+
|
|
3330
|
+
// src/composites/mount-addon-page.tsx
|
|
3331
|
+
import { createElement } from "react";
|
|
3332
|
+
import { createRoot } from "react-dom/client";
|
|
3333
|
+
|
|
3334
|
+
// src/composites/dev-shell.tsx
|
|
3335
|
+
import { createContext as createContext7, useCallback as useCallback8, useContext as useContext7, useMemo as useMemo4, useState as useState12 } from "react";
|
|
3336
|
+
import { createTRPCClient, createWSClient, wsLink, httpLink, splitLink } from "@trpc/client";
|
|
3337
|
+
import superjson from "superjson";
|
|
3338
|
+
|
|
3339
|
+
// src/composites/login-form.tsx
|
|
3340
|
+
import { useState as useState11 } from "react";
|
|
3341
|
+
import { jsx as jsx48, jsxs as jsxs31 } from "react/jsx-runtime";
|
|
3342
|
+
function EyeIcon({ className }) {
|
|
3343
|
+
return /* @__PURE__ */ jsxs31(
|
|
3344
|
+
"svg",
|
|
3345
|
+
{
|
|
3346
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
3347
|
+
viewBox: "0 0 24 24",
|
|
3348
|
+
fill: "none",
|
|
3349
|
+
stroke: "currentColor",
|
|
3350
|
+
strokeWidth: "2",
|
|
3351
|
+
strokeLinecap: "round",
|
|
3352
|
+
strokeLinejoin: "round",
|
|
3353
|
+
className,
|
|
3354
|
+
children: [
|
|
3355
|
+
/* @__PURE__ */ jsx48("path", { d: "M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0" }),
|
|
3356
|
+
/* @__PURE__ */ jsx48("circle", { cx: "12", cy: "12", r: "3" })
|
|
3357
|
+
]
|
|
3358
|
+
}
|
|
3359
|
+
);
|
|
3360
|
+
}
|
|
3361
|
+
function EyeOffIcon({ className }) {
|
|
3362
|
+
return /* @__PURE__ */ jsxs31(
|
|
3363
|
+
"svg",
|
|
3364
|
+
{
|
|
3365
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
3366
|
+
viewBox: "0 0 24 24",
|
|
3367
|
+
fill: "none",
|
|
3368
|
+
stroke: "currentColor",
|
|
3369
|
+
strokeWidth: "2",
|
|
3370
|
+
strokeLinecap: "round",
|
|
3371
|
+
strokeLinejoin: "round",
|
|
3372
|
+
className,
|
|
3373
|
+
children: [
|
|
3374
|
+
/* @__PURE__ */ jsx48("path", { d: "M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49" }),
|
|
3375
|
+
/* @__PURE__ */ jsx48("path", { d: "M14.084 14.158a3 3 0 0 1-4.242-4.242" }),
|
|
3376
|
+
/* @__PURE__ */ jsx48("path", { d: "M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143" }),
|
|
3377
|
+
/* @__PURE__ */ jsx48("path", { d: "m2 2 20 20" })
|
|
3378
|
+
]
|
|
3379
|
+
}
|
|
3380
|
+
);
|
|
3381
|
+
}
|
|
3382
|
+
function SpinnerIcon({ className }) {
|
|
3383
|
+
return /* @__PURE__ */ jsx48(
|
|
3384
|
+
"svg",
|
|
3385
|
+
{
|
|
3386
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
3387
|
+
viewBox: "0 0 24 24",
|
|
3388
|
+
fill: "none",
|
|
3389
|
+
stroke: "currentColor",
|
|
3390
|
+
strokeWidth: "2",
|
|
3391
|
+
strokeLinecap: "round",
|
|
3392
|
+
strokeLinejoin: "round",
|
|
3393
|
+
className,
|
|
3394
|
+
children: /* @__PURE__ */ jsx48("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" })
|
|
3395
|
+
}
|
|
3396
|
+
);
|
|
3397
|
+
}
|
|
3398
|
+
function LoginForm({
|
|
3399
|
+
onLogin,
|
|
3400
|
+
serverUrl,
|
|
3401
|
+
logoSrc,
|
|
3402
|
+
error: externalError,
|
|
3403
|
+
className
|
|
3404
|
+
}) {
|
|
3405
|
+
const [username, setUsername] = useState11("");
|
|
3406
|
+
const [password, setPassword] = useState11("");
|
|
3407
|
+
const [showPassword, setShowPassword] = useState11(false);
|
|
3408
|
+
const [submitting, setSubmitting] = useState11(false);
|
|
3409
|
+
const [internalError, setInternalError] = useState11(null);
|
|
3410
|
+
const error = externalError ?? internalError;
|
|
3411
|
+
const handleSubmit = async (e) => {
|
|
3412
|
+
e.preventDefault();
|
|
3413
|
+
if (submitting) return;
|
|
3414
|
+
setInternalError(null);
|
|
3415
|
+
setSubmitting(true);
|
|
3416
|
+
try {
|
|
3417
|
+
await onLogin(username, password);
|
|
3418
|
+
} catch (err) {
|
|
3419
|
+
const message = err instanceof Error ? err.message : "Login failed. Please try again.";
|
|
3420
|
+
setInternalError(message);
|
|
3421
|
+
} finally {
|
|
3422
|
+
setSubmitting(false);
|
|
3423
|
+
}
|
|
3424
|
+
};
|
|
3425
|
+
return /* @__PURE__ */ jsx48(
|
|
3426
|
+
"div",
|
|
3427
|
+
{
|
|
3428
|
+
className: cn(
|
|
3429
|
+
"flex min-h-screen items-center justify-center bg-background p-4",
|
|
3430
|
+
className
|
|
3431
|
+
),
|
|
3432
|
+
children: /* @__PURE__ */ jsxs31("div", { className: "w-full max-w-sm", children: [
|
|
3433
|
+
logoSrc && /* @__PURE__ */ jsx48("div", { className: "flex justify-center mb-8", children: /* @__PURE__ */ jsx48("img", { src: logoSrc, alt: "Logo", className: "h-12" }) }),
|
|
3434
|
+
serverUrl && /* @__PURE__ */ jsx48("p", { className: "mb-4 text-center text-xs text-foreground-subtle truncate", children: serverUrl }),
|
|
3435
|
+
/* @__PURE__ */ jsxs31(
|
|
3436
|
+
"form",
|
|
3437
|
+
{
|
|
3438
|
+
onSubmit: handleSubmit,
|
|
3439
|
+
className: "space-y-4 rounded-xl border border-border bg-surface p-6 shadow-xl shadow-black/10",
|
|
3440
|
+
children: [
|
|
3441
|
+
error && /* @__PURE__ */ jsx48("div", { className: "rounded-md bg-danger/10 border border-danger/20 px-3 py-2 text-xs text-danger", children: error }),
|
|
3442
|
+
/* @__PURE__ */ jsxs31("div", { className: "space-y-1.5", children: [
|
|
3443
|
+
/* @__PURE__ */ jsx48("label", { className: "text-xs font-medium text-foreground-subtle", children: "Username" }),
|
|
3444
|
+
/* @__PURE__ */ jsx48(
|
|
3445
|
+
"input",
|
|
3446
|
+
{
|
|
3447
|
+
type: "text",
|
|
3448
|
+
value: username,
|
|
3449
|
+
onChange: (e) => setUsername(e.target.value),
|
|
3450
|
+
autoComplete: "username",
|
|
3451
|
+
required: true,
|
|
3452
|
+
className: "w-full rounded-lg border border-border bg-background px-3 py-2.5 text-sm text-foreground placeholder:text-foreground-subtle focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary"
|
|
3453
|
+
}
|
|
3454
|
+
)
|
|
3455
|
+
] }),
|
|
3456
|
+
/* @__PURE__ */ jsxs31("div", { className: "space-y-1.5", children: [
|
|
3457
|
+
/* @__PURE__ */ jsx48("label", { className: "text-xs font-medium text-foreground-subtle", children: "Password" }),
|
|
3458
|
+
/* @__PURE__ */ jsxs31("div", { className: "relative", children: [
|
|
3459
|
+
/* @__PURE__ */ jsx48(
|
|
3460
|
+
"input",
|
|
3461
|
+
{
|
|
3462
|
+
type: showPassword ? "text" : "password",
|
|
3463
|
+
value: password,
|
|
3464
|
+
onChange: (e) => setPassword(e.target.value),
|
|
3465
|
+
autoComplete: "current-password",
|
|
3466
|
+
required: true,
|
|
3467
|
+
className: "w-full rounded-lg border border-border bg-background px-3 py-2.5 pr-10 text-sm text-foreground placeholder:text-foreground-subtle focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary"
|
|
3468
|
+
}
|
|
3469
|
+
),
|
|
3470
|
+
/* @__PURE__ */ jsx48(
|
|
3471
|
+
"button",
|
|
3472
|
+
{
|
|
3473
|
+
type: "button",
|
|
3474
|
+
onClick: () => setShowPassword((prev) => !prev),
|
|
3475
|
+
className: "absolute right-2.5 top-1/2 -translate-y-1/2 text-foreground-subtle hover:text-foreground",
|
|
3476
|
+
tabIndex: -1,
|
|
3477
|
+
children: showPassword ? /* @__PURE__ */ jsx48(EyeOffIcon, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx48(EyeIcon, { className: "h-4 w-4" })
|
|
3478
|
+
}
|
|
3479
|
+
)
|
|
3480
|
+
] })
|
|
3481
|
+
] }),
|
|
3482
|
+
/* @__PURE__ */ jsxs31(
|
|
3483
|
+
"button",
|
|
3484
|
+
{
|
|
3485
|
+
type: "submit",
|
|
3486
|
+
disabled: submitting,
|
|
3487
|
+
className: "w-full rounded-lg bg-primary px-4 py-2.5 text-sm font-semibold text-primary-foreground hover:bg-primary/90 disabled:opacity-50 disabled:cursor-not-allowed transition-colors flex items-center justify-center gap-2",
|
|
3488
|
+
children: [
|
|
3489
|
+
submitting && /* @__PURE__ */ jsx48(SpinnerIcon, { className: "h-4 w-4 animate-spin" }),
|
|
3490
|
+
submitting ? "Logging in..." : "Log in"
|
|
3491
|
+
]
|
|
3492
|
+
}
|
|
3493
|
+
)
|
|
3494
|
+
]
|
|
3495
|
+
}
|
|
3496
|
+
)
|
|
3497
|
+
] })
|
|
3498
|
+
}
|
|
3499
|
+
);
|
|
3500
|
+
}
|
|
3501
|
+
|
|
3502
|
+
// src/composites/dev-shell.tsx
|
|
3503
|
+
import { jsx as jsx49, jsxs as jsxs32 } from "react/jsx-runtime";
|
|
3504
|
+
var STORAGE_KEY = "camstack_dev_token";
|
|
3505
|
+
var DevShellContext = createContext7(null);
|
|
3506
|
+
function useDevShell() {
|
|
3507
|
+
const ctx = useContext7(DevShellContext);
|
|
3508
|
+
if (!ctx) {
|
|
3509
|
+
throw new Error("useDevShell must be used within a DevShell");
|
|
3510
|
+
}
|
|
3511
|
+
return ctx;
|
|
3512
|
+
}
|
|
3513
|
+
function getStoredToken() {
|
|
3514
|
+
if (typeof window === "undefined") return null;
|
|
3515
|
+
return localStorage.getItem(STORAGE_KEY);
|
|
3516
|
+
}
|
|
3517
|
+
function SunIcon({ className }) {
|
|
3518
|
+
return /* @__PURE__ */ jsxs32(
|
|
3519
|
+
"svg",
|
|
3520
|
+
{
|
|
3521
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
3522
|
+
viewBox: "0 0 24 24",
|
|
3523
|
+
fill: "none",
|
|
3524
|
+
stroke: "currentColor",
|
|
3525
|
+
strokeWidth: "2",
|
|
3526
|
+
strokeLinecap: "round",
|
|
3527
|
+
strokeLinejoin: "round",
|
|
3528
|
+
className,
|
|
3529
|
+
children: [
|
|
3530
|
+
/* @__PURE__ */ jsx49("circle", { cx: "12", cy: "12", r: "4" }),
|
|
3531
|
+
/* @__PURE__ */ jsx49("path", { d: "M12 2v2" }),
|
|
3532
|
+
/* @__PURE__ */ jsx49("path", { d: "M12 20v2" }),
|
|
3533
|
+
/* @__PURE__ */ jsx49("path", { d: "m4.93 4.93 1.41 1.41" }),
|
|
3534
|
+
/* @__PURE__ */ jsx49("path", { d: "m17.66 17.66 1.41 1.41" }),
|
|
3535
|
+
/* @__PURE__ */ jsx49("path", { d: "M2 12h2" }),
|
|
3536
|
+
/* @__PURE__ */ jsx49("path", { d: "M20 12h2" }),
|
|
3537
|
+
/* @__PURE__ */ jsx49("path", { d: "m6.34 17.66-1.41 1.41" }),
|
|
3538
|
+
/* @__PURE__ */ jsx49("path", { d: "m19.07 4.93-1.41 1.41" })
|
|
3539
|
+
]
|
|
3540
|
+
}
|
|
3541
|
+
);
|
|
3542
|
+
}
|
|
3543
|
+
function MoonIcon({ className }) {
|
|
3544
|
+
return /* @__PURE__ */ jsx49(
|
|
3545
|
+
"svg",
|
|
3546
|
+
{
|
|
3547
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
3548
|
+
viewBox: "0 0 24 24",
|
|
3549
|
+
fill: "none",
|
|
3550
|
+
stroke: "currentColor",
|
|
3551
|
+
strokeWidth: "2",
|
|
3552
|
+
strokeLinecap: "round",
|
|
3553
|
+
strokeLinejoin: "round",
|
|
3554
|
+
className,
|
|
3555
|
+
children: /* @__PURE__ */ jsx49("path", { d: "M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z" })
|
|
3556
|
+
}
|
|
3557
|
+
);
|
|
3558
|
+
}
|
|
3559
|
+
function DevShellInner({
|
|
3560
|
+
children,
|
|
3561
|
+
serverUrl,
|
|
3562
|
+
title,
|
|
3563
|
+
token,
|
|
3564
|
+
onLogout
|
|
3565
|
+
}) {
|
|
3566
|
+
const theme = useThemeMode();
|
|
3567
|
+
const trpc = useMemo4(
|
|
3568
|
+
() => {
|
|
3569
|
+
const wsUrl = serverUrl.replace(/^http/, "ws") + "/trpc";
|
|
3570
|
+
const wsClient = createWSClient({
|
|
3571
|
+
url: wsUrl,
|
|
3572
|
+
connectionParams: () => ({ token })
|
|
3573
|
+
});
|
|
3574
|
+
return createTRPCClient({
|
|
3575
|
+
links: [
|
|
3576
|
+
splitLink({
|
|
3577
|
+
condition: (op) => op.type === "subscription",
|
|
3578
|
+
true: wsLink({ client: wsClient, transformer: superjson }),
|
|
3579
|
+
false: httpLink({
|
|
3580
|
+
url: `${serverUrl}/trpc`,
|
|
3581
|
+
transformer: superjson,
|
|
3582
|
+
headers: () => ({ authorization: `Bearer ${token}` })
|
|
3583
|
+
})
|
|
3584
|
+
})
|
|
3585
|
+
]
|
|
3586
|
+
});
|
|
3587
|
+
},
|
|
3588
|
+
[serverUrl, token]
|
|
3589
|
+
);
|
|
3590
|
+
const contextValue = useMemo4(
|
|
3591
|
+
() => ({ trpc, token, logout: onLogout }),
|
|
3592
|
+
[trpc, token, onLogout]
|
|
3593
|
+
);
|
|
3594
|
+
return /* @__PURE__ */ jsx49(DevShellContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxs32("div", { className: "min-h-screen bg-background text-foreground", children: [
|
|
3595
|
+
/* @__PURE__ */ jsxs32("div", { className: "flex items-center justify-between border-b border-border bg-surface px-4 py-2", children: [
|
|
3596
|
+
/* @__PURE__ */ jsxs32("div", { className: "flex items-center gap-2", children: [
|
|
3597
|
+
/* @__PURE__ */ jsx49("span", { className: "rounded bg-warning/20 px-2 py-0.5 text-xs font-bold text-warning", children: "DEV MODE" }),
|
|
3598
|
+
title && /* @__PURE__ */ jsx49("span", { className: "text-sm font-medium text-foreground", children: title }),
|
|
3599
|
+
/* @__PURE__ */ jsx49("span", { className: "text-xs text-foreground-subtle", children: serverUrl })
|
|
3600
|
+
] }),
|
|
3601
|
+
/* @__PURE__ */ jsxs32("div", { className: "flex items-center gap-2", children: [
|
|
3602
|
+
/* @__PURE__ */ jsxs32(
|
|
3603
|
+
"button",
|
|
3604
|
+
{
|
|
3605
|
+
type: "button",
|
|
3606
|
+
onClick: theme.toggleMode,
|
|
3607
|
+
className: "flex items-center gap-1.5 rounded-md px-2 py-1 text-xs font-medium text-foreground-subtle hover:text-foreground hover:bg-surface-hover transition-colors",
|
|
3608
|
+
title: `Theme: ${theme.mode}`,
|
|
3609
|
+
children: [
|
|
3610
|
+
theme.resolvedMode === "dark" ? /* @__PURE__ */ jsx49(SunIcon, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx49(MoonIcon, { className: "h-3.5 w-3.5" }),
|
|
3611
|
+
theme.mode === "dark" ? "Dark" : theme.mode === "light" ? "Light" : "System"
|
|
3612
|
+
]
|
|
3613
|
+
}
|
|
3614
|
+
),
|
|
3615
|
+
/* @__PURE__ */ jsx49(
|
|
3616
|
+
"button",
|
|
3617
|
+
{
|
|
3618
|
+
type: "button",
|
|
3619
|
+
onClick: onLogout,
|
|
3620
|
+
className: "rounded-md px-2.5 py-1 text-xs font-medium text-danger hover:bg-danger/10 transition-colors",
|
|
3621
|
+
children: "Logout"
|
|
3622
|
+
}
|
|
3623
|
+
)
|
|
3624
|
+
] })
|
|
3625
|
+
] }),
|
|
3626
|
+
/* @__PURE__ */ jsx49("div", { className: "p-4", children: children({ trpc, theme }) })
|
|
3627
|
+
] }) });
|
|
3628
|
+
}
|
|
3629
|
+
function DevShell({
|
|
3630
|
+
children,
|
|
3631
|
+
serverUrl = "https://localhost:4443",
|
|
3632
|
+
title
|
|
3633
|
+
}) {
|
|
3634
|
+
const [token, setToken] = useState12(getStoredToken);
|
|
3635
|
+
const handleLogin = useCallback8(
|
|
3636
|
+
async (username, password) => {
|
|
3637
|
+
const anonClient = createTRPCClient({
|
|
3638
|
+
links: [
|
|
3639
|
+
httpLink({
|
|
3640
|
+
url: `${serverUrl}/trpc`,
|
|
3641
|
+
transformer: superjson
|
|
3642
|
+
})
|
|
3643
|
+
]
|
|
3644
|
+
});
|
|
3645
|
+
const res = await anonClient.auth.login.mutate({ username, password });
|
|
3646
|
+
if (!res?.token) throw new Error("No token returned");
|
|
3647
|
+
localStorage.setItem(STORAGE_KEY, res.token);
|
|
3648
|
+
setToken(res.token);
|
|
3649
|
+
},
|
|
3650
|
+
[serverUrl]
|
|
3651
|
+
);
|
|
3652
|
+
const handleLogout = useCallback8(() => {
|
|
3653
|
+
localStorage.removeItem(STORAGE_KEY);
|
|
3654
|
+
setToken(null);
|
|
3655
|
+
}, []);
|
|
3656
|
+
if (!token) {
|
|
3657
|
+
return /* @__PURE__ */ jsx49(ThemeProvider, { children: /* @__PURE__ */ jsx49(LoginForm, { onLogin: handleLogin, serverUrl }) });
|
|
3658
|
+
}
|
|
3659
|
+
return /* @__PURE__ */ jsx49(ThemeProvider, { children: /* @__PURE__ */ jsx49(
|
|
3660
|
+
DevShellInner,
|
|
3661
|
+
{
|
|
3662
|
+
serverUrl,
|
|
3663
|
+
title,
|
|
3664
|
+
token,
|
|
3665
|
+
onLogout: handleLogout,
|
|
3666
|
+
children
|
|
3667
|
+
}
|
|
3668
|
+
) });
|
|
3669
|
+
}
|
|
3670
|
+
|
|
3671
|
+
// src/composites/mount-addon-page.tsx
|
|
3672
|
+
function mountAddonPage(PageComponent, options = {}) {
|
|
3673
|
+
const {
|
|
3674
|
+
serverUrl = "https://localhost:4443",
|
|
3675
|
+
title,
|
|
3676
|
+
rootId = "root"
|
|
3677
|
+
} = options;
|
|
3678
|
+
const root = document.getElementById(rootId);
|
|
3679
|
+
if (!root) {
|
|
3680
|
+
console.error(`[mountAddonPage] Element #${rootId} not found`);
|
|
3681
|
+
return;
|
|
3682
|
+
}
|
|
3683
|
+
createRoot(root).render(
|
|
3684
|
+
createElement(DevShell, {
|
|
3685
|
+
serverUrl,
|
|
3686
|
+
title,
|
|
3687
|
+
children: ({ trpc, theme }) => createElement(PageComponent, {
|
|
3688
|
+
trpc,
|
|
3689
|
+
theme: { isDark: theme.resolvedMode === "dark" },
|
|
3690
|
+
navigate: (path) => {
|
|
3691
|
+
console.log("[dev] navigate:", path);
|
|
3692
|
+
}
|
|
3693
|
+
})
|
|
3694
|
+
})
|
|
3695
|
+
);
|
|
3696
|
+
}
|
|
1981
3697
|
export {
|
|
1982
3698
|
AppShell,
|
|
1983
3699
|
Badge,
|
|
1984
3700
|
Button,
|
|
3701
|
+
CLASS_COLORS,
|
|
1985
3702
|
Card,
|
|
1986
3703
|
Checkbox,
|
|
1987
3704
|
CodeBlock,
|
|
1988
3705
|
ConfirmDialog,
|
|
3706
|
+
DEFAULT_CLASS_COLORS,
|
|
3707
|
+
DEFAULT_COLOR,
|
|
1989
3708
|
DataTable,
|
|
3709
|
+
DetectionCanvas,
|
|
3710
|
+
DetectionResultTree,
|
|
3711
|
+
DevShell,
|
|
1990
3712
|
DeviceCard,
|
|
1991
3713
|
DeviceGrid,
|
|
1992
3714
|
Dialog,
|
|
@@ -2005,22 +3727,30 @@ export {
|
|
|
2005
3727
|
FloatingPanel,
|
|
2006
3728
|
FormField,
|
|
2007
3729
|
IconButton,
|
|
3730
|
+
ImageSelector,
|
|
3731
|
+
InferenceConfigSelector,
|
|
2008
3732
|
Input,
|
|
2009
3733
|
KeyValueList,
|
|
2010
3734
|
Label,
|
|
3735
|
+
LoginForm,
|
|
2011
3736
|
PageHeader,
|
|
3737
|
+
PipelineBuilder,
|
|
3738
|
+
PipelineRuntimeSelector,
|
|
3739
|
+
PipelineStep,
|
|
2012
3740
|
Popover,
|
|
2013
3741
|
PopoverContent,
|
|
2014
3742
|
PopoverTrigger,
|
|
2015
3743
|
ProviderBadge,
|
|
2016
3744
|
ScrollArea,
|
|
2017
3745
|
Select,
|
|
3746
|
+
SemanticBadge,
|
|
2018
3747
|
Separator,
|
|
2019
3748
|
Sidebar,
|
|
2020
3749
|
SidebarItem,
|
|
2021
3750
|
Skeleton,
|
|
2022
3751
|
StatCard,
|
|
2023
3752
|
StatusBadge,
|
|
3753
|
+
StepTimings,
|
|
2024
3754
|
Switch,
|
|
2025
3755
|
Tabs,
|
|
2026
3756
|
TabsContent,
|
|
@@ -2030,14 +3760,18 @@ export {
|
|
|
2030
3760
|
Tooltip,
|
|
2031
3761
|
TooltipContent,
|
|
2032
3762
|
TooltipTrigger,
|
|
3763
|
+
VersionBadge,
|
|
2033
3764
|
cn,
|
|
2034
3765
|
createTheme,
|
|
2035
3766
|
darkColors,
|
|
2036
3767
|
defaultTheme,
|
|
3768
|
+
getClassColor,
|
|
2037
3769
|
lightColors,
|
|
3770
|
+
mountAddonPage,
|
|
2038
3771
|
providerIcons,
|
|
2039
3772
|
statusIcons,
|
|
2040
3773
|
themeToCss,
|
|
3774
|
+
useDevShell,
|
|
2041
3775
|
useThemeMode
|
|
2042
3776
|
};
|
|
2043
3777
|
//# sourceMappingURL=index.js.map
|