@hraness/slopcamera 3.6.0 → 3.8.0

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 (57) hide show
  1. package/README.md +49 -33
  2. package/apps/desktop/README.md +3 -10
  3. package/apps/desktop/application/html-overlay-renderer.ts +6 -0
  4. package/apps/desktop/application/operation.ts +1 -1
  5. package/apps/desktop/application/spatial-assets.ts +1 -1
  6. package/apps/desktop/cli/{menubar-status.ts → activity-status.ts} +24 -21
  7. package/apps/desktop/cli/args.ts +62 -10
  8. package/apps/desktop/cli/command-host-resources.ts +5 -1
  9. package/apps/desktop/cli/commands.ts +26 -9
  10. package/apps/desktop/cli/desktop-status.ts +271 -0
  11. package/apps/desktop/cli/help.ts +36 -28
  12. package/apps/desktop/cli/html-film-init.ts +158 -0
  13. package/apps/desktop/cli/html-film-names.ts +17 -0
  14. package/apps/desktop/cli/html-film.ts +471 -0
  15. package/apps/desktop/cli/html-overlay-renderer.ts +29 -2
  16. package/apps/desktop/cli/html-scene.ts +3 -3
  17. package/apps/desktop/cli/legacy-login.ts +184 -0
  18. package/apps/desktop/cli/main.ts +9 -0
  19. package/apps/desktop/cli/outputs.ts +19 -0
  20. package/apps/desktop/cli/portable-surface.ts +55 -1
  21. package/apps/desktop/cli/registry.ts +314 -0
  22. package/apps/desktop/cli/verbs.ts +193 -0
  23. package/apps/desktop/dist/cli/main.js +338 -316
  24. package/apps/desktop/html-overlay/film.ts +418 -0
  25. package/dist/cli.js +13 -2
  26. package/dist/generate.js +1 -1
  27. package/dist/{index-4z9y2wa2.js → index-842t02pp.js} +1 -1
  28. package/dist/{index-qxfexvj3.js → index-k9bjw80z.js} +20 -4
  29. package/dist/index-p74xy76d.js +3 -0
  30. package/dist/{index-fevf4ppk.js → index-y8spxs77.js} +10 -7
  31. package/dist/index.js +1 -1
  32. package/dist/operations.js +1 -1
  33. package/dist/workflow.js +1 -1
  34. package/docs/README.md +6 -6
  35. package/examples/html/launch-film/README.md +51 -0
  36. package/examples/html/launch-film/build.ts +158 -0
  37. package/examples/html/launch-film/film.css +593 -0
  38. package/examples/html/launch-film/film.html +85 -0
  39. package/examples/html/launch-film/film.js +410 -0
  40. package/examples/html/launch-film/film.json +63 -0
  41. package/examples/html/launch-film/mockups.tsx +74 -0
  42. package/examples/html/launch-film/timeline.ts +57 -0
  43. package/package.json +24 -11
  44. package/skills/slopcamera/SKILL.md +4 -3
  45. package/skills/slopcamera/references/install.md +3 -3
  46. package/skills/slopcamera/references/launch-films.md +158 -0
  47. package/skills/slopcamera/references/reference-led-3d.md +1 -1
  48. package/skills/slopcamera/references/visual-communication.md +1 -1
  49. package/src/cli.ts +125 -9
  50. package/src/generation-pricing.ts +21 -1
  51. package/src/icon-set.ts +1086 -0
  52. package/src/icon.ts +396 -59
  53. package/src/index.ts +1 -0
  54. package/src/operations.ts +52 -0
  55. package/src/version.ts +1 -1
  56. package/apps/desktop/cli/menubar.ts +0 -272
  57. package/dist/index-nxtmy18t.js +0 -3
@@ -0,0 +1,3 @@
1
+ // @bun
2
+ import{__require}from"./index-80a36bc0.js";import{createHash,randomUUID}from"crypto";import{link,rm,writeFile}from"fs/promises";import{dirname,extname,resolve}from"path";class SlopcameraCloudError extends Error{code;constructor(code,message,options){super(`[${code}] ${message}`,options);this.name="SlopcameraCloudError";this.code=code}}var slopcameraImageModelPrices=Object.freeze({"openai/gpt-image-1":Object.freeze({kind:"tokens",textInputPerMillion:5000000,outputPerMillion:40000000,worstImage:250000,source:"https://developers.openai.com/api/docs/models/gpt-image-1"}),"openai/gpt-image-1-mini":Object.freeze({kind:"tokens",textInputPerMillion:2000000,outputPerMillion:8000000,worstImage:52000,source:"https://developers.openai.com/api/docs/models/gpt-image-1-mini"}),"openai/gpt-image-1.5":Object.freeze({kind:"tokens",textInputPerMillion:5000000,outputPerMillion:32000000,worstImage:200000,source:"https://developers.openai.com/api/docs/models/gpt-image-1.5"}),"recraft/recraft-v4.1-utility":Object.freeze({kind:"image",perImage:35000,source:"https://vercel.com/ai-gateway/models/recraft-v4.1-utility"}),"bfl/flux-pro-1.1":Object.freeze({kind:"image",perImage:40000,source:"https://vercel.com/ai-gateway/models/flux-pro-1.1"}),"bfl/flux-pro-1.1-ultra":Object.freeze({kind:"image",perImage:60000,source:"https://vercel.com/ai-gateway/models/flux-pro-1.1-ultra"}),"bfl/flux-kontext-pro":Object.freeze({kind:"image",perImage:40000,source:"https://vercel.com/ai-gateway/models/flux-kontext-pro"}),"bfl/flux-kontext-max":Object.freeze({kind:"image",perImage:80000,source:"https://vercel.com/ai-gateway/models/flux-kontext-max"})});var slopcameraPromptTokenAllowance=64;var slopcameraCostProvider="vercel-ai-gateway";function slopcameraImageModelPrice(model){return Object.hasOwn(slopcameraImageModelPrices,model)?slopcameraImageModelPrices[model]:undefined}function tokenCost(tokens,perMillion){return Math.ceil(tokens*perMillion/1e6)}function promptBytes(prompt){return Buffer.byteLength(prompt,"utf8")}function validTokenCount(value){return typeof value==="number"&&Number.isSafeInteger(value)&&value>=0}function estimatedLines(model,prompt,price){if(price.kind==="image"){return[{provider:slopcameraCostProvider,operation:`${model} image`,microUsd:price.perImage,basis:"estimated"}]}return[{provider:slopcameraCostProvider,operation:`${model} image output`,microUsd:price.worstImage,basis:"estimated"},{provider:slopcameraCostProvider,operation:`${model} text input`,microUsd:tokenCost(promptBytes(prompt)+slopcameraPromptTokenAllowance,price.textInputPerMillion),basis:"estimated"}]}function imageCallCosts(model,prompt,usage){const price=slopcameraImageModelPrice(model);if(price===undefined){return[{provider:slopcameraCostProvider,operation:`${model} image`,microUsd:0,basis:"unknown"}]}if(price.kind==="tokens"&&usage!==undefined&&validTokenCount(usage.inputTokens)&&validTokenCount(usage.outputTokens)&&usage.outputTokens>0){return[{provider:slopcameraCostProvider,operation:`${model} image output`,microUsd:tokenCost(usage.outputTokens,price.outputPerMillion),basis:"reported"},{provider:slopcameraCostProvider,operation:`${model} text input`,microUsd:tokenCost(usage.inputTokens,price.textInputPerMillion),basis:"reported"}]}return estimatedLines(model,prompt,price)}var slopcameraGatewayApiBaseUrl="https://ai-gateway.vercel.sh/v4/ai";var slopcameraImageModels=Object.freeze(["openai/gpt-image-1.5","recraft/recraft-v4.1-utility"]);var slopcameraResponseMediaTypes=Object.freeze(["image/png","image/jpeg","image/webp"]);var slopcameraMaximumPromptBytes=32*1024;var slopcameraMaximumRawImageBytes=64*1024*1024;var defaultGenerationTimeoutMs=5*60000;var maximumGenerationTimeoutMs=30*60000;var defaultMaximumGatewayResponseBytes=96*1024*1024;function invalidArgument(message){throw new SlopcameraCloudError("INVALID_ARGUMENT",message)}function credentialValue(value){if(value.length<16||value.length>16*1024||value.trim()!==value||/[^\x21-\x7e]/u.test(value)){throw new SlopcameraCloudError("AUTHENTICATION_REQUIRED","The selected Vercel AI Gateway credential is invalid.")}return value}function environment(injected){return injected??process.env}function resolveSlopcameraGatewayCredential(injected){const values=environment(injected);if(values.AI_GATEWAY_API_KEY!==undefined){return{source:"AI_GATEWAY_API_KEY",token:credentialValue(values.AI_GATEWAY_API_KEY)}}if(values.VERCEL_OIDC_TOKEN!==undefined){return{source:"VERCEL_OIDC_TOKEN",token:credentialValue(values.VERCEL_OIDC_TOKEN)}}throw new SlopcameraCloudError("AUTHENTICATION_REQUIRED","Set AI_GATEWAY_API_KEY or run Slopcamera through `vercel env run -- \u2026` with VERCEL_OIDC_TOKEN available.")}function slopcameraGatewayCredentialStatus(injected){const values=environment(injected);if(values.AI_GATEWAY_API_KEY!==undefined){credentialValue(values.AI_GATEWAY_API_KEY);return{available:true,source:"AI_GATEWAY_API_KEY"}}if(values.VERCEL_OIDC_TOKEN!==undefined){credentialValue(values.VERCEL_OIDC_TOKEN);return{available:true,source:"VERCEL_OIDC_TOKEN"}}return{available:false,source:null}}function validateModel(value){if(typeof value!=="string"||value.length<3||value.length>256||!/^[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._:-]*$/iu.test(value)){invalidArgument("Model must be a bounded Vercel AI Gateway provider/model id.")}return value}function validatePrompt(value){if(typeof value!=="string"||value.trim().length===0||/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/u.test(value)||Buffer.byteLength(value,"utf8")>slopcameraMaximumPromptBytes){invalidArgument(`Prompt must be non-empty and no more than ${slopcameraMaximumPromptBytes} UTF-8 bytes.`)}return value}function validateTimeout(value){const timeout=value??defaultGenerationTimeoutMs;if(!Number.isSafeInteger(timeout)||timeout<1000||timeout>maximumGenerationTimeoutMs){invalidArgument(`timeoutMs must be an integer from 1000 through ${maximumGenerationTimeoutMs}.`)}return timeout}function isObject(value){return typeof value==="object"&&value!==null||typeof value==="function"}function parseFunction(value){if(typeof value!=="function"){throw new SlopcameraCloudError("GENERATION_FAILED","The Vercel AI Gateway runtime is unavailable.")}return value}function disableAiSdkWarningLogging(){globalThis.AI_SDK_LOG_WARNINGS=false}function assertGenerationActive(signal){if(signal.aborted){throw new SlopcameraCloudError("GENERATION_FAILED","Vercel AI Gateway image generation was cancelled or exceeded its deadline.")}}async function loadDefaultGatewayRuntime(){let aiModule;let gatewayModule;try{[aiModule,gatewayModule]=await Promise.all([import("ai-v7"),import("@ai-sdk/gateway-v4")])}catch{throw new SlopcameraCloudError("GENERATION_FAILED","The Vercel AI Gateway runtime is unavailable.")}if(!isObject(aiModule)||!isObject(gatewayModule)){throw new SlopcameraCloudError("GENERATION_FAILED","The Vercel AI Gateway runtime is unavailable.")}const generateImage=parseFunction(aiModule.generateImage);if(typeof gatewayModule.createGateway!=="function"){throw new SlopcameraCloudError("GENERATION_FAILED","The Vercel AI Gateway runtime is unavailable.")}const createGateway=gatewayModule.createGateway;return{createGateway:(settings)=>{const provider=createGateway(settings);if(!isObject(provider)||typeof provider.imageModel!=="function"){throw new SlopcameraCloudError("GENERATION_FAILED","The Vercel AI Gateway runtime is unavailable.")}const imageModel=provider.imageModel;return{imageModel:(modelId)=>imageModel.call(provider,modelId)}},generateImage}}function gatewayUrl(input){try{if(input instanceof Request)return new URL(input.url);if(input instanceof URL)return new URL(input.href);return new URL(input,slopcameraGatewayApiBaseUrl)}catch{throw new SlopcameraCloudError("GENERATION_FAILED","The Vercel AI Gateway request was rejected.")}}function canonicalGatewayInput(input,url){if(!(input instanceof Request))return url;const snapshot=new Request(input);const body=snapshot.method==="GET"||snapshot.method==="HEAD"?undefined:snapshot.body??undefined;const requestInit={headers:snapshot.headers,method:snapshot.method,signal:snapshot.signal,...body===undefined?{}:{body,duplex:"half"}};return new Request(url.href,requestInit)}function boundedResponse(response,maximumBytes){const declared=response.headers.get("content-length");if(declared!==null){const value=Number(declared);if(!Number.isSafeInteger(value)||value<0||value>maximumBytes){response.body?.cancel().catch(()=>{return});throw new SlopcameraCloudError("GENERATION_INVALID_RESPONSE","Vercel AI Gateway returned an invalid bounded response.")}}if(response.body===null)return response;const reader=response.body.getReader();let bytes=0;const body=new ReadableStream({cancel:async(reason)=>await reader.cancel(reason),pull:async(controller)=>{try{const next=await reader.read();if(next.done){reader.releaseLock();controller.close();return}bytes+=next.value.byteLength;if(bytes>maximumBytes){await reader.cancel().catch(()=>{return});controller.error(new SlopcameraCloudError("GENERATION_INVALID_RESPONSE","Vercel AI Gateway returned an invalid bounded response."));return}controller.enqueue(next.value)}catch{controller.error(new SlopcameraCloudError("GENERATION_INVALID_RESPONSE","Vercel AI Gateway returned an invalid bounded response."))}}});return new Response(body,{headers:response.headers,status:response.status,statusText:response.statusText})}function createFixedGatewayFetch(options={}){const fetchImplementation=options.fetch??globalThis.fetch;const maximumResponseBytes=options.maximumResponseBytes??defaultMaximumGatewayResponseBytes;if(!Number.isSafeInteger(maximumResponseBytes)||maximumResponseBytes<1||maximumResponseBytes>1024*1024*1024){invalidArgument("maximumResponseBytes is outside the supported range.")}const fixed=new URL(slopcameraGatewayApiBaseUrl);return async(input,init)=>{const url=gatewayUrl(input);if(url.origin!==fixed.origin||url.pathname!==fixed.pathname&&!url.pathname.startsWith(`${fixed.pathname}/`)||url.username!==""||url.password!==""){throw new SlopcameraCloudError("GENERATION_FAILED","The Vercel AI Gateway request was rejected.")}const canonicalInput=canonicalGatewayInput(input,url);let response;try{response=await fetchImplementation(canonicalInput,{...init,redirect:"error"})}catch(error){if(error instanceof SlopcameraCloudError)throw error;throw new SlopcameraCloudError("GENERATION_FAILED","Vercel AI Gateway image generation failed; the request was not retried.")}if(response.redirected||response.status>=300&&response.status<400){await response.body?.cancel().catch(()=>{return});throw new SlopcameraCloudError("GENERATION_FAILED","The Vercel AI Gateway request was rejected.")}return boundedResponse(response,maximumResponseBytes)}}function combineSignals(caller,timeoutMs){const controller=new AbortController;const abort=()=>controller.abort(caller?.reason);let rejectInterruption;const interruption=new Promise((_resolve,reject)=>{rejectInterruption=reject});const rejectOnAbort=()=>{rejectInterruption?.(new SlopcameraCloudError("GENERATION_FAILED","Vercel AI Gateway image generation was cancelled or exceeded its deadline."))};controller.signal.addEventListener("abort",rejectOnAbort,{once:true});if(caller?.aborted===true)abort();caller?.addEventListener("abort",abort,{once:true});const timer=setTimeout(()=>controller.abort(),timeoutMs);return{dispose:()=>{clearTimeout(timer);caller?.removeEventListener("abort",abort);controller.signal.removeEventListener("abort",rejectOnAbort);rejectInterruption=undefined},interruption,signal:controller.signal}}function mediaType(value){if(typeof value!=="string"||!slopcameraResponseMediaTypes.includes(value)){throw new SlopcameraCloudError("GENERATION_INVALID_RESPONSE","Vercel AI Gateway returned an unsupported image type.")}return value}function validImageBytes(bytes,type){if(bytes.byteLength<12||bytes.byteLength>slopcameraMaximumRawImageBytes){return false}if(type==="image/png"){return Buffer.from(bytes.subarray(0,8)).equals(Buffer.from([137,80,78,71,13,10,26,10]))}if(type==="image/jpeg"){return bytes[0]===255&&bytes[1]===216&&bytes.at(-2)===255&&bytes.at(-1)===217}return bytes.byteLength>=16&&Buffer.from(bytes.subarray(0,4)).toString("ascii")==="RIFF"&&Buffer.from(bytes).readUInt32LE(4)===bytes.byteLength-8&&Buffer.from(bytes.subarray(8,12)).toString("ascii")==="WEBP"&&["VP8 ","VP8L","VP8X"].includes(Buffer.from(bytes.subarray(12,16)).toString("ascii"))}function warningReceipt(value){let type="provider-warning";let detail="Provider warning";if(typeof value==="string"){detail=value}else if(isObject(value)){if(typeof value.type==="string"&&value.type.length<=64){type=value.type.replace(/[^a-z0-9._-]/giu,"-")||type}if(typeof value.message==="string")detail=value.message;else if(typeof value.details==="string")detail=value.details}return`${type} sha256:${createHash("sha256").update(detail).digest("hex")}`}function returnedUsage(value){if(!isObject(value)||!isObject(value.usage))return;const{inputTokens,outputTokens}=value.usage;return{inputTokens:typeof inputTokens==="number"?inputTokens:undefined,outputTokens:typeof outputTokens==="number"?outputTokens:undefined}}function parseResult(value,model){if(!isObject(value)||!Array.isArray(value.images)||value.images.length!==1){throw new SlopcameraCloudError("GENERATION_INVALID_RESPONSE","Vercel AI Gateway did not return exactly one image.")}const image=value.images[0];if(!isObject(image)||!(image.uint8Array instanceof Uint8Array)){throw new SlopcameraCloudError("GENERATION_INVALID_RESPONSE","Vercel AI Gateway returned an invalid bounded image.")}const type=mediaType(image.mediaType);if(!validImageBytes(image.uint8Array,type)){throw new SlopcameraCloudError("GENERATION_INVALID_RESPONSE","Vercel AI Gateway returned an invalid bounded image.")}const gateway=isObject(value.providerMetadata)&&isObject(value.providerMetadata.gateway)?value.providerMetadata.gateway:undefined;const foreignGenerationId=gateway!==undefined&&typeof gateway.generationId==="string"&&gateway.generationId.length>0&&gateway.generationId.length<=256&&!/[\u0000-\u001f\u007f]/u.test(gateway.generationId)?gateway.generationId:randomUUID();const requestId=`sha256:${createHash("sha256").update("slopcamera.gateway-generation-id/v1\x00").update(foreignGenerationId).digest("hex")}`;const warnings=Array.isArray(value.warnings)?value.warnings.slice(0,100).map(warningReceipt):[];return{bytes:image.uint8Array,response:{image:{base64:Buffer.from(image.uint8Array).toString("base64"),mediaType:type},model,provider:"vercel-ai-gateway",requestId,warnings}}}async function performGeneration(input,dependencies){const model=validateModel(input.model);const prompt=validatePrompt(input.prompt);const credential=resolveSlopcameraGatewayCredential(dependencies.environment);const timeout=combineSignals(input.signal,validateTimeout(input.timeoutMs));let dispatched=false;let rejected=false;let reported=false;const report=(usage)=>{if(reported||!dispatched||rejected)return;reported=true;dependencies.onProviderCost?.(imageCallCosts(model,prompt,usage))};const observedFetch=async(request,init)=>{const response=await(dependencies.fetch??globalThis.fetch)(request,init);if(response.status>=400&&response.status<500)rejected=true;return response};try{const generation=(async()=>{assertGenerationActive(timeout.signal);disableAiSdkWarningLogging();const runtime=await(dependencies.loadRuntime??loadDefaultGatewayRuntime)();assertGenerationActive(timeout.signal);const provider=runtime.createGateway({apiKey:credential.token,baseURL:slopcameraGatewayApiBaseUrl,fetch:createFixedGatewayFetch({fetch:observedFetch,...dependencies.maximumResponseBytes===undefined?{}:{maximumResponseBytes:dependencies.maximumResponseBytes}})});assertGenerationActive(timeout.signal);dispatched=true;const generated=await runtime.generateImage({abortSignal:timeout.signal,maxRetries:0,model:provider.imageModel(model),n:1,prompt});report(returnedUsage(generated));return parseResult(generated,model)})();return await Promise.race([generation,timeout.interruption])}catch(error){report(undefined);if(error instanceof SlopcameraCloudError)throw error;throw new SlopcameraCloudError("GENERATION_FAILED","Vercel AI Gateway image generation failed; the request was not retried.")}finally{timeout.dispose()}}async function generateSlopcameraImage(input,dependencies={}){return(await performGeneration(input,dependencies)).response}function expectedMediaType(outputPath){const extension=extname(outputPath).toLocaleLowerCase("en-US");if(extension===".png")return"image/png";if(extension===".jpg"||extension===".jpeg")return"image/jpeg";if(extension===".webp")return"image/webp";invalidArgument("Output path must end in .png, .jpg, .jpeg, or .webp.")}async function atomicImageWrite(outputPath,bytes){const absolutePath=resolve(outputPath);const temporaryPath=resolve(dirname(absolutePath),`.${randomUUID()}.slopcamera-generate.tmp`);try{await writeFile(temporaryPath,bytes,{flag:"wx"});await link(temporaryPath,absolutePath);return absolutePath}catch{throw new SlopcameraCloudError("OUTPUT_WRITE_FAILED","Slopcamera could not atomically write the generated image.")}finally{await rm(temporaryPath,{force:true}).catch(()=>{return})}}async function generateSlopcameraImageFile(input,dependencies={}){if(typeof input.outputPath!=="string"||input.outputPath.length<1||input.outputPath.length>4096||input.outputPath.includes("\x00")){invalidArgument("Output path must be a non-empty local path.")}const expected=expectedMediaType(input.outputPath);const generated=await performGeneration(input,dependencies);if(generated.response.image.mediaType!==expected){throw new SlopcameraCloudError("GENERATION_INVALID_RESPONSE",`Generated ${generated.response.image.mediaType} does not match the requested ${expected} output path.`)}const outputPath=await atomicImageWrite(input.outputPath,generated.bytes);return{bytes:generated.bytes.byteLength,mediaType:generated.response.image.mediaType,model:generated.response.model,outputPath,provider:generated.response.provider,requestId:generated.response.requestId,sha256:createHash("sha256").update(generated.bytes).digest("hex"),warnings:generated.response.warnings}}
3
+ export{SlopcameraCloudError,slopcameraGatewayApiBaseUrl,slopcameraImageModels,slopcameraResponseMediaTypes,slopcameraMaximumPromptBytes,slopcameraMaximumRawImageBytes,resolveSlopcameraGatewayCredential,slopcameraGatewayCredentialStatus,createFixedGatewayFetch,generateSlopcameraImage,generateSlopcameraImageFile};
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- import{VectorizeDeadline,encodeTracePng,loadRaster,normalizedHexColor,parseHexColor,resolveVectorizeLimits,vectorizeHardLimits,vectorizeImage}from"./index-mnp0bftq.js";import{SlopcameraCloudError,createFixedGatewayFetch,generateSlopcameraImage,generateSlopcameraImageFile,resolveSlopcameraGatewayCredential,slopcameraGatewayApiBaseUrl,slopcameraImageModels,slopcameraMaximumPromptBytes}from"./index-nxtmy18t.js";import{createDefaultHostResourceCoordinator}from"./index-b3geqczn.js";import{__require}from"./index-80a36bc0.js";import{randomUUID as randomUUID3}from"crypto";import{mkdir as mkdir3,readFile as readFile2,rename as rename2,rm as rm3,writeFile as writeFile3}from"fs/promises";import{dirname as dirname3,join as join2,resolve as resolve3}from"path";var shared='fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"';var builtInIcons=Object.freeze({brain:{viewBox:"0 0 24 24",body:`<path ${shared} d="M9.5 4.5A3.5 3.5 0 0 0 6 8v.35A3.5 3.5 0 0 0 5.5 15 3.5 3.5 0 0 0 9 19.5M14.5 4.5A3.5 3.5 0 0 1 18 8v.35a3.5 3.5 0 0 1 .5 6.65 3.5 3.5 0 0 1-3.5 4.5M9.5 4.5v15M14.5 4.5v15M6 9.5h3.5M14.5 14.5H18"/>`},check:{viewBox:"0 0 24 24",body:`<path ${shared} d="m5 12 4.25 4.25L19 6.5"/>`},code:{viewBox:"0 0 24 24",body:`<path ${shared} d="m8.5 7-5 5 5 5M15.5 7l5 5-5 5M13.5 4l-3 16"/>`},database:{viewBox:"0 0 24 24",body:`<ellipse ${shared} cx="12" cy="5" rx="7.5" ry="3"/><path ${shared} d="M4.5 5v7c0 1.66 3.36 3 7.5 3s7.5-1.34 7.5-3V5M4.5 12v7c0 1.66 3.36 3 7.5 3s7.5-1.34 7.5-3v-7"/>`},document:{viewBox:"0 0 24 24",body:`<path ${shared} d="M6 2.75h8l4 4V21.25H6zM14 2.75v4h4M9 11h6M9 15h6"/>`},globe:{viewBox:"0 0 24 24",body:`<circle ${shared} cx="12" cy="12" r="9"/><path ${shared} d="M3 12h18M12 3c2.4 2.45 3.5 5.45 3.5 9S14.4 18.55 12 21M12 3C9.6 5.45 8.5 8.45 8.5 12s1.1 6.55 3.5 9"/>`},search:{viewBox:"0 0 24 24",body:`<circle ${shared} cx="10.5" cy="10.5" r="6.5"/><path ${shared} d="m15.25 15.25 5 5"/>`},server:{viewBox:"0 0 24 24",body:`<rect ${shared} x="3" y="3.5" width="18" height="7" rx="2"/><rect ${shared} x="3" y="13.5" width="18" height="7" rx="2"/><path ${shared} d="M7 7h.01M7 17h.01M11 7h6M11 17h6"/>`},shield:{viewBox:"0 0 24 24",body:`<path ${shared} d="M12 2.75 20 6v5.5c0 5.25-3.35 8.2-8 10-4.65-1.8-8-4.75-8-10V6z"/>`},user:{viewBox:"0 0 24 24",body:`<circle ${shared} cx="12" cy="8" r="4"/><path ${shared} d="M4.5 21c.5-4.2 3-6.5 7.5-6.5s7 2.3 7.5 6.5"/>`}});function sanitizeIcon(icon){const dangerous=/<\s*(script|style|foreignObject|iframe|object|embed|image|use|a)\b|on[a-z]+\s*=|(?:xlink:)?href\s*=|style\s*=|javascript:|url\s*\(/i;if(dangerous.test(icon.body)){throw new Error("Icon body contains executable or externally embedded SVG content")}if(!/^[-+.\d\s]+$/.test(icon.viewBox.trim())){throw new Error(`Invalid icon viewBox: ${icon.viewBox}`)}return icon}var defaultLabelFontSize=22;var defaultLabelWeight=600;var defaultLineHeightRatio=1.35;var defaultRowGap=8;var iconLabelGap=12;function characterWidthRatio(fontFamily){return fontFamily==="mono"?0.62:0.56}function estimateDiagramTextWidth(text,fontSize,fontFamily="default"){return text.length*fontSize*characterWidthRatio(fontFamily)}function wrapDiagramText(text,maxWidth,fontSize,fontFamily="default"){const explicitLines=text.split(`
2
+ import{VectorizeDeadline,encodeTracePng,loadRaster,normalizedHexColor,parseHexColor,resolveVectorizeLimits,vectorizeHardLimits,vectorizeImage}from"./index-mnp0bftq.js";import{SlopcameraCloudError,createFixedGatewayFetch,generateSlopcameraImage,generateSlopcameraImageFile,resolveSlopcameraGatewayCredential,slopcameraGatewayApiBaseUrl,slopcameraImageModels,slopcameraMaximumPromptBytes}from"./index-p74xy76d.js";import{createDefaultHostResourceCoordinator}from"./index-b3geqczn.js";import{__require}from"./index-80a36bc0.js";import{randomUUID as randomUUID3}from"crypto";import{mkdir as mkdir3,readFile as readFile2,rename as rename2,rm as rm3,writeFile as writeFile3}from"fs/promises";import{dirname as dirname3,join as join2,resolve as resolve3}from"path";var shared='fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"';var builtInIcons=Object.freeze({brain:{viewBox:"0 0 24 24",body:`<path ${shared} d="M9.5 4.5A3.5 3.5 0 0 0 6 8v.35A3.5 3.5 0 0 0 5.5 15 3.5 3.5 0 0 0 9 19.5M14.5 4.5A3.5 3.5 0 0 1 18 8v.35a3.5 3.5 0 0 1 .5 6.65 3.5 3.5 0 0 1-3.5 4.5M9.5 4.5v15M14.5 4.5v15M6 9.5h3.5M14.5 14.5H18"/>`},check:{viewBox:"0 0 24 24",body:`<path ${shared} d="m5 12 4.25 4.25L19 6.5"/>`},code:{viewBox:"0 0 24 24",body:`<path ${shared} d="m8.5 7-5 5 5 5M15.5 7l5 5-5 5M13.5 4l-3 16"/>`},database:{viewBox:"0 0 24 24",body:`<ellipse ${shared} cx="12" cy="5" rx="7.5" ry="3"/><path ${shared} d="M4.5 5v7c0 1.66 3.36 3 7.5 3s7.5-1.34 7.5-3V5M4.5 12v7c0 1.66 3.36 3 7.5 3s7.5-1.34 7.5-3v-7"/>`},document:{viewBox:"0 0 24 24",body:`<path ${shared} d="M6 2.75h8l4 4V21.25H6zM14 2.75v4h4M9 11h6M9 15h6"/>`},globe:{viewBox:"0 0 24 24",body:`<circle ${shared} cx="12" cy="12" r="9"/><path ${shared} d="M3 12h18M12 3c2.4 2.45 3.5 5.45 3.5 9S14.4 18.55 12 21M12 3C9.6 5.45 8.5 8.45 8.5 12s1.1 6.55 3.5 9"/>`},search:{viewBox:"0 0 24 24",body:`<circle ${shared} cx="10.5" cy="10.5" r="6.5"/><path ${shared} d="m15.25 15.25 5 5"/>`},server:{viewBox:"0 0 24 24",body:`<rect ${shared} x="3" y="3.5" width="18" height="7" rx="2"/><rect ${shared} x="3" y="13.5" width="18" height="7" rx="2"/><path ${shared} d="M7 7h.01M7 17h.01M11 7h6M11 17h6"/>`},shield:{viewBox:"0 0 24 24",body:`<path ${shared} d="M12 2.75 20 6v5.5c0 5.25-3.35 8.2-8 10-4.65-1.8-8-4.75-8-10V6z"/>`},user:{viewBox:"0 0 24 24",body:`<circle ${shared} cx="12" cy="8" r="4"/><path ${shared} d="M4.5 21c.5-4.2 3-6.5 7.5-6.5s7 2.3 7.5 6.5"/>`}});function sanitizeIcon(icon){const dangerous=/<\s*(script|style|foreignObject|iframe|object|embed|image|use|a)\b|on[a-z]+\s*=|(?:xlink:)?href\s*=|style\s*=|javascript:|url\s*\(/i;if(dangerous.test(icon.body)){throw new Error("Icon body contains executable or externally embedded SVG content")}if(!/^[-+.\d\s]+$/.test(icon.viewBox.trim())){throw new Error(`Invalid icon viewBox: ${icon.viewBox}`)}return icon}var defaultLabelFontSize=22;var defaultLabelWeight=600;var defaultLineHeightRatio=1.35;var defaultRowGap=8;var iconLabelGap=12;function characterWidthRatio(fontFamily){return fontFamily==="mono"?0.62:0.56}function estimateDiagramTextWidth(text,fontSize,fontFamily="default"){return text.length*fontSize*characterWidthRatio(fontFamily)}function wrapDiagramText(text,maxWidth,fontSize,fontFamily="default"){const explicitLines=text.split(`
3
3
  `);const maxCharacters=Math.max(1,Math.floor(maxWidth/(fontSize*characterWidthRatio(fontFamily))));const lines=[];for(const explicitLine of explicitLines){if(explicitLine.length<=maxCharacters){lines.push(explicitLine);continue}const words=explicitLine.split(/\s+/);let current="";for(const word of words){const candidate=current===""?word:`${current} ${word}`;if(candidate.length<=maxCharacters||current===""){current=candidate}else{lines.push(current);current=word}}if(current!=="")lines.push(current)}return lines.length===0?[""]:lines}function resolveRows(shape){if(shape.labelRows!==undefined){return shape.labelRows.map((row)=>({text:row.text,fontSize:row.fontSize??defaultLabelFontSize,fontFamily:row.fontFamily??"default",weight:row.weight??defaultLabelWeight,lineHeight:(row.fontSize??defaultLabelFontSize)*defaultLineHeightRatio}))}if(shape.label===undefined)return[];const fontSize=shape.labelFontSize??defaultLabelFontSize;return[{text:shape.label,fontSize,fontFamily:shape.labelFontFamily??"default",weight:shape.labelWeight??defaultLabelWeight,lineHeight:fontSize*defaultLineHeightRatio}]}function layoutBoxContent(shape,resolvedIconSize){const textWidth=Math.max(1,shape.width-32);const pendingRows=resolveRows(shape).map((row)=>{const lines=wrapDiagramText(row.text,textWidth,row.fontSize,row.fontFamily);const height=lines.length*row.lineHeight;return{...row,lines,height}});const rowGap=shape.labelRows===undefined?0:shape.labelRowGap??defaultRowGap;const rowsHeight=pendingRows.reduce((total,row)=>total+row.height,0)+Math.max(0,pendingRows.length-1)*rowGap;const hasIcon=resolvedIconSize!==undefined;const gap=hasIcon&&pendingRows.length>0?iconLabelGap:0;const contentHeight=(resolvedIconSize??0)+gap+rowsHeight;let cursorY=shape.y+(shape.height-contentHeight)/2;const icon=resolvedIconSize===undefined?undefined:{x:shape.x+(shape.width-resolvedIconSize)/2,y:cursorY,size:resolvedIconSize};if(hasIcon)cursorY+=resolvedIconSize+gap;const rows=pendingRows.map((row)=>{const resolved={...row,y:cursorY};cursorY+=row.height+rowGap;return resolved});return{...icon===undefined?{}:{icon},rows}}import{readFile}from"fs/promises";import{extname,isAbsolute}from"path";import{fileURLToPath}from"url";import{Resvg}from"@resvg/resvg-js";var NebulaSans_Bold_default="./NebulaSans-Bold-26se8aek.otf";var NebulaSans_Bold_default2="./NebulaSans-Bold-bcz7y08t.woff2";var NebulaSans_Book_default="./NebulaSans-Book-8cenzchw.otf";var NebulaSans_Book_default2="./NebulaSans-Book-5ax05zvn.woff2";var tones=["neutral","blue","orange","green","red","purple","yellow"];var defaults={light:{background:"#ffffff",foreground:"#18181b",muted:"#71717a",stroke:"#27272a",tones:{neutral:{fill:"#f4f4f5",stroke:"#52525b",text:"#18181b"},blue:{fill:"#dbeafe",stroke:"#2563eb",text:"#1e3a8a"},orange:{fill:"#ffedd5",stroke:"#ea580c",text:"#7c2d12"},green:{fill:"#dcfce7",stroke:"#16a34a",text:"#14532d"},red:{fill:"#fee2e2",stroke:"#dc2626",text:"#7f1d1d"},purple:{fill:"#f3e8ff",stroke:"#9333ea",text:"#581c87"},yellow:{fill:"#fef9c3",stroke:"#ca8a04",text:"#713f12"}}},dark:{background:"#09090b",foreground:"#fafafa",muted:"#a1a1aa",stroke:"#d4d4d8",tones:{neutral:{fill:"#27272a",stroke:"#a1a1aa",text:"#fafafa"},blue:{fill:"#172554",stroke:"#60a5fa",text:"#dbeafe"},orange:{fill:"#431407",stroke:"#fb923c",text:"#ffedd5"},green:{fill:"#052e16",stroke:"#4ade80",text:"#dcfce7"},red:{fill:"#450a0a",stroke:"#f87171",text:"#fee2e2"},purple:{fill:"#3b0764",stroke:"#c084fc",text:"#f3e8ff"},yellow:{fill:"#422006",stroke:"#facc15",text:"#fef9c3"}}}};function mergeTheme(base,override){if(override===undefined)return base;return{background:override.background??base.background,foreground:override.foreground??base.foreground,muted:override.muted??base.muted,stroke:override.stroke??base.stroke,tones:Object.fromEntries(tones.map((tone)=>[tone,{fill:override.tones?.[tone]?.fill??base.tones[tone].fill,stroke:override.tones?.[tone]?.stroke??base.tones[tone].stroke,text:override.tones?.[tone]?.text??base.tones[tone].text}]))}}function resolveTheme(mode,config){return mergeTheme(defaults[mode],config.theme?.[mode])}var bundledAssetPath=(asset)=>isAbsolute(asset)?asset:fileURLToPath(new URL(asset,import.meta.url));var defaultFont=Object.freeze({family:"Nebula Sans",files:Object.freeze([Object.freeze({embed:true,path:bundledAssetPath(NebulaSans_Book_default2),style:"normal",weight:400}),Object.freeze({embed:true,path:bundledAssetPath(NebulaSans_Bold_default2),style:"normal",weight:700}),Object.freeze({embed:false,path:bundledAssetPath(NebulaSans_Book_default),style:"normal",weight:400}),Object.freeze({embed:false,path:bundledAssetPath(NebulaSans_Bold_default),style:"normal",weight:700})])});var escapeXml=(value)=>value.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;").replaceAll("'","&apos;");function fontMime(filePath){switch(extname(filePath).toLowerCase()){case".otf":return"font/otf";case".woff":return"font/woff";case".woff2":return"font/woff2";default:return"font/ttf"}}async function fontCss(font){if(font.files===undefined)return"";const faces=await Promise.all(font.files.filter((file)=>file.embed===true).map(async(file)=>{const encoded=(await readFile(file.path)).toString("base64");return`@font-face{font-family:"${escapeXml(font.family)}";src:url(data:${fontMime(file.path)};base64,${encoded});font-weight:${file.weight??400};font-style:${file.style??"normal"};}`}));return faces.join("")}function textSvg(options){const lines=options.lines??wrapDiagramText(options.text,options.width,options.fontSize);const lineHeight=options.lineHeight??options.fontSize*1.25;const anchor=options.align;const x=anchor==="middle"?options.x+options.width/2:anchor==="end"?options.x+options.width:options.x;const dominantBaseline=options.centerLineBoxes?"central":"hanging";return`<text x="${x}" y="${options.y}" text-anchor="${anchor}" dominant-baseline="${dominantBaseline}" fill="${options.color}" opacity="${options.opacity}" font-family="${escapeXml(options.family)}" font-size="${options.fontSize}" font-weight="${options.weight}">${lines.map((line,index)=>options.centerLineBoxes?`<tspan x="${x}" y="${options.y+(index+0.5)*lineHeight}">${escapeXml(line)}</tspan>`:`<tspan x="${x}" dy="${index===0?0:lineHeight}">${escapeXml(line)}</tspan>`).join("")}</text>`}function iconSvg(options){const icon=sanitizeIcon(options.icon);const parts=icon.viewBox.trim().split(/\s+/).map(Number);const width=(parts[2]??24)-(parts[0]??0);const height=(parts[3]??24)-(parts[1]??0);const scale=options.size/Math.max(width,height);return`<g color="${options.color}" opacity="${options.opacity}" transform="translate(${options.x} ${options.y}) scale(${scale}) translate(${-Number(parts[0]??0)} ${-Number(parts[1]??0)})">${icon.body}</g>`}function boxSvg(shape,theme,families,icons){const tone=theme.tones[shape.tone??"neutral"];const strokeWidth=shape.strokeWidth??2;const opacity=shape.opacity??1;const radius=shape.type==="ellipse"?0:Math.min(shape.radius??22,shape.height/2);const geometry=shape.type==="ellipse"?`<ellipse cx="${shape.x+shape.width/2}" cy="${shape.y+shape.height/2}" rx="${shape.width/2}" ry="${shape.height/2}" fill="${shape.fill===false?"none":tone.fill}" stroke="${tone.stroke}" stroke-width="${strokeWidth}"/>`:`<rect x="${shape.x}" y="${shape.y}" width="${shape.width}" height="${shape.height}" rx="${radius}" fill="${shape.fill===false?"none":tone.fill}" stroke="${tone.stroke}" stroke-width="${strokeWidth}"/>`;const icon=shape.icon===undefined?undefined:icons[shape.icon];if(shape.icon!==undefined&&icon===undefined){throw new Error(`Unknown icon "${shape.icon}" on shape ${shape.id}`)}const iconSize=Math.min(shape.iconSize??52,shape.height*0.45,shape.width*0.32);const content=layoutBoxContent(shape,icon===undefined?undefined:iconSize);const iconMarkup=icon===undefined||content.icon===undefined?"":iconSvg({icon,x:content.icon.x,y:content.icon.y,size:content.icon.size,color:tone.text,opacity:1});const labelMarkup=content.rows.map((row)=>textSvg({text:row.text,x:shape.x+16,y:row.y,width:shape.width-32,fontSize:row.fontSize,weight:row.weight,align:"middle",color:tone.text,opacity:1,family:families[row.fontFamily],lineHeight:row.lineHeight,lines:row.lines,centerLineBoxes:true})).join("");return`<g data-shape-id="${escapeXml(shape.id)}" opacity="${opacity}">${geometry}${iconMarkup}${labelMarkup}</g>`}function pointForAnchor(shape,anchor,toward,position){const center={x:shape.x+shape.width/2,y:shape.y+shape.height/2};let resolved=anchor??"auto";if(resolved==="auto"){const dx=toward.x-center.x;const dy=toward.y-center.y;resolved=Math.abs(dx/shape.width)>=Math.abs(dy/shape.height)?dx>=0?"right":"left":dy>=0?"bottom":"top"}const resolvedPosition=position??0.5;if(shape.type==="ellipse"){const projected=(resolvedPosition-0.5)*2;const radial=Math.sqrt(Math.max(0,1-projected*projected))/2;const normalized=resolved==="top"?{x:resolvedPosition,y:0.5-radial}:resolved==="bottom"?{x:resolvedPosition,y:0.5+radial}:resolved==="left"?{x:0.5-radial,y:resolvedPosition}:{x:0.5+radial,y:resolvedPosition};return{x:shape.x+shape.width*normalized.x,y:shape.y+shape.height*normalized.y,normalized}}switch(resolved){case"top":return{x:shape.x+shape.width*resolvedPosition,y:shape.y,normalized:{x:resolvedPosition,y:0}};case"right":return{x:shape.x+shape.width,y:shape.y+shape.height*resolvedPosition,normalized:{x:1,y:resolvedPosition}};case"bottom":return{x:shape.x+shape.width*resolvedPosition,y:shape.y+shape.height,normalized:{x:resolvedPosition,y:1}};case"left":return{x:shape.x,y:shape.y+shape.height*resolvedPosition,normalized:{x:0,y:resolvedPosition}}}}function resolveEdge(spec,edge){const boxes=new Map(spec.shapes.filter((shape)=>shape.type==="rect"||shape.type==="ellipse").map((shape)=>[shape.id,shape]));const from=boxes.get(edge.from);const to=boxes.get(edge.to);if(from===undefined||to===undefined){throw new Error(`Edge ${edge.id} references a missing box`)}const fromCenter={x:from.x+from.width/2,y:from.y+from.height/2};const toCenter={x:to.x+to.width/2,y:to.y+to.height/2};const start=pointForAnchor(from,edge.start,toCenter,edge.startPosition);const end=pointForAnchor(to,edge.end,fromCenter,edge.endPosition);const dx=end.x-start.x;const dy=end.y-start.y;const length=Math.hypot(dx,dy)||1;const bend=edge.bend??0;return{edge,from,to,start,end,control:{x:(start.x+end.x)/2+-dy/length*bend,y:(start.y+end.y)/2+dx/length*bend}}}function resolveEdgeLabel(resolved){const{edge,start,end,control}=resolved;const t=edge.labelPosition??0.5;const oneMinusT=1-t;const point={x:oneMinusT*oneMinusT*start.x+2*oneMinusT*t*control.x+t*t*end.x,y:oneMinusT*oneMinusT*start.y+2*oneMinusT*t*control.y+t*t*end.y};const tangent={x:2*oneMinusT*(control.x-start.x)+2*t*(end.x-control.x),y:2*oneMinusT*(control.y-start.y)+2*t*(end.y-control.y)};const length=Math.hypot(tangent.x,tangent.y)||1;const offset=edge.labelOffset??-14;return{x:point.x+-tangent.y/length*offset,y:point.y+tangent.x/length*offset}}function edgeSvg(resolved,theme,families,strokeWidth){const{edge,start,end,control}=resolved;const toneName=edge.tone??"neutral";const tone=theme.tones[toneName];const marker=edge.arrowhead==="none"?"":edge.arrowhead==="triangle"?`url(#arrow-triangle-${toneName})`:`url(#arrow-open-${toneName})`;const path=`M ${start.x} ${start.y} Q ${control.x} ${control.y} ${end.x} ${end.y}`;const labelPoint=resolveEdgeLabel(resolved);const label=edge.label===undefined?"":`<text x="${labelPoint.x}" y="${labelPoint.y}" text-anchor="middle" dominant-baseline="central" fill="${tone.text}" stroke="${theme.background}" stroke-width="6" paint-order="stroke" font-family="${escapeXml(families[edge.labelFontFamily??"default"])}" font-size="${edge.labelFontSize??18}" font-weight="${edge.labelWeight??600}">${escapeXml(edge.label)}</text>`;return`<g data-edge-id="${escapeXml(edge.id)}"><path d="${path}" fill="none" stroke="${tone.stroke}" stroke-width="${strokeWidth}" stroke-linecap="round" marker-end="${marker}"/>${label}</g>`}function shapeSvg(shape,theme,families,icons){if(shape.type==="rect"||shape.type==="ellipse"){return boxSvg(shape,theme,families,icons)}if(shape.type==="line"){return`<line data-shape-id="${escapeXml(shape.id)}" x1="${shape.x}" y1="${shape.y}" x2="${shape.x2}" y2="${shape.y2}" stroke="${theme.tones[shape.tone??"neutral"].stroke}" stroke-width="${shape.strokeWidth??3}" stroke-linecap="round" opacity="${shape.opacity??1}"/>`}const text=shape;return textSvg({text:text.text,x:text.x,y:text.y,width:text.width??Math.max(text.text.length*(text.fontSize??24)*0.58,12),fontSize:text.fontSize??24,weight:text.weight??500,align:text.align??"start",color:theme.tones[text.tone??"neutral"].text,opacity:text.opacity??1,family:families[text.fontFamily??"default"]})}async function renderSvg(spec,mode,config,options={}){const edgeStrokeWidth=options.edgeStrokeWidth??3;if(!Number.isFinite(edgeStrokeWidth)||edgeStrokeWidth<0.5||edgeStrokeWidth>4)throw new Error("Edge stroke width must be between 0.5 and 4");const theme=resolveTheme(mode,config);const font=config.font??defaultFont;const families={default:font.family,mono:font.monoFamily??"ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, monospace"};const icons=config.icons??{};const embeddedFonts=await fontCss(font);const markerDefinitions=Object.entries(theme.tones).map(([toneName,tone])=>`<marker id="arrow-open-${toneName}" markerWidth="12" markerHeight="12" refX="10" refY="6" orient="auto" markerUnits="strokeWidth"><path d="M2 2 10 6 2 10" fill="none" stroke="${tone.stroke}" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/></marker><marker id="arrow-triangle-${toneName}" markerWidth="11" markerHeight="11" refX="9" refY="5.5" orient="auto" markerUnits="strokeWidth"><path d="M1 1 10 5.5 1 10z" fill="${tone.stroke}"/></marker>`).join("");const edgeMarkup=(spec.edges??[]).map((edge)=>edgeSvg(resolveEdge(spec,edge),theme,families,edgeStrokeWidth)).join("");const shapeMarkup=spec.shapes.map((shape)=>shapeSvg(shape,theme,families,icons)).join("");const svg=[`<svg xmlns="http://www.w3.org/2000/svg" width="${spec.canvas.width}" height="${spec.canvas.height}" viewBox="0 0 ${spec.canvas.width} ${spec.canvas.height}" role="img" aria-labelledby="diagram-title" color-scheme="${mode}">`,`<title id="diagram-title">${escapeXml(spec.name)}</title>`,`<style>${embeddedFonts}text{font-kerning:normal;text-rendering:geometricPrecision}</style>`,`<rect width="100%" height="100%" fill="${theme.background}"/>`,`<defs>${markerDefinitions}</defs>`,edgeMarkup,shapeMarkup,"</svg>"].join("");return{mode,svg,width:spec.canvas.width,height:spec.canvas.height}}function renderPng(rendered,config,scale=2){const font=config.font??defaultFont;const fontFiles=font.files?.map((file)=>file.path)??[];const renderer=new Resvg(rendered.svg,{fitTo:{mode:"zoom",value:scale},font:{loadSystemFonts:true,...fontFiles.length===0?{}:{fontFiles},defaultFontFamily:font.family}});return renderer.render().asPng()}function boxOutsideCanvas(shape,spec){return shape.x<0||shape.y<0||shape.x+shape.width>spec.canvas.width||shape.y+shape.height>spec.canvas.height}function lintDiagram(spec){const findings=[];const boxes=spec.shapes.filter((shape)=>shape.type==="rect"||shape.type==="ellipse");for(const shape of boxes){if(boxOutsideCanvas(shape,spec)){findings.push({code:"outside-canvas",message:`${shape.id} extends beyond the canvas`,shapeIds:[shape.id]})}const labels=shape.labelRows?.map((row)=>row.text)??(shape.label===undefined?[]:[shape.label]);const longLabel=labels.find((label)=>label.length>32);if(longLabel!==undefined){findings.push({code:"long-label",message:`${shape.id} has a ${longLabel.length}-character label; prefer a short noun phrase`,shapeIds:[shape.id]})}const iconSize=shape.icon===undefined?undefined:Math.min(shape.iconSize??52,shape.height*0.45,shape.width*0.32);const content=layoutBoxContent(shape,iconSize);const contentTop=content.icon?.y??content.rows[0]?.y;const finalRow=content.rows.at(-1);const contentBottom=finalRow===undefined?content.icon===undefined?undefined:content.icon.y+content.icon.size:finalRow.y+finalRow.height;const contentWidth=Math.max(1,shape.width-32);const exceedsWidth=content.rows.some((row)=>row.lines.some((line)=>estimateDiagramTextWidth(line,row.fontSize,row.fontFamily)>contentWidth));if(contentTop!==undefined&&contentBottom!==undefined&&(contentTop<shape.y||contentBottom>shape.y+shape.height||exceedsWidth)){findings.push({code:"label-overflow",message:`${shape.id} label and icon content exceeds the box bounds; increase the box or reduce authored sizes`,shapeIds:[shape.id]})}if(shape.width<120||shape.height<64){findings.push({code:"small-target",message:`${shape.id} is small enough to make its label or icon hard to scan`,shapeIds:[shape.id]})}}if(boxes.length>9){findings.push({code:"too-many-elements",message:`The diagram has ${boxes.length} primary shapes; consider a higher-level visual`,shapeIds:boxes.map((shape)=>shape.id)})}const sharedTerminals=new Map;for(const edge of spec.edges??[]){const resolved=resolveEdge(spec,edge);for(const terminal of[{kind:"start",point:resolved.start,shapeId:edge.from},{kind:"end",point:resolved.end,shapeId:edge.to}]){const key=`${terminal.kind}:${terminal.shapeId}:${terminal.point.x.toFixed(3)}:${terminal.point.y.toFixed(3)}`;const group=sharedTerminals.get(key)??[];group.push({edgeId:edge.id,shapeId:terminal.shapeId});sharedTerminals.set(key,group)}const length=Math.hypot(resolved.end.x-resolved.start.x,resolved.end.y-resolved.start.y);if(length<96){findings.push({code:"short-arrow",message:`${edge.id} is ${Math.round(length)}px long; leave more space between connected shapes`,shapeIds:[edge.from,edge.to]})}if(edge.label!==undefined&&edge.label.length>24){findings.push({code:"long-edge-label",message:`${edge.id} has a long connector label; prefer one short relation`,shapeIds:[edge.from,edge.to]})}}for(const group of sharedTerminals.values()){if(group.length<2)continue;findings.push({code:"shared-edge-port",message:`${group.map(({edgeId})=>edgeId).join(", ")} share one connector port; set distinct startPosition or endPosition values`,shapeIds:[group[0].shapeId]})}return findings}var diagramVersion=1;var stackLayoutDefaults={gap:160,padding:64,align:"center"};var coordinatePrecision=3;var coordinateScale=10**coordinatePrecision;class StackLayoutError extends Error{issues;constructor(issues){super(`Invalid stack layout:
4
4
  ${issues.map((issue)=>`- ${issue}`).join(`
5
5
  `)}`);this.name="StackLayoutError";this.issues=issues}}function coordinateFromUnits(value){return value/coordinateScale}function normalizedScaledCoordinate(value){const scaled=value*coordinateScale;return normalizedGridValue(scaled)}function normalizedGridValue(value){const nearest=Math.round(value);return Math.abs(value-nearest)<=0.000000001?nearest:value}function floorGridValue(value){return Math.floor(normalizedGridValue(value))}function ceilGridValue(value){return Math.ceil(normalizedGridValue(value))}function coordinateUnits(value,label,positive,rounding,issues){if(!Number.isFinite(value)||(positive?value<=0:value<0)){issues.push(positive?`${label} must be positive`:`${label} must not be negative`);return 0}const scaled=normalizedScaledCoordinate(value);const units=rounding==="ceil"?ceilGridValue(scaled):floorGridValue(scaled);if(!Number.isSafeInteger(units)){issues.push(`${label} must be a finite coordinate within the supported range`);return 0}if(positive&&units<1){issues.push(`${label} must occupy at least ${coordinateFromUnits(1)}px in the layout grid`);return 0}return units}function positionedShape(shape,mainPosition,crossPosition,horizontal){return{...shape,x:coordinateFromUnits(horizontal?mainPosition:crossPosition),y:coordinateFromUnits(horizontal?crossPosition:mainPosition)}}function authoredMainSize(shape,horizontal){return horizontal?shape.width:shape.height}function emittedMainPlacement(shapes,horizontal,gap,issues){if(shapes.length===0)return{offsets:[],span:0};const offsets=[];let offset=0;for(const[index,shape]of shapes.entries()){offsets.push(offset);if(index===shapes.length-1)break;const advance=coordinateUnits(authoredMainSize(shape,horizontal)+gap,`space after shape ${shape.id}`,true,"ceil",issues);offset+=advance;if(!Number.isSafeInteger(offset)){issues.push("stack positions exceed the supported coordinate range");return{offsets,span:Number.POSITIVE_INFINITY}}}const last=shapes.at(-1);const span=offset+(last===undefined?0:normalizedScaledCoordinate(authoredMainSize(last,horizontal)));if(!Number.isFinite(span)||!Number.isSafeInteger(Math.ceil(span))){issues.push("stack extent exceeds the supported coordinate range")}return{offsets,span}}function clampedGridPosition(ideal,minimum,maximum){return Math.min(maximum,Math.max(minimum,Math.round(ideal)))}function resolvedAnchors(fromIndex,toIndex,horizontal){const forward=fromIndex<toIndex;if(horizontal){return forward?{start:"right",end:"left"}:{start:"left",end:"right"}}return forward?{start:"bottom",end:"top"}:{start:"top",end:"bottom"}}function resolvedEdge(edge,indexes,horizontal){const fromIndex=indexes.get(edge.from);const toIndex=indexes.get(edge.to);if(fromIndex===undefined||toIndex===undefined){throw new StackLayoutError([`edge ${edge.id} must reference shapes that belong to the stack`])}const anchors=resolvedAnchors(fromIndex,toIndex,horizontal);return{...edge,start:anchors.start,end:anchors.end,bend:0}}function stackStructureIssues(source){const issues=[];if(source.shapes.length<1||source.shapes.length>9){issues.push("stack layouts must contain between 1 and 9 shapes")}const indexes=new Map;const recordIds=new Set;for(const[index,shape]of source.shapes.entries()){if(recordIds.has(shape.id)){issues.push(`shape id ${shape.id} is duplicated`)}else{indexes.set(shape.id,index);recordIds.add(shape.id)}}const connectedPairs=new Set;for(const stackEdge of source.edges??[]){const edge=stackEdge;if(recordIds.has(edge.id)){issues.push(`edge id ${edge.id} is duplicated`)}recordIds.add(edge.id);const fromIndex=indexes.get(edge.from);const toIndex=indexes.get(edge.to);if(fromIndex===undefined){issues.push(`edge ${edge.id} has unknown from id ${edge.from}`)}if(toIndex===undefined){issues.push(`edge ${edge.id} has unknown to id ${edge.to}`)}if(fromIndex!==undefined&&toIndex!==undefined){if(fromIndex===toIndex){issues.push(`edge ${edge.id} cannot connect a shape to itself`)}else{if(Math.abs(fromIndex-toIndex)!==1){issues.push(`edge ${edge.id} must connect adjacent stack shapes`)}const pair=[fromIndex,toIndex].sort((left,right)=>left-right).join(":");if(connectedPairs.has(pair)){issues.push(`edge ${edge.id} duplicates a connection between the same stack shapes`)}connectedPairs.add(pair)}}if(edge.start!==undefined&&edge.start!=="auto"){issues.push(`edge ${edge.id}.start must be auto or omitted in a stack layout`)}if(edge.end!==undefined&&edge.end!=="auto"){issues.push(`edge ${edge.id}.end must be auto or omitted in a stack layout`)}if(edge.bend!==undefined&&edge.bend!==0){issues.push(`edge ${edge.id}.bend must be 0 or omitted in a stack layout`)}}return issues}function resolveStackLayout(source){const issues=[...stackStructureIssues(source)];const horizontal=source.layout.direction==="horizontal";coordinateUnits(source.canvas.width,"canvas width",true,"floor",issues);coordinateUnits(source.canvas.height,"canvas height",true,"floor",issues);const gapValue=source.layout.gap??stackLayoutDefaults.gap;coordinateUnits(gapValue,"stack gap",false,"ceil",issues);const paddingValue=source.canvas.padding??stackLayoutDefaults.padding;const padding=coordinateUnits(paddingValue,"canvas padding",false,"ceil",issues);for(const shape of source.shapes){coordinateUnits(shape.width,`shape ${shape.id} width`,true,"ceil",issues);coordinateUnits(shape.height,`shape ${shape.id} height`,true,"ceil",issues)}const align=source.layout.align??stackLayoutDefaults.align;const canvasMainValue=horizontal?source.canvas.width:source.canvas.height;const canvasCrossValue=horizontal?source.canvas.height:source.canvas.width;const mainPlacement=emittedMainPlacement(source.shapes,horizontal,gapValue,issues);const maximumMainStart=floorGridValue(normalizedScaledCoordinate(canvasMainValue-paddingValue)-mainPlacement.span);if(padding>maximumMainStart){const requiredMain=ceilGridValue(mainPlacement.span);const availableMain=floorGridValue(normalizedScaledCoordinate(canvasMainValue-paddingValue*2));issues.push(`${source.layout.direction} stack needs ${coordinateFromUnits(requiredMain)}px but only ${coordinateFromUnits(availableMain)}px remain inside ${paddingValue}px padding`)}const crossStarts=source.shapes.map((shape)=>{const authoredSize=horizontal?shape.height:shape.width;const maximum=floorGridValue(normalizedScaledCoordinate(canvasCrossValue-paddingValue-authoredSize));if(padding>maximum){const requiredCross=ceilGridValue(normalizedScaledCoordinate(authoredSize));const availableCross=floorGridValue(normalizedScaledCoordinate(canvasCrossValue-paddingValue*2));issues.push(`shape ${shape.id} needs ${coordinateFromUnits(requiredCross)}px on the cross axis but only ${coordinateFromUnits(availableCross)}px remain inside ${paddingValue}px padding`)}return{authoredSize,maximum}});if(issues.length>0)throw new StackLayoutError(issues);const mainStart=clampedGridPosition((normalizedScaledCoordinate(canvasMainValue)-mainPlacement.span)/2,padding,maximumMainStart);const shapes=source.shapes.map((shape,index)=>{const crossStart=crossStarts[index];if(crossStart===undefined){throw new StackLayoutError([`shape ${shape.id} has no cross-axis placement`])}const crossPosition=align==="start"?padding:align==="end"?crossStart.maximum:clampedGridPosition(normalizedScaledCoordinate((canvasCrossValue-crossStart.authoredSize)/2),padding,crossStart.maximum);const mainPosition=mainStart+(mainPlacement.offsets[index]??0);const positioned=positionedShape(shape,mainPosition,crossPosition,horizontal);return positioned});const indexes=new Map(shapes.map((shape,index)=>[shape.id,index]));const edges=source.edges?.map((edge)=>resolvedEdge(edge,indexes,horizontal));return{...source.$schema===undefined?{}:{$schema:source.$schema},version:source.version,name:source.name,canvas:{width:source.canvas.width,height:source.canvas.height,padding:paddingValue},shapes,...edges===undefined?{}:{edges}}}function resolveDiagramSource(source){return"layout"in source?resolveStackLayout(source):source}var tones2=new Set(["neutral","blue","orange","green","red","purple","yellow"]);var anchors=new Set(["auto","top","right","bottom","left"]);var fontFamilies=new Set(["default","mono"]);var stackDirections=new Set(["horizontal","vertical"]);var stackAlignments=new Set(["start","center","end"]);var idPattern=/^[A-Za-z0-9][A-Za-z0-9_-]*$/;var namePattern=/^[a-z0-9]+(?:-[a-z0-9]+)*$/;class DiagramValidationError extends Error{issues;constructor(issues){super(`Invalid diagram specification:
@@ -7,7 +7,7 @@ ${issues.map((issue)=>`- ${issue}`).join(`
7
7
  `)}`);this.name="DiagramValidationError";this.issues=issues}}function isRecord(value){return typeof value==="object"&&value!==null&&!Array.isArray(value)}function readString(record,key,at,issues){const value=record[key];if(typeof value!=="string"){issues.push(`${at}.${key} must be a string`);return}return value}function readOptionalString(record,key,at,issues){const value=record[key];if(value===undefined)return;if(typeof value!=="string"){issues.push(`${at}.${key} must be a string when present`);return}return value}function readNumber(record,key,at,issues,options={}){const value=record[key];if(typeof value!=="number"||!Number.isFinite(value)){issues.push(`${at}.${key} must be a finite number`);return}if(options.positive&&value<=0){issues.push(`${at}.${key} must be greater than zero`);return}if(options.nonNegative&&value<0){issues.push(`${at}.${key} must be zero or greater`);return}return value}function readOptionalNumber(record,key,at,issues,options={}){if(record[key]===undefined)return;return readNumber(record,key,at,issues,options)}function readOptionalTone(record,key,at,issues){const value=record[key];if(value===undefined)return;if(typeof value!=="string"||!tones2.has(value)){issues.push(`${at}.${key} must be one of ${[...tones2].join(", ")}`);return}return value}function readOptionalFontFamily(record,key,at,issues){const value=record[key];if(value===undefined)return;if(typeof value!=="string"||!fontFamilies.has(value)){issues.push(`${at}.${key} must be default or mono`);return}return value}function readOptionalWeight(record,key,at,issues){const value=record[key];if(value===undefined)return;if(typeof value!=="number"||![400,500,600,700].includes(value)){issues.push(`${at}.${key} must be 400, 500, 600, or 700`);return}return value}function parseLabelRows(value,at,issues){if(value===undefined)return;if(!Array.isArray(value)){issues.push(`${at}.labelRows must be an array when present`);return}if(value.length<1||value.length>4){issues.push(`${at}.labelRows must contain between 1 and 4 rows`)}const rows=[];for(const[index,candidate]of value.entries()){const rowAt=`${at}.labelRows[${index}]`;if(!isRecord(candidate)){issues.push(`${rowAt} must be an object`);continue}validateKnownKeys(candidate,new Set(["text","fontSize","fontFamily","weight"]),rowAt,issues);const text=readString(candidate,"text",rowAt,issues);const fontSize=readOptionalNumber(candidate,"fontSize",rowAt,issues,{positive:true});const fontFamily=readOptionalFontFamily(candidate,"fontFamily",rowAt,issues);const weight=readOptionalWeight(candidate,"weight",rowAt,issues);if(text!==undefined){rows.push({text,...fontSize===undefined?{}:{fontSize},...fontFamily===undefined?{}:{fontFamily},...weight===undefined?{}:{weight}})}}return rows.length===0?undefined:rows}function validateKnownKeys(record,allowed,at,issues){for(const key of Object.keys(record)){if(!allowed.has(key))issues.push(`${at}.${key} is not supported`)}}function parseBoxLabelContent(value,at,issues){const label=readOptionalString(value,"label",at,issues);const labelFontSize=readOptionalNumber(value,"labelFontSize",at,issues,{positive:true});const labelFontFamily=readOptionalFontFamily(value,"labelFontFamily",at,issues);const labelWeight=readOptionalWeight(value,"labelWeight",at,issues);const labelRows=parseLabelRows(value.labelRows,at,issues);const labelRowGap=readOptionalNumber(value,"labelRowGap",at,issues,{nonNegative:true});const hasLegacyStyle=value.labelFontSize!==undefined||value.labelFontFamily!==undefined||value.labelWeight!==undefined;if(value.label===undefined&&hasLegacyStyle){issues.push(`${at} cannot style a label that is not present`)}if(value.labelRows!==undefined&&value.label!==undefined){issues.push(`${at}.label and ${at}.labelRows are mutually exclusive`)}if(value.labelRows!==undefined&&hasLegacyStyle){issues.push(`${at}.labelRows cannot be combined with legacy label styling`)}if(value.labelRows===undefined&&value.labelRowGap!==undefined){issues.push(`${at}.labelRowGap requires labelRows`)}if(labelRows!==undefined){return{labelRows,...labelRowGap===undefined?{}:{labelRowGap}}}if(label!==undefined){return{label,...labelFontSize===undefined?{}:{labelFontSize},...labelFontFamily===undefined?{}:{labelFontFamily},...labelWeight===undefined?{}:{labelWeight}}}return{}}function parseBase(record,at,issues){const id=readString(record,"id",at,issues);if(id!==undefined&&!idPattern.test(id)){issues.push(`${at}.id must contain only letters, numbers, underscores, or hyphens`)}const opacity=readOptionalNumber(record,"opacity",at,issues,{nonNegative:true});if(opacity!==undefined&&opacity>1)issues.push(`${at}.opacity must not exceed 1`);const x=readNumber(record,"x",at,issues);const y=readNumber(record,"y",at,issues);const tone=readOptionalTone(record,"tone",at,issues);return{...id===undefined?{}:{id},...x===undefined?{}:{x},...y===undefined?{}:{y},...tone===undefined?{}:{tone},...opacity===undefined?{}:{opacity}}}function parseShape(value,index,issues){const at=`shapes[${index}]`;if(!isRecord(value)){issues.push(`${at} must be an object`);return null}const type=readString(value,"type",at,issues);const base=parseBase(value,at,issues);if(base.id===undefined||base.x===undefined||base.y===undefined||type===undefined){return null}const requiredBase={id:base.id,x:base.x,y:base.y,...base.tone===undefined?{}:{tone:base.tone},...base.opacity===undefined?{}:{opacity:base.opacity}};if(type==="rect"||type==="ellipse"){validateKnownKeys(value,new Set(["id","type","x","y","tone","opacity","width","height","radius","label","labelFontSize","labelFontFamily","labelWeight","labelRows","labelRowGap","icon","iconSize","strokeWidth","fill"]),at,issues);const width=readNumber(value,"width",at,issues,{positive:true});const height=readNumber(value,"height",at,issues,{positive:true});const labelContent=parseBoxLabelContent(value,at,issues);const fill=value.fill;if(fill!==undefined&&typeof fill!=="boolean")issues.push(`${at}.fill must be a boolean`);if(width===undefined||height===undefined)return null;return{...requiredBase,type,width,height,...readOptionalNumber(value,"radius",at,issues,{nonNegative:true})===undefined?{}:{radius:value.radius},...labelContent,...readOptionalString(value,"icon",at,issues)===undefined?{}:{icon:value.icon},...readOptionalNumber(value,"iconSize",at,issues,{positive:true})===undefined?{}:{iconSize:value.iconSize},...readOptionalNumber(value,"strokeWidth",at,issues,{nonNegative:true})===undefined?{}:{strokeWidth:value.strokeWidth},...typeof fill==="boolean"?{fill}:{}}}if(type==="text"){validateKnownKeys(value,new Set(["id","type","x","y","tone","opacity","text","width","fontSize","fontFamily","weight","align"]),at,issues);const text=readString(value,"text",at,issues);const fontFamily=readOptionalFontFamily(value,"fontFamily",at,issues);const weight=readOptionalWeight(value,"weight",at,issues);const align=value.align;if(align!==undefined&&!["start","middle","end"].includes(align)){issues.push(`${at}.align must be start, middle, or end`)}if(text===undefined)return null;return{...requiredBase,type,text,...readOptionalNumber(value,"width",at,issues,{positive:true})===undefined?{}:{width:value.width},...readOptionalNumber(value,"fontSize",at,issues,{positive:true})===undefined?{}:{fontSize:value.fontSize},...fontFamily===undefined?{}:{fontFamily},...weight===undefined?{}:{weight},...align===undefined?{}:{align}}}if(type==="line"){validateKnownKeys(value,new Set(["id","type","x","y","tone","opacity","x2","y2","strokeWidth"]),at,issues);const x2=readNumber(value,"x2",at,issues);const y2=readNumber(value,"y2",at,issues);if(x2===undefined||y2===undefined)return null;return{...requiredBase,type,x2,y2,...readOptionalNumber(value,"strokeWidth",at,issues,{positive:true})===undefined?{}:{strokeWidth:value.strokeWidth}}}issues.push(`${at}.type must be rect, ellipse, text, or line`);return null}function parseStackShape(value,index,issues){const at=`shapes[${index}]`;if(!isRecord(value)){issues.push(`${at} must be an object`);return null}validateKnownKeys(value,new Set(["id","type","tone","opacity","width","height","radius","label","labelFontSize","labelFontFamily","labelWeight","labelRows","labelRowGap","icon","iconSize","strokeWidth","fill"]),at,issues);const id=readString(value,"id",at,issues);if(id!==undefined&&!idPattern.test(id)){issues.push(`${at}.id must contain only letters, numbers, underscores, or hyphens`)}const type=readString(value,"type",at,issues);if(type!==undefined&&type!=="rect"&&type!=="ellipse"){issues.push(`${at}.type must be rect or ellipse in a stack layout`)}const width=readNumber(value,"width",at,issues,{positive:true});const height=readNumber(value,"height",at,issues,{positive:true});const labelContent=parseBoxLabelContent(value,at,issues);const tone=readOptionalTone(value,"tone",at,issues);const opacity=readOptionalNumber(value,"opacity",at,issues,{nonNegative:true});if(opacity!==undefined&&opacity>1)issues.push(`${at}.opacity must not exceed 1`);const radius=readOptionalNumber(value,"radius",at,issues,{nonNegative:true});const icon=readOptionalString(value,"icon",at,issues);const iconSize=readOptionalNumber(value,"iconSize",at,issues,{positive:true});const strokeWidth=readOptionalNumber(value,"strokeWidth",at,issues,{nonNegative:true});const fill=value.fill;if(fill!==undefined&&typeof fill!=="boolean")issues.push(`${at}.fill must be a boolean`);if(id===undefined||type!=="rect"&&type!=="ellipse"||width===undefined||height===undefined){return null}return{id,type,width,height,...tone===undefined?{}:{tone},...opacity===undefined?{}:{opacity},...radius===undefined?{}:{radius},...labelContent,...icon===undefined?{}:{icon},...iconSize===undefined?{}:{iconSize},...strokeWidth===undefined?{}:{strokeWidth},...typeof fill==="boolean"?{fill}:{}}}function parseEdge(value,index,issues){const at=`edges[${index}]`;if(!isRecord(value)){issues.push(`${at} must be an object`);return null}validateKnownKeys(value,new Set(["id","from","to","label","tone","start","end","bend","arrowhead","startPosition","endPosition","labelFontSize","labelFontFamily","labelWeight","labelPosition","labelOffset"]),at,issues);const id=readString(value,"id",at,issues);const from=readString(value,"from",at,issues);const to=readString(value,"to",at,issues);if(id!==undefined&&!idPattern.test(id))issues.push(`${at}.id has unsupported characters`);const start=value.start;const end=value.end;if(start!==undefined&&(typeof start!=="string"||!anchors.has(start))){issues.push(`${at}.start must be auto, top, right, bottom, or left`)}if(end!==undefined&&(typeof end!=="string"||!anchors.has(end))){issues.push(`${at}.end must be auto, top, right, bottom, or left`)}const startPosition=readOptionalNumber(value,"startPosition",at,issues,{nonNegative:true});if(startPosition!==undefined&&startPosition>1){issues.push(`${at}.startPosition must not exceed 1`)}const endPosition=readOptionalNumber(value,"endPosition",at,issues,{nonNegative:true});if(endPosition!==undefined&&endPosition>1){issues.push(`${at}.endPosition must not exceed 1`)}const arrowhead=value.arrowhead;if(arrowhead!==undefined&&!["arrow","triangle","none"].includes(arrowhead)){issues.push(`${at}.arrowhead must be arrow, triangle, or none`)}const label=readOptionalString(value,"label",at,issues);const labelFontSize=readOptionalNumber(value,"labelFontSize",at,issues,{positive:true});const labelFontFamily=readOptionalFontFamily(value,"labelFontFamily",at,issues);const labelWeight=readOptionalWeight(value,"labelWeight",at,issues);const labelPosition=readOptionalNumber(value,"labelPosition",at,issues,{nonNegative:true});if(labelPosition!==undefined&&labelPosition>1){issues.push(`${at}.labelPosition must not exceed 1`)}const labelOffset=readOptionalNumber(value,"labelOffset",at,issues);if(value.label===undefined&&[value.labelFontSize,value.labelFontFamily,value.labelWeight,value.labelPosition,value.labelOffset].some((candidate)=>candidate!==undefined)){issues.push(`${at} cannot style or position a label that is not present`)}if(id===undefined||from===undefined||to===undefined)return null;return{id,from,to,...label===undefined?{}:{label,...labelFontSize===undefined?{}:{labelFontSize},...labelFontFamily===undefined?{}:{labelFontFamily},...labelWeight===undefined?{}:{labelWeight},...labelPosition===undefined?{}:{labelPosition},...labelOffset===undefined?{}:{labelOffset}},...readOptionalTone(value,"tone",at,issues)===undefined?{}:{tone:value.tone},...start===undefined?{}:{start},...end===undefined?{}:{end},...startPosition===undefined?{}:{startPosition},...endPosition===undefined?{}:{endPosition},...readOptionalNumber(value,"bend",at,issues)===undefined?{}:{bend:value.bend},...arrowhead===undefined?{}:{arrowhead}}}function parseStackLayout(value,issues){if(!isRecord(value)){issues.push("layout must be an object");return null}validateKnownKeys(value,new Set(["type","direction","gap","align"]),"layout",issues);if(value.type!=="stack")issues.push("layout.type must be stack");const direction=value.direction;if(typeof direction!=="string"||!stackDirections.has(direction)){issues.push("layout.direction must be horizontal or vertical")}const gap=readOptionalNumber(value,"gap","layout",issues,{nonNegative:true});const align=value.align;if(align!==undefined&&(typeof align!=="string"||!stackAlignments.has(align))){issues.push("layout.align must be start, center, or end")}if(value.type!=="stack"||typeof direction!=="string"||!stackDirections.has(direction)){return null}return{type:"stack",direction,...gap===undefined?{}:{gap},...align===undefined||!stackAlignments.has(align)?{}:{align}}}function parseDiagramSource(value){const issues=[];if(!isRecord(value))throw new DiagramValidationError(["root must be an object"]);const isStackSource="layout"in value;validateKnownKeys(value,new Set(["$schema","version","name","canvas","shapes","edges",...isStackSource?["layout"]:[]]),"root",issues);if(value.version!==diagramVersion)issues.push(`version must be ${diagramVersion}`);const name=readString(value,"name","root",issues);if(name!==undefined&&!namePattern.test(name)){issues.push("name must be lowercase kebab-case")}const canvasValue=value.canvas;let canvas=null;if(!isRecord(canvasValue)){issues.push("canvas must be an object")}else{validateKnownKeys(canvasValue,new Set(["width","height","padding"]),"canvas",issues);const width=readNumber(canvasValue,"width","canvas",issues,{positive:true});const height=readNumber(canvasValue,"height","canvas",issues,{positive:true});const padding=readOptionalNumber(canvasValue,"padding","canvas",issues,{nonNegative:true});if(width!==undefined&&height!==undefined){canvas={width,height,...padding===undefined?{}:{padding}}}}const layout=isStackSource?parseStackLayout(value.layout,issues):null;const shapesValue=value.shapes;const positionedShapes=[];const stackShapes=[];if(!Array.isArray(shapesValue)){issues.push("shapes must be an array")}else{if(isStackSource&&(shapesValue.length<1||shapesValue.length>9)){issues.push("stack layouts must contain between 1 and 9 shapes")}for(const[index,shape]of shapesValue.entries()){if(isStackSource){const parsed=parseStackShape(shape,index,issues);if(parsed!==null)stackShapes.push(parsed)}else{const parsed=parseShape(shape,index,issues);if(parsed!==null)positionedShapes.push(parsed)}}}const edgesValue=value.edges;const edges=[];if(edgesValue!==undefined){if(!Array.isArray(edgesValue)){issues.push("edges must be an array when present")}else{for(const[index,edge]of edgesValue.entries()){const parsed=parseEdge(edge,index,issues);if(parsed!==null)edges.push(parsed)}}}const shapes=isStackSource?stackShapes:positionedShapes;const allIds=new Set;for(const[kind,records]of[["shape",shapes],["edge",edges]]){for(const record of records){if(allIds.has(record.id))issues.push(`${kind} id ${record.id} is duplicated`);allIds.add(record.id)}}const connectableIds=new Set(shapes.filter((shape)=>shape.type==="rect"||shape.type==="ellipse").map((shape)=>shape.id));for(const edge of edges){if(!connectableIds.has(edge.from))issues.push(`edge ${edge.id} has unknown or non-connectable from id ${edge.from}`);if(!connectableIds.has(edge.to))issues.push(`edge ${edge.id} has unknown or non-connectable to id ${edge.to}`);if(edge.from===edge.to)issues.push(`edge ${edge.id} cannot connect a shape to itself`)}if(isStackSource){const indexes=new Map(stackShapes.map((shape,index)=>[shape.id,index]));const connectedPairs=new Set;for(const edge of edges){const fromIndex=indexes.get(edge.from);const toIndex=indexes.get(edge.to);if(fromIndex!==undefined&&toIndex!==undefined&&Math.abs(fromIndex-toIndex)!==1){issues.push(`edge ${edge.id} must connect adjacent stack shapes`)}if(fromIndex!==undefined&&toIndex!==undefined&&fromIndex!==toIndex){const pair=[fromIndex,toIndex].sort((left,right)=>left-right).join(":");if(connectedPairs.has(pair)){issues.push(`edge ${edge.id} duplicates a connection between the same stack shapes`)}connectedPairs.add(pair)}if(edge.start!==undefined&&edge.start!=="auto"){issues.push(`edge ${edge.id}.start must be auto or omitted in a stack layout`)}if(edge.end!==undefined&&edge.end!=="auto"){issues.push(`edge ${edge.id}.end must be auto or omitted in a stack layout`)}if(edge.bend!==undefined&&edge.bend!==0){issues.push(`edge ${edge.id}.bend must be 0 or omitted in a stack layout`)}if(edge.startPosition!==undefined){issues.push(`edge ${edge.id}.startPosition is not supported in a stack layout`)}if(edge.endPosition!==undefined){issues.push(`edge ${edge.id}.endPosition is not supported in a stack layout`)}}}if(issues.length>0||name===undefined||canvas===null||isStackSource&&layout===null){throw new DiagramValidationError(issues)}const common={..."$schema"in value&&typeof value.$schema==="string"?{$schema:value.$schema}:{},version:diagramVersion,name,canvas};if(isStackSource){const stackEdges=edges.map((edge)=>({id:edge.id,from:edge.from,to:edge.to,...edge.label===undefined?{}:{label:edge.label},...edge.tone===undefined?{}:{tone:edge.tone},...edge.start==="auto"?{start:edge.start}:{},...edge.end==="auto"?{end:edge.end}:{},...edge.bend===0?{bend:edge.bend}:{},...edge.arrowhead===undefined?{}:{arrowhead:edge.arrowhead},...edge.label===undefined?{}:{...edge.labelFontSize===undefined?{}:{labelFontSize:edge.labelFontSize},...edge.labelFontFamily===undefined?{}:{labelFontFamily:edge.labelFontFamily},...edge.labelWeight===undefined?{}:{labelWeight:edge.labelWeight},...edge.labelPosition===undefined?{}:{labelPosition:edge.labelPosition},...edge.labelOffset===undefined?{}:{labelOffset:edge.labelOffset}}}));return{...common,layout,shapes:stackShapes,...edgesValue===undefined?{}:{edges:stackEdges}}}return{...common,shapes:positionedShapes,...edgesValue===undefined?{}:{edges}}}function parseDiagramSpec(value){return resolveDiagramSource(parseDiagramSource(value))}var schema={schemaVersion:2,sequences:{"com.tldraw.store":5,"com.tldraw.asset":1,"com.tldraw.camera":1,"com.tldraw.document":2,"com.tldraw.instance":26,"com.tldraw.instance_page_state":5,"com.tldraw.page":1,"com.tldraw.instance_presence":6,"com.tldraw.pointer":1,"com.tldraw.shape":4,"com.tldraw.user":1,"com.tldraw.asset.image":6,"com.tldraw.asset.video":5,"com.tldraw.asset.bookmark":2,"com.tldraw.shape.group":0,"com.tldraw.shape.text":4,"com.tldraw.shape.bookmark":2,"com.tldraw.shape.draw":5,"com.tldraw.shape.geo":11,"com.tldraw.shape.note":13,"com.tldraw.shape.line":5,"com.tldraw.shape.frame":1,"com.tldraw.shape.arrow":8,"com.tldraw.shape.highlight":4,"com.tldraw.shape.embed":4,"com.tldraw.shape.image":5,"com.tldraw.shape.video":4,"com.tldraw.binding.arrow":1}};var tldrawColors={neutral:"black",blue:"blue",orange:"orange",green:"green",red:"red",purple:"violet",yellow:"yellow"};function richText(text,weight=400){return{type:"doc",content:text.split(`
8
8
  `).map((line)=>({type:"paragraph",...line===""?{}:{content:[{type:"text",text:line,...weight<600?{}:{marks:[{type:"bold"}]}}]}}))}}function authoredShapeId(id){return`shape:${id}`}function generatedShapeId(kind,id){return`shape:slopcamera:${kind}:${id}`}function shapeMeta(sourceId){return{diagram:{version:1,sourceId}}}function indexKey(index){if(!Number.isSafeInteger(index)||index<0){throw new Error(`A .tldr index must be a non-negative safe integer, received ${index}`)}const digits="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";let width=1;let remaining=index;let capacity=digits.length;while(remaining>=capacity){remaining-=capacity;width+=1;capacity*=digits.length;if(width>26||!Number.isSafeInteger(capacity)){throw new Error("A .tldr export contains too many generated records")}}let suffix="";for(let place=0;place<width;place+=1){suffix=`${digits[remaining%digits.length]}${suffix}`;remaining=Math.floor(remaining/digits.length)}return`${String.fromCharCode(97+width-1)}${suffix}`}function baseShape(shape,index){return{x:shape.x,y:shape.y,rotation:0,isLocked:false,opacity:shape.opacity??1,meta:shapeMeta(shape.id),id:authoredShapeId(shape.id),parentId:"page:page",index:indexKey(index),typeName:"shape"}}function textShape(options){return{x:options.x,y:options.y,rotation:0,isLocked:false,opacity:options.opacity??1,meta:shapeMeta(options.sourceId),id:options.recordId,type:"text",props:{color:tldrawColors[options.tone],size:options.size,w:options.width,font:options.fontFamily==="mono"?"mono":"sans",textAlign:options.align,autoSize:false,scale:options.scale??1,richText:richText(options.text,options.weight)},parentId:"page:page",index:indexKey(options.index),typeName:"shape"}}function svgIconAsset(icon,color){const clean=sanitizeIcon(icon);const svg=`<svg xmlns="http://www.w3.org/2000/svg" width="96" height="96" viewBox="${clean.viewBox}" fill="none" color="${color}">${clean.body}</svg>`;return`data:image/svg+xml;base64,${Buffer.from(svg).toString("base64")}`}var tldrawTextFontSizes={s:18,m:24,l:36,xl:44};function scaledTldrawSize(fontSize){const size=fontSize<21?"s":fontSize<30?"m":fontSize<40?"l":"xl";return{size,scale:fontSize/tldrawTextFontSizes[size]}}function serializeTldr(spec,config){const records=[{gridSize:10,name:spec.name,meta:{diagram:{version:1}},id:"document:document",typeName:"document"},{meta:{},id:"page:page",name:"Page 1",index:"a1",typeName:"page"}];const icons=config.icons??{};const lightTheme=resolveTheme("light",config);let generatedIndex=1;for(const shape of spec.shapes){generatedIndex+=1;if(shape.type==="rect"||shape.type==="ellipse"){const box=shape;const hasIcon=box.icon!==undefined;const hasSeparateLabel=box.labelRows!==undefined||box.label!==undefined&&(hasIcon||box.labelFontSize!==undefined||box.labelFontFamily!==undefined||box.labelWeight!==undefined);records.push({...baseShape(box,generatedIndex),type:"geo",props:{w:box.width,h:box.height,geo:box.type==="ellipse"?"ellipse":"rectangle",dash:"solid",growY:0,url:"",scale:1,color:tldrawColors[box.tone??"neutral"],labelColor:"black",fill:box.fill===false?"none":"solid",size:(box.strokeWidth??2)>=4?"l":"m",font:"sans",align:"middle",verticalAlign:"middle",richText:richText(hasSeparateLabel?"":box.label??"",box.labelWeight)}});const iconSize=Math.min(box.iconSize??52,box.height*0.45,box.width*0.32);const content=layoutBoxContent(box,hasIcon?iconSize:undefined);if(box.icon!==undefined){const icon=icons[box.icon];if(icon===undefined)throw new Error(`Unknown icon "${box.icon}" on shape ${box.id}`);if(content.icon===undefined)throw new Error(`Missing icon layout for shape ${box.id}`);const assetId=`asset:icon-${box.id}`;records.push({id:assetId,type:"image",typeName:"asset",props:{name:`${box.icon}.svg`,src:svgIconAsset(icon,lightTheme.tones[box.tone??"neutral"].text),w:96,h:96,mimeType:"image/svg+xml",isAnimated:false},meta:{diagram:{version:1,icon:box.icon}}});generatedIndex+=1;records.push({x:content.icon.x,y:content.icon.y,rotation:0,isLocked:false,opacity:box.opacity??1,meta:shapeMeta(box.id),id:generatedShapeId("box-icon",box.id),type:"image",props:{w:iconSize,h:iconSize,assetId,playing:true,url:"",crop:null,flipX:false,flipY:false,altText:box.labelRows?.map((row)=>row.text).join(" ")??box.label??box.icon},parentId:"page:page",index:indexKey(generatedIndex),typeName:"shape"})}if(hasSeparateLabel){for(const[rowIndex,row]of content.rows.entries()){const textStyle=scaledTldrawSize(row.fontSize);generatedIndex+=1;records.push(textShape({recordId:generatedShapeId("box-label",`${box.id}:${rowIndex+1}`),sourceId:box.id,x:box.x+16,y:row.y,width:Math.max(1,box.width-32)/textStyle.scale,text:row.lines.join(`
9
9
  `),tone:box.tone??"neutral",size:textStyle.size,align:"middle",index:generatedIndex,scale:textStyle.scale,fontFamily:row.fontFamily,weight:row.weight,...box.opacity===undefined?{}:{opacity:box.opacity}}))}}continue}if(shape.type==="text"){const text=shape;const fontSize=text.fontSize??24;const textStyle=scaledTldrawSize(fontSize);const width=text.width??Math.max(8,text.text.length*fontSize*0.58);records.push(textShape({recordId:authoredShapeId(text.id),sourceId:text.id,x:text.x,y:text.y,width:width/textStyle.scale,text:text.text,tone:text.tone??"neutral",size:textStyle.size,align:text.align??"start",index:generatedIndex,scale:textStyle.scale,...text.fontFamily===undefined?{}:{fontFamily:text.fontFamily},...text.weight===undefined?{}:{weight:text.weight},...text.opacity===undefined?{}:{opacity:text.opacity}}));continue}const line=shape;records.push({...baseShape(line,generatedIndex),type:"line",props:{dash:"solid",size:(line.strokeWidth??3)>=4?"l":"m",color:tldrawColors[line.tone??"neutral"],spline:"line",points:{a1:{id:"a1",index:"a1",x:0,y:0},a2:{id:"a2",index:"a2",x:line.x2-line.x,y:line.y2-line.y}},scale:1}})}for(const edge of spec.edges??[]){const resolved=resolveEdge(spec,edge);generatedIndex+=1;const arrowId=authoredShapeId(edge.id);records.push({x:resolved.start.x,y:resolved.start.y,rotation:0,isLocked:false,opacity:1,meta:shapeMeta(edge.id),id:arrowId,type:"arrow",props:{kind:"arc",elbowMidPoint:0.5,dash:"solid",size:"m",fill:"none",color:tldrawColors[edge.tone??"neutral"],labelColor:tldrawColors[edge.tone??"neutral"],bend:(edge.bend??0)/2,start:{x:0,y:0},end:{x:resolved.end.x-resolved.start.x,y:resolved.end.y-resolved.start.y},arrowheadStart:"none",arrowheadEnd:edge.arrowhead??"arrow",richText:richText(""),labelPosition:edge.labelPosition??0.5,font:edge.labelFontFamily==="mono"?"mono":"sans",scale:1},parentId:"page:page",index:indexKey(generatedIndex),typeName:"shape"});if(edge.label!==undefined){const labelPoint=resolveEdgeLabel(resolved);const fontSize=edge.labelFontSize??18;const fontFamily=edge.labelFontFamily??"default";const textStyle=scaledTldrawSize(fontSize);const widthRatio=fontFamily==="mono"?0.62:0.56;const width=Math.max(fontSize,edge.label.length*fontSize*widthRatio);generatedIndex+=1;records.push(textShape({recordId:generatedShapeId("edge-label",edge.id),sourceId:edge.id,x:labelPoint.x-width/2,y:labelPoint.y-fontSize*1.35/2,width:width/textStyle.scale,text:edge.label,tone:edge.tone??"neutral",size:textStyle.size,align:"middle",index:generatedIndex,scale:textStyle.scale,fontFamily,weight:edge.labelWeight??600}))}records.push({meta:{},id:`binding:${edge.id}-start`,fromId:arrowId,toId:authoredShapeId(edge.from),type:"arrow",props:{isPrecise:edge.startPosition!==undefined,isExact:false,normalizedAnchor:resolved.start.normalized,snap:"none",terminal:"start"},typeName:"binding"},{meta:{},id:`binding:${edge.id}-end`,fromId:arrowId,toId:authoredShapeId(edge.to),type:"arrow",props:{isPrecise:edge.endPosition!==undefined,isExact:false,normalizedAnchor:resolved.end.normalized,snap:"none",terminal:"end"},typeName:"binding"})}return`${JSON.stringify({tldrawFileFormatVersion:1,schema,records},null,2)}
10
- `}import{createHash,randomUUID}from"crypto";import{mkdir,rename,rm,writeFile}from"fs/promises";import{dirname,resolve}from"path";import{z}from"zod";var slopcameraIconDefaultInk="#2474d4";var slopcameraIconPanel="#f7f8fb";var slopcameraIconDefaultRounds=2;var slopcameraIconMaximumRounds=4;var slopcameraIconSubjectMaximumBytes=1024;var slopcameraIconCritiqueDefaultModel="google/gemini-3-flash";var slopcameraIconCritiqueTimeoutMs=120000;var iconCritiqueMaximumResponseBytes=8*1024*1024;var iconPreviewMaximumEdge=448;var iconCoverageAlphaFloor=24;var iconCropAlphaFloor=16;var iconMarginRatio=0.08;var MARK_CRITIQUE_SYSTEM=`You are a strict design reviewer for small product marks.
10
+ `}import{createHash,randomUUID}from"crypto";import{mkdir,rename,rm,writeFile}from"fs/promises";import{dirname,resolve}from"path";import{z}from"zod";var slopcameraIconDefaultInk="#2474d4";var slopcameraIconPanel="#f7f8fb";var slopcameraIconDefaultRounds=2;var slopcameraIconMaximumRounds=4;var slopcameraIconSubjectMaximumBytes=1024;var slopcameraIconCritiqueDefaultModel="google/gemini-3-flash";var slopcameraIconCritiqueTimeoutMs=120000;var iconCritiqueMaximumResponseBytes=8*1024*1024;var iconPreviewMaximumEdge=448;var iconCoverageAlphaFloor=24;var iconCropAlphaFloor=16;var iconMarginRatio=0.08;var iconMetricNormalizationEdge=512;var iconStrokeMinimumRemaining=0.1;var slopcameraIconContexts=["card","hero","inline"];var slopcameraIconContextProfiles=Object.freeze({card:{aspectMaximum:1.6,coverageMaximum:0.55,coverageMinimum:0.08,pathCountMaximum:64,prompt:"The illustration sits in a compact feature card rendered about 88 pixels tall beside sibling cards. "+"Keep the silhouette readable at that size: medium-bold uniform strokes, a few large planes, and minimal interior detail.",strokePxMinimum:8},hero:{aspectMaximum:1.8,coverageMaximum:0.65,coverageMinimum:0.05,pathCountMaximum:96,prompt:"The illustration leads a marketing hero at roughly 300 pixels or larger. "+"Slightly richer planes are acceptable, but stay inside the single-ink isometric family.",strokePxMinimum:5},inline:{aspectMaximum:1.5,coverageMaximum:0.55,coverageMinimum:0.14,pathCountMaximum:32,prompt:"The illustration appears inline beside text at about 32 to 48 pixels. "+"Reduce the subject to one or two bold masses with no interior detail.",strokePxMinimum:12}});var MARK_CRITIQUE_SYSTEM=`You are a strict design reviewer for small product marks.
11
11
 
12
12
  Style contract:
13
13
  - A distinctive, immediately recognizable silhouette or geometric symbol.
@@ -23,13 +23,16 @@ Style contract:
23
23
  - No hairlines, hatching, texture, tiny repeated detail, shading, gradients, shadows, text, or stray background objects.
24
24
  - No important feature may disappear at 64 px. Center the subject with a modest clear margin.
25
25
 
26
- Judge the attached rendered illustration against the contract and whether it clearly depicts the requested subject. Return pass only when it is ready for a marketing header or feature section. Score is an integer from 0 to 100. When it fails, make promptFix a concrete image-prompt correction that addresses the listed problems.`;function invalidArgument(message){throw new SlopcameraCloudError("INVALID_ARGUMENT",message)}function isRecord2(value){return typeof value==="object"&&value!==null&&!Array.isArray(value)}function isObjectLike(value){return typeof value==="object"&&value!==null||typeof value==="function"}function validateSubject(value){if(typeof value!=="string"||value.trim().length===0||/[\u0000-\u001f\u007f]/u.test(value)||Buffer.byteLength(value,"utf8")>slopcameraIconSubjectMaximumBytes){invalidArgument(`Subject must be non-empty text no more than ${slopcameraIconSubjectMaximumBytes} UTF-8 bytes.`)}return value.trim()}function validateIconModel(value,name){if(typeof value!=="string"||value.length<3||value.length>256||!/^[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._:-]*$/iu.test(value)){invalidArgument(`${name} must be a bounded Vercel AI Gateway provider/model id.`)}return value}function validateRounds(value){const rounds=value??slopcameraIconDefaultRounds;if(!Number.isInteger(rounds)||rounds<1||rounds>slopcameraIconMaximumRounds){invalidArgument(`rounds must be an integer from 1 through ${slopcameraIconMaximumRounds}.`)}return rounds}function iconPromptFor(subject,options={}){const ink=normalizedHexColor(options.ink??slopcameraIconDefaultInk);const purpose=options.purpose??"illustration";const sections=purpose==="mark"?[`A single small product mark for ${subject}.`,"Style rules: front-facing or simple isometric geometry; a distinctive silhouette built from one to three bold masses; "+`one single ink color ${ink} on a flat near-white background ${slopcameraIconPanel}; `+"negative space may separate major parts; no thin outlines, hatching, texture, tiny holes, shading, gradients, shadows, text, border, container shape, or extra objects; "+"clear and recognizable at 16 pixels, balanced at 32 pixels, centered with a modest optical margin."]:[`A single minimal product-brand illustration of ${subject}.`,"Style rules: simple orthographic isometric projection; uniform medium-weight structural lines and at most three large filled planes; "+`one single ink color ${ink} on a flat near-white background ${slopcameraIconPanel}; `+"no hairlines, hatching, texture, tiny repeated detail, shading, gradients, shadows, text, border, or extra objects; "+"every important feature remains visible at 64 pixels; centered with a modest clear margin; clean geometric edges."];const feedback=options.feedback?.trim();if(feedback!==undefined&&feedback.length>0){sections.push(`The previous attempt was rejected. Correct it: ${feedback}`)}return sections.join(`
26
+ Judge the attached rendered illustration against the contract and whether it clearly depicts the requested subject. Return pass only when it is ready for a marketing header or feature section. Score is an integer from 0 to 100. When it fails, make promptFix a concrete image-prompt correction that addresses the listed problems.`;var INLINE_CRITIQUE_CLAUSE=`
27
27
 
28
- `)}function medianChannel(values){const sorted=[...values].sort((left,right)=>left-right);return sorted[Math.floor(sorted.length/2)]}function borderBackground(rgba,width,height){const thickness=Math.max(1,Math.round(Math.min(width,height)*0.02));const step=Math.max(1,Math.floor(2*thickness*(width+height)/8192));const red=[];const green=[];const blue=[];let sampled=0;const collect=(x,y)=>{if(sampled%step!==0){sampled+=1;return}sampled+=1;const index=(y*width+x)*4;const alpha=rgba[index+3];if(alpha<128)return;red.push(rgba[index]);green.push(rgba[index+1]);blue.push(rgba[index+2])};for(let y=0;y<height;y+=1){for(let x=0;x<width;x+=1){if(x<thickness||x>=width-thickness||y<thickness||y>=height-thickness){collect(x,y)}}}if(red.length===0){return[252,252,252]}return[medianChannel(red),medianChannel(green),medianChannel(blue)]}function measuredInkColor(rgba,width,height,background){const total=width*height;const step=Math.max(1,Math.floor(total/1e6));const bins=new Map;const counts=new Map;for(let pixel=0;pixel<total;pixel+=step){const index=pixel*4;if(rgba[index+3]<128)continue;const dr=rgba[index]-background[0];const dg=rgba[index+1]-background[1];const db=rgba[index+2]-background[2];if(Math.sqrt(dr*dr+dg*dg+db*db)<44)continue;const key=rgba[index]>>3<<10|rgba[index+1]>>3<<5|rgba[index+2]>>3;counts.set(key,(counts.get(key)??0)+1);const bucket2=bins.get(key)??[];if(bucket2.length<4096){bucket2.push(rgba[index],rgba[index+1],rgba[index+2]);bins.set(key,bucket2)}}const dominant=[...counts.entries()].sort((left,right)=>right[1]-left[1]||left[0]-right[0])[0];if(dominant===undefined){throw new SlopcameraCloudError("GENERATION_INVALID_RESPONSE","The generated icon raster contains no ink strokes.")}const bucket=bins.get(dominant[0]);const red=[];const green=[];const blue=[];for(let index=0;index+2<bucket.length;index+=3){red.push(bucket[index]);green.push(bucket[index+1]);blue.push(bucket[index+2])}return[medianChannel(red),medianChannel(green),medianChannel(blue)]}function dropSpeckles(alpha,width,height){const minimumComponent=Math.max(8,Math.floor(width*height*0.00004));const seen=new Uint8Array(width*height);const queue=new Int32Array(width*height);let removed=0;for(let start=0;start<alpha.length;start+=1){if(alpha[start]<iconCoverageAlphaFloor||seen[start]===1)continue;let head=0;let tail=0;queue[tail]=start;tail+=1;seen[start]=1;const component=[];let minimumX=width;let maximumX=-1;let minimumY=height;let maximumY=-1;while(head<tail){const pixel=queue[head];head+=1;component.push(pixel);const x=pixel%width;const y=Math.floor(pixel/width);minimumX=Math.min(minimumX,x);maximumX=Math.max(maximumX,x);minimumY=Math.min(minimumY,y);maximumY=Math.max(maximumY,y);const neighbors=[x>0?pixel-1:-1,x<width-1?pixel+1:-1,y>0?pixel-width:-1,y<height-1?pixel+width:-1];for(const neighbor of neighbors){if(neighbor>=0&&seen[neighbor]===0&&alpha[neighbor]>=iconCoverageAlphaFloor){seen[neighbor]=1;queue[tail]=neighbor;tail+=1}}}const componentWidth=maximumX-minimumX+1;const componentHeight=maximumY-minimumY+1;const spansWidth=minimumX===0&&maximumX===width-1;const spansHeight=minimumY===0&&maximumY===height-1;const elongatedBorderArtifact=(spansWidth||spansHeight)&&Math.max(componentWidth/componentHeight,componentHeight/componentWidth)>8;if(component.length<minimumComponent||elongatedBorderArtifact){removed+=1;for(const pixel of component)alpha[pixel]=0}}return removed}function iconVisualGateProblems(purpose,metrics,pathCount){const problems=[];const aspectRatio=Math.max(metrics.width/metrics.height,metrics.height/metrics.width);if(aspectRatio>1.8)problems.push("the subject is too narrow or elongated");if(purpose==="mark"){if(metrics.coverageRatio<0.14)problems.push("the mark has too little bold visual mass");if(metrics.coverageRatio>0.72)problems.push("the mark has too little negative space");if(pathCount>12)problems.push("the mark has too many separate vector paths")}else{if(metrics.coverageRatio<0.035)problems.push("the illustration lines are too sparse or thin");if(metrics.coverageRatio>0.62)problems.push("the illustration is too visually dense");if(pathCount>48)problems.push("the illustration has too much vector detail")}return problems}function extractIconLineArt(rgba,width,height,options={}){if(rgba.length===0||rgba.length%4!==0||!Number.isInteger(width)||!Number.isInteger(height)||width<1||height<1||rgba.length!==width*height*4){invalidArgument("Icon line-art extraction requires a nonempty RGBA raster.")}const ink=normalizedHexColor(options.ink??slopcameraIconDefaultInk);const[inkRed,inkGreen,inkBlue]=parseHexColor(ink);const background=borderBackground(rgba,width,height);const measured=measuredInkColor(rgba,width,height,background);const axis=[measured[0]-background[0],measured[1]-background[1],measured[2]-background[2]];let axisLength2=axis[0]*axis[0]+axis[1]*axis[1]+axis[2]*axis[2];let direction=axis;if(axisLength2<27){direction=[-1,-1,-1];axisLength2=3}const alpha=new Uint8Array(width*height);let coverageMass=0;for(let pixel=0;pixel<width*height;pixel+=1){const index=pixel*4;const sourceAlpha=rgba[index+3]/255;const coverage2=Math.max(0,Math.min(1,((rgba[index]-background[0])*direction[0]+(rgba[index+1]-background[1])*direction[1]+(rgba[index+2]-background[2])*direction[2])/axisLength2));const value=Math.round(255*coverage2*sourceAlpha);alpha[pixel]=value;coverageMass+=value}if(coverageMass===0){throw new SlopcameraCloudError("GENERATION_INVALID_RESPONSE","The generated icon raster contains no ink coverage.")}const removedComponents=dropSpeckles(alpha,width,height);let minimumX=width;let minimumY=height;let maximumX=-1;let maximumY=-1;for(let y=0;y<height;y+=1){for(let x=0;x<width;x+=1){if(alpha[y*width+x]>=iconCropAlphaFloor){if(x<minimumX)minimumX=x;if(x>maximumX)maximumX=x;if(y<minimumY)minimumY=y;if(y>maximumY)maximumY=y}}}if(maximumX<0){throw new SlopcameraCloudError("GENERATION_INVALID_RESPONSE","The generated icon raster contains no ink coverage.")}const margin=Math.round(Math.max(maximumX-minimumX+1,maximumY-minimumY+1)*iconMarginRatio);const cropX=Math.max(0,minimumX-margin);const cropY=Math.max(0,minimumY-margin);const cropWidth=Math.min(width,maximumX+1+margin)-cropX;const cropHeight=Math.min(height,maximumY+1+margin)-cropY;const pixels=new Uint8Array(cropWidth*cropHeight*4);let coverage=0;for(let y=0;y<cropHeight;y+=1){for(let x=0;x<cropWidth;x+=1){const sourceIndex=(cropY+y)*width+cropX+x;const targetIndex=(y*cropWidth+x)*4;const measuredValue=alpha[sourceIndex];const value=options.hardEdges===true?measuredValue>=iconCoverageAlphaFloor?255:0:measuredValue;coverage+=value;pixels[targetIndex]=inkRed;pixels[targetIndex+1]=inkGreen;pixels[targetIndex+2]=inkBlue;pixels[targetIndex+3]=value}}const toHex=(channel)=>channel.toString(16).padStart(2,"0");return{background:`#${background.map(toHex).join("")}`,coverageRatio:coverage/(cropWidth*cropHeight*255),height:cropHeight,measuredInk:`#${measured.map(toHex).join("")}`,pixels,removedComponents,sourceHeight:height,sourceWidth:width,width:cropWidth}}var iconCritiqueSchema=z.object({pass:z.boolean(),problems:z.array(z.string()),promptFix:z.string(),score:z.number()});function boundedText(value,maximum){return value.replace(/[\u0000-\u001f\u007f]/gu," ").slice(0,maximum)}function parseIconCritique(output,resolvedModel){const parsed=iconCritiqueSchema.safeParse(output);if(!parsed.success){throw new SlopcameraCloudError("GENERATION_INVALID_RESPONSE","The icon critique returned an invalid bounded object.")}const score=parsed.data.score;if(!Number.isFinite(score)){throw new SlopcameraCloudError("GENERATION_INVALID_RESPONSE","The icon critique returned an invalid score.")}return{pass:parsed.data.pass,problems:parsed.data.problems.slice(0,8).map((problem)=>boundedText(problem,300)),promptFix:boundedText(parsed.data.promptFix,2000),resolvedModel,score:Math.max(0,Math.min(100,score))}}async function loadIconLanguageRuntime(){let aiModule;let gatewayModule;try{[aiModule,gatewayModule]=await Promise.all([import("ai-v7"),import("@ai-sdk/gateway-v4")])}catch{throw new SlopcameraCloudError("GENERATION_FAILED","The Vercel AI Gateway runtime is unavailable.")}if(!isRecord2(aiModule)||!isRecord2(gatewayModule)||typeof aiModule.generateText!=="function"||!isRecord2(aiModule.Output)||typeof aiModule.Output.object!=="function"||typeof gatewayModule.createGateway!=="function"){throw new SlopcameraCloudError("GENERATION_FAILED","The Vercel AI Gateway runtime is unavailable.")}const createGateway=gatewayModule.createGateway;return{Output:aiModule.Output,createGateway:(settings)=>{const provider=createGateway(settings);if(!isObjectLike(provider)||typeof provider.languageModel!=="function"){throw new SlopcameraCloudError("GENERATION_FAILED","The Vercel AI Gateway runtime is unavailable.")}const languageModel=provider.languageModel;return{languageModel:(id)=>languageModel.call(provider,id)}},generateText:aiModule.generateText}}function disableAiSdkWarningLogging(){globalThis.AI_SDK_LOG_WARNINGS=false}function resolvedModelId(value){const modelId=isRecord2(value)?value.modelId:undefined;return typeof modelId==="string"&&modelId.length>0&&modelId.length<=256?modelId:null}async function critiqueIconRaster(input,dependencies){const runtime=await(dependencies.loadLanguageRuntime??loadIconLanguageRuntime)();const controller=new AbortController;const abort=()=>controller.abort(input.signal?.reason);input.signal?.addEventListener("abort",abort,{once:true});if(input.signal?.aborted===true)abort();const timer=setTimeout(()=>controller.abort(),slopcameraIconCritiqueTimeoutMs);try{disableAiSdkWarningLogging();const apiKey=resolveSlopcameraGatewayCredential(dependencies.environment).token;const gateway=runtime.createGateway({apiKey,baseURL:slopcameraGatewayApiBaseUrl,fetch:createFixedGatewayFetch({...dependencies.fetch===undefined?{}:{fetch:dependencies.fetch},maximumResponseBytes:iconCritiqueMaximumResponseBytes})});const output=runtime.Output.object({description:`One bounded style critique for a rendered product ${input.purpose}.`,name:"slopcamera_icon_critique",schema:iconCritiqueSchema});const result=await runtime.generateText({abortSignal:controller.signal,maxOutputTokens:2048,maxRetries:0,messages:[{content:[{text:`Subject: ${input.subject}
28
+ Context note: this illustration renders inline beside text at about 32 to 48 pixels. Judge silhouette legibility at that size above isometric depth \u2014 a flat bold pictogram satisfies the contract when it reads clearly and still carries one ink color on the flat near-white background.`;function invalidArgument(message){throw new SlopcameraCloudError("INVALID_ARGUMENT",message)}function isRecord2(value){return typeof value==="object"&&value!==null&&!Array.isArray(value)}function isObjectLike(value){return typeof value==="object"&&value!==null||typeof value==="function"}function validateSubject(value){if(typeof value!=="string"||value.trim().length===0||/[\u0000-\u001f\u007f]/u.test(value)||Buffer.byteLength(value,"utf8")>slopcameraIconSubjectMaximumBytes){invalidArgument(`Subject must be non-empty text no more than ${slopcameraIconSubjectMaximumBytes} UTF-8 bytes.`)}return value.trim()}function validateIconModel(value,name){if(typeof value!=="string"||value.length<3||value.length>256||!/^[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._:-]*$/iu.test(value)){invalidArgument(`${name} must be a bounded Vercel AI Gateway provider/model id.`)}return value}function validateRounds(value){const rounds=value??slopcameraIconDefaultRounds;if(!Number.isInteger(rounds)||rounds<1||rounds>slopcameraIconMaximumRounds){invalidArgument(`rounds must be an integer from 1 through ${slopcameraIconMaximumRounds}.`)}return rounds}function iconPromptFor(subject,options={}){const ink=normalizedHexColor(options.ink??slopcameraIconDefaultInk);const purpose=options.purpose??"illustration";const sections=purpose==="mark"?[`A single small product mark for ${subject}.`,"Style rules: front-facing or simple isometric geometry; a distinctive silhouette built from one to three bold masses; "+`one single ink color ${ink} on a flat near-white background ${slopcameraIconPanel}; `+"negative space may separate major parts; no thin outlines, hatching, texture, tiny holes, shading, gradients, shadows, text, border, container shape, or extra objects; "+"clear and recognizable at 16 pixels, balanced at 32 pixels, centered with a modest optical margin."]:[`A single minimal product-brand illustration of ${subject}.`,options.context==="inline"?"Style rules: a bold flat pictogram or slightly dimensional shape; uniform bold structural lines with at most two large filled masses; "+`one single ink color ${ink} on a flat near-white background ${slopcameraIconPanel}; `+"no hairlines, hatching, texture, repeated detail, shading, gradients, shadows, text, border, or extra objects; "+"the silhouette must remain recognizable at 32 pixels; centered with a modest clear margin; clean geometric edges.":"Style rules: simple orthographic isometric projection; uniform medium-weight structural lines and at most three large filled planes; "+`one single ink color ${ink} on a flat near-white background ${slopcameraIconPanel}; `+"no hairlines, hatching, texture, tiny repeated detail, shading, gradients, shadows, text, border, or extra objects; "+"every important feature remains visible at 64 pixels; centered with a modest clear margin; clean geometric edges.",...options.context===undefined?[]:[`Placement: ${slopcameraIconContextProfiles[options.context].prompt}`]];const feedback=options.feedback?.trim();if(feedback!==undefined&&feedback.length>0){sections.push(`The previous attempt was rejected. Correct it: ${feedback}`)}return sections.join(`
29
+
30
+ `)}function medianChannel(values){const sorted=[...values].sort((left,right)=>left-right);return sorted[Math.floor(sorted.length/2)]}function borderBackground(rgba,width,height){const thickness=Math.max(1,Math.round(Math.min(width,height)*0.02));const step=Math.max(1,Math.floor(2*thickness*(width+height)/8192));const red=[];const green=[];const blue=[];let sampled=0;const collect=(x,y)=>{if(sampled%step!==0){sampled+=1;return}sampled+=1;const index=(y*width+x)*4;const alpha=rgba[index+3];if(alpha<128)return;red.push(rgba[index]);green.push(rgba[index+1]);blue.push(rgba[index+2])};for(let y=0;y<height;y+=1){for(let x=0;x<width;x+=1){if(x<thickness||x>=width-thickness||y<thickness||y>=height-thickness){collect(x,y)}}}if(red.length===0){return[252,252,252]}return[medianChannel(red),medianChannel(green),medianChannel(blue)]}function measuredInkColor(rgba,width,height,background){const total=width*height;const step=Math.max(1,Math.floor(total/1e6));const bins=new Map;const counts=new Map;for(let pixel=0;pixel<total;pixel+=step){const index=pixel*4;if(rgba[index+3]<128)continue;const dr=rgba[index]-background[0];const dg=rgba[index+1]-background[1];const db=rgba[index+2]-background[2];if(Math.sqrt(dr*dr+dg*dg+db*db)<44)continue;const key=rgba[index]>>3<<10|rgba[index+1]>>3<<5|rgba[index+2]>>3;counts.set(key,(counts.get(key)??0)+1);const bucket2=bins.get(key)??[];if(bucket2.length<4096){bucket2.push(rgba[index],rgba[index+1],rgba[index+2]);bins.set(key,bucket2)}}const dominant=[...counts.entries()].sort((left,right)=>right[1]-left[1]||left[0]-right[0])[0];if(dominant===undefined){throw new SlopcameraCloudError("GENERATION_INVALID_RESPONSE","The generated icon raster contains no ink strokes.")}const bucket=bins.get(dominant[0]);const red=[];const green=[];const blue=[];for(let index=0;index+2<bucket.length;index+=3){red.push(bucket[index]);green.push(bucket[index+1]);blue.push(bucket[index+2])}return[medianChannel(red),medianChannel(green),medianChannel(blue)]}function dropSpeckles(alpha,width,height){const minimumComponent=Math.max(8,Math.floor(width*height*0.00004));const seen=new Uint8Array(width*height);const queue=new Int32Array(width*height);let removed=0;for(let start=0;start<alpha.length;start+=1){if(alpha[start]<iconCoverageAlphaFloor||seen[start]===1)continue;let head=0;let tail=0;queue[tail]=start;tail+=1;seen[start]=1;const component=[];let minimumX=width;let maximumX=-1;let minimumY=height;let maximumY=-1;while(head<tail){const pixel=queue[head];head+=1;component.push(pixel);const x=pixel%width;const y=Math.floor(pixel/width);minimumX=Math.min(minimumX,x);maximumX=Math.max(maximumX,x);minimumY=Math.min(minimumY,y);maximumY=Math.max(maximumY,y);const neighbors=[x>0?pixel-1:-1,x<width-1?pixel+1:-1,y>0?pixel-width:-1,y<height-1?pixel+width:-1];for(const neighbor of neighbors){if(neighbor>=0&&seen[neighbor]===0&&alpha[neighbor]>=iconCoverageAlphaFloor){seen[neighbor]=1;queue[tail]=neighbor;tail+=1}}}const componentWidth=maximumX-minimumX+1;const componentHeight=maximumY-minimumY+1;const spansWidth=minimumX===0&&maximumX===width-1;const spansHeight=minimumY===0&&maximumY===height-1;const elongatedBorderArtifact=(spansWidth||spansHeight)&&Math.max(componentWidth/componentHeight,componentHeight/componentWidth)>8;if(component.length<minimumComponent||elongatedBorderArtifact){removed+=1;for(const pixel of component)alpha[pixel]=0}}return removed}function iconVisualGateProblems(purpose,metrics,pathCount,context){const problems=[];const aspectRatio=Math.max(metrics.width/metrics.height,metrics.height/metrics.width);if(purpose==="mark"){if(aspectRatio>1.8)problems.push("the subject is too narrow or elongated");if(metrics.coverageRatio<0.14)problems.push("the mark has too little bold visual mass");if(metrics.coverageRatio>0.72)problems.push("the mark has too little negative space");if(pathCount>12)problems.push("the mark has too many separate vector paths");return problems}const profile=context===undefined?undefined:slopcameraIconContextProfiles[context];const aspectMaximum=profile?.aspectMaximum??1.8;const coverageMinimum=profile?.coverageMinimum??0.035;const coverageMaximum=profile?.coverageMaximum??0.62;const pathCountMaximum=profile?.pathCountMaximum??48;const placement=context===undefined?"":` for the ${context} context`;if(aspectRatio>aspectMaximum){problems.push(`the subject is too narrow or elongated${placement}`)}if(metrics.coverageRatio<coverageMinimum){problems.push(`the illustration lines are too sparse or thin${placement}`)}if(metrics.coverageRatio>coverageMaximum){problems.push(`the illustration is too visually dense${placement}`)}if(pathCount>pathCountMaximum){problems.push(`the illustration has too much vector detail${placement}`)}if(profile!==undefined&&metrics.strokePx!==undefined&&metrics.strokePx<profile.strokePxMinimum){problems.push(`the illustration strokes are too thin${placement}`)}return problems}function estimateIconStrokePixels(extraction){const{height,pixels,width}=extraction;if(width<3||height<3)return 0;let mask=new Uint8Array(width*height);let remaining=0;for(let index=0;index<mask.length;index+=1){if(pixels[index*4+3]>=iconCoverageAlphaFloor){mask[index]=1;remaining+=1}}if(remaining===0)return 0;const initial=remaining;let rounds=0;while(rounds<32&&remaining>initial*iconStrokeMinimumRemaining){const next=new Uint8Array(mask.length);let count=0;for(let y=1;y<height-1;y+=1){for(let x=1;x<width-1;x+=1){const index=y*width+x;if(mask[index]===1&&mask[index-1]===1&&mask[index+1]===1&&mask[index-width]===1&&mask[index+width]===1){next[index]=1;count+=1}}}mask=next;remaining=count;rounds+=1}const sourceStroke=rounds*2;const scale=iconMetricNormalizationEdge/Math.max(width,height);return sourceStroke*scale}function measureIconCandidateMetrics(extraction,svg,pathCount){return{aspectRatio:Math.max(extraction.width/extraction.height,extraction.height/extraction.width),bytes:Buffer.byteLength(svg,"utf8"),coverageRatio:extraction.coverageRatio,pathCount,strokePx:estimateIconStrokePixels(extraction)}}function extractIconLineArt(rgba,width,height,options={}){if(rgba.length===0||rgba.length%4!==0||!Number.isInteger(width)||!Number.isInteger(height)||width<1||height<1||rgba.length!==width*height*4){invalidArgument("Icon line-art extraction requires a nonempty RGBA raster.")}const ink=normalizedHexColor(options.ink??slopcameraIconDefaultInk);const[inkRed,inkGreen,inkBlue]=parseHexColor(ink);const background=borderBackground(rgba,width,height);const measured=measuredInkColor(rgba,width,height,background);const axis=[measured[0]-background[0],measured[1]-background[1],measured[2]-background[2]];let axisLength2=axis[0]*axis[0]+axis[1]*axis[1]+axis[2]*axis[2];let direction=axis;if(axisLength2<27){direction=[-1,-1,-1];axisLength2=3}const alpha=new Uint8Array(width*height);let coverageMass=0;for(let pixel=0;pixel<width*height;pixel+=1){const index=pixel*4;const sourceAlpha=rgba[index+3]/255;const coverage2=Math.max(0,Math.min(1,((rgba[index]-background[0])*direction[0]+(rgba[index+1]-background[1])*direction[1]+(rgba[index+2]-background[2])*direction[2])/axisLength2));const value=Math.round(255*coverage2*sourceAlpha);alpha[pixel]=value;coverageMass+=value}if(coverageMass===0){throw new SlopcameraCloudError("GENERATION_INVALID_RESPONSE","The generated icon raster contains no ink coverage.")}const removedComponents=dropSpeckles(alpha,width,height);let minimumX=width;let minimumY=height;let maximumX=-1;let maximumY=-1;for(let y=0;y<height;y+=1){for(let x=0;x<width;x+=1){if(alpha[y*width+x]>=iconCropAlphaFloor){if(x<minimumX)minimumX=x;if(x>maximumX)maximumX=x;if(y<minimumY)minimumY=y;if(y>maximumY)maximumY=y}}}if(maximumX<0){throw new SlopcameraCloudError("GENERATION_INVALID_RESPONSE","The generated icon raster contains no ink coverage.")}const margin=Math.round(Math.max(maximumX-minimumX+1,maximumY-minimumY+1)*iconMarginRatio);const cropX=Math.max(0,minimumX-margin);const cropY=Math.max(0,minimumY-margin);const cropWidth=Math.min(width,maximumX+1+margin)-cropX;const cropHeight=Math.min(height,maximumY+1+margin)-cropY;const pixels=new Uint8Array(cropWidth*cropHeight*4);let coverage=0;for(let y=0;y<cropHeight;y+=1){for(let x=0;x<cropWidth;x+=1){const sourceIndex=(cropY+y)*width+cropX+x;const targetIndex=(y*cropWidth+x)*4;const measuredValue=alpha[sourceIndex];const value=options.hardEdges===true?measuredValue>=iconCoverageAlphaFloor?255:0:measuredValue;coverage+=value;pixels[targetIndex]=inkRed;pixels[targetIndex+1]=inkGreen;pixels[targetIndex+2]=inkBlue;pixels[targetIndex+3]=value}}const toHex=(channel)=>channel.toString(16).padStart(2,"0");return{background:`#${background.map(toHex).join("")}`,coverageRatio:coverage/(cropWidth*cropHeight*255),height:cropHeight,measuredInk:`#${measured.map(toHex).join("")}`,pixels,removedComponents,sourceHeight:height,sourceWidth:width,width:cropWidth}}var iconCritiqueSchema=z.object({pass:z.boolean(),problems:z.array(z.string()),promptFix:z.string(),score:z.number()});function boundedText(value,maximum){return value.replace(/[\u0000-\u001f\u007f]/gu," ").slice(0,maximum)}function parseIconCritique(output,resolvedModel){const parsed=iconCritiqueSchema.safeParse(output);if(!parsed.success){throw new SlopcameraCloudError("GENERATION_INVALID_RESPONSE","The icon critique returned an invalid bounded object.")}const score=parsed.data.score;if(!Number.isFinite(score)){throw new SlopcameraCloudError("GENERATION_INVALID_RESPONSE","The icon critique returned an invalid score.")}return{pass:parsed.data.pass,problems:parsed.data.problems.slice(0,8).map((problem)=>boundedText(problem,300)),promptFix:boundedText(parsed.data.promptFix,2000),resolvedModel,score:Math.max(0,Math.min(100,score))}}async function loadIconLanguageRuntime(){let aiModule;let gatewayModule;try{[aiModule,gatewayModule]=await Promise.all([import("ai-v7"),import("@ai-sdk/gateway-v4")])}catch{throw new SlopcameraCloudError("GENERATION_FAILED","The Vercel AI Gateway runtime is unavailable.")}if(!isRecord2(aiModule)||!isRecord2(gatewayModule)||typeof aiModule.generateText!=="function"||!isRecord2(aiModule.Output)||typeof aiModule.Output.object!=="function"||typeof gatewayModule.createGateway!=="function"){throw new SlopcameraCloudError("GENERATION_FAILED","The Vercel AI Gateway runtime is unavailable.")}const createGateway=gatewayModule.createGateway;return{Output:aiModule.Output,createGateway:(settings)=>{const provider=createGateway(settings);if(!isObjectLike(provider)||typeof provider.languageModel!=="function"){throw new SlopcameraCloudError("GENERATION_FAILED","The Vercel AI Gateway runtime is unavailable.")}const languageModel=provider.languageModel;return{languageModel:(id)=>languageModel.call(provider,id)}},generateText:aiModule.generateText}}function disableAiSdkWarningLogging(){globalThis.AI_SDK_LOG_WARNINGS=false}function resolvedModelId(value){const modelId=isRecord2(value)?value.modelId:undefined;return typeof modelId==="string"&&modelId.length>0&&modelId.length<=256?modelId:null}async function critiqueIconRaster(input,dependencies){const runtime=await(dependencies.loadLanguageRuntime??loadIconLanguageRuntime)();const controller=new AbortController;const abort=()=>controller.abort(input.signal?.reason);input.signal?.addEventListener("abort",abort,{once:true});if(input.signal?.aborted===true)abort();const timer=setTimeout(()=>controller.abort(),slopcameraIconCritiqueTimeoutMs);try{disableAiSdkWarningLogging();const apiKey=resolveSlopcameraGatewayCredential(dependencies.environment).token;const gateway=runtime.createGateway({apiKey,baseURL:slopcameraGatewayApiBaseUrl,fetch:createFixedGatewayFetch({...dependencies.fetch===undefined?{}:{fetch:dependencies.fetch},maximumResponseBytes:iconCritiqueMaximumResponseBytes})});const output=runtime.Output.object({description:`One bounded style critique for a rendered product ${input.purpose}.`,name:"slopcamera_icon_critique",schema:iconCritiqueSchema});const result=await runtime.generateText({abortSignal:controller.signal,maxOutputTokens:2048,maxRetries:0,messages:[{content:[{text:`Subject: ${input.subject}
29
31
  Purpose: ${input.purpose}
30
32
  Ink: ${input.ink}
31
- `+`Judge this rendered ${input.purpose} against the style contract.`,type:"text"},{data:input.png,mediaType:"image/png",type:"file"}],role:"user"}],model:gateway.languageModel(input.model),output,providerOptions:{gateway:{disallowPromptTraining:true,tags:["slopcamera","icon-critique","v1"],zeroDataRetention:true}},system:input.purpose==="mark"?MARK_CRITIQUE_SYSTEM:ILLUSTRATION_CRITIQUE_SYSTEM,temperature:0});return parseIconCritique(isRecord2(result)?result.output:undefined,resolvedModelId(isRecord2(result)?result.response:undefined))}catch(error){if(error instanceof SlopcameraCloudError)throw error;throw new SlopcameraCloudError("GENERATION_FAILED","The icon critique request failed; it was not retried.")}finally{clearTimeout(timer);input.signal?.removeEventListener("abort",abort)}}var markPreviewSmallSizes=[16,32];async function renderIconPreview(svg,purpose){try{const sharp=(await import("sharp")).default;const source=sharp(Buffer.from(svg),{density:96,failOn:"error",limitInputPixels:vectorizeHardLimits.maxDecodedPixels});if(purpose==="illustration"){return Uint8Array.from(await source.resize(iconPreviewMaximumEdge,iconPreviewMaximumEdge,{fit:"inside"}).flatten({background:slopcameraIconPanel}).png({compressionLevel:9}).toBuffer())}const large=await source.clone().resize(iconPreviewMaximumEdge,iconPreviewMaximumEdge,{fit:"inside"}).flatten({background:slopcameraIconPanel}).png({compressionLevel:9}).toBuffer();const smallRenders=[];let cursor=16;for(const size of markPreviewSmallSizes){const rendered=await sharp(Buffer.from(svg),{density:96,failOn:"error",limitInputPixels:vectorizeHardLimits.maxDecodedPixels}).resize(size,size,{fit:"inside"}).flatten({background:slopcameraIconPanel}).png({compressionLevel:9}).toBuffer();smallRenders.push({input:await sharp({create:{background:{b:251,channels:3,g:248,r:247},channels:3,height:size+32,width:size+32}}).composite([{input:rendered,left:16,top:16}]).png().toBuffer(),left:iconPreviewMaximumEdge+16,top:cursor});cursor+=size+48}return Uint8Array.from(await sharp({create:{background:{b:251,channels:3,g:248,r:247},channels:3,height:Math.max(iconPreviewMaximumEdge,cursor+16),width:iconPreviewMaximumEdge+16+96}}).composite([{input:large,left:0,top:0},...smallRenders]).png({compressionLevel:9}).toBuffer())}catch(error){throw new SlopcameraCloudError("GENERATION_FAILED","The canonical icon SVG could not be rendered for critique.",{cause:error})}}async function writeAtomically(path,value){const absolutePath=resolve(path);await mkdir(dirname(absolutePath),{recursive:true});const temporaryPath=`${absolutePath}.${randomUUID()}.tmp`;try{await writeFile(temporaryPath,value,{flag:"wx"});await rename(temporaryPath,absolutePath);return absolutePath}catch(error){await rm(temporaryPath,{force:true}).catch(()=>{return});throw new SlopcameraCloudError("OUTPUT_WRITE_FAILED",`Slopcamera could not atomically write ${absolutePath}.`,{cause:error})}}async function generateSlopcameraIcon(input,dependencies={}){const subject=validateSubject(input.subject);if(typeof input.outputPath!=="string"||input.outputPath.length<1||input.outputPath.length>4096||input.outputPath.includes("\x00")||!input.outputPath.toLowerCase().endsWith(".svg")){invalidArgument("outputPath must be a bounded local path ending in .svg.")}const model=validateIconModel(input.model??"recraft/recraft-v4.1-utility","model");const rounds=validateRounds(input.rounds);const purpose=input.purpose??"illustration";if(purpose!=="illustration"&&purpose!=="mark"){invalidArgument("purpose must be illustration or mark.")}const ink=normalizedHexColor(input.ink??slopcameraIconDefaultInk);const critiqueModel=input.critiqueModel===undefined?slopcameraIconCritiqueDefaultModel:validateIconModel(input.critiqueModel,"critiqueModel");const critiqueEnabled=rounds>1||input.critiqueModel!==undefined;const generate=dependencies.generate??generateSlopcameraImage;const vectorize=dependencies.vectorize??vectorizeImage;const critique=dependencies.critique??((critiqueInput)=>critiqueIconRaster(critiqueInput,dependencies));const limits=resolveVectorizeLimits({});const attempts=[];const candidates=[];let feedback;let lastError;for(let round=1;round<=rounds;round+=1){if(input.signal?.aborted===true){throw new SlopcameraCloudError("GENERATION_FAILED","Icon generation was cancelled.")}const prompt=iconPromptFor(subject,{ink,purpose,...feedback===undefined?{}:{feedback}});let candidate;let extraction;let attemptRequestId="";let attemptProblems=[];try{const generated=await generate({model,prompt,...input.signal===undefined?{}:{signal:input.signal}},dependencies);attemptRequestId=generated.requestId;const bytes=Buffer.from(generated.image.base64,"base64");const raster=await loadRaster(Uint8Array.from(bytes),limits,new VectorizeDeadline(limits.maxDurationMs));extraction=extractIconLineArt(raster.pixels,raster.width,raster.height,{hardEdges:true,ink});const png=await encodeTracePng(extraction.pixels,extraction.width,extraction.height);const traced=await vectorize(png,{...input.inheritedFileDescriptors===undefined?{}:{inheritedFileDescriptors:input.inheritedFileDescriptors}});const visualProblems=iconVisualGateProblems(purpose,extraction,traced.receipt.pathCount);if(visualProblems.length>0){feedback=visualProblems.join("; ");attemptProblems=visualProblems;throw new SlopcameraCloudError("GENERATION_INVALID_RESPONSE",`Generated ${purpose} failed its visual gate: ${feedback}.`)}candidate={png,requestId:generated.requestId,svg:traced.svg,vectorize:traced.receipt,warnings:generated.warnings}}catch(error){lastError=error;attempts.push({...attemptProblems.length===0?{}:{problems:attemptProblems},requestId:attemptRequestId,round,status:"failed",warnings:[]});continue}let review=null;let critiqueError;if(critiqueEnabled){try{const preview=dependencies.rasterize===undefined?await renderIconPreview(candidate.svg,purpose):await dependencies.rasterize(candidate.svg);review=await critique({ink,model:critiqueModel,png:preview,purpose,...input.signal===undefined?{}:{signal:input.signal},subject})}catch(error){critiqueError=error instanceof SlopcameraCloudError?error.code:"critique-failed";review=null}}candidates.push({candidate,critique:review,extraction,round});attempts.push({...critiqueError===undefined?{}:{critiqueError},...review===null?{}:{critiqueModel:review.resolvedModel??critiqueModel},...review===null?{}:{pass:review.pass},...review===null?{}:{problems:review.problems},requestId:candidate.requestId,round,...review===null?{}:{score:review.score},status:"candidate",svgSha256:createHash("sha256").update(candidate.svg).digest("hex"),vectorize:candidate.vectorize,warnings:candidate.warnings});if(review===null||review.pass)break;feedback=[...review.problems,review.promptFix].filter((part)=>part.length>0).join("; ")}if(candidates.length===0){if(lastError instanceof Error)throw lastError;throw new SlopcameraCloudError("GENERATION_FAILED","Every icon generation attempt failed.")}const eligibleCandidates=candidates.filter(({critique:review})=>review===null||review.pass);if(eligibleCandidates.length===0){throw new SlopcameraCloudError("GENERATION_INVALID_RESPONSE",`Every generated ${purpose} failed its design critique.`)}const selected=eligibleCandidates.sort((left,right)=>(right.critique?.score??-1)-(left.critique?.score??-1)||right.round-left.round)[0];const selectedAttempt=attempts.findIndex((attempt)=>attempt.round===selected.round&&attempt.status==="candidate");if(selectedAttempt>=0){attempts[selectedAttempt]={...attempts[selectedAttempt],status:"selected"}}const outputPath=await writeAtomically(input.outputPath,selected.candidate.svg);const receipt={attempts,ink,model,outputPath,purpose,receiptVersion:1,rounds,selectedRound:selected.round,subject,svgSha256:createHash("sha256").update(selected.candidate.svg).digest("hex")};if(input.keepRaster===true){const rasterPath=input.outputPath.replace(/\.svg$/iu,".lineart.png");return{...receipt,rasterPath:await writeAtomically(rasterPath,selected.candidate.png)}}return receipt}import{createHash as createHash2,randomUUID as randomUUID2}from"crypto";import{link,mkdir as mkdir2,rm as rm2,writeFile as writeFile2}from"fs/promises";import{dirname as dirname2,join,resolve as resolve2}from"path";import sharp from"sharp";var slopcameraGalleryKinds=Object.freeze(["image","texture","skybox","backdrop","sprite"]);var slopcameraGalleryAxes=Object.freeze(["style","palette","material","lighting","mood","detail"]);var slopcameraGalleryLimits=Object.freeze({candidates:16,concurrency:4,subjectBytes:2048,candidatePromptBytes:slopcameraMaximumPromptBytes,variantBytes:1024,idLength:64,cellEdgeMin:64,cellEdgeMax:1024,cellEdgeDefault:512,labelHeight:28,cellGap:8,galleryPixels:33554432,candidatePixels:33554432,candidateEdge:8192});var GALLERY_KIND_CONTRACTS={image:{aspect:[1,1],frame:(subject)=>subject,rules:"Clean readable composition; no text, captions, borders, or watermark."},texture:{aspect:[1,1],frame:(subject)=>`Seamless tileable texture of ${subject}.`,rules:"Flat orthographic top-down material surface, uniform diffuse lighting, "+"edge-to-edge surface detail that tiles in both directions; no shadows, "+"no specular highlights, no vignette, no borders, no objects, no text."},skybox:{aspect:[2,1],frame:(subject)=>`Seamless equirectangular 360-degree panorama of ${subject}.`,rules:"Full spherical environment in 2:1 equirectangular projection, continuous "+"horizon exactly at the vertical center, left and right edges wrapping "+"seamlessly; no seams, no text, no watermark, no foreground frame."},backdrop:{aspect:[16,9],frame:(subject)=>`Flat scenic backdrop plate of ${subject}.`,rules:"Edge-to-edge environment plate for compositing behind foreground "+"subjects, consistent perspective and lighting across the frame; no "+"borders, no text, no watermark."},sprite:{aspect:[1,1],frame:(subject)=>`A single ${subject} on a plain solid neutral background.`,rules:"One centered subject, fully visible with a clean silhouette, flat even "+"lighting; no shadows, no extra objects, no text, no watermark."}};var GALLERY_AXIS_DEFAULTS={style:["photorealistic","stylized painterly","flat minimal","hand-drawn"],palette:["warm earthy palette","cool desaturated palette","neutral grayscale palette","vivid saturated palette"],material:["rough weathered surface","smooth polished surface","organic natural surface","synthetic industrial surface"],lighting:["soft diffuse lighting","dramatic directional lighting","flat ambient lighting","golden-hour lighting"],mood:["serene calm mood","dramatic mood","playful mood","mysterious mood"],detail:["minimal sparse detail","balanced detail","intricate dense detail"]};var SKYBOX_AXIS_DEFAULTS={material:["clear air","light haze","thin clouds","dense dramatic clouds"],lighting:["golden hour","overcast midday","clear night sky","midday sun"]};function axisValues(kind,axis){return kind==="skybox"?SKYBOX_AXIS_DEFAULTS[axis]??GALLERY_AXIS_DEFAULTS[axis]:GALLERY_AXIS_DEFAULTS[axis]}function invalidArgument2(message){throw new SlopcameraCloudError("INVALID_ARGUMENT",message)}function isRecord3(value){return typeof value==="object"&&value!==null&&!Array.isArray(value)}function boundedText2(value,name,maximumBytes){if(typeof value!=="string"||value.trim().length===0||/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/u.test(value)||Buffer.byteLength(value,"utf8")>maximumBytes){invalidArgument2(`${name} must be non-empty text no more than ${maximumBytes} UTF-8 bytes.`)}return value.trim()}function galleryCandidateId(value){const id=boundedText2(value,"Candidate id",slopcameraGalleryLimits.idLength);if(!/^[a-z0-9][a-z0-9._-]*$/iu.test(id)){invalidArgument2("Candidate id must start with an alphanumeric and contain only alphanumerics, '.', '_', or '-'.")}return id}function slug(value){const slugged=value.toLowerCase().split(/[^a-z0-9]+/u).filter((part)=>part.length>0).join("-").slice(0,slopcameraGalleryLimits.idLength);return slugged.length===0?"candidate":slugged}function galleryKind(value){const kind=value??"image";if(typeof kind!=="string"||!slopcameraGalleryKinds.includes(kind)){invalidArgument2(`kind must be one of: ${slopcameraGalleryKinds.join(", ")}.`)}return kind}function galleryPromptFor(subject,kind,variant){const contract=GALLERY_KIND_CONTRACTS[kind];const sections=[contract.frame(subject),`Rules: ${contract.rules}`];const direction=variant?.trim();if(direction!==undefined&&direction.length>0){sections.push(`Variation direction: ${direction}.`)}return sections.join(`
33
+ `+(input.context===undefined?"":`Context: ${input.context}
34
+ `)+`Judge this rendered ${input.purpose} against the style contract.`,type:"text"},{data:input.png,mediaType:"image/png",type:"file"}],role:"user"}],model:gateway.languageModel(input.model),output,providerOptions:{gateway:{disallowPromptTraining:true,tags:["slopcamera","icon-critique","v1"],zeroDataRetention:true}},system:input.purpose==="mark"?MARK_CRITIQUE_SYSTEM:input.context==="inline"?ILLUSTRATION_CRITIQUE_SYSTEM+INLINE_CRITIQUE_CLAUSE:ILLUSTRATION_CRITIQUE_SYSTEM,temperature:0});return parseIconCritique(isRecord2(result)?result.output:undefined,resolvedModelId(isRecord2(result)?result.response:undefined))}catch(error){if(error instanceof SlopcameraCloudError)throw error;throw new SlopcameraCloudError("GENERATION_FAILED","The icon critique request failed; it was not retried.")}finally{clearTimeout(timer);input.signal?.removeEventListener("abort",abort)}}var markPreviewSmallSizes=[16,32];async function renderIconPreview(svg,purpose){try{const sharp=(await import("sharp")).default;const source=sharp(Buffer.from(svg),{density:96,failOn:"error",limitInputPixels:vectorizeHardLimits.maxDecodedPixels});if(purpose==="illustration"){return Uint8Array.from(await source.resize(iconPreviewMaximumEdge,iconPreviewMaximumEdge,{fit:"inside"}).flatten({background:slopcameraIconPanel}).png({compressionLevel:9}).toBuffer())}const large=await source.clone().resize(iconPreviewMaximumEdge,iconPreviewMaximumEdge,{fit:"inside"}).flatten({background:slopcameraIconPanel}).png({compressionLevel:9}).toBuffer();const smallRenders=[];let cursor=16;for(const size of markPreviewSmallSizes){const rendered=await sharp(Buffer.from(svg),{density:96,failOn:"error",limitInputPixels:vectorizeHardLimits.maxDecodedPixels}).resize(size,size,{fit:"inside"}).flatten({background:slopcameraIconPanel}).png({compressionLevel:9}).toBuffer();smallRenders.push({input:await sharp({create:{background:{b:251,channels:3,g:248,r:247},channels:3,height:size+32,width:size+32}}).composite([{input:rendered,left:16,top:16}]).png().toBuffer(),left:iconPreviewMaximumEdge+16,top:cursor});cursor+=size+48}return Uint8Array.from(await sharp({create:{background:{b:251,channels:3,g:248,r:247},channels:3,height:Math.max(iconPreviewMaximumEdge,cursor+16),width:iconPreviewMaximumEdge+16+96}}).composite([{input:large,left:0,top:0},...smallRenders]).png({compressionLevel:9}).toBuffer())}catch(error){throw new SlopcameraCloudError("GENERATION_FAILED","The canonical icon SVG could not be rendered for critique.",{cause:error})}}async function writeAtomically(path,value){const absolutePath=resolve(path);await mkdir(dirname(absolutePath),{recursive:true});const temporaryPath=`${absolutePath}.${randomUUID()}.tmp`;try{await writeFile(temporaryPath,value,{flag:"wx"});await rename(temporaryPath,absolutePath);return absolutePath}catch(error){await rm(temporaryPath,{force:true}).catch(()=>{return});throw new SlopcameraCloudError("OUTPUT_WRITE_FAILED",`Slopcamera could not atomically write ${absolutePath}.`,{cause:error})}}function validateCandidatePool(value,rounds){const pool=value??1;if(!Number.isInteger(pool)||pool<1||pool>slopcameraIconMaximumRounds||pool>rounds){invalidArgument(`candidatePool must be an integer from 1 through ${Math.min(rounds,slopcameraIconMaximumRounds)}.`)}return pool}function validateIconContext(value){if(value===undefined)return;if(typeof value!=="string"||!slopcameraIconContexts.includes(value)){invalidArgument(`context must be one of: ${slopcameraIconContexts.join(", ")}.`)}return value}async function collectSlopcameraIconCandidates(input,dependencies={}){const subject=validateSubject(input.subject);const model=validateIconModel(input.model??"recraft/recraft-v4.1-utility","model");const rounds=validateRounds(input.rounds);const purpose=input.purpose??"illustration";if(purpose!=="illustration"&&purpose!=="mark"){invalidArgument("purpose must be illustration or mark.")}const context=validateIconContext(input.context);const ink=normalizedHexColor(input.ink??slopcameraIconDefaultInk);const pool=validateCandidatePool(input.candidatePool,rounds);const critiqueModel=input.critiqueModel===undefined?slopcameraIconCritiqueDefaultModel:validateIconModel(input.critiqueModel,"critiqueModel");const critiqueEnabled=rounds>1||input.critiqueModel!==undefined;const generate=dependencies.generate??generateSlopcameraImage;const vectorize=dependencies.vectorize??vectorizeImage;const critique=dependencies.critique??((critiqueInput)=>critiqueIconRaster(critiqueInput,dependencies));const limits=resolveVectorizeLimits({});const attempts=[];const candidates=[];const eligible=[];let feedback=input.initialFeedback?.trim()||undefined;let lastError;for(let round=1;round<=rounds;round+=1){if(input.signal?.aborted===true){throw new SlopcameraCloudError("GENERATION_FAILED","Icon generation was cancelled.")}const prompt=iconPromptFor(subject,{ink,purpose,...context===undefined?{}:{context},...feedback===undefined?{}:{feedback}});let candidate;let extraction;let metrics;let attemptRequestId="";let attemptProblems=[];try{const generated=await generate({model,prompt,...input.signal===undefined?{}:{signal:input.signal}},dependencies);attemptRequestId=generated.requestId;const bytes=Buffer.from(generated.image.base64,"base64");const raster=await loadRaster(Uint8Array.from(bytes),limits,new VectorizeDeadline(limits.maxDurationMs));extraction=extractIconLineArt(raster.pixels,raster.width,raster.height,{hardEdges:true,ink});const png=await encodeTracePng(extraction.pixels,extraction.width,extraction.height);const traced=await vectorize(png,{...input.inheritedFileDescriptors===undefined?{}:{inheritedFileDescriptors:input.inheritedFileDescriptors}});metrics=measureIconCandidateMetrics(extraction,traced.svg,traced.receipt.pathCount);const visualProblems=iconVisualGateProblems(purpose,{...extraction,strokePx:metrics.strokePx},traced.receipt.pathCount,context);if(visualProblems.length>0){feedback=visualProblems.join("; ");attemptProblems=visualProblems;throw new SlopcameraCloudError("GENERATION_INVALID_RESPONSE",`Generated ${purpose} failed its visual gate: ${feedback}.`)}candidate={png,requestId:generated.requestId,svg:traced.svg,vectorize:traced.receipt,warnings:generated.warnings}}catch(error){lastError=error;attempts.push({...attemptProblems.length===0?{}:{problems:attemptProblems},requestId:attemptRequestId,round,status:"failed",warnings:[]});continue}let review=null;let critiqueError;if(critiqueEnabled){try{const preview=dependencies.rasterize===undefined?await renderIconPreview(candidate.svg,purpose):await dependencies.rasterize(candidate.svg);review=await critique({...context===undefined?{}:{context},ink,model:critiqueModel,png:preview,purpose,...input.signal===undefined?{}:{signal:input.signal},subject})}catch(error){critiqueError=error instanceof SlopcameraCloudError?error.code:"critique-failed";review=null}}const record={candidate,critique:review,extraction,metrics,round};candidates.push(record);attempts.push({...critiqueError===undefined?{}:{critiqueError},...review===null?{}:{critiqueModel:review.resolvedModel??critiqueModel},metrics,...review===null?{}:{pass:review.pass},...review===null?{}:{problems:review.problems},requestId:candidate.requestId,round,...review===null?{}:{score:review.score},status:"candidate",svgSha256:createHash("sha256").update(candidate.svg).digest("hex"),vectorize:candidate.vectorize,warnings:candidate.warnings});if(review!==null&&!review.pass){feedback=[...review.problems,review.promptFix].filter((part)=>part.length>0).join("; ")}if(review===null||review.pass||input.requireCritiquePass===false){eligible.push(record);if(eligible.length>=pool&&(review===null||review.pass))break}}if(candidates.length===0){if(lastError instanceof Error)throw lastError;throw new SlopcameraCloudError("GENERATION_FAILED","Every icon generation attempt failed.")}return{attempts,candidates,eligible,resolved:{context,critiqueModel,ink,model,purpose,rounds,subject}}}async function generateSlopcameraIcon(input,dependencies={}){if(typeof input.outputPath!=="string"||input.outputPath.length<1||input.outputPath.length>4096||input.outputPath.includes("\x00")||!input.outputPath.toLowerCase().endsWith(".svg")){invalidArgument("outputPath must be a bounded local path ending in .svg.")}const{attempts,eligible,resolved}=await collectSlopcameraIconCandidates({...input.candidatePool===undefined?{}:{candidatePool:input.candidatePool},...input.context===undefined?{}:{context:input.context},...input.critiqueModel===undefined?{}:{critiqueModel:input.critiqueModel},...input.inheritedFileDescriptors===undefined?{}:{inheritedFileDescriptors:input.inheritedFileDescriptors},...input.ink===undefined?{}:{ink:input.ink},...input.model===undefined?{}:{model:input.model},...input.purpose===undefined?{}:{purpose:input.purpose},...input.rounds===undefined?{}:{rounds:input.rounds},...input.signal===undefined?{}:{signal:input.signal},subject:input.subject},dependencies);if(eligible.length===0){throw new SlopcameraCloudError("GENERATION_INVALID_RESPONSE",`Every generated ${resolved.purpose} failed its design critique.`)}const selected=[...eligible].sort((left,right)=>(right.critique?.score??-1)-(left.critique?.score??-1)||right.round-left.round)[0];const attemptList=[...attempts];const selectedAttempt=attemptList.findIndex((attempt)=>attempt.round===selected.round&&attempt.status==="candidate");if(selectedAttempt>=0){attemptList[selectedAttempt]={...attemptList[selectedAttempt],status:"selected"}}const outputPath=await writeAtomically(input.outputPath,selected.candidate.svg);const receipt={attempts:attemptList,ink:resolved.ink,model:resolved.model,outputPath,purpose:resolved.purpose,receiptVersion:1,rounds:resolved.rounds,selectedRound:selected.round,subject:resolved.subject,svgSha256:createHash("sha256").update(selected.candidate.svg).digest("hex")};if(input.keepRaster===true){const rasterPath=input.outputPath.replace(/\.svg$/iu,".lineart.png");return{...receipt,rasterPath:await writeAtomically(rasterPath,selected.candidate.png)}}return receipt}import{createHash as createHash2,randomUUID as randomUUID2}from"crypto";import{link,mkdir as mkdir2,rm as rm2,writeFile as writeFile2}from"fs/promises";import{dirname as dirname2,join,resolve as resolve2}from"path";import sharp from"sharp";var slopcameraGalleryKinds=Object.freeze(["image","texture","skybox","backdrop","sprite"]);var slopcameraGalleryAxes=Object.freeze(["style","palette","material","lighting","mood","detail"]);var slopcameraGalleryLimits=Object.freeze({candidates:16,concurrency:4,subjectBytes:2048,candidatePromptBytes:slopcameraMaximumPromptBytes,variantBytes:1024,idLength:64,cellEdgeMin:64,cellEdgeMax:1024,cellEdgeDefault:512,labelHeight:28,cellGap:8,galleryPixels:33554432,candidatePixels:33554432,candidateEdge:8192});var GALLERY_KIND_CONTRACTS={image:{aspect:[1,1],frame:(subject)=>subject,rules:"Clean readable composition; no text, captions, borders, or watermark."},texture:{aspect:[1,1],frame:(subject)=>`Seamless tileable texture of ${subject}.`,rules:"Flat orthographic top-down material surface, uniform diffuse lighting, "+"edge-to-edge surface detail that tiles in both directions; no shadows, "+"no specular highlights, no vignette, no borders, no objects, no text."},skybox:{aspect:[2,1],frame:(subject)=>`Seamless equirectangular 360-degree panorama of ${subject}.`,rules:"Full spherical environment in 2:1 equirectangular projection, continuous "+"horizon exactly at the vertical center, left and right edges wrapping "+"seamlessly; no seams, no text, no watermark, no foreground frame."},backdrop:{aspect:[16,9],frame:(subject)=>`Flat scenic backdrop plate of ${subject}.`,rules:"Edge-to-edge environment plate for compositing behind foreground "+"subjects, consistent perspective and lighting across the frame; no "+"borders, no text, no watermark."},sprite:{aspect:[1,1],frame:(subject)=>`A single ${subject} on a plain solid neutral background.`,rules:"One centered subject, fully visible with a clean silhouette, flat even "+"lighting; no shadows, no extra objects, no text, no watermark."}};var GALLERY_AXIS_DEFAULTS={style:["photorealistic","stylized painterly","flat minimal","hand-drawn"],palette:["warm earthy palette","cool desaturated palette","neutral grayscale palette","vivid saturated palette"],material:["rough weathered surface","smooth polished surface","organic natural surface","synthetic industrial surface"],lighting:["soft diffuse lighting","dramatic directional lighting","flat ambient lighting","golden-hour lighting"],mood:["serene calm mood","dramatic mood","playful mood","mysterious mood"],detail:["minimal sparse detail","balanced detail","intricate dense detail"]};var SKYBOX_AXIS_DEFAULTS={material:["clear air","light haze","thin clouds","dense dramatic clouds"],lighting:["golden hour","overcast midday","clear night sky","midday sun"]};function axisValues(kind,axis){return kind==="skybox"?SKYBOX_AXIS_DEFAULTS[axis]??GALLERY_AXIS_DEFAULTS[axis]:GALLERY_AXIS_DEFAULTS[axis]}function invalidArgument2(message){throw new SlopcameraCloudError("INVALID_ARGUMENT",message)}function isRecord3(value){return typeof value==="object"&&value!==null&&!Array.isArray(value)}function boundedText2(value,name,maximumBytes){if(typeof value!=="string"||value.trim().length===0||/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/u.test(value)||Buffer.byteLength(value,"utf8")>maximumBytes){invalidArgument2(`${name} must be non-empty text no more than ${maximumBytes} UTF-8 bytes.`)}return value.trim()}function galleryCandidateId(value){const id=boundedText2(value,"Candidate id",slopcameraGalleryLimits.idLength);if(!/^[a-z0-9][a-z0-9._-]*$/iu.test(id)){invalidArgument2("Candidate id must start with an alphanumeric and contain only alphanumerics, '.', '_', or '-'.")}return id}function slug(value){const slugged=value.toLowerCase().split(/[^a-z0-9]+/u).filter((part)=>part.length>0).join("-").slice(0,slopcameraGalleryLimits.idLength);return slugged.length===0?"candidate":slugged}function galleryKind(value){const kind=value??"image";if(typeof kind!=="string"||!slopcameraGalleryKinds.includes(kind)){invalidArgument2(`kind must be one of: ${slopcameraGalleryKinds.join(", ")}.`)}return kind}function galleryPromptFor(subject,kind,variant){const contract=GALLERY_KIND_CONTRACTS[kind];const sections=[contract.frame(subject),`Rules: ${contract.rules}`];const direction=variant?.trim();if(direction!==undefined&&direction.length>0){sections.push(`Variation direction: ${direction}.`)}return sections.join(`
32
35
 
33
36
  `)}function axisSpec(value){if(typeof value==="string"){const axis2=value.trim();if(!slopcameraGalleryAxes.includes(axis2)){invalidArgument2(`vary axis must be one of: ${slopcameraGalleryAxes.join(", ")}.`)}return{axis:axis2}}if(!isRecord3(value))invalidArgument2("vary entries must be axis names or {axis, values} objects.");const axis=value.axis;if(!slopcameraGalleryAxes.includes(axis)){invalidArgument2(`vary axis must be one of: ${slopcameraGalleryAxes.join(", ")}.`)}const values=value.values;if(values!==undefined){if(!Array.isArray(values)||values.length<1||values.length>slopcameraGalleryLimits.candidates){invalidArgument2("vary values must be a bounded non-empty list.")}return{axis,values:values.map((item)=>boundedText2(item,"vary value",slopcameraGalleryLimits.variantBytes))}}return{axis}}function explicitCandidate(value){if(!isRecord3(value))invalidArgument2("candidates entries must be {id, prompt|variant} objects.");const id=galleryCandidateId(value.id);const prompt=value.prompt;const variant=value.variant;if(prompt!==undefined&&variant!==undefined){invalidArgument2("A candidate supplies prompt or variant, not both.")}if(prompt===undefined&&variant===undefined){invalidArgument2("A candidate requires a prompt or a variant.")}return{id,...prompt===undefined?{}:{prompt:boundedText2(prompt,"candidate prompt",slopcameraGalleryLimits.candidatePromptBytes)},...variant===undefined?{}:{variant:boundedText2(variant,"candidate variant",slopcameraGalleryLimits.variantBytes)}}}function planSlopcameraGallery(input){if(!isRecord3(input))invalidArgument2("Gallery input must be an object.");const subject=boundedText2(input.subject,"subject",slopcameraGalleryLimits.subjectBytes);const kind=galleryKind(input.kind);const aspect=GALLERY_KIND_CONTRACTS[kind].aspect;const vary=(input.vary??[]).map(axisSpec);const explicit=input.candidates?.map(explicitCandidate);if(explicit!==undefined&&vary.length>0){invalidArgument2("candidates and vary are mutually exclusive.")}if(explicit!==undefined&&input.count!==undefined){invalidArgument2("candidates and count are mutually exclusive.")}if(vary.length>slopcameraGalleryAxes.length||new Set(vary.map((spec)=>spec.axis)).size!==vary.length){invalidArgument2("vary axes must be unique.")}const count=input.count;if(count!==undefined&&(!Number.isInteger(count)||count<1||count>slopcameraGalleryLimits.candidates)){invalidArgument2(`count must be an integer from 1 through ${slopcameraGalleryLimits.candidates}.`)}const tiled=input.tiled;if(tiled!==undefined&&typeof tiled!=="boolean"){invalidArgument2("tiled must be a boolean when set.")}const axes=vary.map((spec)=>({axis:spec.axis,values:spec.values??axisValues(kind,spec.axis).slice(0,slopcameraGalleryLimits.candidates)}));const prompts=[];if(explicit!==undefined){if(explicit.length<1||explicit.length>slopcameraGalleryLimits.candidates){invalidArgument2(`candidates must contain 1 through ${slopcameraGalleryLimits.candidates} entries.`)}for(const candidate of explicit){prompts.push({id:candidate.id,prompt:candidate.prompt??galleryPromptFor(subject,kind,candidate.variant),label:candidate.id})}}else if(axes.length>0){const total=axes.reduce((product,spec)=>product*spec.values.length,1);if(total<1||total>slopcameraGalleryLimits.candidates){invalidArgument2(`vary expands to ${String(total)} candidates; the bound is ${slopcameraGalleryLimits.candidates}.`)}const seen=new Set;for(let combination=0;combination<total;combination++){let remainder=combination;const parts=[];for(let index=axes.length-1;index>=0;index--){const spec=axes[index];const value=spec.values[remainder%spec.values.length];remainder=Math.floor(remainder/spec.values.length);parts.unshift(`${spec.axis}: ${value}`)}const direction=parts.join("; ");let id=slug(parts.map((part)=>part.split(": ")[1]).join("-"));if(seen.has(id))id=`${id}-${String(combination+1)}`;seen.add(id);prompts.push({id,prompt:galleryPromptFor(subject,kind,direction),label:id})}}else{const requested=count??4;for(let index=0;index<requested;index++){const id=`candidate-${String(index+1)}`;prompts.push({id,prompt:galleryPromptFor(subject,kind),label:id})}}return{subject,kind,aspect,axes,candidates:prompts.map((candidate,index)=>({index:index+1,...candidate})),tiled:tiled??kind==="texture"}}function parseSlopcameraGalleryVary(value){const specs=[];for(const part of value.split(";")){const trimmed=part.trim();if(trimmed==="")continue;const[axis,values]=trimmed.split("=",2);if(!slopcameraGalleryAxes.includes(axis.trim())){invalidArgument2(`vary axis must be one of: ${slopcameraGalleryAxes.join(", ")}.`)}if(values===undefined){specs.push({axis:axis.trim()});continue}const list=values.split(",").map((item)=>item.trim()).filter((item)=>item.length>0);if(list.length===0||list.length>slopcameraGalleryLimits.candidates){invalidArgument2("vary values must be a non-empty bounded comma-separated list.")}specs.push({axis:axis.trim(),values:list})}if(specs.length===0){invalidArgument2("vary requires at least one axis, e.g. 'style; palette=warm,cool'.")}return specs}var FONT_5X7={" ":["00000","00000","00000","00000","00000","00000","00000"],"0":["01110","10001","10011","10101","11001","10001","01110"],"1":["00100","01100","00100","00100","00100","00100","01110"],"2":["01110","10001","00001","00010","00100","01000","11111"],"3":["11111","00010","00100","00010","00001","10001","01110"],"4":["00010","00110","01010","11111","00010","00010","00010"],"5":["11111","10000","11110","00001","00001","10001","01110"],"6":["00110","01000","10000","11110","10001","10001","01110"],"7":["11111","00001","00010","00100","01000","01000","01000"],"8":["01110","10001","10001","01110","10001","10001","01110"],"9":["01110","10001","10001","01111","00001","00010","01100"],a:["00000","00000","01110","00001","01111","10001","01111"],b:["10000","10000","10110","11001","10001","10001","11110"],c:["00000","00000","01110","10000","10000","10001","01110"],d:["00001","00001","01101","10011","10001","10001","01111"],e:["00000","00000","01110","10001","11111","10000","01110"],f:["00110","01001","01000","11100","01000","01000","01000"],g:["00000","01111","10001","10001","01111","00001","01110"],h:["10000","10000","10110","11001","10001","10001","10001"],i:["00100","00000","01100","00100","00100","00100","01110"],j:["00010","00000","00110","00010","00010","10010","01100"],k:["10000","10000","10010","10100","11000","10100","10010"],l:["01100","00100","00100","00100","00100","00100","01110"],m:["00000","00000","11010","10101","10101","10101","10101"],n:["00000","00000","10110","11001","10001","10001","10001"],o:["00000","00000","01110","10001","10001","10001","01110"],p:["00000","00000","11110","10001","11110","10000","10000"],q:["00000","00000","01101","10011","01111","00001","00001"],r:["00000","00000","10110","11001","10000","10000","10000"],s:["00000","00000","01111","10000","01110","00001","11110"],t:["01000","01000","11100","01000","01000","01001","00110"],u:["00000","00000","10001","10001","10001","10011","01101"],v:["00000","00000","10001","10001","10001","01010","00100"],w:["00000","00000","10001","10101","10101","10101","01010"],x:["00000","00000","10001","01010","00100","01010","10001"],y:["00000","00000","10001","10001","01111","00001","01110"],z:["00000","00000","11111","00010","00100","01000","11111"],"#":["01010","01010","11111","01010","11111","01010","01010"],"-":["00000","00000","00000","11111","00000","00000","00000"],_:["00000","00000","00000","00000","00000","00000","11111"],".":["00000","00000","00000","00000","00000","01100","01100"],":":["00000","01100","01100","00000","01100","01100","00000"],"=":["00000","00000","11111","00000","11111","00000","00000"],"/":["00001","00010","00010","00100","01000","01000","10000"],"?":["01110","10001","00001","00010","00100","00000","00100"]};function labelPixels(text,width,height){const scale=2;const rgba=Buffer.alloc(width*height*4);for(let pixel=0;pixel<width*height;pixel++){const index=pixel*4;rgba[index]=18;rgba[index+1]=21;rgba[index+2]=27;rgba[index+3]=255}const draw=(character,originX)=>{const glyph=FONT_5X7[character]??FONT_5X7["?"];const originY=Math.floor((height-7*scale)/2);for(let row=0;row<7;row++){for(let column=0;column<5;column++){if(glyph[row][column]!=="1")continue;for(let dy=0;dy<scale;dy++){for(let dx=0;dx<scale;dx++){const x=originX+column*scale+dx;const y=originY+row*scale+dy;if(x<0||x>=width||y<0||y>=height)continue;const index=(y*width+x)*4;rgba[index]=235;rgba[index+1]=238;rgba[index+2]=244;rgba[index+3]=255}}}}};const advance=6*scale;const maximum=Math.floor((width-8)/advance);const sanitized=text.toLowerCase().replace(/[^a-z0-9 #._:=/?-]/gu,"?").slice(0,Math.max(0,maximum));for(let index=0;index<sanitized.length;index++){draw(sanitized[index],4+index*advance)}return rgba}function mediaExtension(mediaType){if(mediaType==="image/png")return"png";if(mediaType==="image/jpeg")return"jpg";if(mediaType==="image/webp")return"webp";invalidArgument2("Candidate media type is unsupported.");return"png"}async function atomicPublish(path,bytes){const temporaryPath=join(dirname2(path),`.${randomUUID2()}.slopcamera-gallery.tmp`);try{await writeFile2(temporaryPath,bytes,{flag:"wx"});await link(temporaryPath,path)}catch{throw new SlopcameraCloudError("OUTPUT_WRITE_FAILED","Slopcamera could not atomically write a gallery output; existing files are never replaced.")}finally{await rm2(temporaryPath,{force:true}).catch(()=>{return})}}async function generateCandidate(candidate,model,input,dependencies){const generate=dependencies.generate??generateSlopcameraImage;try{const generated=await generate({model,prompt:candidate.prompt,...input.signal===undefined?{}:{signal:input.signal},...input.timeoutMs===undefined?{}:{timeoutMs:input.timeoutMs}},dependencies);const bytes=Buffer.from(generated.image.base64,"base64");return{index:candidate.index,id:candidate.id,label:candidate.label,prompt:candidate.prompt,status:"generated",bytes,mediaType:generated.image.mediaType,requestId:generated.requestId,warnings:generated.warnings}}catch(error){return{index:candidate.index,id:candidate.id,label:candidate.label,prompt:candidate.prompt,status:"failed",error:error instanceof Error?error.message:String(error)}}}async function mapBounded(items,concurrency,worker){const results=new Array(items.length);let next=0;await Promise.all(Array.from({length:Math.min(concurrency,items.length)},async()=>{while(next<items.length){const index=next++;results[index]=await worker(items[index])}}));return results}function galleryModel(value){const model=value??slopcameraImageModels[1];if(typeof model!=="string"||model.length>256||!/^[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._:-]*$/iu.test(model)){invalidArgument2("model must be a bounded Vercel AI Gateway provider/model id.")}return model}function galleryCellEdge(value){const cellEdge=value??slopcameraGalleryLimits.cellEdgeDefault;if(typeof cellEdge!=="number"||!Number.isInteger(cellEdge)||cellEdge<slopcameraGalleryLimits.cellEdgeMin||cellEdge>slopcameraGalleryLimits.cellEdgeMax){invalidArgument2(`cellEdge must be an integer from ${String(slopcameraGalleryLimits.cellEdgeMin)} through ${String(slopcameraGalleryLimits.cellEdgeMax)}.`)}return cellEdge}function galleryOutputDir(value){if(typeof value!=="string"||value.length<1||value.length>4096||value.includes("\x00")){invalidArgument2("outputDir must be a non-empty bounded local directory path.")}return resolve2(value)}async function generateSlopcameraImageGallery(rawInput,dependencies={}){if(!isRecord3(rawInput))invalidArgument2("Gallery input must be an object.");const input=rawInput;const outputDir=galleryOutputDir(input.outputDir);const model=galleryModel(input.model);const cellEdge=galleryCellEdge(input.cellEdge);const plan=planSlopcameraGallery(input);await mkdir2(outputDir,{recursive:true});const generated=await mapBounded(plan.candidates,slopcameraGalleryLimits.concurrency,async(candidate)=>await generateCandidate(candidate,model,input,dependencies));return await composeSlopcameraImageGallery({candidates:generated,cellEdge,model,outputDir,plan})}async function composeSlopcameraImageGallery(input){const outputDir=galleryOutputDir(input.outputDir);const model=galleryModel(input.model);const cellEdge=galleryCellEdge(input.cellEdge);const{plan}=input;if(!isRecord3(plan)||!Array.isArray(plan.candidates)||plan.candidates.length===0){invalidArgument2("plan must be a non-empty gallery plan from planSlopcameraGallery.")}const generated=input.candidates;await mkdir2(outputDir,{recursive:true});const labelHeight=slopcameraGalleryLimits.labelHeight;const cellWidth=cellEdge;const cellHeight=Math.round(cellEdge*plan.aspect[1]/plan.aspect[0]);const cellBodyHeight=cellHeight+labelHeight;const columns=Math.max(1,Math.ceil(Math.sqrt(generated.length)));const rows=Math.ceil(generated.length/columns);const gap=slopcameraGalleryLimits.cellGap;const sheetWidth=columns*cellWidth+(columns-1)*gap;const sheetHeight=rows*cellBodyHeight+(rows-1)*gap;if(sheetWidth*sheetHeight>slopcameraGalleryLimits.galleryPixels){invalidArgument2("Gallery sheet exceeds its pixel budget; reduce count or cellEdge.")}const receiptCandidates=[];const overlays=[];for(const candidate of generated){const column=(candidate.index-1)%columns;const row=Math.floor((candidate.index-1)/columns);const x=column*(cellWidth+gap);const y=row*(cellBodyHeight+gap);const labelText=candidate.status==="failed"?`#${String(candidate.index)} ${candidate.label} failed`:`#${String(candidate.index)} ${candidate.label}`;overlays.push({input:await sharp(labelPixels(labelText,cellWidth,labelHeight),{raw:{width:cellWidth,height:labelHeight,channels:4}}).png().toBuffer(),left:x,top:y});if(candidate.status==="generated"){if(candidate.bytes===undefined||candidate.mediaType===undefined){invalidArgument2("A generated gallery candidate requires its bytes and media type.")}const path=candidate.path??join(outputDir,`candidate-${String(candidate.index).padStart(2,"0")}-${slug(candidate.id)}.${mediaExtension(candidate.mediaType)}`);if(candidate.path===undefined){await atomicPublish(path,candidate.bytes)}const sha256=createHash2("sha256").update(candidate.bytes).digest("hex");if(candidate.sha256!==undefined&&candidate.sha256!==sha256){invalidArgument2("A pre-published gallery candidate digest does not match its bytes.")}let cellSource;let cellReceipt;if(candidate.cellImage!==undefined){const cellSha256=createHash2("sha256").update(candidate.cellImage.bytes).digest("hex");if(candidate.cellImage.sha256!==undefined&&candidate.cellImage.sha256!==cellSha256){invalidArgument2("A gallery cell image digest does not match its bytes.")}const cellCandidate=sharp(candidate.cellImage.bytes,{limitInputPixels:slopcameraGalleryLimits.candidatePixels,failOn:"warning"});const cellMetadata=await cellCandidate.metadata();if(cellMetadata.width===undefined||cellMetadata.height===undefined||cellMetadata.width>slopcameraGalleryLimits.candidateEdge||cellMetadata.height>slopcameraGalleryLimits.candidateEdge||cellMetadata.width*cellMetadata.height>slopcameraGalleryLimits.candidatePixels){invalidArgument2("A gallery cell image exceeds its raster bounds.")}cellSource=cellCandidate;cellReceipt={sha256:cellSha256,bytes:candidate.cellImage.bytes.byteLength,mediaType:candidate.cellImage.mediaType,...candidate.cellImage.path===undefined?{}:{path:candidate.cellImage.path}}}else{const source=sharp(candidate.bytes,{limitInputPixels:slopcameraGalleryLimits.candidatePixels,failOn:"warning"});const metadata=await source.metadata();if(metadata.width===undefined||metadata.height===undefined||metadata.width>slopcameraGalleryLimits.candidateEdge||metadata.height>slopcameraGalleryLimits.candidateEdge||metadata.width*metadata.height>slopcameraGalleryLimits.candidatePixels){invalidArgument2("A generated candidate exceeds its raster bounds.")}cellSource=source}const resized=await cellSource.resize(cellWidth,cellHeight,{fit:"cover"}).png().toBuffer();const cellImage=plan.tiled&&candidate.cellImage===undefined?await(async()=>{const tile=await sharp(resized).resize(Math.ceil(cellWidth/2),Math.ceil(cellHeight/2),{fit:"fill"}).png().toBuffer();const positions=[0,Math.floor(cellWidth/2)];const rows2=[0,Math.floor(cellHeight/2)];return sharp({create:{width:cellWidth,height:cellHeight,channels:4,background:{r:0,g:0,b:0,alpha:0}}}).composite(rows2.flatMap((top)=>positions.map((left)=>({input:tile,left,top})))).png().toBuffer()})():resized;overlays.push({input:cellImage,left:x,top:y+labelHeight});receiptCandidates.push({index:candidate.index,id:candidate.id,label:candidate.label,prompt:candidate.prompt,status:"generated",path,sha256,bytes:candidate.bytes.byteLength,mediaType:candidate.mediaType,...candidate.requestId===undefined?{}:{requestId:candidate.requestId},...candidate.warnings===undefined?{}:{warnings:candidate.warnings},...candidate.job===undefined?{}:{job:candidate.job},...plan.tiled&&candidate.cellImage===undefined?{tiled:true}:{},...cellReceipt===undefined?{}:{cellImage:cellReceipt},...candidate.scene===undefined?{}:{scene:candidate.scene},cell:{x,y,width:cellWidth,height:cellBodyHeight}})}else{const hasRetainedArtifact=candidate.bytes!==undefined||candidate.mediaType!==undefined||candidate.path!==undefined||candidate.sha256!==undefined;let retainedArtifact;if(hasRetainedArtifact){if(candidate.bytes===undefined||candidate.mediaType===undefined||candidate.path===undefined){invalidArgument2("A failed gallery candidate artifact requires bytes, media type, and path.")}const sha256=createHash2("sha256").update(candidate.bytes).digest("hex");if(candidate.sha256!==undefined&&candidate.sha256!==sha256){invalidArgument2("A failed gallery candidate artifact digest does not match its bytes.")}retainedArtifact={bytes:candidate.bytes.byteLength,mediaType:candidate.mediaType,path:candidate.path,sha256}}const blank=Buffer.alloc(cellWidth*cellHeight*4);for(let pixel=0;pixel<cellWidth*cellHeight;pixel++){const index=pixel*4;blank[index]=48;blank[index+1]=52;blank[index+2]=60;blank[index+3]=255}overlays.push({input:await sharp(blank,{raw:{width:cellWidth,height:cellHeight,channels:4}}).png().toBuffer(),left:x,top:y+labelHeight});receiptCandidates.push({index:candidate.index,id:candidate.id,label:candidate.label,prompt:candidate.prompt,status:"failed",...retainedArtifact,...candidate.requestId===undefined?{}:{requestId:candidate.requestId},...candidate.warnings===undefined?{}:{warnings:candidate.warnings},...candidate.error===undefined?{}:{error:candidate.error},...candidate.job===undefined?{}:{job:candidate.job},...candidate.scene===undefined?{}:{scene:candidate.scene},cell:{x,y,width:cellWidth,height:cellBodyHeight}})}}const sheet=await sharp({create:{width:sheetWidth,height:sheetHeight,channels:4,background:{r:10,g:12,b:16,alpha:255}}}).composite(overlays).png().toBuffer();const galleryPath=join(outputDir,"gallery.png");await atomicPublish(galleryPath,sheet);const gallerySha256=createHash2("sha256").update(sheet).digest("hex");const failed=generated.filter((candidate)=>candidate.status==="failed").length;const receiptPath=join(outputDir,"receipt.json");const receipt={kind:input.receipt?.kind??"slopcamera.image-gallery",schemaVersion:1,subject:plan.subject,galleryKind:input.receipt===undefined?plan.kind:"scene",model,provider:input.receipt===undefined?"vercel-ai-gateway":"local",...input.receipt===undefined?{}:{baseSceneSha256:input.receipt.baseSceneSha256},cell:{width:cellWidth,height:cellHeight,labelHeight},axes:plan.axes,candidates:receiptCandidates,counts:{requested:generated.length,generated:generated.length-failed,failed},gallery:{path:galleryPath,sha256:gallerySha256,bytes:sheet.byteLength,mediaType:"image/png",width:sheetWidth,height:sheetHeight},receiptPath,clientMaxRetries:0};await atomicPublish(receiptPath,`${JSON.stringify(receipt,null,2)}
34
- `);if(failed===generated.length){throw new SlopcameraCloudError("GENERATION_FAILED",`Every gallery candidate failed; see ${receiptPath} for the retained attempt record.`)}return receipt}var slopcameraOperationCodes=["slopcamera.diagram.check","slopcamera.diagram.render","slopcamera.image.vectorize","slopcamera.image.generate","slopcamera.image.icon","slopcamera.image.gallery"];class SlopcameraOperationError extends Error{code;constructor(code,message){super(`[${code}] ${message}`);this.name="SlopcameraOperationError";this.code=code}}var modelSchema={type:"string",minLength:3,maxLength:256,pattern:"^[a-zA-Z0-9][a-zA-Z0-9._-]*/[a-zA-Z0-9][a-zA-Z0-9._:-]*$"};var pathSchema={type:"string",minLength:1,maxLength:4096};function deepFreeze(value){if(typeof value!=="object"||value===null||Object.isFrozen(value)){return value}for(const nested of Object.values(value))deepFreeze(nested);return Object.freeze(value)}var slopcameraOperationRegistry=deepFreeze([{code:"slopcamera.diagram.check",title:"Check diagram",description:"Parse and lint a checked Slopcamera diagram source without changing its files.",execution:"local",authentication:"none",destructive:false,idempotent:true,inputSchema:{type:"object",additionalProperties:false,required:["path"],properties:{path:pathSchema}},resources:[{resource:"cpu",amount:1},{resource:"local-io",amount:1}]},{code:"slopcamera.diagram.render",title:"Render diagram",description:"Render a checked Slopcamera diagram source to its replaceable light, dark, PNG, SVG, and tldraw artifacts.",execution:"local",authentication:"none",destructive:true,idempotent:true,inputSchema:{type:"object",additionalProperties:false,required:["path"],properties:{path:pathSchema,outDirectory:pathSchema,scale:{type:"number",exclusiveMinimum:0,maximum:4}}},resources:[{resource:"cpu",amount:1},{resource:"local-io",amount:1}]},{code:"slopcamera.image.vectorize",title:"Vectorize image",description:"Convert a local caller-owned raster into a bounded inert SVG without authentication or network access.",execution:"local",authentication:"none",destructive:true,idempotent:false,inputSchema:{type:"object",additionalProperties:false,required:["inputPath","outputPath"],properties:{inputPath:pathSchema,outputPath:pathSchema,duotone:{type:"array",minItems:2,maxItems:2,items:{type:"string",pattern:"^#[a-fA-F0-9]{3}(?:[a-fA-F0-9]{3})?$"}},alphaCutoff:{type:"integer",minimum:1,maximum:64},timeoutMs:{type:"integer",minimum:1,maximum:300000}}},resources:[{resource:"cpu",amount:1},{resource:"local-io",amount:1}]},{code:"slopcamera.image.generate",title:"Generate image",description:"Generate one bounded image directly through Vercel AI Gateway with an environment credential and no client retry.",execution:"gateway",authentication:"environment",destructive:true,idempotent:false,inputSchema:{type:"object",additionalProperties:false,required:["model","prompt","outputPath"],properties:{model:modelSchema,prompt:{type:"string",minLength:1,maxLength:slopcameraMaximumPromptBytes},outputPath:pathSchema}},resources:[{resource:"local-io",amount:1},{resource:"network",amount:1},{resource:"paid-call",amount:1}],transport:{method:"POST",authority:"https://ai-gateway.vercel.sh/v4/ai",authorization:"bearer",retry:"never"}},{code:"slopcamera.image.icon",title:"Generate line-art icon",description:"Generate one isometric line-art SVG icon: a style-locked Vercel AI Gateway raster normalized to canonical ink-on-transparent pixels, traced locally, and optionally critiqued by a vision model across bounded rounds.",execution:"gateway",authentication:"environment",destructive:true,idempotent:false,inputSchema:{type:"object",additionalProperties:false,required:["subject","outputPath"],properties:{subject:{type:"string",minLength:1,maxLength:slopcameraIconSubjectMaximumBytes},outputPath:pathSchema,model:modelSchema,critiqueModel:modelSchema,ink:{type:"string",pattern:"^#[a-fA-F0-9]{3}(?:[a-fA-F0-9]{3})?$"},purpose:{type:"string",enum:["mark","illustration"]},rounds:{type:"integer",minimum:1,maximum:slopcameraIconMaximumRounds},keepRaster:{type:"boolean"}}},resources:[{resource:"cpu",amount:1},{resource:"local-io",amount:1},{resource:"network",amount:1},{resource:"paid-call",amount:1}],transport:{method:"POST",authority:"https://ai-gateway.vercel.sh/v4/ai",authorization:"bearer",retry:"never"}},{code:"slopcamera.image.gallery",title:"Generate image gallery",description:"Generate several bounded Vercel AI Gateway image candidates in parallel and compose a labelled contact sheet with per-candidate provenance for explicit review and selection.",execution:"gateway",authentication:"environment",destructive:true,idempotent:false,inputSchema:{type:"object",additionalProperties:false,required:["subject","outputDir"],properties:{subject:{type:"string",minLength:1,maxLength:slopcameraGalleryLimits.subjectBytes},outputDir:pathSchema,kind:{type:"string",enum:[...slopcameraGalleryKinds]},model:modelSchema,count:{type:"integer",minimum:1,maximum:slopcameraGalleryLimits.candidates},vary:{type:"array",maxItems:slopcameraGalleryAxes.length,items:{anyOf:[{type:"string",enum:[...slopcameraGalleryAxes]},{type:"object",additionalProperties:false,required:["axis"],properties:{axis:{type:"string",enum:[...slopcameraGalleryAxes]},values:{type:"array",minItems:1,maxItems:slopcameraGalleryLimits.candidates,items:{type:"string",minLength:1,maxLength:slopcameraGalleryLimits.variantBytes}}}}]}},candidates:{type:"array",minItems:1,maxItems:slopcameraGalleryLimits.candidates,items:{type:"object",additionalProperties:false,required:["id"],properties:{id:{type:"string",minLength:1,maxLength:slopcameraGalleryLimits.idLength},prompt:{type:"string",minLength:1,maxLength:slopcameraGalleryLimits.candidatePromptBytes},variant:{type:"string",minLength:1,maxLength:slopcameraGalleryLimits.variantBytes}}}},cellEdge:{type:"integer",minimum:slopcameraGalleryLimits.cellEdgeMin,maximum:slopcameraGalleryLimits.cellEdgeMax},tiled:{type:"boolean"},timeoutMs:{type:"integer",minimum:1000,maximum:30*60000}}},resources:[{resource:"cpu",amount:1},{resource:"local-io",amount:1},{resource:"network",amount:1},{resource:"paid-call",amount:1}],transport:{method:"POST",authority:"https://ai-gateway.vercel.sh/v4/ai",authorization:"bearer",retry:"never"}}]);function operationFailure(message){throw new SlopcameraOperationError("INVALID_OPERATION_INPUT",message)}function isRecord4(value){return typeof value==="object"&&value!==null&&!Array.isArray(value)}function record(value,allowedKeys){if(!isRecord4(value))operationFailure("Operation input must be an object.");const unknown=Object.keys(value).filter((key)=>!allowedKeys.includes(key));if(unknown.length>0){operationFailure(`Unsupported operation input field: ${unknown[0]}.`)}return value}function pathValue(value,name){if(typeof value!=="string"||value.length<1||value.length>4096||value.includes("\x00")){operationFailure(`${name} must be a non-empty bounded local path.`)}return value}function parseCheck(value){const input=record(value,["path"]);return{path:pathValue(input.path,"path")}}function parseRender(value){const input=record(value,["path","outDirectory","scale"]);const scale=input.scale;if(scale!==undefined&&(typeof scale!=="number"||!Number.isFinite(scale)||scale<=0||scale>4)){operationFailure("scale must be greater than zero and no more than 4.")}return{path:pathValue(input.path,"path"),...input.outDirectory===undefined?{}:{outDirectory:pathValue(input.outDirectory,"outDirectory")},...scale===undefined?{}:{scale}}}function parseVectorize(value){const input=record(value,["inputPath","outputPath","duotone","alphaCutoff","timeoutMs"]);const inputPath=pathValue(input.inputPath,"inputPath");const outputPath=pathValue(input.outputPath,"outputPath");if(!outputPath.toLowerCase().endsWith(".svg")){operationFailure("outputPath must end in .svg.")}const duotone=input.duotone;if(duotone!==undefined&&(!Array.isArray(duotone)||duotone.length!==2||duotone.some((color)=>typeof color!=="string"||!/^#[a-f0-9]{3}(?:[a-f0-9]{3})?$/iu.test(color)))){operationFailure("duotone must contain exactly two #rgb or #rrggbb colors.")}const alphaCutoff=input.alphaCutoff;if(alphaCutoff!==undefined&&(!Number.isInteger(alphaCutoff)||alphaCutoff<1||alphaCutoff>64)){operationFailure("alphaCutoff must be an integer from 1 through 64.")}const timeoutMs=input.timeoutMs;if(timeoutMs!==undefined&&(!Number.isInteger(timeoutMs)||timeoutMs<1||timeoutMs>300000)){operationFailure("timeoutMs must be an integer from 1 through 300000.")}return{inputPath,outputPath,...duotone===undefined?{}:{duotone},...alphaCutoff===undefined?{}:{alphaCutoff},...timeoutMs===undefined?{}:{timeoutMs}}}function parseGenerate(value){const input=record(value,["model","prompt","outputPath"]);if(typeof input.model!=="string"||input.model.length>256||!/^[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._:-]*$/iu.test(input.model)){operationFailure("model must be a bounded Vercel AI Gateway provider/model id.")}if(typeof input.prompt!=="string"||input.prompt.trim().length<1||/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/u.test(input.prompt)||Buffer.byteLength(input.prompt,"utf8")>slopcameraMaximumPromptBytes){operationFailure(`prompt must be non-empty and no more than ${slopcameraMaximumPromptBytes} UTF-8 bytes.`)}const outputPath=pathValue(input.outputPath,"outputPath");if(!/\.(?:jpe?g|png|webp)$/iu.test(outputPath)){operationFailure("outputPath must end in .png, .jpg, .jpeg, or .webp.")}return{model:input.model,prompt:input.prompt,outputPath}}function parseIcon(value){const input=record(value,["subject","outputPath","model","critiqueModel","ink","rounds","keepRaster"]);if(typeof input.subject!=="string"||input.subject.trim().length<1||/[\u0000-\u001f\u007f]/u.test(input.subject)||Buffer.byteLength(input.subject,"utf8")>slopcameraIconSubjectMaximumBytes){operationFailure(`subject must be non-empty and no more than ${slopcameraIconSubjectMaximumBytes} UTF-8 bytes.`)}const outputPath=pathValue(input.outputPath,"outputPath");if(!outputPath.toLowerCase().endsWith(".svg")){operationFailure("outputPath must end in .svg.")}for(const name of["model","critiqueModel"]){const model=input[name];if(model!==undefined&&(typeof model!=="string"||model.length>256||!/^[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._:-]*$/iu.test(model))){operationFailure(`${name} must be a bounded Vercel AI Gateway provider/model id.`)}}if(input.ink!==undefined&&(typeof input.ink!=="string"||!/^#[a-f0-9]{3}(?:[a-f0-9]{3})?$/iu.test(input.ink))){operationFailure("ink must be a #rgb or #rrggbb color.")}const rounds=input.rounds;if(rounds!==undefined&&(!Number.isInteger(rounds)||rounds<1||rounds>slopcameraIconMaximumRounds)){operationFailure(`rounds must be an integer from 1 through ${slopcameraIconMaximumRounds}.`)}if(input.keepRaster!==undefined&&typeof input.keepRaster!=="boolean"){operationFailure("keepRaster must be a boolean.")}return{subject:input.subject,outputPath,...input.model===undefined?{}:{model:input.model},...input.critiqueModel===undefined?{}:{critiqueModel:input.critiqueModel},...input.ink===undefined?{}:{ink:input.ink},...rounds===undefined?{}:{rounds},...input.keepRaster===undefined?{}:{keepRaster:input.keepRaster}}}function parseGallery(value){const input=record(value,["subject","outputDir","kind","model","count","vary","candidates","cellEdge","tiled","timeoutMs"]);if(typeof input.subject!=="string"||input.subject.trim().length<1||/[\u0000-\u001f\u007f]/u.test(input.subject)||Buffer.byteLength(input.subject,"utf8")>slopcameraGalleryLimits.subjectBytes){operationFailure(`subject must be non-empty and no more than ${slopcameraGalleryLimits.subjectBytes} UTF-8 bytes.`)}if(input.kind!==undefined&&(typeof input.kind!=="string"||!slopcameraGalleryKinds.includes(input.kind))){operationFailure(`kind must be one of: ${slopcameraGalleryKinds.join(", ")}.`)}if(input.model!==undefined&&(typeof input.model!=="string"||input.model.length>256||!/^[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._:-]*$/iu.test(input.model))){operationFailure("model must be a bounded Vercel AI Gateway provider/model id.")}if(input.count!==undefined&&(!Number.isInteger(input.count)||input.count<1||input.count>slopcameraGalleryLimits.candidates)){operationFailure(`count must be an integer from 1 through ${slopcameraGalleryLimits.candidates}.`)}if(input.vary!==undefined&&(!Array.isArray(input.vary)||input.vary.length<1||input.vary.length>slopcameraGalleryAxes.length)){operationFailure(`vary must be a list of at most ${slopcameraGalleryAxes.length} axes.`)}if(input.candidates!==undefined&&(!Array.isArray(input.candidates)||input.candidates.length<1||input.candidates.length>slopcameraGalleryLimits.candidates)){operationFailure(`candidates must contain 1 through ${slopcameraGalleryLimits.candidates} entries.`)}if(input.vary!==undefined&&input.candidates!==undefined){operationFailure("vary and candidates are mutually exclusive.")}if(input.count!==undefined&&input.candidates!==undefined){operationFailure("count and candidates are mutually exclusive.")}const cellEdge=input.cellEdge;if(cellEdge!==undefined&&(!Number.isInteger(cellEdge)||cellEdge<slopcameraGalleryLimits.cellEdgeMin||cellEdge>slopcameraGalleryLimits.cellEdgeMax)){operationFailure(`cellEdge must be an integer from ${slopcameraGalleryLimits.cellEdgeMin} through ${slopcameraGalleryLimits.cellEdgeMax}.`)}const timeoutMs=input.timeoutMs;if(timeoutMs!==undefined&&(!Number.isInteger(timeoutMs)||timeoutMs<1000||timeoutMs>30*60000)){operationFailure("timeoutMs must be an integer from 1000 through 1800000.")}if(input.tiled!==undefined&&typeof input.tiled!=="boolean"){operationFailure("tiled must be a boolean when set.")}return{subject:input.subject,outputDir:pathValue(input.outputDir,"outputDir"),...input.kind===undefined?{}:{kind:input.kind},...input.model===undefined?{}:{model:input.model},...input.count===undefined?{}:{count:input.count},...input.vary===undefined?{}:{vary:input.vary},...input.candidates===undefined?{}:{candidates:input.candidates},...cellEdge===undefined?{}:{cellEdge},...input.tiled===undefined?{}:{tiled:input.tiled},...timeoutMs===undefined?{}:{timeoutMs}}}function parseSlopcameraOperationInput(code,input){switch(code){case"slopcamera.diagram.check":return parseCheck(input);case"slopcamera.diagram.render":return parseRender(input);case"slopcamera.image.vectorize":return parseVectorize(input);case"slopcamera.image.generate":return parseGenerate(input);case"slopcamera.image.icon":return parseIcon(input);case"slopcamera.image.gallery":return parseGallery(input);default:throw new SlopcameraOperationError("INVALID_OPERATION","Unknown Slopcamera operation code.")}}function isSlopcameraOperationCode(value){return slopcameraOperationCodes.includes(value)}function slopcameraOperationHostResourceClaims(code){const descriptor=slopcameraOperationRegistry.find((candidate)=>candidate.code===code);if(descriptor===undefined){throw new SlopcameraOperationError("INVALID_OPERATION","Unknown Slopcamera operation code.")}return descriptor.resources}function searchSlopcameraOperations(query="",limit=slopcameraOperationRegistry.length){if(typeof query!=="string"||query.length>200||/[\u0000-\u001f\u007f]/u.test(query)||!Number.isInteger(limit)||limit<1||limit>20){throw new SlopcameraOperationError("INVALID_SEARCH","Search requires a bounded query and a limit from 1 through 20.")}const terms=query.toLowerCase().split(/\s+/u).filter((term)=>term.length>0);return slopcameraOperationRegistry.filter((operation)=>{const haystack=`${operation.code} ${operation.title} ${operation.description}`.toLowerCase();return terms.every((term)=>haystack.includes(term))}).slice(0,limit)}function operationDependenciesWithLease(dependencies,lease){const inheritedFileDescriptors=[...dependencies.inheritedFileDescriptors??[],lease.inheritedFileDescriptor].filter((descriptor,index,descriptors)=>descriptors.indexOf(descriptor)===index);if(inheritedFileDescriptors.length>16||inheritedFileDescriptors.some((descriptor)=>!Number.isSafeInteger(descriptor)||descriptor<0||descriptor>2147483647)){throw new SlopcameraOperationError("INVALID_OPERATION_INPUT","Operation host-resource inheritance exceeds its descriptor bound.")}const{hostResourceCoordinator:_hostResourceCoordinator,signal:_signal,waitTimeoutMilliseconds:_waitTimeoutMilliseconds,...operationDependencies}=dependencies;return{...operationDependencies,inheritedFileDescriptors}}async function withSlopcameraOperationHostAdmission(code,callback,options={}){const coordinator=options.hostResourceCoordinator??createDefaultHostResourceCoordinator();return await coordinator.withLease(slopcameraOperationHostResourceClaims(code),async(lease)=>{await lease.assertOwned();return await callback(lease)},{...options.signal===undefined?{}:{signal:options.signal},...options.waitTimeoutMilliseconds===undefined?{}:{waitTimeoutMilliseconds:options.waitTimeoutMilliseconds}})}var operationBuiltInConfig=Object.freeze({icons:builtInIcons});async function readOperationDiagram(path){const absolutePath=resolve3(path);let value;try{value=JSON.parse(await readFile2(absolutePath,"utf8"))}catch(cause){throw new SlopcameraOperationError("INVALID_OPERATION_INPUT","Diagram source could not be read as JSON.")}const spec=parseDiagramSpec(value);for(const shape of spec.shapes){if((shape.type==="rect"||shape.type==="ellipse")&&shape.icon!==undefined&&!Object.hasOwn(builtInIcons,shape.icon)){throw new SlopcameraOperationError("INVALID_OPERATION_INPUT","Diagram requests an unavailable built-in icon.")}}return{absolutePath,spec}}async function atomicOperationWrite(path,value){const temporaryPath=join2(dirname3(path),`.${randomUUID3()}.slopcamera-operation.tmp`);try{await writeFile3(temporaryPath,value,{flag:"wx"});await rename2(temporaryPath,path)}finally{await rm3(temporaryPath,{force:true}).catch(()=>{return})}}async function checkOperationDiagram(path){const{spec}=await readOperationDiagram(path);return{findings:lintDiagram(spec),configPath:null}}async function renderOperationDiagram(input){const{absolutePath,spec}=await readOperationDiagram(input.path);const outputDirectory=resolve3(input.outDirectory??dirname3(absolutePath));const scale=input.scale??2;const[light,dark]=await Promise.all([renderSvg(spec,"light",operationBuiltInConfig),renderSvg(spec,"dark",operationBuiltInConfig)]);const[lightPng,darkPng]=[renderPng(light,operationBuiltInConfig,scale),renderPng(dark,operationBuiltInConfig,scale)];const artifacts={spec:absolutePath,tldr:join2(outputDirectory,`${spec.name}.tldr`),lightSvg:join2(outputDirectory,`${spec.name}.light.svg`),darkSvg:join2(outputDirectory,`${spec.name}.dark.svg`),lightPng:join2(outputDirectory,`${spec.name}.light.png`),darkPng:join2(outputDirectory,`${spec.name}.dark.png`)};await mkdir3(outputDirectory,{recursive:true});await Promise.all([atomicOperationWrite(artifacts.tldr,serializeTldr(spec,operationBuiltInConfig)),atomicOperationWrite(artifacts.lightSvg,light.svg),atomicOperationWrite(artifacts.darkSvg,dark.svg),atomicOperationWrite(artifacts.lightPng,lightPng),atomicOperationWrite(artifacts.darkPng,darkPng)]);return{artifacts,findings:lintDiagram(spec),configPath:null}}async function executeSlopcameraOperationUncoordinated(code,value,dependencies={}){const input=parseSlopcameraOperationInput(code,value);switch(code){case"slopcamera.diagram.check":{const options=input;return await checkOperationDiagram(options.path)}case"slopcamera.diagram.render":{const options=input;return await renderOperationDiagram(options)}case"slopcamera.image.vectorize":{const options=input;const result=await vectorizeImage(options.inputPath,{outputPath:options.outputPath,...options.duotone===undefined?{}:{duotone:options.duotone},...options.alphaCutoff===undefined?{}:{alphaCutoff:options.alphaCutoff},...options.timeoutMs===undefined?{}:{limits:{maxDurationMs:options.timeoutMs}},...dependencies.inheritedFileDescriptors===undefined?{}:{inheritedFileDescriptors:dependencies.inheritedFileDescriptors}});if(result.outputPath===null){throw new SlopcameraOperationError("INVALID_OPERATION_INPUT","Vectorization did not publish its required output.")}return{outputPath:result.outputPath,receipt:result.receipt}}case"slopcamera.image.generate":{const options=input;return await generateSlopcameraImageFile({...options,...dependencies.signal===undefined?{}:{signal:dependencies.signal}},dependencies)}case"slopcamera.image.icon":{const options=input;return await generateSlopcameraIcon({...options,...dependencies.signal===undefined?{}:{signal:dependencies.signal},...dependencies.inheritedFileDescriptors===undefined?{}:{inheritedFileDescriptors:dependencies.inheritedFileDescriptors}},dependencies)}case"slopcamera.image.gallery":{const options=input;return await generateSlopcameraImageGallery({...options,...dependencies.signal===undefined?{}:{signal:dependencies.signal}},dependencies)}default:throw new SlopcameraOperationError("INVALID_OPERATION","Unknown Slopcamera operation code.")}}async function executeSlopcameraOperationWithLease(code,value,lease,dependencies={}){await lease.assertOwned();const available=new Map;for(const claim of lease.claims){if(typeof claim.resource!=="string"||!/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u.test(claim.resource)||!Number.isSafeInteger(claim.amount)||claim.amount<1){throw new SlopcameraOperationError("INVALID_OPERATION","The active host-resource lease contains invalid claims.")}const total=(available.get(claim.resource)??0)+claim.amount;if(!Number.isSafeInteger(total)){throw new SlopcameraOperationError("INVALID_OPERATION","The active host-resource lease contains invalid claims.")}available.set(claim.resource,total)}const missing=slopcameraOperationHostResourceClaims(code).filter((claim)=>(available.get(claim.resource)??0)<claim.amount);if(missing.length>0){throw new SlopcameraOperationError("INVALID_OPERATION",`The active host-resource lease does not cover ${missing.map((claim)=>`${claim.resource}:${String(claim.amount)}`).join(", ")}.`)}return await executeSlopcameraOperationUncoordinated(code,value,operationDependenciesWithLease(dependencies,lease))}async function executeSlopcameraOperation(code,value,dependencies={}){const input=parseSlopcameraOperationInput(code,value);return await withSlopcameraOperationHostAdmission(code,async(lease)=>await executeSlopcameraOperationUncoordinated(code,input,operationDependenciesWithLease(dependencies,lease)),dependencies)}
35
- export{builtInIcons,sanitizeIcon,layoutBoxContent,NebulaSans_Bold_default,NebulaSans_Book_default,resolveEdge,renderSvg,renderPng,lintDiagram,stackLayoutDefaults,StackLayoutError,resolveStackLayout,resolveDiagramSource,DiagramValidationError,parseDiagramSource,parseDiagramSpec,serializeTldr,slopcameraIconDefaultInk,slopcameraIconPanel,slopcameraIconDefaultRounds,slopcameraIconMaximumRounds,slopcameraIconSubjectMaximumBytes,slopcameraIconCritiqueDefaultModel,slopcameraIconCritiqueTimeoutMs,iconPromptFor,iconVisualGateProblems,extractIconLineArt,critiqueIconRaster,generateSlopcameraIcon,slopcameraGalleryKinds,slopcameraGalleryAxes,slopcameraGalleryLimits,galleryCandidateId,galleryPromptFor,planSlopcameraGallery,parseSlopcameraGalleryVary,generateSlopcameraImageGallery,composeSlopcameraImageGallery,slopcameraOperationCodes,SlopcameraOperationError,slopcameraOperationRegistry,parseSlopcameraOperationInput,isSlopcameraOperationCode,slopcameraOperationHostResourceClaims,searchSlopcameraOperations,withSlopcameraOperationHostAdmission,executeSlopcameraOperationWithLease,executeSlopcameraOperation};
37
+ `);if(failed===generated.length){throw new SlopcameraCloudError("GENERATION_FAILED",`Every gallery candidate failed; see ${receiptPath} for the retained attempt record.`)}return receipt}var slopcameraOperationCodes=["slopcamera.diagram.check","slopcamera.diagram.render","slopcamera.image.vectorize","slopcamera.image.generate","slopcamera.image.icon","slopcamera.image.gallery"];class SlopcameraOperationError extends Error{code;constructor(code,message){super(`[${code}] ${message}`);this.name="SlopcameraOperationError";this.code=code}}var modelSchema={type:"string",minLength:3,maxLength:256,pattern:"^[a-zA-Z0-9][a-zA-Z0-9._-]*/[a-zA-Z0-9][a-zA-Z0-9._:-]*$"};var pathSchema={type:"string",minLength:1,maxLength:4096};function deepFreeze(value){if(typeof value!=="object"||value===null||Object.isFrozen(value)){return value}for(const nested of Object.values(value))deepFreeze(nested);return Object.freeze(value)}var slopcameraOperationRegistry=deepFreeze([{code:"slopcamera.diagram.check",title:"Check diagram",description:"Parse and lint a checked Slopcamera diagram source without changing its files.",execution:"local",authentication:"none",destructive:false,idempotent:true,inputSchema:{type:"object",additionalProperties:false,required:["path"],properties:{path:pathSchema}},resources:[{resource:"cpu",amount:1},{resource:"local-io",amount:1}]},{code:"slopcamera.diagram.render",title:"Render diagram",description:"Render a checked Slopcamera diagram source to its replaceable light, dark, PNG, SVG, and tldraw artifacts.",execution:"local",authentication:"none",destructive:true,idempotent:true,inputSchema:{type:"object",additionalProperties:false,required:["path"],properties:{path:pathSchema,outDirectory:pathSchema,scale:{type:"number",exclusiveMinimum:0,maximum:4}}},resources:[{resource:"cpu",amount:1},{resource:"local-io",amount:1}]},{code:"slopcamera.image.vectorize",title:"Vectorize image",description:"Convert a local caller-owned raster into a bounded inert SVG without authentication or network access.",execution:"local",authentication:"none",destructive:true,idempotent:false,inputSchema:{type:"object",additionalProperties:false,required:["inputPath","outputPath"],properties:{inputPath:pathSchema,outputPath:pathSchema,duotone:{type:"array",minItems:2,maxItems:2,items:{type:"string",pattern:"^#[a-fA-F0-9]{3}(?:[a-fA-F0-9]{3})?$"}},alphaCutoff:{type:"integer",minimum:1,maximum:64},timeoutMs:{type:"integer",minimum:1,maximum:300000}}},resources:[{resource:"cpu",amount:1},{resource:"local-io",amount:1}]},{code:"slopcamera.image.generate",title:"Generate image",description:"Generate one bounded image directly through Vercel AI Gateway with an environment credential and no client retry.",execution:"gateway",authentication:"environment",destructive:true,idempotent:false,inputSchema:{type:"object",additionalProperties:false,required:["model","prompt","outputPath"],properties:{model:modelSchema,prompt:{type:"string",minLength:1,maxLength:slopcameraMaximumPromptBytes},outputPath:pathSchema}},resources:[{resource:"local-io",amount:1},{resource:"network",amount:1},{resource:"paid-call",amount:1}],transport:{method:"POST",authority:"https://ai-gateway.vercel.sh/v4/ai",authorization:"bearer",retry:"never"}},{code:"slopcamera.image.icon",title:"Generate line-art icon",description:"Generate one isometric line-art SVG icon: a style-locked Vercel AI Gateway raster normalized to canonical ink-on-transparent pixels, traced locally, and optionally critiqued by a vision model across bounded rounds.",execution:"gateway",authentication:"environment",destructive:true,idempotent:false,inputSchema:{type:"object",additionalProperties:false,required:["subject","outputPath"],properties:{subject:{type:"string",minLength:1,maxLength:slopcameraIconSubjectMaximumBytes},outputPath:pathSchema,model:modelSchema,critiqueModel:modelSchema,ink:{type:"string",pattern:"^#[a-fA-F0-9]{3}(?:[a-fA-F0-9]{3})?$"},purpose:{type:"string",enum:["mark","illustration"]},context:{type:"string",enum:[...slopcameraIconContexts]},candidatePool:{type:"integer",minimum:1,maximum:slopcameraIconMaximumRounds},rounds:{type:"integer",minimum:1,maximum:slopcameraIconMaximumRounds},keepRaster:{type:"boolean"}}},resources:[{resource:"cpu",amount:1},{resource:"local-io",amount:1},{resource:"network",amount:1},{resource:"paid-call",amount:1}],transport:{method:"POST",authority:"https://ai-gateway.vercel.sh/v4/ai",authorization:"bearer",retry:"never"}},{code:"slopcamera.image.gallery",title:"Generate image gallery",description:"Generate several bounded Vercel AI Gateway image candidates in parallel and compose a labelled contact sheet with per-candidate provenance for explicit review and selection.",execution:"gateway",authentication:"environment",destructive:true,idempotent:false,inputSchema:{type:"object",additionalProperties:false,required:["subject","outputDir"],properties:{subject:{type:"string",minLength:1,maxLength:slopcameraGalleryLimits.subjectBytes},outputDir:pathSchema,kind:{type:"string",enum:[...slopcameraGalleryKinds]},model:modelSchema,count:{type:"integer",minimum:1,maximum:slopcameraGalleryLimits.candidates},vary:{type:"array",maxItems:slopcameraGalleryAxes.length,items:{anyOf:[{type:"string",enum:[...slopcameraGalleryAxes]},{type:"object",additionalProperties:false,required:["axis"],properties:{axis:{type:"string",enum:[...slopcameraGalleryAxes]},values:{type:"array",minItems:1,maxItems:slopcameraGalleryLimits.candidates,items:{type:"string",minLength:1,maxLength:slopcameraGalleryLimits.variantBytes}}}}]}},candidates:{type:"array",minItems:1,maxItems:slopcameraGalleryLimits.candidates,items:{type:"object",additionalProperties:false,required:["id"],properties:{id:{type:"string",minLength:1,maxLength:slopcameraGalleryLimits.idLength},prompt:{type:"string",minLength:1,maxLength:slopcameraGalleryLimits.candidatePromptBytes},variant:{type:"string",minLength:1,maxLength:slopcameraGalleryLimits.variantBytes}}}},cellEdge:{type:"integer",minimum:slopcameraGalleryLimits.cellEdgeMin,maximum:slopcameraGalleryLimits.cellEdgeMax},tiled:{type:"boolean"},timeoutMs:{type:"integer",minimum:1000,maximum:30*60000}}},resources:[{resource:"cpu",amount:1},{resource:"local-io",amount:1},{resource:"network",amount:1},{resource:"paid-call",amount:1}],transport:{method:"POST",authority:"https://ai-gateway.vercel.sh/v4/ai",authorization:"bearer",retry:"never"}}]);function operationFailure(message){throw new SlopcameraOperationError("INVALID_OPERATION_INPUT",message)}function isRecord4(value){return typeof value==="object"&&value!==null&&!Array.isArray(value)}function record(value,allowedKeys){if(!isRecord4(value))operationFailure("Operation input must be an object.");const unknown=Object.keys(value).filter((key)=>!allowedKeys.includes(key));if(unknown.length>0){operationFailure(`Unsupported operation input field: ${unknown[0]}.`)}return value}function pathValue(value,name){if(typeof value!=="string"||value.length<1||value.length>4096||value.includes("\x00")){operationFailure(`${name} must be a non-empty bounded local path.`)}return value}function parseCheck(value){const input=record(value,["path"]);return{path:pathValue(input.path,"path")}}function parseRender(value){const input=record(value,["path","outDirectory","scale"]);const scale=input.scale;if(scale!==undefined&&(typeof scale!=="number"||!Number.isFinite(scale)||scale<=0||scale>4)){operationFailure("scale must be greater than zero and no more than 4.")}return{path:pathValue(input.path,"path"),...input.outDirectory===undefined?{}:{outDirectory:pathValue(input.outDirectory,"outDirectory")},...scale===undefined?{}:{scale}}}function parseVectorize(value){const input=record(value,["inputPath","outputPath","duotone","alphaCutoff","timeoutMs"]);const inputPath=pathValue(input.inputPath,"inputPath");const outputPath=pathValue(input.outputPath,"outputPath");if(!outputPath.toLowerCase().endsWith(".svg")){operationFailure("outputPath must end in .svg.")}const duotone=input.duotone;if(duotone!==undefined&&(!Array.isArray(duotone)||duotone.length!==2||duotone.some((color)=>typeof color!=="string"||!/^#[a-f0-9]{3}(?:[a-f0-9]{3})?$/iu.test(color)))){operationFailure("duotone must contain exactly two #rgb or #rrggbb colors.")}const alphaCutoff=input.alphaCutoff;if(alphaCutoff!==undefined&&(!Number.isInteger(alphaCutoff)||alphaCutoff<1||alphaCutoff>64)){operationFailure("alphaCutoff must be an integer from 1 through 64.")}const timeoutMs=input.timeoutMs;if(timeoutMs!==undefined&&(!Number.isInteger(timeoutMs)||timeoutMs<1||timeoutMs>300000)){operationFailure("timeoutMs must be an integer from 1 through 300000.")}return{inputPath,outputPath,...duotone===undefined?{}:{duotone},...alphaCutoff===undefined?{}:{alphaCutoff},...timeoutMs===undefined?{}:{timeoutMs}}}function parseGenerate(value){const input=record(value,["model","prompt","outputPath"]);if(typeof input.model!=="string"||input.model.length>256||!/^[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._:-]*$/iu.test(input.model)){operationFailure("model must be a bounded Vercel AI Gateway provider/model id.")}if(typeof input.prompt!=="string"||input.prompt.trim().length<1||/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/u.test(input.prompt)||Buffer.byteLength(input.prompt,"utf8")>slopcameraMaximumPromptBytes){operationFailure(`prompt must be non-empty and no more than ${slopcameraMaximumPromptBytes} UTF-8 bytes.`)}const outputPath=pathValue(input.outputPath,"outputPath");if(!/\.(?:jpe?g|png|webp)$/iu.test(outputPath)){operationFailure("outputPath must end in .png, .jpg, .jpeg, or .webp.")}return{model:input.model,prompt:input.prompt,outputPath}}function parseIcon(value){const input=record(value,["subject","outputPath","model","critiqueModel","ink","purpose","context","candidatePool","rounds","keepRaster"]);if(typeof input.subject!=="string"||input.subject.trim().length<1||/[\u0000-\u001f\u007f]/u.test(input.subject)||Buffer.byteLength(input.subject,"utf8")>slopcameraIconSubjectMaximumBytes){operationFailure(`subject must be non-empty and no more than ${slopcameraIconSubjectMaximumBytes} UTF-8 bytes.`)}const outputPath=pathValue(input.outputPath,"outputPath");if(!outputPath.toLowerCase().endsWith(".svg")){operationFailure("outputPath must end in .svg.")}for(const name of["model","critiqueModel"]){const model=input[name];if(model!==undefined&&(typeof model!=="string"||model.length>256||!/^[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._:-]*$/iu.test(model))){operationFailure(`${name} must be a bounded Vercel AI Gateway provider/model id.`)}}if(input.ink!==undefined&&(typeof input.ink!=="string"||!/^#[a-f0-9]{3}(?:[a-f0-9]{3})?$/iu.test(input.ink))){operationFailure("ink must be a #rgb or #rrggbb color.")}if(input.purpose!==undefined&&input.purpose!=="illustration"&&input.purpose!=="mark"){operationFailure("purpose must be illustration or mark.")}if(input.context!==undefined&&(typeof input.context!=="string"||!slopcameraIconContexts.includes(input.context))){operationFailure(`context must be one of: ${slopcameraIconContexts.join(", ")}.`)}const candidatePool=input.candidatePool;if(candidatePool!==undefined&&(!Number.isInteger(candidatePool)||candidatePool<1||candidatePool>slopcameraIconMaximumRounds)){operationFailure(`candidatePool must be an integer from 1 through ${slopcameraIconMaximumRounds}.`)}const rounds=input.rounds;if(rounds!==undefined&&(!Number.isInteger(rounds)||rounds<1||rounds>slopcameraIconMaximumRounds)){operationFailure(`rounds must be an integer from 1 through ${slopcameraIconMaximumRounds}.`)}if(input.keepRaster!==undefined&&typeof input.keepRaster!=="boolean"){operationFailure("keepRaster must be a boolean.")}return{subject:input.subject,outputPath,...input.model===undefined?{}:{model:input.model},...input.critiqueModel===undefined?{}:{critiqueModel:input.critiqueModel},...input.ink===undefined?{}:{ink:input.ink},...input.purpose===undefined?{}:{purpose:input.purpose},...input.context===undefined?{}:{context:input.context},...candidatePool===undefined?{}:{candidatePool},...rounds===undefined?{}:{rounds},...input.keepRaster===undefined?{}:{keepRaster:input.keepRaster}}}function parseGallery(value){const input=record(value,["subject","outputDir","kind","model","count","vary","candidates","cellEdge","tiled","timeoutMs"]);if(typeof input.subject!=="string"||input.subject.trim().length<1||/[\u0000-\u001f\u007f]/u.test(input.subject)||Buffer.byteLength(input.subject,"utf8")>slopcameraGalleryLimits.subjectBytes){operationFailure(`subject must be non-empty and no more than ${slopcameraGalleryLimits.subjectBytes} UTF-8 bytes.`)}if(input.kind!==undefined&&(typeof input.kind!=="string"||!slopcameraGalleryKinds.includes(input.kind))){operationFailure(`kind must be one of: ${slopcameraGalleryKinds.join(", ")}.`)}if(input.model!==undefined&&(typeof input.model!=="string"||input.model.length>256||!/^[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._:-]*$/iu.test(input.model))){operationFailure("model must be a bounded Vercel AI Gateway provider/model id.")}if(input.count!==undefined&&(!Number.isInteger(input.count)||input.count<1||input.count>slopcameraGalleryLimits.candidates)){operationFailure(`count must be an integer from 1 through ${slopcameraGalleryLimits.candidates}.`)}if(input.vary!==undefined&&(!Array.isArray(input.vary)||input.vary.length<1||input.vary.length>slopcameraGalleryAxes.length)){operationFailure(`vary must be a list of at most ${slopcameraGalleryAxes.length} axes.`)}if(input.candidates!==undefined&&(!Array.isArray(input.candidates)||input.candidates.length<1||input.candidates.length>slopcameraGalleryLimits.candidates)){operationFailure(`candidates must contain 1 through ${slopcameraGalleryLimits.candidates} entries.`)}if(input.vary!==undefined&&input.candidates!==undefined){operationFailure("vary and candidates are mutually exclusive.")}if(input.count!==undefined&&input.candidates!==undefined){operationFailure("count and candidates are mutually exclusive.")}const cellEdge=input.cellEdge;if(cellEdge!==undefined&&(!Number.isInteger(cellEdge)||cellEdge<slopcameraGalleryLimits.cellEdgeMin||cellEdge>slopcameraGalleryLimits.cellEdgeMax)){operationFailure(`cellEdge must be an integer from ${slopcameraGalleryLimits.cellEdgeMin} through ${slopcameraGalleryLimits.cellEdgeMax}.`)}const timeoutMs=input.timeoutMs;if(timeoutMs!==undefined&&(!Number.isInteger(timeoutMs)||timeoutMs<1000||timeoutMs>30*60000)){operationFailure("timeoutMs must be an integer from 1000 through 1800000.")}if(input.tiled!==undefined&&typeof input.tiled!=="boolean"){operationFailure("tiled must be a boolean when set.")}return{subject:input.subject,outputDir:pathValue(input.outputDir,"outputDir"),...input.kind===undefined?{}:{kind:input.kind},...input.model===undefined?{}:{model:input.model},...input.count===undefined?{}:{count:input.count},...input.vary===undefined?{}:{vary:input.vary},...input.candidates===undefined?{}:{candidates:input.candidates},...cellEdge===undefined?{}:{cellEdge},...input.tiled===undefined?{}:{tiled:input.tiled},...timeoutMs===undefined?{}:{timeoutMs}}}function parseSlopcameraOperationInput(code,input){switch(code){case"slopcamera.diagram.check":return parseCheck(input);case"slopcamera.diagram.render":return parseRender(input);case"slopcamera.image.vectorize":return parseVectorize(input);case"slopcamera.image.generate":return parseGenerate(input);case"slopcamera.image.icon":return parseIcon(input);case"slopcamera.image.gallery":return parseGallery(input);default:throw new SlopcameraOperationError("INVALID_OPERATION","Unknown Slopcamera operation code.")}}function isSlopcameraOperationCode(value){return slopcameraOperationCodes.includes(value)}function slopcameraOperationHostResourceClaims(code){const descriptor=slopcameraOperationRegistry.find((candidate)=>candidate.code===code);if(descriptor===undefined){throw new SlopcameraOperationError("INVALID_OPERATION","Unknown Slopcamera operation code.")}return descriptor.resources}function searchSlopcameraOperations(query="",limit=slopcameraOperationRegistry.length){if(typeof query!=="string"||query.length>200||/[\u0000-\u001f\u007f]/u.test(query)||!Number.isInteger(limit)||limit<1||limit>20){throw new SlopcameraOperationError("INVALID_SEARCH","Search requires a bounded query and a limit from 1 through 20.")}const terms=query.toLowerCase().split(/\s+/u).filter((term)=>term.length>0);return slopcameraOperationRegistry.filter((operation)=>{const haystack=`${operation.code} ${operation.title} ${operation.description}`.toLowerCase();return terms.every((term)=>haystack.includes(term))}).slice(0,limit)}function operationDependenciesWithLease(dependencies,lease){const inheritedFileDescriptors=[...dependencies.inheritedFileDescriptors??[],lease.inheritedFileDescriptor].filter((descriptor,index,descriptors)=>descriptors.indexOf(descriptor)===index);if(inheritedFileDescriptors.length>16||inheritedFileDescriptors.some((descriptor)=>!Number.isSafeInteger(descriptor)||descriptor<0||descriptor>2147483647)){throw new SlopcameraOperationError("INVALID_OPERATION_INPUT","Operation host-resource inheritance exceeds its descriptor bound.")}const{hostResourceCoordinator:_hostResourceCoordinator,signal:_signal,waitTimeoutMilliseconds:_waitTimeoutMilliseconds,...operationDependencies}=dependencies;return{...operationDependencies,inheritedFileDescriptors}}async function withSlopcameraOperationHostAdmission(code,callback,options={}){const coordinator=options.hostResourceCoordinator??createDefaultHostResourceCoordinator();return await coordinator.withLease(slopcameraOperationHostResourceClaims(code),async(lease)=>{await lease.assertOwned();return await callback(lease)},{...options.signal===undefined?{}:{signal:options.signal},...options.waitTimeoutMilliseconds===undefined?{}:{waitTimeoutMilliseconds:options.waitTimeoutMilliseconds}})}var operationBuiltInConfig=Object.freeze({icons:builtInIcons});async function readOperationDiagram(path){const absolutePath=resolve3(path);let value;try{value=JSON.parse(await readFile2(absolutePath,"utf8"))}catch(cause){throw new SlopcameraOperationError("INVALID_OPERATION_INPUT","Diagram source could not be read as JSON.")}const spec=parseDiagramSpec(value);for(const shape of spec.shapes){if((shape.type==="rect"||shape.type==="ellipse")&&shape.icon!==undefined&&!Object.hasOwn(builtInIcons,shape.icon)){throw new SlopcameraOperationError("INVALID_OPERATION_INPUT","Diagram requests an unavailable built-in icon.")}}return{absolutePath,spec}}async function atomicOperationWrite(path,value){const temporaryPath=join2(dirname3(path),`.${randomUUID3()}.slopcamera-operation.tmp`);try{await writeFile3(temporaryPath,value,{flag:"wx"});await rename2(temporaryPath,path)}finally{await rm3(temporaryPath,{force:true}).catch(()=>{return})}}async function checkOperationDiagram(path){const{spec}=await readOperationDiagram(path);return{findings:lintDiagram(spec),configPath:null}}async function renderOperationDiagram(input){const{absolutePath,spec}=await readOperationDiagram(input.path);const outputDirectory=resolve3(input.outDirectory??dirname3(absolutePath));const scale=input.scale??2;const[light,dark]=await Promise.all([renderSvg(spec,"light",operationBuiltInConfig),renderSvg(spec,"dark",operationBuiltInConfig)]);const[lightPng,darkPng]=[renderPng(light,operationBuiltInConfig,scale),renderPng(dark,operationBuiltInConfig,scale)];const artifacts={spec:absolutePath,tldr:join2(outputDirectory,`${spec.name}.tldr`),lightSvg:join2(outputDirectory,`${spec.name}.light.svg`),darkSvg:join2(outputDirectory,`${spec.name}.dark.svg`),lightPng:join2(outputDirectory,`${spec.name}.light.png`),darkPng:join2(outputDirectory,`${spec.name}.dark.png`)};await mkdir3(outputDirectory,{recursive:true});await Promise.all([atomicOperationWrite(artifacts.tldr,serializeTldr(spec,operationBuiltInConfig)),atomicOperationWrite(artifacts.lightSvg,light.svg),atomicOperationWrite(artifacts.darkSvg,dark.svg),atomicOperationWrite(artifacts.lightPng,lightPng),atomicOperationWrite(artifacts.darkPng,darkPng)]);return{artifacts,findings:lintDiagram(spec),configPath:null}}async function executeSlopcameraOperationUncoordinated(code,value,dependencies={}){const input=parseSlopcameraOperationInput(code,value);switch(code){case"slopcamera.diagram.check":{const options=input;return await checkOperationDiagram(options.path)}case"slopcamera.diagram.render":{const options=input;return await renderOperationDiagram(options)}case"slopcamera.image.vectorize":{const options=input;const result=await vectorizeImage(options.inputPath,{outputPath:options.outputPath,...options.duotone===undefined?{}:{duotone:options.duotone},...options.alphaCutoff===undefined?{}:{alphaCutoff:options.alphaCutoff},...options.timeoutMs===undefined?{}:{limits:{maxDurationMs:options.timeoutMs}},...dependencies.inheritedFileDescriptors===undefined?{}:{inheritedFileDescriptors:dependencies.inheritedFileDescriptors}});if(result.outputPath===null){throw new SlopcameraOperationError("INVALID_OPERATION_INPUT","Vectorization did not publish its required output.")}return{outputPath:result.outputPath,receipt:result.receipt}}case"slopcamera.image.generate":{const options=input;return await generateSlopcameraImageFile({...options,...dependencies.signal===undefined?{}:{signal:dependencies.signal}},dependencies)}case"slopcamera.image.icon":{const options=input;return await generateSlopcameraIcon({...options,...dependencies.signal===undefined?{}:{signal:dependencies.signal},...dependencies.inheritedFileDescriptors===undefined?{}:{inheritedFileDescriptors:dependencies.inheritedFileDescriptors}},dependencies)}case"slopcamera.image.gallery":{const options=input;return await generateSlopcameraImageGallery({...options,...dependencies.signal===undefined?{}:{signal:dependencies.signal}},dependencies)}default:throw new SlopcameraOperationError("INVALID_OPERATION","Unknown Slopcamera operation code.")}}async function executeSlopcameraOperationWithLease(code,value,lease,dependencies={}){await lease.assertOwned();const available=new Map;for(const claim of lease.claims){if(typeof claim.resource!=="string"||!/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u.test(claim.resource)||!Number.isSafeInteger(claim.amount)||claim.amount<1){throw new SlopcameraOperationError("INVALID_OPERATION","The active host-resource lease contains invalid claims.")}const total=(available.get(claim.resource)??0)+claim.amount;if(!Number.isSafeInteger(total)){throw new SlopcameraOperationError("INVALID_OPERATION","The active host-resource lease contains invalid claims.")}available.set(claim.resource,total)}const missing=slopcameraOperationHostResourceClaims(code).filter((claim)=>(available.get(claim.resource)??0)<claim.amount);if(missing.length>0){throw new SlopcameraOperationError("INVALID_OPERATION",`The active host-resource lease does not cover ${missing.map((claim)=>`${claim.resource}:${String(claim.amount)}`).join(", ")}.`)}return await executeSlopcameraOperationUncoordinated(code,value,operationDependenciesWithLease(dependencies,lease))}async function executeSlopcameraOperation(code,value,dependencies={}){const input=parseSlopcameraOperationInput(code,value);return await withSlopcameraOperationHostAdmission(code,async(lease)=>await executeSlopcameraOperationUncoordinated(code,input,operationDependenciesWithLease(dependencies,lease)),dependencies)}
38
+ export{builtInIcons,sanitizeIcon,layoutBoxContent,NebulaSans_Bold_default,NebulaSans_Book_default,resolveEdge,renderSvg,renderPng,lintDiagram,stackLayoutDefaults,StackLayoutError,resolveStackLayout,resolveDiagramSource,DiagramValidationError,parseDiagramSource,parseDiagramSpec,serializeTldr,slopcameraIconDefaultInk,slopcameraIconPanel,slopcameraIconDefaultRounds,slopcameraIconMaximumRounds,slopcameraIconSubjectMaximumBytes,slopcameraIconCritiqueDefaultModel,slopcameraIconCritiqueTimeoutMs,slopcameraIconContexts,slopcameraIconContextProfiles,iconPromptFor,iconVisualGateProblems,estimateIconStrokePixels,measureIconCandidateMetrics,extractIconLineArt,loadIconLanguageRuntime,critiqueIconRaster,renderIconPreview,writeAtomically,collectSlopcameraIconCandidates,generateSlopcameraIcon,slopcameraGalleryKinds,slopcameraGalleryAxes,slopcameraGalleryLimits,galleryCandidateId,galleryPromptFor,planSlopcameraGallery,parseSlopcameraGalleryVary,generateSlopcameraImageGallery,composeSlopcameraImageGallery,slopcameraOperationCodes,SlopcameraOperationError,slopcameraOperationRegistry,parseSlopcameraOperationInput,isSlopcameraOperationCode,slopcameraOperationHostResourceClaims,searchSlopcameraOperations,withSlopcameraOperationHostAdmission,executeSlopcameraOperationWithLease,executeSlopcameraOperation};