diff --git a/.changeset/curly-parents-drive.md b/.changeset/curly-parents-drive.md
new file mode 100644
index 00000000..572c23f0
--- /dev/null
+++ b/.changeset/curly-parents-drive.md
@@ -0,0 +1,6 @@
+---
+"@shipfox/react-ui": minor
+"@shipfox/vitest": minor
+---
+
+Add Argos CI Upload Screenshots
diff --git a/.changeset/soft-swans-press.md b/.changeset/soft-swans-press.md
new file mode 100644
index 00000000..c444e0e4
--- /dev/null
+++ b/.changeset/soft-swans-press.md
@@ -0,0 +1,5 @@
+---
+"@shipfox/react-ui": minor
+---
+
+Add Modal components
diff --git a/biome.json b/biome.json
index c892e91b..bbfed181 100644
--- a/biome.json
+++ b/biome.json
@@ -17,7 +17,8 @@
"!**/out",
"!**/coverage",
"!**/nix/store",
- "!**/storybook-static"
+ "!**/storybook-static",
+ "!**/screenshots"
],
"ignoreUnknown": false
},
diff --git a/libs/react/ui/.storybook/preview.tsx b/libs/react/ui/.storybook/preview.tsx
index f310edd6..7f66c79b 100644
--- a/libs/react/ui/.storybook/preview.tsx
+++ b/libs/react/ui/.storybook/preview.tsx
@@ -9,6 +9,17 @@ const withTheme: Decorator = (Story, context) => {
const preview: Preview = {
decorators: [withTheme],
parameters: {
+ viewport: {
+ viewports: {
+ large: {
+ name: 'Large Viewport',
+ styles: {
+ width: '1280px',
+ height: '2000px',
+ },
+ },
+ },
+ },
options: {
storySort: {
method: 'alphabetical',
diff --git a/libs/react/ui/index.css b/libs/react/ui/index.css
index 8e3c1ecb..9177e34a 100644
--- a/libs/react/ui/index.css
+++ b/libs/react/ui/index.css
@@ -192,7 +192,7 @@
--background-contrast-pressed: var(--color-neutral-700);
--background-contrast-base: var(--color-neutral-900);
--background-neutral-background: var(--color-neutral-50);
- --background-backdrop-backdrop: var(--color-neutral-0);
+ --background-backdrop-backdrop: var(--color-alpha-black-64);
/* Button Background */
--background-button-transparent-default: var(--color-alpha-white-0);
diff --git a/libs/react/ui/package.json b/libs/react/ui/package.json
index 95f31ed0..9a35e400 100644
--- a/libs/react/ui/package.json
+++ b/libs/react/ui/package.json
@@ -42,7 +42,8 @@
"recharts": "^3.1.0",
"shiki": "^3.15.0",
"sonner": "^2.0.7",
- "tailwind-merge": "^3.2.0"
+ "tailwind-merge": "^3.2.0",
+ "vaul": "^1.1.1"
},
"peerDependencies": {
"@tanstack/react-table": "^8.19.3",
diff --git a/libs/react/ui/src/components/code-block/code-block-footer.tsx b/libs/react/ui/src/components/code-block/code-block-footer.tsx
index 30d6e799..bdde1cd7 100644
--- a/libs/react/ui/src/components/code-block/code-block-footer.tsx
+++ b/libs/react/ui/src/components/code-block/code-block-footer.tsx
@@ -1,5 +1,6 @@
import {Slot} from '@radix-ui/react-slot';
import {Icon} from 'components/icon/icon';
+import {Text} from 'components/typography';
import type {ComponentProps, HTMLAttributes, ReactNode} from 'react';
import {cn} from 'utils/cn';
@@ -53,22 +54,12 @@ export function CodeBlockFooter({
className={cn('flex w-full items-center justify-start gap-12 px-16 py-12', className)}
{...props}
>
-
- {defaultIcon}
-
+ {defaultIcon}
{(message || description) && (
-
- {message && (
-
- {message}
-
- )}
- {description && (
-
- {description}
-
- )}
-
+
+ {message && {message} }
+ {description && {description} }
+
)}
);
@@ -112,7 +103,7 @@ export function CodeBlockFooterContent({
return (
{children}
@@ -130,19 +121,27 @@ export function CodeBlockFooterMessage({
children,
...props
}: CodeBlockFooterMessageProps) {
- const Comp = asChild ? Slot : 'div';
+ if (asChild) {
+ return (
+
+ {children}
+
+ );
+ }
return (
-
{children}
-
+
);
}
@@ -156,18 +155,26 @@ export function CodeBlockFooterDescription({
children,
...props
}: CodeBlockFooterDescriptionProps) {
- const Comp = asChild ? Slot : 'div';
+ if (asChild) {
+ return (
+
+ {children}
+
+ );
+ }
return (
-
{children}
-
+
);
}
diff --git a/libs/react/ui/src/components/dynamic-item/dynamic-item.stories.tsx b/libs/react/ui/src/components/dynamic-item/dynamic-item.stories.tsx
index 543c5b75..ffa7abb0 100644
--- a/libs/react/ui/src/components/dynamic-item/dynamic-item.stories.tsx
+++ b/libs/react/ui/src/components/dynamic-item/dynamic-item.stories.tsx
@@ -1,6 +1,7 @@
import type {Meta, StoryObj} from '@storybook/react';
import {Button} from 'components/button/button';
import {ItemTitle} from 'components/item';
+import {MovingBorder} from 'components/moving-border';
import {cn} from 'utils/cn';
import illustration1 from '../../assets/illustration-1.svg';
import illustration2 from '../../assets/illustration-2.svg';
@@ -8,7 +9,6 @@ import illustrationBg from '../../assets/illustration-gradient.svg';
import {Avatar} from '../avatar/avatar';
import {AvatarGroup, AvatarGroupTooltip} from '../avatar/avatar-group';
import {Icon} from '../icon/icon';
-import {MovingBorder} from '../moving-border/moving-border';
import {DynamicItem} from './dynamic-item';
const meta = {
diff --git a/libs/react/ui/src/components/icon/icon.tsx b/libs/react/ui/src/components/icon/icon.tsx
index e231b0fe..ee66db60 100644
--- a/libs/react/ui/src/components/icon/icon.tsx
+++ b/libs/react/ui/src/components/icon/icon.tsx
@@ -2,6 +2,7 @@ import {
type RemixiconComponentType,
RiAddLine,
RiArrowRightSLine,
+ RiBookOpenFill,
RiCheckLine,
RiCloseLine,
RiFileCopyLine,
@@ -60,6 +61,7 @@ const iconsMap = {
copy: RiFileCopyLine,
addLine: RiAddLine,
chevronRight: RiArrowRightSLine,
+ bookOpen: RiBookOpenFill,
} as const satisfies Record;
export type IconName = keyof typeof iconsMap;
diff --git a/libs/react/ui/src/components/index.ts b/libs/react/ui/src/components/index.ts
index 5b2ed922..9e3e277c 100644
--- a/libs/react/ui/src/components/index.ts
+++ b/libs/react/ui/src/components/index.ts
@@ -10,6 +10,7 @@ export * from './inline-tips';
export * from './input';
export * from './item';
export * from './label';
+export * from './modal';
export * from './textarea';
export * from './theme';
export * from './toast';
diff --git a/libs/react/ui/src/components/modal/index.ts b/libs/react/ui/src/components/modal/index.ts
new file mode 100644
index 00000000..58e16575
--- /dev/null
+++ b/libs/react/ui/src/components/modal/index.ts
@@ -0,0 +1,23 @@
+export type {
+ ModalContentProps,
+ ModalDescriptionProps,
+ ModalHeaderProps,
+ ModalOverlayProps,
+ ModalTitleProps,
+} from './modal';
+export {
+ Modal,
+ ModalBody,
+ ModalClose,
+ ModalContent,
+ ModalDescription,
+ ModalFooter,
+ ModalHeader,
+ ModalOverlay,
+ ModalPortal,
+ ModalTitle,
+ ModalTrigger,
+ modalContentVariants,
+ modalDefaultTransition,
+ modalOverlayVariants,
+} from './modal';
diff --git a/libs/react/ui/src/components/modal/modal.stories.tsx b/libs/react/ui/src/components/modal/modal.stories.tsx
new file mode 100644
index 00000000..1ef24af6
--- /dev/null
+++ b/libs/react/ui/src/components/modal/modal.stories.tsx
@@ -0,0 +1,384 @@
+import {argosScreenshot} from '@argos-ci/storybook/vitest';
+import type {Meta, StoryObj} from '@storybook/react';
+import {screen, within} from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import {Button, ButtonLink} from 'components/button';
+import {
+ CodeBlock,
+ CodeBlockBody,
+ CodeBlockContent,
+ CodeBlockCopyButton,
+ CodeBlockFilename,
+ CodeBlockFiles,
+ CodeBlockFooter,
+ CodeBlockHeader,
+ CodeBlockItem,
+} from 'components/code-block';
+import {DynamicItem} from 'components/dynamic-item';
+import {Icon} from 'components/icon';
+import {Input} from 'components/input';
+import {ItemTitle} from 'components/item';
+import {Label} from 'components/label';
+import {MovingBorder} from 'components/moving-border';
+import {Text} from 'components/typography';
+import {useState} from 'react';
+import {cn} from 'utils/cn';
+import illustration2 from '../../assets/illustration-2.svg';
+import illustrationBg from '../../assets/illustration-gradient.svg';
+import {
+ Modal,
+ ModalBody,
+ ModalContent,
+ ModalFooter,
+ ModalHeader,
+ ModalTitle,
+ ModalTrigger,
+} from './modal';
+
+const OPEN_MODAL_REGEX = /open modal/i;
+const IMPORT_JOBS_REGEX = /import past jobs from github/i;
+const GITHUB_ACTIONS_REGEX = /run github actions on shipfox/i;
+
+const meta = {
+ title: 'Components/Modal',
+ component: Modal,
+ tags: ['autodocs'],
+ parameters: {
+ layout: 'centered',
+ },
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+export const Default: Story = {
+ play: async (ctx) => {
+ const {canvasElement, step} = ctx;
+ const canvas = within(canvasElement);
+ const user = userEvent.setup();
+
+ await step('Open the modal', async () => {
+ const triggerButton = canvas.getByRole('button', {name: OPEN_MODAL_REGEX});
+ await user.click(triggerButton);
+ });
+
+ await step('Wait for dialog to appear and render', async () => {
+ await screen.findByRole('dialog');
+ await new Promise((resolve) => setTimeout(resolve, 100));
+ });
+
+ await argosScreenshot(ctx, 'Default Modal Open');
+ },
+ render: () => {
+ const [open, setOpen] = useState(false);
+
+ return (
+
+
+
+ Open Modal
+
+
+ Modal Title
+
+
+ Modal Title
+
+
+
+
+ This modal automatically adapts between dialog (desktop) and drawer (mobile) based
+ on screen size. Try resizing your browser window!
+
+
+
+ setOpen(false)}>
+ Cancel
+
+ setOpen(false)}>
+ Confirm
+
+
+
+
+
+ );
+ },
+};
+
+export const ImportForm: Story = {
+ play: async (ctx) => {
+ const {canvasElement, step} = ctx;
+ const canvas = within(canvasElement);
+ const user = userEvent.setup();
+
+ await step('Open the modal', async () => {
+ const triggerButton = canvas.getByRole('button', {name: IMPORT_JOBS_REGEX});
+ await user.click(triggerButton);
+ });
+
+ await step('Wait for dialog to appear and render', async () => {
+ await screen.findByRole('dialog');
+ await new Promise((resolve) => setTimeout(resolve, 100));
+ });
+
+ await argosScreenshot(ctx, 'Import Form Modal Open');
+ },
+ render: () => {
+ const [open, setOpen] = useState(false);
+
+ return (
+
+
+
+ Import past jobs from Github
+
+
+ Import past jobs from Github
+
+
+
+ Backfill your CI history by importing past runs from your Github repo. We'll
+ handle the rest by creating a background task to import the data for you.
+
+
+
+
+ setOpen(false)}>
+ Cancel
+
+ setOpen(false)}>
+ Import
+
+
+
+
+
+ );
+ },
+};
+
+const diffCode = `jobs:
+ build:
+- runs-on: ubuntu-latest
++ runs-on: shipfox-2vcpu-ubuntu-2404`;
+
+export const GithubActions: Story = {
+ parameters: {
+ viewport: {
+ defaultViewport: 'large',
+ },
+ },
+ play: async (ctx) => {
+ const {canvasElement, step} = ctx;
+ const canvas = within(canvasElement);
+ const user = userEvent.setup();
+
+ await step('Open the modal', async () => {
+ const triggerButton = canvas.getByRole('button', {name: GITHUB_ACTIONS_REGEX});
+ await user.click(triggerButton);
+ });
+
+ await step('Wait for dialog to appear and render', async () => {
+ await screen.findByRole('dialog');
+ await new Promise((resolve) => setTimeout(resolve, 100));
+ });
+
+ await argosScreenshot(ctx, 'Github Actions Modal Open');
+ },
+ render: () => {
+ const [open, setOpen] = useState(false);
+
+ return (
+
+
+
+ Run GitHub Actions on Shipfox
+
+
+ Run GitHub Actions on Shipfox
+
+
+
+
+ This will run your jobs on Shipfox's optimized infrastructure. Giving you
+ faster builds, and dedicated resources.
+
+
+
+
+
+
+
+
+
+
+ 6000 free credits/month to run your jobs
+
+ }
+ description="~500 builds/month. No payment required."
+ rightElement={
+
+ }
+ />
+
+
+
+
+
+
+
+
+ Update your GitHub Actions workflow
+
+
+ See docs
+
+
+
+ Replace the runs-on line in your workflow file to use Shipfox runners.
+
+
+
+
.yml',
+ code: diffCode,
+ },
+ ]}
+ defaultValue="yaml"
+ >
+
+
+ {(item) => (
+ {item.filename}
+ )}
+
+
+
+
+ {(item) => (
+
+ {item.code}
+
+ )}
+
+
+
+
+
+
+ setOpen(false)}>
+ Got it
+
+
+
+
+
+ );
+ },
+};
+
+export const OpenedModal: Story = {
+ play: async (ctx) => {
+ const {canvasElement, step} = ctx;
+ const canvas = within(canvasElement);
+ const user = userEvent.setup();
+
+ await step('Open the modal', async () => {
+ const triggerButton = canvas.getByRole('button', {name: OPEN_MODAL_REGEX});
+ await user.click(triggerButton);
+ });
+
+ await step('Wait for dialog to appear and render', async () => {
+ await screen.findByRole('dialog');
+ await new Promise((resolve) => setTimeout(resolve, 100));
+ });
+
+ await argosScreenshot(ctx, 'Opened Modal State');
+ },
+ render: () => {
+ const [open, setOpen] = useState(false);
+
+ return (
+
+
+
+ Open Modal
+
+
+ Modal Title
+
+
+ Modal Title
+
+
+
+
+ This modal automatically adapts between dialog (desktop) and drawer (mobile) based
+ on screen size. Try resizing your browser window!
+
+
+
+ setOpen(false)}>
+ Cancel
+
+ setOpen(false)}>
+ Confirm
+
+
+
+
+
+ );
+ },
+};
diff --git a/libs/react/ui/src/components/modal/modal.tsx b/libs/react/ui/src/components/modal/modal.tsx
new file mode 100644
index 00000000..0b6c3bde
--- /dev/null
+++ b/libs/react/ui/src/components/modal/modal.tsx
@@ -0,0 +1,309 @@
+import * as DialogPrimitive from '@radix-ui/react-dialog';
+import {cva} from 'class-variance-authority';
+import {Button} from 'components/button';
+import {Icon} from 'components/icon';
+import {Text} from 'components/typography';
+import {motion, type Transition} from 'framer-motion';
+import {useMediaQuery} from 'hooks/useMediaQuery';
+import {type ComponentProps, createContext, useContext} from 'react';
+import {cn} from 'utils/cn';
+import {Drawer as VaulDrawer} from 'vaul';
+
+const modalDefaultTransition: Transition = {
+ type: 'spring',
+ stiffness: 300,
+ damping: 30,
+};
+
+type ModalContextValue = {
+ breakpoint: string;
+ isDesktop: boolean;
+};
+
+const ModalContext = createContext(null);
+
+function useModalContext() {
+ const context = useContext(ModalContext);
+ if (!context) {
+ throw new Error('Modal components must be used within a Modal component');
+ }
+ return context;
+}
+
+const modalOverlayVariants = cva(
+ 'fixed inset-0 z-40 bg-background-backdrop-backdrop data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
+);
+
+const modalContentVariants = cva(
+ 'fixed left-1/2 top-1/2 z-50 flex flex-col overflow-clip bg-background-neutral-base rounded-16 w-full max-w-[576px] -translate-x-1/2 -translate-y-1/2 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 shadow-tooltip',
+);
+
+function Modal({
+ breakpoint = '(min-width: 768px)',
+ children,
+ ...props
+}: ComponentProps & {breakpoint?: string}) {
+ const isDesktop = useMediaQuery(breakpoint);
+
+ const contextValue: ModalContextValue = {
+ breakpoint,
+ isDesktop,
+ };
+
+ const Root = isDesktop ? DialogPrimitive.Root : VaulDrawer.Root;
+
+ return (
+
+ {children}
+
+ );
+}
+
+function ModalTrigger(props: ComponentProps) {
+ const {isDesktop} = useModalContext();
+
+ if (isDesktop) {
+ return ;
+ }
+
+ return ;
+}
+
+function ModalPortal(props: ComponentProps) {
+ const {isDesktop} = useModalContext();
+
+ if (isDesktop) {
+ return ;
+ }
+
+ return ;
+}
+
+function ModalClose(props: ComponentProps) {
+ const {isDesktop} = useModalContext();
+
+ if (isDesktop) {
+ return ;
+ }
+
+ return ;
+}
+
+type ModalOverlayProps = ComponentProps & {
+ animated?: boolean;
+ transition?: Transition;
+};
+
+function ModalOverlay({
+ className,
+ animated = true,
+ transition = modalDefaultTransition,
+ ...props
+}: ModalOverlayProps) {
+ const {isDesktop} = useModalContext();
+
+ if (!isDesktop) {
+ return ;
+ }
+
+ if (animated) {
+ return (
+
+
+
+ );
+ }
+
+ return ;
+}
+
+type ModalContentProps = ComponentProps & {
+ animated?: boolean;
+ transition?: Transition;
+};
+
+function ModalContent({
+ className,
+ children,
+ animated = true,
+ transition = modalDefaultTransition,
+ ...props
+}: ModalContentProps) {
+ const {isDesktop} = useModalContext();
+
+ if (!isDesktop) {
+ return (
+
+
+
+
+
+
+ );
+ }
+
+ const baseClasses = cn(modalContentVariants(), className);
+
+ return (
+
+
+
+
+
+
+ );
+}
+
+type ModalHeaderProps = ComponentProps<'div'> & {
+ title?: string;
+ showEscIndicator?: boolean;
+ showClose?: boolean;
+};
+
+function ModalHeader({
+ className,
+ title,
+ showEscIndicator = true,
+ showClose = true,
+ children,
+ ...props
+}: ModalHeaderProps) {
+ const {isDesktop} = useModalContext();
+
+ return (
+
+
+ {title ? (
+
+ {title}
+
+ ) : (
+
{children}
+ )}
+
+ {isDesktop && showEscIndicator && (
+
+ esc
+
+ )}
+ {showClose && (
+
+
+
+
+
+ )}
+
+
+
+
+ );
+}
+
+function ModalBody({className, children, ...props}: ComponentProps<'div'>) {
+ const {isDesktop} = useModalContext();
+
+ return (
+
+ {children}
+
+ );
+}
+
+function ModalFooter({className, children, ...props}: ComponentProps<'div'>) {
+ return (
+
+ );
+}
+
+type ModalTitleProps = ComponentProps;
+
+function ModalTitle({className, ...props}: ModalTitleProps) {
+ const {isDesktop} = useModalContext();
+
+ const titleClassName = cn(
+ 'font-medium text-lg leading-20 overflow-ellipsis overflow-hidden text-foreground-neutral-base',
+ className,
+ );
+
+ if (!isDesktop) {
+ return ;
+ }
+
+ return ;
+}
+
+type ModalDescriptionProps = ComponentProps;
+
+function ModalDescription({className, ...props}: ModalDescriptionProps) {
+ const {isDesktop} = useModalContext();
+
+ const descClassName = cn('text-sm leading-20 text-foreground-neutral-subtle', className);
+
+ if (!isDesktop) {
+ return ;
+ }
+
+ return ;
+}
+
+export {
+ Modal,
+ ModalPortal,
+ ModalOverlay,
+ ModalTrigger,
+ ModalClose,
+ ModalContent,
+ ModalHeader,
+ ModalBody,
+ ModalFooter,
+ ModalTitle,
+ ModalDescription,
+ modalContentVariants,
+ modalOverlayVariants,
+ modalDefaultTransition,
+};
+
+export type {
+ ModalContentProps,
+ ModalHeaderProps,
+ ModalOverlayProps,
+ ModalTitleProps,
+ ModalDescriptionProps,
+};
diff --git a/libs/react/ui/src/components/moving-border/index.ts b/libs/react/ui/src/components/moving-border/index.ts
new file mode 100644
index 00000000..df99ab69
--- /dev/null
+++ b/libs/react/ui/src/components/moving-border/index.ts
@@ -0,0 +1 @@
+export * from './moving-border';
diff --git a/libs/react/ui/src/components/typography/text.tsx b/libs/react/ui/src/components/typography/text.tsx
index d4b7c60d..ea4f7ce9 100644
--- a/libs/react/ui/src/components/typography/text.tsx
+++ b/libs/react/ui/src/components/typography/text.tsx
@@ -24,7 +24,15 @@ export type TextProps = PropsWithChildren>
bold?: boolean;
};
-export function Text({children, className, size, as, compact, bold, ...props}: TextProps) {
+export function Text({
+ children,
+ className,
+ size,
+ as,
+ compact = true,
+ bold = false,
+ ...props
+}: TextProps) {
const Component = as ?? 'p';
return (
();
+ private listeners = new Map void>>();
+ private changeHandlers = new Map void>();
+
+ getMatches(query: string): boolean {
+ if (typeof window === 'undefined') return false;
+
+ if (!this.queries.has(query)) {
+ this.queries.set(query, window.matchMedia(query));
+ this.listeners.set(query, new Set());
+ }
+
+ const mediaQuery = this.queries.get(query);
+ return mediaQuery ? mediaQuery.matches : false;
+ }
+
+ subscribe(query: string, callback: () => void): () => void {
+ if (typeof window === 'undefined') {
+ return () => {
+ // Cleanup function for SSR - no-op
+ };
+ }
+
+ if (!this.queries.has(query)) {
+ this.queries.set(query, window.matchMedia(query));
+ this.listeners.set(query, new Set());
+ }
+
+ const mediaQuery = this.queries.get(query);
+ const listeners = this.listeners.get(query);
+
+ if (!mediaQuery || !listeners) {
+ return () => {
+ // Cleanup function - no-op if query wasn't found
+ };
+ }
+
+ listeners.add(callback);
+
+ if (listeners.size === 1) {
+ const changeHandler = () => {
+ for (const cb of listeners) {
+ cb();
+ }
+ };
+ this.changeHandlers.set(query, changeHandler);
+ mediaQuery.addEventListener('change', changeHandler);
+ }
+
+ return () => {
+ listeners.delete(callback);
+
+ if (listeners.size === 0) {
+ const changeHandler = this.changeHandlers.get(query);
+ if (changeHandler) {
+ mediaQuery.removeEventListener('change', changeHandler);
+ this.changeHandlers.delete(query);
+ }
+ this.queries.delete(query);
+ this.listeners.delete(query);
+ }
+ };
+ }
+}
+
+const mediaQueryManager = new MediaQueryManager();
+
+export function useMediaQuery(query: string): boolean {
+ const [matches, setMatches] = useState(() => mediaQueryManager.getMatches(query));
+
+ useEffect(() => {
+ const updateMatches = () => {
+ setMatches(mediaQueryManager.getMatches(query));
+ };
+
+ const unsubscribe = mediaQueryManager.subscribe(query, updateMatches);
+
+ updateMatches();
+
+ return unsubscribe;
+ }, [query]);
+
+ return matches;
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 6b24dc96..8cc796ff 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -349,6 +349,9 @@ importers:
tailwind-merge:
specifier: ^3.2.0
version: 3.4.0
+ vaul:
+ specifier: ^1.1.1
+ version: 1.1.2(@types/react-dom@19.1.6(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
devDependencies:
'@argos-ci/cli':
specifier: ^3.2.1
@@ -5428,6 +5431,12 @@ packages:
resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
hasBin: true
+ vaul@1.1.2:
+ resolution: {integrity: sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA==}
+ peerDependencies:
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc
+
vfile-message@4.0.3:
resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==}
@@ -10709,6 +10718,15 @@ snapshots:
uuid@9.0.1: {}
+ vaul@1.1.2(@types/react-dom@19.1.6(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.1.1(react@19.1.1))(react@19.1.1):
+ dependencies:
+ '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.1.6(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
+ react: 19.1.1
+ react-dom: 19.1.1(react@19.1.1)
+ transitivePeerDependencies:
+ - '@types/react'
+ - '@types/react-dom'
+
vfile-message@4.0.3:
dependencies:
'@types/unist': 3.0.3