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() {
>
{t('package.missingSelection', { id: unavailableSelectionId })}
-
+ {APPEARANCE_MARKET_ENTRY_VISIBLE && (
+
+ )}
)}
diff --git a/src/web-ui/src/infrastructure/config/components/appearancePackageEntryVisibility.ts b/src/web-ui/src/infrastructure/config/components/appearancePackageEntryVisibility.ts
new file mode 100644
index 0000000000..47cae52d41
--- /dev/null
+++ b/src/web-ui/src/infrastructure/config/components/appearancePackageEntryVisibility.ts
@@ -0,0 +1,10 @@
+/**
+ * Visibility switches for the Appearance package distribution entry points.
+ *
+ * The Skin market and the local package import path are hidden from the
+ * Appearance settings page. The market dialog, the market API, and the backend
+ * commands stay wired behind these switches, so restoring an entry point only
+ * requires flipping its switch back to true.
+ */
+export const APPEARANCE_MARKET_ENTRY_VISIBLE: boolean = false;
+export const APPEARANCE_PACKAGE_IMPORT_VISIBLE: boolean = false;
diff --git a/tests/e2e/specs/l0-appearance.spec.ts b/tests/e2e/specs/l0-appearance.spec.ts
index ef96f05db1..b0a2951537 100644
--- a/tests/e2e/specs/l0-appearance.spec.ts
+++ b/tests/e2e/specs/l0-appearance.spec.ts
@@ -843,36 +843,23 @@ describe('L0 Appearance', () => {
expect(await picker.isDisplayed()).toBe(true);
});
- it('should preserve design-system inline spacing on Appearance action buttons', async () => {
+ it('should keep the Skin market and appearance package import entry points hidden', async () => {
await openAppearanceSettings();
- await waitForDisplayed('[data-openbitfun-part="packageActions"] [data-openbitfun-component="button"]');
+ await waitForDisplayed('[data-openbitfun-part="packageSection"]');
- const buttonSpacing = await browser.execute(() => {
- return Array.from(document.querySelectorAll(
+ const packageEntryPoints = await browser.execute(() => ({
+ actionButtons: Array.from(document.querySelectorAll(
'[data-openbitfun-part="packageActions"] [data-openbitfun-component="button"]',
- )).map((button) => {
- const styles = window.getComputedStyle(button);
+ )).map(button => button.textContent?.trim() ?? ''),
+ fileInputs: document.querySelectorAll('.appearance-package-config__file-input').length,
+ }));
- return {
- text: button.textContent?.trim() ?? '',
- paddingLeft: styles.paddingLeft,
- paddingRight: styles.paddingRight,
- paddingToken: styles.getPropertyValue('--_button-padding-inline').trim(),
- width: button.getBoundingClientRect().width,
- };
- });
- });
+ expect(packageEntryPoints.actionButtons).toEqual([]);
+ expect(packageEntryPoints.fileInputs).toBe(0);
- console.log('[L0] Appearance action button spacing:', JSON.stringify(buttonSpacing, null, 2));
- expect(buttonSpacing).toHaveLength(2);
- for (const button of buttonSpacing) {
- expect(button.paddingLeft).toBe('20px');
- expect(button.paddingRight).toBe('20px');
- expect(button.paddingToken).toBe('20px');
- }
await saveElementScreenshot(
- '[data-openbitfun-part="packageActions"]',
- 'l0-appearance-button-inline-spacing',
+ '[data-openbitfun-part="packageSection"]',
+ 'l0-appearance-package-entry-points-hidden',
);
});