diff --git a/tests/hermes_cli/test_dashboard_browser_safe_imports.py b/tests/hermes_cli/test_dashboard_browser_safe_imports.py new file mode 100644 index 0000000000..4c2293503e --- /dev/null +++ b/tests/hermes_cli/test_dashboard_browser_safe_imports.py @@ -0,0 +1,15 @@ +"""Static dashboard tests for browser-safe @nous-research/ui imports.""" +from pathlib import Path + + +WEB_SRC = Path(__file__).resolve().parents[2] / "web" / "src" + + +def test_dashboard_does_not_import_nous_ui_root_barrel(): + offenders = [] + for path in WEB_SRC.rglob("*.tsx"): + content = path.read_text(encoding="utf-8") + if 'from "@nous-research/ui"' in content or "from '@nous-research/ui'" in content: + offenders.append(str(path.relative_to(WEB_SRC))) + + assert offenders == [] diff --git a/web/package-lock.json b/web/package-lock.json index 2c6377b4f2..7f987c5a1d 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -76,7 +76,6 @@ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -1125,7 +1124,6 @@ "resolved": "https://registry.npmjs.org/@observablehq/plot/-/plot-0.6.17.tgz", "integrity": "sha512-/qaXP/7mc4MUS0s4cPPFASDRjtsWp85/TbfsciqDgU1HwYixbSbbytNuInD8AcTYC3xaxACgVX06agdfQy9W+g==", "license": "ISC", - "peer": true, "dependencies": { "d3": "^7.9.0", "interval-tree-1d": "^1.0.0", @@ -1778,7 +1776,6 @@ "resolved": "https://registry.npmjs.org/@react-three/fiber/-/fiber-9.6.0.tgz", "integrity": "sha512-90abYK2q5/qDM+GACs9zRvc5KhEEpEWqWlHSd64zTPNxg+9wCJvTfyD9x2so7hlQhjRYO1Fa6flR3BC/kpTFkA==", "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.17.8", "@types/webxr": "*", @@ -2484,7 +2481,6 @@ "integrity": "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~7.16.0" } @@ -2494,7 +2490,6 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -2505,7 +2500,6 @@ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "devOptional": true, "license": "MIT", - "peer": true, "peerDependencies": { "@types/react": "^19.2.0" } @@ -2570,7 +2564,6 @@ "integrity": "sha512-HDQH9O/47Dxi1ceDhBXdaldtf/WV9yRYMjbjCuNk3qnaTD564qwv61Y7+gTxwxRKzSrgO5uhtw584igXVuuZkA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.59.1", "@typescript-eslint/types": "8.59.1", @@ -2899,7 +2892,6 @@ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3052,7 +3044,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", @@ -3560,7 +3551,6 @@ "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", "license": "ISC", - "peer": true, "engines": { "node": ">=12" } @@ -3874,7 +3864,6 @@ "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -4253,8 +4242,7 @@ "version": "3.15.0", "resolved": "https://registry.npmjs.org/gsap/-/gsap-3.15.0.tgz", "integrity": "sha512-dMW4CWBTUK1AEEDeZc1g4xpPGIrSf9fJF960qbTZmN/QwZIWY5wgliS6JWl9/25fpTGJrMRtSjGtOmPnfjZB+A==", - "license": "Standard 'no charge' license: https://gsap.com/standard-license.", - "peer": true + "license": "Standard 'no charge' license: https://gsap.com/standard-license." }, "node_modules/has-flag": { "version": "4.0.0", @@ -4560,7 +4548,6 @@ "resolved": "https://registry.npmjs.org/leva/-/leva-0.10.1.tgz", "integrity": "sha512-BcjnfUX8jpmwZUz2L7AfBtF9vn4ggTH33hmeufDULbP3YgNZ/C+ss/oO3stbrqRQyaOmRwy70y7BGTGO81S3rA==", "license": "MIT", - "peer": true, "dependencies": { "@radix-ui/react-portal": "^1.1.4", "@radix-ui/react-tooltip": "^1.1.8", @@ -4999,7 +4986,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": "^20.0.0 || >=22.0.0" } @@ -5127,7 +5113,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -5199,7 +5184,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.5.tgz", "integrity": "sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -5219,7 +5203,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.5.tgz", "integrity": "sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -5579,8 +5562,7 @@ "version": "0.180.0", "resolved": "https://registry.npmjs.org/three/-/three-0.180.0.tgz", "integrity": "sha512-o+qycAMZrh+TsE01GqWUxUIKR1AL0S8pq7zDkYOQw8GqfX8b8VoCKYUoHbhiX5j+7hr8XsuHDVU6+gkQJQKg9w==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/tinyglobby": { "version": "0.2.16", @@ -5645,7 +5627,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -5744,7 +5725,6 @@ "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", "license": "MIT", - "peer": true, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } @@ -5760,7 +5740,6 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.2.tgz", "integrity": "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==", "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -5882,7 +5861,6 @@ "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", "dev": true, "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/web/src/App.tsx b/web/src/App.tsx index 835d3e268c..9c09e1151b 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -42,13 +42,11 @@ import { X, Zap, } from "lucide-react"; -import { - Button, - ListItem, - SelectionSwitcher, - Spinner, - Typography, -} from "@nous-research/ui"; +import { Button } from "@nous-research/ui/ui/components/button"; +import { ListItem } from "@nous-research/ui/ui/components/list-item"; +import { SelectionSwitcher } from "@nous-research/ui/ui/components/selection-switcher"; +import { Spinner } from "@nous-research/ui/ui/components/spinner"; +import { Typography } from "@/components/NouiTypography"; import { cn } from "@/lib/utils"; import { Backdrop } from "@/components/Backdrop"; import { SidebarFooter } from "@/components/SidebarFooter"; diff --git a/web/src/components/LanguageSwitcher.tsx b/web/src/components/LanguageSwitcher.tsx index 24684bce2c..dc477021ee 100644 --- a/web/src/components/LanguageSwitcher.tsx +++ b/web/src/components/LanguageSwitcher.tsx @@ -1,4 +1,5 @@ -import { Button, Typography } from "@nous-research/ui"; +import { Button } from "@nous-research/ui/ui/components/button"; +import { Typography } from "@/components/NouiTypography"; import { useI18n } from "@/i18n/context"; /** diff --git a/web/src/components/NouiTypography.tsx b/web/src/components/NouiTypography.tsx new file mode 100644 index 0000000000..4f5547adb5 --- /dev/null +++ b/web/src/components/NouiTypography.tsx @@ -0,0 +1,63 @@ +import { forwardRef, type ElementType, type HTMLAttributes, type ReactNode } from "react"; +import { cn } from "@/lib/utils"; + +type TypographyProps = HTMLAttributes & { + as?: ElementType; + children?: ReactNode; + compressed?: boolean; + courier?: boolean; + expanded?: boolean; + mondwest?: boolean; + mono?: boolean; + sans?: boolean; + variant?: "sm" | "md" | "lg" | "xl"; +}; + +const variantClasses: Record, string> = { + sm: "leading-1.4 text-[.9375rem] tracking-[0.1875rem]", + md: "text-[2.625rem] leading-[1] tracking-[0.0525rem]", + lg: "text-[2.625rem] leading-[1] tracking-[0.0525rem]", + xl: "text-[4.5rem] leading-[1] tracking-[0.135rem]", +}; + +export const Typography = forwardRef(function Typography( + { + as: Component = "span", + className, + compressed, + courier, + expanded, + mondwest, + mono, + sans, + variant, + ...props + }, + ref, +) { + const hasFontVariant = compressed || courier || expanded || mondwest || mono || sans; + + return ( + + ); +}); + +export const H2 = forwardRef>(function H2( + { className, variant = "lg", ...props }, + ref, +) { + return ; +}); diff --git a/web/src/components/OAuthLoginModal.tsx b/web/src/components/OAuthLoginModal.tsx index 8ac630ea72..f4eb610c16 100644 --- a/web/src/components/OAuthLoginModal.tsx +++ b/web/src/components/OAuthLoginModal.tsx @@ -1,6 +1,9 @@ import { useEffect, useRef, useState } from "react"; import { ExternalLink, X, Check } from "lucide-react"; -import { Button, CopyButton, H2, Spinner } from "@nous-research/ui"; +import { Button } from "@nous-research/ui/ui/components/button"; +import { CopyButton } from "@nous-research/ui/ui/components/command-block"; +import { Spinner } from "@nous-research/ui/ui/components/spinner"; +import { H2 } from "@/components/NouiTypography"; import { api, type OAuthProvider, type OAuthStartResponse } from "@/lib/api"; import { Input } from "@/components/ui/input"; import { useI18n } from "@/i18n"; diff --git a/web/src/components/SidebarFooter.tsx b/web/src/components/SidebarFooter.tsx index e163dd2c98..c1810f10e0 100644 --- a/web/src/components/SidebarFooter.tsx +++ b/web/src/components/SidebarFooter.tsx @@ -1,4 +1,4 @@ -import { Typography } from "@nous-research/ui"; +import { Typography } from "@/components/NouiTypography"; import { useSidebarStatus } from "@/hooks/useSidebarStatus"; import { cn } from "@/lib/utils"; import { useI18n } from "@/i18n"; diff --git a/web/src/components/ThemeSwitcher.tsx b/web/src/components/ThemeSwitcher.tsx index 2d1cf3531e..4d50e611ef 100644 --- a/web/src/components/ThemeSwitcher.tsx +++ b/web/src/components/ThemeSwitcher.tsx @@ -1,6 +1,8 @@ import { useCallback, useEffect, useRef, useState } from "react"; import { Palette, Check } from "lucide-react"; -import { Button, ListItem, Typography } from "@nous-research/ui"; +import { Button } from "@nous-research/ui/ui/components/button"; +import { ListItem } from "@nous-research/ui/ui/components/list-item"; +import { Typography } from "@/components/NouiTypography"; import { BUILTIN_THEMES, useTheme } from "@/themes"; import { useI18n } from "@/i18n"; import { cn } from "@/lib/utils"; diff --git a/web/src/pages/ChatPage.tsx b/web/src/pages/ChatPage.tsx index a38116fafa..085d1cfc12 100644 --- a/web/src/pages/ChatPage.tsx +++ b/web/src/pages/ChatPage.tsx @@ -22,7 +22,8 @@ import { WebLinksAddon } from "@xterm/addon-web-links"; import { WebglAddon } from "@xterm/addon-webgl"; import { Terminal } from "@xterm/xterm"; import "@xterm/xterm/css/xterm.css"; -import { Button, Typography } from "@nous-research/ui"; +import { Button } from "@nous-research/ui/ui/components/button"; +import { Typography } from "@/components/NouiTypography"; import { cn } from "@/lib/utils"; import { Copy, PanelRight, X } from "lucide-react"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; diff --git a/web/src/pages/CronPage.tsx b/web/src/pages/CronPage.tsx index 13c2f54a3a..90cc25abe0 100644 --- a/web/src/pages/CronPage.tsx +++ b/web/src/pages/CronPage.tsx @@ -1,6 +1,10 @@ import { useCallback, useEffect, useState } from "react"; import { Clock, Pause, Play, Plus, Trash2, Zap } from "lucide-react"; -import { Badge, Button, H2, Select, SelectOption, Spinner } from "@nous-research/ui"; +import { Badge } from "@nous-research/ui/ui/components/badge"; +import { Button } from "@nous-research/ui/ui/components/button"; +import { Select, SelectOption } from "@nous-research/ui/ui/components/select"; +import { Spinner } from "@nous-research/ui/ui/components/spinner"; +import { H2 } from "@/components/NouiTypography"; import { api } from "@/lib/api"; import type { CronJob } from "@/lib/api"; import { DeleteConfirmDialog } from "@/components/DeleteConfirmDialog"; diff --git a/web/src/pages/ProfilesPage.tsx b/web/src/pages/ProfilesPage.tsx index e55f99977f..769c74d217 100644 --- a/web/src/pages/ProfilesPage.tsx +++ b/web/src/pages/ProfilesPage.tsx @@ -1,6 +1,6 @@ import { useCallback, useEffect, useState } from "react"; import { ChevronDown, Pencil, Plus, Terminal, Trash2, Users } from "lucide-react"; -import { H2 } from "@nous-research/ui"; +import { H2 } from "@/components/NouiTypography"; import { api } from "@/lib/api"; import type { ProfileInfo } from "@/lib/api"; import { DeleteConfirmDialog } from "@/components/DeleteConfirmDialog";