@promptbook/browser 0.112.0-117 → 0.112.0-119

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 (37) hide show
  1. package/esm/index.es.js +197 -44
  2. package/esm/index.es.js.map +1 -1
  3. package/esm/src/_packages/node.index.d.ts +10 -0
  4. package/esm/src/book-3.0/BookNodeAgentSource.d.ts +1 -1
  5. package/esm/src/book-3.0/CliAgent.d.ts +7 -2
  6. package/esm/src/book-3.0/cliAgentEnv.d.ts +33 -0
  7. package/esm/src/book-components/BookEditor/BookEditor.d.ts +1 -1
  8. package/esm/src/book-components/BookEditor/BookEditorForClient.d.ts +1 -1
  9. package/esm/src/book-components/Chat/Chat/CitationIframePreview.d.ts +20 -0
  10. package/esm/src/book-components/_common/Dropdown/Dropdown.d.ts +1 -1
  11. package/esm/src/book-components/_common/MenuHoisting/MenuHoistingContext.d.ts +1 -1
  12. package/esm/src/book-components/_common/Modal/Modal.d.ts +1 -1
  13. package/esm/src/book-components/icons/AboutIcon.d.ts +1 -1
  14. package/esm/src/book-components/icons/DownloadIcon.d.ts +1 -1
  15. package/esm/src/book-components/icons/ExitFullscreenIcon.d.ts +1 -1
  16. package/esm/src/book-components/icons/FullscreenIcon.d.ts +1 -1
  17. package/esm/src/cli/cli-commands/common/promptRunnerCliOptions.d.ts +2 -18
  18. package/esm/src/version.d.ts +1 -1
  19. package/package.json +2 -2
  20. package/umd/index.umd.js +197 -44
  21. package/umd/index.umd.js.map +1 -1
  22. package/umd/src/_packages/node.index.d.ts +10 -0
  23. package/umd/src/book-3.0/BookNodeAgentSource.d.ts +1 -1
  24. package/umd/src/book-3.0/CliAgent.d.ts +7 -2
  25. package/umd/src/book-3.0/cliAgentEnv.d.ts +33 -0
  26. package/umd/src/book-components/BookEditor/BookEditor.d.ts +1 -1
  27. package/umd/src/book-components/BookEditor/BookEditorForClient.d.ts +1 -1
  28. package/umd/src/book-components/Chat/Chat/CitationIframePreview.d.ts +20 -0
  29. package/umd/src/book-components/_common/Dropdown/Dropdown.d.ts +1 -1
  30. package/umd/src/book-components/_common/MenuHoisting/MenuHoistingContext.d.ts +1 -1
  31. package/umd/src/book-components/_common/Modal/Modal.d.ts +1 -1
  32. package/umd/src/book-components/icons/AboutIcon.d.ts +1 -1
  33. package/umd/src/book-components/icons/DownloadIcon.d.ts +1 -1
  34. package/umd/src/book-components/icons/ExitFullscreenIcon.d.ts +1 -1
  35. package/umd/src/book-components/icons/FullscreenIcon.d.ts +1 -1
  36. package/umd/src/cli/cli-commands/common/promptRunnerCliOptions.d.ts +2 -18
  37. package/umd/src/version.d.ts +1 -1
package/esm/index.es.js CHANGED
@@ -28,7 +28,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
28
28
  * @generated
29
29
  * @see https://github.com/webgptorg/promptbook
30
30
  */
31
- const PROMPTBOOK_ENGINE_VERSION = '0.112.0-117';
31
+ const PROMPTBOOK_ENGINE_VERSION = '0.112.0-119';
32
32
  /**
33
33
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
34
34
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -6160,21 +6160,22 @@ function getPointBounds(points) {
6160
6160
  * @private helper of `fractalAvatarVisual`
6161
6161
  */
6162
6162
  function drawDragonCurveLayer(context, points, options) {
6163
- const { size, primaryColor, secondaryColor, tertiaryColor, shadowColor, strokeWidth, timeMs, layerIndex } = options;
6163
+ const { primaryColor, secondaryColor, tertiaryColor, shadowColor, strokeWidth, timeMs, layerIndex } = options;
6164
6164
  const firstPoint = points[0];
6165
6165
  const lastPoint = points[points.length - 1];
6166
6166
  const ribbonGradient = context.createLinearGradient(firstPoint.x, firstPoint.y, lastPoint.x, lastPoint.y);
6167
6167
  ribbonGradient.addColorStop(0, `${primaryColor}f2`);
6168
6168
  ribbonGradient.addColorStop(0.5, `${secondaryColor}e6`);
6169
6169
  ribbonGradient.addColorStop(1, `${tertiaryColor}f2`);
6170
+ // Approximate the blurred shadow stroke with a wider semi-transparent stroke instead of
6171
+ // context.filter blur, which triggers a costly software rasterization pass every frame.
6170
6172
  context.save();
6171
6173
  context.beginPath();
6172
6174
  tracePolyline(context, points);
6173
- context.strokeStyle = `${shadowColor}82`;
6174
- context.lineWidth = strokeWidth * 1.8;
6175
+ context.strokeStyle = `${shadowColor}48`;
6176
+ context.lineWidth = strokeWidth * 4.5;
6175
6177
  context.lineJoin = 'round';
6176
6178
  context.lineCap = 'round';
6177
- context.filter = `blur(${size * 0.022}px)`;
6178
6179
  context.stroke();
6179
6180
  context.restore();
6180
6181
  context.beginPath();
@@ -6758,11 +6759,23 @@ function drawMinecraftBackdrop(context, size, palette, sceneCenterX, spotlightY,
6758
6759
  * @private helper of `minecraft2AvatarVisual`
6759
6760
  */
6760
6761
  function drawMinecraftShadow(context, size, palette, interaction, timeMs) {
6762
+ const cx = size * 0.5 + interaction.gazeX * size * 0.03;
6763
+ const cy = size * 0.85 + Math.sin(timeMs / 880) * size * 0.01;
6764
+ const rx = size * (0.16 + interaction.intensity * 0.015);
6765
+ const ry = size * 0.055;
6766
+ // Radial gradient approximates the blurry ellipse shadow without context.filter blur.
6761
6767
  context.save();
6762
- context.fillStyle = `${palette.shadow}66`;
6763
- context.filter = `blur(${size * 0.02}px)`;
6768
+ context.translate(cx, cy);
6769
+ context.scale(1, ry / rx);
6770
+ const blurRadius = rx * 1.4;
6771
+ const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
6772
+ shadowGradient.addColorStop(0, `${palette.shadow}7a`);
6773
+ shadowGradient.addColorStop(0.45, `${palette.shadow}44`);
6774
+ shadowGradient.addColorStop(0.8, `${palette.shadow}1a`);
6775
+ shadowGradient.addColorStop(1, `${palette.shadow}00`);
6776
+ context.fillStyle = shadowGradient;
6764
6777
  context.beginPath();
6765
- context.ellipse(size * 0.5 + interaction.gazeX * size * 0.03, size * 0.85 + Math.sin(timeMs / 880) * size * 0.01, size * (0.16 + interaction.intensity * 0.015), size * 0.055, 0, 0, Math.PI * 2);
6778
+ context.arc(0, 0, blurRadius, 0, Math.PI * 2);
6766
6779
  context.fill();
6767
6780
  context.restore();
6768
6781
  }
@@ -6986,13 +6999,27 @@ const minecraftAvatarVisual = {
6986
6999
  spotlight.addColorStop(1, `${palette.highlight}00`);
6987
7000
  context.fillStyle = spotlight;
6988
7001
  context.fillRect(0, 0, size, size);
6989
- context.save();
6990
- context.fillStyle = 'rgba(0, 0, 0, 0.22)';
6991
- context.filter = `blur(${size * 0.018}px)`;
6992
- context.beginPath();
6993
- context.ellipse(size * 0.5, size * 0.86, size * 0.2, size * 0.06, 0, 0, Math.PI * 2);
6994
- context.fill();
6995
- context.restore();
7002
+ {
7003
+ // Radial gradient approximates the blurry ellipse shadow without context.filter blur.
7004
+ const cx = size * 0.5;
7005
+ const cy = size * 0.86;
7006
+ const rx = size * 0.2;
7007
+ const ry = size * 0.06;
7008
+ const blurRadius = rx * 1.4;
7009
+ const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
7010
+ shadowGradient.addColorStop(0, 'rgba(0,0,0,0.28)');
7011
+ shadowGradient.addColorStop(0.45, 'rgba(0,0,0,0.14)');
7012
+ shadowGradient.addColorStop(0.8, 'rgba(0,0,0,0.05)');
7013
+ shadowGradient.addColorStop(1, 'rgba(0,0,0,0)');
7014
+ context.save();
7015
+ context.translate(cx, cy);
7016
+ context.scale(1, ry / rx);
7017
+ context.fillStyle = shadowGradient;
7018
+ context.beginPath();
7019
+ context.arc(0, 0, blurRadius, 0, Math.PI * 2);
7020
+ context.fill();
7021
+ context.restore();
7022
+ }
6996
7023
  drawVoxelCuboid(context, {
6997
7024
  x: bodyX,
6998
7025
  y: bodyY,
@@ -8044,6 +8071,35 @@ const LIGHT_DIRECTION$2 = normalizeVector3({
8044
8071
  y: -0.62,
8045
8072
  z: 0.94,
8046
8073
  });
8074
+ /**
8075
+ * Cache keyed by the `createRandom` factory reference (stable per mounted `<Avatar/>`).
8076
+ *
8077
+ * @private helper of `octopus3dAvatarVisual`
8078
+ */
8079
+ const octopus3dStableStateCache = new WeakMap();
8080
+ /**
8081
+ * Returns the stable per-avatar state, computing it on first access and caching for subsequent frames.
8082
+ *
8083
+ * @private helper of `octopus3dAvatarVisual`
8084
+ */
8085
+ function getOctopus3dStableState(createRandom) {
8086
+ const cached = octopus3dStableStateCache.get(createRandom);
8087
+ if (cached !== undefined) {
8088
+ return cached;
8089
+ }
8090
+ const animationRandom = createRandom('octopus3d-animation-profile');
8091
+ const eyeRandom = createRandom('octopus3d-eye-profile');
8092
+ const leftEyePhaseOffset = eyeRandom() * 0.6;
8093
+ const rightEyePhaseOffset = eyeRandom() * 0.6;
8094
+ const state = {
8095
+ morphologyProfile: createOctopus3MorphologyProfile(createRandom),
8096
+ animationPhase: animationRandom() * Math.PI * 2,
8097
+ leftEyePhaseOffset,
8098
+ rightEyePhaseOffset,
8099
+ };
8100
+ octopus3dStableStateCache.set(createRandom, state);
8101
+ return state;
8102
+ }
8047
8103
  /**
8048
8104
  * Proper 3D Octopus visual built from projected organic meshes and tentacles.
8049
8105
  *
@@ -8056,10 +8112,7 @@ const octopus3dAvatarVisual = {
8056
8112
  isAnimated: true,
8057
8113
  supportsPointerTracking: true,
8058
8114
  render({ context, size, palette, createRandom, timeMs, interaction }) {
8059
- const morphologyProfile = createOctopus3MorphologyProfile(createRandom);
8060
- const animationRandom = createRandom('octopus3d-animation-profile');
8061
- const eyeRandom = createRandom('octopus3d-eye-profile');
8062
- const animationPhase = animationRandom() * Math.PI * 2;
8115
+ const { morphologyProfile, animationPhase, leftEyePhaseOffset, rightEyePhaseOffset } = getOctopus3dStableState(createRandom);
8063
8116
  const sceneCenterX = size * 0.5;
8064
8117
  const sceneCenterY = size * 0.56;
8065
8118
  const bob = Math.sin(timeMs / 920 + animationPhase) * size * 0.014;
@@ -8156,12 +8209,12 @@ const octopus3dAvatarVisual = {
8156
8209
  x: -faceEyeSpacing,
8157
8210
  y: faceEyeYOffset,
8158
8211
  z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, -faceEyeSpacing, faceEyeYOffset),
8159
- }, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + eyeRandom() * 0.6, interaction, morphologyProfile.face.eyeStyle);
8212
+ }, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + leftEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
8160
8213
  drawProjectedOrganicEye(context, {
8161
8214
  x: faceEyeSpacing,
8162
8215
  y: faceEyeYOffset,
8163
8216
  z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, faceEyeSpacing, faceEyeYOffset),
8164
- }, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.7 + eyeRandom() * 0.6, interaction, morphologyProfile.face.eyeStyle);
8217
+ }, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.7 + rightEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
8165
8218
  drawProjectedOrganicMouth(context, [
8166
8219
  {
8167
8220
  x: -mouthHalfWidth,
@@ -8205,14 +8258,28 @@ function drawOctopus3dAtmosphere(context, size, palette, sceneCenterX, sceneCent
8205
8258
  /**
8206
8259
  * Draws the soft ground shadow below the octopus.
8207
8260
  *
8261
+ * Uses a scaled radial gradient instead of `context.filter = 'blur()'` to approximate the
8262
+ * blurry ellipse without triggering a costly software rasterization pass on every frame.
8263
+ *
8208
8264
  * @private helper of `octopus3dAvatarVisual`
8209
8265
  */
8210
8266
  function drawOctopus3dShadow(context, size, palette, interaction, timeMs) {
8267
+ const cx = size * 0.5 + interaction.gazeX * size * 0.04;
8268
+ const cy = size * 0.87 + Math.sin(timeMs / 920) * size * 0.008;
8269
+ const rx = size * (0.18 + interaction.intensity * 0.02);
8270
+ const ry = size * 0.06;
8211
8271
  context.save();
8212
- context.fillStyle = `${palette.shadow}66`;
8213
- context.filter = `blur(${size * 0.022}px)`;
8272
+ context.translate(cx, cy);
8273
+ context.scale(1, ry / rx);
8274
+ const blurRadius = rx * 1.4;
8275
+ const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
8276
+ shadowGradient.addColorStop(0, `${palette.shadow}7a`);
8277
+ shadowGradient.addColorStop(0.45, `${palette.shadow}44`);
8278
+ shadowGradient.addColorStop(0.8, `${palette.shadow}1a`);
8279
+ shadowGradient.addColorStop(1, `${palette.shadow}00`);
8280
+ context.fillStyle = shadowGradient;
8214
8281
  context.beginPath();
8215
- 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);
8282
+ context.arc(0, 0, blurRadius, 0, Math.PI * 2);
8216
8283
  context.fill();
8217
8284
  context.restore();
8218
8285
  }
@@ -8443,6 +8510,35 @@ const LIGHT_DIRECTION$1 = normalizeVector3({
8443
8510
  y: -0.6,
8444
8511
  z: 0.98,
8445
8512
  });
8513
+ /**
8514
+ * Cache keyed by the `createRandom` factory reference (stable per mounted `<Avatar/>`).
8515
+ *
8516
+ * @private helper of `octopus3d2AvatarVisual`
8517
+ */
8518
+ const octopus3d2StableStateCache = new WeakMap();
8519
+ /**
8520
+ * Returns the stable per-avatar state, computing it on first access and caching for subsequent frames.
8521
+ *
8522
+ * @private helper of `octopus3d2AvatarVisual`
8523
+ */
8524
+ function getOctopus3d2StableState(createRandom) {
8525
+ const cached = octopus3d2StableStateCache.get(createRandom);
8526
+ if (cached !== undefined) {
8527
+ return cached;
8528
+ }
8529
+ const animationRandom = createRandom('octopus3d2-animation-profile');
8530
+ const eyeRandom = createRandom('octopus3d2-eye-profile');
8531
+ const leftEyePhaseOffset = eyeRandom() * 0.7;
8532
+ const rightEyePhaseOffset = eyeRandom() * 0.7;
8533
+ const state = {
8534
+ morphologyProfile: createOctopus3MorphologyProfile(createRandom),
8535
+ animationPhase: animationRandom() * Math.PI * 2,
8536
+ leftEyePhaseOffset,
8537
+ rightEyePhaseOffset,
8538
+ };
8539
+ octopus3d2StableStateCache.set(createRandom, state);
8540
+ return state;
8541
+ }
8446
8542
  /**
8447
8543
  * Octopus 3D 2 avatar visual.
8448
8544
  *
@@ -8455,10 +8551,7 @@ const octopus3d2AvatarVisual = {
8455
8551
  isAnimated: true,
8456
8552
  supportsPointerTracking: true,
8457
8553
  render({ context, size, palette, createRandom, timeMs, interaction }) {
8458
- const morphologyProfile = createOctopus3MorphologyProfile(createRandom);
8459
- const animationRandom = createRandom('octopus3d2-animation-profile');
8460
- const eyeRandom = createRandom('octopus3d2-eye-profile');
8461
- const animationPhase = animationRandom() * Math.PI * 2;
8554
+ const { morphologyProfile, animationPhase, leftEyePhaseOffset, rightEyePhaseOffset } = getOctopus3d2StableState(createRandom);
8462
8555
  const sceneCenterX = size * 0.5;
8463
8556
  const sceneCenterY = size * 0.575;
8464
8557
  const bob = Math.sin(timeMs / 940 + animationPhase) * size * 0.013;
@@ -8511,8 +8604,8 @@ const octopus3d2AvatarVisual = {
8511
8604
  const rightEyeLocalCenter = sampleBlobbyOctopusSurfacePoint(surfaceOptions, eyeLatitude, eyeLongitude);
8512
8605
  const eyeRadiusX = size * morphologyProfile.face.eyeRadiusXRatio * 0.78;
8513
8606
  const eyeRadiusY = eyeRadiusX * morphologyProfile.face.eyeHeightRatio * 0.92;
8514
- drawProjectedOrganicEye(context, leftEyeLocalCenter, eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + eyeRandom() * 0.7, interaction, morphologyProfile.face.eyeStyle);
8515
- drawProjectedOrganicEye(context, rightEyeLocalCenter, eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.9 + eyeRandom() * 0.7, interaction, morphologyProfile.face.eyeStyle);
8607
+ drawProjectedOrganicEye(context, leftEyeLocalCenter, eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + leftEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
8608
+ drawProjectedOrganicEye(context, rightEyeLocalCenter, eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.9 + rightEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
8516
8609
  drawProjectedOrganicMouth(context, [
8517
8610
  sampleBlobbyOctopusSurfacePoint(surfaceOptions, mouthLatitude, mouthCenterLongitude - mouthHalfLongitude),
8518
8611
  sampleBlobbyOctopusSurfacePoint(surfaceOptions, mouthCurveLatitude, mouthCenterLongitude),
@@ -8541,14 +8634,28 @@ function drawBlobbyOctopusAtmosphere(context, size, palette, sceneCenterX, scene
8541
8634
  /**
8542
8635
  * Draws the soft floor shadow that anchors the single mesh in the frame.
8543
8636
  *
8637
+ * Uses a scaled radial gradient instead of `context.filter = 'blur()'` to approximate the
8638
+ * blurry ellipse without triggering a costly software rasterization pass on every frame.
8639
+ *
8544
8640
  * @private helper of `octopus3d2AvatarVisual`
8545
8641
  */
8546
8642
  function drawBlobbyOctopusShadow(context, size, palette, interaction, timeMs, morphologyProfile) {
8643
+ const cx = size * 0.5 + interaction.gazeX * size * 0.045;
8644
+ const cy = size * 0.88 + Math.sin(timeMs / 940) * size * 0.008;
8645
+ const rx = size * (0.18 + (morphologyProfile.body.horizontalStretch - 1) * 0.04 + interaction.intensity * 0.018);
8646
+ const ry = size * 0.062;
8547
8647
  context.save();
8548
- context.fillStyle = `${palette.shadow}66`;
8549
- context.filter = `blur(${size * 0.024}px)`;
8648
+ context.translate(cx, cy);
8649
+ context.scale(1, ry / rx);
8650
+ const blurRadius = rx * 1.4;
8651
+ const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
8652
+ shadowGradient.addColorStop(0, `${palette.shadow}7a`);
8653
+ shadowGradient.addColorStop(0.45, `${palette.shadow}44`);
8654
+ shadowGradient.addColorStop(0.8, `${palette.shadow}1a`);
8655
+ shadowGradient.addColorStop(1, `${palette.shadow}00`);
8656
+ context.fillStyle = shadowGradient;
8550
8657
  context.beginPath();
8551
- context.ellipse(size * 0.5 + interaction.gazeX * size * 0.045, size * 0.88 + Math.sin(timeMs / 940) * size * 0.008, size * (0.18 + (morphologyProfile.body.horizontalStretch - 1) * 0.04 + interaction.intensity * 0.018), size * 0.062, 0, 0, Math.PI * 2);
8658
+ context.arc(0, 0, blurRadius, 0, Math.PI * 2);
8552
8659
  context.fill();
8553
8660
  context.restore();
8554
8661
  }
@@ -8704,6 +8811,40 @@ const LIGHT_DIRECTION = normalizeVector3({
8704
8811
  * @private helper of `octopus3d3AvatarVisual`
8705
8812
  */
8706
8813
  const OCTOPUS_TENTACLE_COUNT = 8;
8814
+ /**
8815
+ * Cache keyed by the `createRandom` factory reference, which is stable for the lifetime of one
8816
+ * mounted `<Avatar/>` component (created inside `resolveAvatarRenderDefinition` and held in a
8817
+ * React `useMemo`). Using a `WeakMap` ensures the entry is collected when the component unmounts.
8818
+ *
8819
+ * @private helper of `octopus3d3AvatarVisual`
8820
+ */
8821
+ const stableStateCache = new WeakMap();
8822
+ /**
8823
+ * Returns the stable per-avatar state, computing it on first access and returning the cached
8824
+ * result on every subsequent call within the same `<Avatar/>` mount.
8825
+ *
8826
+ * @private helper of `octopus3d3AvatarVisual`
8827
+ */
8828
+ function getOctopus3d3StableState(createRandom) {
8829
+ const cached = stableStateCache.get(createRandom);
8830
+ if (cached !== undefined) {
8831
+ return cached;
8832
+ }
8833
+ const morphologyProfile = createOctopus3MorphologyProfile(createRandom);
8834
+ const animationRandom = createRandom('octopus3d3-animation-profile');
8835
+ const eyeRandom = createRandom('octopus3d3-eye-profile');
8836
+ const leftEyePhaseOffset = eyeRandom() * 0.7;
8837
+ const rightEyePhaseOffset = eyeRandom() * 0.7;
8838
+ const state = {
8839
+ morphologyProfile,
8840
+ animationPhase: animationRandom() * Math.PI * 2,
8841
+ leftEyePhaseOffset,
8842
+ rightEyePhaseOffset,
8843
+ tentacleProfiles: createContinuousTentacleProfiles(createRandom, morphologyProfile),
8844
+ };
8845
+ stableStateCache.set(createRandom, state);
8846
+ return state;
8847
+ }
8707
8848
  /**
8708
8849
  * Octopus 3D 3 avatar visual.
8709
8850
  *
@@ -8716,11 +8857,7 @@ const octopus3d3AvatarVisual = {
8716
8857
  isAnimated: true,
8717
8858
  supportsPointerTracking: true,
8718
8859
  render({ context, size, palette, createRandom, timeMs, interaction }) {
8719
- const morphologyProfile = createOctopus3MorphologyProfile(createRandom);
8720
- const animationRandom = createRandom('octopus3d3-animation-profile');
8721
- const eyeRandom = createRandom('octopus3d3-eye-profile');
8722
- const animationPhase = animationRandom() * Math.PI * 2;
8723
- const tentacleProfiles = createContinuousTentacleProfiles(createRandom, morphologyProfile);
8860
+ const { morphologyProfile, animationPhase, leftEyePhaseOffset, rightEyePhaseOffset, tentacleProfiles } = getOctopus3d3StableState(createRandom);
8724
8861
  const sceneCenterX = size * 0.5;
8725
8862
  const sceneCenterY = size * 0.535;
8726
8863
  const bob = Math.sin(timeMs / 960 + animationPhase) * size * 0.012;
@@ -8797,8 +8934,8 @@ const octopus3d3AvatarVisual = {
8797
8934
  size,
8798
8935
  palette,
8799
8936
  });
8800
- drawProjectedOrganicEye(context, sampleContinuousOctopusSurfacePoint(surfaceOptions, eyeLatitude, -eyeLongitude), eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + eyeRandom() * 0.7, interaction, morphologyProfile.face.eyeStyle);
8801
- drawProjectedOrganicEye(context, sampleContinuousOctopusSurfacePoint(surfaceOptions, eyeLatitude, eyeLongitude), eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.85 + eyeRandom() * 0.7, interaction, morphologyProfile.face.eyeStyle);
8937
+ drawProjectedOrganicEye(context, sampleContinuousOctopusSurfacePoint(surfaceOptions, eyeLatitude, -eyeLongitude), eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + leftEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
8938
+ drawProjectedOrganicEye(context, sampleContinuousOctopusSurfacePoint(surfaceOptions, eyeLatitude, eyeLongitude), eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.85 + rightEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
8802
8939
  drawProjectedOrganicMouth(context, [
8803
8940
  sampleContinuousOctopusSurfacePoint(surfaceOptions, mouthLatitude, mouthCenterLongitude - mouthHalfLongitude),
8804
8941
  sampleContinuousOctopusSurfacePoint(surfaceOptions, mouthCurveLatitude, mouthCenterLongitude),
@@ -8849,14 +8986,30 @@ function drawContinuousOctopusAtmosphere(context, size, palette, sceneCenterX, s
8849
8986
  /**
8850
8987
  * Draws the soft lower shadow that anchors the octopus in the avatar frame.
8851
8988
  *
8989
+ * Uses a scaled radial gradient instead of `context.filter = 'blur()'` to approximate the
8990
+ * blurry ellipse without triggering a costly software rasterization pass on every frame.
8991
+ *
8852
8992
  * @private helper of `octopus3d3AvatarVisual`
8853
8993
  */
8854
8994
  function drawContinuousOctopusShadow(context, size, palette, interaction, timeMs, morphologyProfile) {
8995
+ const cx = size * 0.5 + interaction.gazeX * size * 0.045;
8996
+ const cy = size * 0.9 + Math.sin(timeMs / 980) * size * 0.007;
8997
+ const rx = size * (0.19 + morphologyProfile.tentacles.rootSpreadScale * 0.022 + interaction.intensity * 0.02);
8998
+ const ry = size * 0.06;
8999
+ // Scale the context so that drawing a circle produces the correct ellipse aspect ratio,
9000
+ // then fill with a radial gradient that approximates the blurry edge without context.filter.
8855
9001
  context.save();
8856
- context.fillStyle = `${palette.shadow}66`;
8857
- context.filter = `blur(${size * 0.025}px)`;
9002
+ context.translate(cx, cy);
9003
+ context.scale(1, ry / rx);
9004
+ const blurRadius = rx * 1.4;
9005
+ const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
9006
+ shadowGradient.addColorStop(0, `${palette.shadow}7a`);
9007
+ shadowGradient.addColorStop(0.45, `${palette.shadow}44`);
9008
+ shadowGradient.addColorStop(0.8, `${palette.shadow}1a`);
9009
+ shadowGradient.addColorStop(1, `${palette.shadow}00`);
9010
+ context.fillStyle = shadowGradient;
8858
9011
  context.beginPath();
8859
- context.ellipse(size * 0.5 + interaction.gazeX * size * 0.045, size * 0.9 + Math.sin(timeMs / 980) * size * 0.007, size * (0.19 + morphologyProfile.tentacles.rootSpreadScale * 0.022 + interaction.intensity * 0.02), size * 0.06, 0, 0, Math.PI * 2);
9012
+ context.arc(0, 0, blurRadius, 0, Math.PI * 2);
8860
9013
  context.fill();
8861
9014
  context.restore();
8862
9015
  }
@@ -33276,7 +33429,7 @@ class OpenAiVectorStoreKnowledgeSourcePreparer {
33276
33429
  return { skippedReason: 'invalid_data_url' };
33277
33430
  }
33278
33431
  return {
33279
- file: new File([parsed.buffer], parsed.filename, {
33432
+ file: new File([new Uint8Array(parsed.buffer)], parsed.filename, {
33280
33433
  type: parsed.mimeType,
33281
33434
  }),
33282
33435
  sizeBytes: parsed.buffer.length,