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
4 changes: 4 additions & 0 deletions .Jules/palette.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,7 @@
## 2024-07-13 - CLI 좜λ ₯의 μ˜λ¬Έλ²•μ  λ³΅μˆ˜ν˜• 및 μ—λŸ¬ λ©”μ‹œμ§€ 힌트 κ°œμ„ 
**Learning:** CLI 좜λ ₯μ—μ„œ `file(s)`, `issue(s)`, `rule(s) excluded`와 같이 κ΄„ν˜Έλ₯Ό μ‚¬μš©ν•œ λ³΅μˆ˜ν˜• ν‘œκΈ°λ²•μ€ 가독성을 λ–¨μ–΄λœ¨λ¦¬κ³  νˆ¬λ°•ν•œ(developer-centric) λŠλ‚Œμ„ μ€λ‹ˆλ‹€. λ˜ν•œ, μ—λŸ¬ λ°œμƒ μ‹œ λ‹¨μˆœ μ˜ˆμ™Έ λ‚΄μš©λ§Œ 좜λ ₯ν•˜λŠ” 것보닀 μ¦‰μ‹œ μ‹€ν–‰ κ°€λŠ₯ν•œ ν•΄κ²°μ±…(Hint)을 ν•¨κ»˜ μ œκ³΅ν•˜λŠ” 것이 CLI μ‚¬μš©μž κ²½ν—˜(DX) ν–₯상에 맀우 νš¨κ³Όμ μž…λ‹ˆλ‹€.
**Action:** 쑰건뢀 f-string(예: `s_suffix = "s" if count != 1 else ""`)을 ν™œμš©ν•˜μ—¬ λ™μ μœΌλ‘œ μ •ν™•ν•œ λ³΅μˆ˜ν˜• 단어(file/files)λ₯Ό 좜λ ₯ν•˜λ„λ‘ κ°œμ„ ν•˜κ³ , μ˜ˆμ™Έ λ°œμƒ λ‘œμ§μ—λŠ” 항상 `πŸ’‘ Hint:`λ₯Ό ν¬ν•¨ν•˜μ—¬ 후속 μ•‘μ…˜μ„ μ•ˆλ‚΄ν•˜λ„λ‘ ν‘œμ€€ν™”ν•΄μ•Ό ν•©λ‹ˆλ‹€. (단, Python 3.12 ν™˜κ²½μ˜ `black` 포맀터 ν˜Έν™˜μ„±μ„ μœ„ν•΄ λ°±μŠ¬λž˜μ‹œκ°€ ν¬ν•¨λœ 쀑첩 f-string μ‚¬μš©μ„ ν”Όν•˜κ³  λ³€μˆ˜ μΆ”μΆœμ„ ꢌμž₯ν•©λ‹ˆλ‹€.)

## 2024-05-18 - Global Search Focus Shortcut and Empty State CTA
**Learning:** Users often navigate complex dashboards primarily with the keyboard. Providing a global shortcut (`/`) to focus the search bar, alongside a visual hint in the placeholder, significantly improves navigability. Furthermore, users frequently encounter the "empty state" when loading the dashboard for the first time; instead of making them search the header for a small upload input, providing a prominent call-to-action button directly in the empty state greatly reduces friction.
**Action:** When building dashboard or list views, always include a global shortcut (like `/`) to focus the primary search/filter input, and ensure empty states always have a clear, actionable CTA that allows users to resolve the empty state directly from where they are looking.
13 changes: 12 additions & 1 deletion scanner/dashboard/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,9 @@ <h1>No findings loaded</h1>
<p>Generate a report-safe findings file, then load it above (or place it next to this page).</p>
<p><code>appguardrail scan --findings-json reports/findings.json .</code></p>
<p class="drop">Serve the repo (<code>python3 -m http.server</code>) and open <code>/dashboard/</code>, or drag a <code>findings.json</code> onto this window.</p>
<p><button id="btn-select-file">Select findings.json</button></p>
</div>`;
document.getElementById('btn-select-file').addEventListener('click', () => document.getElementById('file').click());
return;
}
const counts = {CRITICAL:0,HIGH:0,WARNING:0,INFO:0};
Expand Down Expand Up @@ -173,7 +175,7 @@ <h1>Dashboard</h1>
<div class="panel"><h2>Findings by category</h2><div class="rowlist">${catRows||'<div class="r">β€”</div>'}</div></div>
<div>
<div class="toolbar">
<input type="search" id="q" placeholder="Search message, file, rule, category" aria-label="Search findings" value="${esc(query)}">
<input type="search" id="q" placeholder="Search message, file, rule, category (Press '/')" aria-label="Search findings" value="${esc(query)}">
<select id="sev" aria-label="Filter by severity">
<option value="">All severities</option>
${SEV_ORDER.map(s=>`<option value="${s}" ${s===filterSev?'selected':''}>${s}</option>`).join('')}
Expand Down Expand Up @@ -256,6 +258,15 @@ <h1>Dashboard</h1>
render();
}

// Global keyboard shortcuts
document.addEventListener('keydown', e => {
if (e.key === '/' && document.activeElement.tagName !== 'INPUT' && document.activeElement.tagName !== 'TEXTAREA') {
e.preventDefault();
const searchInput = document.getElementById('q');
if (searchInput) searchInput.focus();
}
});

document.getElementById('file').addEventListener('change', e=>{
const f = e.target.files[0]; if(!f) return;
f.text().then(t=>{ try{ load(JSON.parse(t), f.name); }catch(err){ alert('Invalid JSON: '+err.message); } });
Expand Down
Loading