Skip to content

Ship Chrome extension v2.4.0 hardening and local history - #2

Closed
anhtahaylove wants to merge 12 commits into
YujiSoftware:masterfrom
anhtahaylove:codex/chrome-hardening-modularization
Closed

Ship Chrome extension v2.4.0 hardening and local history#2
anhtahaylove wants to merge 12 commits into
YujiSoftware:masterfrom
anhtahaylove:codex/chrome-hardening-modularization

Conversation

@anhtahaylove

Copy link
Copy Markdown

Summary

  • Modernize the Chrome MV3 extension into modular background, content, popup, options, and shared components.
  • Add local clipboard history, search/filter/pin/bulk actions, smart JSON/SQL/JWT/timestamp/Base64 transforms, internal analytics, localization, and Alt+Shift+C shortcut support.
  • Keep the product extension-only: no Companion, Native Messaging permission, cloud sync, account, network service, or telemetry.
  • Harden v2.4.0 upgrade behavior with serialized dynamic registration, validated excluded domains, background-only history writes, UTF-8 quota trimming, current-target Alt-click resolution, and isolated E2E cleanup.

Tests

  • npm run verify:release — PASS
  • npm run validate — 56/56 unit tests; syntax, manifest, and Chrome build PASS
  • npm run test:e2e — 9/9 PASS; final regression run repeated twice consecutively
  • git diff --check — PASS
  • GitHub Actions Chrome CI run 30515495707 — PASS
  • Independent code-review and architecture passes — no remaining P0/P1/P2

Risks

  • Manual in-place upgrade smoke on the existing Chrome profile is still pending because the ChatGPT Chrome control backend was unavailable in the final Codex run.
  • The draft release must remain unpublished until that one profile-preservation smoke confirms settings, excluded domains, local history, popup, and copy behavior after clicking Reload without removing the extension.
  • One low-severity development-only esbuild audit advisory remains; no high/critical production dependency finding was detected.

Release notes

VET and others added 12 commits April 18, 2026 15:26
Reattach the latest recovered extension implementation to the main
history so the upgraded MV3 runtime, popup, settings, fixtures, and
tests become the canonical repository state again. Keep IDE metadata
untracked and drop tool-state directories from the restored tree.

Constraint: Recovery source is unreachable stash commit ac325a0, not a live branch
Constraint: Master must stay recoverable from pre-restore commit 9e434c9
Rejected: Apply stash in the active worktree | untracked .omx paths would collide
Rejected: Restore tool-state directories | not part of the product deliverable
Confidence: high
Scope-risk: broad
Reversibility: clean
Directive: Treat this commit as the recovered Antigravity baseline before further cleanup
Tested: node --test; node --check menu.js options.js background.js popup.js shared.js
Tested: npx --yes web-ext lint --source-dir .; HTTP 200 for three fixture pages
Not-tested: Full interactive browser run with the unpacked extension loaded
…ization

Capture the current working tree as a stable baseline before the
source moves into src modules and the release pipeline expands.
This preserves the current Chrome-only runtime hardening and build
contract so later refactors can be compared against a known-good state.

Constraint: The working tree is the source of truth and must be preserved before deep refactors
Rejected: Refactor in-place without a baseline commit | would make regression isolation harder
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Use this commit as the comparison point for any behavior regressions during modularization
Tested: npm run validate; npm run pack:chrome; fixture HTTP 200 checks
Not-tested: Full manual Chrome extension walkthrough against the checklist
Co-Authored-By: Claude <noreply@anthropic.com>
Move the authored extension logic into src modules, build the shipped
artifact exclusively from that source tree, and add browser automation
so Chrome release verification exercises the unpacked extension instead
of only unit-level helpers. This removes the split-brain risk of editing
runtime files at the repo root while shipping something different.

Constraint: Dist artifacts stay generated and must not become a second source of truth
Constraint: Chrome-only support remains the product contract for this repository
Rejected: Keep root runtime JS as active source | future edits would drift away from the build pipeline
Rejected: Add only unit tests without browser automation | would miss extension lifecycle regressions
Confidence: medium
Scope-risk: broad
Reversibility: clean
Directive: Continue editing source under src and load unpacked from dist/chrome only
Tested: npm run verify:release
Not-tested: Manual exploratory walkthrough beyond the documented Chrome checklist
Co-Authored-By: Claude <noreply@anthropic.com>
…dules

Move the remaining hot-path logic out of the oversized content and
options entrypoints so src/content/bootstrap.js and src/options/index.js
become orchestration layers instead of carrying extraction, overlay,
history, and settings internals directly. This keeps behavior locked by
existing release verification while lowering the cost of future changes.

Constraint: Behavior must continue to match the frozen baseline commit c911c73
Rejected: Leave bootstrap and options index as monoliths | future changes would keep compounding maintenance cost
Confidence: medium
Scope-risk: moderate
Reversibility: clean
Directive: Continue new work in the extracted src/content and src/options modules rather than rebuilding logic in the entrypoints
Tested: npm run verify:release
Not-tested: Extra exploratory scenarios outside the automated smoke suite
Co-Authored-By: Claude <noreply@anthropic.com>
The content helper had become the last broad runtime bucket after the Chrome-only release work. This keeps the createContentHelpers facade stable for bootstrap and events while moving targeting, extraction, overlay, persistence, and clipboard concerns into focused modules.

The pass also locks the behavior that matters for this split with Playwright coverage around rich target extraction, table TSV export, selection priority, shortcut copy, excluded domains, and reload recovery.

Constraint: Chrome-only contract and dist/chrome remain the release surface

Constraint: No product scope expansion or root-authored runtime JS

Rejected: Rewrite bootstrap/events around new imports | higher blast radius with no user-facing value

Rejected: Keep helpers.js as a large bucket | future edits would keep mixing unrelated runtime responsibilities

Confidence: high

Scope-risk: moderate

Directive: Keep createContentHelpers as the content facade unless bootstrap/events are refactored in the same verified pass

Tested: npm run verify:release

Not-tested: Human manual acceptance in visible Chrome
Some pages handle form-button clicks before document bubble listeners can observe them. Google Search is one visible case: the primary search button can run page click behavior while the extension is still waiting for the bubble-phase content listener.

The content click listener now runs in capture phase and claims only recognized copy gestures, so normal clicks and excluded/editable-skip cases retain existing behavior while Alt-click copy bypasses page handlers and form submission.

Constraint: Chrome-only content script behavior must keep dist/chrome as the release surface

Rejected: Add a google.com-specific selector workaround | brittle and misses the underlying propagation issue

Rejected: Stop propagation for all clicks | would break host-page behavior outside copy gestures

Confidence: high

Scope-risk: moderate

Directive: Keep propagation blocking scoped to confirmed copy gestures only

Tested: npm run verify:release

Not-tested: Manual visible Chrome test on live google.com
The extension can now treat the local Companion as the source of truth when the native host is connected, while keeping a local fallback and IndexedDB-backed outbox for offline work. The companion adds the Wails desktop MVP, local JSON history, schema v3 conflict timestamps and tombstones, smart previews, tray/hotkey/autostart support, Windows native-host install scripts, release docs, and native/e2e smoke coverage.

The final review pass also tightened the native messaging transport contract: host responses are capped under Chrome's 1 MiB host-to-extension limit, extension request text is truncated by UTF-8 bytes, and offline pinning now fails open without trimming fallback history if sync settings cannot be read.

Constraint: No cloud sync, account, telemetry, or network transport; clipboard data stays local.

Constraint: Native host name remains com.copy_text_without_selecting.companion and protocol version remains 1.

Constraint: Chrome native messaging host-to-extension responses must stay under 1 MiB.

Rejected: chrome.storage.local as the primary outbox | quota and data-loss risk for large pending payloads.

Rejected: embedding companion/build/appicon.png | fresh workspaces do not contain generated build output.

Confidence: high

Scope-risk: broad

Reversibility: messy

Directive: Do not remove pending outbox operations until Companion ACKs the corresponding native message.

Directive: Keep source assets under companion/assets and companion/frontend/dist; companion/build stays generated output.

Directive: Keep native messaging request and response byte budgets separate; extension text truncation must remain UTF-8 byte-based, not JavaScript string-length-based.

Tested: npm run validate; npm run test:e2e; npm run native:build; npm run native:test-host; npm run native:test-tray; npm run test:e2e:native; npm run pack:chrome; targeted node --test tests/native-messaging.test.js tests/local-history.test.js; targeted go test ./internal/companion; fresh workspace npm ci + validate + native:build; Wails desktop launch screenshot smoke; git diff --check

Not-tested: macOS/Linux native hosts; Chrome Web Store submission; real end-user migration beyond synthetic schema/outbox fixtures.
Chrome already owns Alt+Shift+C as the extension command, so the shortcut flow now waits for a content-script copy acknowledgement and then opens the native Companion through the existing OPEN_APP bridge. The Companion still stores the preferred hotkey, but failed Windows global registration no longer rolls settings back; the UI reports that the saved hotkey is not active when Windows rejects the duplicate.

The same pass keeps the installed Companion aligned with release expectations by allowing 9999 history items, syncing the Wails app icon from the extension icon during native builds, and documenting the manual icon/hotkey smoke points.

Constraint: Windows RegisterHotKey rejects duplicate process-global shortcuts when Chrome owns Alt+Shift+C

Constraint: Wails v2.12 only regenerates build/windows/icon.ico when the stale file is missing

Rejected: Register Alt+Shift+C in both Chrome and Companion | duplicate global hotkeys fail and make settings look unstable

Rejected: Leave Wails build/appicon.png as an ignored manual artifact | fresh builds could silently return to the default Wails icon

Confidence: high

Scope-risk: moderate

Directive: Keep Alt+Shift+C owned by the extension command path; Companion hotkey activation remains best-effort when Windows reports a conflict

Tested: npm run validate

Tested: npm run test:e2e

Tested: npm run native:build

Tested: npm run native:test-host

Tested: npm run test:e2e:native

Tested: Playwright unpacked popup Open app smoke against extension pahmaphhgccgealefimmgjcfmobgofpp

Not-tested: npm run native:test-tray in this turn
The release branch now ships a Windows-native companion, so browser-only Linux CI left the highest-risk packaging and Native Messaging path unguarded. This adds a Windows runner lane that installs the matching Wails CLI, builds the companion, smokes the native host protocol, and runs the native extension E2E flow.

Constraint: GitHub Actions JavaScript action runtime is moving to Node 24, so the workflow uses current v6 setup actions instead of relying on the deprecated Node 20 action runtime.

Rejected: Run tray visual smoke in hosted CI | the notification area path needs an interactive Windows desktop and is still better covered by manual release smoke.

Confidence: high

Scope-risk: narrow

Directive: Keep tray visual validation manual unless the CI environment provides a real interactive desktop session.

Tested: npm run validate; npm run test:e2e; npm run native:build; npm run native:test-host; npm run test:e2e:native; git diff --check

Not-tested: GitHub-hosted Windows runner execution before push
The companion was release-ready functionally, but users still needed npm scripts to register the Native Messaging host. This adds a per-user Go setup executable that embeds the built companion, writes the Chrome/Chromium Native Messaging manifest and HKCU registry entries, creates a Windows uninstall entry, and supports silent install/uninstall for CI and release smoke.

Constraint: Installer must work without Node.js, npm, Go, Wails, admin rights, cloud services, or telemetry on the user's machine.

Rejected: Use Wails NSIS packaging first | Wails can emit NSIS installers, but Native Messaging registration still needs custom lifecycle hooks and an extra installer toolchain.

Rejected: Keep npm install-host as the release path | it requires a developer toolchain on user machines.

Confidence: high

Scope-risk: moderate

Directive: Keep user history/settings preserved by default; only delete APPDATA data when --purge-data is explicitly provided.

Tested: npm run validate; npm run test:e2e; npm run native:build; npm run native:test-host; npm run native:package-installer; npm run native:test-installer; npm run test:e2e:native; git diff --check

Not-tested: Double-click GUI installer experience and code signing
Remove the optional Wails companion, native messaging bridge, Windows installer, native CI lane, popup controls, documentation, and tests. Keep copy history local-only and delete obsolete sync outbox data during extension startup.

Constraint: Preserve Chrome copy, shortcut, local history, analytics, and smart-action behavior without deleting saved user history.

Rejected: Keep dormant companion code behind flags; it would retain permissions and maintenance burden.

Tested: npm run verify:release; npm run validate; npm run test:e2e; git diff --check; active source and built artifact reference scans.

Not-tested: Chrome Web Store in-place upgrade and manual migration on an existing browser profile.
Final release review found lifecycle, domain-validation, clipboard, local-history, and test-isolation edge cases that could break copy behavior or lose local history. This hardens dynamic MV3 registration, keeps the background service worker as the only history writer, bounds stored history by UTF-8 bytes, and always resolves Alt-click from the current event target.

Constraint: Preserve the extension-only architecture, existing settings/history, Alt+Shift+C behavior, and local-only data model without adding dependencies or permissions.

Rejected: Keep unregister-before-register content scripts | an invalid match pattern could leave the extension with no registered content script.

Rejected: Keep content-script history fallback writes | concurrent tabs could overwrite each other's local history.

Confidence: high

Scope-risk: narrow

Directive: Keep background initialization serialized and local history writes owned by the background service worker.

Tested: npm run verify:release; npm run test:e2e twice consecutively; targeted node tests; git diff --check; independent code-review and architecture passes with no remaining P0/P1/P2.

Not-tested: Manual in-place upgrade on the current Chrome profile because the ChatGPT Chrome control backend was unavailable despite local bridge diagnostics passing.
@anhtahaylove
anhtahaylove marked this pull request as ready for review July 30, 2026 07:57
@anhtahaylove

Copy link
Copy Markdown
Author

Closing this PR because development will continue independently in https://github.com/anhtahaylove/copy-text-without-selecting. Thank you for the original MIT-licensed project.

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