What lives where

apps/tablet/src/tablet.ts is the tablet. One BUTTONS table defines which buttons exist, their names in each language, the command string each sends, and its driving state key. Adding a button requires an entry there, a <pk-button id> in index.html, and a case wherever the host handles it. main.ts is the wiring that reads this table; you should not need to open it to change what the tablet contains.

apps/tablet/src/prompts.ts is what the tablet asks the visitor — the shipped five-star rating, and where to add or remove one. See Asking the visitor something below; a fork that wants none can delete the file and its one call in main.ts.

config.json defines only how to reach the hosturl, token, language, adminHoldMs. One rule to remember: config is the connection, source is the tablet.

{ "language": "cs", "adminHoldMs": 3000 }

There is deliberately no host address in there. When the host serves the page (the normal case), the tablet derives ws://<same-host>:9010/link from its own URL, with that port hard-coded. Set url when the host is reached via a tunnel or public address, or whenever you change Port away from 9010.

The token field requires care. The tablet reads a token from two places, fragment first:

Source Use it when

#token=… on the page URL (tokenFromFragment in main.ts)

the normal case. The host serves this page, and therefore config.json, to anyone asking on the port they just scanned. Because a fragment is never sent to a server, the token reaches the page without being fetchable from it. tablet.bat builds the URL from its TABLET_TOKEN

token in config.json

you serve this page yourself from an origin the public cannot fetch

Putting the token in config.json on a host-served page publishes it. The deployment where you would reach for a token is exactly the deployment where the file is reachable.

Layout

packages/kiosk-tablet/src/
  protocol.ts   message types and the state-patch shape - read this to write a host
  core.ts       KioskClient - socket, reconnect, staleness, state mirror, makeThrottle
                (`retryNow()` is public: call it when your tablet knows the operator has arrived)
  ui.ts         <pk-button> <pk-slider>, presentational only
  index.ts      the root barrel - re-exports all three; the one entry with side effects
apps/tablet/
  index.html    markup and branding
  public/config.json  how to reach the host - copied into dist/ verbatim
  src/tablet.ts  WHAT the tablet is: the BUTTONS table, the artwork map, every visible string
  src/main.ts   HOW it is wired: bindButtons(), the corners, the admin menu, the offline fog
  src/gestures.ts  onTap / onLongPress - the pointer rules a captive touch surface needs
  src/app.css   theme and layout
  public/icons/ the artwork this deployment happens to use