Skip to content

Commit 3981bec

Browse files
committed
feat(ui-vue,roxy-data,format): add Vue wrappers, fix the WordPress render regression and the wall-clock DST bug
1 parent 5f4af62 commit 3981bec

344 files changed

Lines changed: 10706 additions & 1961 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/release.yml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,34 @@ jobs:
3636

3737
- run: bun install --frozen-lockfile
3838

39+
# Trusted publishing (OIDC) CANNOT create a package. npmjs.com requires the
40+
# package to already exist before a Trusted Publisher can be configured on it,
41+
# so the first version of any new package must be pushed by a human once
42+
# (npm/cli#8544, still open).
43+
#
44+
# Without this gate the run would publish @roxyapi/ui and @roxyapi/ui-react,
45+
# THEN die on the first-ever @roxyapi/ui-vue publish. The job aborts before
46+
# "Commit, tag, push", so npm ends up bumped while the repo has no version
47+
# commit, no tag and no GitHub release, and the re-run is then rejected for
48+
# republishing an existing version. Fail BEFORE anything is published instead.
49+
- name: Preflight — every package must already exist on npm
50+
run: |
51+
MISSING=""
52+
for pkg in packages/ui packages/ui-react packages/ui-vue; do
53+
NAME=$(jq -r '.name' "$pkg/package.json")
54+
npm view "$NAME" version >/dev/null 2>&1 || MISSING="$MISSING $NAME"
55+
done
56+
if [ -n "$MISSING" ]; then
57+
echo "::error::Never published, so OIDC cannot publish it either:$MISSING"
58+
echo "Bootstrap each package ONCE from a logged-in machine:"
59+
echo " (cd packages/<name> && npm publish --access public)"
60+
echo "then on npmjs.com open the package and set"
61+
echo " Trusted Publisher -> GitHub Actions -> RoxyAPI/ui -> release.yml"
62+
echo "Re-run this workflow afterwards. See npm/cli#8544."
63+
exit 1
64+
fi
65+
echo "All packages exist on npm. Trusted publishing can proceed."
66+
3967
- name: Regenerate types from spec
4068
run: bun run generate
4169

@@ -131,6 +159,7 @@ jobs:
131159
}
132160
bump packages/ui
133161
bump packages/ui-react
162+
bump packages/ui-vue
134163
VERSION=$(jq -r '.version' packages/ui/package.json)
135164
echo "version=$VERSION" >> $GITHUB_OUTPUT
136165
bun run build
@@ -146,6 +175,11 @@ jobs:
146175
run: |
147176
(cd packages/ui-react && npm publish --access public --provenance)
148177
178+
- name: Publish @roxyapi/ui-vue
179+
if: steps.diff.outputs.changed == 'true' || github.event_name == 'workflow_dispatch'
180+
run: |
181+
(cd packages/ui-vue && npm publish --access public --provenance)
182+
149183
- name: Commit, tag, push
150184
if: steps.diff.outputs.changed == 'true' || github.event_name == 'workflow_dispatch'
151185
run: |
@@ -166,6 +200,7 @@ jobs:
166200
mkdir -p .release-assets
167201
(cd packages/ui && npm pack --pack-destination ../../.release-assets)
168202
(cd packages/ui-react && npm pack --pack-destination ../../.release-assets)
203+
(cd packages/ui-vue && npm pack --pack-destination ../../.release-assets)
169204
ls -la .release-assets/
170205
171206
- name: Create GitHub release

.gitignore

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,19 @@ node_modules/
99
# the live one before a release goes out.
1010
packages/ui/src/types/
1111

12-
# packages/ui/dist and packages/ui-react/dist are pure build output. The
12+
# packages/ui/dist and the wrapper-package dist dirs are pure build output. The
1313
# Release workflow runs `bun run build` then `npm publish`, which includes
1414
# dist via each package.json `files` allowlist; the Pages workflow rebuilds
1515
# before deploy. Nothing serves dist from the git repo (the shadcn registry
1616
# is served from `@main`, dist is served from npm via jsDelivr), so dist
1717
# never needs to be git-tracked.
1818
packages/ui/dist/
1919
packages/ui-react/dist/
20+
packages/ui-vue/dist/
2021

2122
# Root README.md, LICENSE, and AGENTS.md are the canonical source files.
2223
# `copyRootDocsToWorkspaces` in `scripts/build.ts` mirrors all three into
23-
# both packages on every `bun run build` so the npm tarballs (which ship
24+
# every package on every `bun run build` so the npm tarballs (which ship
2425
# these files via each package.json `files` allowlist) carry an up-to-date
2526
# copy. The per-package copies are build artifacts; never edit them
2627
# directly, edit the root files only.
@@ -30,6 +31,9 @@ packages/ui/AGENTS.md
3031
packages/ui-react/README.md
3132
packages/ui-react/LICENSE
3233
packages/ui-react/AGENTS.md
34+
packages/ui-vue/README.md
35+
packages/ui-vue/LICENSE
36+
packages/ui-vue/AGENTS.md
3337

3438
# apps/docs/dist is mirrored from packages/ui/dist on each `bun run build`
3539
# so local preview at apps/docs/ matches the GitHub Pages layout exactly.

AGENTS.md

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
This file teaches AI coding agents (Claude Code, Cursor, Copilot, Codex, Gemini CLI, and any MCP-compatible client) how to use Roxy UI when integrating RoxyAPI into a project.
44

5-
This file ships inside both `@roxyapi/ui` and `@roxyapi/ui-react` on npm. After install, read it at `node_modules/@roxyapi/ui/AGENTS.md`.
5+
This file ships inside `@roxyapi/ui`, `@roxyapi/ui-react`, and `@roxyapi/ui-vue` on npm. After install, read it at `node_modules/@roxyapi/ui/AGENTS.md`.
66

77
Live preview: <https://roxyapi.github.io/ui/>. Source of truth for component types: the combined OpenAPI spec at `https://roxyapi.com/api/v2/openapi.json`, regenerated into `packages/ui/src/types/types.gen.ts`. Per-product specs live at `https://roxyapi.com/api/v2/{slug}/openapi.json`.
88

@@ -96,7 +96,7 @@ Use the table below for the formal endpoint to component mapping.
9696
| `<roxy-forecast-timeline>` | Forecast | POST /forecast/timeline | Date-grouped events across Western, Vedic, and biorhythm domains, weighted by significance |
9797
| `<roxy-forecast-digest>` | Forecast | POST /forecast/digest | Per-window event counts, domain breakdown, and the highest-significance events |
9898
| `<roxy-biorhythm-chart>` | Biorhythm | POST /biorhythm/{daily,forecast,critical-days} | Daily bars, forecast cycle lines, critical days |
99-
| `<roxy-hexagram>` | I Ching | GET /iching/hexagrams/{number}, /iching/cast, POST /iching/daily, /iching/daily/cast | Hexagram with trigrams, judgment, image, changing lines |
99+
| `<roxy-hexagram>` | I Ching | GET /iching/hexagrams/{number}, /iching/cast, POST /iching/daily, /iching/daily/cast | Hexagram figure with trigrams, judgment, image, and a reading per line (statement plus meaning); a cast highlights the moving lines and the resulting hexagram |
100100
| `<roxy-crystal-card>` | Crystals | GET /crystals/{id} | Photo, meaning sections, chakra, zodiac, element, hardness, keywords, and pairings |
101101
| `<roxy-crystal-grid>` | Crystals | GET /crystals, /crystals/chakra/{chakra}, /crystals/element/{element}, /crystals/zodiac/{sign}, /crystals/birthstone/{month}, /crystals/search | Crystal gallery tiles with photo, name, and colour swatches |
102102
| `<roxy-dream-card>` | Dreams | GET /dreams/symbols/{id} | Symbol name, interpretation body, and letter chip |
@@ -201,7 +201,24 @@ Several components select a view, mode, or chart layout in addition to `data`. T
201201
<RoxyPanchangTable data={panchang} detail="detailed" />
202202
```
203203

204-
The full set: `RoxyNatalChart` `houseSystem`, `RoxyHoroscopeCard` `period`, `RoxyMoonPhase` `mode`, `RoxyCompatibilityCard` `mode`, `RoxyVedicKundli` and `RoxyDivisionalChart` `chartStyle`, `RoxyPanchangTable` `detail`, `RoxyDashaTimeline` `period`, `RoxyDoshaCard` `type`, `RoxyNumerologyCard` `type`, `RoxyTarotSpread` `spread`, `RoxyBiorhythmChart` `mode`, `RoxyHexagram` `mode`. Outside React, set the same value as a kebab-case attribute or a JS property on the element (for example `chart-style="south"` or `el.chartStyle = 'south'`).
204+
The full set: `RoxyNatalChart` `houseSystem`, `RoxyHoroscopeCard` `period`, `RoxyMoonPhase` `mode`, `RoxyCompatibilityCard` `mode`, `RoxyVedicKundli` and `RoxyDivisionalChart` `chartStyle`, `RoxyPanchangTable` `detail`, `RoxyDashaTimeline` `period`, `RoxyDoshaCard` `type`, `RoxyNumerologyCard` `type`, `RoxyTarotSpread` `spread`, `RoxyBiorhythmChart` `mode`, `RoxyHexagram` `mode`. Outside React and Vue, set the same value as a kebab-case attribute or a JS property on the element (for example `chart-style="south"` or `el.chartStyle = 'south'`).
205+
206+
### 6c. Vue and Nuxt
207+
208+
`@roxyapi/ui-vue` exposes the same components with the same prop names. Bind `data` and the config props normally; the package sets them as DOM properties for you, so an object payload never gets stringified into an attribute. Listen to widget events with the usual `@` syntax.
209+
210+
```vue
211+
<script setup lang="ts">
212+
import { RoxyVedicKundli, RoxyLocationSearch } from '@roxyapi/ui-vue';
213+
</script>
214+
215+
<template>
216+
<RoxyVedicKundli :data="chart" chart-style="south" />
217+
<RoxyLocationSearch @roxy-location-select="onCity" />
218+
</template>
219+
```
220+
221+
In Nuxt, render these in a client context (`<ClientOnly>` or a `.client.vue` component): they mount Custom Elements and need the DOM, the same constraint as `'use client'` in the Next.js App Router.
205222

206223
### 7. Local response interface drift
207224

@@ -451,8 +468,8 @@ When listing domains in user-visible copy, use the canonical order: Western astr
451468

452469
## What not to ship
453470

454-
- Do not bundle `@roxyapi/ui` and `@roxyapi/ui-react` together; they ship independently.
455-
- Use `@roxyapi/ui-react` for React projects. Use `@roxyapi/ui` directly elsewhere.
471+
- Do not bundle `@roxyapi/ui` with `@roxyapi/ui-react` or `@roxyapi/ui-vue`; they ship independently.
472+
- Use `@roxyapi/ui-react` for React projects and `@roxyapi/ui-vue` for Vue and Nuxt projects. Use `@roxyapi/ui` directly elsewhere.
456473
- Do not write your own kundli component. The lifted layout in `<roxy-vedic-kundli>` is the canonical RoxyAPI render path.
457474
- Do not call astrology endpoints with hardcoded coordinates. Always geocode first via `<roxy-location-search>` or `roxy.location.searchCities()`.
458475
- Do not declare a local `interface XyzData` to describe a RoxyAPI response. Import the type from the spec-derived bundle: `import type { XyzResponse } from '@roxyapi/sdk'`. Local interfaces drift the moment the spec changes.

README.md

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ Every chart, table, and card adapts to light and dark automatically. Hover any i
196196
<td width="50%"><strong>I Ching hexagram</strong> · <code>&lt;roxy-hexagram&gt;</code><br><sub>GET /iching/hexagrams/&lbrace;number&rbrace;, /iching/cast</sub><br>
197197
<picture>
198198
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/RoxyAPI/ui/main/assets/screenshots/hexagram-dark.png">
199-
<img src="https://raw.githubusercontent.com/RoxyAPI/ui/main/assets/screenshots/hexagram-light.png" alt="Hexagram with trigrams, judgment, image, changing lines">
199+
<img src="https://raw.githubusercontent.com/RoxyAPI/ui/main/assets/screenshots/hexagram-light.png" alt="Hexagram figure with trigrams, judgment, image, and a reading for every line">
200200
</picture>
201201
</td>
202202
<td width="50%"><strong>Numerology</strong> · <code>&lt;roxy-numerology-card&gt;</code><br><sub>POST /numerology/&lbrace;life-path,expression,personal-year,chart&rbrace;</sub><br>
@@ -268,6 +268,24 @@ export function Chart({ data }: { data: NatalChart }) {
268268
}
269269
```
270270

271+
Vue users get the same typed surface.
272+
273+
```bash
274+
npm install @roxyapi/ui-vue
275+
```
276+
277+
```vue
278+
<script setup lang="ts">
279+
import { RoxyNatalChart } from '@roxyapi/ui-vue';
280+
281+
defineProps<{ data: NatalChart }>();
282+
</script>
283+
284+
<template>
285+
<RoxyNatalChart :data="data" />
286+
</template>
287+
```
288+
271289
## Quick start
272290

273291
```ts
@@ -298,6 +316,24 @@ Serialize with the shipped helper, never a bare `JSON.stringify`. `@roxyapi/ui`
298316

299317
Load the bundle once anywhere on the page. It registers every `roxy-*` element and loads the design tokens, so every component on the page renders themed, in light or dark, from that single tag. Nothing else to add.
300318

319+
### No-JavaScript fallback
320+
321+
The two modes degrade differently, and only one of them can be rescued.
322+
323+
**Controlled mode** (the `<script class="roxy-data">` island above) already holds the reading in the page. Render it server-side as ordinary HTML alongside the island and put that markup *inside* the element. Components render into a shadow root and none of them expose a `<slot>`, so light-DOM children are painted only while the element is un-upgraded, and disappear the moment the bundle registers it. You get the server HTML without JavaScript and the live component with it, from the same markup, with no flash of both.
324+
325+
**Form mode** (`data-endpoint` + a `pk_` key) is a self-fetch widget: it cannot work without JavaScript, because there is nothing to render until the visitor submits the form. Give it a light-DOM fallback that says so and links out.
326+
327+
```html
328+
<roxy-natal-chart data-endpoint="astrology/natal-chart" publishable-key="pk_live_…">
329+
<!-- Painted only when JavaScript is off. Replaced by the component otherwise. -->
330+
<p>JavaScript is required to generate this chart.
331+
<a href="https://roxyapi.com/products/astrology">Open it on roxyapi.com</a>.</p>
332+
</roxy-natal-chart>
333+
```
334+
335+
A `<noscript>` block works too, and is the safer choice if you also need to hide the fallback from screen readers once the component takes over.
336+
301337
```ts
302338
import { roxyDataScript } from '@roxyapi/ui';
303339

@@ -570,6 +606,7 @@ Set `ROXY_API_KEY` to your secret key in your server env for the server-side SDK
570606
|---|---|
571607
| npm `@roxyapi/ui` | `npmjs.com/package/@roxyapi/ui` |
572608
| npm `@roxyapi/ui-react` | `npmjs.com/package/@roxyapi/ui-react` |
609+
| npm `@roxyapi/ui-vue` | `npmjs.com/package/@roxyapi/ui-vue` |
573610
| jsDelivr CDN (full bundle) | `cdn.jsdelivr.net/npm/@roxyapi/ui@latest/dist/cdn/roxy-ui.js` |
574611
| jsDelivr CDN (per component) | `cdn.jsdelivr.net/npm/@roxyapi/ui@latest/dist/cdn/components/{name}.js` |
575612
| Widgets auto-mount (with browser keys, coming soon) | `cdn.jsdelivr.net/npm/@roxyapi/ui@latest/dist/cdn/widgets.js` |
@@ -624,7 +661,7 @@ Set `ROXY_API_KEY` to your secret key in your server env for the server-side SDK
624661
| `<roxy-forecast-timeline>` | Forecast | POST /forecast/timeline | Date-grouped events across Western, Vedic, and biorhythm domains, weighted by significance |
625662
| `<roxy-forecast-digest>` | Forecast | POST /forecast/digest | Per-window event counts, domain breakdown, and the highest-significance events |
626663
| `<roxy-biorhythm-chart>` | Biorhythm | POST /biorhythm/{daily,forecast,critical-days} | Daily bars, forecast cycle lines, critical days |
627-
| `<roxy-hexagram>` | I Ching | GET /iching/hexagrams/{number}, /iching/cast, POST /iching/daily, /iching/daily/cast | Hexagram with trigrams, judgment, image, changing lines |
664+
| `<roxy-hexagram>` | I Ching | GET /iching/hexagrams/{number}, /iching/cast, POST /iching/daily, /iching/daily/cast | Hexagram figure with trigrams, judgment, image, and a reading per line (statement plus meaning); a cast highlights the moving lines and the resulting hexagram |
628665
| `<roxy-crystal-card>` | Crystals | GET /crystals/{id} | Photo, meaning sections, chakra, zodiac, element, hardness, keywords, and pairings |
629666
| `<roxy-crystal-grid>` | Crystals | GET /crystals, /crystals/chakra/{chakra}, /crystals/element/{element}, /crystals/zodiac/{sign}, /crystals/birthstone/{month}, /crystals/search | Crystal gallery tiles with photo, name, and colour swatches |
630667
| `<roxy-dream-card>` | Dreams | GET /dreams/symbols/{id} | Symbol name, interpretation body, and letter chip |
@@ -673,7 +710,7 @@ roxy-natal-chart {
673710

674711
## Built for AI agents
675712

676-
[`AGENTS.md`](AGENTS.md) is bundled inside both npm packages. Once installed, agents can read it from `node_modules/@roxyapi/ui/AGENTS.md` (or `@roxyapi/ui-react/AGENTS.md`) for the component decision tree, integration patterns, and rules.
713+
[`AGENTS.md`](AGENTS.md) is bundled inside every npm package. Once installed, agents can read it from `node_modules/@roxyapi/ui/AGENTS.md` (or `@roxyapi/ui-react/AGENTS.md`, `@roxyapi/ui-vue/AGENTS.md`) for the component decision tree, integration patterns, and rules.
677714

678715
- Works with Claude Code, Cursor, Copilot, Codex, Gemini CLI, and any MCP-compatible client.
679716
- Component decision tree maps each RoxyAPI endpoint to the component that renders its response.
@@ -732,7 +769,7 @@ Persist the choice in `localStorage` from your own code; the components do not o
732769
<details>
733770
<summary><strong>How big is each component? What is the bundle cost?</strong></summary>
734771

735-
Per-component bundles run 9-21 KB gzipped, capped at 30 KB by CI. The full bundle (every component, helpers, base styles, and the inlined design tokens) stays well under the 150 KB CI cap, around 85 KB gzipped today. The React package loads the runtime on mount, so a route that renders one chart pays for one component, not the whole catalog. Pin a concrete version in production for byte-stable cache hits.
772+
Per-component bundles run 9-21 KB gzipped, capped at 30 KB by CI. The full bundle (every component, helpers, base styles, and the inlined design tokens) stays well under the 150 KB CI cap, around 85 KB gzipped today. The React and Vue packages load the runtime on mount, so a route that renders one chart pays for one component, not the whole catalog. Pin a concrete version in production for byte-stable cache hits.
736773
</details>
737774

738775
<details>

0 commit comments

Comments
 (0)