@kibee/renderer-three 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -3,6 +3,7 @@ import { GLTF } from 'three/examples/jsm/loaders/GLTFLoader.js';
3
3
 
4
4
  type BeeVisualState = "idle" | "guiding" | "thinking" | "celebrating";
5
5
  type BeeAssetVariant = "hero" | "runtime";
6
+ type BeeAvatarPreset = "bee" | "orb" | "ring";
6
7
  type BeeQualityTier = "auto" | "high" | "balanced" | "low";
7
8
  type BeeMotionMode = "auto" | "full" | "reduced";
8
9
 
@@ -35,6 +36,9 @@ interface DockMotionOptions {
35
36
  }
36
37
  interface ThreeBeeRendererOptions {
37
38
  assetUrl?: string;
39
+ /** When set, loads GLB from memory (e.g. authenticated API proxy). Takes precedence over assetUrl. */
40
+ assetLoadFn?: () => Promise<ArrayBuffer>;
41
+ avatarPreset?: BeeAvatarPreset;
38
42
  assetVariant?: BeeAssetVariant;
39
43
  qualityTier?: BeeQualityTier;
40
44
  enableShadows?: boolean;
@@ -190,6 +194,8 @@ declare class ThreeBeeRenderer implements BeeRenderer {
190
194
  private createRenderer;
191
195
  private createLights;
192
196
  private loadBeeModel;
197
+ private createPresetAvatar;
198
+ private clearVisualRig;
193
199
  private createFallbackBee;
194
200
  private initializeDockedHome;
195
201
  private startRenderLoop;
@@ -275,4 +281,88 @@ declare class ThreeBeeRenderer implements BeeRenderer {
275
281
 
276
282
  declare function applyLegacyBeeMaterialCompat(gltf: GLTF): Promise<void>;
277
283
 
278
- export { type BeeActionChoice, type BeeActionHandler, type BeeAssetVariant, type BeeDockClickBehavior, type BeeDockPosition, type BeeDockVariant, type BeeMotionMode, type BeeQualityTier, type BeeRenderer, type BeeVisualState, type DockMotionOptions, type MoveToOptions, type PromptOptions, type SetCustomDockPositionOptions, type SpeakOptions, ThreeBeeRenderer, type ThreeBeeRendererOptions, type TooltipArrowEdge, applyLegacyBeeMaterialCompat };
284
+ type Motion = "breathe" | "pulse" | "spin" | "none";
285
+ interface DomAvatarRendererOptions {
286
+ mode: "vector" | "lottie";
287
+ svg?: string;
288
+ assetLoadFn?: () => Promise<ArrayBuffer>;
289
+ motion: Motion;
290
+ color?: string;
291
+ dockSize?: number;
292
+ dockPosition?: {
293
+ right?: string;
294
+ bottom?: string;
295
+ left?: string;
296
+ top?: string;
297
+ };
298
+ dockClickBehavior?: BeeDockClickBehavior;
299
+ }
300
+ declare class DomAvatarRenderer implements BeeRenderer {
301
+ private opts;
302
+ private root;
303
+ private avatar;
304
+ private tooltip;
305
+ private docked;
306
+ private clickBehavior;
307
+ private lottieAnim;
308
+ constructor(opts: DomAvatarRendererOptions);
309
+ mount(container?: HTMLElement): Promise<void>;
310
+ private loadLottie;
311
+ setState(state: BeeVisualState): void;
312
+ moveTo(x: number, y: number, options?: MoveToOptions): Promise<void>;
313
+ dock(_options?: DockMotionOptions): Promise<void>;
314
+ undock(_options?: DockMotionOptions): Promise<void>;
315
+ isDocked(): boolean;
316
+ speak(message: string, _options?: SpeakOptions): void;
317
+ prompt(_message: string, _options?: PromptOptions): Promise<string | null>;
318
+ hideMessage(): void;
319
+ showActions(_handler: BeeActionHandler): void;
320
+ hideActions(): void;
321
+ resize(): void;
322
+ setDockClickBehavior(mode: BeeDockClickBehavior): void;
323
+ getDockScreenPosition(): {
324
+ x: number;
325
+ y: number;
326
+ };
327
+ getBeePosition(): {
328
+ x: number;
329
+ y: number;
330
+ docked: boolean;
331
+ };
332
+ destroy(): void;
333
+ }
334
+
335
+ type AvatarMotion = "breathe" | "pulse" | "spin" | "none";
336
+ /** Mirror of the contracts WidgetAvatarConfig union (kept local to avoid a hard dep). */
337
+ type RendererAvatarConfig = {
338
+ mode: "default";
339
+ } | {
340
+ mode: "preset";
341
+ preset: "orb" | "ring";
342
+ } | {
343
+ mode: "custom";
344
+ assetUrl: string;
345
+ } | {
346
+ mode: "vector";
347
+ svg: string;
348
+ motion: AvatarMotion;
349
+ color?: string;
350
+ } | {
351
+ mode: "lottie";
352
+ assetUrl: string;
353
+ motion: AvatarMotion;
354
+ };
355
+ interface CreateRendererOptions {
356
+ three?: Partial<ThreeBeeRendererOptions>;
357
+ assetLoadFn?: () => Promise<ArrayBuffer>;
358
+ dockSize?: number;
359
+ dockPosition?: {
360
+ right?: string;
361
+ bottom?: string;
362
+ left?: string;
363
+ top?: string;
364
+ };
365
+ }
366
+ declare function createRenderer(avatar: RendererAvatarConfig, opts: CreateRendererOptions): BeeRenderer;
367
+
368
+ export { type BeeActionChoice, type BeeActionHandler, type BeeAssetVariant, type BeeAvatarPreset, type BeeDockClickBehavior, type BeeDockPosition, type BeeDockVariant, type BeeMotionMode, type BeeQualityTier, type BeeRenderer, type BeeVisualState, type CreateRendererOptions, type DockMotionOptions, DomAvatarRenderer, type DomAvatarRendererOptions, type MoveToOptions, type PromptOptions, type RendererAvatarConfig, type SetCustomDockPositionOptions, type SpeakOptions, ThreeBeeRenderer, type ThreeBeeRendererOptions, type TooltipArrowEdge, applyLegacyBeeMaterialCompat, createRenderer };
package/dist/index.d.ts CHANGED
@@ -3,6 +3,7 @@ import { GLTF } from 'three/examples/jsm/loaders/GLTFLoader.js';
3
3
 
4
4
  type BeeVisualState = "idle" | "guiding" | "thinking" | "celebrating";
5
5
  type BeeAssetVariant = "hero" | "runtime";
6
+ type BeeAvatarPreset = "bee" | "orb" | "ring";
6
7
  type BeeQualityTier = "auto" | "high" | "balanced" | "low";
7
8
  type BeeMotionMode = "auto" | "full" | "reduced";
8
9
 
@@ -35,6 +36,9 @@ interface DockMotionOptions {
35
36
  }
36
37
  interface ThreeBeeRendererOptions {
37
38
  assetUrl?: string;
39
+ /** When set, loads GLB from memory (e.g. authenticated API proxy). Takes precedence over assetUrl. */
40
+ assetLoadFn?: () => Promise<ArrayBuffer>;
41
+ avatarPreset?: BeeAvatarPreset;
38
42
  assetVariant?: BeeAssetVariant;
39
43
  qualityTier?: BeeQualityTier;
40
44
  enableShadows?: boolean;
@@ -190,6 +194,8 @@ declare class ThreeBeeRenderer implements BeeRenderer {
190
194
  private createRenderer;
191
195
  private createLights;
192
196
  private loadBeeModel;
197
+ private createPresetAvatar;
198
+ private clearVisualRig;
193
199
  private createFallbackBee;
194
200
  private initializeDockedHome;
195
201
  private startRenderLoop;
@@ -275,4 +281,88 @@ declare class ThreeBeeRenderer implements BeeRenderer {
275
281
 
276
282
  declare function applyLegacyBeeMaterialCompat(gltf: GLTF): Promise<void>;
277
283
 
278
- export { type BeeActionChoice, type BeeActionHandler, type BeeAssetVariant, type BeeDockClickBehavior, type BeeDockPosition, type BeeDockVariant, type BeeMotionMode, type BeeQualityTier, type BeeRenderer, type BeeVisualState, type DockMotionOptions, type MoveToOptions, type PromptOptions, type SetCustomDockPositionOptions, type SpeakOptions, ThreeBeeRenderer, type ThreeBeeRendererOptions, type TooltipArrowEdge, applyLegacyBeeMaterialCompat };
284
+ type Motion = "breathe" | "pulse" | "spin" | "none";
285
+ interface DomAvatarRendererOptions {
286
+ mode: "vector" | "lottie";
287
+ svg?: string;
288
+ assetLoadFn?: () => Promise<ArrayBuffer>;
289
+ motion: Motion;
290
+ color?: string;
291
+ dockSize?: number;
292
+ dockPosition?: {
293
+ right?: string;
294
+ bottom?: string;
295
+ left?: string;
296
+ top?: string;
297
+ };
298
+ dockClickBehavior?: BeeDockClickBehavior;
299
+ }
300
+ declare class DomAvatarRenderer implements BeeRenderer {
301
+ private opts;
302
+ private root;
303
+ private avatar;
304
+ private tooltip;
305
+ private docked;
306
+ private clickBehavior;
307
+ private lottieAnim;
308
+ constructor(opts: DomAvatarRendererOptions);
309
+ mount(container?: HTMLElement): Promise<void>;
310
+ private loadLottie;
311
+ setState(state: BeeVisualState): void;
312
+ moveTo(x: number, y: number, options?: MoveToOptions): Promise<void>;
313
+ dock(_options?: DockMotionOptions): Promise<void>;
314
+ undock(_options?: DockMotionOptions): Promise<void>;
315
+ isDocked(): boolean;
316
+ speak(message: string, _options?: SpeakOptions): void;
317
+ prompt(_message: string, _options?: PromptOptions): Promise<string | null>;
318
+ hideMessage(): void;
319
+ showActions(_handler: BeeActionHandler): void;
320
+ hideActions(): void;
321
+ resize(): void;
322
+ setDockClickBehavior(mode: BeeDockClickBehavior): void;
323
+ getDockScreenPosition(): {
324
+ x: number;
325
+ y: number;
326
+ };
327
+ getBeePosition(): {
328
+ x: number;
329
+ y: number;
330
+ docked: boolean;
331
+ };
332
+ destroy(): void;
333
+ }
334
+
335
+ type AvatarMotion = "breathe" | "pulse" | "spin" | "none";
336
+ /** Mirror of the contracts WidgetAvatarConfig union (kept local to avoid a hard dep). */
337
+ type RendererAvatarConfig = {
338
+ mode: "default";
339
+ } | {
340
+ mode: "preset";
341
+ preset: "orb" | "ring";
342
+ } | {
343
+ mode: "custom";
344
+ assetUrl: string;
345
+ } | {
346
+ mode: "vector";
347
+ svg: string;
348
+ motion: AvatarMotion;
349
+ color?: string;
350
+ } | {
351
+ mode: "lottie";
352
+ assetUrl: string;
353
+ motion: AvatarMotion;
354
+ };
355
+ interface CreateRendererOptions {
356
+ three?: Partial<ThreeBeeRendererOptions>;
357
+ assetLoadFn?: () => Promise<ArrayBuffer>;
358
+ dockSize?: number;
359
+ dockPosition?: {
360
+ right?: string;
361
+ bottom?: string;
362
+ left?: string;
363
+ top?: string;
364
+ };
365
+ }
366
+ declare function createRenderer(avatar: RendererAvatarConfig, opts: CreateRendererOptions): BeeRenderer;
367
+
368
+ export { type BeeActionChoice, type BeeActionHandler, type BeeAssetVariant, type BeeAvatarPreset, type BeeDockClickBehavior, type BeeDockPosition, type BeeDockVariant, type BeeMotionMode, type BeeQualityTier, type BeeRenderer, type BeeVisualState, type CreateRendererOptions, type DockMotionOptions, DomAvatarRenderer, type DomAvatarRendererOptions, type MoveToOptions, type PromptOptions, type RendererAvatarConfig, type SetCustomDockPositionOptions, type SpeakOptions, ThreeBeeRenderer, type ThreeBeeRendererOptions, type TooltipArrowEdge, applyLegacyBeeMaterialCompat, createRenderer };
package/dist/index.js CHANGED
@@ -30,8 +30,10 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ DomAvatarRenderer: () => DomAvatarRenderer,
33
34
  ThreeBeeRenderer: () => ThreeBeeRenderer,
34
- applyLegacyBeeMaterialCompat: () => applyLegacyBeeMaterialCompat
35
+ applyLegacyBeeMaterialCompat: () => applyLegacyBeeMaterialCompat,
36
+ createRenderer: () => createRenderer
35
37
  });
36
38
  module.exports = __toCommonJS(index_exports);
37
39
 
@@ -959,10 +961,15 @@ var ThreeBeeRenderer = class {
959
961
  this.scene.add(ambient, hemisphere, key, fill, rim);
960
962
  }
961
963
  async loadBeeModel() {
964
+ const preset = this.options.avatarPreset ?? "bee";
965
+ if (preset === "orb" || preset === "ring") {
966
+ this.createPresetAvatar(preset);
967
+ return true;
968
+ }
962
969
  try {
963
970
  const loader = new import_GLTFLoader.GLTFLoader();
964
971
  registerPbrSpecularGlossinessGLTFPlugin(loader);
965
- const gltf = await loader.loadAsync(
972
+ const gltf = this.options.assetLoadFn ? await loader.parseAsync(await this.options.assetLoadFn(), "") : await loader.loadAsync(
966
973
  this.options.assetUrl ?? "/models/bee-runtime.glb"
967
974
  );
968
975
  if (this.disposed) {
@@ -1019,9 +1026,69 @@ var ThreeBeeRenderer = class {
1019
1026
  this.updateBeeScreenPosition();
1020
1027
  return true;
1021
1028
  } catch {
1029
+ if (this.options.assetLoadFn || this.options.assetUrl) {
1030
+ this.createPresetAvatar("orb");
1031
+ return true;
1032
+ }
1022
1033
  return false;
1023
1034
  }
1024
1035
  }
1036
+ createPresetAvatar(preset) {
1037
+ this.runtimeWings = null;
1038
+ this.clearVisualRig();
1039
+ if (preset === "orb") {
1040
+ const orbMaterial = new THREE2.MeshPhysicalMaterial({
1041
+ color: 13228274,
1042
+ roughness: 0.28,
1043
+ metalness: 0.04,
1044
+ clearcoat: 0.45,
1045
+ emissive: 9087192,
1046
+ emissiveIntensity: 0.18
1047
+ });
1048
+ const orb = new THREE2.Mesh(new THREE2.SphereGeometry(14, 32, 32), orbMaterial);
1049
+ this.visualRig.add(orb);
1050
+ } else {
1051
+ const ringMaterial = new THREE2.MeshPhysicalMaterial({
1052
+ color: 13943976,
1053
+ transparent: true,
1054
+ opacity: 0.72,
1055
+ roughness: 0.12,
1056
+ metalness: 0.02,
1057
+ side: THREE2.DoubleSide
1058
+ });
1059
+ const ring = new THREE2.Mesh(new THREE2.TorusGeometry(16, 2.4, 16, 64), ringMaterial);
1060
+ ring.rotation.x = Math.PI / 2.3;
1061
+ const core = new THREE2.Mesh(
1062
+ new THREE2.SphereGeometry(6, 24, 24),
1063
+ new THREE2.MeshPhysicalMaterial({
1064
+ color: 15787730,
1065
+ roughness: 0.34,
1066
+ metalness: 0.05
1067
+ })
1068
+ );
1069
+ this.visualRig.add(ring, core);
1070
+ }
1071
+ this.applyHomeFacing(true);
1072
+ this.updateBeeScreenPosition();
1073
+ }
1074
+ clearVisualRig() {
1075
+ while (this.visualRig.children.length > 0) {
1076
+ const child = this.visualRig.children[0];
1077
+ this.visualRig.remove(child);
1078
+ child.traverse((node) => {
1079
+ if (node instanceof THREE2.Mesh) {
1080
+ node.geometry?.dispose();
1081
+ const materials = Array.isArray(node.material) ? node.material : [node.material];
1082
+ for (const material of materials) {
1083
+ material.dispose();
1084
+ }
1085
+ }
1086
+ });
1087
+ }
1088
+ this.mixer = null;
1089
+ this.actions.clear();
1090
+ this.activeActionName = null;
1091
+ }
1025
1092
  createFallbackBee() {
1026
1093
  this.runtimeWings = null;
1027
1094
  const bodyMaterial = new THREE2.MeshStandardMaterial({ color: 13867825 });
@@ -1906,8 +1973,188 @@ var ThreeBeeRenderer = class {
1906
1973
  });
1907
1974
  }
1908
1975
  };
1976
+
1977
+ // src/dom/DomAvatarRenderer.ts
1978
+ var STATE_CLASSES = ["kibee-2d-state--guiding", "kibee-2d-state--thinking", "kibee-2d-state--celebrating"];
1979
+ var MOTION_CLASSES = ["kibee-2d-motion--breathe", "kibee-2d-motion--pulse", "kibee-2d-motion--spin"];
1980
+ var DomAvatarRenderer = class {
1981
+ opts;
1982
+ root = null;
1983
+ avatar = null;
1984
+ tooltip = null;
1985
+ docked = true;
1986
+ clickBehavior;
1987
+ lottieAnim = null;
1988
+ constructor(opts) {
1989
+ this.opts = opts;
1990
+ this.clickBehavior = opts.dockClickBehavior ?? "undock";
1991
+ }
1992
+ async mount(container) {
1993
+ document.querySelectorAll(".kibee-root").forEach((el) => el.remove());
1994
+ const root = document.createElement("div");
1995
+ root.className = "kibee-root kibee-root--dom";
1996
+ const size = this.opts.dockSize ?? 60;
1997
+ root.style.setProperty("--kibee-dock-size", `${size}px`);
1998
+ const pos = this.opts.dockPosition ?? { right: "24px", bottom: "24px" };
1999
+ root.style.setProperty("--kibee-dock-right", pos.right ?? "auto");
2000
+ root.style.setProperty("--kibee-dock-bottom", pos.bottom ?? "auto");
2001
+ root.style.setProperty("--kibee-dock-left", pos.left ?? "auto");
2002
+ root.style.setProperty("--kibee-dock-top", pos.top ?? "auto");
2003
+ if (this.opts.color) root.style.setProperty("--kibee-2d-color", this.opts.color);
2004
+ const avatar = document.createElement("div");
2005
+ avatar.className = "kibee-2d-avatar";
2006
+ if (this.opts.mode === "vector") avatar.innerHTML = this.opts.svg ?? "";
2007
+ const tooltip = document.createElement("div");
2008
+ tooltip.className = "kibee-tooltip kibee-tooltip--hidden";
2009
+ avatar.addEventListener("click", () => {
2010
+ if (this.clickBehavior === "assist-panel") {
2011
+ window.dispatchEvent(new CustomEvent("kibee-assist-toggle"));
2012
+ } else {
2013
+ void this.undock();
2014
+ }
2015
+ });
2016
+ root.appendChild(avatar);
2017
+ root.appendChild(tooltip);
2018
+ (container ?? document.body).appendChild(root);
2019
+ this.root = root;
2020
+ this.avatar = avatar;
2021
+ this.tooltip = tooltip;
2022
+ if (this.opts.mode === "lottie" && this.opts.assetLoadFn) await this.loadLottie(avatar);
2023
+ this.setState("idle");
2024
+ }
2025
+ async loadLottie(host) {
2026
+ try {
2027
+ const [{ default: lottie }, buffer] = await Promise.all([
2028
+ import("lottie-web"),
2029
+ this.opts.assetLoadFn()
2030
+ ]);
2031
+ const json = JSON.parse(new TextDecoder().decode(buffer));
2032
+ this.lottieAnim = lottie.loadAnimation({
2033
+ container: host,
2034
+ renderer: "svg",
2035
+ loop: true,
2036
+ autoplay: true,
2037
+ animationData: json
2038
+ });
2039
+ } catch {
2040
+ host.innerHTML = "<div class='kibee-2d-fallback'></div>";
2041
+ }
2042
+ }
2043
+ setState(state) {
2044
+ if (!this.avatar) return;
2045
+ this.avatar.classList.remove(...STATE_CLASSES);
2046
+ if (this.opts.mode === "vector") {
2047
+ this.avatar.classList.remove(...MOTION_CLASSES);
2048
+ if (state === "idle") {
2049
+ if (this.opts.motion !== "none") this.avatar.classList.add(`kibee-2d-motion--${this.opts.motion}`);
2050
+ } else {
2051
+ this.avatar.classList.add(`kibee-2d-state--${state}`);
2052
+ }
2053
+ }
2054
+ }
2055
+ moveTo(x, y, options) {
2056
+ if (!this.root) return Promise.resolve();
2057
+ const dur = options?.duration ?? 550;
2058
+ this.docked = false;
2059
+ this.root.style.transition = `transform ${dur}ms cubic-bezier(.22,.61,.36,1)`;
2060
+ this.root.style.transform = `translate(${x + (options?.offsetX ?? 0)}px, ${y + (options?.offsetY ?? 0)}px)`;
2061
+ if (dur === 0) return Promise.resolve();
2062
+ return new Promise((resolve) => {
2063
+ const done = () => {
2064
+ this.root?.removeEventListener("transitionend", done);
2065
+ resolve();
2066
+ };
2067
+ this.root.addEventListener("transitionend", done);
2068
+ setTimeout(done, dur + 80);
2069
+ });
2070
+ }
2071
+ dock(_options) {
2072
+ if (this.root) this.root.style.transform = "";
2073
+ this.docked = true;
2074
+ return Promise.resolve();
2075
+ }
2076
+ undock(_options) {
2077
+ this.docked = false;
2078
+ return Promise.resolve();
2079
+ }
2080
+ isDocked() {
2081
+ return this.docked;
2082
+ }
2083
+ speak(message, _options) {
2084
+ if (!this.tooltip) return;
2085
+ this.tooltip.textContent = message;
2086
+ this.tooltip.classList.remove("kibee-tooltip--hidden");
2087
+ this.tooltip.classList.add("kibee-tooltip--visible");
2088
+ }
2089
+ prompt(_message, _options) {
2090
+ return Promise.resolve(null);
2091
+ }
2092
+ hideMessage() {
2093
+ if (!this.tooltip) return;
2094
+ this.tooltip.classList.remove("kibee-tooltip--visible");
2095
+ this.tooltip.classList.add("kibee-tooltip--hidden");
2096
+ }
2097
+ showActions(_handler) {
2098
+ }
2099
+ hideActions() {
2100
+ }
2101
+ resize() {
2102
+ }
2103
+ setDockClickBehavior(mode) {
2104
+ this.clickBehavior = mode;
2105
+ }
2106
+ getDockScreenPosition() {
2107
+ if (!this.root) return { x: 0, y: 0 };
2108
+ const r = this.root.getBoundingClientRect();
2109
+ return { x: r.left + r.width / 2, y: r.top + r.height / 2 };
2110
+ }
2111
+ getBeePosition() {
2112
+ const p = this.getDockScreenPosition();
2113
+ return { x: p.x, y: p.y, docked: this.docked };
2114
+ }
2115
+ destroy() {
2116
+ this.lottieAnim?.destroy();
2117
+ this.lottieAnim = null;
2118
+ this.root?.remove();
2119
+ this.root = null;
2120
+ this.avatar = null;
2121
+ this.tooltip = null;
2122
+ }
2123
+ };
2124
+
2125
+ // src/createRenderer.ts
2126
+ function createRenderer(avatar, opts) {
2127
+ if (avatar.mode === "vector") {
2128
+ return new DomAvatarRenderer({
2129
+ mode: "vector",
2130
+ svg: avatar.svg,
2131
+ motion: avatar.motion,
2132
+ color: avatar.color,
2133
+ dockSize: opts.dockSize,
2134
+ dockPosition: opts.dockPosition
2135
+ });
2136
+ }
2137
+ if (avatar.mode === "lottie") {
2138
+ return new DomAvatarRenderer({
2139
+ mode: "lottie",
2140
+ assetLoadFn: opts.assetLoadFn,
2141
+ motion: avatar.motion,
2142
+ dockSize: opts.dockSize,
2143
+ dockPosition: opts.dockPosition
2144
+ });
2145
+ }
2146
+ const three = { ...opts.three ?? {} };
2147
+ if (avatar.mode === "preset") three.avatarPreset = avatar.preset;
2148
+ else if (avatar.mode === "custom") {
2149
+ if (opts.assetLoadFn) three.assetLoadFn = opts.assetLoadFn;
2150
+ else three.assetUrl = avatar.assetUrl;
2151
+ }
2152
+ return new ThreeBeeRenderer(three);
2153
+ }
1909
2154
  // Annotate the CommonJS export names for ESM import in node:
1910
2155
  0 && (module.exports = {
2156
+ DomAvatarRenderer,
1911
2157
  ThreeBeeRenderer,
1912
- applyLegacyBeeMaterialCompat
2158
+ applyLegacyBeeMaterialCompat,
2159
+ createRenderer
1913
2160
  });
package/dist/index.mjs CHANGED
@@ -922,10 +922,15 @@ var ThreeBeeRenderer = class {
922
922
  this.scene.add(ambient, hemisphere, key, fill, rim);
923
923
  }
924
924
  async loadBeeModel() {
925
+ const preset = this.options.avatarPreset ?? "bee";
926
+ if (preset === "orb" || preset === "ring") {
927
+ this.createPresetAvatar(preset);
928
+ return true;
929
+ }
925
930
  try {
926
931
  const loader = new GLTFLoader();
927
932
  registerPbrSpecularGlossinessGLTFPlugin(loader);
928
- const gltf = await loader.loadAsync(
933
+ const gltf = this.options.assetLoadFn ? await loader.parseAsync(await this.options.assetLoadFn(), "") : await loader.loadAsync(
929
934
  this.options.assetUrl ?? "/models/bee-runtime.glb"
930
935
  );
931
936
  if (this.disposed) {
@@ -982,9 +987,69 @@ var ThreeBeeRenderer = class {
982
987
  this.updateBeeScreenPosition();
983
988
  return true;
984
989
  } catch {
990
+ if (this.options.assetLoadFn || this.options.assetUrl) {
991
+ this.createPresetAvatar("orb");
992
+ return true;
993
+ }
985
994
  return false;
986
995
  }
987
996
  }
997
+ createPresetAvatar(preset) {
998
+ this.runtimeWings = null;
999
+ this.clearVisualRig();
1000
+ if (preset === "orb") {
1001
+ const orbMaterial = new THREE2.MeshPhysicalMaterial({
1002
+ color: 13228274,
1003
+ roughness: 0.28,
1004
+ metalness: 0.04,
1005
+ clearcoat: 0.45,
1006
+ emissive: 9087192,
1007
+ emissiveIntensity: 0.18
1008
+ });
1009
+ const orb = new THREE2.Mesh(new THREE2.SphereGeometry(14, 32, 32), orbMaterial);
1010
+ this.visualRig.add(orb);
1011
+ } else {
1012
+ const ringMaterial = new THREE2.MeshPhysicalMaterial({
1013
+ color: 13943976,
1014
+ transparent: true,
1015
+ opacity: 0.72,
1016
+ roughness: 0.12,
1017
+ metalness: 0.02,
1018
+ side: THREE2.DoubleSide
1019
+ });
1020
+ const ring = new THREE2.Mesh(new THREE2.TorusGeometry(16, 2.4, 16, 64), ringMaterial);
1021
+ ring.rotation.x = Math.PI / 2.3;
1022
+ const core = new THREE2.Mesh(
1023
+ new THREE2.SphereGeometry(6, 24, 24),
1024
+ new THREE2.MeshPhysicalMaterial({
1025
+ color: 15787730,
1026
+ roughness: 0.34,
1027
+ metalness: 0.05
1028
+ })
1029
+ );
1030
+ this.visualRig.add(ring, core);
1031
+ }
1032
+ this.applyHomeFacing(true);
1033
+ this.updateBeeScreenPosition();
1034
+ }
1035
+ clearVisualRig() {
1036
+ while (this.visualRig.children.length > 0) {
1037
+ const child = this.visualRig.children[0];
1038
+ this.visualRig.remove(child);
1039
+ child.traverse((node) => {
1040
+ if (node instanceof THREE2.Mesh) {
1041
+ node.geometry?.dispose();
1042
+ const materials = Array.isArray(node.material) ? node.material : [node.material];
1043
+ for (const material of materials) {
1044
+ material.dispose();
1045
+ }
1046
+ }
1047
+ });
1048
+ }
1049
+ this.mixer = null;
1050
+ this.actions.clear();
1051
+ this.activeActionName = null;
1052
+ }
988
1053
  createFallbackBee() {
989
1054
  this.runtimeWings = null;
990
1055
  const bodyMaterial = new THREE2.MeshStandardMaterial({ color: 13867825 });
@@ -1869,7 +1934,187 @@ var ThreeBeeRenderer = class {
1869
1934
  });
1870
1935
  }
1871
1936
  };
1937
+
1938
+ // src/dom/DomAvatarRenderer.ts
1939
+ var STATE_CLASSES = ["kibee-2d-state--guiding", "kibee-2d-state--thinking", "kibee-2d-state--celebrating"];
1940
+ var MOTION_CLASSES = ["kibee-2d-motion--breathe", "kibee-2d-motion--pulse", "kibee-2d-motion--spin"];
1941
+ var DomAvatarRenderer = class {
1942
+ opts;
1943
+ root = null;
1944
+ avatar = null;
1945
+ tooltip = null;
1946
+ docked = true;
1947
+ clickBehavior;
1948
+ lottieAnim = null;
1949
+ constructor(opts) {
1950
+ this.opts = opts;
1951
+ this.clickBehavior = opts.dockClickBehavior ?? "undock";
1952
+ }
1953
+ async mount(container) {
1954
+ document.querySelectorAll(".kibee-root").forEach((el) => el.remove());
1955
+ const root = document.createElement("div");
1956
+ root.className = "kibee-root kibee-root--dom";
1957
+ const size = this.opts.dockSize ?? 60;
1958
+ root.style.setProperty("--kibee-dock-size", `${size}px`);
1959
+ const pos = this.opts.dockPosition ?? { right: "24px", bottom: "24px" };
1960
+ root.style.setProperty("--kibee-dock-right", pos.right ?? "auto");
1961
+ root.style.setProperty("--kibee-dock-bottom", pos.bottom ?? "auto");
1962
+ root.style.setProperty("--kibee-dock-left", pos.left ?? "auto");
1963
+ root.style.setProperty("--kibee-dock-top", pos.top ?? "auto");
1964
+ if (this.opts.color) root.style.setProperty("--kibee-2d-color", this.opts.color);
1965
+ const avatar = document.createElement("div");
1966
+ avatar.className = "kibee-2d-avatar";
1967
+ if (this.opts.mode === "vector") avatar.innerHTML = this.opts.svg ?? "";
1968
+ const tooltip = document.createElement("div");
1969
+ tooltip.className = "kibee-tooltip kibee-tooltip--hidden";
1970
+ avatar.addEventListener("click", () => {
1971
+ if (this.clickBehavior === "assist-panel") {
1972
+ window.dispatchEvent(new CustomEvent("kibee-assist-toggle"));
1973
+ } else {
1974
+ void this.undock();
1975
+ }
1976
+ });
1977
+ root.appendChild(avatar);
1978
+ root.appendChild(tooltip);
1979
+ (container ?? document.body).appendChild(root);
1980
+ this.root = root;
1981
+ this.avatar = avatar;
1982
+ this.tooltip = tooltip;
1983
+ if (this.opts.mode === "lottie" && this.opts.assetLoadFn) await this.loadLottie(avatar);
1984
+ this.setState("idle");
1985
+ }
1986
+ async loadLottie(host) {
1987
+ try {
1988
+ const [{ default: lottie }, buffer] = await Promise.all([
1989
+ import("lottie-web"),
1990
+ this.opts.assetLoadFn()
1991
+ ]);
1992
+ const json = JSON.parse(new TextDecoder().decode(buffer));
1993
+ this.lottieAnim = lottie.loadAnimation({
1994
+ container: host,
1995
+ renderer: "svg",
1996
+ loop: true,
1997
+ autoplay: true,
1998
+ animationData: json
1999
+ });
2000
+ } catch {
2001
+ host.innerHTML = "<div class='kibee-2d-fallback'></div>";
2002
+ }
2003
+ }
2004
+ setState(state) {
2005
+ if (!this.avatar) return;
2006
+ this.avatar.classList.remove(...STATE_CLASSES);
2007
+ if (this.opts.mode === "vector") {
2008
+ this.avatar.classList.remove(...MOTION_CLASSES);
2009
+ if (state === "idle") {
2010
+ if (this.opts.motion !== "none") this.avatar.classList.add(`kibee-2d-motion--${this.opts.motion}`);
2011
+ } else {
2012
+ this.avatar.classList.add(`kibee-2d-state--${state}`);
2013
+ }
2014
+ }
2015
+ }
2016
+ moveTo(x, y, options) {
2017
+ if (!this.root) return Promise.resolve();
2018
+ const dur = options?.duration ?? 550;
2019
+ this.docked = false;
2020
+ this.root.style.transition = `transform ${dur}ms cubic-bezier(.22,.61,.36,1)`;
2021
+ this.root.style.transform = `translate(${x + (options?.offsetX ?? 0)}px, ${y + (options?.offsetY ?? 0)}px)`;
2022
+ if (dur === 0) return Promise.resolve();
2023
+ return new Promise((resolve) => {
2024
+ const done = () => {
2025
+ this.root?.removeEventListener("transitionend", done);
2026
+ resolve();
2027
+ };
2028
+ this.root.addEventListener("transitionend", done);
2029
+ setTimeout(done, dur + 80);
2030
+ });
2031
+ }
2032
+ dock(_options) {
2033
+ if (this.root) this.root.style.transform = "";
2034
+ this.docked = true;
2035
+ return Promise.resolve();
2036
+ }
2037
+ undock(_options) {
2038
+ this.docked = false;
2039
+ return Promise.resolve();
2040
+ }
2041
+ isDocked() {
2042
+ return this.docked;
2043
+ }
2044
+ speak(message, _options) {
2045
+ if (!this.tooltip) return;
2046
+ this.tooltip.textContent = message;
2047
+ this.tooltip.classList.remove("kibee-tooltip--hidden");
2048
+ this.tooltip.classList.add("kibee-tooltip--visible");
2049
+ }
2050
+ prompt(_message, _options) {
2051
+ return Promise.resolve(null);
2052
+ }
2053
+ hideMessage() {
2054
+ if (!this.tooltip) return;
2055
+ this.tooltip.classList.remove("kibee-tooltip--visible");
2056
+ this.tooltip.classList.add("kibee-tooltip--hidden");
2057
+ }
2058
+ showActions(_handler) {
2059
+ }
2060
+ hideActions() {
2061
+ }
2062
+ resize() {
2063
+ }
2064
+ setDockClickBehavior(mode) {
2065
+ this.clickBehavior = mode;
2066
+ }
2067
+ getDockScreenPosition() {
2068
+ if (!this.root) return { x: 0, y: 0 };
2069
+ const r = this.root.getBoundingClientRect();
2070
+ return { x: r.left + r.width / 2, y: r.top + r.height / 2 };
2071
+ }
2072
+ getBeePosition() {
2073
+ const p = this.getDockScreenPosition();
2074
+ return { x: p.x, y: p.y, docked: this.docked };
2075
+ }
2076
+ destroy() {
2077
+ this.lottieAnim?.destroy();
2078
+ this.lottieAnim = null;
2079
+ this.root?.remove();
2080
+ this.root = null;
2081
+ this.avatar = null;
2082
+ this.tooltip = null;
2083
+ }
2084
+ };
2085
+
2086
+ // src/createRenderer.ts
2087
+ function createRenderer(avatar, opts) {
2088
+ if (avatar.mode === "vector") {
2089
+ return new DomAvatarRenderer({
2090
+ mode: "vector",
2091
+ svg: avatar.svg,
2092
+ motion: avatar.motion,
2093
+ color: avatar.color,
2094
+ dockSize: opts.dockSize,
2095
+ dockPosition: opts.dockPosition
2096
+ });
2097
+ }
2098
+ if (avatar.mode === "lottie") {
2099
+ return new DomAvatarRenderer({
2100
+ mode: "lottie",
2101
+ assetLoadFn: opts.assetLoadFn,
2102
+ motion: avatar.motion,
2103
+ dockSize: opts.dockSize,
2104
+ dockPosition: opts.dockPosition
2105
+ });
2106
+ }
2107
+ const three = { ...opts.three ?? {} };
2108
+ if (avatar.mode === "preset") three.avatarPreset = avatar.preset;
2109
+ else if (avatar.mode === "custom") {
2110
+ if (opts.assetLoadFn) three.assetLoadFn = opts.assetLoadFn;
2111
+ else three.assetUrl = avatar.assetUrl;
2112
+ }
2113
+ return new ThreeBeeRenderer(three);
2114
+ }
1872
2115
  export {
2116
+ DomAvatarRenderer,
1873
2117
  ThreeBeeRenderer,
1874
- applyLegacyBeeMaterialCompat
2118
+ applyLegacyBeeMaterialCompat,
2119
+ createRenderer
1875
2120
  };
package/dist/styles.css CHANGED
@@ -737,3 +737,27 @@ body[data-kibee-empty-dock="true"] .kibee-root[data-docked="true"] .kibee-canvas
737
737
  0 0 24px rgba(255, 208, 0, 0.18);
738
738
  }
739
739
  }
740
+ .kibee-root--dom .kibee-2d-avatar {
741
+ width: var(--kibee-dock-size, 60px);
742
+ height: var(--kibee-dock-size, 60px);
743
+ display: flex; align-items: center; justify-content: center;
744
+ color: var(--kibee-2d-color, currentColor); cursor: pointer;
745
+ }
746
+ .kibee-root--dom .kibee-2d-avatar svg { width: 100%; height: 100%; display: block; }
747
+
748
+ @media (prefers-reduced-motion: reduce) {
749
+ .kibee-2d-avatar, .kibee-2d-avatar * { animation: none !important; }
750
+ }
751
+
752
+ .kibee-2d-motion--breathe { animation: kibee2dBreathe 3.2s ease-in-out infinite; }
753
+ .kibee-2d-motion--pulse { animation: kibee2dPulse 1.8s ease-in-out infinite; }
754
+ .kibee-2d-motion--spin { animation: kibee2dSpin 6s linear infinite; }
755
+ .kibee-2d-state--guiding { animation: kibee2dGuide 0.6s ease-in-out; }
756
+ .kibee-2d-state--thinking { animation: kibee2dPulse 0.9s ease-in-out infinite; }
757
+ .kibee-2d-state--celebrating { animation: kibee2dBounce 0.7s ease-out; }
758
+
759
+ @keyframes kibee2dBreathe { 0%,100%{transform:scale(1)} 50%{transform:scale(1.06)} }
760
+ @keyframes kibee2dPulse { 0%,100%{opacity:1;transform:scale(1)} 50%{opacity:.78;transform:scale(1.04)} }
761
+ @keyframes kibee2dSpin { to { transform: rotate(360deg) } }
762
+ @keyframes kibee2dGuide { 0%{transform:rotate(0)} 30%{transform:rotate(-8deg)} 60%{transform:rotate(6deg)} 100%{transform:rotate(0)} }
763
+ @keyframes kibee2dBounce { 0%{transform:scale(1)} 30%{transform:scale(1.18) translateY(-6px)} 60%{transform:scale(.95)} 100%{transform:scale(1)} }
package/package.json CHANGED
@@ -1,10 +1,12 @@
1
1
  {
2
2
  "name": "@kibee/renderer-three",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.mjs",
6
6
  "types": "./dist/index.d.ts",
7
- "sideEffects": ["*.css"],
7
+ "sideEffects": [
8
+ "*.css"
9
+ ],
8
10
  "exports": {
9
11
  ".": {
10
12
  "types": "./dist/index.d.ts",
@@ -13,13 +15,16 @@
13
15
  },
14
16
  "./styles.css": "./dist/styles.css"
15
17
  },
16
- "files": ["dist"],
18
+ "files": [
19
+ "dist"
20
+ ],
17
21
  "scripts": {
18
- "build": "tsup src/index.ts --format cjs,esm --dts --clean --tsconfig tsconfig.build.json && cp src/styles.css dist/styles.css",
22
+ "build": "tsup src/index.ts --format cjs,esm --dts --clean --tsconfig tsconfig.build.json && cat src/styles.css src/dom/domAvatarStyles.css > dist/styles.css",
19
23
  "dev": "tsup src/index.ts --format cjs,esm --dts --watch"
20
24
  },
21
25
  "dependencies": {
22
26
  "gsap": "^3.12.5",
27
+ "lottie-web": "^5.12.2",
23
28
  "three": "^0.168.0"
24
29
  },
25
30
  "peerDependencies": {
@@ -27,5 +32,8 @@
27
32
  },
28
33
  "devDependencies": {
29
34
  "tsup": "^8.5.1"
35
+ },
36
+ "publishConfig": {
37
+ "access": "public"
30
38
  }
31
39
  }