Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion frontend/app/shadcn/chart.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
// Copyright 2025, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
//
// This file is based on components from shadcn/ui, which is licensed under the MIT License.
// Original source: https://github.com/shadcn/ui
// Modifications made by Command Line Inc.

"use client";

import * as React from "react";
import * as RechartsPrimitive from "recharts";

import cn from "clsx";
import { cn } from "@/shadcn/lib/utils";

// Format: { THEME_NAME: CSS_SELECTOR }
const THEMES = { light: "", dark: ".dark" } as const;
Expand Down
151 changes: 151 additions & 0 deletions frontend/app/shadcn/form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
// Copyright 2025, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
//
// This file is based on components from shadcn/ui, which is licensed under the MIT License.
// Original source: https://github.com/shadcn/ui
// Modifications made by Command Line Inc.

"use client";

import * as LabelPrimitive from "@radix-ui/react-label";
import { Slot } from "@radix-ui/react-slot";
import { Controller, ControllerProps, FieldPath, FieldValues, FormProvider, useFormContext } from "react-hook-form";

import { Label } from "@/shadcn/label";
import * as React from "react";

import { cn } from "@/shadcn/lib/utils";

const Form = FormProvider;

type FormFieldContextValue<
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
> = {
name: TName;
};

const FormFieldContext = React.createContext<FormFieldContextValue>({} as FormFieldContextValue);

const FormField = <
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
>({
...props
}: ControllerProps<TFieldValues, TName>) => {
return (
<FormFieldContext.Provider value={{ name: props.name }}>
<Controller {...props} />
</FormFieldContext.Provider>
);
};

const useFormField = () => {
const fieldContext = React.useContext(FormFieldContext);
const itemContext = React.useContext(FormItemContext);
const { getFieldState, formState } = useFormContext();

const fieldState = getFieldState(fieldContext.name, formState);

if (!fieldContext) {
throw new Error("useFormField should be used within <FormField>");
}

const { id } = itemContext;

return {
id,
name: fieldContext.name,
formItemId: `${id}-form-item`,
formDescriptionId: `${id}-form-item-description`,
formMessageId: `${id}-form-item-message`,
...fieldState,
};
};

type FormItemContextValue = {
id: string;
};

const FormItemContext = React.createContext<FormItemContextValue>({} as FormItemContextValue);

const FormItem = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => {
const id = React.useId();

return (
<FormItemContext.Provider value={{ id }}>
<div ref={ref} className={cn("space-y-2", className)} {...props} />
</FormItemContext.Provider>
);
}
);
FormItem.displayName = "FormItem";

const FormLabel = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
>(({ className, ...props }, ref) => {
const { error, formItemId } = useFormField();

return <Label ref={ref} className={cn(error && "text-destructive", className)} htmlFor={formItemId} {...props} />;
});
FormLabel.displayName = "FormLabel";

const FormControl = React.forwardRef<React.ElementRef<typeof Slot>, React.ComponentPropsWithoutRef<typeof Slot>>(
({ ...props }, ref) => {
const { error, formItemId, formDescriptionId, formMessageId } = useFormField();

return (
<Slot
ref={ref}
id={formItemId}
aria-describedby={!error ? `${formDescriptionId}` : `${formDescriptionId} ${formMessageId}`}
aria-invalid={!!error}
{...props}
/>
);
}
);
FormControl.displayName = "FormControl";

const FormDescription = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(
({ className, ...props }, ref) => {
const { formDescriptionId } = useFormField();

return (
<p
ref={ref}
id={formDescriptionId}
className={cn("text-[0.8rem] text-muted-foreground", className)}
{...props}
/>
);
}
);
FormDescription.displayName = "FormDescription";

const FormMessage = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(
({ className, children, ...props }, ref) => {
const { error, formMessageId } = useFormField();
const body = error ? String(error?.message) : children;

if (!body) {
return null;
}

return (
<p
ref={ref}
id={formMessageId}
className={cn("text-[0.8rem] font-medium text-destructive", className)}
{...props}
>
{body}
</p>
);
}
);
FormMessage.displayName = "FormMessage";

export { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, useFormField };
26 changes: 26 additions & 0 deletions frontend/app/shadcn/label.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright 2025, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
//
// This file is based on components from shadcn/ui, which is licensed under the MIT License.
// Original source: https://github.com/shadcn/ui
// Modifications made by Command Line Inc.

"use client";

import * as LabelPrimitive from "@radix-ui/react-label";
import { cva, type VariantProps } from "class-variance-authority";
import * as React from "react";

import cn from "clsx";

const labelVariants = cva("text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70");

const Label = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> & VariantProps<typeof labelVariants>
>(({ className, ...props }, ref) => (
<LabelPrimitive.Root ref={ref} className={cn(labelVariants(), className)} {...props} />
));
Label.displayName = LabelPrimitive.Root.displayName;

export { Label };
26 changes: 26 additions & 0 deletions frontend/app/shadcn/lib/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright 2025, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
//
// This file is based on components from shadcn/ui, which is licensed under the MIT License.
// Original source: https://github.com/shadcn/ui
// Modifications made by Command Line Inc.

import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";

export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}

export function formatDate(input: string | number): string {
const date = new Date(input);
return date.toLocaleDateString("en-US", {
month: "long",
day: "numeric",
year: "numeric",
});
}

export function absoluteUrl(path: string) {
return `${process.env.NEXT_PUBLIC_APP_URL}${path}`;
}
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@
"@monaco-editor/loader": "^1.4.0",
"@monaco-editor/react": "^4.6.0",
"@observablehq/plot": "^0.6.16",
"@radix-ui/react-label": "^2.1.2",
"@radix-ui/react-slot": "^1.1.2",
"@react-hook/resize-observer": "^2.0.2",
"@table-nav/core": "^0.0.7",
"@table-nav/react": "^0.0.7",
Expand All @@ -106,6 +108,7 @@
"@xterm/addon-webgl": "^0.18.0",
"@xterm/xterm": "^5.5.0",
"base64-js": "^1.5.1",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"color": "^4.2.3",
"colord": "^2.9.3",
Expand Down Expand Up @@ -133,6 +136,7 @@
"react-dom": "^18.3.1",
"react-frame-component": "^5.2.7",
"react-gauge-chart": "^0.5.1",
"react-hook-form": "^7.54.2",
"react-markdown": "^9.0.3",
"react-zoom-pan-pinch": "^3.7.0",
"recharts": "^2.15.1",
Expand All @@ -147,6 +151,7 @@
"sharp": "^0.33.5",
"shell-quote": "^1.8.2",
"sprintf-js": "^1.1.3",
"tailwind-merge": "^3.0.1",
"throttle-debounce": "^5.0.2",
"tinycolor2": "^1.6.0",
"use-device-pixel-ratio": "^1.1.2",
Expand Down
Loading
Loading