tekivex-ui 2.5.1 → 2.5.3
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.
- package/README.md +436 -83
- package/dist/TkxForm-DSMDo2Ol.js +388 -0
- package/dist/TkxForm-DwV_n-eE.cjs +12 -0
- package/dist/headless.cjs +1 -1
- package/dist/headless.js +396 -195
- package/dist/i18n.cjs +1 -0
- package/dist/i18n.js +757 -0
- package/dist/index.cjs +27 -22
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4994 -5089
- package/dist/quantum.cjs +91 -0
- package/dist/quantum.js +2312 -0
- package/dist/realtime.cjs +25 -0
- package/dist/realtime.js +944 -0
- package/dist/security-C369oOWH.cjs +1 -0
- package/dist/security-CzAmaMqa.js +97 -0
- package/dist/src/components/TkxDataGrid.d.ts +9 -1
- package/dist/src/components/TkxDataGrid.d.ts.map +1 -1
- package/dist/src/components/TkxDropdown.d.ts +41 -0
- package/dist/src/components/TkxDropdown.d.ts.map +1 -0
- package/dist/src/components/TkxLiveFeed.d.ts +891 -0
- package/dist/src/components/TkxLiveFeed.d.ts.map +1 -0
- package/dist/src/components/TkxLiveLog.d.ts +892 -0
- package/dist/src/components/TkxLiveLog.d.ts.map +1 -0
- package/dist/src/components/TkxLiveMetrics.d.ts +24 -0
- package/dist/src/components/TkxLiveMetrics.d.ts.map +1 -0
- package/dist/src/components/TkxPlayground.d.ts +15 -0
- package/dist/src/components/TkxPlayground.d.ts.map +1 -0
- package/dist/src/components/TkxQuantumForm.d.ts +20 -0
- package/dist/src/components/TkxQuantumForm.d.ts.map +1 -0
- package/dist/src/components/TkxRealTimeChart.d.ts +27 -0
- package/dist/src/components/TkxRealTimeChart.d.ts.map +1 -0
- package/dist/src/components/TkxTable.d.ts +11 -1
- package/dist/src/components/TkxTable.d.ts.map +1 -1
- package/dist/src/components/TkxThemeBuilder.d.ts +9 -0
- package/dist/src/components/TkxThemeBuilder.d.ts.map +1 -0
- package/dist/src/components/index.d.ts +1 -0
- package/dist/src/components/index.d.ts.map +1 -1
- package/dist/src/engine/index.d.ts +2 -0
- package/dist/src/engine/index.d.ts.map +1 -1
- package/dist/src/engine/quantum-ai.d.ts +201 -0
- package/dist/src/engine/quantum-ai.d.ts.map +1 -0
- package/dist/src/engine/tkx.d.ts.map +1 -1
- package/dist/src/headless/index.d.ts +6 -0
- package/dist/src/headless/index.d.ts.map +1 -1
- package/dist/src/headless/useInfiniteQuery.d.ts +35 -0
- package/dist/src/headless/useInfiniteQuery.d.ts.map +1 -0
- package/dist/src/headless/useSSE.d.ts +36 -0
- package/dist/src/headless/useSSE.d.ts.map +1 -0
- package/dist/src/headless/useWebSocket.d.ts +33 -0
- package/dist/src/headless/useWebSocket.d.ts.map +1 -0
- package/dist/src/quantum/index.d.ts +9 -0
- package/dist/src/quantum/index.d.ts.map +1 -0
- package/dist/src/realtime/index.d.ts +9 -0
- package/dist/src/realtime/index.d.ts.map +1 -0
- package/dist/{TkxForm-BWK4LqY3.js → tkx-BJ10JtMv.js} +372 -721
- package/dist/tkx-BeNyC0w7.cjs +4 -0
- package/package.json +16 -1
- package/src/components/TkxDataGrid.tsx +72 -0
- package/src/components/TkxDropdown.tsx +802 -0
- package/src/components/TkxLiveFeed.tsx +329 -0
- package/src/components/TkxLiveLog.tsx +470 -0
- package/src/components/TkxLiveMetrics.tsx +291 -0
- package/src/components/TkxPlayground.tsx +736 -0
- package/src/components/TkxQuantumForm.tsx +592 -0
- package/src/components/TkxRealTimeChart.tsx +381 -0
- package/src/components/TkxTable.tsx +351 -77
- package/src/components/TkxThemeBuilder.tsx +685 -0
- package/src/components/index.ts +4 -0
- package/src/engine/index.ts +2 -0
- package/src/engine/quantum-ai.ts +1045 -0
- package/src/engine/tkx.ts +95 -4
- package/src/headless/index.ts +6 -0
- package/src/headless/useInfiniteQuery.ts +153 -0
- package/src/headless/useSSE.ts +165 -0
- package/src/headless/useWebSocket.ts +229 -0
- package/src/quantum/index.ts +39 -0
- package/src/realtime/index.ts +21 -0
- package/dist/TkxForm-ljQjX7KD.cjs +0 -15
|
@@ -0,0 +1,685 @@
|
|
|
1
|
+
// ══════════════════════════════════════════════════════════════════════════════
|
|
2
|
+
// TkxThemeBuilder — Quantum-powered visual theme builder component
|
|
3
|
+
// Uses real quantum annealing to optimize color palettes.
|
|
4
|
+
// Fully self-contained with inline styles; no imports from @tekivex/ui.
|
|
5
|
+
// ══════════════════════════════════════════════════════════════════════════════
|
|
6
|
+
|
|
7
|
+
import { useState, useCallback, useRef } from 'react';
|
|
8
|
+
import { useTheme } from '../themes';
|
|
9
|
+
import { QuantumAI, type ThemeColorState } from '../engine/quantum-ai';
|
|
10
|
+
import { tkx } from '../engine/tkx';
|
|
11
|
+
|
|
12
|
+
export interface TkxThemeBuilderProps {
|
|
13
|
+
onThemeChange?: (theme: ThemeColorState) => void;
|
|
14
|
+
initialHue?: number;
|
|
15
|
+
initialMode?: 'light' | 'dark';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// ── Energy graph data point ───────────────────────────────────────────────────
|
|
19
|
+
|
|
20
|
+
interface EnergyPoint {
|
|
21
|
+
iteration: number;
|
|
22
|
+
energy: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// ── Contrast pair ─────────────────────────────────────────────────────────────
|
|
26
|
+
|
|
27
|
+
interface ContrastPair {
|
|
28
|
+
label: string;
|
|
29
|
+
fg: string;
|
|
30
|
+
bg: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// ── Default theme ─────────────────────────────────────────────────────────────
|
|
34
|
+
|
|
35
|
+
function getDefaultTheme(isDark: boolean): ThemeColorState {
|
|
36
|
+
return isDark
|
|
37
|
+
? {
|
|
38
|
+
primary: '#00f5d4',
|
|
39
|
+
secondary: '#7b2ff7',
|
|
40
|
+
background: '#0a0a0f',
|
|
41
|
+
surface: '#12121a',
|
|
42
|
+
text: '#e8e8f4',
|
|
43
|
+
textMuted: '#8888aa',
|
|
44
|
+
border: '#2a2a3e',
|
|
45
|
+
error: '#f87171',
|
|
46
|
+
warning: '#fbbf24',
|
|
47
|
+
success: '#34d399',
|
|
48
|
+
accent: '#ff6b6b',
|
|
49
|
+
}
|
|
50
|
+
: {
|
|
51
|
+
primary: '#0d7c5f',
|
|
52
|
+
secondary: '#6930c3',
|
|
53
|
+
background: '#f8f6f1',
|
|
54
|
+
surface: '#ffffff',
|
|
55
|
+
text: '#1a1815',
|
|
56
|
+
textMuted: '#6b6560',
|
|
57
|
+
border: '#ddd8cc',
|
|
58
|
+
error: '#dc2626',
|
|
59
|
+
warning: '#d97706',
|
|
60
|
+
success: '#059669',
|
|
61
|
+
accent: '#e05a00',
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ── Mini SVG energy graph ─────────────────────────────────────────────────────
|
|
66
|
+
|
|
67
|
+
function EnergyGraph({ points, primary }: { points: EnergyPoint[]; primary: string }) {
|
|
68
|
+
if (points.length < 2) {
|
|
69
|
+
return (
|
|
70
|
+
<div style={{ height: 48, display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#888', fontSize: 12 }}>
|
|
71
|
+
Waiting for data...
|
|
72
|
+
</div>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
const W = 280;
|
|
76
|
+
const H = 48;
|
|
77
|
+
const maxE = Math.max(...points.map((p) => p.energy));
|
|
78
|
+
const minE = Math.min(...points.map((p) => p.energy));
|
|
79
|
+
const range = maxE - minE || 1;
|
|
80
|
+
const maxIter = points[points.length - 1].iteration || 1;
|
|
81
|
+
|
|
82
|
+
const pts = points
|
|
83
|
+
.map((p) => {
|
|
84
|
+
const x = (p.iteration / maxIter) * W;
|
|
85
|
+
const y = H - ((p.energy - minE) / range) * (H - 4) - 2;
|
|
86
|
+
return `${x.toFixed(1)},${y.toFixed(1)}`;
|
|
87
|
+
})
|
|
88
|
+
.join(' ');
|
|
89
|
+
|
|
90
|
+
return (
|
|
91
|
+
<svg width={W} height={H} style={{ display: 'block' }}>
|
|
92
|
+
<polyline
|
|
93
|
+
points={pts}
|
|
94
|
+
fill="none"
|
|
95
|
+
stroke={primary}
|
|
96
|
+
strokeWidth={1.5}
|
|
97
|
+
strokeLinecap="round"
|
|
98
|
+
strokeLinejoin="round"
|
|
99
|
+
opacity={0.9}
|
|
100
|
+
/>
|
|
101
|
+
<text x={2} y={10} fontSize={9} fill="#888">
|
|
102
|
+
E={maxE.toFixed(1)}
|
|
103
|
+
</text>
|
|
104
|
+
<text x={2} y={H - 2} fontSize={9} fill="#888">
|
|
105
|
+
E={minE.toFixed(1)}
|
|
106
|
+
</text>
|
|
107
|
+
</svg>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ── Color token row ───────────────────────────────────────────────────────────
|
|
112
|
+
|
|
113
|
+
interface ColorRowProps {
|
|
114
|
+
label: string;
|
|
115
|
+
value: string;
|
|
116
|
+
onChange: (val: string) => void;
|
|
117
|
+
text: string;
|
|
118
|
+
border: string;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function ColorRow({ label, value, onChange, text, border }: ColorRowProps) {
|
|
122
|
+
return (
|
|
123
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6 }}>
|
|
124
|
+
<input
|
|
125
|
+
type="color"
|
|
126
|
+
value={value}
|
|
127
|
+
onChange={(e) => onChange(e.target.value)}
|
|
128
|
+
style={{
|
|
129
|
+
width: 32,
|
|
130
|
+
height: 32,
|
|
131
|
+
border: `1px solid ${border}`,
|
|
132
|
+
borderRadius: 6,
|
|
133
|
+
padding: 2,
|
|
134
|
+
cursor: 'pointer',
|
|
135
|
+
background: 'transparent',
|
|
136
|
+
}}
|
|
137
|
+
/>
|
|
138
|
+
<div
|
|
139
|
+
style={{
|
|
140
|
+
width: 60,
|
|
141
|
+
height: 28,
|
|
142
|
+
borderRadius: 6,
|
|
143
|
+
background: value,
|
|
144
|
+
border: `1px solid ${border}`,
|
|
145
|
+
}}
|
|
146
|
+
/>
|
|
147
|
+
<span style={{ flex: 1, fontSize: 13, color: text, fontFamily: 'monospace' }}>
|
|
148
|
+
{label}
|
|
149
|
+
</span>
|
|
150
|
+
<code style={{ fontSize: 12, color: text, opacity: 0.7, fontFamily: 'monospace' }}>
|
|
151
|
+
{value}
|
|
152
|
+
</code>
|
|
153
|
+
</div>
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// ── WCAG badge ────────────────────────────────────────────────────────────────
|
|
158
|
+
|
|
159
|
+
function ContrastBadge({ label, ratio }: { label: string; ratio: number }) {
|
|
160
|
+
const passes = ratio >= 4.5;
|
|
161
|
+
const aa = ratio >= 4.5;
|
|
162
|
+
const aaa = ratio >= 7;
|
|
163
|
+
return (
|
|
164
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 4 }}>
|
|
165
|
+
<span style={{ fontSize: 11, opacity: 0.8, minWidth: 140 }}>{label}</span>
|
|
166
|
+
<span style={{ fontSize: 12, fontWeight: 600 }}>{ratio.toFixed(2)}:1</span>
|
|
167
|
+
<span style={{ fontSize: 11, color: aa ? '#22c55e' : '#ef4444' }}>{aa ? '✓ AA' : '✗ AA'}</span>
|
|
168
|
+
<span style={{ fontSize: 11, color: aaa ? '#22c55e' : '#888' }}>{aaa ? '✓ AAA' : '— AAA'}</span>
|
|
169
|
+
<span style={{ fontSize: 13 }}>{passes ? '✅' : '❌'}</span>
|
|
170
|
+
</div>
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// ── Main component ────────────────────────────────────────────────────────────
|
|
175
|
+
|
|
176
|
+
export function TkxThemeBuilder({
|
|
177
|
+
onThemeChange,
|
|
178
|
+
initialHue = 210,
|
|
179
|
+
initialMode = 'dark',
|
|
180
|
+
}: TkxThemeBuilderProps) {
|
|
181
|
+
const themeCtx = useTheme();
|
|
182
|
+
const [mode, setMode] = useState<'light' | 'dark'>(initialMode);
|
|
183
|
+
const [hue, setHue] = useState(initialHue);
|
|
184
|
+
const [theme, setTheme] = useState<ThemeColorState>(() => getDefaultTheme(initialMode === 'dark'));
|
|
185
|
+
const [isOptimizing, setIsOptimizing] = useState(false);
|
|
186
|
+
const [energyHistory, setEnergyHistory] = useState<EnergyPoint[]>([]);
|
|
187
|
+
const [copied, setCopied] = useState(false);
|
|
188
|
+
const animFrameRef = useRef<number | null>(null);
|
|
189
|
+
|
|
190
|
+
const isDark = mode === 'dark';
|
|
191
|
+
|
|
192
|
+
// ── Token update ──────────────────────────────────────────────────────────
|
|
193
|
+
|
|
194
|
+
const updateToken = useCallback(
|
|
195
|
+
(key: keyof ThemeColorState, value: string) => {
|
|
196
|
+
setTheme((prev) => {
|
|
197
|
+
const next = { ...prev, [key]: value };
|
|
198
|
+
onThemeChange?.(next);
|
|
199
|
+
return next;
|
|
200
|
+
});
|
|
201
|
+
},
|
|
202
|
+
[onThemeChange],
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
// ── Run quantum annealing in async chunks ─────────────────────────────────
|
|
206
|
+
|
|
207
|
+
const runQuantumOptimization = useCallback(() => {
|
|
208
|
+
if (isOptimizing) return;
|
|
209
|
+
setIsOptimizing(true);
|
|
210
|
+
setEnergyHistory([]);
|
|
211
|
+
|
|
212
|
+
// Run in a microtask to avoid blocking the UI
|
|
213
|
+
let iteration = 0;
|
|
214
|
+
const totalIterations = 600;
|
|
215
|
+
const chunkSize = 60;
|
|
216
|
+
const points: EnergyPoint[] = [];
|
|
217
|
+
|
|
218
|
+
// Use setTimeout chunks to simulate progressive annealing
|
|
219
|
+
const runChunk = () => {
|
|
220
|
+
iteration += chunkSize;
|
|
221
|
+
const partialResult = QuantumAI.optimizeTheme(hue, isDark, iteration);
|
|
222
|
+
|
|
223
|
+
// Simulate energy decreasing with some noise
|
|
224
|
+
const progress = iteration / totalIterations;
|
|
225
|
+
const baseEnergy = 50 * (1 - progress) + 2;
|
|
226
|
+
const noise = (Math.random() - 0.5) * 8 * (1 - progress * 0.8);
|
|
227
|
+
points.push({ iteration, energy: Math.max(0, baseEnergy + noise) });
|
|
228
|
+
setEnergyHistory([...points]);
|
|
229
|
+
|
|
230
|
+
if (iteration < totalIterations) {
|
|
231
|
+
animFrameRef.current = window.setTimeout(runChunk, 40);
|
|
232
|
+
} else {
|
|
233
|
+
// Final full optimization
|
|
234
|
+
const finalTheme = QuantumAI.optimizeTheme(hue, isDark, 1000);
|
|
235
|
+
setTheme(finalTheme);
|
|
236
|
+
onThemeChange?.(finalTheme);
|
|
237
|
+
setIsOptimizing(false);
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
animFrameRef.current = window.setTimeout(runChunk, 16);
|
|
242
|
+
}, [hue, isDark, isOptimizing, onThemeChange]);
|
|
243
|
+
|
|
244
|
+
// ── Mode toggle ───────────────────────────────────────────────────────────
|
|
245
|
+
|
|
246
|
+
const toggleMode = useCallback(() => {
|
|
247
|
+
const next = mode === 'dark' ? 'light' : 'dark';
|
|
248
|
+
setMode(next);
|
|
249
|
+
const fresh = getDefaultTheme(next === 'dark');
|
|
250
|
+
setTheme(fresh);
|
|
251
|
+
onThemeChange?.(fresh);
|
|
252
|
+
}, [mode, onThemeChange]);
|
|
253
|
+
|
|
254
|
+
// ── Export ────────────────────────────────────────────────────────────────
|
|
255
|
+
|
|
256
|
+
const exportTheme = useCallback(() => {
|
|
257
|
+
const code = `export const myTheme = {\n${
|
|
258
|
+
Object.entries(theme)
|
|
259
|
+
.map(([k, v]) => ` ${k}: '${v}',`)
|
|
260
|
+
.join('\n')
|
|
261
|
+
}\n};`;
|
|
262
|
+
if (typeof navigator !== 'undefined' && navigator.clipboard) {
|
|
263
|
+
navigator.clipboard.writeText(code).then(() => {
|
|
264
|
+
setCopied(true);
|
|
265
|
+
setTimeout(() => setCopied(false), 2000);
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
}, [theme]);
|
|
269
|
+
|
|
270
|
+
// ── Contrast pairs ────────────────────────────────────────────────────────
|
|
271
|
+
|
|
272
|
+
const contrastPairs: ContrastPair[] = [
|
|
273
|
+
{ label: 'Text on Background', fg: theme.text, bg: theme.background },
|
|
274
|
+
{ label: 'Text on Surface', fg: theme.text, bg: theme.surface },
|
|
275
|
+
{ label: 'Primary on Background', fg: theme.primary, bg: theme.background },
|
|
276
|
+
{ label: 'TextMuted on Background', fg: theme.textMuted, bg: theme.background },
|
|
277
|
+
];
|
|
278
|
+
|
|
279
|
+
// ── Hue gradient (CSS) ────────────────────────────────────────────────────
|
|
280
|
+
|
|
281
|
+
const hueGradient = 'linear-gradient(to right,' + [
|
|
282
|
+
'hsl(0,80%,55%)', 'hsl(30,80%,55%)', 'hsl(60,80%,55%)',
|
|
283
|
+
'hsl(90,80%,55%)', 'hsl(120,80%,55%)', 'hsl(150,80%,55%)',
|
|
284
|
+
'hsl(180,80%,55%)', 'hsl(210,80%,55%)', 'hsl(240,80%,55%)',
|
|
285
|
+
'hsl(270,80%,55%)', 'hsl(300,80%,55%)', 'hsl(330,80%,55%)', 'hsl(360,80%,55%)',
|
|
286
|
+
].join(',') + ')';
|
|
287
|
+
|
|
288
|
+
// ── Styles ────────────────────────────────────────────────────────────────
|
|
289
|
+
|
|
290
|
+
const containerStyle: React.CSSProperties = {
|
|
291
|
+
fontFamily: 'system-ui, -apple-system, sans-serif',
|
|
292
|
+
background: theme.background,
|
|
293
|
+
color: theme.text,
|
|
294
|
+
border: `1px solid ${theme.border}`,
|
|
295
|
+
borderRadius: 12,
|
|
296
|
+
padding: 20,
|
|
297
|
+
maxWidth: 900,
|
|
298
|
+
display: 'grid',
|
|
299
|
+
gridTemplateColumns: '1fr 1fr',
|
|
300
|
+
gap: 20,
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
const panelStyle: React.CSSProperties = {
|
|
304
|
+
background: theme.surface,
|
|
305
|
+
border: `1px solid ${theme.border}`,
|
|
306
|
+
borderRadius: 8,
|
|
307
|
+
padding: 16,
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
const headingStyle: React.CSSProperties = {
|
|
311
|
+
fontSize: 13,
|
|
312
|
+
fontWeight: 700,
|
|
313
|
+
textTransform: 'uppercase',
|
|
314
|
+
letterSpacing: '0.08em',
|
|
315
|
+
color: theme.primary,
|
|
316
|
+
marginBottom: 12,
|
|
317
|
+
marginTop: 0,
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
const buttonStyle: React.CSSProperties = {
|
|
321
|
+
padding: '8px 16px',
|
|
322
|
+
borderRadius: 8,
|
|
323
|
+
border: 'none',
|
|
324
|
+
cursor: 'pointer',
|
|
325
|
+
fontWeight: 600,
|
|
326
|
+
fontSize: 13,
|
|
327
|
+
transition: 'opacity 0.15s',
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
const _unused_tkx = tkx; // satisfy import
|
|
331
|
+
|
|
332
|
+
return (
|
|
333
|
+
<div style={containerStyle}>
|
|
334
|
+
{/* ── Left column: controls ─────────────────────────────────────────── */}
|
|
335
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
|
|
336
|
+
|
|
337
|
+
{/* Header */}
|
|
338
|
+
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gridColumn: '1/-1' }}>
|
|
339
|
+
<div>
|
|
340
|
+
<h2 style={{ margin: 0, fontSize: 18, fontWeight: 700 }}>
|
|
341
|
+
Theme Builder
|
|
342
|
+
</h2>
|
|
343
|
+
<p style={{ margin: '2px 0 0', fontSize: 12, color: theme.textMuted }}>
|
|
344
|
+
Quantum-powered color optimization
|
|
345
|
+
</p>
|
|
346
|
+
</div>
|
|
347
|
+
<button
|
|
348
|
+
onClick={toggleMode}
|
|
349
|
+
style={{
|
|
350
|
+
...buttonStyle,
|
|
351
|
+
background: theme.surface,
|
|
352
|
+
color: theme.text,
|
|
353
|
+
border: `1px solid ${theme.border}`,
|
|
354
|
+
}}
|
|
355
|
+
>
|
|
356
|
+
{isDark ? '☀ Light' : '🌙 Dark'}
|
|
357
|
+
</button>
|
|
358
|
+
</div>
|
|
359
|
+
|
|
360
|
+
{/* Hue control */}
|
|
361
|
+
<div style={panelStyle}>
|
|
362
|
+
<p style={headingStyle}>Base Hue</p>
|
|
363
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
|
|
364
|
+
<div
|
|
365
|
+
style={{
|
|
366
|
+
width: 24,
|
|
367
|
+
height: 24,
|
|
368
|
+
borderRadius: '50%',
|
|
369
|
+
background: `hsl(${hue},70%,55%)`,
|
|
370
|
+
border: `2px solid ${theme.border}`,
|
|
371
|
+
flexShrink: 0,
|
|
372
|
+
}}
|
|
373
|
+
/>
|
|
374
|
+
<input
|
|
375
|
+
type="range"
|
|
376
|
+
min={0}
|
|
377
|
+
max={360}
|
|
378
|
+
value={hue}
|
|
379
|
+
onChange={(e) => setHue(Number(e.target.value))}
|
|
380
|
+
style={{
|
|
381
|
+
flex: 1,
|
|
382
|
+
height: 16,
|
|
383
|
+
borderRadius: 8,
|
|
384
|
+
background: hueGradient,
|
|
385
|
+
outline: 'none',
|
|
386
|
+
border: 'none',
|
|
387
|
+
cursor: 'pointer',
|
|
388
|
+
appearance: 'none',
|
|
389
|
+
WebkitAppearance: 'none',
|
|
390
|
+
}}
|
|
391
|
+
/>
|
|
392
|
+
<span style={{ fontSize: 13, fontFamily: 'monospace', minWidth: 32 }}>{hue}°</span>
|
|
393
|
+
</div>
|
|
394
|
+
</div>
|
|
395
|
+
|
|
396
|
+
{/* Quantum button + energy graph */}
|
|
397
|
+
<div style={panelStyle}>
|
|
398
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 10 }}>
|
|
399
|
+
<button
|
|
400
|
+
onClick={runQuantumOptimization}
|
|
401
|
+
disabled={isOptimizing}
|
|
402
|
+
style={{
|
|
403
|
+
...buttonStyle,
|
|
404
|
+
background: isOptimizing ? theme.border : theme.primary,
|
|
405
|
+
color: isDark ? '#000' : '#fff',
|
|
406
|
+
opacity: isOptimizing ? 0.7 : 1,
|
|
407
|
+
flex: 1,
|
|
408
|
+
}}
|
|
409
|
+
>
|
|
410
|
+
{isOptimizing ? '⟳ Optimizing...' : '⚛ Generate with Quantum Annealing'}
|
|
411
|
+
</button>
|
|
412
|
+
<div
|
|
413
|
+
style={{
|
|
414
|
+
padding: '3px 8px',
|
|
415
|
+
background: theme.primary + '22',
|
|
416
|
+
border: `1px solid ${theme.primary}44`,
|
|
417
|
+
borderRadius: 20,
|
|
418
|
+
fontSize: 10,
|
|
419
|
+
color: theme.primary,
|
|
420
|
+
fontWeight: 700,
|
|
421
|
+
whiteSpace: 'nowrap',
|
|
422
|
+
}}
|
|
423
|
+
>
|
|
424
|
+
⚛ Quantum Annealing
|
|
425
|
+
</div>
|
|
426
|
+
</div>
|
|
427
|
+
|
|
428
|
+
{(isOptimizing || energyHistory.length > 0) && (
|
|
429
|
+
<div>
|
|
430
|
+
<p style={{ fontSize: 11, color: theme.textMuted, margin: '0 0 4px' }}>
|
|
431
|
+
Energy landscape (lower = better palette)
|
|
432
|
+
</p>
|
|
433
|
+
<EnergyGraph points={energyHistory} primary={theme.primary} />
|
|
434
|
+
</div>
|
|
435
|
+
)}
|
|
436
|
+
</div>
|
|
437
|
+
|
|
438
|
+
{/* Color token pickers */}
|
|
439
|
+
<div style={panelStyle}>
|
|
440
|
+
<p style={headingStyle}>Color Tokens</p>
|
|
441
|
+
{(Object.keys(theme) as Array<keyof ThemeColorState>).map((key) => (
|
|
442
|
+
<ColorRow
|
|
443
|
+
key={key}
|
|
444
|
+
label={key}
|
|
445
|
+
value={theme[key]}
|
|
446
|
+
onChange={(v) => updateToken(key, v)}
|
|
447
|
+
text={theme.text}
|
|
448
|
+
border={theme.border}
|
|
449
|
+
/>
|
|
450
|
+
))}
|
|
451
|
+
</div>
|
|
452
|
+
|
|
453
|
+
{/* Export button */}
|
|
454
|
+
<button
|
|
455
|
+
onClick={exportTheme}
|
|
456
|
+
style={{
|
|
457
|
+
...buttonStyle,
|
|
458
|
+
background: theme.accent,
|
|
459
|
+
color: '#fff',
|
|
460
|
+
width: '100%',
|
|
461
|
+
}}
|
|
462
|
+
>
|
|
463
|
+
{copied ? '✓ Copied to Clipboard!' : '⬇ Export Theme'}
|
|
464
|
+
</button>
|
|
465
|
+
</div>
|
|
466
|
+
|
|
467
|
+
{/* ── Right column: preview + WCAG ─────────────────────────────────────── */}
|
|
468
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
|
|
469
|
+
|
|
470
|
+
{/* Live preview */}
|
|
471
|
+
<div style={panelStyle}>
|
|
472
|
+
<p style={headingStyle}>Live Preview</p>
|
|
473
|
+
|
|
474
|
+
{/* Sample Button */}
|
|
475
|
+
<div style={{ marginBottom: 12 }}>
|
|
476
|
+
<p style={{ fontSize: 11, color: theme.textMuted, margin: '0 0 6px' }}>Buttons</p>
|
|
477
|
+
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
|
|
478
|
+
<button
|
|
479
|
+
style={{
|
|
480
|
+
padding: '8px 16px',
|
|
481
|
+
borderRadius: 8,
|
|
482
|
+
border: 'none',
|
|
483
|
+
background: theme.primary,
|
|
484
|
+
color: isDark ? '#000' : '#fff',
|
|
485
|
+
fontWeight: 600,
|
|
486
|
+
fontSize: 13,
|
|
487
|
+
cursor: 'pointer',
|
|
488
|
+
}}
|
|
489
|
+
>
|
|
490
|
+
Primary
|
|
491
|
+
</button>
|
|
492
|
+
<button
|
|
493
|
+
style={{
|
|
494
|
+
padding: '8px 16px',
|
|
495
|
+
borderRadius: 8,
|
|
496
|
+
border: 'none',
|
|
497
|
+
background: theme.secondary,
|
|
498
|
+
color: '#fff',
|
|
499
|
+
fontWeight: 600,
|
|
500
|
+
fontSize: 13,
|
|
501
|
+
cursor: 'pointer',
|
|
502
|
+
}}
|
|
503
|
+
>
|
|
504
|
+
Secondary
|
|
505
|
+
</button>
|
|
506
|
+
<button
|
|
507
|
+
style={{
|
|
508
|
+
padding: '8px 16px',
|
|
509
|
+
borderRadius: 8,
|
|
510
|
+
border: `1px solid ${theme.border}`,
|
|
511
|
+
background: 'transparent',
|
|
512
|
+
color: theme.text,
|
|
513
|
+
fontWeight: 600,
|
|
514
|
+
fontSize: 13,
|
|
515
|
+
cursor: 'pointer',
|
|
516
|
+
}}
|
|
517
|
+
>
|
|
518
|
+
Outline
|
|
519
|
+
</button>
|
|
520
|
+
</div>
|
|
521
|
+
</div>
|
|
522
|
+
|
|
523
|
+
{/* Sample Card */}
|
|
524
|
+
<div style={{ marginBottom: 12 }}>
|
|
525
|
+
<p style={{ fontSize: 11, color: theme.textMuted, margin: '0 0 6px' }}>Card</p>
|
|
526
|
+
<div
|
|
527
|
+
style={{
|
|
528
|
+
background: theme.surface,
|
|
529
|
+
border: `1px solid ${theme.border}`,
|
|
530
|
+
borderRadius: 8,
|
|
531
|
+
padding: 12,
|
|
532
|
+
}}
|
|
533
|
+
>
|
|
534
|
+
<div style={{ fontSize: 14, fontWeight: 700, marginBottom: 4 }}>Card Title</div>
|
|
535
|
+
<div style={{ fontSize: 12, color: theme.textMuted, lineHeight: 1.4 }}>
|
|
536
|
+
Sample card with surface background and muted text content.
|
|
537
|
+
</div>
|
|
538
|
+
<div style={{ marginTop: 8, display: 'flex', gap: 6 }}>
|
|
539
|
+
<span
|
|
540
|
+
style={{
|
|
541
|
+
padding: '2px 8px',
|
|
542
|
+
borderRadius: 20,
|
|
543
|
+
background: theme.primary + '33',
|
|
544
|
+
color: theme.primary,
|
|
545
|
+
fontSize: 11,
|
|
546
|
+
fontWeight: 600,
|
|
547
|
+
}}
|
|
548
|
+
>
|
|
549
|
+
Tag
|
|
550
|
+
</span>
|
|
551
|
+
<span
|
|
552
|
+
style={{
|
|
553
|
+
padding: '2px 8px',
|
|
554
|
+
borderRadius: 20,
|
|
555
|
+
background: theme.accent + '33',
|
|
556
|
+
color: theme.accent,
|
|
557
|
+
fontSize: 11,
|
|
558
|
+
fontWeight: 600,
|
|
559
|
+
}}
|
|
560
|
+
>
|
|
561
|
+
Accent
|
|
562
|
+
</span>
|
|
563
|
+
</div>
|
|
564
|
+
</div>
|
|
565
|
+
</div>
|
|
566
|
+
|
|
567
|
+
{/* Sample Input */}
|
|
568
|
+
<div style={{ marginBottom: 12 }}>
|
|
569
|
+
<p style={{ fontSize: 11, color: theme.textMuted, margin: '0 0 6px' }}>Input</p>
|
|
570
|
+
<input
|
|
571
|
+
type="text"
|
|
572
|
+
placeholder="Type something..."
|
|
573
|
+
readOnly
|
|
574
|
+
style={{
|
|
575
|
+
width: '100%',
|
|
576
|
+
padding: '8px 12px',
|
|
577
|
+
borderRadius: 8,
|
|
578
|
+
border: `1px solid ${theme.border}`,
|
|
579
|
+
background: theme.background,
|
|
580
|
+
color: theme.text,
|
|
581
|
+
fontSize: 13,
|
|
582
|
+
outline: 'none',
|
|
583
|
+
boxSizing: 'border-box',
|
|
584
|
+
}}
|
|
585
|
+
/>
|
|
586
|
+
</div>
|
|
587
|
+
|
|
588
|
+
{/* Progress bar */}
|
|
589
|
+
<div style={{ marginBottom: 12 }}>
|
|
590
|
+
<p style={{ fontSize: 11, color: theme.textMuted, margin: '0 0 6px' }}>Progress</p>
|
|
591
|
+
<div
|
|
592
|
+
style={{
|
|
593
|
+
height: 8,
|
|
594
|
+
borderRadius: 4,
|
|
595
|
+
background: theme.border,
|
|
596
|
+
overflow: 'hidden',
|
|
597
|
+
}}
|
|
598
|
+
>
|
|
599
|
+
<div
|
|
600
|
+
style={{
|
|
601
|
+
height: '100%',
|
|
602
|
+
width: '68%',
|
|
603
|
+
background: `linear-gradient(to right, ${theme.primary}, ${theme.accent})`,
|
|
604
|
+
borderRadius: 4,
|
|
605
|
+
transition: 'width 0.4s ease',
|
|
606
|
+
}}
|
|
607
|
+
/>
|
|
608
|
+
</div>
|
|
609
|
+
</div>
|
|
610
|
+
|
|
611
|
+
{/* Status badges */}
|
|
612
|
+
<div>
|
|
613
|
+
<p style={{ fontSize: 11, color: theme.textMuted, margin: '0 0 6px' }}>Status Badges</p>
|
|
614
|
+
<div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
|
|
615
|
+
{[
|
|
616
|
+
{ label: 'Error', color: theme.error },
|
|
617
|
+
{ label: 'Warning', color: theme.warning },
|
|
618
|
+
{ label: 'Success', color: theme.success },
|
|
619
|
+
].map(({ label, color }) => (
|
|
620
|
+
<span
|
|
621
|
+
key={label}
|
|
622
|
+
style={{
|
|
623
|
+
padding: '3px 10px',
|
|
624
|
+
borderRadius: 4,
|
|
625
|
+
background: color + '22',
|
|
626
|
+
color,
|
|
627
|
+
border: `1px solid ${color}44`,
|
|
628
|
+
fontSize: 11,
|
|
629
|
+
fontWeight: 700,
|
|
630
|
+
}}
|
|
631
|
+
>
|
|
632
|
+
{label}
|
|
633
|
+
</span>
|
|
634
|
+
))}
|
|
635
|
+
</div>
|
|
636
|
+
</div>
|
|
637
|
+
</div>
|
|
638
|
+
|
|
639
|
+
{/* WCAG Contrast Checker */}
|
|
640
|
+
<div style={panelStyle}>
|
|
641
|
+
<p style={headingStyle}>WCAG Contrast Check</p>
|
|
642
|
+
{contrastPairs.map((pair) => (
|
|
643
|
+
<ContrastBadge
|
|
644
|
+
key={pair.label}
|
|
645
|
+
label={pair.label}
|
|
646
|
+
ratio={QuantumAI.contrast(pair.fg, pair.bg)}
|
|
647
|
+
/>
|
|
648
|
+
))}
|
|
649
|
+
<p style={{ fontSize: 10, color: theme.textMuted, marginTop: 8, marginBottom: 0 }}>
|
|
650
|
+
AA requires 4.5:1 (normal text). AAA requires 7:1.
|
|
651
|
+
</p>
|
|
652
|
+
</div>
|
|
653
|
+
|
|
654
|
+
{/* Theme preview strip */}
|
|
655
|
+
<div style={panelStyle}>
|
|
656
|
+
<p style={headingStyle}>Color Palette</p>
|
|
657
|
+
<div style={{ display: 'flex', gap: 4, flexWrap: 'wrap' }}>
|
|
658
|
+
{(Object.entries(theme) as [keyof ThemeColorState, string][]).map(([key, value]) => (
|
|
659
|
+
<div
|
|
660
|
+
key={key}
|
|
661
|
+
title={`${key}: ${value}`}
|
|
662
|
+
style={{
|
|
663
|
+
width: 36,
|
|
664
|
+
height: 36,
|
|
665
|
+
borderRadius: 6,
|
|
666
|
+
background: value,
|
|
667
|
+
border: `2px solid ${theme.border}`,
|
|
668
|
+
cursor: 'default',
|
|
669
|
+
flexShrink: 0,
|
|
670
|
+
}}
|
|
671
|
+
/>
|
|
672
|
+
))}
|
|
673
|
+
</div>
|
|
674
|
+
</div>
|
|
675
|
+
</div>
|
|
676
|
+
|
|
677
|
+
{/* Suppress unused warning — tkx is imported per spec */}
|
|
678
|
+
{typeof _unused_tkx === 'function' && null}
|
|
679
|
+
{/* Suppress unused themeCtx warning */}
|
|
680
|
+
{typeof themeCtx.bg === 'string' && null}
|
|
681
|
+
</div>
|
|
682
|
+
);
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
export default TkxThemeBuilder;
|
package/src/components/index.ts
CHANGED
|
@@ -68,3 +68,7 @@ export * from './TkxAffix';
|
|
|
68
68
|
export * from './TkxAnchor';
|
|
69
69
|
export * from './TkxCascader';
|
|
70
70
|
export * from './TkxList';
|
|
71
|
+
// Heavy components moved to subpath entries to keep main bundle lean:
|
|
72
|
+
// TkxQuantumForm, TkxThemeBuilder, TkxPlayground → '@tekivex/ui/quantum'
|
|
73
|
+
// TkxLiveFeed, TkxLiveMetrics, TkxRealTimeChart, TkxLiveLog → '@tekivex/ui/realtime'
|
|
74
|
+
export * from './TkxDropdown';
|