Thanks for your interest in contributing! Whether it’s reporting a bug, suggesting a feature, or submitting code, this guide covers everything you need to know.
Found a bug or have a feature request? Open an issue on GitHub. An issue is just a post describing what bug you ran into or what you’d like to see – no coding or technical knowledge required. You’ll need a free GitHub account to create one. Include as much detail as you can (what you were doing, what happened, what you expected) and I’ll take it from there.
Include as much of the following as you can:
F12 > Console tab)Describe the problem you’re trying to solve, not just the solution you have in mind. Context about your workflow helps prioritize and design the right approach.
Clone your fork and create a branch from main:
git clone https://github.com/<your-username>/domo-toolkit.git
cd domo-toolkit
git checkout -b your-branch-name
Install dependencies and start the dev server:
yarn
yarn dev
dist directory (see Development Setup)Use a short, descriptive branch name that reflects the change. For example:
fix-431-cookie-clearingadd-workflow-deleteupdate-favicon-settings-uiWrite clear, descriptive commit messages. Start with what the change does, not what file was edited. A single commit covering a cohesive set of changes is preferred over many small ones, but split unrelated changes into separate commits.
Good:
Added retry functionality for loading object detailsFixed scrolling height bug when switching between tabs on context footerAvoid:
Fixed bugUpdated filesWIPyarn build)npx eslint --no-warn-ignored src/)npx prettier --write .)All PRs are reviewed before merging. You may be asked to make changes – this is normal and part of the process. Keep an eye on your PR for comments and requested changes.
| Category | Technology | Version |
|---|---|---|
| Framework | React | 19.2.4 |
| Bundler | Vite | 7.3.0 |
| Extension Plugin | @crxjs/vite-plugin | 2.0.3 |
| UI Library | @heroui/react | 3.0.0-beta.8 |
| CSS | Tailwind CSS | 4.1.18 |
| Icons | @tabler/icons-react | 3.36.1 |
| Virtualization | @tanstack/react-virtual | 3.13.18 |
| Linter | ESLint | 10.0.2 |
| Formatter | Prettier | 3.7.4 |
src/
├── assets/ # Static assets and CSS
├── components/ # Shared React components
│ ├── functions/ # Action button implementations
│ ├── options/ # Settings page components
│ └── views/ # View components used in side panel for data discovery features
├── data/ # Release information used for new release badge and page
├── hooks/ # Custom React hooks
├── models/ # Data classes (DomoObject, DomoContext, DomoObjectType)
├── options/ # Settings/options page
├── popup/ # Popup UI (click on extension icon)
├── services/ # Domo API service functions
├── sidepanel/ # Side panel UI (contextual panel alongside pages)
├── utils/ # Utility functions
├── background.js # Service worker (background script)
└── contentScript.js # Content script (injected into Domo pages)
The extension has four main execution contexts:
Content Script (detects page context)
→ Background Service Worker (message relay, caches context)
→ Popup/Sidepanel (receives context via chrome.runtime messages)
→ User triggers action → Services execute via content script
# Clone the repository (or your fork)
git clone https://github.com/brycewc/domo-toolkit.git
cd domo-toolkit
# Install dependencies
yarn # or: npm install
# Start dev server (with HMR)
yarn dev # or: npm run dev
# Build for production
yarn build # or: npm run build
# Preview production build
yarn preview # or: npm run preview
Load the extension in Chrome or Edge:
chrome://extensions/ or edge://extensionsdist directoryThe project uses ESLint with three plugin layers configured in eslint.config.js:
@eslint/js recommended — standard JavaScript code-quality rules (no unused vars, no undef, etc.)eslint-plugin-perfectionist — enforces alphabetical sorting of imports, exports, object keys, JSX props, switch cases, and class members@stylistic/eslint-plugin — enforces consistent formatting (brace style, spacing, indentation, quotes, semicolons, etc.)Key sorting rules enforced by perfectionist:
on*), then multiline propsdefault lastUnused variables must be prefixed with _ (e.g., _event, _unused). Caught errors are exempt.
Run ESLint to check files:
npx eslint --no-warn-ignored <file-paths>
prettier-plugin-tailwindcss@/ maps to src/ (e.g., import { Copy } from '@/components')src/hooks/)forwardRef neededtoJSON() and static fromJSON() for serialization (required for message passing between extension contexts)data-theme attribute on document rootsrc/assets/global.css)Services need to inherit user session, authentication, and permissions by running in the page context:
import { executeInPage } from '@/utils/executeInPage';
const result = await executeInPage(
(arg1, arg2) => {
// This runs in the Domo page context
return fetch('/api/endpoint').then((r) => r.json());
},
[arg1, arg2],
tabId
);
Popup and sidepanel listen for context updates from the background service worker. Messages are filtered by currentTabId so each view only responds to updates for the tab it’s displaying:
useEffect(() => {
const handleMessage = (message, sender, sendResponse) => {
if (message.type === 'TAB_CONTEXT_UPDATED') {
if (message.tabId === currentTabId) {
const context = DomoContext.fromJSON(message.context);
setCurrentContext(context);
}
sendResponse({ received: true });
return true;
}
return false;
};
chrome.runtime.onMessage.addListener(handleMessage);
return () => chrome.runtime.onMessage.removeListener(handleMessage);
}, [currentTabId]);
Status messages are managed by the useStatusBar hook in the parent App component (popup or sidepanel). Child components receive an onStatusUpdate callback prop and call it to display transient alerts:
// In the parent App component (popup/App.jsx or sidepanel/App.jsx)
const { statusBar, showStatus, hideStatus } = useStatusBar();
// Pass showStatus down as a prop
<ActionButtons onStatusUpdate={showStatus} />
<ContextFooter onStatusUpdate={showStatus} />
// Render StatusBar alongside content (animated overlay)
{statusBar.visible && (
<StatusBar
title={statusBar.title}
description={statusBar.description}
status={statusBar.status}
timeout={statusBar.timeout}
onClose={hideStatus}
/>
)}
Inside action components, call the callback to show a status message:
// In any action component
onStatusUpdate('Copied', 'Object ID copied to clipboard', 'success', 3000);
| Permission | Purpose |
|---|---|
sidePanel |
Open side panel UI |
storage |
Persist settings (sync) and cache (local) |
scripting |
Inject and execute scripts in pages |
activeTab |
Access current tab info |
clipboardRead/Write |
Copy object IDs |
cookies |
Clear Domo cookies |
webNavigation |
Listen for navigation events |
webRequest |
Detect 431 errors for auto cookie clearing |
Host permission: *://*.domo.com/*
| File | Purpose |
|---|---|
vite.config.js |
Vite config with CRXJS, Tailwind, and path aliases |
manifest.config.js |
Chrome extension manifest v3 (permissions, content scripts, etc.) |
eslint.config.js |
ESLint config with perfectionist sorting and stylistic rules |
.prettierrc |
Prettier formatting rules |