diff --git a/CHANGELOG.md b/CHANGELOG.md index 34331fb86..ae3e651d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,17 +4,20 @@ ### Added +- Add the reusable Base UI Slider primitive with named scalar/range thumbs, horizontal and vertical composition, RTL keyboard semantics, disabled-state handling, and Storybook coverage for rehearsal range controls. - Name tonight's first playable range on the ready rehearsal map and tell the player to check that span on their instrument before the section. - Display the analyzed song tempo (BPM) as a badge in the rehearsal workspace. - 각 합주 역할(Role)별 개인 연습 진행도를 0~100% 범위로 기록 및 시각화할 수 있는 연습 진척도(`practiceProgress`) 트래커 기능 추가. UI 컨트롤(슬라이더 및 +/- 버튼)과 한/영 다국어 지원 포함. ### Changed +- Upgrade Base UI to 1.8.0 while preserving BandScope's canonical Slider composition, named-thumb accessibility, nested-input focus styling, and dependency-owned absolute thumb placement. - Consolidated Bandit, dependency audits, supplemental secret checks, and Trivy into one trusted-branch security backstop, delegated CodeQL to GitHub default setup, and removed duplicate local PR security and release-preflight runs. - Pinned npm `10.9.9` as the approved lockfile generator, activated it through Node-bundled Corepack before dependency consumption, and fail closed unless its bundled `tar` is at least `7.5.19`; primary CI still consumes the committed lock only through frozen `npm ci` validation, rejects mutable npm resolution in the lock gate, requires integrity evidence for public-registry lock entries, and preserves generator-sensitive root `@esbuild/*` peer metadata. ### Fixed +- Keep Slider keyboard focus paint on the Base UI thumb wrapper through the nested range input's `:focus-visible` state, and preserve Base UI's own absolute thumb-placement authority instead of adding a competing `relative` position token. - Upgraded the local score PDF parser to `pdfjs-dist` 6.2.108, pinned Undici 7.29.0 across the workspace, and constrained PDF loading to copied in-memory bytes with a same-origin bundled worker and npm-generated lock provenance. ## [0.1.3] - 2026-04-29 diff --git a/apps/desktop/package.json b/apps/desktop/package.json index e09719b22..d832cfa2c 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -14,7 +14,7 @@ }, "dependencies": { "@bandscope/shared-types": "0.1.0", - "@base-ui/react": "^1.5.0", + "@base-ui/react": "^1.8.0", "@fontsource-variable/geist": "^5.2.9", "@tauri-apps/api": "^2.11.0", "class-variance-authority": "^0.7.1", @@ -33,6 +33,7 @@ "@tauri-apps/cli": "^2.11.4", "@testing-library/jest-dom": "^6.6.3", "@testing-library/react": "^16.2.0", + "@testing-library/user-event": "^14.6.7", "@types/node": "^26.1.1", "@types/react": "^19.2.17", "@types/react-dom": "^19.2.3", diff --git a/apps/desktop/src/components/ui/slider-composition.test.tsx b/apps/desktop/src/components/ui/slider-composition.test.tsx new file mode 100644 index 000000000..4516eadfe --- /dev/null +++ b/apps/desktop/src/components/ui/slider-composition.test.tsx @@ -0,0 +1,113 @@ +import { render, screen } from "@testing-library/react" +import { describe, expect, it } from "vitest" + +import { + Slider, + SliderControl, + SliderIndicator, + SliderLabel, + SliderThumb, + SliderTrack, +} from "./slider" + +describe("Slider canonical composition", () => { + it("associates a visible SliderLabel with the nested range input", () => { + render( + + Practice progress + + + + + + + + ) + + expect(screen.getByRole("slider", { name: "Practice progress" })).toHaveAccessibleName( + "Practice progress" + ) + }) + + it("keeps range thumbs inside a usable track-press area without clipping their pointer targets", () => { + const { container } = render( + + Selected rehearsal loop boundaries + + + + + + + + + ) + + const control = container.querySelector('[data-slot="slider-control"]') + const track = container.querySelector('[data-slot="slider-track"]') + const start = screen.getByRole("slider", { name: "Loop start" }) + const end = screen.getByRole("slider", { name: "Loop end" }) + const startThumb = start.parentElement + const endThumb = end.parentElement + + expect(control).toHaveClass("min-h-6") + expect(track).toBeInTheDocument() + expect(track).toContainElement(start) + expect(track).toContainElement(end) + expect(track).not.toHaveClass("overflow-hidden") + expect(start).toHaveAttribute("aria-describedby", "loop-range-help") + expect(startThumb).toHaveStyle({ position: "absolute" }) + expect(endThumb).toHaveStyle({ position: "absolute" }) + expect(startThumb).not.toHaveClass("relative") + expect(endThumb).not.toHaveClass("relative") + expect(startThumb).toHaveClass("after:inset-[-12px]") + expect(endThumb).toHaveClass("after:inset-[-12px]") + }) + + it("styles keyboard focus from Base UI's nested range input", () => { + render( + + + + + + + + + ) + + const input = screen.getByRole("slider", { name: "Playback position" }) + const thumb = input.parentElement + + expect(input).toHaveAttribute("type", "range") + expect(thumb).toHaveClass("has-[:focus-visible]:outline-none") + expect(thumb).toHaveClass("has-[:focus-visible]:ring-2") + expect(thumb).not.toHaveClass("focus-visible:ring-2") + }) + + it("preserves the same track-owned thumb anatomy and press width for a vertical control", () => { + const { container } = render( + + + + + + + + + ) + + const control = container.querySelector('[data-slot="slider-control"]') + const track = container.querySelector('[data-slot="slider-track"]') + const thumb = screen.getByRole("slider", { name: "Vertical position" }) + + expect(thumb).toHaveAttribute("aria-orientation", "vertical") + expect(control).toHaveClass("data-[orientation=vertical]:min-w-6") + expect(track).toContainElement(thumb) + expect(track).not.toHaveClass("overflow-hidden") + }) +}) diff --git a/apps/desktop/src/components/ui/slider.stories.tsx b/apps/desktop/src/components/ui/slider.stories.tsx new file mode 100644 index 000000000..76908b652 --- /dev/null +++ b/apps/desktop/src/components/ui/slider.stories.tsx @@ -0,0 +1,123 @@ +import type { Meta, StoryObj } from "@storybook/react-vite" + +import { DirectionProvider } from "@base-ui/react/direction-provider" + +import { + Slider, + SliderControl, + SliderTrack, + SliderIndicator, + SliderThumb, +} from "./slider" + +/** Base UI Slider primitive documentation. */ +const meta = { + title: "UI/Slider", + component: Slider, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], +} satisfies Meta + +export default meta +type Story = StoryObj + +/** Default slider view rendering. */ +export const Default: Story = { + render: (args) => ( +
+ + + + + + + + +
+ ), +} + +/** Two-thumb selected-range rendering for rehearsal loop boundaries. */ +export const Range: Story = { + render: (args) => ( +
+ + + + + + + + + +
+ ), +} + +/** Vertical orientation rendering. */ +export const Vertical: Story = { + render: (args) => ( +
+ + + + + + + + +
+ ), +} + +/** Disabled interaction rendering. */ +export const Disabled: Story = { + render: (args) => ( +
+ + + + + + + + +
+ ), +} + +/** Stepped value rendering. */ +export const CustomStep: Story = { + render: (args) => ( +
+ + + + + + + + +
+ ), +} + +/** Right-to-left reading direction rendering. */ +export const RTL: Story = { + render: (args) => ( + +
+ + + + + + + + +
+
+ ), +} diff --git a/apps/desktop/src/components/ui/slider.tsx b/apps/desktop/src/components/ui/slider.tsx new file mode 100644 index 000000000..fc0825cf6 --- /dev/null +++ b/apps/desktop/src/components/ui/slider.tsx @@ -0,0 +1,144 @@ +"use client" + +import { Slider as SliderPrimitive } from "@base-ui/react/slider" + +import { cn } from "@/lib/utils" + +/** Render the root container for the slider. */ +function Slider({ className, ...props }: SliderPrimitive.Root.Props) { + return ( + + cn( + "relative flex w-full touch-none select-none items-center", + "data-[orientation=vertical]:flex-col data-[orientation=vertical]:w-auto data-[orientation=vertical]:h-full", + "data-disabled:opacity-50 data-disabled:pointer-events-none", + className(state) + ) + : cn( + "relative flex w-full touch-none select-none items-center", + "data-[orientation=vertical]:flex-col data-[orientation=vertical]:w-auto data-[orientation=vertical]:h-full", + "data-disabled:opacity-50 data-disabled:pointer-events-none", + className + ) + } + {...props} + /> + ) +} + +/** Render a visible label that Base UI associates with the slider thumbs. */ +function SliderLabel(props: SliderPrimitive.Label.Props) { + return +} + +/** Render the interactive control area of the slider. */ +function SliderControl({ className, ...props }: SliderPrimitive.Control.Props) { + return ( + + cn( + "relative flex min-h-6 w-full items-center", + "data-[orientation=vertical]:h-full data-[orientation=vertical]:min-w-6 data-[orientation=vertical]:w-auto data-[orientation=vertical]:flex-col", + className(state) + ) + : cn( + "relative flex min-h-6 w-full items-center", + "data-[orientation=vertical]:h-full data-[orientation=vertical]:min-w-6 data-[orientation=vertical]:w-auto data-[orientation=vertical]:flex-col", + className + ) + } + {...props} + /> + ) +} + +/** Render the visual track of the slider. */ +function SliderTrack({ className, ...props }: SliderPrimitive.Track.Props) { + return ( + + cn( + "relative h-2 w-full grow rounded-full bg-secondary", + "data-[orientation=vertical]:h-full data-[orientation=vertical]:w-2", + className(state) + ) + : cn( + "relative h-2 w-full grow rounded-full bg-secondary", + "data-[orientation=vertical]:h-full data-[orientation=vertical]:w-2", + className + ) + } + {...props} + /> + ) +} + +/** Render the filled portion of the slider track. */ +function SliderIndicator({ + className, + ...props +}: SliderPrimitive.Indicator.Props) { + return ( + + cn( + "h-full bg-primary", + "data-[orientation=vertical]:w-full data-[orientation=vertical]:h-auto", + className(state) + ) + : cn( + "h-full bg-primary", + "data-[orientation=vertical]:w-full data-[orientation=vertical]:h-auto", + className + ) + } + {...props} + /> + ) +} + +/** Render the draggable thumb of the slider. */ +function SliderThumb({ className, ...props }: SliderPrimitive.Thumb.Props) { + return ( + + cn( + "block h-5 w-5 rounded-full border-2 border-primary bg-background ring-offset-background transition-colors has-[:focus-visible]:outline-none has-[:focus-visible]:ring-2 has-[:focus-visible]:ring-ring has-[:focus-visible]:ring-offset-2 data-disabled:pointer-events-none data-disabled:opacity-50", + "after:absolute after:inset-[-12px] after:content-['']", + className(state) + ) + : cn( + "block h-5 w-5 rounded-full border-2 border-primary bg-background ring-offset-background transition-colors has-[:focus-visible]:outline-none has-[:focus-visible]:ring-2 has-[:focus-visible]:ring-ring has-[:focus-visible]:ring-offset-2 data-disabled:pointer-events-none data-disabled:opacity-50", + "after:absolute after:inset-[-12px] after:content-['']", + className + ) + } + {...props} + /> + ) +} + +export { + Slider, + SliderLabel, + SliderControl, + SliderTrack, + SliderIndicator, + SliderThumb, +} diff --git a/apps/desktop/src/components/ui/ui-added.test.tsx b/apps/desktop/src/components/ui/ui-added.test.tsx index 18a0ab314..b74ba285b 100644 --- a/apps/desktop/src/components/ui/ui-added.test.tsx +++ b/apps/desktop/src/components/ui/ui-added.test.tsx @@ -1,4 +1,6 @@ import { render, screen, waitFor } from "@testing-library/react" +import userEvent from "@testing-library/user-event" +import { DirectionProvider } from "@base-ui/react/direction-provider" import { describe, expect, it } from "vitest" import { @@ -25,6 +27,13 @@ import { DialogDescription, DialogTitle, } from "./dialog" +import { + Slider, + SliderControl, + SliderTrack, + SliderIndicator, + SliderThumb, +} from "./slider" import { Select, SelectContent, @@ -245,4 +254,107 @@ describe("added ui primitives (runtime render)", () => { toast("분석 준비 완료") expect(await screen.findByText("분석 준비 완료")).toBeTruthy() }) + + it("Slider mounts and renders accessible roles", () => { + render( + + + + + + + + + ) + expect(screen.getByRole("slider", { name: "Test Slider" })).toBeInTheDocument() + }) + + it("Slider mounts vertically and renders accessible roles", () => { + const { container } = render( + + + + + + + + + ) + expect(screen.getByRole("slider", { name: "Vertical Slider" })).toHaveAttribute("aria-orientation", "vertical") + expect(container.querySelector('[data-slot="slider"]')).toHaveClass("data-[orientation=vertical]:flex-col") + expect(container.querySelector('[data-slot="slider-control"]')).toHaveClass("data-[orientation=vertical]:flex-col") + expect(container.querySelector('[data-slot="slider-track"]')).toHaveClass("data-[orientation=vertical]:h-full") + }) + + it("Slider correctly handles disabled state", () => { + render( + + + + + + + + + ) + expect(screen.getByRole("slider", { name: "Disabled Slider" })).toBeDisabled() + }) + + it("Slider handles keyboard focus routing and class-token contract", async () => { + const user = userEvent.setup() + render( + + + + + + + + + ) + const slider = screen.getByRole("slider", { name: "Focus Slider" }) + await user.tab() + expect(slider).toHaveFocus() + expect(slider.parentElement).toHaveClass("has-[:focus-visible]:outline-none") + }) + + it("Slider keeps its extended hit target on Base UI's positioned thumb wrapper", () => { + render( + + + + + + + + + ) + const slider = screen.getByRole("slider", { name: "Hit target Slider" }) + expect(slider.parentElement).toHaveStyle({ position: "absolute" }) + expect(slider.parentElement).not.toHaveClass("relative") + expect(slider.parentElement).toHaveClass("after:absolute") + expect(slider.parentElement).toHaveClass("after:inset-[-12px]") + }) + + it("Slider handles RTL keyboard semantics correctly", async () => { + const user = userEvent.setup() + render( + + + + + + + + + + + ) + const slider = screen.getByRole("slider", { name: "RTL Slider" }) + await user.tab() + await user.keyboard("{ArrowRight}") + expect(slider).toHaveAttribute("aria-valuenow", "49") + await user.keyboard("{ArrowLeft}") + expect(slider).toHaveAttribute("aria-valuenow", "50") + }) }) diff --git a/docs/doctoring/slider-target-geometry.md b/docs/doctoring/slider-target-geometry.md new file mode 100644 index 000000000..5933f2d60 --- /dev/null +++ b/docs/doctoring/slider-target-geometry.md @@ -0,0 +1,108 @@ +# Slider target geometry + +## Decision + +BandScope keeps the reusable Slider thumb's structural extended pointer target on the moving Base UI thumb wrapper. The visible thumb remains 20×20 CSS pixels and the existing `::after` pseudo-element remains `position: absolute` with `inset: -12px`, giving a nominal 44×44 CSS-pixel envelope. + +The positioning and focus owners are Base UI, not BandScope utility classes. The exact adopted Base UI 1.8.0 `SliderThumb` implementation renders a wrapper `
` with inline `position: absolute` and a nested visually hidden ``. Focus/blur/keyboard handlers are attached to that input; the input is sized to the thumb wrapper so VoiceOver focus geometry follows the thumb. BandScope therefore preserves wrapper `has-[:focus-visible]` styling, does not substitute direct wrapper `:focus-visible`, and does not add a competing `relative` position utility. + +Base UI 1.8.0 was released on 2026-09-04. Its Slider-specific release note is `Prevent update loops from unstable refs (#5441)`. The dependency update is useful, but it does not alter the focus or absolute-placement contracts described above. + +This decision is narrower than a browser accessibility claim. Source and jsdom evidence can establish component structure and the upstream contract, but they do not prove that every browser, pointer modality, zoom level, overlap configuration, or assistive technology exposes the entire nominal envelope as an effective target. + +## Problem and correction + +An earlier repair treated the Thumb wrapper as if it were statically positioned and concluded that `after:absolute after:inset-[-12px]` needed a BandScope `relative` class. Exact dependency source disproved that premise: Base UI already positions the wrapper absolutely while computing its value-dependent location. CSS Positioned Layout defines a positioned box as establishing the relevant containing block, so the extra utility was redundant and could encode a false causal narrative. + +A later repair then inverted focus ownership by treating the wrapper as the focus target. Base UI's Slider contract has the nested `input type="range"` receive focus. The 1.8.0 source still attaches focus/blur/keyboard handling to that input and sizes the visually hidden input to the wrapper. Therefore the wrapper's focus paint must continue to use `:has(:focus-visible)` while this upstream anatomy remains in force. + +The Base UI 1.8.0 adoption commit `18f8d41b0209c56e63363b1c20dfdfaf0eb204ee` correctly advanced the manifest/lock graph but simultaneously removed the focused absolute-placement and nested-input focus regressions plus the corresponding doctoring. That evidence deletion was not justified by the dependency upgrade because the 1.8.0 source retains both contracts. The current repair restores those regressions while preserving the 1.8.0 dependency delta. + +This matters because rehearsal timeline/range controls are operated repeatedly under time pressure. WCAG 2.2 Success Criterion 2.5.8 sets a Level AA minimum target-size/spacing requirement of 24×24 CSS pixels, while Success Criterion 2.5.5 defines 44×44 CSS pixels as the enhanced Level AAA benchmark. A slider is treated as one target for the 2.5.8 spatial-selection note, but actual mounted-product geometry still has to be measured. + +## Constraints + +- Keep Base UI `Slider.Root → Slider.Control → Slider.Track → Slider.Indicator + Slider.Thumb` semantics and state-callback `className` contracts intact. +- Do not override Base UI's value-dependent absolute Thumb positioning with an important or competing position utility. +- Keep wrapper focus styling aligned with the nested range input that actually receives focus; do not replace `:has(:focus-visible)` with wrapper `:focus-visible` while the 1.8.0 anatomy remains in force. +- Preserve named thumbs, range indexes, RTL keyboard behavior, vertical layout, disabled behavior, and the existing `aria-describedby` rehearsal-range contract. +- Do not enlarge the visible thumb merely to make a source test pass; visible geometry and interaction geometry are separate design decisions. +- Do not claim pointer/touch success from jsdom, static class strings, or Storybook source alone. +- Do not move actual-audio timeline/range semantics into this primitive. Active Player/product composition remains the owner of audio-time semantics, selection persistence/reload, and stale-media races. + +## Alternatives considered + +### Keep `relative` as the claimed anchor + +Rejected. Base UI 1.8.0 supplies inline `position: absolute` on the wrapper. An ordinary Tailwind `relative` token neither owns the runtime placement nor proves the pseudo-element geometry. + +### Force `position: relative` + +Rejected. An important position declaration could override Base UI's absolute placement and break thumb movement along the track. + +### Style the wrapper with direct `:focus-visible` + +Rejected. In Base UI 1.8.0 the nested range input receives focus. Direct wrapper focus state is therefore not the state that needs painting. + +### Remove the focus/placement regressions because the dependency changed + +Rejected. The exact 1.8.0 source retains the same relevant anatomy: wrapper `
`, nested range input, input-owned focus handlers, and inline absolute placement. A dependency bump is not evidence that an existing contract vanished. + +### Increase the rendered thumb to 44×44 CSS pixels + +Rejected for this repair. That changes visual density, track occlusion, spacing, and layout in every consumer. A buyer-facing sizing change requires design review and real browser evidence. + +### Remove the extended target until browser E2E exists + +Rejected. The existing pseudo-element remains structurally attached to the already-positioned thumb wrapper. Removing it would knowingly reduce the intended pointer affordance while not producing browser evidence. + +## RED → repair lineage + +- Historical RED `913d981bb7274b944d2161e9376e7c25c9b19c5` required a `relative` token. Exact dependency review later showed that premise was wrong; it remains ancestry only. +- Historical production `401c68b2ba873801ff418627bd61295c57bbe6b6` added the redundant token. It did not change Base UI's inline absolute placement. +- Corrective geometry RED `c2ba83274828e4e619add328862a1fb554c192b8` required the rendered wrapper's actual absolute position, preserved the pseudo-element tokens, and rejected the redundant class. +- Historical geometry production `0f530971d6f5b1611b5b5e80e483063d30c359fc` removed `relative`; a later descendant reintroduced it with an incorrect focus-owner premise. +- Focus RED `6c3ca6d40cc9d219b5d0b103d4e7f5f89edb237d` pinned the nested range input and wrapper `has-[:focus-visible]` styling. +- Focus repair `126671b2366f516675cc58dd9488cc8f84ec6a34` restored that contract in both SliderThumb class paths. +- Geometry regression repair `88041422f5ccc41022b36876dd5882f2d32f7841` restored the absolute-position contract and rejected the reintroduced `relative` token. +- Production `480ba908acc70ab38d23f9dfef64c75759400305` removed the redundant token while retaining nested-input focus styling and the extended pseudo-element target. +- Base UI 1.8.0 adoption `18f8d41b0209c56e63363b1c20dfdfaf0eb204ee` preserved production styling but deleted the focused regression assertions and doctoring sections without an upstream contract change. +- Regression restoration `60440b764e1d6eefa0feaed84a905fddba8089d4` restores the absolute-placement and nested-input focus assertions against the 1.8.0 dependency tree. + +Hosted RED is not claimed for source-only ancestry unless an exact workflow reached the relevant assertion before repair. The missing-regression finding is source-backed: `18f8d41...` deleted assertions for contracts that the exact 1.8.0 source still implements. + +## Verification and claim boundary + +The focused jsdom/source contract may prove only that: + +- the accessible slider is the nested `input type="range"` inside the Slider thumb wrapper; +- the wrapper carries `has-[:focus-visible]` styling while the nested input owns keyboard focus; +- Base UI 1.8.0 renders the wrapper with `position: absolute`; +- BandScope does not add the misleading `relative` token; +- the pseudo-element retains absolute positioning and `-12px` inset tokens; +- range thumbs remain inside the unclipped Track with distinct accessible names/indexes and the rehearsal-range description; and +- existing RTL/vertical/disabled/accessibility regressions continue to compile and run when exact-head CI reaches them. + +This does **not** prove effective browser target geometry or focus paint after Tailwind compilation, ancestor clipping, transforms, overlap, zoom, forced-colors, or input-device behavior. It also does not prove actual-audio timeline semantics, selection persistence/reload, stale-media race handling, or locale rendering. + +Commercial UI acceptance still requires mounted-product evidence for pointer/touch acquisition, track press, drag initiation/continuation, multi-thumb collision and overlap, adjacent-control interference, keyboard focus paint, zoom/reflow, forced-colors, Narrator/VoiceOver, and responsive/locale states. + +## Risk and follow-up + +A pseudo-element can still fail the intended buyer outcome if an ancestor clips it, hit testing does not include it, it overlaps another thumb/control, or behavior differs across browser/zoom/input combinations. Range sliders are especially important because Base UI supports multiple thumbs and collision behavior; a nominal 44×44 envelope can overlap another thumb even when source structure is correct. + +The next UI evidence must therefore measure effective targets and drag selection in the mounted product. If the nominal envelope cannot be demonstrated without overlap or clipping, change the consumer layout or target strategy. Do not restore a redundant position class, direct wrapper `:focus-visible`, or weaken acceptance criteria as a substitute for browser evidence. + +## References + +Base UI contributors. (2026, September 4). *Base UI v1.8.0 release notes* [Software release notes]. https://base-ui.com/react/overview/releases/v1-8-0 + +Base UI contributors. (2026). *Slider component* [Documentation]. https://base-ui.com/react/components/slider + +Base UI contributors. (2026). *SliderThumb implementation, v1.8.0* [Source code]. GitHub. https://github.com/mui/base-ui/blob/v1.8.0/packages/react/src/slider/thumb/SliderThumb.tsx + +World Wide Web Consortium. (2023). *Web Content Accessibility Guidelines (WCAG) 2.2: Success Criterion 2.5.8 Target Size (Minimum).* https://www.w3.org/TR/WCAG22/#target-size-minimum + +World Wide Web Consortium. (2023). *Web Content Accessibility Guidelines (WCAG) 2.2: Success Criterion 2.5.5 Target Size (Enhanced).* https://www.w3.org/TR/WCAG22/#target-size-enhanced + +World Wide Web Consortium. (2025). *CSS Positioned Layout Module Level 3: Containing blocks of positioned boxes.* https://www.w3.org/TR/css-position-3/#def-cb diff --git a/package-lock.json b/package-lock.json index 1b2ceef69..c3562af49 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,7 +27,7 @@ "version": "0.1.0", "dependencies": { "@bandscope/shared-types": "0.1.0", - "@base-ui/react": "^1.5.0", + "@base-ui/react": "^1.8.0", "@fontsource-variable/geist": "^5.2.9", "@tauri-apps/api": "^2.11.0", "class-variance-authority": "^0.7.1", @@ -46,6 +46,7 @@ "@tauri-apps/cli": "^2.11.4", "@testing-library/jest-dom": "^6.6.3", "@testing-library/react": "^16.2.0", + "@testing-library/user-event": "^14.6.7", "@types/node": "^26.1.1", "@types/react": "^19.2.17", "@types/react-dom": "^19.2.3", @@ -61,6 +62,80 @@ "vitest": "^4.1.10" } }, + "apps/desktop/node_modules/@base-ui/react": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@base-ui/react/-/react-1.8.0.tgz", + "integrity": "sha512-P0/1sxo6SBVZOklKMIedvTWqw2s2IQzi9x5bIVsXu980cuSOD4NeuRSs+/L7LZQfDkZP/uRZyGPyfFl/B1oH+Q==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.7", + "@base-ui/utils": "0.4.0", + "@floating-ui/react-dom": "^2.1.9", + "@floating-ui/utils": "^0.2.12", + "use-sync-external-store": "^1.6.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@date-fns/tz": "^1.2.0", + "@types/react": "^17 || ^18 || ^19", + "date-fns": "^4.0.0", + "react": "^17 || ^18 || ^19", + "react-dom": "^17 || ^18 || ^19" + }, + "peerDependenciesMeta": { + "@date-fns/tz": { + "optional": true + }, + "@types/react": { + "optional": true + }, + "date-fns": { + "optional": true + } + } + }, + "apps/desktop/node_modules/@base-ui/utils": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@base-ui/utils/-/utils-0.4.0.tgz", + "integrity": "sha512-bO9fz25kKtPf+aZVyfQrC0PDmJdmVni31W2hCS5/Owb+inwdIL3XU26pCPRPlt4LSxZrBgLwubXQXQlKaFEZzw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.7", + "@floating-ui/utils": "^0.2.12", + "reselect": "^5.2.0", + "use-sync-external-store": "^1.6.0" + }, + "peerDependencies": { + "@types/react": "^17 || ^18 || ^19", + "react": "^17 || ^18 || ^19", + "react-dom": "^17 || ^18 || ^19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "apps/desktop/node_modules/@testing-library/user-event": { + "version": "14.6.7", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.7.tgz", + "integrity": "sha512-MPCpX8bxe8zS+JmmTwLp8jd0dy1rAm60Te/SL8JrQM3qvQJcBOs1d7IefJMyZzqM3EWBrDn/LWDt1BCGu4ASfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "peerDependencies": { + "@testing-library/dom": ">=7.21.4" + } + }, "node_modules/@adobe/css-tools": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.5.0.tgz", @@ -406,66 +481,6 @@ "resolved": "packages/shared-types", "link": true }, - "node_modules/@base-ui/react": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@base-ui/react/-/react-1.7.0.tgz", - "integrity": "sha512-j+8QjX44C32jrXD/qyEAGpFr70FRpGL2CY61mQd9nBPWN737CK0xxD1ceJ055rW4RtdvFDT1e7otzdlfxvsYug==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.29.2", - "@base-ui/utils": "0.3.2", - "@floating-ui/react-dom": "^2.1.9", - "@floating-ui/utils": "^0.2.12", - "use-sync-external-store": "^1.6.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@date-fns/tz": "^1.2.0", - "@types/react": "^17 || ^18 || ^19", - "date-fns": "^4.0.0", - "react": "^17 || ^18 || ^19", - "react-dom": "^17 || ^18 || ^19" - }, - "peerDependenciesMeta": { - "@date-fns/tz": { - "optional": true - }, - "@types/react": { - "optional": true - }, - "date-fns": { - "optional": true - } - } - }, - "node_modules/@base-ui/utils": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@base-ui/utils/-/utils-0.3.2.tgz", - "integrity": "sha512-oWy1aq/I2GmYjpl4PhEAhzflF8VPGKgZeq0xAWTbfD5KBWyxcN0ZP2+WHSUm/5Z6lVMBDLReLcoXwSYoRc/zNQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.29.2", - "@floating-ui/utils": "^0.2.12", - "reselect": "^5.2.0", - "use-sync-external-store": "^1.6.0" - }, - "peerDependencies": { - "@types/react": "^17 || ^18 || ^19", - "react": "^17 || ^18 || ^19", - "react-dom": "^17 || ^18 || ^19" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@bcoe/v8-coverage": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz",