hlassiege/panopliepublic Fork
⑂ 0b145af
Commits
⬇ Clone ▾
git clone https://git.rickub.com/hlassiege/panoplie.git
git clone ssh://git@rickub.com/hlassiege/panoplie.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Download ZIP

fix(ui): truncate long header subtitles, show a dash for missing project folders, remove unused badge wrappers

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
hlassiege committed 2026-10-23T14:59:42+02:00 Browse files
0b145af parent: 9d90581
deleted packages/ui/app/components/AgentBadge.vue +0 -10
deleted file mode 100644
@@ -1,10 +0,0 @@
1-<script setup lang="ts">
2-import type { AgentId } from '@skilly/core'
3-
4-// Legacy name kept for existing screens; agents are neutral in the new design (see SkAgentBadge).
5-withDefaults(defineProps<{ agent: AgentId, compact?: boolean }>(), { compact: false })
6-</script>
7-
8-<template>
9- <SkAgentBadge :agent="agent" :compact="compact" variant="tag" />
10-</template>
deleted file mode 100644
@@ -1,10 +0,0 @@
1-<script setup lang="ts">
2-import type { AgentId } from '@skilly/core'
3-
4-// Legacy name kept for existing screens; agents are neutral in the new design (see SkAgentBadge).
5-withDefaults(defineProps<{ agent: AgentId, compact?: boolean }>(), { compact: false })
6-</script>
7-
8-<template>
9- <SkAgentBadge :agent="agent" :compact="compact" variant="tag" />
10-</template>
modified packages/ui/app/components/CatalogHeader.vue +1 -1
@@ -28,7 +28,7 @@ const menu = computed(() => [CAT_ORDER.map(t => ({
2828 </script>
2929
3030 <template>
31- <SkPageHeader title="Catalog">
31+ <SkPageHeader title="Catalog" truncate-subtitle :subtitle-title="where ? `Your shared library. ${where}` : undefined">
3232 <template #subtitle>
3333 Your shared library.
3434 <span v-if="where" class="font-mono text-xs" :title="where">{{ where }}</span>
@@ -28,7 +28,7 @@ const menu = computed(() => [CAT_ORDER.map(t => ({
28 </script>28 </script>
29 29
30 <template>30 <template>
31- <SkPageHeader title="Catalog">31+ <SkPageHeader title="Catalog" truncate-subtitle :subtitle-title="where ? `Your shared library. ${where}` : undefined">
32 <template #subtitle>32 <template #subtitle>
33 Your shared library.33 Your shared library.
34 <span v-if="where" class="font-mono text-xs" :title="where">{{ where }}</span>34 <span v-if="where" class="font-mono text-xs" :title="where">{{ where }}</span>
deleted packages/ui/app/components/StatusBadge.vue +0 -14
deleted file mode 100644
@@ -1,14 +0,0 @@
1-<script setup lang="ts">
2-import type { ItemStatus } from '@skilly/core'
3-
4-// Legacy wrapper kept for existing screens; new screens use SkStatus directly.
5-defineProps<{ status?: ItemStatus }>()
6-</script>
7-
8-<template>
9- <div v-if="status" class="flex flex-wrap items-center gap-1.5">
10- <SkStatus :state="status.state" />
11- <SkTag v-if="status.managed" mono title="Written by skilly: it keeps this file up to date.">managed</SkTag>
12- </div>
13- <span v-else class="text-xs text-sk-faint">-</span>
14-</template>
deleted file mode 100644
@@ -1,14 +0,0 @@
1-<script setup lang="ts">
2-import type { ItemStatus } from '@skilly/core'
3-
4-// Legacy wrapper kept for existing screens; new screens use SkStatus directly.
5-defineProps<{ status?: ItemStatus }>()
6-</script>
7-
8-<template>
9- <div v-if="status" class="flex flex-wrap items-center gap-1.5">
10- <SkStatus :state="status.state" />
11- <SkTag v-if="status.managed" mono title="Written by skilly: it keeps this file up to date.">managed</SkTag>
12- </div>
13- <span v-else class="text-xs text-sk-faint">-</span>
14-</template>
modified packages/ui/app/components/projects/ProjectsTable.vue +1 -1
@@ -72,7 +72,7 @@ const rowStatus = (p: ProjectSummary): { tone: Tone, label: string } | null => {
7272 }
7373 const agentsOf = (p: ProjectSummary) => summaries.value[p.id]?.agents
7474 ?? (p.agentsDetected.length ? p.agentsDetected.map(a => AGENT_LABELS[a]).join(', ') : '—')
75-const countOf = (p: ProjectSummary) => summaries.value[p.id]?.items ?? Object.entries(p.counts).filter(([t]) => t !== 'instructions').reduce((n, [, c]) => n + c, 0)
75+const countOf = (p: ProjectSummary) => !p.exists ? '—' : summaries.value[p.id]?.items ?? Object.entries(p.counts).filter(([t]) => t !== 'instructions').reduce((n, [, c]) => n + c, 0)
7676 </script>
7777
7878 <template>
@@ -72,7 +72,7 @@ const rowStatus = (p: ProjectSummary): { tone: Tone, label: string } | null => {
72 }72 }
73 const agentsOf = (p: ProjectSummary) => summaries.value[p.id]?.agents73 const agentsOf = (p: ProjectSummary) => summaries.value[p.id]?.agents
74 ?? (p.agentsDetected.length ? p.agentsDetected.map(a => AGENT_LABELS[a]).join(', ') : '—')74 ?? (p.agentsDetected.length ? p.agentsDetected.map(a => AGENT_LABELS[a]).join(', ') : '—')
75-const countOf = (p: ProjectSummary) => summaries.value[p.id]?.items ?? Object.entries(p.counts).filter(([t]) => t !== 'instructions').reduce((n, [, c]) => n + c, 0)75+const countOf = (p: ProjectSummary) => !p.exists ? '—' : summaries.value[p.id]?.items ?? Object.entries(p.counts).filter(([t]) => t !== 'instructions').reduce((n, [, c]) => n + c, 0)
76 </script>76 </script>
77 77
78 <template>78 <template>
modified packages/ui/app/components/scope/ScopePage.vue +3 -3
@@ -87,12 +87,12 @@ const homeInstructionCount = computed(() => (inventory.value?.items ?? []).filte
8787
8888 <template>
8989 <SkPage>
90- <SkPageHeader :title="home ? 'Home · user-level config' : title" :subtitle="home ? 'Everything here is visible in every project.' : undefined" :aside="home">
90+ <SkPageHeader :title="home ? 'Home · user-level config' : title" :subtitle="home ? 'Everything here is visible in every project.' : undefined" :aside="home" truncate-subtitle :subtitle-title="rootPath">
9191 <template v-if="home" #lead><SkAvatar /></template>
9292 <template v-else #breadcrumb><SkBreadcrumb :items="[{ label: 'Projects', to: '/projects' }]" /></template>
9393 <template v-if="!home" #subtitle>
94- <span class="flex flex-wrap items-center gap-x-2 gap-y-1 font-mono text-xs text-sk-faint">
95- <span class="max-w-[360px] truncate" :title="rootPath">{{ rootPath }}</span>
94+ <span class="flex items-center gap-x-2 font-mono text-xs text-sk-faint">
95+ <span class="min-w-0 max-w-[360px] shrink truncate" :title="rootPath">{{ rootPath }}</span>
9696 <template v-if="inventory">
9797 <span aria-hidden="true">·</span>
9898 <ScopeTargets v-if="config" />
@@ -87,12 +87,12 @@ const homeInstructionCount = computed(() => (inventory.value?.items ?? []).filte
87 87
88 <template>88 <template>
89 <SkPage>89 <SkPage>
90- <SkPageHeader :title="home ? 'Home · user-level config' : title" :subtitle="home ? 'Everything here is visible in every project.' : undefined" :aside="home">90+ <SkPageHeader :title="home ? 'Home · user-level config' : title" :subtitle="home ? 'Everything here is visible in every project.' : undefined" :aside="home" truncate-subtitle :subtitle-title="rootPath">
91 <template v-if="home" #lead><SkAvatar /></template>91 <template v-if="home" #lead><SkAvatar /></template>
92 <template v-else #breadcrumb><SkBreadcrumb :items="[{ label: 'Projects', to: '/projects' }]" /></template>92 <template v-else #breadcrumb><SkBreadcrumb :items="[{ label: 'Projects', to: '/projects' }]" /></template>
93 <template v-if="!home" #subtitle>93 <template v-if="!home" #subtitle>
94- <span class="flex flex-wrap items-center gap-x-2 gap-y-1 font-mono text-xs text-sk-faint">94+ <span class="flex items-center gap-x-2 font-mono text-xs text-sk-faint">
95- <span class="max-w-[360px] truncate" :title="rootPath">{{ rootPath }}</span>95+ <span class="min-w-0 max-w-[360px] shrink truncate" :title="rootPath">{{ rootPath }}</span>
96 <template v-if="inventory">96 <template v-if="inventory">
97 <span aria-hidden="true">·</span>97 <span aria-hidden="true">·</span>
98 <ScopeTargets v-if="config" />98 <ScopeTargets v-if="config" />
modified packages/ui/app/components/sk/SkPageHeader.vue +9 -3
@@ -4,22 +4,28 @@
44 * - `#lead`: something before the title (e.g. the `~` SkAvatar on Home)
55 * - `#breadcrumb`: above the title (SkBreadcrumb)
66 * - `#subtitle` or `subtitle` prop; `aside` puts the subtitle on the right of the title row (Home style)
7+ * - `truncateSubtitle`: single-line subtitle with an ellipsis; the full text is exposed as a tooltip (`subtitleTitle` or the subtitle itself)
78 * - `#actions`: buttons / SkSeg tabs on the right
89 */
9-withDefaults(defineProps<{ title: string, subtitle?: string, aside?: boolean }>(), { aside: false })
10+withDefaults(defineProps<{ title: string, subtitle?: string, aside?: boolean, truncateSubtitle?: boolean, subtitleTitle?: string }>(), { aside: false, truncateSubtitle: false })
1011 </script>
1112
1213 <template>
1314 <header class="flex flex-wrap items-center gap-x-3 gap-y-2">
1415 <slot name="lead" />
15- <div class="min-w-0">
16+ <div class="min-w-0" :class="truncateSubtitle && 'max-w-full'">
1617 <div v-if="$slots.breadcrumb" class="mb-1.5 text-sk-muted">
1718 <slot name="breadcrumb" />
1819 </div>
1920 <h1 class="m-0 truncate font-mono text-[22px] leading-[1.25] font-normal tracking-[-0.01em] text-sk-text">
2021 {{ title }}
2122 </h1>
22- <p v-if="!aside && (subtitle || $slots.subtitle)" class="mt-1 mb-0 text-pretty text-sk-muted">
23+ <p
24+ v-if="!aside && (subtitle || $slots.subtitle)"
25+ class="mt-1 mb-0 text-sk-muted"
26+ :class="truncateSubtitle ? 'truncate' : 'text-pretty'"
27+ :title="truncateSubtitle ? (subtitleTitle ?? subtitle) : undefined"
28+ >
2329 <slot name="subtitle">{{ subtitle }}</slot>
2430 </p>
2531 </div>
@@ -4,22 +4,28 @@
4 * - `#lead`: something before the title (e.g. the `~` SkAvatar on Home)4 * - `#lead`: something before the title (e.g. the `~` SkAvatar on Home)
5 * - `#breadcrumb`: above the title (SkBreadcrumb)5 * - `#breadcrumb`: above the title (SkBreadcrumb)
6 * - `#subtitle` or `subtitle` prop; `aside` puts the subtitle on the right of the title row (Home style)6 * - `#subtitle` or `subtitle` prop; `aside` puts the subtitle on the right of the title row (Home style)
7+ * - `truncateSubtitle`: single-line subtitle with an ellipsis; the full text is exposed as a tooltip (`subtitleTitle` or the subtitle itself)
7 * - `#actions`: buttons / SkSeg tabs on the right8 * - `#actions`: buttons / SkSeg tabs on the right
8 */9 */
9-withDefaults(defineProps<{ title: string, subtitle?: string, aside?: boolean }>(), { aside: false })10+withDefaults(defineProps<{ title: string, subtitle?: string, aside?: boolean, truncateSubtitle?: boolean, subtitleTitle?: string }>(), { aside: false, truncateSubtitle: false })
10 </script>11 </script>
11 12
12 <template>13 <template>
13 <header class="flex flex-wrap items-center gap-x-3 gap-y-2">14 <header class="flex flex-wrap items-center gap-x-3 gap-y-2">
14 <slot name="lead" />15 <slot name="lead" />
15- <div class="min-w-0">16+ <div class="min-w-0" :class="truncateSubtitle && 'max-w-full'">
16 <div v-if="$slots.breadcrumb" class="mb-1.5 text-sk-muted">17 <div v-if="$slots.breadcrumb" class="mb-1.5 text-sk-muted">
17 <slot name="breadcrumb" />18 <slot name="breadcrumb" />
18 </div>19 </div>
19 <h1 class="m-0 truncate font-mono text-[22px] leading-[1.25] font-normal tracking-[-0.01em] text-sk-text">20 <h1 class="m-0 truncate font-mono text-[22px] leading-[1.25] font-normal tracking-[-0.01em] text-sk-text">
20 {{ title }}21 {{ title }}
21 </h1>22 </h1>
22- <p v-if="!aside && (subtitle || $slots.subtitle)" class="mt-1 mb-0 text-pretty text-sk-muted">23+ <p
24+ v-if="!aside && (subtitle || $slots.subtitle)"
25+ class="mt-1 mb-0 text-sk-muted"
26+ :class="truncateSubtitle ? 'truncate' : 'text-pretty'"
27+ :title="truncateSubtitle ? (subtitleTitle ?? subtitle) : undefined"
28+ >
23 <slot name="subtitle">{{ subtitle }}</slot>29 <slot name="subtitle">{{ subtitle }}</slot>
24 </p>30 </p>
25 </div>31 </div>
modified packages/ui/app/pages/sync.vue +1 -1
@@ -121,7 +121,7 @@ const noRemoteHint = 'Set a remote first'
121121
122122 <template>
123123 <SkPage>
124- <SkPageHeader title="Sync" :subtitle="subtitle">
124+ <SkPageHeader title="Sync" :subtitle="subtitle" truncate-subtitle>
125125 <template #actions>
126126 <SkButton variant="ghost" icon="i-lucide-refresh-cw" label="Refresh" :disabled="working" @click="load" />
127127 </template>
@@ -121,7 +121,7 @@ const noRemoteHint = 'Set a remote first'
121 121
122 <template>122 <template>
123 <SkPage>123 <SkPage>
124- <SkPageHeader title="Sync" :subtitle="subtitle">124+ <SkPageHeader title="Sync" :subtitle="subtitle" truncate-subtitle>
125 <template #actions>125 <template #actions>
126 <SkButton variant="ghost" icon="i-lucide-refresh-cw" label="Refresh" :disabled="working" @click="load" />126 <SkButton variant="ghost" icon="i-lucide-refresh-cw" label="Refresh" :disabled="working" @click="load" />
127 </template>127 </template>