diff --git a/docs/superpowers/specs/2026-04-09-tag-search-design.md b/docs/superpowers/specs/2026-04-09-tag-search-design.md index 5d452763..04af3775 100644 --- a/docs/superpowers/specs/2026-04-09-tag-search-design.md +++ b/docs/superpowers/specs/2026-04-09-tag-search-design.md @@ -19,14 +19,14 @@ Two patch scripts that wire up autocomplete end-to-end with no new endpoints and **What it does:** -Inserts a `case "categories":` block before the existing `default:` case in `queryController`. When `q=category` is requested, instead of falling through to `config.categories` (which is empty), this case: +Inserts a `case "categories":` block before the existing `default:` case in `queryController`. When `q=category` is requested, instead of falling through to `config.categories` (which is an empty array — truthy, so the `default:` branch does not throw, it just returns `{categories:[]}`), this case: -1. Calls `postsCollection.distinct("properties.category")` to get every unique tag used across all published posts. +1. Calls `postsCollection.distinct("properties.category")` to get every unique tag used across all published posts. MongoDB `distinct()` on an array field correctly unwraps array values, so `["photography","travel"]` per-post becomes individual distinct entries. 2. Filters out falsy values (`Boolean` filter) and sorts alphabetically. -3. Passes the result through the existing `queryConfig(cats, { filter, limit, offset })` so the `filter=` query param works for substring matching. +3. Passes the result through `queryConfig(cats, { filter: filter?.toLowerCase(), limit, offset })` — note: `filter` is lowercased before passing to match `queryConfig`'s internal lowercase comparison, making search case-insensitive end-to-end. 4. Returns `{ categories: [...] }`. -If `postsCollection` is not available, returns an empty array gracefully. +If `postsCollection` is not available, returns `{ categories: [] }` gracefully. **Example request/response:** ``` @@ -34,30 +34,45 @@ GET /micropub?q=category&filter=pho → { "categories": ["photography"] } ``` +**OLD_SNIPPET anchor:** The patch uses the closing `break;` of the preceding `source:` case plus the `default: {` line as context for the OLD_SNIPPET, since the `default:` block itself does not contain error-throwing logic for the `categories` query (empty array is truthy). + --- ## Patch 2 — Frontend: `patch-tag-input-autocomplete.mjs` **Target:** `node_modules/@indiekit/frontend/components/tag-input/index.js` +**Micropub endpoint URL discovery:** + +The patch reads the Micropub endpoint URL using the standard IndieWeb discovery selector: + +```js +const micropubHref = document.querySelector('link[rel="micropub"]')?.href ?? "/micropub"; +``` + +This is resolved once per component instantiation. On admin pages Indiekit does not inject the `` element, so the fallback `/micropub` is always used in practice — which is correct for this deployment. + **What it does:** After the `$tagInputInput` reference is obtained inside `TagInputFieldComponent.connectedCallback()`, appends the following behaviour: -1. Creates a `` element and appends it to the component. The uid is derived from the input's `name` attribute to be stable per field. -2. Sets `$tagInputInput.setAttribute("list", datalistId)` — the browser's native datalist autocomplete now applies to the text field. -3. Adds a debounced (300ms) `input` event listener: - - If the current value is ≥ 1 char, fetches `GET /micropub?q=category&filter=` (same-origin, session cookie included by default). - - Replaces datalist `