Conversation
renderer.js still wired a click handler to btn-add-app after index.html
stopped rendering it. $() returned null, addEventListener threw, and every
listener registered after that line never attached: the workspace dropdown,
settings, add-service, task manager, service modal, modal close buttons,
keyboard shortcuts and all main-process handlers. No visible error.
The renderer half of the earlier sidebar change was lost in a cherry-pick
while the HTML and CSS halves landed, which is what left the mismatch.
- Restore the inline add tile and drag affordance in renderer.js
- Add a test asserting every $('id') in renderer.js exists in index.html,
plus that index.html's scripts exist and are covered by build.files
- Ad-hoc sign the macOS app in an afterPack hook. Without it the bundle kept
Electron's signature, which no longer matched after renaming, so macOS
reported "Panebox is damaged and can't be opened"
- Never overwrite an unreadable config: quarantine it, restore from a backup
written on each good load
Reload worked but looked broken: reloading a page that renders identically gives no feedback, so a working button was indistinguishable from a dead one. Back and forward looked clickable with no history and simply did nothing, which read the same way. - Reload spins for as long as the load actually takes, and stops the load if clicked mid-flight, like a browser - Back and forward disable when there is nowhere to go, driven by the webview's own canGoBack/canGoForward - State refreshes on navigation, on load finish, and when switching service
.drag-handle spanned the full window at z-index 101, so an invisible -webkit-app-region: drag strip sat on top of every toolbar control. Mouse clicks landed on the drag region and the buttons never fired. Cmd+R still worked because menu accelerators never touch the DOM, which is what made it look like reload specifically was broken. Automated clicks did not catch this: element.click() dispatches straight to the node and skips hit-testing entirely. Verified this time with a real mouse event through the input pipeline. The strip only exists to make the traffic-light area draggable, so it is now limited to the sidebar's width. #topbar already sets -webkit-app-region: drag with no-drag on the control groups, so window dragging is unaffected.
- Cmd/Ctrl+B collapses the sidebar, like an editor. Persisted, and also in
View -> Toggle Sidebar.
- Reordering now uses pointer events instead of HTML5 drag-and-drop. The
native API tested fine and did nothing under a real mouse; synthetic
DragEvents dispatch straight to the node and prove very little. The tab now
moves under the cursor rather than only showing a drop line.
- Escape closes the workspace popover as well as modals and the find bar.
- Adding a service that already exists numbers it ("Gemini 2"). A second copy
is a legitimate way to run two accounts, but two identical rows in the
service list are impossible to tell apart.
Verified with real Input.dispatchMouseEvent / dispatchKeyEvent rather than
element.click(), which is what let the earlier drag bug through.
The Services list in Settings said "Drag services in the sidebar to reorder them" — it told you to go elsewhere and did nothing itself, which is the obvious place to try first when the list is right in front of you. Each row now has a grip handle and reorders in place, sharing the pointer based helper the sidebar uses. Because that list always shows every service, its order is written straight through. Verified with real mouse drags in both lists.
Most services showed a letter avatar, so the catalog read as a grid of placeholder squares. Brand marks now come from simple-icons and are inlined into icons.js by tools/make-brand-icons.js. Generated rather than hand-written: approximated SVG paths look subtly wrong in a way that is easy to miss in review and obvious on screen. simple-icons is a devDependency and is not shipped, so the app still has no icon dependency and still never fetches an icon over the network. 47 of 52 services now carry an official mark. Slack, LinkedIn, Gmail and ChatGPT keep the hand-checked paths already in the project, since those were withdrawn from simple-icons after trademark requests. Teams, Skype, Outlook, Fastmail and Canva have no official path anywhere and keep letter avatars.
Adding a service accepted almost any text. "not a url" became "https://not a url", was percent-encoded, and landed a permanently broken entry named "Not%20a%20url". "file:///etc/passwd" became "https://file:///etc/passwd" — harmless, but still dead weight the user has to work out how to delete. normalizeServiceUrl now requires something that is actually a host: a real TLD, localhost, or an IP literal. Non-http schemes are refused rather than prefixed. "localhost:3000" still works, because a colon followed by digits is host:port, not a scheme — self-hosting is a legitimate use of this app. Names derive properly too: notion.so -> Notion, an IP keeps host:port instead of becoming "192". Rejection now explains itself and shows an example, rather than saying "That does not look like a valid URL."
Adding a URL by hand gave a letter avatar, then whatever favicon the page advertised first — usually 16x16, which looks like mush at 26px. Two sources now, in order: - If the URL belongs to a service already in the catalog, use its official brand mark. Typing notion.so or app.slack.com now looks identical to picking it from the list. - Otherwise read the page for something sharper: apple-touch-icon, manifest icons at 192/512, or sized link tags, largest wins. SVG beats every bitmap since it scales. All read from the page itself; nothing asked of a third party. Catalog matching weighs the path, not just the host. Gmail and Google Chat share mail.google.com, so host-only matching gave every Gmail URL the Google Chat icon.
Cmd/Ctrl+Shift+S lays the current workspace out as a grid, so a group doubles as a dashboard: make an "AI" group and see ChatGPT, Claude, Gemini and Perplexity side by side. - Columns follow the shape video-call grids settle on, keeping tiles near square: 2 up to 4 panes, 3 up to 9, 4 up to 16. Capped at 12, since each live pane costs memory. - Click a pane's expand button to spotlight it: that pane takes the room and the rest become a thumbnail strip underneath. - Escape steps back out — first the spotlight, then the grid. - Panes in the grid are exempt from sleeping while it is open. Each webview now sits in a pane wrapper built at creation time. Wrappers are never moved afterwards: relocating a <webview> in the DOM tears down its process and reloads the page. Sessions are untouched. Panes are only shown and hidden, and nothing here clears a partition, so logins survive entering and leaving the grid.
The grid took whatever the workspace held, which is wrong when you want two
things side by side and nothing else. It is now an explicit selection:
- Click a sidebar icon while the grid is open to add or remove that service.
Icons in the grid are outlined, and their tooltip says which action a click
performs.
- Every pane has a close button that drops it from the grid.
- A "+" tile opens a picker listing everything not on screen, with "Add all N
from <group>" at the top for the dashboard case.
- Opens with two panes rather than the whole workspace, and the selection is
remembered between launches.
Also fixes headers being invisible in loaded panes: the original
`webview { position: absolute; inset: 0 }` still applied inside a pane, so a
painted page covered its own header. Unloaded panes showed theirs, which made
it look intermittent.
- Drag a sidebar icon onto the grid to add it as a pane. The grid outlines itself and says "Drop to add a pane" while you are over it. Reuses the pointer-drag already used for reordering: leaving the sidebar switches the gesture from reorder to add. - Double-click a pane header to expand it; a third click in the same burst opens that service on its own and leaves the grid. Also fixes the grid wasting a cell. The "+" tile was a grid item, so two services shared a row with a large empty square beside them and each pane got half the height it should have. It now floats over the bottom-right corner, and columns are computed from the panes alone — two panes go from 600x389 to 600x786. The float needed `#view-container .pane-add` to outweigh `#view-container.split .pane`, which had been forcing it back into the flow.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes the crash that disabled most of the UI in 2.1.0, plus split view, brand icons, and toolbar fixes.