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,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Unmagic
|
|
4
|
+
module Components
|
|
5
|
+
# A stretch of time as somebody would say it: 640ms, 2s, 3m 5s, 1h 4m 2s. Prose
|
|
6
|
+
# about a wait rather than a clock, so the larger units are left off until they
|
|
7
|
+
# are needed. Anything under a second is milliseconds, since most tool calls
|
|
8
|
+
# answer in a fraction of one and "0s" says nothing about which fraction.
|
|
9
|
+
#
|
|
10
|
+
# <unmagic-elapsed> writes the same words while a clock is running (elapsed.js),
|
|
11
|
+
# so a reading doesn't change shape the moment it settles. Change both together.
|
|
12
|
+
module Duration
|
|
13
|
+
def self.format(seconds)
|
|
14
|
+
return if seconds.nil?
|
|
15
|
+
|
|
16
|
+
if seconds.positive? && seconds < 1
|
|
17
|
+
return I18n.t("unmagic.components.elapsed.milliseconds", count: (seconds * 1000).round, default: "%{count}ms")
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
whole = seconds.round
|
|
21
|
+
hours, remainder = whole.divmod(3600)
|
|
22
|
+
minutes, secs = remainder.divmod(60)
|
|
23
|
+
|
|
24
|
+
if whole < 60
|
|
25
|
+
I18n.t("unmagic.components.elapsed.seconds", count: whole, default: "%{count}s")
|
|
26
|
+
elsif whole < 3600
|
|
27
|
+
I18n.t("unmagic.components.elapsed.minutes", minutes: minutes, seconds: secs, default: "%{minutes}m %{seconds}s")
|
|
28
|
+
else
|
|
29
|
+
I18n.t("unmagic.components.elapsed.hours", hours: hours, minutes: minutes, seconds: secs,
|
|
30
|
+
default: "%{hours}h %{minutes}m %{seconds}s")
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Unmagic
|
|
4
|
+
module Components
|
|
5
|
+
# A clock counting up from a moment the server named, or down to one. See
|
|
6
|
+
# ActionViewHelpers#elapsed_tag.
|
|
7
|
+
class Elapsed
|
|
8
|
+
DIRECTIONS = %i[up down].freeze
|
|
9
|
+
|
|
10
|
+
def initialize(view, time, direction:, **options)
|
|
11
|
+
unless DIRECTIONS.include?(direction)
|
|
12
|
+
raise ArgumentError, "unknown elapsed_tag direction #{direction.inspect} (expected one of #{DIRECTIONS.inspect})"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
@view = view
|
|
16
|
+
@time = time.in_time_zone
|
|
17
|
+
@direction = direction
|
|
18
|
+
@options = options
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# The server writes the reading the element would, so it is right before the
|
|
22
|
+
# script loads. Whole seconds, floored going up and ceiled going down: a
|
|
23
|
+
# stopwatch says how long it has been, not how long it nearly has been.
|
|
24
|
+
def render
|
|
25
|
+
attribute = @direction == :up ? :since : :until
|
|
26
|
+
|
|
27
|
+
view.content_tag("unmagic-elapsed", Duration.format(seconds), **@options,
|
|
28
|
+
attribute => @time.utc.iso8601(3),
|
|
29
|
+
title: I18n.l(@time, format: :long),
|
|
30
|
+
class: view.class_names("UnmagicElapsed", @options[:class]))
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
attr_reader :view
|
|
36
|
+
|
|
37
|
+
def seconds
|
|
38
|
+
if @direction == :up
|
|
39
|
+
[ (Time.current - @time).floor, 0 ].max
|
|
40
|
+
else
|
|
41
|
+
[ (@time - Time.current).ceil, 0 ].max
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -7,10 +7,19 @@ module Unmagic
|
|
|
7
7
|
class Engine < ::Rails::Engine
|
|
8
8
|
isolate_namespace Unmagic::Components
|
|
9
9
|
|
|
10
|
+
# Both hooks, because a helper test never touches ActionView::Base. The :action_view hook
|
|
11
|
+
# mixes into Base, which is what a template renders through — but ActionView::TestCase builds
|
|
12
|
+
# its own view class and calls the helper under test on the test case itself. So a host helper
|
|
13
|
+
# that composes one of these (badge, button_classes) works in the app and raises NoMethodError
|
|
14
|
+
# in its own spec, which is a confusing place to send someone.
|
|
10
15
|
initializer "unmagic_components.helpers" do
|
|
11
16
|
ActiveSupport.on_load(:action_view) do
|
|
12
17
|
include Unmagic::Components::ActionViewHelpers
|
|
13
18
|
end
|
|
19
|
+
|
|
20
|
+
ActiveSupport.on_load(:action_view_test_case) do
|
|
21
|
+
include Unmagic::Components::ActionViewHelpers
|
|
22
|
+
end
|
|
14
23
|
end
|
|
15
24
|
|
|
16
25
|
# turbo_stream.toast. turbo-rails runs this hook when its tag builder loads, so
|
|
@@ -1,39 +1,47 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "unmagic_icon"
|
|
4
|
+
|
|
3
5
|
module Unmagic
|
|
4
6
|
module Components
|
|
5
|
-
# The
|
|
6
|
-
#
|
|
7
|
-
#
|
|
7
|
+
# The glyphs the components draw for themselves — a close button, a toast's
|
|
8
|
+
# tone, a tool call's state. Rendered inline through unmagic-icon from a subset
|
|
9
|
+
# of Lucide the gem ships in app/assets/icons/lucide (ISC, see the LICENSE
|
|
10
|
+
# beside the files), so a host needs no icon set of its own: kp2 downloads
|
|
11
|
+
# Phosphor, not Lucide, and the components still draw.
|
|
12
|
+
#
|
|
13
|
+
# The library is read from the gem's own directory rather than looked up in
|
|
14
|
+
# unmagic-icon's registry, which only learns about engine icons when the host
|
|
15
|
+
# leaves unmagic-icon to initialise itself. Under Rails the same files are also
|
|
16
|
+
# registered as "unmagic_components:lucide", for a host that wants to draw them.
|
|
8
17
|
module Icons
|
|
9
|
-
|
|
10
|
-
x: '<path d="M18 6 6 18"/><path d="m6 6 12 12"/>',
|
|
11
|
-
check: '<path d="M20 6 9 17l-5-5"/>',
|
|
12
|
-
circle_check: '<circle cx="12" cy="12" r="10"/><path d="m9 12 2 2 4-4"/>',
|
|
13
|
-
circle_x: '<circle cx="12" cy="12" r="10"/><path d="m15 9-6 6"/><path d="m9 9 6 6"/>',
|
|
14
|
-
triangle_alert: '<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"/>' \
|
|
15
|
-
'<path d="M12 9v4"/><path d="M12 17h.01"/>',
|
|
16
|
-
info: '<circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/>',
|
|
17
|
-
copy: '<rect width="14" height="14" x="8" y="8" rx="2" ry="2"/>' \
|
|
18
|
-
'<path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/>',
|
|
19
|
-
rotate: '<path d="M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8"/><path d="M21 3v5h-5"/>',
|
|
20
|
-
ellipsis_vertical: '<circle cx="12" cy="12" r="1"/><circle cx="12" cy="5" r="1"/><circle cx="12" cy="19" r="1"/>',
|
|
21
|
-
arrow_left: '<path d="m12 19-7-7 7-7"/><path d="M19 12H5"/>',
|
|
22
|
-
chevron_down: '<path d="m6 9 6 6 6-6"/>'
|
|
23
|
-
}.freeze
|
|
18
|
+
DIRECTORY = File.expand_path("../../../app/assets/icons/lucide", __dir__)
|
|
24
19
|
|
|
25
20
|
# The glyph a tone leads with, where it has one.
|
|
26
21
|
TONE_ICONS = { good: :circle_check, warn: :triangle_alert, bad: :circle_x, info: :info }.freeze
|
|
27
22
|
|
|
28
|
-
|
|
29
|
-
|
|
23
|
+
class << self
|
|
24
|
+
# An icon by its Lucide name as a symbol, :circle_check for circle-check.
|
|
25
|
+
# Decorative unless the caller says otherwise, so aria-hidden is on by default.
|
|
26
|
+
def svg(view, name, **options)
|
|
27
|
+
library.find(name.to_s.tr("_", "-")).render(
|
|
28
|
+
"aria-hidden": "true", **options, class: view.class_names("UnmagicIcon", options[:class])
|
|
29
|
+
)
|
|
30
|
+
rescue Unmagic::Icon::IconNotFoundError
|
|
31
|
+
raise ArgumentError, "unknown icon #{name.inspect}"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def names
|
|
35
|
+
Dir[File.join(DIRECTORY, "*.svg")].map { |path| File.basename(path, ".svg").tr("-", "_").to_sym }.sort
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
30
39
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
)
|
|
40
|
+
# The library's manifest-free lookup falls back to nothing, so an unknown
|
|
41
|
+
# name raises rather than drawing some other glyph.
|
|
42
|
+
def library
|
|
43
|
+
@library ||= Unmagic::Icon::Library.new(name: "unmagic_components:lucide", path: DIRECTORY)
|
|
44
|
+
end
|
|
37
45
|
end
|
|
38
46
|
end
|
|
39
47
|
end
|
|
@@ -56,7 +56,7 @@ module Unmagic
|
|
|
56
56
|
|
|
57
57
|
panel.footer do
|
|
58
58
|
tag.button type: "button", class: Button.classes(:primary), data: { unmagic_modal_retry: "" } do
|
|
59
|
-
safe_join [ Icons.svg(view, :
|
|
59
|
+
safe_join [ Icons.svg(view, :rotate_cw), I18n.t("unmagic.components.modal.retry", default: "Try again") ]
|
|
60
60
|
end
|
|
61
61
|
end
|
|
62
62
|
|
|
@@ -41,6 +41,14 @@ module Unmagic
|
|
|
41
41
|
nil
|
|
42
42
|
end
|
|
43
43
|
|
|
44
|
+
# Markup beside the title, as it is — the counterpart to leading, and the way to put badges
|
|
45
|
+
# there that something else has already rendered: a status partial, a row a helper returns.
|
|
46
|
+
# Those can't go through badge, which would wrap a badge in a badge.
|
|
47
|
+
def trailing(content = nil, &block)
|
|
48
|
+
@badges << (block ? view.capture(&block) : content)
|
|
49
|
+
nil
|
|
50
|
+
end
|
|
51
|
+
|
|
44
52
|
# As a skeleton, anything given still renders for real, and the rest stands in
|
|
45
53
|
# as shapes: a title bar, a description line (unless description: false) and
|
|
46
54
|
# a button.
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "bigdecimal"
|
|
4
|
+
|
|
5
|
+
module Unmagic
|
|
6
|
+
module Components
|
|
7
|
+
# Drag-and-drop ordering: a list whose items can be dragged, or moved with the
|
|
8
|
+
# keyboard, and dropped in a new place, here or in another list that shares its
|
|
9
|
+
# namespace. See ActionViewHelpers#sortable_list.
|
|
10
|
+
module Sortable
|
|
11
|
+
ORIENTATIONS = %i[vertical horizontal grid].freeze
|
|
12
|
+
|
|
13
|
+
# What the element says to a screen reader as an item is picked up, moved,
|
|
14
|
+
# dropped or put back. {item}, {list}, {position} and {count} are filled in by
|
|
15
|
+
# the element as it goes.
|
|
16
|
+
ANNOUNCEMENTS = {
|
|
17
|
+
instructions: "Press Space to pick up. While holding it, use the arrow keys to move it, Space to drop it, " \
|
|
18
|
+
"and Escape to cancel.",
|
|
19
|
+
picked: "Picked up {item}. Position {position} of {count} in {list}.",
|
|
20
|
+
moved: "{item} moved to position {position} of {count} in {list}.",
|
|
21
|
+
dropped: "Dropped {item} at position {position} of {count} in {list}.",
|
|
22
|
+
cancelled: "Cancelled. {item} is back at position {position} of {count} in {list}."
|
|
23
|
+
}.freeze
|
|
24
|
+
|
|
25
|
+
def self.announcements
|
|
26
|
+
ANNOUNCEMENTS.to_h do |key, default|
|
|
27
|
+
[ key, I18n.t("unmagic.components.sortable.#{key}", default: default) ]
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# The <unmagic-sortable-list> and the items recorded into it.
|
|
32
|
+
class List
|
|
33
|
+
def initialize(view, namespace: nil, params: {}, url: nil, orientation: :vertical, label: nil, **options)
|
|
34
|
+
unless ORIENTATIONS.include?(orientation)
|
|
35
|
+
raise ArgumentError, "unknown sortable_list orientation #{orientation.inspect} (expected one of #{ORIENTATIONS.inspect})"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
@view = view
|
|
39
|
+
@namespace = namespace
|
|
40
|
+
@params = params
|
|
41
|
+
@url = url.nil? ? Components.configuration.sortable_url.call(view) : url
|
|
42
|
+
@orientation = orientation
|
|
43
|
+
@label = label
|
|
44
|
+
@options = options
|
|
45
|
+
@items = []
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# An item for a record (its key and rank come from config.sortable_item), or
|
|
49
|
+
# for key: and rank: given directly. label: is what a screen reader hears it
|
|
50
|
+
# called; without it, its text.
|
|
51
|
+
def item(record = nil, key: nil, rank: nil, label: nil, tag: "unmagic-sortable-item", **options, &block)
|
|
52
|
+
attributes = record ? Components.configuration.sortable_item.call(view, record) : {}
|
|
53
|
+
key ||= attributes[:key]
|
|
54
|
+
rank ||= attributes[:rank]
|
|
55
|
+
raise ArgumentError, "a sortable item needs a record or a key:" if key.blank?
|
|
56
|
+
|
|
57
|
+
rank = rank.to_s("F") if rank.is_a?(BigDecimal)
|
|
58
|
+
@items << view.content_tag(tag, block ? view.capture(&block) : nil, **options,
|
|
59
|
+
key: key,
|
|
60
|
+
"data-sortable-rank": rank,
|
|
61
|
+
"data-sortable-label": label,
|
|
62
|
+
class: view.class_names("UnmagicSortableItem", options[:class]))
|
|
63
|
+
nil
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def render(extra = nil)
|
|
67
|
+
announcements = Sortable.announcements
|
|
68
|
+
|
|
69
|
+
view.content_tag("unmagic-sortable-list", **@options,
|
|
70
|
+
namespace: @namespace.presence,
|
|
71
|
+
url: @url.presence,
|
|
72
|
+
orientation: (@orientation unless @orientation == :vertical),
|
|
73
|
+
label: @label,
|
|
74
|
+
data: announcements.transform_keys { |key| "sortable_#{key}" }.merge(@options[:data] || {}),
|
|
75
|
+
class: view.class_names("UnmagicSortableList", "UnmagicSortableList--#{@orientation}", @options[:class])) do
|
|
76
|
+
view.safe_join [
|
|
77
|
+
*@params.map { |name, value| view.content_tag("unmagic-sortable-param", "", name: name, value: value) },
|
|
78
|
+
*@items,
|
|
79
|
+
extra
|
|
80
|
+
].compact
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
private
|
|
85
|
+
|
|
86
|
+
attr_reader :view
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# The grip an item is dragged by. With one in an item, only the grip starts a
|
|
90
|
+
# drag, so the rest of the item stays clickable, and it is the item's keyboard
|
|
91
|
+
# stop.
|
|
92
|
+
def self.handle(view, label: nil, **options)
|
|
93
|
+
label ||= I18n.t("unmagic.components.sortable.handle", default: "Drag to reorder")
|
|
94
|
+
|
|
95
|
+
view.tag.button(type: "button", **options,
|
|
96
|
+
"aria-label": label, title: label,
|
|
97
|
+
"data-sortable-handle": "",
|
|
98
|
+
class: view.class_names(Button.classes(:icon), "UnmagicSortableHandle", options[:class])) do
|
|
99
|
+
Icons.svg(view, :grip_vertical)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Unmagic
|
|
4
|
+
module Components
|
|
5
|
+
# Server-rendered HTML, revealed at a steady pace as later renders of it are
|
|
6
|
+
# streamed in. See ActionViewHelpers#streaming_markdown_tag.
|
|
7
|
+
class StreamingMarkdown
|
|
8
|
+
def initialize(view, id:, final: false, streaming: false, **options)
|
|
9
|
+
raise ArgumentError, "streaming_markdown_tag needs an id: to stream into" if id.blank?
|
|
10
|
+
|
|
11
|
+
@view = view
|
|
12
|
+
@id = id
|
|
13
|
+
@final = final
|
|
14
|
+
@streaming = streaming
|
|
15
|
+
@options = options
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# aria-busy only while the caller says a reply is live. It is what stops a
|
|
19
|
+
# transcript's live region announcing every flush, and a settled reply
|
|
20
|
+
# rendered busy would be skipped by a screen reader if the script never ran.
|
|
21
|
+
def render(content)
|
|
22
|
+
view.content_tag("unmagic-streaming-markdown", content, **@options,
|
|
23
|
+
id: @id,
|
|
24
|
+
final: ("" if @final),
|
|
25
|
+
"aria-busy": ("true" if @streaming && !@final),
|
|
26
|
+
class: view.class_names("UnmagicStreamingMarkdown", @options[:class]))
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
attr_reader :view
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -14,6 +14,22 @@ module Unmagic
|
|
|
14
14
|
def toast(message, tone: :good)
|
|
15
15
|
append Toast::TARGET, Toast.new(@view_context, message, tone: tone).template
|
|
16
16
|
end
|
|
17
|
+
|
|
18
|
+
# Hands the whole of a reply rendered so far to the <unmagic-streaming-markdown>
|
|
19
|
+
# with that id, which paces its way toward it. Send the full render every
|
|
20
|
+
# time, not the new part: the element works out what is new.
|
|
21
|
+
#
|
|
22
|
+
# turbo_stream.stream_markdown "message_1_content", Markdown.render(message.content)
|
|
23
|
+
#
|
|
24
|
+
# From a model, the same action through Turbo::Broadcastable:
|
|
25
|
+
#
|
|
26
|
+
# broadcast_action_to chat, action: :stream_markdown, target: "#{dom_id(self)}_content",
|
|
27
|
+
# html: Markdown.render(content)
|
|
28
|
+
#
|
|
29
|
+
# Needs import "unmagic/components/streaming_markdown".
|
|
30
|
+
def stream_markdown(target, content = nil, **rendering, &block)
|
|
31
|
+
action :stream_markdown, target, content, **rendering, &block
|
|
32
|
+
end
|
|
17
33
|
end
|
|
18
34
|
end
|
|
19
35
|
end
|
data/lib/unmagic/components.rb
CHANGED
|
@@ -38,10 +38,39 @@ require_relative "components/tabs"
|
|
|
38
38
|
require_relative "components/copy_button"
|
|
39
39
|
require_relative "components/autogrow"
|
|
40
40
|
require_relative "components/uuid_input"
|
|
41
|
+
require_relative "components/duration"
|
|
42
|
+
require_relative "components/elapsed"
|
|
43
|
+
require_relative "components/streaming_markdown"
|
|
44
|
+
require_relative "components/avatar"
|
|
45
|
+
require_relative "components/sortable"
|
|
46
|
+
require_relative "components/board"
|
|
47
|
+
require_relative "components/ai_chat"
|
|
48
|
+
require_relative "components/ai_chat/payload"
|
|
49
|
+
require_relative "components/ai_chat/reasoning"
|
|
50
|
+
require_relative "components/ai_chat/message"
|
|
51
|
+
require_relative "components/ai_chat/transcript"
|
|
52
|
+
require_relative "components/ai_chat/tool_call"
|
|
53
|
+
require_relative "components/ai_chat/failure"
|
|
54
|
+
require_relative "components/ai_chat/section"
|
|
55
|
+
require_relative "components/ai_chat/plan"
|
|
56
|
+
require_relative "components/ai_chat/workspace"
|
|
57
|
+
require_relative "components/ai_chat/composer"
|
|
58
|
+
require_relative "components/ai_chat/request"
|
|
59
|
+
require_relative "components/ai_chat/permission"
|
|
60
|
+
require_relative "components/ai_chat/proposal"
|
|
61
|
+
require_relative "components/ai_chat/citation"
|
|
62
|
+
require_relative "components/ai_chat/welcome"
|
|
63
|
+
require_relative "components/ai_chat/attachments"
|
|
64
|
+
require_relative "components/ai_chat/slash_menu"
|
|
65
|
+
require_relative "components/ai_chat/action_bar"
|
|
66
|
+
require_relative "components/ai_chat/branch_picker"
|
|
41
67
|
require_relative "components/action_view_helpers"
|
|
42
68
|
# ActionView pulls in a partial Rails namespace, so this checks for the constant it
|
|
43
69
|
# actually needs rather than for Rails.
|
|
44
|
-
|
|
70
|
+
if defined?(Rails::Engine)
|
|
71
|
+
require_relative "components/engine"
|
|
72
|
+
require_relative "components/browser"
|
|
73
|
+
end
|
|
45
74
|
|
|
46
75
|
module Unmagic
|
|
47
76
|
module Components
|