From 74e5bb9104551bfd8105ef6780c40502e9087adc Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Thu, 19 Sep 2024 04:24:35 +0300 Subject: [PATCH 01/10] serializer working --- src/services/theme_data/iss_deserializer.js | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 src/services/theme_data/iss_deserializer.js diff --git a/src/services/theme_data/iss_deserializer.js b/src/services/theme_data/iss_deserializer.js new file mode 100644 index 000000000..44a0fadeb --- /dev/null +++ b/src/services/theme_data/iss_deserializer.js @@ -0,0 +1,3 @@ +export const deserializer (string) { +let level = 0 +} From af3b2e3dc9043e66806be85869d867e6b0d23c3c Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Thu, 19 Sep 2024 20:37:14 +0300 Subject: [PATCH 02/10] temp --- src/services/theme_data/iss_deserializer.js | 44 ++++++++++++++++- .../theme_data/theme_data_3.service.js | 30 +++++++++++- .../theme_data/iss_deserializer.spec.js | 47 +++++++++++++++++++ 3 files changed, 117 insertions(+), 4 deletions(-) create mode 100644 test/unit/specs/services/theme_data/iss_deserializer.spec.js diff --git a/src/services/theme_data/iss_deserializer.js b/src/services/theme_data/iss_deserializer.js index 44a0fadeb..431e1b947 100644 --- a/src/services/theme_data/iss_deserializer.js +++ b/src/services/theme_data/iss_deserializer.js @@ -1,3 +1,43 @@ -export const deserializer (string) { -let level = 0 +// this works nearly the same as HTML tree converter +export const deserialize = (input) => { + const buffer = [] + let textBuffer = '' + + const getCurrentBuffer = () => { + let current = buffer[buffer.length - 1][1] + if (current == null) { + current = { name: null, content: [] } + } + buffer.push(current) + return current + } + + // Processes current line buffer, adds it to output buffer and clears line buffer + const flushText = (content) => { + if (textBuffer === '') return + if (content) { + getCurrentBuffer().content.push(textBuffer) + } else { + getCurrentBuffer().name = textBuffer + } + textBuffer = '' + } + + for (let i = 0; i < input.length; i++) { + const char = input[i] + + if (char === ';') { + flushText(true) + } else if (char === '{') { + flushText(false) + } else if (char === '}') { + buffer.push({ name: null, content: [] }) + textBuffer = '' + } else { + textBuffer += char + } + } + + flushText() + return buffer } diff --git a/src/services/theme_data/theme_data_3.service.js b/src/services/theme_data/theme_data_3.service.js index cf58da119..cac450a84 100644 --- a/src/services/theme_data/theme_data_3.service.js +++ b/src/services/theme_data/theme_data_3.service.js @@ -23,6 +23,9 @@ import { findRules } from './iss_utils.js' import { parseCssShadow } from './css_utils.js' +import { + serialize +} from './iss_serializer.js' // Ensuring the order of components const components = { @@ -504,9 +507,32 @@ export const init = ({ console.debug('Eager processing took ' + (t2 - t1) + ' ms') } + // optimization to traverse big-ass array only once instead of twice + const eager = [] + const lazy = [] + + result.forEach(x => { + if (typeof x === 'function') { + lazy.push(x) + } else { + eager.push(x) + } + }) + + const serializedData = serialize(eager) + const file = new File(serializedData, 'ruleset.piss') + const blobUrl = URL.createObjectURL(file) + const a = document.createElement('a') + a.href = blobUrl + a.download = 'ruleset.piss' + document.body.appendChild(a) + a.dispatchEvent(new MouseEvent('click')) + URL.revokeObjectURL(blobUrl) + document.body.removeChild(a) + return { - lazy: result.filter(x => typeof x === 'function'), - eager: result.filter(x => typeof x !== 'function'), + lazy, + eager, staticVars, engineChecksum } diff --git a/test/unit/specs/services/theme_data/iss_deserializer.spec.js b/test/unit/specs/services/theme_data/iss_deserializer.spec.js new file mode 100644 index 000000000..f19671447 --- /dev/null +++ b/test/unit/specs/services/theme_data/iss_deserializer.spec.js @@ -0,0 +1,47 @@ +import { deserialize } from 'src/services/theme_data/iss_deserializer.js' + +/* eslint-disable quotes */ +const testData = ``` + Root { + --accent: color | #e2b188; + --badgeNotification: color | #e15932; + --bg: color | #0f161e; + --cBlue: color | #81beea; + --cGreen: color | #5dc94a; + --cOrange: color | #ffc459; + --cRed: color | #d31014; + --defaultButtonBevel: shadow | $borderSide(#FFFFFF, top, 0.2) | $borderSide(#000000, bottom, 0.2); + --defaultButtonHoverGlow: shadow | 0 0 4 --text; + --defaultButtonShadow: shadow | 0 0 2 #000000; + --defaultInputBevel: shadow | $borderSide(#FFFFFF, bottom, 0.2)| $borderSide(#000000, top, 0.2); + --fg: color | #151e2b; + --font: generic | sans-serif; + --link: color | #e2b188; + --monoFont: generic | monospace; + --pressedButtonBevel: shadow | $borderSide(#FFFFFF, bottom, 0.2)| $borderSide(#000000, top, 0.2); + --selectionBackground: color | --accent; + --selectionText: color | $textColor(--accent, --text, no-preserve); + --text: color | #b9b9ba; + --wallpaper: color | #0c1118; + background: transparent; + opacity: 0; + } + + Root Underlay { + background: #000000; + opacity: 0.6; + } + + Root Underlay, test { + background: #000000; + opacity: 0.6; + } + ``` + +describe.only('html_tree_converter', () => { + describe('convertHtmlToTree', () => { + it('should parse ISS correctly', () => { + console.log(deserialize(testData)) + }) + }) +}) From 0c91c376456520b0b58779696067bbf78e46bfd0 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Thu, 19 Sep 2024 21:42:14 +0300 Subject: [PATCH 03/10] somehow i lost this file and had to rewrite it. now it's even better than before! --- src/services/theme_data/iss_serializer.js | 38 +++++++++++++++++++ .../theme_data/theme_data_3.service.js | 11 ------ 2 files changed, 38 insertions(+), 11 deletions(-) create mode 100644 src/services/theme_data/iss_serializer.js diff --git a/src/services/theme_data/iss_serializer.js b/src/services/theme_data/iss_serializer.js new file mode 100644 index 000000000..8d6e9333d --- /dev/null +++ b/src/services/theme_data/iss_serializer.js @@ -0,0 +1,38 @@ +import { unroll } from './iss_utils.js' + +const serializeShadow = s => `{${s.inset ? 'inset ' : ''}${s.x} ${s.y} ${s.blur} ${s.spread} ${s.color} / ${s.alpha}}` + +export const serialize = (ruleset) => { + return ruleset.map((rule) => { + if (Object.keys(rule.directives || {}).length === 0) return false + + const header = unroll(rule).reverse().map(rule => { + const { component } = rule + const newVariant = rule.variant === 'normal' ? '' : ('.' + rule.variant) + const newState = rule.state.filter(st => st !== 'normal') + + return `${component}${newVariant}${newState.map(st => ':' + st).join('')}` + }).join(' ') + + const content = Object.entries(rule.directives).map(([directive, value]) => { + if (directive.startsWith('--')) { + const [valType, newValue] = value.split('|') // only first one! intentional! + switch (valType) { + case 'shadow': + return ` ${directive}: ${newValue.map(serializeShadow).join(', ')}` + default: + return ` ${directive}: ${newValue}` + } + } else { + switch (directive) { + case 'shadow': + return ` ${directive}: ${value.map(serializeShadow).join(', ')}` + default: + return ` ${directive}: ${value}` + } + } + }) + + return `${header} {\n${content.join(';\n')}\n}` + }).filter(x => x).join('\n\n') +} diff --git a/src/services/theme_data/theme_data_3.service.js b/src/services/theme_data/theme_data_3.service.js index cac450a84..b98cbb986 100644 --- a/src/services/theme_data/theme_data_3.service.js +++ b/src/services/theme_data/theme_data_3.service.js @@ -519,17 +519,6 @@ export const init = ({ } }) - const serializedData = serialize(eager) - const file = new File(serializedData, 'ruleset.piss') - const blobUrl = URL.createObjectURL(file) - const a = document.createElement('a') - a.href = blobUrl - a.download = 'ruleset.piss' - document.body.appendChild(a) - a.dispatchEvent(new MouseEvent('click')) - URL.revokeObjectURL(blobUrl) - document.body.removeChild(a) - return { lazy, eager, From 71a478108035ddf3489d75bec6ef11c91829ab27 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Fri, 20 Sep 2024 02:05:25 +0300 Subject: [PATCH 04/10] at last... it's complete --- src/components/button.style.js | 4 +- src/services/theme_data/iss_deserializer.js | 126 ++++++++++++++++-- src/services/theme_data/iss_serializer.js | 16 ++- .../theme_data/theme_data_3.service.js | 3 - .../theme_data/iss_deserializer.spec.js | 51 ++----- 5 files changed, 136 insertions(+), 64 deletions(-) diff --git a/src/components/button.style.js b/src/components/button.style.js index 6fec67a0b..1bee8f8e7 100644 --- a/src/components/button.style.js +++ b/src/components/button.style.js @@ -34,8 +34,8 @@ export default { directives: { '--defaultButtonHoverGlow': 'shadow | 0 0 4 --text', '--defaultButtonShadow': 'shadow | 0 0 2 #000000', - '--defaultButtonBevel': 'shadow | $borderSide(#FFFFFF, top, 0.2) | $borderSide(#000000, bottom, 0.2)', - '--pressedButtonBevel': 'shadow | $borderSide(#FFFFFF, bottom, 0.2)| $borderSide(#000000, top, 0.2)' + '--defaultButtonBevel': 'shadow | $borderSide(#FFFFFF, top, 0.2), $borderSide(#000000, bottom, 0.2)', + '--pressedButtonBevel': 'shadow | $borderSide(#FFFFFF, bottom, 0.2), $borderSide(#000000, top, 0.2)' } }, { diff --git a/src/services/theme_data/iss_deserializer.js b/src/services/theme_data/iss_deserializer.js index 431e1b947..3cd2f15f6 100644 --- a/src/services/theme_data/iss_deserializer.js +++ b/src/services/theme_data/iss_deserializer.js @@ -1,24 +1,51 @@ +import { flattenDeep } from 'lodash' + +const parseShadow = string => { + const modes = ['_full', 'inset', 'x', 'y', 'blur', 'spread', 'color', 'alpha'] + const regexPrep = [ + // inset keyword (optional) + '^(?:(inset)\\s+)?', + // x + '(?:([0-9]+(?:\\.[0-9]+)?)\\s+)', + // y + '(?:([0-9]+(?:\\.[0-9]+)?)\\s+)', + // blur (optional) + '(?:([0-9]+(?:\\.[0-9]+)?)\\s+)?', + // spread (optional) + '(?:([0-9]+(?:\\.[0-9]+)?)\\s+)?', + // either hex, variable or function + '(#[0-9a-f]{6}|--[a-z\\-_]+|\\$[a-z\\-()_]+)', + // opacity (optional) + '(?:\\s+\\/\\s+([0-9]+(?:\\.[0-9]+)?)\\s*)?$' + ].join('') + const regex = new RegExp(regexPrep, 'gis') // global, (stable) indices, single-string + const result = regex.exec(string) + if (result == null) { + return string + } else { + return Object.fromEntries(modes.map((mode, i) => [mode, result[i]])) + } +} // this works nearly the same as HTML tree converter -export const deserialize = (input) => { - const buffer = [] +const parseIss = (input) => { + const buffer = [{ selector: null, content: [] }] let textBuffer = '' const getCurrentBuffer = () => { - let current = buffer[buffer.length - 1][1] + let current = buffer[buffer.length - 1] if (current == null) { - current = { name: null, content: [] } + current = { selector: null, content: [] } } - buffer.push(current) return current } // Processes current line buffer, adds it to output buffer and clears line buffer - const flushText = (content) => { + const flushText = (kind) => { if (textBuffer === '') return - if (content) { - getCurrentBuffer().content.push(textBuffer) + if (kind === 'content') { + getCurrentBuffer().content.push(textBuffer.trim()) } else { - getCurrentBuffer().name = textBuffer + getCurrentBuffer().selector = textBuffer.trim() } textBuffer = '' } @@ -27,17 +54,90 @@ export const deserialize = (input) => { const char = input[i] if (char === ';') { - flushText(true) + flushText('content') } else if (char === '{') { - flushText(false) + flushText('header') } else if (char === '}') { - buffer.push({ name: null, content: [] }) + flushText('content') + buffer.push({ selector: null, content: [] }) textBuffer = '' } else { textBuffer += char } } - flushText() return buffer } +export const deserialize = (input) => { + const ast = parseIss(input) + const finalResult = ast.filter(i => i.selector != null).map(item => { + const { selector, content } = item + let stateCount = 0 + const selectors = selector.split(/,/g) + const result = selectors.map(selector => { + const output = { component: '' } + let currentDepth = null + + selector.split(/ /g).reverse().forEach((fragment, index, arr) => { + const fragmentObject = { component: '' } + + let mode = 'component' + for (let i = 0; i < fragment.length; i++) { + const char = fragment[i] + switch (char) { + case '.': { + mode = 'variant' + fragmentObject.variant = '' + break + } + case ':': { + mode = 'state' + fragmentObject.state = fragmentObject.state || [] + stateCount++ + break + } + default: { + if (mode === 'state') { + const currentState = fragmentObject.state[stateCount - 1] + if (currentState == null) { + fragmentObject.state.push('') + } + fragmentObject.state[stateCount - 1] += char + } else { + fragmentObject[mode] += char + } + } + } + } + if (currentDepth !== null) { + currentDepth.parent = { ...fragmentObject } + currentDepth = currentDepth.parent + } else { + Object.keys(fragmentObject).forEach(key => { + output[key] = fragmentObject[key] + }) + if (index !== (arr.length - 1)) { + output.parent = { component: '' } + } + currentDepth = output + } + }) + + output.directives = Object.fromEntries(content.map(d => { + const [property, value] = d.split(':') + console.log(property, value) + let realValue = value.trim() + if (property === 'shadow') { + realValue = parseShadow(value.split(',').map(v => v.trim())) + } if (!Number.isNaN(Number(value))) { + realValue = Number(value) + } + return [property, realValue] + })) + + return output + }) + return result + }) + return flattenDeep(finalResult) +} diff --git a/src/services/theme_data/iss_serializer.js b/src/services/theme_data/iss_serializer.js index 8d6e9333d..6bba85e45 100644 --- a/src/services/theme_data/iss_serializer.js +++ b/src/services/theme_data/iss_serializer.js @@ -1,6 +1,12 @@ import { unroll } from './iss_utils.js' -const serializeShadow = s => `{${s.inset ? 'inset ' : ''}${s.x} ${s.y} ${s.blur} ${s.spread} ${s.color} / ${s.alpha}}` +const serializeShadow = s => { + if (typeof s === 'object') { + return `{${s.inset ? 'inset ' : ''}${s.x} ${s.y} ${s.blur} ${s.spread} ${s.color} / ${s.alpha}}` + } else { + return s + } +} export const serialize = (ruleset) => { return ruleset.map((rule) => { @@ -8,8 +14,8 @@ export const serialize = (ruleset) => { const header = unroll(rule).reverse().map(rule => { const { component } = rule - const newVariant = rule.variant === 'normal' ? '' : ('.' + rule.variant) - const newState = rule.state.filter(st => st !== 'normal') + const newVariant = (rule.variant == null || rule.variant === 'normal') ? '' : ('.' + rule.variant) + const newState = (rule.state || []).filter(st => st !== 'normal') return `${component}${newVariant}${newState.map(st => ':' + st).join('')}` }).join(' ') @@ -19,9 +25,9 @@ export const serialize = (ruleset) => { const [valType, newValue] = value.split('|') // only first one! intentional! switch (valType) { case 'shadow': - return ` ${directive}: ${newValue.map(serializeShadow).join(', ')}` + return ` ${directive}: ${valType.trim()} | ${newValue.map(serializeShadow).map(s => s.trim()).join(', ')}` default: - return ` ${directive}: ${newValue}` + return ` ${directive}: ${valType.trim()} | ${newValue.trim()}` } } else { switch (directive) { diff --git a/src/services/theme_data/theme_data_3.service.js b/src/services/theme_data/theme_data_3.service.js index b98cbb986..39c8b74fc 100644 --- a/src/services/theme_data/theme_data_3.service.js +++ b/src/services/theme_data/theme_data_3.service.js @@ -23,9 +23,6 @@ import { findRules } from './iss_utils.js' import { parseCssShadow } from './css_utils.js' -import { - serialize -} from './iss_serializer.js' // Ensuring the order of components const components = { diff --git a/test/unit/specs/services/theme_data/iss_deserializer.spec.js b/test/unit/specs/services/theme_data/iss_deserializer.spec.js index f19671447..3488801c9 100644 --- a/test/unit/specs/services/theme_data/iss_deserializer.spec.js +++ b/test/unit/specs/services/theme_data/iss_deserializer.spec.js @@ -1,47 +1,16 @@ import { deserialize } from 'src/services/theme_data/iss_deserializer.js' +import { serialize } from 'src/services/theme_data/iss_serializer.js' +import Button from 'src/components/button.style.js' -/* eslint-disable quotes */ -const testData = ``` - Root { - --accent: color | #e2b188; - --badgeNotification: color | #e15932; - --bg: color | #0f161e; - --cBlue: color | #81beea; - --cGreen: color | #5dc94a; - --cOrange: color | #ffc459; - --cRed: color | #d31014; - --defaultButtonBevel: shadow | $borderSide(#FFFFFF, top, 0.2) | $borderSide(#000000, bottom, 0.2); - --defaultButtonHoverGlow: shadow | 0 0 4 --text; - --defaultButtonShadow: shadow | 0 0 2 #000000; - --defaultInputBevel: shadow | $borderSide(#FFFFFF, bottom, 0.2)| $borderSide(#000000, top, 0.2); - --fg: color | #151e2b; - --font: generic | sans-serif; - --link: color | #e2b188; - --monoFont: generic | monospace; - --pressedButtonBevel: shadow | $borderSide(#FFFFFF, bottom, 0.2)| $borderSide(#000000, top, 0.2); - --selectionBackground: color | --accent; - --selectionText: color | $textColor(--accent, --text, no-preserve); - --text: color | #b9b9ba; - --wallpaper: color | #0c1118; - background: transparent; - opacity: 0; - } +describe.only('ISS (de)serialization', () => { + describe('ISS deserialization', () => { + it('Output should = input', () => { + const normalized = Button.defaultRules.map(x => ({ component: 'Button', ...x })) + const serialized = serialize(normalized) + const deserialized = deserialize(serialized) + // deserialized.toString() - Root Underlay { - background: #000000; - opacity: 0.6; - } - - Root Underlay, test { - background: #000000; - opacity: 0.6; - } - ``` - -describe.only('html_tree_converter', () => { - describe('convertHtmlToTree', () => { - it('should parse ISS correctly', () => { - console.log(deserialize(testData)) + expect(JSON.stringify(deserialized)).to.equal(JSON.stringify(normalized)) }) }) }) From d8d766932a8b3caff7634ca1f9679525ce7123f1 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Fri, 20 Sep 2024 02:07:27 +0300 Subject: [PATCH 05/10] cleanup --- src/services/theme_data/iss_deserializer.js | 1 - test/unit/specs/services/theme_data/iss_deserializer.spec.js | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/services/theme_data/iss_deserializer.js b/src/services/theme_data/iss_deserializer.js index 3cd2f15f6..8d4c987e8 100644 --- a/src/services/theme_data/iss_deserializer.js +++ b/src/services/theme_data/iss_deserializer.js @@ -125,7 +125,6 @@ export const deserialize = (input) => { output.directives = Object.fromEntries(content.map(d => { const [property, value] = d.split(':') - console.log(property, value) let realValue = value.trim() if (property === 'shadow') { realValue = parseShadow(value.split(',').map(v => v.trim())) diff --git a/test/unit/specs/services/theme_data/iss_deserializer.spec.js b/test/unit/specs/services/theme_data/iss_deserializer.spec.js index 3488801c9..d7d96130f 100644 --- a/test/unit/specs/services/theme_data/iss_deserializer.spec.js +++ b/test/unit/specs/services/theme_data/iss_deserializer.spec.js @@ -4,12 +4,12 @@ import Button from 'src/components/button.style.js' describe.only('ISS (de)serialization', () => { describe('ISS deserialization', () => { - it('Output should = input', () => { + it('Output should equal input', () => { const normalized = Button.defaultRules.map(x => ({ component: 'Button', ...x })) const serialized = serialize(normalized) const deserialized = deserialize(serialized) - // deserialized.toString() + // for some reason comparing objects directly fails the assert expect(JSON.stringify(deserialized)).to.equal(JSON.stringify(normalized)) }) }) From dae206add31955484850f40be3f1dde7303a404d Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Fri, 20 Sep 2024 02:07:46 +0300 Subject: [PATCH 06/10] remove 'only' --- test/unit/specs/services/theme_data/iss_deserializer.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/specs/services/theme_data/iss_deserializer.spec.js b/test/unit/specs/services/theme_data/iss_deserializer.spec.js index d7d96130f..7654fb53b 100644 --- a/test/unit/specs/services/theme_data/iss_deserializer.spec.js +++ b/test/unit/specs/services/theme_data/iss_deserializer.spec.js @@ -2,7 +2,7 @@ import { deserialize } from 'src/services/theme_data/iss_deserializer.js' import { serialize } from 'src/services/theme_data/iss_serializer.js' import Button from 'src/components/button.style.js' -describe.only('ISS (de)serialization', () => { +describe('ISS (de)serialization', () => { describe('ISS deserialization', () => { it('Output should equal input', () => { const normalized = Button.defaultRules.map(x => ({ component: 'Button', ...x })) From 1124ace7f62b7564bd85b66e618dfb3ed2e551e6 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Fri, 20 Sep 2024 11:19:16 +0300 Subject: [PATCH 07/10] lack of changelog --- changelog.d/piss-serialization.skip | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 changelog.d/piss-serialization.skip diff --git a/changelog.d/piss-serialization.skip b/changelog.d/piss-serialization.skip new file mode 100644 index 000000000..e69de29bb From 48f0a95a3bf53321d950f23e480607cf94349751 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Fri, 20 Sep 2024 12:50:05 +0300 Subject: [PATCH 08/10] more tests, fixed some issues --- src/components/alert.style.js | 4 ++- src/components/button_unstyled.style.js | 3 +- src/components/input.style.js | 2 +- src/components/panel_header.style.js | 1 - src/services/theme_data/iss_deserializer.js | 23 ++++++++---- src/services/theme_data/iss_serializer.js | 2 +- .../theme_data/iss_deserializer.spec.js | 36 +++++++++++++++---- 7 files changed, 53 insertions(+), 18 deletions(-) diff --git a/src/components/alert.style.js b/src/components/alert.style.js index 19bd4bbbf..abbeb5baa 100644 --- a/src/components/alert.style.js +++ b/src/components/alert.style.js @@ -27,7 +27,9 @@ export default { component: 'Alert' }, component: 'Border', - textColor: '--parent' + directives: { + textColor: '--parent' + } }, { variant: 'error', diff --git a/src/components/button_unstyled.style.js b/src/components/button_unstyled.style.js index 65b5c57bf..435f9cc67 100644 --- a/src/components/button_unstyled.style.js +++ b/src/components/button_unstyled.style.js @@ -16,8 +16,7 @@ export default { { directives: { background: '#ffffff', - opacity: 0, - shadow: [] + opacity: 0 } }, { diff --git a/src/components/input.style.js b/src/components/input.style.js index 139a0034f..7302cd6db 100644 --- a/src/components/input.style.js +++ b/src/components/input.style.js @@ -26,7 +26,7 @@ export default { { component: 'Root', directives: { - '--defaultInputBevel': 'shadow | $borderSide(#FFFFFF, bottom, 0.2)| $borderSide(#000000, top, 0.2)' + '--defaultInputBevel': 'shadow | $borderSide(#FFFFFF, bottom, 0.2), $borderSide(#000000, top, 0.2)' } }, { diff --git a/src/components/panel_header.style.js b/src/components/panel_header.style.js index 32464bc50..7c1457588 100644 --- a/src/components/panel_header.style.js +++ b/src/components/panel_header.style.js @@ -17,7 +17,6 @@ export default { directives: { backgroundNoCssColor: 'yes', background: '--fg', - shadow: [] } } ] diff --git a/src/services/theme_data/iss_deserializer.js b/src/services/theme_data/iss_deserializer.js index 8d4c987e8..5d71f35fa 100644 --- a/src/services/theme_data/iss_deserializer.js +++ b/src/services/theme_data/iss_deserializer.js @@ -6,13 +6,13 @@ const parseShadow = string => { // inset keyword (optional) '^(?:(inset)\\s+)?', // x - '(?:([0-9]+(?:\\.[0-9]+)?)\\s+)', + '(?:(-?[0-9]+(?:\\.[0-9]+)?)\\s+)', // y - '(?:([0-9]+(?:\\.[0-9]+)?)\\s+)', + '(?:(-?[0-9]+(?:\\.[0-9]+)?)\\s+)', // blur (optional) - '(?:([0-9]+(?:\\.[0-9]+)?)\\s+)?', + '(?:(-?[0-9]+(?:\\.[0-9]+)?)\\s+)?', // spread (optional) - '(?:([0-9]+(?:\\.[0-9]+)?)\\s+)?', + '(?:(-?[0-9]+(?:\\.[0-9]+)?)\\s+)?', // either hex, variable or function '(#[0-9a-f]{6}|--[a-z\\-_]+|\\$[a-z\\-()_]+)', // opacity (optional) @@ -23,7 +23,18 @@ const parseShadow = string => { if (result == null) { return string } else { - return Object.fromEntries(modes.map((mode, i) => [mode, result[i]])) + const numeric = new Set(['x', 'y', 'blur', 'spread', 'alpha']) + const { x, y, blur, spread, alpha, inset, color } = Object.fromEntries(modes.map((mode, i) => { + if (numeric.has(mode)) { + return [mode, Number(result[i])] + } else if (mode === 'inset') { + return [mode, !!result[i]] + } else { + return [mode, result[i]] + } + }).filter(([k, v]) => v !== false).slice(1)) + + return { x, y, blur, spread, color, alpha, inset } } } // this works nearly the same as HTML tree converter @@ -127,7 +138,7 @@ export const deserialize = (input) => { const [property, value] = d.split(':') let realValue = value.trim() if (property === 'shadow') { - realValue = parseShadow(value.split(',').map(v => v.trim())) + realValue = value.split(',').map(v => parseShadow(v.trim())) } if (!Number.isNaN(Number(value))) { realValue = Number(value) } diff --git a/src/services/theme_data/iss_serializer.js b/src/services/theme_data/iss_serializer.js index 6bba85e45..959852b7f 100644 --- a/src/services/theme_data/iss_serializer.js +++ b/src/services/theme_data/iss_serializer.js @@ -2,7 +2,7 @@ import { unroll } from './iss_utils.js' const serializeShadow = s => { if (typeof s === 'object') { - return `{${s.inset ? 'inset ' : ''}${s.x} ${s.y} ${s.blur} ${s.spread} ${s.color} / ${s.alpha}}` + return `${s.inset ? 'inset ' : ''}${s.x} ${s.y} ${s.blur} ${s.spread} ${s.color} / ${s.alpha}` } else { return s } diff --git a/test/unit/specs/services/theme_data/iss_deserializer.spec.js b/test/unit/specs/services/theme_data/iss_deserializer.spec.js index 7654fb53b..6141015c9 100644 --- a/test/unit/specs/services/theme_data/iss_deserializer.spec.js +++ b/test/unit/specs/services/theme_data/iss_deserializer.spec.js @@ -1,16 +1,40 @@ import { deserialize } from 'src/services/theme_data/iss_deserializer.js' import { serialize } from 'src/services/theme_data/iss_serializer.js' -import Button from 'src/components/button.style.js' +const componentsContext = require.context('src', true, /\.style.js(on)?$/) -describe('ISS (de)serialization', () => { - describe('ISS deserialization', () => { - it('Output should equal input', () => { - const normalized = Button.defaultRules.map(x => ({ component: 'Button', ...x })) +describe.only('ISS (de)serialization', () => { + componentsContext.keys().forEach(key => { + const component = componentsContext(key).default + + it(`(De)serialization of component ${component.name} works`, () => { + const normalized = component.defaultRules.map(x => ({ component: component.name, ...x })) const serialized = serialize(normalized) const deserialized = deserialize(serialized) // for some reason comparing objects directly fails the assert - expect(JSON.stringify(deserialized)).to.equal(JSON.stringify(normalized)) + expect(JSON.stringify(deserialized, null, 2)).to.equal(JSON.stringify(normalized, null, 2)) }) }) + + /* + // Debug snippet + const onlyComponent = componentsContext('./components/panel_header.style.js').default + it(`(De)serialization of component ${onlyComponent.name} works`, () => { + const normalized = onlyComponent.defaultRules.map(x => ({ component: onlyComponent.name, ...x })) + console.log('BEGIN INPUT ================') + console.log(normalized) + console.log('END INPUT ==================') + const serialized = serialize(normalized) + console.log('BEGIN SERIAL ===============') + console.log(serialized) + console.log('END SERIAL =================') + const deserialized = deserialize(serialized) + console.log('BEGIN DESERIALIZED =========') + console.log(serialized) + console.log('END DESERIALIZED ===========') + + // for some reason comparing objects directly fails the assert + expect(JSON.stringify(deserialized, null, 2)).to.equal(JSON.stringify(normalized, null, 2)) + }) + */ }) From b5da1f8b89ddbfcb7bb5ad0c53005ddd93c1ba48 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Fri, 20 Sep 2024 12:54:47 +0300 Subject: [PATCH 09/10] fix lint --- src/components/panel_header.style.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/panel_header.style.js b/src/components/panel_header.style.js index 7c1457588..010e42cd2 100644 --- a/src/components/panel_header.style.js +++ b/src/components/panel_header.style.js @@ -16,7 +16,7 @@ export default { component: 'PanelHeader', directives: { backgroundNoCssColor: 'yes', - background: '--fg', + background: '--fg' } } ] From 14328917f1a25770f90dc2181fad090ace4954da Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Fri, 20 Sep 2024 14:25:36 +0300 Subject: [PATCH 10/10] only --- test/unit/specs/services/theme_data/iss_deserializer.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/specs/services/theme_data/iss_deserializer.spec.js b/test/unit/specs/services/theme_data/iss_deserializer.spec.js index 6141015c9..01f8dacf1 100644 --- a/test/unit/specs/services/theme_data/iss_deserializer.spec.js +++ b/test/unit/specs/services/theme_data/iss_deserializer.spec.js @@ -2,7 +2,7 @@ import { deserialize } from 'src/services/theme_data/iss_deserializer.js' import { serialize } from 'src/services/theme_data/iss_serializer.js' const componentsContext = require.context('src', true, /\.style.js(on)?$/) -describe.only('ISS (de)serialization', () => { +describe('ISS (de)serialization', () => { componentsContext.keys().forEach(key => { const component = componentsContext(key).default