Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions src/components/QuickSortCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,10 @@ defineProps<{
}>()

const emit = defineEmits<{
(e: 'swipe-right'): void
(e: 'swipe-left'): void
(e: 'swipe-up'): void
(e: 'swipe-down'): void
(e: 'swipeRight'): void
(e: 'swipeLeft'): void
(e: 'swipeUp'): void
(e: 'swipeDown'): void
}>()

const cardRef = ref<HTMLElement | null>(null)
Expand All @@ -105,10 +105,10 @@ const {
haptics: true,
fourDirectional: true,
mouse: true,
onSwipeRight: () => emit('swipe-right'),
onSwipeLeft: () => emit('swipe-left'),
onSwipeUp: () => emit('swipe-up'),
onSwipeDown: () => emit('swipe-down')
onSwipeRight: () => emit('swipeRight'),
onSwipeLeft: () => emit('swipeLeft'),
onSwipeUp: () => emit('swipeUp'),
onSwipeDown: () => emit('swipeDown')
})

// Card transform style - supports both horizontal and vertical movement
Expand Down
28 changes: 21 additions & 7 deletions src/components/ai/AIMemoryHealthDashboard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,17 @@
</div>

<!-- Error -->
<div v-if="error" class="error-banner glass">{{ error }}</div>
<div v-if="error" class="error-banner glass">
{{ error }}
</div>

<!-- Summary Cards -->
<div v-if="report" class="summary-cards">
<div class="card glass score-card" :class="gradeClass">
<h3>Memory Grade</h3>
<div class="grade-display">{{ report.grade }}</div>
<div class="grade-display">
{{ report.grade }}
</div>
<p>{{ gradeLabel }}</p>
</div>

Expand All @@ -56,21 +60,27 @@

<div class="card glass stat-card">
<h3>Sections</h3>
<div class="stat-value">{{ report.sections.length }}</div>
<div class="stat-value">
{{ report.sections.length }}
</div>
<p>{{ report.mode === 'full' ? 'Full assessment' : 'Quick check' }}</p>
</div>

<div class="card glass stat-card">
<h3>Duration</h3>
<div class="stat-value stat-value--small">{{ formatDuration(report.durationMs) }}</div>
<div class="stat-value stat-value--small">
{{ formatDuration(report.durationMs) }}
</div>
<p>{{ formatTime(report.timestamp) }}</p>
</div>
</div>

<!-- Empty State -->
<div v-else-if="!isRunning" class="empty-state glass">
<p>No assessment results yet.</p>
<p class="empty-hint">"Quick Check" runs heuristic tests instantly. "Full Assessment" adds LLM-as-judge context utilization tests (~30s).</p>
<p class="empty-hint">
"Quick Check" runs heuristic tests instantly. "Full Assessment" adds LLM-as-judge context utilization tests (~30s).
</p>
</div>

<!-- Sections -->
Expand Down Expand Up @@ -115,7 +125,9 @@
<span class="check-name">{{ check.name }}</span>
<span class="check-score" :class="scoreColorClass(check.score)">{{ check.score }}%</span>
</div>
<div class="check-value">{{ check.value }}</div>
<div class="check-value">
{{ check.value }}
</div>
<div v-if="check.recommendation" class="check-recommendation">
{{ check.recommendation }}
</div>
Expand Down Expand Up @@ -160,7 +172,9 @@
<span class="history-time">{{ formatTime(h.timestamp) }}</span>
</div>
</div>
<button class="btn btn-ghost" @click="handleClearHistory">Clear History</button>
<button class="btn btn-ghost" @click="handleClearHistory">
Clear History
</button>
</div>
</div>
</template>
Expand Down
32 changes: 23 additions & 9 deletions src/components/ai/AIQualityDashboard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
<button
class="btn btn-ghost"
:disabled="isRunning"
@click="handleRunTests(1)"
title="Quick check with 1 run per test"
@click="handleRunTests(1)"
>
Quick (1x)
</button>
Expand Down Expand Up @@ -45,7 +45,9 @@
<div v-if="report" class="summary-cards">
<div class="card glass score-card" :class="gradeClass">
<h3>Overall Grade</h3>
<div class="grade-display">{{ report.grade }}</div>
<div class="grade-display">
{{ report.grade }}
</div>
<p>{{ gradeLabel }}</p>
</div>

Expand All @@ -67,14 +69,18 @@

<div class="card glass stat-card">
<h3>Provider</h3>
<div class="stat-value stat-value--small">{{ report.provider }}</div>
<div class="stat-value stat-value--small">
{{ report.provider }}
</div>
<p>{{ formatTime(report.timestamp) }}</p>
</div>
</div>

<!-- Empty State -->
<div v-else-if="!isRunning" class="empty-state glass">
<h3 class="empty-title">AI Quality Assessment</h3>
<h3 class="empty-title">
AI Quality Assessment
</h3>
<p class="empty-desc">
Tests your AI assistant by sending {{ testPrompts.length }} prompts across {{ new Set(testPrompts.map(t => t.category)).size }} categories,
then judges each response against {{ rubrics.length }} quality rubrics using a second LLM call.
Expand Down Expand Up @@ -129,7 +135,7 @@
<span class="result-grade" :class="scoreColorClass(result.overallScore)">
{{ result.grade }}
</span>
<span class="result-grade-dot" :class="gradeDotClass(result.grade)"></span>
<span class="result-grade-dot" :class="gradeDotClass(result.grade)" />
</div>
</div>

Expand All @@ -142,7 +148,9 @@
<!-- AI Response Preview (first 3 lines) -->
<div class="result-response-preview">
<span class="preview-label">AI Response:</span>
<div class="preview-text">{{ result.response }}</div>
<div class="preview-text">
{{ result.response }}
</div>
</div>

<!-- Simple Verdict (derived from judge reasoning) -->
Expand All @@ -154,12 +162,16 @@
<div v-if="expandedCards.has(result.promptId)" class="expanded-content">
<div class="expanded-section">
<h4>Full AI Response</h4>
<div class="response-text">{{ result.response }}</div>
<div class="response-text">
{{ result.response }}
</div>
</div>

<div v-if="result.judgeReasoning" class="expanded-section">
<h4>Judge's Detailed Assessment</h4>
<div class="judge-text">{{ result.judgeReasoning }}</div>
<div class="judge-text">
{{ result.judgeReasoning }}
</div>
</div>

<!-- Expected Behavior -->
Expand Down Expand Up @@ -260,7 +272,9 @@
<span class="history-time">{{ formatTime(h.timestamp) }}</span>
</div>
</div>
<button class="btn btn-ghost" @click="handleClearHistory">Clear History</button>
<button class="btn btn-ghost" @click="handleClearHistory">
Clear History
</button>
</div>
</div>
</template>
Expand Down
28 changes: 20 additions & 8 deletions src/components/ai/AISetupWizard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@

<!-- โ•โ•โ• Step 1: Choose Provider โ•โ•โ• -->
<div v-if="step === 1" class="step-content">
<h2 class="step-title">Set up AI</h2>
<h2 class="step-title">
Set up AI
</h2>
<p class="step-desc">
FlowState uses AI for smart suggestions, task analysis, and chat.
Choose how you'd like to power it.
Expand All @@ -37,7 +39,9 @@
:class="{ selected: chosenProvider === 'groq' }"
@click="chosenProvider = 'groq'"
>
<div class="provider-badge recommended">Recommended</div>
<div class="provider-badge recommended">
Recommended
</div>
<Zap :size="22" class="provider-icon" />
<span class="provider-name">Groq Cloud</span>
<span class="provider-detail">
Expand Down Expand Up @@ -70,7 +74,9 @@

<!-- โ•โ•โ• Step 2a: Groq Setup โ•โ•โ• -->
<div v-if="step === 2 && chosenProvider === 'groq'" class="step-content">
<h2 class="step-title">Connect Groq</h2>
<h2 class="step-title">
Connect Groq
</h2>
<p class="step-desc">
Groq offers free AI API access with generous daily limits.
Create a free account and paste your API key below.
Expand All @@ -90,13 +96,13 @@
<label class="field-label">API Key</label>
<div class="input-row">
<input
:type="showKey ? 'text' : 'password'"
v-model="apiKey"
:type="showKey ? 'text' : 'password'"
placeholder="gsk_..."
class="text-input"
spellcheck="false"
autocomplete="off"
/>
>
<button
class="icon-btn"
:title="showKey ? 'Hide' : 'Show'"
Expand Down Expand Up @@ -133,7 +139,9 @@

<!-- โ•โ•โ• Step 2b: Ollama Setup โ•โ•โ• -->
<div v-if="step === 2 && chosenProvider === 'ollama'" class="step-content">
<h2 class="step-title">Connect Ollama</h2>
<h2 class="step-title">
Connect Ollama
</h2>
<p class="step-desc">
Ollama runs AI models locally on your machine.
Make sure Ollama is installed and running.
Expand All @@ -147,7 +155,9 @@
</div>

<div v-if="!ollamaDetected" class="ollama-steps">
<p class="setup-instruction">To set up Ollama:</p>
<p class="setup-instruction">
To set up Ollama:
</p>
<ol class="setup-list">
<li>
Install from
Expand Down Expand Up @@ -184,7 +194,9 @@
<div class="confirm-icon">
<CheckCircle2 :size="40" />
</div>
<h2 class="step-title">AI is ready!</h2>
<h2 class="step-title">
AI is ready!
</h2>
<p class="step-desc">
<template v-if="chosenProvider === 'groq'">
Groq Cloud is configured. FlowState will use Llama 3.3 70B for AI features.
Expand Down
44 changes: 27 additions & 17 deletions src/components/ai/ChatMessage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -371,15 +371,7 @@
.filter(taskId => !dismissedCardTaskIds.value.has(taskId))
}

function weeklyPlanPrimaryTaskIds(rec: WeeklyPlanRecommendation): string[] {
return rec.primaryTaskId && !dismissedCardTaskIds.value.has(rec.primaryTaskId) ? [rec.primaryTaskId] : []
}

function weeklyPlanRelatedChipIds(rec: WeeklyPlanRecommendation): string[] {
return [...new Set(rec.relatedTaskIds ?? [])]
.filter(taskId => taskId !== rec.primaryTaskId && !dismissedCardTaskIds.value.has(taskId))
.slice(0, 4)
}

function weeklyLaneTitle(rec: WeeklyPlanRecommendation): string {
const locale = weeklyPlan.value?.locale ?? 'en'
Expand Down Expand Up @@ -2586,13 +2578,17 @@
data-testid="ai-clarification"
>
<header class="ai-clarification-header">
<div class="weekly-plan-source">{{ clarification.progressLabel }}</div>
<div class="weekly-plan-source">
{{ clarification.progressLabel }}
</div>
<h2>{{ clarification.locale === 'he' ? 'ืฉืืœื” ืงืฆืจื” ืœืคื ื™ ื”ืชื›ื ื•ืŸ' : 'Quick question before planning' }}</h2>
<p>{{ clarification.summary }}</p>
<details v-if="clarificationDebugLines(clarification).length" class="ai-debug-details">
<summary>{{ clarification.locale === 'he' ? 'ืœืžื” ืื ื™ ืฉื•ืืœ?' : 'Why ask?' }}</summary>
<ul>
<li v-for="line in clarificationDebugLines(clarification)" :key="line">{{ line }}</li>
<li v-for="line in clarificationDebugLines(clarification)" :key="line">
{{ line }}
</li>
</ul>
</details>
</header>
Expand Down Expand Up @@ -2781,7 +2777,9 @@
</div>
<h2>{{ weeklyPlan.headline }}</h2>
<p>{{ weeklyPlan.weekRead.summary }}</p>
<p v-if="weeklyPlan.weekRead.mainTradeoff && !isCompactWeeklyPlan" class="weekly-plan-muted">{{ weeklyPlan.weekRead.mainTradeoff }}</p>
<p v-if="weeklyPlan.weekRead.mainTradeoff && !isCompactWeeklyPlan" class="weekly-plan-muted">
{{ weeklyPlan.weekRead.mainTradeoff }}
</p>
</header>

<section
Expand Down Expand Up @@ -2904,8 +2902,12 @@
<div class="weekly-plan-focus" dir="auto">
{{ weeklyPlan.locale === 'he' ? 'ื ืชื™ื‘' : 'Lane' }}
</div>
<h3 dir="auto">{{ weeklyLaneTitle(rec) }}</h3>
<p dir="auto">{{ weeklyLaneSubtitle(rec) }}</p>
<h3 dir="auto">
{{ weeklyLaneTitle(rec) }}
</h3>
<p dir="auto">
{{ weeklyLaneSubtitle(rec) }}
</p>
</div>
<button
v-if="!props.wideMode"
Expand Down Expand Up @@ -3077,12 +3079,18 @@
>
<div class="weekly-plan-focus" dir="auto">
{{ isCompactWeeklyPlan && weeklyPlan.locale === 'he' ? 'ื ืชื™ื‘' : isCompactWeeklyPlan ? 'Lane' : rec.focusArea }}
<template v-if="isCompactWeeklyPlan">: {{ rec.focusArea }}</template>
<template v-if="isCompactWeeklyPlan">
: {{ rec.focusArea }}
</template>
</div>
<h3>{{ rec.rank }}. {{ rec.title }}</h3>
<p v-if="!isCompactWeeklyPlan">{{ rec.whyThisMatters }}</p>
<p v-if="!isCompactWeeklyPlan">
{{ rec.whyThisMatters }}
</p>
<p>{{ rec.whyThisWeek }}</p>
<p v-if="rec.riskIfIgnored && !isCompactWeeklyPlan" class="weekly-plan-muted">{{ rec.riskIfIgnored }}</p>
<p v-if="rec.riskIfIgnored && !isCompactWeeklyPlan" class="weekly-plan-muted">
{{ rec.riskIfIgnored }}
</p>
<p class="weekly-next-action">
<strong>{{ weeklyPlan.locale === 'he' ? 'ื”ืฆืขื“ ื”ื‘ื' : 'Next action' }}:</strong>
{{ rec.nextAction }}
Expand Down Expand Up @@ -3255,7 +3263,9 @@
<footer v-if="weeklyPlan.deferrals.length && !isCompactWeeklyPlan" class="weekly-plan-footer">
<div v-if="weeklyPlan.deferrals.length">
<strong>{{ weeklyPlan.locale === 'he' ? 'ืœื“ื—ื•ืช ื‘ื›ื•ื•ื ื”' : 'Intentional deferrals' }}</strong>
<p v-for="defer in weeklyPlan.deferrals" :key="defer.taskId">{{ defer.reason }}</p>
<p v-for="defer in weeklyPlan.deferrals" :key="defer.taskId">
{{ defer.reason }}
</p>
</div>
</footer>
</article>
Expand Down Expand Up @@ -3335,7 +3345,7 @@
v-else-if="renderedContent"
class="message-text markdown-body"
:dir="effectiveDirection"
v-html="renderedContent"

Check warning on line 3348 in src/components/ai/ChatMessage.vue

View workflow job for this annotation

GitHub Actions / check

'v-html' directive can lead to XSS attack
/>
<span v-if="inlineFeedbackStatus" class="weekly-question-status inline-feedback-status">
{{ inlineFeedbackStatus }}
Expand Down
3 changes: 2 additions & 1 deletion src/components/base/BaseInput.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@
<div class="input-container">
<slot name="prefix" />

<input :dir="inputDir"
<input
:id="inputId"
ref="inputRef"
v-model="localValue"
:dir="inputDir"
:type="type"
:placeholder="placeholder"
:disabled="disabled"
Expand Down
Loading
Loading