@bitefoundry/retrieviewer 0.0.17 → 0.0.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ASSET-PROVENANCE.md +11 -0
- package/ASSETS-LICENSE.md +6 -0
- package/ATTRIBUTION.md +8 -0
- package/DESKTOP-PLATFORMS.md +1 -1
- package/INSTALLING.md +2 -1
- package/LICENSE +16 -16
- package/NATIVE-RUNTIME.md +89 -0
- package/PREREQUISITES.md +7 -0
- package/PUPPY-MEMORY.md +11 -0
- package/README.ko.md +10 -1
- package/README.md +11 -1
- package/RELEASING.md +19 -0
- package/THIRD-PARTY-NOTICES.md +8 -0
- package/WINDOWS-COMPANION.md +15 -6
- package/braille.mjs +2 -2
- package/integrations/native-windows/CMakeLists.txt +15 -0
- package/integrations/native-windows/THIRD-PARTY-LICENSES.txt +77 -0
- package/integrations/native-windows/application.manifest +8 -0
- package/integrations/native-windows/bin/win32-x64/retrieviewer-native.exe +0 -0
- package/integrations/native-windows/desktop.mjs +45 -0
- package/integrations/native-windows/host.cpp +84 -0
- package/integrations/native-windows/manifest.json +14 -0
- package/integrations/native-windows/native-control.hpp +41 -0
- package/integrations/native-windows/native-window.hpp +128 -0
- package/integrations/native-windows/runtime.bundle.mjs +4694 -0
- package/integrations/native-windows/runtime.mjs +55 -0
- package/integrations/native-windows/sources.json +23 -0
- package/integrations/purrviewer/ART.md +289 -0
- package/integrations/purrviewer/ASSETS-LICENSE.md +21 -0
- package/integrations/purrviewer/LICENSE +24 -0
- package/integrations/purrviewer/art/ginger.bin.gz +0 -0
- package/integrations/purrviewer/art/ginger.json +1707 -0
- package/integrations/purrviewer/art/ginger.rvz +0 -0
- package/integrations/purrviewer/src/contact.mjs +63 -0
- package/integrations/purrviewer/src/habitat.mjs +41 -0
- package/integrations/purrviewer/src/life.mjs +280 -0
- package/integrations/purrviewer/src/purrviewer-core.mjs +130 -0
- package/integrations/purrviewer/src/purrviewer.mjs +37 -0
- package/integrations/purrviewer/src/walk-cycle.mjs +3 -0
- package/integrations/purrviewer/src/world.mjs +216 -0
- package/integrations/purrviewer/vendor/retrieviewer/LICENSE-CODE +21 -0
- package/integrations/purrviewer/vendor/retrieviewer/braille.mjs +73 -0
- package/integrations/purrviewer/vendor/retrieviewer/puppy-frame-file.mjs +15 -0
- package/integrations/purrviewer/vendor/retrieviewer/puppy-frame-indexed.mjs +78 -0
- package/integrations/purrviewer/vendor/retrieviewer/puppy-frame-predictive.mjs +170 -0
- package/integrations/purrviewer/vendor/retrieviewer/puppy-frame-store.mjs +114 -0
- package/integrations/windows-overlay/desktop.mjs +11 -3
- package/integrations/windows-overlay/worker.mjs +9 -2
- package/package.json +39 -4
- package/puppy-clean.mjs +2 -2
- package/puppy-companion-sprites.mjs +2 -2
- package/puppy-frame-indexed.mjs +2 -2
- package/puppy-paper-actions.mjs +2 -2
- package/puppy-paper-behaviors.mjs +1 -1
- package/puppy-paper-contact.mjs +2 -2
- package/puppy-paper-seat.mjs +2 -2
- package/puppy-paper.mjs +12 -12
- package/retrieviewer-cli.mjs +10 -6
- package/retrieviewer.mjs +13 -4
package/ASSET-PROVENANCE.md
CHANGED
|
@@ -121,3 +121,14 @@ domestic trademark research records possible similar marks; owning an npm scope
|
|
|
121
121
|
does not resolve them. No trademark filing or legal service is part of this
|
|
122
122
|
release. The review records provenance, compatible release terms and exclusions;
|
|
123
123
|
it must not be advertised as comprehensive IP clearance.
|
|
124
|
+
|
|
125
|
+
## Ginger cat release · 2026-10-25
|
|
126
|
+
|
|
127
|
+
The owner explicitly selected CC BY-SA4.0 for the approved ginger cat and
|
|
128
|
+
authorized including it in this npm/website release. Code stays MIT.
|
|
129
|
+
Purrviewer source commit5452da3569623b26f1ea87f1097735a1606c5c67 records
|
|
130
|
+
the65-key artwork and code. The included ART.md records source-sheet hashes,
|
|
131
|
+
registration and approved extensions; research photos were observation only,
|
|
132
|
+
not generation inputs or bundled runtime assets. Original source sheets,
|
|
133
|
+
research media and the unused tuxedo prototype are excluded. Alternative RVZ5
|
|
134
|
+
encoding preserves every decoded Q5 pixel. See the cat ASSETS-LICENSE.md.
|
package/ASSETS-LICENSE.md
CHANGED
|
@@ -57,3 +57,9 @@ unrestricted. No warranty or legal-clearance certification is supplied.
|
|
|
57
57
|
사용·수정·재배포도 가능하며, 변경한 그림을 배포할 때는 동일조건을 지켜야
|
|
58
58
|
합니다. 코드에는 별도의 MIT 라이선스가 적용됩니다. 사진 참고 출처와 가공
|
|
59
59
|
경위는 ATTRIBUTION.md에 포함되며, 원본 사진은 패키지에 포함하지 않습니다.
|
|
60
|
+
|
|
61
|
+
## Ginger cat (0.0.18)
|
|
62
|
+
|
|
63
|
+
The included Purrviewer ginger-cat artwork and metadata also use CC BY-SA 4.0,
|
|
64
|
+
explicitly selected by the owner on 2026-10-25. See
|
|
65
|
+
[integrations/purrviewer/ASSETS-LICENSE.md](integrations/purrviewer/ASSETS-LICENSE.md).
|
package/ATTRIBUTION.md
CHANGED
|
@@ -57,3 +57,11 @@ Reference photo collections, YouTube footage, experimental Meshy models,
|
|
|
57
57
|
noncommercial motion research/datasets, interpolation tools/model weights and
|
|
58
58
|
worker logs are not distributed in this package. No exclusive copyright in
|
|
59
59
|
otherwise unprotected AI output is claimed. Code has its own MIT license.
|
|
60
|
+
|
|
61
|
+
## Purrviewer ginger cat
|
|
62
|
+
|
|
63
|
+
Ginger-cat artwork and motion assembly: BiteFoundry project maintainer, 2026.
|
|
64
|
+
AI-generated poses, registered and quantized into grayscale Q5 crops.
|
|
65
|
+
CC BY-SA 4.0; attribution and creation record in
|
|
66
|
+
[integrations/purrviewer/ASSETS-LICENSE.md](integrations/purrviewer/ASSETS-LICENSE.md)
|
|
67
|
+
and [ART.md](integrations/purrviewer/ART.md).
|
package/DESKTOP-PLATFORMS.md
CHANGED
|
@@ -14,7 +14,7 @@ earlier0.0.3 baseline. macOS remains experimental.
|
|
|
14
14
|
|
|
15
15
|
| Platform | Adapter | Current verification boundary |
|
|
16
16
|
| --- | --- | --- |
|
|
17
|
-
| Windows10/11 |
|
|
17
|
+
| Windows10/11 | QuickJS/Win32 basic; Node/.NET optional watching | Live verified; shared-adapter regression checks passed on this host |
|
|
18
18
|
| macOS | Built-in osascript/JXA AppKit, no Python/Xcode/browser runtime | Experimental; no macOS runtime available on this Windows host |
|
|
19
19
|
| Linux X11 | Python3.6+ stdlib ctypes + X11/Xrender/Xext | AlmaLinux8 GLIBC2.28/Python3.6.8/Node22.23.4 live checks passed on owned Xvfb + Openbox + xcompmgr |
|
|
20
20
|
| Linux native Wayland | No arbitrary-window tracking adapter | Refused, including sessions exposing an Xwayland DISPLAY; no insecure compositor workaround |
|
package/INSTALLING.md
CHANGED
|
@@ -13,7 +13,8 @@ mask and native painter, without new color sprites or screen capture.
|
|
|
13
13
|
Windows and AlmaLinux8/GLIBC2.28 X11 have live test evidence. macOS support is
|
|
14
14
|
experimental and not runtime verified; native Wayland is unsupported. Linux needs
|
|
15
15
|
system Python3.6+, X11/Xrender/Xext and a compositor; Windows needs the .NET
|
|
16
|
-
Framework4 compiler
|
|
16
|
+
Framework4 compiler only for the optional compatibility engine. Windows x64
|
|
17
|
+
basic mode uses the bundled native engine. See [Windows controls and removal](WINDOWS-COMPANION.md).
|
|
17
18
|
|
|
18
19
|
## Optional native OpenCode integration
|
|
19
20
|
|
package/LICENSE
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
Retrieviewer licensing
|
|
2
|
-
|
|
3
|
-
Software source code: MIT License, see LICENSE-CODE.
|
|
4
|
-
Bundled puppy artwork/frame data: Creative Commons Attribution-ShareAlike 4.0
|
|
5
|
-
International (CC-BY-SA-4.0), see ASSETS-LICENSE.md and ATTRIBUTION.md.
|
|
6
|
-
https://creativecommons.org/licenses/by-sa/4.0/legalcode.en
|
|
7
|
-
|
|
8
|
-
The complete package is not MIT-only. Commercial use is allowed under the
|
|
9
|
-
applicable terms. Artwork adaptations retain ShareAlike obligations; the
|
|
10
|
-
separately licensed software does not become CC-licensed merely by bundling it.
|
|
11
|
-
|
|
12
|
-
Third-party components retain their original notices, including the MIT license
|
|
13
|
-
under vendor/procedural-animals. See THIRD-PARTY-NOTICES.md.
|
|
14
|
-
|
|
15
|
-
No exclusive copyright in AI-generated output, registered trademark status,
|
|
16
|
-
third-party endorsement or comprehensive legal clearance is claimed.
|
|
1
|
+
Retrieviewer licensing
|
|
2
|
+
|
|
3
|
+
Software source code: MIT License, see LICENSE-CODE.
|
|
4
|
+
Bundled puppy and ginger-cat artwork/frame data: Creative Commons Attribution-ShareAlike 4.0
|
|
5
|
+
International (CC-BY-SA-4.0), see ASSETS-LICENSE.md and ATTRIBUTION.md.
|
|
6
|
+
https://creativecommons.org/licenses/by-sa/4.0/legalcode.en
|
|
7
|
+
|
|
8
|
+
The complete package is not MIT-only. Commercial use is allowed under the
|
|
9
|
+
applicable terms. Artwork adaptations retain ShareAlike obligations; the
|
|
10
|
+
separately licensed software does not become CC-licensed merely by bundling it.
|
|
11
|
+
|
|
12
|
+
Third-party components retain their original notices, including the MIT license
|
|
13
|
+
under vendor/procedural-animals. See THIRD-PARTY-NOTICES.md.
|
|
14
|
+
|
|
15
|
+
No exclusive copyright in AI-generated output, registered trademark status,
|
|
16
|
+
third-party endorsement or comprehensive legal clearance is claimed.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Lightweight desktop runtime · 0.0.18
|
|
2
|
+
|
|
3
|
+
Windows x64 basic desktop mode runs the existing JavaScript pet behavior in
|
|
4
|
+
QuickJS-NG, inside the same native process that paints the transparent window.
|
|
5
|
+
Node/npm launches it and exits. There is no persistent Node worker, CLR runtime,
|
|
6
|
+
browser, Electron, extra npm dependency or install-time compiler in this mode.
|
|
7
|
+
|
|
8
|
+
```sh
|
|
9
|
+
retrieviewer desktop
|
|
10
|
+
retrieviewer desktop --pet cat
|
|
11
|
+
retrieviewer desktop-status --json
|
|
12
|
+
retrieviewer desktop-stop
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Choose Retriever or Ginger cat in the picker/control strip, or run the second
|
|
16
|
+
command to switch a running native companion. Only the selected pet is loaded.
|
|
17
|
+
Switching disposes its assets before creating the other one. No startup service,
|
|
18
|
+
agent patch or wrapper is installed. Removal is `retrieviewer desktop-remove --yes`
|
|
19
|
+
followed by `npm uninstall -g @bitefoundry/retrieviewer`.
|
|
20
|
+
|
|
21
|
+
## Artwork and behavior
|
|
22
|
+
|
|
23
|
+
The same pure dog/cat factories own motion, perspective, contacts, actions and
|
|
24
|
+
autonomy. The native host supplies gzip, Q5 reconstruction, grayscale raster and
|
|
25
|
+
stable Braille kernels. Individually compressed crops are read from indexed
|
|
26
|
+
files, with two borrowed decoded frames per store. The 256 Braille-mask stamp
|
|
27
|
+
atlas replaces thousands of GDI+ ellipse calls and preserves the original dots.
|
|
28
|
+
|
|
29
|
+
The actual native kernels were compared to portable Node across 64 dog and 128
|
|
30
|
+
cat output frames at 160×72 and 480×135 cells, including movement, eating,
|
|
31
|
+
attention, stretching, greeting, company, tower and wheel. All 234,455,040 RGBA
|
|
32
|
+
bytes matched. Tests retain SHA-256 results; no resampling or lossier art was
|
|
33
|
+
introduced. Code is MIT. Dog and cat artwork is CC BY-SA 4.0.
|
|
34
|
+
|
|
35
|
+
## Measured memory
|
|
36
|
+
|
|
37
|
+
On this Windows10/11 x64 host, one native process rendered an owned off-screen
|
|
38
|
+
3840×2160 window for150seconds per species, sampled twice per second. The
|
|
39
|
+
ordinary runtime used its diagnostic target, real GDI+ painter and scheduled
|
|
40
|
+
play/treat/attention or tower/wheel actions. No forced GC or working-set trimming.
|
|
41
|
+
|
|
42
|
+
| Pet | Median RSS | Sampled maximum | OS lifetime peak |
|
|
43
|
+
|---|---:|---:|---:|
|
|
44
|
+
| Retriever |29.4MiB|39.3MiB|39.9MiB|
|
|
45
|
+
| Ginger cat |44.2MiB|61.8MiB|65.3MiB|
|
|
46
|
+
|
|
47
|
+
The cat's scene also includes the tower and wheel. It is typically below50MiB
|
|
48
|
+
in this measurement, but its peaks exceed50MiB; this is not a strict50MiB cap.
|
|
49
|
+
The preceding Node/CLR dog had a sampled maximum170.7MiB on the same host.
|
|
50
|
+
Raw evidence: dist/native-product-memory/{dog,cat,report}.json (local QA output).
|
|
51
|
+
Reproduce with the measurement command below. Package/website figures are
|
|
52
|
+
rounded; memory remains dependent on scene, window dimensions and OS behavior.
|
|
53
|
+
|
|
54
|
+
## Scope
|
|
55
|
+
|
|
56
|
+
This default native runtime is **Windows x64**. `--watch stackbite` and
|
|
57
|
+
`--brain openrouter` retain the Node/.NET compatibility path on Windows;
|
|
58
|
+
`--engine node` explicitly selects it. Their memory use is higher. Linux X11
|
|
59
|
+
keeps Node + system Python; macOS keeps its experimental Node/JXA path. Both
|
|
60
|
+
species work through those adapters, with the existing three controls mapped
|
|
61
|
+
to toy/greeting, treat and company for the cat. Tower/wheel controls are in the
|
|
62
|
+
Windows native bar and website. Browser previews keep their portable loader.
|
|
63
|
+
The native memory figures do not describe optional watching, other operating
|
|
64
|
+
systems or browser tabs. No blanket memory ceiling is promised for every window.
|
|
65
|
+
|
|
66
|
+
## Build and lifecycle
|
|
67
|
+
|
|
68
|
+
Maintainers run `npm run build:native` on Windows x64 with Visual Studio 2022 C++
|
|
69
|
+
Build Tools, CMake and Bun. It bundles the ordinary factories without source
|
|
70
|
+
rewriting, verifies pinned QuickJS/zlib commits, statically links the CRT, and
|
|
71
|
+
writes a size/SHA-256 manifest for the shipped binary and bundle. Only Windows
|
|
72
|
+
system DLLs are required. Sources, source pins and upstream license texts ship
|
|
73
|
+
with the package. End users receive the prebuilt executable.
|
|
74
|
+
|
|
75
|
+
The short-lived launcher sends configuration over stdin, waits for readiness,
|
|
76
|
+
then acknowledges detach. EOF before detach stops the native process. A local
|
|
77
|
+
owner-only named pipe with a per-launch random token supports status, species
|
|
78
|
+
switching and stop; remote pipe clients are rejected. The process removes only
|
|
79
|
+
its own session file on exit. The picker follows one explicitly selected app;
|
|
80
|
+
it hides and freezes when that app is not foreground. It reads window geometry
|
|
81
|
+
and titles, not screen content or keyboard input. Watch/brain remain opt-in.
|
|
82
|
+
|
|
83
|
+
QA: `node --test native-desktop.test.mjs`, `npm run check:native-pixels`, and
|
|
84
|
+
`node tools/measure-native-pet.mjs 150`. These use isolated state and owned
|
|
85
|
+
off-screen diagnostic windows, preserving an already-running user companion.
|
|
86
|
+
|
|
87
|
+
The executable embeds the Windows UTF-8 process code-page manifest (Windows10
|
|
88
|
+
1903+ /11) so QuickJS stdio also reads Korean/non-ASCII installation paths.
|
|
89
|
+
A regression test launches both species from Korean bundle, art and state paths.
|
package/PREREQUISITES.md
CHANGED
|
@@ -93,3 +93,10 @@ npx --yes --package @bitefoundry/retrieviewer@latest retrieviewer desktop
|
|
|
93
93
|
|
|
94
94
|
`npx` still needs Node/npm and a compatible GUI desktop, and may download the
|
|
95
95
|
package into npm's cache. It does not create a permanent global command.
|
|
96
|
+
|
|
97
|
+
## Windows native basic mode (0.0.18)
|
|
98
|
+
|
|
99
|
+
Windows10/11 x64 + Node20+ for npm/launch. The shipped QuickJS/Win32 runtime
|
|
100
|
+
needs no compiler, CLR or additional runtime download. Node exits after launch.
|
|
101
|
+
Optional StackBite/LLM watching or --engine node keeps the existing .NET helper
|
|
102
|
+
and Node worker; that compatibility mode needs the Windows Framework4 compiler.
|
package/PUPPY-MEMORY.md
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
# 0.0.18 native desktop result
|
|
2
|
+
|
|
3
|
+
Windows x64 basic mode now uses a single QuickJS/Win32 process. In real4K
|
|
4
|
+
150second tests the dog median RSS was29.4MiB (OS lifetime peak39.9MiB),
|
|
5
|
+
and cat median44.2MiB (peak65.3MiB). Same artwork/motion; no forced GC/trim.
|
|
6
|
+
The cat typically fits50MiB here but peaks exceed it. These figures exclude
|
|
7
|
+
optional StackBite/LLM watching, other OS hosts and browser tabs.
|
|
8
|
+
See [method, architecture and compatibility scope](NATIVE-RUNTIME.md).
|
|
9
|
+
|
|
10
|
+
## Earlier Node measurements (historical)
|
|
11
|
+
|
|
1
12
|
# Runtime weight and performance
|
|
2
13
|
|
|
3
14
|
## Indexed local art (local candidate, 2026-10-25)
|
package/README.ko.md
CHANGED
|
@@ -80,7 +80,7 @@ PATH에 넣어야 해요. [bash·csh/tcsh·Windows별 안내](PREREQUISITES.md#c
|
|
|
80
80
|
|
|
81
81
|
| 환경 | 상태와 필요 조건 |
|
|
82
82
|
| --- | --- |
|
|
83
|
-
| Windows10/11 x64 | 실제 실행 검증.
|
|
83
|
+
| Windows10/11 x64 | 실제 실행 검증. Node20+. 기본 실행은 포함된 네이티브 엔진, 선택형 감시는 .NET 호환 엔진. |
|
|
84
84
|
| Linux X11 | AlmaLinux8 / GLIBC2.28 / Python3.6.8 / Node22.23.4를 격리된 GUI에서 검증. Python3.6+, X11/Xrender/Xext, compositor 필요. |
|
|
85
85
|
| macOS | **실험적 지원. 실제 Mac 실행 미검증.** 기본 osascript/AppKit 사용. |
|
|
86
86
|
| 네이티브 Wayland·화면 없는 SSH | 데스크톱 오버레이 미지원. 세션·보안 설정을 자동 변경하지 않아요. |
|
|
@@ -147,3 +147,12 @@ JS 플러그인과 JSONL 브리지를 제공해요. 화면·입력·시간 제
|
|
|
147
147
|
|
|
148
148
|
AI로 생성한 그림에 자세 정렬·접지·프레임 처리를 적용한 애니메이션입니다.
|
|
149
149
|
실제 모션 캡처나 자의식이 있는 강아지라고 주장하지 않아요.
|
|
150
|
+
|
|
151
|
+
## 0.0.18
|
|
152
|
+
|
|
153
|
+
Windows x64 기본 실행은 네이티브 프로세스 하나만 남고 Node는 종료돼요.
|
|
154
|
+
그림과 움직임은 그대로예요. `retrieviewer desktop --pet cat` 또는 창 선택창에서
|
|
155
|
+
치즈 고양이를 고를 수 있어요. 둘 중 선택한 펫만 불러옵니다.
|
|
156
|
+
StackBite·LLM 감시는 메모리를 더 쓰는 기존 Node 호환 엔진을 유지해요.
|
|
157
|
+
Linux·macOS·브라우저의 호스트 구조는 그대로이며, 로컬 그림은 두 펫 모두
|
|
158
|
+
필요한 프레임만 파일에서 읽어요. [지원 범위와 측정](NATIVE-RUNTIME.md).
|
package/README.md
CHANGED
|
@@ -77,7 +77,7 @@ Do not use `sudo npm install -g` as a blanket workaround.
|
|
|
77
77
|
|
|
78
78
|
| Desktop | Status / requirements |
|
|
79
79
|
| --- | --- |
|
|
80
|
-
| Windows 10/11 x64 | Live tested.
|
|
80
|
+
| Windows 10/11 x64 | Live tested. Node20+; bundled native engine for basic mode. Optional watching uses the .NET compatibility helper. |
|
|
81
81
|
| 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. |
|
|
82
82
|
| macOS | **Experimental; no Mac runtime verification yet.** Uses built-in osascript/AppKit. |
|
|
83
83
|
| Native Wayland / headless SSH | Not supported by the desktop overlay. No session/privacy workaround is installed. |
|
|
@@ -188,3 +188,13 @@ the complete package is **not MIT-only**.
|
|
|
188
188
|
The puppy artwork was developed with AI-generated imagery and programmatic pose,
|
|
189
189
|
grounding and frame processing. It is authored animation, not motion capture or
|
|
190
190
|
a claim of sentience.
|
|
191
|
+
|
|
192
|
+
## 0.0.18
|
|
193
|
+
|
|
194
|
+
Windows x64 basic mode now runs one native process; Node exits after launch.
|
|
195
|
+
The existing artwork and behavior stay the same. Use `retrieviewer desktop --pet cat`
|
|
196
|
+
for the ginger cat, or select it in the window picker. Only one pet is loaded.
|
|
197
|
+
Optional StackBite/LLM watching retains the higher-memory Node compatibility
|
|
198
|
+
engine. Linux/macOS and browser previews retain their existing host architecture.
|
|
199
|
+
Both species use indexed crops for local loading.
|
|
200
|
+
See [runtime scope and measurements](NATIVE-RUNTIME.md).
|
package/RELEASING.md
CHANGED
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
# Retrieviewer 0.0.18 release process
|
|
2
|
+
|
|
3
|
+
Current target: `@bitefoundry/retrieviewer@0.0.18`, public `latest`.
|
|
4
|
+
The owner authorized immediate npm and website deployment of the lightweight
|
|
5
|
+
shared engine on2026-10-25, and explicitly selected CC BY-SA4.0 for cat artwork.
|
|
6
|
+
Code remains MIT. Ship the real native Windows x64 basic runtime, ginger cat and
|
|
7
|
+
portable compatibility paths. Preserve approved artwork, physics, autonomy,
|
|
8
|
+
optional watcher/brain behavior and the user's already-running pet.
|
|
9
|
+
|
|
10
|
+
Validate exact native-vs-portable rendered pixels, authenticated control,
|
|
11
|
+
species switching, startup EOF cleanup, real picker lifecycle and real4K memory.
|
|
12
|
+
Build/install a fresh exact tarball, compare every shipped byte, run the full
|
|
13
|
+
suite and owned Windows checks, then publish that artifact. Verify registry
|
|
14
|
+
integrity, a fresh public-registry consumer and the deployed bilingual website.
|
|
15
|
+
Native low-memory figures apply only to Windows x64 basic mode; watch/brain,
|
|
16
|
+
other operating systems and browser previews keep their documented scope.
|
|
17
|
+
|
|
18
|
+
## Previous 0.0.17 scope (historical)
|
|
19
|
+
|
|
1
20
|
# Retrieviewer 0.0.17 release process
|
|
2
21
|
|
|
3
22
|
Current target: `@bitefoundry/retrieviewer@0.0.17`, public `latest`.
|
package/THIRD-PARTY-NOTICES.md
CHANGED
|
@@ -17,3 +17,11 @@ photographers (Scott, CC BY 2.0; Stefan Bauer, CC BY-SA 2.5) and adaptation hist
|
|
|
17
17
|
are credited in ATTRIBUTION.md. Original photographs are not bundled. See LICENSE,
|
|
18
18
|
LICENSE-CODE, ASSETS-LICENSE.md and the bounded ASSET-PROVENANCE.md review. No
|
|
19
19
|
comprehensive legal-clearance or exclusive AI-output ownership claim is made.
|
|
20
|
+
|
|
21
|
+
## Native Windows runtime
|
|
22
|
+
|
|
23
|
+
The prebuilt Windows x64 engine includes QuickJS-NG 0.17.0 (MIT) and zlib
|
|
24
|
+
1.3.2 (Zlib). Exact source commits and complete license notices are in
|
|
25
|
+
`integrations/native-windows/sources.json` and `THIRD-PARTY-LICENSES.txt`.
|
|
26
|
+
The optimized kernels and host are MIT. No downloaded runtime is installed
|
|
27
|
+
at launch; no additional npm runtime dependency was added.
|
package/WINDOWS-COMPANION.md
CHANGED
|
@@ -9,9 +9,10 @@ wrapper. Version0.0.2 includes it; version0.0.1 does not. Setup is two steps:
|
|
|
9
9
|
Choose a supported ordinary Windows app window (including a terminal containing
|
|
10
10
|
Codex). Then use your app normally, without a
|
|
11
11
|
wrapper, shell alias, patched binary, changed credentials, or agent configuration.
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
Windows10/11 x64 with Node20+
|
|
12
|
+
Basic mode uses the bundled QuickJS/Win32 engine in one process; Node exits
|
|
13
|
+
after launch. No Electron, Chromium, extra npm dependency or compiler is needed.
|
|
14
|
+
Windows10/11 x64 with Node20+ is required. Optional watch/brain or --engine node
|
|
15
|
+
keeps the existing Node/.NET compatibility path and its Framework4 compiler. Other
|
|
15
16
|
desktop platforms and exclusive fullscreen/security surfaces are not certified.
|
|
16
17
|
Launch reports
|
|
17
18
|
that the picker is ready, **not** that the user has selected a window or visually
|
|
@@ -35,8 +36,8 @@ approved the pet.
|
|
|
35
36
|
The picker locally reads window titles/process names; tracking reads window
|
|
36
37
|
geometry, foreground state, visibility and owning process ID.
|
|
37
38
|
- No automatic startup, background scheduled task, registry entry or MCP is added.
|
|
38
|
-
Exit stops
|
|
39
|
-
exits too. Losing the target hides the pet; tray selection can attach another.
|
|
39
|
+
Exit stops the native process. The compatibility path stops both painter and engine;
|
|
40
|
+
if either closes, its counterpart exits too. Losing the target hides the pet; tray selection can attach another.
|
|
40
41
|
|
|
41
42
|
## Stop, remove, and recover
|
|
42
43
|
|
|
@@ -52,7 +53,8 @@ One-command companion removal (stop, wait for exit, remove only its cache):
|
|
|
52
53
|
retrieviewer desktop-remove --yes
|
|
53
54
|
```
|
|
54
55
|
|
|
55
|
-
The
|
|
56
|
+
The control strip provides window selection, pause/resume and exit. The older
|
|
57
|
+
compatibility helper also has a tray menu. `desktop-stop`
|
|
56
58
|
asks the companion to exit through an authenticated local named pipe; it never
|
|
57
59
|
kills a PID from a stale file. The worker may terminate only its own stuck child
|
|
58
60
|
painter after a graceful timeout. Cache cleanup refuses a live/unreachable worker,
|
|
@@ -111,3 +113,10 @@ check (not total app memory or a4K performance claim). A Windows GUI startup bug
|
|
|
111
113
|
was fixed by building a GUI-subsystem helper and not applying SW_HIDE to it; the
|
|
112
114
|
real user picker was verified in Windows' visible main-window list. User visual
|
|
113
115
|
approval after selecting a terminal remains separate.
|
|
116
|
+
|
|
117
|
+
## Pet selection
|
|
118
|
+
|
|
119
|
+
Use retrieviewer desktop --pet cat, or select Ginger cat in the native picker
|
|
120
|
+
or control strip. Only one pet is loaded; the previous asset lease is disposed
|
|
121
|
+
on switch. Cat controls include greeting, treat, company, tower and wheel.
|
|
122
|
+
See [native runtime](NATIVE-RUNTIME.md) for measurements and scope.
|
package/braille.mjs
CHANGED
|
@@ -37,10 +37,10 @@ function stableBraille(rgba,width,height,brightness,contrast){
|
|
|
37
37
|
for(let y=0;y<rows;y++){for(let x=0;x<columns;x++)cells[x]=GLYPHS[masks[y*columns+x]];lines[y]=cells.join('');}
|
|
38
38
|
return{text:lines.join('\n'),columns,rows,dots};
|
|
39
39
|
}
|
|
40
|
-
export function rgbaToBraille(rgba,width,height,{brightness=1,contrast=1,method='floyd'}={}){
|
|
40
|
+
export function rgbaToBraille(rgba,width,height,{brightness=1,contrast=1,method='floyd',kernel=null}={}){
|
|
41
41
|
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');
|
|
42
42
|
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');
|
|
43
|
-
if(method==='stable')return stableBraille(rgba,width,height,brightness,contrast);
|
|
43
|
+
if(method==='stable')return kernel?.braille?kernel.braille(rgba,width,height,brightness,contrast):stableBraille(rgba,width,height,brightness,contrast);
|
|
44
44
|
const values=new Float32Array(width*height),bits=new Uint8Array(width*height);
|
|
45
45
|
for(let i=0;i<values.length;i++){
|
|
46
46
|
const p=i*4,luma=(.2126*rgba[p]+.7152*rgba[p+1]+.0722*rgba[p+2])/255*(rgba[p+3]/255);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
cmake_minimum_required(VERSION 3.21)
|
|
2
|
+
project(retrieviewer_native LANGUAGES C CXX)
|
|
3
|
+
set(CMAKE_CXX_STANDARD 17)
|
|
4
|
+
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
|
5
|
+
set(QJS_ENABLE_INSTALL OFF CACHE BOOL "" FORCE)
|
|
6
|
+
get_filename_component(PET_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../.." ABSOLUTE)
|
|
7
|
+
add_subdirectory("${PET_ROOT}/dist/native-memory/quickjs-src" quickjs-build EXCLUDE_FROM_ALL)
|
|
8
|
+
set(ZLIB_BUILD_TESTING OFF CACHE BOOL "" FORCE)
|
|
9
|
+
set(ZLIB_BUILD_SHARED OFF CACHE BOOL "" FORCE)
|
|
10
|
+
set(ZLIB_INSTALL OFF CACHE BOOL "" FORCE)
|
|
11
|
+
add_subdirectory("${PET_ROOT}/dist/native-memory/zlib-src" zlib-build EXCLUDE_FROM_ALL)
|
|
12
|
+
add_executable(retrieviewer-native host.cpp)
|
|
13
|
+
target_link_options(retrieviewer-native PRIVATE "/MANIFEST:EMBED" "/MANIFESTINPUT:${CMAKE_CURRENT_SOURCE_DIR}/application.manifest")
|
|
14
|
+
target_link_libraries(retrieviewer-native PRIVATE qjs qjs-libc zlibstatic gdiplus user32 gdi32 advapi32 psapi)
|
|
15
|
+
target_compile_definitions(retrieviewer-native PRIVATE _CRT_SECURE_NO_WARNINGS NOMINMAX UNICODE _UNICODE)
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
QuickJS-NG
|
|
2
|
+
|
|
3
|
+
The MIT License (MIT)
|
|
4
|
+
|
|
5
|
+
Copyright (c) 2017-2026 Fabrice Bellard
|
|
6
|
+
Copyright (c) 2017-2024 Charlie Gordon
|
|
7
|
+
Copyright (c) 2023-2026 Ben Noordhuis
|
|
8
|
+
Copyright (c) 2023-2026 Saúl Ibarra Corretgé
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in
|
|
18
|
+
all copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
23
|
+
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
26
|
+
THE SOFTWARE.
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
zlib
|
|
30
|
+
|
|
31
|
+
Copyright notice:
|
|
32
|
+
|
|
33
|
+
(C) 1995-2026 Jean-loup Gailly and Mark Adler
|
|
34
|
+
|
|
35
|
+
This software is provided 'as-is', without any express or implied
|
|
36
|
+
warranty. In no event will the authors be held liable for any damages
|
|
37
|
+
arising from the use of this software.
|
|
38
|
+
|
|
39
|
+
Permission is granted to anyone to use this software for any purpose,
|
|
40
|
+
including commercial applications, and to alter it and redistribute it
|
|
41
|
+
freely, subject to the following restrictions:
|
|
42
|
+
|
|
43
|
+
1. The origin of this software must not be misrepresented; you must not
|
|
44
|
+
claim that you wrote the original software. If you use this software
|
|
45
|
+
in a product, an acknowledgment in the product documentation would be
|
|
46
|
+
appreciated but is not required.
|
|
47
|
+
2. Altered source versions must be plainly marked as such, and must not be
|
|
48
|
+
misrepresented as being the original software.
|
|
49
|
+
3. This notice may not be removed or altered from any source distribution.
|
|
50
|
+
|
|
51
|
+
Jean-loup Gailly Mark Adler
|
|
52
|
+
jloup@gzip.org madler@alumni.caltech.edu
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
fflate
|
|
56
|
+
|
|
57
|
+
MIT License
|
|
58
|
+
|
|
59
|
+
Copyright (c) 2026 Arjun Barrett
|
|
60
|
+
|
|
61
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
62
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
63
|
+
in the Software without restriction, including without limitation the rights
|
|
64
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
65
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
66
|
+
furnished to do so, subject to the following conditions:
|
|
67
|
+
|
|
68
|
+
The above copyright notice and this permission notice shall be included in all
|
|
69
|
+
copies or substantial portions of the Software.
|
|
70
|
+
|
|
71
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
72
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
73
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
74
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
75
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
76
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
77
|
+
SOFTWARE.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
2
|
+
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
|
3
|
+
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
|
4
|
+
<windowsSettings>
|
|
5
|
+
<activeCodePage xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">UTF-8</activeCodePage>
|
|
6
|
+
</windowsSettings>
|
|
7
|
+
</application>
|
|
8
|
+
</assembly>
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// Short-lived bootstrap only. The native process owns the pet, UI and control pipe.
|
|
2
|
+
import {spawn} from 'node:child_process';
|
|
3
|
+
import {readFile,open,unlink,stat} from 'node:fs/promises';
|
|
4
|
+
import {createHash,randomBytes,randomUUID} from 'node:crypto';
|
|
5
|
+
import {fileURLToPath} from 'node:url';
|
|
6
|
+
import {prepareRoot,noLink,requestSession,readSession} from '../windows-overlay/desktop.mjs';
|
|
7
|
+
import {controlAddress} from '../desktop/platform.mjs';
|
|
8
|
+
const root=new URL('./',import.meta.url),bundle=new URL('./runtime.bundle.mjs',root),binary=new URL('./bin/win32-x64/retrieviewer-native.exe',root);
|
|
9
|
+
export async function verifyNativeArtifact(){
|
|
10
|
+
const manifest=JSON.parse(await readFile(new URL('manifest.json',root),'utf8'));
|
|
11
|
+
if(manifest.version!==1||manifest.platform!=='win32-x64')throw Error('Invalid native runtime manifest');
|
|
12
|
+
for(const name of ['runtime.bundle.mjs','bin/win32-x64/retrieviewer-native.exe']){
|
|
13
|
+
const expected=manifest.files[name],url=new URL(name,root),info=await stat(url);
|
|
14
|
+
if(!expected||info.size!==expected.bytes||info.size>16*1024*1024)throw Error('Native runtime size mismatch: '+name);
|
|
15
|
+
const bytes=await readFile(url);if(createHash('sha256').update(bytes).digest('hex')!==expected.sha256)throw Error('Native runtime checksum mismatch: '+name);
|
|
16
|
+
}
|
|
17
|
+
return manifest;
|
|
18
|
+
}
|
|
19
|
+
export async function switchNativePet(options,session,pet){
|
|
20
|
+
await requestSession(session,'pet-'+pet);
|
|
21
|
+
for(let i=0;i<40;i++){const status=await requestSession(session,'status');if(status.pet===pet)return {...status,alreadyRunning:true,switchedPet:true};await new Promise(r=>setTimeout(r,50));}
|
|
22
|
+
throw Error('The native companion did not finish switching pets');
|
|
23
|
+
}
|
|
24
|
+
export async function startNativeDesktop(options={}){
|
|
25
|
+
if(process.platform!=='win32'||process.arch!=='x64')throw Error('Native desktop currently requires Windows x64');
|
|
26
|
+
await verifyNativeArtifact();const paths=await prepareRoot(options);await noLink(paths.session);
|
|
27
|
+
const pipe=controlAddress({uuid:randomUUID()}),token=randomBytes(32).toString('hex');
|
|
28
|
+
const child=spawn(fileURLToPath(binary),[fileURLToPath(bundle),fileURLToPath(new URL('../../',root)),'--production'],{detached:true,windowsHide:true,stdio:['pipe','pipe','pipe'],env:{...process.env,OPENROUTER_API_KEY:undefined}});
|
|
29
|
+
let owns=false,complete=false,stderr='';const session={owner:'bitefoundry-retrieviewer-overlay-v1',pid:child.pid,pipe,token};
|
|
30
|
+
child.stderr.on('data',b=>stderr=(stderr+b.toString('utf8')).slice(-2048));child.stdin.on('error',()=>{});
|
|
31
|
+
try{
|
|
32
|
+
if(!Number.isInteger(child.pid))throw Error('Cannot start native companion');
|
|
33
|
+
const file=await open(paths.session,'wx',0o600);owns=true;try{await file.writeFile(JSON.stringify(session)+'\n');}finally{await file.close();}
|
|
34
|
+
await new Promise((resolve,reject)=>{
|
|
35
|
+
let buffer='',ready=false,settled=false;const finish=(error)=>{if(settled)return;settled=true;clearTimeout(timer);error?reject(error):resolve();};
|
|
36
|
+
const timer=setTimeout(()=>finish(Error('Native companion startup timed out')),15000);
|
|
37
|
+
child.once('error',error=>finish(error));child.once('exit',code=>finish(Error('Native companion exited before ready ('+code+'): '+stderr)));
|
|
38
|
+
child.stdout.on('data',bytes=>{buffer+=bytes.toString('utf8');if(buffer.length>65536)return finish(Error('Oversized native startup response'));let at;while((at=buffer.indexOf('\n'))>=0){const line=buffer.slice(0,at);buffer=buffer.slice(at+1);let message;try{message=JSON.parse(line);}catch{return finish(Error('Invalid native startup response'));}if(message.ready===true&&!ready){ready=true;child.stdin.end('DETACH\n');}else if(message.detached===true&&ready)finish();}});
|
|
39
|
+
const pet=options.pet??'dog';child.stdin.write(JSON.stringify({pet,seed:options.nativeDiagnostic?731:randomBytes(4).readUInt32LE(),ink:options.ink??(pet==='cat'?'gold':'cream'),inkExplicit:!!options.ink,returnStyle:options.returnStyle??'auto',diagnostic:options.nativeDiagnostic===true,testPicker:options.nativePickerTest===true,pipe,token,sessionFile:paths.session})+'\n');
|
|
40
|
+
});
|
|
41
|
+
const status=await requestSession(session,'status');complete=true;child.unref();child.stdout.destroy();child.stderr.destroy();return {...status,detached:true};
|
|
42
|
+
}finally{
|
|
43
|
+
if(!complete){child.stdin.destroy();if(child.exitCode===null)child.kill();if(owns){const current=await readSession(options).catch(()=>null);if(current?.token===token)await unlink(paths.session).catch(()=>{});}}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#include <quickjs.h>
|
|
2
|
+
#include <quickjs-libc.h>
|
|
3
|
+
#include <algorithm>
|
|
4
|
+
#include <cmath>
|
|
5
|
+
#include <cstdint>
|
|
6
|
+
#include <cstdio>
|
|
7
|
+
#include <fstream>
|
|
8
|
+
#include <string>
|
|
9
|
+
#include <vector>
|
|
10
|
+
#include <stdexcept>
|
|
11
|
+
#include <iostream>
|
|
12
|
+
#include <zlib.h>
|
|
13
|
+
#include "native-window.hpp"
|
|
14
|
+
|
|
15
|
+
struct Bytes { JSContext* ctx; JSValue owner; uint8_t* p; size_t n;
|
|
16
|
+
Bytes(JSContext* c,JSValueConst arg):ctx(c){size_t offset,bpe;owner=JS_GetTypedArrayBuffer(c,arg,&offset,&n,&bpe);if(JS_IsException(owner))throw std::runtime_error("Expected pixels");size_t all;p=JS_GetArrayBuffer(c,&all,owner)+offset;}
|
|
17
|
+
~Bytes(){JS_FreeValue(ctx,owner);}
|
|
18
|
+
};
|
|
19
|
+
static double number(JSContext* c,JSValueConst obj,const char* key){JSValue v=JS_GetPropertyStr(c,obj,key);double n;int result=JS_ToFloat64(c,&n,v);JS_FreeValue(c,v);if(result<0)throw std::runtime_error(key);return n;}
|
|
20
|
+
static double clamp(double n,double low,double high){return std::max(low,std::min(high,n));}
|
|
21
|
+
static uint8_t byteClamp(double v){if(v<=0)return 0;if(v>=255)return 255;double f=floor(v),d=v-f;return (uint8_t)(f+(d>.5||(d==.5&&((int)f&1))?1:0));}
|
|
22
|
+
static JSValue failure(JSContext* c,const std::exception& e){return JS_ThrowTypeError(c,"%s",e.what());}
|
|
23
|
+
static JSValue inflateGzip(JSContext* c,JSValueConst,int argc,JSValueConst* a){try{Bytes compressed(c,a[0]);int32_t size;JS_ToInt32(c,&size,a[1]);if(size<0||size>640*641)throw std::runtime_error("Inflate size");std::vector<uint8_t> out(std::max(1,size));z_stream z{};z.next_in=compressed.p;z.avail_in=(uInt)compressed.n;z.next_out=out.data();z.avail_out=(uInt)out.size();if(inflateInit2(&z,31)!=Z_OK)throw std::runtime_error("Inflate init");int result=inflate(&z,Z_FINISH);auto total=z.total_out;inflateEnd(&z);if(result!=Z_STREAM_END||total!=(uLong)size)throw std::runtime_error("Invalid crop gzip");JSValue buffer=JS_NewArrayBufferCopy(c,out.data(),size);JSValue typed=JS_NewTypedArray(c,1,&buffer,JS_TYPED_ARRAY_UINT8);JS_FreeValue(c,buffer);return typed;}catch(const std::exception& e){return failure(c,e);}}
|
|
24
|
+
static int paeth(int a,int b,int d){int p=a+b-d,aa=abs(p-a),bb=abs(p-b),dd=abs(p-d);return aa<=bb&&aa<=dd?a:bb<=dd?b:d;}
|
|
25
|
+
static JSValue decodeRows(JSContext* c,JSValueConst,int argc,JSValueConst* a){try{Bytes out(c,a[0]),rows(c,a[1]);int32_t x,y,w,h,width;JS_ToInt32(c,&x,a[2]);JS_ToInt32(c,&y,a[3]);JS_ToInt32(c,&w,a[4]);JS_ToInt32(c,&h,a[5]);JS_ToInt32(c,&width,a[6]);if(x<0||y<0||w<0||h<0||width<1||x+w>width||rows.n!=(size_t)h*(w+1)||((size_t)y+h)*width>out.n)throw std::runtime_error("Decode rows bounds");std::fill(out.p,out.p+out.n,0);std::vector<uint8_t> prev(w,0),cur(w,0);size_t at=0;for(int r=0;r<h;r++){int type=rows.p[at++];if(type>4)throw std::runtime_error("Filter");for(int col=0;col<w;col++){int left=col?cur[col-1]:0,up=prev[col],diagonal=col?prev[col-1]:0,p=type==0?0:type==1?left:type==2?up:type==3?(left+up)>>1:paeth(left,up,diagonal),q=(rows.p[at++]+p)&255;if(q>31)throw std::runtime_error("Q5 pixel");cur[col]=(uint8_t)q;out.p[((size_t)y+r)*width+x+col]=(uint8_t)floor(q*255/31.0+.5);}std::swap(cur,prev);}return JS_UNDEFINED;}catch(const std::exception& e){return failure(c,e);}}
|
|
26
|
+
static JSValue clearPixels(JSContext* c,JSValueConst,int argc,JSValueConst* a){try{Bytes out(c,a[0]);std::fill(out.p,out.p+out.n,0);for(size_t i=3;i<out.n;i+=4)out.p[i]=255;return JS_UNDEFINED;}catch(const std::exception& e){return failure(c,e);}}
|
|
27
|
+
static JSValue copyGray(JSContext* c,JSValueConst,int argc,JSValueConst* a){try{Bytes out(c,a[0]),gray(c,a[1]);if(out.n<gray.n*4)throw std::runtime_error("Gray size");for(size_t i=0,j=0;i<gray.n;i++,j+=4)out.p[j]=out.p[j+1]=out.p[j+2]=gray.p[i];return JS_UNDEFINED;}catch(const std::exception& e){return failure(c,e);}}
|
|
28
|
+
static JSValue blendGray(JSContext* c,JSValueConst,int argc,JSValueConst* a){try{Bytes out(c,a[0]),x(c,a[1]),y(c,a[2]);double t;JS_ToFloat64(c,&t,a[3]);if(x.n!=y.n||out.n<x.n*4)throw std::runtime_error("Blend size");for(size_t i=0,j=0;i<x.n;i++,j+=4){const auto v=byteClamp(x.p[i]*(1-t)+y.p[i]*t);out.p[j]=out.p[j+1]=out.p[j+2]=v;}return JS_UNDEFINED;}catch(const std::exception& e){return failure(c,e);}}
|
|
29
|
+
static JSValue catBlit(JSContext* c,JSValueConst,int argc,JSValueConst* a){try{
|
|
30
|
+
Bytes out(c,a[0]),gray(c,a[1]);const auto opts=a[2];int width=(int)number(c,opts,"width"),height=(int)number(c,opts,"height");double px=number(c,opts,"x"),py=number(c,opts,"y"),size=number(c,opts,"size");JSValue mirrored=JS_GetPropertyStr(c,opts,"mirror");bool mirror=JS_ToBool(c,mirrored);JS_FreeValue(c,mirrored);
|
|
31
|
+
if(width<1||height<1||(int64_t)width*height>1000000||out.n!=(size_t)width*height*4||gray.n!=320*320||!std::isfinite(size)||size<=0)throw std::runtime_error("Cat raster bounds");
|
|
32
|
+
struct Pin{double sx,sy,dx,dy,radius;};std::vector<Pin> pins;JSValue list=JS_GetPropertyStr(c,opts,"pins"),len=JS_GetPropertyStr(c,list,"length");uint32_t count;JS_ToUint32(c,&count,len);JS_FreeValue(c,len);if(count>16)throw std::runtime_error("Cat pin limit");for(uint32_t i=0;i<count;i++){JSValue p=JS_GetPropertyUint32(c,list,i),s=JS_GetPropertyStr(c,p,"source"),t=JS_GetPropertyStr(c,p,"target");double radius=number(c,p,"radius");if(!std::isfinite(radius))radius=28;Pin pin{number(c,s,"x"),number(c,s,"y"),number(c,t,"x")-number(c,s,"x"),number(c,t,"y")-number(c,s,"y"),radius};JS_FreeValue(c,s);JS_FreeValue(c,t);JS_FreeValue(c,p);pins.push_back(pin);}JS_FreeValue(c,list);
|
|
33
|
+
double left=px-size*.5,top=py-size*(290.0/320);int x0=std::max(0,(int)floor(left)),y0=std::max(0,(int)floor(top)),x1=std::min(width,(int)ceil(left+size)),y1=std::min(height,(int)ceil(top+size));
|
|
34
|
+
for(int y=y0;y<y1;y++)for(int x=x0;x<x1;x++){double u=(x+.5-left)*320/size-.5,v=(y+.5-top)*320/size-.5;if(mirror)u=319-u;if(v>225)for(const auto& p:pins){if(p.radius<=0||hypot(p.dx,p.dy)>std::min(6.0,.22*p.radius)||fabs(p.dy)>3||hypot(u-p.sx,v-p.sy)>=p.radius)continue;double q=u-p.dx,r=v-p.dy;for(int i=0;i<12;i++){double distance=hypot(q-p.sx,r-p.sy),t=clamp((distance-p.radius*.3)/(p.radius*.7),0,1),weight=1-t*t*(3-2*t);q=u-p.dx*weight;r=v-p.dy*weight;}u=q;v=r;break;}if(u<0||v<0||u>319||v>319)continue;int ax=(int)floor(u),by=(int)floor(v),aa=std::min(319,ax+1),bb=std::min(319,by+1);double fu=u-ax,fv=v-by,value=(gray.p[by*320+ax]*(1-fu)+gray.p[by*320+aa]*fu)*(1-fv)+(gray.p[bb*320+ax]*(1-fu)+gray.p[bb*320+aa]*fu)*fv;auto b=(uint8_t)floor(value+.5);size_t at=((size_t)y*width+x)*4;out.p[at]=out.p[at+1]=out.p[at+2]=b;}
|
|
35
|
+
return JS_UNDEFINED;
|
|
36
|
+
}catch(const std::exception& e){return failure(c,e);}}
|
|
37
|
+
static JSValue braille(JSContext* c,JSValueConst,int argc,JSValueConst* a){try{
|
|
38
|
+
Bytes pixels(c,a[0]);int32_t width,height;double brightness,contrast;JS_ToInt32(c,&width,a[1]);JS_ToInt32(c,&height,a[2]);JS_ToFloat64(c,&brightness,a[3]);JS_ToFloat64(c,&contrast,a[4]);
|
|
39
|
+
if(width<1||height<1||(int64_t)width*height>1000000||pixels.n!=(size_t)width*height*4)throw std::runtime_error("Braille dimensions");
|
|
40
|
+
static float table[256];static double lastB=0,lastC=0;if(lastB!=brightness||lastC!=contrast){for(int i=0;i<256;i++){double l=(.2126*i+.7152*i+.0722*i)/255;table[i]=(float)(l==0?0:clamp((pow(l,.9)*brightness-.5)*contrast+.5,0,1));}lastB=brightness;lastC=contrast;}
|
|
41
|
+
const int cols=(width+1)/2,rows=(height+3)/4;static const int bits[4][2]={{1,8},{2,16},{4,32},{64,128}};std::vector<uint8_t> masks(cols*rows,0);int dots=0;
|
|
42
|
+
for(int y=0;y<height;y++)for(int x=0;x<width;x++){auto* p=pixels.p+((size_t)y*width+x)*4;int r=p[0],g=p[1],b=p[2],alpha=p[3];if(alpha==0||!(r|g|b))continue;float value;
|
|
43
|
+
if(alpha==255&&r==g&&g==b)value=table[r];else{double l=(.2126*r+.7152*g+.0722*b)/255*(alpha/255.0);value=(float)(l==0?0:clamp((pow(l,.9)*brightness-.5)*contrast+.5,0,1));}if(value<=0)continue;
|
|
44
|
+
uint32_t hash=(uint32_t)(x+1)*0x1f123bb5u^(uint32_t)(y+1)*0x5f356495u;hash=(hash^(hash>>16))*0x45d9f3bu;hash=(hash^(hash>>16))*0x45d9f3bu;hash^=hash>>16;
|
|
45
|
+
if(value>(hash+.5)/4294967296.0){masks[(y>>2)*cols+(x>>1)]|=bits[y&3][x&1];dots++;}
|
|
46
|
+
}
|
|
47
|
+
std::string text;text.reserve((size_t)cols*rows*3+rows);for(int y=0;y<rows;y++){if(y)text+='\n';for(int x=0;x<cols;x++){uint32_t ch=0x2800+masks[y*cols+x];text+=(char)(0xe0|(ch>>12));text+=(char)(0x80|((ch>>6)&63));text+=(char)(0x80|(ch&63));}}
|
|
48
|
+
JSValue out=JS_NewObject(c);JS_SetPropertyStr(c,out,"text",JS_NewStringLen(c,text.data(),text.size()));JS_SetPropertyStr(c,out,"columns",JS_NewInt32(c,cols));JS_SetPropertyStr(c,out,"rows",JS_NewInt32(c,rows));JS_SetPropertyStr(c,out,"dots",JS_NewInt32(c,dots));return out;
|
|
49
|
+
}catch(const std::exception& e){return failure(c,e);}}
|
|
50
|
+
struct Layer {double lo,hi,dx,dy,toe,span,a,lastY=NAN,lastSource=NAN;};
|
|
51
|
+
static double smooth(double t){return t<=0?0:t>=1?1:t*t*(3-2*t);}
|
|
52
|
+
static double gy(const Layer& l,double s){double t=(s-226)/l.span;return t<=0?0:t>=1?1:(1-l.a)*smooth(t)+l.a*t;}
|
|
53
|
+
static double inverseY(Layer& l,double y){if(l.dy==0)return y;if(y>=l.toe+l.dy)return y-l.dy;if(y==l.lastY)return l.lastSource;double lo=226,hi=l.toe;for(int i=0;i<48&&hi-lo>1e-9;i++){double m=(lo+hi)/2;if(m+l.dy*gy(l,m)<y)lo=m;else hi=m;}l.lastY=y;l.lastSource=(lo+hi)/2;return l.lastSource;}
|
|
54
|
+
static JSValue blit(JSContext* c,JSValueConst,int argc,JSValueConst* a){try{
|
|
55
|
+
Bytes output(c,a[0]),image(c,a[1]);JSValueConst opts=a[2];const int width=(int)number(c,opts,"width"),height=(int)number(c,opts,"height"),sourceN=(int)number(c,opts,"sourceN");
|
|
56
|
+
const double left=number(c,opts,"left"),top=number(c,opts,"top"),side=number(c,opts,"side"),sideX=number(c,opts,"sideX"),originX=number(c,opts,"originX"),originY=number(c,opts,"originY"),drawTop=number(c,opts,"drawTop"),bottom=number(c,opts,"bottom"),drawLeft=number(c,opts,"drawLeft"),drawRight=number(c,opts,"drawRight");
|
|
57
|
+
JSValue flipValue=JS_GetPropertyStr(c,opts,"flip");bool flip=JS_ToBool(c,flipValue);JS_FreeValue(c,flipValue);
|
|
58
|
+
if(width<1||height<1||(int64_t)width*height>1000000||sourceN<1||sourceN>640||output.n!=(size_t)width*height*4||image.n<(size_t)sourceN*sourceN*4||side<=0||sideX<=0)throw std::runtime_error("Blit dimensions");
|
|
59
|
+
JSValue layersValue=JS_GetPropertyStr(c,opts,"layers"),length=JS_GetPropertyStr(c,layersValue,"length");uint32_t count;JS_ToUint32(c,&count,length);JS_FreeValue(c,length);if(count>16)throw std::runtime_error("Layer limit");std::vector<Layer> layers;
|
|
60
|
+
for(uint32_t i=0;i<count;i++){JSValue v=JS_GetPropertyUint32(c,layersValue,i);Layer l;l.lo=number(c,v,"lo");l.hi=number(c,v,"hi");l.dx=number(c,v,"dx");l.dy=number(c,v,"dy");l.toe=number(c,v,"toeTop");l.span=l.toe-226;l.a=0;if(l.dy<0){double k=.9*l.span/-l.dy;if(k<1)l.a=1;else if(k<1.5)l.a=std::min(1.0,3-2*k);}JS_FreeValue(c,v);layers.push_back(l);}JS_FreeValue(c,layersValue);
|
|
61
|
+
const bool minify=side<320||sideX<320;int pitch=sourceN+1;std::vector<uint32_t> sums;
|
|
62
|
+
if(minify){sums.assign(pitch*pitch,0);for(int y=0;y<sourceN;y++){uint32_t row=0;for(int x=0;x<sourceN;x++){row+=image.p[((size_t)y*sourceN+x)*4];sums[(y+1)*pitch+x+1]=sums[y*pitch+x+1]+row;}}}
|
|
63
|
+
auto integral=[&](double x,double y){x=clamp(x,0,sourceN);y=clamp(y,0,sourceN);int ix=std::min(sourceN-1,(int)floor(x)),iy=std::min(sourceN-1,(int)floor(y));double fx=x-ix,fy=y-iy;size_t k=iy*pitch+ix;return (sums[k]*(1-fx)+sums[k+1]*fx)*(1-fy)+(sums[k+pitch]*(1-fx)+sums[k+pitch+1]*fx)*fy;};
|
|
64
|
+
auto area=[&](double x,double y){double dx=320/sideX,dy=320/side,l=x-dx/2,r=x+dx/2,t=y-dy/2,b=y+dy/2;return std::max(0.0,(integral(r,b)-integral(l,b)-integral(r,t)+integral(l,t))/(dx*dy));};
|
|
65
|
+
auto opaque=[&](double x,double y){double xx=x+originX,yy=y+originY;if(xx<0||xx>=sourceN||yy<0||yy>=sourceN)return false;int sx=(int)floor(xx);if(flip)sx=sourceN-1-sx;return image.p[((size_t)floor(yy)*sourceN+sx)*4]>16;};
|
|
66
|
+
const int y0=std::max(0,(int)(minify?floor(drawTop):ceil(drawTop-.5))),y1=std::min(height,(int)(minify?ceil(bottom):ceil(bottom-.5))),x0=std::max(0,(int)(minify?floor(drawLeft):ceil(drawLeft-.5))),x1=std::min(width,(int)(minify?ceil(drawRight):ceil(drawRight-.5)));
|
|
67
|
+
for(int y=y0;y<y1;y++)for(int x=x0;x<x1;x++){
|
|
68
|
+
double px=(x+.5-left)/sideX*320,py=(y+.5-top)/side*320,wx=px,wy=py;bool claimed=true;
|
|
69
|
+
if(py>=226&&!layers.empty()){claimed=false;bool fallback=false;double fallbackX=0,fallbackY=0;for(auto& l:layers){double sx,sy;if(l.dx==0&&l.dy==0){sx=px;sy=py;}else{sy=inverseY(l,py);sx=sy>=l.toe?px-l.dx:px-l.dx*smooth((sy-226)/l.span);}if(!(sx>=l.lo&&sx<l.hi))continue;if(opaque(sx,sy)){wx=sx;wy=sy;claimed=true;break;}if(!fallback){fallback=true;fallbackX=sx;fallbackY=sy;}}if(!claimed&&fallback){wx=fallbackX;wy=fallbackY;claimed=true;}}
|
|
70
|
+
if(!claimed)continue;double rawX=wx+originX,rawY=wy+originY;int sx=(int)clamp(floor(rawX),0,sourceN-1),sy=(int)clamp(floor(rawY),0,sourceN-1);if(flip)sx=sourceN-1-sx;size_t j=((size_t)y*width+x)*4,k=((size_t)sy*sourceN+sx)*4;
|
|
71
|
+
uint8_t value=byteClamp(minify?area(flip?sourceN-rawX:rawX,rawY):(rawY<0||rawY>=sourceN||rawX<0||rawX>=sourceN?0:image.p[k]));output.p[j]=output.p[j+1]=output.p[j+2]=value;
|
|
72
|
+
}
|
|
73
|
+
return JS_UNDEFINED;
|
|
74
|
+
}catch(const std::exception& e){return failure(c,e);}}
|
|
75
|
+
int wmain(int argc,wchar_t** wideArgs){
|
|
76
|
+
std::vector<std::string> argText;for(int i=0;i<argc;i++){int n=WideCharToMultiByte(CP_UTF8,0,wideArgs[i],-1,nullptr,0,nullptr,nullptr);std::string value(n,0);WideCharToMultiByte(CP_UTF8,0,wideArgs[i],-1,value.data(),n,nullptr,nullptr);value.pop_back();argText.push_back(value);}std::vector<char*> args;for(auto& text:argText)args.push_back(text.data());char** argv=args.data();
|
|
77
|
+
bool production=false;for(int i=1;i<argc;i++)if(std::string(argv[i])=="--production")production=true;std::string configuration;
|
|
78
|
+
if(production){char ch;while(std::cin.get(ch)&&ch!='\n'){if(configuration.size()>=8192)return 2;configuration+=ch;}if(configuration.empty())return 2;}
|
|
79
|
+
if(argc<2)return 2;JSRuntime* rt=JS_NewRuntime();js_std_init_handlers(rt);JSContext* ctx=JS_NewContext(rt);JS_SetModuleLoaderFunc2(rt,nullptr,js_module_loader,js_module_check_attributes,nullptr);js_init_module_std(ctx,"qjs:std");js_init_module_os(ctx,"qjs:os");js_std_add_helpers(ctx,argc-1,argv+1);
|
|
80
|
+
JSValue global=JS_GetGlobalObject(ctx),fast=JS_NewObject(ctx);if(production){JSValue config=JS_ParseJSON(ctx,configuration.data(),configuration.size(),"native-config");if(JS_IsException(config)){js_std_dump_error(ctx);return 2;}JS_SetPropertyStr(ctx,global,"__nativeConfig",config);}JS_SetPropertyStr(ctx,fast,"catBlit",JS_NewCFunction(ctx,catBlit,"catBlit",3));JS_SetPropertyStr(ctx,fast,"inflate",JS_NewCFunction(ctx,inflateGzip,"inflate",2));JS_SetPropertyStr(ctx,fast,"decodeRows",JS_NewCFunction(ctx,decodeRows,"decodeRows",7));JS_SetPropertyStr(ctx,fast,"clear",JS_NewCFunction(ctx,clearPixels,"clear",1));JS_SetPropertyStr(ctx,fast,"copyGray",JS_NewCFunction(ctx,copyGray,"copyGray",2));JS_SetPropertyStr(ctx,fast,"blendGray",JS_NewCFunction(ctx,blendGray,"blendGray",4));JS_SetPropertyStr(ctx,fast,"braille",JS_NewCFunction(ctx,braille,"braille",5));JS_SetPropertyStr(ctx,fast,"blit",JS_NewCFunction(ctx,blit,"blit",3));JS_SetPropertyStr(ctx,global,"__fast",fast);JS_FreeValue(ctx,global);
|
|
81
|
+
std::ifstream input(std::filesystem::u8path(argv[1]),std::ios::binary);if(!input){fprintf(stderr,"Native runtime bundle is missing\n");return 2;}std::string source((std::istreambuf_iterator<char>(input)),std::istreambuf_iterator<char>());JSValue value=JS_Eval(ctx,source.data(),source.size(),argv[1],JS_EVAL_TYPE_MODULE);value=js_std_await(ctx,value);int code=0;if(JS_IsException(value)){js_std_dump_error(ctx);code=1;}JS_FreeValue(ctx,value);if(!code)js_std_loop(ctx);
|
|
82
|
+
global=JS_GetGlobalObject(ctx);JSValue desktop=JS_GetPropertyStr(ctx,global,"__desktopReady");bool runWindow=JS_ToBool(ctx,desktop);JS_FreeValue(ctx,desktop);JS_FreeValue(ctx,global);if(!code&&runWindow)try{code=NativePet::run(ctx,argc,argv);}catch(const std::exception& e){fprintf(stderr,"%s\n",e.what());code=1;}
|
|
83
|
+
js_std_free_handlers(rt);JS_FreeContext(ctx);JS_FreeRuntime(rt);return code;
|
|
84
|
+
}
|