Files

167 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Advanced Context Menu Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Add optional contextual prompt presets and opt-in translation while preserving the existing simple action.
**Architecture:** Pure modules define settings, actions, and launch preparation. Firefox adapters register menus and open tabs/windows; a packaged dialog handles language entry, truncation consent, and errors. Direct and dialog launches share preparation and opening code.
**Tech Stack:** Firefox Desktop MV2 WebExtensions, ES modules, vanilla HTML/CSS, node:test, web-ext; Node.js 22+.
**Spec:** `docs/superpowers/specs/2026-10-05-advanced-context-menu-design.md`
## Global Constraints
- Firefox Desktop remains the only target.
- Preserve the current `storage`, `menus`, and `activeTab` permissions.
- No new host permissions, telemetry, intermediary service, runtime dependency, or API key is part of this design.
- Retain the current 8,000-character serialized URL budget.
- Users still review and submit in ChatGPT; no auto-submit or internal ChatGPT request modification.
- Options remain inline in `about:addons` and use existing visual styles.
- Preserve existing uncommitted changes; they are the working baseline, not disposable noise. Review the working tree before creating an isolated execution checkout; do not branch from HEAD alone and lose that baseline.
- Stage task-specific changes only. Many target files already contain prior work: inspect each staged diff and avoid committing unrelated hunks.
## Review Focus
- A selection inside a link must expose selection actions only (Task 2/5).
- A repeated `{query}` template must remain intact while budgeting repeated source text (Task 3).
- A language containing `&` or non-BMP characters must display literally and obey the 100-code-point limit (Task 1/5/6).
- Background suspension or two simultaneous translation dialogs must not mix or lose an acknowledged request (Task 4).
- Cancel, duplicate Enter, and a failed launch must never send twice or report success prematurely (Task 4).
## File map and contracts
- Modify `lib/defaults.js`, `lib/settings.js`: new defaults and legacy migration.
- Create `lib/actions.js`: context selection, action IDs, prompt rendering, language labels.
- Modify `lib/build-url.js`: retain URL encoding, expose untruncated prompt serialization.
- Create `lib/prepare-action.js`: pure validation and length decisions.
- Create `background/open-chat.js`: tab/window opening with fallback.
- Create `background/dialogs.js`: initial request handoff to packaged dialog.
- Create `background/menus.js`: menu registration and visibility updates.
- Modify `background/background.js`: compose modules and route entry points.
- Create `dialog/action.html`, `dialog/action.css`, `dialog/action.js`, `dialog/controller.js`: accessible dialog shell and controller.
- Modify `options/options.html`, `options/options.js`; create `options/menu-settings.js`: new controls, dependencies, validation.
- Tests mirror these responsibilities under `test/`; docs changes remain in README/CHANGELOG.
Shared data types (plain JavaScript objects, documented with JSDoc):
`ActionRequest = { actionId, source, settings, language? }`, where settings is a
sanitized snapshot, source is a string, and actionId is one of `ask`, `explain`,
`summarize`, `debug`, `translate`, `ask-link`, `review-link`, `ask-page`,
`summarize-page`. Only `ask` permits an empty source (blank-chat shortcut).
`Preparation = {kind:'ready',url} | {kind:'language'} |
{kind:'shortened',url} | {kind:'error',message}`.
## Task 1: Settings schema and migration
**Files:** Modify `lib/defaults.js`, `lib/settings.js`, `test/settings.test.js`.
**Interfaces:** Preserve `mergeSettings(stored)`, `load()`, `save(partial)`.
New keys: contextMenuMode (`off|simple|advanced`, default simple), showTranslate
(false), translationMode (`ask|preferred`, default ask), translationLanguage
(empty). Export `normalizeLanguage(value) -> string` from settings: trim,
return empty on invalid type or more than 100 code points.
- [ ] Add failing assertions for `mergeSettings({showContextMenu:false}).contextMenuMode === 'off'`, absent legacy => simple, valid new mode wins over legacy, invalid new mode falls back through legacy, and unknown keys are removed.
- [ ] Add assertions that 100 emoji are accepted, 101 rejected, whitespace trims, and wrong language/mode/boolean types default. Update existing expected setting shapes without dropping their coverage.
- [ ] Run `node --test test/settings.test.js`; confirm new assertions fail for the intended missing behavior.
- [ ] Implement defaults and migration. `load()` requests both new keys and legacy `showContextMenu`; save writes normalized new keys, not a legacy boolean. Do not delete old data as an initialization side effect.
- [ ] Run `node --test test/settings.test.js`; require nonzero passing tests. Verify load against a fake storage containing only the legacy key.
- [ ] Review/stage only this task and commit `feat: migrate context menu and translation preferences`.
## Task 2: Context and dedicated prompt definitions
**Files:** Create `lib/actions.js`, `test/actions.test.js`.
**Interfaces:** `getContext(info) -> {kind:'selection'|'link'|'page',source}|null`;
`getActions(context,settings) -> [{id,title}]`; `renderPrompt(request,sourceOverride=request.source) -> string`.
`getContext` accepts menu event data, prioritizes nonblank selection then link,
then page URL, and excludes password inputs and media-only contexts. Empty
editable fields get no page group. Link/page URL validation belongs to Task 3.
- [ ] Add tests asserting linked selection chooses selection, empty selection chooses link, media-only and password contexts return null, and page background uses pageUrl. Selection order must be ask/explain/summarize/debug, with translate appended only when enabled.
- [ ] Add prompt assertions: default action applies the saved template; Explain/Debug ignore it; Translate includes the language; link-safety prompt asks for evidence/uncertainty and never claims completed checks. Preserve literal `$&`, `$$`, backticks, Unicode and braces in source text. Escape `&` in menu language labels.
- [ ] Run `node --test test/actions.test.js`; confirm intended failures.
- [ ] Implement fixed action definitions and dedicated prompts matching the spec table. Extract/reuse existing literal template replacement instead of implementing conflicting replacement semantics. Blank `ask` renders empty even with a nonempty template.
- [ ] Run `node --test test/actions.test.js test/build-url.test.js`; expect all passing.
- [ ] Commit reviewed task hunks as `feat: define contextual prompt actions`.
## Task 3: Safe action preparation and length decisions
**Files:** Modify `lib/build-url.js`; create `lib/prepare-action.js`, `test/prepare-action.test.js`; extend `test/build-url.test.js`.
**Interfaces:** `serializePrompt(settings,prompt) -> string` encodes an already
rendered prompt without truncating. Keep legacy `buildChatGptUrl` compatibility
for existing tests until routing is replaced. `prepareAction(request) -> Preparation`
uses Task 2 rendering, Task 1 normalization, and `MAX_URL_CHARS`.
- [ ] Add assertions for ready URLs <=8000, translation needing a language, invalid/credential-bearing/non-HTTP URLs producing error, and oversized URLs producing error rather than shortened. Accepted URL source must survive unchanged within decoded prompt.
- [ ] Add budget tests for repeated `{query}`, emoji, whitespace boundaries, hint overhead, oversized instructions, and the shortening marker. Every shortened result retains all fixed instructions and substitutes only a shortened source; a small source remains byte-identical after decoding.
- [ ] Run `node --test test/prepare-action.test.js`; confirm failures.
- [ ] Implement validation and binary search over source code points, measuring the fully rendered/encoded prompt each time. Reserve the marker `[Selected text shortened]` only when shortening. Prefer a late whitespace boundary as in existing builder. Reject unknown action IDs and missing required sources.
- [ ] Run `node --test test/prepare-action.test.js test/build-url.test.js test/actions.test.js`; require passing assertions and nonzero discovery.
- [ ] Commit reviewed changes as `feat: prepare actions with explicit truncation decisions`.
## Task 4: Launch helper and reliable extension dialogs
**Files:** Create `background/open-chat.js`, `background/dialogs.js`, `dialog/action.html`, `dialog/action.css`, `dialog/action.js`, `dialog/controller.js`, `test/open-chat.test.js`, `test/dialog.test.js`.
**Interfaces:** `openChat(browser,url,openIn) -> Promise<void>` rejects after
final failure. `showActionDialog(browser,payload) -> Promise<void>` transfers
`payload={request,preparation}` to the dialog. `initializeDialog({browser,document,window})`
owns UI and launch after handoff, using Tasks 1–3 and openChat.
- [ ] Write boundary tests: new-window fallback opens a tab, background-tab uses active:false, final failure rejects; language whitespace blocks submit, Cancel/Escape never launch, double submit launches once, rejected launch permits retry, shortening requires explicit consent.
- [ ] Write handoff tests with two opaque IDs and distinct sources: wrong sender/ID cannot claim data; after acknowledgment clearing background state does not affect dialog completion; closed/uninitialized dialogs release requests and show retry rather than processing missing data.
- [ ] Run `node --test test/open-chat.test.js test/dialog.test.js`; confirm failures.
- [ ] Implement tab/window helper and dialog shell. Use runtime extension-page messaging plus a random request ID, verify sender extension URL and associated tab ID, transfer a structured snapshot, acknowledge receipt, then release background payload. Keep launch handling active through handshake; register listeners before opening. Apply a finite 15-second initialization timeout, with clear retry copy; never time out user language entry after handoff. No selected content in URLs, logs, or storage.
- [ ] Implement dialog controller: validate language, rerun preparation, show shortening confirmation if needed, disable launch controls while pending, show errors/retry, close only after success. Use textContent for all source-derived UI. Window creation falls back to an extension tab; closing that surface cancels. Use the same shell for direct-launch error messages.
- [ ] Run boundary tests and manually probe actual Firefox event-page handoff/idle behavior before accepting this task. If lifecycle assumptions fail, correct the handoff design within the approved no-persistence/no-permission constraints before wiring menus.
- [ ] Commit task-specific files as `feat: add action dialogs and shared launch handling`.
## Task 5: Register adaptive menus and route the shortcut
**Files:** Create `background/menus.js`, `test/menus.test.js`; modify `background/background.js`.
**Interfaces:** `initializeMenus({browser,load,onAction})` registers menus,
storage listener, onShown/onClicked, and serializes rebuilding. onAction receives
an ActionRequest. `runAction(request)` in background uses prepareAction then
openChat for ready or showActionDialog otherwise; errors use dialog feedback.
- [ ] Add tests: Off has no root, Simple has one selection-only direct action, Advanced has one root with correct children; onShown reveals exactly one context group and refreshes; selection over link wins; live translation/mode changes rebuild without races.
- [ ] Add assertions that unexpected/stale item IDs or mismatched click contexts do not launch, preferred-language labels escape ampersands, and click creates one settings/source snapshot. Verify shortcut invokes `ask` independently of menu mode, including empty selection.
- [ ] Run `node --test test/menus.test.js`; confirm failures.
- [ ] Implement adapter using existing serialized removeAll/create pattern, namespaced stable IDs and visibility updates. Verify current Firefox menu APIs from Mozilla primary docs at execution time. Preserve the shortcut's focused-frame selection contract and existing fallback behavior; do not expand page-reading scope.
- [ ] Route both entry points through shared preparation, including truncation consent for Simple and shortcut. Remove obsolete menu listeners to prevent duplicates.
- [ ] Run all unit tests. Manually check AGENTS.md frame cases; if the existing ancestor-focus bug is reproduced, make the focused-frame guard correction with a regression check before claiming shortcut preservation.
- [ ] Commit reviewed changes as `feat: wire simple and advanced context menus`.
## Task 6: Options UI, release cleanup, and complete verification
**Files:** Modify `options/options.html`, `options/options.js`, `options/options.css` if needed; create `options/menu-settings.js`, `test/menu-settings.test.js`; modify `README.md`, `CHANGELOG.md`.
**Interfaces:** `initializeMenuSettings({document,onChange}) -> {render(settings),collect()}`
owns context/translation controls. Existing options renderer delegates these
keys rather than assuming every preference maps to a checkbox/text input.
- [ ] Write UI boundary tests for Off/Simple/Advanced dependent visibility, opt-in translation, preferred-language validation, retaining hidden preferences, migration display, and Restore defaults. Assert language length uses code points rather than HTML UTF-16 maxlength alone.
- [ ] Run `node --test test/menu-settings.test.js`; confirm failures.
- [ ] Add controls with approved exact labels, reuse existing styles and debounce/save behavior. Invalid preferred language displays inline guidance; launch behavior remains dialog fallback. Preserve active input against storage echoes. Update restore/shortcut copy that assumes five settings or a checkbox.
- [ ] Remove temporary URL-test HTML and handler. Update README/CHANGELOG and privacy section to describe URL sharing, menu modes, opt-in translation, length consent, and unchanged address-bar behavior.
- [ ] Run `npm test`; require lint with zero warnings, nonzero passing unit tests, and a successful ZIP build. Inspect ZIP entries for new dialog assets and absence of any `docs/` or `test/` entries; do not alter packaging exclusions unless inspection finds a defect.
- [ ] Run `npm run dev` and execute every manual check from the approved spec and AGENTS.md, including light/dark/narrow settings/dialogs, keyboard focus/cancellation, concurrent requests, idle/wakeup, long selection, URL rejection, menu precedence, restart persistence, and all shortcut frame cases. Record actual observed results; ask for user assistance if native interaction is unavailable rather than counting a headless launch as behavioral coverage.
- [ ] Run `git diff --check`, review permissions/search URL/package changes, and commit only reviewed task hunks as `feat: expose advanced menu preferences and usage guidance`.
## Completion and review
Execution must begin with a baseline snapshot/status review and the appropriate
isolation skill; unresolved existing work must not be overwritten. No release,
AMO upload, push, or merge is authorized by this plan. Finish with a review of
the complete change and a report of automated and actual Firefox checks,
including any checks still requiring user interaction.
Plan self-review: spec sections map to Tasks 1–6; all five Review Focus cases
are assigned tests. Shared signatures use the ActionRequest/Preparation types
above. Lifecycle handoff is intentionally verified before entry-point wiring.