diff options
Diffstat (limited to 'llama.cpp/tools/server/webui/src/lib/components/ui')
134 files changed, 3409 insertions, 0 deletions
diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/alert-dialog/alert-dialog-action.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/alert-dialog/alert-dialog-action.svelte new file mode 100644 index 0000000..162107e --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/alert-dialog/alert-dialog-action.svelte @@ -0,0 +1,18 @@ +<script lang="ts"> + import { AlertDialog as AlertDialogPrimitive } from 'bits-ui'; + import { buttonVariants } from '$lib/components/ui/button/index.js'; + import { cn } from '$lib/components/ui/utils.js'; + + let { + ref = $bindable(null), + class: className, + ...restProps + }: AlertDialogPrimitive.ActionProps = $props(); +</script> + +<AlertDialogPrimitive.Action + bind:ref + data-slot="alert-dialog-action" + class={cn(buttonVariants(), className)} + {...restProps} +/> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/alert-dialog/alert-dialog-cancel.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/alert-dialog/alert-dialog-cancel.svelte new file mode 100644 index 0000000..6b3f354 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/alert-dialog/alert-dialog-cancel.svelte @@ -0,0 +1,18 @@ +<script lang="ts"> + import { AlertDialog as AlertDialogPrimitive } from 'bits-ui'; + import { buttonVariants } from '$lib/components/ui/button/index.js'; + import { cn } from '$lib/components/ui/utils.js'; + + let { + ref = $bindable(null), + class: className, + ...restProps + }: AlertDialogPrimitive.CancelProps = $props(); +</script> + +<AlertDialogPrimitive.Cancel + bind:ref + data-slot="alert-dialog-cancel" + class={cn(buttonVariants({ variant: 'outline' }), className)} + {...restProps} +/> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/alert-dialog/alert-dialog-content.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/alert-dialog/alert-dialog-content.svelte new file mode 100644 index 0000000..2398dae --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/alert-dialog/alert-dialog-content.svelte @@ -0,0 +1,35 @@ +<script lang="ts"> + import { AlertDialog as AlertDialogPrimitive } from 'bits-ui'; + import AlertDialogOverlay from './alert-dialog-overlay.svelte'; + import { cn, type WithoutChild, type WithoutChildrenOrChild } from '$lib/components/ui/utils.js'; + + let { + ref = $bindable(null), + class: className, + portalProps, + ...restProps + }: WithoutChild<AlertDialogPrimitive.ContentProps> & { + portalProps?: WithoutChildrenOrChild<AlertDialogPrimitive.PortalProps>; + } = $props(); +</script> + +<AlertDialogPrimitive.Portal {...portalProps}> + <AlertDialogOverlay /> + <AlertDialogPrimitive.Content + bind:ref + data-slot="alert-dialog-content" + class={cn( + 'fixed z-[999999] grid w-full gap-4 border bg-background p-6 shadow-lg duration-200', + // Mobile: Bottom sheet behavior + 'right-0 bottom-0 left-0 max-h-[100dvh] translate-x-0 translate-y-0 overflow-y-auto rounded-t-lg', + 'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-bottom-full', + 'data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:slide-in-from-bottom-full', + // Desktop: Centered dialog behavior + 'sm:top-[50%] sm:right-auto sm:bottom-auto sm:left-[50%] sm:max-h-[100vh] sm:max-w-lg sm:translate-x-[-50%] sm:translate-y-[-50%] sm:rounded-lg', + 'sm:data-[state=closed]:slide-out-to-bottom-0 sm:data-[state=closed]:zoom-out-95', + 'sm:data-[state=open]:slide-in-from-bottom-0 sm:data-[state=open]:zoom-in-95', + className + )} + {...restProps} + /> +</AlertDialogPrimitive.Portal> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/alert-dialog/alert-dialog-description.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/alert-dialog/alert-dialog-description.svelte new file mode 100644 index 0000000..84735d8 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/alert-dialog/alert-dialog-description.svelte @@ -0,0 +1,17 @@ +<script lang="ts"> + import { AlertDialog as AlertDialogPrimitive } from 'bits-ui'; + import { cn } from '$lib/components/ui/utils.js'; + + let { + ref = $bindable(null), + class: className, + ...restProps + }: AlertDialogPrimitive.DescriptionProps = $props(); +</script> + +<AlertDialogPrimitive.Description + bind:ref + data-slot="alert-dialog-description" + class={cn('text-sm text-muted-foreground', className)} + {...restProps} +/> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/alert-dialog/alert-dialog-footer.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/alert-dialog/alert-dialog-footer.svelte new file mode 100644 index 0000000..da0f7be --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/alert-dialog/alert-dialog-footer.svelte @@ -0,0 +1,23 @@ +<script lang="ts"> + import { cn, type WithElementRef } from '$lib/components/ui/utils.js'; + import type { HTMLAttributes } from 'svelte/elements'; + + let { + ref = $bindable(null), + class: className, + children, + ...restProps + }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props(); +</script> + +<div + bind:this={ref} + data-slot="alert-dialog-footer" + class={cn( + 'mt-6 flex flex-row gap-2 sm:mt-0 sm:justify-end [&>*]:flex-1 sm:[&>*]:flex-none', + className + )} + {...restProps} +> + {@render children?.()} +</div> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/alert-dialog/alert-dialog-header.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/alert-dialog/alert-dialog-header.svelte new file mode 100644 index 0000000..fa6539d --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/alert-dialog/alert-dialog-header.svelte @@ -0,0 +1,20 @@ +<script lang="ts"> + import type { HTMLAttributes } from 'svelte/elements'; + import { cn, type WithElementRef } from '$lib/components/ui/utils.js'; + + let { + ref = $bindable(null), + class: className, + children, + ...restProps + }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props(); +</script> + +<div + bind:this={ref} + data-slot="alert-dialog-header" + class={cn('flex flex-col gap-2 text-center sm:text-left', className)} + {...restProps} +> + {@render children?.()} +</div> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/alert-dialog/alert-dialog-overlay.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/alert-dialog/alert-dialog-overlay.svelte new file mode 100644 index 0000000..71f166d --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/alert-dialog/alert-dialog-overlay.svelte @@ -0,0 +1,20 @@ +<script lang="ts"> + import { AlertDialog as AlertDialogPrimitive } from 'bits-ui'; + import { cn } from '$lib/components/ui/utils.js'; + + let { + ref = $bindable(null), + class: className, + ...restProps + }: AlertDialogPrimitive.OverlayProps = $props(); +</script> + +<AlertDialogPrimitive.Overlay + bind:ref + data-slot="alert-dialog-overlay" + class={cn( + 'fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0', + className + )} + {...restProps} +/> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/alert-dialog/alert-dialog-title.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/alert-dialog/alert-dialog-title.svelte new file mode 100644 index 0000000..4c610aa --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/alert-dialog/alert-dialog-title.svelte @@ -0,0 +1,17 @@ +<script lang="ts"> + import { AlertDialog as AlertDialogPrimitive } from 'bits-ui'; + import { cn } from '$lib/components/ui/utils.js'; + + let { + ref = $bindable(null), + class: className, + ...restProps + }: AlertDialogPrimitive.TitleProps = $props(); +</script> + +<AlertDialogPrimitive.Title + bind:ref + data-slot="alert-dialog-title" + class={cn('text-lg font-semibold', className)} + {...restProps} +/> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/alert-dialog/alert-dialog-trigger.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/alert-dialog/alert-dialog-trigger.svelte new file mode 100644 index 0000000..51a3da1 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/alert-dialog/alert-dialog-trigger.svelte @@ -0,0 +1,7 @@ +<script lang="ts"> + import { AlertDialog as AlertDialogPrimitive } from 'bits-ui'; + + let { ref = $bindable(null), ...restProps }: AlertDialogPrimitive.TriggerProps = $props(); +</script> + +<AlertDialogPrimitive.Trigger bind:ref data-slot="alert-dialog-trigger" {...restProps} /> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/alert-dialog/index.ts b/llama.cpp/tools/server/webui/src/lib/components/ui/alert-dialog/index.ts new file mode 100644 index 0000000..a4439bc --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/alert-dialog/index.ts @@ -0,0 +1,39 @@ +import { AlertDialog as AlertDialogPrimitive } from 'bits-ui'; +import Trigger from './alert-dialog-trigger.svelte'; +import Title from './alert-dialog-title.svelte'; +import Action from './alert-dialog-action.svelte'; +import Cancel from './alert-dialog-cancel.svelte'; +import Footer from './alert-dialog-footer.svelte'; +import Header from './alert-dialog-header.svelte'; +import Overlay from './alert-dialog-overlay.svelte'; +import Content from './alert-dialog-content.svelte'; +import Description from './alert-dialog-description.svelte'; + +const Root = AlertDialogPrimitive.Root; +const Portal = AlertDialogPrimitive.Portal; + +export { + Root, + Title, + Action, + Cancel, + Portal, + Footer, + Header, + Trigger, + Overlay, + Content, + Description, + // + Root as AlertDialog, + Title as AlertDialogTitle, + Action as AlertDialogAction, + Cancel as AlertDialogCancel, + Portal as AlertDialogPortal, + Footer as AlertDialogFooter, + Header as AlertDialogHeader, + Trigger as AlertDialogTrigger, + Overlay as AlertDialogOverlay, + Content as AlertDialogContent, + Description as AlertDialogDescription +}; diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/alert/alert-description.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/alert/alert-description.svelte new file mode 100644 index 0000000..440d006 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/alert/alert-description.svelte @@ -0,0 +1,23 @@ +<script lang="ts"> + import type { HTMLAttributes } from 'svelte/elements'; + import { cn, type WithElementRef } from '$lib/components/ui/utils.js'; + + let { + ref = $bindable(null), + class: className, + children, + ...restProps + }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props(); +</script> + +<div + bind:this={ref} + data-slot="alert-description" + class={cn( + 'col-start-2 grid justify-items-start gap-1 text-sm text-muted-foreground [&_p]:leading-relaxed', + className + )} + {...restProps} +> + {@render children?.()} +</div> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/alert/alert-title.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/alert/alert-title.svelte new file mode 100644 index 0000000..0721aeb --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/alert/alert-title.svelte @@ -0,0 +1,20 @@ +<script lang="ts"> + import type { HTMLAttributes } from 'svelte/elements'; + import { cn, type WithElementRef } from '$lib/components/ui/utils.js'; + + let { + ref = $bindable(null), + class: className, + children, + ...restProps + }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props(); +</script> + +<div + bind:this={ref} + data-slot="alert-title" + class={cn('col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight', className)} + {...restProps} +> + {@render children?.()} +</div> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/alert/alert.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/alert/alert.svelte new file mode 100644 index 0000000..7d79e4b --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/alert/alert.svelte @@ -0,0 +1,44 @@ +<script lang="ts" module> + import { type VariantProps, tv } from 'tailwind-variants'; + + export const alertVariants = tv({ + base: 'relative grid w-full grid-cols-[0_1fr] items-start gap-y-0.5 rounded-lg border px-4 py-3 text-sm has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] has-[>svg]:gap-x-3 [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current', + variants: { + variant: { + default: 'bg-card text-card-foreground', + destructive: + 'text-destructive bg-card *:data-[slot=alert-description]:text-destructive/90 [&>svg]:text-current' + } + }, + defaultVariants: { + variant: 'default' + } + }); + + export type AlertVariant = VariantProps<typeof alertVariants>['variant']; +</script> + +<script lang="ts"> + import type { HTMLAttributes } from 'svelte/elements'; + import { cn, type WithElementRef } from '$lib/components/ui/utils.js'; + + let { + ref = $bindable(null), + class: className, + variant = 'default', + children, + ...restProps + }: WithElementRef<HTMLAttributes<HTMLDivElement>> & { + variant?: AlertVariant; + } = $props(); +</script> + +<div + bind:this={ref} + data-slot="alert" + class={cn(alertVariants({ variant }), className)} + {...restProps} + role="alert" +> + {@render children?.()} +</div> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/alert/index.ts b/llama.cpp/tools/server/webui/src/lib/components/ui/alert/index.ts new file mode 100644 index 0000000..5e0f854 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/alert/index.ts @@ -0,0 +1,14 @@ +import Root from './alert.svelte'; +import Description from './alert-description.svelte'; +import Title from './alert-title.svelte'; +export { alertVariants, type AlertVariant } from './alert.svelte'; + +export { + Root, + Description, + Title, + // + Root as Alert, + Description as AlertDescription, + Title as AlertTitle +}; diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/badge/badge.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/badge/badge.svelte new file mode 100644 index 0000000..4d15145 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/badge/badge.svelte @@ -0,0 +1,49 @@ +<script lang="ts" module> + import { type VariantProps, tv } from 'tailwind-variants'; + + export const badgeVariants = tv({ + base: 'focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden whitespace-nowrap rounded-md border px-2 py-0.5 text-xs font-medium transition-[color,box-shadow] focus-visible:ring-[3px] [&>svg]:pointer-events-none [&>svg]:size-3', + variants: { + variant: { + default: 'bg-primary text-primary-foreground [a&]:hover:bg-primary/90 border-transparent', + secondary: + 'bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90 border-transparent', + destructive: + 'bg-destructive [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/70 border-transparent text-white', + outline: 'text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground' + } + }, + defaultVariants: { + variant: 'default' + } + }); + + export type BadgeVariant = VariantProps<typeof badgeVariants>['variant']; +</script> + +<script lang="ts"> + import type { HTMLAnchorAttributes } from 'svelte/elements'; + import { cn, type WithElementRef } from '$lib/components/ui/utils'; + + let { + ref = $bindable(null), + href, + class: className, + variant = 'default', + children, + ...restProps + }: WithElementRef<HTMLAnchorAttributes> & { + variant?: BadgeVariant; + } = $props(); +</script> + +<svelte:element + this={href ? 'a' : 'span'} + bind:this={ref} + data-slot="badge" + {href} + class={cn(badgeVariants({ variant }), className)} + {...restProps} +> + {@render children?.()} +</svelte:element> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/badge/index.ts b/llama.cpp/tools/server/webui/src/lib/components/ui/badge/index.ts new file mode 100644 index 0000000..f05fb87 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/badge/index.ts @@ -0,0 +1,2 @@ +export { default as Badge } from './badge.svelte'; +export { badgeVariants, type BadgeVariant } from './badge.svelte'; diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/button/button.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/button/button.svelte new file mode 100644 index 0000000..d12c8de --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/button/button.svelte @@ -0,0 +1,87 @@ +<script lang="ts" module> + import { cn, type WithElementRef } from '$lib/components/ui/utils'; + import type { HTMLAnchorAttributes, HTMLButtonAttributes } from 'svelte/elements'; + import { type VariantProps, tv } from 'tailwind-variants'; + + export const buttonVariants = tv({ + base: "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive inline-flex shrink-0 items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium outline-none transition-all focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0", + variants: { + variant: { + default: 'bg-primary text-primary-foreground shadow-xs hover:bg-primary/90', + destructive: + 'bg-destructive shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60 text-white', + outline: + 'bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 border', + secondary: 'bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80', + ghost: 'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50', + link: 'text-primary underline-offset-4 hover:underline' + }, + size: { + default: 'h-9 px-4 py-2 has-[>svg]:px-3', + sm: 'h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5', + lg: 'h-10 rounded-md px-6 has-[>svg]:px-4', + icon: 'size-9' + } + }, + defaultVariants: { + variant: 'default', + size: 'default' + } + }); + + export type ButtonVariant = VariantProps<typeof buttonVariants>['variant']; + export type ButtonSize = VariantProps<typeof buttonVariants>['size']; + + export type ButtonProps = WithElementRef<HTMLButtonAttributes> & + WithElementRef<HTMLAnchorAttributes> & { + variant?: ButtonVariant; + size?: ButtonSize; + }; +</script> + +<script lang="ts"> + let { + class: className, + variant = 'default', + size = 'default', + ref = $bindable(null), + href = undefined, + type = 'button', + disabled, + children, + ...restProps + }: ButtonProps = $props(); +</script> + +{#if href} + <a + bind:this={ref} + data-slot="button" + class={cn(buttonVariants({ variant, size }), className)} + href={disabled ? undefined : href} + aria-disabled={disabled} + role={disabled ? 'link' : undefined} + tabindex={disabled ? -1 : undefined} + {...restProps} + > + {@render children?.()} + </a> +{:else} + <button + bind:this={ref} + data-slot="button" + class={cn(buttonVariants({ variant, size }), className)} + {type} + {disabled} + {...restProps} + > + {@render children?.()} + </button> +{/if} + +<style> + a, + button { + cursor: pointer; + } +</style> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/button/index.ts b/llama.cpp/tools/server/webui/src/lib/components/ui/button/index.ts new file mode 100644 index 0000000..5414d9d --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/button/index.ts @@ -0,0 +1,17 @@ +import Root, { + type ButtonProps, + type ButtonSize, + type ButtonVariant, + buttonVariants +} from './button.svelte'; + +export { + Root, + type ButtonProps as Props, + // + Root as Button, + buttonVariants, + type ButtonProps, + type ButtonSize, + type ButtonVariant +}; diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/card/card-action.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/card/card-action.svelte new file mode 100644 index 0000000..0d4e965 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/card/card-action.svelte @@ -0,0 +1,20 @@ +<script lang="ts"> + import { cn, type WithElementRef } from '$lib/components/ui/utils'; + import type { HTMLAttributes } from 'svelte/elements'; + + let { + ref = $bindable(null), + class: className, + children, + ...restProps + }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props(); +</script> + +<div + bind:this={ref} + data-slot="card-action" + class={cn('col-start-2 row-span-2 row-start-1 self-start justify-self-end', className)} + {...restProps} +> + {@render children?.()} +</div> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/card/card-content.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/card/card-content.svelte new file mode 100644 index 0000000..c68f613 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/card/card-content.svelte @@ -0,0 +1,15 @@ +<script lang="ts"> + import type { HTMLAttributes } from 'svelte/elements'; + import { cn, type WithElementRef } from '$lib/components/ui/utils'; + + let { + ref = $bindable(null), + class: className, + children, + ...restProps + }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props(); +</script> + +<div bind:this={ref} data-slot="card-content" class={cn('px-6', className)} {...restProps}> + {@render children?.()} +</div> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/card/card-description.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/card/card-description.svelte new file mode 100644 index 0000000..81578df --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/card/card-description.svelte @@ -0,0 +1,20 @@ +<script lang="ts"> + import type { HTMLAttributes } from 'svelte/elements'; + import { cn, type WithElementRef } from '$lib/components/ui/utils'; + + let { + ref = $bindable(null), + class: className, + children, + ...restProps + }: WithElementRef<HTMLAttributes<HTMLParagraphElement>> = $props(); +</script> + +<p + bind:this={ref} + data-slot="card-description" + class={cn('text-sm text-muted-foreground', className)} + {...restProps} +> + {@render children?.()} +</p> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/card/card-footer.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/card/card-footer.svelte new file mode 100644 index 0000000..0366459 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/card/card-footer.svelte @@ -0,0 +1,20 @@ +<script lang="ts"> + import { cn, type WithElementRef } from '$lib/components/ui/utils'; + import type { HTMLAttributes } from 'svelte/elements'; + + let { + ref = $bindable(null), + class: className, + children, + ...restProps + }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props(); +</script> + +<div + bind:this={ref} + data-slot="card-footer" + class={cn('flex items-center px-6 [.border-t]:pt-6', className)} + {...restProps} +> + {@render children?.()} +</div> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/card/card-header.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/card/card-header.svelte new file mode 100644 index 0000000..74ab163 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/card/card-header.svelte @@ -0,0 +1,23 @@ +<script lang="ts"> + import { cn, type WithElementRef } from '$lib/components/ui/utils'; + import type { HTMLAttributes } from 'svelte/elements'; + + let { + ref = $bindable(null), + class: className, + children, + ...restProps + }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props(); +</script> + +<div + bind:this={ref} + data-slot="card-header" + class={cn( + '@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6', + className + )} + {...restProps} +> + {@render children?.()} +</div> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/card/card-title.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/card/card-title.svelte new file mode 100644 index 0000000..8dfc062 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/card/card-title.svelte @@ -0,0 +1,20 @@ +<script lang="ts"> + import type { HTMLAttributes } from 'svelte/elements'; + import { cn, type WithElementRef } from '$lib/components/ui/utils'; + + let { + ref = $bindable(null), + class: className, + children, + ...restProps + }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props(); +</script> + +<div + bind:this={ref} + data-slot="card-title" + class={cn('leading-none font-semibold', className)} + {...restProps} +> + {@render children?.()} +</div> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/card/card.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/card/card.svelte new file mode 100644 index 0000000..c40d143 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/card/card.svelte @@ -0,0 +1,23 @@ +<script lang="ts"> + import type { HTMLAttributes } from 'svelte/elements'; + import { cn, type WithElementRef } from '$lib/components/ui/utils'; + + let { + ref = $bindable(null), + class: className, + children, + ...restProps + }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props(); +</script> + +<div + bind:this={ref} + data-slot="card" + class={cn( + 'flex flex-col gap-6 rounded-xl border bg-card py-6 text-card-foreground shadow-sm', + className + )} + {...restProps} +> + {@render children?.()} +</div> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/card/index.ts b/llama.cpp/tools/server/webui/src/lib/components/ui/card/index.ts new file mode 100644 index 0000000..77d3674 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/card/index.ts @@ -0,0 +1,25 @@ +import Root from './card.svelte'; +import Content from './card-content.svelte'; +import Description from './card-description.svelte'; +import Footer from './card-footer.svelte'; +import Header from './card-header.svelte'; +import Title from './card-title.svelte'; +import Action from './card-action.svelte'; + +export { + Root, + Content, + Description, + Footer, + Header, + Title, + Action, + // + Root as Card, + Content as CardContent, + Description as CardDescription, + Footer as CardFooter, + Header as CardHeader, + Title as CardTitle, + Action as CardAction +}; diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/checkbox/checkbox.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/checkbox/checkbox.svelte new file mode 100644 index 0000000..aafa071 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/checkbox/checkbox.svelte @@ -0,0 +1,36 @@ +<script lang="ts"> + import { Checkbox as CheckboxPrimitive } from 'bits-ui'; + import CheckIcon from '@lucide/svelte/icons/check'; + import MinusIcon from '@lucide/svelte/icons/minus'; + import { cn, type WithoutChildrenOrChild } from '$lib/components/ui/utils.js'; + + let { + ref = $bindable(null), + checked = $bindable(false), + indeterminate = $bindable(false), + class: className, + ...restProps + }: WithoutChildrenOrChild<CheckboxPrimitive.RootProps> = $props(); +</script> + +<CheckboxPrimitive.Root + bind:ref + data-slot="checkbox" + class={cn( + 'peer flex size-4 shrink-0 items-center justify-center rounded-[4px] border border-input shadow-xs transition-shadow outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:bg-input/30 dark:aria-invalid:ring-destructive/40 dark:data-[state=checked]:bg-primary', + className + )} + bind:checked + bind:indeterminate + {...restProps} +> + {#snippet children({ checked, indeterminate })} + <div data-slot="checkbox-indicator" class="text-current transition-none"> + {#if checked} + <CheckIcon class="size-3.5" /> + {:else if indeterminate} + <MinusIcon class="size-3.5" /> + {/if} + </div> + {/snippet} +</CheckboxPrimitive.Root> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/checkbox/index.ts b/llama.cpp/tools/server/webui/src/lib/components/ui/checkbox/index.ts new file mode 100644 index 0000000..5c27671 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/checkbox/index.ts @@ -0,0 +1,6 @@ +import Root from './checkbox.svelte'; +export { + Root, + // + Root as Checkbox +}; diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/collapsible/collapsible-content.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/collapsible/collapsible-content.svelte new file mode 100644 index 0000000..59b068c --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/collapsible/collapsible-content.svelte @@ -0,0 +1,7 @@ +<script lang="ts"> + import { Collapsible as CollapsiblePrimitive } from 'bits-ui'; + + let { ref = $bindable(null), ...restProps }: CollapsiblePrimitive.ContentProps = $props(); +</script> + +<CollapsiblePrimitive.Content bind:ref data-slot="collapsible-content" {...restProps} /> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/collapsible/collapsible-trigger.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/collapsible/collapsible-trigger.svelte new file mode 100644 index 0000000..c88ceba --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/collapsible/collapsible-trigger.svelte @@ -0,0 +1,7 @@ +<script lang="ts"> + import { Collapsible as CollapsiblePrimitive } from 'bits-ui'; + + let { ref = $bindable(null), ...restProps }: CollapsiblePrimitive.TriggerProps = $props(); +</script> + +<CollapsiblePrimitive.Trigger bind:ref data-slot="collapsible-trigger" {...restProps} /> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/collapsible/collapsible.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/collapsible/collapsible.svelte new file mode 100644 index 0000000..7a8c5da --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/collapsible/collapsible.svelte @@ -0,0 +1,11 @@ +<script lang="ts"> + import { Collapsible as CollapsiblePrimitive } from 'bits-ui'; + + let { + ref = $bindable(null), + open = $bindable(false), + ...restProps + }: CollapsiblePrimitive.RootProps = $props(); +</script> + +<CollapsiblePrimitive.Root bind:ref bind:open data-slot="collapsible" {...restProps} /> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/collapsible/index.ts b/llama.cpp/tools/server/webui/src/lib/components/ui/collapsible/index.ts new file mode 100644 index 0000000..8181f64 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/collapsible/index.ts @@ -0,0 +1,13 @@ +import Root from './collapsible.svelte'; +import Trigger from './collapsible-trigger.svelte'; +import Content from './collapsible-content.svelte'; + +export { + Root, + Content, + Trigger, + // + Root as Collapsible, + Content as CollapsibleContent, + Trigger as CollapsibleTrigger +}; diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/dialog/dialog-close.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/dialog/dialog-close.svelte new file mode 100644 index 0000000..e8a96a7 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/dialog/dialog-close.svelte @@ -0,0 +1,7 @@ +<script lang="ts"> + import { Dialog as DialogPrimitive } from 'bits-ui'; + + let { ref = $bindable(null), ...restProps }: DialogPrimitive.CloseProps = $props(); +</script> + +<DialogPrimitive.Close bind:ref data-slot="dialog-close" {...restProps} /> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/dialog/dialog-content.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/dialog/dialog-content.svelte new file mode 100644 index 0000000..74df0ea --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/dialog/dialog-content.svelte @@ -0,0 +1,43 @@ +<script lang="ts"> + import { Dialog as DialogPrimitive } from 'bits-ui'; + import XIcon from '@lucide/svelte/icons/x'; + import type { Snippet } from 'svelte'; + import * as Dialog from './index.js'; + import { cn, type WithoutChildrenOrChild } from '$lib/components/ui/utils'; + + let { + ref = $bindable(null), + class: className, + portalProps, + children, + showCloseButton = true, + ...restProps + }: WithoutChildrenOrChild<DialogPrimitive.ContentProps> & { + portalProps?: DialogPrimitive.PortalProps; + children: Snippet; + showCloseButton?: boolean; + } = $props(); +</script> + +<Dialog.Portal {...portalProps}> + <Dialog.Overlay /> + <DialogPrimitive.Content + bind:ref + data-slot="dialog-content" + class={cn( + `fixed top-[50%] left-[50%] z-50 grid max-h-[100dvh] w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 overflow-y-auto rounded-lg border border-border/30 bg-background p-6 shadow-lg duration-200 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 sm:max-w-lg md:max-h-[100vh]`, + className + )} + {...restProps} + > + {@render children?.()} + {#if showCloseButton} + <DialogPrimitive.Close + class="absolute top-4 right-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4" + > + <XIcon /> + <span class="sr-only">Close</span> + </DialogPrimitive.Close> + {/if} + </DialogPrimitive.Content> +</Dialog.Portal> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/dialog/dialog-description.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/dialog/dialog-description.svelte new file mode 100644 index 0000000..6c0c192 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/dialog/dialog-description.svelte @@ -0,0 +1,17 @@ +<script lang="ts"> + import { Dialog as DialogPrimitive } from 'bits-ui'; + import { cn } from '$lib/components/ui/utils'; + + let { + ref = $bindable(null), + class: className, + ...restProps + }: DialogPrimitive.DescriptionProps = $props(); +</script> + +<DialogPrimitive.Description + bind:ref + data-slot="dialog-description" + class={cn('text-sm text-muted-foreground', className)} + {...restProps} +/> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/dialog/dialog-footer.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/dialog/dialog-footer.svelte new file mode 100644 index 0000000..abf948f --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/dialog/dialog-footer.svelte @@ -0,0 +1,20 @@ +<script lang="ts"> + import { cn, type WithElementRef } from '$lib/components/ui/utils'; + import type { HTMLAttributes } from 'svelte/elements'; + + let { + ref = $bindable(null), + class: className, + children, + ...restProps + }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props(); +</script> + +<div + bind:this={ref} + data-slot="dialog-footer" + class={cn('flex flex-col-reverse gap-2 sm:flex-row sm:justify-end', className)} + {...restProps} +> + {@render children?.()} +</div> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/dialog/dialog-header.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/dialog/dialog-header.svelte new file mode 100644 index 0000000..7ba9ba1 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/dialog/dialog-header.svelte @@ -0,0 +1,20 @@ +<script lang="ts"> + import type { HTMLAttributes } from 'svelte/elements'; + import { cn, type WithElementRef } from '$lib/components/ui/utils'; + + let { + ref = $bindable(null), + class: className, + children, + ...restProps + }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props(); +</script> + +<div + bind:this={ref} + data-slot="dialog-header" + class={cn('flex flex-col gap-2 text-center sm:text-left', className)} + {...restProps} +> + {@render children?.()} +</div> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/dialog/dialog-overlay.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/dialog/dialog-overlay.svelte new file mode 100644 index 0000000..a6e9a10 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/dialog/dialog-overlay.svelte @@ -0,0 +1,20 @@ +<script lang="ts"> + import { Dialog as DialogPrimitive } from 'bits-ui'; + import { cn } from '$lib/components/ui/utils'; + + let { + ref = $bindable(null), + class: className, + ...restProps + }: DialogPrimitive.OverlayProps = $props(); +</script> + +<DialogPrimitive.Overlay + bind:ref + data-slot="dialog-overlay" + class={cn( + 'fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0', + className + )} + {...restProps} +/> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/dialog/dialog-title.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/dialog/dialog-title.svelte new file mode 100644 index 0000000..e8c99c5 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/dialog/dialog-title.svelte @@ -0,0 +1,17 @@ +<script lang="ts"> + import { Dialog as DialogPrimitive } from 'bits-ui'; + import { cn } from '$lib/components/ui/utils'; + + let { + ref = $bindable(null), + class: className, + ...restProps + }: DialogPrimitive.TitleProps = $props(); +</script> + +<DialogPrimitive.Title + bind:ref + data-slot="dialog-title" + class={cn('text-lg leading-none font-semibold', className)} + {...restProps} +/> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/dialog/dialog-trigger.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/dialog/dialog-trigger.svelte new file mode 100644 index 0000000..ac04d9f --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/dialog/dialog-trigger.svelte @@ -0,0 +1,7 @@ +<script lang="ts"> + import { Dialog as DialogPrimitive } from 'bits-ui'; + + let { ref = $bindable(null), ...restProps }: DialogPrimitive.TriggerProps = $props(); +</script> + +<DialogPrimitive.Trigger bind:ref data-slot="dialog-trigger" {...restProps} /> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/dialog/index.ts b/llama.cpp/tools/server/webui/src/lib/components/ui/dialog/index.ts new file mode 100644 index 0000000..d9e5fb8 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/dialog/index.ts @@ -0,0 +1,37 @@ +import { Dialog as DialogPrimitive } from 'bits-ui'; + +import Title from './dialog-title.svelte'; +import Footer from './dialog-footer.svelte'; +import Header from './dialog-header.svelte'; +import Overlay from './dialog-overlay.svelte'; +import Content from './dialog-content.svelte'; +import Description from './dialog-description.svelte'; +import Trigger from './dialog-trigger.svelte'; +import Close from './dialog-close.svelte'; + +const Root = DialogPrimitive.Root; +const Portal = DialogPrimitive.Portal; + +export { + Root, + Title, + Portal, + Footer, + Header, + Trigger, + Overlay, + Content, + Description, + Close, + // + Root as Dialog, + Title as DialogTitle, + Portal as DialogPortal, + Footer as DialogFooter, + Header as DialogHeader, + Trigger as DialogTrigger, + Overlay as DialogOverlay, + Content as DialogContent, + Description as DialogDescription, + Close as DialogClose +}; diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte new file mode 100644 index 0000000..e71acef --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte @@ -0,0 +1,41 @@ +<script lang="ts"> + import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui'; + import CheckIcon from '@lucide/svelte/icons/check'; + import MinusIcon from '@lucide/svelte/icons/minus'; + import { cn, type WithoutChildrenOrChild } from '$lib/components/ui/utils.js'; + import type { Snippet } from 'svelte'; + + let { + ref = $bindable(null), + checked = $bindable(false), + indeterminate = $bindable(false), + class: className, + children: childrenProp, + ...restProps + }: WithoutChildrenOrChild<DropdownMenuPrimitive.CheckboxItemProps> & { + children?: Snippet; + } = $props(); +</script> + +<DropdownMenuPrimitive.CheckboxItem + bind:ref + bind:checked + bind:indeterminate + data-slot="dropdown-menu-checkbox-item" + class={cn( + "relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", + className + )} + {...restProps} +> + {#snippet children({ checked, indeterminate })} + <span class="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center"> + {#if indeterminate} + <MinusIcon class="size-4" /> + {:else} + <CheckIcon class={cn('size-4', !checked && 'text-transparent')} /> + {/if} + </span> + {@render childrenProp?.()} + {/snippet} +</DropdownMenuPrimitive.CheckboxItem> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte new file mode 100644 index 0000000..869c38e --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte @@ -0,0 +1,27 @@ +<script lang="ts"> + import { cn } from '$lib/components/ui/utils.js'; + import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui'; + + let { + ref = $bindable(null), + sideOffset = 4, + portalProps, + class: className, + ...restProps + }: DropdownMenuPrimitive.ContentProps & { + portalProps?: DropdownMenuPrimitive.PortalProps; + } = $props(); +</script> + +<DropdownMenuPrimitive.Portal {...portalProps}> + <DropdownMenuPrimitive.Content + bind:ref + data-slot="dropdown-menu-content" + {sideOffset} + class={cn( + 'z-50 max-h-(--bits-dropdown-menu-content-available-height) min-w-[8rem] origin-(--bits-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md outline-none data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 dark:border-border/20', + className + )} + {...restProps} + /> +</DropdownMenuPrimitive.Portal> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte new file mode 100644 index 0000000..f217966 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte @@ -0,0 +1,22 @@ +<script lang="ts"> + import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui'; + import { cn } from '$lib/components/ui/utils.js'; + import type { ComponentProps } from 'svelte'; + + let { + ref = $bindable(null), + class: className, + inset, + ...restProps + }: ComponentProps<typeof DropdownMenuPrimitive.GroupHeading> & { + inset?: boolean; + } = $props(); +</script> + +<DropdownMenuPrimitive.GroupHeading + bind:ref + data-slot="dropdown-menu-group-heading" + data-inset={inset} + class={cn('px-2 py-1.5 text-sm font-semibold data-[inset]:pl-8', className)} + {...restProps} +/> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte new file mode 100644 index 0000000..261ab7e --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte @@ -0,0 +1,7 @@ +<script lang="ts"> + import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui'; + + let { ref = $bindable(null), ...restProps }: DropdownMenuPrimitive.GroupProps = $props(); +</script> + +<DropdownMenuPrimitive.Group bind:ref data-slot="dropdown-menu-group" {...restProps} /> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte new file mode 100644 index 0000000..1ac5615 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte @@ -0,0 +1,27 @@ +<script lang="ts"> + import { cn } from '$lib/components/ui/utils.js'; + import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui'; + + let { + ref = $bindable(null), + class: className, + inset, + variant = 'default', + ...restProps + }: DropdownMenuPrimitive.ItemProps & { + inset?: boolean; + variant?: 'default' | 'destructive'; + } = $props(); +</script> + +<DropdownMenuPrimitive.Item + bind:ref + data-slot="dropdown-menu-item" + data-inset={inset} + data-variant={variant} + class={cn( + "relative flex cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-highlighted:bg-accent data-highlighted:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 data-[variant=destructive]:text-destructive data-[variant=destructive]:data-highlighted:bg-destructive/10 data-[variant=destructive]:data-highlighted:text-destructive dark:data-[variant=destructive]:data-highlighted:bg-destructive/20 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground data-[variant=destructive]:*:[svg]:!text-destructive", + className + )} + {...restProps} +/> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte new file mode 100644 index 0000000..15b546e --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte @@ -0,0 +1,24 @@ +<script lang="ts"> + import { cn, type WithElementRef } from '$lib/components/ui/utils.js'; + import type { HTMLAttributes } from 'svelte/elements'; + + let { + ref = $bindable(null), + class: className, + inset, + children, + ...restProps + }: WithElementRef<HTMLAttributes<HTMLDivElement>> & { + inset?: boolean; + } = $props(); +</script> + +<div + bind:this={ref} + data-slot="dropdown-menu-label" + data-inset={inset} + class={cn('px-2 py-1.5 text-sm font-semibold data-[inset]:pl-8', className)} + {...restProps} +> + {@render children?.()} +</div> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte new file mode 100644 index 0000000..3e98749 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte @@ -0,0 +1,16 @@ +<script lang="ts"> + import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui'; + + let { + ref = $bindable(null), + value = $bindable(), + ...restProps + }: DropdownMenuPrimitive.RadioGroupProps = $props(); +</script> + +<DropdownMenuPrimitive.RadioGroup + bind:ref + bind:value + data-slot="dropdown-menu-radio-group" + {...restProps} +/> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte new file mode 100644 index 0000000..97ba772 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte @@ -0,0 +1,31 @@ +<script lang="ts"> + import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui'; + import CircleIcon from '@lucide/svelte/icons/circle'; + import { cn, type WithoutChild } from '$lib/components/ui/utils.js'; + + let { + ref = $bindable(null), + class: className, + children: childrenProp, + ...restProps + }: WithoutChild<DropdownMenuPrimitive.RadioItemProps> = $props(); +</script> + +<DropdownMenuPrimitive.RadioItem + bind:ref + data-slot="dropdown-menu-radio-item" + class={cn( + "relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", + className + )} + {...restProps} +> + {#snippet children({ checked })} + <span class="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center"> + {#if checked} + <CircleIcon class="size-2 fill-current" /> + {/if} + </span> + {@render childrenProp?.({ checked })} + {/snippet} +</DropdownMenuPrimitive.RadioItem> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte new file mode 100644 index 0000000..17b64ac --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte @@ -0,0 +1,17 @@ +<script lang="ts"> + import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui'; + import { cn } from '$lib/components/ui/utils.js'; + + let { + ref = $bindable(null), + class: className, + ...restProps + }: DropdownMenuPrimitive.SeparatorProps = $props(); +</script> + +<DropdownMenuPrimitive.Separator + bind:ref + data-slot="dropdown-menu-separator" + class={cn('-mx-1 my-1 h-px bg-border/20', className)} + {...restProps} +/> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte new file mode 100644 index 0000000..c3ccc21 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte @@ -0,0 +1,20 @@ +<script lang="ts"> + import type { HTMLAttributes } from 'svelte/elements'; + import { cn, type WithElementRef } from '$lib/components/ui/utils.js'; + + let { + ref = $bindable(null), + class: className, + children, + ...restProps + }: WithElementRef<HTMLAttributes<HTMLSpanElement>> = $props(); +</script> + +<span + bind:this={ref} + data-slot="dropdown-menu-shortcut" + class={cn('ml-auto text-xs tracking-widest text-muted-foreground', className)} + {...restProps} +> + {@render children?.()} +</span> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte new file mode 100644 index 0000000..3ceb165 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte @@ -0,0 +1,20 @@ +<script lang="ts"> + import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui'; + import { cn } from '$lib/components/ui/utils.js'; + + let { + ref = $bindable(null), + class: className, + ...restProps + }: DropdownMenuPrimitive.SubContentProps = $props(); +</script> + +<DropdownMenuPrimitive.SubContent + bind:ref + data-slot="dropdown-menu-sub-content" + class={cn( + 'z-50 min-w-[8rem] origin-(--bits-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95', + className + )} + {...restProps} +/> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte new file mode 100644 index 0000000..550a789 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte @@ -0,0 +1,29 @@ +<script lang="ts"> + import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui'; + import ChevronRightIcon from '@lucide/svelte/icons/chevron-right'; + import { cn } from '$lib/components/ui/utils.js'; + + let { + ref = $bindable(null), + class: className, + inset, + children, + ...restProps + }: DropdownMenuPrimitive.SubTriggerProps & { + inset?: boolean; + } = $props(); +</script> + +<DropdownMenuPrimitive.SubTrigger + bind:ref + data-slot="dropdown-menu-sub-trigger" + data-inset={inset} + class={cn( + "flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-highlighted:bg-accent data-highlighted:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 data-[state=open]:bg-accent data-[state=open]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground", + className + )} + {...restProps} +> + {@render children?.()} + <ChevronRightIcon class="ml-auto size-4" /> +</DropdownMenuPrimitive.SubTrigger> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte new file mode 100644 index 0000000..032b645 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte @@ -0,0 +1,7 @@ +<script lang="ts"> + import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui'; + + let { ref = $bindable(null), ...restProps }: DropdownMenuPrimitive.TriggerProps = $props(); +</script> + +<DropdownMenuPrimitive.Trigger bind:ref data-slot="dropdown-menu-trigger" {...restProps} /> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/dropdown-menu/index.ts b/llama.cpp/tools/server/webui/src/lib/components/ui/dropdown-menu/index.ts new file mode 100644 index 0000000..aeb398e --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/dropdown-menu/index.ts @@ -0,0 +1,49 @@ +import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui'; +import CheckboxItem from './dropdown-menu-checkbox-item.svelte'; +import Content from './dropdown-menu-content.svelte'; +import Group from './dropdown-menu-group.svelte'; +import Item from './dropdown-menu-item.svelte'; +import Label from './dropdown-menu-label.svelte'; +import RadioGroup from './dropdown-menu-radio-group.svelte'; +import RadioItem from './dropdown-menu-radio-item.svelte'; +import Separator from './dropdown-menu-separator.svelte'; +import Shortcut from './dropdown-menu-shortcut.svelte'; +import Trigger from './dropdown-menu-trigger.svelte'; +import SubContent from './dropdown-menu-sub-content.svelte'; +import SubTrigger from './dropdown-menu-sub-trigger.svelte'; +import GroupHeading from './dropdown-menu-group-heading.svelte'; +const Sub = DropdownMenuPrimitive.Sub; +const Root = DropdownMenuPrimitive.Root; + +export { + CheckboxItem, + Content, + Root as DropdownMenu, + CheckboxItem as DropdownMenuCheckboxItem, + Content as DropdownMenuContent, + Group as DropdownMenuGroup, + Item as DropdownMenuItem, + Label as DropdownMenuLabel, + RadioGroup as DropdownMenuRadioGroup, + RadioItem as DropdownMenuRadioItem, + Separator as DropdownMenuSeparator, + Shortcut as DropdownMenuShortcut, + Sub as DropdownMenuSub, + SubContent as DropdownMenuSubContent, + SubTrigger as DropdownMenuSubTrigger, + Trigger as DropdownMenuTrigger, + GroupHeading as DropdownMenuGroupHeading, + Group, + GroupHeading, + Item, + Label, + RadioGroup, + RadioItem, + Root, + Separator, + Shortcut, + Sub, + SubContent, + SubTrigger, + Trigger +}; diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/input/index.ts b/llama.cpp/tools/server/webui/src/lib/components/ui/input/index.ts new file mode 100644 index 0000000..15c0933 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/input/index.ts @@ -0,0 +1,7 @@ +import Root from './input.svelte'; + +export { + Root, + // + Root as Input +}; diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/input/input.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/input/input.svelte new file mode 100644 index 0000000..889b720 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/input/input.svelte @@ -0,0 +1,51 @@ +<script lang="ts"> + import type { HTMLInputAttributes, HTMLInputTypeAttribute } from 'svelte/elements'; + import { cn, type WithElementRef } from '$lib/components/ui/utils'; + + type InputType = Exclude<HTMLInputTypeAttribute, 'file'>; + + type Props = WithElementRef< + Omit<HTMLInputAttributes, 'type'> & + ({ type: 'file'; files?: FileList } | { type?: InputType; files?: undefined }) + >; + + let { + ref = $bindable(null), + value = $bindable(), + type, + files = $bindable(), + class: className, + ...restProps + }: Props = $props(); +</script> + +{#if type === 'file'} + <input + bind:this={ref} + data-slot="input" + class={cn( + 'flex h-9 w-full min-w-0 rounded-md border border-input bg-transparent px-3 pt-1.5 text-sm font-medium shadow-xs ring-offset-background transition-[color,box-shadow] outline-none selection:bg-primary selection:text-primary-foreground placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50 md:text-sm dark:bg-input/30', + 'focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50', + 'aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40', + className + )} + type="file" + bind:files + bind:value + {...restProps} + /> +{:else} + <input + bind:this={ref} + data-slot="input" + class={cn( + 'flex h-9 w-full min-w-0 rounded-md border border-input bg-background px-3 py-1 text-base shadow-xs ring-offset-background transition-[color,box-shadow] outline-none selection:bg-primary selection:text-primary-foreground placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50 md:text-sm dark:bg-input/30', + 'focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50', + 'aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40', + className + )} + {type} + bind:value + {...restProps} + /> +{/if} diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/label/index.ts b/llama.cpp/tools/server/webui/src/lib/components/ui/label/index.ts new file mode 100644 index 0000000..808d141 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/label/index.ts @@ -0,0 +1,7 @@ +import Root from './label.svelte'; + +export { + Root, + // + Root as Label +}; diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/label/label.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/label/label.svelte new file mode 100644 index 0000000..9da4ae3 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/label/label.svelte @@ -0,0 +1,20 @@ +<script lang="ts"> + import { Label as LabelPrimitive } from 'bits-ui'; + import { cn } from '$lib/components/ui/utils.js'; + + let { + ref = $bindable(null), + class: className, + ...restProps + }: LabelPrimitive.RootProps = $props(); +</script> + +<LabelPrimitive.Root + bind:ref + data-slot="label" + class={cn( + 'flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50', + className + )} + {...restProps} +/> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/popover/index.ts b/llama.cpp/tools/server/webui/src/lib/components/ui/popover/index.ts new file mode 100644 index 0000000..c5937fb --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/popover/index.ts @@ -0,0 +1,19 @@ +import Root from './popover.svelte'; +import Close from './popover-close.svelte'; +import Content from './popover-content.svelte'; +import Trigger from './popover-trigger.svelte'; +import Portal from './popover-portal.svelte'; + +export { + Root, + Content, + Trigger, + Close, + Portal, + // + Root as Popover, + Content as PopoverContent, + Trigger as PopoverTrigger, + Close as PopoverClose, + Portal as PopoverPortal +}; diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/popover/popover-close.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/popover/popover-close.svelte new file mode 100644 index 0000000..dc4dec4 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/popover/popover-close.svelte @@ -0,0 +1,7 @@ +<script lang="ts"> + import { Popover as PopoverPrimitive } from 'bits-ui'; + + let { ref = $bindable(null), ...restProps }: PopoverPrimitive.CloseProps = $props(); +</script> + +<PopoverPrimitive.Close bind:ref data-slot="popover-close" {...restProps} /> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/popover/popover-content.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/popover/popover-content.svelte new file mode 100644 index 0000000..2d3513d --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/popover/popover-content.svelte @@ -0,0 +1,37 @@ +<script lang="ts"> + import { Popover as PopoverPrimitive } from 'bits-ui'; + import PopoverPortal from './popover-portal.svelte'; + import { cn, type WithoutChildrenOrChild } from '$lib/components/ui/utils.js'; + import type { ComponentProps } from 'svelte'; + + let { + ref = $bindable(null), + class: className, + sideOffset = 4, + side, + align = 'center', + collisionPadding = 8, + avoidCollisions = true, + portalProps, + ...restProps + }: PopoverPrimitive.ContentProps & { + portalProps?: WithoutChildrenOrChild<ComponentProps<typeof PopoverPortal>>; + } = $props(); +</script> + +<PopoverPortal {...portalProps}> + <PopoverPrimitive.Content + bind:ref + data-slot="popover-content" + {sideOffset} + {side} + {align} + {collisionPadding} + {avoidCollisions} + class={cn( + 'z-50 w-72 origin-(--bits-popover-content-transform-origin) rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-hidden data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-end-2 data-[side=right]:slide-in-from-start-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95', + className + )} + {...restProps} + /> +</PopoverPortal> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/popover/popover-portal.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/popover/popover-portal.svelte new file mode 100644 index 0000000..25efb87 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/popover/popover-portal.svelte @@ -0,0 +1,7 @@ +<script lang="ts"> + import { Popover as PopoverPrimitive } from 'bits-ui'; + + let { ...restProps }: PopoverPrimitive.PortalProps = $props(); +</script> + +<PopoverPrimitive.Portal {...restProps} /> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/popover/popover-trigger.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/popover/popover-trigger.svelte new file mode 100644 index 0000000..5ef3d0e --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/popover/popover-trigger.svelte @@ -0,0 +1,17 @@ +<script lang="ts"> + import { cn } from '$lib/components/ui/utils.js'; + import { Popover as PopoverPrimitive } from 'bits-ui'; + + let { + ref = $bindable(null), + class: className, + ...restProps + }: PopoverPrimitive.TriggerProps = $props(); +</script> + +<PopoverPrimitive.Trigger + bind:ref + data-slot="popover-trigger" + class={cn('', className)} + {...restProps} +/> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/popover/popover.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/popover/popover.svelte new file mode 100644 index 0000000..f39b867 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/popover/popover.svelte @@ -0,0 +1,7 @@ +<script lang="ts"> + import { Popover as PopoverPrimitive } from 'bits-ui'; + + let { open = $bindable(false), ...restProps }: PopoverPrimitive.RootProps = $props(); +</script> + +<PopoverPrimitive.Root bind:open {...restProps} /> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/scroll-area/index.ts b/llama.cpp/tools/server/webui/src/lib/components/ui/scroll-area/index.ts new file mode 100644 index 0000000..d546806 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/scroll-area/index.ts @@ -0,0 +1,10 @@ +import Scrollbar from './scroll-area-scrollbar.svelte'; +import Root from './scroll-area.svelte'; + +export { + Root, + Scrollbar, + //, + Root as ScrollArea, + Scrollbar as ScrollAreaScrollbar +}; diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/scroll-area/scroll-area-scrollbar.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/scroll-area/scroll-area-scrollbar.svelte new file mode 100644 index 0000000..3f0d00d --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/scroll-area/scroll-area-scrollbar.svelte @@ -0,0 +1,31 @@ +<script lang="ts"> + import { ScrollArea as ScrollAreaPrimitive } from 'bits-ui'; + import { cn, type WithoutChild } from '$lib/components/ui/utils'; + + let { + ref = $bindable(null), + class: className, + orientation = 'vertical', + children, + ...restProps + }: WithoutChild<ScrollAreaPrimitive.ScrollbarProps> = $props(); +</script> + +<ScrollAreaPrimitive.Scrollbar + bind:ref + data-slot="scroll-area-scrollbar" + {orientation} + class={cn( + 'flex touch-none p-px transition-colors select-none', + orientation === 'vertical' && 'h-full w-2.5 border-l border-l-transparent', + orientation === 'horizontal' && 'h-2.5 flex-col border-t border-t-transparent', + className + )} + {...restProps} +> + {@render children?.()} + <ScrollAreaPrimitive.Thumb + data-slot="scroll-area-thumb" + class="relative flex-1 rounded-full bg-border" + /> +</ScrollAreaPrimitive.Scrollbar> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/scroll-area/scroll-area.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/scroll-area/scroll-area.svelte new file mode 100644 index 0000000..ba6f838 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/scroll-area/scroll-area.svelte @@ -0,0 +1,40 @@ +<script lang="ts"> + import { ScrollArea as ScrollAreaPrimitive } from 'bits-ui'; + import { Scrollbar } from './index.js'; + import { cn, type WithoutChild } from '$lib/components/ui/utils'; + + let { + ref = $bindable(null), + class: className, + orientation = 'vertical', + scrollbarXClasses = '', + scrollbarYClasses = '', + children, + ...restProps + }: WithoutChild<ScrollAreaPrimitive.RootProps> & { + orientation?: 'vertical' | 'horizontal' | 'both' | undefined; + scrollbarXClasses?: string | undefined; + scrollbarYClasses?: string | undefined; + } = $props(); +</script> + +<ScrollAreaPrimitive.Root + bind:ref + data-slot="scroll-area" + class={cn('relative', className)} + {...restProps} +> + <ScrollAreaPrimitive.Viewport + data-slot="scroll-area-viewport" + class="size-full rounded-[inherit] ring-ring/10 outline-ring/50 transition-[color,box-shadow] focus-visible:ring-4 focus-visible:outline-1 dark:ring-ring/20 dark:outline-ring/40" + > + {@render children?.()} + </ScrollAreaPrimitive.Viewport> + {#if orientation === 'vertical' || orientation === 'both'} + <Scrollbar orientation="vertical" class={scrollbarYClasses} /> + {/if} + {#if orientation === 'horizontal' || orientation === 'both'} + <Scrollbar orientation="horizontal" class={scrollbarXClasses} /> + {/if} + <ScrollAreaPrimitive.Corner /> +</ScrollAreaPrimitive.Root> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/select/index.ts b/llama.cpp/tools/server/webui/src/lib/components/ui/select/index.ts new file mode 100644 index 0000000..bfa73d9 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/select/index.ts @@ -0,0 +1,37 @@ +import { Select as SelectPrimitive } from 'bits-ui'; + +import Group from './select-group.svelte'; +import Label from './select-label.svelte'; +import Item from './select-item.svelte'; +import Content from './select-content.svelte'; +import Trigger from './select-trigger.svelte'; +import Separator from './select-separator.svelte'; +import ScrollDownButton from './select-scroll-down-button.svelte'; +import ScrollUpButton from './select-scroll-up-button.svelte'; +import GroupHeading from './select-group-heading.svelte'; + +const Root = SelectPrimitive.Root; + +export { + Root, + Group, + Label, + Item, + Content, + Trigger, + Separator, + ScrollDownButton, + ScrollUpButton, + GroupHeading, + // + Root as Select, + Group as SelectGroup, + Label as SelectLabel, + Item as SelectItem, + Content as SelectContent, + Trigger as SelectTrigger, + Separator as SelectSeparator, + ScrollDownButton as SelectScrollDownButton, + ScrollUpButton as SelectScrollUpButton, + GroupHeading as SelectGroupHeading +}; diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/select/select-content.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/select/select-content.svelte new file mode 100644 index 0000000..4050628 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/select/select-content.svelte @@ -0,0 +1,111 @@ +<script lang="ts"> + import { onDestroy, onMount } from 'svelte'; + import { Select as SelectPrimitive } from 'bits-ui'; + import SelectScrollUpButton from './select-scroll-up-button.svelte'; + import SelectScrollDownButton from './select-scroll-down-button.svelte'; + import { cn, type WithoutChild } from '$lib/components/ui/utils.js'; + + let { + ref = $bindable(null), + class: className, + sideOffset = 4, + portalProps, + children, + ...restProps + }: WithoutChild<SelectPrimitive.ContentProps> & { + portalProps?: SelectPrimitive.PortalProps; + } = $props(); + + let cleanupInternalListeners: (() => void) | undefined; + + onMount(() => { + const listenerOptions: AddEventListenerOptions = { passive: false }; + + const blockOutsideWheel = (event: WheelEvent) => { + if (!ref) { + return; + } + + const target = event.target as Node | null; + + if (!target || !ref.contains(target)) { + event.preventDefault(); + event.stopPropagation(); + } + }; + + const blockOutsideTouchMove = (event: TouchEvent) => { + if (!ref) { + return; + } + + const target = event.target as Node | null; + + if (!target || !ref.contains(target)) { + event.preventDefault(); + event.stopPropagation(); + } + }; + + document.addEventListener('wheel', blockOutsideWheel, listenerOptions); + document.addEventListener('touchmove', blockOutsideTouchMove, listenerOptions); + + return () => { + document.removeEventListener('wheel', blockOutsideWheel, listenerOptions); + document.removeEventListener('touchmove', blockOutsideTouchMove, listenerOptions); + }; + }); + + $effect(() => { + const element = ref; + + cleanupInternalListeners?.(); + + if (!element) { + return; + } + + const stopWheelPropagation = (event: WheelEvent) => { + event.stopPropagation(); + }; + + const stopTouchPropagation = (event: TouchEvent) => { + event.stopPropagation(); + }; + + element.addEventListener('wheel', stopWheelPropagation); + element.addEventListener('touchmove', stopTouchPropagation); + + cleanupInternalListeners = () => { + element.removeEventListener('wheel', stopWheelPropagation); + element.removeEventListener('touchmove', stopTouchPropagation); + }; + }); + + onDestroy(() => { + cleanupInternalListeners?.(); + }); +</script> + +<SelectPrimitive.Portal {...portalProps}> + <SelectPrimitive.Content + bind:ref + {sideOffset} + data-slot="select-content" + class={cn( + 'relative z-[var(--layer-popover,1000000)] max-h-(--bits-select-content-available-height) min-w-[8rem] origin-(--bits-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border bg-popover text-popover-foreground shadow-md data-[side=bottom]:translate-y-1 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:-translate-x-1 data-[side=left]:slide-in-from-right-2 data-[side=right]:translate-x-1 data-[side=right]:slide-in-from-left-2 data-[side=top]:-translate-y-1 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95', + className + )} + {...restProps} + > + <SelectScrollUpButton /> + <SelectPrimitive.Viewport + class={cn( + 'h-(--bits-select-anchor-height) w-full min-w-(--bits-select-anchor-width) scroll-my-1 p-1' + )} + > + {@render children?.()} + </SelectPrimitive.Viewport> + <SelectScrollDownButton /> + </SelectPrimitive.Content> +</SelectPrimitive.Portal> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/select/select-group-heading.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/select/select-group-heading.svelte new file mode 100644 index 0000000..77c2042 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/select/select-group-heading.svelte @@ -0,0 +1,21 @@ +<script lang="ts"> + import { Select as SelectPrimitive } from 'bits-ui'; + import { cn } from '$lib/components/ui/utils.js'; + import type { ComponentProps } from 'svelte'; + + let { + ref = $bindable(null), + class: className, + children, + ...restProps + }: ComponentProps<typeof SelectPrimitive.GroupHeading> = $props(); +</script> + +<SelectPrimitive.GroupHeading + bind:ref + data-slot="select-group-heading" + class={cn('px-2 py-1.5 text-xs text-muted-foreground', className)} + {...restProps} +> + {@render children?.()} +</SelectPrimitive.GroupHeading> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/select/select-group.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/select/select-group.svelte new file mode 100644 index 0000000..2520795 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/select/select-group.svelte @@ -0,0 +1,7 @@ +<script lang="ts"> + import { Select as SelectPrimitive } from 'bits-ui'; + + let { ref = $bindable(null), ...restProps }: SelectPrimitive.GroupProps = $props(); +</script> + +<SelectPrimitive.Group data-slot="select-group" {...restProps} /> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/select/select-item.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/select/select-item.svelte new file mode 100644 index 0000000..02543c1 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/select/select-item.svelte @@ -0,0 +1,38 @@ +<script lang="ts"> + import CheckIcon from '@lucide/svelte/icons/check'; + import { Select as SelectPrimitive } from 'bits-ui'; + import { cn, type WithoutChild } from '$lib/components/ui/utils.js'; + + let { + ref = $bindable(null), + class: className, + value, + label, + children: childrenProp, + ...restProps + }: WithoutChild<SelectPrimitive.ItemProps> = $props(); +</script> + +<SelectPrimitive.Item + bind:ref + {value} + data-slot="select-item" + class={cn( + "relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2", + className + )} + {...restProps} +> + {#snippet children({ selected, highlighted })} + <span class="absolute right-2 flex size-3.5 items-center justify-center"> + {#if selected} + <CheckIcon class="size-4" /> + {/if} + </span> + {#if childrenProp} + {@render childrenProp({ selected, highlighted })} + {:else} + {label || value} + {/if} + {/snippet} +</SelectPrimitive.Item> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/select/select-label.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/select/select-label.svelte new file mode 100644 index 0000000..e2b830c --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/select/select-label.svelte @@ -0,0 +1,20 @@ +<script lang="ts"> + import { cn, type WithElementRef } from '$lib/components/ui/utils.js'; + import type { HTMLAttributes } from 'svelte/elements'; + + let { + ref = $bindable(null), + class: className, + children, + ...restProps + }: WithElementRef<HTMLAttributes<HTMLDivElement>> & {} = $props(); +</script> + +<div + bind:this={ref} + data-slot="select-label" + class={cn('px-2 py-1.5 text-xs text-muted-foreground', className)} + {...restProps} +> + {@render children?.()} +</div> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/select/select-scroll-down-button.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/select/select-scroll-down-button.svelte new file mode 100644 index 0000000..9256dd8 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/select/select-scroll-down-button.svelte @@ -0,0 +1,20 @@ +<script lang="ts"> + import ChevronDownIcon from '@lucide/svelte/icons/chevron-down'; + import { Select as SelectPrimitive } from 'bits-ui'; + import { cn, type WithoutChildrenOrChild } from '$lib/components/ui/utils.js'; + + let { + ref = $bindable(null), + class: className, + ...restProps + }: WithoutChildrenOrChild<SelectPrimitive.ScrollDownButtonProps> = $props(); +</script> + +<SelectPrimitive.ScrollDownButton + bind:ref + data-slot="select-scroll-down-button" + class={cn('flex cursor-default items-center justify-center py-1', className)} + {...restProps} +> + <ChevronDownIcon class="size-4" /> +</SelectPrimitive.ScrollDownButton> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/select/select-scroll-up-button.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/select/select-scroll-up-button.svelte new file mode 100644 index 0000000..552e527 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/select/select-scroll-up-button.svelte @@ -0,0 +1,20 @@ +<script lang="ts"> + import ChevronUpIcon from '@lucide/svelte/icons/chevron-up'; + import { Select as SelectPrimitive } from 'bits-ui'; + import { cn, type WithoutChildrenOrChild } from '$lib/components/ui/utils.js'; + + let { + ref = $bindable(null), + class: className, + ...restProps + }: WithoutChildrenOrChild<SelectPrimitive.ScrollUpButtonProps> = $props(); +</script> + +<SelectPrimitive.ScrollUpButton + bind:ref + data-slot="select-scroll-up-button" + class={cn('flex cursor-default items-center justify-center py-1', className)} + {...restProps} +> + <ChevronUpIcon class="size-4" /> +</SelectPrimitive.ScrollUpButton> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/select/select-separator.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/select/select-separator.svelte new file mode 100644 index 0000000..7daaa8d --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/select/select-separator.svelte @@ -0,0 +1,18 @@ +<script lang="ts"> + import type { Separator as SeparatorPrimitive } from 'bits-ui'; + import { Separator } from '$lib/components/ui/separator/index.js'; + import { cn } from '$lib/components/ui/utils.js'; + + let { + ref = $bindable(null), + class: className, + ...restProps + }: SeparatorPrimitive.RootProps = $props(); +</script> + +<Separator + bind:ref + data-slot="select-separator" + class={cn('pointer-events-none -mx-1 my-1 h-px bg-border', className)} + {...restProps} +/> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/select/select-trigger.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/select/select-trigger.svelte new file mode 100644 index 0000000..5bc28ee --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/select/select-trigger.svelte @@ -0,0 +1,40 @@ +<script lang="ts"> + import { Select as SelectPrimitive } from 'bits-ui'; + import ChevronDownIcon from '@lucide/svelte/icons/chevron-down'; + import { cn, type WithoutChild } from '$lib/components/ui/utils.js'; + + let { + ref = $bindable(null), + class: className, + children, + size = 'default', + variant = 'default', + ...restProps + }: WithoutChild<SelectPrimitive.TriggerProps> & { + size?: 'sm' | 'default'; + variant?: 'default' | 'plain'; + } = $props(); + + const baseClasses = $derived( + variant === 'plain' + ? "group inline-flex w-full items-center justify-end gap-2 whitespace-nowrap px-0 py-0 text-sm font-medium text-muted-foreground transition-colors focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 disabled:cursor-not-allowed disabled:opacity-50 data-[placeholder]:text-muted-foreground data-[size=default]:h-9 data-[size=sm]:h-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3 [&_svg:not([class*='text-'])]:text-muted-foreground" + : "flex w-fit items-center justify-between gap-2 rounded-md border border-input bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none select-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[placeholder]:text-muted-foreground data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground" + ); + + const chevronClasses = $derived( + variant === 'plain' + ? 'size-3 opacity-60 transition-transform group-data-[state=open]:-rotate-180' + : 'size-4 opacity-50' + ); +</script> + +<SelectPrimitive.Trigger + bind:ref + data-slot="select-trigger" + data-size={size} + class={cn(baseClasses, className)} + {...restProps} +> + {@render children?.()} + <ChevronDownIcon class={chevronClasses} /> +</SelectPrimitive.Trigger> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/separator/index.ts b/llama.cpp/tools/server/webui/src/lib/components/ui/separator/index.ts new file mode 100644 index 0000000..768efac --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/separator/index.ts @@ -0,0 +1,7 @@ +import Root from './separator.svelte'; + +export { + Root, + // + Root as Separator +}; diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/separator/separator.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/separator/separator.svelte new file mode 100644 index 0000000..00307fd --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/separator/separator.svelte @@ -0,0 +1,20 @@ +<script lang="ts"> + import { Separator as SeparatorPrimitive } from 'bits-ui'; + import { cn } from '$lib/components/ui/utils.js'; + + let { + ref = $bindable(null), + class: className, + ...restProps + }: SeparatorPrimitive.RootProps = $props(); +</script> + +<SeparatorPrimitive.Root + bind:ref + data-slot="separator" + class={cn( + 'shrink-0 bg-border data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px', + className + )} + {...restProps} +/> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/sheet/index.ts b/llama.cpp/tools/server/webui/src/lib/components/ui/sheet/index.ts new file mode 100644 index 0000000..139e2d2 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/sheet/index.ts @@ -0,0 +1,36 @@ +import { Dialog as SheetPrimitive } from 'bits-ui'; +import Trigger from './sheet-trigger.svelte'; +import Close from './sheet-close.svelte'; +import Overlay from './sheet-overlay.svelte'; +import Content from './sheet-content.svelte'; +import Header from './sheet-header.svelte'; +import Footer from './sheet-footer.svelte'; +import Title from './sheet-title.svelte'; +import Description from './sheet-description.svelte'; + +const Root = SheetPrimitive.Root; +const Portal = SheetPrimitive.Portal; + +export { + Root, + Close, + Trigger, + Portal, + Overlay, + Content, + Header, + Footer, + Title, + Description, + // + Root as Sheet, + Close as SheetClose, + Trigger as SheetTrigger, + Portal as SheetPortal, + Overlay as SheetOverlay, + Content as SheetContent, + Header as SheetHeader, + Footer as SheetFooter, + Title as SheetTitle, + Description as SheetDescription +}; diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/sheet/sheet-close.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/sheet/sheet-close.svelte new file mode 100644 index 0000000..b0180c0 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/sheet/sheet-close.svelte @@ -0,0 +1,7 @@ +<script lang="ts"> + import { Dialog as SheetPrimitive } from 'bits-ui'; + + let { ref = $bindable(null), ...restProps }: SheetPrimitive.CloseProps = $props(); +</script> + +<SheetPrimitive.Close bind:ref data-slot="sheet-close" {...restProps} /> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/sheet/sheet-content.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/sheet/sheet-content.svelte new file mode 100644 index 0000000..f16a0e0 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/sheet/sheet-content.svelte @@ -0,0 +1,60 @@ +<script lang="ts" module> + import { tv, type VariantProps } from 'tailwind-variants'; + export const sheetVariants = tv({ + base: 'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500', + variants: { + side: { + top: 'data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto border-b', + bottom: + 'data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 h-auto border-t', + left: 'data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm', + right: + 'data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm' + } + }, + defaultVariants: { + side: 'right' + } + }); + + export type Side = VariantProps<typeof sheetVariants>['side']; +</script> + +<script lang="ts"> + import { Dialog as SheetPrimitive } from 'bits-ui'; + import XIcon from '@lucide/svelte/icons/x'; + import type { Snippet } from 'svelte'; + import SheetOverlay from './sheet-overlay.svelte'; + import { cn, type WithoutChildrenOrChild } from '$lib/components/ui/utils.js'; + + let { + ref = $bindable(null), + class: className, + side = 'right', + portalProps, + children, + ...restProps + }: WithoutChildrenOrChild<SheetPrimitive.ContentProps> & { + portalProps?: SheetPrimitive.PortalProps; + side?: Side; + children: Snippet; + } = $props(); +</script> + +<SheetPrimitive.Portal {...portalProps}> + <SheetOverlay /> + <SheetPrimitive.Content + bind:ref + data-slot="sheet-content" + class={cn(sheetVariants({ side }), className)} + {...restProps} + > + {@render children?.()} + <SheetPrimitive.Close + class="absolute top-4 right-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:outline-hidden disabled:pointer-events-none" + > + <XIcon class="size-4" /> + <span class="sr-only">Close</span> + </SheetPrimitive.Close> + </SheetPrimitive.Content> +</SheetPrimitive.Portal> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/sheet/sheet-description.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/sheet/sheet-description.svelte new file mode 100644 index 0000000..ef4d58f --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/sheet/sheet-description.svelte @@ -0,0 +1,17 @@ +<script lang="ts"> + import { Dialog as SheetPrimitive } from 'bits-ui'; + import { cn } from '$lib/components/ui/utils.js'; + + let { + ref = $bindable(null), + class: className, + ...restProps + }: SheetPrimitive.DescriptionProps = $props(); +</script> + +<SheetPrimitive.Description + bind:ref + data-slot="sheet-description" + class={cn('text-sm text-muted-foreground', className)} + {...restProps} +/> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/sheet/sheet-footer.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/sheet/sheet-footer.svelte new file mode 100644 index 0000000..4e1b927 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/sheet/sheet-footer.svelte @@ -0,0 +1,20 @@ +<script lang="ts"> + import { cn, type WithElementRef } from '$lib/components/ui/utils.js'; + import type { HTMLAttributes } from 'svelte/elements'; + + let { + ref = $bindable(null), + class: className, + children, + ...restProps + }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props(); +</script> + +<div + bind:this={ref} + data-slot="sheet-footer" + class={cn('mt-auto flex flex-col gap-2 p-4', className)} + {...restProps} +> + {@render children?.()} +</div> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/sheet/sheet-header.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/sheet/sheet-header.svelte new file mode 100644 index 0000000..6c6c1ec --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/sheet/sheet-header.svelte @@ -0,0 +1,20 @@ +<script lang="ts"> + import type { HTMLAttributes } from 'svelte/elements'; + import { cn, type WithElementRef } from '$lib/components/ui/utils.js'; + + let { + ref = $bindable(null), + class: className, + children, + ...restProps + }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props(); +</script> + +<div + bind:this={ref} + data-slot="sheet-header" + class={cn('flex flex-col gap-1.5 p-4', className)} + {...restProps} +> + {@render children?.()} +</div> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/sheet/sheet-overlay.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/sheet/sheet-overlay.svelte new file mode 100644 index 0000000..a6a064f --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/sheet/sheet-overlay.svelte @@ -0,0 +1,20 @@ +<script lang="ts"> + import { Dialog as SheetPrimitive } from 'bits-ui'; + import { cn } from '$lib/components/ui/utils.js'; + + let { + ref = $bindable(null), + class: className, + ...restProps + }: SheetPrimitive.OverlayProps = $props(); +</script> + +<SheetPrimitive.Overlay + bind:ref + data-slot="sheet-overlay" + class={cn( + 'fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0', + className + )} + {...restProps} +/> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/sheet/sheet-title.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/sheet/sheet-title.svelte new file mode 100644 index 0000000..0efcc7a --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/sheet/sheet-title.svelte @@ -0,0 +1,17 @@ +<script lang="ts"> + import { Dialog as SheetPrimitive } from 'bits-ui'; + import { cn } from '$lib/components/ui/utils.js'; + + let { + ref = $bindable(null), + class: className, + ...restProps + }: SheetPrimitive.TitleProps = $props(); +</script> + +<SheetPrimitive.Title + bind:ref + data-slot="sheet-title" + class={cn('font-semibold text-foreground', className)} + {...restProps} +/> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/sheet/sheet-trigger.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/sheet/sheet-trigger.svelte new file mode 100644 index 0000000..d95719a --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/sheet/sheet-trigger.svelte @@ -0,0 +1,7 @@ +<script lang="ts"> + import { Dialog as SheetPrimitive } from 'bits-ui'; + + let { ref = $bindable(null), ...restProps }: SheetPrimitive.TriggerProps = $props(); +</script> + +<SheetPrimitive.Trigger bind:ref data-slot="sheet-trigger" {...restProps} /> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/constants.ts b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/constants.ts new file mode 100644 index 0000000..c7e827b --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/constants.ts @@ -0,0 +1,6 @@ +export const SIDEBAR_COOKIE_NAME = 'sidebar:state'; +export const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7; +export const SIDEBAR_WIDTH = '18rem'; +export const SIDEBAR_WIDTH_MOBILE = '18rem'; +export const SIDEBAR_WIDTH_ICON = '3rem'; +export const SIDEBAR_KEYBOARD_SHORTCUT = 'b'; diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/context.svelte.ts b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/context.svelte.ts new file mode 100644 index 0000000..6fa2aa3 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/context.svelte.ts @@ -0,0 +1,79 @@ +import { IsMobile } from '$lib/hooks/is-mobile.svelte.js'; +import { getContext, setContext } from 'svelte'; +import { SIDEBAR_KEYBOARD_SHORTCUT } from './constants.js'; + +type Getter<T> = () => T; + +export type SidebarStateProps = { + /** + * A getter function that returns the current open state of the sidebar. + * We use a getter function here to support `bind:open` on the `Sidebar.Provider` + * component. + */ + open: Getter<boolean>; + + /** + * A function that sets the open state of the sidebar. To support `bind:open`, we need + * a source of truth for changing the open state to ensure it will be synced throughout + * the sub-components and any `bind:` references. + */ + setOpen: (open: boolean) => void; +}; + +class SidebarState { + readonly props: SidebarStateProps; + open = $derived.by(() => this.props.open()); + openMobile = $state(false); + setOpen: SidebarStateProps['setOpen']; + #isMobile: IsMobile; + state = $derived.by(() => (this.open ? 'expanded' : 'collapsed')); + + constructor(props: SidebarStateProps) { + this.setOpen = props.setOpen; + this.#isMobile = new IsMobile(); + this.props = props; + } + + // Convenience getter for checking if the sidebar is mobile + // without this, we would need to use `sidebar.isMobile.current` everywhere + get isMobile() { + return this.#isMobile.current; + } + + // Event handler to apply to the `<svelte:window>` + handleShortcutKeydown = (e: KeyboardEvent) => { + if (e.key === SIDEBAR_KEYBOARD_SHORTCUT && (e.metaKey || e.ctrlKey)) { + e.preventDefault(); + this.toggle(); + } + }; + + setOpenMobile = (value: boolean) => { + this.openMobile = value; + }; + + toggle = () => { + return this.#isMobile.current ? (this.openMobile = !this.openMobile) : this.setOpen(!this.open); + }; +} + +const SYMBOL_KEY = 'scn-sidebar'; + +/** + * Instantiates a new `SidebarState` instance and sets it in the context. + * + * @param props The constructor props for the `SidebarState` class. + * @returns The `SidebarState` instance. + */ +export function setSidebar(props: SidebarStateProps): SidebarState { + return setContext(Symbol.for(SYMBOL_KEY), new SidebarState(props)); +} + +/** + * Retrieves the `SidebarState` instance from the context. This is a class instance, + * so you cannot destructure it. + * @returns The `SidebarState` instance. + */ +export function useSidebar(): SidebarState { + return getContext(Symbol.for(SYMBOL_KEY)); +} diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/index.ts b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/index.ts new file mode 100644 index 0000000..280e640 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/index.ts @@ -0,0 +1,75 @@ +import { useSidebar } from './context.svelte.js'; +import Content from './sidebar-content.svelte'; +import Footer from './sidebar-footer.svelte'; +import GroupAction from './sidebar-group-action.svelte'; +import GroupContent from './sidebar-group-content.svelte'; +import GroupLabel from './sidebar-group-label.svelte'; +import Group from './sidebar-group.svelte'; +import Header from './sidebar-header.svelte'; +import Input from './sidebar-input.svelte'; +import Inset from './sidebar-inset.svelte'; +import MenuAction from './sidebar-menu-action.svelte'; +import MenuBadge from './sidebar-menu-badge.svelte'; +import MenuButton from './sidebar-menu-button.svelte'; +import MenuItem from './sidebar-menu-item.svelte'; +import MenuSkeleton from './sidebar-menu-skeleton.svelte'; +import MenuSubButton from './sidebar-menu-sub-button.svelte'; +import MenuSubItem from './sidebar-menu-sub-item.svelte'; +import MenuSub from './sidebar-menu-sub.svelte'; +import Menu from './sidebar-menu.svelte'; +import Provider from './sidebar-provider.svelte'; +import Rail from './sidebar-rail.svelte'; +import Separator from './sidebar-separator.svelte'; +import Trigger from './sidebar-trigger.svelte'; +import Root from './sidebar.svelte'; + +export { + Content, + Footer, + Group, + GroupAction, + GroupContent, + GroupLabel, + Header, + Input, + Inset, + Menu, + MenuAction, + MenuBadge, + MenuButton, + MenuItem, + MenuSkeleton, + MenuSub, + MenuSubButton, + MenuSubItem, + Provider, + Rail, + Root, + Separator, + // + Root as Sidebar, + Content as SidebarContent, + Footer as SidebarFooter, + Group as SidebarGroup, + GroupAction as SidebarGroupAction, + GroupContent as SidebarGroupContent, + GroupLabel as SidebarGroupLabel, + Header as SidebarHeader, + Input as SidebarInput, + Inset as SidebarInset, + Menu as SidebarMenu, + MenuAction as SidebarMenuAction, + MenuBadge as SidebarMenuBadge, + MenuButton as SidebarMenuButton, + MenuItem as SidebarMenuItem, + MenuSkeleton as SidebarMenuSkeleton, + MenuSub as SidebarMenuSub, + MenuSubButton as SidebarMenuSubButton, + MenuSubItem as SidebarMenuSubItem, + Provider as SidebarProvider, + Rail as SidebarRail, + Separator as SidebarSeparator, + Trigger as SidebarTrigger, + Trigger, + useSidebar +}; diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-content.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-content.svelte new file mode 100644 index 0000000..0e5f75e --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-content.svelte @@ -0,0 +1,24 @@ +<script lang="ts"> + import type { HTMLAttributes } from 'svelte/elements'; + import { cn, type WithElementRef } from '$lib/components/ui/utils.js'; + + let { + ref = $bindable(null), + class: className, + children, + ...restProps + }: WithElementRef<HTMLAttributes<HTMLElement>> = $props(); +</script> + +<div + bind:this={ref} + data-slot="sidebar-content" + data-sidebar="content" + class={cn( + 'flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden', + className + )} + {...restProps} +> + {@render children?.()} +</div> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-footer.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-footer.svelte new file mode 100644 index 0000000..67be0a4 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-footer.svelte @@ -0,0 +1,21 @@ +<script lang="ts"> + import type { HTMLAttributes } from 'svelte/elements'; + import { cn, type WithElementRef } from '$lib/components/ui/utils.js'; + + let { + ref = $bindable(null), + class: className, + children, + ...restProps + }: WithElementRef<HTMLAttributes<HTMLElement>> = $props(); +</script> + +<div + bind:this={ref} + data-slot="sidebar-footer" + data-sidebar="footer" + class={cn('flex flex-col gap-2 p-2', className)} + {...restProps} +> + {@render children?.()} +</div> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-group-action.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-group-action.svelte new file mode 100644 index 0000000..027a711 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-group-action.svelte @@ -0,0 +1,36 @@ +<script lang="ts"> + import { cn, type WithElementRef } from '$lib/components/ui/utils.js'; + import type { Snippet } from 'svelte'; + import type { HTMLButtonAttributes } from 'svelte/elements'; + + let { + ref = $bindable(null), + class: className, + children, + child, + ...restProps + }: WithElementRef<HTMLButtonAttributes> & { + child?: Snippet<[{ props: Record<string, unknown> }]>; + } = $props(); + + const mergedProps = $derived({ + class: cn( + 'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground outline-hidden absolute right-3 top-3.5 flex aspect-square w-5 items-center justify-center rounded-md p-0 transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0', + // Increases the hit area of the button on mobile. + 'after:absolute after:-inset-2 md:after:hidden', + 'group-data-[collapsible=icon]:hidden', + className + ), + 'data-slot': 'sidebar-group-action', + 'data-sidebar': 'group-action', + ...restProps + }); +</script> + +{#if child} + {@render child({ props: mergedProps })} +{:else} + <button bind:this={ref} {...mergedProps}> + {@render children?.()} + </button> +{/if} diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-group-content.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-group-content.svelte new file mode 100644 index 0000000..9e018fb --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-group-content.svelte @@ -0,0 +1,21 @@ +<script lang="ts"> + import { cn, type WithElementRef } from '$lib/components/ui/utils.js'; + import type { HTMLAttributes } from 'svelte/elements'; + + let { + ref = $bindable(null), + class: className, + children, + ...restProps + }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props(); +</script> + +<div + bind:this={ref} + data-slot="sidebar-group-content" + data-sidebar="group-content" + class={cn('w-full text-sm', className)} + {...restProps} +> + {@render children?.()} +</div> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-group-label.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-group-label.svelte new file mode 100644 index 0000000..79f47d7 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-group-label.svelte @@ -0,0 +1,34 @@ +<script lang="ts"> + import { cn, type WithElementRef } from '$lib/components/ui/utils.js'; + import type { Snippet } from 'svelte'; + import type { HTMLAttributes } from 'svelte/elements'; + + let { + ref = $bindable(null), + children, + child, + class: className, + ...restProps + }: WithElementRef<HTMLAttributes<HTMLElement>> & { + child?: Snippet<[{ props: Record<string, unknown> }]>; + } = $props(); + + const mergedProps = $derived({ + class: cn( + 'text-sidebar-foreground/70 ring-sidebar-ring outline-hidden flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0', + 'group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0', + className + ), + 'data-slot': 'sidebar-group-label', + 'data-sidebar': 'group-label', + ...restProps + }); +</script> + +{#if child} + {@render child({ props: mergedProps })} +{:else} + <div bind:this={ref} {...mergedProps}> + {@render children?.()} + </div> +{/if} diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-group.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-group.svelte new file mode 100644 index 0000000..eed5ace --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-group.svelte @@ -0,0 +1,21 @@ +<script lang="ts"> + import type { HTMLAttributes } from 'svelte/elements'; + import { cn, type WithElementRef } from '$lib/components/ui/utils.js'; + + let { + ref = $bindable(null), + class: className, + children, + ...restProps + }: WithElementRef<HTMLAttributes<HTMLElement>> = $props(); +</script> + +<div + bind:this={ref} + data-slot="sidebar-group" + data-sidebar="group" + class={cn('relative flex w-full min-w-0 flex-col p-2', className)} + {...restProps} +> + {@render children?.()} +</div> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-header.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-header.svelte new file mode 100644 index 0000000..0651550 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-header.svelte @@ -0,0 +1,21 @@ +<script lang="ts"> + import type { HTMLAttributes } from 'svelte/elements'; + import { cn, type WithElementRef } from '$lib/components/ui/utils.js'; + + let { + ref = $bindable(null), + class: className, + children, + ...restProps + }: WithElementRef<HTMLAttributes<HTMLElement>> = $props(); +</script> + +<div + bind:this={ref} + data-slot="sidebar-header" + data-sidebar="header" + class={cn('flex flex-col gap-2 p-2', className)} + {...restProps} +> + {@render children?.()} +</div> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-input.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-input.svelte new file mode 100644 index 0000000..fa57473 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-input.svelte @@ -0,0 +1,21 @@ +<script lang="ts"> + import type { ComponentProps } from 'svelte'; + import { Input } from '$lib/components/ui/input/index.js'; + import { cn } from '$lib/components/ui/utils.js'; + + let { + ref = $bindable(null), + value = $bindable(''), + class: className, + ...restProps + }: ComponentProps<typeof Input> = $props(); +</script> + +<Input + bind:ref + bind:value + data-slot="sidebar-input" + data-sidebar="input" + class={cn('h-8 w-full bg-background shadow-none', className)} + {...restProps} +/> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-inset.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-inset.svelte new file mode 100644 index 0000000..f55d2f4 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-inset.svelte @@ -0,0 +1,24 @@ +<script lang="ts"> + import { cn, type WithElementRef } from '$lib/components/ui/utils.js'; + import type { HTMLAttributes } from 'svelte/elements'; + + let { + ref = $bindable(null), + class: className, + children, + ...restProps + }: WithElementRef<HTMLAttributes<HTMLElement>> = $props(); +</script> + +<main + bind:this={ref} + data-slot="sidebar-inset" + class={cn( + 'relative flex w-full flex-1 flex-col', + 'md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow-sm md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2', + className + )} + {...restProps} +> + {@render children?.()} +</main> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-menu-action.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-menu-action.svelte new file mode 100644 index 0000000..ded1ffd --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-menu-action.svelte @@ -0,0 +1,43 @@ +<script lang="ts"> + import { cn, type WithElementRef } from '$lib/components/ui/utils.js'; + import type { Snippet } from 'svelte'; + import type { HTMLButtonAttributes } from 'svelte/elements'; + + let { + ref = $bindable(null), + class: className, + showOnHover = false, + children, + child, + ...restProps + }: WithElementRef<HTMLButtonAttributes> & { + child?: Snippet<[{ props: Record<string, unknown> }]>; + showOnHover?: boolean; + } = $props(); + + const mergedProps = $derived({ + class: cn( + 'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground peer-hover/menu-button:text-sidebar-accent-foreground outline-hidden absolute right-1 top-1.5 flex aspect-square w-5 items-center justify-center rounded-md p-0 transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0', + // Increases the hit area of the button on mobile. + 'after:absolute after:-inset-2 md:after:hidden', + 'peer-data-[size=sm]/menu-button:top-1', + 'peer-data-[size=default]/menu-button:top-1.5', + 'peer-data-[size=lg]/menu-button:top-2.5', + 'group-data-[collapsible=icon]:hidden', + showOnHover && + 'peer-data-[active=true]/menu-button:text-sidebar-accent-foreground group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 md:opacity-0', + className + ), + 'data-slot': 'sidebar-menu-action', + 'data-sidebar': 'menu-action', + ...restProps + }); +</script> + +{#if child} + {@render child({ props: mergedProps })} +{:else} + <button bind:this={ref} {...mergedProps}> + {@render children?.()} + </button> +{/if} diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-menu-badge.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-menu-badge.svelte new file mode 100644 index 0000000..f4525a1 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-menu-badge.svelte @@ -0,0 +1,29 @@ +<script lang="ts"> + import { cn, type WithElementRef } from '$lib/components/ui/utils.js'; + import type { HTMLAttributes } from 'svelte/elements'; + + let { + ref = $bindable(null), + class: className, + children, + ...restProps + }: WithElementRef<HTMLAttributes<HTMLElement>> = $props(); +</script> + +<div + bind:this={ref} + data-slot="sidebar-menu-badge" + data-sidebar="menu-badge" + class={cn( + 'pointer-events-none absolute right-1 flex h-5 min-w-5 items-center justify-center rounded-md px-1 text-xs font-medium text-sidebar-foreground tabular-nums select-none', + 'peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[active=true]/menu-button:text-sidebar-accent-foreground', + 'peer-data-[size=sm]/menu-button:top-1', + 'peer-data-[size=default]/menu-button:top-1.5', + 'peer-data-[size=lg]/menu-button:top-2.5', + 'group-data-[collapsible=icon]:hidden', + className + )} + {...restProps} +> + {@render children?.()} +</div> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-menu-button.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-menu-button.svelte new file mode 100644 index 0000000..2ce0305 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-menu-button.svelte @@ -0,0 +1,106 @@ +<script lang="ts" module> + import { tv, type VariantProps } from 'tailwind-variants'; + + export const sidebarMenuButtonVariants = tv({ + base: 'peer/menu-button outline-hidden ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground group-has-data-[sidebar=menu-action]/menu-item:pr-8 data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm transition-[width,height,padding] focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:font-medium [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0', + variants: { + variant: { + default: 'hover:bg-sidebar-accent hover:text-sidebar-accent-foreground', + outline: + 'bg-background hover:bg-sidebar-accent hover:text-sidebar-accent-foreground shadow-[0_0_0_1px_var(--sidebar-border)] hover:shadow-[0_0_0_1px_var(--sidebar-accent)]' + }, + size: { + default: 'h-8 text-sm', + sm: 'h-7 text-xs', + lg: 'group-data-[collapsible=icon]:p-0! h-12 text-sm' + } + }, + defaultVariants: { + variant: 'default', + size: 'default' + } + }); + + export type SidebarMenuButtonVariant = VariantProps<typeof sidebarMenuButtonVariants>['variant']; + export type SidebarMenuButtonSize = VariantProps<typeof sidebarMenuButtonVariants>['size']; +</script> + +<script lang="ts"> + import * as Tooltip from '$lib/components/ui/tooltip/index.js'; + import { + cn, + type WithElementRef, + type WithoutChildrenOrChild + } from '$lib/components/ui/utils.js'; + import { mergeProps } from 'bits-ui'; + import type { ComponentProps, Snippet } from 'svelte'; + import type { HTMLAttributes } from 'svelte/elements'; + import { useSidebar } from './context.svelte.js'; + + let { + ref = $bindable(null), + class: className, + children, + child, + variant = 'default', + size = 'default', + isActive = false, + tooltipContent, + tooltipContentProps, + ...restProps + }: WithElementRef<HTMLAttributes<HTMLButtonElement>, HTMLButtonElement> & { + isActive?: boolean; + variant?: SidebarMenuButtonVariant; + size?: SidebarMenuButtonSize; + tooltipContent?: Snippet | string; + tooltipContentProps?: WithoutChildrenOrChild<ComponentProps<typeof Tooltip.Content>>; + child?: Snippet<[{ props: Record<string, unknown> }]>; + } = $props(); + + const sidebar = useSidebar(); + + const buttonProps = $derived({ + class: cn(sidebarMenuButtonVariants({ variant, size }), className), + 'data-slot': 'sidebar-menu-button', + 'data-sidebar': 'menu-button', + 'data-size': size, + 'data-active': isActive, + ...restProps + }); +</script> + +{#snippet Button({ props }: { props?: Record<string, unknown> })} + {@const mergedProps = mergeProps(buttonProps, props)} + {#if child} + {@render child({ props: mergedProps })} + {:else} + <button bind:this={ref} {...mergedProps}> + {@render children?.()} + </button> + {/if} +{/snippet} + +{#if !tooltipContent} + {@render Button({})} +{:else} + <Tooltip.Root> + <Tooltip.Trigger> + {#snippet child({ props })} + {@render Button({ props })} + {/snippet} + </Tooltip.Trigger> + + <Tooltip.Content + side="right" + align="center" + hidden={sidebar.state !== 'collapsed' || sidebar.isMobile} + {...tooltipContentProps} + > + {#if typeof tooltipContent === 'string'} + {tooltipContent} + {:else if tooltipContent} + {@render tooltipContent()} + {/if} + </Tooltip.Content> + </Tooltip.Root> +{/if} diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-menu-item.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-menu-item.svelte new file mode 100644 index 0000000..5adbedd --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-menu-item.svelte @@ -0,0 +1,21 @@ +<script lang="ts"> + import { cn, type WithElementRef } from '$lib/components/ui/utils.js'; + import type { HTMLAttributes } from 'svelte/elements'; + + let { + ref = $bindable(null), + class: className, + children, + ...restProps + }: WithElementRef<HTMLAttributes<HTMLLIElement>, HTMLLIElement> = $props(); +</script> + +<li + bind:this={ref} + data-slot="sidebar-menu-item" + data-sidebar="menu-item" + class={cn('group/menu-item relative', className)} + {...restProps} +> + {@render children?.()} +</li> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-menu-skeleton.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-menu-skeleton.svelte new file mode 100644 index 0000000..2b2acd6 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-menu-skeleton.svelte @@ -0,0 +1,36 @@ +<script lang="ts"> + import { cn, type WithElementRef } from '$lib/components/ui/utils.js'; + import { Skeleton } from '$lib/components/ui/skeleton/index.js'; + import type { HTMLAttributes } from 'svelte/elements'; + + let { + ref = $bindable(null), + class: className, + showIcon = false, + children, + ...restProps + }: WithElementRef<HTMLAttributes<HTMLElement>> & { + showIcon?: boolean; + } = $props(); + + // Random width between 50% and 90% + const width = `${Math.floor(Math.random() * 40) + 50}%`; +</script> + +<div + bind:this={ref} + data-slot="sidebar-menu-skeleton" + data-sidebar="menu-skeleton" + class={cn('flex h-8 items-center gap-2 rounded-md px-2', className)} + {...restProps} +> + {#if showIcon} + <Skeleton class="size-4 rounded-md" data-sidebar="menu-skeleton-icon" /> + {/if} + <Skeleton + class="h-4 max-w-(--skeleton-width) flex-1" + data-sidebar="menu-skeleton-text" + style="--skeleton-width: {width};" + /> + {@render children?.()} +</div> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-menu-sub-button.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-menu-sub-button.svelte new file mode 100644 index 0000000..dabfe0f --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-menu-sub-button.svelte @@ -0,0 +1,43 @@ +<script lang="ts"> + import { cn, type WithElementRef } from '$lib/components/ui/utils.js'; + import type { Snippet } from 'svelte'; + import type { HTMLAnchorAttributes } from 'svelte/elements'; + + let { + ref = $bindable(null), + children, + child, + class: className, + size = 'md', + isActive = false, + ...restProps + }: WithElementRef<HTMLAnchorAttributes> & { + child?: Snippet<[{ props: Record<string, unknown> }]>; + size?: 'sm' | 'md'; + isActive?: boolean; + } = $props(); + + const mergedProps = $derived({ + class: cn( + 'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground [&>svg]:text-sidebar-accent-foreground outline-hidden flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0', + 'data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground', + size === 'sm' && 'text-xs', + size === 'md' && 'text-sm', + 'group-data-[collapsible=icon]:hidden', + className + ), + 'data-slot': 'sidebar-menu-sub-button', + 'data-sidebar': 'menu-sub-button', + 'data-size': size, + 'data-active': isActive, + ...restProps + }); +</script> + +{#if child} + {@render child({ props: mergedProps })} +{:else} + <a bind:this={ref} {...mergedProps}> + {@render children?.()} + </a> +{/if} diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-menu-sub-item.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-menu-sub-item.svelte new file mode 100644 index 0000000..cca870e --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-menu-sub-item.svelte @@ -0,0 +1,21 @@ +<script lang="ts"> + import { cn, type WithElementRef } from '$lib/components/ui/utils.js'; + import type { HTMLAttributes } from 'svelte/elements'; + + let { + ref = $bindable(null), + children, + class: className, + ...restProps + }: WithElementRef<HTMLAttributes<HTMLLIElement>> = $props(); +</script> + +<li + bind:this={ref} + data-slot="sidebar-menu-sub-item" + data-sidebar="menu-sub-item" + class={cn('group/menu-sub-item relative', className)} + {...restProps} +> + {@render children?.()} +</li> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-menu-sub.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-menu-sub.svelte new file mode 100644 index 0000000..5458ced --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-menu-sub.svelte @@ -0,0 +1,25 @@ +<script lang="ts"> + import { cn, type WithElementRef } from '$lib/components/ui/utils.js'; + import type { HTMLAttributes } from 'svelte/elements'; + + let { + ref = $bindable(null), + class: className, + children, + ...restProps + }: WithElementRef<HTMLAttributes<HTMLUListElement>> = $props(); +</script> + +<ul + bind:this={ref} + data-slot="sidebar-menu-sub" + data-sidebar="menu-sub" + class={cn( + 'mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l border-sidebar-border px-2.5 py-0.5', + 'group-data-[collapsible=icon]:hidden', + className + )} + {...restProps} +> + {@render children?.()} +</ul> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-menu.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-menu.svelte new file mode 100644 index 0000000..fee96ed --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-menu.svelte @@ -0,0 +1,21 @@ +<script lang="ts"> + import { cn, type WithElementRef } from '$lib/components/ui/utils.js'; + import type { HTMLAttributes } from 'svelte/elements'; + + let { + ref = $bindable(null), + class: className, + children, + ...restProps + }: WithElementRef<HTMLAttributes<HTMLUListElement>, HTMLUListElement> = $props(); +</script> + +<ul + bind:this={ref} + data-slot="sidebar-menu" + data-sidebar="menu" + class={cn('flex w-full min-w-0 flex-col gap-1', className)} + {...restProps} +> + {@render children?.()} +</ul> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-provider.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-provider.svelte new file mode 100644 index 0000000..364235a --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-provider.svelte @@ -0,0 +1,50 @@ +<script lang="ts"> + import { cn, type WithElementRef } from '$lib/components/ui/utils.js'; + import type { HTMLAttributes } from 'svelte/elements'; + import { + SIDEBAR_COOKIE_MAX_AGE, + SIDEBAR_COOKIE_NAME, + SIDEBAR_WIDTH, + SIDEBAR_WIDTH_ICON + } from './constants.js'; + import { setSidebar } from './context.svelte.js'; + + let { + ref = $bindable(null), + open = $bindable(true), + onOpenChange = () => {}, + class: className, + style, + children, + ...restProps + }: WithElementRef<HTMLAttributes<HTMLDivElement>> & { + open?: boolean; + onOpenChange?: (open: boolean) => void; + } = $props(); + + const sidebar = setSidebar({ + open: () => open, + setOpen: (value: boolean) => { + open = value; + onOpenChange(value); + + // This sets the cookie to keep the sidebar state. + document.cookie = `${SIDEBAR_COOKIE_NAME}=${open}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`; + } + }); +</script> + +<svelte:window onkeydown={sidebar.handleShortcutKeydown} /> + +<div + data-slot="sidebar-wrapper" + style="--sidebar-width: {SIDEBAR_WIDTH}; --sidebar-width-icon: {SIDEBAR_WIDTH_ICON}; {style}" + class={cn( + 'group/sidebar-wrapper flex min-h-svh w-full has-data-[variant=inset]:bg-sidebar', + className + )} + bind:this={ref} + {...restProps} +> + {@render children?.()} +</div> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-rail.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-rail.svelte new file mode 100644 index 0000000..cde9307 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-rail.svelte @@ -0,0 +1,36 @@ +<script lang="ts"> + import { cn, type WithElementRef } from '$lib/components/ui/utils.js'; + import type { HTMLAttributes } from 'svelte/elements'; + import { useSidebar } from './context.svelte.js'; + + let { + ref = $bindable(null), + class: className, + children, + ...restProps + }: WithElementRef<HTMLAttributes<HTMLButtonElement>, HTMLButtonElement> = $props(); + + const sidebar = useSidebar(); +</script> + +<button + bind:this={ref} + data-sidebar="rail" + data-slot="sidebar-rail" + aria-label="Toggle Sidebar" + tabIndex={-1} + onclick={sidebar.toggle} + title="Toggle Sidebar" + class={cn( + 'absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear group-data-[side=left]:-right-4 group-data-[side=right]:left-0 after:absolute after:inset-y-0 after:left-[calc(1/2*100%-1px)] after:w-[2px] hover:after:bg-sidebar-border sm:flex', + 'in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize', + '[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize', + 'group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full hover:group-data-[collapsible=offcanvas]:bg-sidebar', + '[[data-side=left][data-collapsible=offcanvas]_&]:-right-2', + '[[data-side=right][data-collapsible=offcanvas]_&]:-left-2', + className + )} + {...restProps} +> + {@render children?.()} +</button> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-separator.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-separator.svelte new file mode 100644 index 0000000..8fc2065 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-separator.svelte @@ -0,0 +1,19 @@ +<script lang="ts"> + import { Separator } from '$lib/components/ui/separator/index.js'; + import { cn } from '$lib/components/ui/utils.js'; + import type { ComponentProps } from 'svelte'; + + let { + ref = $bindable(null), + class: className, + ...restProps + }: ComponentProps<typeof Separator> = $props(); +</script> + +<Separator + bind:ref + data-slot="sidebar-separator" + data-sidebar="separator" + class={cn('bg-sidebar-border', className)} + {...restProps} +/> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-trigger.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-trigger.svelte new file mode 100644 index 0000000..29d3a9c --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar-trigger.svelte @@ -0,0 +1,35 @@ +<script lang="ts"> + import { Button } from '$lib/components/ui/button/index.js'; + import { cn } from '$lib/components/ui/utils.js'; + import PanelLeftIcon from '@lucide/svelte/icons/panel-left'; + import type { ComponentProps } from 'svelte'; + import { useSidebar } from './context.svelte.js'; + + let { + ref = $bindable(null), + class: className, + onclick, + ...restProps + }: ComponentProps<typeof Button> & { + onclick?: (e: MouseEvent) => void; + } = $props(); + + const sidebar = useSidebar(); +</script> + +<Button + data-sidebar="trigger" + data-slot="sidebar-trigger" + variant="ghost" + size="icon" + class={cn('size-7', className)} + type="button" + onclick={(e) => { + onclick?.(e); + sidebar.toggle(); + }} + {...restProps} +> + <PanelLeftIcon /> + <span class="sr-only">Toggle Sidebar</span> +</Button> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar.svelte new file mode 100644 index 0000000..e2c4a75 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/sidebar/sidebar.svelte @@ -0,0 +1,101 @@ +<script lang="ts"> + import * as Sheet from '$lib/components/ui/sheet/index.js'; + import { cn, type WithElementRef } from '$lib/components/ui/utils.js'; + import type { HTMLAttributes } from 'svelte/elements'; + import { SIDEBAR_WIDTH_MOBILE } from './constants.js'; + import { useSidebar } from './context.svelte.js'; + + let { + ref = $bindable(null), + side = 'left', + variant = 'sidebar', + collapsible = 'offcanvas', + class: className, + children, + ...restProps + }: WithElementRef<HTMLAttributes<HTMLDivElement>> & { + side?: 'left' | 'right'; + variant?: 'sidebar' | 'floating' | 'inset'; + collapsible?: 'offcanvas' | 'icon' | 'none'; + } = $props(); + + const sidebar = useSidebar(); +</script> + +{#if collapsible === 'none'} + <div + class={cn( + 'flex h-full w-(--sidebar-width) flex-col bg-sidebar text-sidebar-foreground', + className + )} + bind:this={ref} + {...restProps} + > + {@render children?.()} + </div> +{:else if sidebar.isMobile} + <Sheet.Root bind:open={() => sidebar.openMobile, (v) => sidebar.setOpenMobile(v)} {...restProps}> + <Sheet.Content + data-sidebar="sidebar" + data-slot="sidebar" + data-mobile="true" + class="z-99999 w-(--sidebar-width) bg-sidebar p-0 text-sidebar-foreground sm:z-99 [&>button]:hidden" + style="--sidebar-width: {SIDEBAR_WIDTH_MOBILE};" + {side} + > + <Sheet.Header class="sr-only"> + <Sheet.Title>Sidebar</Sheet.Title> + <Sheet.Description>Displays the mobile sidebar.</Sheet.Description> + </Sheet.Header> + <div class="flex h-full w-full flex-col"> + {@render children?.()} + </div> + </Sheet.Content> + </Sheet.Root> +{:else} + <div + bind:this={ref} + class="group peer hidden text-sidebar-foreground md:block" + data-state={sidebar.state} + data-collapsible={sidebar.state === 'collapsed' ? collapsible : ''} + data-variant={variant} + data-side={side} + data-slot="sidebar" + > + <!-- This is what handles the sidebar gap on desktop --> + <div + data-slot="sidebar-gap" + class={cn( + 'relative w-(--sidebar-width) bg-transparent transition-[width] duration-200 ease-linear', + 'group-data-[collapsible=offcanvas]:w-0', + 'group-data-[side=right]:rotate-180', + variant === 'floating' || variant === 'inset' + ? 'group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]' + : 'group-data-[collapsible=icon]:w-(--sidebar-width-icon)' + )} + ></div> + <div + data-slot="sidebar-container" + class={cn( + 'fixed inset-y-0 z-999 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear md:z-0 md:flex', + side === 'left' + ? 'left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]' + : 'right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]', + // Adjust the padding for floating and inset variants. + variant === 'floating' || variant === 'inset' + ? 'p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]' + : 'group-data-[collapsible=icon]:w-(--sidebar-width-icon)', + className + )} + {...restProps} + > + <div + data-sidebar="sidebar" + data-slot="sidebar-inner" + class="flex h-full w-full flex-col bg-sidebar group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:border-sidebar-border group-data-[variant=floating]:shadow-sm" + > + {@render children?.()} + </div> + </div> + </div> +{/if} diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/skeleton/index.ts b/llama.cpp/tools/server/webui/src/lib/components/ui/skeleton/index.ts new file mode 100644 index 0000000..3120ce1 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/skeleton/index.ts @@ -0,0 +1,7 @@ +import Root from './skeleton.svelte'; + +export { + Root, + // + Root as Skeleton +}; diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/skeleton/skeleton.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/skeleton/skeleton.svelte new file mode 100644 index 0000000..62b6f80 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/skeleton/skeleton.svelte @@ -0,0 +1,17 @@ +<script lang="ts"> + import { cn, type WithElementRef, type WithoutChildren } from '$lib/components/ui/utils.js'; + import type { HTMLAttributes } from 'svelte/elements'; + + let { + ref = $bindable(null), + class: className, + ...restProps + }: WithoutChildren<WithElementRef<HTMLAttributes<HTMLDivElement>>> = $props(); +</script> + +<div + bind:this={ref} + data-slot="skeleton" + class={cn('animate-pulse rounded-md bg-accent', className)} + {...restProps} +></div> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/switch/index.ts b/llama.cpp/tools/server/webui/src/lib/components/ui/switch/index.ts new file mode 100644 index 0000000..129f8f5 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/switch/index.ts @@ -0,0 +1,7 @@ +import Root from './switch.svelte'; + +export { + Root, + // + Root as Switch +}; diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/switch/switch.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/switch/switch.svelte new file mode 100644 index 0000000..5a5975e --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/switch/switch.svelte @@ -0,0 +1,29 @@ +<script lang="ts"> + import { Switch as SwitchPrimitive } from 'bits-ui'; + import { cn, type WithoutChildrenOrChild } from '$lib/components/ui/utils.js'; + + let { + ref = $bindable(null), + class: className, + checked = $bindable(false), + ...restProps + }: WithoutChildrenOrChild<SwitchPrimitive.RootProps> = $props(); +</script> + +<SwitchPrimitive.Root + bind:ref + bind:checked + data-slot="switch" + class={cn( + 'peer inline-flex h-[1.15rem] w-8 shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input dark:data-[state=unchecked]:bg-input/80', + className + )} + {...restProps} +> + <SwitchPrimitive.Thumb + data-slot="switch-thumb" + class={cn( + 'pointer-events-none block size-4 rounded-full bg-background ring-0 transition-transform data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0 dark:data-[state=checked]:bg-primary-foreground dark:data-[state=unchecked]:bg-foreground' + )} + /> +</SwitchPrimitive.Root> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/table/index.ts b/llama.cpp/tools/server/webui/src/lib/components/ui/table/index.ts new file mode 100644 index 0000000..99239ae --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/table/index.ts @@ -0,0 +1,28 @@ +import Root from './table.svelte'; +import Body from './table-body.svelte'; +import Caption from './table-caption.svelte'; +import Cell from './table-cell.svelte'; +import Footer from './table-footer.svelte'; +import Head from './table-head.svelte'; +import Header from './table-header.svelte'; +import Row from './table-row.svelte'; + +export { + Root, + Body, + Caption, + Cell, + Footer, + Head, + Header, + Row, + // + Root as Table, + Body as TableBody, + Caption as TableCaption, + Cell as TableCell, + Footer as TableFooter, + Head as TableHead, + Header as TableHeader, + Row as TableRow +}; diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/table/table-body.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/table/table-body.svelte new file mode 100644 index 0000000..f8df65c --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/table/table-body.svelte @@ -0,0 +1,20 @@ +<script lang="ts"> + import { cn, type WithElementRef } from '$lib/components/ui/utils.js'; + import type { HTMLAttributes } from 'svelte/elements'; + + let { + ref = $bindable(null), + class: className, + children, + ...restProps + }: WithElementRef<HTMLAttributes<HTMLTableSectionElement>> = $props(); +</script> + +<tbody + bind:this={ref} + data-slot="table-body" + class={cn('[&_tr:last-child]:border-0', className)} + {...restProps} +> + {@render children?.()} +</tbody> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/table/table-caption.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/table/table-caption.svelte new file mode 100644 index 0000000..0fdcc64 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/table/table-caption.svelte @@ -0,0 +1,20 @@ +<script lang="ts"> + import { cn, type WithElementRef } from '$lib/components/ui/utils.js'; + import type { HTMLAttributes } from 'svelte/elements'; + + let { + ref = $bindable(null), + class: className, + children, + ...restProps + }: WithElementRef<HTMLAttributes<HTMLElement>> = $props(); +</script> + +<caption + bind:this={ref} + data-slot="table-caption" + class={cn('mt-4 text-sm text-muted-foreground', className)} + {...restProps} +> + {@render children?.()} +</caption> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/table/table-cell.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/table/table-cell.svelte new file mode 100644 index 0000000..4506fdf --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/table/table-cell.svelte @@ -0,0 +1,23 @@ +<script lang="ts"> + import { cn, type WithElementRef } from '$lib/components/ui/utils.js'; + import type { HTMLTdAttributes } from 'svelte/elements'; + + let { + ref = $bindable(null), + class: className, + children, + ...restProps + }: WithElementRef<HTMLTdAttributes> = $props(); +</script> + +<td + bind:this={ref} + data-slot="table-cell" + class={cn( + 'bg-clip-padding p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pe-0', + className + )} + {...restProps} +> + {@render children?.()} +</td> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/table/table-footer.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/table/table-footer.svelte new file mode 100644 index 0000000..77e4a64 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/table/table-footer.svelte @@ -0,0 +1,20 @@ +<script lang="ts"> + import { cn, type WithElementRef } from '$lib/components/ui/utils.js'; + import type { HTMLAttributes } from 'svelte/elements'; + + let { + ref = $bindable(null), + class: className, + children, + ...restProps + }: WithElementRef<HTMLAttributes<HTMLTableSectionElement>> = $props(); +</script> + +<tfoot + bind:this={ref} + data-slot="table-footer" + class={cn('border-t bg-muted/50 font-medium [&>tr]:last:border-b-0', className)} + {...restProps} +> + {@render children?.()} +</tfoot> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/table/table-head.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/table/table-head.svelte new file mode 100644 index 0000000..c1c57ad --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/table/table-head.svelte @@ -0,0 +1,23 @@ +<script lang="ts"> + import { cn, type WithElementRef } from '$lib/components/ui/utils.js'; + import type { HTMLThAttributes } from 'svelte/elements'; + + let { + ref = $bindable(null), + class: className, + children, + ...restProps + }: WithElementRef<HTMLThAttributes> = $props(); +</script> + +<th + bind:this={ref} + data-slot="table-head" + class={cn( + 'h-10 bg-clip-padding px-2 text-left align-middle font-medium whitespace-nowrap text-foreground [&:has([role=checkbox])]:pe-0', + className + )} + {...restProps} +> + {@render children?.()} +</th> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/table/table-header.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/table/table-header.svelte new file mode 100644 index 0000000..eb36673 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/table/table-header.svelte @@ -0,0 +1,20 @@ +<script lang="ts"> + import { cn, type WithElementRef } from '$lib/components/ui/utils.js'; + import type { HTMLAttributes } from 'svelte/elements'; + + let { + ref = $bindable(null), + class: className, + children, + ...restProps + }: WithElementRef<HTMLAttributes<HTMLTableSectionElement>> = $props(); +</script> + +<thead + bind:this={ref} + data-slot="table-header" + class={cn('[&_tr]:border-b', className)} + {...restProps} +> + {@render children?.()} +</thead> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/table/table-row.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/table/table-row.svelte new file mode 100644 index 0000000..4131d36 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/table/table-row.svelte @@ -0,0 +1,23 @@ +<script lang="ts"> + import { cn, type WithElementRef } from '$lib/components/ui/utils.js'; + import type { HTMLAttributes } from 'svelte/elements'; + + let { + ref = $bindable(null), + class: className, + children, + ...restProps + }: WithElementRef<HTMLAttributes<HTMLTableRowElement>> = $props(); +</script> + +<tr + bind:this={ref} + data-slot="table-row" + class={cn( + 'border-b transition-colors data-[state=selected]:bg-muted hover:[&,&>svelte-css-wrapper]:[&>th,td]:bg-muted/50', + className + )} + {...restProps} +> + {@render children?.()} +</tr> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/table/table.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/table/table.svelte new file mode 100644 index 0000000..c11a6a6 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/table/table.svelte @@ -0,0 +1,22 @@ +<script lang="ts"> + import type { HTMLTableAttributes } from 'svelte/elements'; + import { cn, type WithElementRef } from '$lib/components/ui/utils.js'; + + let { + ref = $bindable(null), + class: className, + children, + ...restProps + }: WithElementRef<HTMLTableAttributes> = $props(); +</script> + +<div data-slot="table-container" class="relative w-full overflow-x-auto"> + <table + bind:this={ref} + data-slot="table" + class={cn('w-full caption-bottom text-sm', className)} + {...restProps} + > + {@render children?.()} + </table> +</div> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/textarea/index.ts b/llama.cpp/tools/server/webui/src/lib/components/ui/textarea/index.ts new file mode 100644 index 0000000..9ccb3bf --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/textarea/index.ts @@ -0,0 +1,7 @@ +import Root from './textarea.svelte'; + +export { + Root, + // + Root as Textarea +}; diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/textarea/textarea.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/textarea/textarea.svelte new file mode 100644 index 0000000..bf83882 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/textarea/textarea.svelte @@ -0,0 +1,22 @@ +<script lang="ts"> + import { cn, type WithElementRef, type WithoutChildren } from '$lib/components/ui/utils'; + import type { HTMLTextareaAttributes } from 'svelte/elements'; + + let { + ref = $bindable(null), + value = $bindable(), + class: className, + ...restProps + }: WithoutChildren<WithElementRef<HTMLTextareaAttributes>> = $props(); +</script> + +<textarea + bind:this={ref} + data-slot="textarea" + class={cn( + 'flex field-sizing-content min-h-16 w-full rounded-md border border-input bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:aria-invalid:ring-destructive/40', + className + )} + bind:value + {...restProps} +></textarea> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/tooltip/index.ts b/llama.cpp/tools/server/webui/src/lib/components/ui/tooltip/index.ts new file mode 100644 index 0000000..273d831 --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/tooltip/index.ts @@ -0,0 +1,21 @@ +import { Tooltip as TooltipPrimitive } from 'bits-ui'; +import Trigger from './tooltip-trigger.svelte'; +import Content from './tooltip-content.svelte'; + +const Root = TooltipPrimitive.Root; +const Provider = TooltipPrimitive.Provider; +const Portal = TooltipPrimitive.Portal; + +export { + Root, + Trigger, + Content, + Provider, + Portal, + // + Root as Tooltip, + Content as TooltipContent, + Trigger as TooltipTrigger, + Provider as TooltipProvider, + Portal as TooltipPortal +}; diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/tooltip/tooltip-content.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/tooltip/tooltip-content.svelte new file mode 100644 index 0000000..72ea93a --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/tooltip/tooltip-content.svelte @@ -0,0 +1,47 @@ +<script lang="ts"> + import { Tooltip as TooltipPrimitive } from 'bits-ui'; + import { cn } from '$lib/components/ui/utils.js'; + + let { + ref = $bindable(null), + class: className, + sideOffset = 0, + side = 'top', + children, + arrowClasses, + ...restProps + }: TooltipPrimitive.ContentProps & { + arrowClasses?: string; + } = $props(); +</script> + +<TooltipPrimitive.Portal> + <TooltipPrimitive.Content + bind:ref + data-slot="tooltip-content" + {sideOffset} + {side} + class={cn( + 'z-50 w-fit origin-(--bits-tooltip-content-transform-origin) animate-in rounded-md bg-primary px-3 py-1.5 text-xs text-balance text-primary-foreground fade-in-0 zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95', + className + )} + {...restProps} + > + {@render children?.()} + <TooltipPrimitive.Arrow> + {#snippet child({ props })} + <div + class={cn( + 'z-50 size-2.5 rotate-45 rounded-[2px] bg-primary', + 'data-[side=top]:translate-x-1/2 data-[side=top]:translate-y-[calc(-50%_+_2px)]', + 'data-[side=bottom]:-translate-x-1/2 data-[side=bottom]:-translate-y-[calc(-50%_+_1px)]', + 'data-[side=right]:translate-x-[calc(50%_+_2px)] data-[side=right]:translate-y-1/2', + 'data-[side=left]:-translate-y-[calc(50%_-_3px)]', + arrowClasses + )} + {...props} + ></div> + {/snippet} + </TooltipPrimitive.Arrow> + </TooltipPrimitive.Content> +</TooltipPrimitive.Portal> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/tooltip/tooltip-trigger.svelte b/llama.cpp/tools/server/webui/src/lib/components/ui/tooltip/tooltip-trigger.svelte new file mode 100644 index 0000000..5631d1b --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/tooltip/tooltip-trigger.svelte @@ -0,0 +1,7 @@ +<script lang="ts"> + import { Tooltip as TooltipPrimitive } from 'bits-ui'; + + let { ref = $bindable(null), ...restProps }: TooltipPrimitive.TriggerProps = $props(); +</script> + +<TooltipPrimitive.Trigger bind:ref data-slot="tooltip-trigger" {...restProps} /> diff --git a/llama.cpp/tools/server/webui/src/lib/components/ui/utils.ts b/llama.cpp/tools/server/webui/src/lib/components/ui/utils.ts new file mode 100644 index 0000000..f92bfcb --- /dev/null +++ b/llama.cpp/tools/server/webui/src/lib/components/ui/utils.ts @@ -0,0 +1,13 @@ +import { clsx, type ClassValue } from 'clsx'; +import { twMerge } from 'tailwind-merge'; + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type WithoutChild<T> = T extends { child?: any } ? Omit<T, 'child'> : T; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type WithoutChildren<T> = T extends { children?: any } ? Omit<T, 'children'> : T; +export type WithoutChildrenOrChild<T> = WithoutChildren<WithoutChild<T>>; +export type WithElementRef<T, U extends HTMLElement = HTMLElement> = T & { ref?: U | null }; |
