-
+
diff --git a/ui/src/views/WatchersView.vue b/ui/src/views/WatchersView.vue
index 5c89ccb0..1c6a6263 100644
--- a/ui/src/views/WatchersView.vue
+++ b/ui/src/views/WatchersView.vue
@@ -158,11 +158,11 @@ onMounted(async () => {
type="text"
placeholder="Filter by name..."
class="flex-1 min-w-[120px] max-w-[240px] px-2.5 py-1.5 dd-rounded text-[0.6875rem] font-medium outline-none dd-bg dd-text dd-placeholder" />
-
+
diff --git a/ui/src/views/dashboard/useDashboardComputed.ts b/ui/src/views/dashboard/useDashboardComputed.ts
index 60a07fd1..1c84857c 100644
--- a/ui/src/views/dashboard/useDashboardComputed.ts
+++ b/ui/src/views/dashboard/useDashboardComputed.ts
@@ -20,6 +20,7 @@ import { getWatcherConfiguration } from './watcherConfiguration';
const DONUT_CIRCUMFERENCE = 301.6;
const RECENT_UPDATES_LIMIT = 6;
+const FILTER_KIND_ANY = 'ANY'.toLowerCase();
const UPDATE_BREAKDOWN_BUCKETS: ReadonlyArray> = [
{
@@ -504,7 +505,7 @@ function useStatsComputed(
icon: 'updates',
color: updatesStatColor,
colorMuted: updatesStatMutedColor,
- route: { path: ROUTES.CONTAINERS, query: { filterKind: 'any' } },
+ route: { path: ROUTES.CONTAINERS, query: { filterKind: FILTER_KIND_ANY } },
detail:
freshUpdates > 0
? `${freshUpdates} new · ${updatesAvailable - freshUpdates} mature`
diff --git a/ui/tests/components/ButtonStandard.spec.ts b/ui/tests/components/ButtonStandard.spec.ts
new file mode 100644
index 00000000..10ea9938
--- /dev/null
+++ b/ui/tests/components/ButtonStandard.spec.ts
@@ -0,0 +1,51 @@
+import { readdirSync, readFileSync } from 'node:fs';
+import { join, relative } from 'node:path';
+import { describe, expect, it } from 'vitest';
+
+const SRC_DIR = join(process.cwd(), 'src');
+
+const ALLOWED_RAW_BUTTON_FILES = new Set([
+ 'src/components/AppButton.vue',
+ 'src/components/ThemeToggle.vue',
+ 'src/components/ToggleSwitch.vue',
+]);
+
+function collectVueFiles(dir: string): string[] {
+ const entries = readdirSync(dir, { withFileTypes: true });
+ const files: string[] = [];
+
+ for (const entry of entries) {
+ const fullPath = join(dir, entry.name);
+ if (entry.isDirectory()) {
+ files.push(...collectVueFiles(fullPath));
+ continue;
+ }
+
+ if (entry.isFile() && entry.name.endsWith('.vue')) {
+ files.push(fullPath);
+ }
+ }
+
+ return files;
+}
+
+describe('button standard', () => {
+ it('uses AppButton as the shared button primitive across Vue templates', () => {
+ const vueFiles = collectVueFiles(SRC_DIR);
+ const offenders: string[] = [];
+
+ for (const filePath of vueFiles) {
+ const relPath = relative(process.cwd(), filePath).replaceAll('\\', '/');
+ if (ALLOWED_RAW_BUTTON_FILES.has(relPath)) {
+ continue;
+ }
+
+ const source = readFileSync(filePath, 'utf8');
+ if (/