mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-08 08:11:38 +00:00
feat(web): migrate dashboard checkboxes to @nous-research/ui + DS polish (#28814)
* feat(web): migrate dashboard checkboxes to @nous-research/ui + DS polish Replaces the hand-rolled shadcn-style `Checkbox` in `web/src/components/ui/` with the Nous DS `Checkbox` (Radix-backed) from `@nous-research/ui`, bumps the DS to 0.14.2, and picks up two regressions surfaced by the bump. Checkbox migration - bump `@nous-research/ui` 0.14.0 → ^0.14.2 and remove `web/src/components/ui/checkbox.tsx` - migrate `ProfilesPage` and `ModelPickerDialog` to the DS Checkbox API (`onCheckedChange`, paired `<Label htmlFor>`) - expose `Checkbox` on the dashboard plugin SDK (`web/src/plugins/registry.ts`) so plugin bundles can use the same DS component - migrate the kanban dashboard plugin's 7 native `<input type="checkbox">` call sites to the SDK `Checkbox`, with a native-input fallback shim so the bundle still renders against older hosts that predate the SDK export Fix: missing font registrations after the 0.14.x split - import `@nous-research/ui/styles/fonts.css` before `globals.css` in `web/src/index.css`. As of 0.14.x, `globals.css` only declares the `--font-*` variables (Collapse, Mondwest, Rules Compressed/Expanded); the `@font-face` registrations now live in a separate `fonts.css`, so without this import the DS components silently fall back to a system font stack and look unstyled. Fix: right-align page header toolbars on sm+ viewports - The mobile dashboard polish in #28127 flipped four pages' `setEnd(...)` wrappers from `justify-end` to `w-full ... justify-start` so toolbars stack below the title and align left on small screens. But the outer `end` slot in `PageHeaderProvider` already has `sm:justify-end`, and that has no effect when its only child is `w-full` — once a flex child fills the row, the parent's `justify-*` can't move it. The toolbar pinned to the *left* of the right-side `sm:max-w-md` (~448px) slot, making the buttons appear to float a couple-hundred pixels off the right edge on Analytics, Models, Logs, and Plugins. - Re-add `sm:justify-end` on the inner wrapper of each affected page, preserving the mobile stacked layout. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(nix): update web npmDeps hash for package-lock bump Co-authored-by: Cursor <cursoragent@cursor.com> * fix(nix): refresh npm lockfile hashes * chore(ci): re-trigger checks after nix lockfile hash fix Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This commit is contained in:
parent
42c4288411
commit
edb2d91057
13 changed files with 282 additions and 207 deletions
|
|
@ -4,7 +4,7 @@ let
|
||||||
src = ../web;
|
src = ../web;
|
||||||
npmDeps = pkgs.fetchNpmDeps {
|
npmDeps = pkgs.fetchNpmDeps {
|
||||||
inherit src;
|
inherit src;
|
||||||
hash = "sha256-FL2E8Vv8gyeClEa5b/pHn/ekWoHWTd4YwzV6zhLEos4=";
|
hash = "sha256-GxSmEpclOwmv94KmGMediPITxqXAsxqTEQOoDIbYkUw=";
|
||||||
};
|
};
|
||||||
|
|
||||||
npm = hermesNpmLib.mkNpmPassthru { folder = "web"; attr = "web"; pname = "hermes-web"; };
|
npm = hermesNpmLib.mkNpmPassthru { folder = "web"; attr = "web"; pname = "hermes-web"; };
|
||||||
|
|
|
||||||
58
plugins/kanban/dashboard/dist/index.js
vendored
58
plugins/kanban/dashboard/dist/index.js
vendored
|
|
@ -24,6 +24,23 @@
|
||||||
const { useState, useEffect, useCallback, useMemo, useRef } = SDK.hooks;
|
const { useState, useEffect, useCallback, useMemo, useRef } = SDK.hooks;
|
||||||
const { cn, timeAgo } = SDK.utils;
|
const { cn, timeAgo } = SDK.utils;
|
||||||
|
|
||||||
|
// Newer host dashboards expose a DS-styled Checkbox on the plugin SDK.
|
||||||
|
// Fall back to a native <input type="checkbox"> shim so older hosts that
|
||||||
|
// predate the design-system rollout still render. The shim normalises
|
||||||
|
// Radix's onCheckedChange(checked) signature to native onChange(event).
|
||||||
|
const Checkbox = SDK.components.Checkbox || function (props) {
|
||||||
|
const { checked, onCheckedChange, className, onClick, ...rest } = props;
|
||||||
|
return h("input", Object.assign({
|
||||||
|
type: "checkbox",
|
||||||
|
checked: !!checked,
|
||||||
|
className: className,
|
||||||
|
onClick: onClick,
|
||||||
|
onChange: function (e) {
|
||||||
|
if (onCheckedChange) onCheckedChange(e.target.checked);
|
||||||
|
},
|
||||||
|
}, rest));
|
||||||
|
};
|
||||||
|
|
||||||
// useI18n is a hook each component calls locally. Older host dashboards
|
// useI18n is a hook each component calls locally. Older host dashboards
|
||||||
// may not expose it yet; fall back to a shim so the bundle still renders
|
// may not expose it yet; fall back to a shim so the bundle still renders
|
||||||
// English against an older host SDK. English fallback strings live
|
// English against an older host SDK. English fallback strings live
|
||||||
|
|
@ -1648,11 +1665,10 @@
|
||||||
h(Label, { className: "text-xs text-muted-foreground" },
|
h(Label, { className: "text-xs text-muted-foreground" },
|
||||||
"Orchestration mode"),
|
"Orchestration mode"),
|
||||||
h("label", { className: "flex items-center gap-2 text-xs h-8" },
|
h("label", { className: "flex items-center gap-2 text-xs h-8" },
|
||||||
h("input", {
|
h(Checkbox, {
|
||||||
type: "checkbox",
|
|
||||||
checked: !!settings.auto_decompose,
|
checked: !!settings.auto_decompose,
|
||||||
onChange: function (e) {
|
onCheckedChange: function (checked) {
|
||||||
saveSettings({ auto_decompose: !!e.target.checked });
|
saveSettings({ auto_decompose: checked === true });
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
"Auto-decompose triage tasks",
|
"Auto-decompose triage tasks",
|
||||||
|
|
@ -1908,10 +1924,9 @@
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
h("label", { className: "flex items-center gap-2 text-xs" },
|
h("label", { className: "flex items-center gap-2 text-xs" },
|
||||||
h("input", {
|
h(Checkbox, {
|
||||||
type: "checkbox",
|
|
||||||
checked: switchTo,
|
checked: switchTo,
|
||||||
onChange: function (e) { setSwitchTo(e.target.checked); },
|
onCheckedChange: function (checked) { setSwitchTo(checked === true); },
|
||||||
}),
|
}),
|
||||||
tx(t, "switchAfterCreate", "Switch to this board after creating it"),
|
tx(t, "switchAfterCreate", "Switch to this board after creating it"),
|
||||||
),
|
),
|
||||||
|
|
@ -1981,19 +1996,17 @@
|
||||||
),
|
),
|
||||||
h("label", { className: "flex items-center gap-2 text-xs",
|
h("label", { className: "flex items-center gap-2 text-xs",
|
||||||
title: "Include archived tasks in the board view. Archived tasks are hidden by default." },
|
title: "Include archived tasks in the board view. Archived tasks are hidden by default." },
|
||||||
h("input", {
|
h(Checkbox, {
|
||||||
type: "checkbox",
|
|
||||||
checked: props.includeArchived,
|
checked: props.includeArchived,
|
||||||
onChange: function (e) { props.setIncludeArchived(e.target.checked); },
|
onCheckedChange: function (checked) { props.setIncludeArchived(checked === true); },
|
||||||
}),
|
}),
|
||||||
tx(t, "showArchived", "Show archived"),
|
tx(t, "showArchived", "Show archived"),
|
||||||
),
|
),
|
||||||
h("label", { className: "flex items-center gap-2 text-xs",
|
h("label", { className: "flex items-center gap-2 text-xs",
|
||||||
title: "Group the Running column by assigned profile" },
|
title: "Group the Running column by assigned profile" },
|
||||||
h("input", {
|
h(Checkbox, {
|
||||||
type: "checkbox",
|
|
||||||
checked: props.laneByProfile,
|
checked: props.laneByProfile,
|
||||||
onChange: function (e) { props.setLaneByProfile(e.target.checked); },
|
onCheckedChange: function (checked) { props.setLaneByProfile(checked === true); },
|
||||||
}),
|
}),
|
||||||
tx(t, "lanesByProfile", "Lanes by profile"),
|
tx(t, "lanesByProfile", "Lanes by profile"),
|
||||||
),
|
),
|
||||||
|
|
@ -2122,10 +2135,9 @@
|
||||||
}, tx(t, "apply", "Apply")),
|
}, tx(t, "apply", "Apply")),
|
||||||
),
|
),
|
||||||
h("label", { className: "hermes-kanban-bulk-reclaim-first", title: "Reclaim any active claims before reassigning" },
|
h("label", { className: "hermes-kanban-bulk-reclaim-first", title: "Reclaim any active claims before reassigning" },
|
||||||
h("input", {
|
h(Checkbox, {
|
||||||
type: "checkbox",
|
|
||||||
checked: reclaimFirst,
|
checked: reclaimFirst,
|
||||||
onChange: function (e) { setReclaimFirst(e.target.checked); },
|
onCheckedChange: function (checked) { setReclaimFirst(checked === true); },
|
||||||
}),
|
}),
|
||||||
"Reclaim first",
|
"Reclaim first",
|
||||||
),
|
),
|
||||||
|
|
@ -2313,14 +2325,12 @@
|
||||||
},
|
},
|
||||||
h("div", { className: "hermes-kanban-column-header",
|
h("div", { className: "hermes-kanban-column-header",
|
||||||
title: colHelp || "" },
|
title: colHelp || "" },
|
||||||
h("input", {
|
h(Checkbox, {
|
||||||
type: "checkbox",
|
|
||||||
className: "hermes-kanban-col-check",
|
className: "hermes-kanban-col-check",
|
||||||
title: "Select all tasks in this column",
|
title: "Select all tasks in this column",
|
||||||
"aria-label": `Select all tasks in ${colLabel || props.column.name}`,
|
"aria-label": `Select all tasks in ${colLabel || props.column.name}`,
|
||||||
checked: props.column.tasks.length > 0 && props.column.tasks.every(function (t) { return props.selectedIds.has(t.id); }),
|
checked: props.column.tasks.length > 0 && props.column.tasks.every(function (t) { return props.selectedIds.has(t.id); }),
|
||||||
onChange: function (e) {
|
onCheckedChange: function () {
|
||||||
e.stopPropagation();
|
|
||||||
if (props.selectAllInColumn) props.selectAllInColumn(props.column.name);
|
if (props.selectAllInColumn) props.selectAllInColumn(props.column.name);
|
||||||
},
|
},
|
||||||
onClick: function (e) { e.stopPropagation(); },
|
onClick: function (e) { e.stopPropagation(); },
|
||||||
|
|
@ -2461,8 +2471,7 @@
|
||||||
if (props.toggleSelected) props.toggleSelected(t.id, false);
|
if (props.toggleSelected) props.toggleSelected(t.id, false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const handleCheckbox = function (e) {
|
const handleCheckedChange = function () {
|
||||||
e.stopPropagation();
|
|
||||||
props.toggleSelected(t.id, true);
|
props.toggleSelected(t.id, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -2495,11 +2504,10 @@
|
||||||
title: tx(i18n, "selectForBulk", "Select for bulk actions"),
|
title: tx(i18n, "selectForBulk", "Select for bulk actions"),
|
||||||
onClick: function (e) { e.stopPropagation(); },
|
onClick: function (e) { e.stopPropagation(); },
|
||||||
},
|
},
|
||||||
h("input", {
|
h(Checkbox, {
|
||||||
type: "checkbox",
|
|
||||||
className: "hermes-kanban-card-check",
|
className: "hermes-kanban-card-check",
|
||||||
checked: props.selected,
|
checked: props.selected,
|
||||||
onChange: handleCheckbox,
|
onCheckedChange: handleCheckedChange,
|
||||||
onClick: function (e) { e.stopPropagation(); },
|
onClick: function (e) { e.stopPropagation(); },
|
||||||
"aria-label": `Select task ${t.id}`,
|
"aria-label": `Select task ${t.id}`,
|
||||||
}),
|
}),
|
||||||
|
|
|
||||||
263
web/package-lock.json
generated
263
web/package-lock.json
generated
|
|
@ -8,7 +8,7 @@
|
||||||
"name": "web",
|
"name": "web",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nous-research/ui": "0.14.0",
|
"@nous-research/ui": "^0.14.2",
|
||||||
"@observablehq/plot": "^0.6.17",
|
"@observablehq/plot": "^0.6.17",
|
||||||
"@react-three/fiber": "^9.6.0",
|
"@react-three/fiber": "^9.6.0",
|
||||||
"@tailwindcss/vite": "^4.2.1",
|
"@tailwindcss/vite": "^4.2.1",
|
||||||
|
|
@ -1081,12 +1081,13 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@nous-research/ui": {
|
"node_modules/@nous-research/ui": {
|
||||||
"version": "0.14.0",
|
"version": "0.14.2",
|
||||||
"resolved": "https://registry.npmjs.org/@nous-research/ui/-/ui-0.14.0.tgz",
|
"resolved": "https://registry.npmjs.org/@nous-research/ui/-/ui-0.14.2.tgz",
|
||||||
"integrity": "sha512-tfpE6jGOxE5oVBab/dTSepOudy/+Xep3gJ6NCFriYJvdtQBGXcqsi4mCaVPiNNaS/ZFf4/10dnl/oJTb6DtLKg==",
|
"integrity": "sha512-H3cMt2e0IpmcTNOmR6zVX+8ja48w4X4F/IFXhWCpaoVs8zKVRN12Ryb4RnX/ac8IrbUu6UsIds7ZtmXxPHcfdQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nanostores/react": "^1.1.0",
|
"@nanostores/react": "^1.1.0",
|
||||||
|
"@radix-ui/react-checkbox": "^1.3.3",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"nanostores": "^1.3.0",
|
"nanostores": "^1.3.0",
|
||||||
|
|
@ -1208,6 +1209,77 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-checkbox": {
|
||||||
|
"version": "1.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.3.3.tgz",
|
||||||
|
"integrity": "sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/primitive": "1.1.3",
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.2",
|
||||||
|
"@radix-ui/react-context": "1.1.2",
|
||||||
|
"@radix-ui/react-presence": "1.1.5",
|
||||||
|
"@radix-ui/react-primitive": "2.1.3",
|
||||||
|
"@radix-ui/react-use-controllable-state": "1.2.2",
|
||||||
|
"@radix-ui/react-use-previous": "1.1.1",
|
||||||
|
"@radix-ui/react-use-size": "1.1.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-checkbox/node_modules/@radix-ui/react-primitive": {
|
||||||
|
"version": "2.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
|
||||||
|
"integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-slot": "1.2.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-checkbox/node_modules/@radix-ui/react-slot": {
|
||||||
|
"version": "1.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
|
||||||
|
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-compose-refs": {
|
"node_modules/@radix-ui/react-compose-refs": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
|
||||||
|
|
@ -1670,6 +1742,21 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-use-previous": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-use-rect": {
|
"node_modules/@radix-ui/react-use-rect": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz",
|
||||||
|
|
@ -3648,6 +3735,12 @@
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/dayjs": {
|
||||||
|
"version": "1.11.20",
|
||||||
|
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.20.tgz",
|
||||||
|
"integrity": "sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/debug": {
|
"node_modules/debug": {
|
||||||
"version": "4.4.3",
|
"version": "4.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
||||||
|
|
@ -4193,6 +4286,33 @@
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/framer-motion": {
|
||||||
|
"version": "12.39.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.39.0.tgz",
|
||||||
|
"integrity": "sha512-+vnLfzrv0MzjLzNl+nvNvR7jdg3q4cxxjz/YvzfifHl0TREtL00cs1RoMTxs+1PzLiEqZGV6gYsBY0oEAYZ24w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"motion-dom": "^12.39.0",
|
||||||
|
"motion-utils": "^12.39.0",
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@emotion/is-prop-valid": "*",
|
||||||
|
"react": "^18.0.0 || ^19.0.0",
|
||||||
|
"react-dom": "^18.0.0 || ^19.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@emotion/is-prop-valid": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/fsevents": {
|
"node_modules/fsevents": {
|
||||||
"version": "2.3.3",
|
"version": "2.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||||
|
|
@ -4564,6 +4684,15 @@
|
||||||
"json-buffer": "3.0.1"
|
"json-buffer": "3.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/launder": {
|
||||||
|
"version": "1.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/launder/-/launder-1.7.1.tgz",
|
||||||
|
"integrity": "sha512-mU6WRz5EusL9ZZuiZ5SO4Y6C0P9PAUR9iwdb6bzj4KDihm28DiHFw+/yk9DBH4f+Pv1wuzQ4e2jV3oQ7mkIqvw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"dayjs": "^1.11.7"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/leva": {
|
"node_modules/leva": {
|
||||||
"version": "0.10.1",
|
"version": "0.10.1",
|
||||||
"resolved": "https://registry.npmjs.org/leva/-/leva-0.10.1.tgz",
|
"resolved": "https://registry.npmjs.org/leva/-/leva-0.10.1.tgz",
|
||||||
|
|
@ -4972,6 +5101,48 @@
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/motion": {
|
||||||
|
"version": "12.39.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/motion/-/motion-12.39.0.tgz",
|
||||||
|
"integrity": "sha512-H4a+Ze+a9j+/NTla5ezfb/g9vmIOxC+viDj++NGDZyTZkdRKjiOz3kSv6TalRWM8ZmD2y/CfC6TkQc97ybyqSA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"framer-motion": "^12.39.0",
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@emotion/is-prop-valid": "*",
|
||||||
|
"react": "^18.0.0 || ^19.0.0",
|
||||||
|
"react-dom": "^18.0.0 || ^19.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@emotion/is-prop-valid": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/motion-dom": {
|
||||||
|
"version": "12.39.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.39.0.tgz",
|
||||||
|
"integrity": "sha512-Xn7aAcGDhco/JZTXOub64UmaYn73C6J1Po7Fk+8EvkJsNGTqfhon6UJY53vJKXW5v5Zl8HrYsVxv6oPXeGoGLQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"motion-utils": "^12.39.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/motion-utils": {
|
||||||
|
"version": "12.39.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.39.0.tgz",
|
||||||
|
"integrity": "sha512-8nadJAJjTtqRkmRF36FoJTrywK9nnFmnPwnSMyxaOCU7GDjN9RTMJIxx9De8ErM+vpPhMccr/6fo5WciyQLnMQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
|
|
@ -5938,90 +6109,6 @@
|
||||||
"optional": true
|
"optional": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"node_modules/dayjs": {
|
|
||||||
"version": "1.11.20",
|
|
||||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.20.tgz",
|
|
||||||
"integrity": "sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/framer-motion": {
|
|
||||||
"version": "12.38.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.38.0.tgz",
|
|
||||||
"integrity": "sha512-rFYkY/pigbcswl1XQSb7q424kSTQ8q6eAC+YUsSKooHQYuLdzdHjrt6uxUC+PRAO++q5IS7+TamgIw1AphxR+g==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"motion-dom": "^12.38.0",
|
|
||||||
"motion-utils": "^12.36.0",
|
|
||||||
"tslib": "^2.4.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@emotion/is-prop-valid": "*",
|
|
||||||
"react": "^18.0.0 || ^19.0.0",
|
|
||||||
"react-dom": "^18.0.0 || ^19.0.0"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"@emotion/is-prop-valid": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"react": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"react-dom": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/launder": {
|
|
||||||
"version": "1.7.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/launder/-/launder-1.7.1.tgz",
|
|
||||||
"integrity": "sha512-mU6WRz5EusL9ZZuiZ5SO4Y6C0P9PAUR9iwdb6bzj4KDihm28DiHFw+/yk9DBH4f+Pv1wuzQ4e2jV3oQ7mkIqvw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"dayjs": "^1.11.7"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/motion": {
|
|
||||||
"version": "12.38.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/motion/-/motion-12.38.0.tgz",
|
|
||||||
"integrity": "sha512-uYfXzeHlgThchzwz5Te47dlv5JOUC7OB4rjJ/7XTUgtBZD8CchMN8qEJ4ZVsUmTyYA44zjV0fBwsiktRuFnn+w==",
|
|
||||||
"license": "MIT",
|
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
|
||||||
"framer-motion": "^12.38.0",
|
|
||||||
"tslib": "^2.4.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@emotion/is-prop-valid": "*",
|
|
||||||
"react": "^18.0.0 || ^19.0.0",
|
|
||||||
"react-dom": "^18.0.0 || ^19.0.0"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"@emotion/is-prop-valid": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"react": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"react-dom": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/motion-dom": {
|
|
||||||
"version": "12.38.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.38.0.tgz",
|
|
||||||
"integrity": "sha512-pdkHLD8QYRp8VfiNLb8xIBJis1byQ9gPT3Jnh2jqfFtAsWUA3dEepDlsWe/xMpO8McV+VdpKVcp+E+TGJEtOoA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"motion-utils": "^12.36.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/motion-utils": {
|
|
||||||
"version": "12.36.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.36.0.tgz",
|
|
||||||
"integrity": "sha512-eHWisygbiwVvf6PZ1vhaHCLamvkSbPIeAYxWUuL3a2PD/TROgE7FvfHWTIH4vMl798QLfMw15nRqIaRDXTlYRg==",
|
|
||||||
"license": "MIT"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nous-research/ui": "0.14.0",
|
"@nous-research/ui": "^0.14.2",
|
||||||
"@observablehq/plot": "^0.6.17",
|
"@observablehq/plot": "^0.6.17",
|
||||||
"@react-three/fiber": "^9.6.0",
|
"@react-three/fiber": "^9.6.0",
|
||||||
"@tailwindcss/vite": "^4.2.1",
|
"@tailwindcss/vite": "^4.2.1",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
import { Button } from "@nous-research/ui/ui/components/button";
|
import { Button } from "@nous-research/ui/ui/components/button";
|
||||||
|
import { Checkbox } from "@nous-research/ui/ui/components/checkbox";
|
||||||
import { ListItem } from "@nous-research/ui/ui/components/list-item";
|
import { ListItem } from "@nous-research/ui/ui/components/list-item";
|
||||||
import { Spinner } from "@nous-research/ui/ui/components/spinner";
|
import { Spinner } from "@nous-research/ui/ui/components/spinner";
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import type { GatewayClient } from "@/lib/gatewayClient";
|
import type { GatewayClient } from "@/lib/gatewayClient";
|
||||||
import { Check, Search, X } from "lucide-react";
|
import { Check, Search, X } from "lucide-react";
|
||||||
|
|
@ -283,15 +285,22 @@ export function ModelPickerDialog(props: Props) {
|
||||||
Saves to config.yaml — applies to new sessions.
|
Saves to config.yaml — applies to new sessions.
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
<label className="flex items-center gap-2 text-xs text-muted-foreground cursor-pointer select-none">
|
<div className="flex items-center gap-2">
|
||||||
<input
|
<Checkbox
|
||||||
type="checkbox"
|
|
||||||
checked={persistGlobal}
|
checked={persistGlobal}
|
||||||
onChange={(e) => setPersistGlobal(e.target.checked)}
|
id="model-picker-persist-global"
|
||||||
className="cursor-pointer"
|
onCheckedChange={(checked) =>
|
||||||
|
setPersistGlobal(checked === true)
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
Persist globally (otherwise this session only)
|
|
||||||
</label>
|
<Label
|
||||||
|
className="font-sans normal-case tracking-normal text-xs text-muted-foreground cursor-pointer"
|
||||||
|
htmlFor="model-picker-persist-global"
|
||||||
|
>
|
||||||
|
Persist globally (otherwise this session only)
|
||||||
|
</Label>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="flex items-center gap-2 ml-auto">
|
<div className="flex items-center gap-2 ml-auto">
|
||||||
|
|
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
import { Check } from "lucide-react";
|
|
||||||
|
|
||||||
interface CheckboxProps
|
|
||||||
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "type"> {
|
|
||||||
label?: React.ReactNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Checkbox({
|
|
||||||
className,
|
|
||||||
label,
|
|
||||||
id,
|
|
||||||
checked,
|
|
||||||
defaultChecked,
|
|
||||||
...props
|
|
||||||
}: CheckboxProps) {
|
|
||||||
// Support both controlled (checked prop) and uncontrolled (defaultChecked) usage.
|
|
||||||
// For visual rendering, prefer `checked` if provided; otherwise fall back to defaultChecked.
|
|
||||||
const isChecked = checked ?? defaultChecked ?? false;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<label
|
|
||||||
htmlFor={id}
|
|
||||||
className={cn(
|
|
||||||
"group flex items-center gap-2.5 cursor-pointer select-none",
|
|
||||||
props.disabled && "cursor-not-allowed opacity-50",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
className={cn(
|
|
||||||
"flex h-4 w-4 shrink-0 items-center justify-center transition-all",
|
|
||||||
"border bg-background/40",
|
|
||||||
// Focus-visible ring for keyboard accessibility
|
|
||||||
"group-has-[:focus-visible]:ring-2 group-has-[:focus-visible]:ring-ring group-has-[:focus-visible]:ring-offset-1",
|
|
||||||
isChecked
|
|
||||||
? "border-foreground bg-foreground/20"
|
|
||||||
: "border-border group-hover:border-foreground/40",
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Check
|
|
||||||
className={cn(
|
|
||||||
"h-3 w-3 transition-opacity",
|
|
||||||
isChecked
|
|
||||||
? "text-foreground opacity-100"
|
|
||||||
: "text-foreground opacity-0",
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
id={id}
|
|
||||||
checked={checked}
|
|
||||||
defaultChecked={checked === undefined ? defaultChecked : undefined}
|
|
||||||
className="sr-only"
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
{label && <span className="text-sm">{label}</span>}
|
|
||||||
</label>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,4 +1,11 @@
|
||||||
@import 'tailwindcss';
|
@import 'tailwindcss';
|
||||||
|
/* `fonts.css` must come BEFORE `globals.css`: as of @nous-research/ui 0.14.x,
|
||||||
|
`globals.css` only declares the `--font-*` CSS variables (Collapse, Rules
|
||||||
|
Compressed/Expanded, Mondwest). The `@font-face` registrations live in
|
||||||
|
`fonts.css`, so without this import the DS variables resolve to font
|
||||||
|
families the browser never loads and components fall back to a system
|
||||||
|
stack (Tabs, Segmented, Typography, Buttons, etc. all look unstyled). */
|
||||||
|
@import '@nous-research/ui/styles/fonts.css';
|
||||||
@import '@nous-research/ui/styles/globals.css';
|
@import '@nous-research/ui/styles/globals.css';
|
||||||
|
|
||||||
/* Scan the published design-system bundle so its utility classes survive
|
/* Scan the published design-system bundle so its utility classes survive
|
||||||
|
|
|
||||||
|
|
@ -439,7 +439,7 @@ export default function AnalyticsPage() {
|
||||||
);
|
);
|
||||||
setEnd(
|
setEnd(
|
||||||
showTokens === false ? null : (
|
showTokens === false ? null : (
|
||||||
<div className="flex w-full min-w-0 flex-wrap items-center justify-start gap-2 sm:gap-2">
|
<div className="flex w-full min-w-0 flex-wrap items-center justify-start gap-2 sm:justify-end sm:gap-2">
|
||||||
<div className="flex flex-wrap items-center gap-1.5">
|
<div className="flex flex-wrap items-center gap-1.5">
|
||||||
{PERIODS.map((p) => (
|
{PERIODS.map((p) => (
|
||||||
<Button
|
<Button
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ export default function LogsPage() {
|
||||||
</span>,
|
</span>,
|
||||||
);
|
);
|
||||||
setEnd(
|
setEnd(
|
||||||
<div className="flex w-full min-w-0 flex-wrap items-center justify-start gap-2 sm:gap-3">
|
<div className="flex w-full min-w-0 flex-wrap items-center justify-start gap-2 sm:justify-end sm:gap-3">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Switch
|
<Switch
|
||||||
checked={autoRefresh}
|
checked={autoRefresh}
|
||||||
|
|
|
||||||
|
|
@ -827,7 +827,7 @@ export default function ModelsPage() {
|
||||||
</span>,
|
</span>,
|
||||||
);
|
);
|
||||||
setEnd(
|
setEnd(
|
||||||
<div className="flex w-full min-w-0 flex-wrap items-center justify-start gap-2 sm:gap-2">
|
<div className="flex w-full min-w-0 flex-wrap items-center justify-start gap-2 sm:justify-end sm:gap-2">
|
||||||
<div className="flex flex-wrap items-center gap-1.5">
|
<div className="flex flex-wrap items-center gap-1.5">
|
||||||
{PERIODS.map((p) => (
|
{PERIODS.map((p) => (
|
||||||
<Button
|
<Button
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ export default function PluginsPage() {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setEnd(
|
setEnd(
|
||||||
<div className="flex w-full min-w-0 justify-start">
|
<div className="flex w-full min-w-0 justify-start sm:justify-end">
|
||||||
<Button
|
<Button
|
||||||
ghost
|
ghost
|
||||||
size="sm"
|
size="sm"
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,19 @@
|
||||||
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
|
import {
|
||||||
import { ChevronDown, Pencil, Plus, Terminal, Trash2, Users, X } from "lucide-react";
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useLayoutEffect,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
} from "react";
|
||||||
|
import {
|
||||||
|
ChevronDown,
|
||||||
|
Pencil,
|
||||||
|
Plus,
|
||||||
|
Terminal,
|
||||||
|
Trash2,
|
||||||
|
Users,
|
||||||
|
X,
|
||||||
|
} from "lucide-react";
|
||||||
import spinners from "unicode-animations";
|
import spinners from "unicode-animations";
|
||||||
import { H2 } from "@/components/NouiTypography";
|
import { H2 } from "@/components/NouiTypography";
|
||||||
import { api } from "@/lib/api";
|
import { api } from "@/lib/api";
|
||||||
|
|
@ -14,7 +28,7 @@ import { Badge } from "@nous-research/ui/ui/components/badge";
|
||||||
import { Button } from "@nous-research/ui/ui/components/button";
|
import { Button } from "@nous-research/ui/ui/components/button";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { Checkbox } from "@/components/ui/checkbox";
|
import { Checkbox } from "@nous-research/ui/ui/components/checkbox";
|
||||||
import { useI18n } from "@/i18n";
|
import { useI18n } from "@/i18n";
|
||||||
import { usePageHeader } from "@/contexts/usePageHeader";
|
import { usePageHeader } from "@/contexts/usePageHeader";
|
||||||
|
|
||||||
|
|
@ -131,7 +145,10 @@ export default function ProfilesPage() {
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await api.renameProfile(renamingFrom, target);
|
await api.renameProfile(renamingFrom, target);
|
||||||
showToast(`${t.profiles.renamed}: ${renamingFrom} → ${target}`, "success");
|
showToast(
|
||||||
|
`${t.profiles.renamed}: ${renamingFrom} → ${target}`,
|
||||||
|
"success",
|
||||||
|
);
|
||||||
setRenamingFrom(null);
|
setRenamingFrom(null);
|
||||||
setRenameTo("");
|
setRenameTo("");
|
||||||
load();
|
load();
|
||||||
|
|
@ -214,10 +231,7 @@ export default function ProfilesPage() {
|
||||||
// Put "Create" button in page header
|
// Put "Create" button in page header
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
setEnd(
|
setEnd(
|
||||||
<Button
|
<Button size="sm" onClick={() => setCreateModalOpen(true)}>
|
||||||
size="sm"
|
|
||||||
onClick={() => setCreateModalOpen(true)}
|
|
||||||
>
|
|
||||||
<Plus className="h-3 w-3" />
|
<Plus className="h-3 w-3" />
|
||||||
{t.common.create}
|
{t.common.create}
|
||||||
</Button>,
|
</Button>,
|
||||||
|
|
@ -266,7 +280,9 @@ export default function ProfilesPage() {
|
||||||
<div
|
<div
|
||||||
ref={createModalRef}
|
ref={createModalRef}
|
||||||
className="fixed inset-0 z-[100] flex items-center justify-center bg-background/85 backdrop-blur-sm p-4"
|
className="fixed inset-0 z-[100] flex items-center justify-center bg-background/85 backdrop-blur-sm p-4"
|
||||||
onClick={(e) => e.target === e.currentTarget && setCreateModalOpen(false)}
|
onClick={(e) =>
|
||||||
|
e.target === e.currentTarget && setCreateModalOpen(false)
|
||||||
|
}
|
||||||
role="dialog"
|
role="dialog"
|
||||||
aria-modal="true"
|
aria-modal="true"
|
||||||
aria-labelledby="create-profile-title"
|
aria-labelledby="create-profile-title"
|
||||||
|
|
@ -313,12 +329,22 @@ export default function ProfilesPage() {
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Checkbox
|
<div className="flex items-center gap-2.5">
|
||||||
id="clone-from-default"
|
<Checkbox
|
||||||
checked={cloneFromDefault}
|
checked={cloneFromDefault}
|
||||||
onChange={(e) => setCloneFromDefault(e.target.checked)}
|
id="clone-from-default"
|
||||||
label={t.profiles.cloneFromDefault}
|
onCheckedChange={(checked) =>
|
||||||
/>
|
setCloneFromDefault(checked === true)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Label
|
||||||
|
className="font-sans normal-case tracking-normal text-sm cursor-pointer"
|
||||||
|
htmlFor="clone-from-default"
|
||||||
|
>
|
||||||
|
{t.profiles.cloneFromDefault}
|
||||||
|
</Label>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<Button size="sm" onClick={handleCreate} disabled={creating}>
|
<Button size="sm" onClick={handleCreate} disabled={creating}>
|
||||||
|
|
@ -426,10 +452,7 @@ export default function ProfilesPage() {
|
||||||
<div className="flex items-center gap-1 shrink-0">
|
<div className="flex items-center gap-1 shrink-0">
|
||||||
{isRenaming ? (
|
{isRenaming ? (
|
||||||
<>
|
<>
|
||||||
<Button
|
<Button size="sm" onClick={handleRenameSubmit}>
|
||||||
size="sm"
|
|
||||||
onClick={handleRenameSubmit}
|
|
||||||
>
|
|
||||||
{t.common.save}
|
{t.common.save}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import { api, fetchJSON } from "@/lib/api";
|
||||||
import { cn, timeAgo, isoTimeAgo } from "@/lib/utils";
|
import { cn, timeAgo, isoTimeAgo } from "@/lib/utils";
|
||||||
import { Badge } from "@nous-research/ui/ui/components/badge";
|
import { Badge } from "@nous-research/ui/ui/components/badge";
|
||||||
import { Button } from "@nous-research/ui/ui/components/button";
|
import { Button } from "@nous-research/ui/ui/components/button";
|
||||||
|
import { Checkbox } from "@nous-research/ui/ui/components/checkbox";
|
||||||
import { Select, SelectOption } from "@nous-research/ui/ui/components/select";
|
import { Select, SelectOption } from "@nous-research/ui/ui/components/select";
|
||||||
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
|
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
|
|
@ -121,7 +122,7 @@ export function exposePluginSDK() {
|
||||||
// Raw fetchJSON for plugin-specific endpoints
|
// Raw fetchJSON for plugin-specific endpoints
|
||||||
fetchJSON,
|
fetchJSON,
|
||||||
|
|
||||||
// UI components (shadcn/ui primitives)
|
// UI components — Nous DS where available, shadcn/ui primitives elsewhere.
|
||||||
components: {
|
components: {
|
||||||
Card,
|
Card,
|
||||||
CardHeader,
|
CardHeader,
|
||||||
|
|
@ -129,6 +130,7 @@ export function exposePluginSDK() {
|
||||||
CardContent,
|
CardContent,
|
||||||
Badge,
|
Badge,
|
||||||
Button,
|
Button,
|
||||||
|
Checkbox,
|
||||||
Input,
|
Input,
|
||||||
Label,
|
Label,
|
||||||
Select,
|
Select,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue