You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maka.apache.org by zh...@apache.org on 2026/09/18 10:19:28 UTC
(maka) branch main updated: fix(desktop): avoid eager model-row tooltip mounting (#5467)
This is an automated email from the ASF dual-hosted git repository.
me2seeks pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/maka.git
The following commit(s) were added to refs/heads/main by this push:
new 3abdf3fd74 fix(desktop): avoid eager model-row tooltip mounting (#5467)
3abdf3fd74 is described below
commit 3abdf3fd74c72d3b4bf82957431dcdbd8d8de756
Author: testikun <bu...@gmail.com>
AuthorDate: Fri Sep 18 19:19:23 2026 +0900
fix(desktop): avoid eager model-row tooltip mounting (#5467)
Use a native hover hint for model parameter actions, retaining the accessible label without creating a portal for every catalog row. Add credential-free large-inventory coverage and a repeatable browser performance probe.
Generated-by: Codex
Co-authored-by: testikun <32...@users.noreply.github.com>
---
.../connection-settings-locale-render.test.ts | 37 ++++++
.../settings/provider-connection-detail.tsx | 9 +-
.../stories/settings/provider-settings.stories.tsx | 32 ++++++
scripts/perf/model-connection-open.mjs | 127 +++++++++++++++++++++
4 files changed, 202 insertions(+), 3 deletions(-)
diff --git a/apps/desktop/src/main/__tests__/connection-settings-locale-render.test.ts b/apps/desktop/src/main/__tests__/connection-settings-locale-render.test.ts
index 0fd1c6a4bd..fca1aff52f 100644
--- a/apps/desktop/src/main/__tests__/connection-settings-locale-render.test.ts
+++ b/apps/desktop/src/main/__tests__/connection-settings-locale-render.test.ts
@@ -384,6 +384,43 @@ test('credential probing does not flash a page-level loading warning', async ()
});
});
+test('model rows retain named parameter actions without mounting a tooltip layer per row', async () => {
+ const harness = installRenderer();
+ const base = relayConnection();
+ const models = Array.from({ length: 32 }, (_, index) => ({
+ id: `fixture/model-${index + 1}`,
+ displayName: `Fixture model ${index + 1}`,
+ }));
+ const connection: ProjectedLlmConnection = {
+ ...base,
+ defaultModel: models[0]!.id,
+ enabledModelIds: [models[0]!.id],
+ models,
+ catalogEntries: models.map((model, index) => ({
+ ...base.catalogEntries[0]!,
+ ...model,
+ isDefault: index === 0,
+ })),
+ };
+ await harness.render('zh-CN', createElement(components.RuntimeHostSettingsTarget, {
+ host: { profileId: 'local', hostId: 'host-local' },
+ children: createElement(components.ConnectionDetail, {
+ bridge: connectionDetailBridge({ hasSecret: async () => false }),
+ connection,
+ isDefault: true,
+ onChanged: async () => {},
+ onDeleted: async () => {},
+ }),
+ }));
+
+ const actions = [...harness.document.querySelectorAll<HTMLButtonElement>('span[title="配置参数"] > button')];
+ assert.equal(actions.length, models.length);
+ for (const [index, action] of actions.entries()) {
+ assert.equal(action.getAttribute('aria-label'), `配置模型参数:${models[index]!.displayName}`);
+ assert.equal(action.hasAttribute('aria-describedby'), false);
+ }
+});
+
test('credential read failures still render the persistent warning', async () => {
const harness = installRenderer();
const credential = deferred<boolean>();
diff --git a/apps/desktop/src/renderer/settings/provider-connection-detail.tsx b/apps/desktop/src/renderer/settings/provider-connection-detail.tsx
index 98010a1fee..b690092c8f 100644
--- a/apps/desktop/src/renderer/settings/provider-connection-detail.tsx
+++ b/apps/desktop/src/renderer/settings/provider-connection-detail.tsx
@@ -673,9 +673,12 @@ function ConnectionDetailInner(props: ConnectionDetailProps) {
) : label;
return (
<SettingsRow key={id} label={rowLabel} end={<>
- <IconButton variant="ghost" size="sm" icon={<Icon icon="wrench" size="sm" />}
- label={copy.declareCapabilitiesAria(label)} tooltip={copy.declareCapabilities}
- isDisabled={allActionsBusy} onClick={() => openRow({ model: id })} />
+ {/* Astryx tooltips resolve a portal on mount, causing a style recalculation per model row. */}
+ <span title={copy.declareCapabilities}>
+ <IconButton variant="ghost" size="sm" icon={<Icon icon="wrench" size="sm" />}
+ label={copy.declareCapabilitiesAria(label)}
+ isDisabled={allActionsBusy} onClick={() => openRow({ model: id })} />
+ </span>
{modelEnableSwitch(id, label)}
</>} />
diff --git a/apps/desktop/stories/settings/provider-settings.stories.tsx b/apps/desktop/stories/settings/provider-settings.stories.tsx
index 8586079cc6..0819df57cf 100644
--- a/apps/desktop/stories/settings/provider-settings.stories.tsx
+++ b/apps/desktop/stories/settings/provider-settings.stories.tsx
@@ -61,6 +61,7 @@ type AutoOpenTarget =
| 'detail-alibaba'
| 'detail-static'
| 'detail-relay'
+ | 'detail-large'
| 'add'
| 'catalog'
| 'oauth'
@@ -137,6 +138,18 @@ const configuredConnections = [
}),
];
+const largeConnection = makeConnection({
+ slug: 'openrouter-large',
+ name: 'OpenRouter',
+ providerType: 'openrouter',
+ defaultModel: 'fixture/model-001',
+ models: Array.from({ length: 444 }, (_, index) => ({
+ id: `fixture/model-${String(index + 1).padStart(3, '0')}`,
+ displayName: `Fixture model ${String(index + 1).padStart(3, '0')}`,
+ })),
+ modelSource: 'fetched',
+});
+
const alibabaTokenPlanConnections = [
makeConnection({
slug: 'alibaba-token-plan-cn',
@@ -686,6 +699,7 @@ function clickAutoOpenTarget(root: HTMLElement, target: AutoOpenTarget): boolean
|| target === 'detail-alibaba'
|| target === 'detail-static'
|| target === 'detail-relay'
+ || target === 'detail-large'
) {
// ListItem's clickable surface is an invisible button inside the row, so
// the row is located by its slug hook and the button taken from within it.
@@ -696,6 +710,8 @@ function clickAutoOpenTarget(root: HTMLElement, target: AutoOpenTarget): boolean
? 'alibaba-token-plan-cn'
: target === 'detail-static'
? 'ark-plan'
+ : target === 'detail-large'
+ ? 'openrouter-large'
: 'relay-house';
const row = root.querySelector<HTMLElement>(`[data-connection-slug="${slug}"]`);
const detailButton = row?.querySelector('button') ?? null;
@@ -790,6 +806,22 @@ export const ConnectionDetailPage: Story = {
),
};
+// Real path: 设置 → 模型 → an OpenRouter connection after fetching hundreds of models.
+// The fetched snapshot is local; browsing it never needs an API key.
+export const LargeConnectionDetail: Story = {
+ render: () => (
+ <ProviderStory
+ bridge={createBridge({ connections: [largeConnection], defaultSlug: 'openrouter-large' })}
+ autoOpen="detail-large"
+ />
+ ),
+ play: async ({ canvasElement }) => {
+ await waitFor(() => {
+ expect(canvasElement.querySelectorAll('button[aria-label*="Fixture model"]').length).toBe(444);
+ }, { timeout: 20_000 });
+ },
+};
+
// Fixed endpoints are inspectable but not editable. Alibaba is the high-signal
// case because several catalog entries share one brand while routing to
// different products and regions (#3636).
diff --git a/scripts/perf/model-connection-open.mjs b/scripts/perf/model-connection-open.mjs
new file mode 100644
index 0000000000..ae6d05b70f
--- /dev/null
+++ b/scripts/perf/model-connection-open.mjs
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// Build Storybook first, then run from the repository root. No provider key or Host is used.
+import assert from 'node:assert/strict';
+import { chromium, expect } from '@playwright/test';
+import { startStaticServer } from '../storybook-visual-smoke.mjs';
+
+const server = await startStaticServer('apps/desktop/storybook-static');
+const browser = await chromium.launch(
+ process.env.MAKA_PERF_CHROME_CHANNEL ? { channel: process.env.MAKA_PERF_CHROME_CHANNEL } : {},
+);
+try {
+ const page = await browser.newPage({ viewport: { width: 1200, height: 900 } });
+ await page.addInitScript(() => {
+ const probe = (window.__modelOpenProbe = { started: 0, completed: 0, longTasks: [] });
+ new PerformanceObserver((list) => {
+ probe.longTasks.push(
+ ...list.getEntries().map((entry) => ({
+ start: entry.startTime,
+ duration: entry.duration,
+ })),
+ );
+ }).observe({ type: 'longtask', buffered: true });
+ document.addEventListener(
+ 'click',
+ (event) => {
+ if (!event.target.closest('[data-connection-slug="openrouter-large"]')) return;
+ probe.started = performance.now();
+ const afterPaint = () =>
+ requestAnimationFrame(() =>
+ requestAnimationFrame(() => {
+ probe.completed = performance.now();
+ probe.rows = document.querySelectorAll('button[aria-label*="Fixture model"]').length;
+ probe.nodes = document.querySelectorAll('*').length;
+ }),
+ );
+ const waitForRows = () => {
+ if (document.querySelectorAll('button[aria-label*="Fixture model"]').length === 444) {
+ afterPaint();
+ } else {
+ requestAnimationFrame(waitForRows);
+ }
+ };
+ requestAnimationFrame(waitForRows);
+ },
+ { capture: true },
+ );
+ });
+
+ const samples = [];
+ for (let i = 0; i < 5; i++) {
+ await page.goto(
+ `${server.baseUrl}/iframe.html?id=product-settings-providers--large-connection-detail&viewMode=story`,
+ );
+ await page.waitForFunction(() => window.__modelOpenProbe?.completed > 0, null, {
+ timeout: 30_000,
+ });
+ const sample = await page.evaluate(() => {
+ const { started, completed, longTasks, rows, nodes } = window.__modelOpenProbe;
+ const duringOpen = longTasks.filter(
+ (task) => task.start + task.duration >= started && task.start <= completed,
+ );
+ return {
+ openMs: Math.round(completed - started),
+ longestTaskMs: Math.round(Math.max(0, ...duringOpen.map((task) => task.duration))),
+ rows,
+ nodes,
+ };
+ });
+ assert.equal(sample.rows, 444);
+ samples.push(sample);
+ }
+ const filter = page.getByRole('textbox', { name: /搜索模型|Search models/ });
+ await filter.fill('fixture/model-444');
+ const action = page.getByRole('button', { name: /Fixture model 444/ });
+ const enabled = page.getByRole('switch', { name: /Fixture model 444/ });
+ await expect(action).toHaveCount(1);
+ await expect(enabled).not.toBeChecked();
+ await enabled.click();
+ await expect(enabled).toBeChecked();
+ await action.click();
+ await expect(page.getByRole('dialog')).toBeVisible();
+ await page.keyboard.press('Escape');
+ await expect(page.getByRole('dialog')).toHaveCount(0);
+ await filter.fill('Fixture model 001');
+ await expect(page.getByRole('button', { name: /Fixture model 001/ })).toHaveCount(1);
+ await filter.fill('');
+ await expect(page.locator('button[aria-label*="Fixture model"]')).toHaveCount(444);
+ if (process.env.MAKA_PERF_SCREENSHOT) {
+ await page.screenshot({ path: process.env.MAKA_PERF_SCREENSHOT });
+ }
+ await page.setViewportSize({ width: 480, height: 800 });
+ await filter.fill('fixture/model-444');
+ await expect(action).toBeVisible();
+ const actionBounds = await action.boundingBox();
+ const switchBounds = await enabled.boundingBox();
+ assert.ok(actionBounds && switchBounds);
+ assert.ok(
+ actionBounds.x + actionBounds.width <= switchBounds.x ||
+ actionBounds.y + actionBounds.height <= switchBounds.y,
+ 'the parameter action must not overlap its enable switch',
+ );
+ if (process.env.MAKA_PERF_MOBILE_SCREENSHOT) {
+ await page.screenshot({ path: process.env.MAKA_PERF_MOBILE_SCREENSHOT });
+ }
+ console.log(JSON.stringify(samples, null, 2));
+} finally {
+ await browser.close();
+ await server.close();
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@maka.apache.org
For additional commands, e-mail: commits-help@maka.apache.org