/**
* @license
* Copyright 2025 Vybestack LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { Box, Text, useIsScreenReaderEnabled } from 'ink';
import type { FC } from 'react';
import { useEffect, useState } from 'react';
import { useUIState } from '../contexts/UIStateContext.js';
import { theme } from '../semantic-colors.js';
import { Colors } from '../colors.js';
import { StreamingState } from '../types.js';
import { UpdateNotification } from './UpdateNotification.js';
import type { UpdateObject } from '../utils/updateCheck.js';
import type { HistoryItem } from '../types.js';
import { debugLogger } from '@vybestack/llxprt-code-telemetry';
import { Storage } from '@vybestack/llxprt-code-settings';
import * as fs from 'node:fs/promises';
import path from 'node:path';
import { getBorderStyle } from '../contexts/UnicodeRenderingContext.js';
const settingsPath = Storage.getGlobalSettingsPath();
const screenReaderNudgeFilePath = path.join(
Storage.getGlobalTempDir(),
'seen_screen_reader_nudge.json',
);
interface NotificationsProps {
startupWarnings: string[];
updateInfo: UpdateObject | null;
history: HistoryItem[];
}
function useScreenReaderNudge(
isScreenReaderEnabled: boolean,
): [boolean | undefined, () => void] {
const [hasSeenScreenReaderNudge, setHasSeenScreenReaderNudge] = useState<
boolean | undefined
>(undefined);
useEffect(() => {
const checkScreenReaderNudge = async () => {
try {
await fs.access(screenReaderNudgeFilePath);
setHasSeenScreenReaderNudge(true);
} catch {
setHasSeenScreenReaderNudge(false);
}
};
void checkScreenReaderNudge();
}, []);
useEffect(() => {
const writeScreenReaderNudgeFile = async () => {
if (isScreenReaderEnabled && hasSeenScreenReaderNudge === false) {
try {
await fs.mkdir(path.dirname(screenReaderNudgeFilePath), {
recursive: true,
});
await fs.writeFile(screenReaderNudgeFilePath, 'true');
} catch (error) {
debugLogger.error('Error storing screen reader nudge', error);
}
}
};
void writeScreenReaderNudgeFile();
}, [isScreenReaderEnabled, hasSeenScreenReaderNudge]);
return [hasSeenScreenReaderNudge, () => setHasSeenScreenReaderNudge(true)];
}
const ScreenReaderNudge: FC = () => (
You are currently in screen reader-friendly view. To switch out, open{' '}
{settingsPath} and remove the entry for {'"screenReader"'}. This will
disappear on next run.
);
interface StartupWarningsBoxProps {
warnings: string[];
}
const StartupWarningsBox: FC = ({ warnings }) => (
{warnings.map((warning, index) => (
{warning}
))}
);
interface InitErrorBoxProps {
initError: string;
history: HistoryItem[];
}
const InitErrorBox: FC = ({ initError, history }) => {
const matchingHistoryError = history.find(
(item) => item.type === 'error' && item.text.includes(initError),
);
if (matchingHistoryError?.text) {
return (
{matchingHistoryError.text}
);
}
return (
Initialization Error: {initError}
{' '}
Please check API key and configuration.
);
};
export const Notifications: FC = ({
startupWarnings,
updateInfo,
history,
}) => {
const { initError, streamingState } = useUIState();
const isScreenReaderEnabled = useIsScreenReaderEnabled();
const [hasSeenScreenReaderNudge] = useScreenReaderNudge(
isScreenReaderEnabled,
);
const showStartupWarnings = startupWarnings.length > 0;
const showInitError =
initError != null && streamingState !== StreamingState.Responding;
const showScreenReaderNudge =
isScreenReaderEnabled && hasSeenScreenReaderNudge === false;
if (
!showStartupWarnings &&
!showInitError &&
updateInfo == null &&
!showScreenReaderNudge
) {
return null;
}
return (
<>
{showScreenReaderNudge && }
{updateInfo && }
{showStartupWarnings && }
{showInitError && (
)}
>
);
};