@@ -46,6 +46,7 @@ const CHAT_MESSAGE_TAG = "shiny-chat-message";
46
46
const CHAT_USER_MESSAGE_TAG = "shiny-user-message" ;
47
47
const CHAT_MESSAGES_TAG = "shiny-chat-messages" ;
48
48
const CHAT_INPUT_TAG = "shiny-chat-input" ;
49
+ const CHAT_INPUT_SENTINEL_TAG = "shiny-chat-input-sentinel" ;
49
50
const CHAT_CONTAINER_TAG = "shiny-chat-container" ;
50
51
51
52
const ICONS = {
@@ -262,6 +263,8 @@ class ChatInput extends LightElement {
262
263
}
263
264
264
265
class ChatContainer extends LightElement {
266
+ inputSentinel ?: HTMLElement ;
267
+ inputSentinelObserver ?: IntersectionObserver ;
265
268
266
269
private get input ( ) : ChatInput {
267
270
return this . querySelector ( CHAT_INPUT_TAG ) as ChatInput ;
@@ -280,6 +283,34 @@ class ChatContainer extends LightElement {
280
283
return html `` ;
281
284
}
282
285
286
+ connectedCallback ( ) : void {
287
+ super . connectedCallback ( ) ;
288
+
289
+ // We use a sentinel element that we place just above the shiny-chat-input. When it
290
+ // moves off-screen we know that the text area input is now floating, add shadow.
291
+ let sentinel = this . querySelector < HTMLElement > ( CHAT_INPUT_SENTINEL_TAG ) ;
292
+ if ( ! sentinel ) {
293
+ sentinel = createElement ( CHAT_INPUT_SENTINEL_TAG , { } ) as HTMLElement ;
294
+ this . input . insertAdjacentElement ( "beforebegin" , sentinel ) ;
295
+ }
296
+
297
+ this . inputSentinel = sentinel ;
298
+ this . inputSentinelObserver = new IntersectionObserver (
299
+ ( entries ) => {
300
+ const inputTextarea = this . input . querySelector ( "textarea" ) ;
301
+ if ( ! inputTextarea ) return ;
302
+ const addShadow = entries [ 0 ] ?. intersectionRatio === 0 ;
303
+ inputTextarea . classList . toggle ( "shadow" , addShadow ) ;
304
+ } ,
305
+ {
306
+ threshold : [ 0 , 1 ] ,
307
+ rootMargin : "0px" ,
308
+ }
309
+ ) ;
310
+
311
+ this . inputSentinelObserver . observe ( this . inputSentinel ) ;
312
+ }
313
+
283
314
firstUpdated ( ) : void {
284
315
// Don't attach event listeners until child elements are rendered
285
316
if ( ! this . messages ) return ;
@@ -306,6 +337,9 @@ class ChatContainer extends LightElement {
306
337
disconnectedCallback ( ) : void {
307
338
super . disconnectedCallback ( ) ;
308
339
340
+ this . inputSentinelObserver ?. disconnect ( ) ;
341
+ this . inputSentinel ?. remove ( ) ;
342
+
309
343
this . removeEventListener ( "shiny-chat-input-sent" , this . #onInputSent) ;
310
344
this . removeEventListener ( "shiny-chat-append-message" , this . #onAppend) ;
311
345
this . removeEventListener (
0 commit comments