-
v0.2.1
Stablereleased this
2026-04-27 20:25:39 +00:00 | 22 commits to main since this releaseRelease 0.2.1
Fixed
- Tag-editor popover no longer renders empty (no quick-assign, no existing tags) on first open of a fresh browser session. The
chrome.runtime.onMessagelistener now registers synchronously at SW top-level eval, gated by an init promise, instead of afterawait storage.open()where Chrome could dispatch the wakeup message into the gap before the listener existed
Downloads
-
Source code (ZIP)
1 download
-
Source code (TAR.GZ)
2 downloads
- Tag-editor popover no longer renders empty (no quick-assign, no existing tags) on first open of a fresh browser session. The
-
v0.2.0
Stablereleased this
2026-04-27 20:25:39 +00:00 | 25 commits to main since this releaseRelease 0.2.0
Initial Chrome Web Store release (extension ID fkbifpikpnlgifinepofopdooakdojli). Ships the full XTagger MV3 extension: storage layer, X.com platform adapter, interactive tag UI, onboarding, accessibility, E2E tests, and packaging. Phase 0 through Phase 6 of foundational work consolidated into the public release.
Changed
- Trim extension description to 132-char Chrome Web Store limit
Fixed
- Escape user-supplied tag content at the render boundary (XSS hardening)
- Dedupe concurrent
IDBAdapter.open()calls onto a singleindexedDB.openrequest, preventing the lost-raceerr()that left the message router unregistered wheninitialise()andonInstalled/onStartupopened storage in parallel
Build
- Align
scripts/package-extension.tsvalidator paths with Vite's HTML emit layout
Added (Phase 1: Storage Layer)
IDBAdapter: fullStoragePortimplementation using IndexedDB- Tag CRUD with indexed queries
- Soft delete with
purgeDeletedTags - Bulk save for import operations
- Settings persistence
- Schema version tracking
MigrationService: versioned forward-only schema migrationsChromeAdapter:BrowserPortimplementation for Chrome MV3 messagingMessageRouter: typed request/response channel routing in service workerConsoleLogger/NoopLogger:LoggerPortimplementations- Typed message protocol (
src/shared/messages.ts): all 12 channels defined - Fully-wired background service worker with proper MV3 lifecycle handling
- Integration tests: IDBAdapter (17 tests), ImportExport (9 tests), MigrationService (4 tests)
fake-indexeddbdev dependency for Node-based IDB testing
Added (Phase 0: Foundation)
- Core domain:
Result<T,E>,EventBus, entity types, Zod schemas - Port interfaces:
StoragePort,PlatformPort,BrowserPort,LoggerPort - Services:
TagService,ImportExportService,ConflictResolver,ColorPalette - Schema migration skeleton
- Chrome MV3 manifest
- X.com selector config v1
- ADRs 001–003
- Forgejo Actions CI/CD pipeline
Added (Phase 2: Platform Adapter)
SelectorEngine: 4-strategy fallback chain (testid → aria → structural → text)- External JSON config loading
- Per-selector failure counting + recovery detection
selector:failed/selector:recoveredEventBus integration- Diagnostics API for popup debug panel
UserDetector: finds@usernamefrom tweet cards via href parsing + @ text- Deduplication within a scan pass
- Reserved path filtering (/home, /explore, etc.)
- Username normalisation (lowercase)
NavigationObserver: SPA navigation detection via History API patching + popstatenavigation:changedEventBus integration- Correct previous/current URL tracking
InjectionManager: Shadow DOM tag pill injectioncompactmode: coloured 8px dots with hover tooltipspillsmode: coloured pill with tag name text- WeakRef tracking for GC-safe element management
remove()/removeAll()for cleanup on navigation
InjectionPipeline: full MutationObserver-driven injection orchestrator- In-memory tag cache (Map<username, Tag[]>) to avoid redundant IPC
- requestAnimationFrame batching (max 20 elements/frame)
- Cache invalidation on tag:created/updated/deleted events
- Navigation-triggered cleanup + re-scan
- Settings change propagation (displayMode switches)
FailureNotifier: Shadow DOM notification banner for selector failures- Auto-dismisses after 8 seconds
- Deduplicated (one banner at a time)
XPlatformAdapter: wires above modules intoPlatformPort- Full content script entry point (
src/ui/content/index.ts)- Settings-aware boot (respects
hiddendisplay mode) - Bundled selector config loading via
chrome.runtime.getURL - Duplicate-init guard
- Settings-aware boot (respects
- ADR-005: Platform adapter design decisions
- Unit tests: SelectorEngine (15), UserDetector (9), InjectionManager (11), NavigationObserver (7)
Added (Phase 3: Interactive UI)
HoverTrigger: event-delegation hover listener shows 🏷️ icon on username hover- Stamped with
data-usernamefor callback identification - Click on icon →
onAddTagcallback - Click on existing pill →
onEditTagcallback - Deduplication (one icon at a time), auto-hide on mouse-leave
- Stamped with
TagEditorPopover: full inline tag editor in Shadow DOM- Add mode: empty form, saves via
tags:createIPC - Edit mode: pre-filled form with delete button, saves via
tags:updateIPC - 16-swatch colour palette grid with selection indicator
- Tag name autocomplete from all existing tag names
- Optional notes field (toggle, 500-char limit with counter)
- Existing user tags shown as mini pills (click to switch to edit mode)
- Keyboard shortcuts: Enter to save, Escape to close / close autocomplete
- Viewport-aware positioning (flips above anchor near screen bottom)
- Singleton: opening a new popover closes the previous
- Add mode: empty form, saves via
- Popup dashboard (
src/ui/popup/): full management UI- Home: search bar (username + tag name filter), tagged user list with pills, empty state
- Import: drag-and-drop / file browse / paste, preview (stats + checksum badge), conflict strategy radio, apply
- Export: JSON download, compact XTAG copy-to-clipboard, per-format size info
- Settings: display mode, theme, extended palette, hover-to-edit; auto-saved on change
- Full CSS theming: dark/light with CSS custom properties, respects OS preference
- Broadcasts
settings:pushto open X.com tabs after settings change
InjectionManagerupdated to stampdata-usernameanddata-color-indexon pills- Content script entry point wires HoverTrigger + TagEditorPopover into the pipeline
- ADR-005 preserved; no new architectural decisions needed (follows established patterns)
- Unit tests: TagEditorPopover (9), HoverTrigger (8)
Added (Phase 4: Polish, Onboarding & Accessibility)
ContextMenuManager: right-click "Tag @username" entry on X.com links/selection- Extracts username from profile URLs or @-prefixed selected text
- "Open XTagger" popup shortcut
- Registered on install/update/startup (handles update refresh)
- Onboarding page (
public/onboarding.html+src/ui/onboarding/main.ts)- 4-step walkthrough: hover → add tag → see in feed → share
- Keyboard navigation (← / → arrow keys)
- Progress dots (clickable)
- Privacy assurance callout
- Auto-opens as a tab on fresh install via
onInstalledhandler
Announcer: ARIA live region for screen reader notifications- Announces tag saved/deleted events politely
- Visually hidden, accessible to all screen readers
- Auto-clears after 3 seconds
- Keyboard navigation in
TagEditorPopovercolour palette- Arrow keys (←/→/↑/↓), Home/End for palette navigation
role="radiogroup"+role="radio"+aria-pressedfor full ARIA compliance- Roving
tabindexpattern (only selected swatch is in tab order)
settings:pushmessage handling in content script- Popup broadcasts settings changes to all open X.com tabs
- Display mode changes take effect immediately without page reload
content:open-tag-editormessage channel- Background sends this from context menu clicks to the active tab
- Background worker updated with context menu registration and onboarding tab open
- Manifest v2 → added
contextMenus+tabspermissions,onboarding.htmlweb_accessible_resource - Extension icons generated (16, 32, 48, 128px PNG) from Python Pillow
- Vite build config updated with
onboardingentry point - Unit tests: Announcer (5), ContextMenuManager (6)
Added (Phase 5: E2E Tests, CI & Packaging)
- Playwright E2E test suite (
tests/e2e/)fixtures.ts: extension loading fixture usinglaunchPersistentContextglobal-setup.ts: auto-builds extension before testspopup.test.ts: 10 tests: popup loads, nav, settings, export, import errortag-creation.test.ts: 3 tests: hover icon, editor opens, full create flowimport-export.test.ts: 5 tests: export, clipboard, preview, apply, errormock-feed.html: X.com DOM mock for content script testing (no auth needed)
playwright.config.ts: chromium-only, global setup, CI-aware reportersscripts/verify-selectors.js: daily X.com selector health check- Opens X.com explore page in headless Chromium
- Tests each strategy for each selector key
- Writes
selector-report/report.json+ screenshots - Referenced by
.forgejo/workflows/selector-check.yml
scripts/package-extension.ts: production build + zip + SHA-256 checksum- Validates all required files are present
- Enforces bundle size budgets before packaging
- Outputs
xtagger-v<version>.zip+.sha256file
- CI workflow updated (
.forgejo/workflows/ci.yml)- Split into 4 jobs:
test→build→e2e→release builduploads dist/ artifact (shared to E2E and release jobs)e2edownloads dist/, installs Playwright, runs full suitereleasejob fires onv*tags, packages and attaches zip + checksum- Bundle size check improved: per-file budgets with pass/fail output
- Split into 4 jobs:
package.jsonscripts:test:e2e:headed,test:e2e:ui,package,verify-selectorsREADME.md: complete documentation- Installation (from source + from zip)
- Usage guide (tagging, editing, sharing, popup)
- Display mode table
- Export format explanations
- Development commands reference
- Architecture overview + contribution guide
- Privacy statement
Added (Phase 5: E2E Tests, CI/CD & Packaging)
- E2E test fixtures rewritten with
mockFeedPagefixture- Uses
page.route()to serve mock HTML athttps://x.com/mock-feed - Content script runs normally (URL matches manifest's
x.com/*) - Replaces brittle
file://approach that couldn't trigger the content script
- Uses
tag-creation.test.ts: 6 scenarios: hover shows icon, click opens popover,
username shown in title, Escape closes, 16 swatches, complete create+verify-in-popup flowpopup.test.ts: 10 scenarios: load, empty state, search filter, all nav views,
settings save, settings persist after reopen, export, import error, theme toggleimport-export.test.ts: 7 scenarios: export generates output, compact format,
copy confirms, import preview, import apply, imported user in list, roundtripverify-selectors.jscompleted: opens x.com/explore, tests all selectors,
writes JSON report + failure screenshots, auto-updateslastVerifiedon successpackage-extension.tsfixed: sync JSON read, proper checksum computationvitest.config.tsupdated: coverage now includessrc/platforms/**+src/ui/**package.json: addedtsxdevDependency (required for package script)README.md: comprehensive: install, usage, architecture, privacy, contributing, roadmap
Fixed (Phase 6: Final Polish & Bug Fixes)
tag:deletedEventBus emission in content script now includes requiredsoft: booleanfieldpublic/popup.htmlstub removed: was silently overwriting the real built popup fromsrc/ui/popup/index.htmlpublic/onboarding.htmlmoved tosrc/ui/onboarding/index.htmlalongside its script; Vite entry updated accordingly- E2E mock feed updated with real test accounts: @elonmusk, @DonaldTrump, @RupertLowe10 (plus @alice_dev for deterministic assertions)
- E2E import test fixture uses valid UUID v4 for tag
id(TagSchema enforces this) - E2E import test
VALID_EXPORTuses correctExportManifestshape (entriesRecord, notusersarray) - E2E popup test backslash typo removed
- Vitest config updated to include
src/platforms/**andsrc/ui/**in coverage
Downloads
-
Source code (ZIP)
2 downloads
-
Source code (TAR.GZ)
2 downloads