fix: show auth url panel in shell only on mobile

- use static url: https://auth.openai.com/codex/device, for codex login.
- add an option for hiding the panel
This commit is contained in:
Haileyesus
2026-02-12 17:51:10 +03:00
parent d99c581179
commit ed44d5e437

View File

@@ -51,6 +51,12 @@ function fallbackCopyToClipboard(text) {
return copied; return copied;
} }
const CODEX_DEVICE_AUTH_URL = 'https://auth.openai.com/codex/device';
function isCodexLoginCommand(command) {
return typeof command === 'string' && /\bcodex\s+login\b/i.test(command);
}
function Shell({ selectedProject, selectedSession, initialCommand, isPlainShell = false, onProcessComplete, minimal = false, autoConnect = false }) { function Shell({ selectedProject, selectedSession, initialCommand, isPlainShell = false, onProcessComplete, minimal = false, autoConnect = false }) {
const { t } = useTranslation('chat'); const { t } = useTranslation('chat');
const terminalRef = useRef(null); const terminalRef = useRef(null);
@@ -64,6 +70,7 @@ function Shell({ selectedProject, selectedSession, initialCommand, isPlainShell
const [isConnecting, setIsConnecting] = useState(false); const [isConnecting, setIsConnecting] = useState(false);
const [authUrl, setAuthUrl] = useState(''); const [authUrl, setAuthUrl] = useState('');
const [authUrlCopyStatus, setAuthUrlCopyStatus] = useState('idle'); const [authUrlCopyStatus, setAuthUrlCopyStatus] = useState('idle');
const [isAuthPanelHidden, setIsAuthPanelHidden] = useState(false);
const selectedProjectRef = useRef(selectedProject); const selectedProjectRef = useRef(selectedProject);
const selectedSessionRef = useRef(selectedSession); const selectedSessionRef = useRef(selectedSession);
@@ -144,6 +151,7 @@ function Shell({ selectedProject, selectedSession, initialCommand, isPlainShell
authUrlRef.current = ''; authUrlRef.current = '';
setAuthUrl(''); setAuthUrl('');
setAuthUrlCopyStatus('idle'); setAuthUrlCopyStatus('idle');
setIsAuthPanelHidden(false);
setTimeout(() => { setTimeout(() => {
if (fitAddon.current && terminal.current) { if (fitAddon.current && terminal.current) {
@@ -190,11 +198,13 @@ function Shell({ selectedProject, selectedSession, initialCommand, isPlainShell
authUrlRef.current = data.url; authUrlRef.current = data.url;
setAuthUrl(data.url); setAuthUrl(data.url);
setAuthUrlCopyStatus('idle'); setAuthUrlCopyStatus('idle');
setIsAuthPanelHidden(false);
} else if (data.type === 'url_open') { } else if (data.type === 'url_open') {
if (data.url) { if (data.url) {
authUrlRef.current = data.url; authUrlRef.current = data.url;
setAuthUrl(data.url); setAuthUrl(data.url);
setAuthUrlCopyStatus('idle'); setAuthUrlCopyStatus('idle');
setIsAuthPanelHidden(false);
} }
} }
} catch (error) { } catch (error) {
@@ -206,6 +216,7 @@ function Shell({ selectedProject, selectedSession, initialCommand, isPlainShell
setIsConnected(false); setIsConnected(false);
setIsConnecting(false); setIsConnecting(false);
setAuthUrlCopyStatus('idle'); setAuthUrlCopyStatus('idle');
setIsAuthPanelHidden(false);
if (terminal.current) { if (terminal.current) {
terminal.current.clear(); terminal.current.clear();
@@ -245,6 +256,7 @@ function Shell({ selectedProject, selectedSession, initialCommand, isPlainShell
authUrlRef.current = ''; authUrlRef.current = '';
setAuthUrl(''); setAuthUrl('');
setAuthUrlCopyStatus('idle'); setAuthUrlCopyStatus('idle');
setIsAuthPanelHidden(false);
}, []); }, []);
const sessionDisplayName = useMemo(() => { const sessionDisplayName = useMemo(() => {
@@ -283,6 +295,7 @@ function Shell({ selectedProject, selectedSession, initialCommand, isPlainShell
authUrlRef.current = ''; authUrlRef.current = '';
setAuthUrl(''); setAuthUrl('');
setAuthUrlCopyStatus('idle'); setAuthUrlCopyStatus('idle');
setIsAuthPanelHidden(false);
setTimeout(() => { setTimeout(() => {
setIsRestarting(false); setIsRestarting(false);
@@ -369,17 +382,21 @@ function Shell({ selectedProject, selectedSession, initialCommand, isPlainShell
terminal.current.open(terminalRef.current); terminal.current.open(terminalRef.current);
terminal.current.attachCustomKeyEventHandler((event) => { terminal.current.attachCustomKeyEventHandler((event) => {
const activeAuthUrl = isCodexLoginCommand(initialCommandRef.current)
? CODEX_DEVICE_AUTH_URL
: authUrlRef.current;
if ( if (
event.type === 'keydown' && event.type === 'keydown' &&
minimal && minimal &&
isPlainShellRef.current && isPlainShellRef.current &&
authUrlRef.current && activeAuthUrl &&
!event.ctrlKey && !event.ctrlKey &&
!event.metaKey && !event.metaKey &&
!event.altKey && !event.altKey &&
event.key?.toLowerCase() === 'c' event.key?.toLowerCase() === 'c'
) { ) {
copyAuthUrlToClipboard(authUrlRef.current).catch(() => {}); copyAuthUrlToClipboard(activeAuthUrl).catch(() => {});
} }
if ( if (
@@ -497,18 +514,32 @@ function Shell({ selectedProject, selectedSession, initialCommand, isPlainShell
} }
if (minimal) { if (minimal) {
const hasAuthUrl = Boolean(authUrl); const displayAuthUrl = isCodexLoginCommand(initialCommand)
? CODEX_DEVICE_AUTH_URL
: authUrl;
const hasAuthUrl = Boolean(displayAuthUrl);
const showMobileAuthPanel = hasAuthUrl && !isAuthPanelHidden;
const showMobileAuthPanelToggle = hasAuthUrl && isAuthPanelHidden;
return ( return (
<div className="h-full w-full bg-gray-900 relative"> <div className="h-full w-full bg-gray-900 relative">
<div ref={terminalRef} className="h-full w-full focus:outline-none" style={{ outline: 'none' }} /> <div ref={terminalRef} className="h-full w-full focus:outline-none" style={{ outline: 'none' }} />
{hasAuthUrl && ( {showMobileAuthPanel && (
<div className="absolute inset-x-0 bottom-14 z-20 border-t border-gray-700/80 bg-gray-900/95 p-3 backdrop-blur-sm"> <div className="absolute inset-x-0 bottom-14 z-20 border-t border-gray-700/80 bg-gray-900/95 p-3 backdrop-blur-sm md:hidden">
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
<p className="text-xs text-gray-300">Open or copy the login URL:</p> <div className="flex items-center justify-between gap-2">
<p className="text-xs text-gray-300">Open or copy the login URL:</p>
<button
type="button"
onClick={() => setIsAuthPanelHidden(true)}
className="rounded bg-gray-700 px-2 py-1 text-[10px] font-medium uppercase tracking-wide text-gray-100 hover:bg-gray-600"
>
Hide
</button>
</div>
<input <input
type="text" type="text"
value={authUrl} value={displayAuthUrl}
readOnly readOnly
onClick={(event) => event.currentTarget.select()} onClick={(event) => event.currentTarget.select()}
className="w-full rounded border border-gray-600 bg-gray-800 px-2 py-1 text-xs text-gray-100 focus:outline-none focus:ring-1 focus:ring-blue-500" className="w-full rounded border border-gray-600 bg-gray-800 px-2 py-1 text-xs text-gray-100 focus:outline-none focus:ring-1 focus:ring-blue-500"
@@ -518,7 +549,7 @@ function Shell({ selectedProject, selectedSession, initialCommand, isPlainShell
<button <button
type="button" type="button"
onClick={() => { onClick={() => {
openAuthUrlInBrowser(authUrl); openAuthUrlInBrowser(displayAuthUrl);
}} }}
className="flex-1 rounded bg-blue-600 px-3 py-2 text-xs font-medium text-white hover:bg-blue-700" className="flex-1 rounded bg-blue-600 px-3 py-2 text-xs font-medium text-white hover:bg-blue-700"
> >
@@ -527,7 +558,7 @@ function Shell({ selectedProject, selectedSession, initialCommand, isPlainShell
<button <button
type="button" type="button"
onClick={async () => { onClick={async () => {
const copied = await copyAuthUrlToClipboard(authUrl); const copied = await copyAuthUrlToClipboard(displayAuthUrl);
setAuthUrlCopyStatus(copied ? 'copied' : 'failed'); setAuthUrlCopyStatus(copied ? 'copied' : 'failed');
}} }}
className="flex-1 rounded bg-gray-700 px-3 py-2 text-xs font-medium text-white hover:bg-gray-600" className="flex-1 rounded bg-gray-700 px-3 py-2 text-xs font-medium text-white hover:bg-gray-600"
@@ -538,6 +569,17 @@ function Shell({ selectedProject, selectedSession, initialCommand, isPlainShell
</div> </div>
</div> </div>
)} )}
{showMobileAuthPanelToggle && (
<div className="absolute bottom-14 right-3 z-20 md:hidden">
<button
type="button"
onClick={() => setIsAuthPanelHidden(false)}
className="rounded bg-gray-800/95 px-3 py-2 text-xs font-medium text-gray-100 shadow-lg backdrop-blur-sm hover:bg-gray-700"
>
Show login URL
</button>
</div>
)}
</div> </div>
); );
} }