unmagic-components 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (292) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +98 -1
  3. data/README.md +506 -1
  4. data/app/assets/icons/lucide/LICENSE +43 -0
  5. data/app/assets/icons/lucide/arrow-down.svg +1 -0
  6. data/app/assets/icons/lucide/arrow-left.svg +1 -0
  7. data/app/assets/icons/lucide/arrow-up.svg +1 -0
  8. data/app/assets/icons/lucide/ban.svg +1 -0
  9. data/app/assets/icons/lucide/brain.svg +1 -0
  10. data/app/assets/icons/lucide/check.svg +1 -0
  11. data/app/assets/icons/lucide/chevron-down.svg +1 -0
  12. data/app/assets/icons/lucide/chevron-left.svg +1 -0
  13. data/app/assets/icons/lucide/chevron-right.svg +1 -0
  14. data/app/assets/icons/lucide/circle-check.svg +1 -0
  15. data/app/assets/icons/lucide/circle-dashed.svg +1 -0
  16. data/app/assets/icons/lucide/circle-question-mark.svg +1 -0
  17. data/app/assets/icons/lucide/circle-stop.svg +1 -0
  18. data/app/assets/icons/lucide/circle-x.svg +1 -0
  19. data/app/assets/icons/lucide/clock.svg +1 -0
  20. data/app/assets/icons/lucide/copy.svg +1 -0
  21. data/app/assets/icons/lucide/ellipsis-vertical.svg +1 -0
  22. data/app/assets/icons/lucide/file.svg +1 -0
  23. data/app/assets/icons/lucide/folder.svg +1 -0
  24. data/app/assets/icons/lucide/grip-vertical.svg +1 -0
  25. data/app/assets/icons/lucide/info.svg +1 -0
  26. data/app/assets/icons/lucide/lightbulb.svg +1 -0
  27. data/app/assets/icons/lucide/list-checks.svg +1 -0
  28. data/app/assets/icons/lucide/loader-circle.svg +1 -0
  29. data/app/assets/icons/lucide/messages-square.svg +1 -0
  30. data/app/assets/icons/lucide/paperclip.svg +1 -0
  31. data/app/assets/icons/lucide/pencil.svg +1 -0
  32. data/app/assets/icons/lucide/plus.svg +1 -0
  33. data/app/assets/icons/lucide/rotate-cw.svg +1 -0
  34. data/app/assets/icons/lucide/shield-alert.svg +1 -0
  35. data/app/assets/icons/lucide/timer.svg +1 -0
  36. data/app/assets/icons/lucide/triangle-alert.svg +1 -0
  37. data/app/assets/icons/lucide/wrench.svg +1 -0
  38. data/app/assets/icons/lucide/x.svg +1 -0
  39. data/app/assets/javascripts/unmagic/components/ai_chat.js +142 -0
  40. data/app/assets/javascripts/unmagic/components/autoscroll.js +103 -0
  41. data/app/assets/javascripts/unmagic/components/board.js +65 -0
  42. data/app/assets/javascripts/unmagic/components/dropzone.js +206 -0
  43. data/app/assets/javascripts/unmagic/components/elapsed.js +64 -0
  44. data/app/assets/javascripts/unmagic/components/optimistic.js +71 -0
  45. data/app/assets/javascripts/unmagic/components/placement.js +30 -0
  46. data/app/assets/javascripts/unmagic/components/slash_menu.js +186 -0
  47. data/app/assets/javascripts/unmagic/components/sortable.js +6 -0
  48. data/app/assets/javascripts/unmagic/components/sortable_item.js +122 -0
  49. data/app/assets/javascripts/unmagic/components/sortable_list.js +135 -0
  50. data/app/assets/javascripts/unmagic/components/sortable_param.js +14 -0
  51. data/app/assets/javascripts/unmagic/components/sortable_session.js +502 -0
  52. data/app/assets/javascripts/unmagic/components/streaming_markdown.js +269 -0
  53. data/app/assets/javascripts/unmagic/components/toasts.js +14 -4
  54. data/app/assets/javascripts/unmagic/components/toolbar.js +76 -0
  55. data/app/assets/javascripts/unmagic/components/upsert.js +4 -20
  56. data/app/assets/javascripts/unmagic/components.js +10 -0
  57. data/app/assets/tailwind/unmagic_components/engine.css +1515 -0
  58. data/lib/unmagic/components/action_view_helpers.rb +558 -3
  59. data/lib/unmagic/components/ai_chat/action_bar.rb +70 -0
  60. data/lib/unmagic/components/ai_chat/attachments.rb +119 -0
  61. data/lib/unmagic/components/ai_chat/branch_picker.rb +65 -0
  62. data/lib/unmagic/components/ai_chat/citation.rb +89 -0
  63. data/lib/unmagic/components/ai_chat/composer.rb +160 -0
  64. data/lib/unmagic/components/ai_chat/failure.rb +80 -0
  65. data/lib/unmagic/components/ai_chat/message.rb +142 -0
  66. data/lib/unmagic/components/ai_chat/payload.rb +93 -0
  67. data/lib/unmagic/components/ai_chat/permission.rb +143 -0
  68. data/lib/unmagic/components/ai_chat/plan.rb +91 -0
  69. data/lib/unmagic/components/ai_chat/proposal.rb +102 -0
  70. data/lib/unmagic/components/ai_chat/reasoning.rb +75 -0
  71. data/lib/unmagic/components/ai_chat/request.rb +206 -0
  72. data/lib/unmagic/components/ai_chat/section.rb +53 -0
  73. data/lib/unmagic/components/ai_chat/slash_menu.rb +68 -0
  74. data/lib/unmagic/components/ai_chat/tool_call.rb +181 -0
  75. data/lib/unmagic/components/ai_chat/transcript.rb +68 -0
  76. data/lib/unmagic/components/ai_chat/welcome.rb +73 -0
  77. data/lib/unmagic/components/ai_chat/workspace.rb +74 -0
  78. data/lib/unmagic/components/ai_chat.rb +31 -0
  79. data/lib/unmagic/components/avatar.rb +134 -0
  80. data/lib/unmagic/components/board.rb +182 -0
  81. data/lib/unmagic/components/browser/app/controllers/unmagic/components/browser/application_controller.rb +80 -0
  82. data/lib/unmagic/components/browser/app/controllers/unmagic/components/browser/demos_controller.rb +137 -0
  83. data/lib/unmagic/components/browser/app/controllers/unmagic/components/browser/pages_controller.rb +40 -0
  84. data/lib/unmagic/components/browser/app/helpers/unmagic/components/browser/application_helper.rb +52 -0
  85. data/lib/unmagic/components/browser/app/models/unmagic/components/browser/board_store.rb +88 -0
  86. data/lib/unmagic/components/browser/app/models/unmagic/components/browser/catalog.rb +65 -0
  87. data/lib/unmagic/components/browser/app/models/unmagic/components/browser/pager.rb +11 -0
  88. data/lib/unmagic/components/browser/app/models/unmagic/components/browser/profile.rb +25 -0
  89. data/lib/unmagic/components/browser/app/models/unmagic/components/browser/reply.rb +72 -0
  90. data/lib/unmagic/components/browser/app/models/unmagic/components/browser/thing.rb +31 -0
  91. data/lib/unmagic/components/browser/app/views/layouts/unmagic/components/browser/application.html.erb +65 -0
  92. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/application/_code.html.erb +7 -0
  93. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/application/_crumb.html.erb +7 -0
  94. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/application/_example.html.erb +37 -0
  95. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/application/_nav.html.erb +17 -0
  96. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/demos/edit_profile.html.erb +9 -0
  97. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat/_conversation.html.erb +101 -0
  98. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat/_live.html.erb +53 -0
  99. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat/_thumbnail.html.erb +7 -0
  100. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat/_welcome.html.erb +12 -0
  101. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_action_bar/_always.html.erb +6 -0
  102. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_action_bar/_hover.html.erb +12 -0
  103. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_action_bar/_thumbnail.html.erb +5 -0
  104. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_attachments/_dropzone.html.erb +17 -0
  105. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_attachments/_sent.html.erb +11 -0
  106. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_attachments/_thumbnail.html.erb +6 -0
  107. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_branch_picker/_positions.html.erb +5 -0
  108. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_branch_picker/_thumbnail.html.erb +3 -0
  109. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_citation/_compact.html.erb +10 -0
  110. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_citation/_quote.html.erb +11 -0
  111. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_citation/_thumbnail.html.erb +7 -0
  112. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_composer/_parts.html.erb +14 -0
  113. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_composer/_states.html.erb +8 -0
  114. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_composer/_thumbnail.html.erb +5 -0
  115. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_failure/_full.html.erb +11 -0
  116. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_failure/_plain.html.erb +3 -0
  117. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_failure/_thumbnail.html.erb +5 -0
  118. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_message/_parts.html.erb +23 -0
  119. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_message/_states.html.erb +9 -0
  120. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_message/_thumbnail.html.erb +4 -0
  121. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_message/_turns.html.erb +16 -0
  122. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_payload/_json.html.erb +4 -0
  123. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_payload/_text.html.erb +4 -0
  124. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_payload/_thumbnail.html.erb +3 -0
  125. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_permission/_answered.html.erb +12 -0
  126. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_permission/_thumbnail.html.erb +7 -0
  127. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_permission/_waiting.html.erb +12 -0
  128. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_plan/_empty.html.erb +3 -0
  129. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_plan/_states.html.erb +9 -0
  130. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_plan/_thumbnail.html.erb +7 -0
  131. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_proposal/_decided.html.erb +11 -0
  132. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_proposal/_inline.html.erb +18 -0
  133. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_proposal/_thumbnail.html.erb +6 -0
  134. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_reasoning/_settled.html.erb +6 -0
  135. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_reasoning/_thinking.html.erb +5 -0
  136. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_reasoning/_thumbnail.html.erb +3 -0
  137. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_request/_answered.html.erb +10 -0
  138. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_request/_choices.html.erb +13 -0
  139. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_request/_form.html.erb +9 -0
  140. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_request/_thumbnail.html.erb +8 -0
  141. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_slash_menu/_composer.html.erb +15 -0
  142. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_slash_menu/_thumbnail.html.erb +9 -0
  143. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_tool_call/_made.html.erb +12 -0
  144. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_tool_call/_run.html.erb +25 -0
  145. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_tool_call/_states.html.erb +22 -0
  146. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_tool_call/_thumbnail.html.erb +7 -0
  147. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_welcome/_suggestions.html.erb +11 -0
  148. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_welcome/_thumbnail.html.erb +6 -0
  149. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_workspace/_files.html.erb +12 -0
  150. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_workspace/_thumbnail.html.erb +7 -0
  151. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/avatar/_group.html.erb +5 -0
  152. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/avatar/_sizes.html.erb +8 -0
  153. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/avatar/_thumbnail.html.erb +5 -0
  154. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/avatar/_tints.html.erb +7 -0
  155. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/badge/_in_context.html.erb +14 -0
  156. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/badge/_thumbnail.html.erb +5 -0
  157. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/badge/_tones.html.erb +8 -0
  158. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/board/_handles.html.erb +13 -0
  159. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/board/_live.html.erb +34 -0
  160. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/board/_thumbnail.html.erb +10 -0
  161. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/button/_sizes.html.erb +12 -0
  162. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/button/_thumbnail.html.erb +4 -0
  163. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/button/_variants.html.erb +6 -0
  164. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/callout/_neutral.html.erb +3 -0
  165. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/callout/_thumbnail.html.erb +5 -0
  166. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/callout/_tones.html.erb +14 -0
  167. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/card/_actions_and_footer.html.erb +7 -0
  168. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/card/_flush_table.html.erb +6 -0
  169. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/card/_link.html.erb +6 -0
  170. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/card/_thumbnail.html.erb +6 -0
  171. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/confirm/_danger.html.erb +3 -0
  172. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/confirm/_plain.html.erb +2 -0
  173. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/confirm/_thumbnail.html.erb +7 -0
  174. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/copy_button/_from_element.html.erb +4 -0
  175. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/copy_button/_thumbnail.html.erb +4 -0
  176. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/copy_button/_with_label.html.erb +3 -0
  177. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/detail_list/_inline.html.erb +5 -0
  178. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/detail_list/_stacked.html.erb +7 -0
  179. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/detail_list/_thumbnail.html.erb +5 -0
  180. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/dialog/_dialog_tag.html.erb +8 -0
  181. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/dialog/_modal.html.erb +10 -0
  182. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/dialog/_thumbnail.html.erb +4 -0
  183. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/elapsed/_countdown.html.erb +3 -0
  184. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/elapsed/_running.html.erb +8 -0
  185. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/elapsed/_thumbnail.html.erb +4 -0
  186. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/empty_state/_default.html.erb +1 -0
  187. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/empty_state/_thumbnail.html.erb +3 -0
  188. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/forms/_autogrow.html.erb +4 -0
  189. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/forms/_control_classes.html.erb +54 -0
  190. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/forms/_fields.html.erb +28 -0
  191. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/forms/_states.html.erb +11 -0
  192. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/forms/_thumbnail.html.erb +8 -0
  193. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/forms/_uuid_field.html.erb +9 -0
  194. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/local_time/_absolute.html.erb +5 -0
  195. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/local_time/_relative.html.erb +14 -0
  196. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/local_time/_thumbnail.html.erb +14 -0
  197. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/menu/_actions.html.erb +10 -0
  198. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/menu/_labelled.html.erb +6 -0
  199. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/menu/_thumbnail.html.erb +14 -0
  200. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/page_header/_default.html.erb +7 -0
  201. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/page_header/_thumbnail.html.erb +5 -0
  202. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/page_header/_with_leading.html.erb +14 -0
  203. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/skeleton/_blocking_out.html.erb +25 -0
  204. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/skeleton/_card.html.erb +7 -0
  205. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/skeleton/_detail_list.html.erb +15 -0
  206. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/skeleton/_page_header.html.erb +7 -0
  207. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/skeleton/_text_and_button.html.erb +17 -0
  208. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/skeleton/_thumbnail.html.erb +10 -0
  209. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/sortable_list/_connected.html.erb +17 -0
  210. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/sortable_list/_grid.html.erb +20 -0
  211. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/sortable_list/_handles.html.erb +22 -0
  212. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/sortable_list/_thumbnail.html.erb +7 -0
  213. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/streaming_markdown/_reveal.html.erb +19 -0
  214. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/streaming_markdown/_stopped.html.erb +31 -0
  215. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/streaming_markdown/_thumbnail.html.erb +5 -0
  216. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/table/_deferred.html.erb +6 -0
  217. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/table/_empty.html.erb +5 -0
  218. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/table/_sortable.html.erb +8 -0
  219. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/table/_table_tag.html.erb +4 -0
  220. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/table/_thumbnail.html.erb +4 -0
  221. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/tabs/_links.html.erb +5 -0
  222. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/tabs/_panels.html.erb +18 -0
  223. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/tabs/_thumbnail.html.erb +5 -0
  224. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/toast/_above_dialog.html.erb +10 -0
  225. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/toast/_flash.html.erb +13 -0
  226. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/toast/_stream.html.erb +6 -0
  227. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/toast/_thumbnail.html.erb +10 -0
  228. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/tooltip/_placement.html.erb +8 -0
  229. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/tooltip/_term.html.erb +5 -0
  230. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/tooltip/_thumbnail.html.erb +5 -0
  231. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/pages/component.html.erb +58 -0
  232. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/pages/installation.html.erb +47 -0
  233. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/pages/overview.html.erb +49 -0
  234. data/lib/unmagic/components/browser/app/views/unmagic/components/browser/pages/theming.html.erb +52 -0
  235. data/lib/unmagic/components/browser/assets/browser.css +2 -0
  236. data/lib/unmagic/components/browser/catalog/ai_chat.rb +21 -0
  237. data/lib/unmagic/components/browser/catalog/ai_chat_action_bar.rb +15 -0
  238. data/lib/unmagic/components/browser/catalog/ai_chat_attachments.rb +15 -0
  239. data/lib/unmagic/components/browser/catalog/ai_chat_branch_picker.rb +12 -0
  240. data/lib/unmagic/components/browser/catalog/ai_chat_citation.rb +15 -0
  241. data/lib/unmagic/components/browser/catalog/ai_chat_composer.rb +16 -0
  242. data/lib/unmagic/components/browser/catalog/ai_chat_failure.rb +15 -0
  243. data/lib/unmagic/components/browser/catalog/ai_chat_message.rb +17 -0
  244. data/lib/unmagic/components/browser/catalog/ai_chat_payload.rb +16 -0
  245. data/lib/unmagic/components/browser/catalog/ai_chat_permission.rb +15 -0
  246. data/lib/unmagic/components/browser/catalog/ai_chat_plan.rb +14 -0
  247. data/lib/unmagic/components/browser/catalog/ai_chat_proposal.rb +15 -0
  248. data/lib/unmagic/components/browser/catalog/ai_chat_reasoning.rb +14 -0
  249. data/lib/unmagic/components/browser/catalog/ai_chat_request.rb +17 -0
  250. data/lib/unmagic/components/browser/catalog/ai_chat_slash_menu.rb +14 -0
  251. data/lib/unmagic/components/browser/catalog/ai_chat_tool_call.rb +17 -0
  252. data/lib/unmagic/components/browser/catalog/ai_chat_welcome.rb +13 -0
  253. data/lib/unmagic/components/browser/catalog/ai_chat_workspace.rb +12 -0
  254. data/lib/unmagic/components/browser/catalog/avatar.rb +15 -0
  255. data/lib/unmagic/components/browser/catalog/badge.rb +11 -0
  256. data/lib/unmagic/components/browser/catalog/board.rb +18 -0
  257. data/lib/unmagic/components/browser/catalog/button.rb +14 -0
  258. data/lib/unmagic/components/browser/catalog/callout.rb +13 -0
  259. data/lib/unmagic/components/browser/catalog/card.rb +16 -0
  260. data/lib/unmagic/components/browser/catalog/confirm.rb +14 -0
  261. data/lib/unmagic/components/browser/catalog/copy_button.rb +15 -0
  262. data/lib/unmagic/components/browser/catalog/detail_list.rb +14 -0
  263. data/lib/unmagic/components/browser/catalog/dialog.rb +15 -0
  264. data/lib/unmagic/components/browser/catalog/elapsed.rb +14 -0
  265. data/lib/unmagic/components/browser/catalog/empty_state.rb +11 -0
  266. data/lib/unmagic/components/browser/catalog/forms.rb +21 -0
  267. data/lib/unmagic/components/browser/catalog/local_time.rb +13 -0
  268. data/lib/unmagic/components/browser/catalog/menu.rb +15 -0
  269. data/lib/unmagic/components/browser/catalog/page_header.rb +13 -0
  270. data/lib/unmagic/components/browser/catalog/skeleton.rb +18 -0
  271. data/lib/unmagic/components/browser/catalog/sortable_list.rb +17 -0
  272. data/lib/unmagic/components/browser/catalog/streaming_markdown.rb +17 -0
  273. data/lib/unmagic/components/browser/catalog/table.rb +17 -0
  274. data/lib/unmagic/components/browser/catalog/tabs.rb +15 -0
  275. data/lib/unmagic/components/browser/catalog/toast.rb +17 -0
  276. data/lib/unmagic/components/browser/catalog/tooltip.rb +14 -0
  277. data/lib/unmagic/components/browser/config/routes.rb +41 -0
  278. data/lib/unmagic/components/browser/tailwind/browser.css +33 -0
  279. data/lib/unmagic/components/browser.rb +39 -0
  280. data/lib/unmagic/components/configuration.rb +31 -1
  281. data/lib/unmagic/components/duration.rb +35 -0
  282. data/lib/unmagic/components/elapsed.rb +46 -0
  283. data/lib/unmagic/components/engine.rb +9 -0
  284. data/lib/unmagic/components/icons.rb +34 -26
  285. data/lib/unmagic/components/modal.rb +1 -1
  286. data/lib/unmagic/components/page_header.rb +8 -0
  287. data/lib/unmagic/components/sortable.rb +104 -0
  288. data/lib/unmagic/components/streaming_markdown.rb +34 -0
  289. data/lib/unmagic/components/turbo_stream_actions.rb +16 -0
  290. data/lib/unmagic/components/version.rb +1 -1
  291. data/lib/unmagic/components.rb +30 -1
  292. metadata +308 -4
@@ -0,0 +1,11 @@
1
+ <div class="mx-auto max-w-2xl">
2
+ <%= ai_chat_welcome heading: "What can I help with?", composer: "welcome_page_composer" do |welcome| %>
3
+ <% welcome.body "It can search your jobs and candidates, draft messages, and keep track of who has replied." %>
4
+ <% welcome.suggestion "Who hasn't replied yet?", icon: :messages_square %>
5
+ <% welcome.suggestion "Draft a follow-up for Ana", icon: :pencil %>
6
+ <% welcome.suggestion "Invite someone to…", icon: :lightbulb, fill: true, value: "Invite someone to " %>
7
+ <% end %>
8
+ <%= form_with url: "#", id: "welcome_page_composer", data: { turbo: false } do |form| %>
9
+ <%= ai_chat_composer form: form, field: :content, placeholder: "Message the assistant…", rows: 1 %>
10
+ <% end %>
11
+ </div>
@@ -0,0 +1,6 @@
1
+ <div class="w-64 origin-center scale-90 text-xs">
2
+ <%= ai_chat_welcome heading: "What can I help with?", class: "py-0" do |welcome| %>
3
+ <% welcome.suggestion "Who hasn't replied?" %>
4
+ <% welcome.suggestion "Draft a nudge" %>
5
+ <% end %>
6
+ </div>
@@ -0,0 +1,12 @@
1
+ <div class="w-80 rounded-xl border border-neutral-200 bg-white dark:border-neutral-800 dark:bg-neutral-900">
2
+ <%= ai_chat_plan id: "workspace_plan", open: false do |plan| %>
3
+ <% plan.step "Gather references", state: :completed %>
4
+ <% plan.step "Draft the scene", state: :in_progress %>
5
+ <% end %>
6
+ <%= ai_chat_workspace id: "workspace_files" do |workspace| %>
7
+ <% workspace.file "references/lantern-red.png", size: 1_480_000, url: "#" %>
8
+ <% workspace.file "references/market-night.jpg", size: 2_210_000, url: "#" %>
9
+ <% workspace.file "scene.json", size: 6_120 %>
10
+ <% workspace.file "notes.md", size: 812, icon: :pencil %>
11
+ <% end %>
12
+ </div>
@@ -0,0 +1,7 @@
1
+ <div class="w-56 rounded-xl border border-neutral-200 bg-white text-xs dark:border-neutral-800 dark:bg-neutral-900">
2
+ <%= ai_chat_workspace class: "p-3" do |workspace| %>
3
+ <% workspace.file "lantern.png", size: 1_480_000 %>
4
+ <% workspace.file "scene.json", size: 6_120 %>
5
+ <% workspace.file "notes.md", size: 812, icon: :pencil %>
6
+ <% end %>
7
+ </div>
@@ -0,0 +1,5 @@
1
+ <%= avatar_group max: 3 do |group| %>
2
+ <% [ "Ada Lovelace", "Grace Hopper", "Katherine Johnson", "Alan Turing", "Margaret Hamilton", "Barbara Liskov" ].each do |name| %>
3
+ <% group.avatar name %>
4
+ <% end %>
5
+ <% end %>
@@ -0,0 +1,8 @@
1
+ <div class="flex items-center gap-4">
2
+ <%= avatar "Ada Lovelace", size: :small %>
3
+ <%= avatar "Ada Lovelace" %>
4
+ <%= avatar "Ada Lovelace", size: :large %>
5
+ <%= avatar "Grace Hopper", size: :large, src: "/missing-picture.png" %>
6
+ <%= avatar "Acme Ltd", size: :large, shape: :square %>
7
+ <%= avatar "Ada Lovelace", size: :large, skeleton: true %>
8
+ </div>
@@ -0,0 +1,5 @@
1
+ <%= avatar_group max: 4, size: :large do |group| %>
2
+ <% [ "Ada Lovelace", "Grace Hopper", "Katherine Johnson", "Alan Turing", "Margaret Hamilton" ].each do |name| %>
3
+ <% group.avatar name %>
4
+ <% end %>
5
+ <% end %>
@@ -0,0 +1,7 @@
1
+ <div class="flex flex-wrap items-center gap-3">
2
+ <% [ "Ada Lovelace", "Grace Hopper", "Katherine Johnson", "Alan Turing", "Margaret Hamilton",
3
+ "Dennis Ritchie", "Barbara Liskov", "Ken Thompson" ].each do |name| %>
4
+ <%= avatar name %>
5
+ <% end %>
6
+ <%= avatar "Neutral Person", tint: false %>
7
+ </div>
@@ -0,0 +1,14 @@
1
+ <ul class="grid w-80 gap-3">
2
+ <li class="flex items-center justify-between gap-3">
3
+ <span>Webhook to Slack</span>
4
+ <%= badge "Failing", tone: :bad %>
5
+ </li>
6
+ <li class="flex items-center justify-between gap-3">
7
+ <span>Nightly export</span>
8
+ <%= badge "Healthy", tone: :good %>
9
+ </li>
10
+ <li class="flex items-center justify-between gap-3">
11
+ <span>Pull requests</span>
12
+ <%= badge "12" %>
13
+ </li>
14
+ </ul>
@@ -0,0 +1,5 @@
1
+ <div class="flex flex-wrap items-center justify-center gap-2">
2
+ <%= badge "Active", tone: :good %>
3
+ <%= badge "Pending", tone: :warn %>
4
+ <%= badge "Archived" %>
5
+ </div>
@@ -0,0 +1,8 @@
1
+ <div class="flex flex-wrap items-center gap-2">
2
+ <%= badge "Neutral" %>
3
+ <%= badge "Good", tone: :good %>
4
+ <%= badge "Warn", tone: :warn %>
5
+ <%= badge "Bad", tone: :bad %>
6
+ <%= badge "Info", tone: :info %>
7
+ <%= badge "Accent", tone: :accent %>
8
+ </div>
@@ -0,0 +1,13 @@
1
+ <%= board id: "handles", sortable_columns: false do |b| %>
2
+ <% [ [ "Inbox", [ "Reply to Ana about Friday", "Send Grace the brief" ] ],
3
+ [ "Waiting", [ "Tom's corrected address" ] ] ].each do |title, cards| %>
4
+ <% b.column title: title, params: { column: title.downcase } do |col| %>
5
+ <% cards.each_with_index do |text, index| %>
6
+ <% col.card key: "#{title}-#{index}", class: "flex cursor-default items-center gap-2 p-2" do %>
7
+ <%= sortable_handle label: "Move #{text}" %>
8
+ <%= link_to text, "#", class: "min-w-0 flex-1 truncate font-medium text-neutral-900 underline-offset-2 hover:underline dark:text-neutral-100" %>
9
+ <% end %>
10
+ <% end %>
11
+ <% end %>
12
+ <% end %>
13
+ <% end %>
@@ -0,0 +1,34 @@
1
+ <div class="flex flex-col gap-4">
2
+ <div class="flex items-center justify-between gap-3">
3
+ <p class="text-sm text-neutral-500">Roadmap · <%= pluralize(@board.columns.sum { |c| @board.cards_in(c).size }, "card") %></p>
4
+ <%= button_to "Reset the board", board_path, method: :delete, class: button_classes(size: :small),
5
+ form: { data: { turbo_confirm: "Put every card back where it started?" } } %>
6
+ </div>
7
+
8
+ <%= board id: "roadmap", url: board_order_path, column_height: "32rem" do |b| %>
9
+ <% @board.columns.each do |column| %>
10
+ <% cards = @board.cards_in(column) %>
11
+ <% b.column key: column["key"], rank: column["rank"], title: column["title"], params: { column: column["key"] } do |col| %>
12
+ <% col.actions do %>
13
+ <%= menu do |m| %>
14
+ <% m.link "Rename", "#" %>
15
+ <% m.link "Archive", "#", tone: :danger %>
16
+ <% end %>
17
+ <% end %>
18
+ <% cards.each do |card| %>
19
+ <% col.card key: card["key"], rank: card["rank"], label: card["title"], id: "board_#{card["key"]}" do %>
20
+ <p class="m-0 leading-snug"><%= card["title"] %></p>
21
+ <% if card["tag"] || card["owner"] %>
22
+ <div class="mt-2.5 flex items-center justify-between gap-2">
23
+ <%= badge card["tag"] if card["tag"] %>
24
+ <%= avatar card["owner"], size: :small, "aria-hidden": true if card["owner"] %>
25
+ </div>
26
+ <% end %>
27
+ <% end %>
28
+ <% end %>
29
+ <% col.add url: board_cards_path, field: "card[title]", params: { "card[column]" => column["key"] } %>
30
+ <% end %>
31
+ <% end %>
32
+ <% b.add_column url: board_columns_path, field: "column[title]" %>
33
+ <% end %>
34
+ </div>
@@ -0,0 +1,10 @@
1
+ <div class="flex w-72 gap-2 text-xs">
2
+ <% [ [ "To do", [ "Write the ad", "Book the room" ] ], [ "Doing", [ "Shortlist" ] ] ].each do |title, cards| %>
3
+ <div class="UnmagicBoard__panel w-1/2 shrink-0">
4
+ <div class="UnmagicBoard__head"><h3 class="UnmagicBoard__title text-xs"><%= title %></h3><span class="UnmagicBoard__count"><%= cards.size %></span></div>
5
+ <div class="UnmagicBoard__cards pb-2">
6
+ <% cards.each do |card| %><div class="UnmagicBoard__card p-2 text-xs"><%= card %></div><% end %>
7
+ </div>
8
+ </div>
9
+ <% end %>
10
+ </div>
@@ -0,0 +1,12 @@
1
+ <div class="flex flex-col items-center gap-3">
2
+ <div class="flex flex-wrap items-center gap-2">
3
+ <%= tag.button "Small", type: "button", class: button_classes(:default, size: :small) %>
4
+ <%= tag.button "Default", type: "button", class: button_classes %>
5
+ <%= tag.button "Large", type: "button", class: button_classes(:default, size: :large) %>
6
+ </div>
7
+ <div class="flex flex-wrap items-center gap-2">
8
+ <%= tag.button "Small", type: "button", class: button_classes(:primary, size: :small) %>
9
+ <%= tag.button "Default", type: "button", class: button_classes(:primary) %>
10
+ <%= tag.button "Large", type: "button", class: button_classes(:primary, size: :large) %>
11
+ </div>
12
+ </div>
@@ -0,0 +1,4 @@
1
+ <div class="flex items-center gap-2">
2
+ <%= tag.span "Stage", class: button_classes(:default, size: :small) %>
3
+ <%= tag.span "Publish", class: button_classes(:primary, size: :small) %>
4
+ </div>
@@ -0,0 +1,6 @@
1
+ <div class="flex flex-wrap items-center gap-2">
2
+ <%= tag.button "Default", type: "button", class: button_classes %>
3
+ <%= tag.button "Primary", type: "button", class: button_classes(:primary) %>
4
+ <%= tag.button "Ghost", type: "button", class: button_classes(:ghost) %>
5
+ <%= tag.button "Danger", type: "button", class: button_classes(:danger) %>
6
+ </div>
@@ -0,0 +1,3 @@
1
+ <%= callout "Heads up" do %>
2
+ Exports run overnight, so today's changes show up tomorrow.
3
+ <% end %>
@@ -0,0 +1,5 @@
1
+ <div class="w-60 text-xs">
2
+ <%= callout "DNS isn't verified", tone: :warn, badge: "Pending" do %>
3
+ Add the TXT record, then check again.
4
+ <% end %>
5
+ </div>
@@ -0,0 +1,14 @@
1
+ <div class="grid gap-3">
2
+ <%= callout "DNS verified", tone: :good do %>
3
+ Mail from this domain is signed and will land in inboxes.
4
+ <% end %>
5
+ <%= callout "DNS isn't verified", tone: :warn, badge: "Pending" do %>
6
+ Add the TXT record below, then check again. Changes can take an hour to spread.
7
+ <% end %>
8
+ <%= callout "Webhook failing", tone: :bad, badge: "12 errors" do %>
9
+ The last delivery returned 500. We'll retry for another 24 hours.
10
+ <% end %>
11
+ <%= callout "How scopes work", tone: :info do %>
12
+ A scope limits what a token can do.
13
+ <% end %>
14
+ </div>
@@ -0,0 +1,7 @@
1
+ <div class="w-80">
2
+ <%= card title: "Members" do |card| %>
3
+ <% card.actions { tag.button "Invite", type: "button", class: button_classes(:default, size: :small) } %>
4
+ <% card.footer { "3 of 5 seats used" } %>
5
+ Ada, Grace and Katherine can see everything in this workspace.
6
+ <% end %>
7
+ </div>
@@ -0,0 +1,6 @@
1
+ <%= card title: "Usage this month", flush: true do %>
2
+ <%= table_tag [ "Metric", "Value" ],
3
+ [ [ "Requests", { content: "1,204", class: "is-right is-numeric" } ],
4
+ [ "Errors", { content: "3", class: "is-right is-numeric" } ] ],
5
+ aligns: [ nil, :right ] %>
6
+ <% end %>
@@ -0,0 +1,6 @@
1
+ <div class="w-80">
2
+ <%= card href: request.path do %>
3
+ <strong>Quarterly review</strong><br>
4
+ Due in 3 days · 4 of 6 sections complete
5
+ <% end %>
6
+ </div>
@@ -0,0 +1,6 @@
1
+ <div class="w-56 text-xs">
2
+ <%= card title: "Members" do |card| %>
3
+ <% card.footer { "3 of 5 seats used" } %>
4
+ Ada, Grace and Katherine.
5
+ <% end %>
6
+ </div>
@@ -0,0 +1,3 @@
1
+ <%= button_to "Reset profile", profile_dialog_path, method: :delete, class: button_classes(:danger),
2
+ form: { data: { turbo_confirm: "Reset the profile to its defaults?",
3
+ turbo_confirm_accept: "Reset", turbo_confirm_variant: "danger" } } %>
@@ -0,0 +1,2 @@
1
+ <%= button_to "Reset profile", profile_dialog_path, method: :delete, class: button_classes,
2
+ form: { data: { turbo_confirm: "Reset the profile?" } } %>
@@ -0,0 +1,7 @@
1
+ <%= dialog title: "Reset the profile?", close: false, class: "w-60 origin-center scale-[0.8] text-xs shadow-md" do |dialog| %>
2
+ This can't be undone.
3
+ <% dialog.footer do %>
4
+ <%= tag.span "Cancel", class: button_classes(:default, size: :small) %>
5
+ <%= tag.span "Reset", class: button_classes(:danger, size: :small) %>
6
+ <% end %>
7
+ <% end %>
@@ -0,0 +1,4 @@
1
+ <div class="flex flex-wrap items-center gap-3">
2
+ <code id="install_command" class="rounded-md bg-neutral-100 px-2 py-1 text-sm dark:bg-neutral-800">bundle add unmagic-components</code>
3
+ <%= copy_button from: "install_command" %>
4
+ </div>
@@ -0,0 +1,4 @@
1
+ <div class="flex items-center gap-2">
2
+ <code class="rounded-md bg-neutral-100 px-2 py-1 text-xs dark:bg-neutral-800">bundle add unmagic-components</code>
3
+ <%= copy_button "bundle add unmagic-components" %>
4
+ </div>
@@ -0,0 +1,3 @@
1
+ <%= copy_button "deploy-key-preview-0000", class: button_classes(:default, size: :small) do %>
2
+ Copy key
3
+ <% end %>
@@ -0,0 +1,5 @@
1
+ <%= detail_list do |list| %>
2
+ <% list.item "Name", "Ada Lovelace" %>
3
+ <% list.item "Role", "Engineer" %>
4
+ <% list.item "Notes", nil %>
5
+ <% end %>
@@ -0,0 +1,7 @@
1
+ <%= detail_list variant: :stacked do |list| %>
2
+ <% list.item "Client ID", "abc123", class: "font-mono" %>
3
+ <% list.item "Scopes", "read write" %>
4
+ <% list.item "Redirect URIs", span: :full do %>
5
+ https://example.com/callback
6
+ <% end %>
7
+ <% end %>
@@ -0,0 +1,5 @@
1
+ <%= detail_list class: "w-56 text-xs" do |list| %>
2
+ <% list.item "Name", "Ada Lovelace" %>
3
+ <% list.item "Role", "Engineer" %>
4
+ <% list.item "Notes", nil %>
5
+ <% end %>
@@ -0,0 +1,8 @@
1
+ <%= dialog_button "What's a scope?", dialog: "scopes_help", class: button_classes %>
2
+
3
+ <%= dialog_tag "scopes_help", title: "Scopes" do |dialog| %>
4
+ <p>A scope limits what a token can do. Grant the fewest that get the job done.</p>
5
+ <% dialog.footer do %>
6
+ <%= tag.button "Got it", type: "button", class: button_classes(:primary), data: { unmagic_dialog_close: "" } %>
7
+ <% end %>
8
+ <% end %>
@@ -0,0 +1,10 @@
1
+ <%= detail_list do |list| %>
2
+ <% list.item "Name", @profile.name %>
3
+ <% list.item "Role", @profile.role %>
4
+ <% end %>
5
+
6
+ <div class="mt-4 flex flex-wrap gap-2">
7
+ <%= modal_link_to "Edit profile", profile_dialog_path, class: button_classes(:primary) %>
8
+ <%= modal_link_to "Slow (skeleton)", slow_dialog_path, class: button_classes %>
9
+ <%= modal_link_to "Forbidden (error)", forbidden_dialog_path, class: button_classes %>
10
+ </div>
@@ -0,0 +1,4 @@
1
+ <%= dialog title: "Edit profile", class: "w-60 origin-center scale-[0.8] text-xs shadow-md" do |dialog| %>
2
+ Ada Lovelace · Engineer
3
+ <% dialog.footer { tag.span "Save", class: button_classes(:primary, size: :small) } %>
4
+ <% end %>
@@ -0,0 +1,3 @@
1
+ <p class="text-sm text-neutral-600 dark:text-neutral-400">
2
+ This link expires in <%= elapsed_tag @now + 45, direction: :down, class: "font-semibold text-neutral-900 dark:text-neutral-100" %>.
3
+ </p>
@@ -0,0 +1,8 @@
1
+ <dl class="grid grid-cols-[auto_auto] gap-x-6 gap-y-2 text-sm">
2
+ <dt class="text-neutral-500">Started just now</dt>
3
+ <dd><%= elapsed_tag @now - 4 %></dd>
4
+ <dt class="text-neutral-500">A slow tool</dt>
5
+ <dd><%= elapsed_tag @now - 185 %></dd>
6
+ <dt class="text-neutral-500">Probably hung</dt>
7
+ <dd><%= elapsed_tag @now - 3842 %></dd>
8
+ </dl>
@@ -0,0 +1,4 @@
1
+ <span class="inline-flex items-center gap-2 rounded-full border border-neutral-200 bg-white px-3 py-1.5 text-neutral-600 dark:border-neutral-800 dark:bg-neutral-900 dark:text-neutral-300">
2
+ <%= Unmagic::Components::Icons.svg(self, :timer) %>
3
+ <%= elapsed_tag @now - 185, class: "text-sm" %>
4
+ </span>
@@ -0,0 +1,3 @@
1
+ <div class="w-56 text-xs">
2
+ <%= empty_state "No invitations yet." %>
3
+ </div>
@@ -0,0 +1,4 @@
1
+ <%= form_with url: request.path, method: :get, scope: :note, builder: Unmagic::Components::FormBuilder do |form| %>
2
+ <%= form.field :body, "Message", as: :autogrow_text_area, rows: 2, class: "max-h-40",
3
+ placeholder: "Type a few lines, then keep going past ten rems…" %>
4
+ <% end %>
@@ -0,0 +1,54 @@
1
+ <div class="grid gap-4">
2
+ <div class="flex flex-wrap items-center gap-2">
3
+ <%= search_field_tag "people[q]", nil, placeholder: "Search people", "aria-label": "Search people",
4
+ class: control_classes(:input, size: :small) %>
5
+ <%= select_tag "people[status]", options_for_select(%w[Active Invited Removed]), "aria-label": "Status",
6
+ class: control_classes(:select, size: :small) %>
7
+ <%= tag.button "Filter", type: "button", class: button_classes(:default, size: :small) %>
8
+ </div>
9
+
10
+ <div class="flex flex-wrap items-center gap-2">
11
+ <%= text_field_tag "key[slug]", "deploy-key", "aria-label": "Slug", class: control_classes(:input) %>
12
+ <%= select_tag "key[scope]", options_for_select([ "Read", "Read and write" ]), "aria-label": "Scope",
13
+ class: control_classes(:select) %>
14
+ <%= tag.button "Save", type: "button", class: button_classes(:primary) %>
15
+ </div>
16
+
17
+ <div class="flex flex-wrap items-center gap-2">
18
+ <%= text_field_tag "report[title]", "Quarterly review", "aria-label": "Title",
19
+ class: control_classes(:input, size: :large) %>
20
+ <%= tag.button "Publish", type: "button", class: button_classes(:primary, size: :large) %>
21
+ </div>
22
+
23
+ <fieldset class="m-0 border-0 p-0">
24
+ <legend class="UnmagicLabel mb-2">Notify me</legend>
25
+ <div class="UnmagicCheckList--inline">
26
+ <% [ "Immediately", "Daily", "Never" ].each_with_index do |choice, index| %>
27
+ <label class="UnmagicCheckField">
28
+ <%= radio_button_tag "notify", choice.downcase, index.zero?, class: control_classes(:radio) %>
29
+ <span class="UnmagicCheckField__label"><%= choice %></span>
30
+ </label>
31
+ <% end %>
32
+ <label class="UnmagicCheckField">
33
+ <%= radio_button_tag "notify", "paused", false, disabled: true, class: control_classes(:radio) %>
34
+ <span class="UnmagicCheckField__label">Paused (disabled)</span>
35
+ </label>
36
+ </div>
37
+ </fieldset>
38
+
39
+ <div class="UnmagicCheckList--inline">
40
+ <label class="UnmagicCheckField">
41
+ <%= check_box_tag "all_teams", "1", false, id: "control_classes_all_teams", class: control_classes(:check) %>
42
+ <span class="UnmagicCheckField__label">All teams (indeterminate)</span>
43
+ </label>
44
+ <label class="UnmagicCheckField">
45
+ <%= check_box_tag "archived", "1", true, id: "control_classes_archived", class: control_classes(:check),
46
+ "aria-invalid": "true" %>
47
+ <span class="UnmagicCheckField__label">Invalid</span>
48
+ </label>
49
+ </div>
50
+
51
+ <%= javascript_tag nonce: true do %>
52
+ document.getElementById("control_classes_all_teams").indeterminate = true
53
+ <% end %>
54
+ </div>
@@ -0,0 +1,28 @@
1
+ <%= form_with url: request.path, method: :get, scope: :person, builder: Unmagic::Components::FormBuilder do |form| %>
2
+ <%= form.group do %>
3
+ <%= form.field :first_name, "First name", required: true, placeholder: "Ada" %>
4
+ <%= form.field :last_name, "Last name", placeholder: "Lovelace" %>
5
+ <% end %>
6
+
7
+ <%= form.field :email, "Email", as: :email_field, placeholder: "ada@example.com",
8
+ hint: "Only used to sign you in." %>
9
+
10
+ <%= form.group do %>
11
+ <%= form.field :password, "Password", as: :password_field %>
12
+ <%= form.field :starts_on, "Starts on", as: :date_field %>
13
+ <% end %>
14
+
15
+ <%= form.field :role, "Role" do %>
16
+ <%= form.select :role, [ "Engineer", "Designer", "Manager" ], include_blank: "Choose a role" %>
17
+ <% end %>
18
+
19
+ <%= form.field :bio, "Bio", as: :text_area, rows: 3, placeholder: "A line or two about yourself." %>
20
+
21
+ <div class="mb-4 grid gap-3">
22
+ <%= form.check_box_field :releases, "Email me about new releases", hint: "Roughly once a month." %>
23
+ <%= form.check_box_collection :teams, [ [ 1, "Platform" ], [ 2, "Billing" ], [ 3, "Growth" ] ],
24
+ :first, :last, inline: true %>
25
+ </div>
26
+
27
+ <%= form.submit "Save" %>
28
+ <% end %>
@@ -0,0 +1,11 @@
1
+ <%= form_with model: @invalid_profile, url: request.path, method: :get,
2
+ builder: Unmagic::Components::FormBuilder do |form| %>
3
+ <%= form.field :name, "Name", required: true %>
4
+ <%= form.field :role, "Role", disabled: true, hint: "Set by your administrator." %>
5
+ <% end %>
6
+
7
+ <%= form_with model: @profile, url: request.path, method: :get, namespace: :locked,
8
+ builder: Unmagic::Components::FormBuilder do |form| %>
9
+ <%= form.field :role, "Role (read-only)", readonly: true %>
10
+ <%= form.check_box_field :name, "Locked option", disabled: true, checked: true %>
11
+ <% end %>
@@ -0,0 +1,8 @@
1
+ <div class="grid w-56 gap-2">
2
+ <span class="UnmagicLabel text-xs">Email</span>
3
+ <input type="email" value="ada@example.com" aria-label="Email" class="<%= control_classes(:input, size: :small) %>">
4
+ <label class="UnmagicCheckField items-center">
5
+ <input type="checkbox" checked aria-label="Email me about releases" class="<%= control_classes(:check) %>">
6
+ <span class="UnmagicCheckField__label text-xs">Email me about releases</span>
7
+ </label>
8
+ </div>
@@ -0,0 +1,9 @@
1
+ <%= form_with url: request.path, method: :get, scope: :message, builder: Unmagic::Components::FormBuilder do |form| %>
2
+ <%= form.uuid_field :id %>
3
+ <%= form.field :body, "Message", hint: "Submits with a client-minted id; resetting the form mints another." %>
4
+
5
+ <div class="flex gap-2">
6
+ <%= form.submit "Send" %>
7
+ <button type="reset" class="<%= button_classes %>">Reset the form</button>
8
+ </div>
9
+ <% end %>
@@ -0,0 +1,5 @@
1
+ <%= table_tag [ "format:", "Rendered" ],
2
+ %i[short medium long full date time].map { |format|
3
+ [ tag.code(":#{format}"), local_time_tag(@now, format: format) ]
4
+ },
5
+ widths: [ "30%", "70%" ] %>
@@ -0,0 +1,14 @@
1
+ <%= table_tag [ "Rendered for", "format: :relative", "compact: true" ],
2
+ [
3
+ [ "10 seconds ago", @now - 10.seconds ],
4
+ [ "5 minutes ago", @now - 5.minutes ],
5
+ [ "3 hours ago", @now - 3.hours ],
6
+ [ "1 day ago", @now - 1.day ],
7
+ [ "3 days ago", @now - 3.days ],
8
+ [ "40 days ago", @now - 40.days ],
9
+ [ "In 2 hours", @now + 2.hours ],
10
+ [ "In 400 days", @now + 400.days ]
11
+ ].map { |label, at|
12
+ [ label, local_time_tag(at, format: :relative), local_time_tag(at, format: :relative, compact: true) ]
13
+ },
14
+ widths: [ "30%", "40%", "30%" ] %>
@@ -0,0 +1,14 @@
1
+ <div class="grid w-56 gap-1.5 text-xs">
2
+ <div class="flex justify-between gap-4">
3
+ <span class="text-neutral-500">Commented</span>
4
+ <%= local_time_tag @now - 5.minutes, format: :relative %>
5
+ </div>
6
+ <div class="flex justify-between gap-4">
7
+ <span class="text-neutral-500">Deployed</span>
8
+ <%= local_time_tag @now - 3.hours, format: :relative, compact: true %>
9
+ </div>
10
+ <div class="flex justify-between gap-4">
11
+ <span class="text-neutral-500">Due</span>
12
+ <%= local_time_tag @now + 2.days, format: :date %>
13
+ </div>
14
+ </div>
@@ -0,0 +1,10 @@
1
+ <div class="pb-28">
2
+ <%= menu do |menu| %>
3
+ <% menu.button "Send a toast", stream_toast_path, params: { tone: "good", message: "Sent from a menu." } %>
4
+ <% menu.divider %>
5
+ <% menu.button "Delete", stream_toast_path, tone: :danger,
6
+ params: { tone: "bad", message: "Deleted (not really)." },
7
+ form: { data: { turbo_confirm: "Delete this thing?", turbo_confirm_accept: "Delete",
8
+ turbo_confirm_variant: "danger" } } %>
9
+ <% end %>
10
+ </div>
@@ -0,0 +1,6 @@
1
+ <div class="pb-24">
2
+ <%= menu "Options", align: :start do |menu| %>
3
+ <% menu.link "Go to toasts", component_path("toast") %>
4
+ <% menu.link "Go to dialogs", component_path("dialog") %>
5
+ <% end %>
6
+ </div>
@@ -0,0 +1,14 @@
1
+ <%# A static open menu: the real one starts closed, and its element closes it on connect.
2
+ The trigger and panel share a positioned wrapper, so the panel hangs off the trigger. %>
3
+ <div class="h-28 w-44 text-xs">
4
+ <div class="relative inline-block">
5
+ <%= tag.span class: button_classes(:default, size: :small) do %>
6
+ Options <%= Unmagic::Components::Icons.svg(self, :chevron_down, class: "size-3") %>
7
+ <% end %>
8
+ <div class="UnmagicMenu__panel UnmagicMenu__panel--start min-w-36">
9
+ <span class="UnmagicMenu__item text-xs">Edit</span>
10
+ <hr class="UnmagicMenu__divider">
11
+ <span class="UnmagicMenu__item UnmagicMenu__item--danger text-xs">Delete</span>
12
+ </div>
13
+ </div>
14
+ </div>
@@ -0,0 +1,7 @@
1
+ <%= page_header title: "Deploy keys", back: { text: "Settings", path: request.path },
2
+ description: "Keys that can pull from this repository. Anyone holding one can read every branch, " \
3
+ "so revoke a key as soon as the machine using it is retired." do |header| %>
4
+ <% header.badge "3 active", tone: :good %>
5
+ <% header.badge "Beta", tone: :info %>
6
+ <%= tag.button "Add key", type: "button", class: button_classes(:primary) %>
7
+ <% end %>
@@ -0,0 +1,5 @@
1
+ <div class="w-60">
2
+ <%= page_header title: "Deploy keys", description: "Keys that can pull from this repository." do |header| %>
3
+ <% header.badge "3 active", tone: :good %>
4
+ <% end %>
5
+ </div>
@@ -0,0 +1,14 @@
1
+ <%= page_header back: { text: "People", path: request.path } do |header| %>
2
+ <% header.leading do %>
3
+ <span class="flex size-12 shrink-0 items-center justify-center rounded-full bg-indigo-100 text-sm font-semibold text-indigo-700">AL</span>
4
+ <% end %>
5
+ <% header.title { "Ada Lovelace" } %>
6
+ <% header.description do %>
7
+ Engineer · London · joined <strong>10 December 1815</strong>
8
+ <% end %>
9
+ <% header.badge "Admin", tone: :accent %>
10
+ <%= menu do |menu| %>
11
+ <% menu.link "Edit", request.path %>
12
+ <% menu.link "Send a message", request.path %>
13
+ <% end %>
14
+ <% end %>