@invisibleloop/pulse 0.20.11 → 0.20.20

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 (266) hide show
  1. package/package.json +6 -5
  2. package/public/.pulse-ui-version +1 -1
  3. package/scripts/build.js +26 -0
  4. package/scripts/release-version.js +17 -8
  5. package/scripts/strip-server.test.js +60 -0
  6. package/src/agent/checklist.md +8 -12
  7. package/src/agent/commands/pulse-start.md +2 -0
  8. package/src/agent/commands/verify.md +30 -8
  9. package/src/agent/guide-components.md +7 -25
  10. package/src/agent/guide-routing.md +0 -12
  11. package/src/agent/guide-server.md +102 -1
  12. package/src/agent/guide-spec.md +8 -1
  13. package/src/agent/guide-styles.md +23 -0
  14. package/src/agent/identity.md +9 -0
  15. package/src/agent/workflow.md +4 -23
  16. package/src/cli/check-bundles.js +113 -0
  17. package/src/cli/check-bundles.test.js +137 -0
  18. package/src/cli/cli.test.js +105 -0
  19. package/src/cli/dev.js +48 -3
  20. package/src/cli/diagnose.js +105 -0
  21. package/src/cli/diagnose.test.js +124 -0
  22. package/src/cli/discover.js +9 -2
  23. package/src/cli/index.js +171 -9
  24. package/src/cli/process-utils.js +57 -0
  25. package/src/cli/process-utils.test.js +114 -0
  26. package/src/cli/review.js +391 -0
  27. package/src/cli/review.test.js +143 -0
  28. package/src/cli/scaffold.js +9 -4
  29. package/src/cli/stamp.js +102 -0
  30. package/src/cli/stamp.test.js +149 -0
  31. package/src/cli/start.js +15 -2
  32. package/src/cli/validate.js +184 -0
  33. package/src/cli/validate.test.js +153 -0
  34. package/src/mcp/mcp.test.js +261 -30
  35. package/src/mcp/server.js +207 -515
  36. package/src/mcp/validate-worker.js +18 -1
  37. package/src/runtime/index.js +144 -11
  38. package/src/runtime/runtime.test.js +199 -0
  39. package/src/runtime/ssr.js +13 -6
  40. package/src/runtime/ssr.test.js +31 -0
  41. package/src/server/index.js +162 -9
  42. package/src/server/server.test.js +225 -2
  43. package/src/spec/schema.js +1 -1
  44. package/src/ui/index.js +0 -5
  45. package/src/ui/ui.test.js +0 -75
  46. package/types/ui.d.ts +0 -21
  47. package/.claude/commands/build-page.md +0 -63
  48. package/.claude/commands/new-doc-page.md +0 -45
  49. package/.claude/commands/pulse-start.md +0 -11
  50. package/.claude/commands/pulse-stop.md +0 -5
  51. package/.claude/commands/verify.md +0 -118
  52. package/.claude/pulse-checklist.md +0 -189
  53. package/.github/workflows/ci.yml +0 -25
  54. package/.github/workflows/publish.yml +0 -45
  55. package/.pulse/load-reports/home/1773432711417.json +0 -22
  56. package/.pulse-verified +0 -1
  57. package/AGENTS.md +0 -697
  58. package/CLAUDE.md +0 -697
  59. package/README.md +0 -95
  60. package/benchmark/public/dist/counter.boot-PO6SNSJZ.js +0 -8
  61. package/benchmark/public/dist/home.boot-YK6VAEQI.js +0 -6
  62. package/benchmark/public/dist/manifest.json +0 -5
  63. package/benchmark/public/dist/runtime-5Y7ESAIB.js +0 -2
  64. package/benchmark/public/dist/runtime-KO4BHUQ3.js +0 -49
  65. package/benchmark/results/2026-03-27-09-20.json +0 -188
  66. package/benchmark/results/2026-03-27-09-21.json +0 -188
  67. package/benchmark/results/2026-03-27-09-29.json +0 -188
  68. package/benchmark/results/2026-03-27-10-14.json +0 -188
  69. package/benchmark/results/2026-03-27-10-18.json +0 -232
  70. package/benchmark/src/pages/counter.js +0 -37
  71. package/benchmark/src/pages/home.js +0 -21
  72. package/docs/.claude/commands/pulse-build.md +0 -7
  73. package/docs/.claude/commands/pulse-dev.md +0 -7
  74. package/docs/.claude/commands/pulse-start.md +0 -9
  75. package/docs/.claude/commands/pulse-stop.md +0 -5
  76. package/docs/.claude/commands/verify.md +0 -84
  77. package/docs/.claude/coverage-check.js +0 -196
  78. package/docs/.claude/pulse-checklist.md +0 -189
  79. package/docs/public/.pulse-ui-version +0 -1
  80. package/docs/public/andypixel.css +0 -237
  81. package/docs/public/bookworms.css +0 -422
  82. package/docs/public/coutumes.css +0 -561
  83. package/docs/public/docs.css +0 -2388
  84. package/docs/public/favicon.svg +0 -4
  85. package/docs/public/home-brut.css +0 -982
  86. package/docs/public/images/bookworms/hero-books.jpg +0 -0
  87. package/docs/public/images/bookworms/how-we-read-now.jpg +0 -0
  88. package/docs/public/images/bookworms/lantern-keepers.jpg +0 -0
  89. package/docs/public/images/bookworms/provincial-bookseller.jpg +0 -0
  90. package/docs/public/images/journey/178.jpg +0 -0
  91. package/docs/public/images/journey/29.jpg +0 -0
  92. package/docs/public/images/retrowheels/hero.jpg +0 -0
  93. package/docs/public/images/shop/devbook-pro.jpg +0 -0
  94. package/docs/public/images/suzukibento/1015.jpg +0 -0
  95. package/docs/public/images/suzukibento/1025.jpg +0 -0
  96. package/docs/public/images/suzukibento/1043.jpg +0 -0
  97. package/docs/public/images/suzukibento/1059.jpg +0 -0
  98. package/docs/public/images/suzukibento/1062.jpg +0 -0
  99. package/docs/public/images/suzukibento/1084.jpg +0 -0
  100. package/docs/public/images/swish/1469334031218-e382a71b716b.jpg +0 -0
  101. package/docs/public/images/swish/1483985988355-763728e1935b.jpg +0 -0
  102. package/docs/public/images/swish/1485518882345-15568b007407.jpg +0 -0
  103. package/docs/public/images/swish/1490481651871-ab68de25d43d.jpg +0 -0
  104. package/docs/public/images/swish/1496747611176-843222e1e57c.jpg +0 -0
  105. package/docs/public/images/swish/1502716119720-b23a93e5fe1b.jpg +0 -0
  106. package/docs/public/images/swish/1539109136881-3be0616acf4b.jpg +0 -0
  107. package/docs/public/images/swish/1551803091-e20673f15770.jpg +0 -0
  108. package/docs/public/images/swish/1558769132-cb1aea458c5e.jpg +0 -0
  109. package/docs/public/images/swish/1572804013309-59a88b7e92f1.jpg +0 -0
  110. package/docs/public/img/heic1501a.jpg +0 -0
  111. package/docs/public/img/placeholder-landscape.svg +0 -18
  112. package/docs/public/img/placeholder-square.svg +0 -14
  113. package/docs/public/img/placeholder-tall.svg +0 -22
  114. package/docs/public/journey.css +0 -528
  115. package/docs/public/menu.js +0 -327
  116. package/docs/public/pulse-ui.css +0 -3028
  117. package/docs/public/pulse-ui.js +0 -257
  118. package/docs/public/retrowheels.css +0 -425
  119. package/docs/public/shop.css +0 -269
  120. package/docs/public/sparkys.css +0 -382
  121. package/docs/public/suzukibento.css +0 -517
  122. package/docs/public/swish.css +0 -394
  123. package/docs/public/theme.css +0 -52
  124. package/docs/public/themes/lumio.css +0 -50
  125. package/docs/public/themes/observatory.css +0 -104
  126. package/docs/server.js +0 -161
  127. package/docs/src/lib/component-page.js +0 -49
  128. package/docs/src/lib/highlight.js +0 -2
  129. package/docs/src/lib/layout.js +0 -190
  130. package/docs/src/lib/metrics-store.js +0 -6
  131. package/docs/src/lib/nav.js +0 -171
  132. package/docs/src/lib/stats.js +0 -81
  133. package/docs/src/pages/accessibility.js +0 -157
  134. package/docs/src/pages/actions.js +0 -194
  135. package/docs/src/pages/auth.js +0 -176
  136. package/docs/src/pages/caching.js +0 -95
  137. package/docs/src/pages/components/accordion.js +0 -48
  138. package/docs/src/pages/components/alert.js +0 -35
  139. package/docs/src/pages/components/app-badge.js +0 -41
  140. package/docs/src/pages/components/avatar.js +0 -35
  141. package/docs/src/pages/components/badge.js +0 -36
  142. package/docs/src/pages/components/breadcrumbs.js +0 -94
  143. package/docs/src/pages/components/button.js +0 -84
  144. package/docs/src/pages/components/card.js +0 -225
  145. package/docs/src/pages/components/charts.js +0 -283
  146. package/docs/src/pages/components/checkbox.js +0 -129
  147. package/docs/src/pages/components/cluster.js +0 -47
  148. package/docs/src/pages/components/container.js +0 -40
  149. package/docs/src/pages/components/cta.js +0 -54
  150. package/docs/src/pages/components/divider.js +0 -37
  151. package/docs/src/pages/components/empty.js +0 -36
  152. package/docs/src/pages/components/feature.js +0 -60
  153. package/docs/src/pages/components/fieldset.js +0 -65
  154. package/docs/src/pages/components/fileupload.js +0 -128
  155. package/docs/src/pages/components/footer.js +0 -58
  156. package/docs/src/pages/components/grid.js +0 -169
  157. package/docs/src/pages/components/heading.js +0 -107
  158. package/docs/src/pages/components/hero.js +0 -123
  159. package/docs/src/pages/components/icons.js +0 -410
  160. package/docs/src/pages/components/image.js +0 -71
  161. package/docs/src/pages/components/input.js +0 -51
  162. package/docs/src/pages/components/media.js +0 -51
  163. package/docs/src/pages/components/modal.js +0 -111
  164. package/docs/src/pages/components/nav.js +0 -136
  165. package/docs/src/pages/components/phone-frame.js +0 -74
  166. package/docs/src/pages/components/progress.js +0 -102
  167. package/docs/src/pages/components/prose.js +0 -111
  168. package/docs/src/pages/components/pullquote.js +0 -71
  169. package/docs/src/pages/components/radio.js +0 -194
  170. package/docs/src/pages/components/rating.js +0 -106
  171. package/docs/src/pages/components/search.js +0 -61
  172. package/docs/src/pages/components/section.js +0 -98
  173. package/docs/src/pages/components/segmented.js +0 -121
  174. package/docs/src/pages/components/select.js +0 -45
  175. package/docs/src/pages/components/slider.js +0 -115
  176. package/docs/src/pages/components/spinner.js +0 -73
  177. package/docs/src/pages/components/stack.js +0 -48
  178. package/docs/src/pages/components/stat.js +0 -55
  179. package/docs/src/pages/components/stepper.js +0 -66
  180. package/docs/src/pages/components/table.js +0 -45
  181. package/docs/src/pages/components/textarea.js +0 -31
  182. package/docs/src/pages/components/toggle.js +0 -84
  183. package/docs/src/pages/components/tooltip.js +0 -48
  184. package/docs/src/pages/components.js +0 -200
  185. package/docs/src/pages/config.js +0 -193
  186. package/docs/src/pages/constraints.js +0 -100
  187. package/docs/src/pages/deploy.js +0 -233
  188. package/docs/src/pages/error-pages.js +0 -99
  189. package/docs/src/pages/extending.js +0 -198
  190. package/docs/src/pages/faq.js +0 -99
  191. package/docs/src/pages/forms.js +0 -129
  192. package/docs/src/pages/getting-started.js +0 -147
  193. package/docs/src/pages/guard.js +0 -120
  194. package/docs/src/pages/home.js +0 -350
  195. package/docs/src/pages/how-it-works.js +0 -244
  196. package/docs/src/pages/hydration.js +0 -108
  197. package/docs/src/pages/images.js +0 -120
  198. package/docs/src/pages/markdown.js +0 -156
  199. package/docs/src/pages/meta.js +0 -193
  200. package/docs/src/pages/mutations.js +0 -107
  201. package/docs/src/pages/navigation.js +0 -85
  202. package/docs/src/pages/performance.js +0 -157
  203. package/docs/src/pages/persist.js +0 -89
  204. package/docs/src/pages/project-structure.js +0 -91
  205. package/docs/src/pages/prompt-examples.js +0 -186
  206. package/docs/src/pages/raw-responses.js +0 -122
  207. package/docs/src/pages/routing.js +0 -120
  208. package/docs/src/pages/server-api.js +0 -291
  209. package/docs/src/pages/server-data.js +0 -203
  210. package/docs/src/pages/slash-commands.js +0 -55
  211. package/docs/src/pages/spec.js +0 -207
  212. package/docs/src/pages/state.js +0 -102
  213. package/docs/src/pages/store.js +0 -203
  214. package/docs/src/pages/streaming.js +0 -107
  215. package/docs/src/pages/stripe.js +0 -194
  216. package/docs/src/pages/supabase.js +0 -324
  217. package/docs/src/pages/templates/andypixel.js +0 -260
  218. package/docs/src/pages/templates/blog-post.js +0 -226
  219. package/docs/src/pages/templates/bookworms.js +0 -227
  220. package/docs/src/pages/templates/coutumes.js +0 -237
  221. package/docs/src/pages/templates/journey.js +0 -186
  222. package/docs/src/pages/templates/mobile-app.js +0 -371
  223. package/docs/src/pages/templates/retrowheels.js +0 -355
  224. package/docs/src/pages/templates/shop.js +0 -268
  225. package/docs/src/pages/templates/sparkys.js +0 -264
  226. package/docs/src/pages/templates/suzukibento.js +0 -200
  227. package/docs/src/pages/templates/swish.js +0 -313
  228. package/docs/src/pages/templates.js +0 -534
  229. package/docs/src/pages/testing.js +0 -199
  230. package/docs/src/pages/validation.js +0 -138
  231. package/examples/.claude/commands/pulse-build.md +0 -7
  232. package/examples/.claude/commands/pulse-dev.md +0 -7
  233. package/examples/.claude/commands/pulse-start.md +0 -9
  234. package/examples/.claude/commands/pulse-stop.md +0 -5
  235. package/examples/.claude/commands/verify.md +0 -84
  236. package/examples/.claude/coverage-check.js +0 -196
  237. package/examples/.claude/pulse-checklist.md +0 -189
  238. package/examples/andypixel.css +0 -237
  239. package/examples/andypixel.js +0 -258
  240. package/examples/bookworms.css +0 -422
  241. package/examples/bookworms.js +0 -225
  242. package/examples/contact.js +0 -166
  243. package/examples/counter.js +0 -94
  244. package/examples/dev.server.js +0 -110
  245. package/examples/examples.test.js +0 -394
  246. package/examples/journey.css +0 -528
  247. package/examples/journey.js +0 -186
  248. package/examples/pricing.js +0 -262
  249. package/examples/products.js +0 -190
  250. package/examples/public/.pulse-ui-version +0 -1
  251. package/examples/public/pulse-ui.css +0 -3028
  252. package/examples/public/pulse-ui.js +0 -257
  253. package/examples/quiz.js +0 -208
  254. package/examples/retrowheels.css +0 -425
  255. package/examples/retrowheels.js +0 -353
  256. package/examples/shared.js +0 -84
  257. package/examples/suzukibento.css +0 -513
  258. package/examples/suzukibento.js +0 -200
  259. package/examples/swish.css +0 -394
  260. package/examples/swish.js +0 -311
  261. package/examples/todos.js +0 -162
  262. package/src/ui/fieldset.js +0 -37
  263. package/src/ui/phone-frame.js +0 -40
  264. package/src/ui/search.js +0 -78
  265. package/src/ui/segmented.js +0 -50
  266. package/src/ui/tooltip.js +0 -28
@@ -1,410 +0,0 @@
1
- import { renderComponentPage, demo } from '../../lib/component-page.js'
2
- import { prevNext } from '../../lib/nav.js'
3
- import { table } from '../../lib/layout.js'
4
- import { modal } from '../../../../src/ui/index.js'
5
- import {
6
- button, feature,
7
- iconArrowLeft, iconArrowRight, iconArrowUp, iconArrowDown,
8
- iconChevronLeft, iconChevronRight, iconChevronUp, iconChevronDown,
9
- iconExternalLink, iconMenu, iconX, iconMoreHorizontal, iconMoreVertical,
10
- iconCheck, iconCheckCircle, iconXCircle, iconAlertCircle, iconAlertTriangle, iconInfo,
11
- iconPlus, iconMinus, iconEdit, iconTrash, iconCopy, iconSearch, iconFilter,
12
- iconDownload, iconUpload, iconRefresh, iconSend,
13
- iconEye, iconEyeOff, iconLock, iconUnlock, iconSettings, iconBell,
14
- iconUser, iconUsers, iconMail, iconMessageSquare,
15
- iconHome, iconLogOut, iconLogIn,
16
- iconFile, iconImage, iconLink, iconCode, iconCalendar, iconClock, iconBookmark, iconTag,
17
- iconPlay, iconPause, iconVolume, iconStar, iconHeart,
18
- iconPhone, iconGamepad,
19
- iconHandPointUp, iconHandPointDown, iconHandPointLeft, iconHandPointRight,
20
- iconGlobe, iconShield, iconZap, iconTrendingUp, iconTrendingDown, iconLoader, iconGrid, iconBug,
21
- iconMapPin, iconSun, iconMoon,
22
- iconFeather, iconTelescope, iconAi, iconSmile, iconBarChart,
23
- iconShoppingCart, iconShoppingBag, iconCreditCard, iconPackage, iconGift,
24
- iconWallet, iconTruck, iconReceipt, iconStore, iconPercent, iconTicket, iconBanknote,
25
- iconUtensils, iconCoffee, iconPizza, iconApple, iconCarrot,
26
- iconWine, iconCakeSlice, iconFish, iconCherry, iconEgg,
27
- iconCookie, iconIceCream, iconCroissant, iconSalad, iconWheat,
28
- } from '../../../../src/ui/index.js'
29
-
30
- const { prev, next } = prevNext('/components/icons')
31
-
32
- // All icons grouped for the visual grid
33
- const ICONS = [
34
- { group: 'Navigation & Direction', icons: [
35
- { name: 'iconArrowLeft', fn: iconArrowLeft },
36
- { name: 'iconArrowRight', fn: iconArrowRight },
37
- { name: 'iconArrowUp', fn: iconArrowUp },
38
- { name: 'iconArrowDown', fn: iconArrowDown },
39
- { name: 'iconChevronLeft', fn: iconChevronLeft },
40
- { name: 'iconChevronRight', fn: iconChevronRight },
41
- { name: 'iconChevronUp', fn: iconChevronUp },
42
- { name: 'iconChevronDown', fn: iconChevronDown },
43
- { name: 'iconExternalLink', fn: iconExternalLink },
44
- { name: 'iconMenu', fn: iconMenu },
45
- { name: 'iconX', fn: iconX },
46
- { name: 'iconMoreHorizontal', fn: iconMoreHorizontal },
47
- { name: 'iconMoreVertical', fn: iconMoreVertical },
48
- ]},
49
- { group: 'Status', icons: [
50
- { name: 'iconCheck', fn: iconCheck },
51
- { name: 'iconCheckCircle', fn: iconCheckCircle },
52
- { name: 'iconXCircle', fn: iconXCircle },
53
- { name: 'iconAlertCircle', fn: iconAlertCircle },
54
- { name: 'iconAlertTriangle', fn: iconAlertTriangle },
55
- { name: 'iconInfo', fn: iconInfo },
56
- { name: 'iconSmile', fn: iconSmile },
57
- ]},
58
- { group: 'AI & Data', icons: [
59
- { name: 'iconAi', fn: iconAi },
60
- { name: 'iconBarChart', fn: iconBarChart },
61
- ]},
62
- { group: 'Astronomy', icons: [
63
- { name: 'iconTelescope', fn: iconTelescope },
64
- ]},
65
- { group: 'Actions', icons: [
66
- { name: 'iconPlus', fn: iconPlus },
67
- { name: 'iconMinus', fn: iconMinus },
68
- { name: 'iconEdit', fn: iconEdit },
69
- { name: 'iconTrash', fn: iconTrash },
70
- { name: 'iconCopy', fn: iconCopy },
71
- { name: 'iconSearch', fn: iconSearch },
72
- { name: 'iconFilter', fn: iconFilter },
73
- { name: 'iconDownload', fn: iconDownload },
74
- { name: 'iconUpload', fn: iconUpload },
75
- { name: 'iconRefresh', fn: iconRefresh },
76
- { name: 'iconSend', fn: iconSend },
77
- ]},
78
- { group: 'UI Controls', icons: [
79
- { name: 'iconEye', fn: iconEye },
80
- { name: 'iconEyeOff', fn: iconEyeOff },
81
- { name: 'iconLock', fn: iconLock },
82
- { name: 'iconUnlock', fn: iconUnlock },
83
- { name: 'iconSettings', fn: iconSettings },
84
- { name: 'iconBell', fn: iconBell },
85
- ]},
86
- { group: 'People & Communication', icons: [
87
- { name: 'iconUser', fn: iconUser },
88
- { name: 'iconUsers', fn: iconUsers },
89
- { name: 'iconMail', fn: iconMail },
90
- { name: 'iconMessageSquare', fn: iconMessageSquare },
91
- ]},
92
- { group: 'Navigation & Pages', icons: [
93
- { name: 'iconHome', fn: iconHome },
94
- { name: 'iconMapPin', fn: iconMapPin },
95
- { name: 'iconLogOut', fn: iconLogOut },
96
- { name: 'iconLogIn', fn: iconLogIn },
97
- ]},
98
- { group: 'Content & Files', icons: [
99
- { name: 'iconFeather', fn: iconFeather },
100
- { name: 'iconFile', fn: iconFile },
101
- { name: 'iconImage', fn: iconImage },
102
- { name: 'iconLink', fn: iconLink },
103
- { name: 'iconCode', fn: iconCode },
104
- { name: 'iconCalendar', fn: iconCalendar },
105
- { name: 'iconClock', fn: iconClock },
106
- { name: 'iconBookmark', fn: iconBookmark },
107
- { name: 'iconTag', fn: iconTag },
108
- ]},
109
- { group: 'Media & Rating', icons: [
110
- { name: 'iconPlay', fn: iconPlay },
111
- { name: 'iconPause', fn: iconPause },
112
- { name: 'iconVolume', fn: iconVolume },
113
- { name: 'iconStar', fn: iconStar },
114
- { name: 'iconHeart', fn: iconHeart },
115
- ]},
116
- { group: 'Devices', icons: [
117
- { name: 'iconPhone', fn: iconPhone },
118
- { name: 'iconGamepad', fn: iconGamepad },
119
- ]},
120
- { group: 'Hand Pointers', icons: [
121
- { name: 'iconHandPointUp', fn: iconHandPointUp },
122
- { name: 'iconHandPointDown', fn: iconHandPointDown },
123
- { name: 'iconHandPointLeft', fn: iconHandPointLeft },
124
- { name: 'iconHandPointRight', fn: iconHandPointRight },
125
- ]},
126
- { group: 'Misc', icons: [
127
- { name: 'iconGlobe', fn: iconGlobe },
128
- { name: 'iconShield', fn: iconShield },
129
- { name: 'iconZap', fn: iconZap },
130
- { name: 'iconTrendingUp', fn: iconTrendingUp },
131
- { name: 'iconTrendingDown', fn: iconTrendingDown },
132
- { name: 'iconLoader', fn: iconLoader },
133
- { name: 'iconGrid', fn: iconGrid },
134
- { name: 'iconBug', fn: iconBug },
135
- ]},
136
- { group: 'Theme', icons: [
137
- { name: 'iconSun', fn: iconSun },
138
- { name: 'iconMoon', fn: iconMoon },
139
- ]},
140
- { group: 'Ecommerce', icons: [
141
- { name: 'iconShoppingCart', fn: iconShoppingCart },
142
- { name: 'iconShoppingBag', fn: iconShoppingBag },
143
- { name: 'iconCreditCard', fn: iconCreditCard },
144
- { name: 'iconPackage', fn: iconPackage },
145
- { name: 'iconGift', fn: iconGift },
146
- { name: 'iconWallet', fn: iconWallet },
147
- { name: 'iconTruck', fn: iconTruck },
148
- { name: 'iconReceipt', fn: iconReceipt },
149
- { name: 'iconStore', fn: iconStore },
150
- { name: 'iconPercent', fn: iconPercent },
151
- { name: 'iconTicket', fn: iconTicket },
152
- { name: 'iconBanknote', fn: iconBanknote },
153
- ]},
154
- { group: 'Food & Drink', icons: [
155
- { name: 'iconUtensils', fn: iconUtensils },
156
- { name: 'iconCoffee', fn: iconCoffee },
157
- { name: 'iconPizza', fn: iconPizza },
158
- { name: 'iconApple', fn: iconApple },
159
- { name: 'iconCarrot', fn: iconCarrot },
160
- { name: 'iconWine', fn: iconWine },
161
- { name: 'iconCakeSlice', fn: iconCakeSlice },
162
- { name: 'iconFish', fn: iconFish },
163
- { name: 'iconCherry', fn: iconCherry },
164
- { name: 'iconEgg', fn: iconEgg },
165
- { name: 'iconCookie', fn: iconCookie },
166
- { name: 'iconIceCream', fn: iconIceCream },
167
- { name: 'iconCroissant', fn: iconCroissant },
168
- { name: 'iconSalad', fn: iconSalad },
169
- { name: 'iconWheat', fn: iconWheat },
170
- ]},
171
- ]
172
-
173
- // Safe ID from icon name: iconArrowLeft → arrow-left
174
- const safeId = name => name.replace(/^icon/, '').replace(/([A-Z])/g, m => '-' + m.toLowerCase()).replace(/^-/, '')
175
-
176
- const PREVIEW_SIZES = [12, 16, 20, 24, 32, 48]
177
- const PREVIEW_COLORS = [
178
- { label: 'Default', style: '' },
179
- { label: 'Accent', style: 'color:var(--accent)' },
180
- { label: 'Green', style: 'color:var(--color-green,#4ade80)' },
181
- { label: 'Yellow', style: 'color:var(--color-yellow,#facc15)' },
182
- { label: 'Red', style: 'color:var(--color-red,#f87171)' },
183
- { label: 'Muted', style: 'color:var(--muted)' },
184
- ]
185
-
186
- function iconPreviewModal({ name, fn }) {
187
- const id = `icon-modal-${safeId(name)}`
188
-
189
- const sizesHtml = PREVIEW_SIZES.map(size => `
190
- <div style="display:flex;flex-direction:column;align-items:center;gap:4px">
191
- ${fn({ size })}
192
- <span style="font-size:0.6rem;color:var(--muted);font-family:var(--mono,monospace)">${size}</span>
193
- </div>
194
- `).join('')
195
-
196
- const colorsHtml = PREVIEW_COLORS.map(({ label, style }) => `
197
- <div style="display:flex;flex-direction:column;align-items:center;gap:4px;${style}">
198
- ${fn({ size: 28 })}
199
- <span style="font-size:0.6rem;color:var(--muted);font-family:var(--mono,monospace)">${label}</span>
200
- </div>
201
- `).join('')
202
-
203
- const bgHtml =
204
- ['circle', 'square'].map(shape =>
205
- ['accent', 'success', 'warning', 'error', 'muted'].map(bgColor =>
206
- fn({ size: 20, bg: shape, bgColor })
207
- ).join('')
208
- ).join('<br style="display:block;margin:.5rem 0">')
209
-
210
- return modal({
211
- id,
212
- title: name,
213
- size: 'md',
214
- content: `
215
- <div style="display:flex;flex-direction:column;gap:1.5rem">
216
- <div>
217
- <p style="font-size:.75rem;color:var(--muted);margin:0 0 .75rem">Sizes</p>
218
- <div style="display:flex;align-items:flex-end;gap:1rem;flex-wrap:wrap">${sizesHtml}</div>
219
- </div>
220
- <div>
221
- <p style="font-size:.75rem;color:var(--muted);margin:0 0 .75rem">Colours</p>
222
- <div style="display:flex;align-items:center;gap:1rem;flex-wrap:wrap">${colorsHtml}</div>
223
- </div>
224
- <div>
225
- <p style="font-size:.75rem;color:var(--muted);margin:0 0 .75rem">Backgrounds</p>
226
- <div style="display:flex;align-items:center;gap:.5rem;flex-wrap:wrap">${bgHtml}</div>
227
- </div>
228
- </div>
229
- `,
230
- })
231
- }
232
-
233
- function iconGrid_() {
234
- return ICONS.map(({ group, icons }) => `
235
- <h3 class="doc-h3" style="margin-top:2rem">${group}</h3>
236
- <div class="icon-grid">
237
- ${icons.map(({ name, fn }) => `
238
- <button type="button" class="icon-grid-item" style="cursor:pointer;font:inherit;color:inherit" data-dialog-open="icon-modal-${safeId(name)}" aria-label="Preview ${name}">
239
- <div class="icon-grid-preview">${fn({ size: 20 })}</div>
240
- <span class="icon-grid-name">${name}</span>
241
- </button>
242
- `).join('')}
243
- </div>
244
- `).join('')
245
- }
246
-
247
- function iconModals_() {
248
- return ICONS.flatMap(({ icons }) => icons).map(iconPreviewModal).join('')
249
- }
250
-
251
- const pageContent = `
252
- <h2 class="doc-h2" id="usage">Usage</h2>
253
- <p>Import the icon functions you need alongside other components. Call each as a function — optionally pass <code>size</code> and <code>class</code>.</p>
254
- ${demo(
255
- `<div style="display:flex;gap:1rem;align-items:center;flex-wrap:wrap">` +
256
- iconCheck({ size: 20 }) +
257
- iconSearch({ size: 20 }) +
258
- iconUser({ size: 20 }) +
259
- iconStar({ size: 20 }) +
260
- iconZap({ size: 20 }) +
261
- iconSettings({ size: 20 }) +
262
- `</div>`,
263
- `import { iconCheck, iconSearch, iconUser } from '@invisibleloop/pulse/ui'
264
-
265
- // Default size (16px)
266
- iconCheck()
267
-
268
- // Custom size
269
- iconSearch({ size: 20 })
270
-
271
- // With extra class
272
- iconUser({ size: 20, class: 'u-text-accent' })`
273
- )}
274
-
275
- <h2 class="doc-h2" id="with-button">With button</h2>
276
- <p>Pass an icon into the <code>icon</code> prop of <code>button()</code>.</p>
277
- ${demo(
278
- `<div style="display:flex;gap:.75rem;flex-wrap:wrap;align-items:center">` +
279
- button({ label: 'Download', variant: 'primary', icon: iconDownload({ size: 14 }) }) +
280
- button({ label: 'Edit', variant: 'secondary', icon: iconEdit({ size: 14 }) }) +
281
- button({ label: 'Delete', variant: 'danger', icon: iconTrash({ size: 14 }) }) +
282
- button({ label: 'Search', variant: 'ghost', icon: iconSearch({ size: 14 }) }) +
283
- `</div>`,
284
- `button({ label: 'Download', variant: 'primary', icon: iconDownload({ size: 14 }) })
285
- button({ label: 'Edit', variant: 'secondary', icon: iconEdit({ size: 14 }) })
286
- button({ label: 'Delete', variant: 'danger', icon: iconTrash({ size: 14 }) })
287
- button({ label: 'Search', variant: 'ghost', icon: iconSearch({ size: 14 }) })`
288
- )}
289
-
290
- <h2 class="doc-h2" id="with-feature">With feature</h2>
291
- <p>Icons compose naturally into the <code>feature()</code> icon slot.</p>
292
- ${demo(
293
- `<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:1rem">` +
294
- feature({ icon: iconZap({ size: 20 }), title: 'Fast', description: 'Streaming SSR. No build step.' }) +
295
- feature({ icon: iconShield({ size: 20 }), title: 'Secure', description: 'Security headers on every response.' }) +
296
- feature({ icon: iconCode({ size: 20 }), title: 'Simple', description: 'Plain JS objects, no JSX.' }) +
297
- `</div>`,
298
- `feature({ icon: iconZap({ size: 20 }), title: 'Fast', description: '...' })
299
- feature({ icon: iconShield({ size: 20 }), title: 'Secure', description: '...' })
300
- feature({ icon: iconCode({ size: 20 }), title: 'Simple', description: '...' })`
301
- )}
302
-
303
- <h2 class="doc-h2" id="background">Background</h2>
304
- <p>Add <code>bg: 'circle'</code> or <code>bg: 'square'</code> to wrap the icon in a tinted background. Use <code>bgColor</code> to pick the colour — defaults to <code>'accent'</code>.</p>
305
-
306
- <h3 class="doc-h3">Circle</h3>
307
- ${demo(
308
- `<div style="display:flex;gap:1rem;align-items:center;flex-wrap:wrap">` +
309
- iconZap({ size: 20, bg: 'circle', bgColor: 'accent' }) +
310
- iconCheck({ size: 20, bg: 'circle', bgColor: 'success' }) +
311
- iconAlertTriangle({ size: 20, bg: 'circle', bgColor: 'warning' }) +
312
- iconXCircle({ size: 20, bg: 'circle', bgColor: 'error' }) +
313
- iconSettings({size: 20, bg: 'circle', bgColor: 'muted' }) +
314
- `</div>`,
315
- `iconZap({ size: 20, bg: 'circle', bgColor: 'accent' })
316
- iconCheck({ size: 20, bg: 'circle', bgColor: 'success' })
317
- iconAlertTriangle({ size: 20, bg: 'circle', bgColor: 'warning' })
318
- iconXCircle({ size: 20, bg: 'circle', bgColor: 'error' })
319
- iconSettings({ size: 20, bg: 'circle', bgColor: 'muted' })`
320
- )}
321
-
322
- <h3 class="doc-h3">Square (rounded)</h3>
323
- ${demo(
324
- `<div style="display:flex;gap:1rem;align-items:center;flex-wrap:wrap">` +
325
- iconZap({ size: 20, bg: 'square', bgColor: 'accent' }) +
326
- iconCheck({ size: 20, bg: 'square', bgColor: 'success' }) +
327
- iconAlertTriangle({ size: 20, bg: 'square', bgColor: 'warning' }) +
328
- iconXCircle({ size: 20, bg: 'square', bgColor: 'error' }) +
329
- iconSettings({size: 20, bg: 'square', bgColor: 'muted' }) +
330
- `</div>`,
331
- `iconZap({ size: 20, bg: 'square', bgColor: 'accent' })`
332
- )}
333
-
334
- <h3 class="doc-h3">With feature()</h3>
335
- <p>Pairs naturally with the <code>feature()</code> icon slot at larger sizes.</p>
336
- ${demo(
337
- `<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:1rem">` +
338
- feature({ icon: iconZap({ size: 22, bg: 'square', bgColor: 'accent' }), title: 'Fast', description: 'Streaming SSR, zero config.' }) +
339
- feature({ icon: iconShield({ size: 22, bg: 'square', bgColor: 'success' }), title: 'Secure', description: 'Security headers by default.' }) +
340
- feature({ icon: iconCode({ size: 22, bg: 'square', bgColor: 'muted' }), title: 'Simple', description: 'Plain JS objects, no JSX.' }) +
341
- `</div>`,
342
- `feature({ icon: iconZap({ size: 22, bg: 'square', bgColor: 'accent' }), title: 'Fast', description: '...' })
343
- feature({ icon: iconShield({ size: 22, bg: 'square', bgColor: 'success' }), title: 'Secure', description: '...' })
344
- feature({ icon: iconCode({ size: 22, bg: 'square', bgColor: 'muted' }), title: 'Simple', description: '...' })`
345
- )}
346
-
347
- <h2 class="doc-h2" id="colour">Colour</h2>
348
- <p>Icons inherit <code>color</code> from their parent — use utility classes or CSS tokens to tint them.</p>
349
- ${demo(
350
- `<div style="display:flex;gap:1.25rem;align-items:center">` +
351
- `<span class="u-text-accent">${iconStar({ size: 20 })}</span>` +
352
- `<span class="u-text-green">${iconCheckCircle({ size: 20 })}</span>` +
353
- `<span class="u-text-red">${iconXCircle({ size: 20 })}</span>` +
354
- `<span class="u-text-yellow">${iconAlertTriangle({ size: 20 })}</span>` +
355
- `<span class="u-text-blue">${iconInfo({ size: 20 })}</span>` +
356
- `<span class="u-text-muted">${iconClock({ size: 20 })}</span>` +
357
- `</div>`,
358
- `<span class="u-text-accent">\${iconStar({ size: 20 })}</span>
359
- <span class="u-text-green">\${iconCheckCircle({ size: 20 })}</span>
360
- <span class="u-text-red">\${iconXCircle({ size: 20 })}</span>
361
- <span class="u-text-yellow">\${iconAlertTriangle({ size: 20 })}</span>`
362
- )}
363
-
364
- <h2 class="doc-h2" id="all-icons">All icons</h2>
365
- <p>Click any icon to preview sizes, colours, and backgrounds.</p>
366
-
367
- ${iconGrid_()}
368
-
369
- ${table(
370
- ['Prop', 'Type', 'Default', 'Description'],
371
- [
372
- ['<code>size</code>', 'number', '16', 'Width and height in px'],
373
- ['<code>class</code>', 'string', '—', 'Extra CSS classes (on wrapper when <code>bg</code> is set, otherwise on the SVG)'],
374
- ['<code>bg</code>', 'string', '—', "'circle' · 'square' — wraps the icon in a tinted background"],
375
- ['<code>bgColor</code>', 'string', "'accent'", "'accent' · 'success' · 'warning' · 'error' · 'muted'"],
376
- ]
377
- )}
378
-
379
- <p style="margin-top:2rem;font-size:.8rem;color:var(--muted)">SVG paths derived from <a href="https://lucide.dev" target="_blank" rel="noopener noreferrer">Lucide</a> (ISC License, Copyright © 2022 Lucide Contributors) and <a href="https://feathericons.com" target="_blank" rel="noopener noreferrer">Feather Icons</a> (MIT License, Copyright © 2013–2017 Cole Bemis). Phosphor hand-pointer icons MIT License, Copyright © 2020 Phosphor Icons.</p>
380
- `
381
-
382
- export default {
383
- route: '/components/icons',
384
- meta: {
385
- theme: 'light',
386
- title: 'Icons — Pulse Docs',
387
- description: 'Built-in icon set for Pulse UI.',
388
- styles: ['/pulse-ui.css', '/theme.css', '/docs.css'],
389
- },
390
-
391
- // Shell: all visible content (nav, examples, icon grid) — sends immediately.
392
- // Modals: 100 hidden <dialog> elements — streamed after. Users never see them
393
- // arrive since dialogs are display:none until opened.
394
- stream: {
395
- shell: ['page'],
396
- deferred: ['modals'],
397
- },
398
-
399
- view: {
400
- page: () => renderComponentPage({
401
- currentHref: '/components/icons',
402
- prev,
403
- next,
404
- name: 'icons',
405
- description: '100 curated icons. All are pure functions returning an SVG string — no external library, no DOM dependency, tree-shakeable. Style: 24×24 viewBox, <code>stroke="currentColor"</code>, compatible with any colour token.',
406
- content: pageContent,
407
- }),
408
- modals: () => iconModals_(),
409
- },
410
- }
@@ -1,71 +0,0 @@
1
- import { renderComponentPage, demo } from '../../lib/component-page.js'
2
- import { prevNext } from '../../lib/nav.js'
3
- import { table } from '../../lib/layout.js'
4
- import { uiImage } from '../../../../src/ui/index.js'
5
-
6
- const { prev, next } = prevNext('/components/image')
7
-
8
- export default {
9
- route: '/components/image',
10
- meta: {
11
- theme: 'light',
12
- title: 'Image — Pulse Docs',
13
- description: 'Responsive image component with optional aspect-ratio crop and caption for Pulse UI.',
14
- styles: ['/pulse-ui.css', '/theme.css', '/docs.css'],
15
- },
16
- view: () => renderComponentPage({
17
- currentHref: '/components/image',
18
- prev,
19
- next,
20
- name: 'uiImage',
21
- description: 'Responsive image component. Supports aspect-ratio cropping with <code>object-fit: cover</code>, optional caption, and rounded corners. Always uses <code>loading="lazy"</code> and <code>decoding="async"</code>.',
22
- content: `
23
-
24
- <h2 class="doc-h2" id="ratio">With aspect ratio</h2>
25
- <p>Set <code>ratio</code> to constrain the image to a fixed aspect ratio. The image fills the crop area with <code>object-fit: cover</code>.</p>
26
- ${demo(
27
- uiImage({ src: '/img/heic1501a.jpg', alt: 'The Pillars of Creation in the Eagle Nebula, captured by the Hubble Space Telescope', ratio: '16/9' }),
28
- `uiImage({ src: '/img/nebula.jpg', alt: 'The Pillars of Creation in the Eagle Nebula', ratio: '16/9' })`,
29
- { col: true }
30
- )}
31
-
32
- <h2 class="doc-h2" id="rounded">Square and rounded</h2>
33
- ${demo(
34
- `<div style="max-width:200px;margin:0 auto">${uiImage({ src: '/img/heic1501a.jpg', alt: 'The Pillars of Creation in the Eagle Nebula, captured by the Hubble Space Telescope', ratio: '1/1', rounded: true })}</div>`,
35
- `uiImage({ src: '/img/nebula.jpg', alt: 'The Pillars of Creation in the Eagle Nebula', ratio: '1/1', rounded: true })`,
36
- { col: true }
37
- )}
38
-
39
- <h2 class="doc-h2" id="caption">With caption</h2>
40
- ${demo(
41
- uiImage({
42
- src: '/img/heic1501a.jpg',
43
- alt: 'The Pillars of Creation in the Eagle Nebula, captured by the Hubble Space Telescope',
44
- ratio: '4/3',
45
- caption: 'The Pillars of Creation, Eagle Nebula. Photo: NASA, ESA, Hubble Heritage Team.',
46
- }),
47
- `uiImage({
48
- src: '/img/nebula.jpg',
49
- alt: 'The Pillars of Creation in the Eagle Nebula',
50
- ratio: '4/3',
51
- caption: 'The Pillars of Creation, Eagle Nebula. Photo: NASA, ESA, Hubble Heritage Team.',
52
- })`,
53
- { col: true }
54
- )}
55
-
56
- ${table(
57
- ['Prop', 'Type', 'Default', 'Description'],
58
- [
59
- ['<code>src</code>', 'string', '—', 'Image source URL'],
60
- ['<code>alt</code>', 'string', '—', 'Alt text — required for accessibility'],
61
- ['<code>caption</code>', 'string', '—', 'Renders a <code>&lt;figcaption&gt;</code> below the image'],
62
- ['<code>ratio</code>', 'string', '—', 'CSS aspect-ratio string e.g. <code>\'16/9\'</code>, <code>\'4/3\'</code>, <code>\'1/1\'</code>. When set, the image fills the crop area with <code>object-fit: cover</code>.'],
63
- ['<code>rounded</code>', 'boolean', 'false', 'Applies full border-radius to the image wrap'],
64
- ['<code>width</code>', 'number', '—', 'Sets the <code>width</code> attribute on the <code>&lt;img&gt;</code>'],
65
- ['<code>height</code>', 'number', '—', 'Sets the <code>height</code> attribute on the <code>&lt;img&gt;</code>'],
66
- ['<code>class</code>', 'string', '—', ''],
67
- ]
68
- )}
69
- `,
70
- }),
71
- }
@@ -1,51 +0,0 @@
1
- import { renderComponentPage, demo } from '../../lib/component-page.js'
2
- import { prevNext } from '../../lib/nav.js'
3
- import { table } from '../../lib/layout.js'
4
- import { input } from '../../../../src/ui/index.js'
5
-
6
- const { prev, next } = prevNext('/components/input')
7
-
8
- export default {
9
- route: '/components/input',
10
- meta: {
11
- theme: 'light',
12
- title: 'Input — Pulse Docs',
13
- description: 'Input component for Pulse UI.',
14
- styles: ['/pulse-ui.css', '/theme.css', '/docs.css'],
15
- },
16
- view: () => renderComponentPage({
17
- currentHref: '/components/input',
18
- prev,
19
- next,
20
- name: 'input',
21
- description: 'The label and error are wired up automatically — <code>for</code>/<code>id</code> and <code>aria-describedby</code> are set from <code>name</code>. You don\'t need to manage ids yourself.',
22
- content: `
23
- ${demo(
24
- input({ name: 'email1', label: 'Email address', type: 'email', placeholder: 'you@example.com' }) +
25
- input({ name: 'email2', label: 'Email address', type: 'email', value: 'bad@', error: 'Enter a valid email address' }) +
26
- input({ name: 'search', label: 'Search', placeholder: 'Filter by name…', hint: 'Results update as you type' }),
27
- `input({ name: 'email', label: 'Email address', type: 'email', placeholder: 'you@example.com' })
28
- input({ name: 'email', label: 'Email address', error: 'Enter a valid email address', value: state.email })
29
- input({ name: 'search', label: 'Search', placeholder: 'Filter by name…', hint: 'Results update as you type' })`,
30
- { col: true }
31
- )}
32
-
33
- ${table(
34
- ['Prop', 'Type', 'Default', 'Description'],
35
- [
36
- ['<code>name</code>', 'string', '—', 'Also used as id base: <code>field-{name}</code>'],
37
- ['<code>label</code>', 'string', '—', ''],
38
- ['<code>type</code>', 'string', 'text', 'Any valid HTML input type'],
39
- ['<code>placeholder</code>', 'string', '—', ''],
40
- ['<code>value</code>', 'string', '—', 'Pre-filled value — escaped automatically'],
41
- ['<code>error</code>', 'string', '—', 'Triggers <code>aria-invalid</code> and <code>role="alert"</code>'],
42
- ['<code>hint</code>', 'string', '—', 'Helper text below the input'],
43
- ['<code>required</code>', 'boolean', 'false','Adds <code>required</code>, <code>aria-required</code>, and a visual asterisk'],
44
- ['<code>disabled</code>', 'boolean', 'false',''],
45
- ['<code>id</code>', 'string', '—', 'Override the generated id'],
46
- ['<code>attrs</code>', 'object', '{}', 'Extra attributes on the <code>&lt;input&gt;</code> element'],
47
- ]
48
- )}
49
- `,
50
- }),
51
- }
@@ -1,51 +0,0 @@
1
- import { renderComponentPage, demo } from '../../lib/component-page.js'
2
- import { prevNext } from '../../lib/nav.js'
3
- import { table } from '../../lib/layout.js'
4
- import { button, stack, media } from '../../../../src/ui/index.js'
5
-
6
- const { prev, next } = prevNext('/components/media')
7
-
8
- export default {
9
- route: '/components/media',
10
- meta: {
11
- theme: 'light',
12
- title: 'Media — Pulse Docs',
13
- description: 'Media component for Pulse UI.',
14
- styles: ['/pulse-ui.css', '/theme.css', '/docs.css'],
15
- },
16
- view: () => renderComponentPage({
17
- currentHref: '/components/media',
18
- prev,
19
- next,
20
- name: 'media',
21
- description: 'Two-column image + text layout. Stacks vertically on mobile. Set <code>reverse</code> to alternate image position for multi-block feature sections.',
22
- content: `
23
- ${demo(
24
- media({
25
- image: `<img src="/img/heic1501a.jpg" alt="The Pillars of Creation in the Eagle Nebula, captured by the Hubble Space Telescope" style="width:100%;height:100%;aspect-ratio:16/9;object-fit:cover;border-radius:8px;display:block">`,
26
- content: stack({ gap: 'md', content: '<h3 style="font-size:1.25rem;font-weight:700;color:var(--ui-text);margin:0">One-tap controls</h3><p style="color:var(--ui-muted);margin:0">Tap to flap. Chippy Bird takes seconds to learn and a lifetime to conquer.</p>' + button({ label: 'Download', href: '#' }) }),
27
- }),
28
- `media({
29
- image: \`<img src="\${screenshot}" alt="App screenshot">\`,
30
- content: stack({ gap: 'md', content:
31
- '<h3>One-tap controls</h3>' +
32
- '<p>Tap to flap.</p>' +
33
- button({ label: 'Download', href: appStoreUrl })
34
- }),
35
- reverse: false,
36
- })`
37
- )}
38
-
39
- ${table(
40
- ['Prop', 'Type', 'Default', 'Description'],
41
- [
42
- ['<code>image</code>', 'string (HTML)', '—', 'Raw HTML slot — img, figure, SVG, or styled div'],
43
- ['<code>content</code>', 'string (HTML)', '—', 'Raw HTML slot'],
44
- ['<code>reverse</code>', 'boolean', 'false', 'Puts text left, image right'],
45
- ['<code>align</code>', 'string', "'center'", "'center' · 'start'"],
46
- ['<code>gap</code>', 'string', "'md'", "'sm' · 'md' · 'lg'"],
47
- ]
48
- )}
49
- `,
50
- }),
51
- }