#!/usr/bin/env python3 """Hide/re-add Parallels VMs in OrionToolbar only; never unregister/delete VMs.""" from pathlib import Path from datetime import datetime import shutil root=Path.cwd() cpath=root/'OrionToolbar/OrionVMController.swift' mpath=root/'OrionToolbar/OrionMenuView.swift' if not cpath.is_file() or not mpath.is_file(): raise SystemExit('Run from OrionToolbar project root') c=cpath.read_text(); m=mpath.read_text() def once(s,old,new,desc): n=s.count(old) if n!=1: raise SystemExit(f'Expected exactly one {desc}, found {n}. No changes made.') return s.replace(old,new,1) if 'orionHiddenVMIDs.v1' in c: raise SystemExit('Already installed; no changes made') c=once(c,' @Published var discoveredVMs: [ParallelsVM] = []',''' @Published var discoveredVMs: [ParallelsVM] = [] @Published private(set) var hiddenVMIDs: Set = Set( UserDefaults.standard.stringArray(forKey: "orionHiddenVMIDs.v1") ?? [] ) func hideVM(_ machine: ParallelsVM) { hiddenVMIDs.insert(machine.id) UserDefaults.standard.set(hiddenVMIDs.sorted(), forKey: "orionHiddenVMIDs.v1") } func restoreVM(_ machine: ParallelsVM) { hiddenVMIDs.remove(machine.id) UserDefaults.standard.set(hiddenVMIDs.sorted(), forKey: "orionHiddenVMIDs.v1") } var visibleVMs: [ParallelsVM] { discoveredVMs.filter { !hiddenVMIDs.contains($0.id) } } var hiddenVMs: [ParallelsVM] { discoveredVMs.filter { hiddenVMIDs.contains($0.id) } } ''','published VM declaration') m=once(m,'vm.discoveredVMs.filter { $0.status.lowercased() == "running" }','vm.visibleVMs.filter { $0.status.lowercased() == "running" }','running list') m=once(m,'count: "\\(vm.discoveredVMs.count) VMs"','count: "\\(vm.visibleVMs.count) VMs"','VM count') m=once(m,'if vm.discoveredVMs.isEmpty {','if vm.visibleVMs.isEmpty {','VM empty state') m=once(m,'ForEach(vm.discoveredVMs) { machine in','ForEach(vm.visibleVMs) { machine in','All VM rows') # Hide an integrated server's row if its Parallels VM has been hidden. m=once(m,'ForEach(vm.servers) { server in\n integratedRow(server)','''ForEach(vm.servers.filter { server in !vm.hiddenVMIDs.contains(server.vmUUID ?? vm.discoveredVMs.first(where: { $0.name == server.vmName })?.id ?? "") }) { server in integratedRow(server)''','Integrated rows') # Add a restore dropdown to the dashboard header; visible even if every VM is hidden. anchor=''' if vm.discovering { ProgressView().controlSize(.small) }''' m=once(m,anchor,''' Menu { if vm.hiddenVMs.isEmpty { Text("No removed virtual machines") } else { ForEach(vm.hiddenVMs) { machine in Button("Re-add " + machine.name) { vm.restoreVM(machine) } } } } label: { Label("Re-add VM", systemImage: "plus.rectangle.on.folder") } .help("Restore virtual machines removed from OrionToolbar") '''+anchor,'header restore menu') # Add context menu to VM row; no changes to existing drag/drop modifiers. start=m.index(' private func machineRow('); end=m.index(' private func integratedRow(',start) block=m[start:end] needle=''' .background(OrionStyle.card.opacity(0.75), in: RoundedRectangle(cornerRadius: 10))''' block=once(block,needle,needle+''' .contextMenu { Button("Remove from OrionToolbar", systemImage: "minus.circle") { vm.hideVM(machine) } }''','All VM context menu') # Also add a clearly visible Remove button, retaining existing start/stop/integrate actions. needle=''' if vm.isIntegrated(machine) {''' block=once(block,needle,''' Button { vm.hideVM(machine) } label: { Image(systemName: "minus.circle") .foregroundStyle(OrionStyle.muted) } .buttonStyle(.plain) .help("Remove from OrionToolbar (keeps the Parallels VM)") '''+needle,'VM remove button') m=m[:start]+block+m[end:] backup=root/'backups'/datetime.now().strftime('%Y%m%d-%H%M%S-%f') for path in (cpath,mpath): dest=backup/path.relative_to(root); dest.parent.mkdir(parents=True,exist_ok=True); shutil.copy2(path,dest) cpath.write_text(c); mpath.write_text(m) print('Updated:',cpath.relative_to(root),mpath.relative_to(root)) print('Backups:',backup) print('Parallels VMs are never deleted or unregistered. Icons and drag ordering untouched.')