Skip to content
Open
3 changes: 3 additions & 0 deletions .Jules/palette.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,6 @@
## 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-19 - Actionable Empty States
**Learning:** Empty states should act as helpful dead-ends with actionable Call-To-Actions (CTAs) rather than passive text. When users encounter an empty state (e.g., no findings loaded, or no search results), providing a direct action like "Browse for file..." or "Clear filters" reduces friction and cognitive load.
**Action:** When designing or refactoring empty states, always ask "What is the most common action the user needs to take to escape this state?" and provide a native HTML `<button>` to facilitate it. Ensure form inputs are always accompanied by screen-reader accessible `<label>`s, using visually hidden CSS if visual labels are not permitted by design constraints.
5 changes: 4 additions & 1 deletion scanner/dashboard/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@
<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 style="margin:20px 0;"><button type="button" onclick="document.getElementById('file').click()">Browse for file...</button></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>
</div>`;
return;
Expand Down Expand Up @@ -173,15 +174,17 @@ <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">
<label for="q" style="position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0;">Search findings</label>
<input type="search" id="q" placeholder="Search message, file, rule, category" aria-label="Search findings" value="${esc(query)}">
<label for="sev" style="position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0;">Filter by severity</label>
<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('')}
</select>
</div>
<div class="panel"><table>
<thead><tr><th>Severity</th><th>Finding</th><th>File</th><th>Category</th><th>Status</th></tr></thead>
<tbody>${rows||'<tr><td colspan="5" style="color:var(--muted);padding:24px">No findings match the filter.</td></tr>'}</tbody>
<tbody>${rows||`<tr><td colspan="5"><div class="empty">No findings match the filter.<br><br><button type="button" onclick="document.getElementById('q').value=''; document.getElementById('sev').value=''; query=''; filterSev=''; render();">Clear filters</button></div></td></tr>`}</tbody>
</table></div>
</div>
</div>`;
Expand Down
Loading