Commit Graph
3 Commits
Author SHA1 Message Date
topkoa 938ddded60 feat(panes)!: dress the renderer's pane window, don't create it
Follows the core change: a pane is now the plugin's REAL panel element,
moved into the pop-out window and still running the plugin's own code
(got-feedback/feedback#928).

That forces one thing here, and it is worth being loud about it:

  WE MUST NOT CREATE THE PANE WINDOW.

To move a live DOM node into another window, the renderer needs a handle on
that window's document. A BrowserWindow we construct in the main process
gives it no such handle. So the renderer opens the window itself with
window.open(), Electron's setWindowOpenHandler turns that into a real
BrowserWindow anyway, and we recognise it in did-create-window by the frame
name the renderer gave it (`fbpane-<paneId>`) and attach the OS behaviour:
remembered bounds, off the taskbar, minimize-to-tray, listed in the tray.

Create the window here instead and the whole feature collapses back into
"reimplement the panel in the pop-out and sync it over IPC" — which is
exactly what we just deleted.

The IPC surface shrinks to two channels, because main never creates or
destroys a pane window and never looks inside one:

  pane:sync    renderer → main   the registry, so the tray can list panes
  pane:toggle  main → renderer   the tray asking for a pane; only the
                                 renderer knows what opening one means (it
                                 might belong in the dock, and its element
                                 lives there)

Gone: pane:open, pane:close, pane:focus, pane:setAlwaysOnTop, pane:closed.
The renderer holds the WindowProxy for a window it opened, so it already
knows when the user closes it — and it has to, because its element is inside
and must be brought home.

Signed-off-by: topkoa <topkoa@gmail.com>
2026-07-12 18:14:49 -04:00
topkoa 39251f3d12 feat(panes): pane pop-out windows + the system tray
feedBack core gained a pane system (window.feedBack.panes): live UI — a
mixer, a camera rig, a readout — authored once and hostable anywhere. In
a browser it pops out via window.open(). This gives it the desktop
treatment: a real BrowserWindow that remembers where you put it, can
float above everything, and lives in the system tray.

First Tray in the app. It exists because a popped-out pane is furniture:
you want it out of the way while you play and back instantly when you
don't — not hunted for behind the main window, and not cluttering the
taskbar. Minimizing a pane sends it to the tray; the tray menu lists
every pane with a checkmark and toggles it.

## The renderer owns the truth

Main never looks inside a pane. It owns OS surfaces only — windows and
their geometry — and learns what panes exist from a `pane:sync` push. The
tray menu is a VIEW of the renderer's registry, not a second copy of it.
When the tray toggles a pane it has no window for, it asks the renderer,
because only the renderer knows what opening one means (it might belong
in the dock).

## The pane window loads OUR origin, and that is load-bearing

A pane is fed over BroadcastChannel, which only reaches windows in the
same Chromium instance and origin. Push the URL anywhere else and the
pane opens looking perfect and never updates again. So `pane:open`
validates the URL against the same origin predicate the navigation guards
use (makeRendererOriginPredicate) and refuses anything else outright —
which also means we can never open arbitrary web content with the full
preload bridge attached. It is the same reason main.ts's
setWindowOpenHandler answers same-origin URLs with `allow` rather than
`deny` + openExternal.

## Details that bite

- sanitizeWindowBounds hard-floored at the MAIN window's 800x600. A 380x560
  pane restored through it would be silently inflated threefold. It now takes
  a WindowSizing; the main window passes its old values as the default, so
  every existing call site and the existing test are byte-for-byte unchanged.
- Pane geometry lives in the DESKTOP config, not the renderer's localStorage
  — localStorage is shared with the pane windows themselves (same origin), so
  a second writer there would race. setDesktopConfig merges shallowly, so
  paneWindows is read-modify-written or one pane's save would drop the rest.
- Pane windows are destroyed when the main window closes. Without the
  renderer there is nothing on the other end of their channel, so they would
  sit showing a frozen playhead forever — and a pane HIDDEN in the tray is
  still an open window, which would stop `window-all-closed` from ever firing
  and leave the app running as an invisible process.
- Geometry is persisted on move/resize, not only on close: a pane window can
  outlive the app in a crash, and the entire point is that you never place it
  twice.
- Electron's 'minimize' is not cancellable here (the listener takes no event),
  so a pane hides right after minimizing rather than preventing it. The window
  is skipTaskbar, so there is no animation to see.
- The tray icon is copied to dist/main/ by build:ts, the same trick
  splash.html and spinner.json use — so __dirname resolves it identically in
  dev and inside a packaged asar, with no app.isPackaged branch and nothing
  added to electron-builder's extraResources. An unreadable icon logs and
  skips the tray rather than creating an invisible one whose menu no one can
  ever reach.

Needs the matching core change (got-feedback/feedback#928), which registers
the `desktop` host when this bridge is present and falls back to a browser
pop-up when it isn't. An older core simply never calls these channels.

Signed-off-by: topkoa <topkoa@gmail.com>
2026-07-12 17:22:44 -04:00
topkoaandClaude Opus 4.8 9d424ece9d Rebrand splash/loading dialog Slopsmith -> fee[dB]ack
The startup splash window and its initial status message still showed
the old "Slopsmith" name. Update the brand label, the static status
line, and the JS fallback in splash.html, plus the main-process startup
status snapshot that overrides it, to the new "fee[dB]ack" branding.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: topkoa <topkoa@gmail.com>
2026-06-19 08:56:26 -04:00