@bitefoundry/retrieviewer 0.0.3 → 0.0.5

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 (263) hide show
  1. package/ASSET-PROVENANCE.md +11 -0
  2. package/ASSETS-LICENSE.md +2 -1
  3. package/ATTRIBUTION.md +6 -1
  4. package/COMFORT-ART.md +95 -0
  5. package/DESKTOP-PLATFORMS.md +6 -1
  6. package/INSTALLING.md +4 -0
  7. package/PLUGIN.md +13 -10
  8. package/PREREQUISITES.md +95 -0
  9. package/PUPPY-MEMORY.md +102 -1
  10. package/README.ko.md +116 -0
  11. package/README.md +121 -59
  12. package/RELEASING.md +33 -13
  13. package/THIRD-PARTY-NOTICES.md +5 -3
  14. package/braille.mjs +35 -9
  15. package/examples/live.mjs +2 -2
  16. package/integrations/linux-overlay/overlay.py +8 -2
  17. package/integrations/macos-overlay/overlay.jxa +9 -7
  18. package/integrations/opencode/runtime.mjs +4 -4
  19. package/integrations/opencode/tui.tsx +1 -1
  20. package/integrations/windows-overlay/Overlay.cs +7 -6
  21. package/integrations/windows-overlay/desktop.mjs +2 -1
  22. package/integrations/windows-overlay/protocol.mjs +13 -7
  23. package/integrations/windows-overlay/worker.mjs +1 -1
  24. package/package.json +34 -13
  25. package/puppy-comfort-sprites.mjs +33 -0
  26. package/puppy-companion-comfort-q5.bin.gz +0 -0
  27. package/puppy-companion-comfort.json +140 -0
  28. package/puppy-companion-life.mjs +58 -18
  29. package/puppy-companion-objects.mjs +12 -0
  30. package/puppy-companion-sprites.mjs +4 -2
  31. package/puppy-frame-predictive.mjs +15 -9
  32. package/puppy-frame-store.mjs +2 -2
  33. package/puppy-paper-actions.mjs +4 -2
  34. package/puppy-paper-behaviors.mjs +1 -1
  35. package/puppy-paper-contact.mjs +4 -2
  36. package/puppy-paper-seat.mjs +4 -2
  37. package/puppy-paper-world.mjs +14 -0
  38. package/puppy-paper.mjs +79 -21
  39. package/retrieviewer-cli.mjs +8 -6
  40. package/retrieviewer-plugin.d.mts +1 -1
  41. package/retrieviewer-plugin.mjs +1 -1
  42. package/retrieviewer.d.mts +12 -4
  43. package/retrieviewer.mjs +20 -14
  44. package/locomotion-core.mjs +0 -21
  45. package/locomotion-puppy.animal +0 -0
  46. package/vendor/procedural-animals/package.json +0 -40
  47. package/vendor/procedural-animals/src/animal.js +0 -146
  48. package/vendor/procedural-animals/src/bake.js +0 -51
  49. package/vendor/procedural-animals/src/build.js +0 -81
  50. package/vendor/procedural-animals/src/core/build/coatKit.js +0 -121
  51. package/vendor/procedural-animals/src/core/build/featherCards.js +0 -189
  52. package/vendor/procedural-animals/src/core/build/finEdges.js +0 -85
  53. package/vendor/procedural-animals/src/core/build/furSlope.js +0 -115
  54. package/vendor/procedural-animals/src/core/build/mesh.js +0 -67
  55. package/vendor/procedural-animals/src/core/build/pipeline.js +0 -272
  56. package/vendor/procedural-animals/src/core/build/seams.js +0 -183
  57. package/vendor/procedural-animals/src/core/build/warp.js +0 -112
  58. package/vendor/procedural-animals/src/core/build/weights.js +0 -393
  59. package/vendor/procedural-animals/src/core/math/vec.js +0 -81
  60. package/vendor/procedural-animals/src/core/motion/actions.js +0 -800
  61. package/vendor/procedural-animals/src/core/motion/bird.js +0 -3063
  62. package/vendor/procedural-animals/src/core/motion/birdActions.js +0 -1059
  63. package/vendor/procedural-animals/src/core/motion/common.js +0 -158
  64. package/vendor/procedural-animals/src/core/motion/index.js +0 -22
  65. package/vendor/procedural-animals/src/core/motion/quadruped.js +0 -2875
  66. package/vendor/procedural-animals/src/core/motion/snake.js +0 -1594
  67. package/vendor/procedural-animals/src/core/motion/spider.js +0 -1810
  68. package/vendor/procedural-animals/src/core/motion/swimmer.js +0 -1047
  69. package/vendor/procedural-animals/src/core/motion/swimmerActions.js +0 -613
  70. package/vendor/procedural-animals/src/core/render/animalObject.js +0 -262
  71. package/vendor/procedural-animals/src/core/render/coatMaterial.js +0 -1301
  72. package/vendor/procedural-animals/src/core/render/dqs.js +0 -159
  73. package/vendor/procedural-animals/src/core/render/eyes.js +0 -676
  74. package/vendor/procedural-animals/src/core/render/membranes.js +0 -77
  75. package/vendor/procedural-animals/src/core/render/skeleton.js +0 -41
  76. package/vendor/procedural-animals/src/core/rig/bird.js +0 -327
  77. package/vendor/procedural-animals/src/core/rig/quadruped.js +0 -76
  78. package/vendor/procedural-animals/src/core/rig/rig.js +0 -79
  79. package/vendor/procedural-animals/src/core/rig/snake.js +0 -76
  80. package/vendor/procedural-animals/src/core/rig/spider.js +0 -110
  81. package/vendor/procedural-animals/src/core/rig/swimmer.js +0 -181
  82. package/vendor/procedural-animals/src/core/sdf/eyeSocket.js +0 -53
  83. package/vendor/procedural-animals/src/core/sdf/mesher.js +0 -238
  84. package/vendor/procedural-animals/src/core/sdf/sdf.js +0 -263
  85. package/vendor/procedural-animals/src/index.d.ts +0 -98
  86. package/vendor/procedural-animals/src/index.js +0 -29
  87. package/vendor/procedural-animals/src/registry.js +0 -48
  88. package/vendor/procedural-animals/src/species/bear/behaviour.js +0 -463
  89. package/vendor/procedural-animals/src/species/bear/claws.js +0 -147
  90. package/vendor/procedural-animals/src/species/bear/coat.js +0 -726
  91. package/vendor/procedural-animals/src/species/bear/index.js +0 -139
  92. package/vendor/procedural-animals/src/species/bear/motion.js +0 -112
  93. package/vendor/procedural-animals/src/species/bear/regions.js +0 -78
  94. package/vendor/procedural-animals/src/species/bear/rig.js +0 -112
  95. package/vendor/procedural-animals/src/species/bear/sculpt.js +0 -272
  96. package/vendor/procedural-animals/src/species/boar/behaviour.js +0 -335
  97. package/vendor/procedural-animals/src/species/boar/coat.js +0 -372
  98. package/vendor/procedural-animals/src/species/boar/index.js +0 -110
  99. package/vendor/procedural-animals/src/species/boar/motion.js +0 -85
  100. package/vendor/procedural-animals/src/species/boar/regions.js +0 -88
  101. package/vendor/procedural-animals/src/species/boar/rig.js +0 -111
  102. package/vendor/procedural-animals/src/species/boar/sculpt.js +0 -285
  103. package/vendor/procedural-animals/src/species/cat/behaviour.js +0 -559
  104. package/vendor/procedural-animals/src/species/cat/coat.js +0 -841
  105. package/vendor/procedural-animals/src/species/cat/index.js +0 -211
  106. package/vendor/procedural-animals/src/species/cat/motion.js +0 -101
  107. package/vendor/procedural-animals/src/species/cat/regions.js +0 -82
  108. package/vendor/procedural-animals/src/species/cat/rig.js +0 -100
  109. package/vendor/procedural-animals/src/species/cat/sculpt.js +0 -266
  110. package/vendor/procedural-animals/src/species/cat/whiskers.js +0 -110
  111. package/vendor/procedural-animals/src/species/cheetah/coat.js +0 -427
  112. package/vendor/procedural-animals/src/species/cheetah/index.js +0 -51
  113. package/vendor/procedural-animals/src/species/cheetah/joints.js +0 -3
  114. package/vendor/procedural-animals/src/species/cheetah/motion.js +0 -77
  115. package/vendor/procedural-animals/src/species/cheetah/regions.js +0 -95
  116. package/vendor/procedural-animals/src/species/cheetah/rig.js +0 -66
  117. package/vendor/procedural-animals/src/species/cheetah/sculpt.js +0 -158
  118. package/vendor/procedural-animals/src/species/chicken/coat.js +0 -313
  119. package/vendor/procedural-animals/src/species/chicken/coverts.js +0 -320
  120. package/vendor/procedural-animals/src/species/chicken/index.js +0 -110
  121. package/vendor/procedural-animals/src/species/chicken/motion.js +0 -177
  122. package/vendor/procedural-animals/src/species/chicken/regions.js +0 -66
  123. package/vendor/procedural-animals/src/species/chicken/rig.js +0 -144
  124. package/vendor/procedural-animals/src/species/chicken/sculpt.js +0 -389
  125. package/vendor/procedural-animals/src/species/chicken/wingLook.js +0 -66
  126. package/vendor/procedural-animals/src/species/chicken/wingStack.js +0 -124
  127. package/vendor/procedural-animals/src/species/cow/behaviour.js +0 -521
  128. package/vendor/procedural-animals/src/species/cow/coat.js +0 -465
  129. package/vendor/procedural-animals/src/species/cow/index.js +0 -118
  130. package/vendor/procedural-animals/src/species/cow/motion.js +0 -81
  131. package/vendor/procedural-animals/src/species/cow/regions.js +0 -98
  132. package/vendor/procedural-animals/src/species/cow/rig.js +0 -116
  133. package/vendor/procedural-animals/src/species/cow/sculpt.js +0 -339
  134. package/vendor/procedural-animals/src/species/crow/coat.js +0 -145
  135. package/vendor/procedural-animals/src/species/crow/index.js +0 -70
  136. package/vendor/procedural-animals/src/species/crow/joints.js +0 -3
  137. package/vendor/procedural-animals/src/species/crow/motion.js +0 -101
  138. package/vendor/procedural-animals/src/species/crow/regions.js +0 -50
  139. package/vendor/procedural-animals/src/species/crow/rig.js +0 -83
  140. package/vendor/procedural-animals/src/species/crow/sculpt.js +0 -118
  141. package/vendor/procedural-animals/src/species/deer/behaviour.js +0 -513
  142. package/vendor/procedural-animals/src/species/deer/coat.js +0 -391
  143. package/vendor/procedural-animals/src/species/deer/index.js +0 -106
  144. package/vendor/procedural-animals/src/species/deer/motion.js +0 -97
  145. package/vendor/procedural-animals/src/species/deer/regions.js +0 -60
  146. package/vendor/procedural-animals/src/species/deer/rig.js +0 -124
  147. package/vendor/procedural-animals/src/species/deer/sculpt.js +0 -297
  148. package/vendor/procedural-animals/src/species/dog/behaviour.js +0 -521
  149. package/vendor/procedural-animals/src/species/dog/coat.js +0 -495
  150. package/vendor/procedural-animals/src/species/dog/index.js +0 -144
  151. package/vendor/procedural-animals/src/species/dog/motion.js +0 -118
  152. package/vendor/procedural-animals/src/species/dog/regions.js +0 -61
  153. package/vendor/procedural-animals/src/species/dog/rig.js +0 -129
  154. package/vendor/procedural-animals/src/species/dog/sculpt.js +0 -246
  155. package/vendor/procedural-animals/src/species/eagle/coat.js +0 -250
  156. package/vendor/procedural-animals/src/species/eagle/index.js +0 -76
  157. package/vendor/procedural-animals/src/species/eagle/joints.js +0 -3
  158. package/vendor/procedural-animals/src/species/eagle/motion.js +0 -137
  159. package/vendor/procedural-animals/src/species/eagle/regions.js +0 -57
  160. package/vendor/procedural-animals/src/species/eagle/rig.js +0 -107
  161. package/vendor/procedural-animals/src/species/eagle/sculpt.js +0 -137
  162. package/vendor/procedural-animals/src/species/fish/coat.js +0 -271
  163. package/vendor/procedural-animals/src/species/fish/index.js +0 -88
  164. package/vendor/procedural-animals/src/species/fish/motion.js +0 -92
  165. package/vendor/procedural-animals/src/species/fish/regions.js +0 -27
  166. package/vendor/procedural-animals/src/species/fish/rig.js +0 -143
  167. package/vendor/procedural-animals/src/species/fish/sculpt.js +0 -214
  168. package/vendor/procedural-animals/src/species/fish/variants.js +0 -126
  169. package/vendor/procedural-animals/src/species/fox/behaviour.js +0 -314
  170. package/vendor/procedural-animals/src/species/fox/coat.js +0 -444
  171. package/vendor/procedural-animals/src/species/fox/index.js +0 -120
  172. package/vendor/procedural-animals/src/species/fox/motion.js +0 -81
  173. package/vendor/procedural-animals/src/species/fox/regions.js +0 -59
  174. package/vendor/procedural-animals/src/species/fox/rig.js +0 -91
  175. package/vendor/procedural-animals/src/species/fox/sculpt.js +0 -202
  176. package/vendor/procedural-animals/src/species/frog/behaviour.js +0 -590
  177. package/vendor/procedural-animals/src/species/frog/coat.js +0 -305
  178. package/vendor/procedural-animals/src/species/frog/index.js +0 -97
  179. package/vendor/procedural-animals/src/species/frog/motion.js +0 -69
  180. package/vendor/procedural-animals/src/species/frog/regions.js +0 -46
  181. package/vendor/procedural-animals/src/species/frog/rig.js +0 -156
  182. package/vendor/procedural-animals/src/species/frog/sculpt.js +0 -240
  183. package/vendor/procedural-animals/src/species/goat/behaviour.js +0 -509
  184. package/vendor/procedural-animals/src/species/goat/coat.js +0 -590
  185. package/vendor/procedural-animals/src/species/goat/index.js +0 -149
  186. package/vendor/procedural-animals/src/species/goat/motion.js +0 -95
  187. package/vendor/procedural-animals/src/species/goat/regions.js +0 -98
  188. package/vendor/procedural-animals/src/species/goat/rig.js +0 -181
  189. package/vendor/procedural-animals/src/species/goat/sculpt.js +0 -547
  190. package/vendor/procedural-animals/src/species/horse/coat.js +0 -361
  191. package/vendor/procedural-animals/src/species/horse/coatSeam.js +0 -122
  192. package/vendor/procedural-animals/src/species/horse/index.js +0 -80
  193. package/vendor/procedural-animals/src/species/horse/motion.js +0 -245
  194. package/vendor/procedural-animals/src/species/horse/regions.js +0 -60
  195. package/vendor/procedural-animals/src/species/horse/rig.js +0 -117
  196. package/vendor/procedural-animals/src/species/horse/sculpt.js +0 -316
  197. package/vendor/procedural-animals/src/species/horse/seamWeights.js +0 -272
  198. package/vendor/procedural-animals/src/species/lion/behaviour.js +0 -509
  199. package/vendor/procedural-animals/src/species/lion/coat.js +0 -1016
  200. package/vendor/procedural-animals/src/species/lion/index.js +0 -111
  201. package/vendor/procedural-animals/src/species/lion/motion.js +0 -87
  202. package/vendor/procedural-animals/src/species/lion/regions.js +0 -106
  203. package/vendor/procedural-animals/src/species/lion/rig.js +0 -110
  204. package/vendor/procedural-animals/src/species/lion/sculpt.js +0 -384
  205. package/vendor/procedural-animals/src/species/lion/whiskers.js +0 -132
  206. package/vendor/procedural-animals/src/species/pig/behaviour.js +0 -575
  207. package/vendor/procedural-animals/src/species/pig/coat.js +0 -730
  208. package/vendor/procedural-animals/src/species/pig/ears.js +0 -242
  209. package/vendor/procedural-animals/src/species/pig/index.js +0 -161
  210. package/vendor/procedural-animals/src/species/pig/motion.js +0 -105
  211. package/vendor/procedural-animals/src/species/pig/regions.js +0 -100
  212. package/vendor/procedural-animals/src/species/pig/rig.js +0 -152
  213. package/vendor/procedural-animals/src/species/pig/sculpt.js +0 -382
  214. package/vendor/procedural-animals/src/species/rabbit/behaviour.js +0 -605
  215. package/vendor/procedural-animals/src/species/rabbit/coat.js +0 -285
  216. package/vendor/procedural-animals/src/species/rabbit/index.js +0 -85
  217. package/vendor/procedural-animals/src/species/rabbit/motion.js +0 -85
  218. package/vendor/procedural-animals/src/species/rabbit/regions.js +0 -48
  219. package/vendor/procedural-animals/src/species/rabbit/rig.js +0 -99
  220. package/vendor/procedural-animals/src/species/rabbit/sculpt.js +0 -148
  221. package/vendor/procedural-animals/src/species/rat/behaviour.js +0 -539
  222. package/vendor/procedural-animals/src/species/rat/coat.js +0 -341
  223. package/vendor/procedural-animals/src/species/rat/index.js +0 -111
  224. package/vendor/procedural-animals/src/species/rat/motion.js +0 -90
  225. package/vendor/procedural-animals/src/species/rat/regions.js +0 -92
  226. package/vendor/procedural-animals/src/species/rat/rig.js +0 -112
  227. package/vendor/procedural-animals/src/species/rat/sculpt.js +0 -163
  228. package/vendor/procedural-animals/src/species/rat/whiskers.js +0 -119
  229. package/vendor/procedural-animals/src/species/shark/coat.js +0 -383
  230. package/vendor/procedural-animals/src/species/shark/index.js +0 -104
  231. package/vendor/procedural-animals/src/species/shark/motion.js +0 -69
  232. package/vendor/procedural-animals/src/species/shark/regions.js +0 -34
  233. package/vendor/procedural-animals/src/species/shark/rig.js +0 -194
  234. package/vendor/procedural-animals/src/species/shark/sculpt.js +0 -343
  235. package/vendor/procedural-animals/src/species/shark/variants.js +0 -105
  236. package/vendor/procedural-animals/src/species/sheep/behaviour.js +0 -522
  237. package/vendor/procedural-animals/src/species/sheep/coat.js +0 -529
  238. package/vendor/procedural-animals/src/species/sheep/index.js +0 -152
  239. package/vendor/procedural-animals/src/species/sheep/motion.js +0 -86
  240. package/vendor/procedural-animals/src/species/sheep/neck.js +0 -41
  241. package/vendor/procedural-animals/src/species/sheep/regions.js +0 -62
  242. package/vendor/procedural-animals/src/species/sheep/rig.js +0 -163
  243. package/vendor/procedural-animals/src/species/sheep/sculpt.js +0 -542
  244. package/vendor/procedural-animals/src/species/snake/coat.js +0 -315
  245. package/vendor/procedural-animals/src/species/snake/index.js +0 -73
  246. package/vendor/procedural-animals/src/species/snake/motion.js +0 -51
  247. package/vendor/procedural-animals/src/species/snake/regions.js +0 -51
  248. package/vendor/procedural-animals/src/species/snake/rig.js +0 -108
  249. package/vendor/procedural-animals/src/species/snake/sculpt.js +0 -122
  250. package/vendor/procedural-animals/src/species/spider/coat.js +0 -279
  251. package/vendor/procedural-animals/src/species/spider/index.js +0 -78
  252. package/vendor/procedural-animals/src/species/spider/motion.js +0 -72
  253. package/vendor/procedural-animals/src/species/spider/regions.js +0 -87
  254. package/vendor/procedural-animals/src/species/spider/rig.js +0 -118
  255. package/vendor/procedural-animals/src/species/spider/sculpt.js +0 -182
  256. package/vendor/procedural-animals/src/species/wolf/behaviour.js +0 -160
  257. package/vendor/procedural-animals/src/species/wolf/coat.js +0 -749
  258. package/vendor/procedural-animals/src/species/wolf/index.js +0 -107
  259. package/vendor/procedural-animals/src/species/wolf/motion.js +0 -80
  260. package/vendor/procedural-animals/src/species/wolf/regions.js +0 -57
  261. package/vendor/procedural-animals/src/species/wolf/rig.js +0 -87
  262. package/vendor/procedural-animals/src/species/wolf/sculpt.js +0 -339
  263. package/vendor/procedural-animals/src/worker.js +0 -29
package/README.md CHANGED
@@ -1,90 +1,152 @@
1
- # Retrieviewer
1
+ <p align="center">
2
+ <a href="https://bitefoundry.github.io/retrieviewer/"><img src="https://raw.githubusercontent.com/BiteFoundry/retrieviewer/main/docs/assets/preview.png" alt="Retrieviewer — a fluffy retriever drawn with tiny monochrome Braille dots" width="960"></a>
3
+ </p>
2
4
 
3
- An autonomous, fluffy Braille retriever that lives above your app window or in a TUI pane.
4
- Give it a ball or a treat; otherwise it wanders and plays on its own. By BiteFoundry.
5
+ <h1 align="center">Retrieviewer</h1>
6
+ <p align="center">A little company for your screen.</p>
7
+ <p align="center">
8
+ <a href="https://bitefoundry.github.io/retrieviewer/">Meet your retriever</a> ·
9
+ <a href="./README.ko.md">한국어</a> ·
10
+ <a href="https://www.npmjs.com/package/@bitefoundry/retrieviewer">npm</a> ·
11
+ <a href="https://github.com/BiteFoundry/retrieviewer/issues">Feedback</a>
12
+ </p>
5
13
 
6
- **Version 0.0.3.** Source code is MIT; puppy artwork is CC BY-SA 4.0, including
7
- commercial use and adaptations under its attribution/ShareAlike conditions.
8
- See [licensing](./LICENSE), [artwork terms](./ASSETS-LICENSE.md),
9
- [credits](./ATTRIBUTION.md) and [brand notice](./TRADEMARKS.md).
10
- The complete package is not MIT-only.
14
+ A fluffy, autonomous Braille-art retriever that hangs out above an app window.
15
+ It wanders, runs, flops belly-up and naps. Toss a ball, offer a treat or play tug.
16
+ Get back to your thing.
11
17
 
12
- ## Install
18
+ Made by [BiteFoundry](https://github.com/BiteFoundry). No model API, account or
19
+ telemetry is needed by the runtime.
13
20
 
14
- Node.js >=20 is required. Windows10/11 x64 uses the Windows .NET Framework4
15
- compiler (automatic helper build on first launch). Linux needs X11, a compositor,
16
- system Python3.6+ and X11/Xrender/Xext; AlmaLinux8/GLIBC2.28 has been tested.
17
- macOS uses built-in osascript/AppKit and is **experimental, not runtime verified**.
18
- Native Wayland is not supported. See [platform requirements](./DESKTOP-PLATFORMS.md).
21
+ ## Bring one home
19
22
 
20
- 1. Install:
23
+ Install **on the computer displaying the app**, in a terminal. Node.js is required;
24
+ [install Node/npm first](./PREREQUISITES.md) if you do not have them.
25
+
26
+ 1. Install (or update):
21
27
 
22
28
  ```sh
23
- npm install -g @bitefoundry/retrieviewer
29
+ npm install -g @bitefoundry/retrieviewer@latest
24
30
  ```
25
31
 
26
- 2. Run and choose the app window you want your puppy to live on:
32
+ 2. Run, then choose an app in the window picker:
27
33
 
28
34
  ```sh
29
35
  retrieviewer desktop
30
36
  ```
31
37
 
32
- That's it. Use your app normally. Ball/treat controls sit above the selected
33
- window; the pet lets ordinary clicks through. No Codex patch, wrapper, MCP setup,
34
- administrator access, or automatic startup registration. It follows a whole window,
35
- not individual tabs, and hides when you switch away. It is a separate transparent
36
- native window, **not a widget injected into another app**. macOS is experimental;
37
- exclusive fullscreen apps and privileged/security surfaces are not certified.
38
+ Next time, just run `retrieviewer desktop`. This is a separate transparent,
39
+ click-through native overlay—not a patched app, shell wrapper or injected MCP
40
+ widget. It follows a whole app window, not a tab, and hides when that window is
41
+ inactive or minimized. No automatic startup entry is installed.
42
+
43
+ Choose the dot color at launch: `retrieviewer desktop --ink gold`, or
44
+ `retrieviewer desktop --ink dark` for light backgrounds. Default: `cream`.
45
+ Quit a running companion first. Colors reuse the same artwork; no screen capture.
46
+
47
+ > **Cursor / VS Code Remote SSH:** run those commands in your **local** computer's
48
+ > terminal, not the remote server's terminal. The puppy belongs where the window
49
+ > is displayed. A server's `DISPLAY=:1` refers to that server's X11 display, which
50
+ > you may be viewing through VNC; it is not automatically your local Cursor window.
38
51
 
39
- Exit from the tray icon or `retrieviewer desktop-stop`. To remove the companion's
40
- own cache, run `retrieviewer desktop-remove --yes`; then
41
- `npm uninstall -g @bitefoundry/retrieviewer` removes the package. See
42
- [platform support](./DESKTOP-PLATFORMS.md) and [Windows controls and removal](./WINDOWS-COMPANION.md).
52
+ <details>
53
+ <summary>Try once with npx / command not found / Node is missing</summary>
43
54
 
44
- ## Optional: native OpenCode pane
55
+ With Node/npm installed, try without a global installation:
45
56
 
46
57
  ```sh
47
- retrieviewer enable --agent opencode --yes
48
- opencode
58
+ npx --yes --package @bitefoundry/retrieviewer@latest retrieviewer desktop
49
59
  ```
50
60
 
51
- Without `--agent` / `--yes`, interactive `retrieviewer enable` offers agent
52
- selection and a preview before changing configuration. The native adapter is
53
- currently verified only with OpenCode 2.1.2 on Windows. Codex, Claude Code, Cursor
54
- Agent, Grok and Antigravity native panes are not implemented. MCP connectivity
55
- alone does not create a pet inside their screens.
61
+ `npx` does **not** permanently add `retrieviewer` to PATH. Use the global install
62
+ above for future short commands. If that still says command not found, see
63
+ [PATH fixes for bash, csh/tcsh and Windows](./PREREQUISITES.md#command-not-found).
64
+ Do not use `sudo npm install -g` as a blanket workaround.
56
65
 
57
- Inside OpenCode use `/retrieviewer ball`, `/retrieviewer treat`, or
58
- `/retrieviewer disable`. See [installation and removal](./INSTALLING.md).
66
+ </details>
59
67
 
60
- ## Embed in your own TUI
68
+ ## Where it can live
61
69
 
62
- Install `@bitefoundry/retrieviewer` in your app, without `-g`, then use the host callbacks:
70
+ | Desktop | Status / requirements |
71
+ | --- | --- |
72
+ | Windows 10/11 x64 | Live tested. Node + Windows .NET Framework 4 compiler; helper builds on first launch. |
73
+ | Linux X11 | AlmaLinux 8 / GLIBC 2.28 / Python 3.6.8 / Node 22.23.4 tested in an isolated composited X11 desktop. Requires system Python 3.6+, X11/Xrender/Xext and a compositor. |
74
+ | macOS | **Experimental; no Mac runtime verification yet.** Uses built-in osascript/AppKit. |
75
+ | Native Wayland / headless SSH | Not supported by the desktop overlay. No session/privacy workaround is installed. |
63
76
 
64
- ```js
65
- import {createPetPlugin} from '@bitefoundry/retrieviewer/plugin';
77
+ Linux currently favors lower load: 16px cells / 15fps versus the Windows default
78
+ 8px cells / 30fps, so its dots look coarser. The Q5 artwork is identical.
79
+ This is not a zero-resource widget: the npm tarball is about 19 MB. An earlier
80
+ 0.0.3 AlmaLinux test used roughly 180 MiB of Node RSS, plus the Python helper;
81
+ that is not a memory budget or a new Linux GUI check for 0.0.4.
82
+ See [platform details and verification boundaries](./DESKTOP-PLATFORMS.md).
83
+
84
+ ## Easy to leave, too
85
+
86
+ Quit through the controls, or run `retrieviewer desktop-stop`.
87
+ To remove the companion's own cache and the package:
88
+
89
+ ```sh
90
+ retrieviewer desktop-remove --yes
91
+ npm uninstall -g @bitefoundry/retrieviewer
92
+ ```
93
+
94
+ The desktop companion does not edit agent credentials, app binaries or shell
95
+ profiles. [Optional OpenCode registration](./INSTALLING.md) is separate; disconnect
96
+ that registration before uninstalling if you explicitly enabled it.
66
97
 
67
- const puppy = await createPetPlugin({host: {
68
- getSize: () => ({columns: pane.width, rows: pane.height}),
69
- render: ({text}) => pane.setContent(text),
70
- onError: error => logToYourApp(error),
71
- }});
98
+ ## Put it inside your own TUI
99
+
100
+ The core is host-driven. Your app owns the pane, input and clock; the puppy does
101
+ not take over stdin/stdout.
102
+
103
+ ```js
104
+ import { createPetPlugin } from '@bitefoundry/retrieviewer/plugin';
105
+
106
+ const puppy = await createPetPlugin({
107
+ host: {
108
+ getSize: () => ({ columns: pane.width, rows: pane.height }),
109
+ render: ({ text }) => pane.setContent(text),
110
+ onError: error => logToYourApp(error),
111
+ },
112
+ });
72
113
  puppy.start();
73
- // Optional interaction: puppy.interact('ball') / puppy.interact('treat')
114
+ // puppy.interact('ball') / puppy.interact('treat') / puppy.interact('tug')
74
115
  // On unmount: puppy.dispose()
75
116
  ```
76
117
 
77
- `pane` and `logToYourApp` are supplied by your app. The pet does not take over
78
- stdin, stdout or the screen. Q5 (32-level) artwork is bundled; no model API,
79
- account or telemetry is needed by the runtime. npm dependencies require an
80
- initial download or a populated cache. This is not a tiny icon: the complete
81
- tarball is about 21 MB and a previous Windows sample used about 172 MB RSS.
118
+ `pane` and `logToYourApp` come from your app.
119
+ [Plugin lifecycle](./PLUGIN.md) · [Core / JSONL bridge](./EMBEDDING.md) ·
120
+ [Optional OpenCode adapter](./INSTALLING.md).
121
+ The native OpenCode pane has Windows/OpenCode 2.1.2 host evidence; Codex, Claude,
122
+ Cursor, Grok and Antigravity **in-terminal** renderers are not implemented.
123
+ An MCP connection alone cannot insert a persistent picture into those clients.
124
+
125
+ ## Build, contribute, be kind
126
+
127
+ ```sh
128
+ npm ci
129
+ npm test
130
+ npm run build:site
131
+ npm start
132
+ ```
133
+
134
+ The public tree contains the shipped runtime, Q5 art, focused runtime/adapter
135
+ tests and reproducible site build. Historical art experiments, original reference
136
+ photos and private worker/QA logs are intentionally absent.
137
+ See [contributing](./CONTRIBUTING.md) and [source scope](./SOURCE-SCOPE.md).
138
+ Compatibility reports are welcome—especially real Mac testing and X11 desktops.
139
+
140
+ ## Code and coat have different licenses
141
+
142
+ **Code: MIT. Artwork: CC BY-SA 4.0.** Commercial reuse is permitted under the
143
+ respective terms. Keep artwork credits and ShareAlike conditions when applicable;
144
+ the complete package is **not MIT-only**.
82
145
 
83
- - [Plugin lifecycle](./PLUGIN.md) — host callbacks, clocks and embedding.
84
- - [Core and JSONL bridge](./EMBEDDING.md) — JS and non-JS TUI integration.
85
- - [Release process](./RELEASING.md) — checks and support boundaries.
86
- - [Third-party notices](./THIRD-PARTY-NOTICES.md).
146
+ [Code license](./LICENSE-CODE) · [Artwork terms](./ASSETS-LICENSE.md) ·
147
+ [Required credits](./ATTRIBUTION.md) · [Third-party notices](./THIRD-PARTY-NOTICES.md) ·
148
+ [Brand notice](./TRADEMARKS.md).
87
149
 
88
- The runtime has no install scripts and never edits agent settings automatically.
89
- Only an explicit `retrieviewer enable` changes settings, after a preview/approval
90
- or an explicit `--yes`. Unsupported hosts are refused.
150
+ The puppy artwork was developed with AI-generated imagery and programmatic pose,
151
+ grounding and frame processing. It is authored animation, not motion capture or
152
+ a claim of sentience.
package/RELEASING.md CHANGED
@@ -1,6 +1,15 @@
1
- # Retrieviewer 0.0.3 release process
2
-
3
- Target: `@bitefoundry/retrieviewer@0.0.3`, public npm registry, `latest` tag.
1
+ # Retrieviewer 0.0.5 release process
2
+
3
+ Current target: `@bitefoundry/retrieviewer@0.0.5`, public `latest`. The owner
4
+ explicitly requested npm publication and homepage update of the approved sleep
5
+ studies and mirrored side on2026-10-23. This also includes manual ink presets,
6
+ sleep pressure independent of energy, shared gesture scratch and5fps sleep paint.
7
+ Preserve0.0.4 registry bytes; never republish them. Run all checks below and publish
8
+ only the exact tested0.0.5 tarball. Four new approved keys stay under the existing
9
+ CC BY-SA4.0 artwork terms; see COMFORT-ART.md. The historical scope below describes
10
+ 0.0.4, not current verification results.
11
+
12
+ Target: `@bitefoundry/retrieviewer@0.0.4`, public npm registry, `latest` tag.
4
13
  The npm organization `bitefoundry` uses the free public-packages plan; account
5
14
  `shinick` owns the organization.
6
15
 
@@ -12,11 +21,15 @@ artwork draft is superseded. ATTRIBUTION.md retains the photographic references
12
21
  and original license links. ASSET-PROVENANCE.md records the bounded review and
13
22
  remaining limits; this is not comprehensive legal clearance.
14
23
 
15
- The owner explicitly authorized publishing0.0.3 with the cross-platform companion
16
- and the two-step npm install/desktop launch guide. Windows and the AlmaLinux8
17
- GLIBC2.28 X11 baseline have live test evidence; macOS ships as experimental and
18
- not runtime verified. Native Wayland is unsupported. No new art license or Codex
19
- modification is part of this release. Preserve all earlier registry artifacts.
24
+ The owner explicitly authorized GitHub push and npm publication of the reviewed
25
+ comfort extension and runtime optimizations on2026-10-23. Version0.0.4 removes
26
+ unused Three.js runtime work, retains only reachable interaction samples, and
27
+ adds belly-up/sleep behavior plus a tiny tug sprite family. Code/art licenses stay
28
+ MIT/CC BY-SA4.0, including the new covered archive. No Codex modification is made.
29
+ Windows live, installed-consumer and browser checks cover this release. Linux
30
+ retains the earlier AlmaLinux8/GLIBC2.28 X11 adapter baseline; new controls have
31
+ grammar/protocol checks, not a new Linux GUI test. macOS remains experimental.
32
+ Native Wayland is unsupported. Preserve all earlier registry artifacts.
20
33
 
21
34
  `release-policy.json` has four independent approval gates and the CC artwork
22
35
  license identifier. The guard still refuses false/missing approvals, private
@@ -38,10 +51,16 @@ interactions, lifecycle, examples, JSONL and isolated installer settings.
38
51
  Its report is in a fresh `dist/package-check-*/package-check.json`; it does not publish or edit live
39
52
  agent configuration. Keep the final tarball hash and file inventory.
40
53
 
41
- The product workspace is not a Git repository. No public source repository,
42
- Actions pipeline or npm trusted publisher is implied by this npm release.
43
- Do not upload this experimental workspace wholesale: original photos, source
44
- image sheets, workers, QA receipts, credentials and local settings stay local.
54
+ The original art playground was not a Git repository. The owner separately
55
+ authorized a curated public source tree at BiteFoundry/retrieviewer and its
56
+ GitHub Pages workflow. This is not an upload of the experimental workspace:
57
+ original photos, source image sheets, workers, QA receipts, credentials and local
58
+ settings remain local. See SOURCE-SCOPE.md. There is no npm trusted publisher or
59
+ automatic npm release workflow; the site workflow deploys the site only.
60
+
61
+ Immutable npm0.0.3 was already published and verified. Do not republish different
62
+ bytes as0.0.3. This release uses0.0.4 with fresh authorization, version/lock/test/
63
+ policy updates, exact-tarball checks and registry-install verification.
45
64
 
46
65
  ## Publish the tested tarball
47
66
 
@@ -58,7 +77,8 @@ https://docs.npmjs.com/trusted-publishers/
58
77
 
59
78
  ## Support boundaries
60
79
 
61
- The default Q5 art and runtime are unchanged by this release. The OpenCode
80
+ The approved existing Q5 frames are unchanged; the new tiny tug family uses the
81
+ same32-level pipeline. Belly/sleep reuse existing source frames. The OpenCode
62
82
  2.1.2 Windows directory-based adapter has local host proof; npm `./tui`
63
83
  discovery alone is not live-host certification. Other agent-native renderers
64
84
  remain unimplemented. MCP alone cannot inject a pet into arbitrary agent UIs.
@@ -1,11 +1,13 @@
1
1
  # Third-party notices
2
2
 
3
3
  - **procedural-animals 0.1.0** — MIT, Copyright (c) 2026 Majid Manzarpour and
4
- procedural-animals contributors. Source is vendored under
5
- `vendor/procedural-animals/src`; the full notice is retained at
4
+ procedural-animals contributors. The original development source is retained
5
+ under `vendor/procedural-animals/src` for motion parity tests; it is no longer
6
+ executed or shipped in the dot-art runtime. The full notice is retained at
6
7
  `vendor/procedural-animals/LICENSE`.
7
8
  Upstream: https://github.com/majidmanzarpour/threejs-procedural-animals
8
- - **three 0.186.1** — MIT; installed as a pinned npm dependency. Its distribution
9
+ - **three 0.186.1** — MIT; development-only dependency for original 3D parity
10
+ tests. Not installed by the production package. Its development distribution
9
11
  includes its own `LICENSE`. https://github.com/mrdoob/three.js
10
12
  - **jsonc-parser 3.3.1** — MIT; installed as a pinned npm dependency. Its
11
13
  distribution includes `LICENSE.md`. https://github.com/microsoft/node-jsonc-parser
package/braille.mjs CHANGED
@@ -2,23 +2,49 @@
2
2
  export const BRAILLE_BITS=[[1,8],[2,16],[4,32],[64,128]];
3
3
  const BAYER=[[0,8,2,10],[12,4,14,6],[3,11,1,9],[15,7,13,5]];
4
4
  const clamp=v=>Math.max(0,Math.min(1,v));
5
+ const GLYPHS=Array.from({length:256},(_,i)=>String.fromCharCode(0x2800+i));
6
+ let grayTable=null,grayBrightness=0,grayContrast=0;
7
+ function stableBraille(rgba,width,height,brightness,contrast){
8
+ if(grayBrightness!==brightness||grayContrast!==contrast){
9
+ grayTable=Float32Array.from({length:256},(_,v)=>{
10
+ const luma=(.2126*v+.7152*v+.0722*v)/255;
11
+ return luma===0?0:clamp((Math.pow(luma,.9)*brightness-.5)*contrast+.5);
12
+ });
13
+ grayBrightness=brightness;grayContrast=contrast;
14
+ }
15
+ const table=grayTable,bytePixels=rgba instanceof Uint8Array||rgba instanceof Uint8ClampedArray,columns=Math.ceil(width/2),rows=Math.ceil(height/4),masks=new Uint8Array(columns*rows);
16
+ let dots=0;
17
+ // Stable dithering has no error diffusion: background pixels cannot affect a
18
+ // neighbor. Skip them before luma/hash work, and write straight to cell masks.
19
+ // Float32 rounding matches the original intermediate values array exactly.
20
+ for(let y=0;y<height;y++)for(let x=0,p=y*width*4;x<width;x++,p+=4){
21
+ const r=rgba[p],g=rgba[p+1],b=rgba[p+2],a=rgba[p+3];
22
+ if(a===0||(r===0&&g===0&&b===0))continue;
23
+ let value;
24
+ if(bytePixels&&a===255&&r===g&&g===b)value=table[r];
25
+ else{
26
+ const luma=(.2126*r+.7152*g+.0722*b)/255*(a/255);
27
+ value=Math.fround(luma===0?0:clamp((Math.pow(luma,.9)*brightness-.5)*contrast+.5));
28
+ }
29
+ if(value<=0)continue;
30
+ let n=Math.imul(x+1,0x1f123bb5)^Math.imul(y+1,0x5f356495);
31
+ n=Math.imul(n^(n>>>16),0x45d9f3b);n=Math.imul(n^(n>>>16),0x45d9f3b);n=(n^(n>>>16))>>>0;
32
+ if(value>(n+.5)/4294967296){masks[(y>>2)*columns+(x>>1)]|=BRAILLE_BITS[y&3][x&1];dots++;}
33
+ }
34
+ const lines=[];
35
+ for(let y=0;y<rows;y++){let line='';for(let x=0;x<columns;x++)line+=GLYPHS[masks[y*columns+x]];lines.push(line);}
36
+ return{text:lines.join('\n'),columns,rows,dots};
37
+ }
5
38
  export function rgbaToBraille(rgba,width,height,{brightness=1,contrast=1,method='floyd'}={}){
6
39
  if(!Number.isInteger(width)||!Number.isInteger(height)||width<1||height<1||width*height>1_000_000||rgba.length!==width*height*4)throw Error('Invalid RGBA image');
7
40
  if(!Number.isFinite(brightness)||brightness<=0||brightness>4||!Number.isFinite(contrast)||contrast<=0||contrast>4||!['floyd','bayer','stable'].includes(method))throw Error('Invalid dithering options');
41
+ if(method==='stable')return stableBraille(rgba,width,height,brightness,contrast);
8
42
  const values=new Float32Array(width*height),bits=new Uint8Array(width*height);
9
43
  for(let i=0;i<values.length;i++){
10
44
  const p=i*4,luma=(.2126*rgba[p]+.7152*rgba[p+1]+.0722*rgba[p+2])/255*(rgba[p+3]/255);
11
45
  values[i]=luma===0?0:clamp((Math.pow(luma,.9)*brightness-.5)*contrast+.5);
12
46
  }
13
- if(method==='stable'){
14
- // Fixed per-pixel threshold, not fresh random noise on every animation tick.
15
- // Unlike the 4x4 Bayer matrix this avoids a repeating cross-hatch on fur.
16
- for(let y=0;y<height;y++)for(let x=0;x<width;x++){
17
- let n=Math.imul(x+1,0x1f123bb5)^Math.imul(y+1,0x5f356495);
18
- n=Math.imul(n^(n>>>16),0x45d9f3b);n=Math.imul(n^(n>>>16),0x45d9f3b);n=(n^(n>>>16))>>>0;
19
- bits[y*width+x]=values[y*width+x]>(n+.5)/4294967296?1:0;
20
- }
21
- }else if(method==='bayer'){
47
+ if(method==='bayer'){
22
48
  for(let y=0;y<height;y++)for(let x=0;x<width;x++)bits[y*width+x]=values[y*width+x]>(BAYER[y%4][x%4]+.5)/16?1:0;
23
49
  }else{
24
50
  const add=(x,y,error,weight)=>{if(x>=0&&x<width&&y<height)values[y*width+x]+=error*weight/16;};
package/examples/live.mjs CHANGED
@@ -13,7 +13,7 @@ if(!out.isTTY||!input.isTTY){
13
13
  const data=bytes=>{for(const c of bytes.toString()){
14
14
  if(c==='q'||c==='\x03')stop();
15
15
  if(c===' ')paused=!paused;
16
- if(c==='b'||c==='t'){if(pet.interact(c==='b'?'ball':'treat'))paused=false;}
16
+ if(c==='b'||c==='t'||c==='g'){if(pet.interact(c==='b'?'ball':c==='t'?'treat':'tug'))paused=false;}
17
17
  }};
18
18
  process.on('SIGINT',stop);process.on('SIGTERM',stop);
19
19
  input.setRawMode(true);input.resume();input.on('data',data);
@@ -28,7 +28,7 @@ if(!out.isTTY||!input.isTTY){
28
28
  const frame=pet.frame({columns,rows}),lines=frame.text.split('\n');let batch='';
29
29
  for(let y=0;y<rows;y++)if(lines[y]!==previous[y])batch+=`\x1b[${y+1};1H${lines[y]}`;
30
30
  previous=lines;
31
- const status=`${paused?'Paused':frame.report.intent} | B ball T treat Space pause Q quit`.slice(0,columns);
31
+ const status=`${paused?'Paused':frame.report.intent} | B ball T treat G tug Space pause Q quit`.slice(0,columns);
32
32
  batch+=`\x1b[${rows+1};1H\x1b[2K${status}`;
33
33
  if(!out.write(batch))await once(out,'drain');
34
34
  await new Promise(resolve=>setTimeout(resolve,Math.max(1,1000/30-(performance.now()-start))));
@@ -180,6 +180,12 @@ class Companion(object):
180
180
  if kind == 'quit':
181
181
  self.quit()
182
182
  elif kind == 'loaded':
183
+ palette = {'cream': (248, 242, 232), 'gold': (231, 184, 103), 'dark': (67, 48, 24)}
184
+ ink = message.get('ink', 'cream')
185
+ if ink not in palette:
186
+ raise OverlayError('Invalid ink')
187
+ self.tiles.ink = palette[ink]
188
+ self.tiles.cache.clear()
183
189
  self.loaded = True
184
190
  self.set_status('playing')
185
191
  if self.test_mode:
@@ -213,7 +219,7 @@ class Companion(object):
213
219
  def press(self, name):
214
220
  if self.quitting:
215
221
  return
216
- if name in ('ball', 'treat'):
222
+ if name in ('ball', 'treat', 'tug'):
217
223
  if self.loaded and not self.paused:
218
224
  self.emit({'type': 'interact', 'kind': name})
219
225
  elif name == 'pause':
@@ -460,7 +466,7 @@ def ascii_label(text, limit):
460
466
 
461
467
  class XDisplay(object):
462
468
  """Owned X11 resources: one ARGB pet window, one control strip, one picker."""
463
- BAR_BUTTONS = (('ball', 'Ball'), ('treat', 'Treat'), ('window', 'Window'), ('pause', 'Pause'), ('quit', 'Quit'))
469
+ BAR_BUTTONS = (('ball', 'Ball'), ('treat', 'Treat'), ('tug', 'Tug'), ('window', 'Window'), ('pause', 'Pause'), ('quit', 'Quit'))
464
470
 
465
471
  def __init__(self, display_name=None, require_compositor=True, x=None):
466
472
  self.x = x or load_libraries()
@@ -238,7 +238,7 @@ function createCore(io, options) {
238
238
  if (s.quitting) return;
239
239
  if (!m || typeof m !== 'object') throw new Error('Invalid overlay message');
240
240
  if (m.type === 'quit') { quit(); return; }
241
- if (m.type === 'loaded') { s.loaded = true; io.status(TEXT.playing); io.loaded(); return; }
241
+ if (m.type === 'loaded') { var palette = {cream:[248,242,232,245],gold:[231,184,103,245],dark:[67,48,24,245]}; var ink = m.ink || 'cream'; if (!Object.prototype.hasOwnProperty.call(palette,ink)) throw new Error('Invalid ink'); INK=palette[ink]; s.loaded = true; io.status(TEXT.playing); io.loaded(); return; }
242
242
  if (m.type === 'interaction') { io.status(m.accepted === true ? TEXT.accepted : TEXT.busy); return; }
243
243
  if (m.type === 'error') { io.status(TEXT.engineError); quit(); return; }
244
244
  if (m.type === 'frame') {
@@ -265,7 +265,7 @@ var STYLE_BORDERLESS = 0, STYLE_TITLED = 1, STYLE_CLOSABLE = 2, STYLE_NONACTIVAT
265
265
  var BACKING_BUFFERED = 2, FLOATING_LEVEL = 3, ACTIVATION_ACCESSORY = 1, ANIMATION_NONE = 2;
266
266
  // canJoinAllSpaces | stationary | ignoresCycle | fullScreenAuxiliary
267
267
  var OVERLAY_SPACES = 1 | 16 | 64 | 256;
268
- var BAR_WIDTH = 354, BAR_HEIGHT = 40;
268
+ var BAR_WIDTH = 404, BAR_HEIGHT = 40;
269
269
 
270
270
  var shell = null;
271
271
 
@@ -379,7 +379,7 @@ function registerClasses() {
379
379
  name: 'RetrieviewerController', superclass: 'NSObject',
380
380
  methods: {
381
381
  'onInput:': guarded('onInput'), 'onTick:': guarded('onTick'),
382
- 'onBall:': guarded('onBall'), 'onTreat:': guarded('onTreat'), 'onPick:': guarded('onPick'),
382
+ 'onBall:': guarded('onBall'), 'onTreat:': guarded('onTreat'), 'onTug:': guarded('onTug'), 'onPick:': guarded('onPick'),
383
383
  'onPause:': guarded('onPause'), 'onQuit:': guarded('onQuit'),
384
384
  'onRefresh:': guarded('onRefresh'), 'onAttach:': guarded('onAttach'), 'onCancel:': guarded('onCancel'),
385
385
  'windowWillClose:': guarded('onWindowWillClose')
@@ -412,10 +412,11 @@ function startCompanion(testMode) {
412
412
  ballButton = makeButton('\uACF5', { x: 8, y: 6, width: 40, height: 28 }, 'onBall:');
413
413
  content.addSubview(ballButton);
414
414
  content.addSubview(makeButton('\uAC04\uC2DD', { x: 52, y: 6, width: 46, height: 28 }, 'onTreat:'));
415
- content.addSubview(makeButton('\uCC3D', { x: 102, y: 6, width: 40, height: 28 }, 'onPick:'));
416
- content.addSubview(makeButton('\uC27C', { x: 146, y: 6, width: 40, height: 28 }, 'onPause:'));
417
- content.addSubview(makeButton('\uC885\uB8CC', { x: 190, y: 6, width: 46, height: 28 }, 'onQuit:'));
418
- statusLabel = makeLabel(TEXT.preparing, { x: 242, y: 10, width: 108, height: 20 }, $.NSColor.whiteColor);
415
+ content.addSubview(makeButton('\uD130\uADF8', { x: 102, y: 6, width: 46, height: 28 }, 'onTug:'));
416
+ content.addSubview(makeButton('\uCC3D', { x: 152, y: 6, width: 40, height: 28 }, 'onPick:'));
417
+ content.addSubview(makeButton('\uC27C', { x: 196, y: 6, width: 40, height: 28 }, 'onPause:'));
418
+ content.addSubview(makeButton('\uC885\uB8CC', { x: 240, y: 6, width: 46, height: 28 }, 'onQuit:'));
419
+ statusLabel = makeLabel(TEXT.preparing, { x: 292, y: 10, width: 108, height: 20 }, $.NSColor.whiteColor);
419
420
  content.addSubview(statusLabel);
420
421
 
421
422
  // Menu bar item: the macOS counterpart of the Windows tray icon.
@@ -535,6 +536,7 @@ function startCompanion(testMode) {
535
536
  };
536
537
  shell.onBall = function () { core.interact('ball'); };
537
538
  shell.onTreat = function () { core.interact('treat'); };
539
+ shell.onTug = function () { core.interact('tug'); };
538
540
  shell.onPick = function () { choose(); };
539
541
  shell.onPause = function () { core.togglePause(); };
540
542
  shell.onQuit = function () { core.quit(); };
@@ -11,7 +11,7 @@ export const HEARTBEAT_MS=5000;
11
11
  // Bounded so the host prompt stays usable: at most 64x12 cells, hidden when the
12
12
  // terminal is too small for a legible pet.
13
13
  export const LIMITS=Object.freeze({maxColumns:64,maxRows:12,minColumns:20,minRows:5,sideMargin:4,reservedRows:14});
14
- export const COMMANDS=Object.freeze(['enable','disable','ball','treat','status']);
14
+ export const COMMANDS=Object.freeze(['enable','disable','ball','treat','tug','status']);
15
15
 
16
16
  const fn=(value,name)=>{if(typeof value!=='function')throw TypeError(`${name} must be a function`);};
17
17
  const defaultScheduler=()=>({now:()=>performance.now(),schedule:(callback,delayMs)=>setTimeout(callback,delayMs),cancel:handle=>clearTimeout(handle)});
@@ -133,7 +133,7 @@ export function createOpenCodePetController({hostVersion='unknown',scheduler,fps
133
133
  enabled=value;reconcile();
134
134
  }
135
135
  function interact(kind){
136
- if(kind!=='ball'&&kind!=='treat')throw TypeError("kind must be 'ball' or 'treat'");
136
+ if(!['ball','treat','tug'].includes(kind))throw TypeError("kind must be 'ball', 'treat' or 'tug'");
137
137
  return plugin?plugin.interact(kind):false;
138
138
  }
139
139
  function status(){
@@ -168,11 +168,11 @@ export function createOpenCodePetController({hostVersion='unknown',scheduler,fps
168
168
  setEnabled,
169
169
  interact,
170
170
  status,
171
- // Slash handler: '/retrieviewer <enable|disable|ball|treat|status>'.
171
+ // Slash handler: '/retrieviewer <enable|disable|ball|treat|tug|status>'.
172
172
  async command(argument){
173
173
  const word=String(argument??'').trim().toLowerCase()||'status';
174
174
  if(word==='enable'||word==='disable'){await setEnabled(word==='enable');return word==='enable'?'Retrieviewer enabled':'Retrieviewer disabled';}
175
- if(word==='ball'||word==='treat'){
175
+ if(word==='ball'||word==='treat'||word==='tug'){
176
176
  if(!plugin)return enabled?'Retrieviewer is still loading':'Retrieviewer is disabled; run /retrieviewer enable';
177
177
  return interact(word)?`Retrieviewer: ${word} accepted`:`Retrieviewer is busy; ${word} not accepted`;
178
178
  }
@@ -43,7 +43,7 @@ export default Plugin.define({
43
43
  commands:[{
44
44
  id:'retrieviewer.pet',
45
45
  title:'Retrieviewer pet',
46
- description:'enable | disable | ball | treat | status',
46
+ description:'enable | disable | ball | treat | tug | status',
47
47
  group:'Retrieviewer',
48
48
  palette:true,
49
49
  slash:{name:'retrieviewer',arguments:true},
@@ -61,6 +61,7 @@ class QuietWindow : Form {
61
61
  sealed class PetWindow : QuietWindow {
62
62
  protected override CreateParams CreateParams {get{CreateParams p=base.CreateParams;p.ExStyle|=0x80000|0x20;return p;}} // LAYERED + TRANSPARENT
63
63
  internal int PaintedDots;
64
+ internal Color Ink=Color.FromArgb(245,248,242,232);
64
65
  internal Bitmap PaintFrame(Dictionary<string,object> m,int cell,Rectangle canvas){
65
66
  int cols=Program.Int(m,"columns"),rows=Program.Int(m,"rows"),cx=Program.Int(m,"x"),cy=Program.Int(m,"y");
66
67
  string text=Program.Str(m,"text");
@@ -69,7 +70,7 @@ sealed class PetWindow : QuietWindow {
69
70
  string[] lines=text.Split('\n');if(lines.Length!=rows)throw new Exception("Invalid overlay row count");
70
71
  Bitmap image=new Bitmap(cols*cell,rows*cell*2,PixelFormat.Format32bppPArgb);
71
72
  try{
72
- using(Graphics g=Graphics.FromImage(image))using(Brush ink=new SolidBrush(Color.FromArgb(245,248,242,232))){
73
+ using(Graphics g=Graphics.FromImage(image))using(Brush ink=new SolidBrush(Ink)){
73
74
  g.Clear(Color.Transparent);g.SmoothingMode=System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
74
75
  int[] dx={0,0,0,1,1,1,0,1},dy={0,1,2,0,1,2,3,3};float step=cell/2f,diameter=Math.Max(1.1f,step*.48f);PaintedDots=0;
75
76
  for(int y=0;y<rows;y++){
@@ -111,10 +112,10 @@ sealed class Companion : ApplicationContext {
111
112
  TargetItem target;Form pickerWindow;QuietWindow testTarget;readonly bool testMode;Rectangle canvas;int revision=0,cell=8;bool visible=false,paused=false,loaded=false,quitting=false,picking=false,announced=false;
112
113
  Label status=new Label();Mutex mutex;
113
114
  internal Companion(Mutex single,bool diagnostic){
114
- mutex=single;testMode=diagnostic;bar.Text="Retrieviewer controls";bar.Size=new Size(354,40);bar.BackColor=Color.FromArgb(28,30,34);bar.ForeColor=Color.White;
115
+ mutex=single;testMode=diagnostic;bar.Text="Retrieviewer controls";bar.Size=new Size(404,40);bar.BackColor=Color.FromArgb(28,30,34);bar.ForeColor=Color.White;
115
116
  bar.Font=new Font("Segoe UI",9);bar.Controls.Add(MakeButton("공",8,()=>Interact("ball")));bar.Controls.Add(MakeButton("간식",52,()=>Interact("treat")));
116
- bar.Controls.Add(MakeButton("창",102,ChooseTarget));bar.Controls.Add(MakeButton("쉼",146,TogglePause));bar.Controls.Add(MakeButton("종료",190,Quit));
117
- status.AutoSize=false;status.Location=new Point(240,12);status.Size=new Size(110,22);status.Text="준비 중";bar.Controls.Add(status);
117
+ bar.Controls.Add(MakeButton("터그",102,()=>Interact("tug")));bar.Controls.Add(MakeButton("창",152,ChooseTarget));bar.Controls.Add(MakeButton("쉼",196,TogglePause));bar.Controls.Add(MakeButton("종료",240,Quit));
118
+ status.AutoSize=false;status.Location=new Point(290,12);status.Size=new Size(110,22);status.Text="준비 중";bar.Controls.Add(status);
118
119
  ContextMenuStrip menu=new ContextMenuStrip();menu.Items.Add("다른 창 선택",null,(s,e)=>ChooseTarget());menu.Items.Add("숨기기 / 다시 표시",null,(s,e)=>TogglePause());menu.Items.Add("종료",null,(s,e)=>Quit());
119
120
  tray.Icon=SystemIcons.Application;tray.Text="Retrieviewer · 창 선택";tray.ContextMenuStrip=menu;tray.Visible=true;tray.DoubleClick+=(s,e)=>ChooseTarget();
120
121
  // Handles are created on the UI thread before the reader can signal EOF.
@@ -127,7 +128,7 @@ sealed class Companion : ApplicationContext {
127
128
  }
128
129
  }
129
130
  Button MakeButton(string title,int x,Action action){
130
- Button b=new Button();b.Text=title;b.TabStop=false;b.Size=new Size(title=="간식"?46:40,28);b.Location=new Point(x,6);b.FlatStyle=FlatStyle.Flat;b.FlatAppearance.BorderSize=0;b.BackColor=Color.FromArgb(48,51,58);b.ForeColor=Color.White;b.Click+=(s,e)=>action();return b;
131
+ Button b=new Button();b.Text=title;b.TabStop=false;b.Size=new Size(title=="간식"||title=="터그"?46:40,28);b.Location=new Point(x,6);b.FlatStyle=FlatStyle.Flat;b.FlatAppearance.BorderSize=0;b.BackColor=Color.FromArgb(48,51,58);b.ForeColor=Color.White;b.Click+=(s,e)=>action();return b;
131
132
  }
132
133
  void ReadInput(){
133
134
  try{
@@ -149,7 +150,7 @@ sealed class Companion : ApplicationContext {
149
150
  for(;;){Dictionary<string,object> m;lock(inputLock){if(messages.Count==0)break;m=messages.Dequeue();}
150
151
  string type=Program.Str(m,"type");
151
152
  if(type=="quit"){Quit();return;}
152
- if(type=="loaded"){loaded=true;status.Text="함께 노는 중";if(testMode)((Button)bar.Controls[0]).PerformClick();continue;}
153
+ if(type=="loaded"){string ink=m.ContainsKey("ink")?Program.Str(m,"ink"):"cream";if(ink!="cream"&&ink!="gold"&&ink!="dark")throw new Exception("Invalid ink");pet.Ink=ink=="gold"?Color.FromArgb(245,231,184,103):ink=="dark"?Color.FromArgb(245,67,48,24):Color.FromArgb(245,248,242,232);loaded=true;status.Text="함께 노는 중";if(testMode)((Button)bar.Controls[0]).PerformClick();continue;}
153
154
  if(type=="interaction"){status.Text=Program.Bool(m,"accepted")?"좋아!":"잠깐 기다려";continue;}
154
155
  if(type=="error"){status.Text="엔진 오류";Quit();return;}
155
156
  if(type=="frame"){
@@ -93,13 +93,14 @@ export async function desktopHelper(options={}){
93
93
  throw Error('Linux desktop needs system Python3.6+ (AlmaLinux8 platform-python is supported). No packages or system settings were installed.');
94
94
  }
95
95
  export async function startDesktop(options={}){
96
+ const ink=options.ink??'cream';if(!['cream','gold','dark'].includes(ink))throw Error('Invalid desktop ink');
96
97
  // Fail before creating any cache/session on unsupported desktops (e.g. Wayland).
97
98
  desktopPlatform(options);
98
99
  const status=await desktopStatus(options);if(status.running)return {...status,alreadyRunning:true};
99
100
  if(status.stale)await stopDesktop(options);
100
101
  const helper=await desktopHelper(options);
101
102
  return new Promise((resolve,reject)=>{
102
- const child=spawn(process.execPath,[fileURLToPath(new URL('./worker.mjs',import.meta.url)),'--helper-spec',JSON.stringify(helper)],{detached:true,windowsHide:true,stdio:['ignore','ignore','ignore','ipc'],env:{...process.env,RETRIEVIEWER_HOME:getConfigPaths(options).root}});
103
+ const child=spawn(process.execPath,[fileURLToPath(new URL('./worker.mjs',import.meta.url)),'--helper-spec',JSON.stringify(helper)],{detached:true,windowsHide:true,stdio:['ignore','ignore','ignore','ipc'],env:{...process.env,RETRIEVIEWER_HOME:getConfigPaths(options).root,RETRIEVIEWER_INK:ink}});
103
104
  let settled=false;const finish=(error,message)=>{if(settled)return;settled=true;clearTimeout(timer);if(child.connected)child.disconnect();child.unref();error?reject(error):resolve(message);};
104
105
  const timer=setTimeout(()=>{if(child.connected)child.send({command:'abort'});finish(Error('Desktop startup timed out; no agent settings changed'));},20000);
105
106
  child.on('error',error=>finish(error));child.on('message',message=>message.error?finish(Error(message.error)):finish(null,message));child.on('exit',code=>finish(Error('Desktop exited during startup ('+code+')')));
@@ -20,20 +20,26 @@ export function cropFrame(frame,revision){
20
20
 
21
21
  // Request/ack rendering: never queue obsolete frames behind a slow native painter.
22
22
  export function createFramePump({pet,send,now=()=>performance.now()}){
23
- let viewport=null,inFlight=false,last=null,revision=0,paused=false,disposed=false;
23
+ let viewport=null,inFlight=false,last=null,lastFrame=null,revision=0,paused=false,disposed=false,nextPaint=0;
24
24
  const alive=()=>{if(disposed)throw Error('Overlay pump is disposed');};
25
25
  return {
26
- viewport(message){alive();const next=viewportOf(message);revision=message.revision;viewport=next;last=null;},
27
- pause(value){alive();paused=!!value;last=null;},
26
+ viewport(message){alive();const next=viewportOf(message);revision=message.revision;viewport=next;last=null;lastFrame=null;nextPaint=0;},
27
+ pause(value){alive();paused=!!value;last=null;lastFrame=null;nextPaint=0;},
28
28
  ack(){alive();inFlight=false;},
29
29
  tick(){
30
30
  alive();if(inFlight||!viewport||paused)return false;
31
31
  const t=now(),dt=last===null?0:Math.max(0,(t-last)/1000);last=t;
32
- pet.step(dt);const frame=pet.frame(viewport);inFlight=true;
33
- try{send(cropFrame(frame,revision));}catch(error){inFlight=false;throw error;}return true;
32
+ pet.step(dt);if(t<nextPaint)return false;
33
+ const source=pet.frame(viewport),frame=cropFrame(source,revision);
34
+ nextPaint=source.state?.state==='sleep'?t+200:0;
35
+ // A held seat/down pose can be byte-identical for seconds. Advance the life
36
+ // clock normally but avoid JSON, IPC, bitmap allocation and native repaint.
37
+ if(lastFrame&&frame.x===lastFrame.x&&frame.y===lastFrame.y&&frame.columns===lastFrame.columns&&frame.rows===lastFrame.rows&&frame.text===lastFrame.text)return false;
38
+ inFlight=true;
39
+ try{send(frame);lastFrame=frame;}catch(error){inFlight=false;throw error;}return true;
34
40
  },
35
- interact(kind){alive();if(!['ball','treat'].includes(kind))throw Error('Invalid overlay interaction');return pet.interact(kind);},
41
+ interact(kind){alive();if(!['ball','treat','tug'].includes(kind))throw Error('Invalid overlay interaction');const accepted=pet.interact(kind);if(accepted){lastFrame=null;nextPaint=0;}return accepted;},
36
42
  get hidden(){return !viewport||paused;},
37
- dispose(){if(!disposed){disposed=true;viewport=null;pet.dispose();}},
43
+ dispose(){if(!disposed){disposed=true;viewport=lastFrame=null;pet.dispose();}},
38
44
  };
39
45
  }