@camstack/ui-library 0.1.25 → 0.1.26
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 +1736 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +279 -2
- package/dist/index.d.ts +279 -2
- package/dist/index.js +1710 -0
- package/dist/index.js.map +1 -1
- package/package.json +10 -1
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/index.ts
|
|
@@ -23,11 +33,17 @@ __export(src_exports, {
|
|
|
23
33
|
AppShell: () => AppShell,
|
|
24
34
|
Badge: () => Badge,
|
|
25
35
|
Button: () => Button,
|
|
36
|
+
CLASS_COLORS: () => CLASS_COLORS,
|
|
26
37
|
Card: () => Card,
|
|
27
38
|
Checkbox: () => Checkbox,
|
|
28
39
|
CodeBlock: () => CodeBlock,
|
|
29
40
|
ConfirmDialog: () => ConfirmDialog,
|
|
41
|
+
DEFAULT_CLASS_COLORS: () => DEFAULT_CLASS_COLORS,
|
|
42
|
+
DEFAULT_COLOR: () => DEFAULT_COLOR,
|
|
30
43
|
DataTable: () => DataTable,
|
|
44
|
+
DetectionCanvas: () => DetectionCanvas,
|
|
45
|
+
DetectionResultTree: () => DetectionResultTree,
|
|
46
|
+
DevShell: () => DevShell,
|
|
31
47
|
DeviceCard: () => DeviceCard,
|
|
32
48
|
DeviceGrid: () => DeviceGrid,
|
|
33
49
|
Dialog: () => Dialog,
|
|
@@ -46,10 +62,16 @@ __export(src_exports, {
|
|
|
46
62
|
FloatingPanel: () => FloatingPanel,
|
|
47
63
|
FormField: () => FormField,
|
|
48
64
|
IconButton: () => IconButton,
|
|
65
|
+
ImageSelector: () => ImageSelector,
|
|
66
|
+
InferenceConfigSelector: () => InferenceConfigSelector,
|
|
49
67
|
Input: () => Input,
|
|
50
68
|
KeyValueList: () => KeyValueList,
|
|
51
69
|
Label: () => Label,
|
|
70
|
+
LoginForm: () => LoginForm,
|
|
52
71
|
PageHeader: () => PageHeader,
|
|
72
|
+
PipelineBuilder: () => PipelineBuilder,
|
|
73
|
+
PipelineRuntimeSelector: () => PipelineRuntimeSelector,
|
|
74
|
+
PipelineStep: () => PipelineStep,
|
|
53
75
|
Popover: () => Popover,
|
|
54
76
|
PopoverContent: () => PopoverContent,
|
|
55
77
|
PopoverTrigger: () => PopoverTrigger,
|
|
@@ -62,6 +84,7 @@ __export(src_exports, {
|
|
|
62
84
|
Skeleton: () => Skeleton,
|
|
63
85
|
StatCard: () => StatCard,
|
|
64
86
|
StatusBadge: () => StatusBadge,
|
|
87
|
+
StepTimings: () => StepTimings,
|
|
65
88
|
Switch: () => Switch,
|
|
66
89
|
Tabs: () => Tabs,
|
|
67
90
|
TabsContent: () => TabsContent,
|
|
@@ -75,10 +98,13 @@ __export(src_exports, {
|
|
|
75
98
|
createTheme: () => createTheme,
|
|
76
99
|
darkColors: () => darkColors,
|
|
77
100
|
defaultTheme: () => defaultTheme,
|
|
101
|
+
getClassColor: () => getClassColor,
|
|
78
102
|
lightColors: () => lightColors,
|
|
103
|
+
mountAddonPage: () => mountAddonPage,
|
|
79
104
|
providerIcons: () => providerIcons,
|
|
80
105
|
statusIcons: () => statusIcons,
|
|
81
106
|
themeToCss: () => themeToCss,
|
|
107
|
+
useDevShell: () => useDevShell,
|
|
82
108
|
useThemeMode: () => useThemeMode
|
|
83
109
|
});
|
|
84
110
|
module.exports = __toCommonJS(src_exports);
|
|
@@ -2020,16 +2046,1716 @@ function DeviceGrid({
|
|
|
2020
2046
|
}
|
|
2021
2047
|
);
|
|
2022
2048
|
}
|
|
2049
|
+
|
|
2050
|
+
// src/composites/pipeline-step.tsx
|
|
2051
|
+
var import_react23 = require("react");
|
|
2052
|
+
var import_lucide_react12 = require("lucide-react");
|
|
2053
|
+
var import_jsx_runtime39 = require("react/jsx-runtime");
|
|
2054
|
+
var ADDON_COLORS = {
|
|
2055
|
+
"object-detection": "border-l-blue-500",
|
|
2056
|
+
"motion-detection": "border-l-amber-500",
|
|
2057
|
+
"face-detection": "border-l-purple-500",
|
|
2058
|
+
"face-recognition": "border-l-violet-500",
|
|
2059
|
+
"plate-detection": "border-l-pink-500",
|
|
2060
|
+
"plate-recognition": "border-l-rose-500",
|
|
2061
|
+
"animal-classifier": "border-l-lime-500",
|
|
2062
|
+
"bird-nabirds-classifier": "border-l-emerald-500",
|
|
2063
|
+
"bird-global-classifier": "border-l-teal-500",
|
|
2064
|
+
"audio-classification": "border-l-green-500",
|
|
2065
|
+
"segmentation-refiner": "border-l-cyan-500"
|
|
2066
|
+
};
|
|
2067
|
+
var SLOT_FALLBACK = {
|
|
2068
|
+
detector: "border-l-blue-500",
|
|
2069
|
+
cropper: "border-l-purple-500",
|
|
2070
|
+
classifier: "border-l-lime-500",
|
|
2071
|
+
refiner: "border-l-cyan-500"
|
|
2072
|
+
};
|
|
2073
|
+
function borderColor(addonId, slot) {
|
|
2074
|
+
return ADDON_COLORS[addonId] ?? SLOT_FALLBACK[slot] ?? "border-l-primary";
|
|
2075
|
+
}
|
|
2076
|
+
var BACKEND_FORMAT = {
|
|
2077
|
+
cpu: "onnx",
|
|
2078
|
+
coreml: "coreml",
|
|
2079
|
+
openvino: "openvino",
|
|
2080
|
+
cuda: "onnx",
|
|
2081
|
+
tensorrt: "onnx",
|
|
2082
|
+
"onnx-py": "onnx",
|
|
2083
|
+
pytorch: "pt"
|
|
2084
|
+
};
|
|
2085
|
+
function backendsForRuntime(runtime, caps, schema) {
|
|
2086
|
+
const allBackends = runtime === "node" ? caps.runtimes.node.backends : caps.runtimes.python.backends;
|
|
2087
|
+
if (!schema) return allBackends;
|
|
2088
|
+
const availableFormats = /* @__PURE__ */ new Set();
|
|
2089
|
+
for (const m of schema.models) {
|
|
2090
|
+
for (const fmt of Object.keys(m.formats)) {
|
|
2091
|
+
availableFormats.add(fmt);
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
if (runtime === "node") availableFormats.add("onnx");
|
|
2095
|
+
return allBackends.map((b) => {
|
|
2096
|
+
const neededFormat = BACKEND_FORMAT[b.id] ?? "onnx";
|
|
2097
|
+
const hasFormat = availableFormats.has(neededFormat);
|
|
2098
|
+
return { ...b, available: b.available && hasFormat };
|
|
2099
|
+
});
|
|
2100
|
+
}
|
|
2101
|
+
function modelsForStep(schema, runtime, backend, caps) {
|
|
2102
|
+
if (!schema) return [];
|
|
2103
|
+
const fmt = runtime === "node" ? "onnx" : caps.runtimes.python.backends.find((b) => b.id === backend)?.modelFormat ?? "onnx";
|
|
2104
|
+
return schema.models.filter((m) => m.formats[fmt]).map((m) => ({
|
|
2105
|
+
id: m.id,
|
|
2106
|
+
name: m.name,
|
|
2107
|
+
downloaded: m.formats[fmt]?.downloaded ?? false
|
|
2108
|
+
}));
|
|
2109
|
+
}
|
|
2110
|
+
function runtimeOptions(caps) {
|
|
2111
|
+
const opts = [
|
|
2112
|
+
{ id: "node", label: "Node.js (ONNX)", available: true }
|
|
2113
|
+
];
|
|
2114
|
+
if (caps.runtimes.python.available && caps.runtimes.python.backends.some((b) => b.available)) {
|
|
2115
|
+
opts.unshift({ id: "python", label: "Python", available: true });
|
|
2116
|
+
}
|
|
2117
|
+
return opts;
|
|
2118
|
+
}
|
|
2119
|
+
function PipelineStep({
|
|
2120
|
+
step,
|
|
2121
|
+
schema,
|
|
2122
|
+
allSchemas,
|
|
2123
|
+
capabilities,
|
|
2124
|
+
depth = 0,
|
|
2125
|
+
onChange,
|
|
2126
|
+
onDelete,
|
|
2127
|
+
readOnly = false
|
|
2128
|
+
}) {
|
|
2129
|
+
const [expanded, setExpanded] = (0, import_react23.useState)(false);
|
|
2130
|
+
const color = borderColor(step.addonId, step.slot);
|
|
2131
|
+
const backends = backendsForRuntime(step.runtime, capabilities, schema);
|
|
2132
|
+
const rtOptions = runtimeOptions(capabilities);
|
|
2133
|
+
const currentBackendAvailable = backends.find((b) => b.id === step.backend)?.available ?? false;
|
|
2134
|
+
if (!currentBackendAvailable && !readOnly) {
|
|
2135
|
+
const firstAvailable = backends.find((b) => b.available);
|
|
2136
|
+
if (firstAvailable && firstAvailable.id !== step.backend) {
|
|
2137
|
+
queueMicrotask(() => onChange({ ...step, backend: firstAvailable.id }));
|
|
2138
|
+
}
|
|
2139
|
+
}
|
|
2140
|
+
const models = modelsForStep(schema, step.runtime, step.backend, capabilities);
|
|
2141
|
+
if (models.length > 0 && !models.some((m) => m.id === step.modelId) && !readOnly) {
|
|
2142
|
+
const defaultModel = schema?.defaultModelId ? models.find((m) => m.id === schema.defaultModelId) : null;
|
|
2143
|
+
const fallback = defaultModel ?? models[0];
|
|
2144
|
+
if (fallback && fallback.id !== step.modelId) {
|
|
2145
|
+
queueMicrotask(() => onChange({ ...step, modelId: fallback.id }));
|
|
2146
|
+
}
|
|
2147
|
+
}
|
|
2148
|
+
function handleClick(e) {
|
|
2149
|
+
if (e.target.closest(".step-config")) return;
|
|
2150
|
+
setExpanded((v) => !v);
|
|
2151
|
+
}
|
|
2152
|
+
return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "space-y-2", children: /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
|
|
2153
|
+
"div",
|
|
2154
|
+
{
|
|
2155
|
+
className: cn(
|
|
2156
|
+
"rounded-xl border border-border bg-surface overflow-hidden border-l-4 shadow-sm",
|
|
2157
|
+
color,
|
|
2158
|
+
!step.enabled && "opacity-[0.45]"
|
|
2159
|
+
),
|
|
2160
|
+
children: [
|
|
2161
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "flex items-center gap-2.5 px-3 py-2.5 cursor-pointer select-none", onClick: handleClick, children: [
|
|
2162
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: "text-foreground-subtle", children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_lucide_react12.ChevronDown, { className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_lucide_react12.ChevronRight, { className: "h-4 w-4" }) }),
|
|
2163
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "flex-1 min-w-0", children: [
|
|
2164
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: "text-[10px] uppercase tracking-wider font-medium text-foreground-subtle/60 block leading-none", children: step.slot }),
|
|
2165
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: "text-sm font-semibold text-foreground truncate block leading-tight", children: step.addonName }),
|
|
2166
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "flex items-center gap-1 mt-0.5 flex-wrap", children: [
|
|
2167
|
+
step.inputClasses.map((c) => /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("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)),
|
|
2168
|
+
step.inputClasses.length > 0 && step.outputClasses.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: "text-foreground-subtle/40 text-[10px]", children: "\u2192" }),
|
|
2169
|
+
step.outputClasses.map((c) => /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("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))
|
|
2170
|
+
] })
|
|
2171
|
+
] }),
|
|
2172
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
2173
|
+
"button",
|
|
2174
|
+
{
|
|
2175
|
+
onClick: (e) => {
|
|
2176
|
+
e.stopPropagation();
|
|
2177
|
+
onChange({ ...step, enabled: !step.enabled });
|
|
2178
|
+
},
|
|
2179
|
+
className: cn(
|
|
2180
|
+
"relative inline-flex h-6 w-11 shrink-0 items-center rounded-full transition-colors",
|
|
2181
|
+
step.enabled ? "bg-success" : "bg-foreground-subtle/30"
|
|
2182
|
+
),
|
|
2183
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: cn(
|
|
2184
|
+
"inline-block h-4 w-4 rounded-full bg-white shadow transition-transform",
|
|
2185
|
+
step.enabled ? "translate-x-6" : "translate-x-1"
|
|
2186
|
+
) })
|
|
2187
|
+
}
|
|
2188
|
+
)
|
|
2189
|
+
] }),
|
|
2190
|
+
expanded && /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "step-config border-t border-border bg-background px-4 py-4 space-y-3", children: [
|
|
2191
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "grid grid-cols-2 gap-3", children: [
|
|
2192
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
2193
|
+
ConfigSelect,
|
|
2194
|
+
{
|
|
2195
|
+
label: "Agent",
|
|
2196
|
+
value: step.agentId,
|
|
2197
|
+
disabled: readOnly,
|
|
2198
|
+
options: [{ value: step.agentId || "hub", label: step.agentId || "Hub (local)" }],
|
|
2199
|
+
onChange: (v) => onChange({ ...step, agentId: v })
|
|
2200
|
+
}
|
|
2201
|
+
),
|
|
2202
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
2203
|
+
ConfigSelect,
|
|
2204
|
+
{
|
|
2205
|
+
label: "Runtime",
|
|
2206
|
+
value: step.runtime,
|
|
2207
|
+
disabled: readOnly,
|
|
2208
|
+
options: rtOptions.map((r) => ({ value: r.id, label: r.label, disabled: !r.available })),
|
|
2209
|
+
onChange: (v) => onChange({ ...step, runtime: v })
|
|
2210
|
+
}
|
|
2211
|
+
),
|
|
2212
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
2213
|
+
ConfigSelect,
|
|
2214
|
+
{
|
|
2215
|
+
label: "Backend",
|
|
2216
|
+
value: step.backend,
|
|
2217
|
+
disabled: readOnly,
|
|
2218
|
+
options: backends.map((b) => ({ value: b.id, label: b.label, disabled: !b.available })),
|
|
2219
|
+
onChange: (v) => onChange({ ...step, backend: v })
|
|
2220
|
+
}
|
|
2221
|
+
),
|
|
2222
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
2223
|
+
ConfigSelect,
|
|
2224
|
+
{
|
|
2225
|
+
label: "Model",
|
|
2226
|
+
value: step.modelId,
|
|
2227
|
+
disabled: readOnly,
|
|
2228
|
+
options: models.map((m) => ({ value: m.id, label: `${m.name}${m.downloaded ? " \u2713" : ""}` })),
|
|
2229
|
+
onChange: (v) => onChange({ ...step, modelId: v })
|
|
2230
|
+
}
|
|
2231
|
+
)
|
|
2232
|
+
] }),
|
|
2233
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { children: [
|
|
2234
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "flex items-center justify-between mb-1", children: [
|
|
2235
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: "text-[10px] font-medium text-foreground-subtle uppercase tracking-wide", children: "Confidence" }),
|
|
2236
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("span", { className: "text-xs font-medium text-foreground tabular-nums", children: [
|
|
2237
|
+
(step.confidence * 100).toFixed(0),
|
|
2238
|
+
"%"
|
|
2239
|
+
] })
|
|
2240
|
+
] }),
|
|
2241
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
2242
|
+
"input",
|
|
2243
|
+
{
|
|
2244
|
+
type: "range",
|
|
2245
|
+
min: 0,
|
|
2246
|
+
max: 1,
|
|
2247
|
+
step: 0.01,
|
|
2248
|
+
value: step.confidence,
|
|
2249
|
+
disabled: readOnly,
|
|
2250
|
+
onChange: (e) => onChange({ ...step, confidence: Number(e.target.value) }),
|
|
2251
|
+
className: "w-full accent-primary h-1.5"
|
|
2252
|
+
}
|
|
2253
|
+
)
|
|
2254
|
+
] })
|
|
2255
|
+
] })
|
|
2256
|
+
]
|
|
2257
|
+
}
|
|
2258
|
+
) });
|
|
2259
|
+
}
|
|
2260
|
+
function ConfigSelect({ label, value, options, disabled, onChange }) {
|
|
2261
|
+
return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { children: [
|
|
2262
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("label", { className: "block text-[10px] font-medium text-foreground-subtle uppercase tracking-wide mb-1.5", children: label }),
|
|
2263
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
|
|
2264
|
+
"select",
|
|
2265
|
+
{
|
|
2266
|
+
value,
|
|
2267
|
+
onChange: (e) => onChange(e.target.value),
|
|
2268
|
+
disabled,
|
|
2269
|
+
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",
|
|
2270
|
+
children: [
|
|
2271
|
+
options.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("option", { value, children: value || "default" }),
|
|
2272
|
+
options.map((o) => /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("option", { value: o.value, disabled: o.disabled, children: o.label }, o.value))
|
|
2273
|
+
]
|
|
2274
|
+
}
|
|
2275
|
+
)
|
|
2276
|
+
] });
|
|
2277
|
+
}
|
|
2278
|
+
|
|
2279
|
+
// src/composites/pipeline-runtime-selector.tsx
|
|
2280
|
+
var import_lucide_react13 = require("lucide-react");
|
|
2281
|
+
var import_jsx_runtime40 = require("react/jsx-runtime");
|
|
2282
|
+
function PipelineRuntimeSelector({ options, value, onChange }) {
|
|
2283
|
+
return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "flex flex-wrap gap-2", children: options.map((opt) => {
|
|
2284
|
+
const active = opt.id === value;
|
|
2285
|
+
return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
|
|
2286
|
+
"button",
|
|
2287
|
+
{
|
|
2288
|
+
onClick: () => opt.available && onChange(opt.id),
|
|
2289
|
+
disabled: !opt.available,
|
|
2290
|
+
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"}`,
|
|
2291
|
+
children: [
|
|
2292
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_lucide_react13.Cpu, { className: "h-3.5 w-3.5 shrink-0" }),
|
|
2293
|
+
opt.label,
|
|
2294
|
+
opt.isBest && /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("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: [
|
|
2295
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_lucide_react13.Star, { className: "h-2.5 w-2.5" }),
|
|
2296
|
+
"Best"
|
|
2297
|
+
] }),
|
|
2298
|
+
opt.platformScore != null && /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("span", { className: "text-[10px] text-foreground-subtle/60", children: [
|
|
2299
|
+
"(",
|
|
2300
|
+
opt.platformScore,
|
|
2301
|
+
")"
|
|
2302
|
+
] }),
|
|
2303
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
2304
|
+
"span",
|
|
2305
|
+
{
|
|
2306
|
+
className: `h-1.5 w-1.5 rounded-full ${opt.available ? "bg-success" : "bg-danger"}`
|
|
2307
|
+
}
|
|
2308
|
+
)
|
|
2309
|
+
]
|
|
2310
|
+
},
|
|
2311
|
+
opt.id
|
|
2312
|
+
);
|
|
2313
|
+
}) });
|
|
2314
|
+
}
|
|
2315
|
+
|
|
2316
|
+
// src/composites/pipeline-builder.tsx
|
|
2317
|
+
var import_react24 = require("react");
|
|
2318
|
+
var import_lucide_react14 = require("lucide-react");
|
|
2319
|
+
|
|
2320
|
+
// src/lib/validate-template.ts
|
|
2321
|
+
function validateTemplate(steps, schema) {
|
|
2322
|
+
const availableAddonIds = new Set(
|
|
2323
|
+
schema.slots.flatMap((s) => s.addons.map((a) => a.id))
|
|
2324
|
+
);
|
|
2325
|
+
const warnings = [];
|
|
2326
|
+
function validateStep(step) {
|
|
2327
|
+
if (!availableAddonIds.has(step.addonId)) {
|
|
2328
|
+
warnings.push(`Addon "${step.addonId}" is no longer available \u2014 step removed`);
|
|
2329
|
+
return null;
|
|
2330
|
+
}
|
|
2331
|
+
const addon = schema.slots.flatMap((s) => s.addons).find((a) => a.id === step.addonId);
|
|
2332
|
+
let modelId = step.modelId;
|
|
2333
|
+
if (addon && !addon.models.some((m) => m.id === modelId)) {
|
|
2334
|
+
const fallback = addon.defaultModelId;
|
|
2335
|
+
warnings.push(`Model "${modelId}" not available for ${step.addonId} \u2014 using "${fallback}"`);
|
|
2336
|
+
modelId = fallback;
|
|
2337
|
+
}
|
|
2338
|
+
const validChildren = step.children.map((c) => validateStep(c)).filter((c) => c !== null);
|
|
2339
|
+
return { ...step, modelId, children: validChildren };
|
|
2340
|
+
}
|
|
2341
|
+
const validSteps = steps.map((s) => validateStep(s)).filter((s) => s !== null);
|
|
2342
|
+
return {
|
|
2343
|
+
valid: warnings.length === 0,
|
|
2344
|
+
steps: validSteps,
|
|
2345
|
+
warnings
|
|
2346
|
+
};
|
|
2347
|
+
}
|
|
2348
|
+
|
|
2349
|
+
// src/composites/pipeline-builder.tsx
|
|
2350
|
+
var import_jsx_runtime41 = require("react/jsx-runtime");
|
|
2351
|
+
function buildSchemaMap(schema) {
|
|
2352
|
+
const map = /* @__PURE__ */ new Map();
|
|
2353
|
+
for (const slot of schema.slots) {
|
|
2354
|
+
for (const addon of slot.addons) {
|
|
2355
|
+
map.set(addon.id, addon);
|
|
2356
|
+
}
|
|
2357
|
+
}
|
|
2358
|
+
return map;
|
|
2359
|
+
}
|
|
2360
|
+
function createDefaultStep(addon, fallbackRuntime, fallbackBackend) {
|
|
2361
|
+
return {
|
|
2362
|
+
addonId: addon.id,
|
|
2363
|
+
addonName: addon.name,
|
|
2364
|
+
slot: addon.slot,
|
|
2365
|
+
inputClasses: [...addon.inputClasses],
|
|
2366
|
+
outputClasses: [...addon.outputClasses],
|
|
2367
|
+
enabled: true,
|
|
2368
|
+
agentId: "hub",
|
|
2369
|
+
// Use per-addon defaults from backend PlatformScorer, fallback to provided
|
|
2370
|
+
runtime: addon.defaultRuntime ?? fallbackRuntime ?? "node",
|
|
2371
|
+
backend: addon.defaultBackend ?? fallbackBackend ?? "cpu",
|
|
2372
|
+
modelId: addon.defaultModelId,
|
|
2373
|
+
confidence: addon.defaultConfidence,
|
|
2374
|
+
classFilters: [...addon.inputClasses],
|
|
2375
|
+
children: []
|
|
2376
|
+
};
|
|
2377
|
+
}
|
|
2378
|
+
function PlaceholderStep({ addon, onClick }) {
|
|
2379
|
+
return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
2380
|
+
"button",
|
|
2381
|
+
{
|
|
2382
|
+
type: "button",
|
|
2383
|
+
onClick,
|
|
2384
|
+
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",
|
|
2385
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "flex items-center gap-3", children: [
|
|
2386
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_lucide_react14.PlusCircle, { className: "h-[18px] w-[18px] text-foreground-subtle/30 group-hover:text-primary/60 shrink-0" }),
|
|
2387
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "flex-1 min-w-0", children: [
|
|
2388
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "text-[13px] font-medium text-foreground-subtle/50 group-hover:text-foreground-subtle block truncate", children: addon.name }),
|
|
2389
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "flex items-center gap-1 mt-0.5 flex-wrap", children: [
|
|
2390
|
+
addon.inputClasses.map((c) => /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("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)),
|
|
2391
|
+
addon.inputClasses.length > 0 && addon.outputClasses.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "text-foreground-subtle/25 text-[10px]", children: "\u2192" }),
|
|
2392
|
+
addon.outputClasses.map((c) => /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("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))
|
|
2393
|
+
] })
|
|
2394
|
+
] })
|
|
2395
|
+
] })
|
|
2396
|
+
}
|
|
2397
|
+
);
|
|
2398
|
+
}
|
|
2399
|
+
function PipelineBuilder({
|
|
2400
|
+
schema,
|
|
2401
|
+
capabilities,
|
|
2402
|
+
steps,
|
|
2403
|
+
onChange,
|
|
2404
|
+
templates,
|
|
2405
|
+
selectedTemplateId,
|
|
2406
|
+
onSelectTemplate,
|
|
2407
|
+
onSaveTemplate,
|
|
2408
|
+
onUpdateTemplate,
|
|
2409
|
+
onDeleteTemplate,
|
|
2410
|
+
readOnly = false,
|
|
2411
|
+
excludeAddons = []
|
|
2412
|
+
}) {
|
|
2413
|
+
const excluded = (0, import_react24.useMemo)(() => new Set(excludeAddons), [excludeAddons]);
|
|
2414
|
+
const schemaMap = (0, import_react24.useMemo)(() => buildSchemaMap(schema), [schema]);
|
|
2415
|
+
const [warnings, setWarnings] = (0, import_react24.useState)([]);
|
|
2416
|
+
const bestPlatformScore = capabilities.platformScores?.find((s) => s.available);
|
|
2417
|
+
const hasPython = capabilities.runtimes.python.available && capabilities.runtimes.python.backends.some((b) => b.available);
|
|
2418
|
+
const defaultRuntime = bestPlatformScore?.runtime ?? (hasPython ? "python" : "node");
|
|
2419
|
+
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");
|
|
2420
|
+
const dirty = selectedTemplateId ? JSON.stringify(steps) !== JSON.stringify(templates.find((t) => t.id === selectedTemplateId)?.steps) : false;
|
|
2421
|
+
function handleSelectTemplate(e) {
|
|
2422
|
+
const id = e.target.value || null;
|
|
2423
|
+
if (id) {
|
|
2424
|
+
const tpl = templates.find((t) => t.id === id);
|
|
2425
|
+
if (tpl) {
|
|
2426
|
+
const result = validateTemplate(tpl.steps, schema);
|
|
2427
|
+
setWarnings([...result.warnings]);
|
|
2428
|
+
}
|
|
2429
|
+
} else {
|
|
2430
|
+
setWarnings([]);
|
|
2431
|
+
}
|
|
2432
|
+
onSelectTemplate(id);
|
|
2433
|
+
}
|
|
2434
|
+
function handleSave() {
|
|
2435
|
+
if (selectedTemplateId) onUpdateTemplate(selectedTemplateId, steps);
|
|
2436
|
+
}
|
|
2437
|
+
function handleSaveAs() {
|
|
2438
|
+
const name = window.prompt("Template name:");
|
|
2439
|
+
if (name?.trim()) onSaveTemplate(name.trim(), steps);
|
|
2440
|
+
}
|
|
2441
|
+
function handleDelete() {
|
|
2442
|
+
if (!selectedTemplateId) return;
|
|
2443
|
+
const tpl = templates.find((t) => t.id === selectedTemplateId);
|
|
2444
|
+
if (tpl && window.confirm(`Delete template "${tpl.name}"?`)) {
|
|
2445
|
+
onDeleteTemplate(selectedTemplateId);
|
|
2446
|
+
}
|
|
2447
|
+
}
|
|
2448
|
+
function handleStepChange(updated) {
|
|
2449
|
+
onChange(steps.map((s) => {
|
|
2450
|
+
if (s.addonId !== updated.addonId) return s;
|
|
2451
|
+
return autoEnableAncestors(updated);
|
|
2452
|
+
}));
|
|
2453
|
+
}
|
|
2454
|
+
function autoEnableAncestors(step) {
|
|
2455
|
+
const hasEnabledChild = step.children.some((c) => c.enabled || c.children.some((gc) => gc.enabled));
|
|
2456
|
+
return {
|
|
2457
|
+
...step,
|
|
2458
|
+
enabled: step.enabled || hasEnabledChild,
|
|
2459
|
+
children: step.children.map((c) => {
|
|
2460
|
+
const hasEnabledGrandchild = c.children.some((gc) => gc.enabled);
|
|
2461
|
+
return {
|
|
2462
|
+
...c,
|
|
2463
|
+
enabled: c.enabled || hasEnabledGrandchild
|
|
2464
|
+
};
|
|
2465
|
+
})
|
|
2466
|
+
};
|
|
2467
|
+
}
|
|
2468
|
+
function handleAddChildToStep(parentAddonId, addon) {
|
|
2469
|
+
const child = createDefaultStep(addon, defaultRuntime, defaultBackend);
|
|
2470
|
+
onChange(steps.map((s) => {
|
|
2471
|
+
if (s.addonId !== parentAddonId) return s;
|
|
2472
|
+
return { ...s, children: [...s.children, child] };
|
|
2473
|
+
}));
|
|
2474
|
+
}
|
|
2475
|
+
function collectIds(list) {
|
|
2476
|
+
const ids = /* @__PURE__ */ new Set();
|
|
2477
|
+
for (const s of list) {
|
|
2478
|
+
ids.add(s.addonId);
|
|
2479
|
+
for (const c of s.children) ids.add(c.addonId);
|
|
2480
|
+
const childIds = collectIds(s.children);
|
|
2481
|
+
for (const id of childIds) ids.add(id);
|
|
2482
|
+
}
|
|
2483
|
+
return ids;
|
|
2484
|
+
}
|
|
2485
|
+
const existingIds = collectIds(steps);
|
|
2486
|
+
function getChildPlaceholders(step) {
|
|
2487
|
+
const stepSchema = schemaMap.get(step.addonId);
|
|
2488
|
+
if (!stepSchema) return [];
|
|
2489
|
+
const childSlotIds = stepSchema.childSlots;
|
|
2490
|
+
const childIds = new Set(step.children.map((c) => c.addonId));
|
|
2491
|
+
const placeholders = [];
|
|
2492
|
+
for (const slot of schema.slots) {
|
|
2493
|
+
if (!childSlotIds.includes(slot.id)) continue;
|
|
2494
|
+
for (const addon of slot.addons) {
|
|
2495
|
+
if (childIds.has(addon.id) || excluded.has(addon.id)) continue;
|
|
2496
|
+
const compatible = addon.inputClasses.some((ic) => step.outputClasses.includes(ic));
|
|
2497
|
+
if (compatible) placeholders.push(addon);
|
|
2498
|
+
}
|
|
2499
|
+
}
|
|
2500
|
+
return placeholders;
|
|
2501
|
+
}
|
|
2502
|
+
function renderStep(step) {
|
|
2503
|
+
const childPlaceholders = getChildPlaceholders(step);
|
|
2504
|
+
return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "space-y-1.5", children: [
|
|
2505
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
2506
|
+
PipelineStep,
|
|
2507
|
+
{
|
|
2508
|
+
step,
|
|
2509
|
+
schema: schemaMap.get(step.addonId) ?? null,
|
|
2510
|
+
allSchemas: schemaMap,
|
|
2511
|
+
capabilities,
|
|
2512
|
+
onChange: handleStepChange,
|
|
2513
|
+
onDelete: readOnly ? void 0 : (id) => onChange(steps.filter((s) => s.addonId !== id)),
|
|
2514
|
+
readOnly
|
|
2515
|
+
}
|
|
2516
|
+
),
|
|
2517
|
+
(step.children.length > 0 || childPlaceholders.length > 0) && /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "ml-6 pl-4 border-l-2 border-dashed border-border/40 space-y-1.5", children: [
|
|
2518
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "text-[10px] font-semibold uppercase tracking-widest text-foreground-subtle/40", children: "Slot: Cropper / Classifier" }),
|
|
2519
|
+
step.children.map((child) => {
|
|
2520
|
+
const childChildPlaceholders = getChildPlaceholders(child);
|
|
2521
|
+
return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "space-y-1.5", children: [
|
|
2522
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
2523
|
+
PipelineStep,
|
|
2524
|
+
{
|
|
2525
|
+
step: child,
|
|
2526
|
+
schema: schemaMap.get(child.addonId) ?? null,
|
|
2527
|
+
allSchemas: schemaMap,
|
|
2528
|
+
capabilities,
|
|
2529
|
+
depth: 1,
|
|
2530
|
+
onChange: (updated) => {
|
|
2531
|
+
handleStepChange({
|
|
2532
|
+
...step,
|
|
2533
|
+
children: step.children.map((c) => c.addonId === updated.addonId ? updated : c)
|
|
2534
|
+
});
|
|
2535
|
+
},
|
|
2536
|
+
onDelete: readOnly ? void 0 : (id) => {
|
|
2537
|
+
handleStepChange({
|
|
2538
|
+
...step,
|
|
2539
|
+
children: step.children.filter((c) => c.addonId !== id)
|
|
2540
|
+
});
|
|
2541
|
+
},
|
|
2542
|
+
readOnly
|
|
2543
|
+
}
|
|
2544
|
+
),
|
|
2545
|
+
(child.children.length > 0 || childChildPlaceholders.length > 0) && /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "ml-6 pl-4 border-l-2 border-dashed border-border/30 space-y-1.5", children: [
|
|
2546
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "text-[10px] font-semibold uppercase tracking-widest text-foreground-subtle/30", children: "Slot: Recognizer" }),
|
|
2547
|
+
child.children.map((grandchild) => /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
2548
|
+
PipelineStep,
|
|
2549
|
+
{
|
|
2550
|
+
step: grandchild,
|
|
2551
|
+
schema: schemaMap.get(grandchild.addonId) ?? null,
|
|
2552
|
+
allSchemas: schemaMap,
|
|
2553
|
+
capabilities,
|
|
2554
|
+
depth: 2,
|
|
2555
|
+
onChange: (updatedGrandchild) => {
|
|
2556
|
+
handleStepChange({
|
|
2557
|
+
...step,
|
|
2558
|
+
children: step.children.map(
|
|
2559
|
+
(c) => c.addonId === child.addonId ? { ...c, children: c.children.map((gc) => gc.addonId === updatedGrandchild.addonId ? updatedGrandchild : gc) } : c
|
|
2560
|
+
)
|
|
2561
|
+
});
|
|
2562
|
+
},
|
|
2563
|
+
onDelete: readOnly ? void 0 : (id) => {
|
|
2564
|
+
handleStepChange({
|
|
2565
|
+
...step,
|
|
2566
|
+
children: step.children.map(
|
|
2567
|
+
(c) => c.addonId === child.addonId ? { ...c, children: c.children.filter((gc) => gc.addonId !== id) } : c
|
|
2568
|
+
)
|
|
2569
|
+
});
|
|
2570
|
+
},
|
|
2571
|
+
readOnly
|
|
2572
|
+
},
|
|
2573
|
+
grandchild.addonId
|
|
2574
|
+
)),
|
|
2575
|
+
!readOnly && childChildPlaceholders.map((addon) => /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
2576
|
+
PlaceholderStep,
|
|
2577
|
+
{
|
|
2578
|
+
addon,
|
|
2579
|
+
onClick: () => {
|
|
2580
|
+
const newChild = createDefaultStep(addon, defaultRuntime, defaultBackend);
|
|
2581
|
+
handleStepChange({
|
|
2582
|
+
...step,
|
|
2583
|
+
children: step.children.map(
|
|
2584
|
+
(c) => c.addonId === child.addonId ? { ...c, children: [...c.children, newChild] } : c
|
|
2585
|
+
)
|
|
2586
|
+
});
|
|
2587
|
+
}
|
|
2588
|
+
},
|
|
2589
|
+
addon.id
|
|
2590
|
+
))
|
|
2591
|
+
] })
|
|
2592
|
+
] }, child.addonId);
|
|
2593
|
+
}),
|
|
2594
|
+
!readOnly && childPlaceholders.map((addon) => /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
2595
|
+
PlaceholderStep,
|
|
2596
|
+
{
|
|
2597
|
+
addon,
|
|
2598
|
+
onClick: () => handleAddChildToStep(step.addonId, addon)
|
|
2599
|
+
},
|
|
2600
|
+
addon.id
|
|
2601
|
+
))
|
|
2602
|
+
] })
|
|
2603
|
+
] }, step.addonId);
|
|
2604
|
+
}
|
|
2605
|
+
const rootSlots = schema.slots.filter((s) => s.parentSlot === null).sort((a, b) => a.priority - b.priority);
|
|
2606
|
+
return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "space-y-4", children: [
|
|
2607
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "rounded-xl border border-border bg-surface p-3", children: /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
2608
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "relative flex-1 min-w-0", children: /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(
|
|
2609
|
+
"select",
|
|
2610
|
+
{
|
|
2611
|
+
value: selectedTemplateId ?? "",
|
|
2612
|
+
onChange: handleSelectTemplate,
|
|
2613
|
+
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",
|
|
2614
|
+
children: [
|
|
2615
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("option", { value: "", children: "No template" }),
|
|
2616
|
+
templates.map((t) => /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("option", { value: t.id, children: t.name }, t.id))
|
|
2617
|
+
]
|
|
2618
|
+
}
|
|
2619
|
+
) }),
|
|
2620
|
+
dirty && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "h-1.5 w-1.5 rounded-full bg-amber-500 shrink-0" }),
|
|
2621
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
2622
|
+
"button",
|
|
2623
|
+
{
|
|
2624
|
+
onClick: handleSave,
|
|
2625
|
+
disabled: !selectedTemplateId || readOnly,
|
|
2626
|
+
title: "Save",
|
|
2627
|
+
className: cn(
|
|
2628
|
+
"p-2 rounded-lg border border-border transition-colors",
|
|
2629
|
+
selectedTemplateId && !readOnly ? "text-foreground-subtle hover:bg-surface-hover" : "text-foreground-subtle/30 cursor-not-allowed"
|
|
2630
|
+
),
|
|
2631
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_lucide_react14.Save, { className: "h-4 w-4" })
|
|
2632
|
+
}
|
|
2633
|
+
),
|
|
2634
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
2635
|
+
"button",
|
|
2636
|
+
{
|
|
2637
|
+
onClick: handleSaveAs,
|
|
2638
|
+
disabled: readOnly,
|
|
2639
|
+
title: "Save As",
|
|
2640
|
+
className: cn(
|
|
2641
|
+
"p-2 rounded-lg border border-border transition-colors",
|
|
2642
|
+
!readOnly ? "text-foreground-subtle hover:bg-surface-hover" : "text-foreground-subtle/30 cursor-not-allowed"
|
|
2643
|
+
),
|
|
2644
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_lucide_react14.CopyPlus, { className: "h-4 w-4" })
|
|
2645
|
+
}
|
|
2646
|
+
),
|
|
2647
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
2648
|
+
"button",
|
|
2649
|
+
{
|
|
2650
|
+
onClick: handleDelete,
|
|
2651
|
+
disabled: !selectedTemplateId || readOnly,
|
|
2652
|
+
title: "Delete",
|
|
2653
|
+
className: cn(
|
|
2654
|
+
"p-2 rounded-lg border border-border transition-colors",
|
|
2655
|
+
selectedTemplateId && !readOnly ? "text-foreground-subtle hover:text-danger" : "text-foreground-subtle/30 cursor-not-allowed"
|
|
2656
|
+
),
|
|
2657
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_lucide_react14.Trash2, { className: "h-4 w-4" })
|
|
2658
|
+
}
|
|
2659
|
+
)
|
|
2660
|
+
] }) }),
|
|
2661
|
+
warnings.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "rounded-lg border border-amber-500/30 bg-amber-500/5 p-3 text-xs text-amber-400 space-y-1", children: [
|
|
2662
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
2663
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "font-medium", children: "Template loaded with warnings:" }),
|
|
2664
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("button", { onClick: () => setWarnings([]), className: "text-amber-400/60 hover:text-amber-400", children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_lucide_react14.X, { className: "h-3.5 w-3.5" }) })
|
|
2665
|
+
] }),
|
|
2666
|
+
warnings.map((w, i) => /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { children: [
|
|
2667
|
+
"\u2022 ",
|
|
2668
|
+
w
|
|
2669
|
+
] }, i))
|
|
2670
|
+
] }),
|
|
2671
|
+
rootSlots.map((slot) => {
|
|
2672
|
+
const slotSteps = steps.filter((s) => s.slot === slot.id && !excluded.has(s.addonId));
|
|
2673
|
+
const missingRootAddons = slot.addons.filter((a) => !existingIds.has(a.id) && !excluded.has(a.id));
|
|
2674
|
+
return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "space-y-2", children: [
|
|
2675
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { className: "text-[10px] font-semibold uppercase tracking-widest text-foreground-subtle/50", children: [
|
|
2676
|
+
"Slot: ",
|
|
2677
|
+
slot.label
|
|
2678
|
+
] }),
|
|
2679
|
+
slotSteps.map((step) => renderStep(step)),
|
|
2680
|
+
!readOnly && missingRootAddons.map((addon) => /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(PlaceholderStep, { addon, onClick: () => {
|
|
2681
|
+
onChange([...steps, createDefaultStep(addon, defaultRuntime, defaultBackend)]);
|
|
2682
|
+
} }, addon.id))
|
|
2683
|
+
] }, slot.id);
|
|
2684
|
+
})
|
|
2685
|
+
] });
|
|
2686
|
+
}
|
|
2687
|
+
|
|
2688
|
+
// src/composites/detection-colors.ts
|
|
2689
|
+
var CLASS_COLORS = {
|
|
2690
|
+
// Primary detection classes
|
|
2691
|
+
person: "#3b82f6",
|
|
2692
|
+
// blue-500
|
|
2693
|
+
vehicle: "#f59e0b",
|
|
2694
|
+
// amber-500
|
|
2695
|
+
animal: "#22c55e",
|
|
2696
|
+
// green-500
|
|
2697
|
+
// Sub-detection classes
|
|
2698
|
+
face: "#a855f7",
|
|
2699
|
+
// purple-500
|
|
2700
|
+
plate: "#ec4899",
|
|
2701
|
+
// pink-500
|
|
2702
|
+
// Specific animal types
|
|
2703
|
+
bird: "#14b8a6",
|
|
2704
|
+
// teal-500
|
|
2705
|
+
dog: "#84cc16",
|
|
2706
|
+
// lime-500
|
|
2707
|
+
cat: "#f97316",
|
|
2708
|
+
// orange-500
|
|
2709
|
+
// Specific vehicle types
|
|
2710
|
+
car: "#f59e0b",
|
|
2711
|
+
// amber-500
|
|
2712
|
+
truck: "#d97706",
|
|
2713
|
+
// amber-600
|
|
2714
|
+
bus: "#b45309",
|
|
2715
|
+
// amber-700
|
|
2716
|
+
motorcycle: "#eab308",
|
|
2717
|
+
// yellow-500
|
|
2718
|
+
bicycle: "#ca8a04",
|
|
2719
|
+
// yellow-600
|
|
2720
|
+
// Other
|
|
2721
|
+
motion: "#facc15"
|
|
2722
|
+
// yellow-400
|
|
2723
|
+
};
|
|
2724
|
+
var FALLBACK_PALETTE = [
|
|
2725
|
+
"#ef4444",
|
|
2726
|
+
// red-500
|
|
2727
|
+
"#8b5cf6",
|
|
2728
|
+
// violet-500
|
|
2729
|
+
"#06b6d4",
|
|
2730
|
+
// cyan-500
|
|
2731
|
+
"#f97316",
|
|
2732
|
+
// orange-500
|
|
2733
|
+
"#10b981",
|
|
2734
|
+
// emerald-500
|
|
2735
|
+
"#6366f1",
|
|
2736
|
+
// indigo-500
|
|
2737
|
+
"#e11d48",
|
|
2738
|
+
// rose-600
|
|
2739
|
+
"#0891b2",
|
|
2740
|
+
// cyan-600
|
|
2741
|
+
"#7c3aed",
|
|
2742
|
+
// violet-600
|
|
2743
|
+
"#059669"
|
|
2744
|
+
// emerald-600
|
|
2745
|
+
];
|
|
2746
|
+
var DEFAULT_COLOR = "#f59e42";
|
|
2747
|
+
function getClassColor(className, customColors) {
|
|
2748
|
+
if (customColors?.[className]) return customColors[className];
|
|
2749
|
+
if (customColors?.[className.toLowerCase()]) return customColors[className.toLowerCase()];
|
|
2750
|
+
if (CLASS_COLORS[className]) return CLASS_COLORS[className];
|
|
2751
|
+
if (CLASS_COLORS[className.toLowerCase()]) return CLASS_COLORS[className.toLowerCase()];
|
|
2752
|
+
let hash = 0;
|
|
2753
|
+
for (let i = 0; i < className.length; i++) {
|
|
2754
|
+
hash = hash * 31 + (className.codePointAt(i) ?? 0) >>> 0;
|
|
2755
|
+
}
|
|
2756
|
+
return FALLBACK_PALETTE[hash % FALLBACK_PALETTE.length] ?? DEFAULT_COLOR;
|
|
2757
|
+
}
|
|
2758
|
+
|
|
2759
|
+
// src/composites/detection-canvas.tsx
|
|
2760
|
+
var import_react25 = require("react");
|
|
2761
|
+
var import_jsx_runtime42 = require("react/jsx-runtime");
|
|
2762
|
+
var DEFAULT_CLASS_COLORS = CLASS_COLORS;
|
|
2763
|
+
function DetectionCanvas({
|
|
2764
|
+
src,
|
|
2765
|
+
imageWidth,
|
|
2766
|
+
imageHeight,
|
|
2767
|
+
detections = [],
|
|
2768
|
+
classColors,
|
|
2769
|
+
aspectRatio,
|
|
2770
|
+
className,
|
|
2771
|
+
placeholder,
|
|
2772
|
+
showConfidence = true,
|
|
2773
|
+
minConfidence = 0,
|
|
2774
|
+
borderWidth = 2
|
|
2775
|
+
}) {
|
|
2776
|
+
function getColor(className2) {
|
|
2777
|
+
return getClassColor(className2, classColors);
|
|
2778
|
+
}
|
|
2779
|
+
const ratio = aspectRatio ?? (imageWidth && imageHeight ? `${imageWidth}/${imageHeight}` : "16/9");
|
|
2780
|
+
const filteredDetections = detections.filter((d) => d.confidence >= minConfidence);
|
|
2781
|
+
return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
2782
|
+
"div",
|
|
2783
|
+
{
|
|
2784
|
+
className: cn(
|
|
2785
|
+
"rounded-lg border border-border bg-surface overflow-hidden relative",
|
|
2786
|
+
className
|
|
2787
|
+
),
|
|
2788
|
+
style: { aspectRatio: ratio },
|
|
2789
|
+
children: src ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_jsx_runtime42.Fragment, { children: [
|
|
2790
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)("img", { src, className: "absolute inset-0 w-full h-full object-fill", alt: "" }),
|
|
2791
|
+
filteredDetections.map(
|
|
2792
|
+
(d, i) => d.mask && d.maskWidth && d.maskHeight ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
2793
|
+
MaskOverlay,
|
|
2794
|
+
{
|
|
2795
|
+
mask: d.mask,
|
|
2796
|
+
maskWidth: d.maskWidth,
|
|
2797
|
+
maskHeight: d.maskHeight,
|
|
2798
|
+
bbox: d.bbox,
|
|
2799
|
+
imageWidth,
|
|
2800
|
+
imageHeight,
|
|
2801
|
+
color: getColor(d.className)
|
|
2802
|
+
},
|
|
2803
|
+
`mask-${i}`
|
|
2804
|
+
) : null
|
|
2805
|
+
),
|
|
2806
|
+
filteredDetections.map((d, i) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
2807
|
+
BoundingBox,
|
|
2808
|
+
{
|
|
2809
|
+
detection: d,
|
|
2810
|
+
imageWidth,
|
|
2811
|
+
imageHeight,
|
|
2812
|
+
color: getColor(d.className),
|
|
2813
|
+
showConfidence,
|
|
2814
|
+
borderWidth: d.mask ? 1 : borderWidth,
|
|
2815
|
+
children: d.children?.filter((c) => {
|
|
2816
|
+
if (c.confidence < minConfidence) return false;
|
|
2817
|
+
const [cx1, cy1, cx2, cy2] = c.bbox;
|
|
2818
|
+
const cw = cx2 - cx1;
|
|
2819
|
+
const ch = cy2 - cy1;
|
|
2820
|
+
if (cw <= 0 || ch <= 0) return false;
|
|
2821
|
+
const [px1, py1, px2, py2] = d.bbox;
|
|
2822
|
+
const pw = px2 - px1;
|
|
2823
|
+
const ph = py2 - py1;
|
|
2824
|
+
if (pw > 0 && ph > 0 && cw * ch / (pw * ph) > 0.8) return false;
|
|
2825
|
+
return true;
|
|
2826
|
+
}).map((child, j) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
2827
|
+
ChildBoundingBox,
|
|
2828
|
+
{
|
|
2829
|
+
child,
|
|
2830
|
+
parentBbox: d.bbox,
|
|
2831
|
+
color: getColor(child.className),
|
|
2832
|
+
showConfidence
|
|
2833
|
+
},
|
|
2834
|
+
`child-${j}`
|
|
2835
|
+
))
|
|
2836
|
+
},
|
|
2837
|
+
`det-${i}`
|
|
2838
|
+
))
|
|
2839
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: "w-full h-full flex items-center justify-center text-foreground-subtle text-sm", children: placeholder ?? "No image loaded" })
|
|
2840
|
+
}
|
|
2841
|
+
);
|
|
2842
|
+
}
|
|
2843
|
+
function BoundingBox({
|
|
2844
|
+
detection,
|
|
2845
|
+
imageWidth,
|
|
2846
|
+
imageHeight,
|
|
2847
|
+
color,
|
|
2848
|
+
showConfidence,
|
|
2849
|
+
borderWidth,
|
|
2850
|
+
children
|
|
2851
|
+
}) {
|
|
2852
|
+
const [x1, y1, x2, y2] = detection.bbox;
|
|
2853
|
+
const labelCount = 1 + (detection.labelsData?.length ?? 0);
|
|
2854
|
+
const labelHeightPx = labelCount * 16 + 4;
|
|
2855
|
+
const topPct = y1 / imageHeight * 100;
|
|
2856
|
+
const containerRef = (0, import_react25.useRef)(null);
|
|
2857
|
+
const showBelow = topPct < labelHeightPx / imageHeight * 100 * 1.5;
|
|
2858
|
+
const labelsElement = /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
|
|
2859
|
+
"div",
|
|
2860
|
+
{
|
|
2861
|
+
className: `absolute left-0 flex flex-col items-start gap-px ${showBelow ? "" : ""}`,
|
|
2862
|
+
style: showBelow ? { top: "100%", marginTop: "2px" } : { bottom: "100%", marginBottom: "2px" },
|
|
2863
|
+
children: [
|
|
2864
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
|
|
2865
|
+
"span",
|
|
2866
|
+
{
|
|
2867
|
+
className: "text-[10px] px-1 rounded-sm whitespace-nowrap text-white",
|
|
2868
|
+
style: { backgroundColor: color },
|
|
2869
|
+
children: [
|
|
2870
|
+
detection.className,
|
|
2871
|
+
showConfidence && ` ${(detection.confidence * 100).toFixed(0)}%`
|
|
2872
|
+
]
|
|
2873
|
+
}
|
|
2874
|
+
),
|
|
2875
|
+
detection.labelsData?.map((l, k) => /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
|
|
2876
|
+
"span",
|
|
2877
|
+
{
|
|
2878
|
+
className: "text-[9px] font-semibold px-1 rounded-sm whitespace-nowrap text-white",
|
|
2879
|
+
style: { backgroundColor: getClassColor(l.addonId ?? l.label) },
|
|
2880
|
+
children: [
|
|
2881
|
+
l.label,
|
|
2882
|
+
" ",
|
|
2883
|
+
(l.score * 100).toFixed(0),
|
|
2884
|
+
"%"
|
|
2885
|
+
]
|
|
2886
|
+
},
|
|
2887
|
+
k
|
|
2888
|
+
))
|
|
2889
|
+
]
|
|
2890
|
+
}
|
|
2891
|
+
);
|
|
2892
|
+
return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
|
|
2893
|
+
"div",
|
|
2894
|
+
{
|
|
2895
|
+
ref: containerRef,
|
|
2896
|
+
className: "absolute rounded-sm",
|
|
2897
|
+
style: {
|
|
2898
|
+
left: `${x1 / imageWidth * 100}%`,
|
|
2899
|
+
top: `${y1 / imageHeight * 100}%`,
|
|
2900
|
+
width: `${(x2 - x1) / imageWidth * 100}%`,
|
|
2901
|
+
height: `${(y2 - y1) / imageHeight * 100}%`,
|
|
2902
|
+
borderWidth: `${borderWidth}px`,
|
|
2903
|
+
borderStyle: "solid",
|
|
2904
|
+
borderColor: color
|
|
2905
|
+
},
|
|
2906
|
+
children: [
|
|
2907
|
+
labelsElement,
|
|
2908
|
+
children
|
|
2909
|
+
]
|
|
2910
|
+
}
|
|
2911
|
+
);
|
|
2912
|
+
}
|
|
2913
|
+
function MaskOverlay({
|
|
2914
|
+
mask,
|
|
2915
|
+
maskWidth,
|
|
2916
|
+
maskHeight,
|
|
2917
|
+
bbox,
|
|
2918
|
+
imageWidth,
|
|
2919
|
+
imageHeight,
|
|
2920
|
+
color
|
|
2921
|
+
}) {
|
|
2922
|
+
const canvasRef = (0, import_react25.useRef)(null);
|
|
2923
|
+
(0, import_react25.useEffect)(() => {
|
|
2924
|
+
const canvas = canvasRef.current;
|
|
2925
|
+
if (!canvas) return;
|
|
2926
|
+
const ctx = canvas.getContext("2d");
|
|
2927
|
+
if (!ctx) return;
|
|
2928
|
+
const binary = atob(mask);
|
|
2929
|
+
const bytes = new Uint8Array(binary.length);
|
|
2930
|
+
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
2931
|
+
const r = parseInt(color.slice(1, 3), 16);
|
|
2932
|
+
const g = parseInt(color.slice(3, 5), 16);
|
|
2933
|
+
const b = parseInt(color.slice(5, 7), 16);
|
|
2934
|
+
canvas.width = maskWidth;
|
|
2935
|
+
canvas.height = maskHeight;
|
|
2936
|
+
const imageData = ctx.createImageData(maskWidth, maskHeight);
|
|
2937
|
+
const totalPixels = maskWidth * maskHeight;
|
|
2938
|
+
for (let i = 0; i < totalPixels; i++) {
|
|
2939
|
+
const val = i < bytes.length ? bytes[i] : 0;
|
|
2940
|
+
const px = i * 4;
|
|
2941
|
+
if (val > 0) {
|
|
2942
|
+
imageData.data[px] = r;
|
|
2943
|
+
imageData.data[px + 1] = g;
|
|
2944
|
+
imageData.data[px + 2] = b;
|
|
2945
|
+
imageData.data[px + 3] = 120;
|
|
2946
|
+
}
|
|
2947
|
+
}
|
|
2948
|
+
ctx.putImageData(imageData, 0, 0);
|
|
2949
|
+
}, [mask, maskWidth, maskHeight, color]);
|
|
2950
|
+
const [x1, y1, x2, y2] = bbox;
|
|
2951
|
+
return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
2952
|
+
"canvas",
|
|
2953
|
+
{
|
|
2954
|
+
ref: canvasRef,
|
|
2955
|
+
className: "absolute pointer-events-none",
|
|
2956
|
+
style: {
|
|
2957
|
+
left: `${x1 / imageWidth * 100}%`,
|
|
2958
|
+
top: `${y1 / imageHeight * 100}%`,
|
|
2959
|
+
width: `${(x2 - x1) / imageWidth * 100}%`,
|
|
2960
|
+
height: `${(y2 - y1) / imageHeight * 100}%`,
|
|
2961
|
+
imageRendering: "pixelated"
|
|
2962
|
+
}
|
|
2963
|
+
}
|
|
2964
|
+
);
|
|
2965
|
+
}
|
|
2966
|
+
function ChildBoundingBox({
|
|
2967
|
+
child,
|
|
2968
|
+
parentBbox,
|
|
2969
|
+
color,
|
|
2970
|
+
showConfidence
|
|
2971
|
+
}) {
|
|
2972
|
+
const [px1, py1, px2, py2] = parentBbox;
|
|
2973
|
+
const [cx1, cy1, cx2, cy2] = child.bbox;
|
|
2974
|
+
const pw = px2 - px1;
|
|
2975
|
+
const ph = py2 - py1;
|
|
2976
|
+
if (pw <= 0 || ph <= 0) return null;
|
|
2977
|
+
return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
2978
|
+
"div",
|
|
2979
|
+
{
|
|
2980
|
+
className: "absolute rounded-sm",
|
|
2981
|
+
style: {
|
|
2982
|
+
left: `${Math.max(0, (cx1 - px1) / pw * 100)}%`,
|
|
2983
|
+
top: `${Math.max(0, (cy1 - py1) / ph * 100)}%`,
|
|
2984
|
+
width: `${Math.min(100, (cx2 - cx1) / pw * 100)}%`,
|
|
2985
|
+
height: `${Math.min(100, (cy2 - cy1) / ph * 100)}%`,
|
|
2986
|
+
borderWidth: "1px",
|
|
2987
|
+
borderStyle: "solid",
|
|
2988
|
+
borderColor: color
|
|
2989
|
+
},
|
|
2990
|
+
children: (() => {
|
|
2991
|
+
const labelCount = 1 + (child.labelsData?.length ?? 0);
|
|
2992
|
+
const relTop = (cy1 - py1) / ph * 100;
|
|
2993
|
+
const showBelow = relTop < labelCount * 6;
|
|
2994
|
+
return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
|
|
2995
|
+
"div",
|
|
2996
|
+
{
|
|
2997
|
+
className: "absolute left-0 flex flex-col items-start gap-px",
|
|
2998
|
+
style: showBelow ? { top: "100%", marginTop: "1px" } : { bottom: "100%", marginBottom: "1px" },
|
|
2999
|
+
children: [
|
|
3000
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
|
|
3001
|
+
"span",
|
|
3002
|
+
{
|
|
3003
|
+
className: "text-[9px] px-0.5 rounded-sm whitespace-nowrap text-white",
|
|
3004
|
+
style: { backgroundColor: color },
|
|
3005
|
+
children: [
|
|
3006
|
+
child.className,
|
|
3007
|
+
showConfidence && ` ${(child.confidence * 100).toFixed(0)}%`
|
|
3008
|
+
]
|
|
3009
|
+
}
|
|
3010
|
+
),
|
|
3011
|
+
child.labelsData?.map((l, k) => /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
|
|
3012
|
+
"span",
|
|
3013
|
+
{
|
|
3014
|
+
className: "text-[8px] font-semibold px-0.5 rounded-sm whitespace-nowrap text-white",
|
|
3015
|
+
style: { backgroundColor: getClassColor(l.addonId ?? l.label) },
|
|
3016
|
+
children: [
|
|
3017
|
+
l.label,
|
|
3018
|
+
" ",
|
|
3019
|
+
(l.score * 100).toFixed(0),
|
|
3020
|
+
"%"
|
|
3021
|
+
]
|
|
3022
|
+
},
|
|
3023
|
+
k
|
|
3024
|
+
))
|
|
3025
|
+
]
|
|
3026
|
+
}
|
|
3027
|
+
);
|
|
3028
|
+
})()
|
|
3029
|
+
}
|
|
3030
|
+
);
|
|
3031
|
+
}
|
|
3032
|
+
|
|
3033
|
+
// src/composites/detection-result-tree.tsx
|
|
3034
|
+
var import_jsx_runtime43 = require("react/jsx-runtime");
|
|
3035
|
+
function DetectionResultTree({
|
|
3036
|
+
detections,
|
|
3037
|
+
classColors,
|
|
3038
|
+
className,
|
|
3039
|
+
hiddenKeys,
|
|
3040
|
+
onToggleVisibility
|
|
3041
|
+
}) {
|
|
3042
|
+
const colors = classColors;
|
|
3043
|
+
if (detections.length === 0) {
|
|
3044
|
+
return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: "text-sm text-foreground-subtle italic text-center py-4", children: "No detections" });
|
|
3045
|
+
}
|
|
3046
|
+
return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className, children: [
|
|
3047
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "text-xs font-medium text-foreground-subtle uppercase tracking-wide mb-2", children: [
|
|
3048
|
+
"Detections (",
|
|
3049
|
+
detections.length,
|
|
3050
|
+
")"
|
|
3051
|
+
] }),
|
|
3052
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: "space-y-2", children: detections.map((d, i) => /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
3053
|
+
DetectionNode,
|
|
3054
|
+
{
|
|
3055
|
+
detection: d,
|
|
3056
|
+
path: String(i),
|
|
3057
|
+
colors,
|
|
3058
|
+
hiddenKeys,
|
|
3059
|
+
onToggleVisibility
|
|
3060
|
+
},
|
|
3061
|
+
i
|
|
3062
|
+
)) })
|
|
3063
|
+
] });
|
|
3064
|
+
}
|
|
3065
|
+
function DetectionNode({
|
|
3066
|
+
detection,
|
|
3067
|
+
path,
|
|
3068
|
+
colors,
|
|
3069
|
+
hiddenKeys,
|
|
3070
|
+
onToggleVisibility
|
|
3071
|
+
}) {
|
|
3072
|
+
const color = getClassColor(detection.className, colors);
|
|
3073
|
+
const isVisible = !hiddenKeys?.has(path);
|
|
3074
|
+
return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: `rounded-md border border-border bg-surface p-3 space-y-1 ${isVisible ? "" : "opacity-40"}`, children: [
|
|
3075
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "flex justify-between items-center", children: [
|
|
3076
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
3077
|
+
onToggleVisibility && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
3078
|
+
"input",
|
|
3079
|
+
{
|
|
3080
|
+
type: "checkbox",
|
|
3081
|
+
checked: isVisible,
|
|
3082
|
+
onChange: () => onToggleVisibility(path, !isVisible),
|
|
3083
|
+
className: "h-3.5 w-3.5 rounded border-border accent-primary cursor-pointer shrink-0"
|
|
3084
|
+
}
|
|
3085
|
+
),
|
|
3086
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
3087
|
+
"span",
|
|
3088
|
+
{
|
|
3089
|
+
className: "h-2.5 w-2.5 rounded-full shrink-0",
|
|
3090
|
+
style: { backgroundColor: color }
|
|
3091
|
+
}
|
|
3092
|
+
),
|
|
3093
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "text-sm font-medium text-foreground", children: detection.className }),
|
|
3094
|
+
detection.mask && detection.maskWidth && detection.maskHeight && /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("span", { className: "text-[9px] font-mono px-1 py-0.5 rounded bg-primary/10 text-primary", children: [
|
|
3095
|
+
"mask ",
|
|
3096
|
+
detection.maskWidth,
|
|
3097
|
+
"x",
|
|
3098
|
+
detection.maskHeight
|
|
3099
|
+
] })
|
|
3100
|
+
] }),
|
|
3101
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(ConfidenceBadge, { confidence: detection.confidence })
|
|
3102
|
+
] }),
|
|
3103
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "text-[10px] text-foreground-subtle font-mono", children: [
|
|
3104
|
+
"bbox: [",
|
|
3105
|
+
detection.bbox.map((v) => Math.round(v)).join(", "),
|
|
3106
|
+
"]"
|
|
3107
|
+
] }),
|
|
3108
|
+
detection.labelsData && detection.labelsData.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: "flex flex-wrap gap-1 mt-1", children: detection.labelsData.map((l, k) => /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
|
|
3109
|
+
"span",
|
|
3110
|
+
{
|
|
3111
|
+
className: "inline-flex items-center gap-1 text-[10px] font-medium px-1.5 py-0.5 rounded-full",
|
|
3112
|
+
style: { backgroundColor: getClassColor(l.addonId ?? l.label, colors) + "20", color: getClassColor(l.addonId ?? l.label, colors) },
|
|
3113
|
+
children: [
|
|
3114
|
+
l.label,
|
|
3115
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("span", { className: "opacity-60", children: [
|
|
3116
|
+
(l.score * 100).toFixed(0),
|
|
3117
|
+
"%"
|
|
3118
|
+
] }),
|
|
3119
|
+
l.addonId && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "opacity-40 text-[8px]", children: l.addonId })
|
|
3120
|
+
]
|
|
3121
|
+
},
|
|
3122
|
+
k
|
|
3123
|
+
)) }),
|
|
3124
|
+
detection.children && detection.children.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
3125
|
+
ChildrenTree,
|
|
3126
|
+
{
|
|
3127
|
+
children: detection.children,
|
|
3128
|
+
parentPath: path,
|
|
3129
|
+
colors,
|
|
3130
|
+
hiddenKeys,
|
|
3131
|
+
onToggleVisibility
|
|
3132
|
+
}
|
|
3133
|
+
)
|
|
3134
|
+
] });
|
|
3135
|
+
}
|
|
3136
|
+
function ChildrenTree({
|
|
3137
|
+
children,
|
|
3138
|
+
parentPath,
|
|
3139
|
+
colors,
|
|
3140
|
+
hiddenKeys,
|
|
3141
|
+
onToggleVisibility
|
|
3142
|
+
}) {
|
|
3143
|
+
return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: "ml-4 mt-1.5 space-y-1.5 border-l-2 border-border pl-3", children: children.map((child, j) => {
|
|
3144
|
+
const childPath = `${parentPath}.${j}`;
|
|
3145
|
+
const childColor = getClassColor(child.className, colors);
|
|
3146
|
+
const isVisible = !hiddenKeys?.has(childPath);
|
|
3147
|
+
return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: `text-xs space-y-0.5 ${isVisible ? "" : "opacity-40"}`, children: [
|
|
3148
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "flex items-center gap-1.5", children: [
|
|
3149
|
+
onToggleVisibility && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
3150
|
+
"input",
|
|
3151
|
+
{
|
|
3152
|
+
type: "checkbox",
|
|
3153
|
+
checked: isVisible,
|
|
3154
|
+
onChange: () => onToggleVisibility(childPath, !isVisible),
|
|
3155
|
+
className: "h-3 w-3 rounded border-border accent-primary cursor-pointer shrink-0"
|
|
3156
|
+
}
|
|
3157
|
+
),
|
|
3158
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
3159
|
+
"span",
|
|
3160
|
+
{
|
|
3161
|
+
className: "h-1.5 w-1.5 rounded-full shrink-0",
|
|
3162
|
+
style: { backgroundColor: childColor }
|
|
3163
|
+
}
|
|
3164
|
+
),
|
|
3165
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "font-medium", style: { color: childColor }, children: child.className }),
|
|
3166
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("span", { className: "text-foreground-subtle", children: [
|
|
3167
|
+
(child.confidence * 100).toFixed(0),
|
|
3168
|
+
"%"
|
|
3169
|
+
] }),
|
|
3170
|
+
child.mask && child.maskWidth && child.maskHeight && /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("span", { className: "text-[9px] font-mono px-1 py-0.5 rounded bg-primary/10 text-primary", children: [
|
|
3171
|
+
"mask ",
|
|
3172
|
+
child.maskWidth,
|
|
3173
|
+
"x",
|
|
3174
|
+
child.maskHeight
|
|
3175
|
+
] })
|
|
3176
|
+
] }),
|
|
3177
|
+
child.labelsData && child.labelsData.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: "flex flex-wrap gap-1 ml-5 mt-0.5", children: child.labelsData.map((l, k) => /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
|
|
3178
|
+
"span",
|
|
3179
|
+
{
|
|
3180
|
+
className: "inline-flex items-center gap-0.5 text-[9px] font-medium px-1 py-0.5 rounded-full",
|
|
3181
|
+
style: { backgroundColor: getClassColor(l.addonId ?? l.label, colors) + "20", color: getClassColor(l.addonId ?? l.label, colors) },
|
|
3182
|
+
children: [
|
|
3183
|
+
l.label,
|
|
3184
|
+
" ",
|
|
3185
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("span", { className: "opacity-60", children: [
|
|
3186
|
+
(l.score * 100).toFixed(0),
|
|
3187
|
+
"%"
|
|
3188
|
+
] })
|
|
3189
|
+
]
|
|
3190
|
+
},
|
|
3191
|
+
k
|
|
3192
|
+
)) }),
|
|
3193
|
+
child.children && child.children.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
3194
|
+
ChildrenTree,
|
|
3195
|
+
{
|
|
3196
|
+
children: child.children,
|
|
3197
|
+
parentPath: childPath,
|
|
3198
|
+
colors,
|
|
3199
|
+
hiddenKeys,
|
|
3200
|
+
onToggleVisibility
|
|
3201
|
+
}
|
|
3202
|
+
)
|
|
3203
|
+
] }, j);
|
|
3204
|
+
}) });
|
|
3205
|
+
}
|
|
3206
|
+
function ConfidenceBadge({ confidence }) {
|
|
3207
|
+
const level = confidence >= 0.8 ? "bg-success/10 text-success" : confidence >= 0.5 ? "bg-warning/10 text-warning" : "bg-danger/10 text-danger";
|
|
3208
|
+
return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("span", { className: `text-xs font-medium px-2 py-0.5 rounded-full ${level}`, children: [
|
|
3209
|
+
(confidence * 100).toFixed(1),
|
|
3210
|
+
"%"
|
|
3211
|
+
] });
|
|
3212
|
+
}
|
|
3213
|
+
|
|
3214
|
+
// src/composites/step-timings.tsx
|
|
3215
|
+
var import_jsx_runtime44 = require("react/jsx-runtime");
|
|
3216
|
+
function StepTimings({ timings, totalMs, className }) {
|
|
3217
|
+
const entries = Object.entries(timings);
|
|
3218
|
+
if (entries.length === 0 && totalMs === void 0) return null;
|
|
3219
|
+
return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: `rounded-lg border border-border bg-surface p-3 space-y-2 ${className ?? ""}`, children: [
|
|
3220
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "text-xs font-medium text-foreground-subtle uppercase tracking-wide", children: "Timings" }),
|
|
3221
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "space-y-1 text-xs", children: [
|
|
3222
|
+
entries.map(([step, ms]) => /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "flex justify-between", children: [
|
|
3223
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "text-foreground-subtle", children: step }),
|
|
3224
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("span", { className: "font-mono text-foreground", children: [
|
|
3225
|
+
ms.toFixed(1),
|
|
3226
|
+
"ms"
|
|
3227
|
+
] })
|
|
3228
|
+
] }, step)),
|
|
3229
|
+
totalMs !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "flex justify-between pt-1 border-t border-border font-medium text-foreground", children: [
|
|
3230
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { children: "Total" }),
|
|
3231
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("span", { className: "font-mono", children: [
|
|
3232
|
+
totalMs.toFixed(1),
|
|
3233
|
+
"ms"
|
|
3234
|
+
] })
|
|
3235
|
+
] })
|
|
3236
|
+
] })
|
|
3237
|
+
] });
|
|
3238
|
+
}
|
|
3239
|
+
|
|
3240
|
+
// src/composites/image-selector.tsx
|
|
3241
|
+
var import_jsx_runtime45 = require("react/jsx-runtime");
|
|
3242
|
+
function ImageSelector({
|
|
3243
|
+
images,
|
|
3244
|
+
selectedFilename,
|
|
3245
|
+
uploadedName,
|
|
3246
|
+
onSelect,
|
|
3247
|
+
onUpload,
|
|
3248
|
+
className
|
|
3249
|
+
}) {
|
|
3250
|
+
const handleUploadClick = () => {
|
|
3251
|
+
const input = document.createElement("input");
|
|
3252
|
+
input.type = "file";
|
|
3253
|
+
input.accept = "image/*";
|
|
3254
|
+
input.onchange = (ev) => {
|
|
3255
|
+
const file = ev.target.files?.[0];
|
|
3256
|
+
if (!file) return;
|
|
3257
|
+
const reader = new FileReader();
|
|
3258
|
+
reader.onload = (e) => {
|
|
3259
|
+
const dataUrl = e.target?.result;
|
|
3260
|
+
const b64 = dataUrl.split(",")[1];
|
|
3261
|
+
if (b64) onUpload(b64, file.name, dataUrl);
|
|
3262
|
+
};
|
|
3263
|
+
reader.readAsDataURL(file);
|
|
3264
|
+
};
|
|
3265
|
+
input.click();
|
|
3266
|
+
};
|
|
3267
|
+
return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: `flex flex-wrap items-center gap-2 ${className ?? ""}`, children: [
|
|
3268
|
+
images.map((img) => /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
3269
|
+
"button",
|
|
3270
|
+
{
|
|
3271
|
+
onClick: () => onSelect(img.filename),
|
|
3272
|
+
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"}`,
|
|
3273
|
+
children: img.id
|
|
3274
|
+
},
|
|
3275
|
+
img.filename
|
|
3276
|
+
)),
|
|
3277
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
3278
|
+
"button",
|
|
3279
|
+
{
|
|
3280
|
+
onClick: handleUploadClick,
|
|
3281
|
+
className: "px-3 py-1.5 text-xs rounded-md border border-border bg-surface text-foreground hover:bg-surface-hover transition-colors",
|
|
3282
|
+
children: "Upload..."
|
|
3283
|
+
}
|
|
3284
|
+
),
|
|
3285
|
+
uploadedName && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "text-xs text-foreground-subtle", children: uploadedName })
|
|
3286
|
+
] });
|
|
3287
|
+
}
|
|
3288
|
+
|
|
3289
|
+
// src/composites/inference-config-selector.tsx
|
|
3290
|
+
var import_jsx_runtime46 = require("react/jsx-runtime");
|
|
3291
|
+
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";
|
|
3292
|
+
function InferenceConfigSelector({
|
|
3293
|
+
runtime,
|
|
3294
|
+
backend,
|
|
3295
|
+
modelId,
|
|
3296
|
+
agentId = "hub",
|
|
3297
|
+
runtimes,
|
|
3298
|
+
backends,
|
|
3299
|
+
models,
|
|
3300
|
+
agents = [],
|
|
3301
|
+
onRuntimeChange,
|
|
3302
|
+
onBackendChange,
|
|
3303
|
+
onModelChange,
|
|
3304
|
+
onAgentChange,
|
|
3305
|
+
layout = "grid",
|
|
3306
|
+
className,
|
|
3307
|
+
showAgent = false
|
|
3308
|
+
}) {
|
|
3309
|
+
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";
|
|
3310
|
+
return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: `${containerClass} ${className ?? ""}`, children: [
|
|
3311
|
+
showAgent && agents.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("label", { className: "space-y-1", children: [
|
|
3312
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { className: "text-xs font-medium text-foreground-subtle", children: "Agent" }),
|
|
3313
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3314
|
+
"select",
|
|
3315
|
+
{
|
|
3316
|
+
value: agentId,
|
|
3317
|
+
onChange: (e) => onAgentChange?.(e.target.value),
|
|
3318
|
+
className: SELECT_CLASS,
|
|
3319
|
+
children: agents.map((a) => /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("option", { value: a.id, children: [
|
|
3320
|
+
a.name,
|
|
3321
|
+
" (",
|
|
3322
|
+
a.status,
|
|
3323
|
+
")"
|
|
3324
|
+
] }, a.id))
|
|
3325
|
+
}
|
|
3326
|
+
)
|
|
3327
|
+
] }),
|
|
3328
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("label", { className: "space-y-1", children: [
|
|
3329
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { className: "text-xs font-medium text-foreground-subtle", children: "Runtime" }),
|
|
3330
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3331
|
+
"select",
|
|
3332
|
+
{
|
|
3333
|
+
value: runtime,
|
|
3334
|
+
onChange: (e) => onRuntimeChange(e.target.value),
|
|
3335
|
+
className: SELECT_CLASS,
|
|
3336
|
+
children: runtimes.map((r) => /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("option", { value: r.value, disabled: !r.available, children: [
|
|
3337
|
+
r.label,
|
|
3338
|
+
!r.available ? " (unavailable)" : ""
|
|
3339
|
+
] }, r.value))
|
|
3340
|
+
}
|
|
3341
|
+
)
|
|
3342
|
+
] }),
|
|
3343
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("label", { className: "space-y-1", children: [
|
|
3344
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { className: "text-xs font-medium text-foreground-subtle", children: "Backend" }),
|
|
3345
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3346
|
+
"select",
|
|
3347
|
+
{
|
|
3348
|
+
value: backend,
|
|
3349
|
+
onChange: (e) => onBackendChange(e.target.value),
|
|
3350
|
+
className: SELECT_CLASS,
|
|
3351
|
+
children: backends.map((b) => /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("option", { value: b.id, disabled: !b.available, children: [
|
|
3352
|
+
b.label,
|
|
3353
|
+
!b.available ? " (unavailable)" : ""
|
|
3354
|
+
] }, b.id))
|
|
3355
|
+
}
|
|
3356
|
+
)
|
|
3357
|
+
] }),
|
|
3358
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("label", { className: "space-y-1", children: [
|
|
3359
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { className: "text-xs font-medium text-foreground-subtle", children: "Model" }),
|
|
3360
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3361
|
+
"select",
|
|
3362
|
+
{
|
|
3363
|
+
value: modelId,
|
|
3364
|
+
onChange: (e) => onModelChange(e.target.value),
|
|
3365
|
+
className: SELECT_CLASS,
|
|
3366
|
+
children: models.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("option", { value: "", children: "No compatible models" }) : models.map((m) => /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("option", { value: m.id, children: [
|
|
3367
|
+
m.name,
|
|
3368
|
+
m.downloaded ? " \u2713" : ""
|
|
3369
|
+
] }, m.id))
|
|
3370
|
+
}
|
|
3371
|
+
)
|
|
3372
|
+
] })
|
|
3373
|
+
] });
|
|
3374
|
+
}
|
|
3375
|
+
|
|
3376
|
+
// src/composites/mount-addon-page.tsx
|
|
3377
|
+
var import_react28 = require("react");
|
|
3378
|
+
var import_client2 = require("react-dom/client");
|
|
3379
|
+
|
|
3380
|
+
// src/composites/dev-shell.tsx
|
|
3381
|
+
var import_react27 = require("react");
|
|
3382
|
+
var import_client = require("@trpc/client");
|
|
3383
|
+
var import_superjson = __toESM(require("superjson"), 1);
|
|
3384
|
+
|
|
3385
|
+
// src/composites/login-form.tsx
|
|
3386
|
+
var import_react26 = require("react");
|
|
3387
|
+
var import_jsx_runtime47 = require("react/jsx-runtime");
|
|
3388
|
+
function EyeIcon({ className }) {
|
|
3389
|
+
return /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
|
|
3390
|
+
"svg",
|
|
3391
|
+
{
|
|
3392
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
3393
|
+
viewBox: "0 0 24 24",
|
|
3394
|
+
fill: "none",
|
|
3395
|
+
stroke: "currentColor",
|
|
3396
|
+
strokeWidth: "2",
|
|
3397
|
+
strokeLinecap: "round",
|
|
3398
|
+
strokeLinejoin: "round",
|
|
3399
|
+
className,
|
|
3400
|
+
children: [
|
|
3401
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)("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" }),
|
|
3402
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)("circle", { cx: "12", cy: "12", r: "3" })
|
|
3403
|
+
]
|
|
3404
|
+
}
|
|
3405
|
+
);
|
|
3406
|
+
}
|
|
3407
|
+
function EyeOffIcon({ className }) {
|
|
3408
|
+
return /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
|
|
3409
|
+
"svg",
|
|
3410
|
+
{
|
|
3411
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
3412
|
+
viewBox: "0 0 24 24",
|
|
3413
|
+
fill: "none",
|
|
3414
|
+
stroke: "currentColor",
|
|
3415
|
+
strokeWidth: "2",
|
|
3416
|
+
strokeLinecap: "round",
|
|
3417
|
+
strokeLinejoin: "round",
|
|
3418
|
+
className,
|
|
3419
|
+
children: [
|
|
3420
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)("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" }),
|
|
3421
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)("path", { d: "M14.084 14.158a3 3 0 0 1-4.242-4.242" }),
|
|
3422
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)("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" }),
|
|
3423
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)("path", { d: "m2 2 20 20" })
|
|
3424
|
+
]
|
|
3425
|
+
}
|
|
3426
|
+
);
|
|
3427
|
+
}
|
|
3428
|
+
function SpinnerIcon({ className }) {
|
|
3429
|
+
return /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
3430
|
+
"svg",
|
|
3431
|
+
{
|
|
3432
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
3433
|
+
viewBox: "0 0 24 24",
|
|
3434
|
+
fill: "none",
|
|
3435
|
+
stroke: "currentColor",
|
|
3436
|
+
strokeWidth: "2",
|
|
3437
|
+
strokeLinecap: "round",
|
|
3438
|
+
strokeLinejoin: "round",
|
|
3439
|
+
className,
|
|
3440
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" })
|
|
3441
|
+
}
|
|
3442
|
+
);
|
|
3443
|
+
}
|
|
3444
|
+
function LoginForm({
|
|
3445
|
+
onLogin,
|
|
3446
|
+
serverUrl,
|
|
3447
|
+
logoSrc,
|
|
3448
|
+
error: externalError,
|
|
3449
|
+
className
|
|
3450
|
+
}) {
|
|
3451
|
+
const [username, setUsername] = (0, import_react26.useState)("");
|
|
3452
|
+
const [password, setPassword] = (0, import_react26.useState)("");
|
|
3453
|
+
const [showPassword, setShowPassword] = (0, import_react26.useState)(false);
|
|
3454
|
+
const [submitting, setSubmitting] = (0, import_react26.useState)(false);
|
|
3455
|
+
const [internalError, setInternalError] = (0, import_react26.useState)(null);
|
|
3456
|
+
const error = externalError ?? internalError;
|
|
3457
|
+
const handleSubmit = async (e) => {
|
|
3458
|
+
e.preventDefault();
|
|
3459
|
+
if (submitting) return;
|
|
3460
|
+
setInternalError(null);
|
|
3461
|
+
setSubmitting(true);
|
|
3462
|
+
try {
|
|
3463
|
+
await onLogin(username, password);
|
|
3464
|
+
} catch (err) {
|
|
3465
|
+
const message = err instanceof Error ? err.message : "Login failed. Please try again.";
|
|
3466
|
+
setInternalError(message);
|
|
3467
|
+
} finally {
|
|
3468
|
+
setSubmitting(false);
|
|
3469
|
+
}
|
|
3470
|
+
};
|
|
3471
|
+
return /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
3472
|
+
"div",
|
|
3473
|
+
{
|
|
3474
|
+
className: cn(
|
|
3475
|
+
"flex min-h-screen items-center justify-center bg-background p-4",
|
|
3476
|
+
className
|
|
3477
|
+
),
|
|
3478
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "w-full max-w-sm", children: [
|
|
3479
|
+
logoSrc && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("div", { className: "flex justify-center mb-8", children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("img", { src: logoSrc, alt: "Logo", className: "h-12" }) }),
|
|
3480
|
+
serverUrl && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("p", { className: "mb-4 text-center text-xs text-foreground-subtle truncate", children: serverUrl }),
|
|
3481
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
|
|
3482
|
+
"form",
|
|
3483
|
+
{
|
|
3484
|
+
onSubmit: handleSubmit,
|
|
3485
|
+
className: "space-y-4 rounded-xl border border-border bg-surface p-6 shadow-xl shadow-black/10",
|
|
3486
|
+
children: [
|
|
3487
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("div", { className: "rounded-md bg-danger/10 border border-danger/20 px-3 py-2 text-xs text-danger", children: error }),
|
|
3488
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "space-y-1.5", children: [
|
|
3489
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)("label", { className: "text-xs font-medium text-foreground-subtle", children: "Username" }),
|
|
3490
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
3491
|
+
"input",
|
|
3492
|
+
{
|
|
3493
|
+
type: "text",
|
|
3494
|
+
value: username,
|
|
3495
|
+
onChange: (e) => setUsername(e.target.value),
|
|
3496
|
+
autoComplete: "username",
|
|
3497
|
+
required: true,
|
|
3498
|
+
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"
|
|
3499
|
+
}
|
|
3500
|
+
)
|
|
3501
|
+
] }),
|
|
3502
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "space-y-1.5", children: [
|
|
3503
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)("label", { className: "text-xs font-medium text-foreground-subtle", children: "Password" }),
|
|
3504
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "relative", children: [
|
|
3505
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
3506
|
+
"input",
|
|
3507
|
+
{
|
|
3508
|
+
type: showPassword ? "text" : "password",
|
|
3509
|
+
value: password,
|
|
3510
|
+
onChange: (e) => setPassword(e.target.value),
|
|
3511
|
+
autoComplete: "current-password",
|
|
3512
|
+
required: true,
|
|
3513
|
+
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"
|
|
3514
|
+
}
|
|
3515
|
+
),
|
|
3516
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
3517
|
+
"button",
|
|
3518
|
+
{
|
|
3519
|
+
type: "button",
|
|
3520
|
+
onClick: () => setShowPassword((prev) => !prev),
|
|
3521
|
+
className: "absolute right-2.5 top-1/2 -translate-y-1/2 text-foreground-subtle hover:text-foreground",
|
|
3522
|
+
tabIndex: -1,
|
|
3523
|
+
children: showPassword ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(EyeOffIcon, { className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(EyeIcon, { className: "h-4 w-4" })
|
|
3524
|
+
}
|
|
3525
|
+
)
|
|
3526
|
+
] })
|
|
3527
|
+
] }),
|
|
3528
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
|
|
3529
|
+
"button",
|
|
3530
|
+
{
|
|
3531
|
+
type: "submit",
|
|
3532
|
+
disabled: submitting,
|
|
3533
|
+
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",
|
|
3534
|
+
children: [
|
|
3535
|
+
submitting && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(SpinnerIcon, { className: "h-4 w-4 animate-spin" }),
|
|
3536
|
+
submitting ? "Logging in..." : "Log in"
|
|
3537
|
+
]
|
|
3538
|
+
}
|
|
3539
|
+
)
|
|
3540
|
+
]
|
|
3541
|
+
}
|
|
3542
|
+
)
|
|
3543
|
+
] })
|
|
3544
|
+
}
|
|
3545
|
+
);
|
|
3546
|
+
}
|
|
3547
|
+
|
|
3548
|
+
// src/composites/dev-shell.tsx
|
|
3549
|
+
var import_jsx_runtime48 = require("react/jsx-runtime");
|
|
3550
|
+
var STORAGE_KEY = "camstack_dev_token";
|
|
3551
|
+
var DevShellContext = (0, import_react27.createContext)(null);
|
|
3552
|
+
function useDevShell() {
|
|
3553
|
+
const ctx = (0, import_react27.useContext)(DevShellContext);
|
|
3554
|
+
if (!ctx) {
|
|
3555
|
+
throw new Error("useDevShell must be used within a DevShell");
|
|
3556
|
+
}
|
|
3557
|
+
return ctx;
|
|
3558
|
+
}
|
|
3559
|
+
function getStoredToken() {
|
|
3560
|
+
if (typeof window === "undefined") return null;
|
|
3561
|
+
return localStorage.getItem(STORAGE_KEY);
|
|
3562
|
+
}
|
|
3563
|
+
function SunIcon({ className }) {
|
|
3564
|
+
return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(
|
|
3565
|
+
"svg",
|
|
3566
|
+
{
|
|
3567
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
3568
|
+
viewBox: "0 0 24 24",
|
|
3569
|
+
fill: "none",
|
|
3570
|
+
stroke: "currentColor",
|
|
3571
|
+
strokeWidth: "2",
|
|
3572
|
+
strokeLinecap: "round",
|
|
3573
|
+
strokeLinejoin: "round",
|
|
3574
|
+
className,
|
|
3575
|
+
children: [
|
|
3576
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)("circle", { cx: "12", cy: "12", r: "4" }),
|
|
3577
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)("path", { d: "M12 2v2" }),
|
|
3578
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)("path", { d: "M12 20v2" }),
|
|
3579
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)("path", { d: "m4.93 4.93 1.41 1.41" }),
|
|
3580
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)("path", { d: "m17.66 17.66 1.41 1.41" }),
|
|
3581
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)("path", { d: "M2 12h2" }),
|
|
3582
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)("path", { d: "M20 12h2" }),
|
|
3583
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)("path", { d: "m6.34 17.66-1.41 1.41" }),
|
|
3584
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)("path", { d: "m19.07 4.93-1.41 1.41" })
|
|
3585
|
+
]
|
|
3586
|
+
}
|
|
3587
|
+
);
|
|
3588
|
+
}
|
|
3589
|
+
function MoonIcon({ className }) {
|
|
3590
|
+
return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
|
|
3591
|
+
"svg",
|
|
3592
|
+
{
|
|
3593
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
3594
|
+
viewBox: "0 0 24 24",
|
|
3595
|
+
fill: "none",
|
|
3596
|
+
stroke: "currentColor",
|
|
3597
|
+
strokeWidth: "2",
|
|
3598
|
+
strokeLinecap: "round",
|
|
3599
|
+
strokeLinejoin: "round",
|
|
3600
|
+
className,
|
|
3601
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("path", { d: "M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z" })
|
|
3602
|
+
}
|
|
3603
|
+
);
|
|
3604
|
+
}
|
|
3605
|
+
function DevShellInner({
|
|
3606
|
+
children,
|
|
3607
|
+
serverUrl,
|
|
3608
|
+
title,
|
|
3609
|
+
token,
|
|
3610
|
+
onLogout
|
|
3611
|
+
}) {
|
|
3612
|
+
const theme = useThemeMode();
|
|
3613
|
+
const trpc = (0, import_react27.useMemo)(
|
|
3614
|
+
() => {
|
|
3615
|
+
const wsUrl = serverUrl.replace(/^http/, "ws") + "/trpc";
|
|
3616
|
+
const wsClient = (0, import_client.createWSClient)({
|
|
3617
|
+
url: wsUrl,
|
|
3618
|
+
connectionParams: () => ({ token })
|
|
3619
|
+
});
|
|
3620
|
+
return (0, import_client.createTRPCClient)({
|
|
3621
|
+
links: [
|
|
3622
|
+
(0, import_client.splitLink)({
|
|
3623
|
+
condition: (op) => op.type === "subscription",
|
|
3624
|
+
true: (0, import_client.wsLink)({ client: wsClient, transformer: import_superjson.default }),
|
|
3625
|
+
false: (0, import_client.httpLink)({
|
|
3626
|
+
url: `${serverUrl}/trpc`,
|
|
3627
|
+
transformer: import_superjson.default,
|
|
3628
|
+
headers: () => ({ authorization: `Bearer ${token}` })
|
|
3629
|
+
})
|
|
3630
|
+
})
|
|
3631
|
+
]
|
|
3632
|
+
});
|
|
3633
|
+
},
|
|
3634
|
+
[serverUrl, token]
|
|
3635
|
+
);
|
|
3636
|
+
const contextValue = (0, import_react27.useMemo)(
|
|
3637
|
+
() => ({ trpc, token, logout: onLogout }),
|
|
3638
|
+
[trpc, token, onLogout]
|
|
3639
|
+
);
|
|
3640
|
+
return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(DevShellContext.Provider, { value: contextValue, children: /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "min-h-screen bg-background text-foreground", children: [
|
|
3641
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "flex items-center justify-between border-b border-border bg-surface px-4 py-2", children: [
|
|
3642
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
3643
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { className: "rounded bg-warning/20 px-2 py-0.5 text-xs font-bold text-warning", children: "DEV MODE" }),
|
|
3644
|
+
title && /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { className: "text-sm font-medium text-foreground", children: title }),
|
|
3645
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { className: "text-xs text-foreground-subtle", children: serverUrl })
|
|
3646
|
+
] }),
|
|
3647
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
3648
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(
|
|
3649
|
+
"button",
|
|
3650
|
+
{
|
|
3651
|
+
type: "button",
|
|
3652
|
+
onClick: theme.toggleMode,
|
|
3653
|
+
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",
|
|
3654
|
+
title: `Theme: ${theme.mode}`,
|
|
3655
|
+
children: [
|
|
3656
|
+
theme.resolvedMode === "dark" ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(SunIcon, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(MoonIcon, { className: "h-3.5 w-3.5" }),
|
|
3657
|
+
theme.mode === "dark" ? "Dark" : theme.mode === "light" ? "Light" : "System"
|
|
3658
|
+
]
|
|
3659
|
+
}
|
|
3660
|
+
),
|
|
3661
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
|
|
3662
|
+
"button",
|
|
3663
|
+
{
|
|
3664
|
+
type: "button",
|
|
3665
|
+
onClick: onLogout,
|
|
3666
|
+
className: "rounded-md px-2.5 py-1 text-xs font-medium text-danger hover:bg-danger/10 transition-colors",
|
|
3667
|
+
children: "Logout"
|
|
3668
|
+
}
|
|
3669
|
+
)
|
|
3670
|
+
] })
|
|
3671
|
+
] }),
|
|
3672
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)("div", { className: "p-4", children: children({ trpc, theme }) })
|
|
3673
|
+
] }) });
|
|
3674
|
+
}
|
|
3675
|
+
function DevShell({
|
|
3676
|
+
children,
|
|
3677
|
+
serverUrl = "https://localhost:4443",
|
|
3678
|
+
title
|
|
3679
|
+
}) {
|
|
3680
|
+
const [token, setToken] = (0, import_react27.useState)(getStoredToken);
|
|
3681
|
+
const handleLogin = (0, import_react27.useCallback)(
|
|
3682
|
+
async (username, password) => {
|
|
3683
|
+
const anonClient = (0, import_client.createTRPCClient)({
|
|
3684
|
+
links: [
|
|
3685
|
+
(0, import_client.httpLink)({
|
|
3686
|
+
url: `${serverUrl}/trpc`,
|
|
3687
|
+
transformer: import_superjson.default
|
|
3688
|
+
})
|
|
3689
|
+
]
|
|
3690
|
+
});
|
|
3691
|
+
const res = await anonClient.auth.login.mutate({ username, password });
|
|
3692
|
+
if (!res?.token) throw new Error("No token returned");
|
|
3693
|
+
localStorage.setItem(STORAGE_KEY, res.token);
|
|
3694
|
+
setToken(res.token);
|
|
3695
|
+
},
|
|
3696
|
+
[serverUrl]
|
|
3697
|
+
);
|
|
3698
|
+
const handleLogout = (0, import_react27.useCallback)(() => {
|
|
3699
|
+
localStorage.removeItem(STORAGE_KEY);
|
|
3700
|
+
setToken(null);
|
|
3701
|
+
}, []);
|
|
3702
|
+
if (!token) {
|
|
3703
|
+
return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(ThemeProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(LoginForm, { onLogin: handleLogin, serverUrl }) });
|
|
3704
|
+
}
|
|
3705
|
+
return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(ThemeProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
|
|
3706
|
+
DevShellInner,
|
|
3707
|
+
{
|
|
3708
|
+
serverUrl,
|
|
3709
|
+
title,
|
|
3710
|
+
token,
|
|
3711
|
+
onLogout: handleLogout,
|
|
3712
|
+
children
|
|
3713
|
+
}
|
|
3714
|
+
) });
|
|
3715
|
+
}
|
|
3716
|
+
|
|
3717
|
+
// src/composites/mount-addon-page.tsx
|
|
3718
|
+
function mountAddonPage(PageComponent, options = {}) {
|
|
3719
|
+
const {
|
|
3720
|
+
serverUrl = "https://localhost:4443",
|
|
3721
|
+
title,
|
|
3722
|
+
rootId = "root"
|
|
3723
|
+
} = options;
|
|
3724
|
+
const root = document.getElementById(rootId);
|
|
3725
|
+
if (!root) {
|
|
3726
|
+
console.error(`[mountAddonPage] Element #${rootId} not found`);
|
|
3727
|
+
return;
|
|
3728
|
+
}
|
|
3729
|
+
(0, import_client2.createRoot)(root).render(
|
|
3730
|
+
(0, import_react28.createElement)(DevShell, {
|
|
3731
|
+
serverUrl,
|
|
3732
|
+
title,
|
|
3733
|
+
children: ({ trpc, theme }) => (0, import_react28.createElement)(PageComponent, {
|
|
3734
|
+
trpc,
|
|
3735
|
+
theme: { isDark: theme.resolvedMode === "dark" },
|
|
3736
|
+
navigate: (path) => {
|
|
3737
|
+
console.log("[dev] navigate:", path);
|
|
3738
|
+
}
|
|
3739
|
+
})
|
|
3740
|
+
})
|
|
3741
|
+
);
|
|
3742
|
+
}
|
|
2023
3743
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2024
3744
|
0 && (module.exports = {
|
|
2025
3745
|
AppShell,
|
|
2026
3746
|
Badge,
|
|
2027
3747
|
Button,
|
|
3748
|
+
CLASS_COLORS,
|
|
2028
3749
|
Card,
|
|
2029
3750
|
Checkbox,
|
|
2030
3751
|
CodeBlock,
|
|
2031
3752
|
ConfirmDialog,
|
|
3753
|
+
DEFAULT_CLASS_COLORS,
|
|
3754
|
+
DEFAULT_COLOR,
|
|
2032
3755
|
DataTable,
|
|
3756
|
+
DetectionCanvas,
|
|
3757
|
+
DetectionResultTree,
|
|
3758
|
+
DevShell,
|
|
2033
3759
|
DeviceCard,
|
|
2034
3760
|
DeviceGrid,
|
|
2035
3761
|
Dialog,
|
|
@@ -2048,10 +3774,16 @@ function DeviceGrid({
|
|
|
2048
3774
|
FloatingPanel,
|
|
2049
3775
|
FormField,
|
|
2050
3776
|
IconButton,
|
|
3777
|
+
ImageSelector,
|
|
3778
|
+
InferenceConfigSelector,
|
|
2051
3779
|
Input,
|
|
2052
3780
|
KeyValueList,
|
|
2053
3781
|
Label,
|
|
3782
|
+
LoginForm,
|
|
2054
3783
|
PageHeader,
|
|
3784
|
+
PipelineBuilder,
|
|
3785
|
+
PipelineRuntimeSelector,
|
|
3786
|
+
PipelineStep,
|
|
2055
3787
|
Popover,
|
|
2056
3788
|
PopoverContent,
|
|
2057
3789
|
PopoverTrigger,
|
|
@@ -2064,6 +3796,7 @@ function DeviceGrid({
|
|
|
2064
3796
|
Skeleton,
|
|
2065
3797
|
StatCard,
|
|
2066
3798
|
StatusBadge,
|
|
3799
|
+
StepTimings,
|
|
2067
3800
|
Switch,
|
|
2068
3801
|
Tabs,
|
|
2069
3802
|
TabsContent,
|
|
@@ -2077,10 +3810,13 @@ function DeviceGrid({
|
|
|
2077
3810
|
createTheme,
|
|
2078
3811
|
darkColors,
|
|
2079
3812
|
defaultTheme,
|
|
3813
|
+
getClassColor,
|
|
2080
3814
|
lightColors,
|
|
3815
|
+
mountAddonPage,
|
|
2081
3816
|
providerIcons,
|
|
2082
3817
|
statusIcons,
|
|
2083
3818
|
themeToCss,
|
|
3819
|
+
useDevShell,
|
|
2084
3820
|
useThemeMode
|
|
2085
3821
|
});
|
|
2086
3822
|
//# sourceMappingURL=index.cjs.map
|