From b839b059d0ed8f127437cec582ad1d48bb983f3a Mon Sep 17 00:00:00 2001 From: duffyduck Date: Fri, 17 Jul 2026 14:46:49 +0200 Subject: [PATCH] =?UTF-8?q?fix(app):=20Cockpit=20=E2=80=94=20alle=20Kachel?= =?UTF-8?q?n=20sichtbar=20+=20Uebersicht-Button=20in=20den=20Header?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Zwei Nachbesserungen am Cockpit-Modus (noch Teil von 0.2.1.9): - Im Cockpit werden IMMER alle vier Kacheln gezeigt (Chat/Editor/Desktop/ Vorschau) statt erst bei Code-Projekten — Editor/Desktop/Vorschau als Platzhalter mit Status-Untertitel. Vorher wirkten sie "verschwunden". - Der "⤢ Uebersicht"-Button hing im Chat genau ueber dem Abbrechen-Button der "ARIA denkt"-Leiste. Jetzt sitzt er im Navigations-Header links (nur sichtbar im Cockpit + Fokus), via neuem cockpitNav-Signal-Singleton. In-Content-Button entfernt. tsc clean. Co-Authored-By: Claude Opus 4.8 --- CHANGELOG.md | 3 +- android/App.tsx | 2 + .../src/components/CockpitOverviewButton.tsx | 48 +++++++++++++++++++ android/src/services/cockpitNav.ts | 43 +++++++++++++++++ android/src/workspace/WorkspaceCanvas.tsx | 33 +++++-------- android/src/workspace/WorkspaceScreen.tsx | 24 ++++++---- 6 files changed, 122 insertions(+), 31 deletions(-) create mode 100644 android/src/components/CockpitOverviewButton.tsx create mode 100644 android/src/services/cockpitNav.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a5b017..78348e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,8 @@ Alle Änderungen am Projekt. Format: [Keep a Changelog](https://keepachangelog.c - **Ansichts-Umschalter im Header** („⧉ Kompakt" / „⧉ Cockpit"): Die App startet in **Kompakt** — der klassische Vollbild-Chat, **exakt wie vor dem Umbau** (Default, Mama-tauglich). Ein Tap auf den Button oben rechts schaltet auf **Cockpit** — den zoom-/verschiebbaren Kachel-Desktop. Persistiert über Neustart (`aria_view_mode`). - **Warum:** Nach dem 0.2.1.8-Deploy sah die App „unverändert" aus — korrekt, denn im normalen Chat gibt es nur eine Kachel (= Vollbild-Chat). Der Umschalter macht den Cockpit-Modus jetzt **explizit sichtbar/steuerbar**, statt nur bei Code-Projekten aufzutauchen. -- Im Cockpit ist die **Übersicht jetzt immer erreichbar** (auch im Hauptchat mit nur einer Kachel): „⤢ Übersicht"-Button (unten rechts, weg von den Chat-Kopf-Icons), 2-Finger-Pinch/Pan, Hardware-Back. +- Im Cockpit ist die **Übersicht jetzt immer erreichbar** (auch im Hauptchat): „⤢ Übersicht" sitzt **im Header links** (kollidiert nicht mehr mit dem Abbrechen-Button der „ARIA denkt"-Leiste), dazu 2-Finger-Pinch/Pan und Hardware-Back. +- Im Cockpit werden **immer alle vier Kacheln** gezeigt (Chat/Editor/Desktop/Vorschau) — Editor/Desktop/Vorschau als Platzhalter mit Status-Untertitel („kein Code-Projekt" / „kein Desktop"), bis ARIA ein Code-Projekt startet bzw. eine VM läuft. Vorher wirkten sie „verschwunden", weil sie erst bei Code-Projekten auftauchten. --- diff --git a/android/App.tsx b/android/App.tsx index 2baf3f8..99dddd7 100644 --- a/android/App.tsx +++ b/android/App.tsx @@ -15,6 +15,7 @@ import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; import WorkspaceScreen from './src/workspace/WorkspaceScreen'; import SettingsScreen from './src/screens/SettingsScreen'; import ViewModeToggle from './src/components/ViewModeToggle'; +import CockpitOverviewButton from './src/components/CockpitOverviewButton'; import rvs from './src/services/rvs'; import { initLogger, installGlobalCrashReporter } from './src/services/logger'; import { acquireBackgroundAudio } from './src/services/backgroundAudio'; @@ -171,6 +172,7 @@ const App: React.FC = () => { options={{ title: 'ARIA Chat', headerTitle: 'ARIA Cockpit', + headerLeft: () => , headerRight: () => , }} /> diff --git a/android/src/components/CockpitOverviewButton.tsx b/android/src/components/CockpitOverviewButton.tsx new file mode 100644 index 0000000..38e5ba0 --- /dev/null +++ b/android/src/components/CockpitOverviewButton.tsx @@ -0,0 +1,48 @@ +/** + * CockpitOverviewButton — Header-Button (links) für „⤢ Übersicht". + * + * Nur sichtbar im Cockpit-Modus, wenn gerade eine Kachel fokussiert ist. Sitzt + * im Navigations-Header, kollidiert also mit nichts im Chat (früher hing er über + * dem Abbrechen-Button der „ARIA denkt"-Leiste). + */ + +import React, { useEffect, useState } from 'react'; +import { StyleSheet, Text, TouchableOpacity } from 'react-native'; +import cockpitNav from '../services/cockpitNav'; +import viewMode, { ViewModeValue } from '../services/viewMode'; + +const CockpitOverviewButton: React.FC = () => { + const [mode, setMode] = useState(viewMode.get()); + const [focused, setFocused] = useState(cockpitNav.isFocused()); + + useEffect(() => viewMode.subscribe(setMode), []); + useEffect(() => cockpitNav.subscribe(setFocused), []); + + if (mode !== 'cockpit' || !focused) return null; + + return ( + cockpitNav.requestOverview()} + style={styles.pill} + hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }} + activeOpacity={0.75} + > + ⤢ Übersicht + + ); +}; + +const styles = StyleSheet.create({ + pill: { + marginLeft: 12, + paddingHorizontal: 12, + paddingVertical: 6, + borderRadius: 16, + borderWidth: 1, + borderColor: '#0096FF', + backgroundColor: '#0A1F33', + }, + text: { color: '#0096FF', fontSize: 13, fontWeight: '700' }, +}); + +export default CockpitOverviewButton; diff --git a/android/src/services/cockpitNav.ts b/android/src/services/cockpitNav.ts new file mode 100644 index 0000000..315cafe --- /dev/null +++ b/android/src/services/cockpitNav.ts @@ -0,0 +1,43 @@ +/** + * cockpitNav — kleine Brücke zwischen WorkspaceCanvas (Fokus-Zustand) und dem + * Navigations-Header, damit der „⤢ Übersicht"-Button im Header sitzen kann + * (kollidiert dort mit nichts im Chat) statt im überfüllten Chat-Inhalt. + * + * Der Canvas meldet, ob gerade eine Kachel fokussiert ist, und registriert eine + * Aktion zum Zurück-zur-Übersicht. Der Header abonniert den Zustand und ruft die + * Aktion. Muster wie services/rvs.ts (Singleton). + */ + +type Sub = (focused: boolean) => void; + +class CockpitNav { + private focused = false; + private overviewHandler: (() => void) | null = null; + private subs: Sub[] = []; + + isFocused(): boolean { return this.focused; } + + setFocused(f: boolean): void { + if (this.focused === f) return; + this.focused = f; + this.subs.forEach((cb) => cb(f)); + } + + /** Canvas registriert seine „zur Übersicht"-Aktion (null beim Unmount). */ + registerOverview(fn: (() => void) | null): void { + this.overviewHandler = fn; + } + + requestOverview(): void { + this.overviewHandler?.(); + } + + subscribe(cb: Sub): () => void { + this.subs.push(cb); + cb(this.focused); + return () => { this.subs = this.subs.filter((s) => s !== cb); }; + } +} + +const cockpitNav = new CockpitNav(); +export default cockpitNav; diff --git a/android/src/workspace/WorkspaceCanvas.tsx b/android/src/workspace/WorkspaceCanvas.tsx index 04394f0..54db78d 100644 --- a/android/src/workspace/WorkspaceCanvas.tsx +++ b/android/src/workspace/WorkspaceCanvas.tsx @@ -17,13 +17,14 @@ */ import React, { useEffect, useMemo, useState } from 'react'; -import { BackHandler, StyleSheet, Text, TouchableOpacity, useWindowDimensions, View } from 'react-native'; +import { BackHandler, StyleSheet, Text, useWindowDimensions, View } from 'react-native'; import Animated, { useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated'; import { Gesture, GestureDetector } from 'react-native-gesture-handler'; import { boundsOf, Camera, focusCamera, overviewCamera, TileId, TILE_RECTS, toLocal, } from './layout'; +import cockpitNav from '../services/cockpitNav'; import Tile from './Tile'; import { useWorkspaceLayout } from './useWorkspaceLayout'; import ChatTile from './tiles/ChatTile'; @@ -96,6 +97,14 @@ const WorkspaceCanvas: React.FC = ({ projectId, visibleTiles, subtitles } // eslint-disable-next-line react-hooks/exhaustive-deps }, [focusedTileId, visibleKey, vw, vh]); + // Fokus-Zustand + „zur Uebersicht"-Aktion an den Header (cockpitNav) melden, + // damit der Uebersicht-Button dort sitzt (kollidiert nicht mit dem Chat). + useEffect(() => { + cockpitNav.setFocused(focusedTileId != null); + cockpitNav.registerOverview(() => setFocusedTileId(null)); + return () => { cockpitNav.registerOverview(null); cockpitNav.setFocused(false); }; + }, [focusedTileId]); + // Hardware-Back: im Fokus → zurueck zur Uebersicht. useEffect(() => { const onBack = () => { @@ -188,12 +197,8 @@ const WorkspaceCanvas: React.FC = ({ projectId, visibleTiles, subtitles } ))} - {/* Steuerung */} - {focusedTileId && ( - setFocusedTileId(null)} activeOpacity={0.8}> - ⤢ Übersicht - - )} + {/* Steuerung — der „⤢ Übersicht"-Button sitzt im Header (cockpitNav), + nicht hier im Chat-Inhalt. */} {!focusedTileId && ( Kachel antippen zum Öffnen · 2 Finger: zoomen & schieben @@ -206,20 +211,6 @@ const WorkspaceCanvas: React.FC = ({ projectId, visibleTiles, subtitles } const styles = StyleSheet.create({ root: { flex: 1, backgroundColor: '#0D0D1A' }, world: { position: 'absolute', left: 0, top: 0 }, - // Unten rechts (ueber der Chat-Eingabezeile) — weg von ChatScreens Kopf-Icons. - overviewBtn: { - position: 'absolute', - bottom: 88, - right: 14, - backgroundColor: 'rgba(18,18,42,0.92)', - borderColor: '#0096FF', - borderWidth: 1, - borderRadius: 18, - paddingHorizontal: 14, - paddingVertical: 8, - elevation: 4, - }, - overviewBtnText: { color: '#0096FF', fontSize: 14, fontWeight: '700' }, hintBar: { position: 'absolute', bottom: 16, diff --git a/android/src/workspace/WorkspaceScreen.tsx b/android/src/workspace/WorkspaceScreen.tsx index dae99dc..72e5974 100644 --- a/android/src/workspace/WorkspaceScreen.tsx +++ b/android/src/workspace/WorkspaceScreen.tsx @@ -16,7 +16,7 @@ import codeFile from '../services/codeFile'; import desktop from '../services/desktop'; import viewMode, { ViewModeValue } from '../services/viewMode'; import ChatScreen from '../screens/ChatScreen'; -import { TileId, visibleTilesFor } from './layout'; +import { TileId, TILE_ORDER } from './layout'; import WorkspaceCanvas from './WorkspaceCanvas'; const WorkspaceScreen: React.FC = () => { @@ -47,15 +47,21 @@ const WorkspaceScreen: React.FC = () => { return unsub; }, [pid, kind]); - const visibleTiles: TileId[] = useMemo( - () => visibleTilesFor({ kind, hasCode, hasDesktop }), - [kind, hasCode, hasDesktop], - ); + // Im Cockpit immer alle Kacheln zeigen (Editor/Desktop/Vorschau als + // Platzhalter, bis ein Code-Projekt/VM aktiv ist) — sonst „fehlen" sie + // gefuehlt. isCode blendet nur die Untertitel-Info um. + const visibleTiles: TileId[] = TILE_ORDER; + const isCode = kind === 'code' || hasCode || hasDesktop; - const subtitles = useMemo( - () => ({ chat: pid ? projectFocus.getProjectName(pid) : 'Hauptchat' } as Partial>), - [pid, focus.projectNameById], - ); + const subtitles = useMemo(() => { + const fileCount = codeFile.getFiles(pid).length; + return { + chat: pid ? projectFocus.getProjectName(pid) : 'Hauptchat', + editor: isCode ? `${fileCount} Datei${fileCount === 1 ? '' : 'en'}` : 'kein Code-Projekt', + vnc: hasDesktop ? 'verbunden' : 'kein Desktop', + preview: undefined, + } as Partial>; + }, [pid, focus.projectNameById, isCode, hasCode, hasDesktop]); // Kompakt-Ansicht: klassischer Vollbild-Chat, exakt wie vor dem Umbau. if (mode === 'compact') {