feat: handle open Jan on HF GGUF repo (#5173)

* feat: handle open Jan on HF GGUF repo

* chore: reset retry attempts
This commit is contained in:
Louis 2025-06-03 01:09:36 +07:00 committed by GitHub
parent 817e3175f3
commit ecef9d7df6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 97 additions and 6 deletions

View File

@ -90,7 +90,7 @@ export default class JanInferenceCortexExtension extends LocalOAIEngine {
Authorization: `Bearer ${apiKey}`,
}
: {},
retry: 4,
retry: 10,
})
return this.api
}

View File

@ -51,6 +51,7 @@ dirs = "6.0.0"
sysinfo = "0.34.2"
ash = "0.38.0"
nvml-wrapper = "0.10.0"
tauri-plugin-deep-link = "2"
[target.'cfg(windows)'.dependencies]
libloading = "0.8.7"
@ -59,3 +60,4 @@ libc = "0.2.172"
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
tauri-plugin-updater = "2"
once_cell = "1.18"
tauri-plugin-single-instance = { version = "2.0.0", features = ["deep-link"] }

19
src-tauri/Info.plist Normal file
View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleIdentifier</key>
<string>jan.ai.app</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>jan.ai.app</string>
<key>CFBundleURLSchemes</key>
<array>
<string>jan</string>
</array>
</dict>
</array>
</dict>
</plist>

View File

@ -19,6 +19,7 @@
"log:default",
"updater:default",
"dialog:default",
"deep-link:default",
"core:webview:allow-create-webview-window",
{
"identifier": "http:default",

View File

@ -14,8 +14,16 @@ use reqwest::blocking::Client;
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_os::init())
let mut builder = tauri::Builder::default();
#[cfg(desktop)]
{
builder = builder.plugin(tauri_plugin_single_instance::init(|_app, argv, _cwd| {
println!("a new app instance was opened with {argv:?} and the deep link event was already triggered");
// when defining deep link schemes at runtime, you must also check `argv` here
}));
}
builder.plugin(tauri_plugin_os::init())
.plugin(tauri_plugin_deep_link::init())
.plugin(tauri_plugin_dialog::init())
.plugin(tauri_plugin_opener::init())
.plugin(tauri_plugin_http::init())

View File

@ -68,7 +68,8 @@
"windows": {
"installMode": "passive"
}
}
},
"deep-link": { "schemes": ["jan"] }
},
"bundle": {
"active": true,

View File

@ -30,6 +30,7 @@
"@tanstack/react-router": "^1.116.0",
"@tanstack/react-router-devtools": "^1.116.0",
"@tauri-apps/api": "^2.5.0",
"@tauri-apps/plugin-deep-link": "~2",
"@tauri-apps/plugin-dialog": "^2.2.1",
"@tauri-apps/plugin-opener": "^2.2.7",
"@tauri-apps/plugin-os": "^2.2.1",

View File

@ -13,6 +13,12 @@ import { getMCPConfig } from '@/services/mcp'
import { useAssistant } from '@/hooks/useAssistant'
import { getAssistants } from '@/services/assistants'
import { migrateData } from '@/utils/migration'
import {
onOpenUrl,
getCurrent as getCurrentDeepLinkUrls,
} from '@tauri-apps/plugin-deep-link'
import { useNavigate } from '@tanstack/react-router'
import { route } from '@/constants/routes'
export function DataProvider() {
const { setProviders } = useModelProvider()
@ -21,6 +27,7 @@ export function DataProvider() {
const { checkForUpdate } = useAppUpdater()
const { setServers } = useMCPServers()
const { setAssistants } = useAssistant()
const navigate = useNavigate()
useEffect(() => {
fetchModels().then((models) => {
@ -39,6 +46,8 @@ export function DataProvider() {
console.warn('Failed to load assistants, keeping default:', error)
})
migrateData()
getCurrentDeepLinkUrls().then(handleDeepLink)
onOpenUrl(handleDeepLink)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
@ -58,5 +67,27 @@ export function DataProvider() {
checkForUpdate()
}, [checkForUpdate])
const handleDeepLink = (urls: string[] | null) => {
if (!urls) return
console.log('Received deeplink:', urls)
const deeplink = urls[0]
if (deeplink) {
const url = new URL(deeplink)
const params = url.pathname.split('/').filter((str) => str.length > 0)
if (params.length < 3) return undefined
// const action = params[0]
// const provider = params[1]
const resource = params.slice(1).join('/')
// return { action, provider, resource }
navigate({
to: route.hub,
search: {
repo: resource,
},
})
}
}
return null
}

View File

@ -1,4 +1,10 @@
import { createFileRoute, Link, useNavigate } from '@tanstack/react-router'
/* eslint-disable @typescript-eslint/no-explicit-any */
import {
createFileRoute,
Link,
useNavigate,
useSearch,
} from '@tanstack/react-router'
import { route } from '@/constants/routes'
import { useModelSources } from '@/hooks/useModelSources'
import { cn, fuzzySearch, toGigabytes } from '@/lib/utils'
@ -37,10 +43,15 @@ type ModelProps = {
}[]
}
}
type SearchParams = {
repo: string
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const Route = createFileRoute(route.hub as any)({
component: Hub,
validateSearch: (search: Record<string, unknown>): SearchParams => ({
repo: search.repo as SearchParams['repo'],
}),
})
const sortOptions = [
@ -50,6 +61,7 @@ const sortOptions = [
function Hub() {
const { sources, fetchSources, loading } = useModelSources()
const search = useSearch({ from: route.hub as any })
const [searchValue, setSearchValue] = useState('')
const [sortSelected, setSortSelected] = useState('newest')
const [expandedModels, setExpandedModels] = useState<Record<string, boolean>>(
@ -71,6 +83,22 @@ function Hub() {
}))
}
useEffect(() => {
if (search.repo) {
setSearchValue(search.repo || '')
setIsSearching(true)
addModelSourceTimeoutRef.current = setTimeout(() => {
addModelSource(search.repo)
.then(() => {
fetchSources()
})
.finally(() => {
setIsSearching(false)
})
}, 500)
}
}, [fetchSources, search])
// Sorting functionality
const sortedModels = useMemo(() => {
return [...sources].sort((a, b) => {