Skip to content

Commit 58f1789

Browse files
committed
fix(bindings,snippets): honour declaration order for the default binding, and generate the copy-paste snippet once
The first binding per component is the DEFAULT everywhere it matters: the widget map, the one-tag div, the demo Embed tab and the hosted /embed shell all take endpoints[0]. sync-bindings sorted those by path, which picked the default alphabetically and had nothing to do with what a reader wants. Twelve components were affected: moon-phase opened a year-and-month calendar form instead of today's phase, forecast-timeline defaulted to significant-dates rather than the timeline it is named after, numerology-card to birth-day over life-path, tarot-spread to a raw draw over three-card, hd-type-card to profile over type, biorhythm-chart to critical-days over daily. UI_BINDINGS already lists each component's endpoints in the order a reader wants them, so declaration order is the intent; only the tag keys are sorted now, and those are never read positionally. The copy-paste snippet had two implementations, one here and one on the roxyapi.com /widgets page, and they had drifted: this repo shipped the practitioner theme commented out and labelled optional, the other emitted the link live, so a site owner copying from the marketing page got a restyle they never chose. It is now built once in scripts/widget-snippets.ts, written into components-catalog.json and mirrored to the demo, so every surface renders the same string rather than deriving its own.
1 parent 84d5662 commit 58f1789

9 files changed

Lines changed: 976 additions & 401 deletions

File tree

apps/docs/components-manifest.js

Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ const DASHA_BODY = {
3636

3737
const REGISTRY_BASE = 'https://cdn.jsdelivr.net/gh/RoxyAPI/ui@main/registry';
3838
const UI_CDN = 'https://cdn.jsdelivr.net/npm/@roxyapi/ui@latest/dist/cdn';
39+
// Must match PK_PLACEHOLDER in scripts/widget-snippets.ts, which produces the snippets.
40+
const PK_PLACEHOLDER = 'pk_live_YOUR_KEY';
3941
const PRACTITIONER_THEME_URL =
4042
'https://cdn.jsdelivr.net/npm/@roxyapi/ui@latest/dist/styles/themes/practitioner.css';
4143

@@ -62,32 +64,25 @@ function lookup(tag) {
6264
* Returns null for a component with no binding (the three helpers), and the demo
6365
* hides the tab for those.
6466
*/
67+
/**
68+
* The Embed-tab snippets for an endpoint-bound component.
69+
*
70+
* Read from `window.ROXY_WIDGET_SNIPPETS`, which `scripts/sync-manifest.ts` emits from the
71+
* same builder that writes the snippets into `components-catalog.json`. This function used to
72+
* rebuild them here, which meant two implementations of one string and they drifted: this one
73+
* shipped the practitioner theme commented out, the roxyapi.com /widgets page emitted it live.
74+
* Do not reintroduce a local build; change `scripts/widget-snippets.ts` instead and every
75+
* surface follows. Returns null for a component with no binding, so the demo hides the tab.
76+
*/
6577
function embedSnippet(tag, slug) {
66-
const bindings = ENDPOINT_BINDINGS[tag];
67-
if (!bindings || !bindings.length) return null;
78+
const snippets = (window.ROXY_WIDGET_SNIPPETS || {})[tag];
79+
if (!snippets) return null;
80+
const bindings = ENDPOINT_BINDINGS[tag] || [];
6881
const def = bindings[0];
69-
const endpoint = def.path.replace(/^\//, '');
70-
// POST is the element default, so only a GET binding needs an explicit method.
71-
const methodAttr = def.method === 'POST' ? '' : ` method="${def.method}"`;
72-
// The default variant's selector attribute (period/mode/type/spread/detail),
73-
// so the script element renders the same view the one-tag default resolves to.
74-
const configAttr = def.attrs
75-
? Object.entries(def.attrs)
76-
.map(([k, v]) => ` ${k}="${v}"`)
77-
.join('')
78-
: '';
79-
80-
const script = `<!-- Optional: warm practitioner theme (drop this line for the default look) -->
81-
<!-- <link rel="stylesheet" href="${PRACTITIONER_THEME_URL}"> -->
82-
<script src="${UI_CDN}/roxy-ui.js" defer></script>
83-
<${tag}${configAttr} data-endpoint="${endpoint}"${methodAttr} publishable-key="pk_live_..." lang="en"></${tag}>`;
84-
85-
const oneTag = `<script src="${UI_CDN}/widgets.js" defer></script>
86-
<div data-roxy-widget="${slug}" data-publishable-key="pk_live_..."></div>`;
8782

8883
// The selector attribute and its non-default values, surfaced on the hint line
8984
// so one data-* attribute on the one-tag div switches variant.
90-
const selector = def.attrs ? Object.keys(def.attrs)[0] : undefined;
85+
const selector = def && def.attrs ? Object.keys(def.attrs)[0] : undefined;
9186
const otherValues = selector
9287
? bindings
9388
.slice(1)
@@ -99,9 +94,9 @@ function embedSnippet(tag, slug) {
9994
? ` Switch variant with data-${selector} (${otherValues.map((v) => `"${v}"`).join(', ')}) on the one-tag div.`
10095
: '';
10196

102-
const hint = `Mint a publishable key at roxyapi.com/account, register the origins you embed on, and replace the pk_live_ placeholder. Works on any site that allows script tags.${variantHint}`;
97+
const hint = `Mint a publishable key at roxyapi.com/account, register the origins you embed on, and replace the ${PK_PLACEHOLDER} placeholder. Works on any site that allows script tags.${variantHint}`;
10398

104-
return { script, oneTag, hint };
99+
return { script: snippets.script, oneTag: snippets.oneTag, hint };
105100
}
106101

107102
function serverRender(tag, body) {

0 commit comments

Comments
 (0)