import SwiftUI import AppKit import UniformTypeIdentifiers func pretty(_ x: Any) -> String { guard let d = try? JSONSerialization.data(withJSONObject: x, options: [.prettyPrinted, .sortedKeys, .withoutEscapingSlashes]) else { return "{}" }; return String(decoding:d,as:UTF8.self) } @MainActor final class Studio: ObservableObject { @Published var geometries: [[String:Any]] = [] @Published var geometry: [String:Any] = [:] @Published var editor = "" @Published var showWeights = false @Published var source = "" @Published var output = "" @Published var report = "" @Published var status = "Starting AFFECTOR…" @Published var busy = false @Published var backend = "Local Qwen" @Published var rewriteStyle = "Restrained" @Published var model = "gpt-6-luna" @Published var key = "" @Published var evaluator = "Local Qwen" @Published var evalModel = "gpt-6-luna" @Published var evalKey = "" @Published var temperature = 0.0 @Published var topP = 0.95 @Published var topK = 40 @Published var tokens = 512 @Published var verification: [String:Any] = [:] @Published var hasRun = false @Published var runSummary = "" @Published var support = "" @Published var error: String? private var process: Process? private var input: FileHandle? private var buffer = Data() func start() { let resources = Bundle.main.resourceURL! let p = Process(); let stdin = Pipe(); let stdout = Pipe(); let stderr = Pipe() p.executableURL = resources.appendingPathComponent("Python.framework/Versions/3.14/bin/python3.14") p.arguments = ["-u",resources.appendingPathComponent("engine/bridge.py").path] p.currentDirectoryURL = resources.appendingPathComponent("engine") var env = ProcessInfo.processInfo.environment env["PYTHONHOME"] = resources.appendingPathComponent("Python.framework/Versions/3.14").path env["PYTHONPATH"] = resources.appendingPathComponent("packages").path env["HF_HOME"] = FileManager.default.urls(for:.cachesDirectory,in:.userDomainMask)[0].appendingPathComponent("AFFECTOR/huggingface").path env["AFFECTOR_MODEL"] = resources.appendingPathComponent("engine/.hf/hub/models--Qwen--Qwen3.5-0.8B/snapshots/2fc06364715b967f1860aea9cf38778875588b17").path env["PYTHONNOUSERSITE"] = "1"; env["PYTHONDONTWRITEBYTECODE"] = "1"; env["PYTORCH_ENABLE_MPS_FALLBACK"] = "1" p.environment = env; p.standardInput = stdin; p.standardOutput = stdout; p.standardError = stderr input = stdin.fileHandleForWriting; process = p stdout.fileHandleForReading.readabilityHandler = { [weak self] h in let d = h.availableData if !d.isEmpty { Task { @MainActor in self?.receive(d) } } } stderr.fileHandleForReading.readabilityHandler = { h in _ = h.availableData } p.terminationHandler = { [weak self] p in Task { @MainActor in self?.busy = false; self?.status = "Engine stopped (\(p.terminationStatus)). Reopen AFFECTOR to restart." } } do { try p.run() } catch { self.error = error.localizedDescription } } func stopProcess() { input?.closeFile(); process?.terminate() } func receive(_ d:Data) { buffer.append(d) while let end = buffer.firstIndex(of:10) { let line = buffer[..String { var d=data if var g=d["geometry"] as? [String:Any] {g["basis"]="Compiled weights included in exported JSON; edit in Geometry + weights.";d["geometry"]=g} let result=pretty(d) return result.count > 80000 ? String(result.prefix(80000))+"\n… Export the report to view all passages." : result } func edited() -> [String:Any]? { guard let d = editor.data(using:.utf8), var g = (try? JSONSerialization.jsonObject(with:d)) as? [String:Any] else { error = "Geometry must be valid JSON. Check commas and brackets."; return nil } if g["basis"] == nil {g["basis"] = geometry["basis"] ?? NSNull()} return g } func send(_ q:[String:Any]) { guard let input, let d = try? JSONSerialization.data(withJSONObject:q) else { return } if q["op"] as? String != "cancel" && q["op"] as? String != "save_run" { busy = true } do { try input.write(contentsOf:d + Data([10])) } catch { busy = false; self.error = error.localizedDescription } } func open(_ op:String, document:Bool = false) { let panel = NSOpenPanel(); panel.canChooseDirectories = false; panel.allowsMultipleSelection = false panel.allowedContentTypes = document ? [UTType.plainText,UTType.rtf,UTType(filenameExtension:"docx")!] : [.json] if panel.runModal() == .OK, let url = panel.url { send(["op":op,"path":url.path]) } } func save(_ op:String, document:Bool = false) { let panel = NSSavePanel(); panel.nameFieldStringValue = document ? "transformed.txt" : (op == "export_geometry" ? "emotional-shape.json" : "affector-transfer.json") panel.allowedContentTypes = document ? [.plainText,.rtf,UTType(filenameExtension:"docx")!] : [.json] panel.allowsOtherFileTypes = false if panel.runModal() == .OK, let url = panel.url { var q:[String:Any] = ["op":op,"path":url.path] if op == "export_geometry" { guard let g = edited() else { return }; q["geometry"] = g } send(q) } } func transform() { guard let g = edited() else { return } send(["op":"transform","rewrite_style":rewriteStyle,"geometry":g,"text":source,"backend":backend,"model":model,"key":key,"sampling":["temperature":temperature,"top_p":topP,"top_k":topK,"max_tokens":tokens]]) } func geometryAction(_ op:String) { if let g = edited() { send(["op":op,"geometry":g]) } } } struct Trajectory: View { @Environment(\.colorScheme) private var colorScheme let geometry:[String:Any] var body: some View { Canvas { ctx,size in let points = geometry["points"] as? [[String:Double]] ?? [] func project(_ x:Double,_ y:Double,_ z:Double)->CGPoint { CGPoint(x:size.width*0.48 + CGFloat(x-y*0.45)*size.width*0.28,y:size.height*0.52 - CGFloat(z+y*0.35)*size.height*0.32) } let axes:[(String,(Double,Double,Double))] = [("X · valence",(1.35,0,0)),("Y · direction",(0,1.35,0)),("Z · intensity",(0,0,1.35))] for (name,a) in axes { var p=Path(); p.move(to:project(0,0,0)); p.addLine(to:project(a.0,a.1,a.2)); ctx.stroke(p,with:.color(.gray.opacity(0.6)),lineWidth:1); ctx.draw(Text(name).font(.system(size:10,design:.monospaced)),at:project(a.0,a.1,a.2),anchor:a.0 > 0 ? .trailing : .bottom) } var path=Path() for (i,p) in points.enumerated() { let pos=project(p["v"] ?? 0,p["d"] ?? 0,2*(p["i"] ?? 0.5)-1); if i==0 {path.move(to:pos)} else {path.addLine(to:pos)}; ctx.fill(Path(ellipseIn:CGRect(x:pos.x-3,y:pos.y-3,width:6,height:6)),with:.color(.primary)) } ctx.stroke(path,with:.color(.primary),lineWidth:2) }.frame(height:205).background(colorScheme == .dark ? Color.black : Color.white).overlay(Rectangle().stroke(.primary.opacity(0.15))) } } struct ContentView: View { @EnvironmentObject var s:Studio @AppStorage("appearance") private var appearance = "light" private var darkMode: Bool { appearance == "dark" } @State private var tab = 0 @State private var confirmDelete = false let providers = ["Local Qwen","OpenAI","VT ARC"] var body: some View { VStack(alignment:.leading,spacing:0) { HStack(alignment:.top) { VStack(alignment:.leading,spacing:1) { Text("AFFECTOR").font(.system(size:53,weight:.black,design:.default)).tracking(-3); Text("EMOTIONAL GEOMETRY → WRITTEN FORM").font(.system(size:10,design:.monospaced)).tracking(2) } Spacer() VStack(alignment:.trailing,spacing:8) { Text(s.status).font(.system(size:12,design:.monospaced)).multilineTextAlignment(.trailing).frame(maxWidth:390,minHeight:35); if s.busy { Button("Stop") { s.send(["op":"cancel"]) } } Picker("Appearance",selection:$appearance) {Text("Light").tag("light");Text("Dark").tag("dark")}.pickerStyle(.segmented).labelsHidden().frame(width:170).accessibilityLabel("Appearance") } }.padding(24) Divider().background(.primary) HStack(alignment:.top,spacing:0) { VStack(alignment:.leading,spacing:12) { label("SHAPE LIBRARY") ScrollView { VStack(alignment:.leading,spacing:3) { ForEach(s.geometries.indices,id:\.self) { i in let g=s.geometries[i] Button { s.select(g) } label: { VStack(alignment:.leading,spacing:5) { Text(g["name"] as? String ?? "Untitled").font(.system(size:13,weight:.medium)); Text(String((g["updated_at"] as? String ?? "").prefix(19))).font(.system(size:9,design:.monospaced)) }.frame(maxWidth:.infinity,alignment:.leading).padding(10).background((g["id"] as? String)==(s.geometry["id"] as? String) ? Color.primary.opacity(0.10):Color.clear) }.buttonStyle(.plain).disabled(s.busy) } } }.frame(minHeight:100,maxHeight:240) HStack { Button("New") { s.send(["op":"new"]) }; Button("Open…") { s.open("import") } }.disabled(s.busy) HStack { Button("Export…") { s.save("export_geometry") }; Button("Delete") { confirmDelete=true } }.disabled(s.busy || s.geometry.isEmpty) Trajectory(geometry:s.geometry) Text("Import Emotional Atlas sessions or object/evolution JSON with emotional coordinates. The line follows the text from beginning to end.").font(.system(size:11)).foregroundStyle(.secondary) Spacer() Text("LOCAL STEERING · API TRANSFER\nQWEN3.5 / 0.8B\nEXPERIMENTAL RESEARCH SOFTWARE").font(.system(size:9,design:.monospaced)).lineSpacing(4) }.padding(20).frame(width:255) Divider() VStack(alignment:.leading,spacing:16) { Picker("Workspace",selection:$tab) { Text("01 Document").tag(0); Text("02 Geometry + weights").tag(1); Text("03 Verification").tag(2) }.pickerStyle(.segmented) if tab==0 { document } if tab==1 { geometry } if tab==2 { verification } }.padding(22).frame(maxWidth:.infinity,maxHeight:.infinity,alignment:.top) } }.background(darkMode ? Color.black : Color.white).foregroundStyle(.primary).preferredColorScheme(darkMode ? .dark : .light).frame(minWidth:1080,minHeight:770) .alert("AFFECTOR",isPresented:Binding(get:{s.error != nil},set:{if !$0 {s.error=nil}})) { Button("OK") {s.error=nil} } message: {Text(s.error ?? "")} .alert("Delete this geometry?",isPresented:$confirmDelete) { Button("Delete",role:.destructive) {s.send(["op":"delete","id":s.geometry["id"] ?? ""]);s.geometry=[:];s.editor=""};Button("Cancel",role:.cancel){} } message:{Text("Export a copy first if you want to retain its trajectory and calibrated weights.")} } func label(_ text:String)->some View { Text(text).font(.system(size:10,weight:.semibold,design:.monospaced)).tracking(1.5) } var document: some View { VStack(alignment:.leading,spacing:14) { HStack { Button("Open document…") {s.open("read_doc",document:true)}; Button("Open transfer…") {s.open("load_run")}; Button("Save transfer…") {s.save("save_run")}.disabled(!s.hasRun); Button("Reset text") {s.send(["op":"reset_text"])}.help("Clear both texts and the automatic checkpoint without saving. Keeps geometry, settings and manually saved files."); Spacer();Button("Export output…") {s.save("export_doc",document:true)}.disabled(!s.hasRun) }.disabled(s.busy) if !s.runSummary.isEmpty { Text(s.runSummary).font(.system(size:11,design:.monospaced)).textSelection(.enabled) } HStack(alignment:.top,spacing:18) { VStack(alignment:.leading) {label("INPUT");TextEditor(text:$s.source).font(.system(size:15,design:.serif)).padding(6).overlay(Rectangle().stroke(.gray.opacity(0.4))).disabled(s.busy)} VStack(alignment:.leading) {label("TRANSFORMED TEXT");ScrollView {Text(s.output.isEmpty ? "Your transformed document will appear here." : s.output).font(.system(size:15,design:.serif)).textSelection(.enabled).frame(maxWidth:.infinity,alignment:.leading).padding(12)}.frame(maxWidth:.infinity,maxHeight:.infinity).overlay(Rectangle().stroke(.gray.opacity(0.4)))} }.frame(minHeight:190) Picker("Rewrite style", selection:$s.rewriteStyle) { Text("Minimal").tag("Minimal"); Text("Restrained").tag("Restrained"); Text("Balanced").tag("Balanced"); Text("Creative").tag("Creative") }.pickerStyle(.segmented).disabled(s.busy) Text(["Minimal":"Light edits to a few words or phrases; preserve nearly all original wording.", "Restrained":"Preserve meaning and details; adjust emotional tone through wording and rhythm.", "Balanced":"Reshape phrasing and structure with modest imagery; preserve facts and events.", "Creative":"Allow new imagery, perspectives and events; meaning may change."][s.rewriteStyle] ?? "").font(.system(size:11)).foregroundStyle(.secondary) Text("TXT / RTF / DOCX formatting is not preserved. Generated drafts are saved, including repetitions and incomplete endings. Requested steering is preserved. Incomplete and repetitive endings are trimmed to complete sentences; raw drafts remain in the report. Output aims for the original length; paragraphs share a 125% ceiling.").font(.system(size:10,design:.monospaced)).foregroundStyle(.secondary) Divider() HStack { Picker("Transform with",selection:$s.backend) {ForEach(providers,id:\.self){Text($0)}}.frame(width:260);Spacer();Button("Transform / resume") {s.transform()}.buttonStyle(.bordered).foregroundStyle(.primary).disabled(s.busy || s.geometry.isEmpty) }.disabled(s.busy) if s.backend != "Local Qwen" { HStack {TextField("Model",text:$s.model);SecureField("API key · kept in memory only",text:$s.key)}.textFieldStyle(.roundedBorder).disabled(s.busy);Text("API mode uses coordinate instructions. Hosted APIs do not expose transformer activations.").font(.system(size:11)) } else { HStack {Text("Sampling").font(.system(size:11,design:.monospaced));Text("Temperature");TextField("Temperature",value:$s.temperature,format:.number).frame(width:55);Text("Top-p").padding(.leading,12);TextField("Top-p",value:$s.topP,format:.number).frame(width:55);Text("Top-k").padding(.leading,12);TextField("Top-k",value:$s.topK,format:.number).frame(width:50)}.font(.system(size:11)).disabled(s.busy) } HStack {Text("Output tokens").font(.system(size:11));TextField("Output tokens",value:$s.tokens,format:.number).frame(width:65);Text("base token allowance (32–2048); expanded automatically for longer passages").font(.system(size:11));Spacer();Button("Save progress…") {s.save("save_run")}.disabled(!s.hasRun)} }.onChange(of:s.backend) { _,b in if b=="VT ARC" {s.model="gpt-oss-120b"} else if b=="OpenAI" {s.model="gpt-6-luna"} } } var geometry: some View { VStack(alignment:.leading,spacing:12) { Text("A shape is a trajectory and a transformer module.").font(.system(size:24,weight:.semibold)) Text("Edit the name, points, selected layers (0–23), controls, or compiled basis vectors below. Save to update the preview. Recalibrate after changing layers or model identity.").font(.system(size:12)) Text("t = position · v = valence [−1,1] · d = direction [−1,1] · i = intensity [0,1]\nstrength = injection scale · budget = maximum relative RMS · inertia = temporal smoothing\naxis_top_k / axis_mass / axis_temperature select and reshape activation coefficients, independently of token sampling.").font(.system(size:11,design:.monospaced)).lineSpacing(5) Text("Extreme values for strength, budget, and inertia may cause generation failures.").font(.system(size:12)).foregroundStyle(.red) HStack { control("Strength", "strength", 0...1) control("Budget", "budget", 0...1) control("Inertia", "inertia", 0...0.99) }.disabled(s.busy) TextEditor(text:$s.editor).font(.system(size:11,design:.monospaced)).padding(6).overlay(Rectangle().stroke(.gray.opacity(0.4))).disabled(s.busy) HStack {Button("Save geometry") {s.geometryAction("save")};Button("Calibrate local module") {s.geometryAction("calibrate")};Button(s.showWeights ? "Hide numerical weights" : "Edit numerical weights") {s.toggleWeights()};Spacer();Text("Calibration is experimental; emotional fit is measured separately.").font(.system(size:10)).foregroundStyle(.secondary)}.disabled(s.busy || s.geometry.isEmpty) } } func control(_ title:String,_ key:String,_ range:ClosedRange)->some View { let binding=Binding(get:{ let obj=(try? JSONSerialization.jsonObject(with:Data(s.editor.utf8))) as? [String:Any] return (obj?["controls"] as? [String:Any])?[key] as? Double ?? 0 },set:{ value in guard var g=(try? JSONSerialization.jsonObject(with:Data(s.editor.utf8))) as? [String:Any],var c=g["controls"] as? [String:Any] else {return} c[key]=value;g["controls"]=c;s.editor=pretty(g) }) return VStack(alignment:.leading){Text("\(title) \(binding.wrappedValue, specifier:"%.2f")").font(.system(size:11,design:.monospaced));Slider(value:binding,in:range).tint(.primary)} } var verification: some View { VStack(alignment:.leading,spacing:14) { Text("Measure the change, passage by passage.").font(.system(size:24,weight:.semibold)) Text("Evaluate original and revised text with the Emotional Atlas scoring prompt. Similarity measures proximity to the requested coordinates; it is not a probability or a measure of semantic fidelity.").font(.system(size:12)) HStack {Picker("Evaluator",selection:$s.evaluator){ForEach(providers,id:\.self){Text($0)}}.frame(width:260);Spacer();Button("Verify output") {s.send(["op":"verify","provider":s.evaluator,"model":s.evalModel,"key":s.evalKey])}.disabled(s.busy || !s.hasRun);Button("Export report…") {s.save("export_report")}.disabled(s.busy || !s.hasRun)} if s.evaluator != "Local Qwen" {HStack {TextField("Evaluator model",text:$s.evalModel);SecureField("Evaluator API key",text:$s.evalKey)}.textFieldStyle(.roundedBorder)} HStack(spacing:30) { score("ORIGINAL",s.verification["before_similarity"] as? Double);score("TRANSFORMED",s.verification["after_similarity"] as? Double);score("IMPROVEMENT",s.verification["improvement"] as? Double);VStack(alignment:.leading){label("PAIRED COVERAGE");Text("\(s.verification["paired_count"] as? Int ?? 0) / \(s.verification["total_count"] as? Int ?? 0)").font(.system(size:26,weight:.light))} }.padding(.vertical,12) Text("Similarity = 1 − distance / (2√3), with z = 2 × intensity − 1. Unknown directions and failed evaluations are excluded. Local 0.8B scoring may be unreliable; compare with an independent API evaluator.").font(.system(size:11)).foregroundStyle(.secondary) label("TRANSFER + EVALUATION RECORD · FULL WEIGHTS IN EXPORT") ScrollView {Text(s.report.isEmpty ? "Run a transfer, then verify its output." : s.report).font(.system(size:11,design:.monospaced)).textSelection(.enabled).frame(maxWidth:.infinity,alignment:.leading).padding(12)}.frame(maxWidth:.infinity,maxHeight:.infinity).overlay(Rectangle().stroke(.gray.opacity(0.4))) }.onChange(of:s.evaluator) {_,b in if b=="VT ARC" {s.evalModel="gpt-oss-120b"} else if b=="OpenAI" {s.evalModel="gpt-6-luna"} } } func score(_ name:String,_ value:Double?)->some View {VStack(alignment:.leading){label(name);Text(value.map{String(format:"%.3f",$0)} ?? "—").font(.system(size:26,weight:.light,design:.monospaced))}} } final class AppDelegate: NSObject, NSApplicationDelegate { var studio:Studio? func applicationShouldTerminate(_ sender:NSApplication)->NSApplication.TerminateReply { if studio?.busy == true {let a=NSAlert();a.messageText="Stop the active operation and quit?";a.informativeText="Generated drafts are saved, including repetitions and incomplete endings. Requested steering is preserved. Incomplete and repetitive endings are trimmed to complete sentences; raw drafts remain in the report. Output aims for the original length; paragraphs share a 125% ceiling. The current passage may need to be repeated.";a.addButton(withTitle:"Quit");a.addButton(withTitle:"Keep working");if a.runModal() != .alertFirstButtonReturn{return .terminateCancel}} studio?.stopProcess();return .terminateNow } func applicationShouldTerminateAfterLastWindowClosed(_ sender:NSApplication)->Bool {true} } @main struct AffectorApp: App { @NSApplicationDelegateAdaptor(AppDelegate.self) var delegate @StateObject private var studio=Studio() var body: some Scene {WindowGroup {ContentView().environmentObject(studio).onAppear{if delegate.studio == nil {delegate.studio=studio;studio.start()}}}.defaultSize(width:1230,height:850).commands {CommandGroup(replacing:.newItem){};CommandGroup(after:.saveItem){Button("Save transfer…"){studio.save("save_run")}.keyboardShortcut("s").disabled(!studio.hasRun)}}} }