@marketrix.ai/widget 3.8.493 → 3.8.494
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/README.md +4 -4
- package/dist/loader.js +8 -1
- package/dist/src/components/WidgetRoot.d.ts +19 -0
- package/dist/src/components/base/Avatar.d.ts +5 -2
- package/dist/src/components/base/Button.d.ts +1 -1
- package/dist/src/components/base/ErrorBoundary.d.ts +19 -0
- package/dist/src/components/base/Flex.d.ts +14 -0
- package/dist/src/components/base/Icon.d.ts +12 -1
- package/dist/src/components/base/IconButton.d.ts +4 -0
- package/dist/src/components/base/Spinner.d.ts +4 -0
- package/dist/src/components/base/Surface.d.ts +22 -5
- package/dist/src/components/base/Text.d.ts +17 -1
- package/dist/src/components/base/icons.d.ts +16 -0
- package/dist/src/components/base/layoutProps.d.ts +20 -6
- package/dist/src/components/blocks/ChatInput.d.ts +9 -0
- package/dist/src/components/blocks/HeaderBar.d.ts +4 -0
- package/dist/src/components/blocks/Notifications.d.ts +20 -10
- package/dist/src/components/blocks/WidgetDialog.d.ts +0 -5
- package/dist/src/components/blocks/WidgetFab.d.ts +10 -0
- package/dist/src/components/chat/MessageItem.d.ts +13 -0
- package/dist/src/components/chat/MessageList.d.ts +20 -0
- package/dist/src/components/chat/VideoStreamDisplay.d.ts +20 -0
- package/dist/src/components/navigation/ShellTabBar.d.ts +0 -1
- package/dist/src/components/views/ChatView.d.ts +20 -0
- package/dist/src/components/views/HomeView.d.ts +9 -0
- package/dist/src/context/ChatContext.d.ts +20 -0
- package/dist/src/context/UIStateContext.d.ts +5 -0
- package/dist/src/context/WidgetProviders.d.ts +20 -1
- package/dist/src/context/sseReducer.d.ts +20 -9
- package/dist/src/design-system/component-tokens.d.ts +27 -1
- package/dist/src/design-system/semantic-tokens.d.ts +18 -0
- package/dist/src/hooks/useDragSnap.d.ts +13 -0
- package/dist/src/hooks/useFocusTrap.d.ts +20 -0
- package/dist/src/hooks/useWidget.d.ts +11 -1
- package/dist/src/index.d.ts +20 -0
- package/dist/src/sdk/contracts/common.d.ts +0 -16
- package/dist/src/sdk/index.d.ts +0 -1
- package/dist/src/services/BrowserToolService.d.ts +22 -5
- package/dist/src/services/ChatService.d.ts +15 -1
- package/dist/src/services/ChatSessionManager.d.ts +0 -2
- package/dist/src/services/DomService.d.ts +18 -3
- package/dist/src/services/RrwebSessionRecorder.d.ts +0 -1
- package/dist/src/services/ShowModeService.d.ts +22 -5
- package/dist/src/services/StorageService.d.ts +20 -1
- package/dist/src/services/StreamClient.d.ts +21 -1
- package/dist/src/services/keySimulation.d.ts +3 -0
- package/dist/src/test/fixtures.d.ts +13 -2
- package/dist/src/test/renderWidget.d.ts +6 -0
- package/dist/src/test/setup.d.ts +14 -0
- package/dist/src/types/index.d.ts +20 -7
- package/dist/src/utils/chat.d.ts +21 -1
- package/dist/src/utils/color.d.ts +21 -0
- package/dist/src/utils/dom.d.ts +21 -0
- package/dist/src/utils/errors.d.ts +12 -0
- package/dist/src/utils/suggestedActions.d.ts +11 -0
- package/dist/src/utils/validation.d.ts +20 -7
- package/dist/src/utils/widgetPositioning.d.ts +20 -0
- package/dist/widget.mjs +63 -64
- package/package.json +6 -6
- package/dist/src/components/base/Stack.d.ts +0 -3
- package/dist/src/components/chat/MessageContent.d.ts +0 -8
- package/dist/src/components/chat/SuggestedActions.d.ts +0 -8
- package/dist/src/components/chat/TaskStatusIcon.d.ts +0 -5
- package/dist/src/components/chat/ThinkingIndicator.d.ts +0 -6
- package/dist/src/components/navigation/ResizeHandle.d.ts +0 -7
- package/dist/src/design-system/layers.d.ts +0 -7
- package/dist/src/design-system/shadows.d.ts +0 -9
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@ Add one `<script>` to your page `<head>`, **before any `<script type="module">`
|
|
|
27
27
|
|
|
28
28
|
`loader.js`:
|
|
29
29
|
|
|
30
|
-
1. Injects a React 19 importmap
|
|
30
|
+
1. Injects a React 19 importmap pointing at `esm.sh/react@19`. If your page already has an importmap before the loader, the browser keeps your entries — a later import map never overrides an earlier key — so a host that already ships React 19 keeps its own copy.
|
|
31
31
|
2. Injects `<script type="module" src=".../widget.mjs">` from the same origin as the loader.
|
|
32
32
|
3. Forwards every `mtx-*` attribute from the loader tag to the widget.
|
|
33
33
|
|
|
@@ -102,7 +102,7 @@ Widget **appearance and behavior** (position, colors, sizing, border radius, ani
|
|
|
102
102
|
</html>
|
|
103
103
|
```
|
|
104
104
|
|
|
105
|
-
The loader takes care of the React 19 importmap. If you manage your own importmap, place it before the loader and the loader
|
|
105
|
+
The loader takes care of the React 19 importmap. If you manage your own importmap, place it before the loader; your entries win and the loader's map only fills what is missing:
|
|
106
106
|
|
|
107
107
|
```html
|
|
108
108
|
<script type="importmap">
|
|
@@ -193,13 +193,12 @@ function Preview() {
|
|
|
193
193
|
return (
|
|
194
194
|
<MarketrixWidgetPreview
|
|
195
195
|
settings={{ widget_enabled: true, widget_position: 'bottom_right' /* ...WidgetSettingsData */ }}
|
|
196
|
-
mtxApiHost='https://api.marketrix.ai'
|
|
197
196
|
/>
|
|
198
197
|
);
|
|
199
198
|
}
|
|
200
199
|
```
|
|
201
200
|
|
|
202
|
-
Props: `settings` (required)
|
|
201
|
+
Props: `settings` (required) and `container?`.
|
|
203
202
|
|
|
204
203
|
---
|
|
205
204
|
|
|
@@ -217,6 +216,7 @@ TypeScript types are bundled with the package:
|
|
|
217
216
|
|
|
218
217
|
- `MarketrixConfig` — full config for `initWidget` / `updateMarketrixConfig` (`mtxId`, `mtxKey`, `mtxApiHost`, `userId`, `show_widget`, `use_screenshare`, plus all widget appearance settings, optional).
|
|
219
218
|
- `AddWidgetConfig` — discriminated config for `mountWidget` (production / preview variants + common options).
|
|
219
|
+
- `ClientOwnedConfig` — the host-supplied options the API never sends (`mtxApiHost`, `userId`, `widget_position_z_index`, `show_widget`, `use_screenshare`).
|
|
220
220
|
- `MarketrixWidgetPreviewProps` — props for the `MarketrixWidgetPreview` component.
|
|
221
221
|
- `ChatMessage`, `WidgetState`, `InstructionType` (`'tell' | 'show' | 'do'`).
|
|
222
222
|
|
package/dist/loader.js
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* The classic script-tag bootstrap, served as `dist/loader.js`. Must sit in <head> before any
|
|
3
|
+
* <script type="module">: it writes the importmap those modules resolve `react`/`react-dom` against
|
|
4
|
+
* (esm.sh, React 19 — the bundle keeps React external and the host must supply it), then appends
|
|
5
|
+
* `widget.mjs` as a module script next to itself, copying every `mtx-*` attribute from its own tag so
|
|
6
|
+
* the widget auto-initializes with the host's settings. Plain ES5 on purpose — this file runs unbundled
|
|
7
|
+
* in whatever browser the host page has.
|
|
8
|
+
*/
|
|
2
9
|
(function () {
|
|
3
10
|
var map = document.createElement('script');
|
|
4
11
|
map.type = 'importmap';
|
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The widget's root component: the one place the raw config prop is touched — position and z-index layered onto it
|
|
3
|
+
* and published through `WidgetConfigContext` — and the one place the root element is published through
|
|
4
|
+
* `PortalContainerContext`. Renders `MessengerShell` (inside an `ErrorBoundary`, so a panel crash still leaves the
|
|
5
|
+
* launcher and toasts alive), `WidgetFab`, `WidgetNotifications`, and a fixed screen-edge glow while a reply or
|
|
6
|
+
* task is in flight; a mount-time effect restores the drag-pinned corner and a second arms the greeting toast.
|
|
7
|
+
*
|
|
8
|
+
* The position storage key is written ONLY by a drag — seeding it with `config.widget_position` would pin the
|
|
9
|
+
* dashboard's setting at whatever it was on a visitor's first load — and preview mode neither reads nor writes it,
|
|
10
|
+
* so the dashboard always shows the configured corner. Preview mode overrides the hidden check (`show_widget:
|
|
11
|
+
* false` / `widget_appearance: 'hidden'` suppress the widget on a host page, but the dashboard preview must still
|
|
12
|
+
* render) with its `null` return sitting below every hook so hook order stays stable either way.
|
|
13
|
+
*
|
|
14
|
+
* The published z-index is `max(tenant value, LAYER_TOKENS.panel)` so a tenant's low setting cannot sink the
|
|
15
|
+
* widget under the host page's own stacking context. The root `Surface` IS the `[data-marketrix-widget]` element
|
|
16
|
+
* carrying every tenant token as an inline style, which is why it, not the shadow root, is the portal container —
|
|
17
|
+
* a portal landing outside it falls back to `index.css`'s hardcoded palette. `onRetry` is spread in only when
|
|
18
|
+
* `StreamClient.canReconnect()`, so a terminal failure offers no Retry button rather than one that does nothing.
|
|
19
|
+
*/
|
|
1
20
|
import React from 'react';
|
|
2
21
|
import type { ValidWidgetConfig } from '../types';
|
|
3
22
|
interface WidgetRootProps {
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Avatar` — an `<img>` sized by preset or number, with fit, radius (via `resolveLayoutStyle`) and
|
|
3
|
+
* elevation from the design tokens; keeps its own class beside a caller className.
|
|
4
|
+
*/
|
|
1
5
|
import type { ComponentPropsWithRef } from 'react';
|
|
2
|
-
import { type RadiusToken } from '../../design-system/component-tokens';
|
|
3
|
-
import type { ShadowToken } from '../../design-system/shadows';
|
|
6
|
+
import { type RadiusToken, type ShadowToken } from '../../design-system/component-tokens';
|
|
4
7
|
type AvatarSize = 'sm' | 'md' | 'lg';
|
|
5
8
|
export interface AvatarProps extends Omit<ComponentPropsWithRef<'img'>, 'size'> {
|
|
6
9
|
src: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ComponentPropsWithRef } from 'react';
|
|
2
|
-
import type
|
|
2
|
+
import { type ShadowToken } from '../../design-system/component-tokens';
|
|
3
3
|
type ButtonVariant = 'primary' | 'secondary' | 'ghost' | 'bare' | 'chip' | 'tab';
|
|
4
4
|
type ButtonSize = 'sm' | 'md';
|
|
5
5
|
type ButtonShape = 'default' | 'pill';
|
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The widget's one React error boundary: a subtree that throws while rendering is contained here rather than
|
|
3
|
+
* tearing down the widget's whole React root. It is a class because React exposes no hook equivalent.
|
|
4
|
+
*
|
|
5
|
+
* `ErrorBoundaryProps` are the wrapped `children`, a `label` naming the subtree in the console line, and an
|
|
6
|
+
* optional `fallback` to show in its place. `getDerivedStateFromError` flips to the caught state,
|
|
7
|
+
* `componentDidCatch` logs the error and the component stack, and `render` returns the fallback (or nothing)
|
|
8
|
+
* once caught.
|
|
9
|
+
*
|
|
10
|
+
* - The widget runs inside arbitrary customer pages, so a crash must degrade to a missing widget and never a
|
|
11
|
+
* broken host page. Rendering `null` with no fallback is that degradation, not a swallowed exception —
|
|
12
|
+
* `componentDidCatch` has already surfaced the error with its stack.
|
|
13
|
+
* - `console.error` specifically: terser's `drop_console` strips `log`/`info`/`debug` from the shipped
|
|
14
|
+
* bundle, so only `warn` and `error` reach a host page's console, and a contained render crash is the
|
|
15
|
+
* unexpected failure `error` is reserved for.
|
|
16
|
+
* - Two call sites, each deliberate: `WidgetRoot` wraps `MessengerShell` with no fallback, so a panel crash
|
|
17
|
+
* leaves the launcher and toasts alive, while `ChatView` wraps only the transcript with a refresh prompt,
|
|
18
|
+
* so one unrenderable message cannot take the composer down with it.
|
|
19
|
+
*/
|
|
1
20
|
import React from 'react';
|
|
2
21
|
interface ErrorBoundaryProps {
|
|
3
22
|
children: React.ReactNode;
|
|
@@ -1,7 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The two flex containers over `Surface`. `Flex` is the primitive: `FlexProps` extends `SurfaceProps`
|
|
3
|
+
* with `direction` (`row` default, `column` opt-in) and children, and the forwardRef component spreads
|
|
4
|
+
* the rest onto `Surface` so the whole layout-token vocabulary still applies. `Stack` (`StackProps`) is
|
|
5
|
+
* that same component with `direction='column'` fixed — it lives here rather than in a file of its own
|
|
6
|
+
* because it is three lines of `Flex` and shares its tests.
|
|
7
|
+
*
|
|
8
|
+
* `display` is resolved here rather than left to `resolveLayoutStyle`, which `Surface` applies BEFORE
|
|
9
|
+
* the caller's `style`: a `display: flex` arriving later in that cascade would silently defeat
|
|
10
|
+
* `hidden`, so this component reads `hidden` itself and emits `none` or `flex` from the one place that
|
|
11
|
+
* wins.
|
|
12
|
+
*/
|
|
1
13
|
import { type ReactNode } from 'react';
|
|
2
14
|
import { type SurfaceProps } from './Surface';
|
|
3
15
|
export interface FlexProps extends SurfaceProps {
|
|
4
16
|
direction?: 'row' | 'column';
|
|
5
17
|
children?: ReactNode;
|
|
6
18
|
}
|
|
19
|
+
export type StackProps = Omit<FlexProps, 'direction'>;
|
|
7
20
|
export declare const Flex: import("react").ForwardRefExoticComponent<FlexProps & import("react").RefAttributes<HTMLElement>>;
|
|
21
|
+
export declare const Stack: import("react").ForwardRefExoticComponent<StackProps & import("react").RefAttributes<HTMLElement>>;
|
|
@@ -1,9 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Icon` — the widget's one SVG glyph primitive: resolves `name` in the `icons` registry and renders a
|
|
3
|
+
* square `<svg>` of `size` (default 16), forwarding ref and any other svg prop. An unknown name renders
|
|
4
|
+
* null, so a stale name degrades to a gap rather than a crash.
|
|
5
|
+
*
|
|
6
|
+
* Glyphs are decorative, so `aria-hidden` is fixed here and the accessible name belongs on the control
|
|
7
|
+
* wrapping the icon. A path's fill defaults to `currentColor` — unless it sets a `stroke`, where the
|
|
8
|
+
* default is `none`; that resolution is what lets `icons.ts`'s `stroked()` omit `fill` altogether.
|
|
9
|
+
* `mtx-icon` (the layout rule in `index.css`) always applies and a caller `className` is appended to it,
|
|
10
|
+
* never substituted — and `className` is for `blocks/` only: with no CSS framework here every class must
|
|
11
|
+
* have a real `index.css` rule, and `WidgetFab`'s `mtx-fab-chevron` is the sole production caller.
|
|
12
|
+
*/
|
|
1
13
|
import type { ComponentPropsWithRef } from 'react';
|
|
2
14
|
import { type IconName } from './icons';
|
|
3
15
|
export interface IconProps extends ComponentPropsWithRef<'svg'> {
|
|
4
16
|
name: IconName;
|
|
5
17
|
size?: number;
|
|
6
|
-
/** @internal blocks/ only */
|
|
7
18
|
className?: string;
|
|
8
19
|
}
|
|
9
20
|
export declare function Icon({ name, size, className, ref, ...props }: IconProps): import("react").JSX.Element | null;
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `IconButton` — an icon-only button that REQUIRES a `label` (its aria-label), so no unnamed control
|
|
3
|
+
* can ship; variant and size ride as `data-*`, tone as an inline colour from `TEXT_TONE`.
|
|
4
|
+
*/
|
|
1
5
|
import type { ComponentPropsWithRef } from 'react';
|
|
2
6
|
import { type TextTone } from '../../design-system/component-tokens';
|
|
3
7
|
type IconButtonVariant = 'primary' | 'secondary' | 'ghost';
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Spinner` — a `role=status` ring in three sizes with a visually-hidden "Loading" label, so the
|
|
3
|
+
* state is announced without visible text.
|
|
4
|
+
*/
|
|
1
5
|
import type { CSSProperties, Ref } from 'react';
|
|
2
6
|
type SpinnerSize = 'sm' | 'md' | 'lg';
|
|
3
7
|
export interface SpinnerProps {
|
|
@@ -1,5 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* `Surface` is the canonical container primitive: a polymorphic `forwardRef` element (`as`, default
|
|
3
|
+
* `div`) that composes the shared layout-token vocabulary with a `background` token, a `SHADOW`
|
|
4
|
+
* `elevation` token and a `paddingPreset`, all emitted as inline style. `SurfaceBackground` and
|
|
5
|
+
* `SurfacePadding` are those token unions, `backgroundStyles` and `paddingPresetStyles` their lookup
|
|
6
|
+
* tables — the `default`/`none` entries are empty, so a bare `Surface` is a plain element — and
|
|
7
|
+
* `SurfaceProps` the prop surface: `LayoutProps` plus the host element's HTML attributes.
|
|
8
|
+
*
|
|
9
|
+
* `className` is dropped from those attributes and re-declared because it is INTERNAL to `blocks/`:
|
|
10
|
+
* layout props are the styling API everywhere else, and the only legitimate classes are the
|
|
11
|
+
* `index.css` hooks the block components key on.
|
|
12
|
+
*
|
|
13
|
+
* Style order is fixed and load-bearing: background → padding preset → elevation → layout props →
|
|
14
|
+
* the caller's own `style` last, so an inline style always wins. `Flex` depends on that tail
|
|
15
|
+
* position, resolving `display` itself because `resolveLayoutStyle` is applied ahead of it.
|
|
16
|
+
*
|
|
17
|
+
* `resolveLayoutStyle` is handed the whole `props` (it reads only layout keys), while the DOM spread
|
|
18
|
+
* goes through `stripLayoutProps` — a layout token left on the props bag reaches the element as an
|
|
19
|
+
* unknown attribute.
|
|
20
|
+
*/
|
|
21
|
+
import { type ElementType } from 'react';
|
|
22
|
+
import { type ShadowToken } from '../../design-system/component-tokens';
|
|
3
23
|
import { type LayoutProps } from './layoutProps';
|
|
4
24
|
export type SurfaceBackground = 'default' | 'card';
|
|
5
25
|
export type SurfacePadding = 'none' | 'card' | 'toast';
|
|
@@ -8,9 +28,6 @@ export interface SurfaceProps extends LayoutProps, Omit<React.HTMLAttributes<HTM
|
|
|
8
28
|
background?: SurfaceBackground;
|
|
9
29
|
elevation?: ShadowToken;
|
|
10
30
|
paddingPreset?: SurfacePadding;
|
|
11
|
-
/** @internal blocks/ only */
|
|
12
31
|
className?: string;
|
|
13
32
|
}
|
|
14
|
-
export declare const backgroundStyles: Record<SurfaceBackground, CSSProperties>;
|
|
15
|
-
export declare const paddingPresetStyles: Record<SurfacePadding, CSSProperties>;
|
|
16
33
|
export declare const Surface: import("react").ForwardRefExoticComponent<SurfaceProps & import("react").RefAttributes<HTMLElement>>;
|
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Text` — the widget's typography primitive; every rendered string goes through it.
|
|
3
|
+
*
|
|
4
|
+
* Contents: `TextProps` (prop surface, extending the rendered element's HTML attributes) · `Text`,
|
|
5
|
+
* which renders `as` (default `span`) and folds tone, size, weight, align, leading, block, italic
|
|
6
|
+
* and truncation into ONE inline style object · the `SIZE` / `WEIGHT` / `TRUNCATE` lookups it reads.
|
|
7
|
+
*
|
|
8
|
+
* Styling is inline only — the widget has no CSS framework and no `cn()`, so a variant is a style
|
|
9
|
+
* value here, never a class name. Tone and leading come from `design-system/component-tokens`, the
|
|
10
|
+
* shared home for those scales; the size, weight and truncate maps stay local because nothing but
|
|
11
|
+
* text reads them. Every optional prop is spread only when set, so an unset one inherits from the
|
|
12
|
+
* cascade rather than being pinned to a default, and the caller's `style` spreads LAST so it wins
|
|
13
|
+
* over every resolved token. `inheritColor` overrides `variant` and resolves to `TEXT_TONE.inherit`,
|
|
14
|
+
* letting text inside an already-coloured container (button, badge) take that colour.
|
|
15
|
+
*
|
|
16
|
+
* `className` is an escape hatch for `blocks/` only — product code styles through the props above.
|
|
17
|
+
*/
|
|
1
18
|
import type { ElementType, Ref } from 'react';
|
|
2
19
|
import { type TextLeading } from '../../design-system/component-tokens';
|
|
3
20
|
type TextVariant = 'default' | 'muted' | 'faint';
|
|
@@ -15,7 +32,6 @@ export interface TextProps extends React.HTMLAttributes<HTMLElement> {
|
|
|
15
32
|
weight?: TextWeight;
|
|
16
33
|
truncate?: boolean;
|
|
17
34
|
align?: TextAlign;
|
|
18
|
-
/** @internal blocks/ only */
|
|
19
35
|
className?: string;
|
|
20
36
|
ref?: Ref<HTMLElement>;
|
|
21
37
|
}
|
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Icon registry: the SVG path data behind every `<Icon name=... />`, plus the shapes Icon.tsx renders.
|
|
3
|
+
*
|
|
4
|
+
* Contents — `IconPath` (one `<path>`'s attributes; Icon.tsx forwards them field by field, so an attribute added
|
|
5
|
+
* here stays inert until it is forwarded there too); `IconData` (an icon's own `viewBox` plus its paths — entries
|
|
6
|
+
* come from source sets drawn on different grids and are never renormalized, so the viewBox travels per icon);
|
|
7
|
+
* `stroked()` (builds an outline path in `currentColor` with round caps and joins, strokeWidth 2 unless
|
|
8
|
+
* overridden); `icons` (the registry itself); `IconName` (its keys).
|
|
9
|
+
*
|
|
10
|
+
* Icon.tsx resolves an absent `fill` to 'none' when a stroke is set and to 'currentColor' otherwise, so
|
|
11
|
+
* a stroked path omits `fill` and a solid path omits both `fill` and `stroke`.
|
|
12
|
+
*
|
|
13
|
+
* `as const satisfies Record<string, IconData>` is load-bearing twice over: `as const` keeps `IconName` a
|
|
14
|
+
* literal union of the keys, and `satisfies` fails a malformed entry at compile time without widening the
|
|
15
|
+
* value type. The bundle is a single chunk, so every entry here is unconditional weight in each host page.
|
|
16
|
+
*/
|
|
1
17
|
export interface IconPath {
|
|
2
18
|
d: string;
|
|
3
19
|
fill?: string;
|
|
@@ -1,3 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Layout vocabulary shared by the base components: the token props `LayoutProps` accepts,
|
|
3
|
+
* `resolveLayoutStyle` which reduces them to a `CSSProperties` object, and `stripLayoutProps` which
|
|
4
|
+
* removes them from a props bag so the remainder can be spread onto a DOM element. `SPACING_SCALE` is
|
|
5
|
+
* the exported `SpacingToken`→pixel table, declared smallest-first so a token name orders the same way
|
|
6
|
+
* as the pixels it emits (`__tests__/layoutProps.test.ts` pins that); `ALIGN`, `JUSTIFY`, `ANIMATION`
|
|
7
|
+
* and `BORDER_SIDE` are the private lookups for the remaining token families.
|
|
8
|
+
*
|
|
9
|
+
* Layout props resolve to a style object rather than class names: as classes they were interpolated
|
|
10
|
+
* (`p-${token}`), which no scanner could see, so a build-time safelist emitting the whole 8x7 matrix
|
|
11
|
+
* was the only thing keeping them alive and a missing entry failed silently at runtime.
|
|
12
|
+
* `resolveLayoutStyle` emits a property only for a prop set to a non-default value — `grow: false`,
|
|
13
|
+
* `shrink: true`, `border: false`, `rounded: false` and `animate: 'none'` deliberately emit nothing —
|
|
14
|
+
* and its `ANIMATION` values name `mtx-*` keyframes `index.css` must define
|
|
15
|
+
* (`__tests__/stylesheet-contract.test.ts` pins the pairing).
|
|
16
|
+
*
|
|
17
|
+
* `LAYOUT_KEYS` must list every key of `LayoutProps`: `stripLayoutProps` filters by that set, so a
|
|
18
|
+
* layout prop missing from it reaches the DOM as an unknown attribute. `as` and `style` are in it
|
|
19
|
+
* because the consuming component (`Surface`) applies them itself rather than forwarding them.
|
|
20
|
+
*/
|
|
1
21
|
import type { CSSProperties, ElementType } from 'react';
|
|
2
22
|
import { type RadiusToken } from '../../design-system/component-tokens';
|
|
3
23
|
export type SpacingToken = 'none' | '2xs' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';
|
|
@@ -47,12 +67,6 @@ export interface LayoutProps {
|
|
|
47
67
|
as?: ElementType;
|
|
48
68
|
style?: CSSProperties;
|
|
49
69
|
}
|
|
50
|
-
/**
|
|
51
|
-
* Layout props resolve to a style object rather than class names. As classes they were interpolated
|
|
52
|
-
* (`p-${token}`), which no scanner could see — a build-time safelist was the only thing keeping them
|
|
53
|
-
* alive, it emitted the whole 8x7 matrix whether used or not, and a missing entry failed silently at
|
|
54
|
-
* runtime. A style object makes every token work by construction and costs nothing per unused one.
|
|
55
|
-
*/
|
|
56
70
|
export declare function resolveLayoutStyle(props: LayoutProps): CSSProperties;
|
|
57
71
|
export declare function stripLayoutProps<T extends LayoutProps>(props: T): Omit<T, keyof LayoutProps>;
|
|
58
72
|
export {};
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The widget's chat input: an auto-growing textarea with send and stop.
|
|
3
|
+
*
|
|
4
|
+
* `MAX_TEXTAREA_HEIGHT` caps growth at three lines — 20px each plus 6px of padding — after which it
|
|
5
|
+
* scrolls, so a long message cannot push the conversation off a small host page.
|
|
6
|
+
*
|
|
7
|
+
* `mergeRefs` combines the caller's ref with the internal one, since the component needs its own
|
|
8
|
+
* handle to measure and resize.
|
|
9
|
+
*/
|
|
1
10
|
import React from 'react';
|
|
2
11
|
import type { InstructionType } from '../../types';
|
|
3
12
|
import type { IconName } from '../base/icons';
|
|
@@ -1,17 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The widget's one notification surface: a Base UI Toast provider, the toast renderer, and the effect
|
|
3
|
+
* that drives toasts from widget state. Base UI owns the live region, the dismiss timers, hover-to-pause
|
|
4
|
+
* and stacking; before this the widget announced nothing to a screen reader and ran its own setTimeout.
|
|
5
|
+
*
|
|
6
|
+
* `NotificationList` renders every live toast and stays a component of its own because `useToastManager`
|
|
7
|
+
* only resolves inside `Toast.Provider`; both text lines truncate through `Text`'s own prop, a toast
|
|
8
|
+
* carrying an action lets its title wrap instead, and a toast's `type` is a free string in Base UI, so it
|
|
9
|
+
* is narrowed inline to the three tones `notificationToneStyles` understands, anything else falling back
|
|
10
|
+
* to `neutral`. `NotificationProvider` wraps the provider, portal and viewport — `container` is the
|
|
11
|
+
* widget's CLOSED shadow root, since portalling to `document.body` instead would leave the injected
|
|
12
|
+
* styles behind, and `offsetBottom` raises the viewport above the launcher when the launcher also sits
|
|
13
|
+
* at the bottom, so the two cannot overlap. `useNotifications` re-exports Base UI's toast manager as the
|
|
14
|
+
* one door for adding and closing toasts.
|
|
15
|
+
*
|
|
16
|
+
* `WidgetNotifications` renders nothing; it mirrors the `error` and `greeting` props into toasts and
|
|
17
|
+
* closes them when the prop clears. Both use a STABLE id, so `add` upserts and a re-render cannot stack
|
|
18
|
+
* duplicates of the same condition. The error toast carries `timeout: 0` — it stays until acted on
|
|
19
|
+
* (dismissed or retried); only the greeting is transient.
|
|
20
|
+
*/
|
|
1
21
|
import { Toast } from '@base-ui/react/toast';
|
|
2
22
|
import React from 'react';
|
|
3
23
|
export declare const GREETING_TIMEOUT_MS = 8000;
|
|
4
24
|
export interface NotificationProviderProps {
|
|
5
25
|
children?: React.ReactNode;
|
|
6
|
-
/** The closed shadow root to portal into — a portal to document.body would leave the styles behind. */
|
|
7
26
|
container?: HTMLElement | null;
|
|
8
|
-
/** Raised above the launcher when the launcher sits at the bottom, so the two cannot overlap. */
|
|
9
27
|
offsetBottom?: number;
|
|
10
28
|
}
|
|
11
|
-
/**
|
|
12
|
-
* The one notification surface. Base UI owns the live region, the dismiss timers, hover-to-pause and
|
|
13
|
-
* stacking; before this the widget announced nothing to a screen reader and ran its own setTimeout.
|
|
14
|
-
*/
|
|
15
29
|
export declare const NotificationProvider: React.FC<NotificationProviderProps>;
|
|
16
30
|
export declare const useNotifications: typeof Toast.useToastManager;
|
|
17
31
|
export interface WidgetNotificationsProps {
|
|
@@ -22,8 +36,4 @@ export interface WidgetNotificationsProps {
|
|
|
22
36
|
greetingBody?: string;
|
|
23
37
|
onGreetingDismiss: () => void;
|
|
24
38
|
}
|
|
25
|
-
/**
|
|
26
|
-
* Drives the toasts from widget state. Both use a stable id, so `add` upserts and a re-render cannot
|
|
27
|
-
* stack duplicates of the same condition.
|
|
28
|
-
*/
|
|
29
39
|
export declare const WidgetNotifications: React.FC<WidgetNotificationsProps>;
|
|
@@ -7,11 +7,6 @@ export interface WidgetDialogProps {
|
|
|
7
7
|
onConfirm?: () => void;
|
|
8
8
|
confirmLabel?: string;
|
|
9
9
|
cancelLabel?: string;
|
|
10
|
-
/**
|
|
11
|
-
* Where focus lands on close. Base UI's default restore is broken here: it descends
|
|
12
|
-
* `element.shadowRoot.activeElement`, which is null for a CLOSED root, so it records the shadow
|
|
13
|
-
* HOST and hands focus to a host-page element on close. Naming the target sidesteps that.
|
|
14
|
-
*/
|
|
15
10
|
finalFocusRef?: React.RefObject<HTMLElement | null>;
|
|
16
11
|
}
|
|
17
12
|
export declare const WidgetDialog: React.FC<WidgetDialogProps>;
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `WidgetFab` — the launcher button: draggable via `useDragSnap`, positioned at the configured corner,
|
|
3
|
+
* glowing while a reply or task is in flight, and showing the stop control while a task runs and the
|
|
4
|
+
* panel is closed. Colours and z-index come from the tenant config; `pointerEvents: none` while open so
|
|
5
|
+
* the panel beneath receives the clicks.
|
|
6
|
+
*
|
|
7
|
+
* Glow and activity ring are ONE class each, red or green keyed on the `data-tone` the error state
|
|
8
|
+
* picks — the same data-attribute variant convention every other component here uses. The two icon
|
|
9
|
+
* layers carry only their own transform and opacity; the transition they share is `.mtx-fab-icon-layer`.
|
|
10
|
+
*/
|
|
1
11
|
import React from 'react';
|
|
2
12
|
import type { WidgetPosition } from '../../types';
|
|
3
13
|
interface WidgetFabProps {
|
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One transcript row and everything drawn inside it. `MessageItem` renders a system message centred and
|
|
3
|
+
* faint, or a user/agent row with a leading glyph (a pointer for show/do requests, a check for a
|
|
4
|
+
* screen-access request or a waiting-for-user placeholder), the sender label as an aria-label, the body,
|
|
5
|
+
* the allow/deny controls of a screen-access request, and a done/failed/stopped glyph in the tenant
|
|
6
|
+
* accent at a per-status opacity. The last message fades in.
|
|
7
|
+
*
|
|
8
|
+
* `MessageBody` renders the message's `parts` — text and progress lines — and `Thinking` is the
|
|
9
|
+
* spinner-and-caption row shown while a reply is pending, its caption switching to name the visitor's
|
|
10
|
+
* action when the agent is blocked on them. Thinking shows while a placeholder carries no text yet, and
|
|
11
|
+
* also while the task is still running on the LAST show/do message, where text has already arrived but
|
|
12
|
+
* more work is coming; a message with no parts at all falls back to a bare `Surface`.
|
|
13
|
+
*/
|
|
1
14
|
import React from 'react';
|
|
2
15
|
import type { ChatMessage } from '../../types';
|
|
3
16
|
interface MessageItemProps {
|
|
@@ -1,3 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The chat transcript pane: a scrolling `role='log'` list of `MessageItem`s with the "Clear conversation"
|
|
3
|
+
* action beneath it and two floating scroll affordances layered over it.
|
|
4
|
+
*
|
|
5
|
+
* `scrollButtonStyle` is the card-on-surface look shared by both affordances. `MessageListProps` carries
|
|
6
|
+
* the end-of-list anchor ref owned by `ChatView` plus the screen-access answers, forwarded only to
|
|
7
|
+
* `MessageItem`. `MessageList` prepends a greeting message built from `widget_body` through the shared
|
|
8
|
+
* `createAgentMessage`, the one home for a `ChatMessage`; it never enters the store, which is why
|
|
9
|
+
* "Clear conversation" is gated on `messages.length` rather than on the rendered list. `handleScroll` derives both affordances from container geometry — top once scrolled past
|
|
10
|
+
* 200px, bottom while the list overflows and sits more than 50px off the end. The transcript paints
|
|
11
|
+
* `widget_background_color` through `backgroundGradient`, the one home for that expansion, zeroing
|
|
12
|
+
* `backgroundColor` for a gradient setting so the two declarations cannot fight.
|
|
13
|
+
*
|
|
14
|
+
* Every scroll is suppressed in preview mode: there the widget is embedded in the dashboard's modal, and
|
|
15
|
+
* `scrollIntoView` would scroll that parent modal rather than this list. The scroll on a new message waits
|
|
16
|
+
* a `requestAnimationFrame` so layout has settled before it fires. A streaming reply arrives as
|
|
17
|
+
* `chat/delta` fragments that grow the last message in place without changing the message count, so a
|
|
18
|
+
* second effect keys on that message's content length and re-pins to the bottom only while the reader is
|
|
19
|
+
* already within 120px of it — a reader who scrolled away is left where they are.
|
|
20
|
+
*/
|
|
1
21
|
import React from 'react';
|
|
2
22
|
interface MessageListProps {
|
|
3
23
|
messagesEndRef: React.RefObject<HTMLDivElement | null>;
|
|
@@ -1,3 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inline screen-share viewport for a chat message: a live MediaStream as a muted, auto-playing <video>
|
|
3
|
+
* under a loading overlay, a failure overlay, a "Live" pill and a persistent banner. `Overlay` is the
|
|
4
|
+
* centred full-bleed scrim carrying a spinner or icon above a caption, shared by the loading and failure
|
|
5
|
+
* states; `VideoStreamDisplay` binds the stream, tracks loaded/failed and layers the rest over it.
|
|
6
|
+
*
|
|
7
|
+
* The bind effect is keyed on `stream`: a replacement clears both flags and rebinds the
|
|
8
|
+
* `loadedmetadata`/`error` listeners, and cleanup nulls `srcObject` so a stopped stream is not retained.
|
|
9
|
+
* `play()` rejecting with AbortError is exactly that replacement racing the previous play — the one
|
|
10
|
+
* benign rejection; anything else is logged with its error and surfaced as the failure overlay.
|
|
11
|
+
* `muted` + `autoPlay` is what makes autoplay legal without a user gesture, and `playsInline` stops iOS
|
|
12
|
+
* Safari taking the stream fullscreen over the host page. The video stays mounted at opacity 0 while
|
|
13
|
+
* loading, since unmounting it leaves nothing for `loadedmetadata` to fire on.
|
|
14
|
+
*
|
|
15
|
+
* Corners are rounded on top only because `MessageItem` collapses the bubble padding to 0 for a video
|
|
16
|
+
* message, so this sits flush in the bubble's top corners. Stacking is deliberate — overlays 10, Live
|
|
17
|
+
* pill 20, banner 30 — and the banner is `pointerEvents: 'none'` so its full-bleed wrapper never
|
|
18
|
+
* swallows clicks meant for the message. The pulsing dot in the Live pill is the shared `mtx-live-dot`,
|
|
19
|
+
* the same one the header's screen-share button wears; it paints in `currentColor`.
|
|
20
|
+
*/
|
|
1
21
|
import React from 'react';
|
|
2
22
|
interface VideoStreamDisplayProps {
|
|
3
23
|
stream: MediaStream;
|
|
@@ -1,3 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The messenger panel's chat view: the scrolling transcript, the composer, and the screen-access
|
|
3
|
+
* dialog that gates Show/Do.
|
|
4
|
+
*
|
|
5
|
+
* `ChatViewProps` are the shell's three wires — a screen-sharing flag lifted to the header, a ref the
|
|
6
|
+
* shell's toolbar button fills with a share toggle, and the composer textarea ref. `MODES` pairs the
|
|
7
|
+
* mode chips' display order with the tenant setting enabling each, so the composer offers only what
|
|
8
|
+
* the workspace turned on. `ChatView` owns the draft text; `handleSendMessage` posts the turn and
|
|
9
|
+
* `handleModeChange` announces a mode switch in the transcript before flipping state.
|
|
10
|
+
*
|
|
11
|
+
* The composer is locked both while a reply is outstanding and while a screen-access request is open,
|
|
12
|
+
* since a second turn queued behind an unanswered permission card has nowhere to land. `use_screenshare`
|
|
13
|
+
* absent means enabled; only an explicit `false` skips the ask — Show and Do request screen access first
|
|
14
|
+
* unless a share is already live, and `useScreenShare` flushes the held turn on every outcome. This view
|
|
15
|
+
* writes the user's bubble itself, so every dispatch from here is `skipUserMessage`. Stop tears down
|
|
16
|
+
* `showModeService` before `stopTask`, since an in-flight highlight owns listeners, a watchdog and
|
|
17
|
+
* injected nodes that outlive the task otherwise. `WidgetDialog` gets an explicit `finalFocusRef`
|
|
18
|
+
* since Base UI's focus restore resolves to the host page inside a closed shadow root otherwise. The
|
|
19
|
+
* transcript sits under its own `ErrorBoundary` so one unrenderable message can't take the composer down.
|
|
20
|
+
*/
|
|
1
21
|
import React from 'react';
|
|
2
22
|
interface ChatViewProps {
|
|
3
23
|
onScreenSharingChange: (isSharing: boolean) => void;
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `HomeView` — the opening screen: tenant greeting and body, the Ask-a-question button that switches to
|
|
3
|
+
* the chat view, the suggested-action chips (`getSuggestedActionsFromConfig`), which navigate and dispatch
|
|
4
|
+
* in one click, and a card linking back into a conversation already in progress.
|
|
5
|
+
*
|
|
6
|
+
* Chip captions render VERBATIM in the tenant text colour: a `show`/`do` caption doubles as the
|
|
7
|
+
* instruction dispatched on click and is given its mode prefix in the config layer, so prefixing here
|
|
8
|
+
* would double it.
|
|
9
|
+
*/
|
|
1
10
|
import React from 'react';
|
|
2
11
|
import { type SuggestedActionItem } from '../../utils/suggestedActions';
|
|
3
12
|
interface HomeViewProps {
|
|
@@ -1,3 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React context owning the widget's chat store and its live SSE wiring: one committed `{messages, task}`
|
|
3
|
+
* state and the `ChatActions`/`TaskActions` mutators reached through `useChatContext`, which throws
|
|
4
|
+
* outside `ChatProvider`. Messages and task share ONE object so they cannot tear across an await.
|
|
5
|
+
*
|
|
6
|
+
* `commit` is the ONLY writer of the `stateRef` mirror — re-syncing from render could regress it between a
|
|
7
|
+
* commit and its paint — and runs the transition synchronously, not inside a `setState` updater: React
|
|
8
|
+
* defers updaters and loses the effects captured in them, so tool calls never execute. `messageDispatch`
|
|
9
|
+
* POSTs fire-and-forget: the reply arrives over SSE, so only a POST failure resolves the placeholder
|
|
10
|
+
* locally, and a stale-reply watchdog keyed on id AND part count re-arms on every progress line.
|
|
11
|
+
*
|
|
12
|
+
* The stream effect subscribes to the `StreamClient` singleton: `handleMessage` does the bookkeeping the
|
|
13
|
+
* pure reducer cannot hold (`tool_call_id` dedupe in a bounded set, cleared on a terminal status), then
|
|
14
|
+
* each effect runs its browser tool, stamps progress, replies `tool/response` and only then fires
|
|
15
|
+
* `afterResponseAttempt`. `handleError` converts only a `StreamGaveUpError` into a transport failure, as a
|
|
16
|
+
* retriable blip settles when the reply lands on the reconnected stream. `stopTask` sends `chat/stop`,
|
|
17
|
+
* which carries no task id. Every failure on the tool and stop paths reaches `uiActions.setError` as well
|
|
18
|
+
* as the console: an undelivered `tool/response` leaves the agent waiting on a reply that never comes, so
|
|
19
|
+
* the run stalls with nothing on screen unless the visitor is told, and `do` mode may still be clicking.
|
|
20
|
+
*/
|
|
1
21
|
import React from 'react';
|
|
2
22
|
import type { ChatMessage, InstructionType } from '../types';
|
|
3
23
|
import { type TaskState } from './sseReducer';
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `UIStateProvider` / `useUIStateContext` — the widget's view state: open/closed, active view, current
|
|
3
|
+
* mode (tell/show/do) and the error banner. Actions are stable (memoised once) so consumers can depend
|
|
4
|
+
* on them without re-rendering; `applyState` merges a partial for restore-from-storage.
|
|
5
|
+
*/
|
|
1
6
|
import React from 'react';
|
|
2
7
|
import type { InstructionType, WidgetView } from '../types';
|
|
3
8
|
export interface UIState {
|
|
@@ -1,5 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The widget's provider stack and the two bridges under it: `WidgetProviders` wraps children in
|
|
3
|
+
* `UIStateProvider` → `ChatProvider` → `InitBridge`, so everything below reads UI state and the chat
|
|
4
|
+
* store from context. Also home to `PortalContainerContext`/`usePortalContainer` — `WidgetRoot`
|
|
5
|
+
* publishes its own root here, since a portal landing outside it escapes the element carrying the
|
|
6
|
+
* tenant tokens and falls back to `index.css`'s hardcoded palette; the default is `document.body`.
|
|
7
|
+
*
|
|
8
|
+
* `InitBridge` runs the one-shot mount init: read the stored snapshot, apply `{currentMode, isOpen}`
|
|
9
|
+
* to UI state and the transcript to the chat store, then get-or-create the chat_id and open the stream.
|
|
10
|
+
* Task state is deliberately not restored — a run never survives a reload. `PersistBridge` writes the
|
|
11
|
+
* snapshot back on every message or UI-state change; it is its own component so subscribing to every
|
|
12
|
+
* message change can't re-render the tree `InitBridge` wraps, and mounts only once `restored` is true —
|
|
13
|
+
* mounted before the read, its first effect would overwrite the stored transcript on every remount.
|
|
14
|
+
* `previewMode` skips init entirely: no chat session is minted, no stream opened, nothing persisted.
|
|
15
|
+
*
|
|
16
|
+
* The init effect's deps are empty on purpose — once per mount — and `cancelled` drops the connect when
|
|
17
|
+
* a StrictMode double-invoke or an unmount cleans up before the chat_id resolves. A failed connect is
|
|
18
|
+
* logged only, since `StreamClient` owns the backoff reconnect; a failed init is logged AND surfaced
|
|
19
|
+
* through `uiActions.setError`.
|
|
20
|
+
*/
|
|
1
21
|
import React from 'react';
|
|
2
|
-
/** WidgetRoot publishes its own root here: a portal outside it escapes the element carrying the tenant tokens. */
|
|
3
22
|
export declare const PortalContainerContext: React.Context<HTMLElement | null>;
|
|
4
23
|
export declare const usePortalContainer: () => HTMLElement;
|
|
5
24
|
interface WidgetProvidersProps {
|