From 657ce29e335a0bb689b738a2880105d7eaa9109d Mon Sep 17 00:00:00 2001 From: ilbertt Date: Mon, 20 Jul 2026 16:33:01 +0100 Subject: [PATCH 1/3] fix(mcping): settings padding, logs label, and single add-server flow - Hide empty server/log lists so their card's row-gap no longer inflates the bottom padding relative to the top. - Rename the "Log" section to "Logs". - Expand a newly added server's form automatically and disable "Add server" until that server connects or is removed, so only one can be set up at a time. Co-Authored-By: Claude Opus 4.8 (1M context) --- apps/mcping/src/renderer/index.html | 2 +- apps/mcping/src/renderer/src/main.ts | 14 +---- .../src/renderer/src/sections/server-card.ts | 58 ++++++++++++++++--- apps/mcping/src/renderer/src/styles.css | 12 ++++ 4 files changed, 66 insertions(+), 20 deletions(-) diff --git a/apps/mcping/src/renderer/index.html b/apps/mcping/src/renderer/index.html index eef2ca9..bf395d0 100644 --- a/apps/mcping/src/renderer/index.html +++ b/apps/mcping/src/renderer/index.html @@ -31,7 +31,7 @@

Launch

- Log + Logs diff --git a/apps/mcping/src/renderer/src/main.ts b/apps/mcping/src/renderer/src/main.ts index b6ee974..b7fa989 100644 --- a/apps/mcping/src/renderer/src/main.ts +++ b/apps/mcping/src/renderer/src/main.ts @@ -1,8 +1,7 @@ import './styles.css'; import { api } from './lib/api.ts'; -import { requireElement } from './lib/dom.ts'; import { renderLogEntry, wireCopyLog } from './sections/log-panel.ts'; -import { addServer, findCard, renderServers, updateCardStatus } from './sections/server-card.ts'; +import { applyServerStatus, renderServers, wireAddServer } from './sections/server-card.ts'; import { fillGlobalSettings, wireGlobalSettings } from './sections/settings.ts'; async function init(): Promise { @@ -10,15 +9,8 @@ async function init(): Promise { wireGlobalSettings(); await renderServers(); - requireElement('#add-server').addEventListener('click', () => { - void addServer(); - }); - api.onStatus((entry) => { - const card = findCard(entry.serverId); - if (card) { - updateCardStatus({ card, status: entry.status }); - } - }); + wireAddServer(); + api.onStatus(applyServerStatus); wireCopyLog(); for (const entry of await api.getLog()) { diff --git a/apps/mcping/src/renderer/src/sections/server-card.ts b/apps/mcping/src/renderer/src/sections/server-card.ts index d7789d6..1cdc0f5 100644 --- a/apps/mcping/src/renderer/src/sections/server-card.ts +++ b/apps/mcping/src/renderer/src/sections/server-card.ts @@ -12,7 +12,19 @@ const STATUS_LABEL: Record = { error: 'Error', }; -export function findCard(serverId: string): HTMLElement | null { +// Set while a just-added server is still blank; blocks a second add until it +// connects or is removed. +let pendingServerId: string | null = null; + +function addButton(): HTMLButtonElement { + return requireElement('#add-server'); +} + +function syncAddButton(): void { + addButton().disabled = pendingServerId !== null; +} + +function findCard(serverId: string): HTMLElement | null { return document.querySelector(`.server[data-server-id="${serverId}"]`); } @@ -28,7 +40,11 @@ async function saveField(options: { id: string; input: HTMLInputElement }): Prom async function removeServer(id: string): Promise { await api.removeServer(id); + if (id === pendingServerId) { + pendingServerId = null; + } await renderServers(); + syncAddButton(); } function wireCardActions(options: { card: HTMLElement; id: string }): void { @@ -75,7 +91,7 @@ function buildServerCard(options: { server: McpServer; authState: ServerAuthStat return card; } -export function updateCardStatus(options: { card: HTMLElement; status: ConnectionStatus }): void { +function updateCardStatus(options: { card: HTMLElement; status: ConnectionStatus }): void { const { card, status } = options; const pill = requireChild({ root: card, selector: '[data-role="status"]' }); pill.textContent = STATUS_LABEL[status.state]; @@ -93,12 +109,20 @@ export function updateCardStatus(options: { card: HTMLElement; status: Connectio actionButton({ card, action: 'disconnect' }).hidden = !active; } +export function applyServerStatus(entry: ServerStatus): void { + const card = findCard(entry.serverId); + if (card) { + updateCardStatus({ card, status: entry.status }); + } + if (entry.serverId === pendingServerId && entry.status.state === 'connected') { + pendingServerId = null; + syncAddButton(); + } +} + function applyStatuses(statuses: ServerStatus[]): void { for (const entry of statuses) { - const card = findCard(entry.serverId); - if (card) { - updateCardStatus({ card, status: entry.status }); - } + applyServerStatus(entry); } } @@ -110,10 +134,28 @@ export async function renderServers(): Promise { buildServerCard({ server, authState: authStates[server.id] ?? EMPTY_AUTH_STATE }), ), ); + const pendingCard = pendingServerId ? findCard(pendingServerId) : null; + if (pendingCard instanceof HTMLDetailsElement) { + pendingCard.open = true; + } applyStatuses(await api.getStatuses()); } -export async function addServer(): Promise { - await api.addServer({ name: '', url: '', autoConnect: true, auth: { type: 'none' } }); +async function addServer(): Promise { + const settings = await api.addServer({ + name: '', + url: '', + autoConnect: true, + auth: { type: 'none' }, + }); + pendingServerId = settings.servers.at(-1)?.id ?? null; await renderServers(); + syncAddButton(); +} + +export function wireAddServer(): void { + addButton().addEventListener('click', () => { + void addServer(); + }); + syncAddButton(); } diff --git a/apps/mcping/src/renderer/src/styles.css b/apps/mcping/src/renderer/src/styles.css index 6f18c1e..8b67611 100644 --- a/apps/mcping/src/renderer/src/styles.css +++ b/apps/mcping/src/renderer/src/styles.css @@ -174,12 +174,24 @@ body { color: var(--warn-fg); } +.button:disabled { + opacity: 0.5; + cursor: default; +} + .servers { display: flex; flex-direction: column; gap: 12px; } +/* An empty list is still a flex child, so its card's row-gap would sit below the + header and inflate the bottom padding. Drop it until it has entries. */ +.servers:empty, +.log:empty { + display: none; +} + .server { border: 1px solid var(--border); border-radius: 8px; From e76d0fd3d0d0cbe3fc74be97093be30d86488277 Mon Sep 17 00:00:00 2001 From: ilbertt Date: Mon, 20 Jul 2026 16:47:54 +0100 Subject: [PATCH 2/3] fix(mcping): disable Connect until required server fields are set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mark server name, URL and the API-key header fields as required, and keep the Connect button disabled until they are filled — for API-key auth that means a header name plus a saved secret. The header name pre-fills with the default when API-key auth is selected so the required field is never blank. Co-Authored-By: Claude Opus 4.8 (1M context) --- apps/mcping/src/renderer/index.html | 6 ++- .../src/renderer/src/sections/server-auth.ts | 19 +++++++--- .../src/renderer/src/sections/server-card.ts | 38 ++++++++++++++++++- 3 files changed, 55 insertions(+), 8 deletions(-) diff --git a/apps/mcping/src/renderer/index.html b/apps/mcping/src/renderer/index.html index bf395d0..2331c2d 100644 --- a/apps/mcping/src/renderer/index.html +++ b/apps/mcping/src/renderer/index.html @@ -50,23 +50,25 @@

Launch