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>
Policy: No footer sections in PageLayout. All footer functionality must move to:
• Header actions slot (Help, AI Suggest, etc.)
• Command bar (Save, Reset, Approve buttons)
• Summary area (Status, watermark, owner info)
Rationale:
✓ Single-screen principle: all interactive controls in initial viewport
✓ Mobile UX: bottom footer buttons are hard to reach
✓ Information density: header/command-bar conveys status
✓ Eliminates wasted ~48px footer space
Example: Move button actions from <template #footer> to <template #commandBar>
Reference: Session 2026-08-16, responsive design standardization
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
- Defines height propagation chain for all pages
- Establishes PageLayout → QueryBoundary → Content pattern
- Documents KsSplitter master-detail implementation
- Includes verification checklist for new pages
- Rationale: flex children need 'min-height: 0' to respect parent constraints
- 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>