@anakin824/prdg-chat-ui 0.2.1 → 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.mjs CHANGED
@@ -324,18 +324,51 @@ function ChatNatsBridge({ natsWsUrl, natsToken, onConnectedChange }) {
324
324
 
325
325
  // src/chat/provider/ChatProvider.tsx
326
326
  var defaultTheme = {
327
- primary: "#0a84ff",
328
- bubbleSent: "#0a84ff",
329
- bubbleReceived: "#2c2c2e",
327
+ primary: "#7c3aed",
328
+ bubbleSent: "#7c3aed",
329
+ bubbleReceived: "#e5e7eb",
330
330
  textOnSent: "#ffffff",
331
- textOnReceived: "#f5f5f7",
332
- radius: "18px",
331
+ textOnReceived: "#111827",
332
+ radius: "12px",
333
333
  fontFamily: "system-ui, -apple-system, sans-serif",
334
- surface: "#1c1c1e",
335
- border: "#3a3a3c",
336
- mutedFill: "rgba(0, 0, 0, 0.2)",
337
- composerShadow: "0 2px 16px rgba(0, 0, 0, 0.38)"
334
+ surface: "#ffffff",
335
+ border: "#e5e7eb",
336
+ mutedFill: "rgba(17, 24, 39, 0.06)",
337
+ composerShadow: "0 1px 2px rgba(15, 23, 42, 0.05), 0 2px 8px rgba(15, 23, 42, 0.06)"
338
338
  };
339
+ var THEME_TO_CSS_VAR = {
340
+ primary: "--chat-primary",
341
+ bubbleSent: "--chat-bubble-sent",
342
+ bubbleReceived: "--chat-bubble-received",
343
+ textOnSent: "--chat-text-on-sent",
344
+ textOnReceived: "--chat-text-on-received",
345
+ surface: "--chat-surface",
346
+ border: "--chat-border",
347
+ mutedFill: "--chat-muted-fill",
348
+ composerShadow: "--chat-composer-shadow"
349
+ };
350
+ function buildThemeColorOverrideCss(themeOverride) {
351
+ if (!themeOverride) return "";
352
+ const parts = [];
353
+ Object.keys(THEME_TO_CSS_VAR).forEach((k) => {
354
+ const v = themeOverride[k];
355
+ const cssName = THEME_TO_CSS_VAR[k];
356
+ if (cssName && typeof v === "string" && v.trim() !== "") {
357
+ parts.push(`${cssName}: ${v};`);
358
+ }
359
+ });
360
+ if (parts.length === 0) return "";
361
+ const body = parts.join(" ");
362
+ return `
363
+ html:not(.dark) [data-chat-root]:not([data-chat-appearance="dark"]),
364
+ [data-chat-root][data-chat-appearance="light"] {
365
+ ${body}
366
+ }
367
+ html.dark [data-chat-root]:not([data-chat-appearance="light"]),
368
+ [data-chat-root][data-chat-appearance="dark"] {
369
+ ${body}
370
+ }`;
371
+ }
339
372
  function ChatProvider({
340
373
  apiUrl,
341
374
  token,
@@ -350,9 +383,12 @@ function ChatProvider({
350
383
  natsWsUrl,
351
384
  natsToken,
352
385
  debug: debugProp,
386
+ userIdentityMode = "external",
387
+ appearance = "auto",
353
388
  children
354
389
  }) {
355
390
  const debug = resolveChatDebug(debugProp);
391
+ const externalIdentity = userIdentityMode === "external";
356
392
  const queryClient = useMemo(
357
393
  () => new QueryClient({
358
394
  defaultOptions: {
@@ -375,7 +411,9 @@ function ChatProvider({
375
411
  () => new ChatAPI(apiUrl.replace(/\/$/, ""), () => tokenRef.current, handleAuthError),
376
412
  [apiUrl, handleAuthError]
377
413
  );
378
- const [resolvedUserId, setResolvedUserId] = useState(userIdProp ?? null);
414
+ const [resolvedUserId, setResolvedUserId] = useState(
415
+ () => externalIdentity ? null : userIdProp ?? null
416
+ );
379
417
  const [resolvedTenantId, setResolvedTenantId] = useState(tenantIdProp ?? null);
380
418
  const [resolvedConduitlyTenantId, setResolvedConduitlyTenantId] = useState(
381
419
  conduitlyTenantIdProp
@@ -385,17 +423,23 @@ function ChatProvider({
385
423
  chatDebugLog(debug, "ChatProvider: session inputs", {
386
424
  apiUrl: apiUrl.replace(/\/$/, ""),
387
425
  hasToken: Boolean(token?.trim?.()),
426
+ userIdentityMode,
388
427
  userIdProp: userIdProp ?? null,
389
428
  tenantIdProp: tenantIdProp ?? null,
390
429
  conduitlyTenantIdProp: conduitlyTenantIdProp ?? null
391
430
  });
392
- }, [debug, apiUrl, token, userIdProp, tenantIdProp, conduitlyTenantIdProp]);
431
+ }, [debug, apiUrl, token, userIdentityMode, userIdProp, tenantIdProp, conduitlyTenantIdProp]);
393
432
  useEffect(() => {
394
433
  setMeError(null);
395
- if (userIdProp) setResolvedUserId(userIdProp);
396
- if (tenantIdProp) setResolvedTenantId(tenantIdProp);
397
434
  if (conduitlyTenantIdProp) setResolvedConduitlyTenantId(conduitlyTenantIdProp);
398
- if (userIdProp && tenantIdProp) {
435
+ if (!externalIdentity) {
436
+ if (userIdProp) setResolvedUserId(userIdProp);
437
+ if (tenantIdProp) setResolvedTenantId(tenantIdProp);
438
+ } else {
439
+ if (tenantIdProp) setResolvedTenantId(tenantIdProp);
440
+ }
441
+ const skipMe = !externalIdentity && Boolean(userIdProp && tenantIdProp);
442
+ if (skipMe) {
399
443
  chatDebugLog(debug, "identity: using userId + tenantId props (skipping GET /me)", {
400
444
  userId: userIdProp,
401
445
  tenantId: tenantIdProp
@@ -403,6 +447,8 @@ function ChatProvider({
403
447
  return;
404
448
  }
405
449
  chatDebugLog(debug, "identity: fetching GET /me \u2026", {
450
+ userIdentityMode,
451
+ externalIdentity,
406
452
  missingUserId: !userIdProp,
407
453
  missingTenantId: !tenantIdProp
408
454
  });
@@ -416,8 +462,22 @@ function ChatProvider({
416
462
  ext_user_id: me.ext_user_id ?? null,
417
463
  conduitly_tenant_id: me.conduitly_tenant_id ?? null
418
464
  });
419
- if (!userIdProp) setResolvedUserId(me.id);
420
- if (!tenantIdProp) setResolvedTenantId(me.tenant_id);
465
+ if (externalIdentity) {
466
+ setResolvedUserId(me.id);
467
+ if (!tenantIdProp) setResolvedTenantId(me.tenant_id);
468
+ const ext = me.ext_user_id?.trim();
469
+ const passed = userIdProp?.trim();
470
+ if (passed && ext && passed.toLowerCase() !== ext.toLowerCase()) {
471
+ chatDebugWarn(
472
+ debug,
473
+ "userIdentityMode=external: `userId` prop does not match GET /me ext_user_id (using JWT user from /me)",
474
+ { userIdProp: passed, me_ext_user_id: ext, internal_id: me.id }
475
+ );
476
+ }
477
+ } else {
478
+ if (!userIdProp) setResolvedUserId(me.id);
479
+ if (!tenantIdProp) setResolvedTenantId(me.tenant_id);
480
+ }
421
481
  if (!conduitlyTenantIdProp && me.conduitly_tenant_id) {
422
482
  setResolvedConduitlyTenantId(me.conduitly_tenant_id);
423
483
  }
@@ -428,26 +488,19 @@ function ChatProvider({
428
488
  return () => {
429
489
  cancelled = true;
430
490
  };
431
- }, [api, token, userIdProp, tenantIdProp, conduitlyTenantIdProp, debug]);
491
+ }, [api, token, userIdProp, tenantIdProp, conduitlyTenantIdProp, debug, externalIdentity, userIdentityMode]);
432
492
  const userId = resolvedUserId ?? "";
433
493
  const tenantId = resolvedTenantId ?? "";
434
494
  const mergedTheme = useMemo(() => ({ ...defaultTheme, ...theme }), [theme]);
435
495
  const styleTag = useMemo(() => {
436
496
  const t = mergedTheme;
437
- return `[data-chat-root] {
438
- --chat-primary: ${t.primary};
439
- --chat-bubble-sent: ${t.bubbleSent};
440
- --chat-bubble-received: ${t.bubbleReceived};
441
- --chat-text-on-sent: ${t.textOnSent};
442
- --chat-text-on-received: ${t.textOnReceived};
497
+ let css = `[data-chat-root] {
443
498
  --chat-radius: ${t.radius};
444
499
  --chat-font-family: ${t.fontFamily};
445
- --chat-surface: ${t.surface};
446
- --chat-border: ${t.border};
447
- --chat-muted-fill: ${t.mutedFill};
448
- ${t.composerShadow ? `--chat-composer-shadow: ${t.composerShadow};` : ""}
449
500
  }`;
450
- }, [mergedTheme]);
501
+ css += buildThemeColorOverrideCss(theme);
502
+ return css;
503
+ }, [mergedTheme, theme]);
451
504
  const [natsConnected, setNatsConnected] = useState(false);
452
505
  const natsUrl = natsWsUrl?.trim() ?? "";
453
506
  const wsConnected = Boolean(natsUrl) && natsConnected;
@@ -532,7 +585,15 @@ function ChatProvider({
532
585
  }
533
586
  return null;
534
587
  }
535
- return /* @__PURE__ */ React.createElement(QueryClientProvider, { client: queryClient }, /* @__PURE__ */ React.createElement(ChatContext.Provider, { value }, /* @__PURE__ */ React.createElement("div", { style: layoutStyle }, natsUrl ? /* @__PURE__ */ React.createElement(ChatNatsBridge, { natsWsUrl: natsUrl, natsToken, onConnectedChange: setNatsConnected }) : null, /* @__PURE__ */ React.createElement("style", { dangerouslySetInnerHTML: { __html: styleTag } }), /* @__PURE__ */ React.createElement("div", { "data-chat-root": true, style: { ...layoutStyle, flex: 1 } }, children))));
588
+ return /* @__PURE__ */ React.createElement(QueryClientProvider, { client: queryClient }, /* @__PURE__ */ React.createElement(ChatContext.Provider, { value }, /* @__PURE__ */ React.createElement("div", { style: layoutStyle }, natsUrl ? /* @__PURE__ */ React.createElement(ChatNatsBridge, { natsWsUrl: natsUrl, natsToken, onConnectedChange: setNatsConnected }) : null, /* @__PURE__ */ React.createElement("style", { dangerouslySetInnerHTML: { __html: styleTag } }), /* @__PURE__ */ React.createElement(
589
+ "div",
590
+ {
591
+ "data-chat-root": true,
592
+ "data-chat-appearance": appearance === "auto" ? void 0 : appearance,
593
+ style: { ...layoutStyle, flex: 1 }
594
+ },
595
+ children
596
+ ))));
536
597
  }
537
598
  function useChatActions() {
538
599
  const { api } = useChat();