A high-throughput, profile-driven logging automation library for browser-based workflows that need deterministic, repeatable text injection and commit semantics.
Note
This README is structured as a library-first technical specification for teams integrating the core text-injection/logging pipeline into browser automation and lead-intelligence workflows.
- Features
- Tech Stack & Architecture
- Getting Started
- Testing
- Deployment
- Usage
- Configuration
- License
- Contacts & Community Support
- Profile-driven logging dictionaries with multi-language support.
- Category-based log batch insertion from contextual UI actions.
- Deterministic event dispatch based on
InputEvent+KeyboardEventto satisfy strict framework listeners. - Works with:
inputtextareacontenteditabletargets
- Asynchronous insertion pipeline with configurable delay (
INSERT_DELAY_MS) to reduce race conditions. - Storage-backed active profile persistence via
chrome.storage.sync. - Runtime menu regeneration on profile changes (
profileChangedmessage broadcast). - Manifest V3 service-worker architecture with explicit modular boundaries.
- CI-aligned repository layout with lint/build/deploy workflow scaffolding.
- Curated dictionaries for English, Spanish, French, German, Portuguese, Polish, Czech, Italian, Turkish, and Japanese.
Tip
The architecture is effective for “log-like” data entry streams where each token must be committed with an Enter boundary (e.g., tags, recruiter filters, CRM multi-value inputs).
- Language: Vanilla JavaScript (ES-compatible patterns).
- Runtime: Chrome Extension Platform, Manifest V3.
- Browser APIs:
chrome.contextMenuschrome.scriptingchrome.storagechrome.runtime
- UI Layer: Popup-driven profile selector (
popup.html+popup.js). - Distribution: GitHub Actions CI/CD workflow templates.
Expand complete repository tree (top-level + key modules)
Duck-Bob/
├── background.js
├── manifest.json
├── popup.html
├── popup.css
├── popup.js
├── background/
│ ├── config.js
│ ├── context-menu.js
│ ├── injector.js
│ └── profile-loader.js
├── popup/
│ ├── constants.js
│ ├── storage.js
│ └── ui.js
├── profiles/
│ ├── english.js
│ ├── spanish.js
│ ├── french.js
│ ├── german.js
│ ├── portuguese.js
│ ├── polish.js
│ ├── czech.js
│ ├── italian.js
│ ├── turkish.js
│ └── japanese.js
├── .github/workflows/
│ ├── lint.yml
│ └── chrome-extension.yml
├── LICENSE
├── SECURITY.md
└── CONTRIBUTING.md
- Module separation between orchestration (
background.js) and responsibility-specific handlers (background/*.js). - Runtime script execution through
chrome.scripting.executeScriptkeeps insertion logic close to active DOM context. - Defensive checks for active tab IDs, profile availability, and category payload validity reduce hard runtime failures.
- Message-driven menu refresh avoids stale context menu state after profile updates.
Expand logging pipeline and event-flow diagram
flowchart LR
A[User selects profile in popup] --> B[chrome.storage.sync set activeProfile]
B --> C[popup sends profileChanged message]
C --> D[background refreshContextMenus]
D --> E[User right-clicks editable target]
E --> F[Context menu category selected]
F --> G[Resolve menu action and profile words]
G --> H[Inject script into active tab]
H --> I[Apply word to input/textarea/contenteditable]
I --> J[Dispatch InputEvent]
J --> K[Dispatch Enter keydown/keyup]
K --> L[Delay and iterate next word]
Important
Event simulation is intentionally explicit to support modern SPAs where assigning .value alone does not trigger controlled-component updates.
- Google Chrome (latest stable recommended).
- OS: macOS, Linux, or Windows with Chrome installed.
- Git (for source-based installation).
- Optional for CI/automation:
- Node.js
20.x - npm
>=9
- Node.js
- Clone the repository:
git clone https://github.com/OstinUA/Duck-Bob.git
cd Duck-Bob- Open
chrome://extensions. - Enable Developer mode.
- Click Load unpacked.
- Select the repository root.
Note
No local build step is required for the default unpacked extension workflow.
Troubleshooting and alternative installation paths
- Context menu does not appear:
- Ensure you right-click inside an editable field.
- Confirm extension is enabled and has
contextMenuspermission.
- No words inserted:
- Verify active profile exists and category is not empty.
- Re-open popup and reselect profile to force menu refresh.
- Popup closes too quickly:
- This is expected (
PROFILE_CLOSE_DELAY_MS = 150).
- This is expected (
npm ci --if-present
npm run build --if-present- Update files locally.
- In
chrome://extensions, click the refresh icon on the unpacked extension card.
Run repository checks with the following commands:
# Syntax check all JS files
find . -type f -name "*.js" -print0 | xargs -0 -I{} node --check "{}"
# Optional lint if package scripts are present
npm run lint --if-present
# Optional build verification
npm run build --if-presentWarning
The CI lint workflow currently validates JavaScript syntax only under api/ if present; adapt it to include background/, popup/, and profiles/ for stricter coverage.
- Build distribution assets if your pipeline generates a
dist/directory. - Zip extension payload.
- Upload to Chrome Web Store (draft or publish).
npm ci --if-present
npm run build --if-present
cd dist && zip -r ../extension.zip .- Use
.github/workflows/chrome-extension.ymlas baseline for:- Build artifact creation
- Artifact handoff between jobs
- Automated Chrome Web Store draft uploads
- Required secrets for upload action:
EXTENSION_IDCLIENT_IDCLIENT_SECRETREFRESH_TOKEN
Deployment hardening checklist
- Pin action versions across all workflows.
- Add branch protections for release branches.
- Enforce lint + syntax gates on pull requests.
- Introduce signed release tags and changelog generation.
- Configure staged rollout percentage in Chrome Web Store.
// 1) User selects a profile from the popup UI
await chrome.storage.sync.set({ activeProfile: 'profile1' });
// 2) Background receives menu selection and resolves category words
const words = selectedProfile['Programmatic'];
// 3) Injection logic dispatches input + enter for every token
for (const word of words) {
target.dispatchEvent(
new InputEvent('input', {
data: word,
inputType: 'insertText',
bubbles: true,
cancelable: true
})
);
target.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', code: 'Enter', keyCode: 13, which: 13, bubbles: true }));
target.dispatchEvent(new KeyboardEvent('keyup', { key: 'Enter', code: 'Enter', keyCode: 13, which: 13, bubbles: true }));
}- Open popup.
- Select language.
- Select profile.
- Focus editable target on destination site.
- Right-click ->
Text Inserter =>-> category. - Verify tags/log entries are committed sequentially.
Advanced Usage: custom profile dictionaries
You can define additional domain-specific dictionaries in profiles/*.js using profile IDs and category arrays.
profiles.profile_custom = {
'Incident Severity': ['P0', 'P1', 'P2', 'P3'],
'Services': ['billing-api', 'identity', 'gateway', 'notification'],
'Regions': ['us-east-1', 'eu-west-1', 'ap-southeast-1']
};Use the popup constants map to expose this profile in UI selection.
Custom formatters and edge-case behavior
- Normalize whitespace before insert.
- Enforce max token length to avoid target UI truncation.
- Add deduplication for repeated words.
- If
document.activeElementchanges mid-loop, insertion may drift. - Some rich-text editors block synthetic
KeyboardEventand require additional triggers. - Cross-origin iframes may not be script-injectable depending on host permissions.
Primary configuration is code-defined and storage-backed.
EXTENSION_DEFAULTS.ACTIVE_PROFILE_ID: fallback profile when storage is empty.EXTENSION_DEFAULTS.INSERT_DELAY_MS: delay between token insertions.MESSAGE_TYPES.PROFILE_CHANGED: runtime signal for menu regeneration.chrome.storage.sync.activeProfile: persisted selected profile ID.
Caution
Very low insertion delays may cause missed key commits in latency-heavy web apps; validate with realistic target forms.
Exhaustive configuration reference
| Key | Location | Type | Default | Purpose |
|---|---|---|---|---|
MENU_IDS.ROOT |
background/config.js |
string |
mainMenu |
Root context menu node ID |
EXTENSION_DEFAULTS.ACTIVE_PROFILE_ID |
background/config.js |
string |
profile1 |
Safe fallback profile |
EXTENSION_DEFAULTS.INSERT_DELAY_MS |
background/config.js |
number |
40 |
Word insertion pacing |
MESSAGE_TYPES.PROFILE_CHANGED |
background/config.js |
string |
profileChanged |
Synchronization event name |
POPUP_DEFAULTS.PROFILE_CLOSE_DELAY_MS |
popup/constants.js |
number |
150 |
Delay before popup auto-close |
{
"activeProfile": "profile1"
}{
"type": "profileChanged"
}This project is licensed under the Apache License 2.0. See LICENSE for full terms.
If you find this tool useful, consider leaving a star on GitHub or supporting the author directly.