@explorer02/cfm-survey-sdk 0.2.5 → 0.2.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/dist/cli/index.js +45 -45
- package/dist/cli/index.mjs +45 -45
- package/package.json +1 -1
- package/templates/docs/00-integration/agent-execution-flow.md +7 -0
- package/templates/docs/00-integration/component-checklist.md +5 -3
- package/templates/docs/00-integration/ui-customization-wizard.md +1 -1
- package/templates/docs/00-integration/wizard-chrome-contract.md +210 -0
- package/templates/docs/00-integration/wizard-config-handoff.md +2 -0
- package/templates/docs/00-integration/wizard-preview-build-guide.md +18 -4
- package/templates/docs/01-components/19-survey-sticky-chrome.md +16 -55
- package/templates/docs/MANIFEST.json +4 -0
- package/templates/docs/index.md +2 -2
- package/templates/docs/templates/Footer.tsx +72 -0
- package/templates/docs/templates/Header.tsx +84 -0
- package/templates/docs/templates/LanguageSelector.tsx +40 -0
- package/templates/docs/templates/PreviewBridgeInit.tsx +13 -0
- package/templates/docs/templates/ProgressBar.tsx +33 -0
- package/templates/docs/templates/Question.tsx +50 -7
- package/templates/docs/templates/SurveyStickyChrome.tsx +19 -5
- package/templates/docs/templates/verify-agent-build.sh +25 -1
- package/templates/preview-harness/preview-bridge.inline.js +1 -1
- package/templates/previewBridge.ts +131 -38
- package/templates/wizard-dist/assets/{PreviewMock-AoZdAEPZ.js → PreviewMock-7peNZK-z.js} +1 -1
- package/templates/wizard-dist/assets/{TypePanel-39FnbtvG.js → TypePanel-BxHnE3cm.js} +1 -1
- package/templates/wizard-dist/assets/index-DtZhNwPS.js +34 -0
- package/templates/wizard-dist/index.html +1 -1
- package/templates/wizard-dist/assets/index-CWKdsJOp.js +0 -34
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# Wizard Chrome Contract — Required DOM + CSS Hooks
|
|
2
|
+
|
|
3
|
+
> **Mandatory Phase 5.** If any hook is missing, wizard live preview fails for that control — even though the survey may look fine in localhost dev without the wizard open.
|
|
4
|
+
> **Copy-first templates:** [`templates/Header.tsx`](../templates/Header.tsx), [`Footer.tsx`](../templates/Footer.tsx), [`ProgressBar.tsx`](../templates/ProgressBar.tsx), [`SurveyStickyChrome.tsx`](../templates/SurveyStickyChrome.tsx), [`LanguageSelector.tsx`](../templates/LanguageSelector.tsx), [`PreviewBridgeInit.tsx`](../templates/PreviewBridgeInit.tsx), [`previewBridge.ts`](../../previewBridge.ts).
|
|
5
|
+
|
|
6
|
+
Pipeline: [`wizard-preview-build-guide.md`](wizard-preview-build-guide.md) · [`apply-ui-config.md`](apply-ui-config.md)
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Symptom → missing hook (quick fix)
|
|
11
|
+
|
|
12
|
+
| Wizard control / symptom | Required fix |
|
|
13
|
+
|--------------------------|--------------|
|
|
14
|
+
| Logo not updating in Branding / Header preview | `data-cfm-logo` img + `data-cfm-logo-text` fallback in `Header.tsx` |
|
|
15
|
+
| Company name not updating | `data-cfm-company` span in `Header.tsx` |
|
|
16
|
+
| Show header works but progress bar toggle does nothing | Progress bar needs `data-cfm-progress` + class `cfm-progress`; header needs class `cfm-header` only — **not** on sticky wrapper |
|
|
17
|
+
| Show footer toggle does nothing | `footer` + class `cfm-footer` |
|
|
18
|
+
| Question numbers / required asterisk toggles | `data-cfm-question-number` + `data-cfm-required` on every question title |
|
|
19
|
+
| Question card padding/shadow/colors not live-updating | Question wrapper uses `var(--cfm-card-*)` — not hardcoded `bg-white shadow-sm` |
|
|
20
|
+
| MCQ / NPS / dropzone style pickers | Root reads `html[data-cfm-mcq-style]`, `[data-cfm-nps-button-style]`, `[data-cfm-dropzone-style]` from bridge |
|
|
21
|
+
| Scale selection colors not updating | Import `selectionStyles.ts`; use `var(--cfm-matrix-selected)` / `var(--cfm-input-focus-ring)` |
|
|
22
|
+
| Nothing updates except static harness | Agent build missing token contract — re-copy templates above |
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Copy order (agents — do this in Phase 5)
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# From SDK templates folder (monorepo or node_modules)
|
|
30
|
+
cp templates/docs/templates/Header.tsx src/components/Header.tsx
|
|
31
|
+
cp templates/docs/templates/Footer.tsx src/components/Footer.tsx
|
|
32
|
+
cp templates/docs/templates/ProgressBar.tsx src/components/ProgressBar.tsx
|
|
33
|
+
cp templates/docs/templates/SurveyStickyChrome.tsx src/components/SurveyStickyChrome.tsx
|
|
34
|
+
cp templates/docs/templates/LanguageSelector.tsx src/components/LanguageSelector.tsx
|
|
35
|
+
cp templates/docs/templates/PreviewBridgeInit.tsx src/components/PreviewBridgeInit.tsx
|
|
36
|
+
cp templates/previewBridge.ts src/lib/previewBridge.ts
|
|
37
|
+
cp templates/survey-theme.css src/styles/survey-theme.css
|
|
38
|
+
cp templates/docs/templates/selectionStyles.ts src/lib/surveyUi/selectionStyles.ts
|
|
39
|
+
cp templates/docs/templates/labelStyles.ts src/lib/surveyUi/labelStyles.ts
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Wire `PreviewBridgeInit` in `layout.tsx` (local dev). Wizard iframes use injected `preview-bridge.inline.js` at export time.
|
|
43
|
+
|
|
44
|
+
Run verification:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
bash templates/verify-agent-build.sh
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Layer 1 — CSS variables (`--cfm-*`)
|
|
53
|
+
|
|
54
|
+
Wizard sends SNAPSHOT (full) + DELTA (partial) CSS vars to `:root`.
|
|
55
|
+
|
|
56
|
+
**Rule:** Every color, radius, spacing, or chrome dimension editable in the wizard must be consumed via `var(--cfm-…)` — not hardcoded `#e20074` / `bg-[#e20074]`.
|
|
57
|
+
|
|
58
|
+
Minimum files:
|
|
59
|
+
|
|
60
|
+
| File | Role |
|
|
61
|
+
|------|------|
|
|
62
|
+
| `src/styles/survey-theme.css` | All default `--cfm-*` vars |
|
|
63
|
+
| `globals.css` | `@import './styles/survey-theme.css'` |
|
|
64
|
+
| Scale components | `selectionStyles.ts` + `labelStyles.ts` |
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Layer 2 — Content patches (`data-cfm-*` + layout flags)
|
|
69
|
+
|
|
70
|
+
Wizard sends `contentPatch` on SNAPSHOT (not DELTA). Preview bridge applies patches to matching DOM nodes.
|
|
71
|
+
|
|
72
|
+
### Branding & header
|
|
73
|
+
|
|
74
|
+
| Patch field | DOM target | Template location |
|
|
75
|
+
|-------------|------------|-------------------|
|
|
76
|
+
| `logoUrl` | `[data-cfm-logo]` → sets `src`, shows img; hides `[data-cfm-logo-text]` | `Header.tsx` |
|
|
77
|
+
| `companyName` | `[data-cfm-company]` text; fills logo-text when img hidden | `Header.tsx` |
|
|
78
|
+
| `layoutFlags.showCompanyName` | `[data-cfm-company]` display | bridge |
|
|
79
|
+
|
|
80
|
+
### Layout toggles (independent — critical)
|
|
81
|
+
|
|
82
|
+
| `layoutFlags` key | Bridge selector | Component requirement |
|
|
83
|
+
|-------------------|-----------------|----------------------|
|
|
84
|
+
| `showHeader` | `.cfm-header`, `header.cfm-header` | Class on `<header>` only |
|
|
85
|
+
| `showProgressBar` | `.cfm-progress`, `[data-cfm-progress]` | **Separate** from header — `ProgressBar.tsx` |
|
|
86
|
+
| `showFooter` | `.cfm-footer`, `footer.cfm-footer` | Class on `<footer>` |
|
|
87
|
+
| `showLanguageSwitcher` | `[data-cfm-language]` | `LanguageSelector.tsx` |
|
|
88
|
+
| `showNextButton` | `[data-cfm-btn-next]` | `SurveyPage.tsx` nav |
|
|
89
|
+
| `showBackButton` | `[data-cfm-btn-back]` | `SurveyPage.tsx` nav |
|
|
90
|
+
| `showQuestionNumbers` | `[data-cfm-question-number]` | `Question.tsx` title |
|
|
91
|
+
| `showRequiredAsterisk` | `[data-cfm-required]` | `Question.tsx` title |
|
|
92
|
+
| `showFooterLogo` | `[data-cfm-footer-logo]` | `Footer.tsx` |
|
|
93
|
+
|
|
94
|
+
**Anti-pattern:** Putting `sticky top-0` on the same element as `cfm-header` — bridge used to hide progress when toggling header. Templates use `cfm-sticky-chrome` wrapper without using it for `showHeader`.
|
|
95
|
+
|
|
96
|
+
### Survey chrome content
|
|
97
|
+
|
|
98
|
+
| Patch field | DOM target |
|
|
99
|
+
|-------------|------------|
|
|
100
|
+
| `surveyTitle` | `[data-cfm-survey-title]` |
|
|
101
|
+
| `thankYouMessage` | `[data-cfm-thank-you]` |
|
|
102
|
+
| `buttonLabels.next` / `.back` | `[data-cfm-btn-next]` / `[data-cfm-btn-back]` |
|
|
103
|
+
| `copyrightText` | `[data-cfm-copyright]` |
|
|
104
|
+
| `footerLinks[]` | `[data-cfm-footer-links]` innerHTML |
|
|
105
|
+
|
|
106
|
+
### Question-type DOM attributes (on `<html>`)
|
|
107
|
+
|
|
108
|
+
Set via `contentPatch.domAttributes`:
|
|
109
|
+
|
|
110
|
+
| Attribute | Wizard source | Component reads |
|
|
111
|
+
|-----------|---------------|-----------------|
|
|
112
|
+
| `data-cfm-mcq-style` | `questionTypes.MCQ.optionStyle` | MCQ card classes / CSS |
|
|
113
|
+
| `data-cfm-nps-button-style` | `questionTypes.NPS_SCALE.buttonStyle` | `RatingScale.tsx` |
|
|
114
|
+
| `data-cfm-dropzone-style` | `questionTypes.FILE_UPLOAD.dropzoneStyle` | `FileUploadScale.tsx` |
|
|
115
|
+
|
|
116
|
+
Vite preview harness also applies `previewState` via `PreviewConfigContext` for placeholders, NPS hints, rank variant, etc.
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## Question card (Theme step)
|
|
121
|
+
|
|
122
|
+
Question outer wrapper **must** use theme vars:
|
|
123
|
+
|
|
124
|
+
```tsx
|
|
125
|
+
<section
|
|
126
|
+
id={question.id}
|
|
127
|
+
className="cfm-question-card space-y-4"
|
|
128
|
+
style={{
|
|
129
|
+
background: 'var(--cfm-card-bg, #fff)',
|
|
130
|
+
borderColor: 'var(--cfm-card-border-color, #e5e7eb)',
|
|
131
|
+
padding: 'var(--cfm-card-padding, 24px)',
|
|
132
|
+
boxShadow: 'var(--cfm-card-shadow, 0 1px 3px rgba(0,0,0,0.1))',
|
|
133
|
+
borderRadius: 'var(--cfm-border-radius, 12px)',
|
|
134
|
+
borderWidth: 1,
|
|
135
|
+
borderStyle: 'solid',
|
|
136
|
+
}}
|
|
137
|
+
>
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Question title hooks:
|
|
141
|
+
|
|
142
|
+
```tsx
|
|
143
|
+
<h2>
|
|
144
|
+
{question.questionNumber != null && (
|
|
145
|
+
<span data-cfm-question-number className="mr-1 font-semibold">
|
|
146
|
+
{question.questionNumber}.
|
|
147
|
+
</span>
|
|
148
|
+
)}
|
|
149
|
+
<span dangerouslySetInnerHTML={{ __html: displayQuestionText }} />
|
|
150
|
+
{question.required && (
|
|
151
|
+
<span data-cfm-required className="ml-1 text-red-500">*</span>
|
|
152
|
+
)}
|
|
153
|
+
</h2>
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
TEXTFIELD placeholder live preview: `data-cfm-textfield` on input/textarea.
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## SurveyPage wiring (content phase)
|
|
161
|
+
|
|
162
|
+
```tsx
|
|
163
|
+
<div className="flex min-h-screen flex-col" style={{ background: 'var(--cfm-background)' }}>
|
|
164
|
+
<SurveyStickyChrome progressPercentage={state.progressPercentage} />
|
|
165
|
+
<main style={{ maxWidth: 'var(--cfm-max-width)' }}>...</main>
|
|
166
|
+
<Footer />
|
|
167
|
+
</div>
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Intro / end / paused: standalone `<Header />` (not sticky chrome). Footer still uses `cfm-footer`.
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## Local wizard quit — preview garbage collection
|
|
175
|
+
|
|
176
|
+
When the client **quits** local wizard (`npx cfm-sdk customize`) without saving:
|
|
177
|
+
|
|
178
|
+
1. Wizard SPA calls `POST /api/wizard-quit` on the customize server
|
|
179
|
+
2. CLI deletes `out/previews/` on agent disk
|
|
180
|
+
3. CLI exits; no orphan preview folders
|
|
181
|
+
|
|
182
|
+
When client **saves**: config written; previews remain until CLI exits (then server stops).
|
|
183
|
+
|
|
184
|
+
When CLI is **Ctrl+C**'d: customize server runs the same preview cleanup on shutdown.
|
|
185
|
+
|
|
186
|
+
EC2 `customize-ui`: quit calls `POST /wizard/session/quit` — disk session folder deleted (see [`wizard-config-handoff.md`](wizard-config-handoff.md)).
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## Verification checklist (blocking before Phase 6b)
|
|
191
|
+
|
|
192
|
+
- [ ] Copied all templates from § Copy order
|
|
193
|
+
- [ ] `grep -r data-cfm-logo src/components/Header.tsx` — match
|
|
194
|
+
- [ ] `grep -r data-cfm-progress src/components/ProgressBar.tsx` — match
|
|
195
|
+
- [ ] `grep -r cfm-footer src/components/Footer.tsx` — match
|
|
196
|
+
- [ ] `grep -r data-cfm-question-number src/components/Question.tsx` — match
|
|
197
|
+
- [ ] `grep -rE 'var\\(--cfm-' src/components/` — no wizard colors as hardcoded hex only
|
|
198
|
+
- [ ] `bash templates/verify-agent-build.sh` exit 0
|
|
199
|
+
- [ ] `npx cfm-sdk export-previews` exit 0
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## Related
|
|
204
|
+
|
|
205
|
+
| Doc | Role |
|
|
206
|
+
|-----|------|
|
|
207
|
+
| [`wizard-preview-build-guide.md`](wizard-preview-build-guide.md) | Full build guide |
|
|
208
|
+
| [`wizard-question-type-styling.md`](wizard-question-type-styling.md) | Per-type CSS vars |
|
|
209
|
+
| [`10-header-footer.md`](../01-components/10-header-footer.md) | Branding patterns |
|
|
210
|
+
| [`12-survey-chrome.md`](../03-ui-specs/12-survey-chrome.md) | Chrome layout spec |
|
|
@@ -53,6 +53,8 @@ When the client uploads a logo in the local wizard:
|
|
|
53
53
|
|
|
54
54
|
The logo is **not** fetched from wizard memory on CloudFront — only from the deploy bundle. See [`aws-deploy.md`](aws-deploy.md) § Logos & local assets.
|
|
55
55
|
|
|
56
|
+
**Local quit (preview GC):** If the client exits without saving, wizard calls `POST /api/wizard-quit` → CLI deletes `out/previews/` on agent disk. Ctrl+C on customize CLI runs the same cleanup.
|
|
57
|
+
|
|
56
58
|
---
|
|
57
59
|
|
|
58
60
|
## Remote client flow — `customize-ui` (fallback only)
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
# Wizard Live Preview — Client Build Guide
|
|
2
2
|
|
|
3
3
|
> **Read during Phase 5 (BUILD) — mandatory.** Wizard **run** (Phase 6b) is optional; this contract is required for every agent build.
|
|
4
|
+
> **Authoritative hook list + copy commands:** [`wizard-chrome-contract.md`](wizard-chrome-contract.md) — fixes logo, layout toggles, question card, and per-type preview issues.
|
|
4
5
|
> Pipeline: [`agent-execution-flow.md`](agent-execution-flow.md).
|
|
5
6
|
|
|
6
7
|
Related docs:
|
|
7
8
|
|
|
9
|
+
- [`wizard-chrome-contract.md`](wizard-chrome-contract.md) — **copy-first templates** + symptom → fix table
|
|
10
|
+
|
|
8
11
|
- [`ui-customization-wizard.md`](ui-customization-wizard.md) — wizard flow and CLI
|
|
9
12
|
- [`apply-ui-config.md`](apply-ui-config.md) — applying final `survey-ui-config.json` after wizard
|
|
10
13
|
- [`wizard-api.md`](wizard-api.md) — EC2 `/wizard/*` API
|
|
@@ -51,14 +54,25 @@ If components use **hardcoded** colors (`bg-[#e20074]`) with no `var(--cfm-*)` h
|
|
|
51
54
|
|
|
52
55
|
## Required project artifacts (Phase 5)
|
|
53
56
|
|
|
54
|
-
|
|
57
|
+
**Copy wizard-ready templates first** (do not hand-write chrome from `apps/client`):
|
|
58
|
+
|
|
59
|
+
| Template (SDK) | Copy to |
|
|
60
|
+
|----------------|---------|
|
|
61
|
+
| `templates/docs/templates/Header.tsx` | `src/components/Header.tsx` |
|
|
62
|
+
| `templates/docs/templates/Footer.tsx` | `src/components/Footer.tsx` |
|
|
63
|
+
| `templates/docs/templates/ProgressBar.tsx` | `src/components/ProgressBar.tsx` |
|
|
64
|
+
| `templates/docs/templates/SurveyStickyChrome.tsx` | `src/components/SurveyStickyChrome.tsx` |
|
|
65
|
+
| `templates/docs/templates/LanguageSelector.tsx` | `src/components/LanguageSelector.tsx` |
|
|
66
|
+
| `templates/docs/templates/PreviewBridgeInit.tsx` | `src/components/PreviewBridgeInit.tsx` |
|
|
67
|
+
| `templates/previewBridge.ts` | `src/lib/previewBridge.ts` |
|
|
68
|
+
| `templates/survey-theme.css` | `src/styles/survey-theme.css` |
|
|
69
|
+
| `templates/docs/templates/Question.tsx` | `src/components/Question.tsx` (dispatcher base) |
|
|
70
|
+
|
|
71
|
+
Full symptom → fix table: [`wizard-chrome-contract.md`](wizard-chrome-contract.md).
|
|
55
72
|
|
|
56
73
|
| File | Purpose |
|
|
57
74
|
|------|---------|
|
|
58
|
-
| `src/styles/survey-theme.css` | All `--cfm-*` CSS custom properties (defaults from mockup) |
|
|
59
75
|
| `survey-ui-config.json` (draft) | Seeds wizard form fields from agent branding |
|
|
60
|
-
| `src/lib/previewBridge.ts` | Live bridge in **local dev** (`npx cfm-sdk customize`) — copy from `templates/previewBridge.ts` |
|
|
61
|
-
| `src/components/PreviewBridgeInit.tsx` | Mount bridge once in `layout.tsx` (local dev only) |
|
|
62
76
|
|
|
63
77
|
Import theme in `globals.css`:
|
|
64
78
|
|
|
@@ -1,30 +1,28 @@
|
|
|
1
1
|
# Survey Sticky Chrome
|
|
2
2
|
|
|
3
|
-
> **UI spec (authoritative):** `03-ui-specs/12-survey-chrome.md`
|
|
4
|
-
> **
|
|
3
|
+
> **UI spec (authoritative):** `03-ui-specs/12-survey-chrome.md`
|
|
4
|
+
> **Wizard contract:** [`wizard-chrome-contract.md`](../00-integration/wizard-chrome-contract.md) — header and progress are **separate** toggle targets
|
|
5
5
|
> **Template:** [`templates/SurveyStickyChrome.tsx`](../templates/SurveyStickyChrome.tsx)
|
|
6
|
-
> **Component**: `SurveyStickyChrome`
|
|
7
6
|
|
|
8
7
|
## Purpose
|
|
9
8
|
|
|
10
|
-
Wrap **Header + ProgressBar** in a
|
|
9
|
+
Wrap **Header + ProgressBar** in a sticky container. The wizard toggles them **independently**:
|
|
11
10
|
|
|
12
|
-
|
|
11
|
+
| Toggle | Target |
|
|
12
|
+
|--------|--------|
|
|
13
|
+
| `showHeader` | `.cfm-header` on `<header>` only |
|
|
14
|
+
| `showProgressBar` | `[data-cfm-progress]` / `.cfm-progress` only |
|
|
13
15
|
|
|
14
|
-
|
|
15
|
-
type SurveyStickyChromeProps = {
|
|
16
|
-
progressPercentage: number; // from state.progressPercentage
|
|
17
|
-
};
|
|
18
|
-
```
|
|
16
|
+
Do **not** put class `cfm-header` on the sticky wrapper.
|
|
19
17
|
|
|
20
|
-
## Implementation
|
|
18
|
+
## Implementation (wizard-ready)
|
|
21
19
|
|
|
22
20
|
```tsx
|
|
23
21
|
export default function SurveyStickyChrome({ progressPercentage }: SurveyStickyChromeProps) {
|
|
24
22
|
return (
|
|
25
|
-
<div className="sticky top-0 z-30
|
|
23
|
+
<div className="cfm-sticky-chrome sticky top-0 z-30" style={{ background: 'var(--cfm-header-bg)' }}>
|
|
26
24
|
<Header embedded />
|
|
27
|
-
<div className="mx-auto w-full
|
|
25
|
+
<div className="mx-auto w-full px-4" style={{ maxWidth: 'var(--cfm-max-width)' }}>
|
|
28
26
|
<ProgressBar progressPercentage={progressPercentage} />
|
|
29
27
|
</div>
|
|
30
28
|
</div>
|
|
@@ -32,48 +30,11 @@ export default function SurveyStickyChrome({ progressPercentage }: SurveyStickyC
|
|
|
32
30
|
}
|
|
33
31
|
```
|
|
34
32
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
Keep a visible gap between the logo block and the progress track — do **not** flush them together.
|
|
38
|
-
|
|
39
|
-
| Token | Tailwind | Purpose |
|
|
40
|
-
|-------|----------|---------|
|
|
41
|
-
| Top inset | `pt-4` | 16px gap below logo / header row |
|
|
42
|
-
| Bottom inset | `pb-5` | 20px gap above scrollable question content |
|
|
43
|
-
| Horizontal | `px-4 sm:px-6 lg:px-8` | Align track with `main` content column (`max-w-4xl`) |
|
|
44
|
-
|
|
45
|
-
## Header `embedded` prop
|
|
46
|
-
|
|
47
|
-
When `Header` is inside sticky chrome, pass `embedded` to avoid a double shadow:
|
|
48
|
-
|
|
49
|
-
```tsx
|
|
50
|
-
<header className={`relative h-[120px] w-full bg-white ${embedded ? '' : 'z-10 shadow-[0_2px_8px_rgba(0,0,0,0.06)]'}`}>
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
Use the standalone `Header` (no `embedded`) on intro, end, loading, and terminal pages.
|
|
54
|
-
|
|
55
|
-
## SurveyPage wiring (content phase only)
|
|
56
|
-
|
|
57
|
-
```tsx
|
|
58
|
-
<div className="flex min-h-screen flex-col bg-white">
|
|
59
|
-
<SurveyStickyChrome progressPercentage={state.progressPercentage} />
|
|
60
|
-
|
|
61
|
-
<main className="mx-auto w-full max-w-4xl flex-1 px-4 py-10 sm:px-6 lg:px-8">
|
|
62
|
-
<LanguageSelector ... />
|
|
63
|
-
{/* questions + nav */}
|
|
64
|
-
</main>
|
|
65
|
-
|
|
66
|
-
<Footer />
|
|
67
|
-
</div>
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
Do **not** render a separate `ProgressBar` inside `<main>` — it belongs in sticky chrome only.
|
|
33
|
+
Copy full file from [`templates/SurveyStickyChrome.tsx`](../templates/SurveyStickyChrome.tsx).
|
|
71
34
|
|
|
72
35
|
## Agent checklist
|
|
73
36
|
|
|
74
|
-
- [ ] `
|
|
75
|
-
- [ ] `ProgressBar`
|
|
76
|
-
- [ ]
|
|
77
|
-
- [ ] `pt
|
|
78
|
-
- [ ] Progress track aligned to same `max-w-4xl` as question content
|
|
79
|
-
- [ ] Standalone `Header` on intro / end / loading / terminal branches
|
|
37
|
+
- [ ] `Header.tsx` has class `cfm-header` + `data-cfm-logo` + `data-cfm-company`
|
|
38
|
+
- [ ] `ProgressBar.tsx` has `data-cfm-progress` + class `cfm-progress`
|
|
39
|
+
- [ ] Sticky wrapper uses `cfm-sticky-chrome` — not `cfm-header`
|
|
40
|
+
- [ ] `pt`/`pb` spacing via `--cfm-sticky-pt` / `--cfm-sticky-pb`
|
|
@@ -156,6 +156,10 @@
|
|
|
156
156
|
"path": "00-integration/client-lib-folder.md",
|
|
157
157
|
"purpose": "src/lib layout — customFieldValues + surveyUi portable templates"
|
|
158
158
|
},
|
|
159
|
+
{
|
|
160
|
+
"path": "00-integration/wizard-chrome-contract.md",
|
|
161
|
+
"purpose": "Wizard DOM/CSS hook contract — copy-first Header/Footer/ProgressBar templates (Phase 5)"
|
|
162
|
+
},
|
|
159
163
|
{
|
|
160
164
|
"path": "00-integration/wizard-preview-build-guide.md",
|
|
161
165
|
"purpose": "Build client components for wizard live preview — --cfm-* vars and data-cfm-* hooks (Phase 5)"
|
package/templates/docs/index.md
CHANGED
|
@@ -77,7 +77,7 @@ Full step-by-step: [`agent-execution-flow.md`](00-integration/agent-execution-fl
|
|
|
77
77
|
| **2** Instance ID | Get JWT | `useSurveySDK.md` — **ask client** |
|
|
78
78
|
| **3** SDK contract | Hook + logic | `client-integration-guide.md`, `question-type-sdk-matrix.md`, logic docs |
|
|
79
79
|
| **4** Plan | Inventory + copy dispatcher | `component-checklist.md`, per-type MANIFEST docs, `implementation_plan.md` |
|
|
80
|
-
| **5** Build | Wizard-ready UI | `
|
|
80
|
+
| **5** Build | Wizard-ready UI | `wizard-chrome-contract.md`, `wizard-preview-build-guide.md`, `wizard-question-type-styling.md`, `03-ui-specs/*` |
|
|
81
81
|
| **6** Verify | Build + dev server | `verify-agent-build.sh` — **ask wizard** |
|
|
82
82
|
| **6b** Wizard | Optional customize | **`npx cfm-sdk customize`** (default) — EC2 `customize-ui` only if client on another device |
|
|
83
83
|
| **6c** Apply | Sync final config | `apply-ui-config.md` |
|
|
@@ -101,7 +101,7 @@ Every agent build includes these even if the client later skips the wizard:
|
|
|
101
101
|
| Scale components | `templates/*Scale*.tsx` etc. | `src/components/` per inventory |
|
|
102
102
|
| Draft config | agent-written | `./survey-ui-config.json` |
|
|
103
103
|
|
|
104
|
-
**Chrome:** `
|
|
104
|
+
**Chrome:** copy `templates/docs/templates/Header.tsx` etc. — see `wizard-chrome-contract.md` for full hook list.
|
|
105
105
|
|
|
106
106
|
**Selection pattern:** focus ring = outer border + inner dot; cell selected = annulus fill — see `wizard-question-type-styling.md`.
|
|
107
107
|
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wizard-ready Footer — copy to src/components/Footer.tsx
|
|
3
|
+
* Required: footer.cfm-footer, data-cfm-copyright, data-cfm-footer-links, data-cfm-footer-logo
|
|
4
|
+
*/
|
|
5
|
+
'use client';
|
|
6
|
+
|
|
7
|
+
export default function Footer() {
|
|
8
|
+
return (
|
|
9
|
+
<footer
|
|
10
|
+
className="cfm-footer w-full"
|
|
11
|
+
style={{
|
|
12
|
+
background: 'var(--cfm-footer-bg, #262626)',
|
|
13
|
+
color: 'var(--cfm-footer-text, #9ca3af)',
|
|
14
|
+
padding: 'var(--cfm-footer-padding-y, 24px) var(--cfm-footer-padding-x, 40px)',
|
|
15
|
+
}}
|
|
16
|
+
>
|
|
17
|
+
<div
|
|
18
|
+
className="cfm-footer-inner mx-auto grid w-full max-w-4xl items-end gap-6"
|
|
19
|
+
style={{
|
|
20
|
+
display: 'var(--cfm-footer-inner-display, grid)',
|
|
21
|
+
flexDirection: 'var(--cfm-footer-inner-direction, row)' as React.CSSProperties['flexDirection'],
|
|
22
|
+
gridTemplateColumns: '1fr 1fr 1fr',
|
|
23
|
+
}}
|
|
24
|
+
>
|
|
25
|
+
<div
|
|
26
|
+
className="space-y-3"
|
|
27
|
+
style={{
|
|
28
|
+
gridColumn: 'var(--cfm-footer-logo-col, 1)',
|
|
29
|
+
justifySelf: 'var(--cfm-footer-logo-justify, start)',
|
|
30
|
+
}}
|
|
31
|
+
>
|
|
32
|
+
<img
|
|
33
|
+
data-cfm-footer-logo
|
|
34
|
+
data-cfm-logo
|
|
35
|
+
src="./logo.svg"
|
|
36
|
+
alt=""
|
|
37
|
+
style={{
|
|
38
|
+
display: 'none',
|
|
39
|
+
width: 'var(--cfm-footer-logo-width, 120px)',
|
|
40
|
+
height: 'var(--cfm-footer-logo-height, 32px)',
|
|
41
|
+
objectFit: 'contain',
|
|
42
|
+
}}
|
|
43
|
+
/>
|
|
44
|
+
<p
|
|
45
|
+
data-cfm-copyright
|
|
46
|
+
className="text-xs"
|
|
47
|
+
style={{ color: 'var(--cfm-footer-text, #9ca3af)' }}
|
|
48
|
+
>
|
|
49
|
+
© Company
|
|
50
|
+
</p>
|
|
51
|
+
</div>
|
|
52
|
+
|
|
53
|
+
<nav
|
|
54
|
+
data-cfm-footer-links
|
|
55
|
+
className="flex flex-wrap gap-x-6 gap-y-2 text-xs"
|
|
56
|
+
style={{
|
|
57
|
+
gridColumn: 'var(--cfm-footer-links-col, 3)',
|
|
58
|
+
justifySelf: 'var(--cfm-footer-links-justify, end)',
|
|
59
|
+
}}
|
|
60
|
+
>
|
|
61
|
+
<a
|
|
62
|
+
href="#"
|
|
63
|
+
className="transition-colors hover:opacity-80"
|
|
64
|
+
style={{ color: 'var(--cfm-footer-link, #9ca3af)' }}
|
|
65
|
+
>
|
|
66
|
+
Privacy
|
|
67
|
+
</a>
|
|
68
|
+
</nav>
|
|
69
|
+
</div>
|
|
70
|
+
</footer>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wizard-ready Header — copy to src/components/Header.tsx
|
|
3
|
+
* Required hooks: data-cfm-logo, data-cfm-logo-text, data-cfm-company
|
|
4
|
+
* Required class: cfm-header (layout toggles target header only — not progress bar)
|
|
5
|
+
*/
|
|
6
|
+
'use client';
|
|
7
|
+
|
|
8
|
+
type HeaderProps = {
|
|
9
|
+
/** When true, omits outer shadow — use inside SurveyStickyChrome */
|
|
10
|
+
embedded?: boolean;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export default function Header({ embedded = false }: HeaderProps) {
|
|
14
|
+
return (
|
|
15
|
+
<header
|
|
16
|
+
className={`cfm-header relative w-full ${embedded ? '' : 'z-10 shadow-[0_2px_8px_rgba(0,0,0,0.06)]'}`}
|
|
17
|
+
style={{
|
|
18
|
+
height: 'var(--cfm-header-height, 120px)',
|
|
19
|
+
background: 'var(--cfm-header-bg, #fff)',
|
|
20
|
+
borderBottom: '1px solid var(--cfm-header-border, #e5e7eb)',
|
|
21
|
+
boxShadow: embedded ? undefined : 'var(--cfm-header-shadow, 0 2px 8px rgba(0,0,0,0.06))',
|
|
22
|
+
}}
|
|
23
|
+
>
|
|
24
|
+
<div
|
|
25
|
+
className="cfm-header-inner grid h-full w-full items-center"
|
|
26
|
+
style={{
|
|
27
|
+
gridTemplateColumns: '1fr 1fr 1fr',
|
|
28
|
+
padding: `0 var(--cfm-header-padding-x, 40px)`,
|
|
29
|
+
gap: 'var(--cfm-header-brand-gap, 16px)',
|
|
30
|
+
}}
|
|
31
|
+
>
|
|
32
|
+
<div
|
|
33
|
+
className="flex items-center"
|
|
34
|
+
style={{
|
|
35
|
+
gridColumn: 'var(--cfm-header-logo-col, 1)',
|
|
36
|
+
justifySelf: 'var(--cfm-header-logo-justify, start)',
|
|
37
|
+
}}
|
|
38
|
+
>
|
|
39
|
+
<img
|
|
40
|
+
data-cfm-logo
|
|
41
|
+
src="./logo.svg"
|
|
42
|
+
alt="Logo"
|
|
43
|
+
className="object-contain"
|
|
44
|
+
style={{
|
|
45
|
+
display: 'none',
|
|
46
|
+
width: 'var(--cfm-header-logo-width, 120px)',
|
|
47
|
+
height: 'var(--cfm-header-logo-height, 120px)',
|
|
48
|
+
padding: 'var(--cfm-header-logo-padding, 10px)',
|
|
49
|
+
background: 'var(--cfm-primary)',
|
|
50
|
+
}}
|
|
51
|
+
/>
|
|
52
|
+
<span
|
|
53
|
+
data-cfm-logo-text
|
|
54
|
+
className="font-semibold"
|
|
55
|
+
style={{
|
|
56
|
+
color: 'var(--cfm-header-company-color, var(--cfm-text))',
|
|
57
|
+
fontSize: 'var(--cfm-header-company-size, 18px)',
|
|
58
|
+
fontWeight: 'var(--cfm-header-company-weight, 600)',
|
|
59
|
+
}}
|
|
60
|
+
>
|
|
61
|
+
Company
|
|
62
|
+
</span>
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
<span
|
|
66
|
+
data-cfm-company
|
|
67
|
+
className="font-semibold"
|
|
68
|
+
style={{
|
|
69
|
+
gridColumn: 'var(--cfm-header-company-col, 2)',
|
|
70
|
+
justifySelf: 'var(--cfm-header-company-justify, center)',
|
|
71
|
+
marginTop: 'var(--cfm-header-company-margin-top, 0)',
|
|
72
|
+
marginLeft: 'var(--cfm-header-company-margin-left, 0)',
|
|
73
|
+
marginRight: 'var(--cfm-header-company-margin-right, 0)',
|
|
74
|
+
color: 'var(--cfm-header-company-color, var(--cfm-text))',
|
|
75
|
+
fontSize: 'var(--cfm-header-company-size, 18px)',
|
|
76
|
+
fontWeight: 'var(--cfm-header-company-weight, 600)',
|
|
77
|
+
}}
|
|
78
|
+
>
|
|
79
|
+
Company Name
|
|
80
|
+
</span>
|
|
81
|
+
</div>
|
|
82
|
+
</header>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import type { SurveyLanguage } from '@explorer02/cfm-survey-sdk';
|
|
4
|
+
|
|
5
|
+
type LanguageSelectorProps = {
|
|
6
|
+
languages: SurveyLanguage[];
|
|
7
|
+
selectedLanguage: string;
|
|
8
|
+
onChange: (language: string) => void;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/** Wizard toggle showLanguageSwitcher targets [data-cfm-language] */
|
|
12
|
+
export default function LanguageSelector({
|
|
13
|
+
languages,
|
|
14
|
+
selectedLanguage,
|
|
15
|
+
onChange,
|
|
16
|
+
}: LanguageSelectorProps) {
|
|
17
|
+
if (languages.length <= 1) return null;
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<div data-cfm-language className="mb-6 flex justify-end">
|
|
21
|
+
<select
|
|
22
|
+
value={selectedLanguage}
|
|
23
|
+
onChange={(e) => onChange(e.target.value)}
|
|
24
|
+
className="rounded border px-3 py-1.5 text-sm outline-none"
|
|
25
|
+
style={{
|
|
26
|
+
background: 'var(--cfm-language-bg, #fff)',
|
|
27
|
+
color: 'var(--cfm-language-text, #2563eb)',
|
|
28
|
+
borderColor: 'var(--cfm-language-border, #2563eb)',
|
|
29
|
+
borderRadius: 'var(--cfm-border-radius, 8px)',
|
|
30
|
+
}}
|
|
31
|
+
>
|
|
32
|
+
{languages.map((lang) => (
|
|
33
|
+
<option key={lang.code} value={lang.code}>
|
|
34
|
+
{lang.label}
|
|
35
|
+
</option>
|
|
36
|
+
))}
|
|
37
|
+
</select>
|
|
38
|
+
</div>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useEffect } from 'react';
|
|
4
|
+
import { initPreviewBridge } from '@/lib/previewBridge';
|
|
5
|
+
|
|
6
|
+
/** Mount once in root layout — enables localhost dev preview with npx cfm-sdk customize */
|
|
7
|
+
export function PreviewBridgeInit() {
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
const cleanup = initPreviewBridge();
|
|
10
|
+
return cleanup;
|
|
11
|
+
}, []);
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wizard-ready ProgressBar — copy to src/components/ProgressBar.tsx
|
|
3
|
+
* Required: data-cfm-progress + class cfm-progress (separate from header toggle)
|
|
4
|
+
*/
|
|
5
|
+
'use client';
|
|
6
|
+
|
|
7
|
+
type ProgressBarProps = {
|
|
8
|
+
progressPercentage: number;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export default function ProgressBar({ progressPercentage }: ProgressBarProps) {
|
|
12
|
+
const width = Math.min(100, Math.max(0, progressPercentage));
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<div data-cfm-progress className="cfm-progress w-full">
|
|
16
|
+
<div
|
|
17
|
+
className="w-full overflow-hidden rounded-full"
|
|
18
|
+
style={{
|
|
19
|
+
height: 'var(--cfm-progress-height, 12px)',
|
|
20
|
+
background: 'var(--cfm-progress-track, #e5e7eb)',
|
|
21
|
+
}}
|
|
22
|
+
>
|
|
23
|
+
<div
|
|
24
|
+
className="h-full rounded-full transition-all duration-300 ease-out"
|
|
25
|
+
style={{
|
|
26
|
+
width: `${width}%`,
|
|
27
|
+
background: 'var(--cfm-progress-fill, var(--cfm-primary))',
|
|
28
|
+
}}
|
|
29
|
+
/>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
);
|
|
33
|
+
}
|