@messenger-box/tailwind-ui-inbox 10.0.3-alpha.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.
- package/CHANGELOG.md +16 -0
- package/LICENSE +21 -0
- package/jest.config.js +9 -0
- package/lib/cdm-locales/en/translations.json +31 -0
- package/lib/cdm-locales/es/translations.json +31 -0
- package/lib/components/AIAgent/AIAgent.d.ts +32 -0
- package/lib/components/AIAgent/AIAgent.d.ts.map +1 -0
- package/lib/components/AIAgent/AIAgent.js +1135 -0
- package/lib/components/AIAgent/AIAgent.js.map +1 -0
- package/lib/components/AIAgent/InputComponent.d.ts +84 -0
- package/lib/components/AIAgent/InputComponent.d.ts.map +1 -0
- package/lib/components/AIAgent/InputComponent.js +417 -0
- package/lib/components/AIAgent/InputComponent.js.map +1 -0
- package/lib/components/AIAgent/index.d.ts +2 -0
- package/lib/components/AIAgent/index.d.ts.map +1 -0
- package/lib/components/InboxMessage/CommonMessage.d.ts +8 -0
- package/lib/components/InboxMessage/CommonMessage.d.ts.map +1 -0
- package/lib/components/InboxMessage/CommonMessage.js +35 -0
- package/lib/components/InboxMessage/CommonMessage.js.map +1 -0
- package/lib/components/InboxMessage/ConversationItem.d.ts +14 -0
- package/lib/components/InboxMessage/ConversationItem.d.ts.map +1 -0
- package/lib/components/InboxMessage/ConversationItem.js +200 -0
- package/lib/components/InboxMessage/ConversationItem.js.map +1 -0
- package/lib/components/InboxMessage/InputComponent.d.ts +20 -0
- package/lib/components/InboxMessage/InputComponent.d.ts.map +1 -0
- package/lib/components/InboxMessage/InputComponent.js +148 -0
- package/lib/components/InboxMessage/InputComponent.js.map +1 -0
- package/lib/components/InboxMessage/LeftSidebar.d.ts +20 -0
- package/lib/components/InboxMessage/LeftSidebar.d.ts.map +1 -0
- package/lib/components/InboxMessage/LeftSidebar.js +102 -0
- package/lib/components/InboxMessage/LeftSidebar.js.map +1 -0
- package/lib/components/InboxMessage/MessageInput.d.ts +9 -0
- package/lib/components/InboxMessage/MessageInput.d.ts.map +1 -0
- package/lib/components/InboxMessage/MessageInput.js +154 -0
- package/lib/components/InboxMessage/MessageInput.js.map +1 -0
- package/lib/components/InboxMessage/MessageInputComponent.d.ts +9 -0
- package/lib/components/InboxMessage/MessageInputComponent.d.ts.map +1 -0
- package/lib/components/InboxMessage/Messages.d.ts +17 -0
- package/lib/components/InboxMessage/Messages.d.ts.map +1 -0
- package/lib/components/InboxMessage/Messages.js +99 -0
- package/lib/components/InboxMessage/Messages.js.map +1 -0
- package/lib/components/InboxMessage/MessagesBuilderUi.d.ts +17 -0
- package/lib/components/InboxMessage/MessagesBuilderUi.d.ts.map +1 -0
- package/lib/components/InboxMessage/Popover.d.ts +3 -0
- package/lib/components/InboxMessage/Popover.d.ts.map +1 -0
- package/lib/components/InboxMessage/Popover.js +31 -0
- package/lib/components/InboxMessage/Popover.js.map +1 -0
- package/lib/components/InboxMessage/RightSidebar.d.ts +9 -0
- package/lib/components/InboxMessage/RightSidebar.d.ts.map +1 -0
- package/lib/components/InboxMessage/RightSidebar.js +9 -0
- package/lib/components/InboxMessage/RightSidebar.js.map +1 -0
- package/lib/components/InboxMessage/RightSidebarAi.d.ts +37 -0
- package/lib/components/InboxMessage/RightSidebarAi.d.ts.map +1 -0
- package/lib/components/InboxMessage/RightSidebarAi.js +9 -0
- package/lib/components/InboxMessage/RightSidebarAi.js.map +1 -0
- package/lib/components/InboxMessage/ServiceConversationItem.d.ts +12 -0
- package/lib/components/InboxMessage/ServiceConversationItem.d.ts.map +1 -0
- package/lib/components/InboxMessage/ServiceConversationItem.js +185 -0
- package/lib/components/InboxMessage/ServiceConversationItem.js.map +1 -0
- package/lib/components/InboxMessage/ServiceInboxItem.d.ts +12 -0
- package/lib/components/InboxMessage/ServiceInboxItem.d.ts.map +1 -0
- package/lib/components/InboxMessage/ServiceInboxItem.js +182 -0
- package/lib/components/InboxMessage/ServiceInboxItem.js.map +1 -0
- package/lib/components/InboxMessage/StreamingMessageBubble.d.ts +18 -0
- package/lib/components/InboxMessage/StreamingMessageBubble.d.ts.map +1 -0
- package/lib/components/InboxMessage/SubscriptionHandler.d.ts +19 -0
- package/lib/components/InboxMessage/SubscriptionHandler.d.ts.map +1 -0
- package/lib/components/InboxMessage/SubscriptionHandler.js +41 -0
- package/lib/components/InboxMessage/SubscriptionHandler.js.map +1 -0
- package/lib/components/InboxMessage/TypingIndicator.d.ts +11 -0
- package/lib/components/InboxMessage/TypingIndicator.d.ts.map +1 -0
- package/lib/components/InboxMessage/UploadImageButton.d.ts +7 -0
- package/lib/components/InboxMessage/UploadImageButton.d.ts.map +1 -0
- package/lib/components/InboxMessage/UploadImageButton.js +30 -0
- package/lib/components/InboxMessage/UploadImageButton.js.map +1 -0
- package/lib/components/InboxMessage/UserModalContent.d.ts +3 -0
- package/lib/components/InboxMessage/UserModalContent.d.ts.map +1 -0
- package/lib/components/InboxMessage/UserModalContent.js +60 -0
- package/lib/components/InboxMessage/UserModalContent.js.map +1 -0
- package/lib/components/InboxMessage/index.d.ts +19 -0
- package/lib/components/InboxMessage/index.d.ts.map +1 -0
- package/lib/components/InboxMessage/message-widgets/CommonMessage.d.ts +11 -0
- package/lib/components/InboxMessage/message-widgets/CommonMessage.d.ts.map +1 -0
- package/lib/components/InboxMessage/message-widgets/CommonMessage.js +44 -0
- package/lib/components/InboxMessage/message-widgets/CommonMessage.js.map +1 -0
- package/lib/components/InboxMessage/message-widgets/ErrorFixCard.d.ts +10 -0
- package/lib/components/InboxMessage/message-widgets/ErrorFixCard.d.ts.map +1 -0
- package/lib/components/InboxMessage/message-widgets/ErrorFixCard.js +194 -0
- package/lib/components/InboxMessage/message-widgets/ErrorFixCard.js.map +1 -0
- package/lib/components/InboxMessage/message-widgets/MessageCard.d.ts +8 -0
- package/lib/components/InboxMessage/message-widgets/MessageCard.d.ts.map +1 -0
- package/lib/components/InboxMessage/message-widgets/MessageSliceRenderer.d.ts +12 -0
- package/lib/components/InboxMessage/message-widgets/MessageSliceRenderer.d.ts.map +1 -0
- package/lib/components/InboxMessage/message-widgets/MessageSliceRenderer.js +37 -0
- package/lib/components/InboxMessage/message-widgets/MessageSliceRenderer.js.map +1 -0
- package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.d.ts +42 -0
- package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.d.ts.map +1 -0
- package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.js +1339 -0
- package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.js.map +1 -0
- package/lib/components/InboxMessage/message-widgets/PlainMessage.d.ts +8 -0
- package/lib/components/InboxMessage/message-widgets/PlainMessage.d.ts.map +1 -0
- package/lib/components/InboxMessage/message-widgets/PlainMessage.js +14 -0
- package/lib/components/InboxMessage/message-widgets/PlainMessage.js.map +1 -0
- package/lib/components/InboxMessage/message-widgets/PropertyMessageWidget.d.ts +9 -0
- package/lib/components/InboxMessage/message-widgets/PropertyMessageWidget.d.ts.map +1 -0
- package/lib/components/InboxMessage/message-widgets/SlackLikeMessageGroup.d.ts +14 -0
- package/lib/components/InboxMessage/message-widgets/SlackLikeMessageGroup.d.ts.map +1 -0
- package/lib/components/InboxMessage/message-widgets/SlackLikeMessageGroup.js +333 -0
- package/lib/components/InboxMessage/message-widgets/SlackLikeMessageGroup.js.map +1 -0
- package/lib/components/InboxMessage/message-widgets/index.d.ts +4 -0
- package/lib/components/InboxMessage/message-widgets/index.d.ts.map +1 -0
- package/lib/components/ModelConfigPanel.d.ts +74 -0
- package/lib/components/ModelConfigPanel.d.ts.map +1 -0
- package/lib/components/ModelConfigPanel.js +1152 -0
- package/lib/components/ModelConfigPanel.js.map +1 -0
- package/lib/components/filler-components/RightSiderBar.d.ts +3 -0
- package/lib/components/filler-components/RightSiderBar.d.ts.map +1 -0
- package/lib/components/filler-components/RightSiderBar.js +532 -0
- package/lib/components/filler-components/RightSiderBar.js.map +1 -0
- package/lib/components/inbox/FilesList.d.ts +20 -0
- package/lib/components/inbox/FilesList.d.ts.map +1 -0
- package/lib/components/inbox/FilesList.js +68 -0
- package/lib/components/inbox/FilesList.js.map +1 -0
- package/lib/components/inbox/MessageItem.d.ts +17 -0
- package/lib/components/inbox/MessageItem.d.ts.map +1 -0
- package/lib/components/inbox/MessageItem.js +50 -0
- package/lib/components/inbox/MessageItem.js.map +1 -0
- package/lib/components/inbox/ThreadItem.d.ts +11 -0
- package/lib/components/inbox/ThreadItem.d.ts.map +1 -0
- package/lib/components/inbox/ThreadItem.js +147 -0
- package/lib/components/inbox/ThreadItem.js.map +1 -0
- package/lib/components/inbox/index.d.ts +4 -0
- package/lib/components/inbox/index.d.ts.map +1 -0
- package/lib/components/index.d.ts +10 -0
- package/lib/components/index.d.ts.map +1 -0
- package/lib/components/live-code-editor/hybrid-live-editor.d.ts +20 -0
- package/lib/components/live-code-editor/hybrid-live-editor.d.ts.map +1 -0
- package/lib/components/live-code-editor/index.d.ts +4 -0
- package/lib/components/live-code-editor/index.d.ts.map +1 -0
- package/lib/components/live-code-editor/live-code-editor.d.ts +14 -0
- package/lib/components/live-code-editor/live-code-editor.d.ts.map +1 -0
- package/lib/components/messages-container-ui/MessagesContainerUI.d.ts +81 -0
- package/lib/components/messages-container-ui/MessagesContainerUI.d.ts.map +1 -0
- package/lib/components/messages-container-ui/MessagesContainerUI.js +77 -0
- package/lib/components/messages-container-ui/MessagesContainerUI.js.map +1 -0
- package/lib/components/messages-container-ui/PlanModeView.d.ts +82 -0
- package/lib/components/messages-container-ui/PlanModeView.d.ts.map +1 -0
- package/lib/components/messages-container-ui/PlanModeView.js +267 -0
- package/lib/components/messages-container-ui/PlanModeView.js.map +1 -0
- package/lib/components/messages-container-ui/index.d.ts +6 -0
- package/lib/components/messages-container-ui/index.d.ts.map +1 -0
- package/lib/components/messages-container-ui/types.d.ts +38 -0
- package/lib/components/messages-container-ui/types.d.ts.map +1 -0
- package/lib/components/slot-fill/chat-message-filler.d.ts +4 -0
- package/lib/components/slot-fill/chat-message-filler.d.ts.map +1 -0
- package/lib/components/slot-fill/chat-message-filler.js +5 -0
- package/lib/components/slot-fill/chat-message-filler.js.map +1 -0
- package/lib/components/slot-fill/chat-message-slot.d.ts +11 -0
- package/lib/components/slot-fill/chat-message-slot.d.ts.map +1 -0
- package/lib/components/slot-fill/chat-message-slot.js +6 -0
- package/lib/components/slot-fill/chat-message-slot.js.map +1 -0
- package/lib/components/slot-fill/index.d.ts +4 -0
- package/lib/components/slot-fill/index.d.ts.map +1 -0
- package/lib/components/slot-fill/right-sidebar-filler.d.ts +4 -0
- package/lib/components/slot-fill/right-sidebar-filler.d.ts.map +1 -0
- package/lib/components/slot-fill/right-sidebar-filler.js +13 -0
- package/lib/components/slot-fill/right-sidebar-filler.js.map +1 -0
- package/lib/components/ui/button.d.ts +9 -0
- package/lib/components/ui/button.d.ts.map +1 -0
- package/lib/compute.d.ts +8 -0
- package/lib/compute.d.ts.map +1 -0
- package/lib/compute.js +264 -0
- package/lib/compute.js.map +1 -0
- package/lib/config/env-config.d.ts +20 -0
- package/lib/config/env-config.d.ts.map +1 -0
- package/lib/config/env-config.js +55 -0
- package/lib/config/env-config.js.map +1 -0
- package/lib/config/index.d.ts +2 -0
- package/lib/config/index.d.ts.map +1 -0
- package/lib/constants/breakpoints.d.ts +8 -0
- package/lib/constants/breakpoints.d.ts.map +1 -0
- package/lib/constants/index.d.ts +3 -0
- package/lib/constants/index.d.ts.map +1 -0
- package/lib/container/AiInbox.d.ts +15 -0
- package/lib/container/AiInbox.d.ts.map +1 -0
- package/lib/container/AiInboxWithLoader.d.ts +36 -0
- package/lib/container/AiInboxWithLoader.d.ts.map +1 -0
- package/lib/container/AiLandingInput.d.ts +27 -0
- package/lib/container/AiLandingInput.d.ts.map +1 -0
- package/lib/container/AiLandingInput.js +149 -0
- package/lib/container/AiLandingInput.js.map +1 -0
- package/lib/container/Inbox.d.ts +15 -0
- package/lib/container/Inbox.d.ts.map +1 -0
- package/lib/container/Inbox.js +964 -0
- package/lib/container/Inbox.js.map +1 -0
- package/lib/container/InboxAiMessagesLoader.d.ts +45 -0
- package/lib/container/InboxAiMessagesLoader.d.ts.map +1 -0
- package/lib/container/InboxAiMessagesLoader.js +80 -0
- package/lib/container/InboxAiMessagesLoader.js.map +1 -0
- package/lib/container/InboxContainer.d.ts +41 -0
- package/lib/container/InboxContainer.d.ts.map +1 -0
- package/lib/container/InboxContainer.js +27 -0
- package/lib/container/InboxContainer.js.map +1 -0
- package/lib/container/InboxTemplate1.d.ts +15 -0
- package/lib/container/InboxTemplate1.d.ts.map +1 -0
- package/lib/container/InboxTemplate1WithLoader.d.ts +36 -0
- package/lib/container/InboxTemplate1WithLoader.d.ts.map +1 -0
- package/lib/container/InboxTemplate2.d.ts +15 -0
- package/lib/container/InboxTemplate2.d.ts.map +1 -0
- package/lib/container/InboxWithAiLoader.d.ts +47 -0
- package/lib/container/InboxWithAiLoader.d.ts.map +1 -0
- package/lib/container/InboxWithAiLoader.js +118 -0
- package/lib/container/InboxWithAiLoader.js.map +1 -0
- package/lib/container/InboxWithLoader.d.ts +36 -0
- package/lib/container/InboxWithLoader.d.ts.map +1 -0
- package/lib/container/InboxWithLoader.js +277 -0
- package/lib/container/InboxWithLoader.js.map +1 -0
- package/lib/container/ServiceInbox.d.ts +9 -0
- package/lib/container/ServiceInbox.d.ts.map +1 -0
- package/lib/container/ServiceInbox.js +141 -0
- package/lib/container/ServiceInbox.js.map +1 -0
- package/lib/container/TestInboxWithAiLoader.d.ts +7 -0
- package/lib/container/TestInboxWithAiLoader.d.ts.map +1 -0
- package/lib/container/TestInboxWithAiLoader.js +135 -0
- package/lib/container/TestInboxWithAiLoader.js.map +1 -0
- package/lib/container/ThreadMessages.d.ts +13 -0
- package/lib/container/ThreadMessages.d.ts.map +1 -0
- package/lib/container/ThreadMessages.js +320 -0
- package/lib/container/ThreadMessages.js.map +1 -0
- package/lib/container/ThreadMessagesInbox.d.ts +14 -0
- package/lib/container/ThreadMessagesInbox.d.ts.map +1 -0
- package/lib/container/ThreadMessagesInbox.js +347 -0
- package/lib/container/ThreadMessagesInbox.js.map +1 -0
- package/lib/container/Threads.d.ts +8 -0
- package/lib/container/Threads.d.ts.map +1 -0
- package/lib/container/Threads.js +231 -0
- package/lib/container/Threads.js.map +1 -0
- package/lib/container/ThreadsInbox.d.ts +21 -0
- package/lib/container/ThreadsInbox.d.ts.map +1 -0
- package/lib/container/ThreadsInbox.js +243 -0
- package/lib/container/ThreadsInbox.js.map +1 -0
- package/lib/container/apply-footer-styles.d.ts +2 -0
- package/lib/container/apply-footer-styles.d.ts.map +1 -0
- package/lib/container/apply-footer-styles.js +16 -0
- package/lib/container/apply-footer-styles.js.map +1 -0
- package/lib/container/index.d.ts +13 -0
- package/lib/container/index.d.ts.map +1 -0
- package/lib/enums/index.d.ts +2 -0
- package/lib/enums/index.d.ts.map +1 -0
- package/lib/enums/messenger-slot-fill-name-enum.d.ts +11 -0
- package/lib/enums/messenger-slot-fill-name-enum.d.ts.map +1 -0
- package/lib/enums/messenger-slot-fill-name-enum.js +11 -0
- package/lib/enums/messenger-slot-fill-name-enum.js.map +1 -0
- package/lib/hooks/index.d.ts +4 -0
- package/lib/hooks/index.d.ts.map +1 -0
- package/lib/hooks/usePersistentModelConfig.d.ts +33 -0
- package/lib/hooks/usePersistentModelConfig.d.ts.map +1 -0
- package/lib/hooks/usePersistentModelConfig.js +123 -0
- package/lib/hooks/usePersistentModelConfig.js.map +1 -0
- package/lib/hooks/useStreamAssembler.d.ts +8 -0
- package/lib/hooks/useStreamAssembler.d.ts.map +1 -0
- package/lib/hooks/useTemplates.d.ts +14 -0
- package/lib/hooks/useTemplates.d.ts.map +1 -0
- package/lib/hooks/useTemplates.js +59 -0
- package/lib/hooks/useTemplates.js.map +1 -0
- package/lib/index.d.ts +14 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -0
- package/lib/interfaces/index.d.ts +2 -0
- package/lib/interfaces/index.d.ts.map +1 -0
- package/lib/interfaces/message-widgets.interface.d.ts +21 -0
- package/lib/interfaces/message-widgets.interface.d.ts.map +1 -0
- package/lib/machines/aiAgentMachine.d.ts +3 -0
- package/lib/machines/aiAgentMachine.d.ts.map +1 -0
- package/lib/machines/aiAgentMachine.js +1083 -0
- package/lib/machines/aiAgentMachine.js.map +1 -0
- package/lib/machines/aiAgentMachine.simple.d.ts +3 -0
- package/lib/machines/aiAgentMachine.simple.d.ts.map +1 -0
- package/lib/machines/aiAgentMachine.simple.js +108 -0
- package/lib/machines/aiAgentMachine.simple.js.map +1 -0
- package/lib/machines/index.d.ts +3 -0
- package/lib/machines/index.d.ts.map +1 -0
- package/lib/machines/types.d.ts +77 -0
- package/lib/machines/types.d.ts.map +1 -0
- package/lib/module.d.ts +7 -0
- package/lib/module.d.ts.map +1 -0
- package/lib/module.js +26 -0
- package/lib/module.js.map +1 -0
- package/lib/routes.json +251 -0
- package/lib/styles/responsive.css +76 -0
- package/lib/templates/InboxWithAi.d.ts +44 -0
- package/lib/templates/InboxWithAi.d.ts.map +1 -0
- package/lib/templates/InboxWithAi.js +651 -0
- package/lib/templates/InboxWithAi.js.map +1 -0
- package/lib/templates/InboxWithAi.tsx +844 -0
- package/lib/templates/index.d.ts +2 -0
- package/lib/templates/index.d.ts.map +1 -0
- package/lib/templates/index.ts +1 -0
- package/lib/types/templates.d.ts +35 -0
- package/lib/types/templates.d.ts.map +1 -0
- package/lib/utils/utils.d.ts +2 -0
- package/lib/utils/utils.d.ts.map +1 -0
- package/lib/xstate/index.d.ts +3 -0
- package/lib/xstate/index.d.ts.map +1 -0
- package/lib/xstate/rightSidebar.machine.d.ts +4 -0
- package/lib/xstate/rightSidebar.machine.d.ts.map +1 -0
- package/lib/xstate/rightSidebar.types.d.ts +57 -0
- package/lib/xstate/rightSidebar.types.d.ts.map +1 -0
- package/package.json +69 -0
- package/rollup.config.mjs +47 -0
- package/src/cdm-locales/en/translations.json +31 -0
- package/src/cdm-locales/es/translations.json +31 -0
- package/src/components/AIAgent/AIAgent.tsx +1468 -0
- package/src/components/AIAgent/AIAgent.tsx.bk +1365 -0
- package/src/components/AIAgent/InputComponent.tsx +608 -0
- package/src/components/AIAgent/README.md +174 -0
- package/src/components/AIAgent/index.ts +1 -0
- package/src/components/InboxMessage/CommonMessage.tsx +40 -0
- package/src/components/InboxMessage/ConversationItem.tsx +255 -0
- package/src/components/InboxMessage/InputComponent.tsx +198 -0
- package/src/components/InboxMessage/LeftSidebar.tsx +140 -0
- package/src/components/InboxMessage/MessageInput.tsx +209 -0
- package/src/components/InboxMessage/MessageInputComponent.tsx +245 -0
- package/src/components/InboxMessage/Messages.tsx +137 -0
- package/src/components/InboxMessage/MessagesBuilderUi.tsx +205 -0
- package/src/components/InboxMessage/Popover.tsx +42 -0
- package/src/components/InboxMessage/RightSidebar.tsx +22 -0
- package/src/components/InboxMessage/RightSidebarAi.tsx +47 -0
- package/src/components/InboxMessage/ServiceConversationItem.tsx +234 -0
- package/src/components/InboxMessage/ServiceInboxItem.tsx +223 -0
- package/src/components/InboxMessage/StreamingMessageBubble.tsx +270 -0
- package/src/components/InboxMessage/SubscriptionHandler.tsx +55 -0
- package/src/components/InboxMessage/TypingIndicator.tsx +38 -0
- package/src/components/InboxMessage/UploadImageButton.tsx +46 -0
- package/src/components/InboxMessage/UserModalContent.tsx +60 -0
- package/src/components/InboxMessage/index.ts +18 -0
- package/src/components/InboxMessage/message-widgets/CommonMessage.tsx +69 -0
- package/src/components/InboxMessage/message-widgets/ErrorFixCard.tsx +239 -0
- package/src/components/InboxMessage/message-widgets/MessageCard.tsx +127 -0
- package/src/components/InboxMessage/message-widgets/MessageSliceRenderer.tsx +40 -0
- package/src/components/InboxMessage/message-widgets/ModernMessageGroup.tsx +1733 -0
- package/src/components/InboxMessage/message-widgets/PlainMessage.tsx +18 -0
- package/src/components/InboxMessage/message-widgets/PropertyMessageWidget.tsx +29 -0
- package/src/components/InboxMessage/message-widgets/SlackLikeMessageGroup.tsx +492 -0
- package/src/components/InboxMessage/message-widgets/index.ts +8 -0
- package/src/components/ModelConfigPanel.tsx +1357 -0
- package/src/components/filler-components/RightSiderBar.tsx +572 -0
- package/src/components/inbox/FilesList.tsx +89 -0
- package/src/components/inbox/MessageItem.tsx +50 -0
- package/src/components/inbox/ThreadItem.tsx +295 -0
- package/src/components/inbox/index.ts +3 -0
- package/src/components/index.ts +29 -0
- package/src/components/live-code-editor/hybrid-live-editor.tsx +105 -0
- package/src/components/live-code-editor/index.ts +3 -0
- package/src/components/live-code-editor/live-code-editor.tsx +257 -0
- package/src/components/messages-container-ui/MessagesContainerUI.tsx +151 -0
- package/src/components/messages-container-ui/PlanModeView.tsx +426 -0
- package/src/components/messages-container-ui/README.md +91 -0
- package/src/components/messages-container-ui/index.ts +5 -0
- package/src/components/messages-container-ui/types.ts +40 -0
- package/src/components/slot-fill/chat-message-filler.tsx +18 -0
- package/src/components/slot-fill/chat-message-slot.tsx +18 -0
- package/src/components/slot-fill/index.ts +3 -0
- package/src/components/slot-fill/right-sidebar-filler.tsx +48 -0
- package/src/components/ui/button.tsx +32 -0
- package/src/compute.ts +271 -0
- package/src/config/env-config.ts +24 -0
- package/src/config/index.ts +1 -0
- package/src/constants/breakpoints.ts +7 -0
- package/src/constants/index.ts +5 -0
- package/src/container/AiInbox.tsx +1879 -0
- package/src/container/AiInboxWithLoader.tsx +356 -0
- package/src/container/AiLandingInput.tsx +200 -0
- package/src/container/Inbox.tsx +1095 -0
- package/src/container/InboxAiMessagesLoader.tsx +129 -0
- package/src/container/InboxContainer.tsx +61 -0
- package/src/container/InboxTemplate1.tsx +1553 -0
- package/src/container/InboxTemplate1WithLoader.tsx +338 -0
- package/src/container/InboxTemplate2.tsx +1617 -0
- package/src/container/InboxWithAiLoader.tsx +177 -0
- package/src/container/InboxWithLoader.tsx +341 -0
- package/src/container/ServiceInbox.tsx +188 -0
- package/src/container/TestInboxWithAiLoader.tsx +147 -0
- package/src/container/ThreadMessages.tsx +378 -0
- package/src/container/ThreadMessagesInbox.tsx +457 -0
- package/src/container/Threads.tsx +270 -0
- package/src/container/ThreadsInbox.tsx +351 -0
- package/src/container/apply-footer-styles.ts +17 -0
- package/src/container/index.ts +31 -0
- package/src/enums/index.ts +1 -0
- package/src/enums/messenger-slot-fill-name-enum.ts +10 -0
- package/src/hooks/index.ts +3 -0
- package/src/hooks/usePersistentModelConfig.ts +166 -0
- package/src/hooks/useStreamAssembler.ts +7 -0
- package/src/hooks/useTemplates.ts +75 -0
- package/src/index.ts +49 -0
- package/src/interfaces/index.ts +1 -0
- package/src/interfaces/message-widgets.interface.ts +21 -0
- package/src/machines/aiAgentMachine.simple.ts +89 -0
- package/src/machines/aiAgentMachine.ts +1296 -0
- package/src/machines/aiAgentMachine.ts.bk +1296 -0
- package/src/machines/index.ts +2 -0
- package/src/machines/types.ts +59 -0
- package/src/module.tsx +32 -0
- package/src/styles/responsive.css +76 -0
- package/src/templates/InboxWithAi.tsx +844 -0
- package/src/templates/index.ts +1 -0
- package/src/types/templates.ts +35 -0
- package/src/utils/utils.ts +3 -0
- package/src/xstate/index.ts +2 -0
- package/src/xstate/rightSidebar.machine.ts +304 -0
- package/src/xstate/rightSidebar.types.ts +58 -0
- package/tsconfig.json +14 -0
- package/webpack.config.js +92 -0
|
@@ -0,0 +1,1733 @@
|
|
|
1
|
+
import React, { useEffect, useMemo } from 'react';
|
|
2
|
+
import { format, differenceInMinutes } from 'date-fns';
|
|
3
|
+
import { AiAgentMessageRole, IAuthUser, IPost } from 'common';
|
|
4
|
+
import { Slot } from '@common-stack/components-pro';
|
|
5
|
+
import { MessengerSlotFillNameEnum } from '../../../enums';
|
|
6
|
+
import { FilesList } from '../../inbox';
|
|
7
|
+
import { ErrorFixCard } from './ErrorFixCard';
|
|
8
|
+
import { marked } from 'marked';
|
|
9
|
+
import ReactMarkdown from 'react-markdown';
|
|
10
|
+
import remarkGfm from 'remark-gfm';
|
|
11
|
+
|
|
12
|
+
// Configure marked for better markdown rendering
|
|
13
|
+
marked.setOptions({
|
|
14
|
+
breaks: true, // Convert line breaks to <br>
|
|
15
|
+
gfm: true, // GitHub Flavored Markdown
|
|
16
|
+
headerIds: false, // Disable header IDs for cleaner HTML
|
|
17
|
+
mangle: false, // Don't mangle email addresses
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// Enhanced CSS styles for HTML content rendering with prettification
|
|
21
|
+
const htmlContentStyles = `
|
|
22
|
+
.html-content {
|
|
23
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
24
|
+
line-height: 1.6;
|
|
25
|
+
color: #374151;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.html-content h1, .html-content h2, .html-content h3, .html-content h4, .html-content h5, .html-content h6 {
|
|
29
|
+
margin-top: 1.5rem;
|
|
30
|
+
margin-bottom: 1rem;
|
|
31
|
+
font-weight: 600;
|
|
32
|
+
line-height: 1.25;
|
|
33
|
+
color: #111827;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.html-content h1 {
|
|
37
|
+
font-size: 1.875rem;
|
|
38
|
+
border-bottom: 2px solid #e5e7eb;
|
|
39
|
+
padding-bottom: 0.5rem;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.html-content h2 {
|
|
43
|
+
font-size: 1.5rem;
|
|
44
|
+
border-bottom: 1px solid #e5e7eb;
|
|
45
|
+
padding-bottom: 0.375rem;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.html-content h3 {
|
|
49
|
+
font-size: 1.25rem;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.html-content p {
|
|
53
|
+
margin-bottom: 1rem;
|
|
54
|
+
line-height: 1.7;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.html-content ul, .html-content ol {
|
|
58
|
+
margin-bottom: 1rem;
|
|
59
|
+
padding-left: 1.5rem;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.html-content ul {
|
|
63
|
+
list-style-type: disc;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.html-content ol {
|
|
67
|
+
list-style-type: decimal;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.html-content li {
|
|
71
|
+
margin-bottom: 0.5rem;
|
|
72
|
+
line-height: 1.6;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.html-content li > ul, .html-content li > ol {
|
|
76
|
+
margin-top: 0.5rem;
|
|
77
|
+
margin-bottom: 0.5rem;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.html-content a {
|
|
81
|
+
color: #2563eb;
|
|
82
|
+
text-decoration: none;
|
|
83
|
+
border-bottom: 1px solid transparent;
|
|
84
|
+
transition: all 0.2s ease;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.html-content a:hover {
|
|
88
|
+
color: #1d4ed8;
|
|
89
|
+
border-bottom-color: #1d4ed8;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.html-content strong, .html-content b {
|
|
93
|
+
font-weight: 600;
|
|
94
|
+
color: #111827;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.html-content em, .html-content i {
|
|
98
|
+
font-style: italic;
|
|
99
|
+
color: #6b7280;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.html-content code {
|
|
103
|
+
background-color: #f3f4f6;
|
|
104
|
+
padding: 0.25rem 0.5rem;
|
|
105
|
+
border-radius: 0.375rem;
|
|
106
|
+
font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace;
|
|
107
|
+
font-size: 0.875rem;
|
|
108
|
+
color: #dc2626;
|
|
109
|
+
border: 1px solid #e5e7eb;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.html-content pre {
|
|
113
|
+
background-color: #f9fafb;
|
|
114
|
+
padding: 1rem;
|
|
115
|
+
border-radius: 0.5rem;
|
|
116
|
+
overflow-x: auto;
|
|
117
|
+
font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace;
|
|
118
|
+
font-size: 0.875rem;
|
|
119
|
+
line-height: 1.5;
|
|
120
|
+
border: 1px solid #e5e7eb;
|
|
121
|
+
margin: 1.5rem 0;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.html-content pre code {
|
|
125
|
+
background-color: transparent;
|
|
126
|
+
padding: 0;
|
|
127
|
+
border: none;
|
|
128
|
+
color: #374151;
|
|
129
|
+
font-size: inherit;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.html-content blockquote {
|
|
133
|
+
border-left: 4px solid #3b82f6;
|
|
134
|
+
padding-left: 1rem;
|
|
135
|
+
margin: 1.5rem 0;
|
|
136
|
+
font-style: italic;
|
|
137
|
+
color: #6b7280;
|
|
138
|
+
background-color: #f8fafc;
|
|
139
|
+
padding: 1rem;
|
|
140
|
+
border-radius: 0.375rem;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.html-content blockquote p {
|
|
144
|
+
margin-bottom: 0;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.html-content table {
|
|
148
|
+
border-collapse: collapse;
|
|
149
|
+
border: 1px solid #e5e7eb;
|
|
150
|
+
width: 100%;
|
|
151
|
+
margin: 1.5rem 0;
|
|
152
|
+
border-radius: 0.5rem;
|
|
153
|
+
overflow: hidden;
|
|
154
|
+
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.html-content th,
|
|
158
|
+
.html-content td {
|
|
159
|
+
border: 1px solid #e5e7eb;
|
|
160
|
+
padding: 0.75rem;
|
|
161
|
+
text-align: left;
|
|
162
|
+
vertical-align: top;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.html-content th {
|
|
166
|
+
background-color: #f9fafb;
|
|
167
|
+
font-weight: 600;
|
|
168
|
+
color: #111827;
|
|
169
|
+
border-bottom: 2px solid #e5e7eb;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.html-content tr:nth-child(even) {
|
|
173
|
+
background-color: #f9fafb;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.html-content tr:hover {
|
|
177
|
+
background-color: #f3f4f6;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
.html-content hr {
|
|
181
|
+
border: none;
|
|
182
|
+
border-top: 2px solid #e5e7eb;
|
|
183
|
+
margin: 2rem 0;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.html-content img {
|
|
187
|
+
max-width: 100%;
|
|
188
|
+
height: auto;
|
|
189
|
+
border-radius: 0.5rem;
|
|
190
|
+
margin: 1rem 0;
|
|
191
|
+
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/* Enhanced message container styling */
|
|
195
|
+
.message-container {
|
|
196
|
+
background: #ffffff;
|
|
197
|
+
padding-top: 16px;
|
|
198
|
+
padding-bottom: 16px;
|
|
199
|
+
margin: 12px 0;
|
|
200
|
+
transition: all 0.2s ease-in-out;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/* If a message container is rendered *inside the user bubble* (Image 2),
|
|
204
|
+
* we must neutralize it so it doesn't override the pill background/padding.
|
|
205
|
+
*/
|
|
206
|
+
.user-message .message-container {
|
|
207
|
+
background: transparent !important;
|
|
208
|
+
padding-top: 0 !important;
|
|
209
|
+
padding-bottom: 0 !important;
|
|
210
|
+
margin: 0 !important;
|
|
211
|
+
transition: none !important;
|
|
212
|
+
box-shadow: none !important;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/* Markdown content styling */
|
|
216
|
+
.markdown-content {
|
|
217
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
218
|
+
line-height: 1.6;
|
|
219
|
+
color: #374151;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.markdown-content h1 {
|
|
223
|
+
font-size: 1.5rem;
|
|
224
|
+
font-weight: 700;
|
|
225
|
+
margin-top: 1.5rem;
|
|
226
|
+
margin-bottom: 1rem;
|
|
227
|
+
color: #111827;
|
|
228
|
+
padding-bottom: 0.5rem;
|
|
229
|
+
border-bottom: 2px solid #e5e7eb;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.markdown-content h2 {
|
|
233
|
+
font-size: 1.25rem;
|
|
234
|
+
font-weight: 600;
|
|
235
|
+
margin-top: 1.25rem;
|
|
236
|
+
margin-bottom: 0.75rem;
|
|
237
|
+
color: #1f2937;
|
|
238
|
+
padding-bottom: 0.375rem;
|
|
239
|
+
border-bottom: 1px solid #e5e7eb;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
.markdown-content h3 {
|
|
243
|
+
font-size: 1.125rem;
|
|
244
|
+
font-weight: 600;
|
|
245
|
+
margin-top: 1rem;
|
|
246
|
+
margin-bottom: 0.5rem;
|
|
247
|
+
color: #374151;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
.markdown-content p {
|
|
251
|
+
margin-bottom: 0.75rem;
|
|
252
|
+
line-height: 1.6;
|
|
253
|
+
color: #4b5563;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
.markdown-content ul, .markdown-content ol {
|
|
257
|
+
margin: 1rem 0;
|
|
258
|
+
padding-left: 1.5rem;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
.markdown-content li {
|
|
262
|
+
margin-bottom: 0.5rem;
|
|
263
|
+
line-height: 1.6;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.markdown-content code {
|
|
267
|
+
background-color: #f3f4f6;
|
|
268
|
+
padding: 0.125rem 0.375rem;
|
|
269
|
+
border-radius: 0.25rem;
|
|
270
|
+
font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace;
|
|
271
|
+
font-size: 0.875rem;
|
|
272
|
+
color: #dc2626;
|
|
273
|
+
border: 1px solid #e5e7eb;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.markdown-content pre {
|
|
277
|
+
color: #1f2937;
|
|
278
|
+
border-radius: 8px;
|
|
279
|
+
overflow-x: auto;
|
|
280
|
+
font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace;
|
|
281
|
+
font-size: 0.875rem;
|
|
282
|
+
line-height: 1.5;
|
|
283
|
+
margin: 1rem 0;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
.markdown-content pre code {
|
|
287
|
+
background-color: transparent;
|
|
288
|
+
padding: 0;
|
|
289
|
+
border: none;
|
|
290
|
+
color: #1f2937;
|
|
291
|
+
font-size: inherit;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
.markdown-content blockquote {
|
|
295
|
+
border-left: 4px solid #3b82f6;
|
|
296
|
+
background-color: #f8fafc;
|
|
297
|
+
padding: 1rem;
|
|
298
|
+
margin: 1rem 0;
|
|
299
|
+
border-radius: 6px;
|
|
300
|
+
font-style: italic;
|
|
301
|
+
color: #475569;
|
|
302
|
+
border-top: 1px solid #e5e7eb;
|
|
303
|
+
border-right: 1px solid #e5e7eb;
|
|
304
|
+
border-bottom: 1px solid #e5e7eb;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
.markdown-content table {
|
|
308
|
+
border-collapse: collapse;
|
|
309
|
+
border: 1px solid #e5e7eb;
|
|
310
|
+
width: 100%;
|
|
311
|
+
margin: 1rem 0;
|
|
312
|
+
border-radius: 8px;
|
|
313
|
+
overflow: hidden;
|
|
314
|
+
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
.markdown-content th {
|
|
318
|
+
background-color: #f9fafb;
|
|
319
|
+
color: #374151;
|
|
320
|
+
font-weight: 600;
|
|
321
|
+
padding: 0.75rem;
|
|
322
|
+
text-align: left;
|
|
323
|
+
border-bottom: 1px solid #e5e7eb;
|
|
324
|
+
font-size: 0.875rem;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
.markdown-content td {
|
|
328
|
+
border: 1px solid #e5e7eb;
|
|
329
|
+
padding: 0.75rem;
|
|
330
|
+
text-align: left;
|
|
331
|
+
vertical-align: top;
|
|
332
|
+
background-color: white;
|
|
333
|
+
font-size: 0.875rem;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
.markdown-content tr:nth-child(even) td {
|
|
337
|
+
background-color: #f9fafb;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
.markdown-content tr:hover td {
|
|
341
|
+
background-color: #f3f4f6;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/* Enhanced list styling */
|
|
345
|
+
.markdown-content ul li {
|
|
346
|
+
position: relative;
|
|
347
|
+
padding-left: 0.5rem;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
.markdown-content ul li::before {
|
|
351
|
+
content: "•";
|
|
352
|
+
color: #000;
|
|
353
|
+
font-weight: bold;
|
|
354
|
+
position: absolute;
|
|
355
|
+
left: -1rem;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
.markdown-content ol li {
|
|
359
|
+
position: relative;
|
|
360
|
+
padding-left: 0.5rem;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
.markdown-content ol li::before {
|
|
364
|
+
content: counter(list-item) ".";
|
|
365
|
+
font-weight: 600;
|
|
366
|
+
position: absolute;
|
|
367
|
+
left: -1.5rem;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/* Code block container styling */
|
|
371
|
+
.code-block-container {
|
|
372
|
+
background: #ffffff;
|
|
373
|
+
border: 1px solid #e5e7eb;
|
|
374
|
+
border-radius: 8px;
|
|
375
|
+
overflow: hidden;
|
|
376
|
+
margin: 1rem 0;
|
|
377
|
+
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
.code-block-header {
|
|
381
|
+
background: #f9fafb;
|
|
382
|
+
padding: 0.5rem 1rem;
|
|
383
|
+
border-bottom: 1px solid #e5e7eb;
|
|
384
|
+
font-weight: 600;
|
|
385
|
+
color: #374151;
|
|
386
|
+
font-size: 0.875rem;
|
|
387
|
+
text-transform: uppercase;
|
|
388
|
+
letter-spacing: 0.05em;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
.code-block-content {
|
|
392
|
+
padding: 1rem;
|
|
393
|
+
overflow-x: auto;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/* Enhanced prose styling for markdown content */
|
|
397
|
+
.prose pre {
|
|
398
|
+
background-color: #f9fafb;
|
|
399
|
+
border: 1px solid #e5e7eb;
|
|
400
|
+
border-radius: 0.5rem;
|
|
401
|
+
padding: 1rem;
|
|
402
|
+
overflow-x: auto;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
.prose code {
|
|
406
|
+
background-color: #f3f4f6;
|
|
407
|
+
padding: 0.125rem 0.375rem;
|
|
408
|
+
border-radius: 0.25rem;
|
|
409
|
+
font-size: 0.875rem;
|
|
410
|
+
border: 1px solid #e5e7eb;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
.prose pre code {
|
|
414
|
+
background-color: transparent;
|
|
415
|
+
padding: 0;
|
|
416
|
+
border: none;
|
|
417
|
+
color:#000;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
.hover\:bg-gray-100:hover {
|
|
421
|
+
background-color: transparent !important;
|
|
422
|
+
}
|
|
423
|
+
`;
|
|
424
|
+
|
|
425
|
+
// Hook to inject CSS styles
|
|
426
|
+
const useInjectStyles = () => {
|
|
427
|
+
useEffect(() => {
|
|
428
|
+
// Check if styles are already injected
|
|
429
|
+
if (document.getElementById('html-content-styles')) {
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// Create and inject style element
|
|
434
|
+
const styleElement = document.createElement('style');
|
|
435
|
+
styleElement.id = 'html-content-styles';
|
|
436
|
+
styleElement.textContent = htmlContentStyles;
|
|
437
|
+
document.head.appendChild(styleElement);
|
|
438
|
+
|
|
439
|
+
// Cleanup function
|
|
440
|
+
return () => {
|
|
441
|
+
const existingStyle = document.getElementById('html-content-styles');
|
|
442
|
+
if (existingStyle) {
|
|
443
|
+
existingStyle.remove();
|
|
444
|
+
}
|
|
445
|
+
};
|
|
446
|
+
}, []);
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
interface ModernMessageGroupProps {
|
|
450
|
+
showAuthorName?: boolean;
|
|
451
|
+
showAvatar?: boolean;
|
|
452
|
+
showTimestamp?: boolean;
|
|
453
|
+
messages: IPost[];
|
|
454
|
+
currentUser: IAuthUser;
|
|
455
|
+
onOpen: (element?: any) => void;
|
|
456
|
+
onMessageClick: (msg: IPost) => void;
|
|
457
|
+
isDesktopView?: boolean;
|
|
458
|
+
isSmallScreen?: boolean;
|
|
459
|
+
sandboxErrors?: any[];
|
|
460
|
+
currentFiles?: Record<string, string>;
|
|
461
|
+
onFixError?: (errorMessage: string) => Promise<void>;
|
|
462
|
+
onRecreateSandbox?: (fragmentId: string) => void;
|
|
463
|
+
/** When true, code blocks show copy and download actions in header and footer (default: true) */
|
|
464
|
+
showCopyDownloadOnCodeBlock?: boolean;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
interface MessageGroupProps {
|
|
468
|
+
showAuthorName?: boolean;
|
|
469
|
+
showAvatar?: boolean;
|
|
470
|
+
showTimestamp?: boolean;
|
|
471
|
+
author: any;
|
|
472
|
+
messages: IPost[];
|
|
473
|
+
currentUser: IAuthUser;
|
|
474
|
+
onOpen: (element?: any) => void;
|
|
475
|
+
onMessageClick: (msg: IPost) => void;
|
|
476
|
+
isDesktopView?: boolean;
|
|
477
|
+
isSmallScreen?: boolean;
|
|
478
|
+
showCopyDownloadOnCodeBlock?: boolean;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// Enhanced utility function to group messages by user and time
|
|
482
|
+
export const groupMessagesByUserAndTime = (messages: IPost[], timeThresholdMinutes = 5): IPost[][] => {
|
|
483
|
+
if (!messages || messages.length === 0) return [];
|
|
484
|
+
|
|
485
|
+
const groups: IPost[][] = [];
|
|
486
|
+
let currentGroup: IPost[] = [];
|
|
487
|
+
let lastMessage: IPost | null = null;
|
|
488
|
+
|
|
489
|
+
for (const message of messages) {
|
|
490
|
+
if (typeof message === 'string') continue; // Skip date separators
|
|
491
|
+
|
|
492
|
+
const shouldStartNewGroup =
|
|
493
|
+
!lastMessage ||
|
|
494
|
+
lastMessage.author?.id !== message.author?.id ||
|
|
495
|
+
differenceInMinutes(new Date(message.createdAt), new Date(lastMessage.createdAt)) > timeThresholdMinutes;
|
|
496
|
+
|
|
497
|
+
if (shouldStartNewGroup) {
|
|
498
|
+
if (currentGroup.length > 0) {
|
|
499
|
+
groups.push(currentGroup);
|
|
500
|
+
}
|
|
501
|
+
currentGroup = [message];
|
|
502
|
+
} else {
|
|
503
|
+
currentGroup.push(message);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
lastMessage = message;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
if (currentGroup.length > 0) {
|
|
510
|
+
groups.push(currentGroup);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
return groups;
|
|
514
|
+
};
|
|
515
|
+
|
|
516
|
+
// Enhanced content detection that properly identifies different content types
|
|
517
|
+
export const detectContentType = (value: string): 'html' | 'markdown' | 'code' | 'text' => {
|
|
518
|
+
if (!value) return 'text';
|
|
519
|
+
|
|
520
|
+
const trimmed = value.trim();
|
|
521
|
+
|
|
522
|
+
// Check for markdown patterns FIRST (before code, as markdown can contain code blocks)
|
|
523
|
+
const markdownPatterns = [
|
|
524
|
+
/^#{1,6}\s+.+$/gm, // Headers (must have text after #)
|
|
525
|
+
/^\s*[-*+]\s+.+$/gm, // Unordered lists
|
|
526
|
+
/^\s*\d+\.\s+.+$/gm, // Numbered lists
|
|
527
|
+
/\[.+?\]\(.+?\)/g, // Links
|
|
528
|
+
/!\[.+?\]\(.+?\)/g, // Images
|
|
529
|
+
/```[\s\S]*?```/g, // Fenced code blocks (markdown feature)
|
|
530
|
+
/^\s*>\s+/gm, // Blockquotes
|
|
531
|
+
/\*\*.*?\*\*/g, // Bold
|
|
532
|
+
/\*[^*].*?\*/g, // Italic (not just asterisks)
|
|
533
|
+
/^\s*\|.+\|/gm, // Tables
|
|
534
|
+
];
|
|
535
|
+
|
|
536
|
+
// Count markdown patterns - if we have multiple, it's likely markdown
|
|
537
|
+
const markdownMatches = markdownPatterns.filter((pattern) => {
|
|
538
|
+
const matches = trimmed.match(pattern);
|
|
539
|
+
return matches && matches.length > 0;
|
|
540
|
+
}).length;
|
|
541
|
+
|
|
542
|
+
// If we have 2+ markdown patterns, it's definitely markdown
|
|
543
|
+
if (markdownMatches >= 2) return 'markdown';
|
|
544
|
+
|
|
545
|
+
// If we have headers or lists, it's likely markdown
|
|
546
|
+
if (/^#{1,6}\s+/gm.test(trimmed) || /^\s*[-*+]\s+/gm.test(trimmed) || /^\s*\d+\.\s+/gm.test(trimmed)) {
|
|
547
|
+
return 'markdown';
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// Check for pure code patterns (without markdown context)
|
|
551
|
+
const pureCodePatterns = [
|
|
552
|
+
/^---\s*\w+\.\w+\s*---$/gm, // File separators (standalone)
|
|
553
|
+
/^@tailwind\s+\w+/gm, // Tailwind directives at start
|
|
554
|
+
/^export\s+default\s*\{/gm, // JS/TS exports at start
|
|
555
|
+
/^import\s+.*\s+from\s+['"]/gm, // Import statements at start
|
|
556
|
+
];
|
|
557
|
+
|
|
558
|
+
const hasPureCodePatterns = pureCodePatterns.some((pattern) => pattern.test(trimmed));
|
|
559
|
+
if (hasPureCodePatterns && markdownMatches === 0) return 'code';
|
|
560
|
+
|
|
561
|
+
// Check for HTML patterns
|
|
562
|
+
const htmlPatterns = [
|
|
563
|
+
/<[a-z][\s\S]*?>/i, // HTML tags
|
|
564
|
+
/&[a-z0-9#]+;/i, // HTML entities
|
|
565
|
+
/<[^>]+>[^<]*<\/[^>]+>/i, // HTML structure
|
|
566
|
+
];
|
|
567
|
+
|
|
568
|
+
const hasHtmlPatterns = htmlPatterns.some((pattern) => pattern.test(trimmed));
|
|
569
|
+
// Only return HTML if it's clearly HTML and not markdown
|
|
570
|
+
if (hasHtmlPatterns && markdownMatches === 0) return 'html';
|
|
571
|
+
|
|
572
|
+
// Default to text, but FormattedMessageContent will still try to parse as markdown
|
|
573
|
+
return 'text';
|
|
574
|
+
};
|
|
575
|
+
|
|
576
|
+
// Legacy function for backward compatibility
|
|
577
|
+
const isProbablyHTML = (value: string): boolean => {
|
|
578
|
+
return detectContentType(value) === 'html';
|
|
579
|
+
};
|
|
580
|
+
|
|
581
|
+
/** Map message type (THINKING, TOOLUSE, etc.) to slot fill name for Kimi-like streaming UI */
|
|
582
|
+
const getMessageSlotName = (type: string | undefined): string | null => {
|
|
583
|
+
if (!type) return null;
|
|
584
|
+
const t = String(type).toUpperCase();
|
|
585
|
+
if (t === 'THINKING') return MessengerSlotFillNameEnum.THINKING_NAME;
|
|
586
|
+
if (t === 'TOOLUSE') return MessengerSlotFillNameEnum.TOOLUSE_NAME;
|
|
587
|
+
if (t === 'CODEBLOCK') return MessengerSlotFillNameEnum.CODEBLOCK_NAME;
|
|
588
|
+
if (t === 'SERVICE') return MessengerSlotFillNameEnum.SERVICE_NAME;
|
|
589
|
+
if (t === 'AIASSISTANT' || t === 'ASSISTANT') return MessengerSlotFillNameEnum.AIASSISTANT_NAME;
|
|
590
|
+
return null;
|
|
591
|
+
};
|
|
592
|
+
|
|
593
|
+
// Enhanced sanitizer that allows most HTML tags while maintaining security
|
|
594
|
+
const sanitizeHtml = (html: string): string => {
|
|
595
|
+
try {
|
|
596
|
+
if (typeof window === 'undefined' || typeof window.DOMParser === 'undefined') {
|
|
597
|
+
return html
|
|
598
|
+
.replace(/<script[\s\S]*?>[\s\S]*?<\/script>/gi, '')
|
|
599
|
+
.replace(/<style[\s\S]*?>[\s\S]*?<\/style>/gi, '')
|
|
600
|
+
.replace(/on\w+\s*=\s*"[^"]*"/gi, '')
|
|
601
|
+
.replace(/on\w+\s*=\s*'[^']*'/gi, '')
|
|
602
|
+
.replace(/on\w+\s*=\s*[^\s>]+/gi, '')
|
|
603
|
+
.replace(/javascript:/gi, '')
|
|
604
|
+
.replace(/data:text\/html/gi, '');
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
const parser = new window.DOMParser();
|
|
608
|
+
const doc = parser.parseFromString(html, 'text/html');
|
|
609
|
+
|
|
610
|
+
// Only remove the most dangerous tags
|
|
611
|
+
const dangerousTags = ['script', 'style'];
|
|
612
|
+
dangerousTags.forEach((tag) => {
|
|
613
|
+
doc.querySelectorAll(tag).forEach((el) => el.remove());
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
const walker = doc.createTreeWalker(doc.body, (NodeFilter as any).SHOW_ELEMENT);
|
|
617
|
+
let node = walker.nextNode() as Element | null;
|
|
618
|
+
while (node) {
|
|
619
|
+
Array.from(node.attributes).forEach((attr) => {
|
|
620
|
+
const name = attr.name.toLowerCase();
|
|
621
|
+
const value = (attr.value || '').toLowerCase();
|
|
622
|
+
|
|
623
|
+
// Remove event handlers and javascript protocols
|
|
624
|
+
if (name.startsWith('on') || value.startsWith('javascript:')) {
|
|
625
|
+
node?.removeAttribute(attr.name);
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
// Allow most attributes but validate URLs for src/href
|
|
629
|
+
if ((name === 'src' || name === 'href') && !/^https?:|^\/.*/.test(attr.value)) {
|
|
630
|
+
node?.removeAttribute(attr.name);
|
|
631
|
+
}
|
|
632
|
+
});
|
|
633
|
+
node = walker.nextNode() as Element | null;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
return doc.body.innerHTML;
|
|
637
|
+
} catch (e) {
|
|
638
|
+
return html.replace(/<script[\s\S]*?>[\s\S]*?<\/script>/gi, '');
|
|
639
|
+
}
|
|
640
|
+
};
|
|
641
|
+
|
|
642
|
+
// Function to prettify HTML content
|
|
643
|
+
const prettifyHtmlContent = (htmlContent: string): string => {
|
|
644
|
+
// Add semantic classes for better styling
|
|
645
|
+
let prettified = htmlContent;
|
|
646
|
+
|
|
647
|
+
// Enhance headings with better styling
|
|
648
|
+
prettified = prettified.replace(/<h([1-6])>/g, '<h$1 class="html-heading html-heading-$1">');
|
|
649
|
+
|
|
650
|
+
// Enhance lists with better styling
|
|
651
|
+
prettified = prettified.replace(/<ul>/g, '<ul class="html-list html-list-unordered">');
|
|
652
|
+
prettified = prettified.replace(/<ol>/g, '<ol class="html-list html-list-ordered">');
|
|
653
|
+
|
|
654
|
+
// Enhance paragraphs with better spacing
|
|
655
|
+
prettified = prettified.replace(/<p>/g, '<p class="html-paragraph">');
|
|
656
|
+
|
|
657
|
+
// Enhance code blocks
|
|
658
|
+
prettified = prettified.replace(/<code>/g, '<code class="html-inline-code">');
|
|
659
|
+
prettified = prettified.replace(/<pre>/g, '<pre class="html-code-block">');
|
|
660
|
+
|
|
661
|
+
// Enhance blockquotes
|
|
662
|
+
prettified = prettified.replace(/<blockquote>/g, '<blockquote class="html-blockquote">');
|
|
663
|
+
|
|
664
|
+
// Enhance tables
|
|
665
|
+
prettified = prettified.replace(/<table>/g, '<table class="html-table">');
|
|
666
|
+
prettified = prettified.replace(/<th>/g, '<th class="html-table-header">');
|
|
667
|
+
prettified = prettified.replace(/<td>/g, '<td class="html-table-cell">');
|
|
668
|
+
|
|
669
|
+
// Add info boxes for certain content patterns
|
|
670
|
+
prettified = prettified.replace(
|
|
671
|
+
/<p>(Note|Tip|Info|Warning|Error):\s*(.*?)<\/p>/gi,
|
|
672
|
+
'<div class="html-info-box"><strong>$1:</strong> $2</div>',
|
|
673
|
+
);
|
|
674
|
+
|
|
675
|
+
return prettified;
|
|
676
|
+
};
|
|
677
|
+
|
|
678
|
+
// Minimal Builder-like blocks renderer (text/image/button/columns)
|
|
679
|
+
const BuilderLikeRenderer: React.FC<{ blocks?: any[] }> = ({ blocks }) => {
|
|
680
|
+
if (!blocks || !Array.isArray(blocks) || blocks.length === 0) return null;
|
|
681
|
+
|
|
682
|
+
return (
|
|
683
|
+
<div className="space-y-3">
|
|
684
|
+
{blocks.map((block, idx) => {
|
|
685
|
+
const type = block?.['@type'] || block?.type;
|
|
686
|
+
if (!type) return null;
|
|
687
|
+
|
|
688
|
+
if (/text/i.test(type)) {
|
|
689
|
+
const html = sanitizeHtml(block?.text || block?.data?.text || '');
|
|
690
|
+
return (
|
|
691
|
+
<div
|
|
692
|
+
key={idx}
|
|
693
|
+
className="max-w-none text-gray-800"
|
|
694
|
+
dangerouslySetInnerHTML={{ __html: html }}
|
|
695
|
+
/>
|
|
696
|
+
);
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
if (/image/i.test(type)) {
|
|
700
|
+
const src = block?.src || block?.image || block?.data?.src;
|
|
701
|
+
const alt = block?.alt || block?.data?.alt || '';
|
|
702
|
+
if (!src) return null;
|
|
703
|
+
return (
|
|
704
|
+
<div key={idx} className="rounded-lg overflow-hidden border border-gray-200">
|
|
705
|
+
<img className="w-full h-auto" src={src} alt={alt} />
|
|
706
|
+
</div>
|
|
707
|
+
);
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
if (/button/i.test(type)) {
|
|
711
|
+
const text = block?.text || block?.data?.text || 'Button';
|
|
712
|
+
const href = block?.href || block?.link || block?.data?.href || '#';
|
|
713
|
+
return (
|
|
714
|
+
<a
|
|
715
|
+
key={idx}
|
|
716
|
+
href={href}
|
|
717
|
+
target="_blank"
|
|
718
|
+
rel="noopener noreferrer"
|
|
719
|
+
className="inline-flex items-center px-3 py-1.5 rounded-md bg-blue-600 text-white text-sm hover:bg-blue-700"
|
|
720
|
+
>
|
|
721
|
+
{text}
|
|
722
|
+
</a>
|
|
723
|
+
);
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
if (/columns?/i.test(type)) {
|
|
727
|
+
const cols = block?.columns || block?.data?.columns || [];
|
|
728
|
+
return (
|
|
729
|
+
<div key={idx} className="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
|
730
|
+
{cols.map((col: any, colIdx: number) => (
|
|
731
|
+
<div key={colIdx} className="space-y-2">
|
|
732
|
+
<BuilderLikeRenderer blocks={col?.blocks || col?.children} />
|
|
733
|
+
</div>
|
|
734
|
+
))}
|
|
735
|
+
</div>
|
|
736
|
+
);
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
return (
|
|
740
|
+
<div key={idx} className="message-container">
|
|
741
|
+
<pre className="text-xs text-gray-600 overflow-x-auto">{JSON.stringify(block, null, 2)}</pre>
|
|
742
|
+
</div>
|
|
743
|
+
);
|
|
744
|
+
})}
|
|
745
|
+
</div>
|
|
746
|
+
);
|
|
747
|
+
};
|
|
748
|
+
|
|
749
|
+
// Code block with optional header (language + copy) and footer (copy + download) per Image 2
|
|
750
|
+
const CodeBlockWithActions: React.FC<{
|
|
751
|
+
content: string;
|
|
752
|
+
language?: string;
|
|
753
|
+
showCopyDownload?: boolean;
|
|
754
|
+
}> = ({ content, language, showCopyDownload = true }) => {
|
|
755
|
+
const [copied, setCopied] = React.useState(false);
|
|
756
|
+
|
|
757
|
+
const handleCopy = React.useCallback(() => {
|
|
758
|
+
navigator.clipboard.writeText(content).then(() => {
|
|
759
|
+
setCopied(true);
|
|
760
|
+
setTimeout(() => setCopied(false), 2000);
|
|
761
|
+
});
|
|
762
|
+
}, [content]);
|
|
763
|
+
|
|
764
|
+
const handleDownload = React.useCallback(() => {
|
|
765
|
+
const ext =
|
|
766
|
+
language === 'jsx' || language === 'tsx' ? '.tsx' : language === 'js' || language === 'ts' ? '.ts' : '.txt';
|
|
767
|
+
const blob = new Blob([content], { type: 'text/plain' });
|
|
768
|
+
const url = URL.createObjectURL(blob);
|
|
769
|
+
const a = document.createElement('a');
|
|
770
|
+
a.href = url;
|
|
771
|
+
a.download = `snippet${ext}`;
|
|
772
|
+
a.click();
|
|
773
|
+
URL.revokeObjectURL(url);
|
|
774
|
+
}, [content, language]);
|
|
775
|
+
|
|
776
|
+
const langLabel = (language || 'code').toUpperCase();
|
|
777
|
+
|
|
778
|
+
/**
|
|
779
|
+
* Duplicate / copy: back square offset top-left (only its top-left shows behind),
|
|
780
|
+
* front square on top toward bottom-right — matches typical “two sheets” reference art.
|
|
781
|
+
*/
|
|
782
|
+
const IconCopy = ({ className }: { className?: string }) => (
|
|
783
|
+
<svg className={className} viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden>
|
|
784
|
+
<rect
|
|
785
|
+
x="3.25"
|
|
786
|
+
y="3.25"
|
|
787
|
+
width="10.5"
|
|
788
|
+
height="10.5"
|
|
789
|
+
rx="2.25"
|
|
790
|
+
ry="2.25"
|
|
791
|
+
stroke="currentColor"
|
|
792
|
+
strokeWidth="1.5"
|
|
793
|
+
strokeLinecap="round"
|
|
794
|
+
strokeLinejoin="round"
|
|
795
|
+
/>
|
|
796
|
+
<rect
|
|
797
|
+
x="8.75"
|
|
798
|
+
y="8.75"
|
|
799
|
+
width="10.5"
|
|
800
|
+
height="10.5"
|
|
801
|
+
rx="2.25"
|
|
802
|
+
ry="2.25"
|
|
803
|
+
stroke="currentColor"
|
|
804
|
+
strokeWidth="1.5"
|
|
805
|
+
strokeLinecap="round"
|
|
806
|
+
strokeLinejoin="round"
|
|
807
|
+
/>
|
|
808
|
+
</svg>
|
|
809
|
+
);
|
|
810
|
+
|
|
811
|
+
/** Arrow into tray (download) */
|
|
812
|
+
const IconDownload = ({ className }: { className?: string }) => (
|
|
813
|
+
<svg
|
|
814
|
+
className={className}
|
|
815
|
+
viewBox="0 0 24 24"
|
|
816
|
+
fill="none"
|
|
817
|
+
stroke="currentColor"
|
|
818
|
+
strokeWidth={1.5}
|
|
819
|
+
strokeLinecap="round"
|
|
820
|
+
strokeLinejoin="round"
|
|
821
|
+
aria-hidden
|
|
822
|
+
>
|
|
823
|
+
<path d="M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5M16.5 12L12 16.5m0 0L7.5 12m4.5 4.5V3" />
|
|
824
|
+
</svg>
|
|
825
|
+
);
|
|
826
|
+
|
|
827
|
+
const IconCheck = ({ className }: { className?: string }) => (
|
|
828
|
+
<svg
|
|
829
|
+
className={className}
|
|
830
|
+
viewBox="0 0 24 24"
|
|
831
|
+
fill="none"
|
|
832
|
+
stroke="currentColor"
|
|
833
|
+
strokeWidth={1.75}
|
|
834
|
+
strokeLinecap="round"
|
|
835
|
+
strokeLinejoin="round"
|
|
836
|
+
aria-hidden
|
|
837
|
+
>
|
|
838
|
+
<path d="M5 13l4 4L19 7" />
|
|
839
|
+
</svg>
|
|
840
|
+
);
|
|
841
|
+
|
|
842
|
+
const iconMuted = 'text-slate-500 hover:text-slate-600';
|
|
843
|
+
|
|
844
|
+
return (
|
|
845
|
+
<div className="rounded-xl bg-white overflow-hidden my-3 shadow-sm ring-1 ring-slate-200/60">
|
|
846
|
+
{/* Header: label + copy — padding aligned with inner code gutter (image 3) */}
|
|
847
|
+
<div className="flex items-center justify-between px-4 pt-4 pb-3 bg-white">
|
|
848
|
+
<span className="text-[11px] font-semibold uppercase tracking-wide text-slate-500">{langLabel}</span>
|
|
849
|
+
<button
|
|
850
|
+
type="button"
|
|
851
|
+
onClick={handleCopy}
|
|
852
|
+
className={`p-1 rounded-md hover:bg-slate-100/80 transition-colors ${iconMuted}`}
|
|
853
|
+
title="Copy"
|
|
854
|
+
aria-label="Copy to clipboard"
|
|
855
|
+
>
|
|
856
|
+
<IconCopy className="w-[15px] h-[15px] shrink-0" />
|
|
857
|
+
</button>
|
|
858
|
+
</div>
|
|
859
|
+
{/* Code body: white, no inner border (single flat surface inside the card) */}
|
|
860
|
+
<div className="px-4 pb-4">
|
|
861
|
+
<div className="overflow-x-auto bg-white py-4">
|
|
862
|
+
<pre className="text-sm text-slate-900 font-mono leading-relaxed whitespace-pre-wrap break-words m-0 p-0 border-0 bg-transparent">
|
|
863
|
+
<code className="bg-transparent">{content}</code>
|
|
864
|
+
</pre>
|
|
865
|
+
</div>
|
|
866
|
+
</div>
|
|
867
|
+
{showCopyDownload && (
|
|
868
|
+
<div className="flex items-center justify-start gap-0.5 px-4 pb-3 pt-0 mt-2 bg-white">
|
|
869
|
+
<button
|
|
870
|
+
type="button"
|
|
871
|
+
onClick={handleCopy}
|
|
872
|
+
className={`p-1 rounded-md hover:bg-slate-100/80 transition-colors ${iconMuted}`}
|
|
873
|
+
title={copied ? 'Copied' : 'Copy'}
|
|
874
|
+
aria-label={copied ? 'Copied' : 'Copy to clipboard'}
|
|
875
|
+
>
|
|
876
|
+
{copied ? (
|
|
877
|
+
<IconCheck className="w-[15px] h-[15px] text-emerald-600" />
|
|
878
|
+
) : (
|
|
879
|
+
<IconCopy className="w-[15px] h-[15px] shrink-0" />
|
|
880
|
+
)}
|
|
881
|
+
</button>
|
|
882
|
+
<button
|
|
883
|
+
type="button"
|
|
884
|
+
onClick={handleDownload}
|
|
885
|
+
className={`p-1 rounded-md hover:bg-slate-100/80 transition-colors ${iconMuted}`}
|
|
886
|
+
title="Download"
|
|
887
|
+
aria-label="Download"
|
|
888
|
+
>
|
|
889
|
+
<IconDownload className="w-[15px] h-[15px] shrink-0" />
|
|
890
|
+
</button>
|
|
891
|
+
</div>
|
|
892
|
+
)}
|
|
893
|
+
</div>
|
|
894
|
+
);
|
|
895
|
+
};
|
|
896
|
+
|
|
897
|
+
// Infer language from file extension or content
|
|
898
|
+
const inferCodeLanguage = (section: string, filename?: string): string => {
|
|
899
|
+
if (filename) {
|
|
900
|
+
const ext = filename.split('.').pop()?.toLowerCase();
|
|
901
|
+
const map: Record<string, string> = {
|
|
902
|
+
tsx: 'tsx',
|
|
903
|
+
jsx: 'jsx',
|
|
904
|
+
ts: 'ts',
|
|
905
|
+
js: 'js',
|
|
906
|
+
css: 'css',
|
|
907
|
+
html: 'html',
|
|
908
|
+
json: 'json',
|
|
909
|
+
};
|
|
910
|
+
return map[ext ?? ''] ?? ext ?? 'code';
|
|
911
|
+
}
|
|
912
|
+
if (/\bimport\b.*from\s+['"]react['"]/i.test(section) || /<\w+[\s>]/.test(section)) return 'jsx';
|
|
913
|
+
if (/\bimport\b/.test(section) && /\.(ts|tsx)\b/.test(section)) return 'ts';
|
|
914
|
+
return 'code';
|
|
915
|
+
};
|
|
916
|
+
|
|
917
|
+
// Enhanced code formatter for raw code content
|
|
918
|
+
export const CodeFormatter: React.FC<{
|
|
919
|
+
content: string;
|
|
920
|
+
showCopyDownload?: boolean;
|
|
921
|
+
variant?: 'default' | 'user';
|
|
922
|
+
/** When variant is `user`, use primary-foreground only on plan-mode bubbles (bg-primary). */
|
|
923
|
+
planMode?: boolean;
|
|
924
|
+
}> = ({ content, showCopyDownload = true, variant = 'default', planMode = false }) => {
|
|
925
|
+
if (!content) return null;
|
|
926
|
+
|
|
927
|
+
// Split content by file separators and format each section
|
|
928
|
+
const sections = content.split(/(---\s*\w+\.\w+\s*---)/g);
|
|
929
|
+
|
|
930
|
+
return (
|
|
931
|
+
<div className={variant === 'user' ? '' : 'message-container'}>
|
|
932
|
+
<div className="space-y-4">
|
|
933
|
+
{sections.map((section, index) => {
|
|
934
|
+
if (!section.trim()) return null;
|
|
935
|
+
|
|
936
|
+
// Check if this is a file separator
|
|
937
|
+
const fileMatch = section.match(/---\s*(\w+\.\w+)\s*---/);
|
|
938
|
+
if (fileMatch) {
|
|
939
|
+
return (
|
|
940
|
+
<div key={index} className="code-block-container">
|
|
941
|
+
<div className="code-block-header">📄 {fileMatch[1]}</div>
|
|
942
|
+
</div>
|
|
943
|
+
);
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
// Format the code content
|
|
947
|
+
const lines = section.trim().split('\n');
|
|
948
|
+
const isCode = lines.some(
|
|
949
|
+
(line) =>
|
|
950
|
+
line.includes('@tailwind') ||
|
|
951
|
+
line.includes('export default') ||
|
|
952
|
+
line.includes('import ') ||
|
|
953
|
+
line.includes('{') ||
|
|
954
|
+
line.includes('}') ||
|
|
955
|
+
line.includes('//') ||
|
|
956
|
+
line.includes('/*'),
|
|
957
|
+
);
|
|
958
|
+
|
|
959
|
+
if (isCode) {
|
|
960
|
+
const lang = inferCodeLanguage(section);
|
|
961
|
+
return (
|
|
962
|
+
<CodeBlockWithActions
|
|
963
|
+
key={index}
|
|
964
|
+
content={section.trim()}
|
|
965
|
+
language={lang}
|
|
966
|
+
showCopyDownload={showCopyDownload}
|
|
967
|
+
/>
|
|
968
|
+
);
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
// Regular text content
|
|
972
|
+
return (
|
|
973
|
+
<div
|
|
974
|
+
key={index}
|
|
975
|
+
className={`text-sm ${
|
|
976
|
+
variant === 'user'
|
|
977
|
+
? planMode
|
|
978
|
+
? 'text-primary-foreground'
|
|
979
|
+
: 'text-gray-900'
|
|
980
|
+
: 'text-gray-700'
|
|
981
|
+
} leading-relaxed whitespace-pre-wrap break-words`}
|
|
982
|
+
>
|
|
983
|
+
{section.trim()}
|
|
984
|
+
</div>
|
|
985
|
+
);
|
|
986
|
+
})}
|
|
987
|
+
</div>
|
|
988
|
+
</div>
|
|
989
|
+
);
|
|
990
|
+
};
|
|
991
|
+
|
|
992
|
+
// Enhanced markdown renderer using marked (similar to MessageBubble.tsx)
|
|
993
|
+
export const FormattedMessageContent: React.FC<{
|
|
994
|
+
content: string;
|
|
995
|
+
variant?: 'default' | 'user';
|
|
996
|
+
/** When variant is `user`, use primary-foreground only on plan-mode bubbles (bg-primary). */
|
|
997
|
+
planMode?: boolean;
|
|
998
|
+
/**
|
|
999
|
+
* When true, fenced code blocks use the same copy/download UI as elsewhere.
|
|
1000
|
+
* Implemented via react-markdown (safe) instead of raw HTML from marked.
|
|
1001
|
+
*/
|
|
1002
|
+
showCopyDownloadOnCodeBlock?: boolean;
|
|
1003
|
+
}> = ({ content, variant = 'default', planMode = false, showCopyDownloadOnCodeBlock = false }) => {
|
|
1004
|
+
if (!content) return null;
|
|
1005
|
+
|
|
1006
|
+
// Parse markdown using marked - be more aggressive in detection
|
|
1007
|
+
const { htmlContent, markdownSource } = useMemo(() => {
|
|
1008
|
+
try {
|
|
1009
|
+
const trimmed = content.trim();
|
|
1010
|
+
|
|
1011
|
+
// Comprehensive markdown detection patterns
|
|
1012
|
+
const markdownIndicators = [
|
|
1013
|
+
/^#{1,6}\s+.+$/gm, // Headers with text
|
|
1014
|
+
/^\s*[-*+]\s+.+$/gm, // Unordered lists
|
|
1015
|
+
/^\s*\d+\.\s+.+$/gm, // Numbered lists
|
|
1016
|
+
/\[.+?\]\(.+?\)/g, // Links
|
|
1017
|
+
/!\[.+?\]\(.+?\)/g, // Images
|
|
1018
|
+
/```[\s\S]*?```/g, // Fenced code blocks
|
|
1019
|
+
/^\s*>\s+/gm, // Blockquotes
|
|
1020
|
+
/\*\*[^*]+\*\*/g, // Bold text
|
|
1021
|
+
/\*[^*\n]+\*/g, // Italic text (not just asterisks)
|
|
1022
|
+
/^\s*\|.+\|/gm, // Tables
|
|
1023
|
+
/~~.+~~/g, // Strikethrough
|
|
1024
|
+
];
|
|
1025
|
+
|
|
1026
|
+
// Check if content has markdown patterns
|
|
1027
|
+
const hasMarkdown = markdownIndicators.some((pattern) => {
|
|
1028
|
+
const matches = trimmed.match(pattern);
|
|
1029
|
+
return matches && matches.length > 0;
|
|
1030
|
+
});
|
|
1031
|
+
|
|
1032
|
+
// Also check for common markdown characters that suggest markdown content
|
|
1033
|
+
const hasMarkdownChars = /[#*`>|\[\]()]/.test(trimmed) && trimmed.length > 10;
|
|
1034
|
+
|
|
1035
|
+
// If we detect markdown, parse it
|
|
1036
|
+
if (hasMarkdown || hasMarkdownChars) {
|
|
1037
|
+
const parsed = marked.parse(trimmed, { async: false }) as string;
|
|
1038
|
+
// Only return if parsing actually produced HTML (not just the same text)
|
|
1039
|
+
if (parsed && parsed.trim() !== trimmed && parsed.includes('<')) {
|
|
1040
|
+
return { htmlContent: parsed, markdownSource: trimmed as string };
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
return { htmlContent: null as string | null, markdownSource: null as string | null };
|
|
1045
|
+
} catch (error) {
|
|
1046
|
+
console.error('Error parsing markdown:', error);
|
|
1047
|
+
return { htmlContent: null as string | null, markdownSource: null as string | null };
|
|
1048
|
+
}
|
|
1049
|
+
}, [content]);
|
|
1050
|
+
|
|
1051
|
+
const proseMarkdownClasses = `text-sm prose prose-sm max-w-none leading-relaxed break-words
|
|
1052
|
+
prose-p:my-2 prose-p:text-gray-700
|
|
1053
|
+
prose-pre:my-2 prose-pre:bg-transparent prose-pre:border-0 prose-pre:p-0
|
|
1054
|
+
prose-ul:my-2 prose-ol:my-2
|
|
1055
|
+
prose-code:break-words prose-code:bg-gray-50 prose-code:px-1.5 prose-code:py-0.5 prose-code:rounded prose-code:text-sm prose-code:font-mono
|
|
1056
|
+
prose-headings:font-semibold prose-headings:text-gray-900 prose-headings:mt-4 prose-headings:mb-2
|
|
1057
|
+
prose-h1:text-2xl prose-h1:border-b prose-h1:border-gray-200 prose-h1:pb-2
|
|
1058
|
+
prose-h2:text-xl prose-h2:border-b prose-h2:border-gray-200 prose-h2:pb-1
|
|
1059
|
+
prose-h3:text-lg
|
|
1060
|
+
prose-h4:text-base
|
|
1061
|
+
prose-a:text-blue-600 prose-a:no-underline hover:prose-a:underline
|
|
1062
|
+
prose-strong:font-semibold prose-strong:text-gray-900
|
|
1063
|
+
prose-em:text-gray-600
|
|
1064
|
+
prose-blockquote:border-l-4 prose-blockquote:border-blue-400 prose-blockquote:pl-4 prose-blockquote:py-2 prose-blockquote:bg-blue-50 prose-blockquote:rounded-r-md prose-blockquote:my-3 prose-blockquote:italic
|
|
1065
|
+
prose-table:w-full prose-table:border prose-table:border-gray-200 prose-table:rounded-md prose-table:my-4
|
|
1066
|
+
prose-th:px-3 prose-th:py-2 prose-th:text-left prose-th:font-semibold prose-th:bg-gray-50 prose-th:border prose-th:border-gray-200
|
|
1067
|
+
prose-td:px-3 prose-td:py-2 prose-td:border prose-td:border-gray-200 prose-td:text-gray-700
|
|
1068
|
+
prose-li:my-1 prose-li:text-gray-700
|
|
1069
|
+
prose-hr:my-4 prose-hr:border-gray-200
|
|
1070
|
+
${
|
|
1071
|
+
variant === 'user'
|
|
1072
|
+
? planMode
|
|
1073
|
+
? '!prose-p:text-primary-foreground !prose-li:text-primary-foreground !prose-strong:text-primary-foreground !prose-em:text-primary-foreground !prose-a:text-primary-foreground'
|
|
1074
|
+
: '!prose-p:text-gray-900 !prose-li:text-gray-900 !prose-strong:text-gray-900 !prose-em:text-gray-700 !prose-a:text-blue-600'
|
|
1075
|
+
: ''
|
|
1076
|
+
}`;
|
|
1077
|
+
|
|
1078
|
+
const markdownComponents = useMemo(
|
|
1079
|
+
() => ({
|
|
1080
|
+
pre: ({ children }: { children?: React.ReactNode }) => <>{children}</>,
|
|
1081
|
+
code: ({ className, children, ...rest }: { className?: string; children?: React.ReactNode }) => {
|
|
1082
|
+
const match = /language-(\w+)/.exec(className || '');
|
|
1083
|
+
const text = String(children).replace(/\n$/, '');
|
|
1084
|
+
if (match) {
|
|
1085
|
+
return (
|
|
1086
|
+
<div className="not-prose max-w-none">
|
|
1087
|
+
<CodeBlockWithActions
|
|
1088
|
+
content={text}
|
|
1089
|
+
language={match[1]}
|
|
1090
|
+
showCopyDownload={showCopyDownloadOnCodeBlock}
|
|
1091
|
+
/>
|
|
1092
|
+
</div>
|
|
1093
|
+
);
|
|
1094
|
+
}
|
|
1095
|
+
if (text.includes('\n')) {
|
|
1096
|
+
return (
|
|
1097
|
+
<div className="not-prose max-w-none">
|
|
1098
|
+
<CodeBlockWithActions
|
|
1099
|
+
content={text}
|
|
1100
|
+
language="code"
|
|
1101
|
+
showCopyDownload={showCopyDownloadOnCodeBlock}
|
|
1102
|
+
/>
|
|
1103
|
+
</div>
|
|
1104
|
+
);
|
|
1105
|
+
}
|
|
1106
|
+
return (
|
|
1107
|
+
<code
|
|
1108
|
+
className={`rounded px-1.5 py-0.5 text-sm font-mono bg-gray-50 break-words ${className || ''}`}
|
|
1109
|
+
{...rest}
|
|
1110
|
+
>
|
|
1111
|
+
{children}
|
|
1112
|
+
</code>
|
|
1113
|
+
);
|
|
1114
|
+
},
|
|
1115
|
+
}),
|
|
1116
|
+
[showCopyDownloadOnCodeBlock],
|
|
1117
|
+
);
|
|
1118
|
+
|
|
1119
|
+
// Fenced code copy/download needs React tree (not marked HTML)
|
|
1120
|
+
if (showCopyDownloadOnCodeBlock && markdownSource) {
|
|
1121
|
+
return (
|
|
1122
|
+
<div className={variant === 'user' ? '' : 'message-container'}>
|
|
1123
|
+
<div className={`${proseMarkdownClasses}`}>
|
|
1124
|
+
<ReactMarkdown remarkPlugins={[remarkGfm]} components={markdownComponents}>
|
|
1125
|
+
{markdownSource}
|
|
1126
|
+
</ReactMarkdown>
|
|
1127
|
+
</div>
|
|
1128
|
+
</div>
|
|
1129
|
+
);
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
// If markdown was parsed, render as HTML with prose styling
|
|
1133
|
+
if (htmlContent) {
|
|
1134
|
+
return (
|
|
1135
|
+
<div className={variant === 'user' ? '' : 'message-container'}>
|
|
1136
|
+
<div
|
|
1137
|
+
className={`text-sm prose prose-sm max-w-none leading-relaxed break-words
|
|
1138
|
+
prose-p:my-2 prose-p:text-gray-700
|
|
1139
|
+
prose-pre:my-2 prose-pre:bg-gray-50 prose-pre:border prose-pre:border-gray-200 prose-pre:rounded prose-pre:p-3
|
|
1140
|
+
prose-ul:my-2 prose-ol:my-2
|
|
1141
|
+
prose-pre:whitespace-pre-wrap prose-pre:break-words prose-pre:overflow-x-auto
|
|
1142
|
+
prose-code:break-words prose-code:bg-gray-50 prose-code:px-1.5 prose-code:py-0.5 prose-code:rounded prose-code:text-sm prose-code:font-mono
|
|
1143
|
+
prose-headings:font-semibold prose-headings:text-gray-900 prose-headings:mt-4 prose-headings:mb-2
|
|
1144
|
+
prose-h1:text-2xl prose-h1:border-b prose-h1:border-gray-200 prose-h1:pb-2
|
|
1145
|
+
prose-h2:text-xl prose-h2:border-b prose-h2:border-gray-200 prose-h2:pb-1
|
|
1146
|
+
prose-h3:text-lg
|
|
1147
|
+
prose-h4:text-base
|
|
1148
|
+
prose-a:text-blue-600 prose-a:no-underline hover:prose-a:underline
|
|
1149
|
+
prose-strong:font-semibold prose-strong:text-gray-900
|
|
1150
|
+
prose-em:text-gray-600
|
|
1151
|
+
prose-blockquote:border-l-4 prose-blockquote:border-blue-400 prose-blockquote:pl-4 prose-blockquote:py-2 prose-blockquote:bg-blue-50 prose-blockquote:rounded-r-md prose-blockquote:my-3 prose-blockquote:italic
|
|
1152
|
+
prose-table:w-full prose-table:border prose-table:border-gray-200 prose-table:rounded-md prose-table:my-4
|
|
1153
|
+
prose-th:px-3 prose-th:py-2 prose-th:text-left prose-th:font-semibold prose-th:bg-gray-50 prose-th:border prose-th:border-gray-200
|
|
1154
|
+
prose-td:px-3 prose-td:py-2 prose-td:border prose-td:border-gray-200 prose-td:text-gray-700
|
|
1155
|
+
prose-li:my-1 prose-li:text-gray-700
|
|
1156
|
+
prose-hr:my-4 prose-hr:border-gray-200
|
|
1157
|
+
${
|
|
1158
|
+
variant === 'user'
|
|
1159
|
+
? planMode
|
|
1160
|
+
? '!prose-p:text-primary-foreground !prose-li:text-primary-foreground !prose-strong:text-primary-foreground !prose-em:text-primary-foreground !prose-a:text-primary-foreground'
|
|
1161
|
+
: '!prose-p:text-gray-900 !prose-li:text-gray-900 !prose-strong:text-gray-900 !prose-em:text-gray-700 !prose-a:text-blue-600'
|
|
1162
|
+
: ''
|
|
1163
|
+
}`}
|
|
1164
|
+
dangerouslySetInnerHTML={{ __html: htmlContent }}
|
|
1165
|
+
/>
|
|
1166
|
+
</div>
|
|
1167
|
+
);
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
// For plain text content
|
|
1171
|
+
return variant === 'user' ? (
|
|
1172
|
+
<p
|
|
1173
|
+
className={`text-sm leading-relaxed whitespace-pre-wrap break-words ${
|
|
1174
|
+
planMode ? 'text-primary-foreground' : 'text-gray-900'
|
|
1175
|
+
}`}
|
|
1176
|
+
>
|
|
1177
|
+
{content}
|
|
1178
|
+
</p>
|
|
1179
|
+
) : (
|
|
1180
|
+
<div className="message-container">
|
|
1181
|
+
<p className="text-sm text-gray-700 leading-relaxed whitespace-pre-wrap break-words">{content}</p>
|
|
1182
|
+
</div>
|
|
1183
|
+
);
|
|
1184
|
+
};
|
|
1185
|
+
|
|
1186
|
+
// Detect predominant text direction (RTL/LTR) for international content
|
|
1187
|
+
const detectTextDirection = (text: string): 'rtl' | 'ltr' => {
|
|
1188
|
+
if (!text) return 'ltr';
|
|
1189
|
+
const rtlRanges = /[\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC]/g; // Hebrew, Arabic, etc.
|
|
1190
|
+
const ltrRanges = /[A-Za-z\u00C0-\u024F\u1E00-\u1EFF]/g;
|
|
1191
|
+
const rtlCount = (text.match(rtlRanges) || []).length;
|
|
1192
|
+
const ltrCount = (text.match(ltrRanges) || []).length;
|
|
1193
|
+
return rtlCount > ltrCount ? 'rtl' : 'ltr';
|
|
1194
|
+
};
|
|
1195
|
+
|
|
1196
|
+
// Lightweight language heuristic for lang attribute
|
|
1197
|
+
const detectLanguageTag = (text: string): string => {
|
|
1198
|
+
if (!text) return 'en';
|
|
1199
|
+
if (/\p{Script=Arabic}/u.test(text)) return 'ar';
|
|
1200
|
+
if (/[\u0590-\u05FF]/.test(text)) return 'he';
|
|
1201
|
+
if (/[\u0600-\u06FF]/.test(text)) return 'fa';
|
|
1202
|
+
if (/[\u0900-\u097F]/.test(text)) return 'hi';
|
|
1203
|
+
if (/[\u4E00-\u9FFF]/.test(text)) return 'zh';
|
|
1204
|
+
if (/[\u3040-\u309F\u30A0-\u30FF]/.test(text)) return 'ja';
|
|
1205
|
+
if (/[\uAC00-\uD7AF]/.test(text)) return 'ko';
|
|
1206
|
+
if (/[\u0400-\u04FF]/.test(text)) return 'ru';
|
|
1207
|
+
if (/[\u0E00-\u0E7F]/.test(text)) return 'th';
|
|
1208
|
+
return 'en';
|
|
1209
|
+
};
|
|
1210
|
+
|
|
1211
|
+
// Attempt to parse Builder.io-like JSON blocks embedded as message string
|
|
1212
|
+
const tryExtractBuilderBlocks = (content?: string): any[] | null => {
|
|
1213
|
+
if (!content || content.length < 2) return null;
|
|
1214
|
+
try {
|
|
1215
|
+
const trimmed = content.trim();
|
|
1216
|
+
const jsonMatch = trimmed.match(/([\[{][\s\S]*[\]}])/);
|
|
1217
|
+
const jsonText = jsonMatch ? jsonMatch[1] : trimmed;
|
|
1218
|
+
const parsed = JSON.parse(jsonText);
|
|
1219
|
+
if (Array.isArray(parsed)) {
|
|
1220
|
+
if (parsed.length && (parsed[0]['@type'] || parsed[0].type)) return parsed;
|
|
1221
|
+
} else if (parsed && typeof parsed === 'object') {
|
|
1222
|
+
if (Array.isArray(parsed.blocks)) return parsed.blocks;
|
|
1223
|
+
if (Array.isArray(parsed?.data?.blocks)) return parsed.data.blocks;
|
|
1224
|
+
}
|
|
1225
|
+
return null;
|
|
1226
|
+
} catch {
|
|
1227
|
+
return null;
|
|
1228
|
+
}
|
|
1229
|
+
};
|
|
1230
|
+
|
|
1231
|
+
const ModernMessageGroup: React.FC<MessageGroupProps> = ({
|
|
1232
|
+
author,
|
|
1233
|
+
messages,
|
|
1234
|
+
currentUser,
|
|
1235
|
+
showAuthorName = false,
|
|
1236
|
+
showAvatar = false,
|
|
1237
|
+
showTimestamp = false,
|
|
1238
|
+
onOpen,
|
|
1239
|
+
onMessageClick,
|
|
1240
|
+
isDesktopView = false,
|
|
1241
|
+
isSmallScreen = false,
|
|
1242
|
+
showCopyDownloadOnCodeBlock = false,
|
|
1243
|
+
}) => {
|
|
1244
|
+
// Inject CSS styles for HTML content
|
|
1245
|
+
useInjectStyles();
|
|
1246
|
+
|
|
1247
|
+
// Preserve server / store order for posts (TOOLUSE, THINKING, etc.); no client-side reordering
|
|
1248
|
+
const displayMessages = messages;
|
|
1249
|
+
|
|
1250
|
+
//const isOwnMessage = author?.id === currentUser?.id;
|
|
1251
|
+
//const isOwnMessage = messages.some((message: any) => (message as any)?.propsConfiguration?.contents?.role === AiAgentMessageRole.User);
|
|
1252
|
+
const isOwnMessage =
|
|
1253
|
+
displayMessages.some(
|
|
1254
|
+
(message: any) => (message as any)?.propsConfiguration?.contents?.role === AiAgentMessageRole.User,
|
|
1255
|
+
) ||
|
|
1256
|
+
(Array.isArray(author?.alias) &&
|
|
1257
|
+
typeof currentUser?.authUserId === 'string' &&
|
|
1258
|
+
author.alias.some((alias: string) => alias?.toLowerCase() === currentUser.authUserId.toLowerCase()));
|
|
1259
|
+
|
|
1260
|
+
const authorName =
|
|
1261
|
+
author?.givenName && author?.familyName
|
|
1262
|
+
? `${author.givenName} ${author.familyName}`
|
|
1263
|
+
: author?.username || 'Unknown User';
|
|
1264
|
+
|
|
1265
|
+
const firstMessage = displayMessages[0];
|
|
1266
|
+
const formatTime = (timestamp: string) => {
|
|
1267
|
+
const date = new Date(timestamp);
|
|
1268
|
+
return format(date, 'h:mm a');
|
|
1269
|
+
};
|
|
1270
|
+
|
|
1271
|
+
// Determine if this is an AI/system message for special styling
|
|
1272
|
+
const isSystemMessage =
|
|
1273
|
+
author?.username?.toLowerCase().includes('ai') ||
|
|
1274
|
+
author?.username?.toLowerCase().includes('assistant') ||
|
|
1275
|
+
author?.username?.toLowerCase().includes('system');
|
|
1276
|
+
// const isSystemMessage = messages.some(
|
|
1277
|
+
// (message: any) => (message as any)?.propsConfiguration?.contents?.role === AiAgentMessageRole.Assistant,
|
|
1278
|
+
// );
|
|
1279
|
+
|
|
1280
|
+
// For user messages, don't show group header, just individual messages with avatars
|
|
1281
|
+
if (isOwnMessage) {
|
|
1282
|
+
return (
|
|
1283
|
+
<div
|
|
1284
|
+
className={`min-h-fit ${
|
|
1285
|
+
isDesktopView ? 'space-y-1 mb-3' : isSmallScreen ? 'space-y-0.5 mb-2' : 'space-y-0.5 mb-2'
|
|
1286
|
+
}`}
|
|
1287
|
+
>
|
|
1288
|
+
{displayMessages.map((message, index) => (
|
|
1289
|
+
<ModernMessageBubble
|
|
1290
|
+
key={message.id}
|
|
1291
|
+
message={message}
|
|
1292
|
+
isOwnMessage={isOwnMessage}
|
|
1293
|
+
isSystemMessage={isSystemMessage}
|
|
1294
|
+
isFirstInGroup={index === 0}
|
|
1295
|
+
isLastInGroup={index === displayMessages.length - 1}
|
|
1296
|
+
onMessageClick={onMessageClick}
|
|
1297
|
+
formatTime={formatTime}
|
|
1298
|
+
showAuthorName={showAuthorName}
|
|
1299
|
+
showAvatar={showAvatar}
|
|
1300
|
+
showTimestamp={showTimestamp}
|
|
1301
|
+
showCopyDownloadOnCodeBlock={showCopyDownloadOnCodeBlock}
|
|
1302
|
+
/>
|
|
1303
|
+
))}
|
|
1304
|
+
</div>
|
|
1305
|
+
);
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
// For other messages (non-user), show the traditional group layout
|
|
1309
|
+
return (
|
|
1310
|
+
<div
|
|
1311
|
+
className={`group transition-all duration-300 ease-in-out ${
|
|
1312
|
+
isDesktopView ? 'mb-4 px-4 py-3' : isSmallScreen ? 'mb-2 px-2 py-1' : 'mb-3 px-3 py-1'
|
|
1313
|
+
} ${isSystemMessage ? 'bg-white rounded-lg' : ' rounded-lg'}`}
|
|
1314
|
+
>
|
|
1315
|
+
<div
|
|
1316
|
+
className={`flex items-start ${
|
|
1317
|
+
isDesktopView ? 'space-x-4' : isSmallScreen ? 'space-x-2' : 'space-x-3'
|
|
1318
|
+
}`}
|
|
1319
|
+
>
|
|
1320
|
+
{/* Enhanced Avatar - Only show for non-system messages */}
|
|
1321
|
+
{/* {!isSystemMessage && (
|
|
1322
|
+
<div className="flex-shrink-0 mt-1">
|
|
1323
|
+
<div className="relative rounded-lg overflow-hidden">
|
|
1324
|
+
<img
|
|
1325
|
+
className={`cursor-pointer hover:opacity-80 transition-opacity ${
|
|
1326
|
+
isDesktopView ? 'w-12 h-12' : isSmallScreen ? 'w-8 h-8' : 'w-10 h-10'
|
|
1327
|
+
} rounded-lg object-cover`}
|
|
1328
|
+
src={author?.picture || '/default-avatar.svg'}
|
|
1329
|
+
alt={authorName}
|
|
1330
|
+
onClick={() => onOpen(firstMessage)}
|
|
1331
|
+
onError={(e) => {
|
|
1332
|
+
e.currentTarget.src = '/default-avatar.svg';
|
|
1333
|
+
}}
|
|
1334
|
+
/>
|
|
1335
|
+
</div>
|
|
1336
|
+
</div>
|
|
1337
|
+
)} */}
|
|
1338
|
+
|
|
1339
|
+
<div className="flex-1 min-w-0 overflow-hidden">
|
|
1340
|
+
{/* Enhanced author header - Only show for non-system messages */}
|
|
1341
|
+
{/* {!isSystemMessage && (
|
|
1342
|
+
<div className="flex items-center space-x-3 mb-2">
|
|
1343
|
+
<span className="font-semibold truncate text-gray-900">{authorName}</span>
|
|
1344
|
+
</div>
|
|
1345
|
+
)} */}
|
|
1346
|
+
|
|
1347
|
+
{/* Enhanced Messages with rich formatting */}
|
|
1348
|
+
<div className="space-y-1">
|
|
1349
|
+
{displayMessages.map((message, index) => (
|
|
1350
|
+
<ModernMessageBubble
|
|
1351
|
+
key={message.id}
|
|
1352
|
+
message={message}
|
|
1353
|
+
isOwnMessage={isOwnMessage}
|
|
1354
|
+
isSystemMessage={isSystemMessage}
|
|
1355
|
+
isFirstInGroup={index === 0}
|
|
1356
|
+
isLastInGroup={index === displayMessages.length - 1}
|
|
1357
|
+
onMessageClick={onMessageClick}
|
|
1358
|
+
formatTime={formatTime}
|
|
1359
|
+
showTimestamp={showTimestamp}
|
|
1360
|
+
showCopyDownloadOnCodeBlock={showCopyDownloadOnCodeBlock}
|
|
1361
|
+
/>
|
|
1362
|
+
))}
|
|
1363
|
+
</div>
|
|
1364
|
+
</div>
|
|
1365
|
+
</div>
|
|
1366
|
+
</div>
|
|
1367
|
+
);
|
|
1368
|
+
};
|
|
1369
|
+
|
|
1370
|
+
interface ModernMessageBubbleProps {
|
|
1371
|
+
showAuthorName?: boolean;
|
|
1372
|
+
showAvatar?: boolean;
|
|
1373
|
+
showTimestamp?: boolean;
|
|
1374
|
+
message: IPost;
|
|
1375
|
+
isOwnMessage: boolean;
|
|
1376
|
+
isSystemMessage: boolean;
|
|
1377
|
+
isFirstInGroup: boolean;
|
|
1378
|
+
isLastInGroup: boolean;
|
|
1379
|
+
onMessageClick: (msg: IPost) => void;
|
|
1380
|
+
formatTime: (timestamp: string) => string;
|
|
1381
|
+
showCopyDownloadOnCodeBlock?: boolean;
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
const ModernMessageBubble: React.FC<ModernMessageBubbleProps> = ({
|
|
1385
|
+
showAuthorName = false,
|
|
1386
|
+
showAvatar = false,
|
|
1387
|
+
showTimestamp = false,
|
|
1388
|
+
message,
|
|
1389
|
+
isOwnMessage,
|
|
1390
|
+
isSystemMessage,
|
|
1391
|
+
isFirstInGroup,
|
|
1392
|
+
isLastInGroup,
|
|
1393
|
+
onMessageClick,
|
|
1394
|
+
formatTime,
|
|
1395
|
+
showCopyDownloadOnCodeBlock = false,
|
|
1396
|
+
}) => {
|
|
1397
|
+
const handleClick = () => {
|
|
1398
|
+
onMessageClick?.(message);
|
|
1399
|
+
};
|
|
1400
|
+
|
|
1401
|
+
const isAssistantRole = (message as any)?.propsConfiguration?.contents?.role === AiAgentMessageRole.Assistant;
|
|
1402
|
+
const messageMode =
|
|
1403
|
+
(message as any)?.propsConfiguration?.contents?.mode ??
|
|
1404
|
+
(message as any)?.propsConfiguration?.content?.mode ??
|
|
1405
|
+
(message as any)?.props?.mode;
|
|
1406
|
+
const isPlanMode = messageMode === 'plan';
|
|
1407
|
+
// For user messages, create a right-aligned layout with avatar and name
|
|
1408
|
+
if (isOwnMessage) {
|
|
1409
|
+
const authorName =
|
|
1410
|
+
message.author?.givenName && message.author?.familyName
|
|
1411
|
+
? `${message.author.givenName} ${message.author.familyName}`
|
|
1412
|
+
: message.author?.username || 'You';
|
|
1413
|
+
|
|
1414
|
+
return (
|
|
1415
|
+
<div className="py-1 px-1 sm:px-2 -mx-1 sm:-mx-2 group">
|
|
1416
|
+
<div className="flex items-start justify-end gap-2">
|
|
1417
|
+
<div className="flex flex-col items-end max-w-[85%]">
|
|
1418
|
+
{showAuthorName && (
|
|
1419
|
+
<div className="flex items-end space-x-2 mb-0.5">
|
|
1420
|
+
<span className="text-sm font-semibold text-gray-900">{authorName}</span>
|
|
1421
|
+
</div>
|
|
1422
|
+
)}
|
|
1423
|
+
|
|
1424
|
+
{/* User message bubble (Image 2) */}
|
|
1425
|
+
<div
|
|
1426
|
+
className={`text-sm ${
|
|
1427
|
+
isAssistantRole
|
|
1428
|
+
? 'cursor-pointer rounded-2xl border border-gray-200 bg-gray-50 text-gray-900 px-4 py-2.5'
|
|
1429
|
+
: // : 'user-message rounded-[22px] rounded-br-[4px] px-4 py-3 bg-primary text-primary-foreground shadow-sm'
|
|
1430
|
+
isPlanMode
|
|
1431
|
+
? 'user-message rounded-[22px] rounded-br-[4px] px-4 py-3 bg-primary text-primary-foreground shadow-sm'
|
|
1432
|
+
: 'user-message rounded-[22px] rounded-br-[4px] px-4 py-3 '
|
|
1433
|
+
}`}
|
|
1434
|
+
onClick={isAssistantRole ? handleClick : undefined}
|
|
1435
|
+
dir={detectTextDirection((message as any)?.message || '')}
|
|
1436
|
+
lang={detectLanguageTag((message as any)?.message || '')}
|
|
1437
|
+
>
|
|
1438
|
+
{message.message && (
|
|
1439
|
+
<div className="max-w-none">
|
|
1440
|
+
{(() => {
|
|
1441
|
+
const contentType = detectContentType(message.message);
|
|
1442
|
+
|
|
1443
|
+
if (contentType === 'code') {
|
|
1444
|
+
return (
|
|
1445
|
+
<CodeFormatter
|
|
1446
|
+
content={message.message}
|
|
1447
|
+
showCopyDownload={showCopyDownloadOnCodeBlock}
|
|
1448
|
+
variant={isAssistantRole ? 'default' : 'user'}
|
|
1449
|
+
planMode={!isAssistantRole && isPlanMode}
|
|
1450
|
+
/>
|
|
1451
|
+
);
|
|
1452
|
+
} else if (contentType === 'html') {
|
|
1453
|
+
return (
|
|
1454
|
+
<div
|
|
1455
|
+
className={
|
|
1456
|
+
isAssistantRole
|
|
1457
|
+
? 'text-gray-800 html-content'
|
|
1458
|
+
: isPlanMode
|
|
1459
|
+
? '!text-primary-foreground html-content'
|
|
1460
|
+
: 'text-gray-900 html-content'
|
|
1461
|
+
}
|
|
1462
|
+
dangerouslySetInnerHTML={{
|
|
1463
|
+
__html: prettifyHtmlContent(sanitizeHtml(message.message)),
|
|
1464
|
+
}}
|
|
1465
|
+
/>
|
|
1466
|
+
);
|
|
1467
|
+
} else {
|
|
1468
|
+
return (
|
|
1469
|
+
<FormattedMessageContent
|
|
1470
|
+
content={message.message}
|
|
1471
|
+
variant={isAssistantRole ? 'default' : 'user'}
|
|
1472
|
+
planMode={!isAssistantRole && isPlanMode}
|
|
1473
|
+
showCopyDownloadOnCodeBlock={showCopyDownloadOnCodeBlock}
|
|
1474
|
+
/>
|
|
1475
|
+
);
|
|
1476
|
+
}
|
|
1477
|
+
})()}
|
|
1478
|
+
</div>
|
|
1479
|
+
)}
|
|
1480
|
+
|
|
1481
|
+
{tryExtractBuilderBlocks((message as any)?.message)?.length ? (
|
|
1482
|
+
<div className="mt-2">
|
|
1483
|
+
<BuilderLikeRenderer blocks={tryExtractBuilderBlocks((message as any)?.message)} />
|
|
1484
|
+
</div>
|
|
1485
|
+
) : null}
|
|
1486
|
+
|
|
1487
|
+
{(message as any)?.props?.blocks?.length ? (
|
|
1488
|
+
<div className="mt-2">
|
|
1489
|
+
<BuilderLikeRenderer blocks={(message as any).props.blocks} />
|
|
1490
|
+
</div>
|
|
1491
|
+
) : null}
|
|
1492
|
+
|
|
1493
|
+
{message.files?.totalCount > 0 && (
|
|
1494
|
+
<div className="mt-2">
|
|
1495
|
+
<FilesList uploaded files={message.files.data} />
|
|
1496
|
+
</div>
|
|
1497
|
+
)}
|
|
1498
|
+
{showTimestamp ? (
|
|
1499
|
+
<div
|
|
1500
|
+
className={`text-[10px] mt-1 text-right ${
|
|
1501
|
+
isAssistantRole
|
|
1502
|
+
? 'text-gray-500'
|
|
1503
|
+
: isPlanMode
|
|
1504
|
+
? 'text-primary-foreground/80'
|
|
1505
|
+
: 'text-gray-500'
|
|
1506
|
+
}`}
|
|
1507
|
+
>
|
|
1508
|
+
{formatTime(message.createdAt)}
|
|
1509
|
+
</div>
|
|
1510
|
+
) : null}
|
|
1511
|
+
</div>
|
|
1512
|
+
</div>
|
|
1513
|
+
|
|
1514
|
+
{/* User avatar on the right */}
|
|
1515
|
+
{showAvatar && (
|
|
1516
|
+
<div className="flex-shrink-0 mt-0.5">
|
|
1517
|
+
<img
|
|
1518
|
+
className={`w-8 h-8 sm:w-10 sm:h-10 rounded-lg ${
|
|
1519
|
+
isAssistantRole ? '' : 'cursor-pointer hover:opacity-80'
|
|
1520
|
+
} transition-opacity object-cover`}
|
|
1521
|
+
src={message.author?.picture || '/default-avatar.svg'}
|
|
1522
|
+
alt={authorName}
|
|
1523
|
+
//onClick={isAssistantRole ? undefined : handleClick}
|
|
1524
|
+
onClick={isAssistantRole ? handleClick : undefined}
|
|
1525
|
+
onError={(e) => {
|
|
1526
|
+
e.currentTarget.src = '/default-avatar.svg';
|
|
1527
|
+
}}
|
|
1528
|
+
/>
|
|
1529
|
+
</div>
|
|
1530
|
+
)}
|
|
1531
|
+
</div>
|
|
1532
|
+
</div>
|
|
1533
|
+
);
|
|
1534
|
+
}
|
|
1535
|
+
|
|
1536
|
+
// For other messages (left-aligned) — use Slot for streaming types (THINKING, TOOLUSE, etc.) like Kimi
|
|
1537
|
+
const attachment =
|
|
1538
|
+
(message as any)?.props?.attachment ?? (message as any)?.propsConfiguration?.contents?.attachment;
|
|
1539
|
+
const slotName = getMessageSlotName((message as any)?.type);
|
|
1540
|
+
// Don't show THINKING slot when attachment has no meaningful content or when dismissed
|
|
1541
|
+
const isThinkingSlot = slotName === MessengerSlotFillNameEnum.THINKING_NAME;
|
|
1542
|
+
const shouldShowThinkingSlot =
|
|
1543
|
+
!!attachment &&
|
|
1544
|
+
attachment.dismissed !== true &&
|
|
1545
|
+
(attachment.isThinking === true || (attachment.thought != null && String(attachment.thought).trim() !== ''));
|
|
1546
|
+
const shouldShowSlot = isThinkingSlot ? shouldShowThinkingSlot : !!attachment;
|
|
1547
|
+
const hasStreamingSlot = !!slotName && shouldShowSlot;
|
|
1548
|
+
return (
|
|
1549
|
+
<div
|
|
1550
|
+
className={`group/message transition-all duration-200 hover:bg-gray-50 rounded-lg px-3 py-1 -mx-3 ${
|
|
1551
|
+
isSystemMessage || isAssistantRole ? 'cursor-pointer' : ''
|
|
1552
|
+
}`}
|
|
1553
|
+
onClick={isSystemMessage || isAssistantRole ? handleClick : undefined}
|
|
1554
|
+
>
|
|
1555
|
+
<div className="flex items-start justify-between gap-3">
|
|
1556
|
+
<div className="flex-1 min-w-0">
|
|
1557
|
+
{/* Slot fill for streaming message types (THINKING, TOOLUSE, CODEBLOCK, SERVICE, AIASSISTANT) */}
|
|
1558
|
+
{slotName && shouldShowSlot && (
|
|
1559
|
+
<Slot
|
|
1560
|
+
name={slotName}
|
|
1561
|
+
fillProps={{
|
|
1562
|
+
active: true,
|
|
1563
|
+
message,
|
|
1564
|
+
index: 0,
|
|
1565
|
+
onMessageClick,
|
|
1566
|
+
}}
|
|
1567
|
+
/>
|
|
1568
|
+
)}
|
|
1569
|
+
{/* Default content when no slot or attachment (slot returns null when active: false) */}
|
|
1570
|
+
{!hasStreamingSlot && message.message && (
|
|
1571
|
+
<div
|
|
1572
|
+
className={`max-w-none ${isSystemMessage ? 'text-gray-800' : 'text-gray-900'}`}
|
|
1573
|
+
dir={detectTextDirection((message as any)?.message || '')}
|
|
1574
|
+
lang={detectLanguageTag((message as any)?.message || '')}
|
|
1575
|
+
>
|
|
1576
|
+
{(() => {
|
|
1577
|
+
const contentType = detectContentType(message.message);
|
|
1578
|
+
|
|
1579
|
+
// Markdown and text both go through FormattedMessageContent
|
|
1580
|
+
// which will intelligently detect and render markdown
|
|
1581
|
+
if (contentType === 'code') {
|
|
1582
|
+
return (
|
|
1583
|
+
<CodeFormatter
|
|
1584
|
+
content={message.message}
|
|
1585
|
+
showCopyDownload={showCopyDownloadOnCodeBlock}
|
|
1586
|
+
/>
|
|
1587
|
+
);
|
|
1588
|
+
} else if (contentType === 'html') {
|
|
1589
|
+
return (
|
|
1590
|
+
<div
|
|
1591
|
+
className="max-w-none text-gray-800 html-content"
|
|
1592
|
+
dangerouslySetInnerHTML={{
|
|
1593
|
+
__html: prettifyHtmlContent(sanitizeHtml(message.message)),
|
|
1594
|
+
}}
|
|
1595
|
+
/>
|
|
1596
|
+
);
|
|
1597
|
+
} else {
|
|
1598
|
+
// Handle both 'markdown' and 'text' - FormattedMessageContent
|
|
1599
|
+
// will detect markdown patterns and render accordingly
|
|
1600
|
+
return (
|
|
1601
|
+
<FormattedMessageContent
|
|
1602
|
+
content={message.message}
|
|
1603
|
+
showCopyDownloadOnCodeBlock={showCopyDownloadOnCodeBlock}
|
|
1604
|
+
/>
|
|
1605
|
+
);
|
|
1606
|
+
}
|
|
1607
|
+
})()}
|
|
1608
|
+
</div>
|
|
1609
|
+
)}
|
|
1610
|
+
|
|
1611
|
+
{!hasStreamingSlot && tryExtractBuilderBlocks((message as any)?.message)?.length ? (
|
|
1612
|
+
<div className="mt-3 p-3 bg-gray-50 rounded-lg border border-gray-200">
|
|
1613
|
+
<BuilderLikeRenderer blocks={tryExtractBuilderBlocks((message as any)?.message)} />
|
|
1614
|
+
</div>
|
|
1615
|
+
) : null}
|
|
1616
|
+
|
|
1617
|
+
{!hasStreamingSlot && (message as any)?.props?.blocks?.length ? (
|
|
1618
|
+
<div className="mt-3 p-3 bg-gray-50 rounded-lg border border-gray-200">
|
|
1619
|
+
<BuilderLikeRenderer blocks={(message as any).props.blocks} />
|
|
1620
|
+
</div>
|
|
1621
|
+
) : null}
|
|
1622
|
+
|
|
1623
|
+
{!hasStreamingSlot && message.files?.totalCount > 0 ? (
|
|
1624
|
+
<div className="mt-3 p-3 bg-gray-50 rounded-lg border border-gray-200">
|
|
1625
|
+
<FilesList uploaded files={message.files.data} />
|
|
1626
|
+
</div>
|
|
1627
|
+
) : null}
|
|
1628
|
+
</div>
|
|
1629
|
+
|
|
1630
|
+
{/* Action buttons on hover - Only show for non-system messages */}
|
|
1631
|
+
{/* {!isSystemMessage && (
|
|
1632
|
+
<div className="flex-shrink-0 mt-1">
|
|
1633
|
+
<div className="opacity-0 group-hover/message:opacity-100 transition-opacity duration-200 flex space-x-1">
|
|
1634
|
+
<button className="p-1 hover:bg-gray-200 rounded transition-colors">
|
|
1635
|
+
<svg
|
|
1636
|
+
className="w-3 h-3 text-gray-400"
|
|
1637
|
+
fill="none"
|
|
1638
|
+
stroke="currentColor"
|
|
1639
|
+
viewBox="0 0 24 24"
|
|
1640
|
+
>
|
|
1641
|
+
<path
|
|
1642
|
+
strokeLinecap="round"
|
|
1643
|
+
strokeLinejoin="round"
|
|
1644
|
+
strokeWidth={2}
|
|
1645
|
+
d="M8.684 13.342C8.886 12.938 9 12.482 9 12c0-.482-.114-.938-.316-1.342m0 2.684a3 3 0 110-2.684m0 2.684l6.632 3.316m-6.632-6l6.632-3.316m0 0a3 3 0 105.367-2.684 3 3 0 00-5.367 2.684zm0 9.316a3 3 0 105.367 2.684 3 3 0 00-5.367-2.684z"
|
|
1646
|
+
/>
|
|
1647
|
+
</svg>
|
|
1648
|
+
</button>
|
|
1649
|
+
<button className="p-1 hover:bg-gray-200 rounded transition-colors">
|
|
1650
|
+
<svg
|
|
1651
|
+
className="w-3 h-3 text-gray-400"
|
|
1652
|
+
fill="none"
|
|
1653
|
+
stroke="currentColor"
|
|
1654
|
+
viewBox="0 0 24 24"
|
|
1655
|
+
>
|
|
1656
|
+
<path
|
|
1657
|
+
strokeLinecap="round"
|
|
1658
|
+
strokeLinejoin="round"
|
|
1659
|
+
strokeWidth={2}
|
|
1660
|
+
d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"
|
|
1661
|
+
/>
|
|
1662
|
+
</svg>
|
|
1663
|
+
</button>
|
|
1664
|
+
</div>
|
|
1665
|
+
</div>
|
|
1666
|
+
)} */}
|
|
1667
|
+
</div>
|
|
1668
|
+
{showTimestamp ? (
|
|
1669
|
+
<div className="text-[10px] text-gray-500 mt-1 text-right">{formatTime(message.createdAt)}</div>
|
|
1670
|
+
) : null}
|
|
1671
|
+
</div>
|
|
1672
|
+
);
|
|
1673
|
+
};
|
|
1674
|
+
|
|
1675
|
+
export const ModernMessageGroupComponent: React.FC<ModernMessageGroupProps> = ({
|
|
1676
|
+
messages,
|
|
1677
|
+
currentUser,
|
|
1678
|
+
showAuthorName = false,
|
|
1679
|
+
showAvatar = false,
|
|
1680
|
+
showTimestamp = false,
|
|
1681
|
+
onOpen,
|
|
1682
|
+
onMessageClick,
|
|
1683
|
+
isDesktopView = false,
|
|
1684
|
+
isSmallScreen = false,
|
|
1685
|
+
sandboxErrors = [],
|
|
1686
|
+
currentFiles = {},
|
|
1687
|
+
onFixError,
|
|
1688
|
+
showCopyDownloadOnCodeBlock = false,
|
|
1689
|
+
}) => {
|
|
1690
|
+
// Inject CSS styles for HTML content
|
|
1691
|
+
useInjectStyles();
|
|
1692
|
+
|
|
1693
|
+
// Filter out non-message items (like date strings)
|
|
1694
|
+
const actualMessages = messages.filter((msg) => typeof msg !== 'string') as IPost[];
|
|
1695
|
+
|
|
1696
|
+
// Group messages by user and time
|
|
1697
|
+
const messageGroups = groupMessagesByUserAndTime(actualMessages);
|
|
1698
|
+
|
|
1699
|
+
return (
|
|
1700
|
+
<div className={`min-h-fit ${isDesktopView ? 'space-y-6' : isSmallScreen ? 'space-y-4' : 'space-y-5'}`}>
|
|
1701
|
+
{messageGroups.map((group, groupIndex) => {
|
|
1702
|
+
const author = group[0]?.author;
|
|
1703
|
+
return (
|
|
1704
|
+
<ModernMessageGroup
|
|
1705
|
+
key={`group-${groupIndex}-${group[0]?.id}`}
|
|
1706
|
+
author={author}
|
|
1707
|
+
messages={group}
|
|
1708
|
+
currentUser={currentUser}
|
|
1709
|
+
onOpen={onOpen}
|
|
1710
|
+
onMessageClick={onMessageClick}
|
|
1711
|
+
isDesktopView={isDesktopView}
|
|
1712
|
+
isSmallScreen={isSmallScreen}
|
|
1713
|
+
showAuthorName={showAuthorName}
|
|
1714
|
+
showAvatar={showAvatar}
|
|
1715
|
+
showTimestamp={showTimestamp}
|
|
1716
|
+
showCopyDownloadOnCodeBlock={showCopyDownloadOnCodeBlock}
|
|
1717
|
+
/>
|
|
1718
|
+
);
|
|
1719
|
+
})}
|
|
1720
|
+
|
|
1721
|
+
{sandboxErrors?.map((error) => (
|
|
1722
|
+
<div key={error.id} className="px-2">
|
|
1723
|
+
<ErrorFixCard
|
|
1724
|
+
error={error}
|
|
1725
|
+
onFixError={onFixError || (() => Promise.resolve())}
|
|
1726
|
+
currentFiles={currentFiles}
|
|
1727
|
+
isFixing={false}
|
|
1728
|
+
/>
|
|
1729
|
+
</div>
|
|
1730
|
+
))}
|
|
1731
|
+
</div>
|
|
1732
|
+
);
|
|
1733
|
+
};
|