Skip to content

Commit ecae045

Browse files
feat(scratchpad): add confirmation step to clear button\n\n- Implements a two-step confirmation (Clear -> Confirm?) for the Scratchpad clear action.\n- Adds a 3-second timeout to reset the button state.\n- Uses VS Code destructive theme colors for the confirmation state.\n- Updates aria-label for better accessibility.\n\nPrevents accidental data loss in the Scratchpad.
1 parent 9d2d722 commit ecae045

2 files changed

Lines changed: 40 additions & 2 deletions

File tree

.Jules/palette.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
## 2024-05-23 - Webview Accessibility Gaps
22
**Learning:** VS Code Webviews are often implemented as raw HTML/JS and can easily miss standard accessibility features like `aria-label` or focus indicators, which native VS Code UI components handle automatically.
33
**Action:** When auditing VS Code extensions, prioritize checking any `webview` implementations for missing ARIA attributes and focus styles.
4+
5+
## 2024-06-15 - Lightweight Confirmation in Webviews
6+
**Learning:** Standard modal dialogs are too heavy for micro-interactions within Webviews. A two-step button confirmation (Click -> Confirm? -> Click) provides safety without context switching.
7+
**Action:** Implement inline state-based confirmation for destructive actions in Webviews, using `setTimeout` to auto-reset.

src/features/scratchpad/scratchpad.html

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,13 @@
7676
outline: 1px solid var(--vscode-focusBorder);
7777
outline-offset: 1px;
7878
}
79+
.btn-clear.confirm {
80+
background-color: var(--vscode-button-destructiveBackground);
81+
color: var(--vscode-button-destructiveForeground);
82+
}
83+
.btn-clear.confirm:hover {
84+
background-color: var(--vscode-button-destructiveHoverBackground);
85+
}
7986
</style>
8087
</head>
8188
<body>
@@ -88,7 +95,7 @@
8895
<div class="footer">
8996
<span id="char-count" class="char-count" aria-live="polite">0 chars</span>
9097
<div class="actions">
91-
<button id="btn-clear" class="btn-clear" aria-label="Clear Scratchpad">
98+
<button id="btn-clear" class="btn-clear" aria-label="Clear Scratchpad" title="Clear all content">
9299
Clear
93100
</button>
94101
</div>
@@ -98,6 +105,7 @@
98105
const textarea = document.getElementById('scratchpad');
99106
const charCount = document.getElementById('char-count');
100107
const btnClear = document.getElementById('btn-clear');
108+
let clearTimeoutId;
101109

102110
// Debounce function
103111
function debounce(func, delay) {
@@ -113,6 +121,17 @@
113121
charCount.textContent = `${length} char${length !== 1 ? 's' : ''}`;
114122
}
115123

124+
function resetClearButton() {
125+
if (clearTimeoutId) {
126+
clearTimeout(clearTimeoutId);
127+
clearTimeoutId = null;
128+
}
129+
btnClear.textContent = 'Clear';
130+
btnClear.classList.remove('confirm');
131+
btnClear.setAttribute('aria-label', 'Clear Scratchpad');
132+
btnClear.dataset.state = 'idle';
133+
}
134+
116135
// 监听内容变化
117136
const onInput = debounce(() => {
118137
vscode.postMessage({
@@ -127,14 +146,29 @@
127146
});
128147

129148
btnClear.addEventListener('click', () => {
130-
if (textarea.value.length > 0) {
149+
if (textarea.value.length === 0) return;
150+
151+
if (btnClear.dataset.state !== 'confirm') {
152+
// First click: Request confirmation
153+
btnClear.textContent = 'Confirm?';
154+
btnClear.classList.add('confirm');
155+
btnClear.setAttribute('aria-label', 'Confirm Clear Scratchpad');
156+
btnClear.dataset.state = 'confirm';
157+
158+
// Reset after 3 seconds
159+
clearTimeoutId = setTimeout(resetClearButton, 3000);
160+
} else {
161+
// Second click: Perform action
131162
textarea.value = '';
132163
updateCharCount();
133164
onInput();
134165
textarea.focus();
166+
resetClearButton();
135167
}
136168
});
137169

170+
textarea.addEventListener('focus', resetClearButton);
171+
138172
// 接收来自扩展的消息
139173
window.addEventListener('message', (event) => {
140174
const message = event.data;

0 commit comments

Comments
 (0)