Skip to content

Commit 0205c35

Browse files
committed
v2.2.3 - Fix YAML/preview parity (show_name/icon, perform-action, bg opacity, aspect-ratio, color:auto, icon spin)
1 parent 1ed9ee1 commit 0205c35

6 files changed

Lines changed: 126 additions & 106 deletions

File tree

custom_components/button_builder/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,5 @@
1616

1717
],
1818
"single_config_entry": true,
19-
"version": "2.2.2"
19+
"version": "2.2.3"
2020
}

custom_components/button_builder/www/index.js

Lines changed: 86 additions & 84 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "button-builder",
33
"private": true,
4-
"version": "2.2.2",
4+
"version": "2.2.3",
55
"type": "module",
66
"scripts": {
77
"dev": "vite",

src/components/PreviewCard.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -743,13 +743,14 @@ export const PreviewCard: React.FC<Props> = ({ config, simulatedState, onSimulat
743743
const stateIconAnimation = matchingStateStyle?.iconAnimation;
744744
const stateIconAnimationSpeed = matchingStateStyle?.iconAnimationSpeed;
745745

746-
// Use state-specific animation if it exists and is not 'none', otherwise use config animation
746+
// Use state-specific animation if it exists and is not 'none', otherwise use config animation.
747+
// config.spin (legacy "Icon Spin" checkbox) maps to the 'spin' animation type.
747748
const effectiveIconAnimation = (stateIconAnimation && stateIconAnimation !== 'none')
748749
? stateIconAnimation
749-
: config.iconAnimation;
750+
: (config.spin && config.iconAnimation === 'none' ? 'spin' : config.iconAnimation);
750751
const effectiveIconAnimationTrigger = (stateIconAnimation && stateIconAnimation !== 'none')
751752
? 'always'
752-
: config.iconAnimationTrigger;
753+
: (config.spin ? 'always' : config.iconAnimationTrigger);
753754

754755
// Icon animations: Always apply when selected (alwaysAnimateIcon checkbox overrides state logic)
755756
const shouldShowIconAnimation = config.alwaysAnimateIcon ||
@@ -760,7 +761,10 @@ export const PreviewCard: React.FC<Props> = ({ config, simulatedState, onSimulat
760761
const iconAnimationClass = (effectiveIconAnimation !== 'none' && shouldShowIconAnimation)
761762
? `cba-animate-${effectiveIconAnimation}`
762763
: '';
763-
const iconAnimationDuration = stateIconAnimationSpeed || config.iconAnimationSpeed || '2s';
764+
// Use spinDuration for the legacy spin property, otherwise use animation speed
765+
const iconAnimationDuration = config.spin && config.iconAnimation === 'none'
766+
? (config.spinDuration || '2s')
767+
: (stateIconAnimationSpeed || config.iconAnimationSpeed || '2s');
764768

765769
// Marquee Logic: If marquee is active and valid
766770
const isMarquee = effectiveCardAnimation === 'marquee' &&

src/utils/yamlGenerator.ts

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -178,13 +178,19 @@ const generateActionYaml = (
178178
): string => {
179179
let yaml = ` action: ${actionType}\n`;
180180

181-
if (actionType === 'call-service' && actionData) {
181+
if ((actionType === 'call-service' || actionType === 'perform-action') && actionData) {
182182
try {
183183
const data = JSON.parse(actionData);
184-
yaml += ` service: ${data.service || ''}\n`;
185-
if (data.service_data) {
186-
yaml += ` service_data:\n`;
187-
Object.entries(data.service_data).forEach(([key, value]) => {
184+
// perform-action uses perform_action key; call-service uses service key
185+
if (actionType === 'perform-action') {
186+
yaml += ` perform_action: ${data.service || data.perform_action || ''}\n`;
187+
} else {
188+
yaml += ` service: ${data.service || ''}\n`;
189+
}
190+
if (data.service_data || data.data) {
191+
const serviceData = data.service_data || data.data;
192+
yaml += actionType === 'perform-action' ? ` data:\n` : ` service_data:\n`;
193+
Object.entries(serviceData).forEach(([key, value]) => {
188194
yaml += ` ${key}: ${JSON.stringify(value)}\n`;
189195
});
190196
}
@@ -276,12 +282,13 @@ export const generateYaml = (config: ButtonConfig): string => {
276282
// --- Base Card Styles ---
277283
const cardStyles = [
278284
config.height !== 'auto' ? `height: ${config.height}` : null,
279-
config.aspectRatio ? `aspect-ratio: ${config.aspectRatio}` : null,
285+
// aspect-ratio is already emitted as a top-level YAML property; omit from styles.card
280286
gradientCSS ? gradientCSS : (bgColor ? `background-color: ${bgColor}` : null),
281287
config.cardOpacity < 100 ? `opacity: ${config.cardOpacity / 100}` : null,
282288
`border-radius: ${config.borderRadius}`,
283289
`padding: ${config.padding}`,
284-
`color: ${config.color}`,
290+
// Only emit color in styles.card when not using color:auto (which is a top-level override)
291+
!config.colorAuto ? `color: ${config.color}` : null,
285292
// Use custom font if specified, otherwise use fontFamily dropdown
286293
(config.customFontName && config.customFontUrl)
287294
? `font-family: '${config.customFontName}', sans-serif`
@@ -346,7 +353,8 @@ export const generateYaml = (config: ButtonConfig): string => {
346353
if (config.nameTemplate) {
347354
yaml += `name: ${config.nameTemplate}\n`;
348355
} else if (config.name) {
349-
yaml += `name: ${config.name}\n`;
356+
// Quote the name to handle colons, hashes and other YAML special characters
357+
yaml += `name: "${config.name.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"\n`;
350358
}
351359

352360
if (config.iconTemplate) {
@@ -407,9 +415,9 @@ export const generateYaml = (config: ButtonConfig): string => {
407415
const showState = hasEntity ? config.showState : false;
408416
const showLastChanged = hasEntity ? config.showLastChanged : false;
409417

410-
// Only output show options that are true
411-
if (config.showName) yaml += `show_name: true\n`;
412-
if (config.showIcon) yaml += `show_icon: true\n`;
418+
// show_name/show_icon default to true in button-card — only emit when overriding to false
419+
if (!config.showName) yaml += `show_name: false\n`;
420+
if (!config.showIcon) yaml += `show_icon: false\n`;
413421
if (showState) yaml += `show_state: true\n`;
414422
if (showLabel) yaml += `show_label: true\n`;
415423
if (showEntityPicture) yaml += `show_entity_picture: true\n`;
@@ -1334,7 +1342,10 @@ ${stateIconStyles.map(s => ` - ${formatStyleForYaml(s)}`).join('\n')}` :
13341342
conditionalCardStyles.push(gradientBg);
13351343
}
13361344
} else if (stateStyle.backgroundColor) {
1337-
conditionalCardStyles.push(`background-color: ${stateStyle.backgroundColor}`);
1345+
// Apply backgroundColorOpacity if set to something other than full opacity
1346+
const bgOpacity = stateStyle.backgroundColorOpacity !== undefined ? stateStyle.backgroundColorOpacity : 100;
1347+
const bgValue = bgOpacity < 100 ? hexToRgba(stateStyle.backgroundColor, bgOpacity) : stateStyle.backgroundColor;
1348+
conditionalCardStyles.push(`background-color: ${bgValue}`);
13381349
}
13391350
if (stateStyle.borderColor) {
13401351
conditionalCardStyles.push(`border-color: ${stateStyle.borderColor}`);

src/utils/yamlImporter.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -439,11 +439,14 @@ function parseAction(
439439
cfg[configPrefix] = action.action;
440440
}
441441

442-
// Service data
443-
if (action.service || action.service_data || action.target) {
442+
// Service data - handle both call-service (service/service_data) and perform-action (perform_action/data) formats
443+
if (action.service || action.service_data || action.target || action.perform_action || action.data) {
444444
const serviceData: any = {};
445-
if (action.service) serviceData.service = action.service;
446-
if (action.service_data) serviceData.service_data = action.service_data;
445+
// Support both old (call-service: service key) and new (perform-action: perform_action key)
446+
const serviceName = action.service || action.perform_action;
447+
if (serviceName) serviceData.service = serviceName;
448+
const dataObj = action.service_data || action.data;
449+
if (dataObj) serviceData.service_data = dataObj;
447450
if (action.target) serviceData.target = action.target;
448451
cfg[`${configPrefix}Data`] = JSON.stringify(serviceData, null, 2);
449452
}

0 commit comments

Comments
 (0)