Thanks for your interest in improving FluentTyper. This document is for developers and contributors.
- Read the user docs in README.md to understand product behavior.
- Check open issues before starting work: github.com/bartekplus/FluentTyper/issues
- For bugs, use the bug report template: issues/new/choose
- Bun
1.3.10(pinned inpackageManager)
- Fork the repository and clone your fork.
- Install dependencies (
bun.lockis the canonical lockfile):bun install
- Build the extension:
bun run build
Build once, or use watch mode for iterative development:
bun run build # production build (Chrome)
bun run build --platform=firefox
bun run watch # dev mode, rebuilds on changeLoad the unpacked extension from the build/ directory:
- Chrome/Edge: open extensions page, enable developer mode, choose "Load unpacked", select
build/ - Firefox: open
about:debugging, choose "This Firefox", click "Load Temporary Add-on", selectbuild/manifest.json
FluentTyper uses a strict layered clean architecture. Imports flow downward only:
src/core/domain/ # Pure business logic, contracts, types
↓
src/core/application/ # Use-case orchestration, repositories, logging
↓
src/adapters/chrome/ # Browser integration (background + content-script)
↓
src/ui/ # Popup, settings, onboarding UI
| Layer | Path | Cannot import from |
|---|---|---|
| Domain | src/core/domain/ |
application, adapters, UI |
| Application | src/core/application/ |
adapters, UI |
| Adapters | src/adapters/chrome/ |
UI |
| UI | src/ui/ |
adapter internals |
Additionally, background and content-script are isolated from each other:
src/adapters/chrome/background/must not import fromcontent-script/src/adapters/chrome/content-script/must not import frombackground/
These boundaries are enforced by ESLint no-restricted-imports rules.
All entry points live in src/entries/:
| Entry | Context | Timing |
|---|---|---|
background.ts |
Service worker | Extension load |
content_script.ts |
Isolated world | document_end |
content_script_main_world.ts |
Main world | document_end |
content_script_main_world_start.ts |
Main world | document_start |
popup.ts |
Popup | User click |
settings.ts |
Options page | User navigation |
onboarding.ts |
Onboarding page | First install |
- Prefer path aliases:
@core/*,@adapters/*,@ui/*,@third-party/* - Do not add imports from legacy roots (
src/background/*,src/content-script/*,src/shared/*) - Cross-layer contracts go in
src/core/domain/contracts/ - Runtime message schemas go in
src/core/domain/messageTypes.d.ts
See docs/agents/architecture.md for full details.
bun run check # lint + format + typecheck (all must pass)
bun run test # unit tests
bun run test:e2e # e2e smoke (Chrome, <=10s target)
bun run check:e2e:coverage # coverage matrix validationRun these when your changes affect the corresponding area:
# Runtime or e2e behavior changed:
bun run test:e2e:full
bun run test:e2e:full --platform=firefox
# Development-mode hooks or toggles changed:
bun run test:e2e:dev
bun run test:e2e:dev --platform=firefox
# Cross-browser smoke (recommended):
bun run test:e2e --platform=firefoxbun run fix # lint:fix + formatEvery bug fix must include a regression test that would have caught the bug. The test must fail on the unfixed code and pass on the fixed code.
Update these when your changes affect the corresponding area:
| Change area | Test file(s) |
|---|---|
| Background routing | tests/background.routing.test.ts |
| Content-script runtime | tests/content_script.behavior.test.ts, tests/content_script.watchdog.test.ts |
- Coverage parity is behavior-based, not test-count-based.
- When behavior coverage moves between e2e/unit/integration tests, update:
tests/e2e/coverage-matrix.jsontests/e2e/coverage-baseline-ids.json
- Keep selector-heavy permutations in unit/integration tests; reserve e2e for truly editor-specific behavior.
- Target:
<=10swall-time forbun run test:e2eon both Chrome and Firefox. - CI reports regressions but does not fail solely for exceeding the target.
Content script observes typing, requests predictions via runtime messaging, background runs prediction and responds, content script renders suggestions.
When changing message shapes, update all of:
src/core/domain/messageTypes.d.tssrc/core/domain/constants.ts- Background routers under
src/adapters/chrome/background/router/ - Content-script handlers under
src/adapters/chrome/content-script/
- Production builds are Presage-only. Do not make WebLLM required for normal operation.
- Preserve safe fallbacks when the AI predictor is unavailable or times out.
- Do not expand the network surface area in production builds.
- Add key/constant in
src/core/domain/constants.ts - Wire through repositories in
src/core/application/repositories/ - Include in runtime config via
src/adapters/chrome/background/config/ConfigAssembler.ts - Update popup or settings UI, defaults, and any needed migrations
- Add to
resolveDynamicVariable()insrc/core/domain/variables.tsif computable locally - Extend
TemplateExpander.createResolver()if it needs browser context (tab, URL, etc.) - Add or update tests
- Production: warn and error only
- Never log user text content
- Guard debug logging behind development mode or logging level controls
When changing presage.xml or the language template, repack:
python3 scripts/rebuild_all.py --repackIf the compiled .so is missing (scripts/.deps/presage/), run a full rebuild first:
python3 scripts/rebuild_libpresage.py --deps --presage
python3 scripts/rebuild_all.py --repackCommit the modified files: public/third_party/libpresage/*.data and src/third_party/libpresage/libpresage.js.
package.jsonis the source of truth for the extension version.- Use
bun run bumpfor version bumps (syncs browser manifests automatically). - Do not hand-edit manifest versions in
platform/*/manifest.json. - Browser manifests:
platform/chrome/manifest.json,platform/firefox/manifest.json,platform/edge/manifest.json
- Create a branch from
master. - Keep commits focused and descriptive.
- Open a pull request against
master. - Ensure CI is green (lint, unit tests, e2e).
- In the PR description:
- Summarize user-visible impact
- List the test suites you ran
- Include screenshots for UI changes
Report bugs via GitHub issue forms:
Include: reproduction steps, expected vs. actual behavior, browser/OS, FluentTyper version, screenshots or recordings.
Propose improvements via GitHub issue forms:
Include: the user problem, a concrete proposal, alternatives considered, browser context.
Do not disclose vulnerabilities in public issues. Follow SECURITY.md for private reporting.
README.md: end-user focusedCONTRIBUTING.md: developer and contribution workflowdocs/agents/: detailed guides for architecture, testing, commands, and runtime features
When behavior changes, update docs in the same pull request.
FluentTyper is built with:
By contributing, you agree that your contributions are licensed under the MIT License.
If you want to support maintenance and development: