| Files | Last commit | Last update |
|---|---|---|
Measure the storage of a hosted server against its plan (#6264) * Measure the storage of a hosted server against its plan The account server sends the plan storage of a hosted server in the host list. The desktop storage card and Server settings > Storage use it. They do not count the 22 GB base image, and the card shows when less than 10 GB of the plan is free. Agents on a hosted server get one short line about the plan storage. * Correct the release notes for the plan storage warning * Read the hosted state of a host from its plan, with one lookup | 12 hours ago | |
Add a server on another computer with one command (#6174) * Add a server on another computer with one command The add server menu has "New server on another computer". The dialog shows a one-time command that installs OpenBot on a Linux computer or VPS and signs it in to the account. The command works once, for 15 minutes. An agent in a chat can also install OpenBot on a server over SSH. The user approves the command, and a card in the chat shows when the server connects. The claim goes to bash on standard input, not as an argument or a sudo variable, because sudo logs both. * Fix the review findings of the server install - Offer "Set up with an agent" only on this computer or a server that the user owns. - Refuse the install tool before the approval card when the owner is not signed in. - Find the server of a card that showed after the server signed in, by its name. - Do not say Copied when the clipboard refuses the text. - Stop the install before the download on a server that is signed in. - Remove a stale note about the claim in the process arguments. * Import the server install watch type by name, so that knip sees its use * Match the install card's server only by the command's name When the command gives a name, another new server does not show in the card. The card records the known servers only after the server list loads. * Keep the server install tool inside the agent context budget Remove the tool's line from the developer instructions and shorten its texts. The catalog and the visible tool list still grow by the new tool, so their baseline grows by the measured bytes. | 14 hours ago | |
Update Solid to 2.0.0-rc.18 and Vite to 8 (#3237) * Update compatible dependencies Apply the Dependabot compatible-updates group (#3159) on current main with a fresh lockfile. The fresh install removes the nested @types/node 26 and the second Vite 8 that the Dependabot lockfile added. Move the dmg-builder, expo, expo-router and expo-splash-screen patches to the new versions. The old patches apply without change. Keep these packages at their current versions: - @biomejs/biome 2.5.13: 2.6 adds rules with false positives and formats Markdown and YAML. - lucide-solid 1.33.0: the Solid 2 patch does not apply to 1.61. - storybook-solidjs-vite 10.7.1: 10.7.4 types fail 115 stories. - react and react-dom 19.3.0, react-native-gesture-handler 3.3, react-native-keyboard-controller 1.22.7: Expo SDK 58 and React Native 0.88 pin these versions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep jsdom 30.0.1 and @paper-design/shaders 0.0.80 jsdom 30.1.3 keeps focus on an iframe after blur(), so the ChatVisual activation test fails. The site artwork hash includes the shaders version, so 0.0.82 marks all committed article images as out of date. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Update Solid to 2.0.0-rc.18 and Vite to 8 - solid-js, @solidjs/signals and @solidjs/web 2.0.0-rc.18; @solidjs/vite-plugin 3.0.0-next.51; @solidjs/testing-library 1.0.0-rc.2. - @tanstack/solid-start and @tanstack/solid-router 2.0.0-rc.9. - vite 8.3.5 and electron-vite 6.0.0, which the Solid plugin needs. - Remove the @solidjs/signals and @solidjs/web patches: rc.18 has their fixes. - Read props in the compute function of four effects. A compiled conditional prop creates a memo, and rc.18 throws PRIMITIVE_IN_EFFECT_CALLBACK when an effect callback creates one. - Read visiblePresentation untracked in the dynamic island effect that writes it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Read Marketplace agent props through memos The window passes agents and activeAgentId as conditional props. Each read makes a memo, and the skill reads run in effect callbacks, so the Skills tab halted the renderer with PRIMITIVE_IN_EFFECT_CALLBACK. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Register Kobalte layers when the ref is set Under Solid 2.0.0-rc.18 a dialog's onSettled can run before its ref is set. DismissableLayer then never added the layer, so Escape did not close dialogs and modals did not block outside pointer events. The patch registers the layer in an effect keyed on the ref. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 14 days ago | |
refactor(renderer): give each domain a directory under features/ (#241) * build: scan the whole renderer for the UI foundation budgets Both budgets were anchored to a directory rather than to the renderer. The manual composite ARIA role count walked `components/` alone and the colour literal count walked `styles/` plus the entry, so either one would have gone quiet the moment a component or a stylesheet moved next to the feature it belongs to - passing by going blind, not by being met. Widened both to `filesUnder(rendererRoot)`, which is what every other rule in this script already does. The composite budget still reads 0/0; verified by adding a `role="dialog"` to a root-level pane and watching the check go red for that reason, then restoring it. Also widened the `noDescendingSpecificity` override to the paths the sidebar and conversation stylesheets are moving to. Both the old and the new paths are listed while the move is in flight so every commit in between stays green; the old ones come out with the last stylesheet commit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(renderer): gather the sidebar domain into features/sidebar The sidebar was six places at once: the domain context at the renderer root, four pure-logic modules beside it, the prop-assembling pane, the entry component in `components/`, and a 26-file part directory with its own `stores/`. Two `stores/` directories for one domain, in two layers of the tree. Reading "fix the pin ordering" meant finding all six. This is the first `features/<domain>/` directory: the context, the logic, the view, the parts and the tests in one place, flat except for `stores/`, which is the shape `components/sidebar/` and `components/conversation/` already had. No barrel, deep relative imports, tests co-located - all unchanged. `sidebar.tsx` becomes `sidebar-context.tsx`. It has to: `sidebar.tsx` and `Sidebar.tsx` coexisted only because they were in different directories, and collapsing them into one collides on case-insensitive APFS. That would have failed the macOS `Check` job alone and looked unrelated to the move. Pure move - no behaviour, no assertion and no test count changed. 37 sidebar tests and 51 App tests pass; `typecheck:renderer` covers every rewritten import, since the renderer has no path aliases. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(renderer): give the sidebar its types apart from its view Nine of the eleven modules that imported `Sidebar.tsx` wanted a type, not the component: the drag engine, the filtering, the scope, the agent-state projection and all four stores name `SidebarProps`, `SidebarAgentState` or `ResolvedPinnedItem` and render nothing. The entry component had become the feature's type module by accident, so every piece of pure logic pulled in the whole view to borrow a name. `sidebar-types.ts` holds the three types; `Sidebar.tsx` imports `SidebarProps` like everyone else and is now nine lines of imports and the markup. The two remaining edges to it - `WorkspaceSidebar` and the test - are the ones that actually render a sidebar. Only visible once both halves were in one directory, which is what the move was for. No behaviour change: 37 tests pass unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(renderer): gather the updates and conversation domains Same shape as `features/sidebar`. Conversation was the widest of the clusters: the context and three pure-logic modules at the renderer root, two panes, six entry components and the controller in `components/`, and a 74-file part directory with its own `stores/` - 92 files that only a reader who already knew where to look could assemble. `conversation.tsx` becomes `conversation-context.tsx`. It is the second and last of the two case collisions in the tree - `conversation.tsx` against `Conversation.tsx` - and would have failed the macOS `Check` job alone. Imports were repaired by resolving each relative specifier against the file's old directory and re-emitting it against the new one, so a file that moved and a file that only referenced a moved file go through the same rule. `typecheck:renderer` is the check on that, and it is exhaustive here because the renderer has no path aliases. Pure move: 257 tests across the migrated features pass unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(renderer): gather the remaining ten domains into features/ agents, servers, dynamic-island, account, settings, onboarding, browser, team, remote-desktop and computer-use, each the same shape as the first three: the domain context renamed `<domain>-context`, its pure logic, its panes, its components and its tests in one directory. One deviation from the manifest, and the reason: `direct-messages.tsx` was listed under `team`, but its pane (`WorkspaceDirectConversation`) and its view (`DirectConversation`) both belong to conversation and already moved there. Splitting a context from the two things that render it is the failure this refactor exists to fix, so it went to `features/conversation` as `direct-messages-context.tsx`. `presence` and the WebRTC transport stay in `team`; they answer who is online, not what was said. Cross-cutting modules stay flat by the same test: `navigation`, `layout`, `turns`, `providers`, `data`, `simple-context`, `scope-lifetime` and the `app-*` wiring are read by every domain and belong to none. `components/` keeps `ui`, the shared avatars and drag/scroll factories, and the four widgets no single feature owns. `lazy-views.ts` was rewritten with everything else - its dynamic `import()` chunk boundaries are unchanged, only the paths. 674 renderer tests across 72 files pass, unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs: write down the feature-directory layout Change rule 7 told you to put a domain context at the root of `src/renderer/src`, which is now the one place it must not go. The location clause is the only part that changes: lifetime placement, `use*()` reaching downwards, the `import type` upward edge and one store per concern are what the rule protects and are untouched. `src/renderer/AGENTS.md` gains the layout section it never had, including the `<domain>-context.tsx` rule and its actual reason - a case-insensitive APFS collision fails `Check` on macos-14 while every ubuntu job stays green - and the test for when a cluster earns a directory, with the caveat that the test is about helpers, not contexts. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(renderer): move conversation.css into features/conversation The @import keeps its position in styles.css, which is the whole content of the cascade order, so the sheet still loads between join-server-dialog and settings-modal exactly as before. The stale `styles/conversation.css` entry leaves the biome override that turns off `noDescendingSpecificity` for it; the new path was added there ahead of the move so no commit in this series was ever missing it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(renderer): move the settings stylesheets into features/settings settings-modal.css is also named in `scripts/design-tokens.test.ts` as the one place allowed to bind an --openbot-* token in a narrower scope, twice - once for the renderer and once for the public web app, whose /app-preview route imports the renderer sheet wholesale. Both were repointed; the test reads the file, so a stale path there fails loudly rather than silently allowing nothing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(renderer): move the account stylesheets into features/account auth.css and account-update-island.css keep their positions in the import chain - auth.css above the shell, the island partial below it - so the login screen still loads before app-shell rules and the update island after them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(renderer): move onboarding.css into features/onboarding Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(renderer): move first-agent-setup.css into features/agents It sits beside FirstAgentSetup.tsx, which moved there with the agents domain. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(renderer): move the dynamic island stylesheets into their feature Both load before base.css and stay in that order relative to each other: the surface sheet styles the separate island window and overrides the shared island rules it follows. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(renderer): move the server stylesheets into features/servers server-settings-modal.css still loads after settings-modal.css, which it builds on rather than repeats. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(renderer): move computer-use.css into features/computer-use Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(renderer): keep app-shell.css whole, and write down why The plan had the sidebar and remote-desktop rules leaving app-shell.css for their feature directories. They cannot, and the reason is worth a comment rather than a second attempt: the tail of the file is a cross-domain theme layer. Fourteen grouped rules assign the palette to selectors from every domain in one declaration, four of those selectors are declared in conversation.css rather than here, and .sidebar-icon-button:hover is declared twice at equal specificity - once early, once in that theme block. Moving the early one into features/sidebar/sidebar.css puts it after the theme block and flips the hover background from #313131 to rgba(255,255,255,0.08). tsc, vitest and check:ui all stay green through that. So app-shell.css keeps its name and place, gains a header saying what would have to happen first (lift the theme layer into its own last-loading sheet), and keeps its noDescendingSpecificity exemption. The biome override drops the features/sidebar/sidebar.css path it was holding open for a file that will not exist. The nine stylesheets that did move were verified by resolving the whole @import chain at the commit before this series and again now: the flattened cascade is byte-identical. Swapping two @import lines makes that comparison fail, so it is checking what it claims to. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs: note the app-shell.css exception where the layout is described Both documents now say every feature keeps its own stylesheet, which is true of nine of them and not of the sidebar or remote-desktop. The exception belongs where someone reads the rule, not only in the file it applies to. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(renderer): repoint the window entries the feature move broke `browser-pip.tsx`, `browser-pip-controls.tsx` and `team-webrtc.ts` are not just modules, they are the entry points of three renderer windows, named as path strings inside `src/renderer/*.html`. Moving them into `features/browser` and `features/team` left all three .html files pointing at files that no longer exist, and `electron-vite build` failed on the first one. My audit of the path-coupled tooling read `electron.vite.config.ts`, saw that it names only .html files, and concluded it needed no change. It does not - the .html files do, and I never opened them. Nothing else in the repository looks at that hop: tsc does not read HTML, vitest never loads it, and the renderer imports these entries from nowhere, so typecheck, lint, check:ui and 674 renderer tests all passed while the build was broken. It surfaced as a red `Check` job on macos-14 four minutes later, which is a slow way to learn a filename. So `scripts/renderer-entries.test.ts` now asserts, in 5 ms, that every window the config names exists, that each one names at least one entry module, and that every entry module resolves on disk. Watched it fail: restoring the old `/src/browser-pip.tsx` turns it red naming that page and that path, and putting the new path back turns it green. The entries stay in their features. A picture-in-picture window is browser code and the WebRTC page is team code; the .html file is the configuration that points at them, and configuration is what should follow the code. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: undo the auth-api churn and repoint two paths in the gallery Three findings from the review, all correct. The migration tool re-emitted every relative specifier it touched, and normalizing a path drops a trailing `/index`. That is invisible where the module moved anyway, but in `apps/auth-api` nothing moved: it rewrote eight imports in `routeTree.gen.ts`, a file whose own header says manual changes get overwritten, and one in `worker-handler.ts`. Neither has anything to do with the renderer, so both files go back to what main has. `Conversation.stories.tsx` carries two paths as message content. One is a command a reader is meant to copy and run, and it named a test file this PR moved; the other is the file-link example, which demonstrated a link to a file that no longer exists. Both now point into `features/conversation`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(renderer): give conversation its types beside its logic, not in the view `ConversationView.tsx` declared `ConversationProps`, `ComposerDraft`, `ConversationTarget`, `MediaPreview`, `SidebarFilePreview` and `RightPanelMode`, and fifteen modules in the feature imported them from there: eleven stores, the scope, the controller, the draft logic and the timeline. None of them renders anything, so each was reaching through a component to borrow a name. It also closed a loop. `ConversationView.tsx` imports `conversation-scope.ts` for its value, and `conversation-scope.ts` imported `ConversationView` back for its types. That is a cycle `noImportCycles` never reported, because one direction is `import type` and it only follows value edges. `conversation-types.ts` holds the six declarations and imports nothing from the feature, which breaks the loop rather than hiding it. This is the same shape as `sidebar-types.ts` and the last outstanding item of the migration plan. Pure move: no declaration changed, no test assertion changed. Renderer only. `typecheck:renderer`, `check:ui` and the 207 conversation tests are green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs: point the import-cycle example at where those two modules now live CONTRIBUTING.md names two modules as the worked example of breaking a cycle by extracting a third module, and this PR moved both. `components/` no longer has a `conversation-controller-context.tsx` at all, so a reader following the example gets a dead path, and `team-typing.ts` is no longer at the renderer root, so the bare name points at the placement the PR just argued against. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> | 2 months ago | |
refactor(renderer): give each domain a directory under features/ (#241) * build: scan the whole renderer for the UI foundation budgets Both budgets were anchored to a directory rather than to the renderer. The manual composite ARIA role count walked `components/` alone and the colour literal count walked `styles/` plus the entry, so either one would have gone quiet the moment a component or a stylesheet moved next to the feature it belongs to - passing by going blind, not by being met. Widened both to `filesUnder(rendererRoot)`, which is what every other rule in this script already does. The composite budget still reads 0/0; verified by adding a `role="dialog"` to a root-level pane and watching the check go red for that reason, then restoring it. Also widened the `noDescendingSpecificity` override to the paths the sidebar and conversation stylesheets are moving to. Both the old and the new paths are listed while the move is in flight so every commit in between stays green; the old ones come out with the last stylesheet commit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(renderer): gather the sidebar domain into features/sidebar The sidebar was six places at once: the domain context at the renderer root, four pure-logic modules beside it, the prop-assembling pane, the entry component in `components/`, and a 26-file part directory with its own `stores/`. Two `stores/` directories for one domain, in two layers of the tree. Reading "fix the pin ordering" meant finding all six. This is the first `features/<domain>/` directory: the context, the logic, the view, the parts and the tests in one place, flat except for `stores/`, which is the shape `components/sidebar/` and `components/conversation/` already had. No barrel, deep relative imports, tests co-located - all unchanged. `sidebar.tsx` becomes `sidebar-context.tsx`. It has to: `sidebar.tsx` and `Sidebar.tsx` coexisted only because they were in different directories, and collapsing them into one collides on case-insensitive APFS. That would have failed the macOS `Check` job alone and looked unrelated to the move. Pure move - no behaviour, no assertion and no test count changed. 37 sidebar tests and 51 App tests pass; `typecheck:renderer` covers every rewritten import, since the renderer has no path aliases. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(renderer): give the sidebar its types apart from its view Nine of the eleven modules that imported `Sidebar.tsx` wanted a type, not the component: the drag engine, the filtering, the scope, the agent-state projection and all four stores name `SidebarProps`, `SidebarAgentState` or `ResolvedPinnedItem` and render nothing. The entry component had become the feature's type module by accident, so every piece of pure logic pulled in the whole view to borrow a name. `sidebar-types.ts` holds the three types; `Sidebar.tsx` imports `SidebarProps` like everyone else and is now nine lines of imports and the markup. The two remaining edges to it - `WorkspaceSidebar` and the test - are the ones that actually render a sidebar. Only visible once both halves were in one directory, which is what the move was for. No behaviour change: 37 tests pass unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(renderer): gather the updates and conversation domains Same shape as `features/sidebar`. Conversation was the widest of the clusters: the context and three pure-logic modules at the renderer root, two panes, six entry components and the controller in `components/`, and a 74-file part directory with its own `stores/` - 92 files that only a reader who already knew where to look could assemble. `conversation.tsx` becomes `conversation-context.tsx`. It is the second and last of the two case collisions in the tree - `conversation.tsx` against `Conversation.tsx` - and would have failed the macOS `Check` job alone. Imports were repaired by resolving each relative specifier against the file's old directory and re-emitting it against the new one, so a file that moved and a file that only referenced a moved file go through the same rule. `typecheck:renderer` is the check on that, and it is exhaustive here because the renderer has no path aliases. Pure move: 257 tests across the migrated features pass unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(renderer): gather the remaining ten domains into features/ agents, servers, dynamic-island, account, settings, onboarding, browser, team, remote-desktop and computer-use, each the same shape as the first three: the domain context renamed `<domain>-context`, its pure logic, its panes, its components and its tests in one directory. One deviation from the manifest, and the reason: `direct-messages.tsx` was listed under `team`, but its pane (`WorkspaceDirectConversation`) and its view (`DirectConversation`) both belong to conversation and already moved there. Splitting a context from the two things that render it is the failure this refactor exists to fix, so it went to `features/conversation` as `direct-messages-context.tsx`. `presence` and the WebRTC transport stay in `team`; they answer who is online, not what was said. Cross-cutting modules stay flat by the same test: `navigation`, `layout`, `turns`, `providers`, `data`, `simple-context`, `scope-lifetime` and the `app-*` wiring are read by every domain and belong to none. `components/` keeps `ui`, the shared avatars and drag/scroll factories, and the four widgets no single feature owns. `lazy-views.ts` was rewritten with everything else - its dynamic `import()` chunk boundaries are unchanged, only the paths. 674 renderer tests across 72 files pass, unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs: write down the feature-directory layout Change rule 7 told you to put a domain context at the root of `src/renderer/src`, which is now the one place it must not go. The location clause is the only part that changes: lifetime placement, `use*()` reaching downwards, the `import type` upward edge and one store per concern are what the rule protects and are untouched. `src/renderer/AGENTS.md` gains the layout section it never had, including the `<domain>-context.tsx` rule and its actual reason - a case-insensitive APFS collision fails `Check` on macos-14 while every ubuntu job stays green - and the test for when a cluster earns a directory, with the caveat that the test is about helpers, not contexts. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(renderer): move conversation.css into features/conversation The @import keeps its position in styles.css, which is the whole content of the cascade order, so the sheet still loads between join-server-dialog and settings-modal exactly as before. The stale `styles/conversation.css` entry leaves the biome override that turns off `noDescendingSpecificity` for it; the new path was added there ahead of the move so no commit in this series was ever missing it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(renderer): move the settings stylesheets into features/settings settings-modal.css is also named in `scripts/design-tokens.test.ts` as the one place allowed to bind an --openbot-* token in a narrower scope, twice - once for the renderer and once for the public web app, whose /app-preview route imports the renderer sheet wholesale. Both were repointed; the test reads the file, so a stale path there fails loudly rather than silently allowing nothing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(renderer): move the account stylesheets into features/account auth.css and account-update-island.css keep their positions in the import chain - auth.css above the shell, the island partial below it - so the login screen still loads before app-shell rules and the update island after them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(renderer): move onboarding.css into features/onboarding Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(renderer): move first-agent-setup.css into features/agents It sits beside FirstAgentSetup.tsx, which moved there with the agents domain. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(renderer): move the dynamic island stylesheets into their feature Both load before base.css and stay in that order relative to each other: the surface sheet styles the separate island window and overrides the shared island rules it follows. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(renderer): move the server stylesheets into features/servers server-settings-modal.css still loads after settings-modal.css, which it builds on rather than repeats. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(renderer): move computer-use.css into features/computer-use Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(renderer): keep app-shell.css whole, and write down why The plan had the sidebar and remote-desktop rules leaving app-shell.css for their feature directories. They cannot, and the reason is worth a comment rather than a second attempt: the tail of the file is a cross-domain theme layer. Fourteen grouped rules assign the palette to selectors from every domain in one declaration, four of those selectors are declared in conversation.css rather than here, and .sidebar-icon-button:hover is declared twice at equal specificity - once early, once in that theme block. Moving the early one into features/sidebar/sidebar.css puts it after the theme block and flips the hover background from #313131 to rgba(255,255,255,0.08). tsc, vitest and check:ui all stay green through that. So app-shell.css keeps its name and place, gains a header saying what would have to happen first (lift the theme layer into its own last-loading sheet), and keeps its noDescendingSpecificity exemption. The biome override drops the features/sidebar/sidebar.css path it was holding open for a file that will not exist. The nine stylesheets that did move were verified by resolving the whole @import chain at the commit before this series and again now: the flattened cascade is byte-identical. Swapping two @import lines makes that comparison fail, so it is checking what it claims to. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs: note the app-shell.css exception where the layout is described Both documents now say every feature keeps its own stylesheet, which is true of nine of them and not of the sidebar or remote-desktop. The exception belongs where someone reads the rule, not only in the file it applies to. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(renderer): repoint the window entries the feature move broke `browser-pip.tsx`, `browser-pip-controls.tsx` and `team-webrtc.ts` are not just modules, they are the entry points of three renderer windows, named as path strings inside `src/renderer/*.html`. Moving them into `features/browser` and `features/team` left all three .html files pointing at files that no longer exist, and `electron-vite build` failed on the first one. My audit of the path-coupled tooling read `electron.vite.config.ts`, saw that it names only .html files, and concluded it needed no change. It does not - the .html files do, and I never opened them. Nothing else in the repository looks at that hop: tsc does not read HTML, vitest never loads it, and the renderer imports these entries from nowhere, so typecheck, lint, check:ui and 674 renderer tests all passed while the build was broken. It surfaced as a red `Check` job on macos-14 four minutes later, which is a slow way to learn a filename. So `scripts/renderer-entries.test.ts` now asserts, in 5 ms, that every window the config names exists, that each one names at least one entry module, and that every entry module resolves on disk. Watched it fail: restoring the old `/src/browser-pip.tsx` turns it red naming that page and that path, and putting the new path back turns it green. The entries stay in their features. A picture-in-picture window is browser code and the WebRTC page is team code; the .html file is the configuration that points at them, and configuration is what should follow the code. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: undo the auth-api churn and repoint two paths in the gallery Three findings from the review, all correct. The migration tool re-emitted every relative specifier it touched, and normalizing a path drops a trailing `/index`. That is invisible where the module moved anyway, but in `apps/auth-api` nothing moved: it rewrote eight imports in `routeTree.gen.ts`, a file whose own header says manual changes get overwritten, and one in `worker-handler.ts`. Neither has anything to do with the renderer, so both files go back to what main has. `Conversation.stories.tsx` carries two paths as message content. One is a command a reader is meant to copy and run, and it named a test file this PR moved; the other is the file-link example, which demonstrated a link to a file that no longer exists. Both now point into `features/conversation`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(renderer): give conversation its types beside its logic, not in the view `ConversationView.tsx` declared `ConversationProps`, `ComposerDraft`, `ConversationTarget`, `MediaPreview`, `SidebarFilePreview` and `RightPanelMode`, and fifteen modules in the feature imported them from there: eleven stores, the scope, the controller, the draft logic and the timeline. None of them renders anything, so each was reaching through a component to borrow a name. It also closed a loop. `ConversationView.tsx` imports `conversation-scope.ts` for its value, and `conversation-scope.ts` imported `ConversationView` back for its types. That is a cycle `noImportCycles` never reported, because one direction is `import type` and it only follows value edges. `conversation-types.ts` holds the six declarations and imports nothing from the feature, which breaks the loop rather than hiding it. This is the same shape as `sidebar-types.ts` and the last outstanding item of the migration plan. Pure move: no declaration changed, no test assertion changed. Renderer only. `typecheck:renderer`, `check:ui` and the 207 conversation tests are green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs: point the import-cycle example at where those two modules now live CONTRIBUTING.md names two modules as the worked example of breaking a cycle by extracting a third module, and this PR moved both. `components/` no longer has a `conversation-controller-context.tsx` at all, so a reader following the example gets a dead path, and `team-typing.ts` is no longer at the renderer root, so the bare name points at the placement the PR just argued against. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> | 2 months ago | |
Fix the Dynamic Island count limit and cut renderer and main memory (#1191) * Fix the Dynamic Island count limit and load App only in the main window - Clamp the unread and remaining counts of a presentation to DYNAMIC_ISLAND_MAX_COUNT. Main rejected a presentation with more than 10,000 unread replies, so the island stopped updating. - Load App with a dynamic import only in the main window. Each Dynamic Island window loaded the full app: 23 MB heap, now 11.9 MB. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep at most 16 idle chats in the main process memory The main process kept each chat that it read, and marking a chat read reads it. A chat of 2,000 messages takes about 1 MB. Now the cache drops the least recently used idle chat when it holds more than 16. A chat with a running turn always stays. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Give the helper windows their own entry, and drop only chats that were read Main sends the first runtime snapshot on did-finish-load, and that event does not wait for a dynamic import. The async App could miss it, and then a pending approval did not show after a reload. Now the main window imports App statically again, and the helper windows load helper.html, which does not import App. The chat cache now drops only chats that setSnapshot stored and that no turn code took since. ensureSnapshot gives its object to code that can change it across an await, so a copy read again could lose that change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Correct the release note for the chat cache limit Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Load a dropped chat again when a queued message is edited A chat opened with unread messages is cached by markRead, and the cache can drop it later. A queue edit then did not publish the chat, so an open chat kept the old text. Now the edit loads the chat again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Send updates to chats that the cache limit dropped Main reads a dropped chat again when a queue, delivery or result change must reach it, as when it was in memory. A chat that main never loaded stays unloaded. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 1 month ago | |
Standardize OpenBot product branding | 2 months ago | |
refactor(renderer): give each domain a directory under features/ (#241) * build: scan the whole renderer for the UI foundation budgets Both budgets were anchored to a directory rather than to the renderer. The manual composite ARIA role count walked `components/` alone and the colour literal count walked `styles/` plus the entry, so either one would have gone quiet the moment a component or a stylesheet moved next to the feature it belongs to - passing by going blind, not by being met. Widened both to `filesUnder(rendererRoot)`, which is what every other rule in this script already does. The composite budget still reads 0/0; verified by adding a `role="dialog"` to a root-level pane and watching the check go red for that reason, then restoring it. Also widened the `noDescendingSpecificity` override to the paths the sidebar and conversation stylesheets are moving to. Both the old and the new paths are listed while the move is in flight so every commit in between stays green; the old ones come out with the last stylesheet commit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(renderer): gather the sidebar domain into features/sidebar The sidebar was six places at once: the domain context at the renderer root, four pure-logic modules beside it, the prop-assembling pane, the entry component in `components/`, and a 26-file part directory with its own `stores/`. Two `stores/` directories for one domain, in two layers of the tree. Reading "fix the pin ordering" meant finding all six. This is the first `features/<domain>/` directory: the context, the logic, the view, the parts and the tests in one place, flat except for `stores/`, which is the shape `components/sidebar/` and `components/conversation/` already had. No barrel, deep relative imports, tests co-located - all unchanged. `sidebar.tsx` becomes `sidebar-context.tsx`. It has to: `sidebar.tsx` and `Sidebar.tsx` coexisted only because they were in different directories, and collapsing them into one collides on case-insensitive APFS. That would have failed the macOS `Check` job alone and looked unrelated to the move. Pure move - no behaviour, no assertion and no test count changed. 37 sidebar tests and 51 App tests pass; `typecheck:renderer` covers every rewritten import, since the renderer has no path aliases. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(renderer): give the sidebar its types apart from its view Nine of the eleven modules that imported `Sidebar.tsx` wanted a type, not the component: the drag engine, the filtering, the scope, the agent-state projection and all four stores name `SidebarProps`, `SidebarAgentState` or `ResolvedPinnedItem` and render nothing. The entry component had become the feature's type module by accident, so every piece of pure logic pulled in the whole view to borrow a name. `sidebar-types.ts` holds the three types; `Sidebar.tsx` imports `SidebarProps` like everyone else and is now nine lines of imports and the markup. The two remaining edges to it - `WorkspaceSidebar` and the test - are the ones that actually render a sidebar. Only visible once both halves were in one directory, which is what the move was for. No behaviour change: 37 tests pass unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(renderer): gather the updates and conversation domains Same shape as `features/sidebar`. Conversation was the widest of the clusters: the context and three pure-logic modules at the renderer root, two panes, six entry components and the controller in `components/`, and a 74-file part directory with its own `stores/` - 92 files that only a reader who already knew where to look could assemble. `conversation.tsx` becomes `conversation-context.tsx`. It is the second and last of the two case collisions in the tree - `conversation.tsx` against `Conversation.tsx` - and would have failed the macOS `Check` job alone. Imports were repaired by resolving each relative specifier against the file's old directory and re-emitting it against the new one, so a file that moved and a file that only referenced a moved file go through the same rule. `typecheck:renderer` is the check on that, and it is exhaustive here because the renderer has no path aliases. Pure move: 257 tests across the migrated features pass unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(renderer): gather the remaining ten domains into features/ agents, servers, dynamic-island, account, settings, onboarding, browser, team, remote-desktop and computer-use, each the same shape as the first three: the domain context renamed `<domain>-context`, its pure logic, its panes, its components and its tests in one directory. One deviation from the manifest, and the reason: `direct-messages.tsx` was listed under `team`, but its pane (`WorkspaceDirectConversation`) and its view (`DirectConversation`) both belong to conversation and already moved there. Splitting a context from the two things that render it is the failure this refactor exists to fix, so it went to `features/conversation` as `direct-messages-context.tsx`. `presence` and the WebRTC transport stay in `team`; they answer who is online, not what was said. Cross-cutting modules stay flat by the same test: `navigation`, `layout`, `turns`, `providers`, `data`, `simple-context`, `scope-lifetime` and the `app-*` wiring are read by every domain and belong to none. `components/` keeps `ui`, the shared avatars and drag/scroll factories, and the four widgets no single feature owns. `lazy-views.ts` was rewritten with everything else - its dynamic `import()` chunk boundaries are unchanged, only the paths. 674 renderer tests across 72 files pass, unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs: write down the feature-directory layout Change rule 7 told you to put a domain context at the root of `src/renderer/src`, which is now the one place it must not go. The location clause is the only part that changes: lifetime placement, `use*()` reaching downwards, the `import type` upward edge and one store per concern are what the rule protects and are untouched. `src/renderer/AGENTS.md` gains the layout section it never had, including the `<domain>-context.tsx` rule and its actual reason - a case-insensitive APFS collision fails `Check` on macos-14 while every ubuntu job stays green - and the test for when a cluster earns a directory, with the caveat that the test is about helpers, not contexts. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(renderer): move conversation.css into features/conversation The @import keeps its position in styles.css, which is the whole content of the cascade order, so the sheet still loads between join-server-dialog and settings-modal exactly as before. The stale `styles/conversation.css` entry leaves the biome override that turns off `noDescendingSpecificity` for it; the new path was added there ahead of the move so no commit in this series was ever missing it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(renderer): move the settings stylesheets into features/settings settings-modal.css is also named in `scripts/design-tokens.test.ts` as the one place allowed to bind an --openbot-* token in a narrower scope, twice - once for the renderer and once for the public web app, whose /app-preview route imports the renderer sheet wholesale. Both were repointed; the test reads the file, so a stale path there fails loudly rather than silently allowing nothing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(renderer): move the account stylesheets into features/account auth.css and account-update-island.css keep their positions in the import chain - auth.css above the shell, the island partial below it - so the login screen still loads before app-shell rules and the update island after them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(renderer): move onboarding.css into features/onboarding Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(renderer): move first-agent-setup.css into features/agents It sits beside FirstAgentSetup.tsx, which moved there with the agents domain. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(renderer): move the dynamic island stylesheets into their feature Both load before base.css and stay in that order relative to each other: the surface sheet styles the separate island window and overrides the shared island rules it follows. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(renderer): move the server stylesheets into features/servers server-settings-modal.css still loads after settings-modal.css, which it builds on rather than repeats. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(renderer): move computer-use.css into features/computer-use Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(renderer): keep app-shell.css whole, and write down why The plan had the sidebar and remote-desktop rules leaving app-shell.css for their feature directories. They cannot, and the reason is worth a comment rather than a second attempt: the tail of the file is a cross-domain theme layer. Fourteen grouped rules assign the palette to selectors from every domain in one declaration, four of those selectors are declared in conversation.css rather than here, and .sidebar-icon-button:hover is declared twice at equal specificity - once early, once in that theme block. Moving the early one into features/sidebar/sidebar.css puts it after the theme block and flips the hover background from #313131 to rgba(255,255,255,0.08). tsc, vitest and check:ui all stay green through that. So app-shell.css keeps its name and place, gains a header saying what would have to happen first (lift the theme layer into its own last-loading sheet), and keeps its noDescendingSpecificity exemption. The biome override drops the features/sidebar/sidebar.css path it was holding open for a file that will not exist. The nine stylesheets that did move were verified by resolving the whole @import chain at the commit before this series and again now: the flattened cascade is byte-identical. Swapping two @import lines makes that comparison fail, so it is checking what it claims to. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs: note the app-shell.css exception where the layout is described Both documents now say every feature keeps its own stylesheet, which is true of nine of them and not of the sidebar or remote-desktop. The exception belongs where someone reads the rule, not only in the file it applies to. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(renderer): repoint the window entries the feature move broke `browser-pip.tsx`, `browser-pip-controls.tsx` and `team-webrtc.ts` are not just modules, they are the entry points of three renderer windows, named as path strings inside `src/renderer/*.html`. Moving them into `features/browser` and `features/team` left all three .html files pointing at files that no longer exist, and `electron-vite build` failed on the first one. My audit of the path-coupled tooling read `electron.vite.config.ts`, saw that it names only .html files, and concluded it needed no change. It does not - the .html files do, and I never opened them. Nothing else in the repository looks at that hop: tsc does not read HTML, vitest never loads it, and the renderer imports these entries from nowhere, so typecheck, lint, check:ui and 674 renderer tests all passed while the build was broken. It surfaced as a red `Check` job on macos-14 four minutes later, which is a slow way to learn a filename. So `scripts/renderer-entries.test.ts` now asserts, in 5 ms, that every window the config names exists, that each one names at least one entry module, and that every entry module resolves on disk. Watched it fail: restoring the old `/src/browser-pip.tsx` turns it red naming that page and that path, and putting the new path back turns it green. The entries stay in their features. A picture-in-picture window is browser code and the WebRTC page is team code; the .html file is the configuration that points at them, and configuration is what should follow the code. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: undo the auth-api churn and repoint two paths in the gallery Three findings from the review, all correct. The migration tool re-emitted every relative specifier it touched, and normalizing a path drops a trailing `/index`. That is invisible where the module moved anyway, but in `apps/auth-api` nothing moved: it rewrote eight imports in `routeTree.gen.ts`, a file whose own header says manual changes get overwritten, and one in `worker-handler.ts`. Neither has anything to do with the renderer, so both files go back to what main has. `Conversation.stories.tsx` carries two paths as message content. One is a command a reader is meant to copy and run, and it named a test file this PR moved; the other is the file-link example, which demonstrated a link to a file that no longer exists. Both now point into `features/conversation`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(renderer): give conversation its types beside its logic, not in the view `ConversationView.tsx` declared `ConversationProps`, `ComposerDraft`, `ConversationTarget`, `MediaPreview`, `SidebarFilePreview` and `RightPanelMode`, and fifteen modules in the feature imported them from there: eleven stores, the scope, the controller, the draft logic and the timeline. None of them renders anything, so each was reaching through a component to borrow a name. It also closed a loop. `ConversationView.tsx` imports `conversation-scope.ts` for its value, and `conversation-scope.ts` imported `ConversationView` back for its types. That is a cycle `noImportCycles` never reported, because one direction is `import type` and it only follows value edges. `conversation-types.ts` holds the six declarations and imports nothing from the feature, which breaks the loop rather than hiding it. This is the same shape as `sidebar-types.ts` and the last outstanding item of the migration plan. Pure move: no declaration changed, no test assertion changed. Renderer only. `typecheck:renderer`, `check:ui` and the 207 conversation tests are green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs: point the import-cycle example at where those two modules now live CONTRIBUTING.md names two modules as the worked example of breaking a cycle by extracting a third module, and this PR moved both. `components/` no longer has a `conversation-controller-context.tsx` at all, so a reader following the example gets a dead path, and `team-typing.ts` is no longer at the renderer root, so the bare name points at the placement the PR just argued against. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> | 2 months ago |
| Files | Last commit | Last update |
|---|---|---|
| 12 hours ago | ||
| 14 hours ago | ||
| 14 days ago | ||
| 2 months ago | ||
| 2 months ago | ||
| 1 month ago | ||
| 2 months ago | ||
| 2 months ago |