@promptbook/remote-server 0.112.0-69 → 0.112.0-71

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 (27) hide show
  1. package/esm/index.es.js +772 -240
  2. package/esm/index.es.js.map +1 -1
  3. package/esm/src/avatars/types/AvatarVisualDefinition.d.ts +1 -1
  4. package/esm/src/avatars/visuals/avatar3dProjectionShared.d.ts +141 -0
  5. package/esm/src/avatars/visuals/octopus3dAvatarVisual.d.ts +7 -0
  6. package/esm/src/cli/cli-commands/agent/agentProjectPaths.d.ts +2 -2
  7. package/esm/src/cli/cli-commands/agent/initializeAgentRunnerCommand.d.ts +22 -0
  8. package/esm/src/cli/cli-commands/agent/runMultiple.d.ts +10 -0
  9. package/esm/src/cli/cli-commands/agent.d.ts +3 -2
  10. package/esm/src/cli/other/install.test.d.ts +1 -0
  11. package/esm/src/commitments/USE_TIMEOUT/USE_TIMEOUT.d.ts +1 -1
  12. package/esm/src/commitments/WALLET/WALLET.d.ts +1 -1
  13. package/esm/src/version.d.ts +1 -1
  14. package/package.json +2 -2
  15. package/umd/index.umd.js +772 -240
  16. package/umd/index.umd.js.map +1 -1
  17. package/umd/src/avatars/types/AvatarVisualDefinition.d.ts +1 -1
  18. package/umd/src/avatars/visuals/avatar3dProjectionShared.d.ts +141 -0
  19. package/umd/src/avatars/visuals/octopus3dAvatarVisual.d.ts +7 -0
  20. package/umd/src/cli/cli-commands/agent/agentProjectPaths.d.ts +2 -2
  21. package/umd/src/cli/cli-commands/agent/initializeAgentRunnerCommand.d.ts +22 -0
  22. package/umd/src/cli/cli-commands/agent/runMultiple.d.ts +10 -0
  23. package/umd/src/cli/cli-commands/agent.d.ts +3 -2
  24. package/umd/src/cli/other/install.test.d.ts +1 -0
  25. package/umd/src/commitments/USE_TIMEOUT/USE_TIMEOUT.d.ts +1 -1
  26. package/umd/src/commitments/WALLET/WALLET.d.ts +1 -1
  27. package/umd/src/version.d.ts +1 -1
package/umd/index.umd.js CHANGED
@@ -50,7 +50,7 @@
50
50
  * @generated
51
51
  * @see https://github.com/webgptorg/promptbook
52
52
  */
53
- const PROMPTBOOK_ENGINE_VERSION = '0.112.0-69';
53
+ const PROMPTBOOK_ENGINE_VERSION = '0.112.0-71';
54
54
  /**
55
55
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
56
56
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -3539,11 +3539,11 @@
3539
3539
  */
3540
3540
  function pipelineJsonToString(pipelineJson) {
3541
3541
  const { title, pipelineUrl, bookVersion, description, parameters, tasks } = pipelineJson;
3542
- let pipelineString = `# ${title}`;
3543
- if (description) {
3544
- pipelineString += '\n\n';
3545
- pipelineString += description;
3546
- }
3542
+ let pipelineString = _spaceTrim.spaceTrim((block) => `
3543
+ # ${title}
3544
+
3545
+ ${block(description || '')}
3546
+ `);
3547
3547
  const commands = [];
3548
3548
  if (pipelineUrl) {
3549
3549
  commands.push(`PIPELINE URL ${pipelineUrl}`);
@@ -3559,20 +3559,17 @@
3559
3559
  for (const parameter of parameters.filter(({ isOutput }) => isOutput)) {
3560
3560
  commands.push(`OUTPUT PARAMETER ${taskParameterJsonToString(parameter)}`);
3561
3561
  }
3562
- pipelineString += '\n\n';
3563
- pipelineString += commands.map((command) => `- ${command}`).join('\n');
3562
+ pipelineString = _spaceTrim.spaceTrim((block) => `
3563
+ ${block(pipelineString)}
3564
+
3565
+ ${block(commands.map((command) => `- ${command}`).join('\n'))}
3566
+ `);
3564
3567
  for (const task of tasks) {
3565
3568
  const {
3566
3569
  /* Note: Not using:> name, */
3567
3570
  title, description,
3568
3571
  /* Note: dependentParameterNames, */
3569
3572
  jokerParameterNames: jokers, taskType, content, postprocessingFunctionNames: postprocessing, expectations, format, resultingParameterName, } = task;
3570
- pipelineString += '\n\n';
3571
- pipelineString += `## ${title}`;
3572
- if (description) {
3573
- pipelineString += '\n\n';
3574
- pipelineString += description;
3575
- }
3576
3573
  const commands = [];
3577
3574
  let contentLanguage = 'text';
3578
3575
  if (taskType === 'PROMPT_TASK') {
@@ -3635,18 +3632,23 @@
3635
3632
  commands.push(`FORMAT JSON`);
3636
3633
  }
3637
3634
  } /* not else */
3638
- pipelineString += '\n\n';
3639
- pipelineString += commands.map((command) => `- ${command}`).join('\n');
3640
- pipelineString += '\n\n';
3641
- pipelineString += '```' + contentLanguage;
3642
- pipelineString += '\n';
3643
- pipelineString += _spaceTrim.spaceTrim(content);
3644
- // <- TODO: [main] !!3 Escape
3645
- // <- TODO: [🧠] Some clear strategy how to spaceTrim the blocks
3646
- pipelineString += '\n';
3647
- pipelineString += '```';
3648
- pipelineString += '\n\n';
3649
- pipelineString += `\`-> {${resultingParameterName}}\``; // <- TODO: [main] !!3 If the parameter here has description, add it and use taskParameterJsonToString
3635
+ pipelineString = _spaceTrim.spaceTrim((block) => `
3636
+ ${block(pipelineString)}
3637
+
3638
+ ## ${title}
3639
+
3640
+ ${block(description || '')}
3641
+
3642
+ ${block(commands.map((command) => `- ${command}`).join('\n'))}
3643
+
3644
+ \`\`\`${contentLanguage}
3645
+ ${block(_spaceTrim.spaceTrim(content))}
3646
+ \`\`\`
3647
+
3648
+ \`-> {${resultingParameterName}}\`
3649
+ `); // <- TODO: [main] !!3 If the parameter here has description, add it and use taskParameterJsonToString
3650
+ // <- TODO: [main] !!3 Escape
3651
+ // <- TODO: [🧠] Some clear strategy how to spaceTrim the blocks
3650
3652
  }
3651
3653
  return validatePipelineString(pipelineString);
3652
3654
  }
@@ -13284,6 +13286,184 @@
13284
13286
  context.restore();
13285
13287
  }
13286
13288
 
13289
+ /* eslint-disable no-magic-numbers */
13290
+ /**
13291
+ * Default camera distance ratio shared by the proper-3D avatar visuals.
13292
+ *
13293
+ * @private helper of the 3D avatar visuals
13294
+ */
13295
+ const DEFAULT_3D_CAMERA_DISTANCE_RATIO = 1.4;
13296
+ /**
13297
+ * Clamps one number into the provided range.
13298
+ *
13299
+ * @param value Input value.
13300
+ * @param minimumValue Inclusive lower bound.
13301
+ * @param maximumValue Inclusive upper bound.
13302
+ * @returns Clamped value.
13303
+ *
13304
+ * @private helper of the 3D avatar visuals
13305
+ */
13306
+ function clampNumber$1(value, minimumValue, maximumValue) {
13307
+ return Math.min(maximumValue, Math.max(minimumValue, value));
13308
+ }
13309
+ /**
13310
+ * Rotates one point around the local Y axis.
13311
+ *
13312
+ * @param point Source point.
13313
+ * @param angle Rotation angle in radians.
13314
+ * @returns Rotated point.
13315
+ *
13316
+ * @private helper of the 3D avatar visuals
13317
+ */
13318
+ function rotatePointAroundY(point, angle) {
13319
+ const cosine = Math.cos(angle);
13320
+ const sine = Math.sin(angle);
13321
+ return {
13322
+ x: point.x * cosine + point.z * sine,
13323
+ y: point.y,
13324
+ z: -point.x * sine + point.z * cosine,
13325
+ };
13326
+ }
13327
+ /**
13328
+ * Rotates one point around the local X axis.
13329
+ *
13330
+ * @param point Source point.
13331
+ * @param angle Rotation angle in radians.
13332
+ * @returns Rotated point.
13333
+ *
13334
+ * @private helper of the 3D avatar visuals
13335
+ */
13336
+ function rotatePointAroundX(point, angle) {
13337
+ const cosine = Math.cos(angle);
13338
+ const sine = Math.sin(angle);
13339
+ return {
13340
+ x: point.x,
13341
+ y: point.y * cosine - point.z * sine,
13342
+ z: point.y * sine + point.z * cosine,
13343
+ };
13344
+ }
13345
+ /**
13346
+ * Applies the local rotations and translation to one scene point.
13347
+ *
13348
+ * @param localPoint Point in local object space.
13349
+ * @param center Object center in scene space.
13350
+ * @param rotationX Object pitch in radians.
13351
+ * @param rotationY Object yaw in radians.
13352
+ * @returns Transformed scene-space point.
13353
+ *
13354
+ * @private helper of the 3D avatar visuals
13355
+ */
13356
+ function transformScenePoint(localPoint, center, rotationX, rotationY) {
13357
+ const yawedPoint = rotatePointAroundY(localPoint, rotationY);
13358
+ const pitchedPoint = rotatePointAroundX(yawedPoint, rotationX);
13359
+ return {
13360
+ x: center.x + pitchedPoint.x,
13361
+ y: center.y + pitchedPoint.y,
13362
+ z: center.z + pitchedPoint.z,
13363
+ };
13364
+ }
13365
+ /**
13366
+ * Projects one scene point into canvas coordinates.
13367
+ *
13368
+ * @param point Scene-space point.
13369
+ * @param size Canvas size in CSS pixels.
13370
+ * @param sceneCenterX Horizontal scene center.
13371
+ * @param sceneCenterY Vertical scene center.
13372
+ * @param cameraDistanceRatio Optional camera distance ratio.
13373
+ * @returns Projected point.
13374
+ *
13375
+ * @private helper of the 3D avatar visuals
13376
+ */
13377
+ function projectScenePoint(point, size, sceneCenterX, sceneCenterY, cameraDistanceRatio = DEFAULT_3D_CAMERA_DISTANCE_RATIO) {
13378
+ const cameraDistance = size * cameraDistanceRatio;
13379
+ const perspectiveScale = cameraDistance / Math.max(cameraDistance - point.z, cameraDistance * 0.35);
13380
+ return {
13381
+ x: sceneCenterX + point.x * perspectiveScale,
13382
+ y: sceneCenterY + point.y * perspectiveScale,
13383
+ z: point.z,
13384
+ };
13385
+ }
13386
+ /**
13387
+ * Subtracts one 3D point from another.
13388
+ *
13389
+ * @param leftPoint Left point.
13390
+ * @param rightPoint Right point.
13391
+ * @returns Difference vector.
13392
+ *
13393
+ * @private helper of the 3D avatar visuals
13394
+ */
13395
+ function subtractPoint3D(leftPoint, rightPoint) {
13396
+ return {
13397
+ x: leftPoint.x - rightPoint.x,
13398
+ y: leftPoint.y - rightPoint.y,
13399
+ z: leftPoint.z - rightPoint.z,
13400
+ };
13401
+ }
13402
+ /**
13403
+ * Computes the 3D cross product of two vectors.
13404
+ *
13405
+ * @param leftVector Left vector.
13406
+ * @param rightVector Right vector.
13407
+ * @returns Cross product.
13408
+ *
13409
+ * @private helper of the 3D avatar visuals
13410
+ */
13411
+ function crossProduct3D(leftVector, rightVector) {
13412
+ return {
13413
+ x: leftVector.y * rightVector.z - leftVector.z * rightVector.y,
13414
+ y: leftVector.z * rightVector.x - leftVector.x * rightVector.z,
13415
+ z: leftVector.x * rightVector.y - leftVector.y * rightVector.x,
13416
+ };
13417
+ }
13418
+ /**
13419
+ * Computes the 3D dot product of two vectors.
13420
+ *
13421
+ * @param leftVector Left vector.
13422
+ * @param rightVector Right vector.
13423
+ * @returns Dot product.
13424
+ *
13425
+ * @private helper of the 3D avatar visuals
13426
+ */
13427
+ function dotProduct3D(leftVector, rightVector) {
13428
+ return leftVector.x * rightVector.x + leftVector.y * rightVector.y + leftVector.z * rightVector.z;
13429
+ }
13430
+ /**
13431
+ * Normalizes one 3D vector while keeping zero vectors stable.
13432
+ *
13433
+ * @param vector Source vector.
13434
+ * @returns Normalized vector.
13435
+ *
13436
+ * @private helper of the 3D avatar visuals
13437
+ */
13438
+ function normalizeVector3(vector) {
13439
+ const length = Math.hypot(vector.x, vector.y, vector.z);
13440
+ if (length === 0) {
13441
+ return vector;
13442
+ }
13443
+ return {
13444
+ x: vector.x / length,
13445
+ y: vector.y / length,
13446
+ z: vector.z / length,
13447
+ };
13448
+ }
13449
+ /**
13450
+ * Measures the perimeter of one projected quad.
13451
+ *
13452
+ * @param corners Quad corners.
13453
+ * @returns Perimeter length.
13454
+ *
13455
+ * @private helper of the 3D avatar visuals
13456
+ */
13457
+ function getProjectedQuadPerimeter(corners) {
13458
+ let perimeter = 0;
13459
+ for (let cornerIndex = 0; cornerIndex < corners.length; cornerIndex++) {
13460
+ const currentCorner = corners[cornerIndex];
13461
+ const nextCorner = corners[(cornerIndex + 1) % corners.length];
13462
+ perimeter += Math.hypot(nextCorner.x - currentCorner.x, nextCorner.y - currentCorner.y);
13463
+ }
13464
+ return perimeter;
13465
+ }
13466
+
13287
13467
  /* eslint-disable no-magic-numbers */
13288
13468
  /**
13289
13469
  * Builds the seeded six-face texture pack used by the Minecraft-style head cuboid.
@@ -13456,18 +13636,12 @@
13456
13636
  }
13457
13637
 
13458
13638
  /* eslint-disable no-magic-numbers */
13459
- /**
13460
- * Fixed scene camera distance used for the proper-3D projection.
13461
- *
13462
- * @private helper of `minecraft2AvatarVisual`
13463
- */
13464
- const CAMERA_DISTANCE_RATIO = 1.4;
13465
13639
  /**
13466
13640
  * Shared light direction used to shade projected cuboid faces.
13467
13641
  *
13468
13642
  * @private helper of `minecraft2AvatarVisual`
13469
13643
  */
13470
- const LIGHT_DIRECTION = normalizeVector3({
13644
+ const LIGHT_DIRECTION$1 = normalizeVector3({
13471
13645
  x: 0.4,
13472
13646
  y: -0.65,
13473
13647
  z: 0.92,
@@ -13679,7 +13853,7 @@
13679
13853
  corners: projectedCorners,
13680
13854
  texture: faceDefinition.texture,
13681
13855
  averageDepth: transformedCorners.reduce((depthSum, corner) => depthSum + corner.z, 0) / transformedCorners.length,
13682
- lightIntensity: clampNumber$1(dotProduct3D(faceNormal, LIGHT_DIRECTION), -1, 1),
13856
+ lightIntensity: clampNumber$1(dotProduct3D(faceNormal, LIGHT_DIRECTION$1), -1, 1),
13683
13857
  outlineColor: cuboid.outlineColor,
13684
13858
  };
13685
13859
  });
@@ -13706,7 +13880,7 @@
13706
13880
  const endX = (columnIndex + 1) / columns;
13707
13881
  const startY = rowIndex / rows;
13708
13882
  const endY = (rowIndex + 1) / rows;
13709
- drawProjectedQuad(context, [
13883
+ drawProjectedQuad$1(context, [
13710
13884
  interpolateProjectedQuad(face.corners, startX, startY),
13711
13885
  interpolateProjectedQuad(face.corners, endX, startY),
13712
13886
  interpolateProjectedQuad(face.corners, endX, endY),
@@ -13715,10 +13889,10 @@
13715
13889
  }
13716
13890
  }
13717
13891
  if (face.lightIntensity > 0) {
13718
- drawProjectedQuad(context, face.corners, `rgba(255, 255, 255, ${0.15 * face.lightIntensity})`);
13892
+ drawProjectedQuad$1(context, face.corners, `rgba(255, 255, 255, ${0.15 * face.lightIntensity})`);
13719
13893
  }
13720
13894
  else if (face.lightIntensity < 0) {
13721
- drawProjectedQuad(context, face.corners, `rgba(0, 0, 0, ${0.22 * Math.abs(face.lightIntensity)})`);
13895
+ drawProjectedQuad$1(context, face.corners, `rgba(0, 0, 0, ${0.22 * Math.abs(face.lightIntensity)})`);
13722
13896
  }
13723
13897
  context.save();
13724
13898
  context.beginPath();
@@ -13742,7 +13916,7 @@
13742
13916
  *
13743
13917
  * @private helper of `minecraft2AvatarVisual`
13744
13918
  */
13745
- function drawProjectedQuad(context, corners, fillStyle) {
13919
+ function drawProjectedQuad$1(context, corners, fillStyle) {
13746
13920
  context.beginPath();
13747
13921
  context.moveTo(corners[0].x, corners[0].y);
13748
13922
  context.lineTo(corners[1].x, corners[1].y);
@@ -13755,203 +13929,34 @@
13755
13929
  /**
13756
13930
  * Interpolates one point inside a projected quad across both quad axes.
13757
13931
  *
13758
- * @param corners Quad corners in clockwise order.
13759
- * @param horizontalRatio Horizontal ratio in the range `[0, 1]`.
13760
- * @param verticalRatio Vertical ratio in the range `[0, 1]`.
13761
- * @returns Interpolated projected point.
13762
- *
13763
- * @private helper of `minecraft2AvatarVisual`
13764
- */
13765
- function interpolateProjectedQuad(corners, horizontalRatio, verticalRatio) {
13766
- const topPoint = interpolateProjectedPoint(corners[0], corners[1], horizontalRatio);
13767
- const bottomPoint = interpolateProjectedPoint(corners[3], corners[2], horizontalRatio);
13768
- return interpolateProjectedPoint(topPoint, bottomPoint, verticalRatio);
13769
- }
13770
- /**
13771
- * Interpolates between two projected points.
13772
- *
13773
- * @param startPoint Start point.
13774
- * @param endPoint End point.
13775
- * @param ratio Interpolation ratio in the range `[0, 1]`.
13776
- * @returns Interpolated projected point.
13777
- *
13778
- * @private helper of `minecraft2AvatarVisual`
13779
- */
13780
- function interpolateProjectedPoint(startPoint, endPoint, ratio) {
13781
- return {
13782
- x: startPoint.x + (endPoint.x - startPoint.x) * ratio,
13783
- y: startPoint.y + (endPoint.y - startPoint.y) * ratio,
13784
- z: startPoint.z + (endPoint.z - startPoint.z) * ratio,
13785
- };
13786
- }
13787
- /**
13788
- * Projects one rotated scene point into canvas coordinates.
13789
- *
13790
- * @param point Scene point.
13791
- * @param size Canvas size in CSS pixels.
13792
- * @param sceneCenterX Horizontal scene center.
13793
- * @param sceneCenterY Vertical scene center.
13794
- * @returns Projected point.
13795
- *
13796
- * @private helper of `minecraft2AvatarVisual`
13797
- */
13798
- function projectScenePoint(point, size, sceneCenterX, sceneCenterY) {
13799
- const cameraDistance = size * CAMERA_DISTANCE_RATIO;
13800
- const perspectiveScale = cameraDistance / Math.max(cameraDistance - point.z, cameraDistance * 0.35);
13801
- return {
13802
- x: sceneCenterX + point.x * perspectiveScale,
13803
- y: sceneCenterY + point.y * perspectiveScale,
13804
- z: point.z,
13805
- };
13806
- }
13807
- /**
13808
- * Applies the local cuboid rotations and translation to one scene point.
13809
- *
13810
- * @param localPoint Point in cuboid-local space.
13811
- * @param center Cuboid center in scene space.
13812
- * @param rotationX Cuboid pitch in radians.
13813
- * @param rotationY Cuboid yaw in radians.
13814
- * @returns Transformed scene-space point.
13815
- *
13816
- * @private helper of `minecraft2AvatarVisual`
13817
- */
13818
- function transformScenePoint(localPoint, center, rotationX, rotationY) {
13819
- const yawedPoint = rotatePointAroundY(localPoint, rotationY);
13820
- const pitchedPoint = rotatePointAroundX(yawedPoint, rotationX);
13821
- return {
13822
- x: center.x + pitchedPoint.x,
13823
- y: center.y + pitchedPoint.y,
13824
- z: center.z + pitchedPoint.z,
13825
- };
13826
- }
13827
- /**
13828
- * Rotates one point around the local Y axis.
13829
- *
13830
- * @param point Source point.
13831
- * @param angle Rotation angle in radians.
13832
- * @returns Rotated point.
13833
- *
13834
- * @private helper of `minecraft2AvatarVisual`
13835
- */
13836
- function rotatePointAroundY(point, angle) {
13837
- const cosine = Math.cos(angle);
13838
- const sine = Math.sin(angle);
13839
- return {
13840
- x: point.x * cosine + point.z * sine,
13841
- y: point.y,
13842
- z: -point.x * sine + point.z * cosine,
13843
- };
13844
- }
13845
- /**
13846
- * Rotates one point around the local X axis.
13847
- *
13848
- * @param point Source point.
13849
- * @param angle Rotation angle in radians.
13850
- * @returns Rotated point.
13851
- *
13852
- * @private helper of `minecraft2AvatarVisual`
13853
- */
13854
- function rotatePointAroundX(point, angle) {
13855
- const cosine = Math.cos(angle);
13856
- const sine = Math.sin(angle);
13857
- return {
13858
- x: point.x,
13859
- y: point.y * cosine - point.z * sine,
13860
- z: point.y * sine + point.z * cosine,
13861
- };
13862
- }
13863
- /**
13864
- * Subtracts one 3D point from another.
13865
- *
13866
- * @param leftPoint Left point.
13867
- * @param rightPoint Right point.
13868
- * @returns Difference vector.
13869
- *
13870
- * @private helper of `minecraft2AvatarVisual`
13871
- */
13872
- function subtractPoint3D(leftPoint, rightPoint) {
13873
- return {
13874
- x: leftPoint.x - rightPoint.x,
13875
- y: leftPoint.y - rightPoint.y,
13876
- z: leftPoint.z - rightPoint.z,
13877
- };
13878
- }
13879
- /**
13880
- * Computes the 3D cross product of two vectors.
13881
- *
13882
- * @param leftVector Left vector.
13883
- * @param rightVector Right vector.
13884
- * @returns Cross product.
13885
- *
13886
- * @private helper of `minecraft2AvatarVisual`
13887
- */
13888
- function crossProduct3D(leftVector, rightVector) {
13889
- return {
13890
- x: leftVector.y * rightVector.z - leftVector.z * rightVector.y,
13891
- y: leftVector.z * rightVector.x - leftVector.x * rightVector.z,
13892
- z: leftVector.x * rightVector.y - leftVector.y * rightVector.x,
13893
- };
13894
- }
13895
- /**
13896
- * Computes the 3D dot product of two vectors.
13897
- *
13898
- * @param leftVector Left vector.
13899
- * @param rightVector Right vector.
13900
- * @returns Dot product.
13901
- *
13902
- * @private helper of `minecraft2AvatarVisual`
13903
- */
13904
- function dotProduct3D(leftVector, rightVector) {
13905
- return leftVector.x * rightVector.x + leftVector.y * rightVector.y + leftVector.z * rightVector.z;
13906
- }
13907
- /**
13908
- * Normalizes one 3D vector while keeping zero vectors stable.
13909
- *
13910
- * @param vector Source vector.
13911
- * @returns Normalized vector.
13912
- *
13913
- * @private helper of `minecraft2AvatarVisual`
13914
- */
13915
- function normalizeVector3(vector) {
13916
- const length = Math.hypot(vector.x, vector.y, vector.z);
13917
- if (length === 0) {
13918
- return vector;
13919
- }
13920
- return {
13921
- x: vector.x / length,
13922
- y: vector.y / length,
13923
- z: vector.z / length,
13924
- };
13925
- }
13926
- /**
13927
- * Clamps one number into the provided range.
13928
- *
13929
- * @param value Input value.
13930
- * @param minimumValue Inclusive lower bound.
13931
- * @param maximumValue Inclusive upper bound.
13932
- * @returns Clamped value.
13932
+ * @param corners Quad corners in clockwise order.
13933
+ * @param horizontalRatio Horizontal ratio in the range `[0, 1]`.
13934
+ * @param verticalRatio Vertical ratio in the range `[0, 1]`.
13935
+ * @returns Interpolated projected point.
13933
13936
  *
13934
13937
  * @private helper of `minecraft2AvatarVisual`
13935
13938
  */
13936
- function clampNumber$1(value, minimumValue, maximumValue) {
13937
- return Math.min(maximumValue, Math.max(minimumValue, value));
13939
+ function interpolateProjectedQuad(corners, horizontalRatio, verticalRatio) {
13940
+ const topPoint = interpolateProjectedPoint(corners[0], corners[1], horizontalRatio);
13941
+ const bottomPoint = interpolateProjectedPoint(corners[3], corners[2], horizontalRatio);
13942
+ return interpolateProjectedPoint(topPoint, bottomPoint, verticalRatio);
13938
13943
  }
13939
13944
  /**
13940
- * Measures the perimeter of one projected quad.
13945
+ * Interpolates between two projected points.
13941
13946
  *
13942
- * @param corners Quad corners.
13943
- * @returns Perimeter length.
13947
+ * @param startPoint Start point.
13948
+ * @param endPoint End point.
13949
+ * @param ratio Interpolation ratio in the range `[0, 1]`.
13950
+ * @returns Interpolated projected point.
13944
13951
  *
13945
13952
  * @private helper of `minecraft2AvatarVisual`
13946
13953
  */
13947
- function getProjectedQuadPerimeter(corners) {
13948
- let perimeter = 0;
13949
- for (let cornerIndex = 0; cornerIndex < corners.length; cornerIndex++) {
13950
- const currentCorner = corners[cornerIndex];
13951
- const nextCorner = corners[(cornerIndex + 1) % corners.length];
13952
- perimeter += Math.hypot(nextCorner.x - currentCorner.x, nextCorner.y - currentCorner.y);
13953
- }
13954
- return perimeter;
13954
+ function interpolateProjectedPoint(startPoint, endPoint, ratio) {
13955
+ return {
13956
+ x: startPoint.x + (endPoint.x - startPoint.x) * ratio,
13957
+ y: startPoint.y + (endPoint.y - startPoint.y) * ratio,
13958
+ z: startPoint.z + (endPoint.z - startPoint.z) * ratio,
13959
+ };
13955
13960
  }
13956
13961
 
13957
13962
  /* eslint-disable no-magic-numbers */
@@ -14521,7 +14526,7 @@
14521
14526
  *
14522
14527
  * @private helper of `octopus3AvatarVisual`
14523
14528
  */
14524
- function formatAlphaHex(opacity) {
14529
+ function formatAlphaHex$1(opacity) {
14525
14530
  return Math.round(Math.min(1, Math.max(0, opacity)) * 255)
14526
14531
  .toString(16)
14527
14532
  .padStart(2, '0');
@@ -14900,7 +14905,7 @@
14900
14905
  context.beginPath();
14901
14906
  context.moveTo(-radiusX * 0.74, radiusY * 0.2);
14902
14907
  context.quadraticCurveTo(0, radiusY * 0.38, radiusX * 0.74, radiusY * 0.2);
14903
- context.strokeStyle = `${palette.highlight}${formatAlphaHex(eyeStyle.lowerLidOpacity)}`;
14908
+ context.strokeStyle = `${palette.highlight}${formatAlphaHex$1(eyeStyle.lowerLidOpacity)}`;
14904
14909
  context.lineWidth = radiusX * 0.08;
14905
14910
  context.lineCap = 'round';
14906
14911
  context.stroke();
@@ -14908,6 +14913,518 @@
14908
14913
  context.restore();
14909
14914
  }
14910
14915
 
14916
+ /* eslint-disable no-magic-numbers */
14917
+ /**
14918
+ * Light direction used by the organic 3D octopus shading.
14919
+ *
14920
+ * @private helper of `octopus3dAvatarVisual`
14921
+ */
14922
+ const LIGHT_DIRECTION = normalizeVector3({
14923
+ x: 0.48,
14924
+ y: -0.62,
14925
+ z: 0.94,
14926
+ });
14927
+ /**
14928
+ * Proper 3D Octopus visual built from projected organic meshes and tentacles.
14929
+ *
14930
+ * @private built-in avatar visual
14931
+ */
14932
+ const octopus3dAvatarVisual = {
14933
+ id: 'octopus3d',
14934
+ title: 'Octopus 3D',
14935
+ description: 'Proper 3D octopus portrait with a turning silhouette, expressive eyes, and depth-sorted tentacles.',
14936
+ isAnimated: true,
14937
+ supportsPointerTracking: true,
14938
+ render({ context, size, palette, createRandom, timeMs, interaction }) {
14939
+ const morphologyProfile = createOctopus3MorphologyProfile(createRandom);
14940
+ const animationRandom = createRandom('octopus3d-animation-profile');
14941
+ const eyeRandom = createRandom('octopus3d-eye-profile');
14942
+ const animationPhase = animationRandom() * Math.PI * 2;
14943
+ const sceneCenterX = size * 0.5;
14944
+ const sceneCenterY = size * 0.56;
14945
+ const bob = Math.sin(timeMs / 920 + animationPhase) * size * 0.014;
14946
+ const mantleCenter = {
14947
+ x: interaction.bodyOffsetX * size * 0.042 + size * morphologyProfile.body.centerXJitterRatio * 0.65,
14948
+ y: -size * 0.09 + interaction.bodyOffsetY * size * 0.028 + bob,
14949
+ z: interaction.intensity * size * 0.012,
14950
+ };
14951
+ const underbodyCenter = {
14952
+ x: mantleCenter.x * 0.86,
14953
+ y: mantleCenter.y + size * 0.168,
14954
+ z: mantleCenter.z - size * 0.018,
14955
+ };
14956
+ const mantleRadiusX = size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.horizontalStretch;
14957
+ const mantleRadiusY = size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.verticalStretch * 1.1;
14958
+ const mantleRadiusZ = size * morphologyProfile.body.bodyRadiusRatio * (0.9 + (morphologyProfile.body.horizontalStretch - 1) * 0.3);
14959
+ const underbodyRadiusX = mantleRadiusX * (0.9 + (morphologyProfile.tentacles.rootSpreadScale - 1) * 0.08);
14960
+ const underbodyRadiusY = mantleRadiusY * (0.44 + morphologyProfile.body.lowerDropRatio * 3.1);
14961
+ const underbodyRadiusZ = mantleRadiusZ * 0.78;
14962
+ const bodyYaw = -0.18 +
14963
+ Math.sin(timeMs / 2400 + animationPhase) * 0.05 +
14964
+ interaction.bodyOffsetX * 0.18 +
14965
+ interaction.gazeX * 0.22;
14966
+ const bodyPitch = -0.08 +
14967
+ Math.cos(timeMs / 2700 + animationPhase * 0.6) * 0.025 -
14968
+ interaction.bodyOffsetY * 0.08 -
14969
+ interaction.gazeY * 0.08;
14970
+ const headYaw = bodyYaw - 0.04 + interaction.gazeX * 0.56;
14971
+ const headPitch = bodyPitch - 0.02 - interaction.gazeY * 0.32;
14972
+ const mantlePatches = resolveVisibleEllipsoidPatches({
14973
+ center: mantleCenter,
14974
+ radiusX: mantleRadiusX,
14975
+ radiusY: mantleRadiusY,
14976
+ radiusZ: mantleRadiusZ,
14977
+ rotationX: headPitch,
14978
+ rotationY: headYaw,
14979
+ sceneCenterX,
14980
+ sceneCenterY,
14981
+ size,
14982
+ palette,
14983
+ verticalColorBias: 0,
14984
+ outlineColor: `${palette.highlight}7a`,
14985
+ });
14986
+ const underbodyPatches = resolveVisibleEllipsoidPatches({
14987
+ center: underbodyCenter,
14988
+ radiusX: underbodyRadiusX,
14989
+ radiusY: underbodyRadiusY,
14990
+ radiusZ: underbodyRadiusZ,
14991
+ rotationX: bodyPitch,
14992
+ rotationY: bodyYaw,
14993
+ sceneCenterX,
14994
+ sceneCenterY,
14995
+ size,
14996
+ palette,
14997
+ verticalColorBias: 0.18,
14998
+ outlineColor: `${palette.shadow}8f`,
14999
+ });
15000
+ const tentacleStrokes = createOctopusTentacleStrokes({
15001
+ createRandom,
15002
+ morphologyProfile,
15003
+ timeMs,
15004
+ size,
15005
+ center: underbodyCenter,
15006
+ radiusX: underbodyRadiusX,
15007
+ radiusY: underbodyRadiusY,
15008
+ radiusZ: underbodyRadiusZ,
15009
+ rotationX: bodyPitch,
15010
+ rotationY: bodyYaw,
15011
+ sceneCenterX,
15012
+ sceneCenterY,
15013
+ animationPhase,
15014
+ });
15015
+ const faceEyeSpacing = size * morphologyProfile.face.eyeSpacingRatio * 0.92;
15016
+ const faceEyeYOffset = size * morphologyProfile.face.eyeCenterYOffsetRatio - mantleRadiusY * 0.02;
15017
+ const faceEyeRadiusX = size * morphologyProfile.face.eyeRadiusXRatio * 0.82;
15018
+ const faceEyeRadiusY = faceEyeRadiusX * morphologyProfile.face.eyeHeightRatio * 0.96;
15019
+ const mouthHalfWidth = size * morphologyProfile.face.mouthWidthRatio * 0.92;
15020
+ const mouthY = size * morphologyProfile.face.mouthYOffsetRatio + mantleRadiusY * 0.08;
15021
+ drawAvatarFrame(context, size, palette);
15022
+ drawOctopus3dAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs);
15023
+ drawOctopus3dShadow(context, size, palette, interaction, timeMs);
15024
+ for (const tentacleStroke of tentacleStrokes.filter((candidateTentacleStroke) => !candidateTentacleStroke.isFrontFacing)) {
15025
+ drawTentacleStroke(context, tentacleStroke, palette);
15026
+ }
15027
+ for (const surfacePatch of [...mantlePatches, ...underbodyPatches].sort((firstSurfacePatch, secondSurfacePatch) => firstSurfacePatch.averageDepth - secondSurfacePatch.averageDepth)) {
15028
+ drawSurfacePatch(context, surfacePatch);
15029
+ }
15030
+ for (const tentacleStroke of tentacleStrokes.filter((candidateTentacleStroke) => candidateTentacleStroke.isFrontFacing)) {
15031
+ drawTentacleStroke(context, tentacleStroke, palette);
15032
+ }
15033
+ drawProjectedEye(context, {
15034
+ x: -faceEyeSpacing,
15035
+ y: faceEyeYOffset,
15036
+ z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, -faceEyeSpacing, faceEyeYOffset),
15037
+ }, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + eyeRandom() * 0.6, interaction, morphologyProfile.face.eyeStyle);
15038
+ drawProjectedEye(context, {
15039
+ x: faceEyeSpacing,
15040
+ y: faceEyeYOffset,
15041
+ z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, faceEyeSpacing, faceEyeYOffset),
15042
+ }, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.7 + eyeRandom() * 0.6, interaction, morphologyProfile.face.eyeStyle);
15043
+ drawProjectedMouth(context, [
15044
+ { x: -mouthHalfWidth, y: mouthY, z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, -mouthHalfWidth, mouthY) },
15045
+ {
15046
+ x: size * morphologyProfile.face.mouthCenterOffsetRatio,
15047
+ y: mouthY +
15048
+ size * morphologyProfile.face.mouthCurveDepthRatio * 0.38 +
15049
+ Math.sin(timeMs / 760 + animationPhase) * size * 0.01 +
15050
+ interaction.gazeY * size * 0.01,
15051
+ z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, size * morphologyProfile.face.mouthCenterOffsetRatio, mouthY),
15052
+ },
15053
+ { x: mouthHalfWidth, y: mouthY, z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, mouthHalfWidth, mouthY) },
15054
+ ], mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, palette, size);
15055
+ },
15056
+ };
15057
+ /**
15058
+ * Draws the atmospheric underwater glow behind the octopus mesh.
15059
+ *
15060
+ * @private helper of `octopus3dAvatarVisual`
15061
+ */
15062
+ function drawOctopus3dAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs) {
15063
+ const glowGradient = context.createRadialGradient(sceneCenterX + interaction.gazeX * size * 0.1, sceneCenterY - size * 0.2 + interaction.gazeY * size * 0.05, size * 0.04, sceneCenterX, sceneCenterY - size * 0.04, size * 0.62);
15064
+ glowGradient.addColorStop(0, `${palette.highlight}5c`);
15065
+ glowGradient.addColorStop(0.36, `${palette.accent}24`);
15066
+ glowGradient.addColorStop(1, `${palette.highlight}00`);
15067
+ context.fillStyle = glowGradient;
15068
+ context.fillRect(0, 0, size, size);
15069
+ const lowerGradient = context.createRadialGradient(sceneCenterX + Math.sin(timeMs / 1700) * size * 0.05, sceneCenterY + size * 0.23, size * 0.08, sceneCenterX, sceneCenterY + size * 0.28, size * 0.5);
15070
+ lowerGradient.addColorStop(0, `${palette.secondary}1d`);
15071
+ lowerGradient.addColorStop(1, `${palette.secondary}00`);
15072
+ context.fillStyle = lowerGradient;
15073
+ context.fillRect(0, 0, size, size);
15074
+ }
15075
+ /**
15076
+ * Draws the soft ground shadow below the octopus.
15077
+ *
15078
+ * @private helper of `octopus3dAvatarVisual`
15079
+ */
15080
+ function drawOctopus3dShadow(context, size, palette, interaction, timeMs) {
15081
+ context.save();
15082
+ context.fillStyle = `${palette.shadow}66`;
15083
+ context.filter = `blur(${size * 0.022}px)`;
15084
+ context.beginPath();
15085
+ context.ellipse(size * 0.5 + interaction.gazeX * size * 0.04, size * 0.87 + Math.sin(timeMs / 920) * size * 0.008, size * (0.18 + interaction.intensity * 0.02), size * 0.06, 0, 0, Math.PI * 2);
15086
+ context.fill();
15087
+ context.restore();
15088
+ }
15089
+ /**
15090
+ * Resolves visible projected patches for one rotated ellipsoid mesh.
15091
+ *
15092
+ * @private helper of `octopus3dAvatarVisual`
15093
+ */
15094
+ function resolveVisibleEllipsoidPatches(options) {
15095
+ const { center, radiusX, radiusY, radiusZ, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, verticalColorBias, outlineColor, } = options;
15096
+ const latitudePatchCount = 10;
15097
+ const longitudePatchCount = 18;
15098
+ const surfacePatches = [];
15099
+ for (let latitudeIndex = 0; latitudeIndex < latitudePatchCount; latitudeIndex++) {
15100
+ const startLatitude = -Math.PI / 2 + (latitudeIndex / latitudePatchCount) * Math.PI;
15101
+ const endLatitude = -Math.PI / 2 + ((latitudeIndex + 1) / latitudePatchCount) * Math.PI;
15102
+ const verticalProgress = (latitudeIndex + 0.5) / latitudePatchCount;
15103
+ for (let longitudeIndex = 0; longitudeIndex < longitudePatchCount; longitudeIndex++) {
15104
+ const startLongitude = -Math.PI + (longitudeIndex / longitudePatchCount) * Math.PI * 2;
15105
+ const endLongitude = -Math.PI + ((longitudeIndex + 1) / longitudePatchCount) * Math.PI * 2;
15106
+ const localCorners = [
15107
+ sampleEllipsoidPoint(radiusX, radiusY, radiusZ, startLatitude, startLongitude),
15108
+ sampleEllipsoidPoint(radiusX, radiusY, radiusZ, startLatitude, endLongitude),
15109
+ sampleEllipsoidPoint(radiusX, radiusY, radiusZ, endLatitude, endLongitude),
15110
+ sampleEllipsoidPoint(radiusX, radiusY, radiusZ, endLatitude, startLongitude),
15111
+ ];
15112
+ const transformedCorners = localCorners.map((localCorner) => transformScenePoint(localCorner, center, rotationX, rotationY));
15113
+ const surfaceNormal = normalizeVector3(crossProduct3D(subtractPoint3D(transformedCorners[1], transformedCorners[0]), subtractPoint3D(transformedCorners[2], transformedCorners[0])));
15114
+ if (surfaceNormal.z <= 0.01) {
15115
+ continue;
15116
+ }
15117
+ const projectedCorners = transformedCorners.map((transformedCorner) => projectScenePoint(transformedCorner, size, sceneCenterX, sceneCenterY));
15118
+ surfacePatches.push({
15119
+ corners: projectedCorners,
15120
+ averageDepth: transformedCorners.reduce((depthSum, transformedCorner) => depthSum + transformedCorner.z, 0) /
15121
+ transformedCorners.length,
15122
+ lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION), -1, 1),
15123
+ fillStyle: resolveSurfacePatchFillStyle(palette, verticalProgress + verticalColorBias),
15124
+ outlineColor,
15125
+ });
15126
+ }
15127
+ }
15128
+ return surfacePatches;
15129
+ }
15130
+ /**
15131
+ * Samples one point on an ellipsoid aligned to the local axes.
15132
+ *
15133
+ * @private helper of `octopus3dAvatarVisual`
15134
+ */
15135
+ function sampleEllipsoidPoint(radiusX, radiusY, radiusZ, latitude, longitude) {
15136
+ const cosineLatitude = Math.cos(latitude);
15137
+ return {
15138
+ x: Math.sin(longitude) * cosineLatitude * radiusX,
15139
+ y: Math.sin(latitude) * radiusY,
15140
+ z: Math.cos(longitude) * cosineLatitude * radiusZ,
15141
+ };
15142
+ }
15143
+ /**
15144
+ * Resolves one base fill tone for a surface patch across the octopus body.
15145
+ *
15146
+ * @private helper of `octopus3dAvatarVisual`
15147
+ */
15148
+ function resolveSurfacePatchFillStyle(palette, verticalProgress) {
15149
+ const clampedVerticalProgress = clampNumber$1(verticalProgress, 0, 1);
15150
+ if (clampedVerticalProgress < 0.2) {
15151
+ return palette.highlight;
15152
+ }
15153
+ if (clampedVerticalProgress < 0.45) {
15154
+ return palette.secondary;
15155
+ }
15156
+ if (clampedVerticalProgress < 0.8) {
15157
+ return palette.primary;
15158
+ }
15159
+ return `${palette.shadow}f2`;
15160
+ }
15161
+ /**
15162
+ * Draws one projected mesh patch with organic shading.
15163
+ *
15164
+ * @private helper of `octopus3dAvatarVisual`
15165
+ */
15166
+ function drawSurfacePatch(context, surfacePatch) {
15167
+ drawProjectedQuad(context, surfacePatch.corners, surfacePatch.fillStyle);
15168
+ if (surfacePatch.lightIntensity > 0) {
15169
+ drawProjectedQuad(context, surfacePatch.corners, `rgba(255, 255, 255, ${0.15 * surfacePatch.lightIntensity})`);
15170
+ }
15171
+ else if (surfacePatch.lightIntensity < 0) {
15172
+ drawProjectedQuad(context, surfacePatch.corners, `rgba(0, 0, 0, ${0.24 * Math.abs(surfacePatch.lightIntensity)})`);
15173
+ }
15174
+ context.save();
15175
+ context.beginPath();
15176
+ context.moveTo(surfacePatch.corners[0].x, surfacePatch.corners[0].y);
15177
+ for (let cornerIndex = 1; cornerIndex < surfacePatch.corners.length; cornerIndex++) {
15178
+ context.lineTo(surfacePatch.corners[cornerIndex].x, surfacePatch.corners[cornerIndex].y);
15179
+ }
15180
+ context.closePath();
15181
+ context.strokeStyle = surfacePatch.outlineColor;
15182
+ context.lineWidth = Math.max(1, getProjectedQuadPerimeter(surfacePatch.corners) * 0.0044);
15183
+ context.lineJoin = 'round';
15184
+ context.stroke();
15185
+ context.restore();
15186
+ }
15187
+ /**
15188
+ * Creates the projected 3D tentacle strokes orbiting around the lower octopus body.
15189
+ *
15190
+ * @private helper of `octopus3dAvatarVisual`
15191
+ */
15192
+ function createOctopusTentacleStrokes(options) {
15193
+ const { createRandom, morphologyProfile, timeMs, size, center, radiusX, radiusY, radiusZ, rotationX, rotationY, sceneCenterX, sceneCenterY, animationPhase, } = options;
15194
+ return Array.from({ length: morphologyProfile.tentacles.count }, (_, tentacleIndex) => {
15195
+ const tentacleRandom = createRandom(`octopus3d-tentacle-${tentacleIndex}`);
15196
+ const spreadProgress = morphologyProfile.tentacles.count === 1 ? 0.5 : tentacleIndex / (morphologyProfile.tentacles.count - 1);
15197
+ const orbitAngle = -Math.PI * 0.92 + spreadProgress * Math.PI * 1.84 + (tentacleRandom() - 0.5) * 0.16;
15198
+ const flowLength = size * (0.19 + morphologyProfile.tentacles.flowLengthScale * 0.075 + tentacleRandom() * 0.018);
15199
+ const lateralReach = size *
15200
+ (0.08 + morphologyProfile.tentacles.lateralReachScale * 0.05 + Math.abs(Math.sin(orbitAngle)) * 0.018);
15201
+ const depthReach = size * (0.028 + morphologyProfile.tentacles.tipReachScale * 0.032);
15202
+ const sway = Math.sin(timeMs / (760 + tentacleIndex * 36) + animationPhase + tentacleRandom() * Math.PI * 2);
15203
+ const anchorPoint = {
15204
+ x: Math.sin(orbitAngle) * radiusX * (0.84 + tentacleRandom() * 0.08),
15205
+ y: radiusY * (0.22 + tentacleRandom() * 0.18),
15206
+ z: Math.cos(orbitAngle) * radiusZ * (0.72 + tentacleRandom() * 0.12),
15207
+ };
15208
+ const controlPointOne = {
15209
+ x: anchorPoint.x + Math.sin(orbitAngle) * lateralReach * 0.44,
15210
+ y: anchorPoint.y + flowLength * 0.26,
15211
+ z: anchorPoint.z + Math.cos(orbitAngle) * depthReach * 0.3 + sway * size * 0.012,
15212
+ };
15213
+ const controlPointTwo = {
15214
+ x: anchorPoint.x + Math.sin(orbitAngle) * lateralReach * (0.82 + morphologyProfile.tentacles.swayScale * 0.12),
15215
+ y: anchorPoint.y + flowLength * 0.66,
15216
+ z: anchorPoint.z + Math.cos(orbitAngle) * depthReach * 0.72 + sway * size * 0.02,
15217
+ };
15218
+ const endPoint = {
15219
+ x: anchorPoint.x +
15220
+ Math.sin(orbitAngle) * lateralReach * (1.02 + morphologyProfile.tentacles.tipWidthScale * 0.12) +
15221
+ sway * size * 0.028,
15222
+ y: anchorPoint.y + flowLength,
15223
+ z: anchorPoint.z + Math.cos(orbitAngle) * depthReach + sway * size * 0.016,
15224
+ };
15225
+ const scenePoints = Array.from({ length: 12 }, (_, sampleIndex) => transformScenePoint(sampleCubicBezierPoint3D(anchorPoint, controlPointOne, controlPointTwo, endPoint, sampleIndex / 11), center, rotationX, rotationY));
15226
+ const projectedPoints = scenePoints.map((scenePoint) => projectScenePoint(scenePoint, size, sceneCenterX, sceneCenterY));
15227
+ const averageDepth = scenePoints.reduce((depthSum, scenePoint) => depthSum + scenePoint.z, 0) / scenePoints.length;
15228
+ return {
15229
+ projectedPoints,
15230
+ averageDepth,
15231
+ isFrontFacing: averageDepth >= center.z - size * 0.006,
15232
+ baseWidth: size *
15233
+ (0.019 +
15234
+ morphologyProfile.tentacles.baseWidthScale * 0.007 +
15235
+ tentacleRandom() * 0.003 +
15236
+ Math.abs(Math.sin(orbitAngle)) * 0.002),
15237
+ tipWidth: size * (0.0046 + morphologyProfile.tentacles.tipWidthScale * 0.0018),
15238
+ colorBias: tentacleRandom(),
15239
+ };
15240
+ });
15241
+ }
15242
+ /**
15243
+ * Samples one point on a cubic Bezier curve in 3D.
15244
+ *
15245
+ * @private helper of `octopus3dAvatarVisual`
15246
+ */
15247
+ function sampleCubicBezierPoint3D(startPoint, controlPointOne, controlPointTwo, endPoint, progress) {
15248
+ const inverseProgress = 1 - progress;
15249
+ return {
15250
+ x: inverseProgress * inverseProgress * inverseProgress * startPoint.x +
15251
+ 3 * inverseProgress * inverseProgress * progress * controlPointOne.x +
15252
+ 3 * inverseProgress * progress * progress * controlPointTwo.x +
15253
+ progress * progress * progress * endPoint.x,
15254
+ y: inverseProgress * inverseProgress * inverseProgress * startPoint.y +
15255
+ 3 * inverseProgress * inverseProgress * progress * controlPointOne.y +
15256
+ 3 * inverseProgress * progress * progress * controlPointTwo.y +
15257
+ progress * progress * progress * endPoint.y,
15258
+ z: inverseProgress * inverseProgress * inverseProgress * startPoint.z +
15259
+ 3 * inverseProgress * inverseProgress * progress * controlPointOne.z +
15260
+ 3 * inverseProgress * progress * progress * controlPointTwo.z +
15261
+ progress * progress * progress * endPoint.z,
15262
+ };
15263
+ }
15264
+ /**
15265
+ * Draws one projected tentacle stroke with a slim highlight ridge.
15266
+ *
15267
+ * @private helper of `octopus3dAvatarVisual`
15268
+ */
15269
+ function drawTentacleStroke(context, tentacleStroke, palette) {
15270
+ const projectedSegments = tentacleStroke.projectedPoints.length - 1;
15271
+ for (let segmentIndex = 0; segmentIndex < projectedSegments; segmentIndex++) {
15272
+ const startPoint = tentacleStroke.projectedPoints[segmentIndex];
15273
+ const endPoint = tentacleStroke.projectedPoints[segmentIndex + 1];
15274
+ const progress = segmentIndex / projectedSegments;
15275
+ const width = tentacleStroke.baseWidth + (tentacleStroke.tipWidth - tentacleStroke.baseWidth) * progress;
15276
+ context.beginPath();
15277
+ context.moveTo(startPoint.x, startPoint.y);
15278
+ context.lineTo(endPoint.x, endPoint.y);
15279
+ context.strokeStyle =
15280
+ tentacleStroke.colorBias > 0.6 ? `${palette.secondary}f0` : `${palette.primary}f0`;
15281
+ context.lineWidth = width;
15282
+ context.lineCap = 'round';
15283
+ context.stroke();
15284
+ context.beginPath();
15285
+ context.moveTo(startPoint.x, startPoint.y);
15286
+ context.lineTo(endPoint.x, endPoint.y);
15287
+ context.strokeStyle = tentacleStroke.isFrontFacing ? `${palette.highlight}80` : `${palette.highlight}40`;
15288
+ context.lineWidth = Math.max(1, width * 0.34);
15289
+ context.lineCap = 'round';
15290
+ context.stroke();
15291
+ }
15292
+ }
15293
+ /**
15294
+ * Resolves the front surface depth on an ellipsoid for one local face point.
15295
+ *
15296
+ * @private helper of `octopus3dAvatarVisual`
15297
+ */
15298
+ function resolveEllipsoidSurfaceDepth(radiusX, radiusY, radiusZ, x, y) {
15299
+ const normalizedX = x / radiusX;
15300
+ const normalizedY = y / radiusY;
15301
+ const remainingDepthRatio = Math.max(0, 1 - normalizedX * normalizedX - normalizedY * normalizedY);
15302
+ return Math.sqrt(remainingDepthRatio) * radiusZ;
15303
+ }
15304
+ /**
15305
+ * Draws one projected eye on the turned octopus mantle.
15306
+ *
15307
+ * @private helper of `octopus3dAvatarVisual`
15308
+ */
15309
+ function drawProjectedEye(context, localCenter, radiusX, radiusY, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, phase, interaction, eyeStyle) {
15310
+ const centerScenePoint = transformScenePoint(localCenter, center, rotationX, rotationY);
15311
+ if (centerScenePoint.z <= center.z) {
15312
+ return;
15313
+ }
15314
+ const horizontalScenePoint = transformScenePoint({ x: localCenter.x + radiusX, y: localCenter.y, z: localCenter.z }, center, rotationX, rotationY);
15315
+ const verticalScenePoint = transformScenePoint({ x: localCenter.x, y: localCenter.y + radiusY, z: localCenter.z }, center, rotationX, rotationY);
15316
+ const projectedCenterPoint = projectScenePoint(centerScenePoint, size, sceneCenterX, sceneCenterY);
15317
+ const projectedHorizontalPoint = projectScenePoint(horizontalScenePoint, size, sceneCenterX, sceneCenterY);
15318
+ const projectedVerticalPoint = projectScenePoint(verticalScenePoint, size, sceneCenterX, sceneCenterY);
15319
+ const projectedRadiusX = Math.hypot(projectedHorizontalPoint.x - projectedCenterPoint.x, projectedHorizontalPoint.y - projectedCenterPoint.y);
15320
+ const projectedRadiusY = Math.hypot(projectedVerticalPoint.x - projectedCenterPoint.x, projectedVerticalPoint.y - projectedCenterPoint.y);
15321
+ if (projectedRadiusX < size * 0.008 || projectedRadiusY < size * 0.008) {
15322
+ return;
15323
+ }
15324
+ const { pupilOffsetX, pupilOffsetY } = resolveOrganicEyeMotion({
15325
+ radiusX: projectedRadiusX,
15326
+ radiusY: projectedRadiusY,
15327
+ timeMs,
15328
+ phase,
15329
+ interaction,
15330
+ });
15331
+ const rotation = Math.atan2(projectedHorizontalPoint.y - projectedCenterPoint.y, projectedHorizontalPoint.x - projectedCenterPoint.x);
15332
+ context.save();
15333
+ context.translate(projectedCenterPoint.x, projectedCenterPoint.y);
15334
+ context.rotate(rotation);
15335
+ context.beginPath();
15336
+ context.ellipse(0, 0, projectedRadiusX, projectedRadiusY, 0, 0, Math.PI * 2);
15337
+ context.fillStyle = '#f8fbff';
15338
+ context.fill();
15339
+ context.clip();
15340
+ const irisGradient = context.createRadialGradient(-projectedRadiusX * 0.2, -projectedRadiusY * 0.26, projectedRadiusX * 0.05, 0, 0, projectedRadiusX * 0.92);
15341
+ irisGradient.addColorStop(0, palette.highlight);
15342
+ irisGradient.addColorStop(0.56, palette.secondary);
15343
+ irisGradient.addColorStop(1, palette.shadow);
15344
+ context.beginPath();
15345
+ context.ellipse(pupilOffsetX, pupilOffsetY, projectedRadiusX * 0.62 * eyeStyle.irisScale, projectedRadiusY * 0.72 * eyeStyle.irisScale, 0, 0, Math.PI * 2);
15346
+ context.fillStyle = irisGradient;
15347
+ context.fill();
15348
+ context.beginPath();
15349
+ context.ellipse(pupilOffsetX, pupilOffsetY, projectedRadiusX * 0.15 * eyeStyle.pupilWidthScale, projectedRadiusY * 0.48 * eyeStyle.pupilHeightScale, 0, 0, Math.PI * 2);
15350
+ context.fillStyle = palette.ink;
15351
+ context.fill();
15352
+ context.beginPath();
15353
+ context.ellipse(pupilOffsetX - projectedRadiusX * 0.22, pupilOffsetY - projectedRadiusY * 0.24, projectedRadiusX * 0.12, projectedRadiusY * 0.14, 0, 0, Math.PI * 2);
15354
+ context.fillStyle = '#ffffff';
15355
+ context.fill();
15356
+ context.restore();
15357
+ context.save();
15358
+ context.translate(projectedCenterPoint.x, projectedCenterPoint.y);
15359
+ context.rotate(rotation);
15360
+ context.beginPath();
15361
+ context.ellipse(0, 0, projectedRadiusX, projectedRadiusY, 0, 0, Math.PI * 2);
15362
+ context.strokeStyle = `${palette.shadow}cc`;
15363
+ context.lineWidth = projectedRadiusX * 0.16;
15364
+ context.stroke();
15365
+ context.beginPath();
15366
+ context.moveTo(-projectedRadiusX * 0.88, -projectedRadiusY * eyeStyle.upperLidInsetRatio);
15367
+ context.quadraticCurveTo(0, -projectedRadiusY * (eyeStyle.upperLidArchRatio - interaction.gazeY * 0.16 + interaction.intensity * 0.08), projectedRadiusX * 0.88, -projectedRadiusY * eyeStyle.upperLidInsetRatio);
15368
+ context.strokeStyle = `${palette.shadow}73`;
15369
+ context.lineWidth = projectedRadiusX * 0.14;
15370
+ context.lineCap = 'round';
15371
+ context.stroke();
15372
+ if (eyeStyle.lowerLidOpacity > 0) {
15373
+ context.beginPath();
15374
+ context.moveTo(-projectedRadiusX * 0.74, projectedRadiusY * 0.2);
15375
+ context.quadraticCurveTo(0, projectedRadiusY * 0.38, projectedRadiusX * 0.74, projectedRadiusY * 0.2);
15376
+ context.strokeStyle = `${palette.highlight}${formatAlphaHex(eyeStyle.lowerLidOpacity)}`;
15377
+ context.lineWidth = projectedRadiusX * 0.08;
15378
+ context.lineCap = 'round';
15379
+ context.stroke();
15380
+ }
15381
+ context.restore();
15382
+ }
15383
+ /**
15384
+ * Draws a subtle projected mouth arc across the front of the mantle.
15385
+ *
15386
+ * @private helper of `octopus3dAvatarVisual`
15387
+ */
15388
+ function drawProjectedMouth(context, localPoints, center, rotationX, rotationY, sceneCenterX, sceneCenterY, palette, size) {
15389
+ const scenePoints = localPoints.map((localPoint) => transformScenePoint(localPoint, center, rotationX, rotationY));
15390
+ if (scenePoints.some((scenePoint) => scenePoint.z <= center.z)) {
15391
+ return;
15392
+ }
15393
+ const projectedPoints = scenePoints.map((scenePoint) => projectScenePoint(scenePoint, size, sceneCenterX, sceneCenterY));
15394
+ context.beginPath();
15395
+ context.moveTo(projectedPoints[0].x, projectedPoints[0].y);
15396
+ context.quadraticCurveTo(projectedPoints[1].x, projectedPoints[1].y, projectedPoints[2].x, projectedPoints[2].y);
15397
+ context.strokeStyle = `${palette.ink}b8`;
15398
+ context.lineWidth = Math.max(1.1, size * 0.009);
15399
+ context.lineCap = 'round';
15400
+ context.stroke();
15401
+ }
15402
+ /**
15403
+ * Draws one filled projected quad.
15404
+ *
15405
+ * @private helper of `octopus3dAvatarVisual`
15406
+ */
15407
+ function drawProjectedQuad(context, corners, fillStyle) {
15408
+ context.beginPath();
15409
+ context.moveTo(corners[0].x, corners[0].y);
15410
+ context.lineTo(corners[1].x, corners[1].y);
15411
+ context.lineTo(corners[2].x, corners[2].y);
15412
+ context.lineTo(corners[3].x, corners[3].y);
15413
+ context.closePath();
15414
+ context.fillStyle = fillStyle;
15415
+ context.fill();
15416
+ }
15417
+ /**
15418
+ * Converts an opacity ratio into a two-digit hexadecimal alpha suffix.
15419
+ *
15420
+ * @private helper of `octopus3dAvatarVisual`
15421
+ */
15422
+ function formatAlphaHex(opacity) {
15423
+ return Math.round(clampNumber$1(opacity, 0, 1) * 255)
15424
+ .toString(16)
15425
+ .padStart(2, '0');
15426
+ }
15427
+
14911
15428
  /* eslint-disable no-magic-numbers */
14912
15429
  /**
14913
15430
  * Octopus avatar visual.
@@ -15676,6 +16193,7 @@
15676
16193
  octopusAvatarVisual,
15677
16194
  octopus2AvatarVisual,
15678
16195
  octopus3AvatarVisual,
16196
+ octopus3dAvatarVisual,
15679
16197
  asciiOctopusAvatarVisual,
15680
16198
  minecraftAvatarVisual,
15681
16199
  minecraft2AvatarVisual,
@@ -17869,16 +18387,22 @@
17869
18387
  * Each teammate is listed with its tool name, TEAM instructions, and optional profile hints.
17870
18388
  */
17871
18389
  function buildTeamSystemMessageBody(teamEntries) {
17872
- const lines = [
17873
- ...TEAM_SYSTEM_MESSAGE_GUIDANCE_LINES,
17874
- '',
17875
- ...teamEntries.map((entry, index) => {
17876
- const toolLine = `${index + 1}) ${entry.teammate.label} tool \`${entry.toolName}\``;
17877
- const detailLines = collectTeamEntryDetails(entry).map(formatTeamEntryDetailLine);
17878
- return [toolLine, ...detailLines].join('\n');
17879
- }),
17880
- ];
17881
- return lines.join('\n');
18390
+ const teammateSections = teamEntries.map((entry, index) => {
18391
+ const toolLine = `${index + 1}) ${entry.teammate.label} tool \`${entry.toolName}\``;
18392
+ const detailLines = collectTeamEntryDetails(entry).map(formatTeamEntryDetailLine);
18393
+ if (detailLines.length === 0) {
18394
+ return toolLine;
18395
+ }
18396
+ return _spaceTrim.spaceTrim((block) => `
18397
+ ${toolLine}
18398
+ ${block(detailLines.join('\n'))}
18399
+ `);
18400
+ });
18401
+ return _spaceTrim.spaceTrim((block) => `
18402
+ ${block(TEAM_SYSTEM_MESSAGE_GUIDANCE_LINES.join('\n'))}
18403
+
18404
+ ${block(teammateSections.join('\n\n'))}
18405
+ `);
17882
18406
  }
17883
18407
  /**
17884
18408
  * Builds the model-visible description for one teammate tool.
@@ -31006,7 +31530,11 @@
31006
31530
  if (examples.length === 0) {
31007
31531
  return null;
31008
31532
  }
31009
- return `## Sample of communication with the agent:\n\n${examples.join('\n\n')}`;
31533
+ return _spaceTrim.spaceTrim((block) => `
31534
+ ## Sample of communication with the agent:
31535
+
31536
+ ${block(examples.join('\n\n'))}
31537
+ `);
31010
31538
  }
31011
31539
  /**
31012
31540
  * Collects the individual lines used in the example interaction section.
@@ -31043,7 +31571,11 @@
31043
31571
  function appendSystemMessageSection(requirements, section) {
31044
31572
  return {
31045
31573
  ...requirements,
31046
- systemMessage: requirements.systemMessage + '\n\n' + section,
31574
+ systemMessage: _spaceTrim.spaceTrim((block) => `
31575
+ ${block(requirements.systemMessage)}
31576
+
31577
+ ${block(section)}
31578
+ `),
31047
31579
  };
31048
31580
  }
31049
31581
  /**