@zen-ui-capri/system 0.0.0-stage → 1.1.1

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 (105) hide show
  1. package/.agents/skills/capri-zen-ui/SKILL.md +59 -0
  2. package/AGENTS.md +57 -0
  3. package/CLAUDE.md +7 -0
  4. package/LICENSE +21 -0
  5. package/README.md +93 -2
  6. package/bin/capri.mjs +1187 -0
  7. package/dist/lib/audio/ZenAudioController.d.ts +10 -0
  8. package/dist/lib/audio/ZenAudioController.d.ts.map +1 -0
  9. package/dist/lib/audio/useZenAudioSynth.d.ts +26 -0
  10. package/dist/lib/audio/useZenAudioSynth.d.ts.map +1 -0
  11. package/dist/lib/capri/capri-engine.d.ts +127 -0
  12. package/dist/lib/capri/capri-engine.d.ts.map +1 -0
  13. package/dist/lib/components/ZenAIConstruct.d.ts +24 -0
  14. package/dist/lib/components/ZenAIConstruct.d.ts.map +1 -0
  15. package/dist/lib/components/ZenAmbientBackground.d.ts +8 -0
  16. package/dist/lib/components/ZenAmbientBackground.d.ts.map +1 -0
  17. package/dist/lib/components/ZenBadge.d.ts +21 -0
  18. package/dist/lib/components/ZenBadge.d.ts.map +1 -0
  19. package/dist/lib/components/ZenButton.d.ts +24 -0
  20. package/dist/lib/components/ZenButton.d.ts.map +1 -0
  21. package/dist/lib/components/ZenCard.d.ts +36 -0
  22. package/dist/lib/components/ZenCard.d.ts.map +1 -0
  23. package/dist/lib/components/ZenDataDisplay.d.ts +50 -0
  24. package/dist/lib/components/ZenDataDisplay.d.ts.map +1 -0
  25. package/dist/lib/components/ZenDataTable.d.ts +20 -0
  26. package/dist/lib/components/ZenDataTable.d.ts.map +1 -0
  27. package/dist/lib/components/ZenDossierPanel.d.ts +33 -0
  28. package/dist/lib/components/ZenDossierPanel.d.ts.map +1 -0
  29. package/dist/lib/components/ZenDropdown.d.ts +19 -0
  30. package/dist/lib/components/ZenDropdown.d.ts.map +1 -0
  31. package/dist/lib/components/ZenFeedback.d.ts +24 -0
  32. package/dist/lib/components/ZenFeedback.d.ts.map +1 -0
  33. package/dist/lib/components/ZenForms.d.ts +84 -0
  34. package/dist/lib/components/ZenForms.d.ts.map +1 -0
  35. package/dist/lib/components/ZenHeading.d.ts +15 -0
  36. package/dist/lib/components/ZenHeading.d.ts.map +1 -0
  37. package/dist/lib/components/ZenIcon.d.ts +24 -0
  38. package/dist/lib/components/ZenIcon.d.ts.map +1 -0
  39. package/dist/lib/components/ZenInput.d.ts +12 -0
  40. package/dist/lib/components/ZenInput.d.ts.map +1 -0
  41. package/dist/lib/components/ZenModal.d.ts +12 -0
  42. package/dist/lib/components/ZenModal.d.ts.map +1 -0
  43. package/dist/lib/components/ZenNavbar.d.ts +11 -0
  44. package/dist/lib/components/ZenNavbar.d.ts.map +1 -0
  45. package/dist/lib/components/ZenNavigation.d.ts +52 -0
  46. package/dist/lib/components/ZenNavigation.d.ts.map +1 -0
  47. package/dist/lib/components/ZenOverlays.d.ts +48 -0
  48. package/dist/lib/components/ZenOverlays.d.ts.map +1 -0
  49. package/dist/lib/components/ZenSegmentedControl.d.ts +18 -0
  50. package/dist/lib/components/ZenSegmentedControl.d.ts.map +1 -0
  51. package/dist/lib/components/ZenSwitch.d.ts +11 -0
  52. package/dist/lib/components/ZenSwitch.d.ts.map +1 -0
  53. package/dist/lib/components/ZenTabs.d.ts +17 -0
  54. package/dist/lib/components/ZenTabs.d.ts.map +1 -0
  55. package/dist/lib/components/ZenTimelineCard.d.ts +14 -0
  56. package/dist/lib/components/ZenTimelineCard.d.ts.map +1 -0
  57. package/dist/lib/components/ZenToast.d.ts +18 -0
  58. package/dist/lib/components/ZenToast.d.ts.map +1 -0
  59. package/dist/lib/cosmos/ZenCosmosCanvas.d.ts +10 -0
  60. package/dist/lib/cosmos/ZenCosmosCanvas.d.ts.map +1 -0
  61. package/dist/lib/cosmos/ZenTelemetryHud.d.ts +45 -0
  62. package/dist/lib/cosmos/ZenTelemetryHud.d.ts.map +1 -0
  63. package/dist/lib/index.d.ts +33 -0
  64. package/dist/lib/index.d.ts.map +1 -0
  65. package/dist/lib/index.js +3922 -0
  66. package/dist/lib/theme/ZenThemeProvider.d.ts +47 -0
  67. package/dist/lib/theme/ZenThemeProvider.d.ts.map +1 -0
  68. package/dist/lib/tokens.css +888 -0
  69. package/dist/lib/universal/zen-universal.d.ts +77 -0
  70. package/dist/lib/universal/zen-universal.d.ts.map +1 -0
  71. package/dist/lib/universal/zen-universal.js +18 -0
  72. package/dist/lib/zen-universal-C0PD8AI9.js +1352 -0
  73. package/package.json +77 -3
  74. package/src/lib/audio/ZenAudioController.tsx +114 -0
  75. package/src/lib/audio/useZenAudioSynth.ts +289 -0
  76. package/src/lib/capri/capri-engine.ts +939 -0
  77. package/src/lib/components/ZenAIConstruct.tsx +594 -0
  78. package/src/lib/components/ZenAmbientBackground.tsx +210 -0
  79. package/src/lib/components/ZenBadge.tsx +111 -0
  80. package/src/lib/components/ZenButton.tsx +127 -0
  81. package/src/lib/components/ZenCard.tsx +180 -0
  82. package/src/lib/components/ZenDataDisplay.tsx +349 -0
  83. package/src/lib/components/ZenDataTable.tsx +143 -0
  84. package/src/lib/components/ZenDossierPanel.tsx +120 -0
  85. package/src/lib/components/ZenDropdown.tsx +208 -0
  86. package/src/lib/components/ZenFeedback.tsx +137 -0
  87. package/src/lib/components/ZenForms.tsx +664 -0
  88. package/src/lib/components/ZenHeading.tsx +63 -0
  89. package/src/lib/components/ZenIcon.tsx +220 -0
  90. package/src/lib/components/ZenInput.tsx +120 -0
  91. package/src/lib/components/ZenModal.tsx +103 -0
  92. package/src/lib/components/ZenNavbar.tsx +106 -0
  93. package/src/lib/components/ZenNavigation.tsx +300 -0
  94. package/src/lib/components/ZenOverlays.tsx +386 -0
  95. package/src/lib/components/ZenSegmentedControl.tsx +113 -0
  96. package/src/lib/components/ZenSwitch.tsx +63 -0
  97. package/src/lib/components/ZenTabs.tsx +103 -0
  98. package/src/lib/components/ZenTimelineCard.tsx +64 -0
  99. package/src/lib/components/ZenToast.tsx +96 -0
  100. package/src/lib/cosmos/ZenCosmosCanvas.tsx +931 -0
  101. package/src/lib/cosmos/ZenTelemetryHud.tsx +385 -0
  102. package/src/lib/index.ts +293 -0
  103. package/src/lib/styles/zen-tokens.css +888 -0
  104. package/src/lib/theme/ZenThemeProvider.tsx +328 -0
  105. package/src/lib/universal/zen-universal.ts +743 -0
@@ -0,0 +1,594 @@
1
+ import { useEffect, useRef, useState } from 'react';
2
+ import { Sparkles, Terminal, Activity, ShieldAlert, Cpu, Radio, Maximize2, Minimize2, FileText } from 'lucide-react';
3
+ import { useZenTheme } from '../theme/ZenThemeProvider';
4
+ import {
5
+ capri,
6
+ CAPRI_SUBROUTINE_SKILLS,
7
+ type CapriSubroutineId,
8
+ } from '../capri/capri-engine';
9
+ import { ZenBadge } from './ZenBadge';
10
+ import { ZenButton, ZenCopyButton } from './ZenButton';
11
+
12
+ export type ZenAIPersonaMode = CapriSubroutineId;
13
+ export type ZenAICameraFraming = 'portrait' | 'full';
14
+
15
+ export interface ZenAIDirective {
16
+ readonly id: ZenAIPersonaMode;
17
+ readonly label: string;
18
+ readonly status: string;
19
+ readonly frequency: string;
20
+ readonly transmission: string;
21
+ readonly metrics: readonly { readonly k: string; readonly v: string }[];
22
+ }
23
+
24
+ const AI_DIRECTIVES: readonly ZenAIDirective[] = [
25
+ {
26
+ id: 'orion',
27
+ label: 'CAPRI // UNLEASHED',
28
+ status: 'UI SYNTHESIS SUBROUTINE',
29
+ frequency: '51 MODULES · 40 VECTOR ICONS',
30
+ transmission:
31
+ CAPRI_SUBROUTINE_SKILLS.orion.summary,
32
+ metrics: [
33
+ { k: 'AI CHAT CMD', v: '"Capri orion"' },
34
+ { k: 'CLI CMD', v: 'npx capri skill orion' },
35
+ { k: 'PRIMITIVES', v: '51 UNIVERSAL' },
36
+ { k: 'PROD AVATAR', v: 'OMITTED (TREE-SHAKEN)' },
37
+ ],
38
+ },
39
+ {
40
+ id: 'architect',
41
+ label: 'SCS // ARCHITECT',
42
+ status: 'MULTI-STACK & TOKEN GOVERNANCE',
43
+ frequency: 'ZERO ANY · READONLY T[] · --ZEN-*',
44
+ transmission:
45
+ CAPRI_SUBROUTINE_SKILLS.architect.summary,
46
+ metrics: [
47
+ { k: 'AI CHAT CMD', v: '"Capri architect"' },
48
+ { k: 'CLI CMD', v: 'npx capri skill architect' },
49
+ { k: 'TYPING', v: '0 ANY · READONLY T[]' },
50
+ { k: 'STACKS', v: 'REACT / NG 20+ / VUE' },
51
+ ],
52
+ },
53
+ {
54
+ id: 'sentinel',
55
+ label: 'CYBERPSYCHOSIS // GUARD',
56
+ status: 'WCAG 2.2 AA & OVERFLOW AUDITOR',
57
+ frequency: 'ZERO BLUR-RACE · 320PX-4K GUARD',
58
+ transmission:
59
+ CAPRI_SUBROUTINE_SKILLS.sentinel.summary,
60
+ metrics: [
61
+ { k: 'AI CHAT CMD', v: '"Capri guard"' },
62
+ { k: 'CLI CMD', v: 'npx capri skill guard' },
63
+ { k: 'DROPDOWNS', v: 'ONMOUSEDOWN LOCK' },
64
+ { k: 'OVERFLOW', v: '0PX (320PX TO 4K)' },
65
+ ],
66
+ },
67
+ {
68
+ id: 'zen',
69
+ label: 'THEME // MATRIX',
70
+ status: '8-THEME POLARITY & 60FPS MOTION',
71
+ frequency: '4 DARK · 4 LIGHT · SPRING PHYSICS',
72
+ transmission:
73
+ CAPRI_SUBROUTINE_SKILLS.zen.summary,
74
+ metrics: [
75
+ { k: 'AI CHAT CMD', v: '"Capri zen"' },
76
+ { k: 'CLI CMD', v: 'npx capri skill zen' },
77
+ { k: 'POLARITIES', v: '4 DARK · 4 LIGHT' },
78
+ { k: 'MOTION', v: 'REDUCED-MOTION SAFE' },
79
+ ],
80
+ },
81
+ {
82
+ id: 'docu',
83
+ label: 'CAPRI // AUTO-DOCU',
84
+ status: 'ARCHITECTURE & USER-FLOW DOCS',
85
+ frequency: 'MERMAID FLOWCHART · 0 MS BUILD COST',
86
+ transmission:
87
+ CAPRI_SUBROUTINE_SKILLS.docu.summary,
88
+ metrics: [
89
+ { k: 'AI CHAT CMD', v: '"Capri docu"' },
90
+ { k: 'CLI CMD', v: 'npx capri docu' },
91
+ { k: 'OUTPUT', v: 'MARKDOWN + MERMAID' },
92
+ { k: 'BUILD COST', v: '0 MS (ON-DEMAND)' },
93
+ ],
94
+ },
95
+ ];
96
+
97
+ export interface ZenAIConstructProps {
98
+ readonly constructName?: string;
99
+ readonly subtitle?: string;
100
+ readonly defaultMode?: ZenAIPersonaMode;
101
+ readonly defaultFraming?: ZenAICameraFraming;
102
+ readonly compact?: boolean;
103
+ readonly className?: string;
104
+ }
105
+
106
+ export function ZenAIConstruct({
107
+ constructName = 'CAPRI // CAPRICORN',
108
+ subtitle = 'EDGERUNNERS S2 SOULKILLER MIXED-CONSTRUCT',
109
+ defaultMode = 'orion',
110
+ defaultFraming = 'portrait',
111
+ compact = false,
112
+ className = '',
113
+ }: ZenAIConstructProps) {
114
+ const { polarity, themeMeta, reducedMotion, setTheme, setRadius, setDensity } = useZenTheme();
115
+ const [activeMode, setActiveMode] = useState<ZenAIPersonaMode>(defaultMode);
116
+ const [framing, setFraming] = useState<ZenAICameraFraming>(defaultFraming);
117
+ const [voiceEnabled, setVoiceEnabled] = useState<boolean>(false);
118
+ const [pulseBoost, setPulseBoost] = useState<number>(0);
119
+ const canvasRef = useRef<HTMLCanvasElement | null>(null);
120
+ const spriteRef = useRef<HTMLImageElement | null>(null);
121
+ const pointerRef = useRef<{ x: number; y: number }>({ x: 0, y: 0 });
122
+ const zoomInterpRef = useRef<number>(defaultFraming === 'portrait' ? 1 : 0);
123
+
124
+ const directive =
125
+ AI_DIRECTIVES.find((d) => d.id === activeMode) ?? AI_DIRECTIVES[0];
126
+ const activeSkill = CAPRI_SUBROUTINE_SKILLS[activeMode];
127
+ const activePromptPayload = voiceEnabled
128
+ ? `${activeSkill.aiSystemPrompt}\n\n${activeSkill.aiVoiceAddendum}`
129
+ : activeSkill.aiSystemPrompt;
130
+
131
+ useEffect(() => {
132
+ const img = new Image();
133
+ img.src = '/capri-construct-base.png';
134
+ img.onload = () => {
135
+ spriteRef.current = img;
136
+ };
137
+ }, []);
138
+
139
+ useEffect(() => {
140
+ const canvas = canvasRef.current;
141
+ if (!canvas) return;
142
+ const ctx = canvas.getContext('2d');
143
+ if (!ctx) return;
144
+
145
+ let rafId = 0;
146
+ let time = 0;
147
+
148
+ const render = () => {
149
+ const dpr = Math.min(window.devicePixelRatio || 1, 2);
150
+ const width = canvas.clientWidth || 320;
151
+ const height = canvas.clientHeight || 340;
152
+ if (canvas.width !== width * dpr || canvas.height !== height * dpr) {
153
+ canvas.width = width * dpr;
154
+ canvas.height = height * dpr;
155
+ }
156
+
157
+ ctx.save();
158
+ ctx.scale(dpr, dpr);
159
+ ctx.clearRect(0, 0, width, height);
160
+
161
+ if (!reducedMotion) {
162
+ time += 0.026;
163
+ }
164
+
165
+ const targetZoom = framing === 'portrait' ? 1 : 0;
166
+ zoomInterpRef.current += (targetZoom - zoomInterpRef.current) * 0.12;
167
+ const z = zoomInterpRef.current;
168
+
169
+ const isLight = polarity === 'light';
170
+ const crimsonRed = '#FF003C';
171
+ const hotPink = '#FF1268';
172
+ const acidGreen = '#00FF44';
173
+ const irisBlue = '#19A0FF';
174
+ const starkWhite = '#FFFFFF';
175
+
176
+ const px = pointerRef.current.x;
177
+ const py = pointerRef.current.y;
178
+ const cx = width * 0.5;
179
+ const cy = height * 0.48;
180
+ const radiusScale = Math.min(width, height) * 0.42;
181
+
182
+ if (isLight) {
183
+ ctx.save();
184
+ ctx.strokeStyle = 'rgba(15, 23, 42, 0.05)';
185
+ ctx.lineWidth = 1;
186
+ const step = 28;
187
+ for (let gx = step; gx < width; gx += step) {
188
+ ctx.beginPath();
189
+ ctx.moveTo(gx, 0);
190
+ ctx.lineTo(gx, height);
191
+ ctx.stroke();
192
+ }
193
+ for (let gy = step; gy < height; gy += step) {
194
+ ctx.beginPath();
195
+ ctx.moveTo(0, gy);
196
+ ctx.lineTo(width, gy);
197
+ ctx.stroke();
198
+ }
199
+ ctx.restore();
200
+ }
201
+
202
+ const bgGlow = ctx.createRadialGradient(
203
+ cx + px * 10,
204
+ cy - 12 + py * 8,
205
+ 8,
206
+ cx,
207
+ cy,
208
+ radiusScale * 1.45
209
+ );
210
+ if (isLight) {
211
+ bgGlow.addColorStop(0, `${themeMeta.accentHex}29`);
212
+ bgGlow.addColorStop(0.55, 'rgba(225, 29, 72, 0.10)');
213
+ bgGlow.addColorStop(0.85, `${themeMeta.secondaryHex}12`);
214
+ bgGlow.addColorStop(1, 'transparent');
215
+ } else {
216
+ bgGlow.addColorStop(0, 'rgba(255, 0, 56, 0.24)');
217
+ bgGlow.addColorStop(0.55, 'rgba(180, 12, 72, 0.10)');
218
+ bgGlow.addColorStop(0.85, 'rgba(110, 24, 150, 0.04)');
219
+ bgGlow.addColorStop(1, 'transparent');
220
+ }
221
+ ctx.fillStyle = bgGlow;
222
+ ctx.fillRect(0, 0, width, height);
223
+
224
+ const sprite = spriteRef.current;
225
+ if (sprite && sprite.complete && sprite.naturalWidth > 0) {
226
+ const floatY = Math.sin(time * 1.85) * 4.0;
227
+ const tiltRad = px * 0.028 + Math.sin(time * 0.9) * 0.008;
228
+
229
+ const scaleMult = 1 + z * 0.64;
230
+ const targetH = (height - 30) * 0.96 * scaleMult;
231
+ const aspect = sprite.naturalWidth / sprite.naturalHeight;
232
+ const targetW = targetH * aspect;
233
+
234
+ const drawX = cx - targetW * (0.48 + z * 0.02) + px * 7;
235
+ const drawY = (-4 - z * 6) + floatY + py * 4;
236
+
237
+ ctx.save();
238
+ ctx.beginPath();
239
+ ctx.rect(0, 0, width, height - 22);
240
+ ctx.clip();
241
+
242
+ ctx.translate(cx, cy);
243
+ ctx.rotate(tiltRad);
244
+ ctx.translate(-cx, -cy);
245
+
246
+ if (pulseBoost > 0) {
247
+ ctx.save();
248
+ ctx.globalAlpha = 0.35;
249
+ ctx.drawImage(
250
+ sprite,
251
+ drawX - 3 * pulseBoost,
252
+ drawY,
253
+ targetW,
254
+ targetH
255
+ );
256
+ ctx.drawImage(
257
+ sprite,
258
+ drawX + 3 * pulseBoost,
259
+ drawY + 1,
260
+ targetW,
261
+ targetH
262
+ );
263
+ ctx.restore();
264
+ }
265
+
266
+ if (isLight) {
267
+ ctx.save();
268
+ ctx.shadowColor = 'rgba(15, 23, 42, 0.22)';
269
+ ctx.shadowBlur = 18;
270
+ ctx.shadowOffsetY = 6;
271
+ ctx.drawImage(sprite, drawX, drawY, targetW, targetH);
272
+ ctx.restore();
273
+ } else {
274
+ ctx.drawImage(sprite, drawX, drawY, targetW, targetH);
275
+ }
276
+
277
+ const blinkCycle = Math.sin(time * 1.1 - 1.2);
278
+ const isBlinking = blinkCycle > 0.992;
279
+
280
+ const eyeOffsetX = px * (2.8 + z * 1.8);
281
+ const eyeOffsetY = py * (2.2 + z * 1.4);
282
+
283
+ const mainEyeX = drawX + targetW * 0.5700 + eyeOffsetX;
284
+ const mainEyeY = drawY + targetH * 0.2325 + eyeOffsetY;
285
+ const mainEyeR = targetW * 0.0195;
286
+
287
+ const leftEyeX = drawX + targetW * 0.4751 + eyeOffsetX * 0.65;
288
+ const leftEyeY = drawY + targetH * 0.2472 + eyeOffsetY * 0.65;
289
+ const leftEyeR = targetW * 0.0105;
290
+
291
+ if (!isBlinking) {
292
+ ctx.beginPath();
293
+ ctx.arc(mainEyeX, mainEyeY, mainEyeR, 0, Math.PI * 2);
294
+ ctx.fillStyle = irisBlue;
295
+ ctx.fill();
296
+
297
+ ctx.beginPath();
298
+ ctx.arc(mainEyeX, mainEyeY, mainEyeR * 0.62, 0, Math.PI * 2);
299
+ ctx.fillStyle = '#0D1322';
300
+ ctx.fill();
301
+
302
+ ctx.beginPath();
303
+ ctx.arc(
304
+ mainEyeX + px * 0.6,
305
+ mainEyeY + py * 0.5,
306
+ mainEyeR * 0.44,
307
+ 0,
308
+ Math.PI * 2
309
+ );
310
+ ctx.fillStyle = starkWhite;
311
+ ctx.fill();
312
+
313
+ ctx.beginPath();
314
+ ctx.arc(leftEyeX, leftEyeY, leftEyeR, 0, Math.PI * 2);
315
+ ctx.fillStyle = irisBlue;
316
+ ctx.fill();
317
+
318
+ ctx.beginPath();
319
+ ctx.arc(leftEyeX, leftEyeY, leftEyeR * 0.48, 0, Math.PI * 2);
320
+ ctx.fillStyle = starkWhite;
321
+ ctx.fill();
322
+ }
323
+
324
+ ctx.restore();
325
+ }
326
+
327
+ const barCount = 28;
328
+ const barWidth = (width * 0.68) / barCount;
329
+ const startX = width * 0.16;
330
+ const baseY = height - 13;
331
+ ctx.globalAlpha = 0.88;
332
+ for (let b = 0; b < barCount; b++) {
333
+ const norm = Math.abs(b - barCount * 0.5) / (barCount * 0.5);
334
+ const amp =
335
+ (1 - norm * 0.72) *
336
+ (4 +
337
+ Math.abs(Math.sin(time * 4.2 + b * 0.45)) *
338
+ (9 + pulseBoost * 8));
339
+ ctx.fillStyle = isLight
340
+ ? b % 3 === 0
341
+ ? themeMeta.accentHex
342
+ : b % 5 === 0
343
+ ? themeMeta.secondaryHex
344
+ : crimsonRed
345
+ : b % 3 === 0
346
+ ? crimsonRed
347
+ : b % 5 === 0
348
+ ? acidGreen
349
+ : hotPink;
350
+ ctx.fillRect(
351
+ startX + b * barWidth,
352
+ baseY - amp * 0.5,
353
+ Math.max(2, barWidth - 3),
354
+ amp
355
+ );
356
+ }
357
+
358
+ ctx.restore();
359
+ rafId = window.requestAnimationFrame(render);
360
+ };
361
+
362
+ rafId = window.requestAnimationFrame(render);
363
+ return () => window.cancelAnimationFrame(rafId);
364
+ }, [polarity, themeMeta.accentHex, themeMeta.secondaryHex, reducedMotion, pulseBoost, framing]);
365
+
366
+ const handleSelectDirective = (next: ZenAIPersonaMode) => {
367
+ setActiveMode(next);
368
+ setPulseBoost(1);
369
+ window.setTimeout(() => setPulseBoost(0), 650);
370
+ };
371
+
372
+ const handleApplySubroutinePreset = () => {
373
+ capri.switchSubroutine(activeMode, {
374
+ applyPresetTheme: true,
375
+ onThemeChange: (nextTheme, nextRadius, nextDensity) => {
376
+ setTheme(nextTheme);
377
+ setRadius(nextRadius);
378
+ setDensity(nextDensity);
379
+ },
380
+ });
381
+ setPulseBoost(1);
382
+ window.setTimeout(() => setPulseBoost(0), 650);
383
+ };
384
+
385
+ return (
386
+ <div
387
+ data-zen-occlude="true"
388
+ onMouseMove={(e) => {
389
+ const rect = e.currentTarget.getBoundingClientRect();
390
+ pointerRef.current = {
391
+ x: ((e.clientX - rect.left) / rect.width) * 2 - 1,
392
+ y: ((e.clientY - rect.top) / rect.height) * 2 - 1,
393
+ };
394
+ }}
395
+ onMouseLeave={() => {
396
+ pointerRef.current = { x: 0, y: 0 };
397
+ }}
398
+ className={`zen-surface-selected overflow-hidden p-4 sm:p-6 lg:p-8 ${className}`.trim()}
399
+ >
400
+ <div className="grid grid-cols-1 lg:grid-cols-12 gap-6 items-center">
401
+ <div
402
+ className={`${
403
+ compact ? 'lg:col-span-5' : 'lg:col-span-5'
404
+ } relative flex flex-col items-center justify-center rounded-2xl bg-[var(--zen-bg-elevated)] border border-[var(--zen-accent-border)] p-2.5 sm:p-3 overflow-hidden shadow-inner transition-colors`}
405
+ >
406
+ <div className="w-full flex flex-wrap items-center justify-between gap-2 px-2 pt-1 z-10">
407
+ <span className="zen-telemetry-label text-[var(--zen-accent-primary)] font-semibold flex items-center gap-1.5 truncate">
408
+ <Radio className="w-3.5 h-3.5 animate-pulse shrink-0" />
409
+ <span className="truncate">{directive.frequency}</span>
410
+ </span>
411
+ <div className="flex items-center gap-1.5 shrink-0">
412
+ <button
413
+ type="button"
414
+ onClick={() =>
415
+ setFraming((prev) => (prev === 'portrait' ? 'full' : 'portrait'))
416
+ }
417
+ className="zen-focus-ring inline-flex items-center gap-1 px-2.5 py-0.5 rounded-full border border-[var(--zen-accent-border)] bg-[var(--zen-accent-subtle)] zen-font-mono text-[10px] font-medium text-[var(--zen-accent-primary)] hover:bg-[var(--zen-surface-subtle-hover)] cursor-pointer transition-colors"
418
+ >
419
+ {framing === 'portrait' ? (
420
+ <>
421
+ <Minimize2 className="w-2.5 h-2.5" />
422
+ <span>FULL POSE</span>
423
+ </>
424
+ ) : (
425
+ <>
426
+ <Maximize2 className="w-2.5 h-2.5" />
427
+ <span>PORTRAIT</span>
428
+ </>
429
+ )}
430
+ </button>
431
+ <ZenBadge tone="gold" pulse>
432
+ CAPRI LIVE
433
+ </ZenBadge>
434
+ </div>
435
+ </div>
436
+
437
+ <canvas
438
+ ref={canvasRef}
439
+ aria-label={`${constructName} Studio Trigger mixed-construct visualization`}
440
+ className="w-full h-72 sm:h-96 block"
441
+ />
442
+
443
+ <div className="w-full flex items-center justify-between gap-2 px-2 pb-1 zen-telemetry-label text-[var(--zen-ink-secondary)]">
444
+ <span className="truncate">ARASAKA // MIXED CONSTRUCT</span>
445
+ <span className="shrink-0 font-semibold text-[var(--zen-accent-primary)]">
446
+ {themeMeta.name.toUpperCase()}
447
+ </span>
448
+ </div>
449
+ </div>
450
+
451
+ <div className="lg:col-span-7 flex flex-col justify-between space-y-4 sm:space-y-5 min-w-0">
452
+ <div>
453
+ <div className="flex flex-wrap items-center justify-between gap-2 mb-2">
454
+ <span className="zen-telemetry-label text-[var(--zen-accent-primary)] flex items-center gap-1.5">
455
+ <Sparkles className="w-3.5 h-3.5 shrink-0" />
456
+ <span>{subtitle}</span>
457
+ </span>
458
+ <span className="zen-font-mono text-xs text-[var(--zen-accent-secondary)]">
459
+ {directive.status}
460
+ </span>
461
+ </div>
462
+
463
+ <div className="flex flex-wrap items-baseline justify-between gap-2 sm:gap-3">
464
+ <div className="flex flex-wrap items-baseline gap-2 sm:gap-3">
465
+ <h3 className="zen-font-display text-2xl sm:text-4xl font-extrabold tracking-tight text-[var(--zen-ink-primary)]">
466
+ {constructName}
467
+ </h3>
468
+ <span className="zen-font-serif italic text-xl sm:text-2xl text-[var(--zen-accent-primary)]">
469
+ AI Subroutine Engine & Co-Pilot
470
+ </span>
471
+ </div>
472
+ <button
473
+ type="button"
474
+ aria-pressed={voiceEnabled}
475
+ onClick={() => setVoiceEnabled((prev) => !prev)}
476
+ className={`zen-focus-ring px-2.5 py-1 rounded-full border zen-font-mono text-[11px] cursor-pointer transition-all ${
477
+ voiceEnabled
478
+ ? 'bg-[var(--zen-accent-primary)] text-[var(--zen-ink-on-accent)] border-[var(--zen-accent-primary)] font-semibold'
479
+ : 'bg-[var(--zen-surface-subtle)] text-[var(--zen-ink-secondary)] border-[var(--zen-border-default)] hover:text-[var(--zen-ink-primary)]'
480
+ }`}
481
+ >
482
+ {voiceEnabled ? 'Persona Voice: ON (--voice)' : 'Persona Voice: OFF (Default)'}
483
+ </button>
484
+ </div>
485
+
486
+ <div className="mt-3 sm:mt-4 p-3.5 sm:p-4 rounded-2xl bg-[var(--zen-surface-subtle)] border border-[var(--zen-border-default)] space-y-2">
487
+ <div className="flex flex-wrap items-center justify-between gap-2">
488
+ <div className="zen-telemetry-label text-[var(--zen-accent-secondary)] flex items-center gap-1.5">
489
+ <Terminal className="w-3.5 h-3.5 shrink-0" />
490
+ <span className="truncate">
491
+ {voiceEnabled
492
+ ? `PERSONA VOICE (--VOICE) // ${directive.label}`
493
+ : `SUBROUTINE SPECIFICATION // ${directive.label}`}
494
+ </span>
495
+ </div>
496
+ <span className="zen-font-mono text-[11px] text-[var(--zen-accent-primary)]">
497
+ Ask in Claude / Codex / Gemini: &ldquo;{voiceEnabled ? activeSkill.voiceCommand : activeSkill.chatCommand}&rdquo;
498
+ </span>
499
+ </div>
500
+ <p className="text-xs sm:text-sm text-[var(--zen-ink-primary)] leading-relaxed">
501
+ {voiceEnabled ? `“${activeSkill.personaQuote}”` : activeSkill.summary}
502
+ </p>
503
+ </div>
504
+ </div>
505
+
506
+ <div className="grid grid-cols-2 sm:grid-cols-4 gap-2 sm:gap-2.5">
507
+ {directive.metrics.map((m) => (
508
+ <div
509
+ key={m.k}
510
+ className="p-2.5 rounded-xl bg-[var(--zen-surface-subtle)] border border-[var(--zen-border-subtle)] min-w-0"
511
+ >
512
+ <div className="zen-telemetry-label truncate">{m.k}</div>
513
+ <div className="zen-font-mono text-xs font-semibold text-[var(--zen-ink-primary)] mt-0.5 truncate">
514
+ {m.v}
515
+ </div>
516
+ </div>
517
+ ))}
518
+ </div>
519
+
520
+ <div className="flex flex-wrap items-center gap-2 pt-2 border-t border-[var(--zen-border-subtle)]">
521
+ <span className="zen-telemetry-label mr-1 w-full sm:w-auto">
522
+ 5 CAPRI SUBROUTINES:
523
+ </span>
524
+ {AI_DIRECTIVES.map((d) => (
525
+ <ZenButton
526
+ key={d.id}
527
+ size="sm"
528
+ mono
529
+ variant={d.id === activeMode ? 'primary' : 'secondary'}
530
+ onClick={() => handleSelectDirective(d.id)}
531
+ iconLeft={
532
+ d.id === 'orion' ? (
533
+ <Activity className="w-3.5 h-3.5" />
534
+ ) : d.id === 'architect' ? (
535
+ <Cpu className="w-3.5 h-3.5" />
536
+ ) : d.id === 'sentinel' ? (
537
+ <ShieldAlert className="w-3.5 h-3.5" />
538
+ ) : d.id === 'zen' ? (
539
+ <Sparkles className="w-3.5 h-3.5" />
540
+ ) : (
541
+ <FileText className="w-3.5 h-3.5" />
542
+ )
543
+ }
544
+ >
545
+ {d.label}
546
+ </ZenButton>
547
+ ))}
548
+ </div>
549
+
550
+ <div className="p-3.5 rounded-2xl bg-[var(--zen-surface-subtle)] border border-[var(--zen-border-default)] space-y-2.5">
551
+ <div className="flex flex-wrap items-center justify-between gap-2">
552
+ <span className="zen-telemetry-label text-[var(--zen-accent-primary)]">
553
+ RULES ENFORCED BY {activeSkill.chatCommand.toUpperCase()} ({activeSkill.cliCommand})
554
+ </span>
555
+ <ZenBadge tone="emerald">
556
+ 0 MS BUILD / RUNTIME COST
557
+ </ZenBadge>
558
+ </div>
559
+ <ul className="grid grid-cols-1 sm:grid-cols-2 gap-1.5 text-xs text-[var(--zen-ink-secondary)]">
560
+ {activeSkill.ruleset.map((rule) => (
561
+ <li key={rule} className="flex items-start gap-1.5">
562
+ <span className="text-[var(--zen-accent-primary)] zen-font-mono">›</span>
563
+ <span>{rule}</span>
564
+ </li>
565
+ ))}
566
+ </ul>
567
+ <div className="flex flex-wrap items-center justify-between gap-2 pt-1.5 border-t border-[var(--zen-border-subtle)]">
568
+ <span className="zen-font-mono text-[11px] text-[var(--zen-ink-muted)]">
569
+ In production apps, &lt;ZenAIConstruct /&gt; is omitted; use these 5 subroutines in Claude, Codex, Gemini, or CLI.
570
+ </span>
571
+ <div className="flex flex-wrap items-center gap-2">
572
+ <ZenButton
573
+ size="sm"
574
+ variant="secondary"
575
+ mono
576
+ onClick={handleApplySubroutinePreset}
577
+ >
578
+ Switch Preset ({activeSkill.recommendedTheme})
579
+ </ZenButton>
580
+ <ZenCopyButton
581
+ size="sm"
582
+ variant="primary"
583
+ value={activePromptPayload}
584
+ label="COPY SKILL FOR CLAUDE / CODEX / GEMINI"
585
+ copiedLabel="SKILL COPIED"
586
+ />
587
+ </div>
588
+ </div>
589
+ </div>
590
+ </div>
591
+ </div>
592
+ </div>
593
+ );
594
+ }