Skip to content

feat(inlay): named profiles + auto-sync#8

Merged
kfrrst merged 2 commits into
mainfrom
feat/inlay-profiles-autosync
May 21, 2026
Merged

feat(inlay): named profiles + auto-sync#8
kfrrst merged 2 commits into
mainfrom
feat/inlay-profiles-autosync

Conversation

@kfrrst
Copy link
Copy Markdown
Owner

@kfrrst kfrrst commented May 21, 2026

Summary

  • Named profiles: InlayProfileStore (version "1") holds a list of InlayProfile objects, each with id, name, rules[], syncUrl, lastSyncedAt, createdAt, updatedAt. Replaces the flat tessellate:inlay:rules clientStorage key.
  • Migration: loadProfileStore() detects a legacy INLAY_RULES_KEY value on first run and migrates it into a "Default" profile automatically. Old key is cleared after migration.
  • Auto-sync: syncProfileIfStale() checks if lastSyncedAt is older than 4 hours; if so, fetches syncUrl (must return an InlayManifest), updates rules and lastSyncedAt, saves. Transparent to the caller — loadActiveManifest() calls it before returning the manifest.
  • CRUD handlers: inlay:new-profile, inlay:duplicate-profile, inlay:delete-profile (guard: must have >1 profile), inlay:rename-profile, inlay:set-active-profile — each responds with inlay:profiles-hydrate.
  • Sync-now: inlay:sync-now { profileId } force-syncs regardless of TTL; responds inlay:sync-result { ok, count, syncedAt, error }.
  • inlay-switch-profile command: narrow 400×320 panel with a <select size="6"> list of all profiles and an Activate button. Sets active profile and closes.
  • UI: profile-bar (dropdown + New / Duplicate / Delete), profile-name-bar (rename), sync-bar (URL input + ↺ Sync button + last-sync timestamp). #inlay-switch section for the standalone switcher. Save button sends inlay:save-profile { profileId, rules, syncUrl }.
  • UUID generation: generateId() uses Math.random() hex — no crypto.randomUUID() for sandbox compat.

Sandbox safety

scripts/check-sandbox-safety.mjs passes.

Test plan

  • First open after upgrade: "Default" profile auto-migrates from existing rules
  • Create new profile → rules list clears for the empty profile
  • Duplicate active profile → new profile has same rules, becomes active
  • Delete profile → only works when >1 profile exists; active switches to first remaining
  • Rename profile → name updates in dropdown
  • Switch profile via "Switch profile…" menu item → list shown, Activate closes plugin
  • Configure sync URL + press ↺ Sync → rules update, timestamp shown
  • Set sync URL, close, reopen after 4h TTL (or mock time) → auto-sync fires
  • Save with profile selected → rules saved to that profile's entry only
  • Sandbox safety passes

@gemini-code-assist
Copy link
Copy Markdown

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@kfrrst kfrrst force-pushed the feat/inlay-preflight-dryrun branch from 552b871 to 2ebf6ad Compare May 21, 2026 05:20
kfrrst added a commit that referenced this pull request May 21, 2026
- Add InlayProfile and InlayProfileStore types with version "1"
- loadProfileStore() migrates legacy tessellate:inlay:rules key into a
  "Default" profile on first run; subsequent loads read INLAY_PROFILES_KEY
- saveProfileStore(), getActiveProfile(), syncProfileIfStale() with 4h TTL
- loadActiveManifest() replaces loadInlayManifest() throughout; syncs stale
  profiles transparently before returning the active profile's rules
- inlay-configure opens with inlay:profiles-hydrate (store + activeProfile)
  instead of inlay:hydrate; font list sent in same open burst
- Message handlers: inlay:save-profile (update rules+syncUrl on named profile),
  inlay:new-profile, inlay:duplicate-profile, inlay:delete-profile,
  inlay:rename-profile, inlay:set-active-profile, inlay:sync-now
- inlay-switch-profile command: 400×320 panel with list-select + Activate button
- generateId() uses Math.random hex — no crypto.randomUUID() (sandbox compat)
- manifest.json: "Switch profile…" added between separator and Configure
- UI: profile-bar (select + New/Duplicate/Delete), profile-name-bar (rename),
  sync-bar (URL + ↺ Sync + last-sync timestamp); #inlay-switch panel for the
  switch-profile command; inlay:profiles-hydrate and inlay:sync-result handlers
@kfrrst kfrrst force-pushed the feat/inlay-profiles-autosync branch from 5adad46 to 5cf044f Compare May 21, 2026 05:20
kfrrst added a commit that referenced this pull request May 21, 2026
## What

- Size-aware `minSize?/maxSize?` on `InlayRule` — Inter Bold can now map to different targets based on font size (Bloomber S6 requirement).
- `buildForwardResolver` with sorted-bucket, tightest-range-wins priority replaces the old `buildInlayLookup`/`lookupReplacement`.
- `getStyledTextSegments(["fontName", "fontSize"])` so the resolver can act on segment size.
- `InlayRuleResult` + `byRule[]` on `InlayResult` → per-rule swap counts in the Apply notification when 2+ rules are active.
- `formatInlayResult` appends a rule breakdown for multi-rule applies.
- UI: 7-column grid with Min pt / Max pt inputs per rule row.
- **Fix**: `resolveFont` null case now falls back to raw target font name instead of recording an error and skipping. `loadFontAsync` handles the true failure case.
- **Fix**: test mock `availableFonts` expanded to cover all Klim / Newsreader fonts referenced as substitution targets in tests.

## Closes

Part of the S6 font-substitution Inlay v2 series (PRs #6#8).
@kfrrst kfrrst force-pushed the feat/inlay-preflight-dryrun branch from 2ebf6ad to cf22c62 Compare May 21, 2026 05:22
kfrrst added a commit that referenced this pull request May 21, 2026
- Add InlayProfile and InlayProfileStore types with version "1"
- loadProfileStore() migrates legacy tessellate:inlay:rules key into a
  "Default" profile on first run; subsequent loads read INLAY_PROFILES_KEY
- saveProfileStore(), getActiveProfile(), syncProfileIfStale() with 4h TTL
- loadActiveManifest() replaces loadInlayManifest() throughout; syncs stale
  profiles transparently before returning the active profile's rules
- inlay-configure opens with inlay:profiles-hydrate (store + activeProfile)
  instead of inlay:hydrate; font list sent in same open burst
- Message handlers: inlay:save-profile (update rules+syncUrl on named profile),
  inlay:new-profile, inlay:duplicate-profile, inlay:delete-profile,
  inlay:rename-profile, inlay:set-active-profile, inlay:sync-now
- inlay-switch-profile command: 400×320 panel with list-select + Activate button
- generateId() uses Math.random hex — no crypto.randomUUID() (sandbox compat)
- manifest.json: "Switch profile…" added between separator and Configure
- UI: profile-bar (select + New/Duplicate/Delete), profile-name-bar (rename),
  sync-bar (URL + ↺ Sync + last-sync timestamp); #inlay-switch panel for the
  switch-profile command; inlay:profiles-hydrate and inlay:sync-result handlers
@kfrrst kfrrst force-pushed the feat/inlay-profiles-autosync branch from 5cf044f to 8133999 Compare May 21, 2026 05:22
kfrrst added a commit that referenced this pull request May 21, 2026
- Test 9: use tessellate:inlay:profiles with syncUrl+empty rules to trigger
  remote fetch, matching loadActiveManifest's new profile-first code path
- Test 10: look for inlay:profiles-hydrate (store+activeProfile) instead of
  the removed inlay:hydrate (manifest+config); verify migrated rules land in
  activeProfile.rules
- Test 11: check tessellate:inlay:profiles for saved rules instead of the
  legacy tessellate:inlay:rules key that inlay:save no longer writes
kfrrst added a commit that referenced this pull request May 21, 2026
## What

- `preflightFonts()` → `PreflightResult`: checks which rule targets are actually installed before applying.
- `runInlayDryRun()` → `InlayDryRunResult`: scans the current page and reports what would swap, without writing.
- `inlay-dryrun-page` command + "Dry run (preview) current page…" menu item.
- Auto-preflight fires on configure open so the UI can surface a warning banner for any missing target fonts.
- UI: `#inlay-dryrun` section with "Preview current page" button and inline `<details>` result; `#preflight-status` warning banner.

Part of the Inlay v2 series (PRs #6#8).
@kfrrst kfrrst changed the base branch from feat/inlay-preflight-dryrun to main May 21, 2026 05:25
kfrrst added 2 commits May 21, 2026 00:25
- Add InlayProfile and InlayProfileStore types with version "1"
- loadProfileStore() migrates legacy tessellate:inlay:rules key into a
  "Default" profile on first run; subsequent loads read INLAY_PROFILES_KEY
- saveProfileStore(), getActiveProfile(), syncProfileIfStale() with 4h TTL
- loadActiveManifest() replaces loadInlayManifest() throughout; syncs stale
  profiles transparently before returning the active profile's rules
- inlay-configure opens with inlay:profiles-hydrate (store + activeProfile)
  instead of inlay:hydrate; font list sent in same open burst
- Message handlers: inlay:save-profile (update rules+syncUrl on named profile),
  inlay:new-profile, inlay:duplicate-profile, inlay:delete-profile,
  inlay:rename-profile, inlay:set-active-profile, inlay:sync-now
- inlay-switch-profile command: 400×320 panel with list-select + Activate button
- generateId() uses Math.random hex — no crypto.randomUUID() (sandbox compat)
- manifest.json: "Switch profile…" added between separator and Configure
- UI: profile-bar (select + New/Duplicate/Delete), profile-name-bar (rename),
  sync-bar (URL + ↺ Sync + last-sync timestamp); #inlay-switch panel for the
  switch-profile command; inlay:profiles-hydrate and inlay:sync-result handlers
- Test 9: use tessellate:inlay:profiles with syncUrl+empty rules to trigger
  remote fetch, matching loadActiveManifest's new profile-first code path
- Test 10: look for inlay:profiles-hydrate (store+activeProfile) instead of
  the removed inlay:hydrate (manifest+config); verify migrated rules land in
  activeProfile.rules
- Test 11: check tessellate:inlay:profiles for saved rules instead of the
  legacy tessellate:inlay:rules key that inlay:save no longer writes
@kfrrst kfrrst force-pushed the feat/inlay-profiles-autosync branch from f8f7757 to 5dc72da Compare May 21, 2026 05:25
@kfrrst kfrrst merged commit 24e594d into main May 21, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant