bluedither 1.0.15 → 1.0.17

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.
@@ -121,6 +121,9 @@ export default async function install(args) {
121
121
 
122
122
  console.log(`Copied ${copied} files to ${bdDir}`);
123
123
 
124
+ // Auto-wire Vite plugin if vite.config exists
125
+ autoWireVitePlugin(targetDir);
126
+
124
127
  // Generate Claude Code command file
125
128
  const claudeDir = resolve(targetDir, '.claude', 'commands');
126
129
  mkdirSync(claudeDir, { recursive: true });
@@ -145,14 +148,46 @@ $ARGUMENTS
145
148
  To apply the theme, use Claude Code:
146
149
  /apply-theme [description of your site]
147
150
 
148
- To customize visually, add this to your HTML:
149
- <script src="./bluedither/bluedither-tuner-inject.js"></script>
150
-
151
- For auto-saving, run in a separate terminal:
152
- npx bluedither tune --save-only
151
+ The tuner panel loads automatically in dev mode.
152
+ Click "Commit Changes" to save tweaks to tokens.json.
153
153
  `);
154
154
  }
155
155
 
156
+ function autoWireVitePlugin(targetDir) {
157
+ // Find vite.config file
158
+ const variants = ['vite.config.js', 'vite.config.ts', 'vite.config.mjs', 'vite.config.mts'];
159
+ let configPath = null;
160
+ for (const v of variants) {
161
+ const p = resolve(targetDir, v);
162
+ if (existsSync(p)) { configPath = p; break; }
163
+ }
164
+ if (!configPath) return;
165
+
166
+ const config = readFileSync(configPath, 'utf-8');
167
+
168
+ // Skip if already wired
169
+ if (config.includes('bluedither')) return;
170
+
171
+ // Add import and plugin
172
+ const importLine = `import { bluedither } from './bluedither/dev-middleware.js'\n`;
173
+ let updated = importLine + config;
174
+
175
+ // Add bluedither() to plugins array
176
+ updated = updated.replace(
177
+ /plugins:\s*\[([^\]]*)\]/,
178
+ (match, inner) => {
179
+ const trimmed = inner.trim();
180
+ if (trimmed.endsWith(',')) {
181
+ return `plugins: [${trimmed} bluedither()]`;
182
+ }
183
+ return `plugins: [${trimmed}, bluedither()]`;
184
+ }
185
+ );
186
+
187
+ writeFileSync(configPath, updated);
188
+ console.log(`Wired BlueDither plugin into ${basename(configPath)}`);
189
+ }
190
+
156
191
  async function installFromRegistry(slug, targetDir) {
157
192
  console.log(`\n Installing theme "${slug}" from marketplace...\n`);
158
193
 
@@ -221,6 +256,9 @@ async function installFromRegistry(slug, targetDir) {
221
256
  const { framework, typescript } = detectFramework(targetDir);
222
257
  console.log(` Detected framework: ${framework}${typescript ? ' + TypeScript' : ''}`);
223
258
 
259
+ // Auto-wire Vite plugin
260
+ autoWireVitePlugin(targetDir);
261
+
224
262
  const claudeDir = resolve(targetDir, '.claude', 'commands');
225
263
  mkdirSync(claudeDir, { recursive: true });
226
264
 
@@ -243,10 +281,7 @@ $ARGUMENTS
243
281
  To apply the theme, use Claude Code:
244
282
  /apply-theme [description of your site]
245
283
 
246
- To customize visually, add this to your HTML:
247
- <script src="./bluedither/bluedither-tuner-inject.js"><\/script>
248
-
249
- For auto-saving, run in a separate terminal:
250
- npx bluedither tune --save-only
284
+ The tuner panel loads automatically in dev mode.
285
+ Click "Commit Changes" to save tweaks to tokens.json.
251
286
  `);
252
287
  }
@@ -2,7 +2,7 @@
2
2
  * BlueDither Tuner — Injectable Loader
3
3
  *
4
4
  * Drop this script into ANY page to get the tuner overlay.
5
- * Reads tokens from bluedither/tokens.json, updates CSS vars live.
5
+ * On load: reads tokens.json, applies CSS vars + shader params (so saved changes persist).
6
6
  * "Commit Changes" saves via dev middleware → sidecar → File System API → download.
7
7
  *
8
8
  * Usage:
@@ -31,11 +31,102 @@
31
31
  defaults = structuredClone(tokens);
32
32
  }
33
33
 
34
+ // ── Apply saved tokens to the page ──
35
+ // This makes "Commit Changes" persist across page reloads.
36
+ const root = document.documentElement;
37
+ const t = tokens;
38
+
39
+ function pxToClamp(px) {
40
+ const dw = t.layout?.designWidth || 1364;
41
+ const maxRem = px / 16;
42
+ const vw = (px / dw) * 100;
43
+ const minRem = maxRem * 0.55;
44
+ return `clamp(${minRem.toFixed(4)}rem, ${vw.toFixed(4)}vw, ${maxRem.toFixed(4)}rem)`;
45
+ }
46
+
47
+ // Colors
48
+ if (t.colors) {
49
+ if (t.colors.background) root.style.setProperty('--bd-bg', t.colors.background);
50
+ if (t.colors.primary) root.style.setProperty('--bd-primary', t.colors.primary);
51
+ if (t.colors.text) root.style.setProperty('--bd-text', t.colors.text);
52
+ if (t.colors.ctaBackground) root.style.setProperty('--bd-cta-bg', t.colors.ctaBackground);
53
+ if (t.colors.ctaText) root.style.setProperty('--bd-cta-text', t.colors.ctaText);
54
+ }
55
+
56
+ // Typography
57
+ if (t.typography) {
58
+ if (t.typography.primaryFont) root.style.setProperty('--bd-font-primary', t.typography.primaryFont);
59
+ if (t.typography.secondaryFont) root.style.setProperty('--bd-font-secondary', t.typography.secondaryFont);
60
+ if (t.typography.headline) {
61
+ root.style.setProperty('--bd-headline-size', pxToClamp(t.typography.headline.referencePx));
62
+ root.style.setProperty('--bd-headline-lh', pxToClamp(t.typography.headline.lineHeightPx));
63
+ }
64
+ if (t.typography.subHeadline) {
65
+ root.style.setProperty('--bd-sub-size', pxToClamp(t.typography.subHeadline.referencePx));
66
+ root.style.setProperty('--bd-sub-lh', pxToClamp(t.typography.subHeadline.lineHeightPx));
67
+ }
68
+ if (t.typography.logo) {
69
+ root.style.setProperty('--bd-logo-size', pxToClamp(t.typography.logo.referencePx));
70
+ root.style.setProperty('--bd-logo-lh', pxToClamp(t.typography.logo.lineHeightPx));
71
+ }
72
+ if (t.typography.navItem) {
73
+ root.style.setProperty('--bd-nav-size', pxToClamp(t.typography.navItem.referencePx));
74
+ root.style.setProperty('--bd-nav-lh', pxToClamp(t.typography.navItem.lineHeightPx));
75
+ }
76
+ if (t.typography.ctaButton) {
77
+ root.style.setProperty('--bd-cta-size', pxToClamp(t.typography.ctaButton.referencePx));
78
+ root.style.setProperty('--bd-cta-lh', pxToClamp(t.typography.ctaButton.lineHeightPx));
79
+ }
80
+ }
81
+
82
+ // Spacing
83
+ if (t.spacing) {
84
+ if (t.spacing.headerPaddingX) root.style.setProperty('--bd-header-px', pxToClamp(t.spacing.headerPaddingX));
85
+ if (t.spacing.headerPaddingY) root.style.setProperty('--bd-header-py', pxToClamp(t.spacing.headerPaddingY));
86
+ if (t.spacing.heroPaddingTop) root.style.setProperty('--bd-hero-pt', pxToClamp(t.spacing.heroPaddingTop));
87
+ if (t.spacing.heroPaddingBottom) root.style.setProperty('--bd-hero-pb', pxToClamp(t.spacing.heroPaddingBottom));
88
+ if (t.spacing.heroPaddingX) root.style.setProperty('--bd-hero-px', pxToClamp(t.spacing.heroPaddingX));
89
+ if (t.spacing.navGap) root.style.setProperty('--bd-nav-gap', pxToClamp(t.spacing.navGap));
90
+ if (t.spacing.ctaPaddingX) root.style.setProperty('--bd-cta-px', pxToClamp(t.spacing.ctaPaddingX));
91
+ if (t.spacing.ctaPaddingY) root.style.setProperty('--bd-cta-py', pxToClamp(t.spacing.ctaPaddingY));
92
+ if (t.spacing.ctaBorderRadius != null) root.style.setProperty('--bd-cta-radius', `${(t.spacing.ctaBorderRadius / 16).toFixed(4)}rem`);
93
+ }
94
+
95
+ // Opacity
96
+ if (t.opacity) {
97
+ if (t.opacity.navLinks != null) root.style.setProperty('--bd-nav-opacity', t.opacity.navLinks);
98
+ }
99
+
100
+ // Shader — wait for __BD_SHADER__ to be available, then update
101
+ if (t.shader || t.colors) {
102
+ const applyShader = () => {
103
+ const shader = window.__BD_SHADER__;
104
+ if (!shader) return false;
105
+ const params = {};
106
+ if (t.colors?.shaderFront) params.colorFront = t.colors.shaderFront;
107
+ if (t.colors?.shaderBack) params.colorBack = t.colors.shaderBack;
108
+ if (t.shader?.shape) params.shape = t.shader.shape;
109
+ if (t.shader?.type) params.type = t.shader.type;
110
+ if (t.shader?.speed != null) params.speed = t.shader.speed;
111
+ if (t.shader?.scale != null) params.scale = t.shader.scale;
112
+ if (t.shader?.size != null) params.size = t.shader.size;
113
+ if (t.shader?.rotation != null) params.rotation = t.shader.rotation;
114
+ shader.updateParams(params);
115
+ return true;
116
+ };
117
+
118
+ // Try immediately, then poll briefly (shader may init after this script)
119
+ if (!applyShader()) {
120
+ let attempts = 0;
121
+ const interval = setInterval(() => {
122
+ if (applyShader() || ++attempts > 20) clearInterval(interval);
123
+ }, 100);
124
+ }
125
+ }
126
+
34
127
  // Inject into globals for the tuner
35
128
  window.__BD_TOKENS__ = tokens;
36
129
  window.__BD_DEFAULTS__ = defaults;
37
-
38
- // Signal that we're in injectable mode (tuner will use commitTokens cascade)
39
130
  window.__BD_TUNER_SERVER_MODE__ = true;
40
131
 
41
132
  // Load tuner CSS
@@ -47,9 +138,7 @@
47
138
  style.textContent = css;
48
139
  document.head.appendChild(style);
49
140
  }
50
- } catch {
51
- // CSS will be inlined in the bundled version
52
- }
141
+ } catch {}
53
142
 
54
143
  // Load and execute tuner script
55
144
  const tunerScript = document.createElement('script');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bluedither",
3
- "version": "1.0.15",
3
+ "version": "1.0.17",
4
4
  "description": "A bold, dithered-shader hero theme for Claude Code — skill + fine-tuner",
5
5
  "type": "module",
6
6
  "bin": {