@symbo.ls/connect 3.2.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/build.js +205 -0
- package/dist/assets/1024x1024.png +0 -0
- package/dist/assets/128x128.png +0 -0
- package/dist/assets/144x144.png +0 -0
- package/dist/assets/192x192.png +0 -0
- package/dist/assets/48x48.png +0 -0
- package/dist/assets/512x512.png +0 -0
- package/dist/assets/72x72.png +0 -0
- package/dist/assets/96x96.png +0 -0
- package/dist/assets/active_cursor.png +0 -0
- package/dist/assets/favicon.svg +6 -0
- package/dist/assets/old/144x144.png +0 -0
- package/dist/assets/old/192x192.png +0 -0
- package/dist/assets/old/48x48.png +0 -0
- package/dist/assets/old/48x48_faint.png +0 -0
- package/dist/assets/old/512x512.png +0 -0
- package/dist/assets/old/72x72.png +0 -0
- package/dist/assets/old/96x96.png +0 -0
- package/dist/auth.js +373 -0
- package/dist/content.css +46 -0
- package/dist/content.js +1171 -0
- package/dist/content.js.map +7 -0
- package/dist/devtools.html +7 -0
- package/dist/devtools.js +5 -0
- package/dist/manifest.json +87 -0
- package/dist/page-agent.js +727 -0
- package/dist/panel.css +2239 -0
- package/dist/panel.html +235 -0
- package/dist/panel.js +4973 -0
- package/dist/picker.html +111 -0
- package/dist/picker.js +300 -0
- package/dist/service_worker.js +219 -0
- package/dist/service_worker.js.map +7 -0
- package/dist/settings.css +128 -0
- package/dist/settings.html +26 -0
- package/dist/settings_ui.js +57 -0
- package/dist/settings_ui.js.map +7 -0
- package/package.json +20 -0
- package/src/content.js +104 -0
- package/src/grabber/clean.js +605 -0
- package/src/grabber/computed.js +78 -0
- package/src/grabber/parse.js +268 -0
- package/src/grabber/stylesheets.js +117 -0
- package/src/grabber/utils.js +238 -0
- package/src/service_worker.js +246 -0
- package/src/settings/settings_ui.js +52 -0
- package/src/settings/settings_utils.js +70 -0
- package/static/assets/1024x1024.png +0 -0
- package/static/assets/128x128.png +0 -0
- package/static/assets/144x144.png +0 -0
- package/static/assets/192x192.png +0 -0
- package/static/assets/48x48.png +0 -0
- package/static/assets/512x512.png +0 -0
- package/static/assets/72x72.png +0 -0
- package/static/assets/96x96.png +0 -0
- package/static/assets/active_cursor.png +0 -0
- package/static/assets/favicon.svg +6 -0
- package/static/assets/old/144x144.png +0 -0
- package/static/assets/old/192x192.png +0 -0
- package/static/assets/old/48x48.png +0 -0
- package/static/assets/old/48x48_faint.png +0 -0
- package/static/assets/old/512x512.png +0 -0
- package/static/assets/old/72x72.png +0 -0
- package/static/assets/old/96x96.png +0 -0
- package/static/auth.js +373 -0
- package/static/content.css +46 -0
- package/static/devtools.html +7 -0
- package/static/devtools.js +5 -0
- package/static/manifest.json +56 -0
- package/static/page-agent.js +727 -0
- package/static/panel.css +2239 -0
- package/static/panel.html +235 -0
- package/static/panel.js +4973 -0
- package/static/picker.html +111 -0
- package/static/picker.js +300 -0
- package/static/settings.css +128 -0
- package/static/settings.html +26 -0
package/dist/picker.html
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<title>Symbols Connect — Select Folder</title>
|
|
6
|
+
<link rel="icon" href="assets/favicon.svg" />
|
|
7
|
+
<style>
|
|
8
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
9
|
+
:root {
|
|
10
|
+
--bg: #141416;
|
|
11
|
+
--bg-alt: #242428;
|
|
12
|
+
--bg-hover: #34343a;
|
|
13
|
+
--border: #34343a;
|
|
14
|
+
--text: #bcbcc2;
|
|
15
|
+
--text-dim: #65656f;
|
|
16
|
+
--text-bright: #e0e0e2;
|
|
17
|
+
--accent: #0085FF;
|
|
18
|
+
}
|
|
19
|
+
body {
|
|
20
|
+
background: var(--bg);
|
|
21
|
+
color: var(--text);
|
|
22
|
+
font-family: 'DmSansVariable', 'Helvetica Neue', 'Helvetica', system-ui, sans-serif;
|
|
23
|
+
display: flex;
|
|
24
|
+
align-items: center;
|
|
25
|
+
justify-content: center;
|
|
26
|
+
height: 100vh;
|
|
27
|
+
}
|
|
28
|
+
.picker-inner {
|
|
29
|
+
text-align: center;
|
|
30
|
+
max-width: 400px;
|
|
31
|
+
padding: 40px;
|
|
32
|
+
}
|
|
33
|
+
.picker-logo {
|
|
34
|
+
width: 48px;
|
|
35
|
+
height: 48px;
|
|
36
|
+
margin: 0 auto 16px;
|
|
37
|
+
display: flex;
|
|
38
|
+
align-items: center;
|
|
39
|
+
justify-content: center;
|
|
40
|
+
}
|
|
41
|
+
.picker-logo svg { width: 48px; height: 48px; fill: var(--accent); }
|
|
42
|
+
h1 {
|
|
43
|
+
font-size: 18px;
|
|
44
|
+
font-weight: 600;
|
|
45
|
+
color: var(--text-bright);
|
|
46
|
+
margin-bottom: 8px;
|
|
47
|
+
}
|
|
48
|
+
p {
|
|
49
|
+
font-size: 13px;
|
|
50
|
+
color: var(--text-dim);
|
|
51
|
+
margin-bottom: 24px;
|
|
52
|
+
line-height: 1.4;
|
|
53
|
+
}
|
|
54
|
+
button {
|
|
55
|
+
background: var(--accent);
|
|
56
|
+
color: #fff;
|
|
57
|
+
border: none;
|
|
58
|
+
padding: 12px 32px;
|
|
59
|
+
border-radius: 8px;
|
|
60
|
+
font-size: 14px;
|
|
61
|
+
font-family: inherit;
|
|
62
|
+
cursor: pointer;
|
|
63
|
+
}
|
|
64
|
+
button:hover { opacity: 0.9; }
|
|
65
|
+
.picker-btn-secondary {
|
|
66
|
+
background: transparent;
|
|
67
|
+
color: var(--text-dim);
|
|
68
|
+
border: 1px solid var(--border);
|
|
69
|
+
padding: 10px 24px;
|
|
70
|
+
border-radius: 8px;
|
|
71
|
+
font-size: 13px;
|
|
72
|
+
font-family: inherit;
|
|
73
|
+
cursor: pointer;
|
|
74
|
+
}
|
|
75
|
+
.picker-btn-secondary:hover {
|
|
76
|
+
color: var(--text-bright);
|
|
77
|
+
border-color: var(--accent);
|
|
78
|
+
}
|
|
79
|
+
.picker-success-icon {
|
|
80
|
+
width: 64px;
|
|
81
|
+
height: 64px;
|
|
82
|
+
margin: 0 auto 16px;
|
|
83
|
+
background: #59AC56;
|
|
84
|
+
color: #fff;
|
|
85
|
+
font-size: 36px;
|
|
86
|
+
line-height: 64px;
|
|
87
|
+
text-align: center;
|
|
88
|
+
border-radius: 50%;
|
|
89
|
+
}
|
|
90
|
+
.status {
|
|
91
|
+
margin-top: 16px;
|
|
92
|
+
font-size: 12px;
|
|
93
|
+
color: var(--text-dim);
|
|
94
|
+
}
|
|
95
|
+
.status.error { color: #EB6650; }
|
|
96
|
+
.status.success { color: #59AC56; }
|
|
97
|
+
</style>
|
|
98
|
+
</head>
|
|
99
|
+
<body>
|
|
100
|
+
<div class="picker-inner">
|
|
101
|
+
<div class="picker-logo">
|
|
102
|
+
<svg viewBox="0 0 24 24"><path d="M13.843 2.7C19.063 2.7 23 6.366 23 11.228c0 3.754-2.862 6.584-6.658 6.584-3.287 0-5.007-2.318-5.007-4.609 0-2.395 1.923-4.344 4.287-4.344.566 0 1.023.12 1.309.223a.212.212 0 01.137.229l-.016.058-.514 1.18a.223.223 0 01-.245.13 2.965 2.965 0 00-.506-.046c-1.245 0-2.258 1.027-2.258 2.288 0 1.33 1.165 2.373 2.651 2.373 2.195 0 3.913-1.777 3.913-4.046 0-3.024-2.294-5.135-5.58-5.135-4.076 0-7.393 3.36-7.393 7.491a7.519 7.519 0 002.871 5.924l-4.96 3.18A12.042 12.042 0 012 14.7c0-6.617 5.313-12 11.843-12z" fill-rule="evenodd"/></svg>
|
|
103
|
+
</div>
|
|
104
|
+
<h1>Select Project Folder</h1>
|
|
105
|
+
<p>Choose a local folder to connect. The extension will be able to read and write files in the selected directory.</p>
|
|
106
|
+
<button id="btn-pick">Choose Folder</button>
|
|
107
|
+
<div id="status" class="status"></div>
|
|
108
|
+
</div>
|
|
109
|
+
<script src="picker.js"></script>
|
|
110
|
+
</body>
|
|
111
|
+
</html>
|
package/dist/picker.js
ADDED
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
const DB_NAME = 'symbols-connect'
|
|
2
|
+
const DB_STORE = 'handles'
|
|
3
|
+
const FILES_STORE = 'files'
|
|
4
|
+
|
|
5
|
+
function openDB () {
|
|
6
|
+
return new Promise((resolve, reject) => {
|
|
7
|
+
const req = indexedDB.open(DB_NAME, 3)
|
|
8
|
+
req.onupgradeneeded = (e) => {
|
|
9
|
+
const db = e.target.result
|
|
10
|
+
if (!db.objectStoreNames.contains(DB_STORE)) db.createObjectStore(DB_STORE)
|
|
11
|
+
if (!db.objectStoreNames.contains(FILES_STORE)) db.createObjectStore(FILES_STORE)
|
|
12
|
+
if (!db.objectStoreNames.contains('threads')) {
|
|
13
|
+
const store = db.createObjectStore('threads', { keyPath: 'id' })
|
|
14
|
+
store.createIndex('project', 'project', { unique: false })
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
req.onsuccess = () => resolve(req.result)
|
|
18
|
+
req.onerror = () => reject(req.error)
|
|
19
|
+
})
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async function saveHandle (name, handle) {
|
|
23
|
+
const db = await openDB()
|
|
24
|
+
const tx = db.transaction(DB_STORE, 'readwrite')
|
|
25
|
+
tx.objectStore(DB_STORE).put({ handle, name, time: Date.now() }, name)
|
|
26
|
+
return new Promise((resolve, reject) => {
|
|
27
|
+
tx.oncomplete = resolve
|
|
28
|
+
tx.onerror = () => reject(tx.error)
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function saveFileCache (projectName, cache) {
|
|
33
|
+
const db = await openDB()
|
|
34
|
+
const tx = db.transaction(FILES_STORE, 'readwrite')
|
|
35
|
+
tx.objectStore(FILES_STORE).put(cache, projectName)
|
|
36
|
+
return new Promise((resolve, reject) => {
|
|
37
|
+
tx.oncomplete = resolve
|
|
38
|
+
tx.onerror = () => reject(tx.error)
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Scan a directory and read all file contents
|
|
43
|
+
async function scanAndCache (dirHandle, basePath, depth) {
|
|
44
|
+
if (depth > 6) return { tree: [], files: {} }
|
|
45
|
+
const tree = []
|
|
46
|
+
const files = {}
|
|
47
|
+
|
|
48
|
+
for await (const entry of dirHandle.values()) {
|
|
49
|
+
const entryPath = basePath ? basePath + '/' + entry.name : entry.name
|
|
50
|
+
if (entry.name.startsWith('.') || entry.name === 'node_modules' || entry.name === 'dist') continue
|
|
51
|
+
|
|
52
|
+
if (entry.kind === 'directory') {
|
|
53
|
+
const sub = await scanAndCache(entry, entryPath, depth + 1)
|
|
54
|
+
tree.push({ name: entry.name, path: entryPath, kind: 'dir', children: sub.tree })
|
|
55
|
+
Object.assign(files, sub.files)
|
|
56
|
+
} else {
|
|
57
|
+
if (/\.(js|jsx|ts|tsx|json|css|html)$/i.test(entry.name)) {
|
|
58
|
+
tree.push({ name: entry.name, path: entryPath, kind: 'file' })
|
|
59
|
+
try {
|
|
60
|
+
const file = await entry.getFile()
|
|
61
|
+
files[entryPath] = await file.text()
|
|
62
|
+
} catch (e) {
|
|
63
|
+
files[entryPath] = null
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
tree.sort((a, b) => {
|
|
70
|
+
if (a.kind !== b.kind) return a.kind === 'dir' ? -1 : 1
|
|
71
|
+
return a.name.localeCompare(b.name)
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
return { tree, files }
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const statusEl = document.getElementById('status')
|
|
78
|
+
const innerEl = document.querySelector('.picker-inner')
|
|
79
|
+
|
|
80
|
+
function showSuccess (name) {
|
|
81
|
+
innerEl.innerHTML = ''
|
|
82
|
+
|
|
83
|
+
const checkmark = document.createElement('div')
|
|
84
|
+
checkmark.className = 'picker-success-icon'
|
|
85
|
+
checkmark.textContent = '\u2713'
|
|
86
|
+
|
|
87
|
+
const title = document.createElement('h1')
|
|
88
|
+
title.textContent = 'Connected'
|
|
89
|
+
title.style.marginBottom = '8px'
|
|
90
|
+
|
|
91
|
+
const nameEl = document.createElement('p')
|
|
92
|
+
nameEl.style.cssText = 'color:#59AC56;font-size:16px;font-weight:500;margin-bottom:8px'
|
|
93
|
+
nameEl.textContent = name
|
|
94
|
+
|
|
95
|
+
const hint = document.createElement('p')
|
|
96
|
+
hint.textContent = 'You can close this tab and return to DevTools.'
|
|
97
|
+
hint.style.marginBottom = '24px'
|
|
98
|
+
|
|
99
|
+
const anotherBtn = document.createElement('button')
|
|
100
|
+
anotherBtn.textContent = 'Choose Another Folder'
|
|
101
|
+
anotherBtn.className = 'picker-btn-secondary'
|
|
102
|
+
anotherBtn.addEventListener('click', () => location.reload())
|
|
103
|
+
|
|
104
|
+
innerEl.appendChild(checkmark)
|
|
105
|
+
innerEl.appendChild(title)
|
|
106
|
+
innerEl.appendChild(nameEl)
|
|
107
|
+
innerEl.appendChild(hint)
|
|
108
|
+
innerEl.appendChild(anotherBtn)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async function pickFolder () {
|
|
112
|
+
try {
|
|
113
|
+
const handle = await window.showDirectoryPicker({ mode: 'readwrite' })
|
|
114
|
+
|
|
115
|
+
// Validate symbols.json
|
|
116
|
+
let configHandle
|
|
117
|
+
try {
|
|
118
|
+
configHandle = await handle.getFileHandle('symbols.json')
|
|
119
|
+
} catch (e) {
|
|
120
|
+
statusEl.textContent = 'No symbols.json found. Please select a Symbols project folder.'
|
|
121
|
+
statusEl.className = 'status error'
|
|
122
|
+
return
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
statusEl.textContent = 'Reading symbols.json...'
|
|
126
|
+
statusEl.className = 'status'
|
|
127
|
+
|
|
128
|
+
const configFile = await configHandle.getFile()
|
|
129
|
+
const configText = await configFile.text()
|
|
130
|
+
let config
|
|
131
|
+
try {
|
|
132
|
+
config = JSON.parse(configText)
|
|
133
|
+
} catch (e) {
|
|
134
|
+
statusEl.textContent = 'Invalid symbols.json: ' + e.message
|
|
135
|
+
statusEl.className = 'status error'
|
|
136
|
+
return
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Resolve symbols directory
|
|
140
|
+
let symbolsDir = (config.dir || './symbols').replace(/^\.\//, '')
|
|
141
|
+
let symbolsDirHandle
|
|
142
|
+
try {
|
|
143
|
+
symbolsDirHandle = await handle.getDirectoryHandle(symbolsDir)
|
|
144
|
+
} catch (e) {
|
|
145
|
+
statusEl.textContent = 'Symbols directory "' + symbolsDir + '" not found.'
|
|
146
|
+
statusEl.className = 'status error'
|
|
147
|
+
return
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
statusEl.textContent = 'Scanning symbols folder...'
|
|
151
|
+
|
|
152
|
+
// Scan and cache all file contents
|
|
153
|
+
const { tree, files } = await scanAndCache(symbolsDirHandle, '', 0)
|
|
154
|
+
|
|
155
|
+
statusEl.textContent = 'Saving...'
|
|
156
|
+
|
|
157
|
+
// Save handle and file cache
|
|
158
|
+
await saveHandle(handle.name, handle)
|
|
159
|
+
await saveFileCache(handle.name, {
|
|
160
|
+
config,
|
|
161
|
+
symbolsDir,
|
|
162
|
+
tree,
|
|
163
|
+
files,
|
|
164
|
+
time: Date.now()
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
showSuccess(handle.name)
|
|
168
|
+
|
|
169
|
+
// Notify the devtools panel
|
|
170
|
+
chrome.runtime.sendMessage({
|
|
171
|
+
type: 'folder-picked',
|
|
172
|
+
name: handle.name
|
|
173
|
+
})
|
|
174
|
+
} catch (e) {
|
|
175
|
+
if (e.name !== 'AbortError') {
|
|
176
|
+
statusEl.textContent = 'Error: ' + e.message
|
|
177
|
+
statusEl.className = 'status error'
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Listen for file operation requests from the panel
|
|
183
|
+
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
|
|
184
|
+
if (msg.type === 'read-file') {
|
|
185
|
+
(async () => {
|
|
186
|
+
try {
|
|
187
|
+
const db = await openDB()
|
|
188
|
+
const tx = db.transaction(FILES_STORE, 'readonly')
|
|
189
|
+
const req = tx.objectStore(FILES_STORE).get(msg.project)
|
|
190
|
+
req.onsuccess = () => {
|
|
191
|
+
const cache = req.result
|
|
192
|
+
if (cache && cache.files && msg.path in cache.files) {
|
|
193
|
+
sendResponse({ content: cache.files[msg.path] })
|
|
194
|
+
} else {
|
|
195
|
+
sendResponse({ error: 'File not in cache' })
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
req.onerror = () => sendResponse({ error: 'DB error' })
|
|
199
|
+
} catch (e) {
|
|
200
|
+
sendResponse({ error: e.message })
|
|
201
|
+
}
|
|
202
|
+
})()
|
|
203
|
+
return true
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (msg.type === 'write-file') {
|
|
207
|
+
(async () => {
|
|
208
|
+
try {
|
|
209
|
+
const db = await openDB()
|
|
210
|
+
const tx = db.transaction(DB_STORE, 'readonly')
|
|
211
|
+
const req = tx.objectStore(DB_STORE).get(msg.project)
|
|
212
|
+
req.onsuccess = async () => {
|
|
213
|
+
try {
|
|
214
|
+
const item = req.result
|
|
215
|
+
if (!item || !item.handle) {
|
|
216
|
+
sendResponse({ error: 'Project handle not found' })
|
|
217
|
+
return
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
let symbolsDir = msg.symbolsDir || 'symbols'
|
|
221
|
+
let dirHandle = await item.handle.getDirectoryHandle(symbolsDir)
|
|
222
|
+
|
|
223
|
+
// Navigate to file
|
|
224
|
+
const parts = msg.path.split('/')
|
|
225
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
226
|
+
dirHandle = await dirHandle.getDirectoryHandle(parts[i])
|
|
227
|
+
}
|
|
228
|
+
const fileHandle = await dirHandle.getFileHandle(parts[parts.length - 1])
|
|
229
|
+
const writable = await fileHandle.createWritable()
|
|
230
|
+
await writable.write(msg.content)
|
|
231
|
+
await writable.close()
|
|
232
|
+
|
|
233
|
+
// Update cache
|
|
234
|
+
const db2 = await openDB()
|
|
235
|
+
const tx2 = db2.transaction(FILES_STORE, 'readwrite')
|
|
236
|
+
const req2 = tx2.objectStore(FILES_STORE).get(msg.project)
|
|
237
|
+
req2.onsuccess = () => {
|
|
238
|
+
const cache = req2.result
|
|
239
|
+
if (cache) {
|
|
240
|
+
cache.files[msg.path] = msg.content
|
|
241
|
+
tx2.objectStore(FILES_STORE).put(cache, msg.project)
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
sendResponse({ success: true })
|
|
246
|
+
} catch (e) {
|
|
247
|
+
sendResponse({ error: e.message })
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
req.onerror = () => sendResponse({ error: 'DB error' })
|
|
251
|
+
} catch (e) {
|
|
252
|
+
sendResponse({ error: e.message })
|
|
253
|
+
}
|
|
254
|
+
})()
|
|
255
|
+
return true
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (msg.type === 'rescan-project') {
|
|
259
|
+
(async () => {
|
|
260
|
+
try {
|
|
261
|
+
const db = await openDB()
|
|
262
|
+
const tx = db.transaction(DB_STORE, 'readonly')
|
|
263
|
+
const req = tx.objectStore(DB_STORE).get(msg.project)
|
|
264
|
+
req.onsuccess = async () => {
|
|
265
|
+
try {
|
|
266
|
+
const item = req.result
|
|
267
|
+
if (!item || !item.handle) {
|
|
268
|
+
sendResponse({ error: 'No handle' })
|
|
269
|
+
return
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const perm = await item.handle.queryPermission({ mode: 'readwrite' })
|
|
273
|
+
if (perm !== 'granted') {
|
|
274
|
+
sendResponse({ error: 'Permission denied. Re-open folder.' })
|
|
275
|
+
return
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
let configHandle = await item.handle.getFileHandle('symbols.json')
|
|
279
|
+
const configFile = await configHandle.getFile()
|
|
280
|
+
const config = JSON.parse(await configFile.text())
|
|
281
|
+
|
|
282
|
+
let symbolsDir = (config.dir || './symbols').replace(/^\.\//, '')
|
|
283
|
+
let symbolsDirHandle = await item.handle.getDirectoryHandle(symbolsDir)
|
|
284
|
+
const { tree, files } = await scanAndCache(symbolsDirHandle, '', 0)
|
|
285
|
+
|
|
286
|
+
await saveFileCache(msg.project, { config, symbolsDir, tree, files, time: Date.now() })
|
|
287
|
+
sendResponse({ success: true, config, symbolsDir, tree })
|
|
288
|
+
} catch (e) {
|
|
289
|
+
sendResponse({ error: e.message })
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
} catch (e) {
|
|
293
|
+
sendResponse({ error: e.message })
|
|
294
|
+
}
|
|
295
|
+
})()
|
|
296
|
+
return true
|
|
297
|
+
}
|
|
298
|
+
})
|
|
299
|
+
|
|
300
|
+
document.getElementById('btn-pick').addEventListener('click', pickFolder)
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
// src/settings/settings_utils.js
|
|
2
|
+
var storageKey = "settings";
|
|
3
|
+
var settingsDefinitions = [
|
|
4
|
+
{
|
|
5
|
+
key: "useStylesheets",
|
|
6
|
+
name: "Use Stylesheets",
|
|
7
|
+
type: "checkbox",
|
|
8
|
+
default: true
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
key: "useComputed",
|
|
12
|
+
name: "Use Computed",
|
|
13
|
+
type: "checkbox",
|
|
14
|
+
default: false
|
|
15
|
+
}
|
|
16
|
+
];
|
|
17
|
+
var getSettings = async () => (await chrome.storage.local.get(storageKey)).settings;
|
|
18
|
+
async function updateSettings(updates) {
|
|
19
|
+
const cur = await getSettings();
|
|
20
|
+
chrome.storage.local.set({ [storageKey]: { ...cur, ...updates } }).catch((reason) => console.error(`failed to update settings : ${reason}`));
|
|
21
|
+
}
|
|
22
|
+
async function initSettings() {
|
|
23
|
+
const curSettings = await getSettings() ?? {};
|
|
24
|
+
const defaultSettings = {};
|
|
25
|
+
settingsDefinitions.forEach(({ key, default: defValue }) => {
|
|
26
|
+
defaultSettings[key] = defValue;
|
|
27
|
+
});
|
|
28
|
+
Object.keys(curSettings).forEach((key) => {
|
|
29
|
+
if (key in defaultSettings) {
|
|
30
|
+
defaultSettings[key] = curSettings[key];
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
updateSettings(defaultSettings);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// src/service_worker.js
|
|
37
|
+
var getTabState = (tabId) => chrome.storage.session.get(String(tabId)).then((data) => parseInt(data[tabId] || 0, 10));
|
|
38
|
+
var setTabState = (tabId, state) => chrome.storage.session.set({ [tabId]: state });
|
|
39
|
+
var getActiveTabs = () => chrome.tabs.query({ active: true, currentWindow: true });
|
|
40
|
+
var messageActiveTabs = async (message, { condition, modifyMsg } = {}) => {
|
|
41
|
+
let tabs = await getActiveTabs();
|
|
42
|
+
tabs = tabs.filter(
|
|
43
|
+
(tab) => /^https?:/u.test(tab.url) && (!condition || condition(tab))
|
|
44
|
+
);
|
|
45
|
+
return Promise.all(
|
|
46
|
+
tabs.map((tab, index) => {
|
|
47
|
+
const msg = modifyMsg ? modifyMsg(tab, message, index) : message;
|
|
48
|
+
return chrome.tabs.sendMessage(tab.id, msg).then((response) => response).catch((reason) => {
|
|
49
|
+
console.log(`Failed to send msg to tab ${tab.id}`, { reason });
|
|
50
|
+
});
|
|
51
|
+
})
|
|
52
|
+
);
|
|
53
|
+
};
|
|
54
|
+
var toggleActiveTabsState = async () => {
|
|
55
|
+
const tabs = await getActiveTabs();
|
|
56
|
+
let states = await Promise.all(tabs.map((tab) => getTabState(tab.id)));
|
|
57
|
+
states = states.map((s) => s ? 0 : 1);
|
|
58
|
+
return messageActiveTabs(
|
|
59
|
+
{ type: "toggle" },
|
|
60
|
+
{
|
|
61
|
+
modifyMsg: (tab, msg, index) => {
|
|
62
|
+
const state = states[index];
|
|
63
|
+
setTabState(tab.id, state);
|
|
64
|
+
return { ...msg, state };
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
);
|
|
68
|
+
};
|
|
69
|
+
chrome.runtime.onInstalled.addListener(({ previousVersion, reason }) => {
|
|
70
|
+
const { name } = chrome.runtime.getManifest();
|
|
71
|
+
chrome.action.setBadgeBackgroundColor({ color: "#099058ff" });
|
|
72
|
+
console.log(`${name} ${reason}`, { id: chrome.runtime.id, previousVersion });
|
|
73
|
+
if (["install", "update"].includes(reason)) {
|
|
74
|
+
initSettings();
|
|
75
|
+
chrome.declarativeNetRequest.updateDynamicRules({
|
|
76
|
+
removeRuleIds: [1, 2, 3],
|
|
77
|
+
addRules: [
|
|
78
|
+
{
|
|
79
|
+
id: 1,
|
|
80
|
+
priority: 1,
|
|
81
|
+
action: {
|
|
82
|
+
type: "modifyHeaders",
|
|
83
|
+
requestHeaders: [
|
|
84
|
+
{ header: "Origin", operation: "set", value: "https://symbols.app" }
|
|
85
|
+
]
|
|
86
|
+
},
|
|
87
|
+
condition: {
|
|
88
|
+
urlFilter: "https://api.symbols.app/*",
|
|
89
|
+
resourceTypes: ["xmlhttprequest", "other"]
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
id: 2,
|
|
94
|
+
priority: 1,
|
|
95
|
+
action: {
|
|
96
|
+
type: "modifyHeaders",
|
|
97
|
+
requestHeaders: [
|
|
98
|
+
{ header: "Origin", operation: "remove" }
|
|
99
|
+
]
|
|
100
|
+
},
|
|
101
|
+
condition: {
|
|
102
|
+
urlFilter: "https://api.anthropic.com/*",
|
|
103
|
+
resourceTypes: ["xmlhttprequest", "other"]
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
]
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
chrome.storage.onChanged.addListener((changes, areas) => {
|
|
111
|
+
if (areas === "session" && Object.values(changes).some((change) => parseInt(change.newValue, 10))) {
|
|
112
|
+
chrome.action.setBadgeText({ text: "ON" });
|
|
113
|
+
} else {
|
|
114
|
+
chrome.action.setBadgeText({ text: null });
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
chrome.tabs.onActivated.addListener(({ tabId }) => {
|
|
118
|
+
chrome.action.setBadgeText({ text: null });
|
|
119
|
+
setTabState(tabId, 0);
|
|
120
|
+
});
|
|
121
|
+
chrome.commands.onCommand.addListener(async (command) => {
|
|
122
|
+
if (command === "toggleGrabber") {
|
|
123
|
+
await toggleActiveTabsState();
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
chrome.action.onClicked.addListener(async () => {
|
|
127
|
+
await toggleActiveTabsState();
|
|
128
|
+
});
|
|
129
|
+
var pickerTabId = null;
|
|
130
|
+
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
|
|
131
|
+
if (msg.type === "open-picker") {
|
|
132
|
+
const pickerUrl = chrome.runtime.getURL("picker.html");
|
|
133
|
+
chrome.tabs.create({ url: pickerUrl, active: true }, (tab) => {
|
|
134
|
+
pickerTabId = tab.id;
|
|
135
|
+
});
|
|
136
|
+
sendResponse({ ok: true });
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
if (msg.type === "read-file" || msg.type === "write-file" || msg.type === "rescan-project") {
|
|
140
|
+
const forwardToPickerTab = (tabId) => {
|
|
141
|
+
chrome.tabs.sendMessage(tabId, msg, (response) => {
|
|
142
|
+
if (chrome.runtime.lastError) {
|
|
143
|
+
sendResponse({ error: "Picker tab error: " + chrome.runtime.lastError.message });
|
|
144
|
+
} else {
|
|
145
|
+
sendResponse(response);
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
};
|
|
149
|
+
const openNewPickerTab = () => {
|
|
150
|
+
const pickerUrl = chrome.runtime.getURL("picker.html?bg");
|
|
151
|
+
chrome.tabs.create({ url: pickerUrl, active: false }, (tab) => {
|
|
152
|
+
pickerTabId = tab.id;
|
|
153
|
+
setTimeout(() => forwardToPickerTab(tab.id), 800);
|
|
154
|
+
});
|
|
155
|
+
};
|
|
156
|
+
if (pickerTabId) {
|
|
157
|
+
chrome.tabs.get(pickerTabId, (tab) => {
|
|
158
|
+
if (chrome.runtime.lastError || !tab) {
|
|
159
|
+
pickerTabId = null;
|
|
160
|
+
openNewPickerTab();
|
|
161
|
+
} else {
|
|
162
|
+
forwardToPickerTab(pickerTabId);
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
} else {
|
|
166
|
+
openNewPickerTab();
|
|
167
|
+
}
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
if (msg.type === "api-fetch") {
|
|
171
|
+
const headers = new Headers(msg.headers || {});
|
|
172
|
+
headers.delete("Origin");
|
|
173
|
+
const fetchOpts = {
|
|
174
|
+
method: msg.method || "GET",
|
|
175
|
+
headers
|
|
176
|
+
};
|
|
177
|
+
if (msg.body && msg.method && msg.method !== "GET") {
|
|
178
|
+
fetchOpts.body = msg.body;
|
|
179
|
+
}
|
|
180
|
+
fetch(msg.url, fetchOpts).then((res) => res.text().then((text) => ({ ok: res.ok, status: res.status, text }))).then(({ ok, status, text }) => {
|
|
181
|
+
let json = null;
|
|
182
|
+
try {
|
|
183
|
+
json = JSON.parse(text);
|
|
184
|
+
} catch (e) {
|
|
185
|
+
}
|
|
186
|
+
sendResponse({ ok, status, data: json, text });
|
|
187
|
+
}).catch((e) => {
|
|
188
|
+
sendResponse({ ok: false, status: 0, error: e.message || String(e) });
|
|
189
|
+
});
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
if (msg.type === "folder-picked") {
|
|
193
|
+
}
|
|
194
|
+
return true;
|
|
195
|
+
});
|
|
196
|
+
chrome.runtime.onMessageExternal.addListener(async (msg, sender, respond) => {
|
|
197
|
+
switch (msg.type) {
|
|
198
|
+
case "toggle_platform_mode": {
|
|
199
|
+
const responses = await toggleActiveTabsState();
|
|
200
|
+
if (responses.some((res) => res && res.success)) {
|
|
201
|
+
const domql = responses.find((res) => res && res.domql)?.domql || null;
|
|
202
|
+
setTabState(sender.tab.id, 0);
|
|
203
|
+
respond({ success: true, domql });
|
|
204
|
+
}
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
case "request_domql": {
|
|
208
|
+
const data = await chrome.storage.local.get("domqlStr");
|
|
209
|
+
const domql = JSON.parse(data.domqlStr);
|
|
210
|
+
respond(domql);
|
|
211
|
+
break;
|
|
212
|
+
}
|
|
213
|
+
default: {
|
|
214
|
+
console.error("Invalid message received", { msg, sender });
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return true;
|
|
218
|
+
});
|
|
219
|
+
//# sourceMappingURL=service_worker.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/settings/settings_utils.js", "../src/service_worker.js"],
|
|
4
|
+
"sourcesContent": ["/**\n * @typedef {Object} Settings\n * @property {boolean} useComputed\n * @property {boolean} useStylesheets\n */\n\nconst storageKey = 'settings'\n\n/**\n * @typedef {Object} SettingDefinition\n * @property {string} type\n * @property {String} name\n * @property {string} [key]\n * @property {any} [default]\n * @property {value} [any]\n */\n\n/**\n * @type {SettingDefinition[]}\n */\nexport const settingsDefinitions = [\n {\n key: 'useStylesheets',\n name: 'Use Stylesheets',\n type: 'checkbox',\n default: true\n },\n {\n key: 'useComputed',\n name: 'Use Computed',\n type: 'checkbox',\n default: false\n }\n]\n\n/**\n * @returns {Promise<Settings>}\n */\nexport const getSettings = async () =>\n (await chrome.storage.local.get(storageKey)).settings\n\n/**\n * @param {Partial<Settings>} updates\n */\nexport async function updateSettings(updates) {\n const cur = await getSettings()\n chrome.storage.local\n .set({ [storageKey]: { ...cur, ...updates } })\n .catch((reason) => console.error(`failed to update settings : ${reason}`))\n}\n\nexport async function initSettings() {\n const curSettings = (await getSettings()) ?? {}\n\n // construct default settings from definitions\n const defaultSettings = {}\n settingsDefinitions.forEach(({ key, default: defValue }) => {\n defaultSettings[key] = defValue\n })\n\n // override defaults with any existing settings\n Object.keys(curSettings).forEach((key) => {\n // only include settings within the current defined set\n if (key in defaultSettings) {\n defaultSettings[key] = curSettings[key]\n }\n })\n\n updateSettings(defaultSettings)\n}\n", "import { initSettings } from './settings/settings_utils'\n\n// ============================================================\n// Tab state helpers (for grabber toggle)\n// ============================================================\nconst getTabState = (tabId) =>\n chrome.storage.session\n .get(String(tabId))\n .then((data) => parseInt(data[tabId] || 0, 10))\n\nconst setTabState = (tabId, state) =>\n chrome.storage.session.set({ [tabId]: state })\n\nconst getActiveTabs = () =>\n chrome.tabs.query({ active: true, currentWindow: true })\n\nconst messageActiveTabs = async (message, { condition, modifyMsg } = {}) => {\n let tabs = await getActiveTabs()\n tabs = tabs.filter(\n (tab) => /^https?:/u.test(tab.url) && (!condition || condition(tab))\n )\n\n return Promise.all(\n tabs.map((tab, index) => {\n const msg = modifyMsg ? modifyMsg(tab, message, index) : message\n return chrome.tabs\n .sendMessage(tab.id, msg)\n .then((response) => response)\n .catch((reason) => {\n console.log(`Failed to send msg to tab ${tab.id}`, { reason })\n })\n })\n )\n}\n\nconst toggleActiveTabsState = async () => {\n const tabs = await getActiveTabs()\n let states = await Promise.all(tabs.map((tab) => getTabState(tab.id)))\n states = states.map((s) => (s ? 0 : 1))\n\n return messageActiveTabs(\n { type: 'toggle' },\n {\n modifyMsg: (tab, msg, index) => {\n const state = states[index]\n setTabState(tab.id, state)\n return { ...msg, state }\n }\n }\n )\n}\n\n// ============================================================\n// Install / update\n// ============================================================\nchrome.runtime.onInstalled.addListener(({ previousVersion, reason }) => {\n const { name } = chrome.runtime.getManifest()\n chrome.action.setBadgeBackgroundColor({ color: '#099058ff' })\n console.log(`${name} ${reason}`, { id: chrome.runtime.id, previousVersion })\n\n if (['install', 'update'].includes(reason)) {\n initSettings()\n\n // Override Origin header on API requests to avoid CORS rejection\n chrome.declarativeNetRequest.updateDynamicRules({\n removeRuleIds: [1, 2, 3],\n addRules: [\n {\n id: 1,\n priority: 1,\n action: {\n type: 'modifyHeaders',\n requestHeaders: [\n { header: 'Origin', operation: 'set', value: 'https://symbols.app' }\n ]\n },\n condition: {\n urlFilter: 'https://api.symbols.app/*',\n resourceTypes: ['xmlhttprequest', 'other']\n }\n },\n {\n id: 2,\n priority: 1,\n action: {\n type: 'modifyHeaders',\n requestHeaders: [\n { header: 'Origin', operation: 'remove' }\n ]\n },\n condition: {\n urlFilter: 'https://api.anthropic.com/*',\n resourceTypes: ['xmlhttprequest', 'other']\n }\n }\n ]\n })\n }\n})\n\n// ============================================================\n// Badge state\n// ============================================================\nchrome.storage.onChanged.addListener((changes, areas) => {\n if (\n areas === 'session' &&\n Object.values(changes).some((change) => parseInt(change.newValue, 10))\n ) {\n chrome.action.setBadgeText({ text: 'ON' })\n } else {\n chrome.action.setBadgeText({ text: null })\n }\n})\n\nchrome.tabs.onActivated.addListener(({ tabId }) => {\n chrome.action.setBadgeText({ text: null })\n setTabState(tabId, 0)\n})\n\n// ============================================================\n// Keyboard shortcut (Ctrl+E / Cmd+E)\n// ============================================================\nchrome.commands.onCommand.addListener(async (command) => {\n if (command === 'toggleGrabber') {\n await toggleActiveTabsState()\n }\n})\n\n// ============================================================\n// Toolbar icon click -> toggle grabber\n// ============================================================\nchrome.action.onClicked.addListener(async () => {\n await toggleActiveTabsState()\n})\n\n// ============================================================\n// Internal messaging (from devtools panel)\n// ============================================================\nlet pickerTabId = null\n\nchrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {\n if (msg.type === 'open-picker') {\n const pickerUrl = chrome.runtime.getURL('picker.html')\n chrome.tabs.create({ url: pickerUrl, active: true }, (tab) => {\n pickerTabId = tab.id\n })\n sendResponse({ ok: true })\n return true\n }\n\n // Forward file operations to the picker tab\n if (msg.type === 'read-file' || msg.type === 'write-file' || msg.type === 'rescan-project') {\n const forwardToPickerTab = (tabId) => {\n chrome.tabs.sendMessage(tabId, msg, (response) => {\n if (chrome.runtime.lastError) {\n sendResponse({ error: 'Picker tab error: ' + chrome.runtime.lastError.message })\n } else {\n sendResponse(response)\n }\n })\n }\n\n const openNewPickerTab = () => {\n const pickerUrl = chrome.runtime.getURL('picker.html?bg')\n chrome.tabs.create({ url: pickerUrl, active: false }, (tab) => {\n pickerTabId = tab.id\n // Wait for the page to load before forwarding\n setTimeout(() => forwardToPickerTab(tab.id), 800)\n })\n }\n\n if (pickerTabId) {\n // Verify the tab still exists\n chrome.tabs.get(pickerTabId, (tab) => {\n if (chrome.runtime.lastError || !tab) {\n pickerTabId = null\n openNewPickerTab()\n } else {\n forwardToPickerTab(pickerTabId)\n }\n })\n } else {\n openNewPickerTab()\n }\n return true\n }\n\n // Proxy API requests \u2014 strip Origin to avoid CORS rejection on server\n if (msg.type === 'api-fetch') {\n const headers = new Headers(msg.headers || {})\n headers.delete('Origin')\n const fetchOpts = {\n method: msg.method || 'GET',\n headers\n }\n if (msg.body && msg.method && msg.method !== 'GET') {\n fetchOpts.body = msg.body\n }\n fetch(msg.url, fetchOpts)\n .then((res) => res.text().then((text) => ({ ok: res.ok, status: res.status, text })))\n .then(({ ok, status, text }) => {\n let json = null\n try { json = JSON.parse(text) } catch (e) {}\n sendResponse({ ok, status, data: json, text })\n })\n .catch((e) => {\n sendResponse({ ok: false, status: 0, error: e.message || String(e) })\n })\n return true\n }\n\n // Forward folder-picked to all extension pages\n if (msg.type === 'folder-picked') {\n // Already handled by chrome.runtime.onMessage in panel\n }\n\n return true\n})\n\n// ============================================================\n// External messaging (from symbols.app / platform.symbo.ls)\n// ============================================================\nchrome.runtime.onMessageExternal.addListener(async (msg, sender, respond) => {\n switch (msg.type) {\n case 'toggle_platform_mode': {\n const responses = await toggleActiveTabsState()\n if (responses.some((res) => res && res.success)) {\n const domql = responses.find((res) => res && res.domql)?.domql || null\n setTabState(sender.tab.id, 0)\n respond({ success: true, domql })\n }\n break\n }\n case 'request_domql': {\n const data = await chrome.storage.local.get('domqlStr')\n const domql = JSON.parse(data.domqlStr)\n respond(domql)\n break\n }\n default: {\n console.error('Invalid message received', { msg, sender })\n }\n }\n\n return true\n})\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|