oficina / meet Public

Clone
git clone https://amadan.net/oficina/meet

Plain git — no account needed to clone.

Download

Download this file

1import AppKit
2@_spi(Fixtures) import CallsKit
3import SwiftUI
4
5/// Offline visual fixtures of the Mac call window, full-window and
6/// classic, from described calls (see CallFixture): controls shown and
7/// hidden, Focus and Grid, a shared screen, the Devices panel open, and
8/// the notices that stay when the controls go. The tiles show their
9/// placeholders — an off-screen capture draws no Metal video anyway.
10@MainActor enum CallSnapshots {
11 static func save(to folder: URL) async throws {
12 NSApplication.shared.setActivationPolicy(.prohibited)
13 try FileManager.default.createDirectory(at: folder, withIntermediateDirectories: true)
14 let size = CGSize(width: 1000, height: 640)
15 for scheme in [ColorScheme.light, .dark] {
16 let suffix = LobbySnapshots.name(scheme)
17 func shot(
18 _ name: String, _ fixture: CallFixture, grid: Bool = false, shown: Bool = true,
19 devices: Bool = false, chat: Bool = false, style: CallLayoutStyle = .fullWindow,
20 backdrop: FixtureBackdrop? = nil, me: FixtureSelf? = nil
21 ) async throws {
22 let call = Call(playAudio: false)
23 fixture.apply(to: call)
24 me?.apply(to: call)
25 try await LobbySnapshots.render(
26 MacCallView(
27 call: call, grid: grid, showingChat: chat, showingDevices: devices, style: style,
28 controlsShown: shown, leave: {}, rejoin: {}
29 ).tint(CallsAppearance.accent).environment(\.fixtureBackdrop, backdrop),
30 scheme: scheme, size: size,
31 to: folder.appendingPathComponent("mac-\(name)-\(suffix).png"))
32 }
33 try await shot("focus-controls", .focus)
34 try await shot("focus-hidden", .focus, shown: false)
35 try await shot("grid-controls", .grid, grid: true)
36 try await shot("grid-hidden", .grid, grid: true, shown: false)
37 try await shot("screen-controls", .screen)
38 try await shot("screen-hidden", .screen, shown: false)
39 try await shot("devices", .focus, devices: true)
40 try await shot("chat", .focus, chat: true)
41 try await shot("notices-hidden", .notices, shown: false)
42 // 2026-10-02: no title pill. A named meeting's title is centred in
43 // the title bar's strip and fades with the controls; an automatic
44 // one ("Call, 24 Sep 10:21", the focus fixture) is not shown.
45 try await shot("named-controls", .named)
46 try await shot("named-hidden", .named, shown: false)
47 try await shot("named-white", .named, backdrop: .white)
48 try await shot("named-busy", .named, backdrop: .busy)
49 try await shot("classic-focus", .focus, style: .classic)
50 try await shot("classic-grid", .grid, grid: true, style: .classic)
51 // The self view in the top-left corner: below the title bar's strip
52 // (there is no pill to keep clear of since 2026-10-02).
53 let corner = UserDefaults.standard.string(forKey: PiPCorner.key)
54 UserDefaults.standard.set(PiPCorner.topLeading.rawValue, forKey: PiPCorner.key)
55 try await shot("pip-top-leading-controls", .focus)
56 try await shot("pip-top-leading-hidden", .focus, shown: false)
57 UserDefaults.standard.set(corner, forKey: PiPCorner.key)
58 // The overlay over the worst pictures there are for it, in each of
59 // your own states: it should look the same in light and dark.
60 for backdrop in FixtureBackdrop.allCases {
61 for me in FixtureSelf.allCases {
62 try await shot("contrast-\(backdrop.rawValue)-\(me.rawValue)", .focus, backdrop: backdrop, me: me)
63 }
64 try await shot("contrast-\(backdrop.rawValue)-notices", .notices, backdrop: backdrop)
65 }
66 try await shot("contrast-busy-hidden", .focus, shown: false, backdrop: .busy)
67 }
68 }
69
70 /// Spec §15.24: two people who have just joined — Samuel still
71 /// connecting and unable to hear you yet, Bea's browser blocking sound in
72 /// place of that — in Grid, Speaker and Classic, light and dark, over a
73 /// busy picture, and the mini window and its tab with the small ear for
74 /// someone it is not showing.
75 static func saveConnecting(to folder: URL) async throws {
76 NSApplication.shared.setActivationPolicy(.prohibited)
77 try FileManager.default.createDirectory(at: folder, withIntermediateDirectories: true)
78 let size = CGSize(width: 1000, height: 640)
79 for scheme in [ColorScheme.light, .dark] {
80 let suffix = LobbySnapshots.name(scheme)
81 func shot(
82 _ name: String, grid: Bool = false, style: CallLayoutStyle = .fullWindow,
83 backdrop: FixtureBackdrop? = nil
84 ) async throws {
85 let call = Call(playAudio: false)
86 CallFixture.connecting.apply(to: call)
87 try await LobbySnapshots.render(
88 MacCallView(
89 call: call, grid: grid, showingChat: false, showingDevices: false, style: style,
90 controlsShown: true, leave: {}, rejoin: {}
91 ).tint(CallsAppearance.accent).environment(\.fixtureBackdrop, backdrop),
92 scheme: scheme, size: size,
93 to: folder.appendingPathComponent("mac-connecting-\(name)-\(suffix).png"))
94 }
95 try await shot("grid", grid: true)
96 try await shot("speaker")
97 try await shot("classic-grid", grid: true, style: .classic)
98 try await shot("busy-grid", grid: true, backdrop: .busy)
99 for hovering in [false, true] {
100 let call = Call(playAudio: false)
101 CallFixture.connecting.apply(to: call)
102 let width = MiniWindowRules.size.width
103 try await LobbySnapshots.render(
104 MiniWindowSnapshot.window(call: call, hovering: hovering), scheme: scheme,
105 size: CGSize(width: width, height: width / MiniWindowRules.aspect),
106 to: folder.appendingPathComponent("mac-connecting-mini\(hovering ? "-hover" : "")-\(suffix).png"))
107 }
108 let call = Call(playAudio: false)
109 CallFixture.connecting.apply(to: call)
110 try await LobbySnapshots.render(
111 MiniWindowSnapshot.tab(call: call), scheme: scheme, size: MiniWindowTab.size,
112 to: folder.appendingPathComponent("mac-connecting-tab-\(suffix).png"))
113 }
114 }
115
116 /// The glass and the glow as the window server draws them (0.3.7). An
117 /// off-screen bitmap has no Liquid Glass and no backdrop to blur, so
118 /// these are real windows, parked below the desktop where nobody sees
119 /// them, and captured by `screencapture -l`: the control bar over black,
120 /// white and a busy frame, in glass and in the material older systems
121 /// draw; the Devices panel; the names glowing at four levels; the tab.
122 static func saveGlass(to folder: URL) async throws {
123 NSApplication.shared.setActivationPolicy(.prohibited)
124 try FileManager.default.createDirectory(at: folder, withIntermediateDirectories: true)
125 let size = CGSize(width: 1000, height: 640)
126 func shot(
127 _ name: String, _ fixture: CallFixture, grid: Bool = false, devices: Bool = false,
128 glass: Bool = true, backdrop: FixtureBackdrop? = nil, me: FixtureSelf? = nil,
129 speaking: FixtureSpeaking? = nil
130 ) async throws {
131 let call = Call(playAudio: false)
132 fixture.apply(to: call)
133 me?.apply(to: call)
134 speaking?.apply(to: call)
135 try await renderLive(
136 MacCallView(
137 call: call, grid: grid, showingDevices: devices, style: .fullWindow,
138 controlsShown: true, leave: {}, rejoin: {}
139 )
140 .tint(CallsAppearance.accent)
141 .environment(\.fixtureBackdrop, backdrop)
142 .environment(\.callOverlayGlass, glass),
143 size: size, to: folder.appendingPathComponent("mac-\(name).png"))
144 }
145 for glass in [true, false] {
146 let kind = glass ? "glass" : "fallback"
147 for backdrop in FixtureBackdrop.allCases {
148 try await shot("bar-\(kind)-\(backdrop.rawValue)", .focus, glass: glass, backdrop: backdrop)
149 }
150 try await shot("bar-\(kind)-busy-muted", .focus, glass: glass, backdrop: .busy, me: .muted)
151 try await shot("bar-\(kind)-busy-sharing", .focus, glass: glass, backdrop: .busy, me: .sharing)
152 try await shot("devices-\(kind)", .focus, devices: true, glass: glass, backdrop: .busy)
153 }
154 for level in FixtureSpeaking.allCases {
155 try await shot("glow-\(level.rawValue)", .grid, grid: true, speaking: level)
156 try await shot("glow-focus-\(level.rawValue)", .focus, speaking: level)
157 }
158 // The warm rim on a name at each level over the pictures it has to
159 // read on, for comparing with the mock-up (0.4.3).
160 for level in FixtureSpeaking.allCases {
161 try await renderLive(
162 WarmRimGallery(level: level).background(Color.black).environment(\.colorScheme, .dark),
163 size: CGSize(width: 380, height: 670),
164 to: folder.appendingPathComponent("mac-rim-\(level.rawValue).png"))
165 }
166 for level in [FixtureSpeaking.silent, .high] {
167 let call = Call(playAudio: false)
168 CallFixture.focus.apply(to: call)
169 level.apply(to: call)
170 try await renderLive(
171 ZStack {
172 FixtureBackdrop.busy.view
173 MiniWindowSnapshot.tab(call: call)
174 },
175 size: CGSize(width: MiniWindowTab.size.width + 40, height: MiniWindowTab.size.height + 40),
176 to: folder.appendingPathComponent("mac-tab-\(level.rawValue).png"))
177 }
178 }
179
180 /// Speaker view and the viewer's order (0.4.0), as the window server
181 /// draws them: a call of four in Speaker view with Tom speaking and the
182 /// other three in the filmstrip (controls up and down, and at the
183 /// smallest window, 760 × 540), Li pinned, a shared screen with the
184 /// sharer's camera in the filmstrip, a drag half way in Grid, and Grid
185 /// after a reorder.
186 static func saveSpeaker(to folder: URL) async throws {
187 NSApplication.shared.setActivationPolicy(.prohibited)
188 try FileManager.default.createDirectory(at: folder, withIntermediateDirectories: true)
189 func shot(
190 _ name: String, _ fixture: CallFixture = .grid, grid: Bool = false, shown: Bool = true,
191 size: CGSize = CGSize(width: 1000, height: 640), speaker: String? = "tom",
192 pinned: StageItem? = nil, lift: String? = nil, order: [String] = []
193 ) async throws {
194 let call = Call(playAudio: false)
195 fixture.apply(to: call)
196 call.levels.speaker.showFixture(speaker)
197 call.arrangement.showPinned(pinned)
198 call.arrangement.showLift(lift)
199 call.arrangement.showOrder(order)
200 try await renderLive(
201 MacCallView(
202 call: call, grid: grid, style: .fullWindow, controlsShown: shown, leave: {}, rejoin: {}
203 )
204 .tint(CallsAppearance.accent),
205 size: size, to: folder.appendingPathComponent("mac-\(name).png"))
206 }
207 try await shot("speaker-filmstrip-controls")
208 try await shot("speaker-filmstrip-hidden", shown: false)
209 try await shot("speaker-filmstrip-760x540", size: CGSize(width: 760, height: 540))
210 try await shot("speaker-pinned", pinned: .camera("li"))
211 try await shot("speaker-screen", .screen, shown: false)
212 try await shot("speaker-drag", lift: "me")
213 try await shot("grid-drag", grid: true, lift: "li")
214 try await shot("grid-reordered", grid: true, order: ["li", "tom", "me", "ana"])
215 try await shot("speaker-two", .focus, shown: false, speaker: nil)
216 }
217
218 /// A real window below the desktop, captured by the window server.
219 static func renderLive(_ content: some View, size: CGSize, to file: URL) async throws {
220 let view = NSHostingView(rootView: content.environment(\.colorScheme, .dark))
221 view.frame.size = size
222 let window = NSWindow(
223 contentRect: NSRect(origin: CGPoint(x: 0, y: 0), size: size),
224 styleMask: [.borderless], backing: .buffered, defer: false)
225 window.isReleasedWhenClosed = false
226 window.level = NSWindow.Level(rawValue: Int(CGWindowLevelForKey(.desktopWindow)) - 1)
227 window.appearance = NSAppearance(named: .darkAqua)
228 window.contentView = view
229 window.orderBack(nil)
230 try await Task.sleep(for: .milliseconds(600))
231 view.layoutSubtreeIfNeeded()
232 let capture = Process()
233 capture.executableURL = URL(fileURLWithPath: "/usr/sbin/screencapture")
234 capture.arguments = ["-x", "-o", "-l", String(window.windowNumber), file.path]
235 try capture.run()
236 capture.waitUntilExit()
237 window.close()
238 guard capture.terminationStatus == 0 else { throw CocoaError(.fileWriteUnknown) }
239 }
240}
241