@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.
- package/ASSET-PROVENANCE.md +11 -0
- package/ASSETS-LICENSE.md +6 -0
- package/ATTRIBUTION.md +8 -0
- package/DESKTOP-PLATFORMS.md +1 -1
- package/EMBEDDING.md +15 -9
- package/INSTALLING.md +2 -1
- package/LICENSE +16 -16
- package/NATIVE-RUNTIME.md +89 -0
- package/PREREQUISITES.md +7 -0
- package/PUPPY-MEMORY.md +70 -0
- package/README.ko.md +13 -1
- package/README.md +14 -1
- package/RELEASING.md +36 -1
- 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 +53 -13
- package/pet-indexed-manifest.json +106 -0
- package/puppy-clean.mjs +2 -2
- package/puppy-companion-comfort-q5.rvz +0 -0
- package/puppy-companion-interactions-v3-q5.rvz +0 -0
- package/puppy-companion-sprites.mjs +2 -2
- package/puppy-frame-file.mjs +15 -0
- package/puppy-frame-indexed.mjs +78 -0
- package/puppy-frame-predictive.mjs +1 -1
- package/puppy-frame-store.mjs +6 -0
- package/puppy-paper-actions-v9-q5.rvz +0 -0
- package/puppy-paper-actions.mjs +2 -2
- package/puppy-paper-approach-q5.rvz +0 -0
- package/puppy-paper-arrival-seat-q5.rvz +0 -0
- package/puppy-paper-behaviors-q5.rvz +0 -0
- package/puppy-paper-behaviors.mjs +1 -1
- package/puppy-paper-contact.mjs +2 -2
- package/puppy-paper-rear-gallop-q5.rvz +0 -0
- package/puppy-paper-seat-q5.rvz +0 -0
- package/puppy-paper-seat.mjs +2 -2
- package/puppy-paper.mjs +12 -12
- package/puppy-stride-tweens-q5.rvz +0 -0
- package/retrieviewer-cli.mjs +10 -6
- package/retrieviewer.d.mts +1 -1
- package/retrieviewer.mjs +35 -14
- package/puppy-companion-comfort-q5.bin.gz +0 -0
- package/puppy-companion-interactions-v3-q5.bin.gz +0 -0
- package/puppy-paper-actions-v9-q5.bin.gz +0 -0
- package/puppy-paper-approach-q5.bin.gz +0 -0
- package/puppy-paper-arrival-seat-q5.bin.gz +0 -0
- package/puppy-paper-behaviors-q5.bin.gz +0 -0
- package/puppy-paper-rear-gallop-q5.bin.gz +0 -0
- package/puppy-paper-seat-q5.bin.gz +0 -0
- package/puppy-stride-tweens-q5.bin.gz +0 -0
package/puppy-paper.mjs
CHANGED
|
@@ -354,9 +354,9 @@ export function createPaperActor(animal,info,{manualTricks=false,trickTimings=nu
|
|
|
354
354
|
};
|
|
355
355
|
}
|
|
356
356
|
|
|
357
|
-
export function createPaperRenderer(cache,info,meta,width,height,{rear,approach,seat,arrivalSeat,behaviors,actions,interactions,comfort,contacts=true,pawTransport=false,motionTables=null}={}){
|
|
357
|
+
export function createPaperRenderer(cache,info,meta,width,height,{rear,approach,seat,arrivalSeat,behaviors,actions,interactions,comfort,contacts=true,pawTransport=false,motionTables=null,kernel=null}={}){
|
|
358
358
|
if(![width,height].every(Number.isInteger)||width<2||height<2||width*height>1_000_000)throw Error('Invalid paper grid');
|
|
359
|
-
const tween=createCachedTween(cache,info),renderMeta={...meta,pairs:info.pair_map,run_sequence:info.run_sequence};
|
|
359
|
+
const tween=createCachedTween(cache,info,kernel),renderMeta={...meta,pairs:info.pair_map,run_sequence:info.run_sequence};
|
|
360
360
|
const out=new Uint8ClampedArray(width*height*4),area=createPaperAreaSampler();
|
|
361
361
|
// Only one authored source is drawn at a time. Small samplers borrow a view of
|
|
362
362
|
// the action-sized scratch. A sleep blend reads each base pixel before writing
|
|
@@ -364,14 +364,14 @@ export function createPaperRenderer(cache,info,meta,width,height,{rear,approach,
|
|
|
364
364
|
const actionScratch=new Uint8ClampedArray(Math.max(320*320,(actions?.info.width??320)*(actions?.info.height??320))*4);
|
|
365
365
|
const spriteScratch=actionScratch.subarray(0,320*320*4);
|
|
366
366
|
for(let i=3;i<actionScratch.length;i+=4)actionScratch[i]=255;
|
|
367
|
-
const rearSample=rear?createRearSampler(rear.bytes,rear.info,spriteScratch):null,lock=createPaperFootLock({maxImageStretch:pawTransport?1.45:Infinity});
|
|
368
|
-
const approachSample=approach?createRearSampler(approach.bytes,approach.info,spriteScratch):null;
|
|
369
|
-
const seatSample=seat?createPaperSeatSampler(seat.bytes,seat.info,spriteScratch):null;
|
|
370
|
-
const arrivalSeatSample=arrivalSeat?createPaperSeatSampler(arrivalSeat.bytes,arrivalSeat.info,spriteScratch):null;
|
|
371
|
-
const behaviorSample=behaviors?createPaperBehaviorSampler(behaviors.bytes,behaviors.info,spriteScratch):null;
|
|
372
|
-
const interactionSample=interactions?createCompanionSpriteSampler(interactions.bytes,interactions.info,spriteScratch):null;
|
|
367
|
+
const rearSample=rear?createRearSampler(rear.bytes,rear.info,spriteScratch,kernel):null,lock=createPaperFootLock({maxImageStretch:pawTransport?1.45:Infinity});
|
|
368
|
+
const approachSample=approach?createRearSampler(approach.bytes,approach.info,spriteScratch,kernel):null;
|
|
369
|
+
const seatSample=seat?createPaperSeatSampler(seat.bytes,seat.info,spriteScratch,kernel):null;
|
|
370
|
+
const arrivalSeatSample=arrivalSeat?createPaperSeatSampler(arrivalSeat.bytes,arrivalSeat.info,spriteScratch,kernel):null;
|
|
371
|
+
const behaviorSample=behaviors?createPaperBehaviorSampler(behaviors.bytes,behaviors.info,spriteScratch,kernel):null;
|
|
372
|
+
const interactionSample=interactions?createCompanionSpriteSampler(interactions.bytes,interactions.info,spriteScratch,kernel):null;
|
|
373
373
|
const comfortSample=comfort?createComfortSpriteSampler(comfort.bytes,comfort.info,spriteScratch):null;
|
|
374
|
-
const actionSample=actions?createPaperActionSampler(actions.bytes,actions.info,actionScratch.subarray(0,actions.info.width*actions.info.height*4),motionTables?.actionClips):null,actionArea=actions?createPaperAreaSampler(actions.info.width):null;
|
|
374
|
+
const actionSample=actions?createPaperActionSampler(actions.bytes,actions.info,actionScratch.subarray(0,actions.info.width*actions.info.height*4),motionTables?.actionClips,kernel):null,actionArea=actions?createPaperAreaSampler(actions.info.width):null;
|
|
375
375
|
const episodes=(key,labels,indices,samples,options)=>motionTables?.support[key]?createBakedSupportEpisodes(motionTables.support[key]):createPaperSupportEpisodes(labels,indices,samples,options);
|
|
376
376
|
const walkEpisodes=actions?episodes('walk',actions.info.feet,actions.info.clips.walk,9):null;
|
|
377
377
|
const sourceEpisodes=rear?{
|
|
@@ -415,8 +415,8 @@ export function createPaperRenderer(cache,info,meta,width,height,{rear,approach,
|
|
|
415
415
|
let sx=Math.floor(rawX);if(state.flip)sx=sourceN-1-sx;
|
|
416
416
|
return image[(Math.floor(rawY)*sourceN+sx)*4]>16;
|
|
417
417
|
}}):null;render.lastTransport=transport;
|
|
418
|
-
const minify=side<320||sideX<320,filter=isAction?actionArea:area;if(minify)filter.prepare(image);
|
|
419
|
-
out.fill(0);for(let i=3;i<out.length;i+=4)out[i]=255;
|
|
418
|
+
const minify=side<320||sideX<320,filter=isAction?actionArea:area;if(minify&&!(kernel?.blit&&transport))filter.prepare(image);
|
|
419
|
+
if(kernel?.clear)kernel.clear(out);else{out.fill(0);for(let i=3;i<out.length;i+=4)out[i]=255;}
|
|
420
420
|
render.lastPlacement={left,top,side,sideX};
|
|
421
421
|
// Measured drawn mouth of the EXACT sample above (same priority, quantization,
|
|
422
422
|
// placement and mirror). Side/rear/action/interaction stay null: unsupported.
|
|
@@ -430,7 +430,7 @@ export function createPaperRenderer(cache,info,meta,width,height,{rear,approach,
|
|
|
430
430
|
}
|
|
431
431
|
const drawTop=top-originY/320*side,bottom=top+Math.max(sourceN-originY,...pins.map(p=>p.target.y+20))/320*side;
|
|
432
432
|
const drawLeft=left+Math.min(-originX,...pins.map(p=>p.target.x-20))/320*sideX,drawRight=left+Math.max(sourceN-originX,...pins.map(p=>p.target.x+20))/320*sideX;
|
|
433
|
-
for(let dy=Math.max(0,minify?Math.floor(drawTop):Math.ceil(drawTop-.5));dy<Math.min(height,minify?Math.ceil(bottom):Math.ceil(bottom-.5));dy++)for(let dx=Math.max(0,minify?Math.floor(drawLeft):Math.ceil(drawLeft-.5));dx<Math.min(width,minify?Math.ceil(drawRight):Math.ceil(drawRight-.5));dx++){
|
|
433
|
+
if(kernel?.blit&&transport)kernel.blit(out,image,{width,height,sourceN,left,top,side,sideX,originX,originY,drawTop,bottom,drawLeft,drawRight,flip:state.flip,layers:transport.layers});else for(let dy=Math.max(0,minify?Math.floor(drawTop):Math.ceil(drawTop-.5));dy<Math.min(height,minify?Math.ceil(bottom):Math.ceil(bottom-.5));dy++)for(let dx=Math.max(0,minify?Math.floor(drawLeft):Math.ceil(drawLeft-.5));dx<Math.min(width,minify?Math.ceil(drawRight):Math.ceil(drawRight-.5));dx++){
|
|
434
434
|
const px=(dx+.5-left)/sideX*320,py=(dy+.5-top)/side*320;
|
|
435
435
|
const warped=transport?transport(px,py):footSourcePoint(px,py,pins);if(!warped)continue;
|
|
436
436
|
const rawX=warped.x+originX,rawY=warped.y+originY;
|
|
Binary file
|
package/retrieviewer-cli.mjs
CHANGED
|
@@ -13,7 +13,7 @@ Usage:
|
|
|
13
13
|
retrieviewer doctor [--agent ID] [--json]
|
|
14
14
|
retrieviewer agents [--json]
|
|
15
15
|
retrieviewer preview [--original]
|
|
16
|
-
retrieviewer desktop [--ink cream|gold|dark] [--return-style auto|brake|trot|run-trot] [--watch stackbite|off] [--brain openrouter|off]
|
|
16
|
+
retrieviewer desktop [--pet dog|cat] [--engine auto|native|node] [--ink cream|gold|dark] [--return-style auto|brake|trot|run-trot] [--watch stackbite|off] [--brain openrouter|off]
|
|
17
17
|
retrieviewer brain-check [--json]
|
|
18
18
|
retrieviewer attention [--json]
|
|
19
19
|
retrieviewer desktop-status [--json]
|
|
@@ -26,8 +26,10 @@ Non-interactive changes require --agent and --yes. Unsupported native renderers
|
|
|
26
26
|
are refused: MCP connectivity alone is never treated as in-screen rendering.
|
|
27
27
|
No command changes credentials, shell profiles or agent executables.
|
|
28
28
|
Desktop is a separate native overlay, NOT a Codex plugin. Choose a window once;
|
|
29
|
-
use Codex normally. Exit from its
|
|
30
|
-
Windows
|
|
29
|
+
use Codex normally. Exit from its controls or desktop-stop. No auto-start is added.
|
|
30
|
+
Windows x64 basic mode uses one native process; Node exits after launch.
|
|
31
|
+
--pet cat selects the ginger cat. Watch/brain use the Node compatibility engine.
|
|
32
|
+
Windows and AlmaLinux8 X11 are verified; macOS is experimental. Native Wayland
|
|
31
33
|
app tracking is not supported. Linux targets AlmaLinux8/GLIBC2.28, system Python3.6+.
|
|
32
34
|
Return style defaults to auto (mix all three). Restart the companion to change it.
|
|
33
35
|
Watch is OFF by default. Inside a live StackBite Seat, --watch stackbite reads only
|
|
@@ -42,7 +44,7 @@ export function parseArguments(args){
|
|
|
42
44
|
const [command='help',...rest]=args,options={};
|
|
43
45
|
for(let i=0;i<rest.length;i++){
|
|
44
46
|
const token=rest[i];
|
|
45
|
-
if(token==='--agent'||token==='--ink'||token==='--return-style'||token==='--watch'||token==='--brain'){
|
|
47
|
+
if(token==='--agent'||token==='--ink'||token==='--return-style'||token==='--watch'||token==='--brain'||token==='--pet'||token==='--engine'){
|
|
46
48
|
const key=token.slice(2);
|
|
47
49
|
if(options[key]||!rest[i+1]||rest[i+1].startsWith('-'))throw Error(token+' requires one value');
|
|
48
50
|
options[key]=rest[++i];
|
|
@@ -50,13 +52,15 @@ export function parseArguments(args){
|
|
|
50
52
|
else throw Error('Unknown argument: '+token);
|
|
51
53
|
}
|
|
52
54
|
if(options.agent&&!AGENTS.some(a=>a.id===options.agent))throw Error('Unknown agent: '+options.agent);
|
|
55
|
+
if(options.pet&&!['dog','cat'].includes(options.pet))throw Error('--pet must be dog or cat');
|
|
56
|
+
if(options.engine&&!['auto','native','node'].includes(options.engine))throw Error('--engine must be auto, native or node');
|
|
53
57
|
if(options.ink&&!['cream','gold','dark'].includes(options.ink))throw Error('--ink must be cream, gold or dark');
|
|
54
58
|
if(options['return-style']&&!['auto','brake','trot','run-trot'].includes(options['return-style']))throw Error('--return-style must be auto, brake, trot or run-trot');
|
|
55
59
|
if(options.watch&&!['off','stackbite'].includes(options.watch))throw Error('--watch must be stackbite or off');
|
|
56
60
|
if(options.brain&&!['off','openrouter'].includes(options.brain))throw Error('--brain must be openrouter or off');
|
|
57
61
|
if(options.brain==='openrouter'&&options.watch!=='stackbite')throw Error('--brain openrouter requires --watch stackbite');
|
|
58
62
|
if(!['help','--help','-h','enable','disable','disconnect','status','doctor','agents','preview','attention','brain-check','desktop','desktop-status','desktop-stop','desktop-clean','desktop-remove'].includes(command))throw Error('Unknown command: '+command);
|
|
59
|
-
const allowed={desktop:['ink','return-style','watch','brain'],attention:['json'],'brain-check':['json'],enable:['agent','yes','json','dry-run'],disconnect:['agent','yes','json','dry-run'],disable:['agent','json'],status:['agent','json'],doctor:['agent','json'],agents:['json'],preview:['original'],'desktop-status':['json'],'desktop-clean':['yes'],'desktop-remove':['yes']};
|
|
63
|
+
const allowed={desktop:['ink','return-style','watch','brain','pet','engine'],attention:['json'],'brain-check':['json'],enable:['agent','yes','json','dry-run'],disconnect:['agent','yes','json','dry-run'],disable:['agent','json'],status:['agent','json'],doctor:['agent','json'],agents:['json'],preview:['original'],'desktop-status':['json'],'desktop-clean':['yes'],'desktop-remove':['yes']};
|
|
60
64
|
for(const key of Object.keys(options))if(!(allowed[command]??[]).includes(key))throw Error(`--${key} is not supported by ${command}`);
|
|
61
65
|
return {command,options};
|
|
62
66
|
}
|
|
@@ -82,7 +86,7 @@ export async function runCli(args,{input=process.stdin,output=process.stdout,err
|
|
|
82
86
|
if(['desktop-clean','desktop-remove'].includes(command)&&!options.yes)throw Error('Use '+command+' --yes to remove only the companion cache.');
|
|
83
87
|
const api=desktop??await import('./integrations/windows-overlay/desktop.mjs');
|
|
84
88
|
const action={'desktop':'startDesktop','desktop-status':'desktopStatus','desktop-stop':'stopDesktop','desktop-clean':'cleanDesktop','desktop-remove':'removeDesktop'}[command];
|
|
85
|
-
emit(await api[action]({...options.ink?{ink:options.ink}:{},...options['return-style']?{returnStyle:options['return-style']}:{},...options.watch?{watch:options.watch}:{},...options.brain?{brain:options.brain}:{} }));return 0;
|
|
89
|
+
emit(await api[action]({...options.pet?{pet:options.pet}:{},...options.engine?{engine:options.engine}:{},...options.ink?{ink:options.ink}:{},...options['return-style']?{returnStyle:options['return-style']}:{},...options.watch?{watch:options.watch}:{},...options.brain?{brain:options.brain}:{} }));return 0;
|
|
86
90
|
}
|
|
87
91
|
if(command==='preview'){await import('./examples/live.mjs');return 0;}
|
|
88
92
|
if(['status','doctor','agents'].includes(command)){
|
package/retrieviewer.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** A headless pet: the host owns input, time and terminal rendering. */
|
|
2
2
|
export type FetchReturnStyle = 'auto' | 'brake' | 'trot' | 'run-trot';
|
|
3
3
|
export interface PetOptions {
|
|
4
|
-
/**
|
|
4
|
+
/** Relative names; return unchanged .rvz/.gz file bytes. Custom loaders use memory-backed art. */
|
|
5
5
|
loadAsset?: (name: string) => Promise<ArrayBuffer | ArrayBufferView>;
|
|
6
6
|
seed?: number;
|
|
7
7
|
energy?: number;
|
package/retrieviewer.mjs
CHANGED
|
@@ -5,6 +5,7 @@ import {createSnappyTrickTimings} from './puppy-paper-trick-timing.mjs';
|
|
|
5
5
|
import {createCompanionLife,FETCH_RETURN_STYLES} from './puppy-companion-life.mjs';
|
|
6
6
|
import {paintCompanionObjects} from './puppy-companion-objects.mjs';
|
|
7
7
|
import {readPackedFrames} from './puppy-frame-store.mjs';
|
|
8
|
+
import {expandIndexedArchive} from './puppy-frame-indexed.mjs';
|
|
8
9
|
import {BAKED_MOTION} from './puppy-motion-tables.generated.mjs';
|
|
9
10
|
|
|
10
11
|
// Embeddable autonomous puppy. Importing or creating has no DOM, timer, stdin/stdout
|
|
@@ -19,12 +20,12 @@ export const ART_PROFILES=Object.freeze(['original','q5']);
|
|
|
19
20
|
export const PET_ASSETS=Object.freeze({
|
|
20
21
|
// Kept as a legacy identifier only; createPet/getPetAssetFiles no longer load it.
|
|
21
22
|
animal:'locomotion-puppy.animal',
|
|
22
|
-
stride:'puppy-stride-tweens-q5.
|
|
23
|
+
stride:'puppy-stride-tweens-q5.rvz',strideInfo:'puppy-stride-meta.json',stableInfo:'puppy-stable-meta.json',
|
|
23
24
|
rear:'puppy-paper-rear-gallop',approach:'puppy-paper-approach',seat:'puppy-paper-seat',arrivalSeat:'puppy-paper-arrival-seat',
|
|
24
25
|
behaviors:'puppy-paper-behaviors',actions:'puppy-paper-actions-v9',interactions:'puppy-companion-interactions-v3',comfort:'puppy-companion-comfort',
|
|
25
26
|
});
|
|
26
27
|
const BUNDLES=['rear','approach','seat','arrivalSeat','behaviors','actions','interactions','comfort'];
|
|
27
|
-
const frameFile=(base,profile)=>base+(profile==='q5'?'-q5.
|
|
28
|
+
const frameFile=(base,profile)=>base+(profile==='q5'?'-q5.rvz':'-compact.bin.gz');
|
|
28
29
|
/** Exact files a host/bundler must copy, including metadata. Never loads assets. */
|
|
29
30
|
export function getPetAssetFiles(artProfile=DEFAULT_ART_PROFILE){
|
|
30
31
|
if(!ART_PROFILES.includes(artProfile))throw RangeError('artProfile must be original or q5');
|
|
@@ -50,9 +51,9 @@ const bytesOf=(name,value)=>{
|
|
|
50
51
|
if(ArrayBuffer.isView(value))return new Uint8Array(value.buffer,value.byteOffset,value.byteLength);
|
|
51
52
|
throw TypeError(`loadAsset(${name}) must return an ArrayBuffer or typed bytes`);
|
|
52
53
|
};
|
|
53
|
-
async function gunzip(bytes){
|
|
54
|
+
async function gunzip(bytes,size){
|
|
54
55
|
// Native Node gzip avoids Blob/Response/stream copies of every large cache.
|
|
55
|
-
if(typeof process!=='undefined'&&process.versions?.node){const {gunzipSync}=await import('node:zlib');return gunzipSync(bytes);}
|
|
56
|
+
if(typeof process!=='undefined'&&process.versions?.node){const {gunzipSync}=await import('node:zlib');return gunzipSync(bytes,size===undefined?undefined:{maxOutputLength:Math.max(1,size)});}
|
|
56
57
|
if(typeof DecompressionStream!=='undefined')return new Uint8Array(await new Response(new Blob([bytes]).stream().pipeThrough(new DecompressionStream('gzip'))).arrayBuffer());
|
|
57
58
|
throw Error('This browser needs DecompressionStream for pet assets');
|
|
58
59
|
}
|
|
@@ -75,8 +76,8 @@ function heldFrameKey(state,report,{columns,rows,cellAspect=.5,camera,poseFps=24
|
|
|
75
76
|
report.ball,report.treat]);
|
|
76
77
|
}
|
|
77
78
|
async function loadPetAssets(loadAsset,artProfile){
|
|
78
|
-
// Node
|
|
79
|
-
//
|
|
79
|
+
// Default local Node hosts keep art on disk and reconstruct two frames per
|
|
80
|
+
// family. Custom/browser loaders retain the compatible memory-backed store.
|
|
80
81
|
let codec;
|
|
81
82
|
if(typeof process!=='undefined'&&process.versions?.node){
|
|
82
83
|
const {deflateRawSync,inflateRawSync}=await import('node:zlib');
|
|
@@ -86,9 +87,18 @@ async function loadPetAssets(loadAsset,artProfile){
|
|
|
86
87
|
}
|
|
87
88
|
const load=async name=>bytesOf(name,await loadAsset(name));
|
|
88
89
|
const json=async name=>JSON.parse(new TextDecoder().decode(await load(name)));
|
|
89
|
-
|
|
90
|
-
|
|
90
|
+
let openFrameFile;
|
|
91
|
+
if(artProfile==='q5'&&loadAsset===defaultLoadAsset&&new URL('.',import.meta.url).protocol==='file:'&&typeof process!=='undefined'&&process.versions?.node)({openFrameFile}=await import('./puppy-frame-file.mjs'));
|
|
92
|
+
const frames=async (name,options)=>{
|
|
93
|
+
if(name.endsWith('.rvz')){
|
|
94
|
+
if(openFrameFile)return openFrameFile(new URL(name,import.meta.url),options);
|
|
95
|
+
return readPackedFrames(await expandIndexedArchive(await load(name),gunzip),{...options,codec});
|
|
96
|
+
}
|
|
97
|
+
return readPackedFrames(await gunzip(await load(name)),{...options,codec});
|
|
98
|
+
};
|
|
99
|
+
// Read small indices locally; expand portable archives one family at a time.
|
|
91
100
|
const data={assets:{}};
|
|
101
|
+
try{
|
|
92
102
|
data.info=await json(PET_ASSETS.strideInfo);data.meta=await json(PET_ASSETS.stableInfo);
|
|
93
103
|
data.cache=await frames(frameFile('puppy-stride-tweens',artProfile));
|
|
94
104
|
for(const key of BUNDLES){
|
|
@@ -100,18 +110,20 @@ async function loadPetAssets(loadAsset,artProfile){
|
|
|
100
110
|
data.assets[key]={info,bytes:await frames(frameFile(base,artProfile),options)};
|
|
101
111
|
}
|
|
102
112
|
return data;
|
|
113
|
+
}catch(error){disposeAssets(data);throw error;}
|
|
103
114
|
}
|
|
115
|
+
function disposeAssets(data){data?.cache?.dispose?.();for(const family of Object.values(data?.assets??{}))family.bytes?.dispose?.();}
|
|
104
116
|
async function acquireAssets(loadAsset,artProfile){
|
|
105
117
|
let entry;
|
|
106
118
|
if(loadAsset===defaultLoadAsset){entry=sharedAssets.get(artProfile);if(!entry){entry={users:0,promise:loadPetAssets(loadAsset,artProfile)};sharedAssets.set(artProfile,entry);}}
|
|
107
119
|
else entry={users:0,promise:loadPetAssets(loadAsset,artProfile)};
|
|
108
120
|
entry.users++;
|
|
109
121
|
let released=false;
|
|
110
|
-
const release=()=>{if(released)return;released=true;const previous=entry;entry=null;if(--previous.users===0
|
|
111
|
-
try{
|
|
122
|
+
const release=()=>{if(released)return;released=true;const previous=entry;entry=null;if(--previous.users===0){if(sharedAssets.get(artProfile)===previous)sharedAssets.delete(artProfile);disposeAssets(previous.data);}};
|
|
123
|
+
try{const data=await entry.promise;entry.data=data;return{data,release};}catch(error){release();throw error;}
|
|
112
124
|
}
|
|
113
125
|
|
|
114
|
-
|
|
126
|
+
function petOptions({loadAsset=defaultLoadAsset,seed,energy,pawTransport=true,artProfile=DEFAULT_ART_PROFILE,returnStyle='auto'}={}){
|
|
115
127
|
if(typeof loadAsset!=='function')throw TypeError('loadAsset must be a function');
|
|
116
128
|
if(seed===undefined)seed=globalThis.crypto?.getRandomValues?globalThis.crypto.getRandomValues(new Uint32Array(1))[0]:731;
|
|
117
129
|
if(!Number.isInteger(seed)||seed<0||seed>0xffffffff)throw RangeError('seed must be a uint32');
|
|
@@ -119,7 +131,16 @@ export async function createPet({loadAsset=defaultLoadAsset,seed,energy,pawTrans
|
|
|
119
131
|
if(typeof pawTransport!=='boolean')throw TypeError('pawTransport must be boolean');
|
|
120
132
|
if(!ART_PROFILES.includes(artProfile))throw RangeError('artProfile must be original or q5');
|
|
121
133
|
if(!FETCH_RETURN_STYLES.includes(returnStyle))throw RangeError('returnStyle must be auto, brake, trot or run-trot');
|
|
122
|
-
|
|
134
|
+
return {loadAsset,seed,energy,pawTransport,artProfile,returnStyle};
|
|
135
|
+
}
|
|
136
|
+
export async function createPet(options={}){
|
|
137
|
+
const normalized=petOptions(options),lease=await acquireAssets(normalized.loadAsset,normalized.artProfile);
|
|
138
|
+
return createPetFromAssets(normalized,lease);
|
|
139
|
+
}
|
|
140
|
+
// Shared simulation/render core for local native hosts. The provider owns I/O.
|
|
141
|
+
export function createPetFromAssets(options,lease,kernel=null){
|
|
142
|
+
const {loadAsset,seed,energy,pawTransport,artProfile,returnStyle}=petOptions(options);
|
|
143
|
+
let data=lease.data;
|
|
123
144
|
const release=lease.release;lease=null;
|
|
124
145
|
let {cache,info,meta}=data;const assets={...data.assets};data=null;
|
|
125
146
|
// Baked tables describe the bundled Q5 source only. External/optional artwork
|
|
@@ -154,14 +175,14 @@ export async function createPet({loadAsset=defaultLoadAsset,seed,energy,pawTrans
|
|
|
154
175
|
if(typeof cellAspect!=='number'||!Number.isFinite(cellAspect)||cellAspect<=0)throw RangeError('cellAspect must be a positive number');
|
|
155
176
|
if(![12,24,30].includes(poseFps))throw RangeError('poseFps must be 12, 24 or 30');
|
|
156
177
|
const width=columns*2,height=rows*4,key=`${width}x${height}`;
|
|
157
|
-
if(paintKey!==key){paint=createPaperRenderer(cache,info,meta,width,height,{...assets,pawTransport,motionTables});paintKey=key;}
|
|
178
|
+
if(paintKey!==key){paint=createPaperRenderer(cache,info,meta,width,height,{...assets,pawTransport,motionTables,kernel});paintKey=key;}
|
|
158
179
|
const viewportWidth=camera?.viewportWidth??columns*cellAspect,viewportHeight=camera?.viewportHeight??rows;
|
|
159
180
|
const projection=createPaperProjection({viewportWidth,viewportHeight,rect:camera?.rect??{left:0,top:0,width:viewportWidth,height:viewportHeight},width,height});
|
|
160
181
|
const state=actor.sample(time,{poseFps}),report=life.report;
|
|
161
182
|
if(report.gesture)state.interactionPose=report.gesture;
|
|
162
183
|
const placement=projection.placement(state.x,state.z,state.bodyLift??0),pixels=paint(state,{...placement,contactEnabled:!!contactEnabled});
|
|
163
184
|
paintCompanionObjects(pixels,width,height,projection,report,state,paint.lastMouth);
|
|
164
|
-
const {text}=rgbaToBraille(pixels,width,height,{method:'stable',brightness:1.18,contrast:1.1});
|
|
185
|
+
const {text}=rgbaToBraille(pixels,width,height,{method:'stable',brightness:1.18,contrast:1.1,kernel});
|
|
165
186
|
lastHeldFrameKey=heldFrameKey(state,report,{columns,rows,cellAspect,camera,poseFps,contactEnabled});
|
|
166
187
|
return {text,columns,rows,pixels,state,report,placement,projection,artProfile,mouth:paint.lastMouth,contacts:paint.report()};
|
|
167
188
|
},
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|