From f50ed398f5dd06dbd99db9cb290b8b6a8848cc54 Mon Sep 17 00:00:00 2001 From: limityan Date: Sat, 25 Jul 2026 18:00:15 +0800 Subject: [PATCH 1/2] fix(external-sources): i18n, alignment, and visual polish for config UI - Fix ecosystem mode Select not showing disabled when master toggle is off - Remove raw English diagnostic messages from hook and source diagnostics - Replace raw error.detail with localized code reference - Add chevron indicators to all expandable
areas - Remove ExternalHooksPanel from page (read-only, no decision impact) - Move policy/master toggle to top of page - Remove catalog-level diagnostics section (redundant with per-source) - Filter out info-severity hook diagnostics (not actionable) - Flatten agent diagnostics: remove raw code, only show nextStep for blocking - Remove raw diagnostic code from agent detail and conflict cards - Rewrite all hook diagnostic texts for clarity (remove jargon) - Rewrite agent diagnostic texts (shorter, plain language) - Rewrite MCP empty state (remove incomplete example, clarify purpose) - Remove gray box backgrounds that look muddy in light theme - Add left-border grouping and top-border separators for visual structure - Fix alignment: all content shares same left edge within sections - Fix chevron-text spacing (gap + margin-right was too large) - Clean up unused SCSS (technical-detail, catalog-diagnostics, etc.) --- .../config/components/ExternalHooksPanel.scss | 94 ++++++++------ .../components/ExternalHooksPanel.test.tsx | 16 +-- .../config/components/ExternalHooksPanel.tsx | 47 +------ .../components/ExternalSourcesConfig.scss | 120 ++++++++++++++---- .../components/ExternalSourcesConfig.test.tsx | 2 - .../components/ExternalSourcesConfig.tsx | 45 +++---- .../en-US/settings/external-sources.json | 94 +++++++------- .../zh-CN/settings/external-sources.json | 104 +++++++-------- .../zh-TW/settings/external-sources.json | 102 +++++++-------- 9 files changed, 324 insertions(+), 300 deletions(-) diff --git a/src/web-ui/src/infrastructure/config/components/ExternalHooksPanel.scss b/src/web-ui/src/infrastructure/config/components/ExternalHooksPanel.scss index f7ab9f866..9eb613282 100644 --- a/src/web-ui/src/infrastructure/config/components/ExternalHooksPanel.scss +++ b/src/web-ui/src/infrastructure/config/components/ExternalHooksPanel.scss @@ -1,6 +1,6 @@ .bitfun-external-hooks { &__body { - padding: var(--size-gap-4); + padding: 0; } &__state, @@ -14,9 +14,9 @@ } &__notice { - padding: var(--size-gap-3); - border-radius: var(--size-radius-sm); - background: var(--element-bg-subtle); + padding: var(--size-gap-3) 0; + border-radius: 0; + background: transparent; } &__spinner { @@ -31,24 +31,52 @@ } &__ecosystem { - border: 1px solid var(--border-subtle); - border-radius: var(--size-radius-sm); - background: var(--element-bg-base); + border-top: 1px solid var(--border-subtle); + border-radius: 0; + background: transparent; overflow: hidden; + &:first-child { + border-top: 0; + } + > summary { display: flex; align-items: center; - justify-content: space-between; - gap: var(--size-gap-3); - padding: var(--size-gap-3); + gap: var(--size-gap-2); + padding: var(--size-gap-3) 0; color: var(--color-text-primary); font-size: var(--font-size-sm); font-weight: var(--font-weight-medium); cursor: pointer; + list-style: none; + user-select: none; + + &::-webkit-details-marker { + display: none; + } + + &::before { + content: ''; + flex-shrink: 0; + width: 6px; + height: 6px; + border-right: 2px solid var(--color-text-muted); + border-bottom: 2px solid var(--color-text-muted); + transform: rotate(-45deg); + transition: transform 0.15s var(--easing-standard); + } + } + + &[open] > summary::before { + transform: rotate(45deg); } } + &__counts { + margin-left: auto; + } + &__counts, &__location, &__entry-detail, @@ -60,13 +88,20 @@ } &__sources { - padding: 0 var(--size-gap-3) var(--size-gap-3); + padding: 0 0 0 var(--size-gap-3); + margin-left: 4px; + border-left: 1px solid var(--border-subtle); } &__source { - padding: var(--size-gap-3); - border-radius: var(--size-radius-sm); - background: var(--element-bg-subtle); + padding: var(--size-gap-2) 0; + border-top: 1px solid var(--border-subtle); + border-radius: 0; + background: transparent; + + &:first-child { + border-top: 0; + } } &__source-heading, @@ -93,14 +128,13 @@ flex-shrink: 0; padding: var(--size-gap-1) var(--size-gap-2); border-radius: var(--size-radius-lg); - background: var(--element-bg-base); + background: transparent; color: var(--color-text-secondary); font-size: var(--font-size-xs); } &__entries, - &__diagnostics, - &__catalog-diagnostics ul { + &__diagnostics { margin: var(--size-gap-3) 0 0; padding: 0; list-style: none; @@ -126,39 +160,17 @@ &__empty-source, &__diagnostics, - &__catalog-diagnostics, &__footnote { margin-top: var(--size-gap-3); } - &__diagnostics, - &__catalog-diagnostics { + &__diagnostics { color: var(--color-text-secondary); font-size: var(--font-size-xs); } - &__diagnostics li, - &__catalog-diagnostics li { - margin-top: var(--size-gap-1); - } - - &__technical-detail { + &__diagnostics li { margin-top: var(--size-gap-1); - - > summary { - cursor: pointer; - } - - > code, - > span { - display: block; - margin-top: var(--size-gap-1); - overflow-wrap: anywhere; - } - - > code { - font-family: var(--font-family-mono); - } } } diff --git a/src/web-ui/src/infrastructure/config/components/ExternalHooksPanel.test.tsx b/src/web-ui/src/infrastructure/config/components/ExternalHooksPanel.test.tsx index 4e811a1ef..5bbe934e9 100644 --- a/src/web-ui/src/infrastructure/config/components/ExternalHooksPanel.test.tsx +++ b/src/web-ui/src/infrastructure/config/components/ExternalHooksPanel.test.tsx @@ -158,8 +158,7 @@ describe('ExternalHooksPanel', () => { await Promise.resolve(); }); - expect(container.textContent?.match(/source-only diagnostic/g)).toHaveLength(1); - expect(container.textContent?.match(/provider-wide diagnostic/g)).toHaveLength(1); + expect(container.querySelectorAll('.bitfun-external-hooks__diagnostics > li')).toHaveLength(1); }); it('shows a remote-specific unsupported state without reading local Hooks', async () => { @@ -302,7 +301,7 @@ describe('ExternalHooksPanel', () => { expect(container.textContent).not.toContain('hooks.empty'); }); - it('bounds provider and catalog diagnostics until the user asks for more', async () => { + it('bounds provider diagnostics until the user asks for more', async () => { const providerDiagnostics = Array.from({ length: 30 }, (_, index) => ({ severity: 'warning', assetKind: 'hook', @@ -310,16 +309,10 @@ describe('ExternalHooksPanel', () => { message: `source diagnostic ${index}`, source: snapshot.sources[0].key, })); - const catalogDiagnostics = Array.from({ length: 30 }, (_, index) => ({ - severity: 'warning', - assetKind: 'hook', - code: `claude.hook.catalog_${index}`, - message: `catalog diagnostic ${index}`, - })); getCatalogMock.mockResolvedValue({ ...snapshot, sources: [{ ...snapshot.sources[0], diagnostics: providerDiagnostics }], - diagnostics: [...providerDiagnostics, ...catalogDiagnostics], + diagnostics: providerDiagnostics, }); await act(async () => { @@ -328,10 +321,9 @@ describe('ExternalHooksPanel', () => { }); expect(container.querySelectorAll('.bitfun-external-hooks__diagnostics > li')).toHaveLength(20); - expect(container.querySelectorAll('.bitfun-external-hooks__catalog-diagnostics li')).toHaveLength(20); expect(Array.from(container.querySelectorAll('button')).filter( (button) => button.textContent?.includes('hooks.showMoreDiagnostics'), - )).toHaveLength(2); + )).toHaveLength(1); }); it('polls the cached snapshot until deferred discovery completes', async () => { diff --git a/src/web-ui/src/infrastructure/config/components/ExternalHooksPanel.tsx b/src/web-ui/src/infrastructure/config/components/ExternalHooksPanel.tsx index d4a935c5e..eca81e231 100644 --- a/src/web-ui/src/infrastructure/config/components/ExternalHooksPanel.tsx +++ b/src/web-ui/src/infrastructure/config/components/ExternalHooksPanel.tsx @@ -77,14 +77,7 @@ function HookDiagnosticItem({ diagnostic, t }: { }) { return (
  • - {t(`hooks.diagnosticCategory.${diagnosticCategory(diagnostic.code)}`, { - code: diagnostic.code, - })} -
    - {t('hooks.technicalDetails')} - {diagnostic.code} - {diagnostic.message} -
    + {t(`hooks.diagnosticCategory.${diagnosticCategory(diagnostic.code)}`)}
  • ); } @@ -140,9 +133,9 @@ function HookSourceCard({ ))} )} - {diagnostics.length > 0 ? ( + {diagnostics.filter((d) => d.severity.toLowerCase() !== 'info').length > 0 ? (