feat(gpt-runner-web): optimize css styles

This commit is contained in:
JinmingYang
2023-08-06 19:01:56 +08:00
parent a282a34998
commit ceb9749af3
42 changed files with 995 additions and 449 deletions

View File

@@ -48,13 +48,13 @@
"dependencies": {
"@nicepkg/gpt-runner-shared": "workspace:*",
"ignore": "^5.2.4",
"langchain": "^0.0.118",
"unconfig": "^0.3.9",
"langchain": "^0.0.122",
"unconfig": "^0.3.10",
"uuid": "^9.0.0",
"zod": "^3.21.4"
},
"devDependencies": {
"@anthropic-ai/sdk": "^0.5.8",
"@anthropic-ai/sdk": "^0.5.10",
"openai": "^3.3.0"
}
}
}

View File

@@ -106,8 +106,8 @@
"launch-editor": "^2.6.0",
"minimatch": "^9.0.3",
"open": "^8.4.2",
"socket.io": "^4.7.1",
"socket.io-client": "^4.7.1",
"socket.io": "^4.7.2",
"socket.io-client": "^4.7.2",
"zod": "^3.21.4",
"zod-to-json-schema": "^3.21.4"
},
@@ -116,4 +116,4 @@
"@types/ip": "^1.1.0",
"express": "^4.18.2"
}
}
}

View File

@@ -1,9 +1,9 @@
{
"common": {
"notificationConfig": {
"createAt": "2023-07-24 23:31:22",
"createAt": "2023-08-06 18:37:10",
"title": "GPT Runner Notification",
"message": "v1.2.0 is release"
"message": "v1.2.2 is release"
},
"releaseConfig": {
"createAt": "2023-07-24 23:41:04",
@@ -23,18 +23,18 @@
},
"zh_CN": {
"notificationConfig": {
"createAt": "2023-07-24 23:31:26",
"createAt": "2023-08-06 18:37:06",
"title": "GPT Runner 通知",
"message": "\n### 版本更新到了 v1.2.0\n1. 重启 vscode 即可扩展处更新\n2. cli 执行 `npm i -g gptr` 即可更新\n\n### 本次功能更新\n1. 针对语言为简体中文的用户提供 OpenAI API key 供应商,也就是你可以白嫖了。\n2. 点击左上角设置,切换供应商即可。\n3. 本次 API Key 由慷慨大方的 `剑廿三` 提供,让我们把掌声送给他。\n\n### 交流\n1. 想进群交流的加 wechat: `qq2214962083`\n "
"message": "\n### 🚀 新版 v1.2.2 (2023-08-06)\n\n> vscode 用户重启 vscode 即可扩展处更新\n>\n> cli 用户执行 `npm i -g gptr` 即可更新\n\n1. 针对语言为简体中文的用户提供 OpenAI API key 第三方供应商,也就是你可以白嫖了。\n2. 点击左上角设置,切换供应商即可。\n3. 本次 API Key 由慷慨大方的 `朝云云` 提供,让我们把掌声送给他。\n\n### 💬 交流\n\n1. 想进群交流的加 wechat: `qq2214962083`\n2. 所有捐赠 API Key 的朋友,都可以在这里免费展示 50 字以内的广告直到 API Key 失效,如果你也想捐赠 API Key可以联系我。\n3. 此处广告仅供展示,与 GPT-Runner 无关,**若有财产交易,请自行承担风险**\n\n### 📢 YunAI - AI驱动的WEB对话应用 (朝云云供应商广告)\n\n🔵 [**点击进入**](https://faschat.zyai.online/)注册即送大量3.5使用次数\n\n如需适配 GPT-Runner 的 4.0-32k 或 Claude 接口服务,可 VX 联系:`YunAi0101`\n\n "
},
"vendorsConfig": {
"createAt": "2023-07-24 23:40:49",
"createAt": "2023-08-06 18:37:22",
"openai": [
{
"vendorName": "xabcai",
"vendorName": "朝云云供应商",
"vendorSecrets": {
"basePath": "https://api.xabcai.com/v1",
"apiKey": "c2stWHZQeGJQMVBySFduZDJFZ0xpa0lKTlQzOTNoc3pZdDdmN0NNZUozSE1pdkw2QVdx"
"basePath": "http://8.130.89.91:3000/v1",
"apiKey": "c2stQUlBc2NTUGk2RVR0cXVSVThmMmYzODU1NTk4NzQ4M2U4YjE1QWU4MzEwMjMxZTRi"
}
}
],

View File

@@ -51,6 +51,7 @@
"settings_tab_settings": "Einstellungen",
"settings_tab_config_info": "Konfigurationsinformationen",
"settings_tab_about": "Über",
"settings_tab_notifications": "Benachrichtigungen",
"override_model_type": "Modelltyp überschreiben",
"default": "Standard",
"override_settings": "Einstellungen überschreiben",
@@ -101,4 +102,4 @@
"file_editor_forgot_save_tips_title": "Möchten Sie die Änderungen an {{fileName}} speichern?",
"file_editor_forgot_save_tips_content": "Ihre Änderungen gehen verloren, wenn Sie sie nicht speichern."
}
}
}

View File

@@ -51,6 +51,7 @@
"settings_tab_settings": "Settings",
"settings_tab_config_info": "Config Info",
"settings_tab_about": "About",
"settings_tab_notifications": "Notifications",
"override_model_type": "Override Model Type",
"default": "Default",
"override_settings": "Override Settings",
@@ -101,4 +102,4 @@
"file_editor_forgot_save_tips_title": "Do you want to save changes to {{fileName}}?",
"file_editor_forgot_save_tips_content": "Your changes will be lost if you don't save them."
}
}
}

View File

@@ -51,6 +51,7 @@
"settings_tab_settings": "設定",
"settings_tab_config_info": "設定情報",
"settings_tab_about": "情報",
"settings_tab_notifications": "通知",
"override_model_type": "モデルタイプを上書き",
"default": "デフォルト",
"override_settings": "設定を上書き",
@@ -101,4 +102,4 @@
"file_editor_forgot_save_tips_title": "変更を{{fileName}}に保存しますか?",
"file_editor_forgot_save_tips_content": "保存しない場合、変更は失われます。"
}
}
}

View File

@@ -51,6 +51,7 @@
"settings_tab_settings": "设置",
"settings_tab_config_info": "配置信息",
"settings_tab_about": "关于",
"settings_tab_notifications": "通知",
"override_model_type": "覆盖模型类型",
"default": "默认",
"override_settings": "覆盖设置",
@@ -101,4 +102,4 @@
"file_editor_forgot_save_tips_title": "你想要保存对{{fileName}}的更改吗?",
"file_editor_forgot_save_tips_content": "如果你不保存,你的改动将会丢失."
}
}
}

View File

@@ -51,6 +51,7 @@
"settings_tab_settings": "設定",
"settings_tab_config_info": "配置信息",
"settings_tab_about": "關於",
"settings_tab_notifications": "通知",
"override_model_type": "覆蓋模型類型",
"default": "默認",
"override_settings": "覆寫設置",
@@ -101,4 +102,4 @@
"file_editor_forgot_save_tips_title": "你想要保存對{{fileName}}的更改嗎?",
"file_editor_forgot_save_tips_content": "如果你不保存,你的改動將會丟失."
}
}
}

View File

@@ -1,5 +1,5 @@
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { type ComponentType, type FC, type PropsWithChildren, Suspense, memo, useEffect } from 'react'
import { type ComponentType, type FC, type PropsWithChildren, Suspense, memo, useEffect, useMemo } from 'react'
import type { FallbackProps } from 'react-error-boundary'
import { ErrorBoundary } from 'react-error-boundary'
import { useTranslation } from 'react-i18next'
@@ -16,6 +16,7 @@ import { Toast } from './components/toast'
import { LoadingView } from './components/loading-view'
import { ConfettiProvider } from './store/context/confetti-context'
import { ModalProvider } from './store/context/modal-context'
import { useCssVarColor } from './hooks/use-css-var-color.hook'
const queryClient = new QueryClient({
defaultOptions: {
@@ -86,10 +87,38 @@ export const AppProviders: FC<PropsWithChildren> = memo(({ children }) => {
})
export const App: FC = memo(() => {
const { rgba: backgroundRgba, isDark } = useCssVarColor({
cssVarName: '--panel-view-background',
})
const { rgba: borderRgba } = useCssVarColor({
cssVarName: '--panel-view-border',
})
const myBackdropBg = useMemo(() => {
if (!backgroundRgba)
return 'transparent'
return `rgba(${backgroundRgba.r}, ${backgroundRgba.g}, ${backgroundRgba.b}, ${isDark ? '0.6' : '0.8'})`
}, [backgroundRgba, isDark])
const [myScrollbarBg, myScrollbarHoverBg] = useMemo(() => {
if (!borderRgba)
return ['var(--panel-view-border)', 'var(--panel-view-border)'] as const
return [`rgba(${borderRgba.r}, ${borderRgba.g}, ${borderRgba.b}, 0.2)`, `rgba(${borderRgba.r}, ${borderRgba.g}, ${borderRgba.b}, 0.8)`] as const
}, [borderRgba, isDark])
return (
<Suspense fallback={<LoadingView absolute />}>
<AppProviders>
<GlobalStyle />
<GlobalStyle appendCss={`
body {
--my-backdrop-bg: ${myBackdropBg};
--my-scrollbar-bg: ${myScrollbarBg};
--my-scrollbar-hover-bg: ${myScrollbarHoverBg};
}
`} />
<GlobalThemeStyle />
<MarkdownStyle />
<AppRouter />

View File

@@ -1,5 +1,6 @@
import { styled } from 'styled-components'
import { Logo } from '../logo'
import { backDropBg } from '../../styles/utils'
export const Wrapper = styled.div`
display: flex;
@@ -9,6 +10,7 @@ export const Wrapper = styled.div`
flex-shrink: 0;
padding: 0.5rem;
border-top: 1px solid var(--panel-view-border);
${backDropBg}
`
export const ToolbarWrapper = styled.div`
@@ -34,6 +36,18 @@ export const TextAreaWrapper = styled.div`
&:hover {
border-color: var(--focus-border);
}
.monaco-editor {
background-color: transparent !important;
.margin {
background-color: transparent !important;
}
}
.monaco-editor-background {
background-color: transparent !important;
}
}
`

View File

@@ -138,7 +138,7 @@ export const ChatMessageInput: FC<ChatMessageInputProps> = memo((props) => {
{toolbarSlot}
{showTopLogo && <LogoWrapper>
<StyledLogo color={'var(--panel-tab-foreground)'} {...logoProps} ></StyledLogo>
<StyledLogo color={'var(--focus-border)'} {...logoProps} ></StyledLogo>
</LogoWrapper>}
</ToolbarWrapper>

View File

@@ -1,5 +1,4 @@
import { styled } from 'styled-components'
import type { MessageItemProps } from '.'
export const MsgWrapper = styled.div<{ $isMe: boolean }>`
display: flex;
@@ -26,7 +25,7 @@ export const MsgContentWrapper = styled.div<{ $isMe: boolean }>`
position: relative;
`
export const MsgContent = styled.div<{ $showToolbar: MessageItemProps['showToolbar']; $isMe: boolean }>`
export const MsgContent = styled.div<{ $isMe: boolean }>`
display: flex;
flex-direction: column;
border-radius: 0.5rem;
@@ -43,34 +42,6 @@ export const MsgContent = styled.div<{ $showToolbar: MessageItemProps['showToolb
& .msg-content-footer {
flex-wrap: wrap;
}
${({ $showToolbar, $isMe }) => ($showToolbar === 'hover'
? `
position: absolute;
top: 0;
${$isMe ? 'right' : 'left'}: 0;
z-index: 1;
min-height: 100%;
& .msg-content-footer {
opacity: 0;
margin-top: 0;
height: 0;
}
&:hover .msg-content-footer {
opacity: 1;
margin-top: 0.5rem;
height: auto;
}
`
: $showToolbar === 'never'
? `
& .msg-content-footer {
display: none;
}
`
: '')}
`
export const MsgContentFooterWrapper = styled.div`

View File

@@ -5,7 +5,6 @@ import { ChatMessageStatus, ChatRole } from '@nicepkg/gpt-runner-shared/common'
import type { MessageTextViewProps } from '../chat-message-text-view'
import { MessageTextView } from '../chat-message-text-view'
import { Icon } from '../icon'
import { useHover } from '../../hooks/use-hover.hook'
import { MsgAvatarWrapper, MsgContent, MsgContentFooterWrapper, MsgContentWrapper, MsgWrapper } from './chat-message-item.styles'
export interface BuildMessageToolbarState extends SingleChatMessage {
@@ -13,7 +12,6 @@ export interface BuildMessageToolbarState extends SingleChatMessage {
}
export interface MessageItemProps extends SingleChatMessage, Partial<MessageTextViewProps> {
status: ChatMessageStatus
showToolbar?: 'always' | 'hover' | 'never'
showAvatar?: boolean
style?: React.CSSProperties
buildMessageToolbar?: (state: BuildMessageToolbarState) => React.ReactNode
@@ -26,34 +24,12 @@ export const MessageItem: FC<MessageItemProps> = memo((props) => {
status,
style,
showAvatar = false,
showToolbar = 'hover',
buildCodeToolbar,
buildMessageToolbar,
...messageTextViewProps
} = props
const [hoverContentRef, isContentHover] = useHover()
const contents = status === ChatMessageStatus.Pending ? `${text}\u{258A}` : text
const renderContent = ({ showToolbar }: Pick<MessageItemProps, 'showToolbar'>) => {
return <MsgContent
$showToolbar={showToolbar}
$isMe={name === ChatRole.User}>
<MessageTextView
contents={contents}
buildCodeToolbar={buildCodeToolbar}
{...messageTextViewProps}
/>
<MsgContentFooterWrapper className='msg-content-footer'>
{buildMessageToolbar?.({
name,
text,
status,
})}
</MsgContentFooterWrapper>
</MsgContent>
}
return (
<MsgWrapper style={style} $isMe={name === ChatRole.User}>
@@ -61,29 +37,26 @@ export const MessageItem: FC<MessageItemProps> = memo((props) => {
<Icon className={clsx(name === ChatRole.User ? 'codicon-account' : 'codicon-github')} />
</MsgAvatarWrapper>
<MsgContentWrapper
ref={hoverContentRef}
$isMe={name === ChatRole.User}
style={{
maxWidth: showAvatar ? 'calc(100% - 6rem)' : '100%',
}}
>
{
showToolbar === 'hover'
? (
<>
{/* use absolute position to render a same content with toolbar
this is helpful to not change height */}
{renderContent({
showToolbar: 'never',
})}
{isContentHover && renderContent({
showToolbar: 'hover',
})}
</>)
: renderContent({
showToolbar,
})
}
<MsgContent $isMe={name === ChatRole.User}>
<MessageTextView
contents={contents}
buildCodeToolbar={buildCodeToolbar}
{...messageTextViewProps}
/>
<MsgContentFooterWrapper className='msg-content-footer'>
{buildMessageToolbar?.({
name,
text,
status,
})}
</MsgContentFooterWrapper>
</MsgContent>
</MsgContentWrapper>
</MsgWrapper>
)

View File

@@ -4,7 +4,10 @@ import { MessageItem } from '../chat-message-item'
import { Panel } from './chat-message-panel.styles'
export interface ChatMessagePanelProps {
style?: React.CSSProperties
topSlot?: React.ReactNode
messageItems: MessageItemProps[]
bottomSlot?: React.ReactNode
}
// Define the ref type for the component
@@ -12,13 +15,15 @@ export type ChatMessagePanelRef = HTMLDivElement
// Use ForwardRefRenderFunction to define the component with an explicit ref parameter
export const ChatMessagePanel = memo(forwardRef<ChatMessagePanelRef, ChatMessagePanelProps>((props, ref) => {
const { messageItems } = props
const { messageItems, style, topSlot, bottomSlot } = props
return (
<Panel ref={ref}>
<Panel style={style} ref={ref}>
{topSlot}
{messageItems.map((item, index) => {
return <MessageItem key={index} {...item}></MessageItem>
})}
{bottomSlot}
</Panel>
)
}))

View File

@@ -5,6 +5,7 @@ const lineWeight = '8px'
export const DragLine = styled.div<{ $dragLineColor: string; $dragLineActiveColor: string; $dragLineWidth: string }>`
position: absolute;
z-index: 10;
/* background: ${({ $dragLineColor }) => $dragLineColor}; */
border-color: ${({ $dragLineColor }) => $dragLineColor};
border-style: solid;

View File

@@ -1,9 +1,8 @@
import type { FC } from 'react'
import { memo, useEffect, useMemo, useRef } from 'react'
import { forwardRef, memo, useEffect, useImperativeHandle, useMemo, useRef } from 'react'
import type { UserDragConfig } from '@use-gesture/react'
import { useDrag } from '@use-gesture/react'
import type { SpringOptions } from 'framer-motion'
import { motion, useMotionValue, useSpring } from 'framer-motion'
import type { MotionValue, SpringOptions } from 'framer-motion'
import { AnimatePresence, motion, useMotionValue, useSpring } from 'framer-motion'
import { isDomHidden } from '../../helpers/utils'
import { DragLine } from './drag-resize-view.styles'
@@ -16,6 +15,12 @@ export interface DragDirectionConfig {
*/
boundary: number[]
}
export interface DragResizeViewRef {
motionDragWidth: MotionValue<number> | undefined
motionDragHeight: MotionValue<number> | undefined
}
export interface DragResizeViewProps {
initWidth?: number
initHeight?: number
@@ -29,14 +34,10 @@ export interface DragResizeViewProps {
dragLineActiveColor?: string
dragLineWidth?: string
children: React.ReactNode
// drawer
drawerClassName?: string
drawerStyle?: React.CSSProperties
open?: boolean
}
export const DragResizeView: FC<DragResizeViewProps> = memo((props) => {
export const DragResizeView = memo(forwardRef<DragResizeViewRef, DragResizeViewProps>((props, ref) => {
const {
initWidth,
initHeight,
@@ -51,13 +52,11 @@ export const DragResizeView: FC<DragResizeViewProps> = memo((props) => {
dragLineWidth = '1px',
children,
// drawer
drawerClassName,
drawerStyle,
// drag
open = true,
} = props
const ref = useRef<HTMLDivElement>(null)
const dragRef = useRef<HTMLDivElement>(null)
const finalWidth = useMotionValue(initWidth)
const finalHeight = useMotionValue(initHeight)
@@ -83,9 +82,9 @@ export const DragResizeView: FC<DragResizeViewProps> = memo((props) => {
useEffect(() => {
const handleWindowResize = () => {
if (ref.current && !isDomHidden(ref.current)) {
finalWidth.set(ref.current.offsetWidth)
finalHeight.set(ref.current.offsetHeight)
if (dragRef.current && !isDomHidden(dragRef.current)) {
finalWidth.set(dragRef.current.offsetWidth)
finalHeight.set(dragRef.current.offsetHeight)
}
}
@@ -198,39 +197,33 @@ export const DragResizeView: FC<DragResizeViewProps> = memo((props) => {
const dragWidth = useSpring(finalWidth, springConfig)
const dragHeight = useSpring(finalHeight, springConfig)
// drawer
const drawerWidth = useSpring(finalWidth, springConfig)
const drawerHeight = useSpring(finalHeight, springConfig)
useEffect(() => {
drawerWidth.set(!open && isX ? 0 : finalWidth.get())
// drawer
dragWidth.set(!open && isX ? 0 : finalWidth.get())
}, [open, isX])
useEffect(() => {
drawerHeight.set(!open && isY ? 0 : finalHeight.get())
// drawer
dragHeight.set(!open && isY ? 0 : finalHeight.get())
}, [open, isY])
// drawer
return <motion.div
className={drawerClassName}
style={{
...drawerStyle,
width: initWidth !== undefined ? drawerWidth : '',
height: initHeight !== undefined ? drawerHeight : '',
overflow: 'hidden',
}}
>
useImperativeHandle(ref, () => ({
motionDragWidth: initWidth !== undefined ? dragWidth : undefined,
motionDragHeight: initHeight !== undefined ? dragHeight : undefined,
}), [initWidth, initHeight])
{/* drag */}
// drag & drag
return <AnimatePresence>
<motion.div
ref={dragRef}
className={dragClassName}
style={{
...dragStyle,
width: initWidth !== undefined ? dragWidth : '',
height: initHeight !== undefined ? dragHeight : '',
position: 'relative',
overflow: 'hidden',
...dragStyle,
}}
ref={ref}
>
{children}
@@ -250,7 +243,7 @@ export const DragResizeView: FC<DragResizeViewProps> = memo((props) => {
: null
})}
</motion.div>
</motion.div>
})
</AnimatePresence>
}))
DragResizeView.displayName = 'DragResizeView'

View File

@@ -4,8 +4,7 @@ import type { FC } from 'react'
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import * as monaco from 'monaco-editor'
import type { MonacoEditorInstance } from '../../types/monaco-editor'
import { useGlobalStore } from '../../store/zustand/global'
import { isDarkTheme } from '../../styles/themes'
import { useDarkTheme } from '../../hooks/use-css-var-color.hook'
import { initLanguageSettings } from './monaco/init-languages-settings'
import { createSwitchLanguageCommand } from './monaco/commands/switch-language'
import { createCtrlSToSaveAction } from './monaco/actions/ctrls-to-save'
@@ -67,10 +66,7 @@ export const Editor: FC<EditorProps> = memo((props) => {
const defaultLanguage = defaultLanguageFromProps || DEFAULT_LANGUAGE
const language = languageFromProps || currentExtLanguage || defaultLanguage
const {
themeName,
} = useGlobalStore()
const isDark = isDarkTheme(themeName)
const isDark = useDarkTheme()
const handleEditorWillMount = useCallback((monaco: Monaco) => {
// here is the monaco instance

View File

@@ -1,13 +1,13 @@
import { css, styled } from 'styled-components'
export const ButtonWrapper = styled.div<{ $hoverShowText?: boolean }>`
export const ButtonWrapper = styled.div<{ $transparentBgWhenNotHover: boolean; $hoverShowText?: boolean }>`
display: flex;
flex-shrink: 0;
overflow: hidden;
vscode-button {
&:not(:hover) {
background: transparent;
background: ${({ $transparentBgWhenNotHover }) => ($transparentBgWhenNotHover ? 'transparent' : '')};
}
/* &::part(control) {

View File

@@ -17,6 +17,7 @@ export interface IconButtonProps extends GetComponentProps<InstanceType<typeof V
radius?: string
showText?: boolean
hoverShowText?: boolean
transparentBgWhenNotHover?: boolean
isAnimating?: boolean
animateDuration?: number
animateEase?: Tween['ease']
@@ -35,6 +36,7 @@ export const IconButton: FC<IconButtonProps> = memo((props) => {
iconClassName,
showText = true,
hoverShowText = true,
transparentBgWhenNotHover = false,
radius = '0.25rem',
className, style,
buttonStyle,
@@ -95,7 +97,7 @@ export const IconButton: FC<IconButtonProps> = memo((props) => {
},
}
return <ButtonWrapper className={clsx('icon-button', className)} style={style} $hoverShowText={hoverShowText}>
return <ButtonWrapper $transparentBgWhenNotHover={transparentBgWhenNotHover} className={clsx('icon-button', className)} style={style} $hoverShowText={hoverShowText}>
<VSCodeButton
onClick={handleClick}
appearance="secondary"

View File

@@ -22,7 +22,8 @@ export const ModalContentWrapper = styled.div`
flex-direction: column;
max-width: 100%;
max-height: 80vh;
width: min(500px, calc(100vw - 1rem));
width: 100%;
max-width: min(800px, calc(100vw - 1rem));
overflow: hidden;
background: var(--panel-view-background);
border-radius: 0.5rem;

View File

@@ -154,7 +154,7 @@ export const PopoverMenu: React.FC<PopoverMenuProps> = memo((props) => {
},
menu: {
maxHeight: Math.min(childrenY, windowHeight) - minusHeightSpace,
height: childrenInMenuWhenOpen ? 'unset' : '100vh',
height: childrenInMenuWhenOpen ? 'unset' : 'auto',
},
},
bottom: {
@@ -164,7 +164,7 @@ export const PopoverMenu: React.FC<PopoverMenuProps> = memo((props) => {
},
menu: {
maxHeight: Math.min(windowHeight - childrenY - childrenHeight, windowHeight) - minusHeightSpace,
height: childrenInMenuWhenOpen ? 'unset' : '100vh',
height: childrenInMenuWhenOpen ? 'unset' : 'auto',
},
},
}
@@ -234,7 +234,9 @@ export const PopoverMenu: React.FC<PopoverMenuProps> = memo((props) => {
>
{yPosition === 'bottom' && renderToolbar()}
<MenuChildrenWrapper>
<MenuChildrenWrapper style={{
display: childrenInMenuWhenOpen ? '' : 'flex',
}}>
{yPosition === 'top' && buildMenuSlot()}
{childrenInMenuWhenOpen
@@ -250,6 +252,7 @@ export const PopoverMenu: React.FC<PopoverMenuProps> = memo((props) => {
</MenuChildrenWrapper>
{yPosition === 'top' && renderToolbar()}
</Menu>
</MenuMask>
</div>

View File

@@ -1,4 +1,5 @@
import styled from 'styled-components'
import { backDropBg } from '../../styles/utils'
export const MenuMask = styled.div`
overflow: hidden;
@@ -16,12 +17,13 @@ export const Toolbar = styled.div`
`
export const Menu = styled.div`
position: relative;
display: flex;
flex-direction: column;
background-color: var(--panel-view-background);
border: 1px solid var(--panel-view-border);
border-radius: 0.5rem;
overflow: hidden;
${backDropBg}
`
export const MenuChildrenWrapper = styled.div`
@@ -29,7 +31,6 @@ export const MenuChildrenWrapper = styled.div`
overflow-x: hidden;
overflow-y: auto;
& > .icon-button {
margin-left: 0;
margin-right: 0;

View File

@@ -5,7 +5,8 @@ import type { TreeProps } from '../tree'
import { Tree } from '../tree'
import type { TreeItemBaseStateOtherInfo, TreeItemProps } from '../tree-item'
import { LoadingView } from '../loading-view'
import { SidebarHeader, SidebarSearch, SidebarSearchRightWrapper, SidebarSearchWrapper, SidebarTreeWrapper, SidebarUnderSearchWrapper, SidebarWrapper } from './sidebar.styles'
import { useElementSizeRealTime } from '../../hooks/use-element-size-real-time.hook'
import { SidebarHeader, SidebarSearch, SidebarSearchRightWrapper, SidebarSearchWrapper, SidebarTopBlurContainer, SidebarTreeWrapper, SidebarUnderSearchWrapper, SidebarWrapper } from './sidebar.styles'
export interface SidebarProps<OtherInfo extends TreeItemBaseStateOtherInfo = TreeItemBaseStateOtherInfo> {
defaultSearchKeyword?: string
@@ -37,6 +38,7 @@ function Sidebar_<OtherInfo extends TreeItemBaseStateOtherInfo = TreeItemBaseSta
const [searchKeyword, setSearchKeyword] = useState(defaultSearchKeyword)
const [debouncedSearchKeyword, setDebouncedSearchKeyword] = useState(defaultSearchKeyword)
const [finalItems, setFinalItems] = useState<TreeItemProps<OtherInfo>[]>([])
const [SidebarTopBlurContainerRef, { height: SidebarTopBlurContainerHeight }] = useElementSizeRealTime<HTMLDivElement>()
useDebounce(() => {
setDebouncedSearchKeyword(searchKeyword)
@@ -78,26 +80,38 @@ function Sidebar_<OtherInfo extends TreeItemBaseStateOtherInfo = TreeItemBaseSta
return <SidebarWrapper style={{
flexDirection: reverseTreeUi ? 'column-reverse' : 'column',
}}>
<SidebarHeader>
{buildTopToolbarSlot?.()}
</SidebarHeader>
<SidebarSearchWrapper>
<SidebarSearch
placeholder={placeholder}
value={searchKeyword}
onInput={(e: any) => {
setSearchKeyword(e.target?.value)
}}>
</SidebarSearch>
<SidebarSearchRightWrapper>
{buildSearchRightSlot?.()}
</SidebarSearchRightWrapper>
</SidebarSearchWrapper>
<SidebarUnderSearchWrapper>
{buildUnderSearchSlot?.()}
</SidebarUnderSearchWrapper>
<SidebarTopBlurContainer ref={SidebarTopBlurContainerRef}>
<SidebarHeader>
{buildTopToolbarSlot?.()}
</SidebarHeader>
<SidebarSearchWrapper>
<SidebarSearch
placeholder={placeholder}
value={searchKeyword}
onInput={(e: any) => {
setSearchKeyword(e.target?.value)
}}>
</SidebarSearch>
<SidebarSearchRightWrapper>
{buildSearchRightSlot?.()}
</SidebarSearchRightWrapper>
</SidebarSearchWrapper>
<SidebarUnderSearchWrapper>
{buildUnderSearchSlot?.()}
</SidebarUnderSearchWrapper>
</SidebarTopBlurContainer>
<SidebarTreeWrapper>
{loading && <LoadingView absolute></LoadingView>}
<div style={{
flexShrink: '0',
width: '100%',
height: SidebarTopBlurContainerHeight,
}}>
</div>
<Tree
{...tree}
items={finalItems}

View File

@@ -1,7 +1,9 @@
import { VSCodeTextField } from '@vscode/webview-ui-toolkit/react'
import { styled } from 'styled-components'
import { backDropBg } from '../../styles/utils'
export const SidebarWrapper = styled.div`
position: relative;
display: flex;
flex-direction: column;
width: 100%;
@@ -10,6 +12,19 @@ export const SidebarWrapper = styled.div`
overflow: hidden;
`
export const SidebarTopBlurContainer = styled.div`
position: absolute;
top: 0;
left: 0;
z-index: 1;
display: flex;
flex-direction: column;
width: 100%;
overflow: hidden;
padding: 0 0.5rem;
${backDropBg}
`
export const SidebarHeader = styled.div`
display: flex;
align-items: center;

View File

@@ -1,17 +1,26 @@
import { motion } from 'framer-motion'
import styled from 'styled-components'
import { Icon } from '../icon'
import { backDropBg } from '../../styles/utils'
export const TabContainer = styled.div`
display: flex;
flex-direction: column;
height: 100%;
position: relative;
padding-top: calc(var(--design-unit) * 7px + var(--border-width) * 3px);
`
export const TabListHeader = styled.div`
flex-shrink: 0;
position: relative;
padding: calc(var(--border-width) * 3px) 1rem;
position: absolute;
top: 0;
left: 0;
z-index: 1;
width: 100%;
${backDropBg}
&[data-show-more=true] {
padding-right: 2rem;

View File

@@ -0,0 +1,131 @@
import { useCallback, useLayoutEffect, useRef, useState } from 'react'
import { useGlobalStore } from '../store/zustand/global'
interface RgbaColor {
r: number
g: number
b: number
a: number
}
// A helper function to convert RGB to boolean indicating if color is dark
function isColorDark(rgba: RgbaColor | null): boolean {
if (!rgba)
return false
// https://stackoverflow.com/questions/11867545/change-text-color-based-on-brightness-of-the-covered-background-area
const brightness = Math.round(((rgba.r * 299) + (rgba.g * 587) + (rgba.b * 114)) / 1000)
return brightness <= 125
}
// Helper function to convert hex color to RGB
function hexToRGBA(hex: string): RgbaColor | null {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})?$/i.exec(hex)
return result
? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16),
a: parseInt(result[4], 16) || 1,
}
: null
}
export interface GetCssVarColorInfoProps {
element: HTMLElement | null
cssVarName: string
}
interface GetCssVarColorInfoResult {
rgba: RgbaColor | null
isDark: boolean
}
export function getCssVarColorInfo(props: GetCssVarColorInfoProps): GetCssVarColorInfoResult {
const { element, cssVarName } = props
let rgba: RgbaColor | null = null
let isDark = false
if (element) {
const colorValue = getComputedStyle(element).getPropertyValue(cssVarName).trim()
if (colorValue.startsWith('#')) {
const result = hexToRGBA(colorValue)
rgba = result
}
else {
const rgbaArr = colorValue.match(/\d+/g)?.map(Number)
if (rgbaArr && rgbaArr.length >= 3) {
const [r, g, b, a = 1] = rgbaArr
rgba = { r, g, b, a }
}
}
isDark = isColorDark(rgba)
}
return { rgba, isDark }
}
export function isDarkTheme() {
const { isDark } = getCssVarColorInfo({ element: document.body, cssVarName: '--panel-view-background' })
return isDark
}
export type UseCssVarColorProps = Omit<GetCssVarColorInfoProps, 'element'> & {
elementRef?: React.RefObject<HTMLElement>
}
export type UseCssVarColorResult = GetCssVarColorInfoResult & {
updateColor: () => void
}
export function useCssVarColor(props: UseCssVarColorProps): UseCssVarColorResult {
const { elementRef: elementRefFromProps, cssVarName } = props
const elementRefFromPrivate = useRef<HTMLElement>(document.body)
const elementRef = elementRefFromProps || elementRefFromPrivate
const hasElement = (elementRefFromProps ? elementRefFromProps.current : elementRefFromPrivate.current) instanceof HTMLElement
const [rgba, setRgbColor] = useState<RgbaColor | null>(null)
const [isDark, setIsDark] = useState(false)
const { themeName } = useGlobalStore()
const updateColor = useCallback(() => {
const app = document.querySelector<HTMLElement>('#root')
if (app)
elementRefFromPrivate.current = app
if (!hasElement)
return
const { rgba, isDark } = getCssVarColorInfo({ element: elementRef.current, cssVarName })
setRgbColor(rgba)
setIsDark(isDark)
}, [cssVarName, hasElement, elementRef.current])
useLayoutEffect(() => {
if (!hasElement)
return
updateColor()
const observer = new MutationObserver((mutationsList) => {
for (const mutation of mutationsList) {
if (mutation.type === 'attributes')
updateColor()
}
})
observer.observe(document.body, { attributes: true })
return () => observer.disconnect()
}, [updateColor, hasElement, themeName])
return { rgba, isDark, updateColor }
}
export function useDarkTheme(): boolean {
const { isDark } = useCssVarColor({ cssVarName: '--panel-view-background' })
return isDark
}

View File

@@ -15,7 +15,6 @@ export const ContentWrapper = styled.div<{ $isPopoverContent?: boolean; $isTopTo
${props => props.$isPopoverContent && css`
width: calc(100vw - 1rem);
height: 100%;
background: var(--panel-view-background);
max-width: 500px;
${withBreakpoint('sm', css`

View File

@@ -2,6 +2,7 @@ import { css, styled } from 'styled-components'
import { withBreakpoint } from '../../../../helpers/with-breakpoint'
export const ChatPanelWrapper = styled.div`
position: relative;
display: flex;
flex-direction: column;
flex: 1;
@@ -15,5 +16,4 @@ export const ChatPanelWrapper = styled.div`
`
export const ChatPanelPopoverTreeWrapper = styled.div`
height: 100%;
`

View File

@@ -1,9 +1,10 @@
import type { FC, RefObject } from 'react'
import { memo, useCallback, useEffect, useMemo, useState } from 'react'
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { ChatMessageStatus, ChatRole, ClientEventName, getErrorMsg } from '@nicepkg/gpt-runner-shared/common'
import { useTranslation } from 'react-i18next'
import { toast } from 'react-hot-toast'
import { copyToClipboard } from '@nicepkg/gpt-runner-shared/browser'
import { motion } from 'framer-motion'
import type { ChatMessagePanelProps } from '../../../../components/chat-message-panel'
import { FormTitle } from '../../../../components/form-title'
import { ChatMessagePanel } from '../../../../components/chat-message-panel'
@@ -16,16 +17,17 @@ import { useGlobalStore } from '../../../../store/zustand/global'
import type { GptFileTreeItem } from '../../../../store/zustand/global/sidebar-tree.slice'
import { getGlobalConfig } from '../../../../helpers/global-config'
import { PopoverMenu } from '../../../../components/popover-menu'
import type { DragResizeViewRef } from '../../../../components/drag-resize-view'
import { DragResizeView } from '../../../../components/drag-resize-view'
import { useElementSizeRealTime } from '../../../../hooks/use-element-size-real-time.hook'
import { useTempStore } from '../../../../store/zustand/temp'
import type { MessageCodeBlockTheme } from '../../../../components/chat-message-code-block'
import { isDarkTheme } from '../../../../styles/themes'
import { emitter } from '../../../../helpers/emitter'
import { ModelSettings } from '../settings/components/model-settings'
import { ContentWrapper } from '../../chat.styles'
import { ContextSettings } from '../settings/components/context-settings'
import { OverrideModelTypeSettings } from '../settings/components/model-settings/override-model-type'
import { useDarkTheme } from '../../../../hooks/use-css-var-color.hook'
import { ChatPanelPopoverTreeWrapper, ChatPanelWrapper } from './chat-panel.styles'
import { createRemarkOpenEditorPlugin } from './remark-plugin'
@@ -68,6 +70,7 @@ export const ChatPanel: FC<ChatPanelProps> = memo((props) => {
const [gptFileTreeItem, setGptFileTreeItem] = useState<GptFileTreeItem>()
const [chatPanelRef, { width: chatPanelWidth, height: chatPanelHeight }] = useElementSizeRealTime<HTMLDivElement>()
const { filesRelativePaths } = useTempStore()
const dargChatInputRef = useRef<DragResizeViewRef>(null)
const defaultInitChatInputHeight = useMemo(() => {
const DEFAULT_CHAT_INPUT_HEIGHT = 250
@@ -251,13 +254,13 @@ export const ChatPanel: FC<ChatPanelProps> = memo((props) => {
</>
}, [handleCopy, handleInsertCodes, handleDiffCodes])
const codeBlockTheme: MessageCodeBlockTheme = isDarkTheme(themeName) ? 'dark' : 'light'
const isDark = useDarkTheme()
const codeBlockTheme: MessageCodeBlockTheme = isDark ? 'dark' : 'light'
const messagePanelProps: ChatMessagePanelProps = useMemo(() => {
return {
messageItems: chatInstance?.messages.map((message, i) => {
const isLast = i === chatInstance.messages.length - 1
const isLastTwo = i >= chatInstance.messages.length - 2
const isAi = message.name === ChatRole.Assistant
const handleRegenerateMessage = () => {
@@ -320,7 +323,6 @@ export const ChatPanel: FC<ChatPanelProps> = memo((props) => {
...message,
remarkPlugins,
status: isLast ? status : ChatMessageStatus.Success,
showToolbar: isLastTwo ? 'always' : 'hover',
showAvatar: chatPanelWidth > 600,
theme: codeBlockTheme,
buildCodeToolbar: status === ChatMessageStatus.Pending ? undefined : buildCodeToolbar,
@@ -482,6 +484,9 @@ export const ChatPanel: FC<ChatPanelProps> = memo((props) => {
childrenInMenuWhenOpen={true}
buildChildrenSlot={({ isHovering }) => {
return <IconButton
style={{
paddingLeft: isHovering ? '0' : '0.5rem',
}}
text={t('chat_page.clear_history_btn')}
iconClassName='codicon-clear-all'
hoverShowText={!isHovering}
@@ -523,30 +528,53 @@ export const ChatPanel: FC<ChatPanelProps> = memo((props) => {
}
return <ChatPanelWrapper ref={chatPanelRef}>
<ChatMessagePanel ref={scrollDownRef} {...messagePanelProps}></ChatMessagePanel>
<ChatMessagePanel
ref={scrollDownRef}
{...messagePanelProps}
bottomSlot={
<motion.div
style={{
flexShrink: '0',
width: '100%',
height: dargChatInputRef.current?.motionDragHeight,
}}
></motion.div>
}
></ChatMessagePanel>
{initChatInputHeight && <DragResizeView
initHeight={initChatInputHeight}
dragDirectionConfigs={[
{
direction: 'top',
boundary: [-(maxInitChatInputHeight - initChatInputHeight), 100],
},
]}>
<ChatMessageInput
showTopLogo={chatPanelWidth > 600}
showBottomLogo={chatPanelWidth <= 600}
value={chatInstance?.inputtingPrompt || ''}
onChange={handleInputChange}
toolbarSlot={renderInputToolbar()}
onSendMessage={handleGenerateAnswer}
logoProps={{
onClick() {
setInitChatInputHeight(initChatInputHeight === defaultInitChatInputHeight ? maxInitChatInputHeight : defaultInitChatInputHeight)
{initChatInputHeight
&& <DragResizeView
ref={dargChatInputRef}
initHeight={initChatInputHeight}
dragDirectionConfigs={[
{
direction: 'top',
boundary: [-(maxInitChatInputHeight - initChatInputHeight), 100],
},
]}
dragStyle={{
position: 'absolute',
bottom: '0',
left: '0',
zIndex: '9',
width: '100%',
}}
></ChatMessageInput>
</DragResizeView>}
>
<ChatMessageInput
showTopLogo={chatPanelWidth > 600}
showBottomLogo={chatPanelWidth <= 600}
value={chatInstance?.inputtingPrompt || ''}
onChange={handleInputChange}
toolbarSlot={renderInputToolbar()}
onSendMessage={handleGenerateAnswer}
logoProps={{
onClick() {
setInitChatInputHeight(initChatInputHeight === defaultInitChatInputHeight ? maxInitChatInputHeight : defaultInitChatInputHeight)
},
}}
></ChatMessageInput>
</DragResizeView>
}
</ChatPanelWrapper>
})

View File

@@ -3,12 +3,12 @@ import type { SingleFileConfig, UserConfig } from '@nicepkg/gpt-runner-shared/co
import { useGlobalStore } from '../../../../../../store/zustand/global'
import type { MessageCodeBlockTheme } from '../../../../../../components/chat-message-code-block'
import { MessageCodeBlock } from '../../../../../../components/chat-message-code-block'
import { isDarkTheme } from '../../../../../../styles/themes'
import { useUserConfig } from '../../../../../../hooks/use-user-config.hook'
import { ConfigInfoWrapper } from '../../settings.styles'
import { FormTitle } from '../../../../../../components/form-title'
import { FlexColumn } from '../../../../../../styles/global.styles'
import { LoadingView } from '../../../../../../components/loading-view'
import { useDarkTheme } from '../../../../../../hooks/use-css-var-color.hook'
export interface ConfigInfoProps {
rootPath?: string
@@ -19,11 +19,12 @@ export interface ConfigInfoProps {
export const ConfigInfo: FC<ConfigInfoProps> = memo((props) => {
const { rootPath, chatId, singleFileConfig: singleFileConfigFromProps, userConfig: userConfigFromProps } = props
const { themeName, getGptFileTreeItemFromChatId } = useGlobalStore()
const { getGptFileTreeItemFromChatId } = useGlobalStore()
const isDark = useDarkTheme()
const codeBlockTheme: MessageCodeBlockTheme = useMemo(() => {
return isDarkTheme(themeName) ? 'dark' : 'light'
}, [themeName])
return isDark ? 'dark' : 'light'
}, [isDark])
const gptFileTreeItem = useMemo(() => {
if (!chatId)

View File

@@ -8,6 +8,7 @@ import { useIsMobile } from '../../../../hooks/use-is-mobile.hook'
import type { UseTokenNumProps } from '../../../../hooks/use-token-num.hook'
import { useTokenNum } from '../../../../hooks/use-token-num.hook'
import { formatNumWithK } from '../../../../helpers/utils'
import { useTempStore } from '../../../../store/zustand/temp'
import { TopToolbarBlank, TopToolbarLeft, TopToolbarRight, TopToolbarWrapper } from './top-toolbar.styles'
export interface TopToolbarProps extends UseTokenNumProps {
@@ -23,13 +24,15 @@ export const TopToolbar = memo(forwardRef<HTMLDivElement, TopToolbarProps>((prop
const { t } = useTranslation()
const isMobile = useIsMobile()
const { totalTokenNum } = useTokenNum(useTokenNumProps)
const { updateCurrentAppConfig } = useTempStore()
const popMenus: {
const menus: {
text: string
alwaysShowText?: boolean
iconClassName: string
menuView?: React.ReactNode
menuProps?: PopoverMenuProps
onClick?: () => void
}[] = [{
text: t('chat_page.settings_btn'),
alwaysShowText: true,
@@ -45,12 +48,21 @@ export const TopToolbar = memo(forwardRef<HTMLDivElement, TopToolbarProps>((prop
alwaysShowText: !isMobile,
iconClassName: 'codicon-info',
menuView: aboutView,
}, {
text: t('chat_page.settings_tab_notifications'),
alwaysShowText: !isMobile,
iconClassName: 'codicon-bell',
onClick() {
updateCurrentAppConfig({
showNotificationModal: true,
})
},
}]
return <>
<TopToolbarWrapper ref={ref}>
<TopToolbarLeft>
{popMenus.map((popMenu, index) => {
{menus.map((popMenu, index) => {
const { text, alwaysShowText, iconClassName, menuView, menuProps } = popMenu
return <PopoverMenu
@@ -73,9 +85,16 @@ export const TopToolbar = memo(forwardRef<HTMLDivElement, TopToolbarProps>((prop
text={text}
iconClassName={iconClassName}
hoverShowText={!alwaysShowText && !isHovering}
transparentBgWhenNotHover
style={{
paddingLeft: '0.5rem',
}}
onClick={(e: any) => {
if (popMenu.onClick) {
e.stopPropagation()
popMenu.onClick?.()
}
}}
></IconButton>
}}
buildMenuSlot={() => {

View File

@@ -1,32 +1,54 @@
import { createGlobalStyle, styled } from 'styled-components'
export const GlobalStyle = createGlobalStyle`
export const GlobalStyle = createGlobalStyle<{ appendCss?: string }>`
#root {
width: 100%;
height: 100%;
width: 100%;
height: 100%;
}
body {
margin: 0;
padding: 0;
width: 100%;
height: 100vh;
overflow: hidden;
user-select: none;
-webkit-user-select: none;
-webkit-user-drag: none;
background: var(--background);
color: var(--foreground);
font-family: var(--font-family);
margin: 0;
padding: 0;
width: 100%;
height: 100vh;
overflow: hidden;
user-select: none;
-webkit-user-select: none;
-webkit-user-drag: none;
background: var(--background);
color: var(--foreground);
font-family: var(--font-family);
--my-button-height: calc(var(--border-width) * 1px * 2 + var(--button-padding-vertical) * 2 + var(--type-ramp-base-font-size));
--my-input-height: calc(var(--border-width) * 1px * 2 + var(--input-height) * 1px);
--my-button-height: calc(var(--border-width) * 1px * 2 + var(--button-padding-vertical) * 2 + var(--type-ramp-base-font-size));
--my-input-height: calc(var(--border-width) * 1px * 2 + var(--input-height) * 1px);
--my-backdrop-filter: saturate(180%) blur(20px);
}
* {
box-sizing: border-box;
padding: 0;
margin: 0;
box-sizing: border-box;
padding: 0;
margin: 0;
::-webkit-scrollbar {
width: 10px;
background: transparent !important;
}
::-webkit-scrollbar-track {
background: transparent !important;
}
::-webkit-scrollbar-thumb {
background: var(--my-scrollbar-bg);
border-radius: 5px;
transition: all 0.3s ease-in-out;
&:hover {
background: var(--my-scrollbar-hover-bg);
}
}
}
${props => props.appendCss}
`
export const FlexRow = styled.div`

View File

@@ -173,6 +173,7 @@ export const MarkdownStyle = createGlobalStyle`
blockquote {
border-left: 4px solid var(--panel-view-border);
padding: 0 15px;
margin-left: 15px;
}
blockquote > :first-child {
margin-top: 0;

View File

@@ -23,9 +23,4 @@ if (getGlobalConfig().defaultTheme !== 'default') {
;(themeMap as any).default = defaultTheme
}
export function isDarkTheme(themeName: ThemeName) {
const darkThemes: ThemeName[] = ['default', 'gptrDark', 'vscodeDynamic', 'vscodeDark', 'jetbrainsDark']
return darkThemes.includes(themeName)
}
export type ThemeName = keyof typeof themeMap

View File

@@ -5,3 +5,18 @@ export const textEllipsis = css`
text-overflow: ellipsis;
white-space: nowrap;
`
export const backDropBg = css`
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
z-index: -1;
backdrop-filter: var(--my-backdrop-filter);
-webkit-backdrop-filter: var(--my-backdrop-filter);
background: var(--my-backdrop-bg);
}
`

View File

@@ -1,12 +1,13 @@
import { useMutation, useQuery } from '@tanstack/react-query'
import type { ReactNode } from 'react'
import { memo, useMemo } from 'react'
import { memo } from 'react'
import type { MarkAsVisitedAppConfigReqParams } from '@nicepkg/gpt-runner-shared/common'
import { useTempStore } from '../../store/zustand/temp'
import { useGlobalStore } from '../../store/zustand/global'
import { fetchAppConfig, markAsVisitedAppConfig } from '../../networks/config'
import { Modal } from '../../components/modal'
import { MessageTextView } from '../../components/chat-message-text-view'
import { Wrapper } from './layout.styles'
export interface LayoutProps {
children?: ReactNode
@@ -37,18 +38,18 @@ export const Layout = memo((props: LayoutProps) => {
})
const notificationConfig = currentAppConfig?.currentConfig?.notificationConfig
const releaseConfig = currentAppConfig?.currentConfig?.releaseConfig
// const releaseConfig = currentAppConfig?.currentConfig?.releaseConfig
const releaseLog = useMemo(() => {
let content = ''
releaseConfig?.changeLogs.forEach((log) => {
content += `## ${log.version}\n`
content += `${log.changes}\n\n`
})
return content
}, [releaseConfig?.changeLogs])
// const releaseLog = useMemo(() => {
// let content = ''
// releaseConfig?.changeLogs.forEach((log) => {
// content += `## ${log.version}\n`
// content += `${log.changes}\n\n`
// })
// return content
// }, [releaseConfig?.changeLogs])
return <>
return <Wrapper>
{/* notification modal */}
<Modal
zIndex={99}
@@ -73,7 +74,7 @@ export const Layout = memo((props: LayoutProps) => {
</Modal>
{/* release log modal */}
<Modal
{/* <Modal
zIndex={100}
open={Boolean(currentAppConfig?.showReleaseModal)}
title={'Release Log'}
@@ -93,10 +94,10 @@ export const Layout = memo((props: LayoutProps) => {
>
<MessageTextView
contents={releaseLog} />
</Modal>
</Modal> */}
{children}
</>
</Wrapper>
})
Layout.displayName = 'Layout'

View File

@@ -0,0 +1,6 @@
import { styled } from 'styled-components'
export const Wrapper = styled.div`
width: 100%;
height: 100%;
`

View File

@@ -80,19 +80,19 @@
"@monaco-editor/react": "^4.5.1",
"@nicepkg/gpt-runner-core": "workspace:*",
"@nicepkg/gpt-runner-shared": "workspace:*",
"@tanstack/react-query": "^4.32.0",
"@tanstack/react-query": "^4.32.5",
"@types/connect-history-api-fallback": "^1.5.0",
"@types/cors": "^2.8.13",
"@types/express": "^4.17.17",
"@types/global-agent": "^2.1.1",
"@types/keyboardjs": "^2.5.1",
"@types/lodash-es": "^4.17.8",
"@types/react": "^18.2.17",
"@types/react": "^18.2.18",
"@types/react-dom": "^18.2.7",
"@types/react-syntax-highlighter": "^15.5.7",
"@types/uuid": "^9.0.2",
"@use-gesture/react": "^10.2.27",
"@vitejs/plugin-react": "^4.0.3",
"@vitejs/plugin-react": "^4.0.4",
"@vscode/webview-ui-toolkit": "^1.2.2",
"clsx": "^2.0.0",
"commander": "^10.0.1",
@@ -104,32 +104,33 @@
"framer-motion": "^10.15.0",
"fs-extra": "^11.1.1",
"global-agent": "^3.0.0",
"i18next": "^23.3.0",
"i18next": "^23.4.1",
"i18next-browser-languagedetector": "^7.1.0",
"i18next-http-backend": "^2.2.1",
"js-base64": "^3.7.5",
"keyboardjs": "^2.7.0",
"lodash-es": "^4.17.21",
"monaco-editor": "^0.40.0",
"monaco-editor": "^0.41.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-error-boundary": "^4.0.10",
"react-hook-form": "^7.45.2",
"react-hot-toast": "^2.4.1",
"react-i18next": "^13.0.2",
"react-i18next": "^13.0.3",
"react-markdown": "^8.0.7",
"react-router-dom": "^6.14.2",
"react-syntax-highlighter": "^15.5.0",
"react-tiny-popover": "^7.2.4",
"react-use": "^17.4.0",
"remark-gfm": "^3.0.1",
"styled-components": "^6.0.5",
"undici": "^5.22.1",
"styled-components": "^6.0.7",
"undici": "^5.23.0",
"unist-util-visit": "^5.0.0",
"uuid": "^9.0.0",
"vite": "^4.4.7",
"vite": "^4.4.8",
"vite-plugin-monaco-editor": "^1.1.0",
"vite-plugin-svgr": "^3.2.0",
"zustand": "^4.3.9"
"web-streams-polyfill": "^3.2.1",
"zustand": "^4.4.0"
}
}
}

View File

@@ -1,25 +1,36 @@
import type { NotificationConfig } from '@nicepkg/gpt-runner-shared/common'
export const notificationConfig: NotificationConfig = {
createAt: '2023-07-24 23:31:22',
createAt: '2023-08-06 18:37:10',
title: 'GPT Runner Notification',
message: 'v1.2.0 is release',
message: 'v1.2.2 is release',
}
export const cnNotificationConfig: NotificationConfig = {
createAt: '2023-07-24 23:31:26',
createAt: '2023-08-06 18:37:06',
title: 'GPT Runner 通知',
message: `
### 版本更新到了 v1.2.0
1. 重启 vscode 即可去扩展处更新
2. cli 的执行 \`npm i -g gptr\` 即可更新
### 🚀 新版 v1.2.2 (2023-08-06)
### 本次功能更新
1. 针对语言为简体中文的用户提供 OpenAI API key 供应商,也就是你可以白嫖了。
> vscode 用户重启 vscode 后即可在扩展处更新
>
> cli 用户执行 \`npm i -g gptr\` 即可更新。
1. 针对语言为简体中文的用户提供 OpenAI API key 第三方供应商,也就是你可以白嫖了。
2. 点击左上角设置,切换供应商即可。
3. 本次 API Key 由慷慨大方的 \`剑廿三\` 提供,让我们把掌声送给他。
3. 本次 API Key 由慷慨大方的 \`朝云云\` 提供,让我们把掌声送给他。
### 💬 交流
### 交流
1. 想进群交流的加 wechat: \`qq2214962083\`
2. 所有捐赠 API Key 的朋友,都可以在这里免费展示 50 字以内的广告直到 API Key 失效,如果你也想捐赠 API Key可以联系我。
3. 此处广告仅供展示,与 GPT-Runner 无关,**若有财产交易,请自行承担风险**
### 📢 YunAI - AI驱动的WEB对话应用 (朝云云供应商广告)
🔵 [**点击进入**](https://faschat.zyai.online/)注册即送大量3.5使用次数
如需适配 GPT-Runner 的 4.0-32k 或 Claude 接口服务,可 VX 联系:\`YunAi0101\`
`,
}

View File

@@ -8,13 +8,13 @@ export const vendorsConfig: VendorsConfig = {
}
export const cnVendorsConfig: VendorsConfig = {
createAt: '2023-07-24 23:40:49',
createAt: '2023-08-06 18:37:22',
[ChatModelType.Openai]: [{
vendorName: 'xabcai',
vendorName: '朝云云供应商',
vendorSecrets: {
basePath: 'https://api.xabcai.com/v1',
basePath: 'http://8.130.89.91:3000/v1',
// don't forgot it should be base64
apiKey: 'c2stWHZQeGJQMVBySFduZDJFZ0xpa0lKTlQzOTNoc3pZdDdmN0NNZUozSE1pdkw2QVdx',
apiKey: 'c2stQUlBc2NTUGk2RVR0cXVSVThmMmYzODU1NTk4NzQ4M2U4YjE1QWU4MzEwMjMxZTRi',
},
}],
[ChatModelType.Anthropic]: [],

View File

@@ -1,6 +1,7 @@
import { canUseNodeFetchWithoutCliFlag, getDefaultProxyUrl } from '@nicepkg/gpt-runner-shared/node'
import { bootstrap } from 'global-agent'
import { Headers, ProxyAgent, Request, Response, fetch, setGlobalDispatcher } from 'undici'
import { ReadableStream } from 'web-streams-polyfill/ponyfill'
if (!canUseNodeFetchWithoutCliFlag()) {
console.log('GPT Runner: add polyfill for fetch', process.version)
@@ -9,6 +10,7 @@ if (!canUseNodeFetchWithoutCliFlag()) {
globalThis.Headers = Headers as any
globalThis.Request = Request as any
globalThis.Response = Response as any
globalThis.ReadableStream = ReadableStream as any
}
// global proxy

650
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff