# pearcord-ui-flow Pearcord desktop UI IPC dispatch and automated flow scenarios for Bare smokes. Mirrors `apps/pearcord` pear-pipe message handling. ## Mission Centralize the `msg.type` → `platform.*` routing table used by the Pear UI sidecar so smoke tests and tooling can drive the full platform surface without duplicating hundreds of if-branches in the app entrypoint. ## When to use / not **Use when:** - Running Bare smokes that send JSON IPC lines to a headless platform instance. - Adding a new UI IPC type—implement handler here and call from app shell. **Do not use when:** - You need platform business logic — implement on `PearcordPlatform` in `pearcord-platform`. - You build non-Pear UIs — import platform directly. ## Public API | Export | Role | |--------|------| | `dispatchUiMessage(platform, msg, hooks)` | Returns `true` if `msg.type` handled | | `fromBase64(str)` | Buffer for attachment/emoji bytes | | `normalizeEmojiImage(image)` | Maps `dataBase64` → `data` buffer | ### Hooks ```javascript { send: (obj) => {}, // reply events to UI pipe pushState: () => {} // refresh serialized state } ``` Returns `false` for unknown types (caller may log). ## Coverage (grouped) | Area | Example `msg.type` values | |------|-------------------------| | Onboarding | `register`, `create-guild`, `join-invite`, `select-guild` | | Automation | `set-automod-config`, `upsert-automation-hook`, `export-automation-manifest`, `dry-run-automation-hook`, schedule/export/dashboard IPC | | Bots | `create-bot-install-token`, `install-bot-token`, `register-slash-commands` | | Announcements | `set-announcement-crosspost`, `set-announcement-hub-publish`, `subscribe-remote-announcement-hub`, `follow-announcement` | | Boosts / stickers | `contribute-guild-boost`, `upsert-guild-sticker`, `upsert-sticker-pack` | | Profile | `update-profile-cosmetics`, `set-profile-banner-image`, step-up PIN types | | Voice / screen | `join-voice`, `join-voice-listen`, `join-stage`, `voice-capture-chunk`, `display-capture-frame` | | Notifications | `mark-all-notifications-read` (emits `mark-all-notifications-read-cleared` + `composer-focus-channel`, v0.8.407), `mark-notification-read` (calls `platform.markNotificationRead`, emits `mark-notification-read-cleared` + `notificationChannelId`, v0.8.407), `open-notification` (marks read via `markNotificationRead`, emits `composer-focus-channel`, v0.8.407), `mark-channel-read` (emits `mark-channel-read-cleared` + `threadChannelId` for threads, v0.8.287), `refresh-notifications` (v0.8.236) | | Workers | `export-integration-worker-script`, `run-worker-install-trial`, `announce-worker-release` | | Moderation / channels | `ban-member`, `upsert-channel-overwrite`, forum/thread types | | Search | `update-search`, `refresh-search`, `remove-search-recent` (v0.8.140) | Uses `USER_STATUS` from `pearcord-platform` for presence IPC. ## Dependencies - `pearcord-platform` — `PearcordPlatform` instance (git dependency in package.json) - `pearcord-qr` — optional QR SVG for device pair codes - `b4a` — base64 decode in Bare ## Platform integration ```javascript // apps/pearcord/index.js pattern: const { dispatchUiMessage } = require('pearcord-ui-flow') async function onPipeLine(line) { const msg = JSON.parse(line) const handled = await dispatchUiMessage(platform, msg, { send, pushState }) if (!handled) send({ type: 'error', message: `unknown type ${msg.type}` }) } ``` ## P2P surface None directly—all mesh behavior delegated to platform methods invoked by dispatch. ## Tests Primary consumer: Bare smoke suites under `apps/pearcord` that spawn platform + pipe JSON lines. Adding handlers here keeps smokes and production UI in sync. ## Code example ```javascript const { dispatchUiMessage, fromBase64 } = require('pearcord-ui-flow') const replies = [] await dispatchUiMessage( platform, { type: 'dry-run-automod', content: 'discord.gg/test' }, { send: (o) => replies.push(o), pushState: () => {} } ) // replies[0].type === 'automod-dry-run' ``` ## Related modules - `pearcord-platform` — implementation target for every branch - All feature modules — reached indirectly through platform **v0.8.174:** `update-guild-settings` IPC forwards `nsfwGateEnabled` to `platform.updateGuildSettings`. ## Composer focus channel (v0.8.406+) After `create-thread`, `create-forum-post`, `mark-all-notifications-read`, or `open-notification`, dispatch emits a sidecar event (not a full IPC round-trip from the UI): ```javascript send({ type: 'composer-focus-channel', channelId: thread.id }) **v0.8.420 (Phase 457):** `create-channel` selects the new channel and emits `composer-focus-channel` (see [CHANNELS.md](../../docs/CHANNELS.md)). // mark-all: channelId = platform.activeChannelId // open-notification: channelId = result.channelId || msg.channelId || platform.activeChannelId ``` The renderer handles `composer-focus-channel` and calls `requestComposerAutofocusForChannel(channelId)` so the message box focuses once the target channel is active. Smokes: `test:composer-autofocus-after-thread-create`, `test:composer-autofocus-after-mark-all-notif`, `test:composer-autofocus-after-notif-navigate`. See [NOTIFICATIONS.md](../../docs/NOTIFICATIONS.md), [MESSAGE_THREADS.md](../../docs/MESSAGE_THREADS.md), and [FORUM_CHANNELS.md](../../docs/FORUM_CHANNELS.md). ## Maintenance When adding IPC in the Pear app, add the same `msg.type` branch here first (or simultaneously) so `dispatchUiMessage` remains the single routing table.