create-kywi-app 0.6.4 → 0.6.6
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/assets/agent-patterns.md +40 -4
- package/assets/kywi-custom-modules-skill.md +11 -1
- package/lib/templates.mjs +24 -1
- package/package.json +1 -1
package/assets/agent-patterns.md
CHANGED
|
@@ -272,7 +272,7 @@ authored elsewhere in the admin).
|
|
|
272
272
|
| `name` | Purpose |
|
|
273
273
|
|---|---|
|
|
274
274
|
| `variantContainer` | Module-level arms revealed client-side per audience — read §8 first |
|
|
275
|
-
| `personalizationBadge` |
|
|
275
|
+
| `personalizationBadge` | Optional inline status pill that opens the global transparency panel — not needed on ordinary pages (§8) |
|
|
276
276
|
| `socialShare` | Share links for a URL/title across networks |
|
|
277
277
|
| `map` | Embedded map at a lat/lng or address |
|
|
278
278
|
| `cookieConsent` | Consent banner with message + privacy-policy link |
|
|
@@ -308,8 +308,12 @@ Full recipe, worked example and hazards: the **`kywi-custom-modules` skill**.
|
|
|
308
308
|
**Design the props for the owner, not for yourself.** The props panel gives the
|
|
309
309
|
owner real controls for `text`/`textarea`/`richText` (text inputs),
|
|
310
310
|
`image`/`file` (media picker), `boolean` (checkbox) and `number`/`date`/`color`
|
|
311
|
-
(native inputs); `select
|
|
312
|
-
|
|
311
|
+
(native inputs); `select` and `multiSelect` render a real dropdown or checkbox
|
|
312
|
+
group when the prop declares `options` — `{ value, label }` pairs, or bare
|
|
313
|
+
strings where the string serves as both — so declare options for every closed
|
|
314
|
+
choice instead of trusting the owner to type the right value. `slug` and
|
|
315
|
+
`relationship` have no options field and are still plain text inputs; that's
|
|
316
|
+
the one place to name the allowed values in the label. A `json` prop is a
|
|
313
317
|
raw JSON textarea — developer territory. A "three stats" module with `stats: {
|
|
314
318
|
type: 'json' }` has taken the copy away from the owner; the same module with
|
|
315
319
|
`stat1Value` / `stat1Label` … as `text` props hasn't.
|
|
@@ -406,12 +410,44 @@ Four traps, each of which fails silently:
|
|
|
406
410
|
audience arm first, so a visitor who matches an audience never enters the
|
|
407
411
|
split. Don't run an experiment and an audience variant on the same container.
|
|
408
412
|
4. **The client runtime needs `/kywi.js`.** Self-ID, behavioral re-evaluation
|
|
409
|
-
and the transparency
|
|
413
|
+
and the transparency panel no-op silently unless the built
|
|
410
414
|
`@kywi-software/js` bundle is at `public/kywi.js`.
|
|
411
415
|
|
|
412
416
|
Standing rule: **the default arm must be complete on its own.** It is what
|
|
413
417
|
search engines, answer engines, and every opted-out visitor receive.
|
|
414
418
|
|
|
419
|
+
### Transparency: automatic, not something you place
|
|
420
|
+
|
|
421
|
+
Once the client runtime is loaded, every personalized (or opted-out-with-a-
|
|
422
|
+
match) page grows a global transparency surface on its own — a small
|
|
423
|
+
collapsed pill, bottom of the page, that expands into a panel showing which
|
|
424
|
+
audience the page was personalized for, an audience switcher, reset-to-
|
|
425
|
+
automatic, and opt-out/opt-in. This is automatic and site-wide; you do not add
|
|
426
|
+
anything to a page to get it.
|
|
427
|
+
|
|
428
|
+
**Don't place the `personalizationBadge` module on ordinary pages.** It is a
|
|
429
|
+
second, optional entry point — a passive inline status pill that opens the
|
|
430
|
+
*same* global panel — for a spot where a deliberate, in-content link to
|
|
431
|
+
personalization settings makes sense (a privacy or preferences page). It
|
|
432
|
+
carries no opt-out controls of its own; switching audience and opting out
|
|
433
|
+
always happen in the one global panel, never in two places that could
|
|
434
|
+
disagree. Placing it on every page is redundant with the surface that's
|
|
435
|
+
already there automatically.
|
|
436
|
+
|
|
437
|
+
`personalization.transparencyNotice.enabled: false` (theme config) suppresses
|
|
438
|
+
the automatic panel from appearing on its own; the badge (or the panel's own
|
|
439
|
+
programmatic open) still works as a deliberate opt-in path.
|
|
440
|
+
|
|
441
|
+
### Self-ID widget: all six display modes
|
|
442
|
+
|
|
443
|
+
The self-ID widget (Admin → Audiences) is authorable in six display modes:
|
|
444
|
+
`modal`, `inline`, `slide-in`, `hello_bar_top`, `hello_bar_bottom`, and
|
|
445
|
+
`drawer`. For `inline`, the client runtime mounts the widget into an element
|
|
446
|
+
matching `[data-kywi-selfid-mount]` when the page provides one; otherwise it
|
|
447
|
+
appends to the end of `<body>`. Give an inline placement its own mount point
|
|
448
|
+
when you want the widget to sit in a specific spot in the page flow rather
|
|
449
|
+
than wherever the runtime defaults to.
|
|
450
|
+
|
|
415
451
|
## 9. Editorial workflow: drafts, review, versions
|
|
416
452
|
|
|
417
453
|
For any site with more than one author — or an owner who wants a safety net —
|
|
@@ -53,7 +53,8 @@ it — then pick the type:
|
|
|
53
53
|
| `image` / `file` | media picker | anything from the Media library |
|
|
54
54
|
| `boolean` | checkbox | layout flips (`reverse`), show/hide |
|
|
55
55
|
| `number` / `date` / `color` | native inputs | counts, deadlines, accents |
|
|
56
|
-
| `select` / `
|
|
56
|
+
| `select` / `multiSelect` | dropdown / checkbox group (needs `options`) | closed choices — declare `options: [{ value, label }, …]` (bare strings also work) per prop |
|
|
57
|
+
| `slug` / `relationship` | plain text input | constrained values with no options field yet — name the allowed values in the label |
|
|
57
58
|
| `json` | raw JSON textarea | **developer territory** — structure the owner never edits |
|
|
58
59
|
|
|
59
60
|
Two rules that decide whether the module is really editable:
|
|
@@ -84,6 +85,15 @@ export default defineKywiConfig({
|
|
|
84
85
|
body: { type: 'richText', label: 'Body' },
|
|
85
86
|
image: { type: 'image', label: 'Image' },
|
|
86
87
|
imageAlt: { type: 'text', label: 'Image alt text' },
|
|
88
|
+
layout: {
|
|
89
|
+
type: 'select',
|
|
90
|
+
label: 'Layout',
|
|
91
|
+
options: [
|
|
92
|
+
{ value: 'split', label: 'Media / text split' },
|
|
93
|
+
{ value: 'stacked', label: 'Stacked' },
|
|
94
|
+
],
|
|
95
|
+
defaultValue: 'split',
|
|
96
|
+
}, // closed choice = real dropdown
|
|
87
97
|
reverse: { type: 'boolean', label: 'Image on the left', defaultValue: false },
|
|
88
98
|
},
|
|
89
99
|
}),
|
package/lib/templates.mjs
CHANGED
|
@@ -779,6 +779,20 @@ export function clientRuntimeEnabled(): boolean {
|
|
|
779
779
|
return theme?.personalization?.clientRuntime === true
|
|
780
780
|
}
|
|
781
781
|
|
|
782
|
+
/**
|
|
783
|
+
* Is the global transparency surface (the automatic pill/panel showing which
|
|
784
|
+
* audience the page was personalized for, with a switcher and opt-out) enabled
|
|
785
|
+
* for this site's theme? \`personalization.transparencyNotice.enabled\` — unset
|
|
786
|
+
* defaults to true; only an explicit \`false\` suppresses the surface from
|
|
787
|
+
* appearing on its own. Even then a \`personalizationBadge\` module can still
|
|
788
|
+
* open it deliberately.
|
|
789
|
+
*/
|
|
790
|
+
export function transparencyNoticeEnabled(): boolean {
|
|
791
|
+
const themeName = config.sites[0]?.theme
|
|
792
|
+
const theme = config.themes.find((t) => t.name === themeName) ?? config.themes[0]
|
|
793
|
+
return theme?.personalization?.transparencyNotice?.enabled !== false
|
|
794
|
+
}
|
|
795
|
+
|
|
782
796
|
// ─── Self-ID widget (#50) ────────────────────────────────────────────────────
|
|
783
797
|
|
|
784
798
|
/** The self-ID widget config in the serializable shape the client runtime reads. */
|
|
@@ -1236,6 +1250,7 @@ import {
|
|
|
1236
1250
|
personalizeLayout,
|
|
1237
1251
|
resolveSelfIdWidget,
|
|
1238
1252
|
clientRuntimeEnabled,
|
|
1253
|
+
transparencyNoticeEnabled,
|
|
1239
1254
|
buildFeedResolver,
|
|
1240
1255
|
buildComponentResolver,
|
|
1241
1256
|
buildSectionComponentResolver,
|
|
@@ -1368,6 +1383,7 @@ export default async function PublicPage({ params, searchParams }: Params & Sear
|
|
|
1368
1383
|
audiences={perso.audiences}
|
|
1369
1384
|
serverSignals={perso.signals}
|
|
1370
1385
|
selfIdWidget={selfIdWidget}
|
|
1386
|
+
transparencyNotice={transparencyNoticeEnabled()}
|
|
1371
1387
|
/>
|
|
1372
1388
|
) : null}
|
|
1373
1389
|
</>
|
|
@@ -1696,6 +1712,12 @@ interface PersonalizationRuntimeProps {
|
|
|
1696
1712
|
serverSignals: Partial<VisitorSignals>
|
|
1697
1713
|
/** Resolved self-ID widget config; when present its trigger/frequency mount it. */
|
|
1698
1714
|
selfIdWidget?: PublicSelfIdWidget | null
|
|
1715
|
+
/**
|
|
1716
|
+
* \`personalization.transparencyNotice.enabled\` — false suppresses the
|
|
1717
|
+
* automatic global transparency panel; the visitor can still open it
|
|
1718
|
+
* deliberately through a \`personalizationBadge\` module. Defaults to true.
|
|
1719
|
+
*/
|
|
1720
|
+
transparencyNotice?: boolean
|
|
1699
1721
|
}
|
|
1700
1722
|
|
|
1701
1723
|
/**
|
|
@@ -1704,7 +1726,7 @@ interface PersonalizationRuntimeProps {
|
|
|
1704
1726
|
* experience, so opt-out is respected end to end. Rendered only when
|
|
1705
1727
|
* theme.personalization.clientRuntime is on.
|
|
1706
1728
|
*/
|
|
1707
|
-
export function PersonalizationRuntime({ audiences, serverSignals, selfIdWidget }: PersonalizationRuntimeProps) {
|
|
1729
|
+
export function PersonalizationRuntime({ audiences, serverSignals, selfIdWidget, transparencyNotice }: PersonalizationRuntimeProps) {
|
|
1708
1730
|
useEffect(() => {
|
|
1709
1731
|
let cancelled = false
|
|
1710
1732
|
const ready = () => typeof window.Kywi?.bootAudienceEngine === 'function'
|
|
@@ -1720,6 +1742,7 @@ export function PersonalizationRuntime({ audiences, serverSignals, selfIdWidget
|
|
|
1720
1742
|
...(selfIdWidget
|
|
1721
1743
|
? { selfIdWidget: { ...selfIdWidget, audiences, currentPath } }
|
|
1722
1744
|
: {}),
|
|
1745
|
+
...(transparencyNotice === false ? { transparencyNotice: false } : {}),
|
|
1723
1746
|
})
|
|
1724
1747
|
.catch(() => {
|
|
1725
1748
|
/* client personalization is best-effort; the server already rendered defaults */
|
package/package.json
CHANGED