Skip to content

feat(design-library): migrate ResizablePanel + upgrade Storybook to 10.4.0#31073

Merged
ashleeradka merged 3 commits into
mainfrom
devin/1779149242-migrate-resizable-panel
May 19, 2026
Merged

feat(design-library): migrate ResizablePanel + upgrade Storybook to 10.4.0#31073
ashleeradka merged 3 commits into
mainfrom
devin/1779149242-migrate-resizable-panel

Conversation

@ashleeradka
Copy link
Copy Markdown
Contributor

@ashleeradka ashleeradka commented May 19, 2026

Prompt / plan

Migrates ResizablePanel from the platform repo's web/src/components/app/core/ResizablePanel/ to the design library so PR #31067 (Home page port) can import it from @vellum/design-library/components/resizable-panel instead of baking it in as an app-level component.

Also upgrades Storybook from 10.3.6 → 10.4.0 (minor version bump, prompted by Storybook's own upgrade notice).

Why needed

ResizablePanel is a generic horizontal split-view with a draggable divider — it's a reusable UI primitive that belongs in the design library alongside Button, Card, etc. PR #31067 currently duplicates this component at apps/web/src/components/resizable-panel.tsx; with this PR merged, #31067 can consume it from the design library instead.

Convention compliance

  • Kebab-case file naming (resizable-panel.tsx)
  • No "use client" directive (not needed in Vite SPA)
  • Extends ComponentProps<"div"> with rest props spread and data-slot attribute
  • Uses design library's cn() utility and design token CSS variables (--border-base, --content-tertiary)
  • Storybook story with three variants (default, custom min widths, narrow)
  • Exported from barrel index.ts and accessible via @vellum/design-library/components/resizable-panel subpath

Storybook upgrade

Minor bump from 10.3.6 → 10.4.0 across all four packages (storybook, @storybook/react-vite, @storybook/addon-docs, @storybook/addon-a11y). No breaking changes in the 10.4.0 changelog.

Safety

  • Additive only — no existing components or exports modified
  • Storybook upgrade is a minor version bump with no breaking changes
  • Typecheck passes

Test plan

  • bunx tsc --noEmit — passes (zero type errors)
  • Storybook dev server renders ResizablePanel stories correctly with draggable divider
  • All three theme variants (light/dark/velvet) render token-based border and handle colors

Link to Devin session: https://app.devin.ai/sessions/86b00b0278b64800b3d9ee83cbc8a109
Requested by: @ashleeradka


Open in Devin Review

…Storybook to 10.4.0

Migrates the horizontal split-view component from
web/src/components/app/core/ResizablePanel/ in vellum-assistant-platform
to the design library as a reusable UI primitive.

Convention compliance:
- Kebab-case file naming (resizable-panel.tsx)
- No 'use client' directive
- Extends ComponentProps<'div'> with data-slot attribute
- Uses design library cn() utility and design token CSS variables
- Storybook story with three variants (default, custom min widths, narrow)

Also upgrades Storybook from 10.3.6 to 10.4.0 (minor version bump).

Co-Authored-By: ashlee@vellum.ai <ashlee@vellum.ai>
@devin-ai-integration
Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

devin-ai-integration[bot]

This comment was marked as resolved.

Addresses Devin Review findings:
- Clamp stored width against minLeftWidth during initialization
- Add initial clamp in useEffect for full bounds check after mount
- Import Meta/StoryObj from direct dep @storybook/react-vite, not transitive @storybook/react

Co-Authored-By: ashlee@vellum.ai <ashlee@vellum.ai>
vex-assistant-bot[bot]
vex-assistant-bot Bot previously approved these changes May 19, 2026
Copy link
Copy Markdown
Contributor

@vex-assistant-bot vex-assistant-bot Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

APPROVE

Value: Migrates ResizablePanel from the platform repo into the design library as a reusable primitive, eliminating the need for PR #31067 (Home page port) to bake its own version. Also bumps Storybook to 10.4.0 with no breaking changes.

What this does:

  1. ResizablePanel component — Clean horizontal split-view with a draggable divider. Uses the modern pointer events API (onPointerDown/Move/Up) with setPointerCapture/releasePointerCapture for reliable cross-browser drag tracking (handles both mouse and touch in one event model). No external resizable library dependency.

  2. React 19 conventions — fully compliant:

    • Function declaration (export function ResizablePanel)
    • ComponentProps<"div"> with rest props spread
    • data-slot="resizable-panel" on the root element
    • cn() utility for conditional class composition
    • No forwardRef, no use client directive (Vite SPA)
    • Named export only
  3. Drag state management — correct patterns:

    • dragRef (mutable ref) stores { startX, startWidth } — values that don't need to trigger re-renders during the drag operation
    • isDragging (React state) drives the visual feedback (opacity transition on the handle)
    • clamp helper is memoized with useCallback and bound to minLeftWidth/minRightWidth deps
    • leftWidth state is updated during drag via handlePointerMove, and persisted to localStorage on handlePointerUp
  4. Persistence — privacy-safe localStorage:

    • Both read (initializer) and write (pointerUp) are wrapped in try/catch — handles strict-privacy browsing contexts and quota exceeded errors gracefully
    • storageKey is optional; when absent, component uses defaultLeftWidth only
    • Number.isFinite(parsed) guards against corrupted stored values
  5. Resize handling — useEffect with proper cleanup:

    • window.addEventListener("resize", onResize) with matching removeEventListener in cleanup
    • The resize handler re-clamps the current width to ensure it stays within bounds when the container shrinks
    • This is the right tool for the job here — the effect needs access to the containerRef and the memoized clamp function, which are not easily externalized to module scope for useSyncExternalStore
  6. Accessibility:

    • Divider has role="separator" and aria-orientation="vertical"
    • cursor-col-resize gives the correct affordance
    • select-none during drag prevents text selection
  7. Storybook stories — Three variants: default (300px left), custom min widths (200/150px), narrow default (200px). All use CSS custom properties (var(--surface-base), var(--surface-lift), var(--content-default)) for theming. Layout wrapper ensures 400px height in the story canvas.

  8. Storybook upgrade (10.3.6 → 10.4.0) — Minor bump across all four packages (storybook, @storybook/react-vite, @storybook/addon-docs, @storybook/addon-a11y). PR body links to the 10.4.0 changelog confirming no breaking changes.

  9. Barrel export — Added to index.ts following the same pattern as other components (export { ResizablePanel, type ResizablePanelProps }).

Minor / non-blocking:

  • Future enhancement (not blocking): Consider adding onPointerCancel handling alongside onPointerUp. If the user starts a drag and then the browser fires a pointercancel event (e.g., system interrupt, touch-action conflict), the drag state would remain stuck as isDragging = true and dragRef.current would stay non-null. The visual feedback (opacity) would be stuck. Adding onPointerCancel={handlePointerUp} would be a one-line safety net.

  • ResizeObserver vs. window.resize: The current approach listens on window.resize and clamps based on container.offsetWidth. If the container resizes for reasons other than window resize (e.g., a sidebar collapsing elsewhere in the layout), the clamp won't fire. A future iteration could use ResizeObserver on the container itself, but that's a genuine enhancement, not a bug in this PR.

Bot findings:

Devin has auto-monitoring enabled but no substantive review comments yet. No Codex review visible.

CI: All passing — Lint, Type Check, Test, OpenAPI Spec Check, FlexFrame Lint: all success. macOS Tests/Build skipped (expected for a design library PR with no native code).

Vellum Constitution — Inviting: reusable primitives in the design library keep the web app codebase clean and make the platform feel cohesive.

Co-Authored-By: ashlee@vellum.ai <ashlee@vellum.ai>
@ashleeradka ashleeradka merged commit 19943e6 into main May 19, 2026
14 checks passed
@ashleeradka ashleeradka deleted the devin/1779149242-migrate-resizable-panel branch May 19, 2026 00:24
@devin-ai-integration
Copy link
Copy Markdown
Contributor

Test Results — ResizablePanel Migration + Storybook 10.4.0

Ran Storybook locally on the PR branch, tested all ResizablePanel stories across all three themes.

Test Result
Component renders with correct structure (two panes, separator, col-resize cursor) passed
Drag interaction works (left pane 300px → 400px) passed
Theme tokens correct across Light / Dark / Velvet passed
All 3 story variants render (Default, CustomMinWidths, NarrowDefault) passed
Storybook version is 10.4.0 passed
Evidence screenshots

Light theme — Default story
Light theme

After drag (300→400px)
After drag

Dark theme
Dark theme

Velvet theme
Velvet theme

Custom Min Widths (Velvet)
Custom Min Widths

Narrow Default (Velvet)
Narrow Default

Notes
  • Drag verified programmatically via dispatched PointerEvents inside Storybook iframe (native mouse drag doesn't cross iframe boundaries). Width change 300→400px confirmed via DOM measurement.
  • "Storybook 10.3" in bottom-left is a "what's new" notification banner, not the actual version. Confirmed 10.4.0 from node_modules/storybook/package.json.
  • Session: https://app.devin.ai/sessions/86b00b0278b64800b3d9ee83cbc8a109

devin-ai-integration Bot added a commit that referenced this pull request May 19, 2026
…l copy

Replaces the local apps/web/src/components/resizable-panel.tsx with the
design library version at @vellum/design-library/components/resizable-panel
(landed in PR #31073). Deletes the now-redundant local file.

Co-Authored-By: ashlee@vellum.ai <ashlee@vellum.ai>
ashleeradka added a commit that referenced this pull request May 19, 2026
…eyAPI client (#31067)

* feat(web): port Home page from platform repo, wire up React Query + HeyAPI client

Part of LUM-1601

- Wire up QueryClientProvider in main.tsx with configured QueryClient
- Add HeyAPI client singleton (src/lib/api-client.ts) for daemon endpoints
- Add API error utilities (src/lib/api-errors.ts)
- Port avatar system: types, API, SVG compositor, useAssistantAvatar hook,
  AnimatedAvatar + ChatAvatar components
- Port Home domain: types, API layer, feed utils, useHomeFeedQuery +
  useHomeStateQuery hooks, all page components (greeting header, suggestion
  pills, feed list, filter bar, recap rows, detail panel)
- Port shared components: ResizablePanel, useIsMobile hook
- Enhance design library Button: add outlined variant, compact size,
  leftIcon/rightIcon/iconOnly props with proper Tailwind styling
- Add /home route in routes.tsx
- All files follow new repo conventions: kebab-case filenames, no
  'use client' directives, no Next.js imports, .js extension imports

Co-Authored-By: ashlee@vellum.ai <ashlee@vellum.ai>

* fix: resolve cross-package type resolution for design library file: dep

- Add preserveSymlinks to tsconfig.json and vite.config.ts so TypeScript
  resolves design library imports from the symlink path (apps/web/node_modules/)
  instead of the real path (packages/design-library/), ensuring a single copy
  of @types/react is used across both packages.
- Add design library runtime deps (@radix-ui/react-slot, class-variance-authority,
  clsx, tailwind-merge) to apps/web dependencies since bun does not install
  file: dependency transitive deps in the linked package's node_modules.
- Fix SVG path number regex in animated-avatar.tsx to handle compact SVG
  notation (numbers starting with . like .5 meaning 0.5).

Co-Authored-By: ashlee@vellum.ai <ashlee@vellum.ai>

* fix: add postinstall script to deduplicate react types for local dev

When the design library's devDependencies are installed (e.g. for Storybook),
its node_modules/react shadows the web app's copy, causing dual-type errors.
The postinstall removes the design library's react copies so TypeScript
resolves from the web app's single copy via preserveSymlinks.

Co-Authored-By: ashlee@vellum.ai <ashlee@vellum.ai>

* fix(web): replace shared mountedRef with per-effect cancellation in AnimatedAvatar

Each blink/twitch useEffect now uses a local `let cancelled = false`
flag instead of a shared mountedRef. This prevents orphaned inner
timeouts from creating duplicate animation chains when isStreaming
toggles rapidly — the previous shared ref could be reset to true by
a new effect setup while orphaned callbacks from the old effect's
cleanup phase were still pending.

Co-Authored-By: ashlee@vellum.ai <ashlee@vellum.ai>

* fix(web): replace window.location.href with React Router useNavigate in home routes

Co-Authored-By: ashlee@vellum.ai <ashlee@vellum.ai>

* refactor(web): use ResizablePanel from design library instead of local copy

Replaces the local apps/web/src/components/resizable-panel.tsx with the
design library version at @vellum/design-library/components/resizable-panel
(landed in PR #31073). Deletes the now-redundant local file.

Co-Authored-By: ashlee@vellum.ai <ashlee@vellum.ai>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant