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
3 changes: 3 additions & 0 deletions .Jules/palette.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## 2026-08-10 - Added ARIA attributes to view selector role
**Learning:** When using custom UI components acting as a segmented control or radio group, apply appropriate semantic ARIA roles like `role="group"` on the container and `:aria-pressed` on the toggle buttons (since it's a group of toggle buttons changing the view mode) to ensure screen readers correctly interpret the state. Note that if `role="radiogroup"` was used, children would need `role="radio"` and `aria-checked`.
**Action:** Always add semantic roles and state attributes (`aria-pressed` or `aria-checked`) dynamically bound to their boolean properties for view mode selectors and segment controls.
9 changes: 2 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,16 +83,11 @@ jobs:
- name: Install Playwright (chromium)
run: npx playwright install --with-deps chromium

- name: Setup Supabase CLI
uses: supabase/setup-cli@v1
with:
version: latest

- name: Boot Supabase + run two-client Realtime e2e (R1–R7)
run: |
supabase start
npx supabase start
./scripts/run-e2e.sh canvas-sync-regressions.spec.ts --project=chromium --workers=2

- name: Stop Supabase
if: always()
run: supabase stop || true
run: npx supabase stop || true
4 changes: 2 additions & 2 deletions scripts/run-e2e.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
set -euo pipefail

# Auto-fetch keys from local Supabase if not already set
SUPABASE_STATUS_ENV="${SUPABASE_STATUS_ENV:-$(supabase status -o env 2>&1 || true)}"
SUPABASE_STATUS_ENV="${SUPABASE_STATUS_ENV:-$(npx supabase status -o env 2>&1 || true)}"

if [ -z "${SUPABASE_SERVICE_ROLE_KEY:-}" ]; then
# Try new-format secret key first (Supabase v2.x uses sb_secret_* instead of HS256 JWT)
Expand All @@ -27,7 +27,7 @@ if [ -z "${VITE_SUPABASE_ANON_KEY:-}" ]; then
fi

if [ -z "${SUPABASE_SERVICE_ROLE_KEY:-}" ] || [ -z "${VITE_SUPABASE_ANON_KEY:-}" ]; then
echo "ERROR: Could not fetch Supabase keys. Is local Supabase running? (supabase start)"
echo "ERROR: Could not fetch Supabase keys. Is local Supabase running? (npx supabase start)"
exit 1
fi

Expand Down
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
36 changes: 27 additions & 9 deletions src/components/ai/ChatMessage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -371,11 +371,11 @@
.filter(taskId => !dismissedCardTaskIds.value.has(taskId))
}

function weeklyPlanPrimaryTaskIds(rec: WeeklyPlanRecommendation): string[] {

Check failure on line 374 in src/components/ai/ChatMessage.vue

View workflow job for this annotation

GitHub Actions / check

'weeklyPlanPrimaryTaskIds' is defined but never used. Allowed unused vars must match /^_/u
return rec.primaryTaskId && !dismissedCardTaskIds.value.has(rec.primaryTaskId) ? [rec.primaryTaskId] : []
}

function weeklyPlanRelatedChipIds(rec: WeeklyPlanRecommendation): string[] {

Check failure on line 378 in src/components/ai/ChatMessage.vue

View workflow job for this annotation

GitHub Actions / check

'weeklyPlanRelatedChipIds' is defined but never used. Allowed unused vars must match /^_/u
return [...new Set(rec.relatedTaskIds ?? [])]
.filter(taskId => taskId !== rec.primaryTaskId && !dismissedCardTaskIds.value.has(taskId))
.slice(0, 4)
Expand Down Expand Up @@ -2586,13 +2586,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 +2785,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 +2910,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 +3087,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 +3271,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
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
7 changes: 5 additions & 2 deletions src/components/calendar/CalendarHeader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ const toggleViewOptions = () => {
:y="popoverY"
position="bottom"
variant="menu"
:close-on-click-outside="true"
close-on-click-outside
@close="showViewOptions = false"
>
<div class="view-options-menu">
Expand Down Expand Up @@ -187,24 +187,27 @@ const toggleViewOptions = () => {
</div>
</BasePopover>

<div class="view-selector view-selector--minimal">
<div class="view-selector view-selector--minimal" role="group" aria-label="Calendar view mode">
<button
class="view-btn"
:class="{ active: viewMode === 'day' }"
:aria-pressed="viewMode === 'day'"
@click="$emit('update:viewMode', 'day')"
>
{{ $t('calendar.day') }}
</button>
<button
class="view-btn"
:class="{ active: viewMode === 'week' }"
:aria-pressed="viewMode === 'week'"
@click="$emit('update:viewMode', 'week')"
>
{{ $t('calendar.week') }}
</button>
<button
class="view-btn"
:class="{ active: viewMode === 'month' }"
:aria-pressed="viewMode === 'month'"
@click="$emit('update:viewMode', 'month')"
>
{{ $t('calendar.month') }}
Expand Down
16 changes: 8 additions & 8 deletions src/mobile/components/MobileQuickSortCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,10 @@ defineProps<{
task: Task | null
}>()
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 @@ -116,10 +116,10 @@ const {
onSwipeStart: () => onSwipeStartCapture(),
onSwipeEnd: () => onSwipeEndCapture(),
onSwipeCancel: () => onSwipeEndCapture(),
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')
})

// BUG-1453: Capture card's viewport position when swipe starts so we can
Expand Down
8 changes: 4 additions & 4 deletions src/mobile/views/MobileQuickSortView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -162,10 +162,10 @@
<MobileQuickSortCard
v-if="currentTask"
:task="currentTask"
@swipe-right="onSwipeRight"
@swipe-left="onSwipeLeft"
@swipe-up="onSwipeUp"
@swipe-down="onSwipeDown"
@swipeRight="onSwipeRight"
@swipeLeft="onSwipeLeft"
@swipeUp="onSwipeUp"
@swipeDown="onSwipeDown"
/>
</div>

Expand Down
2 changes: 1 addition & 1 deletion src/views/QuickSortView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@
:key="currentTaskId ?? undefined"
:task="currentTask"
class="stack-active"
@swipe-right="handleSave"
@swipeRight="handleSave"
@swipe-left="requestDelete"
@swipe-up="handleEditTask"
@swipe-down="handleSkip"
Expand Down
Loading