@neuronection/assistant-ui 0.9.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +3 -2
  3. package/dist/about.d.ts +31 -1
  4. package/dist/about.js +1 -1
  5. package/dist/capability-chips.d.ts +21 -0
  6. package/dist/capability-chips.js +3 -0
  7. package/dist/capability-chips.js.map +1 -0
  8. package/dist/{chunk-3QGVWTBU.js → chunk-7RTSSBBR.js} +157 -4
  9. package/dist/chunk-7RTSSBBR.js.map +1 -0
  10. package/dist/{chunk-RJSN4TVT.js → chunk-A3A4J5M5.js} +4 -3
  11. package/dist/chunk-A3A4J5M5.js.map +1 -0
  12. package/dist/chunk-G3DWN2DJ.js +80 -0
  13. package/dist/chunk-G3DWN2DJ.js.map +1 -0
  14. package/dist/{chunk-UWHUCQC6.js → chunk-LY2B3UJH.js} +10 -4
  15. package/dist/chunk-LY2B3UJH.js.map +1 -0
  16. package/dist/chunk-M67KF4W2.js +130 -0
  17. package/dist/chunk-M67KF4W2.js.map +1 -0
  18. package/dist/chunk-N5V5LVZX.js +517 -0
  19. package/dist/chunk-N5V5LVZX.js.map +1 -0
  20. package/dist/{chunk-OPTH3VUY.js → chunk-RI6CEWWF.js} +2 -2
  21. package/dist/chunk-RI6CEWWF.js.map +1 -0
  22. package/dist/connection-test-row.d.ts +4 -0
  23. package/dist/connection-test-row.js +1 -1
  24. package/dist/index.d.ts +4 -2
  25. package/dist/index.js +9 -7
  26. package/dist/index.js.map +1 -1
  27. package/dist/model-picker.d.ts +2 -0
  28. package/dist/model-picker.js +1 -1
  29. package/dist/model-registry.d.ts +86 -0
  30. package/dist/model-registry.js +3 -0
  31. package/dist/model-registry.js.map +1 -0
  32. package/dist/provider-form.d.ts +2 -0
  33. package/dist/provider-form.js +1 -1
  34. package/dist/styles.css +1 -1
  35. package/dist/task-assignment-picker.d.ts +25 -6
  36. package/dist/task-assignment-picker.js +1 -1
  37. package/package.json +9 -1
  38. package/dist/chunk-3QGVWTBU.js.map +0 -1
  39. package/dist/chunk-OPTH3VUY.js.map +0 -1
  40. package/dist/chunk-RJSN4TVT.js.map +0 -1
  41. package/dist/chunk-TN35RR2R.js +0 -92
  42. package/dist/chunk-TN35RR2R.js.map +0 -1
  43. package/dist/chunk-UWHUCQC6.js.map +0 -1
@@ -15,6 +15,8 @@ var ConnectionTestRow = React.forwardRef(function ConnectionTestRow2({
15
15
  errorMessage,
16
16
  onTest,
17
17
  disabled = false,
18
+ variant = "row",
19
+ meta,
18
20
  className
19
21
  }, ref) {
20
22
  const state = status === "ok" ? { text: okLabel, icon: /* @__PURE__ */ jsx(Check, { className: "size-3.5", "aria-hidden": true }), tone: "text-[var(--as-success)]" } : status === "fail" ? {
@@ -28,8 +30,11 @@ var ConnectionTestRow = React.forwardRef(function ConnectionTestRow2({
28
30
  ref,
29
31
  "data-as": "connection-test-row",
30
32
  "data-status": status,
33
+ "data-variant": variant,
31
34
  className: cn(
32
- "flex items-center justify-between gap-3 rounded-[var(--as-radius)] border border-[var(--as-border)] bg-[var(--as-surface)] px-3 py-2",
35
+ "flex items-center justify-between gap-3 rounded-[var(--as-radius)] px-3 py-2",
36
+ variant === "row" && "border border-[var(--as-border)] bg-[var(--as-surface)]",
37
+ variant === "inline" && "px-0 py-0",
33
38
  className
34
39
  ),
35
40
  children: [
@@ -48,7 +53,8 @@ var ConnectionTestRow = React.forwardRef(function ConnectionTestRow2({
48
53
  latencyLabel
49
54
  ] }) : null
50
55
  ] }) : null,
51
- status === "fail" && errorMessage ? /* @__PURE__ */ jsx("span", { className: "truncate text-xs text-[var(--as-danger)]", children: errorMessage }) : null
56
+ status === "fail" && errorMessage ? /* @__PURE__ */ jsx("span", { className: "truncate text-xs text-[var(--as-danger)]", children: errorMessage }) : null,
57
+ meta ? /* @__PURE__ */ jsx("span", { className: "text-xs text-[var(--as-muted-fg)]", children: meta }) : null
52
58
  ] }),
53
59
  onTest ? /* @__PURE__ */ jsxs(
54
60
  "button",
@@ -69,5 +75,5 @@ var ConnectionTestRow = React.forwardRef(function ConnectionTestRow2({
69
75
  });
70
76
 
71
77
  export { ConnectionTestRow };
72
- //# sourceMappingURL=chunk-UWHUCQC6.js.map
73
- //# sourceMappingURL=chunk-UWHUCQC6.js.map
78
+ //# sourceMappingURL=chunk-LY2B3UJH.js.map
79
+ //# sourceMappingURL=chunk-LY2B3UJH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/connection-test-row/ConnectionTestRow.tsx"],"names":["ConnectionTestRow"],"mappings":"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","file":"chunk-LY2B3UJH.js","sourcesContent":["import * as React from 'react'\nimport { Check, TriangleAlert, X } from 'lucide-react'\nimport { cn } from '../../lib/utils'\nimport { Spinner } from '../spinner/Spinner'\n\nexport type ConnectionTestStatus = 'idle' | 'testing' | 'ok' | 'fail'\n\nexport interface ConnectionTestRowProps {\n status: ConnectionTestStatus\n latencyMs?: number | null\n label?: string\n testLabel?: string\n okLabel?: string\n failLabel?: string\n latencyLabel?: string\n errorMessage?: string | null\n onTest?: () => void\n disabled?: boolean\n /** `row` = standalone bordered row; `inline` = bare, for embedding in app cards. */\n variant?: 'row' | 'inline'\n /** Extra app-side context rendered after the status (e.g. model count). */\n meta?: React.ReactNode\n className?: string\n}\n\n/**\n * Presentational connection-test row (idle/testing/ok/fail + latency).\n * The actual ping is app-side: flip `status` from the test handler.\n */\nexport const ConnectionTestRow = React.forwardRef<\n HTMLDivElement,\n ConnectionTestRowProps\n>(function ConnectionTestRow(\n {\n status,\n latencyMs,\n label = 'Connection',\n testLabel = 'Test',\n okLabel = 'Connected',\n failLabel = 'Failed',\n latencyLabel = 'ms',\n errorMessage,\n onTest,\n disabled = false,\n variant = 'row',\n meta,\n className,\n },\n ref,\n) {\n const state =\n status === 'ok'\n ? { text: okLabel, icon: <Check className=\"size-3.5\" aria-hidden />, tone: 'text-[var(--as-success)]' }\n : status === 'fail'\n ? {\n text: failLabel,\n icon: <TriangleAlert className=\"size-3.5\" aria-hidden />,\n tone: 'text-[var(--as-danger)]',\n }\n : null\n\n return (\n <div\n ref={ref}\n data-as=\"connection-test-row\"\n data-status={status}\n data-variant={variant}\n className={cn(\n 'flex items-center justify-between gap-3 rounded-[var(--as-radius)] px-3 py-2',\n variant === 'row' && 'border border-[var(--as-border)] bg-[var(--as-surface)]',\n variant === 'inline' && 'px-0 py-0',\n className,\n )}\n >\n <div className=\"flex min-w-0 items-center gap-2 text-sm\">\n <span className=\"font-medium text-[var(--as-fg)]\">{label}</span>\n {status === 'testing' ? (\n <span className=\"flex items-center gap-1.5 text-[var(--as-muted-fg)]\" role=\"status\">\n <Spinner size=\"sm\" />\n {testLabel}…\n </span>\n ) : state ? (\n <span className={cn('flex items-center gap-1 font-medium', state.tone)}>\n {state.icon}\n {state.text}\n {status === 'ok' && latencyMs != null ? (\n <span className=\"font-normal text-[var(--as-muted-fg)]\">\n · {latencyMs}\n {latencyLabel}\n </span>\n ) : null}\n </span>\n ) : null}\n {status === 'fail' && errorMessage ? (\n <span className=\"truncate text-xs text-[var(--as-danger)]\">\n {errorMessage}\n </span>\n ) : null}\n {meta ? <span className=\"text-xs text-[var(--as-muted-fg)]\">{meta}</span> : null}\n </div>\n {onTest ? (\n <button\n type=\"button\"\n onClick={onTest}\n disabled={disabled || status === 'testing'}\n className=\"shrink-0 cursor-pointer rounded-[var(--as-radius-sm)] border border-[var(--as-border)] px-2.5 py-1 text-xs font-medium text-[var(--as-fg)] transition-colors hover:bg-[var(--as-secondary)] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)] disabled:pointer-events-none disabled:opacity-50\"\n >\n {status === 'fail' ? <X className=\"mr-1 inline size-3\" aria-hidden /> : null}\n {testLabel}\n </button>\n ) : null}\n </div>\n )\n})\n"]}
@@ -0,0 +1,130 @@
1
+ import { ModelPicker } from './chunk-RI6CEWWF.js';
2
+ import { cn } from './chunk-BLJWR4ZV.js';
3
+ import * as React from 'react';
4
+ import { X } from 'lucide-react';
5
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
6
+
7
+ function filterProviders(providers, requires) {
8
+ if (!requires) return providers;
9
+ const matches = (model) => model.capability === requires || (model.capabilities?.includes(requires) ?? false);
10
+ return providers.map((provider) => ({
11
+ ...provider,
12
+ models: provider.models.filter(matches)
13
+ })).filter((provider) => provider.models.length > 0);
14
+ }
15
+ var TaskAssignmentPicker = React.forwardRef(function TaskAssignmentPicker2({
16
+ tasks = [],
17
+ sections,
18
+ providers,
19
+ value,
20
+ onAssign,
21
+ secondaryValue,
22
+ onAssignSecondary,
23
+ secondaryLabel = "Fallback model",
24
+ renderMeta,
25
+ unassignedLabel = "Not assigned",
26
+ clearLabel = "Clear assignment",
27
+ modelLabel = "Model",
28
+ disabled = false,
29
+ className
30
+ }, ref) {
31
+ const modelName = React.useCallback(
32
+ (modelId) => {
33
+ if (!modelId) return null;
34
+ for (const provider of providers) {
35
+ const model = provider.models.find((m) => m.id === modelId);
36
+ if (model) return { name: model.name, provider: provider.name };
37
+ }
38
+ return null;
39
+ },
40
+ [providers]
41
+ );
42
+ const renderRow = (task, showSecondary) => {
43
+ const assigned = modelName(value[task.id]);
44
+ const catalog = filterProviders(providers, task.requires);
45
+ return /* @__PURE__ */ jsxs(
46
+ "div",
47
+ {
48
+ className: "flex flex-col gap-2 rounded-[var(--as-radius-lg)] border border-[var(--as-border)] bg-[var(--as-surface-raised)] p-3 sm:flex-row sm:items-center sm:justify-between",
49
+ children: [
50
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-3", children: [
51
+ task.icon ? /* @__PURE__ */ jsx("span", { className: "flex size-9 shrink-0 items-center justify-center rounded-[var(--as-radius)] bg-[var(--as-muted)] text-[var(--as-muted-fg)]", children: /* @__PURE__ */ jsx(task.icon, { className: "size-4", "aria-hidden": true }) }) : null,
52
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
53
+ /* @__PURE__ */ jsx("p", { className: "truncate text-sm font-semibold text-[var(--as-fg)]", children: task.label }),
54
+ task.description ? /* @__PURE__ */ jsx("p", { className: "truncate text-xs text-[var(--as-muted-fg)]", children: task.description }) : null,
55
+ renderMeta ? renderMeta(task) : null,
56
+ /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-[var(--as-muted-fg)]", children: assigned ? /* @__PURE__ */ jsxs(Fragment, { children: [
57
+ modelLabel,
58
+ ":",
59
+ " ",
60
+ /* @__PURE__ */ jsxs("span", { className: "font-medium text-[var(--as-fg)]", children: [
61
+ assigned.provider,
62
+ " / ",
63
+ assigned.name
64
+ ] })
65
+ ] }) : unassignedLabel })
66
+ ] })
67
+ ] }),
68
+ /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-1.5", children: [
69
+ showSecondary && onAssignSecondary ? /* @__PURE__ */ jsx(
70
+ ModelPicker,
71
+ {
72
+ providers: catalog,
73
+ value: secondaryValue?.[task.id] ?? "",
74
+ onChange: (modelId) => onAssignSecondary(task.id, modelId),
75
+ disabled,
76
+ clearable: Boolean(secondaryValue?.[task.id]),
77
+ clearLabel,
78
+ label: `${secondaryLabel} \u2014 ${task.label}`,
79
+ className: "w-48"
80
+ }
81
+ ) : null,
82
+ /* @__PURE__ */ jsx(
83
+ ModelPicker,
84
+ {
85
+ providers: catalog,
86
+ value: value[task.id] ?? "",
87
+ onChange: (modelId) => onAssign(task.id, modelId),
88
+ disabled,
89
+ clearable: Boolean(value[task.id]),
90
+ clearLabel,
91
+ label: `${modelLabel} \u2014 ${task.label}`,
92
+ className: "w-56"
93
+ }
94
+ ),
95
+ value[task.id] ? /* @__PURE__ */ jsx(
96
+ "button",
97
+ {
98
+ type: "button",
99
+ "aria-label": `${clearLabel} \u2014 ${task.label}`,
100
+ disabled,
101
+ onClick: () => onAssign(task.id, null),
102
+ className: "cursor-pointer rounded-[var(--as-radius-sm)] p-1.5 text-[var(--as-muted-fg)] transition-colors hover:bg-[var(--as-muted)] hover:text-[var(--as-fg)] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)] disabled:pointer-events-none disabled:opacity-50",
103
+ children: /* @__PURE__ */ jsx(X, { className: "size-4", "aria-hidden": true })
104
+ }
105
+ ) : null
106
+ ] })
107
+ ]
108
+ },
109
+ task.id
110
+ );
111
+ };
112
+ return /* @__PURE__ */ jsx(
113
+ "div",
114
+ {
115
+ ref,
116
+ "data-as": "task-assignment-picker",
117
+ className: cn("flex w-full flex-col gap-3", className),
118
+ children: sections ? sections.map((section) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
119
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-semibold uppercase tracking-wide text-[var(--as-muted-fg)]", children: section.label }),
120
+ section.tasks.map(
121
+ (task) => renderRow(task, section.secondary || Boolean(task.secondary))
122
+ )
123
+ ] }, section.id)) : tasks.map((task) => renderRow(task, Boolean(task.secondary)))
124
+ }
125
+ );
126
+ });
127
+
128
+ export { TaskAssignmentPicker };
129
+ //# sourceMappingURL=chunk-M67KF4W2.js.map
130
+ //# sourceMappingURL=chunk-M67KF4W2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/task-assignment-picker/TaskAssignmentPicker.tsx"],"names":["TaskAssignmentPicker"],"mappings":";;;;;;AAoDA,SAAS,eAAA,CACP,WACA,QAAA,EACuB;AACvB,EAAA,IAAI,CAAC,UAAU,OAAO,SAAA;AACtB,EAAA,MAAM,OAAA,GAAU,CAAC,KAAA,KACf,KAAA,CAAM,UAAA,KAAe,aAAa,KAAA,CAAM,YAAA,EAAc,QAAA,CAAS,QAAQ,CAAA,IAAK,KAAA,CAAA;AAC9E,EAAA,OAAO,SAAA,CACJ,GAAA,CAAI,CAAC,QAAA,MAAc;AAAA,IAClB,GAAG,QAAA;AAAA,IACH,MAAA,EAAQ,QAAA,CAAS,MAAA,CAAO,MAAA,CAAO,OAAO;AAAA,GACxC,CAAE,EACD,MAAA,CAAO,CAAC,aAAa,QAAA,CAAS,MAAA,CAAO,SAAS,CAAC,CAAA;AACpD;AAEO,IAAM,oBAAA,GAA6B,KAAA,CAAA,UAAA,CAGxC,SAASA,qBAAAA,CACT;AAAA,EACE,QAAQ,EAAC;AAAA,EACT,QAAA;AAAA,EACA,SAAA;AAAA,EACA,KAAA;AAAA,EACA,QAAA;AAAA,EACA,cAAA;AAAA,EACA,iBAAA;AAAA,EACA,cAAA,GAAiB,gBAAA;AAAA,EACjB,UAAA;AAAA,EACA,eAAA,GAAkB,cAAA;AAAA,EAClB,UAAA,GAAa,kBAAA;AAAA,EACb,UAAA,GAAa,OAAA;AAAA,EACb,QAAA,GAAW,KAAA;AAAA,EACX;AACF,CAAA,EACA,GAAA,EACA;AACA,EAAA,MAAM,SAAA,GAAkB,KAAA,CAAA,WAAA;AAAA,IACtB,CAAC,OAAA,KAAuC;AACtC,MAAA,IAAI,CAAC,SAAS,OAAO,IAAA;AACrB,MAAA,KAAA,MAAW,YAAY,SAAA,EAAW;AAChC,QAAA,MAAM,KAAA,GAAQ,SAAS,MAAA,CAAO,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,OAAO,OAAO,CAAA;AAC1D,QAAA,IAAI,KAAA,SAAc,EAAE,IAAA,EAAM,MAAM,IAAA,EAAM,QAAA,EAAU,SAAS,IAAA,EAAK;AAAA,MAChE;AACA,MAAA,OAAO,IAAA;AAAA,IACT,CAAA;AAAA,IACA,CAAC,SAAS;AAAA,GACZ;AAEA,EAAA,MAAM,SAAA,GAAY,CAAC,IAAA,EAA0B,aAAA,KAA2B;AACtE,IAAA,MAAM,QAAA,GAAW,SAAA,CAAU,KAAA,CAAM,IAAA,CAAK,EAAE,CAAC,CAAA;AACzC,IAAA,MAAM,OAAA,GAAU,eAAA,CAAgB,SAAA,EAAW,IAAA,CAAK,QAAQ,CAAA;AACxD,IAAA,uBACE,IAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QAEC,SAAA,EAAU,qKAAA;AAAA,QAEV,QAAA,EAAA;AAAA,0BAAA,IAAA,CAAC,KAAA,EAAA,EAAI,WAAU,iCAAA,EACZ,QAAA,EAAA;AAAA,YAAA,IAAA,CAAK,IAAA,mBACJ,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,8HACd,QAAA,kBAAA,GAAA,CAAC,IAAA,CAAK,IAAA,EAAL,EAAU,SAAA,EAAU,QAAA,EAAS,aAAA,EAAW,IAAA,EAAC,GAC5C,CAAA,GACE,IAAA;AAAA,4BACJ,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,SAAA,EACf,QAAA,EAAA;AAAA,8BAAA,GAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,oDAAA,EACV,QAAA,EAAA,IAAA,CAAK,KAAA,EACR,CAAA;AAAA,cACC,IAAA,CAAK,8BACJ,GAAA,CAAC,GAAA,EAAA,EAAE,WAAU,4CAAA,EACV,QAAA,EAAA,IAAA,CAAK,aACR,CAAA,GACE,IAAA;AAAA,cACH,UAAA,GAAa,UAAA,CAAW,IAAI,CAAA,GAAI,IAAA;AAAA,8BACjC,GAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,0CAAA,EACV,qCACC,IAAA,CAAA,QAAA,EAAA,EACG,QAAA,EAAA;AAAA,gBAAA,UAAA;AAAA,gBAAW,GAAA;AAAA,gBAAE,GAAA;AAAA,gCACd,IAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,iCAAA,EACb,QAAA,EAAA;AAAA,kBAAA,QAAA,CAAS,QAAA;AAAA,kBAAS,KAAA;AAAA,kBAAI,QAAA,CAAS;AAAA,iBAAA,EAClC;AAAA,eAAA,EACF,IAEA,eAAA,EAEJ;AAAA,aAAA,EACA;AAAA,WAAA,EACF,CAAA;AAAA,0BACA,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,oCAAA,EACZ,QAAA,EAAA;AAAA,YAAA,aAAA,IAAiB,iBAAA,mBAChB,GAAA;AAAA,cAAC,WAAA;AAAA,cAAA;AAAA,gBACC,SAAA,EAAW,OAAA;AAAA,gBACX,KAAA,EAAO,cAAA,GAAiB,IAAA,CAAK,EAAE,CAAA,IAAK,EAAA;AAAA,gBACpC,UAAU,CAAC,OAAA,KAAY,iBAAA,CAAkB,IAAA,CAAK,IAAI,OAAO,CAAA;AAAA,gBACzD,QAAA;AAAA,gBACA,SAAA,EAAW,OAAA,CAAQ,cAAA,GAAiB,IAAA,CAAK,EAAE,CAAC,CAAA;AAAA,gBAC5C,UAAA;AAAA,gBACA,KAAA,EAAO,CAAA,EAAG,cAAc,CAAA,QAAA,EAAM,KAAK,KAAK,CAAA,CAAA;AAAA,gBACxC,SAAA,EAAU;AAAA;AAAA,aACZ,GACE,IAAA;AAAA,4BACJ,GAAA;AAAA,cAAC,WAAA;AAAA,cAAA;AAAA,gBACC,SAAA,EAAW,OAAA;AAAA,gBACX,KAAA,EAAO,KAAA,CAAM,IAAA,CAAK,EAAE,CAAA,IAAK,EAAA;AAAA,gBACzB,UAAU,CAAC,OAAA,KAAY,QAAA,CAAS,IAAA,CAAK,IAAI,OAAO,CAAA;AAAA,gBAChD,QAAA;AAAA,gBACA,SAAA,EAAW,OAAA,CAAQ,KAAA,CAAM,IAAA,CAAK,EAAE,CAAC,CAAA;AAAA,gBACjC,UAAA;AAAA,gBACA,KAAA,EAAO,CAAA,EAAG,UAAU,CAAA,QAAA,EAAM,KAAK,KAAK,CAAA,CAAA;AAAA,gBACpC,SAAA,EAAU;AAAA;AAAA,aACZ;AAAA,YACC,KAAA,CAAM,IAAA,CAAK,EAAE,CAAA,mBACZ,GAAA;AAAA,cAAC,QAAA;AAAA,cAAA;AAAA,gBACC,IAAA,EAAK,QAAA;AAAA,gBACL,YAAA,EAAY,CAAA,EAAG,UAAU,CAAA,QAAA,EAAM,KAAK,KAAK,CAAA,CAAA;AAAA,gBACzC,QAAA;AAAA,gBACA,OAAA,EAAS,MAAM,QAAA,CAAS,IAAA,CAAK,IAAI,IAAI,CAAA;AAAA,gBACrC,SAAA,EAAU,0SAAA;AAAA,gBAEV,QAAA,kBAAA,GAAA,CAAC,CAAA,EAAA,EAAE,SAAA,EAAU,QAAA,EAAS,eAAW,IAAA,EAAC;AAAA;AAAA,aACpC,GACE;AAAA,WAAA,EACN;AAAA;AAAA,OAAA;AAAA,MAnEK,IAAA,CAAK;AAAA,KAoEZ;AAAA,EAEJ,CAAA;AAEA,EAAA,uBACE,GAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,GAAA;AAAA,MACA,SAAA,EAAQ,wBAAA;AAAA,MACR,SAAA,EAAW,EAAA,CAAG,4BAAA,EAA8B,SAAS,CAAA;AAAA,MAEpD,QAAA,EAAA,QAAA,GACG,SAAS,GAAA,CAAI,CAAC,4BACZ,IAAA,CAAC,KAAA,EAAA,EAAqB,WAAU,qBAAA,EAC9B,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,yEAAA,EACV,QAAA,EAAA,OAAA,CAAQ,KAAA,EACX,CAAA;AAAA,QACC,QAAQ,KAAA,CAAM,GAAA;AAAA,UAAI,CAAC,SAClB,SAAA,CAAU,IAAA,EAAM,QAAQ,SAAA,IAAa,OAAA,CAAQ,IAAA,CAAK,SAAS,CAAC;AAAA;AAC9D,OAAA,EAAA,EANQ,OAAA,CAAQ,EAOlB,CACD,CAAA,GACD,MAAM,GAAA,CAAI,CAAC,IAAA,KAAS,SAAA,CAAU,IAAA,EAAM,OAAA,CAAQ,IAAA,CAAK,SAAS,CAAC,CAAC;AAAA;AAAA,GAClE;AAEJ,CAAC","file":"chunk-M67KF4W2.js","sourcesContent":["import * as React from 'react'\nimport { X } from 'lucide-react'\nimport type { LucideIcon } from 'lucide-react'\nimport { cn } from '../../lib/utils'\nimport {\n ModelPicker,\n type ModelPickerModel,\n type ModelPickerProvider,\n} from '../model-picker/ModelPicker'\n\nexport interface TaskAssignmentTask {\n id: string\n label: string\n description?: string\n /** Rendered in an icon tile at the row's leading edge. */\n icon?: LucideIcon\n /** When set, the row's picker offers only models matching this capability. */\n requires?: string\n /** Render the fallback-model picker for this row (needs `onAssignSecondary`). */\n secondary?: boolean\n}\n\nexport interface TaskAssignmentSection {\n id: string\n label: string\n /** Show the fallback picker on every task in the section (task-level wins). */\n secondary?: boolean\n tasks: TaskAssignmentTask[]\n}\n\nexport interface TaskAssignmentPickerProps {\n /** Flat rendering; ignored when `sections` is set. */\n tasks?: TaskAssignmentTask[]\n /** Grouped rendering; when set, `tasks` is ignored. */\n sections?: TaskAssignmentSection[]\n providers: ModelPickerProvider[]\n /** Current mapping: taskId → modelId (null/undefined = unassigned). */\n value: Record<string, string | null>\n onAssign: (taskId: string, modelId: string | null) => void\n /** Enables fallback-model rows for tasks flagged (directly or via section). */\n secondaryValue?: Record<string, string | null>\n onAssignSecondary?: (taskId: string, modelId: string | null) => void\n secondaryLabel?: string\n /** App-rendered per-row context (inherit notes, spend, badges). */\n renderMeta?: (task: TaskAssignmentTask) => React.ReactNode\n unassignedLabel?: string\n clearLabel?: string\n modelLabel?: string\n disabled?: boolean\n className?: string\n}\n\nfunction filterProviders(\n providers: ModelPickerProvider[],\n requires: string | undefined,\n): ModelPickerProvider[] {\n if (!requires) return providers\n const matches = (model: ModelPickerModel) =>\n model.capability === requires || (model.capabilities?.includes(requires) ?? false)\n return providers\n .map((provider) => ({\n ...provider,\n models: provider.models.filter(matches),\n }))\n .filter((provider) => provider.models.length > 0)\n}\n\nexport const TaskAssignmentPicker = React.forwardRef<\n HTMLDivElement,\n TaskAssignmentPickerProps\n>(function TaskAssignmentPicker(\n {\n tasks = [],\n sections,\n providers,\n value,\n onAssign,\n secondaryValue,\n onAssignSecondary,\n secondaryLabel = 'Fallback model',\n renderMeta,\n unassignedLabel = 'Not assigned',\n clearLabel = 'Clear assignment',\n modelLabel = 'Model',\n disabled = false,\n className,\n },\n ref,\n) {\n const modelName = React.useCallback(\n (modelId: string | null | undefined) => {\n if (!modelId) return null\n for (const provider of providers) {\n const model = provider.models.find((m) => m.id === modelId)\n if (model) return { name: model.name, provider: provider.name }\n }\n return null\n },\n [providers],\n )\n\n const renderRow = (task: TaskAssignmentTask, showSecondary: boolean) => {\n const assigned = modelName(value[task.id])\n const catalog = filterProviders(providers, task.requires)\n return (\n <div\n key={task.id}\n className=\"flex flex-col gap-2 rounded-[var(--as-radius-lg)] border border-[var(--as-border)] bg-[var(--as-surface-raised)] p-3 sm:flex-row sm:items-center sm:justify-between\"\n >\n <div className=\"flex min-w-0 items-center gap-3\">\n {task.icon ? (\n <span className=\"flex size-9 shrink-0 items-center justify-center rounded-[var(--as-radius)] bg-[var(--as-muted)] text-[var(--as-muted-fg)]\">\n <task.icon className=\"size-4\" aria-hidden />\n </span>\n ) : null}\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-semibold text-[var(--as-fg)]\">\n {task.label}\n </p>\n {task.description ? (\n <p className=\"truncate text-xs text-[var(--as-muted-fg)]\">\n {task.description}\n </p>\n ) : null}\n {renderMeta ? renderMeta(task) : null}\n <p className=\"mt-0.5 text-xs text-[var(--as-muted-fg)]\">\n {assigned ? (\n <>\n {modelLabel}:{' '}\n <span className=\"font-medium text-[var(--as-fg)]\">\n {assigned.provider} / {assigned.name}\n </span>\n </>\n ) : (\n unassignedLabel\n )}\n </p>\n </div>\n </div>\n <div className=\"flex shrink-0 items-center gap-1.5\">\n {showSecondary && onAssignSecondary ? (\n <ModelPicker\n providers={catalog}\n value={secondaryValue?.[task.id] ?? ''}\n onChange={(modelId) => onAssignSecondary(task.id, modelId)}\n disabled={disabled}\n clearable={Boolean(secondaryValue?.[task.id])}\n clearLabel={clearLabel}\n label={`${secondaryLabel} — ${task.label}`}\n className=\"w-48\"\n />\n ) : null}\n <ModelPicker\n providers={catalog}\n value={value[task.id] ?? ''}\n onChange={(modelId) => onAssign(task.id, modelId)}\n disabled={disabled}\n clearable={Boolean(value[task.id])}\n clearLabel={clearLabel}\n label={`${modelLabel} — ${task.label}`}\n className=\"w-56\"\n />\n {value[task.id] ? (\n <button\n type=\"button\"\n aria-label={`${clearLabel} — ${task.label}`}\n disabled={disabled}\n onClick={() => onAssign(task.id, null)}\n className=\"cursor-pointer rounded-[var(--as-radius-sm)] p-1.5 text-[var(--as-muted-fg)] transition-colors hover:bg-[var(--as-muted)] hover:text-[var(--as-fg)] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)] disabled:pointer-events-none disabled:opacity-50\"\n >\n <X className=\"size-4\" aria-hidden />\n </button>\n ) : null}\n </div>\n </div>\n )\n }\n\n return (\n <div\n ref={ref}\n data-as=\"task-assignment-picker\"\n className={cn('flex w-full flex-col gap-3', className)}\n >\n {sections\n ? sections.map((section) => (\n <div key={section.id} className=\"flex flex-col gap-2\">\n <p className=\"text-xs font-semibold uppercase tracking-wide text-[var(--as-muted-fg)]\">\n {section.label}\n </p>\n {section.tasks.map((task) =>\n renderRow(task, section.secondary || Boolean(task.secondary)),\n )}\n </div>\n ))\n : tasks.map((task) => renderRow(task, Boolean(task.secondary)))}\n </div>\n )\n})\n"]}