Additional abstractions for both sqlite and chroma.

This commit is contained in:
2025-12-16 01:36:43 +00:00
parent c2904a31ad
commit a208d447dd
20 changed files with 834 additions and 483 deletions
+6 -3
View File
@@ -1,6 +1,6 @@
<script setup lang="ts">
import type { FilterType, ObservationType, ConceptType } from '@/types'
import { OBSERVATION_TYPES, CONCEPT_TYPES } from '@/types/observation'
import { useTypes } from '@/composables/useTypes'
defineProps<{
currentFilter: FilterType
@@ -16,6 +16,9 @@ const emit = defineEmits<{
'update:conceptFilter': [concept: ConceptType | null]
}>()
// Fetch types from API (cached)
const { observationTypes, conceptTypes } = useTypes()
const tabs: { key: FilterType; label: string; icon: string }[] = [
{ key: 'all', label: 'All', icon: 'fa-layer-group' },
{ key: 'observations', label: 'Observations', icon: 'fa-brain' },
@@ -63,7 +66,7 @@ const tabs: { key: FilterType; label: string; icon: string }[] = [
@change="emit('update:typeFilter', ($event.target as HTMLSelectElement).value as ObservationType || null)"
>
<option value="">All Types</option>
<option v-for="type in OBSERVATION_TYPES" :key="type" :value="type">
<option v-for="type in observationTypes" :key="type" :value="type">
{{ type }}
</option>
</select>
@@ -78,7 +81,7 @@ const tabs: { key: FilterType; label: string; icon: string }[] = [
@change="emit('update:conceptFilter', ($event.target as HTMLSelectElement).value as ConceptType || null)"
>
<option value="">All Concepts</option>
<option v-for="concept in CONCEPT_TYPES" :key="concept" :value="concept">
<option v-for="concept in conceptTypes" :key="concept" :value="concept">
{{ concept }}
</option>
</select>
+56
View File
@@ -0,0 +1,56 @@
import { ref, onMounted } from 'vue'
export interface TypesResponse {
observation_types: string[]
concept_types: string[]
}
// Default fallback types
const DEFAULT_OBSERVATION_TYPES = ['bugfix', 'feature', 'refactor', 'discovery', 'decision', 'change']
const DEFAULT_CONCEPT_TYPES = [
'gotcha', 'pattern', 'problem-solution', 'trade-off',
'how-it-works', 'why-it-exists', 'what-changed',
'best-practice', 'anti-pattern', 'architecture',
'security', 'performance', 'testing', 'debugging', 'workflow', 'tooling',
'refactoring', 'api', 'database', 'configuration', 'error-handling',
'caching', 'logging', 'auth', 'validation'
]
// Cached types data (shared across components)
const observationTypes = ref<string[]>(DEFAULT_OBSERVATION_TYPES)
const conceptTypes = ref<string[]>(DEFAULT_CONCEPT_TYPES)
const loaded = ref(false)
const loading = ref(false)
export function useTypes() {
const fetchTypes = async () => {
if (loaded.value || loading.value) return
loading.value = true
try {
const response = await fetch('/api/types')
if (!response.ok) throw new Error('Failed to fetch types')
const data: TypesResponse = await response.json()
observationTypes.value = data.observation_types
conceptTypes.value = data.concept_types
loaded.value = true
} catch (error) {
console.error('Failed to fetch types:', error)
// Keep defaults
loaded.value = true
} finally {
loading.value = false
}
}
// Fetch on first use
onMounted(fetchTypes)
return {
observationTypes,
conceptTypes,
loaded,
loading,
fetchTypes
}
}
+61 -2
View File
@@ -1,6 +1,15 @@
export type ObservationType = 'bugfix' | 'feature' | 'refactor' | 'discovery' | 'decision' | 'change'
export type ObservationScope = 'project' | 'global'
export type ConceptType = 'gotcha' | 'pattern' | 'problem-solution' | 'trade-off' | 'how-it-works' | 'why-it-exists' | 'what-changed'
export type ConceptType =
// Semantic concepts
| 'gotcha' | 'pattern' | 'problem-solution' | 'trade-off'
| 'how-it-works' | 'why-it-exists' | 'what-changed'
// Globalizable concepts
| 'best-practice' | 'anti-pattern' | 'architecture'
| 'security' | 'performance' | 'testing' | 'debugging' | 'workflow' | 'tooling'
// Additional useful concepts
| 'refactoring' | 'api' | 'database' | 'configuration' | 'error-handling'
| 'caching' | 'logging' | 'auth' | 'validation'
export interface Observation {
id: number
@@ -26,13 +35,34 @@ export interface Observation {
export const OBSERVATION_TYPES: ObservationType[] = ['bugfix', 'feature', 'refactor', 'discovery', 'decision', 'change']
export const CONCEPT_TYPES: ConceptType[] = [
// Semantic concepts
'gotcha',
'pattern',
'problem-solution',
'trade-off',
'how-it-works',
'why-it-exists',
'what-changed'
'what-changed',
// Globalizable concepts
'best-practice',
'anti-pattern',
'architecture',
'security',
'performance',
'testing',
'debugging',
'workflow',
'tooling',
// Additional useful concepts
'refactoring',
'api',
'database',
'configuration',
'error-handling',
'caching',
'logging',
'auth',
'validation'
]
export const TYPE_CONFIG: Record<ObservationType, { icon: string; colorClass: string; bgClass: string; borderClass: string; gradient: string }> = {
@@ -44,7 +74,11 @@ export const TYPE_CONFIG: Record<ObservationType, { icon: string; colorClass: st
decision: { icon: 'fa-scale-balanced', colorClass: 'text-yellow-300', bgClass: 'bg-yellow-500/20', borderClass: 'border-yellow-500/30', gradient: 'from-yellow-500 to-yellow-700' },
}
// Default config for unknown concepts
const DEFAULT_CONCEPT_CONFIG = { icon: 'fa-tag', colorClass: 'text-slate-300', bgClass: 'bg-slate-500/20', borderClass: 'border-slate-500/40' }
export const CONCEPT_CONFIG: Record<ConceptType, { icon: string; colorClass: string; bgClass: string; borderClass: string }> = {
// Semantic concepts
gotcha: { icon: 'fa-triangle-exclamation', colorClass: 'text-red-300', bgClass: 'bg-red-500/20', borderClass: 'border-red-500/40' },
pattern: { icon: 'fa-puzzle-piece', colorClass: 'text-purple-300', bgClass: 'bg-purple-500/20', borderClass: 'border-purple-500/40' },
'problem-solution': { icon: 'fa-lightbulb', colorClass: 'text-blue-300', bgClass: 'bg-blue-500/20', borderClass: 'border-blue-500/40' },
@@ -52,4 +86,29 @@ export const CONCEPT_CONFIG: Record<ConceptType, { icon: string; colorClass: str
'how-it-works': { icon: 'fa-gear', colorClass: 'text-cyan-300', bgClass: 'bg-cyan-500/20', borderClass: 'border-cyan-500/40' },
'why-it-exists': { icon: 'fa-circle-question', colorClass: 'text-green-300', bgClass: 'bg-green-500/20', borderClass: 'border-green-500/40' },
'what-changed': { icon: 'fa-clipboard-list', colorClass: 'text-slate-300', bgClass: 'bg-slate-500/20', borderClass: 'border-slate-500/40' },
// Globalizable concepts
'best-practice': { icon: 'fa-check-circle', colorClass: 'text-emerald-300', bgClass: 'bg-emerald-500/20', borderClass: 'border-emerald-500/40' },
'anti-pattern': { icon: 'fa-ban', colorClass: 'text-red-300', bgClass: 'bg-red-500/20', borderClass: 'border-red-500/40' },
architecture: { icon: 'fa-sitemap', colorClass: 'text-indigo-300', bgClass: 'bg-indigo-500/20', borderClass: 'border-indigo-500/40' },
security: { icon: 'fa-shield-halved', colorClass: 'text-rose-300', bgClass: 'bg-rose-500/20', borderClass: 'border-rose-500/40' },
performance: { icon: 'fa-gauge-high', colorClass: 'text-orange-300', bgClass: 'bg-orange-500/20', borderClass: 'border-orange-500/40' },
testing: { icon: 'fa-vial', colorClass: 'text-teal-300', bgClass: 'bg-teal-500/20', borderClass: 'border-teal-500/40' },
debugging: { icon: 'fa-bug', colorClass: 'text-amber-300', bgClass: 'bg-amber-500/20', borderClass: 'border-amber-500/40' },
workflow: { icon: 'fa-diagram-project', colorClass: 'text-violet-300', bgClass: 'bg-violet-500/20', borderClass: 'border-violet-500/40' },
tooling: { icon: 'fa-wrench', colorClass: 'text-zinc-300', bgClass: 'bg-zinc-500/20', borderClass: 'border-zinc-500/40' },
// Additional useful concepts
refactoring: { icon: 'fa-rotate', colorClass: 'text-blue-300', bgClass: 'bg-blue-500/20', borderClass: 'border-blue-500/40' },
api: { icon: 'fa-plug', colorClass: 'text-lime-300', bgClass: 'bg-lime-500/20', borderClass: 'border-lime-500/40' },
database: { icon: 'fa-database', colorClass: 'text-sky-300', bgClass: 'bg-sky-500/20', borderClass: 'border-sky-500/40' },
configuration: { icon: 'fa-sliders', colorClass: 'text-fuchsia-300', bgClass: 'bg-fuchsia-500/20', borderClass: 'border-fuchsia-500/40' },
'error-handling': { icon: 'fa-circle-exclamation', colorClass: 'text-red-300', bgClass: 'bg-red-500/20', borderClass: 'border-red-500/40' },
caching: { icon: 'fa-bolt', colorClass: 'text-yellow-300', bgClass: 'bg-yellow-500/20', borderClass: 'border-yellow-500/40' },
logging: { icon: 'fa-file-lines', colorClass: 'text-gray-300', bgClass: 'bg-gray-500/20', borderClass: 'border-gray-500/40' },
auth: { icon: 'fa-key', colorClass: 'text-amber-300', bgClass: 'bg-amber-500/20', borderClass: 'border-amber-500/40' },
validation: { icon: 'fa-check', colorClass: 'text-green-300', bgClass: 'bg-green-500/20', borderClass: 'border-green-500/40' },
}
// Helper to get config with fallback for unknown concepts
export function getConceptConfig(concept: string) {
return CONCEPT_CONFIG[concept as ConceptType] || DEFAULT_CONCEPT_CONFIG
}