arkaos 3.46.0 → 3.48.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/dashboard/app/components/NotificationsBell.vue +32 -11
- package/dashboard/app/components/PersonaWizard.vue +10 -0
- package/dashboard/app/composables/useActivityFeed.ts +33 -4
- package/dashboard/app/pages/personas/archetypes.vue +106 -0
- package/dashboard/app/pages/personas/index.vue +7 -0
- package/package.json +1 -1
- package/pyproject.toml +1 -1
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.
|
|
1
|
+
3.48.0
|
|
@@ -54,20 +54,30 @@ function kindColor(kind: string): string {
|
|
|
54
54
|
</UButton>
|
|
55
55
|
|
|
56
56
|
<template #content>
|
|
57
|
-
<div class="p-2 border-b border-default flex items-center justify-between">
|
|
57
|
+
<div class="p-2 border-b border-default flex items-center justify-between gap-2">
|
|
58
58
|
<div>
|
|
59
59
|
<p class="text-sm font-semibold">Recent activity</p>
|
|
60
60
|
<p class="text-xs text-muted">
|
|
61
|
-
|
|
61
|
+
{{ feed.unreadCount.value }} unread ·
|
|
62
|
+
{{ feed.events.value.length }} total
|
|
62
63
|
</p>
|
|
63
64
|
</div>
|
|
64
|
-
<
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
65
|
+
<div class="flex items-center gap-1">
|
|
66
|
+
<UButton
|
|
67
|
+
v-if="feed.unreadCount.value > 0"
|
|
68
|
+
label="Mark all read"
|
|
69
|
+
variant="ghost"
|
|
70
|
+
size="xs"
|
|
71
|
+
@click="feed.markAllRead()"
|
|
72
|
+
/>
|
|
73
|
+
<UButton
|
|
74
|
+
v-if="feed.events.value.length > 0"
|
|
75
|
+
label="Clear"
|
|
76
|
+
variant="ghost"
|
|
77
|
+
size="xs"
|
|
78
|
+
@click="feed.clear()"
|
|
79
|
+
/>
|
|
80
|
+
</div>
|
|
71
81
|
</div>
|
|
72
82
|
<div class="max-h-80 overflow-y-auto">
|
|
73
83
|
<p
|
|
@@ -81,7 +91,9 @@ function kindColor(kind: string): string {
|
|
|
81
91
|
<li
|
|
82
92
|
v-for="ev in feed.events.value"
|
|
83
93
|
:key="ev.id"
|
|
84
|
-
class="px-3 py-2 hover:bg-elevated/40 transition-colors group"
|
|
94
|
+
class="px-3 py-2 hover:bg-elevated/40 transition-colors group cursor-pointer"
|
|
95
|
+
:class="{ 'bg-primary/5': !ev.read }"
|
|
96
|
+
@click="feed.markRead(ev.id)"
|
|
85
97
|
>
|
|
86
98
|
<component
|
|
87
99
|
:is="ev.to ? 'NuxtLink' : 'div'"
|
|
@@ -93,7 +105,16 @@ function kindColor(kind: string): string {
|
|
|
93
105
|
:class="['size-4 shrink-0 mt-0.5', kindColor(ev.kind)]"
|
|
94
106
|
/>
|
|
95
107
|
<div class="flex-1 min-w-0">
|
|
96
|
-
<p class="text-sm
|
|
108
|
+
<p class="text-sm flex items-center gap-1.5">
|
|
109
|
+
<span
|
|
110
|
+
v-if="!ev.read"
|
|
111
|
+
class="inline-block size-1.5 rounded-full bg-primary shrink-0"
|
|
112
|
+
aria-label="unread"
|
|
113
|
+
/>
|
|
114
|
+
<span :class="ev.read ? 'font-normal text-muted' : 'font-medium'" class="truncate">
|
|
115
|
+
{{ ev.title }}
|
|
116
|
+
</span>
|
|
117
|
+
</p>
|
|
97
118
|
<p v-if="ev.description" class="text-xs text-muted truncate">
|
|
98
119
|
{{ ev.description }}
|
|
99
120
|
</p>
|
|
@@ -63,6 +63,16 @@ function applyArchetype(arch: Archetype) {
|
|
|
63
63
|
mode.value = 'description'
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
// PR94b v3.48.0 — auto-apply archetype from query string (deep link
|
|
67
|
+
// from the /personas/archetypes catalog).
|
|
68
|
+
const route = useRoute()
|
|
69
|
+
watch(archetypes, (list) => {
|
|
70
|
+
const slug = String(route.query.archetype ?? '')
|
|
71
|
+
if (!slug || list.length === 0) return
|
|
72
|
+
const match = list.find((a) => a.id === slug)
|
|
73
|
+
if (match) applyArchetype(match)
|
|
74
|
+
}, { immediate: true })
|
|
75
|
+
|
|
66
76
|
// ─── Step 2 state ────────────────────────────────────────────────────────
|
|
67
77
|
const ingestJobs = ref<Array<{
|
|
68
78
|
source: string
|
|
@@ -13,6 +13,7 @@ export interface ActivityEvent {
|
|
|
13
13
|
title: string
|
|
14
14
|
description?: string
|
|
15
15
|
to?: string
|
|
16
|
+
read?: boolean
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
const STORAGE_KEY = 'arkaos_activity_feed'
|
|
@@ -21,7 +22,10 @@ const MAX_EVENTS = 50
|
|
|
21
22
|
const _useActivityFeed = () => {
|
|
22
23
|
const events = useState<ActivityEvent[]>('activityFeed', () => [])
|
|
23
24
|
const loaded = useState<boolean>('activityFeedLoaded', () => false)
|
|
24
|
-
|
|
25
|
+
// PR94a v3.47.0 — unread count is now derived from `read: false`.
|
|
26
|
+
const unreadCount = computed(
|
|
27
|
+
() => events.value.filter((e) => !e.read).length,
|
|
28
|
+
)
|
|
25
29
|
|
|
26
30
|
function _persist() {
|
|
27
31
|
if (typeof window === 'undefined') return
|
|
@@ -48,14 +52,39 @@ const _useActivityFeed = () => {
|
|
|
48
52
|
loaded.value = true
|
|
49
53
|
}
|
|
50
54
|
|
|
51
|
-
function push(entry: Omit<ActivityEvent, 'id' | 'ts'>) {
|
|
55
|
+
function push(entry: Omit<ActivityEvent, 'id' | 'ts' | 'read'>) {
|
|
52
56
|
load()
|
|
53
57
|
const id = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
|
|
54
58
|
const ts = new Date().toISOString()
|
|
55
|
-
|
|
59
|
+
// PR94a — every new event starts unread.
|
|
60
|
+
events.value = [{ id, ts, read: false, ...entry }, ...events.value].slice(0, MAX_EVENTS)
|
|
56
61
|
_persist()
|
|
57
62
|
}
|
|
58
63
|
|
|
64
|
+
function markRead(id: string) {
|
|
65
|
+
let changed = false
|
|
66
|
+
events.value = events.value.map((e) => {
|
|
67
|
+
if (e.id === id && !e.read) {
|
|
68
|
+
changed = true
|
|
69
|
+
return { ...e, read: true }
|
|
70
|
+
}
|
|
71
|
+
return e
|
|
72
|
+
})
|
|
73
|
+
if (changed) _persist()
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function markAllRead() {
|
|
77
|
+
let changed = false
|
|
78
|
+
events.value = events.value.map((e) => {
|
|
79
|
+
if (!e.read) {
|
|
80
|
+
changed = true
|
|
81
|
+
return { ...e, read: true }
|
|
82
|
+
}
|
|
83
|
+
return e
|
|
84
|
+
})
|
|
85
|
+
if (changed) _persist()
|
|
86
|
+
}
|
|
87
|
+
|
|
59
88
|
function clear() {
|
|
60
89
|
events.value = []
|
|
61
90
|
_persist()
|
|
@@ -66,7 +95,7 @@ const _useActivityFeed = () => {
|
|
|
66
95
|
_persist()
|
|
67
96
|
}
|
|
68
97
|
|
|
69
|
-
return { events, unreadCount, load, push, clear, remove }
|
|
98
|
+
return { events, unreadCount, load, push, clear, remove, markRead, markAllRead }
|
|
70
99
|
}
|
|
71
100
|
|
|
72
101
|
export const useActivityFeed = createSharedComposable(_useActivityFeed)
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// PR94b v3.48.0 — Browseable catalog of persona archetypes.
|
|
3
|
+
//
|
|
4
|
+
// Reads /api/personas/archetypes (PR93b) and renders each as a card.
|
|
5
|
+
// "Create from this" deep-links to /personas/new?archetype=<id> where
|
|
6
|
+
// the wizard auto-selects description mode and pre-fills.
|
|
7
|
+
|
|
8
|
+
interface Archetype {
|
|
9
|
+
id: string
|
|
10
|
+
name: string
|
|
11
|
+
title: string
|
|
12
|
+
tagline: string
|
|
13
|
+
mbti: string
|
|
14
|
+
disc: { primary: string, secondary: string }
|
|
15
|
+
enneagram: { type: number, wing: number }
|
|
16
|
+
description: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const { fetchApi } = useApi()
|
|
20
|
+
const { data, status, error, refresh } = await fetchApi<{
|
|
21
|
+
archetypes: Archetype[]
|
|
22
|
+
total: number
|
|
23
|
+
}>('/api/personas/archetypes')
|
|
24
|
+
|
|
25
|
+
const archetypes = computed<Archetype[]>(() => data.value?.archetypes ?? [])
|
|
26
|
+
|
|
27
|
+
function discColor(letter: string): 'error' | 'warning' | 'success' | 'primary' | 'neutral' {
|
|
28
|
+
const m: Record<string, 'error' | 'warning' | 'success' | 'primary' | 'neutral'> = {
|
|
29
|
+
D: 'error', I: 'warning', S: 'success', C: 'primary',
|
|
30
|
+
}
|
|
31
|
+
return m[letter] ?? 'neutral'
|
|
32
|
+
}
|
|
33
|
+
</script>
|
|
34
|
+
|
|
35
|
+
<template>
|
|
36
|
+
<UDashboardPanel id="archetypes">
|
|
37
|
+
<template #header>
|
|
38
|
+
<UDashboardNavbar title="Persona archetypes">
|
|
39
|
+
<template #leading>
|
|
40
|
+
<UButton icon="i-lucide-arrow-left" variant="ghost" size="sm" to="/personas" aria-label="Back" />
|
|
41
|
+
</template>
|
|
42
|
+
<template #trailing>
|
|
43
|
+
<UBadge v-if="data?.total" :label="String(data.total)" variant="subtle" />
|
|
44
|
+
</template>
|
|
45
|
+
</UDashboardNavbar>
|
|
46
|
+
</template>
|
|
47
|
+
|
|
48
|
+
<template #body>
|
|
49
|
+
<DashboardState
|
|
50
|
+
:status="status"
|
|
51
|
+
:error="error"
|
|
52
|
+
:empty="!archetypes.length"
|
|
53
|
+
empty-title="No archetypes available"
|
|
54
|
+
empty-icon="i-lucide-sparkles"
|
|
55
|
+
loading-label="Loading archetypes"
|
|
56
|
+
:on-retry="() => refresh()"
|
|
57
|
+
>
|
|
58
|
+
<p class="text-sm text-muted mb-6 max-w-2xl">
|
|
59
|
+
Curated starter profiles. Each one ships a description, behavioural DNA
|
|
60
|
+
defaults, and a recommended communication style. Use them as a base
|
|
61
|
+
when you don't have indexed content yet — the wizard pre-fills the
|
|
62
|
+
description and you tweak from there.
|
|
63
|
+
</p>
|
|
64
|
+
|
|
65
|
+
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
66
|
+
<div
|
|
67
|
+
v-for="arch in archetypes"
|
|
68
|
+
:key="arch.id"
|
|
69
|
+
class="rounded-xl border border-default p-5 flex flex-col gap-3 hover:border-primary/40 transition-colors"
|
|
70
|
+
>
|
|
71
|
+
<div>
|
|
72
|
+
<h3 class="text-lg font-bold">{{ arch.name }}</h3>
|
|
73
|
+
<p class="text-xs text-muted">{{ arch.title }}</p>
|
|
74
|
+
</div>
|
|
75
|
+
<p class="text-sm italic text-muted">"{{ arch.tagline }}"</p>
|
|
76
|
+
<div class="flex flex-wrap gap-1.5">
|
|
77
|
+
<UBadge :label="arch.mbti" variant="subtle" size="xs" />
|
|
78
|
+
<UBadge
|
|
79
|
+
:label="`DISC: ${arch.disc.primary}/${arch.disc.secondary}`"
|
|
80
|
+
:color="discColor(arch.disc.primary)"
|
|
81
|
+
variant="subtle"
|
|
82
|
+
size="xs"
|
|
83
|
+
/>
|
|
84
|
+
<UBadge
|
|
85
|
+
:label="`E${arch.enneagram.type}w${arch.enneagram.wing}`"
|
|
86
|
+
variant="outline"
|
|
87
|
+
size="xs"
|
|
88
|
+
/>
|
|
89
|
+
</div>
|
|
90
|
+
<p class="text-sm text-muted line-clamp-3">{{ arch.description }}</p>
|
|
91
|
+
<div class="pt-2 mt-auto">
|
|
92
|
+
<UButton
|
|
93
|
+
label="Create from this"
|
|
94
|
+
icon="i-lucide-sparkles"
|
|
95
|
+
color="primary"
|
|
96
|
+
size="sm"
|
|
97
|
+
block
|
|
98
|
+
:to="`/personas/new?archetype=${arch.id}`"
|
|
99
|
+
/>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
</DashboardState>
|
|
104
|
+
</template>
|
|
105
|
+
</UDashboardPanel>
|
|
106
|
+
</template>
|
|
@@ -423,6 +423,13 @@ async function undoTrashIds(ids: string[]) {
|
|
|
423
423
|
/>
|
|
424
424
|
</template>
|
|
425
425
|
<template #right>
|
|
426
|
+
<UButton
|
|
427
|
+
label="Archetypes"
|
|
428
|
+
icon="i-lucide-sparkles"
|
|
429
|
+
variant="ghost"
|
|
430
|
+
size="sm"
|
|
431
|
+
to="/personas/archetypes"
|
|
432
|
+
/>
|
|
426
433
|
<UButton
|
|
427
434
|
label="Export ZIP"
|
|
428
435
|
icon="i-lucide-archive"
|
package/package.json
CHANGED