@promptbook/components 0.112.0-103 → 0.112.0-105

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.
package/umd/index.umd.js CHANGED
@@ -31,7 +31,7 @@
31
31
  * @generated
32
32
  * @see https://github.com/webgptorg/promptbook
33
33
  */
34
- const PROMPTBOOK_ENGINE_VERSION = '0.112.0-103';
34
+ const PROMPTBOOK_ENGINE_VERSION = '0.112.0-105';
35
35
  /**
36
36
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
37
37
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -3243,7 +3243,7 @@
3243
3243
  *
3244
3244
  * @private helper of `minecraft2AvatarVisual`
3245
3245
  */
3246
- const LIGHT_DIRECTION$2 = normalizeVector3({
3246
+ const LIGHT_DIRECTION$3 = normalizeVector3({
3247
3247
  x: 0.4,
3248
3248
  y: -0.65,
3249
3249
  z: 0.92,
@@ -3455,7 +3455,7 @@
3455
3455
  corners: projectedCorners,
3456
3456
  texture: faceDefinition.texture,
3457
3457
  averageDepth: transformedCorners.reduce((depthSum, corner) => depthSum + corner.z, 0) / transformedCorners.length,
3458
- lightIntensity: clampNumber$1(dotProduct3D(faceNormal, LIGHT_DIRECTION$2), -1, 1),
3458
+ lightIntensity: clampNumber$1(dotProduct3D(faceNormal, LIGHT_DIRECTION$3), -1, 1),
3459
3459
  outlineColor: cuboid.outlineColor,
3460
3460
  };
3461
3461
  });
@@ -4646,7 +4646,7 @@
4646
4646
  *
4647
4647
  * @private helper of `octopus3dAvatarVisual`
4648
4648
  */
4649
- const LIGHT_DIRECTION$1 = normalizeVector3({
4649
+ const LIGHT_DIRECTION$2 = normalizeVector3({
4650
4650
  x: 0.48,
4651
4651
  y: -0.62,
4652
4652
  z: 0.94,
@@ -4856,7 +4856,7 @@
4856
4856
  corners: projectedCorners,
4857
4857
  averageDepth: transformedCorners.reduce((depthSum, transformedCorner) => depthSum + transformedCorner.z, 0) /
4858
4858
  transformedCorners.length,
4859
- lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$1), -1, 1),
4859
+ lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$2), -1, 1),
4860
4860
  fillStyle: resolveSurfacePatchFillStyle(palette, verticalProgress + verticalColorBias),
4861
4861
  outlineColor,
4862
4862
  });
@@ -5045,7 +5045,7 @@
5045
5045
  *
5046
5046
  * @private helper of `octopus3d2AvatarVisual`
5047
5047
  */
5048
- const LIGHT_DIRECTION = normalizeVector3({
5048
+ const LIGHT_DIRECTION$1 = normalizeVector3({
5049
5049
  x: 0.38,
5050
5050
  y: -0.6,
5051
5051
  z: 0.98,
@@ -5194,7 +5194,7 @@
5194
5194
  corners: projectedCorners,
5195
5195
  averageDepth: transformedCorners.reduce((depthSum, transformedCorner) => depthSum + transformedCorner.z, 0) /
5196
5196
  transformedCorners.length,
5197
- lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION), -1, 1),
5197
+ lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$1), -1, 1),
5198
5198
  fillStyle: resolveBlobbySurfacePatchFillStyle(palette, verticalProgress, Math.max(0, Math.cos(centerLongitude)), resolveLowerLobeWave(centerLongitude, morphologyProfile, animationPhase, timeMs)),
5199
5199
  outlineColor: verticalProgress < 0.58 ? `${palette.highlight}73` : `${palette.shadow}8a`,
5200
5200
  });
@@ -5294,6 +5294,514 @@
5294
5294
  context.restore();
5295
5295
  }
5296
5296
 
5297
+ /* eslint-disable no-magic-numbers */
5298
+ /**
5299
+ * Light direction used by the continuous octopus mesh shading.
5300
+ *
5301
+ * @private helper of `octopus3d3AvatarVisual`
5302
+ */
5303
+ const LIGHT_DIRECTION = normalizeVector3({
5304
+ x: 0.34,
5305
+ y: -0.62,
5306
+ z: 1,
5307
+ });
5308
+ /**
5309
+ * Real-octopus tentacle count used by the continuous lower mesh.
5310
+ *
5311
+ * @private helper of `octopus3d3AvatarVisual`
5312
+ */
5313
+ const OCTOPUS_TENTACLE_COUNT = 8;
5314
+ /**
5315
+ * Octopus 3D 3 avatar visual.
5316
+ *
5317
+ * @private built-in avatar visual
5318
+ */
5319
+ const octopus3d3AvatarVisual = {
5320
+ id: 'octopus3d3',
5321
+ title: 'Octopus 3D 3',
5322
+ description: 'Cute continuous 3D octopus with a blobby single mesh, waving tentacle lobes, rich shading, and cursor-aware eyes.',
5323
+ isAnimated: true,
5324
+ supportsPointerTracking: true,
5325
+ render({ context, size, palette, createRandom, timeMs, interaction }) {
5326
+ const morphologyProfile = createOctopus3MorphologyProfile(createRandom);
5327
+ const animationRandom = createRandom('octopus3d3-animation-profile');
5328
+ const eyeRandom = createRandom('octopus3d3-eye-profile');
5329
+ const animationPhase = animationRandom() * Math.PI * 2;
5330
+ const tentacleProfiles = createContinuousTentacleProfiles(createRandom, morphologyProfile);
5331
+ const sceneCenterX = size * 0.5;
5332
+ const sceneCenterY = size * 0.535;
5333
+ const bob = Math.sin(timeMs / 960 + animationPhase) * size * 0.012;
5334
+ const meshCenter = {
5335
+ x: interaction.bodyOffsetX * size * 0.048 + size * morphologyProfile.body.centerXJitterRatio * 0.44,
5336
+ y: -size * 0.07 + interaction.bodyOffsetY * size * 0.026 + bob,
5337
+ z: interaction.intensity * size * 0.018,
5338
+ };
5339
+ const rotationY = -0.1 +
5340
+ Math.sin(timeMs / 2700 + animationPhase) * 0.035 +
5341
+ interaction.bodyOffsetX * 0.22 +
5342
+ interaction.gazeX * 0.88;
5343
+ const rotationX = -0.07 +
5344
+ Math.cos(timeMs / 3100 + animationPhase * 0.7) * 0.018 -
5345
+ interaction.bodyOffsetY * 0.08 -
5346
+ interaction.gazeY * 0.38;
5347
+ const surfaceOptions = {
5348
+ radiusX: size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.horizontalStretch * 1.1,
5349
+ radiusY: size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.verticalStretch * 1.08,
5350
+ radiusZ: size *
5351
+ morphologyProfile.body.bodyRadiusRatio *
5352
+ (1.02 + (morphologyProfile.body.horizontalStretch - 1) * 0.18),
5353
+ morphologyProfile,
5354
+ timeMs,
5355
+ animationPhase,
5356
+ tentacleProfiles,
5357
+ };
5358
+ const surfacePatches = resolveVisibleContinuousOctopusPatches({
5359
+ ...surfaceOptions,
5360
+ center: meshCenter,
5361
+ rotationX,
5362
+ rotationY,
5363
+ sceneCenterX,
5364
+ sceneCenterY,
5365
+ size,
5366
+ palette,
5367
+ });
5368
+ const eyeLatitude = clampNumber$1(morphologyProfile.face.eyeCenterYOffsetRatio * 4.2 - 0.03, -0.22, 0.08);
5369
+ const eyeLongitude = clampNumber$1(morphologyProfile.face.eyeSpacingRatio * 3.1, 0.18, 0.32);
5370
+ const mouthLatitude = clampNumber$1(eyeLatitude + 0.2 + morphologyProfile.face.mouthYOffsetRatio, 0.08, 0.34);
5371
+ const mouthCenterLongitude = clampNumber$1(morphologyProfile.face.mouthCenterOffsetRatio * 5.6, -0.08, 0.08);
5372
+ const mouthHalfLongitude = clampNumber$1(eyeLongitude * 0.78, 0.15, 0.28);
5373
+ const mouthCurveLatitude = clampNumber$1(mouthLatitude + morphologyProfile.face.mouthCurveDepthRatio * 0.78, mouthLatitude + 0.03, 0.42);
5374
+ const eyeRadiusX = size * morphologyProfile.face.eyeRadiusXRatio * 0.76;
5375
+ const eyeRadiusY = eyeRadiusX * morphologyProfile.face.eyeHeightRatio * 0.9;
5376
+ drawAvatarFrame(context, size, palette);
5377
+ drawContinuousOctopusAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs);
5378
+ drawContinuousOctopusShadow(context, size, palette, interaction, timeMs, morphologyProfile);
5379
+ for (const surfacePatch of surfacePatches.sort((firstSurfacePatch, secondSurfacePatch) => firstSurfacePatch.averageDepth - secondSurfacePatch.averageDepth)) {
5380
+ drawContinuousSurfacePatch(context, surfacePatch);
5381
+ }
5382
+ drawProjectedSurfaceCurrents({
5383
+ context,
5384
+ surfaceOptions,
5385
+ center: meshCenter,
5386
+ rotationX,
5387
+ rotationY,
5388
+ sceneCenterX,
5389
+ sceneCenterY,
5390
+ size,
5391
+ palette,
5392
+ morphologyProfile,
5393
+ timeMs,
5394
+ animationPhase,
5395
+ });
5396
+ drawProjectedTentacleSuckers({
5397
+ context,
5398
+ surfaceOptions,
5399
+ center: meshCenter,
5400
+ rotationX,
5401
+ rotationY,
5402
+ sceneCenterX,
5403
+ sceneCenterY,
5404
+ size,
5405
+ palette,
5406
+ });
5407
+ drawProjectedOrganicEye(context, sampleContinuousOctopusSurfacePoint(surfaceOptions, eyeLatitude, -eyeLongitude), eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + eyeRandom() * 0.7, interaction, morphologyProfile.face.eyeStyle);
5408
+ 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);
5409
+ drawProjectedOrganicMouth(context, [
5410
+ sampleContinuousOctopusSurfacePoint(surfaceOptions, mouthLatitude, mouthCenterLongitude - mouthHalfLongitude),
5411
+ sampleContinuousOctopusSurfacePoint(surfaceOptions, mouthCurveLatitude, mouthCenterLongitude),
5412
+ sampleContinuousOctopusSurfacePoint(surfaceOptions, mouthLatitude, mouthCenterLongitude + mouthHalfLongitude),
5413
+ ], meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, palette, size);
5414
+ },
5415
+ };
5416
+ /**
5417
+ * Creates seeded tentacle-lobe profiles around the visible lower octopus body.
5418
+ *
5419
+ * @private helper of `octopus3d3AvatarVisual`
5420
+ */
5421
+ function createContinuousTentacleProfiles(createRandom, morphologyProfile) {
5422
+ return Array.from({ length: OCTOPUS_TENTACLE_COUNT }, (_, tentacleIndex) => {
5423
+ const tentacleRandom = createRandom(`octopus3d3-tentacle-${tentacleIndex}`);
5424
+ const progress = tentacleIndex / (OCTOPUS_TENTACLE_COUNT - 1);
5425
+ return {
5426
+ centerLongitude: -Math.PI * 0.86 +
5427
+ progress * Math.PI * 1.72 +
5428
+ (tentacleRandom() - 0.5) * (0.08 + morphologyProfile.tentacles.rootSpreadScale * 0.03),
5429
+ widthScale: 0.86 + tentacleRandom() * 0.34 + (morphologyProfile.tentacles.baseWidthScale - 1) * 0.16,
5430
+ lengthScale: 0.86 + tentacleRandom() * 0.36 + (morphologyProfile.tentacles.flowLengthScale - 1) * 0.22,
5431
+ swayScale: 0.82 + tentacleRandom() * 0.38 + (morphologyProfile.tentacles.swayScale - 1) * 0.2,
5432
+ depthScale: 0.86 + tentacleRandom() * 0.32 + (morphologyProfile.tentacles.tipReachScale - 1) * 0.2,
5433
+ phase: tentacleRandom() * Math.PI * 2,
5434
+ suckerSide: tentacleRandom() > 0.5 ? 1 : -1,
5435
+ };
5436
+ });
5437
+ }
5438
+ /**
5439
+ * Draws the soft underwater atmosphere behind the continuous octopus mesh.
5440
+ *
5441
+ * @private helper of `octopus3d3AvatarVisual`
5442
+ */
5443
+ function drawContinuousOctopusAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs) {
5444
+ const glowGradient = context.createRadialGradient(sceneCenterX + interaction.gazeX * size * 0.11, sceneCenterY - size * 0.17 + interaction.gazeY * size * 0.05, size * 0.04, sceneCenterX, sceneCenterY, size * (0.66 + interaction.intensity * 0.02));
5445
+ glowGradient.addColorStop(0, `${palette.highlight}66`);
5446
+ glowGradient.addColorStop(0.34, `${palette.accent}2e`);
5447
+ glowGradient.addColorStop(1, `${palette.highlight}00`);
5448
+ context.fillStyle = glowGradient;
5449
+ context.fillRect(0, 0, size, size);
5450
+ const lowerGradient = context.createRadialGradient(sceneCenterX + Math.sin(timeMs / 1550) * size * 0.05, sceneCenterY + size * 0.29, size * 0.06, sceneCenterX, sceneCenterY + size * 0.3, size * 0.54);
5451
+ lowerGradient.addColorStop(0, `${palette.secondary}25`);
5452
+ lowerGradient.addColorStop(1, `${palette.secondary}00`);
5453
+ context.fillStyle = lowerGradient;
5454
+ context.fillRect(0, 0, size, size);
5455
+ }
5456
+ /**
5457
+ * Draws the soft lower shadow that anchors the octopus in the avatar frame.
5458
+ *
5459
+ * @private helper of `octopus3d3AvatarVisual`
5460
+ */
5461
+ function drawContinuousOctopusShadow(context, size, palette, interaction, timeMs, morphologyProfile) {
5462
+ context.save();
5463
+ context.fillStyle = `${palette.shadow}66`;
5464
+ context.filter = `blur(${size * 0.025}px)`;
5465
+ context.beginPath();
5466
+ 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);
5467
+ context.fill();
5468
+ context.restore();
5469
+ }
5470
+ /**
5471
+ * Resolves visible projected patches for the continuous octopus mesh.
5472
+ *
5473
+ * @private helper of `octopus3d3AvatarVisual`
5474
+ */
5475
+ function resolveVisibleContinuousOctopusPatches(options) {
5476
+ const { center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette } = options;
5477
+ const latitudePatchCount = 16;
5478
+ const longitudePatchCount = 40;
5479
+ const surfacePatches = [];
5480
+ for (let latitudeIndex = 0; latitudeIndex < latitudePatchCount; latitudeIndex++) {
5481
+ const startLatitude = -Math.PI / 2 + (latitudeIndex / latitudePatchCount) * Math.PI;
5482
+ const endLatitude = -Math.PI / 2 + ((latitudeIndex + 1) / latitudePatchCount) * Math.PI;
5483
+ const centerLatitude = (startLatitude + endLatitude) / 2;
5484
+ const verticalProgress = (Math.sin(centerLatitude) + 1) / 2;
5485
+ for (let longitudeIndex = 0; longitudeIndex < longitudePatchCount; longitudeIndex++) {
5486
+ const startLongitude = -Math.PI + (longitudeIndex / longitudePatchCount) * Math.PI * 2;
5487
+ const endLongitude = -Math.PI + ((longitudeIndex + 1) / longitudePatchCount) * Math.PI * 2;
5488
+ const centerLongitude = (startLongitude + endLongitude) / 2;
5489
+ const localCorners = [
5490
+ sampleContinuousOctopusSurfacePoint(options, startLatitude, startLongitude),
5491
+ sampleContinuousOctopusSurfacePoint(options, startLatitude, endLongitude),
5492
+ sampleContinuousOctopusSurfacePoint(options, endLatitude, endLongitude),
5493
+ sampleContinuousOctopusSurfacePoint(options, endLatitude, startLongitude),
5494
+ ];
5495
+ const transformedCorners = localCorners.map((localCorner) => transformScenePoint(localCorner, center, rotationX, rotationY));
5496
+ const surfaceNormal = normalizeVector3(crossProduct3D(subtractPoint3D(transformedCorners[1], transformedCorners[0]), subtractPoint3D(transformedCorners[2], transformedCorners[0])));
5497
+ if (surfaceNormal.z <= 0.008) {
5498
+ continue;
5499
+ }
5500
+ const projectedCorners = transformedCorners.map((transformedCorner) => projectScenePoint(transformedCorner, size, sceneCenterX, sceneCenterY));
5501
+ const tentacleInfluence = resolveContinuousTentacleInfluence(options, centerLongitude);
5502
+ const lowerLobeWave = resolveContinuousLobeWave(options, centerLongitude);
5503
+ surfacePatches.push({
5504
+ corners: projectedCorners,
5505
+ averageDepth: transformedCorners.reduce((depthSum, transformedCorner) => depthSum + transformedCorner.z, 0) /
5506
+ transformedCorners.length,
5507
+ lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION), -1, 1),
5508
+ fillStyle: resolveContinuousSurfacePatchFillStyle(palette, verticalProgress, Math.max(0, Math.cos(centerLongitude)), tentacleInfluence.core, lowerLobeWave),
5509
+ outlineColor: verticalProgress < 0.54 ? `${palette.highlight}69` : `${palette.shadow}78`,
5510
+ });
5511
+ }
5512
+ }
5513
+ return surfacePatches;
5514
+ }
5515
+ /**
5516
+ * Samples one point on the continuous Octopus 3D 3 surface.
5517
+ *
5518
+ * The lower hemisphere is pulled into eight seeded waving lobes, so the portrait reads as
5519
+ * tentacled while still being rendered as one connected blobby mesh.
5520
+ *
5521
+ * @private helper of `octopus3d3AvatarVisual`
5522
+ */
5523
+ function sampleContinuousOctopusSurfacePoint(options, latitude, longitude) {
5524
+ const { radiusX, radiusY, radiusZ, morphologyProfile, timeMs, animationPhase } = options;
5525
+ const cosineLatitude = Math.max(0, Math.cos(latitude));
5526
+ const verticalProgress = (Math.sin(latitude) + 1) / 2;
5527
+ const upperBlend = Math.pow(1 - verticalProgress, 1.28);
5528
+ const lowerBlend = smoothStep(0.38, 1, verticalProgress);
5529
+ const tipBlend = smoothStep(0.68, 1, verticalProgress);
5530
+ const tentacleInfluence = resolveContinuousTentacleInfluence(options, longitude);
5531
+ const centerPull = resolveSignedAngularDistance(longitude, tentacleInfluence.centerLongitude);
5532
+ const effectiveLongitude = longitude + centerPull * lowerBlend * tentacleInfluence.core * (0.24 + tipBlend * 0.2);
5533
+ const lowerLobeWave = resolveContinuousLobeWave(options, longitude);
5534
+ const mantleRipple = Math.sin(longitude * morphologyProfile.body.lobeCount +
5535
+ animationPhase * 0.6 +
5536
+ timeMs / (1750 + morphologyProfile.body.lobeCount * 30)) *
5537
+ (0.018 + morphologyProfile.body.wobbleAmplitudeRatio * 0.8) *
5538
+ (0.3 + lowerBlend * 0.7);
5539
+ const tentacleWave = Math.sin(timeMs / 760 + tentacleInfluence.phase + verticalProgress * 2.4) *
5540
+ lowerBlend *
5541
+ tentacleInfluence.core *
5542
+ tentacleInfluence.swayScale;
5543
+ const horizontalScale = 1.04 +
5544
+ mantleRipple +
5545
+ lowerBlend * (0.16 + (morphologyProfile.tentacles.rootSpreadScale - 1) * 0.1) +
5546
+ lowerBlend * tentacleInfluence.core * (0.2 + lowerLobeWave * 0.12) -
5547
+ upperBlend * 0.08;
5548
+ const depthScale = 1.06 +
5549
+ upperBlend * 0.16 +
5550
+ Math.max(0, Math.cos(effectiveLongitude)) * 0.1 +
5551
+ lowerBlend * tentacleInfluence.core * (0.1 + tentacleInfluence.depthScale * 0.06) -
5552
+ Math.max(0, -Math.cos(effectiveLongitude)) * 0.05;
5553
+ const tentacleTubeRadius = lowerBlend * tentacleInfluence.core * (0.11 + tipBlend * 0.06 + tentacleInfluence.widthScale * 0.025) * radiusX;
5554
+ const planarRadiusX = cosineLatitude * radiusX * horizontalScale + tentacleTubeRadius;
5555
+ const planarRadiusZ = cosineLatitude * radiusZ * depthScale + tentacleTubeRadius * 0.72;
5556
+ const lowerDrop = lowerBlend *
5557
+ radiusY *
5558
+ (0.18 +
5559
+ tentacleInfluence.core *
5560
+ (0.38 +
5561
+ tentacleInfluence.lengthScale * 0.22 +
5562
+ (morphologyProfile.tentacles.flowLengthScale - 1) * 0.08));
5563
+ return {
5564
+ x: Math.sin(effectiveLongitude) * planarRadiusX + tentacleWave * radiusX * (0.052 + tipBlend * 0.05),
5565
+ y: Math.sin(latitude) * radiusY * (1 + upperBlend * 0.12) -
5566
+ upperBlend * radiusY * 0.1 +
5567
+ lowerDrop +
5568
+ Math.sin(timeMs / 1420 + animationPhase + latitude * 1.6) * lowerBlend * radiusY * 0.018 +
5569
+ Math.cos(timeMs / 880 + tentacleInfluence.phase) *
5570
+ lowerBlend *
5571
+ tipBlend *
5572
+ tentacleInfluence.core *
5573
+ radiusY *
5574
+ 0.034,
5575
+ z: Math.cos(effectiveLongitude) * planarRadiusZ +
5576
+ Math.cos(timeMs / 980 + tentacleInfluence.phase + verticalProgress) *
5577
+ lowerBlend *
5578
+ tentacleInfluence.core *
5579
+ radiusZ *
5580
+ 0.04,
5581
+ };
5582
+ }
5583
+ /**
5584
+ * Blends nearby seeded tentacle profiles at one mesh longitude.
5585
+ *
5586
+ * @private helper of `octopus3d3AvatarVisual`
5587
+ */
5588
+ function resolveContinuousTentacleInfluence(options, longitude) {
5589
+ let totalWeight = 0;
5590
+ let weightedSin = 0;
5591
+ let weightedCos = 0;
5592
+ let weightedWidthScale = 0;
5593
+ let weightedLengthScale = 0;
5594
+ let weightedSwayScale = 0;
5595
+ let weightedDepthScale = 0;
5596
+ let weightedPhase = 0;
5597
+ for (const tentacleProfile of options.tentacleProfiles) {
5598
+ const distance = Math.abs(resolveSignedAngularDistance(longitude, tentacleProfile.centerLongitude));
5599
+ const width = 0.2 * tentacleProfile.widthScale;
5600
+ const weight = Math.exp(-(distance * distance) / (width * width));
5601
+ totalWeight += weight;
5602
+ weightedSin += Math.sin(tentacleProfile.centerLongitude) * weight;
5603
+ weightedCos += Math.cos(tentacleProfile.centerLongitude) * weight;
5604
+ weightedWidthScale += tentacleProfile.widthScale * weight;
5605
+ weightedLengthScale += tentacleProfile.lengthScale * weight;
5606
+ weightedSwayScale += tentacleProfile.swayScale * weight;
5607
+ weightedDepthScale += tentacleProfile.depthScale * weight;
5608
+ weightedPhase += tentacleProfile.phase * weight;
5609
+ }
5610
+ if (totalWeight < 0.0001) {
5611
+ return {
5612
+ core: 0,
5613
+ centerLongitude: longitude,
5614
+ widthScale: 1,
5615
+ lengthScale: 1,
5616
+ swayScale: 1,
5617
+ depthScale: 1,
5618
+ phase: 0,
5619
+ };
5620
+ }
5621
+ return {
5622
+ core: clampNumber$1(totalWeight, 0, 1),
5623
+ centerLongitude: Math.atan2(weightedSin / totalWeight, weightedCos / totalWeight),
5624
+ widthScale: weightedWidthScale / totalWeight,
5625
+ lengthScale: weightedLengthScale / totalWeight,
5626
+ swayScale: weightedSwayScale / totalWeight,
5627
+ depthScale: weightedDepthScale / totalWeight,
5628
+ phase: weightedPhase / totalWeight,
5629
+ };
5630
+ }
5631
+ /**
5632
+ * Resolves the soft lower wave that makes the continuous mesh read as a set of tentacles.
5633
+ *
5634
+ * @private helper of `octopus3d3AvatarVisual`
5635
+ */
5636
+ function resolveContinuousLobeWave(options, longitude) {
5637
+ const { morphologyProfile, animationPhase, timeMs } = options;
5638
+ return ((Math.cos(longitude * OCTOPUS_TENTACLE_COUNT +
5639
+ animationPhase +
5640
+ timeMs / (980 + morphologyProfile.body.lobeCount * 18)) +
5641
+ 1) /
5642
+ 2);
5643
+ }
5644
+ /**
5645
+ * Resolves one base fill tone for a patch on the continuous octopus mesh.
5646
+ *
5647
+ * @private helper of `octopus3d3AvatarVisual`
5648
+ */
5649
+ function resolveContinuousSurfacePatchFillStyle(palette, verticalProgress, forwardness, tentacleCore, lowerLobeWave) {
5650
+ const tonalProgress = clampNumber$1(verticalProgress + lowerLobeWave * 0.1 + tentacleCore * 0.08 - forwardness * 0.08, 0, 1);
5651
+ if (tonalProgress < 0.14) {
5652
+ return palette.highlight;
5653
+ }
5654
+ if (tonalProgress < 0.32) {
5655
+ return palette.secondary;
5656
+ }
5657
+ if (tonalProgress < 0.72) {
5658
+ return forwardness > 0.55 ? palette.secondary : palette.primary;
5659
+ }
5660
+ return tentacleCore > 0.44 ? `${palette.primary}f4` : `${palette.shadow}ee`;
5661
+ }
5662
+ /**
5663
+ * Draws one projected mesh patch with soft shading and a subtle edge.
5664
+ *
5665
+ * @private helper of `octopus3d3AvatarVisual`
5666
+ */
5667
+ function drawContinuousSurfacePatch(context, surfacePatch) {
5668
+ drawProjectedQuad(context, surfacePatch.corners, surfacePatch.fillStyle);
5669
+ if (surfacePatch.lightIntensity > 0) {
5670
+ drawProjectedQuad(context, surfacePatch.corners, `rgba(255, 255, 255, ${0.18 * surfacePatch.lightIntensity})`);
5671
+ }
5672
+ else if (surfacePatch.lightIntensity < 0) {
5673
+ drawProjectedQuad(context, surfacePatch.corners, `rgba(0, 0, 0, ${0.25 * Math.abs(surfacePatch.lightIntensity)})`);
5674
+ }
5675
+ context.save();
5676
+ context.beginPath();
5677
+ context.moveTo(surfacePatch.corners[0].x, surfacePatch.corners[0].y);
5678
+ for (let cornerIndex = 1; cornerIndex < surfacePatch.corners.length; cornerIndex++) {
5679
+ context.lineTo(surfacePatch.corners[cornerIndex].x, surfacePatch.corners[cornerIndex].y);
5680
+ }
5681
+ context.closePath();
5682
+ context.strokeStyle = surfacePatch.outlineColor;
5683
+ context.lineWidth = Math.max(0.7, getProjectedQuadPerimeter(surfacePatch.corners) * 0.0032);
5684
+ context.lineJoin = 'round';
5685
+ context.stroke();
5686
+ context.restore();
5687
+ }
5688
+ /**
5689
+ * Draws projected mantle-current lines on the front of the mesh.
5690
+ *
5691
+ * @private helper of `octopus3d3AvatarVisual`
5692
+ */
5693
+ function drawProjectedSurfaceCurrents(options) {
5694
+ const { context, surfaceOptions, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, morphologyProfile, timeMs, animationPhase, } = options;
5695
+ const currentCount = Math.min(6, morphologyProfile.details.mantleCurrentCount);
5696
+ const centerIndex = (currentCount - 1) / 2;
5697
+ context.save();
5698
+ context.lineCap = 'round';
5699
+ context.lineJoin = 'round';
5700
+ for (let currentIndex = 0; currentIndex < currentCount; currentIndex++) {
5701
+ const baseLongitude = (currentIndex - centerIndex) * 0.15;
5702
+ const projectedPoints = [];
5703
+ for (let sampleIndex = 0; sampleIndex < 8; sampleIndex++) {
5704
+ const progress = sampleIndex / 7;
5705
+ const latitude = -0.46 + progress * 0.74;
5706
+ const longitude = baseLongitude + Math.sin(timeMs / 1160 + animationPhase + currentIndex * 0.7 + progress * 2) * 0.035;
5707
+ const scenePoint = transformScenePoint(sampleContinuousOctopusSurfacePoint(surfaceOptions, latitude, longitude), center, rotationX, rotationY);
5708
+ if (scenePoint.z > center.z - size * 0.016) {
5709
+ projectedPoints.push(projectScenePoint(scenePoint, size, sceneCenterX, sceneCenterY));
5710
+ }
5711
+ }
5712
+ if (projectedPoints.length < 3) {
5713
+ continue;
5714
+ }
5715
+ context.beginPath();
5716
+ context.moveTo(projectedPoints[0].x, projectedPoints[0].y);
5717
+ for (const projectedPoint of projectedPoints.slice(1)) {
5718
+ context.lineTo(projectedPoint.x, projectedPoint.y);
5719
+ }
5720
+ context.strokeStyle = currentIndex % 2 === 0 ? `${palette.highlight}3d` : `${palette.accent}33`;
5721
+ context.lineWidth = size * (0.0055 + currentIndex * 0.00045);
5722
+ context.stroke();
5723
+ }
5724
+ context.restore();
5725
+ }
5726
+ /**
5727
+ * Draws small projected sucker highlights on the waving lower mesh lobes.
5728
+ *
5729
+ * @private helper of `octopus3d3AvatarVisual`
5730
+ */
5731
+ function drawProjectedTentacleSuckers(options) {
5732
+ const { surfaceOptions, size } = options;
5733
+ const { timeMs } = surfaceOptions;
5734
+ for (const tentacleProfile of surfaceOptions.tentacleProfiles) {
5735
+ if (Math.cos(tentacleProfile.centerLongitude) < -0.12) {
5736
+ continue;
5737
+ }
5738
+ for (let suckerIndex = 0; suckerIndex < 3; suckerIndex++) {
5739
+ const latitude = 0.52 + suckerIndex * 0.14;
5740
+ const sideOffset = tentacleProfile.suckerSide * (0.035 + suckerIndex * 0.012) * tentacleProfile.widthScale;
5741
+ const waveOffset = Math.sin(timeMs / 900 + tentacleProfile.phase + suckerIndex * 0.8) * 0.018;
5742
+ drawProjectedSurfaceSpot({
5743
+ ...options,
5744
+ latitude,
5745
+ longitude: tentacleProfile.centerLongitude + sideOffset + waveOffset,
5746
+ radiusScale: size * (0.0065 - suckerIndex * 0.0007),
5747
+ });
5748
+ }
5749
+ }
5750
+ }
5751
+ /**
5752
+ * Draws one tiny projected surface spot by sampling local mesh tangents.
5753
+ *
5754
+ * @private helper of `octopus3d3AvatarVisual`
5755
+ */
5756
+ function drawProjectedSurfaceSpot(options) {
5757
+ const { context, surfaceOptions, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, latitude, longitude, radiusScale, } = options;
5758
+ const localCenter = sampleContinuousOctopusSurfacePoint(surfaceOptions, latitude, longitude);
5759
+ const localHorizontal = sampleContinuousOctopusSurfacePoint(surfaceOptions, latitude, longitude + 0.018);
5760
+ const localVertical = sampleContinuousOctopusSurfacePoint(surfaceOptions, latitude + 0.018, longitude);
5761
+ const sceneCenterPoint = transformScenePoint(localCenter, center, rotationX, rotationY);
5762
+ if (sceneCenterPoint.z <= center.z - size * 0.012) {
5763
+ return;
5764
+ }
5765
+ const projectedCenterPoint = projectScenePoint(sceneCenterPoint, size, sceneCenterX, sceneCenterY);
5766
+ const projectedHorizontalPoint = projectScenePoint(transformScenePoint(localHorizontal, center, rotationX, rotationY), size, sceneCenterX, sceneCenterY);
5767
+ const projectedVerticalPoint = projectScenePoint(transformScenePoint(localVertical, center, rotationX, rotationY), size, sceneCenterX, sceneCenterY);
5768
+ const horizontalRadius = clampNumber$1(Math.hypot(projectedHorizontalPoint.x - projectedCenterPoint.x, projectedHorizontalPoint.y - projectedCenterPoint.y) *
5769
+ radiusScale *
5770
+ 0.74, size * 0.003, size * 0.018);
5771
+ const verticalRadius = clampNumber$1(Math.hypot(projectedVerticalPoint.x - projectedCenterPoint.x, projectedVerticalPoint.y - projectedCenterPoint.y) *
5772
+ radiusScale *
5773
+ 0.52, size * 0.0024, size * 0.014);
5774
+ const rotation = Math.atan2(projectedHorizontalPoint.y - projectedCenterPoint.y, projectedHorizontalPoint.x - projectedCenterPoint.x);
5775
+ context.save();
5776
+ context.translate(projectedCenterPoint.x, projectedCenterPoint.y);
5777
+ context.rotate(rotation);
5778
+ context.beginPath();
5779
+ context.ellipse(0, 0, horizontalRadius, verticalRadius, 0, 0, Math.PI * 2);
5780
+ context.fillStyle = `${palette.highlight}73`;
5781
+ context.fill();
5782
+ context.strokeStyle = `${palette.highlight}99`;
5783
+ context.lineWidth = Math.max(0.7, size * 0.0028);
5784
+ context.stroke();
5785
+ context.restore();
5786
+ }
5787
+ /**
5788
+ * Resolves a signed angular distance from the source longitude to the target longitude.
5789
+ *
5790
+ * @private helper of `octopus3d3AvatarVisual`
5791
+ */
5792
+ function resolveSignedAngularDistance(sourceLongitude, targetLongitude) {
5793
+ return Math.atan2(Math.sin(targetLongitude - sourceLongitude), Math.cos(targetLongitude - sourceLongitude));
5794
+ }
5795
+ /**
5796
+ * Smoothly maps a value between two bounds into `[0, 1]`.
5797
+ *
5798
+ * @private helper of `octopus3d3AvatarVisual`
5799
+ */
5800
+ function smoothStep(edgeStart, edgeEnd, value) {
5801
+ const progress = clampNumber$1((value - edgeStart) / (edgeEnd - edgeStart), 0, 1);
5802
+ return progress * progress * (3 - 2 * progress);
5803
+ }
5804
+
5297
5805
  /* eslint-disable no-magic-numbers */
5298
5806
  /**
5299
5807
  * Octopus avatar visual.
@@ -6064,6 +6572,7 @@
6064
6572
  octopus3AvatarVisual,
6065
6573
  octopus3dAvatarVisual,
6066
6574
  octopus3d2AvatarVisual,
6575
+ octopus3d3AvatarVisual,
6067
6576
  asciiOctopusAvatarVisual,
6068
6577
  minecraftAvatarVisual,
6069
6578
  minecraft2AvatarVisual,
@@ -6145,7 +6654,7 @@
6145
6654
  *
6146
6655
  * @private shared avatar contract
6147
6656
  */
6148
- const DEFAULT_AGENT_AVATAR_VISUAL_ID = 'octopus3';
6657
+ const DEFAULT_AGENT_AVATAR_VISUAL_ID = 'octopus3d3';
6149
6658
  /**
6150
6659
  * Resolves the avatar visual preferred by an agent, then falls back to a federated server default
6151
6660
  * and finally to the caller/server default.
@@ -22869,6 +23378,30 @@
22869
23378
  DIAGNOSTIC_MARKER_OWNER: 'book-editor-diagnostics',
22870
23379
  };
22871
23380
 
23381
+ /**
23382
+ * Theme used for the actual notebook editor surface.
23383
+ *
23384
+ * The host application can still pass its resolved app theme, but book sources
23385
+ * are intentionally rendered as light paper in both light and dark app modes.
23386
+ *
23387
+ * @private internal constant of `BookEditor`
23388
+ */
23389
+ const BOOK_EDITOR_RENDER_THEME = 'LIGHT';
23390
+ /**
23391
+ * Resolves an app-level theme into the visual theme used by `<BookEditor/>`.
23392
+ *
23393
+ * @param hostTheme - Resolved theme from the embedding application.
23394
+ * @returns Light theme used by the Book editor surface.
23395
+ *
23396
+ * @private internal utility of `BookEditor`
23397
+ */
23398
+ function resolveBookEditorRenderTheme(hostTheme = BOOK_EDITOR_RENDER_THEME) {
23399
+ if (hostTheme === 'DARK') {
23400
+ return BOOK_EDITOR_RENDER_THEME;
23401
+ }
23402
+ return hostTheme;
23403
+ }
23404
+
22872
23405
  /**
22873
23406
  * Gets a commitment definition by its type
22874
23407
  *
@@ -23878,19 +24411,20 @@
23878
24411
  *
23879
24412
  * @private function of BookEditorMonaco
23880
24413
  */
23881
- function ensureBookEditorMonacoLanguage(monaco, theme = 'LIGHT') {
24414
+ function ensureBookEditorMonacoLanguage(monaco, theme = BOOK_EDITOR_RENDER_THEME) {
24415
+ const renderedTheme = resolveBookEditorRenderTheme(theme);
23882
24416
  const monacoWithLanguageState = monaco;
23883
24417
  if (monacoWithLanguageState[BOOK_EDITOR_LANGUAGE_INITIALIZED_FLAG]) {
23884
- if (monacoWithLanguageState[BOOK_EDITOR_THEME_MODE_FLAG] !== theme) {
23885
- applyBookEditorMonacoTheme(monaco, theme);
23886
- monacoWithLanguageState[BOOK_EDITOR_THEME_MODE_FLAG] = theme;
24418
+ if (monacoWithLanguageState[BOOK_EDITOR_THEME_MODE_FLAG] !== renderedTheme) {
24419
+ applyBookEditorMonacoTheme(monaco, renderedTheme);
24420
+ monacoWithLanguageState[BOOK_EDITOR_THEME_MODE_FLAG] = renderedTheme;
23887
24421
  return;
23888
24422
  }
23889
24423
  monaco.editor.setTheme(BookEditorMonacoConstants.BOOK_THEME_ID);
23890
24424
  return;
23891
24425
  }
23892
24426
  monacoWithLanguageState[BOOK_EDITOR_LANGUAGE_INITIALIZED_FLAG] = true;
23893
- monacoWithLanguageState[BOOK_EDITOR_THEME_MODE_FLAG] = theme;
24427
+ monacoWithLanguageState[BOOK_EDITOR_THEME_MODE_FLAG] = renderedTheme;
23894
24428
  monaco.languages.register({ id: BookEditorMonacoConstants.BOOK_LANGUAGE_ID });
23895
24429
  const commitmentDefinitions = getAllCommitmentDefinitions();
23896
24430
  const commitmentTypes = [...new Set(commitmentDefinitions.map(({ type }) => type))];
@@ -24000,7 +24534,7 @@
24000
24534
  return { links };
24001
24535
  },
24002
24536
  });
24003
- applyBookEditorMonacoTheme(monaco, theme);
24537
+ applyBookEditorMonacoTheme(monaco, renderedTheme);
24004
24538
  }
24005
24539
  /**
24006
24540
  * Ensures the mounted Monaco editor model uses Book language and Book theme.
@@ -24013,8 +24547,9 @@
24013
24547
  * @private function of BookEditorMonaco
24014
24548
  */
24015
24549
  function ensureBookEditorMonacoLanguageForEditor(props) {
24016
- const { monaco, monacoEditor, theme = 'LIGHT' } = props;
24017
- ensureBookEditorMonacoLanguage(monaco, theme);
24550
+ const { monaco, monacoEditor, theme = BOOK_EDITOR_RENDER_THEME } = props;
24551
+ const renderedTheme = resolveBookEditorRenderTheme(theme);
24552
+ ensureBookEditorMonacoLanguage(monaco, renderedTheme);
24018
24553
  const model = monacoEditor.getModel();
24019
24554
  if (!model) {
24020
24555
  return;
@@ -25246,7 +25781,8 @@
25246
25781
  * @private Internal component used by `BookEditor`
25247
25782
  */
25248
25783
  function BookEditorMonaco(props) {
25249
- const { value, onChange, diagnostics, isReadonly, theme = 'LIGHT', translations, onFileUpload, isUploadButtonShown, isCameraButtonShown, isDownloadButtonShown, isAboutButtonShown = true, isFullscreenButtonShown = true, onFullscreenClick, isFullscreen, zoom = 1, monacoModelPath, hoistedMenuItems, } = props;
25784
+ const { value, onChange, diagnostics, isReadonly, theme: hostTheme = BOOK_EDITOR_RENDER_THEME, translations, onFileUpload, isUploadButtonShown, isCameraButtonShown, isDownloadButtonShown, isAboutButtonShown = true, isFullscreenButtonShown = true, onFullscreenClick, isFullscreen, zoom = 1, monacoModelPath, hoistedMenuItems, } = props;
25785
+ const theme = resolveBookEditorRenderTheme(hostTheme);
25250
25786
  const zoomLevel = zoom;
25251
25787
  const { scaledLineHeight, scaledContentPaddingLeft, scaledVerticalLineLeft, scaledFontSize, scaledScrollbarSize } = createBookEditorMonacoScale(zoomLevel);
25252
25788
  const monaco = MonacoEditor.useMonaco();
@@ -25335,15 +25871,16 @@
25335
25871
  * @public exported from `@promptbook/components`
25336
25872
  */
25337
25873
  function BookEditor(props) {
25338
- const { agentSource, className, style, theme = 'LIGHT', zoom = 1, value, onChange, diagnostics, onFileUpload, isVerbose = DEFAULT_IS_VERBOSE, isBorderRadiusDisabled = false, isReadonly = false, translations, isUploadButtonShown = true, isCameraButtonShown, isDownloadButtonShown = true, isAboutButtonShown = true, isFullscreenButtonShown = true, sync, monacoModelPath, hoistedMenuItems, } = props;
25874
+ const { agentSource, className, style, theme: hostTheme = BOOK_EDITOR_RENDER_THEME, zoom = 1, value, onChange, diagnostics, onFileUpload, isVerbose = DEFAULT_IS_VERBOSE, isBorderRadiusDisabled = false, isReadonly = false, translations, isUploadButtonShown = true, isCameraButtonShown, isDownloadButtonShown = true, isAboutButtonShown = true, isFullscreenButtonShown = true, sync, monacoModelPath, hoistedMenuItems, } = props;
25339
25875
  const [isFullscreen, setIsFullscreen] = react.useState(false);
25876
+ const renderedTheme = resolveBookEditorRenderTheme(hostTheme);
25340
25877
  /**
25341
25878
  * Toggles fullscreen mode for the editor.
25342
25879
  */
25343
25880
  const handleFullscreenToggle = () => {
25344
25881
  setIsFullscreen(!isFullscreen);
25345
25882
  };
25346
- const editorContent = (jsxRuntime.jsx("div", { "data-book-component": "BookEditor", "data-book-editor-theme": theme.toLowerCase(), className: classNames(styles$d.BookEditor, isVerbose && styles$d.isVerbose, styles$d.bookEditorWrapper, isBorderRadiusDisabled && styles$d.isBorderRadiusDisabled, isFullscreen && styles$d.fullscreen, className), style: isFullscreen
25883
+ const editorContent = (jsxRuntime.jsx("div", { "data-book-component": "BookEditor", "data-book-editor-theme": renderedTheme.toLowerCase(), className: classNames(styles$d.BookEditor, isVerbose && styles$d.isVerbose, styles$d.bookEditorWrapper, isBorderRadiusDisabled && styles$d.isBorderRadiusDisabled, isFullscreen && styles$d.fullscreen, className), style: isFullscreen
25347
25884
  ? style
25348
25885
  : {
25349
25886
  ...(style || {}),
@@ -25354,7 +25891,7 @@
25354
25891
  ? `${props.height}px`
25355
25892
  : props.height || `${DEFAULT_BOOK_EDITOR_HEIGHT}px`,
25356
25893
  }),
25357
- }, children: jsxRuntime.jsx(BookEditorMonaco, { value: agentSource || value, onChange: onChange, diagnostics: diagnostics, onFileUpload: onFileUpload, isVerbose: isVerbose, isBorderRadiusDisabled: isBorderRadiusDisabled, isReadonly: isReadonly, translations: translations, isUploadButtonShown: isUploadButtonShown, isCameraButtonShown: isCameraButtonShown, isDownloadButtonShown: isDownloadButtonShown, isAboutButtonShown: isAboutButtonShown, isFullscreenButtonShown: isFullscreenButtonShown, onFullscreenClick: handleFullscreenToggle, isFullscreen: isFullscreen, sync: sync, zoom: zoom, monacoModelPath: monacoModelPath, hoistedMenuItems: hoistedMenuItems, theme: theme }) }));
25894
+ }, children: jsxRuntime.jsx(BookEditorMonaco, { value: agentSource || value, onChange: onChange, diagnostics: diagnostics, onFileUpload: onFileUpload, isVerbose: isVerbose, isBorderRadiusDisabled: isBorderRadiusDisabled, isReadonly: isReadonly, translations: translations, isUploadButtonShown: isUploadButtonShown, isCameraButtonShown: isCameraButtonShown, isDownloadButtonShown: isDownloadButtonShown, isAboutButtonShown: isAboutButtonShown, isFullscreenButtonShown: isFullscreenButtonShown, onFullscreenClick: handleFullscreenToggle, isFullscreen: isFullscreen, sync: sync, zoom: zoom, monacoModelPath: monacoModelPath, hoistedMenuItems: hoistedMenuItems, theme: renderedTheme }) }));
25358
25895
  return isFullscreen && typeof document !== 'undefined' ? reactDom.createPortal(editorContent, document.body) : editorContent;
25359
25896
  }
25360
25897