agentix-cli 0.103.2 → 0.103.3

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.
@@ -7,8 +7,9 @@ import AppKit
7
7
  /// The answering agent can send it to something on screen, which it
8
8
  /// marks (#482). Chosen with `voice.look`; the orb stays the default.
9
9
  ///
10
- /// It reacts and never interrupts: its window takes no keys, no clicks
11
- /// but a drag of its body with ⌘ held, and never comes forward by itself.
10
+ /// It reacts and never interrupts: its window takes no keys and never
11
+ /// comes forward by itself. It takes a drag of its body with ⌘ held, and
12
+ /// idle with no bubble a click and a right-click on its body (#579).
12
13
  /// With Reduce Motion on, or "Animated orb" off in the menu, it is a
13
14
  /// still picture that changes between states and stays where it rests.
14
15
  @MainActor
@@ -72,6 +73,15 @@ final class CharacterHost {
72
73
  /// Carried by the pointer: where the pointer took hold, and where it
73
74
  /// stood then, which is not where it rests once it has stepped aside.
74
75
  private var carried: (from: NSPoint, rest: CGPoint)?
76
+ /// Idle with no bubble, a click on it talks and a right-click opens
77
+ /// the menu, as on the pill (#579). Set by the app.
78
+ var onClick: (() -> Void)?
79
+ var contextMenu: (() -> NSMenu)?
80
+ private var clickable: Bool {
81
+ CharacterMath.clickable(activity, asked: asked, sent: guide.showing != nil, bubble: bubbleFrame != nil)
82
+ }
83
+ /// The press that carries it began as a click.
84
+ private var pressed = false
75
85
  /// What the answering agent sent it to show (#482), and its mark.
76
86
  private let guide = CharacterGuide()
77
87
  /// Sent somewhere, and what its bubble says there (#562).
@@ -102,6 +112,10 @@ final class CharacterHost {
102
112
  window.ignoresMouseEvents = true
103
113
  window.contentView = view
104
114
  view.onDrag = { [weak self] phase in self?.drag(phase) }
115
+ view.onMenu = { [weak self] event in
116
+ guard let self, let menu = self.contextMenu?() else { return }
117
+ NSMenu.popUpContextMenu(menu, with: event, for: self.view)
118
+ }
105
119
  view.unit = Self.diameter / 100
106
120
  view.edge = CGPoint(x: Self.size.width / 2, y: Self.size.height - Self.ground)
107
121
  motionObserver = NSWorkspace.shared.notificationCenter.addObserver(
@@ -206,12 +220,13 @@ final class CharacterHost {
206
220
  self.timer = timer
207
221
  }
208
222
 
209
- /// It takes the pointer only to be moved: on its body, with ⌘ held.
210
- /// Any other click goes through it to the window behind.
223
+ /// It takes the pointer on its body: with ⌘ held to be moved, and
224
+ /// idle with no bubble to be clicked. Any other click goes through it
225
+ /// to the window behind.
211
226
  private func aim() {
212
227
  // The button came up where no event told of it.
213
228
  if carried != nil && NSEvent.pressedMouseButtons & 1 == 0 { drop() }
214
- let take = carried != nil || (NSEvent.modifierFlags.contains(.command) && body.contains(pointerSource()))
229
+ let take = carried != nil || ((clickable || NSEvent.modifierFlags.contains(.command)) && body.contains(pointerSource()))
215
230
  if window.ignoresMouseEvents == take { window.ignoresMouseEvents = !take }
216
231
  }
217
232
 
@@ -219,20 +234,26 @@ final class CharacterHost {
219
234
  switch phase {
220
235
  case .began:
221
236
  let mouse = pointerSource()
237
+ pressed = clickable
222
238
  if body.contains(mouse) { guide.end(glide: false); onGuide?(); carried = (mouse, CGPoint(x: body.midX, y: rest.y)) }
223
239
  case .moved:
224
240
  if carried != nil { tick() }
225
241
  case .ended:
226
242
  if carried != nil { tick() }
227
- drop()
243
+ drop(click: pressed)
228
244
  }
229
245
  }
230
246
 
231
- /// Let go: where it was carried to is kept. A click alone moves nothing.
232
- private func drop() {
247
+ /// Let go: where it was carried to is kept. A click alone moves
248
+ /// nothing; on a character that could be clicked, it talks.
249
+ private func drop(click: Bool = false) {
233
250
  guard let was = carried else { return }
234
251
  carried = nil
235
- guard pointerSource() != was.from else { return }
252
+ let now = pointerSource()
253
+ guard hypot(now.x - was.from.x, now.y - was.from.y) >= 4 else {
254
+ if click { onClick?() }
255
+ return
256
+ }
236
257
  place = rest
237
258
  Config.characterPlace = rest
238
259
  }
@@ -287,7 +308,7 @@ final class CharacterHost {
287
308
  edges = strolls && Config.playMode ? Meets.edges(of: Meets.windows(), band: (top - Self.head)...top) : []
288
309
  }
289
310
  frame = sim.step(to: now,
290
- CharacterSim.Input(activity: activity, level: level, pointer: pointer, held: held, sent: guide.showing != nil, asked: asked, strolls: strolls,
311
+ CharacterSim.Input(activity: activity, level: level, pointer: pointer, held: held, clickable: clickable, sent: guide.showing != nil, asked: asked, strolls: strolls,
291
312
  edges: edges, shows: bubbleShows?() ?? false, plays: Config.playMode, animates: quiet ? 0 : animates, down: NSEvent.pressedMouseButtons & 1 != 0,
292
313
  home: home, range: Double(spot.ends.lowerBound)...Double(spot.ends.upperBound)))
293
314
  }
@@ -44,6 +44,12 @@ enum CharacterMath {
44
44
  }
45
45
  }
46
46
 
47
+ /// It can be clicked (#579): idle, where it rests, with no bubble to
48
+ /// click in its place. Then it waits for the pointer and takes the click.
49
+ static func clickable(_ activity: Activity, asked: Mood?, sent: Bool, bubble: Bool) -> Bool {
50
+ activity == .idle && asked == nil && !sent && !bubble
51
+ }
52
+
47
53
  /// The state it goes through on the way from `from` to `to`, if any:
48
54
  /// it notices you before it listens and when it wakes, and nods once
49
55
  /// it has heard you.
@@ -78,10 +78,11 @@ struct CharacterSim {
78
78
  // The pointer comes close: out of its way. Left alone: back home,
79
79
  // unless the pointer is resting there.
80
80
  let near = input.pointer.map { abs($0.x - x) < reach && abs($0.y) < tall } ?? false
81
+ let held = input.held || (input.clickable && near)
81
82
  // Idle with play mode on, a game with the pointer comes first.
82
83
  // Sent to show something, it does not play.
83
84
  var game = PointerPlay.Out()
84
- if input.plays && !input.sent && !input.held && !input.shows && mood == .idle && input.activity == .idle {
85
+ if input.plays && !input.sent && !held && !input.shows && mood == .idle && input.activity == .idle {
85
86
  game = play.step(now, dt, x: x, pointer: input.pointer, down: input.down, range: input.range)
86
87
  } else {
87
88
  play.stop(now, down: input.down)
@@ -91,7 +92,7 @@ struct CharacterSim {
91
92
  restSince = now
92
93
  // The stroll is over: sent home again, it rests there.
93
94
  strolled = 0; strollTo = 0; strollNext = nil
94
- } else if input.held {
95
+ } else if held {
95
96
  target = x
96
97
  awayUntil = max(awayUntil, now + Self.awayFor)
97
98
  } else if let to = game.target {
@@ -133,7 +134,7 @@ struct CharacterSim {
133
134
  if let at = pending, now >= at { pending = nil; go(goal, now) }
134
135
  var pose = change.pose(at: now)
135
136
  // Idle, left alone and standing: a small animation now and then.
136
- let alone = mood == .idle && input.activity == .idle && input.asked == nil && !input.sent && !input.held && !input.shows
137
+ let alone = mood == .idle && input.activity == .idle && input.asked == nil && !input.sent && !held && !input.shows
137
138
  && !near && game == PointerPlay.Out() && play.game == nil && abs(speed) < 40 && strolled == 0 && strollTo == 0
138
139
  let own = idle.step(now, dt, free: alone, gap: input.animates, dozeIn: restSince + Self.dozeAfter - now)
139
140
 
@@ -12,6 +12,9 @@ extension CharacterSim {
12
12
  /// The pointer is on its speech bubble: it stays where it is, so
13
13
  /// the bubble is not pulled from under the pointer.
14
14
  var held = false
15
+ /// It can be clicked (`CharacterMath.clickable`): it waits for a
16
+ /// pointer that comes close, and plays no game with it (#579).
17
+ var clickable = false
15
18
  /// The answering agent sent it to show something (#482): it goes
16
19
  /// to `home` at once, awake, whatever the pointer did before.
17
20
  var sent = false
@@ -12,15 +12,18 @@ final class CharacterView: NSView {
12
12
  /// Its speech bubble in this window, origin bottom-left; nil while hidden.
13
13
  var bubble: NSRect?
14
14
 
15
- /// A drag of its body. Its window takes the mouse for nothing else.
15
+ /// A press on its body: a drag, or a click when it did not move.
16
16
  enum Drag { case began, moved, ended }
17
17
  var onDrag: ((Drag) -> Void)?
18
+ /// A right-click on its body.
19
+ var onMenu: ((NSEvent) -> Void)?
18
20
 
19
21
  override var isFlipped: Bool { true }
20
22
  override func acceptsFirstMouse(for event: NSEvent?) -> Bool { true }
21
23
  override func mouseDown(with event: NSEvent) { onDrag?(.began) }
22
24
  override func mouseDragged(with event: NSEvent) { onDrag?(.moved) }
23
25
  override func mouseUp(with event: NSEvent) { onDrag?(.ended) }
26
+ override func rightMouseDown(with event: NSEvent) { onMenu?(event) }
24
27
 
25
28
  override func draw(_ dirtyRect: NSRect) {
26
29
  guard let ctx = NSGraphicsContext.current?.cgContext else { return }
@@ -477,6 +477,8 @@ final class App: NSObject, NSApplicationDelegate {
477
477
 
478
478
  registerHotkeys()
479
479
  panel.onClick = { [weak self] in self?.toggleListening() }
480
+ character.onClick = { [weak self] in self?.toggleListening() }
481
+ character.contextMenu = { [weak self] in self?.statusMenu.menu ?? NSMenu() }
480
482
  statusMenu.onSettings = { [weak self] in self?.settingsWindow.show() }
481
483
  settingsWindow.model.onSaved = { [weak self] saved in self?.apply(saved) }
482
484
  // While a shortcut field waits for keys, ours stand aside so the
@@ -1,2 +1,2 @@
1
- import{A as a,B as b,C as c}from"./chunk-HVPRCHRG.js";import"./chunk-SFQUP3BP.js";import"./chunk-QOYAX2FT.js";import"./chunk-5MUJZODV.js";import"./chunk-7I7IEX4S.js";import"./chunk-JHCI6XCJ.js";import"./chunk-DSYKYZMT.js";import"./chunk-QIJLFPWE.js";export{a as createAgentContext,b as generate,c as generateStream};
2
- //# sourceMappingURL=agent-MUGC4RZP.js.map
1
+ import{A as a,B as b,C as c}from"./chunk-MZQQVT3G.js";import"./chunk-SFQUP3BP.js";import"./chunk-QOYAX2FT.js";import"./chunk-5MUJZODV.js";import"./chunk-7I7IEX4S.js";import"./chunk-JHCI6XCJ.js";import"./chunk-DSYKYZMT.js";import"./chunk-QIJLFPWE.js";export{a as createAgentContext,b as generate,c as generateStream};
2
+ //# sourceMappingURL=agent-NPE76XHC.js.map