From 896eb41cdc795bca2c023c26e39c008a29abc0c6 Mon Sep 17 00:00:00 2001
From: meetsu <96637888+klNuno@users.noreply.github.com>
Date: Sun, 18 Oct 2026 20:27:05 +0000
Subject: [PATCH 1/3] fix(ui): open Plugins on this PC and keep the project
menu at the right
---
docs/plugins.md | 5 +++
packages/ui/src/components/PluginsPage.svelte | 42 ++++++++++++++++---
.../ui/src/components/PluginsPage.test.ts | 35 ++++++++++++++++
packages/ui/src/components/Sidebar.svelte | 29 +++++--------
packages/ui/src/lib/strings.en.ts | 2 +
packages/ui/src/lib/strings.fr.ts | 1 +
tests/e2e/project-views.test.ts | 4 ++
7 files changed, 93 insertions(+), 25 deletions(-)
diff --git a/docs/plugins.md b/docs/plugins.md
index 31cac02d6..41ecf25f8 100644
--- a/docs/plugins.md
+++ b/docs/plugins.md
@@ -29,6 +29,11 @@ Plugins are administration of the machine that hosts the core, so every
`plugins.*` method is owner only and the phone settings leave the page to the
desktop app ([phone](phone.md)).
+Each core has its own plugins. The page opens on this PC when the window has
+one, since a desktop app shows its window there, whichever machine the open
+conversation runs on. With several machines connected, a menu beside the
+heading names the machine shown and switches to another one this window owns.
+
## The manifest
Put `boite-plugin.json` at the root of the repository:
diff --git a/packages/ui/src/components/PluginsPage.svelte b/packages/ui/src/components/PluginsPage.svelte
index d9aeb6f83..958cfb44c 100644
--- a/packages/ui/src/components/PluginsPage.svelte
+++ b/packages/ui/src/components/PluginsPage.svelte
@@ -1,8 +1,7 @@
+
+{#snippet refusal(rejected: PluginRejected)}
+
+
{t.refusedTitle}
+
+
{t.refusedFile}
{rejected.file}
+
{t.refusedField}
{rejected.field}
+
{t.refusedExpected}
{rejected.expected}
+
+
+{/snippet}
+
+{#snippet autostartSwitch(plugin: PluginState, on: boolean | undefined)}
+
+ {#if on !== undefined}
+
+ {/if}
+{/snippet}
+
+{#snippet row(plugin: PluginState)}
+ {@const working = busy[plugin.id] === true}
+ {@const state = stateLine(plugin)}
+
-
- { moving = false; }} />
+{#key chosen.store}
+
+{/key}
diff --git a/packages/ui/src/components/PluginsPage.test.ts b/packages/ui/src/components/PluginsPage.test.ts
index 2252277e3..f50723ff5 100644
--- a/packages/ui/src/components/PluginsPage.test.ts
+++ b/packages/ui/src/components/PluginsPage.test.ts
@@ -275,6 +275,14 @@ test('opens on this PC rather than on the server the chat is on, and switches ma
const pc = new FakeClient({ delayMs: 0 });
const server = new FakeClient({ delayMs: 0 });
await Promise.all([pc.connect(), server.connect()]);
+ const pcCall = pc.call.bind(pc);
+ // The PC answers Stop only when the test lets it, after the page has moved to the server.
+ let answer: () => void = () => {};
+ const held = new Promise((resolve) => { answer = resolve; });
+ vi.spyOn(pc, 'call').mockImplementation((async (method: string, params: unknown) => {
+ if (method === 'plugins.app') await held;
+ return pcCall(method as never, params as never);
+ }) as never);
const serverList = server.call.bind(server);
// Nothing is installed on the server.
vi.spyOn(server, 'call').mockImplementation((async (method: string, params: unknown) => {
@@ -293,12 +301,21 @@ test('opens on this PC rather than on the server the chat is on, and switches ma
mounted = mount(PluginsPage, { target: document.body, props: { store: serverStore } });
await vi.waitFor(() => expect(row('bots')?.closest('[data-testid="plugins-installed"]') ?? null).not.toBeNull());
expect(q('[data-testid="plugins-machine"]')?.textContent).toContain('nuno-pc');
+ await vi.waitFor(() => expect(appButton('bots', 'stop')).not.toBeNull());
+ appButton('bots', 'stop')!.click();
q('[data-testid="plugins-machine"]')!.click();
flushSync();
q('[data-testid="plugins-machine-menu"] [data-value="https://server.test"]')!.click();
await vi.waitFor(() => expect(row('bots')?.closest('[data-testid="plugins-recommended"]') ?? null).not.toBeNull());
expect(q('[data-testid="plugins-machine"]')?.textContent).toContain('server');
+
+ // The PC's late answer stays out of the server's page.
+ answer();
+ await new Promise((resolve) => setTimeout(resolve, 50));
+ flushSync();
+ expect(row('bots')?.closest('[data-testid="plugins-recommended"]') ?? null).not.toBeNull();
+ expect(app('bots')).toBeNull();
} finally {
workspace.machines = [];
pc.close(); server.close();
From 1882954b1bb612748eb7f922dd37347fb44970db Mon Sep 17 00:00:00 2001
From: meetsu <96637888+klNuno@users.noreply.github.com>
Date: Sun, 18 Oct 2026 21:11:11 +0000
Subject: [PATCH 3/3] ci: raise the UI budget for the plugins machine menu
---
docs/ci.md | 7 +++++++
scripts/ci/budgets.json | 2 +-
2 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/docs/ci.md b/docs/ci.md
index 19f49992a..2562e605d 100644
--- a/docs/ci.md
+++ b/docs/ci.md
@@ -375,6 +375,13 @@ Windows, against 5,037,724 for those two files at that main, 1,607 more; the
entry chunk stays at 596,622. The UI limit rises to 5,040,000; the other
limits stay.
+On 2026-10-18, Plugins opening on this PC with a list mounted per machine and
+the project menu back at the right end of the sidebar header (PR #606) merged
+with `origin/main` at `e22bb094` measure 4,869,757 UI bytes after a fresh
+`bun run build:ui` on Linux, against 4,869,093 for that main: the machine menu
+and `PluginsMachine.svelte` add 664 bytes. The UI limit rises to 4,869,800;
+the other limits stay.
+
On 2026-10-17, PR #566 merged with `origin/main` at `b6ae659a`, which brings
the thread links and agent mail direction (PR #562), measures 595,205 entry
bytes, 5,036,527 UI bytes and 4,390,768 core JavaScript bytes after fresh
diff --git a/scripts/ci/budgets.json b/scripts/ci/budgets.json
index 2f6dbc2b8..ce7104f4b 100644
--- a/scripts/ci/budgets.json
+++ b/scripts/ci/budgets.json
@@ -1,6 +1,6 @@
{
"uiEntryChunk": 492200,
- "uiDist": 4869600,
+ "uiDist": 4869800,
"coreBundle": 995000,
"coreJavaScript": 4479600
}