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:
archipelago
2026-07-23 15:31:02 -04:00
co-authored by Claude Fable 5
parent 960e41e164
commit 9f1daa0f8c
5 changed files with 37 additions and 7 deletions
+7 -6
View File
@@ -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')
}
}