@promptbook/core 0.112.0-98 → 0.112.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. package/README.md +37 -21
  2. package/esm/index.es.js +1767 -128
  3. package/esm/index.es.js.map +1 -1
  4. package/esm/servers.d.ts +1 -9
  5. package/esm/src/_packages/components.index.d.ts +4 -0
  6. package/esm/src/_packages/core.index.d.ts +0 -2
  7. package/esm/src/_packages/node.index.d.ts +40 -0
  8. package/esm/src/_packages/types.index.d.ts +18 -0
  9. package/esm/src/avatars/avatarAnimationScheduler.d.ts +4 -0
  10. package/esm/src/avatars/types/AvatarVisualDefinition.d.ts +1 -1
  11. package/esm/src/avatars/visuals/octopus3d3AvatarVisual.d.ts +7 -0
  12. package/esm/src/avatars/visuals/octopus3d4AvatarVisual.d.ts +7 -0
  13. package/esm/src/book-3.0/BookNodeAgentSource.d.ts +38 -0
  14. package/esm/src/book-3.0/CliAgent.d.ts +66 -0
  15. package/esm/src/book-3.0/CliAgent.test.d.ts +1 -0
  16. package/esm/src/book-3.0/LiteAgent.d.ts +68 -0
  17. package/esm/src/book-3.0/LiteAgent.test.d.ts +1 -0
  18. package/esm/src/book-3.0/agentFolderPaths.d.ts +30 -0
  19. package/esm/src/book-3.0/cliAgentEnv.d.ts +33 -0
  20. package/esm/src/book-components/BookEditor/BookEditor.d.ts +6 -5
  21. package/esm/src/book-components/BookEditor/BookEditorAboutPromptbookInformation.d.ts +12 -0
  22. package/esm/src/book-components/BookEditor/BookEditorBrowserConfig.d.ts +2 -0
  23. package/esm/src/book-components/BookEditor/BookEditorForClient.d.ts +7 -0
  24. package/esm/src/book-components/BookEditor/BookEditorMonacoTokenization.d.ts +1 -0
  25. package/esm/src/book-components/BookEditor/BookEditorTheme.d.ts +24 -0
  26. package/esm/src/book-components/BookEditor/createDeprecatedCommitmentDiagnostics.browser.d.ts +9 -0
  27. package/esm/src/book-components/BookEditor/useBookEditorMonacoLanguage.d.ts +1 -6
  28. package/esm/src/book-components/BookEditor/useBookEditorMonacoLifecycle.d.ts +1 -4
  29. package/esm/src/book-components/BookEditor/useBookEditorMonacoStyles.d.ts +2 -1
  30. package/esm/src/book-components/Chat/Chat/ChatCitationModal.d.ts +2 -0
  31. package/esm/src/book-components/Chat/Chat/ChatMessageItem.d.ts +4 -0
  32. package/esm/src/book-components/Chat/Chat/ChatMessageList.d.ts +4 -0
  33. package/esm/src/book-components/Chat/Chat/ChatProps.d.ts +5 -0
  34. package/esm/src/book-components/Chat/Chat/ChatToolCallModal.d.ts +2 -0
  35. package/esm/src/book-components/Chat/Chat/ChatToolCallModalContent.d.ts +3 -1
  36. package/esm/src/book-components/Chat/Chat/CitationIframePreview.d.ts +20 -0
  37. package/esm/src/book-components/Chat/Chat/TeamToolCallModalContent.d.ts +2 -0
  38. package/esm/src/book-components/Chat/MarkdownContent/MarkdownContent.d.ts +1 -0
  39. package/esm/src/book-components/Chat/SourceChip/SourceChip.d.ts +6 -1
  40. package/esm/src/book-components/Chat/hooks/useResolvedCitationLabel.d.ts +12 -0
  41. package/esm/src/book-components/Chat/types/ChatMessage.d.ts +4 -0
  42. package/esm/src/book-components/Chat/types/ChatParticipant.d.ts +1 -1
  43. package/esm/src/book-components/Chat/types/CitationLabelResolver.d.ts +8 -0
  44. package/esm/src/book-components/Chat/utils/citationHelpers.d.ts +9 -0
  45. package/esm/src/book-components/Chat/utils/decodeJsonUnicodeEscapesInMarkdownText.d.ts +14 -0
  46. package/esm/src/book-components/Chat/utils/decodeJsonUnicodeEscapesInMarkdownText.test.d.ts +1 -0
  47. package/esm/src/book-components/Chat/utils/isVisibleChatToolCall.d.ts +10 -0
  48. package/esm/src/book-components/Chat/utils/parseCitationsFromContent.d.ts +4 -0
  49. package/esm/src/book-components/_common/Dropdown/Dropdown.d.ts +1 -1
  50. package/esm/src/book-components/_common/MenuHoisting/MenuHoistingContext.d.ts +1 -1
  51. package/esm/src/book-components/_common/Modal/Modal.d.ts +1 -1
  52. package/esm/src/book-components/icons/AboutIcon.d.ts +1 -1
  53. package/esm/src/book-components/icons/DownloadIcon.d.ts +1 -1
  54. package/esm/src/book-components/icons/ExitFullscreenIcon.d.ts +1 -1
  55. package/esm/src/book-components/icons/FullscreenIcon.d.ts +1 -1
  56. package/esm/src/cli/cli-commands/agent/agentCliOptions.d.ts +38 -0
  57. package/esm/src/cli/cli-commands/agent/chat.d.ts +10 -0
  58. package/esm/src/cli/cli-commands/agent/exec.d.ts +10 -0
  59. package/esm/src/cli/cli-commands/agent/run.test.d.ts +1 -0
  60. package/esm/src/cli/cli-commands/agent-folder/agentProjectPaths.d.ts +2 -24
  61. package/esm/src/cli/cli-commands/agent.d.ts +14 -0
  62. package/esm/src/cli/cli-commands/agents-server/buildAgentsServer.d.ts +17 -1
  63. package/esm/src/cli/cli-commands/agents-server/run.d.ts +6 -0
  64. package/esm/src/cli/cli-commands/agents-server/startAgentsServer.d.ts +9 -2
  65. package/esm/src/cli/cli-commands/coder/ThinkingLevel.d.ts +1 -1
  66. package/esm/src/cli/cli-commands/coder/ensureCoderDeveloperAgentFile.d.ts +25 -0
  67. package/esm/src/cli/cli-commands/coder/find-unwritten.d.ts +10 -0
  68. package/esm/src/cli/cli-commands/coder/initializeCoderProjectConfiguration.d.ts +2 -0
  69. package/esm/src/cli/cli-commands/coder/server.d.ts +13 -0
  70. package/esm/src/cli/cli-commands/coder/waitOptions.d.ts +14 -0
  71. package/esm/src/cli/cli-commands/common/promptRunnerCliOptions.d.ts +9 -25
  72. package/esm/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.d.ts +22 -0
  73. package/esm/src/commitments/_common/teamInternalAgentAccess.d.ts +9 -1
  74. package/esm/src/llm-providers/_common/register/$provideLlmToolsConfigurationFromEnv.d.ts +1 -1
  75. package/esm/src/llm-providers/_common/register/$provideLlmToolsFromEnv.d.ts +1 -1
  76. package/esm/src/scrapers/website/utils/createShowdownConverter.d.ts +2 -2
  77. package/esm/src/utils/isTimingSafeEqualString.d.ts +25 -0
  78. package/esm/src/utils/validators/url/isValidAgentUrl.d.ts +5 -0
  79. package/esm/src/version.d.ts +1 -1
  80. package/package.json +1 -1
  81. package/umd/index.umd.js +1769 -129
  82. package/umd/index.umd.js.map +1 -1
  83. package/umd/servers.d.ts +1 -9
  84. package/umd/src/_packages/components.index.d.ts +4 -0
  85. package/umd/src/_packages/core.index.d.ts +0 -2
  86. package/umd/src/_packages/node.index.d.ts +40 -0
  87. package/umd/src/_packages/types.index.d.ts +18 -0
  88. package/umd/src/avatars/avatarAnimationScheduler.d.ts +4 -0
  89. package/umd/src/avatars/types/AvatarVisualDefinition.d.ts +1 -1
  90. package/umd/src/avatars/visuals/octopus3d3AvatarVisual.d.ts +7 -0
  91. package/umd/src/avatars/visuals/octopus3d4AvatarVisual.d.ts +7 -0
  92. package/umd/src/book-3.0/BookNodeAgentSource.d.ts +38 -0
  93. package/umd/src/book-3.0/CliAgent.d.ts +66 -0
  94. package/umd/src/book-3.0/CliAgent.test.d.ts +1 -0
  95. package/umd/src/book-3.0/LiteAgent.d.ts +68 -0
  96. package/umd/src/book-3.0/LiteAgent.test.d.ts +1 -0
  97. package/umd/src/book-3.0/agentFolderPaths.d.ts +30 -0
  98. package/umd/src/book-3.0/cliAgentEnv.d.ts +33 -0
  99. package/umd/src/book-components/BookEditor/BookEditor.d.ts +6 -5
  100. package/umd/src/book-components/BookEditor/BookEditorAboutPromptbookInformation.d.ts +12 -0
  101. package/umd/src/book-components/BookEditor/BookEditorBrowserConfig.d.ts +2 -0
  102. package/umd/src/book-components/BookEditor/BookEditorForClient.d.ts +7 -0
  103. package/umd/src/book-components/BookEditor/BookEditorMonacoTokenization.d.ts +1 -0
  104. package/umd/src/book-components/BookEditor/BookEditorTheme.d.ts +24 -0
  105. package/umd/src/book-components/BookEditor/createDeprecatedCommitmentDiagnostics.browser.d.ts +9 -0
  106. package/umd/src/book-components/BookEditor/useBookEditorMonacoLanguage.d.ts +1 -6
  107. package/umd/src/book-components/BookEditor/useBookEditorMonacoLifecycle.d.ts +1 -4
  108. package/umd/src/book-components/BookEditor/useBookEditorMonacoStyles.d.ts +2 -1
  109. package/umd/src/book-components/Chat/Chat/ChatCitationModal.d.ts +2 -0
  110. package/umd/src/book-components/Chat/Chat/ChatMessageItem.d.ts +4 -0
  111. package/umd/src/book-components/Chat/Chat/ChatMessageList.d.ts +4 -0
  112. package/umd/src/book-components/Chat/Chat/ChatProps.d.ts +5 -0
  113. package/umd/src/book-components/Chat/Chat/ChatToolCallModal.d.ts +2 -0
  114. package/umd/src/book-components/Chat/Chat/ChatToolCallModalContent.d.ts +3 -1
  115. package/umd/src/book-components/Chat/Chat/CitationIframePreview.d.ts +20 -0
  116. package/umd/src/book-components/Chat/Chat/TeamToolCallModalContent.d.ts +2 -0
  117. package/umd/src/book-components/Chat/MarkdownContent/MarkdownContent.d.ts +1 -0
  118. package/umd/src/book-components/Chat/SourceChip/SourceChip.d.ts +6 -1
  119. package/umd/src/book-components/Chat/hooks/useResolvedCitationLabel.d.ts +12 -0
  120. package/umd/src/book-components/Chat/types/ChatMessage.d.ts +4 -0
  121. package/umd/src/book-components/Chat/types/ChatParticipant.d.ts +1 -1
  122. package/umd/src/book-components/Chat/types/CitationLabelResolver.d.ts +8 -0
  123. package/umd/src/book-components/Chat/utils/citationHelpers.d.ts +9 -0
  124. package/umd/src/book-components/Chat/utils/decodeJsonUnicodeEscapesInMarkdownText.d.ts +14 -0
  125. package/umd/src/book-components/Chat/utils/decodeJsonUnicodeEscapesInMarkdownText.test.d.ts +1 -0
  126. package/umd/src/book-components/Chat/utils/isVisibleChatToolCall.d.ts +10 -0
  127. package/umd/src/book-components/Chat/utils/parseCitationsFromContent.d.ts +4 -0
  128. package/umd/src/book-components/_common/Dropdown/Dropdown.d.ts +1 -1
  129. package/umd/src/book-components/_common/MenuHoisting/MenuHoistingContext.d.ts +1 -1
  130. package/umd/src/book-components/_common/Modal/Modal.d.ts +1 -1
  131. package/umd/src/book-components/icons/AboutIcon.d.ts +1 -1
  132. package/umd/src/book-components/icons/DownloadIcon.d.ts +1 -1
  133. package/umd/src/book-components/icons/ExitFullscreenIcon.d.ts +1 -1
  134. package/umd/src/book-components/icons/FullscreenIcon.d.ts +1 -1
  135. package/umd/src/cli/cli-commands/agent/agentCliOptions.d.ts +38 -0
  136. package/umd/src/cli/cli-commands/agent/chat.d.ts +10 -0
  137. package/umd/src/cli/cli-commands/agent/exec.d.ts +10 -0
  138. package/umd/src/cli/cli-commands/agent/run.test.d.ts +1 -0
  139. package/umd/src/cli/cli-commands/agent-folder/agentProjectPaths.d.ts +2 -24
  140. package/umd/src/cli/cli-commands/agent.d.ts +14 -0
  141. package/umd/src/cli/cli-commands/agents-server/buildAgentsServer.d.ts +17 -1
  142. package/umd/src/cli/cli-commands/agents-server/run.d.ts +6 -0
  143. package/umd/src/cli/cli-commands/agents-server/startAgentsServer.d.ts +9 -2
  144. package/umd/src/cli/cli-commands/coder/ThinkingLevel.d.ts +1 -1
  145. package/umd/src/cli/cli-commands/coder/ensureCoderDeveloperAgentFile.d.ts +25 -0
  146. package/umd/src/cli/cli-commands/coder/find-unwritten.d.ts +10 -0
  147. package/umd/src/cli/cli-commands/coder/initializeCoderProjectConfiguration.d.ts +2 -0
  148. package/umd/src/cli/cli-commands/coder/server.d.ts +13 -0
  149. package/umd/src/cli/cli-commands/coder/waitOptions.d.ts +14 -0
  150. package/umd/src/cli/cli-commands/common/promptRunnerCliOptions.d.ts +9 -25
  151. package/umd/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.d.ts +22 -0
  152. package/umd/src/commitments/_common/teamInternalAgentAccess.d.ts +9 -1
  153. package/umd/src/llm-providers/_common/register/$provideLlmToolsConfigurationFromEnv.d.ts +1 -1
  154. package/umd/src/llm-providers/_common/register/$provideLlmToolsFromEnv.d.ts +1 -1
  155. package/umd/src/scrapers/website/utils/createShowdownConverter.d.ts +2 -2
  156. package/umd/src/utils/isTimingSafeEqualString.d.ts +25 -0
  157. package/umd/src/utils/validators/url/isValidAgentUrl.d.ts +5 -0
  158. package/umd/src/version.d.ts +1 -1
package/esm/index.es.js CHANGED
@@ -1,14 +1,14 @@
1
- import { SHA256 } from 'crypto-js';
1
+ import CryptoJS from 'crypto-js';
2
2
  import hexEncoder from 'crypto-js/enc-hex';
3
3
  import { spaceTrim as spaceTrim$1 } from 'spacetrim';
4
- import { randomBytes } from 'crypto';
5
4
  import { Subject, BehaviorSubject } from 'rxjs';
6
5
  import { forTime } from 'waitasecond';
7
6
  import sha256 from 'crypto-js/sha256';
8
7
  import { basename, join, dirname, isAbsolute } from 'path';
9
8
  import { lookup, extension } from 'mime-types';
10
- import { parse, unparse } from 'papaparse';
9
+ import papaparse from 'papaparse';
11
10
  import moment from 'moment';
11
+ import { randomBytes } from 'crypto';
12
12
  import colors from 'colors';
13
13
  import { fileSearchTool, tool, Agent as Agent$1, webSearchTool, run, setDefaultOpenAIClient, setDefaultOpenAIKey } from '@openai/agents';
14
14
  import Bottleneck from 'bottleneck';
@@ -28,7 +28,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
28
28
  * @generated
29
29
  * @see https://github.com/webgptorg/promptbook
30
30
  */
31
- const PROMPTBOOK_ENGINE_VERSION = '0.112.0-98';
31
+ const PROMPTBOOK_ENGINE_VERSION = '0.112.0';
32
32
  /**
33
33
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
34
34
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -1653,7 +1653,7 @@ function valueToString(value) {
1653
1653
  * @public exported from `@promptbook/utils`
1654
1654
  */
1655
1655
  function computeHash(value) {
1656
- return SHA256(hexEncoder.parse(spaceTrim$1(valueToString(value)))).toString( /* hex */);
1656
+ return CryptoJS.SHA256(hexEncoder.parse(spaceTrim$1(valueToString(value)))).toString( /* hex */);
1657
1657
  }
1658
1658
  // TODO: [🥬][🥬] Use this ACRY
1659
1659
 
@@ -3773,7 +3773,7 @@ class NotYetImplementedError extends Error {
3773
3773
  * @private internal helper function
3774
3774
  */
3775
3775
  function $randomToken(randomness) {
3776
- return randomBytes(randomness).toString('hex');
3776
+ return CryptoJS.lib.WordArray.random(randomness).toString(CryptoJS.enc.Hex);
3777
3777
  }
3778
3778
  // TODO: [🤶] Maybe export through `@promptbook/utils` or `@promptbook/random` package
3779
3779
  // TODO: Maybe use nanoid instead https://github.com/ai/nanoid
@@ -5457,7 +5457,7 @@ function normalizeToKebabCase(text) {
5457
5457
  * @public exported from `@promptbook/editable`
5458
5458
  */
5459
5459
  function knowledgeSourceContentToName(knowledgeSourceContent) {
5460
- const hash = SHA256(hexEncoder.parse(JSON.stringify(knowledgeSourceContent)))
5460
+ const hash = CryptoJS.SHA256(hexEncoder.parse(JSON.stringify(knowledgeSourceContent)))
5461
5461
  // <- TODO: [🥬] Encapsulate sha256 to some private utility function
5462
5462
  .toString( /* hex */)
5463
5463
  .substring(0, 20);
@@ -6370,7 +6370,7 @@ function csvParse(value /* <- TODO: string_csv */, settings, schema /* <- TODO:
6370
6370
  console.warn('CSV string contains carriage return characters, but in the CSV settings the `newline` setting does not include them. Autohealing the CSV string.');
6371
6371
  value = value.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
6372
6372
  }
6373
- const csv = parse(value, settings);
6373
+ const csv = papaparse.parse(value, settings);
6374
6374
  return csv;
6375
6375
  }
6376
6376
 
@@ -6455,10 +6455,10 @@ const CsvFormatParser = {
6455
6455
  i > index ? { ...row, [outputParameterName]: PENDING_VALUE_PLACEHOLDER } : row,
6456
6456
  );
6457
6457
  */
6458
- await onProgress(unparse(mappedData, { ...settings, ...MANDATORY_CSV_SETTINGS }));
6458
+ await onProgress(papaparse.unparse(mappedData, { ...settings, ...MANDATORY_CSV_SETTINGS }));
6459
6459
  }
6460
6460
  }
6461
- return unparse(mappedData, { ...settings, ...MANDATORY_CSV_SETTINGS });
6461
+ return papaparse.unparse(mappedData, { ...settings, ...MANDATORY_CSV_SETTINGS });
6462
6462
  },
6463
6463
  },
6464
6464
  {
@@ -6486,7 +6486,7 @@ const CsvFormatParser = {
6486
6486
  return /* not await */ mapCallback({ [key]: value }, index, array.length);
6487
6487
  }));
6488
6488
  }));
6489
- return unparse(mappedData, { ...settings, ...MANDATORY_CSV_SETTINGS });
6489
+ return papaparse.unparse(mappedData, { ...settings, ...MANDATORY_CSV_SETTINGS });
6490
6490
  },
6491
6491
  },
6492
6492
  ],
@@ -9898,6 +9898,11 @@ function isVoidPseudoAgentReference(rawReference) {
9898
9898
  * - `isValidAgentUrl` *(this one)* which tests just agent URL
9899
9899
  * - `isValidPipelineUrl` which tests just pipeline URL
9900
9900
  *
9901
+ * Note: This is a pure structural validator and does not block private/internal network
9902
+ * addresses. Callers that fetch the URL server-side from an untrusted network context must
9903
+ * additionally apply the `assertSafeUrl` SSRF guard from the Agents Server before any
9904
+ * outbound request.
9905
+ *
9901
9906
  * @public exported from `@promptbook/utils`
9902
9907
  */
9903
9908
  function isValidAgentUrl(url) {
@@ -9911,12 +9916,6 @@ function isValidAgentUrl(url) {
9911
9916
  // TODO: [🐠]
9912
9917
  return false;
9913
9918
  }
9914
- /*
9915
- Note: [👣][🧠] Is it secure to allow pipeline URLs on private and unsecured networks?
9916
- if (isUrlOnPrivateNetwork(url)) {
9917
- return false;
9918
- }
9919
- */
9920
9919
  return true;
9921
9920
  }
9922
9921
  // TODO: [🐠] Maybe more info why the URL is invalid
@@ -10092,8 +10091,11 @@ class GoalCommitmentDefinition extends BaseCommitmentDefinition {
10092
10091
  if (!trimmedContent) {
10093
10092
  return requirements;
10094
10093
  }
10095
- // Add goal as a proper h2 section to the system message
10096
- const goalSection = `## Goal\n\n${trimmedContent}`;
10094
+ const goalSection = spaceTrim$1((block) => `
10095
+ ## Goal
10096
+
10097
+ ${block(trimmedContent)}
10098
+ `);
10097
10099
  const requirementsWithGoal = this.appendToSystemMessage(requirements, goalSection, '\n\n');
10098
10100
  return this.appendToPromptSuffix(requirementsWithGoal, trimmedContent);
10099
10101
  }
@@ -10615,8 +10617,11 @@ class LanguageCommitmentDefinition extends BaseCommitmentDefinition {
10615
10617
  if (!trimmedContent) {
10616
10618
  return requirements;
10617
10619
  }
10618
- // Add language as a bullet under a ## Language section
10619
- const languageSection = `## Language\n\n- Your language is ${trimmedContent}`;
10620
+ const languageSection = spaceTrim$1((block) => `
10621
+ ## Language
10622
+
10623
+ - Your language is ${block(trimmedContent)}
10624
+ `);
10620
10625
  return this.appendToSystemMessage(requirements, languageSection, '\n\n');
10621
10626
  }
10622
10627
  }
@@ -13063,21 +13068,22 @@ function getPointBounds(points) {
13063
13068
  * @private helper of `fractalAvatarVisual`
13064
13069
  */
13065
13070
  function drawDragonCurveLayer(context, points, options) {
13066
- const { size, primaryColor, secondaryColor, tertiaryColor, shadowColor, strokeWidth, timeMs, layerIndex } = options;
13071
+ const { primaryColor, secondaryColor, tertiaryColor, shadowColor, strokeWidth, timeMs, layerIndex } = options;
13067
13072
  const firstPoint = points[0];
13068
13073
  const lastPoint = points[points.length - 1];
13069
13074
  const ribbonGradient = context.createLinearGradient(firstPoint.x, firstPoint.y, lastPoint.x, lastPoint.y);
13070
13075
  ribbonGradient.addColorStop(0, `${primaryColor}f2`);
13071
13076
  ribbonGradient.addColorStop(0.5, `${secondaryColor}e6`);
13072
13077
  ribbonGradient.addColorStop(1, `${tertiaryColor}f2`);
13078
+ // Approximate the blurred shadow stroke with a wider semi-transparent stroke instead of
13079
+ // context.filter blur, which triggers a costly software rasterization pass every frame.
13073
13080
  context.save();
13074
13081
  context.beginPath();
13075
13082
  tracePolyline(context, points);
13076
- context.strokeStyle = `${shadowColor}82`;
13077
- context.lineWidth = strokeWidth * 1.8;
13083
+ context.strokeStyle = `${shadowColor}48`;
13084
+ context.lineWidth = strokeWidth * 4.5;
13078
13085
  context.lineJoin = 'round';
13079
13086
  context.lineCap = 'round';
13080
- context.filter = `blur(${size * 0.022}px)`;
13081
13087
  context.stroke();
13082
13088
  context.restore();
13083
13089
  context.beginPath();
@@ -13539,7 +13545,7 @@ function fillTextureRect(texture, x, y, width, height, color) {
13539
13545
  *
13540
13546
  * @private helper of `minecraft2AvatarVisual`
13541
13547
  */
13542
- const LIGHT_DIRECTION$2 = normalizeVector3({
13548
+ const LIGHT_DIRECTION$4 = normalizeVector3({
13543
13549
  x: 0.4,
13544
13550
  y: -0.65,
13545
13551
  z: 0.92,
@@ -13661,11 +13667,23 @@ function drawMinecraftBackdrop(context, size, palette, sceneCenterX, spotlightY,
13661
13667
  * @private helper of `minecraft2AvatarVisual`
13662
13668
  */
13663
13669
  function drawMinecraftShadow(context, size, palette, interaction, timeMs) {
13670
+ const cx = size * 0.5 + interaction.gazeX * size * 0.03;
13671
+ const cy = size * 0.85 + Math.sin(timeMs / 880) * size * 0.01;
13672
+ const rx = size * (0.16 + interaction.intensity * 0.015);
13673
+ const ry = size * 0.055;
13674
+ // Radial gradient approximates the blurry ellipse shadow without context.filter blur.
13664
13675
  context.save();
13665
- context.fillStyle = `${palette.shadow}66`;
13666
- context.filter = `blur(${size * 0.02}px)`;
13676
+ context.translate(cx, cy);
13677
+ context.scale(1, ry / rx);
13678
+ const blurRadius = rx * 1.4;
13679
+ const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
13680
+ shadowGradient.addColorStop(0, `${palette.shadow}7a`);
13681
+ shadowGradient.addColorStop(0.45, `${palette.shadow}44`);
13682
+ shadowGradient.addColorStop(0.8, `${palette.shadow}1a`);
13683
+ shadowGradient.addColorStop(1, `${palette.shadow}00`);
13684
+ context.fillStyle = shadowGradient;
13667
13685
  context.beginPath();
13668
- 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);
13686
+ context.arc(0, 0, blurRadius, 0, Math.PI * 2);
13669
13687
  context.fill();
13670
13688
  context.restore();
13671
13689
  }
@@ -13751,7 +13769,7 @@ function resolveVisibleCuboidFaces(cuboid, size, sceneCenterX, sceneCenterY) {
13751
13769
  corners: projectedCorners,
13752
13770
  texture: faceDefinition.texture,
13753
13771
  averageDepth: transformedCorners.reduce((depthSum, corner) => depthSum + corner.z, 0) / transformedCorners.length,
13754
- lightIntensity: clampNumber$1(dotProduct3D(faceNormal, LIGHT_DIRECTION$2), -1, 1),
13772
+ lightIntensity: clampNumber$1(dotProduct3D(faceNormal, LIGHT_DIRECTION$4), -1, 1),
13755
13773
  outlineColor: cuboid.outlineColor,
13756
13774
  };
13757
13775
  });
@@ -13889,13 +13907,27 @@ const minecraftAvatarVisual = {
13889
13907
  spotlight.addColorStop(1, `${palette.highlight}00`);
13890
13908
  context.fillStyle = spotlight;
13891
13909
  context.fillRect(0, 0, size, size);
13892
- context.save();
13893
- context.fillStyle = 'rgba(0, 0, 0, 0.22)';
13894
- context.filter = `blur(${size * 0.018}px)`;
13895
- context.beginPath();
13896
- context.ellipse(size * 0.5, size * 0.86, size * 0.2, size * 0.06, 0, 0, Math.PI * 2);
13897
- context.fill();
13898
- context.restore();
13910
+ {
13911
+ // Radial gradient approximates the blurry ellipse shadow without context.filter blur.
13912
+ const cx = size * 0.5;
13913
+ const cy = size * 0.86;
13914
+ const rx = size * 0.2;
13915
+ const ry = size * 0.06;
13916
+ const blurRadius = rx * 1.4;
13917
+ const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
13918
+ shadowGradient.addColorStop(0, 'rgba(0,0,0,0.28)');
13919
+ shadowGradient.addColorStop(0.45, 'rgba(0,0,0,0.14)');
13920
+ shadowGradient.addColorStop(0.8, 'rgba(0,0,0,0.05)');
13921
+ shadowGradient.addColorStop(1, 'rgba(0,0,0,0)');
13922
+ context.save();
13923
+ context.translate(cx, cy);
13924
+ context.scale(1, ry / rx);
13925
+ context.fillStyle = shadowGradient;
13926
+ context.beginPath();
13927
+ context.arc(0, 0, blurRadius, 0, Math.PI * 2);
13928
+ context.fill();
13929
+ context.restore();
13930
+ }
13899
13931
  drawVoxelCuboid(context, {
13900
13932
  x: bodyX,
13901
13933
  y: bodyY,
@@ -14424,7 +14456,7 @@ function resolveSeededIntegerRange(random, minimumValue, maximumValue) {
14424
14456
  *
14425
14457
  * @private helper of `octopus3AvatarVisual`
14426
14458
  */
14427
- function formatAlphaHex$1(opacity) {
14459
+ function formatAlphaHex$2(opacity) {
14428
14460
  return Math.round(Math.min(1, Math.max(0, opacity)) * 255)
14429
14461
  .toString(16)
14430
14462
  .padStart(2, '0');
@@ -14803,7 +14835,7 @@ function drawSeededEye(context, centerX, centerY, radiusX, radiusY, rotation, pa
14803
14835
  context.beginPath();
14804
14836
  context.moveTo(-radiusX * 0.74, radiusY * 0.2);
14805
14837
  context.quadraticCurveTo(0, radiusY * 0.38, radiusX * 0.74, radiusY * 0.2);
14806
- context.strokeStyle = `${palette.highlight}${formatAlphaHex$1(eyeStyle.lowerLidOpacity)}`;
14838
+ context.strokeStyle = `${palette.highlight}${formatAlphaHex$2(eyeStyle.lowerLidOpacity)}`;
14807
14839
  context.lineWidth = radiusX * 0.08;
14808
14840
  context.lineCap = 'round';
14809
14841
  context.stroke();
@@ -14884,7 +14916,7 @@ function drawProjectedOrganicEye(context, localCenter, radiusX, radiusY, center,
14884
14916
  context.beginPath();
14885
14917
  context.moveTo(-projectedRadiusX * 0.74, projectedRadiusY * 0.2);
14886
14918
  context.quadraticCurveTo(0, projectedRadiusY * 0.38, projectedRadiusX * 0.74, projectedRadiusY * 0.2);
14887
- context.strokeStyle = `${palette.highlight}${formatAlphaHex(eyeStyle.lowerLidOpacity)}`;
14919
+ context.strokeStyle = `${palette.highlight}${formatAlphaHex$1(eyeStyle.lowerLidOpacity)}`;
14888
14920
  context.lineWidth = projectedRadiusX * 0.08;
14889
14921
  context.lineCap = 'round';
14890
14922
  context.stroke();
@@ -14930,7 +14962,7 @@ function drawProjectedQuad(context, corners, fillStyle) {
14930
14962
  *
14931
14963
  * @private helper of the 3D octopus avatar visuals
14932
14964
  */
14933
- function formatAlphaHex(opacity) {
14965
+ function formatAlphaHex$1(opacity) {
14934
14966
  return Math.round(clampNumber$1(opacity, 0, 1) * 255)
14935
14967
  .toString(16)
14936
14968
  .padStart(2, '0');
@@ -14942,11 +14974,40 @@ function formatAlphaHex(opacity) {
14942
14974
  *
14943
14975
  * @private helper of `octopus3dAvatarVisual`
14944
14976
  */
14945
- const LIGHT_DIRECTION$1 = normalizeVector3({
14977
+ const LIGHT_DIRECTION$3 = normalizeVector3({
14946
14978
  x: 0.48,
14947
14979
  y: -0.62,
14948
14980
  z: 0.94,
14949
14981
  });
14982
+ /**
14983
+ * Cache keyed by the `createRandom` factory reference (stable per mounted `<Avatar/>`).
14984
+ *
14985
+ * @private helper of `octopus3dAvatarVisual`
14986
+ */
14987
+ const octopus3dStableStateCache = new WeakMap();
14988
+ /**
14989
+ * Returns the stable per-avatar state, computing it on first access and caching for subsequent frames.
14990
+ *
14991
+ * @private helper of `octopus3dAvatarVisual`
14992
+ */
14993
+ function getOctopus3dStableState(createRandom) {
14994
+ const cached = octopus3dStableStateCache.get(createRandom);
14995
+ if (cached !== undefined) {
14996
+ return cached;
14997
+ }
14998
+ const animationRandom = createRandom('octopus3d-animation-profile');
14999
+ const eyeRandom = createRandom('octopus3d-eye-profile');
15000
+ const leftEyePhaseOffset = eyeRandom() * 0.6;
15001
+ const rightEyePhaseOffset = eyeRandom() * 0.6;
15002
+ const state = {
15003
+ morphologyProfile: createOctopus3MorphologyProfile(createRandom),
15004
+ animationPhase: animationRandom() * Math.PI * 2,
15005
+ leftEyePhaseOffset,
15006
+ rightEyePhaseOffset,
15007
+ };
15008
+ octopus3dStableStateCache.set(createRandom, state);
15009
+ return state;
15010
+ }
14950
15011
  /**
14951
15012
  * Proper 3D Octopus visual built from projected organic meshes and tentacles.
14952
15013
  *
@@ -14959,10 +15020,7 @@ const octopus3dAvatarVisual = {
14959
15020
  isAnimated: true,
14960
15021
  supportsPointerTracking: true,
14961
15022
  render({ context, size, palette, createRandom, timeMs, interaction }) {
14962
- const morphologyProfile = createOctopus3MorphologyProfile(createRandom);
14963
- const animationRandom = createRandom('octopus3d-animation-profile');
14964
- const eyeRandom = createRandom('octopus3d-eye-profile');
14965
- const animationPhase = animationRandom() * Math.PI * 2;
15023
+ const { morphologyProfile, animationPhase, leftEyePhaseOffset, rightEyePhaseOffset } = getOctopus3dStableState(createRandom);
14966
15024
  const sceneCenterX = size * 0.5;
14967
15025
  const sceneCenterY = size * 0.56;
14968
15026
  const bob = Math.sin(timeMs / 920 + animationPhase) * size * 0.014;
@@ -15059,12 +15117,12 @@ const octopus3dAvatarVisual = {
15059
15117
  x: -faceEyeSpacing,
15060
15118
  y: faceEyeYOffset,
15061
15119
  z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, -faceEyeSpacing, faceEyeYOffset),
15062
- }, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + eyeRandom() * 0.6, interaction, morphologyProfile.face.eyeStyle);
15120
+ }, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + leftEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
15063
15121
  drawProjectedOrganicEye(context, {
15064
15122
  x: faceEyeSpacing,
15065
15123
  y: faceEyeYOffset,
15066
15124
  z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, faceEyeSpacing, faceEyeYOffset),
15067
- }, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.7 + eyeRandom() * 0.6, interaction, morphologyProfile.face.eyeStyle);
15125
+ }, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.7 + rightEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
15068
15126
  drawProjectedOrganicMouth(context, [
15069
15127
  {
15070
15128
  x: -mouthHalfWidth,
@@ -15108,14 +15166,28 @@ function drawOctopus3dAtmosphere(context, size, palette, sceneCenterX, sceneCent
15108
15166
  /**
15109
15167
  * Draws the soft ground shadow below the octopus.
15110
15168
  *
15169
+ * Uses a scaled radial gradient instead of `context.filter = 'blur()'` to approximate the
15170
+ * blurry ellipse without triggering a costly software rasterization pass on every frame.
15171
+ *
15111
15172
  * @private helper of `octopus3dAvatarVisual`
15112
15173
  */
15113
15174
  function drawOctopus3dShadow(context, size, palette, interaction, timeMs) {
15175
+ const cx = size * 0.5 + interaction.gazeX * size * 0.04;
15176
+ const cy = size * 0.87 + Math.sin(timeMs / 920) * size * 0.008;
15177
+ const rx = size * (0.18 + interaction.intensity * 0.02);
15178
+ const ry = size * 0.06;
15114
15179
  context.save();
15115
- context.fillStyle = `${palette.shadow}66`;
15116
- context.filter = `blur(${size * 0.022}px)`;
15180
+ context.translate(cx, cy);
15181
+ context.scale(1, ry / rx);
15182
+ const blurRadius = rx * 1.4;
15183
+ const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
15184
+ shadowGradient.addColorStop(0, `${palette.shadow}7a`);
15185
+ shadowGradient.addColorStop(0.45, `${palette.shadow}44`);
15186
+ shadowGradient.addColorStop(0.8, `${palette.shadow}1a`);
15187
+ shadowGradient.addColorStop(1, `${palette.shadow}00`);
15188
+ context.fillStyle = shadowGradient;
15117
15189
  context.beginPath();
15118
- context.ellipse(size * 0.5 + interaction.gazeX * size * 0.04, size * 0.87 + Math.sin(timeMs / 920) * size * 0.008, size * (0.18 + interaction.intensity * 0.02), size * 0.06, 0, 0, Math.PI * 2);
15190
+ context.arc(0, 0, blurRadius, 0, Math.PI * 2);
15119
15191
  context.fill();
15120
15192
  context.restore();
15121
15193
  }
@@ -15152,7 +15224,7 @@ function resolveVisibleEllipsoidPatches(options) {
15152
15224
  corners: projectedCorners,
15153
15225
  averageDepth: transformedCorners.reduce((depthSum, transformedCorner) => depthSum + transformedCorner.z, 0) /
15154
15226
  transformedCorners.length,
15155
- lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$1), -1, 1),
15227
+ lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$3), -1, 1),
15156
15228
  fillStyle: resolveSurfacePatchFillStyle(palette, verticalProgress + verticalColorBias),
15157
15229
  outlineColor,
15158
15230
  });
@@ -15341,11 +15413,40 @@ function resolveEllipsoidSurfaceDepth(radiusX, radiusY, radiusZ, x, y) {
15341
15413
  *
15342
15414
  * @private helper of `octopus3d2AvatarVisual`
15343
15415
  */
15344
- const LIGHT_DIRECTION = normalizeVector3({
15416
+ const LIGHT_DIRECTION$2 = normalizeVector3({
15345
15417
  x: 0.38,
15346
15418
  y: -0.6,
15347
15419
  z: 0.98,
15348
15420
  });
15421
+ /**
15422
+ * Cache keyed by the `createRandom` factory reference (stable per mounted `<Avatar/>`).
15423
+ *
15424
+ * @private helper of `octopus3d2AvatarVisual`
15425
+ */
15426
+ const octopus3d2StableStateCache = new WeakMap();
15427
+ /**
15428
+ * Returns the stable per-avatar state, computing it on first access and caching for subsequent frames.
15429
+ *
15430
+ * @private helper of `octopus3d2AvatarVisual`
15431
+ */
15432
+ function getOctopus3d2StableState(createRandom) {
15433
+ const cached = octopus3d2StableStateCache.get(createRandom);
15434
+ if (cached !== undefined) {
15435
+ return cached;
15436
+ }
15437
+ const animationRandom = createRandom('octopus3d2-animation-profile');
15438
+ const eyeRandom = createRandom('octopus3d2-eye-profile');
15439
+ const leftEyePhaseOffset = eyeRandom() * 0.7;
15440
+ const rightEyePhaseOffset = eyeRandom() * 0.7;
15441
+ const state = {
15442
+ morphologyProfile: createOctopus3MorphologyProfile(createRandom),
15443
+ animationPhase: animationRandom() * Math.PI * 2,
15444
+ leftEyePhaseOffset,
15445
+ rightEyePhaseOffset,
15446
+ };
15447
+ octopus3d2StableStateCache.set(createRandom, state);
15448
+ return state;
15449
+ }
15349
15450
  /**
15350
15451
  * Octopus 3D 2 avatar visual.
15351
15452
  *
@@ -15358,10 +15459,7 @@ const octopus3d2AvatarVisual = {
15358
15459
  isAnimated: true,
15359
15460
  supportsPointerTracking: true,
15360
15461
  render({ context, size, palette, createRandom, timeMs, interaction }) {
15361
- const morphologyProfile = createOctopus3MorphologyProfile(createRandom);
15362
- const animationRandom = createRandom('octopus3d2-animation-profile');
15363
- const eyeRandom = createRandom('octopus3d2-eye-profile');
15364
- const animationPhase = animationRandom() * Math.PI * 2;
15462
+ const { morphologyProfile, animationPhase, leftEyePhaseOffset, rightEyePhaseOffset } = getOctopus3d2StableState(createRandom);
15365
15463
  const sceneCenterX = size * 0.5;
15366
15464
  const sceneCenterY = size * 0.575;
15367
15465
  const bob = Math.sin(timeMs / 940 + animationPhase) * size * 0.013;
@@ -15414,8 +15512,8 @@ const octopus3d2AvatarVisual = {
15414
15512
  const rightEyeLocalCenter = sampleBlobbyOctopusSurfacePoint(surfaceOptions, eyeLatitude, eyeLongitude);
15415
15513
  const eyeRadiusX = size * morphologyProfile.face.eyeRadiusXRatio * 0.78;
15416
15514
  const eyeRadiusY = eyeRadiusX * morphologyProfile.face.eyeHeightRatio * 0.92;
15417
- drawProjectedOrganicEye(context, leftEyeLocalCenter, eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + eyeRandom() * 0.7, interaction, morphologyProfile.face.eyeStyle);
15418
- drawProjectedOrganicEye(context, rightEyeLocalCenter, eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.9 + eyeRandom() * 0.7, interaction, morphologyProfile.face.eyeStyle);
15515
+ drawProjectedOrganicEye(context, leftEyeLocalCenter, eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + leftEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
15516
+ drawProjectedOrganicEye(context, rightEyeLocalCenter, eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.9 + rightEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
15419
15517
  drawProjectedOrganicMouth(context, [
15420
15518
  sampleBlobbyOctopusSurfacePoint(surfaceOptions, mouthLatitude, mouthCenterLongitude - mouthHalfLongitude),
15421
15519
  sampleBlobbyOctopusSurfacePoint(surfaceOptions, mouthCurveLatitude, mouthCenterLongitude),
@@ -15444,54 +15542,118 @@ function drawBlobbyOctopusAtmosphere(context, size, palette, sceneCenterX, scene
15444
15542
  /**
15445
15543
  * Draws the soft floor shadow that anchors the single mesh in the frame.
15446
15544
  *
15545
+ * Uses a scaled radial gradient instead of `context.filter = 'blur()'` to approximate the
15546
+ * blurry ellipse without triggering a costly software rasterization pass on every frame.
15547
+ *
15447
15548
  * @private helper of `octopus3d2AvatarVisual`
15448
15549
  */
15449
15550
  function drawBlobbyOctopusShadow(context, size, palette, interaction, timeMs, morphologyProfile) {
15551
+ const cx = size * 0.5 + interaction.gazeX * size * 0.045;
15552
+ const cy = size * 0.88 + Math.sin(timeMs / 940) * size * 0.008;
15553
+ const rx = size * (0.18 + (morphologyProfile.body.horizontalStretch - 1) * 0.04 + interaction.intensity * 0.018);
15554
+ const ry = size * 0.062;
15450
15555
  context.save();
15451
- context.fillStyle = `${palette.shadow}66`;
15452
- context.filter = `blur(${size * 0.024}px)`;
15556
+ context.translate(cx, cy);
15557
+ context.scale(1, ry / rx);
15558
+ const blurRadius = rx * 1.4;
15559
+ const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
15560
+ shadowGradient.addColorStop(0, `${palette.shadow}7a`);
15561
+ shadowGradient.addColorStop(0.45, `${palette.shadow}44`);
15562
+ shadowGradient.addColorStop(0.8, `${palette.shadow}1a`);
15563
+ shadowGradient.addColorStop(1, `${palette.shadow}00`);
15564
+ context.fillStyle = shadowGradient;
15453
15565
  context.beginPath();
15454
- context.ellipse(size * 0.5 + interaction.gazeX * size * 0.045, size * 0.88 + Math.sin(timeMs / 940) * size * 0.008, size * (0.18 + (morphologyProfile.body.horizontalStretch - 1) * 0.04 + interaction.intensity * 0.018), size * 0.062, 0, 0, Math.PI * 2);
15566
+ context.arc(0, 0, blurRadius, 0, Math.PI * 2);
15455
15567
  context.fill();
15456
15568
  context.restore();
15457
15569
  }
15570
+ /**
15571
+ * Number of latitude segments used by the single blobby octopus mesh.
15572
+ *
15573
+ * @private helper of `octopus3d2AvatarVisual`
15574
+ */
15575
+ const LATITUDE_PATCH_COUNT$2 = 12;
15576
+ /**
15577
+ * Number of longitude segments used by the single blobby octopus mesh.
15578
+ *
15579
+ * @private helper of `octopus3d2AvatarVisual`
15580
+ */
15581
+ const LONGITUDE_PATCH_COUNT$2 = 24;
15458
15582
  /**
15459
15583
  * Resolves all visible projected patches for the single blobby octopus mesh.
15460
15584
  *
15585
+ * Within a single frame, mesh corner samples and longitude-only lobe-wave values are
15586
+ * quantized to the patch grid and computed once each rather than re-evaluated for every
15587
+ * patch corner — the patch loop alone would call `sampleBlobbyOctopusSurfacePoint`
15588
+ * `latitudePatchCount * longitudePatchCount * 4` times without caching, even though most
15589
+ * corners are shared between neighboring patches.
15590
+ *
15461
15591
  * @private helper of `octopus3d2AvatarVisual`
15462
15592
  */
15463
15593
  function resolveVisibleBlobbyOctopusPatches(options) {
15464
15594
  const { center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, morphologyProfile, animationPhase, timeMs, } = options;
15465
- const latitudePatchCount = 12;
15466
- const longitudePatchCount = 24;
15595
+ const latitudePatchCount = LATITUDE_PATCH_COUNT$2;
15596
+ const longitudePatchCount = LONGITUDE_PATCH_COUNT$2;
15467
15597
  const surfacePatches = [];
15598
+ const latitudeBoundaries = new Float64Array(latitudePatchCount + 1);
15599
+ const longitudeBoundaries = new Float64Array(longitudePatchCount + 1);
15600
+ for (let boundaryIndex = 0; boundaryIndex <= latitudePatchCount; boundaryIndex++) {
15601
+ latitudeBoundaries[boundaryIndex] = -Math.PI / 2 + (boundaryIndex / latitudePatchCount) * Math.PI;
15602
+ }
15603
+ for (let boundaryIndex = 0; boundaryIndex <= longitudePatchCount; boundaryIndex++) {
15604
+ longitudeBoundaries[boundaryIndex] = -Math.PI + (boundaryIndex / longitudePatchCount) * Math.PI * 2;
15605
+ }
15606
+ const cachedLobeWavesByCornerLongitude = new Float64Array(longitudePatchCount + 1);
15607
+ for (let boundaryIndex = 0; boundaryIndex <= longitudePatchCount; boundaryIndex++) {
15608
+ cachedLobeWavesByCornerLongitude[boundaryIndex] = resolveLowerLobeWave(longitudeBoundaries[boundaryIndex], morphologyProfile, animationPhase, timeMs);
15609
+ }
15610
+ const cornerCount = (latitudePatchCount + 1) * (longitudePatchCount + 1);
15611
+ const transformedCornerSamples = new Array(cornerCount);
15612
+ for (let latitudeBoundaryIndex = 0; latitudeBoundaryIndex <= latitudePatchCount; latitudeBoundaryIndex++) {
15613
+ const cornerLatitude = latitudeBoundaries[latitudeBoundaryIndex];
15614
+ for (let longitudeBoundaryIndex = 0; longitudeBoundaryIndex <= longitudePatchCount; longitudeBoundaryIndex++) {
15615
+ const cornerLongitude = longitudeBoundaries[longitudeBoundaryIndex];
15616
+ const cornerIndex = latitudeBoundaryIndex * (longitudePatchCount + 1) + longitudeBoundaryIndex;
15617
+ const cornerSample = sampleBlobbyOctopusSurfacePointWithLongitudeCache(options, cornerLatitude, cornerLongitude, cachedLobeWavesByCornerLongitude[longitudeBoundaryIndex]);
15618
+ transformedCornerSamples[cornerIndex] = transformScenePoint(cornerSample, center, rotationX, rotationY);
15619
+ }
15620
+ }
15468
15621
  for (let latitudeIndex = 0; latitudeIndex < latitudePatchCount; latitudeIndex++) {
15469
- const startLatitude = -Math.PI / 2 + (latitudeIndex / latitudePatchCount) * Math.PI;
15470
- const endLatitude = -Math.PI / 2 + ((latitudeIndex + 1) / latitudePatchCount) * Math.PI;
15622
+ const startLatitude = latitudeBoundaries[latitudeIndex];
15623
+ const endLatitude = latitudeBoundaries[latitudeIndex + 1];
15471
15624
  const centerLatitude = (startLatitude + endLatitude) / 2;
15472
15625
  const verticalProgress = (Math.sin(centerLatitude) + 1) / 2;
15626
+ const startCornerRowOffset = latitudeIndex * (longitudePatchCount + 1);
15627
+ const endCornerRowOffset = (latitudeIndex + 1) * (longitudePatchCount + 1);
15473
15628
  for (let longitudeIndex = 0; longitudeIndex < longitudePatchCount; longitudeIndex++) {
15474
- const startLongitude = -Math.PI + (longitudeIndex / longitudePatchCount) * Math.PI * 2;
15475
- const endLongitude = -Math.PI + ((longitudeIndex + 1) / longitudePatchCount) * Math.PI * 2;
15629
+ const startLongitude = longitudeBoundaries[longitudeIndex];
15630
+ const endLongitude = longitudeBoundaries[longitudeIndex + 1];
15476
15631
  const centerLongitude = (startLongitude + endLongitude) / 2;
15477
- const localCorners = [
15478
- sampleBlobbyOctopusSurfacePoint(options, startLatitude, startLongitude),
15479
- sampleBlobbyOctopusSurfacePoint(options, startLatitude, endLongitude),
15480
- sampleBlobbyOctopusSurfacePoint(options, endLatitude, endLongitude),
15481
- sampleBlobbyOctopusSurfacePoint(options, endLatitude, startLongitude),
15632
+ const transformedCorners = [
15633
+ transformedCornerSamples[startCornerRowOffset + longitudeIndex],
15634
+ transformedCornerSamples[startCornerRowOffset + longitudeIndex + 1],
15635
+ transformedCornerSamples[endCornerRowOffset + longitudeIndex + 1],
15636
+ transformedCornerSamples[endCornerRowOffset + longitudeIndex],
15482
15637
  ];
15483
- const transformedCorners = localCorners.map((localCorner) => transformScenePoint(localCorner, center, rotationX, rotationY));
15484
15638
  const surfaceNormal = normalizeVector3(crossProduct3D(subtractPoint3D(transformedCorners[1], transformedCorners[0]), subtractPoint3D(transformedCorners[2], transformedCorners[0])));
15485
15639
  if (surfaceNormal.z <= 0.01) {
15486
15640
  continue;
15487
15641
  }
15488
- const projectedCorners = transformedCorners.map((transformedCorner) => projectScenePoint(transformedCorner, size, sceneCenterX, sceneCenterY));
15642
+ const projectedCorners = [
15643
+ projectScenePoint(transformedCorners[0], size, sceneCenterX, sceneCenterY),
15644
+ projectScenePoint(transformedCorners[1], size, sceneCenterX, sceneCenterY),
15645
+ projectScenePoint(transformedCorners[2], size, sceneCenterX, sceneCenterY),
15646
+ projectScenePoint(transformedCorners[3], size, sceneCenterX, sceneCenterY),
15647
+ ];
15489
15648
  surfacePatches.push({
15490
15649
  corners: projectedCorners,
15491
- averageDepth: transformedCorners.reduce((depthSum, transformedCorner) => depthSum + transformedCorner.z, 0) /
15492
- transformedCorners.length,
15493
- lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION), -1, 1),
15494
- fillStyle: resolveBlobbySurfacePatchFillStyle(palette, verticalProgress, Math.max(0, Math.cos(centerLongitude)), resolveLowerLobeWave(centerLongitude, morphologyProfile, animationPhase, timeMs)),
15650
+ averageDepth: (transformedCorners[0].z +
15651
+ transformedCorners[1].z +
15652
+ transformedCorners[2].z +
15653
+ transformedCorners[3].z) /
15654
+ 4,
15655
+ lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$2), -1, 1),
15656
+ fillStyle: resolveBlobbySurfacePatchFillStyle$1(palette, verticalProgress, Math.max(0, Math.cos(centerLongitude)), resolveLowerLobeWave(centerLongitude, morphologyProfile, animationPhase, timeMs)),
15495
15657
  outlineColor: verticalProgress < 0.58 ? `${palette.highlight}73` : `${palette.shadow}8a`,
15496
15658
  });
15497
15659
  }
@@ -15507,12 +15669,21 @@ function resolveVisibleBlobbyOctopusPatches(options) {
15507
15669
  * @private helper of `octopus3d2AvatarVisual`
15508
15670
  */
15509
15671
  function sampleBlobbyOctopusSurfacePoint(options, latitude, longitude) {
15672
+ const { morphologyProfile, animationPhase, timeMs } = options;
15673
+ return sampleBlobbyOctopusSurfacePointWithLongitudeCache(options, latitude, longitude, resolveLowerLobeWave(longitude, morphologyProfile, animationPhase, timeMs));
15674
+ }
15675
+ /**
15676
+ * Samples one point on the continuous Octopus 3D 2 surface using a precomputed lower-lobe wave
15677
+ * to skip the per-call trig evaluation for `latitudePatchCount + 1` longitude-shared corners.
15678
+ *
15679
+ * @private helper of `octopus3d2AvatarVisual`
15680
+ */
15681
+ function sampleBlobbyOctopusSurfacePointWithLongitudeCache(options, latitude, longitude, lowerLobeWave) {
15510
15682
  const { radiusX, radiusY, radiusZ, morphologyProfile, timeMs, animationPhase } = options;
15511
15683
  const cosineLatitude = Math.max(0, Math.cos(latitude));
15512
15684
  const verticalProgress = (Math.sin(latitude) + 1) / 2;
15513
15685
  const upperBlend = Math.pow(1 - verticalProgress, 1.2);
15514
15686
  const lowerBlend = Math.pow(verticalProgress, 1.42);
15515
- const lowerLobeWave = resolveLowerLobeWave(longitude, morphologyProfile, animationPhase, timeMs);
15516
15687
  const skirtEnvelope = Math.pow(cosineLatitude, 0.5) * lowerBlend;
15517
15688
  const horizontalScale = 1.02 +
15518
15689
  skirtEnvelope * (0.34 + (morphologyProfile.tentacles.rootSpreadScale - 1) * 0.22 + lowerLobeWave * 0.22) -
@@ -15550,7 +15721,7 @@ function resolveLowerLobeWave(longitude, morphologyProfile, animationPhase, time
15550
15721
  *
15551
15722
  * @private helper of `octopus3d2AvatarVisual`
15552
15723
  */
15553
- function resolveBlobbySurfacePatchFillStyle(palette, verticalProgress, forwardness, lowerLobeWave) {
15724
+ function resolveBlobbySurfacePatchFillStyle$1(palette, verticalProgress, forwardness, lowerLobeWave) {
15554
15725
  const tonalProgress = clampNumber$1(verticalProgress + lowerLobeWave * 0.12 - forwardness * 0.07, 0, 1);
15555
15726
  if (tonalProgress < 0.16) {
15556
15727
  return palette.highlight;
@@ -15590,6 +15761,1384 @@ function drawBlobbySurfacePatch(context, surfacePatch) {
15590
15761
  context.restore();
15591
15762
  }
15592
15763
 
15764
+ /* eslint-disable no-magic-numbers */
15765
+ /**
15766
+ * Light direction used by the continuous octopus mesh shading.
15767
+ *
15768
+ * @private helper of `octopus3d3AvatarVisual`
15769
+ */
15770
+ const LIGHT_DIRECTION$1 = normalizeVector3({
15771
+ x: 0.34,
15772
+ y: -0.62,
15773
+ z: 1,
15774
+ });
15775
+ /**
15776
+ * Real-octopus tentacle count used by the continuous lower mesh.
15777
+ *
15778
+ * @private helper of `octopus3d3AvatarVisual`
15779
+ */
15780
+ const OCTOPUS_TENTACLE_COUNT$1 = 8;
15781
+ /**
15782
+ * Cache keyed by the `createRandom` factory reference, which is stable for the lifetime of one
15783
+ * mounted `<Avatar/>` component (created inside `resolveAvatarRenderDefinition` and held in a
15784
+ * React `useMemo`). Using a `WeakMap` ensures the entry is collected when the component unmounts.
15785
+ *
15786
+ * @private helper of `octopus3d3AvatarVisual`
15787
+ */
15788
+ const stableStateCache = new WeakMap();
15789
+ /**
15790
+ * Returns the stable per-avatar state, computing it on first access and returning the cached
15791
+ * result on every subsequent call within the same `<Avatar/>` mount.
15792
+ *
15793
+ * @private helper of `octopus3d3AvatarVisual`
15794
+ */
15795
+ function getOctopus3d3StableState(createRandom) {
15796
+ const cached = stableStateCache.get(createRandom);
15797
+ if (cached !== undefined) {
15798
+ return cached;
15799
+ }
15800
+ const morphologyProfile = createOctopus3MorphologyProfile(createRandom);
15801
+ const animationRandom = createRandom('octopus3d3-animation-profile');
15802
+ const eyeRandom = createRandom('octopus3d3-eye-profile');
15803
+ const leftEyePhaseOffset = eyeRandom() * 0.7;
15804
+ const rightEyePhaseOffset = eyeRandom() * 0.7;
15805
+ const state = {
15806
+ morphologyProfile,
15807
+ animationPhase: animationRandom() * Math.PI * 2,
15808
+ leftEyePhaseOffset,
15809
+ rightEyePhaseOffset,
15810
+ tentacleProfiles: createContinuousTentacleProfiles(createRandom, morphologyProfile),
15811
+ };
15812
+ stableStateCache.set(createRandom, state);
15813
+ return state;
15814
+ }
15815
+ /**
15816
+ * Octopus 3D 3 avatar visual.
15817
+ *
15818
+ * @private built-in avatar visual
15819
+ */
15820
+ const octopus3d3AvatarVisual = {
15821
+ id: 'octopus3d3',
15822
+ title: 'Octopus 3D 3',
15823
+ description: 'Cute continuous 3D octopus with a blobby single mesh, waving tentacle lobes, rich shading, and cursor-aware eyes.',
15824
+ isAnimated: true,
15825
+ supportsPointerTracking: true,
15826
+ render({ context, size, palette, createRandom, timeMs, interaction }) {
15827
+ const { morphologyProfile, animationPhase, leftEyePhaseOffset, rightEyePhaseOffset, tentacleProfiles } = getOctopus3d3StableState(createRandom);
15828
+ const sceneCenterX = size * 0.5;
15829
+ const sceneCenterY = size * 0.535;
15830
+ const bob = Math.sin(timeMs / 960 + animationPhase) * size * 0.012;
15831
+ const meshCenter = {
15832
+ x: interaction.bodyOffsetX * size * 0.048 + size * morphologyProfile.body.centerXJitterRatio * 0.44,
15833
+ y: -size * 0.07 + interaction.bodyOffsetY * size * 0.026 + bob,
15834
+ z: interaction.intensity * size * 0.018,
15835
+ };
15836
+ const rotationY = -0.1 +
15837
+ Math.sin(timeMs / 2700 + animationPhase) * 0.035 +
15838
+ interaction.bodyOffsetX * 0.22 +
15839
+ interaction.gazeX * 0.88;
15840
+ const rotationX = -0.07 +
15841
+ Math.cos(timeMs / 3100 + animationPhase * 0.7) * 0.018 -
15842
+ interaction.bodyOffsetY * 0.08 -
15843
+ interaction.gazeY * 0.38;
15844
+ const surfaceOptions = {
15845
+ radiusX: size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.horizontalStretch * 1.1,
15846
+ radiusY: size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.verticalStretch * 1.08,
15847
+ radiusZ: size *
15848
+ morphologyProfile.body.bodyRadiusRatio *
15849
+ (1.02 + (morphologyProfile.body.horizontalStretch - 1) * 0.18),
15850
+ morphologyProfile,
15851
+ timeMs,
15852
+ animationPhase,
15853
+ tentacleProfiles,
15854
+ };
15855
+ const surfacePatches = resolveVisibleContinuousOctopusPatches({
15856
+ ...surfaceOptions,
15857
+ center: meshCenter,
15858
+ rotationX,
15859
+ rotationY,
15860
+ sceneCenterX,
15861
+ sceneCenterY,
15862
+ size,
15863
+ palette,
15864
+ });
15865
+ const eyeLatitude = clampNumber$1(morphologyProfile.face.eyeCenterYOffsetRatio * 4.2 - 0.03, -0.22, 0.08);
15866
+ const eyeLongitude = clampNumber$1(morphologyProfile.face.eyeSpacingRatio * 3.1, 0.18, 0.32);
15867
+ const mouthLatitude = clampNumber$1(eyeLatitude + 0.2 + morphologyProfile.face.mouthYOffsetRatio, 0.08, 0.34);
15868
+ const mouthCenterLongitude = clampNumber$1(morphologyProfile.face.mouthCenterOffsetRatio * 5.6, -0.08, 0.08);
15869
+ const mouthHalfLongitude = clampNumber$1(eyeLongitude * 0.78, 0.15, 0.28);
15870
+ const mouthCurveLatitude = clampNumber$1(mouthLatitude + morphologyProfile.face.mouthCurveDepthRatio * 0.78, mouthLatitude + 0.03, 0.42);
15871
+ const eyeRadiusX = size * morphologyProfile.face.eyeRadiusXRatio * 0.76;
15872
+ const eyeRadiusY = eyeRadiusX * morphologyProfile.face.eyeHeightRatio * 0.9;
15873
+ drawAvatarFrame(context, size, palette);
15874
+ drawContinuousOctopusAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs);
15875
+ drawContinuousOctopusShadow(context, size, palette, interaction, timeMs, morphologyProfile);
15876
+ for (const surfacePatch of surfacePatches.sort((firstSurfacePatch, secondSurfacePatch) => firstSurfacePatch.averageDepth - secondSurfacePatch.averageDepth)) {
15877
+ drawContinuousSurfacePatch(context, surfacePatch);
15878
+ }
15879
+ drawProjectedSurfaceCurrents({
15880
+ context,
15881
+ surfaceOptions,
15882
+ center: meshCenter,
15883
+ rotationX,
15884
+ rotationY,
15885
+ sceneCenterX,
15886
+ sceneCenterY,
15887
+ size,
15888
+ palette,
15889
+ morphologyProfile,
15890
+ timeMs,
15891
+ animationPhase,
15892
+ });
15893
+ drawProjectedTentacleSuckers({
15894
+ context,
15895
+ surfaceOptions,
15896
+ center: meshCenter,
15897
+ rotationX,
15898
+ rotationY,
15899
+ sceneCenterX,
15900
+ sceneCenterY,
15901
+ size,
15902
+ palette,
15903
+ });
15904
+ drawProjectedOrganicEye(context, sampleContinuousOctopusSurfacePoint(surfaceOptions, eyeLatitude, -eyeLongitude), eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + leftEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
15905
+ drawProjectedOrganicEye(context, sampleContinuousOctopusSurfacePoint(surfaceOptions, eyeLatitude, eyeLongitude), eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.85 + rightEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
15906
+ drawProjectedOrganicMouth(context, [
15907
+ sampleContinuousOctopusSurfacePoint(surfaceOptions, mouthLatitude, mouthCenterLongitude - mouthHalfLongitude),
15908
+ sampleContinuousOctopusSurfacePoint(surfaceOptions, mouthCurveLatitude, mouthCenterLongitude),
15909
+ sampleContinuousOctopusSurfacePoint(surfaceOptions, mouthLatitude, mouthCenterLongitude + mouthHalfLongitude),
15910
+ ], meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, palette, size);
15911
+ },
15912
+ };
15913
+ /**
15914
+ * Creates seeded tentacle-lobe profiles around the visible lower octopus body.
15915
+ *
15916
+ * @private helper of `octopus3d3AvatarVisual`
15917
+ */
15918
+ function createContinuousTentacleProfiles(createRandom, morphologyProfile) {
15919
+ return Array.from({ length: OCTOPUS_TENTACLE_COUNT$1 }, (_, tentacleIndex) => {
15920
+ const tentacleRandom = createRandom(`octopus3d3-tentacle-${tentacleIndex}`);
15921
+ const progress = tentacleIndex / (OCTOPUS_TENTACLE_COUNT$1 - 1);
15922
+ return {
15923
+ centerLongitude: -Math.PI * 0.86 +
15924
+ progress * Math.PI * 1.72 +
15925
+ (tentacleRandom() - 0.5) * (0.08 + morphologyProfile.tentacles.rootSpreadScale * 0.03),
15926
+ widthScale: 0.86 + tentacleRandom() * 0.34 + (morphologyProfile.tentacles.baseWidthScale - 1) * 0.16,
15927
+ lengthScale: 0.86 + tentacleRandom() * 0.36 + (morphologyProfile.tentacles.flowLengthScale - 1) * 0.22,
15928
+ swayScale: 0.82 + tentacleRandom() * 0.38 + (morphologyProfile.tentacles.swayScale - 1) * 0.2,
15929
+ depthScale: 0.86 + tentacleRandom() * 0.32 + (morphologyProfile.tentacles.tipReachScale - 1) * 0.2,
15930
+ phase: tentacleRandom() * Math.PI * 2,
15931
+ suckerSide: tentacleRandom() > 0.5 ? 1 : -1,
15932
+ };
15933
+ });
15934
+ }
15935
+ /**
15936
+ * Draws the soft underwater atmosphere behind the continuous octopus mesh.
15937
+ *
15938
+ * @private helper of `octopus3d3AvatarVisual`
15939
+ */
15940
+ function drawContinuousOctopusAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs) {
15941
+ 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));
15942
+ glowGradient.addColorStop(0, `${palette.highlight}66`);
15943
+ glowGradient.addColorStop(0.34, `${palette.accent}2e`);
15944
+ glowGradient.addColorStop(1, `${palette.highlight}00`);
15945
+ context.fillStyle = glowGradient;
15946
+ context.fillRect(0, 0, size, size);
15947
+ 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);
15948
+ lowerGradient.addColorStop(0, `${palette.secondary}25`);
15949
+ lowerGradient.addColorStop(1, `${palette.secondary}00`);
15950
+ context.fillStyle = lowerGradient;
15951
+ context.fillRect(0, 0, size, size);
15952
+ }
15953
+ /**
15954
+ * Draws the soft lower shadow that anchors the octopus in the avatar frame.
15955
+ *
15956
+ * Uses a scaled radial gradient instead of `context.filter = 'blur()'` to approximate the
15957
+ * blurry ellipse without triggering a costly software rasterization pass on every frame.
15958
+ *
15959
+ * @private helper of `octopus3d3AvatarVisual`
15960
+ */
15961
+ function drawContinuousOctopusShadow(context, size, palette, interaction, timeMs, morphologyProfile) {
15962
+ const cx = size * 0.5 + interaction.gazeX * size * 0.045;
15963
+ const cy = size * 0.9 + Math.sin(timeMs / 980) * size * 0.007;
15964
+ const rx = size * (0.19 + morphologyProfile.tentacles.rootSpreadScale * 0.022 + interaction.intensity * 0.02);
15965
+ const ry = size * 0.06;
15966
+ // Scale the context so that drawing a circle produces the correct ellipse aspect ratio,
15967
+ // then fill with a radial gradient that approximates the blurry edge without context.filter.
15968
+ context.save();
15969
+ context.translate(cx, cy);
15970
+ context.scale(1, ry / rx);
15971
+ const blurRadius = rx * 1.4;
15972
+ const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
15973
+ shadowGradient.addColorStop(0, `${palette.shadow}7a`);
15974
+ shadowGradient.addColorStop(0.45, `${palette.shadow}44`);
15975
+ shadowGradient.addColorStop(0.8, `${palette.shadow}1a`);
15976
+ shadowGradient.addColorStop(1, `${palette.shadow}00`);
15977
+ context.fillStyle = shadowGradient;
15978
+ context.beginPath();
15979
+ context.arc(0, 0, blurRadius, 0, Math.PI * 2);
15980
+ context.fill();
15981
+ context.restore();
15982
+ }
15983
+ /**
15984
+ * Number of latitude segments used by the continuous Octopus 3D 3 mesh.
15985
+ *
15986
+ * @private helper of `octopus3d3AvatarVisual`
15987
+ */
15988
+ const LATITUDE_PATCH_COUNT$1 = 16;
15989
+ /**
15990
+ * Number of longitude segments used by the continuous Octopus 3D 3 mesh.
15991
+ *
15992
+ * @private helper of `octopus3d3AvatarVisual`
15993
+ */
15994
+ const LONGITUDE_PATCH_COUNT$1 = 40;
15995
+ /**
15996
+ * Resolves visible projected patches for the continuous octopus mesh.
15997
+ *
15998
+ * Within a single frame, mesh corner samples and longitude-only computations (tentacle
15999
+ * influence and lobe wave) are quantized to the patch grid and computed once each rather
16000
+ * than re-evaluated for every patch corner — the patch loop alone calls `sampleContinuousOctopusSurfacePoint`
16001
+ * for `latitudePatchCount * longitudePatchCount * 4` corners without caching, and each call
16002
+ * triggers an inner 8-iteration `Math.exp` loop, which dominates the per-frame cost.
16003
+ *
16004
+ * @private helper of `octopus3d3AvatarVisual`
16005
+ */
16006
+ function resolveVisibleContinuousOctopusPatches(options) {
16007
+ const { center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette } = options;
16008
+ const latitudePatchCount = LATITUDE_PATCH_COUNT$1;
16009
+ const longitudePatchCount = LONGITUDE_PATCH_COUNT$1;
16010
+ const surfacePatches = [];
16011
+ const latitudeBoundaries = new Float64Array(latitudePatchCount + 1);
16012
+ const longitudeBoundaries = new Float64Array(longitudePatchCount + 1);
16013
+ for (let boundaryIndex = 0; boundaryIndex <= latitudePatchCount; boundaryIndex++) {
16014
+ latitudeBoundaries[boundaryIndex] = -Math.PI / 2 + (boundaryIndex / latitudePatchCount) * Math.PI;
16015
+ }
16016
+ for (let boundaryIndex = 0; boundaryIndex <= longitudePatchCount; boundaryIndex++) {
16017
+ longitudeBoundaries[boundaryIndex] = -Math.PI + (boundaryIndex / longitudePatchCount) * Math.PI * 2;
16018
+ }
16019
+ const cachedTentacleInfluencesByCornerLongitude = new Array(longitudePatchCount + 1);
16020
+ const cachedLobeWavesByCornerLongitude = new Float64Array(longitudePatchCount + 1);
16021
+ const cachedTentacleInfluencesByPatchCenterLongitude = new Array(longitudePatchCount);
16022
+ const cachedLobeWavesByPatchCenterLongitude = new Float64Array(longitudePatchCount);
16023
+ const cachedCosByPatchCenterLongitude = new Float64Array(longitudePatchCount);
16024
+ for (let boundaryIndex = 0; boundaryIndex <= longitudePatchCount; boundaryIndex++) {
16025
+ const cornerLongitude = longitudeBoundaries[boundaryIndex];
16026
+ cachedTentacleInfluencesByCornerLongitude[boundaryIndex] = resolveContinuousTentacleInfluence(options, cornerLongitude);
16027
+ cachedLobeWavesByCornerLongitude[boundaryIndex] = resolveContinuousLobeWave(options, cornerLongitude);
16028
+ }
16029
+ for (let longitudeIndex = 0; longitudeIndex < longitudePatchCount; longitudeIndex++) {
16030
+ const patchCenterLongitude = (longitudeBoundaries[longitudeIndex] + longitudeBoundaries[longitudeIndex + 1]) / 2;
16031
+ cachedTentacleInfluencesByPatchCenterLongitude[longitudeIndex] = resolveContinuousTentacleInfluence(options, patchCenterLongitude);
16032
+ cachedLobeWavesByPatchCenterLongitude[longitudeIndex] = resolveContinuousLobeWave(options, patchCenterLongitude);
16033
+ cachedCosByPatchCenterLongitude[longitudeIndex] = Math.max(0, Math.cos(patchCenterLongitude));
16034
+ }
16035
+ const cornerCount = (latitudePatchCount + 1) * (longitudePatchCount + 1);
16036
+ const transformedCornerSamples = new Array(cornerCount);
16037
+ for (let latitudeBoundaryIndex = 0; latitudeBoundaryIndex <= latitudePatchCount; latitudeBoundaryIndex++) {
16038
+ const cornerLatitude = latitudeBoundaries[latitudeBoundaryIndex];
16039
+ for (let longitudeBoundaryIndex = 0; longitudeBoundaryIndex <= longitudePatchCount; longitudeBoundaryIndex++) {
16040
+ const cornerLongitude = longitudeBoundaries[longitudeBoundaryIndex];
16041
+ const cornerIndex = latitudeBoundaryIndex * (longitudePatchCount + 1) + longitudeBoundaryIndex;
16042
+ const cornerSample = sampleContinuousOctopusSurfacePointWithLongitudeCache(options, cornerLatitude, cornerLongitude, cachedTentacleInfluencesByCornerLongitude[longitudeBoundaryIndex], cachedLobeWavesByCornerLongitude[longitudeBoundaryIndex]);
16043
+ transformedCornerSamples[cornerIndex] = transformScenePoint(cornerSample, center, rotationX, rotationY);
16044
+ }
16045
+ }
16046
+ for (let latitudeIndex = 0; latitudeIndex < latitudePatchCount; latitudeIndex++) {
16047
+ const startLatitude = latitudeBoundaries[latitudeIndex];
16048
+ const endLatitude = latitudeBoundaries[latitudeIndex + 1];
16049
+ const centerLatitude = (startLatitude + endLatitude) / 2;
16050
+ const verticalProgress = (Math.sin(centerLatitude) + 1) / 2;
16051
+ const startCornerRowOffset = latitudeIndex * (longitudePatchCount + 1);
16052
+ const endCornerRowOffset = (latitudeIndex + 1) * (longitudePatchCount + 1);
16053
+ for (let longitudeIndex = 0; longitudeIndex < longitudePatchCount; longitudeIndex++) {
16054
+ const transformedCorners = [
16055
+ transformedCornerSamples[startCornerRowOffset + longitudeIndex],
16056
+ transformedCornerSamples[startCornerRowOffset + longitudeIndex + 1],
16057
+ transformedCornerSamples[endCornerRowOffset + longitudeIndex + 1],
16058
+ transformedCornerSamples[endCornerRowOffset + longitudeIndex],
16059
+ ];
16060
+ const surfaceNormal = normalizeVector3(crossProduct3D(subtractPoint3D(transformedCorners[1], transformedCorners[0]), subtractPoint3D(transformedCorners[2], transformedCorners[0])));
16061
+ if (surfaceNormal.z <= 0.008) {
16062
+ continue;
16063
+ }
16064
+ const projectedCorners = [
16065
+ projectScenePoint(transformedCorners[0], size, sceneCenterX, sceneCenterY),
16066
+ projectScenePoint(transformedCorners[1], size, sceneCenterX, sceneCenterY),
16067
+ projectScenePoint(transformedCorners[2], size, sceneCenterX, sceneCenterY),
16068
+ projectScenePoint(transformedCorners[3], size, sceneCenterX, sceneCenterY),
16069
+ ];
16070
+ surfacePatches.push({
16071
+ corners: projectedCorners,
16072
+ averageDepth: (transformedCorners[0].z +
16073
+ transformedCorners[1].z +
16074
+ transformedCorners[2].z +
16075
+ transformedCorners[3].z) /
16076
+ 4,
16077
+ lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$1), -1, 1),
16078
+ fillStyle: resolveContinuousSurfacePatchFillStyle(palette, verticalProgress, cachedCosByPatchCenterLongitude[longitudeIndex], cachedTentacleInfluencesByPatchCenterLongitude[longitudeIndex].core, cachedLobeWavesByPatchCenterLongitude[longitudeIndex]),
16079
+ outlineColor: verticalProgress < 0.54 ? `${palette.highlight}69` : `${palette.shadow}78`,
16080
+ });
16081
+ }
16082
+ }
16083
+ return surfacePatches;
16084
+ }
16085
+ /**
16086
+ * Samples one point on the continuous Octopus 3D 3 surface.
16087
+ *
16088
+ * The lower hemisphere is pulled into eight seeded waving lobes, so the portrait reads as
16089
+ * tentacled while still being rendered as one connected blobby mesh.
16090
+ *
16091
+ * @private helper of `octopus3d3AvatarVisual`
16092
+ */
16093
+ function sampleContinuousOctopusSurfacePoint(options, latitude, longitude) {
16094
+ return sampleContinuousOctopusSurfacePointWithLongitudeCache(options, latitude, longitude, resolveContinuousTentacleInfluence(options, longitude), resolveContinuousLobeWave(options, longitude));
16095
+ }
16096
+ /**
16097
+ * Samples one point on the continuous Octopus 3D 3 surface using precomputed longitude-only
16098
+ * values to skip the per-call `Math.exp` tentacle-influence loop and the lobe-wave trig call.
16099
+ *
16100
+ * The patch loop quantizes the mesh into a fixed `(latitudePatchCount + 1) * (longitudePatchCount + 1)`
16101
+ * corner grid, so the same longitude is reused across every latitude row and each
16102
+ * tentacle/lobe value can be computed once per frame instead of `latitudePatchCount * 4` times.
16103
+ *
16104
+ * @private helper of `octopus3d3AvatarVisual`
16105
+ */
16106
+ function sampleContinuousOctopusSurfacePointWithLongitudeCache(options, latitude, longitude, tentacleInfluence, lowerLobeWave) {
16107
+ const { radiusX, radiusY, radiusZ, morphologyProfile, timeMs, animationPhase } = options;
16108
+ const cosineLatitude = Math.max(0, Math.cos(latitude));
16109
+ const verticalProgress = (Math.sin(latitude) + 1) / 2;
16110
+ const upperBlend = Math.pow(1 - verticalProgress, 1.28);
16111
+ const lowerBlend = smoothStep$1(0.38, 1, verticalProgress);
16112
+ const tipBlend = smoothStep$1(0.68, 1, verticalProgress);
16113
+ const centerPull = resolveSignedAngularDistance$1(longitude, tentacleInfluence.centerLongitude);
16114
+ const effectiveLongitude = longitude + centerPull * lowerBlend * tentacleInfluence.core * (0.24 + tipBlend * 0.2);
16115
+ const mantleRipple = Math.sin(longitude * morphologyProfile.body.lobeCount +
16116
+ animationPhase * 0.6 +
16117
+ timeMs / (1750 + morphologyProfile.body.lobeCount * 30)) *
16118
+ (0.018 + morphologyProfile.body.wobbleAmplitudeRatio * 0.8) *
16119
+ (0.3 + lowerBlend * 0.7);
16120
+ const tentacleWave = Math.sin(timeMs / 760 + tentacleInfluence.phase + verticalProgress * 2.4) *
16121
+ lowerBlend *
16122
+ tentacleInfluence.core *
16123
+ tentacleInfluence.swayScale;
16124
+ const horizontalScale = 1.04 +
16125
+ mantleRipple +
16126
+ lowerBlend * (0.16 + (morphologyProfile.tentacles.rootSpreadScale - 1) * 0.1) +
16127
+ lowerBlend * tentacleInfluence.core * (0.2 + lowerLobeWave * 0.12) -
16128
+ upperBlend * 0.08;
16129
+ const depthScale = 1.06 +
16130
+ upperBlend * 0.16 +
16131
+ Math.max(0, Math.cos(effectiveLongitude)) * 0.1 +
16132
+ lowerBlend * tentacleInfluence.core * (0.1 + tentacleInfluence.depthScale * 0.06) -
16133
+ Math.max(0, -Math.cos(effectiveLongitude)) * 0.05;
16134
+ const tentacleTubeRadius = lowerBlend * tentacleInfluence.core * (0.11 + tipBlend * 0.06 + tentacleInfluence.widthScale * 0.025) * radiusX;
16135
+ const planarRadiusX = cosineLatitude * radiusX * horizontalScale + tentacleTubeRadius;
16136
+ const planarRadiusZ = cosineLatitude * radiusZ * depthScale + tentacleTubeRadius * 0.72;
16137
+ const lowerDrop = lowerBlend *
16138
+ radiusY *
16139
+ (0.18 +
16140
+ tentacleInfluence.core *
16141
+ (0.38 +
16142
+ tentacleInfluence.lengthScale * 0.22 +
16143
+ (morphologyProfile.tentacles.flowLengthScale - 1) * 0.08));
16144
+ return {
16145
+ x: Math.sin(effectiveLongitude) * planarRadiusX + tentacleWave * radiusX * (0.052 + tipBlend * 0.05),
16146
+ y: Math.sin(latitude) * radiusY * (1 + upperBlend * 0.12) -
16147
+ upperBlend * radiusY * 0.1 +
16148
+ lowerDrop +
16149
+ Math.sin(timeMs / 1420 + animationPhase + latitude * 1.6) * lowerBlend * radiusY * 0.018 +
16150
+ Math.cos(timeMs / 880 + tentacleInfluence.phase) *
16151
+ lowerBlend *
16152
+ tipBlend *
16153
+ tentacleInfluence.core *
16154
+ radiusY *
16155
+ 0.034,
16156
+ z: Math.cos(effectiveLongitude) * planarRadiusZ +
16157
+ Math.cos(timeMs / 980 + tentacleInfluence.phase + verticalProgress) *
16158
+ lowerBlend *
16159
+ tentacleInfluence.core *
16160
+ radiusZ *
16161
+ 0.04,
16162
+ };
16163
+ }
16164
+ /**
16165
+ * Blends nearby seeded tentacle profiles at one mesh longitude.
16166
+ *
16167
+ * @private helper of `octopus3d3AvatarVisual`
16168
+ */
16169
+ function resolveContinuousTentacleInfluence(options, longitude) {
16170
+ let totalWeight = 0;
16171
+ let weightedSin = 0;
16172
+ let weightedCos = 0;
16173
+ let weightedWidthScale = 0;
16174
+ let weightedLengthScale = 0;
16175
+ let weightedSwayScale = 0;
16176
+ let weightedDepthScale = 0;
16177
+ let weightedPhase = 0;
16178
+ for (const tentacleProfile of options.tentacleProfiles) {
16179
+ const distance = Math.abs(resolveSignedAngularDistance$1(longitude, tentacleProfile.centerLongitude));
16180
+ const width = 0.2 * tentacleProfile.widthScale;
16181
+ const weight = Math.exp(-(distance * distance) / (width * width));
16182
+ totalWeight += weight;
16183
+ weightedSin += Math.sin(tentacleProfile.centerLongitude) * weight;
16184
+ weightedCos += Math.cos(tentacleProfile.centerLongitude) * weight;
16185
+ weightedWidthScale += tentacleProfile.widthScale * weight;
16186
+ weightedLengthScale += tentacleProfile.lengthScale * weight;
16187
+ weightedSwayScale += tentacleProfile.swayScale * weight;
16188
+ weightedDepthScale += tentacleProfile.depthScale * weight;
16189
+ weightedPhase += tentacleProfile.phase * weight;
16190
+ }
16191
+ if (totalWeight < 0.0001) {
16192
+ return {
16193
+ core: 0,
16194
+ centerLongitude: longitude,
16195
+ widthScale: 1,
16196
+ lengthScale: 1,
16197
+ swayScale: 1,
16198
+ depthScale: 1,
16199
+ phase: 0,
16200
+ };
16201
+ }
16202
+ return {
16203
+ core: clampNumber$1(totalWeight, 0, 1),
16204
+ centerLongitude: Math.atan2(weightedSin / totalWeight, weightedCos / totalWeight),
16205
+ widthScale: weightedWidthScale / totalWeight,
16206
+ lengthScale: weightedLengthScale / totalWeight,
16207
+ swayScale: weightedSwayScale / totalWeight,
16208
+ depthScale: weightedDepthScale / totalWeight,
16209
+ phase: weightedPhase / totalWeight,
16210
+ };
16211
+ }
16212
+ /**
16213
+ * Resolves the soft lower wave that makes the continuous mesh read as a set of tentacles.
16214
+ *
16215
+ * @private helper of `octopus3d3AvatarVisual`
16216
+ */
16217
+ function resolveContinuousLobeWave(options, longitude) {
16218
+ const { morphologyProfile, animationPhase, timeMs } = options;
16219
+ return ((Math.cos(longitude * OCTOPUS_TENTACLE_COUNT$1 +
16220
+ animationPhase +
16221
+ timeMs / (980 + morphologyProfile.body.lobeCount * 18)) +
16222
+ 1) /
16223
+ 2);
16224
+ }
16225
+ /**
16226
+ * Resolves one base fill tone for a patch on the continuous octopus mesh.
16227
+ *
16228
+ * @private helper of `octopus3d3AvatarVisual`
16229
+ */
16230
+ function resolveContinuousSurfacePatchFillStyle(palette, verticalProgress, forwardness, tentacleCore, lowerLobeWave) {
16231
+ const tonalProgress = clampNumber$1(verticalProgress + lowerLobeWave * 0.1 + tentacleCore * 0.08 - forwardness * 0.08, 0, 1);
16232
+ if (tonalProgress < 0.14) {
16233
+ return palette.highlight;
16234
+ }
16235
+ if (tonalProgress < 0.32) {
16236
+ return palette.secondary;
16237
+ }
16238
+ if (tonalProgress < 0.72) {
16239
+ return forwardness > 0.55 ? palette.secondary : palette.primary;
16240
+ }
16241
+ return tentacleCore > 0.44 ? `${palette.primary}f4` : `${palette.shadow}ee`;
16242
+ }
16243
+ /**
16244
+ * Draws one projected mesh patch with soft shading and a subtle edge.
16245
+ *
16246
+ * @private helper of `octopus3d3AvatarVisual`
16247
+ */
16248
+ function drawContinuousSurfacePatch(context, surfacePatch) {
16249
+ drawProjectedQuad(context, surfacePatch.corners, surfacePatch.fillStyle);
16250
+ if (surfacePatch.lightIntensity > 0) {
16251
+ drawProjectedQuad(context, surfacePatch.corners, `rgba(255, 255, 255, ${0.18 * surfacePatch.lightIntensity})`);
16252
+ }
16253
+ else if (surfacePatch.lightIntensity < 0) {
16254
+ drawProjectedQuad(context, surfacePatch.corners, `rgba(0, 0, 0, ${0.25 * Math.abs(surfacePatch.lightIntensity)})`);
16255
+ }
16256
+ context.save();
16257
+ context.beginPath();
16258
+ context.moveTo(surfacePatch.corners[0].x, surfacePatch.corners[0].y);
16259
+ for (let cornerIndex = 1; cornerIndex < surfacePatch.corners.length; cornerIndex++) {
16260
+ context.lineTo(surfacePatch.corners[cornerIndex].x, surfacePatch.corners[cornerIndex].y);
16261
+ }
16262
+ context.closePath();
16263
+ context.strokeStyle = surfacePatch.outlineColor;
16264
+ context.lineWidth = Math.max(0.7, getProjectedQuadPerimeter(surfacePatch.corners) * 0.0032);
16265
+ context.lineJoin = 'round';
16266
+ context.stroke();
16267
+ context.restore();
16268
+ }
16269
+ /**
16270
+ * Draws projected mantle-current lines on the front of the mesh.
16271
+ *
16272
+ * @private helper of `octopus3d3AvatarVisual`
16273
+ */
16274
+ function drawProjectedSurfaceCurrents(options) {
16275
+ const { context, surfaceOptions, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, morphologyProfile, timeMs, animationPhase, } = options;
16276
+ const currentCount = Math.min(6, morphologyProfile.details.mantleCurrentCount);
16277
+ const centerIndex = (currentCount - 1) / 2;
16278
+ context.save();
16279
+ context.lineCap = 'round';
16280
+ context.lineJoin = 'round';
16281
+ for (let currentIndex = 0; currentIndex < currentCount; currentIndex++) {
16282
+ const baseLongitude = (currentIndex - centerIndex) * 0.15;
16283
+ const projectedPoints = [];
16284
+ for (let sampleIndex = 0; sampleIndex < 8; sampleIndex++) {
16285
+ const progress = sampleIndex / 7;
16286
+ const latitude = -0.46 + progress * 0.74;
16287
+ const longitude = baseLongitude + Math.sin(timeMs / 1160 + animationPhase + currentIndex * 0.7 + progress * 2) * 0.035;
16288
+ const scenePoint = transformScenePoint(sampleContinuousOctopusSurfacePoint(surfaceOptions, latitude, longitude), center, rotationX, rotationY);
16289
+ if (scenePoint.z > center.z - size * 0.016) {
16290
+ projectedPoints.push(projectScenePoint(scenePoint, size, sceneCenterX, sceneCenterY));
16291
+ }
16292
+ }
16293
+ if (projectedPoints.length < 3) {
16294
+ continue;
16295
+ }
16296
+ context.beginPath();
16297
+ context.moveTo(projectedPoints[0].x, projectedPoints[0].y);
16298
+ for (const projectedPoint of projectedPoints.slice(1)) {
16299
+ context.lineTo(projectedPoint.x, projectedPoint.y);
16300
+ }
16301
+ context.strokeStyle = currentIndex % 2 === 0 ? `${palette.highlight}3d` : `${palette.accent}33`;
16302
+ context.lineWidth = size * (0.0055 + currentIndex * 0.00045);
16303
+ context.stroke();
16304
+ }
16305
+ context.restore();
16306
+ }
16307
+ /**
16308
+ * Draws small projected sucker highlights on the waving lower mesh lobes.
16309
+ *
16310
+ * @private helper of `octopus3d3AvatarVisual`
16311
+ */
16312
+ function drawProjectedTentacleSuckers(options) {
16313
+ const { surfaceOptions, size } = options;
16314
+ const { timeMs } = surfaceOptions;
16315
+ for (const tentacleProfile of surfaceOptions.tentacleProfiles) {
16316
+ if (Math.cos(tentacleProfile.centerLongitude) < -0.12) {
16317
+ continue;
16318
+ }
16319
+ for (let suckerIndex = 0; suckerIndex < 3; suckerIndex++) {
16320
+ const latitude = 0.52 + suckerIndex * 0.14;
16321
+ const sideOffset = tentacleProfile.suckerSide * (0.035 + suckerIndex * 0.012) * tentacleProfile.widthScale;
16322
+ const waveOffset = Math.sin(timeMs / 900 + tentacleProfile.phase + suckerIndex * 0.8) * 0.018;
16323
+ drawProjectedSurfaceSpot({
16324
+ ...options,
16325
+ latitude,
16326
+ longitude: tentacleProfile.centerLongitude + sideOffset + waveOffset,
16327
+ radiusScale: size * (0.0065 - suckerIndex * 0.0007),
16328
+ });
16329
+ }
16330
+ }
16331
+ }
16332
+ /**
16333
+ * Draws one tiny projected surface spot by sampling local mesh tangents.
16334
+ *
16335
+ * @private helper of `octopus3d3AvatarVisual`
16336
+ */
16337
+ function drawProjectedSurfaceSpot(options) {
16338
+ const { context, surfaceOptions, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, latitude, longitude, radiusScale, } = options;
16339
+ const localCenter = sampleContinuousOctopusSurfacePoint(surfaceOptions, latitude, longitude);
16340
+ const localHorizontal = sampleContinuousOctopusSurfacePoint(surfaceOptions, latitude, longitude + 0.018);
16341
+ const localVertical = sampleContinuousOctopusSurfacePoint(surfaceOptions, latitude + 0.018, longitude);
16342
+ const sceneCenterPoint = transformScenePoint(localCenter, center, rotationX, rotationY);
16343
+ if (sceneCenterPoint.z <= center.z - size * 0.012) {
16344
+ return;
16345
+ }
16346
+ const projectedCenterPoint = projectScenePoint(sceneCenterPoint, size, sceneCenterX, sceneCenterY);
16347
+ const projectedHorizontalPoint = projectScenePoint(transformScenePoint(localHorizontal, center, rotationX, rotationY), size, sceneCenterX, sceneCenterY);
16348
+ const projectedVerticalPoint = projectScenePoint(transformScenePoint(localVertical, center, rotationX, rotationY), size, sceneCenterX, sceneCenterY);
16349
+ const horizontalRadius = clampNumber$1(Math.hypot(projectedHorizontalPoint.x - projectedCenterPoint.x, projectedHorizontalPoint.y - projectedCenterPoint.y) *
16350
+ radiusScale *
16351
+ 0.74, size * 0.003, size * 0.018);
16352
+ const verticalRadius = clampNumber$1(Math.hypot(projectedVerticalPoint.x - projectedCenterPoint.x, projectedVerticalPoint.y - projectedCenterPoint.y) *
16353
+ radiusScale *
16354
+ 0.52, size * 0.0024, size * 0.014);
16355
+ const rotation = Math.atan2(projectedHorizontalPoint.y - projectedCenterPoint.y, projectedHorizontalPoint.x - projectedCenterPoint.x);
16356
+ context.save();
16357
+ context.translate(projectedCenterPoint.x, projectedCenterPoint.y);
16358
+ context.rotate(rotation);
16359
+ context.beginPath();
16360
+ context.ellipse(0, 0, horizontalRadius, verticalRadius, 0, 0, Math.PI * 2);
16361
+ context.fillStyle = `${palette.highlight}73`;
16362
+ context.fill();
16363
+ context.strokeStyle = `${palette.highlight}99`;
16364
+ context.lineWidth = Math.max(0.7, size * 0.0028);
16365
+ context.stroke();
16366
+ context.restore();
16367
+ }
16368
+ /**
16369
+ * Resolves a signed angular distance from the source longitude to the target longitude.
16370
+ *
16371
+ * @private helper of `octopus3d3AvatarVisual`
16372
+ */
16373
+ function resolveSignedAngularDistance$1(sourceLongitude, targetLongitude) {
16374
+ return Math.atan2(Math.sin(targetLongitude - sourceLongitude), Math.cos(targetLongitude - sourceLongitude));
16375
+ }
16376
+ /**
16377
+ * Smoothly maps a value between two bounds into `[0, 1]`.
16378
+ *
16379
+ * @private helper of `octopus3d3AvatarVisual`
16380
+ */
16381
+ function smoothStep$1(edgeStart, edgeEnd, value) {
16382
+ const progress = clampNumber$1((value - edgeStart) / (edgeEnd - edgeStart), 0, 1);
16383
+ return progress * progress * (3 - 2 * progress);
16384
+ }
16385
+
16386
+ /* eslint-disable no-magic-numbers */
16387
+ /**
16388
+ * Light direction used by the continuous Octopus 3D 4 mesh shading.
16389
+ *
16390
+ * @private helper of `octopus3d4AvatarVisual`
16391
+ */
16392
+ const LIGHT_DIRECTION = normalizeVector3({
16393
+ x: 0.32,
16394
+ y: -0.66,
16395
+ z: 1,
16396
+ });
16397
+ /**
16398
+ * Rim-light direction used to brighten the side of the silhouette.
16399
+ *
16400
+ * @private helper of `octopus3d4AvatarVisual`
16401
+ */
16402
+ const RIM_LIGHT_DIRECTION = normalizeVector3({
16403
+ x: -0.7,
16404
+ y: -0.18,
16405
+ z: 0.55,
16406
+ });
16407
+ /**
16408
+ * Real-octopus tentacle count used by the continuous lower mesh.
16409
+ *
16410
+ * @private helper of `octopus3d4AvatarVisual`
16411
+ */
16412
+ const OCTOPUS_TENTACLE_COUNT = 8;
16413
+ /**
16414
+ * Number of seeded skin spots painted across the octopus body.
16415
+ *
16416
+ * @private helper of `octopus3d4AvatarVisual`
16417
+ */
16418
+ const SKIN_SPOT_COUNT = 14;
16419
+ /**
16420
+ * Cache keyed by the `createRandom` factory reference, stable per mounted `<Avatar/>` component.
16421
+ *
16422
+ * @private helper of `octopus3d4AvatarVisual`
16423
+ */
16424
+ const octopus3d4StableStateCache = new WeakMap();
16425
+ /**
16426
+ * Returns the stable per-avatar state, computing it on first access and caching afterwards.
16427
+ *
16428
+ * @private helper of `octopus3d4AvatarVisual`
16429
+ */
16430
+ function getOctopus3d4StableState(createRandom) {
16431
+ const cached = octopus3d4StableStateCache.get(createRandom);
16432
+ if (cached !== undefined) {
16433
+ return cached;
16434
+ }
16435
+ const morphologyProfile = createOctopus3MorphologyProfile(createRandom);
16436
+ const animationRandom = createRandom('octopus3d4-animation-profile');
16437
+ const eyeRandom = createRandom('octopus3d4-eye-profile');
16438
+ const leftEyePhaseOffset = eyeRandom() * 0.7;
16439
+ const rightEyePhaseOffset = eyeRandom() * 0.7;
16440
+ const state = {
16441
+ morphologyProfile,
16442
+ animationPhase: animationRandom() * Math.PI * 2,
16443
+ leftEyePhaseOffset,
16444
+ rightEyePhaseOffset,
16445
+ tentacleProfiles: createBlobbyContinuousTentacleProfiles(createRandom, morphologyProfile),
16446
+ skinSpots: createBlobbySkinSpots(createRandom),
16447
+ };
16448
+ octopus3d4StableStateCache.set(createRandom, state);
16449
+ return state;
16450
+ }
16451
+ /**
16452
+ * Octopus 3D 4 avatar visual.
16453
+ *
16454
+ * @private built-in avatar visual
16455
+ */
16456
+ const octopus3d4AvatarVisual = {
16457
+ id: 'octopus3d4',
16458
+ title: 'Octopus 3D 4',
16459
+ description: 'Cute blobby 3D octopus rendered as one smooth continuous mesh with naturally undulating tentacles, rich multi-pass shading, pigment spots, and cursor-aware head and eyes.',
16460
+ isAnimated: true,
16461
+ supportsPointerTracking: true,
16462
+ render({ context, size, palette, createRandom, timeMs, interaction }) {
16463
+ const { morphologyProfile, animationPhase, leftEyePhaseOffset, rightEyePhaseOffset, tentacleProfiles, skinSpots } = getOctopus3d4StableState(createRandom);
16464
+ const sceneCenterX = size * 0.5;
16465
+ const sceneCenterY = size * 0.535;
16466
+ const bob = Math.sin(timeMs / 980 + animationPhase) * size * 0.013;
16467
+ const meshCenter = {
16468
+ x: interaction.bodyOffsetX * size * 0.052 + size * morphologyProfile.body.centerXJitterRatio * 0.44,
16469
+ y: -size * 0.07 + interaction.bodyOffsetY * size * 0.028 + bob,
16470
+ z: interaction.intensity * size * 0.02,
16471
+ };
16472
+ const rotationY = -0.08 +
16473
+ Math.sin(timeMs / 2800 + animationPhase) * 0.04 +
16474
+ interaction.bodyOffsetX * 0.24 +
16475
+ interaction.gazeX * 0.98;
16476
+ const rotationX = -0.07 +
16477
+ Math.cos(timeMs / 3200 + animationPhase * 0.7) * 0.02 -
16478
+ interaction.bodyOffsetY * 0.08 -
16479
+ interaction.gazeY * 0.42;
16480
+ const surfaceOptions = {
16481
+ radiusX: size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.horizontalStretch * 1.12,
16482
+ radiusY: size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.verticalStretch * 1.1,
16483
+ radiusZ: size *
16484
+ morphologyProfile.body.bodyRadiusRatio *
16485
+ (1.04 + (morphologyProfile.body.horizontalStretch - 1) * 0.2),
16486
+ morphologyProfile,
16487
+ timeMs,
16488
+ animationPhase,
16489
+ tentacleProfiles,
16490
+ };
16491
+ const surfacePatches = resolveVisibleBlobbyContinuousPatches({
16492
+ ...surfaceOptions,
16493
+ center: meshCenter,
16494
+ rotationX,
16495
+ rotationY,
16496
+ sceneCenterX,
16497
+ sceneCenterY,
16498
+ size,
16499
+ palette,
16500
+ });
16501
+ const eyeLatitude = clampNumber$1(morphologyProfile.face.eyeCenterYOffsetRatio * 4.4 - 0.04, -0.24, 0.08);
16502
+ const eyeLongitude = clampNumber$1(morphologyProfile.face.eyeSpacingRatio * 3.0, 0.18, 0.32);
16503
+ const mouthLatitude = clampNumber$1(eyeLatitude + 0.21 + morphologyProfile.face.mouthYOffsetRatio, 0.08, 0.34);
16504
+ const mouthCenterLongitude = clampNumber$1(morphologyProfile.face.mouthCenterOffsetRatio * 5.6, -0.08, 0.08);
16505
+ const mouthHalfLongitude = clampNumber$1(eyeLongitude * 0.78, 0.15, 0.28);
16506
+ const mouthCurveLatitude = clampNumber$1(mouthLatitude + morphologyProfile.face.mouthCurveDepthRatio * 0.78, mouthLatitude + 0.03, 0.42);
16507
+ const eyeRadiusX = size * morphologyProfile.face.eyeRadiusXRatio * 0.78;
16508
+ const eyeRadiusY = eyeRadiusX * morphologyProfile.face.eyeHeightRatio * 0.92;
16509
+ drawAvatarFrame(context, size, palette);
16510
+ drawBlobbyContinuousAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs);
16511
+ drawBlobbyContinuousShadow(context, size, palette, interaction, timeMs, morphologyProfile);
16512
+ for (const surfacePatch of surfacePatches.sort((firstSurfacePatch, secondSurfacePatch) => firstSurfacePatch.averageDepth - secondSurfacePatch.averageDepth)) {
16513
+ drawBlobbyContinuousSurfacePatch(context, surfacePatch);
16514
+ }
16515
+ drawBlobbySkinSpots({
16516
+ context,
16517
+ surfaceOptions,
16518
+ center: meshCenter,
16519
+ rotationX,
16520
+ rotationY,
16521
+ sceneCenterX,
16522
+ sceneCenterY,
16523
+ size,
16524
+ palette,
16525
+ skinSpots,
16526
+ });
16527
+ drawBlobbyContinuousCurrents({
16528
+ context,
16529
+ surfaceOptions,
16530
+ center: meshCenter,
16531
+ rotationX,
16532
+ rotationY,
16533
+ sceneCenterX,
16534
+ sceneCenterY,
16535
+ size,
16536
+ palette,
16537
+ morphologyProfile,
16538
+ timeMs,
16539
+ animationPhase,
16540
+ });
16541
+ drawBlobbyContinuousSuckers({
16542
+ context,
16543
+ surfaceOptions,
16544
+ center: meshCenter,
16545
+ rotationX,
16546
+ rotationY,
16547
+ sceneCenterX,
16548
+ sceneCenterY,
16549
+ size,
16550
+ palette,
16551
+ });
16552
+ drawBlobbyContinuousGloss({
16553
+ context,
16554
+ surfaceOptions,
16555
+ center: meshCenter,
16556
+ rotationX,
16557
+ rotationY,
16558
+ sceneCenterX,
16559
+ sceneCenterY,
16560
+ size,
16561
+ palette,
16562
+ });
16563
+ drawProjectedOrganicEye(context, sampleBlobbyContinuousSurfacePoint(surfaceOptions, eyeLatitude, -eyeLongitude), eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + leftEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
16564
+ drawProjectedOrganicEye(context, sampleBlobbyContinuousSurfacePoint(surfaceOptions, eyeLatitude, eyeLongitude), eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.85 + rightEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
16565
+ drawProjectedOrganicMouth(context, [
16566
+ sampleBlobbyContinuousSurfacePoint(surfaceOptions, mouthLatitude, mouthCenterLongitude - mouthHalfLongitude),
16567
+ sampleBlobbyContinuousSurfacePoint(surfaceOptions, mouthCurveLatitude, mouthCenterLongitude),
16568
+ sampleBlobbyContinuousSurfacePoint(surfaceOptions, mouthLatitude, mouthCenterLongitude + mouthHalfLongitude),
16569
+ ], meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, palette, size);
16570
+ },
16571
+ };
16572
+ /**
16573
+ * Creates seeded tentacle-lobe profiles around the visible lower octopus body.
16574
+ *
16575
+ * @private helper of `octopus3d4AvatarVisual`
16576
+ */
16577
+ function createBlobbyContinuousTentacleProfiles(createRandom, morphologyProfile) {
16578
+ return Array.from({ length: OCTOPUS_TENTACLE_COUNT }, (_, tentacleIndex) => {
16579
+ const tentacleRandom = createRandom(`octopus3d4-tentacle-${tentacleIndex}`);
16580
+ const progress = tentacleIndex / (OCTOPUS_TENTACLE_COUNT - 1);
16581
+ return {
16582
+ centerLongitude: -Math.PI * 0.9 +
16583
+ progress * Math.PI * 1.8 +
16584
+ (tentacleRandom() - 0.5) * (0.06 + morphologyProfile.tentacles.rootSpreadScale * 0.025),
16585
+ widthScale: 0.92 + tentacleRandom() * 0.3 + (morphologyProfile.tentacles.baseWidthScale - 1) * 0.18,
16586
+ lengthScale: 0.9 + tentacleRandom() * 0.34 + (morphologyProfile.tentacles.flowLengthScale - 1) * 0.24,
16587
+ swayScale: 0.86 + tentacleRandom() * 0.4 + (morphologyProfile.tentacles.swayScale - 1) * 0.22,
16588
+ depthScale: 0.9 + tentacleRandom() * 0.3 + (morphologyProfile.tentacles.tipReachScale - 1) * 0.22,
16589
+ curlScale: 0.62 + tentacleRandom() * 0.46,
16590
+ primaryPhase: tentacleRandom() * Math.PI * 2,
16591
+ secondaryPhase: tentacleRandom() * Math.PI * 2,
16592
+ suckerSide: tentacleRandom() > 0.5 ? 1 : -1,
16593
+ };
16594
+ });
16595
+ }
16596
+ /**
16597
+ * Creates seeded skin pigment spots distributed across the upper octopus mesh.
16598
+ *
16599
+ * @private helper of `octopus3d4AvatarVisual`
16600
+ */
16601
+ function createBlobbySkinSpots(createRandom) {
16602
+ const spotRandom = createRandom('octopus3d4-skin-spots');
16603
+ return Array.from({ length: SKIN_SPOT_COUNT }, () => ({
16604
+ latitude: -0.45 + spotRandom() * 0.7,
16605
+ longitude: -0.5 + spotRandom() * 1.0,
16606
+ radiusScale: 0.0028 + spotRandom() * 0.0052,
16607
+ opacity: 0.16 + spotRandom() * 0.22,
16608
+ }));
16609
+ }
16610
+ /**
16611
+ * Draws the soft underwater atmosphere behind the continuous octopus mesh.
16612
+ *
16613
+ * @private helper of `octopus3d4AvatarVisual`
16614
+ */
16615
+ function drawBlobbyContinuousAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs) {
16616
+ const glowGradient = context.createRadialGradient(sceneCenterX + interaction.gazeX * size * 0.12, sceneCenterY - size * 0.18 + interaction.gazeY * size * 0.05, size * 0.04, sceneCenterX, sceneCenterY, size * (0.68 + interaction.intensity * 0.025));
16617
+ glowGradient.addColorStop(0, `${palette.highlight}74`);
16618
+ glowGradient.addColorStop(0.32, `${palette.accent}32`);
16619
+ glowGradient.addColorStop(1, `${palette.highlight}00`);
16620
+ context.fillStyle = glowGradient;
16621
+ context.fillRect(0, 0, size, size);
16622
+ const lowerGradient = context.createRadialGradient(sceneCenterX + Math.sin(timeMs / 1500) * size * 0.05, sceneCenterY + size * 0.3, size * 0.06, sceneCenterX, sceneCenterY + size * 0.3, size * 0.56);
16623
+ lowerGradient.addColorStop(0, `${palette.secondary}28`);
16624
+ lowerGradient.addColorStop(1, `${palette.secondary}00`);
16625
+ context.fillStyle = lowerGradient;
16626
+ context.fillRect(0, 0, size, size);
16627
+ }
16628
+ /**
16629
+ * Draws the soft lower shadow that anchors the octopus in the avatar frame.
16630
+ *
16631
+ * @private helper of `octopus3d4AvatarVisual`
16632
+ */
16633
+ function drawBlobbyContinuousShadow(context, size, palette, interaction, timeMs, morphologyProfile) {
16634
+ const cx = size * 0.5 + interaction.gazeX * size * 0.046;
16635
+ const cy = size * 0.9 + Math.sin(timeMs / 980) * size * 0.007;
16636
+ const rx = size * (0.2 + morphologyProfile.tentacles.rootSpreadScale * 0.024 + interaction.intensity * 0.022);
16637
+ const ry = size * 0.062;
16638
+ context.save();
16639
+ context.translate(cx, cy);
16640
+ context.scale(1, ry / rx);
16641
+ const blurRadius = rx * 1.42;
16642
+ const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
16643
+ shadowGradient.addColorStop(0, `${palette.shadow}82`);
16644
+ shadowGradient.addColorStop(0.45, `${palette.shadow}4a`);
16645
+ shadowGradient.addColorStop(0.8, `${palette.shadow}1c`);
16646
+ shadowGradient.addColorStop(1, `${palette.shadow}00`);
16647
+ context.fillStyle = shadowGradient;
16648
+ context.beginPath();
16649
+ context.arc(0, 0, blurRadius, 0, Math.PI * 2);
16650
+ context.fill();
16651
+ context.restore();
16652
+ }
16653
+ /**
16654
+ * Number of latitude segments used by the continuous Octopus 3D 4 mesh.
16655
+ *
16656
+ * @private helper of `octopus3d4AvatarVisual`
16657
+ */
16658
+ const LATITUDE_PATCH_COUNT = 20;
16659
+ /**
16660
+ * Number of longitude segments used by the continuous Octopus 3D 4 mesh.
16661
+ *
16662
+ * @private helper of `octopus3d4AvatarVisual`
16663
+ */
16664
+ const LONGITUDE_PATCH_COUNT = 48;
16665
+ /**
16666
+ * Resolves visible projected patches for the continuous Octopus 3D 4 mesh.
16667
+ *
16668
+ * Within a single frame, mesh corner samples and longitude-only computations (tentacle
16669
+ * influence and lobe wave) are quantized to the patch grid and computed once each rather
16670
+ * than re-evaluated for every patch corner.
16671
+ *
16672
+ * @private helper of `octopus3d4AvatarVisual`
16673
+ */
16674
+ function resolveVisibleBlobbyContinuousPatches(options) {
16675
+ const { center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette } = options;
16676
+ const latitudePatchCount = LATITUDE_PATCH_COUNT;
16677
+ const longitudePatchCount = LONGITUDE_PATCH_COUNT;
16678
+ const surfacePatches = [];
16679
+ const latitudeBoundaries = new Float64Array(latitudePatchCount + 1);
16680
+ const longitudeBoundaries = new Float64Array(longitudePatchCount + 1);
16681
+ for (let boundaryIndex = 0; boundaryIndex <= latitudePatchCount; boundaryIndex++) {
16682
+ latitudeBoundaries[boundaryIndex] = -Math.PI / 2 + (boundaryIndex / latitudePatchCount) * Math.PI;
16683
+ }
16684
+ for (let boundaryIndex = 0; boundaryIndex <= longitudePatchCount; boundaryIndex++) {
16685
+ longitudeBoundaries[boundaryIndex] = -Math.PI + (boundaryIndex / longitudePatchCount) * Math.PI * 2;
16686
+ }
16687
+ const cachedTentacleInfluencesByCornerLongitude = new Array(longitudePatchCount + 1);
16688
+ const cachedLobeWavesByCornerLongitude = new Float64Array(longitudePatchCount + 1);
16689
+ const cachedTentacleInfluencesByPatchCenterLongitude = new Array(longitudePatchCount);
16690
+ const cachedLobeWavesByPatchCenterLongitude = new Float64Array(longitudePatchCount);
16691
+ const cachedCosByPatchCenterLongitude = new Float64Array(longitudePatchCount);
16692
+ for (let boundaryIndex = 0; boundaryIndex <= longitudePatchCount; boundaryIndex++) {
16693
+ const cornerLongitude = longitudeBoundaries[boundaryIndex];
16694
+ cachedTentacleInfluencesByCornerLongitude[boundaryIndex] = resolveBlobbyTentacleInfluence(options, cornerLongitude);
16695
+ cachedLobeWavesByCornerLongitude[boundaryIndex] = resolveBlobbyLobeWave(options, cornerLongitude);
16696
+ }
16697
+ for (let longitudeIndex = 0; longitudeIndex < longitudePatchCount; longitudeIndex++) {
16698
+ const patchCenterLongitude = (longitudeBoundaries[longitudeIndex] + longitudeBoundaries[longitudeIndex + 1]) / 2;
16699
+ cachedTentacleInfluencesByPatchCenterLongitude[longitudeIndex] = resolveBlobbyTentacleInfluence(options, patchCenterLongitude);
16700
+ cachedLobeWavesByPatchCenterLongitude[longitudeIndex] = resolveBlobbyLobeWave(options, patchCenterLongitude);
16701
+ cachedCosByPatchCenterLongitude[longitudeIndex] = Math.max(0, Math.cos(patchCenterLongitude));
16702
+ }
16703
+ const cornerCount = (latitudePatchCount + 1) * (longitudePatchCount + 1);
16704
+ const transformedCornerSamples = new Array(cornerCount);
16705
+ for (let latitudeBoundaryIndex = 0; latitudeBoundaryIndex <= latitudePatchCount; latitudeBoundaryIndex++) {
16706
+ const cornerLatitude = latitudeBoundaries[latitudeBoundaryIndex];
16707
+ for (let longitudeBoundaryIndex = 0; longitudeBoundaryIndex <= longitudePatchCount; longitudeBoundaryIndex++) {
16708
+ const cornerLongitude = longitudeBoundaries[longitudeBoundaryIndex];
16709
+ const cornerIndex = latitudeBoundaryIndex * (longitudePatchCount + 1) + longitudeBoundaryIndex;
16710
+ const cornerSample = sampleBlobbyContinuousSurfacePointWithLongitudeCache(options, cornerLatitude, cornerLongitude, cachedTentacleInfluencesByCornerLongitude[longitudeBoundaryIndex], cachedLobeWavesByCornerLongitude[longitudeBoundaryIndex]);
16711
+ transformedCornerSamples[cornerIndex] = transformScenePoint(cornerSample, center, rotationX, rotationY);
16712
+ }
16713
+ }
16714
+ for (let latitudeIndex = 0; latitudeIndex < latitudePatchCount; latitudeIndex++) {
16715
+ const startLatitude = latitudeBoundaries[latitudeIndex];
16716
+ const endLatitude = latitudeBoundaries[latitudeIndex + 1];
16717
+ const centerLatitude = (startLatitude + endLatitude) / 2;
16718
+ const verticalProgress = (Math.sin(centerLatitude) + 1) / 2;
16719
+ const startCornerRowOffset = latitudeIndex * (longitudePatchCount + 1);
16720
+ const endCornerRowOffset = (latitudeIndex + 1) * (longitudePatchCount + 1);
16721
+ for (let longitudeIndex = 0; longitudeIndex < longitudePatchCount; longitudeIndex++) {
16722
+ const transformedCorners = [
16723
+ transformedCornerSamples[startCornerRowOffset + longitudeIndex],
16724
+ transformedCornerSamples[startCornerRowOffset + longitudeIndex + 1],
16725
+ transformedCornerSamples[endCornerRowOffset + longitudeIndex + 1],
16726
+ transformedCornerSamples[endCornerRowOffset + longitudeIndex],
16727
+ ];
16728
+ const surfaceNormal = normalizeVector3(crossProduct3D(subtractPoint3D(transformedCorners[1], transformedCorners[0]), subtractPoint3D(transformedCorners[2], transformedCorners[0])));
16729
+ if (surfaceNormal.z <= 0.006) {
16730
+ continue;
16731
+ }
16732
+ const projectedCorners = [
16733
+ projectScenePoint(transformedCorners[0], size, sceneCenterX, sceneCenterY),
16734
+ projectScenePoint(transformedCorners[1], size, sceneCenterX, sceneCenterY),
16735
+ projectScenePoint(transformedCorners[2], size, sceneCenterX, sceneCenterY),
16736
+ projectScenePoint(transformedCorners[3], size, sceneCenterX, sceneCenterY),
16737
+ ];
16738
+ const lightIntensity = clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION), -1, 1);
16739
+ const rimLightIntensity = Math.pow(clampNumber$1(dotProduct3D(surfaceNormal, RIM_LIGHT_DIRECTION), 0, 1), 2.6);
16740
+ surfacePatches.push({
16741
+ corners: projectedCorners,
16742
+ averageDepth: (transformedCorners[0].z +
16743
+ transformedCorners[1].z +
16744
+ transformedCorners[2].z +
16745
+ transformedCorners[3].z) /
16746
+ 4,
16747
+ lightIntensity,
16748
+ rimLightIntensity,
16749
+ fillStyle: resolveBlobbySurfacePatchFillStyle(palette, verticalProgress, cachedCosByPatchCenterLongitude[longitudeIndex], cachedTentacleInfluencesByPatchCenterLongitude[longitudeIndex].core, cachedLobeWavesByPatchCenterLongitude[longitudeIndex]),
16750
+ outlineColor: verticalProgress < 0.52 ? `${palette.highlight}5c` : `${palette.shadow}66`,
16751
+ });
16752
+ }
16753
+ }
16754
+ return surfacePatches;
16755
+ }
16756
+ /**
16757
+ * Samples one point on the continuous Octopus 3D 4 surface.
16758
+ *
16759
+ * @private helper of `octopus3d4AvatarVisual`
16760
+ */
16761
+ function sampleBlobbyContinuousSurfacePoint(options, latitude, longitude) {
16762
+ return sampleBlobbyContinuousSurfacePointWithLongitudeCache(options, latitude, longitude, resolveBlobbyTentacleInfluence(options, longitude), resolveBlobbyLobeWave(options, longitude));
16763
+ }
16764
+ /**
16765
+ * Samples one point on the continuous Octopus 3D 4 surface using precomputed longitude-only values.
16766
+ *
16767
+ * The patch loop quantizes the mesh into a fixed corner grid, so the same longitude is reused
16768
+ * across every latitude row and each tentacle/lobe value is computed once per frame instead
16769
+ * of `latitudePatchCount * 4` times.
16770
+ *
16771
+ * @private helper of `octopus3d4AvatarVisual`
16772
+ */
16773
+ function sampleBlobbyContinuousSurfacePointWithLongitudeCache(options, latitude, longitude, tentacleInfluence, lowerLobeWave) {
16774
+ const { radiusX, radiusY, radiusZ, morphologyProfile, timeMs, animationPhase } = options;
16775
+ const cosineLatitude = Math.max(0, Math.cos(latitude));
16776
+ const verticalProgress = (Math.sin(latitude) + 1) / 2;
16777
+ const upperBlend = Math.pow(1 - verticalProgress, 1.32);
16778
+ const lowerBlend = smoothStep(0.34, 1, verticalProgress);
16779
+ const tipBlend = smoothStep(0.66, 1, verticalProgress);
16780
+ const centerPull = resolveSignedAngularDistance(longitude, tentacleInfluence.centerLongitude);
16781
+ const effectiveLongitude = longitude + centerPull * lowerBlend * tentacleInfluence.core * (0.26 + tipBlend * 0.22);
16782
+ const mantleRipple = Math.sin(longitude * morphologyProfile.body.lobeCount +
16783
+ animationPhase * 0.6 +
16784
+ timeMs / (1700 + morphologyProfile.body.lobeCount * 28)) *
16785
+ (0.018 + morphologyProfile.body.wobbleAmplitudeRatio * 0.78) *
16786
+ (0.32 + lowerBlend * 0.7);
16787
+ const primaryTentacleWave = Math.sin(timeMs / 740 + tentacleInfluence.primaryPhase + verticalProgress * 2.6) *
16788
+ lowerBlend *
16789
+ tentacleInfluence.core *
16790
+ tentacleInfluence.swayScale;
16791
+ const secondaryTentacleWave = Math.sin(timeMs / 470 + tentacleInfluence.secondaryPhase + verticalProgress * 4.2) *
16792
+ lowerBlend *
16793
+ tentacleInfluence.core *
16794
+ tentacleInfluence.swayScale *
16795
+ 0.42;
16796
+ const tentacleCurl = Math.sin(timeMs / 1180 + tentacleInfluence.primaryPhase * 0.6 + verticalProgress * 3.2) *
16797
+ tipBlend *
16798
+ tentacleInfluence.core *
16799
+ tentacleInfluence.curlScale *
16800
+ 0.36;
16801
+ const horizontalScale = 1.06 +
16802
+ mantleRipple +
16803
+ lowerBlend * (0.18 + (morphologyProfile.tentacles.rootSpreadScale - 1) * 0.12) +
16804
+ lowerBlend * tentacleInfluence.core * (0.22 + lowerLobeWave * 0.14) -
16805
+ upperBlend * 0.08;
16806
+ const depthScale = 1.08 +
16807
+ upperBlend * 0.16 +
16808
+ Math.max(0, Math.cos(effectiveLongitude)) * 0.12 +
16809
+ lowerBlend * tentacleInfluence.core * (0.12 + tentacleInfluence.depthScale * 0.07) -
16810
+ Math.max(0, -Math.cos(effectiveLongitude)) * 0.05;
16811
+ const tentacleTubeRadius = lowerBlend *
16812
+ tentacleInfluence.core *
16813
+ (0.12 + tipBlend * 0.07 + tentacleInfluence.widthScale * 0.028) *
16814
+ radiusX;
16815
+ const planarRadiusX = cosineLatitude * radiusX * horizontalScale + tentacleTubeRadius;
16816
+ const planarRadiusZ = cosineLatitude * radiusZ * depthScale + tentacleTubeRadius * 0.74;
16817
+ const lowerDrop = lowerBlend *
16818
+ radiusY *
16819
+ (0.2 +
16820
+ tentacleInfluence.core *
16821
+ (0.42 +
16822
+ tentacleInfluence.lengthScale * 0.24 +
16823
+ (morphologyProfile.tentacles.flowLengthScale - 1) * 0.1));
16824
+ const combinedTentacleSway = (primaryTentacleWave + secondaryTentacleWave) * radiusX * (0.06 + tipBlend * 0.06);
16825
+ return {
16826
+ x: Math.sin(effectiveLongitude) * planarRadiusX +
16827
+ combinedTentacleSway +
16828
+ tentacleCurl * radiusX * 0.18,
16829
+ y: Math.sin(latitude) * radiusY * (1 + upperBlend * 0.14) -
16830
+ upperBlend * radiusY * 0.12 +
16831
+ lowerDrop +
16832
+ Math.sin(timeMs / 1380 + animationPhase + latitude * 1.5) * lowerBlend * radiusY * 0.022 +
16833
+ Math.cos(timeMs / 820 + tentacleInfluence.primaryPhase) *
16834
+ lowerBlend *
16835
+ tipBlend *
16836
+ tentacleInfluence.core *
16837
+ radiusY *
16838
+ 0.04,
16839
+ z: Math.cos(effectiveLongitude) * planarRadiusZ +
16840
+ Math.cos(timeMs / 960 + tentacleInfluence.primaryPhase + verticalProgress) *
16841
+ lowerBlend *
16842
+ tentacleInfluence.core *
16843
+ radiusZ *
16844
+ 0.044 +
16845
+ tentacleCurl * radiusZ * 0.14,
16846
+ };
16847
+ }
16848
+ /**
16849
+ * Blends nearby seeded tentacle profiles at one mesh longitude.
16850
+ *
16851
+ * @private helper of `octopus3d4AvatarVisual`
16852
+ */
16853
+ function resolveBlobbyTentacleInfluence(options, longitude) {
16854
+ let totalWeight = 0;
16855
+ let weightedSin = 0;
16856
+ let weightedCos = 0;
16857
+ let weightedWidthScale = 0;
16858
+ let weightedLengthScale = 0;
16859
+ let weightedSwayScale = 0;
16860
+ let weightedDepthScale = 0;
16861
+ let weightedCurlScale = 0;
16862
+ let weightedPrimaryPhase = 0;
16863
+ let weightedSecondaryPhase = 0;
16864
+ for (const tentacleProfile of options.tentacleProfiles) {
16865
+ const distance = Math.abs(resolveSignedAngularDistance(longitude, tentacleProfile.centerLongitude));
16866
+ const width = 0.22 * tentacleProfile.widthScale;
16867
+ const weight = Math.exp(-(distance * distance) / (width * width));
16868
+ totalWeight += weight;
16869
+ weightedSin += Math.sin(tentacleProfile.centerLongitude) * weight;
16870
+ weightedCos += Math.cos(tentacleProfile.centerLongitude) * weight;
16871
+ weightedWidthScale += tentacleProfile.widthScale * weight;
16872
+ weightedLengthScale += tentacleProfile.lengthScale * weight;
16873
+ weightedSwayScale += tentacleProfile.swayScale * weight;
16874
+ weightedDepthScale += tentacleProfile.depthScale * weight;
16875
+ weightedCurlScale += tentacleProfile.curlScale * weight;
16876
+ weightedPrimaryPhase += tentacleProfile.primaryPhase * weight;
16877
+ weightedSecondaryPhase += tentacleProfile.secondaryPhase * weight;
16878
+ }
16879
+ if (totalWeight < 0.0001) {
16880
+ return {
16881
+ core: 0,
16882
+ centerLongitude: longitude,
16883
+ widthScale: 1,
16884
+ lengthScale: 1,
16885
+ swayScale: 1,
16886
+ depthScale: 1,
16887
+ curlScale: 1,
16888
+ primaryPhase: 0,
16889
+ secondaryPhase: 0,
16890
+ };
16891
+ }
16892
+ return {
16893
+ core: clampNumber$1(totalWeight, 0, 1),
16894
+ centerLongitude: Math.atan2(weightedSin / totalWeight, weightedCos / totalWeight),
16895
+ widthScale: weightedWidthScale / totalWeight,
16896
+ lengthScale: weightedLengthScale / totalWeight,
16897
+ swayScale: weightedSwayScale / totalWeight,
16898
+ depthScale: weightedDepthScale / totalWeight,
16899
+ curlScale: weightedCurlScale / totalWeight,
16900
+ primaryPhase: weightedPrimaryPhase / totalWeight,
16901
+ secondaryPhase: weightedSecondaryPhase / totalWeight,
16902
+ };
16903
+ }
16904
+ /**
16905
+ * Resolves the soft lower wave that makes the continuous mesh read as a set of tentacles.
16906
+ *
16907
+ * @private helper of `octopus3d4AvatarVisual`
16908
+ */
16909
+ function resolveBlobbyLobeWave(options, longitude) {
16910
+ const { morphologyProfile, animationPhase, timeMs } = options;
16911
+ return ((Math.cos(longitude * OCTOPUS_TENTACLE_COUNT +
16912
+ animationPhase +
16913
+ timeMs / (940 + morphologyProfile.body.lobeCount * 18)) +
16914
+ 1) /
16915
+ 2);
16916
+ }
16917
+ /**
16918
+ * Resolves one base fill tone for a patch on the continuous octopus mesh.
16919
+ *
16920
+ * @private helper of `octopus3d4AvatarVisual`
16921
+ */
16922
+ function resolveBlobbySurfacePatchFillStyle(palette, verticalProgress, forwardness, tentacleCore, lowerLobeWave) {
16923
+ const tonalProgress = clampNumber$1(verticalProgress + lowerLobeWave * 0.1 + tentacleCore * 0.08 - forwardness * 0.08, 0, 1);
16924
+ if (tonalProgress < 0.12) {
16925
+ return palette.highlight;
16926
+ }
16927
+ if (tonalProgress < 0.3) {
16928
+ return palette.secondary;
16929
+ }
16930
+ if (tonalProgress < 0.72) {
16931
+ return forwardness > 0.56 ? palette.secondary : palette.primary;
16932
+ }
16933
+ return tentacleCore > 0.44 ? `${palette.primary}f4` : `${palette.shadow}ee`;
16934
+ }
16935
+ /**
16936
+ * Draws one projected mesh patch with multi-pass shading and a soft edge.
16937
+ *
16938
+ * @private helper of `octopus3d4AvatarVisual`
16939
+ */
16940
+ function drawBlobbyContinuousSurfacePatch(context, surfacePatch) {
16941
+ drawProjectedQuad(context, surfacePatch.corners, surfacePatch.fillStyle);
16942
+ if (surfacePatch.lightIntensity > 0) {
16943
+ drawProjectedQuad(context, surfacePatch.corners, `rgba(255, 255, 255, ${0.2 * surfacePatch.lightIntensity})`);
16944
+ }
16945
+ else if (surfacePatch.lightIntensity < 0) {
16946
+ drawProjectedQuad(context, surfacePatch.corners, `rgba(0, 0, 0, ${0.26 * Math.abs(surfacePatch.lightIntensity)})`);
16947
+ }
16948
+ if (surfacePatch.rimLightIntensity > 0.04) {
16949
+ drawProjectedQuad(context, surfacePatch.corners, `rgba(255, 247, 230, ${0.22 * surfacePatch.rimLightIntensity})`);
16950
+ }
16951
+ context.save();
16952
+ context.beginPath();
16953
+ context.moveTo(surfacePatch.corners[0].x, surfacePatch.corners[0].y);
16954
+ for (let cornerIndex = 1; cornerIndex < surfacePatch.corners.length; cornerIndex++) {
16955
+ context.lineTo(surfacePatch.corners[cornerIndex].x, surfacePatch.corners[cornerIndex].y);
16956
+ }
16957
+ context.closePath();
16958
+ context.strokeStyle = surfacePatch.outlineColor;
16959
+ context.lineWidth = Math.max(0.6, getProjectedQuadPerimeter(surfacePatch.corners) * 0.0026);
16960
+ context.lineJoin = 'round';
16961
+ context.stroke();
16962
+ context.restore();
16963
+ }
16964
+ /**
16965
+ * Draws projected mantle-current lines on the front of the mesh.
16966
+ *
16967
+ * @private helper of `octopus3d4AvatarVisual`
16968
+ */
16969
+ function drawBlobbyContinuousCurrents(options) {
16970
+ const { context, surfaceOptions, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, morphologyProfile, timeMs, animationPhase, } = options;
16971
+ const currentCount = Math.min(6, morphologyProfile.details.mantleCurrentCount);
16972
+ const centerIndex = (currentCount - 1) / 2;
16973
+ context.save();
16974
+ context.lineCap = 'round';
16975
+ context.lineJoin = 'round';
16976
+ for (let currentIndex = 0; currentIndex < currentCount; currentIndex++) {
16977
+ const baseLongitude = (currentIndex - centerIndex) * 0.15;
16978
+ const projectedPoints = [];
16979
+ for (let sampleIndex = 0; sampleIndex < 9; sampleIndex++) {
16980
+ const progress = sampleIndex / 8;
16981
+ const latitude = -0.48 + progress * 0.78;
16982
+ const longitude = baseLongitude + Math.sin(timeMs / 1140 + animationPhase + currentIndex * 0.7 + progress * 2) * 0.038;
16983
+ const scenePoint = transformScenePoint(sampleBlobbyContinuousSurfacePoint(surfaceOptions, latitude, longitude), center, rotationX, rotationY);
16984
+ if (scenePoint.z > center.z - size * 0.016) {
16985
+ projectedPoints.push(projectScenePoint(scenePoint, size, sceneCenterX, sceneCenterY));
16986
+ }
16987
+ }
16988
+ if (projectedPoints.length < 3) {
16989
+ continue;
16990
+ }
16991
+ context.beginPath();
16992
+ context.moveTo(projectedPoints[0].x, projectedPoints[0].y);
16993
+ for (const projectedPoint of projectedPoints.slice(1)) {
16994
+ context.lineTo(projectedPoint.x, projectedPoint.y);
16995
+ }
16996
+ context.strokeStyle = currentIndex % 2 === 0 ? `${palette.highlight}3d` : `${palette.accent}33`;
16997
+ context.lineWidth = size * (0.0055 + currentIndex * 0.00045);
16998
+ context.stroke();
16999
+ }
17000
+ context.restore();
17001
+ }
17002
+ /**
17003
+ * Draws small projected sucker highlights on the waving lower mesh lobes.
17004
+ *
17005
+ * @private helper of `octopus3d4AvatarVisual`
17006
+ */
17007
+ function drawBlobbyContinuousSuckers(options) {
17008
+ const { surfaceOptions, size } = options;
17009
+ const { timeMs } = surfaceOptions;
17010
+ for (const tentacleProfile of surfaceOptions.tentacleProfiles) {
17011
+ if (Math.cos(tentacleProfile.centerLongitude) < -0.16) {
17012
+ continue;
17013
+ }
17014
+ for (let suckerIndex = 0; suckerIndex < 4; suckerIndex++) {
17015
+ const latitude = 0.5 + suckerIndex * 0.12;
17016
+ const sideOffset = tentacleProfile.suckerSide * (0.038 + suckerIndex * 0.014) * tentacleProfile.widthScale;
17017
+ const waveOffset = Math.sin(timeMs / 880 + tentacleProfile.primaryPhase + suckerIndex * 0.78) * 0.02;
17018
+ drawBlobbyContinuousSurfaceSpot({
17019
+ ...options,
17020
+ latitude,
17021
+ longitude: tentacleProfile.centerLongitude + sideOffset + waveOffset,
17022
+ radiusScale: size * (0.0068 - suckerIndex * 0.0006),
17023
+ });
17024
+ }
17025
+ }
17026
+ }
17027
+ /**
17028
+ * Draws seeded pigment spots across the upper mesh for a richer skin texture.
17029
+ *
17030
+ * @private helper of `octopus3d4AvatarVisual`
17031
+ */
17032
+ function drawBlobbySkinSpots(options) {
17033
+ const { context, surfaceOptions, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, skinSpots, } = options;
17034
+ for (const skinSpot of skinSpots) {
17035
+ const localCenter = sampleBlobbyContinuousSurfacePoint(surfaceOptions, skinSpot.latitude, skinSpot.longitude);
17036
+ const sceneCenterPoint = transformScenePoint(localCenter, center, rotationX, rotationY);
17037
+ if (sceneCenterPoint.z <= center.z - size * 0.01) {
17038
+ continue;
17039
+ }
17040
+ const projectedCenterPoint = projectScenePoint(sceneCenterPoint, size, sceneCenterX, sceneCenterY);
17041
+ const spotRadius = size * skinSpot.radiusScale;
17042
+ context.save();
17043
+ context.beginPath();
17044
+ context.arc(projectedCenterPoint.x, projectedCenterPoint.y, spotRadius, 0, Math.PI * 2);
17045
+ context.fillStyle = `${palette.shadow}${formatAlphaHex(skinSpot.opacity)}`;
17046
+ context.fill();
17047
+ context.restore();
17048
+ }
17049
+ }
17050
+ /**
17051
+ * Draws one tiny projected surface spot by sampling local mesh tangents.
17052
+ *
17053
+ * @private helper of `octopus3d4AvatarVisual`
17054
+ */
17055
+ function drawBlobbyContinuousSurfaceSpot(options) {
17056
+ const { context, surfaceOptions, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, latitude, longitude, radiusScale, } = options;
17057
+ const localCenter = sampleBlobbyContinuousSurfacePoint(surfaceOptions, latitude, longitude);
17058
+ const localHorizontal = sampleBlobbyContinuousSurfacePoint(surfaceOptions, latitude, longitude + 0.018);
17059
+ const localVertical = sampleBlobbyContinuousSurfacePoint(surfaceOptions, latitude + 0.018, longitude);
17060
+ const sceneCenterPoint = transformScenePoint(localCenter, center, rotationX, rotationY);
17061
+ if (sceneCenterPoint.z <= center.z - size * 0.012) {
17062
+ return;
17063
+ }
17064
+ const projectedCenterPoint = projectScenePoint(sceneCenterPoint, size, sceneCenterX, sceneCenterY);
17065
+ const projectedHorizontalPoint = projectScenePoint(transformScenePoint(localHorizontal, center, rotationX, rotationY), size, sceneCenterX, sceneCenterY);
17066
+ const projectedVerticalPoint = projectScenePoint(transformScenePoint(localVertical, center, rotationX, rotationY), size, sceneCenterX, sceneCenterY);
17067
+ const horizontalRadius = clampNumber$1(Math.hypot(projectedHorizontalPoint.x - projectedCenterPoint.x, projectedHorizontalPoint.y - projectedCenterPoint.y) *
17068
+ radiusScale *
17069
+ 0.78, size * 0.003, size * 0.02);
17070
+ const verticalRadius = clampNumber$1(Math.hypot(projectedVerticalPoint.x - projectedCenterPoint.x, projectedVerticalPoint.y - projectedCenterPoint.y) *
17071
+ radiusScale *
17072
+ 0.54, size * 0.0024, size * 0.015);
17073
+ const rotation = Math.atan2(projectedHorizontalPoint.y - projectedCenterPoint.y, projectedHorizontalPoint.x - projectedCenterPoint.x);
17074
+ context.save();
17075
+ context.translate(projectedCenterPoint.x, projectedCenterPoint.y);
17076
+ context.rotate(rotation);
17077
+ context.beginPath();
17078
+ context.ellipse(0, 0, horizontalRadius, verticalRadius, 0, 0, Math.PI * 2);
17079
+ context.fillStyle = `${palette.highlight}80`;
17080
+ context.fill();
17081
+ context.strokeStyle = `${palette.highlight}a8`;
17082
+ context.lineWidth = Math.max(0.7, size * 0.0028);
17083
+ context.stroke();
17084
+ context.restore();
17085
+ }
17086
+ /**
17087
+ * Draws a soft, slowly drifting gloss highlight on the front of the mesh.
17088
+ *
17089
+ * @private helper of `octopus3d4AvatarVisual`
17090
+ */
17091
+ function drawBlobbyContinuousGloss(options) {
17092
+ const { context, surfaceOptions, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size } = options;
17093
+ const { timeMs, animationPhase } = surfaceOptions;
17094
+ const glossLatitude = -0.3 + Math.sin(timeMs / 2700 + animationPhase) * 0.04;
17095
+ const glossLongitude = -0.18 + Math.cos(timeMs / 2300 + animationPhase * 0.8) * 0.05;
17096
+ const localCenter = sampleBlobbyContinuousSurfacePoint(surfaceOptions, glossLatitude, glossLongitude);
17097
+ const sceneCenterPoint = transformScenePoint(localCenter, center, rotationX, rotationY);
17098
+ if (sceneCenterPoint.z <= center.z) {
17099
+ return;
17100
+ }
17101
+ const projectedCenterPoint = projectScenePoint(sceneCenterPoint, size, sceneCenterX, sceneCenterY);
17102
+ const glossRadius = size * 0.058;
17103
+ context.save();
17104
+ const glossGradient = context.createRadialGradient(projectedCenterPoint.x - glossRadius * 0.3, projectedCenterPoint.y - glossRadius * 0.4, glossRadius * 0.04, projectedCenterPoint.x, projectedCenterPoint.y, glossRadius);
17105
+ glossGradient.addColorStop(0, 'rgba(255, 255, 255, 0.34)');
17106
+ glossGradient.addColorStop(0.5, 'rgba(255, 255, 255, 0.1)');
17107
+ glossGradient.addColorStop(1, 'rgba(255, 255, 255, 0)');
17108
+ context.fillStyle = glossGradient;
17109
+ context.beginPath();
17110
+ context.ellipse(projectedCenterPoint.x, projectedCenterPoint.y, glossRadius * 1.05, glossRadius * 0.78, 0, 0, Math.PI * 2);
17111
+ context.fill();
17112
+ context.restore();
17113
+ }
17114
+ /**
17115
+ * Resolves a signed angular distance from the source longitude to the target longitude.
17116
+ *
17117
+ * @private helper of `octopus3d4AvatarVisual`
17118
+ */
17119
+ function resolveSignedAngularDistance(sourceLongitude, targetLongitude) {
17120
+ return Math.atan2(Math.sin(targetLongitude - sourceLongitude), Math.cos(targetLongitude - sourceLongitude));
17121
+ }
17122
+ /**
17123
+ * Smoothly maps a value between two bounds into `[0, 1]`.
17124
+ *
17125
+ * @private helper of `octopus3d4AvatarVisual`
17126
+ */
17127
+ function smoothStep(edgeStart, edgeEnd, value) {
17128
+ const progress = clampNumber$1((value - edgeStart) / (edgeEnd - edgeStart), 0, 1);
17129
+ return progress * progress * (3 - 2 * progress);
17130
+ }
17131
+ /**
17132
+ * Converts an opacity ratio into a two-digit hexadecimal alpha suffix.
17133
+ *
17134
+ * @private helper of `octopus3d4AvatarVisual`
17135
+ */
17136
+ function formatAlphaHex(opacity) {
17137
+ return Math.round(clampNumber$1(opacity, 0, 1) * 255)
17138
+ .toString(16)
17139
+ .padStart(2, '0');
17140
+ }
17141
+
15593
17142
  /* eslint-disable no-magic-numbers */
15594
17143
  /**
15595
17144
  * Octopus avatar visual.
@@ -16360,6 +17909,8 @@ const AVATAR_VISUALS = [
16360
17909
  octopus3AvatarVisual,
16361
17910
  octopus3dAvatarVisual,
16362
17911
  octopus3d2AvatarVisual,
17912
+ octopus3d3AvatarVisual,
17913
+ octopus3d4AvatarVisual,
16363
17914
  asciiOctopusAvatarVisual,
16364
17915
  minecraftAvatarVisual,
16365
17916
  minecraft2AvatarVisual,
@@ -18648,7 +20199,15 @@ function buildTeammateMetadata(entry) {
18648
20199
  * Builds the teammate request text, optionally including context.
18649
20200
  */
18650
20201
  function buildTeammateRequest(message, context) {
18651
- return context ? `${message}\n\nContext:\n${context}` : message;
20202
+ if (!context) {
20203
+ return message;
20204
+ }
20205
+ return spaceTrim$1((block) => `
20206
+ ${block(message)}
20207
+
20208
+ Context:
20209
+ ${block(context)}
20210
+ `);
18652
20211
  }
18653
20212
  /**
18654
20213
  * Builds a minimal chat prompt for teammate calls.
@@ -22474,7 +24033,11 @@ function createUseProjectToolFunctions() {
22474
24033
  const lineRangedContent = applyOptionalLineRange(decodedContent, args.startLine, args.endLine);
22475
24034
  const wasCharacterTruncated = lineRangedContent.length > MAX_PROJECT_FILE_CONTENT_CHARACTERS;
22476
24035
  const contentToReturn = wasCharacterTruncated
22477
- ? `${lineRangedContent.slice(0, MAX_PROJECT_FILE_CONTENT_CHARACTERS)}\n\n[...truncated...]`
24036
+ ? spaceTrim$1((block) => `
24037
+ ${block(lineRangedContent.slice(0, MAX_PROJECT_FILE_CONTENT_CHARACTERS))}
24038
+
24039
+ [...truncated...]
24040
+ `)
22478
24041
  : lineRangedContent;
22479
24042
  const wasTruncated = decoded.isTruncated || wasCharacterTruncated;
22480
24043
  if (wasCharacterTruncated) {
@@ -26010,11 +27573,19 @@ const JsonFileImportPlugin = {
26010
27573
  try {
26011
27574
  const json = JSON.parse(content);
26012
27575
  const formattedJson = JSON.stringify(json, null, 4);
26013
- return `\`\`\`json\n${formattedJson}\n\`\`\``;
27576
+ return spaceTrim$1((block) => `
27577
+ \`\`\`json
27578
+ ${block(formattedJson)}
27579
+ \`\`\`
27580
+ `);
26014
27581
  }
26015
27582
  catch (error) {
26016
27583
  // If JSON is invalid, still import it but maybe not as pretty JSON
26017
- return `\`\`\`json\n${content}\n\`\`\``;
27584
+ return spaceTrim$1((block) => `
27585
+ \`\`\`json
27586
+ ${block(content)}
27587
+ \`\`\`
27588
+ `);
26018
27589
  }
26019
27590
  },
26020
27591
  };
@@ -26038,7 +27609,11 @@ const TextFileImportPlugin = {
26038
27609
  import(content, mimeType) {
26039
27610
  const extension = mimeTypeToExtension(mimeType);
26040
27611
  const codeBlockType = extension || 'txt';
26041
- return `\`\`\`${codeBlockType}\n${content}\n\`\`\``;
27612
+ return spaceTrim$1((block) => `
27613
+ \`\`\`${codeBlockType}
27614
+ ${block(content)}
27615
+ \`\`\`
27616
+ `);
26042
27617
  },
26043
27618
  };
26044
27619
 
@@ -26285,11 +27860,19 @@ function collectExampleInteractionLines(parseResult, samples) {
26285
27860
  const examples = [];
26286
27861
  const initialMessage = (_a = parseResult.commitments.find((commitment) => commitment.type === 'INITIAL MESSAGE')) === null || _a === void 0 ? void 0 : _a.content;
26287
27862
  if (initialMessage) {
26288
- examples.push(`**Agent:**\n${initialMessage}`);
27863
+ examples.push(spaceTrim$1((block) => `
27864
+ **Agent:**
27865
+ ${block(initialMessage)}
27866
+ `));
26289
27867
  }
26290
27868
  if (samples && samples.length > 0) {
26291
27869
  for (const sample of samples) {
26292
- examples.push(`**User:** ${sample.question}\n\n**Agent:**\n${sample.answer}`);
27870
+ examples.push(spaceTrim$1((block) => `
27871
+ **User:** ${block(String(sample.question))}
27872
+
27873
+ **Agent:**
27874
+ ${block(sample.answer)}
27875
+ `));
26293
27876
  }
26294
27877
  }
26295
27878
  return examples;
@@ -28004,7 +29587,11 @@ function removeLeadingTopLevelHeading(markdown) {
28004
29587
  * @private internal utility of `createStandaloneBookLanguageMarkdown`
28005
29588
  */
28006
29589
  function renderDocumentationSection(title, documentation) {
28007
- return `#### ${title}\n\n${removeLeadingTopLevelHeading(documentation)}`;
29590
+ return spaceTrim$1((block) => `
29591
+ #### ${title}
29592
+
29593
+ ${block(removeLeadingTopLevelHeading(documentation))}
29594
+ `);
28008
29595
  }
28009
29596
  /**
28010
29597
  * Renders the documentation body for one grouped commitment entry.
@@ -28655,7 +30242,9 @@ function parseBookMessageHeader(line) {
28655
30242
  * @private internal utility of `Book`
28656
30243
  */
28657
30244
  function parseCommitmentHeader(line) {
28658
- const match = /^([A-Z][A-Z0-9]*(?: [A-Z0-9]+)*)(?:\s+(.*))?$/u.exec(line);
30245
+ // Require at least 2 characters in the first keyword word to avoid treating common
30246
+ // single-letter words (e.g. "V" in Czech, "I" or "A" in English) as commitment headers.
30247
+ const match = /^([A-Z][A-Z0-9]+(?: [A-Z0-9]+)*)(?:\s+(.*))?$/u.exec(line);
28659
30248
  if (!match) {
28660
30249
  return null;
28661
30250
  }
@@ -28851,6 +30440,18 @@ function normalizeHistoryVersionName(versionName) {
28851
30440
  const normalizedVersionName = versionName.trim();
28852
30441
  return normalizedVersionName.length > 0 ? normalizedVersionName : null;
28853
30442
  }
30443
+ /**
30444
+ * Builds a Supabase `.or()` filter for agent name or permanent id lookups.
30445
+ *
30446
+ * @param agentNameOrPermanentId - Agent name or stable permanent identifier to match.
30447
+ * @returns `.or()` filter string safe to pass to Supabase.
30448
+ *
30449
+ * @private internal helper of `AgentCollectionInSupabase`
30450
+ */
30451
+ function buildAgentNameOrPermanentIdFilter(agentNameOrPermanentId) {
30452
+ const encodedAgentIdentifier = encodeURIComponent(agentNameOrPermanentId);
30453
+ return `agentName.eq.${encodedAgentIdentifier},permanentId.eq.${encodedAgentIdentifier}`;
30454
+ }
28854
30455
  /**
28855
30456
  * Agent collection stored in a Supabase table.
28856
30457
  *
@@ -28898,21 +30499,63 @@ class AgentCollectionInSupabase /* TODO: [🌈][🐱‍🚀] implements AgentCol
28898
30499
  if (isVerbose) {
28899
30500
  console.info(`Found ${selectResult.data.length} agents in directory`);
28900
30501
  }
28901
- return selectResult.data.map(({ agentName, agentProfile, permanentId }) => {
28902
- if (isVerbose && agentProfile.agentName !== agentName) {
28903
- console.warn(spaceTrim(`
30502
+ return selectResult.data.map((row) => this.mapAgentBasicInformationRow(row, isVerbose));
30503
+ }
30504
+ /**
30505
+ * Finds one active agent profile by its human-readable name or permanent id.
30506
+ *
30507
+ * This keeps route-level lookups from loading the whole agent collection when only
30508
+ * one canonical route identifier is needed.
30509
+ *
30510
+ * @param agentNameOrPermanentId - Agent name or stable permanent identifier.
30511
+ * @returns Matching active agent profile or `null` when not found.
30512
+ *
30513
+ * @public exported from `@promptbook/core`
30514
+ */
30515
+ async findAgentBasicInformation(agentNameOrPermanentId) {
30516
+ var _a;
30517
+ const { isVerbose = DEFAULT_IS_VERBOSE } = this.options || {};
30518
+ const selectResult = await this.supabaseClient
30519
+ .from(this.getTableName('Agent'))
30520
+ .select('agentName,agentProfile,permanentId')
30521
+ .or(buildAgentNameOrPermanentIdFilter(agentNameOrPermanentId))
30522
+ .is('deletedAt', null)
30523
+ .order('createdAt', { ascending: true })
30524
+ .limit(1);
30525
+ if (selectResult.error) {
30526
+ throw new DatabaseError(spaceTrim((block) => `
30527
+
30528
+ Error fetching agent "${agentNameOrPermanentId}" from Supabase:
30529
+
30530
+ ${block(selectResult.error.message)}
30531
+ `));
30532
+ }
30533
+ const row = (_a = selectResult.data) === null || _a === void 0 ? void 0 : _a[0];
30534
+ return row ? this.mapAgentBasicInformationRow(row, isVerbose) : null;
30535
+ }
30536
+ /**
30537
+ * Converts one database row into public agent information.
30538
+ *
30539
+ * @param row - Database row carrying the persisted profile snapshot.
30540
+ * @param isVerbose - Whether profile-name mismatches should be logged.
30541
+ * @returns Agent profile with canonical database name and permanent id.
30542
+ *
30543
+ * @private internal helper of `AgentCollectionInSupabase`
30544
+ */
30545
+ mapAgentBasicInformationRow({ agentName, agentProfile, permanentId }, isVerbose) {
30546
+ if (isVerbose && agentProfile.agentName !== agentName) {
30547
+ console.warn(spaceTrim(`
28904
30548
  Agent name mismatch for agent "${agentName}". Using name from database.
28905
30549
 
28906
30550
  agentName: "${agentName}"
28907
30551
  agentProfile.agentName: "${agentProfile.agentName}"
28908
30552
  `));
28909
- }
28910
- return {
28911
- ...agentProfile,
28912
- agentName,
28913
- permanentId: permanentId || agentProfile.permanentId,
28914
- };
28915
- });
30553
+ }
30554
+ return {
30555
+ ...agentProfile,
30556
+ agentName,
30557
+ permanentId: permanentId || agentProfile.permanentId,
30558
+ };
28916
30559
  }
28917
30560
  /**
28918
30561
  * Retrieves the permanent ID of an agent by its name or permanent ID.
@@ -28921,7 +30564,7 @@ class AgentCollectionInSupabase /* TODO: [🌈][🐱‍🚀] implements AgentCol
28921
30564
  const selectResult = await this.supabaseClient
28922
30565
  .from(this.getTableName('Agent'))
28923
30566
  .select('permanentId')
28924
- .or(`agentName.eq.${agentNameOrPermanentId},permanentId.eq.${agentNameOrPermanentId}`)
30567
+ .or(buildAgentNameOrPermanentIdFilter(agentNameOrPermanentId))
28925
30568
  .order('createdAt', { ascending: true }) // Pick oldest if multiple match by name
28926
30569
  .limit(1);
28927
30570
  if (selectResult.error || !selectResult.data || selectResult.data.length === 0) {
@@ -28936,7 +30579,7 @@ class AgentCollectionInSupabase /* TODO: [🌈][🐱‍🚀] implements AgentCol
28936
30579
  const selectResult = await this.supabaseClient
28937
30580
  .from(this.getTableName('Agent'))
28938
30581
  .select('agentSource')
28939
- .or(`agentName.eq.${agentNameOrPermanentId},permanentId.eq.${agentNameOrPermanentId}`)
30582
+ .or(buildAgentNameOrPermanentIdFilter(agentNameOrPermanentId))
28940
30583
  .is('deletedAt', null)
28941
30584
  .order('createdAt', { ascending: true }) // Pick oldest if multiple match by name
28942
30585
  .limit(1);
@@ -34417,7 +36060,8 @@ function colorizeAvailableProviderMessage(providerMessage, { isInstalled, isFull
34417
36060
  * @private internal function of `$registeredLlmToolsMessage`
34418
36061
  */
34419
36062
  function createUsedEnvMessage() {
34420
- return `Unknown \`.env\` file` ;
36063
+ return `Unknown \`.env\` file`
36064
+ ;
34421
36065
  }
34422
36066
  // TODO: [®] DRY Register logic
34423
36067
  // TODO: [🧠][⚛] Maybe pass env as argument
@@ -35730,7 +37374,9 @@ class OpenAiAgentKitExecutionToolsToolBuilder {
35730
37374
  if (!executionTools || !executionTools.script) {
35731
37375
  throw new PipelineExecutionError(`Model requested tools but no executionTools.script were provided in OpenAiAgentKitExecutionTools options`);
35732
37376
  }
35733
- return Array.isArray(executionTools.script) ? executionTools.script : [executionTools.script];
37377
+ return Array.isArray(executionTools.script)
37378
+ ? executionTools.script
37379
+ : [executionTools.script];
35734
37380
  }
35735
37381
  /**
35736
37382
  * Resolves the assistant-visible AgentKit tool response while preserving structured tool result data.
@@ -38792,7 +40438,7 @@ class OpenAiVectorStoreKnowledgeSourcePreparer {
38792
40438
  return { skippedReason: 'invalid_data_url' };
38793
40439
  }
38794
40440
  return {
38795
- file: new File([parsed.buffer], parsed.filename, {
40441
+ file: new File([new Uint8Array(parsed.buffer)], parsed.filename, {
38796
40442
  type: parsed.mimeType,
38797
40443
  }),
38798
40444
  sizeBytes: parsed.buffer.length,
@@ -40555,7 +42201,7 @@ function emitAgentLlmExecutionToolsAssistantPreparationProgress(options) {
40555
42201
  * Computes one stable hash from a JSON-serializable value.
40556
42202
  */
40557
42203
  function computeJsonHash$1(value) {
40558
- return SHA256(JSON.stringify(value)).toString();
42204
+ return CryptoJS.SHA256(JSON.stringify(value)).toString();
40559
42205
  }
40560
42206
  /**
40561
42207
  * Handles OpenAI AgentKit-backed executions for `AgentLlmExecutionTools`.
@@ -40713,7 +42359,7 @@ AgentLlmExecutionToolsAgentKitRunner.vectorStoreCache = new Map();
40713
42359
  * Computes one stable hash from a JSON-serializable value.
40714
42360
  */
40715
42361
  function computeJsonHash(value) {
40716
- return SHA256(JSON.stringify(value)).toString();
42362
+ return CryptoJS.SHA256(JSON.stringify(value)).toString();
40717
42363
  }
40718
42364
  /**
40719
42365
  * Removes assistant-managed requirements before the prompt is executed via OpenAI Assistants.
@@ -41103,7 +42749,11 @@ function truncateAttachmentInlineText(content, maxCharacters) {
41103
42749
  };
41104
42750
  }
41105
42751
  return {
41106
- content: `${content.slice(0, Math.max(0, maxCharacters))}\n\n[...truncated...]`,
42752
+ content: spaceTrim$1((block) => `
42753
+ ${block(content.slice(0, Math.max(0, maxCharacters)))}
42754
+
42755
+ [...truncated...]
42756
+ `),
41107
42757
  isTruncated: true,
41108
42758
  };
41109
42759
  }
@@ -41569,7 +43219,7 @@ class AgentLlmExecutionTools {
41569
43219
  * Returns a virtual model name representing the agent behavior.
41570
43220
  */
41571
43221
  get modelName() {
41572
- const hash = SHA256(hexEncoder.parse(this.options.agentSource)).toString( /* hex */);
43222
+ const hash = CryptoJS.SHA256(hexEncoder.parse(this.options.agentSource)).toString( /* hex */);
41573
43223
  const agentId = hash.substring(0, 10);
41574
43224
  return (normalizeToKebabCase(this.title) + '-' + agentId);
41575
43225
  }
@@ -45280,7 +46930,7 @@ const _AnthropicClaudeMetadataRegistration = $llmToolsMetadataRegister.register(
45280
46930
  title: 'Anthropic Claude',
45281
46931
  packageName: '@promptbook/anthropic-claude',
45282
46932
  className: 'AnthropicClaudeExecutionTools',
45283
- envVariables: ['ANTHROPIC_CLAUDE_API_KEY'],
46933
+ envVariables: ['ANTHROPIC_API_KEY', 'ANTHROPIC_CLAUDE_API_KEY'],
45284
46934
  trustLevel: 'CLOSED',
45285
46935
  order: MODEL_ORDERS.TOP_TIER,
45286
46936
  getBoilerplateConfiguration() {
@@ -45297,14 +46947,17 @@ const _AnthropicClaudeMetadataRegistration = $llmToolsMetadataRegister.register(
45297
46947
  };
45298
46948
  },
45299
46949
  createConfigurationFromEnv(env) {
46950
+ var _a;
45300
46951
  // Note: Note using `process.env` BUT `env` to pass in the environment variables dynamically
45301
- if (typeof env.ANTHROPIC_CLAUDE_API_KEY === 'string') {
46952
+ // Note: `ANTHROPIC_API_KEY` takes precedence over the deprecated `ANTHROPIC_CLAUDE_API_KEY`
46953
+ const apiKey = (_a = env.ANTHROPIC_API_KEY) !== null && _a !== void 0 ? _a : env.ANTHROPIC_CLAUDE_API_KEY;
46954
+ if (typeof apiKey === 'string') {
45302
46955
  return {
45303
46956
  title: 'Claude (from env)',
45304
46957
  packageName: '@promptbook/anthropic-claude',
45305
46958
  className: 'AnthropicClaudeExecutionTools',
45306
46959
  options: {
45307
- apiKey: env.ANTHROPIC_CLAUDE_API_KEY,
46960
+ apiKey,
45308
46961
  },
45309
46962
  };
45310
46963
  }
@@ -48715,20 +50368,7 @@ function appendChatAttachmentContext(messageContent, attachments) {
48715
50368
  }
48716
50369
 
48717
50370
  /**
48718
- * Core Promptbook server configuration
48719
- *
48720
- * Used for "Adam" agent which is built in as default ancestor for new agents and other well known agents
48721
- *
48722
- * @public exported from `@promptbook/core`
48723
- */
48724
- const CORE_AGENTS_SERVER = {
48725
- title: 'Promptbook Core',
48726
- description: `Core Promptbook server used for Adam agent which is built in as default ancestor for new agents and other well known agents.`,
48727
- owner: PROMPTBOOK_LEGAL_ENTITY,
48728
- url: 'https://core.ptbk.io/',
48729
- };
48730
- /**
48731
- * Names of well known agents hosted on Core Promptbook server
50371
+ * Names of well known agents bundled in the local `.core` folder of every Agents Server
48732
50372
  *
48733
50373
  * - `Adam`: The default ancestor agent for new agents
48734
50374
  * - `Teacher`: Agent that knows book syntax and can help with self-learning
@@ -48754,7 +50394,6 @@ const CORE_AGENTS_SERVER_WELL_KNOWN_AGENT_NAMES = {
48754
50394
  * @public exported from `@promptbook/core`
48755
50395
  */
48756
50396
  const PUBLIC_AGENTS_SERVERS = [
48757
- CORE_AGENTS_SERVER,
48758
50397
  {
48759
50398
  title: 'Promptbook Gallery',
48760
50399
  description: `Gallery of ideas, AI professions,... like AI Agenta photobank.`,
@@ -49204,5 +50843,5 @@ function $generateBookBoilerplate(options) {
49204
50843
  }
49205
50844
  // TODO: [🤶] Maybe export through `@promptbook/utils` or `@promptbook/random` package
49206
50845
 
49207
- export { $bookTranspilersRegister, $generateBookBoilerplate, $llmToolsMetadataRegister, $llmToolsRegister, $scrapersMetadataRegister, $scrapersRegister, ADMIN_EMAIL, ADMIN_GITHUB_NAME, API_REQUEST_TIMEOUT, AbstractFormatError, Agent, AgentCollectionInSupabase, AgentLlmExecutionTools, AgentOsTranspiler, AnthropicClaudeManagedTranspiler, AnthropicClaudeSdkTranspiler, AuthenticationError, BIG_DATASET_TRESHOLD, BOOK_LANGUAGE_VERSION, BlackholeStorage, BoilerplateError, BoilerplateFormfactorDefinition, Book, CLAIM, CLI_APP_ID, COLOR_CONSTANTS, CORE_AGENTS_SERVER, CORE_AGENTS_SERVER_WELL_KNOWN_AGENT_NAMES, CallbackInterfaceTools, ChatbotFormfactorDefinition, CollectionError, CompletionFormfactorDefinition, ConflictError, CsvFormatError, CsvFormatParser, DEFAULT_AGENTS_DIRNAME, DEFAULT_BOOK, DEFAULT_BOOKS_DIRNAME, DEFAULT_BOOK_OUTPUT_PARAMETER_NAME, DEFAULT_BOOK_TITLE, DEFAULT_CSV_SETTINGS, DEFAULT_DOWNLOAD_CACHE_DIRNAME, DEFAULT_EXECUTION_CACHE_DIRNAME, DEFAULT_GET_PIPELINE_COLLECTION_FUNCTION_NAME, DEFAULT_INTERMEDIATE_FILES_STRATEGY, DEFAULT_IS_AUTO_INSTALLED, DEFAULT_IS_VERBOSE, DEFAULT_MAX_CONCURRENT_UPLOADS, DEFAULT_MAX_EXECUTION_ATTEMPTS, DEFAULT_MAX_FILE_SIZE, DEFAULT_MAX_KNOWLEDGE_SOURCES_SCRAPING_DEPTH, DEFAULT_MAX_KNOWLEDGE_SOURCES_SCRAPING_TOTAL, DEFAULT_MAX_PARALLEL_COUNT, DEFAULT_MAX_RECURSION, DEFAULT_MAX_REQUESTS_PER_MINUTE, DEFAULT_PIPELINE_COLLECTION_BASE_FILENAME, DEFAULT_PROMPT_TASK_TITLE, DEFAULT_REMOTE_SERVER_URL, DEFAULT_SCRAPE_CACHE_DIRNAME, DEFAULT_TASK_SIMULATED_DURATION_MS, DEFAULT_TASK_TITLE, DatabaseError, E2BTranspiler, EXPECTATION_UNITS, EnvironmentMismatchError, ExecutionReportStringOptionsDefaults, ExpectError, FAILED_VALUE_PLACEHOLDER, FORMFACTOR_DEFINITIONS, FormattedBookInMarkdownTranspiler, GENERIC_PIPELINE_INTERFACE, GeneratorFormfactorDefinition, GenericFormfactorDefinition, HTTP_STATUS_CODES, ImageGeneratorFormfactorDefinition, KnowledgeScrapeError, LIMITS, LimitReachedError, MANDATORY_CSV_SETTINGS, MAX_FILENAME_LENGTH, MODEL_ORDERS, MODEL_TRUST_LEVELS, MODEL_VARIANTS, MatcherFormfactorDefinition, MemoryStorage, MissingToolsError, MultipleLlmExecutionTools, NAME, NETWORK_LIMITS, NonTaskSectionTypes, NotAllowed, NotFoundError, NotYetImplementedCommitmentDefinition, NotYetImplementedError, ORDER_OF_PIPELINE_JSON, OpenAiAgentsTranspiler, OpenAiSdkTranspiler, PADDING_LINES, PENDING_VALUE_PLACEHOLDER, PLAYGROUND_APP_ID, PROMPTBOOK_CHAT_COLOR, PROMPTBOOK_COLOR, PROMPTBOOK_ENGINE_VERSION, PROMPTBOOK_ERRORS, PROMPTBOOK_LEGAL_ENTITY, PROMPTBOOK_LOGO_URL, PROMPTBOOK_SYNTAX_COLORS, PROMPT_PARAMETER_SELF_LEARNING_ENABLED, PUBLIC_AGENTS_SERVERS, ParseError, PipelineExecutionError, PipelineLogicError, PipelineUrlError, PrefixStorage, PromptbookFetchError, RESERVED_PARAMETER_NAMES, RemoteAgent, SET_IS_VERBOSE, SectionTypes, SheetsFormfactorDefinition, TIME_INTERVALS, TaskTypes, TextFormatParser, TranslatorFormfactorDefinition, UNCERTAIN_USAGE, UNCERTAIN_ZERO_VALUE, USER_CHAT_COLOR, UnexpectedError, VirusTotalFileSecurityChecker, WrappedError, ZERO_USAGE, ZERO_VALUE, _AgentMetadata, _AgentRegistration, _AnthropicClaudeMetadataRegistration, _AzureOpenAiMetadataRegistration, _BoilerplateScraperMetadataRegistration, _DeepseekMetadataRegistration, _DocumentScraperMetadataRegistration, _GoogleMetadataRegistration, _LegacyDocumentScraperMetadataRegistration, _MarkdownScraperMetadataRegistration, _MarkitdownScraperMetadataRegistration, _OllamaMetadataRegistration, _OpenAiAssistantMetadataRegistration, _OpenAiCompatibleMetadataRegistration, _OpenAiMetadataRegistration, _PdfScraperMetadataRegistration, _WebsiteScraperMetadataRegistration, aboutPromptbookInformation, addUsage, appendChatAttachmentContext, appendChatAttachmentContextWithContent, book, cacheLlmTools, compilePipeline, computeAgentHash, computeCosineSimilarity, countUsage, createAgentLlmExecutionTools, createAgentModelRequirements, createBasicAgentModelRequirements, createDefaultAgentName, createEmptyAgentModelRequirements, createLlmToolsFromConfiguration, createPipelineCollectionFromJson, createPipelineCollectionFromPromise, createPipelineCollectionFromUrl, createPipelineExecutor, createPipelineSubcollection, createStandaloneBookLanguageMarkdown, embeddingVectorToString, executionReportJsonToString, extractParameterNamesFromTask, filterModels, formatChatAttachmentContentContext, formatChatAttachmentContext, generatePlaceholderAgentProfileImageUrl, getAllCommitmentDefinitions, getAllCommitmentTypes, getAllCommitmentsToolTitles, getCommitmentDefinition, getGroupedCommitmentDefinitions, getPipelineInterface, getSingleLlmExecutionTools, identificationToPromptbookToken, isCommitmentSupported, isPassingExpectations, isPipelineImplementingInterface, isPipelineInterfacesEqual, isPipelinePrepared, isValidBook, isValidPipelineString, joinLlmExecutionTools, limitTotalUsage, makeKnowledgeSourceHandler, migratePipeline, normalizeAgentName, normalizeChatAttachments, padBook, parseAgentSource, parseParameters, parsePipeline, pipelineCollectionToJson, pipelineJsonToString, prepareKnowledgePieces, preparePersona, preparePipeline, prettifyPipelineString, promptbookFetch, promptbookTokenToIdentification, resolveChatAttachmentContents, unpreparePipeline, usageToHuman, usageToWorktime, validateBook, validatePipeline, validatePipelineString };
50846
+ export { $bookTranspilersRegister, $generateBookBoilerplate, $llmToolsMetadataRegister, $llmToolsRegister, $scrapersMetadataRegister, $scrapersRegister, ADMIN_EMAIL, ADMIN_GITHUB_NAME, API_REQUEST_TIMEOUT, AbstractFormatError, Agent, AgentCollectionInSupabase, AgentLlmExecutionTools, AgentOsTranspiler, AnthropicClaudeManagedTranspiler, AnthropicClaudeSdkTranspiler, AuthenticationError, BIG_DATASET_TRESHOLD, BOOK_LANGUAGE_VERSION, BlackholeStorage, BoilerplateError, BoilerplateFormfactorDefinition, Book, CLAIM, CLI_APP_ID, COLOR_CONSTANTS, CORE_AGENTS_SERVER_WELL_KNOWN_AGENT_NAMES, CallbackInterfaceTools, ChatbotFormfactorDefinition, CollectionError, CompletionFormfactorDefinition, ConflictError, CsvFormatError, CsvFormatParser, DEFAULT_AGENTS_DIRNAME, DEFAULT_BOOK, DEFAULT_BOOKS_DIRNAME, DEFAULT_BOOK_OUTPUT_PARAMETER_NAME, DEFAULT_BOOK_TITLE, DEFAULT_CSV_SETTINGS, DEFAULT_DOWNLOAD_CACHE_DIRNAME, DEFAULT_EXECUTION_CACHE_DIRNAME, DEFAULT_GET_PIPELINE_COLLECTION_FUNCTION_NAME, DEFAULT_INTERMEDIATE_FILES_STRATEGY, DEFAULT_IS_AUTO_INSTALLED, DEFAULT_IS_VERBOSE, DEFAULT_MAX_CONCURRENT_UPLOADS, DEFAULT_MAX_EXECUTION_ATTEMPTS, DEFAULT_MAX_FILE_SIZE, DEFAULT_MAX_KNOWLEDGE_SOURCES_SCRAPING_DEPTH, DEFAULT_MAX_KNOWLEDGE_SOURCES_SCRAPING_TOTAL, DEFAULT_MAX_PARALLEL_COUNT, DEFAULT_MAX_RECURSION, DEFAULT_MAX_REQUESTS_PER_MINUTE, DEFAULT_PIPELINE_COLLECTION_BASE_FILENAME, DEFAULT_PROMPT_TASK_TITLE, DEFAULT_REMOTE_SERVER_URL, DEFAULT_SCRAPE_CACHE_DIRNAME, DEFAULT_TASK_SIMULATED_DURATION_MS, DEFAULT_TASK_TITLE, DatabaseError, E2BTranspiler, EXPECTATION_UNITS, EnvironmentMismatchError, ExecutionReportStringOptionsDefaults, ExpectError, FAILED_VALUE_PLACEHOLDER, FORMFACTOR_DEFINITIONS, FormattedBookInMarkdownTranspiler, GENERIC_PIPELINE_INTERFACE, GeneratorFormfactorDefinition, GenericFormfactorDefinition, HTTP_STATUS_CODES, ImageGeneratorFormfactorDefinition, KnowledgeScrapeError, LIMITS, LimitReachedError, MANDATORY_CSV_SETTINGS, MAX_FILENAME_LENGTH, MODEL_ORDERS, MODEL_TRUST_LEVELS, MODEL_VARIANTS, MatcherFormfactorDefinition, MemoryStorage, MissingToolsError, MultipleLlmExecutionTools, NAME, NETWORK_LIMITS, NonTaskSectionTypes, NotAllowed, NotFoundError, NotYetImplementedCommitmentDefinition, NotYetImplementedError, ORDER_OF_PIPELINE_JSON, OpenAiAgentsTranspiler, OpenAiSdkTranspiler, PADDING_LINES, PENDING_VALUE_PLACEHOLDER, PLAYGROUND_APP_ID, PROMPTBOOK_CHAT_COLOR, PROMPTBOOK_COLOR, PROMPTBOOK_ENGINE_VERSION, PROMPTBOOK_ERRORS, PROMPTBOOK_LEGAL_ENTITY, PROMPTBOOK_LOGO_URL, PROMPTBOOK_SYNTAX_COLORS, PROMPT_PARAMETER_SELF_LEARNING_ENABLED, PUBLIC_AGENTS_SERVERS, ParseError, PipelineExecutionError, PipelineLogicError, PipelineUrlError, PrefixStorage, PromptbookFetchError, RESERVED_PARAMETER_NAMES, RemoteAgent, SET_IS_VERBOSE, SectionTypes, SheetsFormfactorDefinition, TIME_INTERVALS, TaskTypes, TextFormatParser, TranslatorFormfactorDefinition, UNCERTAIN_USAGE, UNCERTAIN_ZERO_VALUE, USER_CHAT_COLOR, UnexpectedError, VirusTotalFileSecurityChecker, WrappedError, ZERO_USAGE, ZERO_VALUE, _AgentMetadata, _AgentRegistration, _AnthropicClaudeMetadataRegistration, _AzureOpenAiMetadataRegistration, _BoilerplateScraperMetadataRegistration, _DeepseekMetadataRegistration, _DocumentScraperMetadataRegistration, _GoogleMetadataRegistration, _LegacyDocumentScraperMetadataRegistration, _MarkdownScraperMetadataRegistration, _MarkitdownScraperMetadataRegistration, _OllamaMetadataRegistration, _OpenAiAssistantMetadataRegistration, _OpenAiCompatibleMetadataRegistration, _OpenAiMetadataRegistration, _PdfScraperMetadataRegistration, _WebsiteScraperMetadataRegistration, aboutPromptbookInformation, addUsage, appendChatAttachmentContext, appendChatAttachmentContextWithContent, book, cacheLlmTools, compilePipeline, computeAgentHash, computeCosineSimilarity, countUsage, createAgentLlmExecutionTools, createAgentModelRequirements, createBasicAgentModelRequirements, createDefaultAgentName, createEmptyAgentModelRequirements, createLlmToolsFromConfiguration, createPipelineCollectionFromJson, createPipelineCollectionFromPromise, createPipelineCollectionFromUrl, createPipelineExecutor, createPipelineSubcollection, createStandaloneBookLanguageMarkdown, embeddingVectorToString, executionReportJsonToString, extractParameterNamesFromTask, filterModels, formatChatAttachmentContentContext, formatChatAttachmentContext, generatePlaceholderAgentProfileImageUrl, getAllCommitmentDefinitions, getAllCommitmentTypes, getAllCommitmentsToolTitles, getCommitmentDefinition, getGroupedCommitmentDefinitions, getPipelineInterface, getSingleLlmExecutionTools, identificationToPromptbookToken, isCommitmentSupported, isPassingExpectations, isPipelineImplementingInterface, isPipelineInterfacesEqual, isPipelinePrepared, isValidBook, isValidPipelineString, joinLlmExecutionTools, limitTotalUsage, makeKnowledgeSourceHandler, migratePipeline, normalizeAgentName, normalizeChatAttachments, padBook, parseAgentSource, parseParameters, parsePipeline, pipelineCollectionToJson, pipelineJsonToString, prepareKnowledgePieces, preparePersona, preparePipeline, prettifyPipelineString, promptbookFetch, promptbookTokenToIdentification, resolveChatAttachmentContents, unpreparePipeline, usageToHuman, usageToWorktime, validateBook, validatePipeline, validatePipelineString };
49208
50847
  //# sourceMappingURL=index.es.js.map