@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/esm/index.es.js CHANGED
@@ -42,7 +42,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
42
42
  * @generated
43
43
  * @see https://github.com/webgptorg/promptbook
44
44
  */
45
- const PROMPTBOOK_ENGINE_VERSION = '0.112.0-103';
45
+ const PROMPTBOOK_ENGINE_VERSION = '0.112.0-105';
46
46
  /**
47
47
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
48
48
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -3254,7 +3254,7 @@ function fillTextureRect(texture, x, y, width, height, color) {
3254
3254
  *
3255
3255
  * @private helper of `minecraft2AvatarVisual`
3256
3256
  */
3257
- const LIGHT_DIRECTION$2 = normalizeVector3({
3257
+ const LIGHT_DIRECTION$3 = normalizeVector3({
3258
3258
  x: 0.4,
3259
3259
  y: -0.65,
3260
3260
  z: 0.92,
@@ -3466,7 +3466,7 @@ function resolveVisibleCuboidFaces(cuboid, size, sceneCenterX, sceneCenterY) {
3466
3466
  corners: projectedCorners,
3467
3467
  texture: faceDefinition.texture,
3468
3468
  averageDepth: transformedCorners.reduce((depthSum, corner) => depthSum + corner.z, 0) / transformedCorners.length,
3469
- lightIntensity: clampNumber$1(dotProduct3D(faceNormal, LIGHT_DIRECTION$2), -1, 1),
3469
+ lightIntensity: clampNumber$1(dotProduct3D(faceNormal, LIGHT_DIRECTION$3), -1, 1),
3470
3470
  outlineColor: cuboid.outlineColor,
3471
3471
  };
3472
3472
  });
@@ -4657,7 +4657,7 @@ function formatAlphaHex(opacity) {
4657
4657
  *
4658
4658
  * @private helper of `octopus3dAvatarVisual`
4659
4659
  */
4660
- const LIGHT_DIRECTION$1 = normalizeVector3({
4660
+ const LIGHT_DIRECTION$2 = normalizeVector3({
4661
4661
  x: 0.48,
4662
4662
  y: -0.62,
4663
4663
  z: 0.94,
@@ -4867,7 +4867,7 @@ function resolveVisibleEllipsoidPatches(options) {
4867
4867
  corners: projectedCorners,
4868
4868
  averageDepth: transformedCorners.reduce((depthSum, transformedCorner) => depthSum + transformedCorner.z, 0) /
4869
4869
  transformedCorners.length,
4870
- lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$1), -1, 1),
4870
+ lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$2), -1, 1),
4871
4871
  fillStyle: resolveSurfacePatchFillStyle(palette, verticalProgress + verticalColorBias),
4872
4872
  outlineColor,
4873
4873
  });
@@ -5056,7 +5056,7 @@ function resolveEllipsoidSurfaceDepth(radiusX, radiusY, radiusZ, x, y) {
5056
5056
  *
5057
5057
  * @private helper of `octopus3d2AvatarVisual`
5058
5058
  */
5059
- const LIGHT_DIRECTION = normalizeVector3({
5059
+ const LIGHT_DIRECTION$1 = normalizeVector3({
5060
5060
  x: 0.38,
5061
5061
  y: -0.6,
5062
5062
  z: 0.98,
@@ -5205,7 +5205,7 @@ function resolveVisibleBlobbyOctopusPatches(options) {
5205
5205
  corners: projectedCorners,
5206
5206
  averageDepth: transformedCorners.reduce((depthSum, transformedCorner) => depthSum + transformedCorner.z, 0) /
5207
5207
  transformedCorners.length,
5208
- lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION), -1, 1),
5208
+ lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$1), -1, 1),
5209
5209
  fillStyle: resolveBlobbySurfacePatchFillStyle(palette, verticalProgress, Math.max(0, Math.cos(centerLongitude)), resolveLowerLobeWave(centerLongitude, morphologyProfile, animationPhase, timeMs)),
5210
5210
  outlineColor: verticalProgress < 0.58 ? `${palette.highlight}73` : `${palette.shadow}8a`,
5211
5211
  });
@@ -5305,6 +5305,514 @@ function drawBlobbySurfacePatch(context, surfacePatch) {
5305
5305
  context.restore();
5306
5306
  }
5307
5307
 
5308
+ /* eslint-disable no-magic-numbers */
5309
+ /**
5310
+ * Light direction used by the continuous octopus mesh shading.
5311
+ *
5312
+ * @private helper of `octopus3d3AvatarVisual`
5313
+ */
5314
+ const LIGHT_DIRECTION = normalizeVector3({
5315
+ x: 0.34,
5316
+ y: -0.62,
5317
+ z: 1,
5318
+ });
5319
+ /**
5320
+ * Real-octopus tentacle count used by the continuous lower mesh.
5321
+ *
5322
+ * @private helper of `octopus3d3AvatarVisual`
5323
+ */
5324
+ const OCTOPUS_TENTACLE_COUNT = 8;
5325
+ /**
5326
+ * Octopus 3D 3 avatar visual.
5327
+ *
5328
+ * @private built-in avatar visual
5329
+ */
5330
+ const octopus3d3AvatarVisual = {
5331
+ id: 'octopus3d3',
5332
+ title: 'Octopus 3D 3',
5333
+ description: 'Cute continuous 3D octopus with a blobby single mesh, waving tentacle lobes, rich shading, and cursor-aware eyes.',
5334
+ isAnimated: true,
5335
+ supportsPointerTracking: true,
5336
+ render({ context, size, palette, createRandom, timeMs, interaction }) {
5337
+ const morphologyProfile = createOctopus3MorphologyProfile(createRandom);
5338
+ const animationRandom = createRandom('octopus3d3-animation-profile');
5339
+ const eyeRandom = createRandom('octopus3d3-eye-profile');
5340
+ const animationPhase = animationRandom() * Math.PI * 2;
5341
+ const tentacleProfiles = createContinuousTentacleProfiles(createRandom, morphologyProfile);
5342
+ const sceneCenterX = size * 0.5;
5343
+ const sceneCenterY = size * 0.535;
5344
+ const bob = Math.sin(timeMs / 960 + animationPhase) * size * 0.012;
5345
+ const meshCenter = {
5346
+ x: interaction.bodyOffsetX * size * 0.048 + size * morphologyProfile.body.centerXJitterRatio * 0.44,
5347
+ y: -size * 0.07 + interaction.bodyOffsetY * size * 0.026 + bob,
5348
+ z: interaction.intensity * size * 0.018,
5349
+ };
5350
+ const rotationY = -0.1 +
5351
+ Math.sin(timeMs / 2700 + animationPhase) * 0.035 +
5352
+ interaction.bodyOffsetX * 0.22 +
5353
+ interaction.gazeX * 0.88;
5354
+ const rotationX = -0.07 +
5355
+ Math.cos(timeMs / 3100 + animationPhase * 0.7) * 0.018 -
5356
+ interaction.bodyOffsetY * 0.08 -
5357
+ interaction.gazeY * 0.38;
5358
+ const surfaceOptions = {
5359
+ radiusX: size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.horizontalStretch * 1.1,
5360
+ radiusY: size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.verticalStretch * 1.08,
5361
+ radiusZ: size *
5362
+ morphologyProfile.body.bodyRadiusRatio *
5363
+ (1.02 + (morphologyProfile.body.horizontalStretch - 1) * 0.18),
5364
+ morphologyProfile,
5365
+ timeMs,
5366
+ animationPhase,
5367
+ tentacleProfiles,
5368
+ };
5369
+ const surfacePatches = resolveVisibleContinuousOctopusPatches({
5370
+ ...surfaceOptions,
5371
+ center: meshCenter,
5372
+ rotationX,
5373
+ rotationY,
5374
+ sceneCenterX,
5375
+ sceneCenterY,
5376
+ size,
5377
+ palette,
5378
+ });
5379
+ const eyeLatitude = clampNumber$1(morphologyProfile.face.eyeCenterYOffsetRatio * 4.2 - 0.03, -0.22, 0.08);
5380
+ const eyeLongitude = clampNumber$1(morphologyProfile.face.eyeSpacingRatio * 3.1, 0.18, 0.32);
5381
+ const mouthLatitude = clampNumber$1(eyeLatitude + 0.2 + morphologyProfile.face.mouthYOffsetRatio, 0.08, 0.34);
5382
+ const mouthCenterLongitude = clampNumber$1(morphologyProfile.face.mouthCenterOffsetRatio * 5.6, -0.08, 0.08);
5383
+ const mouthHalfLongitude = clampNumber$1(eyeLongitude * 0.78, 0.15, 0.28);
5384
+ const mouthCurveLatitude = clampNumber$1(mouthLatitude + morphologyProfile.face.mouthCurveDepthRatio * 0.78, mouthLatitude + 0.03, 0.42);
5385
+ const eyeRadiusX = size * morphologyProfile.face.eyeRadiusXRatio * 0.76;
5386
+ const eyeRadiusY = eyeRadiusX * morphologyProfile.face.eyeHeightRatio * 0.9;
5387
+ drawAvatarFrame(context, size, palette);
5388
+ drawContinuousOctopusAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs);
5389
+ drawContinuousOctopusShadow(context, size, palette, interaction, timeMs, morphologyProfile);
5390
+ for (const surfacePatch of surfacePatches.sort((firstSurfacePatch, secondSurfacePatch) => firstSurfacePatch.averageDepth - secondSurfacePatch.averageDepth)) {
5391
+ drawContinuousSurfacePatch(context, surfacePatch);
5392
+ }
5393
+ drawProjectedSurfaceCurrents({
5394
+ context,
5395
+ surfaceOptions,
5396
+ center: meshCenter,
5397
+ rotationX,
5398
+ rotationY,
5399
+ sceneCenterX,
5400
+ sceneCenterY,
5401
+ size,
5402
+ palette,
5403
+ morphologyProfile,
5404
+ timeMs,
5405
+ animationPhase,
5406
+ });
5407
+ drawProjectedTentacleSuckers({
5408
+ context,
5409
+ surfaceOptions,
5410
+ center: meshCenter,
5411
+ rotationX,
5412
+ rotationY,
5413
+ sceneCenterX,
5414
+ sceneCenterY,
5415
+ size,
5416
+ palette,
5417
+ });
5418
+ drawProjectedOrganicEye(context, sampleContinuousOctopusSurfacePoint(surfaceOptions, eyeLatitude, -eyeLongitude), eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + eyeRandom() * 0.7, interaction, morphologyProfile.face.eyeStyle);
5419
+ 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);
5420
+ drawProjectedOrganicMouth(context, [
5421
+ sampleContinuousOctopusSurfacePoint(surfaceOptions, mouthLatitude, mouthCenterLongitude - mouthHalfLongitude),
5422
+ sampleContinuousOctopusSurfacePoint(surfaceOptions, mouthCurveLatitude, mouthCenterLongitude),
5423
+ sampleContinuousOctopusSurfacePoint(surfaceOptions, mouthLatitude, mouthCenterLongitude + mouthHalfLongitude),
5424
+ ], meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, palette, size);
5425
+ },
5426
+ };
5427
+ /**
5428
+ * Creates seeded tentacle-lobe profiles around the visible lower octopus body.
5429
+ *
5430
+ * @private helper of `octopus3d3AvatarVisual`
5431
+ */
5432
+ function createContinuousTentacleProfiles(createRandom, morphologyProfile) {
5433
+ return Array.from({ length: OCTOPUS_TENTACLE_COUNT }, (_, tentacleIndex) => {
5434
+ const tentacleRandom = createRandom(`octopus3d3-tentacle-${tentacleIndex}`);
5435
+ const progress = tentacleIndex / (OCTOPUS_TENTACLE_COUNT - 1);
5436
+ return {
5437
+ centerLongitude: -Math.PI * 0.86 +
5438
+ progress * Math.PI * 1.72 +
5439
+ (tentacleRandom() - 0.5) * (0.08 + morphologyProfile.tentacles.rootSpreadScale * 0.03),
5440
+ widthScale: 0.86 + tentacleRandom() * 0.34 + (morphologyProfile.tentacles.baseWidthScale - 1) * 0.16,
5441
+ lengthScale: 0.86 + tentacleRandom() * 0.36 + (morphologyProfile.tentacles.flowLengthScale - 1) * 0.22,
5442
+ swayScale: 0.82 + tentacleRandom() * 0.38 + (morphologyProfile.tentacles.swayScale - 1) * 0.2,
5443
+ depthScale: 0.86 + tentacleRandom() * 0.32 + (morphologyProfile.tentacles.tipReachScale - 1) * 0.2,
5444
+ phase: tentacleRandom() * Math.PI * 2,
5445
+ suckerSide: tentacleRandom() > 0.5 ? 1 : -1,
5446
+ };
5447
+ });
5448
+ }
5449
+ /**
5450
+ * Draws the soft underwater atmosphere behind the continuous octopus mesh.
5451
+ *
5452
+ * @private helper of `octopus3d3AvatarVisual`
5453
+ */
5454
+ function drawContinuousOctopusAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs) {
5455
+ 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));
5456
+ glowGradient.addColorStop(0, `${palette.highlight}66`);
5457
+ glowGradient.addColorStop(0.34, `${palette.accent}2e`);
5458
+ glowGradient.addColorStop(1, `${palette.highlight}00`);
5459
+ context.fillStyle = glowGradient;
5460
+ context.fillRect(0, 0, size, size);
5461
+ 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);
5462
+ lowerGradient.addColorStop(0, `${palette.secondary}25`);
5463
+ lowerGradient.addColorStop(1, `${palette.secondary}00`);
5464
+ context.fillStyle = lowerGradient;
5465
+ context.fillRect(0, 0, size, size);
5466
+ }
5467
+ /**
5468
+ * Draws the soft lower shadow that anchors the octopus in the avatar frame.
5469
+ *
5470
+ * @private helper of `octopus3d3AvatarVisual`
5471
+ */
5472
+ function drawContinuousOctopusShadow(context, size, palette, interaction, timeMs, morphologyProfile) {
5473
+ context.save();
5474
+ context.fillStyle = `${palette.shadow}66`;
5475
+ context.filter = `blur(${size * 0.025}px)`;
5476
+ context.beginPath();
5477
+ 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);
5478
+ context.fill();
5479
+ context.restore();
5480
+ }
5481
+ /**
5482
+ * Resolves visible projected patches for the continuous octopus mesh.
5483
+ *
5484
+ * @private helper of `octopus3d3AvatarVisual`
5485
+ */
5486
+ function resolveVisibleContinuousOctopusPatches(options) {
5487
+ const { center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette } = options;
5488
+ const latitudePatchCount = 16;
5489
+ const longitudePatchCount = 40;
5490
+ const surfacePatches = [];
5491
+ for (let latitudeIndex = 0; latitudeIndex < latitudePatchCount; latitudeIndex++) {
5492
+ const startLatitude = -Math.PI / 2 + (latitudeIndex / latitudePatchCount) * Math.PI;
5493
+ const endLatitude = -Math.PI / 2 + ((latitudeIndex + 1) / latitudePatchCount) * Math.PI;
5494
+ const centerLatitude = (startLatitude + endLatitude) / 2;
5495
+ const verticalProgress = (Math.sin(centerLatitude) + 1) / 2;
5496
+ for (let longitudeIndex = 0; longitudeIndex < longitudePatchCount; longitudeIndex++) {
5497
+ const startLongitude = -Math.PI + (longitudeIndex / longitudePatchCount) * Math.PI * 2;
5498
+ const endLongitude = -Math.PI + ((longitudeIndex + 1) / longitudePatchCount) * Math.PI * 2;
5499
+ const centerLongitude = (startLongitude + endLongitude) / 2;
5500
+ const localCorners = [
5501
+ sampleContinuousOctopusSurfacePoint(options, startLatitude, startLongitude),
5502
+ sampleContinuousOctopusSurfacePoint(options, startLatitude, endLongitude),
5503
+ sampleContinuousOctopusSurfacePoint(options, endLatitude, endLongitude),
5504
+ sampleContinuousOctopusSurfacePoint(options, endLatitude, startLongitude),
5505
+ ];
5506
+ const transformedCorners = localCorners.map((localCorner) => transformScenePoint(localCorner, center, rotationX, rotationY));
5507
+ const surfaceNormal = normalizeVector3(crossProduct3D(subtractPoint3D(transformedCorners[1], transformedCorners[0]), subtractPoint3D(transformedCorners[2], transformedCorners[0])));
5508
+ if (surfaceNormal.z <= 0.008) {
5509
+ continue;
5510
+ }
5511
+ const projectedCorners = transformedCorners.map((transformedCorner) => projectScenePoint(transformedCorner, size, sceneCenterX, sceneCenterY));
5512
+ const tentacleInfluence = resolveContinuousTentacleInfluence(options, centerLongitude);
5513
+ const lowerLobeWave = resolveContinuousLobeWave(options, centerLongitude);
5514
+ surfacePatches.push({
5515
+ corners: projectedCorners,
5516
+ averageDepth: transformedCorners.reduce((depthSum, transformedCorner) => depthSum + transformedCorner.z, 0) /
5517
+ transformedCorners.length,
5518
+ lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION), -1, 1),
5519
+ fillStyle: resolveContinuousSurfacePatchFillStyle(palette, verticalProgress, Math.max(0, Math.cos(centerLongitude)), tentacleInfluence.core, lowerLobeWave),
5520
+ outlineColor: verticalProgress < 0.54 ? `${palette.highlight}69` : `${palette.shadow}78`,
5521
+ });
5522
+ }
5523
+ }
5524
+ return surfacePatches;
5525
+ }
5526
+ /**
5527
+ * Samples one point on the continuous Octopus 3D 3 surface.
5528
+ *
5529
+ * The lower hemisphere is pulled into eight seeded waving lobes, so the portrait reads as
5530
+ * tentacled while still being rendered as one connected blobby mesh.
5531
+ *
5532
+ * @private helper of `octopus3d3AvatarVisual`
5533
+ */
5534
+ function sampleContinuousOctopusSurfacePoint(options, latitude, longitude) {
5535
+ const { radiusX, radiusY, radiusZ, morphologyProfile, timeMs, animationPhase } = options;
5536
+ const cosineLatitude = Math.max(0, Math.cos(latitude));
5537
+ const verticalProgress = (Math.sin(latitude) + 1) / 2;
5538
+ const upperBlend = Math.pow(1 - verticalProgress, 1.28);
5539
+ const lowerBlend = smoothStep(0.38, 1, verticalProgress);
5540
+ const tipBlend = smoothStep(0.68, 1, verticalProgress);
5541
+ const tentacleInfluence = resolveContinuousTentacleInfluence(options, longitude);
5542
+ const centerPull = resolveSignedAngularDistance(longitude, tentacleInfluence.centerLongitude);
5543
+ const effectiveLongitude = longitude + centerPull * lowerBlend * tentacleInfluence.core * (0.24 + tipBlend * 0.2);
5544
+ const lowerLobeWave = resolveContinuousLobeWave(options, longitude);
5545
+ const mantleRipple = Math.sin(longitude * morphologyProfile.body.lobeCount +
5546
+ animationPhase * 0.6 +
5547
+ timeMs / (1750 + morphologyProfile.body.lobeCount * 30)) *
5548
+ (0.018 + morphologyProfile.body.wobbleAmplitudeRatio * 0.8) *
5549
+ (0.3 + lowerBlend * 0.7);
5550
+ const tentacleWave = Math.sin(timeMs / 760 + tentacleInfluence.phase + verticalProgress * 2.4) *
5551
+ lowerBlend *
5552
+ tentacleInfluence.core *
5553
+ tentacleInfluence.swayScale;
5554
+ const horizontalScale = 1.04 +
5555
+ mantleRipple +
5556
+ lowerBlend * (0.16 + (morphologyProfile.tentacles.rootSpreadScale - 1) * 0.1) +
5557
+ lowerBlend * tentacleInfluence.core * (0.2 + lowerLobeWave * 0.12) -
5558
+ upperBlend * 0.08;
5559
+ const depthScale = 1.06 +
5560
+ upperBlend * 0.16 +
5561
+ Math.max(0, Math.cos(effectiveLongitude)) * 0.1 +
5562
+ lowerBlend * tentacleInfluence.core * (0.1 + tentacleInfluence.depthScale * 0.06) -
5563
+ Math.max(0, -Math.cos(effectiveLongitude)) * 0.05;
5564
+ const tentacleTubeRadius = lowerBlend * tentacleInfluence.core * (0.11 + tipBlend * 0.06 + tentacleInfluence.widthScale * 0.025) * radiusX;
5565
+ const planarRadiusX = cosineLatitude * radiusX * horizontalScale + tentacleTubeRadius;
5566
+ const planarRadiusZ = cosineLatitude * radiusZ * depthScale + tentacleTubeRadius * 0.72;
5567
+ const lowerDrop = lowerBlend *
5568
+ radiusY *
5569
+ (0.18 +
5570
+ tentacleInfluence.core *
5571
+ (0.38 +
5572
+ tentacleInfluence.lengthScale * 0.22 +
5573
+ (morphologyProfile.tentacles.flowLengthScale - 1) * 0.08));
5574
+ return {
5575
+ x: Math.sin(effectiveLongitude) * planarRadiusX + tentacleWave * radiusX * (0.052 + tipBlend * 0.05),
5576
+ y: Math.sin(latitude) * radiusY * (1 + upperBlend * 0.12) -
5577
+ upperBlend * radiusY * 0.1 +
5578
+ lowerDrop +
5579
+ Math.sin(timeMs / 1420 + animationPhase + latitude * 1.6) * lowerBlend * radiusY * 0.018 +
5580
+ Math.cos(timeMs / 880 + tentacleInfluence.phase) *
5581
+ lowerBlend *
5582
+ tipBlend *
5583
+ tentacleInfluence.core *
5584
+ radiusY *
5585
+ 0.034,
5586
+ z: Math.cos(effectiveLongitude) * planarRadiusZ +
5587
+ Math.cos(timeMs / 980 + tentacleInfluence.phase + verticalProgress) *
5588
+ lowerBlend *
5589
+ tentacleInfluence.core *
5590
+ radiusZ *
5591
+ 0.04,
5592
+ };
5593
+ }
5594
+ /**
5595
+ * Blends nearby seeded tentacle profiles at one mesh longitude.
5596
+ *
5597
+ * @private helper of `octopus3d3AvatarVisual`
5598
+ */
5599
+ function resolveContinuousTentacleInfluence(options, longitude) {
5600
+ let totalWeight = 0;
5601
+ let weightedSin = 0;
5602
+ let weightedCos = 0;
5603
+ let weightedWidthScale = 0;
5604
+ let weightedLengthScale = 0;
5605
+ let weightedSwayScale = 0;
5606
+ let weightedDepthScale = 0;
5607
+ let weightedPhase = 0;
5608
+ for (const tentacleProfile of options.tentacleProfiles) {
5609
+ const distance = Math.abs(resolveSignedAngularDistance(longitude, tentacleProfile.centerLongitude));
5610
+ const width = 0.2 * tentacleProfile.widthScale;
5611
+ const weight = Math.exp(-(distance * distance) / (width * width));
5612
+ totalWeight += weight;
5613
+ weightedSin += Math.sin(tentacleProfile.centerLongitude) * weight;
5614
+ weightedCos += Math.cos(tentacleProfile.centerLongitude) * weight;
5615
+ weightedWidthScale += tentacleProfile.widthScale * weight;
5616
+ weightedLengthScale += tentacleProfile.lengthScale * weight;
5617
+ weightedSwayScale += tentacleProfile.swayScale * weight;
5618
+ weightedDepthScale += tentacleProfile.depthScale * weight;
5619
+ weightedPhase += tentacleProfile.phase * weight;
5620
+ }
5621
+ if (totalWeight < 0.0001) {
5622
+ return {
5623
+ core: 0,
5624
+ centerLongitude: longitude,
5625
+ widthScale: 1,
5626
+ lengthScale: 1,
5627
+ swayScale: 1,
5628
+ depthScale: 1,
5629
+ phase: 0,
5630
+ };
5631
+ }
5632
+ return {
5633
+ core: clampNumber$1(totalWeight, 0, 1),
5634
+ centerLongitude: Math.atan2(weightedSin / totalWeight, weightedCos / totalWeight),
5635
+ widthScale: weightedWidthScale / totalWeight,
5636
+ lengthScale: weightedLengthScale / totalWeight,
5637
+ swayScale: weightedSwayScale / totalWeight,
5638
+ depthScale: weightedDepthScale / totalWeight,
5639
+ phase: weightedPhase / totalWeight,
5640
+ };
5641
+ }
5642
+ /**
5643
+ * Resolves the soft lower wave that makes the continuous mesh read as a set of tentacles.
5644
+ *
5645
+ * @private helper of `octopus3d3AvatarVisual`
5646
+ */
5647
+ function resolveContinuousLobeWave(options, longitude) {
5648
+ const { morphologyProfile, animationPhase, timeMs } = options;
5649
+ return ((Math.cos(longitude * OCTOPUS_TENTACLE_COUNT +
5650
+ animationPhase +
5651
+ timeMs / (980 + morphologyProfile.body.lobeCount * 18)) +
5652
+ 1) /
5653
+ 2);
5654
+ }
5655
+ /**
5656
+ * Resolves one base fill tone for a patch on the continuous octopus mesh.
5657
+ *
5658
+ * @private helper of `octopus3d3AvatarVisual`
5659
+ */
5660
+ function resolveContinuousSurfacePatchFillStyle(palette, verticalProgress, forwardness, tentacleCore, lowerLobeWave) {
5661
+ const tonalProgress = clampNumber$1(verticalProgress + lowerLobeWave * 0.1 + tentacleCore * 0.08 - forwardness * 0.08, 0, 1);
5662
+ if (tonalProgress < 0.14) {
5663
+ return palette.highlight;
5664
+ }
5665
+ if (tonalProgress < 0.32) {
5666
+ return palette.secondary;
5667
+ }
5668
+ if (tonalProgress < 0.72) {
5669
+ return forwardness > 0.55 ? palette.secondary : palette.primary;
5670
+ }
5671
+ return tentacleCore > 0.44 ? `${palette.primary}f4` : `${palette.shadow}ee`;
5672
+ }
5673
+ /**
5674
+ * Draws one projected mesh patch with soft shading and a subtle edge.
5675
+ *
5676
+ * @private helper of `octopus3d3AvatarVisual`
5677
+ */
5678
+ function drawContinuousSurfacePatch(context, surfacePatch) {
5679
+ drawProjectedQuad(context, surfacePatch.corners, surfacePatch.fillStyle);
5680
+ if (surfacePatch.lightIntensity > 0) {
5681
+ drawProjectedQuad(context, surfacePatch.corners, `rgba(255, 255, 255, ${0.18 * surfacePatch.lightIntensity})`);
5682
+ }
5683
+ else if (surfacePatch.lightIntensity < 0) {
5684
+ drawProjectedQuad(context, surfacePatch.corners, `rgba(0, 0, 0, ${0.25 * Math.abs(surfacePatch.lightIntensity)})`);
5685
+ }
5686
+ context.save();
5687
+ context.beginPath();
5688
+ context.moveTo(surfacePatch.corners[0].x, surfacePatch.corners[0].y);
5689
+ for (let cornerIndex = 1; cornerIndex < surfacePatch.corners.length; cornerIndex++) {
5690
+ context.lineTo(surfacePatch.corners[cornerIndex].x, surfacePatch.corners[cornerIndex].y);
5691
+ }
5692
+ context.closePath();
5693
+ context.strokeStyle = surfacePatch.outlineColor;
5694
+ context.lineWidth = Math.max(0.7, getProjectedQuadPerimeter(surfacePatch.corners) * 0.0032);
5695
+ context.lineJoin = 'round';
5696
+ context.stroke();
5697
+ context.restore();
5698
+ }
5699
+ /**
5700
+ * Draws projected mantle-current lines on the front of the mesh.
5701
+ *
5702
+ * @private helper of `octopus3d3AvatarVisual`
5703
+ */
5704
+ function drawProjectedSurfaceCurrents(options) {
5705
+ const { context, surfaceOptions, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, morphologyProfile, timeMs, animationPhase, } = options;
5706
+ const currentCount = Math.min(6, morphologyProfile.details.mantleCurrentCount);
5707
+ const centerIndex = (currentCount - 1) / 2;
5708
+ context.save();
5709
+ context.lineCap = 'round';
5710
+ context.lineJoin = 'round';
5711
+ for (let currentIndex = 0; currentIndex < currentCount; currentIndex++) {
5712
+ const baseLongitude = (currentIndex - centerIndex) * 0.15;
5713
+ const projectedPoints = [];
5714
+ for (let sampleIndex = 0; sampleIndex < 8; sampleIndex++) {
5715
+ const progress = sampleIndex / 7;
5716
+ const latitude = -0.46 + progress * 0.74;
5717
+ const longitude = baseLongitude + Math.sin(timeMs / 1160 + animationPhase + currentIndex * 0.7 + progress * 2) * 0.035;
5718
+ const scenePoint = transformScenePoint(sampleContinuousOctopusSurfacePoint(surfaceOptions, latitude, longitude), center, rotationX, rotationY);
5719
+ if (scenePoint.z > center.z - size * 0.016) {
5720
+ projectedPoints.push(projectScenePoint(scenePoint, size, sceneCenterX, sceneCenterY));
5721
+ }
5722
+ }
5723
+ if (projectedPoints.length < 3) {
5724
+ continue;
5725
+ }
5726
+ context.beginPath();
5727
+ context.moveTo(projectedPoints[0].x, projectedPoints[0].y);
5728
+ for (const projectedPoint of projectedPoints.slice(1)) {
5729
+ context.lineTo(projectedPoint.x, projectedPoint.y);
5730
+ }
5731
+ context.strokeStyle = currentIndex % 2 === 0 ? `${palette.highlight}3d` : `${palette.accent}33`;
5732
+ context.lineWidth = size * (0.0055 + currentIndex * 0.00045);
5733
+ context.stroke();
5734
+ }
5735
+ context.restore();
5736
+ }
5737
+ /**
5738
+ * Draws small projected sucker highlights on the waving lower mesh lobes.
5739
+ *
5740
+ * @private helper of `octopus3d3AvatarVisual`
5741
+ */
5742
+ function drawProjectedTentacleSuckers(options) {
5743
+ const { surfaceOptions, size } = options;
5744
+ const { timeMs } = surfaceOptions;
5745
+ for (const tentacleProfile of surfaceOptions.tentacleProfiles) {
5746
+ if (Math.cos(tentacleProfile.centerLongitude) < -0.12) {
5747
+ continue;
5748
+ }
5749
+ for (let suckerIndex = 0; suckerIndex < 3; suckerIndex++) {
5750
+ const latitude = 0.52 + suckerIndex * 0.14;
5751
+ const sideOffset = tentacleProfile.suckerSide * (0.035 + suckerIndex * 0.012) * tentacleProfile.widthScale;
5752
+ const waveOffset = Math.sin(timeMs / 900 + tentacleProfile.phase + suckerIndex * 0.8) * 0.018;
5753
+ drawProjectedSurfaceSpot({
5754
+ ...options,
5755
+ latitude,
5756
+ longitude: tentacleProfile.centerLongitude + sideOffset + waveOffset,
5757
+ radiusScale: size * (0.0065 - suckerIndex * 0.0007),
5758
+ });
5759
+ }
5760
+ }
5761
+ }
5762
+ /**
5763
+ * Draws one tiny projected surface spot by sampling local mesh tangents.
5764
+ *
5765
+ * @private helper of `octopus3d3AvatarVisual`
5766
+ */
5767
+ function drawProjectedSurfaceSpot(options) {
5768
+ const { context, surfaceOptions, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, latitude, longitude, radiusScale, } = options;
5769
+ const localCenter = sampleContinuousOctopusSurfacePoint(surfaceOptions, latitude, longitude);
5770
+ const localHorizontal = sampleContinuousOctopusSurfacePoint(surfaceOptions, latitude, longitude + 0.018);
5771
+ const localVertical = sampleContinuousOctopusSurfacePoint(surfaceOptions, latitude + 0.018, longitude);
5772
+ const sceneCenterPoint = transformScenePoint(localCenter, center, rotationX, rotationY);
5773
+ if (sceneCenterPoint.z <= center.z - size * 0.012) {
5774
+ return;
5775
+ }
5776
+ const projectedCenterPoint = projectScenePoint(sceneCenterPoint, size, sceneCenterX, sceneCenterY);
5777
+ const projectedHorizontalPoint = projectScenePoint(transformScenePoint(localHorizontal, center, rotationX, rotationY), size, sceneCenterX, sceneCenterY);
5778
+ const projectedVerticalPoint = projectScenePoint(transformScenePoint(localVertical, center, rotationX, rotationY), size, sceneCenterX, sceneCenterY);
5779
+ const horizontalRadius = clampNumber$1(Math.hypot(projectedHorizontalPoint.x - projectedCenterPoint.x, projectedHorizontalPoint.y - projectedCenterPoint.y) *
5780
+ radiusScale *
5781
+ 0.74, size * 0.003, size * 0.018);
5782
+ const verticalRadius = clampNumber$1(Math.hypot(projectedVerticalPoint.x - projectedCenterPoint.x, projectedVerticalPoint.y - projectedCenterPoint.y) *
5783
+ radiusScale *
5784
+ 0.52, size * 0.0024, size * 0.014);
5785
+ const rotation = Math.atan2(projectedHorizontalPoint.y - projectedCenterPoint.y, projectedHorizontalPoint.x - projectedCenterPoint.x);
5786
+ context.save();
5787
+ context.translate(projectedCenterPoint.x, projectedCenterPoint.y);
5788
+ context.rotate(rotation);
5789
+ context.beginPath();
5790
+ context.ellipse(0, 0, horizontalRadius, verticalRadius, 0, 0, Math.PI * 2);
5791
+ context.fillStyle = `${palette.highlight}73`;
5792
+ context.fill();
5793
+ context.strokeStyle = `${palette.highlight}99`;
5794
+ context.lineWidth = Math.max(0.7, size * 0.0028);
5795
+ context.stroke();
5796
+ context.restore();
5797
+ }
5798
+ /**
5799
+ * Resolves a signed angular distance from the source longitude to the target longitude.
5800
+ *
5801
+ * @private helper of `octopus3d3AvatarVisual`
5802
+ */
5803
+ function resolveSignedAngularDistance(sourceLongitude, targetLongitude) {
5804
+ return Math.atan2(Math.sin(targetLongitude - sourceLongitude), Math.cos(targetLongitude - sourceLongitude));
5805
+ }
5806
+ /**
5807
+ * Smoothly maps a value between two bounds into `[0, 1]`.
5808
+ *
5809
+ * @private helper of `octopus3d3AvatarVisual`
5810
+ */
5811
+ function smoothStep(edgeStart, edgeEnd, value) {
5812
+ const progress = clampNumber$1((value - edgeStart) / (edgeEnd - edgeStart), 0, 1);
5813
+ return progress * progress * (3 - 2 * progress);
5814
+ }
5815
+
5308
5816
  /* eslint-disable no-magic-numbers */
5309
5817
  /**
5310
5818
  * Octopus avatar visual.
@@ -6075,6 +6583,7 @@ const AVATAR_VISUALS = [
6075
6583
  octopus3AvatarVisual,
6076
6584
  octopus3dAvatarVisual,
6077
6585
  octopus3d2AvatarVisual,
6586
+ octopus3d3AvatarVisual,
6078
6587
  asciiOctopusAvatarVisual,
6079
6588
  minecraftAvatarVisual,
6080
6589
  minecraft2AvatarVisual,
@@ -6156,7 +6665,7 @@ function generatePlaceholderAgentProfileImageUrl(agentIdOrName, agentsServerUrl)
6156
6665
  *
6157
6666
  * @private shared avatar contract
6158
6667
  */
6159
- const DEFAULT_AGENT_AVATAR_VISUAL_ID = 'octopus3';
6668
+ const DEFAULT_AGENT_AVATAR_VISUAL_ID = 'octopus3d3';
6160
6669
  /**
6161
6670
  * Resolves the avatar visual preferred by an agent, then falls back to a federated server default
6162
6671
  * and finally to the caller/server default.
@@ -22880,6 +23389,30 @@ const BookEditorMonacoConstants = {
22880
23389
  DIAGNOSTIC_MARKER_OWNER: 'book-editor-diagnostics',
22881
23390
  };
22882
23391
 
23392
+ /**
23393
+ * Theme used for the actual notebook editor surface.
23394
+ *
23395
+ * The host application can still pass its resolved app theme, but book sources
23396
+ * are intentionally rendered as light paper in both light and dark app modes.
23397
+ *
23398
+ * @private internal constant of `BookEditor`
23399
+ */
23400
+ const BOOK_EDITOR_RENDER_THEME = 'LIGHT';
23401
+ /**
23402
+ * Resolves an app-level theme into the visual theme used by `<BookEditor/>`.
23403
+ *
23404
+ * @param hostTheme - Resolved theme from the embedding application.
23405
+ * @returns Light theme used by the Book editor surface.
23406
+ *
23407
+ * @private internal utility of `BookEditor`
23408
+ */
23409
+ function resolveBookEditorRenderTheme(hostTheme = BOOK_EDITOR_RENDER_THEME) {
23410
+ if (hostTheme === 'DARK') {
23411
+ return BOOK_EDITOR_RENDER_THEME;
23412
+ }
23413
+ return hostTheme;
23414
+ }
23415
+
22883
23416
  /**
22884
23417
  * Gets a commitment definition by its type
22885
23418
  *
@@ -23889,19 +24422,20 @@ function createNoteLikeBodyRules(token, commitmentTransitionRules) {
23889
24422
  *
23890
24423
  * @private function of BookEditorMonaco
23891
24424
  */
23892
- function ensureBookEditorMonacoLanguage(monaco, theme = 'LIGHT') {
24425
+ function ensureBookEditorMonacoLanguage(monaco, theme = BOOK_EDITOR_RENDER_THEME) {
24426
+ const renderedTheme = resolveBookEditorRenderTheme(theme);
23893
24427
  const monacoWithLanguageState = monaco;
23894
24428
  if (monacoWithLanguageState[BOOK_EDITOR_LANGUAGE_INITIALIZED_FLAG]) {
23895
- if (monacoWithLanguageState[BOOK_EDITOR_THEME_MODE_FLAG] !== theme) {
23896
- applyBookEditorMonacoTheme(monaco, theme);
23897
- monacoWithLanguageState[BOOK_EDITOR_THEME_MODE_FLAG] = theme;
24429
+ if (monacoWithLanguageState[BOOK_EDITOR_THEME_MODE_FLAG] !== renderedTheme) {
24430
+ applyBookEditorMonacoTheme(monaco, renderedTheme);
24431
+ monacoWithLanguageState[BOOK_EDITOR_THEME_MODE_FLAG] = renderedTheme;
23898
24432
  return;
23899
24433
  }
23900
24434
  monaco.editor.setTheme(BookEditorMonacoConstants.BOOK_THEME_ID);
23901
24435
  return;
23902
24436
  }
23903
24437
  monacoWithLanguageState[BOOK_EDITOR_LANGUAGE_INITIALIZED_FLAG] = true;
23904
- monacoWithLanguageState[BOOK_EDITOR_THEME_MODE_FLAG] = theme;
24438
+ monacoWithLanguageState[BOOK_EDITOR_THEME_MODE_FLAG] = renderedTheme;
23905
24439
  monaco.languages.register({ id: BookEditorMonacoConstants.BOOK_LANGUAGE_ID });
23906
24440
  const commitmentDefinitions = getAllCommitmentDefinitions();
23907
24441
  const commitmentTypes = [...new Set(commitmentDefinitions.map(({ type }) => type))];
@@ -24011,7 +24545,7 @@ function ensureBookEditorMonacoLanguage(monaco, theme = 'LIGHT') {
24011
24545
  return { links };
24012
24546
  },
24013
24547
  });
24014
- applyBookEditorMonacoTheme(monaco, theme);
24548
+ applyBookEditorMonacoTheme(monaco, renderedTheme);
24015
24549
  }
24016
24550
  /**
24017
24551
  * Ensures the mounted Monaco editor model uses Book language and Book theme.
@@ -24024,8 +24558,9 @@ function ensureBookEditorMonacoLanguage(monaco, theme = 'LIGHT') {
24024
24558
  * @private function of BookEditorMonaco
24025
24559
  */
24026
24560
  function ensureBookEditorMonacoLanguageForEditor(props) {
24027
- const { monaco, monacoEditor, theme = 'LIGHT' } = props;
24028
- ensureBookEditorMonacoLanguage(monaco, theme);
24561
+ const { monaco, monacoEditor, theme = BOOK_EDITOR_RENDER_THEME } = props;
24562
+ const renderedTheme = resolveBookEditorRenderTheme(theme);
24563
+ ensureBookEditorMonacoLanguage(monaco, renderedTheme);
24029
24564
  const model = monacoEditor.getModel();
24030
24565
  if (!model) {
24031
24566
  return;
@@ -25257,7 +25792,8 @@ function createBookEditorMonacoOptions({ isReadonly, translations, scaledFontSiz
25257
25792
  * @private Internal component used by `BookEditor`
25258
25793
  */
25259
25794
  function BookEditorMonaco(props) {
25260
- const { value, onChange, diagnostics, isReadonly, theme = 'LIGHT', translations, onFileUpload, isUploadButtonShown, isCameraButtonShown, isDownloadButtonShown, isAboutButtonShown = true, isFullscreenButtonShown = true, onFullscreenClick, isFullscreen, zoom = 1, monacoModelPath, hoistedMenuItems, } = props;
25795
+ 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;
25796
+ const theme = resolveBookEditorRenderTheme(hostTheme);
25261
25797
  const zoomLevel = zoom;
25262
25798
  const { scaledLineHeight, scaledContentPaddingLeft, scaledVerticalLineLeft, scaledFontSize, scaledScrollbarSize } = createBookEditorMonacoScale(zoomLevel);
25263
25799
  const monaco = useMonaco();
@@ -25346,15 +25882,16 @@ const DEFAULT_BOOK_EDITOR_HEIGHT = countLines(DEFAULT_BOOK) * 30 + 20;
25346
25882
  * @public exported from `@promptbook/components`
25347
25883
  */
25348
25884
  function BookEditor(props) {
25349
- 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;
25885
+ 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;
25350
25886
  const [isFullscreen, setIsFullscreen] = useState(false);
25887
+ const renderedTheme = resolveBookEditorRenderTheme(hostTheme);
25351
25888
  /**
25352
25889
  * Toggles fullscreen mode for the editor.
25353
25890
  */
25354
25891
  const handleFullscreenToggle = () => {
25355
25892
  setIsFullscreen(!isFullscreen);
25356
25893
  };
25357
- const editorContent = (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
25894
+ const editorContent = (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
25358
25895
  ? style
25359
25896
  : {
25360
25897
  ...(style || {}),
@@ -25365,7 +25902,7 @@ function BookEditor(props) {
25365
25902
  ? `${props.height}px`
25366
25903
  : props.height || `${DEFAULT_BOOK_EDITOR_HEIGHT}px`,
25367
25904
  }),
25368
- }, children: 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 }) }));
25905
+ }, children: 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 }) }));
25369
25906
  return isFullscreen && typeof document !== 'undefined' ? createPortal(editorContent, document.body) : editorContent;
25370
25907
  }
25371
25908