Skip to content

Commit d4f4e03

Browse files
committed
feat(Textream): Add DirectorServer for managing director-client communication
1 parent 1e35045 commit d4f4e03

7 files changed

Lines changed: 904 additions & 3 deletions

File tree

Textream/Textream/ContentView.swift

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import SwiftUI
99
import UniformTypeIdentifiers
10+
import CoreImage.CIFilterBuiltins
1011

1112
struct ContentView: View {
1213
@ObservedObject private var service = TextreamService.shared
@@ -331,9 +332,83 @@ Happy presenting! [wave]
331332
}
332333
}
333334

335+
private var directorOverlay: some View {
336+
VStack(spacing: 20) {
337+
Spacer()
338+
339+
Image(systemName: "megaphone.fill")
340+
.font(.system(size: 40, weight: .light))
341+
.foregroundStyle(.secondary)
342+
343+
Text("Director Mode")
344+
.font(.system(size: 22, weight: .bold))
345+
346+
Text(service.directorIsReading ? "Reading from director…" : "Waiting for director to send script…")
347+
.font(.system(size: 13))
348+
.foregroundStyle(.secondary)
349+
350+
if let ip = BrowserServer.localIPAddress() {
351+
let url = "http://\(ip):\(NotchSettings.shared.directorServerPort)"
352+
353+
if let qrImage = generateDirectorQRCode(from: url) {
354+
Image(nsImage: qrImage)
355+
.interpolation(.none)
356+
.resizable()
357+
.scaledToFit()
358+
.frame(width: 140, height: 140)
359+
.clipShape(RoundedRectangle(cornerRadius: 8))
360+
}
361+
362+
HStack(spacing: 8) {
363+
Text(url)
364+
.font(.system(size: 13, weight: .semibold, design: .monospaced))
365+
.foregroundStyle(Color.accentColor)
366+
.textSelection(.enabled)
367+
368+
Button {
369+
NSPasteboard.general.clearContents()
370+
NSPasteboard.general.setString(url, forType: .string)
371+
} label: {
372+
Image(systemName: "doc.on.doc")
373+
.font(.system(size: 11, weight: .medium))
374+
}
375+
.buttonStyle(.plain)
376+
.foregroundStyle(.secondary)
377+
}
378+
}
379+
380+
Spacer()
381+
382+
Button {
383+
showSettings = true
384+
} label: {
385+
Text("Open Settings")
386+
.font(.system(size: 12, weight: .medium))
387+
.foregroundStyle(.secondary)
388+
}
389+
.buttonStyle(.plain)
390+
.padding(.bottom, 20)
391+
}
392+
.frame(maxWidth: .infinity, maxHeight: .infinity)
393+
}
394+
395+
private func generateDirectorQRCode(from string: String) -> NSImage? {
396+
let context = CIContext()
397+
let filter = CIFilter.qrCodeGenerator()
398+
filter.message = Data(string.utf8)
399+
filter.correctionLevel = "M"
400+
guard let ciImage = filter.outputImage else { return nil }
401+
let scale = 10.0
402+
let scaled = ciImage.transformed(by: CGAffineTransform(scaleX: scale, y: scale))
403+
guard let cgImage = context.createCGImage(scaled, from: scaled.extent) else { return nil }
404+
return NSImage(cgImage: cgImage, size: NSSize(width: scaled.extent.width, height: scaled.extent.height))
405+
}
406+
334407
var body: some View {
335408
Group {
336-
if service.pages.count > 1 {
409+
if NotchSettings.shared.directorModeEnabled {
410+
directorOverlay
411+
} else if service.pages.count > 1 {
337412
NavigationSplitView {
338413
pageSidebar
339414
} detail: {

0 commit comments

Comments
 (0)