Files

15 KiB
Raw Permalink Blame History

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.