@@ -144,36 +144,36 @@ function renderToolbar() {
144144 <div class="flex flex-wrap items-center justify-between gap-3 mb-2">
145145 <div class="flex items-center gap-2 flex-1">
146146 <input id="search" type="search" placeholder="Search (name, id, version)…"
147- class="w-full md:w-96 px-3 py-2 rounded-xl border border-gray-300 dark:border-gray-700 bg-white/80 dark:bg-zinc-900/80 focus:outline-none focus:ring focus:ring-blue-500/40"
147+ class="w-full md:w-96 px-3 py-2 rounded-xl border border-gray-300 dark:border-gray-700 bg-white/80 dark:bg-zinc-900/80 focus:outline-none focus:ring focus:ring-blue-500/40 prevent-select "
148148 value="${ state . filter } ">
149149 </div>
150150
151151 <div class="flex items-center gap-2">
152152 <button id="sort-installed"
153153 class="px-3 py-2 rounded-xl border border-gray-300 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-zinc-800 ${ state . sortKey === 'installedAt' ? 'ring-1 ring-blue-500/50' : '' } "
154154 title="Sort by install date">
155- <span class="mr-1">Installed</span>
155+ <span class="mr-1 prevent-select ">Installed</span>
156156 <span aria-hidden="true">${ state . sortKey === 'installedAt' ? ( state . sortDir === 'desc' ? '↓' : '↑' ) : '' } </span>
157157 </button>
158158
159159 <button id="sort-last" class="px-3 py-2 rounded-xl border border-gray-300 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-zinc-800 ${ state . sortKey === 'lastUsed' ? 'ring-1 ring-blue-500/50' : '' } " title="Sort by last used">
160- <span class="mr-1">Last used</span>
160+ <span class="mr-1 prevent-select ">Last used</span>
161161 <span aria-hidden="true">${ state . sortKey === 'lastUsed' ? ( state . sortDir === 'desc' ? '↓' : '↑' ) : '' } </span>
162162 </button>
163163
164164 <button id="sort-name" class="px-3 py-2 rounded-xl border border-gray-300 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-zinc-800 ${ state . sortKey === 'name' ? 'ring-1 ring-blue-500/50' : '' } " title="Sort by name">
165- <span class="mr-1">Name</span>
165+ <span class="mr-1 prevent-select ">Name</span>
166166 <span aria-hidden="true">${ state . sortKey === 'name' ? ( state . sortDir === 'asc' ? '↑' : '↓' ) : '' } </span>
167167 </button>
168168
169169 <div class="h-6 w-px bg-gray-300 dark:bg-zinc-700 mx-1"></div>
170170
171171 <label class="inline-flex items-center gap-2 px-3 py-2 rounded-xl border border-gray-300 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-zinc-800 cursor-pointer">
172172 <input id="select-all" type="checkbox" class="h-4 w-4 accent-blue-600">
173- <span>Select all (visible)</span>
173+ <span class="prevent-select" >Select all (visible)</span>
174174 </label>
175175
176- <button id="select-stale" class="px-3 py-2 rounded-xl border border-gray-300 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-zinc-800"
176+ <button id="select-stale" class="px-3 py-2 rounded-xl border border-gray-300 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-zinc-800 prevent-select "
177177 title="Select items last used > 30 days ago that are not the latest version">
178178 Select stale & not-latest
179179 </button>
@@ -196,25 +196,25 @@ function itemRow(i) {
196196 <label class="flex items-center gap-3 flex-1 cursor-pointer">
197197 <input data-role="select" data-id="${ i . id } " type="checkbox" class="h-4 w-4 accent-blue-600" ${ checked } />
198198 <div class="flex flex-col">
199- <div class="text-sm font-medium">
199+ <div class="text-sm font-medium prevent-select ">
200200 ${ escapeHtml ( i . label ) }
201201 <span class="text-gray-500 dark:text-gray-400 font-normal">• ${ escapeHtml ( i . version || "unknown" ) } </span>
202202 </div>
203203 <div class="text-xs text-gray-500 dark:text-gray-400 truncate" title="${ escapeHtml ( ( i . path || i . depId ) + ( i . path ? "" : "" ) ) } ">
204- ${ escapeHtml ( i . depId ) } ${ i . path ? ` • ${ escapeHtml ( i . path ) } ` : "" }
204+ <span class="prevent-select"> ${ escapeHtml ( i . depId ) } </span> ${ i . path ? `<span class="prevent-select"> • </span> ${ escapeHtml ( i . path ) } ` : "" }
205205 </div>
206206 </div>
207207 </label>
208208
209209 <div class="flex items-center gap-2 sm:gap-3 shrink-0">
210210 <span class="text-xs px-2 py-1 rounded-full border border-gray-300 dark:border-zinc-700 text-gray-700 dark:text-gray-300 bg-gray-50 dark:bg-zinc-800">
211- Last used: ${ escapeHtml ( last ) }
211+ <span class="prevent-select"> Last used: </span> ${ escapeHtml ( last ) }
212212 </span>
213213 <span class="text-xs px-2 py-1 rounded-full border border-gray-300 dark:border-zinc-700 text-gray-700 dark:text-gray-300 bg-gray-50 dark:bg-zinc-800">
214- Installed: ${ escapeHtml ( installed ) }
214+ <span class="prevent-select"> Installed: </span> ${ escapeHtml ( installed ) }
215215 </span>
216216 <button data-role="uninstall-one" data-id="${ i . id } "
217- class="px-3 py-2 rounded-xl bg-red-600 text-white hover:bg-red-700">
217+ class="px-3 py-2 rounded-xl bg-red-600 text-white hover:bg-red-700 prevent-select ">
218218 Uninstall
219219 </button>
220220 </div>
@@ -248,7 +248,7 @@ function renderOverlay(visible = false) {
248248function renderFrame ( ) {
249249 document . body . innerHTML = `
250250 <div class="h-full w-full overflow-hidden grid grid-rows-[auto,1fr] text-[13px] text-gray-900 dark:text-gray-100">
251- <header class="px-5 py-4 shrink-0">
251+ <header class="px-5 py-4 shrink-0 prevent-select ">
252252 <h1 class="text-lg font-semibold">Uninstall components</h1>
253253 <p class="text-xs text-gray-500 dark:text-gray-400 mt-1">
254254 Select one or more installed items to uninstall. Sort by last used to find stale components quickly.
@@ -386,8 +386,19 @@ function bindEvents() {
386386 if ( id ) {
387387 if ( target . checked ) state . selected . add ( id ) ; else state . selected . delete ( id ) ;
388388 updateSelectAllCheckbox ( ) ;
389- $ ( "#uninstall-selected" ) ?. setAttribute ( "disabled" , state . selected . size ? "" : "disabled" ) ;
390- if ( state . selected . size ) $ ( "#uninstall-selected" ) ?. removeAttribute ( "disabled" ) ;
389+ const uninstallSelectedBtn = document . getElementById ( "uninstall-selected" ) ;
390+ if ( uninstallSelectedBtn ) {
391+ uninstallSelectedBtn . disabled = state . selected . size === 0 ;
392+ // Update content without having to re-render the whole toolbar
393+ // replace (<number>) in button text with current count
394+ const m = uninstallSelectedBtn . textContent . match ( / \( ( \d + ) \) / ) ;
395+ if ( m ) {
396+ uninstallSelectedBtn . textContent = uninstallSelectedBtn . textContent . replace (
397+ m [ 0 ] ,
398+ `(${ state . selected . size } )` ) ;
399+ }
400+ if ( state . selected . size ) uninstallSelectedBtn . removeAttribute ( "disabled" ) ;
401+ }
391402 }
392403 }
393404 } ) ;
@@ -531,7 +542,7 @@ function renderBootLoading(text = "Loading installed components…") {
531542 <div class="h-full w-full grid place-items-center">
532543 <div class="flex flex-col items-center gap-4 p-8 rounded-2xl bg-white/90 dark:bg-zinc-900/90 border border-gray-200 dark:border-zinc-800">
533544 <div class="h-10 w-10 rounded-full border-4 border-gray-300 dark:border-zinc-700 border-t-transparent animate-spin"></div>
534- <div class="text-sm text-gray-700 dark:text-gray-200">${ escapeHtml ( text ) } </div>
545+ <div class="text-sm text-gray-700 dark:text-gray-200 prevent-select ">${ escapeHtml ( text ) } </div>
535546 </div>
536547 </div>
537548 ` ;
0 commit comments