93 lines
4.6 KiB
Python
93 lines
4.6 KiB
Python
#!/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<String> = 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.')
|