Repository layout
Three programs share one repository: the Next.js app, the Electron main process, and this documentation site. They have separate TypeScript configs and separate dependency trees.
Directories
Section titled “Directories”| Path | Contents | Notes |
|---|---|---|
app/ |
App Router routes and globals.css |
(shell) is a route group; everything except /auth/* renders inside the app chrome |
components/ |
React components, including components/tools/ |
one entry per registry entry, lazily imported |
components/tools/ |
one file or one directory per tool, plus shared/ |
48 tool entries, 1:1 with the registry; shared/ is a helper, not a tool |
lib/ |
all pure logic and every network client | this is where the testable code lives |
lib/reference/ |
static reference data: ports, protocol numbers, address ranges | data, not logic |
contexts/ |
auth and project React contexts | the only two providers with state that outlives a route |
electron/ |
main process, preload, navigation policy, and electron/network/handlers.ts |
compiled separately by electron/tsconfig.json to CommonJS |
tests/unit/ |
node-environment tests over lib/ and electron/ |
the larger of the two projects |
tests/components/ |
happy-dom tests that mount real components | includes the accessibility gate |
tests/fixtures/ |
captured ping, traceroute and arp output |
real macOS transcripts, not synthetic |
docs/ |
this Starlight site | separate pnpm project with its own lockfile |
public/ |
static assets copied verbatim into out/ |
also the drop point for the built docs |
.github/workflows/ |
CI, release, CodeQL, Scorecard, dependency review | every job pins actions by commit SHA |
Casks/ |
the Homebrew cask, regenerated by the release workflow | do not edit by hand |
The split that matters is lib/ versus components/: anything worth testing goes in lib/, and components stay thin enough that the component suite can be about rendering and accessibility rather than logic. CONTRIBUTING.md states the rule directly: write tests for any new logic in lib/.
A tool is a directory whenever it has more than one panel, so a file count under components/tools/ is larger than the tool count and the two must not be conflated. The registry is the arbiter: every load thunk resolves to exactly one entry under components/tools/, and the only directory there that is not a tool is shared/. That distinction is also why docs/scripts/check-counts.mjs excludes the phrase “N tool files” from its total-count rule.
Generated directories
Section titled “Generated directories”Four paths are build output and are gitignored. Editing them does nothing.
| Path | Generated by | Source of truth |
|---|---|---|
out/ |
next build |
the app |
dist-electron/ |
tsc -p electron/tsconfig.json |
electron/ |
public/docs/ |
pnpm build:docs |
docs/dist/ |
docs/src/content/docs/tools/ |
docs/scripts/generate-tool-pages.mjs |
lib/tool-registry.ts |
The last row is the one that surprises people. Every page under /docs/tools/ is written at docs build time from the registry, so a tool’s description in the docs is literally the same string the app renders. Change the registry, and the docs change on the next build. Editing the generated markdown is silently discarded.
The generator also fails the build if the registry gains or loses a category that the manual sidebar in docs/astro.config.mjs does not account for. A missing sidebar entry would be an invisible gap, so it is treated as an error.
The three TypeScript programs
Section titled “The three TypeScript programs”| Config | Covers | Target | Notable settings |
|---|---|---|---|
tsconfig.json |
app/, components/, lib/, contexts/, tests/ |
ES2020, jsx: preserve |
strict: true, noEmit, path alias @/* |
electron/tsconfig.json |
electron/ |
ES2020, CommonJS | moduleResolution: node, emits to dist-electron/ |
docs/tsconfig.json |
docs/ |
extends astro/tsconfigs/strict |
separate program, separate lockfile |
electron/tsconfig.json carries ignoreDeprecations: "6.0" with a comment: node10 module resolution is deprecated in TypeScript 6 but it is the correct setting for a CommonJS main process, and node16 would force an explicit .js extension on every relative import.
Worth knowing before you trust a green build: next.config.mjs sets eslint.ignoreDuringBuilds and typescript.ignoreBuildErrors to true. A passing next build says nothing about types. pnpm typecheck and pnpm lint are the actual gates, and CI runs them as separate steps.
How the docs get into both builds
Section titled “How the docs get into both builds”# what the root build script chains togetherpnpm build:docs # cd docs && pnpm install && pnpm build, then copy dist to public/docsnext build # copies public/ verbatim into out/, so out/docs/ appearsThe web deploy serves out/ from Vercel. The desktop app serves the same out/ over a loopback HTTP server, so /docs/ works with no network. That means anything the docs load from a remote host would break the offline story, which is why they load no webfont, no CDN script, and no external stylesheet.
About the code links in these docs
Section titled “About the code links in these docs”Every link into the source points at main, not at a commit SHA. That is deliberate: a main link keeps working as the file evolves, at the cost of line ranges drifting. The line ranges were taken when each page was written, so treat the range as a hint and the symbol name in the link text as the real anchor. If a range looks wrong, search for the function name.