initial commit
Some checks failed
CI / Go — node-agent tests (push) Has been cancelled
CI / Go — edge-gateway build (push) Has been cancelled
CI / Node — lint, typecheck, test, build (push) Has been cancelled

This commit is contained in:
smueller
2026-06-26 10:45:08 +02:00
commit e37ea87b35
118 changed files with 7726 additions and 0 deletions

24
packages/ui/package.json Normal file
View File

@@ -0,0 +1,24 @@
{
"name": "@hexahost/ui",
"version": "0.0.1",
"private": true,
"sideEffects": false,
"main": "./src/index.ts",
"types": "./src/index.ts",
"exports": {
".": "./src/index.ts"
},
"scripts": {
"typecheck": "tsc --noEmit",
"lint": "tsc --noEmit"
},
"peerDependencies": {
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0"
},
"devDependencies": {
"@types/react": "^19.0.10",
"@types/react-dom": "^19.0.4",
"typescript": "^5.7.3"
}
}

View File

@@ -0,0 +1,79 @@
import { forwardRef, type ButtonHTMLAttributes } from "react";
import { cn } from "../lib/cn";
export type ButtonVariant = "primary" | "secondary" | "ghost" | "danger";
export type ButtonSize = "sm" | "md" | "lg";
export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
variant?: ButtonVariant;
size?: ButtonSize;
isLoading?: boolean;
}
const variantStyles: Record<ButtonVariant, string> = {
primary:
"bg-sky-600 text-white hover:bg-sky-500 focus-visible:ring-sky-500 dark:bg-sky-500 dark:hover:bg-sky-400",
secondary:
"border border-zinc-300 bg-white text-zinc-900 hover:bg-zinc-50 focus-visible:ring-zinc-400 dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-100 dark:hover:bg-zinc-800",
ghost:
"text-zinc-700 hover:bg-zinc-100 focus-visible:ring-zinc-400 dark:text-zinc-300 dark:hover:bg-zinc-800",
danger:
"bg-red-600 text-white hover:bg-red-500 focus-visible:ring-red-500 dark:bg-red-500 dark:hover:bg-red-400",
};
const sizeStyles: Record<ButtonSize, string> = {
sm: "h-8 px-3 text-sm",
md: "h-10 px-4 text-sm",
lg: "h-11 px-6 text-base",
};
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
(
{
className,
variant = "primary",
size = "md",
isLoading = false,
disabled,
type = "button",
children,
...props
},
ref,
) => {
const isDisabled = disabled || isLoading;
return (
<button
ref={ref}
type={type}
disabled={isDisabled}
aria-busy={isLoading || undefined}
aria-disabled={isDisabled || undefined}
className={cn(
"inline-flex items-center justify-center gap-2 rounded-md font-medium transition-colors",
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2",
"dark:focus-visible:ring-offset-zinc-950",
"disabled:pointer-events-none disabled:opacity-50",
variantStyles[variant],
sizeStyles[size],
className,
)}
{...props}
>
{isLoading ? (
<>
<span
className="size-4 animate-spin rounded-full border-2 border-current border-t-transparent"
aria-hidden="true"
/>
<span className="sr-only">Loading</span>
</>
) : null}
{children}
</button>
);
},
);
Button.displayName = "Button";

View File

@@ -0,0 +1,97 @@
import { forwardRef, type HTMLAttributes } from "react";
import { cn } from "../lib/cn";
export interface CardProps extends HTMLAttributes<HTMLDivElement> {}
export const Card = forwardRef<HTMLDivElement, CardProps>(
({ className, children, ...props }, ref) => (
<div
ref={ref}
className={cn(
"rounded-lg border border-zinc-200 bg-white shadow-sm",
"dark:border-zinc-800 dark:bg-zinc-900",
className,
)}
{...props}
>
{children}
</div>
),
);
Card.displayName = "Card";
export interface CardHeaderProps extends HTMLAttributes<HTMLDivElement> {}
export const CardHeader = forwardRef<HTMLDivElement, CardHeaderProps>(
({ className, children, ...props }, ref) => (
<div
ref={ref}
className={cn("flex flex-col gap-1.5 border-b border-zinc-200 px-6 py-4 dark:border-zinc-800", className)}
{...props}
>
{children}
</div>
),
);
CardHeader.displayName = "CardHeader";
export interface CardTitleProps extends HTMLAttributes<HTMLHeadingElement> {}
export const CardTitle = forwardRef<HTMLHeadingElement, CardTitleProps>(
({ className, children, ...props }, ref) => (
<h3
ref={ref}
className={cn("text-base font-semibold tracking-tight text-zinc-900 dark:text-zinc-50", className)}
{...props}
>
{children}
</h3>
),
);
CardTitle.displayName = "CardTitle";
export interface CardDescriptionProps extends HTMLAttributes<HTMLParagraphElement> {}
export const CardDescription = forwardRef<HTMLParagraphElement, CardDescriptionProps>(
({ className, children, ...props }, ref) => (
<p ref={ref} className={cn("text-sm text-zinc-600 dark:text-zinc-400", className)} {...props}>
{children}
</p>
),
);
CardDescription.displayName = "CardDescription";
export interface CardContentProps extends HTMLAttributes<HTMLDivElement> {}
export const CardContent = forwardRef<HTMLDivElement, CardContentProps>(
({ className, children, ...props }, ref) => (
<div ref={ref} className={cn("px-6 py-4", className)} {...props}>
{children}
</div>
),
);
CardContent.displayName = "CardContent";
export interface CardFooterProps extends HTMLAttributes<HTMLDivElement> {}
export const CardFooter = forwardRef<HTMLDivElement, CardFooterProps>(
({ className, children, ...props }, ref) => (
<div
ref={ref}
className={cn(
"flex items-center border-t border-zinc-200 px-6 py-4 dark:border-zinc-800",
className,
)}
{...props}
>
{children}
</div>
),
);
CardFooter.displayName = "CardFooter";

16
packages/ui/src/index.ts Normal file
View File

@@ -0,0 +1,16 @@
export { Button, type ButtonProps, type ButtonSize, type ButtonVariant } from "./components/Button";
export {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
type CardContentProps,
type CardDescriptionProps,
type CardFooterProps,
type CardHeaderProps,
type CardProps,
type CardTitleProps,
} from "./components/Card";
export { cn } from "./lib/cn";

View File

@@ -0,0 +1,3 @@
export function cn(...classes: Array<string | false | null | undefined>): string {
return classes.filter(Boolean).join(" ");
}

20
packages/ui/tsconfig.json Normal file
View File

@@ -0,0 +1,20 @@
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"module": "ESNext",
"moduleResolution": "bundler",
"jsx": "react-jsx",
"strict": true,
"noEmit": true,
"skipLibCheck": true,
"isolatedModules": true,
"declaration": true,
"declarationMap": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}