Initial commit: Add project files
ci / backend (push) Failing after 12s
ci / frontend (push) Failing after 19s
ci / static (push) Failing after 45s

This commit is contained in:
2026-08-02 05:15:36 +09:00
commit dcd1322d41
636 changed files with 122352 additions and 0 deletions
@@ -0,0 +1,18 @@
<script setup lang="ts">
import { computed, useId } from 'vue'
const props = defineProps<{ label: string; inputId?: string; required?: boolean; error?: string; help?: string }>()
const generatedId = useId()
const resolvedId = computed(() => props.inputId ?? `ks-field-${generatedId}`)
const messageId = computed(() => props.error || props.help ? `${resolvedId.value}-message` : undefined)
</script>
<template>
<div class="ks-field-shell" :data-invalid="Boolean(error) || undefined">
<label :for="resolvedId">{{ label }} <span v-if="required" aria-hidden="true">*</span></label>
<slot :input-id="resolvedId" :described-by="messageId" :invalid="Boolean(error)" />
<small v-if="error || help" :id="messageId" :class="{ 'ks-danger-text': error }" :role="error ? 'alert' : undefined">{{ error ?? help }}</small>
</div>
</template>
<style scoped>
.ks-field-shell{display:grid;gap:var(--ks-space-1)}
label{font-weight:650} small{color:var(--ks-color-neutral-600)}
</style>
@@ -0,0 +1,14 @@
<script setup lang="ts">
import type { UiButtonType, UiSeverity } from '../adapter/contracts'
import { useUiAdapter } from '../adapter/useUiAdapter'
withDefaults(defineProps<{ label?: string; severity?: UiSeverity; type?: UiButtonType; disabled?: boolean; loading?: boolean }>(), {
severity: 'primary', type: 'button', disabled: false, loading: false
})
const emit = defineEmits<{ click: [event: MouseEvent] }>()
const adapter = useUiAdapter()
</script>
<template>
<component :is="adapter.components.Button" v-bind="$props" @activate="emit('click', $event)"><slot /></component>
</template>
@@ -0,0 +1,11 @@
<script setup lang="ts">
import { computed, useId } from 'vue'
import { useUiAdapter } from '../adapter/useUiAdapter'
const props = defineProps<{ modelValue: boolean; label: string; inputId?: string; disabled?: boolean }>()
const emit = defineEmits<{ 'update:modelValue': [value: boolean] }>()
const adapter = useUiAdapter()
const generatedId = useId()
const resolvedId = computed(() => props.inputId ?? `ks-check-${generatedId}`)
</script>
<template><label class="ks-check" :for="resolvedId"><component :is="adapter.components.Checkbox" :input-id="resolvedId" :model-value="modelValue" :disabled="disabled" @update:model-value="emit('update:modelValue', $event)" /><span>{{ label }}</span></label></template>
<style scoped>.ks-check { display: inline-flex; align-items: center; gap: var(--ks-space-2); cursor: pointer; }</style>
@@ -0,0 +1,8 @@
<script setup lang="ts">
import KsButton from './KsButton.vue'
export interface CommandBarAction { id: string; label: string; severity?: 'primary'|'secondary'|'success'|'info'|'warning'|'danger'; disabled?: boolean; busy?: boolean }
defineProps<{ actions: readonly CommandBarAction[]; ariaLabel?: string }>()
const emit = defineEmits<{ execute: [actionId: string] }>()
</script>
<template><nav class="ks-command-bar" :aria-label="ariaLabel ?? 'Page actions'"><KsButton v-for="action in actions" :key="action.id" :label="action.label" :severity="action.severity" :disabled="action.disabled" :loading="action.busy" @click="emit('execute', action.id)" /></nav></template>
<style scoped>.ks-command-bar{display:flex;gap:var(--ks-space-2);flex-wrap:wrap;justify-content:flex-end}</style>
@@ -0,0 +1,33 @@
<script setup lang="ts">
import { computed } from 'vue'
import type { VersionSet } from '../../contracts/versionSet'
import EvidenceVersionSet from '../EvidenceVersionSet.vue'
const props = defineProps<{
title: string; asOf: string; datasetId: string; dataHash: string; modelVersion: string; configVersion: string;
codeSha: string; contractVersion: string; projectionVersion?: string; watermark?: string;
stale?: boolean; correlationId?: string
}>()
const versionSet = computed<VersionSet>(() => ({
datasetId: props.datasetId,
dataHash: props.dataHash,
modelVersion: props.modelVersion,
configVersion: props.configVersion,
codeSha: props.codeSha,
contractVersion: props.contractVersion
}))
</script>
<template>
<header class="ks-data-context" :data-stale="stale || undefined">
<div><h1>{{ title }}</h1><p>As-of {{ asOf }} <strong v-if="stale">STALE</strong></p></div>
<EvidenceVersionSet :value="versionSet" compact />
<dl v-if="projectionVersion || watermark || correlationId">
<template v-if="projectionVersion"><dt>Projection</dt><dd>{{ projectionVersion }}</dd></template>
<template v-if="watermark"><dt>Watermark</dt><dd>{{ watermark }}</dd></template>
<template v-if="correlationId"><dt>Correlation</dt><dd>{{ correlationId }}</dd></template>
</dl>
</header>
</template>
<style scoped>
.ks-data-context{display:grid;gap:var(--ks-space-3);padding:var(--ks-space-4);border:1px solid var(--ks-color-neutral-300);border-radius:var(--ks-radius-md)}
.ks-data-context[data-stale=true]{border-color:var(--ks-color-warning-500)} h1,p{margin:0} dl{display:flex;gap:var(--ks-space-3);margin:0;flex-wrap:wrap} dt{font-weight:700} dd{margin:0}
</style>
@@ -0,0 +1,8 @@
<script setup lang="ts">
import type { UiGridColumn } from '../adapter/contracts'
import { useUiAdapter } from '../adapter/useUiAdapter'
withDefaults(defineProps<{ rows: unknown[]; columns: UiGridColumn[]; loading?: boolean; height?: string; rowSelection?: 'single' | 'multiple' | 'none' }>(), { loading: false, height: '32rem', rowSelection: 'single' })
const emit = defineEmits<{ rowSelected: [row: unknown] }>()
const adapter = useUiAdapter()
</script>
<template><component :is="adapter.components.DataGrid" v-bind="$props" @row-selected="emit('rowSelected', $event)" /></template>
@@ -0,0 +1,11 @@
<script setup lang="ts">
import { computed, useId } from 'vue'
import { useUiAdapter } from '../adapter/useUiAdapter'
const props = defineProps<{ modelValue: string | Date | null; label: string; inputId?: string; disabled?: boolean; required?: boolean; error?: string; help?: string; min?: Date; max?: Date }>()
const emit = defineEmits<{ 'update:modelValue': [value: string | Date | null]; blur: [event: FocusEvent] }>()
const adapter = useUiAdapter()
const generatedId = useId()
const resolvedId = computed(() => props.inputId ?? `ks-date-${generatedId}`)
</script>
<template><div class="ks-field"><label :for="resolvedId">{{ label }} <span v-if="required" aria-hidden="true">*</span></label><component :is="adapter.components.DateField" :input-id="resolvedId" :model-value="modelValue" :disabled="disabled" :invalid="Boolean(error)" :min="min" :max="max" :aria-describedby="error || help ? `${resolvedId}-message` : undefined" @update:model-value="emit('update:modelValue', $event)" @blur="emit('blur', $event)" /><small v-if="error || help" :id="`${resolvedId}-message`" :class="{ 'ks-danger-text': error }">{{ error ?? help }}</small></div></template>
<style scoped>.ks-field{display:grid;gap:var(--ks-space-1)}label{font-weight:650}small{color:var(--ks-color-neutral-600)}</style>
@@ -0,0 +1,7 @@
<script setup lang="ts">
import { useUiAdapter } from '../adapter/useUiAdapter'
defineProps<{ visible: boolean; title: string; modal?: boolean; closable?: boolean }>()
const emit = defineEmits<{ 'update:visible': [value: boolean] }>()
const adapter = useUiAdapter()
</script>
<template><component :is="adapter.components.Dialog" v-bind="$props" @update:visible="emit('update:visible', $event)"><slot /><template #footer><slot name="footer" /></template></component></template>
@@ -0,0 +1,8 @@
<script setup lang="ts">
import type { UiSeverity } from '../adapter/contracts'
import { useUiAdapter } from '../adapter/useUiAdapter'
withDefaults(defineProps<{ severity?: UiSeverity; title?: string; message: string; dismissible?: boolean }>(), { severity: 'info', dismissible: false })
const emit = defineEmits<{ dismiss: [] }>()
const adapter = useUiAdapter()
</script>
<template><component :is="adapter.components.InlineMessage" v-bind="$props" @dismiss="emit('dismiss')" /></template>
@@ -0,0 +1,8 @@
<script setup lang="ts">
import type { UiSelectOption } from '../adapter/contracts'
import { useUiAdapter } from '../adapter/useUiAdapter'
withDefaults(defineProps<{ modelValue?: Array<string | number | boolean | null>; options: UiSelectOption[]; label?: string; disabled?: boolean; required?: boolean }>(), { modelValue: () => [] })
const emit = defineEmits<{ 'update:modelValue': [value: Array<string | number | boolean | null>] }>()
const adapter = useUiAdapter()
</script>
<template><component :is="adapter.components.MultiSelect" v-bind="$props" @update:model-value="emit('update:modelValue', $event)" /></template>
@@ -0,0 +1,11 @@
<script setup lang="ts">
import { computed, useId } from 'vue'
import { useUiAdapter } from '../adapter/useUiAdapter'
const props = defineProps<{ modelValue: number | null; label: string; inputId?: string; disabled?: boolean; required?: boolean; error?: string; help?: string; min?: number; max?: number; minFractionDigits?: number; maxFractionDigits?: number }>()
const emit = defineEmits<{ 'update:modelValue': [value: number | null]; blur: [event: FocusEvent] }>()
const adapter = useUiAdapter()
const generatedId = useId()
const resolvedId = computed(() => props.inputId ?? `ks-number-${generatedId}`)
</script>
<template><div class="ks-field"><label :for="resolvedId">{{ label }} <span v-if="required" aria-hidden="true">*</span></label><component :is="adapter.components.NumberField" :input-id="resolvedId" :model-value="modelValue" :disabled="disabled" :invalid="Boolean(error)" :min="min" :max="max" :min-fraction-digits="minFractionDigits" :max-fraction-digits="maxFractionDigits" :aria-describedby="error || help ? `${resolvedId}-message` : undefined" @update:model-value="emit('update:modelValue', $event)" @blur="emit('blur', $event)" /><small v-if="error || help" :id="`${resolvedId}-message`" :class="{ 'ks-danger-text': error }">{{ error ?? help }}</small></div></template>
<style scoped>.ks-field{display:grid;gap:var(--ks-space-1)}label{font-weight:650}small{color:var(--ks-color-neutral-600)}</style>
@@ -0,0 +1,7 @@
<script setup lang="ts">
import { useUiAdapter } from '../adapter/useUiAdapter'
withDefaults(defineProps<{ page: number; pageSize: number; total: number; pageSizes?: number[]; disabled?: boolean }>(), { pageSizes: () => [20, 50, 100], disabled: false })
const emit = defineEmits<{ pageChange: [value: { page: number; pageSize: number }] }>()
const adapter = useUiAdapter()
</script>
<template><component :is="adapter.components.Paginator" v-bind="$props" @page-change="emit('pageChange', $event)" /></template>
@@ -0,0 +1,18 @@
<script setup lang="ts">
import { computed, useId } from 'vue'
import type { UiSelectOption } from '../adapter/contracts'
import { useUiAdapter } from '../adapter/useUiAdapter'
const props = defineProps<{ modelValue: unknown; label: string; options: UiSelectOption[]; inputId?: string; disabled?: boolean; required?: boolean; error?: string; help?: string; placeholder?: string }>()
const emit = defineEmits<{ 'update:modelValue': [value: unknown]; blur: [event: FocusEvent] }>()
const adapter = useUiAdapter()
const generatedId = useId()
const resolvedId = computed(() => props.inputId ?? `ks-select-${generatedId}`)
</script>
<template>
<div class="ks-field">
<label :for="resolvedId">{{ label }} <span v-if="required" aria-hidden="true">*</span></label>
<component :is="adapter.components.Select" :input-id="resolvedId" :model-value="modelValue" :options="options" :disabled="disabled" :invalid="Boolean(error)" :placeholder="placeholder" @update:model-value="emit('update:modelValue', $event)" @blur="emit('blur', $event)" />
<small v-if="error || help" :class="{ 'ks-danger-text': error }">{{ error ?? help }}</small>
</div>
</template>
<style scoped>.ks-field { display: grid; gap: var(--ks-space-1); } label { font-weight: 650; } small { color: var(--ks-color-neutral-600); }</style>
@@ -0,0 +1,7 @@
<script setup lang="ts">
import type { UiSeverity } from '../adapter/contracts'
import { useUiAdapter } from '../adapter/useUiAdapter'
defineProps<{ value: string; severity?: UiSeverity; iconLabel?: string }>()
const adapter = useUiAdapter()
</script>
<template><component :is="adapter.components.StatusTag" v-bind="$props" /></template>
@@ -0,0 +1,8 @@
<script setup lang="ts">
import type { UiTabItem } from '../adapter/contracts'
import { useUiAdapter } from '../adapter/useUiAdapter'
withDefaults(defineProps<{ modelValue: string; items: UiTabItem[]; ariaLabel?: string }>(), { ariaLabel: '탭' })
const emit = defineEmits<{ 'update:modelValue': [value: string] }>()
const adapter = useUiAdapter()
</script>
<template><component :is="adapter.components.Tabs" v-bind="$props" @update:model-value="emit('update:modelValue', $event)"><template #default="slotProps"><slot :active-id="slotProps.activeId" /></template></component></template>
@@ -0,0 +1,17 @@
<script setup lang="ts">
import { computed, useId } from 'vue'
import { useUiAdapter } from '../adapter/useUiAdapter'
const props = defineProps<{ modelValue: string; label: string; inputId?: string; disabled?: boolean; required?: boolean; error?: string; help?: string; rows?: number; placeholder?: string }>()
const emit = defineEmits<{ 'update:modelValue': [value: string]; blur: [event: FocusEvent] }>()
const adapter = useUiAdapter()
const generatedId = useId()
const resolvedId = computed(() => props.inputId ?? `ks-area-${generatedId}`)
</script>
<template>
<div class="ks-field">
<label :for="resolvedId">{{ label }} <span v-if="required" aria-hidden="true">*</span></label>
<component :is="adapter.components.TextArea" :input-id="resolvedId" :model-value="modelValue" :disabled="disabled" :invalid="Boolean(error)" :rows="rows" :placeholder="placeholder" @update:model-value="emit('update:modelValue', $event)" @blur="emit('blur', $event)" />
<small v-if="error || help" :class="{ 'ks-danger-text': error }">{{ error ?? help }}</small>
</div>
</template>
<style scoped>.ks-field { display: grid; gap: var(--ks-space-1); } label { font-weight: 650; } small { color: var(--ks-color-neutral-600); }</style>
@@ -0,0 +1,34 @@
<script setup lang="ts">
import { computed, useId } from 'vue'
import { useUiAdapter } from '../adapter/useUiAdapter'
const props = defineProps<{ modelValue: string; label: string; inputId?: string; disabled?: boolean; required?: boolean; error?: string; help?: string; placeholder?: string }>()
const emit = defineEmits<{ 'update:modelValue': [value: string]; blur: [event: FocusEvent] }>()
const adapter = useUiAdapter()
const generatedId = useId()
const resolvedId = computed(() => props.inputId ?? `ks-field-${generatedId}`)
</script>
<template>
<div class="ks-field">
<label :for="resolvedId">{{ label }} <span v-if="required" aria-hidden="true">*</span></label>
<component
:is="adapter.components.TextField"
:input-id="resolvedId"
:model-value="modelValue"
:disabled="disabled"
:invalid="Boolean(error)"
:placeholder="placeholder"
:aria-describedby="error || help ? `${resolvedId}-message` : undefined"
@update:model-value="emit('update:modelValue', $event)"
@blur="emit('blur', $event)"
/>
<small v-if="error || help" :id="`${resolvedId}-message`" :class="{ 'ks-danger-text': error }">{{ error ?? help }}</small>
</div>
</template>
<style scoped>
.ks-field { display: grid; gap: var(--ks-space-1); }
label { font-weight: 650; }
small { color: var(--ks-color-neutral-600); }
</style>
@@ -0,0 +1,17 @@
export { default as KsButton } from './KsButton.vue'
export { default as KsTextField } from './KsTextField.vue'
export { default as KsTextArea } from './KsTextArea.vue'
export { default as KsSelect } from './KsSelect.vue'
export { default as KsMultiSelect } from './KsMultiSelect.vue'
export { default as KsCheckbox } from './KsCheckbox.vue'
export { default as KsDateField } from './KsDateField.vue'
export { default as KsNumberField } from './KsNumberField.vue'
export { default as KsDialog } from './KsDialog.vue'
export { default as KsStatusTag } from './KsStatusTag.vue'
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 FieldShell } from './FieldShell.vue'
export { default as KsDataContextHeader } from './KsDataContextHeader.vue'
export { default as KsCommandBar } from './KsCommandBar.vue'