@tutti-os/agent-gui 0.0.84 → 0.0.85

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.
@@ -9073,6 +9073,8 @@ html[data-theme="light"] [data-message-center-item-id].agent-gui-edge-glow {
9073
9073
  display: block;
9074
9074
  width: 100%;
9075
9075
  height: 100%;
9076
+ opacity: 0;
9077
+ transition: opacity 120ms ease-out;
9076
9078
 
9077
9079
  /* Fade-out mask: a horizontal band dissolves tiles toward the stage sides
9078
9080
  (the fully-transparent stops sit where the outermost tiles ride, not at
@@ -9090,6 +9092,11 @@ html[data-theme="light"] [data-message-center-item-id].agent-gui-edge-glow {
9090
9092
  mask-composite: intersect;
9091
9093
  }
9092
9094
 
9095
+ .agent-gui-node__empty-hero-carousel[data-icons-ready="true"]
9096
+ > .agent-gui-node__empty-hero-carousel-canvas {
9097
+ opacity: 1;
9098
+ }
9099
+
9093
9100
  .agent-gui-node__empty-hero-carousel-item {
9094
9101
  position: absolute;
9095
9102
  overflow: hidden;
package/dist/index.js CHANGED
@@ -17107,6 +17107,7 @@ var AgentGuiHeroCarouselScene = class _AgentGuiHeroCarouselScene {
17107
17107
  wheelRadius;
17108
17108
  onSettle;
17109
17109
  images = [];
17110
+ ownedImages = /* @__PURE__ */ new Set();
17110
17111
  scroll = 0;
17111
17112
  target = 0;
17112
17113
  velocity = 0;
@@ -17144,22 +17145,26 @@ var AgentGuiHeroCarouselScene = class _AgentGuiHeroCarouselScene {
17144
17145
  this.tiles.push({ mesh });
17145
17146
  }
17146
17147
  options.iconUrls.forEach((iconUrl, agentIndex) => {
17147
- const image = new Image();
17148
+ const loadedImage = options.loadedImages?.[agentIndex] ?? null;
17149
+ const image = loadedImage ?? new Image();
17150
+ if (!loadedImage) {
17151
+ image.decoding = "async";
17152
+ image.loading = "eager";
17153
+ image.setAttribute("fetchpriority", "high");
17154
+ this.ownedImages.add(image);
17155
+ }
17148
17156
  this.images.push(image);
17149
17157
  image.onload = () => {
17150
17158
  if (this.disposed) {
17151
17159
  return;
17152
17160
  }
17153
- const texture = roundedIconTexture(image, () => this.requestRender());
17154
- for (const tile of this.tiles) {
17155
- if (tile.mesh.userData.agentIndex === agentIndex) {
17156
- tile.mesh.material.map = texture;
17157
- tile.mesh.material.visible = true;
17158
- tile.mesh.material.needsUpdate = true;
17159
- }
17160
- }
17161
+ this.applyImageTexture(image, agentIndex);
17161
17162
  };
17162
- image.src = iconUrl;
17163
+ if (image.complete && image.naturalWidth > 0) {
17164
+ this.applyImageTexture(image, agentIndex);
17165
+ } else if (!loadedImage) {
17166
+ image.src = iconUrl;
17167
+ }
17163
17168
  });
17164
17169
  this.applyPoses();
17165
17170
  }
@@ -17245,8 +17250,11 @@ var AgentGuiHeroCarouselScene = class _AgentGuiHeroCarouselScene {
17245
17250
  }
17246
17251
  for (const image of this.images) {
17247
17252
  image.onload = null;
17248
- image.src = "";
17253
+ if (this.ownedImages.has(image)) {
17254
+ image.src = "";
17255
+ }
17249
17256
  }
17257
+ this.ownedImages.clear();
17250
17258
  for (const tile of this.tiles) {
17251
17259
  tile.mesh.geometry.dispose();
17252
17260
  tile.mesh.material.map?.dispose();
@@ -17308,6 +17316,19 @@ var AgentGuiHeroCarouselScene = class _AgentGuiHeroCarouselScene {
17308
17316
  }
17309
17317
  });
17310
17318
  }
17319
+ applyImageTexture(image, agentIndex) {
17320
+ if (this.disposed) {
17321
+ return;
17322
+ }
17323
+ const texture = roundedIconTexture(image, () => this.requestRender());
17324
+ for (const tile of this.tiles) {
17325
+ if (tile.mesh.userData.agentIndex === agentIndex) {
17326
+ tile.mesh.material.map = texture;
17327
+ tile.mesh.material.visible = true;
17328
+ tile.mesh.material.needsUpdate = true;
17329
+ }
17330
+ }
17331
+ }
17311
17332
  applyPoses() {
17312
17333
  const step = Math.PI * 2 / Math.max(this.tileCount, 1);
17313
17334
  this.tiles.forEach((tile, index) => {
@@ -17335,6 +17356,71 @@ function agentGUILaunchpadProviderTarget(providerTargets, provider) {
17335
17356
  var CAROUSEL_WHEEL_STEP_THRESHOLD = 42;
17336
17357
  var CAROUSEL_WHEEL_STEP_COOLDOWN_MS = 110;
17337
17358
  var CAROUSEL_DRAG_STEP_PX = 52;
17359
+ function emptyPreloadedCarouselImages(length) {
17360
+ return Array.from({ length }).map(() => null);
17361
+ }
17362
+ function useAgentGUIHeroCarouselImages(icons, iconKey) {
17363
+ const [preloadState, setPreloadState] = useState7({
17364
+ images: [],
17365
+ ready: icons.length === 0
17366
+ });
17367
+ useEffect6(() => {
17368
+ if (icons.length === 0) {
17369
+ setPreloadState({ images: [], ready: true });
17370
+ return;
17371
+ }
17372
+ if (typeof Image !== "function") {
17373
+ setPreloadState({
17374
+ images: emptyPreloadedCarouselImages(icons.length),
17375
+ ready: true
17376
+ });
17377
+ return;
17378
+ }
17379
+ let cancelled = false;
17380
+ setPreloadState({
17381
+ images: emptyPreloadedCarouselImages(icons.length),
17382
+ ready: false
17383
+ });
17384
+ void Promise.all(
17385
+ icons.map(
17386
+ (icon) => new Promise((resolve) => {
17387
+ const image = new Image();
17388
+ const resolveDecoded = () => {
17389
+ const decode = image.decode?.();
17390
+ if (decode) {
17391
+ void decode.then(() => resolve(image)).catch(() => resolve(image));
17392
+ return;
17393
+ }
17394
+ resolve(image);
17395
+ };
17396
+ image.decoding = "async";
17397
+ image.loading = "eager";
17398
+ image.setAttribute("fetchpriority", "high");
17399
+ image.onload = () => {
17400
+ resolveDecoded();
17401
+ };
17402
+ image.onerror = () => resolve(null);
17403
+ image.src = icon.iconUrl;
17404
+ if (image.complete) {
17405
+ if (image.naturalWidth > 0) {
17406
+ resolveDecoded();
17407
+ } else {
17408
+ resolve(null);
17409
+ }
17410
+ }
17411
+ })
17412
+ )
17413
+ ).then((images) => {
17414
+ if (!cancelled) {
17415
+ setPreloadState({ images, ready: true });
17416
+ }
17417
+ });
17418
+ return () => {
17419
+ cancelled = true;
17420
+ };
17421
+ }, [iconKey]);
17422
+ return preloadState;
17423
+ }
17338
17424
  var AgentGUIHeroAgentCarousel = memo2(
17339
17425
  function AgentGUIHeroAgentCarousel2({
17340
17426
  activeProvider,
@@ -17390,15 +17476,17 @@ var AgentGUIHeroAgentCarousel = memo2(
17390
17476
  const canvasRef = useRef5(null);
17391
17477
  const sceneRef = useRef5(null);
17392
17478
  const iconKey = icons.map((icon) => `${icon.provider}:${icon.iconUrl}`).join("|");
17479
+ const carouselImages = useAgentGUIHeroCarouselImages(icons, iconKey);
17393
17480
  useEffect6(() => {
17394
17481
  const canvas = canvasRef.current;
17395
17482
  const stage = stageRef.current;
17396
- if (!canvas || !stage) {
17483
+ if (!canvas || !stage || !carouselImages.ready) {
17397
17484
  return;
17398
17485
  }
17399
17486
  const scene = AgentGuiHeroCarouselScene.create({
17400
17487
  canvas,
17401
17488
  iconUrls: icons.map((icon) => icon.iconUrl),
17489
+ loadedImages: carouselImages.images,
17402
17490
  onSettle: (index) => {
17403
17491
  centerIndexRef.current = index;
17404
17492
  setCenterIndex(index);
@@ -17424,7 +17512,7 @@ var AgentGUIHeroAgentCarousel = memo2(
17424
17512
  scene.dispose();
17425
17513
  sceneRef.current = null;
17426
17514
  };
17427
- }, [iconKey]);
17515
+ }, [carouselImages.images, carouselImages.ready, iconKey]);
17428
17516
  useEffect6(() => {
17429
17517
  if (activeIconIndex >= 0 && activeIconIndex !== centerIndexRef.current) {
17430
17518
  centerIndexRef.current = activeIconIndex;
@@ -17562,6 +17650,7 @@ var AgentGUIHeroAgentCarousel = memo2(
17562
17650
  "aria-label": interactive ? providerSelectLabel : void 0,
17563
17651
  role: interactive ? "group" : void 0,
17564
17652
  className: AgentGUINode_styles_default.emptyHeroCarousel,
17653
+ "data-icons-ready": carouselImages.ready,
17565
17654
  onKeyDown: interactive ? handleKeyDown : void 0,
17566
17655
  onPointerDown: interactive ? handlePointerDown : void 0,
17567
17656
  onPointerMove: interactive ? handlePointerMove : void 0,