- Restructure detail panel as approval form (EditFormPage pattern)
- Separate concerns: Header (read-only) + Content (form) + Footer (actions)
- Use form/fieldset semantics for read-only vs editable sections
- Request Details and Metrics as disabled fieldsets (read-only)
- Review Comment as textarea form input
- Footer buttons (Approve/Reject) with validation
- CSS refactor: approval-form, form-header/content/footer structure
- Improved accessibility and semantic HTML
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Root cause: CSS Grid with optional (v-if) children caused inconsistent row counts.
Grid-template-rows: auto auto auto auto 1fr auto auto (7 rows) didn't match
actual child count (5-6 rows), causing 1fr collapse to 3px.
Solution: Revert to flexbox (proven stable).
- .ks-page: display: grid → display: flex; flex-direction: column
- .ks-page__workspace: add flex: 1 (replaces 1fr grid expansion)
- All flex children have min-height: 0 (height propagation chain)
Result: ShadowRunQueue grid now expands to full viewport height.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Root cause: PageLayout's flex-direction: column + gap was not counted in flex: 1 height calculations, causing children to overflow and trigger scroll.
Solution: Convert PageLayout from flexbox to CSS Grid with explicit grid-template-rows. Grid automatically accounts for gaps in row sizing.
Changes:
- PageLayout.vue: display: flex → display: grid
- grid-template-rows: auto auto auto auto 1fr auto auto
- .ks-page__content/.ks-page__aside: height: 100% → flex: 1
- .ks-page__workspace: removed flex: 1 (grid cell, not flex)
Impact:
- models-master fits viewport without page-level scroll ✓
- All screen-types layouts auto-fit with correct height propagation
- Fix applies to all pages using PageLayout
AGENTS.md v16.0: Added Layout Rule #7 (PageLayout grid requirement)
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
- Display app version (v0.1.0 or VITE_APP_VERSION) in sidebar footer
- Hidden when sidebar is collapsed
- Positioned above Help/Settings buttons
This provides system version visibility without requiring footer.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Changes:
• base.css: Add global footer hiding rule (applies to all .ks-page__footer, .ks-shell__footer, footer[class*=ks-])
• PageLayout: Remove individual footer CSS (use global rule)
• AppShellLayout: Remove individual footer CSS (use global rule)
Benefits:
✓ Single source of truth (base.css)
✓ Easier to maintain and update globally
✓ Consistent behavior across all layouts
✓ Can enable/disable with one change if needed
✓ Structure preserved (v-if, grid-areas intact)
Design principle: Single-screen + content-first
• All controls in header/command-bar/summary
• Recover ~48-64px per page for content
• Mobile-friendly (no hard-to-reach footer)
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
- Replace custom grid layout with KsSplitter component
- Add storageKey to prevent ratio conflicts with other pages
- Remove dead .content CSS class
- Update .request-list height constraint (flex: 1 → height: 100%)
- Maintain overflow-y: auto for scrollable panes
- Simplifies code by ~50 lines, adds drag-to-resize functionality
ISSUE: Grid height was constrained to min-height: 220px, leaving large
unused space on page (3 rows visible, lots of empty area below).
PRINCIPLE: Grid must expand to fill available vertical space in viewport.
CHANGES:
ModelOperationTable.vue (.grid-wrapper)
- Removed: min-height: 220px (artificial constraint)
- Added: height: 100%
- Added: min-height: 0 (critical for flex overflow behavior)
RESULT: Grid now spans full available height in flex container,
utilizing screen space efficiently.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
CRITICAL FIX: Filters container must explicitly set width: 100% to span
the full parent width and prevent content-based shrink-to-fit.
Without width: 100%, .filters collapses to content width, causing
filter inputs to wrap to multiple lines when parent container width
changes.
Added 'width: 100%;' to .filters in:
- ModelsList.vue
- ShadowRunQueue.vue
- ApprovalQueue.vue
- DataQualityPage.vue
Result: Filters now correctly span full width and display on single line.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
CRITICAL FIX: Restored .filters { display: flex } to all pages.
Previous commit removed this necessary style, causing filters to
wrap to 2 lines instead of staying inline.
All filter sections now display in a single line with:
- display: flex
- align-items: center
- gap: var(--ks-space-3)
This ensures:
- Input fields stay inline (1 line)
- 52px filter height maintained
- 34px input height maintained
- Standard layout across all pages
Affected pages:
- ModelsList.vue
- ShadowRunQueue.vue
- ApprovalQueue.vue
- DataQualityPage.vue (already had this)
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Removed duplicate input field styling from individual pages:
- ModelsList.vue: removed .input height/padding/border styles
- ShadowRunQueue.vue: removed .input height/padding/border styles
- ApprovalQueue.vue: removed .filters and .input styles
PageLayout now provides the authoritative source for input styling:
- All inputs in .ks-page__filters use consistent 34px height
- Padding: 0 0.75rem
- Border-radius: 4px
- Box-sizing: border-box
Individual pages now only define width constraints (max-width: 350px for search).
This applies the DRY principle - single source of truth for filter input
styling across all pages. Reduces code duplication and improves
maintainability.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
PageLayout now provides default styles for all filter inputs:
- Height: 34px
- Padding: 0 0.75rem
- Line-height: 34px
- Border-radius: 4px
- Box-sizing: border-box
This eliminates the need for each page to redefine input styles.
Individual pages now only override width constraints (search-input max-width).
Aligns with DRY principle - single source of truth for input styling.
DataQualityPage:
- Added scoped styles for search-input max-width (350px)
Future: Consider extracting filter inputs into dedicated components
(<KsFilterInput>, <KsFilterSelect>) for even better reusability.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
PageLayout:
- Filter section: min-height 52px (standard height)
- Display: flex with center alignment
- Consistent gap between elements
ModelsList:
- Filter container: align-items center
- Remove bottom margin (handled by PageLayout)
Ensures all pages have consistent filter bar heights
and alignment without per-page CSS adjustments.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
- Light theme: neutral-100/200 with white background
- Dark theme: dark grays (2d3748/4a5568) with dark background
- Uses CSS variables for dark mode support
- Shimmer animation adapts to theme
- Automatic light/dark switching via prefers-color-scheme
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
- Grid height: height: 100% (leverages PageLayout flex)
- Filter inputs: Standard 34px height (KBX compact)
- Removes hardcoded calc() and max/min constraints
- Automatically adjusts to viewport/container changes
- No manual adjustments needed per screen size
This allows the component to scale responsively within
the PageLayout flex container without additional CSS.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
- Simplified data fetching (removed TanStack Query for now)
- Added direct mock API client in component
- Fixed state management (isLoading, isError, modelsData)
- Grid now renders with 3 sample model records
- Updated v-if conditions for loading/error/empty states
- Added grid container height and filter styling
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Removed the incomplete first <script setup> block that was causing
Vite plugin errors. The complete implementation in the second block
already contains all necessary logic.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>