diff --git a/.storybook/global.css b/.storybook/global.css index 0ca6719f3..953125c40 100644 --- a/.storybook/global.css +++ b/.storybook/global.css @@ -2,7 +2,13 @@ html { background: var(--vscode-sideBar-background); } +/* Typography lives on body like real webviews, so portalled content + (menus, tooltips) inherits it too. */ body { + color: var(--vscode-editor-foreground); + font-family: var(--vscode-font-family); + font-weight: var(--vscode-font-weight); + font-size: var(--vscode-font-size); margin: 0; padding: 0; overflow: hidden; @@ -15,10 +21,6 @@ body { #root { overscroll-behavior-x: none; background-color: var(--vscode-sideBar-background); - color: var(--vscode-editor-foreground); - font-family: var(--vscode-font-family); - font-weight: var(--vscode-font-weight); - font-size: var(--vscode-font-size); margin: 0; max-width: 100%; /* arbitrary size choice for the rough VSCode sidebar size */ diff --git a/eslint.config.mjs b/eslint.config.mjs index 8198aae6b..13081916f 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -208,6 +208,25 @@ export default defineConfig( }, }, + // Keep the UI package independent from other workspace packages. + { + files: ["packages/ui/**/*.{ts,tsx}"], + rules: { + "import-x/no-relative-packages": "error", + "no-restricted-imports": [ + "error", + { + patterns: [ + { + group: ["@repo/*"], + message: "packages/ui must not import other workspace packages.", + }, + ], + }, + ], + }, + }, + // React rules with type-checked analysis (covers hooks, JSX, DOM) { files: ["packages/**/*.{ts,tsx}"], diff --git a/packages/ui/README.md b/packages/ui/README.md index 04b76f5bf..4e4d4028a 100644 --- a/packages/ui/README.md +++ b/packages/ui/README.md @@ -23,14 +23,37 @@ import "@repo/ui/tokens.css"; `codicon.css` re-exports the codicon font and classes. +## Overlays + +`Tooltip`, `ContextMenu`, and `DropdownMenu` wrap the Radix primitives and +are styled to match the native VS Code menu and hover widgets. The menus +expose Radix's compound parts as flat named exports +(`DropdownMenuTrigger`, `DropdownMenuItem`, …); `Tooltip` is a single +component taking a `content` prop, with a 500ms show delay matching VS +Code's `workbench.hover.delay` default. Each `Tooltip` mounts its own +Radix provider, so the cross-trigger skip-delay window is not shared +between tooltips; if that matters, expose a shared provider. Checkbox/ +radio items, labels, and keybinding hints are not wrapped yet. + +Overlay content is portalled to `body` and inherits webview typography +from there; surface colors come from the `--ui-menu-*` and +`--ui-tooltip-*` tokens. High contrast follows the VS Code contrast +variables, and the styles handle `forced-colors` and +`prefers-reduced-motion`. + ## Rules The package is shaped for a future standalone NPM split. Keep it that way: - No `workspace:*` runtime dependencies (no `@repo/shared`, - `@repo/webview-shared`). -- `react` stays a peer dependency. + `@repo/webview-shared`); ESLint rejects `@repo/*` imports and relative + cross-package imports in this package. +- `react` stays a peer dependency; the Radix overlay primitives are the + only other runtime dependencies. - New entry points go in the `exports` map; consumers never deep-import `src/`. +- Shared internals are reached through `package.json` subpath imports + (`#cx`, `#storybook`). These resolve only inside this package and ship + with it, so they survive a standalone NPM split. - New components define their VS Code mappings in `tokens.css`, not inline `--vscode-*` references. diff --git a/packages/ui/package.json b/packages/ui/package.json index 9e5fb4fed..6a046c191 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -4,6 +4,10 @@ "description": "Shared component library for VS Code webviews", "private": true, "type": "module", + "sideEffects": [ + "**/*.css", + "./storybook.preview.ts" + ], "exports": { ".": { "types": "./src/index.ts", @@ -12,10 +16,17 @@ "./codicon.css": "./src/codicon.css", "./tokens.css": "./src/tokens.css" }, + "imports": { + "#cx": "./src/cx.ts", + "#storybook": "./src/storybook.ts" + }, "scripts": { "typecheck": "tsc --noEmit" }, "dependencies": { + "@radix-ui/react-context-menu": "catalog:", + "@radix-ui/react-dropdown-menu": "catalog:", + "@radix-ui/react-tooltip": "catalog:", "@vscode/codicons": "catalog:" }, "peerDependencies": { diff --git a/packages/ui/src/components/ContextMenu/ContextMenu.stories.tsx b/packages/ui/src/components/ContextMenu/ContextMenu.stories.tsx new file mode 100644 index 000000000..d22a6d0ee --- /dev/null +++ b/packages/ui/src/components/ContextMenu/ContextMenu.stories.tsx @@ -0,0 +1,119 @@ +import { expect, screen, userEvent, waitFor, within } from "storybook/test"; + +import { FOUR_THEME_MODES } from "#storybook"; + +import { + ContextMenu, + ContextMenuContent, + ContextMenuItem, + ContextMenuSeparator, + ContextMenuSub, + ContextMenuSubContent, + ContextMenuSubTrigger, + ContextMenuTrigger, +} from "./ContextMenu"; + +import type { Meta, StoryObj } from "@storybook/react-vite"; + +const TARGET_STYLE: React.CSSProperties = { + display: "grid", + placeItems: "center", + width: 240, + height: 120, + border: "1px dashed var(--ui-description-foreground)", +}; + +const MenuExample = (): React.JSX.Element => ( + + +
Right-click here
+
+ + + + + + + + + + More actions + + Open logs + Edit settings + + + +
+); + +const meta: Meta = { + title: "UI/ContextMenu", + component: MenuExample, +}; + +export default meta; +type Story = StoryObj; + +/* Right-click at the target's center; without coords the contextmenu + event fires at (0,0) and the menu opens detached from the target. */ +async function rightClickCenter(target: Element): Promise { + const rect = target.getBoundingClientRect(); + await userEvent.pointer({ + keys: "[MouseRight]", + target, + coords: { + clientX: rect.left + rect.width / 2, + clientY: rect.top + rect.height / 2, + }, + }); +} + +export const Closed: Story = {}; + +/* Chromatic crops to in-flow content, so snapshot stories reserve space + for the portalled menu. */ +const OVERLAY_SPACE: React.CSSProperties = { width: 620, height: 400 }; + +/* Opens the menu and its submenu so Chromatic snapshots the open state. */ +export const Open: Story = { + decorators: [ + (Story) => ( +
+ +
+ ), + ], + parameters: { chromatic: { modes: FOUR_THEME_MODES } }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + await rightClickCenter(canvas.getByText("Right-click here")); + const menu = await screen.findByRole("menu"); + // Radix moves focus into the menu on open + await waitFor(() => + expect(menu.contains(document.activeElement)).toBe(true), + ); + // Keyboard avoids the submenu hover-open delay + await userEvent.keyboard("{End}{ArrowRight}"); + await screen.findByRole("menuitem", { name: "Open logs" }); + }, +}; + +export const EscapeClosesMenu: Story = { + parameters: { chromatic: { disableSnapshot: true } }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + await rightClickCenter(canvas.getByText("Right-click here")); + await screen.findByRole("menu"); + await userEvent.keyboard("{Escape}"); + await waitFor(() => + expect(screen.queryByRole("menu")).not.toBeInTheDocument(), + ); + }, +}; diff --git a/packages/ui/src/components/ContextMenu/ContextMenu.tsx b/packages/ui/src/components/ContextMenu/ContextMenu.tsx new file mode 100644 index 000000000..7885d68e3 --- /dev/null +++ b/packages/ui/src/components/ContextMenu/ContextMenu.tsx @@ -0,0 +1,100 @@ +import * as ContextMenuPrimitive from "@radix-ui/react-context-menu"; + +import { cx } from "#cx"; + +import "../menu.css"; + +import type { ComponentPropsWithRef } from "react"; + +export const ContextMenu = ContextMenuPrimitive.Root; +export const ContextMenuTrigger = ContextMenuPrimitive.Trigger; +export const ContextMenuGroup = ContextMenuPrimitive.Group; +export const ContextMenuSub = ContextMenuPrimitive.Sub; + +export function ContextMenuContent({ + className, + // Native menus wrap focus when arrowing past the last item + loop = true, + ...props +}: ComponentPropsWithRef< + typeof ContextMenuPrimitive.Content +>): React.JSX.Element { + return ( + + + + ); +} + +export function ContextMenuSubContent({ + className, + sideOffset = 2, + loop = true, + ...props +}: ComponentPropsWithRef< + typeof ContextMenuPrimitive.SubContent +>): React.JSX.Element { + return ( + + + + ); +} + +export function ContextMenuItem({ + className, + ...props +}: ComponentPropsWithRef): React.JSX.Element { + return ( + + ); +} + +export function ContextMenuSubTrigger({ + className, + children, + ...props +}: ComponentPropsWithRef< + typeof ContextMenuPrimitive.SubTrigger +>): React.JSX.Element { + return ( + + {children} + + ); +} + +export function ContextMenuSeparator({ + className, + ...props +}: ComponentPropsWithRef< + typeof ContextMenuPrimitive.Separator +>): React.JSX.Element { + return ( + + ); +} diff --git a/packages/ui/src/components/DropdownMenu/DropdownMenu.stories.tsx b/packages/ui/src/components/DropdownMenu/DropdownMenu.stories.tsx new file mode 100644 index 000000000..b5de2f917 --- /dev/null +++ b/packages/ui/src/components/DropdownMenu/DropdownMenu.stories.tsx @@ -0,0 +1,137 @@ +import { expect, screen, userEvent, waitFor, within } from "storybook/test"; + +import { FOUR_THEME_MODES, STORY_TRIGGER_CLASS } from "#storybook"; + +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuSub, + DropdownMenuSubContent, + DropdownMenuSubTrigger, + DropdownMenuTrigger, +} from "./DropdownMenu"; + +import type { Meta, StoryObj } from "@storybook/react-vite"; + +const MenuExample = (): React.JSX.Element => ( + + + + + + + + + + + + + + More actions + + Open logs + Edit settings + + + + +); + +const meta: Meta = { + title: "UI/DropdownMenu", + component: MenuExample, +}; + +export default meta; +type Story = StoryObj; + +export const Closed: Story = {}; + +/* Chromatic crops to in-flow content, so snapshot stories reserve space + for the portalled menu. */ +const OVERLAY_SPACE: React.CSSProperties = { width: 540, height: 320 }; + +/* Opens the menu and its submenu so Chromatic snapshots the open state. */ +export const Open: Story = { + decorators: [ + (Story) => ( +
+ +
+ ), + ], + parameters: { chromatic: { modes: FOUR_THEME_MODES } }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + await userEvent.click( + canvas.getByRole("button", { name: "Workspace actions" }), + ); + const menu = await screen.findByRole("menu"); + // Radix moves focus into the menu on open + await waitFor(() => + expect(menu.contains(document.activeElement)).toBe(true), + ); + // Keyboard avoids the submenu hover-open delay + await userEvent.keyboard("{End}{ArrowRight}"); + await screen.findByRole("menuitem", { name: "Open logs" }); + }, +}; + +/* Long menus scroll. The default cap is the viewport space Radix + reports; the story lowers it via style. */ +export const ManyItems: Story = { + decorators: [ + (Story) => ( +
+ +
+ ), + ], + render: () => ( + + + + + + {Array.from({ length: 40 }, (_, i) => ( + Workspace {i + 1} + ))} + + + ), + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + await userEvent.click( + canvas.getByRole("button", { name: "Workspace actions" }), + ); + const menu = await screen.findByRole("menu"); + await waitFor(() => + expect(menu.scrollHeight).toBeGreaterThan(menu.clientHeight), + ); + }, +}; + +export const FocusReturnsToTrigger: Story = { + parameters: { chromatic: { disableSnapshot: true } }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const trigger = canvas.getByRole("button", { name: "Workspace actions" }); + await userEvent.click(trigger); + await screen.findByRole("menu"); + await userEvent.keyboard("{Escape}"); + await waitFor(() => expect(trigger).toHaveFocus()); + await expect(screen.queryByRole("menu")).not.toBeInTheDocument(); + }, +}; diff --git a/packages/ui/src/components/DropdownMenu/DropdownMenu.tsx b/packages/ui/src/components/DropdownMenu/DropdownMenu.tsx new file mode 100644 index 000000000..4ebdfd449 --- /dev/null +++ b/packages/ui/src/components/DropdownMenu/DropdownMenu.tsx @@ -0,0 +1,106 @@ +import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"; + +import { cx } from "#cx"; + +import "../menu.css"; + +import type { ComponentPropsWithRef } from "react"; + +export const DropdownMenu = DropdownMenuPrimitive.Root; +export const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger; +export const DropdownMenuGroup = DropdownMenuPrimitive.Group; +export const DropdownMenuSub = DropdownMenuPrimitive.Sub; + +export function DropdownMenuContent({ + className, + sideOffset = 2, + align = "start", + // Native menus wrap focus when arrowing past the last item + loop = true, + ...props +}: ComponentPropsWithRef< + typeof DropdownMenuPrimitive.Content +>): React.JSX.Element { + return ( + + + + ); +} + +export function DropdownMenuSubContent({ + className, + sideOffset = 2, + loop = true, + ...props +}: ComponentPropsWithRef< + typeof DropdownMenuPrimitive.SubContent +>): React.JSX.Element { + return ( + + + + ); +} + +export function DropdownMenuItem({ + className, + ...props +}: ComponentPropsWithRef< + typeof DropdownMenuPrimitive.Item +>): React.JSX.Element { + return ( + + ); +} + +export function DropdownMenuSubTrigger({ + className, + children, + ...props +}: ComponentPropsWithRef< + typeof DropdownMenuPrimitive.SubTrigger +>): React.JSX.Element { + return ( + + {children} + + ); +} + +export function DropdownMenuSeparator({ + className, + ...props +}: ComponentPropsWithRef< + typeof DropdownMenuPrimitive.Separator +>): React.JSX.Element { + return ( + + ); +} diff --git a/packages/ui/src/components/Tooltip/Tooltip.css b/packages/ui/src/components/Tooltip/Tooltip.css new file mode 100644 index 000000000..c360f2042 --- /dev/null +++ b/packages/ui/src/components/Tooltip/Tooltip.css @@ -0,0 +1,36 @@ +/* Matches the native workbench hover widget; typography inherits from + the webview body. */ +.ui-tooltip { + box-sizing: border-box; + max-width: min(700px, calc(100vw - 16px)); + /* Cap to the space Radix reports; oversized content scrolls */ + max-height: var(--radix-tooltip-content-available-height); + overflow-y: auto; + padding: 4px 8px; + color: var(--ui-tooltip-foreground); + background: var(--ui-tooltip-background); + border: 1px solid var(--ui-tooltip-border); + border-radius: 5px; + box-shadow: var(--ui-widget-shadow); + overflow-wrap: break-word; + z-index: 40; + animation: ui-tooltip-fade-in 100ms linear; +} + +@keyframes ui-tooltip-fade-in { + from { + opacity: 0; + } +} + +@media (prefers-reduced-motion: reduce) { + .ui-tooltip { + animation: none; + } +} + +@media (forced-colors: active) { + .ui-tooltip { + border-color: CanvasText; + } +} diff --git a/packages/ui/src/components/Tooltip/Tooltip.stories.tsx b/packages/ui/src/components/Tooltip/Tooltip.stories.tsx new file mode 100644 index 000000000..4606da88d --- /dev/null +++ b/packages/ui/src/components/Tooltip/Tooltip.stories.tsx @@ -0,0 +1,117 @@ +import { expect, screen, userEvent, waitFor } from "storybook/test"; + +import { FOUR_THEME_MODES, STORY_TRIGGER_CLASS } from "#storybook"; + +import { Tooltip } from "./Tooltip"; + +import type { Meta, StoryObj } from "@storybook/react-vite"; + +const LONG_TEXT = + "This workspace has been running for 14 days without a rebuild. " + + "Stopping it frees compute resources, and any unsaved changes in " + + "the home volume are preserved until the next start."; + +const meta: Meta = { + title: "UI/Tooltip", + component: Tooltip, + args: { + content: "Stops the workspace", + // Instant in stories; the component default is 500ms + delayDuration: 0, + children: ( + + ), + }, +}; + +export default meta; +type Story = StoryObj; + +/* findByRole("tooltip") matches Radix's visually hidden a11y copy, so + open assertions target the visible .ui-tooltip bubble instead. */ +async function openTooltipWithKeyboard(): Promise { + await userEvent.tab(); + await screen.findByRole("tooltip"); + const tooltip = document.querySelector(".ui-tooltip"); + if (!tooltip) { + throw new Error("visible tooltip content not found"); + } + await waitFor(() => expect(tooltip).toBeVisible()); + return tooltip; +} + +/* Chromatic crops to in-flow content; the tooltip opens upward, so the + wrapper reserves space above a bottom-anchored trigger. */ +function tooltipSpace( + width: number, + height: number | string, +): (Story: React.ComponentType) => React.JSX.Element { + const space: React.CSSProperties = { + width, + height, + // Keeps the trigger's focus outline inside the cropped bounds + paddingBottom: 8, + display: "flex", + alignItems: "flex-end", + justifyContent: "center", + }; + return function TooltipSpace(Story) { + return ( +
+ +
+ ); + }; +} + +export const Closed: Story = {}; + +/* Keyboard focus opens instantly and proves keyboard users get the tooltip. */ +export const Open: Story = { + decorators: [tooltipSpace(280, 100)], + parameters: { chromatic: { modes: FOUR_THEME_MODES } }, + play: async () => { + await openTooltipWithKeyboard(); + }, +}; + +/* Long content wraps inside the native hover widget's max-width. */ +export const LongContent: Story = { + decorators: [tooltipSpace(720, 160)], + args: { + content: LONG_TEXT, + }, + play: async () => { + await openTooltipWithKeyboard(); + }, +}; + +/* Overflowing content scrolls inside the tooltip. The default cap is the + viewport space Radix reports; the story lowers it via style. */ +export const OverflowScrolls: Story = { + decorators: [tooltipSpace(720, 240)], + args: { + content: Array.from({ length: 8 }, () => LONG_TEXT).join(" "), + style: { maxHeight: 160 }, + }, + play: async () => { + const tooltip = await openTooltipWithKeyboard(); + await waitFor(() => + expect(tooltip.scrollHeight).toBeGreaterThan(tooltip.clientHeight), + ); + }, +}; + +export const ClosesOnEscape: Story = { + parameters: { chromatic: { disableSnapshot: true } }, + play: async () => { + await userEvent.tab(); + await screen.findByRole("tooltip"); + await userEvent.keyboard("{Escape}"); + await waitFor(() => + expect(screen.queryByRole("tooltip")).not.toBeInTheDocument(), + ); + }, +}; diff --git a/packages/ui/src/components/Tooltip/Tooltip.tsx b/packages/ui/src/components/Tooltip/Tooltip.tsx new file mode 100644 index 000000000..180b8c9e5 --- /dev/null +++ b/packages/ui/src/components/Tooltip/Tooltip.tsx @@ -0,0 +1,44 @@ +import * as TooltipPrimitive from "@radix-ui/react-tooltip"; + +import { cx } from "#cx"; + +import "./Tooltip.css"; + +import type { ComponentPropsWithRef, ReactNode } from "react"; + +export interface TooltipProps extends Omit< + ComponentPropsWithRef, + "content" +> { + content: ReactNode; + /** The trigger element; must accept a forwarded ref (asChild). */ + children: ReactNode; + /** Show delay in ms. Defaults to VS Code's workbench.hover.delay. */ + delayDuration?: number; +} + +export function Tooltip({ + content, + children, + className, + sideOffset = 4, + delayDuration = 500, + ...props +}: TooltipProps): React.JSX.Element { + return ( + + + {children} + + + {content} + + + + + ); +} diff --git a/packages/ui/src/components/menu.css b/packages/ui/src/components/menu.css new file mode 100644 index 000000000..d5b2b3c8a --- /dev/null +++ b/packages/ui/src/components/menu.css @@ -0,0 +1,108 @@ +/* Shared ContextMenu/DropdownMenu surface matching the native menu + widget; typography inherits from the webview body. */ +.ui-menu { + --ui-menu-gutter: 2em; + --ui-menu-highlight-foreground: var(--ui-menu-selection-foreground); + --ui-menu-highlight-background: var(--ui-menu-selection-background); + --ui-menu-disabled-foreground: var(--ui-disabled-foreground); + + box-sizing: border-box; + min-width: 160px; + /* Cap to the space Radix reports (named per primitive) so long menus scroll */ + max-height: var( + --radix-dropdown-menu-content-available-height, + var(--radix-context-menu-content-available-height) + ); + overflow-y: auto; + padding: 4px; + color: var(--ui-menu-foreground); + background: var(--ui-menu-background); + border: 1px solid var(--ui-menu-border); + border-radius: var(--ui-menu-radius); + box-shadow: var(--ui-widget-shadow); + outline: 0; + z-index: 40; + animation: ui-menu-fade-in 83ms linear; +} + +.ui-menu__item { + position: relative; + display: flex; + align-items: center; + height: 24px; + /* Native label inset; the gutters double as the check/icon columns. */ + padding: 0 var(--ui-menu-gutter); + line-height: 1; + white-space: nowrap; + border-radius: var(--ui-menu-item-radius); + cursor: default; + user-select: none; + outline: 1px solid transparent; + outline-offset: -1px; +} + +/* Selection border only resolves in high contrast; an open submenu + keeps its parent highlighted, like native. */ +.ui-menu__item[data-highlighted], +.ui-menu__item[data-state="open"] { + forced-color-adjust: none; + color: var(--ui-menu-highlight-foreground); + background: var(--ui-menu-highlight-background); + outline-color: var(--ui-menu-selection-border); +} + +.ui-menu__item[data-disabled] { + color: var(--ui-menu-disabled-foreground); +} + +/* Leading icons sit in the left gutter so labels stay aligned */ +.ui-menu__item > .codicon:not(.ui-menu__submenu-indicator) { + position: absolute; + left: 0; + width: var(--ui-menu-gutter); + text-align: center; +} + +/* Chevron in the right gutter, like the native indicator */ +.ui-menu__submenu-indicator { + position: absolute; + right: 0; + width: var(--ui-menu-gutter); + text-align: center; +} + +.ui-menu__item[data-disabled] .ui-menu__submenu-indicator { + opacity: 0.4; +} + +.ui-menu__separator { + height: 1px; + margin: 5px 0; + background: var(--ui-menu-separator); +} + +@keyframes ui-menu-fade-in { + from { + opacity: 0; + } +} + +@media (prefers-reduced-motion: reduce) { + .ui-menu { + animation: none; + } +} + +@media (forced-colors: active) { + .ui-menu { + --ui-menu-highlight-foreground: HighlightText; + --ui-menu-highlight-background: Highlight; + --ui-menu-disabled-foreground: GrayText; + + border-color: CanvasText; + } + + .ui-menu__separator { + background: CanvasText; + } +} diff --git a/packages/ui/src/cx.ts b/packages/ui/src/cx.ts new file mode 100644 index 000000000..e8e639d30 --- /dev/null +++ b/packages/ui/src/cx.ts @@ -0,0 +1,4 @@ +/** Joins the truthy class names, dropping conditional falsy entries. */ +export function cx(...classes: Array): string { + return classes.filter(Boolean).join(" "); +} diff --git a/packages/ui/src/foundations.stories.tsx b/packages/ui/src/foundations.stories.tsx index cbd5132fb..830edc1f4 100644 --- a/packages/ui/src/foundations.stories.tsx +++ b/packages/ui/src/foundations.stories.tsx @@ -1,5 +1,6 @@ import { useEffect, useState } from "react"; +import { FOUR_THEME_MODES } from "./storybook"; import { useVscodeTheme } from "./useVscodeTheme"; import type { Meta, StoryObj } from "@storybook/react-vite"; @@ -132,12 +133,7 @@ const meta: Meta = { // Snapshot every theme; tokens are the single theming surface, so this // is where theme regressions show up. chromatic: { - modes: { - light: { theme: "light" }, - dark: { theme: "dark" }, - "high-contrast": { theme: "high-contrast" }, - "high-contrast-light": { theme: "high-contrast-light" }, - }, + modes: FOUR_THEME_MODES, }, }, }; diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts index fb38844db..c1db9bf1b 100644 --- a/packages/ui/src/index.ts +++ b/packages/ui/src/index.ts @@ -1 +1,24 @@ +export { + ContextMenu, + ContextMenuContent, + ContextMenuGroup, + ContextMenuItem, + ContextMenuSeparator, + ContextMenuSub, + ContextMenuSubContent, + ContextMenuSubTrigger, + ContextMenuTrigger, +} from "./components/ContextMenu/ContextMenu"; +export { + DropdownMenu, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuSub, + DropdownMenuSubContent, + DropdownMenuSubTrigger, + DropdownMenuTrigger, +} from "./components/DropdownMenu/DropdownMenu"; +export { Tooltip, type TooltipProps } from "./components/Tooltip/Tooltip"; export { useVscodeTheme, type VscodeThemeKind } from "./useVscodeTheme"; diff --git a/packages/ui/src/story-helpers.css b/packages/ui/src/story-helpers.css new file mode 100644 index 000000000..a9f6edeb4 --- /dev/null +++ b/packages/ui/src/story-helpers.css @@ -0,0 +1,19 @@ +/* Story stand-in: a VS Code secondary button like consuming webviews render. */ +.story-trigger { + padding: 0.4em 1em; + border: 1px solid var(--vscode-button-border, transparent); + border-radius: 2px; + background: var(--vscode-button-secondaryBackground); + color: var(--vscode-button-secondaryForeground); + font: inherit; + cursor: pointer; +} + +.story-trigger:hover { + background: var(--vscode-button-secondaryHoverBackground); +} + +.story-trigger:focus-visible { + outline: 1px solid var(--vscode-focusBorder); + outline-offset: 2px; +} diff --git a/packages/ui/src/storybook.ts b/packages/ui/src/storybook.ts new file mode 100644 index 000000000..2b2a386f0 --- /dev/null +++ b/packages/ui/src/storybook.ts @@ -0,0 +1,11 @@ +import "./story-helpers.css"; + +export const FOUR_THEME_MODES = { + light: { theme: "light" }, + dark: { theme: "dark" }, + "high-contrast": { theme: "high-contrast" }, + "high-contrast-light": { theme: "high-contrast-light" }, +} as const; + +/* Story stand-in for a webview-styled button. */ +export const STORY_TRIGGER_CLASS = "story-trigger"; diff --git a/packages/ui/src/tokens.css b/packages/ui/src/tokens.css index e0cae1cbb..a1b333f75 100644 --- a/packages/ui/src/tokens.css +++ b/packages/ui/src/tokens.css @@ -45,6 +45,37 @@ --ui-error-foreground: var(--vscode-errorForeground); --ui-warning-foreground: var(--vscode-editorWarning-foreground); + /* Disabled text */ + --ui-disabled-foreground: var(--vscode-disabledForeground); + + /* widget.shadow is null in high contrast, hiding the shadow like native */ + --ui-widget-shadow: 0 0 12px var(--vscode-widget-shadow, transparent); + + /* Menus; radius fallbacks predate the corner radius tokens */ + --ui-menu-background: var(--vscode-menu-background); + --ui-menu-foreground: var(--vscode-menu-foreground); + --ui-menu-border: var( + --vscode-menu-border, + var(--vscode-editorWidget-border, transparent) + ); + --ui-menu-selection-background: var( + --vscode-menu-selectionBackground, + transparent + ); + --ui-menu-selection-foreground: var( + --vscode-menu-selectionForeground, + var(--ui-menu-foreground) + ); + --ui-menu-selection-border: var(--vscode-menu-selectionBorder, transparent); + --ui-menu-separator: var(--vscode-menu-separatorBackground); + --ui-menu-radius: var(--vscode-cornerRadius-large, 5px); + --ui-menu-item-radius: var(--vscode-cornerRadius-medium, 4px); + + /* Tooltip (native workbench hover widget) */ + --ui-tooltip-background: var(--vscode-editorHoverWidget-background); + --ui-tooltip-foreground: var(--vscode-editorHoverWidget-foreground); + --ui-tooltip-border: var(--vscode-editorHoverWidget-border); + /* Only defined by high contrast themes; transparent elsewhere */ --ui-contrast-border: var(--vscode-contrastBorder, transparent); --ui-contrast-active-border: var(--vscode-contrastActiveBorder, transparent); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9666dc1ec..76834986c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,6 +7,15 @@ settings: catalogs: default: + '@radix-ui/react-context-menu': + specifier: ^2.3.3 + version: 2.3.3 + '@radix-ui/react-dropdown-menu': + specifier: ^2.1.20 + version: 2.1.20 + '@radix-ui/react-tooltip': + specifier: ^1.2.12 + version: 1.2.12 '@rolldown/plugin-babel': specifier: ^0.2.3 version: 0.2.3 @@ -438,6 +447,15 @@ importers: packages/ui: dependencies: + '@radix-ui/react-context-menu': + specifier: 'catalog:' + version: 2.3.3(@types/react-dom@19.2.3)(@types/react@19.2.17)(react-dom@19.2.7)(react@19.2.7) + '@radix-ui/react-dropdown-menu': + specifier: 'catalog:' + version: 2.1.20(@types/react-dom@19.2.3)(@types/react@19.2.17)(react-dom@19.2.7)(react@19.2.7) + '@radix-ui/react-tooltip': + specifier: 'catalog:' + version: 1.2.12(@types/react-dom@19.2.3)(@types/react@19.2.17)(react-dom@19.2.7)(react@19.2.7) '@vscode/codicons': specifier: 'catalog:' version: 0.0.45 @@ -1015,6 +1033,21 @@ packages: '@noble/hashes': optional: true + '@floating-ui/core@1.8.0': + resolution: {integrity: sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==} + + '@floating-ui/dom@1.8.0': + resolution: {integrity: sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==} + + '@floating-ui/react-dom@2.1.9': + resolution: {integrity: sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@floating-ui/utils@0.2.12': + resolution: {integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==} + '@humanfs/core@0.19.2': resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} engines: {node: '>=18.18.0'} @@ -1511,6 +1544,311 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} + '@radix-ui/primitive@1.1.5': + resolution: {integrity: sha512-d86WIWFYNtGA0H/d8exstrTRTp7eWJYlYJbtNofxr/3ljupZYn6EFDG/Qgu/0Kc8v7yMUxySagqJsL1+PdYjWg==} + + '@radix-ui/react-arrow@1.1.11': + resolution: {integrity: sha512-Kdil9BB1rIFC/khmf4hC35bn8701AJcizTU7G7cUbEbk5XqqbjDuHW60uUfKqO5WojjZcbAW51Q7P0hRmMLw8A==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-collection@1.1.12': + resolution: {integrity: sha512-nb67INpE0IahJKN7EYPp9m9YGwYeKlnzxT3MwXVkgCskaSJia97kG4T0ywpjNUSSnoJk/uvk12V8vbrEHEj+/Q==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-compose-refs@1.1.3': + resolution: {integrity: sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-context-menu@2.3.3': + resolution: {integrity: sha512-PS+gKE0z2prJ74Y0sM+brAGK4mYOHIR7TlcV5EJgUQ6E0xMvyswkK2X4yRqyganrzsRL+WCSKAPu0NQITICRWg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-context@1.2.0': + resolution: {integrity: sha512-fOE+JtN9rygNZkCnHRBEP0TAvLldlhyOxMsbwFvTP4nAs+nBmfnna+o/Zski2wkmY1YMrFC0aSzsHoLY47iLrg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-direction@1.1.2': + resolution: {integrity: sha512-C3vFhbyi4SW3PmbAi6Awpu4OzJtd0MxGurvSsYtr7p7nM8RNB3VAF3CUmnp2j50knpkrRcB7+ycVXzgLgF6yNA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-dismissable-layer@1.1.15': + resolution: {integrity: sha512-b0XaRlzn2QKuo10XyNgi2DAJDf5XC9d1nD3FJcuvCjbR7+4Ad28zmZsLsqx+hvDEzMnRuZaZxZm9gYObV6RmRA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-dropdown-menu@2.1.20': + resolution: {integrity: sha512-slfm+rRaZRuQBvHq60lXvSVUPhid0IPtjSZzIuUlWZMUs01iYZNlGS3mJgRD3ChLQVBAYlKiL/tFyWGX+dz8Xw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-focus-guards@1.1.4': + resolution: {integrity: sha512-cot/aB/mOm0IYVYTTmQcEEK1M48lZWi8FlYe5nDPQQ8NYZUlXEFgncJ9p2Kzer3RKSrY7cTTpEMLZKNo9QoP5Q==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-focus-scope@1.1.12': + resolution: {integrity: sha512-jjk/lqTeNL0azUx5ZYzVrl4NgaDIrdzTNE4mABV9yBFI7FQqN7pIgzV1bTleUezP2QiTGA1BFTqY8MegDgWX9A==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-id@1.1.2': + resolution: {integrity: sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-menu@2.1.20': + resolution: {integrity: sha512-VsUrXxFe9d2ScbZF0fR/oPR1+qjyeLs5p0jzG8h90puMoA9bq4SirYlXbE+USRg9Q2qTeJSFNqjw2nts8jJe4w==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-popper@1.3.3': + resolution: {integrity: sha512-mS7dGpyjv6b+gsDjLF7e0ia1W4Im1B1hSCy2yuXlHuvnZxHKagfDaobt/KAKt27EpZMit2pss8eJBVyVjEWM+g==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-portal@1.1.13': + resolution: {integrity: sha512-z3oXfmaHLJTF1wktbjgD6cn9jiEbq3WSondB10LIuIt2m2Ym4iJlrW04/euMwENDdWDdE7z+OuY7Qyp1YpRSwA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-presence@1.1.7': + resolution: {integrity: sha512-zBZ4QM5XG3JRanDmqXYf3MD6th4AFXFmgU6KNMFzUaV6F3uw9I5/zjMUvFriSEn5ewo1nxuibvyxJdmLlDcslA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-primitive@2.1.7': + resolution: {integrity: sha512-bC3NiwsprbxKjuon9l7X6BUTw7FPVzEYaL92MPEY5SCd/9hUTPXVFtVwRix7778wtRsVao+zE062gL79FZleeQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-roving-focus@1.1.15': + resolution: {integrity: sha512-40svmmugfM3mUN7VUDGVE1tQGOhyi8enlGD0CNJEcMM36C1f71PKM21DFgNHUfem0XnA+d8H8oN3Z9ZpJjSslg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-slot@1.3.0': + resolution: {integrity: sha512-MojKku4U/miO8Av4Dkb+ctMAQx7JmY96LmtDQlAarCRtd7rN52QCSzBF+XAvr5S6coSVj9HEPBgHAHKEJVk/WA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-tooltip@1.2.12': + resolution: {integrity: sha512-U3HoftgWnmla78vzQbLvKKb7bUYJxoiiqYFzp1wu/TBMyDqMZSuCl3aRICsD6EfVEwcJD2mumGDGUXLFVqQHKA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-use-callback-ref@1.1.2': + resolution: {integrity: sha512-xCso9j1/u8sEgP1RNHjFrXJLApL8LiqOkI1R4ywuN00rxWdYg4oQXuwKLS3i0j5NWLromUD27/4nlxj2UFVvIw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-controllable-state@1.2.3': + resolution: {integrity: sha512-PLzC90MS+ReootmjC597dvopoelpZ8Q61HJkDXZSExitIq7PL55vHNnesAHwguHK0aPfBnpdNzQtv1uliaqQrA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-effect-event@0.0.3': + resolution: {integrity: sha512-6c8ZqvPTWILEKnyVkP53EGRCcpnJiKTC21sS/6R1GF5xKyHJJWQEPfkqlcgUkdRQivd6tb23abUwe4ngWmY0JA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-is-hydrated@0.1.1': + resolution: {integrity: sha512-qwOiz4Tjo8CNnrOLAYUMXeZwDzXgXpvK4TKQPmWLECM9XoWvA6+0Z2/7Ag3A4ivjS4ovbLJPbskkxioFyBhr8A==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-layout-effect@1.1.2': + resolution: {integrity: sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-rect@1.1.2': + resolution: {integrity: sha512-d8a+bBY/FxikNPlgJJoaBHZX+zKVbWHYJGTLnLvveQgFSTntkGdEKv3JDtHrMS0DNYpllz2nRsTLGLKYttbpmw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-size@1.1.2': + resolution: {integrity: sha512-giWQp+4mxjBPt4KZ0MmyuykFNWfbDxKt4x+fPkRYmgRFJSbCZFzUglvMb/Kjn38tm10YP4ufiQZDx3zna4LU6w==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-visually-hidden@1.2.7': + resolution: {integrity: sha512-1wNZBggTDK3GRuuQ6nP4k2yi7a6l7I5qbMPbZcRsrGsGVead/f/d5FhEzUvqFs0bcrDLx7n1zKQ3JvLR6whaaw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/rect@1.1.2': + resolution: {integrity: sha512-xnXE7wG13PI+cxieVssYXlQJuYVRhH9NBoxt3KNwzghDIA69GMm7d4wXRouHIYjE+KvS6U/MsMO73NdS2MH9ZA==} + '@rolldown/binding-android-arm64@1.1.4': resolution: {integrity: sha512-EZLpf/8y7GXkkra90ML47kzik/GMP3EMcE9bPyHmRfxLC6z9+aW5A8poCsoxjrT5GfEcNAAvWwUHjvP1pUQkfw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2340,6 +2678,10 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + aria-hidden@1.2.6: + resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} + engines: {node: '>=10'} + aria-query@5.3.0: resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} @@ -2748,6 +3090,9 @@ packages: resolution: {integrity: sha512-qE3Veg1YXzGHQhlA6jzebZN2qVf6NX+A7m7qlhCGG30dJixrAQhYOsJjsnBjJkCSmuOPpCk30145fr8FV0bzog==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + detect-node-es@1.1.0: + resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + detect-node@2.1.0: resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} @@ -3208,6 +3553,10 @@ packages: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} + get-nonce@1.0.1: + resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} + engines: {node: '>=6'} + get-proto@1.0.1: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} @@ -4298,6 +4647,36 @@ packages: react-is@17.0.2: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + react-remove-scroll-bar@2.3.8: + resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + react-remove-scroll@2.7.2: + resolution: {integrity: sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + react-style-singleton@2.2.3: + resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + react@19.2.7: resolution: {integrity: sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==} engines: {node: '>=0.10.0'} @@ -4863,6 +5242,26 @@ packages: url-join@4.0.1: resolution: {integrity: sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==} + use-callback-ref@1.3.3: + resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + use-sidecar@1.1.3: + resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + use-sync-external-store@1.6.0: resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} peerDependencies: @@ -5641,6 +6040,23 @@ snapshots: '@exodus/bytes@1.15.1': {} + '@floating-ui/core@1.8.0': + dependencies: + '@floating-ui/utils': 0.2.12 + + '@floating-ui/dom@1.8.0': + dependencies: + '@floating-ui/core': 1.8.0 + '@floating-ui/utils': 0.2.12 + + '@floating-ui/react-dom@2.1.9(react-dom@19.2.7)(react@19.2.7)': + dependencies: + '@floating-ui/dom': 1.8.0 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + + '@floating-ui/utils@0.2.12': {} + '@humanfs/core@0.19.2': dependencies: '@humanfs/types': 0.15.0 @@ -6106,6 +6522,288 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true + '@radix-ui/primitive@1.1.5': {} + + '@radix-ui/react-arrow@1.1.11(@types/react-dom@19.2.3)(@types/react@19.2.17)(react-dom@19.2.7)(react@19.2.7)': + dependencies: + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3)(@types/react@19.2.17)(react-dom@19.2.7)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-collection@1.1.12(@types/react-dom@19.2.3)(@types/react@19.2.17)(react-dom@19.2.7)(react@19.2.7)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3)(@types/react@19.2.17)(react-dom@19.2.7)(react@19.2.7) + '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-compose-refs@1.1.3(@types/react@19.2.17)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-context-menu@2.3.3(@types/react-dom@19.2.3)(@types/react@19.2.17)(react-dom@19.2.7)(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-menu': 2.1.20(@types/react-dom@19.2.3)(@types/react@19.2.17)(react-dom@19.2.7)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3)(@types/react@19.2.17)(react-dom@19.2.7)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-context@1.2.0(@types/react@19.2.17)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-direction@1.1.2(@types/react@19.2.17)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-dismissable-layer@1.1.15(@types/react-dom@19.2.3)(@types/react@19.2.17)(react-dom@19.2.7)(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3)(@types/react@19.2.17)(react-dom@19.2.7)(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-effect-event': 0.0.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-dropdown-menu@2.1.20(@types/react-dom@19.2.3)(@types/react@19.2.17)(react-dom@19.2.7)(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-menu': 2.1.20(@types/react-dom@19.2.3)(@types/react@19.2.17)(react-dom@19.2.7)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3)(@types/react@19.2.17)(react-dom@19.2.7)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-focus-guards@1.1.4(@types/react@19.2.17)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-focus-scope@1.1.12(@types/react-dom@19.2.3)(@types/react@19.2.17)(react-dom@19.2.7)(react@19.2.7)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3)(@types/react@19.2.17)(react-dom@19.2.7)(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-id@1.1.2(@types/react@19.2.17)(react@19.2.7)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-menu@2.1.20(@types/react-dom@19.2.3)(@types/react@19.2.17)(react-dom@19.2.7)(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-collection': 1.1.12(@types/react-dom@19.2.3)(@types/react@19.2.17)(react-dom@19.2.7)(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.15(@types/react-dom@19.2.3)(@types/react@19.2.17)(react-dom@19.2.7)(react@19.2.7) + '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-focus-scope': 1.1.12(@types/react-dom@19.2.3)(@types/react@19.2.17)(react-dom@19.2.7)(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-popper': 1.3.3(@types/react-dom@19.2.3)(@types/react@19.2.17)(react-dom@19.2.7)(react@19.2.7) + '@radix-ui/react-portal': 1.1.13(@types/react-dom@19.2.3)(@types/react@19.2.17)(react-dom@19.2.7)(react@19.2.7) + '@radix-ui/react-presence': 1.1.7(@types/react-dom@19.2.3)(@types/react@19.2.17)(react-dom@19.2.7)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3)(@types/react@19.2.17)(react-dom@19.2.7)(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.15(@types/react-dom@19.2.3)(@types/react@19.2.17)(react-dom@19.2.7)(react@19.2.7) + '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + aria-hidden: 1.2.6 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-popper@1.3.3(@types/react-dom@19.2.3)(@types/react@19.2.17)(react-dom@19.2.7)(react@19.2.7)': + dependencies: + '@floating-ui/react-dom': 2.1.9(react-dom@19.2.7)(react@19.2.7) + '@radix-ui/react-arrow': 1.1.11(@types/react-dom@19.2.3)(@types/react@19.2.17)(react-dom@19.2.7)(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3)(@types/react@19.2.17)(react-dom@19.2.7)(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-rect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/rect': 1.1.2 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-portal@1.1.13(@types/react-dom@19.2.3)(@types/react@19.2.17)(react-dom@19.2.7)(react@19.2.7)': + dependencies: + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3)(@types/react@19.2.17)(react-dom@19.2.7)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-presence@1.1.7(@types/react-dom@19.2.3)(@types/react@19.2.17)(react-dom@19.2.7)(react@19.2.7)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-primitive@2.1.7(@types/react-dom@19.2.3)(@types/react@19.2.17)(react-dom@19.2.7)(react@19.2.7)': + dependencies: + '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-roving-focus@1.1.15(@types/react-dom@19.2.3)(@types/react@19.2.17)(react-dom@19.2.7)(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-collection': 1.1.12(@types/react-dom@19.2.3)(@types/react@19.2.17)(react-dom@19.2.7)(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3)(@types/react@19.2.17)(react-dom@19.2.7)(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-is-hydrated': 0.1.1(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-slot@1.3.0(@types/react@19.2.17)(react@19.2.7)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-tooltip@1.2.12(@types/react-dom@19.2.3)(@types/react@19.2.17)(react-dom@19.2.7)(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.15(@types/react-dom@19.2.3)(@types/react@19.2.17)(react-dom@19.2.7)(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-popper': 1.3.3(@types/react-dom@19.2.3)(@types/react@19.2.17)(react-dom@19.2.7)(react@19.2.7) + '@radix-ui/react-portal': 1.1.13(@types/react-dom@19.2.3)(@types/react@19.2.17)(react-dom@19.2.7)(react@19.2.7) + '@radix-ui/react-presence': 1.1.7(@types/react-dom@19.2.3)(@types/react@19.2.17)(react-dom@19.2.7)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3)(@types/react@19.2.17)(react-dom@19.2.7)(react@19.2.7) + '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-visually-hidden': 1.2.7(@types/react-dom@19.2.3)(@types/react@19.2.17)(react-dom@19.2.7)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-use-callback-ref@1.1.2(@types/react@19.2.17)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-use-controllable-state@1.2.3(@types/react@19.2.17)(react@19.2.7)': + dependencies: + '@radix-ui/react-use-effect-event': 0.0.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-use-effect-event@0.0.3(@types/react@19.2.17)(react@19.2.7)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-use-is-hydrated@0.1.1(@types/react@19.2.17)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-use-layout-effect@1.1.2(@types/react@19.2.17)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-use-rect@1.1.2(@types/react@19.2.17)(react@19.2.7)': + dependencies: + '@radix-ui/rect': 1.1.2 + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-use-size@1.1.2(@types/react@19.2.17)(react@19.2.7)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-visually-hidden@1.2.7(@types/react-dom@19.2.3)(@types/react@19.2.17)(react-dom@19.2.7)(react@19.2.7)': + dependencies: + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3)(@types/react@19.2.17)(react-dom@19.2.7)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/rect@1.1.2': {} + '@rolldown/binding-android-arm64@1.1.4': optional: true @@ -6977,6 +7675,10 @@ snapshots: argparse@2.0.1: {} + aria-hidden@1.2.6: + dependencies: + tslib: 2.8.1 + aria-query@5.3.0: dependencies: dequal: 2.0.3 @@ -7375,6 +8077,8 @@ snapshots: detect-newline@4.0.1: {} + detect-node-es@1.1.0: {} + detect-node@2.1.0: optional: true @@ -7919,6 +8623,8 @@ snapshots: hasown: 2.0.4 math-intrinsics: 1.1.0 + get-nonce@1.0.1: {} + get-proto@1.0.1: dependencies: dunder-proto: 1.0.1 @@ -9292,6 +9998,33 @@ snapshots: react-is@17.0.2: {} + react-remove-scroll-bar@2.3.8(@types/react@19.2.17)(react@19.2.7): + dependencies: + react: 19.2.7 + react-style-singleton: 2.2.3(@types/react@19.2.17)(react@19.2.7) + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.17 + + react-remove-scroll@2.7.2(@types/react@19.2.17)(react@19.2.7): + dependencies: + react: 19.2.7 + react-remove-scroll-bar: 2.3.8(@types/react@19.2.17)(react@19.2.7) + react-style-singleton: 2.2.3(@types/react@19.2.17)(react@19.2.7) + tslib: 2.8.1 + use-callback-ref: 1.3.3(@types/react@19.2.17)(react@19.2.7) + use-sidecar: 1.1.3(@types/react@19.2.17)(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + + react-style-singleton@2.2.3(@types/react@19.2.17)(react@19.2.7): + dependencies: + get-nonce: 1.0.1 + react: 19.2.7 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.17 + react@19.2.7: {} read-pkg@9.0.1: @@ -9912,6 +10645,21 @@ snapshots: url-join@4.0.1: {} + use-callback-ref@1.3.3(@types/react@19.2.17)(react@19.2.7): + dependencies: + react: 19.2.7 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.17 + + use-sidecar@1.1.3(@types/react@19.2.17)(react@19.2.7): + dependencies: + detect-node-es: 1.1.0 + react: 19.2.7 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.17 + use-sync-external-store@1.6.0(react@19.2.7): dependencies: react: 19.2.7 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index fd1f735b9..db80f00c4 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -2,6 +2,9 @@ packages: - packages/* catalog: + "@radix-ui/react-context-menu": ^2.3.3 + "@radix-ui/react-dropdown-menu": ^2.1.20 + "@radix-ui/react-tooltip": ^1.2.12 "@rolldown/plugin-babel": ^0.2.3 "@storybook/addon-a11y": ^10.4.6 "@storybook/addon-docs": ^10.4.6