@harmonia-core/ui 1.2.2 → 1.2.4

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.
@@ -1,7 +1,8 @@
1
- import { createContext, useState, useCallback, useContext } from 'react';
2
- import { AnimatePresence, motion } from 'motion/react';
3
- import { jsx, jsxs } from 'react/jsx-runtime';
1
+ "use client";
2
+ import { useState, useCallback } from 'react';
3
+ import { useCapacityContext, useFeedback, useDerivedMode, useEnergyField, useAttentionField, useEmotionalValenceField, deriveModeLabel, getModeBadgeColor, entranceClass, hoverClass, ambientClass, listItemClass } from '@harmonia-core/ui';
4
4
  import { rengeVars } from '@renge-ui/tokens';
5
+ import { jsx, jsxs } from 'react/jsx-runtime';
5
6
 
6
7
  var __defProp = Object.defineProperty;
7
8
  var __defProps = Object.defineProperties;
@@ -34,320 +35,6 @@ var __objRest = (source, exclude) => {
34
35
  }
35
36
  return target;
36
37
  };
37
- var FEEDBACK_FREQUENCIES = {
38
- low: 396,
39
- // Foundation/root elements
40
- mid: 528,
41
- // Primary interactive content
42
- high: 741
43
- // Dynamic/feedback elements
44
- };
45
- var DEFAULT_FIELD_CONFIG = {
46
- smoothing: 0.15,
47
- // Exponential smoothing factor
48
- velocityThreshold: 0.05,
49
- // Min velocity to register as trend
50
- debounceMs: 100
51
- // Debounce rapid changes
52
- };
53
- var DEFAULT_USER_CAPACITY = {
54
- cognitive: 0.7,
55
- temporal: 0.7,
56
- emotional: 0.7
57
- };
58
- var DEFAULT_EMOTIONAL_STATE = {
59
- valence: 0.3,
60
- // > 0.15 (with emotional > 0.6) triggers expressive motion mode
61
- arousal: 0.5
62
- };
63
-
64
- // lib/capacity/feedback.ts
65
- var HAPTIC_PATTERNS = {
66
- /** Short tap — confirm/select */
67
- tap: [8],
68
- /** Two pulses — toggle/switch */
69
- toggle: [8, 50, 8],
70
- /** Gentle pulse — ambient/ambient confirmation */
71
- pulse: [15, 30, 15],
72
- /** Error/warning — three quick */
73
- error: [50, 30, 50, 30, 50]
74
- };
75
- function triggerHaptic(pattern = "tap") {
76
- if (typeof navigator !== "undefined" && "vibrate" in navigator) {
77
- navigator.vibrate(HAPTIC_PATTERNS[pattern]);
78
- }
79
- }
80
- var _audioCtx = null;
81
- function getAudioContext() {
82
- if (typeof window === "undefined") return null;
83
- try {
84
- if (!_audioCtx || _audioCtx.state === "closed") {
85
- _audioCtx = new AudioContext();
86
- }
87
- if (_audioCtx.state === "suspended") {
88
- _audioCtx.resume();
89
- }
90
- return _audioCtx;
91
- } catch (e) {
92
- return null;
93
- }
94
- }
95
- function playSonicFeedback(frequency, duration = 120, volume = 0.06) {
96
- const ctx = getAudioContext();
97
- if (!ctx) return;
98
- const oscillator = ctx.createOscillator();
99
- const gainNode = ctx.createGain();
100
- oscillator.connect(gainNode);
101
- gainNode.connect(ctx.destination);
102
- oscillator.type = "sine";
103
- oscillator.frequency.setValueAtTime(frequency, ctx.currentTime);
104
- gainNode.gain.setValueAtTime(0, ctx.currentTime);
105
- gainNode.gain.linearRampToValueAtTime(volume, ctx.currentTime + 0.015);
106
- gainNode.gain.linearRampToValueAtTime(0, ctx.currentTime + duration / 1e3);
107
- oscillator.start(ctx.currentTime);
108
- oscillator.stop(ctx.currentTime + duration / 1e3 + 0.02);
109
- }
110
- function getFrequencyForPace(pace) {
111
- if (pace === "activated") return FEEDBACK_FREQUENCIES.high;
112
- if (pace === "calm") return FEEDBACK_FREQUENCIES.low;
113
- return FEEDBACK_FREQUENCIES.mid;
114
- }
115
- function playPacedSonic(pace, duration) {
116
- playSonicFeedback(getFrequencyForPace(pace), duration);
117
- }
118
-
119
- // lib/capacity/fields/field-manager.ts
120
- function deriveEnergyField(capacity) {
121
- const { cognitive, temporal, emotional } = capacity;
122
- return Math.pow(cognitive * temporal * emotional, 1 / 3);
123
- }
124
- function deriveAttentionField(capacity) {
125
- return 1 - capacity.temporal * 0.5;
126
- }
127
- function deriveEmotionalValenceField(state) {
128
- return state.valence;
129
- }
130
- function createFieldValue(value, previousValue) {
131
- var _a;
132
- const now = Date.now();
133
- const lastChange = (_a = previousValue == null ? void 0 : previousValue.lastChange) != null ? _a : now;
134
- const timeDelta = (now - lastChange) / 1e3;
135
- let trend = "stable";
136
- let velocity;
137
- if (typeof value === "number" && previousValue && typeof previousValue.value === "number") {
138
- const valueDelta = value - previousValue.value;
139
- velocity = timeDelta > 0 ? valueDelta / timeDelta : 0;
140
- if (Math.abs(velocity) > DEFAULT_FIELD_CONFIG.velocityThreshold) {
141
- trend = velocity > 0 ? "rising" : "falling";
142
- }
143
- }
144
- return {
145
- value,
146
- lastChange: now,
147
- trend,
148
- velocity
149
- };
150
- }
151
- var FieldManagerClass = class {
152
- constructor() {
153
- this.listeners = /* @__PURE__ */ new Set();
154
- this.config = DEFAULT_FIELD_CONFIG;
155
- const initialCapacity = DEFAULT_USER_CAPACITY;
156
- const initialState = DEFAULT_EMOTIONAL_STATE;
157
- this.context = {
158
- energy: createFieldValue(deriveEnergyField(initialCapacity)),
159
- attention: createFieldValue(deriveAttentionField(initialCapacity)),
160
- emotionalValence: createFieldValue(deriveEmotionalValenceField(initialState)),
161
- userCapacity: initialCapacity,
162
- emotionalState: initialState
163
- };
164
- }
165
- /**
166
- * Get current ambient context (read-only)
167
- */
168
- getContext() {
169
- return this.context;
170
- }
171
- /**
172
- * Update user capacity (Phase 1 slider system writes here)
173
- */
174
- updateCapacity(capacity) {
175
- const newCapacity = __spreadValues(__spreadValues({}, this.context.userCapacity), capacity);
176
- this.context = __spreadProps(__spreadValues({}, this.context), {
177
- userCapacity: newCapacity,
178
- energy: createFieldValue(deriveEnergyField(newCapacity), this.context.energy),
179
- attention: createFieldValue(deriveAttentionField(newCapacity), this.context.attention)
180
- });
181
- this.notifyListeners();
182
- }
183
- /**
184
- * Update emotional state (Phase 1 slider system writes here)
185
- */
186
- updateEmotionalState(state) {
187
- const newState = __spreadValues(__spreadValues({}, this.context.emotionalState), state);
188
- this.context = __spreadProps(__spreadValues({}, this.context), {
189
- emotionalState: newState,
190
- emotionalValence: createFieldValue(deriveEmotionalValenceField(newState), this.context.emotionalValence)
191
- });
192
- this.notifyListeners();
193
- }
194
- /**
195
- * Subscribe to field changes
196
- */
197
- subscribe(listener) {
198
- this.listeners.add(listener);
199
- return () => {
200
- this.listeners.delete(listener);
201
- };
202
- }
203
- /**
204
- * Notify all listeners of field changes
205
- */
206
- notifyListeners() {
207
- this.listeners.forEach((listener) => {
208
- try {
209
- listener(this.context);
210
- } catch (error) {
211
- console.error("[v0] Field listener error:", error);
212
- }
213
- });
214
- }
215
- /**
216
- * Update field configuration
217
- */
218
- updateConfig(config) {
219
- this.config = __spreadValues(__spreadValues({}, this.config), config);
220
- }
221
- /**
222
- * Get current field configuration
223
- */
224
- getConfig() {
225
- return this.config;
226
- }
227
- };
228
- new FieldManagerClass();
229
-
230
- // lib/capacity/mode.ts
231
- function deriveMode(field) {
232
- var _a;
233
- const lowCognitive = field.cognitive < 0.4;
234
- const highCognitive = field.cognitive > 0.7;
235
- const lowEmotional = field.emotional < 0.4;
236
- const highEmotional = field.emotional > 0.6;
237
- const lowTemporal = field.temporal < 0.4;
238
- const highValence = field.valence > 0.15;
239
- const negValence = field.valence < -0.15;
240
- const density = lowCognitive ? "low" : highCognitive ? "high" : "medium";
241
- const choiceLoad = lowTemporal ? "minimal" : "normal";
242
- const guidance = lowCognitive ? "high" : lowTemporal ? "medium" : "low";
243
- const veryLowEmotional = field.emotional < 0.15;
244
- const motion2 = veryLowEmotional ? "off" : lowEmotional ? "soothing" : highEmotional && highValence ? "expressive" : "subtle";
245
- const contrast = negValence ? "boosted" : "standard";
246
- const focus = motion2 === "off" ? "default" : lowCognitive ? "guided" : !highCognitive ? "gentle" : "default";
247
- const arousal = (_a = field.arousal) != null ? _a : 0.5;
248
- const pace = arousal < 0.35 ? "calm" : arousal > 0.65 ? "activated" : "neutral";
249
- return { density, guidance, motion: motion2, contrast, choiceLoad, focus, pace };
250
- }
251
- function deriveModeLabel(inputs) {
252
- const { cognitive, temporal, emotional } = inputs;
253
- if (cognitive > 0.6 && emotional > 0.6) {
254
- return "Exploratory";
255
- }
256
- if (cognitive < 0.4 && temporal < 0.4) {
257
- return "Minimal";
258
- }
259
- if (cognitive >= 0.55 && temporal >= 0.55) {
260
- return "Focused";
261
- }
262
- return "Calm";
263
- }
264
- function getModeBadgeColor(label) {
265
- switch (label) {
266
- case "Calm":
267
- return "oklch(0.65 0.15 220)";
268
- // Soft blue
269
- case "Focused":
270
- return "oklch(0.68 0.16 45)";
271
- // Primary rust
272
- case "Exploratory":
273
- return "oklch(0.65 0.2 135)";
274
- // Toxic green
275
- case "Minimal":
276
- return "oklch(0.55 0.1 280)";
277
- // Muted purple
278
- default:
279
- return "oklch(0.5 0 0)";
280
- }
281
- }
282
- var CapacityContext = createContext(null);
283
- function useCapacityContext() {
284
- const context = useContext(CapacityContext);
285
- if (!context) {
286
- throw new Error("useCapacityContext must be used within CapacityProvider");
287
- }
288
- return context;
289
- }
290
- function useEnergyField() {
291
- const { context } = useCapacityContext();
292
- return context.energy;
293
- }
294
- function useAttentionField() {
295
- const { context } = useCapacityContext();
296
- return context.attention;
297
- }
298
- function useEmotionalValenceField() {
299
- const { context } = useCapacityContext();
300
- return context.emotionalValence;
301
- }
302
- function useDerivedMode() {
303
- const { context } = useCapacityContext();
304
- const field = {
305
- cognitive: context.userCapacity.cognitive,
306
- temporal: context.userCapacity.temporal,
307
- emotional: context.userCapacity.emotional,
308
- valence: context.emotionalState.valence,
309
- arousal: context.emotionalState.arousal
310
- };
311
- const mode = deriveMode(field);
312
- return { field, mode };
313
- }
314
- function useFeedback() {
315
- const { hapticEnabled, sonicEnabled, setHapticEnabled, setSonicEnabled } = useCapacityContext();
316
- const { mode } = useDerivedMode();
317
- const fire = useCallback((pattern = "tap") => {
318
- if (hapticEnabled) triggerHaptic(pattern);
319
- if (sonicEnabled) playPacedSonic(mode.pace);
320
- }, [hapticEnabled, sonicEnabled, mode.pace]);
321
- return { hapticEnabled, sonicEnabled, setHapticEnabled, setSonicEnabled, fire };
322
- }
323
-
324
- // lib/capacity/animation.ts
325
- var ENTRANCE_PRESETS = {
326
- /** Liquid organic morph -> gentle scale fade -> soft bloom -> none */
327
- morph: { expressive: "morph-fade-in", subtle: "sacred-fade", soothing: "bloom", off: "" },
328
- /** Spinning vortex -> gentle scale fade -> soft bloom -> none */
329
- vortex: { expressive: "vortex-reveal", subtle: "sacred-fade", soothing: "bloom", off: "" },
330
- /** Spiral in from corner -> soft bloom -> soft bloom -> none */
331
- spiral: { expressive: "spiral-in", subtle: "bloom", soothing: "bloom", off: "" }
332
- };
333
- function entranceClass(motion2, preset, hasPlayed) {
334
- return ENTRANCE_PRESETS[preset][motion2];
335
- }
336
- function hoverClass(motion2) {
337
- if (motion2 === "expressive") return "hover-expand";
338
- if (motion2 === "subtle" || motion2 === "soothing") return "hover-lift";
339
- return "";
340
- }
341
- function ambientClass(motion2, type) {
342
- if (motion2 === "expressive") return type;
343
- if (motion2 === "soothing" && (type === "breathe" || type === "float")) return type;
344
- return "";
345
- }
346
- function listItemClass(motion2) {
347
- if (motion2 === "expressive") return "helix-rise";
348
- if (motion2 === "subtle" || motion2 === "soothing") return "sacred-fade";
349
- return "";
350
- }
351
38
  var SLIDER_STYLES = `
352
39
  [data-renge-slider] {
353
40
  -webkit-appearance: none;
@@ -993,7 +680,7 @@ var DEFAULT_CALM_STATE = {
993
680
  function CapacityControls() {
994
681
  var _a;
995
682
  const [isOpen, setIsOpen] = useState(false);
996
- const { updateCapacity, updateEmotionalState, isAutoMode, toggleAutoMode } = useCapacityContext();
683
+ const { updateCapacity, updateEmotionalState, isAutoMode, toggleAutoMode, conflicts } = useCapacityContext();
997
684
  const { hapticEnabled, sonicEnabled, setHapticEnabled, setSonicEnabled, fire: fireFeedback } = useFeedback();
998
685
  const { field, mode } = useDerivedMode();
999
686
  const energy = useEnergyField();
@@ -1016,262 +703,266 @@ function CapacityControls() {
1016
703
  fireFeedback("tap");
1017
704
  }, [fireFeedback]);
1018
705
  return /* @__PURE__ */ jsxs("div", { className: "fixed bottom-4 right-4 z-50", children: [
1019
- /* @__PURE__ */ jsx(AnimatePresence, { children: !isOpen && /* @__PURE__ */ jsxs(
1020
- motion.div,
1021
- {
1022
- initial: { opacity: 0, scale: 0.8 },
1023
- animate: { opacity: 1, scale: 1 },
1024
- exit: { opacity: 0, scale: 0.8 },
1025
- className: "flex items-center gap-2",
1026
- children: [
1027
- /* @__PURE__ */ jsx(
1028
- Badge,
1029
- {
1030
- className: "shadow-lg",
1031
- style: { backgroundColor: modeBadgeColor, color: "white" },
1032
- children: modeLabel
1033
- }
1034
- ),
1035
- /* @__PURE__ */ jsxs(
1036
- Button,
1037
- {
1038
- onClick: () => setIsOpen(true),
1039
- variant: "outline",
1040
- size: "sm",
1041
- className: "shadow-lg bg-background",
1042
- children: [
1043
- /* @__PURE__ */ jsx(SettingsIcon, { className: "w-4 h-4 mr-2" }),
1044
- "Capacity"
1045
- ]
1046
- }
1047
- )
1048
- ]
1049
- }
1050
- ) }),
1051
- /* @__PURE__ */ jsx(AnimatePresence, { children: isOpen && /* @__PURE__ */ jsx(
1052
- motion.div,
706
+ !isOpen && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
707
+ /* @__PURE__ */ jsx(
708
+ Badge,
709
+ {
710
+ className: "shadow-lg",
711
+ style: { backgroundColor: modeBadgeColor, color: "white" },
712
+ children: modeLabel
713
+ }
714
+ ),
715
+ /* @__PURE__ */ jsxs(
716
+ Button,
717
+ {
718
+ onClick: () => setIsOpen(true),
719
+ variant: "outline",
720
+ size: "sm",
721
+ className: "shadow-lg bg-background",
722
+ children: [
723
+ /* @__PURE__ */ jsx(SettingsIcon, { className: "w-4 h-4 mr-2" }),
724
+ "Capacity"
725
+ ]
726
+ }
727
+ )
728
+ ] }),
729
+ isOpen && /* @__PURE__ */ jsx(
730
+ "div",
1053
731
  {
1054
- initial: { opacity: 0 },
1055
- animate: { opacity: 1 },
1056
- exit: { opacity: 0 },
1057
732
  className: "fixed inset-0 bg-black/20 backdrop-blur-sm md:hidden",
1058
733
  onClick: () => setIsOpen(false),
1059
734
  "aria-hidden": "true"
1060
735
  }
1061
- ) }),
1062
- /* @__PURE__ */ jsx(AnimatePresence, { children: isOpen && /* @__PURE__ */ jsx(
1063
- motion.div,
1064
- {
1065
- initial: { opacity: 0, y: 20, scale: 0.95 },
1066
- animate: { opacity: 1, y: 0, scale: 1 },
1067
- exit: { opacity: 0, y: 20, scale: 0.95 },
1068
- transition: { type: "spring", damping: 20, stiffness: 300 },
1069
- className: "relative",
1070
- children: /* @__PURE__ */ jsxs(Card, { className: "w-80 shadow-xl max-h-[85vh] overflow-y-auto", children: [
1071
- /* @__PURE__ */ jsxs(CardHeader, { className: "pb-3", children: [
1072
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-2", children: [
1073
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1074
- /* @__PURE__ */ jsx(CardTitle, { className: "text-sm font-semibold", children: "Capacity Controls" }),
1075
- /* @__PURE__ */ jsx(
1076
- Button,
1077
- {
1078
- variant: "ghost",
1079
- size: "icon",
1080
- className: "h-8 w-8 shrink-0",
1081
- onClick: (e) => {
1082
- e.stopPropagation();
1083
- setIsOpen(false);
1084
- },
1085
- "aria-label": "Close capacity controls",
1086
- children: /* @__PURE__ */ jsx(CloseIcon, { className: "w-4 h-4" })
1087
- }
1088
- )
1089
- ] }),
1090
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1091
- /* @__PURE__ */ jsx(
1092
- Badge,
1093
- {
1094
- className: "text-xs",
1095
- style: { backgroundColor: modeBadgeColor, color: "white" },
1096
- children: modeLabel
1097
- }
1098
- ),
1099
- /* @__PURE__ */ jsx(
1100
- Button,
1101
- {
1102
- variant: isAutoMode ? "default" : "outline",
1103
- size: "sm",
1104
- className: "h-7 text-xs px-2",
1105
- onClick: toggleAutoMode,
1106
- "aria-label": isAutoMode ? "Switch to manual mode" : "Switch to auto mode",
1107
- children: isAutoMode ? "Auto" : "Manual"
1108
- }
1109
- )
1110
- ] })
1111
- ] }),
1112
- /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: isAutoMode ? "Signals are driving values automatically. Move any slider to take manual control." : "Adjust your state to see the UI adapt in real-time." })
1113
- ] }),
1114
- /* @__PURE__ */ jsxs(CardContent, { className: "space-y-6", children: [
1115
- /* @__PURE__ */ jsxs("div", { className: "space-y-2 flex flex-col gap-2", children: [
1116
- /* @__PURE__ */ jsx("label", { className: "text-sm font-medium", children: "Quick Presets" }),
1117
- /* @__PURE__ */ jsxs(
1118
- Select,
1119
- {
1120
- defaultValue: "",
1121
- onValueChange: (value) => {
1122
- if (!value) return;
1123
- const preset = CAPACITY_PRESETS[value];
1124
- updateCapacity({
1125
- cognitive: preset.cognitive,
1126
- temporal: preset.temporal,
1127
- emotional: preset.emotional
1128
- });
1129
- updateEmotionalState({ valence: preset.valence, arousal: preset.arousal });
1130
- fireInteractionFeedback();
1131
- },
1132
- children: [
1133
- /* @__PURE__ */ jsx("option", { value: "", disabled: true, children: "Select a preset..." }),
1134
- Object.entries(CAPACITY_PRESETS).map(([key, preset]) => /* @__PURE__ */ jsxs("option", { value: key, children: [
1135
- preset.label,
1136
- " \u2014 ",
1137
- preset.description
1138
- ] }, key))
1139
- ]
1140
- }
1141
- )
1142
- ] }),
1143
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between border-t border-border pt-4", children: [
1144
- /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Or adjust individually:" }),
1145
- /* @__PURE__ */ jsxs(
1146
- Button,
1147
- {
1148
- variant: "ghost",
1149
- size: "sm",
1150
- onClick: handleReset,
1151
- className: "h-7 text-xs text-muted-foreground hover:text-foreground",
1152
- children: [
1153
- /* @__PURE__ */ jsx(ResetIcon, { className: "w-3 h-3 mr-1" }),
1154
- "Reset"
1155
- ]
1156
- }
1157
- )
1158
- ] }),
736
+ ),
737
+ isOpen && /* @__PURE__ */ jsx("div", { className: "relative", children: /* @__PURE__ */ jsxs(Card, { className: "w-80 shadow-xl max-h-[85vh] overflow-y-auto", children: [
738
+ /* @__PURE__ */ jsxs(CardHeader, { className: "pb-3", children: [
739
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-2", children: [
740
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
741
+ /* @__PURE__ */ jsx(CardTitle, { className: "text-sm font-semibold", children: "Capacity Controls" }),
1159
742
  /* @__PURE__ */ jsx(
1160
- SliderControl,
743
+ Button,
1161
744
  {
1162
- label: "Cognitive Capacity",
1163
- description: "Controls: density, hierarchy, concurrency",
1164
- value: field.cognitive,
1165
- onChange: (v) => updateCapacity({ cognitive: v }),
1166
- lowLabel: "Fewer items",
1167
- highLabel: "More items"
745
+ variant: "ghost",
746
+ size: "icon",
747
+ className: "h-8 w-8 shrink-0",
748
+ onClick: (e) => {
749
+ e.stopPropagation();
750
+ setIsOpen(false);
751
+ },
752
+ "aria-label": "Close capacity controls",
753
+ children: /* @__PURE__ */ jsx(CloseIcon, { className: "w-4 h-4" })
1168
754
  }
1169
- ),
755
+ )
756
+ ] }),
757
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1170
758
  /* @__PURE__ */ jsx(
1171
- SliderControl,
759
+ Badge,
1172
760
  {
1173
- label: "Temporal Capacity",
1174
- description: "Controls: content length, shortcuts, defaults",
1175
- value: field.temporal,
1176
- onChange: (v) => updateCapacity({ temporal: v }),
1177
- lowLabel: "Abbreviated",
1178
- highLabel: "Full detail"
761
+ className: "text-xs",
762
+ style: { backgroundColor: modeBadgeColor, color: "white" },
763
+ children: modeLabel
1179
764
  }
1180
765
  ),
1181
766
  /* @__PURE__ */ jsx(
1182
- SliderControl,
767
+ Button,
1183
768
  {
1184
- label: "Emotional Capacity",
1185
- description: "Controls: motion restraint, friction",
1186
- value: field.emotional,
1187
- onChange: (v) => updateCapacity({ emotional: v }),
1188
- lowLabel: "Calm UI",
1189
- highLabel: "Expressive"
769
+ variant: isAutoMode ? "default" : "outline",
770
+ size: "sm",
771
+ className: "h-7 text-xs px-2",
772
+ onClick: toggleAutoMode,
773
+ "aria-label": isAutoMode ? "Switch to manual mode" : "Switch to auto mode",
774
+ children: isAutoMode ? "Auto" : "Manual"
1190
775
  }
1191
- ),
1192
- /* @__PURE__ */ jsx("div", { className: "pt-2 border-t border-border", children: /* @__PURE__ */ jsx(
1193
- ValenceSliderControl,
776
+ )
777
+ ] })
778
+ ] }),
779
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: isAutoMode ? "Signals are driving values automatically. Move any slider to take manual control." : "Adjust your state to see the UI adapt in real-time." })
780
+ ] }),
781
+ /* @__PURE__ */ jsxs(CardContent, { className: "space-y-6", children: [
782
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2 flex flex-col gap-2", children: [
783
+ /* @__PURE__ */ jsx("label", { className: "text-sm font-medium", children: "Quick Presets" }),
784
+ /* @__PURE__ */ jsxs(
785
+ Select,
786
+ {
787
+ defaultValue: "",
788
+ onValueChange: (value) => {
789
+ if (!value) return;
790
+ const preset = CAPACITY_PRESETS[value];
791
+ updateCapacity({
792
+ cognitive: preset.cognitive,
793
+ temporal: preset.temporal,
794
+ emotional: preset.emotional
795
+ });
796
+ updateEmotionalState({ valence: preset.valence, arousal: preset.arousal });
797
+ fireInteractionFeedback();
798
+ },
799
+ children: [
800
+ /* @__PURE__ */ jsx("option", { value: "", disabled: true, children: "Select a preset..." }),
801
+ Object.entries(CAPACITY_PRESETS).map(([key, preset]) => /* @__PURE__ */ jsxs("option", { value: key, children: [
802
+ preset.label,
803
+ " \u2014 ",
804
+ preset.description
805
+ ] }, key))
806
+ ]
807
+ }
808
+ )
809
+ ] }),
810
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between border-t border-border pt-4", children: [
811
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Or adjust individually:" }),
812
+ /* @__PURE__ */ jsxs(
813
+ Button,
814
+ {
815
+ variant: "ghost",
816
+ size: "sm",
817
+ onClick: handleReset,
818
+ className: "h-7 text-xs text-muted-foreground hover:text-foreground",
819
+ children: [
820
+ /* @__PURE__ */ jsx(ResetIcon, { className: "w-3 h-3 mr-1" }),
821
+ "Reset"
822
+ ]
823
+ }
824
+ )
825
+ ] }),
826
+ /* @__PURE__ */ jsx(
827
+ SliderControl,
828
+ {
829
+ label: "Cognitive Capacity",
830
+ description: "Controls: density, hierarchy, concurrency",
831
+ value: field.cognitive,
832
+ onChange: (v) => updateCapacity({ cognitive: v }),
833
+ lowLabel: "Fewer items",
834
+ highLabel: "More items"
835
+ }
836
+ ),
837
+ /* @__PURE__ */ jsx(
838
+ SliderControl,
839
+ {
840
+ label: "Temporal Capacity",
841
+ description: "Controls: content length, shortcuts, defaults",
842
+ value: field.temporal,
843
+ onChange: (v) => updateCapacity({ temporal: v }),
844
+ lowLabel: "Abbreviated",
845
+ highLabel: "Full detail"
846
+ }
847
+ ),
848
+ /* @__PURE__ */ jsx(
849
+ SliderControl,
850
+ {
851
+ label: "Emotional Capacity",
852
+ description: "Controls: motion restraint, friction",
853
+ value: field.emotional,
854
+ onChange: (v) => updateCapacity({ emotional: v }),
855
+ lowLabel: "Calm UI",
856
+ highLabel: "Expressive"
857
+ }
858
+ ),
859
+ /* @__PURE__ */ jsx("div", { className: "pt-2 border-t border-border", children: /* @__PURE__ */ jsx(
860
+ ValenceSliderControl,
861
+ {
862
+ label: "Emotional Valence",
863
+ description: "Controls: tone, expressiveness (not info volume)",
864
+ value: field.valence,
865
+ onChange: (v) => updateEmotionalState({ valence: v })
866
+ }
867
+ ) }),
868
+ /* @__PURE__ */ jsx(
869
+ SliderControl,
870
+ {
871
+ label: "Arousal",
872
+ description: "Controls: animation pacing (calm \u2192 activated)",
873
+ value: (_a = field.arousal) != null ? _a : 0.5,
874
+ onChange: (v) => updateEmotionalState({ arousal: v }),
875
+ lowLabel: "Calm",
876
+ highLabel: "Activated"
877
+ }
878
+ ),
879
+ /* @__PURE__ */ jsxs("div", { className: "pt-2 border-t border-border space-y-2", children: [
880
+ /* @__PURE__ */ jsxs("p", { className: "text-xs font-medium text-muted-foreground", children: [
881
+ "Feedback ",
882
+ /* @__PURE__ */ jsx("span", { className: "font-normal opacity-60", children: "(opt-in)" })
883
+ ] }),
884
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
885
+ /* @__PURE__ */ jsx(
886
+ "button",
1194
887
  {
1195
- label: "Emotional Valence",
1196
- description: "Controls: tone, expressiveness (not info volume)",
1197
- value: field.valence,
1198
- onChange: (v) => updateEmotionalState({ valence: v })
888
+ onClick: () => setHapticEnabled((v) => !v),
889
+ className: `flex-1 py-1.5 px-2 rounded-md text-xs border transition-colors ${hapticEnabled ? "bg-primary/10 border-primary/50 text-primary" : "border-border text-muted-foreground hover:text-foreground"}`,
890
+ "aria-pressed": hapticEnabled,
891
+ children: "\u{1F4F3} Haptic"
1199
892
  }
1200
- ) }),
893
+ ),
1201
894
  /* @__PURE__ */ jsx(
1202
- SliderControl,
895
+ "button",
1203
896
  {
1204
- label: "Arousal",
1205
- description: "Controls: animation pacing (calm \u2192 activated)",
1206
- value: (_a = field.arousal) != null ? _a : 0.5,
1207
- onChange: (v) => updateEmotionalState({ arousal: v }),
1208
- lowLabel: "Calm",
1209
- highLabel: "Activated"
897
+ onClick: () => setSonicEnabled((v) => !v),
898
+ className: `flex-1 py-1.5 px-2 rounded-md text-xs border transition-colors ${sonicEnabled ? "bg-primary/10 border-primary/50 text-primary" : "border-border text-muted-foreground hover:text-foreground"}`,
899
+ "aria-pressed": sonicEnabled,
900
+ children: "\u{1F514} Sonic"
1210
901
  }
1211
- ),
1212
- /* @__PURE__ */ jsxs("div", { className: "pt-2 border-t border-border space-y-2", children: [
1213
- /* @__PURE__ */ jsxs("p", { className: "text-xs font-medium text-muted-foreground", children: [
1214
- "Feedback ",
1215
- /* @__PURE__ */ jsx("span", { className: "font-normal opacity-60", children: "(opt-in)" })
1216
- ] }),
1217
- /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
1218
- /* @__PURE__ */ jsx(
1219
- "button",
1220
- {
1221
- onClick: () => setHapticEnabled((v) => !v),
1222
- className: `flex-1 py-1.5 px-2 rounded-md text-xs border transition-colors ${hapticEnabled ? "bg-primary/10 border-primary/50 text-primary" : "border-border text-muted-foreground hover:text-foreground"}`,
1223
- "aria-pressed": hapticEnabled,
1224
- children: "\u{1F4F3} Haptic"
1225
- }
1226
- ),
1227
- /* @__PURE__ */ jsx(
1228
- "button",
1229
- {
1230
- onClick: () => setSonicEnabled((v) => !v),
1231
- className: `flex-1 py-1.5 px-2 rounded-md text-xs border transition-colors ${sonicEnabled ? "bg-primary/10 border-primary/50 text-primary" : "border-border text-muted-foreground hover:text-foreground"}`,
1232
- "aria-pressed": sonicEnabled,
1233
- children: "\u{1F514} Sonic"
1234
- }
1235
- )
1236
- ] }),
1237
- /* @__PURE__ */ jsxs("p", { className: "text-[10px] text-muted-foreground opacity-60", children: [
1238
- "Pace: ",
1239
- /* @__PURE__ */ jsx("span", { className: "font-medium", children: mode.pace }),
1240
- " \u2192 ",
1241
- mode.pace === "calm" ? "+50% duration" : mode.pace === "activated" ? "\u221235% duration" : "standard"
1242
- ] })
1243
- ] }),
1244
- /* @__PURE__ */ jsxs("div", { className: "pt-4 border-t border-border", children: [
1245
- /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground mb-2", children: "Derived Fields" }),
1246
- /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-3 gap-2 text-center", children: [
1247
- /* @__PURE__ */ jsx(FieldDisplay, { label: "Energy", value: energy.value, color: "text-chart-1" }),
1248
- /* @__PURE__ */ jsx(FieldDisplay, { label: "Attention", value: attention.value, color: "text-chart-2" }),
1249
- /* @__PURE__ */ jsx(FieldDisplay, { label: "Valence", value: valence.value, color: "text-chart-3", signed: true })
1250
- ] })
1251
- ] }),
1252
- /* @__PURE__ */ jsxs("div", { className: "pt-4 border-t border-border", children: [
1253
- /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground mb-2", children: "Interface Mode" }),
1254
- /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-1 text-xs", children: [
1255
- /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Density:" }),
1256
- /* @__PURE__ */ jsx("span", { className: "font-medium", children: mode.density }),
1257
- /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Guidance:" }),
1258
- /* @__PURE__ */ jsx("span", { className: "font-medium", children: mode.guidance }),
1259
- /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Motion:" }),
1260
- /* @__PURE__ */ jsx("span", { className: "font-medium", children: mode.motion }),
1261
- /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Contrast:" }),
1262
- /* @__PURE__ */ jsx("span", { className: "font-medium", children: mode.contrast }),
1263
- /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Choices:" }),
1264
- /* @__PURE__ */ jsx("span", { className: "font-medium", children: mode.choiceLoad }),
1265
- /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Focus:" }),
1266
- /* @__PURE__ */ jsx("span", { className: "font-medium", children: mode.focus }),
1267
- /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Pace:" }),
1268
- /* @__PURE__ */ jsx("span", { className: "font-medium", children: mode.pace })
1269
- ] })
902
+ )
903
+ ] }),
904
+ /* @__PURE__ */ jsxs("p", { className: "text-[10px] text-muted-foreground opacity-60", children: [
905
+ "Pace: ",
906
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: mode.pace }),
907
+ " \u2192 ",
908
+ mode.pace === "calm" ? "+50% duration" : mode.pace === "activated" ? "\u221235% duration" : "standard"
909
+ ] })
910
+ ] }),
911
+ /* @__PURE__ */ jsxs("div", { className: "pt-4 border-t border-border", children: [
912
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground mb-2", children: "Derived Fields" }),
913
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-3 gap-2 text-center", children: [
914
+ /* @__PURE__ */ jsx(FieldDisplay, { label: "Energy", value: energy.value, color: "text-chart-1" }),
915
+ /* @__PURE__ */ jsx(FieldDisplay, { label: "Attention", value: attention.value, color: "text-chart-2" }),
916
+ /* @__PURE__ */ jsx(FieldDisplay, { label: "Valence", value: valence.value, color: "text-chart-3", signed: true })
917
+ ] })
918
+ ] }),
919
+ conflicts.length > 0 && /* @__PURE__ */ jsxs("div", { className: "pt-4 border-t border-border space-y-2", children: [
920
+ /* @__PURE__ */ jsxs("p", { className: "text-xs font-medium text-muted-foreground", children: [
921
+ "Conflicts ",
922
+ /* @__PURE__ */ jsxs("span", { className: "font-normal opacity-60", children: [
923
+ "(",
924
+ conflicts.length,
925
+ ")"
1270
926
  ] })
927
+ ] }),
928
+ conflicts.map((c) => /* @__PURE__ */ jsxs(
929
+ "div",
930
+ {
931
+ className: `rounded-md p-2 text-xs space-y-1 ${c.severity === "warning" ? "bg-warning/10 border border-warning/30 text-warning-content" : "bg-muted/60 border border-border text-muted-foreground"}`,
932
+ children: [
933
+ /* @__PURE__ */ jsx("p", { className: "font-medium", children: c.label }),
934
+ /* @__PURE__ */ jsx("p", { className: "opacity-80 leading-snug", children: c.message }),
935
+ c.suggestion && /* @__PURE__ */ jsx("p", { className: "opacity-60 italic", children: c.suggestion }),
936
+ c.affectedTokens.length > 0 && /* @__PURE__ */ jsxs("p", { className: "opacity-50", children: [
937
+ "Affects: ",
938
+ c.affectedTokens.join(", ")
939
+ ] })
940
+ ]
941
+ },
942
+ c.id
943
+ ))
944
+ ] }),
945
+ /* @__PURE__ */ jsxs("div", { className: "pt-4 border-t border-border", children: [
946
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground mb-2", children: "Interface Mode" }),
947
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-1 text-xs", children: [
948
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Density:" }),
949
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: mode.density }),
950
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Guidance:" }),
951
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: mode.guidance }),
952
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Motion:" }),
953
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: mode.motion }),
954
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Contrast:" }),
955
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: mode.contrast }),
956
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Choices:" }),
957
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: mode.choiceLoad }),
958
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Focus:" }),
959
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: mode.focus }),
960
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Pace:" }),
961
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: mode.pace })
1271
962
  ] })
1272
963
  ] })
1273
- }
1274
- ) })
964
+ ] })
965
+ ] }) })
1275
966
  ] });
1276
967
  }
1277
968
  function SliderControl({
@@ -1410,7 +1101,7 @@ function CapacityDemoCard() {
1410
1101
  const temporalContent = field.temporal > 0.4 ? TEMPORAL_CONTENT.full : TEMPORAL_CONTENT.abbreviated;
1411
1102
  const toneKey = field.valence > 0.2 ? "positive" : field.valence < -0.2 ? "negative" : "neutral";
1412
1103
  const tone = TONE[toneKey];
1413
- const entrance = entranceClass(mode.motion, "morph");
1104
+ const entrance = entranceClass(mode.motion, "morph", false);
1414
1105
  const hover = hoverClass(mode.motion);
1415
1106
  const visibleFeatures = temporalContent.features.slice(0, densityContent.featureCount);
1416
1107
  return /* @__PURE__ */ jsxs(