@burdenoff/website-sdk 2026.829.8 → 2026.921.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.
- package/README.md +112 -1
- package/dist/components/contact.js +4 -1
- package/dist/components/contact.js.map +1 -1
- package/dist/components/contact.mjs +4 -1
- package/dist/components/contact.mjs.map +1 -1
- package/dist/components/explore.d.mts +14 -1
- package/dist/components/explore.d.ts +14 -1
- package/dist/components/explore.js +217 -43
- package/dist/components/explore.js.map +1 -1
- package/dist/components/explore.mjs +217 -45
- package/dist/components/explore.mjs.map +1 -1
- package/dist/components/newsletter.js +4 -1
- package/dist/components/newsletter.js.map +1 -1
- package/dist/components/newsletter.mjs +4 -1
- package/dist/components/newsletter.mjs.map +1 -1
- package/dist/components/partners.js +23 -13
- package/dist/components/partners.js.map +1 -1
- package/dist/components/partners.mjs +23 -13
- package/dist/components/partners.mjs.map +1 -1
- package/dist/components/pricing.js +4 -1
- package/dist/components/pricing.js.map +1 -1
- package/dist/components/pricing.mjs +4 -1
- package/dist/components/pricing.mjs.map +1 -1
- package/dist/components/unsubscribe.js +4 -1
- package/dist/components/unsubscribe.js.map +1 -1
- package/dist/components/unsubscribe.mjs +4 -1
- package/dist/components/unsubscribe.mjs.map +1 -1
- package/dist/components/waitlist.js +4 -1
- package/dist/components/waitlist.js.map +1 -1
- package/dist/components/waitlist.mjs +4 -1
- package/dist/components/waitlist.mjs.map +1 -1
- package/dist/content/index.js +23 -13
- package/dist/content/index.js.map +1 -1
- package/dist/content/index.mjs +23 -13
- package/dist/content/index.mjs.map +1 -1
- package/dist/{use-explore-chat-BzXCz2UM.d.mts → explore-types-CLw19R76.d.mts} +1 -131
- package/dist/{use-explore-chat-BzXCz2UM.d.ts → explore-types-CLw19R76.d.ts} +1 -131
- package/dist/hooks/index.d.mts +2 -1
- package/dist/hooks/index.d.ts +2 -1
- package/dist/hooks/index.js +23 -7
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/index.mjs +23 -7
- package/dist/hooks/index.mjs.map +1 -1
- package/dist/index.d.mts +143 -3
- package/dist/index.d.ts +143 -3
- package/dist/index.js +1552 -207
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1548 -210
- package/dist/index.mjs.map +1 -1
- package/dist/seo/index.js +4 -1
- package/dist/seo/index.js.map +1 -1
- package/dist/seo/index.mjs +4 -1
- package/dist/seo/index.mjs.map +1 -1
- package/dist/store/index.js +23 -13
- package/dist/store/index.js.map +1 -1
- package/dist/store/index.mjs +23 -13
- package/dist/store/index.mjs.map +1 -1
- package/dist/use-explore-chat-CvW-kyIm.d.mts +145 -0
- package/dist/use-explore-chat-DdGBimiz.d.ts +145 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -191,10 +191,121 @@ with it. This is separate from the v2 checkbox used by `ContactPage`.
|
|
|
191
191
|
setFocusProduct, send, stop, retry, reset, canSend }` — if you want to build a
|
|
192
192
|
different shell around it.
|
|
193
193
|
|
|
194
|
+
**Inline concept illustrations.** An answer may embed images from the product
|
|
195
|
+
sites' concept pages (``, followed by the backend's own caption
|
|
196
|
+
line). They render only once the answer is `COMPLETED` — never from a streaming
|
|
197
|
+
preview — and only when the URL is `https` on a catalog product's website
|
|
198
|
+
(apex or `www.`); anything else renders nothing. Each image is lazy-loaded,
|
|
199
|
+
links to the full file in a new tab (unless the markdown already wraps it in a
|
|
200
|
+
link, which then stays the only link), carries a "Concept illustration" badge,
|
|
201
|
+
and disappears entirely if it fails to load. Nothing to wire on the website.
|
|
202
|
+
|
|
194
203
|
**Testing hooks.** Stable `data-boff-explore` attributes are attached for the
|
|
195
204
|
end-to-end suite: `page`, `messages`, `composer`, `chip`, `thinking`, `user`,
|
|
196
205
|
`assistant` (plus `data-status`), `reference`, `cta` (plus `data-kind`),
|
|
197
|
-
`cta-header`, `cta-mobile`, `cta-floating`, `send`, `stop`, `new-chat`, `error
|
|
206
|
+
`cta-header`, `cta-mobile`, `cta-floating`, `send`, `stop`, `new-chat`, `error`,
|
|
207
|
+
`inline-image`.
|
|
208
|
+
|
|
209
|
+
### Concepts (`/concepts` + `/concept/:slug`)
|
|
210
|
+
|
|
211
|
+
`ConceptsPage` and `ConceptDetailPage` render a product's concepts: each one a
|
|
212
|
+
feature or flow, shown through the mockups drawn for it and explained in
|
|
213
|
+
reviewed copy. Every image is labelled an **illustration of the concept, not a
|
|
214
|
+
screenshot of the actual product** (`CONCEPT_ILLUSTRATION_NOTICE`).
|
|
215
|
+
|
|
216
|
+
The data is a generated `src/data/concepts.ts` in each site — never hand-edit
|
|
217
|
+
it; regenerate with
|
|
218
|
+
`python3 ~/products/dev/scripts/concepts/build_site_concepts.py site --product <p>`.
|
|
219
|
+
Both pages take the data as props and make no requests, so they prerender
|
|
220
|
+
completely. Import them from the package **root**, like every other page.
|
|
221
|
+
|
|
222
|
+
```tsx
|
|
223
|
+
import { Link, Navigate, Route, useParams } from 'react-router'
|
|
224
|
+
import {
|
|
225
|
+
ConceptDetailPage,
|
|
226
|
+
ConceptsPage,
|
|
227
|
+
CONCEPTS_PATH,
|
|
228
|
+
getConceptBySlug,
|
|
229
|
+
type ConceptLinkProps,
|
|
230
|
+
} from '@burdenoff/website-sdk'
|
|
231
|
+
import { CONCEPT_CATEGORIES, CONCEPTS } from '@/data/concepts'
|
|
232
|
+
|
|
233
|
+
const SITE_URL = 'https://vibecontrols.com'
|
|
234
|
+
|
|
235
|
+
// Router adapter, so concept → concept navigation stays client-side.
|
|
236
|
+
// Without `linkComponent` the pages render plain <a href> links.
|
|
237
|
+
function ConceptLink({ href, className, children, 'aria-label': ariaLabel }: ConceptLinkProps) {
|
|
238
|
+
return (
|
|
239
|
+
<Link to={href} className={className} aria-label={ariaLabel}>
|
|
240
|
+
{children}
|
|
241
|
+
</Link>
|
|
242
|
+
)
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// The site resolves the slug; an unknown one redirects to the landing page.
|
|
246
|
+
function ConceptRoute() {
|
|
247
|
+
const { slug } = useParams()
|
|
248
|
+
const concept = getConceptBySlug(CONCEPTS, slug)
|
|
249
|
+
if (!concept) return <Navigate to={CONCEPTS_PATH} replace />
|
|
250
|
+
return (
|
|
251
|
+
<ConceptDetailPage
|
|
252
|
+
productName="VibeControls"
|
|
253
|
+
siteUrl={SITE_URL}
|
|
254
|
+
concept={concept}
|
|
255
|
+
concepts={CONCEPTS}
|
|
256
|
+
categories={CONCEPT_CATEGORIES}
|
|
257
|
+
linkComponent={ConceptLink}
|
|
258
|
+
/>
|
|
259
|
+
)
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Routes
|
|
263
|
+
<Route
|
|
264
|
+
path={CONCEPTS_PATH}
|
|
265
|
+
element={
|
|
266
|
+
<ConceptsPage
|
|
267
|
+
productName="VibeControls"
|
|
268
|
+
siteUrl={SITE_URL}
|
|
269
|
+
concepts={CONCEPTS}
|
|
270
|
+
categories={CONCEPT_CATEGORIES}
|
|
271
|
+
linkComponent={ConceptLink}
|
|
272
|
+
/>
|
|
273
|
+
}
|
|
274
|
+
/>
|
|
275
|
+
<Route path="/concept/:slug" element={<ConceptRoute />} />
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
**`ConceptsPage` props**: `productName`, `siteUrl` (public origin, for
|
|
279
|
+
canonical/OG/JSON-LD URLs), `concepts` (required); `categories` (section order
|
|
280
|
+
and descriptions — a concept in an unlisted category still renders, last),
|
|
281
|
+
`cta` (rendered in the hero), `linkComponent`, `title` (the `<h1>`, default
|
|
282
|
+
`"<Product> concepts"`), `description` (meta description and hero lead; the
|
|
283
|
+
default is built from the counts and stays within 160 characters), `className`.
|
|
284
|
+
With no concepts at all the page says none are published yet, without the
|
|
285
|
+
filter bar or zero counts.
|
|
286
|
+
|
|
287
|
+
**`ConceptDetailPage` props**: `productName`, `siteUrl`, `concept`, `concepts`
|
|
288
|
+
(all of them — for related concepts and previous / next by `order`);
|
|
289
|
+
`categories`, `cta` (rendered as a band at the end), `linkComponent`,
|
|
290
|
+
`className`.
|
|
291
|
+
|
|
292
|
+
**Helpers**: `CONCEPTS_PATH` (`/concepts`), `CONCEPT_PATH_PREFIX` (`/concept`),
|
|
293
|
+
`conceptPath(slug)`, `getConceptBySlug(concepts, slug)`; types `Concept`,
|
|
294
|
+
`ConceptImage`, `ConceptCategory`, `ConceptLinkComponent`, `ConceptLinkProps`.
|
|
295
|
+
|
|
296
|
+
**SEO and llms.** One `<h1>` per page, a self-canonical URL, absolute OG images
|
|
297
|
+
and JSON-LD (`CollectionPage` + `ItemList` + `BreadcrumbList` on the landing
|
|
298
|
+
page; `ImageGallery` + `BreadcrumbList` on a concept). Add `/concepts` and every
|
|
299
|
+
`/concept/<slug>` to the site's prerender list and sitemap. Only the gallery
|
|
300
|
+
images carry `data-llms-image` (one per image — the hero does not), and each is
|
|
301
|
+
followed directly by its `<figcaption>`, which `scripts/llms.mjs` reads as the
|
|
302
|
+
caption. The lightbox is a native `<dialog>` mounted only while open, so it is
|
|
303
|
+
never in the prerendered HTML.
|
|
304
|
+
|
|
305
|
+
**Testing hooks.** `data-concepts="landing" | "card" (plus data-slug) | "filter"
|
|
306
|
+
| "notice" | "empty" (filters match nothing) | "none" (no concepts at all) |
|
|
307
|
+
"detail" | "category" | "hero" | "illustration" | "related" | "pager" | "cta" |
|
|
308
|
+
"lightbox"`, and `data-concepts-notice` on the notice.
|
|
198
309
|
|
|
199
310
|
### SEO Component
|
|
200
311
|
|
|
@@ -290,6 +290,9 @@ function resolveImageUrl(image, base) {
|
|
|
290
290
|
return image;
|
|
291
291
|
}
|
|
292
292
|
}
|
|
293
|
+
function serializeJsonLd(data) {
|
|
294
|
+
return JSON.stringify(data).replace(/</g, "\\u003c").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
295
|
+
}
|
|
293
296
|
function PageHead({
|
|
294
297
|
title,
|
|
295
298
|
description,
|
|
@@ -331,7 +334,7 @@ function PageHead({
|
|
|
331
334
|
fullImageUrl && /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "twitter:image", content: fullImageUrl }),
|
|
332
335
|
twitterHandle && /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "twitter:creator", content: twitterHandle }),
|
|
333
336
|
/* @__PURE__ */ jsxRuntime.jsx("meta", { name: "theme-color", content: themeColor }),
|
|
334
|
-
structuredDataItems.map((data, index) => /* @__PURE__ */ jsxRuntime.jsx("script", { type: "application/ld+json", children:
|
|
337
|
+
structuredDataItems.map((data, index) => /* @__PURE__ */ jsxRuntime.jsx("script", { type: "application/ld+json", children: serializeJsonLd(data) }, `ld-${index}`)),
|
|
335
338
|
additionalMeta?.map((meta, index) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
336
339
|
"meta",
|
|
337
340
|
{
|