mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-29 06:31:32 +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;
|
||||
npmDeps = pkgs.fetchNpmDeps {
|
||||
inherit src;
|
||||
hash = "sha256-FL2E8Vv8gyeClEa5b/pHn/ekWoHWTd4YwzV6zhLEos4=";
|
||||
hash = "sha256-GxSmEpclOwmv94KmGMediPITxqXAsxqTEQOoDIbYkUw=";
|
||||
};
|
||||
|
||||
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 { 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
|
||||
// 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
|
||||
|
|
@ -1648,11 +1665,10 @@
|
|||
h(Label, { className: "text-xs text-muted-foreground" },
|
||||
"Orchestration mode"),
|
||||
h("label", { className: "flex items-center gap-2 text-xs h-8" },
|
||||
h("input", {
|
||||
type: "checkbox",
|
||||
h(Checkbox, {
|
||||
checked: !!settings.auto_decompose,
|
||||
onChange: function (e) {
|
||||
saveSettings({ auto_decompose: !!e.target.checked });
|
||||
onCheckedChange: function (checked) {
|
||||
saveSettings({ auto_decompose: checked === true });
|
||||
},
|
||||
}),
|
||||
"Auto-decompose triage tasks",
|
||||
|
|
@ -1908,10 +1924,9 @@
|
|||
}),
|
||||
),
|
||||
h("label", { className: "flex items-center gap-2 text-xs" },
|
||||
h("input", {
|
||||
type: "checkbox",
|
||||
h(Checkbox, {
|
||||
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"),
|
||||
),
|
||||
|
|
@ -1981,19 +1996,17 @@
|
|||
),
|
||||
h("label", { className: "flex items-center gap-2 text-xs",
|
||||
title: "Include archived tasks in the board view. Archived tasks are hidden by default." },
|
||||
h("input", {
|
||||
type: "checkbox",
|
||||
h(Checkbox, {
|
||||
checked: props.includeArchived,
|
||||
onChange: function (e) { props.setIncludeArchived(e.target.checked); },
|
||||
onCheckedChange: function (checked) { props.setIncludeArchived(checked === true); },
|
||||
}),
|
||||
tx(t, "showArchived", "Show archived"),
|
||||
),
|
||||
h("label", { className: "flex items-center gap-2 text-xs",
|
||||
title: "Group the Running column by assigned profile" },
|
||||
h("input", {
|
||||
type: "checkbox",
|
||||
h(Checkbox, {
|
||||
checked: props.laneByProfile,
|
||||
onChange: function (e) { props.setLaneByProfile(e.target.checked); },
|
||||
onCheckedChange: function (checked) { props.setLaneByProfile(checked === true); },
|
||||
}),
|
||||
tx(t, "lanesByProfile", "Lanes by profile"),
|
||||
),
|
||||
|
|
@ -2122,10 +2135,9 @@
|
|||
}, tx(t, "apply", "Apply")),
|
||||
),
|
||||
h("label", { className: "hermes-kanban-bulk-reclaim-first", title: "Reclaim any active claims before reassigning" },
|
||||
h("input", {
|
||||
type: "checkbox",
|
||||
h(Checkbox, {
|
||||
checked: reclaimFirst,
|
||||
onChange: function (e) { setReclaimFirst(e.target.checked); },
|
||||
onCheckedChange: function (checked) { setReclaimFirst(checked === true); },
|
||||
}),
|
||||
"Reclaim first",
|
||||
),
|
||||
|
|
@ -2313,14 +2325,12 @@
|
|||
},
|
||||
h("div", { className: "hermes-kanban-column-header",
|
||||
title: colHelp || "" },
|
||||
h("input", {
|
||||
type: "checkbox",
|
||||
h(Checkbox, {
|
||||
className: "hermes-kanban-col-check",
|
||||
title: "Select all tasks in this column",
|
||||
"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); }),
|
||||
onChange: function (e) {
|
||||
e.stopPropagation();
|
||||
onCheckedChange: function () {
|
||||
if (props.selectAllInColumn) props.selectAllInColumn(props.column.name);
|
||||
},
|
||||
onClick: function (e) { e.stopPropagation(); },
|
||||
|
|
@ -2461,8 +2471,7 @@
|
|||
if (props.toggleSelected) props.toggleSelected(t.id, false);
|
||||
}
|
||||
};
|
||||
const handleCheckbox = function (e) {
|
||||
e.stopPropagation();
|
||||
const handleCheckedChange = function () {
|
||||
props.toggleSelected(t.id, true);
|
||||
};
|
||||
|
||||
|
|
@ -2495,11 +2504,10 @@
|
|||
title: tx(i18n, "selectForBulk", "Select for bulk actions"),
|
||||
onClick: function (e) { e.stopPropagation(); },
|
||||
},
|
||||
h("input", {
|
||||
type: "checkbox",
|
||||
h(Checkbox, {
|
||||
className: "hermes-kanban-card-check",
|
||||
checked: props.selected,
|
||||
onChange: handleCheckbox,
|
||||
onCheckedChange: handleCheckedChange,
|
||||
onClick: function (e) { e.stopPropagation(); },
|
||||
"aria-label": `Select task ${t.id}`,
|
||||
}),
|
||||
|
|
|
|||
263
web/package-lock.json
generated
263
web/package-lock.json
generated
|
|
@ -8,7 +8,7 @@
|
|||
"name": "web",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@nous-research/ui": "0.14.0",
|
||||
"@nous-research/ui": "^0.14.2",
|
||||
"@observablehq/plot": "^0.6.17",
|
||||
"@react-three/fiber": "^9.6.0",
|
||||
"@tailwindcss/vite": "^4.2.1",
|
||||
|
|
@ -1081,12 +1081,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@nous-research/ui": {
|
||||
"version": "0.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@nous-research/ui/-/ui-0.14.0.tgz",
|
||||
"integrity": "sha512-tfpE6jGOxE5oVBab/dTSepOudy/+Xep3gJ6NCFriYJvdtQBGXcqsi4mCaVPiNNaS/ZFf4/10dnl/oJTb6DtLKg==",
|
||||
"version": "0.14.2",
|
||||
"resolved": "https://registry.npmjs.org/@nous-research/ui/-/ui-0.14.2.tgz",
|
||||
"integrity": "sha512-H3cMt2e0IpmcTNOmR6zVX+8ja48w4X4F/IFXhWCpaoVs8zKVRN12Ryb4RnX/ac8IrbUu6UsIds7ZtmXxPHcfdQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@nanostores/react": "^1.1.0",
|
||||
"@radix-ui/react-checkbox": "^1.3.3",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"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": {
|
||||
"version": "1.1.2",
|
||||
"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": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz",
|
||||
|
|
@ -3648,6 +3735,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": {
|
||||
"version": "4.4.3",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
||||
|
|
@ -4193,6 +4286,33 @@
|
|||
"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": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
|
|
@ -4564,6 +4684,15 @@
|
|||
"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": {
|
||||
"version": "0.10.1",
|
||||
"resolved": "https://registry.npmjs.org/leva/-/leva-0.10.1.tgz",
|
||||
|
|
@ -4972,6 +5101,48 @@
|
|||
"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": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
|
|
@ -5938,90 +6109,6 @@
|
|||
"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"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nous-research/ui": "0.14.0",
|
||||
"@nous-research/ui": "^0.14.2",
|
||||
"@observablehq/plot": "^0.6.17",
|
||||
"@react-three/fiber": "^9.6.0",
|
||||
"@tailwindcss/vite": "^4.2.1",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
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 { Spinner } from "@nous-research/ui/ui/components/spinner";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import type { GatewayClient } from "@/lib/gatewayClient";
|
||||
import { Check, Search, X } from "lucide-react";
|
||||
|
|
@ -283,15 +285,22 @@ export function ModelPickerDialog(props: Props) {
|
|||
Saves to config.yaml — applies to new sessions.
|
||||
</span>
|
||||
) : (
|
||||
<label className="flex items-center gap-2 text-xs text-muted-foreground cursor-pointer select-none">
|
||||
<input
|
||||
type="checkbox"
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox
|
||||
checked={persistGlobal}
|
||||
onChange={(e) => setPersistGlobal(e.target.checked)}
|
||||
className="cursor-pointer"
|
||||
id="model-picker-persist-global"
|
||||
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">
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
/* `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';
|
||||
|
||||
/* Scan the published design-system bundle so its utility classes survive
|
||||
|
|
|
|||
|
|
@ -439,7 +439,7 @@ export default function AnalyticsPage() {
|
|||
);
|
||||
setEnd(
|
||||
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">
|
||||
{PERIODS.map((p) => (
|
||||
<Button
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ export default function LogsPage() {
|
|||
</span>,
|
||||
);
|
||||
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">
|
||||
<Switch
|
||||
checked={autoRefresh}
|
||||
|
|
|
|||
|
|
@ -827,7 +827,7 @@ export default function ModelsPage() {
|
|||
</span>,
|
||||
);
|
||||
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">
|
||||
{PERIODS.map((p) => (
|
||||
<Button
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ export default function PluginsPage() {
|
|||
|
||||
useEffect(() => {
|
||||
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
|
||||
ghost
|
||||
size="sm"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,19 @@
|
|||
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
|
||||
import { ChevronDown, Pencil, Plus, Terminal, Trash2, Users, X } from "lucide-react";
|
||||
import {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useLayoutEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import {
|
||||
ChevronDown,
|
||||
Pencil,
|
||||
Plus,
|
||||
Terminal,
|
||||
Trash2,
|
||||
Users,
|
||||
X,
|
||||
} from "lucide-react";
|
||||
import spinners from "unicode-animations";
|
||||
import { H2 } from "@/components/NouiTypography";
|
||||
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 { Input } from "@/components/ui/input";
|
||||
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 { usePageHeader } from "@/contexts/usePageHeader";
|
||||
|
||||
|
|
@ -131,7 +145,10 @@ export default function ProfilesPage() {
|
|||
}
|
||||
try {
|
||||
await api.renameProfile(renamingFrom, target);
|
||||
showToast(`${t.profiles.renamed}: ${renamingFrom} → ${target}`, "success");
|
||||
showToast(
|
||||
`${t.profiles.renamed}: ${renamingFrom} → ${target}`,
|
||||
"success",
|
||||
);
|
||||
setRenamingFrom(null);
|
||||
setRenameTo("");
|
||||
load();
|
||||
|
|
@ -214,10 +231,7 @@ export default function ProfilesPage() {
|
|||
// Put "Create" button in page header
|
||||
useLayoutEffect(() => {
|
||||
setEnd(
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={() => setCreateModalOpen(true)}
|
||||
>
|
||||
<Button size="sm" onClick={() => setCreateModalOpen(true)}>
|
||||
<Plus className="h-3 w-3" />
|
||||
{t.common.create}
|
||||
</Button>,
|
||||
|
|
@ -266,7 +280,9 @@ export default function ProfilesPage() {
|
|||
<div
|
||||
ref={createModalRef}
|
||||
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"
|
||||
aria-modal="true"
|
||||
aria-labelledby="create-profile-title"
|
||||
|
|
@ -313,12 +329,22 @@ export default function ProfilesPage() {
|
|||
</p>
|
||||
</div>
|
||||
|
||||
<Checkbox
|
||||
id="clone-from-default"
|
||||
checked={cloneFromDefault}
|
||||
onChange={(e) => setCloneFromDefault(e.target.checked)}
|
||||
label={t.profiles.cloneFromDefault}
|
||||
/>
|
||||
<div className="flex items-center gap-2.5">
|
||||
<Checkbox
|
||||
checked={cloneFromDefault}
|
||||
id="clone-from-default"
|
||||
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">
|
||||
<Button size="sm" onClick={handleCreate} disabled={creating}>
|
||||
|
|
@ -426,10 +452,7 @@ export default function ProfilesPage() {
|
|||
<div className="flex items-center gap-1 shrink-0">
|
||||
{isRenaming ? (
|
||||
<>
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={handleRenameSubmit}
|
||||
>
|
||||
<Button size="sm" onClick={handleRenameSubmit}>
|
||||
{t.common.save}
|
||||
</Button>
|
||||
<Button
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import { api, fetchJSON } from "@/lib/api";
|
|||
import { cn, timeAgo, isoTimeAgo } from "@/lib/utils";
|
||||
import { Badge } from "@nous-research/ui/ui/components/badge";
|
||||
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 { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
|
||||
import { Input } from "@/components/ui/input";
|
||||
|
|
@ -121,7 +122,7 @@ export function exposePluginSDK() {
|
|||
// Raw fetchJSON for plugin-specific endpoints
|
||||
fetchJSON,
|
||||
|
||||
// UI components (shadcn/ui primitives)
|
||||
// UI components — Nous DS where available, shadcn/ui primitives elsewhere.
|
||||
components: {
|
||||
Card,
|
||||
CardHeader,
|
||||
|
|
@ -129,6 +130,7 @@ export function exposePluginSDK() {
|
|||
CardContent,
|
||||
Badge,
|
||||
Button,
|
||||
Checkbox,
|
||||
Input,
|
||||
Label,
|
||||
Select,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue