From cd29687ce0163428fa7e7dd197e77961ee8f61a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E9=99=8C?= Date: Wed, 19 Jul 2023 23:39:43 +0800 Subject: [PATCH] no message --- package.json | 24 ++-- src/components/ModalsLayer.tsx | 14 ++ .../header/ConversationHeaderShare.tsx | 2 +- .../ConversationMessageSettingButton.tsx | 23 ++++ .../header/ConversationMessageShareButton.tsx | 29 +++++ src/components/header/Header.tsx | 4 + src/components/ui/SelectMessageModal.tsx | 63 +++++++++ src/components/ui/ShareModal.tsx | 120 ++++++++++++++++++ src/components/ui/base/Checkbox.tsx | 34 +++++ src/components/ui/base/Tabs.tsx | 44 +++++++ src/components/ui/base/index.ts | 2 + src/locale/lang/en.ts | 36 ++++++ src/locale/lang/zh-cn.ts | 36 ++++++ src/providers/openai/index.ts | 9 +- src/stores/messages.ts | 5 +- src/stores/ui.ts | 4 +- 16 files changed, 432 insertions(+), 17 deletions(-) create mode 100644 src/components/header/ConversationMessageSettingButton.tsx create mode 100644 src/components/header/ConversationMessageShareButton.tsx create mode 100644 src/components/ui/SelectMessageModal.tsx create mode 100644 src/components/ui/ShareModal.tsx create mode 100644 src/components/ui/base/Checkbox.tsx create mode 100644 src/components/ui/base/Tabs.tsx diff --git a/package.json b/package.json index ba42900..0b0fe35 100644 --- a/package.json +++ b/package.json @@ -27,15 +27,20 @@ "@solid-primitives/scheduled": "^1.3.2", "@solid-primitives/scroll": "^2.0.14", "@unocss/reset": "^0.50.6", - "@zag-js/dialog": "^0.9.2", - "@zag-js/menu": "^0.9.2", - "@zag-js/select": "^0.9.2", - "@zag-js/slider": "^0.9.2", - "@zag-js/solid": "^0.9.2", - "@zag-js/switch": "^0.9.2", - "@zag-js/toast": "^0.9.2", - "@zag-js/toggle": "^0.9.2", - "@zag-js/tooltip": "^0.9.2", + + "@zag-js/checkbox": "^0.10.3", + "@zag-js/dialog": "^0.10.3", + "@zag-js/menu": "^0.10.3", + "@zag-js/select": "^0.10.3", + "@zag-js/slider": "^0.10.3", + "@zag-js/solid": "^0.10.3", + "@zag-js/switch": "^0.10.3", + "@zag-js/tabs": "^0.10.3", + "@zag-js/toast": "^0.10.3", + "@zag-js/toggle": "^0.10.3", + "@zag-js/tooltip": "^0.10.3", + + "astro": "^2.2.0", "bumpp": "^9.1.0", "destr": "^1.2.2", @@ -69,6 +74,7 @@ "eslint-plugin-astro": "^0.24.0", "lint-staged": "^13.2.2", "punycode": "^2.3.0", + "html2canvas": "^1.4.1", "simple-git-hooks": "^2.8.1", "unocss": "^0.50.6", "vite-plugin-pwa": "^0.14.7" diff --git a/src/components/ModalsLayer.tsx b/src/components/ModalsLayer.tsx index 885096d..44cbe93 100644 --- a/src/components/ModalsLayer.tsx +++ b/src/components/ModalsLayer.tsx @@ -2,12 +2,16 @@ import { showConversationEditModal, showConversationSidebar, showEmojiPickerModal, + showSelectMessageModal, showSettingsSidebar, + showShareModal, } from '@/stores/ui' import ConversationSidebar from './conversations/ConversationSidebar' import SettingsSidebar from './settings/SettingsSidebar' import ConversationEditModal from './conversations/ConversationEditModal' import EmojiPickerModal from './ui/EmojiPickerModal' +import ShareModal from './ui/ShareModal' +import SelectMessageModal from './ui/SelectMessageModal' import Modal from './ui/Modal' export default () => { @@ -33,6 +37,16 @@ export default () => { + +
+ +
+
+ { showShareModal.set(true) }}> +
+ +
+
) } diff --git a/src/components/header/ConversationHeaderShare.tsx b/src/components/header/ConversationHeaderShare.tsx index 94dbbad..0136c83 100644 --- a/src/components/header/ConversationHeaderShare.tsx +++ b/src/components/header/ConversationHeaderShare.tsx @@ -38,7 +38,7 @@ export default () => { <> { $currentConversationId() && (
{ handleShareMessage(true) }} > - +
diff --git a/src/components/header/ConversationMessageSettingButton.tsx b/src/components/header/ConversationMessageSettingButton.tsx new file mode 100644 index 0000000..6ebccef --- /dev/null +++ b/src/components/header/ConversationMessageSettingButton.tsx @@ -0,0 +1,23 @@ +import { useStore } from '@nanostores/solid' +import { showConversationEditModal } from '@/stores/ui' +import { currentConversationId } from '@/stores/conversation' + +export default () => { + // Retrieve the current conversation ID from the store + const $currentConversationId = useStore(currentConversationId) + + return ( + <> + {/* Render the following code if the current conversation ID exists */} + {$currentConversationId() && ( +
{ showConversationEditModal.set(true) }} + > + {/* Render the carbon settings adjust icon */} +
+
+ )} + + ) +} \ No newline at end of file diff --git a/src/components/header/ConversationMessageShareButton.tsx b/src/components/header/ConversationMessageShareButton.tsx new file mode 100644 index 0000000..07899e9 --- /dev/null +++ b/src/components/header/ConversationMessageShareButton.tsx @@ -0,0 +1,29 @@ +import { useStore } from '@nanostores/solid' +import { currentConversationId } from '@/stores/conversation' +import { showShareModal } from '@/stores/ui' +import { getMessagesByConversationId, updateMessage } from '@/stores/messages' + +export default () => { + const $currentConversationId = useStore(currentConversationId) + const handleShareContext = () => { + const messages = getMessagesByConversationId($currentConversationId()) + messages.forEach((message) => { + updateMessage($currentConversationId(), message.id, { isSelected: true }, + ) + }) + showShareModal.set(true) + } + + return ( + <> + {$currentConversationId() && ( +
{ handleShareContext() }} + > +
+
+ )} + + ) +} \ No newline at end of file diff --git a/src/components/header/Header.tsx b/src/components/header/Header.tsx index 5d77a71..8ef24f0 100644 --- a/src/components/header/Header.tsx +++ b/src/components/header/Header.tsx @@ -4,6 +4,8 @@ import { useLargeScreen } from '@/hooks' import ConversationHeaderInfo from './ConversationHeaderInfo' import ConversationMessageClearButton from './ConversationMessageClearButton' import ConversationHeaderShare from './ConversationHeaderShare' +import ConversationMessageShareButton from './ConversationMessageShareButton' +import ConversationMessageSettingButton from './ConversationMessageSettingButton' export default () => { onMount(() => { @@ -26,7 +28,9 @@ export default () => {
+ + {/**/}
showSettingsSidebar.set(true)} diff --git a/src/components/ui/SelectMessageModal.tsx b/src/components/ui/SelectMessageModal.tsx new file mode 100644 index 0000000..1be753c --- /dev/null +++ b/src/components/ui/SelectMessageModal.tsx @@ -0,0 +1,63 @@ +import { useStore } from '@nanostores/solid' +import { For, createSignal } from 'solid-js' +import { useI18n } from '@/hooks' +import { currentConversationId } from '@/stores/conversation' +import { getMessagesByConversationId, updateMessage } from '@/stores/messages' +import { showSelectMessageModal, showShareModal } from '@/stores/ui' +import { Checkbox } from '../ui/base' + +export default () => { + const { t } = useI18n() + const $currentConversationId = useStore(currentConversationId) + const messages = getMessagesByConversationId($currentConversationId()) + const [checkAll, setCheckAll] = createSignal(messages.every(item => item.isSelected)) + const [selectedMessages, setSelectedMessages] = createSignal(messages) + + const handleToggleMessages = (id: string) => { + messages.forEach((item) => { + if (item.id === id) + item.isSelected = !item.isSelected + }) + setSelectedMessages(messages) + } + + const handleSelectAll = () => { + messages.forEach((item) => { + item.isSelected = !checkAll() + }) + setSelectedMessages(messages) + setCheckAll(!checkAll()) + console.log(selectedMessages(), checkAll()) + } + + const handleSaveContext = () => { + messages.forEach((item) => { + updateMessage($currentConversationId(), item.id, { isSelected: item.isSelected }) + }) + showSelectMessageModal.set(false) + showShareModal.set(true) + } + + return ( +
+
+
{t('conversations.share.messages.title')}
+
+
+ {/*
+ handleSelectAll()} initValue={checkAll()} label={`${t('conversations.share.messages.selectAll')}`} /> +
*/} + + {(item) => { + return ( +
+ handleToggleMessages(item.id)} initValue={item.isSelected} label={`${item.role}: ${item.content}`} /> +
+ ) + }} +
+
+
handleSaveContext()}>{t('settings.save')}
+
+ ) +} diff --git a/src/components/ui/ShareModal.tsx b/src/components/ui/ShareModal.tsx new file mode 100644 index 0000000..78261fa --- /dev/null +++ b/src/components/ui/ShareModal.tsx @@ -0,0 +1,120 @@ +import { useStore } from '@nanostores/solid' +import { For, Show, createSignal } from 'solid-js' +import html2canvas from 'html2canvas' +import { useClipboardCopy, useI18n } from '@/hooks' +import { currentConversationId } from '@/stores/conversation' +import { getMessagesByConversationId } from '@/stores/messages' +import { showSelectMessageModal, showShareModal } from '@/stores/ui' +import { Tabs } from '../ui/base' +import type { TabItem } from './base/Tabs' + +export default () => { + const { t } = useI18n() + const $currentConversationId = useStore(currentConversationId) + const messages = getMessagesByConversationId($currentConversationId()).filter(item => item.isSelected) + const [imageUrl, setImageUrl] = createSignal('') + const [imageBuffer, setImageBuffer] = createSignal() + const [loading, setLoading] = createSignal(false) + + const [copied, copy] = useClipboardCopy(messages.map(item => `${item.role}: ${item.content}`).join('\n')) + + const copyImage = () => { + const [,copy] = useClipboardCopy(imageBuffer()!) + copy() + } + + const handleLoadImage = async() => { + setLoading(true) + try { + const messageWrapper = document.getElementById('message_list_wrapper') as HTMLDivElement + messageWrapper.style.display = 'block' + const canvas = await html2canvas(messageWrapper, { + useCORS: true, + }) + messageWrapper.style.display = 'none' + canvas.toBlob((res) => { + if (res) { + setImageBuffer(res) + const url = URL.createObjectURL(res) + setLoading(false) + setImageUrl(url) + } + }) + } catch (error) { + console.log(error) + } finally { + setLoading(false) + } + } + + const tabs: TabItem[] = [ + { + value: 'context', + label: t('conversations.share.tabs.context'), + content:
+ {messages.length + ? ( +
+
copy()}>{copied() ? t('copyed') : t('conversations.share.copy')}
+ + {item => ( +
+
{item.role}:
+
{item.content}
+
+ )} +
+
+ ) + :
{t('empty')}
} +
, + }, + { + value: 'image', + label: t('conversations.share.tabs.image'), + content:
+ {messages.length + ? ( +
+
+ +
{ copyImage() }}>{t('conversations.share.image.copy')}
+
{ window.open(imageUrl()) }}>{t('conversations.share.image.open')}
+
+ +
handleLoadImage()}>{loading() ? t('conversations.share.image.loading') : t('conversations.share.image.btn')}
+
+
+ + + +
+ ) + :
{t('empty')}
} +
, + }, + ] + + return ( +
+
+
{t('conversations.share.title')}
+ {/* TODO */} + {/* */} +
+
+
{ + showSelectMessageModal.set(true) + showShareModal.set(false) + }} + > + {t('conversations.share.messages.selected')} + {messages.length ? `${messages.length} Messages` : t('conversations.share.messages.title')} +
+ +
+
+ ) +} \ No newline at end of file diff --git a/src/components/ui/base/Checkbox.tsx b/src/components/ui/base/Checkbox.tsx new file mode 100644 index 0000000..7a904b9 --- /dev/null +++ b/src/components/ui/base/Checkbox.tsx @@ -0,0 +1,34 @@ +import * as checkbox from '@zag-js/checkbox' +import { normalizeProps, useMachine } from '@zag-js/solid' +import { createMemo, createUniqueId } from 'solid-js' + +interface Props { + initValue?: boolean + label: string + setValue: (v?: boolean) => void +} + +export const Checkbox = (props: Props) => { + const [state, send] = useMachine(checkbox.machine({ + id: createUniqueId(), + checked: props.initValue ?? false, + onChange(detail) { + props.setValue(detail.checked as boolean) + }, + })) + + const api = createMemo(() => checkbox.connect(state, send, normalizeProps)) + + return ( +