arkaos 4.20.0 → 4.22.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/README.md +1 -1
- package/VERSION +1 -1
- package/arka/SKILL.md +2 -1
- package/config/claude-agents/marta-cqo.md +9 -0
- package/config/standards/{claude-md-overlays → stack-rules}/laravel.md +11 -0
- package/config/standards/{claude-md-overlays → stack-rules}/node.md +10 -1
- package/config/standards/{claude-md-overlays → stack-rules}/nuxt.md +12 -1
- package/config/standards/stack-rules/php.md +13 -0
- package/config/standards/{claude-md-overlays → stack-rules}/python.md +5 -0
- package/config/standards/stack-rules/react.md +14 -0
- package/config/standards/stack-rules/vue.md +13 -0
- package/core/cognition/evolve.py +299 -0
- package/core/cognition/evolve_cli.py +55 -0
- package/core/governance/stop_lint.py +266 -0
- package/core/governance/stop_lint_telemetry.py +132 -0
- package/core/governance/tool_loop_check.py +111 -0
- package/core/hooks/stop.py +73 -22
- package/core/runtime/pricing.py +53 -19
- package/core/synapse/layers.py +49 -476
- package/core/synapse/layers_base.py +99 -0
- package/core/synapse/layers_kb.py +395 -0
- package/core/sync/content_syncer.py +79 -10
- package/dashboard/app/components/AgentDependencyGraph.vue +5 -5
- package/dashboard/app/components/AgentEditDrawer.vue +120 -52
- package/dashboard/app/components/AgentSuggestionsCard.vue +4 -2
- package/dashboard/app/components/ConfirmDialog.vue +2 -2
- package/dashboard/app/components/DashboardState.vue +5 -3
- package/dashboard/app/components/GlobalSearch.vue +11 -7
- package/dashboard/app/components/KeyboardShortcutsHelp.vue +8 -6
- package/dashboard/app/components/KnowledgeSourcesList.vue +13 -9
- package/dashboard/app/components/MarkdownEditor.vue +1 -1
- package/dashboard/app/components/NotificationsBell.vue +8 -4
- package/dashboard/app/components/OnboardingTour.vue +24 -11
- package/dashboard/app/components/PersonaCloneDialog.vue +15 -8
- package/dashboard/app/components/PersonaWizard.vue +105 -72
- package/dashboard/app/components/SidebarFavoritesWidget.vue +5 -5
- package/dashboard/app/components/SidebarStatsWidget.vue +3 -1
- package/dashboard/app/components/Terminal.vue +9 -8
- package/dashboard/app/components/TextDiff.vue +27 -15
- package/dashboard/app/composables/useActivityFeed.ts +3 -3
- package/dashboard/app/composables/useApi.ts +4 -2
- package/dashboard/app/composables/useConfirmDialog.ts +1 -1
- package/dashboard/app/composables/useFavorites.ts +8 -8
- package/dashboard/app/composables/useTerminalThemes.ts +13 -13
- package/dashboard/app/composables/useThemeColor.ts +7 -4
- package/dashboard/app/pages/agents/[id].vue +319 -159
- package/dashboard/app/pages/agents/compare.vue +152 -52
- package/dashboard/app/pages/agents/index.vue +68 -55
- package/dashboard/app/pages/agents/new.vue +125 -58
- package/dashboard/app/pages/audit.vue +13 -6
- package/dashboard/app/pages/budget.vue +20 -18
- package/dashboard/app/pages/commands.vue +26 -22
- package/dashboard/app/pages/departments/[dept].vue +67 -31
- package/dashboard/app/pages/departments/compare.vue +85 -27
- package/dashboard/app/pages/departments/index.vue +31 -7
- package/dashboard/app/pages/health.vue +19 -12
- package/dashboard/app/pages/knowledge/index.vue +190 -78
- package/dashboard/app/pages/models.vue +74 -28
- package/dashboard/app/pages/personas/[id].vue +322 -219
- package/dashboard/app/pages/personas/archetypes.vue +20 -6
- package/dashboard/app/pages/personas/compare-with-agent.vue +109 -39
- package/dashboard/app/pages/personas/compare.vue +196 -124
- package/dashboard/app/pages/personas/index.vue +67 -60
- package/dashboard/app/pages/personas/new.vue +1 -1
- package/dashboard/app/pages/settings.vue +1 -0
- package/dashboard/app/pages/tasks.vue +80 -38
- package/dashboard/app/pages/trash.vue +11 -9
- package/dashboard/app/pages/workflows.vue +29 -19
- package/dashboard/nuxt.config.ts +2 -1
- package/dashboard/package.json +5 -0
- package/departments/kb/skills/doc-extraction/SKILL.md +99 -0
- package/departments/kb/skills/doc-redaction/SKILL.md +103 -0
- package/departments/kb/skills/knowledge-ops/SKILL.md +88 -0
- package/departments/ops/skills/github-ops/SKILL.md +89 -0
- package/departments/ops/skills/terminal-ops/SKILL.md +83 -0
- package/installer/index.js +1 -1
- package/knowledge/commands-registry.json +16 -3
- package/knowledge/skills-manifest.json +66 -1
- package/package.json +1 -1
- package/pyproject.toml +1 -1
|
@@ -9,10 +9,10 @@ const { fetchApi } = useApi()
|
|
|
9
9
|
|
|
10
10
|
await favs.load()
|
|
11
11
|
const { data: agentsData } = fetchApi<{ agents: Array<{ id: string, name: string }> }>(
|
|
12
|
-
'/api/agents'
|
|
12
|
+
'/api/agents'
|
|
13
13
|
)
|
|
14
14
|
const { data: personasData } = fetchApi<{ personas: Array<{ id: string, name: string }> }>(
|
|
15
|
-
'/api/personas'
|
|
15
|
+
'/api/personas'
|
|
16
16
|
)
|
|
17
17
|
|
|
18
18
|
interface FavRow { id: string, name: string }
|
|
@@ -20,7 +20,7 @@ interface FavRow { id: string, name: string }
|
|
|
20
20
|
const starredAgents = computed<FavRow[]>(() => {
|
|
21
21
|
const all = agentsData.value?.agents ?? []
|
|
22
22
|
return favs.state.value.agents
|
|
23
|
-
.map(
|
|
23
|
+
.map(id => all.find(a => a.id === id))
|
|
24
24
|
.filter((a): a is FavRow => Boolean(a))
|
|
25
25
|
.slice(0, 4)
|
|
26
26
|
})
|
|
@@ -28,13 +28,13 @@ const starredAgents = computed<FavRow[]>(() => {
|
|
|
28
28
|
const starredPersonas = computed<FavRow[]>(() => {
|
|
29
29
|
const all = personasData.value?.personas ?? []
|
|
30
30
|
return favs.state.value.personas
|
|
31
|
-
.map(
|
|
31
|
+
.map(id => all.find(p => p.id === id))
|
|
32
32
|
.filter((p): p is FavRow => Boolean(p))
|
|
33
33
|
.slice(0, 4)
|
|
34
34
|
})
|
|
35
35
|
|
|
36
36
|
const hasAny = computed(
|
|
37
|
-
() => starredAgents.value.length > 0 || starredPersonas.value.length > 0
|
|
37
|
+
() => starredAgents.value.length > 0 || starredPersonas.value.length > 0
|
|
38
38
|
)
|
|
39
39
|
</script>
|
|
40
40
|
|
|
@@ -15,7 +15,9 @@ const { data, refresh } = fetchApi<SidebarStats>('/api/sidebar-stats')
|
|
|
15
15
|
|
|
16
16
|
let timer: ReturnType<typeof setInterval> | null = null
|
|
17
17
|
onMounted(() => {
|
|
18
|
-
timer = setInterval(() => {
|
|
18
|
+
timer = setInterval(() => {
|
|
19
|
+
refresh()
|
|
20
|
+
}, 60_000)
|
|
19
21
|
})
|
|
20
22
|
onBeforeUnmount(() => {
|
|
21
23
|
if (timer) clearInterval(timer)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
<!-- eslint-disable vue/multi-word-component-names -->
|
|
1
2
|
<script setup lang="ts">
|
|
2
3
|
// PR99b v3.68.0 — xterm.js terminal mount.
|
|
3
4
|
// PR99c v3.69.0 — accepts an external session via prop so the tab
|
|
@@ -46,7 +47,7 @@ onMounted(async () => {
|
|
|
46
47
|
lineHeight: 1.2,
|
|
47
48
|
scrollback: 5000,
|
|
48
49
|
theme: effectiveTheme.value,
|
|
49
|
-
allowProposedApi: true
|
|
50
|
+
allowProposedApi: true
|
|
50
51
|
})
|
|
51
52
|
|
|
52
53
|
// React to theme switches without remounting.
|
|
@@ -69,10 +70,10 @@ onMounted(async () => {
|
|
|
69
70
|
// first mount and shells started at the default 80 cols, leaving
|
|
70
71
|
// empty space on the right of the canvas.
|
|
71
72
|
await nextTick()
|
|
72
|
-
await new Promise<void>(
|
|
73
|
+
await new Promise<void>(resolve => requestAnimationFrame(() => resolve()))
|
|
73
74
|
try {
|
|
74
75
|
fitAddon.fit()
|
|
75
|
-
} catch
|
|
76
|
+
} catch {
|
|
76
77
|
// dom layout not ready — ResizeObserver below will recover
|
|
77
78
|
}
|
|
78
79
|
|
|
@@ -143,7 +144,7 @@ onMounted(async () => {
|
|
|
143
144
|
requestAnimationFrame(() => {
|
|
144
145
|
try {
|
|
145
146
|
fitAddon.fit()
|
|
146
|
-
} catch
|
|
147
|
+
} catch { /* layout not ready */ }
|
|
147
148
|
session.sendResize(t.cols, t.rows)
|
|
148
149
|
})
|
|
149
150
|
}
|
|
@@ -153,7 +154,7 @@ onMounted(async () => {
|
|
|
153
154
|
try {
|
|
154
155
|
fitAddon.fit()
|
|
155
156
|
session.sendResize(t.cols, t.rows)
|
|
156
|
-
} catch
|
|
157
|
+
} catch {
|
|
157
158
|
// dom may have unmounted
|
|
158
159
|
}
|
|
159
160
|
})
|
|
@@ -169,7 +170,7 @@ onMounted(async () => {
|
|
|
169
170
|
try {
|
|
170
171
|
fit.value?.fit()
|
|
171
172
|
session.sendResize(term.value!.cols, term.value!.rows)
|
|
172
|
-
} catch
|
|
173
|
+
} catch { /* layout not ready */ }
|
|
173
174
|
})
|
|
174
175
|
})
|
|
175
176
|
})
|
|
@@ -180,7 +181,7 @@ function refit() {
|
|
|
180
181
|
try {
|
|
181
182
|
fit.value.fit()
|
|
182
183
|
session.sendResize(term.value.cols, term.value.rows)
|
|
183
|
-
} catch
|
|
184
|
+
} catch {
|
|
184
185
|
// layout not ready
|
|
185
186
|
}
|
|
186
187
|
}
|
|
@@ -200,7 +201,7 @@ defineExpose({
|
|
|
200
201
|
status: session.status,
|
|
201
202
|
error: session.error,
|
|
202
203
|
meta: session.meta,
|
|
203
|
-
refit
|
|
204
|
+
refit
|
|
204
205
|
})
|
|
205
206
|
</script>
|
|
206
207
|
|
|
@@ -25,10 +25,16 @@ function lineDiff(a: string, b: string): DiffRow[] {
|
|
|
25
25
|
const n = right.length
|
|
26
26
|
// Build LCS table.
|
|
27
27
|
const dp: number[][] = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0))
|
|
28
|
+
// Indices below are always in-bounds (loops bounded by lengths; dp has
|
|
29
|
+
// m+1 rows / n+1 cols), so the fallbacks never fire — they only satisfy
|
|
30
|
+
// noUncheckedIndexedAccess. 0 matches the table fill value.
|
|
31
|
+
const cell = (i: number, j: number): number => dp[i]?.[j] ?? 0
|
|
28
32
|
for (let i = m - 1; i >= 0; i -= 1) {
|
|
33
|
+
const row = dp[i]
|
|
34
|
+
if (!row) continue
|
|
29
35
|
for (let j = n - 1; j >= 0; j -= 1) {
|
|
30
|
-
if (left[i] === right[j])
|
|
31
|
-
else
|
|
36
|
+
if (left[i] === right[j]) row[j] = cell(i + 1, j + 1) + 1
|
|
37
|
+
else row[j] = Math.max(cell(i + 1, j), cell(i, j + 1))
|
|
32
38
|
}
|
|
33
39
|
}
|
|
34
40
|
// Walk back, emitting rows.
|
|
@@ -36,24 +42,26 @@ function lineDiff(a: string, b: string): DiffRow[] {
|
|
|
36
42
|
let i = 0
|
|
37
43
|
let j = 0
|
|
38
44
|
while (i < m && j < n) {
|
|
39
|
-
|
|
40
|
-
|
|
45
|
+
const l = left[i] ?? ''
|
|
46
|
+
const r = right[j] ?? ''
|
|
47
|
+
if (l === r) {
|
|
48
|
+
rows.push({ kind: 'eq', left: l, right: r })
|
|
41
49
|
i += 1
|
|
42
50
|
j += 1
|
|
43
|
-
} else if (
|
|
44
|
-
rows.push({ kind: 'del', left:
|
|
51
|
+
} else if (cell(i + 1, j) >= cell(i, j + 1)) {
|
|
52
|
+
rows.push({ kind: 'del', left: l, right: '' })
|
|
45
53
|
i += 1
|
|
46
54
|
} else {
|
|
47
|
-
rows.push({ kind: 'add', left: '', right:
|
|
55
|
+
rows.push({ kind: 'add', left: '', right: r })
|
|
48
56
|
j += 1
|
|
49
57
|
}
|
|
50
58
|
}
|
|
51
59
|
while (i < m) {
|
|
52
|
-
rows.push({ kind: 'del', left: left[i], right: '' })
|
|
60
|
+
rows.push({ kind: 'del', left: left[i] ?? '', right: '' })
|
|
53
61
|
i += 1
|
|
54
62
|
}
|
|
55
63
|
while (j < n) {
|
|
56
|
-
rows.push({ kind: 'add', left: '', right: right[j] })
|
|
64
|
+
rows.push({ kind: 'add', left: '', right: right[j] ?? '' })
|
|
57
65
|
j += 1
|
|
58
66
|
}
|
|
59
67
|
return rows
|
|
@@ -62,8 +70,8 @@ function lineDiff(a: string, b: string): DiffRow[] {
|
|
|
62
70
|
const rows = computed(() => lineDiff(props.left || '', props.right || ''))
|
|
63
71
|
|
|
64
72
|
const summary = computed(() => {
|
|
65
|
-
const adds = rows.value.filter(
|
|
66
|
-
const dels = rows.value.filter(
|
|
73
|
+
const adds = rows.value.filter(r => r.kind === 'add').length
|
|
74
|
+
const dels = rows.value.filter(r => r.kind === 'del').length
|
|
67
75
|
return { adds, dels }
|
|
68
76
|
})
|
|
69
77
|
</script>
|
|
@@ -90,9 +98,11 @@ const summary = computed(() => {
|
|
|
90
98
|
:class="[
|
|
91
99
|
'px-3 py-1 whitespace-pre-wrap break-words border-b border-default/50 min-h-[1.5rem]',
|
|
92
100
|
row.kind === 'del' ? 'bg-rose-500/10 text-rose-600 dark:text-rose-400' : '',
|
|
93
|
-
row.kind === 'add' ? 'bg-muted/10' : ''
|
|
101
|
+
row.kind === 'add' ? 'bg-muted/10' : ''
|
|
94
102
|
]"
|
|
95
|
-
>
|
|
103
|
+
>
|
|
104
|
+
{{ row.left || '·' }}
|
|
105
|
+
</div>
|
|
96
106
|
</div>
|
|
97
107
|
<div class="border-l border-default">
|
|
98
108
|
<div
|
|
@@ -101,9 +111,11 @@ const summary = computed(() => {
|
|
|
101
111
|
:class="[
|
|
102
112
|
'px-3 py-1 whitespace-pre-wrap break-words border-b border-default/50 min-h-[1.5rem]',
|
|
103
113
|
row.kind === 'add' ? 'bg-emerald-500/10 text-emerald-600 dark:text-emerald-400' : '',
|
|
104
|
-
row.kind === 'del' ? 'bg-muted/10' : ''
|
|
114
|
+
row.kind === 'del' ? 'bg-muted/10' : ''
|
|
105
115
|
]"
|
|
106
|
-
>
|
|
116
|
+
>
|
|
117
|
+
{{ row.right || '·' }}
|
|
118
|
+
</div>
|
|
107
119
|
</div>
|
|
108
120
|
</div>
|
|
109
121
|
</div>
|
|
@@ -8,7 +8,7 @@ import { createSharedComposable } from '@vueuse/core'
|
|
|
8
8
|
|
|
9
9
|
export interface ActivityEvent {
|
|
10
10
|
id: string
|
|
11
|
-
ts: string
|
|
11
|
+
ts: string // ISO
|
|
12
12
|
kind: 'success' | 'warning' | 'error' | 'info'
|
|
13
13
|
title: string
|
|
14
14
|
description?: string
|
|
@@ -24,7 +24,7 @@ const _useActivityFeed = () => {
|
|
|
24
24
|
const loaded = useState<boolean>('activityFeedLoaded', () => false)
|
|
25
25
|
// PR94a v3.47.0 — unread count is now derived from `read: false`.
|
|
26
26
|
const unreadCount = computed(
|
|
27
|
-
() => events.value.filter(
|
|
27
|
+
() => events.value.filter(e => !e.read).length
|
|
28
28
|
)
|
|
29
29
|
|
|
30
30
|
function _persist() {
|
|
@@ -91,7 +91,7 @@ const _useActivityFeed = () => {
|
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
function remove(id: string) {
|
|
94
|
-
events.value = events.value.filter(
|
|
94
|
+
events.value = events.value.filter(e => e.id !== id)
|
|
95
95
|
_persist()
|
|
96
96
|
}
|
|
97
97
|
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
export const useApi = () => {
|
|
2
2
|
const apiBase = useRuntimeConfig().public.apiBase || 'http://localhost:3334'
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
// path may be a plain string or a getter — useFetch accepts a getter URL
|
|
5
|
+
// and refetches when its reactive deps change (compare pages rely on it).
|
|
6
|
+
const fetchApi = <T>(path: MaybeRefOrGetter<string>, opts?: Record<string, unknown>) =>
|
|
7
|
+
useFetch<T>(() => `${apiBase}${toValue(path)}`, { ...opts })
|
|
6
8
|
|
|
7
9
|
return { fetchApi, apiBase }
|
|
8
10
|
}
|
|
@@ -29,7 +29,7 @@ export const useConfirmDialog = () => {
|
|
|
29
29
|
return async (options: ConfirmDialogOptions): Promise<boolean> => {
|
|
30
30
|
const modal = overlay.create(ConfirmDialog, {
|
|
31
31
|
destroyOnClose: true,
|
|
32
|
-
props: options
|
|
32
|
+
props: options
|
|
33
33
|
})
|
|
34
34
|
|
|
35
35
|
const result = await modal.open()
|
|
@@ -16,7 +16,7 @@ const _useFavorites = () => {
|
|
|
16
16
|
const toast = useToast()
|
|
17
17
|
const state = useState<FavoritesPayload>('favorites', () => ({
|
|
18
18
|
agents: [],
|
|
19
|
-
personas: []
|
|
19
|
+
personas: []
|
|
20
20
|
}))
|
|
21
21
|
const loaded = useState<boolean>('favoritesLoaded', () => false)
|
|
22
22
|
|
|
@@ -26,7 +26,7 @@ const _useFavorites = () => {
|
|
|
26
26
|
const res = await $fetch<FavoritesPayload>(`${apiBase}/api/favorites`)
|
|
27
27
|
state.value = {
|
|
28
28
|
agents: res.agents ?? [],
|
|
29
|
-
personas: res.personas ?? []
|
|
29
|
+
personas: res.personas ?? []
|
|
30
30
|
}
|
|
31
31
|
loaded.value = true
|
|
32
32
|
} catch {
|
|
@@ -47,7 +47,7 @@ const _useFavorites = () => {
|
|
|
47
47
|
async function setMany(
|
|
48
48
|
kind: 'agents' | 'personas',
|
|
49
49
|
ids: string[],
|
|
50
|
-
favorited: boolean
|
|
50
|
+
favorited: boolean
|
|
51
51
|
) {
|
|
52
52
|
try {
|
|
53
53
|
const res = await $fetch<{
|
|
@@ -56,7 +56,7 @@ const _useFavorites = () => {
|
|
|
56
56
|
error?: string
|
|
57
57
|
}>(`${apiBase}/api/favorites/bulk`, {
|
|
58
58
|
method: 'POST',
|
|
59
|
-
body: { kind, ids, favorited }
|
|
59
|
+
body: { kind, ids, favorited }
|
|
60
60
|
})
|
|
61
61
|
if (res.error) throw new Error(res.error)
|
|
62
62
|
// Sync local state with the new server truth.
|
|
@@ -66,7 +66,7 @@ const _useFavorites = () => {
|
|
|
66
66
|
toast.add({
|
|
67
67
|
title: 'Favorites bulk update failed',
|
|
68
68
|
description: err instanceof Error ? err.message : 'unknown error',
|
|
69
|
-
color: 'error'
|
|
69
|
+
color: 'error'
|
|
70
70
|
})
|
|
71
71
|
return null
|
|
72
72
|
}
|
|
@@ -76,21 +76,21 @@ const _useFavorites = () => {
|
|
|
76
76
|
try {
|
|
77
77
|
const res = await $fetch<{ favorited?: boolean, error?: string }>(
|
|
78
78
|
`${apiBase}/api/favorites/${kind}/${id}`,
|
|
79
|
-
{ method: 'POST' }
|
|
79
|
+
{ method: 'POST' }
|
|
80
80
|
)
|
|
81
81
|
if (res.error) throw new Error(res.error)
|
|
82
82
|
const bucket = state.value[kind]
|
|
83
83
|
if (res.favorited && !bucket.includes(id)) {
|
|
84
84
|
state.value = { ...state.value, [kind]: [...bucket, id] }
|
|
85
85
|
} else if (!res.favorited) {
|
|
86
|
-
state.value = { ...state.value, [kind]: bucket.filter(
|
|
86
|
+
state.value = { ...state.value, [kind]: bucket.filter(x => x !== id) }
|
|
87
87
|
}
|
|
88
88
|
return res.favorited
|
|
89
89
|
} catch (err) {
|
|
90
90
|
toast.add({
|
|
91
91
|
title: 'Favorite toggle failed',
|
|
92
92
|
description: err instanceof Error ? err.message : 'unknown error',
|
|
93
|
-
color: 'error'
|
|
93
|
+
color: 'error'
|
|
94
94
|
})
|
|
95
95
|
return null
|
|
96
96
|
}
|
|
@@ -50,9 +50,9 @@ export const TERMINAL_THEMES: Record<string, XtermTheme> = {
|
|
|
50
50
|
brightBlue: '#bae6fd',
|
|
51
51
|
brightMagenta: '#f5d0fe',
|
|
52
52
|
brightCyan: '#a5f3fc',
|
|
53
|
-
brightWhite: '#fafafa'
|
|
53
|
+
brightWhite: '#fafafa'
|
|
54
54
|
},
|
|
55
|
-
dracula: {
|
|
55
|
+
'dracula': {
|
|
56
56
|
background: '#282a36',
|
|
57
57
|
foreground: '#f8f8f2',
|
|
58
58
|
cursor: '#f8f8f2',
|
|
@@ -73,7 +73,7 @@ export const TERMINAL_THEMES: Record<string, XtermTheme> = {
|
|
|
73
73
|
brightBlue: '#d6acff',
|
|
74
74
|
brightMagenta: '#ff92df',
|
|
75
75
|
brightCyan: '#a4ffff',
|
|
76
|
-
brightWhite: '#ffffff'
|
|
76
|
+
brightWhite: '#ffffff'
|
|
77
77
|
},
|
|
78
78
|
'solarized-dark': {
|
|
79
79
|
background: '#002b36',
|
|
@@ -96,7 +96,7 @@ export const TERMINAL_THEMES: Record<string, XtermTheme> = {
|
|
|
96
96
|
brightBlue: '#839496',
|
|
97
97
|
brightMagenta: '#6c71c4',
|
|
98
98
|
brightCyan: '#93a1a1',
|
|
99
|
-
brightWhite: '#fdf6e3'
|
|
99
|
+
brightWhite: '#fdf6e3'
|
|
100
100
|
},
|
|
101
101
|
'solarized-light': {
|
|
102
102
|
background: '#fdf6e3',
|
|
@@ -119,9 +119,9 @@ export const TERMINAL_THEMES: Record<string, XtermTheme> = {
|
|
|
119
119
|
brightBlue: '#839496',
|
|
120
120
|
brightMagenta: '#6c71c4',
|
|
121
121
|
brightCyan: '#93a1a1',
|
|
122
|
-
brightWhite: '#fdf6e3'
|
|
122
|
+
brightWhite: '#fdf6e3'
|
|
123
123
|
},
|
|
124
|
-
nord: {
|
|
124
|
+
'nord': {
|
|
125
125
|
background: '#2e3440',
|
|
126
126
|
foreground: '#d8dee9',
|
|
127
127
|
cursor: '#d8dee9',
|
|
@@ -142,16 +142,16 @@ export const TERMINAL_THEMES: Record<string, XtermTheme> = {
|
|
|
142
142
|
brightBlue: '#81a1c1',
|
|
143
143
|
brightMagenta: '#b48ead',
|
|
144
144
|
brightCyan: '#8fbcbb',
|
|
145
|
-
brightWhite: '#eceff4'
|
|
146
|
-
}
|
|
145
|
+
brightWhite: '#eceff4'
|
|
146
|
+
}
|
|
147
147
|
}
|
|
148
148
|
|
|
149
149
|
export const THEME_LABELS: Record<string, string> = {
|
|
150
150
|
'arkaos-dark': 'ArkaOS Dark',
|
|
151
|
-
dracula: 'Dracula',
|
|
151
|
+
'dracula': 'Dracula',
|
|
152
152
|
'solarized-dark': 'Solarized Dark',
|
|
153
153
|
'solarized-light': 'Solarized Light',
|
|
154
|
-
nord: 'Nord'
|
|
154
|
+
'nord': 'Nord'
|
|
155
155
|
}
|
|
156
156
|
|
|
157
157
|
const STORAGE_KEY = 'arka-terminal-theme'
|
|
@@ -174,17 +174,17 @@ export function useTerminalThemes() {
|
|
|
174
174
|
}
|
|
175
175
|
|
|
176
176
|
const activeTheme = computed<XtermTheme>(
|
|
177
|
-
() => TERMINAL_THEMES[themeName.value] ?? TERMINAL_THEMES[DEFAULT_THEME]
|
|
177
|
+
() => TERMINAL_THEMES[themeName.value] ?? TERMINAL_THEMES[DEFAULT_THEME]!
|
|
178
178
|
)
|
|
179
179
|
|
|
180
180
|
const options = computed(() =>
|
|
181
|
-
Object.entries(THEME_LABELS).map(([value, label]) => ({ value, label }))
|
|
181
|
+
Object.entries(THEME_LABELS).map(([value, label]) => ({ value, label }))
|
|
182
182
|
)
|
|
183
183
|
|
|
184
184
|
return {
|
|
185
185
|
themeName,
|
|
186
186
|
activeTheme,
|
|
187
187
|
setTheme,
|
|
188
|
-
options
|
|
188
|
+
options
|
|
189
189
|
}
|
|
190
190
|
}
|
|
@@ -16,7 +16,7 @@ export const THEME_COLOR_OPTIONS = [
|
|
|
16
16
|
{ label: 'Rose', value: 'rose' },
|
|
17
17
|
{ label: 'Amber', value: 'amber' },
|
|
18
18
|
{ label: 'Teal', value: 'teal' },
|
|
19
|
-
{ label: 'Cyan', value: 'cyan' }
|
|
19
|
+
{ label: 'Cyan', value: 'cyan' }
|
|
20
20
|
] as const
|
|
21
21
|
|
|
22
22
|
export type ThemeColor = (typeof THEME_COLOR_OPTIONS)[number]['value']
|
|
@@ -27,8 +27,11 @@ const _useThemeColor = () => {
|
|
|
27
27
|
|
|
28
28
|
function apply(color: ThemeColor) {
|
|
29
29
|
current.value = color
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
// ui.colors comes from app.config.ts and isn't part of Nuxt's typed
|
|
31
|
+
// AppConfig, so narrow it structurally once before writing.
|
|
32
|
+
const ui = appConfig.ui as { colors?: Record<string, string> } | undefined
|
|
33
|
+
if (ui?.colors) {
|
|
34
|
+
ui.colors.primary = color
|
|
32
35
|
}
|
|
33
36
|
}
|
|
34
37
|
|
|
@@ -42,7 +45,7 @@ const _useThemeColor = () => {
|
|
|
42
45
|
function loadFromStorage() {
|
|
43
46
|
if (typeof window === 'undefined') return
|
|
44
47
|
const saved = window.localStorage.getItem(STORAGE_KEY) as ThemeColor | null
|
|
45
|
-
if (saved && THEME_COLOR_OPTIONS.some(
|
|
48
|
+
if (saved && THEME_COLOR_OPTIONS.some(o => o.value === saved)) {
|
|
46
49
|
apply(saved)
|
|
47
50
|
}
|
|
48
51
|
}
|