feat: remove react-aria-components

This commit is contained in:
Aarnav Tale 2025-02-04 11:42:57 -05:00
parent 0a14533756
commit d1f6c450c0
No known key found for this signature in database
13 changed files with 325 additions and 485 deletions

View File

@ -0,0 +1,26 @@
import { useProgressBar } from 'react-aria';
import cn from '~/utils/cn';
export interface ProgressBarProps {
isVisible: boolean;
}
export default function ProgressBar(props: ProgressBarProps) {
const { isVisible } = props;
const { progressBarProps } = useProgressBar({
label: 'Loading...',
isIndeterminate: true,
});
return (
<div
{...progressBarProps}
aria-hidden={!isVisible}
className={cn(
'fixed top-0 left-0 z-50 w-1/2 h-1 opacity-0',
'bg-headplane-950 dark:bg-headplane-50',
isVisible && 'animate-loading opacity-100',
)}
/>
);
}

View File

@ -6,9 +6,8 @@ function TableList(props: HTMLProps<HTMLDivElement>) {
<div <div
{...props} {...props}
className={clsx( className={clsx(
'border border-gray-300 rounded-lg overflow-clip', 'rounded-xl',
'dark:border-zinc-700 dark:text-gray-300', 'border border-headplane-100 dark:border-headplane-800',
// 'dark:bg-zinc-800',
props.className, props.className,
)} )}
> >
@ -22,9 +21,8 @@ function Item(props: HTMLProps<HTMLDivElement>) {
<div <div
{...props} {...props}
className={clsx( className={clsx(
'flex items-center justify-between px-3 py-2', 'flex items-center justify-between p-2 last:border-b-0',
'border-b border-gray-200 last:border-b-0', 'border-b border-headplane-100 dark:border-headplane-800',
'dark:border-zinc-800',
props.className, props.className,
)} )}
> >

90
app/components/Tabs.tsx Normal file
View File

@ -0,0 +1,90 @@
import { useRef } from 'react';
import {
AriaTabListProps,
AriaTabPanelProps,
useTab,
useTabList,
useTabPanel,
} from 'react-aria';
import { Item, Node, TabListState, useTabListState } from 'react-stately';
import cn from '~/utils/cn';
export interface TabsProps extends AriaTabListProps<object> {
label: string;
className?: string;
}
function Tabs({ label, className, ...props }: TabsProps) {
const state = useTabListState(props);
const ref = useRef<HTMLDivElement | null>(null);
const { tabListProps } = useTabList(props, state, ref);
return (
<div className={cn('flex flex-col', className)}>
<div
{...tabListProps}
ref={ref}
className={cn(
'flex items-center rounded-t-xl w-fit',
'border-headplane-100 dark:border-headplane-800',
'border-t border-x',
)}
>
{[...state.collection].map((item) => (
<Tab key={item.key} item={item} state={state} />
))}
</div>
<TabsPanel key={state.selectedItem?.key} state={state} />
</div>
);
}
export interface TabsTabProps {
item: Node<object>;
state: TabListState<object>;
}
function Tab({ item, state }: TabsTabProps) {
const { key, rendered } = item;
const ref = useRef<HTMLDivElement | null>(null);
const { tabProps } = useTab({ key }, state, ref);
return (
<div
{...tabProps}
ref={ref}
className={cn(
'pl-2 pr-3 py-2.5',
'aria-selected:bg-headplane-100 dark:aria-selected:bg-headplane-950',
'focus:outline-none focus:ring z-10',
'border-r border-headplane-100 dark:border-headplane-800',
'first:rounded-tl-xl last:rounded-tr-xl last:border-r-0',
)}
>
{rendered}
</div>
);
}
export interface TabsPanelProps extends AriaTabPanelProps {
state: TabListState<object>;
}
function TabsPanel({ state, ...props }: TabsPanelProps) {
const ref = useRef<HTMLDivElement | null>(null);
const { tabPanelProps } = useTabPanel(props, state, ref);
return (
<div
{...tabPanelProps}
ref={ref}
className={cn(
'w-full overflow-clip rounded-b-xl rounded-r-xl',
'border border-headplane-100 dark:border-headplane-800',
)}
>
{state.selectedItem?.props.children}
</div>
);
}
export default Object.assign(Tabs, { Item });

View File

@ -8,12 +8,10 @@ import {
useNavigation, useNavigation,
} from 'react-router'; } from 'react-router';
import '@fontsource-variable/inter'; import '@fontsource-variable/inter';
import { ProgressBar } from 'react-aria-components';
import { ErrorPopup } from '~/components/Error'; import { ErrorPopup } from '~/components/Error';
import ProgressBar from '~/components/ProgressBar';
import ToastProvider from '~/components/ToastProvider'; import ToastProvider from '~/components/ToastProvider';
import stylesheet from '~/tailwind.css?url'; import stylesheet from '~/tailwind.css?url';
import cn from '~/utils/cn';
import { useToastQueue } from '~/utils/toast'; import { useToastQueue } from '~/utils/toast';
export const meta: MetaFunction = () => [ export const meta: MetaFunction = () => [
@ -58,15 +56,7 @@ export default function App() {
return ( return (
<> <>
<ProgressBar aria-label="Loading..."> <ProgressBar isVisible={nav.state === 'loading'} />
<div
className={cn(
'fixed top-0 left-0 z-50 w-1/2 h-1',
'bg-blue-500 dark:bg-blue-400 opacity-0',
nav.state === 'loading' && 'animate-loading opacity-100',
)}
/>
</ProgressBar>
<Outlet /> <Outlet />
</> </>
); );

View File

@ -1,13 +1,11 @@
import * as shopify from '@shopify/lang-jsonc'; import * as shopify from '@shopify/lang-jsonc';
import { githubDark, githubLight } from '@uiw/codemirror-theme-github'; import { xcodeDark, xcodeLight } from '@uiw/codemirror-theme-xcode';
import CodeMirror from '@uiw/react-codemirror'; import CodeMirror from '@uiw/react-codemirror';
import React, { useEffect } from 'react'; import { BookCopy, CircleX } from 'lucide-react';
import { useState } from 'react'; import { useEffect, useState } from 'react';
import Merge from 'react-codemirror-merge'; import Merge from 'react-codemirror-merge';
import { ErrorBoundary } from 'react-error-boundary'; import { ErrorBoundary } from 'react-error-boundary';
import { ClientOnly } from 'remix-utils/client-only'; import { ClientOnly } from 'remix-utils/client-only';
import cn from '~/utils/cn';
import Fallback from './fallback'; import Fallback from './fallback';
interface EditorProps { interface EditorProps {
@ -27,39 +25,28 @@ export function Editor(props: EditorProps) {
}); });
return ( return (
<div <div className="overflow-y-scroll h-editor text-sm">
className={cn( <ErrorBoundary
'border border-gray-200 dark:border-gray-700', fallback={
'rounded-b-lg rounded-tr-lg mb-2 z-10 overflow-x-hidden', <div className="flex flex-col items-center gap-2.5 py-8">
)} <CircleX />
> <p className="text-lg font-semibold">Failed to load the editor.</p>
<div className="overflow-y-scroll h-editor text-sm"> </div>
<ErrorBoundary }
fallback={ >
<p <ClientOnly fallback={<Fallback acl={props.value} />}>
className={cn( {() => (
'w-full h-full flex items-center justify-center', <CodeMirror
'text-gray-400 dark:text-gray-500 text-xl', value={props.value}
)} height="100%"
> extensions={[shopify.jsonc()]}
Failed to load the editor. style={{ height: '100%' }}
</p> theme={light ? xcodeLight : xcodeDark}
} onChange={(value) => props.onChange(value)}
> />
<ClientOnly fallback={<Fallback acl={props.value} />}> )}
{() => ( </ClientOnly>
<CodeMirror </ErrorBoundary>
value={props.value}
height="100%"
extensions={[shopify.jsonc()]}
style={{ height: '100%' }}
theme={light ? githubLight : githubDark}
onChange={(value) => props.onChange(value)}
/>
)}
</ClientOnly>
</ErrorBoundary>
</div>
</div> </div>
); );
} }
@ -80,41 +67,27 @@ export function Differ(props: DifferProps) {
}); });
return ( return (
<div <div className="text-sm">
className={cn( {props.left === props.right ? (
'border border-gray-200 dark:border-gray-700', <div className="flex flex-col items-center gap-2.5 py-8">
'rounded-b-lg rounded-tr-lg mb-2 z-10 overflow-x-hidden', <BookCopy />
)} <p className="text-lg font-semibold">No changes</p>
> </div>
<div className="overflow-y-scroll h-editor text-sm"> ) : (
{props.left === props.right ? ( <div className="h-editor overflow-y-scroll">
<p
className={cn(
'w-full h-full flex items-center justify-center',
'text-gray-400 dark:text-gray-500 text-xl',
)}
>
No changes
</p>
) : (
<ErrorBoundary <ErrorBoundary
fallback={ fallback={
<p <div className="flex flex-col items-center gap-2.5 py-8">
className={cn( <CircleX />
'w-full h-full flex items-center justify-center', <p className="text-lg font-semibold">
'text-gray-400 dark:text-gray-500 text-xl', Failed to load the editor.
)} </p>
> </div>
Failed to load the editor.
</p>
} }
> >
<ClientOnly fallback={<Fallback acl={props.right} />}> <ClientOnly fallback={<Fallback acl={props.right} />}>
{() => ( {() => (
<Merge <Merge orientation="a-b" theme={light ? xcodeLight : xcodeDark}>
orientation="a-b"
theme={light ? githubLight : githubDark}
>
<Merge.Original <Merge.Original
readOnly readOnly
value={props.left} value={props.left}
@ -129,8 +102,8 @@ export function Differ(props: DifferProps) {
)} )}
</ClientOnly> </ClientOnly>
</ErrorBoundary> </ErrorBoundary>
)} </div>
</div> )}
</div> </div>
); );
} }

View File

@ -1,29 +1,18 @@
import { setTimeout } from 'node:timers/promises'; import { Construction, Eye, FlaskConical, Pencil } from 'lucide-react';
import {
BeakerIcon,
EyeIcon,
IssueDraftIcon,
PencilIcon,
} from '@primer/octicons-react';
//import { useDebounceFetcher } from 'remix-utils/use-debounce-fetcher';
import { useEffect, useMemo, useState } from 'react'; import { useEffect, useMemo, useState } from 'react';
import { Tab, TabList, TabPanel, Tabs } from 'react-aria-components';
import type { ActionFunctionArgs, LoaderFunctionArgs } from 'react-router'; import type { ActionFunctionArgs, LoaderFunctionArgs } from 'react-router';
import { useFetcher, useLoaderData, useRevalidator } from 'react-router'; import { useFetcher, useLoaderData, useRevalidator } from 'react-router';
import Button from '~/components/Button'; import Button from '~/components/Button';
import Code from '~/components/Code';
import Link from '~/components/Link'; import Link from '~/components/Link';
import Notice from '~/components/Notice'; import Notice from '~/components/Notice';
import Spinner from '~/components/Spinner'; import Spinner from '~/components/Spinner';
import cn from '~/utils/cn'; import Tabs from '~/components/Tabs';
import { loadContext } from '~/utils/config/headplane'; import { loadContext } from '~/utils/config/headplane';
import { loadConfig } from '~/utils/config/headscale'; import { loadConfig } from '~/utils/config/headscale';
import { HeadscaleError, pull, put } from '~/utils/headscale'; import { HeadscaleError, pull, put } from '~/utils/headscale';
import log from '~/utils/log'; import log from '~/utils/log';
import { send } from '~/utils/res'; import { send } from '~/utils/res';
import { getSession } from '~/utils/sessions.server'; import { getSession } from '~/utils/sessions.server';
import toast from '~/utils/toast'; import toast from '~/utils/toast';
import { Differ, Editor } from './components/cm.client'; import { Differ, Editor } from './components/cm.client';
import { ErrorView } from './components/error'; import { ErrorView } from './components/error';
@ -134,7 +123,7 @@ export async function action({ request }: ActionFunctionArgs) {
} catch (error) { } catch (error) {
log.debug('APIC', 'Failed to update ACL policy with error %s', error); log.debug('APIC', 'Failed to update ACL policy with error %s', error);
// @ts-ignore: Shut UP we know it's a string most of the time // @ts-ignore: TODO: Shut UP we know it's a string most of the time
const text = JSON.parse(error.message); const text = JSON.parse(error.message);
return send( return send(
{ success: false, error: text.message }, { success: false, error: text.message },
@ -158,7 +147,6 @@ export default function Page() {
return; return;
} }
// @ts-ignore: useDebounceFetcher is not typed correctly
if (fetcher.data.success) { if (fetcher.data.success) {
toast('Updated tailnet ACL policy'); toast('Updated tailnet ACL policy');
} else { } else {
@ -188,7 +176,6 @@ export default function Page() {
} }
// If we have a failed fetcher state allow the user to try again // If we have a failed fetcher state allow the user to try again
// @ts-ignore: useDebounceFetcher is not typed correctly
if (fetcher.data?.success === false) { if (fetcher.data?.success === false) {
return false; return false;
} }
@ -228,88 +215,52 @@ export default function Page() {
</Link> </Link>
. .
</p> </p>
{ {fetcher.data?.success === false ? (
// @ts-ignore: useDebounceFetcher is not typed correctly <ErrorView message={fetcher.data.error} />
fetcher.data?.success === false ? ( ) : undefined}
// @ts-ignore: useDebounceFetcher is not typed correctly
<ErrorView message={fetcher.data.error} />
) : undefined
}
{data.read ? ( {data.read ? (
<> <>
<Tabs> <Tabs label="ACL Editor" className="mb-4">
<TabList <Tabs.Item
className={cn( key="edit"
'flex border-t border-gray-200 dark:border-gray-700', title={
'w-fit rounded-t-lg overflow-hidden', <div className="flex items-center gap-2">
'text-gray-400 dark:text-gray-500', <Pencil className="p-1" />
)} <span>Edit file</span>
</div>
}
> >
<Tab
id="edit"
className={({ isSelected }) =>
cn(
'px-4 py-2 rounded-tl-lg',
'focus:outline-none flex items-center gap-2',
'border-x border-gray-200 dark:border-gray-700',
isSelected ? 'text-gray-900 dark:text-gray-100' : '',
)
}
>
<PencilIcon className="w-5 h-5" />
<p>Edit file</p>
</Tab>
<Tab
id="diff"
className={({ isSelected }) =>
cn(
'px-4 py-2',
'focus:outline-none flex items-center gap-2',
'border-x border-gray-200 dark:border-gray-700',
isSelected ? 'text-gray-900 dark:text-gray-100' : '',
)
}
>
<EyeIcon className="w-5 h-5" />
<p>Preview changes</p>
</Tab>
<Tab
id="preview"
className={({ isSelected }) =>
cn(
'px-4 py-2 rounded-tr-lg',
'focus:outline-none flex items-center gap-2',
'border-x border-gray-200 dark:border-gray-700',
isSelected ? 'text-gray-900 dark:text-gray-100' : '',
)
}
>
<BeakerIcon className="w-5 h-5" />
<p>Preview rules</p>
</Tab>
</TabList>
<TabPanel id="edit">
<Editor isDisabled={!data.write} value={acl} onChange={setAcl} /> <Editor isDisabled={!data.write} value={acl} onChange={setAcl} />
</TabPanel> </Tabs.Item>
<TabPanel id="diff"> <Tabs.Item
key="diff"
title={
<div className="flex items-center gap-2">
<Eye className="p-1" />
<span>Preview changes</span>
</div>
}
>
<Differ left={data?.policy ?? ''} right={acl} /> <Differ left={data?.policy ?? ''} right={acl} />
</TabPanel> </Tabs.Item>
<TabPanel id="preview"> <Tabs.Item
<div key="preview"
className={cn( title={
'border border-gray-200 dark:border-gray-700', <div className="flex items-center gap-2">
'rounded-b-lg rounded-tr-lg mb-4 overflow-hidden', <FlaskConical className="p-1" />
'p-16 flex flex-col items-center justify-center', <span>Preview rules</span>
)} </div>
> }
<IssueDraftIcon className="w-24 h-24 text-gray-300 dark:text-gray-500" /> >
<div className="flex flex-col items-center py-8">
<Construction />
<p className="w-1/2 text-center mt-4"> <p className="w-1/2 text-center mt-4">
The Preview rules is very much still a work in progress. It is Previewing rules is not available yet. This feature is still
a bit complicated to implement right now but hopefully it will in development and is pretty complicated to implement.
be available soon. Hopefully I will be able to get to it soon.
</p> </p>
</div> </div>
</TabPanel> </Tabs.Item>
</Tabs> </Tabs>
<Button <Button
variant="heavy" variant="heavy"

View File

@ -1,11 +1,9 @@
import { Button } from 'react-aria-components';
import { useSubmit } from 'react-router'; import { useSubmit } from 'react-router';
import Button from '~/components/Button';
import Code from '~/components/Code'; import Code from '~/components/Code';
import Link from '~/components/Link'; import Link from '~/components/Link';
import TableList from '~/components/TableList'; import TableList from '~/components/TableList';
import cn from '~/utils/cn'; import cn from '~/utils/cn';
import AddDNS from '../dialogs/dns'; import AddDNS from '../dialogs/dns';
interface Props { interface Props {
@ -33,24 +31,29 @@ export default function DNS({ records, isDisabled }: Props) {
<TableList className="mb-8"> <TableList className="mb-8">
{records.length === 0 ? ( {records.length === 0 ? (
<TableList.Item> <TableList.Item>
<p className="opacity-50 text-sm mx-auto">No DNS records found</p> <p className="opacity-50 mx-auto">No DNS records found</p>
</TableList.Item> </TableList.Item>
) : ( ) : (
records.map((record, index) => ( records.map((record, index) => (
<TableList.Item key={`${record.name}-${record.value}`}> <TableList.Item key={`${record.name}-${record.value}`}>
<div className="flex gap-24"> <div className="flex gap-24 items-center">
<div className="flex gap-2"> <div className="flex gap-4 items-center">
<p className="font-mono text-sm font-bold">{record.type}</p> <p
className={cn(
'font-mono text-sm font-bold py-1 px-2 rounded-md',
'bg-headplane-100 dark:bg-headplane-700/30',
)}
>
{record.type}
</p>
<p className="font-mono text-sm">{record.name}</p> <p className="font-mono text-sm">{record.name}</p>
</div> </div>
<p className="font-mono text-sm">{record.value}</p> <p className="font-mono text-sm">{record.value}</p>
</div> </div>
<Button <Button
className={cn( className={cn(
'text-sm', 'px-2 py-1 rounded-md',
'text-red-600 dark:text-red-400', 'text-red-500 dark:text-red-400',
'hover:text-red-700 dark:hover:text-red-300',
isDisabled && 'opacity-50 cursor-not-allowed',
)} )}
isDisabled={isDisabled} isDisabled={isDisabled}
onPress={() => { onPress={() => {

View File

@ -11,10 +11,11 @@ import {
verticalListSortingStrategy, verticalListSortingStrategy,
} from '@dnd-kit/sortable'; } from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities'; import { CSS } from '@dnd-kit/utilities';
import { LockIcon, ThreeBarsIcon } from '@primer/octicons-react'; import { GripVertical, Lock } from 'lucide-react';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { Button, Input } from 'react-aria-components';
import { type FetcherWithComponents, useFetcher } from 'react-router'; import { type FetcherWithComponents, useFetcher } from 'react-router';
import Button from '~/components/Button';
import Input from '~/components/Input';
import Spinner from '~/components/Spinner'; import Spinner from '~/components/Spinner';
import TableList from '~/components/TableList'; import TableList from '~/components/TableList';
@ -81,8 +82,10 @@ export default function Domains({
<TableList> <TableList>
{baseDomain ? ( {baseDomain ? (
<TableList.Item key="magic-dns-sd"> <TableList.Item key="magic-dns-sd">
<p className="font-mono text-sm">{baseDomain}</p> <div className="flex items-center gap-4">
<LockIcon className="h-4 w-4" /> <Lock className="p-0.5" />
<p className="font-mono text-sm py-0.5">{baseDomain}</p>
</div>
</TableList.Item> </TableList.Item>
) : undefined} ) : undefined}
<SortableContext <SortableContext
@ -116,20 +119,20 @@ export default function Domains({
<TableList.Item key="add-sd"> <TableList.Item key="add-sd">
<Input <Input
type="text" type="text"
className="font-mono text-sm bg-transparent w-full mr-2" className={cn(
'border-none font-mono p-0',
'rounded-none focus:ring-0 w-full',
)}
placeholder="Search Domain" placeholder="Search Domain"
value={newDomain} onChange={setNewDomain}
onChange={(event) => { label="Search Domain"
setNewDomain(event.target.value); labelHidden
}}
/> />
{fetcher.state === 'idle' ? ( {fetcher.state === 'idle' ? (
<Button <Button
className={cn( className={cn(
'text-sm font-semibold', 'px-2 py-1 rounded-md',
'text-blue-600 dark:text-blue-400', 'text-blue-500 dark:text-blue-400',
'hover:text-blue-700 dark:hover:text-blue-300',
newDomain.length === 0 && 'opacity-50 cursor-not-allowed',
)} )}
isDisabled={newDomain.length === 0} isDisabled={newDomain.length === 0}
onPress={() => { onPress={() => {
@ -165,8 +168,7 @@ type DomainProperties = {
readonly id: number; readonly id: number;
readonly isDrag?: boolean; readonly isDrag?: boolean;
readonly localDomains: string[]; readonly localDomains: string[];
// eslint-disable-next-line react/boolean-prop-naming readonly disabled?: boolean; // TODO: isDisabled
readonly disabled?: boolean;
readonly fetcher: FetcherWithComponents<unknown>; readonly fetcher: FetcherWithComponents<unknown>;
}; };
@ -187,17 +189,12 @@ function Domain({
isDragging, isDragging,
} = useSortable({ id }); } = useSortable({ id });
// TODO: Figure out why TableList.Item breaks dndkit
return ( return (
<div <TableList.Item
ref={setNodeRef} ref={setNodeRef}
className={cn( className={cn(
'flex items-center justify-between px-3 py-2', isDragging ? 'opacity-50' : '',
'border-b border-gray-200 last:border-b-0 dark:border-zinc-800', isDrag ? 'ring bg-white dark:bg-headplane-900' : '',
isDragging ? 'text-gray-400' : '',
isDrag
? 'outline outline-1 outline-gray-500 bg-gray-200 dark:bg-zinc-800'
: '',
)} )}
style={{ style={{
transform: CSS.Transform.toString(transform), transform: CSS.Transform.toString(transform),
@ -206,21 +203,20 @@ function Domain({
> >
<p className="font-mono text-sm flex items-center gap-4"> <p className="font-mono text-sm flex items-center gap-4">
{disabled ? undefined : ( {disabled ? undefined : (
<ThreeBarsIcon <GripVertical {...attributes} {...listeners} className="p-0.5" />
className="h-4 w-4 text-gray-400 focus:outline-none" // <ThreeBarsIcon
{...attributes} // className="h-4 w-4 text-gray-400 focus:outline-none"
{...listeners} // {...attributes}
/> // {...listeners}
// />
)} )}
{domain} {domain}
</p> </p>
{isDrag ? undefined : ( {isDrag ? undefined : (
<Button <Button
className={cn( className={cn(
'text-sm', 'px-2 py-1 rounded-md',
'text-red-600 dark:text-red-400', 'text-red-500 dark:text-red-400',
'hover:text-red-700 dark:hover:text-red-300',
disabled && 'opacity-50 cursor-not-allowed',
)} )}
isDisabled={disabled} isDisabled={disabled}
onPress={() => { onPress={() => {
@ -240,6 +236,6 @@ function Domain({
Remove Remove
</Button> </Button>
)} )}
</div> </TableList.Item>
); );
} }

View File

@ -1,9 +1,8 @@
import { Button } from 'react-aria-components';
import { useSubmit } from 'react-router'; import { useSubmit } from 'react-router';
import Button from '~/components/Button';
import Link from '~/components/Link'; import Link from '~/components/Link';
import TableList from '~/components/TableList'; import TableList from '~/components/TableList';
import cn from '~/utils/cn'; import cn from '~/utils/cn';
import AddNameserver from '../dialogs/nameserver'; import AddNameserver from '../dialogs/nameserver';
interface Props { interface Props {
@ -75,10 +74,8 @@ function NameserverList({
<p className="font-mono text-sm">{ns}</p> <p className="font-mono text-sm">{ns}</p>
<Button <Button
className={cn( className={cn(
'text-sm', 'px-2 py-1 rounded-md',
'text-red-600 dark:text-red-400', 'text-red-500 dark:text-red-400',
'hover:text-red-700 dark:hover:text-red-300',
isDisabled && 'opacity-50 cursor-not-allowed',
)} )}
isDisabled={isDisabled} isDisabled={isDisabled}
onPress={() => { onPress={() => {

View File

@ -26,7 +26,9 @@ export default function Modal({ name, disabled }: Properties) {
</p> </p>
<Input <Input
isReadOnly isReadOnly
labelHidden
className="w-3/5 font-medium text-sm" className="w-3/5 font-medium text-sm"
label="Tailnet name"
value={name} value={name}
onFocus={(event) => { onFocus={(event) => {
event.target.select(); event.target.select();

View File

@ -1,8 +1,10 @@
import { PlusIcon, XIcon } from '@primer/octicons-react'; import { Plus, X } from 'lucide-react';
import { useState } from 'react'; import { useState } from 'react';
import { Button, Input } from 'react-aria-components'; import Button from '~/components/Button';
import Dialog from '~/components/Dialog'; import Dialog from '~/components/Dialog';
import Input from '~/components/Input';
import Link from '~/components/Link'; import Link from '~/components/Link';
import TableList from '~/components/TableList';
import type { Machine } from '~/types'; import type { Machine } from '~/types';
import cn from '~/utils/cn'; import cn from '~/utils/cn';
@ -34,76 +36,55 @@ export default function Tags({ machine, isOpen, setIsOpen }: TagsProps) {
<input type="hidden" name="_method" value="tags" /> <input type="hidden" name="_method" value="tags" />
<input type="hidden" name="id" value={machine.id} /> <input type="hidden" name="id" value={machine.id} />
<input type="hidden" name="tags" value={tags.join(',')} /> <input type="hidden" name="tags" value={tags.join(',')} />
<div <TableList className="mt-4">
className={cn( {tags.length === 0 ? (
'border border-ui-300 rounded-lg overflow-visible', <div
'dark:border-ui-700 dark:text-ui-300 mt-4', className={cn(
)} 'flex py-4 px-4 bg-ui-100 dark:bg-ui-800',
> 'items-center justify-center rounded-t-lg',
<div className="divide-y divide-ui-200 dark:divide-ui-600"> 'text-ui-600 dark:text-ui-300',
{tags.length === 0 ? ( )}
<div >
className={cn( <p>No tags are set on this machine.</p>
'flex py-4 px-4 bg-ui-100 dark:bg-ui-800', </div>
'items-center justify-center rounded-t-lg', ) : (
'text-ui-600 dark:text-ui-300', tags.map((item) => (
)} <TableList.Item className="font-mono" key={item} id={item}>
> {item}
<p>No tags are set on this machine.</p> <Button
</div> className="rounded-md p-0.5"
) : ( onPress={() => {
tags.map((item) => ( setTags(tags.filter((tag) => tag !== item));
<div }}
key={item}
id={item}
className={cn(
'px-2.5 py-1.5 flex',
'items-center justify-between',
'font-mono text-sm',
)}
> >
{item} <X className="p-1" />
<Button </Button>
className="rounded-full p-0 w-6 h-6" </TableList.Item>
onPress={() => { ))
setTags(tags.filter((tag) => tag !== item)); )}
}} <TableList.Item
>
<XIcon className="w-4 h-4" />
</Button>
</div>
))
)}
</div>
<div
className={cn( className={cn(
'flex px-2.5 py-1.5 w-full', 'rounded-b-xl focus-within:ring',
'border-t border-ui-300 dark:border-ui-700',
'rounded-b-lg justify-between items-center',
'dark:bg-ui-800 dark:text-ui-300',
'focus-within:ring-2 focus-within:ring-blue-600',
tag.length > 0 && tag.length > 0 &&
!tag.startsWith('tag:') && (!tag.startsWith('tag:') || tags.includes(tag)) &&
'outline outline-red-500', 'ring ring-red-500 ring-opacity-50',
)} )}
> >
<Input <Input
labelHidden
label="Add a tag"
placeholder="tag:example" placeholder="tag:example"
onChange={setTag}
className={cn( className={cn(
'bg-transparent w-full', 'border-none font-mono p-0',
'border-none focus:ring-0', 'rounded-none focus:ring-0 w-full',
'focus:outline-none font-mono text-sm',
'dark:bg-transparent dark:text-ui-300',
)} )}
value={tag}
onChange={(e) => {
setTag(e.currentTarget.value);
}}
/> />
<Button <Button
className={cn( className={cn(
'rounded-lg p-0 h-6 w-6', 'rounded-md p-0.5',
!tag.startsWith('tag:') && 'opacity-50 cursor-not-allowed', (!tag.startsWith('tag:') || tags.includes(tag)) &&
'opacity-50 cursor-not-allowed',
)} )}
isDisabled={ isDisabled={
tag.length === 0 || tag.length === 0 ||
@ -115,10 +96,10 @@ export default function Tags({ machine, isOpen, setIsOpen }: TagsProps) {
setTag(''); setTag('');
}} }}
> >
<PlusIcon className="w-4 h-4" /> <Plus className="p-1" />
</Button> </Button>
</div> </TableList.Item>
</div> </TableList>
</Dialog.Panel> </Dialog.Panel>
</Dialog> </Dialog>
); );

View File

@ -24,6 +24,7 @@
"@types/react": "^19.0.2", "@types/react": "^19.0.2",
"@types/react-dom": "^19.0.2", "@types/react-dom": "^19.0.2",
"@uiw/codemirror-theme-github": "^4.23.7", "@uiw/codemirror-theme-github": "^4.23.7",
"@uiw/codemirror-theme-xcode": "^4.23.8",
"@uiw/react-codemirror": "^4.23.7", "@uiw/react-codemirror": "^4.23.7",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"dotenv": "^16.4.7", "dotenv": "^16.4.7",
@ -33,7 +34,6 @@
"openid-client": "^6.1.7", "openid-client": "^6.1.7",
"react": "19.0.0", "react": "19.0.0",
"react-aria": "^3.37.0", "react-aria": "^3.37.0",
"react-aria-components": "^1.6.0",
"react-codemirror-merge": "^4.23.7", "react-codemirror-merge": "^4.23.7",
"react-dom": "19.0.0", "react-dom": "19.0.0",
"react-error-boundary": "^5.0.0", "react-error-boundary": "^5.0.0",

View File

@ -55,6 +55,9 @@ importers:
'@uiw/codemirror-theme-github': '@uiw/codemirror-theme-github':
specifier: ^4.23.7 specifier: ^4.23.7
version: 4.23.7(@codemirror/language@6.10.8)(@codemirror/state@6.5.0)(@codemirror/view@6.36.1) version: 4.23.7(@codemirror/language@6.10.8)(@codemirror/state@6.5.0)(@codemirror/view@6.36.1)
'@uiw/codemirror-theme-xcode':
specifier: ^4.23.8
version: 4.23.8(@codemirror/language@6.10.8)(@codemirror/state@6.5.0)(@codemirror/view@6.36.1)
'@uiw/react-codemirror': '@uiw/react-codemirror':
specifier: ^4.23.7 specifier: ^4.23.7
version: 4.23.7(@babel/runtime@7.26.0)(@codemirror/autocomplete@6.18.2(@codemirror/language@6.10.8)(@codemirror/state@6.5.0)(@codemirror/view@6.36.1)(@lezer/common@1.2.3))(@codemirror/language@6.10.8)(@codemirror/lint@6.8.2)(@codemirror/search@6.5.7)(@codemirror/state@6.5.0)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.36.1)(codemirror@6.0.1(@lezer/common@1.2.3))(react-dom@19.0.0(react@19.0.0))(react@19.0.0) version: 4.23.7(@babel/runtime@7.26.0)(@codemirror/autocomplete@6.18.2(@codemirror/language@6.10.8)(@codemirror/state@6.5.0)(@codemirror/view@6.36.1)(@lezer/common@1.2.3))(@codemirror/language@6.10.8)(@codemirror/lint@6.8.2)(@codemirror/search@6.5.7)(@codemirror/state@6.5.0)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.36.1)(codemirror@6.0.1(@lezer/common@1.2.3))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
@ -82,9 +85,6 @@ importers:
react-aria: react-aria:
specifier: ^3.37.0 specifier: ^3.37.0
version: 3.37.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0) version: 3.37.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
react-aria-components:
specifier: ^1.6.0
version: 1.6.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
react-codemirror-merge: react-codemirror-merge:
specifier: ^4.23.7 specifier: ^4.23.7
version: 4.23.7(@babel/runtime@7.26.0)(@codemirror/autocomplete@6.18.2(@codemirror/language@6.10.8)(@codemirror/state@6.5.0)(@codemirror/view@6.36.1)(@lezer/common@1.2.3))(@codemirror/language@6.10.8)(@codemirror/lint@6.8.2)(@codemirror/search@6.5.7)(@codemirror/state@6.5.0)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.36.1)(codemirror@6.0.1(@lezer/common@1.2.3))(react-dom@19.0.0(react@19.0.0))(react@19.0.0) version: 4.23.7(@babel/runtime@7.26.0)(@codemirror/autocomplete@6.18.2(@codemirror/language@6.10.8)(@codemirror/state@6.5.0)(@codemirror/view@6.36.1)(@lezer/common@1.2.3))(@codemirror/language@6.10.8)(@codemirror/lint@6.8.2)(@codemirror/search@6.5.7)(@codemirror/state@6.5.0)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.36.1)(codemirror@6.0.1(@lezer/common@1.2.3))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
@ -876,12 +876,6 @@ packages:
peerDependencies: peerDependencies:
react: '>=16.3' react: '>=16.3'
'@react-aria/autocomplete@3.0.0-alpha.37':
resolution: {integrity: sha512-a7awFG3hshJ/kX7Qti/cJAKOG0XU5F/XW6fQffKGfEge7PmiWIvaLTrT5her79/v8v/bRBykIkpEgDCFE7WGzg==}
peerDependencies:
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
'@react-aria/breadcrumbs@3.5.20': '@react-aria/breadcrumbs@3.5.20':
resolution: {integrity: sha512-xqVSSDPpQuUFpJyIXMQv8L7zumk5CeGX7qTzo4XRvqm5T9qnNAX4XpYEMdktnLrQRY/OemCBScbx7SEwr0B3Kg==} resolution: {integrity: sha512-xqVSSDPpQuUFpJyIXMQv8L7zumk5CeGX7qTzo4XRvqm5T9qnNAX4XpYEMdktnLrQRY/OemCBScbx7SEwr0B3Kg==}
peerDependencies: peerDependencies:
@ -906,12 +900,6 @@ packages:
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
'@react-aria/collections@3.0.0-alpha.7':
resolution: {integrity: sha512-JR2Ro33Chlf26NM12zJsK+MOs5/k+PQallT5+4YawndYmbxqlDLADcoFdcORJqh0pKf9OnluWtANobCkQGd0aQ==}
peerDependencies:
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
'@react-aria/color@3.0.3': '@react-aria/color@3.0.3':
resolution: {integrity: sha512-DDVma2107VHBfSuEnnmy+KJvXvxEXWSAooii2vlHHmQNb5x4rv4YTk+dP5GZl/7MgT8OgPTB9UHoC83bXFMDRA==} resolution: {integrity: sha512-DDVma2107VHBfSuEnnmy+KJvXvxEXWSAooii2vlHHmQNb5x4rv4YTk+dP5GZl/7MgT8OgPTB9UHoC83bXFMDRA==}
peerDependencies: peerDependencies:
@ -1149,12 +1137,6 @@ packages:
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
'@react-aria/tree@3.0.0-beta.3':
resolution: {integrity: sha512-eQnCtvDgpunCHInIT+Da3qdgzDzKEFW9REX2j1vMqWTsbM1YikVlBzB9AJOd9KIAWyn+p4TYdL8zzPWxvuSdfA==}
peerDependencies:
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
'@react-aria/utils@3.26.0': '@react-aria/utils@3.26.0':
resolution: {integrity: sha512-LkZouGSjjQ0rEqo4XJosS4L3YC/zzQkfRM3KoqK6fUOmUJ9t0jQ09WjiF+uOoG9u+p30AVg3TrZRUWmoTS+koQ==} resolution: {integrity: sha512-LkZouGSjjQ0rEqo4XJosS4L3YC/zzQkfRM3KoqK6fUOmUJ9t0jQ09WjiF+uOoG9u+p30AVg3TrZRUWmoTS+koQ==}
peerDependencies: peerDependencies:
@ -1166,12 +1148,6 @@ packages:
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
'@react-aria/virtualizer@4.1.1':
resolution: {integrity: sha512-AYQmC/S9HhxGOj8HkQdxDW8/+sUEmmfcGpjkInzXB8UZCB1FQLC0LpvA8fOP7AfzLaAL+HVcYF5BvnGMPijHTQ==}
peerDependencies:
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
'@react-aria/visually-hidden@3.8.19': '@react-aria/visually-hidden@3.8.19':
resolution: {integrity: sha512-MZgCCyQ3sdG94J5iJz7I7Ai3IxoN0U5d/+EaUnA1mfK7jf2fSYQBqi6Eyp8sWUYzBTLw4giXB5h0RGAnWzk9hA==} resolution: {integrity: sha512-MZgCCyQ3sdG94J5iJz7I7Ai3IxoN0U5d/+EaUnA1mfK7jf2fSYQBqi6Eyp8sWUYzBTLw4giXB5h0RGAnWzk9hA==}
peerDependencies: peerDependencies:
@ -1206,11 +1182,6 @@ packages:
typescript: typescript:
optional: true optional: true
'@react-stately/autocomplete@3.0.0-alpha.0':
resolution: {integrity: sha512-as4si0pBcnGnggwpvemMwCLTeV0h9GS9e5eHSR3RFg14eqUHZBEzYJ0kh9oTugpsGuf1TSM/HDizo8GQk3EtPA==}
peerDependencies:
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
'@react-stately/calendar@3.7.0': '@react-stately/calendar@3.7.0':
resolution: {integrity: sha512-N15zKubP2S7eWfPSJjKVlmJA7YpWzrIGx52BFhwLSQAZcV+OPcMgvOs71WtB7PLwl6DUYQGsgc0B3tcHzzvdvQ==} resolution: {integrity: sha512-N15zKubP2S7eWfPSJjKVlmJA7YpWzrIGx52BFhwLSQAZcV+OPcMgvOs71WtB7PLwl6DUYQGsgc0B3tcHzzvdvQ==}
peerDependencies: peerDependencies:
@ -1269,11 +1240,6 @@ packages:
peerDependencies: peerDependencies:
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
'@react-stately/layout@4.1.1':
resolution: {integrity: sha512-kXeo7HKYTOcqMKru1sKFoMoZA+YywSUqHeIA90MptzRugbFhQGq4nUbIYM2p3FeHAX9HU1JAXThuLcwDOHhB8Q==}
peerDependencies:
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
'@react-stately/list@3.11.2': '@react-stately/list@3.11.2':
resolution: {integrity: sha512-eU2tY3aWj0SEeC7lH9AQoeAB4LL9mwS54FvTgHHoOgc1ZIwRJUaZoiuETyWQe98AL8KMgR1nrnDJ1I+CcT1Y7g==} resolution: {integrity: sha512-eU2tY3aWj0SEeC7lH9AQoeAB4LL9mwS54FvTgHHoOgc1ZIwRJUaZoiuETyWQe98AL8KMgR1nrnDJ1I+CcT1Y7g==}
peerDependencies: peerDependencies:
@ -1354,17 +1320,6 @@ packages:
peerDependencies: peerDependencies:
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
'@react-stately/virtualizer@4.2.1':
resolution: {integrity: sha512-GHGEXV0ZRhq34U/P3LzkByCBfy2IDynYlV1SE4njkUWWGE/0AH56UegM6w2l3GeiNpXsXCgXl7jpAKeIGMEnrQ==}
peerDependencies:
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
'@react-types/autocomplete@3.0.0-alpha.28':
resolution: {integrity: sha512-meHxBVS5H2L7lVOX99jiAfhcvtG0s7EE7iF7X20/yqEnkwWSpyeMKcDKFpvx/bLGUSmRTVFCBLgvPpwUyhcFkg==}
peerDependencies:
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
'@react-types/breadcrumbs@3.7.10': '@react-types/breadcrumbs@3.7.10':
resolution: {integrity: sha512-5HhRxkKHfAQBoyOYzyf4HT+24HgPE/C/QerxJLNNId303LXO03yeYrbvRqhYZSlD1ACLJW9OmpPpREcw5iSqgw==} resolution: {integrity: sha512-5HhRxkKHfAQBoyOYzyf4HT+24HgPE/C/QerxJLNNId303LXO03yeYrbvRqhYZSlD1ACLJW9OmpPpREcw5iSqgw==}
peerDependencies: peerDependencies:
@ -1410,11 +1365,6 @@ packages:
peerDependencies: peerDependencies:
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
'@react-types/form@3.7.9':
resolution: {integrity: sha512-+qGDrQFdIh8umU82zmnYJ0V2rLoGSQ3yApFT02URz//NWeTA7qo0Oab2veKvXUkcBb47oSvytZYmkExPikxIEg==}
peerDependencies:
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
'@react-types/grid@3.2.11': '@react-types/grid@3.2.11':
resolution: {integrity: sha512-Mww9nrasppvPbsBi+uUqFnf7ya8fXN0cTVzDNG+SveD8mhW+sbtuy+gPtEpnFD2Oyi8qLuObefzt4gdekJX2Yw==} resolution: {integrity: sha512-Mww9nrasppvPbsBi+uUqFnf7ya8fXN0cTVzDNG+SveD8mhW+sbtuy+gPtEpnFD2Oyi8qLuObefzt4gdekJX2Yw==}
peerDependencies: peerDependencies:
@ -1660,6 +1610,9 @@ packages:
'@uiw/codemirror-theme-github@4.23.7': '@uiw/codemirror-theme-github@4.23.7':
resolution: {integrity: sha512-r9SstBZD7Ow1sQ8F0EpsRGx9b11K552M2FayvyLWTkal64YJmQMKW0S2KcWykgCMKLWhmDFi7LX+h8cg6nek8g==} resolution: {integrity: sha512-r9SstBZD7Ow1sQ8F0EpsRGx9b11K552M2FayvyLWTkal64YJmQMKW0S2KcWykgCMKLWhmDFi7LX+h8cg6nek8g==}
'@uiw/codemirror-theme-xcode@4.23.8':
resolution: {integrity: sha512-7Okk9Aqy1sEXXJVMUMnkbbJkPQZLyFKpHDoj74eO0RQDVzHZM1ESJpto98YxNp1atMBKf3RxmffPBflHFrEd/Q==}
'@uiw/codemirror-themes@4.23.7': '@uiw/codemirror-themes@4.23.7':
resolution: {integrity: sha512-UNf1XOx1hG9OmJnrtT86PxKcdcwhaNhbrcD+nsk8WxRJ3n5c8nH6euDvgVPdVLPwbizsaQcZTILACgA/FjRpVg==} resolution: {integrity: sha512-UNf1XOx1hG9OmJnrtT86PxKcdcwhaNhbrcD+nsk8WxRJ3n5c8nH6euDvgVPdVLPwbizsaQcZTILACgA/FjRpVg==}
peerDependencies: peerDependencies:
@ -1667,6 +1620,13 @@ packages:
'@codemirror/state': '>=6.0.0' '@codemirror/state': '>=6.0.0'
'@codemirror/view': '>=6.0.0' '@codemirror/view': '>=6.0.0'
'@uiw/codemirror-themes@4.23.8':
resolution: {integrity: sha512-PZmJBZxWMuZ48p/2D5aRPl8zTlBq1d/+NeRqyyH6P6k6yWDF6h71m0Dt+fjslgPE7KmWXux2hbejXXXoRLZO9Q==}
peerDependencies:
'@codemirror/language': '>=6.0.0'
'@codemirror/state': '>=6.0.0'
'@codemirror/view': '>=6.0.0'
'@uiw/react-codemirror@4.23.7': '@uiw/react-codemirror@4.23.7':
resolution: {integrity: sha512-Nh/0P6W+kWta+ARp9YpnKPD9ick5teEnwmtNoPQnyd6NPv0EQP3Ui4YmRVNj1nkUEo+QjrAUaEfcejJ2up/HZA==} resolution: {integrity: sha512-Nh/0P6W+kWta+ARp9YpnKPD9ick5teEnwmtNoPQnyd6NPv0EQP3Ui4YmRVNj1nkUEo+QjrAUaEfcejJ2up/HZA==}
peerDependencies: peerDependencies:
@ -1805,9 +1765,6 @@ packages:
resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==}
engines: {node: '>=18'} engines: {node: '>=18'}
client-only@0.0.1:
resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==}
clsx@2.1.1: clsx@2.1.1:
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
engines: {node: '>=6'} engines: {node: '>=6'}
@ -2517,12 +2474,6 @@ packages:
queue-microtask@1.2.3: queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
react-aria-components@1.6.0:
resolution: {integrity: sha512-YfG9PUE7XrXtDDAqT4pLTGyYQaiHHTBFdAK/wNgGsypVnQSdzmyYlV3Ty8aHlZJI6hP9RWkbywvosXkU7KcPHg==}
peerDependencies:
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
react-aria@3.37.0: react-aria@3.37.0:
resolution: {integrity: sha512-u3WUEMTcbQFaoHauHO3KhPaBYzEv1o42EdPcLAs05GBw9Q6Axlqwo73UFgMrsc2ElwLAZ4EKpSdWHLo1R5gfiw==} resolution: {integrity: sha512-u3WUEMTcbQFaoHauHO3KhPaBYzEv1o42EdPcLAs05GBw9Q6Axlqwo73UFgMrsc2ElwLAZ4EKpSdWHLo1R5gfiw==}
peerDependencies: peerDependencies:
@ -3733,24 +3684,6 @@ snapshots:
dependencies: dependencies:
react: 19.0.0 react: 19.0.0
'@react-aria/autocomplete@3.0.0-alpha.37(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
'@react-aria/combobox': 3.11.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@react-aria/i18n': 3.12.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@react-aria/interactions': 3.23.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@react-aria/listbox': 3.14.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@react-aria/searchfield': 3.8.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@react-aria/textfield': 3.16.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@react-aria/utils': 3.27.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@react-stately/autocomplete': 3.0.0-alpha.0(react@19.0.0)
'@react-stately/combobox': 3.10.2(react@19.0.0)
'@react-types/autocomplete': 3.0.0-alpha.28(react@19.0.0)
'@react-types/button': 3.10.2(react@19.0.0)
'@react-types/shared': 3.27.0(react@19.0.0)
'@swc/helpers': 0.5.15
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
'@react-aria/breadcrumbs@3.5.20(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': '@react-aria/breadcrumbs@3.5.20(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies: dependencies:
'@react-aria/i18n': 3.12.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@react-aria/i18n': 3.12.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
@ -3806,16 +3739,6 @@ snapshots:
react: 19.0.0 react: 19.0.0
react-dom: 19.0.0(react@19.0.0) react-dom: 19.0.0(react@19.0.0)
'@react-aria/collections@3.0.0-alpha.7(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
'@react-aria/ssr': 3.9.7(react@19.0.0)
'@react-aria/utils': 3.27.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@react-types/shared': 3.27.0(react@19.0.0)
'@swc/helpers': 0.5.15
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
use-sync-external-store: 1.4.0(react@19.0.0)
'@react-aria/color@3.0.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': '@react-aria/color@3.0.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies: dependencies:
'@react-aria/i18n': 3.12.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@react-aria/i18n': 3.12.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
@ -4342,19 +4265,6 @@ snapshots:
react: 19.0.0 react: 19.0.0
react-dom: 19.0.0(react@19.0.0) react-dom: 19.0.0(react@19.0.0)
'@react-aria/tree@3.0.0-beta.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
'@react-aria/gridlist': 3.10.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@react-aria/i18n': 3.12.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@react-aria/selection': 3.22.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@react-aria/utils': 3.27.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@react-stately/tree': 3.8.7(react@19.0.0)
'@react-types/button': 3.10.2(react@19.0.0)
'@react-types/shared': 3.27.0(react@19.0.0)
'@swc/helpers': 0.5.15
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
'@react-aria/utils@3.26.0(react@19.0.0)': '@react-aria/utils@3.26.0(react@19.0.0)':
dependencies: dependencies:
'@react-aria/ssr': 3.9.7(react@19.0.0) '@react-aria/ssr': 3.9.7(react@19.0.0)
@ -4374,17 +4284,6 @@ snapshots:
react: 19.0.0 react: 19.0.0
react-dom: 19.0.0(react@19.0.0) react-dom: 19.0.0(react@19.0.0)
'@react-aria/virtualizer@4.1.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
'@react-aria/i18n': 3.12.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@react-aria/interactions': 3.23.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@react-aria/utils': 3.27.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@react-stately/virtualizer': 4.2.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@react-types/shared': 3.27.0(react@19.0.0)
'@swc/helpers': 0.5.15
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
'@react-aria/visually-hidden@3.8.19(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': '@react-aria/visually-hidden@3.8.19(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies: dependencies:
'@react-aria/interactions': 3.23.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@react-aria/interactions': 3.23.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
@ -4455,12 +4354,6 @@ snapshots:
optionalDependencies: optionalDependencies:
typescript: 5.7.2 typescript: 5.7.2
'@react-stately/autocomplete@3.0.0-alpha.0(react@19.0.0)':
dependencies:
'@react-stately/utils': 3.10.5(react@19.0.0)
'@swc/helpers': 0.5.15
react: 19.0.0
'@react-stately/calendar@3.7.0(react@19.0.0)': '@react-stately/calendar@3.7.0(react@19.0.0)':
dependencies: dependencies:
'@internationalized/date': 3.7.0 '@internationalized/date': 3.7.0
@ -4562,19 +4455,6 @@ snapshots:
'@swc/helpers': 0.5.15 '@swc/helpers': 0.5.15
react: 19.0.0 react: 19.0.0
'@react-stately/layout@4.1.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
'@react-stately/collections': 3.12.1(react@19.0.0)
'@react-stately/table': 3.13.1(react@19.0.0)
'@react-stately/virtualizer': 4.2.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@react-types/grid': 3.2.11(react@19.0.0)
'@react-types/shared': 3.27.0(react@19.0.0)
'@react-types/table': 3.10.4(react@19.0.0)
'@swc/helpers': 0.5.15
react: 19.0.0
transitivePeerDependencies:
- react-dom
'@react-stately/list@3.11.2(react@19.0.0)': '@react-stately/list@3.11.2(react@19.0.0)':
dependencies: dependencies:
'@react-stately/collections': 3.12.1(react@19.0.0) '@react-stately/collections': 3.12.1(react@19.0.0)
@ -4706,21 +4586,6 @@ snapshots:
'@swc/helpers': 0.5.15 '@swc/helpers': 0.5.15
react: 19.0.0 react: 19.0.0
'@react-stately/virtualizer@4.2.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
'@react-aria/utils': 3.27.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@react-types/shared': 3.27.0(react@19.0.0)
'@swc/helpers': 0.5.15
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
'@react-types/autocomplete@3.0.0-alpha.28(react@19.0.0)':
dependencies:
'@react-types/combobox': 3.13.2(react@19.0.0)
'@react-types/searchfield': 3.5.11(react@19.0.0)
'@react-types/shared': 3.27.0(react@19.0.0)
react: 19.0.0
'@react-types/breadcrumbs@3.7.10(react@19.0.0)': '@react-types/breadcrumbs@3.7.10(react@19.0.0)':
dependencies: dependencies:
'@react-types/link': 3.5.10(react@19.0.0) '@react-types/link': 3.5.10(react@19.0.0)
@ -4773,11 +4638,6 @@ snapshots:
'@react-types/shared': 3.27.0(react@19.0.0) '@react-types/shared': 3.27.0(react@19.0.0)
react: 19.0.0 react: 19.0.0
'@react-types/form@3.7.9(react@19.0.0)':
dependencies:
'@react-types/shared': 3.27.0(react@19.0.0)
react: 19.0.0
'@react-types/grid@3.2.11(react@19.0.0)': '@react-types/grid@3.2.11(react@19.0.0)':
dependencies: dependencies:
'@react-types/shared': 3.27.0(react@19.0.0) '@react-types/shared': 3.27.0(react@19.0.0)
@ -4996,12 +4856,26 @@ snapshots:
- '@codemirror/state' - '@codemirror/state'
- '@codemirror/view' - '@codemirror/view'
'@uiw/codemirror-theme-xcode@4.23.8(@codemirror/language@6.10.8)(@codemirror/state@6.5.0)(@codemirror/view@6.36.1)':
dependencies:
'@uiw/codemirror-themes': 4.23.8(@codemirror/language@6.10.8)(@codemirror/state@6.5.0)(@codemirror/view@6.36.1)
transitivePeerDependencies:
- '@codemirror/language'
- '@codemirror/state'
- '@codemirror/view'
'@uiw/codemirror-themes@4.23.7(@codemirror/language@6.10.8)(@codemirror/state@6.5.0)(@codemirror/view@6.36.1)': '@uiw/codemirror-themes@4.23.7(@codemirror/language@6.10.8)(@codemirror/state@6.5.0)(@codemirror/view@6.36.1)':
dependencies: dependencies:
'@codemirror/language': 6.10.8 '@codemirror/language': 6.10.8
'@codemirror/state': 6.5.0 '@codemirror/state': 6.5.0
'@codemirror/view': 6.36.1 '@codemirror/view': 6.36.1
'@uiw/codemirror-themes@4.23.8(@codemirror/language@6.10.8)(@codemirror/state@6.5.0)(@codemirror/view@6.36.1)':
dependencies:
'@codemirror/language': 6.10.8
'@codemirror/state': 6.5.0
'@codemirror/view': 6.36.1
'@uiw/react-codemirror@4.23.7(@babel/runtime@7.26.0)(@codemirror/autocomplete@6.18.2(@codemirror/language@6.10.8)(@codemirror/state@6.5.0)(@codemirror/view@6.36.1)(@lezer/common@1.2.3))(@codemirror/language@6.10.8)(@codemirror/lint@6.8.2)(@codemirror/search@6.5.7)(@codemirror/state@6.5.0)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.36.1)(codemirror@6.0.1(@lezer/common@1.2.3))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': '@uiw/react-codemirror@4.23.7(@babel/runtime@7.26.0)(@codemirror/autocomplete@6.18.2(@codemirror/language@6.10.8)(@codemirror/state@6.5.0)(@codemirror/view@6.36.1)(@lezer/common@1.2.3))(@codemirror/language@6.10.8)(@codemirror/lint@6.8.2)(@codemirror/search@6.5.7)(@codemirror/state@6.5.0)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.36.1)(codemirror@6.0.1(@lezer/common@1.2.3))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies: dependencies:
'@babel/runtime': 7.26.0 '@babel/runtime': 7.26.0
@ -5144,8 +5018,6 @@ snapshots:
chownr@3.0.0: {} chownr@3.0.0: {}
client-only@0.0.1: {}
clsx@2.1.1: {} clsx@2.1.1: {}
codemirror@6.0.1(@lezer/common@1.2.3): codemirror@6.0.1(@lezer/common@1.2.3):
@ -5792,45 +5664,6 @@ snapshots:
queue-microtask@1.2.3: {} queue-microtask@1.2.3: {}
react-aria-components@1.6.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
dependencies:
'@internationalized/date': 3.7.0
'@internationalized/string': 3.2.5
'@react-aria/autocomplete': 3.0.0-alpha.37(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@react-aria/collections': 3.0.0-alpha.7(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@react-aria/color': 3.0.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@react-aria/disclosure': 3.0.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@react-aria/dnd': 3.8.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@react-aria/focus': 3.19.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@react-aria/interactions': 3.23.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@react-aria/live-announcer': 3.4.1
'@react-aria/menu': 3.17.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@react-aria/toolbar': 3.0.0-beta.12(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@react-aria/tree': 3.0.0-beta.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@react-aria/utils': 3.27.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@react-aria/virtualizer': 4.1.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@react-stately/autocomplete': 3.0.0-alpha.0(react@19.0.0)
'@react-stately/color': 3.8.2(react@19.0.0)
'@react-stately/disclosure': 3.0.1(react@19.0.0)
'@react-stately/layout': 4.1.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@react-stately/menu': 3.9.1(react@19.0.0)
'@react-stately/selection': 3.19.0(react@19.0.0)
'@react-stately/table': 3.13.1(react@19.0.0)
'@react-stately/utils': 3.10.5(react@19.0.0)
'@react-stately/virtualizer': 4.2.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@react-types/color': 3.0.2(react@19.0.0)
'@react-types/form': 3.7.9(react@19.0.0)
'@react-types/grid': 3.2.11(react@19.0.0)
'@react-types/shared': 3.27.0(react@19.0.0)
'@react-types/table': 3.10.4(react@19.0.0)
'@swc/helpers': 0.5.15
client-only: 0.0.1
react: 19.0.0
react-aria: 3.37.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
react-dom: 19.0.0(react@19.0.0)
react-stately: 3.35.0(react@19.0.0)
use-sync-external-store: 1.4.0(react@19.0.0)
react-aria@3.37.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0): react-aria@3.37.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
dependencies: dependencies:
'@internationalized/string': 3.2.5 '@internationalized/string': 3.2.5