From 33e47fc27392d3df2a0ca994f625b210e481defe Mon Sep 17 00:00:00 2001 From: kev1n77 Date: Sun, 20 Sep 2026 15:41:01 +0800 Subject: [PATCH] feat(web-ui): hide appearance package import and Skin market entries The Appearance settings page still exposed two package distribution entry points: the Skin market dialog and local package import. Both are now behind appearancePackageEntryVisibility switches, shipped hidden, while the market dialog, market API, and backend commands stay wired. Restoring an entry point is a switch flip, not a revert. Export and delete stay available for packages already installed on the device, since neither is a market or import path. The section header no longer renders an empty action row when both entries are hidden and no installed package is selected, which kept the narrow-container header from reserving blank space. Verified with the web-ui type check, the config and global-search suites (74 files, 567 passed), the appearance package section suite (8 passed), and i18n:audit (0 warnings). The l0 appearance spec replaced its market/import button spacing case with a guard that both entries stay hidden; that spacing contract needs restoring together with the switches. Co-authored-by: bitfun-ai --- .../AppearancePackageConfigSection.test.tsx | 18 +++-- .../AppearancePackageConfigSection.tsx | 75 ++++++++++++------- .../appearancePackageEntryVisibility.ts | 10 +++ tests/e2e/specs/l0-appearance.spec.ts | 35 +++------ 4 files changed, 79 insertions(+), 59 deletions(-) create mode 100644 src/web-ui/src/infrastructure/config/components/appearancePackageEntryVisibility.ts diff --git a/src/web-ui/src/infrastructure/config/components/AppearancePackageConfigSection.test.tsx b/src/web-ui/src/infrastructure/config/components/AppearancePackageConfigSection.test.tsx index 604ad54806..2db3b65cf1 100644 --- a/src/web-ui/src/infrastructure/config/components/AppearancePackageConfigSection.test.tsx +++ b/src/web-ui/src/infrastructure/config/components/AppearancePackageConfigSection.test.tsx @@ -91,21 +91,27 @@ describe('AppearancePackageConfigSection', () => { expect(html).toContain('data-testid="appearance-builtin-theme-select"'); expect(html.match(/data-testid="appearance-builtin-theme-option"/g)).toHaveLength(3); expect(html.match(/class="appearance-package-config__selected-mark"/g)).toHaveLength(1); - expect(html).toContain('package.market.open'); - expect(html).toContain('package.import'); expect(html).toContain('aria-label="package.export"'); expect(html).toContain('aria-label="package.delete"'); - expect(html).toContain('accept=".openbitfun-appearance,.zip,application/zip"'); expect(html).toContain('data-openbitfun-part="packageSection"'); expect(html).toContain('data-openbitfun-part="packageActions"'); - expect(html).toContain('data-openbitfun-component="button"'); - expect(html.match(/data-openbitfun-variant="outline"/g)).toHaveLength(2); - expect(html).not.toContain('data-size="md"'); + expect(html).toContain('data-openbitfun-component="icon-button"'); expect(html).toContain('openbitfun-config-page-section'); expect(html).not.toContain('appearance-package-config__action-button'); expect(html).not.toContain('.openbitfun-skin'); }); + it('hides the Skin market and appearance package import entry points', () => { + const html = renderToStaticMarkup(); + + expect(html).not.toContain('package.market.open'); + expect(html).not.toContain('package.import'); + expect(html).not.toContain('accept=".openbitfun-appearance,.zip,application/zip"'); + expect(html).not.toContain('appearance-package-config__file-input'); + expect(html).toContain('aria-label="package.export"'); + expect(html).toContain('aria-label="package.delete"'); + }); + it('uses high-density artwork and a separate selection mark for the built-in package card', () => { appearanceStateMock.selectedAppearanceId = 'system'; diff --git a/src/web-ui/src/infrastructure/config/components/AppearancePackageConfigSection.tsx b/src/web-ui/src/infrastructure/config/components/AppearancePackageConfigSection.tsx index b97334a0a8..2c5262c3ca 100644 --- a/src/web-ui/src/infrastructure/config/components/AppearancePackageConfigSection.tsx +++ b/src/web-ui/src/infrastructure/config/components/AppearancePackageConfigSection.tsx @@ -15,6 +15,10 @@ import { } from '@/infrastructure/appearance'; import { notificationService } from '@/shared/notification-system'; import { AppearanceMarketDialog } from './AppearanceMarketDialog'; +import { + APPEARANCE_MARKET_ENTRY_VISIBLE, + APPEARANCE_PACKAGE_IMPORT_VISIBLE, +} from './appearancePackageEntryVisibility'; import { ConfigPageSection, formatStandaloneUiText } from './common'; const DEFAULT_APPEARANCE_PREVIEW_SRC = '/assets/appearance/openbitfun-default-preview@4x.png'; @@ -318,6 +322,9 @@ export function AppearancePackageConfigSection() { ], [builtinAppearances, t, tApplication]); const selectedBuiltinThemeId = defaultPackageSelected ? selectedAppearanceId : ''; const busy = loading || !initialized || status === 'applying'; + const hasPackageActions = APPEARANCE_MARKET_ENTRY_VISIBLE + || APPEARANCE_PACKAGE_IMPORT_VISIBLE + || Boolean(selectedAppearance); const handleAppearanceSelection = async (id: string) => { if (busy || id === selectedAppearanceId) return; @@ -405,28 +412,32 @@ export function AppearancePackageConfigSection() { description={t('package.description')} bodySurface={false} fieldSurface="ambient" - extra={( + extra={hasPackageActions ? (
- - + {APPEARANCE_MARKET_ENTRY_VISIBLE && ( + + )} + {APPEARANCE_PACKAGE_IMPORT_VISIBLE && ( + + )} {selectedAppearance && ( <> )}
- )} + ) : undefined} data-openbitfun-component="appearance-settings" data-openbitfun-part="packageSection" data-openbitfun-package-type={selectedAppearance ? 'imported' : 'native'} data-openbitfun-state={busy ? 'disabled' : undefined} > - + {APPEARANCE_PACKAGE_IMPORT_VISIBLE && ( + + )}
))}
- setMarketOpen(false)} /> + {APPEARANCE_MARKET_ENTRY_VISIBLE && ( + setMarketOpen(false)} /> + )} {failure && ( setFailure(null)} /> )} @@ -516,9 +531,11 @@ export function AppearancePackageConfigSection() { >