Monorepo layout
genie/ is a pnpm workspace (pnpm@9.15.4). pnpm-workspace.yaml includes apps/*, infra/* and packages/*. Workspace packages are consumed as TypeScript or JavaScript source through file: or workspace: dependencies; there is no separate library build step.
Workspace layout
| Path | Package name | Purpose | Key files |
|---|---|---|---|
apps/standalone | standalone | The Genie frontend: Vite + React 19 + React Router 7. Used by the local web app, the desktop renderer and the cloud web build. | vite.config.ts, src/App.tsx (routes), src/app/lib/appConfig.ts, src/app/GenomeBrowserPanel.tsx, src/app/projects/[projectId]/ProjectWorkspaceScreen.tsx, src/app/projects/[projectId]/agentModes/*.ts, src/app/lib/eg3TrackMapping.ts, public/genie-runtime-config.js |
apps/backend | genie-backend | Local backend host. A thin wrapper that resolves options from the environment and starts @genie/backend-core. | server.js, localServer.js (createLocalBackendOptions, startLocalBackend), integration-server.js |
apps/desktop | genie-desktop | Electron 35 desktop app. Starts the backend in-process and loads the renderer from it. | src/bootstrap.cjs, src/main.js, src/preload.cjs, scripts/prepare-backend-resources.mjs |
apps/genome-track-demo | genome-track-demo | Vite demo of the native GenomeBrowser with the standard tracks. | src/App.tsx, src/scenarios.ts |
apps/docs | genie-docs | This documentation site (Docusaurus). | docusaurus.config.ts, sidebars.ts, docs/, tests/ |
packages/backend-core | @genie/backend-core | HTTP server, WebSocket /ws, Codex app-server bridge, tool handlers, storage. | src/server.js, src/assetCatalog.js, src/analysisState.js, src/genome/{toolHandlers,regionData,browserState,pluginCompiler}.js |
packages/chat-core | @genie/chat-core | Chat types, ChatStore, ChatSession. | src/types.ts, src/store.ts, src/session.ts |
packages/chat-ui | @genie/chat-ui | ChatView React component with Markdown rendering and citation chips. | src/ChatView.tsx |
packages/codex-runtime | @genie/codex-runtime | Browser-side WebSocket transport to the backend. | src/client/webSocketTransport.ts, src/types.ts |
packages/genome-browser | @genie/genome-browser | Native GenomeBrowser, TrackConfig, TrackRegistry, standard track registrations, shared track types. | src/GenomeBrowser.tsx, src/TrackConfig.ts, src/TrackRegistry.ts, src/types.ts, src/standard/* |
packages/genome-track-container | @genie/genome-track-container | Region and navigation models, viewport with pan and zoom, prefetch logic. | src/model/*.ts, src/GenomeTrackViewport.tsx, src/pan/prefetch.ts |
packages/genome-tracks-standard | @genie/genome-tracks-standard | SVG track renderers, data sources (bigWig, bigBed, tabix, gene API), genome helpers, StandardTrackContainer. | src/StandardTrackContainer.tsx, src/render/*, src/sources/*, src/genomes/*, src/egreact/trackTypes.ts |
packages/genome-catalog | @genie/genome-catalog | Generated genome assembly catalog (plain JavaScript with type declarations). | src/index.js, src/index.d.ts, src/generatedCatalog.js, scripts/generate-from-egreact.mjs |
infra/aws-cdk | @genie/aws-cdk | CDK app for the cloud web build and project API. | bin/genie-cloud.ts, lib/genie-cloud-stack.ts, lambda/api/index.mjs |
vendor/eg3 | @eg3/tracks, @eg3/adapter | Pre-built eg3 engine and adapter. Do not edit. | README.md, wuepgg3-track/, eg-adapter/dist/ |
ref/ | Reference notes. | codex-app-server.md, standalone-action-menu-system.md | |
scripts/ | Workspace scripts. | dev-web-cloud.mjs, build-igvf-genome-bundle.mjs, igvf-genome-bundle.entry.ts, igvf-genome-bundle.d.ts | |
session-data/ | Runtime data written by the local backend. Not source. | projects.json, sessions.json, projects/<projectId>/... |
apps/standalone still contains some names from an earlier Next.js setup ("use client" directives, [projectId] folder names, the NEXT_PUBLIC_ variable prefix and the .next-desktop output folder). They are plain Vite code now.
@genie/genome-browser
The native browser stack. In the project workspace, Genie renders with eg3 and uses this package only for types (TrackModel, TrackInspectorSnapshot, TrackMenuComponent) and the re-exported region models. The full component is used by the public landing browser in static builds, the developer pages, the demo app and the IGVF bundle.
Exports:
| Export | Kind | Notes |
|---|---|---|
GenomeBrowser | React component | Props include registry, tracks, viewRegion, onViewRegionChange(start, end), legendWidth, interactionMode ("pan", "zoom", "highlight"), browserRef, widthPx, heightPx and feature, inspector and context-menu callbacks |
GenomeBrowserHandle | Type | exportImage(options?) resolves to { blob, width, height, pixelRatio }, where blob is a PNG image |
TrackConfig | Abstract class | One instance per track; defines data source, renderer and fetch rules |
TrackRegistry | Class | Maps a lower-cased track type to a TrackConfig constructor |
DataSource | Interface | getData(dataRegion, basesPerPixel, options, signal), optional getMeta, getLastRequestInfo, cleanUp |
registerStandardTracks(registry) | Function | Registers the built-in track types |
TrackModel, TrackComponent, TrackComponentProps, inspector, feature and menu types | Types | |
ChromosomeInterval, DisplayedRegionModel, Feature, NavigationContext, OpenInterval, RegionExpander | Re-exports | From @genie/genome-track-container |
TrackConfig methods
| Method | Default | Purpose |
|---|---|---|
initDataSource() | null | Create the track's DataSource. Kept for the life of the track unless shouldRecreateDataSource(prev, next) returns true. |
getComponent() | Abstract | The React component that draws the track |
formatData(raw) | Identity | Turn the data source payload into renderer data |
getInitialData() | null | Data shown before the first fetch |
getDefaultHeightPx() | null | Height when the track has no heightPx (the browser falls back to 60 px) |
usesDefaultFrame() | true | Wrap the renderer in the standard frame (label, loading badge, error band, pan transforms) |
getMenuComponents() | [] | Track-specific context-menu components |
shouldFetch(prev, next) | Region or JSON change | Refetch when start, end or bases per pixel change, or when options or config change |
trackNeedsFetch() | true | Return false for tracks without data (such as the ruler) |
TrackRegistry.createTrackConfig(track) looks up track.type, or track.filetype if type is empty, and throws Unknown track type: <type> when nothing is registered.
Example: a custom track type
This example registers a gcbins track that fetches binned values from a JSON endpoint and draws them as bars. It uses the same coordinate conversion as the standard renderers: genome intervals are converted to navigation-context coordinates, then to pixels with the drawing model.
import {
ChromosomeInterval,
GenomeBrowser,
TrackConfig,
TrackRegistry,
registerStandardTracks,
type DataSource,
type DisplayedRegionModel,
type TrackComponentProps,
type TrackModel,
} from "@genie/genome-browser";
import { makeGenomeDefaultViewRegion } from "@genie/genome-tracks-standard";
import { useState, type ReactElement } from "react";
type GcBin = { chr: string; start: number; end: number; value: number };
type GcOptions = { color?: string };
class GcBinsSource implements DataSource<GcOptions, GcBin[]> {
constructor(private readonly endpoint: string) {}
async getData(
dataRegion: DisplayedRegionModel,
basesPerPixel: number,
_options: GcOptions | undefined,
signal?: AbortSignal,
): Promise<GcBin[]> {
const bins: GcBin[] = [];
for (const locus of dataRegion.getGenomeIntervals()) {
const params = new URLSearchParams({
chr: locus.chr,
start: String(locus.start),
end: String(locus.end),
bpp: String(basesPerPixel),
});
const response = await fetch(`${this.endpoint}?${params.toString()}`, { signal });
if (!response.ok) {
throw new Error(`GC request failed with status ${response.status}`);
}
bins.push(...((await response.json()) as GcBin[]));
}
return bins;
}
}
function GcBinsTrack(props: TrackComponentProps<GcBin[], GcOptions>): ReactElement {
const { data, drawModel, heightPx, track } = props;
const navContext = drawModel.getViewRegion().getNavigationContext();
const color: string = track.options?.color ?? "#0000FF";
const maxValue: number = Math.max(1, ...(data ?? []).map((bin: GcBin) => bin.value));
return (
<svg width={drawModel.getDrawWidth()} height={heightPx}>
{(data ?? []).flatMap((bin: GcBin, index: number) =>
navContext
.convertGenomeIntervalToBases(new ChromosomeInterval(bin.chr, bin.start, bin.end))
.map((span, spanIndex: number) => {
const xSpan = drawModel.baseSpanToXSpan(span);
const barHeight: number = (bin.value / maxValue) * heightPx;
return (
<rect
key={`${index}-${spanIndex}`}
x={xSpan.start}
y={heightPx - barHeight}
width={Math.max(1, xSpan.end - xSpan.start)}
height={barHeight}
fill={color}
/>
);
}),
)}
</svg>
);
}
class GcBinsTrackConfig extends TrackConfig<GcOptions, unknown, GcBin[], GcBin[]> {
initDataSource(): DataSource<GcOptions, GcBin[]> {
const endpoint: string | undefined = this.track.url;
if (!endpoint) {
throw new Error("gcbins track requires a url");
}
return new GcBinsSource(endpoint);
}
getComponent() {
return GcBinsTrack;
}
getInitialData(): GcBin[] {
return [];
}
getDefaultHeightPx(): number | null {
return 50;
}
shouldRecreateDataSource(prev: TrackModel<GcOptions>, next: TrackModel<GcOptions>): boolean {
return prev.url !== next.url;
}
}
const registry = new TrackRegistry();
registerStandardTracks(registry);
registry.registerTrackType("gcbins", GcBinsTrackConfig);
const tracks: TrackModel[] = [
{ id: "ruler", type: "ruler", name: "Ruler" },
{ id: "gc", type: "gcbins", name: "GC content", url: "https://example.org/api/gc", options: { color: "#6501A8" } },
];
export function GcDemo(): ReactElement {
const [viewRegion, setViewRegion] = useState<DisplayedRegionModel>(() => makeGenomeDefaultViewRegion("hg38"));
return (
<GenomeBrowser
registry={registry}
tracks={tracks}
viewRegion={viewRegion}
onViewRegionChange={(start: number, end: number) => setViewRegion(viewRegion.clone().setRegion(start, end))}
legendWidth={120}
/>
);
}
Custom track types registered this way work in the native stack only. The project workspace renders with eg3, which accepts its own fixed list of track types (see Browser engine). The agent's custom track tools are disabled for the same reason.
@genie/genome-track-container
Region and viewport primitives, adapted from the eg-react models.
| Export | What it does |
|---|---|
NavigationContext | Joins features (usually chromosomes) end to end into one coordinate space. parse(str) accepts an exact feature name (returns a 6 bp window at its centre) or anything ChromosomeInterval.parse accepts. convertGenomeIntervalToBases(interval) maps chromosome coordinates to context coordinates. |
DisplayedRegionModel | The visible window [start, end) in context coordinates. setRegion (clamped), pan, panLeft, panRight (one window), zoom(factor, focalPoint = 0.5), getGenomeIntervals(), currentRegionAsString() (1-based start: a region stored 0-based as chr7:27053396-27373765 prints as chr7:27053397-27373765). |
ChromosomeInterval | A chr, start, end interval. ChromosomeInterval.parse strips commas and matches chr, start and end separated by any non-word characters. If the string contains :, the start is treated as 1-based and converted to 0-based. chr7:27,053,397-27,373,765 and chr7 27053396 27373765 give the same interval. |
RegionExpander | Expands the visible window so off-screen data is fetched (default 2 window widths on each side). |
LinearDrawingModel | Base-to-pixel conversion for a region and a drawing width. |
GenomeTrackViewport, GenomeTrackContainer | Viewport with pan, zoom and highlight modes; resize-aware wrapper. |
computeNextTargetWindow | Pan prefetch logic. |
@genie/genome-tracks-standard
| Export | What it does |
|---|---|
StandardTrackContainer | A self-contained browser for ruler, gene annotation, bigWig, bedGraph, bigBed, RepeatMasker, BED and a few annotation types. Used by the public landing browser. |
BigWigSource, BigBedSource, TabixSource | Data sources built on @gmod/bbi and @gmod/tabix |
fetchGeneAnnotations, fetchGeneNameMatches, GENE_ANNOTATION_API_DEFAULT | Gene annotation API client (https://lambda.epigenomegateway.org/v3) |
makeGenomeNavContext, makeGenomeDefaultViewRegion, getGenomeDefaultTracks, getGenomePublicHubManifest | Genome helpers built on the catalog |
EG_REACT_TRACK_TYPES, isEgReactTrackType, normalizeEgReactTrackType | The eg-react track type list used by the UI |
RulerTrackBody, GeneAnnotationTrackBody, BigWigTrackBody, BigBedTrackBody, RepeatMaskerTrackBody, StandardTrackFrame | SVG renderers and the standard frame |
@genie/genome-catalog
A generated catalog of genome assemblies used by the frontend and the backend. It exports DEFAULT_GENOME_ID ("hg38"), allGenomeIds, genomeCatalog, treeOfLife, isKnownGenomeId, normalizeGenomeId, getGenomeCatalogEntry, getGenomeDisplayName (for example "human (hg38)"), getGenomeSummaries, cloneDefaultTracksForGenome and clonePublicHubManifestForGenome.
The raw catalog holds 60 assemblies. The genome picker shows only the 57 that eg3 supports (see Genome assemblies).
src/generatedCatalog.js is generated from eg-react's allGenomes.ts. To regenerate it, point the generator at an eg-react frontend folder:
EG_REACT_FRONTEND_ROOT=/path/to/eg-react/frontend pnpm --filter @genie/genome-catalog generate
Without the variable, the script looks for ../../eg-react-master/frontend relative to the genie/ folder.
Chat packages
| Package | Exports |
|---|---|
@genie/chat-core | Types (ChatMessage, ChatCitation, ChatToolDefinition, ChatToolCall, ChatEvent, ChatTransport, ChatState, ...), ChatStore, ChatSession, createChatSession |
@genie/chat-ui | ChatView and ChatViewProps |
@genie/codex-runtime | Types and WebSocketChatTransport (also at the @genie/codex-runtime/client subpath). The transport waits up to 30 seconds for the backend's ready message. Files under src/server/ are older code that the app does not use. |
Root scripts
Run these from genie/.
| Script | What it runs |
|---|---|
pnpm dev | Vite dev server for standalone (local mode) and the backend, in parallel |
pnpm dev:backend | The backend only (node server.js, no file watching) |
pnpm dev:web | Vite in web mode (cloud configuration, assistant off) |
pnpm dev:web-cloud | genie-local-cloud with simulated latency plus dev:web |
pnpm dev:local-cloud | genie-local-cloud (sibling checkout) |
pnpm dev:local-cloud:latency | genie-local-cloud with 800 ms latency and 400 ms jitter |
pnpm desktop:dev | Build the desktop renderer, prepare backend resources, start Electron |
pnpm desktop:pack | Same build, then electron-builder --dir (unpacked app) |
pnpm desktop:dist | Same build, then macOS dmg and zip (unsigned) |
pnpm build, pnpm build:web | vite build --mode web to apps/standalone/dist |
pnpm build:desktop-renderer | Desktop renderer build to apps/standalone/.next-desktop |
pnpm cdk:synth | cdk synth in infra/aws-cdk |
pnpm verify:backend | @genie/backend-core tests, then the backend WebSocket smoke test |
pnpm lint | ESLint for standalone |
pnpm typecheck | tsc --noEmit for standalone |
pnpm test | pnpm -r test across the workspace |
pnpm bundle:igvf-genome | Bundle the native browser for the IGVF catalog (needs --outdir) |
pnpm docs:dev, docs:build, docs:serve, docs:test | This documentation site (see Docs site) |
See Testing for what the test scripts cover.