@asgard-js/react 0.0.38-canary.6 → 0.0.38-canary.7

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.
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Darkens a color by a given percentage
3
+ * @param color - Hex color string (e.g., "#640000")
4
+ * @param percentage - Percentage to darken (e.g., 0.2 for 20%)
5
+ * @returns Darkened hex color string
6
+ */
7
+ export declare function darkenColor(color: string, percentage: number): string;
8
+ /**
9
+ * Lightens a color by mixing with white
10
+ * @param color - Hex color string (e.g., "#640000")
11
+ * @param percentage - Percentage to lighten (e.g., 0.8 for 20% original, 80% white)
12
+ * @returns Lightened hex color string
13
+ */
14
+ export declare function lightenColor(color: string, percentage: number): string;
15
+ //# sourceMappingURL=color-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"color-utils.d.ts","sourceRoot":"","sources":["../../src/utils/color-utils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAmBrE;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAkBtE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@asgard-js/react",
3
- "version": "0.0.38-canary.6",
3
+ "version": "0.0.38-canary.7",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -54,7 +54,7 @@
54
54
  "vitest": "^1.6.0"
55
55
  },
56
56
  "peerDependencies": {
57
- "@asgard-js/core": "^0.0.38-canary.6",
57
+ "@asgard-js/core": "^0.0.38-canary.7",
58
58
  "react": "^18.0.0",
59
59
  "react-dom": "^18.0.0"
60
60
  },
@@ -33,6 +33,10 @@
33
33
  }
34
34
  }
35
35
 
36
+ .hint_text {
37
+ color: white;
38
+ }
39
+
36
40
  .time {
37
41
  font-size: 12px;
38
42
  color: rgba(140, 140, 140, 1);
@@ -65,7 +65,12 @@ export function HintTemplate(props: HintTemplateProps): ReactNode {
65
65
  style={themeTemplate?.HintMessageTemplate?.style}
66
66
  >
67
67
  <div className={classes.time}>{formatTime(message.time)}</div>
68
- {template.text}
68
+ <div
69
+ className={classes.hint_text}
70
+ style={themeTemplate?.HintMessageTemplate?.style}
71
+ >
72
+ {template.text}
73
+ </div>
69
74
  </div>
70
75
  );
71
76
  }
@@ -11,7 +11,7 @@ interface QuickRepliesProps {
11
11
  export function QuickReplies(props: QuickRepliesProps): ReactNode {
12
12
  const { quickReplies } = props;
13
13
 
14
- const { template } = useAsgardThemeContext();
14
+ const { template, botMessage } = useAsgardThemeContext();
15
15
  const { sendMessage, isConnecting } = useAsgardContext();
16
16
 
17
17
  const onClick = useCallback(
@@ -32,7 +32,10 @@ export function QuickReplies(props: QuickRepliesProps): ReactNode {
32
32
  <button
33
33
  key={quickReply.text}
34
34
  className={styles.quick_reply}
35
- style={template?.quickReplies?.button?.style}
35
+ style={{
36
+ ...template?.quickReplies?.button?.style,
37
+ backgroundColor: botMessage?.quickReplyBackgroundColor || template?.quickReplies?.button?.style?.backgroundColor,
38
+ }}
36
39
  disabled={isConnecting}
37
40
  onClick={() => onClick(quickReply.text)}
38
41
  >
@@ -19,6 +19,7 @@ export function TextTemplate(props: TextTemplateProps): ReactNode {
19
19
  const { avatar } = useAsgardContext();
20
20
 
21
21
  const theme = useAsgardThemeContext();
22
+ const { botMessage } = theme;
22
23
 
23
24
  const { htmlBlocks, lastTypingText } = useMarkdownRenderer(
24
25
  (message as ConversationBotMessage)?.message?.text || '',
@@ -40,9 +41,12 @@ export function TextTemplate(props: TextTemplateProps): ReactNode {
40
41
  backgroundColor: theme?.botMessage?.backgroundColor,
41
42
  };
42
43
  default:
43
- return {};
44
+ return {
45
+ color: theme?.chatbot?.primaryComponent?.secondaryColor || theme?.template?.TextMessageTemplate?.style?.color,
46
+ backgroundColor: botMessage?.unsentBackgroundColor,
47
+ };
44
48
  }
45
- }, [message, theme]);
49
+ }, [message, theme, botMessage]);
46
50
 
47
51
  if (message.type === 'error') return null;
48
52
 
@@ -8,6 +8,7 @@ import {
8
8
  useCallback,
9
9
  } from 'react';
10
10
  import { deepMerge } from '../utils/deep-merge';
11
+ import { darkenColor } from '../utils/color-utils';
11
12
  import {
12
13
  useAsgardAppInitializationContext,
13
14
  Annotations,
@@ -61,7 +62,12 @@ export interface AsgardThemeContextValue {
61
62
  };
62
63
  }>;
63
64
  };
64
- botMessage: Pick<CSSProperties, 'color' | 'backgroundColor'>;
65
+ botMessage: Pick<CSSProperties, 'color' | 'backgroundColor'> & {
66
+ carouselButtonBackgroundColor?: CSSProperties['backgroundColor'];
67
+ linkColor?: CSSProperties['color'];
68
+ unsentBackgroundColor?: CSSProperties['backgroundColor'];
69
+ quickReplyBackgroundColor?: CSSProperties['backgroundColor'];
70
+ };
65
71
  userMessage: Pick<CSSProperties, 'color' | 'backgroundColor'>;
66
72
  template?: Partial<{
67
73
  /**
@@ -316,6 +322,15 @@ export function AsgardThemeContextProvider(
316
322
  botMessage: {
317
323
  backgroundColor: themeFromAnnotations.botMessage?.backgroundColor, // #585858
318
324
  color: themeFromAnnotations.botMessage?.color,
325
+ linkColor: themeFromAnnotations.botMessage?.backgroundColor
326
+ ? darkenColor(themeFromAnnotations.botMessage.backgroundColor, 0.2)
327
+ : undefined,
328
+ unsentBackgroundColor: themeFromAnnotations.botMessage?.backgroundColor
329
+ ? `color-mix(in srgb, ${themeFromAnnotations.botMessage.backgroundColor} 20%, transparent)`
330
+ : undefined,
331
+ quickReplyBackgroundColor: themeFromAnnotations.botMessage?.backgroundColor
332
+ ? `color-mix(in srgb, ${themeFromAnnotations.botMessage.backgroundColor} 20%, transparent)`
333
+ : undefined,
319
334
  },
320
335
  userMessage: {
321
336
  backgroundColor: themeFromAnnotations.userMessage?.backgroundColor,
@@ -382,6 +397,136 @@ export function AsgardThemeContextProvider(
382
397
  },
383
398
  });
384
399
 
400
+ // Handle theme prop that contains backend format (like themeFromAnnotations)
401
+ const propsTheme = theme as any; // Cast to handle backend format
402
+ const propsHasBackendFormat = propsTheme.chatbot?.primaryComponent || propsTheme.chatbot?.backgroundColor || propsTheme.botMessage || propsTheme.userMessage;
403
+
404
+ if (propsHasBackendFormat) {
405
+ // Apply same transformation logic as tempTheme for props theme
406
+ const transformedPropsTheme = deepMerge(defaultAsgardThemeContextValue as unknown as Record<string, unknown>, {
407
+ chatbot: {
408
+ backgroundColor: propsTheme.chatbot?.backgroundColor,
409
+ borderColor: propsTheme.chatbot?.borderColor,
410
+ header: {
411
+ style: {
412
+ borderBottomColor: propsTheme.chatbot?.borderColor,
413
+ },
414
+ title: {
415
+ style: {
416
+ color: propsTheme.chatbot?.primaryComponent?.secondaryColor,
417
+ },
418
+ },
419
+ actionButton: {
420
+ style: {
421
+ color: propsTheme.chatbot?.inactiveColor,
422
+ },
423
+ },
424
+ },
425
+ body: {
426
+ style: {
427
+ // Time/timestamp text color
428
+ color: propsTheme.chatbot?.inactiveColor,
429
+ },
430
+ },
431
+ footer: {
432
+ style: {
433
+ borderTopColor: propsTheme.chatbot?.borderColor,
434
+ },
435
+ textArea: {
436
+ style: {
437
+ color: propsTheme.chatbot?.inactiveColor,
438
+ backgroundColor: propsTheme.chatbot?.backgroundColor,
439
+ },
440
+ '::placeholder': {
441
+ color: propsTheme.chatbot?.inactiveColor,
442
+ },
443
+ },
444
+ submitButton: {
445
+ style: {
446
+ color: propsTheme.chatbot?.primaryComponent?.secondaryColor,
447
+ },
448
+ },
449
+ speechInputButton: {
450
+ style: {
451
+ color: propsTheme.chatbot?.primaryComponent?.secondaryColor,
452
+ },
453
+ },
454
+ },
455
+ },
456
+ botMessage: {
457
+ backgroundColor: propsTheme.botMessage?.backgroundColor,
458
+ color: propsTheme.botMessage?.color,
459
+ linkColor: propsTheme.botMessage?.backgroundColor
460
+ ? darkenColor(propsTheme.botMessage.backgroundColor, 0.2)
461
+ : undefined,
462
+ unsentBackgroundColor: propsTheme.botMessage?.backgroundColor
463
+ ? `color-mix(in srgb, ${propsTheme.botMessage.backgroundColor} 20%, transparent)`
464
+ : undefined,
465
+ quickReplyBackgroundColor: propsTheme.botMessage?.backgroundColor
466
+ ? `color-mix(in srgb, ${propsTheme.botMessage.backgroundColor} 20%, transparent)`
467
+ : undefined,
468
+ },
469
+ userMessage: {
470
+ backgroundColor: propsTheme.userMessage?.backgroundColor,
471
+ color: propsTheme.userMessage?.color,
472
+ },
473
+ template: {
474
+ quickReplies: {
475
+ button: {
476
+ style: {
477
+ color: propsTheme.chatbot?.primaryComponent?.secondaryColor,
478
+ borderColor: propsTheme.chatbot?.borderColor,
479
+ backgroundColor: propsTheme.botMessage?.backgroundColor
480
+ ? `${propsTheme.botMessage.backgroundColor}33`
481
+ : undefined,
482
+ },
483
+ },
484
+ },
485
+ time: {
486
+ style: {
487
+ color: propsTheme.chatbot?.inactiveColor,
488
+ },
489
+ },
490
+ TextMessageTemplate: {
491
+ style: {
492
+ // For unset messages
493
+ color: propsTheme.chatbot?.primaryComponent?.secondaryColor,
494
+ },
495
+ },
496
+ HintMessageTemplate: {
497
+ style: {
498
+ color: propsTheme.chatbot?.primaryComponent?.secondaryColor,
499
+ },
500
+ },
501
+ ButtonMessageTemplate: {
502
+ button: {
503
+ style: {
504
+ borderColor: propsTheme.chatbot?.borderColor,
505
+ backgroundColor: propsTheme.chatbot?.primaryComponent?.mainColor,
506
+ color: propsTheme.chatbot?.primaryComponent?.secondaryColor,
507
+ },
508
+ },
509
+ },
510
+ CarouselMessageTemplate: {
511
+ card: {
512
+ style: {
513
+ backgroundColor: propsTheme.botMessage?.carouselButtonBackgroundColor,
514
+ },
515
+ button: {
516
+ style: {
517
+ borderColor: propsTheme.chatbot?.borderColor,
518
+ backgroundColor: propsTheme.chatbot?.primaryComponent?.mainColor,
519
+ color: propsTheme.chatbot?.primaryComponent?.secondaryColor,
520
+ },
521
+ },
522
+ },
523
+ },
524
+ },
525
+ });
526
+
527
+ return deepMerge(tempTheme, transformedPropsTheme);
528
+ }
529
+
385
530
  return deepMerge(tempTheme, theme);
386
531
  },
387
532
  [theme, annotations?.embedConfig?.theme]
@@ -14,6 +14,7 @@ import rehypeKatex from 'rehype-katex';
14
14
  import 'katex/dist/katex.min.css';
15
15
  import classes from '../components/templates/text-template/text-template.module.scss';
16
16
  import { useAsgardTemplateContext } from '../context/asgard-template-context';
17
+ import { useAsgardThemeContext } from '../context/asgard-theme-context';
17
18
  import { safeWindowOpen } from '../utils/uri-validation';
18
19
 
19
20
  interface MarkdownRenderResult {
@@ -98,9 +99,10 @@ const CodeRenderer = ({ children, className, ...props }: React.ComponentProps<'c
98
99
  );
99
100
  };
100
101
 
101
- // Custom link renderer to integrate defaultLinkTarget prop
102
+ // Custom link renderer to integrate defaultLinkTarget prop and theme colors
102
103
  const LinkRenderer = ({ children, href, ...props }: React.ComponentProps<'a'>): ReactNode => {
103
104
  const { defaultLinkTarget } = useAsgardTemplateContext();
105
+ const { botMessage } = useAsgardThemeContext();
104
106
 
105
107
  const handleClick = useCallback(
106
108
  (e: React.MouseEvent) => {
@@ -113,7 +115,17 @@ const LinkRenderer = ({ children, href, ...props }: React.ComponentProps<'a'>):
113
115
  );
114
116
 
115
117
  return (
116
- <a href={href} onClick={handleClick} rel="noopener noreferrer" {...props}>
118
+ <a
119
+ href={href}
120
+ onClick={handleClick}
121
+ rel="noopener noreferrer"
122
+ style={{
123
+ color: botMessage?.linkColor || '#0066cc',
124
+ textDecoration: 'underline',
125
+ ...props.style
126
+ }}
127
+ {...props}
128
+ >
117
129
  {children}
118
130
  </a>
119
131
  );
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Darkens a color by a given percentage
3
+ * @param color - Hex color string (e.g., "#640000")
4
+ * @param percentage - Percentage to darken (e.g., 0.2 for 20%)
5
+ * @returns Darkened hex color string
6
+ */
7
+ export function darkenColor(color: string, percentage: number): string {
8
+ // Remove # if present
9
+ const hex = color.replace('#', '');
10
+
11
+ // Parse RGB values
12
+ const r = parseInt(hex.slice(0, 2), 16);
13
+ const g = parseInt(hex.slice(2, 4), 16);
14
+ const b = parseInt(hex.slice(4, 6), 16);
15
+
16
+ // Apply darkening (multiply by 1 - percentage)
17
+ const factor = 1 - percentage;
18
+ const newR = Math.round(r * factor);
19
+ const newG = Math.round(g * factor);
20
+ const newB = Math.round(b * factor);
21
+
22
+ // Convert back to hex
23
+ const toHex = (n: number): string => n.toString(16).padStart(2, '0');
24
+
25
+ return `#${toHex(newR)}${toHex(newG)}${toHex(newB)}`;
26
+ }
27
+
28
+ /**
29
+ * Lightens a color by mixing with white
30
+ * @param color - Hex color string (e.g., "#640000")
31
+ * @param percentage - Percentage to lighten (e.g., 0.8 for 20% original, 80% white)
32
+ * @returns Lightened hex color string
33
+ */
34
+ export function lightenColor(color: string, percentage: number): string {
35
+ // Remove # if present
36
+ const hex = color.replace('#', '');
37
+
38
+ // Parse RGB values
39
+ const r = parseInt(hex.slice(0, 2), 16);
40
+ const g = parseInt(hex.slice(2, 4), 16);
41
+ const b = parseInt(hex.slice(4, 6), 16);
42
+
43
+ // Mix with white: original + (255 - original) * percentage
44
+ const newR = Math.round(r + (255 - r) * percentage);
45
+ const newG = Math.round(g + (255 - g) * percentage);
46
+ const newB = Math.round(b + (255 - b) * percentage);
47
+
48
+ // Convert back to hex
49
+ const toHex = (n: number): string => n.toString(16).padStart(2, '0');
50
+
51
+ return `#${toHex(newR)}${toHex(newG)}${toHex(newB)}`;
52
+ }