feat: add HF token setting

This commit is contained in:
Louis 2025-07-11 00:05:52 +07:00
parent 9cea579c8e
commit b8259e7794
4 changed files with 96 additions and 42 deletions

View File

@ -1,6 +1,6 @@
import { invoke } from '@tauri-apps/api/core';
import { listen } from '@tauri-apps/api/event';
import { BaseExtension, events } from '@janhq/core';
import { invoke } from '@tauri-apps/api/core'
import { listen } from '@tauri-apps/api/event'
import { BaseExtension, events } from '@janhq/core'
export enum Settings {
hfToken = 'hf-token',
@ -24,7 +24,7 @@ export default class DownloadManager extends BaseExtension {
this.hfToken = await this.getSetting<string>(Settings.hfToken, undefined)
}
async onUnload() { }
async onUnload() {}
async downloadFile(
url: string,
@ -39,26 +39,36 @@ export default class DownloadManager extends BaseExtension {
)
}
onSettingUpdate<T>(key: string, value: T): void {
if (key === Settings.hfToken) {
this.hfToken = value as string
}
}
async downloadFiles(
items: DownloadItem[],
taskId: string,
onProgress?: (transferred: number, total: number) => void
) {
// relay tauri events to onProgress callback
const unlisten = await listen<DownloadEvent>(`download-${taskId}`, (event) => {
if (onProgress) {
let payload = event.payload
onProgress(payload.transferred, payload.total)
const unlisten = await listen<DownloadEvent>(
`download-${taskId}`,
(event) => {
if (onProgress) {
let payload = event.payload
onProgress(payload.transferred, payload.total)
}
}
})
)
try {
await invoke<void>(
"download_files",
{ items, taskId, headers: this._getHeaders() },
)
await invoke<void>('download_files', {
items,
taskId,
headers: this._getHeaders(),
})
} catch (error) {
console.error("Error downloading task", taskId, error)
console.error('Error downloading task', taskId, error)
throw error
} finally {
unlisten()
@ -67,16 +77,16 @@ export default class DownloadManager extends BaseExtension {
async cancelDownload(taskId: string) {
try {
await invoke<void>("cancel_download_task", { taskId })
await invoke<void>('cancel_download_task', { taskId })
} catch (error) {
console.error("Error cancelling download:", error)
console.error('Error cancelling download:', error)
throw error
}
}
_getHeaders() {
return {
...(this.hfToken && { Authorization: `Bearer ${this.hfToken}` })
...(this.hfToken && { Authorization: `Bearer ${this.hfToken}` }),
}
}
}

View File

@ -1,11 +1,14 @@
import { create } from 'zustand'
import { persist, createJSONStorage } from 'zustand/middleware'
import { localStorageKey } from '@/constants/localStorage'
import { ExtensionManager } from '@/lib/extension'
type LeftPanelStoreState = {
currentLanguage: Language
spellCheckChatInput: boolean
experimentalFeatures: boolean
huggingfaceToken?: string
setHuggingfaceToken: (token: string) => void
setExperimentalFeatures: (value: boolean) => void
setSpellCheckChatInput: (value: boolean) => void
setCurrentLanguage: (value: Language) => void
@ -17,9 +20,29 @@ export const useGeneralSetting = create<LeftPanelStoreState>()(
currentLanguage: 'en',
spellCheckChatInput: true,
experimentalFeatures: false,
huggingfaceToken: undefined,
setExperimentalFeatures: (value) => set({ experimentalFeatures: value }),
setSpellCheckChatInput: (value) => set({ spellCheckChatInput: value }),
setCurrentLanguage: (value) => set({ currentLanguage: value }),
setHuggingfaceToken: (token) => {
set({ huggingfaceToken: token })
ExtensionManager.getInstance()
.getByName('@janhq/download-extension')
?.getSettings()
.then((settings) => {
if (settings) {
const newSettings = settings.map((e) => {
if (e.key === 'hf-token') {
e.controllerProps.value = token
}
return e
})
ExtensionManager.getInstance()
.getByName('@janhq/download-extension')
?.updateSettings(newSettings)
}
})
},
}),
{
name: localStorageKey.settingGeneral,

View File

@ -212,6 +212,8 @@
"factoryResetDesc": "This will reset all app settings to their defaults. This can't be undone. We only recommend this if the app is corrupted.",
"cancel": "Cancel",
"reset": "Reset",
"huggingfaceToken": "HuggingFace Token",
"huggingfaceTokenDesc": "Your HuggingFace API token for accessing models.",
"resources": "Resources",
"documentation": "Documentation",
"documentationDesc": "Learn how to use Jan and explore its features.",

View File

@ -45,6 +45,7 @@ import { isDev } from '@/lib/utils'
import { emit } from '@tauri-apps/api/event'
import { stopAllModels } from '@/services/models'
import { SystemEvent } from '@/types/events'
import { Input } from '@/components/ui/input'
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const Route = createFileRoute(route.settings.general as any)({
@ -58,6 +59,8 @@ function General() {
setSpellCheckChatInput,
experimentalFeatures,
setExperimentalFeatures,
huggingfaceToken,
setHuggingfaceToken,
} = useGeneralSetting()
const openFileTitle = (): string => {
@ -245,20 +248,6 @@ function General() {
/>
</Card>
{/* Advanced */}
<Card title="Advanced">
<CardItem
title="Experimental Features"
description="Enable experimental features. They may be unstable or change at any time."
actions={
<Switch
checked={experimentalFeatures}
onCheckedChange={(e) => setExperimentalFeatures(e)}
/>
}
/>
</Card>
{/* Data folder */}
<Card title={t('common:dataFolder')}>
<CardItem
@ -399,20 +388,15 @@ function General() {
}
/>
</Card>
{/* Other */}
<Card title={t('common:others')}>
{/* Advanced */}
<Card title="Advanced">
<CardItem
title={t('settings:others.spellCheck', {
ns: 'settings',
})}
description={t('settings:others.spellCheckDesc', {
ns: 'settings',
})}
title="Experimental Features"
description="Enable experimental features. They may be unstable or change at any time."
actions={
<Switch
checked={spellCheckChatInput}
onCheckedChange={(e) => setSpellCheckChatInput(e)}
checked={experimentalFeatures}
onCheckedChange={(e) => setExperimentalFeatures(e)}
/>
}
/>
@ -464,6 +448,41 @@ function General() {
/>
</Card>
{/* Other */}
<Card title={t('common:others')}>
<CardItem
title={t('settings:others.spellCheck', {
ns: 'settings',
})}
description={t('settings:others.spellCheckDesc', {
ns: 'settings',
})}
actions={
<Switch
checked={spellCheckChatInput}
onCheckedChange={(e) => setSpellCheckChatInput(e)}
/>
}
/>
<CardItem
title={t('settings:general.huggingfaceToken', {
ns: 'settings',
})}
description={t('settings:general.huggingfaceTokenDesc', {
ns: 'settings',
})}
actions={
<Input
id="hf-token"
value={huggingfaceToken || ''}
onChange={(e) => setHuggingfaceToken(e.target.value)}
placeholder={'hf_xxx'}
required
/>
}
/>
</Card>
{/* Resources */}
<Card title={t('settings:general.resources')}>
<CardItem