@lobehub/ui 6.0.0-beta.3 → 6.0.0-beta.5
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/es/Accordion/style.d.mts +1 -1
- package/es/Anchor/Anchor.mjs +2 -2
- package/es/Anchor/Anchor.mjs.map +1 -1
- package/es/AutoComplete/style.d.mts +1 -1
- package/es/Carousel/Carousel.mjs +1 -1
- package/es/Carousel/Carousel.mjs.map +1 -1
- package/es/CodeDiff/CodeDiff.mjs.map +1 -1
- package/es/CodeDiff/PatchDiff.mjs.map +1 -1
- package/es/CodeDiff/theme.mjs.map +1 -1
- package/es/CodeDiff/type.d.mts +2 -2
- package/es/CodeEditor/CodeEditor.mjs +1 -1
- package/es/CodeEditor/CodeEditor.mjs.map +1 -1
- package/es/ColorPicker/ColorPicker.mjs +1 -1
- package/es/ColorPicker/ColorPicker.mjs.map +1 -1
- package/es/ColorSwatches/ColorSwatches.mjs +1 -1
- package/es/ColorSwatches/ColorSwatches.mjs.map +1 -1
- package/es/ConfigProvider/cdnAssets.mjs +6 -0
- package/es/ConfigProvider/cdnAssets.mjs.map +1 -0
- package/es/ConfigProvider/index.mjs +2 -1
- package/es/ConfigProvider/index.mjs.map +1 -1
- package/es/ContextMenu/store.d.mts +0 -1
- package/es/ContextMenu/store.mjs.map +1 -1
- package/es/DatePicker/DatePicker.mjs +1 -1
- package/es/DatePicker/DatePicker.mjs.map +1 -1
- package/es/DatePicker/DateRangePicker.mjs +1 -1
- package/es/DatePicker/DateRangePicker.mjs.map +1 -1
- package/es/DraggablePanel/atoms.mjs +1 -1
- package/es/DraggablePanel/atoms.mjs.map +1 -1
- package/es/DraggableSideNav/DraggableSideNav.mjs +2 -2
- package/es/DraggableSideNav/DraggableSideNav.mjs.map +1 -1
- package/es/Drawer/atoms.mjs +1 -1
- package/es/Drawer/atoms.mjs.map +1 -1
- package/es/DropdownMenu/atoms.mjs +1 -1
- package/es/DropdownMenu/atoms.mjs.map +1 -1
- package/es/EditableText/ControlInput.mjs +42 -29
- package/es/EditableText/ControlInput.mjs.map +1 -1
- package/es/EditableText/EditableText.mjs +1 -1
- package/es/EditableText/EditableText.mjs.map +1 -1
- package/es/EmojiPicker/EmojiPicker.mjs +3 -9
- package/es/EmojiPicker/EmojiPicker.mjs.map +1 -1
- package/es/EmojiPicker/useEmojiMartI18n.mjs +37 -0
- package/es/EmojiPicker/useEmojiMartI18n.mjs.map +1 -0
- package/es/FormKit/engine/tanstack.mjs +1 -1
- package/es/FormKit/engine/tanstack.mjs.map +1 -1
- package/es/HotkeyInput/HotkeyInput.mjs +1 -1
- package/es/HotkeyInput/HotkeyInput.mjs.map +1 -1
- package/es/Image/viewer/ImageViewer.mjs +1 -1
- package/es/Image/viewer/ImageViewer.mjs.map +1 -1
- package/es/ImageSelect/ImageSelect.mjs +1 -1
- package/es/ImageSelect/ImageSelect.mjs.map +1 -1
- package/es/Input/Input.mjs +7 -5
- package/es/Input/Input.mjs.map +1 -1
- package/es/Input/TextArea.mjs +1 -1
- package/es/Input/TextArea.mjs.map +1 -1
- package/es/Modal/atoms.mjs +3 -3
- package/es/Modal/atoms.mjs.map +1 -1
- package/es/Pagination/Pagination.mjs +1 -1
- package/es/Pagination/Pagination.mjs.map +1 -1
- package/es/Popover/PopoverInGroup.mjs +1 -1
- package/es/Popover/PopoverInGroup.mjs.map +1 -1
- package/es/Popover/atoms.mjs +1 -1
- package/es/Popover/atoms.mjs.map +1 -1
- package/es/Rate/Rate.mjs +1 -1
- package/es/Rate/Rate.mjs.map +1 -1
- package/es/ScrollShadow/ScrollShadow.mjs +1 -1
- package/es/ScrollShadow/ScrollShadow.mjs.map +1 -1
- package/es/SearchBar/SearchBar.mjs +31 -25
- package/es/SearchBar/SearchBar.mjs.map +1 -1
- package/es/Segmented/Segmented.mjs +1 -1
- package/es/Segmented/Segmented.mjs.map +1 -1
- package/es/Select/atoms.mjs +1 -1
- package/es/Select/atoms.mjs.map +1 -1
- package/es/Switch/atoms.mjs +1 -1
- package/es/Switch/atoms.mjs.map +1 -1
- package/es/Tabs/Tabs.mjs +35 -21
- package/es/Tabs/Tabs.mjs.map +1 -1
- package/es/Text/style.d.mts +3 -3
- package/es/ToggleGroup/ToggleGroup.mjs +1 -1
- package/es/ToggleGroup/ToggleGroup.mjs.map +1 -1
- package/es/Tooltip/TooltipInGroup.mjs +1 -1
- package/es/Tooltip/TooltipInGroup.mjs.map +1 -1
- package/es/Tooltip/TooltipStandalone.mjs +1 -1
- package/es/Tooltip/TooltipStandalone.mjs.map +1 -1
- package/es/Tree/Tree.mjs +1 -1
- package/es/Tree/Tree.mjs.map +1 -1
- package/es/Video/context.mjs.map +1 -1
- package/es/Video/parts/Caption.mjs +4 -1
- package/es/Video/parts/Caption.mjs.map +1 -1
- package/es/Video/parts/Controls.mjs +6 -1
- package/es/Video/parts/Controls.mjs.map +1 -1
- package/es/Video/parts/Frame.mjs +9 -2
- package/es/Video/parts/Frame.mjs.map +1 -1
- package/es/awesome/Giscus/style.mjs +9 -9
- package/es/awesome/Giscus/style.mjs.map +1 -1
- package/es/awesome/GridBackground/GridBackground.mjs +1 -1
- package/es/awesome/GridBackground/GridBackground.mjs.map +1 -1
- package/es/awesome/GridBackground/GridShowcase.mjs +4 -4
- package/es/awesome/GridBackground/GridShowcase.mjs.map +1 -1
- package/es/awesome/Spline/ParentSize.mjs +1 -1
- package/es/awesome/Spline/ParentSize.mjs.map +1 -1
- package/es/chat/BackBottom/BackBottom.mjs +1 -1
- package/es/chat/BackBottom/BackBottom.mjs.map +1 -1
- package/es/chat/BackBottom/type.d.mts +2 -2
- package/es/chat/EditableMessage/EditableMessage.mjs +1 -1
- package/es/chat/EditableMessage/EditableMessage.mjs.map +1 -1
- package/es/chat/EditableMessageList/EditableMessageList.mjs +1 -1
- package/es/chat/EditableMessageList/EditableMessageList.mjs.map +1 -1
- package/es/chat/EditableMessageList/messageReducer.mjs +30 -23
- package/es/chat/EditableMessageList/messageReducer.mjs.map +1 -1
- package/es/chat/MessageModal/MessageModal.mjs +1 -1
- package/es/chat/MessageModal/MessageModal.mjs.map +1 -1
- package/es/chat/TokenTag/TokenTag.mjs +2 -3
- package/es/chat/TokenTag/TokenTag.mjs.map +1 -1
- package/es/hooks/useControlledState.mjs +20 -0
- package/es/hooks/useControlledState.mjs.map +1 -0
- package/es/hooks/useHighlight.mjs +2 -2
- package/es/hooks/useHighlight.mjs.map +1 -1
- package/es/hooks/useHover.mjs +22 -0
- package/es/hooks/useHover.mjs.map +1 -0
- package/es/hooks/useScroll.d.mts +8 -0
- package/es/hooks/useScroll.mjs +33 -0
- package/es/hooks/useScroll.mjs.map +1 -0
- package/es/hooks/useSize.mjs +26 -0
- package/es/hooks/useSize.mjs.map +1 -0
- package/es/internal/virtual/VirtualScrollArea.mjs +1 -1
- package/es/internal/virtual/VirtualScrollArea.mjs.map +1 -1
- package/es/mobile/ChatInputArea/ChatInputArea.mjs +1 -1
- package/es/mobile/ChatInputArea/ChatInputArea.mjs.map +1 -1
- package/es/mobile/TabBar/TabBar.mjs +1 -1
- package/es/mobile/TabBar/TabBar.mjs.map +1 -1
- package/es/node_modules/.pnpm/@eslint_core@1.2.1/node_modules/@eslint/core/dist/esm/types.d.mts +2 -46
- package/es/node_modules/.pnpm/@eslint_plugin-kit@0.7.3/node_modules/@eslint/plugin-kit/dist/cjs/index.d.mts +1 -1
- package/es/node_modules/.pnpm/@types_estree@1.0.9/node_modules/@types/estree/index.d.mts +1 -4
- package/es/node_modules/.pnpm/eslint@10.12.0_jiti@2.6.1_supports-color@10.2.2/node_modules/eslint/lib/types/index.d.mts +1 -1
- package/es/styles/theme/token/neutral.mjs +5 -5
- package/es/styles/theme/token/neutral.mjs.map +1 -1
- package/es/utils/colorMath.mjs +52 -0
- package/es/utils/colorMath.mjs.map +1 -0
- package/es/utils/formatNumber.mjs +7 -0
- package/es/utils/formatNumber.mjs.map +1 -0
- package/es/utils/genCdnUrl.mjs +6 -3
- package/es/utils/genCdnUrl.mjs.map +1 -1
- package/es/utils/hashString.mjs +19 -0
- package/es/utils/hashString.mjs.map +1 -0
- package/es/utils/mergeRefs.mjs +20 -0
- package/es/utils/mergeRefs.mjs.map +1 -0
- package/es/utils/placement.d.mts +5 -5
- package/es/utils/placement.mjs.map +1 -1
- package/package.json +16 -42
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import { useControlledState } from "../../hooks/useControlledState.mjs";
|
|
2
3
|
import Markdown from "../../Markdown/Markdown.mjs";
|
|
3
4
|
import MessageInput from "../MessageInput/MessageInput.mjs";
|
|
4
5
|
import MessageModal from "../MessageModal/MessageModal.mjs";
|
|
5
6
|
import { memo } from "react";
|
|
6
7
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
7
8
|
import clsx from "clsx";
|
|
8
|
-
import useControlledState from "use-merge-value";
|
|
9
9
|
//#region src/chat/EditableMessage/EditableMessage.tsx
|
|
10
10
|
const EditableMessage = memo(({ value, onChange, classNames = {}, onEditingChange, editing, openModal, onOpenChange, placeholder, showEditWhenEmpty = false, styles: customStyles, className, style, height, variant, editButtonSize, text, fullFeaturedCodeBlock, model, fontSize, language = "markdown", markdownProps }) => {
|
|
11
11
|
const [isEdit, setTyping] = useControlledState(false, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EditableMessage.mjs","names":[],"sources":["../../../src/chat/EditableMessage/EditableMessage.tsx"],"sourcesContent":["'use client';\n\nimport clsx from 'clsx';\nimport { memo } from 'react';\nimport
|
|
1
|
+
{"version":3,"file":"EditableMessage.mjs","names":[],"sources":["../../../src/chat/EditableMessage/EditableMessage.tsx"],"sourcesContent":["'use client';\n\nimport clsx from 'clsx';\nimport { memo } from 'react';\n\nimport MessageInput from '@/chat/MessageInput';\nimport MessageModal from '@/chat/MessageModal';\nimport { useControlledState } from '@/hooks/useControlledState';\nimport Markdown from '@/Markdown';\n\nimport { type EditableMessageProps } from './type';\n\nconst EditableMessage = memo<EditableMessageProps>(\n ({\n value,\n onChange,\n classNames = {},\n onEditingChange,\n editing,\n openModal,\n onOpenChange,\n placeholder,\n showEditWhenEmpty = false,\n styles: customStyles,\n className,\n style,\n height,\n variant,\n editButtonSize,\n text,\n fullFeaturedCodeBlock,\n model,\n fontSize,\n language = 'markdown',\n markdownProps,\n }) => {\n const [isEdit, setTyping] = useControlledState(false, {\n onChange: onEditingChange,\n value: editing,\n });\n\n const [expand, setExpand] = useControlledState<boolean>(false, {\n onChange: onOpenChange,\n value: openModal,\n });\n\n const isAutoSize = height === 'auto';\n\n const input = (\n <MessageInput\n shortcut\n className={clsx(className, classNames?.input)}\n classNames={classNames}\n defaultValue={value}\n editButtonSize={editButtonSize}\n height={height}\n language={language}\n placeholder={placeholder}\n styles={customStyles}\n text={text}\n variant={variant}\n style={{\n ...style,\n ...customStyles?.input,\n }}\n onCancel={() => setTyping(false)}\n onConfirm={(text) => {\n onChange?.(text);\n setTyping(false);\n }}\n />\n );\n\n if (!value && showEditWhenEmpty) return input;\n\n return (\n <>\n {!expand && isEdit ? (\n input\n ) : (\n <Markdown\n className={clsx(className, classNames?.markdown)}\n fontSize={fontSize}\n fullFeaturedCodeBlock={fullFeaturedCodeBlock}\n variant={'chat'}\n style={{\n height: isAutoSize ? 'unset' : height,\n ...style,\n ...customStyles?.markdown,\n }}\n {...markdownProps}\n >\n {value || placeholder || ''}\n </Markdown>\n )}\n {expand && (\n <MessageModal\n editing={isEdit}\n extra={model?.extra}\n footer={model?.footer}\n height={height}\n language={language}\n open={expand}\n placeholder={placeholder}\n text={text}\n value={value}\n onChange={onChange}\n onEditingChange={setTyping}\n onOpenChange={(e) => {\n setExpand(e);\n setTyping(false);\n }}\n />\n )}\n </>\n );\n },\n);\n\nEditableMessage.displayName = 'EditableMessage';\n\nexport default EditableMessage;\n"],"mappings":";;;;;;;;;AAYA,MAAM,kBAAkB,MACrB,EACC,OACA,UACA,aAAa,CAAC,GACd,iBACA,SACA,WACA,cACA,aACA,oBAAoB,OACpB,QAAQ,cACR,WACA,OACA,QACA,SACA,gBACA,MACA,uBACA,OACA,UACA,WAAW,YACX,oBACI;CACJ,MAAM,CAAC,QAAQ,aAAa,mBAAmB,OAAO;EACpD,UAAU;EACV,OAAO;CACT,CAAC;CAED,MAAM,CAAC,QAAQ,aAAa,mBAA4B,OAAO;EAC7D,UAAU;EACV,OAAO;CACT,CAAC;CAED,MAAM,aAAa,WAAW;CAE9B,MAAM,QACJ,oBAAC,cAAD;EACE,UAAA;EACA,WAAW,KAAK,WAAW,YAAY,KAAK;EAChC;EACZ,cAAc;EACE;EACR;EACE;EACG;EACb,QAAQ;EACF;EACG;EACT,OAAO;GACL,GAAG;GACH,GAAG,cAAc;EACnB;EACA,gBAAgB,UAAU,KAAK;EAC/B,YAAY,SAAS;GACnB,WAAW,IAAI;GACf,UAAU,KAAK;EACjB;CACD,CAAA;CAGH,IAAI,CAAC,SAAS,mBAAmB,OAAO;CAExC,OACE,qBAAA,YAAA,EAAA,UAAA,CACG,CAAC,UAAU,SACV,QAEA,oBAAC,UAAD;EACE,WAAW,KAAK,WAAW,YAAY,QAAQ;EACrC;EACa;EACvB,SAAS;EACT,OAAO;GACL,QAAQ,aAAa,UAAU;GAC/B,GAAG;GACH,GAAG,cAAc;EACnB;EACA,GAAI;EAEH,UAAA,SAAS,eAAe;CACjB,CAAA,GAEX,UACC,oBAAC,cAAD;EACE,SAAS;EACT,OAAO,OAAO;EACd,QAAQ,OAAO;EACP;EACE;EACV,MAAM;EACO;EACP;EACC;EACG;EACV,iBAAiB;EACjB,eAAe,MAAM;GACnB,UAAU,CAAC;GACX,UAAU,KAAK;EACjB;CACD,CAAA,CAEH,EAAA,CAAA;AAEN,CACF;AAEA,gBAAgB,cAAc"}
|
|
@@ -10,7 +10,7 @@ import { messagesReducer } from "./messageReducer.mjs";
|
|
|
10
10
|
import { memo, useEffect, useReducer } from "react";
|
|
11
11
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
12
12
|
import { Plus, Trash } from "lucide-react";
|
|
13
|
-
import isEqual from "
|
|
13
|
+
import { isEqual } from "es-toolkit/compat";
|
|
14
14
|
//#region src/chat/EditableMessageList/EditableMessageList.tsx
|
|
15
15
|
const EditableMessageList = memo(({ disabled, dataSources, onChange, texts }) => {
|
|
16
16
|
const [chatMessages, dispatch] = useReducer(messagesReducer, dataSources);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EditableMessageList.mjs","names":["editableMessageMessages","Flexbox"],"sources":["../../../src/chat/EditableMessageList/EditableMessageList.tsx"],"sourcesContent":["'use client';\n\nimport isEqual from '
|
|
1
|
+
{"version":3,"file":"EditableMessageList.mjs","names":["editableMessageMessages","Flexbox"],"sources":["../../../src/chat/EditableMessageList/EditableMessageList.tsx"],"sourcesContent":["'use client';\n\nimport { isEqual } from 'es-toolkit/compat';\nimport { Plus, Trash } from 'lucide-react';\nimport { memo, useEffect, useReducer } from 'react';\n\nimport ActionIcon from '@/ActionIcon';\nimport Button from '@/Button';\nimport ControlInput from '@/EditableText/ControlInput';\nimport { Flexbox } from '@/Flex';\nimport editableMessageMessages from '@/i18n/resources/en/editableMessage';\nimport { useTranslation } from '@/i18n/useTranslation';\nimport Select from '@/Select';\n\nimport { messagesReducer } from './messageReducer';\nimport { type EditableMessageListProps } from './type';\n\nconst EditableMessageList = memo<EditableMessageListProps>(\n ({ disabled, dataSources, onChange, texts }) => {\n const [chatMessages, dispatch] = useReducer(messagesReducer, dataSources);\n const { t } = useTranslation(editableMessageMessages);\n\n const addPropsText = texts?.addProps ?? t('editableMessage.addProps');\n const deleteText = texts?.delete ?? t('editableMessage.delete');\n const inputPlaceholderText = texts?.inputPlaceholder ?? t('editableMessage.inputPlaceholder');\n const outputPlaceholderText =\n texts?.outputPlaceholder ?? t('editableMessage.outputPlaceholder');\n const systemText = texts?.system ?? t('editableMessage.system');\n const inputText = texts?.input ?? t('editableMessage.input');\n const outputText = texts?.output ?? t('editableMessage.output');\n\n useEffect(() => {\n if (!isEqual(dataSources, chatMessages)) {\n onChange?.(chatMessages);\n }\n }, [chatMessages]);\n\n return dataSources ? (\n <Flexbox gap={12}>\n {chatMessages.map((item, index) => (\n <Flexbox\n horizontal\n align={'center'}\n gap={8}\n key={`${index}-${item.content}`}\n width={'100%'}\n >\n <Select\n disabled={disabled}\n style={{ width: 120 }}\n value={item.role}\n options={[\n { label: systemText, value: 'system' },\n { label: inputText, value: 'user' },\n { label: outputText, value: 'assistant' },\n ]}\n onChange={(value) => {\n dispatch({ index, role: value, type: 'updateMessageRole' });\n }}\n />\n <ControlInput\n disabled={disabled}\n placeholder={item.role === 'user' ? inputPlaceholderText : outputPlaceholderText}\n value={item.content}\n onChange={(e) => {\n dispatch({ index, message: e, type: 'updateMessage' });\n }}\n />\n <ActionIcon\n icon={Trash}\n title={deleteText}\n variant={'filled'}\n onClick={() => {\n dispatch({ index, type: 'deleteMessage' });\n }}\n />\n </Flexbox>\n ))}\n\n <Button\n block\n disabled={disabled}\n icon={Plus}\n onClick={() => {\n const lastMeg = chatMessages.at(-1);\n\n dispatch({\n message: { content: '', role: lastMeg?.role === 'user' ? 'assistant' : 'user' },\n type: 'addMessage',\n });\n }}\n >\n {addPropsText}\n </Button>\n </Flexbox>\n ) : undefined;\n },\n isEqual,\n);\n\nEditableMessageList.displayName = 'EditableMessageList';\n\nexport default EditableMessageList;\n"],"mappings":";;;;;;;;;;;;;;AAiBA,MAAM,sBAAsB,MACzB,EAAE,UAAU,aAAa,UAAU,YAAY;CAC9C,MAAM,CAAC,cAAc,YAAY,WAAW,iBAAiB,WAAW;CACxE,MAAM,EAAE,MAAM,eAAeA,uBAAuB;CAEpD,MAAM,eAAe,OAAO,YAAY,EAAE,0BAA0B;CACpE,MAAM,aAAa,OAAO,UAAU,EAAE,wBAAwB;CAC9D,MAAM,uBAAuB,OAAO,oBAAoB,EAAE,kCAAkC;CAC5F,MAAM,wBACJ,OAAO,qBAAqB,EAAE,mCAAmC;CACnE,MAAM,aAAa,OAAO,UAAU,EAAE,wBAAwB;CAC9D,MAAM,YAAY,OAAO,SAAS,EAAE,uBAAuB;CAC3D,MAAM,aAAa,OAAO,UAAU,EAAE,wBAAwB;CAE9D,gBAAgB;EACd,IAAI,CAAC,QAAQ,aAAa,YAAY,GACpC,WAAW,YAAY;CAE3B,GAAG,CAAC,YAAY,CAAC;CAEjB,OAAO,cACL,qBAACC,mBAAD;EAAS,KAAK;EAAd,UAAA,CACG,aAAa,KAAK,MAAM,UACvB,qBAACA,mBAAD;GACE,YAAA;GACA,OAAO;GACP,KAAK;GAEL,OAAO;GALT,UAAA;IAOE,oBAAC,QAAD;KACY;KACV,OAAO,EAAE,OAAO,IAAI;KACpB,OAAO,KAAK;KACZ,SAAS;MACP;OAAE,OAAO;OAAY,OAAO;MAAS;MACrC;OAAE,OAAO;OAAW,OAAO;MAAO;MAClC;OAAE,OAAO;OAAY,OAAO;MAAY;KAC1C;KACA,WAAW,UAAU;MACnB,SAAS;OAAE;OAAO,MAAM;OAAO,MAAM;MAAoB,CAAC;KAC5D;IACD,CAAA;IACD,oBAAC,cAAD;KACY;KACV,aAAa,KAAK,SAAS,SAAS,uBAAuB;KAC3D,OAAO,KAAK;KACZ,WAAW,MAAM;MACf,SAAS;OAAE;OAAO,SAAS;OAAG,MAAM;MAAgB,CAAC;KACvD;IACD,CAAA;IACD,oBAAC,YAAD;KACE,MAAM;KACN,OAAO;KACP,SAAS;KACT,eAAe;MACb,SAAS;OAAE;OAAO,MAAM;MAAgB,CAAC;KAC3C;IACD,CAAA;GACM;EAhCF,GAAA,GAAG,MAAM,GAAG,KAAK,SAgCf,CACV,GAED,oBAAC,QAAD;GACE,OAAA;GACU;GACV,MAAM;GACN,eAAe;IACb,MAAM,UAAU,aAAa,GAAG,EAAE;IAElC,SAAS;KACP,SAAS;MAAE,SAAS;MAAI,MAAM,SAAS,SAAS,SAAS,cAAc;KAAO;KAC9E,MAAM;IACR,CAAC;GACH;GAEC,UAAA;EACK,CAAA,CACD;CACP,CAAA,IAAA,KAAA;AACN,GACA,OACF;AAEA,oBAAoB,cAAc"}
|
|
@@ -1,39 +1,46 @@
|
|
|
1
|
-
import { produce } from "immer";
|
|
2
1
|
//#region src/chat/EditableMessageList/messageReducer.ts
|
|
3
2
|
const messagesReducer = (state, payload) => {
|
|
4
3
|
switch (payload.type) {
|
|
5
4
|
case "addMessage": return [...state, payload.message];
|
|
6
|
-
case "insertMessage": return
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
case "insertMessage": return [
|
|
6
|
+
...state.slice(0, payload.index),
|
|
7
|
+
payload.message,
|
|
8
|
+
...state.slice(payload.index)
|
|
9
|
+
];
|
|
9
10
|
case "deleteMessage": return state.filter((_, index) => index !== payload.index);
|
|
10
11
|
case "resetMessages": return [];
|
|
11
|
-
case "updateMessage":
|
|
12
|
+
case "updateMessage": {
|
|
12
13
|
const { index, message } = payload;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
return state.map((item, i) => i === index ? {
|
|
15
|
+
...item,
|
|
16
|
+
content: message
|
|
17
|
+
} : item);
|
|
18
|
+
}
|
|
19
|
+
case "updateMessageRole": {
|
|
16
20
|
const { index, role } = payload;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
return state.map((item, i) => i === index ? {
|
|
22
|
+
...item,
|
|
23
|
+
role
|
|
24
|
+
} : item);
|
|
25
|
+
}
|
|
26
|
+
case "addUserMessage": return [...state, {
|
|
27
|
+
content: payload.message,
|
|
28
|
+
role: "user"
|
|
29
|
+
}];
|
|
30
|
+
case "updateLatestBotMessage": {
|
|
27
31
|
const newMessage = {
|
|
28
|
-
content: responseStream.join(""),
|
|
32
|
+
content: payload.responseStream.join(""),
|
|
29
33
|
role: "assistant"
|
|
30
34
|
};
|
|
31
35
|
return [...state.slice(0, -1), newMessage];
|
|
32
|
-
}
|
|
33
|
-
case "setErrorMessage":
|
|
36
|
+
}
|
|
37
|
+
case "setErrorMessage": {
|
|
34
38
|
const { index, error } = payload;
|
|
35
|
-
|
|
36
|
-
|
|
39
|
+
return state.map((item, i) => i === index ? {
|
|
40
|
+
...item,
|
|
41
|
+
error
|
|
42
|
+
} : item);
|
|
43
|
+
}
|
|
37
44
|
default: throw new Error("暂未实现的 type,请检查 reducer");
|
|
38
45
|
}
|
|
39
46
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messageReducer.mjs","names":[],"sources":["../../../src/chat/EditableMessageList/messageReducer.ts"],"sourcesContent":["import
|
|
1
|
+
{"version":3,"file":"messageReducer.mjs","names":[],"sources":["../../../src/chat/EditableMessageList/messageReducer.ts"],"sourcesContent":["import type { ChatMessageError, LLMMessage, LLMRoleType } from '@/chat/types';\n\nexport type MessageDispatch =\n | { message: LLMMessage; type: 'addMessage' }\n | { index: number; message: LLMMessage; type: 'insertMessage' }\n | { index: number; type: 'deleteMessage' }\n | { type: 'resetMessages' }\n | { index: number; message: string; type: 'updateMessage' }\n | { index: number; role: LLMRoleType; type: 'updateMessageRole' }\n //\n | { message: string; type: 'addUserMessage' }\n | { responseStream: string[]; type: 'updateLatestBotMessage' }\n | { index: number; message: string; type: 'updateMessageChoice' }\n | {\n error: ChatMessageError | undefined;\n index: number;\n type: 'setErrorMessage';\n };\n\nexport const messagesReducer = (state: LLMMessage[], payload: MessageDispatch): LLMMessage[] => {\n switch (payload.type) {\n case 'addMessage': {\n return [...state, payload.message];\n }\n\n case 'insertMessage': {\n return [...state.slice(0, payload.index), payload.message, ...state.slice(payload.index)];\n }\n\n case 'deleteMessage': {\n return state.filter((_, index) => index !== payload.index);\n }\n case 'resetMessages': {\n return [];\n }\n\n case 'updateMessage': {\n const { index, message } = payload;\n return state.map((item, i) => (i === index ? { ...item, content: message } : item));\n }\n case 'updateMessageRole': {\n const { index, role } = payload;\n return state.map((item, i) => (i === index ? { ...item, role } : item));\n }\n\n case 'addUserMessage': {\n return [...state, { content: payload.message, role: 'user' }];\n }\n\n case 'updateLatestBotMessage': {\n const newMessage: LLMMessage = {\n content: payload.responseStream.join(''),\n role: 'assistant',\n };\n return [...state.slice(0, -1), newMessage];\n }\n\n case 'setErrorMessage': {\n const { index, error } = payload;\n return state.map((item, i) => (i === index ? ({ ...item, error } as LLMMessage) : item));\n }\n\n default: {\n throw new Error('暂未实现的 type,请检查 reducer');\n }\n }\n};\n"],"mappings":";AAmBA,MAAa,mBAAmB,OAAqB,YAA2C;CAC9F,QAAQ,QAAQ,MAAhB;EACE,KAAK,cACH,OAAO,CAAC,GAAG,OAAO,QAAQ,OAAO;EAGnC,KAAK,iBACH,OAAO;GAAC,GAAG,MAAM,MAAM,GAAG,QAAQ,KAAK;GAAG,QAAQ;GAAS,GAAG,MAAM,MAAM,QAAQ,KAAK;EAAC;EAG1F,KAAK,iBACH,OAAO,MAAM,QAAQ,GAAG,UAAU,UAAU,QAAQ,KAAK;EAE3D,KAAK,iBACH,OAAO,CAAC;EAGV,KAAK,iBAAiB;GACpB,MAAM,EAAE,OAAO,YAAY;GAC3B,OAAO,MAAM,KAAK,MAAM,MAAO,MAAM,QAAQ;IAAE,GAAG;IAAM,SAAS;GAAQ,IAAI,IAAK;EACpF;EACA,KAAK,qBAAqB;GACxB,MAAM,EAAE,OAAO,SAAS;GACxB,OAAO,MAAM,KAAK,MAAM,MAAO,MAAM,QAAQ;IAAE,GAAG;IAAM;GAAK,IAAI,IAAK;EACxE;EAEA,KAAK,kBACH,OAAO,CAAC,GAAG,OAAO;GAAE,SAAS,QAAQ;GAAS,MAAM;EAAO,CAAC;EAG9D,KAAK,0BAA0B;GAC7B,MAAM,aAAyB;IAC7B,SAAS,QAAQ,eAAe,KAAK,EAAE;IACvC,MAAM;GACR;GACA,OAAO,CAAC,GAAG,MAAM,MAAM,GAAG,EAAE,GAAG,UAAU;EAC3C;EAEA,KAAK,mBAAmB;GACtB,MAAM,EAAE,OAAO,UAAU;GACzB,OAAO,MAAM,KAAK,MAAM,MAAO,MAAM,QAAS;IAAE,GAAG;IAAM;GAAM,IAAmB,IAAK;EACzF;EAEA,SACE,MAAM,IAAI,MAAM,wBAAwB;CAE5C;AACF"}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import Button from "../../Button/Button.mjs";
|
|
3
3
|
import FlexBasic_default from "../../Flex/FlexBasic.mjs";
|
|
4
4
|
import { useResponsive } from "../../styles/theme/scope.mjs";
|
|
5
|
+
import { useControlledState } from "../../hooks/useControlledState.mjs";
|
|
5
6
|
import { useTranslation } from "../../i18n/useTranslation.mjs";
|
|
6
7
|
import TextArea_default from "../../Input/TextArea.mjs";
|
|
7
8
|
import CodeEditor from "../../CodeEditor/CodeEditor.mjs";
|
|
@@ -11,7 +12,6 @@ import { styles, textAreaStyle } from "../MessageInput/style.mjs";
|
|
|
11
12
|
import messageModal_default from "../../i18n/resources/en/messageModal.mjs";
|
|
12
13
|
import { memo, useState } from "react";
|
|
13
14
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
14
|
-
import useControlledState from "use-merge-value";
|
|
15
15
|
//#region src/chat/MessageModal/MessageModal.tsx
|
|
16
16
|
const MessageModal = memo(({ editing, open, height = "75vh", onOpenChange, onEditingChange, placeholder, value, language = "markdown", onChange, text, footer, extra }) => {
|
|
17
17
|
const { mobile } = useResponsive();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageModal.mjs","names":["messageModalMessages","Flexbox","TextArea","textStyles"],"sources":["../../../src/chat/MessageModal/MessageModal.tsx"],"sourcesContent":["'use client';\n\nimport { memo, useState } from 'react';\
|
|
1
|
+
{"version":3,"file":"MessageModal.mjs","names":["messageModalMessages","Flexbox","TextArea","textStyles"],"sources":["../../../src/chat/MessageModal/MessageModal.tsx"],"sourcesContent":["'use client';\n\nimport { memo, useState } from 'react';\n\nimport Button from '@/Button';\nimport { styles as textStyles, textAreaStyle } from '@/chat/MessageInput/style';\nimport CodeEditor from '@/CodeEditor';\nimport { Flexbox } from '@/Flex';\nimport { useControlledState } from '@/hooks/useControlledState';\nimport messageModalMessages from '@/i18n/resources/en/messageModal';\nimport { useTranslation } from '@/i18n/useTranslation';\nimport { TextArea } from '@/Input';\nimport Markdown from '@/Markdown';\nimport { Modal } from '@/Modal';\nimport { useResponsive } from '@/styles/theme/scope';\n\nimport { type MessageModalProps } from './type';\n\nconst MessageModal = memo<MessageModalProps>(\n ({\n editing,\n open,\n height = '75vh',\n onOpenChange,\n onEditingChange,\n placeholder,\n value,\n language = 'markdown',\n onChange,\n text,\n footer,\n extra,\n }) => {\n const { mobile } = useResponsive();\n const { t } = useTranslation(messageModalMessages);\n const [isEdit, setTyping] = useControlledState(false, {\n onChange: onEditingChange,\n value: editing,\n });\n\n const [showModal, setShowModal] = useControlledState(false, {\n onChange: onOpenChange,\n value: open,\n });\n\n const [temporaryValue, setMessage] = useState(value);\n\n const confirmText = text?.confirm ?? t('messageModal.confirm');\n const cancelText = text?.cancel ?? t('messageModal.cancel');\n const editText = text?.edit ?? t('messageModal.edit');\n\n const modalFooter = isEdit ? (\n <Flexbox direction={'horizontal-reverse'} gap={8}>\n <Button\n type=\"primary\"\n onClick={() => {\n setTyping(false);\n onChange?.(temporaryValue);\n setMessage(value);\n }}\n >\n {confirmText}\n </Button>\n <Button\n onClick={() => {\n setTyping(false);\n setMessage(value);\n }}\n >\n {cancelText}\n </Button>\n </Flexbox>\n ) : (\n footer\n );\n\n return (\n <Modal\n allowFullscreen\n destroyOnHidden\n cancelText={cancelText}\n footer={modalFooter}\n height={height}\n okText={editText}\n open={showModal}\n title={text?.title}\n onOk={() => setTyping(true)}\n onCancel={() => {\n setShowModal(false);\n setTyping(false);\n setMessage(value);\n }}\n >\n {isEdit ? (\n mobile ? (\n <TextArea\n autoSize\n className={textStyles}\n defaultValue={temporaryValue}\n placeholder={placeholder}\n style={textAreaStyle}\n value={temporaryValue}\n variant={'borderless'}\n onBlur={(e) => setMessage(e.target.value)}\n onChange={(value) => setMessage(value.target.value)}\n />\n ) : (\n <CodeEditor\n className={textStyles}\n defaultValue={temporaryValue}\n language={language}\n placeholder={placeholder}\n value={temporaryValue}\n variant={'borderless'}\n onBlur={(e) => setMessage(e.target.value)}\n onValueChange={(value) => setMessage(value)}\n />\n )\n ) : (\n <>\n {extra}\n <Markdown variant={'chat'}>{String(value || placeholder)}</Markdown>\n </>\n )}\n </Modal>\n );\n },\n);\n\nMessageModal.displayName = 'MessageModal';\n\nexport default MessageModal;\n"],"mappings":";;;;;;;;;;;;;;;AAkBA,MAAM,eAAe,MAClB,EACC,SACA,MACA,SAAS,QACT,cACA,iBACA,aACA,OACA,WAAW,YACX,UACA,MACA,QACA,YACI;CACJ,MAAM,EAAE,WAAW,cAAc;CACjC,MAAM,EAAE,MAAM,eAAeA,oBAAoB;CACjD,MAAM,CAAC,QAAQ,aAAa,mBAAmB,OAAO;EACpD,UAAU;EACV,OAAO;CACT,CAAC;CAED,MAAM,CAAC,WAAW,gBAAgB,mBAAmB,OAAO;EAC1D,UAAU;EACV,OAAO;CACT,CAAC;CAED,MAAM,CAAC,gBAAgB,cAAc,SAAS,KAAK;CAEnD,MAAM,cAAc,MAAM,WAAW,EAAE,sBAAsB;CAC7D,MAAM,aAAa,MAAM,UAAU,EAAE,qBAAqB;CAC1D,MAAM,WAAW,MAAM,QAAQ,EAAE,mBAAmB;CA2BpD,OACE,oBAAC,OAAD;EACE,iBAAA;EACA,iBAAA;EACY;EACZ,QA9BgB,SAClB,qBAACC,mBAAD;GAAS,WAAW;GAAsB,KAAK;GAA/C,UAAA,CACE,oBAAC,QAAD;IACE,MAAK;IACL,eAAe;KACb,UAAU,KAAK;KACf,WAAW,cAAc;KACzB,WAAW,KAAK;IAClB;IAEC,UAAA;GACK,CAAA,GACR,oBAAC,QAAD;IACE,eAAe;KACb,UAAU,KAAK;KACf,WAAW,KAAK;IAClB;IAEC,UAAA;GACK,CAAA,CACD;EAET,CAAA,IAAA;EASU;EACR,QAAQ;EACR,MAAM;EACN,OAAO,MAAM;EACb,YAAY,UAAU,IAAI;EAC1B,gBAAgB;GACd,aAAa,KAAK;GAClB,UAAU,KAAK;GACf,WAAW,KAAK;EAClB;EAEC,UAAA,SACC,SACE,oBAACC,kBAAD;GACE,UAAA;GACA,WAAWC;GACX,cAAc;GACD;GACb,OAAO;GACP,OAAO;GACP,SAAS;GACT,SAAS,MAAM,WAAW,EAAE,OAAO,KAAK;GACxC,WAAW,UAAU,WAAW,MAAM,OAAO,KAAK;EACnD,CAAA,IAED,oBAAC,YAAD;GACE,WAAWA;GACX,cAAc;GACJ;GACG;GACb,OAAO;GACP,SAAS;GACT,SAAS,MAAM,WAAW,EAAE,OAAO,KAAK;GACxC,gBAAgB,UAAU,WAAW,KAAK;EAC3C,CAAA,IAGH,qBAAA,YAAA,EAAA,UAAA,CACG,OACD,oBAAC,UAAD;GAAU,SAAS;GAAS,UAAA,OAAO,SAAS,WAAW;EAAY,CAAA,CACnE,EAAA,CAAA;CAEC,CAAA;AAEX,CACF;AAEA,aAAa,cAAc"}
|
|
@@ -4,11 +4,10 @@ import ActionIcon from "../../ActionIcon/ActionIcon.mjs";
|
|
|
4
4
|
import { useTranslation } from "../../i18n/useTranslation.mjs";
|
|
5
5
|
import Progress from "../../Progress/Progress.mjs";
|
|
6
6
|
import chat_default from "../../i18n/resources/en/chat.mjs";
|
|
7
|
+
import { formatNumber } from "../../utils/formatNumber.mjs";
|
|
7
8
|
import { useMemo } from "react";
|
|
8
9
|
import { jsx } from "react/jsx-runtime";
|
|
9
|
-
import numeral from "numeral";
|
|
10
10
|
//#region src/chat/TokenTag/TokenTag.tsx
|
|
11
|
-
const format = (number) => numeral(number).format("0,0");
|
|
12
11
|
const TokenTag = ({ mode = "remained", maxValue, value, text, showInfo, size = 20, ...rest }) => {
|
|
13
12
|
const { t } = useTranslation(chat_default);
|
|
14
13
|
const valueLeft = maxValue - value;
|
|
@@ -34,7 +33,7 @@ const TokenTag = ({ mode = "remained", maxValue, value, text, showInfo, size = 2
|
|
|
34
33
|
type
|
|
35
34
|
};
|
|
36
35
|
}, [percent]);
|
|
37
|
-
const title = valueLeft > 0 ? [mode === "remained" ? remainedText : usedText, mode === "remained" ?
|
|
36
|
+
const title = valueLeft > 0 ? [mode === "remained" ? remainedText : usedText, mode === "remained" ? formatNumber(valueLeft) : formatNumber(value)].join(" ") : overloadText;
|
|
38
37
|
return /* @__PURE__ */ jsx(ActionIcon, {
|
|
39
38
|
size,
|
|
40
39
|
title: showInfo ? title : void 0,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TokenTag.mjs","names":["chatMessages"],"sources":["../../../src/chat/TokenTag/TokenTag.tsx"],"sourcesContent":["'use client';\n\nimport
|
|
1
|
+
{"version":3,"file":"TokenTag.mjs","names":["chatMessages"],"sources":["../../../src/chat/TokenTag/TokenTag.tsx"],"sourcesContent":["'use client';\n\nimport { type FC, useMemo } from 'react';\n\nimport ActionIcon from '@/ActionIcon';\nimport chatMessages from '@/i18n/resources/en/chat';\nimport { useTranslation } from '@/i18n/useTranslation';\nimport Progress from '@/Progress';\nimport { cssVar } from '@/styles/stylex/cssVar.stylex';\nimport { formatNumber } from '@/utils/formatNumber';\n\nimport type { TokenTagProps } from './type';\n\nconst TokenTag: FC<TokenTagProps> = ({\n mode = 'remained',\n maxValue,\n value,\n text,\n showInfo,\n size = 20,\n ...rest\n}) => {\n const { t } = useTranslation(chatMessages);\n const valueLeft = maxValue - value;\n const percent = value / maxValue;\n const remainedText = text?.remained ?? t('tokenTag.remained');\n const usedText = text?.used ?? t('tokenTag.used');\n const overloadText = text?.overload ?? t('tokenTag.overload');\n\n const data = useMemo(() => {\n let type: 'normal' | 'low' | 'overload';\n let color;\n\n if (percent < 0.7) {\n type = 'normal';\n color = cssVar.colorText;\n } else if (percent < 0.9) {\n type = 'low';\n color = cssVar.colorWarning;\n } else {\n type = 'overload';\n color = cssVar.colorError;\n }\n return {\n color,\n type,\n };\n }, [percent]);\n\n const title =\n valueLeft > 0\n ? [\n mode === 'remained' ? remainedText : usedText,\n mode === 'remained' ? formatNumber(valueLeft) : formatNumber(value),\n ].join(' ')\n : overloadText;\n\n return (\n <ActionIcon\n size={size}\n title={showInfo ? title : undefined}\n icon={\n <Progress\n percent={percent * 100}\n showInfo={false}\n size={Number(typeof size === 'object' ? size?.size || 20 : size) || 20}\n strokeColor={data.color}\n type=\"circle\"\n />\n }\n {...rest}\n />\n );\n};\n\nTokenTag.displayName = 'TokenTag';\n\nexport default TokenTag;\n"],"mappings":";;;;;;;;;;AAaA,MAAM,YAA+B,EACnC,OAAO,YACP,UACA,OACA,MACA,UACA,OAAO,IACP,GAAG,WACC;CACJ,MAAM,EAAE,MAAM,eAAeA,YAAY;CACzC,MAAM,YAAY,WAAW;CAC7B,MAAM,UAAU,QAAQ;CACxB,MAAM,eAAe,MAAM,YAAY,EAAE,mBAAmB;CAC5D,MAAM,WAAW,MAAM,QAAQ,EAAE,eAAe;CAChD,MAAM,eAAe,MAAM,YAAY,EAAE,mBAAmB;CAE5D,MAAM,OAAO,cAAc;EACzB,IAAI;EACJ,IAAI;EAEJ,IAAI,UAAU,IAAK;GACjB,OAAO;GACP,QAAQ,OAAO;EACjB,OAAO,IAAI,UAAU,IAAK;GACxB,OAAO;GACP,QAAQ,OAAO;EACjB,OAAO;GACL,OAAO;GACP,QAAQ,OAAO;EACjB;EACA,OAAO;GACL;GACA;EACF;CACF,GAAG,CAAC,OAAO,CAAC;CAEZ,MAAM,QACJ,YAAY,IACR,CACE,SAAS,aAAa,eAAe,UACrC,SAAS,aAAa,aAAa,SAAS,IAAI,aAAa,KAAK,CACpE,CAAC,CAAC,KAAK,GAAG,IACV;CAEN,OACE,oBAAC,YAAD;EACQ;EACN,OAAO,WAAW,QAAQ,KAAA;EAC1B,MACE,oBAAC,UAAD;GACE,SAAS,UAAU;GACnB,UAAU;GACV,MAAM,OAAO,OAAO,SAAS,WAAW,MAAM,QAAQ,KAAK,IAAI,KAAK;GACpE,aAAa,KAAK;GAClB,MAAK;EACN,CAAA;EAEH,GAAI;CACL,CAAA;AAEL;AAEA,SAAS,cAAc"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { useEventCallback } from "./useEventCallback.mjs";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
//#region src/hooks/useControlledState.ts
|
|
4
|
+
const resolveInitial = (initial) => typeof initial === "function" ? initial() : initial;
|
|
5
|
+
const useControlledState = (defaultStateValue, { defaultValue, onChange, value } = {}) => {
|
|
6
|
+
const [innerValue, setInnerValue] = useState(() => {
|
|
7
|
+
if (value !== void 0) return value;
|
|
8
|
+
if (defaultValue !== void 0) return resolveInitial(defaultValue);
|
|
9
|
+
return resolveInitial(defaultStateValue);
|
|
10
|
+
});
|
|
11
|
+
const mergedValue = value === void 0 ? innerValue : value;
|
|
12
|
+
return [mergedValue, useEventCallback((next) => {
|
|
13
|
+
setInnerValue(next);
|
|
14
|
+
if (mergedValue !== next) onChange?.(next, mergedValue);
|
|
15
|
+
})];
|
|
16
|
+
};
|
|
17
|
+
//#endregion
|
|
18
|
+
export { useControlledState };
|
|
19
|
+
|
|
20
|
+
//# sourceMappingURL=useControlledState.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useControlledState.mjs","names":[],"sources":["../../src/hooks/useControlledState.ts"],"sourcesContent":["import { useState } from 'react';\n\nimport { useEventCallback } from './useEventCallback';\n\ntype Initial<T> = T | (() => T);\n\nexport interface ControlledStateOptions<T> {\n defaultValue?: Initial<T>;\n onChange?: (value: T, prevValue: T) => void;\n value?: T;\n}\n\nconst resolveInitial = <T>(initial: Initial<T>): T =>\n typeof initial === 'function' ? (initial as () => T)() : initial;\n\nexport const useControlledState = <T>(\n defaultStateValue: Initial<T>,\n { defaultValue, onChange, value }: ControlledStateOptions<T> = {},\n): [T, (next: T) => void] => {\n const [innerValue, setInnerValue] = useState<T>(() => {\n if (value !== undefined) return value;\n if (defaultValue !== undefined) return resolveInitial(defaultValue);\n return resolveInitial(defaultStateValue);\n });\n\n const mergedValue = value === undefined ? innerValue : value;\n\n const setValue = useEventCallback((next: T) => {\n setInnerValue(next);\n if (mergedValue !== next) onChange?.(next, mergedValue);\n });\n\n return [mergedValue, setValue];\n};\n"],"mappings":";;;AAYA,MAAM,kBAAqB,YACzB,OAAO,YAAY,aAAc,QAAoB,IAAI;AAE3D,MAAa,sBACX,mBACA,EAAE,cAAc,UAAU,UAAqC,CAAC,MACrC;CAC3B,MAAM,CAAC,YAAY,iBAAiB,eAAkB;EACpD,IAAI,UAAU,KAAA,GAAW,OAAO;EAChC,IAAI,iBAAiB,KAAA,GAAW,OAAO,eAAe,YAAY;EAClE,OAAO,eAAe,iBAAiB;CACzC,CAAC;CAED,MAAM,cAAc,UAAU,KAAA,IAAY,aAAa;CAOvD,OAAO,CAAC,aALS,kBAAkB,SAAY;EAC7C,cAAc,IAAI;EAClB,IAAI,gBAAgB,MAAM,WAAW,MAAM,WAAW;CACxD,CAE4B,CAAC;AAC/B"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import lobe_theme_default from "../Highlighter/theme/lobe-theme.mjs";
|
|
3
3
|
import { getCodeLanguageByInput } from "../Highlighter/const.mjs";
|
|
4
|
+
import { hashString } from "../utils/hashString.mjs";
|
|
4
5
|
import { useEffect, useMemo, useState } from "react";
|
|
5
6
|
import { transformerNotationDiff, transformerNotationErrorLevel, transformerNotationFocus, transformerNotationHighlight, transformerNotationWordHighlight } from "@shikijs/transformers";
|
|
6
|
-
import { Md5 } from "ts-md5";
|
|
7
7
|
//#region src/hooks/useHighlight.ts
|
|
8
8
|
const MD5_LENGTH_THRESHOLD = 1e4;
|
|
9
9
|
const highlightCache = /* @__PURE__ */ new Map();
|
|
@@ -46,7 +46,7 @@ const useHighlight = (text, { language, enableTransformer, theme: builtinTheme,
|
|
|
46
46
|
}, [enableTransformer]);
|
|
47
47
|
const cacheKey = useMemo(() => {
|
|
48
48
|
if (streaming) return null;
|
|
49
|
-
const hash = safeText.length < 1e4 ? safeText :
|
|
49
|
+
const hash = safeText.length < 1e4 ? safeText : `${safeText.length}:${hashString(safeText)}`;
|
|
50
50
|
return [
|
|
51
51
|
matchedLanguage,
|
|
52
52
|
builtinTheme,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useHighlight.mjs","names":["lobeTheme"],"sources":["../../src/hooks/useHighlight.ts"],"sourcesContent":["'use client';\n\nimport {\n transformerNotationDiff,\n transformerNotationErrorLevel,\n transformerNotationFocus,\n transformerNotationHighlight,\n transformerNotationWordHighlight,\n} from '@shikijs/transformers';\nimport { type CSSProperties, useEffect, useMemo, useState } from 'react';\nimport type { BuiltinTheme, CodeToHastOptions, ThemedToken } from 'shiki';\
|
|
1
|
+
{"version":3,"file":"useHighlight.mjs","names":["lobeTheme"],"sources":["../../src/hooks/useHighlight.ts"],"sourcesContent":["'use client';\n\nimport {\n transformerNotationDiff,\n transformerNotationErrorLevel,\n transformerNotationFocus,\n transformerNotationHighlight,\n transformerNotationWordHighlight,\n} from '@shikijs/transformers';\nimport { type CSSProperties, useEffect, useMemo, useState } from 'react';\nimport type { BuiltinTheme, CodeToHastOptions, ThemedToken } from 'shiki';\n\nimport { getCodeLanguageByInput } from '@/Highlighter/const';\nimport lobeTheme from '@/Highlighter/theme/lobe-theme';\nimport { hashString } from '@/utils/hashString';\n\n// Application-level cache to avoid repeated calculations\nexport const MD5_LENGTH_THRESHOLD = 10_000;\n\nexport type StreamingHighlightResult = {\n lines: ThemedToken[][];\n preStyle?: CSSProperties;\n};\n\n// Application-level cache for highlighted HTML\n// Key: cacheKey string, Value: Promise<string>\nconst highlightCache = new Map<string, Promise<string>>();\n\n// Maximum cache size to prevent memory leaks\nconst MAX_CACHE_SIZE = 1000;\n\n// Clean up old cache entries when limit is reached\nconst cleanupCache = () => {\n if (highlightCache.size > MAX_CACHE_SIZE) {\n // Remove oldest 20% of entries\n const entriesToRemove = Math.floor(MAX_CACHE_SIZE * 0.2);\n const keysToRemove = Array.from(highlightCache.keys()).slice(0, entriesToRemove);\n for (const key of keysToRemove) {\n highlightCache.delete(key);\n }\n }\n};\n\nexport type ICodeToHtml = (code: string, options: CodeToHastOptions) => Promise<string>;\nexport type ShikiModule = typeof import('shiki');\n\n// Use codeToHtml shorthand for better performance\n// It automatically manages highlighter instances and loads themes/languages on-demand\nlet codeToHtmlPromise: Promise<ICodeToHtml | null> | null = null;\n\nconst loadCodeToHtml = (): Promise<ICodeToHtml | null> => {\n if (typeof window === 'undefined') return Promise.resolve(null);\n\n if (!codeToHtmlPromise) {\n codeToHtmlPromise = import('shiki').then((mod) => mod.codeToHtml ?? null);\n }\n\n return codeToHtmlPromise;\n};\n\n// Export shikiModulePromise for useStreamHighlight compatibility\nconst loadShikiModule = (): Promise<ShikiModule | null> => {\n if (typeof window === 'undefined') return Promise.resolve(null);\n return import('shiki');\n};\nexport const shikiModulePromise = loadShikiModule();\n\n// Helper function: Safe HTML escaping\nexport const escapeHtml = (str: string): string => {\n return str\n .replaceAll('&', '&')\n .replaceAll('<', '<')\n .replaceAll('>', '>')\n .replaceAll('\"', '"')\n .replaceAll(\"'\", ''');\n};\n\n// Main highlight component - optimized version without SWR\nconst customThemes = {\n 'lobe-theme': lobeTheme,\n};\n\nexport const useHighlight = (\n text: string,\n {\n language,\n enableTransformer,\n theme: builtinTheme,\n streaming,\n }: { enableTransformer?: boolean; language: string; streaming?: boolean; theme?: BuiltinTheme },\n): string => {\n // Safely handle language and text with boundary checks\n const safeText = text ?? '';\n const lang = (language ?? 'plaintext').toLowerCase();\n\n // Match supported languages\n const matchedLanguage = useMemo(() => getCodeLanguageByInput(lang), [lang]);\n\n // Optimize transformer creation\n const transformers = useMemo(() => {\n if (!enableTransformer) return;\n return [\n transformerNotationDiff(),\n transformerNotationHighlight(),\n transformerNotationWordHighlight(),\n transformerNotationFocus(),\n transformerNotationErrorLevel(),\n ];\n }, [enableTransformer]);\n\n // Build cache key\n const cacheKey = useMemo((): string | null => {\n if (streaming) return null;\n // Use hash for long text\n const hash =\n safeText.length < MD5_LENGTH_THRESHOLD\n ? safeText\n : `${safeText.length}:${hashString(safeText)}`;\n return [matchedLanguage, builtinTheme, hash].filter(Boolean).join('-');\n }, [safeText, matchedLanguage, builtinTheme, streaming]);\n\n const [data, setData] = useState<string | undefined>();\n\n useEffect(() => {\n if (!cacheKey) {\n setData(undefined);\n return;\n }\n\n // Check cache first\n const cachedPromise = highlightCache.get(cacheKey);\n if (cachedPromise) {\n cachedPromise\n .then((html) => {\n setData(html);\n })\n .catch(() => {\n // Silently handle errors, fallback will be handled in the promise\n });\n return;\n }\n\n // Create new promise for highlighting\n // Using codeToHtml shorthand: automatically loads themes/languages on-demand\n const highlightPromise = (async (): Promise<string> => {\n try {\n // Try full rendering with transformers\n const shikiModule = await shikiModulePromise;\n if (!shikiModule) return safeText;\n\n const effectiveTheme = builtinTheme || 'lobe-theme';\n\n // Load custom theme if using slack-dark or slack-ochin\n if (!builtinTheme && effectiveTheme === 'lobe-theme') {\n const customTheme = customThemes[effectiveTheme];\n if (customTheme) {\n // Use getSingletonHighlighter to load custom theme\n const highlighter = await shikiModule.getSingletonHighlighter({\n langs: [matchedLanguage],\n themes: [customTheme as any],\n });\n\n const html = await highlighter.codeToHtml(safeText, {\n lang: matchedLanguage,\n theme: effectiveTheme,\n transformers,\n });\n\n return html;\n }\n }\n\n // Fallback to codeToHtml for builtin themes\n const codeToHtml = await loadCodeToHtml();\n if (!codeToHtml) return safeText;\n\n const html = await codeToHtml(safeText, {\n lang: matchedLanguage,\n theme: effectiveTheme,\n transformers,\n });\n\n return html;\n } catch (error_) {\n console.error('Advanced rendering failed:', error_);\n\n try {\n // Try simple rendering (without transformers)\n const codeToHtml = await loadCodeToHtml();\n if (!codeToHtml) return safeText;\n const html = await codeToHtml(safeText, {\n lang: matchedLanguage,\n theme: 'lobe-theme',\n });\n return html;\n } catch {\n // Fallback to plain text\n const fallbackHtml = `<pre class=\"fallback\"><code>${escapeHtml(safeText)}</code></pre>`;\n return fallbackHtml;\n }\n }\n })();\n\n // Cache the promise\n highlightCache.set(cacheKey, highlightPromise);\n cleanupCache();\n\n // Handle promise result\n highlightPromise\n .then((html) => {\n // Only update if this is still the current cache key\n if (highlightCache.get(cacheKey) === highlightPromise) {\n setData(html);\n }\n })\n .catch(() => {\n // Remove failed promise from cache\n if (highlightCache.get(cacheKey) === highlightPromise) {\n highlightCache.delete(cacheKey);\n }\n });\n }, [cacheKey, safeText, matchedLanguage, builtinTheme, transformers, customThemes]);\n\n return data || '';\n};\n"],"mappings":";;;;;;;AAiBA,MAAa,uBAAuB;AASpC,MAAM,iCAAiB,IAAI,IAA6B;AAGxD,MAAM,iBAAiB;AAGvB,MAAM,qBAAqB;CACzB,IAAI,eAAe,OAAO,gBAAgB;EAExC,MAAM,kBAAkB,KAAK,MAAM,iBAAiB,EAAG;EACvD,MAAM,eAAe,MAAM,KAAK,eAAe,KAAK,CAAC,CAAC,CAAC,MAAM,GAAG,eAAe;EAC/E,KAAK,MAAM,OAAO,cAChB,eAAe,OAAO,GAAG;CAE7B;AACF;AAOA,IAAI,oBAAwD;AAE5D,MAAM,uBAAoD;CACxD,IAAI,OAAO,WAAW,aAAa,OAAO,QAAQ,QAAQ,IAAI;CAE9D,IAAI,CAAC,mBACH,oBAAoB,OAAO,QAAQ,CAAC,MAAM,QAAQ,IAAI,cAAc,IAAI;CAG1E,OAAO;AACT;AAGA,MAAM,wBAAqD;CACzD,IAAI,OAAO,WAAW,aAAa,OAAO,QAAQ,QAAQ,IAAI;CAC9D,OAAO,OAAO;AAChB;AACA,MAAa,qBAAqB,gBAAgB;AAGlD,MAAa,cAAc,QAAwB;CACjD,OAAO,IACJ,WAAW,KAAK,OAAO,CAAC,CACxB,WAAW,KAAK,MAAM,CAAC,CACvB,WAAW,KAAK,MAAM,CAAC,CACvB,WAAW,MAAK,QAAQ,CAAC,CACzB,WAAW,KAAK,QAAQ;AAC7B;AAGA,MAAM,eAAe,EACnB,cAAcA,mBAChB;AAEA,MAAa,gBACX,MACA,EACE,UACA,mBACA,OAAO,cACP,gBAES;CAEX,MAAM,WAAW,QAAQ;CACzB,MAAM,QAAQ,YAAY,YAAA,CAAa,YAAY;CAGnD,MAAM,kBAAkB,cAAc,uBAAuB,IAAI,GAAG,CAAC,IAAI,CAAC;CAG1E,MAAM,eAAe,cAAc;EACjC,IAAI,CAAC,mBAAmB;EACxB,OAAO;GACL,wBAAwB;GACxB,6BAA6B;GAC7B,iCAAiC;GACjC,yBAAyB;GACzB,8BAA8B;EAChC;CACF,GAAG,CAAC,iBAAiB,CAAC;CAGtB,MAAM,WAAW,cAA6B;EAC5C,IAAI,WAAW,OAAO;EAEtB,MAAM,OACJ,SAAS,SAAA,MACL,WACA,GAAG,SAAS,OAAO,GAAG,WAAW,QAAQ;EAC/C,OAAO;GAAC;GAAiB;GAAc;EAAI,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;CACvE,GAAG;EAAC;EAAU;EAAiB;EAAc;CAAS,CAAC;CAEvD,MAAM,CAAC,MAAM,WAAW,SAA6B;CAErD,gBAAgB;EACd,IAAI,CAAC,UAAU;GACb,QAAQ,KAAA,CAAS;GACjB;EACF;EAGA,MAAM,gBAAgB,eAAe,IAAI,QAAQ;EACjD,IAAI,eAAe;GACjB,cACG,MAAM,SAAS;IACd,QAAQ,IAAI;GACd,CAAC,CAAC,CACD,YAAY,CAEb,CAAC;GACH;EACF;EAIA,MAAM,oBAAoB,YAA6B;GACrD,IAAI;IAEF,MAAM,cAAc,MAAM;IAC1B,IAAI,CAAC,aAAa,OAAO;IAEzB,MAAM,iBAAiB,gBAAgB;IAGvC,IAAI,CAAC,gBAAgB,mBAAmB,cAAc;KACpD,MAAM,cAAc,aAAa;KACjC,IAAI,aAaF,OAAO,OANY,MALO,YAAY,wBAAwB;MAC5D,OAAO,CAAC,eAAe;MACvB,QAAQ,CAAC,WAAkB;KAC7B,CAAC,EAAA,CAE8B,WAAW,UAAU;MAClD,MAAM;MACN,OAAO;MACP;KACF,CAAC;IAIL;IAGA,MAAM,aAAa,MAAM,eAAe;IACxC,IAAI,CAAC,YAAY,OAAO;IAQxB,OAAO,MANY,WAAW,UAAU;KACtC,MAAM;KACN,OAAO;KACP;IACF,CAAC;GAGH,SAAS,QAAQ;IACf,QAAQ,MAAM,8BAA8B,MAAM;IAElD,IAAI;KAEF,MAAM,aAAa,MAAM,eAAe;KACxC,IAAI,CAAC,YAAY,OAAO;KAKxB,OAAO,MAJY,WAAW,UAAU;MACtC,MAAM;MACN,OAAO;KACT,CAAC;IAEH,QAAQ;KAGN,OAAO,+BAD6C,WAAW,QAAQ,EAAE;IAE3E;GACF;EACF,EAAA,CAAG;EAGH,eAAe,IAAI,UAAU,gBAAgB;EAC7C,aAAa;EAGb,iBACG,MAAM,SAAS;GAEd,IAAI,eAAe,IAAI,QAAQ,MAAM,kBACnC,QAAQ,IAAI;EAEhB,CAAC,CAAC,CACD,YAAY;GAEX,IAAI,eAAe,IAAI,QAAQ,MAAM,kBACnC,eAAe,OAAO,QAAQ;EAElC,CAAC;CACL,GAAG;EAAC;EAAU;EAAU;EAAiB;EAAc;EAAc;CAAY,CAAC;CAElF,OAAO,QAAQ;AACjB"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
//#region src/hooks/useHover.ts
|
|
3
|
+
const useHover = (ref) => {
|
|
4
|
+
const [hovering, setHovering] = useState(false);
|
|
5
|
+
useEffect(() => {
|
|
6
|
+
const element = ref.current;
|
|
7
|
+
if (!element) return;
|
|
8
|
+
const enter = () => setHovering(true);
|
|
9
|
+
const leave = () => setHovering(false);
|
|
10
|
+
element.addEventListener("mouseenter", enter);
|
|
11
|
+
element.addEventListener("mouseleave", leave);
|
|
12
|
+
return () => {
|
|
13
|
+
element.removeEventListener("mouseenter", enter);
|
|
14
|
+
element.removeEventListener("mouseleave", leave);
|
|
15
|
+
};
|
|
16
|
+
}, [ref]);
|
|
17
|
+
return hovering;
|
|
18
|
+
};
|
|
19
|
+
//#endregion
|
|
20
|
+
export { useHover };
|
|
21
|
+
|
|
22
|
+
//# sourceMappingURL=useHover.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useHover.mjs","names":[],"sources":["../../src/hooks/useHover.ts"],"sourcesContent":["import { type RefObject, useEffect, useState } from 'react';\n\nexport const useHover = (ref: RefObject<Element | null>): boolean => {\n const [hovering, setHovering] = useState(false);\n\n useEffect(() => {\n const element = ref.current;\n if (!element) return;\n const enter = () => setHovering(true);\n const leave = () => setHovering(false);\n element.addEventListener('mouseenter', enter);\n element.addEventListener('mouseleave', leave);\n return () => {\n element.removeEventListener('mouseenter', enter);\n element.removeEventListener('mouseleave', leave);\n };\n }, [ref]);\n\n return hovering;\n};\n"],"mappings":";;AAEA,MAAa,YAAY,QAA4C;CACnE,MAAM,CAAC,UAAU,eAAe,SAAS,KAAK;CAE9C,gBAAgB;EACd,MAAM,UAAU,IAAI;EACpB,IAAI,CAAC,SAAS;EACd,MAAM,cAAc,YAAY,IAAI;EACpC,MAAM,cAAc,YAAY,KAAK;EACrC,QAAQ,iBAAiB,cAAc,KAAK;EAC5C,QAAQ,iBAAiB,cAAc,KAAK;EAC5C,aAAa;GACX,QAAQ,oBAAoB,cAAc,KAAK;GAC/C,QAAQ,oBAAoB,cAAc,KAAK;EACjD;CACF,GAAG,CAAC,GAAG,CAAC;CAER,OAAO;AACT"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
//#region src/hooks/useScroll.d.ts
|
|
2
|
+
type ScrollTargetValue = Element | Document | null | undefined;
|
|
3
|
+
export type ScrollTarget = ScrollTargetValue | (() => ScrollTargetValue) | {
|
|
4
|
+
current: ScrollTargetValue;
|
|
5
|
+
};
|
|
6
|
+
//#endregion
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=useScroll.d.mts.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
//#region src/hooks/useScroll.ts
|
|
3
|
+
const resolveTarget = (target) => {
|
|
4
|
+
if (typeof target === "function") return target();
|
|
5
|
+
if (target && "current" in target) return target.current;
|
|
6
|
+
return target;
|
|
7
|
+
};
|
|
8
|
+
const readPosition = (element) => {
|
|
9
|
+
const scroller = element instanceof Document ? element.scrollingElement ?? element.documentElement : element;
|
|
10
|
+
return {
|
|
11
|
+
left: scroller.scrollLeft,
|
|
12
|
+
top: scroller.scrollTop
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
const useScroll = (target) => {
|
|
16
|
+
const [position, setPosition] = useState();
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
const element = resolveTarget(target);
|
|
19
|
+
if (!element) return;
|
|
20
|
+
const update = () => {
|
|
21
|
+
const next = readPosition(element);
|
|
22
|
+
setPosition((prev) => prev && prev.left === next.left && prev.top === next.top ? prev : next);
|
|
23
|
+
};
|
|
24
|
+
update();
|
|
25
|
+
element.addEventListener("scroll", update, { passive: true });
|
|
26
|
+
return () => element.removeEventListener("scroll", update);
|
|
27
|
+
}, [target]);
|
|
28
|
+
return position;
|
|
29
|
+
};
|
|
30
|
+
//#endregion
|
|
31
|
+
export { useScroll };
|
|
32
|
+
|
|
33
|
+
//# sourceMappingURL=useScroll.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useScroll.mjs","names":[],"sources":["../../src/hooks/useScroll.ts"],"sourcesContent":["import { useEffect, useState } from 'react';\n\ntype ScrollTargetValue = Element | Document | null | undefined;\n\nexport type ScrollTarget =\n ScrollTargetValue | (() => ScrollTargetValue) | { current: ScrollTargetValue };\n\nexport interface ScrollPosition {\n left: number;\n top: number;\n}\n\nconst resolveTarget = (target: ScrollTarget): ScrollTargetValue => {\n if (typeof target === 'function') return target();\n if (target && 'current' in target) return target.current;\n return target;\n};\n\nconst readPosition = (element: Element | Document): ScrollPosition => {\n const scroller =\n element instanceof Document ? (element.scrollingElement ?? element.documentElement) : element;\n return { left: scroller.scrollLeft, top: scroller.scrollTop };\n};\n\nexport const useScroll = (target: ScrollTarget): ScrollPosition | undefined => {\n const [position, setPosition] = useState<ScrollPosition>();\n\n useEffect(() => {\n const element = resolveTarget(target);\n if (!element) return;\n const update = () => {\n const next = readPosition(element);\n setPosition((prev) =>\n prev && prev.left === next.left && prev.top === next.top ? prev : next,\n );\n };\n update();\n element.addEventListener('scroll', update, { passive: true });\n return () => element.removeEventListener('scroll', update);\n }, [target]);\n\n return position;\n};\n"],"mappings":";;AAYA,MAAM,iBAAiB,WAA4C;CACjE,IAAI,OAAO,WAAW,YAAY,OAAO,OAAO;CAChD,IAAI,UAAU,aAAa,QAAQ,OAAO,OAAO;CACjD,OAAO;AACT;AAEA,MAAM,gBAAgB,YAAgD;CACpE,MAAM,WACJ,mBAAmB,WAAY,QAAQ,oBAAoB,QAAQ,kBAAmB;CACxF,OAAO;EAAE,MAAM,SAAS;EAAY,KAAK,SAAS;CAAU;AAC9D;AAEA,MAAa,aAAa,WAAqD;CAC7E,MAAM,CAAC,UAAU,eAAe,SAAyB;CAEzD,gBAAgB;EACd,MAAM,UAAU,cAAc,MAAM;EACpC,IAAI,CAAC,SAAS;EACd,MAAM,eAAe;GACnB,MAAM,OAAO,aAAa,OAAO;GACjC,aAAa,SACX,QAAQ,KAAK,SAAS,KAAK,QAAQ,KAAK,QAAQ,KAAK,MAAM,OAAO,IACpE;EACF;EACA,OAAO;EACP,QAAQ,iBAAiB,UAAU,QAAQ,EAAE,SAAS,KAAK,CAAC;EAC5D,aAAa,QAAQ,oBAAoB,UAAU,MAAM;CAC3D,GAAG,CAAC,MAAM,CAAC;CAEX,OAAO;AACT"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { useLayoutEffect, useState } from "react";
|
|
2
|
+
//#region src/hooks/useSize.ts
|
|
3
|
+
const useSize = (ref) => {
|
|
4
|
+
const [size, setSize] = useState();
|
|
5
|
+
useLayoutEffect(() => {
|
|
6
|
+
const element = ref.current;
|
|
7
|
+
if (!element) return;
|
|
8
|
+
setSize({
|
|
9
|
+
height: element.clientHeight,
|
|
10
|
+
width: element.clientWidth
|
|
11
|
+
});
|
|
12
|
+
const observer = new ResizeObserver((entries) => {
|
|
13
|
+
for (const { target } of entries) setSize({
|
|
14
|
+
height: target.clientHeight,
|
|
15
|
+
width: target.clientWidth
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
observer.observe(element);
|
|
19
|
+
return () => observer.disconnect();
|
|
20
|
+
}, [ref]);
|
|
21
|
+
return size;
|
|
22
|
+
};
|
|
23
|
+
//#endregion
|
|
24
|
+
export { useSize };
|
|
25
|
+
|
|
26
|
+
//# sourceMappingURL=useSize.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useSize.mjs","names":[],"sources":["../../src/hooks/useSize.ts"],"sourcesContent":["import { type RefObject, useLayoutEffect, useState } from 'react';\n\nexport interface ElementSize {\n height: number;\n width: number;\n}\n\nexport const useSize = (ref: RefObject<Element | null>): ElementSize | undefined => {\n const [size, setSize] = useState<ElementSize>();\n\n useLayoutEffect(() => {\n const element = ref.current;\n if (!element) return;\n setSize({ height: element.clientHeight, width: element.clientWidth });\n\n const observer = new ResizeObserver((entries) => {\n for (const { target } of entries) {\n setSize({ height: target.clientHeight, width: target.clientWidth });\n }\n });\n observer.observe(element);\n return () => observer.disconnect();\n }, [ref]);\n\n return size;\n};\n"],"mappings":";;AAOA,MAAa,WAAW,QAA4D;CAClF,MAAM,CAAC,MAAM,WAAW,SAAsB;CAE9C,sBAAsB;EACpB,MAAM,UAAU,IAAI;EACpB,IAAI,CAAC,SAAS;EACd,QAAQ;GAAE,QAAQ,QAAQ;GAAc,OAAO,QAAQ;EAAY,CAAC;EAEpE,MAAM,WAAW,IAAI,gBAAgB,YAAY;GAC/C,KAAK,MAAM,EAAE,YAAY,SACvB,QAAQ;IAAE,QAAQ,OAAO;IAAc,OAAO,OAAO;GAAY,CAAC;EAEtE,CAAC;EACD,SAAS,QAAQ,OAAO;EACxB,aAAa,SAAS,WAAW;CACnC,GAAG,CAAC,GAAG,CAAC;CAER,OAAO;AACT"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { styleProps } from "../../styles/stylex/props.mjs";
|
|
3
|
+
import { useMergeRefs } from "../../utils/mergeRefs.mjs";
|
|
3
4
|
import { ScrollAreaRoot, ScrollAreaScrollbar, ScrollAreaThumb, ScrollAreaViewportImpl } from "../../ScrollArea/atoms.mjs";
|
|
4
5
|
import { Children, cloneElement, useCallback, useEffect, useRef } from "react";
|
|
5
6
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
-
import { useMergeRefs } from "react-merge-refs";
|
|
7
7
|
import { Virtualizer } from "virtua";
|
|
8
8
|
//#region src/internal/virtual/VirtualScrollArea.tsx
|
|
9
9
|
const styles = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VirtualScrollArea.mjs","names":[],"sources":["../../../src/internal/virtual/VirtualScrollArea.tsx"],"sourcesContent":["'use client';\n\nimport * as stylex from '@stylexjs/stylex';\nimport {\n Children,\n cloneElement,\n type ReactElement,\n type ReactNode,\n type Ref,\n useCallback,\n useEffect,\n useRef,\n} from 'react';\nimport {
|
|
1
|
+
{"version":3,"file":"VirtualScrollArea.mjs","names":[],"sources":["../../../src/internal/virtual/VirtualScrollArea.tsx"],"sourcesContent":["'use client';\n\nimport * as stylex from '@stylexjs/stylex';\nimport {\n Children,\n cloneElement,\n type ReactElement,\n type ReactNode,\n type Ref,\n useCallback,\n useEffect,\n useRef,\n} from 'react';\nimport { Virtualizer, type VirtualizerHandle } from 'virtua';\n\nimport {\n ScrollAreaRoot,\n type ScrollAreaRootProps,\n ScrollAreaScrollbar,\n ScrollAreaThumb,\n ScrollAreaViewportImpl,\n type ScrollAreaViewportProps,\n} from '@/ScrollArea/atoms';\nimport { styleProps } from '@/styles/stylex/props';\nimport { useMergeRefs } from '@/utils/mergeRefs';\n\n// `--lobe-virtual-scroll-inset` is the inline padding of the box the scroll area sits in: the\n// root bleeds out by it so the scrollbar hugs that box's edge, and the viewport pads the rows back.\nconst styles = stylex.create({\n root: {\n marginInline: 'calc(-1 * var(--lobe-virtual-scroll-inset, 0px))',\n display: 'flex',\n flexBasis: '0%',\n flexDirection: 'column',\n flexGrow: 1,\n flexShrink: 1,\n minHeight: 0,\n },\n viewport: {\n paddingInline: 'var(--lobe-virtual-scroll-inset, 0)',\n flexBasis: '0%',\n flexGrow: 1,\n flexShrink: 1,\n height: 'auto',\n minHeight: 0,\n },\n});\n\n// Rows mounted on the very first render, before the viewport is measured, so Base UI's\n// list navigation sees the leading rows (not a pinned footer item) when it focuses on open.\nconst INITIAL_ROW_COUNT = 20;\n\nexport interface VirtualScrollAreaProps extends Omit<ScrollAreaRootProps, 'children'> {\n children: ReactNode;\n itemSize?: number;\n keepMounted?: readonly number[];\n /**\n * Element that owns the scrolling box (e.g. a Base UI `Select.List`); it is rendered\n * through the ScrollArea viewport so its own props and role win.\n */\n viewport?: ReactElement<{ children?: ReactNode; render?: ReactElement }>;\n viewportProps?: ScrollAreaViewportProps;\n virtualizerRef?: Ref<VirtualizerHandle>;\n}\n\nexport const VirtualScrollArea = ({\n children,\n className,\n itemSize,\n keepMounted,\n viewport,\n viewportProps,\n virtualizerRef,\n ...rest\n}: VirtualScrollAreaProps) => {\n const viewportRef = useRef<HTMLDivElement | null>(null);\n const mergedViewportRef = useMergeRefs([viewportRef, viewportProps?.ref]);\n\n const list = (\n <Virtualizer\n itemSize={itemSize}\n keepMounted={keepMounted}\n ref={virtualizerRef}\n scrollRef={viewportRef}\n ssrCount={Math.min(INITIAL_ROW_COUNT, Children.count(children))}\n >\n {children}\n </Virtualizer>\n );\n const viewportElement = (\n <ScrollAreaViewportImpl\n tabIndex={-1}\n {...viewportProps}\n ref={mergedViewportRef}\n xstyle={styles.viewport}\n />\n );\n\n return (\n <ScrollAreaRoot\n data-virtual=\"\"\n {...rest}\n className={styleProps(styles.root, className as string).className}\n >\n {cloneElement(viewport ?? viewportElement, {\n children: list,\n ...(viewport && { render: viewportElement }),\n })}\n <ScrollAreaScrollbar>\n <ScrollAreaThumb />\n </ScrollAreaScrollbar>\n </ScrollAreaRoot>\n );\n};\n\nVirtualScrollArea.displayName = 'VirtualScrollArea';\n\n// Base UI scrolls the highlighted item into view after focusing it; while the pointer drives\n// the scroll that would yank the list back, so callers consult `pointerScrollRef` first.\nexport function usePointerScrollGuard() {\n const pointerScrollRef = useRef(false);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const markPointerScroll = useCallback(() => {\n pointerScrollRef.current = true;\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\n timeoutRef.current = setTimeout(() => {\n pointerScrollRef.current = false;\n }, 120);\n }, []);\n\n useEffect(() => {\n return () => {\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\n };\n }, []);\n\n return {\n markPointerScroll,\n pointerScrollRef,\n scrollGuardProps: {\n onPointerDown: markPointerScroll,\n onTouchMove: markPointerScroll,\n onWheel: markPointerScroll,\n },\n };\n}\n"],"mappings":";;;;;;;;AAWA,MAAM,SAAS;CACb,MAAM;EACJ,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,OAAO;EACP,QAAQ;EACR,OAAO;CACT;CACA,UAAU;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,OAAO;EACP,QAAQ;EACR,QAAQ;EACR,OAAO;CACT;AACF;AAIA,MAAM,oBAAoB;AAgB1B,MAAa,qBAAqB,EAChC,UACA,WACA,UACA,aACA,UACA,eACA,gBACA,GAAG,WACyB;CAC5B,MAAM,cAAc,OAA8B,IAAI;CACtD,MAAM,oBAAoB,aAAa,CAAC,aAAa,eAAe,GAAG,CAAC;CACxE,MAAM,OAAO,oBAAC,aAAD;EAAuB;EAAuB;EAAa,KAAK;EAAgB,WAAW;EAAa,UAAU,KAAK,IAAI,mBAAmB,SAAS,MAAM,QAAQ,CAAC;EAC9K;CACU,CAAA;CACf,MAAM,kBAAkB,oBAAC,wBAAD;EAAwB,UAAU;EAAI,GAAI;EAAe,KAAK;EAAmB,QAAQ,OAAO;CAAW,CAAA;CACnI,OAAO,qBAAC,gBAAD;EAAgB,gBAAa;EAAG,GAAI;EAAM,WAAW,WAAW,OAAO,MAAM,SAAmB,CAAC,CAAC;EAAlG,UAAA,CACF,aAAa,YAAY,iBAAiB;GAC3C,UAAU;GACV,GAAI,YAAY,EACd,QAAQ,gBACV;EACF,CAAC,GACC,oBAAC,qBAAD,EAAA,UACE,oBAAC,iBAAD,CAAkB,CAAA,EACC,CAAA,CACP;;AACpB;AACA,kBAAkB,cAAc;AAIhC,SAAgB,wBAAwB;CACtC,MAAM,mBAAmB,OAAO,KAAK;CACrC,MAAM,aAAa,OAA6C,IAAI;CACpE,MAAM,oBAAoB,kBAAkB;EAC1C,iBAAiB,UAAU;EAC3B,IAAI,WAAW,SAAS,aAAa,WAAW,OAAO;EACvD,WAAW,UAAU,iBAAiB;GACpC,iBAAiB,UAAU;EAC7B,GAAG,GAAG;CACR,GAAG,CAAC,CAAC;CACL,gBAAgB;EACd,aAAa;GACX,IAAI,WAAW,SAAS,aAAa,WAAW,OAAO;EACzD;CACF,GAAG,CAAC,CAAC;CACL,OAAO;EACL;EACA;EACA,kBAAkB;GAChB,eAAe;GACf,aAAa;GACb,SAAS;EACX;CACF;AACF"}
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
import { styleProps } from "../../styles/stylex/props.mjs";
|
|
3
3
|
import FlexBasic_default from "../../Flex/FlexBasic.mjs";
|
|
4
4
|
import ActionIcon from "../../ActionIcon/ActionIcon.mjs";
|
|
5
|
+
import { useSize } from "../../hooks/useSize.mjs";
|
|
5
6
|
import ChatInputAreaInner from "../../chat/ChatInputArea/components/ChatInputAreaInner.mjs";
|
|
6
7
|
import SafeArea from "../SafeArea/SafeArea.mjs";
|
|
7
8
|
import { styles } from "./style.mjs";
|
|
8
9
|
import { memo, useCallback, useEffect, useRef, useState } from "react";
|
|
9
10
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
10
11
|
import { ChevronDown, ChevronUp } from "lucide-react";
|
|
11
|
-
import { useSize } from "ahooks";
|
|
12
12
|
//#region src/mobile/ChatInputArea/ChatInputArea.tsx
|
|
13
13
|
const ChatInputArea = memo(({ ref, className, style, topAddons, textAreaLeftAddons, textAreaRightAddons, bottomAddons, expand = false, setExpand, onSend, onInput, loading, value, safeArea }) => {
|
|
14
14
|
const containerRef = useRef(null);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatInputArea.mjs","names":["Flexbox"],"sources":["../../../src/mobile/ChatInputArea/ChatInputArea.tsx"],"sourcesContent":["'use client';\n\nimport {
|
|
1
|
+
{"version":3,"file":"ChatInputArea.mjs","names":["Flexbox"],"sources":["../../../src/mobile/ChatInputArea/ChatInputArea.tsx"],"sourcesContent":["'use client';\n\nimport { ChevronDown, ChevronUp } from 'lucide-react';\nimport {\n type FC,\n memo,\n type PropsWithChildren,\n type ReactNode,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from 'react';\n\nimport ActionIcon from '@/ActionIcon';\nimport ChatInputAreaInner from '@/chat/ChatInputArea/components/ChatInputAreaInner';\nimport { Flexbox } from '@/Flex';\nimport { useSize } from '@/hooks/useSize';\nimport SafeArea from '@/mobile/SafeArea';\nimport { styleProps } from '@/styles/stylex/props';\n\nimport { styles } from './style';\nimport { type ChatInputAreaProps } from './type';\n\nconst ChatInputArea = memo<ChatInputAreaProps>(\n ({\n ref,\n className,\n style,\n topAddons,\n textAreaLeftAddons,\n textAreaRightAddons,\n bottomAddons,\n expand = false,\n setExpand,\n onSend,\n onInput,\n loading,\n value,\n safeArea,\n }) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const size = useSize(containerRef);\n const [showFullscreen, setShowFullscreen] = useState<boolean>(false);\n const [isFocused, setIsFocused] = useState<boolean>(false);\n\n useEffect(() => {\n if (!size?.height) return;\n setShowFullscreen(size.height > 72);\n }, [size]);\n\n const InnerContainer: FC<\n PropsWithChildren & {\n bottomAddons?: ReactNode;\n textAreaLeftAddons?: ReactNode;\n textAreaRightAddons?: ReactNode;\n topAddons?: ReactNode;\n }\n > = useCallback(\n ({ children, ...r }) =>\n expand ? (\n <Flexbox gap={8} height={'inherit'} paddingInline={8}>\n <Flexbox horizontal gap={8} justify={'flex-end'}>\n {r.textAreaLeftAddons}\n {r.textAreaRightAddons}\n </Flexbox>\n {children}\n {r.topAddons}\n {r.bottomAddons}\n </Flexbox>\n ) : (\n <Flexbox horizontal align={'flex-end'} gap={8} height={'inherit'} paddingInline={8}>\n {r.textAreaLeftAddons}\n {children}\n {r.textAreaRightAddons}\n </Flexbox>\n ),\n [expand, loading],\n );\n\n const showAddons = !expand && !isFocused;\n\n return (\n <Flexbox\n flex={'none'}\n gap={12}\n height={expand ? '100%' : undefined}\n paddingBlock={12}\n width={expand ? '100%' : undefined}\n {...styleProps([styles.container, expand && styles.expand], className, style)}\n >\n {topAddons && <Flexbox style={showAddons ? {} : { display: 'none' }}>{topAddons}</Flexbox>}\n <Flexbox\n height={expand ? '100%' : undefined}\n ref={containerRef}\n style={{ position: 'relative' }}\n width={expand ? '100%' : undefined}\n >\n {showFullscreen && (\n <ActionIcon\n active\n icon={expand ? ChevronDown : ChevronUp}\n id={'sssssss'}\n size={{ blockSize: 24, borderRadius: '50%', size: 14 }}\n style={{ insetInlineEnd: 14, position: 'absolute', ...(expand ? { top: 6 } : {}) }}\n onClick={() => setExpand?.(!expand)}\n />\n )}\n <InnerContainer\n bottomAddons={bottomAddons}\n textAreaLeftAddons={textAreaLeftAddons}\n textAreaRightAddons={textAreaRightAddons}\n topAddons={topAddons}\n >\n <ChatInputAreaInner\n autoSize={expand ? false : { maxRows: 6, minRows: 1 }}\n loading={loading}\n ref={ref}\n style={{ flex: 1, minHeight: 36, paddingBlock: 6 }}\n value={value}\n variant={expand ? 'borderless' : 'filled'}\n onBlur={() => setIsFocused(false)}\n onFocus={() => setIsFocused(true)}\n onInput={onInput}\n onSend={onSend}\n />\n </InnerContainer>\n </Flexbox>\n {bottomAddons && (\n <Flexbox style={showAddons ? {} : { display: 'none' }}>{bottomAddons}</Flexbox>\n )}\n {safeArea && !isFocused && <SafeArea position={'bottom'} />}\n </Flexbox>\n );\n },\n);\n\nChatInputArea.displayName = 'ChatInputArea';\n\nexport default ChatInputArea;\n"],"mappings":";;;;;;;;;;;;AAwBA,MAAM,gBAAgB,MACnB,EACC,KACA,WACA,OACA,WACA,oBACA,qBACA,cACA,SAAS,OACT,WACA,QACA,SACA,SACA,OACA,eACI;CACJ,MAAM,eAAe,OAAuB,IAAI;CAChD,MAAM,OAAO,QAAQ,YAAY;CACjC,MAAM,CAAC,gBAAgB,qBAAqB,SAAkB,KAAK;CACnE,MAAM,CAAC,WAAW,gBAAgB,SAAkB,KAAK;CAEzD,gBAAgB;EACd,IAAI,CAAC,MAAM,QAAQ;EACnB,kBAAkB,KAAK,SAAS,EAAE;CACpC,GAAG,CAAC,IAAI,CAAC;CAET,MAAM,iBAOF,aACD,EAAE,UAAU,GAAG,QACd,SACE,qBAACA,mBAAD;EAAS,KAAK;EAAG,QAAQ;EAAW,eAAe;EAAnD,UAAA;GACE,qBAACA,mBAAD;IAAS,YAAA;IAAW,KAAK;IAAG,SAAS;IAArC,UAAA,CACG,EAAE,oBACF,EAAE,mBACI;;GACR;GACA,EAAE;GACF,EAAE;EACI;CAET,CAAA,IAAA,qBAACA,mBAAD;EAAS,YAAA;EAAW,OAAO;EAAY,KAAK;EAAG,QAAQ;EAAW,eAAe;EAAjF,UAAA;GACG,EAAE;GACF;GACA,EAAE;EACI;CAEb,CAAA,GAAA,CAAC,QAAQ,OAAO,CAClB;CAEA,MAAM,aAAa,CAAC,UAAU,CAAC;CAE/B,OACE,qBAACA,mBAAD;EACE,MAAM;EACN,KAAK;EACL,QAAQ,SAAS,SAAS,KAAA;EAC1B,cAAc;EACd,OAAO,SAAS,SAAS,KAAA;EACzB,GAAI,WAAW,CAAC,OAAO,WAAW,UAAU,OAAO,MAAM,GAAG,WAAW,KAAK;EAN9E,UAAA;GAQG,aAAa,oBAACA,mBAAD;IAAS,OAAO,aAAa,CAAC,IAAI,EAAE,SAAS,OAAO;IAAI,UAAA;GAAmB,CAAA;GACzF,qBAACA,mBAAD;IACE,QAAQ,SAAS,SAAS,KAAA;IAC1B,KAAK;IACL,OAAO,EAAE,UAAU,WAAW;IAC9B,OAAO,SAAS,SAAS,KAAA;IAJ3B,UAAA,CAMG,kBACC,oBAAC,YAAD;KACE,QAAA;KACA,MAAM,SAAS,cAAc;KAC7B,IAAI;KACJ,MAAM;MAAE,WAAW;MAAI,cAAc;MAAO,MAAM;KAAG;KACrD,OAAO;MAAE,gBAAgB;MAAI,UAAU;MAAY,GAAI,SAAS,EAAE,KAAK,EAAE,IAAI,CAAC;KAAG;KACjF,eAAe,YAAY,CAAC,MAAM;IACnC,CAAA,GAEH,oBAAC,gBAAD;KACgB;KACM;KACC;KACV;KAEX,UAAA,oBAAC,oBAAD;MACE,UAAU,SAAS,QAAQ;OAAE,SAAS;OAAG,SAAS;MAAE;MAC3C;MACJ;MACL,OAAO;OAAE,MAAM;OAAG,WAAW;OAAI,cAAc;MAAE;MAC1C;MACP,SAAS,SAAS,eAAe;MACjC,cAAc,aAAa,KAAK;MAChC,eAAe,aAAa,IAAI;MACvB;MACD;KACT,CAAA;IACa,CAAA,CACT;;GACR,gBACC,oBAACA,mBAAD;IAAS,OAAO,aAAa,CAAC,IAAI,EAAE,SAAS,OAAO;IAAI,UAAA;GAAsB,CAAA;GAE/E,YAAY,CAAC,aAAa,oBAAC,UAAD,EAAU,UAAU,SAAW,CAAA;EACnD;;AAEb,CACF;AAEA,cAAc,cAAc"}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { styleProps } from "../../styles/stylex/props.mjs";
|
|
3
3
|
import FlexBasic_default from "../../Flex/FlexBasic.mjs";
|
|
4
|
+
import { useControlledState } from "../../hooks/useControlledState.mjs";
|
|
4
5
|
import SafeArea from "../SafeArea/SafeArea.mjs";
|
|
5
6
|
import { styles } from "./style.mjs";
|
|
6
7
|
import { memo } from "react";
|
|
7
8
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
9
|
import * as stylex from "@stylexjs/stylex";
|
|
9
|
-
import useControlledState from "use-merge-value";
|
|
10
10
|
//#region src/mobile/TabBar/TabBar.tsx
|
|
11
11
|
const TabBar = memo(({ ref, className, safeArea, items, activeKey, defaultActiveKey, onChange, ...rest }) => {
|
|
12
12
|
const [currentActive, setCurrentActive] = useControlledState(defaultActiveKey || items[0].key, {
|