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.
- package/VirtualKeyboard.js +39 -180
- package/layouts.js +2 -2
- package/package.json +1 -1
- package/style/kbstyle.js +1 -1
package/VirtualKeyboard.js
CHANGED
|
@@ -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;
|
|
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
|
-
|
|
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='
|
|
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='
|
|
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
|
-
["
|
|
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
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:
|
|
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() {
|