@invisibleloop/pulse 0.5.0 → 0.6.0

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 (99) hide show
  1. package/CLAUDE.md +36 -0
  2. package/docs/.claude/pulse-checklist.md +7 -5
  3. package/docs/public/.pulse-ui-version +1 -1
  4. package/docs/public/pulse-ui.css +27 -0
  5. package/docs/server.js +1 -0
  6. package/docs/src/lib/highlight.js +2 -299
  7. package/docs/src/lib/nav.js +1 -0
  8. package/docs/src/pages/accessibility.js +0 -1
  9. package/docs/src/pages/auth.js +0 -2
  10. package/docs/src/pages/caching.js +0 -1
  11. package/docs/src/pages/components/accordion.js +0 -1
  12. package/docs/src/pages/components/alert.js +0 -1
  13. package/docs/src/pages/components/app-badge.js +0 -1
  14. package/docs/src/pages/components/avatar.js +0 -1
  15. package/docs/src/pages/components/badge.js +0 -1
  16. package/docs/src/pages/components/banner.js +0 -1
  17. package/docs/src/pages/components/breadcrumbs.js +0 -1
  18. package/docs/src/pages/components/button.js +0 -1
  19. package/docs/src/pages/components/card.js +0 -1
  20. package/docs/src/pages/components/carousel.js +0 -1
  21. package/docs/src/pages/components/charts.js +0 -1
  22. package/docs/src/pages/components/checkbox.js +0 -1
  23. package/docs/src/pages/components/cluster.js +0 -1
  24. package/docs/src/pages/components/code-window.js +0 -1
  25. package/docs/src/pages/components/container.js +0 -1
  26. package/docs/src/pages/components/divider.js +0 -1
  27. package/docs/src/pages/components/empty.js +0 -1
  28. package/docs/src/pages/components/feature.js +0 -1
  29. package/docs/src/pages/components/fieldset.js +0 -1
  30. package/docs/src/pages/components/footer.js +0 -1
  31. package/docs/src/pages/components/grid.js +0 -1
  32. package/docs/src/pages/components/heading.js +0 -1
  33. package/docs/src/pages/components/icons.js +0 -1
  34. package/docs/src/pages/components/image.js +0 -1
  35. package/docs/src/pages/components/input.js +0 -1
  36. package/docs/src/pages/components/list.js +0 -1
  37. package/docs/src/pages/components/media.js +0 -1
  38. package/docs/src/pages/components/modal.js +0 -1
  39. package/docs/src/pages/components/nav.js +0 -1
  40. package/docs/src/pages/components/pricing.js +0 -1
  41. package/docs/src/pages/components/progress.js +0 -1
  42. package/docs/src/pages/components/prose.js +2 -3
  43. package/docs/src/pages/components/pullquote.js +0 -1
  44. package/docs/src/pages/components/radio.js +0 -1
  45. package/docs/src/pages/components/rating.js +0 -1
  46. package/docs/src/pages/components/search.js +0 -1
  47. package/docs/src/pages/components/section.js +0 -1
  48. package/docs/src/pages/components/segmented.js +0 -1
  49. package/docs/src/pages/components/select.js +0 -1
  50. package/docs/src/pages/components/spinner.js +0 -1
  51. package/docs/src/pages/components/stack.js +0 -1
  52. package/docs/src/pages/components/stat.js +0 -1
  53. package/docs/src/pages/components/stepper.js +0 -1
  54. package/docs/src/pages/components/table.js +0 -1
  55. package/docs/src/pages/components/testimonial.js +0 -1
  56. package/docs/src/pages/components/textarea.js +0 -1
  57. package/docs/src/pages/components/timeline.js +0 -1
  58. package/docs/src/pages/components/toggle.js +0 -1
  59. package/docs/src/pages/components/tooltip.js +0 -1
  60. package/docs/src/pages/components.js +0 -1
  61. package/docs/src/pages/config.js +0 -1
  62. package/docs/src/pages/deploy.js +0 -1
  63. package/docs/src/pages/extending.js +0 -1
  64. package/docs/src/pages/faq.js +0 -1
  65. package/docs/src/pages/getting-started.js +0 -1
  66. package/docs/src/pages/guard.js +0 -3
  67. package/docs/src/pages/how-it-works.js +0 -1
  68. package/docs/src/pages/images.js +0 -2
  69. package/docs/src/pages/markdown.js +155 -0
  70. package/docs/src/pages/meta.js +0 -2
  71. package/docs/src/pages/navigation.js +0 -1
  72. package/docs/src/pages/performance.js +0 -2
  73. package/docs/src/pages/project-structure.js +0 -2
  74. package/docs/src/pages/prompt-examples.js +0 -1
  75. package/docs/src/pages/raw-responses.js +0 -4
  76. package/docs/src/pages/routing.js +0 -2
  77. package/docs/src/pages/server-api.js +0 -2
  78. package/docs/src/pages/server-data.js +18 -1
  79. package/docs/src/pages/slash-commands.js +0 -1
  80. package/docs/src/pages/spec.js +2 -1
  81. package/docs/src/pages/streaming.js +0 -2
  82. package/docs/src/pages/testing.js +0 -1
  83. package/docs/src/pages/validation.js +0 -1
  84. package/package.json +5 -1
  85. package/public/.pulse-ui-version +1 -1
  86. package/public/pulse-ui.css +27 -0
  87. package/scripts/build.js +53 -2
  88. package/scripts/strip-server.test.js +74 -2
  89. package/src/agent/checklist.md +20 -0
  90. package/src/agent/guide-server.md +62 -0
  91. package/src/agent/guide-spec.md +5 -3
  92. package/src/agent/guide.md +105 -2
  93. package/src/cli/dev.js +4 -0
  94. package/src/cli/scaffold.js +0 -2
  95. package/src/cli/start.js +1 -0
  96. package/src/md/highlight.js +369 -0
  97. package/src/md/index.js +337 -0
  98. package/src/spec/schema.js +8 -1
  99. package/types/md.d.ts +66 -0
package/CLAUDE.md CHANGED
@@ -448,6 +448,42 @@ Pulse views are plain JS template literals, not JSX. These React patterns are **
448
448
 
449
449
  Also: mutations must be pure (no fetch, no DOM access). Only `actions` can be async.
450
450
 
451
+ ## Markdown
452
+
453
+ Pulse has a built-in markdown parser. Use it for blog posts, docs pages, and any static content written in `.md` files. Import from `@invisibleloop/pulse/md`. All parsing is server-side — zero browser JS.
454
+
455
+ ```js
456
+ import { md } from '@invisibleloop/pulse/md'
457
+ import { prose } from '@invisibleloop/pulse/ui'
458
+
459
+ // Static path
460
+ const page = md('content/about.md')
461
+
462
+ // Dynamic route — :param resolved from ctx.params
463
+ const post = md('content/blog/:slug.md')
464
+
465
+ export default {
466
+ route: '/blog/:slug',
467
+ meta: {
468
+ title: async (ctx) => (await post(ctx)).frontmatter.title,
469
+ description: async (ctx) => (await post(ctx)).frontmatter.description,
470
+ },
471
+ server: { post },
472
+ view: (state, { post }) => `
473
+ <main id="main-content">
474
+ ${prose({ content: post.html })}
475
+ </main>
476
+ `,
477
+ onViewError: () => `<main id="main-content"><p>Post not found.</p></main>`,
478
+ }
479
+ ```
480
+
481
+ - `md(path)` returns an async fetcher — call it in both `meta` and `server`, the file is read only once per request
482
+ - Result is `{ html, frontmatter }` — `html` goes into `prose()`, `frontmatter` holds the `---` key/value pairs
483
+ - Missing file throws `{ status: 404 }` — always add `onViewError` on dynamic markdown routes
484
+ - For markdown from a database/API use `parseMd(source)` directly: `import { parseMd } from '@invisibleloop/pulse/md'`
485
+ - Use `new URL('./content/file.md', import.meta.url)` to resolve relative to the spec file rather than `process.cwd()`
486
+
451
487
  ## Check Components Before Building
452
488
 
453
489
  Before writing any UI HTML by hand, check `src/ui/index.js` — there are 50+ components available. Use them. Do not reinvent `button`, `card`, `alert`, `modal`, `spinner`, `badge`, `input`, etc.
@@ -4,11 +4,7 @@ Before finishing any spec, verify every point below. Fix anything that fails.
4
4
 
5
5
  ### Critical
6
6
 
7
- - **`hydrate` is set on every interactive page.** Without it, `data-event` / `data-action` bindings do nothing, `persist` never runs, and client-side navigation cannot re-mount the page. Every spec with `mutations`, `actions`, or `persist` must include:
8
- ```js
9
- hydrate: '/src/pages/my-page.js', // browser-importable path to this file
10
- ```
11
- Omit `hydrate` only for purely server-rendered pages with zero client interactivity.
7
+ - **Do not set `hydrate` in specs.** It is auto-derived by the framework from the URL entry passed to `createServer`. Specs with `mutations`, `actions`, or `persist` are hydrated automatically. Purely server-rendered specs get zero JavaScript — no configuration needed.
12
8
 
13
9
  ### Components first
14
10
 
@@ -81,6 +77,12 @@ Before finishing any spec, verify every point below. Fix anything that fails.
81
77
  ```
82
78
  Supported selectors: `tag`, `.class`, `#id`, `[attr]`, `[attr="value"]`, and combinations (`button.primary[disabled]`).
83
79
 
80
+ ### Markdown
81
+
82
+ - **Use `md()` from `@invisibleloop/pulse/md` for `.md` file content.** Never read and parse markdown manually. Pass the result's `html` to `prose()`.
83
+ - **Call the same `md()` fetcher in both `meta` and `server`** — it caches on `ctx._mdCache` so the file is only read once per request. Do not create two separate fetchers for the same file.
84
+ - **Always add `onViewError` on dynamic markdown routes** (routes with `:param` segments loading `.md` files). A missing file throws `{ status: 404 }` which must be caught gracefully.
85
+
84
86
  ### View error handling
85
87
 
86
88
  - **Define `onViewError` on any page where the view could throw due to bad or missing data.** Without it, a runtime view error returns a 500 on the server and shows a generic inline message on the client. With it, the server returns 200 with your fallback HTML instead of a 500, and the client renders your fallback:
@@ -1 +1 @@
1
- 0.2.14
1
+ 0.5.0
@@ -2806,6 +2806,26 @@ fieldset[disabled] .ui-rating-stars .ui-rating-star:hover ~ .ui-rating-star { co
2806
2806
  color: var(--ui-text);
2807
2807
  }
2808
2808
 
2809
+ /* ─── Syntax highlighting tokens ────────────────────────────────────────────
2810
+ Used by ui-prose pre code blocks rendered via @invisibleloop/pulse/md.
2811
+ Dark theme defaults; light theme overrides below. */
2812
+ :root {
2813
+ --tok-kw: #c792ea;
2814
+ --tok-str: #c3e88d;
2815
+ --tok-cmt: #7a8a9a;
2816
+ --tok-num: #f78c6c;
2817
+ --tok-fn: #82aaff;
2818
+ --tok-op: #89ddff;
2819
+ --tok-punct: #7e8899;
2820
+ }
2821
+ .tok-kw { color: var(--tok-kw); }
2822
+ .tok-str { color: var(--tok-str); }
2823
+ .tok-cmt { color: var(--tok-cmt); font-style: italic; }
2824
+ .tok-num { color: var(--tok-num); }
2825
+ .tok-fn { color: var(--tok-fn); }
2826
+ .tok-op { color: var(--tok-op); }
2827
+ .tok-punct { color: var(--tok-punct); }
2828
+
2809
2829
  /* ─── Light theme ────────────────────────────────────────────────────────────
2810
2830
  Apply to any ancestor element: <body data-theme="light"> or .ui-theme-light
2811
2831
  All --ui-* tokens are overridden; component markup stays identical. */
@@ -2829,4 +2849,11 @@ fieldset[disabled] .ui-rating-stars .ui-rating-star:hover ~ .ui-rating-star { co
2829
2849
  --ui-blue: #1e40af;
2830
2850
  --ui-blue-dim: rgba( 30, 64, 175, .12);
2831
2851
  --ui-nav-sticky-bg: rgba(255, 255, 255, .85);
2852
+ --tok-kw: #7c3aed;
2853
+ --tok-str: #15803d;
2854
+ --tok-cmt: #6b7280;
2855
+ --tok-num: #c2410c;
2856
+ --tok-fn: #1d4ed8;
2857
+ --tok-op: #0891b2;
2858
+ --tok-punct: #6b7280;
2832
2859
  }
package/docs/server.js CHANGED
@@ -33,6 +33,7 @@ await createServer(
33
33
  new URL('./src/pages/persist.js', import.meta.url),
34
34
  // Server
35
35
  new URL('./src/pages/server-data.js', import.meta.url),
36
+ new URL('./src/pages/markdown.js', import.meta.url),
36
37
  new URL('./src/pages/store.js', import.meta.url),
37
38
  new URL('./src/pages/routing.js', import.meta.url),
38
39
  new URL('./src/pages/streaming.js', import.meta.url),
@@ -1,299 +1,2 @@
1
- /**
2
- * Pulse Docs — Server-side syntax highlighter
3
- *
4
- * Character-by-character tokeniser. No dependencies, no client JS.
5
- * Returns an HTML string of <span> elements ready to drop inside <code>.
6
- */
7
-
8
- const KEYWORDS = new Set([
9
- 'export', 'default', 'import', 'from', 'as',
10
- 'async', 'await', 'const', 'let', 'var',
11
- 'return', 'if', 'else', 'for', 'while', 'of', 'in',
12
- 'true', 'false', 'null', 'undefined',
13
- 'function', 'class', 'new', 'this', 'typeof', 'instanceof',
14
- 'try', 'catch', 'finally', 'throw',
15
- ])
16
-
17
- function esc(s) {
18
- return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
19
- }
20
-
21
- function span(cls, text) {
22
- return `<span class="${cls}">${esc(text)}</span>`
23
- }
24
-
25
- export function highlight(code, lang = 'js') {
26
- if (lang === 'bash') return highlightBash(code)
27
- if (lang === 'html') return highlightHtml(code)
28
- return highlightJs(code)
29
- }
30
-
31
- // ---------------------------------------------------------------------------
32
- // JavaScript tokeniser
33
- // ---------------------------------------------------------------------------
34
-
35
- function highlightJs(code) {
36
- let out = ''
37
- let i = 0
38
- const n = code.length
39
-
40
- while (i < n) {
41
- const ch = code[i]
42
- const ch2 = code[i + 1]
43
-
44
- // Single-line comment
45
- if (ch === '/' && ch2 === '/') {
46
- const end = code.indexOf('\n', i)
47
- const tok = end === -1 ? code.slice(i) : code.slice(i, end)
48
- out += span('tok-cmt', tok)
49
- i += tok.length
50
- continue
51
- }
52
-
53
- // Block comment
54
- if (ch === '/' && ch2 === '*') {
55
- const end = code.indexOf('*/', i + 2)
56
- const tok = end === -1 ? code.slice(i) : code.slice(i, end + 2)
57
- out += span('tok-cmt', tok)
58
- i += tok.length
59
- continue
60
- }
61
-
62
- // Template literal
63
- if (ch === '`') {
64
- let tok = '`'
65
- i++
66
- let depth = 0
67
- while (i < n) {
68
- if (code[i] === '\\') {
69
- tok += code[i] + (code[i + 1] || '')
70
- i += 2
71
- continue
72
- }
73
- if (code[i] === '$' && code[i + 1] === '{') {
74
- tok += '${'
75
- i += 2
76
- depth++
77
- continue
78
- }
79
- if (code[i] === '}' && depth > 0) {
80
- tok += '}'
81
- i++
82
- depth--
83
- continue
84
- }
85
- if (code[i] === '`' && depth === 0) {
86
- tok += '`'
87
- i++
88
- break
89
- }
90
- tok += code[i++]
91
- }
92
- out += span('tok-str', tok)
93
- continue
94
- }
95
-
96
- // Single or double quoted string
97
- if (ch === '"' || ch === "'") {
98
- let tok = ch
99
- i++
100
- while (i < n && code[i] !== ch && code[i] !== '\n') {
101
- if (code[i] === '\\') {
102
- tok += code[i] + (code[i + 1] || '')
103
- i += 2
104
- continue
105
- }
106
- tok += code[i++]
107
- }
108
- if (i < n && code[i] === ch) tok += code[i++]
109
- out += span('tok-str', tok)
110
- continue
111
- }
112
-
113
- // Number
114
- if (/\d/.test(ch) && (i === 0 || !/\w/.test(code[i - 1]))) {
115
- let tok = ''
116
- while (i < n && /[\d.xXa-fA-F]/.test(code[i])) tok += code[i++]
117
- out += span('tok-num', tok)
118
- continue
119
- }
120
-
121
- // Identifier / keyword
122
- if (/[a-zA-Z_$]/.test(ch)) {
123
- let tok = ''
124
- while (i < n && /[\w$]/.test(code[i])) tok += code[i++]
125
-
126
- if (KEYWORDS.has(tok)) {
127
- out += span('tok-kw', tok)
128
- } else {
129
- // Look ahead: followed by ( → function call
130
- let j = i
131
- while (j < n && code[j] === ' ') j++
132
- if (code[j] === '(') {
133
- out += span('tok-fn', tok)
134
- } else {
135
- out += esc(tok)
136
- }
137
- }
138
- continue
139
- }
140
-
141
- // Operators
142
- if (/[=!<>|&?.]/.test(ch)) {
143
- let tok = ch
144
- // Grab two-char operators
145
- if (/[=!<>|&?]/.test(ch) && /[=|&>?]/.test(ch2)) {
146
- tok = code.slice(i, i + 2)
147
- // Three-char: ===, !==, ...
148
- if (code[i + 2] === '=') tok = code.slice(i, i + 3)
149
- }
150
- out += span('tok-op', tok)
151
- i += tok.length
152
- continue
153
- }
154
-
155
- // Punctuation
156
- if (/[{}()[\],;:]/.test(ch)) {
157
- out += span('tok-punct', ch)
158
- i++
159
- continue
160
- }
161
-
162
- // Everything else (whitespace, newlines, etc.)
163
- out += esc(ch)
164
- i++
165
- }
166
-
167
- return out
168
- }
169
-
170
- // ---------------------------------------------------------------------------
171
- // Bash tokeniser (minimal)
172
- // ---------------------------------------------------------------------------
173
-
174
- function highlightBash(code) {
175
- return code.split('\n').map(line => {
176
- // Comment line
177
- if (/^\s*#/.test(line)) return span('tok-cmt', line)
178
-
179
- let out = ''
180
- let i = 0
181
- while (i < line.length) {
182
- // Inline comment
183
- if (line[i] === '#') {
184
- out += span('tok-cmt', line.slice(i))
185
- break
186
- }
187
- // String
188
- if (line[i] === '"' || line[i] === "'") {
189
- const q = line[i]
190
- let tok = q
191
- i++
192
- while (i < line.length && line[i] !== q) tok += line[i++]
193
- tok += line[i] === q ? line[i++] : ''
194
- out += span('tok-str', tok)
195
- continue
196
- }
197
- // Flag/option
198
- if (line[i] === '-' && /\w/.test(line[i + 1] || '')) {
199
- let tok = '-'
200
- i++
201
- while (i < line.length && /[-\w]/.test(line[i])) tok += line[i++]
202
- out += span('tok-op', tok)
203
- continue
204
- }
205
- out += esc(line[i++])
206
- }
207
- return out
208
- }).join('\n')
209
- }
210
-
211
- // ---------------------------------------------------------------------------
212
- // HTML tokeniser (minimal)
213
- // ---------------------------------------------------------------------------
214
-
215
- function highlightHtmlTag(tag) {
216
- // Single-pass tag highlighter — avoids the chained .replace() trap where a
217
- // later pass re-processes class="tok-fn" strings already injected by an
218
- // earlier pass, producing broken HTML like <span class=<span …>"tok-fn"</span>>.
219
- let out = ''
220
- let i = 0
221
-
222
- // Opening < or </
223
- out += '&lt;'
224
- i++ // skip <
225
- if (tag[i] === '/') { out += '/'; i++ }
226
-
227
- // Tag name
228
- let name = ''
229
- while (i < tag.length && /[\w-]/.test(tag[i])) name += tag[i++]
230
- if (name) out += span('tok-kw', name)
231
-
232
- // Attributes and remainder up to >
233
- while (i < tag.length) {
234
- const ch = tag[i]
235
- if (ch === '>') { out += '&gt;'; i++; break }
236
- if (ch === '/') { out += '/'; i++; continue }
237
-
238
- if (/[\w-]/.test(ch)) {
239
- // Attribute name
240
- let attr = ''
241
- while (i < tag.length && /[\w-]/.test(tag[i])) attr += tag[i++]
242
-
243
- if (tag[i] === '=') {
244
- out += span('tok-fn', attr) + esc('=')
245
- i++ // skip =
246
- // Attribute value
247
- const q = tag[i]
248
- if (q === '"' || q === "'") {
249
- let val = q
250
- i++
251
- while (i < tag.length && tag[i] !== q) {
252
- if (tag[i] === '\\') { val += tag[i] + (tag[i + 1] || ''); i += 2; continue }
253
- val += tag[i++]
254
- }
255
- if (i < tag.length) val += tag[i++] // closing quote
256
- out += span('tok-str', val)
257
- }
258
- } else {
259
- out += esc(attr)
260
- }
261
- continue
262
- }
263
-
264
- out += esc(ch)
265
- i++
266
- }
267
-
268
- return out
269
- }
270
-
271
- function highlightHtml(code) {
272
- let out = ''
273
- let i = 0
274
- const n = code.length
275
-
276
- while (i < n) {
277
- // Comment
278
- if (code.slice(i, i + 4) === '<!--') {
279
- const end = code.indexOf('-->', i + 4)
280
- const tok = end === -1 ? code.slice(i) : code.slice(i, end + 3)
281
- out += span('tok-cmt', tok)
282
- i += tok.length
283
- continue
284
- }
285
-
286
- // Tag
287
- if (code[i] === '<') {
288
- const end = code.indexOf('>', i)
289
- if (end === -1) { out += esc(code[i++]); continue }
290
- out += highlightHtmlTag(code.slice(i, end + 1))
291
- i = end + 1
292
- continue
293
- }
294
-
295
- out += esc(code[i++])
296
- }
297
-
298
- return out
299
- }
1
+ // Re-export from the framework's canonical highlight implementation.
2
+ export { highlight } from '../../../src/md/highlight.js'
@@ -38,6 +38,7 @@ export const NAV = [
38
38
  section: 'Server',
39
39
  items: [
40
40
  { label: 'Server Data', href: '/server-data' },
41
+ { label: 'Markdown', href: '/markdown' },
41
42
  { label: 'Global Store', href: '/store' },
42
43
  { label: 'Routing', href: '/routing' },
43
44
  { label: 'Streaming SSR', href: '/streaming' },
@@ -11,7 +11,6 @@ export default {
11
11
  description: 'Keyboard navigation, focus management, semantic HTML, and ARIA patterns in Pulse.',
12
12
  styles: ['/docs.css'],
13
13
  },
14
- state: {},
15
14
  view: () => renderLayout({
16
15
  currentHref: '/accessibility',
17
16
  prev,
@@ -11,7 +11,6 @@ export default {
11
11
  description: 'Integrating Auth0 OAuth authentication with Pulse using guard functions, ctx.setCookie, and raw response specs.',
12
12
  styles: ['/docs.css'],
13
13
  },
14
- state: {},
15
14
  view: () => renderLayout({
16
15
  currentHref: '/auth',
17
16
  prev,
@@ -141,7 +140,6 @@ export default {
141
140
  profile: async (ctx) => fetchUserProfile(ctx.cookies.session),
142
141
  },
143
142
 
144
- state: {},
145
143
  view: (state, server) => \`
146
144
  <main id="main-content">
147
145
  <h1>Welcome, \${server.profile.name}</h1>
@@ -11,7 +11,6 @@ export default {
11
11
  description: 'HTTP cache headers, in-process server data caching, and asset caching in Pulse.',
12
12
  styles: ['/docs.css'],
13
13
  },
14
- state: {},
15
14
  view: () => renderLayout({
16
15
  currentHref: '/caching',
17
16
  prev,
@@ -12,7 +12,6 @@ export default {
12
12
  description: 'Accordion component for Pulse UI.',
13
13
  styles: ['/pulse-ui.css', '/docs.css'],
14
14
  },
15
- state: {},
16
15
  view: () => renderComponentPage({
17
16
  currentHref: '/components/accordion',
18
17
  prev,
@@ -11,7 +11,6 @@ export default {
11
11
  description: 'Alert component for Pulse UI.',
12
12
  styles: ['/pulse-ui.css', '/docs.css'],
13
13
  },
14
- state: {},
15
14
  view: () => renderComponentPage({
16
15
  currentHref: '/components/alert',
17
16
  prev,
@@ -12,7 +12,6 @@ export default {
12
12
  description: 'App Badge component for Pulse UI.',
13
13
  styles: ['/pulse-ui.css', '/docs.css'],
14
14
  },
15
- state: {},
16
15
  view: () => renderComponentPage({
17
16
  currentHref: '/components/app-badge',
18
17
  prev,
@@ -11,7 +11,6 @@ export default {
11
11
  description: 'Avatar component for Pulse UI.',
12
12
  styles: ['/pulse-ui.css', '/docs.css'],
13
13
  },
14
- state: {},
15
14
  view: () => renderComponentPage({
16
15
  currentHref: '/components/avatar',
17
16
  prev,
@@ -11,7 +11,6 @@ export default {
11
11
  description: 'Badge component for Pulse UI.',
12
12
  styles: ['/pulse-ui.css', '/docs.css'],
13
13
  },
14
- state: {},
15
14
  view: () => renderComponentPage({
16
15
  currentHref: '/components/badge',
17
16
  prev,
@@ -12,7 +12,6 @@ export default {
12
12
  description: 'Banner component for Pulse UI.',
13
13
  styles: ['/pulse-ui.css', '/docs.css'],
14
14
  },
15
- state: {},
16
15
  view: () => renderComponentPage({
17
16
  currentHref: '/components/banner',
18
17
  prev,
@@ -12,7 +12,6 @@ export default {
12
12
  description: 'Accessible breadcrumb navigation component for Pulse UI.',
13
13
  styles: ['/pulse-ui.css', '/docs.css'],
14
14
  },
15
- state: {},
16
15
  view: () => renderComponentPage({
17
16
  currentHref: '/components/breadcrumbs',
18
17
  prev,
@@ -13,7 +13,6 @@ export default {
13
13
  description: 'Button component for Pulse UI.',
14
14
  styles: ['/pulse-ui.css', '/docs.css'],
15
15
  },
16
- state: {},
17
16
  view: () => renderComponentPage({
18
17
  currentHref: '/components/button',
19
18
  prev,
@@ -12,7 +12,6 @@ export default {
12
12
  description: 'Card component for Pulse UI.',
13
13
  styles: ['/pulse-ui.css', '/docs.css'],
14
14
  },
15
- state: {},
16
15
  view: () => renderComponentPage({
17
16
  currentHref: '/components/card',
18
17
  prev,
@@ -16,7 +16,6 @@ export default {
16
16
  description: 'Carousel / slider component for Pulse UI.',
17
17
  styles: ['/pulse-ui.css', '/docs.css'],
18
18
  },
19
- state: {},
20
19
  view: () => renderComponentPage({
21
20
  currentHref: '/components/carousel',
22
21
  prev,
@@ -32,7 +32,6 @@ export default {
32
32
  description: 'Server-rendered SVG charts for Pulse UI.',
33
33
  styles: ['/pulse-ui.css', '/docs.css'],
34
34
  },
35
- state: {},
36
35
  view: () => renderComponentPage({
37
36
  currentHref: '/components/charts',
38
37
  prev,
@@ -12,7 +12,6 @@ export default {
12
12
  description: 'Custom-styled checkbox component for Pulse UI.',
13
13
  styles: ['/pulse-ui.css', '/docs.css'],
14
14
  },
15
- state: {},
16
15
  view: () => renderComponentPage({
17
16
  currentHref: '/components/checkbox',
18
17
  prev,
@@ -12,7 +12,6 @@ export default {
12
12
  description: 'Cluster component for Pulse UI.',
13
13
  styles: ['/pulse-ui.css', '/docs.css'],
14
14
  },
15
- state: {},
16
15
  view: () => renderComponentPage({
17
16
  currentHref: '/components/cluster',
18
17
  prev,
@@ -12,7 +12,6 @@ export default {
12
12
  description: 'macOS-style window chrome around a code block. Accepts pre-highlighted HTML as the content slot.',
13
13
  styles: ['/pulse-ui.css', '/docs.css'],
14
14
  },
15
- state: {},
16
15
  view: () => renderComponentPage({
17
16
  currentHref: '/components/code-window',
18
17
  prev,
@@ -12,7 +12,6 @@ export default {
12
12
  description: 'Container component for Pulse UI.',
13
13
  styles: ['/pulse-ui.css', '/docs.css'],
14
14
  },
15
- state: {},
16
15
  view: () => renderComponentPage({
17
16
  currentHref: '/components/container',
18
17
  prev,
@@ -12,7 +12,6 @@ export default {
12
12
  description: 'Divider component for Pulse UI.',
13
13
  styles: ['/pulse-ui.css', '/docs.css'],
14
14
  },
15
- state: {},
16
15
  view: () => renderComponentPage({
17
16
  currentHref: '/components/divider',
18
17
  prev,
@@ -11,7 +11,6 @@ export default {
11
11
  description: 'Empty state component for Pulse UI.',
12
12
  styles: ['/pulse-ui.css', '/docs.css'],
13
13
  },
14
- state: {},
15
14
  view: () => renderComponentPage({
16
15
  currentHref: '/components/empty',
17
16
  prev,
@@ -13,7 +13,6 @@ export default {
13
13
  description: 'Feature component for Pulse UI.',
14
14
  styles: ['/pulse-ui.css', '/docs.css'],
15
15
  },
16
- state: {},
17
16
  view: () => renderComponentPage({
18
17
  currentHref: '/components/feature',
19
18
  prev,
@@ -12,7 +12,6 @@ export default {
12
12
  description: 'Semantic grouping of related form fields with an accessible legend.',
13
13
  styles: ['/pulse-ui.css', '/docs.css'],
14
14
  },
15
- state: {},
16
15
  view: () => renderComponentPage({
17
16
  currentHref: '/components/fieldset',
18
17
  prev,
@@ -12,7 +12,6 @@ export default {
12
12
  description: 'Accessible site footer with logo slot, navigation links, and legal text.',
13
13
  styles: ['/pulse-ui.css', '/docs.css'],
14
14
  },
15
- state: {},
16
15
  view: () => renderComponentPage({
17
16
  currentHref: '/components/footer',
18
17
  prev,
@@ -16,7 +16,6 @@ export default {
16
16
  description: 'Grid component for Pulse UI.',
17
17
  styles: ['/pulse-ui.css', '/docs.css'],
18
18
  },
19
- state: {},
20
19
  view: () => renderComponentPage({
21
20
  currentHref: '/components/grid',
22
21
  prev,
@@ -12,7 +12,6 @@ export default {
12
12
  description: 'Styled semantic heading component for Pulse UI.',
13
13
  styles: ['/pulse-ui.css', '/docs.css'],
14
14
  },
15
- state: {},
16
15
  view: () => renderComponentPage({
17
16
  currentHref: '/components/heading',
18
17
  prev,