@bitefoundry/retrieviewer 0.0.16 → 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.
Files changed (83) hide show
  1. package/ASSET-PROVENANCE.md +11 -0
  2. package/ASSETS-LICENSE.md +6 -0
  3. package/ATTRIBUTION.md +8 -0
  4. package/DESKTOP-PLATFORMS.md +1 -1
  5. package/EMBEDDING.md +15 -9
  6. package/INSTALLING.md +2 -1
  7. package/LICENSE +16 -16
  8. package/NATIVE-RUNTIME.md +89 -0
  9. package/PREREQUISITES.md +7 -0
  10. package/PUPPY-MEMORY.md +70 -0
  11. package/README.ko.md +13 -1
  12. package/README.md +14 -1
  13. package/RELEASING.md +36 -1
  14. package/THIRD-PARTY-NOTICES.md +8 -0
  15. package/WINDOWS-COMPANION.md +15 -6
  16. package/braille.mjs +2 -2
  17. package/integrations/native-windows/CMakeLists.txt +15 -0
  18. package/integrations/native-windows/THIRD-PARTY-LICENSES.txt +77 -0
  19. package/integrations/native-windows/application.manifest +8 -0
  20. package/integrations/native-windows/bin/win32-x64/retrieviewer-native.exe +0 -0
  21. package/integrations/native-windows/desktop.mjs +45 -0
  22. package/integrations/native-windows/host.cpp +84 -0
  23. package/integrations/native-windows/manifest.json +14 -0
  24. package/integrations/native-windows/native-control.hpp +41 -0
  25. package/integrations/native-windows/native-window.hpp +128 -0
  26. package/integrations/native-windows/runtime.bundle.mjs +4694 -0
  27. package/integrations/native-windows/runtime.mjs +55 -0
  28. package/integrations/native-windows/sources.json +23 -0
  29. package/integrations/purrviewer/ART.md +289 -0
  30. package/integrations/purrviewer/ASSETS-LICENSE.md +21 -0
  31. package/integrations/purrviewer/LICENSE +24 -0
  32. package/integrations/purrviewer/art/ginger.bin.gz +0 -0
  33. package/integrations/purrviewer/art/ginger.json +1707 -0
  34. package/integrations/purrviewer/art/ginger.rvz +0 -0
  35. package/integrations/purrviewer/src/contact.mjs +63 -0
  36. package/integrations/purrviewer/src/habitat.mjs +41 -0
  37. package/integrations/purrviewer/src/life.mjs +280 -0
  38. package/integrations/purrviewer/src/purrviewer-core.mjs +130 -0
  39. package/integrations/purrviewer/src/purrviewer.mjs +37 -0
  40. package/integrations/purrviewer/src/walk-cycle.mjs +3 -0
  41. package/integrations/purrviewer/src/world.mjs +216 -0
  42. package/integrations/purrviewer/vendor/retrieviewer/LICENSE-CODE +21 -0
  43. package/integrations/purrviewer/vendor/retrieviewer/braille.mjs +73 -0
  44. package/integrations/purrviewer/vendor/retrieviewer/puppy-frame-file.mjs +15 -0
  45. package/integrations/purrviewer/vendor/retrieviewer/puppy-frame-indexed.mjs +78 -0
  46. package/integrations/purrviewer/vendor/retrieviewer/puppy-frame-predictive.mjs +170 -0
  47. package/integrations/purrviewer/vendor/retrieviewer/puppy-frame-store.mjs +114 -0
  48. package/integrations/windows-overlay/desktop.mjs +11 -3
  49. package/integrations/windows-overlay/worker.mjs +9 -2
  50. package/package.json +53 -13
  51. package/pet-indexed-manifest.json +106 -0
  52. package/puppy-clean.mjs +2 -2
  53. package/puppy-companion-comfort-q5.rvz +0 -0
  54. package/puppy-companion-interactions-v3-q5.rvz +0 -0
  55. package/puppy-companion-sprites.mjs +2 -2
  56. package/puppy-frame-file.mjs +15 -0
  57. package/puppy-frame-indexed.mjs +78 -0
  58. package/puppy-frame-predictive.mjs +1 -1
  59. package/puppy-frame-store.mjs +6 -0
  60. package/puppy-paper-actions-v9-q5.rvz +0 -0
  61. package/puppy-paper-actions.mjs +2 -2
  62. package/puppy-paper-approach-q5.rvz +0 -0
  63. package/puppy-paper-arrival-seat-q5.rvz +0 -0
  64. package/puppy-paper-behaviors-q5.rvz +0 -0
  65. package/puppy-paper-behaviors.mjs +1 -1
  66. package/puppy-paper-contact.mjs +2 -2
  67. package/puppy-paper-rear-gallop-q5.rvz +0 -0
  68. package/puppy-paper-seat-q5.rvz +0 -0
  69. package/puppy-paper-seat.mjs +2 -2
  70. package/puppy-paper.mjs +12 -12
  71. package/puppy-stride-tweens-q5.rvz +0 -0
  72. package/retrieviewer-cli.mjs +10 -6
  73. package/retrieviewer.d.mts +1 -1
  74. package/retrieviewer.mjs +35 -14
  75. package/puppy-companion-comfort-q5.bin.gz +0 -0
  76. package/puppy-companion-interactions-v3-q5.bin.gz +0 -0
  77. package/puppy-paper-actions-v9-q5.bin.gz +0 -0
  78. package/puppy-paper-approach-q5.bin.gz +0 -0
  79. package/puppy-paper-arrival-seat-q5.bin.gz +0 -0
  80. package/puppy-paper-behaviors-q5.bin.gz +0 -0
  81. package/puppy-paper-rear-gallop-q5.bin.gz +0 -0
  82. package/puppy-paper-seat-q5.bin.gz +0 -0
  83. package/puppy-stride-tweens-q5.bin.gz +0 -0
@@ -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
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "version": 1,
3
+ "platform": "win32-x64",
4
+ "files": {
5
+ "runtime.bundle.mjs": {
6
+ "bytes": 235608,
7
+ "sha256": "e9da942dbe57ac778c44ea8f300e2e868face5e73b00ffcd4399aeb7e94ee09c"
8
+ },
9
+ "bin/win32-x64/retrieviewer-native.exe": {
10
+ "bytes": 1551872,
11
+ "sha256": "317841c3a61f78ee57e56b9eb7f778d59d17ee6a3ab49e04eab37a40509799c1"
12
+ }
13
+ }
14
+ }
@@ -0,0 +1,41 @@
1
+ #include <atomic>
2
+ #include <mutex>
3
+ #include <thread>
4
+ #include <sstream>
5
+ #include <fstream>
6
+ #include <filesystem>
7
+ #include <sddl.h>
8
+ #include <psapi.h>
9
+ #include <functional>
10
+
11
+ static std::wstring wide(const std::string& text){if(text.empty())return L"";int n=MultiByteToWideChar(CP_UTF8,MB_ERR_INVALID_CHARS,text.data(),(int)text.size(),nullptr,0);if(!n)throw std::runtime_error("Invalid UTF-8");std::wstring result(n,0);MultiByteToWideChar(CP_UTF8,MB_ERR_INVALID_CHARS,text.data(),(int)text.size(),result.data(),n);return result;}
12
+ static std::string userSid(){HANDLE token;if(!OpenProcessToken(GetCurrentProcess(),TOKEN_QUERY,&token))throw std::runtime_error("Cannot read process owner");DWORD n=0;GetTokenInformation(token,TokenUser,nullptr,0,&n);std::vector<uint8_t> data(n);BOOL ok=GetTokenInformation(token,TokenUser,data.data(),n,&n);CloseHandle(token);if(!ok)throw std::runtime_error("Cannot read process owner");LPWSTR sid;if(!ConvertSidToStringSidW(((TOKEN_USER*)data.data())->User.Sid,&sid))throw std::runtime_error("Cannot read process owner");std::wstring s(sid);LocalFree(sid);return std::string(s.begin(),s.end());}
13
+ static std::string propertyString(JSContext* ctx,JSValueConst object,const char* name,const std::string& fallback=""){JSValue value=JS_GetPropertyStr(ctx,object,name);if(JS_IsUndefined(value)||JS_IsNull(value)){JS_FreeValue(ctx,value);return fallback;}const char* text=JS_ToCString(ctx,value);if(!text){JS_FreeValue(ctx,value);throw std::runtime_error(name);}std::string out(text);JS_FreeCString(ctx,text);JS_FreeValue(ctx,value);return out;}
14
+ static bool propertyBool(JSContext* ctx,JSValueConst object,const char* name){JSValue value=JS_GetPropertyStr(ctx,object,name);bool out=JS_ToBool(ctx,value);JS_FreeValue(ctx,value);return out;}
15
+ class NativeControl {
16
+ std::thread worker;std::atomic<bool> alive{false};std::mutex mutex;std::string status,token,session;std::wstring name;HANDLE pipe=INVALID_HANDLE_VALUE;PSECURITY_DESCRIPTOR security=nullptr;std::function<void(const std::string&)> action;
17
+ void serve(){
18
+ while(alive){BOOL connected=ConnectNamedPipe(pipe,nullptr);if(!connected&&GetLastError()!=ERROR_PIPE_CONNECTED){if(!alive)break;continue;}
19
+ std::string body;const ULONGLONG deadline=GetTickCount64()+1800;
20
+ while(alive&&body.size()<=2048&&GetTickCount64()<deadline){DWORD available=0;if(!PeekNamedPipe(pipe,nullptr,0,nullptr,&available,nullptr))break;if(!available){Sleep(10);continue;}char bytes[2048];DWORD got=0;if(!ReadFile(pipe,bytes,std::min<DWORD>(available,sizeof(bytes)),&got,nullptr)||!got)break;body.append(bytes,got);if(body.find('\n')!=std::string::npos)break;}
21
+ const std::string prefix="{\"token\":\""+token+"\",\"command\":\"";std::string response;
22
+ std::string command;for(const auto* choice:{"stop","pet-dog","pet-cat"})if(body==prefix+choice+"\"}\n")command=choice;
23
+ if(body==prefix+"status\"}\n"){std::lock_guard<std::mutex> lock(mutex);response=status+"\n";}
24
+ else if(!command.empty())response=command=="stop"?"{\"stopped\":true}\n":"{\"accepted\":true}\n";
25
+ if(!response.empty()){DWORD wrote=0;WriteFile(pipe,response.data(),(DWORD)response.size(),&wrote,nullptr);if(!command.empty())action(command);FlushFileBuffers(pipe);}
26
+ DisconnectNamedPipe(pipe);
27
+ }
28
+ }
29
+ public:
30
+ void start(const std::string& address,const std::string& secret,const std::string& file,std::function<void(const std::string&)> callback){
31
+ if(secret.size()!=64||secret.find_first_not_of("0123456789abcdef")!=std::string::npos||address.rfind("\\\\.\\pipe\\retrieviewer-overlay-",0)!=0)throw std::runtime_error("Invalid native control configuration");
32
+ token=secret;name=wide(address);session=file;action=callback;std::wstring sddl=wide("D:P(A;;GA;;;"+userSid()+")");if(!ConvertStringSecurityDescriptorToSecurityDescriptorW(sddl.c_str(),SDDL_REVISION_1,&security,nullptr))throw std::runtime_error("Cannot protect native control pipe");SECURITY_ATTRIBUTES attributes{sizeof(SECURITY_ATTRIBUTES),security,FALSE};
33
+ pipe=CreateNamedPipeW(name.c_str(),PIPE_ACCESS_DUPLEX|FILE_FLAG_FIRST_PIPE_INSTANCE,PIPE_TYPE_BYTE|PIPE_READMODE_BYTE|PIPE_WAIT|PIPE_REJECT_REMOTE_CLIENTS,1,8192,4096,0,&attributes);if(pipe==INVALID_HANDLE_VALUE)throw std::runtime_error("Cannot open native control pipe");alive=true;worker=std::thread([this]{serve();});
34
+ }
35
+ void update(const std::string& value){std::lock_guard<std::mutex> lock(mutex);status=value;}
36
+ void close(){if(alive.exchange(false)){if(worker.joinable()){CancelSynchronousIo(worker.native_handle());HANDLE wake=CreateFileW(name.c_str(),GENERIC_READ|GENERIC_WRITE,0,nullptr,OPEN_EXISTING,0,nullptr);if(wake!=INVALID_HANDLE_VALUE)CloseHandle(wake);worker.join();}if(pipe!=INVALID_HANDLE_VALUE){CloseHandle(pipe);pipe=INVALID_HANDLE_VALUE;}}
37
+ if(security){LocalFree(security);security=nullptr;}
38
+ if(!session.empty()){auto path=std::filesystem::u8path(session);DWORD attr=GetFileAttributesW(path.c_str()),parent=GetFileAttributesW(path.parent_path().c_str());if(attr!=INVALID_FILE_ATTRIBUTES&&parent!=INVALID_FILE_ATTRIBUTES&&!(attr&FILE_ATTRIBUTE_REPARSE_POINT)&&!(parent&FILE_ATTRIBUTE_REPARSE_POINT)){std::ifstream file(path,std::ios::binary);std::string bytes((std::istreambuf_iterator<char>(file)),std::istreambuf_iterator<char>());file.close();if(bytes.size()<8192&&bytes.find("\"token\":\""+token+"\"")!=std::string::npos)DeleteFileW(path.c_str());}session.clear();}
39
+ }
40
+ ~NativeControl(){close();}
41
+ };
@@ -0,0 +1,128 @@
1
+ #include <windows.h>
2
+ #include <gdiplus.h>
3
+ #include <memory>
4
+ #include "native-control.hpp"
5
+ namespace NativePet {
6
+ static JSContext* context;
7
+ static HWND target=nullptr,overlay=nullptr;
8
+ static bool showing=false,paused=false,failed=false;
9
+ static ULONGLONG startTime=0,lastTick=0,durationMs=0;
10
+ static int stage=0,painted=0;
11
+ static std::string previous;
12
+ static std::vector<double> timings;
13
+ static bool saveProof=false,verifyAtlas=false,proofSaved=false,atlasVerified=false;
14
+ static bool production=false,diagnostic=false,testPicker=false,visible=false,inkExplicit=false;
15
+ static HWND followed=nullptr,picker=nullptr,windowList=nullptr;
16
+ static DWORD followedPid=0;
17
+ static HANDLE instanceMutex=nullptr;
18
+ static NativeControl control;
19
+ static std::thread startupGuard;
20
+ static std::atomic<bool> detached{false};
21
+ static std::string petKind="dog",inkName="cream",returnStyle="auto",lastKind;
22
+ static bool lastAccepted=false;
23
+ static uint64_t frames=0;static size_t timingCursor=0;
24
+ static std::vector<std::pair<HWND,DWORD>> choices;
25
+ static POINT lastCanvas{INT_MIN,INT_MIN};static int lastWidth=0,lastHeight=0;
26
+ static Gdiplus::Color ink(){return inkName=="dark"?Gdiplus::Color(245,67,48,24):inkName=="gold"?(petKind=="cat"?Gdiplus::Color(245,241,182,107):Gdiplus::Color(245,231,184,103)):Gdiplus::Color(245,248,242,232);}
27
+ static void updateStatus();static void chooseWindow();static void layoutControls();
28
+ static JSValue call(const char* name,int argc=0,JSValue* args=nullptr){JSValue global=JS_GetGlobalObject(context),fn=JS_GetPropertyStr(context,global,name),out=JS_Call(context,fn,global,argc,args);JS_FreeValue(context,fn);JS_FreeValue(context,global);if(JS_IsException(out)){js_std_dump_error(context);failed=true;if(target)PostMessage(target,WM_CLOSE,0,0);}return out;}
29
+ static void interact(const char* kind){JSValue arg=JS_NewString(context,kind),result=call("__desktopInteract",1,&arg);bool accepted=!JS_IsException(result)&&JS_ToBool(context,result);JS_FreeValue(context,result);JS_FreeValue(context,arg);lastKind=kind;lastAccepted=accepted;if(!production||diagnostic){printf("{\"interaction\":\"%s\",\"accepted\":%s}\n",kind,accepted?"true":"false");fflush(stdout);}previous.clear();updateStatus();}
30
+ struct Surface {
31
+ HDC dc=nullptr;HBITMAP bitmap=nullptr;HGDIOBJ old=nullptr;uint8_t* bits=nullptr;int width=0,height=0;
32
+ ~Surface(){release();}
33
+ void release(){if(dc){if(old)SelectObject(dc,old);if(bitmap)DeleteObject(bitmap);DeleteDC(dc);}dc=nullptr;bitmap=nullptr;old=nullptr;bits=nullptr;width=height=0;}
34
+ void ensure(int w,int h){if(w<=width&&h<=height)return;w=std::max(width,(w+63)/64*64);h=std::max(height,(h+63)/64*64);release();width=w;height=h;BITMAPINFO info{};info.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);info.bmiHeader.biWidth=w;info.bmiHeader.biHeight=-h;info.bmiHeader.biPlanes=1;info.bmiHeader.biBitCount=32;info.bmiHeader.biCompression=BI_RGB;HDC screen=GetDC(nullptr);dc=CreateCompatibleDC(screen);bitmap=CreateDIBSection(screen,&info,DIB_RGB_COLORS,(void**)&bits,nullptr,0);ReleaseDC(nullptr,screen);if(!bitmap||!dc)throw std::runtime_error("Native surface allocation");old=SelectObject(dc,bitmap);}
35
+ };
36
+ static Surface surface;
37
+ struct DotAtlas {
38
+ int cell=0;uint32_t color=0;std::vector<uint8_t> pixels;
39
+ void ensure(int value){if(cell==value&&color==ink().GetValue())return;cell=value;color=ink().GetValue();size_t length=(size_t)cell*cell*2*4;pixels.assign(length*256,0);static const int dx[]={0,0,0,1,1,1,0,1},dy[]={0,1,2,0,1,2,3,3};float step=cell/2.f,diameter=std::max(1.1f,step*.48f);
40
+ for(int mask=0;mask<256;mask++){Gdiplus::Bitmap bitmap(cell,cell*2,cell*4,PixelFormat32bppPARGB,pixels.data()+mask*length);Gdiplus::Graphics g(&bitmap);g.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);Gdiplus::SolidBrush brush(ink());for(int bit=0;bit<8;bit++)if(mask&(1<<bit)){float x=(dx[bit]+.5f)*step,y=(dy[bit]+.5f)*step;g.FillEllipse(&brush,x-diameter/2,y-diameter/2,diameter,diameter);}}
41
+ }
42
+ };
43
+ static DotAtlas atlas;
44
+ static void paint(const std::string& text,int columns,int rows,int cell,POINT canvas){
45
+ std::vector<uint8_t> masks((size_t)columns*rows);size_t at=0;int left=columns,top=rows,right=-1,bottom=-1;
46
+ for(int y=0;y<rows;y++){if(y){if(at>=text.size()||text[at++]!='\n')throw std::runtime_error("Native row separator");}for(int x=0;x<columns;x++){if(at+3>text.size())throw std::runtime_error("Native text length");const auto* p=(const unsigned char*)text.data()+at;uint32_t ch=((p[0]&15)<<12)|((p[1]&63)<<6)|(p[2]&63);at+=3;if(ch<0x2800||ch>0x28ff)throw std::runtime_error("Native glyph");uint8_t mask=(uint8_t)(ch-0x2800);masks[y*columns+x]=mask;if(mask){left=std::min(left,x);right=std::max(right,x);top=std::min(top,y);bottom=std::max(bottom,y);}}}
47
+ if(at!=text.size())throw std::runtime_error("Native trailing text");if(right<left){ShowWindow(overlay,SW_HIDE);return;}
48
+ int w=(right-left+1)*cell,h=(bottom-top+1)*cell*2;if((int64_t)w*h>8000000){ShowWindow(overlay,SW_HIDE);visible=false;return;}surface.ensure(w,h);std::fill(surface.bits,surface.bits+(size_t)surface.width*surface.height*4,0);
49
+ atlas.ensure(cell);size_t stampSize=(size_t)cell*cell*2*4;
50
+ for(int y=top;y<=bottom;y++)for(int x=left;x<=right;x++){int mask=masks[y*columns+x];if(!mask)continue;const uint8_t* stamp=atlas.pixels.data()+mask*stampSize;for(int sy=0;sy<cell*2;sy++)memcpy(surface.bits+((size_t)((y-top)*cell*2+sy)*surface.width+(x-left)*cell)*4,stamp+(size_t)sy*cell*4,cell*4);}
51
+ if(verifyAtlas&&!atlasVerified){std::vector<uint8_t> reference((size_t)w*h*4,0);Gdiplus::Bitmap bitmap(w,h,w*4,PixelFormat32bppPARGB,reference.data());Gdiplus::Graphics g(&bitmap);g.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);Gdiplus::SolidBrush ink(NativePet::ink());static const int dx[]={0,0,0,1,1,1,0,1},dy[]={0,1,2,0,1,2,3,3};float step=cell/2.f,diameter=std::max(1.1f,step*.48f);for(int y=top;y<=bottom;y++)for(int x=left;x<=right;x++){int mask=masks[y*columns+x];for(int bit=0;bit<8;bit++)if(mask&(1<<bit)){float px=(x-left)*cell+(dx[bit]+.5f)*step,py=(y-top)*cell*2+(dy[bit]+.5f)*step;g.FillEllipse(&ink,px-diameter/2,py-diameter/2,diameter,diameter);}}g.Flush(Gdiplus::FlushIntentionSync);for(int y=0;y<h;y++)if(memcmp(reference.data()+(size_t)y*w*4,surface.bits+(size_t)y*surface.width*4,w*4)!=0)throw std::runtime_error("Dot atlas changed pixels");atlasVerified=true;printf("{\"stage\":\"dot-atlas-verified\",\"pixelExact\":true}\n");fflush(stdout);}
52
+ if(saveProof&&!proofSaved){Gdiplus::Bitmap source(surface.width,surface.height,surface.width*4,PixelFormat32bppPARGB,surface.bits),proof(w,h,PixelFormat32bppARGB);Gdiplus::Graphics pg(&proof);pg.Clear(Gdiplus::Color(255,0,0,0));pg.DrawImage(&source,Gdiplus::Rect(0,0,w,h),0,0,w,h,Gdiplus::UnitPixel);pg.Flush(Gdiplus::FlushIntentionSync);CLSID png={0x557cf406,0x1a04,0x11d3,{0x9a,0x73,0x00,0x00,0xf8,0x1e,0xf3,0x2e}};if(proof.Save(L"dist/native-memory/native-window-proof.png",&png,nullptr)!=Gdiplus::Ok)throw std::runtime_error("PNG proof failed");proofSaved=true;}
53
+ POINT destination={canvas.x+left*cell,canvas.y+top*cell*2},origin={0,0};SIZE size={w,h};BLENDFUNCTION blend={AC_SRC_OVER,0,255,AC_SRC_ALPHA};HDC screen=GetDC(nullptr);BOOL result=UpdateLayeredWindow(overlay,screen,&destination,&size,surface.dc,&origin,0,&blend,ULW_ALPHA);ReleaseDC(nullptr,screen);if(!result)throw std::runtime_error("Native layered update");SetWindowPos(overlay,HWND_TOPMOST,destination.x,destination.y,w,h,SWP_NOACTIVATE|SWP_SHOWWINDOW);painted++;
54
+ }
55
+ static void updateStatus(){
56
+ if(!production)return;
57
+ PROCESS_MEMORY_COUNTERS_EX memory{};GetProcessMemoryInfo(GetCurrentProcess(),(PROCESS_MEMORY_COUNTERS*)&memory,sizeof(memory));
58
+ std::ostringstream out;out<<"{\"running\":true,\"reachable\":true,\"engine\":\"quickjs-native\",\"pet\":\""<<petKind<<"\",\"ink\":\""<<inkName<<"\",\"returnStyle\":\""<<returnStyle<<"\",\"selected\":"<<(followed?"true":"false")<<",\"visible\":"<<(visible?"true":"false")<<",\"pickerVisible\":"<<(picker?"true":"false")<<",\"paused\":"<<(paused?"true":"false")<<",\"frames\":"<<frames<<",\"paintedFrames\":"<<painted<<",\"pid\":"<<GetCurrentProcessId()<<",\"painterPid\":"<<GetCurrentProcessId()<<",\"rssBytes\":"<<memory.WorkingSetSize<<",\"peakWorkingSetBytes\":"<<memory.PeakWorkingSetSize<<",\"privateBytes\":"<<memory.PrivateUsage<<",\"watch\":{\"source\":\"off\",\"state\":\"off\",\"pendingCount\":0},\"brain\":{\"provider\":\"off\",\"state\":\"off\"},\"lastInteraction\":";
59
+ if(lastKind.empty())out<<"null";else out<<"{\"kind\":\""<<lastKind<<"\",\"accepted\":"<<(lastAccepted?"true":"false")<<"}";out<<"}";control.update(out.str());
60
+ }
61
+ static void switchPet(const std::string& next){
62
+ if(next==petKind)return;JSValue arg=JS_NewString(context,next.c_str()),result=call("__desktopSwitchPet",1,&arg);bool ok=!JS_IsException(result)&&JS_ToBool(context,result);JS_FreeValue(context,arg);JS_FreeValue(context,result);if(!ok)return;petKind=next;if(!inkExplicit)inkName=next=="cat"?"gold":"cream";previous.clear();surface.release();lastCanvas={INT_MIN,INT_MIN};layoutControls();updateStatus();
63
+ }
64
+ static BOOL CALLBACK enumerate(HWND window,LPARAM){
65
+ if(!IsWindowVisible(window)||GetWindow(window,GW_OWNER))return TRUE;DWORD pid=0;GetWindowThreadProcessId(window,&pid);if(!pid||pid==GetCurrentProcessId())return TRUE;wchar_t title[512];if(!GetWindowText(window,title,512))return TRUE;RECT rect;if(!GetClientRect(window,&rect)||rect.right<160||rect.bottom<120)return TRUE;LRESULT index=SendMessage(windowList,LB_ADDSTRING,0,(LPARAM)title);if(index!=LB_ERR&&index!=LB_ERRSPACE){SendMessage(windowList,LB_SETITEMDATA,index,(LPARAM)choices.size());choices.emplace_back(window,pid);}return TRUE;
66
+ }
67
+ static void attachSelection(){
68
+ LRESULT selected=SendMessage(windowList,LB_GETCURSEL,0,0);if(selected==LB_ERR)return;LRESULT item=SendMessage(windowList,LB_GETITEMDATA,selected,0);if(item<0||(size_t)item>=choices.size())return;const auto candidate=choices[(size_t)item];DWORD pid=0;GetWindowThreadProcessId(candidate.first,&pid);if(!IsWindow(candidate.first)||pid!=candidate.second)return;followed=candidate.first;followedPid=candidate.second;HWND old=picker;picker=nullptr;DestroyWindow(old);previous.clear();SetForegroundWindow(followed);updateStatus();
69
+ }
70
+ static void addPetCombo(HWND parent){HWND combo=CreateWindow(L"COMBOBOX",L"",WS_CHILD|WS_VISIBLE|CBS_DROPDOWNLIST,8,8,114,180,parent,(HMENU)9,GetModuleHandle(nullptr),nullptr);SendMessage(combo,CB_ADDSTRING,0,(LPARAM)L"Retriever");SendMessage(combo,CB_ADDSTRING,0,(LPARAM)L"Ginger cat");SendMessage(combo,CB_SETCURSEL,petKind=="cat"?1:0,0);}
71
+ static LRESULT CALLBACK pickerProcedure(HWND window,UINT message,WPARAM w,LPARAM l){
72
+ if(message==WM_COMMAND){int id=LOWORD(w);if(id==101||(id==100&&HIWORD(w)==LBN_DBLCLK)){attachSelection();return 0;}if(id==102){SendMessage(windowList,LB_RESETCONTENT,0,0);choices.clear();EnumWindows(enumerate,0);return 0;}if(id==9&&HIWORD(w)==CBN_SELCHANGE){switchPet(SendMessage((HWND)l,CB_GETCURSEL,0,0)==1?"cat":"dog");return 0;}}
73
+ if(message==WM_DESTROY){if(picker==window)picker=nullptr;if(!followed&&target)PostMessage(target,WM_CLOSE,0,0);updateStatus();return 0;}return DefWindowProc(window,message,w,l);
74
+ }
75
+ static void chooseWindow(){
76
+ if(!production||(diagnostic&&!testPicker))return;if(picker){SetForegroundWindow(picker);return;}visible=false;ShowWindow(overlay,SW_HIDE);ShowWindow(target,SW_HIDE);previous.clear();WNDCLASS wc{};wc.lpfnWndProc=pickerProcedure;wc.hInstance=GetModuleHandle(nullptr);wc.lpszClassName=L"RetrieviewerNativePicker";wc.hbrBackground=(HBRUSH)(COLOR_WINDOW+1);RegisterClass(&wc);
77
+ picker=CreateWindowEx(WS_EX_TOOLWINDOW,wc.lpszClassName,L"Retrieviewer - choose an app window",WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU, std::max(0,(GetSystemMetrics(SM_CXSCREEN)-650)/2),std::max(0,(GetSystemMetrics(SM_CYSCREEN)-450)/2),650,450,nullptr,nullptr,wc.hInstance,nullptr);if(!picker)throw std::runtime_error("Cannot open window picker");
78
+ addPetCombo(picker);CreateWindow(L"STATIC",L"Choose the app window your companion should follow.",WS_CHILD|WS_VISIBLE,136,12,490,24,picker,nullptr,wc.hInstance,nullptr);windowList=CreateWindowEx(WS_EX_CLIENTEDGE,L"LISTBOX",L"",WS_CHILD|WS_VISIBLE|WS_VSCROLL|LBS_NOTIFY,12,44,610,310,picker,(HMENU)100,wc.hInstance,nullptr);CreateWindow(L"BUTTON",L"Attach",WS_CHILD|WS_VISIBLE|BS_DEFPUSHBUTTON,434,367,90,30,picker,(HMENU)101,wc.hInstance,nullptr);CreateWindow(L"BUTTON",L"Refresh",WS_CHILD|WS_VISIBLE,532,367,90,30,picker,(HMENU)102,wc.hInstance,nullptr);choices.clear();EnumWindows(enumerate,0);ShowWindow(picker,SW_SHOW);SetWindowPos(picker,HWND_TOP,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE|SWP_SHOWWINDOW);SetForegroundWindow(picker);updateStatus();
79
+ }
80
+ static void layoutControls(){
81
+ if(!production||diagnostic||!target)return;const std::vector<int> ids=petKind=="cat"?std::vector<int>{1,2,3,6,8,4,5,7}:std::vector<int>{1,2,3,4,5,7};
82
+ for(int id:{1,2,3,4,5,6,7,8})ShowWindow(GetDlgItem(target,id),SW_HIDE);int x=128;
83
+ for(int id:ids){const wchar_t* label=id==1?(petKind=="cat"?L"Greet":L"Ball"):id==2?L"Treat":id==3?(petKind=="cat"?L"Company":L"Tug"):id==4?(paused?L"Resume":L"Pause"):id==5?L"Window":id==6?L"Tower":id==8?L"Wheel":L"Quit";HWND button=GetDlgItem(target,id);SetWindowText(button,label);SetWindowPos(button,nullptr,x,6,64,28,SWP_NOACTIVATE|SWP_NOZORDER|SWP_SHOWWINDOW);x+=68;}
84
+ SendMessage(GetDlgItem(target,9),CB_SETCURSEL,petKind=="cat"?1:0,0);SetWindowPos(target,nullptr,0,0,x+6,40,SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER);previous.clear();
85
+ }
86
+ static void tick(){
87
+ const ULONGLONG now=GetTickCount64(),elapsed=now-startTime;if((durationMs&&elapsed>=durationMs)||(production&&!detached&&elapsed>12000)){DestroyWindow(target);return;}
88
+ if(!showing&&!testPicker&&(!production||diagnostic)){const int next=elapsed>=120000?5:elapsed>=95000?4:elapsed>=70000?3:elapsed>=45000?2:elapsed>=1000?1:0;if(next>stage){stage=next;if(petKind=="cat")interact(stage==1?"toy":stage==2?"treat":stage==3?"company":stage==4?"tower":"wheel");else if(stage==1||stage==2)interact("ball");else if(stage==3)interact("treat");else if(stage==4)interact("tug");else if(stage==5)interact("wall");}}
89
+ double dt=lastTick?std::min(.1,(now-lastTick)/1000.0):0;lastTick=now;
90
+ HWND scene=production&&(!diagnostic||testPicker)?followed:target;
91
+ DWORD scenePid=0;if(scene)GetWindowThreadProcessId(scene,&scenePid);
92
+ if(production&&(!diagnostic||testPicker)&&scene&&(!IsWindow(scene)||scenePid!=followedPid)){followed=nullptr;chooseWindow();scene=nullptr;}
93
+ if(picker||!scene||IsIconic(scene)||(production&&(!diagnostic||testPicker)&&GetForegroundWindow()!=scene&&GetForegroundWindow()!=target)||(showing&&GetForegroundWindow()!=target)){
94
+ visible=false;ShowWindow(overlay,SW_HIDE);if(production&&(!diagnostic||testPicker))ShowWindow(target,SW_HIDE);previous.clear();updateStatus();return;
95
+ }
96
+ RECT rect;GetClientRect(scene,&rect);POINT canvas{0,0};ClientToScreen(scene,&canvas);int cell=8,columns=rect.right/cell,rows=rect.bottom/(cell*2);while(columns*rows>125000&&cell<32){cell+=2;columns=rect.right/cell;rows=rect.bottom/(cell*2);}if(columns<8||rows<4||columns*rows>125000){visible=false;ShowWindow(overlay,SW_HIDE);previous.clear();updateStatus();return;}
97
+ visible=true;if(canvas.x!=lastCanvas.x||canvas.y!=lastCanvas.y||rect.right!=lastWidth||rect.bottom!=lastHeight){previous.clear();lastCanvas=canvas;lastWidth=rect.right;lastHeight=rect.bottom;}
98
+ if(production&&(!diagnostic||testPicker)){RECT bar;GetWindowRect(target,&bar);int x=canvas.x+std::max<LONG>(0,rect.right-(bar.right-bar.left)),y=canvas.y+8;if(bar.left!=x||bar.top!=y||!IsWindowVisible(target))SetWindowPos(target,HWND_TOPMOST,x,y,0,0,SWP_NOSIZE|SWP_NOACTIVATE|SWP_SHOWWINDOW);}
99
+ if(paused){updateStatus();return;}
100
+ LARGE_INTEGER freq,t0,t1;QueryPerformanceFrequency(&freq);QueryPerformanceCounter(&t0);JSValue args[]={JS_NewFloat64(context,dt),JS_NewInt32(context,columns),JS_NewInt32(context,rows),JS_NewBool(context,previous.empty())},result=call("__desktopFrame",4,args);for(auto value:args)JS_FreeValue(context,value);if(JS_IsException(result)||JS_IsNull(result)||JS_IsUndefined(result)){JS_FreeValue(context,result);updateStatus();return;}
101
+ frames++;size_t length;const char* value=JS_ToCStringLen(context,&length,result);if(!value){JS_FreeValue(context,result);throw std::runtime_error("Native frame string");}std::string text(value,length);JS_FreeCString(context,value);JS_FreeValue(context,result);if(text!=previous){paint(text,columns,rows,cell,canvas);previous=std::move(text);}QueryPerformanceCounter(&t1);double cost=(t1.QuadPart-t0.QuadPart)*1000.0/freq.QuadPart;if(production&&timings.size()>=1024)timings[timingCursor++%1024]=cost;else timings.push_back(cost);updateStatus();
102
+ }
103
+ static LRESULT CALLBACK procedure(HWND window,UINT message,WPARAM w,LPARAM l){
104
+ if(message==WM_TIMER){try{tick();}catch(const std::exception& e){fprintf(stderr,"Native renderer: %s\n",e.what());failed=true;DestroyWindow(window);}return 0;}
105
+ if(message==WM_APP+1){if(w==1||w==2)switchPet(w==2?"cat":"dog");return 0;}
106
+ if(message==WM_COMMAND){int id=LOWORD(w);if(id==9&&HIWORD(w)==CBN_SELCHANGE)switchPet(SendMessage((HWND)l,CB_GETCURSEL,0,0)==1?"cat":"dog");else if(id==1)interact(petKind=="cat"?"greet":"ball");else if(id==2)interact("treat");else if(id==3)interact(petKind=="cat"?"company":"tug");else if(id==4){paused=!paused;previous.clear();layoutControls();updateStatus();}else if(id==5){if(production)chooseWindow();else interact("paw");}else if(id==6)interact(production?"tower":"wall");else if(id==8)interact("wheel");else if(id==7)DestroyWindow(window);return 0;}
107
+ if(message==WM_SIZE||message==WM_MOVE){previous.clear();return 0;}
108
+ if(message==WM_DESTROY){KillTimer(window,1);followed=nullptr;if(picker){HWND old=picker;picker=nullptr;SetWindowLongPtr(old,GWLP_WNDPROC,(LONG_PTR)DefWindowProc);DestroyWindow(old);}if(overlay)DestroyWindow(overlay);overlay=nullptr;PostQuitMessage(0);return 0;}
109
+ return DefWindowProc(window,message,w,l);
110
+ }
111
+ static int run(JSContext* ctx,int argc,char** argv){
112
+ context=ctx;showing=false;durationMs=150000;for(int i=1;i<argc;i++){std::string arg=argv[i];if(arg=="--production"){production=true;durationMs=0;}if(arg=="--show"){showing=true;durationMs=0;}if(arg=="--proof")saveProof=true;if(arg=="--verify-atlas")verifyAtlas=true;if(arg.rfind("--duration=",0)==0)durationMs=(ULONGLONG)(std::stod(arg.substr(11))*1000);}
113
+ JSValue global=JS_GetGlobalObject(ctx),config=JS_GetPropertyStr(ctx,global,"__nativeConfig");if(JS_IsObject(config)){petKind=propertyString(ctx,config,"pet","dog");inkName=propertyString(ctx,config,"ink",petKind=="cat"?"gold":"cream");returnStyle=propertyString(ctx,config,"returnStyle","auto");inkExplicit=propertyBool(ctx,config,"inkExplicit");diagnostic=propertyBool(ctx,config,"diagnostic");testPicker=diagnostic&&propertyBool(ctx,config,"testPicker");}if(petKind!="dog"&&petKind!="cat")throw std::runtime_error("Invalid native pet");if(inkName!="cream"&&inkName!="gold"&&inkName!="dark")throw std::runtime_error("Invalid native ink");
114
+ SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);Gdiplus::GdiplusStartupInput gdip;ULONG_PTR token;if(Gdiplus::GdiplusStartup(&token,&gdip,nullptr)!=Gdiplus::Ok)throw std::runtime_error("GDI+ startup");
115
+ if(production){std::wstring name=wide("Local\\BiteFoundry.Retrieviewer.Overlay."+userSid()+(diagnostic?".Diagnostics."+std::to_string(GetCurrentProcessId()):""));instanceMutex=CreateMutexW(nullptr,TRUE,name.c_str());if(!instanceMutex||GetLastError()==ERROR_ALREADY_EXISTS)throw std::runtime_error("A Retrieviewer companion is already running");}
116
+ HINSTANCE instance=GetModuleHandle(nullptr);WNDCLASS wc{};wc.lpfnWndProc=procedure;wc.hInstance=instance;wc.lpszClassName=L"RetrieviewerNativeHost";wc.hbrBackground=(HBRUSH)GetStockObject(BLACK_BRUSH);RegisterClass(&wc);
117
+ const bool attach=production&&(!diagnostic||testPicker);target=CreateWindowEx(WS_EX_TOOLWINDOW|(attach?WS_EX_NOACTIVATE:0),wc.lpszClassName,attach?L"Retrieviewer controls":L"Retrieviewer diagnostic scene",showing?WS_OVERLAPPEDWINDOW:WS_POPUP,showing?80:-12000,showing?80:-12000,attach?552:showing?1280:3840,attach?40:showing?800:2160,nullptr,nullptr,instance,nullptr);if(!target)throw std::runtime_error("Native host window");if(!attach)followed=target;
118
+ WNDCLASS pet{};pet.lpfnWndProc=DefWindowProc;pet.hInstance=instance;pet.lpszClassName=L"RetrieviewerNativeDots";RegisterClass(&pet);overlay=CreateWindowEx(WS_EX_LAYERED|WS_EX_TRANSPARENT|WS_EX_TOOLWINDOW|WS_EX_NOACTIVATE|WS_EX_TOPMOST,pet.lpszClassName,L"",WS_POPUP,0,0,1,1,nullptr,nullptr,instance,nullptr);if(!overlay)throw std::runtime_error("Native overlay window");
119
+ if(attach){addPetCombo(target);for(int id=1;id<=8;id++)CreateWindow(L"BUTTON",L"",WS_CHILD|BS_PUSHBUTTON,0,0,1,1,target,(HMENU)(INT_PTR)id,instance,nullptr);layoutControls();chooseWindow();}
120
+ else if(showing){const wchar_t* names[]={L"Ball",L"Treat",L"Tug",L"Pause",L"Paw",L"Wall",L"Quit"};for(int i=0;i<7;i++)CreateWindow(L"BUTTON",names[i],WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,8+i*70,8,66,28,target,(HMENU)(INT_PTR)(i+1),instance,nullptr);}
121
+ if(!attach)ShowWindow(target,showing?SW_SHOW:SW_SHOWNOACTIVATE);startTime=GetTickCount64();lastTick=0;timings.reserve(1024);SetTimer(target,1,33,nullptr);
122
+ if(production){updateStatus();control.start(propertyString(ctx,config,"pipe"),propertyString(ctx,config,"token"),propertyString(ctx,config,"sessionFile"),[](const std::string& command){PostMessage(target,command=="stop"?WM_CLOSE:WM_APP+1,command=="pet-cat"?2:command=="pet-dog"?1:0,0);});printf("{\"ready\":true,\"pid\":%lu}\n",GetCurrentProcessId());fflush(stdout);startupGuard=std::thread([]{std::string ack;bool ok=std::getline(std::cin,ack)&&ack=="DETACH";if(ok){detached=true;printf("{\"detached\":true}\n");fflush(stdout);}else{DWORD pid=0;GetWindowThreadProcessId(target,&pid);if(pid==GetCurrentProcessId())PostMessage(target,WM_CLOSE,0,0);}});}
123
+ else{detached=true;printf("{\"stage\":\"native-window\",\"width\":%d,\"height\":%d,\"durationMs\":%llu,\"pid\":%lu}\n",showing?1280:3840,showing?800:2160,durationMs,GetCurrentProcessId());fflush(stdout);}
124
+ JS_FreeValue(ctx,config);JS_FreeValue(ctx,global);MSG msg;while(GetMessage(&msg,nullptr,0,0)>0){TranslateMessage(&msg);DispatchMessage(&msg);}
125
+ if(startupGuard.joinable()){CancelSynchronousIo(startupGuard.native_handle());startupGuard.join();}control.close();JSValue result=call("__desktopDispose");JS_FreeValue(ctx,result);surface.release();if(instanceMutex){ReleaseMutex(instanceMutex);CloseHandle(instanceMutex);instanceMutex=nullptr;}Gdiplus::GdiplusShutdown(token);
126
+ std::sort(timings.begin(),timings.end());double sum=0;for(auto t:timings)sum+=t;if(!production||diagnostic){printf("{\"stage\":\"native-exit\",\"painted\":%d,\"renders\":%llu,\"meanMs\":%.3f,\"p95Ms\":%.3f,\"maxMs\":%.3f,\"failed\":%s}\n",painted,frames,timings.empty()?0:sum/timings.size(),timings.empty()?0:timings[(size_t)(timings.size()*.95)],timings.empty()?0:timings.back(),failed?"true":"false");fflush(stdout);}return failed?1:0;
127
+ }
128
+ }