@cavuno/board 1.34.0 → 1.35.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.
@@ -32,14 +32,13 @@ Read `package.json` and the project layout before writing anything. Identify: th
32
32
  - `next` → use Server Components + per-call `FetchOptions` (`next: { revalidate, tags }`).
33
33
  - Anything else (Nuxt, SvelteKit, Astro, SolidStart, plain JS) → use the core skills directly; the SDK surface is identical.
34
34
 
35
- ## Use standard environment names
35
+ ## Use the standard board environment name
36
36
 
37
- Read these two values from the environment; never hard-code them:
37
+ Read the board key from the environment; never hard-code it:
38
38
 
39
- - `PUBLIC_CAVUNO_API_URL` — the API base, e.g. `https://api.cavuno.com`.
40
39
  - `PUBLIC_CAVUNO_BOARD` — the board identifier. Use the `pk_…` publishable key, not the slug (the slug is operator-mutable and breaks deployed frontends on rename).
41
40
 
42
- Both are public-safe (the `pk_…` key is client-safe by design). Use your framework's public-env convention for the variable name (`VITE_`, `PUBLIC_`, `NEXT_PUBLIC_`); the values are the same.
41
+ The `pk_…` key is public-safe by design. Use your framework's public-env convention for the variable name (`VITE_`, `PUBLIC_`, `NEXT_PUBLIC_`). The SDK uses `https://api.cavuno.com` by default. Pass `baseUrl` only when Cavuno supplies a staging or development origin.
43
42
 
44
43
  ## Keep credentials server-side
45
44
 
@@ -57,7 +56,6 @@ Create one client and reuse it. See `cavuno-board-client`.
57
56
  import { createBoardClient } from '@cavuno/board';
58
57
 
59
58
  export const board = createBoardClient({
60
- baseUrl: process.env.PUBLIC_CAVUNO_API_URL!,
61
59
  board: process.env.PUBLIC_CAVUNO_BOARD!,
62
60
  });
63
61
  ```
@@ -68,7 +66,7 @@ export const board = createBoardClient({
68
66
 
69
67
  ## Build jobs browsing + detail
70
68
 
71
- The core surface. `jobs.list` / `jobs.search` for listing pages, `jobs.retrieve` for the detail page, `jobs.similar` for the related rail. Honor storefront pagination and the candidate-paywall `gatedCount`; use `paginate()` for full-catalog walks (sitemaps, feeds). See `cavuno-board-jobs`.
69
+ The core surface. `jobs.list` / `jobs.search` for listing pages, `jobs.retrieve` for the detail page, `jobs.similar` for the related rail. Honor catalog pagination and the candidate-paywall `gatedCount`; use `paginate()` for full-catalog walks (sitemaps, feeds). See `cavuno-board-jobs`.
72
70
 
73
71
  ## Add board users + saved jobs
74
72
 
@@ -85,7 +83,7 @@ hand-rolling (each has a skill):
85
83
 
86
84
  - Salary/date/label formatting in the board language → `cavuno-board-format`
87
85
  - Listing-filter vocabulary + URL param parsing → `cavuno-board-filters`
88
- - Board theme → shadcn CSS variables + fonts `cavuno-board-theme`
86
+ - Optional hosted-board theme compatibility `cavuno-board-theme`. By default, the consuming application owns tokens, fonts, and color mode.
89
87
 
90
88
  ## Build the remaining surfaces per feature flag
91
89
 
@@ -117,4 +115,4 @@ Then run the `cavuno-board-smoke-test` skill against your `pk_…` — type chec
117
115
 
118
116
  ## Stop conditions
119
117
 
120
- Stop and ask the human when: no `pk_…` board identifier or API URL is available; the framework is unrecognized and has no server boundary for secrets; or a surface you need (e.g. job alerts, applications) has no corresponding skill yet — the SDK only exposes endpoints that are live, so a missing skill means the endpoint isn't shipped.
118
+ Stop and ask the human when: no `pk_…` board identifier is available; the framework is unrecognized and has no server boundary for credentials; or a surface you need (e.g. job alerts, applications) has no corresponding skill yet — the SDK only exposes endpoints that are live, so a missing skill means the endpoint isn't shipped.
@@ -18,7 +18,7 @@ auth, and SEO surfaces behave. Verify at runtime, in this order.
18
18
  ## 0 — Run `doctor` first (deterministic pass)
19
19
 
20
20
  ```bash
21
- PUBLIC_CAVUNO_API_URL=... PUBLIC_CAVUNO_BOARD=pk_... \
21
+ PUBLIC_CAVUNO_BOARD=pk_... \
22
22
  npx @cavuno/board doctor --frontend http://localhost:3000
23
23
  ```
24
24
 
@@ -45,21 +45,22 @@ board; wait a few minutes.
45
45
 
46
46
  ## 1 — Probe the API directly (before blaming app code)
47
47
 
48
- Use the real env values the app reads (`PUBLIC_CAVUNO_API_URL`,
49
- `PUBLIC_CAVUNO_BOARD`). Expected outputs are exact.
48
+ Use the real `PUBLIC_CAVUNO_BOARD` value the app reads. The SDK and doctor use `https://api.cavuno.com` by default; set `PUBLIC_CAVUNO_API_URL` only for a Cavuno-supplied non-production override. Expected outputs are exact.
50
49
 
51
50
  ```bash
51
+ CAVUNO_API_URL="${PUBLIC_CAVUNO_API_URL:-https://api.cavuno.com}"
52
+
52
53
  # Board context: MUST return JSON with "object": "public_board" and your
53
54
  # board's name — not an HTML error page, not {"error":{"code":"boards_not_found"}}.
54
- curl -s "$PUBLIC_CAVUNO_API_URL/v1/boards/$PUBLIC_CAVUNO_BOARD" | head -c 300
55
+ curl -s "$CAVUNO_API_URL/v1/boards/$PUBLIC_CAVUNO_BOARD" | head -c 300
55
56
 
56
57
  # Jobs list: MUST return {"object":"list", ... "data":[...]}. An empty data
57
58
  # array on a board you know has jobs means the wrong board identifier.
58
- curl -s "$PUBLIC_CAVUNO_API_URL/v1/boards/$PUBLIC_CAVUNO_BOARD/jobs?limit=2" | head -c 300
59
+ curl -s "$CAVUNO_API_URL/v1/boards/$PUBLIC_CAVUNO_BOARD/jobs?limit=2" | head -c 300
59
60
 
60
61
  # Error envelope: a bogus job slug MUST return the v1 error shape with
61
62
  # "code":"jobs_not_found" — anything else means a proxy is rewriting responses.
62
- curl -s "$PUBLIC_CAVUNO_API_URL/v1/boards/$PUBLIC_CAVUNO_BOARD/jobs/definitely-not-a-job"
63
+ curl -s "$CAVUNO_API_URL/v1/boards/$PUBLIC_CAVUNO_BOARD/jobs/definitely-not-a-job"
63
64
  ```
64
65
 
65
66
  On a password-protected board, every content read above returns 401 with
@@ -1,69 +1,57 @@
1
1
  ---
2
2
  name: cavuno-board-theme
3
- description: Board branding with @cavuno/board/theme map the board's stored theme (16 semantic colors × light/dark + typography from board.context().theme) onto shadcn CSS variables, resolve the color-scheme mode, and build the Google Fonts request. Use when wiring the app shell, dark mode, or brand styling for a tenant frontend.
3
+ description: Optional hosted-board theme compatibility with @cavuno/board/theme. Use only when a custom frontend deliberately mirrors or migrates an existing Cavuno hosted theme; the consuming application normally owns tokens, fonts, and color mode.
4
4
  ---
5
5
 
6
- # Theme: board branding → shadcn tokens
6
+ # Optional hosted-board theme compatibility
7
7
 
8
- `@cavuno/board/theme` maps the board's stored theme onto the canonical
9
- shadcn token vocabulary as CSS-variable overrides. One contract, two
10
- consumers: the dashboard edits the board theme; agents restyle through the
11
- standard shadcn theme file — both end up in the same tokens.
8
+ The custom frontend owns its visual system by default: semantic design tokens, component styles, font loading, and light/dark-mode state belong in the application. Do not make remote `board.context().theme` the default source of truth for a new SDK frontend.
9
+
10
+ `@cavuno/board/theme` exists for a narrower job: mirroring an existing hosted board or providing a starting point during a hosted-to-custom migration.
12
11
 
13
12
  ## When to use
14
13
 
15
- - The app shell/root layout of any tenant frontend, once.
16
- - Wiring dark mode or brand fonts.
14
+ - The human explicitly wants the custom frontend to mirror the current hosted board.
15
+ - A migration needs a temporary compatibility layer while tokens move into the application.
16
+ - One application intentionally renders many Cavuno boards with different operator-owned themes.
17
17
 
18
18
  ## When not to use
19
19
 
20
- - Component-level styling write normal Tailwind/shadcn classes; they pick
21
- the overridden tokens up automatically.
20
+ - A normal custom frontend whose design system already defines tokens and fonts.
21
+ - Component-level styling.
22
+ - As a replacement for the framework's theme provider or font system.
22
23
 
23
- ## Wire it once at the shell
24
+ ## Read compatibility values
24
25
 
25
26
  ```ts snippet
26
- import { boardThemeToCss, themeMode, googleFontsUrl } from '@cavuno/board/theme';
27
+ import {
28
+ boardThemeToCss,
29
+ googleFontsUrl,
30
+ themeMode,
31
+ } from '@cavuno/board/theme';
27
32
 
28
33
  const context = await board.context();
29
- const css = boardThemeToCss(context.theme); // ':root {…}' + '.dark {…}'
30
- const mode = themeMode(context.theme); // 'light' | 'dark' | 'system'
31
- const fontsHref = googleFontsUrl(context.theme); // one <link>, or null
34
+ const hostedTheme = {
35
+ css: boardThemeToCss(context.theme),
36
+ fontUrl: googleFontsUrl(context.theme),
37
+ mode: themeMode(context.theme),
38
+ };
32
39
  ```
33
40
 
34
- Inject `css` in a `<style>` AFTER the static theme stylesheet so the
35
- overrides win; render nothing when it's empty (a null theme means the app's
36
- default theme applies untouched). Apply `mode` by toggling the `.dark`
37
- class (`system` = follow `prefers-color-scheme`).
38
-
39
- ## The mapping is the contract
40
-
41
- All 16 board color keys are consumed — a coverage golden in-monorepo
42
- asserts neither the hosted board nor this module drops one. Standard
43
- shadcn tokens carry the core (background/foreground, card, popover,
44
- primary, secondary, muted, accent, destructive, border, input, ring);
45
- the four keys shadcn has no standard token for ship as
46
- `--contrast-background`, `--contrast-foreground`, `--foreground-subtle`,
47
- `--foreground-disabled`, plus `--foreground-error`.
41
+ `boardThemeToCss` validates the stored colors before emitting CSS. A null theme returns no CSS, so the application default remains intact.
48
42
 
49
- ## Anti-patterns
50
-
51
- ```ts no-check
52
- // NEVER hardcode brand colors in components — they bypass the board theme:
53
- <button style={{ background: '#7c3aed' }} />
54
- // NEVER re-map theme keys ad hoc per page; the shell mapping is the single source.
55
- // NEVER fetch fonts per-family — googleFontsUrl builds ONE deduped request.
56
- ```
43
+ ## Migration guidance
57
44
 
58
- ## Out of scope do not invent exports
45
+ 1. Compare the returned colors, fonts, and mode with the consuming design system.
46
+ 2. Move the chosen values into the application's own semantic tokens and font loader.
47
+ 3. Remove the runtime theme injection when the application becomes authoritative.
59
48
 
60
- No color math (hover/pressed derivation, contrast checking the hosted
61
- dashboard owns palette design), no per-component theme props, no CSS-in-JS
62
- runtime. The module emits strings; the app owns injection.
49
+ If permanent multi-board mirroring is an explicit requirement, inject the validated CSS after the static theme and make the framework's theme provider apply the returned mode. Do not interpolate raw stored colors or build font URLs yourself.
63
50
 
64
51
  ## Verify
65
52
 
66
- - [ ] A board with a custom theme renders its brand color on primary
67
- buttons and focus rings; a board without one renders the app default.
68
- - [ ] Dark palette applies under `.dark` and `mode` drives the class.
69
- - [ ] The Google Fonts request appears once, covering sans + heading.
53
+ - [ ] The application renders correctly when `context.theme` is null.
54
+ - [ ] New custom frontends do not fetch theme data merely to establish basic styling.
55
+ - [ ] Compatibility CSS never contains unsafe stored color syntax.
56
+ - [ ] Font loading and color-mode ownership are explicit and tested.
57
+ - [ ] A migration has a documented point where the application becomes authoritative.
@@ -19,7 +19,6 @@ import { createBoardClient } from '@cavuno/board';
19
19
 
20
20
  // Module-scoped, no auth.storage → safe across concurrent Workers requests.
21
21
  export const board = createBoardClient({
22
- baseUrl: process.env.PUBLIC_CAVUNO_API_URL!,
23
22
  board: process.env.PUBLIC_CAVUNO_BOARD!,
24
23
  });
25
24
  ```
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.34.0",
2
+ "version": "1.35.0",
3
3
  "skills": [
4
4
  {
5
5
  "name": "cavuno-board-account",
@@ -31,7 +31,7 @@
31
31
  },
32
32
  {
33
33
  "name": "cavuno-board-client",
34
- "description": "Create and configure the @cavuno/board client — baseUrl and the pk_ board identifier, global headers, request/response hooks, per-call FetchOptions caching passthrough, the client.fetch escape hatch, and the rule that keeps one shared instance safe under SSR.",
34
+ "description": "Create and configure the @cavuno/board client — the pk_ board identifier, optional API-origin override, global headers, hooks, per-call FetchOptions, and the rule that keeps one shared instance safe under SSR.",
35
35
  "path": "skills/cavuno-board-client/SKILL.md",
36
36
  "framework": null,
37
37
  "category": "core"
@@ -87,7 +87,7 @@
87
87
  },
88
88
  {
89
89
  "name": "cavuno-board-jobs",
90
- "description": "Browse, search, and render jobs with the @cavuno/board SDK — jobs.list, jobs.search, jobs.retrieve, jobs.similar. Covers the slim card vs full job shapes, storefront pagination (count/limit/offset + opaque cursor), filters, and the candidate-paywall gatedCount.",
90
+ "description": "Browse, search, and render jobs with the @cavuno/board SDK — jobs.list, jobs.search, jobs.retrieve, jobs.similar. Covers the slim card vs full job shapes, catalog pagination (count/limit/offset + opaque cursor), filters, and the candidate-paywall gatedCount.",
91
91
  "path": "skills/cavuno-board-jobs/SKILL.md",
92
92
  "framework": null,
93
93
  "category": "core"
@@ -157,7 +157,7 @@
157
157
  },
158
158
  {
159
159
  "name": "cavuno-board-theme",
160
- "description": "Board branding with @cavuno/board/theme map the board's stored theme (16 semantic colors × light/dark + typography from board.context().theme) onto shadcn CSS variables, resolve the color-scheme mode, and build the Google Fonts request. Use when wiring the app shell, dark mode, or brand styling for a tenant frontend.",
160
+ "description": "Optional hosted-board theme compatibility with @cavuno/board/theme. Use only when a custom frontend deliberately mirrors or migrates an existing Cavuno hosted theme; the consuming application normally owns tokens, fonts, and color mode.",
161
161
  "path": "skills/cavuno-board-theme/SKILL.md",
162
162
  "framework": null,
163
163
  "category": "core"