From fe1bfc64f764fbdfd9bf16cdfa67d814c7b5e276 Mon Sep 17 00:00:00 2001 From: "N. Taylor Mullen" Date: Wed, 12 Nov 2025 23:36:18 -0800 Subject: [PATCH] feat: disengage surface adhesion protocols (#12989) --- packages/cli/src/ui/components/AsciiArt.ts | 49 +++++++++++++++++++ .../cli/src/ui/components/Header.test.tsx | 37 +++++++++++++- packages/cli/src/ui/components/Header.tsx | 17 +++++-- packages/cli/src/ui/utils/terminalSetup.ts | 12 ++++- 4 files changed, 108 insertions(+), 7 deletions(-) diff --git a/packages/cli/src/ui/components/AsciiArt.ts b/packages/cli/src/ui/components/AsciiArt.ts index 79eb522c80..6ca17d51a1 100644 --- a/packages/cli/src/ui/components/AsciiArt.ts +++ b/packages/cli/src/ui/components/AsciiArt.ts @@ -4,6 +4,10 @@ * SPDX-License-Identifier: Apache-2.0 */ +function flipAsciiArt(art: string): string { + return art.split('\n').reverse().join('\n'); +} + export const shortAsciiLogo = ` █████████ ██████████ ██████ ██████ █████ ██████ █████ █████ ███░░░░░███░░███░░░░░█░░██████ ██████ ░░███ ░░██████ ░░███ ░░███ @@ -36,3 +40,48 @@ export const tinyAsciiLogo = ` ███░ ░░█████████ ░░░ ░░░░░░░░░ `; + +const rawShortAsciiLogoIde = ` + ░░░░░░░░░ ░░░░░░░░░░ ░░░░░░ ░░░░░░ ░░░░░ ░░░░░░ ░░░░░ ░░░░░ + ░░░ ░░░ ░░░ ░░░░░░ ░░░░░░ ░░░ ░░░░░░ ░░░░░ ░░░ + ░░░ ░░░ ░░░ ░░░ ░░░ ░░░ ░░░ ░░░ ░░░ ░░░ ░░░ + █████████░░██████████ ██████ ░░██████░█████░██████ ░░█████ █████░ + ███░░ ███░███░░ ██████ ░██████░░███░░██████ ░█████ ███░░ + ███░░ ░░███░░ ███░███ ███ ███░░███░░███░███ ███░░ ███░░ + ███░░░░████░██████░░░░░███░░█████ ███░░███░░███░░███ ███░░░ ███░░░ + ███ ███ ███ ███ ███ ███ ███ ███ ██████ ███ + ███ ███ ███ ███ ███ ███ ███ █████ ███ + █████████ ██████████ ███ ███ █████ ███ █████ █████ +`; + +export const shortAsciiLogoIde = flipAsciiArt(rawShortAsciiLogoIde); + +const rawLongAsciiLogoIde = ` + ░░░ ░░░░░░░░░ ░░░░░░░░░░ ░░░░░░ ░░░░░░ ░░░░░ ░░░░░░ ░░░░░ ░░░░░ + ░░░ ░░░ ░░░ ░░░ ░░░░░░ ░░░░░░ ░░░ ░░░░░░ ░░░░░ ░░░ + ░░░ ░░░ ░░░ ░░░ ░░░ ░░░ ░░░ ░░░ ░░░ ░░░ ░░░ ░░░ + ███ ░░░ █████████░░██████████ ██████ ░░██████░█████░██████ ░░█████ █████░ + ███ ░░░ ███░ ███░███░░ ██████ ░██████░░███░░██████ ░█████ ███░░ + ███ ███░░░ ░░███░░ ███░███ ███ ███░░███░░███░███ ███░░ ███░░ + ░░░ ███ ███ ░░░█████░██████░░░░░███░░█████ ███░░███░░███░░███ ███░░░ ███░░░ + ███ ███ ███ ███ ███ ███ ███ ███ ███ ██████ ███ + ███ ███ ███ ███ ███ ███ ███ ███ █████ ███ + ███ █████████ ██████████ ███ ███ █████ ███ █████ █████ +`; + +export const longAsciiLogoIde = flipAsciiArt(rawLongAsciiLogoIde); + +const rawTinyAsciiLogoIde = ` + ░░░ ░░░░░░░░░ + ░░░ ░░░ ░░░ + ░░░ ░░░ + ███ ░░░ █████████░░░ + ███ ░░░ ███░░ ███░░ + ███ ███░░ ░░░ + ░░░ ███ ███░░░░████░ + ███ ███ ███ + ███ ███ ███ + ███ █████████ +`; + +export const tinyAsciiLogoIde = flipAsciiArt(rawTinyAsciiLogoIde); diff --git a/packages/cli/src/ui/components/Header.test.tsx b/packages/cli/src/ui/components/Header.test.tsx index 8b4aa6b674..4cadc73a48 100644 --- a/packages/cli/src/ui/components/Header.test.tsx +++ b/packages/cli/src/ui/components/Header.test.tsx @@ -8,12 +8,16 @@ import { render } from '../../test-utils/render.js'; import { describe, it, expect, vi, beforeEach, type Mock } from 'vitest'; import { Header } from './Header.js'; import * as useTerminalSize from '../hooks/useTerminalSize.js'; -import { longAsciiLogo } from './AsciiArt.js'; +import { longAsciiLogo, longAsciiLogoIde } from './AsciiArt.js'; import * as semanticColors from '../semantic-colors.js'; +import * as terminalSetup from '../utils/terminalSetup.js'; import { Text } from 'ink'; import type React from 'react'; vi.mock('../hooks/useTerminalSize.js'); +vi.mock('../utils/terminalSetup.js', () => ({ + getTerminalProgram: vi.fn(), +})); vi.mock('ink-gradient', () => { const MockGradient = ({ children }: { children: React.ReactNode }) => ( <>{children} @@ -34,6 +38,7 @@ vi.mock('ink', async () => { describe('
', () => { beforeEach(() => { vi.clearAllMocks(); + vi.mocked(terminalSetup.getTerminalProgram).mockReturnValue(null); }); it('renders the long logo on a wide terminal', () => { @@ -50,6 +55,22 @@ describe('
', () => { ); }); + it('uses the IDE logo when running in an IDE', () => { + vi.spyOn(useTerminalSize, 'useTerminalSize').mockReturnValue({ + columns: 120, + rows: 20, + }); + vi.mocked(terminalSetup.getTerminalProgram).mockReturnValue('vscode'); + + render(
); + expect(Text).toHaveBeenCalledWith( + expect.objectContaining({ + children: longAsciiLogoIde, + }), + undefined, + ); + }); + it('renders custom ASCII art when provided', () => { const customArt = 'CUSTOM ART'; render( @@ -63,6 +84,20 @@ describe('
', () => { ); }); + it('renders custom ASCII art as is when running in an IDE', () => { + const customArt = 'CUSTOM ART'; + vi.mocked(terminalSetup.getTerminalProgram).mockReturnValue('vscode'); + render( +
, + ); + expect(Text).toHaveBeenCalledWith( + expect.objectContaining({ + children: customArt, + }), + undefined, + ); + }); + it('displays the version number when nightly is true', () => { render(
); const textCalls = (Text as Mock).mock.calls; diff --git a/packages/cli/src/ui/components/Header.tsx b/packages/cli/src/ui/components/Header.tsx index 51e9cd8c71..08b356ef89 100644 --- a/packages/cli/src/ui/components/Header.tsx +++ b/packages/cli/src/ui/components/Header.tsx @@ -8,9 +8,17 @@ import type React from 'react'; import { Box, Text } from 'ink'; import Gradient from 'ink-gradient'; import { theme } from '../semantic-colors.js'; -import { shortAsciiLogo, longAsciiLogo, tinyAsciiLogo } from './AsciiArt.js'; +import { + shortAsciiLogo, + longAsciiLogo, + tinyAsciiLogo, + shortAsciiLogoIde, + longAsciiLogoIde, + tinyAsciiLogoIde, +} from './AsciiArt.js'; import { getAsciiArtWidth } from '../utils/textUtils.js'; import { useTerminalSize } from '../hooks/useTerminalSize.js'; +import { getTerminalProgram } from '../utils/terminalSetup.js'; interface HeaderProps { customAsciiArt?: string; // For user-defined ASCII art @@ -44,6 +52,7 @@ export const Header: React.FC = ({ nightly, }) => { const { columns: terminalWidth } = useTerminalSize(); + const isIde = getTerminalProgram(); let displayTitle; const widthOfLongLogo = getAsciiArtWidth(longAsciiLogo); const widthOfShortLogo = getAsciiArtWidth(shortAsciiLogo); @@ -51,11 +60,11 @@ export const Header: React.FC = ({ if (customAsciiArt) { displayTitle = customAsciiArt; } else if (terminalWidth >= widthOfLongLogo) { - displayTitle = longAsciiLogo; + displayTitle = isIde ? longAsciiLogoIde : longAsciiLogo; } else if (terminalWidth >= widthOfShortLogo) { - displayTitle = shortAsciiLogo; + displayTitle = isIde ? shortAsciiLogoIde : shortAsciiLogo; } else { - displayTitle = tinyAsciiLogo; + displayTitle = isIde ? tinyAsciiLogoIde : tinyAsciiLogo; } const artWidth = getAsciiArtWidth(displayTitle); diff --git a/packages/cli/src/ui/utils/terminalSetup.ts b/packages/cli/src/ui/utils/terminalSetup.ts index 87e4ae1501..a0b541318d 100644 --- a/packages/cli/src/ui/utils/terminalSetup.ts +++ b/packages/cli/src/ui/utils/terminalSetup.ts @@ -53,8 +53,7 @@ export interface TerminalSetupResult { type SupportedTerminal = 'vscode' | 'cursor' | 'windsurf'; -// Terminal detection -async function detectTerminal(): Promise { +export function getTerminalProgram(): SupportedTerminal | null { const termProgram = process.env['TERM_PROGRAM']; // Check VS Code and its forks - check forks first to avoid false positives @@ -75,6 +74,15 @@ async function detectTerminal(): Promise { if (termProgram === 'vscode' || process.env['VSCODE_GIT_IPC_HANDLE']) { return 'vscode'; } + return null; +} + +// Terminal detection +async function detectTerminal(): Promise { + const envTerminal = getTerminalProgram(); + if (envTerminal) { + return envTerminal; + } // Check parent process name if (os.platform() !== 'win32') {