Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions frontend/taskdeck-web/src/tests/views/paperRootInventory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
export type PaperViewRoot = {
readonly view: string
readonly selector: string
readonly eyebrow?: string
}

/**
* The complete Paper-idiom view-root inventory used by the static substrate
* guards. Keeping the eyebrow selector beside its view prevents the two guards
* from silently drifting when a new Paper root is added.
*/
export const PAPER_VIEW_ROOTS = [
{ view: 'ActivityView.vue', selector: '.paper-activity', eyebrow: '.paper-activity__eyebrow' },
{ view: 'AgentRunDetailView.vue', selector: '.paper-run-detail', eyebrow: '.paper-run-detail__eyebrow' },
{ view: 'AgentRunsView.vue', selector: '.paper-agent-runs', eyebrow: '.paper-agent-runs__eyebrow' },
{ view: 'AgentsView.vue', selector: '.paper-agents', eyebrow: '.paper-agents__eyebrow' },
{ view: 'ApiKeySettingsView.vue', selector: '.paper-api-keys', eyebrow: '.paper-api-keys__eyebrow' },
{ view: 'AppearanceSettingsView.vue', selector: '.paper-appearance', eyebrow: '.paper-appearance__eyebrow' },
{ view: 'ArchiveView.vue', selector: '.paper-archive', eyebrow: '.paper-archive__eyebrow' },
{ view: 'AutomationChatView.vue', selector: '.paper-chat' },
{ view: 'AutomationQueueView.vue', selector: '.paper-queue', eyebrow: '.paper-queue__eyebrow' },
{ view: 'BoardAccessView.vue', selector: '.paper-access', eyebrow: '.paper-access__eyebrow' },
{ view: 'BoardsListView.vue', selector: '.paper-boards', eyebrow: '.paper-boards__eyebrow' },
{ view: 'CalendarView.vue', selector: '.paper-calendar', eyebrow: '.paper-calendar__eyebrow' },
{ view: 'DevToolsView.vue', selector: '.paper-devtools' },
{ view: 'ExportImportView.vue', selector: '.paper-portability', eyebrow: '.paper-portability__eyebrow' },
{ view: 'IntegrationsView.vue', selector: '.paper-int', eyebrow: '.paper-int__eyebrow' },
{ view: 'MetricsView.vue', selector: '.paper-metrics', eyebrow: '.paper-metrics__eyebrow' },
{ view: 'NotFoundView.vue', selector: '.paper-not-found', eyebrow: '.paper-not-found__eyebrow' },
{ view: 'NotificationInboxView.vue', selector: '.paper-notifications', eyebrow: '.paper-notifications__eyebrow' },
{ view: 'NotificationPreferencesView.vue', selector: '.paper-prefs', eyebrow: '.paper-prefs__eyebrow' },
{ view: 'OpsConsoleView.vue', selector: '.paper-ops', eyebrow: '.paper-ops__eyebrow' },
{ view: 'ProfileSettingsView.vue', selector: '.paper-profile', eyebrow: '.paper-profile__eyebrow' },
{ view: 'SavedViewsView.vue', selector: '.paper-views', eyebrow: '.paper-views__eyebrow' },
] as const satisfies ReadonlyArray<PaperViewRoot>
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { describe, expect, it } from 'vitest'

import { PAPER_VIEW_ROOTS } from './paperRootInventory'

import activitySource from '../../views/ActivityView.vue?raw'
import agentRunDetailSource from '../../views/AgentRunDetailView.vue?raw'
import agentRunsSource from '../../views/AgentRunsView.vue?raw'
Expand Down Expand Up @@ -50,34 +52,38 @@ import savedViewsSource from '../../views/SavedViewsView.vue?raw'
* "node", and its quarantine list may only shrink.
*/

const VIEW_ROOTS: ReadonlyArray<{ view: string; selector: string; source: string }> = [
// #1780 / PR #1807 — the four high-traffic roots the guard was written for.
{ view: 'MetricsView.vue', selector: '.paper-metrics', source: metricsSource },
{ view: 'ActivityView.vue', selector: '.paper-activity', source: activitySource },
{ view: 'CalendarView.vue', selector: '.paper-calendar', source: calendarSource },
{ view: 'BoardsListView.vue', selector: '.paper-boards', source: boardsSource },
// #1775 / #1813 — the Saved Views restyle.
{ view: 'SavedViewsView.vue', selector: '.paper-views', source: savedViewsSource },
// PR #1808 — the six Settings roots.
{ view: 'ApiKeySettingsView.vue', selector: '.paper-api-keys', source: apiKeysSource },
{ view: 'AppearanceSettingsView.vue', selector: '.paper-appearance', source: appearanceSource },
{ view: 'BoardAccessView.vue', selector: '.paper-access', source: boardAccessSource },
{ view: 'ExportImportView.vue', selector: '.paper-portability', source: exportImportSource },
{ view: 'NotificationPreferencesView.vue', selector: '.paper-prefs', source: notificationPrefsSource },
{ view: 'ProfileSettingsView.vue', selector: '.paper-profile', source: profileSource },
// PR #1810 — the secondary views.
{ view: 'AgentRunDetailView.vue', selector: '.paper-run-detail', source: agentRunDetailSource },
{ view: 'AgentRunsView.vue', selector: '.paper-agent-runs', source: agentRunsSource },
{ view: 'AgentsView.vue', selector: '.paper-agents', source: agentsSource },
{ view: 'ArchiveView.vue', selector: '.paper-archive', source: archiveSource },
{ view: 'AutomationChatView.vue', selector: '.paper-chat', source: automationChatSource },
{ view: 'AutomationQueueView.vue', selector: '.paper-queue', source: automationQueueSource },
{ view: 'DevToolsView.vue', selector: '.paper-devtools', source: devToolsSource },
{ view: 'IntegrationsView.vue', selector: '.paper-int', source: integrationsSource },
{ view: 'NotFoundView.vue', selector: '.paper-not-found', source: notFoundSource },
{ view: 'NotificationInboxView.vue', selector: '.paper-notifications', source: notificationInboxSource },
{ view: 'OpsConsoleView.vue', selector: '.paper-ops', source: opsConsoleSource },
]
type PaperViewName = (typeof PAPER_VIEW_ROOTS)[number]['view']

const VIEW_SOURCES: Readonly<Record<PaperViewName, string>> = {
'ActivityView.vue': activitySource,
'AgentRunDetailView.vue': agentRunDetailSource,
'AgentRunsView.vue': agentRunsSource,
'AgentsView.vue': agentsSource,
'ApiKeySettingsView.vue': apiKeysSource,
'AppearanceSettingsView.vue': appearanceSource,
'ArchiveView.vue': archiveSource,
'AutomationChatView.vue': automationChatSource,
'AutomationQueueView.vue': automationQueueSource,
'BoardAccessView.vue': boardAccessSource,
'BoardsListView.vue': boardsSource,
'CalendarView.vue': calendarSource,
'DevToolsView.vue': devToolsSource,
'ExportImportView.vue': exportImportSource,
'IntegrationsView.vue': integrationsSource,
'MetricsView.vue': metricsSource,
'NotFoundView.vue': notFoundSource,
'NotificationInboxView.vue': notificationInboxSource,
'NotificationPreferencesView.vue': notificationPrefsSource,
'OpsConsoleView.vue': opsConsoleSource,
'ProfileSettingsView.vue': profileSource,
'SavedViewsView.vue': savedViewsSource,
}

const VIEW_ROOTS = PAPER_VIEW_ROOTS.map(({ view, selector }) => ({
view,
selector,
source: VIEW_SOURCES[view],
}))

/**
* A root satisfies the invariant by painting ANY Paper substrate token, not
Expand Down
32 changes: 5 additions & 27 deletions frontend/taskdeck-web/tests/paper-eyebrow-token.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { fileURLToPath } from 'node:url'

import { describe, expect, it } from 'vitest'

import { PAPER_VIEW_ROOTS } from '../src/tests/views/paperRootInventory'

/**
* Canonical eyebrow token across the Paper view roots (#1842, ADR-0053).
*
Expand Down Expand Up @@ -47,33 +49,9 @@ const CANONICAL_EYEBROW_TOKEN = '--mute'
/** Its literal fallback, for Legacy ("Paper off") where Paper vars are absent. */
const CANONICAL_EYEBROW_FALLBACK = '#635c4e'

/**
* Every Paper-idiom view root that has a page-header eyebrow. Mirrors the roots
* in `src/tests/views/paperViewLegacySubstrate.spec.ts`, minus
* `AutomationChatView` and `DevToolsView`, which have no eyebrow.
*/
const EYEBROW_ROOTS: ReadonlyArray<[view: string, rule: string]> = [
['ActivityView.vue', '.paper-activity__eyebrow'],
['AgentRunDetailView.vue', '.paper-run-detail__eyebrow'],
['AgentRunsView.vue', '.paper-agent-runs__eyebrow'],
['AgentsView.vue', '.paper-agents__eyebrow'],
['ApiKeySettingsView.vue', '.paper-api-keys__eyebrow'],
['AppearanceSettingsView.vue', '.paper-appearance__eyebrow'],
['ArchiveView.vue', '.paper-archive__eyebrow'],
['AutomationQueueView.vue', '.paper-queue__eyebrow'],
['BoardAccessView.vue', '.paper-access__eyebrow'],
['BoardsListView.vue', '.paper-boards__eyebrow'],
['CalendarView.vue', '.paper-calendar__eyebrow'],
['ExportImportView.vue', '.paper-portability__eyebrow'],
['IntegrationsView.vue', '.paper-int__eyebrow'],
['MetricsView.vue', '.paper-metrics__eyebrow'],
['NotFoundView.vue', '.paper-not-found__eyebrow'],
['NotificationInboxView.vue', '.paper-notifications__eyebrow'],
['NotificationPreferencesView.vue', '.paper-prefs__eyebrow'],
['OpsConsoleView.vue', '.paper-ops__eyebrow'],
['ProfileSettingsView.vue', '.paper-profile__eyebrow'],
['SavedViewsView.vue', '.paper-views__eyebrow'],
]
const EYEBROW_ROOTS = PAPER_VIEW_ROOTS.flatMap(({ view, eyebrow }) =>
eyebrow === undefined ? [] : ([[view, eyebrow]] as const),
)

/** The four core-loop surfaces, and the eyebrow markup each was measured at. */
const CORE_LOOP_SURFACES: ReadonlyArray<[surface: string, file: string]> = [
Expand Down
Loading