arkaos 3.40.0 → 3.42.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/OnboardingTour.vue +165 -0
- package/dashboard/app/composables/useThemeColor.ts +53 -0
- package/dashboard/app/layouts/default.vue +5 -0
- package/dashboard/app/pages/settings.vue +36 -0
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/scripts/__pycache__/dashboard-api.cpython-313.pyc +0 -0
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.
|
|
1
|
+
3.42.0
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// PR92c v3.41.0 — first-visit onboarding tour.
|
|
3
|
+
//
|
|
4
|
+
// One-shot modal that shows up on the home page until the operator
|
|
5
|
+
// dismisses it. Walks through Agents → Personas → Workflows → Budget.
|
|
6
|
+
// Dismissal persists in localStorage as `arkaos_onboarding_dismissed`.
|
|
7
|
+
|
|
8
|
+
const open = ref(false)
|
|
9
|
+
const step = ref(0)
|
|
10
|
+
const router = useRouter()
|
|
11
|
+
const route = useRoute()
|
|
12
|
+
|
|
13
|
+
const STORAGE_KEY = 'arkaos_onboarding_dismissed'
|
|
14
|
+
|
|
15
|
+
interface TourStep {
|
|
16
|
+
icon: string
|
|
17
|
+
title: string
|
|
18
|
+
body: string
|
|
19
|
+
cta?: { label: string, to: string }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const steps: TourStep[] = [
|
|
23
|
+
{
|
|
24
|
+
icon: 'i-lucide-sparkles',
|
|
25
|
+
title: 'Welcome to ArkaOS',
|
|
26
|
+
body: 'A 4-minute tour of where to start. Press Esc anytime to skip — you can replay this from Settings.',
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
icon: 'i-lucide-users',
|
|
30
|
+
title: 'Agents',
|
|
31
|
+
body: 'Your specialist team. Browse the table, click a row for full detail (DNA, expertise, history). Create new ones with the AI draft on /agents/new.',
|
|
32
|
+
cta: { label: 'Open /agents', to: '/agents' },
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
icon: 'i-lucide-user-plus',
|
|
36
|
+
title: 'Personas',
|
|
37
|
+
body: 'Behavioural profiles of real people (or archetypes) that seed your agents. Import from .md files / URLs, export as ZIP, clone to agents.',
|
|
38
|
+
cta: { label: 'Open /personas', to: '/personas' },
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
icon: 'i-lucide-workflow',
|
|
42
|
+
title: 'Workflows',
|
|
43
|
+
body: 'YAML-defined orchestrations under departments/. Click a workflow for its phase flow, raw YAML, and recent runs.',
|
|
44
|
+
cta: { label: 'Open /workflows', to: '/workflows' },
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
icon: 'i-lucide-wallet',
|
|
48
|
+
title: 'Budget',
|
|
49
|
+
body: 'Real LLM spend by provider/model/category, daily trend chart (7/14/30d), CSV export. Powered by PR47 telemetry.',
|
|
50
|
+
cta: { label: 'Open /budget', to: '/budget' },
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
icon: 'i-lucide-keyboard',
|
|
54
|
+
title: 'Power shortcuts',
|
|
55
|
+
body: 'Press ? anywhere for the full keymap. Try / for search, g a for agents, g p for personas.',
|
|
56
|
+
},
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
onMounted(() => {
|
|
60
|
+
if (typeof window === 'undefined') return
|
|
61
|
+
if (window.localStorage.getItem(STORAGE_KEY) === '1') return
|
|
62
|
+
// Only auto-open on the home route — operators clicking around shouldn't
|
|
63
|
+
// be interrupted.
|
|
64
|
+
if (route.path !== '/') return
|
|
65
|
+
open.value = true
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
function next() {
|
|
69
|
+
if (step.value < steps.length - 1) {
|
|
70
|
+
step.value += 1
|
|
71
|
+
} else {
|
|
72
|
+
dismiss()
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function back() {
|
|
77
|
+
if (step.value > 0) step.value -= 1
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function dismiss() {
|
|
81
|
+
open.value = false
|
|
82
|
+
if (typeof window !== 'undefined') {
|
|
83
|
+
window.localStorage.setItem(STORAGE_KEY, '1')
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function navigateAndDismiss(to: string) {
|
|
88
|
+
dismiss()
|
|
89
|
+
router.push(to)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const current = computed(() => steps[step.value])
|
|
93
|
+
const progress = computed(() => Math.round(((step.value + 1) / steps.length) * 100))
|
|
94
|
+
</script>
|
|
95
|
+
|
|
96
|
+
<template>
|
|
97
|
+
<UModal
|
|
98
|
+
v-model:open="open"
|
|
99
|
+
:ui="{ content: 'max-w-lg' }"
|
|
100
|
+
title="Welcome"
|
|
101
|
+
>
|
|
102
|
+
<template #content>
|
|
103
|
+
<UCard>
|
|
104
|
+
<template #header>
|
|
105
|
+
<div class="flex items-center justify-between gap-3">
|
|
106
|
+
<div class="flex items-center gap-3">
|
|
107
|
+
<UIcon :name="current.icon" class="size-6 text-primary" />
|
|
108
|
+
<div>
|
|
109
|
+
<h2 class="text-lg font-bold">{{ current.title }}</h2>
|
|
110
|
+
<p class="text-xs text-muted mt-0.5">
|
|
111
|
+
Step {{ step + 1 }} of {{ steps.length }}
|
|
112
|
+
</p>
|
|
113
|
+
</div>
|
|
114
|
+
</div>
|
|
115
|
+
<UButton icon="i-lucide-x" variant="ghost" size="sm" aria-label="Skip" @click="dismiss" />
|
|
116
|
+
</div>
|
|
117
|
+
</template>
|
|
118
|
+
|
|
119
|
+
<div class="space-y-4">
|
|
120
|
+
<p class="text-sm">{{ current.body }}</p>
|
|
121
|
+
<UButton
|
|
122
|
+
v-if="current.cta"
|
|
123
|
+
:label="current.cta.label"
|
|
124
|
+
:icon="current.icon"
|
|
125
|
+
variant="soft"
|
|
126
|
+
color="primary"
|
|
127
|
+
@click="navigateAndDismiss(current.cta!.to)"
|
|
128
|
+
/>
|
|
129
|
+
<div class="h-1.5 rounded-full bg-elevated/40 overflow-hidden">
|
|
130
|
+
<div
|
|
131
|
+
class="h-1.5 rounded-full bg-primary transition-all duration-200"
|
|
132
|
+
:style="{ width: `${progress}%` }"
|
|
133
|
+
/>
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
|
|
137
|
+
<template #footer>
|
|
138
|
+
<div class="flex items-center justify-between gap-2 text-xs">
|
|
139
|
+
<UButton
|
|
140
|
+
label="Don't show again"
|
|
141
|
+
variant="ghost"
|
|
142
|
+
size="xs"
|
|
143
|
+
@click="dismiss"
|
|
144
|
+
/>
|
|
145
|
+
<div class="flex items-center gap-2">
|
|
146
|
+
<UButton
|
|
147
|
+
v-if="step > 0"
|
|
148
|
+
label="Back"
|
|
149
|
+
variant="ghost"
|
|
150
|
+
size="sm"
|
|
151
|
+
@click="back"
|
|
152
|
+
/>
|
|
153
|
+
<UButton
|
|
154
|
+
:label="step === steps.length - 1 ? 'Finish' : 'Next'"
|
|
155
|
+
icon="i-lucide-arrow-right"
|
|
156
|
+
size="sm"
|
|
157
|
+
@click="next"
|
|
158
|
+
/>
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
</template>
|
|
162
|
+
</UCard>
|
|
163
|
+
</template>
|
|
164
|
+
</UModal>
|
|
165
|
+
</template>
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// PR92d v3.42.0 — runtime theme color picker.
|
|
2
|
+
//
|
|
3
|
+
// Stores the operator's preferred primary color in localStorage and
|
|
4
|
+
// applies it to the live Nuxt UI app config so every component picks
|
|
5
|
+
// up the new hue on next render.
|
|
6
|
+
|
|
7
|
+
import { createSharedComposable } from '@vueuse/core'
|
|
8
|
+
|
|
9
|
+
const STORAGE_KEY = 'arkaos_theme_color'
|
|
10
|
+
|
|
11
|
+
export const THEME_COLOR_OPTIONS = [
|
|
12
|
+
{ label: 'Emerald (default)', value: 'emerald' },
|
|
13
|
+
{ label: 'Blue', value: 'blue' },
|
|
14
|
+
{ label: 'Indigo', value: 'indigo' },
|
|
15
|
+
{ label: 'Violet', value: 'violet' },
|
|
16
|
+
{ label: 'Rose', value: 'rose' },
|
|
17
|
+
{ label: 'Amber', value: 'amber' },
|
|
18
|
+
{ label: 'Teal', value: 'teal' },
|
|
19
|
+
{ label: 'Cyan', value: 'cyan' },
|
|
20
|
+
] as const
|
|
21
|
+
|
|
22
|
+
export type ThemeColor = (typeof THEME_COLOR_OPTIONS)[number]['value']
|
|
23
|
+
|
|
24
|
+
const _useThemeColor = () => {
|
|
25
|
+
const appConfig = useAppConfig()
|
|
26
|
+
const current = ref<ThemeColor>('emerald')
|
|
27
|
+
|
|
28
|
+
function apply(color: ThemeColor) {
|
|
29
|
+
current.value = color
|
|
30
|
+
if (appConfig.ui?.colors) {
|
|
31
|
+
;(appConfig.ui.colors as any).primary = color
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function setAndPersist(color: ThemeColor) {
|
|
36
|
+
apply(color)
|
|
37
|
+
if (typeof window !== 'undefined') {
|
|
38
|
+
window.localStorage.setItem(STORAGE_KEY, color)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function loadFromStorage() {
|
|
43
|
+
if (typeof window === 'undefined') return
|
|
44
|
+
const saved = window.localStorage.getItem(STORAGE_KEY) as ThemeColor | null
|
|
45
|
+
if (saved && THEME_COLOR_OPTIONS.some((o) => o.value === saved)) {
|
|
46
|
+
apply(saved)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return { current, apply, setAndPersist, loadFromStorage }
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export const useThemeColor = createSharedComposable(_useThemeColor)
|
|
@@ -4,6 +4,10 @@ import type { NavigationMenuItem } from '@nuxt/ui'
|
|
|
4
4
|
// PR85c v3.13.0 — registers keyboard shortcuts globally.
|
|
5
5
|
useDashboard()
|
|
6
6
|
|
|
7
|
+
// PR92d v3.42.0 — apply the operator's chosen primary color on boot.
|
|
8
|
+
const theme = useThemeColor()
|
|
9
|
+
onMounted(() => theme.loadFromStorage())
|
|
10
|
+
|
|
7
11
|
const open = ref(false)
|
|
8
12
|
|
|
9
13
|
const links = [[{
|
|
@@ -158,5 +162,6 @@ const links = [[{
|
|
|
158
162
|
<slot />
|
|
159
163
|
<KeyboardShortcutsHelp />
|
|
160
164
|
<GlobalSearch />
|
|
165
|
+
<OnboardingTour />
|
|
161
166
|
</UDashboardGroup>
|
|
162
167
|
</template>
|
|
@@ -234,6 +234,11 @@ const themeOptions = [
|
|
|
234
234
|
{ label: 'Dark', value: 'dark' },
|
|
235
235
|
]
|
|
236
236
|
|
|
237
|
+
// PR92d v3.42.0 — primary color picker.
|
|
238
|
+
import { THEME_COLOR_OPTIONS, useThemeColor } from '~/composables/useThemeColor'
|
|
239
|
+
const themeColor = useThemeColor()
|
|
240
|
+
const themeColorOptions = THEME_COLOR_OPTIONS
|
|
241
|
+
|
|
237
242
|
function transportColor(transport: string): 'primary' | 'warning' | 'success' | 'neutral' {
|
|
238
243
|
if (transport === 'stdio') return 'primary'
|
|
239
244
|
if (transport === 'http' || transport === 'sse') return 'success'
|
|
@@ -750,6 +755,37 @@ const activeSection = ref<SectionId>('profile')
|
|
|
750
755
|
Currently rendering as
|
|
751
756
|
<UBadge :label="colorMode.value" variant="subtle" size="xs" />
|
|
752
757
|
</p>
|
|
758
|
+
|
|
759
|
+
<!-- PR92d v3.42.0 — primary color picker -->
|
|
760
|
+
<UFormField label="Primary color" help="Tints buttons, badges, links across the dashboard.">
|
|
761
|
+
<div class="flex flex-wrap gap-2">
|
|
762
|
+
<button
|
|
763
|
+
v-for="opt in themeColorOptions"
|
|
764
|
+
:key="opt.value"
|
|
765
|
+
type="button"
|
|
766
|
+
class="rounded-lg border p-2 transition-colors text-xs flex items-center gap-2"
|
|
767
|
+
:class="themeColor.current.value === opt.value
|
|
768
|
+
? 'border-primary bg-primary/10 font-semibold'
|
|
769
|
+
: 'border-default hover:border-primary/40'"
|
|
770
|
+
@click="themeColor.setAndPersist(opt.value)"
|
|
771
|
+
>
|
|
772
|
+
<span
|
|
773
|
+
class="size-4 rounded-full"
|
|
774
|
+
:class="{
|
|
775
|
+
'bg-emerald-500': opt.value === 'emerald',
|
|
776
|
+
'bg-blue-500': opt.value === 'blue',
|
|
777
|
+
'bg-indigo-500': opt.value === 'indigo',
|
|
778
|
+
'bg-violet-500': opt.value === 'violet',
|
|
779
|
+
'bg-rose-500': opt.value === 'rose',
|
|
780
|
+
'bg-amber-500': opt.value === 'amber',
|
|
781
|
+
'bg-teal-500': opt.value === 'teal',
|
|
782
|
+
'bg-cyan-500': opt.value === 'cyan',
|
|
783
|
+
}"
|
|
784
|
+
/>
|
|
785
|
+
{{ opt.label }}
|
|
786
|
+
</button>
|
|
787
|
+
</div>
|
|
788
|
+
</UFormField>
|
|
753
789
|
</div>
|
|
754
790
|
</UCard>
|
|
755
791
|
</section>
|
package/package.json
CHANGED
package/pyproject.toml
CHANGED
|
Binary file
|