@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,599 @@
1
+ <script setup lang="ts">
2
+ import { computed, ref } from "vue";
3
+ import { Button, Card, Tabs } from "../../primitives";
4
+ import FeatureCard from "../FeatureCard/FeatureCard.vue";
5
+ import PageSurface from "../PageSurface/PageSurface.vue";
6
+ import PreviewPanel from "../PreviewPanel/PreviewPanel.vue";
7
+ import SectionClarifier from "../SectionClarifier/SectionClarifier.vue";
8
+ import SectionIntro from "../SectionIntro/SectionIntro.vue";
9
+ import StepList from "../StepList/StepList.vue";
10
+ import LandingIcon from "./LandingIcon.vue";
11
+ import type {
12
+ LandingPageAction,
13
+ LandingPageConfig,
14
+ LandingPageLink,
15
+ LandingPagePreview,
16
+ } from "./LandingPage.types";
17
+
18
+ const props = defineProps<{
19
+ appTitle?: string;
20
+ config: LandingPageConfig;
21
+ }>();
22
+
23
+ const developerTab = ref(props.config.developerSection.tabs[0]?.value ?? "overview");
24
+
25
+ const hasDocumentationSections =
26
+ Boolean(props.config.proofModelSection) ||
27
+ Boolean(props.config.proofJsonSection) ||
28
+ Boolean(props.config.surfacesSection) ||
29
+ Boolean(props.config.staticBuildSection) ||
30
+ Boolean(props.config.suiteSection) ||
31
+ Boolean(props.config.nonGoalsSection);
32
+
33
+ const showTopNavigation = computed(
34
+ () => !hasDocumentationSections && props.config.navigationLinks.length > 0,
35
+ );
36
+
37
+ function isExternalLink(href: string) {
38
+ return /^https?:\/\//.test(href);
39
+ }
40
+
41
+ function resolveButtonProps(link: LandingPageLink | LandingPageAction) {
42
+ if (link.href.startsWith("/")) {
43
+ return { as: "RouterLink", to: link.href };
44
+ }
45
+
46
+ return {
47
+ as: "a",
48
+ href: link.href,
49
+ target: isExternalLink(link.href) ? "_blank" : undefined,
50
+ rel: isExternalLink(link.href) ? "noreferrer" : undefined,
51
+ };
52
+ }
53
+
54
+ function resolveLinkProps(link: LandingPageLink) {
55
+ if (link.href.startsWith("/")) {
56
+ return { to: link.href };
57
+ }
58
+
59
+ return {
60
+ href: link.href,
61
+ target: isExternalLink(link.href) ? "_blank" : undefined,
62
+ rel: isExternalLink(link.href) ? "noreferrer" : undefined,
63
+ };
64
+ }
65
+
66
+ function previewTabs(preview: LandingPagePreview) {
67
+ return preview.tabs?.map((tab) => tab.label);
68
+ }
69
+
70
+ function previewActiveTab(preview: LandingPagePreview) {
71
+ return preview.tabs?.find((tab) => tab.active)?.label ?? preview.tabs?.[0]?.label;
72
+ }
73
+
74
+ function resolveActionVariant(action: LandingPageAction) {
75
+ return action.variant === "secondary" ? "secondary" : "primary";
76
+ }
77
+
78
+ function resolveSuiteItemStyle(themeColor: string) {
79
+ return {
80
+ "--landing-suite-accent": themeColor,
81
+ };
82
+ }
83
+ </script>
84
+
85
+ <template>
86
+ <PageSurface>
87
+ <main
88
+ :class="
89
+ hasDocumentationSections
90
+ ? 'mx-auto max-w-6xl px-6 pb-28 pt-12 lg:px-8 lg:pb-32 lg:pt-16'
91
+ : 'mx-auto max-w-7xl px-6 pb-24 pt-16 lg:px-8 lg:pb-28 lg:pt-24'
92
+ "
93
+ >
94
+ <section
95
+ :class="
96
+ hasDocumentationSections
97
+ ? 'grid items-start gap-12 lg:grid-cols-[minmax(0,1.18fr)_minmax(20rem,0.82fr)] lg:gap-18'
98
+ : 'grid items-center gap-14 lg:grid-cols-[1.12fr_0.88fr] lg:gap-16'
99
+ "
100
+ >
101
+ <div class="min-w-0">
102
+ <SectionIntro
103
+ :eyebrow="props.config.hero.eyebrow"
104
+ :title="props.config.hero.title"
105
+ :description="props.config.hero.description"
106
+ size="hero"
107
+ title-tag="h1"
108
+ >
109
+ <template #actions>
110
+ <Button
111
+ v-bind="resolveButtonProps(props.config.hero.primaryAction)"
112
+ :variant="resolveActionVariant(props.config.hero.primaryAction)"
113
+ size="lg"
114
+ >
115
+ {{ props.config.hero.primaryAction.label }}
116
+ </Button>
117
+ <Button
118
+ v-if="props.config.hero.secondaryAction"
119
+ v-bind="resolveButtonProps(props.config.hero.secondaryAction)"
120
+ :variant="resolveActionVariant(props.config.hero.secondaryAction)"
121
+ size="lg"
122
+ >
123
+ {{ props.config.hero.secondaryAction.label }}
124
+ </Button>
125
+ <Button
126
+ v-if="props.config.hero.tertiaryAction"
127
+ v-bind="resolveButtonProps(props.config.hero.tertiaryAction)"
128
+ variant="plain-secondary"
129
+ size="lg"
130
+ >
131
+ {{ props.config.hero.tertiaryAction.label }}
132
+ </Button>
133
+ </template>
134
+ </SectionIntro>
135
+
136
+ <div
137
+ v-if="props.config.hero.supportingLine || props.config.hero.note"
138
+ class="mt-6 space-y-3"
139
+ >
140
+ <p
141
+ v-if="props.config.hero.supportingLine"
142
+ class="m-0 max-w-2xl text-sm leading-7 text-[var(--ui-fg-muted)]"
143
+ >
144
+ {{ props.config.hero.supportingLine }}
145
+ </p>
146
+ <p
147
+ v-if="props.config.hero.note"
148
+ class="m-0 max-w-2xl text-sm leading-7 text-[var(--ui-fg)]"
149
+ >
150
+ {{ props.config.hero.note }}
151
+ </p>
152
+ </div>
153
+ </div>
154
+
155
+ <slot name="hero-preview" :preview="props.config.hero.preview">
156
+ <PreviewPanel
157
+ :title="props.config.hero.preview.title"
158
+ :meta="props.config.hero.preview.meta"
159
+ :status-label="props.config.hero.preview.statusLabel"
160
+ :status-tone="props.config.hero.preview.statusTone"
161
+ :rows="props.config.hero.preview.rows"
162
+ :code="props.config.hero.preview.code"
163
+ :tabs="previewTabs(props.config.hero.preview)"
164
+ :active-tab="previewActiveTab(props.config.hero.preview)"
165
+ :footer-text="props.config.hero.preview.footerText"
166
+ emphasis="strong"
167
+ />
168
+ </slot>
169
+ </section>
170
+
171
+ <section v-if="props.config.proofModelSection" id="proof-model" class="pt-28">
172
+ <SectionIntro
173
+ :eyebrow="props.config.proofModelSection.eyebrow"
174
+ :title="props.config.proofModelSection.title"
175
+ :description="props.config.proofModelSection.description"
176
+ />
177
+
178
+ <div class="mt-12 grid gap-x-10 gap-y-8 md:grid-cols-2">
179
+ <div
180
+ v-for="item in props.config.proofModelSection.items"
181
+ :key="item.title"
182
+ class="space-y-3 border-t border-[color-mix(in_srgb,var(--ui-border)_82%,transparent)] pt-5"
183
+ >
184
+ <h3 class="m-0 text-base font-medium tracking-[-0.02em] text-[var(--ui-fg)]">
185
+ {{ item.title }}
186
+ </h3>
187
+ <p class="m-0 max-w-2xl text-sm leading-7 text-[var(--ui-fg-muted)]">
188
+ {{ item.description }}
189
+ </p>
190
+ </div>
191
+ </div>
192
+ </section>
193
+
194
+ <section v-if="props.config.proofJsonSection" id="proof-json" class="pt-20">
195
+ <SectionIntro
196
+ :eyebrow="props.config.proofJsonSection.eyebrow"
197
+ :title="props.config.proofJsonSection.title"
198
+ :description="props.config.proofJsonSection.description"
199
+ />
200
+ <slot name="proofJson" :section="props.config.proofJsonSection">
201
+ <div
202
+ class="mt-12 overflow-hidden rounded-[var(--ui-radius-lg)] border border-[color-mix(in_srgb,var(--ui-border)_86%,transparent)] bg-[color-mix(in_srgb,var(--ui-surface)_94%,transparent)]"
203
+ >
204
+ <div
205
+ class="border-b border-[color-mix(in_srgb,var(--ui-border)_80%,transparent)] px-5 py-3 text-[0.72rem] font-semibold uppercase tracking-[0.18em] text-[var(--ui-fg-muted)]"
206
+ >
207
+ proof.json
208
+ </div>
209
+ <pre
210
+ class="m-0 overflow-x-auto p-6 text-[0.84rem] leading-7 text-[var(--ui-fg)]"
211
+ ><code>{{ props.config.proofJsonSection.code }}</code></pre>
212
+ </div>
213
+ <p
214
+ v-if="props.config.proofJsonSection.supportingText"
215
+ class="mt-4 m-0 text-sm leading-7 text-[var(--ui-fg-muted)]"
216
+ >
217
+ {{ props.config.proofJsonSection.supportingText }}
218
+ </p>
219
+ </slot>
220
+ </section>
221
+
222
+ <section v-if="props.config.surfacesSection" id="surfaces" class="pt-24">
223
+ <SectionIntro
224
+ :eyebrow="props.config.surfacesSection.eyebrow"
225
+ :title="props.config.surfacesSection.title"
226
+ :description="props.config.surfacesSection.description"
227
+ />
228
+
229
+ <div
230
+ class="mt-12 grid gap-0 border-y border-[color-mix(in_srgb,var(--ui-border)_82%,transparent)] md:grid-cols-3"
231
+ >
232
+ <div
233
+ v-for="surface in props.config.surfacesSection.items"
234
+ :key="surface.title"
235
+ class="space-y-3 px-0 py-6 md:px-6 md:py-8 md:not-first:border-l md:not-first:border-[color-mix(in_srgb,var(--ui-border)_82%,transparent)]"
236
+ >
237
+ <h3 class="m-0 text-lg font-medium tracking-[-0.02em] text-[var(--ui-fg)]">
238
+ {{ surface.title }}
239
+ </h3>
240
+ <p class="m-0 text-sm leading-7 text-[var(--ui-fg-muted)]">
241
+ {{ surface.description }}
242
+ </p>
243
+ </div>
244
+ </div>
245
+ </section>
246
+
247
+ <section v-if="props.config.suiteSection" id="suite" class="pt-24">
248
+ <div class="max-w-4xl">
249
+ <SectionIntro
250
+ :eyebrow="props.config.suiteSection.eyebrow"
251
+ :title="props.config.suiteSection.title"
252
+ :description="props.config.suiteSection.description"
253
+ />
254
+ <p
255
+ v-if="props.config.suiteSection.supportingText"
256
+ class="mt-5 m-0 text-sm leading-7 text-[var(--ui-fg-muted)]"
257
+ >
258
+ {{ props.config.suiteSection.supportingText }}
259
+ </p>
260
+ </div>
261
+
262
+ <div
263
+ class="mt-12 grid gap-0 border-y border-[color-mix(in_srgb,var(--ui-border)_82%,transparent)] md:grid-cols-3"
264
+ >
265
+ <article
266
+ v-for="item in props.config.suiteSection.items"
267
+ :key="item.title"
268
+ :style="resolveSuiteItemStyle(item.themeColor)"
269
+ class="space-y-4 px-0 py-6 md:px-6 md:py-8 md:not-first:border-l md:not-first:border-[color-mix(in_srgb,var(--ui-border)_82%,transparent)]"
270
+ >
271
+ <div
272
+ class="inline-flex rounded-full border px-3 py-1 text-[0.68rem] font-semibold uppercase tracking-[0.18em]"
273
+ style="
274
+ border-color: color-mix(in srgb, var(--landing-suite-accent) 36%, transparent);
275
+ background: color-mix(in srgb, var(--landing-suite-accent) 12%, transparent);
276
+ color: var(--landing-suite-accent);
277
+ "
278
+ >
279
+ {{ item.title }}
280
+ </div>
281
+ <div class="space-y-3">
282
+ <h3 class="m-0 text-lg font-medium tracking-[-0.02em] text-[var(--ui-fg)]">
283
+ {{ item.title }}
284
+ </h3>
285
+ <p class="m-0 text-sm leading-7 text-[var(--ui-fg-muted)]">
286
+ {{ item.description }}
287
+ </p>
288
+ </div>
289
+ <a
290
+ class="inline-flex items-center gap-2 text-sm font-medium no-underline transition hover:opacity-85"
291
+ style="color: var(--landing-suite-accent)"
292
+ v-bind="resolveLinkProps(item.link)"
293
+ >
294
+ {{ item.link.label }}
295
+ <span aria-hidden="true">→</span>
296
+ </a>
297
+ </article>
298
+ </div>
299
+ </section>
300
+
301
+ <section
302
+ v-if="!hasDocumentationSections && props.config.featureSection"
303
+ id="features"
304
+ class="pt-24"
305
+ >
306
+ <SectionIntro
307
+ :eyebrow="props.config.featureSection.eyebrow"
308
+ :title="props.config.featureSection.title"
309
+ :description="props.config.featureSection.description"
310
+ />
311
+
312
+ <div class="mt-10 grid gap-5 md:grid-cols-2 xl:grid-cols-4">
313
+ <FeatureCard
314
+ v-for="feature in props.config.featureSection.items"
315
+ :key="feature.title"
316
+ :title="feature.title"
317
+ :description="feature.description"
318
+ :tone="feature.tone"
319
+ surface="elevated"
320
+ >
321
+ <template #icon>
322
+ <LandingIcon :name="feature.icon" />
323
+ </template>
324
+ </FeatureCard>
325
+ </div>
326
+ </section>
327
+
328
+ <section
329
+ v-if="!hasDocumentationSections && props.config.howItWorksSection"
330
+ id="how-it-works"
331
+ class="pt-24"
332
+ >
333
+ <div class="grid gap-8 lg:grid-cols-[0.95fr_1.05fr]">
334
+ <PreviewPanel
335
+ :title="props.config.howItWorksSection.preview.title"
336
+ :meta="props.config.howItWorksSection.preview.meta"
337
+ :status-label="props.config.howItWorksSection.preview.statusLabel"
338
+ :status-tone="props.config.howItWorksSection.preview.statusTone"
339
+ :rows="props.config.howItWorksSection.preview.rows"
340
+ :code="props.config.howItWorksSection.preview.code"
341
+ :tabs="previewTabs(props.config.howItWorksSection.preview)"
342
+ :active-tab="previewActiveTab(props.config.howItWorksSection.preview)"
343
+ :footer-text="props.config.howItWorksSection.preview.footerText"
344
+ />
345
+
346
+ <div class="flex min-w-0 flex-col gap-8">
347
+ <SectionIntro
348
+ :eyebrow="props.config.howItWorksSection.eyebrow"
349
+ :title="props.config.howItWorksSection.title"
350
+ />
351
+ <StepList :items="props.config.howItWorksSection.steps" />
352
+ </div>
353
+ </div>
354
+ </section>
355
+
356
+ <section
357
+ v-if="!hasDocumentationSections && props.config.useCasesSection"
358
+ id="use-cases"
359
+ class="pt-24"
360
+ >
361
+ <SectionIntro
362
+ :eyebrow="props.config.useCasesSection.eyebrow"
363
+ :title="props.config.useCasesSection.title"
364
+ />
365
+
366
+ <div class="mt-10 grid gap-5 sm:grid-cols-2 xl:grid-cols-3">
367
+ <Card
368
+ v-for="useCase in props.config.useCasesSection.items"
369
+ :key="useCase.title"
370
+ variant="subtle"
371
+ padding="md"
372
+ class="space-y-4"
373
+ >
374
+ <div class="flex items-start justify-between gap-3">
375
+ <div class="space-y-2">
376
+ <h3 class="m-0 text-xl font-medium tracking-[-0.02em] text-[var(--ui-fg)]">
377
+ {{ useCase.title }}
378
+ </h3>
379
+ </div>
380
+ <div
381
+ class="inline-flex size-10 items-center justify-center rounded-[var(--ui-radius-md)] border border-[var(--ui-border)] bg-[var(--ui-tonal-secondary)] text-[var(--ui-fg-muted)]"
382
+ >
383
+ <LandingIcon :name="useCase.icon" />
384
+ </div>
385
+ </div>
386
+ <p class="m-0 text-sm leading-7 text-[var(--ui-fg-muted)]">
387
+ {{ useCase.description }}
388
+ </p>
389
+ </Card>
390
+ </div>
391
+ </section>
392
+
393
+ <section v-if="props.config.staticBuildSection" id="static-build" class="pt-20">
394
+ <div class="max-w-4xl">
395
+ <SectionIntro
396
+ :eyebrow="props.config.staticBuildSection.eyebrow"
397
+ :title="props.config.staticBuildSection.title"
398
+ :description="props.config.staticBuildSection.description"
399
+ />
400
+
401
+ <ol class="mt-10 space-y-6">
402
+ <li
403
+ v-for="(step, index) in props.config.staticBuildSection.steps"
404
+ :key="step.title"
405
+ class="flex gap-5"
406
+ >
407
+ <div
408
+ class="flex size-7 shrink-0 items-center justify-center rounded-full border border-[color-mix(in_srgb,var(--ui-border)_90%,transparent)] text-xs font-semibold text-[var(--ui-fg-muted)]"
409
+ >
410
+ {{ index + 1 }}
411
+ </div>
412
+ <div
413
+ class="space-y-2 border-t border-[color-mix(in_srgb,var(--ui-border)_82%,transparent)] pt-4"
414
+ >
415
+ <h3 class="m-0 text-base font-medium text-[var(--ui-fg)]">
416
+ {{ step.title }}
417
+ </h3>
418
+ <p class="m-0 text-sm leading-7 text-[var(--ui-fg-muted)]">
419
+ {{ step.description }}
420
+ </p>
421
+ </div>
422
+ </li>
423
+ </ol>
424
+ <p
425
+ v-if="props.config.staticBuildSection.closingLine"
426
+ class="mt-8 m-0 text-sm leading-7 text-[var(--ui-fg)]"
427
+ >
428
+ {{ props.config.staticBuildSection.closingLine }}
429
+ </p>
430
+ </div>
431
+ </section>
432
+
433
+ <section id="developers" class="pt-24">
434
+ <div
435
+ :class="
436
+ hasDocumentationSections
437
+ ? 'grid gap-10 lg:grid-cols-[minmax(0,0.82fr)_minmax(0,1.18fr)] lg:items-start'
438
+ : 'rounded-[var(--ui-radius-lg)] border border-[color-mix(in_srgb,var(--ui-border)_82%,transparent)] bg-[color-mix(in_srgb,var(--ui-surface)_94%,transparent)] p-8'
439
+ "
440
+ >
441
+ <div class="min-w-0 flex flex-col gap-8">
442
+ <SectionIntro
443
+ :eyebrow="props.config.developerSection.eyebrow"
444
+ :title="props.config.developerSection.title"
445
+ :description="props.config.developerSection.description"
446
+ />
447
+
448
+ <div
449
+ :class="
450
+ hasDocumentationSections
451
+ ? 'flex flex-wrap gap-x-6 gap-y-2 border-t border-[color-mix(in_srgb,var(--ui-border)_82%,transparent)] pt-5'
452
+ : 'grid gap-3 sm:grid-cols-3'
453
+ "
454
+ >
455
+ <div
456
+ v-for="surface in props.config.developerSection.surfaces"
457
+ :key="surface"
458
+ :class="
459
+ hasDocumentationSections
460
+ ? 'text-sm text-[var(--ui-fg-muted)]'
461
+ : 'rounded-[var(--ui-radius-md)] border border-[var(--ui-border)] bg-[var(--ui-tonal-secondary)] px-4 py-4'
462
+ "
463
+ >
464
+ <p class="m-0 text-sm font-medium text-[var(--ui-fg)]">
465
+ {{ surface }}
466
+ </p>
467
+ </div>
468
+ </div>
469
+ </div>
470
+
471
+ <Tabs
472
+ v-model="developerTab"
473
+ :items="
474
+ props.config.developerSection.tabs.map((tab) => ({
475
+ value: tab.value,
476
+ label: tab.label,
477
+ }))
478
+ "
479
+ variant="pill"
480
+ class="min-w-0"
481
+ >
482
+ <template
483
+ v-for="tab in props.config.developerSection.tabs"
484
+ :key="tab.value"
485
+ v-slot:[`panel-${tab.value}`]
486
+ >
487
+ <div
488
+ class="space-y-4 rounded-[var(--ui-radius-lg)] border border-[color-mix(in_srgb,var(--ui-border)_82%,transparent)] bg-[color-mix(in_srgb,var(--ui-surface)_94%,transparent)] p-5"
489
+ >
490
+ <div class="space-y-1">
491
+ <p
492
+ class="m-0 text-xs font-semibold uppercase tracking-[0.18em] text-[var(--ui-fg-muted)]"
493
+ >
494
+ {{ tab.meta }}
495
+ </p>
496
+ <h3 class="m-0 text-lg font-medium tracking-[-0.02em] text-[var(--ui-fg)]">
497
+ {{ tab.title }}
498
+ </h3>
499
+ </div>
500
+ <pre
501
+ class="m-0 overflow-x-auto rounded-[calc(var(--ui-radius-md)-2px)] border border-[color-mix(in_srgb,var(--ui-border)_82%,transparent)] bg-[color-mix(in_srgb,var(--ui-surface)_90%,transparent)] p-4 text-[0.8rem] leading-6 text-[var(--ui-fg)]"
502
+ ><code>{{ tab.code }}</code></pre>
503
+ <p class="m-0 text-sm leading-7 text-[var(--ui-fg-muted)]">
504
+ {{ tab.supportingCopy }}
505
+ </p>
506
+ <a
507
+ class="text-sm text-[var(--ui-fg-muted)] underline decoration-[color-mix(in_srgb,var(--ui-border)_70%,transparent)] underline-offset-4 transition hover:text-[var(--ui-fg)]"
508
+ v-bind="resolveLinkProps(tab.link)"
509
+ >
510
+ {{ tab.link.label }}
511
+ </a>
512
+ </div>
513
+ </template>
514
+ </Tabs>
515
+ </div>
516
+ </section>
517
+
518
+ <section v-if="!hasDocumentationSections && props.config.clarifierSection" class="pt-14">
519
+ <SectionClarifier
520
+ :eyebrow="props.config.clarifierSection.eyebrow"
521
+ :title="props.config.clarifierSection.title"
522
+ >
523
+ <div class="grid gap-8 md:grid-cols-2 lg:gap-14">
524
+ <div
525
+ v-for="column in props.config.clarifierSection.columns"
526
+ :key="column.title"
527
+ class="space-y-5"
528
+ >
529
+ <h3
530
+ class="m-0 mb-4 text-sm font-semibold uppercase tracking-[0.12em] text-[var(--ui-fg)]"
531
+ >
532
+ {{ column.title }}
533
+ </h3>
534
+ <ul
535
+ class="m-0 list-outside list-disc space-y-2 pl-5 text-sm leading-7 text-[var(--ui-fg-muted)] marker:text-[var(--ui-fg-muted)]"
536
+ >
537
+ <li v-for="item in column.items" :key="item">{{ item }}</li>
538
+ </ul>
539
+ </div>
540
+ </div>
541
+ </SectionClarifier>
542
+ </section>
543
+
544
+ <section v-if="props.config.nonGoalsSection" class="pt-14">
545
+ <SectionClarifier
546
+ :eyebrow="props.config.nonGoalsSection.eyebrow || 'Boundaries'"
547
+ :title="props.config.nonGoalsSection.title"
548
+ >
549
+ <Card variant="subtle" padding="lg">
550
+ <ul
551
+ class="m-0 list-outside list-disc space-y-2 pl-5 text-sm leading-7 text-[var(--ui-fg-muted)] marker:text-[var(--ui-fg-muted)]"
552
+ >
553
+ <li v-for="item in props.config.nonGoalsSection.items" :key="item">
554
+ {{ item }}
555
+ </li>
556
+ </ul>
557
+ </Card>
558
+ </SectionClarifier>
559
+ </section>
560
+
561
+ <section class="pt-24">
562
+ <Card variant="elevated" padding="lg">
563
+ <SectionIntro
564
+ align="center"
565
+ :eyebrow="props.config.ctaSection.eyebrow"
566
+ :title="props.config.ctaSection.title"
567
+ :description="props.config.ctaSection.description"
568
+ >
569
+ <template #actions>
570
+ <Button
571
+ v-bind="resolveButtonProps(props.config.ctaSection.primaryAction)"
572
+ :variant="resolveActionVariant(props.config.ctaSection.primaryAction)"
573
+ size="lg"
574
+ >
575
+ {{ props.config.ctaSection.primaryAction.label }}
576
+ </Button>
577
+ <Button
578
+ v-if="props.config.ctaSection.secondaryAction"
579
+ v-bind="resolveButtonProps(props.config.ctaSection.secondaryAction)"
580
+ :variant="resolveActionVariant(props.config.ctaSection.secondaryAction)"
581
+ size="lg"
582
+ >
583
+ {{ props.config.ctaSection.secondaryAction.label }}
584
+ </Button>
585
+ <Button
586
+ v-if="props.config.ctaSection.tertiaryAction"
587
+ v-bind="resolveButtonProps(props.config.ctaSection.tertiaryAction)"
588
+ variant="plain-secondary"
589
+ size="lg"
590
+ >
591
+ {{ props.config.ctaSection.tertiaryAction.label }}
592
+ </Button>
593
+ </template>
594
+ </SectionIntro>
595
+ </Card>
596
+ </section>
597
+ </main>
598
+ </PageSurface>
599
+ </template>
@@ -0,0 +1,33 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+ import { describe, expect, it } from "vitest";
4
+
5
+ describe("ListWorkspaceLayout", () => {
6
+ it("defines a fixed workspace rail width and sticky-capable main body", () => {
7
+ const source = readFileSync(
8
+ resolve(process.cwd(), "src/patterns/ListWorkspaceLayout/ListWorkspaceLayout.vue"),
9
+ "utf8",
10
+ );
11
+
12
+ expect(source).toContain("w-64");
13
+ expect(source).toContain("overflow-auto");
14
+ expect(source).toContain("backdrop-blur");
15
+ expect(
16
+ "hidden w-64 shrink-0 overflow-auto border-r border-[var(--ui-border)] bg-[var(--ui-surface)] p-4 lg:block",
17
+ ).toMatchInlineSnapshot(
18
+ "\"hidden w-64 shrink-0 overflow-auto border-r border-[var(--ui-border)] bg-[var(--ui-surface)] p-4 lg:block\"",
19
+ );
20
+ });
21
+
22
+ it("declares rail and default slots", () => {
23
+ const source = readFileSync(
24
+ resolve(process.cwd(), "src/patterns/ListWorkspaceLayout/ListWorkspaceLayout.vue"),
25
+ "utf8",
26
+ );
27
+
28
+ expect(source).toContain("showRail: true");
29
+ expect(source).toContain("props.showRail && Boolean(slots.rail)");
30
+ expect(source).toContain('<slot name="rail" />');
31
+ expect(source).toContain("<slot />");
32
+ });
33
+ });
@@ -0,0 +1,44 @@
1
+ <script setup lang="ts">
2
+ import { computed, useSlots } from "vue";
3
+
4
+ const props = withDefaults(
5
+ defineProps<{
6
+ rootClass?: string;
7
+ railClass?: string;
8
+ mainClass?: string;
9
+ showRail?: boolean;
10
+ dataTestPrefix?: string;
11
+ }>(),
12
+ {
13
+ rootClass: "",
14
+ railClass: "",
15
+ mainClass: "",
16
+ showRail: true,
17
+ dataTestPrefix: "workspace-layout",
18
+ },
19
+ );
20
+ const slots = useSlots();
21
+
22
+ const rootClasses = computed(() => ["flex h-full min-h-0", props.rootClass]);
23
+ const railClasses = computed(() => [
24
+ "hidden w-64 shrink-0 overflow-auto border-r border-[var(--ui-border)] bg-[var(--ui-surface)] p-4 lg:block",
25
+ props.railClass,
26
+ ]);
27
+ const mainClasses = computed(() => [
28
+ "flex min-w-0 flex-1 flex-col bg-[color-mix(in_srgb,var(--ui-surface)_86%,transparent)] backdrop-blur",
29
+ props.mainClass,
30
+ ]);
31
+ const shouldRenderRail = computed(() => props.showRail && Boolean(slots.rail));
32
+ </script>
33
+
34
+ <template>
35
+ <div :class="rootClasses" :data-test="dataTestPrefix">
36
+ <aside v-if="shouldRenderRail" :class="railClasses" :data-test="`${dataTestPrefix}-rail`">
37
+ <slot name="rail" />
38
+ </aside>
39
+
40
+ <section :class="mainClasses" :data-test="`${dataTestPrefix}-main`">
41
+ <slot />
42
+ </section>
43
+ </div>
44
+ </template>