@nuxtseo/design-system 0.0.0-stage → 0.1.1

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.
Files changed (219) hide show
  1. package/README.md +181 -2
  2. package/app/app.config.ts +592 -0
  3. package/app/component-attributes.d.ts +8 -0
  4. package/app/components/README.md +38 -0
  5. package/app/components/brand/UiBrandMark.vue +63 -0
  6. package/app/components/brand/UiModuleIcon.vue +45 -0
  7. package/app/components/brand/UiProLettering.vue +26 -0
  8. package/app/components/brand/UiRangeTerrain.vue +311 -0
  9. package/app/components/brand/range-glyphs.ts +18 -0
  10. package/app/components/container/BrowserTab.vue +145 -0
  11. package/app/components/container/IntegrationRow.vue +120 -0
  12. package/app/components/container/KnowDoRails.vue +40 -0
  13. package/app/components/container/RailPanel.vue +93 -0
  14. package/app/components/container/UiAlert.vue +120 -0
  15. package/app/components/container/UiAppShell.vue +165 -0
  16. package/app/components/container/UiCard.vue +204 -0
  17. package/app/components/container/UiDisclosure.vue +43 -0
  18. package/app/components/container/UiEmptyState.vue +102 -0
  19. package/app/components/container/UiErrorState.vue +61 -0
  20. package/app/components/container/UiLoadingState.vue +20 -0
  21. package/app/components/container/UiPageHeader.vue +95 -0
  22. package/app/components/container/UiPopover.vue +102 -0
  23. package/app/components/container/UiSampleDataOverlay.vue +47 -0
  24. package/app/components/container/UiSectionHeader.vue +83 -0
  25. package/app/components/container/UiSummaryStrip.vue +40 -0
  26. package/app/components/container/UiToast.vue +230 -0
  27. package/app/components/container/UiToaster.vue +271 -0
  28. package/app/components/container/UiWidgetState.vue +143 -0
  29. package/app/components/container/ui-nav-drawer.ts +48 -0
  30. package/app/components/data/ActionRail.vue +152 -0
  31. package/app/components/data/FacetStrip.vue +93 -0
  32. package/app/components/data/UiBarCell.vue +37 -0
  33. package/app/components/data/UiBarChart.vue +281 -0
  34. package/app/components/data/UiChartAnnotationControls.vue +74 -0
  35. package/app/components/data/UiChartAnnotations.vue +163 -0
  36. package/app/components/data/UiChartBrushOverlay.vue +96 -0
  37. package/app/components/data/UiChartDataReveal.vue +81 -0
  38. package/app/components/data/UiChartFrame.vue +123 -0
  39. package/app/components/data/UiChartTooltip.vue +81 -0
  40. package/app/components/data/UiCoverageNote.vue +90 -0
  41. package/app/components/data/UiCwvSparkline.vue +188 -0
  42. package/app/components/data/UiDataList.vue +276 -0
  43. package/app/components/data/UiDataTableSection.vue +233 -0
  44. package/app/components/data/UiDistributionBars.vue +164 -0
  45. package/app/components/data/UiDonutChart.vue +132 -0
  46. package/app/components/data/UiDoorwayColumn.vue +108 -0
  47. package/app/components/data/UiDoorwayGrid.vue +58 -0
  48. package/app/components/data/UiDoorwayRow.vue +83 -0
  49. package/app/components/data/UiEntitySummary.vue +261 -0
  50. package/app/components/data/UiFactsGrid.vue +107 -0
  51. package/app/components/data/UiImpactMeter.vue +33 -0
  52. package/app/components/data/UiIssueRow.vue +149 -0
  53. package/app/components/data/UiLineChart.vue +431 -0
  54. package/app/components/data/UiMetricBarPopover.vue +121 -0
  55. package/app/components/data/UiMetricLabel.vue +30 -0
  56. package/app/components/data/UiMetricStat.vue +73 -0
  57. package/app/components/data/UiMetricToggle.vue +156 -0
  58. package/app/components/data/UiMetricsRow.vue +64 -0
  59. package/app/components/data/UiPositionMetric.vue +53 -0
  60. package/app/components/data/UiRelatedFixes.vue +113 -0
  61. package/app/components/data/UiRichText.vue +325 -0
  62. package/app/components/data/UiScatterPlot.vue +144 -0
  63. package/app/components/data/UiSeverityMarker.vue +30 -0
  64. package/app/components/data/UiSparkColumns.vue +160 -0
  65. package/app/components/data/UiSparkline.vue +681 -0
  66. package/app/components/data/UiStat.vue +445 -0
  67. package/app/components/data/UiStats.vue +76 -0
  68. package/app/components/data/UiTable.vue +517 -0
  69. package/app/components/data/UiTableExport.vue +40 -0
  70. package/app/components/data/UiTableFrame.vue +149 -0
  71. package/app/components/data/UiTablePagination.vue +37 -0
  72. package/app/components/data/UiTableShell.vue +48 -0
  73. package/app/components/data/UiTableTd.vue +39 -0
  74. package/app/components/data/UiTableTh.vue +61 -0
  75. package/app/components/data/UiTopEntityStackChart.vue +393 -0
  76. package/app/components/data/cells/UiTableDash.vue +3 -0
  77. package/app/components/data/cells/UiTableHeaderCell.vue +72 -0
  78. package/app/components/data/cells/UiTableMetricCell.vue +72 -0
  79. package/app/components/data/cells/UiTablePathCell.vue +76 -0
  80. package/app/components/data/cells/UiTableScoreTile.vue +49 -0
  81. package/app/components/data/cells/UiTableTrendCell.vue +41 -0
  82. package/app/components/element/UiAtmosphere.vue +64 -0
  83. package/app/components/element/UiAuthHeading.vue +24 -0
  84. package/app/components/element/UiAuthProviders.vue +46 -0
  85. package/app/components/element/UiBrandIcon.vue +39 -0
  86. package/app/components/element/UiButton.vue +549 -0
  87. package/app/components/element/UiCelebration.vue +65 -0
  88. package/app/components/element/UiChannelChips.vue +62 -0
  89. package/app/components/element/UiChatQuickLink.vue +319 -0
  90. package/app/components/element/UiChip.vue +101 -0
  91. package/app/components/element/UiConfirmDialog.vue +84 -0
  92. package/app/components/element/UiCopyButton.vue +90 -0
  93. package/app/components/element/UiDitherAvatar.vue +177 -0
  94. package/app/components/element/UiDotLabel.vue +19 -0
  95. package/app/components/element/UiEjectMenu.vue +181 -0
  96. package/app/components/element/UiEjectOverlay.vue +29 -0
  97. package/app/components/element/UiFavicon.vue +235 -0
  98. package/app/components/element/UiFaviconStack.vue +84 -0
  99. package/app/components/element/UiFilterMenu.vue +324 -0
  100. package/app/components/element/UiFocusedSurface.vue +129 -0
  101. package/app/components/element/UiHealthDot.vue +32 -0
  102. package/app/components/element/UiHelpLabel.vue +28 -0
  103. package/app/components/element/UiIcon.vue +22 -0
  104. package/app/components/element/UiInput.vue +35 -0
  105. package/app/components/element/UiLogoPlausible.vue +12 -0
  106. package/app/components/element/UiLogoPro.vue +25 -0
  107. package/app/components/element/UiLogoUmami.vue +12 -0
  108. package/app/components/element/UiNavIcon.vue +107 -0
  109. package/app/components/element/UiNavList.vue +156 -0
  110. package/app/components/element/UiOnboardingAnswers.vue +127 -0
  111. package/app/components/element/UiOverflowCount.vue +31 -0
  112. package/app/components/element/UiProBadge.vue +19 -0
  113. package/app/components/element/UiProgressCircle.vue +79 -0
  114. package/app/components/element/UiProgressPercent.vue +35 -0
  115. package/app/components/element/UiRelativeTime.vue +58 -0
  116. package/app/components/element/UiScrollFade.vue +16 -0
  117. package/app/components/element/UiSelect.vue +68 -0
  118. package/app/components/element/UiSeverityDot.vue +27 -0
  119. package/app/components/element/UiSiteFavicon.vue +24 -0
  120. package/app/components/element/UiSkeleton.vue +148 -0
  121. package/app/components/element/UiSlotText.vue +65 -0
  122. package/app/components/element/UiSourceLogos.vue +113 -0
  123. package/app/components/element/UiSourceRail.vue +71 -0
  124. package/app/components/element/UiStaticTerminal.vue +107 -0
  125. package/app/components/element/UiStatusBadge.vue +62 -0
  126. package/app/components/element/UiSyncDot.vue +46 -0
  127. package/app/components/element/UiTabs.vue +245 -0
  128. package/app/components/element/UiTogglePill.vue +86 -0
  129. package/app/components/element/UiTooltip.vue +390 -0
  130. package/app/components/element/UiTooltipGrid.vue +457 -0
  131. package/app/components/element/UiTrend.vue +184 -0
  132. package/app/components/element/UiTrialBenefits.vue +71 -0
  133. package/app/components/element/UiUrlLabel.vue +75 -0
  134. package/app/components/element/UiValenceDot.vue +37 -0
  135. package/app/components/element/UiWizardModal.vue +284 -0
  136. package/app/components/element/UiWizardNav.vue +59 -0
  137. package/app/components/element/UiWizardProgress.vue +78 -0
  138. package/app/components/element/UiWizardShell.vue +43 -0
  139. package/app/components/element/eject-menu.ts +359 -0
  140. package/app/components/element/eject-tracker.ts +34 -0
  141. package/app/components/element/onboarding-answers.ts +93 -0
  142. package/app/components/element/onboarding-intents.ts +19 -0
  143. package/app/composables/dataVizColors.ts +350 -0
  144. package/app/composables/formatting.ts +252 -0
  145. package/app/composables/semanticColors.ts +80 -0
  146. package/app/composables/useActivityOverlay.ts +55 -0
  147. package/app/composables/useAppShellNav.ts +21 -0
  148. package/app/composables/useChartBrush.ts +221 -0
  149. package/app/composables/useChartHover.ts +153 -0
  150. package/app/composables/useChartTickPlan.ts +186 -0
  151. package/app/composables/useCloseOnNavigation.ts +7 -0
  152. package/app/composables/useConfirm.ts +38 -0
  153. package/app/composables/useEdgeFade.ts +37 -0
  154. package/app/composables/useEject.ts +69 -0
  155. package/app/composables/useEntranceMotion.ts +22 -0
  156. package/app/composables/useFaviconBacking.ts +164 -0
  157. package/app/composables/useGridCols.ts +39 -0
  158. package/app/composables/useIssueOverlay.ts +43 -0
  159. package/app/composables/useOnboardingService.ts +106 -0
  160. package/app/composables/useOpenedOnce.ts +19 -0
  161. package/app/composables/useScrollSpy.ts +178 -0
  162. package/app/composables/useSquircleFallback.ts +21 -0
  163. package/app/composables/useWizardRoute.ts +123 -0
  164. package/app/plugins/favicon.ts +15 -0
  165. package/app/plugins/lazy-motion.ts +22 -0
  166. package/app/plugins/selection-highlight.client.ts +176 -0
  167. package/app/shared/cli.ts +10 -0
  168. package/app/shared/error-state.ts +150 -0
  169. package/app/shared/icons.ts +1 -0
  170. package/app/shared/motion.ts +1 -0
  171. package/app/shared/nav.ts +38 -0
  172. package/app/shared/navigation.ts +5 -0
  173. package/app/shared/static-terminal.ts +14 -0
  174. package/app/shared/table-export.ts +36 -0
  175. package/app/shared/table.ts +105 -0
  176. package/app/shared/ui-icons.ts +1 -0
  177. package/app/shared/view-state.ts +85 -0
  178. package/app/shared/widget-failure.ts +126 -0
  179. package/app/utils/app-favicon.ts +64 -0
  180. package/app/utils/chartAnnotations.ts +326 -0
  181. package/app/utils/countries.ts +337 -0
  182. package/app/utils/export-download.ts +100 -0
  183. package/app/utils/favicon-fallback.ts +12 -0
  184. package/app/utils/icon-color.ts +105 -0
  185. package/app/utils/number.ts +74 -0
  186. package/app/utils/render-link.ts +14 -0
  187. package/app/utils/slot-text.ts +18 -0
  188. package/app/utils/squircle.ts +154 -0
  189. package/app/utils/topEntityStack.ts +197 -0
  190. package/app/utils/vue.ts +57 -0
  191. package/build/design-system.ts +69 -0
  192. package/build/external-links.ts +38 -0
  193. package/build/fonts.ts +39 -0
  194. package/build/icon-server-bundle.ts +276 -0
  195. package/build/runtime/icon-server-bundle.ts +29 -0
  196. package/css/global.css +1896 -0
  197. package/nuxt.config.ts +143 -0
  198. package/package.json +93 -3
  199. package/public/fonts/README.md +33 -0
  200. package/public/fonts/fira-code-OFL.txt +93 -0
  201. package/public/fonts/fira-code-var-latin.woff2 +0 -0
  202. package/public/fonts/hubot-sans-OFL.txt +93 -0
  203. package/public/fonts/hubot-sans-var-latin.woff2 +0 -0
  204. package/server/api/favicon.get.ts +122 -0
  205. package/server/utils/favicon-source.ts +51 -0
  206. package/shared/contracts/feedback.ts +42 -0
  207. package/shared/contracts/index.ts +1 -0
  208. package/shared/delivery-channels.ts +53 -0
  209. package/shared/icons/index.ts +143 -0
  210. package/shared/icons/registry.ts +347 -0
  211. package/shared/icons/ui-slots.ts +58 -0
  212. package/shared/markdown.ts +44 -0
  213. package/shared/motion.ts +120 -0
  214. package/shared/rangi.ts +60 -0
  215. package/shared/rich-text.ts +34 -0
  216. package/shared/ui-icons.ts +6 -0
  217. package/shared/utils/urls.ts +64 -0
  218. package/types/index.ts +1 -0
  219. package/types/ui.ts +339 -0
@@ -0,0 +1,107 @@
1
+ <script setup lang="ts">
2
+ import type { SemanticStatus } from '../../composables/semanticColors'
3
+ import { NuxtLink, UiHelpLabel, UiSkeleton } from '#components'
4
+ import { semanticColors } from '../../composables/semanticColors'
5
+
6
+ /**
7
+ * UiFactsGrid — the quiet definition-list facts grid (label-over-value).
8
+ * Extracted from the hand-rolled `<dl>` in per-URL / scan / entity headers.
9
+ *
10
+ * Color budget: a fact's `status` renders as a calm LEADING DOT + neutral value
11
+ * (never a tinted value), so a grid of facts can't spend the page's color budget
12
+ * on non-events. `mono` is for IDs / hashes / URLs / raw machine strings ONLY —
13
+ * numerals stay `.numerals-display` at the call site, never mono.
14
+ */
15
+
16
+ export interface FactItem {
17
+ label: string
18
+ value?: string | number | null
19
+ /** Preserve the fact cell while its value is being resolved. */
20
+ loading?: boolean
21
+ /** Semantic status — renders a calm leading dot; never tints the value. */
22
+ status?: SemanticStatus
23
+ /** Mono value — IDs / hashes / raw machine strings ONLY (not URLs, not numerals). */
24
+ mono?: boolean
25
+ /** Span the full row (long values like a canonical URL). */
26
+ span?: boolean
27
+ /** Wrap long prose instead of truncating it. */
28
+ wrap?: boolean
29
+ /** Link the value (row-level navigation is fine; the value is not an @click). */
30
+ to?: string
31
+ /** Contextual help — renders a (?) UiHelpLabel on the fact label. */
32
+ tooltip?: string
33
+ /** Bold title in the help tooltip. Defaults to the label. */
34
+ tooltipTitle?: string
35
+ /** Full value shown on hover when the visible value is shortened. */
36
+ title?: string
37
+ }
38
+
39
+ const { facts, columns = 3 } = defineProps<{
40
+ facts: FactItem[]
41
+ /** Max columns at sm+ (mobile is always 2). Default 3. */
42
+ columns?: 2 | 3
43
+ }>()
44
+ </script>
45
+
46
+ <template>
47
+ <dl
48
+ data-ui="UiFactsGrid"
49
+ class="grid grid-cols-2 gap-x-6 gap-y-3"
50
+ :class="columns === 3 ? 'sm:grid-cols-3' : 'sm:grid-cols-2'"
51
+ >
52
+ <div
53
+ v-for="fact in facts"
54
+ :key="fact.label"
55
+ class="min-w-0"
56
+ :class="fact.span ? 'col-span-full' : ''"
57
+ >
58
+ <!--
59
+ `UiHelpLabel` wraps its text in a UiTooltip trigger BUTTON, whose own
60
+ type classes beat the inherited `.text-label` spec. Without this a fact
61
+ that carries a tooltip renders sentence-case at a different size than
62
+ the fact beside it, so one grid shows two label styles. Every property
63
+ inherits from `.text-label` rather than naming a case, so a site that
64
+ restyles the token restyles both kinds of fact.
65
+ -->
66
+ <dt class="text-label [&_button]:font-[inherit] [&_button]:text-[inherit] [&_button]:[text-transform:inherit] [&_button]:tracking-[inherit]">
67
+ <UiHelpLabel
68
+ v-if="fact.tooltip"
69
+ :text="fact.label"
70
+ :tooltip="fact.tooltip"
71
+ :tooltip-title="fact.tooltipTitle"
72
+ />
73
+ <template v-else>
74
+ {{ fact.label }}
75
+ </template>
76
+ </dt>
77
+ <dd class="mt-0.5 flex items-center gap-1.5 text-sm">
78
+ <span
79
+ v-if="fact.status && !fact.loading"
80
+ class="size-1.5 shrink-0 rounded-full"
81
+ :class="semanticColors[fact.status].dot"
82
+ aria-hidden="true"
83
+ />
84
+ <UiSkeleton
85
+ v-if="fact.loading"
86
+ type="text"
87
+ :base="80"
88
+ :range="20"
89
+ class="max-w-28"
90
+ />
91
+ <component
92
+ :is="fact.to ? NuxtLink : 'span'"
93
+ v-else
94
+ :to="fact.to || undefined"
95
+ :class="[
96
+ fact.wrap ? 'break-words' : 'truncate',
97
+ fact.mono ? 'font-mono text-muted' : 'text-default',
98
+ fact.to ? 'underline-offset-2 hover:text-default hover:underline' : '',
99
+ ]"
100
+ :title="fact.title ?? (fact.value != null ? String(fact.value) : undefined)"
101
+ >
102
+ {{ fact.value ?? '—' }}
103
+ </component>
104
+ </dd>
105
+ </div>
106
+ </dl>
107
+ </template>
@@ -0,0 +1,33 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * UiImpactMeter — monochrome 3-bar magnitude glyph (ascending signal bars).
4
+ * The magnitude counterpart of the severity dot/bar: severity says "how
5
+ * broken", the meter says "how big" (an opportunity's upside, a action's
6
+ * effort). Shape carries the scale, never color (Icons color rule) — filled
7
+ * bars read as dimmed text, empty bars as border. The owning layer maps its
8
+ * domain figure to the 1–3 tier (design-system knows shapes, not traffic or
9
+ * effort thresholds); the `label` names which magnitude this instance encodes.
10
+ */
11
+
12
+ const { tier, label } = defineProps<{
13
+ /** 1 (low) – 3 (high). */
14
+ tier: 1 | 2 | 3
15
+ /** Accessible + tooltip label, e.g. "Projected impact: high". */
16
+ label?: string
17
+ }>()
18
+
19
+ const BAR_HEIGHT = ['h-1', 'h-1.5', 'h-2'] as const
20
+ </script>
21
+
22
+ <template>
23
+ <span class="inline-flex items-end gap-px" :title="label">
24
+ <span v-if="label" class="sr-only">{{ label }}</span>
25
+ <span
26
+ v-for="i in 3"
27
+ :key="i"
28
+ class="w-[3px] rounded-[1px]"
29
+ :class="[BAR_HEIGHT[i - 1], i <= tier ? 'bg-[var(--ui-text-dimmed)]' : 'bg-[var(--ui-border)]']"
30
+ aria-hidden="true"
31
+ />
32
+ </span>
33
+ </template>
@@ -0,0 +1,149 @@
1
+ <script setup lang="ts">
2
+ import type { RichSegment } from '../../../shared/rich-text'
3
+ import type { FaviconStackSite } from '../element/UiFaviconStack.vue'
4
+ import { computed } from 'vue'
5
+ import { UiFaviconStack, UiIcon, UiImpactMeter, UiNavIcon, UiRichText, UiSeverityMarker } from '#components'
6
+ /**
7
+ * The shared issue and opportunity row. Diagnosis leads. Source identity is
8
+ * available in regular layouts. Compact rows rely on their page context. A
9
+ * regular row may add a Site favicon for portfolio decisions.
10
+ *
11
+ * The row reads on ONE line from `sm` up. State ("New", "In progress") is an
12
+ * inline mark after the diagnosis, the Site and the value are right-aligned,
13
+ * and only a caller-supplied `evidence` string opens a second line. The parent
14
+ * owns interaction. `interactive` adds the matching hover and chevron without
15
+ * creating nested controls.
16
+ */
17
+
18
+ const {
19
+ title,
20
+ richSegments,
21
+ evidence,
22
+ severity,
23
+ showSeverity = true,
24
+ syncing = false,
25
+ subdued = false,
26
+ impact,
27
+ impactLabel,
28
+ density = 'compact',
29
+ glyphIcon,
30
+ glyphHint,
31
+ value,
32
+ novelty,
33
+ status,
34
+ site,
35
+ interactive = false,
36
+ } = defineProps<{
37
+ title: string
38
+ /**
39
+ * Typed headline tokens (`actionDisplay().headline`), rendered via `UiRichText`.
40
+ * Falls back to the plain `title` string when omitted.
41
+ */
42
+ richSegments?: RichSegment[]
43
+ /** One dim secondary line; suppressed when it merely repeats the title. */
44
+ evidence?: string | null
45
+ /** The board's deterministic severity, shown with a distinct marker shape. */
46
+ severity: 'error' | 'warning' | 'info'
47
+ /**
48
+ * Render the severity bar. Off for opportunity lists, where severity is
49
+ * uniformly `info` — an identical bar on every row carries no signal (the
50
+ * `value` column is the differentiator there). On for Issues, where it varies.
51
+ */
52
+ showSeverity?: boolean
53
+ /**
54
+ * The engine is checking this row (a claimed change awaiting verification).
55
+ * Renders a pulsing dot in the marker slot: the row is not a live fault to
56
+ * triage, so it must not read like one. The caller turns severity off.
57
+ */
58
+ syncing?: boolean
59
+ /**
60
+ * Opportunity impact tier — renders a `UiImpactMeter` in the severity slot.
61
+ * Pass only on PURE opportunity lists (every row carries one), never in a
62
+ * mixed list where issues rows lead with the thinner severity bar — the two
63
+ * glyph widths would stagger the title column.
64
+ */
65
+ impact?: 1 | 2 | 3 | null
66
+ /** Tooltip + sr-only label for the impact meter. */
67
+ impactLabel?: string
68
+ density?: 'compact' | 'regular'
69
+ /**
70
+ * Lower the diagnosis weight for rows listed under a pattern header. The
71
+ * readable 14px size stays unchanged.
72
+ */
73
+ subdued?: boolean
74
+ /** `regular` fallback when the `leading` slot is empty. */
75
+ glyphIcon?: string
76
+ glyphHint?: string
77
+ /** The row's primary stat — the bold top of the right cell (e.g. "~17K/mo"). */
78
+ value?: string | null
79
+ /**
80
+ * "New" / "Returned" — a quiet inline mark after the diagnosis. It used to
81
+ * open a second line, which cost the row its whole height to carry one word.
82
+ */
83
+ novelty?: string | null
84
+ /** Progress label, when the row itself owns that context. */
85
+ status?: string | null
86
+ /**
87
+ * Cross-site identity, shown as one framed favicon in the right column,
88
+ * before the value and the chevron. Spelled out, the Site name was a second
89
+ * text column that fought the diagnosis for the same truncating line; the
90
+ * favicon says the same thing in 20px and keeps the name one hover away.
91
+ */
92
+ site?: FaviconStackSite | null
93
+ /** True when the parent is a link or button. Controls hover and the chevron. */
94
+ interactive?: boolean
95
+ }>()
96
+
97
+ // The dim secondary line drops a duplicate of the title.
98
+ const evidenceText = computed(() => (evidence && evidence.trim() !== title.trim() ? evidence.trim() : null))
99
+ // Only state that changes the next choice belongs in the summary row, and it
100
+ // reads on the HORIZONTAL plane: one word ("New", "In progress") is not worth
101
+ // a second line, and stacked down a list it painted a stripe rather than a
102
+ // signal. Callers already suppress a mark every row would carry.
103
+ const stateMark = computed(() => status || novelty)
104
+ </script>
105
+
106
+ <template>
107
+ <div
108
+ class="flex min-h-11 items-start gap-2.5 py-2.5 pl-4 pr-3.5 transition-colors sm:min-h-0 sm:items-center"
109
+ :class="interactive ? 'hover:bg-elevated/60' : ''"
110
+ >
111
+ <div class="flex min-w-0 flex-1 flex-col gap-1">
112
+ <!-- Severity, Source, then the diagnosis. -->
113
+ <p class="flex min-w-0 items-start gap-1.5 text-sm leading-5 text-highlighted sm:items-center" :class="subdued ? 'font-normal' : 'font-medium'">
114
+ <UiSyncDot v-if="syncing" status="syncing" size="2" class="shrink-0" />
115
+ <UiSeverityMarker v-else-if="showSeverity" :severity />
116
+ <UiImpactMeter v-else-if="impact" :tier="impact" :label="impactLabel" class="shrink-0" />
117
+ <span v-if="density !== 'compact' && ($slots.leading || glyphIcon)" :title="$slots.leading ? undefined : glyphHint" class="mt-0.5 shrink-0 sm:mt-0">
118
+ <slot name="leading">
119
+ <UiNavIcon :icon="glyphIcon!" size="xs" />
120
+ </slot>
121
+ </span>
122
+ <!-- No `flex-1`: the mark below must hug the diagnosis it qualifies.
123
+ Grown to fill, the title pushed the mark to the far right of this
124
+ column, where it read as a prefix of the value. -->
125
+ <span class="min-w-0 line-clamp-2 sm:line-clamp-1">
126
+ <UiRichText v-if="richSegments?.length" :segments="richSegments" size="sm" inert-links truncate />
127
+ <template v-else>
128
+ {{ title }}
129
+ </template>
130
+ </span>
131
+ <!-- Neutral text, never a tinted badge: these repeat down a list. -->
132
+ <span v-if="stateMark" class="shrink-0 text-mini font-medium uppercase tracking-wide text-dimmed">{{ stateMark }}</span>
133
+ </p>
134
+ <!-- Evidence stays a line of its own; the value falls under the title
135
+ only where the right column is hidden. -->
136
+ <p v-if="evidenceText || value" class="min-w-0 text-sm text-muted">
137
+ <span v-if="evidenceText" class="block truncate">{{ evidenceText }}</span>
138
+ <span v-if="value" class="mt-0.5 block truncate font-medium tabular-nums text-default sm:hidden">{{ value }}</span>
139
+ </p>
140
+ </div>
141
+
142
+ <!-- Site, Impact and the interaction cue earn a right column. -->
143
+ <div v-if="site || value || interactive" class="flex shrink-0 items-center gap-2.5 pt-0.5 sm:pt-0">
144
+ <UiFaviconStack v-if="site" :sites="[site]" size="xs" />
145
+ <span v-if="value" class="hidden whitespace-nowrap text-sm font-medium tabular-nums text-default sm:inline">{{ value }}</span>
146
+ <UiIcon v-if="interactive" name="chevron-right" class="size-3.5 shrink-0 text-muted" aria-hidden="true" />
147
+ </div>
148
+ </div>
149
+ </template>