@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/static/auth.js
ADDED
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
// Auth module — manages login/logout and token storage via chrome.storage.local
|
|
2
|
+
;(function () {
|
|
3
|
+
'use strict'
|
|
4
|
+
|
|
5
|
+
const API_BASE = 'https://api.symbols.app'
|
|
6
|
+
const STORAGE_KEYS = {
|
|
7
|
+
accessToken: 'symbols_access_token',
|
|
8
|
+
refreshToken: 'symbols_refresh_token',
|
|
9
|
+
expiresAt: 'symbols_expires_at',
|
|
10
|
+
user: 'symbols_user'
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// ============================================================
|
|
14
|
+
// Token storage (chrome.storage.local)
|
|
15
|
+
// ============================================================
|
|
16
|
+
function getStoredAuth () {
|
|
17
|
+
return new Promise((resolve) => {
|
|
18
|
+
chrome.storage.local.get(Object.values(STORAGE_KEYS), (data) => {
|
|
19
|
+
resolve({
|
|
20
|
+
accessToken: data[STORAGE_KEYS.accessToken] || null,
|
|
21
|
+
refreshToken: data[STORAGE_KEYS.refreshToken] || null,
|
|
22
|
+
expiresAt: data[STORAGE_KEYS.expiresAt] || 0,
|
|
23
|
+
user: data[STORAGE_KEYS.user] || null
|
|
24
|
+
})
|
|
25
|
+
})
|
|
26
|
+
})
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function setStoredAuth (tokens, user) {
|
|
30
|
+
const data = {}
|
|
31
|
+
if (tokens) {
|
|
32
|
+
data[STORAGE_KEYS.accessToken] = tokens.accessToken
|
|
33
|
+
data[STORAGE_KEYS.refreshToken] = tokens.refreshToken
|
|
34
|
+
data[STORAGE_KEYS.expiresAt] = tokens.accessTokenExp
|
|
35
|
+
? (tokens.accessTokenExp.expiresAt || (Date.now() / 1000 + (tokens.accessTokenExp.expiresIn || 3600)))
|
|
36
|
+
: (Date.now() / 1000 + 3600)
|
|
37
|
+
}
|
|
38
|
+
if (user) {
|
|
39
|
+
data[STORAGE_KEYS.user] = user
|
|
40
|
+
}
|
|
41
|
+
return new Promise((resolve) => chrome.storage.local.set(data, resolve))
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function clearStoredAuth () {
|
|
45
|
+
return new Promise((resolve) => {
|
|
46
|
+
chrome.storage.local.remove(Object.values(STORAGE_KEYS), resolve)
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ============================================================
|
|
51
|
+
// API calls (routed through service worker to bypass CORS)
|
|
52
|
+
// ============================================================
|
|
53
|
+
async function apiRequest (path, options) {
|
|
54
|
+
var url = API_BASE + path
|
|
55
|
+
var headers = { 'Content-Type': 'application/json' }
|
|
56
|
+
if (options && options.headers) {
|
|
57
|
+
Object.assign(headers, options.headers)
|
|
58
|
+
}
|
|
59
|
+
var resp = await swFetch(url, {
|
|
60
|
+
method: (options && options.method) || 'GET',
|
|
61
|
+
headers: headers,
|
|
62
|
+
body: (options && options.body) || undefined
|
|
63
|
+
})
|
|
64
|
+
if (!resp.ok) {
|
|
65
|
+
throw new Error((resp.data && resp.data.message) || resp.text || resp.error || ('HTTP ' + resp.status))
|
|
66
|
+
}
|
|
67
|
+
return resp.data
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function authedRequest (path, options) {
|
|
71
|
+
const auth = await getStoredAuth()
|
|
72
|
+
let token = auth.accessToken
|
|
73
|
+
|
|
74
|
+
// Refresh if expired (30s buffer)
|
|
75
|
+
if (token && auth.expiresAt && (Date.now() / 1000) > auth.expiresAt - 30) {
|
|
76
|
+
try {
|
|
77
|
+
const refreshed = await refreshTokens(auth.refreshToken)
|
|
78
|
+
token = refreshed.accessToken
|
|
79
|
+
} catch (e) {
|
|
80
|
+
// Refresh failed — clear auth
|
|
81
|
+
await clearStoredAuth()
|
|
82
|
+
throw new Error('Session expired — please sign in again')
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (!token) throw new Error('Not signed in')
|
|
87
|
+
|
|
88
|
+
return apiRequest(path, {
|
|
89
|
+
...options,
|
|
90
|
+
headers: {
|
|
91
|
+
...(options && options.headers),
|
|
92
|
+
Authorization: 'Bearer ' + token
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// ============================================================
|
|
98
|
+
// Auth operations
|
|
99
|
+
// ============================================================
|
|
100
|
+
async function login (email, password) {
|
|
101
|
+
const data = await apiRequest('/core/auth/login', {
|
|
102
|
+
method: 'POST',
|
|
103
|
+
body: JSON.stringify({ email, password })
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
const tokens = data.tokens || data
|
|
107
|
+
const user = data.user || null
|
|
108
|
+
await setStoredAuth(tokens, user)
|
|
109
|
+
return { tokens, user }
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function refreshTokens (refreshToken) {
|
|
113
|
+
const data = await apiRequest('/core/auth/refresh', {
|
|
114
|
+
method: 'POST',
|
|
115
|
+
body: JSON.stringify({ refreshToken })
|
|
116
|
+
})
|
|
117
|
+
const tokens = data.tokens || data
|
|
118
|
+
const auth = await getStoredAuth()
|
|
119
|
+
await setStoredAuth(tokens, auth.user)
|
|
120
|
+
return tokens
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async function getMe () {
|
|
124
|
+
return authedRequest('/core/auth/me', { method: 'GET' })
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async function logout () {
|
|
128
|
+
try {
|
|
129
|
+
const auth = await getStoredAuth()
|
|
130
|
+
if (auth.accessToken) {
|
|
131
|
+
await apiRequest('/core/auth/logout', {
|
|
132
|
+
method: 'POST',
|
|
133
|
+
headers: { Authorization: 'Bearer ' + auth.accessToken }
|
|
134
|
+
}).catch(() => {})
|
|
135
|
+
}
|
|
136
|
+
} finally {
|
|
137
|
+
await clearStoredAuth()
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async function isSignedIn () {
|
|
142
|
+
const auth = await getStoredAuth()
|
|
143
|
+
if (!auth.accessToken) return false
|
|
144
|
+
try {
|
|
145
|
+
await getMe()
|
|
146
|
+
return true
|
|
147
|
+
} catch (e) {
|
|
148
|
+
return false
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// ============================================================
|
|
153
|
+
// Platform API — projects
|
|
154
|
+
// ============================================================
|
|
155
|
+
async function listProjects () {
|
|
156
|
+
return authedRequest('/core/projects', { method: 'GET' })
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async function getProjectByKey (key) {
|
|
160
|
+
return authedRequest('/core/projects/key/' + encodeURIComponent(key), { method: 'GET' })
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async function getProjectData (projectId, branch) {
|
|
164
|
+
return authedRequest(
|
|
165
|
+
'/core/projects/' + projectId + '/data?branch=' + (branch || 'main') + '&version=latest',
|
|
166
|
+
{ method: 'GET' }
|
|
167
|
+
)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async function getServiceToken () {
|
|
171
|
+
try {
|
|
172
|
+
const res = await fetch(API_BASE + '/service-token')
|
|
173
|
+
const json = await res.json().catch(() => null)
|
|
174
|
+
if (json && json.token) return json.token.trim()
|
|
175
|
+
const txt = await res.text()
|
|
176
|
+
return (txt || '').replace(/\s+/g, '') || null
|
|
177
|
+
} catch (e) {
|
|
178
|
+
return null
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
async function getAccessToken () {
|
|
183
|
+
const auth = await getStoredAuth()
|
|
184
|
+
return auth.accessToken
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// ============================================================
|
|
188
|
+
// Browser sign-in (PKCE session flow — same as CLI)
|
|
189
|
+
// ============================================================
|
|
190
|
+
const WEBSITE_BASE = 'https://symbols.app'
|
|
191
|
+
|
|
192
|
+
function randomVerifier (len) {
|
|
193
|
+
len = len || 64
|
|
194
|
+
var chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~'
|
|
195
|
+
var arr = new Uint8Array(len)
|
|
196
|
+
crypto.getRandomValues(arr)
|
|
197
|
+
var out = ''
|
|
198
|
+
for (var i = 0; i < len; i++) out += chars.charAt(arr[i] % chars.length)
|
|
199
|
+
return out
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async function sha256Base64url (input) {
|
|
203
|
+
var encoder = new TextEncoder()
|
|
204
|
+
var data = encoder.encode(input)
|
|
205
|
+
var hash = await crypto.subtle.digest('SHA-256', data)
|
|
206
|
+
var bytes = new Uint8Array(hash)
|
|
207
|
+
var binary = ''
|
|
208
|
+
for (var i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i])
|
|
209
|
+
return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, '')
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Proxy fetch through service worker to avoid CORS preflight issues
|
|
213
|
+
function swFetch (url, options) {
|
|
214
|
+
return new Promise(function (resolve, reject) {
|
|
215
|
+
try {
|
|
216
|
+
chrome.runtime.sendMessage({
|
|
217
|
+
type: 'api-fetch',
|
|
218
|
+
url: url,
|
|
219
|
+
method: (options && options.method) || 'GET',
|
|
220
|
+
headers: (options && options.headers) || {},
|
|
221
|
+
body: (options && options.body) || undefined
|
|
222
|
+
}, function (resp) {
|
|
223
|
+
if (chrome.runtime.lastError) {
|
|
224
|
+
reject(new Error(chrome.runtime.lastError.message))
|
|
225
|
+
return
|
|
226
|
+
}
|
|
227
|
+
if (!resp) {
|
|
228
|
+
reject(new Error('No response from service worker'))
|
|
229
|
+
return
|
|
230
|
+
}
|
|
231
|
+
resolve(resp)
|
|
232
|
+
})
|
|
233
|
+
} catch (e) {
|
|
234
|
+
reject(new Error('sendMessage failed: ' + (e.message || e)))
|
|
235
|
+
}
|
|
236
|
+
})
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Try swFetch — no fallback to direct fetch (CORS blocks it)
|
|
240
|
+
async function proxyFetch (url, options) {
|
|
241
|
+
return swFetch(url, options)
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
async function loginViaBrowser (onStatus) {
|
|
245
|
+
var sessionId = crypto.randomUUID()
|
|
246
|
+
var codeVerifier = randomVerifier(64)
|
|
247
|
+
var codeChallenge = await sha256Base64url(codeVerifier)
|
|
248
|
+
|
|
249
|
+
if (onStatus) onStatus('Creating secure session...')
|
|
250
|
+
|
|
251
|
+
// Create PKCE session via service worker (avoids CORS)
|
|
252
|
+
var sessionResp
|
|
253
|
+
try {
|
|
254
|
+
sessionResp = await proxyFetch(API_BASE + '/core/auth/session', {
|
|
255
|
+
method: 'POST',
|
|
256
|
+
headers: { 'Content-Type': 'application/json' },
|
|
257
|
+
body: JSON.stringify({
|
|
258
|
+
session_id: sessionId,
|
|
259
|
+
code_challenge: codeChallenge,
|
|
260
|
+
plugin_info: { version: 'chrome-extension', figma_env: 'chrome' }
|
|
261
|
+
})
|
|
262
|
+
})
|
|
263
|
+
} catch (fetchErr) {
|
|
264
|
+
chrome.tabs.create({ url: WEBSITE_BASE + '/signin' })
|
|
265
|
+
throw new Error('Session fetch error: ' + fetchErr.message)
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (!sessionResp.ok) {
|
|
269
|
+
chrome.tabs.create({ url: WEBSITE_BASE + '/signin' })
|
|
270
|
+
throw new Error('Session failed (HTTP ' + sessionResp.status + '): ' + (sessionResp.text || '').substring(0, 120))
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Open signin with session param
|
|
274
|
+
var signinUrl = WEBSITE_BASE + '/signin?session=' + encodeURIComponent(sessionId)
|
|
275
|
+
chrome.tabs.create({ url: signinUrl })
|
|
276
|
+
|
|
277
|
+
if (onStatus) onStatus('Waiting for sign-in in browser...')
|
|
278
|
+
|
|
279
|
+
var startedAt = Date.now()
|
|
280
|
+
var timeoutMs = 3 * 60 * 1000
|
|
281
|
+
var pollMs = 1500
|
|
282
|
+
|
|
283
|
+
while (true) {
|
|
284
|
+
if (Date.now() - startedAt > timeoutMs) {
|
|
285
|
+
throw new Error('Sign-in timed out — please try again')
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
await new Promise(function (r) { setTimeout(r, pollMs) })
|
|
289
|
+
|
|
290
|
+
try {
|
|
291
|
+
var statusResp = await swFetch(
|
|
292
|
+
API_BASE + '/core/auth/session/status?session=' + encodeURIComponent(sessionId),
|
|
293
|
+
{ method: 'GET' }
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
if (!statusResp.ok) continue
|
|
297
|
+
|
|
298
|
+
var statusData = statusResp.data || {}
|
|
299
|
+
var status = statusData.status || statusData.data?.status || statusData.state || statusData.data?.state
|
|
300
|
+
if (status === 'ready_for_confirm') break
|
|
301
|
+
if (status === 'expired' || status === 'revoked' || status === 'invalid') {
|
|
302
|
+
throw new Error('Session ' + status + ' — please try again')
|
|
303
|
+
}
|
|
304
|
+
} catch (pollErr) {
|
|
305
|
+
if (pollErr.message && pollErr.message.includes('Session')) throw pollErr
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (onStatus) onStatus('Confirming session...')
|
|
310
|
+
|
|
311
|
+
var confirmResp = await proxyFetch(API_BASE + '/core/auth/session/confirm', {
|
|
312
|
+
method: 'POST',
|
|
313
|
+
headers: { 'Content-Type': 'application/json' },
|
|
314
|
+
body: JSON.stringify({
|
|
315
|
+
session_id: sessionId,
|
|
316
|
+
code_verifier: codeVerifier
|
|
317
|
+
})
|
|
318
|
+
})
|
|
319
|
+
|
|
320
|
+
if (!confirmResp.ok) {
|
|
321
|
+
var confirmData = confirmResp.data || {}
|
|
322
|
+
throw new Error(confirmData.message || confirmData.error || 'Session confirm failed')
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
var cData = confirmResp.data || {}
|
|
326
|
+
var token = cData.access_token || cData.data?.access_token ||
|
|
327
|
+
cData.token || cData.data?.token
|
|
328
|
+
if (!token) {
|
|
329
|
+
throw new Error('Sign-in succeeded but no token returned')
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
var tempTokens = { accessToken: token, refreshToken: null, accessTokenExp: null }
|
|
333
|
+
await setStoredAuth(tempTokens, null)
|
|
334
|
+
|
|
335
|
+
try {
|
|
336
|
+
var user = await getMe()
|
|
337
|
+
await setStoredAuth(tempTokens, user)
|
|
338
|
+
return { tokens: tempTokens, user: user }
|
|
339
|
+
} catch (e) {
|
|
340
|
+
return { tokens: tempTokens, user: null }
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// ============================================================
|
|
345
|
+
// AI — platform AI endpoint
|
|
346
|
+
// ============================================================
|
|
347
|
+
async function aiPrompt (messages, context) {
|
|
348
|
+
return authedRequest('/core/ai/chat', {
|
|
349
|
+
method: 'POST',
|
|
350
|
+
body: JSON.stringify({ messages, context })
|
|
351
|
+
})
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// ============================================================
|
|
355
|
+
// Expose global API
|
|
356
|
+
// ============================================================
|
|
357
|
+
window.SymbolsAuth = {
|
|
358
|
+
login,
|
|
359
|
+
loginViaBrowser,
|
|
360
|
+
logout,
|
|
361
|
+
getMe,
|
|
362
|
+
isSignedIn,
|
|
363
|
+
getStoredAuth,
|
|
364
|
+
getAccessToken,
|
|
365
|
+
getServiceToken,
|
|
366
|
+
listProjects,
|
|
367
|
+
getProjectByKey,
|
|
368
|
+
getProjectData,
|
|
369
|
+
authedRequest,
|
|
370
|
+
aiPrompt,
|
|
371
|
+
API_BASE
|
|
372
|
+
}
|
|
373
|
+
})()
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#symbols-overlay {
|
|
2
|
+
position: fixed;
|
|
3
|
+
top: 0;
|
|
4
|
+
left: 0;
|
|
5
|
+
width: 100vw;
|
|
6
|
+
height: 100vh;
|
|
7
|
+
z-index: 999999999998;
|
|
8
|
+
opacity: 0;
|
|
9
|
+
transition: opacity 350ms cubic-bezier(0.29, 0.67, 0.51, 0.97);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
body:not(.symbols-grabber-active) #symbols-overlay {
|
|
13
|
+
pointer-events: none;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
:not(#symbols-overlay) .symbols-grabber-hovered {
|
|
17
|
+
outline: 3px solid #0085ff !important;
|
|
18
|
+
outline-offset: 1px !important;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
iframe#symbols-frame {
|
|
22
|
+
position: fixed;
|
|
23
|
+
top: 50%;
|
|
24
|
+
left: 50%;
|
|
25
|
+
width: 85vw;
|
|
26
|
+
height: 85vh;
|
|
27
|
+
border-radius: 0.618em;
|
|
28
|
+
transform: translate(-50%, -50%);
|
|
29
|
+
border: none;
|
|
30
|
+
box-shadow: #121212f7 0 0 0 1000em;
|
|
31
|
+
background: #121212f7;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
div#close-btn {
|
|
35
|
+
cursor: pointer;
|
|
36
|
+
z-index: 9999999999999;
|
|
37
|
+
position: fixed;
|
|
38
|
+
top: 5vh;
|
|
39
|
+
right: 6.4vw;
|
|
40
|
+
font-size: 1.5rem;
|
|
41
|
+
line-height: 0.9;
|
|
42
|
+
width: 1em;
|
|
43
|
+
height: 1em;
|
|
44
|
+
color: white;
|
|
45
|
+
font-family: monospace;
|
|
46
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"manifest_version": 3,
|
|
3
|
+
"name": "Symbols Connect",
|
|
4
|
+
"version": "3.0.0",
|
|
5
|
+
"description": "symbols.app connect — DOMQL Inspector and element grabber for the Symbols design system",
|
|
6
|
+
"permissions": ["scripting", "tabs", "activeTab", "storage", "declarativeNetRequest"],
|
|
7
|
+
"host_permissions": ["<all_urls>"],
|
|
8
|
+
"icons": {
|
|
9
|
+
"48": "assets/48x48.png",
|
|
10
|
+
"144": "assets/144x144.png"
|
|
11
|
+
},
|
|
12
|
+
"action": {
|
|
13
|
+
"default_icon": {
|
|
14
|
+
"48": "assets/48x48.png",
|
|
15
|
+
"72": "assets/72x72.png",
|
|
16
|
+
"144": "assets/144x144.png"
|
|
17
|
+
},
|
|
18
|
+
"default_title": "Symbols Connect"
|
|
19
|
+
},
|
|
20
|
+
"background": {
|
|
21
|
+
"service_worker": "service_worker.js"
|
|
22
|
+
},
|
|
23
|
+
"devtools_page": "devtools.html",
|
|
24
|
+
"options_page": "settings.html",
|
|
25
|
+
"commands": {
|
|
26
|
+
"toggleGrabber": {
|
|
27
|
+
"suggested_key": {
|
|
28
|
+
"default": "Ctrl+E",
|
|
29
|
+
"mac": "Command+E"
|
|
30
|
+
},
|
|
31
|
+
"description": "Toggle grabber mode"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"content_scripts": [
|
|
35
|
+
{
|
|
36
|
+
"matches": ["https://*/*", "http://*/*"],
|
|
37
|
+
"exclude_globs": ["https://symbols.app/*", "https://platform.symbo.ls/*"],
|
|
38
|
+
"js": ["content.js"],
|
|
39
|
+
"run_at": "document_end",
|
|
40
|
+
"all_frames": true
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"matches": ["https://*/*", "http://*/*"],
|
|
44
|
+
"css": ["content.css"],
|
|
45
|
+
"run_at": "document_end",
|
|
46
|
+
"all_frames": true
|
|
47
|
+
}
|
|
48
|
+
],
|
|
49
|
+
"web_accessible_resources": [{
|
|
50
|
+
"resources": ["page-agent.js", "picker.html"],
|
|
51
|
+
"matches": ["<all_urls>"]
|
|
52
|
+
}],
|
|
53
|
+
"externally_connectable": {
|
|
54
|
+
"matches": ["https://symbols.app/*", "https://platform.symbo.ls/*"]
|
|
55
|
+
}
|
|
56
|
+
}
|