@jian-ai/remote-control 0.8.2 → 0.8.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.
@@ -3,7 +3,7 @@
3
3
  Jian Remote Control — Linux computer-use helper (X11).
4
4
 
5
5
  Newline-delimited JSON on stdin → JSON lines on stdout (same cmds as macOS helper).
6
- Overlay: override-redirect window with geometric white pointer + soft blue glow.
6
+ Overlay: override-redirect window with white pointer + #2880F8 stroke/glow (parity with macOS sprite).
7
7
  Input: xdotool when available (save/restore cursor around clicks so the user's
8
8
  pointer is not left at the Jian position). Wayland: best-effort; overlay may be
9
9
  unavailable — input still works when ydotool/wtype/xdotool can reach the session.
@@ -279,13 +279,14 @@ class Overlay:
279
279
  cmap = X11.XDefaultColormap(self.dpy, self.screen)
280
280
  white = XColor()
281
281
  exact = XColor()
282
- if X11.XAllocNamedColor(self.dpy, cmap, b"#FAFAF8", byref(white), byref(exact)):
282
+ if X11.XAllocNamedColor(self.dpy, cmap, b"#FFFFFF", byref(white), byref(exact)):
283
283
  X11.XSetForeground(self.dpy, self.gc, white.pixel)
284
284
  glow = XColor()
285
- if X11.XAllocNamedColor(self.dpy, cmap, b"#3A7AD6", byref(glow), byref(exact)):
285
+ # Match macOS ship sprite stroke #2880F8 (geometric stand-in; bitmap is macOS-only).
286
+ if X11.XAllocNamedColor(self.dpy, cmap, b"#2880F8", byref(glow), byref(exact)):
286
287
  X11.XSetForeground(self.dpy, self.gc_glow, glow.pixel)
287
288
  ink = XColor()
288
- if X11.XAllocNamedColor(self.dpy, cmap, b"#171716", byref(ink), byref(exact)):
289
+ if X11.XAllocNamedColor(self.dpy, cmap, b"#2880F8", byref(ink), byref(exact)):
289
290
  X11.XSetForeground(self.dpy, self.gc_ink, ink.pixel)
290
291
  self.available = True
291
292
  return True
Binary file
@@ -5,8 +5,9 @@
5
5
  Protocol: newline-delimited JSON on stdin → newline-delimited JSON on stdout (flush each).
6
6
  Errors go to stderr in English only.
7
7
 
8
- Pointer UX (Lucas / v0.8.2 polish):
9
- - Geometric white arrow (anathema-white style) with a soft brand-blue glow underneath.
8
+ Pointer UX (Lucas / v0.8.3):
9
+ - Bitmap sprite: white fill + thick #2880F8 stroke + soft blue glow (embedded PNG).
10
+ Prefer sprite over live CGPath so AA/glow match the approved preview.
10
11
  - Agent actions save the user’s cursor, hide it briefly, drive HID at the target, then
11
12
  restore — so clicks do not leave the physical mouse stranded at the Jian position.
12
13
  - Overlay tip animates (ease-out) between positions; system cursor is not the visible UX.
@@ -79,18 +80,151 @@ private func intValue(_ object: [String: Any], _ key: String) -> Int? {
79
80
  return nil
80
81
  }
81
82
 
82
- // MARK: - Colors (Jian + geometric white pointer)
83
+ // MARK: - Pointer sprite (embedded PNG)
83
84
 
84
- private let jianInk = NSColor(srgbRed: 0x17 / 255, green: 0x17 / 255, blue: 0x16 / 255, alpha: 1)
85
- /// Soft brand blue under the pointer — visible, not neon.
86
- private let jianGlow = NSColor(srgbRed: 0x3A / 255, green: 0x7A / 255, blue: 0xD6 / 255, alpha: 1)
87
- private let pointerWhite = NSColor(srgbRed: 0xFA / 255, green: 0xFA / 255, blue: 0xF8 / 255, alpha: 1)
85
+ /// Soft click-ripple blue (matches ship stroke #2880F8).
86
+ private let jianGlow = NSColor(srgbRed: 0x28 / 255, green: 0x80 / 255, blue: 0xF8 / 255, alpha: 1)
88
87
 
89
88
  private let sessionAlpha: CGFloat = 0.94
90
89
  private let idleAlpha: CGFloat = 0.18
91
- private let pointerSize: CGFloat = 36
92
- /// Tip of the geometric arrow (hotspot) near the view's top-left.
93
- private let hotspot = CGPoint(x: 2, y: 2)
90
+ /// Padded sprite canvas (40 pt blade + 10 pt glow pad each side).
91
+ private let pointerSize: CGFloat = 60
92
+ /// Tip hotspot in the padded 60 pt view (content tip was (2,2) in the 40 pt box).
93
+ private let hotspot = CGPoint(x: 12, y: 12)
94
+
95
+ /// Ship asset `macos/jian-pointer.png` (@3x master) as base64 — single-file helper stays
96
+ /// reliable when only the binary is copied into Application Support (no sidecar PNG).
97
+ private let jianPointerPngBase64 =
98
+ "iVBORw0KGgoAAAANSUhEUgAAALQAAAC0CAYAAAA9zQYyAAAaf0lEQVR4nO1dW6wkV3Vd+1T38EcQ"
99
+ "Fi+bGc+MZ+w4g4AoJIoQkSIh8oNEflAUPhARQrZxHIcoxglfiD+IQTaDI5IoEZHgI5FCflBeH0mk"
100
+ "RCbGCEuQsQdzPXc8Y5vwCijhJ3du19n5OK99TlXfe7v7VN+err3s7nr0o2qq1121zt7n7AMoFAqF"
101
+ "QqFQKBQKhUKhUCgUCoVCoVAoFAqFQqFQKBQKhUKhUCgUCoVCoTh20HGfwHyQODfm4zsPxc2EYyQ0"
102
+ "VTq2kl2RsCZC95B31SPPpbESfMxYA6EFmXuPRnM35oB7V3s2ldzjw4CELlS5y9tDjl2+fCA3ufOe"
103
+ "7O1K7LFgIELPU+U+IpNcULG3A04rXRJnO5TcY8QAhO4jc0lkAigxueckDjqvDlPZPfURnPuJraTe"
104
+ "VlQmNM3T2STDFNgtX6O+M+nZyfFJrqLLaZ5LbCX1VqMioXvJfACRKawWxD2kEemoW8ouC4KnHUrs"
105
+ "0aESoQ8nc5fIgbgkxbrfX2eQqizJGYjJGbl7iK2k3mJM6n/lXDKT42sgvyB011NT8X0Ckb2UFJn9"
106
+ "+8Sqf4UAcNpmt0VuiwK3iZTU24EKCi3VeR6ZpSqLdU9wimTPuEy9ycRcjd0Tc3xy+5j9WxhJoVWt"
107
+ "R4A6Ct3hXYfMXoUDodOOqNpOsenOj/3wp+FbvvPJ170yHYDlsTjxjhlgJqfDgbhecYMgu/eSV29h"
108
+ "sv0fEHM6hKr1zYwVFbpPnecocyKzYzZRRvI7P/aDn/YdYedTt77KfS6KqfDK0ic7YoulV225H/F9"
109
+ "c9VaY9Y3NWoSOnjjaJZz9XVLgnHbhxB5HnYeOXkLgEBMwKk1g9mz2HaJHdaL/eg2GtWC3OSoROh5"
110
+ "6izJbIIqE8gQiMydf/j9/13mqM9/5sxrI6EjkQNRreW0fhDZD/DWQc2Rbys2Hit46DJUB3S8MoQy"
111
+ "JzIbENGyZAYAMpOGBaHJk9XtM0Rk3TbIghypyVkOL82e1MJbewcewiRui4Jah9apEnvTsYJCp2gc"
112
+ "kIjsNNoAgAEZgMgkm2GMU2pjQGTOP/zyj1c5+SuP3XVbICdHNbaWnRJbp9CWmdM6/I6uNQF61Fot"
113
+ "yE2GGoSOcWXKIxnk1JKMsxhOmYkaAzLm/MMvrUTmgN2LF05xICuz9es2EdtaWGbAitekorNFEm+1"
114
+ "IDc5KhE6euegxADIkPfKgdBOmRs6//BLP6l0/gCA3cfffMYR1BHZq7FlG4md9ol1562TzxYNR09p"
115
+ "ZsliVevNR8VMobfMyYL4DZ88caR2Sl0ZZx/41lUA2H38LWeTOreGYFtma4hbC6aWmYiYCEyWmQC2"
116
+ "TL616FhqQ6aRiV2LoIhZc0o/Ahqz3jwsQa6gwkAQeOrkqlOgw9kMQwCZqNID4ewD39ylZjqFmU7J"
117
+ "TCdophNqJg2ZSQMzmRA1DfzDrRsDagyRMd4auQecPUL4h6ZsPIk7Uroeio3BquSSzE6ZvxiLjiE6"
118
+ "H7YbRqElztz/9A410wk10wkZ94CZTsg4YrtHY2Aak0jtrFC6i1D2iFYqkTeRWqb0FceOupbDL72j"
119
+ "TmQIihcVcFic+fDXnwOAq5//xbvAxoJMCzbEZCzZ1lkP27ZsYIkJYAKzJTAsOQthmRmgaEFC2tx1"
120
+ "itK0+caiErlILoV0CZWLUQ6zNiU78+GvP0cmqPVkQmYygVs2aRktiIGPwKBrQWiOBYFakM1CDUKn"
121
+ "BItMrGRZwmg5DMzwCi1x+r6vXSZzYormxDSzIs5bT4gmDZmmgWkaIiMtiFELcvNhBXJ1MoVhW0hX"
122
+ "SLYEhVvMQ1/++CuWPz2B0/d+9Zkz9z31bfKkRhO89aShZuKW1BiYiSHTGOexTWzEUqHSgdRRtPPI"
123
+ "jqr1MWI1tSwyheG/dBuWqmaWinLUIjUAnL73yWc9qaNaIzUWJ06pJ30WxFsmtSCbjsoeOhkP8YNL"
124
+ "H+1u4QuiKqnv8WrtQnvT4LHRJFJHC2KahowRvlotyKajrp+NiRXpnfMedstGOS5//BV1iX3f1y5T"
125
+ "c2JCzYkpUniv8Q1HR2oXszaO4H0WxKgF2TCsSGgSi/jjdW65FNXM37pXQF0L8h/PevvhH5NIah+v"
126
+ "dkkZaky/BSG1IBuGlRMrc7YzqwGYQrFXQ2ULcslZkBOTaEOaaRbWS8tgQRq1IBuKumE7v0KxsZga"
127
+ "hxSiHagTtqtvQZ70FmQaiB0yjBMkpfYWxKgF2VBUjgnnCRb/HHxJahhWRH0LcmKKEK9uJhMy08al"
128
+ "0Bu1IDcBKhA6KHG+wzcQw3r+qIy6FuSJSz5mPaHmxATJW6sFuQkwQNaOslXZ7S766AFQ3YLc++Tl"
129
+ "GNaL1iMsJ2pBNhQVCU0kFMc/idtt+jEH/dEqW5BnnAXx3rpJ5A72Qy3IZqF2KTAqVsm7j8EVWiKQ"
130
+ "+u5P7K38XafveeISALzwp7/8c6AZsW/fsiUitK3rfkfE3PrBA3CDB6xlAiyHgQMCYlAufO88AkgH"
131
+ "5VbAMI1CKrY9qZP9WA+qp807FmSS+oRIC0JqQY4LQ/R88w3BuOmNiBDrNWJ4CzKZpERMGB1T24Io"
132
+ "sY+KgaqPzldqrJvRWKcFIWdBLBGbxSwIqGf8IslBuURqQQ7HkH2T0+0zhaWl9Kwdm2xBetRaLcgS"
133
+ "GJDQKdxx8L71YqMtSB6z9kcRFkRj1odiAMshIZMr2AQ+A9hgC6IlFFbGmoZDldG8zcCwFmQ6z4I0"
134
+ "cyyIK8hzaAkFqAU5AGsc30dRozcJx2BBjLAgjbAgvr+4iNerBVkYA1uOEuuNQx8Vw1kQ6rMgRJZa"
135
+ "b0GsK6FAWsWpEtY6AnvTMYgFkQVvfO+92NdaqzhVhxK6QHULIksohH4gzVGrOFHnoT33DsaaLcfN"
136
+ "gcEtiCViBAtCfVWcAD/zka9njSNUcVILAlXoAzFICQVTWpBJgxSv7liQWMXpaIkYd7ARWxAl9CGo"
137
+ "XULBR0FyCxKiIKmKkyFRlqxIxJRRkH5SR4yL1Go5joDBLAjKKAiCBQEb+CgIwBaAi4VYlkkYIFgQ"
138
+ "ZmIXDwlFJElOUzce+6EKvQDqW5BOPZBJqrPn+oH4uiByFoR8mBd67Qcg+9K4lVEotRJ6QdQelHvm"
139
+ "w19/jprp1A/GFX56YsgkP51XcCIqrUc/qUtPvf1QQi+B2uMX3aDciR9lHhqJvnKTSRnFVDA+xqqj"
140
+ "Qh/aUAQwBpVWQq+AunVBnvo2ykZiHFneITX5GcaKcmtIpC5leiTWQwm9Iuoq9dcun7n/6Z1QkN1X"
141
+ "QhU+OpZL8DMixJnGQtmIRN+s68xWcziDEroCqluQ+7+xE5Q5VkPt+GjfjzqyWSp19M55NnEEUEJX"
142
+ "RF1SP71DSaVD1KMR8WlKE5qaLOKB6EOyDo7lOM+thBK6MmqT+uwD37qa+nu4KT3IhBAeBesh/bT7"
143
+ "cNLmPOqx5aRWQg+A2hbk7O/85wtuygxP6uijTayFAGmkc7Uei9sAoIQeFFVJ/eClayT6d6RZebNE"
144
+ "C7KeeOn5gNT4dkFT3wOjZtr87IPPXgeAK4+ev5XJMoGYXZiDmVMcml1HPM9gHkfO20MVek2oqdZ3"
145
+ "/N7Od8/9/tXvd3rgSZWOXjqEp3NXHV/cMiih14iapAaAcw+98MNYVSyPcABxRIsc97b9jUMl9JpR"
146
+ "u8F47qHrPxIKDUjPXCQMO8zdQlIroY8JNUl9/uGXfhzZm4IbiaS0hcydAyX0MaIWqXceOXVLsh0A"
147
+ "ksXI+bvtbIYS+thRw4Kc/+j1/z7/8Ms/EYNk0ekXnfcl3drwnRJ6Q1BHrbeTpItACb1BWFmp/+C/"
148
+ "/kdsFoo8DiihNwyrWxA6YHP7ea2ZwgVRI+M3LMaUF+xCFXoBbD6ZM7B4znZtM5TQiq2CEnrrwfFp"
149
+ "DFBCbxG+88nXvdIVlGEWHC7IvN3cPgZCb/cFPWZw4Zk5e20EWDOhmTGSC7tu7Hzy9T/jLy+H/+OL"
150
+ "gddiz7b+DGsitL/QYVVRFTufesOrOLcaoq7dfMneRgwch45EdnoxrvbJ4Nh55OSrwS2DOXsIcos7"
151
+ "4jiu+4CE7mOvMroGnv/06dcwtxZsGeyeMgJLHRb3Re79OYBtEu6BCC2Imyvz1nq3oXHl0fO3Mrct"
152
+ "rLXM1pPZsuOztVGd2RObpQXJPHXxG2wPmYFBCC1uc3nkKPi6tV/Ao2b4DutDcfnjr1gqW/i9r/z2"
153
+ "e7nd2+d2f5/bG/vc3phxu78Puz/jdn/Gdtaync0Ql61l27bMsxbWWnDbMrfWrUcye2JnVgMZX3mO"
154
+ "gmyhMgdUbhR2bnVIBOdCKIbH3Z/YW4iAR3nvUh2HbGth25btrIWdtbBtG7bZti3btvVk9tszT+a2"
155
+ "ZW5bto7MzK11VqO1zOzJ7EjNsMlHI1dnPw8LMIff24TaUY4+ZUZgMstox4BYlMjlZ2vj9b/+J3/L"
156
+ "bB0prQ3qa2HbFuzJzG0guoVfd6ocCNxGdRaWwwKCzNJLs2yvbDeJJSoSurx2SZkdk6W3Gw7r6EC0"
157
+ "lEqzZXh1RSA3W8u2tcy2hW2jAnOwFs5uBM8syMyJzFKVmaXJ8Jd9nnfeTtRUaJ6nzEh85qEUehVV"
158
+ "Xgdufe8X/4EjQkMuLFsWpHUkto74XTKnRqD/nrwR6MUjkXn7IxsSFQhd2ojcM6fXh1Po2kQezEuD"
159
+ "GbAMWO/AJEETeTkncKHM3LUZmTJzce3dgXOB3k4yA8M2CoMyF4F/W+1ibroql3jj+/7mX8HhP5EA"
160
+ "kZEKjkQNcWbOGoDI3iPJHBuC3EfmkaAGoYuGYNhI8hxVmq37cSpgaCIPp9KAvCzcR+zgjSN5rVf2"
161
+ "XjJnNmMumUegzsAgUY4Q0QAn9xEi/uGWuxpuJlUucfL9X3kije3rOLWCqIVVm9sA7PXMoyMzsDKh"
162
+ "WahzvHipEdj7oyxP6E20GMuo9Knf+qenitoZBeL1y3ZySVCOF/UIZB4HKkc5sh8hWg0ORGZrl7Uc"
163
+ "x0HkQY+ZzWAFzCG3VGaU4hBuhEXiZA6Zt1+dgWqEliSGuB32NXjsQoTeRFUusYxK3/7Bf3k6q4Hb"
164
+ "U0tRIrJU3g3dqlBmQeTMZoyDzEC1sF1YC1cyqIlv8yCLoR754g5B5B/840PvX+T961PpUPX2ABvi"
165
+ "ltHLdVrifS5lZFiB0CwvYBKQtJ03YHwMlRdU6Jr43lfuf6+d7e19/+8/8r7a372USn/o376ZzQRL"
166
+ "afLMo9eEKdTZrY9KlSVq9LZjwM2D4H4EZjATyEkGeQNNsMalc9u1E/q7X/7Au90EO36OEtM06z6H"
167
+ "uaBUhT9MAMREAOczabplxxv3sHmcRA6o66FZ3BZjiMnHUJ06L+yhV8XLf/Ub7+TZ3h7P/u+Gne3d"
168
+ "4HbvBs/2bizyHbW6n/bh9D1PXJKkzubsTlNJ9Ot1T1Bj7KjfOUkEnyWxWfbhXQNe/NJ73vHil97z"
169
+ "DtsmEvvlvm339r/75Q+8e4jjLkNqIhKT0RvqeOswJb1/+3w7Mm51BpYidAqHAvB5XL8a8lQpqpR6"
170
+ "g4l+C88/eu4NdU6/H9f/8td+yRN4n2eOxNze2PfLG/6xv8h3Dtk4PH3vk8+KqdmKGWHD5Gxid8D2"
171
+ "115cGEsqdCB1bAIKaYgKjdQ48X0S4Elth1Ppa3/xq291hN3b55kjriBx2N7n9sb+y3/9m+8a4hyW"
172
+ "SonHOQezWWEFwaX1SGERv13pzG9+VByC5TlMFNZTYgXMBMtgYoa1CzThj4wX/uztFzIfGarZE4k5"
173
+ "sRsDtpZ44u8cm+M8z9z31Lev/vHPnwMMsfDRDL/q2tm+5d1z2gTfkBy37RggsRIag9J/2KDOvntk"
174
+ "a5//9OnX1Dk2cPXzb7uTZ0l5ex6z4rHvxvLtzxY5ziK2YzmVDopc+migUGqoKvejZm87kcnyKylV"
175
+ "KzukR1KveuDdx996dvfxt5x15Lyxnz08Ybndn7nBqX5Qqt8XBqi++KX3vGPV86iFM/c/vRMnoxeV"
176
+ "9+NkQMW0KVTuUNRIrIieMrGJWHSDFA1DSeqdR07esuzRdy9eOMX2hiesJ3DxQCCuTQRGO5uxnaV9"
177
+ "drZZKi0noCefPRQWmsJchH1cjpMGjRe1PLRLrqR0oZ9yGgC5FiPBsvv7sZYBQ4AFmHf+6I2vdkkP"
178
+ "onMPXfvRvANceezO24ojyiyDCMSmmc2iFzXGMAzDiM49/r1ERC9+8d1vP/n+v/tqnUuxIrIJNP06"
179
+ "IzE6XecDLfVYUYnQ/vo65sKx2KuI28cMMLFFIjUT2BgitgATM9HOIydvIRCd++j1SOznP3PmtZnq"
180
+ "ZD8gJ1KHn1gQ2iubARsLMobADUcOSI9qBlO1ZWp5EIg42gpQh6+uAQjPZ3L/em0QAit7L+nt0vx3"
181
+ "KQ8gwk4iUeCSCATAmJgZS0kEkTFLp1j22uEyPRZT7yK05b7KuDtAY0CmITNpYCYNmUlDzXQKM52Y"
182
+ "5sT01Af/+RuL/MsXsROLEnr34oXbud3fZ7vfws5m7OtzpJIGceR3snUAOuHUEWKITGHoCoasYSgH"
183
+ "eGZFUnwj0VpZhyIWY4Gdtb4uRRsfRcEW956ZhfW1LmIhF1eByFUimvnPz0RRl1kLdtWKrn/hXW+r"
184
+ "dy1yLOqlzz74zLX5s7GN2iIfihUtB7tbXbzdR+/hXyQQOHVc4uRL3GvEIv1FADHH7xM2g/PIdSdV"
185
+ "WQagKUi9u0swmIk5/vGyIRATwZqWyRAwc3HqBXD3J/aqztctsXvxwu0H+2I1zvNQoz+0CNdlRBNP"
186
+ "PePkfGclzofri0IsbZvVqRCP2GvPVR6yKD8f6sH5SkWpwKG/XYfacUHpffWi61945y+sfj36sQj5"
187
+ "zz74zDW3No+0SuZ5GKDguUh9+4R3TINn9iMUSAlVNCPBU0FC6y2IJ6V7BFviKw+xFWRPxPWVijhV"
188
+ "LArkdvbFVzEKteQSwRfAsCNp8r4FMjyqmI9aUY7Cevh4nW+KMxPge/lK8yFSMeTb6t6OsLinytvr"
189
+ "gT9mOB7YH5tcdJBY+BdmIrIEhm0BS0StddZjRrCL2Y5FsAT5g5Uqb3uKA1CzL8ccUntPzcRMHANO"
190
+ "ediY2XdoR/oRQ7Ks50ec35L3n3PcZrLefxv4/iQW7EJixC2BybKlFmSIrCHQzFz781958+0f+vdv"
191
+ "1boqS6t46GvCJakVB6FyfejDSQ0ATIK9IdgWgtgkvuuwn7F8vfvHEXI4Bm6kgSEm686RLHNriQ3B"
192
+ "tpZhWsQ+yUdHX+NwVSty5bG7bktp2BAzSt4tb58oJAaIARVJkHyjzNPGJC51P7D0CYhIhw92GEOu"
193
+ "w4+BaQz5YVhEjYtJN5OGzGRCZjpBM52QmU5O3/PEpRonswyuPHrnraHBGsOUoTELa2GZAR+LhghD"
194
+ "axx6sAr+iEoNlGrt98Qkn/fN4T04tNcNx88VkH8u3rcjdPkjC4KxYOsP78OF3BIsESNYj7IX/Xpx"
195
+ "5bN3n4xlv1LPRaSl2+g2K8ZLYokhJw3iSIxI43T1kXG2+C0EWRf6lTgchInJPTlSc/wbcBuWicky"
196
+ "iIiJmC2RI3bLnWEha4YrxsPo1LBDMNTZJdmgLt0bgYHnKSxUg8sN4ROlRzzyg7n7+RgiRHjqvq8Y"
197
+ "42hbl6m0rYWfFuLq599216CXZh5i2NJlVUUX3HhLE2/urIwdA89TCPSTeo4CdrvhzPnKOftJREjc"
198
+ "cWPsMPkUy+6WQOyGhoVGYgswWuaQRTzaqdTElcfuui1UG+U4bM03Cv3foXsns3K4H2sgdB8kyWWK"
199
+ "e9WvRYp0OA/P3kcnjhN8nxLrXT3BWQ+ALRGhdX56jdi9eOF2DnOqhPp/he1I3WNEC7D0GyNvEALH"
200
+ "RmiJmj9A1hANwWif1+GYo2Hi0KOVmS0TyLIFyLTENqVydi++6XaYpjn7wDd3651jFzFzWU7bltRZ"
201
+ "WCf0XDIV7IANIHRNiIZolOvCZ7oOUiHRwgBs7FkVOM8AWYAN8x0PXLo25Blf+ezPvjGl4+WIntA7"
202
+ "0Xp/ESxHCErH9qGg8rjVGdjavoiyNlzqZ+2i0yZVJyJj0ohwQ2SMATVNKBlGxvWhvuMjz71U+wyv"
203
+ "XLxwSvRvjrNgxXhzNr+KIzZEwROECq9ZAkoJvaWEBhKpxUABigMJ4kiVWLHIkdoQGQPTEJFp3H5P"
204
+ "cNOYO3738ournNHu5958Jp+mLU3hJuYhlFMf2zgFhZ9fpYjeQAmdY7sJDSCROg6mkYNPZRaRIqE9"
205
+ "wR2RfT2PWOzR/RGcffDZ60c9k93Pvem0S+iV0xpHAnP/umwYhqgHhDrHvh5QMjtsMaEBodL+qUNq"
206
+ "EyyI2yfsh1DunOSyboaZq9pXLl44FX1vTJAUE2fKuQdhLdtgLcQ8hLG7bWodZuE7VecM209oABmp"
207
+ "Y80L8rWYU1GXkqxSud3rYQyk+Ez2XbGTqkjhhTF/Yi5ulvMUynolro9GLP2Qz0VYWI3QUAz/ViU0"
208
+ "sHVRjhI9UQ9m5lAcwMXzYvyWYYhgjSemG5kOQwRmMBl2I8SIYOOAX1cqQZA5D6yI2PFBxE4eOdoM"
209
+ "mSVMmXvBYCVzH7ac0B4x4eLXGMxpwIEgoQWDbBgY4LMxxEwEMuzGR/p6H+zFmYmEUU+x7kg3EW6L"
210
+ "EbcsUtElt4xixMhzDNNBrihybLnlCOgvtxArLkjLIKIgVGzn+0GxsRmOIa9mUNW44Q1DQeyk3N39"
211
+ "6c4RLIxajcMwEkIDh5MaKfohQ34lsd1u0SMvkNt/BVIGxy8KIkbljS1GkSsRoTjP5j4yi69XQucY"
212
+ "EaGBIuHinxIhuw28EA2RjUmkfdmXUX41O4SLigt4z5HHkXNVdu9UMi+K8REaQC+pg4noJW2RpBGv"
213
+ "iabgPKTYWt6ok4rsXy1VGVAyL4aRERqYT2q/oELGY2dAaTf6PnsgpOntiSMn0oa4SN4AVDIfFSMk"
214
+ "NHAwqf0qlaY7cxg9163cJxuFxT7OX+9XcBklyb5DyXwARkpoICPgwcQWb6biE0cxHNmK2OLOSv6p"
215
+ "3t1K5kMwYkID/aSOG8W1Kcnd85a54Hytn8R+ByN/M6BEPjpGTmjgcH4e5JMXuXy9nOS+hXhZibwg"
216
+ "lNAdFF64q9y9G4egT3YP2KVEXhZK6LnoafjVulr9Yq0krgAl9JFRu1aHElihUCgUCoVCoVAoFAqF"
217
+ "QqFQKBQKhUKhUCgUCoVCoVAoFAqFQqFQKBQKhUKhUCgUa8X/A+26fqLnlvojAAAAAElFTkSuQmCC"
218
+
219
+ private let jianPointerImage: NSImage? = {
220
+ guard let data = Data(base64Encoded: jianPointerPngBase64, options: .ignoreUnknownCharacters)
221
+ else {
222
+ fputs("computer-helper: embedded pointer PNG base64 decode failed\n", stderr)
223
+ fflush(stderr)
224
+ return nil
225
+ }
226
+ return NSImage(data: data)
227
+ }()
94
228
 
95
229
  // MARK: - Overlay view
96
230
 
@@ -106,7 +240,7 @@ private final class JianPointerView: NSView {
106
240
  guard let ctx = NSGraphicsContext.current?.cgContext else { return }
107
241
 
108
242
  if rippleVisible {
109
- let maxRadius = pointerSize * 0.9
243
+ let maxRadius = pointerSize * 0.55
110
244
  let radius = 3 + maxRadius * rippleProgress
111
245
  let alpha = max(0, 1 - rippleProgress)
112
246
  ctx.saveGState()
@@ -121,52 +255,25 @@ private final class JianPointerView: NSView {
121
255
  ctx.restoreGState()
122
256
  }
123
257
 
124
- let path = Self.arrowPath(in: bounds)
125
-
126
- // Soft blue glow under the white blade (brand cue without neon spam).
127
- ctx.saveGState()
128
- ctx.setShadow(
129
- offset: CGSize(width: 0, height: 2),
130
- blur: 10,
131
- color: jianGlow.withAlphaComponent(0.55).cgColor
132
- )
133
- jianGlow.withAlphaComponent(0.35).setFill()
134
- path.fill()
135
- ctx.restoreGState()
136
-
137
- ctx.saveGState()
138
- ctx.setShadow(
139
- offset: CGSize(width: 0.5, height: 1.2),
140
- blur: 2.5,
141
- color: NSColor.black.withAlphaComponent(0.22).cgColor
142
- )
143
- let fillAlpha = 1 - fillPulse * 0.28
144
- pointerWhite.withAlphaComponent(fillAlpha).setFill()
145
- path.fill()
146
- ctx.restoreGState()
147
-
148
- jianInk.withAlphaComponent(0.85).setStroke()
149
- path.lineWidth = 1.15
150
- path.lineJoinStyle = .miter
151
- path.lineCapStyle = .butt
152
- path.miterLimit = 8
153
- path.stroke()
154
- }
155
-
156
- /// Sharp geometric pointer (anathema-white style): tip → shaft → barb.
157
- private static func arrowPath(in bounds: NSRect) -> NSBezierPath {
158
- let s = min(bounds.width, bounds.height)
159
- let path = NSBezierPath()
160
- // Coordinates in view space (flipped Y): tip near top-left hotspot.
161
- path.move(to: CGPoint(x: s * 0.06, y: s * 0.06))
162
- path.line(to: CGPoint(x: s * 0.06, y: s * 0.72))
163
- path.line(to: CGPoint(x: s * 0.28, y: s * 0.55))
164
- path.line(to: CGPoint(x: s * 0.42, y: s * 0.88))
165
- path.line(to: CGPoint(x: s * 0.55, y: s * 0.82))
166
- path.line(to: CGPoint(x: s * 0.36, y: s * 0.48))
167
- path.line(to: CGPoint(x: s * 0.62, y: s * 0.48))
168
- path.close()
169
- return path
258
+ // Bitmap blade (white + #2880F8 stroke + baked glow). Pulse alpha on click flash.
259
+ let alpha = 1 - fillPulse * 0.28
260
+ if let image = jianPointerImage {
261
+ image.draw(
262
+ in: bounds,
263
+ from: .zero,
264
+ operation: .sourceOver,
265
+ fraction: alpha,
266
+ respectFlipped: true,
267
+ hints: nil
268
+ )
269
+ } else {
270
+ // Last-resort fallback if decode failed — still a visible tip, not ink/cream geometry.
271
+ jianGlow.withAlphaComponent(0.9 * alpha).setFill()
272
+ let tip = NSBezierPath(
273
+ ovalIn: CGRect(x: hotspot.x - 3, y: hotspot.y - 3, width: 6, height: 6)
274
+ )
275
+ tip.fill()
276
+ }
170
277
  }
171
278
 
172
279
  func flashClick(duration: TimeInterval = 0.16) {
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jian-ai/remote-control",
3
- "version": "0.8.2",
3
+ "version": "0.8.3",
4
4
  "description": "Jian Remote Control worker — connect a computer to a Jian gateway over HTTPS",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  # Jian Remote Control — Windows computer-use helper
2
2
  # JSON lines on stdin → JSON lines on stdout (same cmds as macOS/Linux helpers).
3
- # Overlay: topmost transparent WinForms pointer (geometric white + blue glow).
3
+ # Overlay: topmost transparent WinForms pointer (white + #2880F8 stroke/glow; macOS uses bitmap).
4
4
  # Input: SendInput with save/restore so agent clicks do not strand the user cursor.
5
5
  # English-only errors.
6
6
 
@@ -123,7 +123,8 @@ function Ensure-Overlay {
123
123
  (New-Object System.Drawing.Point(13, 17)),
124
124
  (New-Object System.Drawing.Point(22, 17))
125
125
  )
126
- $glowBrush = New-Object System.Drawing.SolidBrush ([System.Drawing.Color]::FromArgb(140, 58, 122, 214))
126
+ # Parity with macOS ship sprite: white fill + #2880F8 stroke/glow (geometric stand-in).
127
+ $glowBrush = New-Object System.Drawing.SolidBrush ([System.Drawing.Color]::FromArgb(140, 40, 128, 248))
127
128
  $glowPts = @(
128
129
  (New-Object System.Drawing.Point(3, 4)),
129
130
  (New-Object System.Drawing.Point(3, 28)),
@@ -134,9 +135,9 @@ function Ensure-Overlay {
134
135
  (New-Object System.Drawing.Point(23, 19))
135
136
  )
136
137
  $g.FillPolygon($glowBrush, $glowPts)
137
- $fill = New-Object System.Drawing.SolidBrush ([System.Drawing.Color]::FromArgb(255, 250, 250, 248))
138
+ $fill = New-Object System.Drawing.SolidBrush ([System.Drawing.Color]::FromArgb(255, 255, 255, 255))
138
139
  $g.FillPolygon($fill, $pts)
139
- $pen = New-Object System.Drawing.Pen ([System.Drawing.Color]::FromArgb(220, 23, 23, 22), 1.1)
140
+ $pen = New-Object System.Drawing.Pen ([System.Drawing.Color]::FromArgb(255, 40, 128, 248), 2.4)
140
141
  $g.DrawPolygon($pen, $pts)
141
142
  $glowBrush.Dispose(); $fill.Dispose(); $pen.Dispose()
142
143
  })