Visueller Renderer fuer aria_view: Orb (reanimated-Puls je Zustand), CardView (text/image/list/map/code, Sci-Fi-Chrome), AriaViewCanvas (pannbare Flaeche, 2-Finger-Pan + Pinch, Karten materialisieren gestaffelt). WorkspaceScreen blendet die Flaeche als Overlay ueber Chat/Cockpit ein, sobald ARIA fuers fokussierte Projekt eine Ansicht komponiert. v1/Vorgeschmack, tsc-clean; Markdown=Klartext, Map=Marker-Liste. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
103 lines
3.6 KiB
TypeScript
103 lines
3.6 KiB
TypeScript
/**
|
|
* WorkspaceScreen — Screen-Wrapper fuer die Workbench.
|
|
*
|
|
* Kompakt-Modus → klassischer Vollbild-Chat (wie vor dem Umbau).
|
|
* Cockpit-Modus → Workbench mit Taskleisten-Dock: Chat · Code · Desktop.
|
|
*
|
|
* Aktivitaets-Badges am Dock: Editor blau, wenn schon Code-Dateien da sind;
|
|
* Desktop gruen NUR, wenn im aktiven Projekt eine VM laeuft.
|
|
*/
|
|
|
|
import React, { useEffect, useMemo, useState } from 'react';
|
|
import { View } from 'react-native';
|
|
import projectFocus, { FocusSnapshot } from '../services/projectFocus';
|
|
import codeFile from '../services/codeFile';
|
|
import brainApi from '../services/brainApi';
|
|
import viewMode, { ViewModeValue } from '../services/viewMode';
|
|
import ChatScreen from '../screens/ChatScreen';
|
|
import { TileId } from './layout';
|
|
import WorkspaceDeck from './WorkspaceDeck';
|
|
import ariaView, { AriaView } from '../services/ariaView';
|
|
import AriaViewCanvas from './AriaViewCanvas';
|
|
|
|
const COCKPIT_PANELS: TileId[] = ['chat', 'files', 'editor', 'vnc'];
|
|
|
|
const WorkspaceScreen: React.FC = () => {
|
|
const [mode, setMode] = useState<ViewModeValue>(viewMode.get());
|
|
const [focus, setFocus] = useState<FocusSnapshot>(projectFocus.get());
|
|
const [hasCode, setHasCode] = useState(false);
|
|
const [hasDesktop, setHasDesktop] = useState(false);
|
|
const [view, setView] = useState<AriaView | undefined>(undefined);
|
|
|
|
useEffect(() => viewMode.subscribe(setMode), []);
|
|
useEffect(() => projectFocus.subscribe(setFocus), []);
|
|
|
|
const pid = focus.focusedProjectId;
|
|
|
|
// aria_view: ARIAs komponierte Ansicht fuers fokussierte Projekt spiegeln.
|
|
useEffect(() => {
|
|
setView(ariaView.getView(pid));
|
|
return ariaView.subscribe((v) => {
|
|
if ((v.projectId || '') === (pid || '')) setView(v);
|
|
});
|
|
}, [pid]);
|
|
|
|
// Code-Signal: hat der Spiegel schon Dateien fuer dieses Projekt?
|
|
useEffect(() => {
|
|
setHasCode(codeFile.getFiles(pid).length > 0);
|
|
return codeFile.subscribe((u) => {
|
|
if ((u.projectId || '') === (pid || '')) setHasCode(true);
|
|
});
|
|
}, [pid]);
|
|
|
|
// Desktop-Signal: gruener Punkt NUR, wenn im AKTIVEN Projekt wirklich eine VM
|
|
// laeuft (nicht generell irgendwo). Quelle ist die projektbezogene VM-Liste;
|
|
// leichtes Nachfassen, damit Start/Stop sich zeitnah zeigt.
|
|
useEffect(() => {
|
|
if (!pid) { setHasDesktop(false); return; }
|
|
let alive = true;
|
|
const check = () => {
|
|
brainApi.listProjectVms(pid)
|
|
.then(r => { if (alive) setHasDesktop((r.vms || []).some(v => v.running)); })
|
|
.catch(() => { if (alive) setHasDesktop(false); });
|
|
};
|
|
check();
|
|
const t = setInterval(check, 6000);
|
|
return () => { alive = false; clearInterval(t); };
|
|
}, [pid]);
|
|
|
|
const badges = useMemo(() => ({
|
|
editor: hasCode ? '#0096FF' : undefined,
|
|
vnc: hasDesktop ? '#34C759' : undefined,
|
|
} as Partial<Record<TileId, string>>), [hasCode, hasDesktop]);
|
|
|
|
// Kompakt-Ansicht: klassischer Vollbild-Chat; Cockpit: Workbench mit Dock.
|
|
const content =
|
|
mode === 'compact' ? (
|
|
<ChatScreen />
|
|
) : (
|
|
<WorkspaceDeck projectId={pid} panels={COCKPIT_PANELS} badges={badges} />
|
|
);
|
|
|
|
// Generative Flaeche als Overlay, sobald ARIA fuer dieses Projekt eine Ansicht
|
|
// komponiert hat (present_view → aria_view). Chat/Cockpit bleiben darunter.
|
|
const showView = !!view && (view.projectId || '') === (pid || '');
|
|
|
|
return (
|
|
<View style={{ flex: 1 }}>
|
|
{content}
|
|
{showView && view && (
|
|
<AriaViewCanvas
|
|
view={view.view}
|
|
onClose={() => {
|
|
ariaView.clear(pid);
|
|
setView(undefined);
|
|
}}
|
|
/>
|
|
)}
|
|
</View>
|
|
);
|
|
};
|
|
|
|
export default WorkspaceScreen;
|