intent-engine 0.11.1

A command-line database service for tracking strategic intent, tasks, and events
Documentation
<script setup lang="ts">
import { computed, ref } from 'vue'
import { useAppStore } from '../stores/appStore'
import { useI18n } from '../composables/useI18n'
import { MessageSquare, Send, Trash2 } from 'lucide-vue-next'
import InPlaceEditor from './InPlaceEditor.vue'
import MarkdownRenderer from './MarkdownRenderer.vue'
const { t } = useI18n()
const store = useAppStore()
const events = computed(() => store.events)
const newNote = ref('')

function getColor(type: string) {
  switch (type) {
    case 'decision': return 'text-sci-cyan border-sci-cyan bg-sci-cyan-dim'
    case 'blocker': return 'text-sci-danger border-sci-danger bg-sci-danger-dim'
    case 'milestone': return 'text-sci-success border-sci-success bg-sci-success-dim'
    default: return 'text-sci-text-sec border-sci-text-dim bg-sci-panel'
  }
}

function addNote() {
  if (!newNote.value.trim() || !store.viewingTaskId) return
  store.addEvent(store.viewingTaskId, 'note', newNote.value)
  newNote.value = ''
}

function updateEvent(id: number, newData: string) {
  if (!store.viewingTaskId) return
  store.updateEvent(store.viewingTaskId, id, newData)
}

function deleteEvent(id: number) {
  if (!store.viewingTaskId) return
  if (confirm(t('DELETE_LOG_CONFIRM'))) {
    store.deleteEvent(store.viewingTaskId, id)
  }
}
</script>

<template>
  <aside class="w-full bg-sci-panel flex flex-col h-full relative z-10">
    <!-- Integrated Header -->
    <div class="px-4 pt-4 pb-2">
      <h2 class="font-display font-bold text-sci-text-pri tracking-wider mb-1 flex items-center gap-2 text-sm">
        <MessageSquare class="w-4 h-4 text-sci-cyan" />
        {{ t('SYSTEM_LOGS') }}
      </h2>
    </div>

    <!-- Add Note Input (Seamless) -->
    <div v-if="store.viewingTaskId" class="px-4 pb-2">
      <div class="relative group">
        <input 
          v-model="newNote"
          @keyup.enter="addNote"
          type="text" 
          :placeholder="t('ADD_NOTE')" 
          class="w-full bg-transparent border border-transparent border-b-sci-border/50 rounded-none pl-0 pr-10 py-2 text-sm font-mono text-sci-text-pri focus:outline-none focus:border-b-sci-cyan transition-all placeholder-sci-text-dim/50 focus:bg-sci-base/30 focus:pl-2 focus:rounded-sm"
        >
        <button 
          @click="addNote"
          class="absolute right-2 top-2 text-sci-text-dim hover:text-sci-cyan transition-colors opacity-0 group-hover:opacity-100 focus:opacity-100"
          :disabled="!newNote.trim()"
        >
          <Send class="w-4 h-4" />
        </button>
      </div>
    </div>

    <div class="flex-1 overflow-y-auto p-4 space-y-4 scrollbar-thin scrollbar-thumb-sci-border scrollbar-track-transparent">
      <div v-if="events.length === 0" class="text-center py-8 text-sci-text-dim font-mono text-xs opacity-50">
        {{ t('NO_DATA_STREAM') }}
      </div>
      
      <div 
        v-for="event in events" 
        :key="event.id"
        class="relative pl-4 border-l group transition-all hover:pl-5 pb-6"
        :class="event?.log_type ? getColor(event.log_type).split(' ')[1] : 'border-sci-border'"
      >
        <!-- Timeline Dot -->
        <div 
          class="absolute -left-[5px] top-1.5 w-2.5 h-2.5 rounded-full border-2 bg-sci-panel z-10 transition-colors"
          :class="event?.log_type ? getColor(event.log_type).split(' ')[1] : 'border-sci-border'"
        ></div>

        <!-- Header: Time + Type + Actions -->
        <div class="flex items-center gap-2 mb-1" v-if="event?.log_type">
          <!-- Inline Time -->
          <span class="text-[10px] font-mono text-sci-text-dim opacity-60">
            {{ new Date(event.timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) }}
          </span>

          <span class="text-xs font-mono font-bold uppercase tracking-wider opacity-90" :class="getColor(event.log_type).split(' ')[0]">
            {{ t(`TYPE_${event.log_type.toUpperCase()}` as any) || event.log_type }}
          </span>
          
          <!-- Delete Action -->
          <button 
            @click="deleteEvent(event.id)"
            class="ml-auto opacity-0 group-hover:opacity-100 hover:text-sci-danger transition-opacity"
            :title="t('DELETE_LOG')"
          >
            <Trash2 class="w-3 h-3" />
          </button>
        </div>
        
        <!-- Clean Content Area -->
        <div class="text-sm text-sci-text-pri leading-relaxed w-full">
          <InPlaceEditor 
            :model-value="event.discussion_data" 
            multiline 
            markdown
            @save="(val) => updateEvent(event.id, val)"
          >
            <template #display>
              <MarkdownRenderer 
                :source="event.discussion_data" 
                small
              />
            </template>
          </InPlaceEditor>
        </div>
      </div>
    </div>
  </aside>
</template>