botframework-webchat-fluent-theme 4.17.1 → 4.18.1-hotfix.20260127.b53acdf

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.
Files changed (96) hide show
  1. package/dist/botframework-webchat-fluent-theme.css.map +1 -1
  2. package/dist/botframework-webchat-fluent-theme.d.mts +23 -5
  3. package/dist/botframework-webchat-fluent-theme.d.ts +23 -5
  4. package/dist/botframework-webchat-fluent-theme.development.css.map +1 -1
  5. package/dist/botframework-webchat-fluent-theme.development.js +11 -1
  6. package/dist/botframework-webchat-fluent-theme.development.js.map +1 -1
  7. package/dist/botframework-webchat-fluent-theme.js +1 -1
  8. package/dist/botframework-webchat-fluent-theme.js.map +1 -1
  9. package/dist/botframework-webchat-fluent-theme.mjs +1 -1
  10. package/dist/botframework-webchat-fluent-theme.mjs.map +1 -1
  11. package/dist/botframework-webchat-fluent-theme.production.min.css.map +1 -1
  12. package/dist/botframework-webchat-fluent-theme.production.min.js +11 -1
  13. package/dist/botframework-webchat-fluent-theme.production.min.js.map +1 -1
  14. package/package.json +27 -26
  15. package/src/bundle.ts +2 -0
  16. package/src/components/activity/ActivityDecorator.module.css +432 -0
  17. package/src/components/activity/ActivityDecorator.tsx +26 -0
  18. package/src/components/activity/ActivityLoader.module.css +10 -0
  19. package/src/components/activity/ActivityLoader.tsx +21 -0
  20. package/src/components/activity/CopilotMessageHeader.module.css +38 -0
  21. package/src/components/activity/CopilotMessageHeader.tsx +49 -0
  22. package/src/components/activity/index.ts +2 -0
  23. package/src/components/activity/private/isAIGeneratedActivity.ts +5 -0
  24. package/src/components/activity/private/useActivityAuthor.ts +16 -0
  25. package/src/components/activity/private/useActivityStyleOptions.ts +19 -0
  26. package/src/components/assets/AssetComposer.tsx +37 -0
  27. package/src/components/assets/AssetName.ts +1 -0
  28. package/src/components/assets/SlidingDots.tsx +61 -0
  29. package/src/components/assets/private/Context.ts +24 -0
  30. package/src/components/assets/private/useAssetURL.ts +12 -0
  31. package/src/components/assets/private/useContext.ts +8 -0
  32. package/src/components/dropZone/DropZone.module.css +3 -4
  33. package/src/components/dropZone/DropZone.tsx +41 -6
  34. package/src/components/linerActivity/index.ts +2 -0
  35. package/src/components/linerActivity/private/LinerActivity.tsx +20 -0
  36. package/src/components/linerActivity/private/LinerMessageActivity.module.css +28 -0
  37. package/src/components/linerActivity/private/isLinerMessageActivity.ts +7 -0
  38. package/src/components/preChatActivity/PreChatMessageActivity.module.css +56 -0
  39. package/src/components/preChatActivity/PreChatMessageActivity.tsx +60 -0
  40. package/src/components/preChatActivity/StarterPromptsCardAction.module.css +126 -0
  41. package/src/components/preChatActivity/StarterPromptsCardAction.tsx +75 -0
  42. package/src/components/preChatActivity/StarterPromptsToolbar.module.css +18 -0
  43. package/src/components/preChatActivity/StarterPromptsToolbar.tsx +47 -0
  44. package/src/components/preChatActivity/index.tsx +2 -0
  45. package/src/components/preChatActivity/isPreChatMessageActivity.ts +7 -0
  46. package/src/components/sendBox/Attachments.module.css +1 -1
  47. package/src/components/sendBox/Attachments.tsx +5 -4
  48. package/src/components/sendBox/ErrorMessage.tsx +15 -4
  49. package/src/components/sendBox/SendBox.module.css +21 -6
  50. package/src/components/sendBox/SendBox.tsx +95 -56
  51. package/src/components/sendBox/TextArea.module.css +27 -8
  52. package/src/components/sendBox/TextArea.tsx +60 -31
  53. package/src/components/sendBox/Toolbar.module.css +15 -7
  54. package/src/components/sendBox/Toolbar.tsx +17 -7
  55. package/src/components/sendBox/index.tsx +1 -1
  56. package/src/components/sendBox/private/useSubmitError.ts +17 -4
  57. package/src/components/sendBox/private/useTranscriptNavigation.ts +53 -0
  58. package/src/components/suggestedActions/AccessibleButton.tsx +15 -13
  59. package/src/components/suggestedActions/SuggestedAction.module.css +14 -13
  60. package/src/components/suggestedActions/SuggestedAction.tsx +7 -4
  61. package/src/components/suggestedActions/SuggestedActions.module.css +2 -3
  62. package/src/components/suggestedActions/SuggestedActions.tsx +49 -46
  63. package/src/components/telephoneKeypad/private/Button.module.css +2 -3
  64. package/src/components/telephoneKeypad/private/Button.tsx +1 -5
  65. package/src/components/telephoneKeypad/private/TelephoneKeypad.module.css +0 -1
  66. package/src/components/telephoneKeypad/private/TelephoneKeypad.tsx +4 -8
  67. package/src/components/telephoneKeypad/types.ts +1 -1
  68. package/src/components/theme/Theme.module.css +665 -15
  69. package/src/components/theme/Theme.tsx +4 -3
  70. package/src/components/typingIndicator/SlidingDotsTypingIndicator.module.css +12 -0
  71. package/src/components/typingIndicator/SlidingDotsTypingIndicator.tsx +19 -0
  72. package/src/env.d.ts +1 -7
  73. package/src/external.umd/botframework-webchat-api/decorator.ts +1 -0
  74. package/src/external.umd/botframework-webchat-component/decorator.ts +1 -0
  75. package/src/external.umd/botframework-webchat-component/index.ts +5 -0
  76. package/src/external.umd/botframework-webchat-component/internal.ts +1 -0
  77. package/src/icons/AddDocumentIcon.tsx +8 -16
  78. package/src/icons/AttachmentIcon.tsx +8 -16
  79. package/src/icons/InfoSmallIcon.tsx +7 -15
  80. package/src/icons/SendIcon.tsx +7 -15
  81. package/src/icons/TelephoneKeypadIcon.tsx +7 -15
  82. package/src/index.ts +2 -4
  83. package/src/private/FluentThemeProvider.tsx +91 -10
  84. package/src/private/VariantComposer.ts +29 -0
  85. package/src/private/createComposer.tsx +16 -0
  86. package/src/private/useVariants.ts +7 -0
  87. package/src/styles/createStyles.ts +5 -0
  88. package/src/styles/index.ts +3 -2
  89. package/src/styles/useStyles.ts +2 -19
  90. package/src/styles/useVariantClassName.ts +16 -0
  91. package/src/testIds.ts +3 -0
  92. package/src/tsconfig.json +12 -10
  93. package/src/types/PropsOf.ts +2 -5
  94. package/src/external.umd/botframework-webchat-component.ts +0 -4
  95. package/src/styles/injectStyle.ts +0 -9
  96. /package/src/external.umd/{botframework-webchat-api.ts → botframework-webchat-api/index.ts} +0 -0
@@ -0,0 +1,5 @@
1
+ import { getOrgSchemaMessage, type WebChatActivity } from 'botframework-webchat-core';
2
+
3
+ export default function isAIGeneratedActivity(activity: undefined | WebChatActivity) {
4
+ return !!(activity && getOrgSchemaMessage(activity?.entities || [])?.keywords?.includes('AIGeneratedContent'));
5
+ }
@@ -0,0 +1,16 @@
1
+ import { useMemo } from 'react';
2
+ import { getOrgSchemaMessage, type WebChatActivity } from 'botframework-webchat-core';
3
+
4
+ export default function useActivityAuthor(activity?: WebChatActivity | undefined) {
5
+ return useMemo(() => {
6
+ const entity = getOrgSchemaMessage(activity?.entities || []);
7
+ return typeof entity?.author === 'string'
8
+ ? {
9
+ '@type': 'Person',
10
+ description: undefined,
11
+ image: undefined,
12
+ name: entity?.author
13
+ }
14
+ : entity?.author;
15
+ }, [activity]);
16
+ }
@@ -0,0 +1,19 @@
1
+ import { useMemo } from 'react';
2
+ import { hooks, type WebChatActivity } from 'botframework-webchat-component';
3
+ import { type StrictStyleOptions } from 'botframework-webchat-api';
4
+
5
+ const { useStyleOptions } = hooks;
6
+
7
+ export default function useActivityStyleOptions(activity?: WebChatActivity | undefined) {
8
+ const [styleOptions] = useStyleOptions();
9
+ return useMemo<readonly [Readonly<StrictStyleOptions>]>(
10
+ () =>
11
+ Object.freeze([
12
+ {
13
+ ...styleOptions,
14
+ ...activity?.channelData?.webChat?.styleOptions
15
+ }
16
+ ]),
17
+ [activity?.channelData?.webChat?.styleOptions, styleOptions]
18
+ );
19
+ }
@@ -0,0 +1,37 @@
1
+ import { type ContextOf } from 'botframework-webchat-api';
2
+ import React, { memo, useEffect, useMemo, type ReactNode } from 'react';
3
+
4
+ import { type AssetName } from './AssetName';
5
+ import Context from './private/Context';
6
+
7
+ type ContextType = ContextOf<typeof Context>;
8
+
9
+ type AssetComposerProps = Readonly<{
10
+ children?: ReactNode | undefined;
11
+ }>;
12
+
13
+ const SLIDING_DOTS_SVG_STRING =
14
+ '<svg xmlns="http://www.w3.org/2000/svg" width="400" height="20" viewBox="0 0 400 20"><defs><linearGradient id="a" x1="0" x2="100%" y1="0" y2="0" gradientUnits="userSpaceOnUse"><stop offset="0%"><animate attributeName="stop-color" dur="2s" keyTimes="0;0.2;0.33;0.5;0.66;0.8;1" repeatCount="indefinite" values="#ad5ae1;#ad5ae1;#0E94E1;#0E94E1;#669fc2;#669fc2;#ad5ae1"/></stop><stop offset="50%"><animate attributeName="stop-color" dur="2s" keyTimes="0;0.2;0.33;0.5;0.66;0.8;1" repeatCount="indefinite" values="#e9618d;#e9618d;#57AB82;#57AB82;#6377e0;#6377e0;#e9618d"/></stop><stop offset="100%"><animate attributeName="stop-color" dur="2s" keyTimes="0;0.2;0.33;0.5;0.66;0.8;1" repeatCount="indefinite" values="#fd9e5f;#fd9e5f;#C6C225;#C6C225;#9b80ec;#9b80ec;#fd9e5f"/></stop></linearGradient></defs><g fill="url(#a)"><rect height="20" rx="10"><animate attributeName="x" dur="2s" keyTimes="0;0.5;0.66;1" repeatCount="indefinite" values="26;26;0;0"/><animate attributeName="width" dur="2s" keyTimes="0;0.2;0.33;0.5;0.66;1" repeatCount="indefinite" values="20;20;30;30;20;20"/><animate attributeName="opacity" dur="2s" keyTimes="0;0.5;0.66;1" repeatCount="indefinite" values="1;1;0;0"/></rect><rect height="20" rx="10"><animate attributeName="x" dur="2s" keyTimes="0;0.2;0.33;0.5;0.66;0.8;1" repeatCount="indefinite" values="62;62;72;72;26;26;0"/><animate attributeName="width" dur="2s" keyTimes="0;0.2;0.33;0.5;0.66;0.8;1" repeatCount="indefinite" values="104;104;20;20;70;70;20"/><animate attributeName="opacity" dur="2s" keyTimes="0;0.8;1" repeatCount="indefinite" values="1;1;0"/></rect><rect height="20" rx="10"><animate attributeName="x" dur="2s" keyTimes="0;0.2;0.33;0.5;0.66;0.8;1" repeatCount="indefinite" values="182;182;108;108;112;112;26"/><animate attributeName="width" dur="2s" keyTimes="0;0.2;0.33;0.5;0.66;1" repeatCount="indefinite" values="20;20;60;60;20;20"/></rect><rect height="20" rx="10"><animate attributeName="x" dur="2s" keyTimes="0;0.2;0.33;0.5;0.66;0.8;1" repeatCount="indefinite" values="218;218;184;184;148;148;62"/><animate attributeName="width" dur="2s" keyTimes="0;0.2;0.33;0.5;0.66;0.8;1" repeatCount="indefinite" values="60;60;80;80;40;40;104"/></rect><rect height="20" rx="10"><animate attributeName="x" dur="2s" keyTimes="0;0.2;0.33;0.5;0.66;0.8;1" repeatCount="indefinite" values="294;294;280;280;204;204;182"/><animate attributeName="width" dur="2s" keyTimes="0;0.2;0.33;0.5;0.66;0.8;1" repeatCount="indefinite" values="40;40;20;20;80;80;20"/></rect><rect height="20" rx="10"><animate attributeName="x" dur="2s" keyTimes="0;0.2;0.33;0.5;0.66;0.8;1" repeatCount="indefinite" values="350;350;316;316;300;300;218"/><animate attributeName="width" dur="2s" keyTimes="0;0.2;0.33;0.5;0.66;0.8;1" repeatCount="indefinite" values="20;20;60;60;20;20;60"/></rect><rect height="20" rx="10"><animate attributeName="x" dur="2s" keyTimes="0;0.2;0.33;0.5;0.66;0.8;1" repeatCount="indefinite" values="386;386;392;392;336;336;294"/><animate attributeName="width" dur="2s" keyTimes="0;0.5;0.66;1" repeatCount="indefinite" values="20;20;40;40"/><animate attributeName="opacity" dur="2s" keyTimes="0;0.5;0.66;1" repeatCount="indefinite" values="0;0;1;1"/></rect><rect width="20" height="20" rx="10"><animate attributeName="x" dur="2s" keyTimes="0;0.2;0.33;0.5;0.66;0.8;1" repeatCount="indefinite" values="422;422;428;428;392;392;350"/><animate attributeName="opacity" dur="2s" keyTimes="0;0.8;1" repeatCount="indefinite" values="0;0;1"/></rect></g></svg>';
15
+
16
+ const AssetComposer = memo(({ children }: AssetComposerProps) => {
17
+ const slidingDotsURL = useMemo(
18
+ () => URL.createObjectURL(new Blob([SLIDING_DOTS_SVG_STRING], { type: 'image/svg+xml' })),
19
+ []
20
+ );
21
+
22
+ useEffect(() => () => URL.revokeObjectURL(slidingDotsURL), [slidingDotsURL]);
23
+
24
+ const context = useMemo<ContextType>(
25
+ () =>
26
+ Object.freeze({
27
+ urlStateMap: new Map<AssetName, readonly [URL]>([['sliding dots', Object.freeze([new URL(slidingDotsURL)])]])
28
+ }),
29
+ [slidingDotsURL]
30
+ );
31
+
32
+ return <Context.Provider value={context}>{children}</Context.Provider>;
33
+ });
34
+
35
+ AssetComposer.displayName = 'AssetComposer';
36
+
37
+ export default AssetComposer;
@@ -0,0 +1 @@
1
+ export type AssetName = 'sliding dots';
@@ -0,0 +1,61 @@
1
+ import { hooks } from 'botframework-webchat-component';
2
+ import React, { memo, useCallback, useEffect, useRef } from 'react';
3
+ import { useRefFrom } from 'use-ref-from';
4
+
5
+ import useAssetURL from './private/useAssetURL';
6
+
7
+ const { useLocalizer, useShouldReduceMotion } = hooks;
8
+
9
+ type SlidingDotsProps = Readonly<{ className: string }>;
10
+
11
+ const SlidingDots = ({ className }: SlidingDotsProps) => {
12
+ const [shouldReduceMotion] = useShouldReduceMotion();
13
+ const [url] = useAssetURL('sliding dots');
14
+ const localize = useLocalizer();
15
+ const objectElementRef = useRef<HTMLObjectElement>(null);
16
+
17
+ const altText = localize('TYPING_INDICATOR_ALT');
18
+ const shouldReduceMotionRef = useRefFrom(shouldReduceMotion);
19
+
20
+ const pauseAnimations = useCallback(() => {
21
+ const contentDocument = objectElementRef.current?.contentDocument;
22
+ const svgElement = contentDocument?.documentElement;
23
+ const { SVGSVGElement } = contentDocument?.defaultView || {};
24
+
25
+ SVGSVGElement && svgElement instanceof SVGSVGElement && svgElement.pauseAnimations();
26
+ }, [objectElementRef]);
27
+
28
+ const unpauseAnimations = useCallback(() => {
29
+ const contentDocument = objectElementRef.current?.contentDocument;
30
+ const svgElement = contentDocument?.documentElement;
31
+ const { SVGSVGElement } = contentDocument?.defaultView || {};
32
+
33
+ SVGSVGElement && svgElement instanceof SVGSVGElement && svgElement.unpauseAnimations();
34
+ }, [objectElementRef]);
35
+
36
+ const pauseOrUnpauseAnimations = useCallback(
37
+ () => (shouldReduceMotionRef.current ? pauseAnimations() : unpauseAnimations()),
38
+ [pauseAnimations, shouldReduceMotionRef, unpauseAnimations]
39
+ );
40
+
41
+ useEffect(pauseOrUnpauseAnimations, [
42
+ pauseOrUnpauseAnimations,
43
+ // Call "pauseOrUnpauseAnimations()" when "shouldReduceMotion" change.
44
+ shouldReduceMotion
45
+ ]);
46
+
47
+ return (
48
+ <object
49
+ aria-label={altText}
50
+ className={className}
51
+ data={url.href}
52
+ onLoad={pauseOrUnpauseAnimations}
53
+ ref={objectElementRef}
54
+ type="image/svg+xml"
55
+ />
56
+ );
57
+ };
58
+
59
+ SlidingDots.displayName = 'SlidingDots';
60
+
61
+ export default memo(SlidingDots);
@@ -0,0 +1,24 @@
1
+ import { createContext } from 'react';
2
+
3
+ import type { AssetName } from '../AssetName';
4
+
5
+ type ContextType = Readonly<{
6
+ urlStateMap: ReadonlyMap<AssetName, readonly [URL]>;
7
+ }>;
8
+
9
+ type ContextAsGetter<T extends Record<string, unknown>> =
10
+ T extends Record<infer K, infer V> ? Record<K, { get(): V }> : never;
11
+
12
+ const defaultContextValue: ContextAsGetter<ContextType> = {
13
+ urlStateMap: {
14
+ get() {
15
+ throw new Error('urlMap cannot be used outside of <AssetComposerContext>.');
16
+ }
17
+ }
18
+ };
19
+
20
+ const Context = createContext<ContextType>(Object.create({}, defaultContextValue));
21
+
22
+ Context.displayName = 'AssetComposerContext';
23
+
24
+ export default Context;
@@ -0,0 +1,12 @@
1
+ import { type AssetName } from '../AssetName';
2
+ import useContext from './useContext';
3
+
4
+ export default function useAssetURL(assetName: AssetName): readonly [URL] {
5
+ const urlState = useContext().urlStateMap.get(assetName);
6
+
7
+ if (!urlState) {
8
+ throw new Error(`botframework-webchat-fluent-theme internal: Asset "${assetName}" was not found.`);
9
+ }
10
+
11
+ return urlState;
12
+ }
@@ -0,0 +1,8 @@
1
+ import { type ContextOf } from 'botframework-webchat-api';
2
+ import { useContext as useReactContext } from 'react';
3
+
4
+ import Context from './Context';
5
+
6
+ export default function useContext(): ContextOf<typeof Context> {
7
+ return useReactContext(Context);
8
+ }
@@ -8,16 +8,15 @@
8
8
  place-content: center;
9
9
  place-items: center;
10
10
  position: absolute;
11
+ transition: all var(--webchat-durationNormal) var(--webchat-curveDecelerateMid);
11
12
  }
12
13
 
13
14
  :global(.webchat-fluent) .sendbox__attachment-drop-zone--droppable {
14
- background-color: #e00;
15
- color: White
15
+ background-color: var(--webchat-colorBrandBackground2Hover);
16
16
  }
17
17
 
18
18
  :global(.webchat-fluent) .sendbox__attachment-drop-zone-icon {
19
- height: 36px;
19
+ font-size: 36px;
20
20
  /* Set "pointer-events: none" to ignore dragging over the icon. Otherwise, when dragging over the icon; it would disable the "--droppable" modifier.*/
21
21
  pointer-events: none;
22
- width: 36px
23
22
  }
@@ -1,6 +1,14 @@
1
1
  import { hooks } from 'botframework-webchat-component';
2
2
  import cx from 'classnames';
3
- import React, { memo, useCallback, useEffect, useRef, useState, type DragEventHandler } from 'react';
3
+ import React, {
4
+ memo,
5
+ useCallback,
6
+ useEffect,
7
+ useRef,
8
+ useState,
9
+ type DragEvent as ReactDragEvent,
10
+ type DragEventHandler
11
+ } from 'react';
4
12
  import { useRefFrom } from 'use-ref-from';
5
13
 
6
14
  import { AddDocumentIcon } from '../../icons';
@@ -10,14 +18,20 @@ import { useStyles } from '../../styles';
10
18
 
11
19
  const { useLocalizer } = hooks;
12
20
 
13
- const handleDragOver: DragEventHandler<HTMLDivElement> = event => {
14
- // This is for preventing the browser from opening the dropped file in a new tab.
21
+ const handleDragOver = (event: ReactDragEvent<unknown> | DragEvent) => {
22
+ // Prevent default dragover behavior to enable drop event triggering.
23
+ // Browsers require this to fire subsequent drop events - without it,
24
+ // they would handle the drop directly (e.g., open files in new tabs).
25
+ // This is needed regardless of whether we prevent default drop behavior,
26
+ // as it ensures our dropzone receives the drop event first. If we allow
27
+ // default drop handling (by not calling preventDefault there), the browser
28
+ // will still process the drop after our event handlers complete.
15
29
  event.preventDefault();
16
30
  };
17
31
 
18
32
  // Notes: For files dragging from outside of browser, it only tell us if it is a "File" instead of "text/plain" or "text/uri-list".
19
33
  // For images dragging inside of browser, it only tell us that it is "text/plain", "text/uri-list" and "text/html". But not "image/*".
20
- // So we cannot whitelist what is droppable.
34
+ // So we cannot allowlist what is droppable.
21
35
  // We are using case-insensitive of type "files" so we can drag in WebDriver.
22
36
  const isFilesTransferEvent = (event: DragEvent) =>
23
37
  !!event.dataTransfer?.types?.some(type => type.toLowerCase() === 'files');
@@ -47,6 +61,8 @@ const DropZone = (props: { readonly onFilesAdded: (files: File[]) => void }) =>
47
61
  let entranceCounter = 0;
48
62
 
49
63
  const handleDragEnter = (event: DragEvent) => {
64
+ document.addEventListener('dragover', handleDragOver);
65
+
50
66
  entranceCounter++;
51
67
 
52
68
  if (isFilesTransferEvent(event)) {
@@ -62,12 +78,31 @@ const DropZone = (props: { readonly onFilesAdded: (files: File[]) => void }) =>
62
78
 
63
79
  const handleDragLeave = () => --entranceCounter <= 0 && setDropZoneState(false);
64
80
 
65
- document.addEventListener('dragenter', handleDragEnter, false);
66
- document.addEventListener('dragleave', handleDragLeave, false);
81
+ const handleDragEnd = () => {
82
+ document.removeEventListener('dragover', handleDragOver);
83
+
84
+ entranceCounter = 0;
85
+
86
+ setDropZoneState(false);
87
+ };
88
+
89
+ const handleDocumentDrop = (event: DragEvent) => {
90
+ if (!dropZoneRef.current?.contains(event.target as Node)) {
91
+ handleDragEnd();
92
+ }
93
+ };
94
+
95
+ document.addEventListener('dragend', handleDragEnd);
96
+ document.addEventListener('dragenter', handleDragEnter);
97
+ document.addEventListener('dragleave', handleDragLeave);
98
+ document.addEventListener('drop', handleDocumentDrop);
67
99
 
68
100
  return () => {
101
+ document.removeEventListener('dragend', handleDragEnd);
69
102
  document.removeEventListener('dragenter', handleDragEnter);
70
103
  document.removeEventListener('dragleave', handleDragLeave);
104
+ document.removeEventListener('dragover', handleDragOver);
105
+ document.removeEventListener('drop', handleDocumentDrop);
71
106
  };
72
107
  }, [setDropZoneState]);
73
108
 
@@ -0,0 +1,2 @@
1
+ export { default as isLinerMessageActivity } from './private/isLinerMessageActivity';
2
+ export { default as LinerMessageActivity } from './private/LinerActivity';
@@ -0,0 +1,20 @@
1
+ import { type WebChatActivity } from 'botframework-webchat-core';
2
+ import React, { memo } from 'react';
3
+ import { useStyles } from '../../../styles/index.js';
4
+ import styles from './LinerMessageActivity.module.css';
5
+
6
+ type Props = Readonly<{ activity: WebChatActivity & { type: 'message' } }>;
7
+
8
+ const LinerMessageActivity = ({ activity }: Props) => {
9
+ const classNames = useStyles(styles);
10
+
11
+ return (
12
+ <div className={classNames['liner-message-activity']} role="separator">
13
+ <span className={classNames['liner-message-activity__text']}>{activity.text}</span>
14
+ </div>
15
+ );
16
+ };
17
+
18
+ LinerMessageActivity.displayName = 'LinerMessageActivity';
19
+
20
+ export default memo(LinerMessageActivity);
@@ -0,0 +1,28 @@
1
+ :global(.webchat-fluent) .liner-message-activity {
2
+ align-items: center;
3
+ box-sizing: border-box;
4
+ color: var(--webchat-colorNeutralForeground3);
5
+ display: flex;
6
+ flex-direction: row;
7
+ font-family: var(--webchat__font--primary);
8
+ font-size: var(--webchat-fontSizeBase200);
9
+ font-weight: var(--webchat-fontWeightRegular);
10
+ gap: var(--webchat-spacingHorizontalM);
11
+ line-height: var(--webchat-lineHeightBase200);
12
+ padding: var(--webchat-spacingVerticalSNudge) var(--webchat-spacingHorizontalM);
13
+ text-align: center;
14
+ width: 100%;
15
+
16
+ &::before,
17
+ &::after {
18
+ border-top: var(--webchat-strokeWidthThin) solid var(--webchat-colorNeutralStroke2);
19
+ content: '';
20
+ display: flex;
21
+ flex: auto;
22
+ min-width: 8px;
23
+ }
24
+ }
25
+
26
+ :global(.webchat-fluent) .liner-message-activity__text {
27
+ text-align: center;
28
+ }
@@ -0,0 +1,7 @@
1
+ import { type WebChatActivity } from 'botframework-webchat-core';
2
+
3
+ export default function isLinerMessageActivity(
4
+ activity: undefined | WebChatActivity
5
+ ): activity is WebChatActivity & { type: 'message'; from: { role: 'channel' } } {
6
+ return !!(activity && activity.from.role === 'channel' && activity.type === 'message' && 'text' in activity);
7
+ }
@@ -0,0 +1,56 @@
1
+ :global(.webchat-fluent) .pre-chat-message-activity {
2
+ box-sizing: border-box;
3
+ display: grid;
4
+ gap: var(--webchat-spacingHorizontalXXXL);
5
+ grid-template-areas: 'body' 'toolbar';
6
+ grid-template-rows: auto auto;
7
+ padding: var(--webchat-spacingHorizontalXXXL) var(--webchat-spacingHorizontalM);
8
+ position: relative;
9
+ }
10
+
11
+ :global(.webchat-fluent) .pre-chat-message-activity__body {
12
+ align-items: center;
13
+ display: flex;
14
+ flex-flow: column nowrap;
15
+ font-family: var(--webchat-fontFamilyBase);
16
+ font-size: var(--webchat-fontSizeBase300);
17
+ font-weight: var(--webchat-fontWeightRegular);
18
+ gap: var(--webchat-spacingVerticalXS);
19
+ grid-area: body;
20
+ line-height: var(--webchat-lineHeightBase300);
21
+ text-align: center;
22
+ }
23
+
24
+ :global(.webchat-fluent) .pre-chat-message-activity__body--blueprint {
25
+ opacity: 60%;
26
+
27
+ .pre-chat-message-activity__body-avatar {
28
+ filter: grayscale(1);
29
+ }
30
+ }
31
+
32
+ :global(.webchat-fluent) .pre-chat-message-activity__body-avatar {
33
+ border-radius: var(--webchat-borderRadiusMedium);
34
+ height: 64px;
35
+ margin-block-end: var(--webchat-spacingVerticalM);
36
+ }
37
+
38
+ :global(.webchat-fluent) .pre-chat-message-activity__body-title {
39
+ color: var(--webchat-colorNeutralForeground1);
40
+ font-family: inherit;
41
+ font-size: var(--webchat-fontSizeHero700);
42
+ font-weight: var(--webchat-fontWeightSemibold);
43
+ line-height: var(--webchat-lineHeightHero700);
44
+ margin: 0;
45
+ overflow-wrap: anywhere;
46
+ }
47
+
48
+ :global(.webchat-fluent) .pre-chat-message-activity__body-subtitle {
49
+ font-size: var(--webchat-fontSizeBase300);
50
+ line-height: var(--webchat-lineHeightBase300);
51
+ overflow-wrap: anywhere;
52
+ }
53
+
54
+ :global(.webchat-fluent) .pre-chat-message-activity__toolbar {
55
+ grid-area: toolbar;
56
+ }
@@ -0,0 +1,60 @@
1
+ import { hooks } from 'botframework-webchat-component';
2
+ import { type WebChatActivity } from 'botframework-webchat-core';
3
+ import cx from 'classnames';
4
+ import React, { memo, useMemo } from 'react';
5
+ import { useStyles } from '../../styles/index.js';
6
+ import styles from './PreChatMessageActivity.module.css';
7
+ import StarterPromptsToolbar from './StarterPromptsToolbar.js';
8
+ import { useActivityAuthor } from '../activity/index.js';
9
+
10
+ type Props = Readonly<{ activity: WebChatActivity & { type: 'message' } }>;
11
+
12
+ const { useLocalizer, useRenderMarkdownAsHTML, useUIState } = hooks;
13
+
14
+ const PreChatMessageActivity = ({ activity }: Props) => {
15
+ const [uiState] = useUIState();
16
+ const classNames = useStyles(styles);
17
+ const renderMarkdownAsHTML = useRenderMarkdownAsHTML();
18
+ const localize = useLocalizer();
19
+
20
+ const author = useActivityAuthor(activity);
21
+
22
+ const html = useMemo(
23
+ () => (renderMarkdownAsHTML ? { __html: renderMarkdownAsHTML(author?.description || '') } : { __html: '' }),
24
+ [author?.description, renderMarkdownAsHTML]
25
+ );
26
+
27
+ return (
28
+ <div className={classNames['pre-chat-message-activity']}>
29
+ {author && (
30
+ <div
31
+ className={cx(
32
+ classNames['pre-chat-message-activity__body'],
33
+ uiState === 'blueprint' && classNames['pre-chat-message-activity__body--blueprint']
34
+ )}
35
+ >
36
+ {author.image && (
37
+ <img
38
+ alt={localize('AVATAR_ALT', author.name)}
39
+ className={classNames['pre-chat-message-activity__body-avatar']}
40
+ src={author.image}
41
+ />
42
+ )}
43
+ {author.name && <h2 className={classNames['pre-chat-message-activity__body-title']}>{author.name}</h2>}
44
+ {author.description && (
45
+ // eslint-disable-next-line react/no-danger
46
+ <div className={classNames['pre-chat-message-activity__body-subtitle']} dangerouslySetInnerHTML={html} />
47
+ )}
48
+ </div>
49
+ )}
50
+ <StarterPromptsToolbar
51
+ cardActions={activity.suggestedActions?.actions || []}
52
+ className={classNames['pre-chat-message-activity__toolbar']}
53
+ />
54
+ </div>
55
+ );
56
+ };
57
+
58
+ PreChatMessageActivity.displayName = 'PreChatMessageActivity';
59
+
60
+ export default memo(PreChatMessageActivity);
@@ -0,0 +1,126 @@
1
+ :global(.webchat-fluent) .pre-chat-message-activity__card-action-box {
2
+ appearance: none;
3
+ background-color: var(--webchat-colorNeutralBackground1);
4
+ border: 0;
5
+ border-radius: 16px;
6
+ box-shadow: var(--webchat-shadow2);
7
+ color: var(--webchat-colorNeutralForeground1);
8
+ cursor: pointer;
9
+ display: grid;
10
+ gap: var(--webchat-spacingVerticalS);
11
+ grid-template-areas: 'title' 'subtitle';
12
+ grid-template-columns: 1fr;
13
+ grid-template-rows: auto 1fr;
14
+ overflow: hidden;
15
+ padding: 16px 20px;
16
+ text-align: left;
17
+ transition: background-color var(--webchat-durationNormal) var(--webchat-curveDecelerateMid);
18
+ user-select: none;
19
+
20
+ &:has(.pre-chat-message-activity__card-action-image) {
21
+ grid-template-areas: 'image title' 'image subtitle';
22
+ grid-template-columns: 20px 1fr;
23
+ }
24
+
25
+ &:empty {
26
+ row-gap: 14px;
27
+ }
28
+
29
+ &[aria-disabled='true'],
30
+ &:empty {
31
+ cursor: default;
32
+ }
33
+
34
+ &[aria-disabled='true']:not(:empty) {
35
+ background-color: var(--webchat-colorNeutralBackgroundDisabled);
36
+ }
37
+
38
+ &:hover:not([aria-disabled='true'], :empty) {
39
+ background-color: var(--webchat-colorNeutralBackground1Hover);
40
+ }
41
+
42
+ &:active:not([aria-disabled='true'], :empty) {
43
+ background-color: var(--webchat-colorNeutralBackground1Pressed);
44
+ }
45
+
46
+ &:focus-visible {
47
+ outline: solid 2px var(--webchat-colorStrokeFocus2);
48
+ outline-offset: -2px;
49
+ }
50
+
51
+ &:empty::after,
52
+ &:empty::before {
53
+ animation: blueprintAnimation 3s linear infinite;
54
+ background-attachment: fixed;
55
+ background-image: linear-gradient(
56
+ -90deg,
57
+ var(--webchat-colorNeutralStencil1) 0%,
58
+ var(--webchat-colorNeutralStencil2) 50%,
59
+ var(--webchat-colorNeutralStencil1) 100%
60
+ );
61
+ background-size: 300% 100%;
62
+ content: '';
63
+ display: block;
64
+ }
65
+
66
+ /* animation-* needs to position after animation shorthand. */
67
+ @media (prefers-reduced-motion: reduce) {
68
+ &:empty::after,
69
+ &:empty::before {
70
+ animation-delay: -1s;
71
+ animation-play-state: paused;
72
+ }
73
+ }
74
+
75
+ &:dir(rtl) {
76
+ &:empty::after,
77
+ &:empty::before {
78
+ animation-direction: reverse;
79
+ }
80
+ }
81
+
82
+ &:empty::after {
83
+ border-radius: 16px;
84
+ height: 16px;
85
+ width: 100%;
86
+ }
87
+
88
+ &:empty::before {
89
+ border-radius: 18px;
90
+ height: 18px;
91
+ width: 66%;
92
+ }
93
+ }
94
+
95
+ @keyframes blueprintAnimation {
96
+ from {
97
+ background-position-x: 0%;
98
+ }
99
+
100
+ to {
101
+ background-position-x: -300%;
102
+ }
103
+ }
104
+
105
+ :global(.webchat-fluent) .pre-chat-message-activity__card-action-image {
106
+ color: var(--webchat-colorNeutralForeground4);
107
+ font-size: 20px;
108
+ grid-area: image;
109
+ }
110
+
111
+ :global(.webchat-fluent) .pre-chat-message-activity__card-action-subtitle {
112
+ font-family: var(--webchat-fontFamilyBase);
113
+ font-size: 14px;
114
+ font-weight: var(--webchat-fontWeightRegular);
115
+ grid-area: subtitle;
116
+ line-height: 20px;
117
+ pointer-events: none; /* Links in subtitle are not clickable. */
118
+ }
119
+
120
+ :global(.webchat-fluent) .pre-chat-message-activity__card-action-title {
121
+ font-family: var(--webchat-fontFamilyBase);
122
+ font-size: 14px;
123
+ font-weight: var(--webchat-fontWeightSemibold);
124
+ grid-area: title;
125
+ line-height: 20px;
126
+ }