From e64043dca5598f583d0208ca3b5744f58f626415 Mon Sep 17 00:00:00 2001 From: duffyduck Date: Mon, 20 Jul 2026 23:31:31 +0200 Subject: [PATCH] feat: Datei-Symbol am Projekt (auto) + VM-Startparameter im Desktop-Panel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Brain: /projects/status + /list liefern has_files + file_count pro Projekt (Scan /shared/projects//). Ersetzt das manuelle Code-Flag als primaeren Indikator. VM-Liste liefert boot_cmd (lesbarer aria-vm-Startbefehl). - App: ๐Ÿ“„-Symbol (+ Anzahl) an Projekten mit Dateien im ProjectsBrowser. DesktopTile zeigt pro VM den Start-Befehl als Wert dahinter. - Diagnostic: ๐Ÿ“„-Symbol (+ Anzahl, Tooltip) an Projekten mit Dateien. Hinweis (kein Code): der VNC-Stream laeuft komplett durch RVS โ€” der Port ist nur der interne QEMU-Display-Port, den die Bridge lokal auf dem Host nutzt; die App oeffnet nie einen Port (firewall-unabhaengig). py/tsc clean. Co-Authored-By: Claude Opus 4.8 --- android/src/components/ProjectsBrowser.tsx | 3 ++ android/src/services/brainApi.ts | 4 ++ android/src/workspace/tiles/DesktopTile.tsx | 6 ++- aria-brain/main.py | 49 ++++++++++++++++++++- diagnostic/index.html | 1 + 5 files changed, 59 insertions(+), 4 deletions(-) diff --git a/android/src/components/ProjectsBrowser.tsx b/android/src/components/ProjectsBrowser.tsx index 311189f..0483f10 100644 --- a/android/src/components/ProjectsBrowser.tsx +++ b/android/src/components/ProjectsBrowser.tsx @@ -225,6 +225,9 @@ export const ProjectsBrowser: React.FC = ({ visible = true, onClose, onAc )} {item.name} + {item.has_files && ( + ๐Ÿ“„{item.file_count ? ` ${item.file_count}` : ''} + )} {hidden && versteckt} {item.status === 'ended' && beendet} {isActive && โœ“ FOCUS} diff --git a/android/src/services/brainApi.ts b/android/src/services/brainApi.ts index 29d1437..34ce3e5 100644 --- a/android/src/services/brainApi.ts +++ b/android/src/services/brainApi.ts @@ -168,6 +168,9 @@ export interface Project { // Optionale absolute noVNC-URL (falls der Desktop direkt erreichbar ist, // sonst laeuft der VNC-Stream als RFB-Bytes durch RVS). desktop_url?: string; + // Automatisch: hat das Projekt Dateien in /shared/projects//? โ†’ Datei-Symbol. + has_files?: boolean; + file_count?: number; } export interface ProjectStatus { @@ -185,6 +188,7 @@ export interface ProjectVm { mem: number; running?: boolean; vnc_port?: number; + boot_cmd?: string; created_at?: number; } diff --git a/android/src/workspace/tiles/DesktopTile.tsx b/android/src/workspace/tiles/DesktopTile.tsx index 3c7aeee..32b4863 100644 --- a/android/src/workspace/tiles/DesktopTile.tsx +++ b/android/src/workspace/tiles/DesktopTile.tsx @@ -101,8 +101,9 @@ const DesktopTile: React.FC = ({ projectId, focused }) => { โ— {vm.name} + {vm.arch} ยท {vm.running ? 'lรคuft' : 'gestoppt'} - {vm.arch} ยท Display :{vm.vnc_display} ยท {vm.running ? 'lรคuft' : 'gestoppt'} + {vm.boot_cmd || `aria-vm boot ${vm.name} --vnc-display ${vm.vnc_display}`} {isBusy ? ( @@ -145,7 +146,8 @@ const styles = StyleSheet.create({ err: { color: '#FF6E6E', fontSize: 13, marginTop: 16 }, vmRow: { flexDirection: 'row', alignItems: 'center', backgroundColor: '#12122A', borderRadius: 10, padding: 12, marginBottom: 8 }, vmName: { color: '#E0E0F0', fontSize: 15, fontWeight: '700' }, - vmMeta: { color: '#8888AA', fontSize: 12, marginTop: 3 }, + vmMeta: { color: '#8888AA', fontSize: 12, fontWeight: '400' }, + vmCmd: { color: '#6A9BD0', fontSize: 11, fontFamily: 'monospace', marginTop: 4 }, vmBtns: { flexDirection: 'row', gap: 6, alignItems: 'center' }, vmBtn: { borderWidth: 1, borderRadius: 8, paddingHorizontal: 10, paddingVertical: 6 }, vmBtnText: { fontSize: 12, fontWeight: '700' }, diff --git a/aria-brain/main.py b/aria-brain/main.py index 7df53e2..e7fa22f 100644 --- a/aria-brain/main.py +++ b/aria-brain/main.py @@ -770,15 +770,49 @@ def projects_queue_status(): # โ”€โ”€ Projekte โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +def _project_file_count(pid: str) -> int: + """Anzahl Dateien in /shared/projects// (rekursiv, gecappt). 0 = leer.""" + base = os.path.join("/shared/projects", pid or "") + if not os.path.isdir(base): + return 0 + cnt = 0 + try: + for _dp, dns, fns in os.walk(base): + dns[:] = [d for d in dns if d not in (".git", "node_modules", "__pycache__", ".venv", "venv")] + cnt += len(fns) + if cnt > 999: + return 999 + except Exception: + return 0 + return cnt + + +def _enrich_projects(projects: list) -> list: + """Ergaenzt has_files + file_count pro Projekt (fuer das Datei-Symbol in der + Liste). Das ersetzt das manuelle Code-Flag als primaeren Code-Indikator.""" + for p in projects or []: + if not isinstance(p, dict): + continue + c = _project_file_count(p.get("id") or "") + p["file_count"] = c + p["has_files"] = c > 0 + return projects + + @app.get("/projects/status") def projects_status(): """Komplett-Status: aktives Projekt + Liste aller (nicht-archivierten).""" - return projects_mod.status() + st = projects_mod.status() + _enrich_projects(st.get("projects", [])) + if st.get("active"): + _enrich_projects([st["active"]]) + return st @app.get("/projects/list") def projects_list(include_archived: bool = False): - return {"projects": projects_mod.list_projects(include_archived=include_archived)} + return {"projects": _enrich_projects( + projects_mod.list_projects(include_archived=include_archived))} class ProjectCreateBody(BaseModel): @@ -933,6 +967,16 @@ class VmAddBody(BaseModel): size: str = "10G" +def _vm_boot_cmd(v: dict) -> str: + """Lesbarer Start-Befehl (aria-vm) als 'Wert' hinter dem VM-Eintrag.""" + parts = ["aria-vm", "boot", v.get("name", "?"), + "--vnc-display", str(v.get("vnc_display", 1)), + "--mem", str(v.get("mem", 1024))] + if v.get("iso"): + parts += ["--iso", v["iso"]] + return " ".join(parts) + + @app.get("/projects/{project_id}/vms") def project_vms_list(project_id: str): vms = [dict(v) for v in project_vms_mod.list_vms(project_id)] @@ -940,6 +984,7 @@ def project_vms_list(project_id: str): for v in vms: v["running"] = v.get("name") in running v["vnc_port"] = 5900 + int(v.get("vnc_display", 1)) + v["boot_cmd"] = _vm_boot_cmd(v) return {"projectId": project_id, "vms": vms} diff --git a/diagnostic/index.html b/diagnostic/index.html index 6bf89d1..27ce6ed 100644 --- a/diagnostic/index.html +++ b/diagnostic/index.html @@ -3192,6 +3192,7 @@
${hidden ? '๐Ÿ™ˆ' : '๐Ÿ“'} ${escapeHtml(p.name)} + ${p.has_files ? `๐Ÿ“„${p.file_count ? ' ' + p.file_count : ''}` : ''} ${hidden ? 'versteckt' : ''} ${ended ? 'beendet' : ''} ${p.kind === 'code' ? '</> Code' : ''}