mirror of
https://github.com/langgenius/dify.git
synced 2026-05-05 18:08:07 +08:00
Merge branch 'main' into feat/rag-2
This commit is contained in:
@ -35,7 +35,7 @@ import type { AppDetailResponse } from '@/models/app'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import type { AppSSO } from '@/types/app'
|
||||
import Indicator from '@/app/components/header/indicator'
|
||||
import { fetchAppDetail } from '@/service/apps'
|
||||
import { fetchAppDetailDirect } from '@/service/apps'
|
||||
import { AccessMode } from '@/models/access-control'
|
||||
import AccessControl from '../app-access-control'
|
||||
import { useAppWhiteListSubjects } from '@/service/access-control'
|
||||
@ -161,11 +161,15 @@ function AppCard({
|
||||
return
|
||||
setShowAccessControl(true)
|
||||
}, [appDetail])
|
||||
const handleAccessControlUpdate = useCallback(() => {
|
||||
fetchAppDetail({ url: '/apps', id: appDetail!.id }).then((res) => {
|
||||
const handleAccessControlUpdate = useCallback(async () => {
|
||||
try {
|
||||
const res = await fetchAppDetailDirect({ url: '/apps', id: appDetail!.id })
|
||||
setAppDetail(res)
|
||||
setShowAccessControl(false)
|
||||
})
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Failed to fetch app detail:', error)
|
||||
}
|
||||
}, [appDetail, setAppDetail])
|
||||
|
||||
return (
|
||||
@ -123,7 +123,7 @@ const Chart: React.FC<IChartProps> = ({
|
||||
dimensions: ['date', yField],
|
||||
source: statistics,
|
||||
},
|
||||
grid: { top: 8, right: 36, bottom: 0, left: 0, containLabel: true },
|
||||
grid: { top: 8, right: 36, bottom: 10, left: 25, containLabel: true },
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
position: 'top',
|
||||
@ -165,7 +165,7 @@ const Chart: React.FC<IChartProps> = ({
|
||||
lineStyle: {
|
||||
color: COMMON_COLOR_MAP.splitLineDark,
|
||||
},
|
||||
interval(index, value) {
|
||||
interval(_index, value) {
|
||||
return !!value
|
||||
},
|
||||
},
|
||||
@ -242,7 +242,7 @@ const Chart: React.FC<IChartProps> = ({
|
||||
? ''
|
||||
: <span>{t('appOverview.analysis.tokenUsage.consumed')} Tokens<span className='text-sm'>
|
||||
<span className='ml-1 text-text-tertiary'>(</span>
|
||||
<span className='text-orange-400'>~{sum(statistics.map(item => Number.parseFloat(get(item, 'total_price', '0')))).toLocaleString('en-US', { style: 'currency', currency: 'USD', minimumFractionDigits: 4 })}</span>
|
||||
<span className='text-orange-400'>~{sum(statistics.map(item => Number.parseFloat(String(get(item, 'total_price', '0'))))).toLocaleString('en-US', { style: 'currency', currency: 'USD', minimumFractionDigits: 4 })}</span>
|
||||
<span className='text-text-tertiary'>)</span>
|
||||
</span></span>}
|
||||
textStyle={{ main: `!text-3xl !font-normal ${sumData === 0 ? '!text-text-quaternary' : ''}` }} />
|
||||
@ -268,7 +268,7 @@ export const MessagesChart: FC<IBizChartProps> = ({ id, period }) => {
|
||||
const noDataFlag = !response.data || response.data.length === 0
|
||||
return <Chart
|
||||
basicInfo={{ title: t('appOverview.analysis.totalMessages.title'), explanation: t('appOverview.analysis.totalMessages.explanation'), timePeriod: period.name }}
|
||||
chartData={!noDataFlag ? response : { data: getDefaultChartData(period.query ?? defaultPeriod) }}
|
||||
chartData={!noDataFlag ? response : { data: getDefaultChartData(period.query ?? defaultPeriod) } as any}
|
||||
chartType='messages'
|
||||
{...(noDataFlag && { yMax: 500 })}
|
||||
/>
|
||||
@ -282,7 +282,7 @@ export const ConversationsChart: FC<IBizChartProps> = ({ id, period }) => {
|
||||
const noDataFlag = !response.data || response.data.length === 0
|
||||
return <Chart
|
||||
basicInfo={{ title: t('appOverview.analysis.totalConversations.title'), explanation: t('appOverview.analysis.totalConversations.explanation'), timePeriod: period.name }}
|
||||
chartData={!noDataFlag ? response : { data: getDefaultChartData(period.query ?? defaultPeriod) }}
|
||||
chartData={!noDataFlag ? response : { data: getDefaultChartData(period.query ?? defaultPeriod) } as any}
|
||||
chartType='conversations'
|
||||
{...(noDataFlag && { yMax: 500 })}
|
||||
/>
|
||||
@ -297,7 +297,7 @@ export const EndUsersChart: FC<IBizChartProps> = ({ id, period }) => {
|
||||
const noDataFlag = !response.data || response.data.length === 0
|
||||
return <Chart
|
||||
basicInfo={{ title: t('appOverview.analysis.activeUsers.title'), explanation: t('appOverview.analysis.activeUsers.explanation'), timePeriod: period.name }}
|
||||
chartData={!noDataFlag ? response : { data: getDefaultChartData(period.query ?? defaultPeriod) }}
|
||||
chartData={!noDataFlag ? response : { data: getDefaultChartData(period.query ?? defaultPeriod) } as any}
|
||||
chartType='endUsers'
|
||||
{...(noDataFlag && { yMax: 500 })}
|
||||
/>
|
||||
@ -380,7 +380,7 @@ export const CostChart: FC<IBizChartProps> = ({ id, period }) => {
|
||||
const noDataFlag = !response.data || response.data.length === 0
|
||||
return <Chart
|
||||
basicInfo={{ title: t('appOverview.analysis.tokenUsage.title'), explanation: t('appOverview.analysis.tokenUsage.explanation'), timePeriod: period.name }}
|
||||
chartData={!noDataFlag ? response : { data: getDefaultChartData(period.query ?? defaultPeriod) }}
|
||||
chartData={!noDataFlag ? response : { data: getDefaultChartData(period.query ?? defaultPeriod) } as any}
|
||||
chartType='costs'
|
||||
{...(noDataFlag && { yMax: 100 })}
|
||||
/>
|
||||
@ -394,7 +394,7 @@ export const WorkflowMessagesChart: FC<IBizChartProps> = ({ id, period }) => {
|
||||
const noDataFlag = !response.data || response.data.length === 0
|
||||
return <Chart
|
||||
basicInfo={{ title: t('appOverview.analysis.totalMessages.title'), explanation: t('appOverview.analysis.totalMessages.explanation'), timePeriod: period.name }}
|
||||
chartData={!noDataFlag ? response : { data: getDefaultChartData({ ...(period.query ?? defaultPeriod), key: 'runs' }) }}
|
||||
chartData={!noDataFlag ? response : { data: getDefaultChartData({ ...(period.query ?? defaultPeriod), key: 'runs' }) } as any}
|
||||
chartType='conversations'
|
||||
valueKey='runs'
|
||||
{...(noDataFlag && { yMax: 500 })}
|
||||
@ -410,7 +410,7 @@ export const WorkflowDailyTerminalsChart: FC<IBizChartProps> = ({ id, period })
|
||||
const noDataFlag = !response.data || response.data.length === 0
|
||||
return <Chart
|
||||
basicInfo={{ title: t('appOverview.analysis.activeUsers.title'), explanation: t('appOverview.analysis.activeUsers.explanation'), timePeriod: period.name }}
|
||||
chartData={!noDataFlag ? response : { data: getDefaultChartData(period.query ?? defaultPeriod) }}
|
||||
chartData={!noDataFlag ? response : { data: getDefaultChartData(period.query ?? defaultPeriod) } as any}
|
||||
chartType='endUsers'
|
||||
{...(noDataFlag && { yMax: 500 })}
|
||||
/>
|
||||
@ -425,7 +425,7 @@ export const WorkflowCostChart: FC<IBizChartProps> = ({ id, period }) => {
|
||||
const noDataFlag = !response.data || response.data.length === 0
|
||||
return <Chart
|
||||
basicInfo={{ title: t('appOverview.analysis.tokenUsage.title'), explanation: t('appOverview.analysis.tokenUsage.explanation'), timePeriod: period.name }}
|
||||
chartData={!noDataFlag ? response : { data: getDefaultChartData(period.query ?? defaultPeriod) }}
|
||||
chartData={!noDataFlag ? response : { data: getDefaultChartData(period.query ?? defaultPeriod) } as any}
|
||||
chartType='workflowCosts'
|
||||
{...(noDataFlag && { yMax: 100 })}
|
||||
/>
|
||||
@ -18,3 +18,13 @@
|
||||
.pluginInstallIcon {
|
||||
background-image: url(../assets/chromeplugin-install.svg);
|
||||
}
|
||||
|
||||
:global(html[data-theme="dark"]) .iframeIcon,
|
||||
:global(html[data-theme="dark"]) .scriptsIcon,
|
||||
:global(html[data-theme="dark"]) .chromePluginIcon {
|
||||
filter: invert(0.86) hue-rotate(180deg) saturate(0.5) brightness(0.95);
|
||||
}
|
||||
|
||||
:global(html[data-theme="dark"]) .pluginInstallIcon {
|
||||
filter: invert(0.9);
|
||||
}
|
||||
|
||||
@ -117,7 +117,7 @@ const Flowchart = React.forwardRef((props: {
|
||||
const [isInitialized, setIsInitialized] = useState(false)
|
||||
const [currentTheme, setCurrentTheme] = useState<'light' | 'dark'>(props.theme || 'light')
|
||||
const containerRef = useRef<HTMLDivElement>(null)
|
||||
const chartId = useRef(`mermaid-chart-${Math.random().toString(36).substr(2, 9)}`).current
|
||||
const chartId = useRef(`mermaid-chart-${Math.random().toString(36).slice(2, 11)}`).current
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
const renderTimeoutRef = useRef<NodeJS.Timeout>()
|
||||
const [errMsg, setErrMsg] = useState('')
|
||||
|
||||
@ -259,7 +259,7 @@ function getFullMatchOffset(
|
||||
): number {
|
||||
let triggerOffset = offset
|
||||
for (let i = triggerOffset; i <= entryText.length; i++) {
|
||||
if (documentText.substr(-i) === entryText.substr(0, i))
|
||||
if (documentText.slice(-i) === entryText.slice(0, i))
|
||||
triggerOffset = i
|
||||
}
|
||||
return triggerOffset
|
||||
|
||||
@ -32,7 +32,7 @@ const GotoAnything: FC<Props> = ({
|
||||
const { t } = useTranslation()
|
||||
const [show, setShow] = useState<boolean>(false)
|
||||
const [searchQuery, setSearchQuery] = useState<string>('')
|
||||
const [cmdVal, setCmdVal] = useState<string>('')
|
||||
const [cmdVal, setCmdVal] = useState<string>('_')
|
||||
const inputRef = useRef<HTMLInputElement>(null)
|
||||
const handleNavSearch = useCallback((q: string) => {
|
||||
setShow(true)
|
||||
@ -120,9 +120,14 @@ const GotoAnything: FC<Props> = ({
|
||||
},
|
||||
)
|
||||
|
||||
// Prevent automatic selection of the first option when cmdVal is not set
|
||||
const clearSelection = () => {
|
||||
setCmdVal('_')
|
||||
}
|
||||
|
||||
const handleCommandSelect = useCallback((commandKey: string) => {
|
||||
setSearchQuery(`${commandKey} `)
|
||||
setCmdVal('')
|
||||
clearSelection()
|
||||
setTimeout(() => {
|
||||
inputRef.current?.focus()
|
||||
}, 0)
|
||||
@ -233,9 +238,6 @@ const GotoAnything: FC<Props> = ({
|
||||
inputRef.current?.focus()
|
||||
})
|
||||
}
|
||||
return () => {
|
||||
setCmdVal('')
|
||||
}
|
||||
}, [show])
|
||||
|
||||
return (
|
||||
@ -245,6 +247,7 @@ const GotoAnything: FC<Props> = ({
|
||||
onClose={() => {
|
||||
setShow(false)
|
||||
setSearchQuery('')
|
||||
clearSelection()
|
||||
onHide?.()
|
||||
}}
|
||||
closable={false}
|
||||
@ -268,7 +271,7 @@ const GotoAnything: FC<Props> = ({
|
||||
onChange={(e) => {
|
||||
setSearchQuery(e.target.value)
|
||||
if (!e.target.value.startsWith('@'))
|
||||
setCmdVal('')
|
||||
clearSelection()
|
||||
}}
|
||||
className='flex-1 !border-0 !bg-transparent !shadow-none'
|
||||
wrapperClassName='flex-1 !border-0 !bg-transparent'
|
||||
@ -321,40 +324,40 @@ const GotoAnything: FC<Props> = ({
|
||||
/>
|
||||
) : (
|
||||
Object.entries(groupedResults).map(([type, results], groupIndex) => (
|
||||
<Command.Group key={groupIndex} heading={(() => {
|
||||
const typeMap: Record<string, string> = {
|
||||
'app': 'app.gotoAnything.groups.apps',
|
||||
'plugin': 'app.gotoAnything.groups.plugins',
|
||||
'knowledge': 'app.gotoAnything.groups.knowledgeBases',
|
||||
'workflow-node': 'app.gotoAnything.groups.workflowNodes',
|
||||
}
|
||||
return t(typeMap[type] || `${type}s`)
|
||||
})()} className='p-2 capitalize text-text-secondary'>
|
||||
{results.map(result => (
|
||||
<Command.Item
|
||||
key={`${result.type}-${result.id}`}
|
||||
value={result.title}
|
||||
className='flex cursor-pointer items-center gap-3 rounded-md p-3 will-change-[background-color] hover:bg-state-base-hover aria-[selected=true]:bg-state-base-hover-alt data-[selected=true]:bg-state-base-hover-alt'
|
||||
onSelect={() => handleNavigate(result)}
|
||||
>
|
||||
{result.icon}
|
||||
<div className='min-w-0 flex-1'>
|
||||
<div className='truncate font-medium text-text-secondary'>
|
||||
{result.title}
|
||||
</div>
|
||||
{result.description && (
|
||||
<div className='mt-0.5 truncate text-xs text-text-quaternary'>
|
||||
{result.description}
|
||||
<Command.Group key={groupIndex} heading={(() => {
|
||||
const typeMap: Record<string, string> = {
|
||||
'app': 'app.gotoAnything.groups.apps',
|
||||
'plugin': 'app.gotoAnything.groups.plugins',
|
||||
'knowledge': 'app.gotoAnything.groups.knowledgeBases',
|
||||
'workflow-node': 'app.gotoAnything.groups.workflowNodes',
|
||||
}
|
||||
return t(typeMap[type] || `${type}s`)
|
||||
})()} className='p-2 capitalize text-text-secondary'>
|
||||
{results.map(result => (
|
||||
<Command.Item
|
||||
key={`${result.type}-${result.id}`}
|
||||
value={result.title}
|
||||
className='flex cursor-pointer items-center gap-3 rounded-md p-3 will-change-[background-color] aria-[selected=true]:bg-state-base-hover data-[selected=true]:bg-state-base-hover'
|
||||
onSelect={() => handleNavigate(result)}
|
||||
>
|
||||
{result.icon}
|
||||
<div className='min-w-0 flex-1'>
|
||||
<div className='truncate font-medium text-text-secondary'>
|
||||
{result.title}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className='text-xs capitalize text-text-quaternary'>
|
||||
{result.type}
|
||||
</div>
|
||||
</Command.Item>
|
||||
))}
|
||||
</Command.Group>
|
||||
))
|
||||
{result.description && (
|
||||
<div className='mt-0.5 truncate text-xs text-text-quaternary'>
|
||||
{result.description}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className='text-xs capitalize text-text-quaternary'>
|
||||
{result.type}
|
||||
</div>
|
||||
</Command.Item>
|
||||
))}
|
||||
</Command.Group>
|
||||
))
|
||||
)}
|
||||
{!isCommandsMode && emptyResult}
|
||||
{!isCommandsMode && defaultUI}
|
||||
@ -373,7 +376,7 @@ const GotoAnything: FC<Props> = ({
|
||||
{t('app.gotoAnything.resultCount', { count: searchResults.length })}
|
||||
{searchMode !== 'general' && (
|
||||
<span className='ml-2 opacity-60'>
|
||||
{t('app.gotoAnything.inScope', { scope: searchMode.replace('@', '') })}
|
||||
{t('app.gotoAnything.inScope', { scope: searchMode.replace('@', '') })}
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user