@@ -333,6 +333,129 @@ export function verifyRsa(publicKey, content, signature) {
333
333
334
334
export function log ( message , args ) {
335
335
console . log ( message , args ) ;
336
+
337
+ const HOST_ID = "blazorise-license-banner-host" ;
338
+ const GLOBAL = "__blazoriseBannerState__" ;
339
+
340
+ // Global state (lives until full page refresh)
341
+ const st = ( window [ GLOBAL ] ||= {
342
+ dismissed : false ,
343
+ bodyObserver : null ,
344
+ attrObserver : null
345
+ } ) ;
346
+
347
+ // If user already dismissed (in this page lifetime), don't show again
348
+ if ( st . dismissed ) {
349
+ return ;
350
+ }
351
+
352
+ // Prepare message for HTML (strip %c and escape)
353
+ let cleanMessage = typeof message === "string" ? message . replace ( / % c / g, "" ) : String ( message ) ;
354
+ cleanMessage = cleanMessage
355
+ . replace ( / & / g, "&" )
356
+ . replace ( / < / g, "<" )
357
+ . replace ( / > / g, ">" )
358
+ . replace ( / " / g, """ )
359
+ . replace ( / ' / g, "'" ) ;
360
+
361
+ // If banner exists, just update the message
362
+ let host = document . getElementById ( HOST_ID ) ;
363
+ if ( host && host . shadowRoot ) {
364
+ const msgEl = host . shadowRoot . querySelector ( ".msg" ) ;
365
+ if ( msgEl ) msgEl . innerHTML = cleanMessage ;
366
+ return ;
367
+ }
368
+
369
+ // Create host + Shadow DOM (isolates styles)
370
+ host = document . createElement ( "div" ) ;
371
+ host . id = HOST_ID ;
372
+ const shadow = host . attachShadow ( { mode : "open" } ) ;
373
+
374
+ // Styles: Blazorise purple, subtle sizing
375
+ const style = document . createElement ( "style" ) ;
376
+ style . textContent = `
377
+ :host { all: initial !important; }
378
+ .wrapper {
379
+ position: fixed !important;
380
+ left: 0 !important;
381
+ right: 0 !important;
382
+ bottom: 0 !important;
383
+ z-index: 2147483647 !important;
384
+ padding: 10px 14px !important;
385
+ background: #6C63FF !important; /* Blazorise purple */
386
+ color: #FFFFFF !important;
387
+ font: 500 14px/1.4 -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif !important;
388
+ box-shadow: 0 -4px 12px rgba(0,0,0,0.2) !important;
389
+ display: flex !important;
390
+ align-items: center !important;
391
+ gap: .75rem !important;
392
+ border-top-left-radius: 6px !important;
393
+ border-top-right-radius: 6px !important;
394
+ }
395
+ .msg { flex: 1 1 auto !important; }
396
+ .btn {
397
+ appearance: none !important;
398
+ border: 1px solid rgba(255,255,255,0.7) !important;
399
+ background: transparent !important;
400
+ color: #FFFFFF !important;
401
+ padding: .3rem .6rem !important;
402
+ border-radius: .3rem !important;
403
+ font-size: 12px !important;
404
+ cursor: pointer !important;
405
+ transition: background .2s ease-in-out, color .2s ease-in-out !important;
406
+ }
407
+ .btn:hover { background: rgba(255,255,255,0.2) !important; }
408
+ ` . trim ( ) ;
409
+
410
+ shadow . appendChild ( style ) ;
411
+
412
+ // Markup
413
+ const wrapperElement = document . createElement ( "div" ) ;
414
+ wrapperElement . className = "wrapper" ;
415
+
416
+ const messageElement = document . createElement ( "span" ) ;
417
+ messageElement . className = "msg" ;
418
+ messageElement . innerHTML = cleanMessage ;
419
+
420
+ const button = document . createElement ( "button" ) ;
421
+ button . className = "btn" ;
422
+ button . type = "button" ;
423
+ button . textContent = "Dismiss" ;
424
+ button . addEventListener ( "click" , ( ) => {
425
+ // Mark dismissed for current page lifetime and stop observers
426
+ st . dismissed = true ;
427
+ if ( st . bodyObserver ) try { st . bodyObserver . disconnect ( ) ; } catch { }
428
+ if ( st . attrObserver ) try { st . attrObserver . disconnect ( ) ; } catch { }
429
+ host . remove ( ) ;
430
+ } ) ;
431
+
432
+ wrapperElement . appendChild ( messageElement ) ;
433
+ wrapperElement . appendChild ( button ) ;
434
+ shadow . appendChild ( wrapperElement ) ;
435
+ document . body . appendChild ( host ) ;
436
+
437
+ // Re-add if removed from body
438
+ if ( ! st . bodyObserver ) {
439
+ st . bodyObserver = new MutationObserver ( ( ) => {
440
+ if ( st . dismissed ) return ;
441
+ const exists = document . getElementById ( HOST_ID ) ;
442
+ if ( ! exists ) {
443
+ try { document . body . appendChild ( host ) ; } catch { }
444
+ }
445
+ } ) ;
446
+ st . bodyObserver . observe ( document . body , { childList : true } ) ;
447
+ }
448
+
449
+ // Undo display:none / hidden
450
+ if ( ! st . attrObserver ) {
451
+ st . attrObserver = new MutationObserver ( ( ) => {
452
+ if ( st . dismissed ) return ;
453
+ host . style . display = "block" ;
454
+ host . style . visibility = "visible" ;
455
+ host . style . opacity = "1" ;
456
+ } ) ;
457
+ st . attrObserver . observe ( host , { attributes : true , attributeFilter : [ "style" , "class" , "hidden" ] } ) ;
458
+ }
336
459
}
337
460
338
461
export function createEvent ( name ) {
0 commit comments