@harkenapp/sdk-react-native 0.0.1-alpha.1 → 0.0.1-alpha.2

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 (278) hide show
  1. package/README.md +44 -7
  2. package/app.plugin.cjs +12 -17
  3. package/dist/__mocks__/async-storage.d.ts +16 -0
  4. package/dist/__mocks__/async-storage.d.ts.map +1 -0
  5. package/dist/__mocks__/async-storage.js +39 -0
  6. package/dist/__mocks__/async-storage.js.map +1 -0
  7. package/dist/__mocks__/expo-document-picker.d.ts +26 -0
  8. package/dist/__mocks__/expo-document-picker.d.ts.map +1 -0
  9. package/dist/__mocks__/expo-document-picker.js +25 -0
  10. package/dist/__mocks__/expo-document-picker.js.map +1 -0
  11. package/dist/__mocks__/expo-file-system.d.ts +42 -0
  12. package/dist/__mocks__/expo-file-system.d.ts.map +1 -0
  13. package/dist/__mocks__/expo-file-system.js +37 -0
  14. package/dist/__mocks__/expo-file-system.js.map +1 -0
  15. package/dist/__mocks__/expo-image-picker.d.ts +30 -0
  16. package/dist/__mocks__/expo-image-picker.d.ts.map +1 -0
  17. package/dist/__mocks__/expo-image-picker.js +30 -0
  18. package/dist/__mocks__/expo-image-picker.js.map +1 -0
  19. package/dist/__mocks__/expo-secure-store.d.ts +15 -0
  20. package/dist/__mocks__/expo-secure-store.d.ts.map +1 -0
  21. package/dist/__mocks__/expo-secure-store.js +30 -0
  22. package/dist/__mocks__/expo-secure-store.js.map +1 -0
  23. package/dist/__mocks__/react-native.d.ts +73 -0
  24. package/dist/__mocks__/react-native.d.ts.map +1 -0
  25. package/dist/__mocks__/react-native.js +45 -0
  26. package/dist/__mocks__/react-native.js.map +1 -0
  27. package/dist/api/client.d.ts +8 -8
  28. package/dist/api/client.d.ts.map +1 -1
  29. package/dist/api/client.js +17 -19
  30. package/dist/api/client.js.map +1 -1
  31. package/dist/api/client.test.d.ts +2 -0
  32. package/dist/api/client.test.d.ts.map +1 -0
  33. package/dist/api/client.test.js +417 -0
  34. package/dist/api/client.test.js.map +1 -0
  35. package/dist/api/errors.d.ts +3 -3
  36. package/dist/api/errors.d.ts.map +1 -1
  37. package/dist/api/errors.js +3 -3
  38. package/dist/api/errors.js.map +1 -1
  39. package/dist/api/errors.test.d.ts +2 -0
  40. package/dist/api/errors.test.d.ts.map +1 -0
  41. package/dist/api/errors.test.js +155 -0
  42. package/dist/api/errors.test.js.map +1 -0
  43. package/dist/api/index.d.ts +6 -6
  44. package/dist/api/index.d.ts.map +1 -1
  45. package/dist/api/index.js.map +1 -1
  46. package/dist/api/retry.d.ts +1 -1
  47. package/dist/api/retry.d.ts.map +1 -1
  48. package/dist/api/retry.js.map +1 -1
  49. package/dist/api/retry.test.d.ts +2 -0
  50. package/dist/api/retry.test.d.ts.map +1 -0
  51. package/dist/api/retry.test.js +193 -0
  52. package/dist/api/retry.test.js.map +1 -0
  53. package/dist/attachments/FeedbackSheet.d.ts +36 -13
  54. package/dist/attachments/FeedbackSheet.d.ts.map +1 -1
  55. package/dist/attachments/FeedbackSheet.js +50 -30
  56. package/dist/attachments/FeedbackSheet.js.map +1 -1
  57. package/dist/attachments/index.d.ts +2 -2
  58. package/dist/components/AttachmentGrid.d.ts +12 -4
  59. package/dist/components/AttachmentGrid.d.ts.map +1 -1
  60. package/dist/components/AttachmentGrid.js +44 -34
  61. package/dist/components/AttachmentGrid.js.map +1 -1
  62. package/dist/components/AttachmentPicker.d.ts +3 -3
  63. package/dist/components/AttachmentPicker.d.ts.map +1 -1
  64. package/dist/components/AttachmentPicker.js +34 -36
  65. package/dist/components/AttachmentPicker.js.map +1 -1
  66. package/dist/components/AttachmentPreview.d.ts +10 -4
  67. package/dist/components/AttachmentPreview.d.ts.map +1 -1
  68. package/dist/components/AttachmentPreview.js +48 -34
  69. package/dist/components/AttachmentPreview.js.map +1 -1
  70. package/dist/components/CategorySelector.d.ts +3 -3
  71. package/dist/components/CategorySelector.d.ts.map +1 -1
  72. package/dist/components/CategorySelector.js +21 -27
  73. package/dist/components/CategorySelector.js.map +1 -1
  74. package/dist/components/FeedbackForm.d.ts +3 -3
  75. package/dist/components/FeedbackForm.d.ts.map +1 -1
  76. package/dist/components/FeedbackForm.js +7 -8
  77. package/dist/components/FeedbackForm.js.map +1 -1
  78. package/dist/components/FeedbackSheet.d.ts +34 -11
  79. package/dist/components/FeedbackSheet.d.ts.map +1 -1
  80. package/dist/components/FeedbackSheet.js +46 -28
  81. package/dist/components/FeedbackSheet.js.map +1 -1
  82. package/dist/components/ThemedButton.d.ts +16 -5
  83. package/dist/components/ThemedButton.d.ts.map +1 -1
  84. package/dist/components/ThemedButton.js +38 -29
  85. package/dist/components/ThemedButton.js.map +1 -1
  86. package/dist/components/ThemedText.d.ts +3 -3
  87. package/dist/components/ThemedText.d.ts.map +1 -1
  88. package/dist/components/ThemedText.js +1 -1
  89. package/dist/components/ThemedText.js.map +1 -1
  90. package/dist/components/ThemedTextInput.d.ts +11 -2
  91. package/dist/components/ThemedTextInput.d.ts.map +1 -1
  92. package/dist/components/ThemedTextInput.js +19 -9
  93. package/dist/components/ThemedTextInput.js.map +1 -1
  94. package/dist/components/UploadStatusOverlay.d.ts +11 -3
  95. package/dist/components/UploadStatusOverlay.d.ts.map +1 -1
  96. package/dist/components/UploadStatusOverlay.js +59 -76
  97. package/dist/components/UploadStatusOverlay.js.map +1 -1
  98. package/dist/components/index.d.ts +18 -18
  99. package/dist/components/index.d.ts.map +1 -1
  100. package/dist/components/index.js.map +1 -1
  101. package/dist/context/HarkenContext.d.ts +20 -15
  102. package/dist/context/HarkenContext.d.ts.map +1 -1
  103. package/dist/context/HarkenContext.js +20 -17
  104. package/dist/context/HarkenContext.js.map +1 -1
  105. package/dist/context/index.d.ts +2 -2
  106. package/dist/domain/index.d.ts +2 -2
  107. package/dist/domain/index.d.ts.map +1 -1
  108. package/dist/domain/index.js.map +1 -1
  109. package/dist/hooks/index.d.ts +5 -5
  110. package/dist/hooks/useAnonymousId.js +1 -1
  111. package/dist/hooks/useAnonymousId.test.d.ts +2 -0
  112. package/dist/hooks/useAnonymousId.test.d.ts.map +1 -0
  113. package/dist/hooks/useAnonymousId.test.js +154 -0
  114. package/dist/hooks/useAnonymousId.test.js.map +1 -0
  115. package/dist/hooks/useAttachmentPicker.d.ts +3 -3
  116. package/dist/hooks/useAttachmentPicker.js +7 -7
  117. package/dist/hooks/useAttachmentStatus.d.ts +1 -1
  118. package/dist/hooks/useAttachmentStatus.d.ts.map +1 -1
  119. package/dist/hooks/useAttachmentStatus.js.map +1 -1
  120. package/dist/hooks/useAttachmentUpload.d.ts +2 -2
  121. package/dist/hooks/useAttachmentUpload.d.ts.map +1 -1
  122. package/dist/hooks/useAttachmentUpload.js +5 -5
  123. package/dist/hooks/useAttachmentUpload.js.map +1 -1
  124. package/dist/hooks/useAttachmentUpload.test.d.ts +2 -0
  125. package/dist/hooks/useAttachmentUpload.test.d.ts.map +1 -0
  126. package/dist/hooks/useAttachmentUpload.test.js +542 -0
  127. package/dist/hooks/useAttachmentUpload.test.js.map +1 -0
  128. package/dist/hooks/useFeedback.d.ts +4 -4
  129. package/dist/hooks/useFeedback.d.ts.map +1 -1
  130. package/dist/hooks/useFeedback.js +3 -5
  131. package/dist/hooks/useFeedback.js.map +1 -1
  132. package/dist/hooks/useFeedback.test.d.ts +2 -0
  133. package/dist/hooks/useFeedback.test.d.ts.map +1 -0
  134. package/dist/hooks/useFeedback.test.js +299 -0
  135. package/dist/hooks/useFeedback.test.js.map +1 -0
  136. package/dist/hooks/useHarkenContext.d.ts +1 -1
  137. package/dist/hooks/useHarkenContext.js +1 -1
  138. package/dist/hooks/useHarkenTheme.d.ts +27 -3
  139. package/dist/hooks/useHarkenTheme.d.ts.map +1 -1
  140. package/dist/hooks/useHarkenTheme.js +26 -2
  141. package/dist/hooks/useHarkenTheme.js.map +1 -1
  142. package/dist/index.d.ts +28 -28
  143. package/dist/index.d.ts.map +1 -1
  144. package/dist/index.js.map +1 -1
  145. package/dist/services/index.d.ts +3 -3
  146. package/dist/services/index.d.ts.map +1 -1
  147. package/dist/services/index.js.map +1 -1
  148. package/dist/services/uploadQueueService.d.ts +2 -2
  149. package/dist/services/uploadQueueService.d.ts.map +1 -1
  150. package/dist/services/uploadQueueService.js +16 -17
  151. package/dist/services/uploadQueueService.js.map +1 -1
  152. package/dist/services/uploadQueueService.test.d.ts +2 -0
  153. package/dist/services/uploadQueueService.test.d.ts.map +1 -0
  154. package/dist/services/uploadQueueService.test.js +426 -0
  155. package/dist/services/uploadQueueService.test.js.map +1 -0
  156. package/dist/services/uploadQueueStorage.d.ts +1 -1
  157. package/dist/services/uploadQueueStorage.d.ts.map +1 -1
  158. package/dist/services/uploadQueueStorage.js +4 -4
  159. package/dist/services/uploadQueueStorage.js.map +1 -1
  160. package/dist/services/uploadQueueStorage.test.d.ts +2 -0
  161. package/dist/services/uploadQueueStorage.test.d.ts.map +1 -0
  162. package/dist/services/uploadQueueStorage.test.js +200 -0
  163. package/dist/services/uploadQueueStorage.test.js.map +1 -0
  164. package/dist/storage/IdentityStore.d.ts +1 -1
  165. package/dist/storage/IdentityStore.d.ts.map +1 -1
  166. package/dist/storage/IdentityStore.js.map +1 -1
  167. package/dist/storage/IdentityStore.test.d.ts +2 -0
  168. package/dist/storage/IdentityStore.test.d.ts.map +1 -0
  169. package/dist/storage/IdentityStore.test.js +176 -0
  170. package/dist/storage/IdentityStore.test.js.map +1 -0
  171. package/dist/storage/SecureStoreAdapter.d.ts +1 -1
  172. package/dist/storage/SecureStoreAdapter.test.d.ts +2 -0
  173. package/dist/storage/SecureStoreAdapter.test.d.ts.map +1 -0
  174. package/dist/storage/SecureStoreAdapter.test.js +114 -0
  175. package/dist/storage/SecureStoreAdapter.test.js.map +1 -0
  176. package/dist/storage/defaultStorage.d.ts +1 -1
  177. package/dist/storage/defaultStorage.js +4 -4
  178. package/dist/storage/defaultStorage.test.d.ts +2 -0
  179. package/dist/storage/defaultStorage.test.d.ts.map +1 -0
  180. package/dist/storage/defaultStorage.test.js +159 -0
  181. package/dist/storage/defaultStorage.test.js.map +1 -0
  182. package/dist/storage/index.d.ts +5 -5
  183. package/dist/storage/types.js +1 -1
  184. package/dist/theme/defaults.d.ts +14 -3
  185. package/dist/theme/defaults.d.ts.map +1 -1
  186. package/dist/theme/defaults.js +58 -43
  187. package/dist/theme/defaults.js.map +1 -1
  188. package/dist/theme/index.d.ts +3 -2
  189. package/dist/theme/index.d.ts.map +1 -1
  190. package/dist/theme/index.js +4 -1
  191. package/dist/theme/index.js.map +1 -1
  192. package/dist/theme/resolver.d.ts +16 -0
  193. package/dist/theme/resolver.d.ts.map +1 -0
  194. package/dist/theme/resolver.js +375 -0
  195. package/dist/theme/resolver.js.map +1 -0
  196. package/dist/theme/resolver.test.d.ts +2 -0
  197. package/dist/theme/resolver.test.d.ts.map +1 -0
  198. package/dist/theme/resolver.test.js +344 -0
  199. package/dist/theme/resolver.test.js.map +1 -0
  200. package/dist/theme/types.d.ts +378 -5
  201. package/dist/theme/types.d.ts.map +1 -1
  202. package/dist/types/config.d.ts +4 -4
  203. package/dist/types/index.d.ts +2 -2
  204. package/dist/utils/index.d.ts +1 -1
  205. package/dist/utils/uuid.d.ts.map +1 -1
  206. package/dist/utils/uuid.js +4 -5
  207. package/dist/utils/uuid.js.map +1 -1
  208. package/dist/utils/uuid.test.d.ts +2 -0
  209. package/dist/utils/uuid.test.d.ts.map +1 -0
  210. package/dist/utils/uuid.test.js +78 -0
  211. package/dist/utils/uuid.test.js.map +1 -0
  212. package/package.json +21 -13
  213. package/src/@types/expo-file-system-legacy.d.ts +3 -3
  214. package/src/__mocks__/async-storage.ts +46 -0
  215. package/src/__mocks__/expo-document-picker.ts +41 -0
  216. package/src/__mocks__/expo-file-system.ts +62 -0
  217. package/src/__mocks__/expo-image-picker.ts +48 -0
  218. package/src/__mocks__/expo-secure-store.ts +29 -0
  219. package/src/__mocks__/react-native.ts +46 -0
  220. package/src/api/client.test.ts +515 -0
  221. package/src/api/client.ts +45 -64
  222. package/src/api/errors.test.ts +193 -0
  223. package/src/api/errors.ts +7 -11
  224. package/src/api/index.ts +6 -10
  225. package/src/api/retry.test.ts +251 -0
  226. package/src/api/retry.ts +3 -6
  227. package/src/attachments/FeedbackSheet.tsx +100 -80
  228. package/src/attachments/index.ts +2 -2
  229. package/src/components/AttachmentGrid.tsx +54 -45
  230. package/src/components/AttachmentPicker.tsx +43 -54
  231. package/src/components/AttachmentPreview.tsx +51 -47
  232. package/src/components/CategorySelector.tsx +29 -35
  233. package/src/components/FeedbackForm.tsx +23 -35
  234. package/src/components/FeedbackSheet.tsx +89 -68
  235. package/src/components/ThemedButton.tsx +49 -47
  236. package/src/components/ThemedText.tsx +7 -10
  237. package/src/components/ThemedTextInput.tsx +23 -13
  238. package/src/components/UploadStatusOverlay.tsx +66 -89
  239. package/src/components/index.ts +18 -21
  240. package/src/context/HarkenContext.tsx +29 -28
  241. package/src/context/index.ts +2 -2
  242. package/src/domain/index.ts +2 -5
  243. package/src/domain/upload-queue.ts +5 -5
  244. package/src/hooks/index.ts +5 -5
  245. package/src/hooks/useAnonymousId.test.ts +189 -0
  246. package/src/hooks/useAnonymousId.ts +3 -3
  247. package/src/hooks/useAttachmentPicker.ts +12 -12
  248. package/src/hooks/useAttachmentStatus.ts +12 -16
  249. package/src/hooks/useAttachmentUpload.test.ts +632 -0
  250. package/src/hooks/useAttachmentUpload.ts +45 -54
  251. package/src/hooks/useFeedback.test.ts +376 -0
  252. package/src/hooks/useFeedback.ts +12 -14
  253. package/src/hooks/useHarkenContext.ts +4 -4
  254. package/src/hooks/useHarkenTheme.ts +30 -6
  255. package/src/index.ts +28 -52
  256. package/src/services/index.ts +3 -9
  257. package/src/services/uploadQueueService.test.ts +489 -0
  258. package/src/services/uploadQueueService.ts +40 -56
  259. package/src/services/uploadQueueStorage.test.ts +243 -0
  260. package/src/services/uploadQueueStorage.ts +7 -9
  261. package/src/storage/IdentityStore.test.ts +173 -0
  262. package/src/storage/IdentityStore.ts +4 -5
  263. package/src/storage/SecureStoreAdapter.test.ts +147 -0
  264. package/src/storage/SecureStoreAdapter.ts +1 -1
  265. package/src/storage/defaultStorage.test.ts +159 -0
  266. package/src/storage/defaultStorage.ts +6 -6
  267. package/src/storage/index.ts +5 -5
  268. package/src/storage/types.ts +1 -1
  269. package/src/theme/defaults.ts +75 -46
  270. package/src/theme/index.ts +15 -2
  271. package/src/theme/resolver.test.ts +411 -0
  272. package/src/theme/resolver.ts +446 -0
  273. package/src/theme/types.ts +453 -15
  274. package/src/types/config.ts +4 -4
  275. package/src/types/index.ts +2 -2
  276. package/src/utils/index.ts +1 -1
  277. package/src/utils/uuid.test.ts +85 -0
  278. package/src/utils/uuid.ts +4 -7
@@ -1,15 +1,21 @@
1
1
  /**
2
2
  * Color tokens for Harken SDK theming.
3
3
  * All colors support full override by host apps.
4
+ *
5
+ * Base tokens are required. Component tokens are optional and fall back to base tokens.
4
6
  */
5
7
  export interface HarkenColors {
8
+ // === BASE TOKENS (required) ===
9
+
6
10
  /** Primary brand color for buttons and accents */
7
11
  primary: string;
8
12
  /** Darker variant of primary for pressed states */
9
13
  primaryPressed: string;
10
- /** Background color for the feedback form */
14
+ /** App-level background color */
11
15
  background: string;
12
- /** Secondary background (cards, inputs) */
16
+ /** Container/modal surface color (distinct from background) */
17
+ surface: string;
18
+ /** @deprecated Use `surface` instead. Kept for backwards compatibility. */
13
19
  backgroundSecondary: string;
14
20
  /** Primary text color */
15
21
  text: string;
@@ -41,6 +47,106 @@ export interface HarkenColors {
41
47
  accent2: string;
42
48
  /** Accent color 3 (e.g., files option) */
43
49
  accent3: string;
50
+
51
+ // === COMPONENT TOKENS (optional, fall back to base tokens) ===
52
+
53
+ // Category Chips
54
+ /** Chip background color (falls back to surface) */
55
+ chipBackground?: string;
56
+ /** Selected chip background color (falls back to primary) */
57
+ chipBackgroundSelected?: string;
58
+ /** Chip border color (falls back to border) */
59
+ chipBorder?: string;
60
+ /** Selected chip border color (falls back to primary) */
61
+ chipBorderSelected?: string;
62
+ /** Chip text color (falls back to text) */
63
+ chipText?: string;
64
+ /** Selected chip text color (falls back to textOnPrimary) */
65
+ chipTextSelected?: string;
66
+
67
+ // Text Input
68
+ /** Input background color (falls back to surface) */
69
+ inputBackground?: string;
70
+ /** Input border color (falls back to border) */
71
+ inputBorder?: string;
72
+ /** Input focused border color (falls back to borderFocused) */
73
+ inputBorderFocused?: string;
74
+ /** Input error border color (falls back to error) */
75
+ inputBorderError?: string;
76
+ /** Input text color (falls back to text) */
77
+ inputText?: string;
78
+ /** Input placeholder color (falls back to textPlaceholder) */
79
+ inputPlaceholder?: string;
80
+
81
+ // Buttons - Primary variant
82
+ /** Primary button background (falls back to primary) */
83
+ buttonPrimaryBackground?: string;
84
+ /** Primary button pressed background (falls back to primaryPressed) */
85
+ buttonPrimaryBackgroundPressed?: string;
86
+ /** Primary button text color (falls back to textOnPrimary) */
87
+ buttonPrimaryText?: string;
88
+
89
+ // Buttons - Secondary variant
90
+ /** Secondary button background (falls back to surface) */
91
+ buttonSecondaryBackground?: string;
92
+ /** Secondary button border color (falls back to border) */
93
+ buttonSecondaryBorder?: string;
94
+ /** Secondary button text color (falls back to text) */
95
+ buttonSecondaryText?: string;
96
+
97
+ // Buttons - Ghost variant
98
+ /** Ghost button text color (falls back to text) */
99
+ buttonGhostText?: string;
100
+
101
+ // Attachment Add Button
102
+ /** Add button background (falls back to surface) */
103
+ addButtonBackground?: string;
104
+ /** Add button pressed background (falls back to border) */
105
+ addButtonBackgroundPressed?: string;
106
+ /** Add button border color (falls back to border) */
107
+ addButtonBorder?: string;
108
+ /** Add button icon color (falls back to textSecondary) */
109
+ addButtonIcon?: string;
110
+ /** Add button text color (falls back to textSecondary) */
111
+ addButtonText?: string;
112
+
113
+ // Attachment Tile
114
+ /** Tile background color (falls back to surface) */
115
+ tileBackground?: string;
116
+ /** Tile border color (falls back to border) */
117
+ tileBorder?: string;
118
+
119
+ // Upload Status Overlay
120
+ /** Upload overlay background (falls back to overlay) */
121
+ uploadOverlay?: string;
122
+ /** Upload error overlay background (falls back to overlayDark) */
123
+ uploadOverlayError?: string;
124
+ /** Upload progress track color */
125
+ uploadProgressTrack?: string;
126
+ /** Upload progress fill color (falls back to primary) */
127
+ uploadProgressFill?: string;
128
+ /** Upload success badge color (falls back to success) */
129
+ uploadBadgeSuccess?: string;
130
+ /** Upload overlay text color (falls back to textOnPrimary) */
131
+ uploadText?: string;
132
+
133
+ // Attachment Picker
134
+ /** Picker overlay background (falls back to overlay) */
135
+ pickerOverlay?: string;
136
+ /** Picker sheet background (falls back to background) */
137
+ pickerBackground?: string;
138
+ /** Picker handle color (falls back to textSecondary) */
139
+ pickerHandle?: string;
140
+ /** Picker option background (falls back to surface) */
141
+ pickerOptionBackground?: string;
142
+ /** Picker option pressed background (falls back to border) */
143
+ pickerOptionBackgroundPressed?: string;
144
+ /** Picker cancel text color (falls back to error) */
145
+ pickerCancelText?: string;
146
+
147
+ // Form Container
148
+ /** Form background color (falls back to transparent for modal embedding) */
149
+ formBackground?: string;
44
150
  }
45
151
 
46
152
  /**
@@ -80,23 +186,27 @@ export interface HarkenTypography {
80
186
 
81
187
  /** Font weight values supported across platforms */
82
188
  export type TextWeight =
83
- | 'normal'
84
- | 'bold'
85
- | '100'
86
- | '200'
87
- | '300'
88
- | '400'
89
- | '500'
90
- | '600'
91
- | '700'
92
- | '800'
93
- | '900';
189
+ | "normal"
190
+ | "bold"
191
+ | "100"
192
+ | "200"
193
+ | "300"
194
+ | "400"
195
+ | "500"
196
+ | "600"
197
+ | "700"
198
+ | "800"
199
+ | "900";
94
200
 
95
201
  /**
96
202
  * Spacing tokens for consistent layout.
97
203
  * All values are in logical pixels.
204
+ *
205
+ * Base tokens are required. Component tokens are optional and fall back to base tokens.
98
206
  */
99
207
  export interface HarkenSpacing {
208
+ // === BASE TOKENS (required) ===
209
+
100
210
  /** Extra small spacing (4px default) */
101
211
  xs: number;
102
212
  /** Small spacing (8px default) */
@@ -109,13 +219,38 @@ export interface HarkenSpacing {
109
219
  xl: number;
110
220
  /** 2x extra large spacing (48px default) */
111
221
  xxl: number;
222
+
223
+ // === COMPONENT TOKENS (optional, fall back to base tokens) ===
224
+
225
+ /** Chip vertical padding (falls back to sm) */
226
+ chipPaddingVertical?: number;
227
+ /** Chip horizontal padding (falls back to md) */
228
+ chipPaddingHorizontal?: number;
229
+ /** Gap between chips (falls back to sm) */
230
+ chipGap?: number;
231
+ /** Input padding (falls back to md) */
232
+ inputPadding?: number;
233
+ /** Button vertical padding (falls back to sm) */
234
+ buttonPaddingVertical?: number;
235
+ /** Button horizontal padding (falls back to md) */
236
+ buttonPaddingHorizontal?: number;
237
+ /** Form container padding (falls back to lg) */
238
+ formPadding?: number;
239
+ /** Gap between form sections (falls back to lg) */
240
+ sectionGap?: number;
241
+ /** Gap between attachment tiles (falls back to sm) */
242
+ tileGap?: number;
112
243
  }
113
244
 
114
245
  /**
115
246
  * Border radius tokens for rounded corners.
116
247
  * All values are in logical pixels.
248
+ *
249
+ * Base tokens are required. Component tokens are optional and fall back to base tokens.
117
250
  */
118
251
  export interface HarkenRadii {
252
+ // === BASE TOKENS (required) ===
253
+
119
254
  /** No radius */
120
255
  none: number;
121
256
  /** Small radius for subtle rounding (4px default) */
@@ -128,16 +263,62 @@ export interface HarkenRadii {
128
263
  xl: number;
129
264
  /** Full/pill radius */
130
265
  full: number;
266
+
267
+ // === COMPONENT TOKENS (optional, fall back to base tokens) ===
268
+
269
+ /** Chip border radius (falls back to full) */
270
+ chip?: number;
271
+ /** Input border radius (falls back to md) */
272
+ input?: number;
273
+ /** Button border radius (falls back to md) */
274
+ button?: number;
275
+ /** Attachment tile border radius (falls back to md) */
276
+ tile?: number;
277
+ /** Form container border radius (falls back to lg) */
278
+ form?: number;
279
+ /** Picker sheet border radius (falls back to xl) */
280
+ picker?: number;
281
+ }
282
+
283
+ /**
284
+ * Sizing tokens for component dimensions.
285
+ * All values are in logical pixels and optional with sensible defaults.
286
+ */
287
+ export interface HarkenSizing {
288
+ /** Button minimum height (default: 48) */
289
+ buttonMinHeight?: number;
290
+ /** Input minimum height (default: 44) */
291
+ inputMinHeight?: number;
292
+ /** Attachment tile size (default: 80) */
293
+ tileSize?: number;
294
+ /** Add button icon size (default: 28) */
295
+ addButtonIconSize?: number;
296
+ /** Picker icon container size (default: 44) */
297
+ pickerIconSize?: number;
131
298
  }
132
299
 
133
300
  /**
134
- * Complete theme object combining all token types.
301
+ * Opacity tokens for interactive states.
302
+ * All values are between 0 and 1 and optional with sensible defaults.
303
+ */
304
+ export interface HarkenOpacity {
305
+ /** Disabled state opacity (default: 0.6) */
306
+ disabled?: number;
307
+ /** Pressed state opacity (default: 0.8) */
308
+ pressed?: number;
309
+ }
310
+
311
+ /**
312
+ * Input theme object with optional component tokens.
313
+ * Used for theme configuration by consumers.
135
314
  */
136
315
  export interface HarkenTheme {
137
316
  colors: HarkenColors;
138
317
  typography: HarkenTypography;
139
318
  spacing: HarkenSpacing;
140
319
  radii: HarkenRadii;
320
+ sizing?: HarkenSizing;
321
+ opacity?: HarkenOpacity;
141
322
  }
142
323
 
143
324
  /**
@@ -149,9 +330,266 @@ export type PartialHarkenTheme = {
149
330
  typography?: Partial<HarkenTypography>;
150
331
  spacing?: Partial<HarkenSpacing>;
151
332
  radii?: Partial<HarkenRadii>;
333
+ sizing?: Partial<HarkenSizing>;
334
+ opacity?: Partial<HarkenOpacity>;
152
335
  };
153
336
 
154
337
  /**
155
338
  * Theme mode for automatic light/dark theming.
156
339
  */
157
- export type ThemeMode = 'light' | 'dark' | 'system';
340
+ export type ThemeMode = "light" | "dark" | "system";
341
+
342
+ // ============================================================================
343
+ // RESOLVED THEME TYPES
344
+ // These types represent the fully-resolved theme after fallback resolution.
345
+ // Components should use these types - all values are guaranteed to exist.
346
+ // ============================================================================
347
+
348
+ /**
349
+ * Fully resolved color tokens with all component tokens populated.
350
+ * All optional component tokens have been resolved to their fallback values.
351
+ */
352
+ export interface ResolvedHarkenColors {
353
+ // Base tokens
354
+ primary: string;
355
+ primaryPressed: string;
356
+ background: string;
357
+ surface: string;
358
+ backgroundSecondary: string;
359
+ text: string;
360
+ textSecondary: string;
361
+ textPlaceholder: string;
362
+ textOnPrimary: string;
363
+ border: string;
364
+ borderFocused: string;
365
+ error: string;
366
+ success: string;
367
+ warning: string;
368
+ info: string;
369
+ overlay: string;
370
+ overlayDark: string;
371
+ accent1: string;
372
+ accent2: string;
373
+ accent3: string;
374
+
375
+ // Component tokens (all resolved, no longer optional)
376
+ chipBackground: string;
377
+ chipBackgroundSelected: string;
378
+ chipBorder: string;
379
+ chipBorderSelected: string;
380
+ chipText: string;
381
+ chipTextSelected: string;
382
+
383
+ inputBackground: string;
384
+ inputBorder: string;
385
+ inputBorderFocused: string;
386
+ inputBorderError: string;
387
+ inputText: string;
388
+ inputPlaceholder: string;
389
+
390
+ buttonPrimaryBackground: string;
391
+ buttonPrimaryBackgroundPressed: string;
392
+ buttonPrimaryText: string;
393
+ buttonSecondaryBackground: string;
394
+ buttonSecondaryBorder: string;
395
+ buttonSecondaryText: string;
396
+ buttonGhostText: string;
397
+
398
+ addButtonBackground: string;
399
+ addButtonBackgroundPressed: string;
400
+ addButtonBorder: string;
401
+ addButtonIcon: string;
402
+ addButtonText: string;
403
+
404
+ tileBackground: string;
405
+ tileBorder: string;
406
+
407
+ uploadOverlay: string;
408
+ uploadOverlayError: string;
409
+ uploadProgressTrack: string;
410
+ uploadProgressFill: string;
411
+ uploadBadgeSuccess: string;
412
+ uploadText: string;
413
+
414
+ pickerOverlay: string;
415
+ pickerBackground: string;
416
+ pickerHandle: string;
417
+ pickerOptionBackground: string;
418
+ pickerOptionBackgroundPressed: string;
419
+ pickerCancelText: string;
420
+
421
+ formBackground: string;
422
+ }
423
+
424
+ /**
425
+ * Fully resolved spacing tokens with all component tokens populated.
426
+ */
427
+ export interface ResolvedHarkenSpacing {
428
+ // Base tokens
429
+ xs: number;
430
+ sm: number;
431
+ md: number;
432
+ lg: number;
433
+ xl: number;
434
+ xxl: number;
435
+
436
+ // Component tokens (all resolved)
437
+ chipPaddingVertical: number;
438
+ chipPaddingHorizontal: number;
439
+ chipGap: number;
440
+ inputPadding: number;
441
+ buttonPaddingVertical: number;
442
+ buttonPaddingHorizontal: number;
443
+ formPadding: number;
444
+ sectionGap: number;
445
+ tileGap: number;
446
+ }
447
+
448
+ /**
449
+ * Fully resolved radii tokens with all component tokens populated.
450
+ */
451
+ export interface ResolvedHarkenRadii {
452
+ // Base tokens
453
+ none: number;
454
+ sm: number;
455
+ md: number;
456
+ lg: number;
457
+ xl: number;
458
+ full: number;
459
+
460
+ // Component tokens (all resolved)
461
+ chip: number;
462
+ input: number;
463
+ button: number;
464
+ tile: number;
465
+ form: number;
466
+ picker: number;
467
+ }
468
+
469
+ /**
470
+ * Fully resolved sizing tokens.
471
+ */
472
+ export interface ResolvedHarkenSizing {
473
+ buttonMinHeight: number;
474
+ inputMinHeight: number;
475
+ tileSize: number;
476
+ addButtonIconSize: number;
477
+ pickerIconSize: number;
478
+ }
479
+
480
+ /**
481
+ * Fully resolved opacity tokens.
482
+ */
483
+ export interface ResolvedHarkenOpacity {
484
+ disabled: number;
485
+ pressed: number;
486
+ }
487
+
488
+ /**
489
+ * Structured component token aliases for better discoverability.
490
+ * Maps to flat tokens but grouped by component for ergonomic access.
491
+ */
492
+ export interface HarkenComponentTokens {
493
+ chip: {
494
+ background: string;
495
+ backgroundSelected: string;
496
+ border: string;
497
+ borderSelected: string;
498
+ text: string;
499
+ textSelected: string;
500
+ paddingVertical: number;
501
+ paddingHorizontal: number;
502
+ gap: number;
503
+ radius: number;
504
+ };
505
+ input: {
506
+ background: string;
507
+ border: string;
508
+ borderFocused: string;
509
+ borderError: string;
510
+ text: string;
511
+ placeholder: string;
512
+ padding: number;
513
+ radius: number;
514
+ minHeight: number;
515
+ };
516
+ button: {
517
+ primary: {
518
+ background: string;
519
+ backgroundPressed: string;
520
+ text: string;
521
+ };
522
+ secondary: {
523
+ background: string;
524
+ border: string;
525
+ text: string;
526
+ };
527
+ ghost: {
528
+ text: string;
529
+ };
530
+ paddingVertical: number;
531
+ paddingHorizontal: number;
532
+ radius: number;
533
+ minHeight: number;
534
+ };
535
+ addButton: {
536
+ background: string;
537
+ backgroundPressed: string;
538
+ border: string;
539
+ icon: string;
540
+ text: string;
541
+ iconSize: number;
542
+ };
543
+ tile: {
544
+ background: string;
545
+ border: string;
546
+ radius: number;
547
+ size: number;
548
+ gap: number;
549
+ };
550
+ upload: {
551
+ overlay: string;
552
+ overlayError: string;
553
+ progressTrack: string;
554
+ progressFill: string;
555
+ badgeSuccess: string;
556
+ text: string;
557
+ };
558
+ picker: {
559
+ overlay: string;
560
+ background: string;
561
+ handle: string;
562
+ optionBackground: string;
563
+ optionBackgroundPressed: string;
564
+ cancelText: string;
565
+ radius: number;
566
+ iconSize: number;
567
+ };
568
+ form: {
569
+ background: string;
570
+ padding: number;
571
+ sectionGap: number;
572
+ radius: number;
573
+ };
574
+ }
575
+
576
+ /**
577
+ * Fully resolved theme object with all fallbacks applied.
578
+ * This is what components receive - all values are guaranteed to exist.
579
+ */
580
+ export interface ResolvedHarkenTheme {
581
+ /** Flat color tokens (for compatibility) */
582
+ colors: ResolvedHarkenColors;
583
+ /** Typography tokens */
584
+ typography: HarkenTypography;
585
+ /** Flat spacing tokens (for compatibility) */
586
+ spacing: ResolvedHarkenSpacing;
587
+ /** Flat radii tokens (for compatibility) */
588
+ radii: ResolvedHarkenRadii;
589
+ /** Sizing tokens */
590
+ sizing: ResolvedHarkenSizing;
591
+ /** Opacity tokens */
592
+ opacity: ResolvedHarkenOpacity;
593
+ /** Structured component tokens (for discoverability) */
594
+ components: HarkenComponentTokens;
595
+ }
@@ -1,5 +1,5 @@
1
- import type { PartialHarkenTheme, ThemeMode } from '../theme';
2
- import type { SecureStorage } from '../storage';
1
+ import type { PartialHarkenTheme, ThemeMode } from "../theme";
2
+ import type { SecureStorage } from "../storage";
3
3
 
4
4
  /**
5
5
  * Configuration options for the Harken SDK.
@@ -88,12 +88,12 @@ export interface HarkenProviderProps {
88
88
  /**
89
89
  * Feedback category types supported by the API.
90
90
  */
91
- export type FeedbackCategory = 'bug' | 'idea' | 'ux' | 'other';
91
+ export type FeedbackCategory = "bug" | "idea" | "ux" | "other";
92
92
 
93
93
  /**
94
94
  * Platform type for device metadata.
95
95
  */
96
- export type Platform = 'ios' | 'android';
96
+ export type Platform = "ios" | "android";
97
97
 
98
98
  /**
99
99
  * Device metadata collected with feedback submissions.
@@ -4,7 +4,7 @@ export type {
4
4
  FeedbackCategory,
5
5
  Platform,
6
6
  DeviceMetadata,
7
- } from './config.js';
7
+ } from "./config.js";
8
8
 
9
9
  // Re-export OpenAPI generated types
10
- export type { components, operations, paths } from './openapi.js';
10
+ export type { components, operations, paths } from "./openapi.js";
@@ -1 +1 @@
1
- export { generateUUID } from './uuid';
1
+ export { generateUUID } from "./uuid";
@@ -0,0 +1,85 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { generateUUID } from "./uuid";
3
+
4
+ describe("generateUUID", () => {
5
+ // UUID v4 format: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
6
+ // where y is one of [8, 9, a, b]
7
+ const uuidV4Regex = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
8
+
9
+ it("generates valid UUID v4 format", () => {
10
+ const uuid = generateUUID();
11
+ expect(uuid).toMatch(uuidV4Regex);
12
+ });
13
+
14
+ it("generates 36-character string with hyphens", () => {
15
+ const uuid = generateUUID();
16
+ expect(uuid).toHaveLength(36);
17
+ expect(uuid.split("-")).toHaveLength(5);
18
+ });
19
+
20
+ it("has version 4 identifier in correct position", () => {
21
+ const uuid = generateUUID();
22
+ // The 13th character (index 14, after first two groups) should be '4'
23
+ expect(uuid[14]).toBe("4");
24
+ });
25
+
26
+ it("has valid variant bits", () => {
27
+ const uuid = generateUUID();
28
+ // The 17th character (index 19, after third group) should be 8, 9, a, or b
29
+ expect(["8", "9", "a", "b"]).toContain(uuid[19]!.toLowerCase());
30
+ });
31
+
32
+ it("uses lowercase hex characters", () => {
33
+ // Generate multiple to ensure consistent format
34
+ for (let i = 0; i < 10; i++) {
35
+ const uuid = generateUUID();
36
+ const hexPart = uuid.replace(/-/g, "");
37
+ expect(hexPart).toMatch(/^[0-9a-f]+$/);
38
+ }
39
+ });
40
+
41
+ describe("with crypto.getRandomValues available", () => {
42
+ it("uses crypto for randomness", () => {
43
+ // crypto.getRandomValues is available in Node.js
44
+ const uuid = generateUUID();
45
+ expect(uuid).toMatch(uuidV4Regex);
46
+ });
47
+ });
48
+
49
+ describe("fallback with Math.random", () => {
50
+ // Note: We can't easily test the Math.random fallback in Node.js
51
+ // because crypto is a read-only property. The fallback path
52
+ // is tested implicitly by the fact that our implementation handles
53
+ // the case where crypto might not exist (React Native older runtimes).
54
+ //
55
+ // Instead, we verify the algorithm produces valid UUIDs with any
56
+ // randomness source by testing the format constraints.
57
+
58
+ it("algorithm produces valid version and variant bits", () => {
59
+ // The UUID generation algorithm sets version=4 and variant=RFC4122
60
+ // regardless of which random source is used. We verify this by
61
+ // generating many UUIDs and checking format compliance.
62
+ for (let i = 0; i < 100; i++) {
63
+ const uuid = generateUUID();
64
+ // Version 4 at position 14
65
+ expect(uuid[14]).toBe("4");
66
+ // Variant at position 19 is 8, 9, a, or b
67
+ expect(["8", "9", "a", "b"]).toContain(uuid[19]!.toLowerCase());
68
+ }
69
+ });
70
+ });
71
+
72
+ describe("format consistency", () => {
73
+ it("consistently produces 8-4-4-4-12 format", () => {
74
+ for (let i = 0; i < 10; i++) {
75
+ const uuid = generateUUID();
76
+ const parts = uuid.split("-");
77
+ expect(parts[0]).toHaveLength(8);
78
+ expect(parts[1]).toHaveLength(4);
79
+ expect(parts[2]).toHaveLength(4);
80
+ expect(parts[3]).toHaveLength(4);
81
+ expect(parts[4]).toHaveLength(12);
82
+ }
83
+ });
84
+ });
85
+ });
package/src/utils/uuid.ts CHANGED
@@ -18,10 +18,7 @@ declare const crypto:
18
18
  */
19
19
  export function generateUUID(): string {
20
20
  // Use crypto.getRandomValues if available (React Native has this)
21
- if (
22
- typeof crypto !== 'undefined' &&
23
- typeof crypto.getRandomValues === 'function'
24
- ) {
21
+ if (typeof crypto !== "undefined" && typeof crypto.getRandomValues === "function") {
25
22
  return generateUUIDCrypto();
26
23
  }
27
24
 
@@ -64,8 +61,8 @@ function generateUUIDFallback(): string {
64
61
  */
65
62
  function formatUUID(bytes: Uint8Array): string {
66
63
  const hex = Array.from(bytes)
67
- .map((b) => b.toString(16).padStart(2, '0'))
68
- .join('');
64
+ .map((b) => b.toString(16).padStart(2, "0"))
65
+ .join("");
69
66
 
70
67
  return [
71
68
  hex.slice(0, 8),
@@ -73,5 +70,5 @@ function formatUUID(bytes: Uint8Array): string {
73
70
  hex.slice(12, 16),
74
71
  hex.slice(16, 20),
75
72
  hex.slice(20, 32),
76
- ].join('-');
73
+ ].join("-");
77
74
  }