@promptbook/wizard 0.112.0-65 โ†’ 0.112.0-66

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
@@ -9,7 +9,7 @@ import { spawn } from 'child_process';
9
9
  import { forTime } from 'waitasecond';
10
10
  import { SHA256 } from 'crypto-js';
11
11
  import hexEncoder from 'crypto-js/enc-hex';
12
- import { basename, join, dirname, isAbsolute, relative } from 'path';
12
+ import { posix, basename, join, dirname, isAbsolute, relative } from 'path';
13
13
  import { Readability } from '@mozilla/readability';
14
14
  import { JSDOM } from 'jsdom';
15
15
  import { Converter } from 'showdown';
@@ -38,7 +38,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
38
38
  * @generated
39
39
  * @see https://github.com/webgptorg/promptbook
40
40
  */
41
- const PROMPTBOOK_ENGINE_VERSION = '0.112.0-65';
41
+ const PROMPTBOOK_ENGINE_VERSION = '0.112.0-66';
42
42
  /**
43
43
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
44
44
  * Note: [๐Ÿ’ž] Ignore a discrepancy between file name and entity name
@@ -971,6 +971,29 @@ function saturate(amount) {
971
971
  }
972
972
  // TODO: Maybe implement by mix+hsl
973
973
 
974
+ /**
975
+ * Relative directory name without the `./` prefix for Git ignore rules and glob patterns.
976
+ *
977
+ * @private internal utility for Promptbook-owned temp files
978
+ */
979
+ const PROMPTBOOK_TEMP_DIRECTORY_NAME = '.promptbook';
980
+ /**
981
+ * Builds one project-relative path inside the shared Promptbook working directory.
982
+ *
983
+ * @private internal utility for Promptbook-owned temp files
984
+ */
985
+ function getPromptbookTempPath(...pathSegments) {
986
+ return `./${getPromptbookTempPosixPath(...pathSegments)}`;
987
+ }
988
+ /**
989
+ * Builds one POSIX path fragment inside the shared Promptbook working directory for globs and generated text files.
990
+ *
991
+ * @private internal utility for Promptbook-owned temp files
992
+ */
993
+ function getPromptbookTempPosixPath(...pathSegments) {
994
+ return posix.join(PROMPTBOOK_TEMP_DIRECTORY_NAME, ...pathSegments);
995
+ }
996
+
974
997
  /**
975
998
  * Returns the same value that is passed as argument.
976
999
  * No side effects.
@@ -1185,7 +1208,6 @@ const DEFAULT_MAX_EXECUTION_ATTEMPTS = 7; // <- TODO: [๐Ÿคนโ€โ™‚๏ธ]
1185
1208
  */
1186
1209
  const DEFAULT_BOOKS_DIRNAME = './books';
1187
1210
  // <- TODO: [๐Ÿ•] Make also `AGENTS_DIRNAME_ALTERNATIVES`
1188
- // TODO: Just `.promptbook` in config, hardcode subfolders like `download-cache` or `execution-cache`
1189
1211
  /**
1190
1212
  * Where to store the temporary downloads
1191
1213
  *
@@ -1193,7 +1215,7 @@ const DEFAULT_BOOKS_DIRNAME = './books';
1193
1215
  *
1194
1216
  * @public exported from `@promptbook/core`
1195
1217
  */
1196
- const DEFAULT_DOWNLOAD_CACHE_DIRNAME = './.promptbook/download-cache';
1218
+ const DEFAULT_DOWNLOAD_CACHE_DIRNAME = getPromptbookTempPath('download-cache');
1197
1219
  /**
1198
1220
  * Where to store the cache of executions for promptbook CLI
1199
1221
  *
@@ -1201,7 +1223,7 @@ const DEFAULT_DOWNLOAD_CACHE_DIRNAME = './.promptbook/download-cache';
1201
1223
  *
1202
1224
  * @public exported from `@promptbook/core`
1203
1225
  */
1204
- const DEFAULT_EXECUTION_CACHE_DIRNAME = './.promptbook/execution-cache';
1226
+ const DEFAULT_EXECUTION_CACHE_DIRNAME = getPromptbookTempPath('execution-cache');
1205
1227
  /**
1206
1228
  * Where to store the scrape cache
1207
1229
  *
@@ -1209,7 +1231,7 @@ const DEFAULT_EXECUTION_CACHE_DIRNAME = './.promptbook/execution-cache';
1209
1231
  *
1210
1232
  * @public exported from `@promptbook/core`
1211
1233
  */
1212
- const DEFAULT_SCRAPE_CACHE_DIRNAME = './.promptbook/scrape-cache';
1234
+ const DEFAULT_SCRAPE_CACHE_DIRNAME = getPromptbookTempPath('scrape-cache');
1213
1235
  /*
1214
1236
  TODO: [๐ŸŒƒ]
1215
1237
  /**
@@ -23401,6 +23423,177 @@ function drawFractalCore(context, size, palette, timeMs, corePhase) {
23401
23423
  context.restore();
23402
23424
  }
23403
23425
 
23426
+ /* eslint-disable no-magic-numbers */
23427
+ /**
23428
+ * Builds the seeded six-face texture pack used by the Minecraft-style head cuboid.
23429
+ *
23430
+ * @param random Seeded random generator.
23431
+ * @param palette Derived avatar palette.
23432
+ * @param hasHeadband Whether the generated avatar should include a colored headband.
23433
+ * @returns Head cuboid textures.
23434
+ *
23435
+ * @private helper of the Minecraft avatar visuals
23436
+ */
23437
+ function createMinecraftHeadTextures(random, palette, hasHeadband) {
23438
+ const faceTexture = createMinecraftFaceTexture(random, palette, hasHeadband);
23439
+ const hairColor = random() < 0.5 ? palette.primary : palette.secondary;
23440
+ const skinColor = palette.highlight;
23441
+ const headbandColor = hasHeadband ? palette.accent : hairColor;
23442
+ const sideTexture = createFilledTexture(skinColor);
23443
+ const backTexture = createFilledTexture(skinColor);
23444
+ const topTexture = createFilledTexture(hairColor);
23445
+ const bottomTexture = createFilledTexture(`${palette.shadow}cc`);
23446
+ fillTextureRect(sideTexture, 0, 0, 8, 3, hairColor);
23447
+ fillTextureRect(backTexture, 0, 0, 8, 5, hairColor);
23448
+ fillTextureRect(backTexture, 1, 5, 6, 1, hairColor);
23449
+ if (hasHeadband) {
23450
+ fillTextureRect(sideTexture, 0, 2, 8, 1, headbandColor);
23451
+ fillTextureRect(backTexture, 0, 2, 8, 1, headbandColor);
23452
+ fillTextureRect(topTexture, 0, 4, 8, 1, headbandColor);
23453
+ }
23454
+ sideTexture[4][4] = `${palette.shadow}99`;
23455
+ sideTexture[5][4] = `${palette.shadow}cc`;
23456
+ backTexture[6][2] = `${palette.shadow}99`;
23457
+ backTexture[6][5] = `${palette.shadow}99`;
23458
+ return {
23459
+ front: faceTexture,
23460
+ back: backTexture,
23461
+ left: sideTexture,
23462
+ right: mirrorMinecraftTexture(sideTexture),
23463
+ top: topTexture,
23464
+ bottom: bottomTexture,
23465
+ };
23466
+ }
23467
+ /**
23468
+ * Builds the seeded six-face texture pack used by the Minecraft-style torso cuboid.
23469
+ *
23470
+ * @param random Seeded random generator.
23471
+ * @param palette Derived avatar palette.
23472
+ * @returns Torso cuboid textures.
23473
+ *
23474
+ * @private helper of the Minecraft avatar visuals
23475
+ */
23476
+ function createMinecraftTorsoTextures(random, palette) {
23477
+ const frontTexture = createMinecraftShirtTexture(random, palette);
23478
+ const sideTexture = createFilledTexture(palette.primary);
23479
+ const backTexture = createFilledTexture(palette.primary);
23480
+ const topTexture = createFilledTexture(`${palette.highlight}dd`);
23481
+ const bottomTexture = createFilledTexture(`${palette.shadow}dd`);
23482
+ const stripeColor = random() < 0.5 ? palette.secondary : palette.highlight;
23483
+ fillTextureRect(sideTexture, 0, 0, 8, 2, palette.shadow);
23484
+ fillTextureRect(backTexture, 0, 0, 8, 2, palette.shadow);
23485
+ fillTextureRect(backTexture, 3, 2, 2, 6, stripeColor);
23486
+ fillTextureRect(sideTexture, 4, 2, 1, 6, stripeColor);
23487
+ fillTextureRect(topTexture, 0, 0, 8, 2, palette.shadow);
23488
+ fillTextureRect(topTexture, 2, 2, 4, 4, stripeColor);
23489
+ return {
23490
+ front: frontTexture,
23491
+ back: backTexture,
23492
+ left: sideTexture,
23493
+ right: mirrorMinecraftTexture(sideTexture),
23494
+ top: topTexture,
23495
+ bottom: bottomTexture,
23496
+ };
23497
+ }
23498
+ /**
23499
+ * Mirrors one Minecraft texture horizontally.
23500
+ *
23501
+ * @param texture Source texture.
23502
+ * @returns Mirrored texture copy.
23503
+ *
23504
+ * @private helper of the Minecraft avatar visuals
23505
+ */
23506
+ function mirrorMinecraftTexture(texture) {
23507
+ return texture.map((row) => [...row].reverse());
23508
+ }
23509
+ /**
23510
+ * Creates the front-face pixel texture for the cube head.
23511
+ *
23512
+ * @param random Seeded random generator.
23513
+ * @param palette Derived avatar palette.
23514
+ * @param hasHeadband Whether the avatar should render a headband row.
23515
+ * @returns 8x8 pixel texture.
23516
+ *
23517
+ * @private helper of the Minecraft avatar visuals
23518
+ */
23519
+ function createMinecraftFaceTexture(random, palette, hasHeadband) {
23520
+ const texture = createFilledTexture(palette.highlight);
23521
+ const hairlineColor = random() < 0.5 ? palette.primary : palette.secondary;
23522
+ const cheekColor = random() < 0.5 ? `${palette.accent}bb` : `${palette.secondary}bb`;
23523
+ fillTextureRect(texture, 0, 0, 8, 2, hairlineColor);
23524
+ texture[2][0] = hairlineColor;
23525
+ texture[2][7] = hairlineColor;
23526
+ texture[3][0] = hairlineColor;
23527
+ texture[3][7] = hairlineColor;
23528
+ if (hasHeadband) {
23529
+ fillTextureRect(texture, 0, 2, 8, 1, palette.accent);
23530
+ }
23531
+ texture[3][2] = palette.ink;
23532
+ texture[3][5] = palette.ink;
23533
+ texture[4][2] = '#ffffff';
23534
+ texture[4][5] = '#ffffff';
23535
+ texture[5][1] = cheekColor;
23536
+ texture[5][6] = cheekColor;
23537
+ texture[5][3] = palette.shadow;
23538
+ texture[5][4] = palette.shadow;
23539
+ texture[6][3] = palette.shadow;
23540
+ texture[6][4] = palette.shadow;
23541
+ return texture;
23542
+ }
23543
+ /**
23544
+ * Creates the front-face pixel texture for the torso.
23545
+ *
23546
+ * @param random Seeded random generator.
23547
+ * @param palette Derived avatar palette.
23548
+ * @returns 8x8 torso texture.
23549
+ *
23550
+ * @private helper of the Minecraft avatar visuals
23551
+ */
23552
+ function createMinecraftShirtTexture(random, palette) {
23553
+ const texture = createFilledTexture(palette.primary);
23554
+ const stripeColor = random() < 0.5 ? palette.secondary : palette.highlight;
23555
+ fillTextureRect(texture, 0, 0, 8, 2, palette.shadow);
23556
+ for (let rowIndex = 2; rowIndex < 8; rowIndex++) {
23557
+ texture[rowIndex][3] = stripeColor;
23558
+ texture[rowIndex][4] = stripeColor;
23559
+ }
23560
+ texture[4][1] = palette.accent;
23561
+ texture[4][6] = palette.accent;
23562
+ texture[5][2] = palette.highlight;
23563
+ texture[5][5] = palette.highlight;
23564
+ return texture;
23565
+ }
23566
+ /**
23567
+ * Creates one solid-color 8x8 Minecraft texture.
23568
+ *
23569
+ * @param color Fill color.
23570
+ * @returns Filled 8x8 texture.
23571
+ *
23572
+ * @private helper of the Minecraft avatar visuals
23573
+ */
23574
+ function createFilledTexture(color) {
23575
+ return Array.from({ length: 8 }, () => Array.from({ length: 8 }, () => color));
23576
+ }
23577
+ /**
23578
+ * Fills one rectangular area inside a mutable Minecraft texture.
23579
+ *
23580
+ * @param texture Mutable target texture.
23581
+ * @param x Left texture coordinate.
23582
+ * @param y Top texture coordinate.
23583
+ * @param width Rectangle width.
23584
+ * @param height Rectangle height.
23585
+ * @param color Fill color.
23586
+ *
23587
+ * @private helper of the Minecraft avatar visuals
23588
+ */
23589
+ function fillTextureRect(texture, x, y, width, height, color) {
23590
+ for (let rowIndex = y; rowIndex < y + height; rowIndex++) {
23591
+ for (let columnIndex = x; columnIndex < x + width; columnIndex++) {
23592
+ texture[rowIndex][columnIndex] = color;
23593
+ }
23594
+ }
23595
+ }
23596
+
23404
23597
  /* eslint-disable no-magic-numbers */
23405
23598
  /**
23406
23599
  * Minecraft-style 3D avatar visual.
@@ -23425,8 +23618,8 @@ const minecraftAvatarVisual = {
23425
23618
  const bodyX = size * 0.33;
23426
23619
  const bodyY = headY + headSize * 0.96;
23427
23620
  const hasHeadband = random() < 0.5;
23428
- const faceTexture = createMinecraftFaceTexture(createRandom('minecraft-face'), palette, hasHeadband);
23429
- const shirtTexture = createMinecraftShirtTexture(createRandom('minecraft-shirt'), palette);
23621
+ const headTextures = createMinecraftHeadTextures(createRandom('minecraft-face'), palette, hasHeadband);
23622
+ const torsoTextures = createMinecraftTorsoTextures(createRandom('minecraft-shirt'), palette);
23430
23623
  drawAvatarFrame(context, size, palette);
23431
23624
  const spotlight = context.createRadialGradient(size * 0.5, size * 0.18, size * 0.05, size * 0.5, size * 0.18, size * 0.5);
23432
23625
  spotlight.addColorStop(0, `${palette.highlight}66`);
@@ -23446,7 +23639,7 @@ const minecraftAvatarVisual = {
23446
23639
  width: bodyWidth,
23447
23640
  height: bodyHeight,
23448
23641
  depth: bodyDepth,
23449
- frontTexture: shirtTexture,
23642
+ frontTexture: torsoTextures.front,
23450
23643
  topColor: `${palette.highlight}cc`,
23451
23644
  sideColor: `${palette.secondary}dd`,
23452
23645
  outlineColor: `${palette.shadow}aa`,
@@ -23457,7 +23650,7 @@ const minecraftAvatarVisual = {
23457
23650
  width: headSize,
23458
23651
  height: headSize,
23459
23652
  depth,
23460
- frontTexture: faceTexture,
23653
+ frontTexture: headTextures.front,
23461
23654
  topColor: `${palette.highlight}ee`,
23462
23655
  sideColor: `${palette.secondary}ee`,
23463
23656
  outlineColor: `${palette.shadow}cc`,
@@ -23522,72 +23715,505 @@ function drawVoxelCuboid(context, cuboid) {
23522
23715
  context.closePath();
23523
23716
  context.stroke();
23524
23717
  }
23718
+
23719
+ /* eslint-disable no-magic-numbers */
23525
23720
  /**
23526
- * Creates the front-face pixel texture for the cube head.
23721
+ * Fixed scene camera distance used for the proper-3D projection.
23527
23722
  *
23528
- * @param random Seeded random generator.
23723
+ * @private helper of `minecraft2AvatarVisual`
23724
+ */
23725
+ const CAMERA_DISTANCE_RATIO = 1.4;
23726
+ /**
23727
+ * Shared light direction used to shade projected cuboid faces.
23728
+ *
23729
+ * @private helper of `minecraft2AvatarVisual`
23730
+ */
23731
+ const LIGHT_DIRECTION = normalizeVector3({
23732
+ x: 0.4,
23733
+ y: -0.65,
23734
+ z: 0.92,
23735
+ });
23736
+ /**
23737
+ * Minecraft 3D 2 avatar visual.
23738
+ *
23739
+ * @private built-in avatar visual
23740
+ */
23741
+ const minecraft2AvatarVisual = {
23742
+ id: 'minecraft2',
23743
+ title: 'Minecraft 3D 2',
23744
+ description: 'Proper 3D Minecraft-style portrait with textured cuboids and pointer-driven head turns.',
23745
+ isAnimated: true,
23746
+ supportsPointerTracking: true,
23747
+ render({ context, size, palette, createRandom, timeMs, interaction }) {
23748
+ const spotlightY = size * 0.22;
23749
+ const headRandom = createRandom('minecraft2-head');
23750
+ const hasHeadband = headRandom() < 0.5;
23751
+ const headTextures = createMinecraftHeadTextures(createRandom('minecraft2-head-textures'), palette, hasHeadband);
23752
+ const torsoTextures = createMinecraftTorsoTextures(createRandom('minecraft2-body-textures'), palette);
23753
+ const bob = Math.sin(timeMs / 880) * size * 0.014;
23754
+ const bodyYaw = -0.24 + Math.sin(timeMs / 2300) * 0.06 + interaction.bodyOffsetX * 0.16;
23755
+ const bodyPitch = -0.12 + Math.cos(timeMs / 2800) * 0.02 - interaction.bodyOffsetY * 0.06;
23756
+ const headYaw = -0.18 + Math.sin(timeMs / 1900 + 0.6) * 0.05 + interaction.gazeX * 0.62;
23757
+ const headPitch = -0.12 + Math.cos(timeMs / 2400 + 1.1) * 0.03 - interaction.gazeY * 0.38;
23758
+ const sceneCenterX = size * 0.5;
23759
+ const sceneCenterY = size * 0.57;
23760
+ const bodyWidth = size * 0.225;
23761
+ const bodyHeight = size * 0.245;
23762
+ const bodyDepth = size * 0.145;
23763
+ const headSize = size * 0.24;
23764
+ const headLift = size * 0.205;
23765
+ const headForwardShift = interaction.intensity * size * 0.018;
23766
+ const sceneCuboids = [
23767
+ {
23768
+ center: {
23769
+ x: interaction.bodyOffsetX * size * 0.026,
23770
+ y: size * 0.05 + interaction.bodyOffsetY * size * 0.018 + bob,
23771
+ z: 0,
23772
+ },
23773
+ width: bodyWidth,
23774
+ height: bodyHeight,
23775
+ depth: bodyDepth,
23776
+ rotationX: bodyPitch,
23777
+ rotationY: bodyYaw,
23778
+ textures: torsoTextures,
23779
+ outlineColor: `${palette.shadow}cc`,
23780
+ },
23781
+ {
23782
+ center: {
23783
+ x: interaction.bodyOffsetX * size * 0.018 + interaction.gazeX * size * 0.016,
23784
+ y: -headLift + bob * 1.15,
23785
+ z: headForwardShift,
23786
+ },
23787
+ width: headSize,
23788
+ height: headSize,
23789
+ depth: headSize,
23790
+ rotationX: headPitch,
23791
+ rotationY: headYaw,
23792
+ textures: headTextures,
23793
+ outlineColor: `${palette.shadow}dd`,
23794
+ },
23795
+ ];
23796
+ const visibleFaces = sceneCuboids
23797
+ .flatMap((cuboid) => resolveVisibleCuboidFaces(cuboid, size, sceneCenterX, sceneCenterY))
23798
+ .sort((firstFace, secondFace) => firstFace.averageDepth - secondFace.averageDepth);
23799
+ drawAvatarFrame(context, size, palette);
23800
+ drawMinecraftBackdrop(context, size, palette, sceneCenterX, spotlightY, interaction, timeMs);
23801
+ drawMinecraftShadow(context, size, palette, interaction, timeMs);
23802
+ for (const visibleFace of visibleFaces) {
23803
+ drawTexturedProjectedFace(context, visibleFace);
23804
+ }
23805
+ },
23806
+ };
23807
+ /**
23808
+ * Draws the shared background atmosphere behind the Minecraft 3D 2 portrait.
23809
+ *
23810
+ * @param context Canvas 2D context.
23811
+ * @param size Canvas size in CSS pixels.
23529
23812
  * @param palette Derived avatar palette.
23530
- * @param hasHeadband Whether the avatar should render a headband row.
23531
- * @returns 8x8 pixel texture.
23813
+ * @param sceneCenterX Horizontal scene center.
23814
+ * @param spotlightY Vertical spotlight anchor.
23815
+ * @param interaction Smoothed pointer-aware interaction state.
23816
+ * @param timeMs Current animation time in milliseconds.
23532
23817
  *
23533
- * @private helper of `minecraftAvatarVisual`
23818
+ * @private helper of `minecraft2AvatarVisual`
23534
23819
  */
23535
- function createMinecraftFaceTexture(random, palette, hasHeadband) {
23536
- const texture = Array.from({ length: 8 }, () => Array.from({ length: 8 }, () => palette.highlight));
23537
- const hairlineColor = random() < 0.5 ? palette.primary : palette.secondary;
23538
- const cheekColor = random() < 0.5 ? `${palette.accent}bb` : `${palette.secondary}bb`;
23539
- for (let rowIndex = 0; rowIndex < 2; rowIndex++) {
23540
- for (let columnIndex = 0; columnIndex < 8; columnIndex++) {
23541
- texture[rowIndex][columnIndex] = hairlineColor;
23820
+ function drawMinecraftBackdrop(context, size, palette, sceneCenterX, spotlightY, interaction, timeMs) {
23821
+ const spotlightGradient = context.createRadialGradient(sceneCenterX + interaction.gazeX * size * 0.08, spotlightY + interaction.gazeY * size * 0.05, size * 0.03, sceneCenterX, spotlightY, size * 0.52);
23822
+ spotlightGradient.addColorStop(0, `${palette.highlight}66`);
23823
+ spotlightGradient.addColorStop(0.42, `${palette.accent}1d`);
23824
+ spotlightGradient.addColorStop(1, `${palette.highlight}00`);
23825
+ context.fillStyle = spotlightGradient;
23826
+ context.fillRect(0, 0, size, size);
23827
+ const rimGradient = context.createLinearGradient(0, size * 0.14, 0, size * 0.92);
23828
+ rimGradient.addColorStop(0, `${palette.highlight}12`);
23829
+ rimGradient.addColorStop(0.55, `${palette.secondary}0a`);
23830
+ rimGradient.addColorStop(1, `${palette.shadow}00`);
23831
+ context.fillStyle = rimGradient;
23832
+ context.fillRect(0, 0, size, size);
23833
+ context.save();
23834
+ context.globalAlpha = 0.08 + interaction.intensity * 0.04;
23835
+ context.fillStyle = palette.highlight;
23836
+ context.beginPath();
23837
+ context.arc(size * 0.72 + Math.sin(timeMs / 1600) * size * 0.03, size * 0.2 + Math.cos(timeMs / 1400) * size * 0.018, size * 0.025, 0, Math.PI * 2);
23838
+ context.fill();
23839
+ context.restore();
23840
+ }
23841
+ /**
23842
+ * Draws the soft floor shadow used to anchor the cuboids in the frame.
23843
+ *
23844
+ * @param context Canvas 2D context.
23845
+ * @param size Canvas size in CSS pixels.
23846
+ * @param palette Derived avatar palette.
23847
+ * @param interaction Smoothed pointer-aware interaction state.
23848
+ * @param timeMs Current animation time in milliseconds.
23849
+ *
23850
+ * @private helper of `minecraft2AvatarVisual`
23851
+ */
23852
+ function drawMinecraftShadow(context, size, palette, interaction, timeMs) {
23853
+ context.save();
23854
+ context.fillStyle = `${palette.shadow}66`;
23855
+ context.filter = `blur(${size * 0.02}px)`;
23856
+ context.beginPath();
23857
+ 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);
23858
+ context.fill();
23859
+ context.restore();
23860
+ }
23861
+ /**
23862
+ * Resolves all visible projected faces for one scene cuboid.
23863
+ *
23864
+ * @param cuboid Scene cuboid definition.
23865
+ * @param size Canvas size in CSS pixels.
23866
+ * @param sceneCenterX Horizontal scene center.
23867
+ * @param sceneCenterY Vertical scene center.
23868
+ * @returns Visible faces sorted later by depth.
23869
+ *
23870
+ * @private helper of `minecraft2AvatarVisual`
23871
+ */
23872
+ function resolveVisibleCuboidFaces(cuboid, size, sceneCenterX, sceneCenterY) {
23873
+ const halfWidth = cuboid.width / 2;
23874
+ const halfHeight = cuboid.height / 2;
23875
+ const halfDepth = cuboid.depth / 2;
23876
+ const faceDefinitions = [
23877
+ {
23878
+ texture: cuboid.textures.front,
23879
+ corners: [
23880
+ { x: -halfWidth, y: -halfHeight, z: halfDepth },
23881
+ { x: halfWidth, y: -halfHeight, z: halfDepth },
23882
+ { x: halfWidth, y: halfHeight, z: halfDepth },
23883
+ { x: -halfWidth, y: halfHeight, z: halfDepth },
23884
+ ],
23885
+ },
23886
+ {
23887
+ texture: cuboid.textures.back,
23888
+ corners: [
23889
+ { x: halfWidth, y: -halfHeight, z: -halfDepth },
23890
+ { x: -halfWidth, y: -halfHeight, z: -halfDepth },
23891
+ { x: -halfWidth, y: halfHeight, z: -halfDepth },
23892
+ { x: halfWidth, y: halfHeight, z: -halfDepth },
23893
+ ],
23894
+ },
23895
+ {
23896
+ texture: cuboid.textures.right,
23897
+ corners: [
23898
+ { x: halfWidth, y: -halfHeight, z: halfDepth },
23899
+ { x: halfWidth, y: -halfHeight, z: -halfDepth },
23900
+ { x: halfWidth, y: halfHeight, z: -halfDepth },
23901
+ { x: halfWidth, y: halfHeight, z: halfDepth },
23902
+ ],
23903
+ },
23904
+ {
23905
+ texture: cuboid.textures.left,
23906
+ corners: [
23907
+ { x: -halfWidth, y: -halfHeight, z: -halfDepth },
23908
+ { x: -halfWidth, y: -halfHeight, z: halfDepth },
23909
+ { x: -halfWidth, y: halfHeight, z: halfDepth },
23910
+ { x: -halfWidth, y: halfHeight, z: -halfDepth },
23911
+ ],
23912
+ },
23913
+ {
23914
+ texture: cuboid.textures.top,
23915
+ corners: [
23916
+ { x: -halfWidth, y: -halfHeight, z: -halfDepth },
23917
+ { x: halfWidth, y: -halfHeight, z: -halfDepth },
23918
+ { x: halfWidth, y: -halfHeight, z: halfDepth },
23919
+ { x: -halfWidth, y: -halfHeight, z: halfDepth },
23920
+ ],
23921
+ },
23922
+ {
23923
+ texture: cuboid.textures.bottom,
23924
+ corners: [
23925
+ { x: -halfWidth, y: halfHeight, z: halfDepth },
23926
+ { x: halfWidth, y: halfHeight, z: halfDepth },
23927
+ { x: halfWidth, y: halfHeight, z: -halfDepth },
23928
+ { x: -halfWidth, y: halfHeight, z: -halfDepth },
23929
+ ],
23930
+ },
23931
+ ];
23932
+ const visibleFaces = faceDefinitions
23933
+ .map((faceDefinition) => {
23934
+ const transformedCorners = faceDefinition.corners.map((corner) => transformScenePoint(corner, cuboid.center, cuboid.rotationX, cuboid.rotationY));
23935
+ const faceNormal = normalizeVector3(crossProduct3D(subtractPoint3D(transformedCorners[1], transformedCorners[0]), subtractPoint3D(transformedCorners[2], transformedCorners[0])));
23936
+ if (faceNormal.z <= 0.02) {
23937
+ return null;
23542
23938
  }
23939
+ const projectedCorners = transformedCorners.map((corner) => projectScenePoint(corner, size, sceneCenterX, sceneCenterY));
23940
+ return {
23941
+ corners: projectedCorners,
23942
+ texture: faceDefinition.texture,
23943
+ averageDepth: transformedCorners.reduce((depthSum, corner) => depthSum + corner.z, 0) / transformedCorners.length,
23944
+ lightIntensity: clampNumber$1(dotProduct3D(faceNormal, LIGHT_DIRECTION), -1, 1),
23945
+ outlineColor: cuboid.outlineColor,
23946
+ };
23947
+ });
23948
+ return visibleFaces.filter((visibleFace) => visibleFace !== null);
23949
+ }
23950
+ /**
23951
+ * Draws one projected textured face by tessellating its texture cells into quads.
23952
+ *
23953
+ * @param context Canvas 2D context.
23954
+ * @param face Visible projected face.
23955
+ *
23956
+ * @private helper of `minecraft2AvatarVisual`
23957
+ */
23958
+ function drawTexturedProjectedFace(context, face) {
23959
+ var _a;
23960
+ const rows = face.texture.length;
23961
+ const columns = ((_a = face.texture[0]) === null || _a === void 0 ? void 0 : _a.length) || 0;
23962
+ if (rows === 0 || columns === 0) {
23963
+ return;
23543
23964
  }
23544
- texture[2][0] = hairlineColor;
23545
- texture[2][7] = hairlineColor;
23546
- texture[3][0] = hairlineColor;
23547
- texture[3][7] = hairlineColor;
23548
- if (hasHeadband) {
23549
- for (let columnIndex = 0; columnIndex < 8; columnIndex++) {
23550
- texture[2][columnIndex] = palette.accent;
23965
+ for (let rowIndex = 0; rowIndex < rows; rowIndex++) {
23966
+ for (let columnIndex = 0; columnIndex < columns; columnIndex++) {
23967
+ const startX = columnIndex / columns;
23968
+ const endX = (columnIndex + 1) / columns;
23969
+ const startY = rowIndex / rows;
23970
+ const endY = (rowIndex + 1) / rows;
23971
+ drawProjectedQuad(context, [
23972
+ interpolateProjectedQuad(face.corners, startX, startY),
23973
+ interpolateProjectedQuad(face.corners, endX, startY),
23974
+ interpolateProjectedQuad(face.corners, endX, endY),
23975
+ interpolateProjectedQuad(face.corners, startX, endY),
23976
+ ], face.texture[rowIndex][columnIndex]);
23551
23977
  }
23552
23978
  }
23553
- texture[3][2] = palette.ink;
23554
- texture[3][5] = palette.ink;
23555
- texture[4][2] = '#ffffff';
23556
- texture[4][5] = '#ffffff';
23557
- texture[5][1] = cheekColor;
23558
- texture[5][6] = cheekColor;
23559
- texture[5][3] = palette.shadow;
23560
- texture[5][4] = palette.shadow;
23561
- texture[6][3] = palette.shadow;
23562
- texture[6][4] = palette.shadow;
23563
- return texture;
23979
+ if (face.lightIntensity > 0) {
23980
+ drawProjectedQuad(context, face.corners, `rgba(255, 255, 255, ${0.15 * face.lightIntensity})`);
23981
+ }
23982
+ else if (face.lightIntensity < 0) {
23983
+ drawProjectedQuad(context, face.corners, `rgba(0, 0, 0, ${0.22 * Math.abs(face.lightIntensity)})`);
23984
+ }
23985
+ context.save();
23986
+ context.beginPath();
23987
+ context.moveTo(face.corners[0].x, face.corners[0].y);
23988
+ for (let cornerIndex = 1; cornerIndex < face.corners.length; cornerIndex++) {
23989
+ context.lineTo(face.corners[cornerIndex].x, face.corners[cornerIndex].y);
23990
+ }
23991
+ context.closePath();
23992
+ context.strokeStyle = face.outlineColor;
23993
+ context.lineWidth = Math.max(1.1, getProjectedQuadPerimeter(face.corners) * 0.0045);
23994
+ context.lineJoin = 'round';
23995
+ context.stroke();
23996
+ context.restore();
23564
23997
  }
23565
23998
  /**
23566
- * Creates the front-face pixel texture for the torso.
23999
+ * Draws one filled projected quad.
23567
24000
  *
23568
- * @param random Seeded random generator.
23569
- * @param palette Derived avatar palette.
23570
- * @returns 8x8 torso texture.
24001
+ * @param context Canvas 2D context.
24002
+ * @param corners Quad corners in clockwise order.
24003
+ * @param fillStyle CSS fill style.
23571
24004
  *
23572
- * @private helper of `minecraftAvatarVisual`
24005
+ * @private helper of `minecraft2AvatarVisual`
23573
24006
  */
23574
- function createMinecraftShirtTexture(random, palette) {
23575
- const texture = Array.from({ length: 8 }, () => Array.from({ length: 8 }, () => palette.primary));
23576
- const stripeColor = random() < 0.5 ? palette.secondary : palette.highlight;
23577
- for (let rowIndex = 0; rowIndex < 2; rowIndex++) {
23578
- for (let columnIndex = 0; columnIndex < 8; columnIndex++) {
23579
- texture[rowIndex][columnIndex] = palette.shadow;
23580
- }
24007
+ function drawProjectedQuad(context, corners, fillStyle) {
24008
+ context.beginPath();
24009
+ context.moveTo(corners[0].x, corners[0].y);
24010
+ context.lineTo(corners[1].x, corners[1].y);
24011
+ context.lineTo(corners[2].x, corners[2].y);
24012
+ context.lineTo(corners[3].x, corners[3].y);
24013
+ context.closePath();
24014
+ context.fillStyle = fillStyle;
24015
+ context.fill();
24016
+ }
24017
+ /**
24018
+ * Interpolates one point inside a projected quad across both quad axes.
24019
+ *
24020
+ * @param corners Quad corners in clockwise order.
24021
+ * @param horizontalRatio Horizontal ratio in the range `[0, 1]`.
24022
+ * @param verticalRatio Vertical ratio in the range `[0, 1]`.
24023
+ * @returns Interpolated projected point.
24024
+ *
24025
+ * @private helper of `minecraft2AvatarVisual`
24026
+ */
24027
+ function interpolateProjectedQuad(corners, horizontalRatio, verticalRatio) {
24028
+ const topPoint = interpolateProjectedPoint(corners[0], corners[1], horizontalRatio);
24029
+ const bottomPoint = interpolateProjectedPoint(corners[3], corners[2], horizontalRatio);
24030
+ return interpolateProjectedPoint(topPoint, bottomPoint, verticalRatio);
24031
+ }
24032
+ /**
24033
+ * Interpolates between two projected points.
24034
+ *
24035
+ * @param startPoint Start point.
24036
+ * @param endPoint End point.
24037
+ * @param ratio Interpolation ratio in the range `[0, 1]`.
24038
+ * @returns Interpolated projected point.
24039
+ *
24040
+ * @private helper of `minecraft2AvatarVisual`
24041
+ */
24042
+ function interpolateProjectedPoint(startPoint, endPoint, ratio) {
24043
+ return {
24044
+ x: startPoint.x + (endPoint.x - startPoint.x) * ratio,
24045
+ y: startPoint.y + (endPoint.y - startPoint.y) * ratio,
24046
+ z: startPoint.z + (endPoint.z - startPoint.z) * ratio,
24047
+ };
24048
+ }
24049
+ /**
24050
+ * Projects one rotated scene point into canvas coordinates.
24051
+ *
24052
+ * @param point Scene point.
24053
+ * @param size Canvas size in CSS pixels.
24054
+ * @param sceneCenterX Horizontal scene center.
24055
+ * @param sceneCenterY Vertical scene center.
24056
+ * @returns Projected point.
24057
+ *
24058
+ * @private helper of `minecraft2AvatarVisual`
24059
+ */
24060
+ function projectScenePoint(point, size, sceneCenterX, sceneCenterY) {
24061
+ const cameraDistance = size * CAMERA_DISTANCE_RATIO;
24062
+ const perspectiveScale = cameraDistance / Math.max(cameraDistance - point.z, cameraDistance * 0.35);
24063
+ return {
24064
+ x: sceneCenterX + point.x * perspectiveScale,
24065
+ y: sceneCenterY + point.y * perspectiveScale,
24066
+ z: point.z,
24067
+ };
24068
+ }
24069
+ /**
24070
+ * Applies the local cuboid rotations and translation to one scene point.
24071
+ *
24072
+ * @param localPoint Point in cuboid-local space.
24073
+ * @param center Cuboid center in scene space.
24074
+ * @param rotationX Cuboid pitch in radians.
24075
+ * @param rotationY Cuboid yaw in radians.
24076
+ * @returns Transformed scene-space point.
24077
+ *
24078
+ * @private helper of `minecraft2AvatarVisual`
24079
+ */
24080
+ function transformScenePoint(localPoint, center, rotationX, rotationY) {
24081
+ const yawedPoint = rotatePointAroundY(localPoint, rotationY);
24082
+ const pitchedPoint = rotatePointAroundX(yawedPoint, rotationX);
24083
+ return {
24084
+ x: center.x + pitchedPoint.x,
24085
+ y: center.y + pitchedPoint.y,
24086
+ z: center.z + pitchedPoint.z,
24087
+ };
24088
+ }
24089
+ /**
24090
+ * Rotates one point around the local Y axis.
24091
+ *
24092
+ * @param point Source point.
24093
+ * @param angle Rotation angle in radians.
24094
+ * @returns Rotated point.
24095
+ *
24096
+ * @private helper of `minecraft2AvatarVisual`
24097
+ */
24098
+ function rotatePointAroundY(point, angle) {
24099
+ const cosine = Math.cos(angle);
24100
+ const sine = Math.sin(angle);
24101
+ return {
24102
+ x: point.x * cosine + point.z * sine,
24103
+ y: point.y,
24104
+ z: -point.x * sine + point.z * cosine,
24105
+ };
24106
+ }
24107
+ /**
24108
+ * Rotates one point around the local X axis.
24109
+ *
24110
+ * @param point Source point.
24111
+ * @param angle Rotation angle in radians.
24112
+ * @returns Rotated point.
24113
+ *
24114
+ * @private helper of `minecraft2AvatarVisual`
24115
+ */
24116
+ function rotatePointAroundX(point, angle) {
24117
+ const cosine = Math.cos(angle);
24118
+ const sine = Math.sin(angle);
24119
+ return {
24120
+ x: point.x,
24121
+ y: point.y * cosine - point.z * sine,
24122
+ z: point.y * sine + point.z * cosine,
24123
+ };
24124
+ }
24125
+ /**
24126
+ * Subtracts one 3D point from another.
24127
+ *
24128
+ * @param leftPoint Left point.
24129
+ * @param rightPoint Right point.
24130
+ * @returns Difference vector.
24131
+ *
24132
+ * @private helper of `minecraft2AvatarVisual`
24133
+ */
24134
+ function subtractPoint3D(leftPoint, rightPoint) {
24135
+ return {
24136
+ x: leftPoint.x - rightPoint.x,
24137
+ y: leftPoint.y - rightPoint.y,
24138
+ z: leftPoint.z - rightPoint.z,
24139
+ };
24140
+ }
24141
+ /**
24142
+ * Computes the 3D cross product of two vectors.
24143
+ *
24144
+ * @param leftVector Left vector.
24145
+ * @param rightVector Right vector.
24146
+ * @returns Cross product.
24147
+ *
24148
+ * @private helper of `minecraft2AvatarVisual`
24149
+ */
24150
+ function crossProduct3D(leftVector, rightVector) {
24151
+ return {
24152
+ x: leftVector.y * rightVector.z - leftVector.z * rightVector.y,
24153
+ y: leftVector.z * rightVector.x - leftVector.x * rightVector.z,
24154
+ z: leftVector.x * rightVector.y - leftVector.y * rightVector.x,
24155
+ };
24156
+ }
24157
+ /**
24158
+ * Computes the 3D dot product of two vectors.
24159
+ *
24160
+ * @param leftVector Left vector.
24161
+ * @param rightVector Right vector.
24162
+ * @returns Dot product.
24163
+ *
24164
+ * @private helper of `minecraft2AvatarVisual`
24165
+ */
24166
+ function dotProduct3D(leftVector, rightVector) {
24167
+ return leftVector.x * rightVector.x + leftVector.y * rightVector.y + leftVector.z * rightVector.z;
24168
+ }
24169
+ /**
24170
+ * Normalizes one 3D vector while keeping zero vectors stable.
24171
+ *
24172
+ * @param vector Source vector.
24173
+ * @returns Normalized vector.
24174
+ *
24175
+ * @private helper of `minecraft2AvatarVisual`
24176
+ */
24177
+ function normalizeVector3(vector) {
24178
+ const length = Math.hypot(vector.x, vector.y, vector.z);
24179
+ if (length === 0) {
24180
+ return vector;
23581
24181
  }
23582
- for (let rowIndex = 2; rowIndex < 8; rowIndex++) {
23583
- texture[rowIndex][3] = stripeColor;
23584
- texture[rowIndex][4] = stripeColor;
24182
+ return {
24183
+ x: vector.x / length,
24184
+ y: vector.y / length,
24185
+ z: vector.z / length,
24186
+ };
24187
+ }
24188
+ /**
24189
+ * Clamps one number into the provided range.
24190
+ *
24191
+ * @param value Input value.
24192
+ * @param minimumValue Inclusive lower bound.
24193
+ * @param maximumValue Inclusive upper bound.
24194
+ * @returns Clamped value.
24195
+ *
24196
+ * @private helper of `minecraft2AvatarVisual`
24197
+ */
24198
+ function clampNumber$1(value, minimumValue, maximumValue) {
24199
+ return Math.min(maximumValue, Math.max(minimumValue, value));
24200
+ }
24201
+ /**
24202
+ * Measures the perimeter of one projected quad.
24203
+ *
24204
+ * @param corners Quad corners.
24205
+ * @returns Perimeter length.
24206
+ *
24207
+ * @private helper of `minecraft2AvatarVisual`
24208
+ */
24209
+ function getProjectedQuadPerimeter(corners) {
24210
+ let perimeter = 0;
24211
+ for (let cornerIndex = 0; cornerIndex < corners.length; cornerIndex++) {
24212
+ const currentCorner = corners[cornerIndex];
24213
+ const nextCorner = corners[(cornerIndex + 1) % corners.length];
24214
+ perimeter += Math.hypot(nextCorner.x - currentCorner.x, nextCorner.y - currentCorner.y);
23585
24215
  }
23586
- texture[4][1] = palette.accent;
23587
- texture[4][6] = palette.accent;
23588
- texture[5][2] = palette.highlight;
23589
- texture[5][5] = palette.highlight;
23590
- return texture;
24216
+ return perimeter;
23591
24217
  }
23592
24218
 
23593
24219
  /* eslint-disable no-magic-numbers */
@@ -25192,6 +25818,7 @@ const AVATAR_VISUALS = [
25192
25818
  octopus3AvatarVisual,
25193
25819
  asciiOctopusAvatarVisual,
25194
25820
  minecraftAvatarVisual,
25821
+ minecraft2AvatarVisual,
25195
25822
  fractalAvatarVisual,
25196
25823
  orbAvatarVisual,
25197
25824
  ];
@@ -27400,7 +28027,10 @@ function buildTeamSystemMessageBody(teamEntries) {
27400
28027
  */
27401
28028
  function buildTeamToolDescription(entry) {
27402
28029
  const detailLines = collectTeamEntryDetails(entry).map(({ label, content }) => `${label}: ${content}`);
27403
- return [`Consult teammate ${entry.teammate.label}`, ...detailLines].join('\n');
28030
+ return spaceTrim$1((block) => `
28031
+ Consult teammate ${entry.teammate.label}
28032
+ ${block(detailLines.join('\n'))}
28033
+ `);
27404
28034
  }
27405
28035
  /**
27406
28036
  * Collects structured teammate details that should stay visible to the model.
@@ -32687,7 +33317,10 @@ function createListedTimeoutsAssistantMessage(options) {
32687
33317
  if (hiddenCount > 0) {
32688
33318
  summaryRows.push(`...and ${hiddenCount} more.`);
32689
33319
  }
32690
- return [`Found ${options.total} ${options.total === 1 ? 'timeout' : 'timeouts'}:`, ...summaryRows].join('\n');
33320
+ return spaceTrim$1((block) => `
33321
+ Found ${options.total} ${options.total === 1 ? 'timeout' : 'timeouts'}:
33322
+ ${block(summaryRows.join('\n'))}
33323
+ `);
32691
33324
  }
32692
33325
  /**
32693
33326
  * Formats one timeout row for assistant-visible timeout listings.