@harmonia-core/ui 1.0.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 (77) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +267 -0
  3. package/dist/capacity/animation.d.ts +77 -0
  4. package/dist/capacity/animation.d.ts.map +1 -0
  5. package/dist/capacity/constants.d.ts +119 -0
  6. package/dist/capacity/constants.d.ts.map +1 -0
  7. package/dist/capacity/feedback.d.ts +55 -0
  8. package/dist/capacity/feedback.d.ts.map +1 -0
  9. package/dist/capacity/fields/field-manager.d.ts +45 -0
  10. package/dist/capacity/fields/field-manager.d.ts.map +1 -0
  11. package/dist/capacity/index.d.ts +15 -0
  12. package/dist/capacity/index.d.ts.map +1 -0
  13. package/dist/capacity/index.js +1313 -0
  14. package/dist/capacity/index.js.map +1 -0
  15. package/dist/capacity/index.mjs +1267 -0
  16. package/dist/capacity/index.mjs.map +1 -0
  17. package/dist/capacity/mode.d.ts +50 -0
  18. package/dist/capacity/mode.d.ts.map +1 -0
  19. package/dist/capacity/prediction/hooks.d.ts +11 -0
  20. package/dist/capacity/prediction/hooks.d.ts.map +1 -0
  21. package/dist/capacity/prediction/pattern-extractor.d.ts +26 -0
  22. package/dist/capacity/prediction/pattern-extractor.d.ts.map +1 -0
  23. package/dist/capacity/prediction/pattern-store.d.ts +35 -0
  24. package/dist/capacity/prediction/pattern-store.d.ts.map +1 -0
  25. package/dist/capacity/prediction/prediction-engine.d.ts +39 -0
  26. package/dist/capacity/prediction/prediction-engine.d.ts.map +1 -0
  27. package/dist/capacity/prediction/types.d.ts +24 -0
  28. package/dist/capacity/prediction/types.d.ts.map +1 -0
  29. package/dist/capacity/provider.d.ts +119 -0
  30. package/dist/capacity/provider.d.ts.map +1 -0
  31. package/dist/capacity/signals/aggregator.d.ts +38 -0
  32. package/dist/capacity/signals/aggregator.d.ts.map +1 -0
  33. package/dist/capacity/signals/detectors/environment-detector.d.ts +31 -0
  34. package/dist/capacity/signals/detectors/environment-detector.d.ts.map +1 -0
  35. package/dist/capacity/signals/detectors/input-detector.d.ts +23 -0
  36. package/dist/capacity/signals/detectors/input-detector.d.ts.map +1 -0
  37. package/dist/capacity/signals/detectors/interaction-detector.d.ts +27 -0
  38. package/dist/capacity/signals/detectors/interaction-detector.d.ts.map +1 -0
  39. package/dist/capacity/signals/detectors/scroll-detector.d.ts +35 -0
  40. package/dist/capacity/signals/detectors/scroll-detector.d.ts.map +1 -0
  41. package/dist/capacity/signals/detectors/session-detector.d.ts +23 -0
  42. package/dist/capacity/signals/detectors/session-detector.d.ts.map +1 -0
  43. package/dist/capacity/signals/detectors/time-detector.d.ts +20 -0
  44. package/dist/capacity/signals/detectors/time-detector.d.ts.map +1 -0
  45. package/dist/capacity/signals/detectors/types.d.ts +25 -0
  46. package/dist/capacity/signals/detectors/types.d.ts.map +1 -0
  47. package/dist/capacity/signals/signal-bus.d.ts +50 -0
  48. package/dist/capacity/signals/signal-bus.d.ts.map +1 -0
  49. package/dist/capacity/types.d.ts +239 -0
  50. package/dist/capacity/types.d.ts.map +1 -0
  51. package/dist/capacity/utils/index.d.ts +7 -0
  52. package/dist/capacity/utils/index.d.ts.map +1 -0
  53. package/dist/capacity/utils/typography.d.ts +176 -0
  54. package/dist/capacity/utils/typography.d.ts.map +1 -0
  55. package/dist/components/ambient-field-monitor.d.ts +10 -0
  56. package/dist/components/ambient-field-monitor.d.ts.map +1 -0
  57. package/dist/components/capacity-controls.d.ts +15 -0
  58. package/dist/components/capacity-controls.d.ts.map +1 -0
  59. package/dist/components/capacity-demo-card.d.ts +13 -0
  60. package/dist/components/capacity-demo-card.d.ts.map +1 -0
  61. package/dist/components/index.d.ts +18 -0
  62. package/dist/components/index.d.ts.map +1 -0
  63. package/dist/components/index.js +1703 -0
  64. package/dist/components/index.js.map +1 -0
  65. package/dist/components/index.mjs +1688 -0
  66. package/dist/components/index.mjs.map +1 -0
  67. package/dist/components/ui/badge.d.ts +8 -0
  68. package/dist/components/ui/badge.d.ts.map +1 -0
  69. package/dist/components/ui/button.d.ts +10 -0
  70. package/dist/components/ui/button.d.ts.map +1 -0
  71. package/dist/components/ui/card.d.ts +10 -0
  72. package/dist/components/ui/card.d.ts.map +1 -0
  73. package/dist/components/ui/select.d.ts +6 -0
  74. package/dist/components/ui/select.d.ts.map +1 -0
  75. package/dist/components/ui/slider.d.ts +14 -0
  76. package/dist/components/ui/slider.d.ts.map +1 -0
  77. package/package.json +98 -0
@@ -0,0 +1,1267 @@
1
+ import { createContext, useState, useRef, useEffect, useCallback, useContext } from 'react';
2
+ import { jsx } from 'react/jsx-runtime';
3
+
4
+ var __defProp = Object.defineProperty;
5
+ var __defProps = Object.defineProperties;
6
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
7
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
10
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
11
+ var __spreadValues = (a, b) => {
12
+ for (var prop in b || (b = {}))
13
+ if (__hasOwnProp.call(b, prop))
14
+ __defNormalProp(a, prop, b[prop]);
15
+ if (__getOwnPropSymbols)
16
+ for (var prop of __getOwnPropSymbols(b)) {
17
+ if (__propIsEnum.call(b, prop))
18
+ __defNormalProp(a, prop, b[prop]);
19
+ }
20
+ return a;
21
+ };
22
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
23
+ var __async = (__this, __arguments, generator) => {
24
+ return new Promise((resolve, reject) => {
25
+ var fulfilled = (value) => {
26
+ try {
27
+ step(generator.next(value));
28
+ } catch (e) {
29
+ reject(e);
30
+ }
31
+ };
32
+ var rejected = (value) => {
33
+ try {
34
+ step(generator.throw(value));
35
+ } catch (e) {
36
+ reject(e);
37
+ }
38
+ };
39
+ var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
40
+ step((generator = generator.apply(__this, __arguments)).next());
41
+ });
42
+ };
43
+
44
+ // lib/capacity/constants.ts
45
+ var PHI = 1.618033988749895;
46
+ var PHI_INVERSE = 0.618033988749895;
47
+ var FIBONACCI = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144];
48
+ var FEEDBACK_FREQUENCIES = {
49
+ low: 396,
50
+ // Foundation/root elements
51
+ mid: 528,
52
+ // Primary interactive content
53
+ high: 741
54
+ // Dynamic/feedback elements
55
+ };
56
+ var DEFAULT_FIELD_CONFIG = {
57
+ smoothing: 0.15,
58
+ // Exponential smoothing factor
59
+ velocityThreshold: 0.05,
60
+ // Min velocity to register as trend
61
+ debounceMs: 100
62
+ // Debounce rapid changes
63
+ };
64
+ var DEFAULT_USER_CAPACITY = {
65
+ cognitive: 0.7,
66
+ temporal: 0.7,
67
+ emotional: 0.7
68
+ };
69
+ var DEFAULT_EMOTIONAL_STATE = {
70
+ valence: 0.3,
71
+ // > 0.15 (with emotional > 0.6) triggers expressive motion mode
72
+ arousal: 0.5
73
+ };
74
+ var DEFAULT_COMPONENT_RESPONSE = {
75
+ visual: {
76
+ opacityRange: [0.4, 1],
77
+ scaleRange: [0.95, 1]
78
+ },
79
+ spatial: {
80
+ densityRange: [0.6, 1],
81
+ spacingMultiplier: [1, PHI]
82
+ },
83
+ sonic: {
84
+ enabled: false
85
+ // Opt-in
86
+ },
87
+ semantic: {
88
+ verbosityLevel: "concise",
89
+ urgencyFraming: "neutral"
90
+ }
91
+ };
92
+ var MOTION_TOKENS = {
93
+ off: {
94
+ durationFast: 0,
95
+ durationBase: 0,
96
+ durationSlow: 0,
97
+ easing: "linear",
98
+ // Essential transitions still allowed (opacity, focus rings)
99
+ essentialDuration: 100,
100
+ essentialEasing: "ease-out"
101
+ },
102
+ soothing: {
103
+ durationFast: 0,
104
+ // No fast motion -- everything is slow and rhythmic
105
+ durationBase: 800,
106
+ durationSlow: 1200,
107
+ easing: "ease-in-out",
108
+ // Smooth, no sharp edges
109
+ essentialDuration: 200,
110
+ essentialEasing: "ease-in-out"
111
+ },
112
+ subtle: {
113
+ durationFast: 100,
114
+ durationBase: 200,
115
+ durationSlow: 350,
116
+ easing: "ease-out",
117
+ essentialDuration: 150,
118
+ essentialEasing: "ease-out"
119
+ },
120
+ expressive: {
121
+ durationFast: 200,
122
+ durationBase: 400,
123
+ durationSlow: 700,
124
+ easing: "cubic-bezier(0.34, 1.56, 0.64, 1)",
125
+ // Spring-like overshoot
126
+ essentialDuration: 150,
127
+ essentialEasing: "ease-out"
128
+ }
129
+ };
130
+
131
+ // lib/capacity/feedback.ts
132
+ var HAPTIC_PATTERNS = {
133
+ /** Short tap — confirm/select */
134
+ tap: [8],
135
+ /** Two pulses — toggle/switch */
136
+ toggle: [8, 50, 8],
137
+ /** Gentle pulse — ambient/ambient confirmation */
138
+ pulse: [15, 30, 15],
139
+ /** Error/warning — three quick */
140
+ error: [50, 30, 50, 30, 50]
141
+ };
142
+ function triggerHaptic(pattern = "tap") {
143
+ if (typeof navigator !== "undefined" && "vibrate" in navigator) {
144
+ navigator.vibrate(HAPTIC_PATTERNS[pattern]);
145
+ }
146
+ }
147
+ var _audioCtx = null;
148
+ function getAudioContext() {
149
+ if (typeof window === "undefined") return null;
150
+ try {
151
+ if (!_audioCtx || _audioCtx.state === "closed") {
152
+ _audioCtx = new AudioContext();
153
+ }
154
+ if (_audioCtx.state === "suspended") {
155
+ _audioCtx.resume();
156
+ }
157
+ return _audioCtx;
158
+ } catch (e) {
159
+ return null;
160
+ }
161
+ }
162
+ function playSonicFeedback(frequency, duration = 120, volume = 0.06) {
163
+ const ctx = getAudioContext();
164
+ if (!ctx) return;
165
+ const oscillator = ctx.createOscillator();
166
+ const gainNode = ctx.createGain();
167
+ oscillator.connect(gainNode);
168
+ gainNode.connect(ctx.destination);
169
+ oscillator.type = "sine";
170
+ oscillator.frequency.setValueAtTime(frequency, ctx.currentTime);
171
+ gainNode.gain.setValueAtTime(0, ctx.currentTime);
172
+ gainNode.gain.linearRampToValueAtTime(volume, ctx.currentTime + 0.015);
173
+ gainNode.gain.linearRampToValueAtTime(0, ctx.currentTime + duration / 1e3);
174
+ oscillator.start(ctx.currentTime);
175
+ oscillator.stop(ctx.currentTime + duration / 1e3 + 0.02);
176
+ }
177
+ function getFrequencyForPace(pace) {
178
+ if (pace === "activated") return FEEDBACK_FREQUENCIES.high;
179
+ if (pace === "calm") return FEEDBACK_FREQUENCIES.low;
180
+ return FEEDBACK_FREQUENCIES.mid;
181
+ }
182
+ function playPacedSonic(pace, duration) {
183
+ playSonicFeedback(getFrequencyForPace(pace), duration);
184
+ }
185
+
186
+ // lib/capacity/fields/field-manager.ts
187
+ function deriveEnergyField(capacity) {
188
+ const { cognitive, temporal, emotional } = capacity;
189
+ return Math.pow(cognitive * temporal * emotional, 1 / 3);
190
+ }
191
+ function deriveAttentionField(capacity) {
192
+ return 1 - capacity.temporal * 0.5;
193
+ }
194
+ function deriveEmotionalValenceField(state) {
195
+ return state.valence;
196
+ }
197
+ function createFieldValue(value, previousValue) {
198
+ var _a;
199
+ const now = Date.now();
200
+ const lastChange = (_a = previousValue == null ? void 0 : previousValue.lastChange) != null ? _a : now;
201
+ const timeDelta = (now - lastChange) / 1e3;
202
+ let trend = "stable";
203
+ let velocity;
204
+ if (typeof value === "number" && previousValue && typeof previousValue.value === "number") {
205
+ const valueDelta = value - previousValue.value;
206
+ velocity = timeDelta > 0 ? valueDelta / timeDelta : 0;
207
+ if (Math.abs(velocity) > DEFAULT_FIELD_CONFIG.velocityThreshold) {
208
+ trend = velocity > 0 ? "rising" : "falling";
209
+ }
210
+ }
211
+ return {
212
+ value,
213
+ lastChange: now,
214
+ trend,
215
+ velocity
216
+ };
217
+ }
218
+ var FieldManagerClass = class {
219
+ constructor() {
220
+ this.listeners = /* @__PURE__ */ new Set();
221
+ this.config = DEFAULT_FIELD_CONFIG;
222
+ const initialCapacity = DEFAULT_USER_CAPACITY;
223
+ const initialState = DEFAULT_EMOTIONAL_STATE;
224
+ this.context = {
225
+ energy: createFieldValue(deriveEnergyField(initialCapacity)),
226
+ attention: createFieldValue(deriveAttentionField(initialCapacity)),
227
+ emotionalValence: createFieldValue(deriveEmotionalValenceField(initialState)),
228
+ userCapacity: initialCapacity,
229
+ emotionalState: initialState
230
+ };
231
+ }
232
+ /**
233
+ * Get current ambient context (read-only)
234
+ */
235
+ getContext() {
236
+ return this.context;
237
+ }
238
+ /**
239
+ * Update user capacity (Phase 1 slider system writes here)
240
+ */
241
+ updateCapacity(capacity) {
242
+ const newCapacity = __spreadValues(__spreadValues({}, this.context.userCapacity), capacity);
243
+ this.context = __spreadProps(__spreadValues({}, this.context), {
244
+ userCapacity: newCapacity,
245
+ energy: createFieldValue(deriveEnergyField(newCapacity), this.context.energy),
246
+ attention: createFieldValue(deriveAttentionField(newCapacity), this.context.attention)
247
+ });
248
+ this.notifyListeners();
249
+ }
250
+ /**
251
+ * Update emotional state (Phase 1 slider system writes here)
252
+ */
253
+ updateEmotionalState(state) {
254
+ const newState = __spreadValues(__spreadValues({}, this.context.emotionalState), state);
255
+ this.context = __spreadProps(__spreadValues({}, this.context), {
256
+ emotionalState: newState,
257
+ emotionalValence: createFieldValue(deriveEmotionalValenceField(newState), this.context.emotionalValence)
258
+ });
259
+ this.notifyListeners();
260
+ }
261
+ /**
262
+ * Subscribe to field changes
263
+ */
264
+ subscribe(listener) {
265
+ this.listeners.add(listener);
266
+ return () => {
267
+ this.listeners.delete(listener);
268
+ };
269
+ }
270
+ /**
271
+ * Notify all listeners of field changes
272
+ */
273
+ notifyListeners() {
274
+ this.listeners.forEach((listener) => {
275
+ try {
276
+ listener(this.context);
277
+ } catch (error) {
278
+ console.error("[v0] Field listener error:", error);
279
+ }
280
+ });
281
+ }
282
+ /**
283
+ * Update field configuration
284
+ */
285
+ updateConfig(config) {
286
+ this.config = __spreadValues(__spreadValues({}, this.config), config);
287
+ }
288
+ /**
289
+ * Get current field configuration
290
+ */
291
+ getConfig() {
292
+ return this.config;
293
+ }
294
+ };
295
+ var FieldManager = new FieldManagerClass();
296
+
297
+ // lib/capacity/mode.ts
298
+ function deriveMode(field) {
299
+ var _a;
300
+ const lowCognitive = field.cognitive < 0.4;
301
+ const highCognitive = field.cognitive > 0.7;
302
+ const lowEmotional = field.emotional < 0.4;
303
+ const highEmotional = field.emotional > 0.6;
304
+ const lowTemporal = field.temporal < 0.4;
305
+ const highValence = field.valence > 0.15;
306
+ const negValence = field.valence < -0.15;
307
+ const density = lowCognitive ? "low" : highCognitive ? "high" : "medium";
308
+ const choiceLoad = lowTemporal ? "minimal" : "normal";
309
+ const guidance = lowCognitive ? "high" : lowTemporal ? "medium" : "low";
310
+ const veryLowEmotional = field.emotional < 0.15;
311
+ const motion = veryLowEmotional ? "off" : lowEmotional ? "soothing" : highEmotional && highValence ? "expressive" : "subtle";
312
+ const contrast = negValence ? "boosted" : "standard";
313
+ const focus = motion === "off" ? "default" : lowCognitive ? "guided" : !highCognitive ? "gentle" : "default";
314
+ const arousal = (_a = field.arousal) != null ? _a : 0.5;
315
+ const pace = arousal < 0.35 ? "calm" : arousal > 0.65 ? "activated" : "neutral";
316
+ return { density, guidance, motion, contrast, choiceLoad, focus, pace };
317
+ }
318
+ function deriveModeLabel(inputs) {
319
+ const { cognitive, temporal, emotional } = inputs;
320
+ if (cognitive > 0.6 && emotional > 0.6) {
321
+ return "Exploratory";
322
+ }
323
+ if (cognitive < 0.4 && temporal < 0.4) {
324
+ return "Minimal";
325
+ }
326
+ if (cognitive >= 0.55 && temporal >= 0.55) {
327
+ return "Focused";
328
+ }
329
+ return "Calm";
330
+ }
331
+ function getModeBadgeColor(label) {
332
+ switch (label) {
333
+ case "Calm":
334
+ return "oklch(0.65 0.15 220)";
335
+ // Soft blue
336
+ case "Focused":
337
+ return "oklch(0.68 0.16 45)";
338
+ // Primary rust
339
+ case "Exploratory":
340
+ return "oklch(0.65 0.2 135)";
341
+ // Toxic green
342
+ case "Minimal":
343
+ return "oklch(0.55 0.1 280)";
344
+ // Muted purple
345
+ default:
346
+ return "oklch(0.5 0 0)";
347
+ }
348
+ }
349
+
350
+ // lib/capacity/signals/detectors/time-detector.ts
351
+ var TimeDetector = class {
352
+ constructor() {
353
+ this.name = "TimeDetector";
354
+ this.weight = 0.6;
355
+ }
356
+ // Medium weight — time is significant but a broad generalisation
357
+ /**
358
+ * Detects and returns SignalReadings based on the current time and day.
359
+ * Returns two readings: cognitive (hour-of-day) and temporal (weekday/weekend).
360
+ */
361
+ detect() {
362
+ const now = /* @__PURE__ */ new Date();
363
+ const hour = now.getHours();
364
+ const dayOfWeek = now.getDay();
365
+ const ts = now.getTime();
366
+ let cognitiveValue;
367
+ if (hour >= 9 && hour < 12) {
368
+ cognitiveValue = 0.8;
369
+ } else if (hour >= 14 && hour < 17) {
370
+ cognitiveValue = 0.6;
371
+ } else if (hour >= 17 && hour < 20) {
372
+ cognitiveValue = 0.5;
373
+ } else if (hour >= 20 || hour < 6) {
374
+ cognitiveValue = 0.3;
375
+ } else {
376
+ cognitiveValue = 0.7;
377
+ }
378
+ const temporalValue = dayOfWeek >= 1 && dayOfWeek <= 5 ? 0.7 : 0.9;
379
+ return [
380
+ {
381
+ dimension: "cognitive",
382
+ value: cognitiveValue,
383
+ confidence: 0.7,
384
+ // Medium — population average, not personalised
385
+ timestamp: ts,
386
+ detectorName: this.name
387
+ },
388
+ {
389
+ dimension: "temporal",
390
+ value: temporalValue,
391
+ confidence: 0.6,
392
+ // Slightly lower — weekday/weekend is a coarser signal
393
+ timestamp: ts,
394
+ detectorName: this.name
395
+ }
396
+ ];
397
+ }
398
+ };
399
+
400
+ // lib/capacity/signals/detectors/session-detector.ts
401
+ var SessionDetector = class {
402
+ constructor() {
403
+ this.name = "SessionDetector";
404
+ this.weight = 0.7;
405
+ this.sessionStartTime = Date.now();
406
+ }
407
+ /**
408
+ * Detects and returns a SignalReading based on the current session duration.
409
+ * It provides insights into the temporal dimension.
410
+ *
411
+ * @returns {SignalReading} A reading indicating the inferred capacity.
412
+ */
413
+ detect() {
414
+ const now = Date.now();
415
+ const sessionDurationMinutes = (now - this.sessionStartTime) / (1e3 * 60);
416
+ let temporalValue;
417
+ let confidence;
418
+ if (sessionDurationMinutes < 15) {
419
+ temporalValue = 0.9;
420
+ confidence = 0.8;
421
+ } else if (sessionDurationMinutes < 60) {
422
+ temporalValue = 0.7;
423
+ confidence = 0.7;
424
+ } else if (sessionDurationMinutes < 180) {
425
+ temporalValue = 0.5;
426
+ confidence = 0.6;
427
+ } else {
428
+ temporalValue = 0.3;
429
+ confidence = 0.7;
430
+ }
431
+ return [{
432
+ dimension: "temporal",
433
+ value: temporalValue,
434
+ confidence,
435
+ timestamp: now,
436
+ detectorName: this.name
437
+ }];
438
+ }
439
+ };
440
+
441
+ // lib/capacity/signals/detectors/scroll-detector.ts
442
+ var DEBOUNCE_TIME_MS = 100;
443
+ var ScrollDetector = class {
444
+ constructor() {
445
+ this.name = "ScrollDetector";
446
+ this.weight = 0.5;
447
+ // Moderate weight, as scroll velocity can indicate engagement or frustration
448
+ this.lastScrollY = 0;
449
+ this.lastScrollTime = 0;
450
+ this.scrollVelocity = 0;
451
+ this.timeoutId = null;
452
+ /**
453
+ * Handles the scroll event, debouncing it and calculating scroll velocity.
454
+ * @private
455
+ */
456
+ this.handleScroll = () => {
457
+ if (this.timeoutId) {
458
+ clearTimeout(this.timeoutId);
459
+ }
460
+ this.timeoutId = setTimeout(() => {
461
+ const now = Date.now();
462
+ const scrollY = window.scrollY;
463
+ const distance = Math.abs(scrollY - this.lastScrollY);
464
+ const timeElapsed = now - this.lastScrollTime;
465
+ if (timeElapsed > 0) {
466
+ this.scrollVelocity = distance / timeElapsed * 1e3;
467
+ } else {
468
+ this.scrollVelocity = 0;
469
+ }
470
+ this.lastScrollY = scrollY;
471
+ this.lastScrollTime = now;
472
+ }, DEBOUNCE_TIME_MS);
473
+ };
474
+ if (typeof window !== "undefined") {
475
+ window.addEventListener("scroll", this.handleScroll, { passive: true });
476
+ }
477
+ }
478
+ /**
479
+ * Detects and returns a SignalReading based on the current scroll velocity.
480
+ * It provides insights into the cognitive dimension.
481
+ *
482
+ * @returns {SignalReading} A reading indicating the inferred capacity.
483
+ */
484
+ detect() {
485
+ const now = Date.now();
486
+ let cognitiveValue;
487
+ let confidence;
488
+ if (this.scrollVelocity > 1500) {
489
+ cognitiveValue = 0.4;
490
+ confidence = 0.6;
491
+ } else if (this.scrollVelocity > 500) {
492
+ cognitiveValue = 0.7;
493
+ confidence = 0.8;
494
+ } else if (this.scrollVelocity > 50) {
495
+ cognitiveValue = 0.6;
496
+ confidence = 0.7;
497
+ } else {
498
+ cognitiveValue = 0.5;
499
+ confidence = 0.5;
500
+ }
501
+ return [{
502
+ dimension: "cognitive",
503
+ value: cognitiveValue,
504
+ confidence,
505
+ timestamp: now,
506
+ detectorName: this.name
507
+ }];
508
+ }
509
+ /**
510
+ * Cleans up the scroll event listener when the detector is no longer needed.
511
+ */
512
+ destroy() {
513
+ if (typeof window !== "undefined") {
514
+ window.removeEventListener("scroll", this.handleScroll);
515
+ if (this.timeoutId) {
516
+ clearTimeout(this.timeoutId);
517
+ }
518
+ }
519
+ }
520
+ };
521
+
522
+ // lib/capacity/signals/detectors/interaction-detector.ts
523
+ var IDLE_THRESHOLD_MS = 15e3;
524
+ var CLICK_WINDOW_MS = 6e4;
525
+ var InteractionDetector = class {
526
+ constructor() {
527
+ this.name = "InteractionDetector";
528
+ this.weight = 0.7;
529
+ this.lastMouseMoveTime = 0;
530
+ this.lastClickTime = 0;
531
+ this.lastClickPosition = null;
532
+ this.clickHistory = [];
533
+ // rolling 60-second window
534
+ this.idleTimer = null;
535
+ this.isIdle = false;
536
+ this.resetIdleTimer = () => {
537
+ if (this.idleTimer) clearTimeout(this.idleTimer);
538
+ this.isIdle = false;
539
+ this.idleTimer = setTimeout(() => {
540
+ this.isIdle = true;
541
+ }, IDLE_THRESHOLD_MS);
542
+ };
543
+ this.handleMouseMove = () => {
544
+ this.lastMouseMoveTime = Date.now();
545
+ this.resetIdleTimer();
546
+ };
547
+ this.handleClick = (event) => {
548
+ this.resetIdleTimer();
549
+ const now = Date.now();
550
+ this.lastClickTime = now;
551
+ let distance = 0;
552
+ if (this.lastClickPosition) {
553
+ const dx = event.clientX - this.lastClickPosition.x;
554
+ const dy = event.clientY - this.lastClickPosition.y;
555
+ distance = Math.sqrt(dx * dx + dy * dy);
556
+ }
557
+ this.lastClickPosition = { x: event.clientX, y: event.clientY };
558
+ this.clickHistory.push({ time: now, distance });
559
+ };
560
+ if (typeof window !== "undefined") {
561
+ window.addEventListener("mousemove", this.handleMouseMove, { passive: true });
562
+ window.addEventListener("click", this.handleClick, { passive: true });
563
+ }
564
+ this.resetIdleTimer();
565
+ }
566
+ detect() {
567
+ const now = Date.now();
568
+ const cutoff = now - CLICK_WINDOW_MS;
569
+ this.clickHistory = this.clickHistory.filter((c) => c.time >= cutoff);
570
+ const clickCount = this.clickHistory.length;
571
+ const avgClickDistance = clickCount > 0 ? this.clickHistory.reduce((sum, c) => sum + c.distance, 0) / clickCount : 0;
572
+ const timeSinceLastClick = now - this.lastClickTime;
573
+ let cognitiveValue;
574
+ let confidence;
575
+ if (this.isIdle) {
576
+ cognitiveValue = 0.4;
577
+ confidence = 0.6;
578
+ } else if (timeSinceLastClick < 500 && clickCount > 5 && avgClickDistance < 20) {
579
+ cognitiveValue = 0.9;
580
+ confidence = 0.9;
581
+ } else if (timeSinceLastClick < 1500 && clickCount > 1 && avgClickDistance < 50) {
582
+ cognitiveValue = 0.7;
583
+ confidence = 0.7;
584
+ } else if (avgClickDistance > 100) {
585
+ cognitiveValue = 0.3;
586
+ confidence = 0.6;
587
+ } else {
588
+ cognitiveValue = 0.5;
589
+ confidence = 0.5;
590
+ }
591
+ return [{
592
+ dimension: "cognitive",
593
+ value: cognitiveValue,
594
+ confidence,
595
+ timestamp: now,
596
+ detectorName: this.name
597
+ }];
598
+ }
599
+ destroy() {
600
+ if (typeof window !== "undefined") {
601
+ window.removeEventListener("mousemove", this.handleMouseMove);
602
+ window.removeEventListener("click", this.handleClick);
603
+ }
604
+ if (this.idleTimer) clearTimeout(this.idleTimer);
605
+ }
606
+ };
607
+
608
+ // lib/capacity/signals/detectors/input-detector.ts
609
+ var TYPING_SPEED_SAMPLE_SIZE = 10;
610
+ var ERROR_CHECK_WINDOW = 5e3;
611
+ var InputDetector = class {
612
+ // timestamps of recent Backspace/Delete presses
613
+ constructor() {
614
+ this.name = "InputDetector";
615
+ this.weight = 0.6;
616
+ this.keyPressTimes = [];
617
+ this.errorTimes = [];
618
+ this.handleKeyDown = (event) => {
619
+ const now = Date.now();
620
+ this.keyPressTimes.push(now);
621
+ if (this.keyPressTimes.length > TYPING_SPEED_SAMPLE_SIZE) {
622
+ this.keyPressTimes.shift();
623
+ }
624
+ if (event.key === "Backspace" || event.key === "Delete") {
625
+ this.errorTimes.push(now);
626
+ }
627
+ };
628
+ if (typeof window !== "undefined") {
629
+ window.addEventListener("keydown", this.handleKeyDown, { passive: true });
630
+ }
631
+ }
632
+ detect() {
633
+ const now = Date.now();
634
+ let typingSpeedCPM = 0;
635
+ if (this.keyPressTimes.length > 1) {
636
+ const elapsed = this.keyPressTimes[this.keyPressTimes.length - 1] - this.keyPressTimes[0];
637
+ if (elapsed > 0) {
638
+ typingSpeedCPM = this.keyPressTimes.length / elapsed * 6e4;
639
+ }
640
+ }
641
+ const cutoff = now - ERROR_CHECK_WINDOW;
642
+ this.errorTimes = this.errorTimes.filter((t) => t >= cutoff);
643
+ const recentErrorCount = this.errorTimes.length;
644
+ let cognitiveValue;
645
+ let confidence;
646
+ if (typingSpeedCPM > 100 && recentErrorCount === 0) {
647
+ cognitiveValue = 0.9;
648
+ confidence = 0.8;
649
+ } else if (typingSpeedCPM > 40 && recentErrorCount <= 1) {
650
+ cognitiveValue = 0.7;
651
+ confidence = 0.7;
652
+ } else if (recentErrorCount > 2 || typingSpeedCPM < 20) {
653
+ cognitiveValue = 0.4;
654
+ confidence = 0.6;
655
+ } else {
656
+ cognitiveValue = 0.6;
657
+ confidence = 0.5;
658
+ }
659
+ return [{
660
+ dimension: "cognitive",
661
+ value: cognitiveValue,
662
+ confidence,
663
+ timestamp: now,
664
+ detectorName: this.name
665
+ }];
666
+ }
667
+ destroy() {
668
+ if (typeof window !== "undefined") {
669
+ window.removeEventListener("keydown", this.handleKeyDown);
670
+ }
671
+ }
672
+ };
673
+
674
+ // lib/capacity/signals/detectors/environment-detector.ts
675
+ var EnvironmentDetector = class {
676
+ constructor() {
677
+ this.name = "EnvironmentDetector";
678
+ this.weight = 0.8;
679
+ // High weight — these are explicit user preferences
680
+ this.mqlReducedMotion = null;
681
+ this.mqlDarkMode = null;
682
+ this.handleChange = () => {
683
+ };
684
+ if (typeof window !== "undefined") {
685
+ this.mqlReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)");
686
+ this.mqlDarkMode = window.matchMedia("(prefers-color-scheme: dark)");
687
+ this.mqlReducedMotion.addEventListener("change", this.handleChange);
688
+ this.mqlDarkMode.addEventListener("change", this.handleChange);
689
+ }
690
+ }
691
+ /**
692
+ * Returns two readings:
693
+ * - temporal: based on prefers-reduced-motion (low → less time pressure on animations)
694
+ * - emotional: based on prefers-color-scheme (dark → slightly lower emotional load)
695
+ */
696
+ detect() {
697
+ const now = Date.now();
698
+ const prefersReducedMotion = this.mqlReducedMotion != null ? this.mqlReducedMotion.matches : typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
699
+ const prefersDarkMode = this.mqlDarkMode != null ? this.mqlDarkMode.matches : typeof window !== "undefined" && window.matchMedia("(prefers-color-scheme: dark)").matches;
700
+ return [
701
+ {
702
+ dimension: "temporal",
703
+ // prefers-reduced-motion → user may have lower tolerance for demanding UIs
704
+ value: prefersReducedMotion ? 0.3 : 0.8,
705
+ confidence: 0.9,
706
+ timestamp: now,
707
+ detectorName: this.name
708
+ },
709
+ {
710
+ dimension: "emotional",
711
+ // Dark mode preference → slightly lower emotional capacity or reduced-stimulation preference
712
+ value: prefersDarkMode ? 0.6 : 0.7,
713
+ confidence: 0.9,
714
+ timestamp: now,
715
+ detectorName: this.name
716
+ }
717
+ ];
718
+ }
719
+ /**
720
+ * Removes the event listeners registered in the constructor.
721
+ * Uses the stored refs so the same function reference is unregistered.
722
+ */
723
+ destroy() {
724
+ var _a, _b;
725
+ (_a = this.mqlReducedMotion) == null ? void 0 : _a.removeEventListener("change", this.handleChange);
726
+ (_b = this.mqlDarkMode) == null ? void 0 : _b.removeEventListener("change", this.handleChange);
727
+ this.mqlReducedMotion = null;
728
+ this.mqlDarkMode = null;
729
+ }
730
+ };
731
+
732
+ // lib/capacity/signals/aggregator.ts
733
+ var _SignalAggregator = class _SignalAggregator {
734
+ constructor() {
735
+ this.detectors = [
736
+ new TimeDetector(),
737
+ new SessionDetector(),
738
+ new ScrollDetector(),
739
+ new InteractionDetector(),
740
+ new InputDetector(),
741
+ new EnvironmentDetector()
742
+ ];
743
+ }
744
+ /**
745
+ * Collects signal readings from all detectors and aggregates them into a
746
+ * confidence-weighted CapacityField.
747
+ */
748
+ aggregateSignals() {
749
+ return __async(this, null, function* () {
750
+ const readings = [];
751
+ for (const detector of this.detectors) {
752
+ const detectorReadings = yield detector.detect();
753
+ readings.push(...detectorReadings);
754
+ }
755
+ const weightedSums = {
756
+ cognitive: 0,
757
+ temporal: 0,
758
+ emotional: 0,
759
+ valence: 0
760
+ };
761
+ const totalWeights = {
762
+ cognitive: 0,
763
+ temporal: 0,
764
+ emotional: 0,
765
+ valence: 0
766
+ };
767
+ for (const reading of readings) {
768
+ const effectiveWeight = reading.confidence * this.getDetectorWeight(reading.dimension, reading.detectorName);
769
+ weightedSums[reading.dimension] += reading.value * effectiveWeight;
770
+ totalWeights[reading.dimension] += effectiveWeight;
771
+ }
772
+ return {
773
+ cognitive: totalWeights.cognitive > 0 ? weightedSums.cognitive / totalWeights.cognitive : 0.5,
774
+ temporal: totalWeights.temporal > 0 ? weightedSums.temporal / totalWeights.temporal : 0.5,
775
+ emotional: totalWeights.emotional > 0 ? weightedSums.emotional / totalWeights.emotional : 0.5,
776
+ valence: totalWeights.valence > 0 ? weightedSums.valence / totalWeights.valence : 0
777
+ };
778
+ });
779
+ }
780
+ /**
781
+ * Returns the effective weight for a detector/dimension pair.
782
+ * Checks DIMENSION_WEIGHTS first; falls back to detector.weight.
783
+ */
784
+ getDetectorWeight(dimension, detectorName) {
785
+ var _a, _b, _c;
786
+ const override = (_a = _SignalAggregator.DIMENSION_WEIGHTS[detectorName]) == null ? void 0 : _a[dimension];
787
+ if (override !== void 0) return override;
788
+ return (_c = (_b = this.detectors.find((d) => d.name === detectorName)) == null ? void 0 : _b.weight) != null ? _c : 0;
789
+ }
790
+ /** Cleans up all detector resources (event listeners, timers). */
791
+ destroy() {
792
+ var _a;
793
+ for (const detector of this.detectors) {
794
+ (_a = detector.destroy) == null ? void 0 : _a.call(detector);
795
+ }
796
+ }
797
+ };
798
+ /**
799
+ * Per-detector, per-dimension weight overrides.
800
+ * Falls back to detector.weight for any unlisted combination.
801
+ *
802
+ * Rationale for asymmetries:
803
+ * - TimeDetector: cognitive signal is stronger (diurnal pattern) than temporal
804
+ * (weekday/weekend is coarser)
805
+ * - EnvironmentDetector: emotional signal (color scheme) is a stronger explicit
806
+ * preference than temporal (reduced-motion)
807
+ */
808
+ _SignalAggregator.DIMENSION_WEIGHTS = {
809
+ TimeDetector: { cognitive: 0.6, temporal: 0.5 },
810
+ EnvironmentDetector: { emotional: 0.8, temporal: 0.7 },
811
+ InteractionDetector: { cognitive: 0.7 },
812
+ InputDetector: { cognitive: 0.6 },
813
+ SessionDetector: { temporal: 0.7 },
814
+ ScrollDetector: { cognitive: 0.5 }
815
+ };
816
+ var SignalAggregator = _SignalAggregator;
817
+ var CapacityContext = createContext(null);
818
+ var AUTO_EMA_ALPHA = 0.2;
819
+ function applyEMA(prev, next, alpha) {
820
+ return {
821
+ cognitive: prev.cognitive * (1 - alpha) + next.cognitive * alpha,
822
+ temporal: prev.temporal * (1 - alpha) + next.temporal * alpha,
823
+ emotional: prev.emotional * (1 - alpha) + next.emotional * alpha,
824
+ valence: prev.valence * (1 - alpha) + next.valence * alpha
825
+ };
826
+ }
827
+ function CapacityProvider({ children }) {
828
+ const [context, setContext] = useState(() => FieldManager.getContext());
829
+ const [isAutoMode, setIsAutoMode] = useState(true);
830
+ const [hapticEnabled, setHapticEnabled] = useState(false);
831
+ const [sonicEnabled, setSonicEnabled] = useState(false);
832
+ const isFirstAggregationComplete = useRef(false);
833
+ const smoothedFieldRef = useRef(null);
834
+ const aggregatorRef = useRef(null);
835
+ useEffect(() => {
836
+ aggregatorRef.current = new SignalAggregator();
837
+ const unsubscribe = FieldManager.subscribe((newContext) => {
838
+ setContext(newContext);
839
+ });
840
+ return () => {
841
+ unsubscribe();
842
+ if (aggregatorRef.current) {
843
+ aggregatorRef.current.destroy();
844
+ }
845
+ };
846
+ }, []);
847
+ useEffect(() => {
848
+ let intervalId;
849
+ if (isAutoMode && aggregatorRef.current) {
850
+ isFirstAggregationComplete.current = false;
851
+ smoothedFieldRef.current = null;
852
+ intervalId = setInterval(() => __async(null, null, function* () {
853
+ var _a;
854
+ try {
855
+ const suggestedField = yield aggregatorRef.current.aggregateSignals();
856
+ if (!isFirstAggregationComplete.current) {
857
+ isFirstAggregationComplete.current = true;
858
+ smoothedFieldRef.current = suggestedField;
859
+ } else {
860
+ smoothedFieldRef.current = applyEMA(
861
+ (_a = smoothedFieldRef.current) != null ? _a : suggestedField,
862
+ suggestedField,
863
+ AUTO_EMA_ALPHA
864
+ );
865
+ const smoothed = smoothedFieldRef.current;
866
+ FieldManager.updateCapacity({
867
+ cognitive: smoothed.cognitive,
868
+ temporal: smoothed.temporal,
869
+ emotional: smoothed.emotional
870
+ });
871
+ FieldManager.updateEmotionalState({
872
+ valence: smoothed.valence
873
+ });
874
+ }
875
+ } catch (error) {
876
+ console.warn("[CapacityProvider] Signal aggregation failed:", error);
877
+ }
878
+ }), 2e3);
879
+ }
880
+ return () => {
881
+ if (intervalId) {
882
+ clearInterval(intervalId);
883
+ }
884
+ };
885
+ }, [isAutoMode]);
886
+ const updateCapacity = useCallback((capacity) => {
887
+ if (isAutoMode) {
888
+ setIsAutoMode(false);
889
+ }
890
+ FieldManager.updateCapacity(capacity);
891
+ }, [isAutoMode]);
892
+ const updateEmotionalState = useCallback((state) => {
893
+ if (isAutoMode) {
894
+ setIsAutoMode(false);
895
+ }
896
+ FieldManager.updateEmotionalState(state);
897
+ }, [isAutoMode]);
898
+ const updateCapacityField = useCallback((field) => {
899
+ FieldManager.updateCapacity({
900
+ cognitive: field.cognitive,
901
+ temporal: field.temporal,
902
+ emotional: field.emotional
903
+ });
904
+ FieldManager.updateEmotionalState({
905
+ valence: field.valence
906
+ });
907
+ }, []);
908
+ const toggleAutoMode = useCallback(() => {
909
+ setIsAutoMode((prev) => !prev);
910
+ }, []);
911
+ return /* @__PURE__ */ jsx(CapacityContext.Provider, { value: {
912
+ context,
913
+ updateCapacity,
914
+ updateEmotionalState,
915
+ isAutoMode,
916
+ toggleAutoMode,
917
+ updateCapacityField,
918
+ hapticEnabled,
919
+ sonicEnabled,
920
+ setHapticEnabled,
921
+ setSonicEnabled
922
+ }, children });
923
+ }
924
+ function useCapacityContext() {
925
+ const context = useContext(CapacityContext);
926
+ if (!context) {
927
+ throw new Error("useCapacityContext must be used within CapacityProvider");
928
+ }
929
+ return context;
930
+ }
931
+ function useEnergyField() {
932
+ const { context } = useCapacityContext();
933
+ return context.energy;
934
+ }
935
+ function useAttentionField() {
936
+ const { context } = useCapacityContext();
937
+ return context.attention;
938
+ }
939
+ function useEmotionalValenceField() {
940
+ const { context } = useCapacityContext();
941
+ return context.emotionalValence;
942
+ }
943
+ function useFieldControls() {
944
+ const { updateCapacity, updateEmotionalState, isAutoMode, toggleAutoMode, updateCapacityField } = useCapacityContext();
945
+ return { updateCapacity, updateEmotionalState, isAutoMode, toggleAutoMode, updateCapacityField };
946
+ }
947
+ function usePrefersReducedMotion() {
948
+ const [prefersReducedMotion, setPrefersReducedMotion] = useState(false);
949
+ useEffect(() => {
950
+ const mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
951
+ setPrefersReducedMotion(mediaQuery.matches);
952
+ const handleChange = (event) => {
953
+ setPrefersReducedMotion(event.matches);
954
+ };
955
+ mediaQuery.addEventListener("change", handleChange);
956
+ return () => mediaQuery.removeEventListener("change", handleChange);
957
+ }, []);
958
+ return prefersReducedMotion;
959
+ }
960
+ function useDerivedMode() {
961
+ const { context } = useCapacityContext();
962
+ const field = {
963
+ cognitive: context.userCapacity.cognitive,
964
+ temporal: context.userCapacity.temporal,
965
+ emotional: context.userCapacity.emotional,
966
+ valence: context.emotionalState.valence,
967
+ arousal: context.emotionalState.arousal
968
+ };
969
+ const mode = deriveMode(field);
970
+ return { field, mode };
971
+ }
972
+ function useEffectiveMotion() {
973
+ const { field } = useDerivedMode();
974
+ const prefersReducedMotion = usePrefersReducedMotion();
975
+ const derivedMode = deriveMode(field);
976
+ const effectiveMode = prefersReducedMotion ? "off" : derivedMode.motion;
977
+ return {
978
+ mode: effectiveMode,
979
+ tokens: MOTION_TOKENS[effectiveMode],
980
+ prefersReducedMotion
981
+ };
982
+ }
983
+ function useFeedback() {
984
+ const { hapticEnabled, sonicEnabled, setHapticEnabled, setSonicEnabled } = useCapacityContext();
985
+ const { mode } = useDerivedMode();
986
+ const fire = useCallback((pattern = "tap") => {
987
+ if (hapticEnabled) triggerHaptic(pattern);
988
+ if (sonicEnabled) playPacedSonic(mode.pace);
989
+ }, [hapticEnabled, sonicEnabled, mode.pace]);
990
+ return { hapticEnabled, sonicEnabled, setHapticEnabled, setSonicEnabled, fire };
991
+ }
992
+ function usePacedMotionTokens() {
993
+ const { mode } = useDerivedMode();
994
+ const { mode: effectiveMotion, tokens: baseTokens, prefersReducedMotion } = useEffectiveMotion();
995
+ const effectivePace = prefersReducedMotion ? "calm" : mode.pace;
996
+ const multiplier = effectivePace === "calm" ? 1.5 : effectivePace === "activated" ? 0.65 : 1;
997
+ return {
998
+ mode: effectiveMotion,
999
+ pace: effectivePace,
1000
+ tokens: __spreadProps(__spreadValues({}, baseTokens), {
1001
+ durationFast: Math.round(baseTokens.durationFast * multiplier),
1002
+ durationBase: Math.round(baseTokens.durationBase * multiplier),
1003
+ durationSlow: Math.round(baseTokens.durationSlow * multiplier)
1004
+ })
1005
+ };
1006
+ }
1007
+
1008
+ // lib/capacity/animation.ts
1009
+ var ENTRANCE_PRESETS = {
1010
+ /** Liquid organic morph -> gentle scale fade -> soft bloom -> none */
1011
+ morph: { expressive: "morph-fade-in", subtle: "sacred-fade", soothing: "bloom", off: "" },
1012
+ /** Spinning vortex -> gentle scale fade -> soft bloom -> none */
1013
+ vortex: { expressive: "vortex-reveal", subtle: "sacred-fade", soothing: "bloom", off: "" },
1014
+ /** Spiral in from corner -> soft bloom -> soft bloom -> none */
1015
+ spiral: { expressive: "spiral-in", subtle: "bloom", soothing: "bloom", off: "" }
1016
+ };
1017
+ function entranceClass(motion, preset, hasPlayed) {
1018
+ if (hasPlayed) return "";
1019
+ return ENTRANCE_PRESETS[preset][motion];
1020
+ }
1021
+ function hoverClass(motion) {
1022
+ if (motion === "expressive") return "hover-expand";
1023
+ if (motion === "subtle" || motion === "soothing") return "hover-lift";
1024
+ return "";
1025
+ }
1026
+ function ambientClass(motion, type) {
1027
+ if (motion === "expressive") return type;
1028
+ if (motion === "soothing" && (type === "breathe" || type === "float")) return type;
1029
+ return "";
1030
+ }
1031
+ function listItemClass(motion) {
1032
+ if (motion === "expressive") return "helix-rise";
1033
+ if (motion === "subtle" || motion === "soothing") return "sacred-fade";
1034
+ return "";
1035
+ }
1036
+ function focusBeaconClass(focus) {
1037
+ if (focus === "guided") return "attention-beacon focus-highlight";
1038
+ if (focus === "gentle") return "gentle-beacon gentle-highlight";
1039
+ return "";
1040
+ }
1041
+ function focusTextClass(focus) {
1042
+ if (focus === "guided") return "attention-text";
1043
+ if (focus === "gentle") return "gentle-text";
1044
+ return "";
1045
+ }
1046
+
1047
+ // lib/capacity/utils/typography.ts
1048
+ var BASE_FONT_SIZE = 16;
1049
+ var MIN_FONT_SIZE = 14;
1050
+ var JITTER_FACTOR = 0.05;
1051
+ var SCALE_STEPS = {
1052
+ h1: 4,
1053
+ // φ^4 ≈ 6.85x base
1054
+ h2: 3,
1055
+ // φ^3 ≈ 4.24x base
1056
+ h3: 2,
1057
+ // φ^2 ≈ 2.62x base
1058
+ h4: 1,
1059
+ // φ^1 ≈ 1.62x base
1060
+ body: 0,
1061
+ // φ^0 = 1x base
1062
+ label: -0.5,
1063
+ // φ^-0.5 ≈ 0.79x base
1064
+ caption: -1
1065
+ // φ^-1 ≈ 0.62x base
1066
+ };
1067
+ var ENERGY_BIAS = {
1068
+ low: 1.05,
1069
+ // +5% for better readability when tired
1070
+ medium: 1,
1071
+ // Neutral
1072
+ high: 0.95
1073
+ // -5% for higher density when alert
1074
+ };
1075
+ var ATTENTION_WEIGHT = {
1076
+ low: 400,
1077
+ // Regular
1078
+ medium: 450,
1079
+ // Medium
1080
+ high: 500
1081
+ // Medium-bold for focus
1082
+ };
1083
+ var ATTENTION_TRACKING = {
1084
+ low: 0.02,
1085
+ // Loose tracking for comfortable reading
1086
+ medium: 0,
1087
+ // Normal
1088
+ high: -0.01
1089
+ // Tight tracking for focus
1090
+ };
1091
+ function modularScale(step, base = BASE_FONT_SIZE) {
1092
+ return base * Math.pow(PHI, step);
1093
+ }
1094
+ function getFontSize(role, energy = "medium", options) {
1095
+ const { base = BASE_FONT_SIZE, jitter = true, minSize = MIN_FONT_SIZE } = options || {};
1096
+ const step = SCALE_STEPS[role];
1097
+ const baseSize = modularScale(step, base);
1098
+ const jitterAmount = jitter ? (Math.random() - 0.5) * 2 * JITTER_FACTOR : 0;
1099
+ const jitteredSize = baseSize * (1 + jitterAmount);
1100
+ const energyAdjustedSize = jitteredSize * ENERGY_BIAS[energy];
1101
+ return Math.max(energyAdjustedSize, minSize);
1102
+ }
1103
+ function getFontWeight(attention = "medium") {
1104
+ return ATTENTION_WEIGHT[attention];
1105
+ }
1106
+ function getLetterSpacing(attention = "medium") {
1107
+ return ATTENTION_TRACKING[attention];
1108
+ }
1109
+ function getLineHeight(role) {
1110
+ const lineHeights = {
1111
+ h1: 1.2,
1112
+ h2: 1.25,
1113
+ h3: 1.3,
1114
+ h4: 1.35,
1115
+ body: 1.5,
1116
+ label: 1.4,
1117
+ caption: 1.45
1118
+ };
1119
+ return lineHeights[role];
1120
+ }
1121
+ function getTypographyStyles(role, energy = "medium", attention = "medium") {
1122
+ return {
1123
+ fontSize: `${getFontSize(role, energy)}px`,
1124
+ fontWeight: getFontWeight(attention),
1125
+ lineHeight: getLineHeight(role),
1126
+ letterSpacing: `${getLetterSpacing(attention)}em`
1127
+ };
1128
+ }
1129
+ function getFluidFontSize(role, energy = "medium") {
1130
+ const minSize = getFontSize(role, energy, { jitter: false });
1131
+ const maxSize = minSize * 1.2;
1132
+ return `clamp(${minSize}px, ${minSize}px + (${maxSize - minSize}) * ((100vw - 320px) / 1600), ${maxSize}px)`;
1133
+ }
1134
+ var SPACING_BASE = 4;
1135
+ var SPACING_SCALE = FIBONACCI.map((f) => f * SPACING_BASE);
1136
+ function getSpacing(step, unit = "px") {
1137
+ const clampedStep = Math.max(0, Math.min(step, SPACING_SCALE.length - 1));
1138
+ const raw = SPACING_SCALE[clampedStep];
1139
+ if (unit === "raw") return raw;
1140
+ if (unit === "rem") return `${(raw / 16).toFixed(4).replace(/\.?0+$/, "")}rem`;
1141
+ return `${raw}px`;
1142
+ }
1143
+ function getProportionalSpacing(density) {
1144
+ const shift = density === "low" ? -1 : density === "high" ? 1 : 0;
1145
+ return {
1146
+ xs: getSpacing(2 + shift),
1147
+ sm: getSpacing(3 + shift),
1148
+ md: getSpacing(5 + shift),
1149
+ lg: getSpacing(7 + shift),
1150
+ gap: getSpacing(4 + shift)
1151
+ };
1152
+ }
1153
+ function phiRatio(steps) {
1154
+ return Math.pow(PHI, steps);
1155
+ }
1156
+
1157
+ // lib/capacity/signals/signal-bus.ts
1158
+ var SignalBusClass = class {
1159
+ constructor() {
1160
+ this.handlers = /* @__PURE__ */ new Map();
1161
+ this.signalQueue = [];
1162
+ this.processing = false;
1163
+ }
1164
+ /**
1165
+ * Emit a signal to all subscribed handlers
1166
+ */
1167
+ emit(type, payload, priority = "normal", source) {
1168
+ const signal = {
1169
+ type,
1170
+ payload,
1171
+ timestamp: Date.now(),
1172
+ priority,
1173
+ source
1174
+ };
1175
+ if (priority === "critical") {
1176
+ this.signalQueue.unshift(signal);
1177
+ } else {
1178
+ this.signalQueue.push(signal);
1179
+ }
1180
+ this.processQueue();
1181
+ }
1182
+ /**
1183
+ * Subscribe to a specific signal type
1184
+ */
1185
+ subscribe(type, handler) {
1186
+ if (!this.handlers.has(type)) {
1187
+ this.handlers.set(type, /* @__PURE__ */ new Set());
1188
+ }
1189
+ const handlers = this.handlers.get(type);
1190
+ handlers.add(handler);
1191
+ return () => {
1192
+ handlers.delete(handler);
1193
+ if (handlers.size === 0) {
1194
+ this.handlers.delete(type);
1195
+ }
1196
+ };
1197
+ }
1198
+ /**
1199
+ * Subscribe to multiple signal types with same handler
1200
+ */
1201
+ subscribeMultiple(types, handler) {
1202
+ const unsubscribers = types.map((type) => this.subscribe(type, handler));
1203
+ return () => {
1204
+ unsubscribers.forEach((unsub) => unsub());
1205
+ };
1206
+ }
1207
+ /**
1208
+ * Process signal queue
1209
+ */
1210
+ processQueue() {
1211
+ return __async(this, null, function* () {
1212
+ if (this.processing || this.signalQueue.length === 0) {
1213
+ return;
1214
+ }
1215
+ this.processing = true;
1216
+ while (this.signalQueue.length > 0) {
1217
+ const signal = this.signalQueue.shift();
1218
+ const handlers = this.handlers.get(signal.type);
1219
+ if (handlers) {
1220
+ handlers.forEach((handler) => {
1221
+ try {
1222
+ handler(signal);
1223
+ } catch (error) {
1224
+ console.error(`[v0] Signal handler error for "${signal.type}":`, error);
1225
+ }
1226
+ });
1227
+ }
1228
+ }
1229
+ this.processing = false;
1230
+ });
1231
+ }
1232
+ /**
1233
+ * Get count of handlers for a signal type
1234
+ */
1235
+ getHandlerCount(type) {
1236
+ var _a, _b;
1237
+ return (_b = (_a = this.handlers.get(type)) == null ? void 0 : _a.size) != null ? _b : 0;
1238
+ }
1239
+ /**
1240
+ * Clear all handlers (useful for testing)
1241
+ */
1242
+ clear() {
1243
+ this.handlers.clear();
1244
+ this.signalQueue = [];
1245
+ }
1246
+ };
1247
+ var SignalBus = new SignalBusClass();
1248
+ var SIGNAL_TYPES = {
1249
+ // Field changes
1250
+ FIELD_ENERGY_CHANGED: "field:energy:changed",
1251
+ FIELD_ATTENTION_CHANGED: "field:attention:changed",
1252
+ FIELD_VALENCE_CHANGED: "field:valence:changed",
1253
+ // User interactions
1254
+ USER_INTERACTION_START: "user:interaction:start",
1255
+ USER_INTERACTION_END: "user:interaction:end",
1256
+ USER_FOCUS_CHANGED: "user:focus:changed",
1257
+ // Component lifecycle
1258
+ COMPONENT_MOUNTED: "component:mounted",
1259
+ COMPONENT_UNMOUNTED: "component:unmounted",
1260
+ // Accessibility
1261
+ A11Y_ANNOUNCE: "a11y:announce",
1262
+ A11Y_FOCUS_TRAP: "a11y:focus:trap"
1263
+ };
1264
+
1265
+ export { CapacityProvider, DEFAULT_COMPONENT_RESPONSE, FEEDBACK_FREQUENCIES, FIBONACCI, FieldManager, HAPTIC_PATTERNS, MOTION_TOKENS, PHI, PHI_INVERSE, SIGNAL_TYPES, SPACING_SCALE, SignalBus, ambientClass, deriveMode, deriveModeLabel, entranceClass, focusBeaconClass, focusTextClass, getFluidFontSize, getFontSize, getFontWeight, getFrequencyForPace, getLetterSpacing, getLineHeight, getModeBadgeColor, getProportionalSpacing, getSpacing, getTypographyStyles, hoverClass, listItemClass, modularScale, phiRatio, playPacedSonic, playSonicFeedback, triggerHaptic, useAttentionField, useCapacityContext, useDerivedMode, useEffectiveMotion, useEmotionalValenceField, useEnergyField, useFeedback, useFieldControls, usePacedMotionTokens, usePrefersReducedMotion };
1266
+ //# sourceMappingURL=index.mjs.map
1267
+ //# sourceMappingURL=index.mjs.map