Docs

Multi-Repo Setup

This setup is for local development only. Production build configuration is handled separately.

Tranquil’s owned packages live in separate GitHub repos and are linked into tranquil-client for local development via Yarn 1’s link: feature, which creates live symlinks — no reinstall needed when you edit a package.


Repository Layout

~/Documents/Tranquil/Repos/
  tranquil-client/          ← main app (this repo)
  tranquil-automations/     ← login/session automation package
  tranquil-browser/         ← embedded browser package
  tranquil-config/          ← secrets/config package
  tranquil-theme-dark/      ← dark UI theme
  tranquil-theme-light/     ← light UI theme
  tranquil-tips/            ← background tips shown when no editors are open

All six directories must be siblings. The link:../ paths in tranquil-client/package.json resolve relative to the repo root.


GitHub Repos

npm nameGitHub repo
tranquil-automationstranquillabs/tranquil-automations
tranquil-browsertranquillabs/tranquil-browser
tranquil-configtranquillabs/tranquil-config
tranquil-theme-darktranquillabs/tranquil-theme-dark
tranquil-theme-lighttranquillabs/tranquil-theme-light
tranquil-tipstranquillabs/tranquil-tips

All repos are on the main branch.


First-Time Setup

Clone all owned repos alongside tranquil-client, then install:

cd /Users/david/Documents/Tranquil/Repos

git clone https://github.com/tranquillabs/tranquil-automations.git
git clone https://github.com/tranquillabs/tranquil-browser.git
git clone https://github.com/tranquillabs/tranquil-config.git
git clone https://github.com/tranquillabs/tranquil-theme-dark.git
git clone https://github.com/tranquillabs/tranquil-theme-light.git
git clone https://github.com/tranquillabs/tranquil-tips.git

cd tranquil-client
source ~/.nvm/nvm.sh && nvm use
yarn install
git submodule update --init ppm
cd ppm && yarn install && cd ..

# Install deps inside each sibling package so they can find their own modules
cd ../tranquil-automations && yarn install
cd ../tranquil-browser && yarn install
cd ../tranquil-tips && yarn install
cd ../tranquil-client

After installing, verify the symlinks exist:

ls -la node_modules/tranquil-automations 
        node_modules/tranquil-browser 
        node_modules/tranquil-config 
        node_modules/tranquil-theme-dark 
        node_modules/tranquil-theme-light

Each should point to ../../tranquil-* (two levels up, into the sibling directory).

Dev packages symlinks

Symlink the owned packages into ~/.tranquil/dev/packages/ so Tranquil discovers them as dev packages rather than core packages in the Settings view:

mkdir -p ~/.tranquil/dev/packages
ln -s /Users/david/Documents/Tranquil/Repos/tranquil-automations ~/.tranquil/dev/packages/tranquil-automations
ln -s /Users/david/Documents/Tranquil/Repos/tranquil-browser ~/.tranquil/dev/packages/tranquil-browser
ln -s /Users/david/Documents/Tranquil/Repos/tranquil-config ~/.tranquil/dev/packages/tranquil-config
ln -s /Users/david/Documents/Tranquil/Repos/tranquil-theme-dark ~/.tranquil/dev/packages/tranquil-theme-dark
ln -s /Users/david/Documents/Tranquil/Repos/tranquil-theme-light ~/.tranquil/dev/packages/tranquil-theme-light
ln -s /Users/david/Documents/Tranquil/Repos/tranquil-tips ~/.tranquil/dev/packages/tranquil-tips

Pulsar/Atom scans dev/packages/ before processing packageDependencies, so packages found there get isBundled: false and appear under Dev Packages instead of Core Packages. In a production build the symlinks won’t exist, so the packages fall through to packageDependencies and load from node_modules as bundled — which is the correct behavior for a shipped binary.


How link: Works

Yarn 1’s link: creates a symlink in node_modules pointing to the given path. Unlike file:, which copies the package into .yarn-cache on install, link: is a live reference — editing files in ../tranquil-automations/lib/ takes effect immediately in the running app without any reinstall.

package.json entries:

"tranquil-automations": "link:../tranquil-automations",
"tranquil-browser":     "link:../tranquil-browser",
"tranquil-config":     "link:../tranquil-config",
"tranquil-theme-dark":  "link:../tranquil-theme-dark",
"tranquil-theme-light": "link:../tranquil-theme-light"

These entries appear in both dependencies and packageDependencies, except tranquil-config which is only in dependencies — it is a utility library, not an Atom plugin, so Atom should not activate it directly.


Module Resolution

Sibling repos live outside tranquil-client. When Node.js resolves a symlink to its real path, upward traversal for node_modules starts from that real path — so packages inside tranquil-automations/ can’t find deps in tranquil-client/node_modules/.

The fix is to run yarn install inside each sibling package that has its own dependencies. Yarn creates a local node_modules/ there (including a tranquil-config symlink inside tranquil-automations/node_modules/), so all require() calls resolve correctly from the real path.

This is why the First-Time Setup section includes yarn install steps for tranquil-automations and tranquil-browser, not just tranquil-client.

A corollary that bites when adding a new package: a top-level require() for a dependency that only exists in tranquil-client/node_modules will throw at load and silently abort the package’s activation. See Adding a New Owned Package for the safe options (vendor the dep in the package, or require only atom + your own files).


tranquil-config Setup

tranquil-config provides API keys (GitHub) via a generated file that is never committed. After cloning tranquil-config, you must generate this file before yarn start will work.

One-time setup:

cd /Users/david/Documents/Tranquil/Repos/tranquil-config

# Copy the example and fill in real values
cp .env.example .env
# edit .env with actual keys

# Write config.json from .env
node write_config.js --staging    # or --production

# Generate src/generated/index.js from config.json
node generate.js

The generated file is gitignored and must be re-run whenever keys change. If you just need the app to start without real keys (e.g. after a fresh clone), you can create a stub:

cd /Users/david/Documents/Tranquil/Repos/tranquil-config
echo '{"github_token": ""}' > config.json
node generate.js

Updating an Owned Package

Work directly in the sibling directory, commit, and push to its own repo:

cd /Users/david/Documents/Tranquil/Repos/tranquil-automations
# ... make changes ...
git add -p
git commit -m "fix: ..."
git push

No changes to tranquil-client are needed unless the package’s npm name or API changes.


Adding a New Owned Package

The one fact that keeps costing time: under yarn start an owned package is loaded as a bundled package from tranquil-client/node_modules/<name>. That node_modules/<name> symlink is what actually loads the package. The ~/.tranquil/dev/packages/<name> symlink is cosmetic — it only moves the package from “Core Packages” to “Dev Packages” in the Settings view; on its own it does not load the package under yarn start. Create the package with only the dev/packages symlink and it silently never loads.

Steps, in order:

  1. Create the sibling repo ../tranquil-new-thing/ with a package.json whose main points at the entry file, e.g. "main": "./lib/main". (Atom loads <main>; if main is wrong or missing, activate() never runs.)

  2. Add the link: entry to both blocks in tranquil-client/package.jsondependencies and packageDependencies:

    "tranquil-new-thing": "link:../tranquil-new-thing"

    (Utility libraries Atom must not activate — like tranquil-config — go in dependencies only.)

  3. Create the node_modules/<name> symlink — this is the load-bearing step. Either run yarn install in tranquil-client, or create it directly to skip a full install:

    cd tranquil-client
    ln -sfn ../../tranquil-new-thing node_modules/tranquil-new-thing

    Verify it resolves into the sibling repo and exposes main:

    node -e "console.log(require.resolve('tranquil-new-thing/package.json'))"
  4. (Optional, cosmetic) Symlink into ~/.tranquil/dev/packages/ so it appears under Dev Packages rather than Core Packages in Settings:

    ln -s /Users/david/Documents/Tranquil/Repos/tranquil-new-thing ~/.tranquil/dev/packages/tranquil-new-thing
  5. Fully restart yarn start — not just a window reload. Package discovery re-reads the symlinks at startup; a window that was already open when the symlink was missing has cached a stub package with no main, so reloading it won’t pick up the fix.

  6. Create the private GitHub repo tranquillabs/tranquil-new-thing and push.

Verifying it loaded (and diagnosing when it didn’t)

In the editor DevTools console (⌘⌥I):

atom.packages.getActivePackage('tranquil-new-thing')
  • Missing node_modules/<name> symlink → you still get a package object (its name is in packageDependencies), but a stub: path points at .../node_modules/tranquil-new-thing, metadata is {name} only (no main), mainModulePath: undefined, mainModule: null, mainActivated: false. Atom found the name but the path didn’t exist, so it never required a main module and activate() never ran. → Do step 3, then fully restart.
  • HealthymainModule is your module object and mainActivated: true; commands/features are registered.

Gotcha — what an owned package may require

A package loaded via the node_modules/<name> symlink resolves its own require()s from its real sibling-repo path, not from tranquil-client/node_modules. So a top-level require('some-app-dependency') that lives only in tranquil-client/node_modules throws at module load, and a throw at load silently aborts activation — no error surfaced, no commands registered. (Same silent-crash failure mode as a missing dependency in a main-process file.) Safe options:

  • Require only atom, your own lib/, and Node built-ins; prefer a design with zero external deps (e.g. register one command per option instead of pulling in a picker library); or
  • Give the package its own dependency and run yarn install inside it so the module lands in <pkg>/node_modules — this is what tranquil-browser does for atom-space-pen-views.

When “the commands don’t show up,” suspect a throwing top-level require first, then check getActivePackage(...) as above.


Why Separate Repos?

tranquil-client is forked from Pulsar (open source). Keeping owned packages in their own repos means:

  • No proprietary code in the Pulsar fork’s history
  • Each package can be versioned and released independently
  • The tranquil-client remote stays clean