Merge pull request #5801 from menloresearch/release/v0.6.5
Sync release/0.6.5 into dev to start new development cycle
This commit is contained in:
commit
32966f9259
@ -15,7 +15,7 @@ export default defineConfig([
|
|||||||
`http://127.0.0.1:${process.env.CORTEX_API_PORT ?? '39291'}`
|
`http://127.0.0.1:${process.env.CORTEX_API_PORT ?? '39291'}`
|
||||||
),
|
),
|
||||||
PLATFORM: JSON.stringify(process.platform),
|
PLATFORM: JSON.stringify(process.platform),
|
||||||
CORTEX_ENGINE_VERSION: JSON.stringify('b5509'),
|
CORTEX_ENGINE_VERSION: JSON.stringify('b5857'),
|
||||||
DEFAULT_REMOTE_ENGINES: JSON.stringify(engines),
|
DEFAULT_REMOTE_ENGINES: JSON.stringify(engines),
|
||||||
DEFAULT_REMOTE_MODELS: JSON.stringify(models),
|
DEFAULT_REMOTE_MODELS: JSON.stringify(models),
|
||||||
DEFAULT_REQUEST_PAYLOAD_TRANSFORM: JSON.stringify(
|
DEFAULT_REQUEST_PAYLOAD_TRANSFORM: JSON.stringify(
|
||||||
@ -38,7 +38,7 @@ export default defineConfig([
|
|||||||
file: 'dist/node/index.cjs.js',
|
file: 'dist/node/index.cjs.js',
|
||||||
},
|
},
|
||||||
define: {
|
define: {
|
||||||
CORTEX_ENGINE_VERSION: JSON.stringify('b5509'),
|
CORTEX_ENGINE_VERSION: JSON.stringify('b5857'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
set BIN_PATH=./bin
|
set BIN_PATH=./bin
|
||||||
set SHARED_PATH=./../../electron/shared
|
set SHARED_PATH=./../../electron/shared
|
||||||
set /p CORTEX_VERSION=<./bin/version.txt
|
set /p CORTEX_VERSION=<./bin/version.txt
|
||||||
set ENGINE_VERSION=b5509
|
set ENGINE_VERSION=b5857
|
||||||
|
|
||||||
@REM Download llama.cpp binaries
|
@REM Download llama.cpp binaries
|
||||||
set DOWNLOAD_URL=https://github.com/menloresearch/llama.cpp/releases/download/%ENGINE_VERSION%/llama-%ENGINE_VERSION%-bin-win
|
set DOWNLOAD_URL=https://github.com/menloresearch/llama.cpp/releases/download/%ENGINE_VERSION%/llama-%ENGINE_VERSION%-bin-win
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
# Read CORTEX_VERSION
|
# Read CORTEX_VERSION
|
||||||
CORTEX_VERSION=$(cat ./bin/version.txt)
|
CORTEX_VERSION=$(cat ./bin/version.txt)
|
||||||
ENGINE_VERSION=b5509
|
ENGINE_VERSION=b5857
|
||||||
CORTEX_RELEASE_URL="https://github.com/menloresearch/cortex.cpp/releases/download"
|
CORTEX_RELEASE_URL="https://github.com/menloresearch/cortex.cpp/releases/download"
|
||||||
ENGINE_DOWNLOAD_URL=https://github.com/menloresearch/llama.cpp/releases/download/${ENGINE_VERSION}/llama-${ENGINE_VERSION}-bin
|
ENGINE_DOWNLOAD_URL=https://github.com/menloresearch/llama.cpp/releases/download/${ENGINE_VERSION}/llama-${ENGINE_VERSION}-bin
|
||||||
CUDA_DOWNLOAD_URL=https://github.com/menloresearch/llama.cpp/releases/download/${ENGINE_VERSION}
|
CUDA_DOWNLOAD_URL=https://github.com/menloresearch/llama.cpp/releases/download/${ENGINE_VERSION}
|
||||||
|
|||||||
@ -19,7 +19,7 @@ export default defineConfig([
|
|||||||
CORTEX_SOCKET_URL: JSON.stringify(
|
CORTEX_SOCKET_URL: JSON.stringify(
|
||||||
`ws://127.0.0.1:${process.env.CORTEX_API_PORT ?? '39291'}`
|
`ws://127.0.0.1:${process.env.CORTEX_API_PORT ?? '39291'}`
|
||||||
),
|
),
|
||||||
CORTEX_ENGINE_VERSION: JSON.stringify('b5509'),
|
CORTEX_ENGINE_VERSION: JSON.stringify('b5857'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
@echo off
|
@echo off
|
||||||
|
|
||||||
set CORTEX_VERSION=1.0.14
|
set CORTEX_VERSION=1.0.14
|
||||||
set ENGINE_VERSION=b5509
|
set ENGINE_VERSION=b5857
|
||||||
set ENGINE_DOWNLOAD_URL=https://github.com/menloresearch/llama.cpp/releases/download/%ENGINE_VERSION%/llama-%ENGINE_VERSION%-bin-win
|
set ENGINE_DOWNLOAD_URL=https://github.com/menloresearch/llama.cpp/releases/download/%ENGINE_VERSION%/llama-%ENGINE_VERSION%-bin-win
|
||||||
set ENGINE_DOWNLOAD_GGML_URL=https://github.com/ggml-org/llama.cpp/releases/download/%ENGINE_VERSION%/llama-%ENGINE_VERSION%-bin-win
|
set ENGINE_DOWNLOAD_GGML_URL=https://github.com/ggml-org/llama.cpp/releases/download/%ENGINE_VERSION%/llama-%ENGINE_VERSION%-bin-win
|
||||||
set CUDA_DOWNLOAD_URL=https://github.com/menloresearch/llama.cpp/releases/download/%ENGINE_VERSION%
|
set CUDA_DOWNLOAD_URL=https://github.com/menloresearch/llama.cpp/releases/download/%ENGINE_VERSION%
|
||||||
|
|||||||
@ -15,7 +15,7 @@ download() {
|
|||||||
|
|
||||||
# Read CORTEX_VERSION
|
# Read CORTEX_VERSION
|
||||||
CORTEX_VERSION=1.0.14
|
CORTEX_VERSION=1.0.14
|
||||||
ENGINE_VERSION=b5509
|
ENGINE_VERSION=b5857
|
||||||
CORTEX_RELEASE_URL="https://github.com/menloresearch/cortex.cpp/releases/download"
|
CORTEX_RELEASE_URL="https://github.com/menloresearch/cortex.cpp/releases/download"
|
||||||
ENGINE_DOWNLOAD_URL=https://github.com/menloresearch/llama.cpp/releases/download/${ENGINE_VERSION}/llama-${ENGINE_VERSION}-bin
|
ENGINE_DOWNLOAD_URL=https://github.com/menloresearch/llama.cpp/releases/download/${ENGINE_VERSION}/llama-${ENGINE_VERSION}-bin
|
||||||
CUDA_DOWNLOAD_URL=https://github.com/menloresearch/llama.cpp/releases/download/${ENGINE_VERSION}
|
CUDA_DOWNLOAD_URL=https://github.com/menloresearch/llama.cpp/releases/download/${ENGINE_VERSION}
|
||||||
|
|||||||
@ -33,7 +33,7 @@ ${StrLoc}
|
|||||||
!define VERSION "jan_version"
|
!define VERSION "jan_version"
|
||||||
!define VERSIONWITHBUILD "jan_build"
|
!define VERSIONWITHBUILD "jan_build"
|
||||||
!define HOMEPAGE ""
|
!define HOMEPAGE ""
|
||||||
!define INSTALLMODE "both"
|
!define INSTALLMODE "currentUser"
|
||||||
!define LICENSE ""
|
!define LICENSE ""
|
||||||
!define INSTALLERICON "D:\a\jan\jan\src-tauri\icons\icon.ico"
|
!define INSTALLERICON "D:\a\jan\jan\src-tauri\icons\icon.ico"
|
||||||
!define SIDEBARIMAGE ""
|
!define SIDEBARIMAGE ""
|
||||||
|
|||||||
@ -58,6 +58,7 @@
|
|||||||
"react-i18next": "^15.5.1",
|
"react-i18next": "^15.5.1",
|
||||||
"react-joyride": "^2.9.3",
|
"react-joyride": "^2.9.3",
|
||||||
"react-markdown": "^10.1.0",
|
"react-markdown": "^10.1.0",
|
||||||
|
"react-resizable-panels": "^3.0.3",
|
||||||
"react-syntax-highlighter": "^15.6.1",
|
"react-syntax-highlighter": "^15.6.1",
|
||||||
"react-syntax-highlighter-virtualized-renderer": "^1.1.0",
|
"react-syntax-highlighter-virtualized-renderer": "^1.1.0",
|
||||||
"react-textarea-autosize": "^8.5.9",
|
"react-textarea-autosize": "^8.5.9",
|
||||||
@ -68,7 +69,7 @@
|
|||||||
"remark-math": "^6.0.0",
|
"remark-math": "^6.0.0",
|
||||||
"sonner": "^2.0.3",
|
"sonner": "^2.0.3",
|
||||||
"tailwindcss": "^4.1.4",
|
"tailwindcss": "^4.1.4",
|
||||||
"token.js": "npm:token.js-fork@0.7.12",
|
"token.js": "npm:token.js-fork@0.7.15",
|
||||||
"tw-animate-css": "^1.2.7",
|
"tw-animate-css": "^1.2.7",
|
||||||
"ulidx": "^2.4.1",
|
"ulidx": "^2.4.1",
|
||||||
"unified": "^11.0.5",
|
"unified": "^11.0.5",
|
||||||
|
|||||||
54
web-app/src/components/ui/resizable.tsx
Normal file
54
web-app/src/components/ui/resizable.tsx
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import * as React from 'react'
|
||||||
|
import { GripVerticalIcon } from 'lucide-react'
|
||||||
|
import * as ResizablePrimitive from 'react-resizable-panels'
|
||||||
|
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
function ResizablePanelGroup({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof ResizablePrimitive.PanelGroup>) {
|
||||||
|
return (
|
||||||
|
<ResizablePrimitive.PanelGroup
|
||||||
|
data-slot="resizable-panel-group"
|
||||||
|
className={cn(
|
||||||
|
'flex h-full w-full data-[panel-group-direction=vertical]:flex-col',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function ResizablePanel({
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof ResizablePrimitive.Panel>) {
|
||||||
|
return <ResizablePrimitive.Panel data-slot="resizable-panel" {...props} />
|
||||||
|
}
|
||||||
|
|
||||||
|
function ResizableHandle({
|
||||||
|
withHandle,
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof ResizablePrimitive.PanelResizeHandle> & {
|
||||||
|
withHandle?: boolean
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<ResizablePrimitive.PanelResizeHandle
|
||||||
|
data-slot="resizable-handle"
|
||||||
|
className={cn(
|
||||||
|
'bg-border focus-visible:ring-ring relative flex w-px items-center justify-center after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:ring-1 focus-visible:ring-offset-1 focus-visible:outline-hidden data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:translate-x-0 data-[panel-group-direction=vertical]:after:-translate-y-1/2 [&[data-panel-group-direction=vertical]>div]:rotate-90',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{withHandle && (
|
||||||
|
<div className="bg-main-view z-10 flex h-4 w-3 items-center justify-center rounded-xs border border-main-view-fg/10 relative left-0.5">
|
||||||
|
<GripVerticalIcon className="size-2.5 text-main-view-fg" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</ResizablePrimitive.PanelResizeHandle>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { ResizablePanelGroup, ResizablePanel, ResizableHandle }
|
||||||
@ -182,7 +182,9 @@ export function DownloadManagement() {
|
|||||||
getProviders().then(setProviders)
|
getProviders().then(setProviders)
|
||||||
toast.success(t('common:toast.downloadComplete.title'), {
|
toast.success(t('common:toast.downloadComplete.title'), {
|
||||||
id: 'download-complete',
|
id: 'download-complete',
|
||||||
description: t('common:toast.downloadComplete.description', { modelId: state.modelId }),
|
description: t('common:toast.downloadComplete.description', {
|
||||||
|
modelId: state.modelId,
|
||||||
|
}),
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
[removeDownload, removeLocalDownloadingModel, setProviders, t]
|
[removeDownload, removeLocalDownloadingModel, setProviders, t]
|
||||||
@ -237,10 +239,14 @@ export function DownloadManagement() {
|
|||||||
<PopoverTrigger asChild>
|
<PopoverTrigger asChild>
|
||||||
{isLeftPanelOpen ? (
|
{isLeftPanelOpen ? (
|
||||||
<div className="bg-left-panel-fg/10 hover:bg-left-panel-fg/12 p-2 rounded-md my-1 relative border border-left-panel-fg/10 cursor-pointer text-left">
|
<div className="bg-left-panel-fg/10 hover:bg-left-panel-fg/12 p-2 rounded-md my-1 relative border border-left-panel-fg/10 cursor-pointer text-left">
|
||||||
<div className="bg-primary font-bold size-5 rounded-full absolute -top-2 -right-1 flex items-center justify-center text-primary-fg">
|
<div className="text-left-panel-fg/80 font-medium flex gap-2">
|
||||||
{downloadCount}
|
<span>{t('downloads')}</span>
|
||||||
|
<span>
|
||||||
|
<div className="bg-primary font-bold size-5 rounded-full flex items-center justify-center text-primary-fg">
|
||||||
|
{downloadCount}
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-left-panel-fg/80 font-medium">{t('downloads')}</p>
|
|
||||||
<div className="mt-2 flex items-center justify-between space-x-2">
|
<div className="mt-2 flex items-center justify-between space-x-2">
|
||||||
<Progress value={overallProgress * 100} />
|
<Progress value={overallProgress * 100} />
|
||||||
<span className="text-xs font-medium text-left-panel-fg/80 shrink-0">
|
<span className="text-xs font-medium text-left-panel-fg/80 shrink-0">
|
||||||
@ -252,7 +258,7 @@ export function DownloadManagement() {
|
|||||||
<div className="fixed bottom-4 left-4 z-50 size-10 bg-main-view border-2 border-main-view-fg/10 rounded-full shadow-md cursor-pointer flex items-center justify-center">
|
<div className="fixed bottom-4 left-4 z-50 size-10 bg-main-view border-2 border-main-view-fg/10 rounded-full shadow-md cursor-pointer flex items-center justify-center">
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<IconDownload
|
<IconDownload
|
||||||
className="text-left-panel-fg/50 -mt-1"
|
className="text-main-view-fg/50 -mt-1"
|
||||||
size={20}
|
size={20}
|
||||||
/>
|
/>
|
||||||
<div className="bg-primary font-bold size-5 rounded-full absolute -top-4 -right-4 flex items-center justify-center text-primary-fg">
|
<div className="bg-primary font-bold size-5 rounded-full absolute -top-4 -right-4 flex items-center justify-center text-primary-fg">
|
||||||
@ -272,7 +278,9 @@ export function DownloadManagement() {
|
|||||||
>
|
>
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<div className="p-2 py-1.5 bg-main-view-fg/5 border-b border-main-view-fg/6">
|
<div className="p-2 py-1.5 bg-main-view-fg/5 border-b border-main-view-fg/6">
|
||||||
<p className="text-xs text-main-view-fg/70">{t('downloading')}</p>
|
<p className="text-xs text-main-view-fg/70">
|
||||||
|
{t('downloading')}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-2 max-h-[300px] overflow-y-auto space-y-2">
|
<div className="p-2 max-h-[300px] overflow-y-auto space-y-2">
|
||||||
{appUpdateState.isDownloading && (
|
{appUpdateState.isDownloading && (
|
||||||
@ -309,10 +317,15 @@ export function DownloadManagement() {
|
|||||||
title="Cancel download"
|
title="Cancel download"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
abortDownload(download.name).then(() => {
|
abortDownload(download.name).then(() => {
|
||||||
toast.info(t('common:toast.downloadCancelled.title'), {
|
toast.info(
|
||||||
id: 'cancel-download',
|
t('common:toast.downloadCancelled.title'),
|
||||||
description: t('common:toast.downloadCancelled.description'),
|
{
|
||||||
})
|
id: 'cancel-download',
|
||||||
|
description: t(
|
||||||
|
'common:toast.downloadCancelled.description'
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
if (downloadProcesses.length === 0) {
|
if (downloadProcesses.length === 0) {
|
||||||
setIsPopoverOpen(false)
|
setIsPopoverOpen(false)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -79,6 +79,9 @@ const LeftPanel = () => {
|
|||||||
const searchContainerRef = useRef<HTMLDivElement>(null)
|
const searchContainerRef = useRef<HTMLDivElement>(null)
|
||||||
const searchContainerMacRef = useRef<HTMLDivElement>(null)
|
const searchContainerMacRef = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
|
// Determine if we're in a resizable context (large screen with panel open)
|
||||||
|
const isResizableContext = !isSmallScreen && open
|
||||||
|
|
||||||
// Use click outside hook for panel with debugging
|
// Use click outside hook for panel with debugging
|
||||||
useClickOutside(
|
useClickOutside(
|
||||||
() => {
|
() => {
|
||||||
@ -189,9 +192,17 @@ const LeftPanel = () => {
|
|||||||
<aside
|
<aside
|
||||||
ref={panelRef}
|
ref={panelRef}
|
||||||
className={cn(
|
className={cn(
|
||||||
'w-48 shrink-0 rounded-lg m-1.5 mr-0 text-left-panel-fg overflow-hidden',
|
'text-left-panel-fg overflow-hidden',
|
||||||
|
// Resizable context: full height and width, no margins
|
||||||
|
isResizableContext && 'h-full w-full',
|
||||||
|
// Small screen context: fixed positioning and styling
|
||||||
isSmallScreen &&
|
isSmallScreen &&
|
||||||
'fixed h-[calc(100%-16px)] bg-main-view z-40 rounded-sm border border-left-panel-fg/10 m-2 px-1',
|
'fixed h-[calc(100%-16px)] bg-main-view z-40 rounded-sm border border-left-panel-fg/10 m-2 px-1 w-48',
|
||||||
|
// Default context: original styling
|
||||||
|
!isResizableContext &&
|
||||||
|
!isSmallScreen &&
|
||||||
|
'w-48 shrink-0 rounded-lg m-1.5 mr-0',
|
||||||
|
// Visibility controls
|
||||||
open
|
open
|
||||||
? 'opacity-100 visibility-visible'
|
? 'opacity-100 visibility-visible'
|
||||||
: 'w-0 absolute -top-100 -left-100 visibility-hidden'
|
: 'w-0 absolute -top-100 -left-100 visibility-hidden'
|
||||||
@ -209,7 +220,12 @@ const LeftPanel = () => {
|
|||||||
{!IS_MACOS && (
|
{!IS_MACOS && (
|
||||||
<div
|
<div
|
||||||
ref={searchContainerRef}
|
ref={searchContainerRef}
|
||||||
className="relative top-1.5 mb-4 mx-1 mt-1 w-[calc(100%-32px)] z-50"
|
className={cn(
|
||||||
|
'relative top-1.5 mb-4 mt-1 z-50',
|
||||||
|
isResizableContext
|
||||||
|
? 'mx-2 w-[calc(100%-48px)]'
|
||||||
|
: 'mx-1 w-[calc(100%-32px)]'
|
||||||
|
)}
|
||||||
data-ignore-outside-clicks
|
data-ignore-outside-clicks
|
||||||
>
|
>
|
||||||
<IconSearch className="absolute size-4 top-1/2 left-2 -translate-y-1/2 text-left-panel-fg/50" />
|
<IconSearch className="absolute size-4 top-1/2 left-2 -translate-y-1/2 text-left-panel-fg/50" />
|
||||||
@ -241,7 +257,10 @@ const LeftPanel = () => {
|
|||||||
{IS_MACOS && (
|
{IS_MACOS && (
|
||||||
<div
|
<div
|
||||||
ref={searchContainerMacRef}
|
ref={searchContainerMacRef}
|
||||||
className="relative mb-4 mx-1 mt-1"
|
className={cn(
|
||||||
|
'relative mb-4 mt-1',
|
||||||
|
isResizableContext ? 'mx-2' : 'mx-1'
|
||||||
|
)}
|
||||||
data-ignore-outside-clicks
|
data-ignore-outside-clicks
|
||||||
>
|
>
|
||||||
<IconSearch className="absolute size-4 top-1/2 left-2 -translate-y-1/2 text-left-panel-fg/50" />
|
<IconSearch className="absolute size-4 top-1/2 left-2 -translate-y-1/2 text-left-panel-fg/50" />
|
||||||
|
|||||||
@ -105,6 +105,11 @@ const SortableItem = memo(({ thread }: { thread: Thread }) => {
|
|||||||
{...attributes}
|
{...attributes}
|
||||||
{...listeners}
|
{...listeners}
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
|
onContextMenu={(e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
setOpenDropdown(true)
|
||||||
|
}}
|
||||||
className={cn(
|
className={cn(
|
||||||
'mb-1 rounded hover:bg-left-panel-fg/10 flex items-center justify-between gap-2 px-1.5 group/thread-list transition-all',
|
'mb-1 rounded hover:bg-left-panel-fg/10 flex items-center justify-between gap-2 px-1.5 group/thread-list transition-all',
|
||||||
isDragging ? 'cursor-move' : 'cursor-pointer',
|
isDragging ? 'cursor-move' : 'cursor-pointer',
|
||||||
|
|||||||
@ -10,7 +10,6 @@ import { Button } from '@/components/ui/button'
|
|||||||
import { useToolApproval } from '@/hooks/useToolApproval'
|
import { useToolApproval } from '@/hooks/useToolApproval'
|
||||||
import { AlertTriangle } from 'lucide-react'
|
import { AlertTriangle } from 'lucide-react'
|
||||||
import { useTranslation } from '@/i18n/react-i18next-compat'
|
import { useTranslation } from '@/i18n/react-i18next-compat'
|
||||||
import { Trans } from 'react-i18next'
|
|
||||||
|
|
||||||
export default function ToolApproval() {
|
export default function ToolApproval() {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
@ -52,11 +51,8 @@ export default function ToolApproval() {
|
|||||||
<div>
|
<div>
|
||||||
<DialogTitle>{t('tools:toolApproval.title')}</DialogTitle>
|
<DialogTitle>{t('tools:toolApproval.title')}</DialogTitle>
|
||||||
<DialogDescription className="mt-1 text-main-view-fg/70">
|
<DialogDescription className="mt-1 text-main-view-fg/70">
|
||||||
<Trans
|
{t('tools:toolApproval.description')}{' '}
|
||||||
i18nKey="tools:toolApproval.description"
|
<span className="font-semibold">{toolName}</span>
|
||||||
values={{ toolName }}
|
|
||||||
components={{ strong: <strong className="font-semibold" /> }}
|
|
||||||
/>
|
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -4,14 +4,18 @@ import { localStorageKey } from '@/constants/localStorage'
|
|||||||
|
|
||||||
type LeftPanelStoreState = {
|
type LeftPanelStoreState = {
|
||||||
open: boolean
|
open: boolean
|
||||||
|
size: number
|
||||||
setLeftPanel: (value: boolean) => void
|
setLeftPanel: (value: boolean) => void
|
||||||
|
setLeftPanelSize: (value: number) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useLeftPanel = create<LeftPanelStoreState>()(
|
export const useLeftPanel = create<LeftPanelStoreState>()(
|
||||||
persist(
|
persist(
|
||||||
(set) => ({
|
(set) => ({
|
||||||
open: true,
|
open: true,
|
||||||
|
size: 20, // Default size of 20%
|
||||||
setLeftPanel: (value) => set({ open: value }),
|
setLeftPanel: (value) => set({ open: value }),
|
||||||
|
setLeftPanelSize: (value) => set({ size: value }),
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
name: localStorageKey.LeftPanel,
|
name: localStorageKey.LeftPanel,
|
||||||
|
|||||||
@ -36,7 +36,16 @@ export const useModelProvider = create<ModelProviderState>()(
|
|||||||
},
|
},
|
||||||
setProviders: (providers) =>
|
setProviders: (providers) =>
|
||||||
set((state) => {
|
set((state) => {
|
||||||
const existingProviders = state.providers
|
const existingProviders = state.providers.map((provider) => {
|
||||||
|
return {
|
||||||
|
...provider,
|
||||||
|
models: provider.models.filter(
|
||||||
|
(e) =>
|
||||||
|
('id' in e || 'model' in e) &&
|
||||||
|
typeof (e.id ?? e.model) === 'string'
|
||||||
|
),
|
||||||
|
}
|
||||||
|
})
|
||||||
// Ensure deletedModels is always an array
|
// Ensure deletedModels is always an array
|
||||||
const currentDeletedModels = Array.isArray(state.deletedModels)
|
const currentDeletedModels = Array.isArray(state.deletedModels)
|
||||||
? state.deletedModels
|
? state.deletedModels
|
||||||
@ -46,11 +55,17 @@ export const useModelProvider = create<ModelProviderState>()(
|
|||||||
const existingProvider = existingProviders.find(
|
const existingProvider = existingProviders.find(
|
||||||
(x) => x.provider === provider.provider
|
(x) => x.provider === provider.provider
|
||||||
)
|
)
|
||||||
const models = existingProvider?.models || []
|
const models = (existingProvider?.models || []).filter(
|
||||||
|
(e) =>
|
||||||
|
('id' in e || 'model' in e) &&
|
||||||
|
typeof (e.id ?? e.model) === 'string'
|
||||||
|
)
|
||||||
const mergedModels = [
|
const mergedModels = [
|
||||||
...models,
|
...models,
|
||||||
...(provider?.models ?? []).filter(
|
...(provider?.models ?? []).filter(
|
||||||
(e) =>
|
(e) =>
|
||||||
|
('id' in e || 'model' in e) &&
|
||||||
|
typeof (e.id ?? e.model) === 'string' &&
|
||||||
!models.some((m) => m.id === e.id) &&
|
!models.some((m) => m.id === e.id) &&
|
||||||
!currentDeletedModels.includes(e.id)
|
!currentDeletedModels.includes(e.id)
|
||||||
),
|
),
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"assistants": "Assistants",
|
"assistants": "Assistants",
|
||||||
"hardware": "Hardware",
|
"hardware": "Hardware",
|
||||||
"mcp-servers": "Mcp Servers",
|
"mcp-servers": "MCP Servers",
|
||||||
"local_api_server": "Local API Server",
|
"local_api_server": "Local API Server",
|
||||||
"https_proxy": "HTTPS Proxy",
|
"https_proxy": "HTTPS Proxy",
|
||||||
"extensions": "Extensions",
|
"extensions": "Extensions",
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"toolApproval": {
|
"toolApproval": {
|
||||||
"title": "Tool Approval Required",
|
"title": "Tool Approval Required",
|
||||||
"description": "The assistant wants to use <strong>{{toolName}}</strong>",
|
"description": "The assistant wants to use",
|
||||||
"securityNotice": "This tool wants to perform an action. Please review and approve.",
|
"securityNotice": "Malicious tools or conversation content could potentially trick the assistant into attempting harmful actions. Review each tool call carefully before approving.",
|
||||||
"deny": "Deny",
|
"deny": "Deny",
|
||||||
"allowOnce": "Allow Once",
|
"allowOnce": "Allow Once",
|
||||||
"alwaysAllow": "Always Allow"
|
"alwaysAllow": "Always Allow"
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"title": "Permintaan Panggilan Alat",
|
"title": "Permintaan Panggilan Alat",
|
||||||
"description": "Asisten ingin menggunakan alat: <strong>{{toolName}}</strong>",
|
"description": "Asisten ingin menggunakan alat: <strong>{{toolName}}</strong>",
|
||||||
"securityNotice": "<strong>Pemberitahuan Keamanan:</strong> Alat berbahaya atau konten percakapan berpotensi menipu asisten untuk mencoba tindakan berbahaya. Tinjau setiap panggilan alat dengan cermat sebelum menyetujui.",
|
"securityNotice": "<strong>Pemberitahuan Keamanan:</strong> Alat berbahaya atau konten percakapan dapat menipu asisten untuk mencoba melakukan tindakan yang merugikan. Tinjau setiap permintaan penggunaan alat dengan cermat sebelum menyetujui.",
|
||||||
"deny": "Tolak",
|
"deny": "Tolak",
|
||||||
"allowOnce": "Izinkan Sekali",
|
"allowOnce": "Izinkan Sekali",
|
||||||
"alwaysAllow": "Selalu Izinkan",
|
"alwaysAllow": "Selalu Izinkan",
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"assistants": "Trợ lý",
|
"assistants": "Trợ lý",
|
||||||
"hardware": "Phần cứng",
|
"hardware": "Phần cứng",
|
||||||
"mcp-servers": "Máy chủ Mcp",
|
"mcp-servers": "Máy chủ MCP",
|
||||||
"local_api_server": "Máy chủ API cục bộ",
|
"local_api_server": "Máy chủ API cục bộ",
|
||||||
"https_proxy": "Proxy HTTPS",
|
"https_proxy": "Proxy HTTPS",
|
||||||
"extensions": "Tiện ích mở rộng",
|
"extensions": "Tiện ích mở rộng",
|
||||||
|
|||||||
@ -62,7 +62,7 @@ export const predefinedProviders = [
|
|||||||
{
|
{
|
||||||
active: true,
|
active: true,
|
||||||
api_key: '',
|
api_key: '',
|
||||||
base_url: 'https://api.anthropic.com/v1',
|
base_url: 'https://api.anthropic.com',
|
||||||
provider: 'anthropic',
|
provider: 'anthropic',
|
||||||
explore_models_url:
|
explore_models_url:
|
||||||
'https://docs.anthropic.com/en/docs/about-claude/models',
|
'https://docs.anthropic.com/en/docs/about-claude/models',
|
||||||
@ -87,8 +87,8 @@ export const predefinedProviders = [
|
|||||||
'The base endpoint to use. See the [Anthropic API documentation](https://docs.anthropic.com/en/api/messages) for more information.',
|
'The base endpoint to use. See the [Anthropic API documentation](https://docs.anthropic.com/en/api/messages) for more information.',
|
||||||
controller_type: 'input',
|
controller_type: 'input',
|
||||||
controller_props: {
|
controller_props: {
|
||||||
placeholder: 'https://api.anthropic.com/v1',
|
placeholder: 'https://api.anthropic.com',
|
||||||
value: 'https://api.anthropic.com/v1',
|
value: 'https://api.anthropic.com',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@ -20,6 +20,13 @@ import { cn } from '@/lib/utils'
|
|||||||
import ToolApproval from '@/containers/dialogs/ToolApproval'
|
import ToolApproval from '@/containers/dialogs/ToolApproval'
|
||||||
import { TranslationProvider } from '@/i18n/TranslationContext'
|
import { TranslationProvider } from '@/i18n/TranslationContext'
|
||||||
import OutOfContextPromiseModal from '@/containers/dialogs/OutOfContextDialog'
|
import OutOfContextPromiseModal from '@/containers/dialogs/OutOfContextDialog'
|
||||||
|
import { useSmallScreen } from '@/hooks/useMediaQuery'
|
||||||
|
import {
|
||||||
|
ResizablePanelGroup,
|
||||||
|
ResizablePanel,
|
||||||
|
ResizableHandle,
|
||||||
|
} from '@/components/ui/resizable'
|
||||||
|
import { useCallback } from 'react'
|
||||||
|
|
||||||
export const Route = createRootRoute({
|
export const Route = createRootRoute({
|
||||||
component: RootLayout,
|
component: RootLayout,
|
||||||
@ -27,7 +34,33 @@ export const Route = createRootRoute({
|
|||||||
|
|
||||||
const AppLayout = () => {
|
const AppLayout = () => {
|
||||||
const { productAnalyticPrompt } = useAnalytic()
|
const { productAnalyticPrompt } = useAnalytic()
|
||||||
const { open: isLeftPanelOpen } = useLeftPanel()
|
const {
|
||||||
|
open: isLeftPanelOpen,
|
||||||
|
setLeftPanel,
|
||||||
|
size: leftPanelSize,
|
||||||
|
setLeftPanelSize,
|
||||||
|
} = useLeftPanel()
|
||||||
|
const isSmallScreen = useSmallScreen()
|
||||||
|
|
||||||
|
// Minimum width threshold for auto-close (10% of screen width)
|
||||||
|
const MIN_PANEL_WIDTH_THRESHOLD = 14
|
||||||
|
|
||||||
|
// Handle panel size changes
|
||||||
|
const handlePanelLayout = useCallback(
|
||||||
|
(sizes: number[]) => {
|
||||||
|
if (sizes.length > 0) {
|
||||||
|
const newSize = sizes[0]
|
||||||
|
|
||||||
|
// Close panel if resized below minimum threshold
|
||||||
|
if (newSize < MIN_PANEL_WIDTH_THRESHOLD) {
|
||||||
|
setLeftPanel(false)
|
||||||
|
} else {
|
||||||
|
setLeftPanelSize(newSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[setLeftPanelSize, setLeftPanel]
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
@ -37,22 +70,56 @@ const AppLayout = () => {
|
|||||||
{/* Fake absolute panel top to enable window drag */}
|
{/* Fake absolute panel top to enable window drag */}
|
||||||
<div className="absolute w-full h-10 z-10" data-tauri-drag-region />
|
<div className="absolute w-full h-10 z-10" data-tauri-drag-region />
|
||||||
<DialogAppUpdater />
|
<DialogAppUpdater />
|
||||||
<div className="flex h-full">
|
|
||||||
{/* left content panel - only show if not logs route */}
|
|
||||||
<LeftPanel />
|
|
||||||
|
|
||||||
{/* Main content panel */}
|
{/* Use ResizablePanelGroup only on larger screens */}
|
||||||
<div
|
{!isSmallScreen && isLeftPanelOpen ? (
|
||||||
className={cn(
|
<ResizablePanelGroup
|
||||||
'h-full flex w-full p-1 ',
|
direction="horizontal"
|
||||||
isLeftPanelOpen && 'w-full md:w-[calc(100%-198px)]'
|
className="h-full"
|
||||||
)}
|
onLayout={handlePanelLayout}
|
||||||
>
|
>
|
||||||
<div className="bg-main-view text-main-view-fg border border-main-view-fg/5 w-full rounded-lg overflow-hidden">
|
{/* Left Panel */}
|
||||||
<Outlet />
|
<ResizablePanel
|
||||||
|
defaultSize={leftPanelSize}
|
||||||
|
minSize={MIN_PANEL_WIDTH_THRESHOLD}
|
||||||
|
maxSize={40}
|
||||||
|
collapsible
|
||||||
|
>
|
||||||
|
<div className="h-full p-1">
|
||||||
|
<LeftPanel />
|
||||||
|
</div>
|
||||||
|
</ResizablePanel>
|
||||||
|
|
||||||
|
{/* Resize Handle */}
|
||||||
|
<ResizableHandle withHandle />
|
||||||
|
|
||||||
|
{/* Main Content Panel */}
|
||||||
|
<ResizablePanel defaultSize={100 - leftPanelSize} minSize={60}>
|
||||||
|
<div className="h-full p-1 pl-0">
|
||||||
|
<div className="bg-main-view text-main-view-fg border border-main-view-fg/5 w-full h-full rounded-lg overflow-hidden">
|
||||||
|
<Outlet />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ResizablePanel>
|
||||||
|
</ResizablePanelGroup>
|
||||||
|
) : (
|
||||||
|
<div className="flex h-full">
|
||||||
|
{/* left content panel - only show if not logs route */}
|
||||||
|
<LeftPanel />
|
||||||
|
|
||||||
|
{/* Main content panel */}
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
'h-full flex w-full p-1 ',
|
||||||
|
isLeftPanelOpen && 'w-full md:w-[calc(100%-198px)]'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="bg-main-view text-main-view-fg border border-main-view-fg/5 w-full rounded-lg overflow-hidden">
|
||||||
|
<Outlet />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
</main>
|
</main>
|
||||||
{productAnalyticPrompt && <PromptAnalytic />}
|
{productAnalyticPrompt && <PromptAnalytic />}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
|
|||||||
@ -5,7 +5,6 @@ import HeaderPage from '@/containers/HeaderPage'
|
|||||||
import { Switch } from '@/components/ui/switch'
|
import { Switch } from '@/components/ui/switch'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import { Card, CardItem } from '@/containers/Card'
|
import { Card, CardItem } from '@/containers/Card'
|
||||||
import LanguageSwitcher from '@/containers/LanguageSwitcher'
|
|
||||||
import { useTranslation } from '@/i18n/react-i18next-compat'
|
import { useTranslation } from '@/i18n/react-i18next-compat'
|
||||||
import { useGeneralSetting } from '@/hooks/useGeneralSetting'
|
import { useGeneralSetting } from '@/hooks/useGeneralSetting'
|
||||||
import { useAppUpdater } from '@/hooks/useAppUpdater'
|
import { useAppUpdater } from '@/hooks/useAppUpdater'
|
||||||
@ -239,10 +238,10 @@ function General() {
|
|||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<CardItem
|
{/* <CardItem
|
||||||
title={t('common:language')}
|
title={t('common:language')}
|
||||||
actions={<LanguageSwitcher />}
|
actions={<LanguageSwitcher />}
|
||||||
/>
|
/> */}
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
{/* Advanced */}
|
{/* Advanced */}
|
||||||
|
|||||||
@ -38,7 +38,7 @@ function ModelProviders() {
|
|||||||
const [name, setName] = useState('')
|
const [name, setName] = useState('')
|
||||||
|
|
||||||
const createProvider = useCallback(() => {
|
const createProvider = useCallback(() => {
|
||||||
if (providers.some((e) => e.provider === name)) {
|
if (providers.some((e) => e.provider.toLowerCase() === name.toLowerCase())) {
|
||||||
toast.error(t('providerAlreadyExists', { name }))
|
toast.error(t('providerAlreadyExists', { name }))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@ -210,7 +210,9 @@ export const fetchModelsFromProvider = async (
|
|||||||
return data.data.map((model: { id: string }) => model.id).filter(Boolean)
|
return data.data.map((model: { id: string }) => model.id).filter(Boolean)
|
||||||
} else if (Array.isArray(data)) {
|
} else if (Array.isArray(data)) {
|
||||||
// Direct array format: ["model-id1", "model-id2", ...]
|
// Direct array format: ["model-id1", "model-id2", ...]
|
||||||
return data.filter(Boolean)
|
return data
|
||||||
|
.filter(Boolean)
|
||||||
|
.map((model) => ('id' in model ? model.id : model))
|
||||||
} else if (data.models && Array.isArray(data.models)) {
|
} else if (data.models && Array.isArray(data.models)) {
|
||||||
// Alternative format: { models: [...] }
|
// Alternative format: { models: [...] }
|
||||||
return data.models
|
return data.models
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user