feat(ui): mobile mesh tabs, AIUI-style audio player, cloud grid + map fixes

UI (this session):
- Global audio player now scales the whole interface into the space above it
  on desktop (sidebar + main) and docks directly above the tab bar on mobile;
  it stays visible while navigating.
- Mesh mobile redesign: floating Chat / BTC / Dead Man / AI / Map tab strip
  with a single fixed, internally-scrolling pane (page no longer scrolls);
  tabs hide while a conversation is open; floating back button; collapsible
  Device panel (starts collapsed); keyboard-aware conversation sizing via
  VisualViewport so the chat sits just above the keyboard.
- Cloud file grid: uniform 4/3 card heights (folders + images match).
- Swipe left/right switches tabs on the Apps and Web5 screens.
- Map tool fills its pane (no bottom gap); fix skewed Share Location toggle
  on mobile (global min-height rule was deforming the switch).
- Trim redundant helper copy from the mesh AI tab.

Also bundles pre-existing in-progress work that was already in the tree:
mesh listener/session + wallet + container + bitcoin-status backend changes,
docker UI updates, and assorted other UI tweaks.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
archipelago
2026-06-19 09:52:26 -04:00
co-authored by Claude Opus 4.8
parent c4855526fe
commit 1bce694ebb
37 changed files with 1260 additions and 208 deletions
+59 -21
View File
@@ -95,9 +95,11 @@
/>
<!-- Re-key on the current folder path so the depth/zoom animation replays
at every level (folder subfolder ), not just on first entry.
Only the file content zooms; the header + breadcrumb nav above stay
fixed in place. -->
<Transition name="cloud-zoom" mode="out-in">
The transition name flips with navigation direction so descending
zooms forward and going back up zooms in reverse matching the
cloud folder route transition. Only the file content zooms; the
header + breadcrumb nav above stay fixed in place. -->
<Transition :name="folderTransition" mode="out-in">
<FileGrid
:key="cloudStore.currentPath"
:items="cloudStore.sortedItems"
@@ -177,6 +179,19 @@ const cloudStore = useCloudStore()
const viewMode = ref<'list' | 'grid'>('grid')
const audioPlayer = useAudioPlayer()
// Direction-aware folder zoom: descending into a subfolder plays the same
// "depth-forward" feel as the cloud → folder route transition (new arrives from
// depth, current zooms out toward the viewer); navigating back up plays its
// mirror ("depth-back"). Picked by comparing folder depth on each path change.
const folderTransition = ref<'cloud-zoom-forward' | 'cloud-zoom-back'>('cloud-zoom-forward')
let prevFolderDepth = -1
watch(() => cloudStore.currentPath, (path) => {
const depth = path.split('/').filter(Boolean).length
// First render (prevFolderDepth === -1) defaults to forward.
folderTransition.value = depth < prevFolderDepth ? 'cloud-zoom-back' : 'cloud-zoom-forward'
prevFolderDepth = depth
})
const iframeLoaded = ref(false)
const uploading = ref(false)
const folderId = computed(() => route.params.folderId as string)
@@ -395,37 +410,60 @@ function goBack() {
</script>
<!-- Not scoped: the transition classes are applied to the FileGrid child's root
element, which lives outside this component's style scope. Matches the
`depth-forward` route transition feel (zoom in from depth + blur). -->
element, which lives outside this component's style scope. Mirrors the
`depth-forward` / `depth-back` route transitions (same scale magnitudes +
blur) so descending into a folder and going back up feel identical to the
cloud folder route change. -->
<style>
.cloud-zoom-enter-active,
.cloud-zoom-leave-active {
.cloud-zoom-forward-enter-active,
.cloud-zoom-forward-leave-active,
.cloud-zoom-back-enter-active,
.cloud-zoom-back-leave-active {
transition:
opacity 0.38s cubic-bezier(0.25, 0.46, 0.45, 0.94),
transform 0.38s cubic-bezier(0.25, 0.46, 0.45, 0.94),
filter 0.38s cubic-bezier(0.25, 0.46, 0.45, 0.94);
opacity 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94),
transform 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94),
filter 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94);
transform-origin: center center;
will-change: opacity, transform, filter;
}
/* New folder zooms in from depth */
.cloud-zoom-enter-from {
/* Forward (into a deeper folder): new folder arrives from depth while the
current one zooms out toward the viewer — matches depth-forward. */
.cloud-zoom-forward-enter-from {
opacity: 0;
transform: scale(0.82);
transform: scale(0.75);
filter: blur(4px);
}
/* Previous folder recedes forward as it leaves */
.cloud-zoom-leave-to {
.cloud-zoom-forward-leave-to {
opacity: 0;
transform: scale(1.12);
filter: blur(6px);
transform: scale(1.2);
filter: blur(8px);
}
/* Back (up to a parent folder): the mirror — new folder shrinks in from the
front while the current one recedes into depth — matches depth-back. */
.cloud-zoom-back-enter-from {
opacity: 0;
transform: scale(1.2);
filter: blur(8px);
}
.cloud-zoom-back-leave-to {
opacity: 0;
transform: scale(0.75);
filter: blur(4px);
}
@media (prefers-reduced-motion: reduce) {
.cloud-zoom-enter-active,
.cloud-zoom-leave-active {
.cloud-zoom-forward-enter-active,
.cloud-zoom-forward-leave-active,
.cloud-zoom-back-enter-active,
.cloud-zoom-back-leave-active {
transition: opacity 0.2s ease;
}
.cloud-zoom-enter-from,
.cloud-zoom-leave-to {
.cloud-zoom-forward-enter-from,
.cloud-zoom-forward-leave-to,
.cloud-zoom-back-enter-from,
.cloud-zoom-back-leave-to {
transform: none;
filter: none;
}