@tindalabs/shield 0.1.0
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/LICENSE +9 -0
- package/README.md +357 -0
- package/dist/assess.d.ts +16 -0
- package/dist/assess.js +220 -0
- package/dist/config/default-extensions-config.json +103 -0
- package/dist/core/ContentProtector.d.ts +63 -0
- package/dist/core/ContentProtector.js +281 -0
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.js +2 -0
- package/dist/core/mediator/ContentProtectionMediator.d.ts +86 -0
- package/dist/core/mediator/ContentProtectionMediator.js +238 -0
- package/dist/core/mediator/eventDataTypes.d.ts +112 -0
- package/dist/core/mediator/eventDataTypes.js +23 -0
- package/dist/core/mediator/handlers/abstractEventHandler.d.ts +41 -0
- package/dist/core/mediator/handlers/abstractEventHandler.js +59 -0
- package/dist/core/mediator/handlers/devToolsEventHandler.d.ts +9 -0
- package/dist/core/mediator/handlers/devToolsEventHandler.js +95 -0
- package/dist/core/mediator/handlers/eventHandlerRegistry.d.ts +9 -0
- package/dist/core/mediator/handlers/eventHandlerRegistry.js +34 -0
- package/dist/core/mediator/handlers/extensionEventHandlers.d.ts +40 -0
- package/dist/core/mediator/handlers/extensionEventHandlers.js +140 -0
- package/dist/core/mediator/handlers/iFrameEventHandlers.d.ts +27 -0
- package/dist/core/mediator/handlers/iFrameEventHandlers.js +93 -0
- package/dist/core/mediator/handlers/screenShotEventHandlers.d.ts +34 -0
- package/dist/core/mediator/handlers/screenShotEventHandlers.js +111 -0
- package/dist/core/mediator/protection-event.d.ts +77 -0
- package/dist/core/mediator/protection-event.js +32 -0
- package/dist/core/mediator/types.d.ts +105 -0
- package/dist/core/mediator/types.js +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +7 -0
- package/dist/otel.d.ts +24 -0
- package/dist/otel.js +83 -0
- package/dist/policy.d.ts +98 -0
- package/dist/policy.js +97 -0
- package/dist/strategies/AbstractStrategy.d.ts +124 -0
- package/dist/strategies/AbstractStrategy.js +256 -0
- package/dist/strategies/ClipboardStrategy.d.ts +67 -0
- package/dist/strategies/ClipboardStrategy.js +291 -0
- package/dist/strategies/ContextMenuStrategy.d.ts +60 -0
- package/dist/strategies/ContextMenuStrategy.js +454 -0
- package/dist/strategies/DevToolsStrategy.d.ts +55 -0
- package/dist/strategies/DevToolsStrategy.js +314 -0
- package/dist/strategies/ExtensionStrategy.d.ts +66 -0
- package/dist/strategies/ExtensionStrategy.js +486 -0
- package/dist/strategies/IFrameStrategy.d.ts +49 -0
- package/dist/strategies/IFrameStrategy.js +255 -0
- package/dist/strategies/KeyboardStrategy.d.ts +35 -0
- package/dist/strategies/KeyboardStrategy.js +130 -0
- package/dist/strategies/PrintStrategy.d.ts +47 -0
- package/dist/strategies/PrintStrategy.js +201 -0
- package/dist/strategies/ScreenshotStrategy.d.ts +90 -0
- package/dist/strategies/ScreenshotStrategy.js +502 -0
- package/dist/strategies/SelectionStrategy.d.ts +49 -0
- package/dist/strategies/SelectionStrategy.js +216 -0
- package/dist/strategies/WatermarkStrategy.d.ts +56 -0
- package/dist/strategies/WatermarkStrategy.js +287 -0
- package/dist/strategies/index.d.ts +10 -0
- package/dist/strategies/index.js +11 -0
- package/dist/types/assessment.d.ts +62 -0
- package/dist/types/assessment.js +1 -0
- package/dist/types/index.d.ts +278 -0
- package/dist/types/index.js +17 -0
- package/dist/utils/DOMObserver.d.ts +68 -0
- package/dist/utils/DOMObserver.js +134 -0
- package/dist/utils/base/LoggableComponent.d.ts +44 -0
- package/dist/utils/base/LoggableComponent.js +56 -0
- package/dist/utils/detectors/AbstractDevToolsDetector.d.ts +98 -0
- package/dist/utils/detectors/AbstractDevToolsDetector.js +127 -0
- package/dist/utils/detectors/dateToStringDetector.d.ts +43 -0
- package/dist/utils/detectors/dateToStringDetector.js +96 -0
- package/dist/utils/detectors/debugLibDetector.d.ts +64 -0
- package/dist/utils/detectors/debugLibDetector.js +195 -0
- package/dist/utils/detectors/debuggerDetector.d.ts +51 -0
- package/dist/utils/detectors/debuggerDetector.js +211 -0
- package/dist/utils/detectors/defineGetterDetector.d.ts +48 -0
- package/dist/utils/detectors/defineGetterDetector.js +150 -0
- package/dist/utils/detectors/detectorInterface.d.ts +36 -0
- package/dist/utils/detectors/detectorInterface.js +1 -0
- package/dist/utils/detectors/devToolsDetectorManager.d.ts +88 -0
- package/dist/utils/detectors/devToolsDetectorManager.js +243 -0
- package/dist/utils/detectors/funcToStringDetector.d.ts +43 -0
- package/dist/utils/detectors/funcToStringDetector.js +90 -0
- package/dist/utils/detectors/regToStringDetector.d.ts +43 -0
- package/dist/utils/detectors/regToStringDetector.js +129 -0
- package/dist/utils/detectors/sizeDetector.d.ts +54 -0
- package/dist/utils/detectors/sizeDetector.js +134 -0
- package/dist/utils/detectors/timingDetector.d.ts +55 -0
- package/dist/utils/detectors/timingDetector.js +143 -0
- package/dist/utils/dom.d.ts +20 -0
- package/dist/utils/dom.js +83 -0
- package/dist/utils/environment.d.ts +29 -0
- package/dist/utils/environment.js +267 -0
- package/dist/utils/eventManager.d.ts +162 -0
- package/dist/utils/eventManager.js +548 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.js +3 -0
- package/dist/utils/intervalManager.d.ts +91 -0
- package/dist/utils/intervalManager.js +221 -0
- package/dist/utils/keyboardShortcutManager/keyboardShortcutManager.d.ts +41 -0
- package/dist/utils/keyboardShortcutManager/keyboardShortcutManager.js +135 -0
- package/dist/utils/keyboardShortcutManager/keyboardShortcuts.d.ts +18 -0
- package/dist/utils/keyboardShortcutManager/keyboardShortcuts.js +195 -0
- package/dist/utils/logging/simple/Loggable.d.ts +33 -0
- package/dist/utils/logging/simple/Loggable.js +1 -0
- package/dist/utils/logging/simple/LoggingDelegate.d.ts +42 -0
- package/dist/utils/logging/simple/LoggingDelegate.js +53 -0
- package/dist/utils/logging/simple/SimpleLoggingService.d.ts +39 -0
- package/dist/utils/logging/simple/SimpleLoggingService.js +58 -0
- package/dist/utils/orientation.d.ts +15 -0
- package/dist/utils/orientation.js +32 -0
- package/dist/utils/protectedContentManager.d.ts +155 -0
- package/dist/utils/protectedContentManager.js +424 -0
- package/dist/utils/securityOverlayManager.d.ts +253 -0
- package/dist/utils/securityOverlayManager.js +786 -0
- package/dist/utils/timeoutManager.d.ts +50 -0
- package/dist/utils/timeoutManager.js +113 -0
- package/package.json +61 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { isBrowser } from "./environment";
|
|
2
|
+
import { LoggableComponent } from "./base/LoggableComponent";
|
|
3
|
+
/**
|
|
4
|
+
* Manages periodic tasks for the content protection toolkit
|
|
5
|
+
* Consolidates multiple interval timers into a single efficient timer
|
|
6
|
+
*/
|
|
7
|
+
export class IntervalManager extends LoggableComponent {
|
|
8
|
+
/**
|
|
9
|
+
* Create a new IntervalManager
|
|
10
|
+
* @param debugMode Enable debug mode for troubleshooting
|
|
11
|
+
*/
|
|
12
|
+
constructor(debugMode = false) {
|
|
13
|
+
super("IntervalManager", debugMode);
|
|
14
|
+
this.tasks = new Map();
|
|
15
|
+
this.intervalId = null;
|
|
16
|
+
this.intervalFrequency = 500; // Base interval in ms (2 checks per second)
|
|
17
|
+
this.isRunning = false;
|
|
18
|
+
this.logger.log("Initialized");
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Register a new task to be executed periodically
|
|
22
|
+
* @param id Unique identifier for the task
|
|
23
|
+
* @param callback Function to execute
|
|
24
|
+
* @param frequency How often to run the task in milliseconds
|
|
25
|
+
* @returns The task ID for later reference
|
|
26
|
+
*/
|
|
27
|
+
registerTask(id, callback, frequency) {
|
|
28
|
+
if (!isBrowser())
|
|
29
|
+
return id;
|
|
30
|
+
// Ensure we have a unique ID by appending a timestamp if needed
|
|
31
|
+
let taskId = id;
|
|
32
|
+
if (this.tasks.has(taskId)) {
|
|
33
|
+
taskId = `${id}-${Date.now()}`;
|
|
34
|
+
}
|
|
35
|
+
this.tasks.set(taskId, {
|
|
36
|
+
id: taskId,
|
|
37
|
+
callback,
|
|
38
|
+
frequency,
|
|
39
|
+
lastRun: 0,
|
|
40
|
+
isActive: true,
|
|
41
|
+
});
|
|
42
|
+
this.logger.log(`Registered task "${taskId}" with frequency ${frequency}ms`);
|
|
43
|
+
// Start the interval if it's not already running
|
|
44
|
+
this.startInterval();
|
|
45
|
+
return taskId;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Unregister a task by ID
|
|
49
|
+
* @param id Task ID to remove
|
|
50
|
+
* @returns True if the task was found and removed
|
|
51
|
+
*/
|
|
52
|
+
unregisterTask(id) {
|
|
53
|
+
if (!isBrowser())
|
|
54
|
+
return false;
|
|
55
|
+
const result = this.tasks.delete(id);
|
|
56
|
+
if (result) {
|
|
57
|
+
this.logger.log(`Unregistered task "${id}"`);
|
|
58
|
+
}
|
|
59
|
+
// If no tasks remain, stop the interval
|
|
60
|
+
if (this.tasks.size === 0) {
|
|
61
|
+
this.stopInterval();
|
|
62
|
+
}
|
|
63
|
+
return result;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Pause a specific task without removing it
|
|
67
|
+
* @param id Task ID to pause
|
|
68
|
+
* @returns True if the task was found and paused
|
|
69
|
+
*/
|
|
70
|
+
pauseTask(id) {
|
|
71
|
+
if (!isBrowser())
|
|
72
|
+
return false;
|
|
73
|
+
const task = this.tasks.get(id);
|
|
74
|
+
if (task) {
|
|
75
|
+
task.isActive = false;
|
|
76
|
+
this.tasks.set(id, task);
|
|
77
|
+
this.logger.log(`Paused task "${id}"`);
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Resume a paused task
|
|
84
|
+
* @param id Task ID to resume
|
|
85
|
+
* @returns True if the task was found and resumed
|
|
86
|
+
*/
|
|
87
|
+
resumeTask(id) {
|
|
88
|
+
if (!isBrowser())
|
|
89
|
+
return false;
|
|
90
|
+
const task = this.tasks.get(id);
|
|
91
|
+
if (task) {
|
|
92
|
+
task.isActive = true;
|
|
93
|
+
this.tasks.set(id, task);
|
|
94
|
+
this.logger.log(`Resumed task "${id}"`);
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Start the interval timer
|
|
101
|
+
*/
|
|
102
|
+
startInterval() {
|
|
103
|
+
if (this.isRunning || !isBrowser())
|
|
104
|
+
return;
|
|
105
|
+
this.intervalId = window.setInterval(() => {
|
|
106
|
+
this.executeEligibleTasks();
|
|
107
|
+
}, this.intervalFrequency);
|
|
108
|
+
this.isRunning = true;
|
|
109
|
+
this.logger.log(`Started interval timer with frequency ${this.intervalFrequency}ms`);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Stop the interval timer
|
|
113
|
+
*/
|
|
114
|
+
stopInterval() {
|
|
115
|
+
if (!this.isRunning || !isBrowser())
|
|
116
|
+
return;
|
|
117
|
+
if (this.intervalId !== null) {
|
|
118
|
+
window.clearInterval(this.intervalId);
|
|
119
|
+
this.intervalId = null;
|
|
120
|
+
}
|
|
121
|
+
this.isRunning = false;
|
|
122
|
+
this.logger.log("Stopped interval timer");
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Execute all tasks that are due to run
|
|
126
|
+
*/
|
|
127
|
+
executeEligibleTasks() {
|
|
128
|
+
const now = Date.now();
|
|
129
|
+
this.tasks.forEach((task) => {
|
|
130
|
+
// Skip inactive tasks
|
|
131
|
+
if (!task.isActive)
|
|
132
|
+
return;
|
|
133
|
+
// Check if the task is due to run
|
|
134
|
+
const timeSinceLastRun = now - (task.lastRun || 0);
|
|
135
|
+
if (timeSinceLastRun >= task.frequency) {
|
|
136
|
+
try {
|
|
137
|
+
this.logger.log(`Executing task "${task.id}"`);
|
|
138
|
+
// Execute the task
|
|
139
|
+
task.callback();
|
|
140
|
+
// Update the last run time
|
|
141
|
+
task.lastRun = now;
|
|
142
|
+
this.tasks.set(task.id, task);
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
this.logger.error(`Error executing task "${task.id}":`, error);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Force execution of a specific task immediately
|
|
152
|
+
* @param id Task ID to execute
|
|
153
|
+
* @returns True if the task was found and executed
|
|
154
|
+
*/
|
|
155
|
+
executeTaskNow(id) {
|
|
156
|
+
if (!isBrowser())
|
|
157
|
+
return false;
|
|
158
|
+
const task = this.tasks.get(id);
|
|
159
|
+
if (task && task.isActive) {
|
|
160
|
+
try {
|
|
161
|
+
this.logger.log(`Executing task "${id}" immediately`);
|
|
162
|
+
task.callback();
|
|
163
|
+
task.lastRun = Date.now();
|
|
164
|
+
this.tasks.set(id, task);
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
this.logger.error(`Error executing task "${id}" immediately:`, error);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Get the current status of all registered tasks
|
|
175
|
+
* @returns Array of task status objects
|
|
176
|
+
*/
|
|
177
|
+
getTasksStatus() {
|
|
178
|
+
const now = Date.now();
|
|
179
|
+
const result = [];
|
|
180
|
+
this.tasks.forEach((task) => {
|
|
181
|
+
result.push({
|
|
182
|
+
id: task.id,
|
|
183
|
+
isActive: task.isActive,
|
|
184
|
+
frequency: task.frequency,
|
|
185
|
+
lastRun: task.lastRun,
|
|
186
|
+
timeSinceLastRun: task.lastRun ? now - task.lastRun : null,
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
return result;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Clean up and stop all intervals
|
|
193
|
+
*/
|
|
194
|
+
dispose() {
|
|
195
|
+
if (!isBrowser())
|
|
196
|
+
return;
|
|
197
|
+
this.stopInterval();
|
|
198
|
+
this.tasks.clear();
|
|
199
|
+
this.logger.log("Disposed");
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Update the base interval frequency
|
|
203
|
+
* @param frequency New frequency in milliseconds
|
|
204
|
+
*/
|
|
205
|
+
setIntervalFrequency(frequency) {
|
|
206
|
+
if (frequency < 100) {
|
|
207
|
+
this.logger.warn("Frequency below 100ms may cause performance issues");
|
|
208
|
+
}
|
|
209
|
+
this.intervalFrequency = frequency;
|
|
210
|
+
// Restart the interval with the new frequency if it's running
|
|
211
|
+
if (this.isRunning) {
|
|
212
|
+
this.stopInterval();
|
|
213
|
+
this.startInterval();
|
|
214
|
+
}
|
|
215
|
+
this.logger.log(`Updated interval frequency to ${frequency}ms`);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Create a singleton instance for use throughout the application
|
|
220
|
+
*/
|
|
221
|
+
export const intervalManager = new IntervalManager(false);
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { ShortcutCategory, KeyboardShortcut } from "./keyboardShortcuts";
|
|
2
|
+
export type OSType = "windows" | "mac" | "linux" | "unknown";
|
|
3
|
+
export declare class KeyboardShortcutManager {
|
|
4
|
+
private static instance;
|
|
5
|
+
private shortcuts;
|
|
6
|
+
private osType;
|
|
7
|
+
private constructor();
|
|
8
|
+
static getInstance(): KeyboardShortcutManager;
|
|
9
|
+
/**
|
|
10
|
+
* Get all registered shortcuts
|
|
11
|
+
*/
|
|
12
|
+
getAllShortcuts(): KeyboardShortcut[];
|
|
13
|
+
/**
|
|
14
|
+
* Get shortcuts by category
|
|
15
|
+
*/
|
|
16
|
+
getShortcutsByCategory(category: ShortcutCategory): KeyboardShortcut[];
|
|
17
|
+
/**
|
|
18
|
+
* Get shortcuts for multiple categories
|
|
19
|
+
*/
|
|
20
|
+
getShortcutsByCategories(categories: ShortcutCategory[]): KeyboardShortcut[];
|
|
21
|
+
/**
|
|
22
|
+
* Check if a keyboard event matches any shortcut in the given categories
|
|
23
|
+
*/
|
|
24
|
+
matchesShortcut(event: KeyboardEvent, categories?: ShortcutCategory[]): KeyboardShortcut | null;
|
|
25
|
+
/**
|
|
26
|
+
* Check if a keyboard event matches a specific shortcut
|
|
27
|
+
*/
|
|
28
|
+
private eventMatchesShortcut;
|
|
29
|
+
/**
|
|
30
|
+
* Check if modifiers in the event match the required modifiers
|
|
31
|
+
*/
|
|
32
|
+
private checkModifiers;
|
|
33
|
+
/**
|
|
34
|
+
* Get a description of a keyboard shortcut for the current OS
|
|
35
|
+
*/
|
|
36
|
+
getShortcutDescription(shortcut: KeyboardShortcut): string;
|
|
37
|
+
/**
|
|
38
|
+
* Format keys for display (e.g., "Ctrl+C")
|
|
39
|
+
*/
|
|
40
|
+
private formatKeysForDisplay;
|
|
41
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { ShortcutCategory, ALL_SHORTCUTS } from "./keyboardShortcuts";
|
|
2
|
+
import { getOS } from "../environment";
|
|
3
|
+
export class KeyboardShortcutManager {
|
|
4
|
+
constructor() {
|
|
5
|
+
this.shortcuts = ALL_SHORTCUTS;
|
|
6
|
+
const os = getOS();
|
|
7
|
+
this.osType = os.name;
|
|
8
|
+
}
|
|
9
|
+
static getInstance() {
|
|
10
|
+
if (!KeyboardShortcutManager.instance) {
|
|
11
|
+
KeyboardShortcutManager.instance = new KeyboardShortcutManager();
|
|
12
|
+
}
|
|
13
|
+
return KeyboardShortcutManager.instance;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Get all registered shortcuts
|
|
17
|
+
*/
|
|
18
|
+
getAllShortcuts() {
|
|
19
|
+
return this.shortcuts;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Get shortcuts by category
|
|
23
|
+
*/
|
|
24
|
+
getShortcutsByCategory(category) {
|
|
25
|
+
return this.shortcuts.filter(shortcut => shortcut.category === category);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Get shortcuts for multiple categories
|
|
29
|
+
*/
|
|
30
|
+
getShortcutsByCategories(categories) {
|
|
31
|
+
return this.shortcuts.filter(shortcut => categories.includes(shortcut.category));
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Check if a keyboard event matches any shortcut in the given categories
|
|
35
|
+
*/
|
|
36
|
+
matchesShortcut(event, categories = Object.values(ShortcutCategory)) {
|
|
37
|
+
const relevantShortcuts = this.getShortcutsByCategories(categories);
|
|
38
|
+
for (const shortcut of relevantShortcuts) {
|
|
39
|
+
if (this.eventMatchesShortcut(event, shortcut)) {
|
|
40
|
+
return shortcut;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Check if a keyboard event matches a specific shortcut
|
|
47
|
+
*/
|
|
48
|
+
eventMatchesShortcut(event, shortcut) {
|
|
49
|
+
// Get the appropriate keys based on platform
|
|
50
|
+
let keysToCheck = shortcut.keys;
|
|
51
|
+
if (this.osType === "mac" && shortcut.macKeys) {
|
|
52
|
+
keysToCheck = shortcut.macKeys;
|
|
53
|
+
}
|
|
54
|
+
else if (this.osType === "linux" && shortcut.linuxKeys) {
|
|
55
|
+
keysToCheck = shortcut.linuxKeys;
|
|
56
|
+
}
|
|
57
|
+
// If there are no keys for this platform, this shortcut doesn't apply
|
|
58
|
+
if (!keysToCheck || keysToCheck.length === 0) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
// Special case for modifier keys
|
|
62
|
+
const modifiersMatch = this.checkModifiers(event, keysToCheck);
|
|
63
|
+
// Check if the main key matches (non-modifier key)
|
|
64
|
+
const mainKeys = keysToCheck.filter(key => !["Control", "Alt", "Shift", "Meta", "Win", "Option"].includes(key));
|
|
65
|
+
// If there's no main key, just check modifiers
|
|
66
|
+
if (mainKeys.length === 0) {
|
|
67
|
+
return modifiersMatch;
|
|
68
|
+
}
|
|
69
|
+
// Check if any of the main keys match the event key
|
|
70
|
+
const keyMatches = mainKeys.some(key => {
|
|
71
|
+
// Handle special cases like PrintScreen which might be reported differently
|
|
72
|
+
if (key === "PrintScreen" &&
|
|
73
|
+
(event.key === "PrintScreen" ||
|
|
74
|
+
event.key === "PrtScn" ||
|
|
75
|
+
event.key === "Print" ||
|
|
76
|
+
event.code === "PrintScreen" ||
|
|
77
|
+
event.keyCode === 44)) {
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
// Handle function keys
|
|
81
|
+
if (key.startsWith("F") && !isNaN(parseInt(key.substring(1)))) {
|
|
82
|
+
return event.key === key;
|
|
83
|
+
}
|
|
84
|
+
// Normal key matching
|
|
85
|
+
return event.key.toLowerCase() === key.toLowerCase() ||
|
|
86
|
+
event.code.toLowerCase() === key.toLowerCase();
|
|
87
|
+
});
|
|
88
|
+
return modifiersMatch && keyMatches;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Check if modifiers in the event match the required modifiers
|
|
92
|
+
*/
|
|
93
|
+
checkModifiers(event, keys) {
|
|
94
|
+
// Handle different naming conventions
|
|
95
|
+
const hasCtrl = keys.includes("Control") || keys.includes("Ctrl");
|
|
96
|
+
const hasAlt = keys.includes("Alt") || keys.includes("Option");
|
|
97
|
+
const hasShift = keys.includes("Shift");
|
|
98
|
+
const hasMeta = keys.includes("Meta") || keys.includes("Win") || keys.includes("Command");
|
|
99
|
+
return ((hasCtrl === event.ctrlKey) &&
|
|
100
|
+
(hasAlt === event.altKey) &&
|
|
101
|
+
(hasShift === event.shiftKey) &&
|
|
102
|
+
(hasMeta === event.metaKey));
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Get a description of a keyboard shortcut for the current OS
|
|
106
|
+
*/
|
|
107
|
+
getShortcutDescription(shortcut) {
|
|
108
|
+
let keysToUse = shortcut.keys;
|
|
109
|
+
if (this.osType === "mac" && shortcut.macKeys) {
|
|
110
|
+
keysToUse = shortcut.macKeys;
|
|
111
|
+
}
|
|
112
|
+
else if (this.osType === "linux" && shortcut.linuxKeys) {
|
|
113
|
+
keysToUse = shortcut.linuxKeys;
|
|
114
|
+
}
|
|
115
|
+
// Format the keys for display
|
|
116
|
+
return this.formatKeysForDisplay(keysToUse);
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Format keys for display (e.g., "Ctrl+C")
|
|
120
|
+
*/
|
|
121
|
+
formatKeysForDisplay(keys) {
|
|
122
|
+
return keys.map(key => {
|
|
123
|
+
// Replace with more readable versions
|
|
124
|
+
switch (key) {
|
|
125
|
+
case "Control": return this.osType === "mac" ? "Ctrl" : "Ctrl";
|
|
126
|
+
case "Meta": return this.osType === "mac" ? "⌘" : "Win";
|
|
127
|
+
case "Alt": return this.osType === "mac" ? "Option" : "Alt";
|
|
128
|
+
case "Shift": return this.osType === "mac" ? "⇧" : "Shift";
|
|
129
|
+
case "Option": return "Option";
|
|
130
|
+
case "Win": return "Win";
|
|
131
|
+
default: return key;
|
|
132
|
+
}
|
|
133
|
+
}).join(this.osType === "mac" ? " + " : "+");
|
|
134
|
+
}
|
|
135
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare enum ShortcutCategory {
|
|
2
|
+
COPY = "copy",
|
|
3
|
+
SAVE = "save",
|
|
4
|
+
PRINT = "print",
|
|
5
|
+
VIEW_SOURCE = "viewSource",
|
|
6
|
+
DEVTOOLS = "devTools",
|
|
7
|
+
SCREENSHOT = "screenshot",
|
|
8
|
+
FULLSCREEN = "fullscreen"
|
|
9
|
+
}
|
|
10
|
+
export interface KeyboardShortcut {
|
|
11
|
+
id: string;
|
|
12
|
+
category: ShortcutCategory;
|
|
13
|
+
description: string;
|
|
14
|
+
keys: string[];
|
|
15
|
+
macKeys?: string[];
|
|
16
|
+
linuxKeys?: string[];
|
|
17
|
+
}
|
|
18
|
+
export declare const ALL_SHORTCUTS: KeyboardShortcut[];
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
export var ShortcutCategory;
|
|
2
|
+
(function (ShortcutCategory) {
|
|
3
|
+
ShortcutCategory["COPY"] = "copy";
|
|
4
|
+
ShortcutCategory["SAVE"] = "save";
|
|
5
|
+
ShortcutCategory["PRINT"] = "print";
|
|
6
|
+
ShortcutCategory["VIEW_SOURCE"] = "viewSource";
|
|
7
|
+
ShortcutCategory["DEVTOOLS"] = "devTools";
|
|
8
|
+
ShortcutCategory["SCREENSHOT"] = "screenshot";
|
|
9
|
+
ShortcutCategory["FULLSCREEN"] = "fullscreen";
|
|
10
|
+
})(ShortcutCategory || (ShortcutCategory = {}));
|
|
11
|
+
// Define all shortcuts
|
|
12
|
+
export const ALL_SHORTCUTS = [
|
|
13
|
+
{
|
|
14
|
+
id: "save",
|
|
15
|
+
category: ShortcutCategory.SAVE,
|
|
16
|
+
description: "Save page",
|
|
17
|
+
keys: ["Control", "s"],
|
|
18
|
+
macKeys: ["Meta", "s"],
|
|
19
|
+
// Linux typically uses the same as Windows for this
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
id: "print",
|
|
23
|
+
category: ShortcutCategory.PRINT,
|
|
24
|
+
description: "Print page",
|
|
25
|
+
keys: ["Control", "p"],
|
|
26
|
+
macKeys: ["Meta", "p"],
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: "view-source",
|
|
30
|
+
category: ShortcutCategory.VIEW_SOURCE,
|
|
31
|
+
description: "View page source",
|
|
32
|
+
keys: ["Control", "u"],
|
|
33
|
+
macKeys: ["Meta", "u"],
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
id: "copy",
|
|
37
|
+
category: ShortcutCategory.COPY,
|
|
38
|
+
description: "Copy selected content",
|
|
39
|
+
keys: ["Control", "c"],
|
|
40
|
+
macKeys: ["Meta", "c"],
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: "cut",
|
|
44
|
+
category: ShortcutCategory.COPY,
|
|
45
|
+
description: "Cut selected content",
|
|
46
|
+
keys: ["Control", "x"],
|
|
47
|
+
macKeys: ["Meta", "x"],
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
id: "devtools-open",
|
|
51
|
+
category: ShortcutCategory.DEVTOOLS,
|
|
52
|
+
description: "Open developer tools",
|
|
53
|
+
keys: ["F12"],
|
|
54
|
+
macKeys: ["F12"],
|
|
55
|
+
linuxKeys: ["F12"],
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
id: "devtools-inspect",
|
|
59
|
+
category: ShortcutCategory.DEVTOOLS,
|
|
60
|
+
description: "Open developer tools inspector",
|
|
61
|
+
keys: ["Control", "Shift", "i"],
|
|
62
|
+
macKeys: ["Meta", "Option", "i"],
|
|
63
|
+
linuxKeys: ["Control", "Shift", "i"],
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
id: "devtools-console",
|
|
67
|
+
category: ShortcutCategory.DEVTOOLS,
|
|
68
|
+
description: "Open developer tools console",
|
|
69
|
+
keys: ["Control", "Shift", "j"],
|
|
70
|
+
macKeys: ["Meta", "Option", "j"],
|
|
71
|
+
linuxKeys: ["Control", "Shift", "j"],
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
id: "screenshot-printscreen",
|
|
75
|
+
category: ShortcutCategory.SCREENSHOT,
|
|
76
|
+
description: "Take screenshot (Windows/Linux)",
|
|
77
|
+
keys: ["PrintScreen"],
|
|
78
|
+
linuxKeys: ["PrintScreen"],
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
id: "screenshot-area-linux",
|
|
82
|
+
category: ShortcutCategory.SCREENSHOT,
|
|
83
|
+
description: "Take area screenshot (Linux)",
|
|
84
|
+
keys: ["Shift", "PrintScreen"],
|
|
85
|
+
linuxKeys: ["Shift", "PrintScreen"],
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
id: "screenshot-window-linux",
|
|
89
|
+
category: ShortcutCategory.SCREENSHOT,
|
|
90
|
+
description: "Take window screenshot (Linux)",
|
|
91
|
+
keys: ["Alt", "PrintScreen"],
|
|
92
|
+
linuxKeys: ["Alt", "PrintScreen"],
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
id: "screenshot-clipboard-linux",
|
|
96
|
+
category: ShortcutCategory.SCREENSHOT,
|
|
97
|
+
description: "Copy screenshot to clipboard (Linux)",
|
|
98
|
+
keys: ["Control", "PrintScreen"],
|
|
99
|
+
linuxKeys: ["Control", "PrintScreen"],
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
id: "screenshot-gnome-special",
|
|
103
|
+
category: ShortcutCategory.SCREENSHOT,
|
|
104
|
+
description: "GNOME special screenshot shortcut",
|
|
105
|
+
keys: ["Control", "Shift", "p"],
|
|
106
|
+
linuxKeys: ["Control", "Shift", "p"],
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
id: "screenshot-flameshot",
|
|
110
|
+
category: ShortcutCategory.SCREENSHOT,
|
|
111
|
+
description: "Flameshot screenshot tool (Linux)",
|
|
112
|
+
keys: ["F10"],
|
|
113
|
+
linuxKeys: ["F10"],
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
id: "screenshot-mac-full",
|
|
117
|
+
category: ShortcutCategory.SCREENSHOT,
|
|
118
|
+
description: "Take full screenshot (Mac)",
|
|
119
|
+
keys: [], // Empty for Windows/Linux
|
|
120
|
+
macKeys: ["Shift", "Meta", "3"],
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
id: "screenshot-mac-area",
|
|
124
|
+
category: ShortcutCategory.SCREENSHOT,
|
|
125
|
+
description: "Take area screenshot (Mac)",
|
|
126
|
+
keys: [], // Empty for Windows/Linux
|
|
127
|
+
macKeys: ["Shift", "Meta", "4"],
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
id: "screenshot-mac-tools",
|
|
131
|
+
category: ShortcutCategory.SCREENSHOT,
|
|
132
|
+
description: "Open screenshot tools (Mac)",
|
|
133
|
+
keys: [], // Empty for Windows/Linux
|
|
134
|
+
macKeys: ["Shift", "Meta", "5"],
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
id: "screenshot-win-snipping",
|
|
138
|
+
category: ShortcutCategory.SCREENSHOT,
|
|
139
|
+
description: "Open Snipping Tool (Windows)",
|
|
140
|
+
keys: ["Win", "Shift", "s"],
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
id: "screenshot-win-snapshot",
|
|
144
|
+
category: ShortcutCategory.SCREENSHOT,
|
|
145
|
+
description: "Windows SnapshotKey",
|
|
146
|
+
keys: ["SnapshotKey"],
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
id: "screenshot-firefox-save",
|
|
150
|
+
category: ShortcutCategory.SCREENSHOT,
|
|
151
|
+
description: "Firefox Save Page As (can be used for screenshots)",
|
|
152
|
+
keys: ["Alt", "s"],
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
id: "fullscreen",
|
|
156
|
+
category: ShortcutCategory.FULLSCREEN,
|
|
157
|
+
description: "Toggle fullscreen mode",
|
|
158
|
+
keys: ["F11"],
|
|
159
|
+
macKeys: ["F11"],
|
|
160
|
+
linuxKeys: ["F11"],
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
id: "fullscreen-alt-enter",
|
|
164
|
+
category: ShortcutCategory.FULLSCREEN,
|
|
165
|
+
description: "Toggle fullscreen mode (Alt+Enter)",
|
|
166
|
+
keys: ["Alt", "Enter"],
|
|
167
|
+
linuxKeys: ["Alt", "Enter"],
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
id: "fullscreen-ctrl-f",
|
|
171
|
+
category: ShortcutCategory.FULLSCREEN,
|
|
172
|
+
description: "Toggle fullscreen in some browsers",
|
|
173
|
+
keys: ["Control", "f"],
|
|
174
|
+
macKeys: ["Meta", "f"],
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
id: "fullscreen-alt-f",
|
|
178
|
+
category: ShortcutCategory.FULLSCREEN,
|
|
179
|
+
description: "Toggle fullscreen in some applications",
|
|
180
|
+
keys: ["Alt", "f"],
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
id: "fullscreen-alt-k",
|
|
184
|
+
category: ShortcutCategory.FULLSCREEN,
|
|
185
|
+
description: "Toggle fullscreen in some media players",
|
|
186
|
+
keys: ["Alt", "k"],
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
id: "fullscreen-mac",
|
|
190
|
+
category: ShortcutCategory.FULLSCREEN,
|
|
191
|
+
description: "Toggle fullscreen on Mac",
|
|
192
|
+
keys: [],
|
|
193
|
+
macKeys: ["Control", "Meta", "f"],
|
|
194
|
+
},
|
|
195
|
+
];
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interface for components that support logging
|
|
3
|
+
*/
|
|
4
|
+
export interface Loggable {
|
|
5
|
+
/**
|
|
6
|
+
* Log a debug message if debug mode is enabled
|
|
7
|
+
* @param message Message to log
|
|
8
|
+
* @param args Additional arguments to log
|
|
9
|
+
*/
|
|
10
|
+
log(message: string, ...args: unknown[]): void;
|
|
11
|
+
/**
|
|
12
|
+
* Log a warning message
|
|
13
|
+
* @param message Warning message
|
|
14
|
+
* @param args Additional arguments to log
|
|
15
|
+
*/
|
|
16
|
+
warn(message: string, ...args: unknown[]): void;
|
|
17
|
+
/**
|
|
18
|
+
* Log an error message
|
|
19
|
+
* @param message Error message
|
|
20
|
+
* @param args Additional arguments to log
|
|
21
|
+
*/
|
|
22
|
+
error(message: string, ...args: unknown[]): void;
|
|
23
|
+
/**
|
|
24
|
+
* Set debug mode
|
|
25
|
+
* @param enabled Whether debug mode should be enabled
|
|
26
|
+
*/
|
|
27
|
+
setDebugMode(enabled: boolean): void;
|
|
28
|
+
/**
|
|
29
|
+
* Check if debug mode is enabled
|
|
30
|
+
* @returns Whether debug mode is enabled
|
|
31
|
+
*/
|
|
32
|
+
isDebugEnabled(): boolean;
|
|
33
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Loggable } from "./Loggable";
|
|
2
|
+
/**
|
|
3
|
+
* Delegate class that implements logging functionality
|
|
4
|
+
* Can be composed into any class that needs logging
|
|
5
|
+
*/
|
|
6
|
+
export declare class LoggingDelegate implements Loggable {
|
|
7
|
+
private logger;
|
|
8
|
+
/**
|
|
9
|
+
* Create a new LoggingDelegate
|
|
10
|
+
* @param componentName Name of the component for log prefixing
|
|
11
|
+
* @param debugMode Whether debug mode is enabled
|
|
12
|
+
*/
|
|
13
|
+
constructor(componentName: string, debugMode?: boolean);
|
|
14
|
+
/**
|
|
15
|
+
* Log a debug message if debug mode is enabled
|
|
16
|
+
* @param message Message to log
|
|
17
|
+
* @param args Additional arguments to log
|
|
18
|
+
*/
|
|
19
|
+
log(message: string, ...args: unknown[]): void;
|
|
20
|
+
/**
|
|
21
|
+
* Log a warning message
|
|
22
|
+
* @param message Warning message
|
|
23
|
+
* @param args Additional arguments to log
|
|
24
|
+
*/
|
|
25
|
+
warn(message: string, ...args: unknown[]): void;
|
|
26
|
+
/**
|
|
27
|
+
* Log an error message
|
|
28
|
+
* @param message Error message
|
|
29
|
+
* @param args Additional arguments to log
|
|
30
|
+
*/
|
|
31
|
+
error(message: string, ...args: unknown[]): void;
|
|
32
|
+
/**
|
|
33
|
+
* Set debug mode
|
|
34
|
+
* @param enabled Whether debug mode should be enabled
|
|
35
|
+
*/
|
|
36
|
+
setDebugMode(enabled: boolean): void;
|
|
37
|
+
/**
|
|
38
|
+
* Check if debug mode is enabled
|
|
39
|
+
* @returns Whether debug mode is enabled
|
|
40
|
+
*/
|
|
41
|
+
isDebugEnabled(): boolean;
|
|
42
|
+
}
|