@qualified-salesforce/web 0.0.1-oidc-bootstrap → 0.1.1
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/CAPABILITY-REFERENCE.md +41 -0
- package/CHANGELOG.md +84 -0
- package/README.md +65 -0
- package/constants.generated.js +30 -2
- package/css/widget/multimodal_v2/index.split-css.panda.css +265 -47
- package/package.json +18 -4
- package/packs/widget/api/docking/viewport_lock.js +93 -2
- package/packs/widget/esm/index.d.ts +134 -0
- package/packs/widget/esm/index.js +18 -2
- package/packs/widget/esm/qualified.css +265 -47
- package/src/graphql-types.js +74 -1
- package/src/url.js +4 -1
- package/vendor/actioncable/connection.js +40 -3
- package/vendor/actioncable/connection_monitor.js +8 -0
- package/vendor/actioncable/consumer.js +7 -1
- package/vendor/actioncable/index.js +5 -2
- package/vendor/actioncable/sse_socket.js +262 -0
- package/widget/analytics.js +8 -0
- package/widget/api_handler/base_api_handler.js +24 -0
- package/widget/booker_outcome_tracker.js +58 -20
- package/widget/capabilities.js +190 -0
- package/widget/channel.js +38 -6
- package/widget/constants.generated.js +2 -1
- package/widget/multimodal_v2/components/AppLayout.js +1 -1
- package/widget/multimodal_v2/components/app/Foreground.js +10 -9
- package/widget/multimodal_v2/components/app/Input.js +8 -3
- package/widget/multimodal_v2/components/app/LightboxExperience.js +17 -3
- package/widget/multimodal_v2/components/app/Main.js +87 -31
- package/widget/multimodal_v2/components/app/MessengerButton.js +18 -1
- package/widget/multimodal_v2/components/chat/ChatInputForm.js +8 -6
- package/widget/multimodal_v2/components/chat/timeline/CalendarEventPickRenderer.js +2 -2
- package/widget/multimodal_v2/components/chat/timeline/PresentDeckRenderer.js +346 -170
- package/widget/multimodal_v2/components/chat/timeline/SystemRenderer.js +4 -7
- package/widget/multimodal_v2/components/decks/index.js +506 -52
- package/widget/multimodal_v2/components/helpers/content_click_payloads.js +67 -1
- package/widget/multimodal_v2/components/lightbox_gather/index.js +4 -4
- package/widget/multimodal_v2/components/meetings/MeetingBookerEntry.js +8 -1
- package/widget/multimodal_v2/components/meetings/MeetingOfferEmailStep.js +1 -1
- package/widget/multimodal_v2/components/meetings/MeetingSummaryRail.js +1 -1
- package/widget/multimodal_v2/components/meetings/MeetingTimeSelector.js +1 -1
- package/widget/multimodal_v2/components/multimodal/Controls.js +11 -3
- package/widget/multimodal_v2/components/multimodal/useMultimodal.js +19 -7
- package/widget/multimodal_v2/components/presentations/index.js +61 -0
- package/widget/multimodal_v2/constants.js +1 -1
- package/widget/multimodal_v2/images/icons.js +17 -1
- package/widget/multimodal_v2/index.panda.js +2 -2
- package/widget/multimodal_v2/locales/cs.js +8 -1
- package/widget/multimodal_v2/locales/da.js +8 -1
- package/widget/multimodal_v2/locales/de.js +8 -1
- package/widget/multimodal_v2/locales/en.js +8 -1
- package/widget/multimodal_v2/locales/en_AU.js +8 -1
- package/widget/multimodal_v2/locales/en_GB.js +8 -1
- package/widget/multimodal_v2/locales/es.js +8 -1
- package/widget/multimodal_v2/locales/es_LA.js +8 -1
- package/widget/multimodal_v2/locales/fi.js +8 -1
- package/widget/multimodal_v2/locales/fr.js +8 -1
- package/widget/multimodal_v2/locales/fr_CA.js +8 -1
- package/widget/multimodal_v2/locales/id.js +8 -1
- package/widget/multimodal_v2/locales/it.js +8 -1
- package/widget/multimodal_v2/locales/ja.js +8 -1
- package/widget/multimodal_v2/locales/ko.js +8 -1
- package/widget/multimodal_v2/locales/nb.js +8 -1
- package/widget/multimodal_v2/locales/nl.js +8 -1
- package/widget/multimodal_v2/locales/pl.js +8 -1
- package/widget/multimodal_v2/locales/pt_BR.js +8 -1
- package/widget/multimodal_v2/locales/ru.js +8 -1
- package/widget/multimodal_v2/locales/sv.js +8 -1
- package/widget/multimodal_v2/locales/th.js +8 -1
- package/widget/multimodal_v2/locales/tr.js +8 -1
- package/widget/multimodal_v2/locales/vi.js +8 -1
- package/widget/multimodal_v2/locales/zh_CN.js +8 -1
- package/widget/multimodal_v2/locales/zh_TW.js +8 -1
- package/widget/multimodal_v2/multimodal.js +5 -1
- package/widget/multimodal_v2/multimodal_channel.js +13 -8
- package/widget/multimodal_v2/signals/messages_signal.js +4 -0
- package/widget/multimodal_v2/stores/ai_conversation_store.js +6 -2
- package/widget/multimodal_v2/stores/artifact_store.js +352 -27
- package/widget/multimodal_v2/stores/call_store.js +40 -1
- package/widget/multimodal_v2/stores/call_ui_adapter.js +1 -1
- package/widget/multimodal_v2/stores/decks_store.js +405 -39
- package/widget/multimodal_v2/stores/meeting_store.js +15 -10
- package/widget/multimodal_v2/stores/presentation_ga_adapter.js +64 -0
- package/widget/multimodal_v2/stores/presentation_telemetry_store.js +556 -0
- package/widget/multimodal_v2/stores/presentations_store.js +1 -0
- package/widget/multimodal_v2/stores/root_store.js +157 -268
- package/widget/multimodal_v2/stores/screen_share_store.js +41 -21
- package/widget/multimodal_v2/stores/translation_store.js +9 -0
- package/widget/multimodal_v2/stores/ui_store.js +7 -7
- package/widget/multimodal_v2/stores/website_navigation_store.js +3 -1
- package/widget/multimodal_v2/text_turn_response_tracker.js +419 -0
- package/widget/multimodal_v2/types.js +21 -1
- package/widget/multimodal_v2/utils/locale.js +2 -1
- package/widget/multimodal_v2/utils/signals.js +35 -4
- package/widget/pardot_visitor.js +7 -1
- package/widget/server-logger.js +97 -2
- package/widget/tracker.js +69 -21
- package/widget/widget/page_mirror_factory.js +9 -1
- package/widget/widget/widget.js +29 -4
|
@@ -1,12 +1,25 @@
|
|
|
1
|
-
import { jsxRuntime_module_u, useComputed } from "../../../../../js/multimodal_v2.js";
|
|
2
|
-
import {
|
|
1
|
+
import { jsxRuntime_module_u, preact_module_k, useComputed } from "../../../../../js/multimodal_v2.js";
|
|
2
|
+
import { SlidesIcon } from "../../../images/icons.js";
|
|
3
3
|
import "../../../styled-system/jsx/index.js";
|
|
4
|
-
import { RichText } from "../../helpers/RichText.js";
|
|
5
4
|
import { Button } from "../../shared/Button.js";
|
|
6
5
|
import { MessageEntry } from "./MessageEntry.js";
|
|
7
6
|
import { MaximizedState as types_MaximizedState, StartCallTrigger as types_StartCallTrigger } from "../../../types.js";
|
|
8
7
|
import { styled } from "../../../styled-system/jsx/factory.js";
|
|
9
8
|
|
|
9
|
+
function _extends() {
|
|
10
|
+
_extends = Object.assign || function(target) {
|
|
11
|
+
for(var i = 1; i < arguments.length; i++){
|
|
12
|
+
var source = arguments[i];
|
|
13
|
+
for(var key in source){
|
|
14
|
+
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
15
|
+
target[key] = source[key];
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return target;
|
|
20
|
+
};
|
|
21
|
+
return _extends.apply(this, arguments);
|
|
22
|
+
}
|
|
10
23
|
|
|
11
24
|
/** @jsxImportSource preact */
|
|
12
25
|
|
|
@@ -14,246 +27,388 @@ import { styled } from "../../../styled-system/jsx/factory.js";
|
|
|
14
27
|
|
|
15
28
|
|
|
16
29
|
|
|
17
|
-
|
|
18
30
|
/**
|
|
19
|
-
* Renders a `present_deck` message in the timeline
|
|
31
|
+
* Renders a `present_deck` message in the timeline as one card, whatever has
|
|
32
|
+
* happened to the deck. Activating it either opens the slides for the visitor to
|
|
33
|
+
* step through themselves or starts a call for Piper to present them — see
|
|
34
|
+
* `usePresentDeckCard` for which.
|
|
20
35
|
*
|
|
21
|
-
* -
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* transcript, which would otherwise arrive as a single wall of text (the raw
|
|
25
|
-
* transcript is folded into this message server-side and never shown).
|
|
26
|
-
* - Off-call: a "join call" prompt whose button starts the presentation.
|
|
27
|
-
*
|
|
28
|
-
* The mode is a stable property of the message (talk tracks are only ever
|
|
29
|
-
* populated for on-call presentations), so this dispatcher never calls hooks —
|
|
30
|
-
* each sub-component owns its own.
|
|
36
|
+
* Navigation-only messages control the overlay without adding a card of their
|
|
37
|
+
* own, so they render nothing. Keeping that guard here leaves the card free to
|
|
38
|
+
* use hooks.
|
|
31
39
|
*/ const PresentDeckRenderer = (param)=>{
|
|
32
40
|
let { item, context } = param;
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
if (presentedSlides.length > 0) {
|
|
36
|
-
return /*#__PURE__*/ jsxRuntime_module_u(PresentDeckTranscript, {
|
|
37
|
-
item: item,
|
|
38
|
-
context: context
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
return /*#__PURE__*/ jsxRuntime_module_u(PresentDeckJoinCard, {
|
|
41
|
+
if (item.navigationOnly) return null;
|
|
42
|
+
return /*#__PURE__*/ jsxRuntime_module_u(PresentDeckCard, {
|
|
42
43
|
item: item,
|
|
43
44
|
context: context
|
|
44
45
|
});
|
|
45
46
|
};
|
|
46
|
-
|
|
47
|
-
* The presentation record: each slide's thumbnail followed by its talk track.
|
|
48
|
-
*
|
|
49
|
-
* A presentation can span several `present_deck` messages — the initial share
|
|
50
|
-
* plus any resume after an interruption, where the agent re-shares only the
|
|
51
|
-
* remaining slides (e.g. interrupted on slide 3 of 5 → resume carries just 3-5).
|
|
52
|
-
* Rendering each message on its own would show a fragment that appears to start
|
|
53
|
-
* mid-deck. Instead we render ONE consolidated record on the LAST message for the
|
|
54
|
-
* deck — the union of every slide shown across all its shares, each once, in
|
|
55
|
-
* slide order — and nothing on the earlier ones. When a slide was shown more than
|
|
56
|
-
* once (a resume re-covers the slide it picked up on), the latest share wins, so
|
|
57
|
-
* the talk track reflects what was actually narrated on the resumed run.
|
|
58
|
-
*/ function PresentDeckTranscript(param) {
|
|
59
|
-
let { item, context } = param;
|
|
60
|
-
const record = useComputed(()=>{
|
|
61
|
-
var _forDeck_;
|
|
62
|
-
// All shares for this deck, oldest first. Include `item` itself in case it
|
|
63
|
-
// isn't in the message list yet (optimistic render / tests).
|
|
64
|
-
const forDeck = context.messages.value.filter((m)=>m.type === "present_deck" && m.deckId === item.deckId);
|
|
65
|
-
if (!forDeck.some((m)=>m.id === item.id)) forDeck.push(item);
|
|
66
|
-
forDeck.sort((a, b)=>new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
|
|
67
|
-
// Only the last share renders the consolidated record.
|
|
68
|
-
if (((_forDeck_ = forDeck[forDeck.length - 1]) == null ? void 0 : _forDeck_.id) !== item.id) return [];
|
|
69
|
-
const bySlideIndex = new Map();
|
|
70
|
-
for (const message of forDeck){
|
|
71
|
-
var _message_slides;
|
|
72
|
-
for (const slide of (_message_slides = message.slides) != null ? _message_slides : []){
|
|
73
|
-
if (slide.talkTrack) {
|
|
74
|
-
bySlideIndex.set(slide.slideIndex, slide);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
return [
|
|
79
|
-
...bySlideIndex.values()
|
|
80
|
-
].sort((a, b)=>a.slideIndex - b.slideIndex);
|
|
81
|
-
});
|
|
82
|
-
if (record.value.length === 0) return null;
|
|
83
|
-
return /*#__PURE__*/ jsxRuntime_module_u(MessageEntry, {
|
|
84
|
-
isOwn: false,
|
|
85
|
-
children: /*#__PURE__*/ jsxRuntime_module_u(Transcript, {
|
|
86
|
-
"data-component-name": "PresentDeckRenderer.Transcript",
|
|
87
|
-
children: record.value.map((slide)=>{
|
|
88
|
-
var _ref, _slide_pageTitle;
|
|
89
|
-
return /*#__PURE__*/ jsxRuntime_module_u(SlideBlock, {
|
|
90
|
-
children: [
|
|
91
|
-
/*#__PURE__*/ jsxRuntime_module_u(SlidePreview, {
|
|
92
|
-
type: "button",
|
|
93
|
-
"aria-label": context.translationStore.t("slides.viewSlide", {
|
|
94
|
-
id: slide.slideIndex + 1
|
|
95
|
-
}),
|
|
96
|
-
onClick: ()=>context.revisitDeckSlide(item, slide.slideIndex, slide.pageTitle),
|
|
97
|
-
children: /*#__PURE__*/ jsxRuntime_module_u(SlidePreviewImage, {
|
|
98
|
-
crossOrigin: "anonymous",
|
|
99
|
-
src: slide.imageUrl,
|
|
100
|
-
alt: (_ref = (_slide_pageTitle = slide.pageTitle) != null ? _slide_pageTitle : item.deckTitle) != null ? _ref : ""
|
|
101
|
-
})
|
|
102
|
-
}),
|
|
103
|
-
/*#__PURE__*/ jsxRuntime_module_u(RichText, {
|
|
104
|
-
text: slide.talkTrack,
|
|
105
|
-
animated: false,
|
|
106
|
-
paragraphSpacing: false
|
|
107
|
-
})
|
|
108
|
-
]
|
|
109
|
-
}, slide.pageId + "-" + slide.slideIndex);
|
|
110
|
-
})
|
|
111
|
-
})
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
/**
|
|
115
|
-
* Off-call "join call" prompt. Mirrors PresentVideoRenderer — decks are
|
|
116
|
-
* call-gated, so the deck itself only plays in the maximized overlay once a call
|
|
117
|
-
* is in progress.
|
|
118
|
-
*/ function PresentDeckJoinCard(param) {
|
|
47
|
+
function PresentDeckCard(param) {
|
|
119
48
|
let { item, context } = param;
|
|
120
|
-
const { hidden, thumbnailUrl,
|
|
49
|
+
const { hidden, thumbnailUrl, cardLabel, actionLabel, slideCount, slideCountLabel, activate } = usePresentDeckCard({
|
|
121
50
|
item,
|
|
122
51
|
context
|
|
123
52
|
});
|
|
124
53
|
if (hidden) return null;
|
|
54
|
+
const stacked = slideCount > 1;
|
|
125
55
|
return /*#__PURE__*/ jsxRuntime_module_u(MessageEntry, {
|
|
126
56
|
isOwn: false,
|
|
127
57
|
children: /*#__PURE__*/ jsxRuntime_module_u(Card, {
|
|
128
58
|
"data-component-name": "PresentDeckRenderer.Card",
|
|
59
|
+
type: "button",
|
|
60
|
+
"aria-label": cardLabel,
|
|
61
|
+
onClick: activate,
|
|
129
62
|
children: [
|
|
130
|
-
/*#__PURE__*/ jsxRuntime_module_u(
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
63
|
+
/*#__PURE__*/ jsxRuntime_module_u(ThumbnailStack, {
|
|
64
|
+
"data-component-name": "PresentDeckRenderer.ThumbnailStack",
|
|
65
|
+
children: [
|
|
66
|
+
stacked && /*#__PURE__*/ jsxRuntime_module_u(preact_module_k, {
|
|
67
|
+
children: [
|
|
68
|
+
/*#__PURE__*/ jsxRuntime_module_u(StackSheet, {
|
|
69
|
+
depth: "far",
|
|
70
|
+
"aria-hidden": "true",
|
|
71
|
+
children: thumbnailUrl && /*#__PURE__*/ jsxRuntime_module_u(SheetTint, {
|
|
72
|
+
src: thumbnailUrl,
|
|
73
|
+
crossOrigin: "anonymous",
|
|
74
|
+
alt: ""
|
|
75
|
+
})
|
|
76
|
+
}),
|
|
77
|
+
/*#__PURE__*/ jsxRuntime_module_u(StackSheet, {
|
|
78
|
+
depth: "near",
|
|
79
|
+
"aria-hidden": "true",
|
|
80
|
+
children: thumbnailUrl && /*#__PURE__*/ jsxRuntime_module_u(SheetTint, {
|
|
81
|
+
src: thumbnailUrl,
|
|
82
|
+
crossOrigin: "anonymous",
|
|
83
|
+
alt: ""
|
|
84
|
+
})
|
|
85
|
+
})
|
|
86
|
+
]
|
|
87
|
+
}),
|
|
88
|
+
/*#__PURE__*/ jsxRuntime_module_u(ThumbnailFrame, {
|
|
89
|
+
children: [
|
|
90
|
+
thumbnailUrl && // Every slide load must be crossorigin, here and everywhere else. The assets
|
|
91
|
+
// origin only sends `Access-Control-Allow-Origin` (and `Vary: Origin`) when the
|
|
92
|
+
// request carries an `Origin` header, so a plain <img> caches a response with
|
|
93
|
+
// neither, and the browser then serves that ACAO-less entry to a later
|
|
94
|
+
// crossorigin request for the same url, which fails the CORS check. This card
|
|
95
|
+
// previews the deck's FIRST slide off-call, so it was the plain load that
|
|
96
|
+
// poisoned slide 1 for the overlay and the perception capture that followed.
|
|
97
|
+
//
|
|
98
|
+
// alt="": the card names the deck itself, and a title here as well would
|
|
99
|
+
// put it into the card's announcement twice over.
|
|
100
|
+
/*#__PURE__*/ jsxRuntime_module_u(ThumbnailImage, {
|
|
101
|
+
"data-component-name": "PresentDeckRenderer.Thumbnail",
|
|
102
|
+
src: thumbnailUrl,
|
|
103
|
+
alt: "",
|
|
104
|
+
crossOrigin: "anonymous"
|
|
105
|
+
}),
|
|
106
|
+
stacked && /*#__PURE__*/ jsxRuntime_module_u(SlideCountBadge, {
|
|
107
|
+
"aria-hidden": "true",
|
|
108
|
+
children: [
|
|
109
|
+
/*#__PURE__*/ jsxRuntime_module_u(SlidesIcon, {}),
|
|
110
|
+
slideCountLabel
|
|
111
|
+
]
|
|
112
|
+
})
|
|
113
|
+
]
|
|
114
|
+
})
|
|
115
|
+
]
|
|
143
116
|
}),
|
|
144
117
|
/*#__PURE__*/ jsxRuntime_module_u(Button, {
|
|
118
|
+
as: "span",
|
|
145
119
|
variant: "primary",
|
|
146
120
|
size: "md",
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
121
|
+
fullWidth: true,
|
|
122
|
+
visualOnly: true,
|
|
123
|
+
css: {
|
|
124
|
+
// The slide sets the card's width, so the label may have less room than a
|
|
125
|
+
// translation of it needs, and the shared button never wraps. Wrapping and
|
|
126
|
+
// growing beats a label hanging off the card's edge; that turns md's height
|
|
127
|
+
// into a floor, so its block padding comes down to keep a one-line label the
|
|
128
|
+
// 32px every other md button in the widget is.
|
|
129
|
+
whiteSpace: "normal",
|
|
130
|
+
height: "[auto]",
|
|
131
|
+
minHeight: "[32px]",
|
|
132
|
+
paddingBlock: "$xs"
|
|
133
|
+
},
|
|
134
|
+
children: actionLabel
|
|
151
135
|
})
|
|
152
136
|
]
|
|
153
137
|
})
|
|
154
138
|
});
|
|
155
139
|
}
|
|
156
|
-
function
|
|
140
|
+
function usePresentDeckCard(param) {
|
|
157
141
|
let { item, context } = param;
|
|
158
|
-
var _ref, _item_deckTitle;
|
|
142
|
+
var _ref, _item_totalSlides, _ref1, _item_deckTitle, _ref2;
|
|
143
|
+
var _item_slideImageUrls, _item_slideImageUrls1;
|
|
159
144
|
const { t } = context.translationStore;
|
|
160
|
-
// The tool can fire
|
|
161
|
-
//
|
|
162
|
-
//
|
|
163
|
-
|
|
145
|
+
// The tool can fire several times for one deck (the initial share, a resume
|
|
146
|
+
// after an interruption, plain navigation). Only the last card for a deck is
|
|
147
|
+
// worth drawing — one entry point is enough. Navigation-only messages are
|
|
148
|
+
// skipped: they draw nothing themselves, so letting a trailing "go back to
|
|
149
|
+
// slide 2" win would swallow the deck's card entirely. Walk backwards (no
|
|
150
|
+
// findLast — the widget runs in old browsers).
|
|
151
|
+
const lastCard = useComputed(()=>{
|
|
164
152
|
const visible = context.messages.visible.value;
|
|
165
153
|
for(let i = visible.length - 1; i >= 0; i--){
|
|
166
154
|
const m = visible[i];
|
|
167
|
-
if (m.type === "present_deck" && m.deckId === item.deckId) {
|
|
155
|
+
if (m.type === "present_deck" && m.deckId === item.deckId && !m.navigationOnly) {
|
|
168
156
|
return m;
|
|
169
157
|
}
|
|
170
158
|
}
|
|
171
159
|
return undefined;
|
|
172
160
|
});
|
|
173
|
-
//
|
|
174
|
-
//
|
|
175
|
-
//
|
|
161
|
+
// Whether Piper has actually presented this deck. The server records the slides
|
|
162
|
+
// she reached when her narration turn ends, so a non-empty `presentedSlideIndexes`
|
|
163
|
+
// on any of the deck's shares means the presentation happened and is over. This
|
|
164
|
+
// asks the question directly rather than inferring it from a talk track, which
|
|
165
|
+
// says only that the slide has a caption.
|
|
166
|
+
//
|
|
167
|
+
// Read through `context.messages`, a signal, so this recomputes the moment the
|
|
168
|
+
// server records the presentation mid-call. That is load-bearing rather than
|
|
169
|
+
// incidental: `useComputed` re-runs only when a signal it read changes, and the
|
|
170
|
+
// re-broadcast that records the presentation reaches the card by replacing the
|
|
171
|
+
// message in `messages` (RootStore#updateMessage). Reading a plain property off
|
|
172
|
+
// `item` alone would leave the card offering a call for a deck Piper had just
|
|
173
|
+
// finished presenting.
|
|
174
|
+
const alreadyPresented = useComputed(()=>{
|
|
175
|
+
// `item` itself first, in case it isn't in the message list yet (optimistic
|
|
176
|
+
// render / tests) — the same allowance the presentation record used to make.
|
|
177
|
+
if (presented(item)) return true;
|
|
178
|
+
return context.messages.value.some((m)=>m.type === "present_deck" && m.deckId === item.deckId && presented(m));
|
|
179
|
+
});
|
|
180
|
+
// Is a call even possible? Mode and capability agree in production
|
|
181
|
+
// (UIStore#validateConversationMode forces the mode down to TEXT when voice and
|
|
182
|
+
// video are both disallowed), so this is the belt to that braces: without it a
|
|
183
|
+
// mid-call voice failover would leave the button offering a call that cannot
|
|
184
|
+
// start.
|
|
185
|
+
const callAvailable = useComputed(()=>context.uiStore.allowedConversationModes.voice.value || context.uiStore.allowedConversationModes.video.value);
|
|
186
|
+
// A conversation that could never have a call, which is a different question from
|
|
187
|
+
// which mode it is in right now — and the two come apart in exactly the direction
|
|
188
|
+
// that matters here. Dropping to TEXT leaves the modes it is allowed alone, so a
|
|
189
|
+
// visitor who left a call or whose connection attempt fell back to chat is in text
|
|
190
|
+
// mode with a call still on the table. Only configuration (no voice/avatar flag, a
|
|
191
|
+
// `text` modality preference) or the server's voice circuit breaker takes the call
|
|
192
|
+
// away, and that is what "text-only" means. The pairing the deck viewer weighs
|
|
193
|
+
// before offering its own switch to voice.
|
|
194
|
+
const textOnly = useComputed(()=>context.conversationMode.text.value && !callAvailable.value);
|
|
195
|
+
// Where the button goes, and with it which mode the deck opens in. Text-only has
|
|
196
|
+
// no call to join; an already-presented deck is a review of what Piper said, not a
|
|
197
|
+
// reason to present it again.
|
|
198
|
+
const opensSlides = useComputed(()=>context.conversationMode.text.value || alreadyPresented.value || !callAvailable.value);
|
|
199
|
+
// Hidden when the deck is already up in the overlay (the card would duplicate
|
|
200
|
+
// it), when a call is in progress with nothing presented yet (Piper is about to
|
|
201
|
+
// present it herself), or when this isn't the deck's last card. Text-only no
|
|
202
|
+
// longer hides it — there the card is the visitor's only way into the deck.
|
|
176
203
|
const hidden = useComputed(()=>{
|
|
177
204
|
var _context_artifact_message_value;
|
|
178
|
-
return context.maximizedState.value === types_MaximizedState.ARTIFACT && ((_context_artifact_message_value = context.artifact.message.value) == null ? void 0 : _context_artifact_message_value.type) === "present_deck" || context.
|
|
205
|
+
return context.maximizedState.value === types_MaximizedState.ARTIFACT && ((_context_artifact_message_value = context.artifact.message.value) == null ? void 0 : _context_artifact_message_value.type) === "present_deck" || context.callInProgress.value && !alreadyPresented.value || lastCard.value != null && lastCard.value.id !== item.id;
|
|
179
206
|
});
|
|
180
|
-
const
|
|
207
|
+
const slideCount = (_ref = (_item_totalSlides = item.totalSlides) != null ? _item_totalSlides : (_item_slideImageUrls = item.slideImageUrls) == null ? void 0 : _item_slideImageUrls.length) != null ? _ref : 0;
|
|
208
|
+
const deckTitle = (_ref1 = (_item_deckTitle = item.deckTitle) != null ? _item_deckTitle : item.pageTitle) != null ? _ref1 : "";
|
|
209
|
+
// Which of `opensSlides`' three reasons is true decides the label, though none of
|
|
210
|
+
// them changes the behaviour. "Continue presentation" needs a presentation to
|
|
211
|
+
// continue, so only an already-presented deck earns it: the other two routes into
|
|
212
|
+
// the viewer open a deck nobody has presented, where it would name something that
|
|
213
|
+
// never happened.
|
|
214
|
+
//
|
|
215
|
+
// Text-only is then held back to "Start presentation" even for a deck Piper did
|
|
216
|
+
// present, on instruction. `textOnly` rather than the mode alone is the whole point
|
|
217
|
+
// of the distinction — a visitor who watched the presentation and then left the
|
|
218
|
+
// call is in text mode with voice still available, and there she is picking the
|
|
219
|
+
// presentation back up, so that case reads "Continue presentation".
|
|
220
|
+
const actionLabel = alreadyPresented.value && !textOnly.value ? t("presentation.continue") : t("presentation.start");
|
|
221
|
+
const activate = ()=>{
|
|
222
|
+
if (opensSlides.peek()) {
|
|
223
|
+
void context.presentSelfGuidedDeck(item);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
181
226
|
if (context.callInProgress.peek()) return;
|
|
182
227
|
context.getCallUiAdapter().actions.startCall({
|
|
183
|
-
trigger: {
|
|
228
|
+
trigger: _extends({
|
|
184
229
|
type: types_StartCallTrigger.StartDeck,
|
|
185
230
|
deckId: item.deckId,
|
|
186
231
|
slideIndex: item.slideIndex
|
|
187
|
-
}
|
|
232
|
+
}, item.presentationBrief && {
|
|
233
|
+
presentationBrief: item.presentationBrief
|
|
234
|
+
}, item.targetDurationSeconds && {
|
|
235
|
+
targetDurationSeconds: item.targetDurationSeconds
|
|
236
|
+
})
|
|
188
237
|
});
|
|
189
238
|
};
|
|
190
239
|
return {
|
|
191
240
|
hidden: hidden.value,
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
241
|
+
// The deck's first slide, not the share's. `imageUrl` is whichever slide this
|
|
242
|
+
// share opened on, so a resume of slides 5-8 would put slide 5 on the card —
|
|
243
|
+
// the deck's cover is what the visitor is being offered either way.
|
|
244
|
+
thumbnailUrl: (_ref2 = (_item_slideImageUrls1 = item.slideImageUrls) == null ? void 0 : _item_slideImageUrls1[0]) != null ? _ref2 : item.imageUrl,
|
|
245
|
+
// The card is the control, so the card is what has to name both the deck on
|
|
246
|
+
// offer and what activating it does. Nothing reaches that name from the card's
|
|
247
|
+
// content — the thumbnail is decorative and the affordance is aria-hidden — so
|
|
248
|
+
// it is assembled here. Joined rather than looked up as one composed string: a
|
|
249
|
+
// separator needs no translating, and a key for it would have to be added to
|
|
250
|
+
// every locale to say nothing.
|
|
251
|
+
cardLabel: deckTitle ? deckTitle + ", " + actionLabel : actionLabel,
|
|
252
|
+
actionLabel,
|
|
253
|
+
slideCount,
|
|
254
|
+
slideCountLabel: t("presentation.slideCount", {
|
|
255
|
+
count: slideCount
|
|
256
|
+
}),
|
|
257
|
+
activate
|
|
196
258
|
};
|
|
197
259
|
}
|
|
198
|
-
|
|
260
|
+
/**
|
|
261
|
+
* Whether a share records slides the conversation actually reached on a call.
|
|
262
|
+
*
|
|
263
|
+
* Decks presented before the server started sending `presentedSlideIndexes` have
|
|
264
|
+
* none, so their card offers a call instead of the slides until presented again.
|
|
265
|
+
*/ function presented(message) {
|
|
266
|
+
var _message_presentedSlideIndexes;
|
|
267
|
+
return ((_message_presentedSlideIndexes = message.presentedSlideIndexes) != null ? _message_presentedSlideIndexes : []).length > 0;
|
|
268
|
+
}
|
|
269
|
+
// The whole card is the control, as it is on the offer card next to it in the
|
|
270
|
+
// transcript (LinkPreviewCard, a card-wide <a>): the visitor is offered a deck, not
|
|
271
|
+
// a button underneath one. A real <button> rather than a div wearing a role and a
|
|
272
|
+
// keydown handler — this renders inside arbitrary customer pages, where the
|
|
273
|
+
// platform's own activation, focus and screen-reader behaviour is worth more than
|
|
274
|
+
// any of it reimplemented here.
|
|
275
|
+
//
|
|
276
|
+
// The hover lift matches the offer card's. It is duplicated rather than shared
|
|
277
|
+
// because Panda only extracts style objects it can see in the file that uses them —
|
|
278
|
+
// an imported one compiles to class names with no rules.
|
|
279
|
+
//
|
|
280
|
+
// It sits on the card rather than the thumbnail so the sheets behind the slide
|
|
281
|
+
// come along: they are positioned against ThumbnailStack, which the transform
|
|
282
|
+
// carries with the rest of the subtree, so the stack moves as one object.
|
|
283
|
+
const Card = styled("button", {
|
|
199
284
|
base: {
|
|
285
|
+
// Everything back that the element used to get for being a div. Preflight is
|
|
286
|
+
// off, so nothing else undoes the UA button: its own border, centred text and
|
|
287
|
+
// 13px system font, the ButtonText colour, the align-items that only shows once
|
|
288
|
+
// display is flex, and on Safari a native re-skin that only appearance stops.
|
|
289
|
+
// Padding, background and margin are covered by the card's own styles and the
|
|
290
|
+
// global reset.
|
|
291
|
+
appearance: "none",
|
|
292
|
+
// Prefixed as well: iOS Safari only dropped the prefix in 15.4, and it is the
|
|
293
|
+
// one that skins a button hardest.
|
|
294
|
+
WebkitAppearance: "none",
|
|
295
|
+
border: "none",
|
|
296
|
+
font: "inherit",
|
|
297
|
+
color: "[inherit]",
|
|
298
|
+
textAlign: "inherit",
|
|
299
|
+
alignItems: "stretch",
|
|
300
|
+
cursor: "pointer",
|
|
200
301
|
display: "flex",
|
|
201
302
|
flexDirection: "column",
|
|
202
|
-
gap: "$
|
|
303
|
+
gap: "$md",
|
|
203
304
|
padding: "$lg",
|
|
305
|
+
// The slide sets the card's width, so a wide one would otherwise stretch the
|
|
306
|
+
// card across the whole transcript.
|
|
307
|
+
maxWidth: "[320px]",
|
|
204
308
|
borderRadius: "[8px]",
|
|
205
309
|
outline: "1px solid token(colors.$gray.border)",
|
|
206
310
|
outlineOffset: "-1px",
|
|
207
|
-
backgroundColor: "$white.solid"
|
|
311
|
+
backgroundColor: "$white.solid",
|
|
312
|
+
transition: "box-shadow 0.2s ease, transform 0.2s ease",
|
|
313
|
+
// The card's edge is an outline, so the focus ring is that same outline in the
|
|
314
|
+
// widget's own colour rather than a second ring outside it — the treatment the
|
|
315
|
+
// shared Button uses.
|
|
316
|
+
"&:focus-visible": {
|
|
317
|
+
outline: "2px solid token(colors.$primary)",
|
|
318
|
+
outlineOffset: "-2px"
|
|
319
|
+
},
|
|
320
|
+
"&:hover": {
|
|
321
|
+
boxShadow: "[0 4px 12px rgba(0, 0, 0, 0.1)]",
|
|
322
|
+
transform: "translateY(-2px)"
|
|
323
|
+
},
|
|
324
|
+
// The affordance inside is pointer-events: none and never focused, so it can
|
|
325
|
+
// never match its own :hover or :focus-visible. Without this it would sit inert
|
|
326
|
+
// under a lifted card, in the one place the visitor is aiming. Mirrors
|
|
327
|
+
// StyledButton's primary hover, which is the state being stood in for.
|
|
328
|
+
'&:is(:hover, :focus-visible) [data-component-name="Button.StyledButton"]': {
|
|
329
|
+
background: "[linear-gradient(token(colors.$button.hoverBackgroundColor), token(colors.$button.hoverBackgroundColor)) var(--q-button-hover-background-layer, #ffffff)]",
|
|
330
|
+
color: "$button.hoverTextColor",
|
|
331
|
+
borderColor: "$button.hoverBorderColor"
|
|
332
|
+
},
|
|
333
|
+
// The shadow stays and the travel goes: the card still answers the pointer.
|
|
334
|
+
"@media (prefers-reduced-motion: reduce)": {
|
|
335
|
+
"&:hover": {
|
|
336
|
+
transform: "none"
|
|
337
|
+
}
|
|
338
|
+
}
|
|
208
339
|
}
|
|
209
340
|
});
|
|
210
341
|
Card.displayName = "PresentDeckCard";
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
gap: "$lg"
|
|
216
|
-
}
|
|
217
|
-
});
|
|
218
|
-
Transcript.displayName = "PresentDeckTranscript";
|
|
219
|
-
const SlideBlock = styled("div", {
|
|
342
|
+
// Holds the slide and the two sheets peeking out above it. The top padding is the
|
|
343
|
+
// room those sheets sit in — without it they'd be clipped by the card instead of
|
|
344
|
+
// reading as slides behind this one.
|
|
345
|
+
const ThumbnailStack = styled("div", {
|
|
220
346
|
base: {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
gap: "$sm"
|
|
347
|
+
position: "relative",
|
|
348
|
+
paddingTop: "$md"
|
|
224
349
|
}
|
|
225
350
|
});
|
|
226
|
-
|
|
227
|
-
//
|
|
228
|
-
//
|
|
229
|
-
//
|
|
230
|
-
|
|
351
|
+
ThumbnailStack.displayName = "PresentDeckThumbnailStack";
|
|
352
|
+
// A slide edge behind the top slide: inset from both sides and pushed up, so only
|
|
353
|
+
// its rounded top shows. Two of them, the further one smaller and paler, which is
|
|
354
|
+
// what makes the stack read as deep rather than as a double border. The colour
|
|
355
|
+
// comes from SheetTint below; the fill here is what a deck with no image falls
|
|
356
|
+
// back to.
|
|
357
|
+
const StackSheet = styled("div", {
|
|
231
358
|
base: {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
border: "none",
|
|
235
|
-
borderRadius: "md",
|
|
359
|
+
position: "absolute",
|
|
360
|
+
height: "[16px]",
|
|
236
361
|
overflow: "hidden",
|
|
237
|
-
|
|
238
|
-
|
|
362
|
+
borderTopLeftRadius: "[8px]",
|
|
363
|
+
borderTopRightRadius: "[8px]"
|
|
364
|
+
},
|
|
365
|
+
variants: {
|
|
366
|
+
depth: {
|
|
367
|
+
far: {
|
|
368
|
+
top: "[0]",
|
|
369
|
+
insetInline: "[16px]",
|
|
370
|
+
backgroundColor: "$primary.lighter",
|
|
371
|
+
opacity: 0.45
|
|
372
|
+
},
|
|
373
|
+
near: {
|
|
374
|
+
top: "[6px]",
|
|
375
|
+
insetInline: "[8px]",
|
|
376
|
+
backgroundColor: "$primary.lighter",
|
|
377
|
+
opacity: 0.75
|
|
378
|
+
}
|
|
379
|
+
}
|
|
239
380
|
}
|
|
240
381
|
});
|
|
241
|
-
|
|
242
|
-
|
|
382
|
+
StackSheet.displayName = "PresentDeckStackSheet";
|
|
383
|
+
// What gives each sheet its colour: the first slide again, blown past the sheet on
|
|
384
|
+
// every side and blurred down to a smear, so the few pixels showing above the
|
|
385
|
+
// thumbnail are that slide's own background rather than a fixed tint. The bleed
|
|
386
|
+
// keeps the blur's soft fringe outside the sheet, which clips it back to a clean
|
|
387
|
+
// rounded top; the sheet's own opacity still does the front-to-back progression.
|
|
388
|
+
// An <img> rather than a CSS background, and crossorigin like every other slide
|
|
389
|
+
// load: a background-image is fetched without an Origin header and would cache the
|
|
390
|
+
// ACAO-less response that the overlay's crossorigin request then fails on.
|
|
391
|
+
const SheetTint = styled("img", {
|
|
243
392
|
base: {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
393
|
+
position: "absolute",
|
|
394
|
+
top: "[-20px]",
|
|
395
|
+
insetInline: "[-20px]",
|
|
396
|
+
width: "[calc(100% + 40px)]",
|
|
397
|
+
height: "[calc(100% + 40px)]",
|
|
398
|
+
objectFit: "cover",
|
|
399
|
+
objectPosition: "top",
|
|
400
|
+
filter: "[blur(12px)]"
|
|
247
401
|
}
|
|
248
402
|
});
|
|
249
|
-
|
|
250
|
-
// Fits the slide at its natural aspect
|
|
251
|
-
//
|
|
252
|
-
//
|
|
253
|
-
//
|
|
254
|
-
//
|
|
403
|
+
SheetTint.displayName = "PresentDeckSheetTint";
|
|
404
|
+
// Fits the slide at its natural aspect. A fixed 16:9 frame with an
|
|
405
|
+
// object-fit: cover image centre-cropped a portrait slide, cutting off its title;
|
|
406
|
+
// a deck may contain pages of any shape, so the frame takes the slide's. With no
|
|
407
|
+
// image to take a shape from, keep a 16:9 placeholder so the card doesn't
|
|
408
|
+
// collapse to a bare button.
|
|
255
409
|
const ThumbnailFrame = styled("div", {
|
|
256
410
|
base: {
|
|
411
|
+
position: "relative",
|
|
257
412
|
width: "[100%]",
|
|
258
413
|
overflow: "hidden",
|
|
259
414
|
borderRadius: "[8px]",
|
|
@@ -272,6 +427,27 @@ const ThumbnailImage = styled("img", {
|
|
|
272
427
|
}
|
|
273
428
|
});
|
|
274
429
|
ThumbnailImage.displayName = "PresentDeckThumbnailImage";
|
|
430
|
+
const SlideCountBadge = styled("span", {
|
|
431
|
+
base: {
|
|
432
|
+
position: "absolute",
|
|
433
|
+
right: "$sm",
|
|
434
|
+
bottom: "$sm",
|
|
435
|
+
display: "flex",
|
|
436
|
+
alignItems: "center",
|
|
437
|
+
gap: "$xs",
|
|
438
|
+
paddingInline: "$sm",
|
|
439
|
+
paddingBlock: "$xs",
|
|
440
|
+
borderRadius: "sm",
|
|
441
|
+
backgroundColor: "$text.dark",
|
|
442
|
+
color: "$white.solid",
|
|
443
|
+
fontFamily: "$inter",
|
|
444
|
+
fontSize: "[12px]",
|
|
445
|
+
fontWeight: "$semibold",
|
|
446
|
+
lineHeight: "[16px]",
|
|
447
|
+
whiteSpace: "nowrap"
|
|
448
|
+
}
|
|
449
|
+
});
|
|
450
|
+
SlideCountBadge.displayName = "PresentDeckSlideCountBadge";
|
|
275
451
|
/* export default */ const timeline_PresentDeckRenderer = (PresentDeckRenderer);
|
|
276
452
|
|
|
277
453
|
var PresentDeckRenderer_PresentDeckRenderer = /* unused export */ undefined;
|