fuzzy find my records ken.waow.tech
embeddings pds search
6
fork

Configure Feed

Select the types of activity you want to include in your feed.

pack-meta: visible pill trigger with explicit subject, kill orphan bullet

three holistic fixes addressing user frustration with the pack-meta
line:

1. the resting state now reads "search index: saved" / "search index:
not saved" inside a real pill button. the earlier version was bare
muted text that said "saved" with no subject — users legitimately
asked "saved what?". the subject is explicit now and survives
zero-context viewing.

2. the trigger is a visible bordered pill with padding, hover, and a
0.9em chevron. the earlier 0.75em chevron on muted text was nearly
invisible — several users couldn't even tell it was a dropdown.

3. removed the orphaned "·" separator that dangled after "no text
content" because margin-left: auto pushed the menu far right. no
separator span anymore; the pill's border gives it enough visual
weight to not need one. flex gap handles spacing.

also renamed "pack" → "search index" in all user-facing copy for
consistency: the description, view-on-pds link, save button, delete
button, and delete confirm prompt. "pack" stays as the internal
record name (tech.waow.ken.pack) but isn't user-facing anymore.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

zzstoatzz 39daa31c 340fb77a

+67 -40
+7 -5
backend/src/assets/index.html
··· 62 62 63 63 <div id="pack-meta" class="pack-meta hidden"> 64 64 <span id="pack-stats" class="muted"></span> 65 - <span id="pack-meta-sep" class="sep muted">·</span> 66 65 <details id="pack-menu" class="pack-menu"> 67 - <summary id="pack-state" class="pack-menu-trigger muted">not saved</summary> 66 + <summary class="pack-menu-trigger"> 67 + <span class="pack-menu-label">search index</span> 68 + <span id="pack-state" class="pack-menu-state">not saved</span> 69 + </summary> 68 70 <div class="pack-menu-panel"> 69 71 <div id="pack-menu-desc" class="pack-menu-desc muted"></div> 70 - <a id="pack-view-link" class="pack-menu-item text-btn hidden" href="#" target="_blank" rel="noopener">view pack on PDS ↗</a> 71 - <button id="pack-save-btn" type="button" class="pack-menu-item text-btn text-btn-accent hidden">save pack to my PDS</button> 72 - <button id="pack-delete-btn" type="button" class="pack-menu-item text-btn text-btn-danger hidden">delete saved pack</button> 72 + <a id="pack-view-link" class="pack-menu-item text-btn hidden" href="#" target="_blank" rel="noopener">view search index on PDS ↗</a> 73 + <button id="pack-save-btn" type="button" class="pack-menu-item text-btn text-btn-accent hidden">save search index to my PDS</button> 74 + <button id="pack-delete-btn" type="button" class="pack-menu-item text-btn text-btn-danger hidden">delete saved search index</button> 73 75 <button id="pack-share-btn" type="button" class="pack-menu-item text-btn text-btn-accent hidden">share this search</button> 74 76 </div> 75 77 </details>
+5 -8
backend/src/assets/main.js
··· 36 36 const packViewLink = $("#pack-view-link"); 37 37 const packShareBtn = $("#pack-share-btn"); 38 38 const packSharedLabel = $("#pack-shared-label"); 39 - const packMetaSep = $("#pack-meta-sep"); 40 39 const aboutBtn = $("#about-btn"); 41 40 const aboutModal = $("#about-modal"); 42 41 const aboutOverlay = $("#about-overlay"); ··· 335 334 // leave shared-view mode behind if we were in it 336 335 packSharedLabel.classList.add("hidden"); 337 336 packMenuEl.classList.remove("hidden"); 338 - packMetaSep.classList.remove("hidden"); 339 337 340 338 if (j.persisted) { 341 339 packStateEl.textContent = "saved"; 342 340 packMenuDescEl.textContent = 343 - "the vector pack for your records is saved on your PDS as a tech.waow.ken.pack record."; 341 + "a vector index of your records lives as a record on your PDS. ken reloads it on sign-in so you can search across everything you've written without re-embedding."; 344 342 packViewLink.classList.remove("hidden"); 345 343 packViewLink.href = `https://pdsls.dev/${j.persisted_uri}`; 346 344 packDeleteBtn.classList.remove("hidden"); ··· 348 346 } else { 349 347 packStateEl.textContent = "not saved"; 350 348 packMenuDescEl.textContent = 351 - "the vector pack lives in ken's memory only. save it to your PDS to make it portable and shareable."; 349 + "a vector index of your records lives in ken's memory right now. save it to your PDS to keep it across sessions and make it portable."; 352 350 packViewLink.classList.add("hidden"); 353 351 packDeleteBtn.classList.add("hidden"); 354 352 packSaveBtn.classList.remove("hidden"); ··· 598 596 showStatus(`save error: ${e.message}`, true); 599 597 } finally { 600 598 packSaveBtn.disabled = false; 601 - packSaveBtn.textContent = "save pack to my PDS"; 599 + packSaveBtn.textContent = "save search index to my PDS"; 602 600 refreshPackState(); 603 601 } 604 602 }); ··· 606 604 packDeleteBtn.addEventListener("click", async () => { 607 605 if (!me) return; 608 606 closePackMenu(); 609 - if (!confirm("delete the saved pack record from your PDS? your in-memory search will still work until you sign out.")) return; 607 + if (!confirm("delete the saved search index from your PDS? your in-memory search will still work until you sign out.")) return; 610 608 packDeleteBtn.disabled = true; 611 609 packDeleteBtn.textContent = "deleting…"; 612 610 try { ··· 622 620 showStatus(`delete error: ${e.message}`, true); 623 621 } finally { 624 622 packDeleteBtn.disabled = false; 625 - packDeleteBtn.textContent = "delete saved pack"; 623 + packDeleteBtn.textContent = "delete saved search index"; 626 624 refreshPackState(); 627 625 } 628 626 }); ··· 740 738 packMetaEl.classList.remove("hidden"); 741 739 packStatsEl.textContent = `viewing @${handle}`; 742 740 packMenuEl.classList.add("hidden"); 743 - packMetaSep.classList.add("hidden"); 744 741 packSharedLabel.classList.remove("hidden"); 745 742 // hide signout — there's no session to clear 746 743 signoutBtn.classList.add("hidden");
+55 -27
backend/src/assets/style.css
··· 319 319 320 320 /* ------- pack menu (single-disclosure popover) ------- 321 321 * 322 - * one clickable trigger (the state label itself — "saved" or "not saved") 323 - * opens a small popover that lists every action you can take on the pack 324 - * (view, save, delete, share). keeps the resting meta line terse and 325 - * avoids the older "saved delete" side-by-side read that looked like a 326 - * nonsense phrase. 322 + * the trigger is a real pill button that always says "search index" 323 + * plus its current state ("saved" / "not saved" / "local only"). the 324 + * subject is explicit so users don't have to wonder "saved what?". 325 + * clicking the pill opens a popover with every action you can take 326 + * on the index (view on PDS, save, delete, share-this-search). 327 + * 328 + * visual weight: the pill has a visible border, padding, and a chevron 329 + * that flips when the popover is open, so it's obviously clickable — 330 + * an earlier iteration used muted text with a 0.75em chevron and was 331 + * borderline invisible. 327 332 */ 328 333 .pack-menu { 329 334 position: relative; 330 335 display: inline-block; 331 - margin-left: auto; 332 336 } 333 - .pack-menu summary { 337 + .pack-menu-trigger { 334 338 list-style: none; 335 339 cursor: pointer; 336 - display: inline-block; 337 - padding: 2px 4px; 338 - border-radius: var(--radius); 340 + display: inline-flex; 341 + align-items: center; 342 + gap: 6px; 343 + padding: 5px 10px 5px 12px; 344 + border: 1px solid var(--border); 345 + border-radius: 999px; 346 + background: transparent; 347 + color: var(--fg); 348 + font-size: var(--text-small); 349 + line-height: 1; 350 + transition: border-color 0.15s, background 0.15s; 351 + white-space: nowrap; 339 352 } 340 - .pack-menu summary::-webkit-details-marker { display: none; } 341 - .pack-menu summary::after { 342 - content: " ▾"; 343 - font-size: 0.75em; 344 - opacity: 0.6; 353 + .pack-menu-trigger::-webkit-details-marker { display: none; } 354 + .pack-menu-trigger::after { 355 + content: "▾"; 356 + font-size: 0.9em; 357 + opacity: 0.7; 345 358 margin-left: 2px; 346 359 } 347 - .pack-menu[open] summary::after { content: " ▴"; } 348 - .pack-menu summary:hover { color: var(--fg); } 349 - .pack-menu[open] summary { color: var(--fg); } 360 + .pack-menu-trigger:hover { 361 + border-color: var(--accent-dim); 362 + background: rgba(255, 255, 255, 0.025); 363 + } 364 + .pack-menu[open] .pack-menu-trigger { 365 + border-color: var(--accent); 366 + background: rgba(255, 255, 255, 0.03); 367 + } 368 + .pack-menu[open] .pack-menu-trigger::after { content: "▴"; } 369 + 370 + /* "search index" label is muted, the state word pops */ 371 + .pack-menu-label { 372 + color: var(--fg-mute); 373 + } 374 + .pack-menu-state { 375 + color: var(--accent); 376 + } 377 + 350 378 .pack-menu-panel { 351 379 position: absolute; 352 380 right: 0; 353 - top: calc(100% + 6px); 354 - min-width: 15rem; 355 - padding: 12px 14px; 381 + top: calc(100% + 8px); 382 + min-width: 17rem; 383 + padding: 14px 16px; 356 384 background: var(--bg); 357 385 border: 1px solid var(--border); 358 386 border-radius: var(--radius); 359 387 display: flex; 360 388 flex-direction: column; 361 - gap: 8px; 389 + gap: 10px; 362 390 z-index: 20; 363 - box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4); 391 + box-shadow: 0 6px 24px rgba(0, 0, 0, 0.5); 364 392 } 365 393 .pack-menu-desc { 366 394 font-size: var(--text-small); 367 - line-height: 1.4; 368 - padding-bottom: 8px; 395 + line-height: 1.5; 396 + padding-bottom: 10px; 369 397 border-bottom: 1px solid var(--border); 370 398 } 371 399 .pack-menu-item { 372 400 text-align: left; 373 - padding: 2px 0; 401 + padding: 4px 0; 374 402 font-size: var(--text-small); 375 403 } 376 404 /* on very narrow screens pin the popover to the viewport so it doesn't ··· 379 407 .pack-menu-panel { 380 408 right: auto; 381 409 left: 0; 382 - min-width: min(15rem, calc(100vw - 32px)); 410 + min-width: min(17rem, calc(100vw - 32px)); 383 411 } 384 412 } 385 413