@promptbook/browser 0.112.0-69 → 0.112.0-70

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 +765 -233
  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 +765 -233
  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
@@ -27,7 +27,7 @@
27
27
  * @generated
28
28
  * @see https://github.com/webgptorg/promptbook
29
29
  */
30
- const PROMPTBOOK_ENGINE_VERSION = '0.112.0-69';
30
+ const PROMPTBOOK_ENGINE_VERSION = '0.112.0-70';
31
31
  /**
32
32
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
33
33
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -6237,6 +6237,184 @@
6237
6237
  context.restore();
6238
6238
  }
6239
6239
 
6240
+ /* eslint-disable no-magic-numbers */
6241
+ /**
6242
+ * Default camera distance ratio shared by the proper-3D avatar visuals.
6243
+ *
6244
+ * @private helper of the 3D avatar visuals
6245
+ */
6246
+ const DEFAULT_3D_CAMERA_DISTANCE_RATIO = 1.4;
6247
+ /**
6248
+ * Clamps one number into the provided range.
6249
+ *
6250
+ * @param value Input value.
6251
+ * @param minimumValue Inclusive lower bound.
6252
+ * @param maximumValue Inclusive upper bound.
6253
+ * @returns Clamped value.
6254
+ *
6255
+ * @private helper of the 3D avatar visuals
6256
+ */
6257
+ function clampNumber$1(value, minimumValue, maximumValue) {
6258
+ return Math.min(maximumValue, Math.max(minimumValue, value));
6259
+ }
6260
+ /**
6261
+ * Rotates one point around the local Y axis.
6262
+ *
6263
+ * @param point Source point.
6264
+ * @param angle Rotation angle in radians.
6265
+ * @returns Rotated point.
6266
+ *
6267
+ * @private helper of the 3D avatar visuals
6268
+ */
6269
+ function rotatePointAroundY(point, angle) {
6270
+ const cosine = Math.cos(angle);
6271
+ const sine = Math.sin(angle);
6272
+ return {
6273
+ x: point.x * cosine + point.z * sine,
6274
+ y: point.y,
6275
+ z: -point.x * sine + point.z * cosine,
6276
+ };
6277
+ }
6278
+ /**
6279
+ * Rotates one point around the local X axis.
6280
+ *
6281
+ * @param point Source point.
6282
+ * @param angle Rotation angle in radians.
6283
+ * @returns Rotated point.
6284
+ *
6285
+ * @private helper of the 3D avatar visuals
6286
+ */
6287
+ function rotatePointAroundX(point, angle) {
6288
+ const cosine = Math.cos(angle);
6289
+ const sine = Math.sin(angle);
6290
+ return {
6291
+ x: point.x,
6292
+ y: point.y * cosine - point.z * sine,
6293
+ z: point.y * sine + point.z * cosine,
6294
+ };
6295
+ }
6296
+ /**
6297
+ * Applies the local rotations and translation to one scene point.
6298
+ *
6299
+ * @param localPoint Point in local object space.
6300
+ * @param center Object center in scene space.
6301
+ * @param rotationX Object pitch in radians.
6302
+ * @param rotationY Object yaw in radians.
6303
+ * @returns Transformed scene-space point.
6304
+ *
6305
+ * @private helper of the 3D avatar visuals
6306
+ */
6307
+ function transformScenePoint(localPoint, center, rotationX, rotationY) {
6308
+ const yawedPoint = rotatePointAroundY(localPoint, rotationY);
6309
+ const pitchedPoint = rotatePointAroundX(yawedPoint, rotationX);
6310
+ return {
6311
+ x: center.x + pitchedPoint.x,
6312
+ y: center.y + pitchedPoint.y,
6313
+ z: center.z + pitchedPoint.z,
6314
+ };
6315
+ }
6316
+ /**
6317
+ * Projects one scene point into canvas coordinates.
6318
+ *
6319
+ * @param point Scene-space point.
6320
+ * @param size Canvas size in CSS pixels.
6321
+ * @param sceneCenterX Horizontal scene center.
6322
+ * @param sceneCenterY Vertical scene center.
6323
+ * @param cameraDistanceRatio Optional camera distance ratio.
6324
+ * @returns Projected point.
6325
+ *
6326
+ * @private helper of the 3D avatar visuals
6327
+ */
6328
+ function projectScenePoint(point, size, sceneCenterX, sceneCenterY, cameraDistanceRatio = DEFAULT_3D_CAMERA_DISTANCE_RATIO) {
6329
+ const cameraDistance = size * cameraDistanceRatio;
6330
+ const perspectiveScale = cameraDistance / Math.max(cameraDistance - point.z, cameraDistance * 0.35);
6331
+ return {
6332
+ x: sceneCenterX + point.x * perspectiveScale,
6333
+ y: sceneCenterY + point.y * perspectiveScale,
6334
+ z: point.z,
6335
+ };
6336
+ }
6337
+ /**
6338
+ * Subtracts one 3D point from another.
6339
+ *
6340
+ * @param leftPoint Left point.
6341
+ * @param rightPoint Right point.
6342
+ * @returns Difference vector.
6343
+ *
6344
+ * @private helper of the 3D avatar visuals
6345
+ */
6346
+ function subtractPoint3D(leftPoint, rightPoint) {
6347
+ return {
6348
+ x: leftPoint.x - rightPoint.x,
6349
+ y: leftPoint.y - rightPoint.y,
6350
+ z: leftPoint.z - rightPoint.z,
6351
+ };
6352
+ }
6353
+ /**
6354
+ * Computes the 3D cross product of two vectors.
6355
+ *
6356
+ * @param leftVector Left vector.
6357
+ * @param rightVector Right vector.
6358
+ * @returns Cross product.
6359
+ *
6360
+ * @private helper of the 3D avatar visuals
6361
+ */
6362
+ function crossProduct3D(leftVector, rightVector) {
6363
+ return {
6364
+ x: leftVector.y * rightVector.z - leftVector.z * rightVector.y,
6365
+ y: leftVector.z * rightVector.x - leftVector.x * rightVector.z,
6366
+ z: leftVector.x * rightVector.y - leftVector.y * rightVector.x,
6367
+ };
6368
+ }
6369
+ /**
6370
+ * Computes the 3D dot product of two vectors.
6371
+ *
6372
+ * @param leftVector Left vector.
6373
+ * @param rightVector Right vector.
6374
+ * @returns Dot product.
6375
+ *
6376
+ * @private helper of the 3D avatar visuals
6377
+ */
6378
+ function dotProduct3D(leftVector, rightVector) {
6379
+ return leftVector.x * rightVector.x + leftVector.y * rightVector.y + leftVector.z * rightVector.z;
6380
+ }
6381
+ /**
6382
+ * Normalizes one 3D vector while keeping zero vectors stable.
6383
+ *
6384
+ * @param vector Source vector.
6385
+ * @returns Normalized vector.
6386
+ *
6387
+ * @private helper of the 3D avatar visuals
6388
+ */
6389
+ function normalizeVector3(vector) {
6390
+ const length = Math.hypot(vector.x, vector.y, vector.z);
6391
+ if (length === 0) {
6392
+ return vector;
6393
+ }
6394
+ return {
6395
+ x: vector.x / length,
6396
+ y: vector.y / length,
6397
+ z: vector.z / length,
6398
+ };
6399
+ }
6400
+ /**
6401
+ * Measures the perimeter of one projected quad.
6402
+ *
6403
+ * @param corners Quad corners.
6404
+ * @returns Perimeter length.
6405
+ *
6406
+ * @private helper of the 3D avatar visuals
6407
+ */
6408
+ function getProjectedQuadPerimeter(corners) {
6409
+ let perimeter = 0;
6410
+ for (let cornerIndex = 0; cornerIndex < corners.length; cornerIndex++) {
6411
+ const currentCorner = corners[cornerIndex];
6412
+ const nextCorner = corners[(cornerIndex + 1) % corners.length];
6413
+ perimeter += Math.hypot(nextCorner.x - currentCorner.x, nextCorner.y - currentCorner.y);
6414
+ }
6415
+ return perimeter;
6416
+ }
6417
+
6240
6418
  /* eslint-disable no-magic-numbers */
6241
6419
  /**
6242
6420
  * Builds the seeded six-face texture pack used by the Minecraft-style head cuboid.
@@ -6409,18 +6587,12 @@
6409
6587
  }
6410
6588
 
6411
6589
  /* eslint-disable no-magic-numbers */
6412
- /**
6413
- * Fixed scene camera distance used for the proper-3D projection.
6414
- *
6415
- * @private helper of `minecraft2AvatarVisual`
6416
- */
6417
- const CAMERA_DISTANCE_RATIO = 1.4;
6418
6590
  /**
6419
6591
  * Shared light direction used to shade projected cuboid faces.
6420
6592
  *
6421
6593
  * @private helper of `minecraft2AvatarVisual`
6422
6594
  */
6423
- const LIGHT_DIRECTION = normalizeVector3({
6595
+ const LIGHT_DIRECTION$1 = normalizeVector3({
6424
6596
  x: 0.4,
6425
6597
  y: -0.65,
6426
6598
  z: 0.92,
@@ -6632,7 +6804,7 @@
6632
6804
  corners: projectedCorners,
6633
6805
  texture: faceDefinition.texture,
6634
6806
  averageDepth: transformedCorners.reduce((depthSum, corner) => depthSum + corner.z, 0) / transformedCorners.length,
6635
- lightIntensity: clampNumber$1(dotProduct3D(faceNormal, LIGHT_DIRECTION), -1, 1),
6807
+ lightIntensity: clampNumber$1(dotProduct3D(faceNormal, LIGHT_DIRECTION$1), -1, 1),
6636
6808
  outlineColor: cuboid.outlineColor,
6637
6809
  };
6638
6810
  });
@@ -6659,7 +6831,7 @@
6659
6831
  const endX = (columnIndex + 1) / columns;
6660
6832
  const startY = rowIndex / rows;
6661
6833
  const endY = (rowIndex + 1) / rows;
6662
- drawProjectedQuad(context, [
6834
+ drawProjectedQuad$1(context, [
6663
6835
  interpolateProjectedQuad(face.corners, startX, startY),
6664
6836
  interpolateProjectedQuad(face.corners, endX, startY),
6665
6837
  interpolateProjectedQuad(face.corners, endX, endY),
@@ -6668,10 +6840,10 @@
6668
6840
  }
6669
6841
  }
6670
6842
  if (face.lightIntensity > 0) {
6671
- drawProjectedQuad(context, face.corners, `rgba(255, 255, 255, ${0.15 * face.lightIntensity})`);
6843
+ drawProjectedQuad$1(context, face.corners, `rgba(255, 255, 255, ${0.15 * face.lightIntensity})`);
6672
6844
  }
6673
6845
  else if (face.lightIntensity < 0) {
6674
- drawProjectedQuad(context, face.corners, `rgba(0, 0, 0, ${0.22 * Math.abs(face.lightIntensity)})`);
6846
+ drawProjectedQuad$1(context, face.corners, `rgba(0, 0, 0, ${0.22 * Math.abs(face.lightIntensity)})`);
6675
6847
  }
6676
6848
  context.save();
6677
6849
  context.beginPath();
@@ -6695,7 +6867,7 @@
6695
6867
  *
6696
6868
  * @private helper of `minecraft2AvatarVisual`
6697
6869
  */
6698
- function drawProjectedQuad(context, corners, fillStyle) {
6870
+ function drawProjectedQuad$1(context, corners, fillStyle) {
6699
6871
  context.beginPath();
6700
6872
  context.moveTo(corners[0].x, corners[0].y);
6701
6873
  context.lineTo(corners[1].x, corners[1].y);
@@ -6718,193 +6890,24 @@
6718
6890
  function interpolateProjectedQuad(corners, horizontalRatio, verticalRatio) {
6719
6891
  const topPoint = interpolateProjectedPoint(corners[0], corners[1], horizontalRatio);
6720
6892
  const bottomPoint = interpolateProjectedPoint(corners[3], corners[2], horizontalRatio);
6721
- return interpolateProjectedPoint(topPoint, bottomPoint, verticalRatio);
6722
- }
6723
- /**
6724
- * Interpolates between two projected points.
6725
- *
6726
- * @param startPoint Start point.
6727
- * @param endPoint End point.
6728
- * @param ratio Interpolation ratio in the range `[0, 1]`.
6729
- * @returns Interpolated projected point.
6730
- *
6731
- * @private helper of `minecraft2AvatarVisual`
6732
- */
6733
- function interpolateProjectedPoint(startPoint, endPoint, ratio) {
6734
- return {
6735
- x: startPoint.x + (endPoint.x - startPoint.x) * ratio,
6736
- y: startPoint.y + (endPoint.y - startPoint.y) * ratio,
6737
- z: startPoint.z + (endPoint.z - startPoint.z) * ratio,
6738
- };
6739
- }
6740
- /**
6741
- * Projects one rotated scene point into canvas coordinates.
6742
- *
6743
- * @param point Scene point.
6744
- * @param size Canvas size in CSS pixels.
6745
- * @param sceneCenterX Horizontal scene center.
6746
- * @param sceneCenterY Vertical scene center.
6747
- * @returns Projected point.
6748
- *
6749
- * @private helper of `minecraft2AvatarVisual`
6750
- */
6751
- function projectScenePoint(point, size, sceneCenterX, sceneCenterY) {
6752
- const cameraDistance = size * CAMERA_DISTANCE_RATIO;
6753
- const perspectiveScale = cameraDistance / Math.max(cameraDistance - point.z, cameraDistance * 0.35);
6754
- return {
6755
- x: sceneCenterX + point.x * perspectiveScale,
6756
- y: sceneCenterY + point.y * perspectiveScale,
6757
- z: point.z,
6758
- };
6759
- }
6760
- /**
6761
- * Applies the local cuboid rotations and translation to one scene point.
6762
- *
6763
- * @param localPoint Point in cuboid-local space.
6764
- * @param center Cuboid center in scene space.
6765
- * @param rotationX Cuboid pitch in radians.
6766
- * @param rotationY Cuboid yaw in radians.
6767
- * @returns Transformed scene-space point.
6768
- *
6769
- * @private helper of `minecraft2AvatarVisual`
6770
- */
6771
- function transformScenePoint(localPoint, center, rotationX, rotationY) {
6772
- const yawedPoint = rotatePointAroundY(localPoint, rotationY);
6773
- const pitchedPoint = rotatePointAroundX(yawedPoint, rotationX);
6774
- return {
6775
- x: center.x + pitchedPoint.x,
6776
- y: center.y + pitchedPoint.y,
6777
- z: center.z + pitchedPoint.z,
6778
- };
6779
- }
6780
- /**
6781
- * Rotates one point around the local Y axis.
6782
- *
6783
- * @param point Source point.
6784
- * @param angle Rotation angle in radians.
6785
- * @returns Rotated point.
6786
- *
6787
- * @private helper of `minecraft2AvatarVisual`
6788
- */
6789
- function rotatePointAroundY(point, angle) {
6790
- const cosine = Math.cos(angle);
6791
- const sine = Math.sin(angle);
6792
- return {
6793
- x: point.x * cosine + point.z * sine,
6794
- y: point.y,
6795
- z: -point.x * sine + point.z * cosine,
6796
- };
6797
- }
6798
- /**
6799
- * Rotates one point around the local X axis.
6800
- *
6801
- * @param point Source point.
6802
- * @param angle Rotation angle in radians.
6803
- * @returns Rotated point.
6804
- *
6805
- * @private helper of `minecraft2AvatarVisual`
6806
- */
6807
- function rotatePointAroundX(point, angle) {
6808
- const cosine = Math.cos(angle);
6809
- const sine = Math.sin(angle);
6810
- return {
6811
- x: point.x,
6812
- y: point.y * cosine - point.z * sine,
6813
- z: point.y * sine + point.z * cosine,
6814
- };
6815
- }
6816
- /**
6817
- * Subtracts one 3D point from another.
6818
- *
6819
- * @param leftPoint Left point.
6820
- * @param rightPoint Right point.
6821
- * @returns Difference vector.
6822
- *
6823
- * @private helper of `minecraft2AvatarVisual`
6824
- */
6825
- function subtractPoint3D(leftPoint, rightPoint) {
6826
- return {
6827
- x: leftPoint.x - rightPoint.x,
6828
- y: leftPoint.y - rightPoint.y,
6829
- z: leftPoint.z - rightPoint.z,
6830
- };
6831
- }
6832
- /**
6833
- * Computes the 3D cross product of two vectors.
6834
- *
6835
- * @param leftVector Left vector.
6836
- * @param rightVector Right vector.
6837
- * @returns Cross product.
6838
- *
6839
- * @private helper of `minecraft2AvatarVisual`
6840
- */
6841
- function crossProduct3D(leftVector, rightVector) {
6842
- return {
6843
- x: leftVector.y * rightVector.z - leftVector.z * rightVector.y,
6844
- y: leftVector.z * rightVector.x - leftVector.x * rightVector.z,
6845
- z: leftVector.x * rightVector.y - leftVector.y * rightVector.x,
6846
- };
6847
- }
6848
- /**
6849
- * Computes the 3D dot product of two vectors.
6850
- *
6851
- * @param leftVector Left vector.
6852
- * @param rightVector Right vector.
6853
- * @returns Dot product.
6854
- *
6855
- * @private helper of `minecraft2AvatarVisual`
6856
- */
6857
- function dotProduct3D(leftVector, rightVector) {
6858
- return leftVector.x * rightVector.x + leftVector.y * rightVector.y + leftVector.z * rightVector.z;
6859
- }
6860
- /**
6861
- * Normalizes one 3D vector while keeping zero vectors stable.
6862
- *
6863
- * @param vector Source vector.
6864
- * @returns Normalized vector.
6865
- *
6866
- * @private helper of `minecraft2AvatarVisual`
6867
- */
6868
- function normalizeVector3(vector) {
6869
- const length = Math.hypot(vector.x, vector.y, vector.z);
6870
- if (length === 0) {
6871
- return vector;
6872
- }
6873
- return {
6874
- x: vector.x / length,
6875
- y: vector.y / length,
6876
- z: vector.z / length,
6877
- };
6878
- }
6879
- /**
6880
- * Clamps one number into the provided range.
6881
- *
6882
- * @param value Input value.
6883
- * @param minimumValue Inclusive lower bound.
6884
- * @param maximumValue Inclusive upper bound.
6885
- * @returns Clamped value.
6886
- *
6887
- * @private helper of `minecraft2AvatarVisual`
6888
- */
6889
- function clampNumber$1(value, minimumValue, maximumValue) {
6890
- return Math.min(maximumValue, Math.max(minimumValue, value));
6893
+ return interpolateProjectedPoint(topPoint, bottomPoint, verticalRatio);
6891
6894
  }
6892
6895
  /**
6893
- * Measures the perimeter of one projected quad.
6896
+ * Interpolates between two projected points.
6894
6897
  *
6895
- * @param corners Quad corners.
6896
- * @returns Perimeter length.
6898
+ * @param startPoint Start point.
6899
+ * @param endPoint End point.
6900
+ * @param ratio Interpolation ratio in the range `[0, 1]`.
6901
+ * @returns Interpolated projected point.
6897
6902
  *
6898
6903
  * @private helper of `minecraft2AvatarVisual`
6899
6904
  */
6900
- function getProjectedQuadPerimeter(corners) {
6901
- let perimeter = 0;
6902
- for (let cornerIndex = 0; cornerIndex < corners.length; cornerIndex++) {
6903
- const currentCorner = corners[cornerIndex];
6904
- const nextCorner = corners[(cornerIndex + 1) % corners.length];
6905
- perimeter += Math.hypot(nextCorner.x - currentCorner.x, nextCorner.y - currentCorner.y);
6906
- }
6907
- return perimeter;
6905
+ function interpolateProjectedPoint(startPoint, endPoint, ratio) {
6906
+ return {
6907
+ x: startPoint.x + (endPoint.x - startPoint.x) * ratio,
6908
+ y: startPoint.y + (endPoint.y - startPoint.y) * ratio,
6909
+ z: startPoint.z + (endPoint.z - startPoint.z) * ratio,
6910
+ };
6908
6911
  }
6909
6912
 
6910
6913
  /* eslint-disable no-magic-numbers */
@@ -7474,7 +7477,7 @@
7474
7477
  *
7475
7478
  * @private helper of `octopus3AvatarVisual`
7476
7479
  */
7477
- function formatAlphaHex(opacity) {
7480
+ function formatAlphaHex$1(opacity) {
7478
7481
  return Math.round(Math.min(1, Math.max(0, opacity)) * 255)
7479
7482
  .toString(16)
7480
7483
  .padStart(2, '0');
@@ -7853,7 +7856,7 @@
7853
7856
  context.beginPath();
7854
7857
  context.moveTo(-radiusX * 0.74, radiusY * 0.2);
7855
7858
  context.quadraticCurveTo(0, radiusY * 0.38, radiusX * 0.74, radiusY * 0.2);
7856
- context.strokeStyle = `${palette.highlight}${formatAlphaHex(eyeStyle.lowerLidOpacity)}`;
7859
+ context.strokeStyle = `${palette.highlight}${formatAlphaHex$1(eyeStyle.lowerLidOpacity)}`;
7857
7860
  context.lineWidth = radiusX * 0.08;
7858
7861
  context.lineCap = 'round';
7859
7862
  context.stroke();
@@ -7861,6 +7864,518 @@
7861
7864
  context.restore();
7862
7865
  }
7863
7866
 
7867
+ /* eslint-disable no-magic-numbers */
7868
+ /**
7869
+ * Light direction used by the organic 3D octopus shading.
7870
+ *
7871
+ * @private helper of `octopus3dAvatarVisual`
7872
+ */
7873
+ const LIGHT_DIRECTION = normalizeVector3({
7874
+ x: 0.48,
7875
+ y: -0.62,
7876
+ z: 0.94,
7877
+ });
7878
+ /**
7879
+ * Proper 3D Octopus visual built from projected organic meshes and tentacles.
7880
+ *
7881
+ * @private built-in avatar visual
7882
+ */
7883
+ const octopus3dAvatarVisual = {
7884
+ id: 'octopus3d',
7885
+ title: 'Octopus 3D',
7886
+ description: 'Proper 3D octopus portrait with a turning silhouette, expressive eyes, and depth-sorted tentacles.',
7887
+ isAnimated: true,
7888
+ supportsPointerTracking: true,
7889
+ render({ context, size, palette, createRandom, timeMs, interaction }) {
7890
+ const morphologyProfile = createOctopus3MorphologyProfile(createRandom);
7891
+ const animationRandom = createRandom('octopus3d-animation-profile');
7892
+ const eyeRandom = createRandom('octopus3d-eye-profile');
7893
+ const animationPhase = animationRandom() * Math.PI * 2;
7894
+ const sceneCenterX = size * 0.5;
7895
+ const sceneCenterY = size * 0.56;
7896
+ const bob = Math.sin(timeMs / 920 + animationPhase) * size * 0.014;
7897
+ const mantleCenter = {
7898
+ x: interaction.bodyOffsetX * size * 0.042 + size * morphologyProfile.body.centerXJitterRatio * 0.65,
7899
+ y: -size * 0.09 + interaction.bodyOffsetY * size * 0.028 + bob,
7900
+ z: interaction.intensity * size * 0.012,
7901
+ };
7902
+ const underbodyCenter = {
7903
+ x: mantleCenter.x * 0.86,
7904
+ y: mantleCenter.y + size * 0.168,
7905
+ z: mantleCenter.z - size * 0.018,
7906
+ };
7907
+ const mantleRadiusX = size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.horizontalStretch;
7908
+ const mantleRadiusY = size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.verticalStretch * 1.1;
7909
+ const mantleRadiusZ = size * morphologyProfile.body.bodyRadiusRatio * (0.9 + (morphologyProfile.body.horizontalStretch - 1) * 0.3);
7910
+ const underbodyRadiusX = mantleRadiusX * (0.9 + (morphologyProfile.tentacles.rootSpreadScale - 1) * 0.08);
7911
+ const underbodyRadiusY = mantleRadiusY * (0.44 + morphologyProfile.body.lowerDropRatio * 3.1);
7912
+ const underbodyRadiusZ = mantleRadiusZ * 0.78;
7913
+ const bodyYaw = -0.18 +
7914
+ Math.sin(timeMs / 2400 + animationPhase) * 0.05 +
7915
+ interaction.bodyOffsetX * 0.18 +
7916
+ interaction.gazeX * 0.22;
7917
+ const bodyPitch = -0.08 +
7918
+ Math.cos(timeMs / 2700 + animationPhase * 0.6) * 0.025 -
7919
+ interaction.bodyOffsetY * 0.08 -
7920
+ interaction.gazeY * 0.08;
7921
+ const headYaw = bodyYaw - 0.04 + interaction.gazeX * 0.56;
7922
+ const headPitch = bodyPitch - 0.02 - interaction.gazeY * 0.32;
7923
+ const mantlePatches = resolveVisibleEllipsoidPatches({
7924
+ center: mantleCenter,
7925
+ radiusX: mantleRadiusX,
7926
+ radiusY: mantleRadiusY,
7927
+ radiusZ: mantleRadiusZ,
7928
+ rotationX: headPitch,
7929
+ rotationY: headYaw,
7930
+ sceneCenterX,
7931
+ sceneCenterY,
7932
+ size,
7933
+ palette,
7934
+ verticalColorBias: 0,
7935
+ outlineColor: `${palette.highlight}7a`,
7936
+ });
7937
+ const underbodyPatches = resolveVisibleEllipsoidPatches({
7938
+ center: underbodyCenter,
7939
+ radiusX: underbodyRadiusX,
7940
+ radiusY: underbodyRadiusY,
7941
+ radiusZ: underbodyRadiusZ,
7942
+ rotationX: bodyPitch,
7943
+ rotationY: bodyYaw,
7944
+ sceneCenterX,
7945
+ sceneCenterY,
7946
+ size,
7947
+ palette,
7948
+ verticalColorBias: 0.18,
7949
+ outlineColor: `${palette.shadow}8f`,
7950
+ });
7951
+ const tentacleStrokes = createOctopusTentacleStrokes({
7952
+ createRandom,
7953
+ morphologyProfile,
7954
+ timeMs,
7955
+ size,
7956
+ center: underbodyCenter,
7957
+ radiusX: underbodyRadiusX,
7958
+ radiusY: underbodyRadiusY,
7959
+ radiusZ: underbodyRadiusZ,
7960
+ rotationX: bodyPitch,
7961
+ rotationY: bodyYaw,
7962
+ sceneCenterX,
7963
+ sceneCenterY,
7964
+ animationPhase,
7965
+ });
7966
+ const faceEyeSpacing = size * morphologyProfile.face.eyeSpacingRatio * 0.92;
7967
+ const faceEyeYOffset = size * morphologyProfile.face.eyeCenterYOffsetRatio - mantleRadiusY * 0.02;
7968
+ const faceEyeRadiusX = size * morphologyProfile.face.eyeRadiusXRatio * 0.82;
7969
+ const faceEyeRadiusY = faceEyeRadiusX * morphologyProfile.face.eyeHeightRatio * 0.96;
7970
+ const mouthHalfWidth = size * morphologyProfile.face.mouthWidthRatio * 0.92;
7971
+ const mouthY = size * morphologyProfile.face.mouthYOffsetRatio + mantleRadiusY * 0.08;
7972
+ drawAvatarFrame(context, size, palette);
7973
+ drawOctopus3dAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs);
7974
+ drawOctopus3dShadow(context, size, palette, interaction, timeMs);
7975
+ for (const tentacleStroke of tentacleStrokes.filter((candidateTentacleStroke) => !candidateTentacleStroke.isFrontFacing)) {
7976
+ drawTentacleStroke(context, tentacleStroke, palette);
7977
+ }
7978
+ for (const surfacePatch of [...mantlePatches, ...underbodyPatches].sort((firstSurfacePatch, secondSurfacePatch) => firstSurfacePatch.averageDepth - secondSurfacePatch.averageDepth)) {
7979
+ drawSurfacePatch(context, surfacePatch);
7980
+ }
7981
+ for (const tentacleStroke of tentacleStrokes.filter((candidateTentacleStroke) => candidateTentacleStroke.isFrontFacing)) {
7982
+ drawTentacleStroke(context, tentacleStroke, palette);
7983
+ }
7984
+ drawProjectedEye(context, {
7985
+ x: -faceEyeSpacing,
7986
+ y: faceEyeYOffset,
7987
+ z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, -faceEyeSpacing, faceEyeYOffset),
7988
+ }, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + eyeRandom() * 0.6, interaction, morphologyProfile.face.eyeStyle);
7989
+ drawProjectedEye(context, {
7990
+ x: faceEyeSpacing,
7991
+ y: faceEyeYOffset,
7992
+ z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, faceEyeSpacing, faceEyeYOffset),
7993
+ }, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.7 + eyeRandom() * 0.6, interaction, morphologyProfile.face.eyeStyle);
7994
+ drawProjectedMouth(context, [
7995
+ { x: -mouthHalfWidth, y: mouthY, z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, -mouthHalfWidth, mouthY) },
7996
+ {
7997
+ x: size * morphologyProfile.face.mouthCenterOffsetRatio,
7998
+ y: mouthY +
7999
+ size * morphologyProfile.face.mouthCurveDepthRatio * 0.38 +
8000
+ Math.sin(timeMs / 760 + animationPhase) * size * 0.01 +
8001
+ interaction.gazeY * size * 0.01,
8002
+ z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, size * morphologyProfile.face.mouthCenterOffsetRatio, mouthY),
8003
+ },
8004
+ { x: mouthHalfWidth, y: mouthY, z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, mouthHalfWidth, mouthY) },
8005
+ ], mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, palette, size);
8006
+ },
8007
+ };
8008
+ /**
8009
+ * Draws the atmospheric underwater glow behind the octopus mesh.
8010
+ *
8011
+ * @private helper of `octopus3dAvatarVisual`
8012
+ */
8013
+ function drawOctopus3dAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs) {
8014
+ 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);
8015
+ glowGradient.addColorStop(0, `${palette.highlight}5c`);
8016
+ glowGradient.addColorStop(0.36, `${palette.accent}24`);
8017
+ glowGradient.addColorStop(1, `${palette.highlight}00`);
8018
+ context.fillStyle = glowGradient;
8019
+ context.fillRect(0, 0, size, size);
8020
+ 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);
8021
+ lowerGradient.addColorStop(0, `${palette.secondary}1d`);
8022
+ lowerGradient.addColorStop(1, `${palette.secondary}00`);
8023
+ context.fillStyle = lowerGradient;
8024
+ context.fillRect(0, 0, size, size);
8025
+ }
8026
+ /**
8027
+ * Draws the soft ground shadow below the octopus.
8028
+ *
8029
+ * @private helper of `octopus3dAvatarVisual`
8030
+ */
8031
+ function drawOctopus3dShadow(context, size, palette, interaction, timeMs) {
8032
+ context.save();
8033
+ context.fillStyle = `${palette.shadow}66`;
8034
+ context.filter = `blur(${size * 0.022}px)`;
8035
+ context.beginPath();
8036
+ 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);
8037
+ context.fill();
8038
+ context.restore();
8039
+ }
8040
+ /**
8041
+ * Resolves visible projected patches for one rotated ellipsoid mesh.
8042
+ *
8043
+ * @private helper of `octopus3dAvatarVisual`
8044
+ */
8045
+ function resolveVisibleEllipsoidPatches(options) {
8046
+ const { center, radiusX, radiusY, radiusZ, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, verticalColorBias, outlineColor, } = options;
8047
+ const latitudePatchCount = 10;
8048
+ const longitudePatchCount = 18;
8049
+ const surfacePatches = [];
8050
+ for (let latitudeIndex = 0; latitudeIndex < latitudePatchCount; latitudeIndex++) {
8051
+ const startLatitude = -Math.PI / 2 + (latitudeIndex / latitudePatchCount) * Math.PI;
8052
+ const endLatitude = -Math.PI / 2 + ((latitudeIndex + 1) / latitudePatchCount) * Math.PI;
8053
+ const verticalProgress = (latitudeIndex + 0.5) / latitudePatchCount;
8054
+ for (let longitudeIndex = 0; longitudeIndex < longitudePatchCount; longitudeIndex++) {
8055
+ const startLongitude = -Math.PI + (longitudeIndex / longitudePatchCount) * Math.PI * 2;
8056
+ const endLongitude = -Math.PI + ((longitudeIndex + 1) / longitudePatchCount) * Math.PI * 2;
8057
+ const localCorners = [
8058
+ sampleEllipsoidPoint(radiusX, radiusY, radiusZ, startLatitude, startLongitude),
8059
+ sampleEllipsoidPoint(radiusX, radiusY, radiusZ, startLatitude, endLongitude),
8060
+ sampleEllipsoidPoint(radiusX, radiusY, radiusZ, endLatitude, endLongitude),
8061
+ sampleEllipsoidPoint(radiusX, radiusY, radiusZ, endLatitude, startLongitude),
8062
+ ];
8063
+ const transformedCorners = localCorners.map((localCorner) => transformScenePoint(localCorner, center, rotationX, rotationY));
8064
+ const surfaceNormal = normalizeVector3(crossProduct3D(subtractPoint3D(transformedCorners[1], transformedCorners[0]), subtractPoint3D(transformedCorners[2], transformedCorners[0])));
8065
+ if (surfaceNormal.z <= 0.01) {
8066
+ continue;
8067
+ }
8068
+ const projectedCorners = transformedCorners.map((transformedCorner) => projectScenePoint(transformedCorner, size, sceneCenterX, sceneCenterY));
8069
+ surfacePatches.push({
8070
+ corners: projectedCorners,
8071
+ averageDepth: transformedCorners.reduce((depthSum, transformedCorner) => depthSum + transformedCorner.z, 0) /
8072
+ transformedCorners.length,
8073
+ lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION), -1, 1),
8074
+ fillStyle: resolveSurfacePatchFillStyle(palette, verticalProgress + verticalColorBias),
8075
+ outlineColor,
8076
+ });
8077
+ }
8078
+ }
8079
+ return surfacePatches;
8080
+ }
8081
+ /**
8082
+ * Samples one point on an ellipsoid aligned to the local axes.
8083
+ *
8084
+ * @private helper of `octopus3dAvatarVisual`
8085
+ */
8086
+ function sampleEllipsoidPoint(radiusX, radiusY, radiusZ, latitude, longitude) {
8087
+ const cosineLatitude = Math.cos(latitude);
8088
+ return {
8089
+ x: Math.sin(longitude) * cosineLatitude * radiusX,
8090
+ y: Math.sin(latitude) * radiusY,
8091
+ z: Math.cos(longitude) * cosineLatitude * radiusZ,
8092
+ };
8093
+ }
8094
+ /**
8095
+ * Resolves one base fill tone for a surface patch across the octopus body.
8096
+ *
8097
+ * @private helper of `octopus3dAvatarVisual`
8098
+ */
8099
+ function resolveSurfacePatchFillStyle(palette, verticalProgress) {
8100
+ const clampedVerticalProgress = clampNumber$1(verticalProgress, 0, 1);
8101
+ if (clampedVerticalProgress < 0.2) {
8102
+ return palette.highlight;
8103
+ }
8104
+ if (clampedVerticalProgress < 0.45) {
8105
+ return palette.secondary;
8106
+ }
8107
+ if (clampedVerticalProgress < 0.8) {
8108
+ return palette.primary;
8109
+ }
8110
+ return `${palette.shadow}f2`;
8111
+ }
8112
+ /**
8113
+ * Draws one projected mesh patch with organic shading.
8114
+ *
8115
+ * @private helper of `octopus3dAvatarVisual`
8116
+ */
8117
+ function drawSurfacePatch(context, surfacePatch) {
8118
+ drawProjectedQuad(context, surfacePatch.corners, surfacePatch.fillStyle);
8119
+ if (surfacePatch.lightIntensity > 0) {
8120
+ drawProjectedQuad(context, surfacePatch.corners, `rgba(255, 255, 255, ${0.15 * surfacePatch.lightIntensity})`);
8121
+ }
8122
+ else if (surfacePatch.lightIntensity < 0) {
8123
+ drawProjectedQuad(context, surfacePatch.corners, `rgba(0, 0, 0, ${0.24 * Math.abs(surfacePatch.lightIntensity)})`);
8124
+ }
8125
+ context.save();
8126
+ context.beginPath();
8127
+ context.moveTo(surfacePatch.corners[0].x, surfacePatch.corners[0].y);
8128
+ for (let cornerIndex = 1; cornerIndex < surfacePatch.corners.length; cornerIndex++) {
8129
+ context.lineTo(surfacePatch.corners[cornerIndex].x, surfacePatch.corners[cornerIndex].y);
8130
+ }
8131
+ context.closePath();
8132
+ context.strokeStyle = surfacePatch.outlineColor;
8133
+ context.lineWidth = Math.max(1, getProjectedQuadPerimeter(surfacePatch.corners) * 0.0044);
8134
+ context.lineJoin = 'round';
8135
+ context.stroke();
8136
+ context.restore();
8137
+ }
8138
+ /**
8139
+ * Creates the projected 3D tentacle strokes orbiting around the lower octopus body.
8140
+ *
8141
+ * @private helper of `octopus3dAvatarVisual`
8142
+ */
8143
+ function createOctopusTentacleStrokes(options) {
8144
+ const { createRandom, morphologyProfile, timeMs, size, center, radiusX, radiusY, radiusZ, rotationX, rotationY, sceneCenterX, sceneCenterY, animationPhase, } = options;
8145
+ return Array.from({ length: morphologyProfile.tentacles.count }, (_, tentacleIndex) => {
8146
+ const tentacleRandom = createRandom(`octopus3d-tentacle-${tentacleIndex}`);
8147
+ const spreadProgress = morphologyProfile.tentacles.count === 1 ? 0.5 : tentacleIndex / (morphologyProfile.tentacles.count - 1);
8148
+ const orbitAngle = -Math.PI * 0.92 + spreadProgress * Math.PI * 1.84 + (tentacleRandom() - 0.5) * 0.16;
8149
+ const flowLength = size * (0.19 + morphologyProfile.tentacles.flowLengthScale * 0.075 + tentacleRandom() * 0.018);
8150
+ const lateralReach = size *
8151
+ (0.08 + morphologyProfile.tentacles.lateralReachScale * 0.05 + Math.abs(Math.sin(orbitAngle)) * 0.018);
8152
+ const depthReach = size * (0.028 + morphologyProfile.tentacles.tipReachScale * 0.032);
8153
+ const sway = Math.sin(timeMs / (760 + tentacleIndex * 36) + animationPhase + tentacleRandom() * Math.PI * 2);
8154
+ const anchorPoint = {
8155
+ x: Math.sin(orbitAngle) * radiusX * (0.84 + tentacleRandom() * 0.08),
8156
+ y: radiusY * (0.22 + tentacleRandom() * 0.18),
8157
+ z: Math.cos(orbitAngle) * radiusZ * (0.72 + tentacleRandom() * 0.12),
8158
+ };
8159
+ const controlPointOne = {
8160
+ x: anchorPoint.x + Math.sin(orbitAngle) * lateralReach * 0.44,
8161
+ y: anchorPoint.y + flowLength * 0.26,
8162
+ z: anchorPoint.z + Math.cos(orbitAngle) * depthReach * 0.3 + sway * size * 0.012,
8163
+ };
8164
+ const controlPointTwo = {
8165
+ x: anchorPoint.x + Math.sin(orbitAngle) * lateralReach * (0.82 + morphologyProfile.tentacles.swayScale * 0.12),
8166
+ y: anchorPoint.y + flowLength * 0.66,
8167
+ z: anchorPoint.z + Math.cos(orbitAngle) * depthReach * 0.72 + sway * size * 0.02,
8168
+ };
8169
+ const endPoint = {
8170
+ x: anchorPoint.x +
8171
+ Math.sin(orbitAngle) * lateralReach * (1.02 + morphologyProfile.tentacles.tipWidthScale * 0.12) +
8172
+ sway * size * 0.028,
8173
+ y: anchorPoint.y + flowLength,
8174
+ z: anchorPoint.z + Math.cos(orbitAngle) * depthReach + sway * size * 0.016,
8175
+ };
8176
+ const scenePoints = Array.from({ length: 12 }, (_, sampleIndex) => transformScenePoint(sampleCubicBezierPoint3D(anchorPoint, controlPointOne, controlPointTwo, endPoint, sampleIndex / 11), center, rotationX, rotationY));
8177
+ const projectedPoints = scenePoints.map((scenePoint) => projectScenePoint(scenePoint, size, sceneCenterX, sceneCenterY));
8178
+ const averageDepth = scenePoints.reduce((depthSum, scenePoint) => depthSum + scenePoint.z, 0) / scenePoints.length;
8179
+ return {
8180
+ projectedPoints,
8181
+ averageDepth,
8182
+ isFrontFacing: averageDepth >= center.z - size * 0.006,
8183
+ baseWidth: size *
8184
+ (0.019 +
8185
+ morphologyProfile.tentacles.baseWidthScale * 0.007 +
8186
+ tentacleRandom() * 0.003 +
8187
+ Math.abs(Math.sin(orbitAngle)) * 0.002),
8188
+ tipWidth: size * (0.0046 + morphologyProfile.tentacles.tipWidthScale * 0.0018),
8189
+ colorBias: tentacleRandom(),
8190
+ };
8191
+ });
8192
+ }
8193
+ /**
8194
+ * Samples one point on a cubic Bezier curve in 3D.
8195
+ *
8196
+ * @private helper of `octopus3dAvatarVisual`
8197
+ */
8198
+ function sampleCubicBezierPoint3D(startPoint, controlPointOne, controlPointTwo, endPoint, progress) {
8199
+ const inverseProgress = 1 - progress;
8200
+ return {
8201
+ x: inverseProgress * inverseProgress * inverseProgress * startPoint.x +
8202
+ 3 * inverseProgress * inverseProgress * progress * controlPointOne.x +
8203
+ 3 * inverseProgress * progress * progress * controlPointTwo.x +
8204
+ progress * progress * progress * endPoint.x,
8205
+ y: inverseProgress * inverseProgress * inverseProgress * startPoint.y +
8206
+ 3 * inverseProgress * inverseProgress * progress * controlPointOne.y +
8207
+ 3 * inverseProgress * progress * progress * controlPointTwo.y +
8208
+ progress * progress * progress * endPoint.y,
8209
+ z: inverseProgress * inverseProgress * inverseProgress * startPoint.z +
8210
+ 3 * inverseProgress * inverseProgress * progress * controlPointOne.z +
8211
+ 3 * inverseProgress * progress * progress * controlPointTwo.z +
8212
+ progress * progress * progress * endPoint.z,
8213
+ };
8214
+ }
8215
+ /**
8216
+ * Draws one projected tentacle stroke with a slim highlight ridge.
8217
+ *
8218
+ * @private helper of `octopus3dAvatarVisual`
8219
+ */
8220
+ function drawTentacleStroke(context, tentacleStroke, palette) {
8221
+ const projectedSegments = tentacleStroke.projectedPoints.length - 1;
8222
+ for (let segmentIndex = 0; segmentIndex < projectedSegments; segmentIndex++) {
8223
+ const startPoint = tentacleStroke.projectedPoints[segmentIndex];
8224
+ const endPoint = tentacleStroke.projectedPoints[segmentIndex + 1];
8225
+ const progress = segmentIndex / projectedSegments;
8226
+ const width = tentacleStroke.baseWidth + (tentacleStroke.tipWidth - tentacleStroke.baseWidth) * progress;
8227
+ context.beginPath();
8228
+ context.moveTo(startPoint.x, startPoint.y);
8229
+ context.lineTo(endPoint.x, endPoint.y);
8230
+ context.strokeStyle =
8231
+ tentacleStroke.colorBias > 0.6 ? `${palette.secondary}f0` : `${palette.primary}f0`;
8232
+ context.lineWidth = width;
8233
+ context.lineCap = 'round';
8234
+ context.stroke();
8235
+ context.beginPath();
8236
+ context.moveTo(startPoint.x, startPoint.y);
8237
+ context.lineTo(endPoint.x, endPoint.y);
8238
+ context.strokeStyle = tentacleStroke.isFrontFacing ? `${palette.highlight}80` : `${palette.highlight}40`;
8239
+ context.lineWidth = Math.max(1, width * 0.34);
8240
+ context.lineCap = 'round';
8241
+ context.stroke();
8242
+ }
8243
+ }
8244
+ /**
8245
+ * Resolves the front surface depth on an ellipsoid for one local face point.
8246
+ *
8247
+ * @private helper of `octopus3dAvatarVisual`
8248
+ */
8249
+ function resolveEllipsoidSurfaceDepth(radiusX, radiusY, radiusZ, x, y) {
8250
+ const normalizedX = x / radiusX;
8251
+ const normalizedY = y / radiusY;
8252
+ const remainingDepthRatio = Math.max(0, 1 - normalizedX * normalizedX - normalizedY * normalizedY);
8253
+ return Math.sqrt(remainingDepthRatio) * radiusZ;
8254
+ }
8255
+ /**
8256
+ * Draws one projected eye on the turned octopus mantle.
8257
+ *
8258
+ * @private helper of `octopus3dAvatarVisual`
8259
+ */
8260
+ function drawProjectedEye(context, localCenter, radiusX, radiusY, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, phase, interaction, eyeStyle) {
8261
+ const centerScenePoint = transformScenePoint(localCenter, center, rotationX, rotationY);
8262
+ if (centerScenePoint.z <= center.z) {
8263
+ return;
8264
+ }
8265
+ const horizontalScenePoint = transformScenePoint({ x: localCenter.x + radiusX, y: localCenter.y, z: localCenter.z }, center, rotationX, rotationY);
8266
+ const verticalScenePoint = transformScenePoint({ x: localCenter.x, y: localCenter.y + radiusY, z: localCenter.z }, center, rotationX, rotationY);
8267
+ const projectedCenterPoint = projectScenePoint(centerScenePoint, size, sceneCenterX, sceneCenterY);
8268
+ const projectedHorizontalPoint = projectScenePoint(horizontalScenePoint, size, sceneCenterX, sceneCenterY);
8269
+ const projectedVerticalPoint = projectScenePoint(verticalScenePoint, size, sceneCenterX, sceneCenterY);
8270
+ const projectedRadiusX = Math.hypot(projectedHorizontalPoint.x - projectedCenterPoint.x, projectedHorizontalPoint.y - projectedCenterPoint.y);
8271
+ const projectedRadiusY = Math.hypot(projectedVerticalPoint.x - projectedCenterPoint.x, projectedVerticalPoint.y - projectedCenterPoint.y);
8272
+ if (projectedRadiusX < size * 0.008 || projectedRadiusY < size * 0.008) {
8273
+ return;
8274
+ }
8275
+ const { pupilOffsetX, pupilOffsetY } = resolveOrganicEyeMotion({
8276
+ radiusX: projectedRadiusX,
8277
+ radiusY: projectedRadiusY,
8278
+ timeMs,
8279
+ phase,
8280
+ interaction,
8281
+ });
8282
+ const rotation = Math.atan2(projectedHorizontalPoint.y - projectedCenterPoint.y, projectedHorizontalPoint.x - projectedCenterPoint.x);
8283
+ context.save();
8284
+ context.translate(projectedCenterPoint.x, projectedCenterPoint.y);
8285
+ context.rotate(rotation);
8286
+ context.beginPath();
8287
+ context.ellipse(0, 0, projectedRadiusX, projectedRadiusY, 0, 0, Math.PI * 2);
8288
+ context.fillStyle = '#f8fbff';
8289
+ context.fill();
8290
+ context.clip();
8291
+ const irisGradient = context.createRadialGradient(-projectedRadiusX * 0.2, -projectedRadiusY * 0.26, projectedRadiusX * 0.05, 0, 0, projectedRadiusX * 0.92);
8292
+ irisGradient.addColorStop(0, palette.highlight);
8293
+ irisGradient.addColorStop(0.56, palette.secondary);
8294
+ irisGradient.addColorStop(1, palette.shadow);
8295
+ context.beginPath();
8296
+ context.ellipse(pupilOffsetX, pupilOffsetY, projectedRadiusX * 0.62 * eyeStyle.irisScale, projectedRadiusY * 0.72 * eyeStyle.irisScale, 0, 0, Math.PI * 2);
8297
+ context.fillStyle = irisGradient;
8298
+ context.fill();
8299
+ context.beginPath();
8300
+ context.ellipse(pupilOffsetX, pupilOffsetY, projectedRadiusX * 0.15 * eyeStyle.pupilWidthScale, projectedRadiusY * 0.48 * eyeStyle.pupilHeightScale, 0, 0, Math.PI * 2);
8301
+ context.fillStyle = palette.ink;
8302
+ context.fill();
8303
+ context.beginPath();
8304
+ context.ellipse(pupilOffsetX - projectedRadiusX * 0.22, pupilOffsetY - projectedRadiusY * 0.24, projectedRadiusX * 0.12, projectedRadiusY * 0.14, 0, 0, Math.PI * 2);
8305
+ context.fillStyle = '#ffffff';
8306
+ context.fill();
8307
+ context.restore();
8308
+ context.save();
8309
+ context.translate(projectedCenterPoint.x, projectedCenterPoint.y);
8310
+ context.rotate(rotation);
8311
+ context.beginPath();
8312
+ context.ellipse(0, 0, projectedRadiusX, projectedRadiusY, 0, 0, Math.PI * 2);
8313
+ context.strokeStyle = `${palette.shadow}cc`;
8314
+ context.lineWidth = projectedRadiusX * 0.16;
8315
+ context.stroke();
8316
+ context.beginPath();
8317
+ context.moveTo(-projectedRadiusX * 0.88, -projectedRadiusY * eyeStyle.upperLidInsetRatio);
8318
+ context.quadraticCurveTo(0, -projectedRadiusY * (eyeStyle.upperLidArchRatio - interaction.gazeY * 0.16 + interaction.intensity * 0.08), projectedRadiusX * 0.88, -projectedRadiusY * eyeStyle.upperLidInsetRatio);
8319
+ context.strokeStyle = `${palette.shadow}73`;
8320
+ context.lineWidth = projectedRadiusX * 0.14;
8321
+ context.lineCap = 'round';
8322
+ context.stroke();
8323
+ if (eyeStyle.lowerLidOpacity > 0) {
8324
+ context.beginPath();
8325
+ context.moveTo(-projectedRadiusX * 0.74, projectedRadiusY * 0.2);
8326
+ context.quadraticCurveTo(0, projectedRadiusY * 0.38, projectedRadiusX * 0.74, projectedRadiusY * 0.2);
8327
+ context.strokeStyle = `${palette.highlight}${formatAlphaHex(eyeStyle.lowerLidOpacity)}`;
8328
+ context.lineWidth = projectedRadiusX * 0.08;
8329
+ context.lineCap = 'round';
8330
+ context.stroke();
8331
+ }
8332
+ context.restore();
8333
+ }
8334
+ /**
8335
+ * Draws a subtle projected mouth arc across the front of the mantle.
8336
+ *
8337
+ * @private helper of `octopus3dAvatarVisual`
8338
+ */
8339
+ function drawProjectedMouth(context, localPoints, center, rotationX, rotationY, sceneCenterX, sceneCenterY, palette, size) {
8340
+ const scenePoints = localPoints.map((localPoint) => transformScenePoint(localPoint, center, rotationX, rotationY));
8341
+ if (scenePoints.some((scenePoint) => scenePoint.z <= center.z)) {
8342
+ return;
8343
+ }
8344
+ const projectedPoints = scenePoints.map((scenePoint) => projectScenePoint(scenePoint, size, sceneCenterX, sceneCenterY));
8345
+ context.beginPath();
8346
+ context.moveTo(projectedPoints[0].x, projectedPoints[0].y);
8347
+ context.quadraticCurveTo(projectedPoints[1].x, projectedPoints[1].y, projectedPoints[2].x, projectedPoints[2].y);
8348
+ context.strokeStyle = `${palette.ink}b8`;
8349
+ context.lineWidth = Math.max(1.1, size * 0.009);
8350
+ context.lineCap = 'round';
8351
+ context.stroke();
8352
+ }
8353
+ /**
8354
+ * Draws one filled projected quad.
8355
+ *
8356
+ * @private helper of `octopus3dAvatarVisual`
8357
+ */
8358
+ function drawProjectedQuad(context, corners, fillStyle) {
8359
+ context.beginPath();
8360
+ context.moveTo(corners[0].x, corners[0].y);
8361
+ context.lineTo(corners[1].x, corners[1].y);
8362
+ context.lineTo(corners[2].x, corners[2].y);
8363
+ context.lineTo(corners[3].x, corners[3].y);
8364
+ context.closePath();
8365
+ context.fillStyle = fillStyle;
8366
+ context.fill();
8367
+ }
8368
+ /**
8369
+ * Converts an opacity ratio into a two-digit hexadecimal alpha suffix.
8370
+ *
8371
+ * @private helper of `octopus3dAvatarVisual`
8372
+ */
8373
+ function formatAlphaHex(opacity) {
8374
+ return Math.round(clampNumber$1(opacity, 0, 1) * 255)
8375
+ .toString(16)
8376
+ .padStart(2, '0');
8377
+ }
8378
+
7864
8379
  /* eslint-disable no-magic-numbers */
7865
8380
  /**
7866
8381
  * Octopus avatar visual.
@@ -8629,6 +9144,7 @@
8629
9144
  octopusAvatarVisual,
8630
9145
  octopus2AvatarVisual,
8631
9146
  octopus3AvatarVisual,
9147
+ octopus3dAvatarVisual,
8632
9148
  asciiOctopusAvatarVisual,
8633
9149
  minecraftAvatarVisual,
8634
9150
  minecraft2AvatarVisual,
@@ -10896,16 +11412,22 @@
10896
11412
  * Each teammate is listed with its tool name, TEAM instructions, and optional profile hints.
10897
11413
  */
10898
11414
  function buildTeamSystemMessageBody(teamEntries) {
10899
- const lines = [
10900
- ...TEAM_SYSTEM_MESSAGE_GUIDANCE_LINES,
10901
- '',
10902
- ...teamEntries.map((entry, index) => {
10903
- const toolLine = `${index + 1}) ${entry.teammate.label} tool \`${entry.toolName}\``;
10904
- const detailLines = collectTeamEntryDetails(entry).map(formatTeamEntryDetailLine);
10905
- return [toolLine, ...detailLines].join('\n');
10906
- }),
10907
- ];
10908
- return lines.join('\n');
11415
+ const teammateSections = teamEntries.map((entry, index) => {
11416
+ const toolLine = `${index + 1}) ${entry.teammate.label} tool \`${entry.toolName}\``;
11417
+ const detailLines = collectTeamEntryDetails(entry).map(formatTeamEntryDetailLine);
11418
+ if (detailLines.length === 0) {
11419
+ return toolLine;
11420
+ }
11421
+ return spacetrim.spaceTrim((block) => `
11422
+ ${toolLine}
11423
+ ${block(detailLines.join('\n'))}
11424
+ `);
11425
+ });
11426
+ return spacetrim.spaceTrim((block) => `
11427
+ ${block(TEAM_SYSTEM_MESSAGE_GUIDANCE_LINES.join('\n'))}
11428
+
11429
+ ${block(teammateSections.join('\n\n'))}
11430
+ `);
10909
11431
  }
10910
11432
  /**
10911
11433
  * Builds the model-visible description for one teammate tool.
@@ -21558,11 +22080,11 @@
21558
22080
  */
21559
22081
  function pipelineJsonToString(pipelineJson) {
21560
22082
  const { title, pipelineUrl, bookVersion, description, parameters, tasks } = pipelineJson;
21561
- let pipelineString = `# ${title}`;
21562
- if (description) {
21563
- pipelineString += '\n\n';
21564
- pipelineString += description;
21565
- }
22083
+ let pipelineString = spacetrim.spaceTrim((block) => `
22084
+ # ${title}
22085
+
22086
+ ${block(description || '')}
22087
+ `);
21566
22088
  const commands = [];
21567
22089
  if (pipelineUrl) {
21568
22090
  commands.push(`PIPELINE URL ${pipelineUrl}`);
@@ -21578,20 +22100,17 @@
21578
22100
  for (const parameter of parameters.filter(({ isOutput }) => isOutput)) {
21579
22101
  commands.push(`OUTPUT PARAMETER ${taskParameterJsonToString(parameter)}`);
21580
22102
  }
21581
- pipelineString += '\n\n';
21582
- pipelineString += commands.map((command) => `- ${command}`).join('\n');
22103
+ pipelineString = spacetrim.spaceTrim((block) => `
22104
+ ${block(pipelineString)}
22105
+
22106
+ ${block(commands.map((command) => `- ${command}`).join('\n'))}
22107
+ `);
21583
22108
  for (const task of tasks) {
21584
22109
  const {
21585
22110
  /* Note: Not using:> name, */
21586
22111
  title, description,
21587
22112
  /* Note: dependentParameterNames, */
21588
22113
  jokerParameterNames: jokers, taskType, content, postprocessingFunctionNames: postprocessing, expectations, format, resultingParameterName, } = task;
21589
- pipelineString += '\n\n';
21590
- pipelineString += `## ${title}`;
21591
- if (description) {
21592
- pipelineString += '\n\n';
21593
- pipelineString += description;
21594
- }
21595
22114
  const commands = [];
21596
22115
  let contentLanguage = 'text';
21597
22116
  if (taskType === 'PROMPT_TASK') {
@@ -21654,18 +22173,23 @@
21654
22173
  commands.push(`FORMAT JSON`);
21655
22174
  }
21656
22175
  } /* not else */
21657
- pipelineString += '\n\n';
21658
- pipelineString += commands.map((command) => `- ${command}`).join('\n');
21659
- pipelineString += '\n\n';
21660
- pipelineString += '```' + contentLanguage;
21661
- pipelineString += '\n';
21662
- pipelineString += spacetrim.spaceTrim(content);
21663
- // <- TODO: [main] !!3 Escape
21664
- // <- TODO: [🧠] Some clear strategy how to spaceTrim the blocks
21665
- pipelineString += '\n';
21666
- pipelineString += '```';
21667
- pipelineString += '\n\n';
21668
- pipelineString += `\`-> {${resultingParameterName}}\``; // <- TODO: [main] !!3 If the parameter here has description, add it and use taskParameterJsonToString
22176
+ pipelineString = spacetrim.spaceTrim((block) => `
22177
+ ${block(pipelineString)}
22178
+
22179
+ ## ${title}
22180
+
22181
+ ${block(description || '')}
22182
+
22183
+ ${block(commands.map((command) => `- ${command}`).join('\n'))}
22184
+
22185
+ \`\`\`${contentLanguage}
22186
+ ${block(spacetrim.spaceTrim(content))}
22187
+ \`\`\`
22188
+
22189
+ \`-> {${resultingParameterName}}\`
22190
+ `); // <- TODO: [main] !!3 If the parameter here has description, add it and use taskParameterJsonToString
22191
+ // <- TODO: [main] !!3 Escape
22192
+ // <- TODO: [🧠] Some clear strategy how to spaceTrim the blocks
21669
22193
  }
21670
22194
  return validatePipelineString(pipelineString);
21671
22195
  }
@@ -26635,7 +27159,11 @@
26635
27159
  if (examples.length === 0) {
26636
27160
  return null;
26637
27161
  }
26638
- return `## Sample of communication with the agent:\n\n${examples.join('\n\n')}`;
27162
+ return spacetrim.spaceTrim((block) => `
27163
+ ## Sample of communication with the agent:
27164
+
27165
+ ${block(examples.join('\n\n'))}
27166
+ `);
26639
27167
  }
26640
27168
  /**
26641
27169
  * Collects the individual lines used in the example interaction section.
@@ -26672,7 +27200,11 @@
26672
27200
  function appendSystemMessageSection(requirements, section) {
26673
27201
  return {
26674
27202
  ...requirements,
26675
- systemMessage: requirements.systemMessage + '\n\n' + section,
27203
+ systemMessage: spacetrim.spaceTrim((block) => `
27204
+ ${block(requirements.systemMessage)}
27205
+
27206
+ ${block(section)}
27207
+ `),
26676
27208
  };
26677
27209
  }
26678
27210
  /**