feat: disengage surface adhesion protocols (#12989)

This commit is contained in:
N. Taylor Mullen
2025-11-12 23:36:18 -08:00
committed by GitHub
parent 0f9ec2735c
commit fe1bfc64f7
4 changed files with 108 additions and 7 deletions

View File

@@ -4,6 +4,10 @@
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
function flipAsciiArt(art: string): string {
return art.split('\n').reverse().join('\n');
}
export const shortAsciiLogo = ` 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);

View File

@@ -8,12 +8,16 @@ import { render } from '../../test-utils/render.js';
import { describe, it, expect, vi, beforeEach, type Mock } from 'vitest'; import { describe, it, expect, vi, beforeEach, type Mock } from 'vitest';
import { Header } from './Header.js'; import { Header } from './Header.js';
import * as useTerminalSize from '../hooks/useTerminalSize.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 semanticColors from '../semantic-colors.js';
import * as terminalSetup from '../utils/terminalSetup.js';
import { Text } from 'ink'; import { Text } from 'ink';
import type React from 'react'; import type React from 'react';
vi.mock('../hooks/useTerminalSize.js'); vi.mock('../hooks/useTerminalSize.js');
vi.mock('../utils/terminalSetup.js', () => ({
getTerminalProgram: vi.fn(),
}));
vi.mock('ink-gradient', () => { vi.mock('ink-gradient', () => {
const MockGradient = ({ children }: { children: React.ReactNode }) => ( const MockGradient = ({ children }: { children: React.ReactNode }) => (
<>{children}</> <>{children}</>
@@ -34,6 +38,7 @@ vi.mock('ink', async () => {
describe('<Header />', () => { describe('<Header />', () => {
beforeEach(() => { beforeEach(() => {
vi.clearAllMocks(); vi.clearAllMocks();
vi.mocked(terminalSetup.getTerminalProgram).mockReturnValue(null);
}); });
it('renders the long logo on a wide terminal', () => { it('renders the long logo on a wide terminal', () => {
@@ -50,6 +55,22 @@ describe('<Header />', () => {
); );
}); });
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(<Header version="1.0.0" nightly={false} />);
expect(Text).toHaveBeenCalledWith(
expect.objectContaining({
children: longAsciiLogoIde,
}),
undefined,
);
});
it('renders custom ASCII art when provided', () => { it('renders custom ASCII art when provided', () => {
const customArt = 'CUSTOM ART'; const customArt = 'CUSTOM ART';
render( render(
@@ -63,6 +84,20 @@ describe('<Header />', () => {
); );
}); });
it('renders custom ASCII art as is when running in an IDE', () => {
const customArt = 'CUSTOM ART';
vi.mocked(terminalSetup.getTerminalProgram).mockReturnValue('vscode');
render(
<Header version="1.0.0" nightly={false} customAsciiArt={customArt} />,
);
expect(Text).toHaveBeenCalledWith(
expect.objectContaining({
children: customArt,
}),
undefined,
);
});
it('displays the version number when nightly is true', () => { it('displays the version number when nightly is true', () => {
render(<Header version="1.0.0" nightly={true} />); render(<Header version="1.0.0" nightly={true} />);
const textCalls = (Text as Mock).mock.calls; const textCalls = (Text as Mock).mock.calls;

View File

@@ -8,9 +8,17 @@ import type React from 'react';
import { Box, Text } from 'ink'; import { Box, Text } from 'ink';
import Gradient from 'ink-gradient'; import Gradient from 'ink-gradient';
import { theme } from '../semantic-colors.js'; 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 { getAsciiArtWidth } from '../utils/textUtils.js';
import { useTerminalSize } from '../hooks/useTerminalSize.js'; import { useTerminalSize } from '../hooks/useTerminalSize.js';
import { getTerminalProgram } from '../utils/terminalSetup.js';
interface HeaderProps { interface HeaderProps {
customAsciiArt?: string; // For user-defined ASCII art customAsciiArt?: string; // For user-defined ASCII art
@@ -44,6 +52,7 @@ export const Header: React.FC<HeaderProps> = ({
nightly, nightly,
}) => { }) => {
const { columns: terminalWidth } = useTerminalSize(); const { columns: terminalWidth } = useTerminalSize();
const isIde = getTerminalProgram();
let displayTitle; let displayTitle;
const widthOfLongLogo = getAsciiArtWidth(longAsciiLogo); const widthOfLongLogo = getAsciiArtWidth(longAsciiLogo);
const widthOfShortLogo = getAsciiArtWidth(shortAsciiLogo); const widthOfShortLogo = getAsciiArtWidth(shortAsciiLogo);
@@ -51,11 +60,11 @@ export const Header: React.FC<HeaderProps> = ({
if (customAsciiArt) { if (customAsciiArt) {
displayTitle = customAsciiArt; displayTitle = customAsciiArt;
} else if (terminalWidth >= widthOfLongLogo) { } else if (terminalWidth >= widthOfLongLogo) {
displayTitle = longAsciiLogo; displayTitle = isIde ? longAsciiLogoIde : longAsciiLogo;
} else if (terminalWidth >= widthOfShortLogo) { } else if (terminalWidth >= widthOfShortLogo) {
displayTitle = shortAsciiLogo; displayTitle = isIde ? shortAsciiLogoIde : shortAsciiLogo;
} else { } else {
displayTitle = tinyAsciiLogo; displayTitle = isIde ? tinyAsciiLogoIde : tinyAsciiLogo;
} }
const artWidth = getAsciiArtWidth(displayTitle); const artWidth = getAsciiArtWidth(displayTitle);

View File

@@ -53,8 +53,7 @@ export interface TerminalSetupResult {
type SupportedTerminal = 'vscode' | 'cursor' | 'windsurf'; type SupportedTerminal = 'vscode' | 'cursor' | 'windsurf';
// Terminal detection export function getTerminalProgram(): SupportedTerminal | null {
async function detectTerminal(): Promise<SupportedTerminal | null> {
const termProgram = process.env['TERM_PROGRAM']; const termProgram = process.env['TERM_PROGRAM'];
// Check VS Code and its forks - check forks first to avoid false positives // Check VS Code and its forks - check forks first to avoid false positives
@@ -75,6 +74,15 @@ async function detectTerminal(): Promise<SupportedTerminal | null> {
if (termProgram === 'vscode' || process.env['VSCODE_GIT_IPC_HANDLE']) { if (termProgram === 'vscode' || process.env['VSCODE_GIT_IPC_HANDLE']) {
return 'vscode'; return 'vscode';
} }
return null;
}
// Terminal detection
async function detectTerminal(): Promise<SupportedTerminal | null> {
const envTerminal = getTerminalProgram();
if (envTerminal) {
return envTerminal;
}
// Check parent process name // Check parent process name
if (os.platform() !== 'win32') { if (os.platform() !== 'win32') {