@thehoneyjar/sigil-hud 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Signal Capture Hook
3
+ *
4
+ * Capture taste signals from HUD interactions.
5
+ */
6
+
7
+ import { useCallback, useRef } from 'react'
8
+ import type { Signal } from '../types'
9
+
10
+ /**
11
+ * Props for useSignalCapture
12
+ */
13
+ export interface UseSignalCaptureProps {
14
+ /** Whether signal capture is enabled */
15
+ enabled?: boolean
16
+ /** Callback when signal is captured */
17
+ onSignal?: (signal: Signal) => void
18
+ }
19
+
20
+ /**
21
+ * Hook to capture taste signals from HUD
22
+ */
23
+ export function useSignalCapture({
24
+ enabled = true,
25
+ onSignal,
26
+ }: UseSignalCaptureProps = {}) {
27
+ const signalsRef = useRef<Signal[]>([])
28
+
29
+ /**
30
+ * Append a signal to the taste log
31
+ */
32
+ const appendSignal = useCallback(
33
+ async (signalData: Omit<Signal, 'timestamp' | 'source'>): Promise<void> => {
34
+ if (!enabled) return
35
+
36
+ const signal: Signal = {
37
+ ...signalData,
38
+ timestamp: new Date().toISOString(),
39
+ source: 'hud',
40
+ }
41
+
42
+ // Store locally
43
+ signalsRef.current.push(signal)
44
+
45
+ // Notify callback
46
+ onSignal?.(signal)
47
+
48
+ // In a full implementation, this would write to grimoires/sigil/taste.md
49
+ // For now, we log to console in development
50
+ if (process.env.NODE_ENV === 'development') {
51
+ console.log('[Sigil HUD] Signal captured:', signal)
52
+ }
53
+ },
54
+ [enabled, onSignal]
55
+ )
56
+
57
+ /**
58
+ * Create an ACCEPT signal
59
+ */
60
+ const accept = useCallback(
61
+ (component: string, effect: string, craftType: Signal['component']['craft_type'] = 'generate') => {
62
+ return appendSignal({
63
+ signal: 'ACCEPT',
64
+ component: {
65
+ name: component,
66
+ effect,
67
+ craft_type: craftType,
68
+ },
69
+ })
70
+ },
71
+ [appendSignal]
72
+ )
73
+
74
+ /**
75
+ * Create a MODIFY signal
76
+ */
77
+ const modify = useCallback(
78
+ (
79
+ component: string,
80
+ effect: string,
81
+ change: { from: string; to: string },
82
+ learning?: { inference: string; recommendation?: string }
83
+ ) => {
84
+ return appendSignal({
85
+ signal: 'MODIFY',
86
+ component: {
87
+ name: component,
88
+ effect,
89
+ craft_type: 'generate',
90
+ },
91
+ change,
92
+ learning,
93
+ })
94
+ },
95
+ [appendSignal]
96
+ )
97
+
98
+ /**
99
+ * Create a REJECT signal
100
+ */
101
+ const reject = useCallback(
102
+ (component: string, effect: string, reason: string) => {
103
+ return appendSignal({
104
+ signal: 'REJECT',
105
+ component: {
106
+ name: component,
107
+ effect,
108
+ craft_type: 'generate',
109
+ },
110
+ rejection_reason: reason,
111
+ })
112
+ },
113
+ [appendSignal]
114
+ )
115
+
116
+ /**
117
+ * Get all captured signals
118
+ */
119
+ const getSignals = useCallback(() => {
120
+ return [...signalsRef.current]
121
+ }, [])
122
+
123
+ /**
124
+ * Clear captured signals
125
+ */
126
+ const clearSignals = useCallback(() => {
127
+ signalsRef.current = []
128
+ }, [])
129
+
130
+ return {
131
+ appendSignal,
132
+ accept,
133
+ modify,
134
+ reject,
135
+ getSignals,
136
+ clearSignals,
137
+ }
138
+ }
package/src/index.ts ADDED
@@ -0,0 +1,112 @@
1
+ /**
2
+ * @sigil/hud
3
+ *
4
+ * Diagnostic HUD for Sigil - composable React components for development.
5
+ */
6
+
7
+ // Types
8
+ export type {
9
+ HudPosition,
10
+ HudConfig,
11
+ HudPanelType,
12
+ HudState,
13
+ HudActions,
14
+ HudContextValue,
15
+ HudProviderProps,
16
+ Observation,
17
+ Signal,
18
+ // Service types (mirrored from optional packages)
19
+ LensService,
20
+ LensState,
21
+ ForkService,
22
+ ForkState,
23
+ SimulationService,
24
+ SimulationState,
25
+ DiagnosticsService,
26
+ DiagnosticResult,
27
+ DiagnosticIssue,
28
+ EffectType,
29
+ ComplianceResult,
30
+ BehavioralCompliance,
31
+ AnimationCompliance,
32
+ MaterialCompliance,
33
+ } from './types'
34
+
35
+ export {
36
+ DEFAULT_HUD_STATE,
37
+ DEFAULT_HUD_CONFIG,
38
+ DEFAULT_LENS_STATE,
39
+ } from './types'
40
+
41
+ // Store
42
+ export { useHudStore, getHudState } from './store'
43
+ export type { HudStore } from './store'
44
+
45
+ // Provider
46
+ export { HudProvider, useHud, useHudOptional } from './providers/HudProvider'
47
+
48
+ // Components
49
+ export { HudPanel } from './components/HudPanel'
50
+ export type { HudPanelProps } from './components/HudPanel'
51
+
52
+ export { HudTrigger } from './components/HudTrigger'
53
+ export type { HudTriggerProps } from './components/HudTrigger'
54
+
55
+ export { LensPanel } from './components/LensPanel'
56
+ export type { LensPanelProps } from './components/LensPanel'
57
+
58
+ export { SimulationPanel } from './components/SimulationPanel'
59
+ export type { SimulationPanelProps } from './components/SimulationPanel'
60
+
61
+ export { DiagnosticsPanel } from './components/DiagnosticsPanel'
62
+ export type { DiagnosticsPanelProps } from './components/DiagnosticsPanel'
63
+
64
+ export { PhysicsAnalysis } from './components/PhysicsAnalysis'
65
+ export type { PhysicsAnalysisProps } from './components/PhysicsAnalysis'
66
+
67
+ export { IssueList } from './components/IssueList'
68
+ export type { IssueListProps } from './components/IssueList'
69
+
70
+ export { DataSourceIndicator } from './components/DataSourceIndicator'
71
+ export type { DataSourceIndicatorProps, DataSourceType } from './components/DataSourceIndicator'
72
+
73
+ export { StateComparison } from './components/StateComparison'
74
+ export type { StateComparisonProps } from './components/StateComparison'
75
+
76
+ export { ObservationCaptureModal } from './components/ObservationCaptureModal'
77
+ export type { ObservationCaptureModalProps } from './components/ObservationCaptureModal'
78
+
79
+ export { FeedbackPrompt } from './components/FeedbackPrompt'
80
+ export type { FeedbackPromptProps } from './components/FeedbackPrompt'
81
+
82
+ // Hooks
83
+ export { useKeyboardShortcuts, getShortcutHelp } from './hooks/useKeyboardShortcuts'
84
+ export type { UseKeyboardShortcutsProps } from './hooks/useKeyboardShortcuts'
85
+
86
+ export { useSignalCapture } from './hooks/useSignalCapture'
87
+ export type { UseSignalCaptureProps } from './hooks/useSignalCapture'
88
+
89
+ export { useObservationCapture } from './hooks/useObservationCapture'
90
+ export type { UseObservationCaptureProps } from './hooks/useObservationCapture'
91
+
92
+ export { useDataSource, useMultipleDataSources } from './hooks/useDataSource'
93
+ export type {
94
+ UseDataSourceProps,
95
+ DataSourceMeta,
96
+ DataSourceEntry,
97
+ } from './hooks/useDataSource'
98
+
99
+ // Theme
100
+ export {
101
+ colors,
102
+ typography,
103
+ spacing,
104
+ radii,
105
+ shadows,
106
+ transitions,
107
+ zIndex,
108
+ patterns,
109
+ effectColors,
110
+ getEffectColor,
111
+ createStyles,
112
+ } from './styles/theme'
@@ -0,0 +1,115 @@
1
+ /**
2
+ * HUD Provider
3
+ *
4
+ * Context provider for HUD state and services.
5
+ */
6
+
7
+ import { createContext, useContext, useMemo } from 'react'
8
+ import type { HudProviderProps, HudContextValue, HudConfig } from '../types'
9
+ import { DEFAULT_HUD_CONFIG, DEFAULT_HUD_STATE } from '../types'
10
+ import { useHudStore } from '../store'
11
+
12
+ /**
13
+ * HUD context
14
+ */
15
+ const HudContext = createContext<HudContextValue | null>(null)
16
+
17
+ /**
18
+ * HUD Provider component
19
+ */
20
+ export function HudProvider({
21
+ children,
22
+ config = {},
23
+ lensService,
24
+ forkService,
25
+ simulationService,
26
+ diagnosticsService,
27
+ anchorClient,
28
+ }: HudProviderProps) {
29
+ // Get state and actions from store
30
+ const isOpen = useHudStore((state) => state.isOpen)
31
+ const activePanel = useHudStore((state) => state.activePanel)
32
+ const position = useHudStore((state) => state.position)
33
+ const isMinimized = useHudStore((state) => state.isMinimized)
34
+ const open = useHudStore((state) => state.open)
35
+ const close = useHudStore((state) => state.close)
36
+ const toggle = useHudStore((state) => state.toggle)
37
+ const setActivePanel = useHudStore((state) => state.setActivePanel)
38
+ const setPosition = useHudStore((state) => state.setPosition)
39
+ const toggleMinimized = useHudStore((state) => state.toggleMinimized)
40
+
41
+ // Merge config with defaults
42
+ const mergedConfig: HudConfig = useMemo(
43
+ () => ({
44
+ ...DEFAULT_HUD_CONFIG,
45
+ ...config,
46
+ }),
47
+ [config]
48
+ )
49
+
50
+ // Build context value
51
+ const value: HudContextValue = useMemo(
52
+ () => ({
53
+ // State
54
+ isOpen,
55
+ activePanel,
56
+ position,
57
+ isMinimized,
58
+ // Actions
59
+ open,
60
+ close,
61
+ toggle,
62
+ setActivePanel,
63
+ setPosition,
64
+ toggleMinimized,
65
+ // Services
66
+ lensService: lensService ?? null,
67
+ forkService: forkService ?? null,
68
+ simulationService: simulationService ?? null,
69
+ diagnosticsService: diagnosticsService ?? null,
70
+ anchorClient: anchorClient ?? null,
71
+ // Config
72
+ config: mergedConfig,
73
+ }),
74
+ [
75
+ isOpen,
76
+ activePanel,
77
+ position,
78
+ isMinimized,
79
+ open,
80
+ close,
81
+ toggle,
82
+ setActivePanel,
83
+ setPosition,
84
+ toggleMinimized,
85
+ lensService,
86
+ forkService,
87
+ simulationService,
88
+ diagnosticsService,
89
+ anchorClient,
90
+ mergedConfig,
91
+ ]
92
+ )
93
+
94
+ return <HudContext.Provider value={value}>{children}</HudContext.Provider>
95
+ }
96
+
97
+ /**
98
+ * Hook to access HUD context
99
+ *
100
+ * @throws Error if used outside HudProvider
101
+ */
102
+ export function useHud(): HudContextValue {
103
+ const context = useContext(HudContext)
104
+ if (!context) {
105
+ throw new Error('useHud must be used within a HudProvider')
106
+ }
107
+ return context
108
+ }
109
+
110
+ /**
111
+ * Hook to access HUD context (returns null if not in provider)
112
+ */
113
+ export function useHudOptional(): HudContextValue | null {
114
+ return useContext(HudContext)
115
+ }
package/src/store.ts ADDED
@@ -0,0 +1,60 @@
1
+ /**
2
+ * HUD Store
3
+ *
4
+ * Zustand store for HUD state management.
5
+ */
6
+
7
+ import { create } from 'zustand'
8
+ import { persist } from 'zustand/middleware'
9
+ import type { HudState, HudActions, HudPosition, HudPanelType } from './types'
10
+ import { DEFAULT_HUD_STATE } from './types'
11
+
12
+ /**
13
+ * Combined HUD store type
14
+ */
15
+ export type HudStore = HudState & HudActions
16
+
17
+ /**
18
+ * Create the HUD store
19
+ */
20
+ export const useHudStore = create<HudStore>()(
21
+ persist(
22
+ (set) => ({
23
+ ...DEFAULT_HUD_STATE,
24
+
25
+ open: () => set({ isOpen: true }),
26
+
27
+ close: () => set({ isOpen: false }),
28
+
29
+ toggle: () => set((state) => ({ isOpen: !state.isOpen })),
30
+
31
+ setActivePanel: (panel: HudPanelType | null) =>
32
+ set({ activePanel: panel }),
33
+
34
+ setPosition: (position: HudPosition) => set({ position }),
35
+
36
+ toggleMinimized: () =>
37
+ set((state) => ({ isMinimized: !state.isMinimized })),
38
+ }),
39
+ {
40
+ name: 'sigil-hud-storage',
41
+ partialize: (state) => ({
42
+ position: state.position,
43
+ isMinimized: state.isMinimized,
44
+ }),
45
+ }
46
+ )
47
+ )
48
+
49
+ /**
50
+ * Get HUD store state (for non-React contexts)
51
+ */
52
+ export function getHudState(): HudState {
53
+ const state = useHudStore.getState()
54
+ return {
55
+ isOpen: state.isOpen,
56
+ activePanel: state.activePanel,
57
+ position: state.position,
58
+ isMinimized: state.isMinimized,
59
+ }
60
+ }
@@ -0,0 +1,256 @@
1
+ /**
2
+ * HUD Theme Constants
3
+ *
4
+ * Shared design tokens for consistent styling across HUD components.
5
+ * Implements TASK-205 styling requirements.
6
+ */
7
+
8
+ /**
9
+ * Color palette
10
+ */
11
+ export const colors = {
12
+ // Brand
13
+ primary: '#10b981', // emerald-500
14
+ primaryLight: 'rgba(16, 185, 129, 0.2)',
15
+ primaryBorder: 'rgba(16, 185, 129, 0.3)',
16
+
17
+ // Status
18
+ success: '#22c55e', // green-500
19
+ successLight: 'rgba(34, 197, 94, 0.1)',
20
+ successBorder: 'rgba(34, 197, 94, 0.3)',
21
+
22
+ warning: '#eab308', // yellow-500
23
+ warningLight: 'rgba(234, 179, 8, 0.1)',
24
+ warningBorder: 'rgba(234, 179, 8, 0.3)',
25
+
26
+ error: '#ef4444', // red-500
27
+ errorLight: 'rgba(239, 68, 68, 0.1)',
28
+ errorBorder: 'rgba(239, 68, 68, 0.3)',
29
+
30
+ info: '#3b82f6', // blue-500
31
+ infoLight: 'rgba(59, 130, 246, 0.1)',
32
+ infoBorder: 'rgba(59, 130, 246, 0.3)',
33
+
34
+ // Neutral
35
+ text: '#fff',
36
+ textMuted: '#888',
37
+ textDim: '#666',
38
+ textDisabled: '#555',
39
+
40
+ background: 'rgba(0, 0, 0, 0.9)',
41
+ backgroundElevated: 'rgba(26, 26, 26, 1)',
42
+ backgroundHover: 'rgba(255, 255, 255, 0.05)',
43
+ backgroundActive: 'rgba(255, 255, 255, 0.08)',
44
+ backgroundInput: 'rgba(255, 255, 255, 0.02)',
45
+
46
+ border: 'rgba(255, 255, 255, 0.1)',
47
+ borderSubtle: 'rgba(255, 255, 255, 0.05)',
48
+ borderStrong: 'rgba(255, 255, 255, 0.2)',
49
+
50
+ // Effects
51
+ financial: '#ef4444',
52
+ destructive: '#f97316',
53
+ softDelete: '#eab308',
54
+ standard: '#22c55e',
55
+ local: '#3b82f6',
56
+ navigation: '#8b5cf6',
57
+ query: '#06b6d4',
58
+ } as const
59
+
60
+ /**
61
+ * Typography
62
+ */
63
+ export const typography = {
64
+ fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace',
65
+ fontFamilySans: 'ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif',
66
+
67
+ // Sizes
68
+ xs: '10px',
69
+ sm: '11px',
70
+ base: '12px',
71
+ md: '13px',
72
+ lg: '14px',
73
+
74
+ // Weights
75
+ normal: 400,
76
+ medium: 500,
77
+ semibold: 600,
78
+ bold: 700,
79
+
80
+ // Line heights
81
+ lineHeightTight: 1.25,
82
+ lineHeightNormal: 1.5,
83
+ lineHeightRelaxed: 1.6,
84
+
85
+ // Letter spacing
86
+ letterSpacingTight: '-0.02em',
87
+ letterSpacingNormal: '0',
88
+ letterSpacingWide: '0.5px',
89
+ } as const
90
+
91
+ /**
92
+ * Spacing
93
+ */
94
+ export const spacing = {
95
+ xs: '4px',
96
+ sm: '6px',
97
+ md: '8px',
98
+ lg: '12px',
99
+ xl: '16px',
100
+ '2xl': '20px',
101
+ '3xl': '24px',
102
+ } as const
103
+
104
+ /**
105
+ * Border radius
106
+ */
107
+ export const radii = {
108
+ sm: '4px',
109
+ md: '6px',
110
+ lg: '8px',
111
+ xl: '12px',
112
+ full: '9999px',
113
+ } as const
114
+
115
+ /**
116
+ * Shadows
117
+ */
118
+ export const shadows = {
119
+ sm: '0 2px 4px rgba(0, 0, 0, 0.2)',
120
+ md: '0 4px 12px rgba(0, 0, 0, 0.3)',
121
+ lg: '0 8px 20px rgba(0, 0, 0, 0.4)',
122
+ xl: '0 20px 40px rgba(0, 0, 0, 0.5)',
123
+ primary: '0 4px 12px rgba(16, 185, 129, 0.3)',
124
+ primaryHover: '0 6px 16px rgba(16, 185, 129, 0.4)',
125
+ } as const
126
+
127
+ /**
128
+ * Transitions
129
+ */
130
+ export const transitions = {
131
+ fast: '0.1s ease-out',
132
+ normal: '0.15s ease-out',
133
+ slow: '0.2s ease-out',
134
+ spring: '0.3s cubic-bezier(0.34, 1.56, 0.64, 1)',
135
+ } as const
136
+
137
+ /**
138
+ * Z-index
139
+ */
140
+ export const zIndex = {
141
+ base: 1,
142
+ dropdown: 100,
143
+ sticky: 1000,
144
+ fixed: 5000,
145
+ modal: 10000,
146
+ tooltip: 15000,
147
+ } as const
148
+
149
+ /**
150
+ * Common style patterns
151
+ */
152
+ export const patterns = {
153
+ // Buttons
154
+ button: {
155
+ base: {
156
+ padding: `${spacing.md} ${spacing.lg}`,
157
+ borderRadius: radii.md,
158
+ fontSize: typography.sm,
159
+ fontWeight: typography.medium,
160
+ cursor: 'pointer',
161
+ transition: transitions.normal,
162
+ border: '1px solid',
163
+ },
164
+ primary: {
165
+ backgroundColor: colors.primaryLight,
166
+ borderColor: colors.primaryBorder,
167
+ color: colors.primary,
168
+ },
169
+ secondary: {
170
+ backgroundColor: colors.backgroundInput,
171
+ borderColor: colors.border,
172
+ color: colors.textMuted,
173
+ },
174
+ danger: {
175
+ backgroundColor: colors.errorLight,
176
+ borderColor: colors.errorBorder,
177
+ color: colors.error,
178
+ },
179
+ },
180
+
181
+ // Inputs
182
+ input: {
183
+ base: {
184
+ padding: spacing.md,
185
+ backgroundColor: colors.backgroundInput,
186
+ border: `1px solid ${colors.border}`,
187
+ borderRadius: radii.md,
188
+ color: colors.text,
189
+ fontSize: typography.sm,
190
+ fontFamily: typography.fontFamily,
191
+ },
192
+ },
193
+
194
+ // Cards
195
+ card: {
196
+ base: {
197
+ padding: spacing.lg,
198
+ backgroundColor: 'rgba(0, 0, 0, 0.2)',
199
+ borderRadius: radii.lg,
200
+ border: `1px solid ${colors.borderSubtle}`,
201
+ },
202
+ },
203
+
204
+ // Labels
205
+ label: {
206
+ base: {
207
+ fontSize: typography.xs,
208
+ fontWeight: typography.semibold,
209
+ color: colors.textMuted,
210
+ textTransform: 'uppercase' as const,
211
+ letterSpacing: typography.letterSpacingWide,
212
+ },
213
+ },
214
+
215
+ // Badges
216
+ badge: {
217
+ base: {
218
+ padding: `2px ${spacing.sm}`,
219
+ borderRadius: radii.sm,
220
+ fontSize: '9px',
221
+ fontWeight: typography.semibold,
222
+ border: '1px solid',
223
+ textTransform: 'uppercase' as const,
224
+ letterSpacing: '0.3px',
225
+ },
226
+ },
227
+ } as const
228
+
229
+ /**
230
+ * Effect type to color mapping
231
+ */
232
+ export const effectColors: Record<string, string> = {
233
+ financial: colors.financial,
234
+ destructive: colors.destructive,
235
+ 'soft-delete': colors.softDelete,
236
+ standard: colors.standard,
237
+ local: colors.local,
238
+ navigation: colors.navigation,
239
+ query: colors.query,
240
+ }
241
+
242
+ /**
243
+ * Get effect color
244
+ */
245
+ export function getEffectColor(effect: string): string {
246
+ return effectColors[effect] ?? colors.textMuted
247
+ }
248
+
249
+ /**
250
+ * Create CSS properties object
251
+ */
252
+ export function createStyles<T extends Record<string, React.CSSProperties>>(
253
+ styles: T
254
+ ): T {
255
+ return styles
256
+ }