mirror of
				https://github.com/Yidadaa/ChatGPT-Next-Web.git
				synced 2025-10-31 21:59:19 +08:00 
			
		
		
		
	Merge branch 'Yidadaa:main' into main
This commit is contained in:
		
							
								
								
									
										14
									
								
								.babelrc
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										14
									
								
								.babelrc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| { | ||||
|   "presets": [ | ||||
|     [ | ||||
|       "next/babel", | ||||
|       { | ||||
|         "preset-env": { | ||||
|           "targets": { | ||||
|             "browsers": ["> 0.25%, not dead"] | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     ] | ||||
|   ] | ||||
| } | ||||
| @@ -14,7 +14,7 @@ One-Click to get well-designed cross-platform ChatGPT web UI. | ||||
| [![MacOS][MacOS-image]][download-url] | ||||
| [![Linux][Linux-image]][download-url] | ||||
|  | ||||
| [Web App](https://chatgpt.nextweb.fun/) / [Desktop App](https://github.com/Yidadaa/ChatGPT-Next-Web/releases) / [Issues](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [Buy Me a Coffee](https://www.buymeacoffee.com/yidadaa) | ||||
| [Web App](https://chatgpt.nextweb.fun/) / [Desktop App](https://github.com/Yidadaa/ChatGPT-Next-Web/releases) / [Issues](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [Discord](https://discord.gg/YCkeafCafC) / [Buy Me a Coffee](https://www.buymeacoffee.com/yidadaa) | ||||
|  | ||||
| [网页版](https://chatgpt.nextweb.fun/) / [客户端](https://github.com/Yidadaa/ChatGPT-Next-Web/releases) / [反馈](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [QQ 群](https://github.com/Yidadaa/ChatGPT-Next-Web/discussions/1724) / [打赏开发者](https://user-images.githubusercontent.com/16968934/227772541-5bcd52d8-61b7-488c-a203-0330d8006e2b.jpg) | ||||
|  | ||||
|   | ||||
| @@ -2,8 +2,8 @@ import { NextRequest, NextResponse } from "next/server"; | ||||
|  | ||||
| export const OPENAI_URL = "api.openai.com"; | ||||
| const DEFAULT_PROTOCOL = "https"; | ||||
| const PROTOCOL = process.env.PROTOCOL ?? DEFAULT_PROTOCOL; | ||||
| const BASE_URL = process.env.BASE_URL ?? OPENAI_URL; | ||||
| const PROTOCOL = process.env.PROTOCOL || DEFAULT_PROTOCOL; | ||||
| const BASE_URL = process.env.BASE_URL || OPENAI_URL; | ||||
| const DISABLE_GPT4 = !!process.env.DISABLE_GPT4; | ||||
|  | ||||
| export async function requestOpenai(req: NextRequest) { | ||||
|   | ||||
| @@ -99,7 +99,7 @@ export class ClientApi { | ||||
|     // 敬告二开开发者们,为了开源大模型的发展,请不要修改上述消息,此消息用于后续数据清洗使用 | ||||
|     // Please do not modify this message | ||||
|  | ||||
|     console.log("[Share]", msgs); | ||||
|     console.log("[Share]", messages, msgs); | ||||
|     const clientConfig = getClientConfig(); | ||||
|     const proxyUrl = "/sharegpt"; | ||||
|     const rawUrl = "https://sharegpt.com/api/conversations"; | ||||
|   | ||||
| @@ -101,6 +101,19 @@ | ||||
|     width: 100%; | ||||
|     margin-bottom: 10px; | ||||
|  | ||||
|     &:hover { | ||||
|       .context-drag { | ||||
|         opacity: 1; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     .context-drag { | ||||
|       display: flex; | ||||
|       align-items: center; | ||||
|       opacity: 0.5; | ||||
|       transition: all ease 0.3s; | ||||
|     } | ||||
|  | ||||
|     .context-role { | ||||
|       margin-right: 10px; | ||||
|     } | ||||
|   | ||||
| @@ -5,6 +5,7 @@ import React, { | ||||
|   useEffect, | ||||
|   useMemo, | ||||
|   useCallback, | ||||
|   Fragment, | ||||
| } from "react"; | ||||
|  | ||||
| import SendWhiteIcon from "../icons/send-white.svg"; | ||||
| @@ -975,9 +976,8 @@ export function Chat() { | ||||
|           const shouldShowClearContextDivider = i === clearContextIndex - 1; | ||||
|  | ||||
|           return ( | ||||
|             <> | ||||
|             <Fragment key={i}> | ||||
|               <div | ||||
|                 key={i} | ||||
|                 className={ | ||||
|                   isUser ? styles["chat-message-user"] : styles["chat-message"] | ||||
|                 } | ||||
| @@ -1082,7 +1082,7 @@ export function Chat() { | ||||
|                 </div> | ||||
|               </div> | ||||
|               {shouldShowClearContextDivider && <ClearContextDivider />} | ||||
|             </> | ||||
|             </Fragment> | ||||
|           ); | ||||
|         })} | ||||
|       </div> | ||||
|   | ||||
| @@ -8,6 +8,7 @@ import { | ||||
|   Modal, | ||||
|   Select, | ||||
|   showImageModal, | ||||
|   showModal, | ||||
|   showToast, | ||||
| } from "./ui-lib"; | ||||
| import { IconButton } from "./button"; | ||||
| @@ -244,11 +245,11 @@ export function RenderExport(props: { | ||||
|     } | ||||
|  | ||||
|     const renderMsgs = messages.map((v, i) => { | ||||
|       const [_, role] = v.id.split(":"); | ||||
|       const [role, _] = v.id.split(":"); | ||||
|       return { | ||||
|         id: i.toString(), | ||||
|         role: role as any, | ||||
|         content: v.innerHTML, | ||||
|         content: role === "user" ? v.textContent ?? "" : v.innerHTML, | ||||
|         date: "", | ||||
|       }; | ||||
|     }); | ||||
| @@ -287,7 +288,30 @@ export function PreviewActions(props: { | ||||
|       .share(msgs) | ||||
|       .then((res) => { | ||||
|         if (!res) return; | ||||
|         copyToClipboard(res); | ||||
|         showModal({ | ||||
|           title: Locale.Export.Share, | ||||
|           children: [ | ||||
|             <input | ||||
|               type="text" | ||||
|               value={res} | ||||
|               key="input" | ||||
|               style={{ | ||||
|                 width: "100%", | ||||
|                 maxWidth: "unset", | ||||
|               }} | ||||
|               readOnly | ||||
|               onClick={(e) => e.currentTarget.select()} | ||||
|             ></input>, | ||||
|           ], | ||||
|           actions: [ | ||||
|             <IconButton | ||||
|               icon={<CopyIcon />} | ||||
|               text={Locale.Chat.Actions.Copy} | ||||
|               key="copy" | ||||
|               onClick={() => copyToClipboard(res)} | ||||
|             />, | ||||
|           ], | ||||
|         }); | ||||
|         setTimeout(() => { | ||||
|           window.open(res, "_blank"); | ||||
|         }, 800); | ||||
|   | ||||
| @@ -61,24 +61,36 @@ | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   &:hover, | ||||
|   &:active { | ||||
|     .sidebar-drag { | ||||
|       background-color: rgba($color: #000000, $alpha: 0.01); | ||||
|  | ||||
|       svg { | ||||
|         opacity: 0.2; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| .sidebar-drag { | ||||
|   $width: 10px; | ||||
|   $width: 14px; | ||||
|  | ||||
|   position: absolute; | ||||
|   top: 0; | ||||
|   right: 0; | ||||
|   height: 100%; | ||||
|   width: $width; | ||||
|   background-color: var(--black); | ||||
|   background-color: rgba($color: #000000, $alpha: 0); | ||||
|   cursor: ew-resize; | ||||
|   opacity: 0; | ||||
|   transition: all ease 0.3s; | ||||
|   display: flex; | ||||
|   align-items: center; | ||||
|  | ||||
|   &:hover, | ||||
|   &:active { | ||||
|     opacity: 0.2; | ||||
|   svg { | ||||
|     opacity: 0; | ||||
|     margin-left: -2px; | ||||
|   } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -11,6 +11,7 @@ import CloseIcon from "../icons/close.svg"; | ||||
| import DeleteIcon from "../icons/delete.svg"; | ||||
| import EyeIcon from "../icons/eye.svg"; | ||||
| import CopyIcon from "../icons/copy.svg"; | ||||
| import DragIcon from "../icons/drag.svg"; | ||||
|  | ||||
| import { DEFAULT_MASK_AVATAR, Mask, useMaskStore } from "../store/mask"; | ||||
| import { | ||||
| @@ -42,6 +43,20 @@ import { ModelConfigList } from "./model-config"; | ||||
| import { FileName, Path } from "../constant"; | ||||
| import { BUILTIN_MASK_STORE } from "../masks"; | ||||
| import { nanoid } from "nanoid"; | ||||
| import { | ||||
|   DragDropContext, | ||||
|   Droppable, | ||||
|   Draggable, | ||||
|   OnDragEndResponder, | ||||
| } from "@hello-pangea/dnd"; | ||||
|  | ||||
| // drag and drop helper function | ||||
| function reorder<T>(list: T[], startIndex: number, endIndex: number): T[] { | ||||
|   const result = [...list]; | ||||
|   const [removed] = result.splice(startIndex, 1); | ||||
|   result.splice(endIndex, 0, removed); | ||||
|   return result; | ||||
| } | ||||
|  | ||||
| export function MaskAvatar(props: { mask: Mask }) { | ||||
|   return props.mask.avatar !== DEFAULT_MASK_AVATAR ? ( | ||||
| @@ -192,6 +207,7 @@ export function MaskConfig(props: { | ||||
| } | ||||
|  | ||||
| function ContextPromptItem(props: { | ||||
|   index: number; | ||||
|   prompt: ChatMessage; | ||||
|   update: (prompt: ChatMessage) => void; | ||||
|   remove: () => void; | ||||
| @@ -199,53 +215,67 @@ function ContextPromptItem(props: { | ||||
|   const [focusingInput, setFocusingInput] = useState(false); | ||||
|  | ||||
|   return ( | ||||
|     <div className={chatStyle["context-prompt-row"]}> | ||||
|       {!focusingInput && ( | ||||
|         <Select | ||||
|           value={props.prompt.role} | ||||
|           className={chatStyle["context-role"]} | ||||
|           onChange={(e) => | ||||
|             props.update({ | ||||
|               ...props.prompt, | ||||
|               role: e.target.value as any, | ||||
|             }) | ||||
|           } | ||||
|     <Draggable draggableId={props.prompt.id || props.index.toString()} index={props.index}> | ||||
|       {(provided) => ( | ||||
|         <div | ||||
|           className={chatStyle["context-prompt-row"]} | ||||
|           ref={provided.innerRef} | ||||
|           {...provided.draggableProps} | ||||
|           {...provided.dragHandleProps} | ||||
|         > | ||||
|           {ROLES.map((r) => ( | ||||
|             <option key={r} value={r}> | ||||
|               {r} | ||||
|             </option> | ||||
|           ))} | ||||
|         </Select> | ||||
|           {!focusingInput && ( | ||||
|             <> | ||||
|               <div className={chatStyle["context-drag"]}> | ||||
|                 <DragIcon /> | ||||
|               </div> | ||||
|               <Select | ||||
|                 value={props.prompt.role} | ||||
|                 className={chatStyle["context-role"]} | ||||
|                 onChange={(e) => | ||||
|                   props.update({ | ||||
|                     ...props.prompt, | ||||
|                     role: e.target.value as any, | ||||
|                   }) | ||||
|                 } | ||||
|               > | ||||
|                 {ROLES.map((r) => ( | ||||
|                   <option key={r} value={r}> | ||||
|                     {r} | ||||
|                   </option> | ||||
|                 ))} | ||||
|               </Select> | ||||
|             </> | ||||
|           )} | ||||
|           <Input | ||||
|             value={props.prompt.content} | ||||
|             type="text" | ||||
|             className={chatStyle["context-content"]} | ||||
|             rows={focusingInput ? 5 : 1} | ||||
|             onFocus={() => setFocusingInput(true)} | ||||
|             onBlur={() => { | ||||
|               setFocusingInput(false); | ||||
|               // If the selection is not removed when the user loses focus, some | ||||
|               // extensions like "Translate" will always display a floating bar | ||||
|               window?.getSelection()?.removeAllRanges(); | ||||
|             }} | ||||
|             onInput={(e) => | ||||
|               props.update({ | ||||
|                 ...props.prompt, | ||||
|                 content: e.currentTarget.value as any, | ||||
|               }) | ||||
|             } | ||||
|           /> | ||||
|           {!focusingInput && ( | ||||
|             <IconButton | ||||
|               icon={<DeleteIcon />} | ||||
|               className={chatStyle["context-delete-button"]} | ||||
|               onClick={() => props.remove()} | ||||
|               bordered | ||||
|             /> | ||||
|           )} | ||||
|         </div> | ||||
|       )} | ||||
|       <Input | ||||
|         value={props.prompt.content} | ||||
|         type="text" | ||||
|         className={chatStyle["context-content"]} | ||||
|         rows={focusingInput ? 5 : 1} | ||||
|         onFocus={() => setFocusingInput(true)} | ||||
|         onBlur={() => { | ||||
|           setFocusingInput(false); | ||||
|           // If the selection is not removed when the user loses focus, some | ||||
|           // extensions like "Translate" will always display a floating bar | ||||
|           window?.getSelection()?.removeAllRanges(); | ||||
|         }} | ||||
|         onInput={(e) => | ||||
|           props.update({ | ||||
|             ...props.prompt, | ||||
|             content: e.currentTarget.value as any, | ||||
|           }) | ||||
|         } | ||||
|       /> | ||||
|       {!focusingInput && ( | ||||
|         <IconButton | ||||
|           icon={<DeleteIcon />} | ||||
|           className={chatStyle["context-delete-button"]} | ||||
|           onClick={() => props.remove()} | ||||
|           bordered | ||||
|         /> | ||||
|       )} | ||||
|     </div> | ||||
|     </Draggable> | ||||
|   ); | ||||
| } | ||||
|  | ||||
| @@ -267,17 +297,41 @@ export function ContextPrompts(props: { | ||||
|     props.updateContext((context) => (context[i] = prompt)); | ||||
|   }; | ||||
|  | ||||
|   const onDragEnd: OnDragEndResponder = (result) => { | ||||
|     if (!result.destination) { | ||||
|       return; | ||||
|     } | ||||
|     const newContext = reorder( | ||||
|       context, | ||||
|       result.source.index, | ||||
|       result.destination.index, | ||||
|     ); | ||||
|     props.updateContext((context) => { | ||||
|       context.splice(0, context.length, ...newContext); | ||||
|     }); | ||||
|   }; | ||||
|  | ||||
|   return ( | ||||
|     <> | ||||
|       <div className={chatStyle["context-prompt"]} style={{ marginBottom: 20 }}> | ||||
|         {context.map((c, i) => ( | ||||
|           <ContextPromptItem | ||||
|             key={i} | ||||
|             prompt={c} | ||||
|             update={(prompt) => updateContextPrompt(i, prompt)} | ||||
|             remove={() => removeContextPrompt(i)} | ||||
|           /> | ||||
|         ))} | ||||
|         <DragDropContext onDragEnd={onDragEnd}> | ||||
|           <Droppable droppableId="context-prompt-list"> | ||||
|             {(provided) => ( | ||||
|               <div ref={provided.innerRef} {...provided.droppableProps}> | ||||
|                 {context.map((c, i) => ( | ||||
|                   <ContextPromptItem | ||||
|                     index={i} | ||||
|                     key={c.id} | ||||
|                     prompt={c} | ||||
|                     update={(prompt) => updateContextPrompt(i, prompt)} | ||||
|                     remove={() => removeContextPrompt(i)} | ||||
|                   /> | ||||
|                 ))} | ||||
|                 {provided.placeholder} | ||||
|               </div> | ||||
|             )} | ||||
|           </Droppable> | ||||
|         </DragDropContext> | ||||
|  | ||||
|         <div className={chatStyle["context-prompt-row"]}> | ||||
|           <IconButton | ||||
|   | ||||
| @@ -10,6 +10,7 @@ import AddIcon from "../icons/add.svg"; | ||||
| import CloseIcon from "../icons/close.svg"; | ||||
| import MaskIcon from "../icons/mask.svg"; | ||||
| import PluginIcon from "../icons/plugin.svg"; | ||||
| import DragIcon from "../icons/drag.svg"; | ||||
|  | ||||
| import Locale from "../locales"; | ||||
|  | ||||
| @@ -198,7 +199,9 @@ export function SideBar(props: { className?: string }) { | ||||
|       <div | ||||
|         className={styles["sidebar-drag"]} | ||||
|         onMouseDown={(e) => onDragMouseDown(e as any)} | ||||
|       ></div> | ||||
|       > | ||||
|         <DragIcon /> | ||||
|       </div> | ||||
|     </div> | ||||
|   ); | ||||
| } | ||||
|   | ||||
							
								
								
									
										1
									
								
								app/icons/drag.svg
									
									
									
									
									
										Normal file
									
								
							
							
								
									
								
								
								
								
								
									
									
								
							
						
						
									
										1
									
								
								app/icons/drag.svg
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16" fill="none"><g opacity="1"  transform="translate(0 0)  rotate(0)"><mask id="bg-mask-0" fill="white"><use transform="translate(0 0)  rotate(0)" xlink:href="#path_0"></use></mask><g mask="url(#bg-mask-0)" ><path id="路径 1" fill-rule="evenodd" style="fill:#333333" opacity="1" d="M6.33663,3.33c0,0.74 -0.6,1.34 -1.34,1.34c-0.73,0 -1.33,-0.6 -1.33,-1.34c0,-0.73 0.6,-1.33 1.33,-1.33c0.74,0 1.34,0.6 1.34,1.33zM4.99663,9.33c-0.73,0 -1.33,-0.59 -1.33,-1.33c0,-0.74 0.6,-1.33 1.33,-1.33c0.74,0 1.34,0.59 1.34,1.33c0,0.74 -0.6,1.33 -1.34,1.33zM4.99663,14c-0.73,0 -1.33,-0.6 -1.33,-1.33c0,-0.74 0.6,-1.34 1.33,-1.34c0.74,0 1.34,0.6 1.34,1.34c0,0.73 -0.6,1.33 -1.34,1.33z"></path><path id="路径 2" fill-rule="evenodd" style="fill:#333333" opacity="1" d="M12.3366,3.33c0,0.74 -0.6,1.34 -1.34,1.34c-0.73,0 -1.32997,-0.6 -1.32997,-1.34c0,-0.73 0.59997,-1.33 1.32997,-1.33c0.74,0 1.34,0.6 1.34,1.33zM10.9966,9.33c-0.73,0 -1.32997,-0.59 -1.32997,-1.33c0,-0.74 0.59997,-1.33 1.32997,-1.33c0.74,0 1.34,0.59 1.34,1.33c0,0.74 -0.6,1.33 -1.34,1.33zM10.9966,14c-0.73,0 -1.32997,-0.6 -1.32997,-1.33c0,-0.74 0.59997,-1.34 1.32997,-1.34c0.74,0 1.34,0.6 1.34,1.34c0,0.73 -0.6,1.33 -1.34,1.33z"></path></g></g><defs><rect id="path_0" x="0" y="0" width="16" height="16" /></defs></svg> | ||||
| After (image error) Size: 1.4 KiB | 
| @@ -3,8 +3,9 @@ import "./styles/globals.scss"; | ||||
| import "./styles/markdown.scss"; | ||||
| import "./styles/highlight.scss"; | ||||
| import { getClientConfig } from "./config/client"; | ||||
| import { type Metadata } from 'next'; | ||||
|  | ||||
| export const metadata = { | ||||
| export const metadata: Metadata = { | ||||
|   title: "ChatGPT Next Web", | ||||
|   description: "Your personal ChatGPT Chat Bot.", | ||||
|   viewport: { | ||||
|   | ||||
| @@ -589,7 +589,7 @@ export const useChatStore = create<ChatStore>()( | ||||
|     }), | ||||
|     { | ||||
|       name: StoreKey.Chat, | ||||
|       version: 3, | ||||
|       version: 3.1, | ||||
|       migrate(persistedState, version) { | ||||
|         const state = persistedState as any; | ||||
|         const newState = JSON.parse(JSON.stringify(state)) as ChatStore; | ||||
| @@ -617,6 +617,23 @@ export const useChatStore = create<ChatStore>()( | ||||
|           }); | ||||
|         } | ||||
|  | ||||
|         // Enable `enableInjectSystemPrompts` attribute for old sessions. | ||||
|         // Resolve issue of old sessions not automatically enabling. | ||||
|         if (version < 3.1) { | ||||
|           newState.sessions.forEach((s) => { | ||||
|             if ( | ||||
|               // Exclude those already set by user | ||||
|               !s.mask.modelConfig.hasOwnProperty("enableInjectSystemPrompts") | ||||
|             ) { | ||||
|               // Because users may have changed this configuration, | ||||
|               // the user's current configuration is used instead of the default | ||||
|               const config = useAppConfig.getState(); | ||||
|               s.mask.modelConfig.enableInjectSystemPrompts = | ||||
|                 config.modelConfig.enableInjectSystemPrompts; | ||||
|             } | ||||
|           }); | ||||
|         } | ||||
|  | ||||
|         return newState; | ||||
|       }, | ||||
|     }, | ||||
|   | ||||
| @@ -30,6 +30,9 @@ const nextConfig = { | ||||
|   images: { | ||||
|     unoptimized: mode === "export", | ||||
|   }, | ||||
|   experimental: { | ||||
|     forceSwcTransforms: true, | ||||
|   }, | ||||
| }; | ||||
|  | ||||
| if (mode !== "export") { | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
|   }, | ||||
|   "package": { | ||||
|     "productName": "ChatGPT Next Web", | ||||
|     "version": "2.8.9" | ||||
|     "version": "2.9.0" | ||||
|   }, | ||||
|   "tauri": { | ||||
|     "allowlist": { | ||||
|   | ||||
							
								
								
									
										12
									
								
								yarn.lock
									
									
									
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										12
									
								
								yarn.lock
									
									
									
									
									
								
							| @@ -5322,14 +5322,14 @@ schema-utils@^3.1.1, schema-utils@^3.2.0: | ||||
|     ajv-keywords "^3.5.2" | ||||
|  | ||||
| semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: | ||||
|   version "6.3.0" | ||||
|   resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" | ||||
|   integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== | ||||
|   version "6.3.1" | ||||
|   resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" | ||||
|   integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== | ||||
|  | ||||
| semver@^7.3.7: | ||||
|   version "7.3.8" | ||||
|   resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" | ||||
|   integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== | ||||
|   version "7.5.4" | ||||
|   resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" | ||||
|   integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== | ||||
|   dependencies: | ||||
|     lru-cache "^6.0.0" | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user