@ternent/core 0.0.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 (313) hide show
  1. package/.changeset/README.md +8 -0
  2. package/.changeset/config.json +17 -0
  3. package/.github/workflows/deploy-armour.yml +42 -0
  4. package/.github/workflows/deploy-identity.yml +42 -0
  5. package/.github/workflows/deploy-seal.yml +42 -0
  6. package/.github/workflows/deploy-ui.yml +42 -0
  7. package/.github/workflows/deploy-utils.yml +42 -0
  8. package/.github/workflows/release-create.yml +59 -0
  9. package/.github/workflows/release-publish.yml +54 -0
  10. package/.nvmrc +1 -0
  11. package/.ops/publish.mjs +31 -0
  12. package/package.json +16 -0
  13. package/packages/README.md +0 -0
  14. package/packages/armour/CHANGELOG.md +66 -0
  15. package/packages/armour/CLAUDE.md +8 -0
  16. package/packages/armour/README.md +103 -0
  17. package/packages/armour/SPEC.md +92 -0
  18. package/packages/armour/package.json +45 -0
  19. package/packages/armour/src/constants.ts +5 -0
  20. package/packages/armour/src/deps.d.ts +56 -0
  21. package/packages/armour/src/errors.ts +172 -0
  22. package/packages/armour/src/files.ts +73 -0
  23. package/packages/armour/src/identity.ts +72 -0
  24. package/packages/armour/src/index.ts +56 -0
  25. package/packages/armour/src/init.ts +10 -0
  26. package/packages/armour/src/passphrase.ts +33 -0
  27. package/packages/armour/src/recipients.ts +73 -0
  28. package/packages/armour/src/text.ts +68 -0
  29. package/packages/armour/src/types.ts +93 -0
  30. package/packages/armour/test/armour.test.ts +270 -0
  31. package/packages/armour/tsconfig.build.json +12 -0
  32. package/packages/armour/tsconfig.json +12 -0
  33. package/packages/armour/vite.config.ts +29 -0
  34. package/packages/concord/CHANGELOG.md +83 -0
  35. package/packages/concord/CLAUDE.md +9 -0
  36. package/packages/concord/README.md +146 -0
  37. package/packages/concord/SPEC.md +287 -0
  38. package/packages/concord/package.json +51 -0
  39. package/packages/concord/src/app.ts +717 -0
  40. package/packages/concord/src/errors.ts +9 -0
  41. package/packages/concord/src/index.ts +20 -0
  42. package/packages/concord/src/types.ts +127 -0
  43. package/packages/concord/test/concord.test.ts +978 -0
  44. package/packages/concord/tsconfig.json +12 -0
  45. package/packages/concord/vite.browser.config.ts +27 -0
  46. package/packages/concord/vite.config.ts +35 -0
  47. package/packages/concord/vite.config.ts.timestamp-1774262297922-ffd76e35ea668.mjs +83 -0
  48. package/packages/identity/CHANGELOG.md +47 -0
  49. package/packages/identity/README.md +236 -0
  50. package/packages/identity/package.json +41 -0
  51. package/packages/identity/src/index.ts +538 -0
  52. package/packages/identity/test/identity.test.ts +172 -0
  53. package/packages/identity/tsconfig.build.json +12 -0
  54. package/packages/identity/vite.config.ts +17 -0
  55. package/packages/ledger/CHANGELOG.md +69 -0
  56. package/packages/ledger/CLAUDE.md +9 -0
  57. package/packages/ledger/SPEC.md +304 -0
  58. package/packages/ledger/package.json +48 -0
  59. package/packages/ledger/src/index.ts +2 -0
  60. package/packages/ledger/src/ledger.ts +1286 -0
  61. package/packages/ledger/src/seal-cli.d.ts +25 -0
  62. package/packages/ledger/src/types.ts +294 -0
  63. package/packages/ledger/test/ledger.test.ts +838 -0
  64. package/packages/ledger/tsconfig.json +12 -0
  65. package/packages/ledger/vite.browser.config.ts +27 -0
  66. package/packages/ledger/vite.config.ts +39 -0
  67. package/packages/seal/CHANGELOG.md +137 -0
  68. package/packages/seal/CLAUDE.md +8 -0
  69. package/packages/seal/README.md +258 -0
  70. package/packages/seal/bin/seal +6 -0
  71. package/packages/seal/package.json +59 -0
  72. package/packages/seal/src/artifact.ts +380 -0
  73. package/packages/seal/src/cli.ts +372 -0
  74. package/packages/seal/src/commands/identity.ts +52 -0
  75. package/packages/seal/src/commands/manifest.ts +71 -0
  76. package/packages/seal/src/commands/publicKey.ts +7 -0
  77. package/packages/seal/src/commands/sign.ts +56 -0
  78. package/packages/seal/src/commands/verify.ts +54 -0
  79. package/packages/seal/src/crypto.ts +85 -0
  80. package/packages/seal/src/errors.ts +88 -0
  81. package/packages/seal/src/index.ts +5 -0
  82. package/packages/seal/src/manifest.ts +114 -0
  83. package/packages/seal/src/node.ts +18 -0
  84. package/packages/seal/src/proof.ts +344 -0
  85. package/packages/seal/test/artifact.test.ts +86 -0
  86. package/packages/seal/test/cli.test.ts +208 -0
  87. package/packages/seal/test/crypto.test.ts +21 -0
  88. package/packages/seal/test/manifest.test.ts +32 -0
  89. package/packages/seal/test/proof.test.ts +60 -0
  90. package/packages/seal/tsconfig.json +12 -0
  91. package/packages/seal/vite.config.ts +54 -0
  92. package/packages/ui/CHANGELOG.md +393 -0
  93. package/packages/ui/README.md +57 -0
  94. package/packages/ui/jsconfig.json +19 -0
  95. package/packages/ui/package.json +64 -0
  96. package/packages/ui/scripts/check-tokens.js +56 -0
  97. package/packages/ui/scripts/generate-theme-css.mjs +85 -0
  98. package/packages/ui/src/design-system/base.css +8 -0
  99. package/packages/ui/src/design-system/docs/ACCESSIBILITY_RULES.md +186 -0
  100. package/packages/ui/src/design-system/docs/AI_SYSTEM.md +281 -0
  101. package/packages/ui/src/design-system/docs/PATTERN_RULES.md +83 -0
  102. package/packages/ui/src/design-system/docs/PRIMITIVE_RULES.md +258 -0
  103. package/packages/ui/src/design-system/docs/TOKEN_RULES.md +235 -0
  104. package/packages/ui/src/design-system/docs/VISUAL_DIRECTION.md +68 -0
  105. package/packages/ui/src/design-system/foundation.js +420 -0
  106. package/packages/ui/src/design-system/tokens.css +140 -0
  107. package/packages/ui/src/design-system/tokens.js +327 -0
  108. package/packages/ui/src/design-system/utils.js +246 -0
  109. package/packages/ui/src/main.js +4 -0
  110. package/packages/ui/src/patterns/FeatureCard/FeatureCard.spec.md +24 -0
  111. package/packages/ui/src/patterns/FeatureCard/FeatureCard.types.ts +8 -0
  112. package/packages/ui/src/patterns/FeatureCard/FeatureCard.vue +175 -0
  113. package/packages/ui/src/patterns/FormField/FormField.spec.md +65 -0
  114. package/packages/ui/src/patterns/FormField/FormField.types.ts +11 -0
  115. package/packages/ui/src/patterns/FormField/FormField.vue +87 -0
  116. package/packages/ui/src/patterns/IdentityGlyph/IdentityGlyph.vue +61 -0
  117. package/packages/ui/src/patterns/IdentityGlyph/IdentityHandle.vue +58 -0
  118. package/packages/ui/src/patterns/IdentityGlyph/identityGlyph.types.ts +36 -0
  119. package/packages/ui/src/patterns/IdentityGlyph/identityGlyph.utils.ts +585 -0
  120. package/packages/ui/src/patterns/IdentityGlyph/index.ts +5 -0
  121. package/packages/ui/src/patterns/KeyValueList/KeyValueList.spec.md +28 -0
  122. package/packages/ui/src/patterns/KeyValueList/KeyValueList.types.ts +16 -0
  123. package/packages/ui/src/patterns/KeyValueList/KeyValueList.vue +50 -0
  124. package/packages/ui/src/patterns/LandingPage/LandingIcon.vue +90 -0
  125. package/packages/ui/src/patterns/LandingPage/LandingPage.spec.md +24 -0
  126. package/packages/ui/src/patterns/LandingPage/LandingPage.types.ts +212 -0
  127. package/packages/ui/src/patterns/LandingPage/LandingPage.vue +599 -0
  128. package/packages/ui/src/patterns/ListWorkspaceLayout/ListWorkspaceLayout.test.ts +33 -0
  129. package/packages/ui/src/patterns/ListWorkspaceLayout/ListWorkspaceLayout.vue +44 -0
  130. package/packages/ui/src/patterns/Logo/Logo.spec.md +22 -0
  131. package/packages/ui/src/patterns/Logo/Logo.vue +160 -0
  132. package/packages/ui/src/patterns/PageSurface/PageSurface.spec.md +15 -0
  133. package/packages/ui/src/patterns/PageSurface/PageSurface.vue +85 -0
  134. package/packages/ui/src/patterns/PanelChrome/PanelChrome.spec.md +39 -0
  135. package/packages/ui/src/patterns/PanelChrome/PanelChrome.types.ts +1 -0
  136. package/packages/ui/src/patterns/PanelChrome/PanelChrome.vue +187 -0
  137. package/packages/ui/src/patterns/PreviewPanel/PreviewPanel.spec.md +31 -0
  138. package/packages/ui/src/patterns/PreviewPanel/PreviewPanel.types.ts +23 -0
  139. package/packages/ui/src/patterns/PreviewPanel/PreviewPanel.vue +354 -0
  140. package/packages/ui/src/patterns/RecordList/RecordList.spec.md +35 -0
  141. package/packages/ui/src/patterns/RecordList/RecordList.test.ts +42 -0
  142. package/packages/ui/src/patterns/RecordList/RecordList.types.ts +9 -0
  143. package/packages/ui/src/patterns/RecordList/RecordList.utils.ts +5 -0
  144. package/packages/ui/src/patterns/RecordList/RecordList.vue +134 -0
  145. package/packages/ui/src/patterns/SectionClarifier/SectionClarifier.vue +85 -0
  146. package/packages/ui/src/patterns/SectionIntro/SectionIntro.spec.md +25 -0
  147. package/packages/ui/src/patterns/SectionIntro/SectionIntro.types.ts +7 -0
  148. package/packages/ui/src/patterns/SectionIntro/SectionIntro.vue +141 -0
  149. package/packages/ui/src/patterns/SidebarNav/SidebarNav.spec.md +34 -0
  150. package/packages/ui/src/patterns/SidebarNav/SidebarNav.types.ts +17 -0
  151. package/packages/ui/src/patterns/SidebarNav/SidebarNav.vue +110 -0
  152. package/packages/ui/src/patterns/SplitView/SplitView.spec.md +28 -0
  153. package/packages/ui/src/patterns/SplitView/SplitView.test.ts +22 -0
  154. package/packages/ui/src/patterns/SplitView/SplitView.types.ts +3 -0
  155. package/packages/ui/src/patterns/SplitView/SplitView.utils.ts +13 -0
  156. package/packages/ui/src/patterns/SplitView/SplitView.vue +39 -0
  157. package/packages/ui/src/patterns/StepList/StepList.spec.md +15 -0
  158. package/packages/ui/src/patterns/StepList/StepList.types.ts +4 -0
  159. package/packages/ui/src/patterns/StepList/StepList.vue +91 -0
  160. package/packages/ui/src/patterns/Verification/VerificationBadge.vue +97 -0
  161. package/packages/ui/src/patterns/Verification/VerificationComponents.test.ts +153 -0
  162. package/packages/ui/src/patterns/Verification/VerificationDetailsPanel.vue +270 -0
  163. package/packages/ui/src/patterns/Verification/VerificationSummary.vue +171 -0
  164. package/packages/ui/src/patterns/Verification/index.ts +6 -0
  165. package/packages/ui/src/patterns/Verification/verification.types.ts +8 -0
  166. package/packages/ui/src/patterns/Verification/verification.utils.test.ts +37 -0
  167. package/packages/ui/src/patterns/Verification/verification.utils.ts +75 -0
  168. package/packages/ui/src/patterns/index.ts +25 -0
  169. package/packages/ui/src/primitives/Accordian/Accordian.vue +11 -0
  170. package/packages/ui/src/primitives/Accordian/AccordianItem.vue +14 -0
  171. package/packages/ui/src/primitives/Accordion/Accordion.props.ts +21 -0
  172. package/packages/ui/src/primitives/Accordion/Accordion.spec.md +50 -0
  173. package/packages/ui/src/primitives/Accordion/Accordion.types.ts +4 -0
  174. package/packages/ui/src/primitives/Accordion/Accordion.variants.ts +12 -0
  175. package/packages/ui/src/primitives/Accordion/Accordion.vue +71 -0
  176. package/packages/ui/src/primitives/Accordion/AccordionItem.props.ts +14 -0
  177. package/packages/ui/src/primitives/Accordion/AccordionItem.vue +40 -0
  178. package/packages/ui/src/primitives/Badge/Badge.props.ts +17 -0
  179. package/packages/ui/src/primitives/Badge/Badge.spec.md +17 -0
  180. package/packages/ui/src/primitives/Badge/Badge.types.ts +15 -0
  181. package/packages/ui/src/primitives/Badge/Badge.variants.ts +48 -0
  182. package/packages/ui/src/primitives/Badge/Badge.vue +31 -0
  183. package/packages/ui/src/primitives/Button/Button.props.ts +29 -0
  184. package/packages/ui/src/primitives/Button/Button.spec.md +139 -0
  185. package/packages/ui/src/primitives/Button/Button.types.ts +19 -0
  186. package/packages/ui/src/primitives/Button/Button.variants.ts +72 -0
  187. package/packages/ui/src/primitives/Button/Button.vue +90 -0
  188. package/packages/ui/src/primitives/Card/Card.props.ts +17 -0
  189. package/packages/ui/src/primitives/Card/Card.spec.md +29 -0
  190. package/packages/ui/src/primitives/Card/Card.types.ts +12 -0
  191. package/packages/ui/src/primitives/Card/Card.variants.ts +27 -0
  192. package/packages/ui/src/primitives/Card/Card.vue +37 -0
  193. package/packages/ui/src/primitives/Checkbox/Checkbox.props.ts +21 -0
  194. package/packages/ui/src/primitives/Checkbox/Checkbox.spec.md +51 -0
  195. package/packages/ui/src/primitives/Checkbox/Checkbox.types.ts +4 -0
  196. package/packages/ui/src/primitives/Checkbox/Checkbox.variants.ts +34 -0
  197. package/packages/ui/src/primitives/Checkbox/Checkbox.vue +92 -0
  198. package/packages/ui/src/primitives/Dialog/Dialog.props.ts +29 -0
  199. package/packages/ui/src/primitives/Dialog/Dialog.spec.md +52 -0
  200. package/packages/ui/src/primitives/Dialog/Dialog.types.ts +3 -0
  201. package/packages/ui/src/primitives/Dialog/Dialog.variants.ts +27 -0
  202. package/packages/ui/src/primitives/Dialog/Dialog.vue +78 -0
  203. package/packages/ui/src/primitives/Drawer/Drawer.props.ts +33 -0
  204. package/packages/ui/src/primitives/Drawer/Drawer.spec.md +50 -0
  205. package/packages/ui/src/primitives/Drawer/Drawer.types.ts +5 -0
  206. package/packages/ui/src/primitives/Drawer/Drawer.variants.ts +35 -0
  207. package/packages/ui/src/primitives/Drawer/Drawer.vue +88 -0
  208. package/packages/ui/src/primitives/FieldMessage/FieldMessage.props.ts +17 -0
  209. package/packages/ui/src/primitives/FieldMessage/FieldMessage.spec.md +35 -0
  210. package/packages/ui/src/primitives/FieldMessage/FieldMessage.types.ts +5 -0
  211. package/packages/ui/src/primitives/FieldMessage/FieldMessage.variants.ts +14 -0
  212. package/packages/ui/src/primitives/FieldMessage/FieldMessage.vue +40 -0
  213. package/packages/ui/src/primitives/FileInput/FileInput.props.ts +41 -0
  214. package/packages/ui/src/primitives/FileInput/FileInput.types.ts +6 -0
  215. package/packages/ui/src/primitives/FileInput/FileInput.variants.ts +46 -0
  216. package/packages/ui/src/primitives/FileInput/FileInput.vue +163 -0
  217. package/packages/ui/src/primitives/Input/Input.props.ts +29 -0
  218. package/packages/ui/src/primitives/Input/Input.spec.md +79 -0
  219. package/packages/ui/src/primitives/Input/Input.types.ts +13 -0
  220. package/packages/ui/src/primitives/Input/Input.variants.ts +54 -0
  221. package/packages/ui/src/primitives/Input/Input.vue +99 -0
  222. package/packages/ui/src/primitives/Label/Label.props.ts +25 -0
  223. package/packages/ui/src/primitives/Label/Label.spec.md +31 -0
  224. package/packages/ui/src/primitives/Label/Label.types.ts +3 -0
  225. package/packages/ui/src/primitives/Label/Label.variants.ts +17 -0
  226. package/packages/ui/src/primitives/Label/Label.vue +38 -0
  227. package/packages/ui/src/primitives/Menu/Menu.props.ts +17 -0
  228. package/packages/ui/src/primitives/Menu/Menu.spec.md +38 -0
  229. package/packages/ui/src/primitives/Menu/Menu.types.ts +10 -0
  230. package/packages/ui/src/primitives/Menu/Menu.variants.ts +10 -0
  231. package/packages/ui/src/primitives/Menu/Menu.vue +57 -0
  232. package/packages/ui/src/primitives/Popover/Popover.props.ts +25 -0
  233. package/packages/ui/src/primitives/Popover/Popover.spec.md +49 -0
  234. package/packages/ui/src/primitives/Popover/Popover.types.ts +3 -0
  235. package/packages/ui/src/primitives/Popover/Popover.variants.ts +18 -0
  236. package/packages/ui/src/primitives/Popover/Popover.vue +74 -0
  237. package/packages/ui/src/primitives/RadioGroup/RadioGroup.props.ts +29 -0
  238. package/packages/ui/src/primitives/RadioGroup/RadioGroup.spec.md +50 -0
  239. package/packages/ui/src/primitives/RadioGroup/RadioGroup.types.ts +12 -0
  240. package/packages/ui/src/primitives/RadioGroup/RadioGroup.variants.ts +48 -0
  241. package/packages/ui/src/primitives/RadioGroup/RadioGroup.vue +87 -0
  242. package/packages/ui/src/primitives/Separator/Separator.props.ts +9 -0
  243. package/packages/ui/src/primitives/Separator/Separator.spec.md +15 -0
  244. package/packages/ui/src/primitives/Separator/Separator.types.ts +3 -0
  245. package/packages/ui/src/primitives/Separator/Separator.variants.ts +8 -0
  246. package/packages/ui/src/primitives/Separator/Separator.vue +23 -0
  247. package/packages/ui/src/primitives/Skeleton/Skeleton.props.ts +21 -0
  248. package/packages/ui/src/primitives/Skeleton/Skeleton.spec.md +18 -0
  249. package/packages/ui/src/primitives/Skeleton/Skeleton.types.ts +5 -0
  250. package/packages/ui/src/primitives/Skeleton/Skeleton.variants.ts +18 -0
  251. package/packages/ui/src/primitives/Skeleton/Skeleton.vue +37 -0
  252. package/packages/ui/src/primitives/Spinner/Spinner.props.ts +13 -0
  253. package/packages/ui/src/primitives/Spinner/Spinner.spec.md +16 -0
  254. package/packages/ui/src/primitives/Spinner/Spinner.types.ts +5 -0
  255. package/packages/ui/src/primitives/Spinner/Spinner.variants.ts +15 -0
  256. package/packages/ui/src/primitives/Spinner/Spinner.vue +33 -0
  257. package/packages/ui/src/primitives/SplitButton/SplitButton.vue +108 -0
  258. package/packages/ui/src/primitives/Switch/Switch.props.ts +21 -0
  259. package/packages/ui/src/primitives/Switch/Switch.spec.md +49 -0
  260. package/packages/ui/src/primitives/Switch/Switch.types.ts +3 -0
  261. package/packages/ui/src/primitives/Switch/Switch.variants.ts +34 -0
  262. package/packages/ui/src/primitives/Switch/Switch.vue +71 -0
  263. package/packages/ui/src/primitives/Tabs/Tabs.props.ts +25 -0
  264. package/packages/ui/src/primitives/Tabs/Tabs.spec.md +48 -0
  265. package/packages/ui/src/primitives/Tabs/Tabs.types.ts +11 -0
  266. package/packages/ui/src/primitives/Tabs/Tabs.variants.ts +28 -0
  267. package/packages/ui/src/primitives/Tabs/Tabs.vue +59 -0
  268. package/packages/ui/src/primitives/Textarea/Textarea.props.ts +33 -0
  269. package/packages/ui/src/primitives/Textarea/Textarea.spec.md +59 -0
  270. package/packages/ui/src/primitives/Textarea/Textarea.types.ts +5 -0
  271. package/packages/ui/src/primitives/Textarea/Textarea.variants.ts +27 -0
  272. package/packages/ui/src/primitives/Textarea/Textarea.vue +74 -0
  273. package/packages/ui/src/primitives/Tooltip/Tooltip.props.ts +21 -0
  274. package/packages/ui/src/primitives/Tooltip/Tooltip.spec.md +45 -0
  275. package/packages/ui/src/primitives/Tooltip/Tooltip.types.ts +3 -0
  276. package/packages/ui/src/primitives/Tooltip/Tooltip.variants.ts +4 -0
  277. package/packages/ui/src/primitives/Tooltip/Tooltip.vue +31 -0
  278. package/packages/ui/src/primitives/TreeView/TreeView.types.ts +10 -0
  279. package/packages/ui/src/primitives/TreeView/TreeView.vue +113 -0
  280. package/packages/ui/src/primitives/TreeView/TreeViewNode.vue +190 -0
  281. package/packages/ui/src/primitives/index.ts +29 -0
  282. package/packages/ui/src/style.css +7 -0
  283. package/packages/ui/src/style.js +1 -0
  284. package/packages/ui/src/themes/armour.css +147 -0
  285. package/packages/ui/src/themes/aurora.css +147 -0
  286. package/packages/ui/src/themes/citrine-ash.css +147 -0
  287. package/packages/ui/src/themes/concord.css +147 -0
  288. package/packages/ui/src/themes/garnet-honey.css +147 -0
  289. package/packages/ui/src/themes/harbor-rose.css +147 -0
  290. package/packages/ui/src/themes/ledger.css +147 -0
  291. package/packages/ui/src/themes/neon-noir.css +74 -0
  292. package/packages/ui/src/themes/obsidian-iris.css +147 -0
  293. package/packages/ui/src/themes/pixpax.css +147 -0
  294. package/packages/ui/src/themes/print.css +147 -0
  295. package/packages/ui/src/themes/prism.css +147 -0
  296. package/packages/ui/src/themes/proof.css +145 -0
  297. package/packages/ui/src/themes/semanticThemeContract.js +2256 -0
  298. package/packages/ui/src/themes/spruce-ink.css +147 -0
  299. package/packages/ui/src/themes/sunset.css +147 -0
  300. package/packages/ui/tailwind.config.js +64 -0
  301. package/packages/ui/vite.config.js +35 -0
  302. package/packages/ui/vite.config.js.timestamp-1780697224943-89fbc929987bc.mjs +38 -0
  303. package/packages/utils/CHANGELOG.md +111 -0
  304. package/packages/utils/README.md +3 -0
  305. package/packages/utils/package.json +46 -0
  306. package/packages/utils/src/index.test.js +39 -0
  307. package/packages/utils/src/index.ts +289 -0
  308. package/packages/utils/tsconfig.build.json +12 -0
  309. package/packages/utils/vite.config.js +28 -0
  310. package/pnpm-workspace.yaml +8 -0
  311. package/scripts/vite/package-lib-config.ts +59 -0
  312. package/tsconfig.json +24 -0
  313. package/tsconfig.node.json +9 -0
@@ -0,0 +1,585 @@
1
+ import type {
2
+ IdentityGlyphAlgorithm,
3
+ IdentityGlyphInput,
4
+ IdentityGlyphModel,
5
+ IdentityGlyphPalette,
6
+ IdentityGlyphSize,
7
+ ResolvedIdentityGlyphInput,
8
+ } from "./identityGlyph.types";
9
+
10
+ const GRID_SIZE = 7;
11
+ const CENTER_INDEX = Math.floor(GRID_SIZE / 2);
12
+ const FALLBACK_CANONICAL_IDENTITY = "fallback:glyph:v1";
13
+ const ED25519_MULTICODEC_PREFIX = new Uint8Array([0xed, 0x01]);
14
+ const ED25519_PUBLIC_KEY_BYTES = 32;
15
+ const BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
16
+ const BASE58_LOOKUP = new Map(Array.from(BASE58_ALPHABET).map((char, index) => [char, index]));
17
+
18
+ type Rgb = {
19
+ r: number;
20
+ g: number;
21
+ b: number;
22
+ };
23
+
24
+ function isRecord(value: unknown): value is Record<string, unknown> {
25
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
26
+ }
27
+
28
+ function normalizeBase64Url(input: string): string {
29
+ return String(input || "")
30
+ .trim()
31
+ .replace(/\+/g, "-")
32
+ .replace(/\//g, "_")
33
+ .replace(/=+$/g, "");
34
+ }
35
+
36
+ function decodeBase64Url(value: string): Uint8Array {
37
+ const normalized = normalizeBase64Url(value);
38
+ if (!normalized) {
39
+ throw new Error("Base64url value is required.");
40
+ }
41
+
42
+ const pad = normalized.length % 4 === 0 ? "" : "=".repeat(4 - (normalized.length % 4));
43
+ const base64 = `${normalized.replace(/-/g, "+").replace(/_/g, "/")}${pad}`;
44
+
45
+ if (typeof Buffer !== "undefined") {
46
+ return new Uint8Array(Buffer.from(base64, "base64"));
47
+ }
48
+
49
+ if (typeof atob === "undefined") {
50
+ throw new Error("Base64url decode is unavailable in this runtime.");
51
+ }
52
+
53
+ const binary = atob(base64);
54
+ const bytes = new Uint8Array(binary.length);
55
+ for (let index = 0; index < binary.length; index += 1) {
56
+ bytes[index] = binary.charCodeAt(index);
57
+ }
58
+ return bytes;
59
+ }
60
+
61
+ function encodeBase64Url(bytes: Uint8Array): string {
62
+ if (typeof Buffer !== "undefined") {
63
+ return Buffer.from(bytes)
64
+ .toString("base64")
65
+ .replace(/\+/g, "-")
66
+ .replace(/\//g, "_")
67
+ .replace(/=+$/g, "");
68
+ }
69
+
70
+ let binary = "";
71
+ for (const byte of bytes) {
72
+ binary += String.fromCharCode(byte);
73
+ }
74
+
75
+ if (typeof btoa === "undefined") {
76
+ throw new Error("Base64url encode is unavailable in this runtime.");
77
+ }
78
+
79
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
80
+ }
81
+
82
+ function decodeBase58(input: string): Uint8Array {
83
+ const value = String(input || "").trim();
84
+ if (!value) {
85
+ throw new Error("Base58 payload is required.");
86
+ }
87
+
88
+ let numeric = 0n;
89
+ for (const character of value) {
90
+ const digit = BASE58_LOOKUP.get(character);
91
+ if (digit === undefined) {
92
+ throw new Error(`Invalid base58 character '${character}'.`);
93
+ }
94
+ numeric = numeric * 58n + BigInt(digit);
95
+ }
96
+
97
+ const bytes: number[] = [];
98
+ while (numeric > 0n) {
99
+ bytes.push(Number(numeric % 256n));
100
+ numeric /= 256n;
101
+ }
102
+ bytes.reverse();
103
+
104
+ let leadingZeros = 0;
105
+ for (const character of value) {
106
+ if (character !== "1") {
107
+ break;
108
+ }
109
+ leadingZeros += 1;
110
+ }
111
+
112
+ return new Uint8Array([...new Array(leadingZeros).fill(0), ...bytes]);
113
+ }
114
+
115
+ function resolvePublicKeyCanonical(publicKey: string): string {
116
+ const normalized = normalizeBase64Url(publicKey);
117
+ const bytes = decodeBase64Url(normalized);
118
+ if (bytes.length !== ED25519_PUBLIC_KEY_BYTES) {
119
+ throw new Error("Identity public key must be 32-byte Ed25519 base64url.");
120
+ }
121
+ return normalized;
122
+ }
123
+
124
+ function resolveDidKeyCanonical(identityKey: string): string {
125
+ const value = String(identityKey || "").trim();
126
+ if (!value.startsWith("did:key:z")) {
127
+ throw new Error("Identity key must be a did:key:z value.");
128
+ }
129
+
130
+ const decoded = decodeBase58(value.slice("did:key:z".length));
131
+ if (decoded.length !== ED25519_MULTICODEC_PREFIX.length + ED25519_PUBLIC_KEY_BYTES) {
132
+ throw new Error("Identity key must decode to Ed25519 multicodec bytes.");
133
+ }
134
+
135
+ const prefix = decoded.slice(0, ED25519_MULTICODEC_PREFIX.length);
136
+ if (prefix[0] !== ED25519_MULTICODEC_PREFIX[0] || prefix[1] !== ED25519_MULTICODEC_PREFIX[1]) {
137
+ throw new Error("Identity key multicodec prefix is not Ed25519.");
138
+ }
139
+
140
+ return encodeBase64Url(decoded.slice(ED25519_MULTICODEC_PREFIX.length));
141
+ }
142
+
143
+ function resolveCanonicalFromUnknown(value: unknown): string {
144
+ if (typeof value === "string") {
145
+ const normalized = value.trim();
146
+ if (!normalized) {
147
+ throw new Error("Identity value is required.");
148
+ }
149
+
150
+ if (normalized.startsWith("did:key:z")) {
151
+ return resolveDidKeyCanonical(normalized);
152
+ }
153
+
154
+ return resolvePublicKeyCanonical(normalized);
155
+ }
156
+
157
+ if (!isRecord(value)) {
158
+ throw new Error("Identity value is unsupported.");
159
+ }
160
+
161
+ if ("identity" in value) {
162
+ return resolveCanonicalFromUnknown(value.identity);
163
+ }
164
+
165
+ if (typeof value.publicKey === "string") {
166
+ return resolvePublicKeyCanonical(value.publicKey);
167
+ }
168
+
169
+ if (typeof value.identityKey === "string") {
170
+ return resolveDidKeyCanonical(value.identityKey);
171
+ }
172
+
173
+ throw new Error("Identity object is missing public key material.");
174
+ }
175
+
176
+ function shortIdentity(value: string): string {
177
+ const normalized = String(value || "").trim();
178
+ if (!normalized) {
179
+ return "unknown";
180
+ }
181
+
182
+ if (normalized.length <= 16) {
183
+ return normalized;
184
+ }
185
+
186
+ return `${normalized.slice(0, 8)}...${normalized.slice(-6)}`;
187
+ }
188
+
189
+ function fnv1aHash(input: string): number {
190
+ let hash = 0x811c9dc5;
191
+ for (let index = 0; index < input.length; index += 1) {
192
+ hash ^= input.charCodeAt(index);
193
+ hash = Math.imul(hash, 0x01000193) >>> 0;
194
+ }
195
+ return hash >>> 0;
196
+ }
197
+
198
+ function createPrng(seed: number): () => number {
199
+ let state = seed >>> 0 || 0x9e3779b9;
200
+ return () => {
201
+ state ^= state << 13;
202
+ state ^= state >>> 17;
203
+ state ^= state << 5;
204
+ return (state >>> 0) / 0x100000000;
205
+ };
206
+ }
207
+
208
+ function hslToRgb(h: number, s: number, l: number): Rgb {
209
+ const sat = s / 100;
210
+ const light = l / 100;
211
+ const c = (1 - Math.abs(2 * light - 1)) * sat;
212
+ const hp = h / 60;
213
+ const x = c * (1 - Math.abs((hp % 2) - 1));
214
+
215
+ let r = 0;
216
+ let g = 0;
217
+ let b = 0;
218
+
219
+ if (hp >= 0 && hp < 1) {
220
+ r = c;
221
+ g = x;
222
+ } else if (hp < 2) {
223
+ r = x;
224
+ g = c;
225
+ } else if (hp < 3) {
226
+ g = c;
227
+ b = x;
228
+ } else if (hp < 4) {
229
+ g = x;
230
+ b = c;
231
+ } else if (hp < 5) {
232
+ r = x;
233
+ b = c;
234
+ } else {
235
+ r = c;
236
+ b = x;
237
+ }
238
+
239
+ const m = light - c / 2;
240
+ return {
241
+ r: Math.round((r + m) * 255),
242
+ g: Math.round((g + m) * 255),
243
+ b: Math.round((b + m) * 255),
244
+ };
245
+ }
246
+
247
+ function rgbToHex(value: Rgb): string {
248
+ const toHex = (channel: number) =>
249
+ Math.max(0, Math.min(255, channel)).toString(16).padStart(2, "0");
250
+ return `#${toHex(value.r)}${toHex(value.g)}${toHex(value.b)}`;
251
+ }
252
+
253
+ function channelToLinear(value: number): number {
254
+ const normalized = value / 255;
255
+ return normalized <= 0.03928 ? normalized / 12.92 : Math.pow((normalized + 0.055) / 1.055, 2.4);
256
+ }
257
+
258
+ function contrastRatio(first: Rgb, second: Rgb): number {
259
+ const luminanceA =
260
+ 0.2126 * channelToLinear(first.r) +
261
+ 0.7152 * channelToLinear(first.g) +
262
+ 0.0722 * channelToLinear(first.b);
263
+ const luminanceB =
264
+ 0.2126 * channelToLinear(second.r) +
265
+ 0.7152 * channelToLinear(second.g) +
266
+ 0.0722 * channelToLinear(second.b);
267
+
268
+ const light = Math.max(luminanceA, luminanceB);
269
+ const dark = Math.min(luminanceA, luminanceB);
270
+ return (light + 0.05) / (dark + 0.05);
271
+ }
272
+
273
+ function tuneContrast(background: Rgb, h: number, s: number, l: number, minRatio = 3): Rgb {
274
+ let best = hslToRgb(h, s, l);
275
+ if (contrastRatio(background, best) >= minRatio) {
276
+ return best;
277
+ }
278
+
279
+ let bestRatio = contrastRatio(background, best);
280
+ for (let delta = 4; delta <= 52; delta += 4) {
281
+ const darker = hslToRgb(h, s, Math.max(8, l - delta));
282
+ const darkerRatio = contrastRatio(background, darker);
283
+ if (darkerRatio > bestRatio) {
284
+ best = darker;
285
+ bestRatio = darkerRatio;
286
+ }
287
+ if (darkerRatio >= minRatio) {
288
+ return darker;
289
+ }
290
+
291
+ const lighter = hslToRgb(h, s, Math.min(92, l + delta));
292
+ const lighterRatio = contrastRatio(background, lighter);
293
+ if (lighterRatio > bestRatio) {
294
+ best = lighter;
295
+ bestRatio = lighterRatio;
296
+ }
297
+ if (lighterRatio >= minRatio) {
298
+ return lighter;
299
+ }
300
+ }
301
+
302
+ return best;
303
+ }
304
+
305
+ function createPalette(seed: number): IdentityGlyphPalette {
306
+ const random = createPrng(seed ^ 0xa5a5a5a5);
307
+
308
+ const baseHue = Math.floor(random() * 360);
309
+ const background = hslToRgb(baseHue, 22 + random() * 10, 86 + random() * 7);
310
+
311
+ const primaryHue = (baseHue + 130 + random() * 70) % 360;
312
+ const secondaryHue = (baseHue + 30 + random() * 120) % 360;
313
+ const accentHue = (primaryHue + 170 + random() * 25) % 360;
314
+
315
+ const primary = tuneContrast(background, primaryHue, 68 + random() * 18, 30 + random() * 12, 3.4);
316
+ const secondary = tuneContrast(
317
+ background,
318
+ secondaryHue,
319
+ 58 + random() * 20,
320
+ 36 + random() * 14,
321
+ 2.8,
322
+ );
323
+ const accent = tuneContrast(background, accentHue, 76 + random() * 16, 44 + random() * 14, 3.1);
324
+
325
+ return {
326
+ background: rgbToHex(background),
327
+ primary: rgbToHex(primary),
328
+ secondary: rgbToHex(secondary),
329
+ accent: rgbToHex(accent),
330
+ };
331
+ }
332
+
333
+ function createGrid(): number[][] {
334
+ return Array.from({ length: GRID_SIZE }, () => new Array(GRID_SIZE).fill(0));
335
+ }
336
+
337
+ function setSymmetricCell(grid: number[][], x: number, y: number, value: number): void {
338
+ if (x < 0 || x >= GRID_SIZE || y < 0 || y >= GRID_SIZE) {
339
+ return;
340
+ }
341
+
342
+ grid[y][x] = value;
343
+ grid[y][GRID_SIZE - 1 - x] = value;
344
+ }
345
+
346
+ function countFilled(grid: number[][]): number {
347
+ let total = 0;
348
+ for (const row of grid) {
349
+ for (const cell of row) {
350
+ if (cell !== 0) {
351
+ total += 1;
352
+ }
353
+ }
354
+ }
355
+ return total;
356
+ }
357
+
358
+ function centerCoreFilled(grid: number[][]): number {
359
+ const positions = [
360
+ [CENTER_INDEX, CENTER_INDEX],
361
+ [CENTER_INDEX, CENTER_INDEX - 1],
362
+ [CENTER_INDEX, CENTER_INDEX + 1],
363
+ [CENTER_INDEX - 1, CENTER_INDEX],
364
+ [CENTER_INDEX + 1, CENTER_INDEX],
365
+ ] as const;
366
+
367
+ return positions.reduce((count, [x, y]) => count + (grid[y][x] === 0 ? 0 : 1), 0);
368
+ }
369
+
370
+ function buildFallbackGrid(): number[][] {
371
+ const grid = createGrid();
372
+
373
+ for (let y = 1; y <= 5; y += 1) {
374
+ setSymmetricCell(grid, CENTER_INDEX, y, 1);
375
+ }
376
+
377
+ for (let x = 1; x <= CENTER_INDEX; x += 1) {
378
+ setSymmetricCell(grid, x, CENTER_INDEX, 2);
379
+ }
380
+
381
+ setSymmetricCell(grid, 2, 2, 1);
382
+ setSymmetricCell(grid, 2, 4, 1);
383
+ setSymmetricCell(grid, CENTER_INDEX, CENTER_INDEX, 3);
384
+
385
+ return grid;
386
+ }
387
+
388
+ function buildGlyphGrid(seed: number): number[][] {
389
+ const random = createPrng(seed);
390
+ const grid = createGrid();
391
+
392
+ // Base silhouette with clear center of gravity.
393
+ for (let y = 1; y <= 5; y += 1) {
394
+ setSymmetricCell(grid, CENTER_INDEX, y, 1);
395
+ }
396
+ for (let x = 1; x <= CENTER_INDEX; x += 1) {
397
+ setSymmetricCell(grid, x, CENTER_INDEX, 1);
398
+ }
399
+ setSymmetricCell(grid, 2, 2, 1);
400
+ setSymmetricCell(grid, 2, 4, 1);
401
+
402
+ const silhouetteCandidates = [
403
+ [1, 2],
404
+ [1, 4],
405
+ [2, 1],
406
+ [2, 5],
407
+ [3, 1],
408
+ [3, 5],
409
+ [2, 3],
410
+ ] as const;
411
+
412
+ for (const [x, y] of silhouetteCandidates) {
413
+ if (random() > 0.45) {
414
+ setSymmetricCell(grid, x, y, 1);
415
+ }
416
+ }
417
+
418
+ // Landmarks.
419
+ const landmarkCandidates = [
420
+ [1, 1],
421
+ [1, 3],
422
+ [1, 5],
423
+ [2, 2],
424
+ [2, 3],
425
+ [2, 4],
426
+ [3, 2],
427
+ [3, 4],
428
+ [3, 5],
429
+ [0, 3],
430
+ ] as const;
431
+
432
+ for (const [x, y] of landmarkCandidates) {
433
+ if (random() > 0.52) {
434
+ setSymmetricCell(grid, x, y, 2);
435
+ }
436
+ }
437
+
438
+ // Optional cuts in interior zones.
439
+ const cutCandidates = [
440
+ [2, 2],
441
+ [2, 3],
442
+ [2, 4],
443
+ [3, 2],
444
+ [3, 4],
445
+ ] as const;
446
+
447
+ for (const [x, y] of cutCandidates) {
448
+ if (random() > 0.82 && !(x === CENTER_INDEX && y === CENTER_INDEX)) {
449
+ setSymmetricCell(grid, x, y, 0);
450
+ }
451
+ }
452
+
453
+ // Rare accents.
454
+ let accentCount = 0;
455
+ const accentCandidates = [
456
+ [1, 1],
457
+ [1, 5],
458
+ [2, 2],
459
+ [2, 4],
460
+ [3, 3],
461
+ [3, 1],
462
+ [3, 5],
463
+ ] as const;
464
+
465
+ for (const [x, y] of accentCandidates) {
466
+ if (accentCount >= 2) {
467
+ break;
468
+ }
469
+ if (grid[y][x] !== 0 && random() > 0.9) {
470
+ setSymmetricCell(grid, x, y, 3);
471
+ accentCount += 1;
472
+ }
473
+ }
474
+
475
+ // Guarantees: center core and minimum density.
476
+ const centerFillTargets = [
477
+ [CENTER_INDEX, CENTER_INDEX],
478
+ [CENTER_INDEX, CENTER_INDEX - 1],
479
+ [CENTER_INDEX, CENTER_INDEX + 1],
480
+ [CENTER_INDEX - 1, CENTER_INDEX],
481
+ [CENTER_INDEX - 1, CENTER_INDEX - 1],
482
+ [CENTER_INDEX - 1, CENTER_INDEX + 1],
483
+ ] as const;
484
+
485
+ for (const [x, y] of centerFillTargets) {
486
+ if (centerCoreFilled(grid) >= 4) {
487
+ break;
488
+ }
489
+ if (grid[y][x] === 0) {
490
+ setSymmetricCell(grid, x, y, 1);
491
+ }
492
+ }
493
+
494
+ const minFilled = 16;
495
+ const densityCandidates = [
496
+ [1, 2],
497
+ [1, 4],
498
+ [0, 3],
499
+ [2, 1],
500
+ [2, 5],
501
+ [3, 1],
502
+ [3, 5],
503
+ [0, 2],
504
+ [0, 4],
505
+ ] as const;
506
+
507
+ for (const [x, y] of densityCandidates) {
508
+ if (countFilled(grid) >= minFilled) {
509
+ break;
510
+ }
511
+ if (grid[y][x] === 0) {
512
+ setSymmetricCell(grid, x, y, 1);
513
+ }
514
+ }
515
+
516
+ return grid;
517
+ }
518
+
519
+ function buildModel(
520
+ resolved: ResolvedIdentityGlyphInput,
521
+ algorithm: IdentityGlyphAlgorithm,
522
+ ): IdentityGlyphModel {
523
+ const paletteSeed = fnv1aHash(`${algorithm}|palette|${resolved.canonicalIdentity}`);
524
+
525
+ if (resolved.fallback) {
526
+ return {
527
+ algorithm,
528
+ canonicalIdentity: resolved.canonicalIdentity,
529
+ shortIdentity: resolved.shortIdentity,
530
+ fallback: true,
531
+ grid: buildFallbackGrid(),
532
+ palette: createPalette(paletteSeed ^ 0x41424344),
533
+ };
534
+ }
535
+
536
+ const seed = fnv1aHash(`${algorithm}|grid|${resolved.canonicalIdentity}`);
537
+ return {
538
+ algorithm,
539
+ canonicalIdentity: resolved.canonicalIdentity,
540
+ shortIdentity: resolved.shortIdentity,
541
+ fallback: false,
542
+ grid: buildGlyphGrid(seed),
543
+ palette: createPalette(paletteSeed),
544
+ };
545
+ }
546
+
547
+ export function resolveIdentityGlyphSize(size?: number | IdentityGlyphSize): number {
548
+ if (typeof size === "number" && Number.isFinite(size)) {
549
+ return Math.max(16, Math.round(size));
550
+ }
551
+
552
+ if (size === "xs") return 24;
553
+ if (size === "sm") return 32;
554
+ if (size === "md") return 40;
555
+ if (size === "lg") return 48;
556
+ return 32;
557
+ }
558
+
559
+ export function resolveIdentityGlyphInput(input: IdentityGlyphInput): ResolvedIdentityGlyphInput {
560
+ try {
561
+ const canonicalIdentity = resolveCanonicalFromUnknown(input);
562
+ return {
563
+ canonicalIdentity,
564
+ shortIdentity: shortIdentity(canonicalIdentity),
565
+ fallback: false,
566
+ };
567
+ } catch {
568
+ return {
569
+ canonicalIdentity: FALLBACK_CANONICAL_IDENTITY,
570
+ shortIdentity: "fallback",
571
+ fallback: true,
572
+ };
573
+ }
574
+ }
575
+
576
+ export function createIdentityGlyphModel(
577
+ input: IdentityGlyphInput,
578
+ algorithm: IdentityGlyphAlgorithm = "glyph:v1",
579
+ ): IdentityGlyphModel {
580
+ return buildModel(resolveIdentityGlyphInput(input), algorithm);
581
+ }
582
+
583
+ export function getIdentityGlyphPaletteValues(palette: IdentityGlyphPalette): string[] {
584
+ return [palette.background, palette.primary, palette.secondary, palette.accent];
585
+ }
@@ -0,0 +1,5 @@
1
+ export { default as IdentityGlyph } from "./IdentityGlyph.vue";
2
+ export { default as IdentityHandle } from "./IdentityHandle.vue";
3
+
4
+ export type * from "./identityGlyph.types";
5
+ export * from "./identityGlyph.utils";
@@ -0,0 +1,28 @@
1
+ # KeyValueList
2
+
3
+ ## Purpose
4
+
5
+ Reusable metadata and status list composition for concise key/value summaries.
6
+
7
+ ## Category
8
+
9
+ Pattern
10
+
11
+ ## Public API
12
+
13
+ Props:
14
+
15
+ - `items`: `KeyValueListItem[]`
16
+ - `variant`: card variant (`outline` by default)
17
+ - `padding`: card padding (`md` by default)
18
+
19
+ Slots:
20
+
21
+ - `value` scoped slot for custom value rendering
22
+
23
+ ## Behavior
24
+
25
+ - composes `Card` and `Separator` primitives
26
+ - uses semantic `dl/dt/dd` markup for accessibility
27
+ - normalizes boolean values to `yes/no`
28
+ - renders `-` for empty values
@@ -0,0 +1,16 @@
1
+ import type { CardPadding, CardVariant } from "../../primitives/Card/Card.types";
2
+
3
+ export type KeyValueListValue = boolean | number | string | null | undefined;
4
+
5
+ export type KeyValueListItem = {
6
+ dataTest?: string;
7
+ id?: string;
8
+ label: string;
9
+ value: KeyValueListValue;
10
+ };
11
+
12
+ export type KeyValueListProps = {
13
+ items: KeyValueListItem[];
14
+ padding?: CardPadding;
15
+ variant?: CardVariant;
16
+ };
@@ -0,0 +1,50 @@
1
+ <script setup lang="ts">
2
+ import { computed } from "vue";
3
+ import Card from "../../primitives/Card/Card.vue";
4
+ import Separator from "../../primitives/Separator/Separator.vue";
5
+ import type { KeyValueListItem, KeyValueListValue } from "./KeyValueList.types";
6
+
7
+ const props = withDefaults(
8
+ defineProps<{
9
+ items: KeyValueListItem[];
10
+ padding?: "sm" | "md" | "lg";
11
+ variant?: "default" | "subtle" | "outline" | "elevated" | "panel" | "showcase";
12
+ }>(),
13
+ {
14
+ padding: "md",
15
+ variant: "outline",
16
+ },
17
+ );
18
+
19
+ const normalizedItems = computed(() => props.items ?? []);
20
+
21
+ function formatValue(value: KeyValueListValue): string {
22
+ if (typeof value === "boolean") {
23
+ return value ? "yes" : "no";
24
+ }
25
+ if (value === null || value === undefined || value === "") {
26
+ return "-";
27
+ }
28
+ return String(value);
29
+ }
30
+ </script>
31
+
32
+ <template>
33
+ <Card :variant="props.variant" :padding="props.padding">
34
+ <dl class="m-0 flex flex-col gap-2" data-test="key-value-list">
35
+ <template v-for="(item, index) in normalizedItems" :key="item.id ?? item.label">
36
+ <div class="flex items-center justify-between gap-4">
37
+ <dt class="text-sm text-[var(--ui-fg-muted)]">
38
+ {{ item.label }}
39
+ </dt>
40
+ <dd class="m-0 text-sm text-[var(--ui-fg)]" :data-test="item.dataTest">
41
+ <slot name="value" :item="item">
42
+ {{ formatValue(item.value) }}
43
+ </slot>
44
+ </dd>
45
+ </div>
46
+ <Separator v-if="index < normalizedItems.length - 1" orientation="horizontal" />
47
+ </template>
48
+ </dl>
49
+ </Card>
50
+ </template>