@flickfyi/flkr 0.0.182 → 0.0.185

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 (243) hide show
  1. package/dist/{api-client-CAg1b8Bh.js → api-client-nn4U_Vfn.js} +1 -1
  2. package/dist/{app-DpM5nhb9.js → app-COXGJhrt.js} +3 -3
  3. package/dist/{bundler-DNrDcqyk.js → bundler-CW4YC73e.js} +4 -2
  4. package/dist/cli.js +11 -11
  5. package/dist/{delegate-DcVye0KE.js → delegate-aUVluBjB.js} +2 -2
  6. package/dist/{generate-B1l1z_FZ.js → generate-BeL1QPDi.js} +1 -1
  7. package/dist/{preview-Dqn_Tq6K.js → preview-DTVM8Csb.js} +2 -2
  8. package/dist/{publish-BmDqO1xU.js → publish-BJG-s-w5.js} +4 -4
  9. package/dist/{pull-CO80DMn8.js → pull-B_Yfjpi2.js} +1 -1
  10. package/dist/{save-8crfOeo1.js → save-UrfgmK2-.js} +2 -2
  11. package/dist/{sdk-app-BXWrf_pf.js → sdk-app-BAxznAf9.js} +3 -3
  12. package/dist/{sdk-deploy-CaEonlvx.js → sdk-deploy-D1wgQxse.js} +3 -3
  13. package/dist/{sdk-generation-CF8ATbrL.js → sdk-generation-CLQ-QrVM.js} +1 -1
  14. package/dist/{sdk-project-CKV3U3u9.js → sdk-project-BaO0fqhW.js} +1 -1
  15. package/dist/{use-auth-B6nwU2rp.js → use-auth-CtM4_BAY.js} +1 -1
  16. package/node_modules/@flickfyi/biscuit/.turbo/turbo-build.log +4 -4
  17. package/node_modules/@flickfyi/core/README.md +19 -9
  18. package/node_modules/@flickfyi/core/dist/chunks/{default-CuxDsQoM.js → default-BTmKidHV.js} +1 -1
  19. package/node_modules/@flickfyi/core/dist/chunks/{default-BB6YexEH.cjs → default-JhTq5I_y.cjs} +1 -1
  20. package/node_modules/@flickfyi/core/dist/chunks/{define-CUJRqVKY.cjs → define-BKCRhpuY.cjs} +1 -1
  21. package/node_modules/@flickfyi/core/dist/chunks/{define-e_VqSs4M.js → define-DZXgUsy7.js} +5 -5
  22. package/node_modules/@flickfyi/core/dist/kowalski/catalog.cjs +1 -1
  23. package/node_modules/@flickfyi/core/dist/kowalski/catalog.js +728 -549
  24. package/node_modules/@flickfyi/core/dist/kowalski/index.cjs +5 -5
  25. package/node_modules/@flickfyi/core/dist/kowalski/index.js +4297 -3700
  26. package/node_modules/@flickfyi/core/dist/kowalski/schemas.cjs +1 -0
  27. package/node_modules/@flickfyi/core/dist/kowalski/schemas.js +637 -0
  28. package/node_modules/@flickfyi/core/dist/kowalski-catalog.css +516 -0
  29. package/node_modules/@flickfyi/core/dist/kowalski.css +10 -0
  30. package/node_modules/@flickfyi/core/dist/themes/athletic/athletic.cjs +1 -1
  31. package/node_modules/@flickfyi/core/dist/themes/athletic/athletic.js +1 -1
  32. package/node_modules/@flickfyi/core/dist/themes/compact/compact.cjs +1 -1
  33. package/node_modules/@flickfyi/core/dist/themes/compact/compact.js +1 -1
  34. package/node_modules/@flickfyi/core/dist/themes/default/default.cjs +1 -1
  35. package/node_modules/@flickfyi/core/dist/themes/default/default.js +1 -1
  36. package/node_modules/@flickfyi/core/dist/themes/editorial/editorial.cjs +1 -1
  37. package/node_modules/@flickfyi/core/dist/themes/editorial/editorial.js +1 -1
  38. package/node_modules/@flickfyi/core/dist/themes/handwritten/handwritten.cjs +1 -1
  39. package/node_modules/@flickfyi/core/dist/themes/handwritten/handwritten.js +1 -1
  40. package/node_modules/@flickfyi/core/dist/themes/index.cjs +1 -1
  41. package/node_modules/@flickfyi/core/dist/themes/index.js +1 -1
  42. package/node_modules/@flickfyi/core/dist/themes/kids/kids.cjs +1 -1
  43. package/node_modules/@flickfyi/core/dist/themes/kids/kids.js +1 -1
  44. package/node_modules/@flickfyi/core/dist/themes/rounded/rounded.cjs +1 -1
  45. package/node_modules/@flickfyi/core/dist/themes/rounded/rounded.js +1 -1
  46. package/node_modules/@flickfyi/core/dist/themes/technical/technical.cjs +1 -1
  47. package/node_modules/@flickfyi/core/dist/themes/technical/technical.js +1 -1
  48. package/node_modules/@flickfyi/core/dist-types/hooks/use-feature-store/index.d.ts +43 -0
  49. package/node_modules/@flickfyi/core/dist-types/kowalski/calculations.d.ts +57 -3
  50. package/node_modules/@flickfyi/core/dist-types/kowalski/calculator.d.ts +7 -6
  51. package/node_modules/@flickfyi/core/dist-types/kowalski/catalog.d.cts +524 -809
  52. package/node_modules/@flickfyi/core/dist-types/kowalski/catalog.d.ts +524 -809
  53. package/node_modules/@flickfyi/core/dist-types/kowalski/components/bill-split.d.ts +38 -0
  54. package/node_modules/@flickfyi/core/dist-types/kowalski/components/bookmark.d.ts +48 -0
  55. package/node_modules/@flickfyi/core/dist-types/kowalski/components/budget.d.ts +39 -0
  56. package/node_modules/@flickfyi/core/dist-types/kowalski/components/calculator.d.ts +62 -0
  57. package/node_modules/@flickfyi/core/dist-types/kowalski/components/checklist.d.ts +35 -0
  58. package/node_modules/@flickfyi/core/dist-types/kowalski/components/color-palette.d.ts +32 -0
  59. package/node_modules/@flickfyi/core/dist-types/kowalski/components/contact.d.ts +41 -0
  60. package/node_modules/@flickfyi/core/dist-types/kowalski/components/countdown.d.ts +34 -0
  61. package/node_modules/@flickfyi/core/dist-types/kowalski/components/event.d.ts +43 -0
  62. package/node_modules/@flickfyi/core/dist-types/kowalski/components/expense.d.ts +40 -0
  63. package/node_modules/@flickfyi/core/dist-types/kowalski/components/goal.d.ts +36 -0
  64. package/node_modules/@flickfyi/core/dist-types/kowalski/components/habit.d.ts +57 -0
  65. package/node_modules/@flickfyi/core/dist-types/kowalski/components/ledger.d.ts +43 -0
  66. package/node_modules/@flickfyi/core/dist-types/kowalski/components/note.d.ts +35 -0
  67. package/node_modules/@flickfyi/core/dist-types/kowalski/components/poll.d.ts +41 -0
  68. package/node_modules/@flickfyi/core/dist-types/kowalski/components/random-picker.d.ts +69 -0
  69. package/node_modules/@flickfyi/core/dist-types/kowalski/components/rating.d.ts +33 -0
  70. package/node_modules/@flickfyi/core/dist-types/kowalski/components/reminder.d.ts +48 -0
  71. package/node_modules/@flickfyi/core/dist-types/kowalski/components/rotation.d.ts +39 -0
  72. package/node_modules/@flickfyi/core/dist-types/kowalski/components/scoreboard.d.ts +37 -0
  73. package/node_modules/@flickfyi/core/dist-types/kowalski/components/shopping-list.d.ts +41 -0
  74. package/node_modules/@flickfyi/core/dist-types/kowalski/components/task-board.d.ts +39 -0
  75. package/node_modules/@flickfyi/core/dist-types/kowalski/components/time-zone.d.ts +34 -0
  76. package/node_modules/@flickfyi/core/dist-types/kowalski/components/timer.d.ts +43 -0
  77. package/node_modules/@flickfyi/core/dist-types/kowalski/components/trip.d.ts +61 -0
  78. package/node_modules/@flickfyi/core/dist-types/kowalski/components/unit-converter.d.ts +106 -0
  79. package/node_modules/@flickfyi/core/dist-types/kowalski/create-schemas.d.ts +6 -0
  80. package/node_modules/@flickfyi/core/dist-types/kowalski/date-time.d.ts +1 -1
  81. package/node_modules/@flickfyi/core/dist-types/kowalski/everyday.d.ts +28 -8
  82. package/node_modules/@flickfyi/core/dist-types/kowalski/feature-schema.d.ts +65 -0
  83. package/node_modules/@flickfyi/core/dist-types/kowalski/format.d.ts +4 -0
  84. package/node_modules/@flickfyi/core/dist-types/kowalski/fortune.d.ts +21 -0
  85. package/node_modules/@flickfyi/core/dist-types/kowalski/frame.d.ts +4 -4
  86. package/node_modules/@flickfyi/core/dist-types/kowalski/index.d.cts +31 -7
  87. package/node_modules/@flickfyi/core/dist-types/kowalski/index.d.ts +31 -7
  88. package/node_modules/@flickfyi/core/dist-types/kowalski/lesson.d.ts +89 -0
  89. package/node_modules/@flickfyi/core/dist-types/kowalski/recipe.d.ts +129 -8
  90. package/node_modules/@flickfyi/core/dist-types/kowalski/schemas.d.cts +4 -0
  91. package/node_modules/@flickfyi/core/dist-types/kowalski/schemas.d.ts +4 -0
  92. package/node_modules/@flickfyi/core/dist-types/kowalski/time-since.d.ts +34 -0
  93. package/node_modules/@flickfyi/core/examples/example-metadata.ts +19 -2
  94. package/node_modules/@flickfyi/core/examples/kowalski/bill-split/bill-split.stories.tsx +25 -6
  95. package/node_modules/@flickfyi/core/examples/kowalski/bill-split/bill-split.tsx +6 -12
  96. package/node_modules/@flickfyi/core/examples/kowalski/bookmark/bookmark.stories.tsx +22 -5
  97. package/node_modules/@flickfyi/core/examples/kowalski/bookmark/bookmark.tsx +6 -18
  98. package/node_modules/@flickfyi/core/examples/kowalski/budget/budget.stories.tsx +26 -5
  99. package/node_modules/@flickfyi/core/examples/kowalski/budget/budget.tsx +6 -21
  100. package/node_modules/@flickfyi/core/examples/kowalski/calculator/calculator.stories.tsx +18 -5
  101. package/node_modules/@flickfyi/core/examples/kowalski/calculator/calculator.tsx +6 -19
  102. package/node_modules/@flickfyi/core/examples/kowalski/checklist/checklist.stories.tsx +25 -5
  103. package/node_modules/@flickfyi/core/examples/kowalski/checklist/checklist.tsx +6 -21
  104. package/node_modules/@flickfyi/core/examples/kowalski/color-palette/color-palette.stories.tsx +19 -6
  105. package/node_modules/@flickfyi/core/examples/kowalski/color-palette/color-palette.tsx +6 -17
  106. package/node_modules/@flickfyi/core/examples/kowalski/contact/contact.stories.tsx +30 -5
  107. package/node_modules/@flickfyi/core/examples/kowalski/contact/contact.tsx +6 -18
  108. package/node_modules/@flickfyi/core/examples/kowalski/countdown/countdown.stories.tsx +22 -5
  109. package/node_modules/@flickfyi/core/examples/kowalski/countdown/countdown.tsx +6 -18
  110. package/node_modules/@flickfyi/core/examples/kowalski/event/event.stories.tsx +23 -5
  111. package/node_modules/@flickfyi/core/examples/kowalski/event/event.tsx +6 -19
  112. package/node_modules/@flickfyi/core/examples/kowalski/expense/expense.stories.tsx +23 -5
  113. package/node_modules/@flickfyi/core/examples/kowalski/expense/expense.tsx +6 -18
  114. package/node_modules/@flickfyi/core/examples/kowalski/fortune/fortune.stories.tsx +61 -0
  115. package/node_modules/@flickfyi/core/examples/kowalski/fortune/fortune.tsx +11 -0
  116. package/node_modules/@flickfyi/core/examples/kowalski/goal/goal.stories.tsx +18 -5
  117. package/node_modules/@flickfyi/core/examples/kowalski/goal/goal.tsx +3 -19
  118. package/node_modules/@flickfyi/core/examples/kowalski/habit/habit.stories.tsx +22 -5
  119. package/node_modules/@flickfyi/core/examples/kowalski/habit/habit.tsx +6 -18
  120. package/node_modules/@flickfyi/core/examples/kowalski/ledger/ledger.stories.tsx +25 -5
  121. package/node_modules/@flickfyi/core/examples/kowalski/ledger/ledger.tsx +6 -20
  122. package/node_modules/@flickfyi/core/examples/kowalski/lesson/lesson.stories.tsx +99 -0
  123. package/node_modules/@flickfyi/core/examples/kowalski/lesson/lesson.tsx +11 -0
  124. package/node_modules/@flickfyi/core/examples/kowalski/note/note.stories.tsx +21 -5
  125. package/node_modules/@flickfyi/core/examples/kowalski/note/note.tsx +3 -17
  126. package/node_modules/@flickfyi/core/examples/kowalski/poll/poll.stories.tsx +26 -5
  127. package/node_modules/@flickfyi/core/examples/kowalski/poll/poll.tsx +3 -21
  128. package/node_modules/@flickfyi/core/examples/kowalski/random-picker/random-picker.stories.tsx +37 -6
  129. package/node_modules/@flickfyi/core/examples/kowalski/random-picker/random-picker.tsx +6 -18
  130. package/node_modules/@flickfyi/core/examples/kowalski/rating/rating.stories.tsx +18 -5
  131. package/node_modules/@flickfyi/core/examples/kowalski/rating/rating.tsx +6 -14
  132. package/node_modules/@flickfyi/core/examples/kowalski/recipe/recipe.stories.tsx +117 -5
  133. package/node_modules/@flickfyi/core/examples/kowalski/recipe/recipe.tsx +6 -115
  134. package/node_modules/@flickfyi/core/examples/kowalski/reminder/reminder.stories.tsx +22 -5
  135. package/node_modules/@flickfyi/core/examples/kowalski/reminder/reminder.tsx +6 -18
  136. package/node_modules/@flickfyi/core/examples/kowalski/rotation/rotation.stories.tsx +26 -5
  137. package/node_modules/@flickfyi/core/examples/kowalski/rotation/rotation.tsx +6 -22
  138. package/node_modules/@flickfyi/core/examples/kowalski/scoreboard/scoreboard.stories.tsx +26 -5
  139. package/node_modules/@flickfyi/core/examples/kowalski/scoreboard/scoreboard.tsx +6 -21
  140. package/node_modules/@flickfyi/core/examples/kowalski/shopping-list/shopping-list.stories.tsx +28 -6
  141. package/node_modules/@flickfyi/core/examples/kowalski/shopping-list/shopping-list.tsx +6 -22
  142. package/node_modules/@flickfyi/core/examples/kowalski/task-board/task-board.stories.tsx +27 -6
  143. package/node_modules/@flickfyi/core/examples/kowalski/task-board/task-board.tsx +6 -22
  144. package/node_modules/@flickfyi/core/examples/kowalski/time-since/time-since.stories.tsx +33 -0
  145. package/node_modules/@flickfyi/core/examples/kowalski/time-since/time-since.tsx +12 -0
  146. package/node_modules/@flickfyi/core/examples/kowalski/time-zone/time-zone.stories.tsx +22 -6
  147. package/node_modules/@flickfyi/core/examples/kowalski/time-zone/time-zone.tsx +6 -17
  148. package/node_modules/@flickfyi/core/examples/kowalski/timer/timer.stories.tsx +18 -5
  149. package/node_modules/@flickfyi/core/examples/kowalski/timer/timer.tsx +6 -18
  150. package/node_modules/@flickfyi/core/examples/kowalski/trip/trip.stories.tsx +25 -5
  151. package/node_modules/@flickfyi/core/examples/kowalski/trip/trip.tsx +3 -21
  152. package/node_modules/@flickfyi/core/examples/kowalski/unit-converter/unit-converter.stories.tsx +19 -6
  153. package/node_modules/@flickfyi/core/examples/kowalski/unit-converter/unit-converter.tsx +6 -19
  154. package/node_modules/@flickfyi/core/examples/parrott/calculator/calculator.stories.tsx +10 -3
  155. package/node_modules/@flickfyi/core/examples/parrott/calculator/calculator.tsx +2 -13
  156. package/node_modules/@flickfyi/core/examples/parrott/checklist/checklist.stories.tsx +10 -3
  157. package/node_modules/@flickfyi/core/examples/parrott/checklist/checklist.tsx +0 -8
  158. package/node_modules/@flickfyi/core/examples/parrott/countdown/countdown.stories.tsx +10 -3
  159. package/node_modules/@flickfyi/core/examples/parrott/countdown/countdown.tsx +0 -8
  160. package/node_modules/@flickfyi/core/examples/parrott/counter/counter.stories.tsx +10 -3
  161. package/node_modules/@flickfyi/core/examples/parrott/counter/counter.tsx +0 -8
  162. package/node_modules/@flickfyi/core/examples/parrott/fortune/fortune.stories.tsx +10 -3
  163. package/node_modules/@flickfyi/core/examples/parrott/fortune/fortune.tsx +0 -8
  164. package/node_modules/@flickfyi/core/examples/parrott/grid/grid.stories.tsx +10 -3
  165. package/node_modules/@flickfyi/core/examples/parrott/grid/grid.tsx +4 -9
  166. package/node_modules/@flickfyi/core/examples/parrott/kale-smoothie/kale-smoothie.stories.tsx +10 -3
  167. package/node_modules/@flickfyi/core/examples/parrott/kale-smoothie/kale-smoothie.tsx +0 -8
  168. package/node_modules/@flickfyi/core/examples/parrott/list/list.stories.tsx +10 -3
  169. package/node_modules/@flickfyi/core/examples/parrott/list/list.tsx +0 -8
  170. package/node_modules/@flickfyi/core/examples/parrott/log/log.stories.tsx +10 -3
  171. package/node_modules/@flickfyi/core/examples/parrott/log/log.tsx +0 -8
  172. package/node_modules/@flickfyi/core/examples/parrott/timed-task/timed-task.stories.tsx +10 -3
  173. package/node_modules/@flickfyi/core/examples/parrott/timed-task/timed-task.tsx +0 -8
  174. package/node_modules/@flickfyi/core/examples/parrott/timer/timer.stories.tsx +10 -3
  175. package/node_modules/@flickfyi/core/examples/parrott/timer/timer.tsx +0 -8
  176. package/node_modules/@flickfyi/core/package.json +16 -3
  177. package/node_modules/@flickfyi/core/src/{kowalski/use-configured-feature.ts → hooks/use-configured-feature/index.ts} +1 -1
  178. package/node_modules/@flickfyi/core/src/{kowalski/use-feature-store.ts → hooks/use-feature-store/index.ts} +37 -12
  179. package/node_modules/@flickfyi/core/src/kowalski/README.md +13 -0
  180. package/node_modules/@flickfyi/core/src/kowalski/calculations.ts +36 -8
  181. package/node_modules/@flickfyi/core/src/kowalski/calculator.ts +15 -7
  182. package/node_modules/@flickfyi/core/src/kowalski/catalog.ts +110 -704
  183. package/node_modules/@flickfyi/core/src/kowalski/components/bill-split.tsx +284 -0
  184. package/node_modules/@flickfyi/core/src/kowalski/components/bookmark.tsx +347 -0
  185. package/node_modules/@flickfyi/core/src/kowalski/components/budget.tsx +169 -0
  186. package/node_modules/@flickfyi/core/src/kowalski/components/calculator.tsx +202 -0
  187. package/node_modules/@flickfyi/core/src/kowalski/components/checklist.tsx +119 -0
  188. package/node_modules/@flickfyi/core/src/kowalski/components/color-palette.tsx +302 -0
  189. package/node_modules/@flickfyi/core/src/kowalski/components/contact.tsx +247 -0
  190. package/node_modules/@flickfyi/core/src/kowalski/components/countdown.tsx +173 -0
  191. package/node_modules/@flickfyi/core/src/kowalski/components/event.tsx +260 -0
  192. package/node_modules/@flickfyi/core/src/kowalski/components/expense.tsx +257 -0
  193. package/node_modules/@flickfyi/core/src/kowalski/components/goal.tsx +182 -0
  194. package/node_modules/@flickfyi/core/src/kowalski/components/habit.tsx +272 -0
  195. package/node_modules/@flickfyi/core/src/kowalski/components/ledger.tsx +211 -0
  196. package/node_modules/@flickfyi/core/src/kowalski/components/note.tsx +188 -0
  197. package/node_modules/@flickfyi/core/src/kowalski/components/poll.tsx +242 -0
  198. package/node_modules/@flickfyi/core/src/kowalski/components/random-picker.tsx +308 -0
  199. package/node_modules/@flickfyi/core/src/kowalski/components/rating.tsx +112 -0
  200. package/node_modules/@flickfyi/core/src/kowalski/components/reminder.tsx +298 -0
  201. package/node_modules/@flickfyi/core/src/kowalski/components/rotation.tsx +146 -0
  202. package/node_modules/@flickfyi/core/src/kowalski/components/scoreboard.tsx +139 -0
  203. package/node_modules/@flickfyi/core/src/kowalski/components/shopping-list.tsx +144 -0
  204. package/node_modules/@flickfyi/core/src/kowalski/components/task-board.tsx +146 -0
  205. package/node_modules/@flickfyi/core/src/kowalski/components/time-zone.tsx +240 -0
  206. package/node_modules/@flickfyi/core/src/kowalski/components/timer.tsx +225 -0
  207. package/node_modules/@flickfyi/core/src/kowalski/components/trip.tsx +338 -0
  208. package/node_modules/@flickfyi/core/src/kowalski/components/unit-converter.tsx +285 -0
  209. package/node_modules/@flickfyi/core/src/kowalski/create-schemas.ts +8 -0
  210. package/node_modules/@flickfyi/core/src/kowalski/date-time.tsx +1 -1
  211. package/node_modules/@flickfyi/core/src/kowalski/everyday.tsx +1 -640
  212. package/node_modules/@flickfyi/core/src/kowalski/feature-schema.ts +84 -0
  213. package/node_modules/@flickfyi/core/src/kowalski/format.ts +29 -3
  214. package/node_modules/@flickfyi/core/src/kowalski/fortune.tsx +67 -0
  215. package/node_modules/@flickfyi/core/src/kowalski/frame.tsx +5 -6
  216. package/node_modules/@flickfyi/core/src/kowalski/index.ts +31 -22
  217. package/node_modules/@flickfyi/core/src/kowalski/lesson.tsx +564 -0
  218. package/node_modules/@flickfyi/core/src/kowalski/recipe.tsx +78 -37
  219. package/node_modules/@flickfyi/core/src/kowalski/schemas.ts +67 -0
  220. package/node_modules/@flickfyi/core/src/kowalski/stories/composition.tsx +22 -20
  221. package/node_modules/@flickfyi/core/src/kowalski/time-since.tsx +145 -0
  222. package/node_modules/@flickfyi/core/src/molecules/progress/progress.tsx +4 -1
  223. package/node_modules/@flickfyi/core/src/oklch.ts +1 -6
  224. package/node_modules/@flickfyi/core/src/parrott/theme.ts +3 -1
  225. package/node_modules/@flickfyi/core/src/themes/editorial/editorial.ts +6 -1
  226. package/node_modules/@flickfyi/core/src/themes/palette.ts +5 -5
  227. package/node_modules/@flickfyi/core/src/themes/stories/composition.tsx +11 -9
  228. package/node_modules/@flickfyi/core/src/themes/stories/gallery.tsx +7 -11
  229. package/node_modules/@flickfyi/core/tsgo.bun.lock +7 -7
  230. package/node_modules/@flickfyi/core/tsgo.package.json +1 -1
  231. package/package.json +2 -2
  232. package/node_modules/@flickfyi/core/dist-types/kowalski/clocks.d.ts +0 -4
  233. package/node_modules/@flickfyi/core/dist-types/kowalski/details.d.ts +0 -7
  234. package/node_modules/@flickfyi/core/dist-types/kowalski/productivity.d.ts +0 -5
  235. package/node_modules/@flickfyi/core/dist-types/kowalski/tools.d.ts +0 -7
  236. package/node_modules/@flickfyi/core/dist-types/kowalski/use-feature-store.d.ts +0 -15
  237. package/node_modules/@flickfyi/core/src/kowalski/clocks.tsx +0 -464
  238. package/node_modules/@flickfyi/core/src/kowalski/details.tsx +0 -1387
  239. package/node_modules/@flickfyi/core/src/kowalski/productivity.tsx +0 -575
  240. package/node_modules/@flickfyi/core/src/kowalski/tools.tsx +0 -1213
  241. /package/node_modules/@flickfyi/core/dist-types/{kowalski/use-clock.d.ts → hooks/use-clock/index.d.ts} +0 -0
  242. /package/node_modules/@flickfyi/core/dist-types/{kowalski/use-configured-feature.d.ts → hooks/use-configured-feature/index.d.ts} +0 -0
  243. /package/node_modules/@flickfyi/core/src/{kowalski/use-clock.ts → hooks/use-clock/index.ts} +0 -0
@@ -0,0 +1,564 @@
1
+ import { Flex, Icon, Text } from '@flickfyi/core'
2
+ import { Button as NativeButton } from '@flickfyi/core/atoms'
3
+ import { Lesson as LessonEntity } from '@flickfyi/schemas/lesson'
4
+ import * as stylex from '@stylexjs/stylex'
5
+ import { type ReactNode, useState } from 'react'
6
+ import { z } from 'zod/v4'
7
+
8
+ import { buttonVars } from '~/atoms/button/button.stylex'
9
+ import { useConfiguredFeature } from '~/hooks/use-configured-feature'
10
+ import type { CreateSchemas } from '~/kowalski/create-schemas'
11
+ import { size, space } from '~/themes/scale.stylex'
12
+ import { appearance, button, roles } from '~/themes/shared'
13
+ import { colors, shape } from '~/themes/tokens.stylex'
14
+
15
+ import { entries, featureFields } from './feature-schema'
16
+ import { format } from './format'
17
+ import { FeatureAction, FeatureFrame, FeatureProgress, FeatureWell } from './frame'
18
+
19
+ /**
20
+ * Teaches a lesson as a sequence of single-purpose screens. The overview names the lesson and
21
+ * the path of ideas with Start at the bottom. Learning shows only a progress bar and one thing:
22
+ * an idea to read, or a question about it with its answers as large tiles and the action at the
23
+ * bottom. Missed questions return in a review round with the idea retaught above them. The end
24
+ * screen leads with the score.
25
+ */
26
+ const defaultId = 'KowalskiLesson'
27
+ const defaultScope = 'private'
28
+
29
+ export const LessonData = LessonEntity
30
+ export type LessonData = LessonEntity
31
+
32
+ export const LessonProps = z.strictObject({ data: LessonData, ...featureFields })
33
+ export type LessonProps = z.input<typeof LessonProps> & {
34
+ style?: stylex.StyleXArray<stylex.StyleXStyles>
35
+ }
36
+
37
+ /** `result_<question id>` is "first" when right on the first try, "retry" after a miss, null when unanswered. */
38
+ export const lessonStoreSchema = entries(
39
+ 'result_',
40
+ z.enum(['first', 'retry']).nullable(),
41
+ {
42
+ missed: z.array(z.string()).default([]),
43
+ seen: z.array(z.string()).default([]),
44
+ started: z.boolean().default(false),
45
+ }
46
+ )
47
+
48
+ type Concept = LessonData['teaches'][number]
49
+ type Question = LessonData['hasPart'][number]
50
+ type Result = 'first' | 'retry' | null
51
+ type TileState = 'correct' | 'dimmed' | 'idle' | 'missed' | 'selected' | 'wrong'
52
+
53
+ export const createSchemas: CreateSchemas = ({
54
+ id = defaultId,
55
+ scope = defaultScope,
56
+ } = {}) => ({
57
+ [`stores/${scope}/${id}`]: lessonStoreSchema,
58
+ })
59
+
60
+ export function Lesson({
61
+ data,
62
+ id = defaultId,
63
+ revision = 0,
64
+ scope = defaultScope,
65
+ style,
66
+ }: LessonProps) {
67
+ const store = useConfiguredFeature({
68
+ name: id,
69
+ revision,
70
+ schema: lessonStoreSchema,
71
+ scope,
72
+ seed: lessonStoreSchema.parse({}),
73
+ })
74
+ const [selected, setSelected] = useState<string[]>([])
75
+ const [checked, setChecked] = useState(false)
76
+ const result = (question: Question): Result =>
77
+ store.value[`result_${question.id}`] ?? null
78
+ const seen = new Set(store.value.seen)
79
+ const missed = store.value.missed
80
+ const step = nextStep(data, seen, missed, result)
81
+ const total = data.teaches.length + data.hasPart.length
82
+ const done =
83
+ data.teaches.filter(concept => seen.has(concept.id)).length +
84
+ data.hasPart.filter(question => result(question) !== null).length
85
+ const ready = store.ready && !store.saving
86
+ const frame = (header: ReactNode, children: ReactNode) => (
87
+ <FeatureFrame
88
+ component="lesson"
89
+ header={header}
90
+ notice={store.error}
91
+ onRetry={store.retry}
92
+ style={style}
93
+ title={data.title}
94
+ >
95
+ {children}
96
+ </FeatureFrame>
97
+ )
98
+ const progress = (label: string, accent = true) => (
99
+ <Flex {...stylex.props(styles.bar)}>
100
+ <FeatureProgress component="lesson" value={total > 0 ? done / total : 0} />
101
+ <Text {...stylex.props(roles.caption, accent && appearance.accent)}>{label}</Text>
102
+ </Flex>
103
+ )
104
+
105
+ if (!store.value.started) {
106
+ return frame(
107
+ <Flex {...stylex.props(styles.title)}>
108
+ <Text {...stylex.props(roles.caption, appearance.accent)}>Lesson</Text>
109
+ <Text {...stylex.props(roles.h1)}>{data.title}</Text>
110
+ </Flex>,
111
+ <>
112
+ <Ideas data={data} result={result} seen={seen} />
113
+ <FeatureAction
114
+ component="lesson"
115
+ disabled={!ready || total === 0}
116
+ onClick={() => store.set(() => ({ started: true }))}
117
+ size="lg"
118
+ variant="primary"
119
+ >
120
+ {done > 0 ? 'Continue lesson' : 'Start lesson'}
121
+ </FeatureAction>
122
+ </>
123
+ )
124
+ }
125
+
126
+ if (step.kind === 'done') {
127
+ const first = data.hasPart.filter(question => result(question) === 'first').length
128
+ return frame(
129
+ <Flex {...stylex.props(styles.title)}>
130
+ <Text {...stylex.props(roles.caption, appearance.accent)}>Lesson complete</Text>
131
+ <Text {...stylex.props(roles.h2)}>{data.title}</Text>
132
+ </Flex>,
133
+ <>
134
+ <Flex {...stylex.props(styles.score)}>
135
+ <Text animated="numeric" {...stylex.props(roles.display)}>
136
+ {`${format.formatNumber(first)}/${format.formatNumber(data.hasPart.length)}`}
137
+ </Text>
138
+ <Text {...stylex.props(roles.muted)}>
139
+ {first === data.hasPart.length
140
+ ? 'Every question right on the first try.'
141
+ : 'Right on the first try. Everything else you got on review.'}
142
+ </Text>
143
+ </Flex>
144
+ <FeatureAction
145
+ component="lesson"
146
+ disabled={!ready}
147
+ onClick={() =>
148
+ store.set(() => ({
149
+ ...Object.fromEntries(
150
+ data.hasPart.map(question => [`result_${question.id}`, null])
151
+ ),
152
+ missed: [],
153
+ seen: [],
154
+ started: false,
155
+ }))
156
+ }
157
+ variant="quiet"
158
+ >
159
+ Start over
160
+ </FeatureAction>
161
+ </>
162
+ )
163
+ }
164
+
165
+ if (step.kind === 'teach') {
166
+ const { concept } = step
167
+ return frame(
168
+ progress(
169
+ `Idea ${format.formatNumber(data.teaches.indexOf(concept) + 1)} of ${format.formatNumber(data.teaches.length)}`
170
+ ),
171
+ <>
172
+ <Text {...stylex.props(roles.h1)}>{concept.name}</Text>
173
+ <Text {...stylex.props(roles.lead)}>{concept.description}</Text>
174
+ {concept.examples.length > 0 && (
175
+ <FeatureWell>
176
+ {concept.examples.map(example => (
177
+ <Flex key={example.name} {...stylex.props(styles.pair)}>
178
+ <Text {...stylex.props(roles.h2)}>{example.name}</Text>
179
+ <Text {...stylex.props(roles.muted)}>{example.value}</Text>
180
+ </Flex>
181
+ ))}
182
+ </FeatureWell>
183
+ )}
184
+ <FeatureAction
185
+ component="lesson"
186
+ disabled={!ready}
187
+ onClick={() =>
188
+ store.set(current => ({ seen: [...new Set([...current.seen, concept.id])] }))
189
+ }
190
+ size="lg"
191
+ variant="primary"
192
+ >
193
+ Got it
194
+ </FeatureAction>
195
+ </>
196
+ )
197
+ }
198
+
199
+ const { question, review } = step
200
+ const concept = data.teaches.find(candidate => candidate.id === question.assesses)
201
+ const accepted = question.acceptedAnswer.map(answer => answer.text)
202
+ const flashcard = question.eduQuestionType === 'Flashcard'
203
+ const multiple = question.eduQuestionType === 'Checkbox'
204
+ const correct =
205
+ selected.length === accepted.length && selected.every(text => accepted.includes(text))
206
+ const wrong = question.suggestedAnswer.filter(answer => selected.includes(answer.text))
207
+ const answer = (right: boolean) => {
208
+ setSelected([])
209
+ setChecked(false)
210
+ if (right) {
211
+ store.set(current => ({
212
+ ...Object.fromEntries([
213
+ [
214
+ `result_${question.id}`,
215
+ current.missed.includes(question.id) ? 'retry' : 'first',
216
+ ],
217
+ ]),
218
+ missed: current.missed.filter(id => id !== question.id),
219
+ }))
220
+ } else {
221
+ store.set(current => ({
222
+ missed: [...current.missed.filter(id => id !== question.id), question.id],
223
+ }))
224
+ }
225
+ }
226
+ const tileState = (option: string): TileState => {
227
+ const chosen = selected.includes(option)
228
+ if (!checked) {
229
+ return chosen ? 'selected' : 'idle'
230
+ }
231
+ if (accepted.includes(option)) {
232
+ return chosen ? 'correct' : 'missed'
233
+ }
234
+ return chosen ? 'wrong' : 'dimmed'
235
+ }
236
+ return frame(
237
+ progress(
238
+ review ? `Review · ${concept?.name ?? 'Practice'}` : (concept?.name ?? 'Practice')
239
+ ),
240
+ <>
241
+ {review && concept && (
242
+ <Text {...stylex.props(roles.muted)}>{concept.description}</Text>
243
+ )}
244
+ <Flex {...stylex.props(styles.prompt)}>
245
+ <Text {...stylex.props(roles.h1)}>{question.text}</Text>
246
+ {multiple && !checked && (
247
+ <Text {...stylex.props(roles.muted)}>Choose every answer that applies.</Text>
248
+ )}
249
+ </Flex>
250
+ {flashcard ? (
251
+ checked && (
252
+ <Flex {...stylex.props(appearance.screen, styles.example)}>
253
+ <Text {...stylex.props(roles.caption)}>Answer</Text>
254
+ <Text {...stylex.props(roles.h1)}>
255
+ {format.formatList(accepted, { type: 'conjunction' })}
256
+ </Text>
257
+ {question.acceptedAnswer.map(item => (
258
+ <Text key={item.text} {...stylex.props(roles.text)}>
259
+ {item.answerExplanation}
260
+ </Text>
261
+ ))}
262
+ </Flex>
263
+ )
264
+ ) : (
265
+ <Flex {...stylex.props(styles.tiles)}>
266
+ {choices(question).map(option => (
267
+ <AnswerTile
268
+ key={option}
269
+ disabled={!store.ready}
270
+ label={option}
271
+ multiple={multiple}
272
+ state={tileState(option)}
273
+ onClick={() => {
274
+ if (checked) {
275
+ return
276
+ }
277
+ setSelected(current =>
278
+ multiple
279
+ ? current.includes(option)
280
+ ? current.filter(text => text !== option)
281
+ : [...current, option]
282
+ : [option]
283
+ )
284
+ }}
285
+ />
286
+ ))}
287
+ </Flex>
288
+ )}
289
+ {checked && !flashcard && (
290
+ <FeatureWell style={styles.feedback}>
291
+ <Flex {...stylex.props(styles.verdict)}>
292
+ <Icon
293
+ name={correct ? 'checkmark.circle.fill' : 'xmark.circle.fill'}
294
+ {...stylex.props(correct ? appearance.accent : styles.danger)}
295
+ />
296
+ <Text
297
+ {...stylex.props(roles.h2, correct ? appearance.accent : styles.danger)}
298
+ >
299
+ {correct ? 'Correct' : 'Not quite'}
300
+ </Text>
301
+ </Flex>
302
+ {!correct && (
303
+ <Flex {...stylex.props(styles.pair)}>
304
+ <Text {...stylex.props(roles.caption)}>
305
+ {accepted.length > 1 ? 'Answers' : 'Answer'}
306
+ </Text>
307
+ {accepted.map(text => (
308
+ <Text key={text} {...stylex.props(roles.text, roles.strong)}>
309
+ {text}
310
+ </Text>
311
+ ))}
312
+ </Flex>
313
+ )}
314
+ {!correct &&
315
+ wrong.flatMap(item =>
316
+ item.comment
317
+ ? [
318
+ <Text key={item.text} {...stylex.props(roles.text, roles.strong)}>
319
+ {item.comment}
320
+ </Text>,
321
+ ]
322
+ : []
323
+ )}
324
+ {question.acceptedAnswer.map(item => (
325
+ <Text key={item.text} {...stylex.props(roles.text)}>
326
+ {item.answerExplanation}
327
+ </Text>
328
+ ))}
329
+ </FeatureWell>
330
+ )}
331
+ {flashcard && checked ? (
332
+ <Flex {...stylex.props(styles.grade)}>
333
+ <FeatureAction
334
+ component="lesson"
335
+ disabled={!ready}
336
+ onClick={() => answer(false)}
337
+ size="lg"
338
+ >
339
+ Not yet
340
+ </FeatureAction>
341
+ <FeatureAction
342
+ component="lesson"
343
+ disabled={!ready}
344
+ onClick={() => answer(true)}
345
+ size="lg"
346
+ variant="primary"
347
+ >
348
+ I knew it
349
+ </FeatureAction>
350
+ </Flex>
351
+ ) : (
352
+ <FeatureAction
353
+ component="lesson"
354
+ disabled={!ready || (!flashcard && selected.length === 0)}
355
+ onClick={() => (checked ? answer(correct) : setChecked(true))}
356
+ size="lg"
357
+ variant="primary"
358
+ >
359
+ {checked ? 'Continue' : flashcard ? 'Show answer' : 'Check'}
360
+ </FeatureAction>
361
+ )}
362
+ </>
363
+ )
364
+ }
365
+
366
+ /** One answer as a full-width tile; after checking it shows right, wrong, or missed. */
367
+ function AnswerTile({
368
+ disabled,
369
+ label,
370
+ multiple,
371
+ onClick,
372
+ state,
373
+ }: {
374
+ disabled: boolean
375
+ label: string
376
+ multiple: boolean
377
+ onClick: () => void
378
+ state: TileState
379
+ }) {
380
+ const icon =
381
+ state === 'correct' || state === 'missed'
382
+ ? 'checkmark.circle.fill'
383
+ : state === 'wrong'
384
+ ? 'xmark.circle.fill'
385
+ : multiple
386
+ ? state === 'selected'
387
+ ? 'checkmark.square.fill'
388
+ : 'square'
389
+ : undefined
390
+ return (
391
+ <NativeButton
392
+ accessibilityValue={
393
+ state === 'idle' || state === 'dimmed' ? 'Unselected' : 'Selected'
394
+ }
395
+ disabled={disabled}
396
+ onClick={onClick}
397
+ style={[
398
+ button.secondary,
399
+ styles.tile,
400
+ state === 'selected' && styles.selected,
401
+ state === 'correct' && styles.correct,
402
+ state === 'missed' && styles.missed,
403
+ state === 'wrong' && styles.wrong,
404
+ state === 'dimmed' && styles.dimmed,
405
+ ]}
406
+ >
407
+ <Flex {...stylex.props(styles.tileRow)}>
408
+ <Text {...stylex.props(roles.text, styles.tileLabel)}>{label}</Text>
409
+ {icon && (
410
+ <Icon
411
+ name={icon}
412
+ {...stylex.props(
413
+ state === 'wrong'
414
+ ? styles.danger
415
+ : state === 'idle'
416
+ ? null
417
+ : appearance.accent
418
+ )}
419
+ />
420
+ )}
421
+ </Flex>
422
+ </NativeButton>
423
+ )
424
+ }
425
+
426
+ /** What the lesson teaches, by name only; an idea is ticked once taught and practiced. */
427
+ function Ideas({
428
+ data,
429
+ result,
430
+ seen,
431
+ }: {
432
+ data: LessonData
433
+ result: (question: Question) => Result
434
+ seen: ReadonlySet<string>
435
+ }) {
436
+ return (
437
+ <Flex {...stylex.props(styles.ideas)}>
438
+ <Text {...stylex.props(roles.caption)}>You’ll learn</Text>
439
+ {data.teaches.map(concept => {
440
+ const complete =
441
+ seen.has(concept.id) &&
442
+ data.hasPart.every(
443
+ question => question.assesses !== concept.id || result(question) !== null
444
+ )
445
+ return (
446
+ <Flex key={concept.id} {...stylex.props(styles.idea)}>
447
+ <Icon
448
+ name={complete ? 'checkmark.circle.fill' : 'circle'}
449
+ {...stylex.props(complete ? appearance.accent : appearance.mutedInk)}
450
+ />
451
+ <Text {...stylex.props(roles.text)}>{concept.name}</Text>
452
+ </Flex>
453
+ )
454
+ })}
455
+ </Flex>
456
+ )
457
+ }
458
+
459
+ const styles = /* @__PURE__ */ stylex.create({
460
+ bar: { gap: space[2] },
461
+ // Unchosen answers sit quietly; a chosen answer takes the raised surface.
462
+ correct: {
463
+ [buttonVars.background]: colors.surface,
464
+ [buttonVars.border]: colors.accent,
465
+ },
466
+ danger: { color: colors.danger },
467
+ dimmed: { opacity: 0.45 },
468
+ example: { gap: space[1], padding: space[4] },
469
+ feedback: { alignItems: 'flex-start', gap: space[2] },
470
+ grade: { columnGap: space[2], flexDirection: 'row' },
471
+ idea: { alignItems: 'center', columnGap: space[2], flexDirection: 'row' },
472
+ ideas: { gap: space[2] },
473
+ missed: { [buttonVars.border]: colors.accent, borderStyle: 'dashed' },
474
+ selected: {
475
+ [buttonVars.background]: colors.surface,
476
+ [buttonVars.border]: colors.accent,
477
+ },
478
+ pair: { gap: space[0.5] },
479
+ prompt: { gap: space[2] },
480
+ score: { alignItems: 'flex-start', gap: space[1] },
481
+ tile: {
482
+ // Page sits between the tray and the surface's contrast in both modes, so unchosen answers
483
+ // stand off the tray while the chosen answer's surface stays the most prominent.
484
+ [buttonVars.background]: colors.page,
485
+ [buttonVars.border]: 'transparent',
486
+ alignItems: 'stretch',
487
+ borderRadius: shape.controlRadius,
488
+ minHeight: size[14],
489
+ paddingLeft: space[4],
490
+ paddingRight: space[4],
491
+ width: '100%',
492
+ },
493
+ tileLabel: { flexBasis: '0%', flexGrow: 1, minWidth: 0, textAlign: 'left' },
494
+ tileRow: {
495
+ alignItems: 'center',
496
+ columnGap: space[3],
497
+ flexDirection: 'row',
498
+ width: '100%',
499
+ },
500
+ tiles: { gap: space[2] },
501
+ title: { gap: space[1] },
502
+ verdict: { alignItems: 'center', columnGap: space[2], flexDirection: 'row' },
503
+ wrong: {
504
+ [buttonVars.background]: colors.surface,
505
+ [buttonVars.border]: colors.danger,
506
+ [buttonVars.text]: colors.danger,
507
+ },
508
+ })
509
+
510
+ /** Accepted and suggested answers mixed in an order that is stable for each question. */
511
+ function choices(question: Question) {
512
+ const texts = [
513
+ ...question.acceptedAnswer.map(answer => answer.text),
514
+ ...question.suggestedAnswer.map(answer => answer.text),
515
+ ]
516
+ return [...new Set(texts)].toSorted(
517
+ (a, b) => hash(`${question.id}:${a}`) - hash(`${question.id}:${b}`)
518
+ )
519
+ }
520
+
521
+ function hash(text: string) {
522
+ let value = 5381
523
+ for (const character of text) {
524
+ value = (value * 33 + (character.codePointAt(0) ?? 0)) % 2_147_483_647
525
+ }
526
+ return value
527
+ }
528
+
529
+ /** Concepts in order, each taught before its unanswered questions, then missed questions. */
530
+ function nextStep(
531
+ data: LessonData,
532
+ seen: ReadonlySet<string>,
533
+ missed: readonly string[],
534
+ result: (question: Question) => Result
535
+ ):
536
+ | { concept: Concept; kind: 'teach' }
537
+ | { kind: 'question'; question: Question; review: boolean }
538
+ | { kind: 'done' } {
539
+ const pending = (question: Question) =>
540
+ result(question) === null && !missed.includes(question.id)
541
+ for (const concept of data.teaches) {
542
+ if (!seen.has(concept.id)) {
543
+ return { concept, kind: 'teach' }
544
+ }
545
+ const question = data.hasPart.find(
546
+ candidate => candidate.assesses === concept.id && pending(candidate)
547
+ )
548
+ if (question) {
549
+ return { kind: 'question', question, review: false }
550
+ }
551
+ }
552
+ // Questions whose concept is missing still get practiced, after every taught concept.
553
+ const known = new Set(data.teaches.map(concept => concept.id))
554
+ const orphan = data.hasPart.find(
555
+ question => !known.has(question.assesses) && pending(question)
556
+ )
557
+ if (orphan) {
558
+ return { kind: 'question', question: orphan, review: false }
559
+ }
560
+ const retry = missed.flatMap(id =>
561
+ data.hasPart.filter(question => question.id === id && result(question) === null)
562
+ )[0]
563
+ return retry ? { kind: 'question', question: retry, review: true } : { kind: 'done' }
564
+ }