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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +98 -1
- data/README.md +506 -1
- data/app/assets/icons/lucide/LICENSE +43 -0
- data/app/assets/icons/lucide/arrow-down.svg +1 -0
- data/app/assets/icons/lucide/arrow-left.svg +1 -0
- data/app/assets/icons/lucide/arrow-up.svg +1 -0
- data/app/assets/icons/lucide/ban.svg +1 -0
- data/app/assets/icons/lucide/brain.svg +1 -0
- data/app/assets/icons/lucide/check.svg +1 -0
- data/app/assets/icons/lucide/chevron-down.svg +1 -0
- data/app/assets/icons/lucide/chevron-left.svg +1 -0
- data/app/assets/icons/lucide/chevron-right.svg +1 -0
- data/app/assets/icons/lucide/circle-check.svg +1 -0
- data/app/assets/icons/lucide/circle-dashed.svg +1 -0
- data/app/assets/icons/lucide/circle-question-mark.svg +1 -0
- data/app/assets/icons/lucide/circle-stop.svg +1 -0
- data/app/assets/icons/lucide/circle-x.svg +1 -0
- data/app/assets/icons/lucide/clock.svg +1 -0
- data/app/assets/icons/lucide/copy.svg +1 -0
- data/app/assets/icons/lucide/ellipsis-vertical.svg +1 -0
- data/app/assets/icons/lucide/file.svg +1 -0
- data/app/assets/icons/lucide/folder.svg +1 -0
- data/app/assets/icons/lucide/grip-vertical.svg +1 -0
- data/app/assets/icons/lucide/info.svg +1 -0
- data/app/assets/icons/lucide/lightbulb.svg +1 -0
- data/app/assets/icons/lucide/list-checks.svg +1 -0
- data/app/assets/icons/lucide/loader-circle.svg +1 -0
- data/app/assets/icons/lucide/messages-square.svg +1 -0
- data/app/assets/icons/lucide/paperclip.svg +1 -0
- data/app/assets/icons/lucide/pencil.svg +1 -0
- data/app/assets/icons/lucide/plus.svg +1 -0
- data/app/assets/icons/lucide/rotate-cw.svg +1 -0
- data/app/assets/icons/lucide/shield-alert.svg +1 -0
- data/app/assets/icons/lucide/timer.svg +1 -0
- data/app/assets/icons/lucide/triangle-alert.svg +1 -0
- data/app/assets/icons/lucide/wrench.svg +1 -0
- data/app/assets/icons/lucide/x.svg +1 -0
- data/app/assets/javascripts/unmagic/components/ai_chat.js +142 -0
- data/app/assets/javascripts/unmagic/components/autoscroll.js +103 -0
- data/app/assets/javascripts/unmagic/components/board.js +65 -0
- data/app/assets/javascripts/unmagic/components/dropzone.js +206 -0
- data/app/assets/javascripts/unmagic/components/elapsed.js +64 -0
- data/app/assets/javascripts/unmagic/components/optimistic.js +71 -0
- data/app/assets/javascripts/unmagic/components/placement.js +30 -0
- data/app/assets/javascripts/unmagic/components/slash_menu.js +186 -0
- data/app/assets/javascripts/unmagic/components/sortable.js +6 -0
- data/app/assets/javascripts/unmagic/components/sortable_item.js +122 -0
- data/app/assets/javascripts/unmagic/components/sortable_list.js +135 -0
- data/app/assets/javascripts/unmagic/components/sortable_param.js +14 -0
- data/app/assets/javascripts/unmagic/components/sortable_session.js +502 -0
- data/app/assets/javascripts/unmagic/components/streaming_markdown.js +269 -0
- data/app/assets/javascripts/unmagic/components/toasts.js +14 -4
- data/app/assets/javascripts/unmagic/components/toolbar.js +76 -0
- data/app/assets/javascripts/unmagic/components/upsert.js +4 -20
- data/app/assets/javascripts/unmagic/components.js +10 -0
- data/app/assets/tailwind/unmagic_components/engine.css +1515 -0
- data/lib/unmagic/components/action_view_helpers.rb +558 -3
- data/lib/unmagic/components/ai_chat/action_bar.rb +70 -0
- data/lib/unmagic/components/ai_chat/attachments.rb +119 -0
- data/lib/unmagic/components/ai_chat/branch_picker.rb +65 -0
- data/lib/unmagic/components/ai_chat/citation.rb +89 -0
- data/lib/unmagic/components/ai_chat/composer.rb +160 -0
- data/lib/unmagic/components/ai_chat/failure.rb +80 -0
- data/lib/unmagic/components/ai_chat/message.rb +142 -0
- data/lib/unmagic/components/ai_chat/payload.rb +93 -0
- data/lib/unmagic/components/ai_chat/permission.rb +143 -0
- data/lib/unmagic/components/ai_chat/plan.rb +91 -0
- data/lib/unmagic/components/ai_chat/proposal.rb +102 -0
- data/lib/unmagic/components/ai_chat/reasoning.rb +75 -0
- data/lib/unmagic/components/ai_chat/request.rb +206 -0
- data/lib/unmagic/components/ai_chat/section.rb +53 -0
- data/lib/unmagic/components/ai_chat/slash_menu.rb +68 -0
- data/lib/unmagic/components/ai_chat/tool_call.rb +181 -0
- data/lib/unmagic/components/ai_chat/transcript.rb +68 -0
- data/lib/unmagic/components/ai_chat/welcome.rb +73 -0
- data/lib/unmagic/components/ai_chat/workspace.rb +74 -0
- data/lib/unmagic/components/ai_chat.rb +31 -0
- data/lib/unmagic/components/avatar.rb +134 -0
- data/lib/unmagic/components/board.rb +182 -0
- data/lib/unmagic/components/browser/app/controllers/unmagic/components/browser/application_controller.rb +80 -0
- data/lib/unmagic/components/browser/app/controllers/unmagic/components/browser/demos_controller.rb +137 -0
- data/lib/unmagic/components/browser/app/controllers/unmagic/components/browser/pages_controller.rb +40 -0
- data/lib/unmagic/components/browser/app/helpers/unmagic/components/browser/application_helper.rb +52 -0
- data/lib/unmagic/components/browser/app/models/unmagic/components/browser/board_store.rb +88 -0
- data/lib/unmagic/components/browser/app/models/unmagic/components/browser/catalog.rb +65 -0
- data/lib/unmagic/components/browser/app/models/unmagic/components/browser/pager.rb +11 -0
- data/lib/unmagic/components/browser/app/models/unmagic/components/browser/profile.rb +25 -0
- data/lib/unmagic/components/browser/app/models/unmagic/components/browser/reply.rb +72 -0
- data/lib/unmagic/components/browser/app/models/unmagic/components/browser/thing.rb +31 -0
- data/lib/unmagic/components/browser/app/views/layouts/unmagic/components/browser/application.html.erb +65 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/application/_code.html.erb +7 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/application/_crumb.html.erb +7 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/application/_example.html.erb +37 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/application/_nav.html.erb +17 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/demos/edit_profile.html.erb +9 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat/_conversation.html.erb +101 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat/_live.html.erb +53 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat/_thumbnail.html.erb +7 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat/_welcome.html.erb +12 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_action_bar/_always.html.erb +6 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_action_bar/_hover.html.erb +12 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_action_bar/_thumbnail.html.erb +5 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_attachments/_dropzone.html.erb +17 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_attachments/_sent.html.erb +11 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_attachments/_thumbnail.html.erb +6 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_branch_picker/_positions.html.erb +5 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_branch_picker/_thumbnail.html.erb +3 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_citation/_compact.html.erb +10 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_citation/_quote.html.erb +11 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_citation/_thumbnail.html.erb +7 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_composer/_parts.html.erb +14 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_composer/_states.html.erb +8 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_composer/_thumbnail.html.erb +5 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_failure/_full.html.erb +11 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_failure/_plain.html.erb +3 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_failure/_thumbnail.html.erb +5 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_message/_parts.html.erb +23 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_message/_states.html.erb +9 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_message/_thumbnail.html.erb +4 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_message/_turns.html.erb +16 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_payload/_json.html.erb +4 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_payload/_text.html.erb +4 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_payload/_thumbnail.html.erb +3 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_permission/_answered.html.erb +12 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_permission/_thumbnail.html.erb +7 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_permission/_waiting.html.erb +12 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_plan/_empty.html.erb +3 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_plan/_states.html.erb +9 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_plan/_thumbnail.html.erb +7 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_proposal/_decided.html.erb +11 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_proposal/_inline.html.erb +18 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_proposal/_thumbnail.html.erb +6 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_reasoning/_settled.html.erb +6 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_reasoning/_thinking.html.erb +5 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_reasoning/_thumbnail.html.erb +3 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_request/_answered.html.erb +10 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_request/_choices.html.erb +13 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_request/_form.html.erb +9 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_request/_thumbnail.html.erb +8 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_slash_menu/_composer.html.erb +15 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_slash_menu/_thumbnail.html.erb +9 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_tool_call/_made.html.erb +12 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_tool_call/_run.html.erb +25 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_tool_call/_states.html.erb +22 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_tool_call/_thumbnail.html.erb +7 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_welcome/_suggestions.html.erb +11 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_welcome/_thumbnail.html.erb +6 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_workspace/_files.html.erb +12 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/ai_chat_workspace/_thumbnail.html.erb +7 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/avatar/_group.html.erb +5 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/avatar/_sizes.html.erb +8 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/avatar/_thumbnail.html.erb +5 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/avatar/_tints.html.erb +7 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/badge/_in_context.html.erb +14 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/badge/_thumbnail.html.erb +5 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/badge/_tones.html.erb +8 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/board/_handles.html.erb +13 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/board/_live.html.erb +34 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/board/_thumbnail.html.erb +10 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/button/_sizes.html.erb +12 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/button/_thumbnail.html.erb +4 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/button/_variants.html.erb +6 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/callout/_neutral.html.erb +3 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/callout/_thumbnail.html.erb +5 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/callout/_tones.html.erb +14 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/card/_actions_and_footer.html.erb +7 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/card/_flush_table.html.erb +6 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/card/_link.html.erb +6 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/card/_thumbnail.html.erb +6 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/confirm/_danger.html.erb +3 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/confirm/_plain.html.erb +2 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/confirm/_thumbnail.html.erb +7 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/copy_button/_from_element.html.erb +4 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/copy_button/_thumbnail.html.erb +4 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/copy_button/_with_label.html.erb +3 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/detail_list/_inline.html.erb +5 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/detail_list/_stacked.html.erb +7 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/detail_list/_thumbnail.html.erb +5 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/dialog/_dialog_tag.html.erb +8 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/dialog/_modal.html.erb +10 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/dialog/_thumbnail.html.erb +4 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/elapsed/_countdown.html.erb +3 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/elapsed/_running.html.erb +8 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/elapsed/_thumbnail.html.erb +4 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/empty_state/_default.html.erb +1 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/empty_state/_thumbnail.html.erb +3 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/forms/_autogrow.html.erb +4 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/forms/_control_classes.html.erb +54 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/forms/_fields.html.erb +28 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/forms/_states.html.erb +11 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/forms/_thumbnail.html.erb +8 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/forms/_uuid_field.html.erb +9 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/local_time/_absolute.html.erb +5 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/local_time/_relative.html.erb +14 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/local_time/_thumbnail.html.erb +14 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/menu/_actions.html.erb +10 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/menu/_labelled.html.erb +6 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/menu/_thumbnail.html.erb +14 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/page_header/_default.html.erb +7 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/page_header/_thumbnail.html.erb +5 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/page_header/_with_leading.html.erb +14 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/skeleton/_blocking_out.html.erb +25 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/skeleton/_card.html.erb +7 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/skeleton/_detail_list.html.erb +15 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/skeleton/_page_header.html.erb +7 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/skeleton/_text_and_button.html.erb +17 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/skeleton/_thumbnail.html.erb +10 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/sortable_list/_connected.html.erb +17 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/sortable_list/_grid.html.erb +20 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/sortable_list/_handles.html.erb +22 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/sortable_list/_thumbnail.html.erb +7 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/streaming_markdown/_reveal.html.erb +19 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/streaming_markdown/_stopped.html.erb +31 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/streaming_markdown/_thumbnail.html.erb +5 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/table/_deferred.html.erb +6 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/table/_empty.html.erb +5 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/table/_sortable.html.erb +8 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/table/_table_tag.html.erb +4 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/table/_thumbnail.html.erb +4 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/tabs/_links.html.erb +5 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/tabs/_panels.html.erb +18 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/tabs/_thumbnail.html.erb +5 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/toast/_above_dialog.html.erb +10 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/toast/_flash.html.erb +13 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/toast/_stream.html.erb +6 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/toast/_thumbnail.html.erb +10 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/tooltip/_placement.html.erb +8 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/tooltip/_term.html.erb +5 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/examples/tooltip/_thumbnail.html.erb +5 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/pages/component.html.erb +58 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/pages/installation.html.erb +47 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/pages/overview.html.erb +49 -0
- data/lib/unmagic/components/browser/app/views/unmagic/components/browser/pages/theming.html.erb +52 -0
- data/lib/unmagic/components/browser/assets/browser.css +2 -0
- data/lib/unmagic/components/browser/catalog/ai_chat.rb +21 -0
- data/lib/unmagic/components/browser/catalog/ai_chat_action_bar.rb +15 -0
- data/lib/unmagic/components/browser/catalog/ai_chat_attachments.rb +15 -0
- data/lib/unmagic/components/browser/catalog/ai_chat_branch_picker.rb +12 -0
- data/lib/unmagic/components/browser/catalog/ai_chat_citation.rb +15 -0
- data/lib/unmagic/components/browser/catalog/ai_chat_composer.rb +16 -0
- data/lib/unmagic/components/browser/catalog/ai_chat_failure.rb +15 -0
- data/lib/unmagic/components/browser/catalog/ai_chat_message.rb +17 -0
- data/lib/unmagic/components/browser/catalog/ai_chat_payload.rb +16 -0
- data/lib/unmagic/components/browser/catalog/ai_chat_permission.rb +15 -0
- data/lib/unmagic/components/browser/catalog/ai_chat_plan.rb +14 -0
- data/lib/unmagic/components/browser/catalog/ai_chat_proposal.rb +15 -0
- data/lib/unmagic/components/browser/catalog/ai_chat_reasoning.rb +14 -0
- data/lib/unmagic/components/browser/catalog/ai_chat_request.rb +17 -0
- data/lib/unmagic/components/browser/catalog/ai_chat_slash_menu.rb +14 -0
- data/lib/unmagic/components/browser/catalog/ai_chat_tool_call.rb +17 -0
- data/lib/unmagic/components/browser/catalog/ai_chat_welcome.rb +13 -0
- data/lib/unmagic/components/browser/catalog/ai_chat_workspace.rb +12 -0
- data/lib/unmagic/components/browser/catalog/avatar.rb +15 -0
- data/lib/unmagic/components/browser/catalog/badge.rb +11 -0
- data/lib/unmagic/components/browser/catalog/board.rb +18 -0
- data/lib/unmagic/components/browser/catalog/button.rb +14 -0
- data/lib/unmagic/components/browser/catalog/callout.rb +13 -0
- data/lib/unmagic/components/browser/catalog/card.rb +16 -0
- data/lib/unmagic/components/browser/catalog/confirm.rb +14 -0
- data/lib/unmagic/components/browser/catalog/copy_button.rb +15 -0
- data/lib/unmagic/components/browser/catalog/detail_list.rb +14 -0
- data/lib/unmagic/components/browser/catalog/dialog.rb +15 -0
- data/lib/unmagic/components/browser/catalog/elapsed.rb +14 -0
- data/lib/unmagic/components/browser/catalog/empty_state.rb +11 -0
- data/lib/unmagic/components/browser/catalog/forms.rb +21 -0
- data/lib/unmagic/components/browser/catalog/local_time.rb +13 -0
- data/lib/unmagic/components/browser/catalog/menu.rb +15 -0
- data/lib/unmagic/components/browser/catalog/page_header.rb +13 -0
- data/lib/unmagic/components/browser/catalog/skeleton.rb +18 -0
- data/lib/unmagic/components/browser/catalog/sortable_list.rb +17 -0
- data/lib/unmagic/components/browser/catalog/streaming_markdown.rb +17 -0
- data/lib/unmagic/components/browser/catalog/table.rb +17 -0
- data/lib/unmagic/components/browser/catalog/tabs.rb +15 -0
- data/lib/unmagic/components/browser/catalog/toast.rb +17 -0
- data/lib/unmagic/components/browser/catalog/tooltip.rb +14 -0
- data/lib/unmagic/components/browser/config/routes.rb +41 -0
- data/lib/unmagic/components/browser/tailwind/browser.css +33 -0
- data/lib/unmagic/components/browser.rb +39 -0
- data/lib/unmagic/components/configuration.rb +31 -1
- data/lib/unmagic/components/duration.rb +35 -0
- data/lib/unmagic/components/elapsed.rb +46 -0
- data/lib/unmagic/components/engine.rb +9 -0
- data/lib/unmagic/components/icons.rb +34 -26
- data/lib/unmagic/components/modal.rb +1 -1
- data/lib/unmagic/components/page_header.rb +8 -0
- data/lib/unmagic/components/sortable.rb +104 -0
- data/lib/unmagic/components/streaming_markdown.rb +34 -0
- data/lib/unmagic/components/turbo_stream_actions.rb +16 -0
- data/lib/unmagic/components/version.rb +1 -1
- data/lib/unmagic/components.rb +30 -1
- metadata +308 -4
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<div class="grid items-start gap-6 sm:grid-cols-2">
|
|
2
|
+
<%= card do %>
|
|
3
|
+
<div class="flex items-center gap-3">
|
|
4
|
+
<div class="size-12 shrink-0 rounded-full bg-indigo-200"></div>
|
|
5
|
+
<div class="min-w-0 flex-1">
|
|
6
|
+
<div class="font-semibold">Ada Lovelace</div>
|
|
7
|
+
<div>Engineer · London</div>
|
|
8
|
+
</div>
|
|
9
|
+
</div>
|
|
10
|
+
<p class="mt-3 mb-0">Wrote the first published algorithm for a machine, and saw it could reach beyond arithmetic.</p>
|
|
11
|
+
<% end %>
|
|
12
|
+
|
|
13
|
+
<%= card do %>
|
|
14
|
+
<%= skeleton label: "Loading candidate" do |s| %>
|
|
15
|
+
<div class="flex items-center gap-3">
|
|
16
|
+
<%= s.circle size: "3rem" %>
|
|
17
|
+
<div class="min-w-0 flex-1">
|
|
18
|
+
<div class="font-semibold"><%= s.text width: "60%" %></div>
|
|
19
|
+
<%= s.text width: "40%" %>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
<p class="mt-3 mb-0"><%= s.text lines: 2 %></p>
|
|
23
|
+
<% end %>
|
|
24
|
+
<% end %>
|
|
25
|
+
</div>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<div class="grid items-start gap-6 sm:grid-cols-2">
|
|
2
|
+
<%= detail_list do |list|
|
|
3
|
+
list.item "Name", "Ada Lovelace"
|
|
4
|
+
list.item "Role", "Engineer"
|
|
5
|
+
list.item "Joined", "10 December 1815"
|
|
6
|
+
list.item "Location", "London"
|
|
7
|
+
end %>
|
|
8
|
+
|
|
9
|
+
<%= detail_list skeleton: true do |list|
|
|
10
|
+
list.item "Name"
|
|
11
|
+
list.item "Role"
|
|
12
|
+
list.item "Joined"
|
|
13
|
+
list.item "Location"
|
|
14
|
+
end %>
|
|
15
|
+
</div>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<div class="grid items-start gap-6 sm:grid-cols-2">
|
|
2
|
+
<%= page_header title: "Deploy keys", description: "Keys that can pull from this repository." do %>
|
|
3
|
+
<%= tag.button "Add key", type: "button", class: button_classes(:primary) %>
|
|
4
|
+
<% end %>
|
|
5
|
+
|
|
6
|
+
<%= page_header skeleton: true %>
|
|
7
|
+
</div>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<div class="grid items-start gap-6 sm:grid-cols-2">
|
|
2
|
+
<div>
|
|
3
|
+
<p class="m-0">One line of body text.</p>
|
|
4
|
+
<p class="m-0"><%= skeleton_text width: "45%" %></p>
|
|
5
|
+
<p class="mt-4 mb-0 text-2xl font-semibold">A larger heading</p>
|
|
6
|
+
<p class="m-0 text-2xl"><%= skeleton_text width: "60%" %></p>
|
|
7
|
+
</div>
|
|
8
|
+
|
|
9
|
+
<div class="flex flex-wrap items-center gap-2">
|
|
10
|
+
<%= tag.button "Small", type: "button", class: button_classes(:default, size: :small) %>
|
|
11
|
+
<%= skeleton_button size: :small %>
|
|
12
|
+
<%= tag.button "Default", type: "button", class: button_classes %>
|
|
13
|
+
<%= skeleton_button %>
|
|
14
|
+
<%= tag.button "Large", type: "button", class: button_classes(:default, size: :large) %>
|
|
15
|
+
<%= skeleton_button size: :large %>
|
|
16
|
+
</div>
|
|
17
|
+
</div>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<%= skeleton label: "Loading", class: "grid w-56 gap-3" do |s| %>
|
|
2
|
+
<div class="flex items-center gap-3">
|
|
3
|
+
<%= s.circle size: "2rem" %>
|
|
4
|
+
<div class="min-w-0 flex-1">
|
|
5
|
+
<%= s.text width: "60%" %>
|
|
6
|
+
<%= s.text width: "40%" %>
|
|
7
|
+
</div>
|
|
8
|
+
</div>
|
|
9
|
+
<%= s.text lines: 2 %>
|
|
10
|
+
<% end %>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<div class="grid gap-6 sm:grid-cols-2">
|
|
2
|
+
<% { "Shortlist" => [ "Ada Lovelace", "Grace Hopper", "Alan Turing" ], "Maybe later" => [ "Katherine Johnson" ] }.each do |title, people| %>
|
|
3
|
+
<div class="rounded-xl border border-neutral-200 p-3 dark:border-neutral-800">
|
|
4
|
+
<h3 class="mb-2 text-sm font-semibold"><%= title %></h3>
|
|
5
|
+
<%= sortable_list namespace: "people", label: title, params: { list: title },
|
|
6
|
+
class: "flex min-h-24 flex-col gap-1.5 rounded-lg p-1 sortable-active:bg-neutral-50 sortable-over:bg-neutral-100 dark:sortable-active:bg-neutral-900 dark:sortable-over:bg-neutral-800" do |list| %>
|
|
7
|
+
<% people.each do |person| %>
|
|
8
|
+
<%= list.item(key: person.parameterize, label: person) do %>
|
|
9
|
+
<div class="flex cursor-grab items-center gap-2 rounded-md bg-white p-2 text-sm shadow-xs dark:bg-neutral-900">
|
|
10
|
+
<%= avatar person, size: :small, "aria-hidden": true %> <%= person %>
|
|
11
|
+
</div>
|
|
12
|
+
<% end %>
|
|
13
|
+
<% end %>
|
|
14
|
+
<% end %>
|
|
15
|
+
</div>
|
|
16
|
+
<% end %>
|
|
17
|
+
</div>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<%# Written out in full, since Tailwind can only generate classes it can read. %>
|
|
2
|
+
<% tiles = [
|
|
3
|
+
"bg-sky-100 text-sky-800 dark:bg-sky-400/15 dark:text-sky-300",
|
|
4
|
+
"bg-emerald-100 text-emerald-800 dark:bg-emerald-400/15 dark:text-emerald-300",
|
|
5
|
+
"bg-amber-100 text-amber-800 dark:bg-amber-400/15 dark:text-amber-300",
|
|
6
|
+
"bg-rose-100 text-rose-800 dark:bg-rose-400/15 dark:text-rose-300",
|
|
7
|
+
"bg-violet-100 text-violet-800 dark:bg-violet-400/15 dark:text-violet-300",
|
|
8
|
+
"bg-teal-100 text-teal-800 dark:bg-teal-400/15 dark:text-teal-300",
|
|
9
|
+
"bg-orange-100 text-orange-800 dark:bg-orange-400/15 dark:text-orange-300",
|
|
10
|
+
"bg-lime-100 text-lime-800 dark:bg-lime-400/15 dark:text-lime-300"
|
|
11
|
+
] %>
|
|
12
|
+
<%= sortable_list orientation: :grid, label: "Screenshots", class: "grid grid-cols-2 gap-3 sm:grid-cols-4" do |list| %>
|
|
13
|
+
<% tiles.each_with_index do |colours, index| %>
|
|
14
|
+
<%= list.item(key: "shot-#{index}", rank: index + 1, label: "Screenshot #{index + 1}") do %>
|
|
15
|
+
<div class="grid aspect-video cursor-grab place-items-center rounded-lg text-sm font-medium <%= colours %>">
|
|
16
|
+
Screenshot <%= index + 1 %>
|
|
17
|
+
</div>
|
|
18
|
+
<% end %>
|
|
19
|
+
<% end %>
|
|
20
|
+
<% end %>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<div class="w-96 max-w-full">
|
|
2
|
+
<%= sortable_list id: "steps", label: "Interview steps", class: "flex flex-col gap-2" do |list| %>
|
|
3
|
+
<% [ "Phone screen", "Take-home exercise", "Pairing session", "Meet the team" ].each_with_index do |step, index| %>
|
|
4
|
+
<%= list.item(key: "step-#{index}", rank: index + 1, label: step) do %>
|
|
5
|
+
<div class="flex items-center gap-2 rounded-lg border border-neutral-200 bg-white px-2 py-1.5 text-sm dark:border-neutral-800 dark:bg-neutral-900">
|
|
6
|
+
<%= sortable_handle label: "Move #{step}" %>
|
|
7
|
+
<span class="flex-1"><%= step %></span>
|
|
8
|
+
<button type="button" class="<%= button_classes(:icon) %>" aria-label="Edit <%= step %>" title="Edit">
|
|
9
|
+
<%= Unmagic::Components::Icons.svg(self, :pencil) %>
|
|
10
|
+
</button>
|
|
11
|
+
</div>
|
|
12
|
+
<% end %>
|
|
13
|
+
<% end %>
|
|
14
|
+
<% end %>
|
|
15
|
+
<output id="steps_log" class="mt-3 block min-h-5 font-mono text-xs text-neutral-500">Drop something to see the move.</output>
|
|
16
|
+
<script type="module">
|
|
17
|
+
document.getElementById("steps").addEventListener("unmagic-sortable:move", (event) => {
|
|
18
|
+
const { key, prev, next } = event.detail
|
|
19
|
+
document.getElementById("steps_log").textContent = `moved ${key} · prev ${prev || "—"} · next ${next || "—"}`
|
|
20
|
+
})
|
|
21
|
+
</script>
|
|
22
|
+
</div>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<div class="flex w-52 flex-col gap-1.5 text-xs">
|
|
2
|
+
<% [ "Phone screen", "Take-home", "Pairing" ].each_with_index do |step, index| %>
|
|
3
|
+
<div class="flex items-center gap-1.5 rounded-md border border-neutral-200 bg-white px-1.5 py-1 dark:border-neutral-800 dark:bg-neutral-900 <%= "opacity-40" if index == 1 %>">
|
|
4
|
+
<%= Unmagic::Components::Icons.svg(self, :grip_vertical, class: "size-3 text-neutral-400") %><%= step %>
|
|
5
|
+
</div>
|
|
6
|
+
<% end %>
|
|
7
|
+
</div>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<div class="mx-auto max-w-2xl">
|
|
2
|
+
<button type="button" id="reveal_play" class="<%= button_classes %>">Play</button>
|
|
3
|
+
<%= streaming_markdown_tag id: "reveal_reply", class: "UnmagicProse mt-4 min-h-40" %>
|
|
4
|
+
<template id="reveal_flushes">
|
|
5
|
+
<% Unmagic::Components::Browser::Reply.snapshots.each do |html| %>
|
|
6
|
+
<template><%= turbo_stream.stream_markdown "reveal_reply", html %></template>
|
|
7
|
+
<% end %>
|
|
8
|
+
</template>
|
|
9
|
+
<script type="module">
|
|
10
|
+
document.getElementById("reveal_play").addEventListener("click", async () => {
|
|
11
|
+
const reply = document.getElementById("reveal_reply")
|
|
12
|
+
reply.replaceChildren()
|
|
13
|
+
for (const frame of document.getElementById("reveal_flushes").content.children) {
|
|
14
|
+
await new Promise((resolve) => setTimeout(resolve, 40 + Math.random() * 260))
|
|
15
|
+
document.body.append(frame.content.cloneNode(true))
|
|
16
|
+
}
|
|
17
|
+
})
|
|
18
|
+
</script>
|
|
19
|
+
</div>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<div class="mx-auto max-w-2xl">
|
|
2
|
+
<div class="flex gap-2">
|
|
3
|
+
<button type="button" id="stopped_play" class="<%= button_classes %>">Play</button>
|
|
4
|
+
<button type="button" id="stopped_stop" class="<%= button_classes %>">Stop</button>
|
|
5
|
+
</div>
|
|
6
|
+
<div id="stopped_holder" class="mt-4 min-h-24">
|
|
7
|
+
<%= streaming_markdown_tag id: "stopped_reply", class: "UnmagicProse" %>
|
|
8
|
+
</div>
|
|
9
|
+
<template id="stopped_final">
|
|
10
|
+
<%= streaming_markdown_tag id: "stopped_reply", final: true, class: "UnmagicProse" do %>
|
|
11
|
+
<p class="text-neutral-500">Response stopped.</p>
|
|
12
|
+
<% end %>
|
|
13
|
+
</template>
|
|
14
|
+
<template id="stopped_fresh"><%= streaming_markdown_tag id: "stopped_reply", class: "UnmagicProse" %></template>
|
|
15
|
+
<template id="stopped_flushes">
|
|
16
|
+
<% Unmagic::Components::Browser::Reply.snapshots.each do |html| %>
|
|
17
|
+
<template><%= turbo_stream.stream_markdown "stopped_reply", html %></template>
|
|
18
|
+
<% end %>
|
|
19
|
+
</template>
|
|
20
|
+
<script type="module">
|
|
21
|
+
const swap = (id) => document.getElementById("stopped_reply").replaceWith(document.getElementById(id).content.cloneNode(true))
|
|
22
|
+
document.getElementById("stopped_stop").addEventListener("click", () => swap("stopped_final"))
|
|
23
|
+
document.getElementById("stopped_play").addEventListener("click", async () => {
|
|
24
|
+
swap("stopped_fresh")
|
|
25
|
+
for (const frame of document.getElementById("stopped_flushes").content.children) {
|
|
26
|
+
await new Promise((resolve) => setTimeout(resolve, 60 + Math.random() * 240))
|
|
27
|
+
document.body.append(frame.content.cloneNode(true))
|
|
28
|
+
}
|
|
29
|
+
})
|
|
30
|
+
</script>
|
|
31
|
+
</div>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<%= table_for @things, defer: true, paginate: @pager, id: "preview_table", row_id: ->(thing) { "deferred_thing_#{thing.id}" } do |table| %>
|
|
2
|
+
<% table.column "Name", :name, width: "34%" %>
|
|
3
|
+
<% table.column "Role", :role, width: "26%" %>
|
|
4
|
+
<% table.column "Score", :score, numeric: true, width: "20%" %>
|
|
5
|
+
<% table.column "Right", :score, align: :right, width: "20%" %>
|
|
6
|
+
<% end %>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<%= table_for @things, paginate: @pager do |table| %>
|
|
2
|
+
<% table.column "Name", :name, sort: :name, width: "34%" %>
|
|
3
|
+
<% table.column "Role", :role, sort: :role, width: "22%" %>
|
|
4
|
+
<% table.column "Score", :score, numeric: true, width: "14%" %>
|
|
5
|
+
<% table.column "Centred", :role, align: :center, width: "16%" %>
|
|
6
|
+
<% table.column "Right", :score, align: :right, width: "14%" %>
|
|
7
|
+
<% table.details { |thing| thing.note } %>
|
|
8
|
+
<% end %>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<%= tabs id: "preview_tabs" do |tabs| %>
|
|
2
|
+
<% tabs.tab "Body" %>
|
|
3
|
+
<% tabs.tab "Headers" %>
|
|
4
|
+
<% tabs.tab "Preview", disabled: "HTML only" %>
|
|
5
|
+
<% tabs.tab "Timing" %>
|
|
6
|
+
<% tabs.panel do %>
|
|
7
|
+
<pre class="m-0">{ "ok": true }</pre>
|
|
8
|
+
<% end %>
|
|
9
|
+
<% tabs.panel do %>
|
|
10
|
+
<%= detail_list do |list| %>
|
|
11
|
+
<% list.item "Content-Type", "application/json" %>
|
|
12
|
+
<% list.item "Cache-Control", "no-store" %>
|
|
13
|
+
<% end %>
|
|
14
|
+
<% end %>
|
|
15
|
+
<% tabs.panel do %>
|
|
16
|
+
Took 42ms.
|
|
17
|
+
<% end %>
|
|
18
|
+
<% end %>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<%= dialog_button "Open a dialog", dialog: "toast_over_dialog", class: button_classes %>
|
|
2
|
+
|
|
3
|
+
<%= dialog_tag "toast_over_dialog", title: "A dialog" do %>
|
|
4
|
+
<p>
|
|
5
|
+
A toast streamed from in here shows above the dialog, not under its backdrop. It can't be
|
|
6
|
+
hovered or dismissed until the dialog closes, but it still times out on its own.
|
|
7
|
+
</p>
|
|
8
|
+
<%= button_to "Stream a toast", stream_toast_path, params: { tone: "info", message: "Above the dialog." },
|
|
9
|
+
class: button_classes(:primary) %>
|
|
10
|
+
<% end %>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<div class="flex flex-wrap gap-2">
|
|
2
|
+
<%= button_to "Notice", flash_toast_path, params: { type: "notice", message: "Changes saved." },
|
|
3
|
+
class: button_classes(:primary) %>
|
|
4
|
+
<%= button_to "Alert", flash_toast_path, params: { type: "alert", message: "Couldn't save your changes." },
|
|
5
|
+
class: button_classes %>
|
|
6
|
+
<%= button_to "Warning", flash_toast_path, params: { type: "warning", message: "Your trial ends in 3 days." },
|
|
7
|
+
class: button_classes %>
|
|
8
|
+
<%= button_to "Info", flash_toast_path, params: { type: "info", message: "A new version is available." },
|
|
9
|
+
class: button_classes %>
|
|
10
|
+
<%= button_to "Long", flash_toast_path,
|
|
11
|
+
params: { type: "notice", message: "Imported 1,204 contacts from contacts-export-2026-09-16-final-final.csv into the Sales pipeline." },
|
|
12
|
+
class: button_classes %>
|
|
13
|
+
</div>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<div class="flex flex-wrap gap-2">
|
|
2
|
+
<%= button_to "Stream a toast", stream_toast_path, params: { tone: "good", message: "Copied to your clipboard." },
|
|
3
|
+
class: button_classes %>
|
|
4
|
+
<%= button_to "Stream a bad toast", stream_toast_path, params: { tone: "bad", message: "Couldn't reach Slack." },
|
|
5
|
+
class: button_classes(:danger) %>
|
|
6
|
+
</div>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<%# A static stand-in: a real toast only shows once <unmagic-toasts> opens it. %>
|
|
2
|
+
<div class="flex w-60 flex-col gap-2 text-xs">
|
|
3
|
+
<% { good: "Changes saved.", bad: "Couldn't reach Slack." }.each do |tone, message| %>
|
|
4
|
+
<%= tag.div class: "UnmagicToast UnmagicToast--#{tone}", data: { open: "" } do %>
|
|
5
|
+
<%= Unmagic::Components::Icons.svg(self, Unmagic::Components::Icons::TONE_ICONS.fetch(tone),
|
|
6
|
+
class: "UnmagicToast__icon size-3.5") %>
|
|
7
|
+
<%= tag.div message, class: "UnmagicToast__message" %>
|
|
8
|
+
<% end %>
|
|
9
|
+
<% end %>
|
|
10
|
+
</div>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<div class="flex flex-wrap justify-between gap-2">
|
|
2
|
+
<%= tooltip text: "Shown below, flipping above when there's no room.", placement: :bottom do %>
|
|
3
|
+
<button type="button" class="<%= button_classes %>">placement: :bottom</button>
|
|
4
|
+
<% end %>
|
|
5
|
+
<%= tooltip text: "Clamped so it never runs off the edge of the screen, however long the hint happens to get." do %>
|
|
6
|
+
<button type="button" class="<%= button_classes %>">Near the right edge</button>
|
|
7
|
+
<% end %>
|
|
8
|
+
</div>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<%# A static open hint: the real one is a popover the element shows on hover. %>
|
|
2
|
+
<div class="flex flex-col items-center gap-1.5">
|
|
3
|
+
<span class="UnmagicTooltip__popup static opacity-100" data-open>Copy the key</span>
|
|
4
|
+
<%= tag.span "Copy", class: button_classes(:default, size: :small) %>
|
|
5
|
+
</div>
|
data/lib/unmagic/components/browser/app/views/unmagic/components/browser/pages/component.html.erb
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
<% content_for :title, @component.name %>
|
|
2
|
+
<% content_for :breadcrumbs do %>
|
|
3
|
+
<%= render "crumb", label: @component.group || "Components", path: root_path %>
|
|
4
|
+
<%= render "crumb", label: @component.name %>
|
|
5
|
+
<% end %>
|
|
6
|
+
|
|
7
|
+
<% position = @components.index(@component) %>
|
|
8
|
+
<% previous_component = @components[position - 1] if position.positive? %>
|
|
9
|
+
<% next_component = @components[position + 1] %>
|
|
10
|
+
|
|
11
|
+
<div class="xl:grid xl:grid-cols-[minmax(0,1fr)_12rem] xl:gap-12">
|
|
12
|
+
<article class="min-w-0">
|
|
13
|
+
<header class="mb-12">
|
|
14
|
+
<h1 class="text-3xl font-semibold tracking-tight"><%= @component.name %></h1>
|
|
15
|
+
<p class="mt-3 max-w-2xl text-base leading-7 text-neutral-600 dark:text-neutral-400"><%= @component.description %></p>
|
|
16
|
+
<div class="mt-5 flex flex-wrap items-center gap-2">
|
|
17
|
+
<%= badge tag.code(@component.helper) if @component.helper %>
|
|
18
|
+
<%= badge tag.code(%(import "#{@component.import}")) if @component.import %>
|
|
19
|
+
<%= badge pluralize(@component.examples.size, "example") %>
|
|
20
|
+
</div>
|
|
21
|
+
</header>
|
|
22
|
+
|
|
23
|
+
<div class="space-y-14">
|
|
24
|
+
<% @component.examples.each do |example| %>
|
|
25
|
+
<%= render "example", component: @component, example: example %>
|
|
26
|
+
<% end %>
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
<nav class="mt-16 grid gap-4 border-t border-neutral-200 pt-8 sm:grid-cols-2 dark:border-neutral-800" aria-label="More components">
|
|
30
|
+
<% pager = "block rounded-xl border border-neutral-200 p-4 hover:border-neutral-300 hover:bg-neutral-50 dark:border-neutral-800 dark:hover:border-neutral-700 dark:hover:bg-neutral-900" %>
|
|
31
|
+
<% if previous_component %>
|
|
32
|
+
<%= link_to component_path(previous_component.slug), class: pager do %>
|
|
33
|
+
<span class="block text-xs text-neutral-500">Previous</span>
|
|
34
|
+
<span class="mt-1 block text-sm font-medium"><%= previous_component.name %></span>
|
|
35
|
+
<% end %>
|
|
36
|
+
<% end %>
|
|
37
|
+
<% if next_component %>
|
|
38
|
+
<%= link_to component_path(next_component.slug), class: "#{pager} text-right sm:col-start-2" do %>
|
|
39
|
+
<span class="block text-xs text-neutral-500">Next</span>
|
|
40
|
+
<span class="mt-1 block text-sm font-medium"><%= next_component.name %></span>
|
|
41
|
+
<% end %>
|
|
42
|
+
<% end %>
|
|
43
|
+
</nav>
|
|
44
|
+
</article>
|
|
45
|
+
|
|
46
|
+
<aside class="hidden xl:block">
|
|
47
|
+
<div class="sticky top-24">
|
|
48
|
+
<p class="text-xs font-semibold text-neutral-500 dark:text-neutral-400">On this page</p>
|
|
49
|
+
<ul class="mt-3 space-y-1 border-l border-neutral-200 dark:border-neutral-800">
|
|
50
|
+
<% @component.examples.each do |example| %>
|
|
51
|
+
<li>
|
|
52
|
+
<a href="#<%= @component.slug %>_<%= example.key %>" class="-ml-px block border-l border-transparent py-1 pl-3 text-sm text-neutral-600 hover:border-neutral-400 hover:text-neutral-900 dark:text-neutral-400 dark:hover:border-neutral-500 dark:hover:text-neutral-100"><%= example.title %></a>
|
|
53
|
+
</li>
|
|
54
|
+
<% end %>
|
|
55
|
+
</ul>
|
|
56
|
+
</div>
|
|
57
|
+
</aside>
|
|
58
|
+
</div>
|
data/lib/unmagic/components/browser/app/views/unmagic/components/browser/pages/installation.html.erb
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<% content_for :title, "Installation" %>
|
|
2
|
+
<% content_for :breadcrumbs do %>
|
|
3
|
+
<%= render "crumb", label: "Getting started", path: root_path %>
|
|
4
|
+
<%= render "crumb", label: "Installation" %>
|
|
5
|
+
<% end %>
|
|
6
|
+
|
|
7
|
+
<% prose = "mt-3 max-w-2xl text-base leading-7 text-neutral-600 dark:text-neutral-400" %>
|
|
8
|
+
<% step = "mt-12 text-lg font-semibold" %>
|
|
9
|
+
|
|
10
|
+
<article class="max-w-3xl">
|
|
11
|
+
<h1 class="text-3xl font-semibold tracking-tight">Installation</h1>
|
|
12
|
+
<p class="<%= prose %>">
|
|
13
|
+
Add the gem, import its styles into your Tailwind build, and import its JavaScript. The helpers are
|
|
14
|
+
mixed into your views automatically.
|
|
15
|
+
</p>
|
|
16
|
+
|
|
17
|
+
<h2 class="<%= step %>">1. Add the gem</h2>
|
|
18
|
+
<div class="mt-4"><%= render "code", id: "install_gem", source: "bundle add unmagic-components" %></div>
|
|
19
|
+
|
|
20
|
+
<h2 class="<%= step %>">2. Import the styles</h2>
|
|
21
|
+
<p class="<%= prose %>">
|
|
22
|
+
The components are styled with Tailwind CSS v4, which your app needs. With tailwindcss-rails, import the
|
|
23
|
+
gem's styles after Tailwind in <code>app/assets/tailwind/application.css</code>:
|
|
24
|
+
</p>
|
|
25
|
+
<div class="mt-4">
|
|
26
|
+
<%= render "code", id: "install_tailwind", source: %(@import "tailwindcss";\n@import "../builds/tailwind/unmagic_components";) %>
|
|
27
|
+
</div>
|
|
28
|
+
<p class="<%= prose %>">
|
|
29
|
+
tailwindcss-rails writes that entry file on every build. With the Tailwind CLI or an npm build, import
|
|
30
|
+
<code>app/assets/tailwind/unmagic_components/engine.css</code> from the gem's install path
|
|
31
|
+
(<code>bundle show unmagic-components</code>) instead.
|
|
32
|
+
</p>
|
|
33
|
+
|
|
34
|
+
<h2 class="<%= step %>">3. Import the JavaScript</h2>
|
|
35
|
+
<p class="<%= prose %>">
|
|
36
|
+
With importmap-rails the engine pins every component. Import them all, or only the ones a page uses:
|
|
37
|
+
</p>
|
|
38
|
+
<div class="mt-4">
|
|
39
|
+
<%= render "code", id: "install_js", source: %(import "unmagic/components"\n\n// or one at a time\nimport "unmagic/components/menu") %>
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
<h2 class="<%= step %>">4. Use the form builder</h2>
|
|
43
|
+
<p class="<%= prose %>">Pass it per form, or make it the default for every form:</p>
|
|
44
|
+
<div class="mt-4">
|
|
45
|
+
<%= render "code", id: "install_builder", source: "config.action_view.default_form_builder = Unmagic::Components::FormBuilder" %>
|
|
46
|
+
</div>
|
|
47
|
+
</article>
|
data/lib/unmagic/components/browser/app/views/unmagic/components/browser/pages/overview.html.erb
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
<% content_for :breadcrumbs do %>
|
|
2
|
+
<%= render "crumb", label: "Components", path: root_path %>
|
|
3
|
+
<%= render "crumb", label: "Overview" %>
|
|
4
|
+
<% end %>
|
|
5
|
+
|
|
6
|
+
<section class="max-w-3xl">
|
|
7
|
+
<h1 class="text-4xl font-semibold tracking-tight">
|
|
8
|
+
unmagic-components <span class="text-neutral-400 dark:text-neutral-500">for Rails</span>
|
|
9
|
+
</h1>
|
|
10
|
+
<p class="mt-5 text-lg leading-8 text-neutral-600 dark:text-neutral-400">
|
|
11
|
+
Server-rendered UI components for Rails views, in the spirit of <code class="text-base">form_for</code>:
|
|
12
|
+
describe the columns, the fields or the tabs, and get the chrome. Styled with Tailwind CSS v4, with
|
|
13
|
+
behaviour from plain custom elements and no Stimulus.
|
|
14
|
+
</p>
|
|
15
|
+
<div class="mt-7 flex flex-wrap gap-2">
|
|
16
|
+
<%= link_to "Installation", installation_path, class: button_classes(:primary) %>
|
|
17
|
+
<%= link_to "Theming", theming_path, class: button_classes %>
|
|
18
|
+
</div>
|
|
19
|
+
</section>
|
|
20
|
+
|
|
21
|
+
<hr class="my-14 border-t border-dashed border-neutral-200 dark:border-neutral-800">
|
|
22
|
+
|
|
23
|
+
<% @component_groups.each_with_index do |(group, components), index| %>
|
|
24
|
+
<section class="<%= "mt-16" unless index.zero? %>">
|
|
25
|
+
<h2 class="text-xl font-semibold"><%= group %></h2>
|
|
26
|
+
<p class="mt-2 text-sm text-neutral-600 dark:text-neutral-400">
|
|
27
|
+
<%= pluralize(components.size, "component") %> and <%= pluralize(components.sum { |component| component.examples.size }, "example") %>,
|
|
28
|
+
each rendered live with its source beside it.
|
|
29
|
+
</p>
|
|
30
|
+
|
|
31
|
+
<div class="mt-8 grid gap-5 sm:grid-cols-2 lg:grid-cols-3 2xl:grid-cols-4">
|
|
32
|
+
<% components.each do |component| %>
|
|
33
|
+
<%# The card isn't a link itself: the thumbnail can hold links and buttons of its
|
|
34
|
+
own, which can't sit inside an <a>. The title's link stretches over the card. %>
|
|
35
|
+
<div class="group relative overflow-hidden rounded-xl border border-neutral-200 bg-white hover:border-neutral-300 hover:shadow-sm dark:border-neutral-800 dark:bg-neutral-950 dark:hover:border-neutral-700">
|
|
36
|
+
<div class="flex h-40 items-center justify-center overflow-hidden border-b border-neutral-200 bg-neutral-50 bg-[radial-gradient(var(--color-neutral-200)_1px,transparent_1px)] [background-size:16px_16px] p-5 dark:border-neutral-800 dark:bg-neutral-900/40 dark:bg-[radial-gradient(var(--color-neutral-800)_1px,transparent_1px)]">
|
|
37
|
+
<div inert class="pointer-events-none flex max-h-full max-w-full select-none items-center justify-center">
|
|
38
|
+
<%= render partial: example_partial(component, "thumbnail") %>
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
<div class="px-4 py-3">
|
|
42
|
+
<%= link_to component.name, component_path(component.slug), class: "text-sm font-semibold after:absolute after:inset-0" %>
|
|
43
|
+
<p class="mt-0.5 text-xs text-neutral-500 dark:text-neutral-400"><%= pluralize(component.examples.size, "example") %></p>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
<% end %>
|
|
47
|
+
</div>
|
|
48
|
+
</section>
|
|
49
|
+
<% end %>
|
data/lib/unmagic/components/browser/app/views/unmagic/components/browser/pages/theming.html.erb
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<% content_for :title, "Theming" %>
|
|
2
|
+
<% content_for :breadcrumbs do %>
|
|
3
|
+
<%= render "crumb", label: "Getting started", path: root_path %>
|
|
4
|
+
<%= render "crumb", label: "Theming" %>
|
|
5
|
+
<% end %>
|
|
6
|
+
|
|
7
|
+
<% prose = "mt-3 max-w-2xl text-base leading-7 text-neutral-600 dark:text-neutral-400" %>
|
|
8
|
+
<% step = "mt-12 text-lg font-semibold" %>
|
|
9
|
+
|
|
10
|
+
<article class="max-w-3xl">
|
|
11
|
+
<h1 class="text-3xl font-semibold tracking-tight">Theming</h1>
|
|
12
|
+
<p class="<%= prose %>">
|
|
13
|
+
The components use Tailwind's own palette, with a <code>dark:</code> variant for every colour, so they
|
|
14
|
+
follow your theme from the defaults onward.
|
|
15
|
+
</p>
|
|
16
|
+
|
|
17
|
+
<h2 class="<%= step %>">Dark mode</h2>
|
|
18
|
+
<p class="<%= prose %>">
|
|
19
|
+
Dark mode follows your app's <code>dark</code> variant, which is the visitor's system setting by default.
|
|
20
|
+
To switch on a class or an attribute instead, redefine the variant. This browser uses
|
|
21
|
+
<code>data-theme</code>:
|
|
22
|
+
</p>
|
|
23
|
+
<div class="mt-4">
|
|
24
|
+
<%= render "code", id: "theming_dark", source: "@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));" %>
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
<h2 class="<%= step %>">Colours, radii and fonts</h2>
|
|
28
|
+
<p class="<%= prose %>">They come from your theme, so change them there and every component follows:</p>
|
|
29
|
+
<div class="mt-4">
|
|
30
|
+
<%= render "code", id: "theming_theme", source: "@theme {\n --color-neutral-900: oklch(0.21 0.03 265);\n --radius-md: 0.25rem;\n}" %>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
<h2 class="<%= step %>">One component</h2>
|
|
34
|
+
<p class="<%= prose %>">
|
|
35
|
+
Pass utilities through <code>class:</code>. The gem's rules sit in <code>@layer components</code>, which
|
|
36
|
+
Tailwind orders before utilities, so yours win:
|
|
37
|
+
</p>
|
|
38
|
+
<%# Captured as template text: ERB inside a Ruby string would close this tag early. %>
|
|
39
|
+
<% utilities_source = capture do %><%%= card title: "Members", class: "rounded-none shadow-none" do %>…<%% end %><% end %>
|
|
40
|
+
<div class="mt-4">
|
|
41
|
+
<%= render "code", id: "theming_utilities", source: utilities_source.to_str %>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<h2 class="<%= step %>">Form controls</h2>
|
|
45
|
+
<p class="<%= prose %>">
|
|
46
|
+
Controls wear a class for their kind, which comes from a setting. Point it at your own classes, or
|
|
47
|
+
return <code>nil</code> to leave controls unstyled:
|
|
48
|
+
</p>
|
|
49
|
+
<div class="mt-4">
|
|
50
|
+
<%= render "code", id: "theming_controls", source: %(config.control_class = ->(_view, kind) { kind == :select ? "form-select" : "form-input" }) %>
|
|
51
|
+
</div>
|
|
52
|
+
</article>
|