Hide desktop theme imports from paired web clients (#20015)

* Hide Ghostty import from paired web clients

- Ghostty import now respects showDesktopOnlySettings, matching Warp behavior
- Consolidates desktop-only theme imports under a single showDesktopThemeImports flag
- Adds showGhosttyImport option to control visibility across settings UI and search

* Hide desktop theme imports from web clients

Consolidate Warp and Ghostty import visibility behind a single
`showDesktopThemeImports` flag. These theme import flows are
desktop-only and should not appear on paired web clients.

---------

Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
Co-authored-by: m4air <m4air@Mac.localdomain>
This commit is contained in:
OrcaWin
2026-09-10 22:11:34 -07:00
committed by GitHub
co-authored by m4air m4air
parent 2ffac471bd
commit 8acce092ef
7 changed files with 89 additions and 63 deletions
@@ -147,7 +147,9 @@ export function AppearancePane({
]
const terminalSearchEntries = [
{ title: terminalTitle },
...getTerminalAppearanceSearchEntries({ showWarpImport: !isWebClient })
...getTerminalAppearanceSearchEntries({
showDesktopThemeImports: !isWebClient
})
]
const windowSearchEntries = [
{
@@ -524,8 +524,36 @@ describe('TerminalAppearanceSection ghostty import wiring', () => {
expect(findTerminalThemeCatalogSection(element)?.props.showThemeImport).toBe(false)
expect(findWarpThemeImportModal(element)).toBeNull()
expect(findButtons(element).some((button) => button.text === 'Import from Ghostty')).toBe(false)
expect(findGhosttyImportModal(element)).toBeNull()
})
it.each([false, true])(
'hides Ghostty search results on web clients with forceVisiblePrimary=%s',
(forceVisiblePrimary) => {
vi.stubGlobal('window', { __ORCA_WEB_CLIENT__: true })
mockSettingsSearchQuery = 'ghostty'
const element = TerminalAppearanceSection({
settings: {} as never,
updateSettings: () => {},
systemPrefersDark: true,
terminalFontSuggestions: [],
ghostty: ghosttyMock,
warpThemes: warpThemesMock,
forceVisiblePrimary
})
expect(findButtons(element).some((button) => button.text === 'Import from Ghostty')).toBe(
false
)
expect(findGhosttyImportModal(element)).toBeNull()
if (!forceVisiblePrimary) {
expect(findComponentByTypeName(element, 'SettingsSubsectionHeader')).toBeNull()
}
}
)
it('passes hook state to GhosttyImportModal', () => {
const element = TerminalAppearanceSection({
settings: {} as never,
@@ -34,7 +34,7 @@ import { GhosttyImportModal } from './GhosttyImportModal'
import type { UseGhosttyImportReturn } from './useGhosttyImport'
import { WarpThemeImportModal } from './WarpThemeImportModal'
import type { UseWarpThemeImportReturn } from './useWarpThemeImport'
import { isWebClientLocation } from '@/hooks/useSettingsNavigationMetadata'
import { isWebClientLocation } from '@/lib/web-client-location'
import ghosttyIcon from '../../../../../resources/ghostty.svg'
import { translate } from '@/i18n/i18n'
@@ -83,7 +83,7 @@ export function TerminalAppearanceSection({
const isSearching = normalizeSettingsSearchQuery(searchQuery).length > 0
const [themeSearch, setThemeSearch] = useState('')
const [previewFontFamily, setPreviewFontFamily] = useState<string | null>(null)
const showWarpThemeImport = !isWebClientLocation()
const showDesktopThemeImports = !isWebClientLocation()
const darkThemeSearchEntries = getTerminalDarkThemeSearchEntries()
const lightThemeSearchEntries = getTerminalLightThemeSearchEntries()
const terminalTypographyEntries = getTerminalTypographySearchEntries()
@@ -92,7 +92,7 @@ export function TerminalAppearanceSection({
...getTerminalThemeTargetSearchEntries(),
...darkThemeSearchEntries,
...lightThemeSearchEntries,
...(showWarpThemeImport
...(showDesktopThemeImports
? [...getTerminalWarpImportSearchEntries(), ...getTerminalYamlImportSearchEntries()]
: [])
]
@@ -116,14 +116,16 @@ export function TerminalAppearanceSection({
searchQuery,
terminalTypographyEntries.slice(0, 2)
)
const ghosttyImportMatches = matchesSettingsSearch(searchQuery, ghosttyImportEntries)
const ghosttyImportMatches =
showDesktopThemeImports && matchesSettingsSearch(searchQuery, ghosttyImportEntries)
const showPrimaryTypography =
!isSearching ||
forceVisiblePrimary ||
primaryTypographyMatches ||
typographyMatches ||
ghosttyImportMatches
const showGhosttyImport = !isSearching || forceVisiblePrimary || ghosttyImportMatches
const showGhosttyImport =
showDesktopThemeImports && (!isSearching || forceVisiblePrimary || ghosttyImportMatches)
const showTypographyAdvancedDisclosure = !isSearching || typographyMatches
const advancedGroups = [
@@ -259,36 +261,38 @@ export function TerminalAppearanceSection({
previewFontFamily={previewFontFamily}
importedHighlightSignal={warpThemes.importSignal}
warpThemes={warpThemes}
showThemeImport={showWarpThemeImport}
showThemeImport={showDesktopThemeImports}
preferredTarget={preferredThemeTarget}
advancedContent={previewAdvancedContent}
/>
) : null}
<GhosttyImportModal
open={ghostty.open}
onOpenChange={ghostty.handleOpenChange}
preview={ghostty.preview}
loading={ghostty.loading}
onApply={ghostty.handleApply}
applied={ghostty.applied}
applyError={ghostty.applyError}
/>
{showWarpThemeImport ? (
<WarpThemeImportModal
open={warpThemes.open}
mode={warpThemes.mode}
preview={warpThemes.preview}
loading={warpThemes.loading}
desktopOnly={warpThemes.desktopOnly}
applyError={warpThemes.applyError}
selectedThemeIds={warpThemes.selectedThemeIds}
handlePreviewSource={warpThemes.handlePreviewSource}
handleToggleTheme={warpThemes.handleToggleTheme}
handleToggleAll={warpThemes.handleToggleAll}
handleApply={warpThemes.handleApply}
handleOpenChange={warpThemes.handleOpenChange}
/>
{showDesktopThemeImports ? (
<>
<GhosttyImportModal
open={ghostty.open}
onOpenChange={ghostty.handleOpenChange}
preview={ghostty.preview}
loading={ghostty.loading}
onApply={ghostty.handleApply}
applied={ghostty.applied}
applyError={ghostty.applyError}
/>
<WarpThemeImportModal
open={warpThemes.open}
mode={warpThemes.mode}
preview={warpThemes.preview}
loading={warpThemes.loading}
desktopOnly={warpThemes.desktopOnly}
applyError={warpThemes.applyError}
selectedThemeIds={warpThemes.selectedThemeIds}
handlePreviewSource={warpThemes.handlePreviewSource}
handleToggleTheme={warpThemes.handleToggleTheme}
handleToggleAll={warpThemes.handleToggleAll}
handleApply={warpThemes.handleApply}
handleOpenChange={warpThemes.handleOpenChange}
/>
</>
) : null}
</div>
)
@@ -223,13 +223,13 @@ const getAppearanceSectionEntries = createLocalizedCatalog((): SettingsSearchEnt
])
type AppearancePaneSearchOptions = {
showWarpImport?: boolean
showDesktopThemeImports?: boolean
showSystemTray?: boolean
showMenuBarIcon?: boolean
}
function buildAppearancePaneSearchEntries(
options: AppearancePaneSearchOptions
export function getAppearancePaneSearchEntries(
options: AppearancePaneSearchOptions = {}
): SettingsSearchEntry[] {
return [
...getAppearanceSectionEntries(),
@@ -247,13 +247,3 @@ function buildAppearancePaneSearchEntries(
...getMenuBarIconEntries(options)
]
}
export function getAppearancePaneSearchEntries(
options: AppearancePaneSearchOptions = {}
): SettingsSearchEntry[] {
return buildAppearancePaneSearchEntries({
showWarpImport: options.showWarpImport ?? true,
showSystemTray: options.showSystemTray,
showMenuBarIcon: options.showMenuBarIcon
})
}
@@ -156,14 +156,17 @@ describe('getTerminalPaneSearchEntries', () => {
expect(matchesSettingsSearch(query, getAppearancePaneSearchEntries())).toBe(true)
})
it('omits the Warp import appearance entry when desktop-only controls are hidden', () => {
const desktopEntries = getAppearancePaneSearchEntries({ showWarpImport: true })
const webEntries = getAppearancePaneSearchEntries({ showWarpImport: false })
it.each(['ghostty', 'warp', 'yaml'])(
'omits desktop-only %s search results on web clients',
(query) => {
const desktopEntries = getAppearancePaneSearchEntries()
const webEntries = getAppearancePaneSearchEntries({ showDesktopThemeImports: false })
expect(desktopEntries.some((entry) => entry.title === 'Import from Warp')).toBe(true)
expect(webEntries.some((entry) => entry.title === 'Import from Warp')).toBe(false)
expect(webEntries.some((entry) => entry.title === 'Import from Ghostty')).toBe(true)
})
expect(matchesSettingsSearch(query, desktopEntries)).toBe(true)
expect(matchesSettingsSearch(query, webEntries)).toBe(false)
expect(matchesSettingsSearch('font size', webEntries)).toBe(true)
}
)
it('includes the system tray appearance entry only when desktop tray controls are shown', () => {
const desktopEntries = getAppearancePaneSearchEntries({ showSystemTray: true })
@@ -63,10 +63,10 @@ export {
} from './terminal-window-setup-search'
type TerminalAppearanceSearchOptions = {
showWarpImport?: boolean
showDesktopThemeImports?: boolean
}
const getTerminalAppearanceSearchEntriesWithoutWarp = createLocalizedCatalog(
const getTerminalAppearanceSearchEntriesWithoutImports = createLocalizedCatalog(
(): SettingsSearchEntry[] => [
...getTerminalTypographySearchEntries(),
...getTerminalCursorSearchEntries(),
@@ -74,16 +74,15 @@ const getTerminalAppearanceSearchEntriesWithoutWarp = createLocalizedCatalog(
...getTerminalThemeTargetSearchEntries(),
...getTerminalDarkThemeSearchEntries(),
...getTerminalLightThemeSearchEntries(),
...getTerminalWindowSearchEntries(),
...getTerminalGhosttyImportSearchEntries()
...getTerminalWindowSearchEntries()
]
)
// Why: compose rather than filter — entry titles are localized, so matching on
// an English title would leak the Warp entry back in under non-English locales.
const getTerminalAppearanceSearchEntriesWithWarp = createLocalizedCatalog(
// Compose catalogs because translated titles cannot reliably identify desktop-only entries.
const getTerminalAppearanceSearchEntriesWithImports = createLocalizedCatalog(
(): SettingsSearchEntry[] => [
...getTerminalAppearanceSearchEntriesWithoutWarp(),
...getTerminalAppearanceSearchEntriesWithoutImports(),
...getTerminalGhosttyImportSearchEntries(),
...getTerminalWarpImportSearchEntries(),
...getTerminalYamlImportSearchEntries()
]
@@ -92,9 +91,9 @@ const getTerminalAppearanceSearchEntriesWithWarp = createLocalizedCatalog(
export function getTerminalAppearanceSearchEntries(
options: TerminalAppearanceSearchOptions = {}
): SettingsSearchEntry[] {
return (options.showWarpImport ?? true)
? getTerminalAppearanceSearchEntriesWithWarp()
: getTerminalAppearanceSearchEntriesWithoutWarp()
return (options.showDesktopThemeImports ?? true)
? getTerminalAppearanceSearchEntriesWithImports()
: getTerminalAppearanceSearchEntriesWithoutImports()
}
export function getTerminalPaneSearchEntries(platform: {
@@ -26,7 +26,7 @@ export function buildInterfaceSettingsSections({
),
icon: Palette,
searchEntries: getAppearancePaneSearchEntries({
showWarpImport: showDesktopOnlySettings,
showDesktopThemeImports: showDesktopOnlySettings,
showSystemTray: showDesktopOnlySettings && isWindows,
showMenuBarIcon: showDesktopOnlySettings && isMac
}),