Skip to content
Closed
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
@@ -1,3 +1,6 @@
## 2026-05-16 - Add ARIA live region for dynamic form validation
**Learning:** For multi-step wizard forms or dynamic client-side validation, screen readers need explicit context when errors appear. Using a hidden error div without `role="alert"` and `aria-live="polite"` prevents the error from being announced.
**Action:** Always link the input field to its error container using `aria-describedby` and dynamically toggle `aria-invalid="true"` on the input when it fails validation.
## 2024-05-22 - Actionable Empty States
**Learning:** Empty states that simply state a lack of data (e.g., "No scan history yet.") are unhelpful. Providing actionable guidance or a direct call-to-action (e.g., "Trigger a scan from the domain overview.") significantly improves the user experience by guiding them on what to do next.
**Action:** Always include actionable guidance or clear calls-to-action in empty states across the application.
12 changes: 6 additions & 6 deletions src/views/dashboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1670,7 +1670,7 @@ export function renderDomainPanel({
// add-domain CTA out of the way when the user is just narrowing a list.
tableBody = isFiltered
? `<div class="empty-state">
<p>No domains match these filters.</p>
<p>No domains match these filters. Try adjusting your search or clearing filters.</p>
<a href="/dashboard" class="btn btn-secondary">Clear filters</a>
</div>`
: `<div class="empty-state">
Expand Down Expand Up @@ -2290,7 +2290,7 @@ export function renderDomainDetailPage({
const historySection =
scanHistory.length > 0
? `<ul class="history-list">${historyItems}</ul>`
: `<p style="color:var(--clr-text-muted);font-size:0.875rem;padding:0.75rem 0">No scan history yet.</p>`;
: `<p style="color:var(--clr-text-muted);font-size:0.875rem;padding:0.75rem 0">No scan history yet. Click "Scan Now" above to trigger your first scan.</p>`;

const body = `<div class="domain-detail-header">
<span class="grade-badge ${gradeClass(grade)}">${esc(grade)}</span>
Expand Down Expand Up @@ -2358,7 +2358,7 @@ const GRADE_RANK_FOR_SPARKLINE: Record<string, number> = {

function renderSparkline(entries: HistoryScanEntry[]): string {
if (entries.length === 0) {
return `<p style="color:var(--clr-text-muted);font-size:0.875rem;padding:0.75rem 0">No scans yet to chart.</p>`;
return `<p style="color:var(--clr-text-muted);font-size:0.875rem;padding:0.75rem 0">No scans yet to chart. Trigger a scan from the domain overview.</p>`;
}
const width = 600;
const height = 80;
Expand Down Expand Up @@ -2408,7 +2408,7 @@ function renderDriftCell(

function renderDriftTable(entries: HistoryScanEntry[]): string {
if (entries.length === 0) {
return `<p style="color:var(--clr-text-muted);font-size:0.875rem;padding:0.75rem 0">No scans yet.</p>`;
return `<p style="color:var(--clr-text-muted);font-size:0.875rem;padding:0.75rem 0">No scans yet. Trigger a scan from the domain overview.</p>`;
}
// Rows are newest-first. To highlight "changed vs the prior (older) scan",
// we compare each row to the NEXT row in the list (which is chronologically
Expand Down Expand Up @@ -2626,7 +2626,7 @@ ${error ? `<div class="bulk-error">${esc(error)}</div>` : ""}`;
</p>
${
results.results.length === 0
? `<p class="bulk-summary">No domains parsed from the submission.</p>`
? `<p class="bulk-summary">No domains parsed from the submission. Please check your input format and try again.</p>`
: `<table class="bulk-results-table">
<thead>
<tr>
Expand Down Expand Up @@ -2940,7 +2940,7 @@ export function renderApiKeysPage({

let table: string;
if (keys.length === 0) {
table = `<p style="color:var(--clr-text-muted);font-size:0.875rem">No API keys yet.</p>`;
table = `<p style="color:var(--clr-text-muted);font-size:0.875rem">No API keys yet. Generate your first one above.</p>`;
} else {
const rows = keys
.map((k) => {
Expand Down
2 changes: 1 addition & 1 deletion test/mcp.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import { describe, expect, it, vi } from "vitest";
import {
handleMcpRequest,
MCP_PROTOCOL_VERSION,
Expand Down