feat(t101): 托盤刪除語意取代看守語意——leo 實測裁定
「沒有看守這件事,只要列入就是看守,不想同步就刪掉」: 子選單「停止看守這個資料夾」→「刪除這個知識庫」(按下從列表剔除、冪等); 「+ 建立新資料夾並看守…」整項刪除。tray go test 綠(總管親跑)。 (實作=子 CC;驗證+commit=總管。此案原被誤擱置害 v0.13 漏裝=mistakes c947e5a)
This commit is contained in:
+2
-49
@@ -494,7 +494,7 @@ func main() {
|
|||||||
desk, hasTray := a.(desktop.App)
|
desk, hasTray := a.(desktop.App)
|
||||||
|
|
||||||
// rebuildTray 依現況重建整個 tray 選單(daemon-beta t7 多資料夾):
|
// rebuildTray 依現況重建整個 tray 選單(daemon-beta t7 多資料夾):
|
||||||
// 每個看守中的資料夾一列(點=在 Finder 打開)+各自的「停止看守」項;
|
// 每個列入的資料夾一列(點=在 Finder 打開)+各自的「刪除這個知識庫」項;
|
||||||
// 重設選單=刷新(比 (*Menu).Refresh() 跨版本更穩)。
|
// 重設選單=刷新(比 (*Menu).Refresh() 跨版本更穩)。
|
||||||
var rebuildTray func()
|
var rebuildTray func()
|
||||||
|
|
||||||
@@ -599,51 +599,6 @@ func main() {
|
|||||||
}, win)
|
}, win)
|
||||||
}
|
}
|
||||||
|
|
||||||
// t17:fyne 內建 folder picker 無法輸入名稱建新資料夾(leo 07-24 親測)——
|
|
||||||
// 補一條「建立新資料夾並看守…」:文字輸入+確定 → 在 ~/ 底下 mkdir(已存在則沿用)→ 看守。
|
|
||||||
newFolderAction := func() {
|
|
||||||
win.Show()
|
|
||||||
win.RequestFocus()
|
|
||||||
entry := widget.NewEntry()
|
|
||||||
entry.SetPlaceHolder("例如:我的知識庫")
|
|
||||||
content := container.NewVBox(
|
|
||||||
widget.NewLabel("輸入新資料夾名稱(會建立在你的家目錄底下):"),
|
|
||||||
entry,
|
|
||||||
)
|
|
||||||
dialog.ShowCustomConfirm("建立新資料夾並看守", "確定", "取消", content, func(ok bool) {
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
name := strings.TrimSpace(entry.Text)
|
|
||||||
if name == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 只收「單層名稱」:擋路徑分隔與 ..(避免建到家目錄之外)
|
|
||||||
if strings.ContainsAny(name, `/\`) || name == "." || name == ".." {
|
|
||||||
dialog.ShowError(errors.New("名稱不能包含 / 或 \\,也不能是 . 或 .."), win)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
home, err := os.UserHomeDir()
|
|
||||||
if err != nil {
|
|
||||||
dialog.ShowError(err, win)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
p := filepath.Join(home, name)
|
|
||||||
if err := os.MkdirAll(p, 0o755); err != nil { // 已存在=直接沿用
|
|
||||||
dialog.ShowError(err, win)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
addWatchFolder(cfg, p)
|
|
||||||
if err := saveConfig(cfg); err != nil {
|
|
||||||
dialog.ShowError(err, win)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
restartWatch()
|
|
||||||
rebuildTray()
|
|
||||||
win.Hide()
|
|
||||||
}, win)
|
|
||||||
}
|
|
||||||
|
|
||||||
// t98:立刻同步——寫訊號檔觸發 collector 立即跑一輪,無需等下一個定時週期。
|
// t98:立刻同步——寫訊號檔觸發 collector 立即跑一輪,無需等下一個定時週期。
|
||||||
// syncNowItem 宣告在 rebuildTray 外,確保 click handler 的 goroutine 能還原標籤後重建選單。
|
// syncNowItem 宣告在 rebuildTray 外,確保 click handler 的 goroutine 能還原標籤後重建選單。
|
||||||
var syncNowItem *fyne.MenuItem
|
var syncNowItem *fyne.MenuItem
|
||||||
@@ -719,7 +674,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
it.ChildMenu = fyne.NewMenu("",
|
it.ChildMenu = fyne.NewMenu("",
|
||||||
fyne.NewMenuItem("停止看守這個資料夾", func() {
|
fyne.NewMenuItem("刪除這個知識庫", func() {
|
||||||
removeWatchFolder(cfg, folder)
|
removeWatchFolder(cfg, folder)
|
||||||
if err := saveConfig(cfg); err != nil {
|
if err := saveConfig(cfg); err != nil {
|
||||||
dialog.ShowError(err, win)
|
dialog.ShowError(err, win)
|
||||||
@@ -733,7 +688,6 @@ func main() {
|
|||||||
}
|
}
|
||||||
items = append(items,
|
items = append(items,
|
||||||
fyne.NewMenuItem("+ 新增知識資料夾…", addAction),
|
fyne.NewMenuItem("+ 新增知識資料夾…", addAction),
|
||||||
fyne.NewMenuItem("+ 建立新資料夾並看守…", newFolderAction), // t17
|
|
||||||
fyne.NewMenuItem("🔗 連上知識庫…(換帳號/重新連線)", func() { showConnectWizard() }), // t54
|
fyne.NewMenuItem("🔗 連上知識庫…(換帳號/重新連線)", func() { showConnectWizard() }), // t54
|
||||||
fyne.NewMenuItemSeparator(),
|
fyne.NewMenuItemSeparator(),
|
||||||
syncNowItem, // t98:立刻同步
|
syncNowItem, // t98:立刻同步
|
||||||
@@ -774,7 +728,6 @@ func main() {
|
|||||||
widget.NewLabelWithStyle("Arcrun RAG 知識同步", fyne.TextAlignCenter, fyne.TextStyle{Bold: true}),
|
widget.NewLabelWithStyle("Arcrun RAG 知識同步", fyne.TextAlignCenter, fyne.TextStyle{Bold: true}),
|
||||||
widget.NewLabel("把檔案丟進你選的資料夾,就會自動進你的知識庫。\n這個程式會待在選單列/系統匣,關掉視窗它仍在背景看守。"),
|
widget.NewLabel("把檔案丟進你選的資料夾,就會自動進你的知識庫。\n這個程式會待在選單列/系統匣,關掉視窗它仍在背景看守。"),
|
||||||
widget.NewButton("新增知識資料夾…", func() { addAction() }),
|
widget.NewButton("新增知識資料夾…", func() { addAction() }),
|
||||||
widget.NewButton("建立新資料夾並看守…", func() { newFolderAction() }), // t17
|
|
||||||
))
|
))
|
||||||
|
|
||||||
// 開機即看守(若已連上知識庫)
|
// 開機即看守(若已連上知識庫)
|
||||||
|
|||||||
@@ -246,6 +246,44 @@ func TestSyncNowSignalPath(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── t101:removeWatchFolder 刪除邏輯 ─────────────────────────────────────────
|
||||||
|
|
||||||
|
// TestRemoveWatchFolder_removesEntry:移除後 WatchFolders 不含該路徑,WatchFolder 同步更新。
|
||||||
|
func TestRemoveWatchFolder_removesEntry(t *testing.T) {
|
||||||
|
cfg := &directConfig{
|
||||||
|
WatchFolder: "/a",
|
||||||
|
WatchFolders: []string{"/a", "/b", "/c"},
|
||||||
|
}
|
||||||
|
removeWatchFolder(cfg, "/b")
|
||||||
|
for _, f := range cfg.WatchFolders {
|
||||||
|
if f == "/b" {
|
||||||
|
t.Errorf("移除後 WatchFolders 仍含 /b:%v", cfg.WatchFolders)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if cfg.WatchFolder == "/b" {
|
||||||
|
t.Errorf("移除後 WatchFolder 不應是 /b")
|
||||||
|
}
|
||||||
|
// 移除中間項不影響剩餘項
|
||||||
|
if len(cfg.WatchFolders) != 2 {
|
||||||
|
t.Errorf("移除一項後應剩 2 項,got %d:%v", len(cfg.WatchFolders), cfg.WatchFolders)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestRemoveWatchFolder_idempotent:對不在清單中的路徑呼叫,清單保持不變(冪等)。
|
||||||
|
func TestRemoveWatchFolder_idempotent(t *testing.T) {
|
||||||
|
cfg := &directConfig{
|
||||||
|
WatchFolder: "/a",
|
||||||
|
WatchFolders: []string{"/a", "/b"},
|
||||||
|
}
|
||||||
|
removeWatchFolder(cfg, "/nonexistent")
|
||||||
|
if len(cfg.WatchFolders) != 2 {
|
||||||
|
t.Errorf("重複刪除不存在路徑後清單應不變,got %v", cfg.WatchFolders)
|
||||||
|
}
|
||||||
|
if cfg.WatchFolder != "/a" {
|
||||||
|
t.Errorf("WatchFolder 不應改變,got %q", cfg.WatchFolder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestDirectConfigPreservesClaudeBin:config JSON round-trip 保留 claude_bin 欄位(t92 回寫驗收)。
|
// TestDirectConfigPreservesClaudeBin:config JSON round-trip 保留 claude_bin 欄位(t92 回寫驗收)。
|
||||||
func TestDirectConfigPreservesClaudeBin(t *testing.T) {
|
func TestDirectConfigPreservesClaudeBin(t *testing.T) {
|
||||||
c := &directConfig{
|
c := &directConfig{
|
||||||
|
|||||||
Reference in New Issue
Block a user