fix: Clean up KBX v60 references and simplify frontend pages
deploy / deploy (push) Successful in 1m49s
deploy / notify (push) Successful in 1s

- Removed all @kbx/contracts imports and types
- Cleaned up feature registries (minimal definitions)
- Simplified page components (HomePage, ModelsList, ShadowRunList)
- Removed KBX UI components and adapters
- Fixed TypeScript errors with type casting
- Frontend build: 737KB (204KB gzip) 

CI/CD Pipeline: Ready for testing
This commit is contained in:
2026-08-15 11:58:44 +09:00
parent 4f34dc7dfb
commit 70852bf378
71 changed files with 412 additions and 7541 deletions
+1 -18
View File
@@ -1,14 +1,7 @@
/**
* KBX UI Adapter - Export all wrapped components
* Single boundary: PrimeVue/AG Grid usage restricted to this module
* UI Adapter - Theme and density tokens
*/
// Contracts & Types
export * from '@shared/contracts/kbx-types'
// Adapter Components (PrimeVue wrapped)
// Density tokens
export const densityTokens = {
compact: {
inputHeight: 34,
@@ -29,13 +22,3 @@ export const densityTokens = {
fontSize: 16,
},
}
// Theme configuration
export const defaultTheme = {
primary: '#3b82f6',
secondary: '#6b7280',
danger: '#ef4444',
success: '#10b981',
warning: '#f59e0b',
info: '#06b6d4',
}
@@ -1,53 +0,0 @@
<script setup lang="ts">
import type { KbxScreenDefinition, KbxAsyncState, KbxSummaryItem, KbxQuickFilterItem, KbxScreenContext } from '@shared/contracts/kbx-types'
withDefaults(defineProps<{
screen: KbxScreenDefinition
dataState?: KbxAsyncState
loading?: boolean
selectionCount?: number
summaryItems?: KbxSummaryItem[]
quickFilters?: KbxQuickFilterItem[]
context?: KbxScreenContext | null
allowActions?: boolean
}>(), { dataState: 'ready', loading: false, selectionCount: 0, summaryItems: () => [], quickFilters: () => [], context: null, allowActions: true })
const emit = defineEmits<{ command: [commandId: string]; quickFilter: [filterId: string]; refresh: [] }>()
</script>
<template>
<div class="ks-list-page">
<header class="ks-list-page__header">
<div><h1>{{ screen.title }}</h1><p v-if="screen.description">{{ screen.description }}</p></div>
<div><slot name="header-actions" /></div>
</header>
<section v-if="screen.type" class="ks-list-page__search"><slot name="search" /></section>
<nav v-if="quickFilters.length" class="ks-list-page__quick-filters" aria-label="빠른 필터">
<button v-for="filter in quickFilters" :key="filter.id" type="button" :aria-pressed="filter.active" @click="emit('quickFilter', filter.id)">{{ filter.label }}<span v-if="filter.badge"> {{ filter.badge }}</span></button>
</nav>
<section v-if="context" class="ks-list-page__context"><slot name="context" /></section>
<main class="ks-list-page__content" :aria-busy="loading">
<div v-if="dataState === 'pending'">Loading data...</div>
<div v-else-if="dataState === 'empty'">No results found <button type="button" @click="emit('command', 'new')">Create New</button></div>
<div v-else-if="dataState === 'error'" role="alert">Error loading data <button type="button" @click="emit('refresh')">Retry</button></div>
<slot v-else name="content" />
</main>
<footer v-if="summaryItems.length" class="ks-list-page__footer">
<span v-if="selectionCount">{{ selectionCount }} item(s) selected</span>
<span v-for="item in summaryItems" :key="item.label"><strong>{{ item.label }}:</strong> {{ item.value }}</span>
</footer>
</div>
</template>
<style scoped>
.ks-list-page { display:flex; flex-direction:column; height:100%; background:var(--ks-color-surface,#fff); }
.ks-list-page__header,.ks-list-page__search,.ks-list-page__quick-filters,.ks-list-page__footer { padding:var(--ks-space-4); border-bottom:1px solid var(--ks-color-neutral-200); }
.ks-list-page__header { display:flex; justify-content:space-between; gap:var(--ks-space-4); background:var(--ks-color-neutral-50); }
.ks-list-page__header h1 { margin:0; }
.ks-list-page__header p { margin:.5rem 0 0; color:var(--ks-color-text-muted); }
.ks-list-page__quick-filters { display:flex; gap:var(--ks-space-2); overflow:auto; }
.ks-list-page__quick-filters button { padding:.5rem .75rem; border:1px solid var(--ks-color-neutral-300); border-radius:var(--ks-radius-sm); background:#fff; }
.ks-list-page__quick-filters button[aria-pressed='true'] { background:var(--ks-color-action); color:#fff; }
.ks-list-page__content { flex:1; overflow:auto; }
.ks-list-page__footer { display:flex; gap:var(--ks-space-4); flex-wrap:wrap; background:var(--ks-color-neutral-50); }
</style>
+1 -1
View File
@@ -1,3 +1,4 @@
export { default as SkeletonLoader } from './SkeletonLoader.vue'
export { default as KsButton } from './KsButton.vue'
export { default as KsTextField } from './KsTextField.vue'
export { default as KsTextArea } from './KsTextArea.vue'
@@ -14,7 +15,6 @@ export { default as KsInlineMessage } from './KsInlineMessage.vue'
export { default as KsPaginator } from './KsPaginator.vue'
export { default as KsTabs } from './KsTabs.vue'
export { default as KsDataGrid } from './KsDataGrid.vue'
export { default as KsListPage } from './KsListPage.vue'
export { default as FieldShell } from './FieldShell.vue'
export { default as KsDataContextHeader } from './KsDataContextHeader.vue'
export { default as KsCommandBar } from './KsCommandBar.vue'
+1 -20
View File
@@ -1,20 +1 @@
import type { KbxGridColumn } from '@shared/contracts/kbx-types'
import type { UiGridColumn } from './adapter/contracts'
/** Converts registry-owned KBX columns into the provider-neutral grid contract. */
export function toUiGridColumns(columns: readonly KbxGridColumn[]): UiGridColumn[] {
return columns.map(column => {
if (typeof column.field !== 'string') {
throw new Error(`Grid column field must be a string: ${String(column.field)}`)
}
return {
field: column.field,
header: column.header,
width: typeof column.width === 'number' ? column.width : undefined,
sortable: column.sortable,
filterable: column.filterable,
formatter: column.formatter,
}
})
}
// Grid column utilities
@@ -1,39 +0,0 @@
import { describe, expect, it } from 'vitest'
import { toUiGridColumns } from '../gridColumnAdapter'
describe('toUiGridColumns', () => {
it('preserves the provider-neutral column semantics and formatter', () => {
const formatter = (value: unknown) => String(value ?? '')
expect(toUiGridColumns([{
field: 'modelId',
header: 'Model ID',
width: 150,
sortable: false,
filterable: true,
formatter,
}])).toEqual([{
field: 'modelId',
header: 'Model ID',
width: 150,
sortable: false,
filterable: true,
formatter,
}])
})
it('does not guess how string widths should be interpreted', () => {
expect(toUiGridColumns([{ field: 'name', header: 'Name', width: '20rem' }])).toEqual([{
field: 'name',
header: 'Name',
width: undefined,
sortable: undefined,
filterable: undefined,
formatter: undefined,
}])
})
it('rejects non-string fields before they reach an adapter', () => {
expect(() => toUiGridColumns([{ field: 1, header: 'Invalid' }])).toThrow('Grid column field must be a string')
})
})