Skip to content

Commit f12665e

Browse files
committed
Release v2.0.0-beta: Major UI improvements and bubble card builder
- Added gradient background support with opacity control for state appearances - Implemented bubble card builder with comprehensive presets - Added weather icons to bubble card preview - Removed duplicate ripple effect toggle from main UI - Enhanced YAML generation with gradient support - Fixed state conditional rendering - Improved icon rendering in bubble card preview - Various bug fixes and UI improvements
1 parent b0464a5 commit f12665e

31 files changed

+16795
-1115
lines changed

.github/copilot-instructions.md

Lines changed: 13 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -1,122 +1,13 @@
1-
# Button Builder - AI Agent Instructions
2-
3-
## Project Overview
4-
Button Builder is a **Home Assistant custom integration** that provides a visual UI for designing `custom:button-card` YAML configurations. It's a React/TypeScript frontend embedded in HA via iframe panel.
5-
6-
## Architecture
7-
8-
### Dual-Layer Structure
9-
1. **Frontend (React/Vite)**: `src/App.tsx`, `src/components/`, `src/services/`, `src/utils/`
10-
2. **HA Integration (Python)**: `custom_components/button_builder/` - registers sidebar panel
11-
12-
### Key Data Flow
13-
```
14-
User Input → ConfigPanel → ButtonConfig state → yamlGenerator.ts → YAML Output
15-
16-
PreviewCard (live preview)
17-
```
18-
19-
### Critical Files
20-
- **`src/types.ts`**: `ButtonConfig` interface (~100+ properties) - the single source of truth
21-
- **`src/ButtonCardApp.tsx`**: Main state management, preset handling, import/export
22-
- **`src/utils/yamlGenerator.ts`**: Converts `ButtonConfig` → button-card YAML (1300+ lines)
23-
- **`src/presets.ts`**: 80+ style presets with `Partial<ButtonConfig>` configs
24-
- **`src/services/geminiService.ts`**: AI generation with structured schema output
25-
26-
## Project Structure
27-
```
28-
button-builder/
29-
├── src/ # Source code
30-
│ ├── components/ # React components
31-
│ ├── services/ # API services
32-
│ ├── utils/ # Utility functions
33-
│ ├── bubble-card/ # Bubble Card builder (beta)
34-
│ └── assets/ # Images & assets
35-
├── docs/ # Documentation
36-
├── scripts/ # Build & deploy scripts
37-
├── wiki/ # GitHub wiki pages
38-
├── custom_components/ # Home Assistant integration
39-
│ └── button_builder/
40-
│ └── www/ # Built frontend files
41-
└── brands_submission/ # HACS brand assets
42-
```
43-
44-
## Development Workflow
45-
46-
### Commands
47-
```bash
48-
npm run dev # Local dev server at localhost:3000
49-
npm run build # Build to custom_components/button_builder/www/
50-
```
51-
52-
### Version Bump Checklist (CRITICAL)
53-
When releasing, update version in **BOTH**:
54-
1. `package.json``"version"`
55-
2. `custom_components/button_builder/manifest.json``"version"`
56-
57-
Cache busting uses `manifest.json` version + timestamp, so mismatched versions cause stale cache issues.
58-
59-
### Release Process
60-
```bash
61-
npm run build
62-
git add -A && git commit -m "message"
63-
git tag vX.Y.Z && git push origin main --tags
64-
gh release create vX.Y.Z --title "vX.Y.Z" --notes "..." --latest
65-
```
66-
67-
## Code Patterns
68-
69-
### State Management
70-
- `isApplyingPresetRef` uses `useRef` (not `useState`) to avoid React batching race conditions
71-
- `setConfig` wrapper auto-clears `activePreset` when user modifies config manually
72-
- Config persists to `localStorage` with key `button-builder-config`
73-
74-
### Adding New ButtonConfig Properties
75-
1. Add to `ButtonConfig` interface in `src/types.ts`
76-
2. Add default value to `DEFAULT_CONFIG` in `src/types.ts`
77-
3. Add UI control in `src/components/ConfigPanel.tsx`
78-
4. Handle in `src/utils/yamlGenerator.ts` to output correct YAML
79-
5. Add to AI schema in `src/services/geminiService.ts` if AI should generate it
80-
81-
### Preset Structure
82-
```typescript
83-
// src/presets.ts
84-
{
85-
name: 'Preset Name',
86-
description: 'Short description',
87-
category: 'minimal' | 'glass' | 'neon' | 'gradient' | 'animated' | '3d' | 'cyberpunk' | 'retro' | 'nature' | 'icon-styles' | 'custom',
88-
config: Partial<ButtonConfig> // Only override properties, rest use defaults
89-
}
90-
```
91-
92-
### YAML Generation Pattern
93-
```typescript
94-
// yamlGenerator.ts - only output non-default values
95-
if (config.propertyName !== DEFAULT_CONFIG.propertyName) {
96-
yaml += `property_name: ${config.propertyName}\n`;
97-
}
98-
```
99-
100-
## Common Pitfalls
101-
102-
1. **Removing props/state**: Search ALL `.tsx` files for references before removing - leftover references cause runtime crashes (not caught by build)
103-
2. **HA caching**: After HACS update, users need: Restart HA + hard refresh browser (Ctrl+Shift+R)
104-
3. **Panel registration**: Uses 2-second delay in `__init__.py` to ensure HA frontend is ready
105-
4. **Tailwind in production**: Uses CDN (`cdn.tailwindcss.com`) - expected console warning
106-
107-
## Testing in Home Assistant
108-
109-
The panel runs inside HA's iframe sandbox. To test locally with HA APIs:
110-
- Panel URL: `/button_builder/panel.html?v={version}`
111-
- Static assets served from `/button_builder/`
112-
- Auth token read from `localStorage.hassTokens`
113-
114-
## File Relationships
115-
```
116-
ButtonConfig (src/types.ts)
117-
├── src/components/ConfigPanel.tsx (UI inputs)
118-
├── src/components/PreviewCard.tsx (live preview)
119-
├── src/utils/yamlGenerator.ts (YAML output)
120-
├── src/services/geminiService.ts (AI schema)
121-
└── src/presets.ts (preset configs)
122-
```
1+
# Button Builder – AI Agent Guide
2+
- What this is: Home Assistant custom integration plus a React/Vite UI to design `custom:button-card` YAML (and a beta bubble-card builder). The panel is served from `custom_components/button_builder/www/` into an HA iframe registered in `custom_components/button_builder/__init__.py` (2s delayed setup).
3+
- Frontend map: Main entry `src/ButtonCardApp.tsx`, shell `src/App.tsx`, UI pieces under `src/components/`, YAML logic in `src/utils/yamlGenerator.ts`, presets in `src/presets.ts`, services in `src/services/`. Bubble-card has a parallel stack in `src/bubble-card/` (`BubbleCardApp`, `components/Preview.tsx`, `utils/yamlGenerator.ts`).
4+
- State/data flow: User input in `components/ConfigPanel.tsx` updates `ButtonConfig` state (held in `ButtonCardApp.tsx`) → `utils/yamlGenerator.ts` emits YAML (only non-defaults) → live preview `components/PreviewCard.tsx` and YAML viewer. Bubble-card mirrors this with its own types and generator.
5+
- Canonical schema: `src/types.ts` defines `ButtonConfig` + `DEFAULT_CONFIG`. When adding a field, update `DEFAULT_CONFIG`, expose controls in `components/ConfigPanel.tsx`, render/preview as needed, emit via `utils/yamlGenerator.ts`, include in presets (`src/presets.ts`) and AI schema (`services/geminiService.ts`). Bubble-card equivalents live in `src/bubble-card/types.ts` and friends.
6+
- Presets: ~80 presets in `src/presets.ts` (bubble-card has its own). Application uses `isApplyingPresetRef` to avoid React batching glitches; manual edits clear `activePreset` through the `setConfig` wrapper.
7+
- Persistence/import: Button config persists to `localStorage` key `button-builder-config`; Gemini key lives only in-browser. YAML import path: `utils/yamlImporter.ts` maps button-card options back into `ButtonConfig`.
8+
- Services: `services/geminiService.ts` uses `@google/genai` with a structured schema returning `ButtonConfig`. `homeAssistantService.ts` and `dashboardService.ts` wrap HA APIs; auth tokens expected in `localStorage.hassTokens`.
9+
- Styling/assets: Lucide + MDI icons; Tailwind injected via CDN in production (console warning is normal). Core styles `src/index.css`; HTML entry `src/index.html`. Keep ASCII; existing assets in `src/assets/`.
10+
- Build/dev: `npm run dev` (Vite on :3000), `npm run build` outputs to `custom_components/button_builder/www/`, `npm run preview` to inspect the build. Windows scripts in `scripts/*.ps1` wrap build/deploy steps.
11+
- Deploy/HA: Served in HA at `/button_builder/panel.html?v={version}`. Keep versions in `package.json` and `custom_components/button_builder/manifest.json` in lockstep for cache busting; HA caches aggressively—rebuild, copy to HA, restart HA, hard-refresh browser.
12+
- Patterns to preserve: Do not remove props/state without updating all `.tsx` call sites. Keep schema aligned between `DEFAULT_CONFIG` and both YAML generators (button + bubble) to avoid missing YAML. Presets/AI schema must track new fields.
13+
- Files to know: `src/components/` (UI controls + preview + MagicBuilder), `src/utils/yamlGenerator.ts` and `src/utils/yamlImporter.ts`, `src/services/` (Gemini/HA APIs), `src/presets.ts`, `custom_components/button_builder/__init__.py` (panel registration) and `manifest.json` (versioning), `custom_components/button_builder/www/` (built assets, do not hand-edit).

App.tsx

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// Main App - Card Type Selector Wrapper
2+
// Allows switching between Button Card Builder and Bubble Card Builder
3+
4+
import React, { useState, useEffect } from 'react';
5+
import { ButtonCardApp } from './ButtonCardApp';
6+
import { BubbleCardApp } from './bubble-card/BubbleCardApp';
7+
import { Layers } from 'lucide-react';
8+
9+
type CardType = 'button-card' | 'bubble-card';
10+
11+
const CARD_TYPE_STORAGE_KEY = 'button-builder-card-type';
12+
13+
const loadSavedCardType = (): CardType => {
14+
try {
15+
const saved = localStorage.getItem(CARD_TYPE_STORAGE_KEY);
16+
if (saved === 'button-card' || saved === 'bubble-card') {
17+
return saved;
18+
}
19+
} catch (e) {
20+
console.warn('Failed to load saved card type:', e);
21+
}
22+
return 'button-card';
23+
};
24+
25+
const App: React.FC = () => {
26+
const [cardType, setCardType] = useState<CardType>(loadSavedCardType);
27+
28+
useEffect(() => {
29+
try {
30+
localStorage.setItem(CARD_TYPE_STORAGE_KEY, cardType);
31+
} catch (e) {
32+
console.warn('Failed to save card type:', e);
33+
}
34+
}, [cardType]);
35+
36+
return (
37+
<div className="h-screen w-screen flex flex-col bg-black text-white overflow-hidden">
38+
{/* Card Type Selector Header */}
39+
<div className="shrink-0 h-10 bg-gray-950 border-b border-gray-800 flex items-center justify-center gap-1 px-4">
40+
<div className="flex items-center gap-2 mr-4">
41+
<Layers size={16} className="text-gray-500" />
42+
<span className="text-xs font-medium text-gray-400 uppercase tracking-wider hidden sm:inline">Card Type</span>
43+
</div>
44+
<div className="flex bg-gray-900 rounded-lg p-0.5 border border-gray-800">
45+
<button
46+
onClick={() => setCardType('button-card')}
47+
className={`px-3 py-1.5 text-xs font-medium rounded-md transition-all ${
48+
cardType === 'button-card'
49+
? 'bg-blue-600 text-white shadow-lg'
50+
: 'text-gray-400 hover:text-white hover:bg-gray-800'
51+
}`}
52+
>
53+
<span className="hidden sm:inline">custom:</span>button-card
54+
</button>
55+
<button
56+
onClick={() => setCardType('bubble-card')}
57+
className={`px-3 py-1.5 text-xs font-medium rounded-md transition-all flex items-center gap-1.5 ${
58+
cardType === 'bubble-card'
59+
? 'bg-purple-600 text-white shadow-lg'
60+
: 'text-gray-400 hover:text-white hover:bg-gray-800'
61+
}`}
62+
>
63+
<span><span className="hidden sm:inline">custom:</span>bubble-card</span>
64+
<span className="text-[9px] px-1 py-0.5 rounded bg-amber-500/80 text-amber-950 font-bold uppercase">Beta</span>
65+
</button>
66+
</div>
67+
</div>
68+
69+
{/* Selected Card Builder */}
70+
<div className="flex-1 min-h-0 overflow-hidden">
71+
{cardType === 'button-card' ? (
72+
<ButtonCardApp />
73+
) : (
74+
<BubbleCardApp />
75+
)}
76+
</div>
77+
</div>
78+
);
79+
};
80+
81+
export default App;

0 commit comments

Comments
 (0)