{
  "sourceKind": "separate production-editor working copy; not a public GitHub commit",
  "reviewedAt": "2026-10-22",
  "excerpts": [
    {
      "file": "app/studio/hdr-merge.ts",
      "start": 5,
      "end": 8,
      "code": "export type HdrImage={width:number;height:number;data:Float32Array;alpha:Uint8ClampedArray};\nconst linear=(v:number)=>v<=.04045?v/12.92:((v+.055)/1.055)**2.4,encoded=(v:number)=>255*(v<=.0031308?v*12.92:1.055*Math.max(0,v)**(1/2.4)-.055);\nexport async function mergeHdr(images:ImageData[],exposures:number[],cancel=()=>false,ghost?:HdrGhostOptions):Promise<HdrImage>{if(images.length<2||images.length>8||images.length!==exposures.length||exposures.some(e=>!Number.isFinite(e)||Math.abs(e)>20)||new Set(exposures).size<2)throw Error('Use 2–8 exposures with distinct EV values between -20 and +20.');const w=images[0].width,h=images[0].height;if(w*h>4_000_000||images.some(im=>im.width!==w||im.height!==h))throw Error('HDR sources must have the same dimensions, up to 4 MP.');const ghostMask=ghost?await hdrGhostMask(images,exposures,ghost,cancel):null;const out=new Float32Array(w*h*3),alpha=new Uint8ClampedArray(w*h),factors=exposures.map(e=>2**e);for(let y=0;y<h;y++){if(y%32===0){await new Promise<void>(r=>setTimeout(r,0));if(cancel())throw Error('HDR merge cancelled.');}for(let x=0;x<w;x++){const i=y*w+x,k=i*4;alpha[i]=Math.max(...images.map(im=>im.data[k+3]));for(let ch=0;ch<3;ch++){let sum=0,total=0,best=-1,bestDistance=Infinity;for(let j=0;j<images.length;j++){const im=images[j].data,v=im[k+ch]/255,a=im[k+3]/255;if(!a)continue;const distance=Math.abs(v-.5),weight=Math.max(0,1-distance*2)*a;if(distance<bestDistance||(distance===bestDistance&&best>=0&&(v>.5?factors[j]<factors[best]:factors[j]>factors[best]))){bestDistance=distance;best=j;}sum+=linear(v)/factors[j]*weight;total+=weight;}out[i*3+ch]=total?sum/total:best>=0?linear(images[best].data[k+ch]/255)/factors[best]:0;if(ghost&&ghostMask){const m=ghostMask[i];out[i*3+ch]=out[i*3+ch]*(1-m)+linear(images[ghost.reference].data[k+ch]/255)/factors[ghost.reference]*m;}}}}if(cancel())throw Error('HDR merge cancelled.');return {width:w,height:h,data:out,alpha};}\nexport function toneMapHdr(hdr:HdrImage,exposure=0,whitePoint=4){if(!Number.isFinite(exposure)||Math.abs(exposure)>10||!Number.isFinite(whitePoint)||whitePoint<1||whitePoint>32)throw Error('Invalid tone mapping.');const c=surface(hdr.width,hdr.height),g=c.getContext('2d')!,im=g.createImageData(c.width,c.height),gain=2**exposure;for(let i=0;i<hdr.alpha.length;i++){const r=hdr.data[i*3]*gain,green=hdr.data[i*3+1]*gain,b=hdr.data[i*3+2]*gain,y=.2126*r+.7152*green+.0722*b,mapped=y*(1+y/(whitePoint*whitePoint))/(1+y),scale=y?mapped/y:1;im.data[i*4]=encoded(r*scale);im.data[i*4+1]=encoded(green*scale);im.data[i*4+2]=encoded(b*scale);im.data[i*4+3]=hdr.alpha[i];}g.putImageData(im,0,0);return c;}",
      "ref": "working-copy-2026-10-22",
      "sha256": "07363b7add98d8f575e738c5cfce2631007a3ccdb4cc59be3857bab532d9f1d5"
    },
    {
      "file": "app/studio/hdr-merge.ts",
      "start": 9,
      "end": 12,
      "code": "export function hdrDocument(hdr:HdrImage,exposure:number,whitePoint:number,name='HDR Merge'):StudioDoc{const c=toneMapHdr(hdr,exposure,whitePoint);return {...blankDoc(),version:6,name,width:c.width,height:c.height,sourceFile:{name:name+'.hdr',mode:3,depth:32,warnings:['Tone-mapped editing document is 8-bit sRGB. Export Radiance HDR from the merge dialog to retain the linear high dynamic range data.']},layers:[makeLayer({name,kind:'image',width:c.width,height:c.height,src:c.toDataURL()})]};}\nexport function encodeRadiance(hdr:HdrImage){const {width:w,height:h,data,alpha}=hdr;if(!Number.isInteger(w)||!Number.isInteger(h)||h<1||w<8||w>16384||data.length!==w*h*3||alpha.length!==w*h)throw Error('Radiance export requires width 8–16,384 pixels.');const header=new TextEncoder().encode('#?RADIANCE\\nFORMAT=32-bit_rle_rgbe\\n\\n-Y '+h+' +X '+w+'\\n'),chunks:Uint8Array[]=[header];for(let y=0;y<h;y++){const row=new Uint8Array(w*4);for(let x=0;x<w;x++){const i=y*w+x,a=alpha[i]/255,r=data[i*3]*a,g=data[i*3+1]*a,b=data[i*3+2]*a,max=Math.max(r,g,b);if(![r,g,b].every(v=>Number.isFinite(v)&&v>=0))throw Error('Invalid HDR radiance.');if(max<1e-32)continue;const exponent=Math.floor(Math.log2(max))+1,scale=256/2**exponent;row[x]=Math.min(255,Math.floor(r*scale));row[w+x]=Math.min(255,Math.floor(g*scale));row[w*2+x]=Math.min(255,Math.floor(b*scale));row[w*3+x]=exponent+128;}const encoded=new Uint8Array(4+w*4+Math.ceil(w/128)*4);encoded.set([2,2,w>>8,w&255]);let position=4;for(let c=0;c<4;c++)for(let x=0;x<w;x+=128){const count=Math.min(128,w-x);encoded[position++]=count;encoded.set(row.subarray(c*w+x,c*w+x+count),position);position+=count;}chunks.push(encoded);}return new Blob(chunks as BlobPart[],{type:'image/vnd.radiance'});}\n\nexport async function hdrPrecisionDocument(hdr:HdrImage,name='HDR Merge 32-bit'):Promise<StudioDoc>{const data=new Float32Array(hdr.width*hdr.height*4);for(let i=0;i<hdr.alpha.length;i++){data.set(hdr.data.subarray(i*3,i*3+3),i*4);data[i*4+3]=hdr.alpha[i]/255;}const raster={width:hdr.width,height:hdr.height,space:'RGB' as const,depth:32 as const,data},canvas=await precisionCanvas(raster),src=canvas.toDataURL();return {...blankDoc(),version:6,name,width:hdr.width,height:hdr.height,precision:{space:'RGB',depth:32},sourceFile:{name:name+'.hdr',mode:3,depth:32,warnings:['Linear HDR retained in native 32-bit RGB. Screen preview clips display highlights; use Precision Channels to adjust exposure.']},layers:[makeLayer({name,kind:'image',width:hdr.width,height:hdr.height,src,precision:packPrecision(raster),precisionSource:precisionSource(src)})]};}",
      "ref": "working-copy-2026-10-22",
      "sha256": "07363b7add98d8f575e738c5cfce2631007a3ccdb4cc59be3857bab532d9f1d5"
    },
    {
      "file": "app/studio/panorama-merge.ts",
      "start": 1,
      "end": 3,
      "code": "import {blankDoc,makeLayer,composite,type StudioDoc} from './document.ts';import {autoAlign} from './auto-align.ts';import {autoBlend} from './auto-blend.ts';import {projectedCopies} from './panorama-projection.ts';import {quadPoints} from './photoshop-engine.ts';\nexport type PanoramaOptions={projection:'reposition'|'perspective'|'cylindrical'|'spherical';fieldOfView:number;matchColors:boolean};\nexport async function mergePanorama(inputs:StudioDoc[],options:PanoramaOptions,signal:AbortSignal,progress=(message:string)=>{}):Promise<StudioDoc>{if(inputs.length<2||inputs.length>8||!['reposition','perspective','cylindrical','spherical'].includes(options.projection)||!Number.isFinite(options.fieldOfView)||options.fieldOfView<10||options.fieldOfView>150)throw Error('Use 2–8 ordered overlapping photos and a valid projection.');if(inputs.reduce((sum,d)=>sum+d.width*d.height,0)>32_000_000)throw Error('Panorama inputs support up to 32 MP in total.');const check=()=>{if(signal.aborted)throw new DOMException('Panorama cancelled.','AbortError');};let doc:StudioDoc={...blankDoc(),name:'Panorama',width:Math.max(...inputs.map(d=>d.width)),height:Math.max(...inputs.map(d=>d.height)),resolution:inputs[0].resolution,layers:[]};for(let i=0;i<inputs.length;i++){check();progress('Preparing photo '+(i+1)+' / '+inputs.length+'…');const c=await composite(inputs[i]);doc.layers.push(makeLayer({kind:'image',name:inputs[i].name,width:c.width,height:c.height,src:c.toDataURL()}));}let ids=doc.layers.map(l=>l.id);if(options.projection==='cylindrical'||options.projection==='spherical'){progress('Projecting photographs…');const result=await projectedCopies(doc,ids,options.projection,options.fieldOfView,signal);doc={...result.doc,layers:[...result.doc.layers.filter(l=>!result.ids.includes(l.id)),...result.doc.layers.filter(l=>result.ids.includes(l.id))]};ids=result.ids;}for(let i=1;i<ids.length;i++){check();progress('Aligning overlap '+i+' / '+(ids.length-1)+'…');const result=await autoAlign(doc,[ids[i-1],ids[i]],ids[i-1],signal,undefined,options.projection==='perspective'?'perspective':'reposition');doc=result.doc;}const points=doc.layers.filter(l=>ids.includes(l.id)).flatMap(quadPoints),left=Math.floor(Math.min(...points.map(p=>p.x))),top=Math.floor(Math.min(...points.map(p=>p.y))),right=Math.ceil(Math.max(...points.map(p=>p.x))),bottom=Math.ceil(Math.max(...points.map(p=>p.y))),width=right-left,height=bottom-top;if(width<1||height<1||width>16384||height>16384||width*height>16_000_000)throw Error('Merged panorama exceeds 16 MP or 16,384 pixels per side. Use smaller inputs.');doc={...doc,width,height,layers:doc.layers.map(l=>({...l,x:l.x-left,y:l.y-top}))};check();progress('Blending overlaps…');doc=await autoBlend(doc,ids,{mode:'panorama',feather:3,matchColors:options.matchColors,color:true,signal});check();return {...doc,name:'Panorama',version:6,sourceFile:{name:'Panorama',mode:3,depth:8,warnings:['Ordered photos merged as 8-bit sRGB layers with editable overlap masks. Input files are unchanged. Automatic subject ghost removal is not applied.']}};}",
      "ref": "working-copy-2026-10-22",
      "sha256": "9920684c46fd6635911820c84c2bab91a40c9164d6544f6308170eaea6fdd018"
    },
    {
      "file": "app/studio/hdr-deghost.ts",
      "start": 1,
      "end": 4,
      "code": "const linear=(v:number)=>v<=.04045?v/12.92:((v+.055)/1.055)**2.4;\nexport type HdrGhostOptions={reference:number;threshold:number};\n/** Uses the selected capture in regions whose exposure-normalized colors disagree. */\nexport async function hdrGhostMask(images:ImageData[],ev:number[],options:HdrGhostOptions,cancel=()=>false){",
      "ref": "working-copy-2026-10-22",
      "sha256": "d3f2b50cc22fb0efb756a02e60c440083bebeaaa11794c2e83f01da0b1db42ef"
    }
  ],
  "productionUI": {
    "episode": 29,
    "checkedAt": "2026-10-22",
    "input": "AI-generated fictional adult; three synthetic exposure variants, not camera brackets",
    "hdr": {
      "settings": {
        "EV": [
          -1,
          0,
          1
        ],
        "alignment": false,
        "deghost": false,
        "previewExposure": 0,
        "whitePoint": 4
      },
      "precision": {
        "space": "RGB",
        "depth": 32
      },
      "allFloat32Finite": true,
      "rgbMin": 0.0,
      "rgbMax": 0.9144884347915649,
      "weightedMergeMaxError": 2.8542925911700934e-08,
      "radianceHeaderValid": true,
      "radianceDecodedMaxError": 0.003868609666824341,
      "projectRoundtripExact": true
    },
    "panorama": {
      "projection": "reposition",
      "matchColors": true,
      "inputs": [
        1024,
        1024
      ],
      "overlapPixels": 512,
      "inputCropsExact": true,
      "outputDimensions": [
        1536,
        1024
      ],
      "outputRGBAExactlyMatchesOriginal": true,
      "maxPixelDifference": 0,
      "originalLayerPixelsPreserved": true,
      "layers": [
        {
          "name": "pano-left",
          "x": 0,
          "y": 0,
          "visible": false,
          "mask": false
        },
        {
          "name": "pano-right",
          "x": 512,
          "y": 0,
          "visible": false,
          "mask": false
        },
        {
          "name": "pano-left (Color Matched)",
          "x": 0,
          "y": 0,
          "visible": true,
          "mask": true
        },
        {
          "name": "pano-right (Color Matched)",
          "x": 512,
          "y": 0,
          "visible": true,
          "mask": true
        }
      ],
      "projectRoundtripExact": true
    },
    "passed": true
  },
  "historicalReports": [
    {
      "file": "projective-align-render-results.json",
      "sha256": "01c4f06cb913035ec69d7ee4249d447d015470f308995c65ee4393c450ba2b70",
      "results": {
        "passed": true,
        "checks": [
          "Known projective transform recovered within 1.5 pixels",
          "Cancellation"
        ],
        "cornerErrors": [
          0.03440737762671381,
          0.02490431013142308,
          0.1575810656553454,
          0.07036412508982129
        ],
        "confidence": 0.995222916862952
      },
      "freshRun": false
    },
    {
      "file": "projection-workflows-render-results.json",
      "sha256": "8b0175d411ca98ec7dfeb29c3af0734768a80eaf06dbd3bb6f67899ea5d6be18",
      "results": {
        "passed": true,
        "checks": [
          "cylindrical: inverse camera projection, preserved hidden originals, aligned copies and PSD/PSB exact roundtrip",
          "spherical: inverse camera projection, preserved hidden originals, aligned copies and PSD/PSB exact roundtrip"
        ]
      },
      "freshRun": false
    },
    {
      "file": "raw-hdr-precision-results.json",
      "sha256": "98eaf9d8b83ed28573a89076854dbe6d074cdf125c61a72205aab134bd66f681",
      "results": {
        "passed": true,
        "rawNative16ExactSamples": true,
        "rawProjectPrecisionRoundTrip": true,
        "hdrNative32AboveOne": true,
        "validDocuments": true
      },
      "freshRun": false
    }
  ]
}