chrometools-mcp 2.4.2 → 3.1.2
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/CHANGELOG.md +540 -0
- package/COMPONENT_MAPPING_SPEC.md +1217 -0
- package/README.md +494 -38
- package/bridge/bridge-client.js +472 -0
- package/bridge/bridge-service.js +399 -0
- package/bridge/install.js +241 -0
- package/browser/browser-manager.js +107 -2
- package/browser/page-manager.js +226 -69
- package/docs/CHROME_EXTENSION.md +219 -0
- package/docs/PAGE_OBJECT_MODEL_CONCEPT.md +1756 -0
- package/element-finder-utils.js +138 -28
- package/extension/background.js +643 -0
- package/extension/content.js +715 -0
- package/extension/icons/create-icons.js +164 -0
- package/extension/icons/icon128.png +0 -0
- package/extension/icons/icon16.png +0 -0
- package/extension/icons/icon48.png +0 -0
- package/extension/manifest.json +58 -0
- package/extension/popup/popup.css +437 -0
- package/extension/popup/popup.html +102 -0
- package/extension/popup/popup.js +415 -0
- package/extension/recorder-overlay.css +93 -0
- package/figma-tools.js +120 -0
- package/index.js +3347 -2518
- package/models/BaseInputModel.js +93 -0
- package/models/CheckboxGroupModel.js +199 -0
- package/models/CheckboxModel.js +103 -0
- package/models/ColorInputModel.js +53 -0
- package/models/DateInputModel.js +67 -0
- package/models/RadioGroupModel.js +126 -0
- package/models/RangeInputModel.js +60 -0
- package/models/SelectModel.js +97 -0
- package/models/TextInputModel.js +34 -0
- package/models/TextareaModel.js +59 -0
- package/models/TimeInputModel.js +49 -0
- package/models/index.js +122 -0
- package/package.json +3 -2
- package/pom/apom-converter.js +267 -0
- package/pom/apom-tree-converter.js +515 -0
- package/pom/element-id-generator.js +175 -0
- package/recorder/page-object-generator.js +16 -0
- package/recorder/scenario-executor.js +80 -2
- package/server/tool-definitions.js +839 -656
- package/server/tool-groups.js +3 -2
- package/server/tool-schemas.js +367 -296
- package/server/websocket-bridge.js +447 -0
- package/utils/selector-resolver.js +186 -0
- package/utils/ui-framework-detector.js +392 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create ChromeTools extension icons
|
|
3
|
+
* Run with: node create-icons.js
|
|
4
|
+
*
|
|
5
|
+
* Creates a happy robot icon with Chrome colors
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import Jimp from 'jimp';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import { fileURLToPath } from 'url';
|
|
11
|
+
|
|
12
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
|
|
14
|
+
// Colors (RGBA format for Jimp)
|
|
15
|
+
const CHROME_BLUE = 0x4285F4FF;
|
|
16
|
+
const CHROME_GREEN = 0x34A853FF;
|
|
17
|
+
const CHROME_YELLOW = 0xFBBC05FF;
|
|
18
|
+
const CHROME_RED = 0xEA4335FF;
|
|
19
|
+
const ROBOT_BODY = 0x5C6BC0FF; // Nice indigo/purple
|
|
20
|
+
const ROBOT_FACE = 0x7986CBFF; // Lighter purple for face
|
|
21
|
+
const ROBOT_LIGHT = 0x9FA8DAFF; // Highlight
|
|
22
|
+
const WHITE = 0xFFFFFFFF;
|
|
23
|
+
const BLACK = 0x333333FF;
|
|
24
|
+
const SMILE_COLOR = 0xFFFFFFFF;
|
|
25
|
+
const ANTENNA_COLOR = 0xFFD54FFF; // Golden antenna ball
|
|
26
|
+
const TRANSPARENT = 0x00000000;
|
|
27
|
+
|
|
28
|
+
async function createIcon(size) {
|
|
29
|
+
const image = new Jimp(size, size, TRANSPARENT);
|
|
30
|
+
|
|
31
|
+
const center = size / 2;
|
|
32
|
+
const headRadius = size * 0.38;
|
|
33
|
+
|
|
34
|
+
// Draw robot head (rounded rectangle approximation with circle)
|
|
35
|
+
for (let y = 0; y < size; y++) {
|
|
36
|
+
for (let x = 0; x < size; x++) {
|
|
37
|
+
const dx = x - center;
|
|
38
|
+
const dy = y - center + size * 0.05; // Slightly lower
|
|
39
|
+
const dist = Math.sqrt(dx * dx + dy * dy);
|
|
40
|
+
|
|
41
|
+
// Head shape (circle with flat bottom)
|
|
42
|
+
if (dist <= headRadius && y < center + headRadius * 0.85) {
|
|
43
|
+
// Gradient effect - lighter at top
|
|
44
|
+
const gradientFactor = 1 - (y / size) * 0.3;
|
|
45
|
+
if (dy < -headRadius * 0.3) {
|
|
46
|
+
image.setPixelColor(ROBOT_LIGHT, x, y);
|
|
47
|
+
} else {
|
|
48
|
+
image.setPixelColor(ROBOT_FACE, x, y);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Draw ears (Chrome colored circles on sides)
|
|
55
|
+
const earRadius = size * 0.12;
|
|
56
|
+
const earY = center;
|
|
57
|
+
drawCircle(image, center - headRadius - earRadius * 0.3, earY, earRadius, CHROME_BLUE);
|
|
58
|
+
drawCircle(image, center + headRadius + earRadius * 0.3, earY, earRadius, CHROME_GREEN);
|
|
59
|
+
|
|
60
|
+
// Draw antenna
|
|
61
|
+
const antennaX = center;
|
|
62
|
+
const antennaTopY = center - headRadius - size * 0.12;
|
|
63
|
+
const antennaBottomY = center - headRadius + size * 0.05;
|
|
64
|
+
|
|
65
|
+
// Antenna stem
|
|
66
|
+
for (let y = antennaTopY + size * 0.06; y <= antennaBottomY; y++) {
|
|
67
|
+
const thickness = Math.max(1, size * 0.03);
|
|
68
|
+
for (let dx = -thickness; dx <= thickness; dx++) {
|
|
69
|
+
image.setPixelColor(ROBOT_LIGHT, Math.round(antennaX + dx), Math.round(y));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Antenna ball (golden/yellow)
|
|
74
|
+
drawCircle(image, antennaX, antennaTopY + size * 0.03, size * 0.07, ANTENNA_COLOR);
|
|
75
|
+
|
|
76
|
+
// Draw happy eyes (big and expressive)
|
|
77
|
+
const eyeRadius = size * 0.11;
|
|
78
|
+
const eyeOffset = size * 0.15;
|
|
79
|
+
const eyeY = center - size * 0.05;
|
|
80
|
+
|
|
81
|
+
// Eye whites
|
|
82
|
+
drawCircle(image, center - eyeOffset, eyeY, eyeRadius, WHITE);
|
|
83
|
+
drawCircle(image, center + eyeOffset, eyeY, eyeRadius, WHITE);
|
|
84
|
+
|
|
85
|
+
// Pupils (looking slightly up and to the side - friendly look)
|
|
86
|
+
const pupilRadius = eyeRadius * 0.5;
|
|
87
|
+
const pupilOffsetX = size * 0.02;
|
|
88
|
+
const pupilOffsetY = -size * 0.02;
|
|
89
|
+
drawCircle(image, center - eyeOffset + pupilOffsetX, eyeY + pupilOffsetY, pupilRadius, CHROME_BLUE);
|
|
90
|
+
drawCircle(image, center + eyeOffset + pupilOffsetX, eyeY + pupilOffsetY, pupilRadius, CHROME_BLUE);
|
|
91
|
+
|
|
92
|
+
// Eye shine (small white dot)
|
|
93
|
+
if (size >= 32) {
|
|
94
|
+
const shineRadius = Math.max(1, size * 0.025);
|
|
95
|
+
drawCircle(image, center - eyeOffset + pupilOffsetX - size * 0.02, eyeY + pupilOffsetY - size * 0.02, shineRadius, WHITE);
|
|
96
|
+
drawCircle(image, center + eyeOffset + pupilOffsetX - size * 0.02, eyeY + pupilOffsetY - size * 0.02, shineRadius, WHITE);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Draw big happy smile
|
|
100
|
+
const smileY = center + size * 0.12;
|
|
101
|
+
const smileWidth = size * 0.22;
|
|
102
|
+
const smileHeight = size * 0.08;
|
|
103
|
+
|
|
104
|
+
for (let x = center - smileWidth; x <= center + smileWidth; x++) {
|
|
105
|
+
// Curved smile (arc)
|
|
106
|
+
const normalizedX = (x - center) / smileWidth;
|
|
107
|
+
const curveY = smileHeight * (1 - normalizedX * normalizedX);
|
|
108
|
+
|
|
109
|
+
for (let dy = 0; dy <= curveY; dy++) {
|
|
110
|
+
const py = Math.round(smileY + dy);
|
|
111
|
+
if (py < size) {
|
|
112
|
+
image.setPixelColor(WHITE, Math.round(x), py);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Rosy cheeks (small pink circles for larger sizes)
|
|
118
|
+
if (size >= 48) {
|
|
119
|
+
const cheekRadius = size * 0.04;
|
|
120
|
+
const cheekY = center + size * 0.08;
|
|
121
|
+
const cheekColor = 0xFFAB91FF; // Soft coral/pink
|
|
122
|
+
|
|
123
|
+
drawCircle(image, center - eyeOffset - size * 0.1, cheekY, cheekRadius, cheekColor);
|
|
124
|
+
drawCircle(image, center + eyeOffset + size * 0.1, cheekY, cheekRadius, cheekColor);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return image;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function drawCircle(image, cx, cy, radius, color) {
|
|
131
|
+
const r2 = radius * radius;
|
|
132
|
+
for (let y = cy - radius - 1; y <= cy + radius + 1; y++) {
|
|
133
|
+
for (let x = cx - radius - 1; x <= cx + radius + 1; x++) {
|
|
134
|
+
const dx = x - cx;
|
|
135
|
+
const dy = y - cy;
|
|
136
|
+
if (dx * dx + dy * dy <= r2) {
|
|
137
|
+
if (x >= 0 && x < image.getWidth() && y >= 0 && y < image.getHeight()) {
|
|
138
|
+
image.setPixelColor(color, Math.round(x), Math.round(y));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async function main() {
|
|
146
|
+
console.log('Creating ChromeTools icons (happy robot)...');
|
|
147
|
+
|
|
148
|
+
// Create icons
|
|
149
|
+
const icon16 = await createIcon(16);
|
|
150
|
+
const icon48 = await createIcon(48);
|
|
151
|
+
const icon128 = await createIcon(128);
|
|
152
|
+
|
|
153
|
+
// Save icons
|
|
154
|
+
await icon16.writeAsync(path.join(__dirname, 'icon16.png'));
|
|
155
|
+
await icon48.writeAsync(path.join(__dirname, 'icon48.png'));
|
|
156
|
+
await icon128.writeAsync(path.join(__dirname, 'icon128.png'));
|
|
157
|
+
|
|
158
|
+
console.log('✅ Icons created successfully!');
|
|
159
|
+
console.log(' - icon16.png (happy robot)');
|
|
160
|
+
console.log(' - icon48.png (happy robot)');
|
|
161
|
+
console.log(' - icon128.png (happy robot)');
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
main().catch(console.error);
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"manifest_version": 3,
|
|
3
|
+
"name": "ChromeTools MCP",
|
|
4
|
+
"version": "3.1.2",
|
|
5
|
+
"description": "Tab tracking and scenario recording for chrometools-mcp",
|
|
6
|
+
|
|
7
|
+
"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxLLqg7Nu1h9ogRVgQoVMRPv8Jp7uRJugZSGUh++Niq0xm3khJefBuJ3L0dSG6xb9tkjTdgqUyg81VUgJBDVw9Bxu6iz1uL17VnEGHDZKe5wpsEpG8o6ZsTWtKRDeoxmkCGSOSDsh/ihlJe8mFaqpBYz6RBaO28R89TNobVhSobTQPB1ptyEND7W7JnsnMOiMcTo9l6j9HrIHLoHj7tO42DHNI4tEyLxI7C6R3i5dLIdwwxJMj0Hhrx4Ncmh24AzPyZypxVvpa1V7HP3sAXGBoUjLd/SEaY8j50lnaIQI3AkYv86pS9l6EZ6y3XCuW7C7W9guTTL/7ZNawYoE2bJ1HwIDAQAB",
|
|
8
|
+
|
|
9
|
+
"permissions": [
|
|
10
|
+
"tabs",
|
|
11
|
+
"activeTab",
|
|
12
|
+
"scripting",
|
|
13
|
+
"storage",
|
|
14
|
+
"webNavigation",
|
|
15
|
+
"nativeMessaging"
|
|
16
|
+
],
|
|
17
|
+
|
|
18
|
+
"host_permissions": [
|
|
19
|
+
"<all_urls>"
|
|
20
|
+
],
|
|
21
|
+
|
|
22
|
+
"background": {
|
|
23
|
+
"service_worker": "background.js",
|
|
24
|
+
"type": "module"
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
"content_scripts": [
|
|
28
|
+
{
|
|
29
|
+
"matches": ["<all_urls>"],
|
|
30
|
+
"js": ["content.js"],
|
|
31
|
+
"css": ["recorder-overlay.css"],
|
|
32
|
+
"run_at": "document_start",
|
|
33
|
+
"all_frames": false
|
|
34
|
+
}
|
|
35
|
+
],
|
|
36
|
+
|
|
37
|
+
"action": {
|
|
38
|
+
"default_popup": "popup/popup.html",
|
|
39
|
+
"default_icon": {
|
|
40
|
+
"16": "icons/icon16.png",
|
|
41
|
+
"48": "icons/icon48.png",
|
|
42
|
+
"128": "icons/icon128.png"
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
"icons": {
|
|
47
|
+
"16": "icons/icon16.png",
|
|
48
|
+
"48": "icons/icon48.png",
|
|
49
|
+
"128": "icons/icon128.png"
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
"web_accessible_resources": [
|
|
53
|
+
{
|
|
54
|
+
"resources": ["recorder-overlay.css"],
|
|
55
|
+
"matches": ["<all_urls>"]
|
|
56
|
+
}
|
|
57
|
+
]
|
|
58
|
+
}
|
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
/* ChromeTools MCP Extension - Popup Styles */
|
|
2
|
+
|
|
3
|
+
* {
|
|
4
|
+
margin: 0;
|
|
5
|
+
padding: 0;
|
|
6
|
+
box-sizing: border-box;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
body {
|
|
10
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
11
|
+
font-size: 13px;
|
|
12
|
+
color: #1f2937;
|
|
13
|
+
background: #f9fafb;
|
|
14
|
+
min-width: 320px;
|
|
15
|
+
max-width: 320px;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.popup-container {
|
|
19
|
+
display: flex;
|
|
20
|
+
flex-direction: column;
|
|
21
|
+
min-height: 400px;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/* Header */
|
|
25
|
+
.header {
|
|
26
|
+
display: flex;
|
|
27
|
+
justify-content: space-between;
|
|
28
|
+
align-items: center;
|
|
29
|
+
padding: 12px 16px;
|
|
30
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
31
|
+
color: white;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.logo {
|
|
35
|
+
display: flex;
|
|
36
|
+
align-items: center;
|
|
37
|
+
gap: 8px;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.logo-icon {
|
|
41
|
+
width: 28px;
|
|
42
|
+
height: 28px;
|
|
43
|
+
background: rgba(255, 255, 255, 0.2);
|
|
44
|
+
border-radius: 6px;
|
|
45
|
+
display: flex;
|
|
46
|
+
align-items: center;
|
|
47
|
+
justify-content: center;
|
|
48
|
+
font-weight: 700;
|
|
49
|
+
font-size: 11px;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.logo-text {
|
|
53
|
+
font-weight: 600;
|
|
54
|
+
font-size: 14px;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.connection-status {
|
|
58
|
+
display: flex;
|
|
59
|
+
align-items: center;
|
|
60
|
+
gap: 6px;
|
|
61
|
+
font-size: 11px;
|
|
62
|
+
opacity: 0.9;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.status-dot {
|
|
66
|
+
width: 8px;
|
|
67
|
+
height: 8px;
|
|
68
|
+
border-radius: 50%;
|
|
69
|
+
background: #ef4444;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.connection-status.connected .status-dot {
|
|
73
|
+
background: #10b981;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* Tabs Navigation */
|
|
77
|
+
.tabs {
|
|
78
|
+
display: flex;
|
|
79
|
+
background: white;
|
|
80
|
+
border-bottom: 1px solid #e5e7eb;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.tab {
|
|
84
|
+
flex: 1;
|
|
85
|
+
padding: 10px 16px;
|
|
86
|
+
border: none;
|
|
87
|
+
background: none;
|
|
88
|
+
font-size: 13px;
|
|
89
|
+
font-weight: 500;
|
|
90
|
+
color: #6b7280;
|
|
91
|
+
cursor: pointer;
|
|
92
|
+
transition: all 0.2s;
|
|
93
|
+
border-bottom: 2px solid transparent;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.tab:hover {
|
|
97
|
+
color: #374151;
|
|
98
|
+
background: #f3f4f6;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.tab.active {
|
|
102
|
+
color: #667eea;
|
|
103
|
+
border-bottom-color: #667eea;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/* Tab Content */
|
|
107
|
+
.tab-content {
|
|
108
|
+
display: none;
|
|
109
|
+
padding: 16px;
|
|
110
|
+
flex: 1;
|
|
111
|
+
background: white;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.tab-content.active {
|
|
115
|
+
display: block;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/* Recording Status */
|
|
119
|
+
.recording-status {
|
|
120
|
+
display: flex;
|
|
121
|
+
align-items: center;
|
|
122
|
+
justify-content: center;
|
|
123
|
+
gap: 8px;
|
|
124
|
+
padding: 12px;
|
|
125
|
+
background: #f3f4f6;
|
|
126
|
+
border-radius: 8px;
|
|
127
|
+
margin-bottom: 16px;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.recording-dot {
|
|
131
|
+
width: 10px;
|
|
132
|
+
height: 10px;
|
|
133
|
+
border-radius: 50%;
|
|
134
|
+
background: #9ca3af;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.recording-status.recording .recording-dot {
|
|
138
|
+
background: #ef4444;
|
|
139
|
+
animation: pulse 1.5s ease-in-out infinite;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.recording-status.paused .recording-dot {
|
|
143
|
+
background: #f59e0b;
|
|
144
|
+
animation: none;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
@keyframes pulse {
|
|
148
|
+
0%, 100% { opacity: 1; transform: scale(1); }
|
|
149
|
+
50% { opacity: 0.5; transform: scale(1.1); }
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.recording-text {
|
|
153
|
+
font-weight: 500;
|
|
154
|
+
color: #374151;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/* Metadata Form */
|
|
158
|
+
.metadata-form {
|
|
159
|
+
margin-bottom: 16px;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.form-group {
|
|
163
|
+
margin-bottom: 12px;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.form-group label {
|
|
167
|
+
display: block;
|
|
168
|
+
font-size: 11px;
|
|
169
|
+
font-weight: 500;
|
|
170
|
+
color: #6b7280;
|
|
171
|
+
margin-bottom: 4px;
|
|
172
|
+
text-transform: uppercase;
|
|
173
|
+
letter-spacing: 0.5px;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.form-group input {
|
|
177
|
+
width: 100%;
|
|
178
|
+
padding: 10px 12px;
|
|
179
|
+
border: 1px solid #e5e7eb;
|
|
180
|
+
border-radius: 6px;
|
|
181
|
+
font-size: 13px;
|
|
182
|
+
transition: border-color 0.2s, box-shadow 0.2s;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.form-group input:focus {
|
|
186
|
+
outline: none;
|
|
187
|
+
border-color: #667eea;
|
|
188
|
+
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.form-group input::placeholder {
|
|
192
|
+
color: #9ca3af;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/* Action Count Display */
|
|
196
|
+
.action-count-display {
|
|
197
|
+
text-align: center;
|
|
198
|
+
padding: 24px;
|
|
199
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
200
|
+
border-radius: 8px;
|
|
201
|
+
color: white;
|
|
202
|
+
margin-bottom: 16px;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.count-number {
|
|
206
|
+
display: block;
|
|
207
|
+
font-size: 48px;
|
|
208
|
+
font-weight: 700;
|
|
209
|
+
line-height: 1;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.count-label {
|
|
213
|
+
font-size: 12px;
|
|
214
|
+
opacity: 0.8;
|
|
215
|
+
text-transform: uppercase;
|
|
216
|
+
letter-spacing: 1px;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/* Buttons */
|
|
220
|
+
.controls {
|
|
221
|
+
display: flex;
|
|
222
|
+
flex-direction: column;
|
|
223
|
+
gap: 8px;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.btn {
|
|
227
|
+
display: flex;
|
|
228
|
+
align-items: center;
|
|
229
|
+
justify-content: center;
|
|
230
|
+
gap: 8px;
|
|
231
|
+
padding: 12px 16px;
|
|
232
|
+
border: none;
|
|
233
|
+
border-radius: 8px;
|
|
234
|
+
font-size: 13px;
|
|
235
|
+
font-weight: 600;
|
|
236
|
+
cursor: pointer;
|
|
237
|
+
transition: all 0.2s;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
.btn:hover {
|
|
241
|
+
transform: translateY(-1px);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
.btn:active {
|
|
245
|
+
transform: translateY(0);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.btn:disabled {
|
|
249
|
+
opacity: 0.5;
|
|
250
|
+
cursor: not-allowed;
|
|
251
|
+
transform: none;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
.btn-icon {
|
|
255
|
+
font-size: 10px;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.btn-primary {
|
|
259
|
+
background: linear-gradient(135deg, #10b981 0%, #059669 100%);
|
|
260
|
+
color: white;
|
|
261
|
+
box-shadow: 0 2px 8px rgba(16, 185, 129, 0.3);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
.btn-primary:hover {
|
|
265
|
+
box-shadow: 0 4px 12px rgba(16, 185, 129, 0.4);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
.btn-secondary {
|
|
269
|
+
background: #f3f4f6;
|
|
270
|
+
color: #374151;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
.btn-secondary:hover {
|
|
274
|
+
background: #e5e7eb;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.btn-danger {
|
|
278
|
+
background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
|
|
279
|
+
color: white;
|
|
280
|
+
box-shadow: 0 2px 8px rgba(239, 68, 68, 0.3);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.btn-danger:hover {
|
|
284
|
+
box-shadow: 0 4px 12px rgba(239, 68, 68, 0.4);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
.btn-ghost {
|
|
288
|
+
background: transparent;
|
|
289
|
+
color: #6b7280;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
.btn-ghost:hover {
|
|
293
|
+
background: #f3f4f6;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
.btn-reset {
|
|
297
|
+
background: #fef2f2;
|
|
298
|
+
color: #dc2626;
|
|
299
|
+
border: 1px solid #fecaca;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.btn-reset:hover {
|
|
303
|
+
background: #fee2e2;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/* Actions Preview */
|
|
307
|
+
.actions-preview {
|
|
308
|
+
margin-top: 16px;
|
|
309
|
+
border-top: 1px solid #e5e7eb;
|
|
310
|
+
padding-top: 16px;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.actions-preview h4 {
|
|
314
|
+
font-size: 11px;
|
|
315
|
+
font-weight: 600;
|
|
316
|
+
color: #6b7280;
|
|
317
|
+
text-transform: uppercase;
|
|
318
|
+
letter-spacing: 0.5px;
|
|
319
|
+
margin-bottom: 8px;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
.actions-list {
|
|
323
|
+
list-style: none;
|
|
324
|
+
max-height: 150px;
|
|
325
|
+
overflow-y: auto;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
.actions-list li {
|
|
329
|
+
padding: 8px;
|
|
330
|
+
background: #f9fafb;
|
|
331
|
+
border-radius: 4px;
|
|
332
|
+
margin-bottom: 4px;
|
|
333
|
+
font-size: 11px;
|
|
334
|
+
display: flex;
|
|
335
|
+
justify-content: space-between;
|
|
336
|
+
align-items: center;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
.action-type {
|
|
340
|
+
font-weight: 600;
|
|
341
|
+
color: #667eea;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
.action-details {
|
|
345
|
+
color: #6b7280;
|
|
346
|
+
font-size: 10px;
|
|
347
|
+
max-width: 150px;
|
|
348
|
+
overflow: hidden;
|
|
349
|
+
text-overflow: ellipsis;
|
|
350
|
+
white-space: nowrap;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/* Tabs List */
|
|
354
|
+
.tabs-list {
|
|
355
|
+
max-height: 300px;
|
|
356
|
+
overflow-y: auto;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
.tabs-loading {
|
|
360
|
+
text-align: center;
|
|
361
|
+
padding: 32px;
|
|
362
|
+
color: #9ca3af;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
.tab-item {
|
|
366
|
+
display: flex;
|
|
367
|
+
align-items: center;
|
|
368
|
+
gap: 12px;
|
|
369
|
+
padding: 10px;
|
|
370
|
+
border-radius: 6px;
|
|
371
|
+
cursor: pointer;
|
|
372
|
+
transition: background 0.2s;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
.tab-item:hover {
|
|
376
|
+
background: #f3f4f6;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
.tab-item.active {
|
|
380
|
+
background: rgba(102, 126, 234, 0.1);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
.tab-favicon {
|
|
384
|
+
width: 16px;
|
|
385
|
+
height: 16px;
|
|
386
|
+
border-radius: 2px;
|
|
387
|
+
background: #e5e7eb;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
.tab-info {
|
|
391
|
+
flex: 1;
|
|
392
|
+
min-width: 0;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
.tab-title {
|
|
396
|
+
font-weight: 500;
|
|
397
|
+
color: #1f2937;
|
|
398
|
+
white-space: nowrap;
|
|
399
|
+
overflow: hidden;
|
|
400
|
+
text-overflow: ellipsis;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
.tab-url {
|
|
404
|
+
font-size: 11px;
|
|
405
|
+
color: #9ca3af;
|
|
406
|
+
white-space: nowrap;
|
|
407
|
+
overflow: hidden;
|
|
408
|
+
text-overflow: ellipsis;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
.tab-active-badge {
|
|
412
|
+
font-size: 9px;
|
|
413
|
+
padding: 2px 6px;
|
|
414
|
+
background: #10b981;
|
|
415
|
+
color: white;
|
|
416
|
+
border-radius: 10px;
|
|
417
|
+
text-transform: uppercase;
|
|
418
|
+
font-weight: 600;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/* Footer */
|
|
422
|
+
.footer {
|
|
423
|
+
padding: 8px 16px;
|
|
424
|
+
background: #f9fafb;
|
|
425
|
+
border-top: 1px solid #e5e7eb;
|
|
426
|
+
text-align: center;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
.version {
|
|
430
|
+
font-size: 10px;
|
|
431
|
+
color: #9ca3af;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/* Utility */
|
|
435
|
+
.hidden {
|
|
436
|
+
display: none !important;
|
|
437
|
+
}
|