feat(mobile): mobile app navigation rework and beta-feedback closeout (#2561)
Navigation model rebuilt around two full-width drawers and a minimal header (sessions / title-switcher / usage ring / workspace): - Left sessions drawer: cross-project tree with live status indicators, swipe actions on sessions (rename/archive/delete) and on group headers (project edit / two-step close, worktree delete), reorder-only edit mode with collapsible project cards and draggable worktrees, app-level footer (connected instance, settings, pending web update). - Right workspace drawer: Changes / Files / Terminal / Notes / MCP as pill tabs (inactive tabs icon-only); panes stay mounted once visited. The full desktop file editor serves the Files tab; read/skill tool taps in chat open the file there at the requested line. - Header session switcher on title tap: 10 cross-project recents with live busy/attention indicators and project · branch metadata; the usage ring opens a metadata overlay with an explicit loading state. - The overflow menu is gone on phones (its destinations moved into the drawers); iPad keeps it until its dedicated layout pass. Correctness and continuity: - /auth/session answers bearer-first, so a stale WebView cookie can no longer mask a revoked device token; cold launches classify failures fast and land on an explicit connect screen. - Authoritative session snapshots raise frozen ordering baselines and stale live ranks — recents stay truthful after the app slept. - Cold launches reopen the last active session per instance (persisted pointer, confirmed against a sessions snapshot; a user-opened draft clears it), with a logo hold instead of a draft flash. Also: collapsed pill composer gains the stop control; chat tool rows share one 36px rhythm; Task subtool rows truncate; larger bottom safe area so the composer clears big-screen corner radii; Capacitor build hides About/Update (store updates apply there); widgets link to the sessions drawer with a list icon; MobileApp split into focused modules; five mobile-surface detectors unified; translucent borders normalized to 70%; all new strings translated across the 10 locales. iPad and foldable layouts are intentionally untouched - separate next version PR.
This commit is contained in:
committed by
GitHub
parent
ea8cc5d7b0
commit
86ef96302d
@@ -1,43 +1,5 @@
|
||||
/* Mobile Adaptations - All Devices */
|
||||
|
||||
/* Set global device type variables */
|
||||
:root {
|
||||
--is-mobile: 0;
|
||||
--device-type: 'desktop';
|
||||
--font-scale: 1;
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
:root.mobile-pointer:not(.desktop-runtime) {
|
||||
--is-mobile: 1;
|
||||
--device-type: 'mobile';
|
||||
--font-scale: 0.9;
|
||||
}
|
||||
}
|
||||
|
||||
/* Utility classes for device-specific styling */
|
||||
.desktop-only {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.mobile-only {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
:root.mobile-pointer:not(.desktop-runtime) .desktop-only {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
:root.desktop-runtime .mobile-only {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
:root.mobile-pointer:not(.desktop-runtime) .mobile-only {
|
||||
display: block !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* General mobile improvements for all mobile devices */
|
||||
@media (max-width: 1024px) {
|
||||
/* Override CSS custom properties for mobile */
|
||||
@@ -82,7 +44,10 @@
|
||||
/* Improve touch targets for mobile */
|
||||
:root.mobile-pointer:not(.desktop-runtime) button:not([role="radio"]):not([role="checkbox"]):not([role="switch"]),
|
||||
:root.mobile-pointer:not(.desktop-runtime) .btn,
|
||||
:root.mobile-pointer:not(.desktop-runtime) [role="button"] {
|
||||
:root.mobile-pointer:not(.desktop-runtime) [role="button"],
|
||||
/* Static chat tool rows: not interactive as a whole, but they must share
|
||||
the 36px rhythm of the [role="button"] expandable/reasoning rows. */
|
||||
:root.mobile-pointer:not(.desktop-runtime) .oc-static-tool-row {
|
||||
min-height: 36px;
|
||||
min-width: 36px;
|
||||
}
|
||||
@@ -144,17 +109,6 @@
|
||||
-webkit-touch-callout: default;
|
||||
}
|
||||
|
||||
/* Improve mobile spacing */
|
||||
:root.mobile-pointer:not(.desktop-runtime) .px-4 {
|
||||
padding-left: 1rem !important;
|
||||
padding-right: 1rem !important;
|
||||
}
|
||||
|
||||
:root.mobile-pointer:not(.desktop-runtime) .py-2 {
|
||||
padding-top: 0.75rem !important;
|
||||
padding-bottom: 0.75rem !important;
|
||||
}
|
||||
|
||||
/* Fix mobile scroll containers */
|
||||
:root.mobile-pointer:not(.desktop-runtime) .overflow-hidden {
|
||||
overflow-x: hidden !important;
|
||||
@@ -205,30 +159,6 @@
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
/* Mobile sidebar positioning */
|
||||
:root.mobile-pointer:not(.desktop-runtime) .mobile-sidebar-top {
|
||||
top: var(--header-height, 3rem);
|
||||
height: calc(100vh - var(--header-height, 3rem));
|
||||
}
|
||||
|
||||
/* Set header height variable */
|
||||
:root.mobile-pointer:not(.desktop-runtime) .header-safe-area {
|
||||
--header-height: 3rem;
|
||||
}
|
||||
|
||||
/* For mobile devices with larger header */
|
||||
@media (max-width: 768px) {
|
||||
:root.mobile-pointer:not(.desktop-runtime) .header-safe-area {
|
||||
--header-height: 3.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* For tablet devices */
|
||||
@media (min-width: 769px) and (max-width: 1024px) {
|
||||
:root.mobile-pointer:not(.desktop-runtime) .header-safe-area {
|
||||
--header-height: 3.25rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* iOS PWA Adaptations */
|
||||
@@ -241,7 +171,7 @@
|
||||
--oc-safe-area-top: env(safe-area-inset-top, 0);
|
||||
--oc-safe-area-right: env(safe-area-inset-right, 0);
|
||||
--oc-safe-area-bottom: env(safe-area-inset-bottom, 0);
|
||||
--oc-safe-area-bottom-visual: clamp(0px, calc(var(--oc-safe-area-bottom) * 0.05), 4px);
|
||||
--oc-safe-area-bottom-visual: clamp(0px, calc(var(--oc-safe-area-bottom) * 0.35), 12px);
|
||||
--oc-safe-area-left: env(safe-area-inset-left, 0);
|
||||
}
|
||||
|
||||
@@ -258,25 +188,10 @@
|
||||
|
||||
/* Phase 1: iOS PWA safe area handling - Enhanced positioning approach */
|
||||
@media (display-mode: standalone) {
|
||||
/* iOS-specific safe area handling with CSS variable support */
|
||||
/* iOS-specific safe area handling. The --oc-safe-area-* tokens and the
|
||||
.header-safe-area padding are already set by the unconditional iOS block
|
||||
above; only the standalone-specific rules live here. */
|
||||
@supports (-webkit-touch-callout: none) {
|
||||
/* Safe area handling for fixed positioned elements */
|
||||
:root.device-mobile:not(.desktop-runtime),
|
||||
:root.device-tablet:not(.desktop-runtime),
|
||||
:root.mobile-pointer:not(.desktop-runtime) {
|
||||
--oc-safe-area-top: env(safe-area-inset-top, 0);
|
||||
--oc-safe-area-right: env(safe-area-inset-right, 0);
|
||||
--oc-safe-area-bottom: env(safe-area-inset-bottom, 0);
|
||||
--oc-safe-area-bottom-visual: clamp(0px, calc(var(--oc-safe-area-bottom) * 0.05), 4px);
|
||||
--oc-safe-area-left: env(safe-area-inset-left, 0);
|
||||
}
|
||||
|
||||
:root.device-mobile:not(.desktop-runtime) .header-safe-area,
|
||||
:root.device-tablet:not(.desktop-runtime) .header-safe-area,
|
||||
:root.mobile-pointer:not(.desktop-runtime) .header-safe-area {
|
||||
padding-top: var(--oc-safe-area-top);
|
||||
}
|
||||
|
||||
:root.device-mobile:not(.desktop-runtime) .main-content-safe-area,
|
||||
:root.device-tablet:not(.desktop-runtime) .main-content-safe-area,
|
||||
:root.mobile-pointer:not(.desktop-runtime) .main-content-safe-area {
|
||||
@@ -293,14 +208,6 @@
|
||||
padding-bottom: var(--oc-safe-area-bottom-visual) !important;
|
||||
}
|
||||
|
||||
/* Drawer safe area - top already offset by header height */
|
||||
:root.device-mobile:not(.desktop-runtime) .drawer-safe-area,
|
||||
:root.device-tablet:not(.desktop-runtime) .drawer-safe-area,
|
||||
:root.mobile-pointer:not(.desktop-runtime) .drawer-safe-area {
|
||||
padding-top: 0;
|
||||
padding-bottom: var(--oc-safe-area-bottom-visual);
|
||||
}
|
||||
|
||||
/* Fix iOS viewport issues */
|
||||
:root.device-mobile:not(.desktop-runtime) .flex.flex-col.h-screen,
|
||||
:root.device-tablet:not(.desktop-runtime) .flex.flex-col.h-screen,
|
||||
@@ -318,19 +225,6 @@
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
/* Update header height for iOS */
|
||||
:root.device-mobile:not(.desktop-runtime) .header-safe-area,
|
||||
:root.device-tablet:not(.desktop-runtime) .header-safe-area,
|
||||
:root.mobile-pointer:not(.desktop-runtime) .header-safe-area {
|
||||
--header-height: 4.5rem;
|
||||
}
|
||||
|
||||
:root.device-mobile:not(.desktop-runtime) .mobile-sidebar-top,
|
||||
:root.device-tablet:not(.desktop-runtime) .mobile-sidebar-top,
|
||||
:root.mobile-pointer:not(.desktop-runtime) .mobile-sidebar-top {
|
||||
--header-height: 4.5rem;
|
||||
}
|
||||
|
||||
/* Mobile typography improvements */
|
||||
.typography-markdown {
|
||||
font-size: 1rem !important;
|
||||
@@ -370,7 +264,7 @@
|
||||
--oc-safe-area-top: env(safe-area-inset-top, 0);
|
||||
--oc-safe-area-right: env(safe-area-inset-right, 0);
|
||||
--oc-safe-area-bottom: env(safe-area-inset-bottom, 0);
|
||||
--oc-safe-area-bottom-visual: clamp(0px, calc(var(--oc-safe-area-bottom) * 0.05), 4px);
|
||||
--oc-safe-area-bottom-visual: clamp(0px, calc(var(--oc-safe-area-bottom) * 0.35), 12px);
|
||||
--oc-safe-area-left: env(safe-area-inset-left, 0);
|
||||
}
|
||||
|
||||
@@ -537,7 +431,7 @@
|
||||
--oc-safe-area-top: var(--safe-area-inset-top, env(safe-area-inset-top, 0px));
|
||||
--oc-safe-area-right: var(--safe-area-inset-right, env(safe-area-inset-right, 0px));
|
||||
--oc-safe-area-bottom: var(--safe-area-inset-bottom, env(safe-area-inset-bottom, 0px));
|
||||
--oc-safe-area-bottom-visual: clamp(0px, calc(var(--oc-safe-area-bottom) * 0.05), 4px);
|
||||
--oc-safe-area-bottom-visual: clamp(0px, calc(var(--oc-safe-area-bottom) * 0.35), 12px);
|
||||
--oc-safe-area-left: var(--safe-area-inset-left, env(safe-area-inset-left, 0px));
|
||||
}
|
||||
|
||||
@@ -784,7 +678,7 @@
|
||||
collapsing browser chrome, so the dynamic and large viewports are the same
|
||||
thing — pin the shell to 100lvh, which always reports full screen height.
|
||||
Initial geometry is identical (lvh == dvh on load). */
|
||||
:root:not(.oc-capacitor-app) .h-\[100dvh\] {
|
||||
:root:not(.oc-capacitor-app) .oc-mobile-app-shell {
|
||||
height: 100lvh;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user