Skip to content

Commit 3594757

Browse files
committed
feat(extension): add options page for managing allowed sites
1 parent deba6ab commit 3594757

21 files changed

Lines changed: 628 additions & 120 deletions

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
"adm-zip": "^0.5.16",
3131
"copy-webpack-plugin": "^13.0.0",
3232
"css-loader": "^7.1.2",
33-
"eslint": "^9.24.0",
33+
"eslint": "^9.25.1",
3434
"eslint-plugin-prettier": "^5.2.6",
3535
"file-loader": "^6.2.0",
3636
"mini-css-extract-plugin": "^2.9.2",
@@ -42,7 +42,7 @@
4242
"ts-loader": "^9.5.2",
4343
"typescript": "^5.8.3",
4444
"typescript-eslint": "^8.29.1",
45-
"webpack": "^5.99.5",
45+
"webpack": "^5.99.6",
4646
"webpack-cli": "^6.0.1",
4747
"webpack-merge": "^6.0.1"
4848
},

packages/chrome-extension/config/webpack.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const config = (env, argv) => {
1212
contentScript: PATHS.src + '/contentScript.ts',
1313
background: PATHS.src + '/background.ts',
1414
injectedScript: PATHS.src + '/injectedScript.ts',
15+
options: PATHS.src + '/options.ts',
1516
},
1617
devtool: isProduction ? false : 'source-map',
1718
optimization: {

packages/chrome-extension/src/background.ts

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { checkAllowedStatus } from '@vue-devtools-unlocker/shared';
2+
13
// Store Vue DevTools unlock status for each tab
24
const tabStatus: Record<number, unknown> = {};
35

@@ -14,18 +16,41 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
1416
// Listen for popup open event
1517
chrome.runtime.onConnect.addListener((port) => {
1618
if (port.name === 'popup') {
19+
port.postMessage({
20+
type: 'VueDevtoolsStatus',
21+
payload: {
22+
loading: true,
23+
},
24+
});
1725
// When popup connects, query the current active tab
1826
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
1927
if (tabs[0] && tabs[0].id) {
2028
const tabId = tabs[0].id;
21-
// Send tab status to popup
22-
port.postMessage({
23-
type: 'VueDevtoolsStatus',
24-
payload: tabStatus[tabId] || {
25-
success: false,
26-
message: 'No Vue application detected or DevTools not unlocked on this page',
27-
},
28-
});
29+
let retryCount = 0;
30+
const MAX_RETRIES = 5; // (5 * 500ms = 2500ms)
31+
32+
const checkStatus = () => {
33+
if (tabStatus[tabId]) {
34+
port.postMessage({
35+
type: 'VueDevtoolsStatus',
36+
payload: tabStatus[tabId],
37+
});
38+
} else if (retryCount < MAX_RETRIES) {
39+
retryCount++;
40+
setTimeout(checkStatus, 500);
41+
} else {
42+
// 超时后发送错误状态
43+
port.postMessage({
44+
type: 'VueDevtoolsStatus',
45+
payload: {
46+
success: false,
47+
message: 'Unable to detect Vue application status. Please refresh the page and try again.',
48+
},
49+
});
50+
}
51+
};
52+
53+
checkStatus();
2954
}
3055
});
3156
}
@@ -34,3 +59,10 @@ chrome.runtime.onConnect.addListener((port) => {
3459
chrome.tabs.onRemoved.addListener((tabId) => {
3560
delete tabStatus[tabId];
3661
});
62+
63+
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
64+
delete tabStatus[tabId];
65+
if (changeInfo.status === 'complete' && tab.url) {
66+
checkAllowedStatus(tabId, tab.url);
67+
}
68+
});
Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,24 @@
11
import { injectScriptFile } from '@vue-devtools-unlocker/shared';
22

3-
injectScriptFile('injectedScript.js');
3+
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
4+
if (message.type === 'CheckIsAllowed' && message.isAllowed) {
5+
injectScriptFile('injectedScript.js');
6+
window.addEventListener('message', (event) => {
7+
if (event.data?.source === 'vue-devtools-unlocker') {
8+
chrome.runtime.sendMessage(event.data);
9+
}
10+
});
411

5-
window.addEventListener('message', (event) => {
6-
if (event.data?.source === 'vue-devtools-unlocker') {
7-
console.log('post message', event.data);
8-
chrome.runtime.sendMessage(event.data);
12+
sendResponse({ success: true });
13+
} else {
14+
chrome.runtime.sendMessage({
15+
source: 'vue-devtools-unlocker',
16+
type: 'VueDevtoolsStatus',
17+
payload: {
18+
success: false,
19+
isNotAllowed: true,
20+
message: 'This page is not allowed to use Vue DevTools.',
21+
},
22+
});
923
}
1024
});
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
*,
2+
*::before,
3+
*::after {
4+
margin: 0;
5+
padding: 0;
6+
box-sizing: border-box;
7+
}
8+
9+
:root {
10+
--primary-color: #42b883;
11+
--secondary-color: #35495e;
12+
--background-color: #f8f8f8;
13+
--text-color: #2c3e50;
14+
--border-color: #e0e0e0;
15+
--success-color: #42b883;
16+
--error-color: #ff5252;
17+
}
18+
19+
html {
20+
font-family:
21+
-apple-system,
22+
BlinkMacSystemFont,
23+
Segoe UI,
24+
Helvetica,
25+
Arial,
26+
sans-serif;
27+
}
28+
29+
body {
30+
width: 400px;
31+
color: var(--text-color);
32+
}
33+
34+
.container {
35+
background: #fff;
36+
padding: 20px;
37+
border-radius: 8px;
38+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
39+
}
40+
41+
h1 {
42+
color: var(--secondary-color);
43+
margin-bottom: 20px;
44+
}
45+
46+
main {
47+
margin-bottom: 20px;
48+
49+
label {
50+
display: block;
51+
margin-bottom: 10px;
52+
color: var(--text-color);
53+
}
54+
55+
textarea {
56+
width: 100%;
57+
height: 150px;
58+
padding: 10px;
59+
border: 1px solid var(--border-color);
60+
border-radius: 4px;
61+
font-family: monospace;
62+
resize: none;
63+
}
64+
65+
.help-text {
66+
font-size: 12px;
67+
color: #666;
68+
margin-top: 5px;
69+
}
70+
}
71+
72+
button {
73+
background: var(--primary-color);
74+
color: #fff;
75+
border: none;
76+
padding: 8px 16px;
77+
border-radius: 4px;
78+
cursor: pointer;
79+
80+
&:hover {
81+
background: #3aa876;
82+
}
83+
}
84+
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { getAllowedSites, setAllowedSites } from '@vue-devtools-unlocker/shared';
2+
import '@/options.scss';
3+
4+
document.addEventListener('DOMContentLoaded', () => {
5+
const textarea = document.getElementById('allowedSites') as HTMLTextAreaElement;
6+
const saveButton = document.getElementById('save') as HTMLButtonElement;
7+
8+
// Load saved settings
9+
getAllowedSites((text) => {
10+
textarea.value = text;
11+
});
12+
13+
// Save settings
14+
saveButton.addEventListener('click', () => {
15+
const sites = textarea.value
16+
.split('\n')
17+
.map((site) => site.trim())
18+
.filter((site) => site.length > 0);
19+
20+
setAllowedSites(sites, () => {
21+
saveButton.textContent = 'Saved!';
22+
setTimeout(() => {
23+
saveButton.textContent = 'Save Settings';
24+
}, 2000);
25+
});
26+
});
27+
});

packages/chrome-extension/src/popup.scss

Lines changed: 40 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,10 @@ body {
2525
color: var(--text-color);
2626
}
2727

28-
.app {
28+
.container {
2929
display: flex;
3030
flex-direction: column;
3131
background-color: var(--background-color);
32-
min-height: 200px;
3332
overflow: hidden;
3433
}
3534

@@ -65,14 +64,26 @@ main {
6564
background-color: white;
6665
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
6766
border: 1px solid var(--border-color);
67+
68+
h3 {
69+
margin-bottom: 8px;
70+
}
6871
}
6972

7073
.status-success {
7174
border-left: 4px solid var(--success-color);
75+
76+
h3 {
77+
color: var(--success-color);
78+
}
7279
}
7380

7481
.status-error {
7582
border-left: 4px solid var(--error-color);
83+
84+
h3 {
85+
color: var(--error-color);
86+
}
7687
}
7788

7889
.loading {
@@ -81,14 +92,29 @@ main {
8192
gap: 10px;
8293
}
8394

84-
.version-tag {
85-
display: inline-block;
86-
padding: 2px 6px;
87-
background-color: var(--primary-color);
88-
color: white;
89-
border-radius: 4px;
95+
.version-info {
96+
margin-top: 8px;
97+
98+
.version-tag {
99+
display: inline-block;
100+
padding: 2px 6px;
101+
background-color: var(--primary-color);
102+
color: white;
103+
border-radius: 4px;
104+
font-size: 12px;
105+
margin-left: 6px;
106+
}
107+
}
108+
109+
.guide-text {
110+
margin-top: 8px;
90111
font-size: 12px;
91-
margin-left: 6px;
112+
color: #666;
113+
}
114+
115+
.guide-steps {
116+
margin-top: 4px;
117+
padding-left: 20px;
92118
}
93119

94120
.spinner {
@@ -112,16 +138,19 @@ footer {
112138
text-align: center;
113139
font-size: 12px;
114140
border-top: 1px solid var(--border-color);
141+
display: flex;
142+
justify-content: center;
143+
gap: 16px;
115144

116-
.github-link {
145+
.github-link, .settings-link {
117146
color: var(--secondary-color);
118147
text-decoration: none;
119148
display: inline-flex;
120149
align-items: center;
121150
gap: 4px;
122151
}
123152

124-
.github-link:hover {
153+
.github-link:hover, .settings-link:hover {
125154
text-decoration: underline;
126155
}
127156
}

0 commit comments

Comments
 (0)