mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-02-24 01:27:42 +00:00
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:
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user