dsh-notch 0.3.1 → 0.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +58 -47
- package/README.md +60 -55
- package/cordis.patch.yml +3 -0
- package/desktop/README.md +29 -0
- package/desktop/notch-lifecycle.mjs +120 -0
- package/desktop/restart-probe.mjs +61 -0
- package/docs/desktop-017rc2.md +23 -0
- package/docs/elastic-edge-dock.md +127 -0
- package/docs/motion-audit.md +1 -1
- package/docs/releases/v0.3.2.md +38 -0
- package/docs/releases/v0.3.3.md +9 -0
- package/docs/subagent-compatibility.md +51 -0
- package/docs/verification/elastic-algorithm-timing.json +337 -0
- package/docs/verification/elastic-attention.json +44 -0
- package/docs/verification/elastic-frame-timing.json +240 -0
- package/docs/verification/elastic-tension.json +59 -0
- package/docs/verification/elastic-tip-taper.json +181 -0
- package/dshx.yml +2 -0
- package/lib/client.js +112 -0
- package/lib/client.js.map +1 -0
- package/lib/index.mjs +990 -0
- package/lib/index.mjs.map +1 -0
- package/package.json +66 -10
- package/src/board.ts +178 -35
- package/src/client/index.ts +104 -0
- package/src/dsh-notch.ts +19 -2
- package/src/http.ts +17 -2
- package/src/session-state.ts +15 -1
- package/src/store.ts +3 -3
- package/src/types.ts +3 -0
- package/BASELINE.md +0 -13
- package/DESIGN.md +0 -34
- package/cordis.yml +0 -5
- package/macos/Package.swift +0 -17
- package/macos/STATUS-MOTION.md +0 -49
- package/macos/Sources/Client.swift +0 -139
- package/macos/Sources/IdleRobot.swift +0 -423
- package/macos/Sources/NotchMarkdown.swift +0 -266
- package/macos/Sources/Panel.swift +0 -153
- package/macos/Sources/Resources/Idle/balance.json +0 -1
- package/macos/Sources/Resources/Idle/blink.json +0 -1
- package/macos/Sources/Resources/Idle/cube-in.json +0 -1
- package/macos/Sources/Resources/Idle/dance.json +0 -1
- package/macos/Sources/Resources/Idle/hop.json +0 -1
- package/macos/Sources/Resources/Idle/nod.json +0 -1
- package/macos/Sources/Resources/Idle/satellite-in.json +0 -1
- package/macos/Sources/Resources/Idle/satellite-out.json +0 -1
- package/macos/Sources/Resources/Idle/scan.json +0 -1
- package/macos/Sources/Resources/Idle/sleep.json +0 -1
- package/macos/Sources/Resources/Idle/sneeze.json +0 -1
- package/macos/Sources/Resources/Idle/stretch.json +0 -1
- package/macos/Sources/Resources/Idle/tilt.json +0 -1
- package/macos/Sources/ReviewStudio.swift +0 -337
- package/macos/Sources/RootView.swift +0 -1093
- package/macos/Sources/StatusOrbit.swift +0 -642
- package/macos/Sources/main.swift +0 -153
- package/macos/Tests/ExpandedHeightProbe.swift +0 -108
- package/macos/Tests/GeometryProbe.swift +0 -98
- package/macos/Tests/IdleProbe.swift +0 -294
- package/macos/Tests/MotionProbe.swift +0 -106
- package/macos/Tests/OutcomeProbe.swift +0 -133
- package/macos/Tests/ScrollbarProbe.swift +0 -91
- package/macos/Tests/expanded-height.sh +0 -13
- package/macos/Tests/geometry.sh +0 -14
- package/macos/Tests/idle.sh +0 -14
- package/macos/Tests/motion.sh +0 -14
- package/macos/Tests/outcome.sh +0 -14
- package/macos/Tests/scrollbar.sh +0 -13
- package/tests/answer-lifecycle.test.mjs +0 -23
- package/tests/board.test.mjs +0 -7
- package/tests/browse-sync.test.mjs +0 -20
- package/tests/mirror.test.mjs +0 -4
- package/tests/peer-range.test.mjs +0 -21
- package/tests/session-state.test.mjs +0 -54
- package/tests/unread-hold.test.mjs +0 -90
- package/tools/desktop-shell/README.md +0 -56
- package/tools/desktop-shell/child-log.mjs +0 -6
- package/tools/desktop-shell/child-log.test.mjs +0 -14
- package/tools/desktop-shell/package-recovery.py +0 -78
- package/tools/desktop-shell/recovery-probe.cjs +0 -50
- package/tools/desktop-shell/renderer-recovery.mjs +0 -95
- package/tools/desktop-shell/renderer-recovery.test.mjs +0 -24
- package/tools/diagnostics/completion-watch.py +0 -101
- package/tools/diagnostics/completion-watch.test.py +0 -17
- package/tools/diagnostics/renderer-diagnostics.mjs +0 -56
- package/tools/diagnostics/renderer-diagnostics.test.mjs +0 -32
- package/tools/idle/LICENSE.open-bot-motion +0 -21
- package/tools/idle/export.mjs +0 -40
- package/tools/idle/index.html +0 -4
- package/tools/idle/motions.js +0 -83
- package/tools/idle/original.js +0 -1673
- package/tools/idle/renderer.js +0 -1673
- package/tools/recording/Client.swift +0 -76
- package/tools/recording/DemoCatalog.swift +0 -174
- package/tools/recording/README.md +0 -23
- package/tools/recording/build.sh +0 -41
- package/tools/recording/main.swift +0 -100
- package/tools/review/ReviewStudio.swift +0 -236
- package/tools/review/audit.sh +0 -19
- package/tools/review/build.sh +0 -7
- package/tools/review/main.swift +0 -158
- package/tools/review/render-audit.swift +0 -32
|
@@ -1,337 +0,0 @@
|
|
|
1
|
-
import AppKit
|
|
2
|
-
import SwiftUI
|
|
3
|
-
|
|
4
|
-
/// Legacy local walkthrough. Launch with `--demo`; the bilingual recorder is in tools/recording.
|
|
5
|
-
@MainActor
|
|
6
|
-
final class DemoStudio: ObservableObject {
|
|
7
|
-
let board = BoardModel()
|
|
8
|
-
@Published var caption = "按「开始」再录"
|
|
9
|
-
@Published var playing = false
|
|
10
|
-
@Published var hoverNotch = false
|
|
11
|
-
private var job: Task<Void, Never>?
|
|
12
|
-
private var generation = 0
|
|
13
|
-
|
|
14
|
-
init() {
|
|
15
|
-
board.previewMode = true
|
|
16
|
-
board.maximumExpandedHeight = 460
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
func reset() {
|
|
20
|
-
job?.cancel()
|
|
21
|
-
job = nil
|
|
22
|
-
playing = false
|
|
23
|
-
generation += 1
|
|
24
|
-
caption = "按「开始」再录"
|
|
25
|
-
clearBoard()
|
|
26
|
-
IdleDirector.previewInstance?.automaticActions = false
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
func start() {
|
|
30
|
-
job?.cancel()
|
|
31
|
-
generation += 1
|
|
32
|
-
let token = generation
|
|
33
|
-
clearBoard()
|
|
34
|
-
playing = true
|
|
35
|
-
caption = "开始"
|
|
36
|
-
job = Task { @MainActor in
|
|
37
|
-
await runScript(token)
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
private func clearBoard() {
|
|
42
|
-
board.expanded = false
|
|
43
|
-
board.showingExpanded = false
|
|
44
|
-
board.wizard = nil
|
|
45
|
-
board.previewMode = true
|
|
46
|
-
board.maximumExpandedHeight = 460
|
|
47
|
-
board.applySnapshot(NotchSnapshot(ok: true, generatedAt: Date().timeIntervalSince1970, origin: "demo", rows: []))
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
private func snap(_ rows: [NotchRow]) {
|
|
51
|
-
board.applySnapshot(NotchSnapshot(ok: true, generatedAt: Date().timeIntervalSince1970, origin: "demo", rows: rows))
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
private func busyRow(_ i: Int) -> NotchRow {
|
|
55
|
-
NotchRow(id: "demo-\(i)", title: "任务 \(i + 1)", child: false, busy: true, unread: false, lastTurn: nil, approval: nil, ask: nil)
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
private func greenRow(_ i: Int) -> NotchRow {
|
|
59
|
-
NotchRow(id: "demo-\(i)", title: "任务 \(i + 1)", child: false, busy: false, unread: true, lastTurn: nil, approval: nil, ask: nil)
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
private func redRow(_ i: Int) -> NotchRow {
|
|
63
|
-
NotchRow(
|
|
64
|
-
id: "demo-\(i)",
|
|
65
|
-
title: "任务 \(i + 1)",
|
|
66
|
-
child: false,
|
|
67
|
-
busy: false,
|
|
68
|
-
unread: true,
|
|
69
|
-
lastTurn: NotchLastTurn(at: Date().timeIntervalSince1970, kind: "error", failed: true),
|
|
70
|
-
approval: nil,
|
|
71
|
-
ask: nil
|
|
72
|
-
)
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
private func askRow(_ i: Int) -> NotchRow {
|
|
76
|
-
NotchRow(
|
|
77
|
-
id: "demo-\(i)",
|
|
78
|
-
title: "任务 \(i + 1)",
|
|
79
|
-
child: false,
|
|
80
|
-
busy: false,
|
|
81
|
-
unread: false,
|
|
82
|
-
lastTurn: nil,
|
|
83
|
-
approval: nil,
|
|
84
|
-
ask: NotchAsk(id: "demo-ask", questions: [
|
|
85
|
-
NotchQuestion(
|
|
86
|
-
id: "q1",
|
|
87
|
-
question: "这条任务下一步怎么走?",
|
|
88
|
-
detail: "录屏用的假问题,点任意一项即可。",
|
|
89
|
-
header: nil,
|
|
90
|
-
options: [
|
|
91
|
-
NotchOption(label: "先改动画", description: "收起和展开再顺一点"),
|
|
92
|
-
NotchOption(label: "先改文案", description: "字幕再短一点"),
|
|
93
|
-
NotchOption(label: "都行", description: "你选就好"),
|
|
94
|
-
],
|
|
95
|
-
multiSelect: false
|
|
96
|
-
)
|
|
97
|
-
])
|
|
98
|
-
)
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
private func runScript(_ token: Int) async {
|
|
102
|
-
func alive() -> Bool { token == generation && !Task.isCancelled }
|
|
103
|
-
|
|
104
|
-
try? await Task.sleep(for: .milliseconds(500))
|
|
105
|
-
guard alive() else { return }
|
|
106
|
-
IdleDirector.previewInstance?.automaticActions = false
|
|
107
|
-
let tour = IdleDirector.basics + ["dance"]
|
|
108
|
-
for id in tour {
|
|
109
|
-
guard alive() else { return }
|
|
110
|
-
caption = "待机 · \(id)"
|
|
111
|
-
IdleDirector.previewInstance?.play(id)
|
|
112
|
-
let wait = max(1.2, IdleDirector.previewInstance?.currentDuration ?? 7)
|
|
113
|
-
try? await Task.sleep(for: .seconds(wait))
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
guard alive() else { return }
|
|
117
|
-
caption = "工作中 · 4"
|
|
118
|
-
board.expanded = false
|
|
119
|
-
snap((0..<4).map(busyRow))
|
|
120
|
-
try? await Task.sleep(for: .seconds(3.4))
|
|
121
|
-
|
|
122
|
-
guard alive() else { return }
|
|
123
|
-
caption = "工作中 → 待决策"
|
|
124
|
-
snap([askRow(2), busyRow(0), busyRow(1), busyRow(3)])
|
|
125
|
-
board.expanded = false
|
|
126
|
-
try? await Task.sleep(for: .seconds(1.8))
|
|
127
|
-
|
|
128
|
-
guard alive() else { return }
|
|
129
|
-
caption = "工作中 → 成功"
|
|
130
|
-
snap([greenRow(0), askRow(2), busyRow(1), busyRow(3)])
|
|
131
|
-
try? await Task.sleep(for: .seconds(1.6))
|
|
132
|
-
|
|
133
|
-
guard alive() else { return }
|
|
134
|
-
caption = "工作中 → 失败"
|
|
135
|
-
snap([greenRow(0), redRow(1), askRow(2), busyRow(3)])
|
|
136
|
-
try? await Task.sleep(for: .seconds(1.6))
|
|
137
|
-
guard alive() else { return }
|
|
138
|
-
caption = "请把鼠标移到黄点上,再选一项"
|
|
139
|
-
|
|
140
|
-
while alive() && board.needsAction {
|
|
141
|
-
try? await Task.sleep(for: .milliseconds(120))
|
|
142
|
-
}
|
|
143
|
-
guard alive() else { return }
|
|
144
|
-
board.expanded = false
|
|
145
|
-
caption = "决策完成 · 回到工作中"
|
|
146
|
-
try? await Task.sleep(for: .seconds(2.2))
|
|
147
|
-
|
|
148
|
-
guard alive() else { return }
|
|
149
|
-
caption = "工作中 → 成功"
|
|
150
|
-
let rest = board.rows.map { row -> NotchRow in
|
|
151
|
-
if row.busy { return greenRow(Int(row.id.split(separator: "-").last ?? "0") ?? 0) }
|
|
152
|
-
return row
|
|
153
|
-
}
|
|
154
|
-
snap(rest)
|
|
155
|
-
try? await Task.sleep(for: .seconds(1.5))
|
|
156
|
-
guard alive() else { return }
|
|
157
|
-
caption = "请点红点"
|
|
158
|
-
|
|
159
|
-
while alive() && !board.failedRows.isEmpty {
|
|
160
|
-
try? await Task.sleep(for: .milliseconds(120))
|
|
161
|
-
}
|
|
162
|
-
guard alive() else { return }
|
|
163
|
-
caption = "请点绿点"
|
|
164
|
-
|
|
165
|
-
while alive() && board.completedUnreadCount > 0 {
|
|
166
|
-
try? await Task.sleep(for: .milliseconds(120))
|
|
167
|
-
}
|
|
168
|
-
guard alive() else { return }
|
|
169
|
-
caption = "结束 · 可按「重来」"
|
|
170
|
-
playing = false
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
struct DemoWallpaper: View {
|
|
175
|
-
var body: some View {
|
|
176
|
-
GeometryReader { geo in
|
|
177
|
-
let w = geo.size.width
|
|
178
|
-
let h = geo.size.height
|
|
179
|
-
ZStack(alignment: .bottom) {
|
|
180
|
-
LinearGradient(
|
|
181
|
-
colors: [
|
|
182
|
-
Color(red: 0.45, green: 0.72, blue: 0.95),
|
|
183
|
-
Color(red: 0.62, green: 0.82, blue: 0.96),
|
|
184
|
-
Color(red: 0.93, green: 0.86, blue: 0.70),
|
|
185
|
-
],
|
|
186
|
-
startPoint: .top,
|
|
187
|
-
endPoint: .bottom
|
|
188
|
-
)
|
|
189
|
-
Circle()
|
|
190
|
-
.fill(Color(red: 1, green: 0.86, blue: 0.45).opacity(0.95))
|
|
191
|
-
.frame(width: 88, height: 88)
|
|
192
|
-
.blur(radius: 0.5)
|
|
193
|
-
.offset(x: w * 0.22 - w / 2, y: -(h * 0.58))
|
|
194
|
-
mountain(width: w, height: h * 0.42, peak: 0.22, fill: Color(red: 0.35, green: 0.48, blue: 0.58).opacity(0.85))
|
|
195
|
-
mountain(width: w, height: h * 0.36, peak: 0.62, fill: Color(red: 0.28, green: 0.42, blue: 0.46).opacity(0.9))
|
|
196
|
-
Ellipse()
|
|
197
|
-
.fill(
|
|
198
|
-
LinearGradient(
|
|
199
|
-
colors: [Color(red: 0.35, green: 0.62, blue: 0.72).opacity(0.55), Color(red: 0.18, green: 0.38, blue: 0.48).opacity(0.7)],
|
|
200
|
-
startPoint: .top,
|
|
201
|
-
endPoint: .bottom
|
|
202
|
-
)
|
|
203
|
-
)
|
|
204
|
-
.frame(width: w * 1.2, height: h * 0.28)
|
|
205
|
-
.offset(y: h * 0.08)
|
|
206
|
-
HStack(alignment: .bottom, spacing: 18) {
|
|
207
|
-
ForEach(0..<7, id: \.self) { i in
|
|
208
|
-
Capsule()
|
|
209
|
-
.fill(Color(red: 0.12, green: 0.32, blue: 0.22).opacity(0.85))
|
|
210
|
-
.frame(width: 10 + CGFloat(i % 3) * 3, height: 48 + CGFloat((i * 17) % 40))
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
.frame(maxWidth: .infinity, alignment: .leading)
|
|
214
|
-
.padding(.leading, 28)
|
|
215
|
-
.padding(.bottom, 18)
|
|
216
|
-
}
|
|
217
|
-
.clipped()
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
private func mountain(width: CGFloat, height: CGFloat, peak: CGFloat, fill: Color) -> some View {
|
|
222
|
-
Path { path in
|
|
223
|
-
path.move(to: CGPoint(x: 0, y: height))
|
|
224
|
-
path.addLine(to: CGPoint(x: width * (peak - 0.18), y: height * 0.55))
|
|
225
|
-
path.addLine(to: CGPoint(x: width * peak, y: 8))
|
|
226
|
-
path.addLine(to: CGPoint(x: width * (peak + 0.16), y: height * 0.48))
|
|
227
|
-
path.addLine(to: CGPoint(x: width, y: height))
|
|
228
|
-
path.closeSubpath()
|
|
229
|
-
}
|
|
230
|
-
.fill(fill)
|
|
231
|
-
.frame(width: width, height: height, alignment: .bottom)
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
struct DemoStudioView: View {
|
|
236
|
-
@ObservedObject var studio: DemoStudio
|
|
237
|
-
@ObservedObject var board: BoardModel
|
|
238
|
-
|
|
239
|
-
private var previewWidth: CGFloat {
|
|
240
|
-
board.expanded ? 320 : (board.isPillHovered ? 42 : 38)
|
|
241
|
-
}
|
|
242
|
-
private var previewHeight: CGFloat {
|
|
243
|
-
if board.expanded {
|
|
244
|
-
return min(max(board.measuredContentHeight, 120), 420)
|
|
245
|
-
}
|
|
246
|
-
return max(44, board.orbitLayout.height + 24)
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
var body: some View {
|
|
250
|
-
ZStack(alignment: .topTrailing) {
|
|
251
|
-
DemoWallpaper()
|
|
252
|
-
RootView(
|
|
253
|
-
model: board,
|
|
254
|
-
panelSize: CGSize(width: 320, height: 460),
|
|
255
|
-
restSize: CGSize(width: 38, height: 44)
|
|
256
|
-
)
|
|
257
|
-
.frame(width: previewWidth, height: previewHeight, alignment: .topTrailing)
|
|
258
|
-
.animation(.spring(response: 0.32, dampingFraction: 0.78), value: previewWidth)
|
|
259
|
-
.animation(.spring(response: 0.32, dampingFraction: 0.78), value: previewHeight)
|
|
260
|
-
.padding(.top, 96)
|
|
261
|
-
.onHover { hovering in
|
|
262
|
-
studio.hoverNotch = hovering
|
|
263
|
-
if hovering && board.needsAction {
|
|
264
|
-
board.expanded = true
|
|
265
|
-
} else if !hovering {
|
|
266
|
-
DispatchQueue.main.asyncAfter(deadline: .now() + 0.38) {
|
|
267
|
-
if !studio.hoverNotch { board.expanded = false }
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
VStack(alignment: .leading, spacing: 10) {
|
|
273
|
-
Text(studio.caption)
|
|
274
|
-
.font(.system(size: 13, weight: .medium))
|
|
275
|
-
.foregroundStyle(.white)
|
|
276
|
-
.shadow(color: .black.opacity(0.35), radius: 4, y: 1)
|
|
277
|
-
HStack(spacing: 8) {
|
|
278
|
-
Button("开始") { studio.start() }
|
|
279
|
-
.buttonStyle(.borderedProminent)
|
|
280
|
-
.tint(Color(red: 0.20, green: 0.48, blue: 0.36))
|
|
281
|
-
.disabled(studio.playing)
|
|
282
|
-
Button("重来") { studio.reset() }
|
|
283
|
-
.buttonStyle(.bordered)
|
|
284
|
-
.tint(.white)
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
.padding(18)
|
|
288
|
-
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottomLeading)
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
final class DemoKeyWindow: NSWindow {
|
|
294
|
-
override var canBecomeKey: Bool { true }
|
|
295
|
-
override var canBecomeMain: Bool { true }
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
@MainActor
|
|
299
|
-
final class DemoStudioDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
|
|
300
|
-
static var hold: DemoStudioDelegate?
|
|
301
|
-
let studio = DemoStudio()
|
|
302
|
-
var window: NSWindow?
|
|
303
|
-
func applicationDidFinishLaunching(_ notification: Notification) {
|
|
304
|
-
show()
|
|
305
|
-
}
|
|
306
|
-
func show() {
|
|
307
|
-
guard window == nil else {
|
|
308
|
-
window?.makeKeyAndOrderFront(nil)
|
|
309
|
-
return
|
|
310
|
-
}
|
|
311
|
-
setbuf(stdout, nil)
|
|
312
|
-
let screen = NSScreen.screens.first { abs($0.frame.minX) < 1 && abs($0.frame.minY) < 1 }
|
|
313
|
-
?? NSScreen.main
|
|
314
|
-
?? NSScreen.screens.first
|
|
315
|
-
let vis = screen?.visibleFrame ?? NSRect(x: 0, y: 0, width: 1280, height: 800)
|
|
316
|
-
let width = min(1280, vis.width - 48)
|
|
317
|
-
let height = min(800, vis.height - 48)
|
|
318
|
-
let frame = NSRect(x: vis.minX + 24, y: vis.maxY - 24 - height, width: width, height: height)
|
|
319
|
-
let window = DemoKeyWindow(
|
|
320
|
-
contentRect: frame,
|
|
321
|
-
styleMask: [.titled, .closable, .resizable, .miniaturizable],
|
|
322
|
-
backing: .buffered,
|
|
323
|
-
defer: false
|
|
324
|
-
)
|
|
325
|
-
window.title = "dsh-notch 录屏模拟台"
|
|
326
|
-
window.delegate = self
|
|
327
|
-
window.isReleasedWhenClosed = false
|
|
328
|
-
window.backgroundColor = NSColor(red: 0.45, green: 0.72, blue: 0.95, alpha: 1)
|
|
329
|
-
window.contentView = NSHostingView(rootView: DemoStudioView(studio: studio, board: studio.board))
|
|
330
|
-
window.setFrame(frame, display: true)
|
|
331
|
-
window.makeKeyAndOrderFront(nil)
|
|
332
|
-
NSApplication.shared.activate(ignoringOtherApps: true)
|
|
333
|
-
self.window = window
|
|
334
|
-
FileHandle.standardOutput.write(Data("DEMO_STUDIO ready \(NSStringFromRect(window.frame)) count=1\n".utf8))
|
|
335
|
-
}
|
|
336
|
-
func windowWillClose(_ notification: Notification) { NSApplication.shared.terminate(nil) }
|
|
337
|
-
}
|