@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.
Files changed (98) hide show
  1. package/CAPABILITY-REFERENCE.md +41 -0
  2. package/CHANGELOG.md +84 -0
  3. package/README.md +65 -0
  4. package/constants.generated.js +30 -2
  5. package/css/widget/multimodal_v2/index.split-css.panda.css +265 -47
  6. package/package.json +18 -4
  7. package/packs/widget/api/docking/viewport_lock.js +93 -2
  8. package/packs/widget/esm/index.d.ts +134 -0
  9. package/packs/widget/esm/index.js +18 -2
  10. package/packs/widget/esm/qualified.css +265 -47
  11. package/src/graphql-types.js +74 -1
  12. package/src/url.js +4 -1
  13. package/vendor/actioncable/connection.js +40 -3
  14. package/vendor/actioncable/connection_monitor.js +8 -0
  15. package/vendor/actioncable/consumer.js +7 -1
  16. package/vendor/actioncable/index.js +5 -2
  17. package/vendor/actioncable/sse_socket.js +262 -0
  18. package/widget/analytics.js +8 -0
  19. package/widget/api_handler/base_api_handler.js +24 -0
  20. package/widget/booker_outcome_tracker.js +58 -20
  21. package/widget/capabilities.js +190 -0
  22. package/widget/channel.js +38 -6
  23. package/widget/constants.generated.js +2 -1
  24. package/widget/multimodal_v2/components/AppLayout.js +1 -1
  25. package/widget/multimodal_v2/components/app/Foreground.js +10 -9
  26. package/widget/multimodal_v2/components/app/Input.js +8 -3
  27. package/widget/multimodal_v2/components/app/LightboxExperience.js +17 -3
  28. package/widget/multimodal_v2/components/app/Main.js +87 -31
  29. package/widget/multimodal_v2/components/app/MessengerButton.js +18 -1
  30. package/widget/multimodal_v2/components/chat/ChatInputForm.js +8 -6
  31. package/widget/multimodal_v2/components/chat/timeline/CalendarEventPickRenderer.js +2 -2
  32. package/widget/multimodal_v2/components/chat/timeline/PresentDeckRenderer.js +346 -170
  33. package/widget/multimodal_v2/components/chat/timeline/SystemRenderer.js +4 -7
  34. package/widget/multimodal_v2/components/decks/index.js +506 -52
  35. package/widget/multimodal_v2/components/helpers/content_click_payloads.js +67 -1
  36. package/widget/multimodal_v2/components/lightbox_gather/index.js +4 -4
  37. package/widget/multimodal_v2/components/meetings/MeetingBookerEntry.js +8 -1
  38. package/widget/multimodal_v2/components/meetings/MeetingOfferEmailStep.js +1 -1
  39. package/widget/multimodal_v2/components/meetings/MeetingSummaryRail.js +1 -1
  40. package/widget/multimodal_v2/components/meetings/MeetingTimeSelector.js +1 -1
  41. package/widget/multimodal_v2/components/multimodal/Controls.js +11 -3
  42. package/widget/multimodal_v2/components/multimodal/useMultimodal.js +19 -7
  43. package/widget/multimodal_v2/components/presentations/index.js +61 -0
  44. package/widget/multimodal_v2/constants.js +1 -1
  45. package/widget/multimodal_v2/images/icons.js +17 -1
  46. package/widget/multimodal_v2/index.panda.js +2 -2
  47. package/widget/multimodal_v2/locales/cs.js +8 -1
  48. package/widget/multimodal_v2/locales/da.js +8 -1
  49. package/widget/multimodal_v2/locales/de.js +8 -1
  50. package/widget/multimodal_v2/locales/en.js +8 -1
  51. package/widget/multimodal_v2/locales/en_AU.js +8 -1
  52. package/widget/multimodal_v2/locales/en_GB.js +8 -1
  53. package/widget/multimodal_v2/locales/es.js +8 -1
  54. package/widget/multimodal_v2/locales/es_LA.js +8 -1
  55. package/widget/multimodal_v2/locales/fi.js +8 -1
  56. package/widget/multimodal_v2/locales/fr.js +8 -1
  57. package/widget/multimodal_v2/locales/fr_CA.js +8 -1
  58. package/widget/multimodal_v2/locales/id.js +8 -1
  59. package/widget/multimodal_v2/locales/it.js +8 -1
  60. package/widget/multimodal_v2/locales/ja.js +8 -1
  61. package/widget/multimodal_v2/locales/ko.js +8 -1
  62. package/widget/multimodal_v2/locales/nb.js +8 -1
  63. package/widget/multimodal_v2/locales/nl.js +8 -1
  64. package/widget/multimodal_v2/locales/pl.js +8 -1
  65. package/widget/multimodal_v2/locales/pt_BR.js +8 -1
  66. package/widget/multimodal_v2/locales/ru.js +8 -1
  67. package/widget/multimodal_v2/locales/sv.js +8 -1
  68. package/widget/multimodal_v2/locales/th.js +8 -1
  69. package/widget/multimodal_v2/locales/tr.js +8 -1
  70. package/widget/multimodal_v2/locales/vi.js +8 -1
  71. package/widget/multimodal_v2/locales/zh_CN.js +8 -1
  72. package/widget/multimodal_v2/locales/zh_TW.js +8 -1
  73. package/widget/multimodal_v2/multimodal.js +5 -1
  74. package/widget/multimodal_v2/multimodal_channel.js +13 -8
  75. package/widget/multimodal_v2/signals/messages_signal.js +4 -0
  76. package/widget/multimodal_v2/stores/ai_conversation_store.js +6 -2
  77. package/widget/multimodal_v2/stores/artifact_store.js +352 -27
  78. package/widget/multimodal_v2/stores/call_store.js +40 -1
  79. package/widget/multimodal_v2/stores/call_ui_adapter.js +1 -1
  80. package/widget/multimodal_v2/stores/decks_store.js +405 -39
  81. package/widget/multimodal_v2/stores/meeting_store.js +15 -10
  82. package/widget/multimodal_v2/stores/presentation_ga_adapter.js +64 -0
  83. package/widget/multimodal_v2/stores/presentation_telemetry_store.js +556 -0
  84. package/widget/multimodal_v2/stores/presentations_store.js +1 -0
  85. package/widget/multimodal_v2/stores/root_store.js +157 -268
  86. package/widget/multimodal_v2/stores/screen_share_store.js +41 -21
  87. package/widget/multimodal_v2/stores/translation_store.js +9 -0
  88. package/widget/multimodal_v2/stores/ui_store.js +7 -7
  89. package/widget/multimodal_v2/stores/website_navigation_store.js +3 -1
  90. package/widget/multimodal_v2/text_turn_response_tracker.js +419 -0
  91. package/widget/multimodal_v2/types.js +21 -1
  92. package/widget/multimodal_v2/utils/locale.js +2 -1
  93. package/widget/multimodal_v2/utils/signals.js +35 -4
  94. package/widget/pardot_visitor.js +7 -1
  95. package/widget/server-logger.js +97 -2
  96. package/widget/tracker.js +69 -21
  97. package/widget/widget/page_mirror_factory.js +9 -1
  98. package/widget/widget/widget.js +29 -4
@@ -1,12 +1,25 @@
1
- import { jsxRuntime_module_u, useComputed } from "../../../../../js/multimodal_v2.js";
2
- import { PlayIcon } from "../../../images/icons.js";
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. Two modes:
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
- * - Presented on a call: the deck's per-slide talk tracks are available, so we
22
- * render the presentation as a formatted record — each slide thumbnail
23
- * followed by its narration. This replaces the realtime API's one-turn
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
- var _item_slides;
34
- const presentedSlides = ((_item_slides = item.slides) != null ? _item_slides : []).filter((slide)=>!!slide.talkTrack);
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, ariaLabel, startLabel, handleStartDeck } = usePresentDeckJoinCard({
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(ThumbnailFrame, {
131
- children: thumbnailUrl && // Every slide load must be crossorigin, here and everywhere else. The assets
132
- // origin only sends `Access-Control-Allow-Origin` (and `Vary: Origin`) when the
133
- // request carries an `Origin` header, so a plain <img> caches a response with
134
- // neither, and the browser then serves that ACAO-less entry to a later
135
- // crossorigin request for the same url, which fails the CORS check. This card
136
- // previews the deck's FIRST slide off-call, so it was the plain load that
137
- // poisoned slide 1 for the overlay and the perception capture that followed.
138
- /*#__PURE__*/ jsxRuntime_module_u(ThumbnailImage, {
139
- src: thumbnailUrl,
140
- alt: ariaLabel,
141
- crossOrigin: "anonymous"
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
- iconOnly: true,
148
- "aria-label": startLabel,
149
- onClick: handleStartDeck,
150
- children: /*#__PURE__*/ jsxRuntime_module_u(PlayIcon, {})
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 usePresentDeckJoinCard(param) {
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 multiple times for the same deck (navigating slides). Only
161
- // show the card for the last message of a given deck — one "join call" prompt
162
- // is enough. Walk backwards (no findLast — the widget runs in old browsers).
163
- const lastPresentDeck = useComputed(()=>{
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
- // Hidden when: a deck is up in the overlay (where it plays on a call); text-only
174
- // mode has no call capability; a call is already in progress; or this isn't the
175
- // last card for the deck.
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.conversationMode.text.value || context.callInProgress.value || lastPresentDeck.value != null && lastPresentDeck.value.id !== item.id;
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 handleStartDeck = ()=>{
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
- thumbnailUrl: item.imageUrl,
193
- ariaLabel: (_ref = (_item_deckTitle = item.deckTitle) != null ? _item_deckTitle : item.pageTitle) != null ? _ref : "",
194
- startLabel: t("presentation.start"),
195
- handleStartDeck
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
- const Card = styled("div", {
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: "$sm",
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
- const Transcript = styled("div", {
212
- base: {
213
- display: "flex",
214
- flexDirection: "column",
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
- display: "flex",
222
- flexDirection: "column",
223
- gap: "$sm"
347
+ position: "relative",
348
+ paddingTop: "$md"
224
349
  }
225
350
  });
226
- SlideBlock.displayName = "PresentDeckSlideBlock";
227
- // Matches the slide-image preview size used elsewhere (SlidesThumbnail): a
228
- // fixed 200px width at the slide's natural aspect ratio, not a full-width crop.
229
- // A button so clicking re-opens the slide in the viewer.
230
- const SlidePreview = styled("button", {
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
- width: "[200px]",
233
- padding: "$none",
234
- border: "none",
235
- borderRadius: "md",
359
+ position: "absolute",
360
+ height: "[16px]",
236
361
  overflow: "hidden",
237
- cursor: "pointer",
238
- backgroundColor: "$gray"
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
- SlidePreview.displayName = "PresentDeckSlidePreview";
242
- const SlidePreviewImage = styled("img", {
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
- width: "[100%]",
245
- height: "[auto]",
246
- display: "block"
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
- SlidePreviewImage.displayName = "PresentDeckSlidePreviewImage";
250
- // Fits the slide at its natural aspect, like SlidePreview above. A fixed 16:9 frame
251
- // with an object-fit: cover image centre-cropped a portrait slide, cutting off its
252
- // title; a deck may contain pages of any shape, so the frame takes the slide's.
253
- // With no image to take a shape from, keep the 16:9 placeholder the frame used to
254
- // be so the card doesn't collapse to a bare button.
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;