Skip to content

fix(webui): smooth kanban detail panel animation and column alignment#42

Open
jimpablo wants to merge 4 commits into
mainfrom
feat/webui-kanban-column-fix
Open

fix(webui): smooth kanban detail panel animation and column alignment#42
jimpablo wants to merge 4 commits into
mainfrom
feat/webui-kanban-column-fix

Conversation

@jimpablo
Copy link
Copy Markdown
Collaborator

@jimpablo jimpablo commented May 7, 2026

Summary

  • Rewrites Kanban detail panel open/close animation: active column stays frozen at measured width, hidden columns animate only width+padding+opacity (no flex-grow transitions) to eliminate reflow and jitter
  • Detail panel is absolutely positioned and slides in sync with the active column via left transition, ensuring correct alignment for all 4 columns (not just Queue)
  • Fixes hidden columns contributing layout space when width:0 due to box-sizing: border-box + padding — clears padding on hidden columns to ensure true 0px width
  • isSnapping flag suppresses transitions on close so columns snap back invisibly under the still-opaque panel before it fades out
  • Closes detail panel automatically when switching source, group, or navigation view in the sidebar

jimpablo added 4 commits May 7, 2026 11:53
Signed-off-by: jimpablo <194239734+jimpablo@users.noreply.github.com>
Signed-off-by: jimpablo <194239734+jimpablo@users.noreply.github.com>
Signed-off-by: jimpablo <194239734+jimpablo@users.noreply.github.com>
Signed-off-by: jimpablo <194239734+jimpablo@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 7, 2026 07:31
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates the WebUI Kanban inbox layout/animation so opening the detail panel no longer causes flex reflow/jitter, and so the panel aligns correctly regardless of which column is active. It also improves timestamp robustness and ensures the detail panel closes when changing sidebar filters/views.

Changes:

  • Reworks Kanban board column/panel layout using measured widths and an absolutely positioned sliding detail panel.
  • Makes formatRelativeTime tolerate empty/invalid timestamps, and uses metadata.created_at as a preferred timestamp for cards.
  • Clears the selected envelope when switching sidebar view/source/group/prefix.

Reviewed changes

Copilot reviewed 4 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
loom/webui/frontend/src/lib/utils.ts Makes relative-time formatting robust to empty/invalid date strings.
loom/webui/frontend/src/components/KanbanBoard.tsx Implements the new column freezing + absolute detail panel animation/layout logic.
loom/webui/frontend/src/components/EnvelopeCard.tsx Displays relative time based on metadata.created_at with fallback to received_at.
loom/webui/frontend/src/App.tsx Clears selection when navigation/filter context changes.
loom/webui/frontend/package-lock.json Lockfile normalization (removes peer: true entries).
Files not reviewed (1)
  • loom/webui/frontend/package-lock.json: Language not supported
Comments suppressed due to low confidence (1)

loom/webui/frontend/src/components/KanbanBoard.tsx:33

  • onCloseDetail is declared in KanbanBoardProps and destructured in the component props, but it is no longer used. With noUnusedParameters: true in the frontend tsconfig, this will fail tsc. Either remove onCloseDetail from the props/interface and stop passing it from App, or call it from the detail panel close handler.
interface KanbanBoardProps {
  envelopes: Envelope[]
  showArchived: boolean
  selectedId: string | null
  onSelect: (id: string | null) => void
  onCloseDetail: () => void
}

export function KanbanBoard({
  envelopes,
  showArchived,
  selectedId,
  onSelect,
  onCloseDetail,
}: KanbanBoardProps) {

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +9 to +11
if (!iso) return ""
const date = new Date(iso)
if (isNaN(date.getTime())) return ""
Comment on lines 192 to +206
<div
className={cn(
"flex-none overflow-hidden rounded-lg border-l border-border transition-all duration-300 ease-in-out",
isOpen
? "ml-3 min-w-[480px] flex-[3] opacity-100"
: "min-w-0 w-0 flex-none opacity-0"
"absolute inset-y-3 right-0 overflow-hidden rounded-lg border-l border-border",
isOpen ? "opacity-100" : "opacity-0 pointer-events-none",
)}
style={{
left: panelLeft ?? "100%",
transition: isPanelSnapping
? "opacity 300ms ease-in-out"
: "opacity 300ms ease-in-out, left 300ms ease-in-out",
}}
>
{isOpen && <DetailPanel envelopeId={selectedId} onClose={onCloseDetail} />}
{(isOpen || frozenStatus != null) && (
<DetailPanel envelopeId={selectedId} onClose={() => handleSelect(null)} />
)}
Comment on lines 160 to +179
return (
<div className="flex h-full px-4 py-3">
{orderedColumns.map((colDef, idx) => {
const isHidden = isOpen && idx > 0
<div ref={containerRef} className="relative flex h-full overflow-hidden px-4 py-3">
{COLUMN_DEFS.map((colDef) => {
const isActive = effectiveStatus === colDef.status && frozenWidth != null
const isHidden = isOpen && effectiveStatus != null && effectiveStatus !== colDef.status
return (
<div
key={colDef.status}
ref={(el) => { colRefs.current[colDef.status] = el }}
className={cn(
"transition-all duration-300 ease-in-out",
isHidden
? "w-0 min-w-0 flex-none overflow-hidden opacity-0"
: "flex h-full min-w-[260px] flex-1 flex-col rounded-lg p-2"
"flex-none h-full flex-col overflow-hidden rounded-lg p-2",
isHidden && "opacity-0 pointer-events-none select-none",
)}
style={isHidden ? { marginLeft: 0, marginRight: 0 } : undefined}
style={{
width: isActive ? frozenWidth! : isHidden ? 0 : "25%",
...(isHidden ? { padding: 0 } : {}),
transition: isActive || isSnapping
? "none"
: "width 300ms ease-in-out, padding 300ms ease-in-out, opacity 300ms ease-in-out",
}}
{ title: "Done", status: "done" as const },
] as const

const ANIM_MS = 300
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.

2 participants