@promptbook/components 0.112.0-117 → 0.112.0-118
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 +225 -45
- package/esm/index.es.js.map +1 -1
- package/esm/src/_packages/node.index.d.ts +10 -0
- package/esm/src/book-3.0/BookNodeAgentSource.d.ts +1 -1
- package/esm/src/book-3.0/CliAgent.d.ts +7 -2
- package/esm/src/book-3.0/cliAgentEnv.d.ts +33 -0
- package/esm/src/cli/cli-commands/common/promptRunnerCliOptions.d.ts +2 -18
- package/esm/src/version.d.ts +1 -1
- package/package.json +1 -1
- package/umd/index.umd.js +225 -45
- package/umd/index.umd.js.map +1 -1
- package/umd/src/_packages/node.index.d.ts +10 -0
- package/umd/src/book-3.0/BookNodeAgentSource.d.ts +1 -1
- package/umd/src/book-3.0/CliAgent.d.ts +7 -2
- package/umd/src/book-3.0/cliAgentEnv.d.ts +33 -0
- package/umd/src/cli/cli-commands/common/promptRunnerCliOptions.d.ts +2 -18
- package/umd/src/version.d.ts +1 -1
|
@@ -11,6 +11,11 @@ import type { CliAgentThinkingLevel } from '../book-3.0/CliAgent';
|
|
|
11
11
|
import type { CliAgentRunOptions } from '../book-3.0/CliAgent';
|
|
12
12
|
import type { CliAgentOptions } from '../book-3.0/CliAgent';
|
|
13
13
|
import { CliAgent } from '../book-3.0/CliAgent';
|
|
14
|
+
import { CLI_AGENT_HARNESS_NAMES } from '../book-3.0/cliAgentEnv';
|
|
15
|
+
import { CLI_AGENT_THINKING_LEVEL_VALUES } from '../book-3.0/cliAgentEnv';
|
|
16
|
+
import { PTBK_HARNESS_ENV } from '../book-3.0/cliAgentEnv';
|
|
17
|
+
import { PTBK_MODEL_ENV } from '../book-3.0/cliAgentEnv';
|
|
18
|
+
import { PTBK_THINKING_LEVEL_ENV } from '../book-3.0/cliAgentEnv';
|
|
14
19
|
import type { LiteAgentOptions } from '../book-3.0/LiteAgent';
|
|
15
20
|
import type { LiteAgentRunOptions } from '../book-3.0/LiteAgent';
|
|
16
21
|
import { LiteAgent } from '../book-3.0/LiteAgent';
|
|
@@ -39,6 +44,11 @@ export type { CliAgentThinkingLevel };
|
|
|
39
44
|
export type { CliAgentRunOptions };
|
|
40
45
|
export type { CliAgentOptions };
|
|
41
46
|
export { CliAgent };
|
|
47
|
+
export { CLI_AGENT_HARNESS_NAMES };
|
|
48
|
+
export { CLI_AGENT_THINKING_LEVEL_VALUES };
|
|
49
|
+
export { PTBK_HARNESS_ENV };
|
|
50
|
+
export { PTBK_MODEL_ENV };
|
|
51
|
+
export { PTBK_THINKING_LEVEL_ENV };
|
|
42
52
|
export type { LiteAgentOptions };
|
|
43
53
|
export type { LiteAgentRunOptions };
|
|
44
54
|
export { LiteAgent };
|
|
@@ -15,7 +15,7 @@ export type BookNodeAgentSource = Book | string_book;
|
|
|
15
15
|
*/
|
|
16
16
|
export type BookNodeAgentSourceOptions = {
|
|
17
17
|
readonly agentPath?: string;
|
|
18
|
-
readonly book?: BookNodeAgentSource;
|
|
18
|
+
readonly book?: string | BookNodeAgentSource;
|
|
19
19
|
readonly currentWorkingDirectory?: string;
|
|
20
20
|
};
|
|
21
21
|
/**
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import type { BookNodeAgentSourceOptions } from './BookNodeAgentSource';
|
|
2
|
+
import { CLI_AGENT_HARNESS_NAMES, CLI_AGENT_THINKING_LEVEL_VALUES } from './cliAgentEnv';
|
|
2
3
|
/**
|
|
3
4
|
* CLI harness names supported by `ptbk agent exec`.
|
|
4
5
|
*
|
|
5
6
|
* @public exported from `@promptbook/node`
|
|
6
7
|
*/
|
|
7
|
-
export type CliAgentHarness =
|
|
8
|
+
export type CliAgentHarness = (typeof CLI_AGENT_HARNESS_NAMES)[number];
|
|
8
9
|
/**
|
|
9
10
|
* Thinking levels supported by CLI coding harnesses.
|
|
10
11
|
*
|
|
11
12
|
* @public exported from `@promptbook/node`
|
|
12
13
|
*/
|
|
13
|
-
export type CliAgentThinkingLevel =
|
|
14
|
+
export type CliAgentThinkingLevel = (typeof CLI_AGENT_THINKING_LEVEL_VALUES)[number];
|
|
14
15
|
/**
|
|
15
16
|
* Per-run CLI options exposed by `CliAgent`.
|
|
16
17
|
*
|
|
@@ -22,6 +23,7 @@ export type CliAgentRunOptions = {
|
|
|
22
23
|
readonly allowCredits?: boolean;
|
|
23
24
|
readonly context?: string;
|
|
24
25
|
readonly harness?: CliAgentHarness;
|
|
26
|
+
readonly isVerbose?: boolean;
|
|
25
27
|
readonly model?: string;
|
|
26
28
|
readonly noUi?: boolean;
|
|
27
29
|
readonly thinkingLevel?: CliAgentThinkingLevel;
|
|
@@ -38,6 +40,9 @@ export type CliAgentOptions = BookNodeAgentSourceOptions & CliAgentRunOptions;
|
|
|
38
40
|
* It uses the same harnesses and execution path as `ptbk agent exec`, running the runner
|
|
39
41
|
* in-process instead of spawning a separate CLI process.
|
|
40
42
|
*
|
|
43
|
+
* When no `harness` is provided in the constructor or per-run options, `CliAgent` falls back
|
|
44
|
+
* to the `PTBK_HARNESS` environment variable, mirroring `ptbk agent exec` behavior.
|
|
45
|
+
*
|
|
41
46
|
* @public exported from `@promptbook/node`
|
|
42
47
|
*/
|
|
43
48
|
export declare class CliAgent {
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* All CLI harness names supported by `CliAgent` and `ptbk agent exec`.
|
|
3
|
+
*
|
|
4
|
+
* @public exported from `@promptbook/node`
|
|
5
|
+
*/
|
|
6
|
+
export declare const CLI_AGENT_HARNESS_NAMES: readonly ["openai-codex", "github-copilot", "cline", "claude-code", "opencode", "gemini"];
|
|
7
|
+
/**
|
|
8
|
+
* All supported thinking-level values for CLI coding-agent runners.
|
|
9
|
+
*
|
|
10
|
+
* @public exported from `@promptbook/node`
|
|
11
|
+
*/
|
|
12
|
+
export declare const CLI_AGENT_THINKING_LEVEL_VALUES: readonly ["low", "medium", "high", "xhigh"];
|
|
13
|
+
/**
|
|
14
|
+
* Environment variable used as the default runner identifier when `--harness` is omitted or not set in `CliAgent`.
|
|
15
|
+
*
|
|
16
|
+
* Set this to one of the harness names (`openai-codex`, `github-copilot`, `cline`, `claude-code`, `opencode`, `gemini`)
|
|
17
|
+
* so that `CliAgent` and `ptbk agent exec` can run without an explicit `harness` option.
|
|
18
|
+
*
|
|
19
|
+
* @public exported from `@promptbook/node`
|
|
20
|
+
*/
|
|
21
|
+
export declare const PTBK_HARNESS_ENV = "PTBK_HARNESS";
|
|
22
|
+
/**
|
|
23
|
+
* Environment variable used as the default runner model when `--model` is omitted or not set in `CliAgent`.
|
|
24
|
+
*
|
|
25
|
+
* @public exported from `@promptbook/node`
|
|
26
|
+
*/
|
|
27
|
+
export declare const PTBK_MODEL_ENV = "PTBK_MODEL";
|
|
28
|
+
/**
|
|
29
|
+
* Environment variable used as the default thinking level when `--thinking-level` is omitted or not set in `CliAgent`.
|
|
30
|
+
*
|
|
31
|
+
* @public exported from `@promptbook/node`
|
|
32
|
+
*/
|
|
33
|
+
export declare const PTBK_THINKING_LEVEL_ENV = "PTBK_THINKING_LEVEL";
|
|
@@ -1,29 +1,13 @@
|
|
|
1
1
|
import { Command as Program } from 'commander';
|
|
2
2
|
import type { ThinkingLevel } from '../coder/ThinkingLevel';
|
|
3
|
+
import { PTBK_HARNESS_ENV, PTBK_MODEL_ENV, PTBK_THINKING_LEVEL_ENV } from '../../../book-3.0/cliAgentEnv';
|
|
4
|
+
export { PTBK_HARNESS_ENV, PTBK_MODEL_ENV, PTBK_THINKING_LEVEL_ENV };
|
|
3
5
|
/**
|
|
4
6
|
* Runner identifiers supported by Promptbook CLI agent orchestration commands.
|
|
5
7
|
*
|
|
6
8
|
* @private internal utility of `promptbookCli`
|
|
7
9
|
*/
|
|
8
10
|
export declare const PROMPT_RUNNER_HARNESS_NAMES: readonly ["openai-codex", "github-copilot", "cline", "claude-code", "opencode", "gemini"];
|
|
9
|
-
/**
|
|
10
|
-
* Environment variable used as the default runner identifier when `--harness` is omitted.
|
|
11
|
-
*
|
|
12
|
-
* @private internal utility of `promptbookCli`
|
|
13
|
-
*/
|
|
14
|
-
export declare const PTBK_HARNESS_ENV = "PTBK_HARNESS";
|
|
15
|
-
/**
|
|
16
|
-
* Environment variable used as the default runner model when `--model` is omitted.
|
|
17
|
-
*
|
|
18
|
-
* @private internal utility of `promptbookCli`
|
|
19
|
-
*/
|
|
20
|
-
export declare const PTBK_MODEL_ENV = "PTBK_MODEL";
|
|
21
|
-
/**
|
|
22
|
-
* Environment variable used as the default runner thinking level when `--thinking-level` is omitted.
|
|
23
|
-
*
|
|
24
|
-
* @private internal utility of `promptbookCli`
|
|
25
|
-
*/
|
|
26
|
-
export declare const PTBK_THINKING_LEVEL_ENV = "PTBK_THINKING_LEVEL";
|
|
27
11
|
/**
|
|
28
12
|
* Runner identifier supported by Promptbook CLI agent orchestration commands.
|
|
29
13
|
*
|
package/esm/src/version.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ export declare const BOOK_LANGUAGE_VERSION: string_semantic_version;
|
|
|
15
15
|
export declare const PROMPTBOOK_ENGINE_VERSION: string_promptbook_version;
|
|
16
16
|
/**
|
|
17
17
|
* Represents the version string of the Promptbook engine.
|
|
18
|
-
* It follows semantic versioning (e.g., `0.112.0-
|
|
18
|
+
* It follows semantic versioning (e.g., `0.112.0-117`).
|
|
19
19
|
*
|
|
20
20
|
* @generated
|
|
21
21
|
*/
|
package/package.json
CHANGED
package/umd/index.umd.js
CHANGED
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
* @generated
|
|
35
35
|
* @see https://github.com/webgptorg/promptbook
|
|
36
36
|
*/
|
|
37
|
-
const PROMPTBOOK_ENGINE_VERSION = '0.112.0-
|
|
37
|
+
const PROMPTBOOK_ENGINE_VERSION = '0.112.0-118';
|
|
38
38
|
/**
|
|
39
39
|
* TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
|
|
40
40
|
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
@@ -2770,21 +2770,22 @@
|
|
|
2770
2770
|
* @private helper of `fractalAvatarVisual`
|
|
2771
2771
|
*/
|
|
2772
2772
|
function drawDragonCurveLayer(context, points, options) {
|
|
2773
|
-
const {
|
|
2773
|
+
const { primaryColor, secondaryColor, tertiaryColor, shadowColor, strokeWidth, timeMs, layerIndex } = options;
|
|
2774
2774
|
const firstPoint = points[0];
|
|
2775
2775
|
const lastPoint = points[points.length - 1];
|
|
2776
2776
|
const ribbonGradient = context.createLinearGradient(firstPoint.x, firstPoint.y, lastPoint.x, lastPoint.y);
|
|
2777
2777
|
ribbonGradient.addColorStop(0, `${primaryColor}f2`);
|
|
2778
2778
|
ribbonGradient.addColorStop(0.5, `${secondaryColor}e6`);
|
|
2779
2779
|
ribbonGradient.addColorStop(1, `${tertiaryColor}f2`);
|
|
2780
|
+
// Approximate the blurred shadow stroke with a wider semi-transparent stroke instead of
|
|
2781
|
+
// context.filter blur, which triggers a costly software rasterization pass every frame.
|
|
2780
2782
|
context.save();
|
|
2781
2783
|
context.beginPath();
|
|
2782
2784
|
tracePolyline(context, points);
|
|
2783
|
-
context.strokeStyle = `${shadowColor}
|
|
2784
|
-
context.lineWidth = strokeWidth *
|
|
2785
|
+
context.strokeStyle = `${shadowColor}48`;
|
|
2786
|
+
context.lineWidth = strokeWidth * 4.5;
|
|
2785
2787
|
context.lineJoin = 'round';
|
|
2786
2788
|
context.lineCap = 'round';
|
|
2787
|
-
context.filter = `blur(${size * 0.022}px)`;
|
|
2788
2789
|
context.stroke();
|
|
2789
2790
|
context.restore();
|
|
2790
2791
|
context.beginPath();
|
|
@@ -3368,11 +3369,23 @@
|
|
|
3368
3369
|
* @private helper of `minecraft2AvatarVisual`
|
|
3369
3370
|
*/
|
|
3370
3371
|
function drawMinecraftShadow(context, size, palette, interaction, timeMs) {
|
|
3372
|
+
const cx = size * 0.5 + interaction.gazeX * size * 0.03;
|
|
3373
|
+
const cy = size * 0.85 + Math.sin(timeMs / 880) * size * 0.01;
|
|
3374
|
+
const rx = size * (0.16 + interaction.intensity * 0.015);
|
|
3375
|
+
const ry = size * 0.055;
|
|
3376
|
+
// Radial gradient approximates the blurry ellipse shadow without context.filter blur.
|
|
3371
3377
|
context.save();
|
|
3372
|
-
context.
|
|
3373
|
-
context.
|
|
3378
|
+
context.translate(cx, cy);
|
|
3379
|
+
context.scale(1, ry / rx);
|
|
3380
|
+
const blurRadius = rx * 1.4;
|
|
3381
|
+
const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
|
|
3382
|
+
shadowGradient.addColorStop(0, `${palette.shadow}7a`);
|
|
3383
|
+
shadowGradient.addColorStop(0.45, `${palette.shadow}44`);
|
|
3384
|
+
shadowGradient.addColorStop(0.8, `${palette.shadow}1a`);
|
|
3385
|
+
shadowGradient.addColorStop(1, `${palette.shadow}00`);
|
|
3386
|
+
context.fillStyle = shadowGradient;
|
|
3374
3387
|
context.beginPath();
|
|
3375
|
-
context.
|
|
3388
|
+
context.arc(0, 0, blurRadius, 0, Math.PI * 2);
|
|
3376
3389
|
context.fill();
|
|
3377
3390
|
context.restore();
|
|
3378
3391
|
}
|
|
@@ -3596,13 +3609,27 @@
|
|
|
3596
3609
|
spotlight.addColorStop(1, `${palette.highlight}00`);
|
|
3597
3610
|
context.fillStyle = spotlight;
|
|
3598
3611
|
context.fillRect(0, 0, size, size);
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3612
|
+
{
|
|
3613
|
+
// Radial gradient approximates the blurry ellipse shadow without context.filter blur.
|
|
3614
|
+
const cx = size * 0.5;
|
|
3615
|
+
const cy = size * 0.86;
|
|
3616
|
+
const rx = size * 0.2;
|
|
3617
|
+
const ry = size * 0.06;
|
|
3618
|
+
const blurRadius = rx * 1.4;
|
|
3619
|
+
const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
|
|
3620
|
+
shadowGradient.addColorStop(0, 'rgba(0,0,0,0.28)');
|
|
3621
|
+
shadowGradient.addColorStop(0.45, 'rgba(0,0,0,0.14)');
|
|
3622
|
+
shadowGradient.addColorStop(0.8, 'rgba(0,0,0,0.05)');
|
|
3623
|
+
shadowGradient.addColorStop(1, 'rgba(0,0,0,0)');
|
|
3624
|
+
context.save();
|
|
3625
|
+
context.translate(cx, cy);
|
|
3626
|
+
context.scale(1, ry / rx);
|
|
3627
|
+
context.fillStyle = shadowGradient;
|
|
3628
|
+
context.beginPath();
|
|
3629
|
+
context.arc(0, 0, blurRadius, 0, Math.PI * 2);
|
|
3630
|
+
context.fill();
|
|
3631
|
+
context.restore();
|
|
3632
|
+
}
|
|
3606
3633
|
drawVoxelCuboid(context, {
|
|
3607
3634
|
x: bodyX,
|
|
3608
3635
|
y: bodyY,
|
|
@@ -4654,6 +4681,35 @@
|
|
|
4654
4681
|
y: -0.62,
|
|
4655
4682
|
z: 0.94,
|
|
4656
4683
|
});
|
|
4684
|
+
/**
|
|
4685
|
+
* Cache keyed by the `createRandom` factory reference (stable per mounted `<Avatar/>`).
|
|
4686
|
+
*
|
|
4687
|
+
* @private helper of `octopus3dAvatarVisual`
|
|
4688
|
+
*/
|
|
4689
|
+
const octopus3dStableStateCache = new WeakMap();
|
|
4690
|
+
/**
|
|
4691
|
+
* Returns the stable per-avatar state, computing it on first access and caching for subsequent frames.
|
|
4692
|
+
*
|
|
4693
|
+
* @private helper of `octopus3dAvatarVisual`
|
|
4694
|
+
*/
|
|
4695
|
+
function getOctopus3dStableState(createRandom) {
|
|
4696
|
+
const cached = octopus3dStableStateCache.get(createRandom);
|
|
4697
|
+
if (cached !== undefined) {
|
|
4698
|
+
return cached;
|
|
4699
|
+
}
|
|
4700
|
+
const animationRandom = createRandom('octopus3d-animation-profile');
|
|
4701
|
+
const eyeRandom = createRandom('octopus3d-eye-profile');
|
|
4702
|
+
const leftEyePhaseOffset = eyeRandom() * 0.6;
|
|
4703
|
+
const rightEyePhaseOffset = eyeRandom() * 0.6;
|
|
4704
|
+
const state = {
|
|
4705
|
+
morphologyProfile: createOctopus3MorphologyProfile(createRandom),
|
|
4706
|
+
animationPhase: animationRandom() * Math.PI * 2,
|
|
4707
|
+
leftEyePhaseOffset,
|
|
4708
|
+
rightEyePhaseOffset,
|
|
4709
|
+
};
|
|
4710
|
+
octopus3dStableStateCache.set(createRandom, state);
|
|
4711
|
+
return state;
|
|
4712
|
+
}
|
|
4657
4713
|
/**
|
|
4658
4714
|
* Proper 3D Octopus visual built from projected organic meshes and tentacles.
|
|
4659
4715
|
*
|
|
@@ -4666,10 +4722,7 @@
|
|
|
4666
4722
|
isAnimated: true,
|
|
4667
4723
|
supportsPointerTracking: true,
|
|
4668
4724
|
render({ context, size, palette, createRandom, timeMs, interaction }) {
|
|
4669
|
-
const morphologyProfile =
|
|
4670
|
-
const animationRandom = createRandom('octopus3d-animation-profile');
|
|
4671
|
-
const eyeRandom = createRandom('octopus3d-eye-profile');
|
|
4672
|
-
const animationPhase = animationRandom() * Math.PI * 2;
|
|
4725
|
+
const { morphologyProfile, animationPhase, leftEyePhaseOffset, rightEyePhaseOffset } = getOctopus3dStableState(createRandom);
|
|
4673
4726
|
const sceneCenterX = size * 0.5;
|
|
4674
4727
|
const sceneCenterY = size * 0.56;
|
|
4675
4728
|
const bob = Math.sin(timeMs / 920 + animationPhase) * size * 0.014;
|
|
@@ -4766,12 +4819,12 @@
|
|
|
4766
4819
|
x: -faceEyeSpacing,
|
|
4767
4820
|
y: faceEyeYOffset,
|
|
4768
4821
|
z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, -faceEyeSpacing, faceEyeYOffset),
|
|
4769
|
-
}, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase +
|
|
4822
|
+
}, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + leftEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
|
|
4770
4823
|
drawProjectedOrganicEye(context, {
|
|
4771
4824
|
x: faceEyeSpacing,
|
|
4772
4825
|
y: faceEyeYOffset,
|
|
4773
4826
|
z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, faceEyeSpacing, faceEyeYOffset),
|
|
4774
|
-
}, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.7 +
|
|
4827
|
+
}, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.7 + rightEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
|
|
4775
4828
|
drawProjectedOrganicMouth(context, [
|
|
4776
4829
|
{
|
|
4777
4830
|
x: -mouthHalfWidth,
|
|
@@ -4815,14 +4868,28 @@
|
|
|
4815
4868
|
/**
|
|
4816
4869
|
* Draws the soft ground shadow below the octopus.
|
|
4817
4870
|
*
|
|
4871
|
+
* Uses a scaled radial gradient instead of `context.filter = 'blur()'` to approximate the
|
|
4872
|
+
* blurry ellipse without triggering a costly software rasterization pass on every frame.
|
|
4873
|
+
*
|
|
4818
4874
|
* @private helper of `octopus3dAvatarVisual`
|
|
4819
4875
|
*/
|
|
4820
4876
|
function drawOctopus3dShadow(context, size, palette, interaction, timeMs) {
|
|
4877
|
+
const cx = size * 0.5 + interaction.gazeX * size * 0.04;
|
|
4878
|
+
const cy = size * 0.87 + Math.sin(timeMs / 920) * size * 0.008;
|
|
4879
|
+
const rx = size * (0.18 + interaction.intensity * 0.02);
|
|
4880
|
+
const ry = size * 0.06;
|
|
4821
4881
|
context.save();
|
|
4822
|
-
context.
|
|
4823
|
-
context.
|
|
4882
|
+
context.translate(cx, cy);
|
|
4883
|
+
context.scale(1, ry / rx);
|
|
4884
|
+
const blurRadius = rx * 1.4;
|
|
4885
|
+
const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
|
|
4886
|
+
shadowGradient.addColorStop(0, `${palette.shadow}7a`);
|
|
4887
|
+
shadowGradient.addColorStop(0.45, `${palette.shadow}44`);
|
|
4888
|
+
shadowGradient.addColorStop(0.8, `${palette.shadow}1a`);
|
|
4889
|
+
shadowGradient.addColorStop(1, `${palette.shadow}00`);
|
|
4890
|
+
context.fillStyle = shadowGradient;
|
|
4824
4891
|
context.beginPath();
|
|
4825
|
-
context.
|
|
4892
|
+
context.arc(0, 0, blurRadius, 0, Math.PI * 2);
|
|
4826
4893
|
context.fill();
|
|
4827
4894
|
context.restore();
|
|
4828
4895
|
}
|
|
@@ -5053,6 +5120,35 @@
|
|
|
5053
5120
|
y: -0.6,
|
|
5054
5121
|
z: 0.98,
|
|
5055
5122
|
});
|
|
5123
|
+
/**
|
|
5124
|
+
* Cache keyed by the `createRandom` factory reference (stable per mounted `<Avatar/>`).
|
|
5125
|
+
*
|
|
5126
|
+
* @private helper of `octopus3d2AvatarVisual`
|
|
5127
|
+
*/
|
|
5128
|
+
const octopus3d2StableStateCache = new WeakMap();
|
|
5129
|
+
/**
|
|
5130
|
+
* Returns the stable per-avatar state, computing it on first access and caching for subsequent frames.
|
|
5131
|
+
*
|
|
5132
|
+
* @private helper of `octopus3d2AvatarVisual`
|
|
5133
|
+
*/
|
|
5134
|
+
function getOctopus3d2StableState(createRandom) {
|
|
5135
|
+
const cached = octopus3d2StableStateCache.get(createRandom);
|
|
5136
|
+
if (cached !== undefined) {
|
|
5137
|
+
return cached;
|
|
5138
|
+
}
|
|
5139
|
+
const animationRandom = createRandom('octopus3d2-animation-profile');
|
|
5140
|
+
const eyeRandom = createRandom('octopus3d2-eye-profile');
|
|
5141
|
+
const leftEyePhaseOffset = eyeRandom() * 0.7;
|
|
5142
|
+
const rightEyePhaseOffset = eyeRandom() * 0.7;
|
|
5143
|
+
const state = {
|
|
5144
|
+
morphologyProfile: createOctopus3MorphologyProfile(createRandom),
|
|
5145
|
+
animationPhase: animationRandom() * Math.PI * 2,
|
|
5146
|
+
leftEyePhaseOffset,
|
|
5147
|
+
rightEyePhaseOffset,
|
|
5148
|
+
};
|
|
5149
|
+
octopus3d2StableStateCache.set(createRandom, state);
|
|
5150
|
+
return state;
|
|
5151
|
+
}
|
|
5056
5152
|
/**
|
|
5057
5153
|
* Octopus 3D 2 avatar visual.
|
|
5058
5154
|
*
|
|
@@ -5065,10 +5161,7 @@
|
|
|
5065
5161
|
isAnimated: true,
|
|
5066
5162
|
supportsPointerTracking: true,
|
|
5067
5163
|
render({ context, size, palette, createRandom, timeMs, interaction }) {
|
|
5068
|
-
const morphologyProfile =
|
|
5069
|
-
const animationRandom = createRandom('octopus3d2-animation-profile');
|
|
5070
|
-
const eyeRandom = createRandom('octopus3d2-eye-profile');
|
|
5071
|
-
const animationPhase = animationRandom() * Math.PI * 2;
|
|
5164
|
+
const { morphologyProfile, animationPhase, leftEyePhaseOffset, rightEyePhaseOffset } = getOctopus3d2StableState(createRandom);
|
|
5072
5165
|
const sceneCenterX = size * 0.5;
|
|
5073
5166
|
const sceneCenterY = size * 0.575;
|
|
5074
5167
|
const bob = Math.sin(timeMs / 940 + animationPhase) * size * 0.013;
|
|
@@ -5121,8 +5214,8 @@
|
|
|
5121
5214
|
const rightEyeLocalCenter = sampleBlobbyOctopusSurfacePoint(surfaceOptions, eyeLatitude, eyeLongitude);
|
|
5122
5215
|
const eyeRadiusX = size * morphologyProfile.face.eyeRadiusXRatio * 0.78;
|
|
5123
5216
|
const eyeRadiusY = eyeRadiusX * morphologyProfile.face.eyeHeightRatio * 0.92;
|
|
5124
|
-
drawProjectedOrganicEye(context, leftEyeLocalCenter, eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase +
|
|
5125
|
-
drawProjectedOrganicEye(context, rightEyeLocalCenter, eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.9 +
|
|
5217
|
+
drawProjectedOrganicEye(context, leftEyeLocalCenter, eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + leftEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
|
|
5218
|
+
drawProjectedOrganicEye(context, rightEyeLocalCenter, eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.9 + rightEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
|
|
5126
5219
|
drawProjectedOrganicMouth(context, [
|
|
5127
5220
|
sampleBlobbyOctopusSurfacePoint(surfaceOptions, mouthLatitude, mouthCenterLongitude - mouthHalfLongitude),
|
|
5128
5221
|
sampleBlobbyOctopusSurfacePoint(surfaceOptions, mouthCurveLatitude, mouthCenterLongitude),
|
|
@@ -5151,14 +5244,28 @@
|
|
|
5151
5244
|
/**
|
|
5152
5245
|
* Draws the soft floor shadow that anchors the single mesh in the frame.
|
|
5153
5246
|
*
|
|
5247
|
+
* Uses a scaled radial gradient instead of `context.filter = 'blur()'` to approximate the
|
|
5248
|
+
* blurry ellipse without triggering a costly software rasterization pass on every frame.
|
|
5249
|
+
*
|
|
5154
5250
|
* @private helper of `octopus3d2AvatarVisual`
|
|
5155
5251
|
*/
|
|
5156
5252
|
function drawBlobbyOctopusShadow(context, size, palette, interaction, timeMs, morphologyProfile) {
|
|
5253
|
+
const cx = size * 0.5 + interaction.gazeX * size * 0.045;
|
|
5254
|
+
const cy = size * 0.88 + Math.sin(timeMs / 940) * size * 0.008;
|
|
5255
|
+
const rx = size * (0.18 + (morphologyProfile.body.horizontalStretch - 1) * 0.04 + interaction.intensity * 0.018);
|
|
5256
|
+
const ry = size * 0.062;
|
|
5157
5257
|
context.save();
|
|
5158
|
-
context.
|
|
5159
|
-
context.
|
|
5258
|
+
context.translate(cx, cy);
|
|
5259
|
+
context.scale(1, ry / rx);
|
|
5260
|
+
const blurRadius = rx * 1.4;
|
|
5261
|
+
const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
|
|
5262
|
+
shadowGradient.addColorStop(0, `${palette.shadow}7a`);
|
|
5263
|
+
shadowGradient.addColorStop(0.45, `${palette.shadow}44`);
|
|
5264
|
+
shadowGradient.addColorStop(0.8, `${palette.shadow}1a`);
|
|
5265
|
+
shadowGradient.addColorStop(1, `${palette.shadow}00`);
|
|
5266
|
+
context.fillStyle = shadowGradient;
|
|
5160
5267
|
context.beginPath();
|
|
5161
|
-
context.
|
|
5268
|
+
context.arc(0, 0, blurRadius, 0, Math.PI * 2);
|
|
5162
5269
|
context.fill();
|
|
5163
5270
|
context.restore();
|
|
5164
5271
|
}
|
|
@@ -5314,6 +5421,40 @@
|
|
|
5314
5421
|
* @private helper of `octopus3d3AvatarVisual`
|
|
5315
5422
|
*/
|
|
5316
5423
|
const OCTOPUS_TENTACLE_COUNT = 8;
|
|
5424
|
+
/**
|
|
5425
|
+
* Cache keyed by the `createRandom` factory reference, which is stable for the lifetime of one
|
|
5426
|
+
* mounted `<Avatar/>` component (created inside `resolveAvatarRenderDefinition` and held in a
|
|
5427
|
+
* React `useMemo`). Using a `WeakMap` ensures the entry is collected when the component unmounts.
|
|
5428
|
+
*
|
|
5429
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
5430
|
+
*/
|
|
5431
|
+
const stableStateCache = new WeakMap();
|
|
5432
|
+
/**
|
|
5433
|
+
* Returns the stable per-avatar state, computing it on first access and returning the cached
|
|
5434
|
+
* result on every subsequent call within the same `<Avatar/>` mount.
|
|
5435
|
+
*
|
|
5436
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
5437
|
+
*/
|
|
5438
|
+
function getOctopus3d3StableState(createRandom) {
|
|
5439
|
+
const cached = stableStateCache.get(createRandom);
|
|
5440
|
+
if (cached !== undefined) {
|
|
5441
|
+
return cached;
|
|
5442
|
+
}
|
|
5443
|
+
const morphologyProfile = createOctopus3MorphologyProfile(createRandom);
|
|
5444
|
+
const animationRandom = createRandom('octopus3d3-animation-profile');
|
|
5445
|
+
const eyeRandom = createRandom('octopus3d3-eye-profile');
|
|
5446
|
+
const leftEyePhaseOffset = eyeRandom() * 0.7;
|
|
5447
|
+
const rightEyePhaseOffset = eyeRandom() * 0.7;
|
|
5448
|
+
const state = {
|
|
5449
|
+
morphologyProfile,
|
|
5450
|
+
animationPhase: animationRandom() * Math.PI * 2,
|
|
5451
|
+
leftEyePhaseOffset,
|
|
5452
|
+
rightEyePhaseOffset,
|
|
5453
|
+
tentacleProfiles: createContinuousTentacleProfiles(createRandom, morphologyProfile),
|
|
5454
|
+
};
|
|
5455
|
+
stableStateCache.set(createRandom, state);
|
|
5456
|
+
return state;
|
|
5457
|
+
}
|
|
5317
5458
|
/**
|
|
5318
5459
|
* Octopus 3D 3 avatar visual.
|
|
5319
5460
|
*
|
|
@@ -5326,11 +5467,7 @@
|
|
|
5326
5467
|
isAnimated: true,
|
|
5327
5468
|
supportsPointerTracking: true,
|
|
5328
5469
|
render({ context, size, palette, createRandom, timeMs, interaction }) {
|
|
5329
|
-
const morphologyProfile =
|
|
5330
|
-
const animationRandom = createRandom('octopus3d3-animation-profile');
|
|
5331
|
-
const eyeRandom = createRandom('octopus3d3-eye-profile');
|
|
5332
|
-
const animationPhase = animationRandom() * Math.PI * 2;
|
|
5333
|
-
const tentacleProfiles = createContinuousTentacleProfiles(createRandom, morphologyProfile);
|
|
5470
|
+
const { morphologyProfile, animationPhase, leftEyePhaseOffset, rightEyePhaseOffset, tentacleProfiles } = getOctopus3d3StableState(createRandom);
|
|
5334
5471
|
const sceneCenterX = size * 0.5;
|
|
5335
5472
|
const sceneCenterY = size * 0.535;
|
|
5336
5473
|
const bob = Math.sin(timeMs / 960 + animationPhase) * size * 0.012;
|
|
@@ -5407,8 +5544,8 @@
|
|
|
5407
5544
|
size,
|
|
5408
5545
|
palette,
|
|
5409
5546
|
});
|
|
5410
|
-
drawProjectedOrganicEye(context, sampleContinuousOctopusSurfacePoint(surfaceOptions, eyeLatitude, -eyeLongitude), eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase +
|
|
5411
|
-
drawProjectedOrganicEye(context, sampleContinuousOctopusSurfacePoint(surfaceOptions, eyeLatitude, eyeLongitude), eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.85 +
|
|
5547
|
+
drawProjectedOrganicEye(context, sampleContinuousOctopusSurfacePoint(surfaceOptions, eyeLatitude, -eyeLongitude), eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + leftEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
|
|
5548
|
+
drawProjectedOrganicEye(context, sampleContinuousOctopusSurfacePoint(surfaceOptions, eyeLatitude, eyeLongitude), eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.85 + rightEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
|
|
5412
5549
|
drawProjectedOrganicMouth(context, [
|
|
5413
5550
|
sampleContinuousOctopusSurfacePoint(surfaceOptions, mouthLatitude, mouthCenterLongitude - mouthHalfLongitude),
|
|
5414
5551
|
sampleContinuousOctopusSurfacePoint(surfaceOptions, mouthCurveLatitude, mouthCenterLongitude),
|
|
@@ -5459,14 +5596,30 @@
|
|
|
5459
5596
|
/**
|
|
5460
5597
|
* Draws the soft lower shadow that anchors the octopus in the avatar frame.
|
|
5461
5598
|
*
|
|
5599
|
+
* Uses a scaled radial gradient instead of `context.filter = 'blur()'` to approximate the
|
|
5600
|
+
* blurry ellipse without triggering a costly software rasterization pass on every frame.
|
|
5601
|
+
*
|
|
5462
5602
|
* @private helper of `octopus3d3AvatarVisual`
|
|
5463
5603
|
*/
|
|
5464
5604
|
function drawContinuousOctopusShadow(context, size, palette, interaction, timeMs, morphologyProfile) {
|
|
5605
|
+
const cx = size * 0.5 + interaction.gazeX * size * 0.045;
|
|
5606
|
+
const cy = size * 0.9 + Math.sin(timeMs / 980) * size * 0.007;
|
|
5607
|
+
const rx = size * (0.19 + morphologyProfile.tentacles.rootSpreadScale * 0.022 + interaction.intensity * 0.02);
|
|
5608
|
+
const ry = size * 0.06;
|
|
5609
|
+
// Scale the context so that drawing a circle produces the correct ellipse aspect ratio,
|
|
5610
|
+
// then fill with a radial gradient that approximates the blurry edge without context.filter.
|
|
5465
5611
|
context.save();
|
|
5466
|
-
context.
|
|
5467
|
-
context.
|
|
5612
|
+
context.translate(cx, cy);
|
|
5613
|
+
context.scale(1, ry / rx);
|
|
5614
|
+
const blurRadius = rx * 1.4;
|
|
5615
|
+
const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
|
|
5616
|
+
shadowGradient.addColorStop(0, `${palette.shadow}7a`);
|
|
5617
|
+
shadowGradient.addColorStop(0.45, `${palette.shadow}44`);
|
|
5618
|
+
shadowGradient.addColorStop(0.8, `${palette.shadow}1a`);
|
|
5619
|
+
shadowGradient.addColorStop(1, `${palette.shadow}00`);
|
|
5620
|
+
context.fillStyle = shadowGradient;
|
|
5468
5621
|
context.beginPath();
|
|
5469
|
-
context.
|
|
5622
|
+
context.arc(0, 0, blurRadius, 0, Math.PI * 2);
|
|
5470
5623
|
context.fill();
|
|
5471
5624
|
context.restore();
|
|
5472
5625
|
}
|
|
@@ -30733,6 +30886,22 @@
|
|
|
30733
30886
|
}
|
|
30734
30887
|
|
|
30735
30888
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
30889
|
+
/**
|
|
30890
|
+
* Target frames per second for the shared avatar animation loop.
|
|
30891
|
+
*
|
|
30892
|
+
* Animated octopus visuals change slowly enough that 24 fps is indistinguishable
|
|
30893
|
+
* from 60 fps in practice, while cutting rendering work by ~60% when multiple
|
|
30894
|
+
* avatars are on screen simultaneously.
|
|
30895
|
+
*
|
|
30896
|
+
* @private utility of the avatar rendering system
|
|
30897
|
+
*/
|
|
30898
|
+
const AVATAR_TARGET_FPS = 24;
|
|
30899
|
+
/**
|
|
30900
|
+
* Minimum elapsed time in milliseconds required between avatar render passes.
|
|
30901
|
+
*
|
|
30902
|
+
* @private utility of the avatar rendering system
|
|
30903
|
+
*/
|
|
30904
|
+
const AVATAR_TARGET_FRAME_INTERVAL_MS = 1000 / AVATAR_TARGET_FPS;
|
|
30736
30905
|
/**
|
|
30737
30906
|
* Next registration id used by the shared avatar animation scheduler.
|
|
30738
30907
|
*
|
|
@@ -30751,6 +30920,14 @@
|
|
|
30751
30920
|
* @private utility of the avatar rendering system
|
|
30752
30921
|
*/
|
|
30753
30922
|
let avatarAnimationFrameId = null;
|
|
30923
|
+
/**
|
|
30924
|
+
* Timestamp of the most recently rendered avatar frame.
|
|
30925
|
+
*
|
|
30926
|
+
* Used to throttle callbacks to `AVATAR_TARGET_FRAME_INTERVAL_MS`.
|
|
30927
|
+
*
|
|
30928
|
+
* @private utility of the avatar rendering system
|
|
30929
|
+
*/
|
|
30930
|
+
let lastAvatarFrameTime = 0;
|
|
30754
30931
|
/**
|
|
30755
30932
|
* Registers one avatar animation callback in the shared animation loop.
|
|
30756
30933
|
*
|
|
@@ -30785,8 +30962,11 @@
|
|
|
30785
30962
|
}
|
|
30786
30963
|
const runFrame = (now) => {
|
|
30787
30964
|
avatarAnimationFrameId = null;
|
|
30788
|
-
|
|
30789
|
-
|
|
30965
|
+
if (now - lastAvatarFrameTime >= AVATAR_TARGET_FRAME_INTERVAL_MS) {
|
|
30966
|
+
lastAvatarFrameTime = now;
|
|
30967
|
+
for (const avatarAnimationListener of [...avatarAnimationListeners.values()]) {
|
|
30968
|
+
avatarAnimationListener(now);
|
|
30969
|
+
}
|
|
30790
30970
|
}
|
|
30791
30971
|
ensureAvatarAnimationLoop();
|
|
30792
30972
|
};
|