fix(local-tool): sidebar icon + Wails Windows icon.ico + 掃裝置路徑修復
sidebar E icon:
- frontend/src/components/layout/sidebar.tsx 左上角的 "E" 方塊換成 <img> 指向
/visiona-logo.png,從 frontend/public/visiona-logo.png serve
Wails 桌面/工作列 icon:
- 把 branding/icon.ico 複製到 visiona-local/build/windows/icon.ico
- Wails v2 Windows build 偵測到這個檔案就會直接用它當 exe embedded icon,
不再從 appicon.png 自動 resize(解析度更好)
掃裝置根因修復:
1. server main.go:新增 resolveBridgeScript() 智慧找 kneron_bridge.py
- 優先 <exe>/scripts/kneron_bridge.py
- fallback <exe>/../scripts/... 對應 Windows installer 的 {app}\bin\ + {app}\scripts\ 佈局
- fallback <exe>/../Resources/scripts/... 對應 macOS app bundle
2. server kneron/detector.go:ResolvePython 重寫
- 最高優先:VISIONA_PYTHON 環境變數(由 Wails 殼層注入)
- 加入 visiona-local 新路徑:%APPDATA%\visiona-local\runtime\venv、
~/Library/Application Support/visiona-local/runtime/venv、
~/.local/share/visiona-local/runtime/venv
- 保留 edge-ai-platform 舊路徑作為 legacy fallback
3. visiona-local/app.go:spawn server 時 export VISIONA_PYTHON=<pyBin>
讓 detector 直接用 Wails 殼層已經 resolve 好的 python interpreter,
不再自己獨立去找造成不一致。
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
50a3f73acd
commit
f8533a6b04
BIN
local-tool/frontend/public/visiona-logo.png
Normal file
BIN
local-tool/frontend/public/visiona-logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
@ -21,9 +21,12 @@ export function Sidebar() {
|
||||
<aside className="flex h-full w-60 flex-col border-r bg-card">
|
||||
<div className="flex h-14 items-center border-b px-4">
|
||||
<Link href="/" className="flex items-center gap-2 font-semibold">
|
||||
<div className="flex h-8 w-8 items-center justify-center rounded-lg bg-primary text-primary-foreground text-sm font-bold">
|
||||
E
|
||||
</div>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
src="/visiona-logo.png"
|
||||
alt="visionA Local"
|
||||
className="h-8 w-8 rounded-lg"
|
||||
/>
|
||||
<span className="text-lg">{t('nav.appName')}</span>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
@ -13,28 +13,52 @@ import (
|
||||
)
|
||||
|
||||
// ResolvePython finds the best Python interpreter for the given script path.
|
||||
// Search order: script-local venv → parent venv → platform config dir venv → system python3/python.
|
||||
//
|
||||
// Search order:
|
||||
// 1. VISIONA_PYTHON env var (highest priority — set by Wails shell when it
|
||||
// has already provisioned a bundled venv)
|
||||
// 2. Script-local venv / parent venv
|
||||
// 3. %APPDATA%\visiona-local\runtime\venv (Windows installer seeded venv)
|
||||
// 4. ~/.local/share/visiona-local/runtime/venv (Linux)
|
||||
// 5. ~/Library/Application Support/visiona-local/runtime/venv (macOS)
|
||||
// 6. Legacy: ~/.edge-ai-platform/venv / %LOCALAPPDATA%\EdgeAIPlatform\venv
|
||||
// 7. System python3 / python
|
||||
func ResolvePython(scriptPath string) string {
|
||||
// 1. Environment variable override (set by Wails shell)
|
||||
if p := os.Getenv("VISIONA_PYTHON"); p != "" {
|
||||
if _, err := os.Stat(p); err == nil {
|
||||
return p
|
||||
}
|
||||
}
|
||||
|
||||
scriptDir := filepath.Dir(scriptPath)
|
||||
parentDir := filepath.Dir(scriptDir)
|
||||
|
||||
// Build candidate list with both Unix and Windows venv layouts
|
||||
// 2. Script-local / parent venv
|
||||
var candidates []string
|
||||
for _, base := range []string{scriptDir, parentDir} {
|
||||
candidates = append(candidates,
|
||||
filepath.Join(base, "venv", "bin", "python3"), // Unix
|
||||
filepath.Join(base, "venv", "Scripts", "python.exe"), // Windows
|
||||
filepath.Join(base, "venv", "bin", "python3"), // Unix
|
||||
filepath.Join(base, "venv", "Scripts", "python.exe"), // Windows
|
||||
)
|
||||
}
|
||||
|
||||
// 3-5. Platform-specific data dir (visiona-local)
|
||||
if home, err := os.UserHomeDir(); err == nil {
|
||||
candidates = append(candidates,
|
||||
// Windows: %APPDATA%\visiona-local\runtime\venv
|
||||
filepath.Join(os.Getenv("APPDATA"), "visiona-local", "runtime", "venv", "Scripts", "python.exe"),
|
||||
// macOS
|
||||
filepath.Join(home, "Library", "Application Support", "visiona-local", "runtime", "venv", "bin", "python3"),
|
||||
// Linux
|
||||
filepath.Join(home, ".local", "share", "visiona-local", "runtime", "venv", "bin", "python3"),
|
||||
// 6. Legacy edge-ai-platform paths (backwards compat for upgrades)
|
||||
filepath.Join(home, ".edge-ai-platform", "venv", "bin", "python3"),
|
||||
filepath.Join(home, ".edge-ai-platform", "venv", "Scripts", "python.exe"),
|
||||
)
|
||||
}
|
||||
|
||||
// On Windows, also check %LOCALAPPDATA%\EdgeAIPlatform\venv
|
||||
// 6b. Legacy %LOCALAPPDATA%\EdgeAIPlatform\venv
|
||||
if appData := os.Getenv("LOCALAPPDATA"); appData != "" {
|
||||
candidates = append(candidates,
|
||||
filepath.Join(appData, "EdgeAIPlatform", "venv", "Scripts", "python.exe"),
|
||||
@ -42,12 +66,15 @@ func ResolvePython(scriptPath string) string {
|
||||
}
|
||||
|
||||
for _, p := range candidates {
|
||||
if p == "" {
|
||||
continue
|
||||
}
|
||||
if _, err := os.Stat(p); err == nil {
|
||||
return p
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to system python
|
||||
// 7. Fallback to system python
|
||||
for _, name := range []string{"python3", "python"} {
|
||||
if p, err := exec.LookPath(name); err == nil {
|
||||
return p
|
||||
|
||||
@ -49,6 +49,33 @@ func baseDir(devMode bool) string {
|
||||
return filepath.Dir(exe)
|
||||
}
|
||||
|
||||
// resolveBridgeScript finds kneron_bridge.py across different packaging layouts.
|
||||
//
|
||||
// Possible locations (tried in order):
|
||||
// 1. <base>/scripts/kneron_bridge.py — dev mode or flat layout
|
||||
// 2. <base>/../scripts/kneron_bridge.py — Windows/Linux installer: binary in {app}/bin, scripts in {app}/scripts
|
||||
// 3. <base>/../Resources/scripts/kneron_bridge.py — macOS app bundle: binary in Contents/MacOS, scripts in Contents/Resources
|
||||
// 4. ./scripts/kneron_bridge.py — cwd fallback
|
||||
func resolveBridgeScript(base string) string {
|
||||
candidates := []string{
|
||||
filepath.Join(base, "scripts", "kneron_bridge.py"),
|
||||
filepath.Join(base, "..", "scripts", "kneron_bridge.py"),
|
||||
filepath.Join(base, "..", "Resources", "scripts", "kneron_bridge.py"),
|
||||
filepath.Join(".", "scripts", "kneron_bridge.py"),
|
||||
}
|
||||
for _, c := range candidates {
|
||||
abs, err := filepath.Abs(c)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if info, err := os.Stat(abs); err == nil && !info.IsDir() {
|
||||
return abs
|
||||
}
|
||||
}
|
||||
// Nothing found — return the default so downstream logs a clear error
|
||||
return filepath.Join(base, "scripts", "kneron_bridge.py")
|
||||
}
|
||||
|
||||
func main() {
|
||||
cfg := config.Load()
|
||||
|
||||
@ -101,7 +128,9 @@ func main() {
|
||||
|
||||
// Initialize device manager
|
||||
registry := device.NewRegistry()
|
||||
deviceMgr := device.NewManager(registry, cfg.MockMode, cfg.MockDeviceCount, filepath.Join(base, "scripts", "kneron_bridge.py"))
|
||||
bridgeScript := resolveBridgeScript(base)
|
||||
logger.Info("Kneron bridge script: %s", bridgeScript)
|
||||
deviceMgr := device.NewManager(registry, cfg.MockMode, cfg.MockDeviceCount, bridgeScript)
|
||||
deviceMgr.SetLogBroadcaster(logBroadcaster)
|
||||
deviceMgr.Start()
|
||||
|
||||
|
||||
@ -377,6 +377,12 @@ func (a *App) startServer() error {
|
||||
env = append(env, "VISIONA_BUNDLE_BIN_DIR="+binDir)
|
||||
fmt.Fprintln(os.Stderr, "[visiona-local] bundle bin dir:", binDir)
|
||||
}
|
||||
// 注入 python interpreter 路徑給 server(kneron detector 會讀 VISIONA_PYTHON)
|
||||
// 避免 detector 自己去 resolve 又走不同的路徑邏輯造成不一致。
|
||||
if !a.mockMode && pyBin != "" {
|
||||
env = append(env, "VISIONA_PYTHON="+pyBin)
|
||||
fmt.Fprintln(os.Stderr, "[visiona-local] python interpreter:", pyBin)
|
||||
}
|
||||
cmd.Env = env
|
||||
if stdoutLog != nil {
|
||||
cmd.Stdout = stdoutLog
|
||||
|
||||
BIN
local-tool/visiona-local/build/windows/icon.ico
Normal file
BIN
local-tool/visiona-local/build/windows/icon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 50 KiB |
Loading…
x
Reference in New Issue
Block a user