js_lis 2.0.6 → 2.0.7

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.
@@ -14,35 +14,16 @@ export class VirtualKeyboard {
14
14
  this.shiftActive = false;
15
15
  this.capsLockActive = false;
16
16
  this.isScrambled = false;
17
- this.isVirtualKeyboardActive = false; // Flag to track VK operations
17
+ this.isVirtualKeyboardActive = false;
18
18
 
19
- // Maintain plaintext buffers per input (not stored in DOM). DOM will store only encrypted text.
20
19
  this.inputPlaintextBuffers = new WeakMap();
21
-
22
- // Security state
23
- // this.security = {
24
- // detectorStatus: "idle",
25
- // suspiciousEvents: [],
26
- // observer: null,
27
- // addSuspicious(event) {
28
- // try {
29
- // this.suspiciousEvents.push({
30
- // time: new Date().toISOString(),
31
- // ...event,
32
- // });
33
- // console.warn("[VK Security] Suspicious activity detected:", event);
34
- // } catch (_) {}
35
- // },
36
- // };
37
-
38
- injectCSS();
20
+
39
21
  this.initialize();
40
22
  }
41
23
 
42
24
  async initialize() {
43
25
  try {
44
- // this.applyCSPReportOnly();
45
- // this.startKeyloggerDetector();
26
+ injectCSS();
46
27
  this.render();
47
28
  this.initializeInputListeners();
48
29
  console.log("VirtualKeyboard initialized successfully.");
@@ -51,113 +32,6 @@ export class VirtualKeyboard {
51
32
  }
52
33
  }
53
34
 
54
- // Inject CSP Report-Only meta for runtime visibility into potential violations
55
- // applyCSPReportOnly() {
56
- // // Disabled: Report-Only CSP must be set via HTTP headers. See server middleware in app.js.
57
- // return;
58
- // }
59
-
60
- // Start MutationObserver and limited listener-hook to detect potential keylogger injections
61
- // startKeyloggerDetector() {
62
- // try {
63
- // const allowlistedScriptHosts = new Set([
64
- // window.location.host,
65
- // "cdnjs.cloudflare.com",
66
- // ]);
67
-
68
- // const isSuspiciousScript = (node) => {
69
- // try {
70
- // if (!(node instanceof HTMLScriptElement)) return false;
71
- // const src = node.getAttribute("src") || "";
72
- // if (!src) {
73
- // const code = (node.textContent || "").toLowerCase();
74
- // return /addEventListener\(['\"]key|document\.onkey|keylogger|send\(|fetch\(|xmlhttprequest/i.test(code);
75
- // }
76
- // const url = new URL(src, window.location.href);
77
- // return !Array.from(allowlistedScriptHosts).some((h) => url.host.endsWith(h));
78
- // } catch (_) {
79
- // return true;
80
- // }
81
- // };
82
-
83
- // const isSuspiciousElement = (node) => {
84
- // try {
85
- // if (!(node instanceof Element)) return false;
86
- // const hasKeyHandlers = node.hasAttribute("onkeydown") || node.hasAttribute("onkeypress") || node.hasAttribute("onkeyup");
87
- // const hiddenFrame = node.tagName === "IFRAME" && (node.getAttribute("sandbox") === null || node.getAttribute("srcdoc") !== null);
88
- // return hasKeyHandlers || hiddenFrame;
89
- // } catch (_) {
90
- // return false;
91
- // }
92
- // };
93
-
94
- // const quarantineScript = (script) => {
95
- // try {
96
- // script.type = "text/plain";
97
- // script.setAttribute("data-quarantined", "true");
98
- // } catch (_) {}
99
- // };
100
-
101
- // const observer = new MutationObserver((mutations) => {
102
- // for (const m of mutations) {
103
- // if (m.type === "childList") {
104
- // m.addedNodes.forEach((node) => {
105
- // if (isSuspiciousScript(node)) {
106
- // this.security.addSuspicious({ type: "script", reason: "suspicious source or inline pattern", node });
107
- // quarantineScript(node);
108
- // } else if (isSuspiciousElement(node)) {
109
- // this.security.addSuspicious({ type: "element", reason: "inline key handlers or risky frame", node });
110
- // }
111
- // });
112
- // } else if (m.type === "attributes" && isSuspiciousElement(m.target)) {
113
- // this.security.addSuspicious({ type: "attr", reason: `attribute ${m.attributeName}`, node: m.target });
114
- // }
115
- // }
116
- // });
117
-
118
- // observer.observe(document.documentElement, {
119
- // childList: true,
120
- // subtree: true,
121
- // attributes: true,
122
- // attributeFilter: ["onkeydown", "onkeypress", "onkeyup", "src", "type", "sandbox", "srcdoc"],
123
- // });
124
-
125
- // // Hook addEventListener to monitor keyboard listeners registration
126
- // const OriginalAddEventListener = EventTarget.prototype.addEventListener;
127
- // const selfRef = this;
128
- // EventTarget.prototype.addEventListener = function(type, listener, options) {
129
- // try {
130
- // if (type && /^(key(?:down|up|press))$/i.test(type)) {
131
- // const info = {
132
- // type: "event-listener",
133
- // reason: `keyboard listener registered: ${type}`,
134
- // target: this,
135
- // };
136
- // selfRef.security.addSuspicious(info);
137
- // }
138
- // } catch (_) {}
139
- // return OriginalAddEventListener.call(this, type, listener, options);
140
- // };
141
-
142
- // this.security.observer = observer;
143
- // this.security.detectorStatus = "running";
144
-
145
- // // Expose minimal security API
146
- // window.VKSecurity = {
147
- // getStatus: () => this.security.detectorStatus,
148
- // getEvents: () => [...this.security.suspiciousEvents],
149
- // stop: () => {
150
- // try { observer.disconnect(); } catch (_) {}
151
- // this.security.detectorStatus = "stopped";
152
- // },
153
- // };
154
- // console.info("[VK Security] Keylogger detector started");
155
- // } catch (err) {
156
- // this.security.detectorStatus = "error";
157
- // console.warn("[VK Security] Detector error:", err);
158
- // }
159
- // }
160
-
161
35
  getLayoutName(layout) {
162
36
  switch (layout) {
163
37
  case "full":
@@ -170,6 +44,8 @@ export class VirtualKeyboard {
170
44
  return "Numpad Keyboard";
171
45
  case "symbols":
172
46
  return "Symbols Keyboard";
47
+ case "full-layouts":
48
+ return "Full English + Numpad";
173
49
  default:
174
50
  return "Unknown Layout";
175
51
  }
@@ -179,13 +55,8 @@ export class VirtualKeyboard {
179
55
  document.addEventListener("click", (e) => {
180
56
  const target = e.target;
181
57
  if (target.tagName === "INPUT" || target.tagName === "TEXTAREA") {
182
- // Store the cursor position immediately after click
183
58
  const clickCursorPos = target.selectionStart;
184
-
185
- // Don't move cursor to end if virtual keyboard is currently active
186
59
  this.setCurrentInput(target, !this.isVirtualKeyboardActive);
187
-
188
- // Restore the cursor position that user clicked to set
189
60
  setTimeout(() => {
190
61
  if (target.selectionStart !== clickCursorPos) {
191
62
  target.setSelectionRange(clickCursorPos, clickCursorPos);
@@ -194,12 +65,9 @@ export class VirtualKeyboard {
194
65
  }
195
66
  });
196
67
 
197
- document.addEventListener(
198
- "focus",
199
- (e) => {
68
+ document.addEventListener("focus", (e) => {
200
69
  const target = e.target;
201
70
  if (target.tagName === "INPUT" || target.tagName === "TEXTAREA") {
202
- // Don't move cursor to end if virtual keyboard is currently active
203
71
  this.setCurrentInput(target, !this.isVirtualKeyboardActive);
204
72
  }
205
73
  },
@@ -210,7 +78,6 @@ export class VirtualKeyboard {
210
78
  document.addEventListener("input", (e) => {
211
79
  const target = e.target;
212
80
  if ((target.tagName === "INPUT" || target.tagName === "TEXTAREA") && target === this.currentInput) {
213
- // Only handle real keyboard input if it's not coming from virtual keyboard operations
214
81
  if (!this.isVirtualKeyboardActive) {
215
82
  this.handleRealKeyboardInput(target);
216
83
  }
@@ -221,10 +88,7 @@ export class VirtualKeyboard {
221
88
  document.addEventListener("keydown", (e) => {
222
89
  const target = e.target;
223
90
  if ((target.tagName === "INPUT" || target.tagName === "TEXTAREA") && target === this.currentInput) {
224
- // Handle arrow keys, HOME, END from real keyboard
225
91
  if (['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Home', 'End'].includes(e.code)) {
226
- // Let the browser handle the navigation naturally
227
- // Just ensure the input stays focused
228
92
  setTimeout(() => {
229
93
  target.focus();
230
94
  }, 0);
@@ -246,7 +110,6 @@ export class VirtualKeyboard {
246
110
  this.currentInput = inputElement;
247
111
  this.currentInput.classList.add("keyboard-active");
248
112
 
249
- // Ensure the input is focused and cursor is visible
250
113
  this.currentInput.focus();
251
114
 
252
115
  // Ensure buffer initialized for this input
@@ -434,6 +297,20 @@ export class VirtualKeyboard {
434
297
  key.classList.toggle("active", this.isScrambled);
435
298
  });
436
299
  }
300
+
301
+ if (this.currentLayout === "full") {
302
+ if (this.isScrambled) {
303
+ this.unscrambleKeys();
304
+ } else {
305
+ this.scrambleFullLayoutKeys();
306
+ }
307
+ this.isScrambled = !this.isScrambled;
308
+ document
309
+ .querySelectorAll('.key[data-key="scrambled"]')
310
+ .forEach((key) => {
311
+ key.classList.toggle("active", this.isScrambled);
312
+ });
313
+ }
437
314
  this.isVirtualKeyboardActive = false;
438
315
  return;
439
316
  }
@@ -535,28 +412,12 @@ export class VirtualKeyboard {
535
412
  case "←":
536
413
  const leftNewPos = Math.max(0, start - 1);
537
414
  this.currentInput.setSelectionRange(leftNewPos, leftNewPos);
538
- // Force immediate focus and ensure cursor is visible
539
- this.currentInput.focus();
540
- setTimeout(() => {
541
- this.currentInput.setSelectionRange(leftNewPos, leftNewPos);
542
- this.currentInput.focus();
543
- }, 5);
544
- // Skip the general cursor repositioning for arrow keys
545
- this.isVirtualKeyboardActive = false;
546
415
  break;
547
416
 
548
417
  case "→":
549
418
  const bufferLength = this.getCurrentBuffer().length;
550
419
  const rightNewPos = Math.min(bufferLength, start + 1);
551
420
  this.currentInput.setSelectionRange(rightNewPos, rightNewPos);
552
- // Force immediate focus and ensure cursor is visible
553
- this.currentInput.focus();
554
- setTimeout(() => {
555
- this.currentInput.setSelectionRange(rightNewPos, rightNewPos);
556
- this.currentInput.focus();
557
- }, 5);
558
- // Skip the general cursor repositioning for arrow keys
559
- this.isVirtualKeyboardActive = false;
560
421
  break;
561
422
 
562
423
  case "↑":
@@ -602,24 +463,17 @@ export class VirtualKeyboard {
602
463
 
603
464
  if (isShiftActive && !isCapsActive) this.toggleShift();
604
465
 
605
- // Only apply general focus and cursor management if not an arrow key
606
- // Arrow keys handle their own cursor positioning and return early
607
466
  this.currentInput.focus();
608
-
609
- // Use setTimeout to ensure cursor positioning happens after DOM updates
467
+
610
468
  setTimeout(() => {
611
469
  this.currentInput.focus();
612
- // Ensure cursor is visible by setting selection range
613
470
  const cursorPos = this.currentInput.selectionStart;
614
471
  this.currentInput.setSelectionRange(cursorPos, cursorPos);
615
472
  }, 0);
616
473
 
474
+ this.currentInput.focus();
617
475
  const event = new Event("input", { bubbles: true });
618
476
  this.currentInput.dispatchEvent(event);
619
-
620
- // Reset flag after virtual keyboard operation is complete
621
- this.isVirtualKeyboardActive = false;
622
- // console.log(keyPressed)
623
477
  }
624
478
 
625
479
  // [3]
@@ -632,17 +486,11 @@ export class VirtualKeyboard {
632
486
  const newBuffer = buffer.slice(0, start) + textvalue + buffer.slice(end);
633
487
  this.setCurrentBuffer(newBuffer);
634
488
 
635
- // Calculate new cursor position first
636
489
  const newCursorPos = start + textvalue.length;
637
490
 
638
- // Update displayed value without preserving old cursor position
639
491
  this.updateDisplayedValue(false);
640
492
 
641
- // Set cursor position after the inserted text
642
493
  this.currentInput.setSelectionRange(newCursorPos, newCursorPos);
643
-
644
- // Ensure the input remains focused with visible cursor
645
- // this.currentInput.focus();
646
494
  }
647
495
 
648
496
  // Helper: get current plaintext buffer for active input
@@ -689,10 +537,6 @@ export class VirtualKeyboard {
689
537
 
690
538
  // Encrypt and store in data-encrypted attribute
691
539
  this.updateDomEncrypted(input, currentValue);
692
-
693
- // this.updateDisplayedValue();
694
- // console.log("Real keyboard input captured and encrypted for:", input.id || input.name || "unnamed input");
695
- // console.log("Input type:", input.type, "Value length:", currentValue.length);
696
540
  }
697
541
 
698
542
  // Helper: encrypt and store ciphertext in DOM attribute
@@ -855,7 +699,7 @@ export class VirtualKeyboard {
855
699
  // [12]
856
700
  scrambleEnglishKeys() {
857
701
  const keys = document.querySelectorAll(
858
- ".key:not([data-key='scrambled']):not([data-key='std']):not([data-key='scr']):not([data-key='Space']):not([data-key='Backspace']):not([data-key='Caps 🄰']):not([data-key='Shift ⇧']):not([data-key='Enter']):not([data-key='Tab ↹'])"
702
+ ".key:not([data-key='scrambled']):not([data-key='std']):not([data-key='scr']):not([data-key=' ']):not([data-key='Backspace']):not([data-key='Caps 🄰']):not([data-key='Shift ⇧']):not([data-key='Enter']):not([data-key='Tab ↹'])"
859
703
  );
860
704
  const englishAlphabet = "abcdefghijklmnopqrstuvwxyz/.,';\\][`1234567890-=".split("");
861
705
  this.shuffleArray(englishAlphabet);
@@ -868,7 +712,7 @@ export class VirtualKeyboard {
868
712
  // [13]
869
713
  scrambleThaiKeys() {
870
714
  const keys = document.querySelectorAll(
871
- ".key:not([data-key='scrambled']):not([data-key='scr']):not([data-key='Backspace']):not([data-key='Caps 🄰']):not([data-key='Shift ⇧']):not([data-key='Enter']):not([data-key='Space']):not([data-key='Tab ↹'])"
715
+ ".key:not([data-key='scrambled']):not([data-key='scr']):not([data-key='Backspace']):not([data-key='Caps 🄰']):not([data-key='Shift ⇧']):not([data-key='Enter']):not([data-key=' ']):not([data-key='Tab ↹'])"
872
716
  );
873
717
  const ThaiAlphabet = '_ๅ/-ภถุึคตจขชๆไำพะัีรนยบลฃฟหกดเ้่าสวงผปแอิืทมใฝ%+๑๒๓๔ู฿๕๖๗๘๙๐"ฎฑธํ๊ณฯญฐ,ฅฤฆฏโฌ็๋ษศซ.ฦฬฒ?์ฺฮฉ)('.split("");
874
718
  this.shuffleArray(ThaiAlphabet);
@@ -891,6 +735,21 @@ export class VirtualKeyboard {
891
735
  });
892
736
  }
893
737
 
738
+
739
+ scrambleFullLayoutKeys() {
740
+ const keys = document.querySelectorAll(
741
+ ".key:not(.concat-keys):not([data-key='scrambled']):not([data-key='std']):not([data-key='scr']):not([data-key=' ']):not([data-key='Backspace']):not([data-key='Caps 🄰']):not([data-key='Shift ⇧']):not([data-key='Enter']):not([data-key='Tab ↹'])"
742
+ );
743
+ const englishChars = "abcdefghijklmnopqrstuvwxyz1234567890;'\\/][`,.-=".split("");
744
+ this.shuffleArray(englishChars);
745
+ keys.forEach((key, index) => {
746
+ if (index < englishChars.length) {
747
+ key.textContent = englishChars[index];
748
+ key.dataset.key = englishChars[index];
749
+ }
750
+ });
751
+ }
752
+
894
753
  // [14] จัดการการลากคีย์บอร์ด
895
754
  startDrag(event) {
896
755
  this.isDragging = true;
package/layouts.js CHANGED
@@ -1,11 +1,11 @@
1
1
  export const layouts = {
2
2
  full: [
3
- // ["Esc", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "DEL⌦", "HOME", "END"],
3
+ // ["Esc", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "DEL⌦", "HOME", "END"],"ㅤ","ㅤ","ㅤ","ㅤ",
4
4
  ["`", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "Backspace"].concat(["+", "-", "*","/"]),
5
5
  ["Tab ↹", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "[", "]", "\\"].concat(["7", "8", "9", "%"]),
6
6
  ["Caps 🄰", "a", "s", "d", "f", "g", "h", "j", "k", "l", ";", "'", "Enter"].concat(["4", "5", "6", "_"]),
7
7
  ["Shift ⇧", "z", "x", "c", "v", "b", "n", "m", ",", ".", "/", "Shift ⇧", "↑"].concat(["1", "2", "3", "="]),
8
- ["@", " ", "←", "↓", "→"].concat(["0", "."]),
8
+ ["scrambled", " ", "←", "↓", "→"].concat(["0", "."]),
9
9
  ],
10
10
  en: [
11
11
  ["`", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "Backspace"],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "js_lis",
3
- "version": "2.0.6",
3
+ "version": "2.0.7",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1"
package/style/kbstyle.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // CSS-in-JS version of kbstyle.css
2
2
  export const css = `
3
- .keyboard-row,.virtual-keyboard{display:flex;display:flex}.keyb,.keyboard-key{cursor:pointer;font-size:16px;text-align:center}.virtual-keyboard{flex-direction:column;width:100%;max-width:800px;margin:0 auto;border:2px solid #333;border-radius:10px;padding:20px;background-color:#f5f5f5;box-shadow:0 4px 10px rgba(0,0,0,.1)}.keyboard-row{justify-content:space-between}.keyboard-key{flex:1;padding:10px 15px;margin:2px;border:1px solid #ccc;border-radius:5px;background-color:#fff;transition:background-color .2s}.keyboard-active,input:focus,textarea:focus{border-color:#007bff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.backspacew{flex:1.5;width:260px}.keyboard-key:hover{background:#e0e0e0}.keyboard-key:active{background:#d0d0d0;transform:translateY(1px)}.keyboard-row{display:flex;justify-content:flex-start;margin:5px 0}.controls{margin-bottom:20px}button,select{margin-right:10px;padding:8px 12px;border:1px solid #ccc;border-radius:4px;font-size:14px;cursor:pointer}button:hover{background:#f0f0f0}input:focus,textarea:focus{outline:0}#keyboard{position:absolute;cursor:move;top:80%;left:50%;transform:translate(-50%,-50%)}.keyb{color:#fff;border:#000;width:40px;height:25px;line-height:50px;margin:8px}.keyboard-row .active{background:#c7c7c7}body.dark-mode .virtual-keyboard{background-color:#333;box-shadow:0 4px 8px rgba(247,245,245,.1)}body.dark-mode{background-color:#333}body.dark-mode .login-container{background-color:#000;box-shadow:0 4px 8px rgba(247,245,245,.1);color:#fff}body.dark-mode .login-container input{background-color:#3333335d;color:#fff}body.dark-mode .login-container button{background-color:#005703;color:#fff}.fa-keyboard{color:#000}body.dark-mode .keyb .fa-keyboard{color:#ffffffde}body.dark-mode .keyboard-key.key{background-color:#222;color:#ffffffde}[data-key="Caps 🄰"],[data-key="Shift ⇧"],[data-key="Tab ↹"]{min-width:90px}.keyboard-key.key[data-key="Shift ⇧"]{color:#333}.virtual-keyboard.full [data-key=Enter]{min-width:95px}.virtual-keyboard.full [data-key="Shift ⇧"]{min-width:92px}.virtual-keyboard.full [data-key=" "]{min-width:620.8px}.virtual-keyboard.full .concat-keys[data-key="0"]{min-width:102px}.virtual-keyboard.full{min-width:1000px}.virtual-keyboard.Scnum,.virtual-keyboard.numpad{max-width:300px}.hidden{display:none!important}[data-key="Tab ↹"],[data-key=Backspace]{min-width:80px}[data-key="Shift ⇧"]{min-width:100px}[data-key=" "]{min-width:670px}
3
+ .keyboard-row,.virtual-keyboard{display:flex;display:flex}.keyb,.keyboard-key{cursor:pointer;font-size:16px;text-align:center}.virtual-keyboard{flex-direction:column;width:100%;max-width:800px;margin:0 auto;border:2px solid #333;border-radius:10px;padding:20px;background-color:#f5f5f5;box-shadow:0 4px 10px rgba(0,0,0,.1)}.keyboard-row{justify-content:space-between}.keyboard-key{flex:1;padding:10px 15px;margin:2px;border:1px solid #ccc;border-radius:5px;background-color:#fff;transition:background-color .2s}.keyboard-active,input:focus,textarea:focus{border-color:#007bff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.backspacew{flex:1.5;width:260px}.keyboard-key:hover{background:#e0e0e0}.keyboard-key:active{background:#d0d0d0;transform:translateY(1px)}.keyboard-row{display:flex;justify-content:flex-start;margin:5px 0}.controls{margin-bottom:20px}button,select{margin-right:10px;padding:8px 12px;border:1px solid #ccc;border-radius:4px;font-size:14px;cursor:pointer}button:hover{background:#f0f0f0}input:focus,textarea:focus{outline:0}#keyboard{position:absolute;cursor:move;top:80%;left:50%;transform:translate(-50%,-50%)}.keyb{color:#fff;border:#000;width:40px;height:25px;line-height:50px;margin:8px}.keyboard-row .active{background:#c7c7c7}body.dark-mode .virtual-keyboard{background-color:#333;box-shadow:0 4px 8px rgba(247,245,245,.1)}body.dark-mode{background-color:#333}body.dark-mode .login-container{background-color:#000;box-shadow:0 4px 8px rgba(247,245,245,.1);color:#fff}body.dark-mode .login-container input{background-color:#3333335d;color:#fff}body.dark-mode .login-container button{background-color:#005703;color:#fff}.fa-keyboard{color:#000}body.dark-mode .keyb .fa-keyboard{color:#ffffffde}body.dark-mode .keyboard-key.key{background-color:#222;color:#ffffffde}[data-key="Caps 🄰"],[data-key="Shift ⇧"],[data-key="Tab ↹"]{min-width:90px}.keyboard-key.key[data-key="Shift ⇧"]{color:#333}.virtual-keyboard.full [data-key=Enter]{min-width:95px}.virtual-keyboard.full [data-key="Shift ⇧"]{min-width:92px}.virtual-keyboard.full [data-key=" "]{min-width:563.8px}.virtual-keyboard.full .concat-keys[data-key="0"]{min-width:102px}.virtual-keyboard.full{min-width:1000px}.virtual-keyboard.Scnum,.virtual-keyboard.numpad{max-width:300px}.hidden{display:none!important}[data-key="Tab ↹"],[data-key=Backspace]{min-width:80px}[data-key="Shift ⇧"]{min-width:100px}[data-key=" "]{min-width:670px}
4
4
  `;
5
5
 
6
6
  export function injectCSS() {