@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,25 +1,76 @@
|
|
|
1
|
-
import { jsxRuntime_module_u, utils_module_o, useSignalEffect } from "../../../../js/multimodal_v2.js";
|
|
1
|
+
import { preact_module_k, useComputed, jsxRuntime_module_u, utils_module_o, useSignalEffect } from "../../../../js/multimodal_v2.js";
|
|
2
|
+
import { SpeakNowMicIcon, ArrowRightIcon } from "../../images/icons.js";
|
|
2
3
|
import "../../styled-system/jsx/index.js";
|
|
3
4
|
import { slideZoomStyle } from "../../utils/slide_zoom.js";
|
|
5
|
+
import { RichText } from "../helpers/RichText.js";
|
|
6
|
+
import { Status } from "../multimodal/Status.js";
|
|
7
|
+
import { Button } from "../shared/Button.js";
|
|
8
|
+
import { DeckMode as types_DeckMode, StartCallTrigger as types_StartCallTrigger } from "../../types.js";
|
|
4
9
|
import { styled } from "../../styled-system/jsx/factory.js";
|
|
5
10
|
|
|
11
|
+
function _extends() {
|
|
12
|
+
_extends = Object.assign || function(target) {
|
|
13
|
+
for(var i = 1; i < arguments.length; i++){
|
|
14
|
+
var source = arguments[i];
|
|
15
|
+
for(var key in source){
|
|
16
|
+
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
17
|
+
target[key] = source[key];
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return target;
|
|
22
|
+
};
|
|
23
|
+
return _extends.apply(this, arguments);
|
|
24
|
+
}
|
|
6
25
|
|
|
7
26
|
/** @jsxImportSource preact */
|
|
8
27
|
|
|
9
28
|
|
|
10
29
|
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
11
35
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
36
|
+
* Prefix for every `data-component-name` this component emits, per mode.
|
|
37
|
+
*
|
|
38
|
+
* This is load-bearing, not cosmetic. `pointAtSlide` finds the element to zoom
|
|
39
|
+
* by querying `[data-component-name="Deck.SlideImage"]`
|
|
40
|
+
* (website_navigation_store), and the self-guided viewer must not be findable by
|
|
41
|
+
* it: the deck card stays available during a call once something has been
|
|
42
|
+
* presented, so the visitor can be reading slide 5 while Piper points at slide
|
|
43
|
+
* 2. Were the viewer's image named `Deck.SlideImage`, her focal point would be
|
|
44
|
+
* applied to whatever slide they happen to be browsing, mid-interaction.
|
|
45
|
+
*
|
|
46
|
+
* Every name goes through `componentName` below so the guarantee holds for the
|
|
47
|
+
* whole subtree rather than one attribute, and index.test.tsx asserts no
|
|
48
|
+
* `Deck.*` element exists in self-guided mode.
|
|
49
|
+
*
|
|
50
|
+
* The prefixes are a DOM contract rather than a mirror of `DeckMode`'s names:
|
|
51
|
+
* guided must stay exactly `Deck`, and the other prefix only has to be
|
|
52
|
+
* something that does not begin with `Deck.` — `SelfGuidedDeck.SlideImage`
|
|
53
|
+
* qualifies, since the query and the guard both match on that leading `Deck.`.
|
|
54
|
+
*/ const COMPONENT_NAME_PREFIX = {
|
|
55
|
+
[types_DeckMode.Guided]: "Deck",
|
|
56
|
+
[types_DeckMode.SelfGuided]: "SelfGuidedDeck"
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Deck is the entrypoint for the `present_deck` artifact, in either mode. Which
|
|
60
|
+
* mode is a property of the store's playback state.
|
|
14
61
|
*/ const Deck = (param)=>{
|
|
15
62
|
let { rootStore } = param;
|
|
16
63
|
var _ref, _currentDeck_pageTitle;
|
|
17
|
-
|
|
64
|
+
const { t } = rootStore.translationStore;
|
|
18
65
|
const zoomRef = utils_module_o(null);
|
|
19
66
|
// Zoom the slide in place toward wherever Piper is pointing (see pointAtSlide + utils/slide_zoom),
|
|
20
67
|
// mirroring the demo <video> (presentations/index.tsx). Applied to an inner layer so the <img>'s
|
|
21
68
|
// static white-strip transform and SlideClip's shadow/rounding are left untouched by the zoom.
|
|
22
69
|
// Only touches the style during a call (when the nav store exists).
|
|
70
|
+
//
|
|
71
|
+
// Safe to run in both modes only because of the early return: self-guided mode never renders
|
|
72
|
+
// SlideZoomLayer, so `layer` is null and this bails before reading `slideZoom` — never
|
|
73
|
+
// subscribing to it. Rendering the zoom layer in self-guided mode would break that.
|
|
23
74
|
useSignalEffect(()=>{
|
|
24
75
|
const layer = zoomRef.value;
|
|
25
76
|
const navStore = rootStore.websiteNavigationStore.value;
|
|
@@ -30,37 +81,211 @@ import { styled } from "../../styled-system/jsx/factory.js";
|
|
|
30
81
|
layer.style.clipPath = style.clipPath;
|
|
31
82
|
layer.style.transition = style.transition;
|
|
32
83
|
});
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
84
|
+
// Whether handing the deck back to Piper is on the table. Three things have to
|
|
85
|
+
// hold, and each closes off a different dead end:
|
|
86
|
+
//
|
|
87
|
+
// - A call has to be possible at all. Mode and capability agree in production
|
|
88
|
+
// (UIStore#validateConversationMode forces the mode down to TEXT when voice and
|
|
89
|
+
// video are both disallowed), so this is the belt to the mode's braces: without
|
|
90
|
+
// it a mid-call voice failover would leave a button offering a call that cannot
|
|
91
|
+
// start. The same pair PresentDeckRenderer weighs before offering its own call.
|
|
92
|
+
// - Text-only has no call to switch to.
|
|
93
|
+
// - Neither does a call already running, and that is a live state rather than a
|
|
94
|
+
// theoretical one: the viewer stays open during a call, so the visitor can be
|
|
95
|
+
// reading slide 5 while Piper points at slide 2.
|
|
96
|
+
//
|
|
97
|
+
// Above the early return because it is a hook, and computed rather than read
|
|
98
|
+
// straight so a failover mid-read takes the button away with it.
|
|
99
|
+
const voiceSwitchAvailable = useComputed(()=>(rootStore.uiStore.allowedConversationModes.voice.value || rootStore.uiStore.allowedConversationModes.video.value) && !rootStore.conversationMode.text.value && !rootStore.callInProgress.value);
|
|
100
|
+
// The message over the dimmed slide while the call is starting, in two phases:
|
|
101
|
+
// the call being established, then the presentation itself.
|
|
102
|
+
//
|
|
103
|
+
// The first phase is over when the call UI's own connecting state is over, on
|
|
104
|
+
// that state's signal rather than one of this overlay's choosing
|
|
105
|
+
// (ArtifactStore#deckPresentationConnecting wraps it). It borrows the call UI's
|
|
106
|
+
// words as well, so what the two surfaces say about one event is the same
|
|
107
|
+
// sentence, and neither the sentence nor the moment it stops being true can
|
|
108
|
+
// drift from the panel's.
|
|
109
|
+
//
|
|
110
|
+
// Live, with nothing latched on the way past: a label that held its phase could
|
|
111
|
+
// only hold the wrong one, which is what it did — Piper would start talking over
|
|
112
|
+
// an overlay still promising to connect the visitor to her. Following the signal
|
|
113
|
+
// means a reconnect takes the text back to connecting, which is the same thing
|
|
114
|
+
// the call UI does at that moment and so reads as one interface rather than two.
|
|
115
|
+
//
|
|
116
|
+
// A signal rather than a value read during render: passed straight to `Status`,
|
|
117
|
+
// the text advances without this component re-rendering, so the slide behind it
|
|
118
|
+
// is not even diffed, let alone remounted.
|
|
119
|
+
const startingMessage = useComputed(()=>{
|
|
120
|
+
var _rootStore_activeConversation_aiSdr_value;
|
|
121
|
+
if (!rootStore.artifactStore.deckPresentationConnecting.value) {
|
|
122
|
+
return t("presentation.starting");
|
|
123
|
+
}
|
|
124
|
+
const name = ((_rootStore_activeConversation_aiSdr_value = rootStore.activeConversation.aiSdr.value) == null ? void 0 : _rootStore_activeConversation_aiSdr_value.name) || t("video.yourAssistant");
|
|
125
|
+
return t("video.connectingTo", {
|
|
126
|
+
name
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
const decksStore = rootStore.decksStore.value;
|
|
130
|
+
const currentDeck = decksStore == null ? void 0 : decksStore.currentDeck.value;
|
|
131
|
+
if (!decksStore || !currentDeck) return null;
|
|
132
|
+
const { sequencePosition, sequenceLength, mode, starting } = currentDeck;
|
|
133
|
+
const componentName = (part)=>COMPONENT_NAME_PREFIX[mode] + "." + part;
|
|
134
|
+
// Guided mode is Piper already presenting, so there is nothing to hand back.
|
|
135
|
+
// Neither is there while the call that takes it off them is starting: the offer
|
|
136
|
+
// has been accepted, and the panel it lives on is gone for the duration.
|
|
137
|
+
const showVoiceSwitch = mode === types_DeckMode.SelfGuided && !starting && voiceSwitchAvailable.value;
|
|
138
|
+
// Hand the deck back to Piper from wherever the visitor has read to, not from
|
|
139
|
+
// the slide the share opened on — resuming where they are is the whole point of
|
|
140
|
+
// the switch. The brief and the target duration come off the share driving the
|
|
141
|
+
// viewer, which is the same message `currentDeck` is computed from (DecksStore),
|
|
142
|
+
// so the call opens with the intent the off-call share recorded, exactly as the
|
|
143
|
+
// timeline card's own call does (PresentDeckRenderer#activate).
|
|
144
|
+
const switchToVoice = ()=>{
|
|
145
|
+
if (rootStore.callInProgress.peek()) return;
|
|
146
|
+
const artifactMessage = rootStore.artifact.message.peek();
|
|
147
|
+
const share = (artifactMessage == null ? void 0 : artifactMessage.type) === "present_deck" ? artifactMessage : undefined;
|
|
148
|
+
rootStore.getCallUiAdapter().actions.startCall({
|
|
149
|
+
trigger: _extends({
|
|
150
|
+
type: types_StartCallTrigger.StartDeck,
|
|
151
|
+
deckId: currentDeck.deckId,
|
|
152
|
+
slideIndex: currentDeck.currentIndex
|
|
153
|
+
}, (share == null ? void 0 : share.presentationBrief) && {
|
|
154
|
+
presentationBrief: share.presentationBrief
|
|
155
|
+
}, (share == null ? void 0 : share.targetDurationSeconds) && {
|
|
156
|
+
targetDurationSeconds: share.targetDurationSeconds
|
|
157
|
+
})
|
|
158
|
+
});
|
|
159
|
+
};
|
|
160
|
+
const slideImage = /* Crossorigin like every other slide load: the assets origin only sends
|
|
161
|
+
`Access-Control-Allow-Origin` when the request carries an `Origin`
|
|
162
|
+
header, so a plain load here would cache an ACAO-less response and
|
|
163
|
+
fail the CORS check for the same url elsewhere. */ /*#__PURE__*/ jsxRuntime_module_u(SlideImage, {
|
|
164
|
+
mode: mode,
|
|
165
|
+
"data-component-name": componentName("SlideImage"),
|
|
166
|
+
crossOrigin: "anonymous",
|
|
167
|
+
src: currentDeck.slideImageUrls[currentDeck.currentIndex],
|
|
168
|
+
alt: (_ref = (_currentDeck_pageTitle = currentDeck.pageTitle) != null ? _currentDeck_pageTitle : currentDeck.deckTitle) != null ? _ref : "",
|
|
169
|
+
onLoad: ()=>decksStore.notifyCurrentSlideReady()
|
|
170
|
+
});
|
|
171
|
+
const progressDots = /*#__PURE__*/ jsxRuntime_module_u(ProgressDots, {
|
|
172
|
+
mode: mode,
|
|
173
|
+
"data-component-name": componentName("ProgressDots"),
|
|
174
|
+
// A bare div may not carry aria-label (axe: aria-prohibited-attr), and
|
|
175
|
+
// without the label the dots are announced as nothing at all.
|
|
176
|
+
role: "group",
|
|
177
|
+
"aria-label": t("presentation.slidePosition", {
|
|
178
|
+
current: sequencePosition + 1,
|
|
179
|
+
total: sequenceLength
|
|
180
|
+
}),
|
|
181
|
+
children: Array.from({
|
|
182
|
+
length: sequenceLength
|
|
183
|
+
}).map((_, index)=>/*#__PURE__*/ jsxRuntime_module_u(Dot, {
|
|
184
|
+
mode: mode,
|
|
185
|
+
"data-active": index === sequencePosition ? "true" : undefined
|
|
186
|
+
}, index))
|
|
187
|
+
});
|
|
36
188
|
return /*#__PURE__*/ jsxRuntime_module_u(Layout, {
|
|
37
|
-
|
|
189
|
+
mode: mode,
|
|
190
|
+
"data-component-name": componentName("Layout"),
|
|
38
191
|
children: [
|
|
39
192
|
/*#__PURE__*/ jsxRuntime_module_u(SlideFrame, {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
193
|
+
mode: mode,
|
|
194
|
+
withVoiceSwitch: showVoiceSwitch,
|
|
195
|
+
starting: starting,
|
|
196
|
+
"data-component-name": componentName("SlideFrame"),
|
|
197
|
+
children: [
|
|
198
|
+
mode === types_DeckMode.Guided ? // SlideClip owns the rounding and shadow, and SlideZoomLayer is the
|
|
199
|
+
// element the zoom is applied to, kept apart so scaling the slide
|
|
200
|
+
// leaves both of those and the image's own transform alone.
|
|
201
|
+
/*#__PURE__*/ jsxRuntime_module_u(SlideClip, {
|
|
202
|
+
"data-component-name": componentName("SlideClip"),
|
|
203
|
+
children: /*#__PURE__*/ jsxRuntime_module_u(SlideZoomLayer, {
|
|
204
|
+
ref: zoomRef,
|
|
205
|
+
"data-component-name": componentName("SlideZoomLayer"),
|
|
206
|
+
children: slideImage
|
|
51
207
|
})
|
|
208
|
+
}) : slideImage,
|
|
209
|
+
starting && /*#__PURE__*/ jsxRuntime_module_u(preact_module_k, {
|
|
210
|
+
children: [
|
|
211
|
+
/*#__PURE__*/ jsxRuntime_module_u(Scrim, {
|
|
212
|
+
"data-component-name": componentName("Scrim"),
|
|
213
|
+
"aria-hidden": "true"
|
|
214
|
+
}),
|
|
215
|
+
/*#__PURE__*/ jsxRuntime_module_u(Progress, {
|
|
216
|
+
"data-component-name": componentName("Progress"),
|
|
217
|
+
role: "status",
|
|
218
|
+
children: /*#__PURE__*/ jsxRuntime_module_u(Status, {
|
|
219
|
+
variant: "pill",
|
|
220
|
+
colorScheme: "overlayText",
|
|
221
|
+
mainText: startingMessage,
|
|
222
|
+
subText: undefined,
|
|
223
|
+
// The widget's own call-loading indicator, in the form it takes
|
|
224
|
+
// over a visual: the animated ellipsis it uses over blurred video
|
|
225
|
+
// (useMultimodal), rather than a second spinner of this
|
|
226
|
+
// component's.
|
|
227
|
+
loadingIndicator: "dots"
|
|
228
|
+
})
|
|
229
|
+
})
|
|
230
|
+
]
|
|
52
231
|
})
|
|
53
|
-
|
|
232
|
+
]
|
|
54
233
|
}),
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
"
|
|
58
|
-
children:
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
234
|
+
mode === types_DeckMode.SelfGuided && !starting && /*#__PURE__*/ jsxRuntime_module_u(ControlPanel, {
|
|
235
|
+
withVoiceSwitch: showVoiceSwitch,
|
|
236
|
+
"data-component-name": componentName("ControlPanel"),
|
|
237
|
+
children: [
|
|
238
|
+
/*#__PURE__*/ jsxRuntime_module_u(Caption, {
|
|
239
|
+
"data-component-name": componentName("Caption"),
|
|
240
|
+
children: currentDeck.caption && /*#__PURE__*/ jsxRuntime_module_u(RichText, {
|
|
241
|
+
text: currentDeck.caption,
|
|
242
|
+
animated: false,
|
|
243
|
+
paragraphSpacing: false
|
|
244
|
+
})
|
|
245
|
+
}),
|
|
246
|
+
showVoiceSwitch && /*#__PURE__*/ jsxRuntime_module_u(Button, {
|
|
247
|
+
variant: "primary",
|
|
248
|
+
size: "md",
|
|
249
|
+
"data-component-name": componentName("VoiceSwitch"),
|
|
250
|
+
onClick: switchToVoice,
|
|
251
|
+
css: {
|
|
252
|
+
alignSelf: "flex-start",
|
|
253
|
+
maxWidth: "[100%]"
|
|
254
|
+
},
|
|
255
|
+
children: [
|
|
256
|
+
/*#__PURE__*/ jsxRuntime_module_u(SpeakNowMicIcon, {}),
|
|
257
|
+
t("presentation.switchToVoice")
|
|
258
|
+
]
|
|
259
|
+
}),
|
|
260
|
+
/*#__PURE__*/ jsxRuntime_module_u(Controls, {
|
|
261
|
+
children: [
|
|
262
|
+
/*#__PURE__*/ jsxRuntime_module_u(Button, {
|
|
263
|
+
variant: "primary",
|
|
264
|
+
size: "md",
|
|
265
|
+
iconOnly: true,
|
|
266
|
+
"aria-label": t("presentation.previousSlide"),
|
|
267
|
+
disabled: sequencePosition === 0,
|
|
268
|
+
onClick: ()=>decksStore.goToPreviousSlide(),
|
|
269
|
+
css: {
|
|
270
|
+
transform: "rotate(180deg)"
|
|
271
|
+
},
|
|
272
|
+
children: /*#__PURE__*/ jsxRuntime_module_u(ArrowRightIcon, {})
|
|
273
|
+
}),
|
|
274
|
+
progressDots,
|
|
275
|
+
/*#__PURE__*/ jsxRuntime_module_u(Button, {
|
|
276
|
+
variant: "primary",
|
|
277
|
+
size: "md",
|
|
278
|
+
iconOnly: true,
|
|
279
|
+
"aria-label": t("presentation.nextSlide"),
|
|
280
|
+
disabled: sequencePosition >= sequenceLength - 1,
|
|
281
|
+
onClick: ()=>decksStore.goToNextSlide(),
|
|
282
|
+
children: /*#__PURE__*/ jsxRuntime_module_u(ArrowRightIcon, {})
|
|
283
|
+
})
|
|
284
|
+
]
|
|
285
|
+
})
|
|
286
|
+
]
|
|
287
|
+
}),
|
|
288
|
+
mode === types_DeckMode.Guided && sequenceLength > 1 && progressDots
|
|
64
289
|
]
|
|
65
290
|
});
|
|
66
291
|
};
|
|
@@ -69,32 +294,148 @@ const Layout = styled("div", {
|
|
|
69
294
|
base: {
|
|
70
295
|
display: "flex",
|
|
71
296
|
flexDirection: "column",
|
|
72
|
-
alignItems: "center",
|
|
73
|
-
justifyContent: "center",
|
|
74
|
-
gap: "[12px]",
|
|
75
297
|
width: "[100%]",
|
|
76
|
-
height: "[100%]",
|
|
77
298
|
minHeight: "[0]"
|
|
299
|
+
},
|
|
300
|
+
variants: {
|
|
301
|
+
mode: {
|
|
302
|
+
// Centred in whatever the overlay gives it, over the dark backdrop.
|
|
303
|
+
guided: {
|
|
304
|
+
alignItems: "center",
|
|
305
|
+
justifyContent: "center",
|
|
306
|
+
gap: "[12px]",
|
|
307
|
+
height: "[100%]"
|
|
308
|
+
},
|
|
309
|
+
// A reading column: slide above, panel below, both full width, centred in
|
|
310
|
+
// the overlay when the two do not fill it. Takes the overlay's full height
|
|
311
|
+
// rather than shrink-wrapping so that height is a definite number the two
|
|
312
|
+
// can be divided against — SlideFrame's cap below is a percentage of it.
|
|
313
|
+
//
|
|
314
|
+
// Ungapped on purpose: the slide and the panel are stitched into one card,
|
|
315
|
+
// each rounding only its outer corners (see SlideFrame and ControlPanel),
|
|
316
|
+
// and a gap here would put a stripe of scrim through the seam.
|
|
317
|
+
self_guided: {
|
|
318
|
+
alignItems: "stretch",
|
|
319
|
+
justifyContent: "center",
|
|
320
|
+
height: "[100%]"
|
|
321
|
+
}
|
|
322
|
+
}
|
|
78
323
|
}
|
|
79
324
|
});
|
|
80
325
|
Layout.displayName = "Layout";
|
|
81
326
|
const SlideFrame = styled("div", {
|
|
82
327
|
base: {
|
|
83
328
|
display: "flex",
|
|
84
|
-
flexDirection: "column",
|
|
85
|
-
alignItems: "center",
|
|
86
|
-
gap: "[10px]",
|
|
87
|
-
width: "[auto]",
|
|
88
|
-
height: "[auto]",
|
|
89
|
-
maxWidth: "[100%]",
|
|
90
|
-
maxHeight: "[100%]",
|
|
91
329
|
minHeight: "[0]"
|
|
330
|
+
},
|
|
331
|
+
variants: {
|
|
332
|
+
mode: {
|
|
333
|
+
// Shrink-wraps the slide at its intrinsic size; SlideClip inside does the
|
|
334
|
+
// white-strip clipping and carries the rounding.
|
|
335
|
+
guided: {
|
|
336
|
+
flexDirection: "column",
|
|
337
|
+
alignItems: "center",
|
|
338
|
+
gap: "[10px]",
|
|
339
|
+
width: "[auto]",
|
|
340
|
+
height: "[auto]",
|
|
341
|
+
maxWidth: "[100%]",
|
|
342
|
+
maxHeight: "[100%]"
|
|
343
|
+
},
|
|
344
|
+
// No SlideClip in this mode, so the frame itself clips the white strip and
|
|
345
|
+
// carries the rounding.
|
|
346
|
+
//
|
|
347
|
+
// Unshrinkable under a cap, which is what orders the squeeze: flex-shrink
|
|
348
|
+
// would take from both children at once, so the slide would start
|
|
349
|
+
// letterboxing while the panel still had room to give. Instead the frame is
|
|
350
|
+
// simply the smaller of the slide's natural height and everything the panel
|
|
351
|
+
// does not need, so the panel yields first and the slide only gives ground
|
|
352
|
+
// once the panel is at its floor. The subtrahend is that floor, the column
|
|
353
|
+
// being ungapped — change either and this has to follow. The floor itself
|
|
354
|
+
// moves with what the panel holds, which is what the variant below is for.
|
|
355
|
+
//
|
|
356
|
+
// Square along the bottom, where ControlPanel butts against it: the slide
|
|
357
|
+
// and its narration are one card, so only the pair's outer corners round.
|
|
358
|
+
// The slide's own bottom edge is what lands on that seam — the image is
|
|
359
|
+
// stretched to the frame's full height, so `contain` letterboxes it down
|
|
360
|
+
// the sides rather than under it, and it always reaches this edge.
|
|
361
|
+
//
|
|
362
|
+
// Squared for as long as there is a panel to meet, which is not the whole of
|
|
363
|
+
// this mode: the starting variant below has none and puts the rounding back.
|
|
364
|
+
//
|
|
365
|
+
// Deliberately unfilled: what the cap letterboxes shows the overlay's
|
|
366
|
+
// scrim, the same surround the slide has in guided mode.
|
|
367
|
+
self_guided: {
|
|
368
|
+
justifyContent: "center",
|
|
369
|
+
overflow: "hidden",
|
|
370
|
+
borderRadius: "lg",
|
|
371
|
+
borderBottomLeftRadius: "[0]",
|
|
372
|
+
borderBottomRightRadius: "[0]",
|
|
373
|
+
flexShrink: "0",
|
|
374
|
+
maxHeight: "[calc(100% - 124px)]"
|
|
375
|
+
}
|
|
376
|
+
},
|
|
377
|
+
// The panel's taller floor, subtracted the same way. Declared after `mode` so
|
|
378
|
+
// it wins the merge, and only ever true in self-guided, the one mode with a
|
|
379
|
+
// panel to subtract at all.
|
|
380
|
+
withVoiceSwitch: {
|
|
381
|
+
true: {
|
|
382
|
+
maxHeight: "[calc(100% - 172px)]"
|
|
383
|
+
}
|
|
384
|
+
},
|
|
385
|
+
// The panel is gone while the call starts, so the two things the self-guided
|
|
386
|
+
// frame owes it are gone with it. There is nothing to subtract — the caps
|
|
387
|
+
// above are arithmetic against a panel's contents, and holding 124px of empty
|
|
388
|
+
// scrim open under the slide is what inheriting one would do — and nothing
|
|
389
|
+
// butting against the bottom edge, so the slide is the whole card and every
|
|
390
|
+
// corner is an outer corner again. Both stated here rather than by touching
|
|
391
|
+
// the variants above, whose numbers belong to the state that has a panel.
|
|
392
|
+
//
|
|
393
|
+
// The bottom corners are named individually because that is how they were
|
|
394
|
+
// squared: a `borderRadius` shorthand cannot be relied on to beat the
|
|
395
|
+
// longhands above it, whichever order the variants are declared in. Through
|
|
396
|
+
// `token()` because only the shorthand takes the radii scale by name
|
|
397
|
+
// (panda.config's utilities), so this is `lg` said the long way round.
|
|
398
|
+
//
|
|
399
|
+
// `relative` for the scrim and the message, which are positioned against the
|
|
400
|
+
// slide. The slide's own width is unchanged either way, and for any slide
|
|
401
|
+
// wider than it is tall the width is what binds, so what the visitor sees
|
|
402
|
+
// across the hand-off is the panel going away rather than the slide resizing.
|
|
403
|
+
starting: {
|
|
404
|
+
true: {
|
|
405
|
+
position: "relative",
|
|
406
|
+
maxHeight: "[100%]",
|
|
407
|
+
borderRadius: "lg",
|
|
408
|
+
borderBottomLeftRadius: "[token(radii.lg)]",
|
|
409
|
+
borderBottomRightRadius: "[token(radii.lg)]"
|
|
410
|
+
}
|
|
411
|
+
}
|
|
92
412
|
}
|
|
93
413
|
});
|
|
94
414
|
SlideFrame.displayName = "SlideFrame";
|
|
415
|
+
// Dims the slide while the call starts, covering the frame it is positioned in.
|
|
416
|
+
const Scrim = styled("div", {
|
|
417
|
+
base: {
|
|
418
|
+
position: "absolute",
|
|
419
|
+
inset: "[0]",
|
|
420
|
+
backgroundColor: "[rgba(0, 0, 0, 0.5)]"
|
|
421
|
+
}
|
|
422
|
+
});
|
|
423
|
+
Scrim.displayName = "Scrim";
|
|
424
|
+
// Holds the starting message over the dimmed slide rather than under it. Under it
|
|
425
|
+
// would need a strip of chrome to sit on, and that strip is the panel this state
|
|
426
|
+
// exists without. Boxless past the positioning: Status' `pill` variant fills and
|
|
427
|
+
// centres itself.
|
|
428
|
+
const Progress = styled("div", {
|
|
429
|
+
base: {
|
|
430
|
+
position: "absolute",
|
|
431
|
+
inset: "[0]",
|
|
432
|
+
display: "flex"
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
Progress.displayName = "Progress";
|
|
95
436
|
// Clips the ~2px white strip baked into the top of each rendered PDF page (a
|
|
96
437
|
// Ghostscript rasterization artifact). Holds the rounded corners + shadow so
|
|
97
|
-
// the scaled image inside is clipped cleanly.
|
|
438
|
+
// the scaled image inside is clipped cleanly. Guided mode only.
|
|
98
439
|
const SlideClip = styled("div", {
|
|
99
440
|
base: {
|
|
100
441
|
display: "flex",
|
|
@@ -109,7 +450,7 @@ const SlideClip = styled("div", {
|
|
|
109
450
|
SlideClip.displayName = "SlideClip";
|
|
110
451
|
// Shrink-wraps the slide image so the point-at-screen zoom (slideZoomStyle, applied imperatively in
|
|
111
452
|
// Deck's effect) scales/clips the image in place without touching SlideClip's shadow + rounding or
|
|
112
|
-
// the image's own white-strip transform.
|
|
453
|
+
// the image's own white-strip transform. Guided mode only — see the effect's comment.
|
|
113
454
|
const SlideZoomLayer = styled("div", {
|
|
114
455
|
base: {
|
|
115
456
|
display: "flex",
|
|
@@ -121,37 +462,150 @@ const SlideZoomLayer = styled("div", {
|
|
|
121
462
|
SlideZoomLayer.displayName = "SlideZoomLayer";
|
|
122
463
|
const SlideImage = styled("img", {
|
|
123
464
|
base: {
|
|
124
|
-
width: "[auto]",
|
|
125
465
|
height: "[auto]",
|
|
126
466
|
maxWidth: "[100%]",
|
|
127
467
|
maxHeight: "[100%]",
|
|
128
468
|
minHeight: "[0]",
|
|
129
469
|
objectFit: "contain",
|
|
130
470
|
display: "block",
|
|
131
|
-
// Scale up a hair from the bottom so the top edge is pushed past
|
|
132
|
-
// overflow, hiding the baked-in white strip. Scales with
|
|
471
|
+
// Scale up a hair from the bottom so the top edge is pushed past the
|
|
472
|
+
// clipping ancestor's overflow, hiding the baked-in white strip. Scales with
|
|
473
|
+
// any display size.
|
|
133
474
|
transform: "[scale(1.004)]",
|
|
134
475
|
transformOrigin: "[bottom]"
|
|
476
|
+
},
|
|
477
|
+
variants: {
|
|
478
|
+
mode: {
|
|
479
|
+
// Intrinsic size, so the slide is never upscaled past its own resolution.
|
|
480
|
+
guided: {
|
|
481
|
+
width: "[auto]"
|
|
482
|
+
},
|
|
483
|
+
// Fills the reading column, whatever the slide's own size, leaving the base's
|
|
484
|
+
// `contain` to handle the one case where the frame is capped below the
|
|
485
|
+
// slide's natural height: it scales the whole slide down to fit rather than
|
|
486
|
+
// filling the frame with a piece of it.
|
|
487
|
+
self_guided: {
|
|
488
|
+
width: "[100%]"
|
|
489
|
+
}
|
|
490
|
+
}
|
|
135
491
|
}
|
|
136
492
|
});
|
|
137
493
|
SlideImage.displayName = "SlideImage";
|
|
138
|
-
|
|
494
|
+
// The white panel under the slide, holding the narration and the controls.
|
|
495
|
+
// Self-guided mode only.
|
|
496
|
+
//
|
|
497
|
+
// Gives up its height to the slide before the slide gives up any, the caption
|
|
498
|
+
// scrolling inside whatever is left. The floor is what the panel is still worth
|
|
499
|
+
// reading at: $lg of padding twice, $lg of gap, the 32px control row, and two
|
|
500
|
+
// 22px lines of caption under it — 124px. The voice-switch row adds a second $lg
|
|
501
|
+
// gap and a second 32px row, taking the floor to 172px. The arrows and the switch
|
|
502
|
+
// are outside the caption's scroll box, so they have to be counted here rather
|
|
503
|
+
// than left to scroll. Stating a floor at all also replaces the automatic
|
|
504
|
+
// minimum, which would otherwise pin this to its content and let nothing shrink.
|
|
505
|
+
//
|
|
506
|
+
// Square along the top, the other half of the stitch SlideFrame makes: the two
|
|
507
|
+
// meet flush, so the rounding belongs on the pair's outer corners only.
|
|
508
|
+
const ControlPanel = styled("div", {
|
|
139
509
|
base: {
|
|
140
510
|
display: "flex",
|
|
141
|
-
|
|
511
|
+
flexDirection: "column",
|
|
512
|
+
gap: "$lg",
|
|
513
|
+
padding: "$lg",
|
|
514
|
+
borderRadius: "lg",
|
|
515
|
+
borderTopLeftRadius: "[0]",
|
|
516
|
+
borderTopRightRadius: "[0]",
|
|
517
|
+
backgroundColor: "$white.solid",
|
|
518
|
+
minHeight: "[124px]"
|
|
519
|
+
},
|
|
520
|
+
variants: {
|
|
521
|
+
// Conditional, so the floor is too: fixed at the taller number it would hold
|
|
522
|
+
// 48px of empty panel open in text-only mode, where the switch never appears
|
|
523
|
+
// and the slide could have had the room.
|
|
524
|
+
withVoiceSwitch: {
|
|
525
|
+
true: {
|
|
526
|
+
minHeight: "[172px]"
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
});
|
|
531
|
+
ControlPanel.displayName = "ControlPanel";
|
|
532
|
+
const Caption = styled("div", {
|
|
533
|
+
base: {
|
|
534
|
+
fontFamily: "$inter",
|
|
535
|
+
fontSize: "[14px]",
|
|
536
|
+
lineHeight: "[22px]",
|
|
537
|
+
color: "$text.dark",
|
|
538
|
+
flexGrow: "1",
|
|
539
|
+
overflowY: "auto",
|
|
540
|
+
// Spelt out rather than left to `overflow` to zero the automatic minimum:
|
|
541
|
+
// this is the only thing standing between a long talk track and a panel that
|
|
542
|
+
// refuses to shrink, taking the scrolling with it.
|
|
543
|
+
minHeight: "[0]",
|
|
544
|
+
"&:empty": {
|
|
545
|
+
display: "none"
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
});
|
|
549
|
+
Caption.displayName = "Caption";
|
|
550
|
+
// Outside the caption's scroll box, so squeezing the panel scrolls the narration
|
|
551
|
+
// rather than carrying the arrows off with it.
|
|
552
|
+
const Controls = styled("div", {
|
|
553
|
+
base: {
|
|
554
|
+
display: "flex",
|
|
555
|
+
alignItems: "center",
|
|
556
|
+
justifyContent: "space-between",
|
|
557
|
+
gap: "$md",
|
|
142
558
|
flexShrink: "0"
|
|
143
559
|
}
|
|
144
560
|
});
|
|
561
|
+
Controls.displayName = "Controls";
|
|
562
|
+
const ProgressDots = styled("div", {
|
|
563
|
+
base: {
|
|
564
|
+
display: "flex"
|
|
565
|
+
},
|
|
566
|
+
variants: {
|
|
567
|
+
mode: {
|
|
568
|
+
// Sits alone under the slide.
|
|
569
|
+
guided: {
|
|
570
|
+
gap: "[8px]",
|
|
571
|
+
flexShrink: "0"
|
|
572
|
+
},
|
|
573
|
+
// Sits between the two nav buttons, and wraps rather than pushing them
|
|
574
|
+
// apart when the deck is long.
|
|
575
|
+
self_guided: {
|
|
576
|
+
alignItems: "center",
|
|
577
|
+
justifyContent: "center",
|
|
578
|
+
gap: "$sm",
|
|
579
|
+
flexWrap: "wrap"
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
});
|
|
145
584
|
ProgressDots.displayName = "ProgressDots";
|
|
146
585
|
const Dot = styled("span", {
|
|
147
586
|
base: {
|
|
148
587
|
width: "[8px]",
|
|
149
588
|
height: "[8px]",
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
589
|
+
transition: "[background-color 0.2s ease]"
|
|
590
|
+
},
|
|
591
|
+
variants: {
|
|
592
|
+
mode: {
|
|
593
|
+
// On the dark backdrop, so white at two opacities.
|
|
594
|
+
guided: {
|
|
595
|
+
borderRadius: "[50%]",
|
|
596
|
+
backgroundColor: "[rgba(255, 255, 255, 0.4)]",
|
|
597
|
+
"&[data-active='true']": {
|
|
598
|
+
backgroundColor: "[rgba(255, 255, 255, 0.95)]"
|
|
599
|
+
}
|
|
600
|
+
},
|
|
601
|
+
// On the white panel, so the theme's grey and primary.
|
|
602
|
+
self_guided: {
|
|
603
|
+
borderRadius: "full",
|
|
604
|
+
backgroundColor: "$gray.border",
|
|
605
|
+
"&[data-active='true']": {
|
|
606
|
+
backgroundColor: "$primary"
|
|
607
|
+
}
|
|
608
|
+
}
|
|
155
609
|
}
|
|
156
610
|
}
|
|
157
611
|
});
|