Tranquil Settings — Delivery Notes
Delivery summary for the Tranquil settings tab — a top-level panel in the settings pane, seeded with a Toast Duration control, plus a theming pass so the whole settings surface wears the business theme. For the decision record, read ADR-0011: A first-class Tranquil settings tab; this note says what shipped and where.
Status
Implemented; pending in-app verification. Work is isolated on a feat/tranquil-settings-panel branch in each affected repo. Not yet committed or merged.
| Piece | State |
|---|---|
tranquil.* config namespace + tranquil.toastDuration (schema in tranquil-config) | ✅ built |
“Tranquil” tab registered on settings-view via addCorePanel (reuses core SettingsPanel) | ✅ built |
One-way bridge tranquil.toastDuration → notifications.defaultTimeout | ✅ built |
notify() helpers read the setting (tranquil-automations + tranquil-browser) | ✅ built |
Settings-pane theming in both business themes (settings.less) | ✅ built |
| ADR-0011 + this note | ✅ written |
| End-to-end run in the app | ⏳ pending |
Why this shape
The trigger was trivial — toasts vanished in ~1s — but the fix was hard-coded in two places: TOAST_TIMEOUT = 1000 inside Tranquil’s own notify() helper (duplicated in tranquil-automations and tranquil-browser). Core’s notifications.defaultTimeout was not the culprit; it only governs
non-dismissable direct toasts.
Rather than bump a constant, we gave Tranquil a real settings home. Two constraints shaped it:
- settings-view exposes no service to add a top-level tab — only an internal
addCorePanel(name, icon, factory)on the liveSettingsViewinstance. We reach each instance viaatom.workspace.observePaneItems(settings-view is a pane item) and call it — no third-party edits. - Reuse over reinvention: core’s
SettingsPanelauto-renders config-bound controls for a whole namespace. Pointing it at atranquil.*namespace gives free two-way binding and — because it emits the same markup as Core/Editor — means theming the settings surface once styles our tab too.
That last point is the payoff: the Tranquil tab needs zero bespoke CSS.
What shipped & where
tranquil-config (src/index.js) — the tab, schema, bridge
- Schema via
atom.config.setSchema('tranquil', …)(not package.json, so keys aretranquil.*nottranquil-config.*):toastDurationinteger, default3000, min1000, max60000. - Bridge:
atom.config.observe('tranquil.toastDuration', v => atom.config.set('notifications.defaultTimeout', v))so the few directatom.notifications.add*callers (tranquil-drag-drop, tranquil-window-color) honor the same value. - Tab registration:
atom.workspace.observePaneItems(item => …)duck-types eachSettingsViewinstance (typeof item.addCorePanel === 'function'+ constructor name) and callsaddCorePanel('Tranquil', 'gear', factory), idempotent viapanelCreateCallbacks['Tranquil']. - Panel factory: lazy-requires
SettingsPanelby resolved path (atom.packages.resolvePackagePath('settings-view')+lib/settings-panel), wrapsnew SettingsPanel({ namespace: 'tranquil', icon: 'gear', title: 'Tranquil Settings' })in a.panels-itemshell that suppliesshow/focus/destroy(SettingsPanel only hasdestroy), mirroring core’sgeneral-panel.js. Lazy + path-resolved so a missing internal degrades to “no tab”, never a crash (the dev-package silent-require trap). - The existing tree-view-sync toggle’s inline toast now also reads
tranquil.toastDuration.
notify() helpers — read the setting
tranquil-automations/lib/notify.js— replacedTOAST_TIMEOUT = 1000with atoastTimeout(options)readingatom.config.get('tranquil.toastDuration')(fallback3000), still honoring a per-calloptions.timeoutoverride.tranquil-browser/lib/notify.js— same base timeout, but errors/warnings keep a longer floor:Math.max(LONG_TIMEOUT /* 6000 */, base), so they stay readable/copyable.
These two files are duplicated across repos and were edited in lockstep. Nothing imported the old
TOAST_TIMEOUTexport, so removing it is safe.
Business themes — styles/settings.less (identical in both)
Expanded the existing partial coverage into a full pass over the settings surface, all scoped under @{theme-scope} and written against colors.less tokens (so the light theme is a byte-identical
copy):
- Form widgets, matched to the design system (main-view.html mockup + the find bar): config mini
text/number inputs styled as
.form-input(surface-input fill,@border-strong, 6px radius,@accentfocus ring); checkboxes/radios as the native control tinted withaccent-color: @accent(not hand-drawn boxes); selects and color swatches in the same input family. - Setting rows:
.setting-title/.setting-descriptionhierarchy,.control-groupspacing. - Buttons: neutral
.btnon@surface-active;.btn-primary/.btn-info/.update-all-button/.selectedon the@accentfill; grouped-button radii. - Chrome: nav hover/active with accent icons, section-heading octicons, badges, breadcrumb, alerts
(
info/success/warning/dangertinted with semantic tokens), keybinding.is-userrow, tables, search results, package cards.
The Tranquil tab inherits all of the above with no dedicated rules.
Where it lives (file map)
tranquil-config/src/index.js— schema, bridge, tab registration, panel factorytranquil-automations/lib/notify.js,tranquil-browser/lib/notify.js— config-backed timeoutstranquil-business-dark/styles/settings.less,tranquil-business-light/styles/settings.less— identical settings-pane themingtranquil-client— unchanged (all work is in the sibling owned repos)
Verify
nvm use && yarn startfromtranquil-client.- Open Settings (
cmd-,) → a Tranquil tab (gear) sits in the nav atatom://config/tranquil, showing a themed Toast Duration field defaulting to3000and clamping1000–60000. - Trigger a
notify()toast (browser “Saved …” on a.urlsave; an automations register command) → it now stays ~3s and tracks the field live (no restart); browser error/warning toasts still ~6s;atom.config.get('notifications.defaultTimeout')tracks the setting. - Switch
tranquil-business-dark↔-light→ nav, inputs, checkboxes, buttons/.btn-primary, badges, breadcrumb, cards all read as Tranquil in both, and the Tranquil tab is indistinguishable from Core/Editor in styling. - Confirm no regression: tree-view-sync toggle and command-palette MRU/filter still work (lazy
require(settings-panel)didn’t throw at load); the pane still lays out in a left/right dock.