feat(ui): per-app display-mode defaults (IndeeHub fullscreen) + gamepad plays media + node list scales
- Per-app default display mode: explicit user pick (remembered per app) -> app default -> last global -> panel; IndeeHub defaults to fullscreen. - data-controller-primary: a media file card's gamepad-select clicks the card (audio -> bottom-bar player, video -> lightbox) instead of the card's download link. - Discovered-nodes list scales with the viewport (max(14rem,45vh)) instead of cramming a long list into a fixed 224px box. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
960e41e164
commit
9f1daa0f8c
@ -2,6 +2,7 @@
|
||||
<button
|
||||
class="cloud-file-item group"
|
||||
data-controller-container
|
||||
data-controller-primary
|
||||
tabindex="0"
|
||||
@click="handleClick"
|
||||
>
|
||||
|
||||
@ -364,6 +364,15 @@ export function useControllerNav(containerRef?: { value: HTMLElement | null }) {
|
||||
e.preventDefault()
|
||||
|
||||
if (isContainer(activeEl)) {
|
||||
// Container declares its own click as THE action (e.g. a media file
|
||||
// card whose click plays it) — without this the a[href] fallback
|
||||
// below hits the card's download link and gamepad-select downloads
|
||||
// a song instead of playing it.
|
||||
if (activeEl.hasAttribute('data-controller-primary')) {
|
||||
playNavSound('action')
|
||||
activeEl.click()
|
||||
return
|
||||
}
|
||||
// Prioritised action: install button
|
||||
if (activeEl.hasAttribute('data-controller-install')) {
|
||||
const btn = activeEl.querySelector<HTMLButtonElement>('[data-controller-install-btn]:not([disabled])')
|
||||
|
||||
@ -103,7 +103,7 @@ import AppSessionFrame from './appSession/AppSessionFrame.vue'
|
||||
import MobileGamepad from './appSession/MobileGamepad.vue'
|
||||
import {
|
||||
type DisplayMode, DISPLAY_MODE_KEY, NEW_TAB_APPS, IFRAME_BLOCKED_APPS,
|
||||
resolveAppUrl, resolveAppTitle,
|
||||
initialDisplayMode, resolveAppUrl, resolveAppTitle,
|
||||
} from './appSession/appSessionConfig'
|
||||
import { launchBlockedReason, resolveAppIcon } from './apps/appsConfig'
|
||||
import { useAppIdentity } from './appSession/useAppIdentity'
|
||||
@ -142,11 +142,6 @@ let loadTimeoutId: ReturnType<typeof setTimeout> | null = null
|
||||
let autoRetryId: ReturnType<typeof setTimeout> | null = null
|
||||
let iframeCheckId: ReturnType<typeof setTimeout> | null = null
|
||||
|
||||
// Display mode -- persisted in localStorage
|
||||
const displayMode = ref<DisplayMode>(
|
||||
(localStorage.getItem(DISPLAY_MODE_KEY) as DisplayMode) || 'panel'
|
||||
)
|
||||
|
||||
const appId = computed(() => {
|
||||
const id = props.appIdProp || (route.params.appId as string)
|
||||
if (typeof id !== 'string' || !/^[a-z0-9][a-z0-9._-]*$/.test(id) || id.length > 64) {
|
||||
@ -156,6 +151,9 @@ const appId = computed(() => {
|
||||
return id
|
||||
})
|
||||
|
||||
// Display mode -- per-app user choice → per-app default → last global → panel
|
||||
const displayMode = ref<DisplayMode>(initialDisplayMode(appId.value))
|
||||
|
||||
const appTitle = computed(() => resolveAppTitle(appId.value))
|
||||
const packageEntry = computed(() => store.data?.['package-data']?.[appId.value] || null)
|
||||
const appIcon = computed(() =>
|
||||
@ -231,6 +229,8 @@ function setMode(mode: DisplayMode) {
|
||||
}
|
||||
displayMode.value = mode
|
||||
localStorage.setItem(DISPLAY_MODE_KEY, mode)
|
||||
// Remember the explicit pick for this app so it wins over the app default.
|
||||
if (appId.value) localStorage.setItem(`${DISPLAY_MODE_KEY}:${appId.value}`, mode)
|
||||
|
||||
// Route-based sessions (deep links) hand off to the store-driven session so
|
||||
// the app keeps floating above the dashboard instead of owning the route.
|
||||
@ -367,6 +367,7 @@ function onFullscreenChange() {
|
||||
if (!document.fullscreenElement && displayMode.value === 'fullscreen') {
|
||||
displayMode.value = 'overlay'
|
||||
localStorage.setItem(DISPLAY_MODE_KEY, 'overlay')
|
||||
if (appId.value) localStorage.setItem(`${DISPLAY_MODE_KEY}:${appId.value}`, 'overlay')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -7,6 +7,23 @@ export type DisplayMode = 'panel' | 'overlay' | 'fullscreen'
|
||||
|
||||
export const DISPLAY_MODE_KEY = 'archipelago_app_display_mode'
|
||||
|
||||
/** Per-app default display mode. Used when the user hasn't explicitly picked
|
||||
* a mode for that app (an explicit pick is remembered per app and wins).
|
||||
* Apps not listed fall back to the last globally-used mode, then 'panel'. */
|
||||
export const APP_DEFAULT_DISPLAY_MODE: Record<string, DisplayMode> = {
|
||||
'indeedhub': 'fullscreen',
|
||||
}
|
||||
|
||||
/** Initial display mode for an app session: per-app user choice → per-app
|
||||
* default → last global choice → panel. */
|
||||
export function initialDisplayMode(id: string): DisplayMode {
|
||||
const perApp = localStorage.getItem(`${DISPLAY_MODE_KEY}:${id}`) as DisplayMode | null
|
||||
if (perApp === 'panel' || perApp === 'overlay' || perApp === 'fullscreen') return perApp
|
||||
const appDefault = APP_DEFAULT_DISPLAY_MODE[id]
|
||||
if (appDefault) return appDefault
|
||||
return (localStorage.getItem(DISPLAY_MODE_KEY) as DisplayMode) || 'panel'
|
||||
}
|
||||
|
||||
/** Container apps: manifest-generated launch ports plus overrides for companions and aliases. */
|
||||
export const APP_PORTS: Record<string, number> = {
|
||||
...GENERATED_APP_PORTS,
|
||||
|
||||
@ -73,7 +73,9 @@
|
||||
<div v-else-if="discoveredNodes.length === 0" class="py-4 text-center text-white/40 text-xs">
|
||||
No discoverable nodes found yet. Nodes appear here as relays gossip their presence.
|
||||
</div>
|
||||
<div v-else class="space-y-2 max-h-56 overflow-y-auto pr-1">
|
||||
<!-- min 14rem, otherwise scale with the viewport so a long discovery
|
||||
list uses the screen instead of cramming into a fixed 224px box -->
|
||||
<div v-else class="space-y-2 max-h-[max(14rem,45vh)] overflow-y-auto pr-1">
|
||||
<div
|
||||
v-for="node in discoveredNodes"
|
||||
:key="node.nostr_pubkey"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user