@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,14 +1,36 @@
|
|
|
1
1
|
import { __webpack_require__ } from "../../../js/runtime.js";
|
|
2
2
|
import { signals_core_module_E, signals_core_module_d, signals_core_module_w } from "../../../js/multimodal_v2.js";
|
|
3
|
+
import { DeckMode as types_DeckMode } from "../types.js";
|
|
3
4
|
import "../../utils/native_globals.js";
|
|
4
5
|
|
|
5
6
|
/* provided dependency */ var decks_store_setTimeout = __webpack_require__(68034).pS;
|
|
6
7
|
/* provided dependency */ var decks_store_clearTimeout = __webpack_require__(68034).Kj;
|
|
8
|
+
function _extends() {
|
|
9
|
+
_extends = Object.assign || function(target) {
|
|
10
|
+
for(var i = 1; i < arguments.length; i++){
|
|
11
|
+
var source = arguments[i];
|
|
12
|
+
for(var key in source){
|
|
13
|
+
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
14
|
+
target[key] = source[key];
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return target;
|
|
19
|
+
};
|
|
20
|
+
return _extends.apply(this, arguments);
|
|
21
|
+
}
|
|
22
|
+
|
|
7
23
|
|
|
8
24
|
// Floor for how long a slide stays up before auto-advancing. A slide whose
|
|
9
25
|
// talk-track generation failed has an estimated duration of 0; without a floor
|
|
10
26
|
// setTimeout(…, 0) would flash through the whole sequence instantly.
|
|
27
|
+
// An absent estimate is legacy unknown timing and is not auto-completed.
|
|
11
28
|
const MIN_SLIDE_DURATION_MS = 2000;
|
|
29
|
+
// Matches the server-side deck-ready wait. If Coffee never reports a silent
|
|
30
|
+
// boundary (for example on an older/partial client where the lead-in flows
|
|
31
|
+
// directly into narration), start timing when the server releases the tool on
|
|
32
|
+
// its timeout rather than leaving the deck frozen forever.
|
|
33
|
+
const DECK_READY_TIMEOUT_MS = 3000;
|
|
12
34
|
class DecksStore {
|
|
13
35
|
dispose() {
|
|
14
36
|
this.unsubscribeArtifact == null ? void 0 : this.unsubscribeArtifact.call(this);
|
|
@@ -17,13 +39,46 @@ class DecksStore {
|
|
|
17
39
|
this.disposeInterruptEffect = null;
|
|
18
40
|
this.disposeTextInterruptEffect == null ? void 0 : this.disposeTextInterruptEffect.call(this);
|
|
19
41
|
this.disposeTextInterruptEffect = null;
|
|
42
|
+
this.disposeNarrationStartEffect == null ? void 0 : this.disposeNarrationStartEffect.call(this);
|
|
43
|
+
this.disposeNarrationStartEffect = null;
|
|
44
|
+
this.disposeDeckReadyEffect == null ? void 0 : this.disposeDeckReadyEffect.call(this);
|
|
45
|
+
this.disposeDeckReadyEffect = null;
|
|
46
|
+
this.clearPendingDeckReady();
|
|
47
|
+
this.clearNarrationStart();
|
|
20
48
|
this.clearAdvanceTimer();
|
|
49
|
+
// The starting state's watch and deadline are armed by the artifact store but
|
|
50
|
+
// are waiting on state that lives here, so they go down with it.
|
|
51
|
+
this.rootStore.artifactStore.stopWatchingDeckPresentation();
|
|
52
|
+
}
|
|
53
|
+
// Called by the guided deck image's load event. Send at most one readiness
|
|
54
|
+
// acknowledgement per presentation message; later slide loads do not gate the
|
|
55
|
+
// already-running narration turn.
|
|
56
|
+
notifyCurrentSlideReady() {
|
|
57
|
+
var _this_deckReadyDeadlineAt;
|
|
58
|
+
const message = this.rootStore.artifact.message.peek();
|
|
59
|
+
const deck = this.currentDeck.peek();
|
|
60
|
+
if (!message || message.type !== "present_deck" || message.navigationOnly || !message.syncNarration || this.stopped.peek()) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (!deck || deck.mode !== types_DeckMode.Guided || deck.sequencePosition !== 0) return;
|
|
64
|
+
if (this.readyMessageIds.has(message.id)) return;
|
|
65
|
+
this.clearDeckReadyFallbackTimer();
|
|
66
|
+
const fallbackDelay = Math.max(0, ((_this_deckReadyDeadlineAt = this.deckReadyDeadlineAt) != null ? _this_deckReadyDeadlineAt : Date.now() + DECK_READY_TIMEOUT_MS) - Date.now());
|
|
67
|
+
this.deckReadyFallbackTimerId = decks_store_setTimeout(()=>{
|
|
68
|
+
var _ref;
|
|
69
|
+
var _this_rootStore_callStore_value_isBotSpeaking, _this_rootStore_callStore_value;
|
|
70
|
+
if (this.pendingReadyMessageId.peek() !== message.id || this.stopped.peek()) return;
|
|
71
|
+
const speaking = (_ref = (_this_rootStore_callStore_value = this.rootStore.callStore.value) == null ? void 0 : (_this_rootStore_callStore_value_isBotSpeaking = _this_rootStore_callStore_value.isBotSpeaking) == null ? void 0 : _this_rootStore_callStore_value_isBotSpeaking.peek()) != null ? _ref : false;
|
|
72
|
+
this.releaseDeckReady(message.id, speaking);
|
|
73
|
+
}, fallbackDelay);
|
|
74
|
+
this.pendingReadyMessageId.value = message.id;
|
|
21
75
|
}
|
|
22
76
|
// Show a single slide statically (visitor clicked it in the transcript record).
|
|
23
77
|
// Set the revisit target before the artifact overlay opens so the artifact
|
|
24
78
|
// subscription treats the open as a revisit rather than a fresh presentation.
|
|
25
79
|
prepareRevisit(messageId, slideIndex, pageTitle) {
|
|
26
80
|
this.clearAdvanceTimer();
|
|
81
|
+
this.clearSelfGuided();
|
|
27
82
|
this.stopped.value = true;
|
|
28
83
|
this.revisitMessageId = messageId;
|
|
29
84
|
// Set the title before the index signal, which triggers the currentDeck recompute.
|
|
@@ -33,11 +88,55 @@ class DecksStore {
|
|
|
33
88
|
isRevisiting(messageId) {
|
|
34
89
|
return this.revisitMessageId === messageId && this.revisitIndex.peek() != null;
|
|
35
90
|
}
|
|
91
|
+
// Hand the deck over to the visitor: they step through it themselves from the
|
|
92
|
+
// interactive viewer. Like prepareRevisit this is set BEFORE the artifact
|
|
93
|
+
// overlay opens, so the artifact subscription treats the open as self-guided
|
|
94
|
+
// rather than a fresh presentation. `stopped` keeps the interruption effects
|
|
95
|
+
// inert — there is no narration to barge in on.
|
|
96
|
+
//
|
|
97
|
+
// Deliberately never auto-advances, unlike the guided path next door: the
|
|
98
|
+
// visitor sets the pace, and a timer would fight the arrows they are using.
|
|
99
|
+
prepareSelfGuidedPlayback(messageId, startIndex) {
|
|
100
|
+
if (startIndex === void 0) startIndex = 0;
|
|
101
|
+
this.clearAdvanceTimer();
|
|
102
|
+
this.clearRevisit();
|
|
103
|
+
this.stopped.value = true;
|
|
104
|
+
this.selfGuidedMessageId = messageId;
|
|
105
|
+
this.selfGuidedIndex.value = Math.max(startIndex, 0);
|
|
106
|
+
}
|
|
107
|
+
isSelfGuided(messageId) {
|
|
108
|
+
return this.selfGuidedMessageId === messageId && this.selfGuidedIndex.peek() != null;
|
|
109
|
+
}
|
|
110
|
+
// The visitor has handed the deck to Piper and the call is starting. Holds the
|
|
111
|
+
// slide they were on with the viewer's controls dropped, which is the whole of
|
|
112
|
+
// the state — see CurrentDeck#starting.
|
|
113
|
+
//
|
|
114
|
+
// Self-guided underneath, with the same index it always had: the deck is still
|
|
115
|
+
// in the visitor's hands until Piper's own share replaces this message, and
|
|
116
|
+
// going through the same preparation means the path in from the timeline card
|
|
117
|
+
// and the path in from the viewer arrive at the same place. From the viewer that
|
|
118
|
+
// makes this a no-op plus a flag — it is already this message, at this index.
|
|
119
|
+
prepareStartingPresentation(messageId, startIndex) {
|
|
120
|
+
if (startIndex === void 0) startIndex = 0;
|
|
121
|
+
this.prepareSelfGuidedPlayback(messageId, startIndex);
|
|
122
|
+
this.startingMessageId.value = messageId;
|
|
123
|
+
}
|
|
124
|
+
// Give the deck back to the visitor, leaving them on the slide they handed over
|
|
125
|
+
// — the whole of what undoing this state means, since the viewer underneath was
|
|
126
|
+
// never dismantled. Piper arriving does not come through here: her share is a
|
|
127
|
+
// different message, and the artifact change clears this along with the rest of
|
|
128
|
+
// the visitor's playback state.
|
|
129
|
+
cancelStartingPresentation() {
|
|
130
|
+
this.startingMessageId.value = null;
|
|
131
|
+
}
|
|
132
|
+
goToNextSlide() {
|
|
133
|
+
this.stepSlide(1);
|
|
134
|
+
}
|
|
135
|
+
goToPreviousSlide() {
|
|
136
|
+
this.stepSlide(-1);
|
|
137
|
+
}
|
|
36
138
|
// Fire-and-forget the current on-screen slide + the full sequence to the backend,
|
|
37
|
-
// which computes the remaining slides and seeds the agent's context.
|
|
38
|
-
// return in handleInterruption (guarding on `stopped`) makes this fire exactly
|
|
39
|
-
// once per guided run; a new deck message resets `stopped` so a later resume can
|
|
40
|
-
// be interrupted and re-report.
|
|
139
|
+
// which computes the remaining slides and seeds the agent's context.
|
|
41
140
|
notifyInterruption(message) {
|
|
42
141
|
var _ref, _message_slides;
|
|
43
142
|
var _message_slides1;
|
|
@@ -52,22 +151,34 @@ class DecksStore {
|
|
|
52
151
|
});
|
|
53
152
|
}
|
|
54
153
|
scheduleAdvance(slides) {
|
|
55
|
-
var
|
|
56
|
-
|
|
57
|
-
if (this.stopped.value) return; // visitor interrupted — do not advance
|
|
154
|
+
var _currentSlide_estimatedDurationMs;
|
|
155
|
+
if (this.stopped.value || slides.length === 0) return;
|
|
58
156
|
const position = this.playbackIndex.value;
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
157
|
+
const currentSlide = slides[position];
|
|
158
|
+
if (!currentSlide) return;
|
|
159
|
+
if (position >= slides.length - 1 && currentSlide.estimatedDurationMs === undefined) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
const durationMs = Math.max((_currentSlide_estimatedDurationMs = currentSlide.estimatedDurationMs) != null ? _currentSlide_estimatedDurationMs : MIN_SLIDE_DURATION_MS, MIN_SLIDE_DURATION_MS);
|
|
163
|
+
if (position < slides.length - 1) {
|
|
164
|
+
var _slides_;
|
|
165
|
+
// Warm the browser cache for the next slide's image now, while the current one
|
|
166
|
+
// is on screen, so the auto-advance swap doesn't flash a blank/stale frame
|
|
167
|
+
// while the full-res PDF page downloads (narration keeps pace regardless).
|
|
168
|
+
this.preloadImage((_slides_ = slides[position + 1]) == null ? void 0 : _slides_.imageUrl);
|
|
169
|
+
}
|
|
67
170
|
this.advanceTimerId = decks_store_setTimeout(()=>{
|
|
68
171
|
this.advanceTimerId = null;
|
|
69
172
|
if (this.stopped.value) return;
|
|
70
|
-
|
|
173
|
+
if (position >= slides.length - 1) {
|
|
174
|
+
this.rootStore.presentationTelemetryStore.onDeckFinalDisplayIntervalComplete();
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
this.playbackIndex.value = position + 1;
|
|
178
|
+
const nextSlide = slides[position + 1];
|
|
179
|
+
if (nextSlide) {
|
|
180
|
+
this.rootStore.presentationTelemetryStore.onDeckSlideRendered(nextSlide.pageId, nextSlide.slideIndex, nextSlide.pageTitle);
|
|
181
|
+
}
|
|
71
182
|
this.scheduleAdvance(slides);
|
|
72
183
|
}, durationMs);
|
|
73
184
|
}
|
|
@@ -76,12 +187,71 @@ class DecksStore {
|
|
|
76
187
|
this.revisitIndex.value = null;
|
|
77
188
|
this.revisitPageTitle = undefined;
|
|
78
189
|
}
|
|
190
|
+
clearSelfGuided() {
|
|
191
|
+
this.selfGuidedMessageId = null;
|
|
192
|
+
this.selfGuidedIndex.value = null;
|
|
193
|
+
// The starting state is a state of self-guided playback, so it cannot outlive
|
|
194
|
+
// it. This is what makes Piper's arrival the end of it for free: her share is
|
|
195
|
+
// a new message, and handling it clears the visitor's playback wholesale.
|
|
196
|
+
this.startingMessageId.value = null;
|
|
197
|
+
}
|
|
198
|
+
// Move one slide, stopping at either end. Wrapping would silently restart the
|
|
199
|
+
// deck; a disabled arrow says what happened.
|
|
200
|
+
stepSlide(delta) {
|
|
201
|
+
var _ref;
|
|
202
|
+
var _this_currentDeck_peek;
|
|
203
|
+
const current = this.selfGuidedIndex.peek();
|
|
204
|
+
if (current == null) return;
|
|
205
|
+
const length = (_ref = (_this_currentDeck_peek = this.currentDeck.peek()) == null ? void 0 : _this_currentDeck_peek.slideImageUrls.length) != null ? _ref : 0;
|
|
206
|
+
const next = current + delta;
|
|
207
|
+
if (next < 0 || next >= length) return;
|
|
208
|
+
this.selfGuidedIndex.value = next;
|
|
209
|
+
}
|
|
79
210
|
clearAdvanceTimer() {
|
|
80
211
|
if (this.advanceTimerId !== null) {
|
|
81
212
|
decks_store_clearTimeout(this.advanceTimerId);
|
|
82
213
|
this.advanceTimerId = null;
|
|
83
214
|
}
|
|
84
215
|
}
|
|
216
|
+
releaseDeckReady(messageId, continuousSpeechFallback) {
|
|
217
|
+
if (this.pendingReadyMessageId.peek() !== messageId) return;
|
|
218
|
+
this.clearPendingDeckReady();
|
|
219
|
+
if (this.readyMessageIds.has(messageId)) return;
|
|
220
|
+
this.readyMessageIds.add(messageId);
|
|
221
|
+
if (continuousSpeechFallback) {
|
|
222
|
+
// The server releases present_deck after the same timeout. With no usable
|
|
223
|
+
// silence/sequence boundary, this is the closest observable narration
|
|
224
|
+
// anchor and, critically, prevents permanent first-slide stalls.
|
|
225
|
+
this.clearNarrationStart();
|
|
226
|
+
this.scheduleAdvance(this.sequence.peek());
|
|
227
|
+
} else {
|
|
228
|
+
var _ref;
|
|
229
|
+
var _this_rootStore_callStore_value_voiceAnswerSequence, _this_rootStore_callStore_value;
|
|
230
|
+
// Snapshot only after the slide is visible and the lead-in has stopped.
|
|
231
|
+
// Any delayed preamble marker received before this point becomes part of
|
|
232
|
+
// the baseline rather than being mistaken for the narration response.
|
|
233
|
+
this.narrationAnswerBaseline = (_ref = (_this_rootStore_callStore_value = this.rootStore.callStore.value) == null ? void 0 : (_this_rootStore_callStore_value_voiceAnswerSequence = _this_rootStore_callStore_value.voiceAnswerSequence) == null ? void 0 : _this_rootStore_callStore_value_voiceAnswerSequence.peek()) != null ? _ref : null;
|
|
234
|
+
this.awaitingNarrationStart.value = true;
|
|
235
|
+
}
|
|
236
|
+
this.rootStore.channel.perform("notify_deck_ready", {
|
|
237
|
+
presentation_message_id: messageId
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
clearDeckReadyFallbackTimer() {
|
|
241
|
+
if (this.deckReadyFallbackTimerId !== null) {
|
|
242
|
+
decks_store_clearTimeout(this.deckReadyFallbackTimerId);
|
|
243
|
+
this.deckReadyFallbackTimerId = null;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
clearPendingDeckReady() {
|
|
247
|
+
this.pendingReadyMessageId.value = null;
|
|
248
|
+
this.deckReadyDeadlineAt = null;
|
|
249
|
+
this.clearDeckReadyFallbackTimer();
|
|
250
|
+
}
|
|
251
|
+
clearNarrationStart() {
|
|
252
|
+
this.awaitingNarrationStart.value = false;
|
|
253
|
+
this.narrationAnswerBaseline = null;
|
|
254
|
+
}
|
|
85
255
|
// Kick off a background fetch of an image URL so it's cached before it's shown. Crossorigin
|
|
86
256
|
// like every other slide load, so this warm-up can't be the one that caches an ACAO-less
|
|
87
257
|
// response and poisons the entry the visible <img> needs.
|
|
@@ -97,7 +267,7 @@ class DecksStore {
|
|
|
97
267
|
this.rootStore = rootStore;
|
|
98
268
|
// Derived state: the deck slide currently presented in the ARTIFACT overlay.
|
|
99
269
|
//
|
|
100
|
-
// A single
|
|
270
|
+
// A single present_deck tool call can present one slide or an auto-advancing
|
|
101
271
|
// sequence. The slide on screen is `slides[playbackIndex]`; for legacy/single
|
|
102
272
|
// messages the index comes straight from the message.
|
|
103
273
|
this.currentDeck = signals_core_module_w(()=>{
|
|
@@ -108,11 +278,43 @@ class DecksStore {
|
|
|
108
278
|
const slideImageUrls = ((_message_slideImageUrls = message.slideImageUrls) == null ? void 0 : _message_slideImageUrls.length) > 0 ? message.slideImageUrls : [
|
|
109
279
|
message.imageUrl
|
|
110
280
|
];
|
|
281
|
+
// Self-guided: the visitor opened the deck from its timeline card and steps
|
|
282
|
+
// through it themselves. The whole deck is in scope — every image the
|
|
283
|
+
// message carries, not just the slides this share presented — so position
|
|
284
|
+
// and length come from slideImageUrls.
|
|
285
|
+
//
|
|
286
|
+
// Only ever for the share the visitor is actually driving. The position is
|
|
287
|
+
// theirs and so is the message it is a position in, so a different message
|
|
288
|
+
// being on screen means this branch is not what the deck is, whatever is left
|
|
289
|
+
// in the position. `handleArtifactMessageChange` clears that leftover a moment
|
|
290
|
+
// later; checking the id means nothing can observe the gap, where before this
|
|
291
|
+
// branch would answer for Piper's share with the visitor's chrome — and with
|
|
292
|
+
// `starting` false, since that is keyed to the visitor's message too. The
|
|
293
|
+
// field is read rather than subscribed to because it never changes without
|
|
294
|
+
// `selfGuidedIndex` changing in the same breath, which is what recomputes this.
|
|
295
|
+
const selfGuidedIndex = this.selfGuidedIndex.value;
|
|
296
|
+
if (selfGuidedIndex != null && message.id === this.selfGuidedMessageId) {
|
|
297
|
+
var _ref3;
|
|
298
|
+
const currentIndex = clampIndex(selfGuidedIndex, slideImageUrls.length);
|
|
299
|
+
const slide = this.presentedSlides.value.get(currentIndex);
|
|
300
|
+
return {
|
|
301
|
+
deckId: message.deckId,
|
|
302
|
+
slideImageUrls,
|
|
303
|
+
currentIndex,
|
|
304
|
+
sequencePosition: currentIndex,
|
|
305
|
+
sequenceLength: slideImageUrls.length,
|
|
306
|
+
pageTitle: (_ref3 = slide == null ? void 0 : slide.pageTitle) != null ? _ref3 : message.pageTitle,
|
|
307
|
+
deckTitle: message.deckTitle,
|
|
308
|
+
mode: types_DeckMode.SelfGuided,
|
|
309
|
+
starting: this.startingMessageId.value === message.id,
|
|
310
|
+
caption: slide == null ? void 0 : slide.talkTrack
|
|
311
|
+
};
|
|
312
|
+
}
|
|
111
313
|
// Revisit: the visitor clicked a slide in the transcript record. Show that
|
|
112
314
|
// single slide statically (no auto-advance, no progress dots).
|
|
113
315
|
const revisitIndex = this.revisitIndex.value;
|
|
114
316
|
if (revisitIndex != null) {
|
|
115
|
-
var
|
|
317
|
+
var _ref4, _this_revisitPageTitle;
|
|
116
318
|
var _message_slides_find, _message_slides;
|
|
117
319
|
return {
|
|
118
320
|
deckId: message.deckId,
|
|
@@ -120,8 +322,10 @@ class DecksStore {
|
|
|
120
322
|
currentIndex: clampIndex(revisitIndex, slideImageUrls.length),
|
|
121
323
|
sequencePosition: 0,
|
|
122
324
|
sequenceLength: 1,
|
|
123
|
-
pageTitle: (
|
|
124
|
-
deckTitle: message.deckTitle
|
|
325
|
+
pageTitle: (_ref4 = (_this_revisitPageTitle = this.revisitPageTitle) != null ? _this_revisitPageTitle : (_message_slides = message.slides) == null ? void 0 : (_message_slides_find = _message_slides.find((slide)=>slide.slideIndex === revisitIndex)) == null ? void 0 : _message_slides_find.pageTitle) != null ? _ref4 : message.pageTitle,
|
|
326
|
+
deckTitle: message.deckTitle,
|
|
327
|
+
mode: types_DeckMode.Guided,
|
|
328
|
+
starting: false
|
|
125
329
|
};
|
|
126
330
|
}
|
|
127
331
|
const sequence = this.sequence.value;
|
|
@@ -135,7 +339,9 @@ class DecksStore {
|
|
|
135
339
|
sequencePosition: position,
|
|
136
340
|
sequenceLength: sequence.length,
|
|
137
341
|
pageTitle: (_ref2 = slide == null ? void 0 : slide.pageTitle) != null ? _ref2 : message.pageTitle,
|
|
138
|
-
deckTitle: message.deckTitle
|
|
342
|
+
deckTitle: message.deckTitle,
|
|
343
|
+
mode: types_DeckMode.Guided,
|
|
344
|
+
starting: false
|
|
139
345
|
};
|
|
140
346
|
});
|
|
141
347
|
// Everything below is private
|
|
@@ -145,6 +351,11 @@ class DecksStore {
|
|
|
145
351
|
this.stopped = signals_core_module_d(false);
|
|
146
352
|
this.advanceTimerId = null;
|
|
147
353
|
this.lastHandledMessageId = null;
|
|
354
|
+
// The last guided sequence for the deck on screen. A go_to call arrives as a
|
|
355
|
+
// new one-slide navigation message, but it is still movement within this
|
|
356
|
+
// presentation. Retain the sequence so the progress dots remain oriented
|
|
357
|
+
// while auto-advance stays stopped on the requested slide.
|
|
358
|
+
this.presentationSequence = signals_core_module_d(null);
|
|
148
359
|
// Set when the visitor clicks a slide in the transcript record to revisit it.
|
|
149
360
|
// While set, `currentDeck` shows that single slide statically. Cleared when a
|
|
150
361
|
// new/different deck message becomes the active artifact.
|
|
@@ -154,9 +365,25 @@ class DecksStore {
|
|
|
154
365
|
// revisit shows the right title even when the active message (the last share)
|
|
155
366
|
// doesn't contain that slide. Cleared alongside the revisit state.
|
|
156
367
|
this.revisitPageTitle = undefined;
|
|
368
|
+
// 0-based position within the whole deck while the visitor drives it. Null
|
|
369
|
+
// whenever playback is agent-driven.
|
|
370
|
+
this.selfGuidedIndex = signals_core_module_d(null);
|
|
371
|
+
this.selfGuidedMessageId = null;
|
|
372
|
+
// The self-guided share whose call is starting, or null. Always one of the
|
|
373
|
+
// above — cleared with them by clearSelfGuided, so no artifact change can leave
|
|
374
|
+
// it behind.
|
|
375
|
+
this.startingMessageId = signals_core_module_d(null);
|
|
157
376
|
this.unsubscribeArtifact = null;
|
|
158
377
|
this.disposeInterruptEffect = null;
|
|
159
378
|
this.disposeTextInterruptEffect = null;
|
|
379
|
+
this.disposeNarrationStartEffect = null;
|
|
380
|
+
this.disposeDeckReadyEffect = null;
|
|
381
|
+
this.awaitingNarrationStart = signals_core_module_d(false);
|
|
382
|
+
this.narrationAnswerBaseline = null;
|
|
383
|
+
this.pendingReadyMessageId = signals_core_module_d(null);
|
|
384
|
+
this.deckReadyFallbackTimerId = null;
|
|
385
|
+
this.deckReadyDeadlineAt = null;
|
|
386
|
+
this.readyMessageIds = new Set();
|
|
160
387
|
// Id of the most recent visitor text message we've already accounted for, so a
|
|
161
388
|
// text-mode interruption only fires for a genuinely new message.
|
|
162
389
|
this.lastVisitorTextMessageId = null;
|
|
@@ -164,27 +391,79 @@ class DecksStore {
|
|
|
164
391
|
// synthesized one-item sequence for legacy messages without a `slides` array.
|
|
165
392
|
this.sequence = signals_core_module_w(()=>{
|
|
166
393
|
var _message_slideIndex;
|
|
167
|
-
var
|
|
394
|
+
var _messageSlides_;
|
|
168
395
|
const message = this.rootStore.artifact.message.value;
|
|
169
396
|
if (!message || message.type !== "present_deck") return [];
|
|
170
|
-
|
|
171
|
-
return
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
397
|
+
const messageSlides = slidesForMessage(message);
|
|
398
|
+
if (!message.navigationOnly) return messageSlides;
|
|
399
|
+
const currentSlideIndex = (_message_slideIndex = message.slideIndex) != null ? _message_slideIndex : (_messageSlides_ = messageSlides[0]) == null ? void 0 : _messageSlides_.slideIndex;
|
|
400
|
+
const preserved = this.presentationSequence.value;
|
|
401
|
+
if ((preserved == null ? void 0 : preserved.deckId) === message.deckId && currentSlideIndex !== undefined && preserved.slides.some((slide)=>slide.slideIndex === currentSlideIndex)) {
|
|
402
|
+
return preserved.slides;
|
|
403
|
+
}
|
|
404
|
+
return messageSlides;
|
|
405
|
+
});
|
|
406
|
+
// Every slide of the active deck the widget can caption, keyed by slide index.
|
|
407
|
+
//
|
|
408
|
+
// A presentation can span several `present_deck` messages — the initial share
|
|
409
|
+
// plus any resume after an interruption, where the agent re-shares only the
|
|
410
|
+
// remaining slides. Merging them oldest-first means the latest share wins for a
|
|
411
|
+
// slide covered twice, so a slide's caption is the narration actually delivered
|
|
412
|
+
// on the resumed run.
|
|
413
|
+
//
|
|
414
|
+
// `deckPages` goes underneath as the base layer. Every share carries a seed
|
|
415
|
+
// caption for every page of the deck, so a visitor stepping past the slides the
|
|
416
|
+
// agent actually selected still gets a caption instead of a blank panel — the
|
|
417
|
+
// card's slide count promises the whole deck. `slides` layered on top means a
|
|
418
|
+
// slide that was presented shows what she said rather than the seed.
|
|
419
|
+
//
|
|
420
|
+
// The top layer often has no caption of its own, and the seed shows through
|
|
421
|
+
// rather than the slide blanking out. That happens for an off-call share, which
|
|
422
|
+
// narrates nothing, and for a slide still being narrated, whose talk track the
|
|
423
|
+
// server withholds until the turn ends (Message::PresentDeckSerializer#slides).
|
|
424
|
+
this.presentedSlides = signals_core_module_w(()=>{
|
|
425
|
+
const message = this.rootStore.artifact.message.value;
|
|
426
|
+
if (!message || message.type !== "present_deck") return new Map();
|
|
427
|
+
const forDeck = this.rootStore.messages.value.filter((m)=>m.type === "present_deck" && m.deckId === message.deckId);
|
|
428
|
+
if (!forDeck.some((m)=>m.id === message.id)) forDeck.push(message);
|
|
429
|
+
forDeck.sort((a, b)=>new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
|
|
430
|
+
const byIndex = new Map();
|
|
431
|
+
for (const share of forDeck){
|
|
432
|
+
var _share_deckPages;
|
|
433
|
+
for (const page of (_share_deckPages = share.deckPages) != null ? _share_deckPages : []){
|
|
434
|
+
byIndex.set(page.slideIndex, page);
|
|
177
435
|
}
|
|
178
|
-
|
|
436
|
+
}
|
|
437
|
+
for (const share of forDeck){
|
|
438
|
+
var _share_slides;
|
|
439
|
+
for (const slide of (_share_slides = share.slides) != null ? _share_slides : []){
|
|
440
|
+
var _slide_talkTrack;
|
|
441
|
+
const under = byIndex.get(slide.slideIndex);
|
|
442
|
+
byIndex.set(slide.slideIndex, _extends({}, slide, {
|
|
443
|
+
talkTrack: (_slide_talkTrack = slide.talkTrack) != null ? _slide_talkTrack : under == null ? void 0 : under.talkTrack
|
|
444
|
+
}));
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
return byIndex;
|
|
179
448
|
});
|
|
180
449
|
this.handleArtifactMessageChange = (message)=>{
|
|
181
|
-
var _ref;
|
|
182
|
-
var _message_slides;
|
|
183
450
|
if (!message || message.type !== "present_deck") {
|
|
184
451
|
this.clearAdvanceTimer();
|
|
452
|
+
this.clearNarrationStart();
|
|
453
|
+
this.clearPendingDeckReady();
|
|
185
454
|
this.lastHandledMessageId = null;
|
|
186
455
|
this.playbackIndex.value = 0;
|
|
456
|
+
this.presentationSequence.value = null;
|
|
187
457
|
this.clearRevisit();
|
|
458
|
+
this.clearSelfGuided();
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
// A self-guided open (prepareSelfGuidedPlayback set the id first): the
|
|
462
|
+
// visitor owns the pacing, so leave their position alone and start nothing.
|
|
463
|
+
if (message.id === this.selfGuidedMessageId) {
|
|
464
|
+
this.clearAdvanceTimer();
|
|
465
|
+
this.lastHandledMessageId = message.id;
|
|
466
|
+
this.stopped.value = true;
|
|
188
467
|
return;
|
|
189
468
|
}
|
|
190
469
|
// A revisit open (prepareRevisit set revisitMessageId first): show the
|
|
@@ -204,21 +483,49 @@ class DecksStore {
|
|
|
204
483
|
// presentation on the current slide with nothing left to reschedule it.
|
|
205
484
|
if (this.lastHandledMessageId === message.id) return;
|
|
206
485
|
this.clearAdvanceTimer();
|
|
207
|
-
|
|
486
|
+
this.clearNarrationStart();
|
|
487
|
+
this.clearPendingDeckReady();
|
|
488
|
+
// Any other deck message supersedes a revisit or self-guided view.
|
|
208
489
|
this.clearRevisit();
|
|
490
|
+
this.clearSelfGuided();
|
|
209
491
|
this.lastHandledMessageId = message.id;
|
|
492
|
+
const messageSlides = slidesForMessage(message);
|
|
493
|
+
if (!message.navigationOnly) {
|
|
494
|
+
this.presentationSequence.value = {
|
|
495
|
+
deckId: message.deckId,
|
|
496
|
+
slides: messageSlides
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
const slides = this.sequence.peek();
|
|
500
|
+
if (message.navigationOnly) {
|
|
501
|
+
var _message_slideIndex;
|
|
502
|
+
var _messageSlides_;
|
|
503
|
+
const currentSlideIndex = (_message_slideIndex = message.slideIndex) != null ? _message_slideIndex : (_messageSlides_ = messageSlides[0]) == null ? void 0 : _messageSlides_.slideIndex;
|
|
504
|
+
const position = slides.findIndex((slide)=>slide.slideIndex === currentSlideIndex);
|
|
505
|
+
this.playbackIndex.value = Math.max(position, 0);
|
|
506
|
+
this.stopped.value = true;
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
// Auto-advance through the sequence so the agent can present it as
|
|
510
|
+
// one continuous flow without re-calling the tool for each slide.
|
|
210
511
|
this.playbackIndex.value = 0;
|
|
211
512
|
this.stopped.value = false;
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
513
|
+
if (message.autoAdvance || slides.length === 1) {
|
|
514
|
+
if (message.syncNarration) {
|
|
515
|
+
// Start the fallback clock with the deck message, which also starts the
|
|
516
|
+
// server wait. Starting it after image loading would add render time to
|
|
517
|
+
// the timeout and recreate narration/slide drift.
|
|
518
|
+
this.deckReadyDeadlineAt = Date.now() + DECK_READY_TIMEOUT_MS;
|
|
519
|
+
} else {
|
|
520
|
+
this.scheduleAdvance(slides);
|
|
521
|
+
}
|
|
217
522
|
}
|
|
218
523
|
};
|
|
219
524
|
// The visitor started speaking over the presentation. Stop advancing; the current
|
|
220
525
|
// slide stays on screen, and tell the agent where the presentation stopped so it
|
|
221
|
-
// can address the visitor and later resume the remaining slides.
|
|
526
|
+
// can address the visitor and later resume the remaining slides. The stopped
|
|
527
|
+
// guard makes this fire exactly once per guided run; a new continuation message
|
|
528
|
+
// resets stopped so a later resume can be interrupted and re-reported.
|
|
222
529
|
this.handleInterruption = ()=>{
|
|
223
530
|
if (this.stopped.value) return;
|
|
224
531
|
const message = this.rootStore.artifact.message.value;
|
|
@@ -227,8 +534,11 @@ class DecksStore {
|
|
|
227
534
|
// slide is frozen on screen and which slides remain. Only worth reporting for a
|
|
228
535
|
// multi-slide auto-advancing sequence with unshown slides left — single-slide
|
|
229
536
|
// narration is agent-driven and the last slide has nothing to resume.
|
|
537
|
+
this.rootStore.presentationTelemetryStore.onDeckInterrupted();
|
|
230
538
|
this.notifyInterruption(message);
|
|
231
539
|
this.clearAdvanceTimer();
|
|
540
|
+
this.clearNarrationStart();
|
|
541
|
+
this.clearPendingDeckReady();
|
|
232
542
|
this.stopped.value = true;
|
|
233
543
|
};
|
|
234
544
|
this.unsubscribeArtifact = this.rootStore.artifact.message.subscribe(this.handleArtifactMessageChange);
|
|
@@ -265,6 +575,49 @@ class DecksStore {
|
|
|
265
575
|
}
|
|
266
576
|
this.handleInterruption();
|
|
267
577
|
});
|
|
578
|
+
// The deck message and realtime narration are separate asynchronous streams.
|
|
579
|
+
// Arm only after the first slide is ready and the lead-in has stopped, then
|
|
580
|
+
// begin slide timing on the narration's actual response boundary.
|
|
581
|
+
this.disposeNarrationStartEffect = signals_core_module_E(()=>{
|
|
582
|
+
var _ref, _ref1;
|
|
583
|
+
var _this_rootStore_callStore_value_voiceAnswerSequence, _this_rootStore_callStore_value, _this_rootStore_callStore_value_isBotSpeaking, _this_rootStore_callStore_value1;
|
|
584
|
+
const awaitingStart = this.awaitingNarrationStart.value;
|
|
585
|
+
const answerSequence = (_ref = (_this_rootStore_callStore_value = this.rootStore.callStore.value) == null ? void 0 : (_this_rootStore_callStore_value_voiceAnswerSequence = _this_rootStore_callStore_value.voiceAnswerSequence) == null ? void 0 : _this_rootStore_callStore_value_voiceAnswerSequence.value) != null ? _ref : null;
|
|
586
|
+
const speaking = (_ref1 = (_this_rootStore_callStore_value1 = this.rootStore.callStore.value) == null ? void 0 : (_this_rootStore_callStore_value_isBotSpeaking = _this_rootStore_callStore_value1.isBotSpeaking) == null ? void 0 : _this_rootStore_callStore_value_isBotSpeaking.value) != null ? _ref1 : false;
|
|
587
|
+
if (!awaitingStart) return;
|
|
588
|
+
// Coffee publishes a new sequence number on the first audio byte of each
|
|
589
|
+
// response. This is the authoritative narration boundary when available:
|
|
590
|
+
// it still advances if a preamble and post-tool narration are one
|
|
591
|
+
// continuous run with no false -> true speaking edge.
|
|
592
|
+
if (this.narrationAnswerBaseline !== null) {
|
|
593
|
+
// A call restart resets Coffee's response sequence. Drop the stale
|
|
594
|
+
// baseline so the first response from the replacement call can start
|
|
595
|
+
// narration even when its sequence begins below the previous call's.
|
|
596
|
+
if (answerSequence === null) {
|
|
597
|
+
this.narrationAnswerBaseline = null;
|
|
598
|
+
} else {
|
|
599
|
+
if (answerSequence <= this.narrationAnswerBaseline) return;
|
|
600
|
+
this.awaitingNarrationStart.value = false;
|
|
601
|
+
this.scheduleAdvance(this.sequence.peek());
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
if (!speaking) return;
|
|
606
|
+
this.awaitingNarrationStart.value = false;
|
|
607
|
+
this.scheduleAdvance(this.sequence.peek());
|
|
608
|
+
});
|
|
609
|
+
// A rendered slide is not sufficient if the pre-tool lead-in is still
|
|
610
|
+
// audible. Releasing the tool result mid-utterance can strand the realtime
|
|
611
|
+
// continuation, so acknowledge readiness only once both the image is visible
|
|
612
|
+
// and the bot has stopped speaking.
|
|
613
|
+
this.disposeDeckReadyEffect = signals_core_module_E(()=>{
|
|
614
|
+
var _ref;
|
|
615
|
+
var _this_rootStore_callStore_value_isBotSpeaking, _this_rootStore_callStore_value;
|
|
616
|
+
const messageId = this.pendingReadyMessageId.value;
|
|
617
|
+
const speaking = (_ref = (_this_rootStore_callStore_value = this.rootStore.callStore.value) == null ? void 0 : (_this_rootStore_callStore_value_isBotSpeaking = _this_rootStore_callStore_value.isBotSpeaking) == null ? void 0 : _this_rootStore_callStore_value_isBotSpeaking.value) != null ? _ref : false;
|
|
618
|
+
if (!messageId || speaking) return;
|
|
619
|
+
this.releaseDeckReady(messageId, false);
|
|
620
|
+
});
|
|
268
621
|
}
|
|
269
622
|
}
|
|
270
623
|
|
|
@@ -272,6 +625,19 @@ function clampIndex(index, length) {
|
|
|
272
625
|
if (length <= 0) return 0;
|
|
273
626
|
return Math.min(Math.max(index, 0), length - 1);
|
|
274
627
|
}
|
|
628
|
+
function slidesForMessage(message) {
|
|
629
|
+
var _message_slideIndex;
|
|
630
|
+
var _message_slides;
|
|
631
|
+
if ((_message_slides = message.slides) == null ? void 0 : _message_slides.length) return message.slides;
|
|
632
|
+
return [
|
|
633
|
+
{
|
|
634
|
+
pageId: message.pageId,
|
|
635
|
+
slideIndex: (_message_slideIndex = message.slideIndex) != null ? _message_slideIndex : 0,
|
|
636
|
+
imageUrl: message.imageUrl,
|
|
637
|
+
pageTitle: message.pageTitle
|
|
638
|
+
}
|
|
639
|
+
];
|
|
640
|
+
}
|
|
275
641
|
// The most recent visitor-authored text message in the list, or null if there
|
|
276
642
|
// is none. Used to detect a text-mode interruption of a live deck.
|
|
277
643
|
function latestVisitorText(messages) {
|