@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/umd/index.umd.js
CHANGED
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
* @generated
|
|
31
31
|
* @see https://github.com/webgptorg/promptbook
|
|
32
32
|
*/
|
|
33
|
-
const PROMPTBOOK_ENGINE_VERSION = '0.112.0-
|
|
33
|
+
const PROMPTBOOK_ENGINE_VERSION = '0.112.0-49';
|
|
34
34
|
/**
|
|
35
35
|
* TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
|
|
36
36
|
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
@@ -1392,11 +1392,12 @@
|
|
|
1392
1392
|
* Creates the shared derived palette used by every avatar visual.
|
|
1393
1393
|
*
|
|
1394
1394
|
* @param avatarDefinition Stable avatar definition.
|
|
1395
|
+
* @param surface Surface style used by the parent UI.
|
|
1395
1396
|
* @returns Derived palette.
|
|
1396
1397
|
*
|
|
1397
1398
|
* @private utility of the avatar rendering system
|
|
1398
1399
|
*/
|
|
1399
|
-
function createAvatarPalette(avatarDefinition) {
|
|
1400
|
+
function createAvatarPalette(avatarDefinition, surface = 'framed') {
|
|
1400
1401
|
const normalizedAvatarDefinition = normalizeAvatarDefinition(avatarDefinition);
|
|
1401
1402
|
const primaryColor = Color.fromSafe(normalizedAvatarDefinition.colors[0] || PROMPTBOOK_COLOR);
|
|
1402
1403
|
const secondaryColor = Color.fromSafe(normalizedAvatarDefinition.colors[1] || primaryColor.then(lighten(0.12)).then(saturate(0.16)));
|
|
@@ -1406,8 +1407,8 @@
|
|
|
1406
1407
|
const highlightColor = Color.fromSafe(accentColor.then(lighten(0.22)).then(saturate(0.08)));
|
|
1407
1408
|
const shadowColor = Color.fromSafe(primaryColor.then(darken(0.46)).then(saturate(0.14)));
|
|
1408
1409
|
return {
|
|
1409
|
-
background: backgroundColor.toHex(),
|
|
1410
|
-
backgroundSecondary: backgroundSecondaryColor.toHex(),
|
|
1410
|
+
background: surface === 'transparent' ? 'transparent' : backgroundColor.toHex(),
|
|
1411
|
+
backgroundSecondary: surface === 'transparent' ? 'transparent' : backgroundSecondaryColor.toHex(),
|
|
1411
1412
|
primary: primaryColor.toHex(),
|
|
1412
1413
|
secondary: secondaryColor.toHex(),
|
|
1413
1414
|
accent: accentColor.toHex(),
|
|
@@ -1426,6 +1427,9 @@
|
|
|
1426
1427
|
* @private utility of the avatar rendering system
|
|
1427
1428
|
*/
|
|
1428
1429
|
function drawAvatarFrame(context, size, palette) {
|
|
1430
|
+
if (palette.background === 'transparent' && palette.backgroundSecondary === 'transparent') {
|
|
1431
|
+
return;
|
|
1432
|
+
}
|
|
1429
1433
|
const gradient = context.createLinearGradient(0, 0, size, size);
|
|
1430
1434
|
gradient.addColorStop(0, palette.background);
|
|
1431
1435
|
gradient.addColorStop(1, palette.backgroundSecondary);
|
|
@@ -1505,10 +1509,22 @@
|
|
|
1505
1509
|
*/
|
|
1506
1510
|
function prepareAvatarCanvas(canvas, context, size, devicePixelRatio) {
|
|
1507
1511
|
const normalizedDevicePixelRatio = Math.max(1, Math.round(devicePixelRatio * 100) / 100);
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
+
const nextCanvasWidth = Math.round(size * normalizedDevicePixelRatio);
|
|
1513
|
+
const nextCanvasHeight = Math.round(size * normalizedDevicePixelRatio);
|
|
1514
|
+
const nextCanvasStyleWidth = `${size}px`;
|
|
1515
|
+
const nextCanvasStyleHeight = `${size}px`;
|
|
1516
|
+
if (canvas.width !== nextCanvasWidth) {
|
|
1517
|
+
canvas.width = nextCanvasWidth;
|
|
1518
|
+
}
|
|
1519
|
+
if (canvas.height !== nextCanvasHeight) {
|
|
1520
|
+
canvas.height = nextCanvasHeight;
|
|
1521
|
+
}
|
|
1522
|
+
if (canvas.style.width !== nextCanvasStyleWidth) {
|
|
1523
|
+
canvas.style.width = nextCanvasStyleWidth;
|
|
1524
|
+
}
|
|
1525
|
+
if (canvas.style.height !== nextCanvasStyleHeight) {
|
|
1526
|
+
canvas.style.height = nextCanvasStyleHeight;
|
|
1527
|
+
}
|
|
1512
1528
|
context.setTransform(normalizedDevicePixelRatio, 0, 0, normalizedDevicePixelRatio, 0, 0);
|
|
1513
1529
|
context.clearRect(0, 0, size, size);
|
|
1514
1530
|
}
|
|
@@ -1577,7 +1593,7 @@
|
|
|
1577
1593
|
*
|
|
1578
1594
|
* @private shared avatar contract
|
|
1579
1595
|
*/
|
|
1580
|
-
const DEFAULT_AGENT_AVATAR_VISUAL_ID = '
|
|
1596
|
+
const DEFAULT_AGENT_AVATAR_VISUAL_ID = 'octopus3';
|
|
1581
1597
|
/**
|
|
1582
1598
|
* Resolve a base URL for relative images, preferring the provided base or browser location.
|
|
1583
1599
|
*
|
|
@@ -11722,6 +11738,7 @@
|
|
|
11722
11738
|
* Supported USE types:
|
|
11723
11739
|
* - USE BROWSER: Enables the agent to use a web browser tool
|
|
11724
11740
|
* - USE SEARCH ENGINE (future): Enables search engine access
|
|
11741
|
+
* - USE DEEPSEARCH: Enables deeper research-oriented search access
|
|
11725
11742
|
* - USE FILE SYSTEM (future): Enables file system operations
|
|
11726
11743
|
* - USE MCP (future): Enables MCP server connections
|
|
11727
11744
|
*
|
|
@@ -11744,7 +11761,7 @@
|
|
|
11744
11761
|
* Short one-line description of USE commitments.
|
|
11745
11762
|
*/
|
|
11746
11763
|
get description() {
|
|
11747
|
-
return 'Enable the agent to use specific tools or capabilities (BROWSER, SEARCH ENGINE, etc.).';
|
|
11764
|
+
return 'Enable the agent to use specific tools or capabilities (BROWSER, SEARCH ENGINE, DEEPSEARCH, etc.).';
|
|
11748
11765
|
}
|
|
11749
11766
|
/**
|
|
11750
11767
|
* Icon for this commitment.
|
|
@@ -11765,6 +11782,7 @@
|
|
|
11765
11782
|
|
|
11766
11783
|
- **USE BROWSER** - Enables the agent to use a web browser tool to access and retrieve information from the internet
|
|
11767
11784
|
- **USE SEARCH ENGINE** (future) - Enables search engine access
|
|
11785
|
+
- **USE DEEPSEARCH** - Enables deeper research-oriented search access
|
|
11768
11786
|
- **USE FILE SYSTEM** (future) - Enables file system operations
|
|
11769
11787
|
- **USE MCP** (future) - Enables MCP server connections
|
|
11770
11788
|
|
|
@@ -11819,7 +11837,7 @@
|
|
|
11819
11837
|
* Checks if this is a known USE type
|
|
11820
11838
|
*/
|
|
11821
11839
|
isKnownUseType(useType) {
|
|
11822
|
-
const knownTypes = ['BROWSER', 'SEARCH ENGINE', 'FILE SYSTEM', 'MCP'];
|
|
11840
|
+
const knownTypes = ['BROWSER', 'SEARCH ENGINE', 'DEEPSEARCH', 'FILE SYSTEM', 'MCP'];
|
|
11823
11841
|
return knownTypes.includes(useType.toUpperCase());
|
|
11824
11842
|
}
|
|
11825
11843
|
}
|
|
@@ -11832,6 +11850,7 @@
|
|
|
11832
11850
|
*/
|
|
11833
11851
|
const AGGREGATED_USE_COMMITMENT_TYPES = [
|
|
11834
11852
|
'USE BROWSER',
|
|
11853
|
+
'USE DEEPSEARCH',
|
|
11835
11854
|
'USE SEARCH ENGINE',
|
|
11836
11855
|
'USE TIME',
|
|
11837
11856
|
];
|
|
@@ -11911,6 +11930,15 @@
|
|
|
11911
11930
|
- Do not tell the user you cannot search for information, YOU CAN.
|
|
11912
11931
|
${block(formatOptionalInstructionBlock('Search instructions', combinedAdditionalInstructions))}
|
|
11913
11932
|
`);
|
|
11933
|
+
case 'USE DEEPSEARCH':
|
|
11934
|
+
return spacetrim.spaceTrim((block) => `
|
|
11935
|
+
Tool:
|
|
11936
|
+
- You have access to DeepSearch via the tool "deep_search".
|
|
11937
|
+
- Use it for broader research tasks that need multi-step investigation, comparison, or synthesis across multiple sources.
|
|
11938
|
+
- Prefer it over quick search when the user asks for a well-grounded brief, report, or deeper investigation.
|
|
11939
|
+
- Do not pretend you cannot research current information when this tool is available.
|
|
11940
|
+
${block(formatOptionalInstructionBlock('DeepSearch instructions', combinedAdditionalInstructions))}
|
|
11941
|
+
`);
|
|
11914
11942
|
}
|
|
11915
11943
|
}
|
|
11916
11944
|
/**
|
|
@@ -13460,6 +13488,207 @@
|
|
|
13460
13488
|
}
|
|
13461
13489
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
13462
13490
|
|
|
13491
|
+
/**
|
|
13492
|
+
* A search engine implementation that uses the SerpApi to fetch Google search results.
|
|
13493
|
+
*
|
|
13494
|
+
* @private <- TODO: !!!! Export via some package
|
|
13495
|
+
*/
|
|
13496
|
+
class SerpSearchEngine {
|
|
13497
|
+
get title() {
|
|
13498
|
+
return 'SerpApi Search Engine';
|
|
13499
|
+
}
|
|
13500
|
+
get description() {
|
|
13501
|
+
return 'Search engine that uses SerpApi to fetch Google search results';
|
|
13502
|
+
}
|
|
13503
|
+
checkConfiguration() {
|
|
13504
|
+
if (!process.env.SERP_API_KEY) {
|
|
13505
|
+
throw new Error('SERP_API_KEY is not configured');
|
|
13506
|
+
}
|
|
13507
|
+
}
|
|
13508
|
+
async search(query, options = {}) {
|
|
13509
|
+
const apiKey = process.env.SERP_API_KEY;
|
|
13510
|
+
if (!apiKey) {
|
|
13511
|
+
throw new Error('SERP_API_KEY is not configured');
|
|
13512
|
+
}
|
|
13513
|
+
const url = new URL('https://serpapi.com/search');
|
|
13514
|
+
url.searchParams.set('api_key', apiKey);
|
|
13515
|
+
url.searchParams.set('engine', 'google');
|
|
13516
|
+
url.searchParams.set('q', query);
|
|
13517
|
+
for (const [key, value] of Object.entries(options)) {
|
|
13518
|
+
url.searchParams.set(key, String(value));
|
|
13519
|
+
}
|
|
13520
|
+
const response = await fetch(url.toString());
|
|
13521
|
+
if (!response.ok) {
|
|
13522
|
+
const body = await response.text();
|
|
13523
|
+
throw new Error(`SerpApi failed with status ${response.status}: ${response.statusText}\n${body}`);
|
|
13524
|
+
}
|
|
13525
|
+
const data = (await response.json());
|
|
13526
|
+
return (data.organic_results || []).map((item) => ({
|
|
13527
|
+
title: item.title,
|
|
13528
|
+
url: item.link,
|
|
13529
|
+
snippet: item.snippet || '',
|
|
13530
|
+
}));
|
|
13531
|
+
}
|
|
13532
|
+
}
|
|
13533
|
+
|
|
13534
|
+
/**
|
|
13535
|
+
* Creates one SERP-backed tool function used as a local fallback for search-like commitments.
|
|
13536
|
+
*
|
|
13537
|
+
* @param toolName - Technical tool name used for validation messages.
|
|
13538
|
+
* @param resultLabel - Human-readable label used in formatted results.
|
|
13539
|
+
* @returns Async tool function compatible with commitment tool registration.
|
|
13540
|
+
*
|
|
13541
|
+
* @private internal helper for search-like commitments
|
|
13542
|
+
*/
|
|
13543
|
+
function createSerpSearchToolFunction(toolName, resultLabel) {
|
|
13544
|
+
return async (rawArgs) => {
|
|
13545
|
+
const { query, ...searchOptions } = rawArgs;
|
|
13546
|
+
if (typeof query !== 'string' || !query.trim()) {
|
|
13547
|
+
throw new Error(`${toolName} query is required`);
|
|
13548
|
+
}
|
|
13549
|
+
const searchEngine = new SerpSearchEngine();
|
|
13550
|
+
const results = await searchEngine.search(query, searchOptions);
|
|
13551
|
+
return spacetrim.spaceTrim((block) => `
|
|
13552
|
+
${resultLabel} results for "${query}"${Object.keys(searchOptions).length === 0
|
|
13553
|
+
? ''
|
|
13554
|
+
: ` with options ${JSON.stringify(searchOptions)}`}:
|
|
13555
|
+
|
|
13556
|
+
${block(results
|
|
13557
|
+
.map((result) => spacetrim.spaceTrim(`
|
|
13558
|
+
- **${result.title}**
|
|
13559
|
+
${result.url}
|
|
13560
|
+
${result.snippet}
|
|
13561
|
+
`))
|
|
13562
|
+
.join('\n\n'))}
|
|
13563
|
+
`);
|
|
13564
|
+
};
|
|
13565
|
+
}
|
|
13566
|
+
|
|
13567
|
+
/**
|
|
13568
|
+
* USE DEEPSEARCH commitment definition
|
|
13569
|
+
*
|
|
13570
|
+
* The `USE DEEPSEARCH` commitment indicates that the agent should use a deeper research-oriented
|
|
13571
|
+
* search workflow instead of lightweight web search when it needs fresh information from the internet.
|
|
13572
|
+
*
|
|
13573
|
+
* The content following `USE DEEPSEARCH` is an arbitrary text that the agent should know
|
|
13574
|
+
* (e.g. search scope or research instructions).
|
|
13575
|
+
*
|
|
13576
|
+
* Example usage in agent source:
|
|
13577
|
+
*
|
|
13578
|
+
* ```book
|
|
13579
|
+
* USE DEEPSEARCH
|
|
13580
|
+
* USE DEEPSEARCH Compare official vendor documentation with independent benchmarks.
|
|
13581
|
+
* ```
|
|
13582
|
+
*
|
|
13583
|
+
* @private [🪔] Maybe export the commitments through some package
|
|
13584
|
+
*/
|
|
13585
|
+
class UseDeepSearchCommitmentDefinition extends BaseCommitmentDefinition {
|
|
13586
|
+
constructor() {
|
|
13587
|
+
super('USE DEEPSEARCH');
|
|
13588
|
+
}
|
|
13589
|
+
get requiresContent() {
|
|
13590
|
+
return false;
|
|
13591
|
+
}
|
|
13592
|
+
/**
|
|
13593
|
+
* Short one-line description of USE DEEPSEARCH.
|
|
13594
|
+
*/
|
|
13595
|
+
get description() {
|
|
13596
|
+
return 'Enable the agent to use DeepSearch for more thorough internet research.';
|
|
13597
|
+
}
|
|
13598
|
+
/**
|
|
13599
|
+
* Icon for this commitment.
|
|
13600
|
+
*/
|
|
13601
|
+
get icon() {
|
|
13602
|
+
return '🔬';
|
|
13603
|
+
}
|
|
13604
|
+
/**
|
|
13605
|
+
* Markdown documentation for USE DEEPSEARCH commitment.
|
|
13606
|
+
*/
|
|
13607
|
+
get documentation() {
|
|
13608
|
+
return spacetrim.spaceTrim(`
|
|
13609
|
+
# USE DEEPSEARCH
|
|
13610
|
+
|
|
13611
|
+
Enables the agent to use DeepSearch for broader, more thorough internet research than lightweight web search.
|
|
13612
|
+
|
|
13613
|
+
## Key aspects
|
|
13614
|
+
|
|
13615
|
+
- The content following \`USE DEEPSEARCH\` is arbitrary guidance for the research workflow.
|
|
13616
|
+
- In Agents Server, the OpenAI Agents SDK runtime uses a nested deep-research agent for this tool.
|
|
13617
|
+
- Use this for investigations, comparisons, market scans, or other tasks that benefit from deeper synthesis.
|
|
13618
|
+
- Prefer regular \`USE SEARCH ENGINE\` when a quick factual lookup is enough.
|
|
13619
|
+
|
|
13620
|
+
## Examples
|
|
13621
|
+
|
|
13622
|
+
\`\`\`book
|
|
13623
|
+
Due Diligence Researcher
|
|
13624
|
+
|
|
13625
|
+
GOAL Investigate vendors thoroughly before making recommendations.
|
|
13626
|
+
USE DEEPSEARCH Compare official sources with credible third-party analysis.
|
|
13627
|
+
RULE Cite the strongest supporting sources in the final answer.
|
|
13628
|
+
\`\`\`
|
|
13629
|
+
|
|
13630
|
+
\`\`\`book
|
|
13631
|
+
Market Analyst
|
|
13632
|
+
|
|
13633
|
+
GOAL Build concise but well-grounded research briefs.
|
|
13634
|
+
USE DEEPSEARCH Focus on recent public information and competing viewpoints.
|
|
13635
|
+
CLOSED
|
|
13636
|
+
\`\`\`
|
|
13637
|
+
`);
|
|
13638
|
+
}
|
|
13639
|
+
applyToAgentModelRequirements(requirements, content) {
|
|
13640
|
+
const existingTools = requirements.tools || [];
|
|
13641
|
+
const updatedTools = existingTools.some((tool) => tool.name === 'deep_search')
|
|
13642
|
+
? existingTools
|
|
13643
|
+
: [
|
|
13644
|
+
...existingTools,
|
|
13645
|
+
{
|
|
13646
|
+
name: 'deep_search',
|
|
13647
|
+
description: spacetrim.spaceTrim(`
|
|
13648
|
+
Research the internet deeply and synthesize a grounded answer.
|
|
13649
|
+
Use this tool for broader investigations, comparisons, and requests that need more than a quick search.
|
|
13650
|
+
`),
|
|
13651
|
+
parameters: {
|
|
13652
|
+
type: 'object',
|
|
13653
|
+
properties: {
|
|
13654
|
+
query: {
|
|
13655
|
+
type: 'string',
|
|
13656
|
+
description: 'The research question or investigation request.',
|
|
13657
|
+
},
|
|
13658
|
+
},
|
|
13659
|
+
required: ['query'],
|
|
13660
|
+
additionalProperties: false,
|
|
13661
|
+
},
|
|
13662
|
+
},
|
|
13663
|
+
];
|
|
13664
|
+
return appendAggregatedUseCommitmentPlaceholder({
|
|
13665
|
+
...requirements,
|
|
13666
|
+
tools: updatedTools,
|
|
13667
|
+
_metadata: {
|
|
13668
|
+
...requirements._metadata,
|
|
13669
|
+
useDeepSearch: content || true,
|
|
13670
|
+
},
|
|
13671
|
+
}, this.type);
|
|
13672
|
+
}
|
|
13673
|
+
/**
|
|
13674
|
+
* Gets human-readable titles for tool functions provided by this commitment.
|
|
13675
|
+
*/
|
|
13676
|
+
getToolTitles() {
|
|
13677
|
+
return {
|
|
13678
|
+
deep_search: 'DeepSearch',
|
|
13679
|
+
};
|
|
13680
|
+
}
|
|
13681
|
+
/**
|
|
13682
|
+
* Gets the local fallback implementation for the `deep_search` tool.
|
|
13683
|
+
*/
|
|
13684
|
+
getToolFunctions() {
|
|
13685
|
+
return {
|
|
13686
|
+
deep_search: createSerpSearchToolFunction('deep_search', 'DeepSearch'),
|
|
13687
|
+
};
|
|
13688
|
+
}
|
|
13689
|
+
}
|
|
13690
|
+
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
13691
|
+
|
|
13463
13692
|
/**
|
|
13464
13693
|
* Lightweight email token matcher used for `USE EMAIL` first-line parsing.
|
|
13465
13694
|
*
|
|
@@ -15691,49 +15920,6 @@
|
|
|
15691
15920
|
}
|
|
15692
15921
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
15693
15922
|
|
|
15694
|
-
/**
|
|
15695
|
-
* A search engine implementation that uses the SerpApi to fetch Google search results.
|
|
15696
|
-
*
|
|
15697
|
-
* @private <- TODO: !!!! Export via some package
|
|
15698
|
-
*/
|
|
15699
|
-
class SerpSearchEngine {
|
|
15700
|
-
get title() {
|
|
15701
|
-
return 'SerpApi Search Engine';
|
|
15702
|
-
}
|
|
15703
|
-
get description() {
|
|
15704
|
-
return 'Search engine that uses SerpApi to fetch Google search results';
|
|
15705
|
-
}
|
|
15706
|
-
checkConfiguration() {
|
|
15707
|
-
if (!process.env.SERP_API_KEY) {
|
|
15708
|
-
throw new Error('SERP_API_KEY is not configured');
|
|
15709
|
-
}
|
|
15710
|
-
}
|
|
15711
|
-
async search(query, options = {}) {
|
|
15712
|
-
const apiKey = process.env.SERP_API_KEY;
|
|
15713
|
-
if (!apiKey) {
|
|
15714
|
-
throw new Error('SERP_API_KEY is not configured');
|
|
15715
|
-
}
|
|
15716
|
-
const url = new URL('https://serpapi.com/search');
|
|
15717
|
-
url.searchParams.set('api_key', apiKey);
|
|
15718
|
-
url.searchParams.set('engine', 'google');
|
|
15719
|
-
url.searchParams.set('q', query);
|
|
15720
|
-
for (const [key, value] of Object.entries(options)) {
|
|
15721
|
-
url.searchParams.set(key, String(value));
|
|
15722
|
-
}
|
|
15723
|
-
const response = await fetch(url.toString());
|
|
15724
|
-
if (!response.ok) {
|
|
15725
|
-
const body = await response.text();
|
|
15726
|
-
throw new Error(`SerpApi failed with status ${response.status}: ${response.statusText}\n${body}`);
|
|
15727
|
-
}
|
|
15728
|
-
const data = (await response.json());
|
|
15729
|
-
return (data.organic_results || []).map((item) => ({
|
|
15730
|
-
title: item.title,
|
|
15731
|
-
url: item.link,
|
|
15732
|
-
snippet: item.snippet || '',
|
|
15733
|
-
}));
|
|
15734
|
-
}
|
|
15735
|
-
}
|
|
15736
|
-
|
|
15737
15923
|
/**
|
|
15738
15924
|
* USE SEARCH ENGINE commitment definition
|
|
15739
15925
|
*
|
|
@@ -15877,26 +16063,7 @@
|
|
|
15877
16063
|
*/
|
|
15878
16064
|
getToolFunctions() {
|
|
15879
16065
|
return {
|
|
15880
|
-
|
|
15881
|
-
console.log('!!!! [Tool] web_search called', { args });
|
|
15882
|
-
const { query, ...options } = args;
|
|
15883
|
-
if (!query) {
|
|
15884
|
-
throw new Error('Search query is required');
|
|
15885
|
-
}
|
|
15886
|
-
const searchEngine = new SerpSearchEngine();
|
|
15887
|
-
const results = await searchEngine.search(query, options);
|
|
15888
|
-
return spacetrim.spaceTrim((block) => `
|
|
15889
|
-
Search results for "${query}"${Object.keys(options).length === 0 ? '' : ` with options ${JSON.stringify(options)}`}:
|
|
15890
|
-
|
|
15891
|
-
${block(results
|
|
15892
|
-
.map((result) => spacetrim.spaceTrim(`
|
|
15893
|
-
- **${result.title}**
|
|
15894
|
-
${result.url}
|
|
15895
|
-
${result.snippet}
|
|
15896
|
-
`))
|
|
15897
|
-
.join('\n\n'))}
|
|
15898
|
-
`);
|
|
15899
|
-
},
|
|
16066
|
+
web_search: createSerpSearchToolFunction('web_search', 'Search'),
|
|
15900
16067
|
};
|
|
15901
16068
|
}
|
|
15902
16069
|
}
|
|
@@ -17542,6 +17709,7 @@
|
|
|
17542
17709
|
new ClosedCommitmentDefinition(),
|
|
17543
17710
|
new TeamCommitmentDefinition(),
|
|
17544
17711
|
new UseBrowserCommitmentDefinition(),
|
|
17712
|
+
new UseDeepSearchCommitmentDefinition(),
|
|
17545
17713
|
new UseSearchEngineCommitmentDefinition(),
|
|
17546
17714
|
new UseSpawnCommitmentDefinition(),
|
|
17547
17715
|
new UseTimeoutCommitmentDefinition(),
|
|
@@ -17853,6 +18021,11 @@
|
|
|
17853
18021
|
label: 'Internet',
|
|
17854
18022
|
iconName: 'Search',
|
|
17855
18023
|
},
|
|
18024
|
+
'USE DEEPSEARCH': {
|
|
18025
|
+
type: 'search-engine',
|
|
18026
|
+
label: 'DeepSearch',
|
|
18027
|
+
iconName: 'Search',
|
|
18028
|
+
},
|
|
17856
18029
|
'USE TIME': {
|
|
17857
18030
|
type: 'time',
|
|
17858
18031
|
label: 'Time',
|
|
@@ -25728,6 +25901,496 @@
|
|
|
25728
25901
|
}), children: jsxRuntime.jsx(SendIcon, { size: 25 }) })] }), speechRecognition && (jsxRuntime.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 && (jsxRuntime.jsxs("div", { className: styles$5.uploadProgress, children: [jsxRuntime.jsx("div", { className: styles$5.uploadProgressBar, children: jsxRuntime.jsx("div", { className: styles$5.uploadProgressFill }) }), jsxRuntime.jsx("span", { children: "Uploading files..." })] })), isDragOver && onFileUpload && (jsxRuntime.jsx("div", { className: styles$5.dragOverlay, children: jsxRuntime.jsxs("div", { className: styles$5.dragOverlayContent, children: [jsxRuntime.jsx(AttachmentIcon, { size: 48 }), jsxRuntime.jsx("span", { children: "Drop files here to upload" })] }) }))] }));
|
|
25729
25902
|
}
|
|
25730
25903
|
|
|
25904
|
+
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
25905
|
+
/**
|
|
25906
|
+
* Maximum normalized eye travel used when the viewer moves across the viewport.
|
|
25907
|
+
*
|
|
25908
|
+
* @private utility of the avatar rendering system
|
|
25909
|
+
*/
|
|
25910
|
+
const MAX_GAZE_OFFSET = 0.78;
|
|
25911
|
+
/**
|
|
25912
|
+
* Maximum normalized body lean used for subtle mantle response.
|
|
25913
|
+
*
|
|
25914
|
+
* @private utility of the avatar rendering system
|
|
25915
|
+
*/
|
|
25916
|
+
const MAX_BODY_OFFSET = 0.28;
|
|
25917
|
+
/**
|
|
25918
|
+
* Smoothing window used while a live pointer or touch target is active.
|
|
25919
|
+
*
|
|
25920
|
+
* @private utility of the avatar rendering system
|
|
25921
|
+
*/
|
|
25922
|
+
const ACTIVE_INTERACTION_SMOOTHING_MS = 90;
|
|
25923
|
+
/**
|
|
25924
|
+
* Slower smoothing window used when easing the avatar back to its idle state.
|
|
25925
|
+
*
|
|
25926
|
+
* @private utility of the avatar rendering system
|
|
25927
|
+
*/
|
|
25928
|
+
const IDLE_INTERACTION_SMOOTHING_MS = 230;
|
|
25929
|
+
/**
|
|
25930
|
+
* Maximum frame delta allowed when smoothing interaction after tab stalls.
|
|
25931
|
+
*
|
|
25932
|
+
* @private utility of the avatar rendering system
|
|
25933
|
+
*/
|
|
25934
|
+
const MAX_INTERACTION_FRAME_DELTA_MS = 64;
|
|
25935
|
+
/**
|
|
25936
|
+
* Extra damping used for the slower body lean compared with the quicker eye motion.
|
|
25937
|
+
*
|
|
25938
|
+
* @private utility of the avatar rendering system
|
|
25939
|
+
*/
|
|
25940
|
+
const BODY_INTERACTION_SMOOTHING_MULTIPLIER = 1.2;
|
|
25941
|
+
/**
|
|
25942
|
+
* Stable zeroed interaction state used by non-interactive render paths.
|
|
25943
|
+
*
|
|
25944
|
+
* @private utility of the avatar rendering system
|
|
25945
|
+
*/
|
|
25946
|
+
const IDLE_AVATAR_INTERACTION_STATE = {
|
|
25947
|
+
gazeX: 0,
|
|
25948
|
+
gazeY: 0,
|
|
25949
|
+
bodyOffsetX: 0,
|
|
25950
|
+
bodyOffsetY: 0,
|
|
25951
|
+
intensity: 0,
|
|
25952
|
+
isPointerActive: false,
|
|
25953
|
+
pointerType: 'idle',
|
|
25954
|
+
};
|
|
25955
|
+
/**
|
|
25956
|
+
* Creates one stable cache key from the meaningful avatar-definition fields.
|
|
25957
|
+
*
|
|
25958
|
+
* @param avatarDefinition Normalized or raw avatar definition.
|
|
25959
|
+
* @returns Stable cache key that ignores object identity churn.
|
|
25960
|
+
*
|
|
25961
|
+
* @private utility of the avatar rendering system
|
|
25962
|
+
*/
|
|
25963
|
+
function createAvatarDefinitionKey(avatarDefinition) {
|
|
25964
|
+
const normalizedAvatarDefinition = normalizeAvatarDefinition(avatarDefinition);
|
|
25965
|
+
return [
|
|
25966
|
+
normalizedAvatarDefinition.agentName,
|
|
25967
|
+
normalizedAvatarDefinition.agentHash,
|
|
25968
|
+
normalizedAvatarDefinition.colors.join('|'),
|
|
25969
|
+
].join('::');
|
|
25970
|
+
}
|
|
25971
|
+
/**
|
|
25972
|
+
* Returns the neutral interaction state used by static/server-side renders.
|
|
25973
|
+
*
|
|
25974
|
+
* @returns Zeroed interaction state.
|
|
25975
|
+
*
|
|
25976
|
+
* @private utility of the avatar rendering system
|
|
25977
|
+
*/
|
|
25978
|
+
function createIdleAvatarInteractionState() {
|
|
25979
|
+
return IDLE_AVATAR_INTERACTION_STATE;
|
|
25980
|
+
}
|
|
25981
|
+
/**
|
|
25982
|
+
* Creates a fresh runtime state for the interactive animation loop.
|
|
25983
|
+
*
|
|
25984
|
+
* @returns Runtime interaction state with neutral values.
|
|
25985
|
+
*
|
|
25986
|
+
* @private utility of the avatar rendering system
|
|
25987
|
+
*/
|
|
25988
|
+
function createAvatarInteractionRuntimeState() {
|
|
25989
|
+
return {
|
|
25990
|
+
...IDLE_AVATAR_INTERACTION_STATE,
|
|
25991
|
+
lastFrameMs: null,
|
|
25992
|
+
};
|
|
25993
|
+
}
|
|
25994
|
+
/**
|
|
25995
|
+
* Converts the shared viewport pointer state into one avatar-local gaze target.
|
|
25996
|
+
*
|
|
25997
|
+
* @param avatarBounds Canvas bounds in viewport coordinates.
|
|
25998
|
+
* @param pointerSnapshot Latest shared pointer sample.
|
|
25999
|
+
* @returns Local target used to steer eyes and subtle body lean.
|
|
26000
|
+
*
|
|
26001
|
+
* @private utility of the avatar rendering system
|
|
26002
|
+
*/
|
|
26003
|
+
function resolveAvatarPointerTarget(avatarBounds, pointerSnapshot) {
|
|
26004
|
+
if (!pointerSnapshot || !pointerSnapshot.isPointerActive) {
|
|
26005
|
+
return {
|
|
26006
|
+
...IDLE_AVATAR_INTERACTION_STATE,
|
|
26007
|
+
};
|
|
26008
|
+
}
|
|
26009
|
+
const centerX = avatarBounds.left + avatarBounds.width / 2;
|
|
26010
|
+
const centerY = avatarBounds.top + avatarBounds.height / 2;
|
|
26011
|
+
const normalizedX = (pointerSnapshot.clientX - centerX) / Math.max(avatarBounds.width / 2, 1);
|
|
26012
|
+
const normalizedY = (pointerSnapshot.clientY - centerY) / Math.max(avatarBounds.height / 2, 1);
|
|
26013
|
+
const normalizedLength = Math.hypot(normalizedX, normalizedY) || 1;
|
|
26014
|
+
const clampedLength = Math.min(1, normalizedLength);
|
|
26015
|
+
const targetX = (normalizedX / normalizedLength) * clampedLength;
|
|
26016
|
+
const targetY = (normalizedY / normalizedLength) * clampedLength;
|
|
26017
|
+
const intensity = clamp01$1(clampedLength);
|
|
26018
|
+
return {
|
|
26019
|
+
gazeX: targetX * MAX_GAZE_OFFSET,
|
|
26020
|
+
gazeY: targetY * MAX_GAZE_OFFSET,
|
|
26021
|
+
bodyOffsetX: targetX * MAX_BODY_OFFSET,
|
|
26022
|
+
bodyOffsetY: targetY * MAX_BODY_OFFSET,
|
|
26023
|
+
intensity,
|
|
26024
|
+
isPointerActive: true,
|
|
26025
|
+
pointerType: pointerSnapshot.pointerType,
|
|
26026
|
+
};
|
|
26027
|
+
}
|
|
26028
|
+
/**
|
|
26029
|
+
* Advances the smoothed interaction state toward the latest pointer target.
|
|
26030
|
+
*
|
|
26031
|
+
* @param runtimeState Previous animation-frame state.
|
|
26032
|
+
* @param pointerTarget Latest local pointer target.
|
|
26033
|
+
* @param nowMs Current animation-frame timestamp.
|
|
26034
|
+
* @returns Next runtime state to keep in the animation loop.
|
|
26035
|
+
*
|
|
26036
|
+
* @private utility of the avatar rendering system
|
|
26037
|
+
*/
|
|
26038
|
+
function stepAvatarInteractionRuntimeState(runtimeState, pointerTarget, nowMs) {
|
|
26039
|
+
const deltaMs = runtimeState.lastFrameMs === null
|
|
26040
|
+
? 16
|
|
26041
|
+
: Math.min(MAX_INTERACTION_FRAME_DELTA_MS, Math.max(8, nowMs - runtimeState.lastFrameMs));
|
|
26042
|
+
const smoothingWindowMs = pointerTarget.isPointerActive
|
|
26043
|
+
? ACTIVE_INTERACTION_SMOOTHING_MS
|
|
26044
|
+
: IDLE_INTERACTION_SMOOTHING_MS;
|
|
26045
|
+
// This exponential interpolation keeps the gaze response smooth regardless of fluctuating frame rates.
|
|
26046
|
+
return {
|
|
26047
|
+
gazeX: interpolateExponentially(runtimeState.gazeX, pointerTarget.gazeX, deltaMs, smoothingWindowMs),
|
|
26048
|
+
gazeY: interpolateExponentially(runtimeState.gazeY, pointerTarget.gazeY, deltaMs, smoothingWindowMs),
|
|
26049
|
+
bodyOffsetX: interpolateExponentially(runtimeState.bodyOffsetX, pointerTarget.bodyOffsetX, deltaMs, smoothingWindowMs * BODY_INTERACTION_SMOOTHING_MULTIPLIER),
|
|
26050
|
+
bodyOffsetY: interpolateExponentially(runtimeState.bodyOffsetY, pointerTarget.bodyOffsetY, deltaMs, smoothingWindowMs * BODY_INTERACTION_SMOOTHING_MULTIPLIER),
|
|
26051
|
+
intensity: interpolateExponentially(runtimeState.intensity, pointerTarget.intensity, deltaMs, smoothingWindowMs),
|
|
26052
|
+
isPointerActive: pointerTarget.isPointerActive,
|
|
26053
|
+
pointerType: pointerTarget.pointerType,
|
|
26054
|
+
lastFrameMs: nowMs,
|
|
26055
|
+
};
|
|
26056
|
+
}
|
|
26057
|
+
/**
|
|
26058
|
+
* Clamps a scalar into the inclusive `[0, 1]` range.
|
|
26059
|
+
*
|
|
26060
|
+
* @param value Arbitrary scalar.
|
|
26061
|
+
* @returns Clamped scalar.
|
|
26062
|
+
*
|
|
26063
|
+
* @private utility of the avatar rendering system
|
|
26064
|
+
*/
|
|
26065
|
+
function clamp01$1(value) {
|
|
26066
|
+
return Math.min(1, Math.max(0, value));
|
|
26067
|
+
}
|
|
26068
|
+
/**
|
|
26069
|
+
* Interpolates between two values using frame-rate-independent exponential easing.
|
|
26070
|
+
*
|
|
26071
|
+
* @param currentValue Current smoothed value.
|
|
26072
|
+
* @param targetValue Target value.
|
|
26073
|
+
* @param deltaMs Elapsed milliseconds since the previous frame.
|
|
26074
|
+
* @param smoothingWindowMs Time constant controlling responsiveness.
|
|
26075
|
+
* @returns Smoothed next value.
|
|
26076
|
+
*
|
|
26077
|
+
* @private utility of the avatar rendering system
|
|
26078
|
+
*/
|
|
26079
|
+
function interpolateExponentially(currentValue, targetValue, deltaMs, smoothingWindowMs) {
|
|
26080
|
+
const blend = 1 - Math.exp(-deltaMs / Math.max(1, smoothingWindowMs));
|
|
26081
|
+
return currentValue + (targetValue - currentValue) * blend;
|
|
26082
|
+
}
|
|
26083
|
+
|
|
26084
|
+
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
26085
|
+
/**
|
|
26086
|
+
* Next registration id used by the shared avatar animation scheduler.
|
|
26087
|
+
*
|
|
26088
|
+
* @private utility of the avatar rendering system
|
|
26089
|
+
*/
|
|
26090
|
+
let nextAvatarAnimationListenerId = 1;
|
|
26091
|
+
/**
|
|
26092
|
+
* Active avatar animation callbacks keyed by their registration id.
|
|
26093
|
+
*
|
|
26094
|
+
* @private utility of the avatar rendering system
|
|
26095
|
+
*/
|
|
26096
|
+
const avatarAnimationListeners = new Map();
|
|
26097
|
+
/**
|
|
26098
|
+
* Active shared animation-frame handle.
|
|
26099
|
+
*
|
|
26100
|
+
* @private utility of the avatar rendering system
|
|
26101
|
+
*/
|
|
26102
|
+
let avatarAnimationFrameId = null;
|
|
26103
|
+
/**
|
|
26104
|
+
* Registers one avatar animation callback in the shared animation loop.
|
|
26105
|
+
*
|
|
26106
|
+
* @param avatarAnimationListener Frame callback invoked on every animation frame.
|
|
26107
|
+
* @returns Cleanup function that unregisters the callback.
|
|
26108
|
+
*
|
|
26109
|
+
* @private utility of the avatar rendering system
|
|
26110
|
+
*/
|
|
26111
|
+
function retainAvatarAnimationListener(avatarAnimationListener) {
|
|
26112
|
+
if (typeof window === 'undefined') {
|
|
26113
|
+
return () => undefined;
|
|
26114
|
+
}
|
|
26115
|
+
const listenerId = nextAvatarAnimationListenerId++;
|
|
26116
|
+
avatarAnimationListeners.set(listenerId, avatarAnimationListener);
|
|
26117
|
+
ensureAvatarAnimationLoop();
|
|
26118
|
+
return () => {
|
|
26119
|
+
avatarAnimationListeners.delete(listenerId);
|
|
26120
|
+
if (avatarAnimationListeners.size === 0 && avatarAnimationFrameId !== null) {
|
|
26121
|
+
window.cancelAnimationFrame(avatarAnimationFrameId);
|
|
26122
|
+
avatarAnimationFrameId = null;
|
|
26123
|
+
}
|
|
26124
|
+
};
|
|
26125
|
+
}
|
|
26126
|
+
/**
|
|
26127
|
+
* Ensures the shared animation loop is running while at least one avatar listener is active.
|
|
26128
|
+
*
|
|
26129
|
+
* @private utility of the avatar rendering system
|
|
26130
|
+
*/
|
|
26131
|
+
function ensureAvatarAnimationLoop() {
|
|
26132
|
+
if (avatarAnimationFrameId !== null || avatarAnimationListeners.size === 0 || typeof window === 'undefined') {
|
|
26133
|
+
return;
|
|
26134
|
+
}
|
|
26135
|
+
const runFrame = (now) => {
|
|
26136
|
+
avatarAnimationFrameId = null;
|
|
26137
|
+
for (const avatarAnimationListener of [...avatarAnimationListeners.values()]) {
|
|
26138
|
+
avatarAnimationListener(now);
|
|
26139
|
+
}
|
|
26140
|
+
ensureAvatarAnimationLoop();
|
|
26141
|
+
};
|
|
26142
|
+
avatarAnimationFrameId = window.requestAnimationFrame(runFrame);
|
|
26143
|
+
}
|
|
26144
|
+
|
|
26145
|
+
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
26146
|
+
/**
|
|
26147
|
+
* Shared `IntersectionObserver` callbacks grouped by observed element.
|
|
26148
|
+
*
|
|
26149
|
+
* @private utility of the avatar rendering system
|
|
26150
|
+
*/
|
|
26151
|
+
const avatarVisibilityListeners = new Map();
|
|
26152
|
+
/**
|
|
26153
|
+
* Lazily created shared `IntersectionObserver` used by avatar canvases.
|
|
26154
|
+
*
|
|
26155
|
+
* @private utility of the avatar rendering system
|
|
26156
|
+
*/
|
|
26157
|
+
let avatarVisibilityObserver = null;
|
|
26158
|
+
/**
|
|
26159
|
+
* Observes one avatar element and notifies the caller when it enters or leaves the viewport.
|
|
26160
|
+
*
|
|
26161
|
+
* @param element Observed avatar element.
|
|
26162
|
+
* @param avatarVisibilityListener Listener notified with the current visibility state.
|
|
26163
|
+
* @returns Cleanup function that stops observing the element.
|
|
26164
|
+
*
|
|
26165
|
+
* @private utility of the avatar rendering system
|
|
26166
|
+
*/
|
|
26167
|
+
function observeAvatarVisibility(element, avatarVisibilityListener) {
|
|
26168
|
+
if (typeof window === 'undefined' || typeof IntersectionObserver === 'undefined') {
|
|
26169
|
+
avatarVisibilityListener(true);
|
|
26170
|
+
return () => undefined;
|
|
26171
|
+
}
|
|
26172
|
+
const observer = getAvatarVisibilityObserver();
|
|
26173
|
+
const elementListeners = avatarVisibilityListeners.get(element) || new Set();
|
|
26174
|
+
elementListeners.add(avatarVisibilityListener);
|
|
26175
|
+
avatarVisibilityListeners.set(element, elementListeners);
|
|
26176
|
+
observer.observe(element);
|
|
26177
|
+
return () => {
|
|
26178
|
+
const currentElementListeners = avatarVisibilityListeners.get(element);
|
|
26179
|
+
if (!currentElementListeners) {
|
|
26180
|
+
return;
|
|
26181
|
+
}
|
|
26182
|
+
currentElementListeners.delete(avatarVisibilityListener);
|
|
26183
|
+
if (currentElementListeners.size > 0) {
|
|
26184
|
+
return;
|
|
26185
|
+
}
|
|
26186
|
+
avatarVisibilityListeners.delete(element);
|
|
26187
|
+
observer.unobserve(element);
|
|
26188
|
+
};
|
|
26189
|
+
}
|
|
26190
|
+
/**
|
|
26191
|
+
* Creates the shared `IntersectionObserver` used by all avatar canvases.
|
|
26192
|
+
*
|
|
26193
|
+
* @returns Shared observer instance.
|
|
26194
|
+
*
|
|
26195
|
+
* @private utility of the avatar rendering system
|
|
26196
|
+
*/
|
|
26197
|
+
function getAvatarVisibilityObserver() {
|
|
26198
|
+
if (avatarVisibilityObserver) {
|
|
26199
|
+
return avatarVisibilityObserver;
|
|
26200
|
+
}
|
|
26201
|
+
avatarVisibilityObserver = new IntersectionObserver((entries) => {
|
|
26202
|
+
for (const entry of entries) {
|
|
26203
|
+
const elementListeners = avatarVisibilityListeners.get(entry.target);
|
|
26204
|
+
if (!elementListeners) {
|
|
26205
|
+
continue;
|
|
26206
|
+
}
|
|
26207
|
+
const isVisible = entry.isIntersecting && entry.intersectionRatio > 0;
|
|
26208
|
+
for (const avatarVisibilityListener of elementListeners) {
|
|
26209
|
+
avatarVisibilityListener(isVisible);
|
|
26210
|
+
}
|
|
26211
|
+
}
|
|
26212
|
+
});
|
|
26213
|
+
return avatarVisibilityObserver;
|
|
26214
|
+
}
|
|
26215
|
+
|
|
26216
|
+
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
26217
|
+
/**
|
|
26218
|
+
* Active avatar instances currently consuming the shared pointer tracker.
|
|
26219
|
+
*
|
|
26220
|
+
* @private utility of the avatar rendering system
|
|
26221
|
+
*/
|
|
26222
|
+
let avatarPointerTrackingConsumerCount = 0;
|
|
26223
|
+
/**
|
|
26224
|
+
* Latest shared viewport pointer sample.
|
|
26225
|
+
*
|
|
26226
|
+
* @private utility of the avatar rendering system
|
|
26227
|
+
*/
|
|
26228
|
+
let currentAvatarPointerSnapshot = null;
|
|
26229
|
+
/**
|
|
26230
|
+
* Monotonic version incremented whenever the shared pointer snapshot changes.
|
|
26231
|
+
*
|
|
26232
|
+
* @private utility of the avatar rendering system
|
|
26233
|
+
*/
|
|
26234
|
+
let currentAvatarPointerSnapshotVersion = 0;
|
|
26235
|
+
/**
|
|
26236
|
+
* Monotonic version incremented whenever scrolling or resizing may invalidate cached avatar bounds.
|
|
26237
|
+
*
|
|
26238
|
+
* @private utility of the avatar rendering system
|
|
26239
|
+
*/
|
|
26240
|
+
let currentAvatarViewportLayoutVersion = 0;
|
|
26241
|
+
/**
|
|
26242
|
+
* Cleanup function for the lazily attached global listeners.
|
|
26243
|
+
*
|
|
26244
|
+
* @private utility of the avatar rendering system
|
|
26245
|
+
*/
|
|
26246
|
+
let releaseAvatarPointerTrackingListeners = null;
|
|
26247
|
+
/**
|
|
26248
|
+
* Starts the shared pointer tracker and returns a disposer for the caller.
|
|
26249
|
+
*
|
|
26250
|
+
* @returns Cleanup function that releases one consumer.
|
|
26251
|
+
*
|
|
26252
|
+
* @private utility of the avatar rendering system
|
|
26253
|
+
*/
|
|
26254
|
+
function retainAvatarPointerTracking() {
|
|
26255
|
+
avatarPointerTrackingConsumerCount++;
|
|
26256
|
+
if (avatarPointerTrackingConsumerCount === 1) {
|
|
26257
|
+
releaseAvatarPointerTrackingListeners = attachAvatarPointerTrackingListeners();
|
|
26258
|
+
}
|
|
26259
|
+
return () => {
|
|
26260
|
+
avatarPointerTrackingConsumerCount = Math.max(0, avatarPointerTrackingConsumerCount - 1);
|
|
26261
|
+
if (avatarPointerTrackingConsumerCount === 0) {
|
|
26262
|
+
currentAvatarPointerSnapshot = null;
|
|
26263
|
+
releaseAvatarPointerTrackingListeners === null || releaseAvatarPointerTrackingListeners === void 0 ? void 0 : releaseAvatarPointerTrackingListeners();
|
|
26264
|
+
releaseAvatarPointerTrackingListeners = null;
|
|
26265
|
+
}
|
|
26266
|
+
};
|
|
26267
|
+
}
|
|
26268
|
+
/**
|
|
26269
|
+
* Returns the latest shared viewport pointer sample when available.
|
|
26270
|
+
*
|
|
26271
|
+
* @returns Shared pointer snapshot or `null`.
|
|
26272
|
+
*
|
|
26273
|
+
* @private utility of the avatar rendering system
|
|
26274
|
+
*/
|
|
26275
|
+
function getAvatarPointerSnapshot() {
|
|
26276
|
+
return currentAvatarPointerSnapshot;
|
|
26277
|
+
}
|
|
26278
|
+
/**
|
|
26279
|
+
* Returns the current pointer snapshot version.
|
|
26280
|
+
*
|
|
26281
|
+
* @returns Monotonic pointer snapshot version.
|
|
26282
|
+
*
|
|
26283
|
+
* @private utility of the avatar rendering system
|
|
26284
|
+
*/
|
|
26285
|
+
function getAvatarPointerSnapshotVersion() {
|
|
26286
|
+
return currentAvatarPointerSnapshotVersion;
|
|
26287
|
+
}
|
|
26288
|
+
/**
|
|
26289
|
+
* Returns the current viewport-layout version used to invalidate cached avatar bounds.
|
|
26290
|
+
*
|
|
26291
|
+
* @returns Monotonic viewport-layout version.
|
|
26292
|
+
*
|
|
26293
|
+
* @private utility of the avatar rendering system
|
|
26294
|
+
*/
|
|
26295
|
+
function getAvatarViewportLayoutVersion() {
|
|
26296
|
+
return currentAvatarViewportLayoutVersion;
|
|
26297
|
+
}
|
|
26298
|
+
/**
|
|
26299
|
+
* Attaches the global pointer/touch listeners used by all live avatar canvases.
|
|
26300
|
+
*
|
|
26301
|
+
* @returns Cleanup function for the attached listeners.
|
|
26302
|
+
*
|
|
26303
|
+
* @private utility of the avatar rendering system
|
|
26304
|
+
*/
|
|
26305
|
+
function attachAvatarPointerTrackingListeners() {
|
|
26306
|
+
if (typeof window === 'undefined') {
|
|
26307
|
+
return () => undefined;
|
|
26308
|
+
}
|
|
26309
|
+
const clearPointerSnapshot = () => {
|
|
26310
|
+
if (currentAvatarPointerSnapshot === null) {
|
|
26311
|
+
return;
|
|
26312
|
+
}
|
|
26313
|
+
currentAvatarPointerSnapshot = null;
|
|
26314
|
+
currentAvatarPointerSnapshotVersion++;
|
|
26315
|
+
};
|
|
26316
|
+
const updatePointerSnapshot = (clientX, clientY, pointerType) => {
|
|
26317
|
+
currentAvatarPointerSnapshot = {
|
|
26318
|
+
clientX,
|
|
26319
|
+
clientY,
|
|
26320
|
+
isPointerActive: true,
|
|
26321
|
+
pointerType,
|
|
26322
|
+
};
|
|
26323
|
+
currentAvatarPointerSnapshotVersion++;
|
|
26324
|
+
};
|
|
26325
|
+
const invalidateViewportLayout = () => {
|
|
26326
|
+
currentAvatarViewportLayoutVersion++;
|
|
26327
|
+
};
|
|
26328
|
+
const handlePointerMove = (event) => {
|
|
26329
|
+
updatePointerSnapshot(event.clientX, event.clientY, normalizeAvatarPointerType(event.pointerType));
|
|
26330
|
+
};
|
|
26331
|
+
const handlePointerDown = (event) => {
|
|
26332
|
+
updatePointerSnapshot(event.clientX, event.clientY, normalizeAvatarPointerType(event.pointerType));
|
|
26333
|
+
};
|
|
26334
|
+
const handlePointerUp = (event) => {
|
|
26335
|
+
if (normalizeAvatarPointerType(event.pointerType) !== 'mouse') {
|
|
26336
|
+
clearPointerSnapshot();
|
|
26337
|
+
}
|
|
26338
|
+
};
|
|
26339
|
+
const handleMouseOut = (event) => {
|
|
26340
|
+
if (!event.relatedTarget) {
|
|
26341
|
+
clearPointerSnapshot();
|
|
26342
|
+
}
|
|
26343
|
+
};
|
|
26344
|
+
const handleTouchEvent = (event) => {
|
|
26345
|
+
const touch = event.touches[0] || event.changedTouches[0];
|
|
26346
|
+
if (!touch) {
|
|
26347
|
+
clearPointerSnapshot();
|
|
26348
|
+
return;
|
|
26349
|
+
}
|
|
26350
|
+
updatePointerSnapshot(touch.clientX, touch.clientY, 'touch');
|
|
26351
|
+
};
|
|
26352
|
+
window.addEventListener('pointermove', handlePointerMove, { passive: true });
|
|
26353
|
+
window.addEventListener('pointerdown', handlePointerDown, { passive: true });
|
|
26354
|
+
window.addEventListener('pointerup', handlePointerUp, { passive: true });
|
|
26355
|
+
window.addEventListener('pointercancel', clearPointerSnapshot, { passive: true });
|
|
26356
|
+
window.addEventListener('mouseout', handleMouseOut, { passive: true });
|
|
26357
|
+
window.addEventListener('blur', clearPointerSnapshot);
|
|
26358
|
+
window.addEventListener('touchstart', handleTouchEvent, { passive: true });
|
|
26359
|
+
window.addEventListener('touchmove', handleTouchEvent, { passive: true });
|
|
26360
|
+
window.addEventListener('touchend', clearPointerSnapshot, { passive: true });
|
|
26361
|
+
window.addEventListener('touchcancel', clearPointerSnapshot, { passive: true });
|
|
26362
|
+
window.addEventListener('scroll', invalidateViewportLayout, { passive: true, capture: true });
|
|
26363
|
+
window.addEventListener('resize', invalidateViewportLayout, { passive: true });
|
|
26364
|
+
return () => {
|
|
26365
|
+
window.removeEventListener('pointermove', handlePointerMove);
|
|
26366
|
+
window.removeEventListener('pointerdown', handlePointerDown);
|
|
26367
|
+
window.removeEventListener('pointerup', handlePointerUp);
|
|
26368
|
+
window.removeEventListener('pointercancel', clearPointerSnapshot);
|
|
26369
|
+
window.removeEventListener('mouseout', handleMouseOut);
|
|
26370
|
+
window.removeEventListener('blur', clearPointerSnapshot);
|
|
26371
|
+
window.removeEventListener('touchstart', handleTouchEvent);
|
|
26372
|
+
window.removeEventListener('touchmove', handleTouchEvent);
|
|
26373
|
+
window.removeEventListener('touchend', clearPointerSnapshot);
|
|
26374
|
+
window.removeEventListener('touchcancel', clearPointerSnapshot);
|
|
26375
|
+
window.removeEventListener('scroll', invalidateViewportLayout, true);
|
|
26376
|
+
window.removeEventListener('resize', invalidateViewportLayout);
|
|
26377
|
+
};
|
|
26378
|
+
}
|
|
26379
|
+
/**
|
|
26380
|
+
* Normalizes browser pointer-type strings into the shared avatar contract.
|
|
26381
|
+
*
|
|
26382
|
+
* @param pointerType Raw browser pointer type.
|
|
26383
|
+
* @returns Shared pointer type.
|
|
26384
|
+
*
|
|
26385
|
+
* @private utility of the avatar rendering system
|
|
26386
|
+
*/
|
|
26387
|
+
function normalizeAvatarPointerType(pointerType) {
|
|
26388
|
+
if (pointerType === 'touch' || pointerType === 'pen') {
|
|
26389
|
+
return pointerType;
|
|
26390
|
+
}
|
|
26391
|
+
return 'mouse';
|
|
26392
|
+
}
|
|
26393
|
+
|
|
25731
26394
|
/* eslint-disable no-magic-numbers */
|
|
25732
26395
|
/**
|
|
25733
26396
|
* Builds a smoothly morphing octopus-like silhouette from deterministic parameters.
|
|
@@ -25804,8 +26467,9 @@
|
|
|
25804
26467
|
* @private shared geometry helper of `octopus3AvatarVisual` and `asciiOctopusAvatarVisual`
|
|
25805
26468
|
*/
|
|
25806
26469
|
function createOrganicOctopusTentacleShapes(options) {
|
|
25807
|
-
const { size, centerX, centerY, bodyRadius, horizontalStretch, tentacleCount, shapePhase, createRandom, timeMs, saltPrefix, } = options;
|
|
26470
|
+
const { size, centerX, centerY, bodyRadius, horizontalStretch, tentacleCount, shapePhase, createRandom, timeMs, saltPrefix, bodyPoints } = options;
|
|
25808
26471
|
const baseY = centerY + bodyRadius * 0.74;
|
|
26472
|
+
const lowerBodyAnchorPoints = bodyPoints ? resolveTentacleBodyAnchorPoints(bodyPoints, centerY + bodyRadius * 0.04) : null;
|
|
25809
26473
|
return Array.from({ length: tentacleCount }, (_, tentacleIndex) => {
|
|
25810
26474
|
const tentacleRandom = createRandom(`${saltPrefix}-tentacle-${tentacleIndex}`);
|
|
25811
26475
|
const spreadProgress = tentacleCount === 1 ? 0.5 : tentacleIndex / (tentacleCount - 1);
|
|
@@ -25817,10 +26481,21 @@
|
|
|
25817
26481
|
const curlDirection = centeredProgress === 0 ? (tentacleRandom() < 0.5 ? -1 : 1) : Math.sign(centeredProgress);
|
|
25818
26482
|
const lateralReach = centeredProgress * size * (0.1 + tentacleRandom() * 0.1) + temporalSway;
|
|
25819
26483
|
const tipReach = curlDirection * size * (0.025 + tentacleRandom() * 0.07);
|
|
25820
|
-
const
|
|
25821
|
-
|
|
25822
|
-
|
|
25823
|
-
|
|
26484
|
+
const startYOffset = Math.abs(centeredProgress) * size * 0.012 + tentacleRandom() * size * 0.01;
|
|
26485
|
+
const startPoint = lowerBodyAnchorPoints && lowerBodyAnchorPoints.length >= 2
|
|
26486
|
+
? createInsetTentacleStartPoint({
|
|
26487
|
+
bodyPoints: lowerBodyAnchorPoints,
|
|
26488
|
+
anchorProgress: spreadProgress,
|
|
26489
|
+
centerX,
|
|
26490
|
+
centerY,
|
|
26491
|
+
bodyRadius,
|
|
26492
|
+
centeredProgress,
|
|
26493
|
+
startYOffset,
|
|
26494
|
+
})
|
|
26495
|
+
: {
|
|
26496
|
+
x: centerX + centeredProgress * bodyRadius * horizontalStretch * 1.52,
|
|
26497
|
+
y: baseY + startYOffset,
|
|
26498
|
+
};
|
|
25824
26499
|
const controlPointOne = {
|
|
25825
26500
|
x: startPoint.x + centeredProgress * size * 0.045 + temporalSway * 0.4,
|
|
25826
26501
|
y: startPoint.y + flowLength * (0.21 + tentacleRandom() * 0.08),
|
|
@@ -25850,6 +26525,67 @@
|
|
|
25850
26525
|
};
|
|
25851
26526
|
});
|
|
25852
26527
|
}
|
|
26528
|
+
/**
|
|
26529
|
+
* Narrows the body contour to lower anchor points that can safely host tentacle roots.
|
|
26530
|
+
*
|
|
26531
|
+
* @param bodyPoints Generated closed-loop body points.
|
|
26532
|
+
* @param lowerBodyThresholdY Minimum Y coordinate considered part of the lower mantle.
|
|
26533
|
+
* @returns Body points sorted from left to right across the lower silhouette.
|
|
26534
|
+
*
|
|
26535
|
+
* @private shared geometry helper of `octopus3AvatarVisual`
|
|
26536
|
+
*/
|
|
26537
|
+
function resolveTentacleBodyAnchorPoints(bodyPoints, lowerBodyThresholdY) {
|
|
26538
|
+
const lowerBodyPoints = bodyPoints.filter((bodyPoint) => bodyPoint.y >= lowerBodyThresholdY).sort((leftPoint, rightPoint) => leftPoint.x - rightPoint.x);
|
|
26539
|
+
if (lowerBodyPoints.length >= 2) {
|
|
26540
|
+
return lowerBodyPoints;
|
|
26541
|
+
}
|
|
26542
|
+
return [...bodyPoints].sort((leftPoint, rightPoint) => leftPoint.x - rightPoint.x);
|
|
26543
|
+
}
|
|
26544
|
+
/**
|
|
26545
|
+
* Resolves one tentacle root from the provided lower body contour and nudges it inside the mantle.
|
|
26546
|
+
*
|
|
26547
|
+
* @param options Tentacle anchor options.
|
|
26548
|
+
* @returns Tentacle start point safely embedded inside the body silhouette.
|
|
26549
|
+
*
|
|
26550
|
+
* @private shared geometry helper of `octopus3AvatarVisual`
|
|
26551
|
+
*/
|
|
26552
|
+
function createInsetTentacleStartPoint(options) {
|
|
26553
|
+
const { bodyPoints, anchorProgress, centerX, centerY, bodyRadius, centeredProgress, startYOffset } = options;
|
|
26554
|
+
const clampedAnchorProgress = Math.min(0.94, Math.max(0.06, anchorProgress));
|
|
26555
|
+
const bodyAnchorPoint = interpolatePointAlongTentacleAnchors(bodyPoints, clampedAnchorProgress);
|
|
26556
|
+
const inwardX = centerX - bodyAnchorPoint.x;
|
|
26557
|
+
const inwardY = centerY + bodyRadius * 0.08 - bodyAnchorPoint.y;
|
|
26558
|
+
const inwardLength = Math.hypot(inwardX, inwardY) || 1;
|
|
26559
|
+
const insetDistance = bodyRadius * (0.12 + Math.abs(centeredProgress) * 0.05) + startYOffset * 0.32;
|
|
26560
|
+
return {
|
|
26561
|
+
x: bodyAnchorPoint.x + (inwardX / inwardLength) * insetDistance,
|
|
26562
|
+
y: bodyAnchorPoint.y + (inwardY / inwardLength) * insetDistance,
|
|
26563
|
+
};
|
|
26564
|
+
}
|
|
26565
|
+
/**
|
|
26566
|
+
* Interpolates one left-to-right anchor point along the prepared lower body contour.
|
|
26567
|
+
*
|
|
26568
|
+
* @param bodyPoints Lower body contour points sorted from left to right.
|
|
26569
|
+
* @param progress Interpolation progress in the range `[0, 1]`.
|
|
26570
|
+
* @returns Interpolated anchor point.
|
|
26571
|
+
*
|
|
26572
|
+
* @private shared geometry helper of `octopus3AvatarVisual`
|
|
26573
|
+
*/
|
|
26574
|
+
function interpolatePointAlongTentacleAnchors(bodyPoints, progress) {
|
|
26575
|
+
if (bodyPoints.length === 1) {
|
|
26576
|
+
return bodyPoints[0];
|
|
26577
|
+
}
|
|
26578
|
+
const anchorIndex = progress * (bodyPoints.length - 1);
|
|
26579
|
+
const startIndex = Math.floor(anchorIndex);
|
|
26580
|
+
const endIndex = Math.min(bodyPoints.length - 1, startIndex + 1);
|
|
26581
|
+
const blend = anchorIndex - startIndex;
|
|
26582
|
+
const startPoint = bodyPoints[startIndex];
|
|
26583
|
+
const endPoint = bodyPoints[endIndex];
|
|
26584
|
+
return {
|
|
26585
|
+
x: startPoint.x + (endPoint.x - startPoint.x) * blend,
|
|
26586
|
+
y: startPoint.y + (endPoint.y - startPoint.y) * blend,
|
|
26587
|
+
};
|
|
26588
|
+
}
|
|
25853
26589
|
/**
|
|
25854
26590
|
* Samples the cubic tentacle centerline and offsets normals to build a filled ribbon.
|
|
25855
26591
|
*
|
|
@@ -25879,6 +26615,26 @@
|
|
|
25879
26615
|
};
|
|
25880
26616
|
});
|
|
25881
26617
|
}
|
|
26618
|
+
/**
|
|
26619
|
+
* Resolves smooth pupil offsets that blend autonomous idle drift with live viewer tracking.
|
|
26620
|
+
*
|
|
26621
|
+
* @param options Eye motion options.
|
|
26622
|
+
* @returns Resolved pupil offsets.
|
|
26623
|
+
*
|
|
26624
|
+
* @private shared geometry helper of octopus avatar visuals
|
|
26625
|
+
*/
|
|
26626
|
+
function resolveOrganicEyeMotion(options) {
|
|
26627
|
+
const { radiusX, radiusY, timeMs, phase, interaction, autonomousDriftRatioX = 0.12, autonomousDriftRatioY = 0.08, } = options;
|
|
26628
|
+
const autonomousOffsetX = Math.sin(timeMs / 1280 + phase) * radiusX * autonomousDriftRatioX;
|
|
26629
|
+
const autonomousOffsetY = Math.cos(timeMs / 940 + phase) * radiusY * autonomousDriftRatioY;
|
|
26630
|
+
const interactionBlend = Math.min(1, interaction.intensity * 0.9);
|
|
26631
|
+
return {
|
|
26632
|
+
pupilOffsetX: autonomousOffsetX * (1 - interactionBlend) +
|
|
26633
|
+
interaction.gazeX * radiusX * (0.18 + interactionBlend * 0.18),
|
|
26634
|
+
pupilOffsetY: autonomousOffsetY * (1 - interactionBlend) +
|
|
26635
|
+
interaction.gazeY * radiusY * (0.16 + interactionBlend * 0.16),
|
|
26636
|
+
};
|
|
26637
|
+
}
|
|
25882
26638
|
/**
|
|
25883
26639
|
* Samples one point on a cubic Bezier curve.
|
|
25884
26640
|
*
|
|
@@ -25932,13 +26688,14 @@
|
|
|
25932
26688
|
const asciiOctopusAvatarVisual = {
|
|
25933
26689
|
id: 'ascii-octopus',
|
|
25934
26690
|
title: 'AsciiOctopus',
|
|
25935
|
-
description: 'Morphing alien octopus translated into animated ASCII glyphs with
|
|
26691
|
+
description: 'Morphing alien octopus translated into animated ASCII glyphs with responsive eyes and seeded geometry.',
|
|
25936
26692
|
isAnimated: true,
|
|
25937
|
-
|
|
26693
|
+
supportsPointerTracking: true,
|
|
26694
|
+
render({ context, size, palette, createRandom, timeMs, interaction }) {
|
|
25938
26695
|
const gridRandom = createRandom('ascii-octopus-grid');
|
|
25939
26696
|
const staticRandom = createRandom('ascii-octopus-static');
|
|
25940
26697
|
const gridMetrics = createAsciiGridMetrics(size, gridRandom);
|
|
25941
|
-
const layout = createAsciiOctopusLayout(size, timeMs, createRandom, staticRandom);
|
|
26698
|
+
const layout = createAsciiOctopusLayout(size, timeMs, createRandom, staticRandom, interaction);
|
|
25942
26699
|
drawAvatarFrame(context, size, palette);
|
|
25943
26700
|
drawAsciiBackdrop(context, size, palette, layout, timeMs);
|
|
25944
26701
|
context.save();
|
|
@@ -26012,7 +26769,8 @@
|
|
|
26012
26769
|
*/
|
|
26013
26770
|
function resolveAsciiGlyph(options) {
|
|
26014
26771
|
const { point, layout, palette, cellWidth, cellHeight, noise, timeMs } = options;
|
|
26015
|
-
const eyeGlyphDescriptor = resolveEyeGlyph(point, layout.leftEye,
|
|
26772
|
+
const eyeGlyphDescriptor = resolveEyeGlyph(point, layout.leftEye, layout.interaction, palette, timeMs) ||
|
|
26773
|
+
resolveEyeGlyph(point, layout.rightEye, layout.interaction, palette, timeMs);
|
|
26016
26774
|
if (eyeGlyphDescriptor) {
|
|
26017
26775
|
return eyeGlyphDescriptor;
|
|
26018
26776
|
}
|
|
@@ -26057,9 +26815,14 @@
|
|
|
26057
26815
|
*
|
|
26058
26816
|
* @private helper of `asciiOctopusAvatarVisual`
|
|
26059
26817
|
*/
|
|
26060
|
-
function resolveEyeGlyph(point, eyeFeature, palette, timeMs) {
|
|
26061
|
-
const pupilOffsetX =
|
|
26062
|
-
|
|
26818
|
+
function resolveEyeGlyph(point, eyeFeature, interaction, palette, timeMs) {
|
|
26819
|
+
const { pupilOffsetX, pupilOffsetY } = resolveOrganicEyeMotion({
|
|
26820
|
+
radiusX: eyeFeature.radiusX,
|
|
26821
|
+
radiusY: eyeFeature.radiusY,
|
|
26822
|
+
timeMs,
|
|
26823
|
+
phase: eyeFeature.phase,
|
|
26824
|
+
interaction,
|
|
26825
|
+
});
|
|
26063
26826
|
const scleraDistance = measureRotatedEllipseDistance(point, eyeFeature.centerX, eyeFeature.centerY, eyeFeature.radiusX, eyeFeature.radiusY, eyeFeature.rotation);
|
|
26064
26827
|
if (scleraDistance > 1.08) {
|
|
26065
26828
|
return null;
|
|
@@ -26233,9 +26996,9 @@
|
|
|
26233
26996
|
*
|
|
26234
26997
|
* @private helper of `asciiOctopusAvatarVisual`
|
|
26235
26998
|
*/
|
|
26236
|
-
function createAsciiOctopusLayout(size, timeMs, createRandom, staticRandom) {
|
|
26237
|
-
const centerX = size * (0.5 + (staticRandom() - 0.5) * 0.02);
|
|
26238
|
-
const centerY = size * (0.41 + staticRandom() * 0.05);
|
|
26999
|
+
function createAsciiOctopusLayout(size, timeMs, createRandom, staticRandom, interaction) {
|
|
27000
|
+
const centerX = size * (0.5 + (staticRandom() - 0.5) * 0.02) + interaction.bodyOffsetX * size * 0.05;
|
|
27001
|
+
const centerY = size * (0.41 + staticRandom() * 0.05) + interaction.bodyOffsetY * size * 0.035;
|
|
26239
27002
|
const bodyRadius = size * (0.195 + staticRandom() * 0.05);
|
|
26240
27003
|
const horizontalStretch = 1.08 + staticRandom() * 0.22;
|
|
26241
27004
|
const verticalStretch = 0.88 + staticRandom() * 0.14;
|
|
@@ -26275,6 +27038,7 @@
|
|
|
26275
27038
|
createRandom,
|
|
26276
27039
|
timeMs,
|
|
26277
27040
|
saltPrefix: 'ascii-octopus',
|
|
27041
|
+
bodyPoints,
|
|
26278
27042
|
});
|
|
26279
27043
|
const sampledTentacles = tentacleShapes.map(sampleOrganicTentacleRibbonPoints);
|
|
26280
27044
|
const leftEye = {
|
|
@@ -26295,7 +27059,7 @@
|
|
|
26295
27059
|
};
|
|
26296
27060
|
const mouthPoints = sampleQuadraticBezierPoints({ x: centerX - size * 0.074, y: centerY + size * 0.092 }, {
|
|
26297
27061
|
x: centerX,
|
|
26298
|
-
y: centerY + size * (0.142 + Math.sin(timeMs / 620 + shapePhase) * 0.016),
|
|
27062
|
+
y: centerY + size * (0.142 + Math.sin(timeMs / 620 + shapePhase) * 0.016) + interaction.gazeY * size * 0.012,
|
|
26299
27063
|
}, { x: centerX + size * 0.074, y: centerY + size * 0.092 }, 12);
|
|
26300
27064
|
let leftBound = Number.POSITIVE_INFINITY;
|
|
26301
27065
|
let rightBound = Number.NEGATIVE_INFINITY;
|
|
@@ -26321,6 +27085,7 @@
|
|
|
26321
27085
|
bodyRadius,
|
|
26322
27086
|
horizontalStretch,
|
|
26323
27087
|
shapePhase,
|
|
27088
|
+
interaction,
|
|
26324
27089
|
bodyPoints,
|
|
26325
27090
|
sampledTentacles,
|
|
26326
27091
|
leftEye,
|
|
@@ -27041,15 +27806,16 @@
|
|
|
27041
27806
|
const octopusAvatarVisual = {
|
|
27042
27807
|
id: 'octopus',
|
|
27043
27808
|
title: 'Octopus',
|
|
27044
|
-
description: 'Playful underwater mascot with animated tentacles, bubbles, and
|
|
27809
|
+
description: 'Playful underwater mascot with cursor-following eyes, animated tentacles, bubbles, and seeded markings.',
|
|
27045
27810
|
isAnimated: true,
|
|
27046
|
-
|
|
27811
|
+
supportsPointerTracking: true,
|
|
27812
|
+
render({ context, size, palette, createRandom, timeMs, interaction }) {
|
|
27047
27813
|
const staticRandom = createRandom('octopus-static');
|
|
27048
27814
|
const bubbleRandom = createRandom('octopus-bubbles');
|
|
27049
27815
|
const bubbleCount = 8;
|
|
27050
27816
|
const bubbleRadiusBase = size * 0.02;
|
|
27051
|
-
const centerX = size * 0.5;
|
|
27052
|
-
const centerY = size * 0.42;
|
|
27817
|
+
const centerX = size * 0.5 + interaction.bodyOffsetX * size * 0.035;
|
|
27818
|
+
const centerY = size * 0.42 + interaction.bodyOffsetY * size * 0.024;
|
|
27053
27819
|
const headRadius = size * (0.19 + staticRandom() * 0.03);
|
|
27054
27820
|
const mantleHeight = headRadius * 1.18;
|
|
27055
27821
|
const tentacleLength = size * (0.18 + staticRandom() * 0.06);
|
|
@@ -27129,11 +27895,9 @@
|
|
|
27129
27895
|
}
|
|
27130
27896
|
const eyeOffsetX = headRadius * 0.42;
|
|
27131
27897
|
const eyeY = centerY + headRadius * 0.04;
|
|
27132
|
-
const pupilDriftX = Math.sin(timeMs / 850) * headRadius * 0.05;
|
|
27133
|
-
const pupilDriftY = Math.cos(timeMs / 930) * headRadius * 0.03;
|
|
27134
27898
|
const eyeRadius = headRadius * 0.22;
|
|
27135
|
-
drawEye(context, centerX - eyeOffsetX, eyeY, eyeRadius, palette,
|
|
27136
|
-
drawEye(context, centerX + eyeOffsetX, eyeY, eyeRadius, palette,
|
|
27899
|
+
drawEye(context, centerX - eyeOffsetX, eyeY, eyeRadius, palette, timeMs, interaction, 0);
|
|
27900
|
+
drawEye(context, centerX + eyeOffsetX, eyeY, eyeRadius, palette, timeMs, interaction, Math.PI / 5);
|
|
27137
27901
|
context.beginPath();
|
|
27138
27902
|
context.arc(centerX - headRadius * 0.28, centerY + headRadius * 0.3, headRadius * 0.12, 0, Math.PI * 2);
|
|
27139
27903
|
context.arc(centerX + headRadius * 0.28, centerY + headRadius * 0.3, headRadius * 0.12, 0, Math.PI * 2);
|
|
@@ -27156,22 +27920,32 @@
|
|
|
27156
27920
|
* @param centerY Eye center Y coordinate.
|
|
27157
27921
|
* @param radius Eye radius.
|
|
27158
27922
|
* @param palette Derived avatar palette.
|
|
27159
|
-
* @param
|
|
27160
|
-
* @param
|
|
27923
|
+
* @param timeMs Current animation time in milliseconds.
|
|
27924
|
+
* @param interaction Smoothed avatar interaction state.
|
|
27925
|
+
* @param phase Seed-based phase offset.
|
|
27161
27926
|
*
|
|
27162
27927
|
* @private helper of `octopusAvatarVisual`
|
|
27163
27928
|
*/
|
|
27164
|
-
function drawEye(context, centerX, centerY, radius, palette,
|
|
27929
|
+
function drawEye(context, centerX, centerY, radius, palette, timeMs, interaction, phase) {
|
|
27930
|
+
const { pupilOffsetX, pupilOffsetY } = resolveOrganicEyeMotion({
|
|
27931
|
+
radiusX: radius,
|
|
27932
|
+
radiusY: radius,
|
|
27933
|
+
timeMs,
|
|
27934
|
+
phase,
|
|
27935
|
+
interaction,
|
|
27936
|
+
autonomousDriftRatioX: 0.05,
|
|
27937
|
+
autonomousDriftRatioY: 0.03,
|
|
27938
|
+
});
|
|
27165
27939
|
context.beginPath();
|
|
27166
27940
|
context.arc(centerX, centerY, radius, 0, Math.PI * 2);
|
|
27167
27941
|
context.fillStyle = '#ffffff';
|
|
27168
27942
|
context.fill();
|
|
27169
27943
|
context.beginPath();
|
|
27170
|
-
context.arc(centerX +
|
|
27944
|
+
context.arc(centerX + pupilOffsetX, centerY + pupilOffsetY, radius * 0.45, 0, Math.PI * 2);
|
|
27171
27945
|
context.fillStyle = palette.ink;
|
|
27172
27946
|
context.fill();
|
|
27173
27947
|
context.beginPath();
|
|
27174
|
-
context.arc(centerX +
|
|
27948
|
+
context.arc(centerX + pupilOffsetX - radius * 0.12, centerY + pupilOffsetY - radius * 0.12, radius * 0.15, 0, Math.PI * 2);
|
|
27175
27949
|
context.fillStyle = '#ffffff';
|
|
27176
27950
|
context.fill();
|
|
27177
27951
|
context.beginPath();
|
|
@@ -27190,12 +27964,13 @@
|
|
|
27190
27964
|
const octopus2AvatarVisual = {
|
|
27191
27965
|
id: 'octopus2',
|
|
27192
27966
|
title: 'Octopus2',
|
|
27193
|
-
description: 'Organic alien octopus rendered as one continuously morphing blob with luminous eyes
|
|
27967
|
+
description: 'Organic alien octopus rendered as one continuously morphing blob with responsive luminous eyes.',
|
|
27194
27968
|
isAnimated: true,
|
|
27195
|
-
|
|
27969
|
+
supportsPointerTracking: true,
|
|
27970
|
+
render({ context, size, palette, createRandom, timeMs, interaction }) {
|
|
27196
27971
|
const staticRandom = createRandom('octopus2-static');
|
|
27197
|
-
const centerX = size * 0.5;
|
|
27198
|
-
const centerY = size * (0.48 + staticRandom() * 0.03);
|
|
27972
|
+
const centerX = size * 0.5 + interaction.bodyOffsetX * size * 0.042;
|
|
27973
|
+
const centerY = size * (0.48 + staticRandom() * 0.03) + interaction.bodyOffsetY * size * 0.028;
|
|
27199
27974
|
const bodyRadius = size * (0.25 + staticRandom() * 0.035);
|
|
27200
27975
|
const horizontalStretch = 1.04 + staticRandom() * 0.16;
|
|
27201
27976
|
const verticalStretch = 0.94 + staticRandom() * 0.12;
|
|
@@ -27266,11 +28041,11 @@
|
|
|
27266
28041
|
const eyeCenterY = centerY - size * 0.02;
|
|
27267
28042
|
const eyeRadiusX = size * 0.072;
|
|
27268
28043
|
const eyeRadiusY = size * 0.086;
|
|
27269
|
-
drawAlienEye(context, centerX - eyeOffsetX, eyeCenterY, eyeRadiusX, eyeRadiusY, palette, timeMs, shapePhase);
|
|
27270
|
-
drawAlienEye(context, centerX + eyeOffsetX, eyeCenterY, eyeRadiusX, eyeRadiusY, palette, timeMs, shapePhase + Math.PI / 5);
|
|
28044
|
+
drawAlienEye(context, centerX - eyeOffsetX, eyeCenterY, eyeRadiusX, eyeRadiusY, palette, timeMs, shapePhase, interaction);
|
|
28045
|
+
drawAlienEye(context, centerX + eyeOffsetX, eyeCenterY, eyeRadiusX, eyeRadiusY, palette, timeMs, shapePhase + Math.PI / 5, interaction);
|
|
27271
28046
|
context.beginPath();
|
|
27272
28047
|
context.moveTo(centerX - size * 0.08, centerY + size * 0.12);
|
|
27273
|
-
context.quadraticCurveTo(centerX, centerY + size * (0.175 + Math.sin(timeMs / 520 + shapePhase) * 0.012), centerX + size * 0.08, centerY + size * 0.12);
|
|
28048
|
+
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);
|
|
27274
28049
|
context.strokeStyle = `${palette.ink}b3`;
|
|
27275
28050
|
context.lineWidth = size * 0.013;
|
|
27276
28051
|
context.lineCap = 'round';
|
|
@@ -27348,12 +28123,19 @@
|
|
|
27348
28123
|
* @param palette Derived avatar palette.
|
|
27349
28124
|
* @param timeMs Current animation time in milliseconds.
|
|
27350
28125
|
* @param phase Seed-based animation phase.
|
|
28126
|
+
* @param interaction Smoothed avatar interaction state.
|
|
27351
28127
|
*
|
|
27352
28128
|
* @private helper of `octopus2AvatarVisual`
|
|
27353
28129
|
*/
|
|
27354
|
-
function drawAlienEye(context, centerX, centerY, radiusX, radiusY, palette, timeMs, phase) {
|
|
27355
|
-
const pupilOffsetX =
|
|
27356
|
-
|
|
28130
|
+
function drawAlienEye(context, centerX, centerY, radiusX, radiusY, palette, timeMs, phase, interaction) {
|
|
28131
|
+
const { pupilOffsetX, pupilOffsetY } = resolveOrganicEyeMotion({
|
|
28132
|
+
radiusX,
|
|
28133
|
+
radiusY,
|
|
28134
|
+
timeMs,
|
|
28135
|
+
phase,
|
|
28136
|
+
interaction,
|
|
28137
|
+
autonomousDriftRatioY: 0.1,
|
|
28138
|
+
});
|
|
27357
28139
|
context.save();
|
|
27358
28140
|
context.beginPath();
|
|
27359
28141
|
context.ellipse(centerX, centerY, radiusX, radiusY, 0, 0, Math.PI * 2);
|
|
@@ -27393,12 +28175,13 @@
|
|
|
27393
28175
|
const octopus3AvatarVisual = {
|
|
27394
28176
|
id: 'octopus3',
|
|
27395
28177
|
title: 'Octopus3',
|
|
27396
|
-
description: 'Gelatinous alien octopus with a morphing mantle,
|
|
28178
|
+
description: 'Gelatinous alien octopus with a morphing mantle, responsive eyes, and visible ribbon tentacles.',
|
|
27397
28179
|
isAnimated: true,
|
|
27398
|
-
|
|
28180
|
+
supportsPointerTracking: true,
|
|
28181
|
+
render({ context, size, palette, createRandom, timeMs, interaction }) {
|
|
27399
28182
|
const staticRandom = createRandom('octopus3-static');
|
|
27400
|
-
const centerX = size * (0.5 + (staticRandom() - 0.5) * 0.02);
|
|
27401
|
-
const centerY = size * (0.41 + staticRandom() * 0.05);
|
|
28183
|
+
const centerX = size * (0.5 + (staticRandom() - 0.5) * 0.02) + interaction.bodyOffsetX * size * 0.05;
|
|
28184
|
+
const centerY = size * (0.41 + staticRandom() * 0.05) + interaction.bodyOffsetY * size * 0.035;
|
|
27402
28185
|
const bodyRadius = size * (0.2 + staticRandom() * 0.045);
|
|
27403
28186
|
const horizontalStretch = 1.08 + staticRandom() * 0.22;
|
|
27404
28187
|
const verticalStretch = 0.9 + staticRandom() * 0.12;
|
|
@@ -27438,6 +28221,7 @@
|
|
|
27438
28221
|
createRandom,
|
|
27439
28222
|
timeMs,
|
|
27440
28223
|
saltPrefix: 'octopus3',
|
|
28224
|
+
bodyPoints,
|
|
27441
28225
|
});
|
|
27442
28226
|
drawAvatarFrame(context, size, palette);
|
|
27443
28227
|
drawOctopus3Atmosphere(context, size, palette, centerX, centerY, timeMs, shapePhase);
|
|
@@ -27483,11 +28267,11 @@
|
|
|
27483
28267
|
context.ellipse(centerX, centerY - size * 0.14, size * 0.18, size * 0.062, 0, Math.PI, Math.PI * 2);
|
|
27484
28268
|
context.fillStyle = `${palette.highlight}3d`;
|
|
27485
28269
|
context.fill();
|
|
27486
|
-
drawSeededEye(context, centerX - eyeSpacing, centerY - size * 0.01, eyeRadiusX, eyeRadiusY, (staticRandom() - 0.5) * 0.28, palette, timeMs, shapePhase);
|
|
27487
|
-
drawSeededEye(context, centerX + eyeSpacing, centerY - size * 0.01, eyeRadiusX, eyeRadiusY, (staticRandom() - 0.5) * 0.28, palette, timeMs, shapePhase + Math.PI / 4);
|
|
28270
|
+
drawSeededEye(context, centerX - eyeSpacing, centerY - size * 0.01, eyeRadiusX, eyeRadiusY, (staticRandom() - 0.5) * 0.28, palette, timeMs, shapePhase, interaction);
|
|
28271
|
+
drawSeededEye(context, centerX + eyeSpacing, centerY - size * 0.01, eyeRadiusX, eyeRadiusY, (staticRandom() - 0.5) * 0.28, palette, timeMs, shapePhase + Math.PI / 4, interaction);
|
|
27488
28272
|
context.beginPath();
|
|
27489
28273
|
context.moveTo(centerX - size * 0.07, centerY + size * 0.09);
|
|
27490
|
-
context.quadraticCurveTo(centerX, centerY + size * (0.14 + Math.sin(timeMs / 620 + shapePhase) * 0.016), centerX + size * 0.07, centerY + size * 0.09);
|
|
28274
|
+
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);
|
|
27491
28275
|
context.strokeStyle = `${palette.ink}b3`;
|
|
27492
28276
|
context.lineWidth = size * 0.012;
|
|
27493
28277
|
context.lineCap = 'round';
|
|
@@ -27675,12 +28459,18 @@
|
|
|
27675
28459
|
* @param palette Derived avatar palette.
|
|
27676
28460
|
* @param timeMs Current animation time in milliseconds.
|
|
27677
28461
|
* @param phase Seed-based animation phase.
|
|
28462
|
+
* @param interaction Smoothed avatar interaction state.
|
|
27678
28463
|
*
|
|
27679
28464
|
* @private helper of `octopus3AvatarVisual`
|
|
27680
28465
|
*/
|
|
27681
|
-
function drawSeededEye(context, centerX, centerY, radiusX, radiusY, rotation, palette, timeMs, phase) {
|
|
27682
|
-
const pupilOffsetX =
|
|
27683
|
-
|
|
28466
|
+
function drawSeededEye(context, centerX, centerY, radiusX, radiusY, rotation, palette, timeMs, phase, interaction) {
|
|
28467
|
+
const { pupilOffsetX, pupilOffsetY } = resolveOrganicEyeMotion({
|
|
28468
|
+
radiusX,
|
|
28469
|
+
radiusY,
|
|
28470
|
+
timeMs,
|
|
28471
|
+
phase,
|
|
28472
|
+
interaction,
|
|
28473
|
+
});
|
|
27684
28474
|
context.save();
|
|
27685
28475
|
context.translate(centerX, centerY);
|
|
27686
28476
|
context.rotate(rotation);
|
|
@@ -27716,7 +28506,7 @@
|
|
|
27716
28506
|
context.stroke();
|
|
27717
28507
|
context.beginPath();
|
|
27718
28508
|
context.moveTo(-radiusX * 0.88, -radiusY * 0.08);
|
|
27719
|
-
context.quadraticCurveTo(0, -radiusY * 0.9, radiusX * 0.88, -radiusY * 0.08);
|
|
28509
|
+
context.quadraticCurveTo(0, -radiusY * (0.9 - interaction.gazeY * 0.16 + interaction.intensity * 0.08), radiusX * 0.88, -radiusY * 0.08);
|
|
27720
28510
|
context.strokeStyle = `${palette.shadow}73`;
|
|
27721
28511
|
context.lineWidth = radiusX * 0.16;
|
|
27722
28512
|
context.lineCap = 'round';
|
|
@@ -27854,30 +28644,56 @@
|
|
|
27854
28644
|
return avatarVisual;
|
|
27855
28645
|
}
|
|
27856
28646
|
|
|
28647
|
+
/**
|
|
28648
|
+
* Resolves the stable avatar render inputs reused across multiple frames.
|
|
28649
|
+
*
|
|
28650
|
+
* @param options Avatar identity and visual selection.
|
|
28651
|
+
* @returns Stable render data ready to be used by `renderAvatarVisual`.
|
|
28652
|
+
*
|
|
28653
|
+
* @private shared helper for canvas avatar rendering
|
|
28654
|
+
*/
|
|
28655
|
+
function resolveAvatarRenderDefinition(options) {
|
|
28656
|
+
const avatarDefinition = normalizeAvatarDefinition(options.avatarDefinition);
|
|
28657
|
+
const surface = options.surface || 'framed';
|
|
28658
|
+
return {
|
|
28659
|
+
avatarDefinition,
|
|
28660
|
+
avatarVisual: getAvatarVisualById(options.visualId),
|
|
28661
|
+
surface,
|
|
28662
|
+
palette: createAvatarPalette(avatarDefinition, surface),
|
|
28663
|
+
createRandom: createAvatarRandomFactory(avatarDefinition),
|
|
28664
|
+
};
|
|
28665
|
+
}
|
|
27857
28666
|
/**
|
|
27858
28667
|
* Renders one deterministic avatar frame into the provided canvas.
|
|
27859
28668
|
*
|
|
27860
28669
|
* @param options Rendering options.
|
|
28670
|
+
* @param resolvedAvatarRenderDefinition Optional stable render data reused between frames.
|
|
27861
28671
|
*
|
|
27862
28672
|
* @private shared helper for canvas avatar rendering
|
|
27863
28673
|
*/
|
|
27864
|
-
function renderAvatarVisual(options) {
|
|
27865
|
-
const
|
|
27866
|
-
|
|
28674
|
+
function renderAvatarVisual(options, resolvedAvatarRenderDefinition) {
|
|
28675
|
+
const resolvedRenderDefinition = resolvedAvatarRenderDefinition ||
|
|
28676
|
+
resolveAvatarRenderDefinition({
|
|
28677
|
+
avatarDefinition: options.avatarDefinition,
|
|
28678
|
+
visualId: options.visualId,
|
|
28679
|
+
surface: options.surface,
|
|
28680
|
+
});
|
|
27867
28681
|
const context = options.canvas.getContext('2d');
|
|
27868
28682
|
if (!context) {
|
|
27869
28683
|
throw new Error('2D canvas rendering context is unavailable.');
|
|
27870
28684
|
}
|
|
27871
28685
|
prepareAvatarCanvas(options.canvas, context, options.size, options.devicePixelRatio || 1);
|
|
27872
|
-
avatarVisual.render({
|
|
28686
|
+
resolvedRenderDefinition.avatarVisual.render({
|
|
27873
28687
|
canvas: options.canvas,
|
|
27874
28688
|
context,
|
|
27875
28689
|
size: options.size,
|
|
27876
28690
|
devicePixelRatio: options.devicePixelRatio || 1,
|
|
27877
28691
|
timeMs: options.timeMs,
|
|
27878
|
-
avatarDefinition:
|
|
27879
|
-
palette:
|
|
27880
|
-
createRandom:
|
|
28692
|
+
avatarDefinition: resolvedRenderDefinition.avatarDefinition,
|
|
28693
|
+
palette: resolvedRenderDefinition.palette,
|
|
28694
|
+
createRandom: resolvedRenderDefinition.createRandom,
|
|
28695
|
+
surface: resolvedRenderDefinition.surface,
|
|
28696
|
+
interaction: options.interaction || createIdleAvatarInteractionState(),
|
|
27881
28697
|
});
|
|
27882
28698
|
}
|
|
27883
28699
|
|
|
@@ -27887,48 +28703,136 @@
|
|
|
27887
28703
|
* @private helper of `<Avatar/>`
|
|
27888
28704
|
*/
|
|
27889
28705
|
const AVATAR_CANVAS_RADIUS_RATIO = 0.18;
|
|
28706
|
+
/**
|
|
28707
|
+
* Maximum time between layout-bound refreshes while pointer tracking is active.
|
|
28708
|
+
*
|
|
28709
|
+
* This keeps pointer-aware visuals aligned with chat/layout shifts without forcing a layout read every frame.
|
|
28710
|
+
*
|
|
28711
|
+
* @private helper of `<Avatar/>`
|
|
28712
|
+
*/
|
|
28713
|
+
const ACTIVE_POINTER_BOUNDS_REFRESH_MS = 120;
|
|
27890
28714
|
/**
|
|
27891
28715
|
* Canvas-based deterministic avatar component.
|
|
27892
28716
|
*
|
|
27893
28717
|
* @private shared component for in-repository avatar previews
|
|
27894
28718
|
*/
|
|
27895
28719
|
function Avatar(props) {
|
|
27896
|
-
const { avatarDefinition, visualId, size = DEFAULT_AVATAR_SIZE, title, className, style } = props;
|
|
28720
|
+
const { avatarDefinition, visualId, surface = 'framed', size = DEFAULT_AVATAR_SIZE, title, className, style } = props;
|
|
27897
28721
|
const canvasRef = react.useRef(null);
|
|
27898
|
-
const
|
|
27899
|
-
const
|
|
28722
|
+
const animationStartRef = react.useRef(null);
|
|
28723
|
+
const interactionRuntimeStateRef = react.useRef(createAvatarInteractionRuntimeState());
|
|
28724
|
+
const avatarBoundsRef = react.useRef(null);
|
|
28725
|
+
const lastResolvedPointerVersionRef = react.useRef(-1);
|
|
28726
|
+
const lastResolvedViewportLayoutVersionRef = react.useRef(-1);
|
|
28727
|
+
const lastAvatarBoundsRefreshAtRef = react.useRef(0);
|
|
28728
|
+
const avatarColorsKey = avatarDefinition.colors.join('|');
|
|
28729
|
+
const resolvedAvatarRenderDefinition = react.useMemo(() => resolveAvatarRenderDefinition({
|
|
28730
|
+
avatarDefinition,
|
|
28731
|
+
visualId,
|
|
28732
|
+
surface,
|
|
28733
|
+
}), [avatarDefinition.agentHash, avatarDefinition.agentName, avatarColorsKey, surface, visualId]);
|
|
28734
|
+
const avatarDefinitionKey = react.useMemo(() => createAvatarDefinitionKey(resolvedAvatarRenderDefinition.avatarDefinition), [
|
|
28735
|
+
resolvedAvatarRenderDefinition.avatarDefinition.agentHash,
|
|
28736
|
+
resolvedAvatarRenderDefinition.avatarDefinition.agentName,
|
|
28737
|
+
resolvedAvatarRenderDefinition.avatarDefinition.colors.join('|'),
|
|
28738
|
+
]);
|
|
28739
|
+
const [isVisible, setIsVisible] = react.useState(true);
|
|
27900
28740
|
react.useEffect(() => {
|
|
27901
28741
|
const canvas = canvasRef.current;
|
|
27902
28742
|
if (!canvas) {
|
|
27903
28743
|
throw new Error('Avatar canvas is not mounted.');
|
|
27904
28744
|
}
|
|
27905
|
-
|
|
27906
|
-
|
|
28745
|
+
return observeAvatarVisibility(canvas, setIsVisible);
|
|
28746
|
+
}, []);
|
|
28747
|
+
react.useEffect(() => {
|
|
28748
|
+
interactionRuntimeStateRef.current = createAvatarInteractionRuntimeState();
|
|
28749
|
+
avatarBoundsRef.current = null;
|
|
28750
|
+
lastResolvedPointerVersionRef.current = -1;
|
|
28751
|
+
lastResolvedViewportLayoutVersionRef.current = -1;
|
|
28752
|
+
lastAvatarBoundsRefreshAtRef.current = 0;
|
|
28753
|
+
}, [avatarDefinitionKey, isVisible, visualId]);
|
|
28754
|
+
react.useEffect(() => {
|
|
28755
|
+
const canvas = canvasRef.current;
|
|
28756
|
+
if (!canvas || typeof ResizeObserver === 'undefined') {
|
|
28757
|
+
return;
|
|
28758
|
+
}
|
|
28759
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
28760
|
+
avatarBoundsRef.current = null;
|
|
28761
|
+
});
|
|
28762
|
+
resizeObserver.observe(canvas);
|
|
28763
|
+
return () => {
|
|
28764
|
+
resizeObserver.disconnect();
|
|
28765
|
+
};
|
|
28766
|
+
}, []);
|
|
28767
|
+
react.useEffect(() => {
|
|
28768
|
+
const canvas = canvasRef.current;
|
|
28769
|
+
if (!canvas) {
|
|
28770
|
+
throw new Error('Avatar canvas is not mounted.');
|
|
28771
|
+
}
|
|
28772
|
+
const avatarVisual = resolvedAvatarRenderDefinition.avatarVisual;
|
|
28773
|
+
const isDynamicAvatar = avatarVisual.isAnimated || avatarVisual.supportsPointerTracking === true;
|
|
28774
|
+
const shouldTrackPointer = avatarVisual.supportsPointerTracking === true && isVisible;
|
|
28775
|
+
const releasePointerTracking = shouldTrackPointer ? retainAvatarPointerTracking() : null;
|
|
28776
|
+
if (animationStartRef.current === null) {
|
|
28777
|
+
animationStartRef.current = performance.now();
|
|
28778
|
+
}
|
|
27907
28779
|
const renderFrame = (now) => {
|
|
28780
|
+
const pointerSnapshot = avatarVisual.supportsPointerTracking ? getAvatarPointerSnapshot() : null;
|
|
28781
|
+
let interactionState = createIdleAvatarInteractionState();
|
|
28782
|
+
if (avatarVisual.supportsPointerTracking && pointerSnapshot) {
|
|
28783
|
+
const pointerSnapshotVersion = getAvatarPointerSnapshotVersion();
|
|
28784
|
+
const viewportLayoutVersion = getAvatarViewportLayoutVersion();
|
|
28785
|
+
if (avatarBoundsRef.current === null ||
|
|
28786
|
+
lastResolvedPointerVersionRef.current !== pointerSnapshotVersion ||
|
|
28787
|
+
lastResolvedViewportLayoutVersionRef.current !== viewportLayoutVersion ||
|
|
28788
|
+
now - lastAvatarBoundsRefreshAtRef.current >= ACTIVE_POINTER_BOUNDS_REFRESH_MS) {
|
|
28789
|
+
avatarBoundsRef.current = canvas.getBoundingClientRect();
|
|
28790
|
+
lastResolvedPointerVersionRef.current = pointerSnapshotVersion;
|
|
28791
|
+
lastResolvedViewportLayoutVersionRef.current = viewportLayoutVersion;
|
|
28792
|
+
lastAvatarBoundsRefreshAtRef.current = now;
|
|
28793
|
+
}
|
|
28794
|
+
interactionRuntimeStateRef.current = stepAvatarInteractionRuntimeState(interactionRuntimeStateRef.current, resolveAvatarPointerTarget(avatarBoundsRef.current, pointerSnapshot), now);
|
|
28795
|
+
interactionState = interactionRuntimeStateRef.current;
|
|
28796
|
+
}
|
|
28797
|
+
else if (avatarVisual.supportsPointerTracking) {
|
|
28798
|
+
interactionRuntimeStateRef.current = stepAvatarInteractionRuntimeState(interactionRuntimeStateRef.current, createIdleAvatarInteractionState(), now);
|
|
28799
|
+
interactionState = interactionRuntimeStateRef.current;
|
|
28800
|
+
}
|
|
27908
28801
|
renderAvatarVisual({
|
|
27909
28802
|
canvas,
|
|
27910
|
-
avatarDefinition:
|
|
28803
|
+
avatarDefinition: resolvedAvatarRenderDefinition.avatarDefinition,
|
|
27911
28804
|
visualId,
|
|
28805
|
+
surface,
|
|
27912
28806
|
size,
|
|
27913
|
-
timeMs: now -
|
|
28807
|
+
timeMs: now - animationStartRef.current,
|
|
27914
28808
|
devicePixelRatio: window.devicePixelRatio || 1,
|
|
27915
|
-
|
|
27916
|
-
|
|
27917
|
-
animationFrameId = window.requestAnimationFrame(renderFrame);
|
|
27918
|
-
}
|
|
28809
|
+
interaction: interactionState,
|
|
28810
|
+
}, resolvedAvatarRenderDefinition);
|
|
27919
28811
|
};
|
|
27920
|
-
renderFrame(
|
|
28812
|
+
renderFrame(performance.now());
|
|
28813
|
+
if (!isDynamicAvatar || !isVisible) {
|
|
28814
|
+
return () => {
|
|
28815
|
+
releasePointerTracking === null || releasePointerTracking === void 0 ? void 0 : releasePointerTracking();
|
|
28816
|
+
};
|
|
28817
|
+
}
|
|
28818
|
+
const releaseAnimationListener = retainAvatarAnimationListener(renderFrame);
|
|
27921
28819
|
return () => {
|
|
27922
|
-
|
|
27923
|
-
|
|
27924
|
-
}
|
|
28820
|
+
releaseAnimationListener();
|
|
28821
|
+
releasePointerTracking === null || releasePointerTracking === void 0 ? void 0 : releasePointerTracking();
|
|
27925
28822
|
};
|
|
27926
|
-
}, [
|
|
27927
|
-
|
|
28823
|
+
}, [
|
|
28824
|
+
avatarDefinitionKey,
|
|
28825
|
+
isVisible,
|
|
28826
|
+
resolvedAvatarRenderDefinition,
|
|
28827
|
+
size,
|
|
28828
|
+
surface,
|
|
28829
|
+
visualId,
|
|
28830
|
+
]);
|
|
28831
|
+
return (jsxRuntime.jsx("canvas", { ref: canvasRef, title: title || `${resolvedAvatarRenderDefinition.avatarDefinition.agentName} avatar`, className: className, style: {
|
|
27928
28832
|
width: size,
|
|
27929
28833
|
height: size,
|
|
27930
28834
|
display: 'block',
|
|
27931
|
-
borderRadius: size * AVATAR_CANVAS_RADIUS_RATIO,
|
|
28835
|
+
borderRadius: surface === 'transparent' ? 0 : size * AVATAR_CANVAS_RADIUS_RATIO,
|
|
27932
28836
|
...style,
|
|
27933
28837
|
} }));
|
|
27934
28838
|
}
|
|
@@ -27939,9 +28843,9 @@
|
|
|
27939
28843
|
* @private shared component for avatar media rendering
|
|
27940
28844
|
*/
|
|
27941
28845
|
function AvatarOrImage(props) {
|
|
27942
|
-
const { imageUrl, avatarDefinition, visualId, size, alt, className, style } = props;
|
|
28846
|
+
const { imageUrl, avatarDefinition, visualId, surface, size, alt, className, style } = props;
|
|
27943
28847
|
if (avatarDefinition && visualId) {
|
|
27944
|
-
return (jsxRuntime.jsx(Avatar, { avatarDefinition: avatarDefinition, visualId: visualId, size: size, title: alt, className: className, style: style }));
|
|
28848
|
+
return (jsxRuntime.jsx(Avatar, { avatarDefinition: avatarDefinition, visualId: visualId, surface: surface, size: size, title: alt, className: className, style: style }));
|
|
27945
28849
|
}
|
|
27946
28850
|
if (!imageUrl) {
|
|
27947
28851
|
return null;
|
|
@@ -38549,6 +39453,14 @@
|
|
|
38549
39453
|
* Constant for default agent kit model name.
|
|
38550
39454
|
*/
|
|
38551
39455
|
const DEFAULT_AGENT_KIT_MODEL_NAME = 'gpt-5.4-mini';
|
|
39456
|
+
/**
|
|
39457
|
+
* Default model used for nested DeepSearch tool invocations.
|
|
39458
|
+
*/
|
|
39459
|
+
const DEFAULT_DEEP_SEARCH_MODEL_NAME = 'o4-mini-deep-research';
|
|
39460
|
+
/**
|
|
39461
|
+
* Tool name used by the Book commitment-backed DeepSearch capability.
|
|
39462
|
+
*/
|
|
39463
|
+
const DEEP_SEARCH_TOOL_NAME = 'deep_search';
|
|
38552
39464
|
/**
|
|
38553
39465
|
* Creates one structured log entry for streamed tool-call updates.
|
|
38554
39466
|
*
|
|
@@ -38591,6 +39503,98 @@
|
|
|
38591
39503
|
}
|
|
38592
39504
|
return 'COMPLETE';
|
|
38593
39505
|
}
|
|
39506
|
+
/**
|
|
39507
|
+
* Returns true when one tool definition represents the dedicated DeepSearch capability.
|
|
39508
|
+
*
|
|
39509
|
+
* @param toolDefinition - Tool definition from compiled model requirements.
|
|
39510
|
+
* @returns `true` when the tool should be backed by a nested deep-research agent.
|
|
39511
|
+
*
|
|
39512
|
+
* @private helper of `OpenAiAgentKitExecutionTools`
|
|
39513
|
+
*/
|
|
39514
|
+
function isDeepSearchToolDefinition(toolDefinition) {
|
|
39515
|
+
return toolDefinition.name === DEEP_SEARCH_TOOL_NAME;
|
|
39516
|
+
}
|
|
39517
|
+
/**
|
|
39518
|
+
* Normalizes Promptbook JSON-schema tool parameters for AgentKit function tools.
|
|
39519
|
+
*
|
|
39520
|
+
* @param parameters - Promptbook tool parameters.
|
|
39521
|
+
* @returns AgentKit-compatible JSON schema or `undefined`.
|
|
39522
|
+
*
|
|
39523
|
+
* @private helper of `OpenAiAgentKitExecutionTools`
|
|
39524
|
+
*/
|
|
39525
|
+
function normalizeAgentKitToolParameters(parameters) {
|
|
39526
|
+
var _a, _b;
|
|
39527
|
+
if (!parameters) {
|
|
39528
|
+
return undefined;
|
|
39529
|
+
}
|
|
39530
|
+
return {
|
|
39531
|
+
...parameters,
|
|
39532
|
+
additionalProperties: (_a = parameters.additionalProperties) !== null && _a !== void 0 ? _a : false,
|
|
39533
|
+
required: (_b = parameters.required) !== null && _b !== void 0 ? _b : [],
|
|
39534
|
+
};
|
|
39535
|
+
}
|
|
39536
|
+
/**
|
|
39537
|
+
* Creates instructions for the nested DeepSearch specialist agent.
|
|
39538
|
+
*
|
|
39539
|
+
* @param toolDescription - Model-facing description from the original tool definition.
|
|
39540
|
+
* @returns System instructions for the nested deep-research agent.
|
|
39541
|
+
*
|
|
39542
|
+
* @private helper of `OpenAiAgentKitExecutionTools`
|
|
39543
|
+
*/
|
|
39544
|
+
function createDeepSearchAgentInstructions(toolDescription) {
|
|
39545
|
+
const normalizedDescription = toolDescription.trim();
|
|
39546
|
+
return spacetrim.spaceTrim((block) => `
|
|
39547
|
+
You are a DeepSearch specialist working as a tool for another agent.
|
|
39548
|
+
Perform thorough, source-grounded public-web research based on the provided request.
|
|
39549
|
+
Use web search to gather current information, compare relevant viewpoints, and synthesize a concise research brief.
|
|
39550
|
+
Do not ask follow-up questions. If the request is not specific enough, state the assumptions you had to make.
|
|
39551
|
+
Include citations in the research brief whenever sources were used.
|
|
39552
|
+
${block(normalizedDescription ? `Tool guidance:\n${normalizedDescription}` : '')}
|
|
39553
|
+
`);
|
|
39554
|
+
}
|
|
39555
|
+
/**
|
|
39556
|
+
* Builds the nested DeepSearch prompt from structured tool arguments.
|
|
39557
|
+
*
|
|
39558
|
+
* @param rawInput - Parsed function-tool arguments provided by the outer agent.
|
|
39559
|
+
* @returns Prompt text passed to the nested deep-research agent.
|
|
39560
|
+
*
|
|
39561
|
+
* @private helper of `OpenAiAgentKitExecutionTools`
|
|
39562
|
+
*/
|
|
39563
|
+
function buildDeepSearchToolInput(rawInput) {
|
|
39564
|
+
const input = rawInput && typeof rawInput === 'object' ? rawInput : {};
|
|
39565
|
+
const query = typeof input.query === 'string' ? input.query.trim() : '';
|
|
39566
|
+
const additionalHints = Object.entries(input)
|
|
39567
|
+
.filter(([key, value]) => key !== 'query' && value !== undefined && value !== null && String(value).trim() !== '')
|
|
39568
|
+
.map(([key, value]) => `- ${key}: ${typeof value === 'string' ? value : JSON.stringify(value)}`);
|
|
39569
|
+
return spacetrim.spaceTrim((block) => `
|
|
39570
|
+
Research request:
|
|
39571
|
+
${query || JSON.stringify(input)}
|
|
39572
|
+
${block(additionalHints.length > 0 ? `Execution hints:\n${additionalHints.join('\n')}` : '')}
|
|
39573
|
+
`);
|
|
39574
|
+
}
|
|
39575
|
+
/**
|
|
39576
|
+
* Creates the native Agent SDK tool used for `USE DEEPSEARCH`.
|
|
39577
|
+
*
|
|
39578
|
+
* @param toolDefinition - Promptbook tool definition for `deep_search`.
|
|
39579
|
+
* @returns AgentKit tool backed by a nested deep-research agent.
|
|
39580
|
+
*
|
|
39581
|
+
* @private helper of `OpenAiAgentKitExecutionTools`
|
|
39582
|
+
*/
|
|
39583
|
+
function createDeepSearchAgentKitTool(toolDefinition) {
|
|
39584
|
+
const deepSearchAgent = new agents.Agent({
|
|
39585
|
+
name: 'DeepSearch',
|
|
39586
|
+
model: DEFAULT_DEEP_SEARCH_MODEL_NAME,
|
|
39587
|
+
instructions: createDeepSearchAgentInstructions(toolDefinition.description),
|
|
39588
|
+
tools: [agents.webSearchTool({ searchContextSize: 'high' })],
|
|
39589
|
+
});
|
|
39590
|
+
return deepSearchAgent.asTool({
|
|
39591
|
+
toolName: toolDefinition.name,
|
|
39592
|
+
toolDescription: toolDefinition.description,
|
|
39593
|
+
parameters: normalizeAgentKitToolParameters(toolDefinition.parameters),
|
|
39594
|
+
inputBuilder: ({ params }) => buildDeepSearchToolInput(params),
|
|
39595
|
+
customOutputExtractor: (result) => { var _a; return typeof result.finalOutput === 'string' ? result.finalOutput : JSON.stringify((_a = result.finalOutput) !== null && _a !== void 0 ? _a : ''); },
|
|
39596
|
+
});
|
|
39597
|
+
}
|
|
38594
39598
|
/**
|
|
38595
39599
|
* Constant for default JSON schema name.
|
|
38596
39600
|
*/
|
|
@@ -38931,25 +39935,23 @@
|
|
|
38931
39935
|
* Builds the tool list for AgentKit, including hosted file search when applicable.
|
|
38932
39936
|
*/
|
|
38933
39937
|
buildAgentKitTools(options) {
|
|
38934
|
-
var _a;
|
|
38935
39938
|
const { tools, vectorStoreId } = options;
|
|
38936
39939
|
const agentKitTools = [];
|
|
38937
39940
|
if (vectorStoreId) {
|
|
38938
39941
|
agentKitTools.push(agents.fileSearchTool(vectorStoreId));
|
|
38939
39942
|
}
|
|
38940
39943
|
if (tools && tools.length > 0) {
|
|
38941
|
-
|
|
39944
|
+
let scriptTools = null;
|
|
38942
39945
|
for (const toolDefinition of tools) {
|
|
39946
|
+
if (isDeepSearchToolDefinition(toolDefinition)) {
|
|
39947
|
+
agentKitTools.push(createDeepSearchAgentKitTool(toolDefinition));
|
|
39948
|
+
continue;
|
|
39949
|
+
}
|
|
39950
|
+
scriptTools !== null && scriptTools !== void 0 ? scriptTools : (scriptTools = this.resolveScriptTools());
|
|
38943
39951
|
agentKitTools.push(agents.tool({
|
|
38944
39952
|
name: toolDefinition.name,
|
|
38945
39953
|
description: toolDefinition.description,
|
|
38946
|
-
parameters: toolDefinition.parameters
|
|
38947
|
-
? {
|
|
38948
|
-
...toolDefinition.parameters,
|
|
38949
|
-
additionalProperties: false,
|
|
38950
|
-
required: (_a = toolDefinition.parameters.required) !== null && _a !== void 0 ? _a : [],
|
|
38951
|
-
}
|
|
38952
|
-
: undefined,
|
|
39954
|
+
parameters: normalizeAgentKitToolParameters(toolDefinition.parameters),
|
|
38953
39955
|
strict: false,
|
|
38954
39956
|
execute: async (input, runContext, details) => {
|
|
38955
39957
|
var _a, _b, _c, _d;
|
|
@@ -45136,6 +46138,7 @@
|
|
|
45136
46138
|
delete_wallet_record: { title: 'Deleting wallet', emoji: '👛' },
|
|
45137
46139
|
request_wallet_record: { title: 'Requesting wallet', emoji: '👛' },
|
|
45138
46140
|
web_search: { title: 'Searching the web', emoji: '🔎' },
|
|
46141
|
+
deep_search: { title: 'Deep research', emoji: '🔬' },
|
|
45139
46142
|
useSearchEngine: { title: 'Searching the web', emoji: '🔎' },
|
|
45140
46143
|
search: { title: 'Searching the web', emoji: '🔎' },
|
|
45141
46144
|
useBrowser: { title: 'Browsing the web', emoji: '🌐' },
|
|
@@ -48097,7 +49100,7 @@
|
|
|
48097
49100
|
* @private function of ChatToolCallModal
|
|
48098
49101
|
*/
|
|
48099
49102
|
function isSearchToolCallName(toolName) {
|
|
48100
|
-
return toolName === 'web_search' || toolName === 'useSearchEngine' || toolName === 'search';
|
|
49103
|
+
return toolName === 'web_search' || toolName === 'deep_search' || toolName === 'useSearchEngine' || toolName === 'search';
|
|
48101
49104
|
}
|
|
48102
49105
|
/**
|
|
48103
49106
|
* Checks whether a tool name should use the time renderer.
|
|
@@ -48833,7 +49836,12 @@
|
|
|
48833
49836
|
avatarSrc: resolvedTeammateAvatar || undefined,
|
|
48834
49837
|
},
|
|
48835
49838
|
];
|
|
48836
|
-
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { className: classNames(styles$5.searchModalHeader, styles$5.teamModalHeader), children: jsxRuntime.jsxs("div", { className: styles$5.teamHeaderParticipants, children: [jsxRuntime.jsx(TeamHeaderProfile, { label: resolvedAgentLabel, avatarSrc: resolvedAgentAvatar, avatarDefinition: resolvedAgentAvatarDefinition, avatarVisualId: resolvedAgentAvatarVisualId, fallbackColor: resolvedAgentHeaderColor }), jsxRuntime.jsx("span", { className: styles$5.teamHeaderDivider, children: "talking with" }), jsxRuntime.jsx(TeamHeaderProfile, { label: resolvedTeammateLabel, avatarSrc: resolvedTeammateAvatar, fallbackColor: "#0ea5e9", href: teammateLink })] }) }), jsxRuntime.jsxs("div", { className: styles$5.searchModalContent, children: [messages.length > 0 ? (jsxRuntime.jsx("div", { className: styles$5.teamChatContainer, children: jsxRuntime.jsx(MockedChat, { title: `Chat between ${resolvedAgentLabel} and ${resolvedTeammateLabel}`, messages: messages, participants: participants, isResettable: false, isPausable: false, isSaveButtonEnabled: false, isCopyButtonEnabled: false,
|
|
49839
|
+
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { className: classNames(styles$5.searchModalHeader, styles$5.teamModalHeader), children: jsxRuntime.jsxs("div", { className: styles$5.teamHeaderParticipants, children: [jsxRuntime.jsx(TeamHeaderProfile, { label: resolvedAgentLabel, avatarSrc: resolvedAgentAvatar, avatarDefinition: resolvedAgentAvatarDefinition, avatarVisualId: resolvedAgentAvatarVisualId, fallbackColor: resolvedAgentHeaderColor }), jsxRuntime.jsx("span", { className: styles$5.teamHeaderDivider, children: "talking with" }), jsxRuntime.jsx(TeamHeaderProfile, { label: resolvedTeammateLabel, avatarSrc: resolvedTeammateAvatar, fallbackColor: "#0ea5e9", href: teammateLink })] }) }), jsxRuntime.jsxs("div", { className: styles$5.searchModalContent, children: [messages.length > 0 ? (jsxRuntime.jsx("div", { className: styles$5.teamChatContainer, children: jsxRuntime.jsx(MockedChat, { title: `Chat between ${resolvedAgentLabel} and ${resolvedTeammateLabel}`, messages: messages, participants: participants, isResettable: false, isPausable: false, isSaveButtonEnabled: false, isCopyButtonEnabled: false, layout: "STANDALONE", delayConfig: {
|
|
49840
|
+
// 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}`
|
|
49841
|
+
...FAST_FLOW,
|
|
49842
|
+
beforeFirstMessage: 0,
|
|
49843
|
+
showIntermediateMessages: messages.length,
|
|
49844
|
+
}, visualMode: "BUBBLE_MODE" }) })) : (jsxRuntime.jsx("div", { className: styles$5.noResults, children: "No teammate conversation available." })), (hasTeamToolCalls || hasTeamCitations) && (jsxRuntime.jsxs("div", { className: styles$5.teamToolCallSection, children: [hasTeamToolCalls && (jsxRuntime.jsxs("div", { className: styles$5.teamToolCallGroup, children: [jsxRuntime.jsx("div", { className: styles$5.teamToolCallHeading, children: "Actions" }), jsxRuntime.jsx("div", { className: styles$5.teamToolCallChips, children: teamToolCalls.map((toolCallEntry, index) => {
|
|
48837
49845
|
const chipletInfo = getToolCallChipletInfo(toolCallEntry.toolCall, undefined, toolTitles);
|
|
48838
49846
|
const chipletText = buildToolCallChipText(chipletInfo);
|
|
48839
49847
|
return (jsxRuntime.jsxs("button", { className: styles$5.completedToolCall, onClick: () => {
|
|
@@ -49772,7 +50780,7 @@
|
|
|
49772
50780
|
* @public exported from `@promptbook/components`
|
|
49773
50781
|
*/
|
|
49774
50782
|
function Chat(props) {
|
|
49775
|
-
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,
|
|
50783
|
+
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;
|
|
49776
50784
|
const buttonColor = react.useMemo(() => Color.from(buttonColorRaw || '#0066cc'), [buttonColorRaw]);
|
|
49777
50785
|
const agentParticipant = react.useMemo(() => participants.find((participant) => participant.name === 'AGENT'), [participants]);
|
|
49778
50786
|
const postprocessedMessages = useChatPostprocessedMessages({
|
|
@@ -49813,11 +50821,11 @@
|
|
|
49813
50821
|
extraActions,
|
|
49814
50822
|
isSaveButtonEnabled,
|
|
49815
50823
|
});
|
|
49816
|
-
const isConstrainedArticleMode = visualMode === 'ARTICLE_MODE' &&
|
|
50824
|
+
const isConstrainedArticleMode = visualMode === 'ARTICLE_MODE' && layout === 'FULL_PAGE';
|
|
49817
50825
|
useChatCompleteNotification(messages, soundSystem);
|
|
49818
50826
|
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [feedbackStatus && (jsxRuntime.jsx("div", { className: classNames(styles$5.feedbackStatus, feedbackStatus.variant === 'success'
|
|
49819
50827
|
? styles$5.feedbackStatusSuccess
|
|
49820
|
-
: styles$5.feedbackStatusError), "aria-live": "polite", role: "status", children: feedbackStatus.message })), effectConfigs && effectConfigs.length > 0 && (jsxRuntime.jsx(ChatEffectsSystem, { messages: postprocessedMessages, effectConfigs: effectConfigs, soundSystem: soundSystem })), jsxRuntime.jsx("div", { className: classNames(className, styles$5.Chat,
|
|
50828
|
+
: styles$5.feedbackStatusError), "aria-live": "polite", role: "status", children: feedbackStatus.message })), effectConfigs && effectConfigs.length > 0 && (jsxRuntime.jsx(ChatEffectsSystem, { messages: postprocessedMessages, effectConfigs: effectConfigs, soundSystem: soundSystem })), jsxRuntime.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: jsxRuntime.jsxs("div", { className: classNames(className, styles$5.chatMainFlow, getChatCssClassName('chatMainFlow'), chatCssClassNames.chatMainFlow), children: [children && jsxRuntime.jsx("div", { className: classNames(styles$5.chatChildren), children: children }), shouldShowScrollToBottom && (jsxRuntime.jsx("div", { className: styles$5.scrollToBottomContainer, children: jsxRuntime.jsxs("div", { className: styles$5.scrollToBottomWrapper, children: [jsxRuntime.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 && (jsxRuntime.jsx("span", { className: styles$5.scrollToBottomBadge, "aria-live": "polite", role: "status", children: badgeLabel }))] }) })), isVoiceCalling && (jsxRuntime.jsx("div", { className: styles$5.voiceCallIndicatorBar, children: jsxRuntime.jsxs("div", { className: styles$5.voiceCallIndicator, children: [jsxRuntime.jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "currentColor", children: jsxRuntime.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" }) }), jsxRuntime.jsx("span", { children: "Voice call active" }), jsxRuntime.jsx("div", { className: styles$5.voiceCallPulse })] }) })), jsxRuntime.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 }), jsxRuntime.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 && (jsxRuntime.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) }))] }) }), jsxRuntime.jsx(ChatToolCallModal, { isOpen: toolCallModalOpen, toolCall: selectedToolCall, toolCallIdentity: selectedToolCallIdentity, onClose: closeToolCallModal, toolTitles: toolTitles, agentParticipant: agentParticipant, buttonColor: buttonColor, teamAgentProfiles: teamAgentProfiles, chatUiTranslations: chatUiTranslations, locale: chatLocale, availableTools: selectedMessageAvailableTools }), jsxRuntime.jsx(ChatCitationModal, { isOpen: citationModalOpen, citation: selectedCitation, participants: participants, soundSystem: soundSystem, onClose: closeCitationModal }), jsxRuntime.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 })] }));
|
|
49821
50829
|
}
|
|
49822
50830
|
|
|
49823
50831
|
/**
|
|
@@ -51123,7 +52131,7 @@
|
|
|
51123
52131
|
: styles.PromptbookAgentSeamlessIntegrationStatusPending) }), jsxRuntime.jsxs("div", { className: styles.PromptbookAgentSeamlessIntegrationText, children: [jsxRuntime.jsx("div", { className: styles.PromptbookAgentSeamlessIntegrationLabel, children: "Chat" }), jsxRuntime.jsx("div", { className: styles.PromptbookAgentSeamlessIntegrationHint, children: displayName })] }), jsxRuntime.jsx("span", { className: styles.PromptbookAgentSeamlessIntegrationScreenReaderOnly, children: connectionStatusText })] }), isOpen && (jsxRuntime.jsxs("div", { className: styles.PromptbookAgentSeamlessIntegrationWindow, id: windowId, children: [jsxRuntime.jsxs("div", { className: styles.PromptbookAgentSeamlessIntegrationHeader, style: { backgroundColor: color }, ref: setHeaderElement, children: [jsxRuntime.jsxs("div", { className: styles.PromptbookAgentSeamlessIntegrationTitleWrap, children: [jsxRuntime.jsx("div", { className: styles.PromptbookAgentSeamlessIntegrationTitle, children: displayName }), jsxRuntime.jsx("div", { className: styles.PromptbookAgentSeamlessIntegrationSubtitle, children: connectionStatusText })] }), isIframeUsed && (jsxRuntime.jsx("button", { className: styles.PromptbookAgentSeamlessIntegrationClose, onClick: () => setOpen(false), title: "Close", "aria-label": "Close chat", children: jsxRuntime.jsx(CloseIcon, {}) }))] }), jsxRuntime.jsx("div", { className: styles.PromptbookAgentSeamlessIntegrationContent, children: isIframeUsed ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [!isIframeLoaded && (jsxRuntime.jsxs("div", { className: styles.PromptbookAgentSeamlessIntegrationLoading, children: [jsxRuntime.jsx("div", { className: styles.PromptbookAgentSeamlessIntegrationLoadingShimmer }), jsxRuntime.jsx("div", { className: styles.PromptbookAgentSeamlessIntegrationLoadingSpinner }), jsxRuntime.jsx("div", { className: styles.PromptbookAgentSeamlessIntegrationLoadingTitle, children: "Preparing your chat" }), jsxRuntime.jsxs("div", { className: styles.PromptbookAgentSeamlessIntegrationLoadingText, children: ["Connecting to ", displayName, "..."] })] })), jsxRuntime.jsx("iframe", { src: agentUrl + '/chat?headless', className: styles.PromptbookAgentSeamlessIntegrationIframe, style: { opacity: isIframeLoaded ? 1 : 0 }, tabIndex: -1, onLoad: () => {
|
|
51124
52132
|
setIsIframeLoaded(true);
|
|
51125
52133
|
setIsChatConnected(true);
|
|
51126
|
-
} })] })) : agent ? (jsxRuntime.jsx(AgentChat, { agent: agent, actionsContainer: headerElement, isFocusedOnLoad: isFocusedOnLoad, extraActions: jsxRuntime.jsx("button", { className: styles.PromptbookAgentSeamlessIntegrationClose, onClick: () => setOpen(false), title: "Close", "aria-label": "Close chat", children: jsxRuntime.jsx(CloseIcon, {}) }),
|
|
52134
|
+
} })] })) : agent ? (jsxRuntime.jsx(AgentChat, { agent: agent, actionsContainer: headerElement, isFocusedOnLoad: isFocusedOnLoad, extraActions: jsxRuntime.jsx("button", { className: styles.PromptbookAgentSeamlessIntegrationClose, onClick: () => setOpen(false), title: "Close", "aria-label": "Close chat", children: jsxRuntime.jsx(CloseIcon, {}) }), layout: "STANDALONE" })) : error ? (jsxRuntime.jsxs("div", { className: styles.PromptbookAgentSeamlessIntegrationError, children: [jsxRuntime.jsx("div", { className: styles.PromptbookAgentSeamlessIntegrationErrorTitle, children: "Failed to connect to the agent" }), jsxRuntime.jsx("div", { className: styles.PromptbookAgentSeamlessIntegrationErrorMessage, children: error.message })] })) : (jsxRuntime.jsxs("div", { className: styles.PromptbookAgentSeamlessIntegrationLoading, children: [jsxRuntime.jsx("div", { className: styles.PromptbookAgentSeamlessIntegrationLoadingShimmer }), jsxRuntime.jsx("div", { className: styles.PromptbookAgentSeamlessIntegrationLoadingSpinner }), jsxRuntime.jsx("div", { className: styles.PromptbookAgentSeamlessIntegrationLoadingTitle, children: "Preparing your chat" }), jsxRuntime.jsxs("div", { className: styles.PromptbookAgentSeamlessIntegrationLoadingText, children: ["Connecting to ", displayName, "..."] })] })) })] }))] }));
|
|
51127
52135
|
}
|
|
51128
52136
|
|
|
51129
52137
|
/**
|