@elevasis/ui 2.52.1 → 2.53.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.
@@ -3,7 +3,7 @@ import { ChatInterface } from '../../chunk-E6NSKXYN.js';
3
3
  import { WS_MAX_RETRIES_BEFORE_ERROR, WS_RECONNECT_BASE_DELAY, WS_RECONNECT_MAX_DELAY, validateTokenLimit, TOKEN_LIMITS } from '../../chunk-FVOMKZ7S.js';
4
4
  import '../../chunk-TVTSASST.js';
5
5
  import '../../chunk-I2KLQ2HA.js';
6
- import { Center, Loader, Paper, Stack, Group, Title, Alert, PasswordInput, Button, Text, Box, Badge, Divider } from '@mantine/core';
6
+ import { Center, Loader, Paper, Stack, Group, Title, Alert, PasswordInput, Button, Text, List, Box, Badge, Divider } from '@mantine/core';
7
7
  import { IconLock, IconAlertCircle, IconSend, IconMessageCircle } from '@tabler/icons-react';
8
8
  import { useState, useRef, useCallback, useEffect, useMemo } from 'react';
9
9
  import { useQuery, useQueryClient } from '@tanstack/react-query';
@@ -200,7 +200,14 @@ function usePublicAgentChatWebSocket(sessionId, apiUrl, capabilityToken) {
200
200
  async function fetchJson(url, init) {
201
201
  const response = await fetch(url, init);
202
202
  if (!response.ok) {
203
- throw new Error(`Request failed with status ${response.status}`);
203
+ let message = `Request failed with status ${response.status}`;
204
+ try {
205
+ const body = await response.json();
206
+ const apiMessage = typeof body.error === "string" && body.error.trim().length > 0 ? body.error : typeof body.message === "string" && body.message.trim().length > 0 ? body.message : null;
207
+ if (apiMessage) message = apiMessage;
208
+ } catch {
209
+ }
210
+ throw new Error(message);
204
211
  }
205
212
  return await response.json();
206
213
  }
@@ -221,6 +228,12 @@ function resolveAgentSubtitle(grant) {
221
228
  const branding = brandingRecord(grant?.branding);
222
229
  return brandingText(branding.subtitle) ?? brandingText(branding.description);
223
230
  }
231
+ function hasIntroContent(grant) {
232
+ const branding = brandingRecord(grant?.branding);
233
+ const hasIntroText = brandingText(branding.intro) !== void 0;
234
+ const hasInstructions = Array.isArray(branding.instructions) && branding.instructions.length > 0;
235
+ return hasIntroText || hasInstructions;
236
+ }
224
237
  function statusStyles(tone) {
225
238
  if (tone === "ready") {
226
239
  return {
@@ -383,6 +396,7 @@ function PublicAgentChat({
383
396
  const [accessCode, setAccessCode] = useState("");
384
397
  const [input, setInput] = useState("");
385
398
  const autoStartedRef = useRef(false);
399
+ const greetingTimeRef = useRef(/* @__PURE__ */ new Date());
386
400
  const displayTitle = resolveAgentTitle(title, slug, grant);
387
401
  const displaySubtitle = resolveAgentSubtitle(grant);
388
402
  const { data: historyMessages = [] } = usePublicAgentChatMessages(apiUrl, session?.sessionId ?? null, capabilityToken);
@@ -403,7 +417,13 @@ function PublicAgentChat({
403
417
  fetchJson(`${apiUrl}/api/public/agent-chat/${encodeURIComponent(slug)}`).then((payload) => {
404
418
  if (cancelled) return;
405
419
  setGrant(payload.grant);
406
- setStatus(payload.grant.requiresCode ? "code-required" : "authorizing");
420
+ if (payload.grant.requiresCode) {
421
+ setStatus("code-required");
422
+ } else if (hasIntroContent(payload.grant)) {
423
+ setStatus("intro");
424
+ } else {
425
+ setStatus("authorizing");
426
+ }
407
427
  }).catch((requestError) => {
408
428
  if (cancelled) return;
409
429
  setError(requestError instanceof Error ? requestError.message : "Unable to load agent chat");
@@ -454,16 +474,27 @@ function PublicAgentChat({
454
474
  [apiUrl, grant, metadata, onSessionReady, slug, visitorId]
455
475
  );
456
476
  useEffect(() => {
457
- if (!grant || grant.requiresCode || autoStartedRef.current) {
477
+ if (!grant || grant.requiresCode || hasIntroContent(grant) || autoStartedRef.current) {
458
478
  return;
459
479
  }
460
480
  autoStartedRef.current = true;
461
481
  void startSession();
462
482
  }, [grant, startSession]);
463
- const allMessages = useMemo(
464
- () => mergeSessionMessages(historyMessages, liveMessages),
465
- [historyMessages, liveMessages]
466
- );
483
+ const allMessages = useMemo(() => {
484
+ const merged = mergeSessionMessages(historyMessages, liveMessages);
485
+ const greeting = brandingText(brandingRecord(grant?.branding).greeting);
486
+ if (!greeting) return merged;
487
+ const greetingMessage = {
488
+ id: "greeting-bubble",
489
+ role: "assistant",
490
+ messageType: "assistant_message",
491
+ text: greeting,
492
+ turnNumber: 0,
493
+ messageIndex: -1,
494
+ createdAt: greetingTimeRef.current
495
+ };
496
+ return [greetingMessage, ...merged];
497
+ }, [historyMessages, liveMessages, grant]);
467
498
  const handleSend = () => {
468
499
  const trimmed = input.trim();
469
500
  if (!trimmed || state.isProcessing) return;
@@ -536,6 +567,50 @@ function PublicAgentChat({
536
567
  }
537
568
  );
538
569
  }
570
+ if (status === "intro" && grant) {
571
+ const branding = brandingRecord(grant.branding);
572
+ const introText = brandingText(branding.intro);
573
+ const instructions = Array.isArray(branding.instructions) ? branding.instructions.filter((item) => typeof item === "string") : [];
574
+ const ctaLabel = brandingText(branding.ctaLabel) ?? "Start the conversation";
575
+ return /* @__PURE__ */ jsx(
576
+ PublicAgentChatFrame,
577
+ {
578
+ className,
579
+ style,
580
+ title: displayTitle,
581
+ subtitle: displaySubtitle,
582
+ statusTone: "ready",
583
+ children: /* @__PURE__ */ jsx(Center, { style: { height: "100%", padding: 16 }, children: /* @__PURE__ */ jsx(
584
+ Paper,
585
+ {
586
+ withBorder: true,
587
+ p: "xl",
588
+ radius: 8,
589
+ style: {
590
+ width: "min(100%, 480px)",
591
+ background: "var(--color-surface)",
592
+ borderColor: "var(--color-border)",
593
+ boxShadow: "var(--card-shadow)"
594
+ },
595
+ children: /* @__PURE__ */ jsxs(Stack, { gap: "lg", children: [
596
+ introText && /* @__PURE__ */ jsx(Text, { size: "sm", style: { color: "var(--color-text)", lineHeight: 1.6 }, children: introText }),
597
+ instructions.length > 0 && /* @__PURE__ */ jsx(List, { size: "sm", style: { color: "var(--color-text-dimmed)" }, children: instructions.map((item, index) => /* @__PURE__ */ jsx(List.Item, { children: item }, index)) }),
598
+ /* @__PURE__ */ jsx(
599
+ Button,
600
+ {
601
+ onClick: () => {
602
+ void startSession();
603
+ },
604
+ leftSection: /* @__PURE__ */ jsx(IconSend, { size: 16 }),
605
+ children: ctaLabel
606
+ }
607
+ )
608
+ ] })
609
+ }
610
+ ) })
611
+ }
612
+ );
613
+ }
539
614
  if (status === "error") {
540
615
  return /* @__PURE__ */ jsx(
541
616
  PublicAgentChatFrame,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elevasis/ui",
3
- "version": "2.52.1",
3
+ "version": "2.53.0",
4
4
  "description": "UI components and platform-aware hooks for building custom frontends on the Elevasis platform",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -274,11 +274,11 @@
274
274
  "typescript": "5.9.2",
275
275
  "vite": "^7.0.0",
276
276
  "vitest": "^3.2.4",
277
- "@elevasis/sdk": "1.36.2",
278
- "@repo/core": "0.49.1",
277
+ "@elevasis/sdk": "1.36.3",
279
278
  "@repo/elevasis-core": "1.0.0",
280
- "@repo/eslint-config": "0.0.0",
281
- "@repo/typescript-config": "0.0.0"
279
+ "@repo/typescript-config": "0.0.0",
280
+ "@repo/core": "0.50.0",
281
+ "@repo/eslint-config": "0.0.0"
282
282
  },
283
283
  "dependencies": {
284
284
  "@dagrejs/dagre": "^1.1.4",