always there fullscreen button

This commit is contained in:
legop3
2026-03-23 02:31:59 -04:00
parent 626b48f777
commit ad1b6b48f8
8 changed files with 84 additions and 15 deletions
+22
View File
@@ -22,6 +22,7 @@ import TurnAlertListener from './components/TurnAlertListener.jsx';
import RawUserPilePanel from './components/RawUserPilePanel.jsx';
import ChatPanel from './components/ChatPanel.jsx';
import FullscreenPrompt from './components/FullscreenPrompt.jsx';
import FloatingFullscreenButton from './components/FloatingFullscreenButton.jsx';
import { useFullscreenPrompt } from './hooks/useFullscreenPrompt.js';
import { useSettingsNamespace } from './settings/index.js';
import HelpOverlay from './components/HelpOverlay.jsx';
@@ -231,8 +232,11 @@ function AppWithProviders({ layout, isDesktop, fullscreen }) {
const {
visible: fullscreenVisible,
mode: fullscreenMode,
isIOS: fullscreenIsIOS,
nativeSupported: fullscreenNativeSupported,
enterFullscreen,
dismiss,
showPrompt,
} = fullscreen;
const {
@@ -244,6 +248,8 @@ function AppWithProviders({ layout, isDesktop, fullscreen }) {
swapMobileControlColumns: false,
});
const swapMobileControlColumns = Boolean(pageSettings?.swapMobileControlColumns);
const fullscreenButtonSide = swapMobileControlColumns ? 'left' : 'right';
const showFloatingFullscreenButton = !isDesktop && (fullscreenIsIOS || fullscreenNativeSupported);
const [helpVisible, setHelpVisible] = useState(false);
useEffect(() => {
@@ -254,6 +260,16 @@ function AppWithProviders({ layout, isDesktop, fullscreen }) {
const openHelp = useCallback(() => setHelpVisible(true), []);
const closeHelp = useCallback(() => setHelpVisible(false), []);
const handleFloatingFullscreen = useCallback(async () => {
if (fullscreenIsIOS) {
showPrompt();
return;
}
const entered = await enterFullscreen();
if (!entered) {
showPrompt();
}
}, [enterFullscreen, fullscreenIsIOS, showPrompt]);
const setShowOnLoad = useCallback(
(enabled) => {
const next = Boolean(enabled);
@@ -299,6 +315,12 @@ function AppWithProviders({ layout, isDesktop, fullscreen }) {
onEnterFullscreen={enterFullscreen}
onDismiss={dismiss}
/>
{showFloatingFullscreenButton ? (
<FloatingFullscreenButton
side={fullscreenButtonSide}
onClick={handleFloatingFullscreen}
/>
) : null}
</ControlSystemProvider>
);
}
@@ -0,0 +1,29 @@
export default function FloatingFullscreenButton({ side = 'right', onClick }) {
const sideClass = side === 'left' ? 'left-2' : 'right-2';
return (
<button
type="button"
onClick={onClick}
className={`fixed bottom-2 ${sideClass} z-30 flex h-10 w-10 items-center justify-center rounded-full border border-cyan-200/50 bg-slate-900/70 text-cyan-100 shadow-lg backdrop-blur-sm transition hover:bg-slate-800/80 active:scale-95`}
aria-label="Enter fullscreen"
title="Enter fullscreen"
>
<svg
viewBox="0 0 24 24"
width="18"
height="18"
aria-hidden="true"
className="pointer-events-none"
>
<path
d="M4 9V4h5M15 4h5v5M20 15v5h-5M9 20H4v-5"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</button>
);
}
+20 -2
View File
@@ -53,14 +53,17 @@ export function useFullscreenPrompt(layout) {
const isMobileLayout = MOBILE_LAYOUTS.has(layout);
const [visible, setVisible] = useState(false);
const [mode, setMode] = useState('native');
const [forceVisible, setForceVisible] = useState(false);
const isIOS = useMemo(() => detectIOS(), []);
const nativeSupported = useMemo(() => supportsFullscreen(), []);
const reevaluate = useCallback(() => {
if (!isMobileLayout) {
setVisible(false);
setForceVisible(false);
return;
}
if (isDismissed()) {
if (!forceVisible && isDismissed()) {
setVisible(false);
return;
}
@@ -79,7 +82,7 @@ export function useFullscreenPrompt(layout) {
}
setMode('native');
setVisible(true);
}, [isIOS, isMobileLayout]);
}, [forceVisible, isIOS, isMobileLayout]);
useEffect(() => {
reevaluate();
@@ -89,6 +92,7 @@ export function useFullscreenPrompt(layout) {
if (typeof document === 'undefined') return undefined;
const handler = () => {
if (document.fullscreenElement) {
setForceVisible(false);
setVisible(false);
return;
}
@@ -104,12 +108,14 @@ export function useFullscreenPrompt(layout) {
const dismiss = useCallback(() => {
markDismissed();
setForceVisible(false);
setVisible(false);
}, []);
const enterFullscreen = useCallback(async () => {
if (!isMobileLayout || isIOS) return false;
if (typeof document === 'undefined') return false;
if (document.fullscreenElement) return true;
const element = document.documentElement;
if (!element) return false;
const request =
@@ -124,6 +130,7 @@ export function useFullscreenPrompt(layout) {
await result;
}
markDismissed();
setForceVisible(false);
setVisible(false);
return true;
} catch (error) {
@@ -132,8 +139,16 @@ export function useFullscreenPrompt(layout) {
}
}, [isIOS, isMobileLayout]);
const showPrompt = useCallback(() => {
if (!isMobileLayout) return;
setForceVisible(true);
setMode(isIOS ? 'pwa-hint' : 'native');
setVisible(true);
}, [isIOS, isMobileLayout]);
useEffect(() => {
if (!isMobileLayout) {
setForceVisible(false);
setVisible(false);
}
}, [isMobileLayout]);
@@ -141,8 +156,11 @@ export function useFullscreenPrompt(layout) {
return {
visible,
mode,
isIOS,
nativeSupported,
enterFullscreen,
dismiss,
showPrompt,
};
}