@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.
Files changed (77) hide show
  1. package/build.js +205 -0
  2. package/dist/assets/1024x1024.png +0 -0
  3. package/dist/assets/128x128.png +0 -0
  4. package/dist/assets/144x144.png +0 -0
  5. package/dist/assets/192x192.png +0 -0
  6. package/dist/assets/48x48.png +0 -0
  7. package/dist/assets/512x512.png +0 -0
  8. package/dist/assets/72x72.png +0 -0
  9. package/dist/assets/96x96.png +0 -0
  10. package/dist/assets/active_cursor.png +0 -0
  11. package/dist/assets/favicon.svg +6 -0
  12. package/dist/assets/old/144x144.png +0 -0
  13. package/dist/assets/old/192x192.png +0 -0
  14. package/dist/assets/old/48x48.png +0 -0
  15. package/dist/assets/old/48x48_faint.png +0 -0
  16. package/dist/assets/old/512x512.png +0 -0
  17. package/dist/assets/old/72x72.png +0 -0
  18. package/dist/assets/old/96x96.png +0 -0
  19. package/dist/auth.js +373 -0
  20. package/dist/content.css +46 -0
  21. package/dist/content.js +1171 -0
  22. package/dist/content.js.map +7 -0
  23. package/dist/devtools.html +7 -0
  24. package/dist/devtools.js +5 -0
  25. package/dist/manifest.json +87 -0
  26. package/dist/page-agent.js +727 -0
  27. package/dist/panel.css +2239 -0
  28. package/dist/panel.html +235 -0
  29. package/dist/panel.js +4973 -0
  30. package/dist/picker.html +111 -0
  31. package/dist/picker.js +300 -0
  32. package/dist/service_worker.js +219 -0
  33. package/dist/service_worker.js.map +7 -0
  34. package/dist/settings.css +128 -0
  35. package/dist/settings.html +26 -0
  36. package/dist/settings_ui.js +57 -0
  37. package/dist/settings_ui.js.map +7 -0
  38. package/package.json +20 -0
  39. package/src/content.js +104 -0
  40. package/src/grabber/clean.js +605 -0
  41. package/src/grabber/computed.js +78 -0
  42. package/src/grabber/parse.js +268 -0
  43. package/src/grabber/stylesheets.js +117 -0
  44. package/src/grabber/utils.js +238 -0
  45. package/src/service_worker.js +246 -0
  46. package/src/settings/settings_ui.js +52 -0
  47. package/src/settings/settings_utils.js +70 -0
  48. package/static/assets/1024x1024.png +0 -0
  49. package/static/assets/128x128.png +0 -0
  50. package/static/assets/144x144.png +0 -0
  51. package/static/assets/192x192.png +0 -0
  52. package/static/assets/48x48.png +0 -0
  53. package/static/assets/512x512.png +0 -0
  54. package/static/assets/72x72.png +0 -0
  55. package/static/assets/96x96.png +0 -0
  56. package/static/assets/active_cursor.png +0 -0
  57. package/static/assets/favicon.svg +6 -0
  58. package/static/assets/old/144x144.png +0 -0
  59. package/static/assets/old/192x192.png +0 -0
  60. package/static/assets/old/48x48.png +0 -0
  61. package/static/assets/old/48x48_faint.png +0 -0
  62. package/static/assets/old/512x512.png +0 -0
  63. package/static/assets/old/72x72.png +0 -0
  64. package/static/assets/old/96x96.png +0 -0
  65. package/static/auth.js +373 -0
  66. package/static/content.css +46 -0
  67. package/static/devtools.html +7 -0
  68. package/static/devtools.js +5 -0
  69. package/static/manifest.json +56 -0
  70. package/static/page-agent.js +727 -0
  71. package/static/panel.css +2239 -0
  72. package/static/panel.html +235 -0
  73. package/static/panel.js +4973 -0
  74. package/static/picker.html +111 -0
  75. package/static/picker.js +300 -0
  76. package/static/settings.css +128 -0
  77. package/static/settings.html +26 -0
@@ -0,0 +1,268 @@
1
+ import {
2
+ consolidateStyles,
3
+ consolidateFlexCSS,
4
+ consolidateGridCSS,
5
+ splitPropsFromStyles
6
+ } from './clean'
7
+ import {
8
+ getAppliedComputedStyles,
9
+ getStyleDefaultsFromEmptyNode
10
+ } from './computed'
11
+ import { getSettings } from '../settings/settings_utils'
12
+ import { getAllAppliedSheetStyles } from './stylesheets'
13
+ import {
14
+ capitalize,
15
+ cleanSharedProperties,
16
+ convertKeysToCamelCase,
17
+ extractRootVars,
18
+ reformatChildren
19
+ } from './utils'
20
+
21
+ export function getNodeText(node) {
22
+ if (node.nodeType === window.Node.TEXT_NODE) {
23
+ return window.Node.TEXT_NODE // This node itself is text
24
+ }
25
+
26
+ let val = ''
27
+ for (const child of node.childNodes) {
28
+ if (
29
+ child.nodeType === window.Node.TEXT_NODE &&
30
+ child.textContent.trim() !== ''
31
+ ) {
32
+ val += child.textContent.trim()
33
+ }
34
+ }
35
+
36
+ if (val.trim() !== '') {
37
+ return val
38
+ }
39
+ }
40
+
41
+ export function parseNodeAttributes(node) {
42
+ const attributes = {}
43
+
44
+ if (node && node.attributes) {
45
+ for (const { name, value } of node.attributes) {
46
+ if (name !== 'class' && name !== 'style') {
47
+ attributes[name] = value
48
+ }
49
+ }
50
+ }
51
+
52
+ return Object.keys(attributes).length ? attributes : null
53
+ }
54
+
55
+ export function extendsFromTag(props) {
56
+ const extend = []
57
+
58
+ const tag = (props.tag ?? 'div').toLowerCase()
59
+
60
+ if (tag === 'a') {
61
+ extend.push('Link')
62
+ }
63
+
64
+ const validTags = [
65
+ 'input',
66
+ 'button',
67
+ 'img',
68
+ 'span',
69
+ 'strong',
70
+ 'section',
71
+ 'picture',
72
+ 'form',
73
+ 'dialog',
74
+ 'p',
75
+ 'hr',
76
+ 'h1',
77
+ 'h2',
78
+ 'h3',
79
+ 'h4',
80
+ 'h5',
81
+ 'h6',
82
+ 'header',
83
+ 'footer',
84
+ 'progress',
85
+ 'select',
86
+ 'textarea',
87
+ 'aside',
88
+ 'pre',
89
+ 'code',
90
+ 'svg',
91
+ 'label',
92
+ 'hgroup'
93
+ ]
94
+
95
+ if (validTags.includes(tag)) {
96
+ extend.push(capitalize(tag))
97
+ }
98
+
99
+ return extend
100
+ }
101
+
102
+ /**
103
+ * @param {HTMLElement} node
104
+ * @param {CSSStyleDeclaration} rootStyles
105
+ * @returns
106
+ */
107
+ export async function parseElement(node, rootStyles) {
108
+ const tag = node.tagName.toLowerCase()
109
+ const defaults = getStyleDefaultsFromEmptyNode(tag)
110
+
111
+ const settings = await getSettings()
112
+
113
+ const [sheets, conditionalSheets] =
114
+ settings.useStylesheets ?? true
115
+ ? getAllAppliedSheetStyles(node, defaults)
116
+ : {}
117
+ const computed = settings.useComputed
118
+ ? getAppliedComputedStyles(node, defaults)
119
+ : {}
120
+
121
+ const rawStyles = {
122
+ ...sheets,
123
+ ...computed
124
+ }
125
+
126
+ let rootVars = extractRootVars(rawStyles, rootStyles)
127
+
128
+ const camelCased = convertKeysToCamelCase(rawStyles)
129
+
130
+ // Process all conditional styles updating both variables as well as resulting props/styles
131
+ const camelCasedConditionals = {}
132
+ Object.entries(conditionalSheets).forEach(([query, cSheet]) => {
133
+ rootVars = { ...extractRootVars(cSheet, rootStyles), ...rootVars }
134
+
135
+ const { props, style } = splitPropsFromStyles(
136
+ convertKeysToCamelCase(cSheet)
137
+ )
138
+ camelCasedConditionals[query] = {}
139
+ if (Object.keys(props).length) {
140
+ camelCasedConditionals[query] = props
141
+ }
142
+
143
+ if (Object.keys(style).length) {
144
+ camelCasedConditionals[query].style = style
145
+ }
146
+ })
147
+
148
+ let props = { ...rootVars }
149
+ if (tag !== 'div') {
150
+ props.tag = tag
151
+ }
152
+
153
+ const { props: styleProps, style } = splitPropsFromStyles(
154
+ consolidateStyles(camelCased)
155
+ )
156
+ props = { ...props, ...styleProps }
157
+
158
+ const text = getNodeText(node)
159
+
160
+ let children = []
161
+ const childPromises = []
162
+
163
+ if (node.children.length > 0) {
164
+ const ignore = ['STYLE', 'SCRIPT']
165
+
166
+ for (const childNode of node.children) {
167
+ if (!ignore.includes(childNode.tagName)) {
168
+ if (childNode.tagName === 'svg') {
169
+ const html = childNode.innerHTML
170
+ const attr = parseNodeAttributes(childNode)
171
+
172
+ children.push({
173
+ extend: ['Svg'],
174
+ attr,
175
+ props: { html }
176
+ })
177
+ } else {
178
+ childPromises.push(parseElement(childNode, rootStyles))
179
+ }
180
+ }
181
+ }
182
+ }
183
+
184
+ const results = await Promise.all(childPromises)
185
+ children = [...children, ...results]
186
+
187
+ // check all children for shared props and hoist them to childProps
188
+ let childProps = null
189
+ if (children.length > 1) {
190
+ childProps = cleanSharedProperties(children)
191
+ // remove shared props from each child
192
+ Object.keys(childProps).forEach((key) => {
193
+ children.forEach((child) => delete child.props[key])
194
+ })
195
+ }
196
+
197
+ const attr = parseNodeAttributes(node)
198
+ // process attributes of specific tags to props
199
+ const copyAttrFromThese = ['a', 'img', 'input', 'button', 'iframe']
200
+ if (attr && copyAttrFromThese.includes(tag)) {
201
+ Object.keys(attr).forEach((prop) => {
202
+ props[prop] = attr[prop]
203
+ delete attr[prop]
204
+ })
205
+ }
206
+
207
+ // fix relative src for images
208
+ if (tag === 'img' && props.src && props.src.startsWith('/')) {
209
+ props.src = window.location.origin + props.src
210
+ }
211
+
212
+ if (tag === 'a' && props.href && props.href.startsWith('/')) {
213
+ props.href = window.location.origin + props.href
214
+ }
215
+
216
+ const extend = extendsFromTag(props)
217
+ if (extend.length === 1) {
218
+ delete props.tag
219
+ }
220
+
221
+ if (props.display === 'flex') {
222
+ props = consolidateFlexCSS(props)
223
+ delete props.display
224
+ extend.push('Flex')
225
+ }
226
+
227
+ if (props.display === 'grid') {
228
+ props = consolidateGridCSS(props)
229
+ delete props.display
230
+ extend.push('Grid')
231
+ }
232
+
233
+ let element = {
234
+ props: { ...props }
235
+ }
236
+
237
+ if (Object.keys(style).length) {
238
+ element.props.style = style
239
+ }
240
+
241
+ if (Object.keys(camelCasedConditionals).length) {
242
+ element.props = { ...element.props, ...camelCasedConditionals }
243
+ }
244
+
245
+ if (text) {
246
+ element.props.text = text
247
+ }
248
+
249
+ if (attr && Object.keys(attr).length) {
250
+ element.props.attr = attr
251
+ }
252
+
253
+ if (extend.length) {
254
+ element.extend = extend
255
+ }
256
+
257
+ if (children.length) {
258
+ if (childProps && Object.keys(childProps).length) {
259
+ element.props.childProps = childProps
260
+ }
261
+
262
+ element = { ...element, ...reformatChildren(children) }
263
+ } else if (childProps) {
264
+ console.warn('childProps without children')
265
+ }
266
+
267
+ return element
268
+ }
@@ -0,0 +1,117 @@
1
+ const getRelevantRules = (node, rules) => {
2
+ let relevantRules = []
3
+
4
+ for (const rule of rules) {
5
+ const { selectorText, conditionText, media, cssRules } = rule
6
+
7
+ if (selectorText && node.matches(selectorText)) {
8
+ relevantRules.push(rule)
9
+ } else if ((conditionText || (media && media.length > 0)) && cssRules) {
10
+ relevantRules = [
11
+ ...relevantRules,
12
+ ...getRelevantRules(node, rule.cssRules)
13
+ ]
14
+ }
15
+ }
16
+
17
+ return relevantRules
18
+ }
19
+
20
+ const extractStylesFromRule = (rule, defaults = {}) => {
21
+ const { style } = rule
22
+ const results = {}
23
+
24
+ // Loop through each property and add it to the styles object
25
+ for (let k = 0; k < style.length; k++) {
26
+ const prop = style[k]
27
+ const value = style.getPropertyValue(prop)
28
+ const defaultValue = defaults[prop]
29
+
30
+ if (value && value !== defaultValue) {
31
+ results[prop] = value
32
+ }
33
+ }
34
+
35
+ return results
36
+ }
37
+
38
+ const processMediaQueries = (rule, parentRule) => {
39
+ const { conditionText } = parentRule
40
+ let key = `@media ${conditionText}`
41
+ if (
42
+ ['(prefers-color-scheme: dark)', '(prefers-color-scheme: light)'].includes(
43
+ conditionText
44
+ )
45
+ ) {
46
+ key = conditionText.includes('dark') ? '@dark' : '@light'
47
+ }
48
+
49
+ return { [key]: { ...extractStylesFromRule(rule) } }
50
+ }
51
+
52
+ const getAppliedStylesheets = (node) => {
53
+ let styles = {}
54
+ let conditionalStyles = {}
55
+
56
+ // Loop through all stylesheets
57
+ for (const styleSheet of document.styleSheets) {
58
+ let baseRules = null
59
+ // Try to access the cssRules of the stylesheet (some stylesheets may be cross-origin and not accessible)
60
+ try {
61
+ baseRules = styleSheet.cssRules
62
+ } catch (error) {
63
+ // Unable to access stylesheet (possibly cross-origin), skip it
64
+ console.log('Could not access stylesheet rules:', { styleSheet, error })
65
+ }
66
+
67
+ if (baseRules !== null) {
68
+ // filter rules down to relevant ones with selectors
69
+ const rules = getRelevantRules(node, baseRules)
70
+
71
+ // Loop through all rules within the stylesheet
72
+ for (const rule of rules) {
73
+ const { parentRule } = rule
74
+
75
+ // Rule is enclosed within a media query
76
+ if (parentRule && parentRule.conditionText) {
77
+ conditionalStyles = {
78
+ ...conditionalStyles,
79
+ ...processMediaQueries(rule, parentRule)
80
+ }
81
+ } else if (rule.selectorText.includes(':hover')) {
82
+ // Hover styles should be extracted separately as they do not
83
+ // follow the same patterns as other conditional styles
84
+ const hoverKey = ':hover'
85
+ conditionalStyles[hoverKey] = {
86
+ ...conditionalStyles[hoverKey],
87
+ ...extractStylesFromRule(rule)
88
+ }
89
+ } else {
90
+ styles = { ...styles, ...extractStylesFromRule(rule) }
91
+ }
92
+ }
93
+ }
94
+ }
95
+
96
+ return [styles, conditionalStyles]
97
+ }
98
+
99
+ const getInlineStyles = (node, defaults) => {
100
+ const styles = {}
101
+ for (let i = 0; i < node.style.length; i++) {
102
+ const prop = node.style[i]
103
+ const val = node.style.getPropertyValue(prop)
104
+
105
+ if (val && val !== defaults[prop]) {
106
+ styles[prop] = val
107
+ }
108
+ }
109
+
110
+ return styles
111
+ }
112
+
113
+ export function getAllAppliedSheetStyles(node, defaults) {
114
+ const inlineStyles = getInlineStyles(node, defaults)
115
+ const [stylesheetStyles, conditionalStyles] = getAppliedStylesheets(node)
116
+ return [{ ...stylesheetStyles, ...inlineStyles }, conditionalStyles]
117
+ }
@@ -0,0 +1,238 @@
1
+ /** Helper function to check if all values in an array are equal
2
+ * @param {Array} arr
3
+ * @returns {boolean}
4
+ */
5
+ export const allEqual = (arr) => arr.every((v) => v === arr[0])
6
+
7
+ // Capitalize function for the border properties
8
+ export const capitalize = (string) =>
9
+ string.charAt(0).toUpperCase() + string.slice(1)
10
+
11
+ // Convert kebab-case to camelCase
12
+ export const toCamelCase = (str) =>
13
+ str.replace(/[_\- ]+(?<first>[a-z])/giu, (_, char, offSet) =>
14
+ offSet > 0 ? char.toUpperCase() : char
15
+ )
16
+
17
+ /**
18
+ * Convert all keys in a style object to camelCase, separating out CSS variables
19
+ * @param {Record<string, string>} styles
20
+ * @returns {Record<string, string>}
21
+ */
22
+ export function extractCssVars(styles) {
23
+ const cssVariables = {}
24
+
25
+ for (const key in styles) {
26
+ if (Object.hasOwn(styles, key) && key.includes('--')) {
27
+ cssVariables[key] = styles[key]
28
+ }
29
+ }
30
+
31
+ return cssVariables
32
+ }
33
+
34
+ /**
35
+ * Convert all keys in a style object to camelCase except CSS variables
36
+ * @param {Record<string, string>} styles
37
+ * @returns {Record<string, string>}
38
+ */
39
+ export function convertKeysToCamelCase(styles) {
40
+ const camelCased = {}
41
+
42
+ for (const key in styles) {
43
+ if (Object.hasOwn(styles, key) && !key.includes('--')) {
44
+ camelCased[toCamelCase(key)] = styles[key]
45
+ }
46
+ }
47
+
48
+ return camelCased
49
+ }
50
+
51
+ function findVarPattern(input) {
52
+ // Regex to capture variable names with letters, numbers, and hyphens
53
+ const regex = /var\(--(?<varName>[a-zA-Z0-9-]+)(?:,\s*[a-zA-Z0-9%-]+)?\)/gu
54
+ const matches = []
55
+ let match = regex.exec(input)
56
+
57
+ // Find all matches in the input string
58
+ while (match !== null) {
59
+ const varName = `--${match.groups.varName}`
60
+ matches.push(varName)
61
+
62
+ match = regex.exec(input)
63
+ }
64
+
65
+ // Remove duplicates and return the result
66
+ return [...new Set(matches)]
67
+ }
68
+
69
+ /**
70
+ * @param {Record<string, string>} css
71
+ * @param {CSSStyleDeclaration} globalStyles
72
+ * @returns {Record<string, string>} extracted root variables
73
+ */
74
+ export const extractRootVars = (css, globalStyles) => {
75
+ const variables = extractCssVars(css)
76
+ let rootVars = Object.values(css).reduce((acc, value) => {
77
+ const vars = {}
78
+
79
+ if (value.includes('var(')) {
80
+ const varNames = findVarPattern(value)
81
+ varNames.forEach((v) => {
82
+ let val = variables[v]
83
+ if (val || val === '') {
84
+ vars[v] = val || ' '
85
+ } else {
86
+ val = globalStyles.getPropertyValue(v)
87
+ if (val || val === '') {
88
+ vars[v] = val || ' '
89
+ }
90
+ }
91
+ })
92
+ }
93
+
94
+ return { ...acc, ...vars }
95
+ }, {})
96
+
97
+ // Check for nested root variables
98
+ if (Object.keys(rootVars).length) {
99
+ let checkingNested = true
100
+ let nested = {}
101
+ while (checkingNested) {
102
+ const parsed = Object.values({
103
+ ...rootVars,
104
+ ...variables,
105
+ ...nested
106
+ }).reduce(
107
+ /* eslint-disable-next-line no-loop-func -- checking the contents of rootVars and nested is safe */
108
+ (acc, value) => {
109
+ const vars = {}
110
+ if (value.includes('var(')) {
111
+ const varNames = findVarPattern(value)
112
+ varNames.forEach((v) => {
113
+ if (v in rootVars || v in nested) {
114
+ return // Skip if already in rootVars or nested to prevent infinite loop
115
+ }
116
+ let val = variables[v]
117
+ if (val || val === '') {
118
+ vars[v] = val || ' '
119
+ } else {
120
+ val = globalStyles.getPropertyValue(v)
121
+ if (val || val === '') {
122
+ vars[v] = val || ' '
123
+ }
124
+ }
125
+ })
126
+ }
127
+
128
+ return { ...acc, ...vars }
129
+ },
130
+ {}
131
+ )
132
+
133
+ // If no new variables found stop checking, otherwise merge them into nested
134
+ if (Object.keys(parsed).length) {
135
+ nested = { ...nested, ...parsed }
136
+ } else {
137
+ checkingNested = false
138
+ }
139
+ }
140
+
141
+ rootVars = { ...rootVars, ...nested }
142
+ }
143
+
144
+ return rootVars
145
+ }
146
+
147
+ // Helper function to deep compare objects, with support for circular references
148
+ function isEqual(objA, objB, visited = new WeakSet()) {
149
+ if (objA === objB) {
150
+ return true
151
+ } // Check for reference equality
152
+ if (objA == null || objB == null) {
153
+ return false
154
+ } // Handle null or undefined
155
+
156
+ if (typeof objA !== 'object' || typeof objB !== 'object') {
157
+ return false // Both must be objects
158
+ }
159
+
160
+ // Handle circular references
161
+ if (visited.has(objA) || visited.has(objB)) {
162
+ return false
163
+ }
164
+ visited.add(objA)
165
+ visited.add(objB)
166
+
167
+ const keysA = Object.keys(objA)
168
+ const keysB = Object.keys(objB)
169
+
170
+ if (keysA.length !== keysB.length) {
171
+ return false
172
+ } // Different number of keys
173
+
174
+ for (const key of keysA) {
175
+ if (!keysB.includes(key) || !isEqual(objA[key], objB[key], visited)) {
176
+ return false // Keys don't match or values are different
177
+ }
178
+ }
179
+
180
+ return true // Objects are equal
181
+ }
182
+
183
+ // Function to find shared properties
184
+ export function cleanSharedProperties(children) {
185
+ // Extract props from children
186
+ const props = children.map((child) => child.props)
187
+ const shared = {}
188
+
189
+ // Get keys from the first style object
190
+ const keys = Object.keys(props[0])
191
+
192
+ for (const key of keys) {
193
+ // Check if all style objects have the same value for this key
194
+ const values = props.map((style) => style[key])
195
+
196
+ if (values.every((value) => isEqual(value, values[0]))) {
197
+ const [val] = values
198
+ shared[key] = val // Add to shared properties
199
+ }
200
+ }
201
+
202
+ return shared // Return the shared properties object
203
+ }
204
+
205
+ /**
206
+ * Converts an array of children objects into a single object with unique keys.
207
+ * @param {Array<any>} children
208
+ * @returns {Record<string, any>} reformatted children object
209
+ */
210
+ export function reformatChildren(children) {
211
+ const childrenObj = {}
212
+
213
+ const keyCounts = {}
214
+
215
+ children.forEach((child) => {
216
+ let key = child.extend ?? 'Child' // Default key if `extend` is missing
217
+
218
+ // Check for multiple `extends` in the array
219
+ if (Array.isArray(key)) {
220
+ key = child.extend.shift()
221
+ if (!child.extend.length) {
222
+ delete child.extend
223
+ }
224
+ }
225
+
226
+ // Increment the key count to handle duplicates
227
+ if (!(key in keyCounts)) {
228
+ keyCounts[key] = 0
229
+ }
230
+ keyCounts[key]++
231
+
232
+ const newKey = keyCounts[key] > 1 ? `${key}_${keyCounts[key]}` : key
233
+
234
+ childrenObj[newKey] = { ...child }
235
+ })
236
+
237
+ return childrenObj
238
+ }