Stepping the grid coarser pulled tiles past the right edge back in and left them wherever that put them — on top of one another, which the board never allows under a drag. Tiles now settle in reading order: one that would land on a tile already settled takes the first free spot, the same rule a newly added tile follows. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
426 lines
24 KiB
Markdown
426 lines
24 KiB
Markdown
# ProdDash
|
||
|
||
A modular **production dashboard** for Waters Church. Any browser on the
|
||
building network opens one page and composes its own dashboard out of
|
||
**modules** — independent tiles that each show one aspect of a live service.
|
||
Ships with the live **ProdCom transcript**, the **ProPresenter Now / Next**
|
||
display, and a booth **clock**; new modules drop into `modules/` without
|
||
touching the shell (see [docs/MODULE-GUIDE.md](docs/MODULE-GUIDE.md)).
|
||
|
||
## How it works
|
||
|
||
`server.js` (zero dependencies, just Node.js) does four things:
|
||
|
||
1. Serves the dashboard shell (`public/`) and the admin page (`/admin`).
|
||
2. Discovers modules in `modules/` and serves each one's client at
|
||
`/modules/<id>/`.
|
||
3. Mounts each enabled module's server routes under `/api/modules/<id>/` —
|
||
that's where module servers follow ProdCom, poll ProPresenter, and stream
|
||
live updates over SSE — one upstream connection per module, however many
|
||
dashboards are open — so browsers never talk to production gear directly
|
||
(no CORS, and API keys never leave the server).
|
||
4. Stores server-wide module config and named layouts in a per-machine data
|
||
directory outside the app folder, so updating ProdDash never loses them.
|
||
|
||
Each browser arranges its own tiles (add, drag, resize, remove, per-tile
|
||
settings); the layout autosaves to that browser's `localStorage`. Named
|
||
layouts can also be saved to the server and loaded from any other browser.
|
||
Every module survives its upstream disappearing: tiles show a red status dot
|
||
and a clear degraded state, then recover on their own.
|
||
|
||
## Run it
|
||
|
||
### macOS — the app
|
||
|
||
Open the disk image and drag **ProdDash** to Applications. That's the whole
|
||
installation: the app carries the dashboard and its own Node runtime, so
|
||
nothing else has to be installed and nothing can be missing. It lives in the
|
||
menu bar, starts and stops the server, can open at login, and holds the
|
||
permissions the modules need — the microphone the LTC listener reads, and
|
||
local network access for ProdCom and ProPresenter. macOS grants those to the
|
||
app that owns the process, so a server started any other way (over SSH, from
|
||
a launchd job) simply doesn't have them, and an LTC input goes silently to
|
||
zeroes.
|
||
|
||
Build the disk image from a checkout with
|
||
[launcher/macos](launcher/macos)/`build.sh`:
|
||
|
||
```bash
|
||
cd launcher/macos && ./build.sh --dmg
|
||
```
|
||
|
||
### Anywhere else, or from a checkout
|
||
|
||
Requires [Node.js](https://nodejs.org) 18 or newer.
|
||
|
||
```bash
|
||
node server.js
|
||
```
|
||
|
||
Or double-click **Start ProdDash.command** (macOS) / **start.bat** (Windows).
|
||
|
||
Then open the URL it prints — `http://localhost:24500` on this machine, or
|
||
the `http://<this-machine's-IP>:24500` address from any other device on the
|
||
network.
|
||
|
||
## Configure
|
||
|
||
Server-wide module settings (ProdCom URL, ProPresenter host, API keys,
|
||
enable/disable) live in the **admin page** — open **Admin** from the
|
||
dashboard's top bar, or go to `/admin`. Saving re-initialises the module
|
||
immediately; open tiles reconnect on their own.
|
||
|
||
### Where settings are kept
|
||
|
||
Everything you enter in the admin page (module connection settings, API keys,
|
||
enabled/disabled flags) and every named layout is written to a **data
|
||
directory on the machine running the server** — not into the app folder:
|
||
|
||
| OS | Data directory |
|
||
| --- | --- |
|
||
| Windows | `%APPDATA%\ProdDash` (e.g. `C:\Users\<you>\AppData\Roaming\ProdDash`) |
|
||
| macOS | `~/Library/Application Support/ProdDash` |
|
||
| Linux | `$XDG_CONFIG_HOME/proddash`, else `~/.config/proddash` |
|
||
|
||
Because it lives outside the checkout, you can `git pull`, re-clone, or reset
|
||
the app folder from `main` and start the new version with everything still
|
||
configured. The admin page shows the exact path in use, and the server prints
|
||
it at startup (`Settings : …`).
|
||
|
||
The directory holds `modules.json` (module config), `layouts/` (named
|
||
layouts) and `modules/` (modules installed from the admin page), all written
|
||
by the app itself. Installed from the macOS app, it also holds `app/` — the
|
||
copy of ProdDash that actually runs, laid down from inside the app bundle on
|
||
first launch and updated in place from then on (nothing may write inside a
|
||
signed .app, and ProdDash updates itself). It may also hold a `proddash.json`
|
||
with this machine's shell settings — the same keys as the checked-in
|
||
[config/proddash.json](config/proddash.json), which supplies the defaults:
|
||
|
||
| Key | Meaning |
|
||
| --- | --- |
|
||
| `port` | Port ProdDash serves on (default `24500`). The macOS launcher writes this one, so the port it is set to holds however ProdDash is started. |
|
||
| `adminPin` | The admin PIN, as a scrypt hash. **Set it in the admin page's Admin PIN section**, not by hand — 6 digits, and only the hash is stored. Absent means admin is open to anyone on the network. |
|
||
| `adminPasscode` | An older free-text passcode. Still honoured, so an existing install keeps working; new installs should use `adminPin`. |
|
||
| `forge` | Where updates and module installs are fetched from. Absent or `"github"` uses GitHub; a Gitea or Forgejo base URL such as `"http://10.3.8.90:3000"` uses a server of your own. Set it per machine in the data directory, alongside `repo`. |
|
||
| `theme` | The dashboard palette: `booth` (default), `harbor`, `graphite`, `ember`, `daylight`, `midnight`, `moss`, `plum`, `sepia`, `blackout`, `paper` or `iron`. The admin page's **Theme** section writes this one — see [Themes](#themes). |
|
||
|
||
**Locked out?** Delete `adminPin` from the data directory's `proddash.json`
|
||
and restart, or start the server with `PRODDASH_PASSCODE` set — that passcode
|
||
overrides the PIN and always gets you in.
|
||
|
||
Only `/admin` and the admin API are behind the PIN. Dashboards stay open, so a
|
||
wall display needs no PIN, and it is unrelated to a module's own PIN (Slack's
|
||
4–8 digit send PIN, say), which guards that module's actions only.
|
||
|
||
**Updating from your own git server.** ProdDash fetches one `tar.gz` of the
|
||
branch — nothing else — so any Gitea or Forgejo works. Point `forge` at its
|
||
base URL and `repo` at the owner/repo there:
|
||
|
||
```json
|
||
{ "forge": "http://10.3.8.90:3000", "repo": "michael/ProdDash", "branch": "main" }
|
||
```
|
||
|
||
Gitea spells an archive URL differently from GitHub, which is the only thing
|
||
that changes; the tarball itself is identical, commit hash included, so update
|
||
detection and module installs behave exactly as before. Make the repository
|
||
readable without signing in, or ProdDash cannot fetch it.
|
||
|
||
Environment variables override both files: `PORT`, `PRODDASH_PASSCODE`, and
|
||
`PRODDASH_FORGE`, and `PRODDASH_DATA_DIR` to put the data directory somewhere else (a relative path
|
||
is taken from the app folder; `PRODDASH_DATA_DIR=config` restores the old
|
||
in-repo location). If the default directory can't be created, the server says
|
||
so and falls back to the app's `config/` folder.
|
||
|
||
**Upgrading from an earlier version:** the first start copies an existing
|
||
`config/modules.json` and `config/layouts/` from the app folder into the data
|
||
directory, so nothing has to be re-entered. The old files are left in place and
|
||
ignored from then on.
|
||
|
||
### Themes
|
||
|
||
The whole dashboard — every tile, on every browser — draws with one palette,
|
||
chosen in the admin page's **Theme** section and switched live: pick a swatch
|
||
and open dashboards change without a reload. The section starts folded —
|
||
a building picks its palette once — and holds twelve presets:
|
||
|
||
| | |
|
||
|---|---|
|
||
| **Booth** | the default dark, mint accent |
|
||
| **Harbor** | deep navy, cyan |
|
||
| **Graphite** | neutral charcoal, electric blue |
|
||
| **Ember** | warm broadcast black, orange |
|
||
| **Daylight** | a cool light palette, for a front-of-house desk in daytime |
|
||
| **Midnight** | indigo night, violet |
|
||
| **Moss** | forest floor, lime |
|
||
| **Plum** | aubergine, purple |
|
||
| **Sepia** | warm brown room, cool teal — Ember's arrangement reversed |
|
||
| **Blackout** | true black, cyan, for a dark room where any glow carries |
|
||
| **Paper** | the warm light one, indigo accent |
|
||
| **Iron** | no colour of its own: a near-white accent, so the only hues on screen are a module's own and the red and amber that mean something |
|
||
|
||
Each keeps the status colours apart — the accent is never near the danger red
|
||
or the warning amber — and muted text readable in a dark booth. The choice is
|
||
stored as `theme` in the data directory's `proddash.json` (table above), so
|
||
it holds across restarts and updates; a browser remembers the last theme it
|
||
saw and applies it before the server answers, so nothing flashes. Modules
|
||
style with the shell's colour variables, which is why they follow; a colour
|
||
set on a tile itself (a PCO Plan header colour, say) is deliberately kept.
|
||
|
||
## Installing modules and updating
|
||
|
||
Everything below happens in the **admin page**; ProdDash talks to its git
|
||
repository — `michael/ProdDash` on the church's own Gitea at
|
||
`chronicles-ab.waterschurch.org:3000`, branch `main`. Change it with `forge`,
|
||
`repo` and `branch` in `proddash.json`, or the `PRODDASH_FORGE` and
|
||
`PRODDASH_REPO` variables (`owner/repo#branch`). It downloads one small archive of the branch, at most
|
||
every few minutes, only while the admin page is open or for the update check
|
||
below. Nothing is installed without someone clicking.
|
||
|
||
- **Available modules** lists modules in the repo that aren't installed
|
||
here. **Install** downloads the module into the data directory's `modules/`
|
||
and starts it right away — nothing is written into the app folder, and no
|
||
restart is needed. Modules that ship with your copy of ProdDash are
|
||
"bundled"; ones you installed are "installed"; if both exist, the newer
|
||
version runs.
|
||
- **Uninstall** on a module card removes an installed module's files. For a
|
||
bundled module (its folder belongs to the app) it hides the module from
|
||
every dashboard instead; it reappears under Available modules and can be
|
||
brought back without downloading. Settings are kept either way.
|
||
- **Module updates**: when the repo has a newer version of an installed
|
||
module, its card shows **Update to vX**.
|
||
- **Version requirements**: every module declares the ProdDash versions it
|
||
needs (`"proddash": ">=1.1.0"` in its manifest). A module that needs a
|
||
newer shell is listed but not loaded, and cannot be installed until
|
||
ProdDash is updated.
|
||
- **ProdDash updates**: the admin page checks the repo shortly after start
|
||
and every six hours, and shows a banner when `main` has a newer version
|
||
(or new commits). **Update now** brings the app folder up to date and
|
||
restarts the server; dashboards reconnect on their own. A git checkout is
|
||
updated with `git pull --ff-only` (only when it is on the repo branch with
|
||
no local changes — otherwise the banner says why and you update with git).
|
||
A plain folder (no `.git`) has the repo archive unpacked over it; files
|
||
removed upstream are left behind but harmless. Settings and layouts are
|
||
never touched — nothing inside the data directory is written, even on an
|
||
install where that directory *is* the app's own `config/`. **What changed**
|
||
opens the commit comparison on the forge.
|
||
- **If an update goes wrong**: the download has to parse before anything is
|
||
swapped in, so a truncated or mangled `server.js` is refused with the
|
||
reason and the running copy is left exactly as it was. Past that, the copy
|
||
being replaced is set aside as `app.previous` next to the app folder, and
|
||
dropped once the new version has stayed up for ninety seconds — so that
|
||
folder existing means "the last update has not proven itself yet". The
|
||
macOS app watches for a version that will not stay up (four stops in two
|
||
minutes) and puts the old one back by itself, once, with a notification.
|
||
Running some other way, the way back is a rename: `mv app.previous app`.
|
||
- **Restarting**: after an update the server exits with code 75 and starts
|
||
its own replacement, which waits for the port to free up. If you supervise
|
||
ProdDash with your own launcher, set `PRODDASH_LAUNCHER=1` so the server
|
||
just exits and your launcher restarts it on exit code 75.
|
||
`PRODDASH_NO_REMOTE_CHECK=1` turns off the periodic check (the admin page
|
||
still checks when opened).
|
||
|
||
## Using the dashboard
|
||
|
||
- **+** — add any enabled module; multiple tiles of the same module are
|
||
fine (e.g. two transcripts filtered to different channels).
|
||
- Drag a tile by its **title bar** to move it; **✕** removes it.
|
||
- **Resize from any edge or bottom corner**, the way a window resizes:
|
||
nothing is drawn on a tile to mark the spots — the cursor over an edge is
|
||
what says it can be dragged. An edge moves only itself, so the tile grows
|
||
or shrinks into the space beside it while the opposite edge stays put,
|
||
stopping at a neighbour or at the module's own minimum size. Two
|
||
exceptions, both of which move a *shared* edge instead: with another tile
|
||
directly below, the bottom edge trades rows between the two so the column
|
||
keeps its total height; and where two tiles meet side by side, the seam
|
||
between them trades width (macOS split-view style), showing itself only
|
||
while you drag it.
|
||
- **Fullscreen** (the ⛶ button) is a viewing mode: the menu bar hides (its
|
||
green lip at the top-left brings it back, minus the Admin button), the
|
||
occupied columns stretch to the full width, and every column runs to the
|
||
bottom of the screen — the last tile in each column is stretched to end
|
||
there. A short tile like the clock is never stretched into a ribbon: under
|
||
another tile it slides to the bottom edge and the tile above grows into the
|
||
gap; alone in its columns it stays as it is. Nothing is saved — the layout
|
||
reverts when you leave fullscreen. A layout deeper than the screen keeps
|
||
its normal row height and scrolls.
|
||
- A tile's actions (menus, text size, clear…) live in its **title bar**.
|
||
- **Auto-hide bars** (the button left of ⛶) folds every title bar away, for
|
||
a windowed screen that should read like a wall display. A folded bar is
|
||
not a gone bar: point at the tile's top edge and it slides back in over
|
||
the content, where its buttons click and its width drags the tile exactly
|
||
as before — so a dashboard can be arranged with every bar out of the way.
|
||
The switch is off on a fresh dashboard and saves with the layout —
|
||
including a named layout on the server, so a booth loads its screen with
|
||
the bars already tucked away. Fullscreen folds every bar regardless, and
|
||
the same hover brings one back there too.
|
||
- The **pin** beside a tile's title keeps *that* tile's bar showing while
|
||
auto-hide is on — for the one or two tiles whose controls are wanted at
|
||
a glance. It turns the accent colour when it is on and saves with the
|
||
layout. Fullscreen still folds a pinned bar with the rest.
|
||
- The menu bar's own handle is the **ProdDash
|
||
name itself**, wrapped in a translucent green pill at the left — click it
|
||
to hide the bar; the slim green lip it leaves at the top-left edge brings
|
||
the bar back. It stays at the left so it never sits on top of a tile's
|
||
own tab.
|
||
- The **gear** on a tile holds per-tile settings (filters, text size,
|
||
display options) — these belong to this browser's layout, not the server.
|
||
- The **grid icon** — save the current arrangement to the server under a
|
||
name, load a named layout (it becomes a local copy — browsers are never
|
||
linked), or reset this browser's dashboard. Loading says what it did —
|
||
*Loaded "FOH": 9 tiles, 2 not shown — Service, #ops aren't offered right
|
||
now* — with **Undo** in the message to get back the arrangement you had.
|
||
A tile whose module or picker entry isn't offered at the moment keeps its
|
||
place and shows nothing, then appears when it is; it is never replaced by
|
||
the module's default tile. Saving over an existing name asks first, and
|
||
the server keeps the copy it replaced: **Restore previous** in Admin →
|
||
Saved layouts swaps it back (and swapping again undoes that). The menu's
|
||
**Grid** stepper sets how many columns this browser cuts the board into —
|
||
8 to 24, 12 by default. Finer lets tiles be smaller so more fit across (a
|
||
4K wall), coarser suits a small screen read close up; row height follows,
|
||
so tiles keep their shape and simply change size. Going coarser, a tile
|
||
that no longer fits where it was moves to the first free spot rather than
|
||
onto another tile. It saves with the layout, so a named layout comes back
|
||
at the density it was arranged on.
|
||
- Every tile has a **status dot**: green = live, amber = connecting,
|
||
red = upstream unreachable (hover it for details). Tiles reconnect
|
||
automatically.
|
||
|
||
## Included modules
|
||
|
||
- **ProdCom** — live transcript stream with in-place updates of in-progress
|
||
speech, channel colors, per-tile channel visibility toggles and group
|
||
presets, timestamps, text size, a top-down / bottom-up direction toggle
|
||
and jump-to-latest in the title bar.
|
||
Admin config: ProdCom server (host : port), API key.
|
||
Tile settings: channel icons, direction and timestamps.
|
||
- **ProPresenter** — what's live and what's next, with slide progress,
|
||
driven by the shared `propresenter-core` client (ProPresenter 7/20).
|
||
The + picker offers two tiles: **Now / Next**, the full card, and **Now**,
|
||
the live item alone with the gradient filling the tile edge to edge — a
|
||
wall panel that says one thing.
|
||
Admin config: ProPresenter endpoint (host : port), password.
|
||
Tile settings: a **Show** group of switches for the playlist name, live
|
||
status, NOW and NEXT labels, slide count, progress bar, the next list and
|
||
its item tags, plus tidy titles.
|
||
- **Timers** — live ProPresenter timers (countdown, countdown-to-time,
|
||
elapsed) and LTC timecode as auto-sized cards. The + picker lists every
|
||
discovered timer (and the LTC card) as its own tile, or add "All timers"
|
||
and pick a selection via the tile's "Timers ▾" menu; overruns are
|
||
unmistakably red. Read-only — no start/stop controls. Admin config is in
|
||
three groups: **ProPresenter** (endpoint host : port, API password),
|
||
**LTC Timecode** (the listener switch, and — when it's on — the audio
|
||
device and channel), and **Display** (Timer font — Default or Monospace,
|
||
which keeps the digits from shifting as the numbers change).
|
||
|
||
ProPresenter exposes no timecode over any API, so LTC is decoded from the
|
||
audio signal, behind the admin **LTC listener** switch (off ⇒ no LTC tile
|
||
in the picker at all). On, the module captures the selected audio input
|
||
device/channel on the ProdDash machine — a native `ltc-capture` helper,
|
||
compiled on first use — and decodes the SMPTE frames in-process:
|
||
running/stopped/no-signal, the timecode counting frame-accurately in real
|
||
time, and the frame rate (incl. 29.97 drop-frame). macOS note: the
|
||
ProdDash server needs microphone permission — launch it from a logged-in
|
||
session (Start ProdDash.command) and approve the one-time prompt; a server
|
||
started over SSH or by bare launchd silently captures zeros. (Engine
|
||
self-test: `node modules/propresenter-timers/ltc.selftest.js`.)
|
||
- **PCO Plan** — the order of service from Planning Center Services: every
|
||
plan item with its scheduled start time, length, type, song key,
|
||
description and item notes. When the ProPresenter module is running it
|
||
follows that module's live item, highlights the matching plan item, dims
|
||
what's done and shows the elapsed vs planned runtime and how far
|
||
ahead/behind the service is. Admin config is the connection: Planning
|
||
Center credentials (Personal Access Token app ID + secret, or an OAuth
|
||
token), the service type (a list grouped by your Planning Center folders)
|
||
and an optional pinned plan. Everything about the display is per tile
|
||
(gear menu): the top-bar text (placeholders such as
|
||
`{series} • {part} | {serviceType} • {date}`), colors, text size, and a
|
||
show/hide checklist for every detail.
|
||
- **Slack** — live transcripts of this location's Slack channels, plus
|
||
PIN-protected sending as the location. The + picker lists one transcript
|
||
tile per configured channel, titled `#channel`, and two send surfaces (one
|
||
of each per dashboard): **Send message**, a free-text box with the admin's
|
||
quick emojis one press away, and **Quick replies**, a grid of preset
|
||
buttons. Both post to the channel of whichever transcript was clicked last
|
||
— its bottom edge lights up in the accent color — and to the first
|
||
configured channel until one is clicked. Messages arrive with avatars,
|
||
names and times, Slack's inline formatting and the workspace's custom
|
||
emoji; a right-click on one opens the reaction menu, and text size and
|
||
jump-to-latest sit in the title bar. Nothing leaves the machine until the
|
||
PIN is entered: one PIN unlocks every Slack tile on that dashboard page
|
||
until the auto-lock idles out, a reload asks again, and the lock button on
|
||
a send tile's title bar ends it early.
|
||
Admin config is in four groups: **Slack account** (the location account's
|
||
User OAuth Token — `xoxp-…`, so messages are literally from the campus —
|
||
or a bot token plus "Send as"), **Channels** (the joined channels to
|
||
follow, one transcript tile each), **Transcript** (refresh interval,
|
||
messages kept, thread replies) and **Sending** (the switch on the group's
|
||
title row, a 4–8 digit PIN, auto-lock minutes, quick replies one per line
|
||
as `Label | Message` — add `| #channel` to bind one to a channel — and up
|
||
to eight quick emojis).
|
||
Tile settings: avatars, timestamps, names and channel notices.
|
||
- **Band Lineup** — the backstage stage board: everyone scheduled on the
|
||
band, vocal and production teams for the upcoming Planning Center plan,
|
||
placed into their mic slot with photo, name and mic/pack label; birthdays
|
||
in the service week get confetti. Guests and MCs not on the plan are pinned
|
||
per slot from the title bar's Edit button. Admin config: Planning Center
|
||
credentials, service type and plan, the teams to include, the stage slots
|
||
(one mic/pack label per line, in stage order), default columns, and a
|
||
Google Drive folder for manual photos. Tile settings: names, mic labels,
|
||
parallax, and the birthday confetti with its intensity.
|
||
- **Clock** — a big booth clock; also the smallest possible module and the
|
||
reference for module authors.
|
||
|
||
To develop modules against mocks instead of real gear:
|
||
`node tools/prodcom-mock.js` (ProdCom on `127.0.0.1:24480`),
|
||
`node band-lineup-display/tmp/pp-mock.js 1599` (ProPresenter slides on
|
||
`127.0.0.1:1599`), `node tools/pp-timers-mock.js 1600` (ProPresenter
|
||
timers + timecode on `127.0.0.1:1600`), `node tools/pco-mock.js`
|
||
(Planning Center Services on `127.0.0.1:24700` — run ProdDash with
|
||
`PCO_PLAN_API_BASE=http://127.0.0.1:24700`) and
|
||
`node tools/slack-mock.js 24716` (the Slack Web API on `127.0.0.1:24716` —
|
||
run ProdDash with `SLACK_API_BASE=http://127.0.0.1:24716/api`) and
|
||
`node tools/pco-band-mock.js 24710` (Planning Center for the Band Lineup
|
||
board — a plan, its team members, birthdays and photos — run ProdDash with
|
||
`BAND_LINEUP_API_BASE=http://127.0.0.1:24710`). See the testing section of
|
||
the [module guide](docs/MODULE-GUIDE.md).
|
||
|
||
## Install as a desktop web app
|
||
|
||
Open the page in a browser first, then:
|
||
|
||
- **macOS — Safari**: File → **Add to Dock…**
|
||
- **macOS/Windows — Chrome**: click the install icon in the address bar, or
|
||
⋮ menu → Cast, save and share → **Install page as app…**
|
||
- **Windows — Edge**: ⋯ menu → Apps → **Install this site as an app**
|
||
|
||
Note: Chrome only offers the one-click PWA install on `http://localhost` (a
|
||
"secure context"). When opening from *another* machine over plain HTTP, use
|
||
Edge's "Install this site as an app" or Chrome's ⋮ → Cast, save and share →
|
||
"Create shortcut…" (check "Open as window") — same result: a dock/taskbar
|
||
app in its own window.
|
||
|
||
## Repository layout
|
||
|
||
```
|
||
ProdDash/
|
||
├── server.js shell server: static files, module discovery/mounting,
|
||
│ admin + layouts APIs
|
||
├── public/ dashboard shell client (index.html, shell.js, style.css)
|
||
│ └── admin/ admin page client
|
||
├── modules/ installed modules, one folder each
|
||
│ ├── clock/
|
||
│ ├── prodcom-transcript/
|
||
│ └── propresenter-now-next/ (carries its own copy of propresenter-core)
|
||
├── package.json name + version (what the update check compares)
|
||
├── config/ proddash.json defaults (runtime state lives in the
|
||
│ per-machine data directory — see "Configure")
|
||
├── docs/MODULE-GUIDE.md how to build and install a module
|
||
├── tools/prodcom-mock.js mock ProdCom API for development
|
||
├── prodcom-listener/ reference fork — read-only, not served
|
||
└── band-lineup-display/ reference fork — read-only, not served
|
||
```
|
||
|
||
The two reference folders are snapshots of the apps ProdDash grew out of;
|
||
they are not wired into anything (but `band-lineup-display/tmp/pp-mock.js`
|
||
is handy for testing).
|