feat: widgets and ai chat

This commit is contained in:
Rotimi Best
2026-04-27 11:40:38 +01:00
parent ae1801f85f
commit 5717dd8004
391 changed files with 28177 additions and 22467 deletions
+10 -1
View File
@@ -40,11 +40,13 @@
export type ButtonVariant = VariantProps<typeof buttonVariants>['variant'];
export type ButtonSize = VariantProps<typeof buttonVariants>['size'];
export type ButtonAnimate = 'border' | 'bg-shine';
export type ButtonPropsWithoutHTML = WithChildren<{
ref?: HTMLElement | null;
variant?: ButtonVariant;
size?: ButtonSize;
animate?: ButtonAnimate;
loading?: boolean;
onClickPromise?: (
e: MouseEvent & {
@@ -78,6 +80,7 @@
ref = $bindable(null),
variant = 'default',
size = 'default',
animate = undefined,
href = undefined,
type = 'button',
loading = false,
@@ -89,6 +92,12 @@
children,
...rest
}: ButtonProps = $props();
const animateClasses: Record<ButtonAnimate, string> = {
border: '',
'bg-shine':
'ui:animate-shine ui:border ui:border-neutral-800 ui:bg-[linear-gradient(110deg,#000103,45%,#1e2631,55%,#000103)] ui:bg-[length:200%_100%] ui:text-white'
};
</script>
<!-- This approach to disabled links is inspired by bits-ui see: https://github.com/huntabyte/bits-ui/pull/1055 -->
@@ -102,7 +111,7 @@
aria-disabled={href ? disabled : undefined}
role={href && disabled ? 'link' : undefined}
tabindex={href && disabled ? -1 : tabindex}
class={cn(buttonVariants({ variant, size }), className)}
class={cn(buttonVariants({ variant, size }), animate && animateClasses[animate], className)}
bind:this={ref}
onclick={async (
// eslint-disable-next-line @typescript-eslint/no-explicit-any
+2
View File
@@ -6,6 +6,7 @@ import Root, {
type ButtonProps,
type ButtonSize,
type ButtonVariant,
type ButtonAnimate,
type AnchorElementProps,
type ButtonElementProps,
type ButtonPropsWithoutHTML,
@@ -21,6 +22,7 @@ export {
type ButtonProps,
type ButtonSize,
type ButtonVariant,
type ButtonAnimate,
type AnchorElementProps,
type ButtonElementProps,
type ButtonPropsWithoutHTML
@@ -1,7 +1,7 @@
<script lang="ts">
import { Button } from '../button';
import { UseClipboard } from '../../use-clipboard.svelte';
import { cn } from '../../utils.js';
import { cn } from '../../utils';
import CheckIcon from '@lucide/svelte/icons/check';
import CopyIcon from '@lucide/svelte/icons/copy';
import XIcon from '@lucide/svelte/icons/x';
@@ -1,7 +1,7 @@
<script lang="ts">
import { Dialog as DialogPrimitive } from 'bits-ui';
import type { Snippet } from 'svelte';
import * as Dialog from './index.js';
import * as Dialog from './index';
import { cn, type WithoutChildrenOrChild } from '../../tools';
import CrossIcon from '@lucide/svelte/icons/x';
@@ -25,7 +25,7 @@
bind:ref
data-slot="dialog-content"
class={cn(
'ui:bg-background ui:data-[state=open]:animate-in ui:data-[state=closed]:animate-out ui:data-[state=closed]:fade-out-0 ui:data-[state=open]:fade-in-0 ui:data-[state=closed]:zoom-out-95 ui:data-[state=open]:zoom-in-95 ui:fixed ui:left-[50%] ui:top-[50%] ui:z-200 ui:grid ui:w-full ui:max-w-[calc(100%-2rem)] ui:translate-x-[-50%] ui:translate-y-[-50%] ui:gap-4 ui:rounded-lg ui:p-6 ui:shadow-lg ui:duration-200 ui:sm:max-w-lg',
'ui:bg-background ui:border ui:data-[state=open]:animate-in ui:data-[state=closed]:animate-out ui:data-[state=closed]:fade-out-0 ui:data-[state=open]:fade-in-0 ui:data-[state=closed]:zoom-out-95 ui:data-[state=open]:zoom-in-95 ui:fixed ui:left-[50%] ui:top-[50%] ui:z-200 ui:grid ui:w-full ui:max-w-[calc(100%-2rem)] ui:translate-x-[-50%] ui:translate-y-[-50%] ui:gap-4 ui:rounded-lg ui:p-6 ui:shadow-lg ui:duration-200 ui:sm:max-w-lg',
className
)}
{...restProps}
@@ -13,6 +13,7 @@
'ui:group/field-label ui:peer/field-label ui:flex ui:w-fit ui:gap-2 ui:leading-snug ui:group-data-[disabled=true]/field:opacity-50',
'ui:has-[>[data-slot=field]]:w-full ui:has-[>[data-slot=field]]:flex-col ui:has-[>[data-slot=field]]:rounded-md ui:has-[>[data-slot=field]]:border ui:*:data-[slot=field]:p-4',
'ui:has-data-[state=checked]:bg-primary/5 ui:has-data-[state=checked]:border-primary ui:dark:has-data-[state=checked]:bg-primary/10',
'ui:hover:bg-primary/5 ui:cursor-pointer ui:transition-colors ui:transition-duration-200',
className
)}
{...restProps}
@@ -1,6 +1,6 @@
import { IsMobile } from '../../hooks/is-mobile.svelte';
import { getContext, setContext } from 'svelte';
import { SIDEBAR_KEYBOARD_SHORTCUT } from './constants.js';
import { SIDEBAR_KEYBOARD_SHORTCUT } from './constants';
type Getter<T> = () => T;
@@ -16,7 +16,7 @@
const mergedProps = $derived({
class: cn(
'ui:text-sidebar-foreground/70 ui:ring-sidebar-ring ui:outline-hidden ui:flex ui:h-8 ui:shrink-0 ui:items-center ui:rounded-md ui:px-2 ui:text-xs ui:font-medium ui:transition-[margin,opacity] ui:duration-200 ui:ease-linear ui:focus-visible:ring-2 ui:[&>svg]:size-4 ui:[&>svg]:shrink-0',
'ui:group-data-[collapsible=icon]:-mt-8 ui:group-data-[collapsible=icon]:opacity-0',
'ui:group-data-[collapsible=icon]:-mt-8 ui:group-data-[collapsible=icon]:opacity-0 ui:group-data-[collapsible=icon]:pointer-events-none',
className
),
'data-slot': 'sidebar-group-label',
@@ -2,7 +2,7 @@
import * as Tooltip from '../tooltip';
import { cn, type WithElementRef } from '../../tools';
import type { HTMLAttributes } from 'svelte/elements';
import { SIDEBAR_COOKIE_MAX_AGE, SIDEBAR_COOKIE_NAME, SIDEBAR_WIDTH, SIDEBAR_WIDTH_ICON } from './constants.js';
import { SIDEBAR_COOKIE_MAX_AGE, SIDEBAR_COOKIE_NAME, SIDEBAR_WIDTH, SIDEBAR_WIDTH_ICON } from './constants';
import { setSidebar } from './context.svelte';
let {
@@ -6,6 +6,8 @@
let {
ref = $bindable(null),
class: className,
onclick,
onpointerdown,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLButtonElement>, HTMLButtonElement> = $props();
@@ -19,7 +21,15 @@
data-slot="sidebar-rail"
aria-label="Toggle Sidebar"
tabIndex={-1}
onclick={sidebar.toggle}
onclick={(event) => {
if (onclick) {
onclick(event);
return;
}
sidebar.toggle();
}}
{onpointerdown}
title="Toggle Sidebar"
class={cn(
'ui:hover:after:bg-sidebar-border ui:absolute ui:inset-y-0 ui:z-20 ui:hidden ui:w-4 ui:-translate-x-1/2 ui:transition-all ui:ease-linear ui:after:absolute ui:after:inset-y-0 ui:after:left-[calc(1/2*100%-1px)] ui:after:w-[2px] ui:sm:flex ui:group-data-[side=left]:-right-4 ui:group-data-[side=right]:left-0',
+2 -2
View File
@@ -2,7 +2,7 @@
import * as Sheet from '../sheet';
import { cn, type WithElementRef } from '../../../src/tools';
import type { HTMLAttributes } from 'svelte/elements';
import { SIDEBAR_WIDTH_MOBILE } from './constants.js';
import { SIDEBAR_WIDTH_MOBILE } from './constants';
import { useSidebar } from './context.svelte';
let {
@@ -77,7 +77,7 @@
<div
data-slot="sidebar-container"
class={cn(
'ui:w-(--sidebar-width) ui:fixed ui:inset-y-0 ui:z-10 ui:hidden ui:max-h-svh ui:transition-[left,right,width] ui:duration-200 ui:ease-linear ui:md:flex',
'ui:w-(--sidebar-width) ui:fixed ui:inset-y-0 ui:z-100 ui:hidden ui:max-h-svh ui:transition-[left,right,width] ui:duration-200 ui:ease-linear ui:md:flex',
side === 'left'
? 'ui:left-0 ui:group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]'
: 'ui:right-0 ui:group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]',