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.
Files changed (102) hide show
  1. package/README.en.md +58 -47
  2. package/README.md +60 -55
  3. package/cordis.patch.yml +3 -0
  4. package/desktop/README.md +29 -0
  5. package/desktop/notch-lifecycle.mjs +120 -0
  6. package/desktop/restart-probe.mjs +61 -0
  7. package/docs/desktop-017rc2.md +23 -0
  8. package/docs/elastic-edge-dock.md +127 -0
  9. package/docs/motion-audit.md +1 -1
  10. package/docs/releases/v0.3.2.md +38 -0
  11. package/docs/releases/v0.3.3.md +9 -0
  12. package/docs/subagent-compatibility.md +51 -0
  13. package/docs/verification/elastic-algorithm-timing.json +337 -0
  14. package/docs/verification/elastic-attention.json +44 -0
  15. package/docs/verification/elastic-frame-timing.json +240 -0
  16. package/docs/verification/elastic-tension.json +59 -0
  17. package/docs/verification/elastic-tip-taper.json +181 -0
  18. package/dshx.yml +2 -0
  19. package/lib/client.js +112 -0
  20. package/lib/client.js.map +1 -0
  21. package/lib/index.mjs +990 -0
  22. package/lib/index.mjs.map +1 -0
  23. package/package.json +66 -10
  24. package/src/board.ts +178 -35
  25. package/src/client/index.ts +104 -0
  26. package/src/dsh-notch.ts +19 -2
  27. package/src/http.ts +17 -2
  28. package/src/session-state.ts +15 -1
  29. package/src/store.ts +3 -3
  30. package/src/types.ts +3 -0
  31. package/BASELINE.md +0 -13
  32. package/DESIGN.md +0 -34
  33. package/cordis.yml +0 -5
  34. package/macos/Package.swift +0 -17
  35. package/macos/STATUS-MOTION.md +0 -49
  36. package/macos/Sources/Client.swift +0 -139
  37. package/macos/Sources/IdleRobot.swift +0 -423
  38. package/macos/Sources/NotchMarkdown.swift +0 -266
  39. package/macos/Sources/Panel.swift +0 -153
  40. package/macos/Sources/Resources/Idle/balance.json +0 -1
  41. package/macos/Sources/Resources/Idle/blink.json +0 -1
  42. package/macos/Sources/Resources/Idle/cube-in.json +0 -1
  43. package/macos/Sources/Resources/Idle/dance.json +0 -1
  44. package/macos/Sources/Resources/Idle/hop.json +0 -1
  45. package/macos/Sources/Resources/Idle/nod.json +0 -1
  46. package/macos/Sources/Resources/Idle/satellite-in.json +0 -1
  47. package/macos/Sources/Resources/Idle/satellite-out.json +0 -1
  48. package/macos/Sources/Resources/Idle/scan.json +0 -1
  49. package/macos/Sources/Resources/Idle/sleep.json +0 -1
  50. package/macos/Sources/Resources/Idle/sneeze.json +0 -1
  51. package/macos/Sources/Resources/Idle/stretch.json +0 -1
  52. package/macos/Sources/Resources/Idle/tilt.json +0 -1
  53. package/macos/Sources/ReviewStudio.swift +0 -337
  54. package/macos/Sources/RootView.swift +0 -1093
  55. package/macos/Sources/StatusOrbit.swift +0 -642
  56. package/macos/Sources/main.swift +0 -153
  57. package/macos/Tests/ExpandedHeightProbe.swift +0 -108
  58. package/macos/Tests/GeometryProbe.swift +0 -98
  59. package/macos/Tests/IdleProbe.swift +0 -294
  60. package/macos/Tests/MotionProbe.swift +0 -106
  61. package/macos/Tests/OutcomeProbe.swift +0 -133
  62. package/macos/Tests/ScrollbarProbe.swift +0 -91
  63. package/macos/Tests/expanded-height.sh +0 -13
  64. package/macos/Tests/geometry.sh +0 -14
  65. package/macos/Tests/idle.sh +0 -14
  66. package/macos/Tests/motion.sh +0 -14
  67. package/macos/Tests/outcome.sh +0 -14
  68. package/macos/Tests/scrollbar.sh +0 -13
  69. package/tests/answer-lifecycle.test.mjs +0 -23
  70. package/tests/board.test.mjs +0 -7
  71. package/tests/browse-sync.test.mjs +0 -20
  72. package/tests/mirror.test.mjs +0 -4
  73. package/tests/peer-range.test.mjs +0 -21
  74. package/tests/session-state.test.mjs +0 -54
  75. package/tests/unread-hold.test.mjs +0 -90
  76. package/tools/desktop-shell/README.md +0 -56
  77. package/tools/desktop-shell/child-log.mjs +0 -6
  78. package/tools/desktop-shell/child-log.test.mjs +0 -14
  79. package/tools/desktop-shell/package-recovery.py +0 -78
  80. package/tools/desktop-shell/recovery-probe.cjs +0 -50
  81. package/tools/desktop-shell/renderer-recovery.mjs +0 -95
  82. package/tools/desktop-shell/renderer-recovery.test.mjs +0 -24
  83. package/tools/diagnostics/completion-watch.py +0 -101
  84. package/tools/diagnostics/completion-watch.test.py +0 -17
  85. package/tools/diagnostics/renderer-diagnostics.mjs +0 -56
  86. package/tools/diagnostics/renderer-diagnostics.test.mjs +0 -32
  87. package/tools/idle/LICENSE.open-bot-motion +0 -21
  88. package/tools/idle/export.mjs +0 -40
  89. package/tools/idle/index.html +0 -4
  90. package/tools/idle/motions.js +0 -83
  91. package/tools/idle/original.js +0 -1673
  92. package/tools/idle/renderer.js +0 -1673
  93. package/tools/recording/Client.swift +0 -76
  94. package/tools/recording/DemoCatalog.swift +0 -174
  95. package/tools/recording/README.md +0 -23
  96. package/tools/recording/build.sh +0 -41
  97. package/tools/recording/main.swift +0 -100
  98. package/tools/review/ReviewStudio.swift +0 -236
  99. package/tools/review/audit.sh +0 -19
  100. package/tools/review/build.sh +0 -7
  101. package/tools/review/main.swift +0 -158
  102. 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
- }