Release v0.4.2 — Tags & Collections #9

Open
Gravity Bot wants to merge 25 commits from release/0.4.2 into main
Collaborator

Release v0.4.2 — Tags & Collections

Full Tags & Collections feature set for FontShelf. Adds tag CRUD, icon tagging, tag navigation, cross-tag filtering, and sticky per-tag view settings.

Changes

Epic #850 — Tag CRUD & Management

  • OP#1860: Tag service layer (CreateTag, UpdateTag, DeleteTag, ReorderTags, ListTags)
  • OP#1861: Tag CRUD handler routes (GET/POST/PATCH/DELETE /tags, POST /tags/reorder)
  • OP#1862: Tag bar UI with inline create form and color picker
  • OP#1863: Unit tests for tag CRUD handlers (10 tests)
  • OP#1885: Right-click context menu to delete tags from tag bar

Epic #851 — Icon Tagging

  • OP#1864: Icon tagging service layer (TagIcon, UntagIcon, GetIconTags, GetTaggedIcons)
  • OP#1865: Icon tagging handler routes and tag checkbox component
  • OP#1866: HTMX tag assignment in icon detail modal
  • OP#1867: Quick-tag overlay on icon grid cards (hover "+" button with dropdown)
  • OP#1868: Unit tests for icon tagging (18 handler + 5 component tests)

Epic #852 — Tag Navigation

  • OP#1869: Tag page route with filtered icon grid (?tag= query param)
  • OP#1870: Search and style filters within tag pages (tag passthrough)
  • OP#1871: Cross-tag colored dots on icon cards (batch tag summary)
  • OP#1872: Unit tests for tag navigation (11 tests)

Epic #853 — Tag Page Filter Controls

  • OP#1873: tag_view_settings migration (003) and model
  • OP#1874: Filter bar component with toggleable cross-tag pills
  • OP#1875: Sticky filter settings persistence (DB-backed, per-user per-tag)
  • OP#1876: Unit tests for tag filters (10 tests)

Bug Fixes

  • OP#1881: Fix route conflict — GET /icons/{id} swallowing /icons/{id}/tags requests
  • OP#1882: Fix quick-tag dropdown not loading tags (HTMX trigger on hidden element + z-index)
  • OP#1883: Fix 500 error when toggling last hidden tag filter (nil slice NOT NULL violation)

Checklist

  • All version tasks closed in Gravity PM
  • Tests passing (go test ./...)
  • go vet clean
  • Version file matches Gravity PM version (VERSION = 0.4.2)
  • Draft Forgejo release created

References

Version: 0.4.2 — Tags & Collections (Gravity PM)

## Release v0.4.2 — Tags & Collections Full Tags & Collections feature set for FontShelf. Adds tag CRUD, icon tagging, tag navigation, cross-tag filtering, and sticky per-tag view settings. ### Changes #### Epic #850 — Tag CRUD & Management - OP#1860: Tag service layer (CreateTag, UpdateTag, DeleteTag, ReorderTags, ListTags) - OP#1861: Tag CRUD handler routes (GET/POST/PATCH/DELETE /tags, POST /tags/reorder) - OP#1862: Tag bar UI with inline create form and color picker - OP#1863: Unit tests for tag CRUD handlers (10 tests) - OP#1885: Right-click context menu to delete tags from tag bar #### Epic #851 — Icon Tagging - OP#1864: Icon tagging service layer (TagIcon, UntagIcon, GetIconTags, GetTaggedIcons) - OP#1865: Icon tagging handler routes and tag checkbox component - OP#1866: HTMX tag assignment in icon detail modal - OP#1867: Quick-tag overlay on icon grid cards (hover "+" button with dropdown) - OP#1868: Unit tests for icon tagging (18 handler + 5 component tests) #### Epic #852 — Tag Navigation - OP#1869: Tag page route with filtered icon grid (?tag= query param) - OP#1870: Search and style filters within tag pages (tag passthrough) - OP#1871: Cross-tag colored dots on icon cards (batch tag summary) - OP#1872: Unit tests for tag navigation (11 tests) #### Epic #853 — Tag Page Filter Controls - OP#1873: tag_view_settings migration (003) and model - OP#1874: Filter bar component with toggleable cross-tag pills - OP#1875: Sticky filter settings persistence (DB-backed, per-user per-tag) - OP#1876: Unit tests for tag filters (10 tests) #### Bug Fixes - OP#1881: Fix route conflict — `GET /icons/{id}` swallowing `/icons/{id}/tags` requests - OP#1882: Fix quick-tag dropdown not loading tags (HTMX trigger on hidden element + z-index) - OP#1883: Fix 500 error when toggling last hidden tag filter (nil slice NOT NULL violation) ### Checklist - [x] All version tasks closed in Gravity PM - [x] Tests passing (`go test ./...`) - [x] `go vet` clean - [x] Version file matches Gravity PM version (VERSION = 0.4.2) - [x] Draft Forgejo release created ### References Version: 0.4.2 — Tags & Collections (Gravity PM)
Implements CreateTag, UpdateTag, DeleteTag, ReorderTags, ListTags,
and GetTag functions in internal/services/tags.go. All operations
are user-scoped and ReorderTags uses a transaction for atomicity.

Closes OP#1860
Creates TagHandler with POST/PATCH/DELETE/GET routes for tag
management, wired into main.go behind RequireAuth middleware.
Supports form-based create/update, JSON reorder, and toast feedback.

Closes OP#1861
Adds TagBar component with "All" tab, user tag tabs with color dots,
inline create form with color picker, and integrates into the browse
page. Updates handlers to return TagBar HTML for HTMX swaps.

Closes OP#1862
Tests auth requirements, input validation, and error handling for
all tag handler endpoints (create, update, delete, list, reorder).
10 tests covering auth, empty name, invalid UUID, and bad input.

Closes OP#1863
Adds TagIcon, UntagIcon, GetIconTags, and GetTaggedIcons functions.
TagIcon is idempotent with ownership verification. GetTaggedIcons
supports search, style, and library filters with pagination.

Closes OP#1864
Adds POST/DELETE/GET routes for /icons/{id}/tags/{tagID} to tag/untag
icons. Creates IconTagList templ component with toggle checkboxes
and color dots for HTMX-driven tag assignment.

Closes OP#1865
Replace placeholder "coming soon" section with live tag checkboxes
loaded via hx-get="/icons/{id}/tags" on modal open.

Closes OP#1866

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Hover over any icon card to reveal a "+" button. Clicking opens a
dropdown that loads tag checkboxes via HTMX for inline tag/untag.

Closes OP#1867

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Handler tests: auth, invalid icon/tag ID for TagIcon, UntagIcon, GetIconTags.
Component tests: IconTagList (empty, with tags, applied checkmark, HTMX targets),
quickTagButton (Alpine.js state, HTMX endpoint, escape/outside close).

Closes OP#1868

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Browse handler reads ?tag= query param and calls GetTaggedIcons to show
only icons belonging to the selected tag. Search bar passes the active
tag through to HTMX requests so search works within tag pages.

Closes OP#1869

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Library switcher now includes #tag-value in hx-include so switching
libraries preserves the active tag context on tag pages.

Closes OP#1870

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Batch-fetch tag summaries for icons on tag pages and render small
colored dots below each card's label showing other applied tags.
Active tag excluded from dots; hover shows tag name via title attr.

Closes OP#1871

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tests cover: tag bar rendering (All tab, user tags, active state,
color dots, create button), cross-tag dot display on icon cards,
and search bar tag passthrough for HTMX.

Closes OP#1872

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Migration 003 creates tag_view_settings table for sticky per-tag
filter preferences (hidden tags, library, style). One row per
user-tag pair with cascade deletes.

Closes OP#1873

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Cross-tag filter pills on tag pages show other tags in the collection.
Toggle a pill to hide/show icons with that tag. Service layer:
GetTagFilterOptions, SaveTagViewSettings, GetTagViewSettings. Handler
routes for GET/POST filter toggle with HTMX partial swap.

Closes OP#1874

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Load saved hidden_tags on tag page load and apply to icon query.
GetTaggedIcons now excludes icons matching hidden cross-tags via
NOT EXISTS subquery. Filter toggle triggers HX-Trigger event to
refresh the icon grid with updated settings.

Closes OP#1875

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Component tests: empty state, options rendering, toggle endpoint URLs,
hidden state styling, ID attribute. Handler tests: auth, invalid IDs
for GetTagFilters and ToggleTagFilter.

Closes OP#1876

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
chore(release): bump version to 0.4.2
All checks were successful
CI / build (pull_request) Successful in 1m31s
b095ea1c06
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix(tags): resolve route conflict between icon detail and icon tags
All checks were successful
CI / build (pull_request) Successful in 41s
5f8c1b0ab7
GET /icons/{id} was matching /icons/{uuid}/tags requests because the
wildcard swallowed the /tags suffix. Register icon tag routes directly
on the outer mux with explicit method+path patterns so they take
precedence over the broader icon detail route.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: use Alpine click event to trigger HTMX tag loading in quick-tag dropdown
All checks were successful
CI / build (pull_request) Successful in 3m25s
212d083494
The hx-trigger="intersect once" strategy fails when the dropdown starts
hidden via Alpine x-show (display: none prevents IntersectionObserver
from ever firing). Switch to a custom "loadTags" event dispatched from
the Alpine @click handler on first open, with a loaded flag to prevent
duplicate requests.

Refs OP#1877

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: use htmx.ajax() for quick-tag dropdown instead of event trigger
All checks were successful
CI / build (pull_request) Successful in 40s
01af9a04a9
htmx.trigger() dispatches a custom DOM event that relies on HTMX having
initialized an event listener on the target element. This fails when HTMX
hasn't processed the hidden element or after grid content swaps. Switch
to htmx.ajax() which directly issues the request — no event indirection,
works regardless of element visibility or initialization state.

Refs OP#1877

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: elevate icon card z-index when quick-tag dropdown is open
All checks were successful
CI / build (pull_request) Successful in 42s
00ce8ee5bb
Each card's hover:scale transform creates a stacking context, so the
dropdown's z-50 only applies within its own card. Use x-effect to set
z-index 50 on the parent card whenever the dropdown is open, ensuring
it renders above neighboring cards.

Refs OP#1877

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: coerce nil hidden_tags to empty slice before DB insert
All checks were successful
CI / build (pull_request) Successful in 3m18s
331a5a4690
When the last hidden tag is toggled back to visible, the slice becomes
nil. pgx sends nil as SQL NULL, violating the NOT NULL constraint on
the hidden_tags column and causing a 500 error.

Refs OP#1877

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: add right-click context menu to delete tags from tag bar
All checks were successful
CI / build (pull_request) Successful in 44s
e78ab8c3e6
Right-clicking a tag tab opens a context menu with a Delete option.
Uses htmx.ajax() for the DELETE request and removes the tag element
from the DOM on success. If the user is on the deleted tag's page,
redirects to the "All" view.

Also fixes a bug where TriggerToast was overwritten by a second
HX-Trigger header set for tagDeleted — merged both events into
a single JSON object.

Refs OP#1877

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
All checks were successful
CI / build (pull_request) Successful in 44s
Required
Details
This pull request can be merged automatically.
You are not authorized to merge this pull request.
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
mike/fontshelf!9
No description provided.