@newtonedev/configurator 0.1.0

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 (62) hide show
  1. package/dist/Configurator.d.ts +13 -0
  2. package/dist/Configurator.d.ts.map +1 -0
  3. package/dist/Configurator.types.d.ts +13 -0
  4. package/dist/Configurator.types.d.ts.map +1 -0
  5. package/dist/bridge/toCSS.d.ts +7 -0
  6. package/dist/bridge/toCSS.d.ts.map +1 -0
  7. package/dist/bridge/toJSON.d.ts +15 -0
  8. package/dist/bridge/toJSON.d.ts.map +1 -0
  9. package/dist/bridge/toThemeConfig.d.ts +8 -0
  10. package/dist/bridge/toThemeConfig.d.ts.map +1 -0
  11. package/dist/constants.d.ts +16 -0
  12. package/dist/constants.d.ts.map +1 -0
  13. package/dist/hooks/useConfigurator.d.ts +11 -0
  14. package/dist/hooks/useConfigurator.d.ts.map +1 -0
  15. package/dist/hooks/usePreviewColors.d.ts +8 -0
  16. package/dist/hooks/usePreviewColors.d.ts.map +1 -0
  17. package/dist/hue-conversion.d.ts +10 -0
  18. package/dist/hue-conversion.d.ts.map +1 -0
  19. package/dist/index.cjs +827 -0
  20. package/dist/index.cjs.map +1 -0
  21. package/dist/index.d.ts +14 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js +814 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/panels/CanvasPanel.d.ts +8 -0
  26. package/dist/panels/CanvasPanel.d.ts.map +1 -0
  27. package/dist/panels/ExportPanel.d.ts +8 -0
  28. package/dist/panels/ExportPanel.d.ts.map +1 -0
  29. package/dist/panels/GlobalPanel.d.ts +10 -0
  30. package/dist/panels/GlobalPanel.d.ts.map +1 -0
  31. package/dist/panels/PalettePanel.d.ts +13 -0
  32. package/dist/panels/PalettePanel.d.ts.map +1 -0
  33. package/dist/panels/PreviewPanel.d.ts +12 -0
  34. package/dist/panels/PreviewPanel.d.ts.map +1 -0
  35. package/dist/state/actions.d.ts +62 -0
  36. package/dist/state/actions.d.ts.map +1 -0
  37. package/dist/state/defaults.d.ts +9 -0
  38. package/dist/state/defaults.d.ts.map +1 -0
  39. package/dist/state/reducer.d.ts +4 -0
  40. package/dist/state/reducer.d.ts.map +1 -0
  41. package/dist/types.d.ts +35 -0
  42. package/dist/types.d.ts.map +1 -0
  43. package/package.json +56 -0
  44. package/src/Configurator.tsx +177 -0
  45. package/src/Configurator.types.ts +13 -0
  46. package/src/bridge/toCSS.ts +43 -0
  47. package/src/bridge/toJSON.ts +24 -0
  48. package/src/bridge/toThemeConfig.ts +58 -0
  49. package/src/constants.ts +16 -0
  50. package/src/hooks/useConfigurator.ts +30 -0
  51. package/src/hooks/usePreviewColors.ts +40 -0
  52. package/src/hue-conversion.ts +25 -0
  53. package/src/index.ts +24 -0
  54. package/src/panels/CanvasPanel.tsx +90 -0
  55. package/src/panels/ExportPanel.tsx +95 -0
  56. package/src/panels/GlobalPanel.tsx +126 -0
  57. package/src/panels/PalettePanel.tsx +145 -0
  58. package/src/panels/PreviewPanel.tsx +124 -0
  59. package/src/state/actions.ts +27 -0
  60. package/src/state/defaults.ts +74 -0
  61. package/src/state/reducer.ts +163 -0
  62. package/src/types.ts +37 -0
package/dist/index.cjs ADDED
@@ -0,0 +1,827 @@
1
+ 'use strict';
2
+
3
+ var React3 = require('react');
4
+ var reactNative = require('react-native');
5
+ var components = require('@newtonedev/components');
6
+ var newtone = require('newtone');
7
+
8
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
+
10
+ var React3__default = /*#__PURE__*/_interopDefault(React3);
11
+
12
+ // src/Configurator.tsx
13
+
14
+ // src/state/defaults.ts
15
+ var DEFAULT_CONFIGURATOR_STATE = {
16
+ palettes: [
17
+ {
18
+ name: "Neutral",
19
+ hue: 220,
20
+ saturation: 24,
21
+ desaturationStrength: "none",
22
+ desaturationDirection: "light",
23
+ hueGradeStrength: "none",
24
+ hueGradeHue: 0,
25
+ hueGradeDirection: "light"
26
+ },
27
+ {
28
+ name: "Accent",
29
+ hue: 24,
30
+ saturation: 80,
31
+ desaturationStrength: "none",
32
+ desaturationDirection: "light",
33
+ hueGradeStrength: "none",
34
+ hueGradeHue: 0,
35
+ hueGradeDirection: "light"
36
+ },
37
+ {
38
+ name: "Success",
39
+ hue: 145,
40
+ saturation: 64,
41
+ desaturationStrength: "none",
42
+ desaturationDirection: "light",
43
+ hueGradeStrength: "none",
44
+ hueGradeHue: 0,
45
+ hueGradeDirection: "light"
46
+ },
47
+ {
48
+ name: "Warning",
49
+ hue: 40,
50
+ saturation: 80,
51
+ desaturationStrength: "none",
52
+ desaturationDirection: "light",
53
+ hueGradeStrength: "none",
54
+ hueGradeHue: 0,
55
+ hueGradeDirection: "light"
56
+ },
57
+ {
58
+ name: "Error",
59
+ hue: 0,
60
+ saturation: 80,
61
+ desaturationStrength: "none",
62
+ desaturationDirection: "light",
63
+ hueGradeStrength: "none",
64
+ hueGradeHue: 0,
65
+ hueGradeDirection: "light"
66
+ }
67
+ ],
68
+ dynamicRange: {
69
+ lightest: 1,
70
+ darkest: 1
71
+ },
72
+ globalHueGrading: {
73
+ light: { strength: "none", hue: 30 },
74
+ dark: { strength: "none", hue: 220 }
75
+ },
76
+ preview: {
77
+ mode: "light",
78
+ theme: "neutral"
79
+ }
80
+ };
81
+
82
+ // src/state/reducer.ts
83
+ function updatePalette(palettes, index, update) {
84
+ if (index < 0 || index >= palettes.length) return palettes;
85
+ return palettes.map((p, i) => i === index ? { ...p, ...update } : p);
86
+ }
87
+ function clamp(value, min, max) {
88
+ return Math.max(min, Math.min(max, value));
89
+ }
90
+ function wrapHue(hue) {
91
+ return (hue % 360 + 360) % 360;
92
+ }
93
+ function configuratorReducer(state, action) {
94
+ switch (action.type) {
95
+ // Palette actions
96
+ case "SET_PALETTE_HUE":
97
+ return {
98
+ ...state,
99
+ palettes: updatePalette(state.palettes, action.index, {
100
+ hue: wrapHue(action.hue)
101
+ })
102
+ };
103
+ case "SET_PALETTE_SATURATION":
104
+ return {
105
+ ...state,
106
+ palettes: updatePalette(state.palettes, action.index, {
107
+ saturation: clamp(action.saturation, 0, 100)
108
+ })
109
+ };
110
+ case "SET_PALETTE_DESAT_STRENGTH":
111
+ return {
112
+ ...state,
113
+ palettes: updatePalette(state.palettes, action.index, {
114
+ desaturationStrength: action.strength
115
+ })
116
+ };
117
+ case "SET_PALETTE_DESAT_DIRECTION":
118
+ return {
119
+ ...state,
120
+ palettes: updatePalette(state.palettes, action.index, {
121
+ desaturationDirection: action.direction
122
+ })
123
+ };
124
+ case "SET_PALETTE_HUE_GRADE_STRENGTH":
125
+ return {
126
+ ...state,
127
+ palettes: updatePalette(state.palettes, action.index, {
128
+ hueGradeStrength: action.strength
129
+ })
130
+ };
131
+ case "SET_PALETTE_HUE_GRADE_HUE":
132
+ return {
133
+ ...state,
134
+ palettes: updatePalette(state.palettes, action.index, {
135
+ hueGradeHue: wrapHue(action.hue)
136
+ })
137
+ };
138
+ case "SET_PALETTE_HUE_GRADE_DIRECTION":
139
+ return {
140
+ ...state,
141
+ palettes: updatePalette(state.palettes, action.index, {
142
+ hueGradeDirection: action.direction
143
+ })
144
+ };
145
+ // Dynamic range actions
146
+ case "SET_LIGHTEST":
147
+ return {
148
+ ...state,
149
+ dynamicRange: {
150
+ ...state.dynamicRange,
151
+ lightest: clamp(action.value, 0, 1)
152
+ }
153
+ };
154
+ case "SET_DARKEST":
155
+ return {
156
+ ...state,
157
+ dynamicRange: {
158
+ ...state.dynamicRange,
159
+ darkest: clamp(action.value, 0, 1)
160
+ }
161
+ };
162
+ // Global hue grading actions
163
+ case "SET_GLOBAL_GRADE_LIGHT_STRENGTH":
164
+ return {
165
+ ...state,
166
+ globalHueGrading: {
167
+ ...state.globalHueGrading,
168
+ light: { ...state.globalHueGrading.light, strength: action.strength }
169
+ }
170
+ };
171
+ case "SET_GLOBAL_GRADE_LIGHT_HUE":
172
+ return {
173
+ ...state,
174
+ globalHueGrading: {
175
+ ...state.globalHueGrading,
176
+ light: { ...state.globalHueGrading.light, hue: wrapHue(action.hue) }
177
+ }
178
+ };
179
+ case "SET_GLOBAL_GRADE_DARK_STRENGTH":
180
+ return {
181
+ ...state,
182
+ globalHueGrading: {
183
+ ...state.globalHueGrading,
184
+ dark: { ...state.globalHueGrading.dark, strength: action.strength }
185
+ }
186
+ };
187
+ case "SET_GLOBAL_GRADE_DARK_HUE":
188
+ return {
189
+ ...state,
190
+ globalHueGrading: {
191
+ ...state.globalHueGrading,
192
+ dark: { ...state.globalHueGrading.dark, hue: wrapHue(action.hue) }
193
+ }
194
+ };
195
+ // Preview actions
196
+ case "SET_PREVIEW_MODE":
197
+ return {
198
+ ...state,
199
+ preview: { ...state.preview, mode: action.mode }
200
+ };
201
+ case "SET_PREVIEW_THEME":
202
+ return {
203
+ ...state,
204
+ preview: { ...state.preview, theme: action.theme }
205
+ };
206
+ // Control actions
207
+ case "RESET":
208
+ return DEFAULT_CONFIGURATOR_STATE;
209
+ case "LOAD_STATE":
210
+ return action.state;
211
+ default:
212
+ return state;
213
+ }
214
+ }
215
+ function traditionalHueToOklch(hue) {
216
+ const h = (hue % 360 + 360) % 360;
217
+ const x = 1 - Math.abs(h / 60 % 2 - 1);
218
+ let r, g, b;
219
+ if (h < 60) {
220
+ r = 1;
221
+ g = x;
222
+ b = 0;
223
+ } else if (h < 120) {
224
+ r = x;
225
+ g = 1;
226
+ b = 0;
227
+ } else if (h < 180) {
228
+ r = 0;
229
+ g = 1;
230
+ b = x;
231
+ } else if (h < 240) {
232
+ r = 0;
233
+ g = x;
234
+ b = 1;
235
+ } else if (h < 300) {
236
+ r = x;
237
+ g = 0;
238
+ b = 1;
239
+ } else {
240
+ r = 1;
241
+ g = 0;
242
+ b = x;
243
+ }
244
+ return newtone.srgbToOklch({ r, g, b }).h;
245
+ }
246
+
247
+ // src/bridge/toThemeConfig.ts
248
+ function toThemeConfig(state) {
249
+ const palettes = state.palettes.map((p) => {
250
+ const oklchHue = traditionalHueToOklch(p.hue);
251
+ const desaturation = p.desaturationStrength !== "none" ? { direction: p.desaturationDirection, strength: p.desaturationStrength } : void 0;
252
+ const paletteHueGrading = p.hueGradeStrength !== "none" ? {
253
+ hue: traditionalHueToOklch(p.hueGradeHue),
254
+ strength: p.hueGradeStrength,
255
+ direction: p.hueGradeDirection
256
+ } : void 0;
257
+ return { hue: oklchHue, saturation: p.saturation, desaturation, paletteHueGrading };
258
+ });
259
+ const light = state.globalHueGrading.light.strength !== "none" ? { hue: traditionalHueToOklch(state.globalHueGrading.light.hue), strength: state.globalHueGrading.light.strength } : void 0;
260
+ const dark = state.globalHueGrading.dark.strength !== "none" ? { hue: traditionalHueToOklch(state.globalHueGrading.dark.hue), strength: state.globalHueGrading.dark.strength } : void 0;
261
+ const hueGrading = light || dark ? { light, dark } : void 0;
262
+ const dynamicRange = {
263
+ lightest: state.dynamicRange.lightest,
264
+ darkest: state.dynamicRange.darkest,
265
+ ...hueGrading ? { hueGrading } : {}
266
+ };
267
+ return {
268
+ colorSystem: { dynamicRange, palettes },
269
+ themes: {
270
+ neutral: { paletteIndex: 0, lightModeNv: 0.95, darkModeNv: 0.1 },
271
+ primary: { paletteIndex: 1, lightModeNv: 0.95, darkModeNv: 0.1 },
272
+ secondary: { paletteIndex: 1, lightModeNv: 0.85, darkModeNv: 0.15 },
273
+ strong: { paletteIndex: 0, lightModeNv: 0.1, darkModeNv: 0.95 }
274
+ },
275
+ elevation: {
276
+ offsets: [-0.02, 0, 0.04]
277
+ }
278
+ };
279
+ }
280
+
281
+ // src/hooks/useConfigurator.ts
282
+ function useConfigurator(initialState) {
283
+ const mergedInitial = initialState ? { ...DEFAULT_CONFIGURATOR_STATE, ...initialState } : DEFAULT_CONFIGURATOR_STATE;
284
+ const [state, dispatch] = React3.useReducer(configuratorReducer, mergedInitial);
285
+ const themeConfig = React3.useMemo(() => toThemeConfig(state), [state]);
286
+ const reset = React3.useCallback(() => dispatch({ type: "RESET" }), []);
287
+ return { state, dispatch, themeConfig, reset };
288
+ }
289
+ var PREVIEW_STEPS = 26;
290
+ function usePreviewColors(state) {
291
+ return React3.useMemo(() => {
292
+ const light = state.globalHueGrading.light.strength !== "none" ? { hue: traditionalHueToOklch(state.globalHueGrading.light.hue), strength: state.globalHueGrading.light.strength } : void 0;
293
+ const dark = state.globalHueGrading.dark.strength !== "none" ? { hue: traditionalHueToOklch(state.globalHueGrading.dark.hue), strength: state.globalHueGrading.dark.strength } : void 0;
294
+ const hueGrading = light || dark ? { light, dark } : void 0;
295
+ const dynamicRange = {
296
+ lightest: state.dynamicRange.lightest,
297
+ darkest: state.dynamicRange.darkest,
298
+ ...hueGrading ? { hueGrading } : {}
299
+ };
300
+ return state.palettes.map((p) => {
301
+ const oklchHue = traditionalHueToOklch(p.hue);
302
+ const desat = p.desaturationStrength !== "none" ? { direction: p.desaturationDirection, strength: p.desaturationStrength } : void 0;
303
+ const phg = p.hueGradeStrength !== "none" ? { hue: traditionalHueToOklch(p.hueGradeHue), strength: p.hueGradeStrength, direction: p.hueGradeDirection } : void 0;
304
+ return newtone.generatePreview(oklchHue, p.saturation, dynamicRange, PREVIEW_STEPS, desat, phg);
305
+ });
306
+ }, [state]);
307
+ }
308
+
309
+ // src/constants.ts
310
+ var SEMANTIC_HUE_RANGES = {
311
+ 2: { min: 80, max: 160 },
312
+ // Success (greens)
313
+ 3: { min: 25, max: 55 },
314
+ // Warning (yellow/orange)
315
+ 4: { min: 345, max: 375 }
316
+ // Error (reds, wraps 0°)
317
+ };
318
+
319
+ // src/panels/PalettePanel.tsx
320
+ var STRENGTH_OPTIONS = [
321
+ { label: "None", value: "none" },
322
+ { label: "Low", value: "low" },
323
+ { label: "Medium", value: "medium" },
324
+ { label: "Hard", value: "hard" }
325
+ ];
326
+ function PalettePanel({ palette, index, dispatch, previewColors }) {
327
+ const tokens = components.useTokens(1);
328
+ const hueRange = SEMANTIC_HUE_RANGES[index];
329
+ return /* @__PURE__ */ React3__default.default.createElement(components.Card, { elevation: 1, style: styles.container }, /* @__PURE__ */ React3__default.default.createElement(reactNative.Text, { style: [styles.title, { color: newtone.srgbToHex(tokens.textPrimary.srgb) }] }, palette.name), previewColors && /* @__PURE__ */ React3__default.default.createElement(reactNative.View, { style: styles.inlineSwatches }, previewColors.map((color, i) => /* @__PURE__ */ React3__default.default.createElement(
330
+ reactNative.View,
331
+ {
332
+ key: i,
333
+ style: [styles.inlineSwatch, { backgroundColor: newtone.srgbToHex(color.srgb) }]
334
+ }
335
+ ))), /* @__PURE__ */ React3__default.default.createElement(
336
+ components.HueSlider,
337
+ {
338
+ value: palette.hue,
339
+ onValueChange: (hue) => dispatch({ type: "SET_PALETTE_HUE", index, hue }),
340
+ label: "Hue",
341
+ showValue: true,
342
+ ...hueRange ? { min: hueRange.min, max: hueRange.max } : {}
343
+ }
344
+ ), /* @__PURE__ */ React3__default.default.createElement(
345
+ components.Slider,
346
+ {
347
+ value: palette.saturation,
348
+ onValueChange: (saturation) => dispatch({ type: "SET_PALETTE_SATURATION", index, saturation }),
349
+ min: 0,
350
+ max: 100,
351
+ label: "Saturation",
352
+ showValue: true
353
+ }
354
+ ), /* @__PURE__ */ React3__default.default.createElement(reactNative.View, { style: styles.row }, /* @__PURE__ */ React3__default.default.createElement(reactNative.View, { style: styles.flex }, /* @__PURE__ */ React3__default.default.createElement(
355
+ components.Select,
356
+ {
357
+ options: STRENGTH_OPTIONS,
358
+ value: palette.desaturationStrength,
359
+ onValueChange: (strength) => dispatch({ type: "SET_PALETTE_DESAT_STRENGTH", index, strength }),
360
+ label: "Desaturation"
361
+ }
362
+ )), palette.desaturationStrength !== "none" && /* @__PURE__ */ React3__default.default.createElement(reactNative.View, { style: styles.toggleContainer }, /* @__PURE__ */ React3__default.default.createElement(
363
+ components.Toggle,
364
+ {
365
+ value: palette.desaturationDirection === "dark",
366
+ onValueChange: (v) => dispatch({ type: "SET_PALETTE_DESAT_DIRECTION", index, direction: v ? "dark" : "light" }),
367
+ label: "Invert"
368
+ }
369
+ ))), /* @__PURE__ */ React3__default.default.createElement(reactNative.View, { style: styles.row }, /* @__PURE__ */ React3__default.default.createElement(reactNative.View, { style: styles.flex }, /* @__PURE__ */ React3__default.default.createElement(
370
+ components.Select,
371
+ {
372
+ options: STRENGTH_OPTIONS,
373
+ value: palette.hueGradeStrength,
374
+ onValueChange: (strength) => dispatch({ type: "SET_PALETTE_HUE_GRADE_STRENGTH", index, strength }),
375
+ label: "Hue Grading"
376
+ }
377
+ )), palette.hueGradeStrength !== "none" && /* @__PURE__ */ React3__default.default.createElement(reactNative.View, { style: styles.toggleContainer }, /* @__PURE__ */ React3__default.default.createElement(
378
+ components.Toggle,
379
+ {
380
+ value: palette.hueGradeDirection === "dark",
381
+ onValueChange: (v) => dispatch({ type: "SET_PALETTE_HUE_GRADE_DIRECTION", index, direction: v ? "dark" : "light" }),
382
+ label: "Invert"
383
+ }
384
+ ))), palette.hueGradeStrength !== "none" && /* @__PURE__ */ React3__default.default.createElement(
385
+ components.HueSlider,
386
+ {
387
+ value: palette.hueGradeHue,
388
+ onValueChange: (hue) => dispatch({ type: "SET_PALETTE_HUE_GRADE_HUE", index, hue }),
389
+ label: "Grade Target",
390
+ showValue: true
391
+ }
392
+ ));
393
+ }
394
+ var styles = reactNative.StyleSheet.create({
395
+ container: {
396
+ gap: 12,
397
+ marginBottom: 12
398
+ },
399
+ title: {
400
+ fontSize: 16,
401
+ fontWeight: "700"
402
+ },
403
+ inlineSwatches: {
404
+ flexDirection: "row",
405
+ flexWrap: "wrap",
406
+ gap: 1
407
+ },
408
+ inlineSwatch: {
409
+ width: 20,
410
+ height: 16,
411
+ borderRadius: 2
412
+ },
413
+ row: {
414
+ flexDirection: "row",
415
+ alignItems: "flex-end",
416
+ gap: 12
417
+ },
418
+ flex: {
419
+ flex: 1
420
+ },
421
+ toggleContainer: {
422
+ paddingBottom: 2
423
+ }
424
+ });
425
+ var STRENGTH_OPTIONS2 = [
426
+ { label: "None", value: "none" },
427
+ { label: "Low", value: "low" },
428
+ { label: "Medium", value: "medium" },
429
+ { label: "Hard", value: "hard" }
430
+ ];
431
+ function GlobalPanel({ state, dispatch }) {
432
+ const tokens = components.useTokens(1);
433
+ return /* @__PURE__ */ React3__default.default.createElement(components.Card, { elevation: 1, style: styles2.container }, /* @__PURE__ */ React3__default.default.createElement(reactNative.Text, { style: [styles2.title, { color: newtone.srgbToHex(tokens.textPrimary.srgb) }] }, "Dynamic Range"), /* @__PURE__ */ React3__default.default.createElement(reactNative.View, { style: styles2.row }, /* @__PURE__ */ React3__default.default.createElement(reactNative.View, { style: styles2.flex }, /* @__PURE__ */ React3__default.default.createElement(
434
+ components.Slider,
435
+ {
436
+ value: Math.round(state.dynamicRange.lightest * 100),
437
+ onValueChange: (v) => dispatch({ type: "SET_LIGHTEST", value: v / 100 }),
438
+ min: 0,
439
+ max: 100,
440
+ label: "Lightest",
441
+ showValue: true
442
+ }
443
+ )), /* @__PURE__ */ React3__default.default.createElement(reactNative.View, { style: styles2.flex }, /* @__PURE__ */ React3__default.default.createElement(
444
+ components.Slider,
445
+ {
446
+ value: Math.round(state.dynamicRange.darkest * 100),
447
+ onValueChange: (v) => dispatch({ type: "SET_DARKEST", value: v / 100 }),
448
+ min: 0,
449
+ max: 100,
450
+ label: "Darkest",
451
+ showValue: true
452
+ }
453
+ ))), /* @__PURE__ */ React3__default.default.createElement(reactNative.Text, { style: [styles2.subtitle, { color: newtone.srgbToHex(tokens.textSecondary.srgb) }] }, "Global Hue Grading \u2014 Light End"), /* @__PURE__ */ React3__default.default.createElement(reactNative.View, { style: styles2.row }, /* @__PURE__ */ React3__default.default.createElement(reactNative.View, { style: styles2.flex }, /* @__PURE__ */ React3__default.default.createElement(
454
+ components.Select,
455
+ {
456
+ options: STRENGTH_OPTIONS2,
457
+ value: state.globalHueGrading.light.strength,
458
+ onValueChange: (s) => dispatch({ type: "SET_GLOBAL_GRADE_LIGHT_STRENGTH", strength: s }),
459
+ label: "Strength"
460
+ }
461
+ )), state.globalHueGrading.light.strength !== "none" && /* @__PURE__ */ React3__default.default.createElement(reactNative.View, { style: styles2.flex }, /* @__PURE__ */ React3__default.default.createElement(
462
+ components.HueSlider,
463
+ {
464
+ value: state.globalHueGrading.light.hue,
465
+ onValueChange: (hue) => dispatch({ type: "SET_GLOBAL_GRADE_LIGHT_HUE", hue }),
466
+ label: "Target Hue",
467
+ showValue: true
468
+ }
469
+ ))), /* @__PURE__ */ React3__default.default.createElement(reactNative.Text, { style: [styles2.subtitle, { color: newtone.srgbToHex(tokens.textSecondary.srgb) }] }, "Global Hue Grading \u2014 Dark End"), /* @__PURE__ */ React3__default.default.createElement(reactNative.View, { style: styles2.row }, /* @__PURE__ */ React3__default.default.createElement(reactNative.View, { style: styles2.flex }, /* @__PURE__ */ React3__default.default.createElement(
470
+ components.Select,
471
+ {
472
+ options: STRENGTH_OPTIONS2,
473
+ value: state.globalHueGrading.dark.strength,
474
+ onValueChange: (s) => dispatch({ type: "SET_GLOBAL_GRADE_DARK_STRENGTH", strength: s }),
475
+ label: "Strength"
476
+ }
477
+ )), state.globalHueGrading.dark.strength !== "none" && /* @__PURE__ */ React3__default.default.createElement(reactNative.View, { style: styles2.flex }, /* @__PURE__ */ React3__default.default.createElement(
478
+ components.HueSlider,
479
+ {
480
+ value: state.globalHueGrading.dark.hue,
481
+ onValueChange: (hue) => dispatch({ type: "SET_GLOBAL_GRADE_DARK_HUE", hue }),
482
+ label: "Target Hue",
483
+ showValue: true
484
+ }
485
+ ))));
486
+ }
487
+ var styles2 = reactNative.StyleSheet.create({
488
+ container: {
489
+ gap: 12
490
+ },
491
+ title: {
492
+ fontSize: 16,
493
+ fontWeight: "700"
494
+ },
495
+ subtitle: {
496
+ fontSize: 13,
497
+ fontWeight: "600",
498
+ marginTop: 4
499
+ },
500
+ row: {
501
+ flexDirection: "row",
502
+ gap: 12
503
+ },
504
+ flex: {
505
+ flex: 1
506
+ }
507
+ });
508
+ var THEME_OPTIONS = [
509
+ { label: "Neutral", value: "neutral" },
510
+ { label: "Primary", value: "primary" },
511
+ { label: "Secondary", value: "secondary" },
512
+ { label: "Strong", value: "strong" }
513
+ ];
514
+ function PreviewCard({
515
+ state,
516
+ dispatch
517
+ }) {
518
+ const tokens = components.useTokens(1);
519
+ return /* @__PURE__ */ React3__default.default.createElement(components.Card, { elevation: 1, style: styles3.container }, /* @__PURE__ */ React3__default.default.createElement(reactNative.Text, { style: [styles3.title, { color: newtone.srgbToHex(tokens.textPrimary.srgb) }] }, "Component Preview"), /* @__PURE__ */ React3__default.default.createElement(reactNative.View, { style: styles3.controls }, /* @__PURE__ */ React3__default.default.createElement(
520
+ components.Select,
521
+ {
522
+ options: THEME_OPTIONS,
523
+ value: state.preview.theme,
524
+ onValueChange: (t) => dispatch({ type: "SET_PREVIEW_THEME", theme: t }),
525
+ label: "Theme"
526
+ }
527
+ )), /* @__PURE__ */ React3__default.default.createElement(reactNative.View, { style: previewStyles.wrapper }, /* @__PURE__ */ React3__default.default.createElement(reactNative.View, { style: previewStyles.row }, /* @__PURE__ */ React3__default.default.createElement(components.Button, { variant: "primary", size: "sm" }, "Primary"), /* @__PURE__ */ React3__default.default.createElement(components.Button, { variant: "secondary", size: "sm" }, "Secondary"), /* @__PURE__ */ React3__default.default.createElement(components.Button, { variant: "ghost", size: "sm" }, "Ghost"), /* @__PURE__ */ React3__default.default.createElement(components.Button, { variant: "outline", size: "sm" }, "Outline")), /* @__PURE__ */ React3__default.default.createElement(components.TextInput, { label: "Sample Input", value: "Hello, Newtone", onChangeText: () => {
528
+ } }), /* @__PURE__ */ React3__default.default.createElement(reactNative.View, { style: previewStyles.row }, /* @__PURE__ */ React3__default.default.createElement(reactNative.View, { style: [previewStyles.statusDot, { backgroundColor: newtone.srgbToHex(tokens.success.srgb) }] }), /* @__PURE__ */ React3__default.default.createElement(reactNative.Text, { style: [previewStyles.statusText, { color: newtone.srgbToHex(tokens.textPrimary.srgb) }] }, "Success"), /* @__PURE__ */ React3__default.default.createElement(reactNative.View, { style: [previewStyles.statusDot, { backgroundColor: newtone.srgbToHex(tokens.warning.srgb) }] }), /* @__PURE__ */ React3__default.default.createElement(reactNative.Text, { style: [previewStyles.statusText, { color: newtone.srgbToHex(tokens.textPrimary.srgb) }] }, "Warning"), /* @__PURE__ */ React3__default.default.createElement(reactNative.View, { style: [previewStyles.statusDot, { backgroundColor: newtone.srgbToHex(tokens.error.srgb) }] }), /* @__PURE__ */ React3__default.default.createElement(reactNative.Text, { style: [previewStyles.statusText, { color: newtone.srgbToHex(tokens.textPrimary.srgb) }] }, "Error"))));
529
+ }
530
+ function PreviewPanel({ state, dispatch, themeConfig }) {
531
+ return /* @__PURE__ */ React3__default.default.createElement(
532
+ components.NewtoneProvider,
533
+ {
534
+ key: `${state.preview.mode}-${state.preview.theme}`,
535
+ config: themeConfig,
536
+ initialMode: state.preview.mode,
537
+ initialTheme: state.preview.theme
538
+ },
539
+ /* @__PURE__ */ React3__default.default.createElement(PreviewCard, { state, dispatch })
540
+ );
541
+ }
542
+ var styles3 = reactNative.StyleSheet.create({
543
+ container: {
544
+ gap: 12
545
+ },
546
+ title: {
547
+ fontSize: 16,
548
+ fontWeight: "700"
549
+ },
550
+ controls: {
551
+ flexDirection: "row",
552
+ alignItems: "flex-end",
553
+ gap: 16
554
+ }
555
+ });
556
+ var previewStyles = reactNative.StyleSheet.create({
557
+ wrapper: {
558
+ gap: 12
559
+ },
560
+ row: {
561
+ flexDirection: "row",
562
+ alignItems: "center",
563
+ gap: 8,
564
+ flexWrap: "wrap"
565
+ },
566
+ statusDot: {
567
+ width: 12,
568
+ height: 12,
569
+ borderRadius: 6
570
+ },
571
+ statusText: {
572
+ fontSize: 13,
573
+ marginRight: 8
574
+ }
575
+ });
576
+ function toCSS(state) {
577
+ const config = toThemeConfig(state);
578
+ const modes = ["light", "dark"];
579
+ let css = "";
580
+ for (const mode of modes) {
581
+ const tokens = components.computeTokens(
582
+ config.colorSystem,
583
+ mode,
584
+ config.themes.neutral,
585
+ 1,
586
+ config.elevation.offsets
587
+ );
588
+ const selector = mode === "light" ? ":root" : '[data-theme="dark"]';
589
+ css += `${selector} {
590
+ `;
591
+ css += ` --newtone-background: ${newtone.srgbToHex(tokens.background.srgb)};
592
+ `;
593
+ css += ` --newtone-background-elevated: ${newtone.srgbToHex(tokens.backgroundElevated.srgb)};
594
+ `;
595
+ css += ` --newtone-background-sunken: ${newtone.srgbToHex(tokens.backgroundSunken.srgb)};
596
+ `;
597
+ css += ` --newtone-text-primary: ${newtone.srgbToHex(tokens.textPrimary.srgb)};
598
+ `;
599
+ css += ` --newtone-text-secondary: ${newtone.srgbToHex(tokens.textSecondary.srgb)};
600
+ `;
601
+ css += ` --newtone-interactive: ${newtone.srgbToHex(tokens.interactive.srgb)};
602
+ `;
603
+ css += ` --newtone-interactive-hover: ${newtone.srgbToHex(tokens.interactiveHover.srgb)};
604
+ `;
605
+ css += ` --newtone-interactive-active: ${newtone.srgbToHex(tokens.interactiveActive.srgb)};
606
+ `;
607
+ css += ` --newtone-border: ${newtone.srgbToHex(tokens.border.srgb)};
608
+ `;
609
+ css += ` --newtone-success: ${newtone.srgbToHex(tokens.success.srgb)};
610
+ `;
611
+ css += ` --newtone-warning: ${newtone.srgbToHex(tokens.warning.srgb)};
612
+ `;
613
+ css += ` --newtone-error: ${newtone.srgbToHex(tokens.error.srgb)};
614
+ `;
615
+ css += `}
616
+
617
+ `;
618
+ }
619
+ return css;
620
+ }
621
+
622
+ // src/bridge/toJSON.ts
623
+ function toJSON(state) {
624
+ const output = {
625
+ version: "1.0",
626
+ configuratorState: state,
627
+ themeConfig: toThemeConfig(state)
628
+ };
629
+ return JSON.stringify(output, null, 2);
630
+ }
631
+
632
+ // src/panels/ExportPanel.tsx
633
+ function ExportPanel({ state }) {
634
+ const tokens = components.useTokens(1);
635
+ const [format, setFormat] = React3.useState("css");
636
+ const [copied, setCopied] = React3.useState(false);
637
+ const output = React3.useMemo(() => {
638
+ return format === "css" ? toCSS(state) : toJSON(state);
639
+ }, [state, format]);
640
+ const handleCopy = React3.useCallback(() => {
641
+ if (typeof navigator !== "undefined" && navigator.clipboard) {
642
+ navigator.clipboard.writeText(output).then(() => {
643
+ setCopied(true);
644
+ setTimeout(() => setCopied(false), 2e3);
645
+ });
646
+ }
647
+ }, [output]);
648
+ return /* @__PURE__ */ React3__default.default.createElement(components.Card, { elevation: 1, style: styles4.container }, /* @__PURE__ */ React3__default.default.createElement(reactNative.Text, { style: [styles4.title, { color: newtone.srgbToHex(tokens.textPrimary.srgb) }] }, "Export"), /* @__PURE__ */ React3__default.default.createElement(reactNative.View, { style: styles4.tabs }, /* @__PURE__ */ React3__default.default.createElement(
649
+ components.Button,
650
+ {
651
+ variant: format === "css" ? "primary" : "ghost",
652
+ size: "sm",
653
+ onPress: () => setFormat("css")
654
+ },
655
+ "CSS Variables"
656
+ ), /* @__PURE__ */ React3__default.default.createElement(
657
+ components.Button,
658
+ {
659
+ variant: format === "json" ? "primary" : "ghost",
660
+ size: "sm",
661
+ onPress: () => setFormat("json")
662
+ },
663
+ "JSON"
664
+ ), /* @__PURE__ */ React3__default.default.createElement(components.Button, { variant: "outline", size: "sm", onPress: handleCopy }, copied ? "Copied!" : "Copy")), /* @__PURE__ */ React3__default.default.createElement(reactNative.View, { style: [styles4.codeBlock, { backgroundColor: newtone.srgbToHex(tokens.backgroundSunken.srgb) }] }, /* @__PURE__ */ React3__default.default.createElement(
665
+ reactNative.Text,
666
+ {
667
+ style: [styles4.code, { color: newtone.srgbToHex(tokens.textPrimary.srgb) }],
668
+ selectable: true
669
+ },
670
+ output
671
+ )));
672
+ }
673
+ var styles4 = reactNative.StyleSheet.create({
674
+ container: {
675
+ gap: 12,
676
+ marginBottom: 12
677
+ },
678
+ title: {
679
+ fontSize: 16,
680
+ fontWeight: "700"
681
+ },
682
+ tabs: {
683
+ flexDirection: "row",
684
+ gap: 8
685
+ },
686
+ codeBlock: {
687
+ borderRadius: 6,
688
+ padding: 12,
689
+ maxHeight: 300,
690
+ overflow: "scroll"
691
+ },
692
+ code: {
693
+ fontSize: 11,
694
+ fontFamily: "monospace",
695
+ lineHeight: 16
696
+ }
697
+ });
698
+
699
+ // src/Configurator.tsx
700
+ function Configurator({
701
+ initialState,
702
+ onChange,
703
+ showExport = true,
704
+ showPreview = true
705
+ }) {
706
+ const { state, dispatch, themeConfig, reset } = useConfigurator(initialState);
707
+ const tokens = components.useTokens(1);
708
+ const { setMode } = components.useNewtoneTheme();
709
+ const isInitialRender = React3.useRef(true);
710
+ const [activePaletteIndex, setActivePaletteIndex] = React3.useState(0);
711
+ const previews = usePreviewColors(state);
712
+ React3.useEffect(() => {
713
+ setMode(state.preview.mode);
714
+ }, []);
715
+ React3.useEffect(() => {
716
+ if (isInitialRender.current) {
717
+ isInitialRender.current = false;
718
+ return;
719
+ }
720
+ onChange?.(state, themeConfig);
721
+ }, [state, themeConfig, onChange]);
722
+ return /* @__PURE__ */ React3__default.default.createElement(reactNative.View, { style: [styles5.container, { backgroundColor: newtone.srgbToHex(tokens.backgroundSunken.srgb) }] }, /* @__PURE__ */ React3__default.default.createElement(reactNative.View, { style: styles5.topBar }, /* @__PURE__ */ React3__default.default.createElement(
723
+ components.Toggle,
724
+ {
725
+ value: state.preview.mode === "dark",
726
+ onValueChange: (isDark) => {
727
+ const mode = isDark ? "dark" : "light";
728
+ dispatch({ type: "SET_PREVIEW_MODE", mode });
729
+ setMode(mode);
730
+ },
731
+ label: "Dark Mode"
732
+ }
733
+ )), /* @__PURE__ */ React3__default.default.createElement(reactNative.View, { style: styles5.topRow }, /* @__PURE__ */ React3__default.default.createElement(reactNative.View, { style: styles5.topRowPanel }, /* @__PURE__ */ React3__default.default.createElement(GlobalPanel, { state, dispatch })), showPreview && /* @__PURE__ */ React3__default.default.createElement(reactNative.View, { style: styles5.topRowPanel }, /* @__PURE__ */ React3__default.default.createElement(PreviewPanel, { state, dispatch, themeConfig }))), /* @__PURE__ */ React3__default.default.createElement(reactNative.View, { style: styles5.tabBar }, state.palettes.map((palette, index) => /* @__PURE__ */ React3__default.default.createElement(
734
+ reactNative.Pressable,
735
+ {
736
+ key: index,
737
+ onPress: () => setActivePaletteIndex(index),
738
+ style: [
739
+ styles5.tab,
740
+ {
741
+ backgroundColor: index === activePaletteIndex ? newtone.srgbToHex(tokens.background.srgb) : "transparent",
742
+ borderColor: index === activePaletteIndex ? newtone.srgbToHex(tokens.border.srgb) : "transparent"
743
+ }
744
+ ]
745
+ },
746
+ /* @__PURE__ */ React3__default.default.createElement(reactNative.Text, { style: [
747
+ styles5.tabText,
748
+ {
749
+ color: newtone.srgbToHex(
750
+ index === activePaletteIndex ? tokens.textPrimary.srgb : tokens.textSecondary.srgb
751
+ ),
752
+ fontWeight: index === activePaletteIndex ? "700" : "500"
753
+ }
754
+ ] }, palette.name)
755
+ ))), /* @__PURE__ */ React3__default.default.createElement(
756
+ PalettePanel,
757
+ {
758
+ palette: state.palettes[activePaletteIndex],
759
+ index: activePaletteIndex,
760
+ dispatch,
761
+ previewColors: previews[activePaletteIndex]
762
+ }
763
+ ), showExport && /* @__PURE__ */ React3__default.default.createElement(ExportPanel, { state }), /* @__PURE__ */ React3__default.default.createElement(
764
+ "button",
765
+ {
766
+ onClick: () => reset(),
767
+ style: {
768
+ marginTop: 16,
769
+ padding: "8px 16px",
770
+ backgroundColor: "#ffffff",
771
+ color: "#000000",
772
+ border: "2px solid #000000",
773
+ borderRadius: 4,
774
+ fontWeight: 700,
775
+ fontSize: 14,
776
+ cursor: "pointer"
777
+ }
778
+ },
779
+ "Reset to Defaults"
780
+ ));
781
+ }
782
+ var styles5 = reactNative.StyleSheet.create({
783
+ container: {
784
+ borderRadius: 8,
785
+ padding: 16,
786
+ gap: 4
787
+ },
788
+ topBar: {
789
+ flexDirection: "row",
790
+ justifyContent: "flex-end",
791
+ alignItems: "center",
792
+ marginBottom: 8
793
+ },
794
+ topRow: {
795
+ flexDirection: "row",
796
+ gap: 8,
797
+ marginBottom: 4
798
+ },
799
+ topRowPanel: {
800
+ flex: 1
801
+ },
802
+ tabBar: {
803
+ flexDirection: "row",
804
+ gap: 4,
805
+ marginBottom: 4
806
+ },
807
+ tab: {
808
+ paddingVertical: 8,
809
+ paddingHorizontal: 12,
810
+ borderRadius: 6,
811
+ borderWidth: 1
812
+ },
813
+ tabText: {
814
+ fontSize: 13
815
+ }
816
+ });
817
+
818
+ exports.Configurator = Configurator;
819
+ exports.DEFAULT_CONFIGURATOR_STATE = DEFAULT_CONFIGURATOR_STATE;
820
+ exports.toCSS = toCSS;
821
+ exports.toJSON = toJSON;
822
+ exports.toThemeConfig = toThemeConfig;
823
+ exports.traditionalHueToOklch = traditionalHueToOklch;
824
+ exports.useConfigurator = useConfigurator;
825
+ exports.usePreviewColors = usePreviewColors;
826
+ //# sourceMappingURL=index.cjs.map
827
+ //# sourceMappingURL=index.cjs.map