@promptbook/components 0.112.0-47 → 0.112.0-49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/index.es.js +1175 -167
- package/esm/index.es.js.map +1 -1
- package/esm/src/avatars/AvatarOrImage.d.ts +5 -1
- package/esm/src/avatars/avatarAnimationScheduler.d.ts +16 -0
- package/esm/src/avatars/avatarInteractionUtils.d.ts +81 -0
- package/esm/src/avatars/avatarInteractionUtils.test.d.ts +1 -0
- package/esm/src/avatars/avatarPointerTracking.d.ts +33 -0
- package/esm/src/avatars/avatarRenderingUtils.d.ts +3 -2
- package/esm/src/avatars/avatarRenderingUtils.test.d.ts +1 -0
- package/esm/src/avatars/avatarVisibilityTracking.d.ts +17 -0
- package/esm/src/avatars/index.d.ts +1 -1
- package/esm/src/avatars/renderAvatarVisual.d.ts +29 -1
- package/esm/src/avatars/types/AvatarVisualDefinition.d.ts +35 -0
- package/esm/src/avatars/visuals/octopusAvatarVisualShared.d.ts +34 -0
- package/esm/src/avatars/visuals/octopusAvatarVisualShared.test.d.ts +1 -0
- package/esm/src/book-components/Chat/Chat/ChatProps.d.ts +3 -3
- package/esm/src/book-components/Chat/Chat/TeamToolCallModalContent.test.d.ts +2 -0
- package/esm/src/commitments/USE/USE.d.ts +1 -0
- package/esm/src/commitments/USE/aggregateUseCommitmentSystemMessages.d.ts +1 -1
- package/esm/src/commitments/USE_DEEPSEARCH/USE_DEEPSEARCH.d.ts +47 -0
- package/esm/src/commitments/USE_DEEPSEARCH/USE_DEEPSEARCH.test.d.ts +1 -0
- package/esm/src/commitments/_common/createSerpSearchToolFunction.d.ts +12 -0
- package/esm/src/commitments/index.d.ts +2 -1
- package/esm/src/llm-providers/openai/OpenAiAgentKitExecutionTools.test.d.ts +1 -0
- package/esm/src/version.d.ts +1 -1
- package/package.json +1 -1
- package/umd/index.umd.js +1174 -166
- package/umd/index.umd.js.map +1 -1
- package/umd/src/avatars/AvatarOrImage.d.ts +5 -1
- package/umd/src/avatars/avatarAnimationScheduler.d.ts +16 -0
- package/umd/src/avatars/avatarInteractionUtils.d.ts +81 -0
- package/umd/src/avatars/avatarInteractionUtils.test.d.ts +1 -0
- package/umd/src/avatars/avatarPointerTracking.d.ts +33 -0
- package/umd/src/avatars/avatarRenderingUtils.d.ts +3 -2
- package/umd/src/avatars/avatarRenderingUtils.test.d.ts +1 -0
- package/umd/src/avatars/avatarVisibilityTracking.d.ts +17 -0
- package/umd/src/avatars/index.d.ts +1 -1
- package/umd/src/avatars/renderAvatarVisual.d.ts +29 -1
- package/umd/src/avatars/types/AvatarVisualDefinition.d.ts +35 -0
- package/umd/src/avatars/visuals/octopusAvatarVisualShared.d.ts +34 -0
- package/umd/src/avatars/visuals/octopusAvatarVisualShared.test.d.ts +1 -0
- package/umd/src/book-components/Chat/Chat/ChatProps.d.ts +3 -3
- package/umd/src/book-components/Chat/Chat/TeamToolCallModalContent.test.d.ts +2 -0
- package/umd/src/commitments/USE/USE.d.ts +1 -0
- package/umd/src/commitments/USE/aggregateUseCommitmentSystemMessages.d.ts +1 -1
- package/umd/src/commitments/USE_DEEPSEARCH/USE_DEEPSEARCH.d.ts +47 -0
- package/umd/src/commitments/USE_DEEPSEARCH/USE_DEEPSEARCH.test.d.ts +1 -0
- package/umd/src/commitments/_common/createSerpSearchToolFunction.d.ts +12 -0
- package/umd/src/commitments/index.d.ts +2 -1
- package/umd/src/llm-providers/openai/OpenAiAgentKitExecutionTools.test.d.ts +1 -0
- package/umd/src/version.d.ts +1 -1
package/esm/index.es.js
CHANGED
|
@@ -21,7 +21,7 @@ import { forTime } from 'waitasecond';
|
|
|
21
21
|
import sha256 from 'crypto-js/sha256';
|
|
22
22
|
import { parse, unparse } from 'papaparse';
|
|
23
23
|
import colors from 'colors';
|
|
24
|
-
import { Agent as Agent$1, setDefaultOpenAIClient, setDefaultOpenAIKey, fileSearchTool, tool, run } from '@openai/agents';
|
|
24
|
+
import { Agent as Agent$1, setDefaultOpenAIClient, setDefaultOpenAIKey, fileSearchTool, tool, run, webSearchTool } from '@openai/agents';
|
|
25
25
|
import Bottleneck from 'bottleneck';
|
|
26
26
|
import OpenAI from 'openai';
|
|
27
27
|
import QRCode from 'qrcode';
|
|
@@ -40,7 +40,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
|
|
|
40
40
|
* @generated
|
|
41
41
|
* @see https://github.com/webgptorg/promptbook
|
|
42
42
|
*/
|
|
43
|
-
const PROMPTBOOK_ENGINE_VERSION = '0.112.0-
|
|
43
|
+
const PROMPTBOOK_ENGINE_VERSION = '0.112.0-49';
|
|
44
44
|
/**
|
|
45
45
|
* TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
|
|
46
46
|
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
@@ -1402,11 +1402,12 @@ function createAvatarDefinitionFromAgentBasicInformation(agentBasicInformation)
|
|
|
1402
1402
|
* Creates the shared derived palette used by every avatar visual.
|
|
1403
1403
|
*
|
|
1404
1404
|
* @param avatarDefinition Stable avatar definition.
|
|
1405
|
+
* @param surface Surface style used by the parent UI.
|
|
1405
1406
|
* @returns Derived palette.
|
|
1406
1407
|
*
|
|
1407
1408
|
* @private utility of the avatar rendering system
|
|
1408
1409
|
*/
|
|
1409
|
-
function createAvatarPalette(avatarDefinition) {
|
|
1410
|
+
function createAvatarPalette(avatarDefinition, surface = 'framed') {
|
|
1410
1411
|
const normalizedAvatarDefinition = normalizeAvatarDefinition(avatarDefinition);
|
|
1411
1412
|
const primaryColor = Color.fromSafe(normalizedAvatarDefinition.colors[0] || PROMPTBOOK_COLOR);
|
|
1412
1413
|
const secondaryColor = Color.fromSafe(normalizedAvatarDefinition.colors[1] || primaryColor.then(lighten(0.12)).then(saturate(0.16)));
|
|
@@ -1416,8 +1417,8 @@ function createAvatarPalette(avatarDefinition) {
|
|
|
1416
1417
|
const highlightColor = Color.fromSafe(accentColor.then(lighten(0.22)).then(saturate(0.08)));
|
|
1417
1418
|
const shadowColor = Color.fromSafe(primaryColor.then(darken(0.46)).then(saturate(0.14)));
|
|
1418
1419
|
return {
|
|
1419
|
-
background: backgroundColor.toHex(),
|
|
1420
|
-
backgroundSecondary: backgroundSecondaryColor.toHex(),
|
|
1420
|
+
background: surface === 'transparent' ? 'transparent' : backgroundColor.toHex(),
|
|
1421
|
+
backgroundSecondary: surface === 'transparent' ? 'transparent' : backgroundSecondaryColor.toHex(),
|
|
1421
1422
|
primary: primaryColor.toHex(),
|
|
1422
1423
|
secondary: secondaryColor.toHex(),
|
|
1423
1424
|
accent: accentColor.toHex(),
|
|
@@ -1436,6 +1437,9 @@ function createAvatarPalette(avatarDefinition) {
|
|
|
1436
1437
|
* @private utility of the avatar rendering system
|
|
1437
1438
|
*/
|
|
1438
1439
|
function drawAvatarFrame(context, size, palette) {
|
|
1440
|
+
if (palette.background === 'transparent' && palette.backgroundSecondary === 'transparent') {
|
|
1441
|
+
return;
|
|
1442
|
+
}
|
|
1439
1443
|
const gradient = context.createLinearGradient(0, 0, size, size);
|
|
1440
1444
|
gradient.addColorStop(0, palette.background);
|
|
1441
1445
|
gradient.addColorStop(1, palette.backgroundSecondary);
|
|
@@ -1515,10 +1519,22 @@ function createAvatarRandomFactory(avatarDefinition) {
|
|
|
1515
1519
|
*/
|
|
1516
1520
|
function prepareAvatarCanvas(canvas, context, size, devicePixelRatio) {
|
|
1517
1521
|
const normalizedDevicePixelRatio = Math.max(1, Math.round(devicePixelRatio * 100) / 100);
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
+
const nextCanvasWidth = Math.round(size * normalizedDevicePixelRatio);
|
|
1523
|
+
const nextCanvasHeight = Math.round(size * normalizedDevicePixelRatio);
|
|
1524
|
+
const nextCanvasStyleWidth = `${size}px`;
|
|
1525
|
+
const nextCanvasStyleHeight = `${size}px`;
|
|
1526
|
+
if (canvas.width !== nextCanvasWidth) {
|
|
1527
|
+
canvas.width = nextCanvasWidth;
|
|
1528
|
+
}
|
|
1529
|
+
if (canvas.height !== nextCanvasHeight) {
|
|
1530
|
+
canvas.height = nextCanvasHeight;
|
|
1531
|
+
}
|
|
1532
|
+
if (canvas.style.width !== nextCanvasStyleWidth) {
|
|
1533
|
+
canvas.style.width = nextCanvasStyleWidth;
|
|
1534
|
+
}
|
|
1535
|
+
if (canvas.style.height !== nextCanvasStyleHeight) {
|
|
1536
|
+
canvas.style.height = nextCanvasStyleHeight;
|
|
1537
|
+
}
|
|
1522
1538
|
context.setTransform(normalizedDevicePixelRatio, 0, 0, normalizedDevicePixelRatio, 0, 0);
|
|
1523
1539
|
context.clearRect(0, 0, size, size);
|
|
1524
1540
|
}
|
|
@@ -1587,7 +1603,7 @@ function generatePlaceholderAgentProfileImageUrl(agentIdOrName, agentsServerUrl)
|
|
|
1587
1603
|
*
|
|
1588
1604
|
* @private shared avatar contract
|
|
1589
1605
|
*/
|
|
1590
|
-
const DEFAULT_AGENT_AVATAR_VISUAL_ID = '
|
|
1606
|
+
const DEFAULT_AGENT_AVATAR_VISUAL_ID = 'octopus3';
|
|
1591
1607
|
/**
|
|
1592
1608
|
* Resolve a base URL for relative images, preferring the provided base or browser location.
|
|
1593
1609
|
*
|
|
@@ -11732,6 +11748,7 @@ class TemplateCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
11732
11748
|
* Supported USE types:
|
|
11733
11749
|
* - USE BROWSER: Enables the agent to use a web browser tool
|
|
11734
11750
|
* - USE SEARCH ENGINE (future): Enables search engine access
|
|
11751
|
+
* - USE DEEPSEARCH: Enables deeper research-oriented search access
|
|
11735
11752
|
* - USE FILE SYSTEM (future): Enables file system operations
|
|
11736
11753
|
* - USE MCP (future): Enables MCP server connections
|
|
11737
11754
|
*
|
|
@@ -11754,7 +11771,7 @@ class UseCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
11754
11771
|
* Short one-line description of USE commitments.
|
|
11755
11772
|
*/
|
|
11756
11773
|
get description() {
|
|
11757
|
-
return 'Enable the agent to use specific tools or capabilities (BROWSER, SEARCH ENGINE, etc.).';
|
|
11774
|
+
return 'Enable the agent to use specific tools or capabilities (BROWSER, SEARCH ENGINE, DEEPSEARCH, etc.).';
|
|
11758
11775
|
}
|
|
11759
11776
|
/**
|
|
11760
11777
|
* Icon for this commitment.
|
|
@@ -11775,6 +11792,7 @@ class UseCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
11775
11792
|
|
|
11776
11793
|
- **USE BROWSER** - Enables the agent to use a web browser tool to access and retrieve information from the internet
|
|
11777
11794
|
- **USE SEARCH ENGINE** (future) - Enables search engine access
|
|
11795
|
+
- **USE DEEPSEARCH** - Enables deeper research-oriented search access
|
|
11778
11796
|
- **USE FILE SYSTEM** (future) - Enables file system operations
|
|
11779
11797
|
- **USE MCP** (future) - Enables MCP server connections
|
|
11780
11798
|
|
|
@@ -11829,7 +11847,7 @@ class UseCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
11829
11847
|
* Checks if this is a known USE type
|
|
11830
11848
|
*/
|
|
11831
11849
|
isKnownUseType(useType) {
|
|
11832
|
-
const knownTypes = ['BROWSER', 'SEARCH ENGINE', 'FILE SYSTEM', 'MCP'];
|
|
11850
|
+
const knownTypes = ['BROWSER', 'SEARCH ENGINE', 'DEEPSEARCH', 'FILE SYSTEM', 'MCP'];
|
|
11833
11851
|
return knownTypes.includes(useType.toUpperCase());
|
|
11834
11852
|
}
|
|
11835
11853
|
}
|
|
@@ -11842,6 +11860,7 @@ class UseCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
11842
11860
|
*/
|
|
11843
11861
|
const AGGREGATED_USE_COMMITMENT_TYPES = [
|
|
11844
11862
|
'USE BROWSER',
|
|
11863
|
+
'USE DEEPSEARCH',
|
|
11845
11864
|
'USE SEARCH ENGINE',
|
|
11846
11865
|
'USE TIME',
|
|
11847
11866
|
];
|
|
@@ -11921,6 +11940,15 @@ function createAggregatedUseCommitmentSystemMessage(type, additionalInstructions
|
|
|
11921
11940
|
- Do not tell the user you cannot search for information, YOU CAN.
|
|
11922
11941
|
${block(formatOptionalInstructionBlock('Search instructions', combinedAdditionalInstructions))}
|
|
11923
11942
|
`);
|
|
11943
|
+
case 'USE DEEPSEARCH':
|
|
11944
|
+
return spaceTrim$1((block) => `
|
|
11945
|
+
Tool:
|
|
11946
|
+
- You have access to DeepSearch via the tool "deep_search".
|
|
11947
|
+
- Use it for broader research tasks that need multi-step investigation, comparison, or synthesis across multiple sources.
|
|
11948
|
+
- Prefer it over quick search when the user asks for a well-grounded brief, report, or deeper investigation.
|
|
11949
|
+
- Do not pretend you cannot research current information when this tool is available.
|
|
11950
|
+
${block(formatOptionalInstructionBlock('DeepSearch instructions', combinedAdditionalInstructions))}
|
|
11951
|
+
`);
|
|
11924
11952
|
}
|
|
11925
11953
|
}
|
|
11926
11954
|
/**
|
|
@@ -13470,6 +13498,207 @@ function addConfiguredCalendarIfMissing(configuredCalendars, calendarReference)
|
|
|
13470
13498
|
}
|
|
13471
13499
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
13472
13500
|
|
|
13501
|
+
/**
|
|
13502
|
+
* A search engine implementation that uses the SerpApi to fetch Google search results.
|
|
13503
|
+
*
|
|
13504
|
+
* @private <- TODO: !!!! Export via some package
|
|
13505
|
+
*/
|
|
13506
|
+
class SerpSearchEngine {
|
|
13507
|
+
get title() {
|
|
13508
|
+
return 'SerpApi Search Engine';
|
|
13509
|
+
}
|
|
13510
|
+
get description() {
|
|
13511
|
+
return 'Search engine that uses SerpApi to fetch Google search results';
|
|
13512
|
+
}
|
|
13513
|
+
checkConfiguration() {
|
|
13514
|
+
if (!process.env.SERP_API_KEY) {
|
|
13515
|
+
throw new Error('SERP_API_KEY is not configured');
|
|
13516
|
+
}
|
|
13517
|
+
}
|
|
13518
|
+
async search(query, options = {}) {
|
|
13519
|
+
const apiKey = process.env.SERP_API_KEY;
|
|
13520
|
+
if (!apiKey) {
|
|
13521
|
+
throw new Error('SERP_API_KEY is not configured');
|
|
13522
|
+
}
|
|
13523
|
+
const url = new URL('https://serpapi.com/search');
|
|
13524
|
+
url.searchParams.set('api_key', apiKey);
|
|
13525
|
+
url.searchParams.set('engine', 'google');
|
|
13526
|
+
url.searchParams.set('q', query);
|
|
13527
|
+
for (const [key, value] of Object.entries(options)) {
|
|
13528
|
+
url.searchParams.set(key, String(value));
|
|
13529
|
+
}
|
|
13530
|
+
const response = await fetch(url.toString());
|
|
13531
|
+
if (!response.ok) {
|
|
13532
|
+
const body = await response.text();
|
|
13533
|
+
throw new Error(`SerpApi failed with status ${response.status}: ${response.statusText}\n${body}`);
|
|
13534
|
+
}
|
|
13535
|
+
const data = (await response.json());
|
|
13536
|
+
return (data.organic_results || []).map((item) => ({
|
|
13537
|
+
title: item.title,
|
|
13538
|
+
url: item.link,
|
|
13539
|
+
snippet: item.snippet || '',
|
|
13540
|
+
}));
|
|
13541
|
+
}
|
|
13542
|
+
}
|
|
13543
|
+
|
|
13544
|
+
/**
|
|
13545
|
+
* Creates one SERP-backed tool function used as a local fallback for search-like commitments.
|
|
13546
|
+
*
|
|
13547
|
+
* @param toolName - Technical tool name used for validation messages.
|
|
13548
|
+
* @param resultLabel - Human-readable label used in formatted results.
|
|
13549
|
+
* @returns Async tool function compatible with commitment tool registration.
|
|
13550
|
+
*
|
|
13551
|
+
* @private internal helper for search-like commitments
|
|
13552
|
+
*/
|
|
13553
|
+
function createSerpSearchToolFunction(toolName, resultLabel) {
|
|
13554
|
+
return async (rawArgs) => {
|
|
13555
|
+
const { query, ...searchOptions } = rawArgs;
|
|
13556
|
+
if (typeof query !== 'string' || !query.trim()) {
|
|
13557
|
+
throw new Error(`${toolName} query is required`);
|
|
13558
|
+
}
|
|
13559
|
+
const searchEngine = new SerpSearchEngine();
|
|
13560
|
+
const results = await searchEngine.search(query, searchOptions);
|
|
13561
|
+
return spaceTrim$1((block) => `
|
|
13562
|
+
${resultLabel} results for "${query}"${Object.keys(searchOptions).length === 0
|
|
13563
|
+
? ''
|
|
13564
|
+
: ` with options ${JSON.stringify(searchOptions)}`}:
|
|
13565
|
+
|
|
13566
|
+
${block(results
|
|
13567
|
+
.map((result) => spaceTrim$1(`
|
|
13568
|
+
- **${result.title}**
|
|
13569
|
+
${result.url}
|
|
13570
|
+
${result.snippet}
|
|
13571
|
+
`))
|
|
13572
|
+
.join('\n\n'))}
|
|
13573
|
+
`);
|
|
13574
|
+
};
|
|
13575
|
+
}
|
|
13576
|
+
|
|
13577
|
+
/**
|
|
13578
|
+
* USE DEEPSEARCH commitment definition
|
|
13579
|
+
*
|
|
13580
|
+
* The `USE DEEPSEARCH` commitment indicates that the agent should use a deeper research-oriented
|
|
13581
|
+
* search workflow instead of lightweight web search when it needs fresh information from the internet.
|
|
13582
|
+
*
|
|
13583
|
+
* The content following `USE DEEPSEARCH` is an arbitrary text that the agent should know
|
|
13584
|
+
* (e.g. search scope or research instructions).
|
|
13585
|
+
*
|
|
13586
|
+
* Example usage in agent source:
|
|
13587
|
+
*
|
|
13588
|
+
* ```book
|
|
13589
|
+
* USE DEEPSEARCH
|
|
13590
|
+
* USE DEEPSEARCH Compare official vendor documentation with independent benchmarks.
|
|
13591
|
+
* ```
|
|
13592
|
+
*
|
|
13593
|
+
* @private [🪔] Maybe export the commitments through some package
|
|
13594
|
+
*/
|
|
13595
|
+
class UseDeepSearchCommitmentDefinition extends BaseCommitmentDefinition {
|
|
13596
|
+
constructor() {
|
|
13597
|
+
super('USE DEEPSEARCH');
|
|
13598
|
+
}
|
|
13599
|
+
get requiresContent() {
|
|
13600
|
+
return false;
|
|
13601
|
+
}
|
|
13602
|
+
/**
|
|
13603
|
+
* Short one-line description of USE DEEPSEARCH.
|
|
13604
|
+
*/
|
|
13605
|
+
get description() {
|
|
13606
|
+
return 'Enable the agent to use DeepSearch for more thorough internet research.';
|
|
13607
|
+
}
|
|
13608
|
+
/**
|
|
13609
|
+
* Icon for this commitment.
|
|
13610
|
+
*/
|
|
13611
|
+
get icon() {
|
|
13612
|
+
return '🔬';
|
|
13613
|
+
}
|
|
13614
|
+
/**
|
|
13615
|
+
* Markdown documentation for USE DEEPSEARCH commitment.
|
|
13616
|
+
*/
|
|
13617
|
+
get documentation() {
|
|
13618
|
+
return spaceTrim$1(`
|
|
13619
|
+
# USE DEEPSEARCH
|
|
13620
|
+
|
|
13621
|
+
Enables the agent to use DeepSearch for broader, more thorough internet research than lightweight web search.
|
|
13622
|
+
|
|
13623
|
+
## Key aspects
|
|
13624
|
+
|
|
13625
|
+
- The content following \`USE DEEPSEARCH\` is arbitrary guidance for the research workflow.
|
|
13626
|
+
- In Agents Server, the OpenAI Agents SDK runtime uses a nested deep-research agent for this tool.
|
|
13627
|
+
- Use this for investigations, comparisons, market scans, or other tasks that benefit from deeper synthesis.
|
|
13628
|
+
- Prefer regular \`USE SEARCH ENGINE\` when a quick factual lookup is enough.
|
|
13629
|
+
|
|
13630
|
+
## Examples
|
|
13631
|
+
|
|
13632
|
+
\`\`\`book
|
|
13633
|
+
Due Diligence Researcher
|
|
13634
|
+
|
|
13635
|
+
GOAL Investigate vendors thoroughly before making recommendations.
|
|
13636
|
+
USE DEEPSEARCH Compare official sources with credible third-party analysis.
|
|
13637
|
+
RULE Cite the strongest supporting sources in the final answer.
|
|
13638
|
+
\`\`\`
|
|
13639
|
+
|
|
13640
|
+
\`\`\`book
|
|
13641
|
+
Market Analyst
|
|
13642
|
+
|
|
13643
|
+
GOAL Build concise but well-grounded research briefs.
|
|
13644
|
+
USE DEEPSEARCH Focus on recent public information and competing viewpoints.
|
|
13645
|
+
CLOSED
|
|
13646
|
+
\`\`\`
|
|
13647
|
+
`);
|
|
13648
|
+
}
|
|
13649
|
+
applyToAgentModelRequirements(requirements, content) {
|
|
13650
|
+
const existingTools = requirements.tools || [];
|
|
13651
|
+
const updatedTools = existingTools.some((tool) => tool.name === 'deep_search')
|
|
13652
|
+
? existingTools
|
|
13653
|
+
: [
|
|
13654
|
+
...existingTools,
|
|
13655
|
+
{
|
|
13656
|
+
name: 'deep_search',
|
|
13657
|
+
description: spaceTrim$1(`
|
|
13658
|
+
Research the internet deeply and synthesize a grounded answer.
|
|
13659
|
+
Use this tool for broader investigations, comparisons, and requests that need more than a quick search.
|
|
13660
|
+
`),
|
|
13661
|
+
parameters: {
|
|
13662
|
+
type: 'object',
|
|
13663
|
+
properties: {
|
|
13664
|
+
query: {
|
|
13665
|
+
type: 'string',
|
|
13666
|
+
description: 'The research question or investigation request.',
|
|
13667
|
+
},
|
|
13668
|
+
},
|
|
13669
|
+
required: ['query'],
|
|
13670
|
+
additionalProperties: false,
|
|
13671
|
+
},
|
|
13672
|
+
},
|
|
13673
|
+
];
|
|
13674
|
+
return appendAggregatedUseCommitmentPlaceholder({
|
|
13675
|
+
...requirements,
|
|
13676
|
+
tools: updatedTools,
|
|
13677
|
+
_metadata: {
|
|
13678
|
+
...requirements._metadata,
|
|
13679
|
+
useDeepSearch: content || true,
|
|
13680
|
+
},
|
|
13681
|
+
}, this.type);
|
|
13682
|
+
}
|
|
13683
|
+
/**
|
|
13684
|
+
* Gets human-readable titles for tool functions provided by this commitment.
|
|
13685
|
+
*/
|
|
13686
|
+
getToolTitles() {
|
|
13687
|
+
return {
|
|
13688
|
+
deep_search: 'DeepSearch',
|
|
13689
|
+
};
|
|
13690
|
+
}
|
|
13691
|
+
/**
|
|
13692
|
+
* Gets the local fallback implementation for the `deep_search` tool.
|
|
13693
|
+
*/
|
|
13694
|
+
getToolFunctions() {
|
|
13695
|
+
return {
|
|
13696
|
+
deep_search: createSerpSearchToolFunction('deep_search', 'DeepSearch'),
|
|
13697
|
+
};
|
|
13698
|
+
}
|
|
13699
|
+
}
|
|
13700
|
+
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
13701
|
+
|
|
13473
13702
|
/**
|
|
13474
13703
|
* Lightweight email token matcher used for `USE EMAIL` first-line parsing.
|
|
13475
13704
|
*
|
|
@@ -15701,49 +15930,6 @@ function addConfiguredProjectIfMissing(configuredProjects, repositoryReference)
|
|
|
15701
15930
|
}
|
|
15702
15931
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
15703
15932
|
|
|
15704
|
-
/**
|
|
15705
|
-
* A search engine implementation that uses the SerpApi to fetch Google search results.
|
|
15706
|
-
*
|
|
15707
|
-
* @private <- TODO: !!!! Export via some package
|
|
15708
|
-
*/
|
|
15709
|
-
class SerpSearchEngine {
|
|
15710
|
-
get title() {
|
|
15711
|
-
return 'SerpApi Search Engine';
|
|
15712
|
-
}
|
|
15713
|
-
get description() {
|
|
15714
|
-
return 'Search engine that uses SerpApi to fetch Google search results';
|
|
15715
|
-
}
|
|
15716
|
-
checkConfiguration() {
|
|
15717
|
-
if (!process.env.SERP_API_KEY) {
|
|
15718
|
-
throw new Error('SERP_API_KEY is not configured');
|
|
15719
|
-
}
|
|
15720
|
-
}
|
|
15721
|
-
async search(query, options = {}) {
|
|
15722
|
-
const apiKey = process.env.SERP_API_KEY;
|
|
15723
|
-
if (!apiKey) {
|
|
15724
|
-
throw new Error('SERP_API_KEY is not configured');
|
|
15725
|
-
}
|
|
15726
|
-
const url = new URL('https://serpapi.com/search');
|
|
15727
|
-
url.searchParams.set('api_key', apiKey);
|
|
15728
|
-
url.searchParams.set('engine', 'google');
|
|
15729
|
-
url.searchParams.set('q', query);
|
|
15730
|
-
for (const [key, value] of Object.entries(options)) {
|
|
15731
|
-
url.searchParams.set(key, String(value));
|
|
15732
|
-
}
|
|
15733
|
-
const response = await fetch(url.toString());
|
|
15734
|
-
if (!response.ok) {
|
|
15735
|
-
const body = await response.text();
|
|
15736
|
-
throw new Error(`SerpApi failed with status ${response.status}: ${response.statusText}\n${body}`);
|
|
15737
|
-
}
|
|
15738
|
-
const data = (await response.json());
|
|
15739
|
-
return (data.organic_results || []).map((item) => ({
|
|
15740
|
-
title: item.title,
|
|
15741
|
-
url: item.link,
|
|
15742
|
-
snippet: item.snippet || '',
|
|
15743
|
-
}));
|
|
15744
|
-
}
|
|
15745
|
-
}
|
|
15746
|
-
|
|
15747
15933
|
/**
|
|
15748
15934
|
* USE SEARCH ENGINE commitment definition
|
|
15749
15935
|
*
|
|
@@ -15887,26 +16073,7 @@ class UseSearchEngineCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
15887
16073
|
*/
|
|
15888
16074
|
getToolFunctions() {
|
|
15889
16075
|
return {
|
|
15890
|
-
|
|
15891
|
-
console.log('!!!! [Tool] web_search called', { args });
|
|
15892
|
-
const { query, ...options } = args;
|
|
15893
|
-
if (!query) {
|
|
15894
|
-
throw new Error('Search query is required');
|
|
15895
|
-
}
|
|
15896
|
-
const searchEngine = new SerpSearchEngine();
|
|
15897
|
-
const results = await searchEngine.search(query, options);
|
|
15898
|
-
return spaceTrim$1((block) => `
|
|
15899
|
-
Search results for "${query}"${Object.keys(options).length === 0 ? '' : ` with options ${JSON.stringify(options)}`}:
|
|
15900
|
-
|
|
15901
|
-
${block(results
|
|
15902
|
-
.map((result) => spaceTrim$1(`
|
|
15903
|
-
- **${result.title}**
|
|
15904
|
-
${result.url}
|
|
15905
|
-
${result.snippet}
|
|
15906
|
-
`))
|
|
15907
|
-
.join('\n\n'))}
|
|
15908
|
-
`);
|
|
15909
|
-
},
|
|
16076
|
+
web_search: createSerpSearchToolFunction('web_search', 'Search'),
|
|
15910
16077
|
};
|
|
15911
16078
|
}
|
|
15912
16079
|
}
|
|
@@ -17552,6 +17719,7 @@ const COMMITMENT_REGISTRY = [
|
|
|
17552
17719
|
new ClosedCommitmentDefinition(),
|
|
17553
17720
|
new TeamCommitmentDefinition(),
|
|
17554
17721
|
new UseBrowserCommitmentDefinition(),
|
|
17722
|
+
new UseDeepSearchCommitmentDefinition(),
|
|
17555
17723
|
new UseSearchEngineCommitmentDefinition(),
|
|
17556
17724
|
new UseSpawnCommitmentDefinition(),
|
|
17557
17725
|
new UseTimeoutCommitmentDefinition(),
|
|
@@ -17863,6 +18031,11 @@ const SIMPLE_CAPABILITY_BY_COMMITMENT_TYPE = {
|
|
|
17863
18031
|
label: 'Internet',
|
|
17864
18032
|
iconName: 'Search',
|
|
17865
18033
|
},
|
|
18034
|
+
'USE DEEPSEARCH': {
|
|
18035
|
+
type: 'search-engine',
|
|
18036
|
+
label: 'DeepSearch',
|
|
18037
|
+
iconName: 'Search',
|
|
18038
|
+
},
|
|
17866
18039
|
'USE TIME': {
|
|
17867
18040
|
type: 'time',
|
|
17868
18041
|
label: 'Time',
|
|
@@ -25738,6 +25911,496 @@ function ChatInputArea(props) {
|
|
|
25738
25911
|
}), children: jsx(SendIcon, { size: 25 }) })] }), speechRecognition && (jsx(ChatInputAreaDictationPanel, { bubbleText: speechRecognitionUiDescriptor.bubbleText, bubbleTone: speechRecognitionUiDescriptor.bubbleTone, shouldShowPanel: shouldShowDictationPanel, isExpanded: isDictationPanelExpanded, interimText: dictationInterimText, error: dictationError, lastFinalChunk: dictationLastFinalChunk, editableChunk: dictationEditableChunk, canBacktrack: canBacktrack, dictationSettings: dictationSettings, isBrowserSpeechFallbackSupported: isBrowserSpeechFallbackSupported, canOpenBrowserSettings: canOpenBrowserSettings, onToggleExpanded: toggleDictationPanel, onExpand: expandDictationPanel, onEditableChunkChange: setDictationEditableChunk, onRetryPermissionRequest: handleRetryPermissionRequest, onOpenBrowserSettings: handleOpenBrowserSettings, onApplyCorrection: handleApplyCorrection, onBacktrackLastChunk: handleBacktrackLastChunk, onDictationSettingChange: handleDictationSettingChange })), isUploading && (jsxs("div", { className: styles$5.uploadProgress, children: [jsx("div", { className: styles$5.uploadProgressBar, children: jsx("div", { className: styles$5.uploadProgressFill }) }), jsx("span", { children: "Uploading files..." })] })), isDragOver && onFileUpload && (jsx("div", { className: styles$5.dragOverlay, children: jsxs("div", { className: styles$5.dragOverlayContent, children: [jsx(AttachmentIcon, { size: 48 }), jsx("span", { children: "Drop files here to upload" })] }) }))] }));
|
|
25739
25912
|
}
|
|
25740
25913
|
|
|
25914
|
+
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
25915
|
+
/**
|
|
25916
|
+
* Maximum normalized eye travel used when the viewer moves across the viewport.
|
|
25917
|
+
*
|
|
25918
|
+
* @private utility of the avatar rendering system
|
|
25919
|
+
*/
|
|
25920
|
+
const MAX_GAZE_OFFSET = 0.78;
|
|
25921
|
+
/**
|
|
25922
|
+
* Maximum normalized body lean used for subtle mantle response.
|
|
25923
|
+
*
|
|
25924
|
+
* @private utility of the avatar rendering system
|
|
25925
|
+
*/
|
|
25926
|
+
const MAX_BODY_OFFSET = 0.28;
|
|
25927
|
+
/**
|
|
25928
|
+
* Smoothing window used while a live pointer or touch target is active.
|
|
25929
|
+
*
|
|
25930
|
+
* @private utility of the avatar rendering system
|
|
25931
|
+
*/
|
|
25932
|
+
const ACTIVE_INTERACTION_SMOOTHING_MS = 90;
|
|
25933
|
+
/**
|
|
25934
|
+
* Slower smoothing window used when easing the avatar back to its idle state.
|
|
25935
|
+
*
|
|
25936
|
+
* @private utility of the avatar rendering system
|
|
25937
|
+
*/
|
|
25938
|
+
const IDLE_INTERACTION_SMOOTHING_MS = 230;
|
|
25939
|
+
/**
|
|
25940
|
+
* Maximum frame delta allowed when smoothing interaction after tab stalls.
|
|
25941
|
+
*
|
|
25942
|
+
* @private utility of the avatar rendering system
|
|
25943
|
+
*/
|
|
25944
|
+
const MAX_INTERACTION_FRAME_DELTA_MS = 64;
|
|
25945
|
+
/**
|
|
25946
|
+
* Extra damping used for the slower body lean compared with the quicker eye motion.
|
|
25947
|
+
*
|
|
25948
|
+
* @private utility of the avatar rendering system
|
|
25949
|
+
*/
|
|
25950
|
+
const BODY_INTERACTION_SMOOTHING_MULTIPLIER = 1.2;
|
|
25951
|
+
/**
|
|
25952
|
+
* Stable zeroed interaction state used by non-interactive render paths.
|
|
25953
|
+
*
|
|
25954
|
+
* @private utility of the avatar rendering system
|
|
25955
|
+
*/
|
|
25956
|
+
const IDLE_AVATAR_INTERACTION_STATE = {
|
|
25957
|
+
gazeX: 0,
|
|
25958
|
+
gazeY: 0,
|
|
25959
|
+
bodyOffsetX: 0,
|
|
25960
|
+
bodyOffsetY: 0,
|
|
25961
|
+
intensity: 0,
|
|
25962
|
+
isPointerActive: false,
|
|
25963
|
+
pointerType: 'idle',
|
|
25964
|
+
};
|
|
25965
|
+
/**
|
|
25966
|
+
* Creates one stable cache key from the meaningful avatar-definition fields.
|
|
25967
|
+
*
|
|
25968
|
+
* @param avatarDefinition Normalized or raw avatar definition.
|
|
25969
|
+
* @returns Stable cache key that ignores object identity churn.
|
|
25970
|
+
*
|
|
25971
|
+
* @private utility of the avatar rendering system
|
|
25972
|
+
*/
|
|
25973
|
+
function createAvatarDefinitionKey(avatarDefinition) {
|
|
25974
|
+
const normalizedAvatarDefinition = normalizeAvatarDefinition(avatarDefinition);
|
|
25975
|
+
return [
|
|
25976
|
+
normalizedAvatarDefinition.agentName,
|
|
25977
|
+
normalizedAvatarDefinition.agentHash,
|
|
25978
|
+
normalizedAvatarDefinition.colors.join('|'),
|
|
25979
|
+
].join('::');
|
|
25980
|
+
}
|
|
25981
|
+
/**
|
|
25982
|
+
* Returns the neutral interaction state used by static/server-side renders.
|
|
25983
|
+
*
|
|
25984
|
+
* @returns Zeroed interaction state.
|
|
25985
|
+
*
|
|
25986
|
+
* @private utility of the avatar rendering system
|
|
25987
|
+
*/
|
|
25988
|
+
function createIdleAvatarInteractionState() {
|
|
25989
|
+
return IDLE_AVATAR_INTERACTION_STATE;
|
|
25990
|
+
}
|
|
25991
|
+
/**
|
|
25992
|
+
* Creates a fresh runtime state for the interactive animation loop.
|
|
25993
|
+
*
|
|
25994
|
+
* @returns Runtime interaction state with neutral values.
|
|
25995
|
+
*
|
|
25996
|
+
* @private utility of the avatar rendering system
|
|
25997
|
+
*/
|
|
25998
|
+
function createAvatarInteractionRuntimeState() {
|
|
25999
|
+
return {
|
|
26000
|
+
...IDLE_AVATAR_INTERACTION_STATE,
|
|
26001
|
+
lastFrameMs: null,
|
|
26002
|
+
};
|
|
26003
|
+
}
|
|
26004
|
+
/**
|
|
26005
|
+
* Converts the shared viewport pointer state into one avatar-local gaze target.
|
|
26006
|
+
*
|
|
26007
|
+
* @param avatarBounds Canvas bounds in viewport coordinates.
|
|
26008
|
+
* @param pointerSnapshot Latest shared pointer sample.
|
|
26009
|
+
* @returns Local target used to steer eyes and subtle body lean.
|
|
26010
|
+
*
|
|
26011
|
+
* @private utility of the avatar rendering system
|
|
26012
|
+
*/
|
|
26013
|
+
function resolveAvatarPointerTarget(avatarBounds, pointerSnapshot) {
|
|
26014
|
+
if (!pointerSnapshot || !pointerSnapshot.isPointerActive) {
|
|
26015
|
+
return {
|
|
26016
|
+
...IDLE_AVATAR_INTERACTION_STATE,
|
|
26017
|
+
};
|
|
26018
|
+
}
|
|
26019
|
+
const centerX = avatarBounds.left + avatarBounds.width / 2;
|
|
26020
|
+
const centerY = avatarBounds.top + avatarBounds.height / 2;
|
|
26021
|
+
const normalizedX = (pointerSnapshot.clientX - centerX) / Math.max(avatarBounds.width / 2, 1);
|
|
26022
|
+
const normalizedY = (pointerSnapshot.clientY - centerY) / Math.max(avatarBounds.height / 2, 1);
|
|
26023
|
+
const normalizedLength = Math.hypot(normalizedX, normalizedY) || 1;
|
|
26024
|
+
const clampedLength = Math.min(1, normalizedLength);
|
|
26025
|
+
const targetX = (normalizedX / normalizedLength) * clampedLength;
|
|
26026
|
+
const targetY = (normalizedY / normalizedLength) * clampedLength;
|
|
26027
|
+
const intensity = clamp01$1(clampedLength);
|
|
26028
|
+
return {
|
|
26029
|
+
gazeX: targetX * MAX_GAZE_OFFSET,
|
|
26030
|
+
gazeY: targetY * MAX_GAZE_OFFSET,
|
|
26031
|
+
bodyOffsetX: targetX * MAX_BODY_OFFSET,
|
|
26032
|
+
bodyOffsetY: targetY * MAX_BODY_OFFSET,
|
|
26033
|
+
intensity,
|
|
26034
|
+
isPointerActive: true,
|
|
26035
|
+
pointerType: pointerSnapshot.pointerType,
|
|
26036
|
+
};
|
|
26037
|
+
}
|
|
26038
|
+
/**
|
|
26039
|
+
* Advances the smoothed interaction state toward the latest pointer target.
|
|
26040
|
+
*
|
|
26041
|
+
* @param runtimeState Previous animation-frame state.
|
|
26042
|
+
* @param pointerTarget Latest local pointer target.
|
|
26043
|
+
* @param nowMs Current animation-frame timestamp.
|
|
26044
|
+
* @returns Next runtime state to keep in the animation loop.
|
|
26045
|
+
*
|
|
26046
|
+
* @private utility of the avatar rendering system
|
|
26047
|
+
*/
|
|
26048
|
+
function stepAvatarInteractionRuntimeState(runtimeState, pointerTarget, nowMs) {
|
|
26049
|
+
const deltaMs = runtimeState.lastFrameMs === null
|
|
26050
|
+
? 16
|
|
26051
|
+
: Math.min(MAX_INTERACTION_FRAME_DELTA_MS, Math.max(8, nowMs - runtimeState.lastFrameMs));
|
|
26052
|
+
const smoothingWindowMs = pointerTarget.isPointerActive
|
|
26053
|
+
? ACTIVE_INTERACTION_SMOOTHING_MS
|
|
26054
|
+
: IDLE_INTERACTION_SMOOTHING_MS;
|
|
26055
|
+
// This exponential interpolation keeps the gaze response smooth regardless of fluctuating frame rates.
|
|
26056
|
+
return {
|
|
26057
|
+
gazeX: interpolateExponentially(runtimeState.gazeX, pointerTarget.gazeX, deltaMs, smoothingWindowMs),
|
|
26058
|
+
gazeY: interpolateExponentially(runtimeState.gazeY, pointerTarget.gazeY, deltaMs, smoothingWindowMs),
|
|
26059
|
+
bodyOffsetX: interpolateExponentially(runtimeState.bodyOffsetX, pointerTarget.bodyOffsetX, deltaMs, smoothingWindowMs * BODY_INTERACTION_SMOOTHING_MULTIPLIER),
|
|
26060
|
+
bodyOffsetY: interpolateExponentially(runtimeState.bodyOffsetY, pointerTarget.bodyOffsetY, deltaMs, smoothingWindowMs * BODY_INTERACTION_SMOOTHING_MULTIPLIER),
|
|
26061
|
+
intensity: interpolateExponentially(runtimeState.intensity, pointerTarget.intensity, deltaMs, smoothingWindowMs),
|
|
26062
|
+
isPointerActive: pointerTarget.isPointerActive,
|
|
26063
|
+
pointerType: pointerTarget.pointerType,
|
|
26064
|
+
lastFrameMs: nowMs,
|
|
26065
|
+
};
|
|
26066
|
+
}
|
|
26067
|
+
/**
|
|
26068
|
+
* Clamps a scalar into the inclusive `[0, 1]` range.
|
|
26069
|
+
*
|
|
26070
|
+
* @param value Arbitrary scalar.
|
|
26071
|
+
* @returns Clamped scalar.
|
|
26072
|
+
*
|
|
26073
|
+
* @private utility of the avatar rendering system
|
|
26074
|
+
*/
|
|
26075
|
+
function clamp01$1(value) {
|
|
26076
|
+
return Math.min(1, Math.max(0, value));
|
|
26077
|
+
}
|
|
26078
|
+
/**
|
|
26079
|
+
* Interpolates between two values using frame-rate-independent exponential easing.
|
|
26080
|
+
*
|
|
26081
|
+
* @param currentValue Current smoothed value.
|
|
26082
|
+
* @param targetValue Target value.
|
|
26083
|
+
* @param deltaMs Elapsed milliseconds since the previous frame.
|
|
26084
|
+
* @param smoothingWindowMs Time constant controlling responsiveness.
|
|
26085
|
+
* @returns Smoothed next value.
|
|
26086
|
+
*
|
|
26087
|
+
* @private utility of the avatar rendering system
|
|
26088
|
+
*/
|
|
26089
|
+
function interpolateExponentially(currentValue, targetValue, deltaMs, smoothingWindowMs) {
|
|
26090
|
+
const blend = 1 - Math.exp(-deltaMs / Math.max(1, smoothingWindowMs));
|
|
26091
|
+
return currentValue + (targetValue - currentValue) * blend;
|
|
26092
|
+
}
|
|
26093
|
+
|
|
26094
|
+
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
26095
|
+
/**
|
|
26096
|
+
* Next registration id used by the shared avatar animation scheduler.
|
|
26097
|
+
*
|
|
26098
|
+
* @private utility of the avatar rendering system
|
|
26099
|
+
*/
|
|
26100
|
+
let nextAvatarAnimationListenerId = 1;
|
|
26101
|
+
/**
|
|
26102
|
+
* Active avatar animation callbacks keyed by their registration id.
|
|
26103
|
+
*
|
|
26104
|
+
* @private utility of the avatar rendering system
|
|
26105
|
+
*/
|
|
26106
|
+
const avatarAnimationListeners = new Map();
|
|
26107
|
+
/**
|
|
26108
|
+
* Active shared animation-frame handle.
|
|
26109
|
+
*
|
|
26110
|
+
* @private utility of the avatar rendering system
|
|
26111
|
+
*/
|
|
26112
|
+
let avatarAnimationFrameId = null;
|
|
26113
|
+
/**
|
|
26114
|
+
* Registers one avatar animation callback in the shared animation loop.
|
|
26115
|
+
*
|
|
26116
|
+
* @param avatarAnimationListener Frame callback invoked on every animation frame.
|
|
26117
|
+
* @returns Cleanup function that unregisters the callback.
|
|
26118
|
+
*
|
|
26119
|
+
* @private utility of the avatar rendering system
|
|
26120
|
+
*/
|
|
26121
|
+
function retainAvatarAnimationListener(avatarAnimationListener) {
|
|
26122
|
+
if (typeof window === 'undefined') {
|
|
26123
|
+
return () => undefined;
|
|
26124
|
+
}
|
|
26125
|
+
const listenerId = nextAvatarAnimationListenerId++;
|
|
26126
|
+
avatarAnimationListeners.set(listenerId, avatarAnimationListener);
|
|
26127
|
+
ensureAvatarAnimationLoop();
|
|
26128
|
+
return () => {
|
|
26129
|
+
avatarAnimationListeners.delete(listenerId);
|
|
26130
|
+
if (avatarAnimationListeners.size === 0 && avatarAnimationFrameId !== null) {
|
|
26131
|
+
window.cancelAnimationFrame(avatarAnimationFrameId);
|
|
26132
|
+
avatarAnimationFrameId = null;
|
|
26133
|
+
}
|
|
26134
|
+
};
|
|
26135
|
+
}
|
|
26136
|
+
/**
|
|
26137
|
+
* Ensures the shared animation loop is running while at least one avatar listener is active.
|
|
26138
|
+
*
|
|
26139
|
+
* @private utility of the avatar rendering system
|
|
26140
|
+
*/
|
|
26141
|
+
function ensureAvatarAnimationLoop() {
|
|
26142
|
+
if (avatarAnimationFrameId !== null || avatarAnimationListeners.size === 0 || typeof window === 'undefined') {
|
|
26143
|
+
return;
|
|
26144
|
+
}
|
|
26145
|
+
const runFrame = (now) => {
|
|
26146
|
+
avatarAnimationFrameId = null;
|
|
26147
|
+
for (const avatarAnimationListener of [...avatarAnimationListeners.values()]) {
|
|
26148
|
+
avatarAnimationListener(now);
|
|
26149
|
+
}
|
|
26150
|
+
ensureAvatarAnimationLoop();
|
|
26151
|
+
};
|
|
26152
|
+
avatarAnimationFrameId = window.requestAnimationFrame(runFrame);
|
|
26153
|
+
}
|
|
26154
|
+
|
|
26155
|
+
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
26156
|
+
/**
|
|
26157
|
+
* Shared `IntersectionObserver` callbacks grouped by observed element.
|
|
26158
|
+
*
|
|
26159
|
+
* @private utility of the avatar rendering system
|
|
26160
|
+
*/
|
|
26161
|
+
const avatarVisibilityListeners = new Map();
|
|
26162
|
+
/**
|
|
26163
|
+
* Lazily created shared `IntersectionObserver` used by avatar canvases.
|
|
26164
|
+
*
|
|
26165
|
+
* @private utility of the avatar rendering system
|
|
26166
|
+
*/
|
|
26167
|
+
let avatarVisibilityObserver = null;
|
|
26168
|
+
/**
|
|
26169
|
+
* Observes one avatar element and notifies the caller when it enters or leaves the viewport.
|
|
26170
|
+
*
|
|
26171
|
+
* @param element Observed avatar element.
|
|
26172
|
+
* @param avatarVisibilityListener Listener notified with the current visibility state.
|
|
26173
|
+
* @returns Cleanup function that stops observing the element.
|
|
26174
|
+
*
|
|
26175
|
+
* @private utility of the avatar rendering system
|
|
26176
|
+
*/
|
|
26177
|
+
function observeAvatarVisibility(element, avatarVisibilityListener) {
|
|
26178
|
+
if (typeof window === 'undefined' || typeof IntersectionObserver === 'undefined') {
|
|
26179
|
+
avatarVisibilityListener(true);
|
|
26180
|
+
return () => undefined;
|
|
26181
|
+
}
|
|
26182
|
+
const observer = getAvatarVisibilityObserver();
|
|
26183
|
+
const elementListeners = avatarVisibilityListeners.get(element) || new Set();
|
|
26184
|
+
elementListeners.add(avatarVisibilityListener);
|
|
26185
|
+
avatarVisibilityListeners.set(element, elementListeners);
|
|
26186
|
+
observer.observe(element);
|
|
26187
|
+
return () => {
|
|
26188
|
+
const currentElementListeners = avatarVisibilityListeners.get(element);
|
|
26189
|
+
if (!currentElementListeners) {
|
|
26190
|
+
return;
|
|
26191
|
+
}
|
|
26192
|
+
currentElementListeners.delete(avatarVisibilityListener);
|
|
26193
|
+
if (currentElementListeners.size > 0) {
|
|
26194
|
+
return;
|
|
26195
|
+
}
|
|
26196
|
+
avatarVisibilityListeners.delete(element);
|
|
26197
|
+
observer.unobserve(element);
|
|
26198
|
+
};
|
|
26199
|
+
}
|
|
26200
|
+
/**
|
|
26201
|
+
* Creates the shared `IntersectionObserver` used by all avatar canvases.
|
|
26202
|
+
*
|
|
26203
|
+
* @returns Shared observer instance.
|
|
26204
|
+
*
|
|
26205
|
+
* @private utility of the avatar rendering system
|
|
26206
|
+
*/
|
|
26207
|
+
function getAvatarVisibilityObserver() {
|
|
26208
|
+
if (avatarVisibilityObserver) {
|
|
26209
|
+
return avatarVisibilityObserver;
|
|
26210
|
+
}
|
|
26211
|
+
avatarVisibilityObserver = new IntersectionObserver((entries) => {
|
|
26212
|
+
for (const entry of entries) {
|
|
26213
|
+
const elementListeners = avatarVisibilityListeners.get(entry.target);
|
|
26214
|
+
if (!elementListeners) {
|
|
26215
|
+
continue;
|
|
26216
|
+
}
|
|
26217
|
+
const isVisible = entry.isIntersecting && entry.intersectionRatio > 0;
|
|
26218
|
+
for (const avatarVisibilityListener of elementListeners) {
|
|
26219
|
+
avatarVisibilityListener(isVisible);
|
|
26220
|
+
}
|
|
26221
|
+
}
|
|
26222
|
+
});
|
|
26223
|
+
return avatarVisibilityObserver;
|
|
26224
|
+
}
|
|
26225
|
+
|
|
26226
|
+
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
26227
|
+
/**
|
|
26228
|
+
* Active avatar instances currently consuming the shared pointer tracker.
|
|
26229
|
+
*
|
|
26230
|
+
* @private utility of the avatar rendering system
|
|
26231
|
+
*/
|
|
26232
|
+
let avatarPointerTrackingConsumerCount = 0;
|
|
26233
|
+
/**
|
|
26234
|
+
* Latest shared viewport pointer sample.
|
|
26235
|
+
*
|
|
26236
|
+
* @private utility of the avatar rendering system
|
|
26237
|
+
*/
|
|
26238
|
+
let currentAvatarPointerSnapshot = null;
|
|
26239
|
+
/**
|
|
26240
|
+
* Monotonic version incremented whenever the shared pointer snapshot changes.
|
|
26241
|
+
*
|
|
26242
|
+
* @private utility of the avatar rendering system
|
|
26243
|
+
*/
|
|
26244
|
+
let currentAvatarPointerSnapshotVersion = 0;
|
|
26245
|
+
/**
|
|
26246
|
+
* Monotonic version incremented whenever scrolling or resizing may invalidate cached avatar bounds.
|
|
26247
|
+
*
|
|
26248
|
+
* @private utility of the avatar rendering system
|
|
26249
|
+
*/
|
|
26250
|
+
let currentAvatarViewportLayoutVersion = 0;
|
|
26251
|
+
/**
|
|
26252
|
+
* Cleanup function for the lazily attached global listeners.
|
|
26253
|
+
*
|
|
26254
|
+
* @private utility of the avatar rendering system
|
|
26255
|
+
*/
|
|
26256
|
+
let releaseAvatarPointerTrackingListeners = null;
|
|
26257
|
+
/**
|
|
26258
|
+
* Starts the shared pointer tracker and returns a disposer for the caller.
|
|
26259
|
+
*
|
|
26260
|
+
* @returns Cleanup function that releases one consumer.
|
|
26261
|
+
*
|
|
26262
|
+
* @private utility of the avatar rendering system
|
|
26263
|
+
*/
|
|
26264
|
+
function retainAvatarPointerTracking() {
|
|
26265
|
+
avatarPointerTrackingConsumerCount++;
|
|
26266
|
+
if (avatarPointerTrackingConsumerCount === 1) {
|
|
26267
|
+
releaseAvatarPointerTrackingListeners = attachAvatarPointerTrackingListeners();
|
|
26268
|
+
}
|
|
26269
|
+
return () => {
|
|
26270
|
+
avatarPointerTrackingConsumerCount = Math.max(0, avatarPointerTrackingConsumerCount - 1);
|
|
26271
|
+
if (avatarPointerTrackingConsumerCount === 0) {
|
|
26272
|
+
currentAvatarPointerSnapshot = null;
|
|
26273
|
+
releaseAvatarPointerTrackingListeners === null || releaseAvatarPointerTrackingListeners === void 0 ? void 0 : releaseAvatarPointerTrackingListeners();
|
|
26274
|
+
releaseAvatarPointerTrackingListeners = null;
|
|
26275
|
+
}
|
|
26276
|
+
};
|
|
26277
|
+
}
|
|
26278
|
+
/**
|
|
26279
|
+
* Returns the latest shared viewport pointer sample when available.
|
|
26280
|
+
*
|
|
26281
|
+
* @returns Shared pointer snapshot or `null`.
|
|
26282
|
+
*
|
|
26283
|
+
* @private utility of the avatar rendering system
|
|
26284
|
+
*/
|
|
26285
|
+
function getAvatarPointerSnapshot() {
|
|
26286
|
+
return currentAvatarPointerSnapshot;
|
|
26287
|
+
}
|
|
26288
|
+
/**
|
|
26289
|
+
* Returns the current pointer snapshot version.
|
|
26290
|
+
*
|
|
26291
|
+
* @returns Monotonic pointer snapshot version.
|
|
26292
|
+
*
|
|
26293
|
+
* @private utility of the avatar rendering system
|
|
26294
|
+
*/
|
|
26295
|
+
function getAvatarPointerSnapshotVersion() {
|
|
26296
|
+
return currentAvatarPointerSnapshotVersion;
|
|
26297
|
+
}
|
|
26298
|
+
/**
|
|
26299
|
+
* Returns the current viewport-layout version used to invalidate cached avatar bounds.
|
|
26300
|
+
*
|
|
26301
|
+
* @returns Monotonic viewport-layout version.
|
|
26302
|
+
*
|
|
26303
|
+
* @private utility of the avatar rendering system
|
|
26304
|
+
*/
|
|
26305
|
+
function getAvatarViewportLayoutVersion() {
|
|
26306
|
+
return currentAvatarViewportLayoutVersion;
|
|
26307
|
+
}
|
|
26308
|
+
/**
|
|
26309
|
+
* Attaches the global pointer/touch listeners used by all live avatar canvases.
|
|
26310
|
+
*
|
|
26311
|
+
* @returns Cleanup function for the attached listeners.
|
|
26312
|
+
*
|
|
26313
|
+
* @private utility of the avatar rendering system
|
|
26314
|
+
*/
|
|
26315
|
+
function attachAvatarPointerTrackingListeners() {
|
|
26316
|
+
if (typeof window === 'undefined') {
|
|
26317
|
+
return () => undefined;
|
|
26318
|
+
}
|
|
26319
|
+
const clearPointerSnapshot = () => {
|
|
26320
|
+
if (currentAvatarPointerSnapshot === null) {
|
|
26321
|
+
return;
|
|
26322
|
+
}
|
|
26323
|
+
currentAvatarPointerSnapshot = null;
|
|
26324
|
+
currentAvatarPointerSnapshotVersion++;
|
|
26325
|
+
};
|
|
26326
|
+
const updatePointerSnapshot = (clientX, clientY, pointerType) => {
|
|
26327
|
+
currentAvatarPointerSnapshot = {
|
|
26328
|
+
clientX,
|
|
26329
|
+
clientY,
|
|
26330
|
+
isPointerActive: true,
|
|
26331
|
+
pointerType,
|
|
26332
|
+
};
|
|
26333
|
+
currentAvatarPointerSnapshotVersion++;
|
|
26334
|
+
};
|
|
26335
|
+
const invalidateViewportLayout = () => {
|
|
26336
|
+
currentAvatarViewportLayoutVersion++;
|
|
26337
|
+
};
|
|
26338
|
+
const handlePointerMove = (event) => {
|
|
26339
|
+
updatePointerSnapshot(event.clientX, event.clientY, normalizeAvatarPointerType(event.pointerType));
|
|
26340
|
+
};
|
|
26341
|
+
const handlePointerDown = (event) => {
|
|
26342
|
+
updatePointerSnapshot(event.clientX, event.clientY, normalizeAvatarPointerType(event.pointerType));
|
|
26343
|
+
};
|
|
26344
|
+
const handlePointerUp = (event) => {
|
|
26345
|
+
if (normalizeAvatarPointerType(event.pointerType) !== 'mouse') {
|
|
26346
|
+
clearPointerSnapshot();
|
|
26347
|
+
}
|
|
26348
|
+
};
|
|
26349
|
+
const handleMouseOut = (event) => {
|
|
26350
|
+
if (!event.relatedTarget) {
|
|
26351
|
+
clearPointerSnapshot();
|
|
26352
|
+
}
|
|
26353
|
+
};
|
|
26354
|
+
const handleTouchEvent = (event) => {
|
|
26355
|
+
const touch = event.touches[0] || event.changedTouches[0];
|
|
26356
|
+
if (!touch) {
|
|
26357
|
+
clearPointerSnapshot();
|
|
26358
|
+
return;
|
|
26359
|
+
}
|
|
26360
|
+
updatePointerSnapshot(touch.clientX, touch.clientY, 'touch');
|
|
26361
|
+
};
|
|
26362
|
+
window.addEventListener('pointermove', handlePointerMove, { passive: true });
|
|
26363
|
+
window.addEventListener('pointerdown', handlePointerDown, { passive: true });
|
|
26364
|
+
window.addEventListener('pointerup', handlePointerUp, { passive: true });
|
|
26365
|
+
window.addEventListener('pointercancel', clearPointerSnapshot, { passive: true });
|
|
26366
|
+
window.addEventListener('mouseout', handleMouseOut, { passive: true });
|
|
26367
|
+
window.addEventListener('blur', clearPointerSnapshot);
|
|
26368
|
+
window.addEventListener('touchstart', handleTouchEvent, { passive: true });
|
|
26369
|
+
window.addEventListener('touchmove', handleTouchEvent, { passive: true });
|
|
26370
|
+
window.addEventListener('touchend', clearPointerSnapshot, { passive: true });
|
|
26371
|
+
window.addEventListener('touchcancel', clearPointerSnapshot, { passive: true });
|
|
26372
|
+
window.addEventListener('scroll', invalidateViewportLayout, { passive: true, capture: true });
|
|
26373
|
+
window.addEventListener('resize', invalidateViewportLayout, { passive: true });
|
|
26374
|
+
return () => {
|
|
26375
|
+
window.removeEventListener('pointermove', handlePointerMove);
|
|
26376
|
+
window.removeEventListener('pointerdown', handlePointerDown);
|
|
26377
|
+
window.removeEventListener('pointerup', handlePointerUp);
|
|
26378
|
+
window.removeEventListener('pointercancel', clearPointerSnapshot);
|
|
26379
|
+
window.removeEventListener('mouseout', handleMouseOut);
|
|
26380
|
+
window.removeEventListener('blur', clearPointerSnapshot);
|
|
26381
|
+
window.removeEventListener('touchstart', handleTouchEvent);
|
|
26382
|
+
window.removeEventListener('touchmove', handleTouchEvent);
|
|
26383
|
+
window.removeEventListener('touchend', clearPointerSnapshot);
|
|
26384
|
+
window.removeEventListener('touchcancel', clearPointerSnapshot);
|
|
26385
|
+
window.removeEventListener('scroll', invalidateViewportLayout, true);
|
|
26386
|
+
window.removeEventListener('resize', invalidateViewportLayout);
|
|
26387
|
+
};
|
|
26388
|
+
}
|
|
26389
|
+
/**
|
|
26390
|
+
* Normalizes browser pointer-type strings into the shared avatar contract.
|
|
26391
|
+
*
|
|
26392
|
+
* @param pointerType Raw browser pointer type.
|
|
26393
|
+
* @returns Shared pointer type.
|
|
26394
|
+
*
|
|
26395
|
+
* @private utility of the avatar rendering system
|
|
26396
|
+
*/
|
|
26397
|
+
function normalizeAvatarPointerType(pointerType) {
|
|
26398
|
+
if (pointerType === 'touch' || pointerType === 'pen') {
|
|
26399
|
+
return pointerType;
|
|
26400
|
+
}
|
|
26401
|
+
return 'mouse';
|
|
26402
|
+
}
|
|
26403
|
+
|
|
25741
26404
|
/* eslint-disable no-magic-numbers */
|
|
25742
26405
|
/**
|
|
25743
26406
|
* Builds a smoothly morphing octopus-like silhouette from deterministic parameters.
|
|
@@ -25814,8 +26477,9 @@ function traceSmoothClosedPath(context, points) {
|
|
|
25814
26477
|
* @private shared geometry helper of `octopus3AvatarVisual` and `asciiOctopusAvatarVisual`
|
|
25815
26478
|
*/
|
|
25816
26479
|
function createOrganicOctopusTentacleShapes(options) {
|
|
25817
|
-
const { size, centerX, centerY, bodyRadius, horizontalStretch, tentacleCount, shapePhase, createRandom, timeMs, saltPrefix, } = options;
|
|
26480
|
+
const { size, centerX, centerY, bodyRadius, horizontalStretch, tentacleCount, shapePhase, createRandom, timeMs, saltPrefix, bodyPoints } = options;
|
|
25818
26481
|
const baseY = centerY + bodyRadius * 0.74;
|
|
26482
|
+
const lowerBodyAnchorPoints = bodyPoints ? resolveTentacleBodyAnchorPoints(bodyPoints, centerY + bodyRadius * 0.04) : null;
|
|
25819
26483
|
return Array.from({ length: tentacleCount }, (_, tentacleIndex) => {
|
|
25820
26484
|
const tentacleRandom = createRandom(`${saltPrefix}-tentacle-${tentacleIndex}`);
|
|
25821
26485
|
const spreadProgress = tentacleCount === 1 ? 0.5 : tentacleIndex / (tentacleCount - 1);
|
|
@@ -25827,10 +26491,21 @@ function createOrganicOctopusTentacleShapes(options) {
|
|
|
25827
26491
|
const curlDirection = centeredProgress === 0 ? (tentacleRandom() < 0.5 ? -1 : 1) : Math.sign(centeredProgress);
|
|
25828
26492
|
const lateralReach = centeredProgress * size * (0.1 + tentacleRandom() * 0.1) + temporalSway;
|
|
25829
26493
|
const tipReach = curlDirection * size * (0.025 + tentacleRandom() * 0.07);
|
|
25830
|
-
const
|
|
25831
|
-
|
|
25832
|
-
|
|
25833
|
-
|
|
26494
|
+
const startYOffset = Math.abs(centeredProgress) * size * 0.012 + tentacleRandom() * size * 0.01;
|
|
26495
|
+
const startPoint = lowerBodyAnchorPoints && lowerBodyAnchorPoints.length >= 2
|
|
26496
|
+
? createInsetTentacleStartPoint({
|
|
26497
|
+
bodyPoints: lowerBodyAnchorPoints,
|
|
26498
|
+
anchorProgress: spreadProgress,
|
|
26499
|
+
centerX,
|
|
26500
|
+
centerY,
|
|
26501
|
+
bodyRadius,
|
|
26502
|
+
centeredProgress,
|
|
26503
|
+
startYOffset,
|
|
26504
|
+
})
|
|
26505
|
+
: {
|
|
26506
|
+
x: centerX + centeredProgress * bodyRadius * horizontalStretch * 1.52,
|
|
26507
|
+
y: baseY + startYOffset,
|
|
26508
|
+
};
|
|
25834
26509
|
const controlPointOne = {
|
|
25835
26510
|
x: startPoint.x + centeredProgress * size * 0.045 + temporalSway * 0.4,
|
|
25836
26511
|
y: startPoint.y + flowLength * (0.21 + tentacleRandom() * 0.08),
|
|
@@ -25860,6 +26535,67 @@ function createOrganicOctopusTentacleShapes(options) {
|
|
|
25860
26535
|
};
|
|
25861
26536
|
});
|
|
25862
26537
|
}
|
|
26538
|
+
/**
|
|
26539
|
+
* Narrows the body contour to lower anchor points that can safely host tentacle roots.
|
|
26540
|
+
*
|
|
26541
|
+
* @param bodyPoints Generated closed-loop body points.
|
|
26542
|
+
* @param lowerBodyThresholdY Minimum Y coordinate considered part of the lower mantle.
|
|
26543
|
+
* @returns Body points sorted from left to right across the lower silhouette.
|
|
26544
|
+
*
|
|
26545
|
+
* @private shared geometry helper of `octopus3AvatarVisual`
|
|
26546
|
+
*/
|
|
26547
|
+
function resolveTentacleBodyAnchorPoints(bodyPoints, lowerBodyThresholdY) {
|
|
26548
|
+
const lowerBodyPoints = bodyPoints.filter((bodyPoint) => bodyPoint.y >= lowerBodyThresholdY).sort((leftPoint, rightPoint) => leftPoint.x - rightPoint.x);
|
|
26549
|
+
if (lowerBodyPoints.length >= 2) {
|
|
26550
|
+
return lowerBodyPoints;
|
|
26551
|
+
}
|
|
26552
|
+
return [...bodyPoints].sort((leftPoint, rightPoint) => leftPoint.x - rightPoint.x);
|
|
26553
|
+
}
|
|
26554
|
+
/**
|
|
26555
|
+
* Resolves one tentacle root from the provided lower body contour and nudges it inside the mantle.
|
|
26556
|
+
*
|
|
26557
|
+
* @param options Tentacle anchor options.
|
|
26558
|
+
* @returns Tentacle start point safely embedded inside the body silhouette.
|
|
26559
|
+
*
|
|
26560
|
+
* @private shared geometry helper of `octopus3AvatarVisual`
|
|
26561
|
+
*/
|
|
26562
|
+
function createInsetTentacleStartPoint(options) {
|
|
26563
|
+
const { bodyPoints, anchorProgress, centerX, centerY, bodyRadius, centeredProgress, startYOffset } = options;
|
|
26564
|
+
const clampedAnchorProgress = Math.min(0.94, Math.max(0.06, anchorProgress));
|
|
26565
|
+
const bodyAnchorPoint = interpolatePointAlongTentacleAnchors(bodyPoints, clampedAnchorProgress);
|
|
26566
|
+
const inwardX = centerX - bodyAnchorPoint.x;
|
|
26567
|
+
const inwardY = centerY + bodyRadius * 0.08 - bodyAnchorPoint.y;
|
|
26568
|
+
const inwardLength = Math.hypot(inwardX, inwardY) || 1;
|
|
26569
|
+
const insetDistance = bodyRadius * (0.12 + Math.abs(centeredProgress) * 0.05) + startYOffset * 0.32;
|
|
26570
|
+
return {
|
|
26571
|
+
x: bodyAnchorPoint.x + (inwardX / inwardLength) * insetDistance,
|
|
26572
|
+
y: bodyAnchorPoint.y + (inwardY / inwardLength) * insetDistance,
|
|
26573
|
+
};
|
|
26574
|
+
}
|
|
26575
|
+
/**
|
|
26576
|
+
* Interpolates one left-to-right anchor point along the prepared lower body contour.
|
|
26577
|
+
*
|
|
26578
|
+
* @param bodyPoints Lower body contour points sorted from left to right.
|
|
26579
|
+
* @param progress Interpolation progress in the range `[0, 1]`.
|
|
26580
|
+
* @returns Interpolated anchor point.
|
|
26581
|
+
*
|
|
26582
|
+
* @private shared geometry helper of `octopus3AvatarVisual`
|
|
26583
|
+
*/
|
|
26584
|
+
function interpolatePointAlongTentacleAnchors(bodyPoints, progress) {
|
|
26585
|
+
if (bodyPoints.length === 1) {
|
|
26586
|
+
return bodyPoints[0];
|
|
26587
|
+
}
|
|
26588
|
+
const anchorIndex = progress * (bodyPoints.length - 1);
|
|
26589
|
+
const startIndex = Math.floor(anchorIndex);
|
|
26590
|
+
const endIndex = Math.min(bodyPoints.length - 1, startIndex + 1);
|
|
26591
|
+
const blend = anchorIndex - startIndex;
|
|
26592
|
+
const startPoint = bodyPoints[startIndex];
|
|
26593
|
+
const endPoint = bodyPoints[endIndex];
|
|
26594
|
+
return {
|
|
26595
|
+
x: startPoint.x + (endPoint.x - startPoint.x) * blend,
|
|
26596
|
+
y: startPoint.y + (endPoint.y - startPoint.y) * blend,
|
|
26597
|
+
};
|
|
26598
|
+
}
|
|
25863
26599
|
/**
|
|
25864
26600
|
* Samples the cubic tentacle centerline and offsets normals to build a filled ribbon.
|
|
25865
26601
|
*
|
|
@@ -25889,6 +26625,26 @@ function sampleOrganicTentacleRibbonPoints(tentacleShape) {
|
|
|
25889
26625
|
};
|
|
25890
26626
|
});
|
|
25891
26627
|
}
|
|
26628
|
+
/**
|
|
26629
|
+
* Resolves smooth pupil offsets that blend autonomous idle drift with live viewer tracking.
|
|
26630
|
+
*
|
|
26631
|
+
* @param options Eye motion options.
|
|
26632
|
+
* @returns Resolved pupil offsets.
|
|
26633
|
+
*
|
|
26634
|
+
* @private shared geometry helper of octopus avatar visuals
|
|
26635
|
+
*/
|
|
26636
|
+
function resolveOrganicEyeMotion(options) {
|
|
26637
|
+
const { radiusX, radiusY, timeMs, phase, interaction, autonomousDriftRatioX = 0.12, autonomousDriftRatioY = 0.08, } = options;
|
|
26638
|
+
const autonomousOffsetX = Math.sin(timeMs / 1280 + phase) * radiusX * autonomousDriftRatioX;
|
|
26639
|
+
const autonomousOffsetY = Math.cos(timeMs / 940 + phase) * radiusY * autonomousDriftRatioY;
|
|
26640
|
+
const interactionBlend = Math.min(1, interaction.intensity * 0.9);
|
|
26641
|
+
return {
|
|
26642
|
+
pupilOffsetX: autonomousOffsetX * (1 - interactionBlend) +
|
|
26643
|
+
interaction.gazeX * radiusX * (0.18 + interactionBlend * 0.18),
|
|
26644
|
+
pupilOffsetY: autonomousOffsetY * (1 - interactionBlend) +
|
|
26645
|
+
interaction.gazeY * radiusY * (0.16 + interactionBlend * 0.16),
|
|
26646
|
+
};
|
|
26647
|
+
}
|
|
25892
26648
|
/**
|
|
25893
26649
|
* Samples one point on a cubic Bezier curve.
|
|
25894
26650
|
*
|
|
@@ -25942,13 +26698,14 @@ const ATMOSPHERE_GLYPHS = ['.', ':', '\'', '`'];
|
|
|
25942
26698
|
const asciiOctopusAvatarVisual = {
|
|
25943
26699
|
id: 'ascii-octopus',
|
|
25944
26700
|
title: 'AsciiOctopus',
|
|
25945
|
-
description: 'Morphing alien octopus translated into animated ASCII glyphs with
|
|
26701
|
+
description: 'Morphing alien octopus translated into animated ASCII glyphs with responsive eyes and seeded geometry.',
|
|
25946
26702
|
isAnimated: true,
|
|
25947
|
-
|
|
26703
|
+
supportsPointerTracking: true,
|
|
26704
|
+
render({ context, size, palette, createRandom, timeMs, interaction }) {
|
|
25948
26705
|
const gridRandom = createRandom('ascii-octopus-grid');
|
|
25949
26706
|
const staticRandom = createRandom('ascii-octopus-static');
|
|
25950
26707
|
const gridMetrics = createAsciiGridMetrics(size, gridRandom);
|
|
25951
|
-
const layout = createAsciiOctopusLayout(size, timeMs, createRandom, staticRandom);
|
|
26708
|
+
const layout = createAsciiOctopusLayout(size, timeMs, createRandom, staticRandom, interaction);
|
|
25952
26709
|
drawAvatarFrame(context, size, palette);
|
|
25953
26710
|
drawAsciiBackdrop(context, size, palette, layout, timeMs);
|
|
25954
26711
|
context.save();
|
|
@@ -26022,7 +26779,8 @@ function drawAsciiBackdrop(context, size, palette, layout, timeMs) {
|
|
|
26022
26779
|
*/
|
|
26023
26780
|
function resolveAsciiGlyph(options) {
|
|
26024
26781
|
const { point, layout, palette, cellWidth, cellHeight, noise, timeMs } = options;
|
|
26025
|
-
const eyeGlyphDescriptor = resolveEyeGlyph(point, layout.leftEye,
|
|
26782
|
+
const eyeGlyphDescriptor = resolveEyeGlyph(point, layout.leftEye, layout.interaction, palette, timeMs) ||
|
|
26783
|
+
resolveEyeGlyph(point, layout.rightEye, layout.interaction, palette, timeMs);
|
|
26026
26784
|
if (eyeGlyphDescriptor) {
|
|
26027
26785
|
return eyeGlyphDescriptor;
|
|
26028
26786
|
}
|
|
@@ -26067,9 +26825,14 @@ function resolveAsciiGlyph(options) {
|
|
|
26067
26825
|
*
|
|
26068
26826
|
* @private helper of `asciiOctopusAvatarVisual`
|
|
26069
26827
|
*/
|
|
26070
|
-
function resolveEyeGlyph(point, eyeFeature, palette, timeMs) {
|
|
26071
|
-
const pupilOffsetX =
|
|
26072
|
-
|
|
26828
|
+
function resolveEyeGlyph(point, eyeFeature, interaction, palette, timeMs) {
|
|
26829
|
+
const { pupilOffsetX, pupilOffsetY } = resolveOrganicEyeMotion({
|
|
26830
|
+
radiusX: eyeFeature.radiusX,
|
|
26831
|
+
radiusY: eyeFeature.radiusY,
|
|
26832
|
+
timeMs,
|
|
26833
|
+
phase: eyeFeature.phase,
|
|
26834
|
+
interaction,
|
|
26835
|
+
});
|
|
26073
26836
|
const scleraDistance = measureRotatedEllipseDistance(point, eyeFeature.centerX, eyeFeature.centerY, eyeFeature.radiusX, eyeFeature.radiusY, eyeFeature.rotation);
|
|
26074
26837
|
if (scleraDistance > 1.08) {
|
|
26075
26838
|
return null;
|
|
@@ -26243,9 +27006,9 @@ function createAsciiGridMetrics(size, staticRandom) {
|
|
|
26243
27006
|
*
|
|
26244
27007
|
* @private helper of `asciiOctopusAvatarVisual`
|
|
26245
27008
|
*/
|
|
26246
|
-
function createAsciiOctopusLayout(size, timeMs, createRandom, staticRandom) {
|
|
26247
|
-
const centerX = size * (0.5 + (staticRandom() - 0.5) * 0.02);
|
|
26248
|
-
const centerY = size * (0.41 + staticRandom() * 0.05);
|
|
27009
|
+
function createAsciiOctopusLayout(size, timeMs, createRandom, staticRandom, interaction) {
|
|
27010
|
+
const centerX = size * (0.5 + (staticRandom() - 0.5) * 0.02) + interaction.bodyOffsetX * size * 0.05;
|
|
27011
|
+
const centerY = size * (0.41 + staticRandom() * 0.05) + interaction.bodyOffsetY * size * 0.035;
|
|
26249
27012
|
const bodyRadius = size * (0.195 + staticRandom() * 0.05);
|
|
26250
27013
|
const horizontalStretch = 1.08 + staticRandom() * 0.22;
|
|
26251
27014
|
const verticalStretch = 0.88 + staticRandom() * 0.14;
|
|
@@ -26285,6 +27048,7 @@ function createAsciiOctopusLayout(size, timeMs, createRandom, staticRandom) {
|
|
|
26285
27048
|
createRandom,
|
|
26286
27049
|
timeMs,
|
|
26287
27050
|
saltPrefix: 'ascii-octopus',
|
|
27051
|
+
bodyPoints,
|
|
26288
27052
|
});
|
|
26289
27053
|
const sampledTentacles = tentacleShapes.map(sampleOrganicTentacleRibbonPoints);
|
|
26290
27054
|
const leftEye = {
|
|
@@ -26305,7 +27069,7 @@ function createAsciiOctopusLayout(size, timeMs, createRandom, staticRandom) {
|
|
|
26305
27069
|
};
|
|
26306
27070
|
const mouthPoints = sampleQuadraticBezierPoints({ x: centerX - size * 0.074, y: centerY + size * 0.092 }, {
|
|
26307
27071
|
x: centerX,
|
|
26308
|
-
y: centerY + size * (0.142 + Math.sin(timeMs / 620 + shapePhase) * 0.016),
|
|
27072
|
+
y: centerY + size * (0.142 + Math.sin(timeMs / 620 + shapePhase) * 0.016) + interaction.gazeY * size * 0.012,
|
|
26309
27073
|
}, { x: centerX + size * 0.074, y: centerY + size * 0.092 }, 12);
|
|
26310
27074
|
let leftBound = Number.POSITIVE_INFINITY;
|
|
26311
27075
|
let rightBound = Number.NEGATIVE_INFINITY;
|
|
@@ -26331,6 +27095,7 @@ function createAsciiOctopusLayout(size, timeMs, createRandom, staticRandom) {
|
|
|
26331
27095
|
bodyRadius,
|
|
26332
27096
|
horizontalStretch,
|
|
26333
27097
|
shapePhase,
|
|
27098
|
+
interaction,
|
|
26334
27099
|
bodyPoints,
|
|
26335
27100
|
sampledTentacles,
|
|
26336
27101
|
leftEye,
|
|
@@ -27051,15 +27816,16 @@ function createMinecraftShirtTexture(random, palette) {
|
|
|
27051
27816
|
const octopusAvatarVisual = {
|
|
27052
27817
|
id: 'octopus',
|
|
27053
27818
|
title: 'Octopus',
|
|
27054
|
-
description: 'Playful underwater mascot with animated tentacles, bubbles, and
|
|
27819
|
+
description: 'Playful underwater mascot with cursor-following eyes, animated tentacles, bubbles, and seeded markings.',
|
|
27055
27820
|
isAnimated: true,
|
|
27056
|
-
|
|
27821
|
+
supportsPointerTracking: true,
|
|
27822
|
+
render({ context, size, palette, createRandom, timeMs, interaction }) {
|
|
27057
27823
|
const staticRandom = createRandom('octopus-static');
|
|
27058
27824
|
const bubbleRandom = createRandom('octopus-bubbles');
|
|
27059
27825
|
const bubbleCount = 8;
|
|
27060
27826
|
const bubbleRadiusBase = size * 0.02;
|
|
27061
|
-
const centerX = size * 0.5;
|
|
27062
|
-
const centerY = size * 0.42;
|
|
27827
|
+
const centerX = size * 0.5 + interaction.bodyOffsetX * size * 0.035;
|
|
27828
|
+
const centerY = size * 0.42 + interaction.bodyOffsetY * size * 0.024;
|
|
27063
27829
|
const headRadius = size * (0.19 + staticRandom() * 0.03);
|
|
27064
27830
|
const mantleHeight = headRadius * 1.18;
|
|
27065
27831
|
const tentacleLength = size * (0.18 + staticRandom() * 0.06);
|
|
@@ -27139,11 +27905,9 @@ const octopusAvatarVisual = {
|
|
|
27139
27905
|
}
|
|
27140
27906
|
const eyeOffsetX = headRadius * 0.42;
|
|
27141
27907
|
const eyeY = centerY + headRadius * 0.04;
|
|
27142
|
-
const pupilDriftX = Math.sin(timeMs / 850) * headRadius * 0.05;
|
|
27143
|
-
const pupilDriftY = Math.cos(timeMs / 930) * headRadius * 0.03;
|
|
27144
27908
|
const eyeRadius = headRadius * 0.22;
|
|
27145
|
-
drawEye(context, centerX - eyeOffsetX, eyeY, eyeRadius, palette,
|
|
27146
|
-
drawEye(context, centerX + eyeOffsetX, eyeY, eyeRadius, palette,
|
|
27909
|
+
drawEye(context, centerX - eyeOffsetX, eyeY, eyeRadius, palette, timeMs, interaction, 0);
|
|
27910
|
+
drawEye(context, centerX + eyeOffsetX, eyeY, eyeRadius, palette, timeMs, interaction, Math.PI / 5);
|
|
27147
27911
|
context.beginPath();
|
|
27148
27912
|
context.arc(centerX - headRadius * 0.28, centerY + headRadius * 0.3, headRadius * 0.12, 0, Math.PI * 2);
|
|
27149
27913
|
context.arc(centerX + headRadius * 0.28, centerY + headRadius * 0.3, headRadius * 0.12, 0, Math.PI * 2);
|
|
@@ -27166,22 +27930,32 @@ const octopusAvatarVisual = {
|
|
|
27166
27930
|
* @param centerY Eye center Y coordinate.
|
|
27167
27931
|
* @param radius Eye radius.
|
|
27168
27932
|
* @param palette Derived avatar palette.
|
|
27169
|
-
* @param
|
|
27170
|
-
* @param
|
|
27933
|
+
* @param timeMs Current animation time in milliseconds.
|
|
27934
|
+
* @param interaction Smoothed avatar interaction state.
|
|
27935
|
+
* @param phase Seed-based phase offset.
|
|
27171
27936
|
*
|
|
27172
27937
|
* @private helper of `octopusAvatarVisual`
|
|
27173
27938
|
*/
|
|
27174
|
-
function drawEye(context, centerX, centerY, radius, palette,
|
|
27939
|
+
function drawEye(context, centerX, centerY, radius, palette, timeMs, interaction, phase) {
|
|
27940
|
+
const { pupilOffsetX, pupilOffsetY } = resolveOrganicEyeMotion({
|
|
27941
|
+
radiusX: radius,
|
|
27942
|
+
radiusY: radius,
|
|
27943
|
+
timeMs,
|
|
27944
|
+
phase,
|
|
27945
|
+
interaction,
|
|
27946
|
+
autonomousDriftRatioX: 0.05,
|
|
27947
|
+
autonomousDriftRatioY: 0.03,
|
|
27948
|
+
});
|
|
27175
27949
|
context.beginPath();
|
|
27176
27950
|
context.arc(centerX, centerY, radius, 0, Math.PI * 2);
|
|
27177
27951
|
context.fillStyle = '#ffffff';
|
|
27178
27952
|
context.fill();
|
|
27179
27953
|
context.beginPath();
|
|
27180
|
-
context.arc(centerX +
|
|
27954
|
+
context.arc(centerX + pupilOffsetX, centerY + pupilOffsetY, radius * 0.45, 0, Math.PI * 2);
|
|
27181
27955
|
context.fillStyle = palette.ink;
|
|
27182
27956
|
context.fill();
|
|
27183
27957
|
context.beginPath();
|
|
27184
|
-
context.arc(centerX +
|
|
27958
|
+
context.arc(centerX + pupilOffsetX - radius * 0.12, centerY + pupilOffsetY - radius * 0.12, radius * 0.15, 0, Math.PI * 2);
|
|
27185
27959
|
context.fillStyle = '#ffffff';
|
|
27186
27960
|
context.fill();
|
|
27187
27961
|
context.beginPath();
|
|
@@ -27200,12 +27974,13 @@ function drawEye(context, centerX, centerY, radius, palette, pupilDriftX, pupilD
|
|
|
27200
27974
|
const octopus2AvatarVisual = {
|
|
27201
27975
|
id: 'octopus2',
|
|
27202
27976
|
title: 'Octopus2',
|
|
27203
|
-
description: 'Organic alien octopus rendered as one continuously morphing blob with luminous eyes
|
|
27977
|
+
description: 'Organic alien octopus rendered as one continuously morphing blob with responsive luminous eyes.',
|
|
27204
27978
|
isAnimated: true,
|
|
27205
|
-
|
|
27979
|
+
supportsPointerTracking: true,
|
|
27980
|
+
render({ context, size, palette, createRandom, timeMs, interaction }) {
|
|
27206
27981
|
const staticRandom = createRandom('octopus2-static');
|
|
27207
|
-
const centerX = size * 0.5;
|
|
27208
|
-
const centerY = size * (0.48 + staticRandom() * 0.03);
|
|
27982
|
+
const centerX = size * 0.5 + interaction.bodyOffsetX * size * 0.042;
|
|
27983
|
+
const centerY = size * (0.48 + staticRandom() * 0.03) + interaction.bodyOffsetY * size * 0.028;
|
|
27209
27984
|
const bodyRadius = size * (0.25 + staticRandom() * 0.035);
|
|
27210
27985
|
const horizontalStretch = 1.04 + staticRandom() * 0.16;
|
|
27211
27986
|
const verticalStretch = 0.94 + staticRandom() * 0.12;
|
|
@@ -27276,11 +28051,11 @@ const octopus2AvatarVisual = {
|
|
|
27276
28051
|
const eyeCenterY = centerY - size * 0.02;
|
|
27277
28052
|
const eyeRadiusX = size * 0.072;
|
|
27278
28053
|
const eyeRadiusY = size * 0.086;
|
|
27279
|
-
drawAlienEye(context, centerX - eyeOffsetX, eyeCenterY, eyeRadiusX, eyeRadiusY, palette, timeMs, shapePhase);
|
|
27280
|
-
drawAlienEye(context, centerX + eyeOffsetX, eyeCenterY, eyeRadiusX, eyeRadiusY, palette, timeMs, shapePhase + Math.PI / 5);
|
|
28054
|
+
drawAlienEye(context, centerX - eyeOffsetX, eyeCenterY, eyeRadiusX, eyeRadiusY, palette, timeMs, shapePhase, interaction);
|
|
28055
|
+
drawAlienEye(context, centerX + eyeOffsetX, eyeCenterY, eyeRadiusX, eyeRadiusY, palette, timeMs, shapePhase + Math.PI / 5, interaction);
|
|
27281
28056
|
context.beginPath();
|
|
27282
28057
|
context.moveTo(centerX - size * 0.08, centerY + size * 0.12);
|
|
27283
|
-
context.quadraticCurveTo(centerX, centerY + size * (0.175 + Math.sin(timeMs / 520 + shapePhase) * 0.012), centerX + size * 0.08, centerY + size * 0.12);
|
|
28058
|
+
context.quadraticCurveTo(centerX, centerY + size * (0.175 + Math.sin(timeMs / 520 + shapePhase) * 0.012) + interaction.gazeY * size * 0.01, centerX + size * 0.08, centerY + size * 0.12);
|
|
27284
28059
|
context.strokeStyle = `${palette.ink}b3`;
|
|
27285
28060
|
context.lineWidth = size * 0.013;
|
|
27286
28061
|
context.lineCap = 'round';
|
|
@@ -27358,12 +28133,19 @@ function drawLowerSuckers(context, centerX, centerY, size, palette, createRandom
|
|
|
27358
28133
|
* @param palette Derived avatar palette.
|
|
27359
28134
|
* @param timeMs Current animation time in milliseconds.
|
|
27360
28135
|
* @param phase Seed-based animation phase.
|
|
28136
|
+
* @param interaction Smoothed avatar interaction state.
|
|
27361
28137
|
*
|
|
27362
28138
|
* @private helper of `octopus2AvatarVisual`
|
|
27363
28139
|
*/
|
|
27364
|
-
function drawAlienEye(context, centerX, centerY, radiusX, radiusY, palette, timeMs, phase) {
|
|
27365
|
-
const pupilOffsetX =
|
|
27366
|
-
|
|
28140
|
+
function drawAlienEye(context, centerX, centerY, radiusX, radiusY, palette, timeMs, phase, interaction) {
|
|
28141
|
+
const { pupilOffsetX, pupilOffsetY } = resolveOrganicEyeMotion({
|
|
28142
|
+
radiusX,
|
|
28143
|
+
radiusY,
|
|
28144
|
+
timeMs,
|
|
28145
|
+
phase,
|
|
28146
|
+
interaction,
|
|
28147
|
+
autonomousDriftRatioY: 0.1,
|
|
28148
|
+
});
|
|
27367
28149
|
context.save();
|
|
27368
28150
|
context.beginPath();
|
|
27369
28151
|
context.ellipse(centerX, centerY, radiusX, radiusY, 0, 0, Math.PI * 2);
|
|
@@ -27403,12 +28185,13 @@ function drawAlienEye(context, centerX, centerY, radiusX, radiusY, palette, time
|
|
|
27403
28185
|
const octopus3AvatarVisual = {
|
|
27404
28186
|
id: 'octopus3',
|
|
27405
28187
|
title: 'Octopus3',
|
|
27406
|
-
description: 'Gelatinous alien octopus with a morphing mantle,
|
|
28188
|
+
description: 'Gelatinous alien octopus with a morphing mantle, responsive eyes, and visible ribbon tentacles.',
|
|
27407
28189
|
isAnimated: true,
|
|
27408
|
-
|
|
28190
|
+
supportsPointerTracking: true,
|
|
28191
|
+
render({ context, size, palette, createRandom, timeMs, interaction }) {
|
|
27409
28192
|
const staticRandom = createRandom('octopus3-static');
|
|
27410
|
-
const centerX = size * (0.5 + (staticRandom() - 0.5) * 0.02);
|
|
27411
|
-
const centerY = size * (0.41 + staticRandom() * 0.05);
|
|
28193
|
+
const centerX = size * (0.5 + (staticRandom() - 0.5) * 0.02) + interaction.bodyOffsetX * size * 0.05;
|
|
28194
|
+
const centerY = size * (0.41 + staticRandom() * 0.05) + interaction.bodyOffsetY * size * 0.035;
|
|
27412
28195
|
const bodyRadius = size * (0.2 + staticRandom() * 0.045);
|
|
27413
28196
|
const horizontalStretch = 1.08 + staticRandom() * 0.22;
|
|
27414
28197
|
const verticalStretch = 0.9 + staticRandom() * 0.12;
|
|
@@ -27448,6 +28231,7 @@ const octopus3AvatarVisual = {
|
|
|
27448
28231
|
createRandom,
|
|
27449
28232
|
timeMs,
|
|
27450
28233
|
saltPrefix: 'octopus3',
|
|
28234
|
+
bodyPoints,
|
|
27451
28235
|
});
|
|
27452
28236
|
drawAvatarFrame(context, size, palette);
|
|
27453
28237
|
drawOctopus3Atmosphere(context, size, palette, centerX, centerY, timeMs, shapePhase);
|
|
@@ -27493,11 +28277,11 @@ const octopus3AvatarVisual = {
|
|
|
27493
28277
|
context.ellipse(centerX, centerY - size * 0.14, size * 0.18, size * 0.062, 0, Math.PI, Math.PI * 2);
|
|
27494
28278
|
context.fillStyle = `${palette.highlight}3d`;
|
|
27495
28279
|
context.fill();
|
|
27496
|
-
drawSeededEye(context, centerX - eyeSpacing, centerY - size * 0.01, eyeRadiusX, eyeRadiusY, (staticRandom() - 0.5) * 0.28, palette, timeMs, shapePhase);
|
|
27497
|
-
drawSeededEye(context, centerX + eyeSpacing, centerY - size * 0.01, eyeRadiusX, eyeRadiusY, (staticRandom() - 0.5) * 0.28, palette, timeMs, shapePhase + Math.PI / 4);
|
|
28280
|
+
drawSeededEye(context, centerX - eyeSpacing, centerY - size * 0.01, eyeRadiusX, eyeRadiusY, (staticRandom() - 0.5) * 0.28, palette, timeMs, shapePhase, interaction);
|
|
28281
|
+
drawSeededEye(context, centerX + eyeSpacing, centerY - size * 0.01, eyeRadiusX, eyeRadiusY, (staticRandom() - 0.5) * 0.28, palette, timeMs, shapePhase + Math.PI / 4, interaction);
|
|
27498
28282
|
context.beginPath();
|
|
27499
28283
|
context.moveTo(centerX - size * 0.07, centerY + size * 0.09);
|
|
27500
|
-
context.quadraticCurveTo(centerX, centerY + size * (0.14 + Math.sin(timeMs / 620 + shapePhase) * 0.016), centerX + size * 0.07, centerY + size * 0.09);
|
|
28284
|
+
context.quadraticCurveTo(centerX, centerY + size * (0.14 + Math.sin(timeMs / 620 + shapePhase) * 0.016) + interaction.gazeY * size * 0.012, centerX + size * 0.07, centerY + size * 0.09);
|
|
27501
28285
|
context.strokeStyle = `${palette.ink}b3`;
|
|
27502
28286
|
context.lineWidth = size * 0.012;
|
|
27503
28287
|
context.lineCap = 'round';
|
|
@@ -27685,12 +28469,18 @@ function drawMantleNodes(context, centerX, centerY, size, palette, createRandom)
|
|
|
27685
28469
|
* @param palette Derived avatar palette.
|
|
27686
28470
|
* @param timeMs Current animation time in milliseconds.
|
|
27687
28471
|
* @param phase Seed-based animation phase.
|
|
28472
|
+
* @param interaction Smoothed avatar interaction state.
|
|
27688
28473
|
*
|
|
27689
28474
|
* @private helper of `octopus3AvatarVisual`
|
|
27690
28475
|
*/
|
|
27691
|
-
function drawSeededEye(context, centerX, centerY, radiusX, radiusY, rotation, palette, timeMs, phase) {
|
|
27692
|
-
const pupilOffsetX =
|
|
27693
|
-
|
|
28476
|
+
function drawSeededEye(context, centerX, centerY, radiusX, radiusY, rotation, palette, timeMs, phase, interaction) {
|
|
28477
|
+
const { pupilOffsetX, pupilOffsetY } = resolveOrganicEyeMotion({
|
|
28478
|
+
radiusX,
|
|
28479
|
+
radiusY,
|
|
28480
|
+
timeMs,
|
|
28481
|
+
phase,
|
|
28482
|
+
interaction,
|
|
28483
|
+
});
|
|
27694
28484
|
context.save();
|
|
27695
28485
|
context.translate(centerX, centerY);
|
|
27696
28486
|
context.rotate(rotation);
|
|
@@ -27726,7 +28516,7 @@ function drawSeededEye(context, centerX, centerY, radiusX, radiusY, rotation, pa
|
|
|
27726
28516
|
context.stroke();
|
|
27727
28517
|
context.beginPath();
|
|
27728
28518
|
context.moveTo(-radiusX * 0.88, -radiusY * 0.08);
|
|
27729
|
-
context.quadraticCurveTo(0, -radiusY * 0.9, radiusX * 0.88, -radiusY * 0.08);
|
|
28519
|
+
context.quadraticCurveTo(0, -radiusY * (0.9 - interaction.gazeY * 0.16 + interaction.intensity * 0.08), radiusX * 0.88, -radiusY * 0.08);
|
|
27730
28520
|
context.strokeStyle = `${palette.shadow}73`;
|
|
27731
28521
|
context.lineWidth = radiusX * 0.16;
|
|
27732
28522
|
context.lineCap = 'round';
|
|
@@ -27864,30 +28654,56 @@ function getAvatarVisualById(visualId) {
|
|
|
27864
28654
|
return avatarVisual;
|
|
27865
28655
|
}
|
|
27866
28656
|
|
|
28657
|
+
/**
|
|
28658
|
+
* Resolves the stable avatar render inputs reused across multiple frames.
|
|
28659
|
+
*
|
|
28660
|
+
* @param options Avatar identity and visual selection.
|
|
28661
|
+
* @returns Stable render data ready to be used by `renderAvatarVisual`.
|
|
28662
|
+
*
|
|
28663
|
+
* @private shared helper for canvas avatar rendering
|
|
28664
|
+
*/
|
|
28665
|
+
function resolveAvatarRenderDefinition(options) {
|
|
28666
|
+
const avatarDefinition = normalizeAvatarDefinition(options.avatarDefinition);
|
|
28667
|
+
const surface = options.surface || 'framed';
|
|
28668
|
+
return {
|
|
28669
|
+
avatarDefinition,
|
|
28670
|
+
avatarVisual: getAvatarVisualById(options.visualId),
|
|
28671
|
+
surface,
|
|
28672
|
+
palette: createAvatarPalette(avatarDefinition, surface),
|
|
28673
|
+
createRandom: createAvatarRandomFactory(avatarDefinition),
|
|
28674
|
+
};
|
|
28675
|
+
}
|
|
27867
28676
|
/**
|
|
27868
28677
|
* Renders one deterministic avatar frame into the provided canvas.
|
|
27869
28678
|
*
|
|
27870
28679
|
* @param options Rendering options.
|
|
28680
|
+
* @param resolvedAvatarRenderDefinition Optional stable render data reused between frames.
|
|
27871
28681
|
*
|
|
27872
28682
|
* @private shared helper for canvas avatar rendering
|
|
27873
28683
|
*/
|
|
27874
|
-
function renderAvatarVisual(options) {
|
|
27875
|
-
const
|
|
27876
|
-
|
|
28684
|
+
function renderAvatarVisual(options, resolvedAvatarRenderDefinition) {
|
|
28685
|
+
const resolvedRenderDefinition = resolvedAvatarRenderDefinition ||
|
|
28686
|
+
resolveAvatarRenderDefinition({
|
|
28687
|
+
avatarDefinition: options.avatarDefinition,
|
|
28688
|
+
visualId: options.visualId,
|
|
28689
|
+
surface: options.surface,
|
|
28690
|
+
});
|
|
27877
28691
|
const context = options.canvas.getContext('2d');
|
|
27878
28692
|
if (!context) {
|
|
27879
28693
|
throw new Error('2D canvas rendering context is unavailable.');
|
|
27880
28694
|
}
|
|
27881
28695
|
prepareAvatarCanvas(options.canvas, context, options.size, options.devicePixelRatio || 1);
|
|
27882
|
-
avatarVisual.render({
|
|
28696
|
+
resolvedRenderDefinition.avatarVisual.render({
|
|
27883
28697
|
canvas: options.canvas,
|
|
27884
28698
|
context,
|
|
27885
28699
|
size: options.size,
|
|
27886
28700
|
devicePixelRatio: options.devicePixelRatio || 1,
|
|
27887
28701
|
timeMs: options.timeMs,
|
|
27888
|
-
avatarDefinition:
|
|
27889
|
-
palette:
|
|
27890
|
-
createRandom:
|
|
28702
|
+
avatarDefinition: resolvedRenderDefinition.avatarDefinition,
|
|
28703
|
+
palette: resolvedRenderDefinition.palette,
|
|
28704
|
+
createRandom: resolvedRenderDefinition.createRandom,
|
|
28705
|
+
surface: resolvedRenderDefinition.surface,
|
|
28706
|
+
interaction: options.interaction || createIdleAvatarInteractionState(),
|
|
27891
28707
|
});
|
|
27892
28708
|
}
|
|
27893
28709
|
|
|
@@ -27897,48 +28713,136 @@ function renderAvatarVisual(options) {
|
|
|
27897
28713
|
* @private helper of `<Avatar/>`
|
|
27898
28714
|
*/
|
|
27899
28715
|
const AVATAR_CANVAS_RADIUS_RATIO = 0.18;
|
|
28716
|
+
/**
|
|
28717
|
+
* Maximum time between layout-bound refreshes while pointer tracking is active.
|
|
28718
|
+
*
|
|
28719
|
+
* This keeps pointer-aware visuals aligned with chat/layout shifts without forcing a layout read every frame.
|
|
28720
|
+
*
|
|
28721
|
+
* @private helper of `<Avatar/>`
|
|
28722
|
+
*/
|
|
28723
|
+
const ACTIVE_POINTER_BOUNDS_REFRESH_MS = 120;
|
|
27900
28724
|
/**
|
|
27901
28725
|
* Canvas-based deterministic avatar component.
|
|
27902
28726
|
*
|
|
27903
28727
|
* @private shared component for in-repository avatar previews
|
|
27904
28728
|
*/
|
|
27905
28729
|
function Avatar(props) {
|
|
27906
|
-
const { avatarDefinition, visualId, size = DEFAULT_AVATAR_SIZE, title, className, style } = props;
|
|
28730
|
+
const { avatarDefinition, visualId, surface = 'framed', size = DEFAULT_AVATAR_SIZE, title, className, style } = props;
|
|
27907
28731
|
const canvasRef = useRef(null);
|
|
27908
|
-
const
|
|
27909
|
-
const
|
|
28732
|
+
const animationStartRef = useRef(null);
|
|
28733
|
+
const interactionRuntimeStateRef = useRef(createAvatarInteractionRuntimeState());
|
|
28734
|
+
const avatarBoundsRef = useRef(null);
|
|
28735
|
+
const lastResolvedPointerVersionRef = useRef(-1);
|
|
28736
|
+
const lastResolvedViewportLayoutVersionRef = useRef(-1);
|
|
28737
|
+
const lastAvatarBoundsRefreshAtRef = useRef(0);
|
|
28738
|
+
const avatarColorsKey = avatarDefinition.colors.join('|');
|
|
28739
|
+
const resolvedAvatarRenderDefinition = useMemo(() => resolveAvatarRenderDefinition({
|
|
28740
|
+
avatarDefinition,
|
|
28741
|
+
visualId,
|
|
28742
|
+
surface,
|
|
28743
|
+
}), [avatarDefinition.agentHash, avatarDefinition.agentName, avatarColorsKey, surface, visualId]);
|
|
28744
|
+
const avatarDefinitionKey = useMemo(() => createAvatarDefinitionKey(resolvedAvatarRenderDefinition.avatarDefinition), [
|
|
28745
|
+
resolvedAvatarRenderDefinition.avatarDefinition.agentHash,
|
|
28746
|
+
resolvedAvatarRenderDefinition.avatarDefinition.agentName,
|
|
28747
|
+
resolvedAvatarRenderDefinition.avatarDefinition.colors.join('|'),
|
|
28748
|
+
]);
|
|
28749
|
+
const [isVisible, setIsVisible] = useState(true);
|
|
27910
28750
|
useEffect(() => {
|
|
27911
28751
|
const canvas = canvasRef.current;
|
|
27912
28752
|
if (!canvas) {
|
|
27913
28753
|
throw new Error('Avatar canvas is not mounted.');
|
|
27914
28754
|
}
|
|
27915
|
-
|
|
27916
|
-
|
|
28755
|
+
return observeAvatarVisibility(canvas, setIsVisible);
|
|
28756
|
+
}, []);
|
|
28757
|
+
useEffect(() => {
|
|
28758
|
+
interactionRuntimeStateRef.current = createAvatarInteractionRuntimeState();
|
|
28759
|
+
avatarBoundsRef.current = null;
|
|
28760
|
+
lastResolvedPointerVersionRef.current = -1;
|
|
28761
|
+
lastResolvedViewportLayoutVersionRef.current = -1;
|
|
28762
|
+
lastAvatarBoundsRefreshAtRef.current = 0;
|
|
28763
|
+
}, [avatarDefinitionKey, isVisible, visualId]);
|
|
28764
|
+
useEffect(() => {
|
|
28765
|
+
const canvas = canvasRef.current;
|
|
28766
|
+
if (!canvas || typeof ResizeObserver === 'undefined') {
|
|
28767
|
+
return;
|
|
28768
|
+
}
|
|
28769
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
28770
|
+
avatarBoundsRef.current = null;
|
|
28771
|
+
});
|
|
28772
|
+
resizeObserver.observe(canvas);
|
|
28773
|
+
return () => {
|
|
28774
|
+
resizeObserver.disconnect();
|
|
28775
|
+
};
|
|
28776
|
+
}, []);
|
|
28777
|
+
useEffect(() => {
|
|
28778
|
+
const canvas = canvasRef.current;
|
|
28779
|
+
if (!canvas) {
|
|
28780
|
+
throw new Error('Avatar canvas is not mounted.');
|
|
28781
|
+
}
|
|
28782
|
+
const avatarVisual = resolvedAvatarRenderDefinition.avatarVisual;
|
|
28783
|
+
const isDynamicAvatar = avatarVisual.isAnimated || avatarVisual.supportsPointerTracking === true;
|
|
28784
|
+
const shouldTrackPointer = avatarVisual.supportsPointerTracking === true && isVisible;
|
|
28785
|
+
const releasePointerTracking = shouldTrackPointer ? retainAvatarPointerTracking() : null;
|
|
28786
|
+
if (animationStartRef.current === null) {
|
|
28787
|
+
animationStartRef.current = performance.now();
|
|
28788
|
+
}
|
|
27917
28789
|
const renderFrame = (now) => {
|
|
28790
|
+
const pointerSnapshot = avatarVisual.supportsPointerTracking ? getAvatarPointerSnapshot() : null;
|
|
28791
|
+
let interactionState = createIdleAvatarInteractionState();
|
|
28792
|
+
if (avatarVisual.supportsPointerTracking && pointerSnapshot) {
|
|
28793
|
+
const pointerSnapshotVersion = getAvatarPointerSnapshotVersion();
|
|
28794
|
+
const viewportLayoutVersion = getAvatarViewportLayoutVersion();
|
|
28795
|
+
if (avatarBoundsRef.current === null ||
|
|
28796
|
+
lastResolvedPointerVersionRef.current !== pointerSnapshotVersion ||
|
|
28797
|
+
lastResolvedViewportLayoutVersionRef.current !== viewportLayoutVersion ||
|
|
28798
|
+
now - lastAvatarBoundsRefreshAtRef.current >= ACTIVE_POINTER_BOUNDS_REFRESH_MS) {
|
|
28799
|
+
avatarBoundsRef.current = canvas.getBoundingClientRect();
|
|
28800
|
+
lastResolvedPointerVersionRef.current = pointerSnapshotVersion;
|
|
28801
|
+
lastResolvedViewportLayoutVersionRef.current = viewportLayoutVersion;
|
|
28802
|
+
lastAvatarBoundsRefreshAtRef.current = now;
|
|
28803
|
+
}
|
|
28804
|
+
interactionRuntimeStateRef.current = stepAvatarInteractionRuntimeState(interactionRuntimeStateRef.current, resolveAvatarPointerTarget(avatarBoundsRef.current, pointerSnapshot), now);
|
|
28805
|
+
interactionState = interactionRuntimeStateRef.current;
|
|
28806
|
+
}
|
|
28807
|
+
else if (avatarVisual.supportsPointerTracking) {
|
|
28808
|
+
interactionRuntimeStateRef.current = stepAvatarInteractionRuntimeState(interactionRuntimeStateRef.current, createIdleAvatarInteractionState(), now);
|
|
28809
|
+
interactionState = interactionRuntimeStateRef.current;
|
|
28810
|
+
}
|
|
27918
28811
|
renderAvatarVisual({
|
|
27919
28812
|
canvas,
|
|
27920
|
-
avatarDefinition:
|
|
28813
|
+
avatarDefinition: resolvedAvatarRenderDefinition.avatarDefinition,
|
|
27921
28814
|
visualId,
|
|
28815
|
+
surface,
|
|
27922
28816
|
size,
|
|
27923
|
-
timeMs: now -
|
|
28817
|
+
timeMs: now - animationStartRef.current,
|
|
27924
28818
|
devicePixelRatio: window.devicePixelRatio || 1,
|
|
27925
|
-
|
|
27926
|
-
|
|
27927
|
-
animationFrameId = window.requestAnimationFrame(renderFrame);
|
|
27928
|
-
}
|
|
28819
|
+
interaction: interactionState,
|
|
28820
|
+
}, resolvedAvatarRenderDefinition);
|
|
27929
28821
|
};
|
|
27930
|
-
renderFrame(
|
|
28822
|
+
renderFrame(performance.now());
|
|
28823
|
+
if (!isDynamicAvatar || !isVisible) {
|
|
28824
|
+
return () => {
|
|
28825
|
+
releasePointerTracking === null || releasePointerTracking === void 0 ? void 0 : releasePointerTracking();
|
|
28826
|
+
};
|
|
28827
|
+
}
|
|
28828
|
+
const releaseAnimationListener = retainAvatarAnimationListener(renderFrame);
|
|
27931
28829
|
return () => {
|
|
27932
|
-
|
|
27933
|
-
|
|
27934
|
-
}
|
|
28830
|
+
releaseAnimationListener();
|
|
28831
|
+
releasePointerTracking === null || releasePointerTracking === void 0 ? void 0 : releasePointerTracking();
|
|
27935
28832
|
};
|
|
27936
|
-
}, [
|
|
27937
|
-
|
|
28833
|
+
}, [
|
|
28834
|
+
avatarDefinitionKey,
|
|
28835
|
+
isVisible,
|
|
28836
|
+
resolvedAvatarRenderDefinition,
|
|
28837
|
+
size,
|
|
28838
|
+
surface,
|
|
28839
|
+
visualId,
|
|
28840
|
+
]);
|
|
28841
|
+
return (jsx("canvas", { ref: canvasRef, title: title || `${resolvedAvatarRenderDefinition.avatarDefinition.agentName} avatar`, className: className, style: {
|
|
27938
28842
|
width: size,
|
|
27939
28843
|
height: size,
|
|
27940
28844
|
display: 'block',
|
|
27941
|
-
borderRadius: size * AVATAR_CANVAS_RADIUS_RATIO,
|
|
28845
|
+
borderRadius: surface === 'transparent' ? 0 : size * AVATAR_CANVAS_RADIUS_RATIO,
|
|
27942
28846
|
...style,
|
|
27943
28847
|
} }));
|
|
27944
28848
|
}
|
|
@@ -27949,9 +28853,9 @@ function Avatar(props) {
|
|
|
27949
28853
|
* @private shared component for avatar media rendering
|
|
27950
28854
|
*/
|
|
27951
28855
|
function AvatarOrImage(props) {
|
|
27952
|
-
const { imageUrl, avatarDefinition, visualId, size, alt, className, style } = props;
|
|
28856
|
+
const { imageUrl, avatarDefinition, visualId, surface, size, alt, className, style } = props;
|
|
27953
28857
|
if (avatarDefinition && visualId) {
|
|
27954
|
-
return (jsx(Avatar, { avatarDefinition: avatarDefinition, visualId: visualId, size: size, title: alt, className: className, style: style }));
|
|
28858
|
+
return (jsx(Avatar, { avatarDefinition: avatarDefinition, visualId: visualId, surface: surface, size: size, title: alt, className: className, style: style }));
|
|
27955
28859
|
}
|
|
27956
28860
|
if (!imageUrl) {
|
|
27957
28861
|
return null;
|
|
@@ -38559,6 +39463,14 @@ class OpenAiVectorStoreHandler extends OpenAiExecutionTools {
|
|
|
38559
39463
|
* Constant for default agent kit model name.
|
|
38560
39464
|
*/
|
|
38561
39465
|
const DEFAULT_AGENT_KIT_MODEL_NAME = 'gpt-5.4-mini';
|
|
39466
|
+
/**
|
|
39467
|
+
* Default model used for nested DeepSearch tool invocations.
|
|
39468
|
+
*/
|
|
39469
|
+
const DEFAULT_DEEP_SEARCH_MODEL_NAME = 'o4-mini-deep-research';
|
|
39470
|
+
/**
|
|
39471
|
+
* Tool name used by the Book commitment-backed DeepSearch capability.
|
|
39472
|
+
*/
|
|
39473
|
+
const DEEP_SEARCH_TOOL_NAME = 'deep_search';
|
|
38562
39474
|
/**
|
|
38563
39475
|
* Creates one structured log entry for streamed tool-call updates.
|
|
38564
39476
|
*
|
|
@@ -38601,6 +39513,98 @@ function resolveFinalToolCallState$1(options) {
|
|
|
38601
39513
|
}
|
|
38602
39514
|
return 'COMPLETE';
|
|
38603
39515
|
}
|
|
39516
|
+
/**
|
|
39517
|
+
* Returns true when one tool definition represents the dedicated DeepSearch capability.
|
|
39518
|
+
*
|
|
39519
|
+
* @param toolDefinition - Tool definition from compiled model requirements.
|
|
39520
|
+
* @returns `true` when the tool should be backed by a nested deep-research agent.
|
|
39521
|
+
*
|
|
39522
|
+
* @private helper of `OpenAiAgentKitExecutionTools`
|
|
39523
|
+
*/
|
|
39524
|
+
function isDeepSearchToolDefinition(toolDefinition) {
|
|
39525
|
+
return toolDefinition.name === DEEP_SEARCH_TOOL_NAME;
|
|
39526
|
+
}
|
|
39527
|
+
/**
|
|
39528
|
+
* Normalizes Promptbook JSON-schema tool parameters for AgentKit function tools.
|
|
39529
|
+
*
|
|
39530
|
+
* @param parameters - Promptbook tool parameters.
|
|
39531
|
+
* @returns AgentKit-compatible JSON schema or `undefined`.
|
|
39532
|
+
*
|
|
39533
|
+
* @private helper of `OpenAiAgentKitExecutionTools`
|
|
39534
|
+
*/
|
|
39535
|
+
function normalizeAgentKitToolParameters(parameters) {
|
|
39536
|
+
var _a, _b;
|
|
39537
|
+
if (!parameters) {
|
|
39538
|
+
return undefined;
|
|
39539
|
+
}
|
|
39540
|
+
return {
|
|
39541
|
+
...parameters,
|
|
39542
|
+
additionalProperties: (_a = parameters.additionalProperties) !== null && _a !== void 0 ? _a : false,
|
|
39543
|
+
required: (_b = parameters.required) !== null && _b !== void 0 ? _b : [],
|
|
39544
|
+
};
|
|
39545
|
+
}
|
|
39546
|
+
/**
|
|
39547
|
+
* Creates instructions for the nested DeepSearch specialist agent.
|
|
39548
|
+
*
|
|
39549
|
+
* @param toolDescription - Model-facing description from the original tool definition.
|
|
39550
|
+
* @returns System instructions for the nested deep-research agent.
|
|
39551
|
+
*
|
|
39552
|
+
* @private helper of `OpenAiAgentKitExecutionTools`
|
|
39553
|
+
*/
|
|
39554
|
+
function createDeepSearchAgentInstructions(toolDescription) {
|
|
39555
|
+
const normalizedDescription = toolDescription.trim();
|
|
39556
|
+
return spaceTrim$1((block) => `
|
|
39557
|
+
You are a DeepSearch specialist working as a tool for another agent.
|
|
39558
|
+
Perform thorough, source-grounded public-web research based on the provided request.
|
|
39559
|
+
Use web search to gather current information, compare relevant viewpoints, and synthesize a concise research brief.
|
|
39560
|
+
Do not ask follow-up questions. If the request is not specific enough, state the assumptions you had to make.
|
|
39561
|
+
Include citations in the research brief whenever sources were used.
|
|
39562
|
+
${block(normalizedDescription ? `Tool guidance:\n${normalizedDescription}` : '')}
|
|
39563
|
+
`);
|
|
39564
|
+
}
|
|
39565
|
+
/**
|
|
39566
|
+
* Builds the nested DeepSearch prompt from structured tool arguments.
|
|
39567
|
+
*
|
|
39568
|
+
* @param rawInput - Parsed function-tool arguments provided by the outer agent.
|
|
39569
|
+
* @returns Prompt text passed to the nested deep-research agent.
|
|
39570
|
+
*
|
|
39571
|
+
* @private helper of `OpenAiAgentKitExecutionTools`
|
|
39572
|
+
*/
|
|
39573
|
+
function buildDeepSearchToolInput(rawInput) {
|
|
39574
|
+
const input = rawInput && typeof rawInput === 'object' ? rawInput : {};
|
|
39575
|
+
const query = typeof input.query === 'string' ? input.query.trim() : '';
|
|
39576
|
+
const additionalHints = Object.entries(input)
|
|
39577
|
+
.filter(([key, value]) => key !== 'query' && value !== undefined && value !== null && String(value).trim() !== '')
|
|
39578
|
+
.map(([key, value]) => `- ${key}: ${typeof value === 'string' ? value : JSON.stringify(value)}`);
|
|
39579
|
+
return spaceTrim$1((block) => `
|
|
39580
|
+
Research request:
|
|
39581
|
+
${query || JSON.stringify(input)}
|
|
39582
|
+
${block(additionalHints.length > 0 ? `Execution hints:\n${additionalHints.join('\n')}` : '')}
|
|
39583
|
+
`);
|
|
39584
|
+
}
|
|
39585
|
+
/**
|
|
39586
|
+
* Creates the native Agent SDK tool used for `USE DEEPSEARCH`.
|
|
39587
|
+
*
|
|
39588
|
+
* @param toolDefinition - Promptbook tool definition for `deep_search`.
|
|
39589
|
+
* @returns AgentKit tool backed by a nested deep-research agent.
|
|
39590
|
+
*
|
|
39591
|
+
* @private helper of `OpenAiAgentKitExecutionTools`
|
|
39592
|
+
*/
|
|
39593
|
+
function createDeepSearchAgentKitTool(toolDefinition) {
|
|
39594
|
+
const deepSearchAgent = new Agent$1({
|
|
39595
|
+
name: 'DeepSearch',
|
|
39596
|
+
model: DEFAULT_DEEP_SEARCH_MODEL_NAME,
|
|
39597
|
+
instructions: createDeepSearchAgentInstructions(toolDefinition.description),
|
|
39598
|
+
tools: [webSearchTool({ searchContextSize: 'high' })],
|
|
39599
|
+
});
|
|
39600
|
+
return deepSearchAgent.asTool({
|
|
39601
|
+
toolName: toolDefinition.name,
|
|
39602
|
+
toolDescription: toolDefinition.description,
|
|
39603
|
+
parameters: normalizeAgentKitToolParameters(toolDefinition.parameters),
|
|
39604
|
+
inputBuilder: ({ params }) => buildDeepSearchToolInput(params),
|
|
39605
|
+
customOutputExtractor: (result) => { var _a; return typeof result.finalOutput === 'string' ? result.finalOutput : JSON.stringify((_a = result.finalOutput) !== null && _a !== void 0 ? _a : ''); },
|
|
39606
|
+
});
|
|
39607
|
+
}
|
|
38604
39608
|
/**
|
|
38605
39609
|
* Constant for default JSON schema name.
|
|
38606
39610
|
*/
|
|
@@ -38941,25 +39945,23 @@ class OpenAiAgentKitExecutionTools extends OpenAiVectorStoreHandler {
|
|
|
38941
39945
|
* Builds the tool list for AgentKit, including hosted file search when applicable.
|
|
38942
39946
|
*/
|
|
38943
39947
|
buildAgentKitTools(options) {
|
|
38944
|
-
var _a;
|
|
38945
39948
|
const { tools, vectorStoreId } = options;
|
|
38946
39949
|
const agentKitTools = [];
|
|
38947
39950
|
if (vectorStoreId) {
|
|
38948
39951
|
agentKitTools.push(fileSearchTool(vectorStoreId));
|
|
38949
39952
|
}
|
|
38950
39953
|
if (tools && tools.length > 0) {
|
|
38951
|
-
|
|
39954
|
+
let scriptTools = null;
|
|
38952
39955
|
for (const toolDefinition of tools) {
|
|
39956
|
+
if (isDeepSearchToolDefinition(toolDefinition)) {
|
|
39957
|
+
agentKitTools.push(createDeepSearchAgentKitTool(toolDefinition));
|
|
39958
|
+
continue;
|
|
39959
|
+
}
|
|
39960
|
+
scriptTools !== null && scriptTools !== void 0 ? scriptTools : (scriptTools = this.resolveScriptTools());
|
|
38953
39961
|
agentKitTools.push(tool({
|
|
38954
39962
|
name: toolDefinition.name,
|
|
38955
39963
|
description: toolDefinition.description,
|
|
38956
|
-
parameters: toolDefinition.parameters
|
|
38957
|
-
? {
|
|
38958
|
-
...toolDefinition.parameters,
|
|
38959
|
-
additionalProperties: false,
|
|
38960
|
-
required: (_a = toolDefinition.parameters.required) !== null && _a !== void 0 ? _a : [],
|
|
38961
|
-
}
|
|
38962
|
-
: undefined,
|
|
39964
|
+
parameters: normalizeAgentKitToolParameters(toolDefinition.parameters),
|
|
38963
39965
|
strict: false,
|
|
38964
39966
|
execute: async (input, runContext, details) => {
|
|
38965
39967
|
var _a, _b, _c, _d;
|
|
@@ -45146,6 +46148,7 @@ const TOOL_TITLES = {
|
|
|
45146
46148
|
delete_wallet_record: { title: 'Deleting wallet', emoji: '👛' },
|
|
45147
46149
|
request_wallet_record: { title: 'Requesting wallet', emoji: '👛' },
|
|
45148
46150
|
web_search: { title: 'Searching the web', emoji: '🔎' },
|
|
46151
|
+
deep_search: { title: 'Deep research', emoji: '🔬' },
|
|
45149
46152
|
useSearchEngine: { title: 'Searching the web', emoji: '🔎' },
|
|
45150
46153
|
search: { title: 'Searching the web', emoji: '🔎' },
|
|
45151
46154
|
useBrowser: { title: 'Browsing the web', emoji: '🌐' },
|
|
@@ -48107,7 +49110,7 @@ function renderGenericToolCallDetails(options) {
|
|
|
48107
49110
|
* @private function of ChatToolCallModal
|
|
48108
49111
|
*/
|
|
48109
49112
|
function isSearchToolCallName(toolName) {
|
|
48110
|
-
return toolName === 'web_search' || toolName === 'useSearchEngine' || toolName === 'search';
|
|
49113
|
+
return toolName === 'web_search' || toolName === 'deep_search' || toolName === 'useSearchEngine' || toolName === 'search';
|
|
48111
49114
|
}
|
|
48112
49115
|
/**
|
|
48113
49116
|
* Checks whether a tool name should use the time renderer.
|
|
@@ -48843,7 +49846,12 @@ function TeamToolCallModalContent(options) {
|
|
|
48843
49846
|
avatarSrc: resolvedTeammateAvatar || undefined,
|
|
48844
49847
|
},
|
|
48845
49848
|
];
|
|
48846
|
-
return (jsxs(Fragment, { children: [jsx("div", { className: classNames(styles$5.searchModalHeader, styles$5.teamModalHeader), children: jsxs("div", { className: styles$5.teamHeaderParticipants, children: [jsx(TeamHeaderProfile, { label: resolvedAgentLabel, avatarSrc: resolvedAgentAvatar, avatarDefinition: resolvedAgentAvatarDefinition, avatarVisualId: resolvedAgentAvatarVisualId, fallbackColor: resolvedAgentHeaderColor }), jsx("span", { className: styles$5.teamHeaderDivider, children: "talking with" }), jsx(TeamHeaderProfile, { label: resolvedTeammateLabel, avatarSrc: resolvedTeammateAvatar, fallbackColor: "#0ea5e9", href: teammateLink })] }) }), jsxs("div", { className: styles$5.searchModalContent, children: [messages.length > 0 ? (jsx("div", { className: styles$5.teamChatContainer, children: jsx(MockedChat, { title: `Chat between ${resolvedAgentLabel} and ${resolvedTeammateLabel}`, messages: messages, participants: participants, isResettable: false, isPausable: false, isSaveButtonEnabled: false, isCopyButtonEnabled: false,
|
|
49849
|
+
return (jsxs(Fragment, { children: [jsx("div", { className: classNames(styles$5.searchModalHeader, styles$5.teamModalHeader), children: jsxs("div", { className: styles$5.teamHeaderParticipants, children: [jsx(TeamHeaderProfile, { label: resolvedAgentLabel, avatarSrc: resolvedAgentAvatar, avatarDefinition: resolvedAgentAvatarDefinition, avatarVisualId: resolvedAgentAvatarVisualId, fallbackColor: resolvedAgentHeaderColor }), jsx("span", { className: styles$5.teamHeaderDivider, children: "talking with" }), jsx(TeamHeaderProfile, { label: resolvedTeammateLabel, avatarSrc: resolvedTeammateAvatar, fallbackColor: "#0ea5e9", href: teammateLink })] }) }), jsxs("div", { className: styles$5.searchModalContent, children: [messages.length > 0 ? (jsx("div", { className: styles$5.teamChatContainer, children: jsx(MockedChat, { title: `Chat between ${resolvedAgentLabel} and ${resolvedTeammateLabel}`, messages: messages, participants: participants, isResettable: false, isPausable: false, isSaveButtonEnabled: false, isCopyButtonEnabled: false, layout: "STANDALONE", delayConfig: {
|
|
49850
|
+
// Note+TODO: For some strange reason, <MockedChat/> is not running and stays static on the initial frame, so doing this hack to force it to show the entire chat at once. Need to investigate why the animation is not running as expected and then just use `delayConfig={FAST_FLOW}`
|
|
49851
|
+
...FAST_FLOW,
|
|
49852
|
+
beforeFirstMessage: 0,
|
|
49853
|
+
showIntermediateMessages: messages.length,
|
|
49854
|
+
}, visualMode: "BUBBLE_MODE" }) })) : (jsx("div", { className: styles$5.noResults, children: "No teammate conversation available." })), (hasTeamToolCalls || hasTeamCitations) && (jsxs("div", { className: styles$5.teamToolCallSection, children: [hasTeamToolCalls && (jsxs("div", { className: styles$5.teamToolCallGroup, children: [jsx("div", { className: styles$5.teamToolCallHeading, children: "Actions" }), jsx("div", { className: styles$5.teamToolCallChips, children: teamToolCalls.map((toolCallEntry, index) => {
|
|
48847
49855
|
const chipletInfo = getToolCallChipletInfo(toolCallEntry.toolCall, undefined, toolTitles);
|
|
48848
49856
|
const chipletText = buildToolCallChipText(chipletInfo);
|
|
48849
49857
|
return (jsxs("button", { className: styles$5.completedToolCall, onClick: () => {
|
|
@@ -49782,7 +50790,7 @@ function hasChatActions(postprocessedMessages, { onReset, newChatButtonHref, onU
|
|
|
49782
50790
|
* @public exported from `@promptbook/components`
|
|
49783
50791
|
*/
|
|
49784
50792
|
function Chat(props) {
|
|
49785
|
-
const { title = 'Chat', messages, onChange, onMessage, onActionButton, onQuickMessageButton, onReplyToMessage, onCancelReply, onReset, resetRequiresConfirmation = true, newChatButtonHref, onFeedback, feedbackMode = 'stars', feedbackTranslations, timingTranslations, onFileUpload, chatLocale, speechRecognition, placeholderMessageContent, defaultMessage, enterBehavior, resolveEnterBehavior, children, className, style, isAiTextHumanizedAndPromptbookified = true, isVoiceCalling = false, isFocusedOnLoad, participants = [], canReplyToMessage, replyingToMessage, extraActions, actionsContainer, saveFormats, isSaveButtonEnabled = true, isCopyButtonEnabled = true, buttonColor: buttonColorRaw, onUseTemplate, onCreateAgent, toolTitles, teammates, teamAgentProfiles,
|
|
50793
|
+
const { title = 'Chat', messages, onChange, onMessage, onActionButton, onQuickMessageButton, onReplyToMessage, onCancelReply, onReset, resetRequiresConfirmation = true, newChatButtonHref, onFeedback, feedbackMode = 'stars', feedbackTranslations, timingTranslations, onFileUpload, chatLocale, speechRecognition, placeholderMessageContent, defaultMessage, enterBehavior, resolveEnterBehavior, children, className, style, isAiTextHumanizedAndPromptbookified = true, isVoiceCalling = false, isFocusedOnLoad, participants = [], canReplyToMessage, replyingToMessage, extraActions, actionsContainer, saveFormats, isSaveButtonEnabled = true, isCopyButtonEnabled = true, buttonColor: buttonColorRaw, onUseTemplate, onCreateAgent, toolTitles, teammates, teamAgentProfiles, layout, visualMode = 'ARTICLE_MODE', effectConfigs, soundSystem, speechRecognitionLanguage, isSpeechPlaybackEnabled = true, elevenLabsVoiceId, chatUiTranslations, } = props;
|
|
49786
50794
|
const buttonColor = useMemo(() => Color.from(buttonColorRaw || '#0066cc'), [buttonColorRaw]);
|
|
49787
50795
|
const agentParticipant = useMemo(() => participants.find((participant) => participant.name === 'AGENT'), [participants]);
|
|
49788
50796
|
const postprocessedMessages = useChatPostprocessedMessages({
|
|
@@ -49823,11 +50831,11 @@ function Chat(props) {
|
|
|
49823
50831
|
extraActions,
|
|
49824
50832
|
isSaveButtonEnabled,
|
|
49825
50833
|
});
|
|
49826
|
-
const isConstrainedArticleMode = visualMode === 'ARTICLE_MODE' &&
|
|
50834
|
+
const isConstrainedArticleMode = visualMode === 'ARTICLE_MODE' && layout === 'FULL_PAGE';
|
|
49827
50835
|
useChatCompleteNotification(messages, soundSystem);
|
|
49828
50836
|
return (jsxs(Fragment, { children: [feedbackStatus && (jsx("div", { className: classNames(styles$5.feedbackStatus, feedbackStatus.variant === 'success'
|
|
49829
50837
|
? styles$5.feedbackStatusSuccess
|
|
49830
|
-
: styles$5.feedbackStatusError), "aria-live": "polite", role: "status", children: feedbackStatus.message })), effectConfigs && effectConfigs.length > 0 && (jsx(ChatEffectsSystem, { messages: postprocessedMessages, effectConfigs: effectConfigs, soundSystem: soundSystem })), jsx("div", { className: classNames(className, styles$5.Chat,
|
|
50838
|
+
: styles$5.feedbackStatusError), "aria-live": "polite", role: "status", children: feedbackStatus.message })), effectConfigs && effectConfigs.length > 0 && (jsx(ChatEffectsSystem, { messages: postprocessedMessages, effectConfigs: effectConfigs, soundSystem: soundSystem })), jsx("div", { className: classNames(className, styles$5.Chat, layout === 'STANDALONE' && styles$5.standaloneVisual, layout === 'FULL_PAGE' && styles$5.fullPageVisual, isConstrainedArticleMode && styles$5.constrainedArticleVisual, getChatCssClassName('Chat'), chatCssClassNames.chat), style, children: jsxs("div", { className: classNames(className, styles$5.chatMainFlow, getChatCssClassName('chatMainFlow'), chatCssClassNames.chatMainFlow), children: [children && jsx("div", { className: classNames(styles$5.chatChildren), children: children }), shouldShowScrollToBottom && (jsx("div", { className: styles$5.scrollToBottomContainer, children: jsxs("div", { className: styles$5.scrollToBottomWrapper, children: [jsx(SolidArrowButton, { "data-button-type": "custom", direction: "down", iconSize: 33, className: classNames(styles$5.scrollToBottom, scrollToBottomCssClassName), onClick: handleButtonClick(() => scrollToBottom()), "aria-label": ariaLabel, title: ariaLabel }), badgeLabel && (jsx("span", { className: styles$5.scrollToBottomBadge, "aria-live": "polite", role: "status", children: badgeLabel }))] }) })), isVoiceCalling && (jsx("div", { className: styles$5.voiceCallIndicatorBar, children: jsxs("div", { className: styles$5.voiceCallIndicator, children: [jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "currentColor", children: jsx("path", { d: "M6.62 10.79c1.44 2.83 3.76 5.14 6.59 6.59l2.2-2.2c.27-.27.67-.36 1.02-.24 1.12.37 2.33.57 3.57.57.55 0 1 .45 1 1V20c0 .55-.45 1-1 1-9.39 0-17-7.61-17-17 0-.55.45-1 1-1h3.5c.55 0 1 .45 1 1 0 1.25.2 2.45.57 3.57.11.35.03.74-.25 1.02l-2.2 2.2z" }) }), jsx("span", { children: "Voice call active" }), jsx("div", { className: styles$5.voiceCallPulse })] }) })), jsx(ChatActionsBar, { actionsRef: actionsRef, actionsContainer: actionsContainer, messages: postprocessedMessages, participants: participants, title: title, onReset: onReset, resetRequiresConfirmation: resetRequiresConfirmation, newChatButtonHref: newChatButtonHref, onUseTemplate: onUseTemplate, extraActions: extraActions, saveFormats: saveFormats, isSaveButtonEnabled: isSaveButtonEnabled, shouldFadeActions: shouldFadeActions, shouldDisableActions: shouldDisableActions, chatUiTranslations: chatUiTranslations, onButtonClick: handleButtonClick }), jsx(ChatMessageList, { messages: postprocessedMessages, participants: participants, expandedMessageId: expandedMessageId, messageRatings: messageRatings, setExpandedMessageId: setExpandedMessageId, handleRating: handleRating, mode: mode, isCopyButtonEnabled: isCopyButtonEnabled, isFeedbackEnabled: isFeedbackEnabled, feedbackMode: feedbackMode, feedbackTranslations: feedbackTranslations, timingTranslations: timingTranslations, chatLocale: chatLocale, onCopy: handleCopy, onMessage: onMessage, onActionButton: onActionButton, onQuickMessageButton: onQuickMessageButton, onReplyToMessage: onReplyToMessage, canReplyToMessage: canReplyToMessage, onCreateAgent: onCreateAgent, toolTitles: toolTitles, teammates: teammates, teamAgentProfiles: teamAgentProfiles, visualMode: visualMode, soundSystem: soundSystem, onToolCallClick: openToolCall, onCitationClick: openCitation, setChatMessagesElement: setChatMessagesElement, onScroll: handleChatScroll, isSpeechPlaybackEnabled: isSpeechPlaybackEnabled, elevenLabsVoiceId: elevenLabsVoiceId, chatUiTranslations: chatUiTranslations, chatMessagesClassName: classNames(isConstrainedArticleMode && styles$5.articleModeChatMessages, getChatCssClassName('chatMessages'), chatCssClassNames.chatMessages), hasActions: hasActions }), onMessage && (jsx(ChatInputArea, { onMessage: onMessage, onChange: onChange, onFileUpload: onFileUpload, speechRecognition: speechRecognition, speechRecognitionLanguage: speechRecognitionLanguage, replyingToMessage: replyingToMessage, onCancelReply: onCancelReply, defaultMessage: defaultMessage, enterBehavior: enterBehavior, resolveEnterBehavior: resolveEnterBehavior, placeholderMessageContent: placeholderMessageContent || (chatUiTranslations === null || chatUiTranslations === void 0 ? void 0 : chatUiTranslations.inputPlaceholder), isFocusedOnLoad: isFocusedOnLoad, isMobile: isMobile, isVoiceCalling: isVoiceCalling, participants: participants, buttonColor: buttonColor, soundSystem: soundSystem, onButtonClick: handleButtonClick, chatUiTranslations: chatUiTranslations, chatInputClassName: classNames(isConstrainedArticleMode && styles$5.articleModeChatInput, getChatCssClassName('chatInput'), chatCssClassNames.chatInput) }))] }) }), jsx(ChatToolCallModal, { isOpen: toolCallModalOpen, toolCall: selectedToolCall, toolCallIdentity: selectedToolCallIdentity, onClose: closeToolCallModal, toolTitles: toolTitles, agentParticipant: agentParticipant, buttonColor: buttonColor, teamAgentProfiles: teamAgentProfiles, chatUiTranslations: chatUiTranslations, locale: chatLocale, availableTools: selectedMessageAvailableTools }), jsx(ChatCitationModal, { isOpen: citationModalOpen, citation: selectedCitation, participants: participants, soundSystem: soundSystem, onClose: closeCitationModal }), jsx(ChatRatingModal, { isOpen: ratingModalOpen, selectedMessage: selectedMessage, postprocessedMessages: postprocessedMessages, messages: messages, hoveredRating: hoveredRating, messageRatings: messageRatings, textRating: textRating, feedbackMode: feedbackMode, feedbackTranslations: feedbackTranslations, mode: mode, isMobile: isMobile, onClose: () => setRatingModalOpen(false), setHoveredRating: setHoveredRating, setMessageRatings: setMessageRatings, setSelectedMessage: setSelectedMessage, setTextRating: setTextRating, submitRating: submitRating })] }));
|
|
49831
50839
|
}
|
|
49832
50840
|
|
|
49833
50841
|
/**
|
|
@@ -51133,7 +52141,7 @@ function PromptbookAgentSeamlessIntegration(props) {
|
|
|
51133
52141
|
: styles.PromptbookAgentSeamlessIntegrationStatusPending) }), jsxs("div", { className: styles.PromptbookAgentSeamlessIntegrationText, children: [jsx("div", { className: styles.PromptbookAgentSeamlessIntegrationLabel, children: "Chat" }), jsx("div", { className: styles.PromptbookAgentSeamlessIntegrationHint, children: displayName })] }), jsx("span", { className: styles.PromptbookAgentSeamlessIntegrationScreenReaderOnly, children: connectionStatusText })] }), isOpen && (jsxs("div", { className: styles.PromptbookAgentSeamlessIntegrationWindow, id: windowId, children: [jsxs("div", { className: styles.PromptbookAgentSeamlessIntegrationHeader, style: { backgroundColor: color }, ref: setHeaderElement, children: [jsxs("div", { className: styles.PromptbookAgentSeamlessIntegrationTitleWrap, children: [jsx("div", { className: styles.PromptbookAgentSeamlessIntegrationTitle, children: displayName }), jsx("div", { className: styles.PromptbookAgentSeamlessIntegrationSubtitle, children: connectionStatusText })] }), isIframeUsed && (jsx("button", { className: styles.PromptbookAgentSeamlessIntegrationClose, onClick: () => setOpen(false), title: "Close", "aria-label": "Close chat", children: jsx(CloseIcon, {}) }))] }), jsx("div", { className: styles.PromptbookAgentSeamlessIntegrationContent, children: isIframeUsed ? (jsxs(Fragment, { children: [!isIframeLoaded && (jsxs("div", { className: styles.PromptbookAgentSeamlessIntegrationLoading, children: [jsx("div", { className: styles.PromptbookAgentSeamlessIntegrationLoadingShimmer }), jsx("div", { className: styles.PromptbookAgentSeamlessIntegrationLoadingSpinner }), jsx("div", { className: styles.PromptbookAgentSeamlessIntegrationLoadingTitle, children: "Preparing your chat" }), jsxs("div", { className: styles.PromptbookAgentSeamlessIntegrationLoadingText, children: ["Connecting to ", displayName, "..."] })] })), jsx("iframe", { src: agentUrl + '/chat?headless', className: styles.PromptbookAgentSeamlessIntegrationIframe, style: { opacity: isIframeLoaded ? 1 : 0 }, tabIndex: -1, onLoad: () => {
|
|
51134
52142
|
setIsIframeLoaded(true);
|
|
51135
52143
|
setIsChatConnected(true);
|
|
51136
|
-
} })] })) : agent ? (jsx(AgentChat, { agent: agent, actionsContainer: headerElement, isFocusedOnLoad: isFocusedOnLoad, extraActions: jsx("button", { className: styles.PromptbookAgentSeamlessIntegrationClose, onClick: () => setOpen(false), title: "Close", "aria-label": "Close chat", children: jsx(CloseIcon, {}) }),
|
|
52144
|
+
} })] })) : agent ? (jsx(AgentChat, { agent: agent, actionsContainer: headerElement, isFocusedOnLoad: isFocusedOnLoad, extraActions: jsx("button", { className: styles.PromptbookAgentSeamlessIntegrationClose, onClick: () => setOpen(false), title: "Close", "aria-label": "Close chat", children: jsx(CloseIcon, {}) }), layout: "STANDALONE" })) : error ? (jsxs("div", { className: styles.PromptbookAgentSeamlessIntegrationError, children: [jsx("div", { className: styles.PromptbookAgentSeamlessIntegrationErrorTitle, children: "Failed to connect to the agent" }), jsx("div", { className: styles.PromptbookAgentSeamlessIntegrationErrorMessage, children: error.message })] })) : (jsxs("div", { className: styles.PromptbookAgentSeamlessIntegrationLoading, children: [jsx("div", { className: styles.PromptbookAgentSeamlessIntegrationLoadingShimmer }), jsx("div", { className: styles.PromptbookAgentSeamlessIntegrationLoadingSpinner }), jsx("div", { className: styles.PromptbookAgentSeamlessIntegrationLoadingTitle, children: "Preparing your chat" }), jsxs("div", { className: styles.PromptbookAgentSeamlessIntegrationLoadingText, children: ["Connecting to ", displayName, "..."] })] })) })] }))] }));
|
|
51137
52145
|
}
|
|
51138
52146
|
|
|
51139
52147
|
/**
|