arkaos 3.60.0 → 3.61.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/VERSION +1 -1
- package/core/__pycache__/favorites.cpython-313.pyc +0 -0
- package/core/favorites.py +33 -0
- package/dashboard/app/composables/useFavorites.ts +30 -1
- package/dashboard/app/pages/agents/index.vue +32 -0
- package/dashboard/app/pages/personas/index.vue +32 -0
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/scripts/__pycache__/dashboard-api.cpython-313.pyc +0 -0
- package/scripts/dashboard-api.py +16 -0
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.
|
|
1
|
+
3.61.0
|
|
Binary file
|
package/core/favorites.py
CHANGED
|
@@ -89,3 +89,36 @@ def set_favorite(kind: str, item_id: str, favorited: bool) -> dict:
|
|
|
89
89
|
state[kind] = bucket
|
|
90
90
|
_save(state)
|
|
91
91
|
return {"kind": kind, "id": item_id, "favorited": favorited}
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def set_many(kind: str, ids: list[str], favorited: bool) -> dict:
|
|
95
|
+
"""PR97c v3.61.0 — bulk-set favourite state for many ids.
|
|
96
|
+
|
|
97
|
+
Returns ``{kind, favorited, applied: N, total: N}`` where applied
|
|
98
|
+
counts how many ids actually changed state.
|
|
99
|
+
"""
|
|
100
|
+
if kind not in _VALID_KINDS:
|
|
101
|
+
return {"error": f"unknown kind: {kind!r}", "applied": 0, "total": 0}
|
|
102
|
+
if not isinstance(ids, list):
|
|
103
|
+
return {"error": "ids must be a list", "applied": 0, "total": 0}
|
|
104
|
+
state = _load()
|
|
105
|
+
bucket = state.setdefault(kind, [])
|
|
106
|
+
existing = set(bucket)
|
|
107
|
+
applied = 0
|
|
108
|
+
for item_id in ids:
|
|
109
|
+
if not isinstance(item_id, str) or not item_id:
|
|
110
|
+
continue
|
|
111
|
+
if favorited and item_id not in existing:
|
|
112
|
+
existing.add(item_id)
|
|
113
|
+
applied += 1
|
|
114
|
+
elif not favorited and item_id in existing:
|
|
115
|
+
existing.discard(item_id)
|
|
116
|
+
applied += 1
|
|
117
|
+
state[kind] = list(existing)
|
|
118
|
+
_save(state)
|
|
119
|
+
return {
|
|
120
|
+
"kind": kind,
|
|
121
|
+
"favorited": favorited,
|
|
122
|
+
"applied": applied,
|
|
123
|
+
"total": len([i for i in ids if isinstance(i, str) and i]),
|
|
124
|
+
}
|
|
@@ -43,6 +43,35 @@ const _useFavorites = () => {
|
|
|
43
43
|
return state.value.personas.includes(id)
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
// PR97c v3.61.0 — bulk set N ids in one POST.
|
|
47
|
+
async function setMany(
|
|
48
|
+
kind: 'agents' | 'personas',
|
|
49
|
+
ids: string[],
|
|
50
|
+
favorited: boolean,
|
|
51
|
+
) {
|
|
52
|
+
try {
|
|
53
|
+
const res = await $fetch<{
|
|
54
|
+
applied?: number
|
|
55
|
+
total?: number
|
|
56
|
+
error?: string
|
|
57
|
+
}>(`${apiBase}/api/favorites/bulk`, {
|
|
58
|
+
method: 'POST',
|
|
59
|
+
body: { kind, ids, favorited },
|
|
60
|
+
})
|
|
61
|
+
if (res.error) throw new Error(res.error)
|
|
62
|
+
// Sync local state with the new server truth.
|
|
63
|
+
await load(true)
|
|
64
|
+
return res.applied ?? 0
|
|
65
|
+
} catch (err) {
|
|
66
|
+
toast.add({
|
|
67
|
+
title: 'Favorites bulk update failed',
|
|
68
|
+
description: err instanceof Error ? err.message : 'unknown error',
|
|
69
|
+
color: 'error',
|
|
70
|
+
})
|
|
71
|
+
return null
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
46
75
|
async function toggle(kind: 'agents' | 'personas', id: string) {
|
|
47
76
|
try {
|
|
48
77
|
const res = await $fetch<{ favorited?: boolean, error?: string }>(
|
|
@@ -67,7 +96,7 @@ const _useFavorites = () => {
|
|
|
67
96
|
}
|
|
68
97
|
}
|
|
69
98
|
|
|
70
|
-
return { state, load, isAgentFavorite, isPersonaFavorite, toggle, loaded }
|
|
99
|
+
return { state, load, isAgentFavorite, isPersonaFavorite, toggle, setMany, loaded }
|
|
71
100
|
}
|
|
72
101
|
|
|
73
102
|
export const useFavorites = createSharedComposable(_useFavorites)
|
|
@@ -416,6 +416,22 @@ async function bulkDelete() {
|
|
|
416
416
|
await refreshAll()
|
|
417
417
|
}
|
|
418
418
|
|
|
419
|
+
// PR97c v3.61.0 — bulk star / unstar selected agents.
|
|
420
|
+
async function bulkStar(favorited: boolean) {
|
|
421
|
+
if (selected.value.size === 0) return
|
|
422
|
+
const ids = Array.from(selected.value)
|
|
423
|
+
const applied = await favs.setMany('agents', ids, favorited)
|
|
424
|
+
if (applied === null) return
|
|
425
|
+
toast.add({
|
|
426
|
+
title: favorited
|
|
427
|
+
? `Starred ${applied} agent${applied === 1 ? '' : 's'}`
|
|
428
|
+
: `Unstarred ${applied} agent${applied === 1 ? '' : 's'}`,
|
|
429
|
+
description: applied < ids.length ? `${ids.length - applied} already in state` : undefined,
|
|
430
|
+
color: 'success',
|
|
431
|
+
icon: favorited ? 'i-lucide-star' : 'i-lucide-star-off',
|
|
432
|
+
})
|
|
433
|
+
}
|
|
434
|
+
|
|
419
435
|
async function undoTrashIds(ids: string[]) {
|
|
420
436
|
const results = await Promise.allSettled(
|
|
421
437
|
ids.map((tid) =>
|
|
@@ -636,6 +652,22 @@ async function undoTrashIds(ids: string[]) {
|
|
|
636
652
|
@click="clearSelection"
|
|
637
653
|
/>
|
|
638
654
|
<div class="h-5 w-px bg-default" />
|
|
655
|
+
<UButton
|
|
656
|
+
icon="i-lucide-star"
|
|
657
|
+
size="sm"
|
|
658
|
+
variant="soft"
|
|
659
|
+
color="warning"
|
|
660
|
+
aria-label="Star selected"
|
|
661
|
+
@click="bulkStar(true)"
|
|
662
|
+
/>
|
|
663
|
+
<UButton
|
|
664
|
+
icon="i-lucide-star-off"
|
|
665
|
+
size="sm"
|
|
666
|
+
variant="ghost"
|
|
667
|
+
color="neutral"
|
|
668
|
+
aria-label="Unstar selected"
|
|
669
|
+
@click="bulkStar(false)"
|
|
670
|
+
/>
|
|
639
671
|
<UButton
|
|
640
672
|
label="Compare"
|
|
641
673
|
icon="i-lucide-columns-2"
|
|
@@ -418,6 +418,22 @@ async function bulkDelete() {
|
|
|
418
418
|
await refreshAll()
|
|
419
419
|
}
|
|
420
420
|
|
|
421
|
+
// PR97c v3.61.0 — bulk star / unstar selected personas.
|
|
422
|
+
async function bulkStar(favorited: boolean) {
|
|
423
|
+
if (selected.value.size === 0) return
|
|
424
|
+
const ids = Array.from(selected.value)
|
|
425
|
+
const applied = await favs.setMany('personas', ids, favorited)
|
|
426
|
+
if (applied === null) return
|
|
427
|
+
toast.add({
|
|
428
|
+
title: favorited
|
|
429
|
+
? `Starred ${applied} persona${applied === 1 ? '' : 's'}`
|
|
430
|
+
: `Unstarred ${applied} persona${applied === 1 ? '' : 's'}`,
|
|
431
|
+
description: applied < ids.length ? `${ids.length - applied} already in state` : undefined,
|
|
432
|
+
color: 'success',
|
|
433
|
+
icon: favorited ? 'i-lucide-star' : 'i-lucide-star-off',
|
|
434
|
+
})
|
|
435
|
+
}
|
|
436
|
+
|
|
421
437
|
async function undoTrashIds(ids: string[]) {
|
|
422
438
|
const results = await Promise.allSettled(
|
|
423
439
|
ids.map((tid) =>
|
|
@@ -721,6 +737,22 @@ async function undoTrashIds(ids: string[]) {
|
|
|
721
737
|
@click="clearSelection"
|
|
722
738
|
/>
|
|
723
739
|
<div class="h-5 w-px bg-default" />
|
|
740
|
+
<UButton
|
|
741
|
+
icon="i-lucide-star"
|
|
742
|
+
size="sm"
|
|
743
|
+
variant="soft"
|
|
744
|
+
color="warning"
|
|
745
|
+
aria-label="Star selected"
|
|
746
|
+
@click="bulkStar(true)"
|
|
747
|
+
/>
|
|
748
|
+
<UButton
|
|
749
|
+
icon="i-lucide-star-off"
|
|
750
|
+
size="sm"
|
|
751
|
+
variant="ghost"
|
|
752
|
+
color="neutral"
|
|
753
|
+
aria-label="Unstar selected"
|
|
754
|
+
@click="bulkStar(false)"
|
|
755
|
+
/>
|
|
724
756
|
<UButton
|
|
725
757
|
label="Export ZIP"
|
|
726
758
|
icon="i-lucide-archive"
|
package/package.json
CHANGED
package/pyproject.toml
CHANGED
|
Binary file
|
package/scripts/dashboard-api.py
CHANGED
|
@@ -2607,6 +2607,22 @@ def favorites_toggle(kind: str, item_id: str):
|
|
|
2607
2607
|
return _fav.toggle(kind, item_id)
|
|
2608
2608
|
|
|
2609
2609
|
|
|
2610
|
+
@app.post("/api/favorites/bulk")
|
|
2611
|
+
def favorites_bulk(body: dict):
|
|
2612
|
+
"""PR97c v3.61.0 — bulk star/unstar many ids in one shot.
|
|
2613
|
+
|
|
2614
|
+
Body: ``{"kind": "agents"|"personas", "ids": [...], "favorited": bool}``
|
|
2615
|
+
Returns ``{kind, favorited, applied, total}``.
|
|
2616
|
+
"""
|
|
2617
|
+
if not isinstance(body, dict):
|
|
2618
|
+
return {"error": "body must be an object"}
|
|
2619
|
+
kind = (body.get("kind") or "").strip()
|
|
2620
|
+
ids = body.get("ids") or []
|
|
2621
|
+
favorited = bool(body.get("favorited"))
|
|
2622
|
+
from core import favorites as _fav
|
|
2623
|
+
return _fav.set_many(kind, ids if isinstance(ids, list) else [], favorited)
|
|
2624
|
+
|
|
2625
|
+
|
|
2610
2626
|
# --- Global search (PR85d v3.14.0) ---
|
|
2611
2627
|
|
|
2612
2628
|
@app.get("/api/search")
|