@baravak/risloo-profile-cli 4.70.0 → 4.77.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.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: create-profile
3
- description: Build a new psychological profile (نیم‌رخ) or a new page/chart of one — or edit an existing profile's chart — from a Figma design into a JS controller (src/samples/<NAME>.js) + Handlebars SVG template (views/profiles/samples/<NAME>*.hbs). Use when the user wants to create/extend/edit a profile, references Figma SVG/HTML exports (x/<NAME>.svg, x/<NAME>.html) for a <NAME>, or asks to wire a `score` dataset into a chart. Read AGENTS.md alongside this skill it holds the conventions, helper reference, and the Gotchas this skill points to.
3
+ description: Build a new psychological profile (نیم‌رخ) or a new page/chart of one — or edit an existing profile's chart — from a Figma design into a sample JS controller and Handlebars SVG template. Use when the user wants to create, extend, or edit a profile; provides Figma SVG/HTML exports for a sample; or asks to wire a `score` dataset into a chart. Read AGENTS.md alongside this skill for project conventions, helper references, and reusable SVG guidance.
4
4
  ---
5
5
 
6
6
  # create-profile (pointer)
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: create-profile
3
- description: Build a new psychological profile (نیم‌رخ) or a new page/chart of one — or edit an existing profile's chart — from a Figma design into a JS controller (src/samples/<NAME>.js) + Handlebars SVG template (views/profiles/samples/<NAME>*.hbs). Use when the user wants to create/extend/edit a profile, references Figma SVG/HTML exports (x/<NAME>.svg, x/<NAME>.html) for a <NAME>, or asks to wire a `score` dataset into a chart. Read AGENTS.md alongside this skill it holds the conventions, helper reference, and the Gotchas this skill points to.
3
+ description: Build a new psychological profile (نیم‌رخ) or a new page/chart of one — or edit an existing profile's chart — from a Figma design into a sample JS controller and Handlebars SVG template. Use when the user wants to create, extend, or edit a profile; provides Figma SVG, PNG, HTML, or handoff exports for a sample; or asks to wire a `score` dataset into a chart. Read AGENTS.md alongside this skill for project conventions, helper references, and reusable SVG guidance.
4
4
  ---
5
5
 
6
6
  # Create / edit a profile (نیم‌رخ)
@@ -11,70 +11,193 @@ This project renders psychological-test results into SVG/PNG profiles. A profile
11
11
 
12
12
  You draw only the Figma **Chart** layer (the raw chart). The engine injects the header, sidebar and footer via the `{{#> layout}}` wrapper. **Never put header/sidebar/footer in the HBS.** The CLI emits a `raw` variant (chart only) and a full with-sidebar variant. (AGENTS.md › _What the HBS Draws_.)
13
13
 
14
- ## 1. Gather inputs ask the user for these 6 files (as prompts)
14
+ ## 1. Gather inputs and assign authority
15
15
 
16
- 1. **Scoring source** — the engine that computes the `score` object. Its **language varies (PHP, Python, or Node.js)** and it usually lives in a sibling repo (e.g. for NEO: `../risloo-docker/services/risloo/app/Score<NAME>.php` + trait). Don't assume the language/path — ask. It tells you how each `score` key is computed and what flags mean.
16
+ Ask for the relevant inputs:
17
+
18
+ 1. **Scoring source** — the engine that computes the `score` object. Its **language varies (PHP, Python, or Node.js)** and it usually lives in a sibling repo or service. Don't assume the language/path — ask. It tells you how each `score` key is computed and what flags mean.
17
19
  2. **Scoring documentation** — the validity rules, thresholds, and level/norm descriptions (a file or pasted text).
18
- 3. **Profile SVG** — `x/<NAME>.svg`. The **source of truth**: which elements exist, exact coordinates, and styling (e.g. a line that looks solid in HTML is `stroke-dasharray` dashed here).
19
- 4. **Profile HTML** — `x/<NAME>.html`. A **helper** only: SVG text is outlined paths, so use the HTML to read the text content and learn **where each label goes (and where it doesn't)**, plus colors/gradients. If the HTML has an element the SVG lacks, it's a disabled Figma layer — drop it (AGENTS.md › Gotchas).
20
+ 3. **Profile SVG** — `x/<NAME>.svg`.
21
+ 4. **Profile HTML**, when available and authorized — `x/<NAME>.html`.
20
22
  5. **Profile JSON** — `src/publish/json/profiles/<NAME>.json` (input dataset; its `score` object drives everything).
21
- 6. **Reference PNG** — the designer's rendered export of the **Chart** layer (same variant as the CLI `raw` output, *not* the full sidebar page). This is the validation target for step 7: at the end you render your own `raw` PNG and visually match it against this one. Note which demo data it was rendered with (usually the values in the HTML) so the JSON reproduces the same bar positions.
23
+ 6. **Reference PNG** — the designer's rendered export of the **Chart** layer (same variant as the CLI `raw` output, *not* the full sidebar page).
24
+
25
+ Then **ask for the designer's Figma comments** — thresholds, coefficients, conditional behavior, spacing rules, and other implementation notes. The Figma MCP / SVG / HTML do **not** contain comments, and these specs usually live only there. Don't start coding until you have them.
26
+
27
+ Do not select one design artifact and ignore the others. Establish an authority table for the current task:
28
+
29
+ - **PNG** — final visual acceptance target, including visible orientation, wrapping, cropping, alignment, and composition.
30
+ - **SVG** — exact vector geometry, coordinates, colors, gradients, filters, clipping, strokes, radii, and icon paths.
31
+ - **HTML** — optional text-content and semantic-order aid only when the user authorizes its use.
32
+ - **Designer notes** — conditional behavior, thresholds, spacing rules, exceptions, and presentation states.
33
+ - **Scoring source** — score keys, valid raw values, report states, thresholds, sorting, and correction logic.
34
+
35
+ If two authorized sources disagree, stop and surface the conflict. Do not silently choose one. Respect task-specific source restrictions; if the user excludes HTML or declares PNG authoritative for appearance, record and follow that decision.
36
+
37
+ ## 2. Select reusable patterns
38
+
39
+ Search the repository for existing implementations that match the required chart structure, such as bar orientation, axis mapping, repeated rows, conditional panels, pagination, or RTL label layout.
40
+
41
+ Choose reusable patterns by structural similarity, not by a permanently designated reference profile. More than one existing implementation may be relevant.
22
42
 
23
- Then **ask for the designer's Figma comments** — thresholds, coefficients, the inside/outside text threshold, validity logic. The Figma MCP / SVG / HTML do **not** contain comments, and these specs usually live only there. Don't start coding until you have them.
43
+ Before reusing code:
24
44
 
25
- If the SVG and HTML disagree (an element present in one, absent in the other, or a different layout), surface it and ask which is authoritative before building.
45
+ 1. Identify which behavior is genuinely shared.
46
+ 2. List the design-specific differences.
47
+ 3. Decide whether reuse belongs in a partial, controller utility, configuration object, or remains profile-local.
48
+ 4. Keep profile-specific formulas, text, thresholds, dimensions, and palettes outside shared components.
26
49
 
27
- ## 2. Pick a base profile
50
+ Read every selected controller and template end to end before copying or extracting a pattern.
28
51
 
29
- Find the closest existing profile and copy its idioms rather than writing from scratch:
30
- - Horizontal bars + validity boxes → `src/samples/FRHPT93.js` + `views/profiles/samples/FRHPT93_2.hbs`.
31
- - A simple 5-row bar chart → `src/samples/BSCT93.js`.
32
- - The most recent full example built with this skill → `src/samples/NEO93.js` + `views/profiles/samples/NEO93_1.hbs`.
52
+ ## 3. Inventory the design and variations
33
53
 
34
- Read its JS + HBS end to end.
54
+ Before implementation, create an inventory of:
35
55
 
36
- ## 3. Geometry (from the SVG)
56
+ - pages;
57
+ - presentation variants;
58
+ - data or scoring variants;
59
+ - repeated component types;
60
+ - optional components;
61
+ - variable-length content;
62
+ - numeric rendering thresholds; and
63
+ - every existing consumer of a partial that will be changed.
37
64
 
38
- - The Figma **Chart** layer = the SVG `viewBox` (e.g. `0 0 800 674`).
39
- - `dimensions = Chart + 2·padding`; derive `padding` from `(Main − Chart)/2` using the HTML offset (e.g. content at `left:72 top:40` inside a `943×754` frame → `{x:71.5, y:40}`).
40
- - Draw all content inside `<g transform="translate(padding.x, padding.y)">`.
65
+ For each variation, record whether it changes data, geometry, text, palette, visibility, or only title metadata.
66
+
67
+ Use this inventory to define the controller context and the validation matrix. Do not encode assumptions from another profile.
68
+
69
+ Before writing controller or template code, freeze a per-page rendering contract:
70
+
71
+ | Field | Required value |
72
+ |---|---|
73
+ | Final Chart size | Exact width × height of the delivered raw chart |
74
+ | Logical design size | Upright working-space width × height |
75
+ | Orientation | Normal, 90° clockwise, 90° counter-clockwise, or 180° |
76
+ | Production transform | The single exact transform applied to the logical design |
77
+ | Layout-owned padding | Padding applied by the rendering layout |
78
+ | Chart-owned offset | An offset that visibly exists inside the Chart layer |
79
+ | Overflow policy | Content must fit, clip, or intentionally overflow |
80
+ | Axis mapping | Linear or piecewise score-to-position mapping |
81
+
82
+ Do not begin detailed drawing while any value in this contract is inferred or provisional. Treat every page independently; never assume that pages in one profile have the same dimensions, padding, orientation, or coordinate system.
83
+
84
+ ## 4. Derive geometry from the SVG
85
+
86
+ - The Figma **Chart** layer = the SVG `viewBox` (e.g. `0 0 800 674`) and defines the HBS coordinate space.
87
+ - Measure padding independently for each page. `dimensions = Chart + 2·padding`, but padding is layout metadata; do not apply it again inside the HBS when the layout already owns it.
88
+ - Never introduce a conventional `translate(20,20)`, arbitrary centering transform, or scale. Add an inner transform only when it visibly belongs to the Chart layer and is measured from an authorized source.
89
+ - If most elements share one rotation, author the page in an upright logical coordinate system and rotate one outer `<g>` into the final Chart coordinate system. Keep ordinary text, bars, cards, icons, multiline labels, and alignment components unrotated inside that logical space. Use local rotation only for genuine exceptions.
90
+ - For a final `W × H` chart rendered by rotating an upright `H × W` design clockwise, use the exact equivalent of `<g transform="translate(W 0) rotate(90)">`. Never use scaling to make a rotated design fit.
41
91
  - **Transcribe verbatim**, in the SVG's element order, the static parts: gridlines (`line`), tracks/boxes (`rect`), `linearGradient`, `filter`, icon `path`s. Draw order = z-order — keep it (e.g. a shadowed baseline drawn *after* the bars sits in front).
42
92
  - **Reconstruct** every text run as a real `<text>` at the SVG's x/y — Figma exports text as outlined `<path>`, never `<text>` (AGENTS.md › Gotchas).
43
- - Check the **axis**: if gridline positions look "irregular", they may be a zoomed range. Derive the linear map `x = a·pct + b` from two `(label, x)` pairs and draw the bars on that scale (AGENTS.md Gotchas Custom/zoomed axis).
44
- - **Alignment is the whole job.** This is pixel-precise infographic work — measure exact x/y (and text glyph bounds) from the SVG; never estimate. Get every label's horizontal alignment (left/center/right via `text-anchor`, RTL-aware) and vertical alignment (row mid-line, shared baseline) right. If the chart sits in an offset frame, transcribe its `translate` exactly. See AGENTS.md › Gotchas › _Alignment is the whole job_.
93
+ - Check the **axis**: if gridline positions look irregular, derive either the linear map or every piecewise segment from labeled score/position pairs. A score exactly on a breakpoint must land exactly on its gridline.
94
+ - **Alignment is the whole job.** This is pixel-precise infographic work — measure exact x/y from the SVG; never estimate. Determine whether each spacing specification refers to component bounds, a text layout box, glyph bounds, a baseline, or visible pixels. Get every label's horizontal alignment (`text-anchor`, RTL-aware) and vertical alignment (row mid-line, shared baseline) right. If the chart sits in an offset frame, transcribe its `translate` exactly. See AGENTS.md › _Alignment and text measurement_.
95
+
96
+ Before drawing repeated components, build a geometry and style ledger containing:
45
97
 
46
- ## 4. Wire the data
98
+ - bar origin, direction, width, height, and rounded corners;
99
+ - gridline and breakpoint coordinates;
100
+ - invisible text-box dimensions;
101
+ - code-box, dash, and title gaps;
102
+ - text alignment and writing direction;
103
+ - multiline block center;
104
+ - inside/outside score threshold and gap;
105
+ - exact palette tokens for every component state;
106
+ - font class for Persian text, English codes, and numeric values; and
107
+ - icon source path and dimensions.
108
+
109
+ Identify text alignment families before assigning individual coordinates. Record whether each text or number belongs to a shared row, column, baseline, or guide; which edge or center aligns to that guide; its layout-box dimensions; `text-anchor`; writing direction; and behavior after page rotation. Derive family members from the shared guide instead of nudging them independently. Verify the shortest, longest, and an intermediate member.
110
+
111
+ ## 5. Wire the data
47
112
 
48
113
  - In the controller, define `labels` so each entry's `eng` matches a `score` key; `Object.values(this.labels)` feeds `Dataset._extractData`, producing `dataset.score = [{ label, mark }]`.
49
114
  - In `_calcContext()`, read **every** mark with `?? 0` (AGENTS.md › `?? 0` rule) — `_extractData` turns a `0` into `undefined`. Compute per-item geometry (bar width / percentage, text x + anchor) and any validity indicators.
50
115
  - Confirm ambiguous encodings (validity flags `1`=valid vs invalid, reversed answer scales, norm-based levels) against the **scoring source** (PHP/Python/Node.js) — never guess.
51
116
 
52
- ## 5. Write the controller + template
117
+ ## 6. Write the controller + template
53
118
 
54
119
  - **Controller**: `labels`; `profileSpec` (`sample`, `profile.dimensions`/`padding`, `labels: Object.values(this.labels)`); `constructor` calling `this._init(...)`; `_calcContext()` returning one context object per page.
55
- - **Template**: `{{#> layout}}` → `<defs>` (gradients/clips/filters) → `<g transform="translate(padding.x, padding.y)">` → static structure + `{{#each items}}` rows + conditional boxes → `{{/layout}}`.
120
+ - **Template**: `{{#> layout}}` → `<defs>` (gradients/clips/filters) → optional measured global-orientation or Chart-owned transform → static structure + `{{#each items}}` rows + conditional boxes → `{{/layout}}`.
56
121
  - Use the helpers from AGENTS.md › _Helper Reference_ (`bar`, `math`, `boolean`, `ternary`, `object`, `toRad`). Apply the gotcha checklist below.
57
122
 
58
- ## 6. Multi-page
123
+ When extracting a partial:
124
+
125
+ - define a small explicit input contract;
126
+ - pass semantic colors and geometry anchors;
127
+ - derive dependent coordinates from those anchors;
128
+ - keep scoring and profile-specific decisions in the controller;
129
+ - namespace or parameterize `<defs>` IDs; and
130
+ - render every existing consumer after changing the partial.
131
+
132
+ Do not create a partial solely because markup looks similar. Extract it only when the components share the same behavioral and geometric contract.
133
+
134
+ Implement through three visual gates:
135
+
136
+ 1. **Frame gate** — render only the page frame, major groups, axes, baselines, and representative tracks. Confirm dimensions, origin, orientation, and the global transform.
137
+ 2. **Component gate** — complete one representative bar, one label cluster, one card, one multiline label, and one conditional state. Compare them against the PNG and exact SVG coordinates.
138
+ 3. **Repetition gate** — expand verified components through loops and partials.
139
+
140
+ Do not build the whole page before validating the frame and one representative instance of each component contract.
141
+
142
+ ## 7. Multi-page and variants
59
143
 
60
144
  - Set `static pages = N`; build `<NAME>_1.hbs … <NAME>_N.hbs`. Create blank `{{#> layout}} … {{/layout}}` placeholders for pages not built yet — **a missing template file errors the whole run**.
61
- - Make `profile.dimensions` / `profile.padding` arrays (indexed `page-1` by `layout.hbs`); give each page a `titleAppend`.
145
+ - Make `profile.dimensions` / `profile.padding` arrays (indexed `page-1` by `layout.hbs`).
146
+ - Treat pages and presentation variants as independent output dimensions. Enumerate the supported combinations before implementation.
147
+ - Give each page context the required `titleAppend`. Build it from semantic page and variant metadata rather than template filenames.
148
+ - Keep scoring and geometry identical across presentation variants unless the specification explicitly requires a difference.
62
149
 
63
- ## 7. Verify
150
+ ## 8. Verify
64
151
 
65
152
  - Render: `./bin/risloo.js E <NAME> -d ./src/publish/json/profiles/<NAME>.json -a ./temp`
66
- - Open `temp/<NAME>.raw.png` (chart only) and `temp/<NAME>.png` (with sidebar); eyeball against `x/<NAME>.svg` / `x/<NAME>.html`.
67
- - Test edge & validity states with **scratch JSONs** that modify the `score` object (flip a `voption_*`/validity flag, push a value to 0% or 100%) and render with `-d <scratch>.json`. Clean up scratch files afterward.
68
- - **Close with a PNG visual match (the final gate).** Render with the **same data the reference PNG used** (the demo values from the HTML) so bars land in the same place, then **view both** `temp/<NAME>.raw.png` and the reference PNG (Read them) and compare region by region. Your eyes catch structural/color/placement/RTL errors but are **not a pixel ruler** — when your eye flags a spot, pull the exact coordinate from the **SVG** and fix from that number, then re-render. Iterate render → view → fix until the two match. (AGENTS.md › Gotchas › _Close with a PNG visual match_.)
153
+ - Build scratch JSONs for the verification matrix, render them with `-d <scratch>.json`, and clean them up afterward.
154
+
155
+ Derive the verification matrix from the profile's own variability axes:
156
+
157
+ 1. Render every page.
158
+ 2. Render every supported presentation variant.
159
+ 3. Exercise each independent conditional component in visible and hidden states.
160
+ 4. Exercise supported content cardinalities where layout depends on item count.
161
+ 5. For every rendering threshold, test the nearest valid values below, at, and above it.
162
+ 6. For every piecewise axis, test every breakpoint and the nearest valid value on both sides.
163
+ 7. Cover minimum, maximum, missing, and clamped values where applicable.
164
+ 8. Render both raw and full output when injected layout or title metadata is involved.
165
+ 9. After changing a shared component, render all its consumers.
166
+
167
+ Build a coverage matrix, not an automatic Cartesian product. One fixture may cover several independent states. Use a full Cartesian matrix only when the states interact visually or geometrically; otherwise use focused or pairwise cases that still exercise every behavior. Name each fixture by the states it exercises, and use scoring-valid raw values from the scoring source. If a fixture is intentionally presentation-only and scoring-inconsistent, label it explicitly.
168
+
169
+ Verification has three separate gates:
170
+
171
+ 1. **Semantic validation**
172
+ - Confirm expected texts and title suffixes exist.
173
+ - Confirm no `undefined`, `NaN`, invalid dimensions, or broken references exist.
174
+ - Confirm referenced gradients, clips, masks, and filters resolve.
175
+ 2. **Structural SVG validation**
176
+ - Confirm coordinates, transforms, clipping, filter bounds, radii, strokes, and paint order match the authoritative SVG.
177
+ 3. **Visual validation**
178
+ - Compare raw output with the raw design reference using identical data.
179
+ - Inspect every page and variant.
180
+ - Use the PNG to locate discrepancies, then use SVG measurements to determine the correction.
181
+
182
+ Passing one gate does not replace the others. Do not claim a visual issue is fixed from code inspection or a successful render command alone. Close by iterating render → open the actual PNG → compare → measure → fix until the raw output matches the reference, then inspect the full output for injected layout and title metadata. If local coordinate patches accumulate, stop and re-establish the coordinate model instead of continuing to nudge individual elements.
69
183
 
70
184
  ## Gotcha checklist (detail in AGENTS.md › Gotchas)
71
185
 
72
- - [ ] RTL `text-anchor`: right-align Persian with `start`; inside-bar `%` `start`, outside `end`.
186
+ - [ ] Globally rotated pages authored upright and transformed by one exact outer group; no per-element rotation unless genuinely exceptional.
187
+ - [ ] No generic padding translation or scale inside the HBS.
188
+ - [ ] RTL/LTR `direction` and `text-anchor` derived together from the intended visual edge and growth direction.
73
189
  - [ ] `?? 0` on every mark read in `_calcContext`.
74
190
  - [ ] Text reconstructed as `<text>`, not transcribed glyph paths.
75
- - [ ] Western digits in templates (the `DanaFaNum` font shapes them to Persian).
191
+ - [ ] English font class used only for semantic Latin identifiers; score and report numbers retain the intended numeric font.
192
+ - [ ] Mixed-direction tokens such as percentages visually rechecked after changing spacing or direction.
76
193
  - [ ] Elements emitted in Figma-SVG order (draw order = z-order).
77
- - [ ] Bars drawn on the same scale as the (possibly zoomed) axis labels.
78
- - [ ] Validity/alert boxes hidden unless ≥1 indicator; separator dots via `{{#unless last}}`.
194
+ - [ ] Bars drawn on the same linear or piecewise scale as the axis labels, with exact breakpoint tests.
195
+ - [ ] Text families derived from shared rows, columns, baselines, or guides and tested with different content lengths.
196
+ - [ ] Shared-partial geometry derived from explicit anchors; semantic variant inputs passed from the controller.
197
+ - [ ] Conditional components follow the design's reserve/collapse/reflow behavior for every independent condition.
198
+ - [ ] `<defs>` geometry, filter bounds and primitives, clips, masks, borders, radii, shadows, and paint order verified exactly.
199
+ - [ ] Numeric rendering boundaries tested below, at, and above the boundary with valid domain values.
200
+ - [ ] Every page, presentation variant, and affected shared-partial consumer rendered.
201
+ - [ ] `titleAppend` verified in full output whenever page or variant metadata requires it.
79
202
  - [ ] No header/sidebar/footer in the HBS — only the Chart layer.
80
- - [ ] Closed with a PNG visual match against the reference (raw-vs-raw, same data): viewed both images, iterated render → view → fix until aligned.
203
+ - [ ] Closed with semantic, structural SVG, and visual checks against the reference using identical data.
package/AGENTS.md CHANGED
@@ -32,7 +32,7 @@ risloo-extractor-app/
32
32
  │ │ └── utilities/ # BaseOps, Benchmarker, Response, Errors, Status codes
33
33
  │ ├── Profile.js # Base Profile class (math utilities)
34
34
  │ ├── Gift.js # Gift card class
35
- │ ├── samples/ # JS controller files — one per profile (e.g. BSCT93.js)
35
+ │ ├── samples/ # JS controller files — one per profile
36
36
  │ ├── handlebars/
37
37
  │ │ ├── init.js # Handlebars initialization
38
38
  │ │ ├── helpers.js # Entry point for all helpers
@@ -42,7 +42,7 @@ risloo-extractor-app/
42
42
  │ ├── helpers/ # Math helpers (angleABS, polarXY, gauge, polygonXY)
43
43
  │ ├── qrcode/ # QR code generation & rendering
44
44
  │ └── publish/
45
- │ ├── json/profiles/ # Template JSON per sample (e.g. BSCT93.json)
45
+ │ ├── json/profiles/ # Template JSON per sample
46
46
  │ ├── json/gift/ # Gift template data
47
47
  │ ├── test.js # Auto-test all samples
48
48
  │ └── bot.js # Post-publish automation
@@ -74,15 +74,25 @@ npm test # → node ./src/publish/test.js
74
74
 
75
75
  ## Profile Development Workflow (Figma → Code)
76
76
 
77
- ### Design Source
77
+ ### Design Sources and Authority
78
78
 
79
79
  Each profile is designed in Figma. Per task, one or both of the following may be provided:
80
80
 
81
81
  - **Figma design file** — connect via Figma MCP to read layer dimensions and structure. The relevant layer is named **Chart**.
82
- - **Figma handoff file** — contains additional implementation notes and specs. May state that this profile is similar to an existing one (e.g. "similar to BSCT93 with these changes"). In that case: find the referenced profile's JS + HBS files, use them as the base, and apply only the described differences.
82
+ - **Figma handoff file** — contains additional implementation notes and specs. It may state that this profile is similar to an existing one. In that case, inspect the referenced profile's JS + HBS files, identify the genuinely shared behavior, and apply only the described differences.
83
83
 
84
84
  > **Important:** The Figma MCP reads text layers only — it does NOT read Figma comments. Designers often leave critical specs (thresholds, coefficients, pixel values) as Figma comments. Always ask the user to share designer comments before finalizing the plan.
85
85
 
86
+ Assign authority by source role instead of selecting one artifact and ignoring the others:
87
+
88
+ - **PNG** — final visual acceptance target, including visible orientation, wrapping, cropping, alignment, and composition.
89
+ - **SVG** — exact vector geometry, coordinates, colors, gradients, filters, clipping, strokes, radii, and icon paths.
90
+ - **HTML** — optional text-content and semantic-order aid only when the user authorizes its use.
91
+ - **Designer notes** — conditional behavior, thresholds, spacing rules, exceptions, and presentation states.
92
+ - **Scoring source** — score keys, valid raw values, report states, thresholds, sorting, and correction logic.
93
+
94
+ Respect task-specific source restrictions. If two authorized sources disagree, stop and surface the conflict instead of silently choosing one.
95
+
86
96
  ### What the HBS Draws
87
97
 
88
98
  The HBS file draws **only the Chart layer** — i.e. the **raw** chart. The engine produces the final page by automatically injecting the header (test name, dates, client info), the sidebar (logo, room info, prerequisites) and the footer/closing info via the `{{#> layout}}` wrapper. Never include any of these in the HBS. (The CLI emits both a `raw` variant — chart only, e.g. `<NAME>.raw.svg/png` — and the full with-sidebar variant `<NAME>.svg/png`.)
@@ -97,18 +107,47 @@ Since this is SVG, coordinate origin matters for correctness across **all** inpu
97
107
  - Vertical total bar fills bottom → up via the transform trick: `translate(0, barHeight - barHeight * p)`
98
108
  - All positions must remain stable for edge-case inputs: 0% score, 100% score, missing data
99
109
 
100
- ### Inside/Outside Bar Text
110
+ ### Global Page Orientation
111
+
112
+ Global rotation is a coordinate-system decision. When most elements on a page share one rotation, author the page in an upright logical coordinate system and rotate one outer group into the final Chart space.
113
+
114
+ For a final `W × H` chart rendered by rotating an upright `H × W` design clockwise:
115
+
116
+ ```svg
117
+ <g transform="translate(W 0) rotate(90)">
118
+ <!-- author regular geometry here -->
119
+ </g>
120
+ ```
121
+
122
+ The coordinate mapping is:
101
123
 
102
- When rendering percentage text on a bar, check if the bar is wide enough to contain the text. The threshold is specified by the designer per profile (in Figma comments):
124
+ ```text
125
+ finalX = W - logicalY
126
+ finalY = logicalX
127
+ ```
128
+
129
+ and its inverse is:
103
130
 
104
- ```hbs
105
- {{#if (boolean factor.percentage '<=' THRESHOLD)}}
106
- <text x="{{math (math BAR_WIDTH '*' factor.p) '+' 6}}" ...>{{factor.percentage}} ٪</text>
107
- {{else}}
108
- <text x="{{math (math BAR_WIDTH '*' factor.p) '-' 4}}" ...>{{factor.percentage}} ٪</text>
109
- {{/if}}
131
+ ```text
132
+ logicalX = finalY
133
+ logicalY = W - finalX
110
134
  ```
111
135
 
136
+ Use this conversion systematically when measurements come from an already rotated SVG. Do not mentally swap `x` and `y` for individual nodes. Keep ordinary text, bars, cards, icons, multiline labels, and alignment components unrotated inside the logical space; apply local rotation only to genuine exceptions. Never use scaling to make a rotated design fit.
137
+
138
+ ### Bar Geometry and Inside/Outside Text
139
+
140
+ Use `{{bar ...}}` when only the terminal corners are rounded. Do not replace a partially rounded bar with `<rect rx="...">`, which rounds all four corners.
141
+
142
+ Treat the bar end as the center of a two-sided safe zone:
143
+
144
+ - **Inside label** — anchor at `barEnd - gap` and let the text grow opposite the fill direction.
145
+ - **Outside label** — anchor at `barEnd + gap` and let the text grow in the fill direction.
146
+
147
+ Choose `text-anchor` from the actual writing direction and coordinate system. Do not preserve an anchor after changing `direction` or moving a label between rotated and unrotated spaces.
148
+
149
+ Inside/outside thresholds are presentation rules and may differ between chart groups. Never reuse one global threshold unless the design explicitly defines one. Verify zero-width, narrow, threshold, maximum, and clamped bars.
150
+
112
151
  ### File Structure per Profile
113
152
 
114
153
  Each profile (نیم‌رخ) consists of three files:
@@ -128,12 +167,9 @@ Multi-page mechanics:
128
167
 
129
168
  ### Dimensions & Padding Convention
130
169
 
131
- The chart drawing must fit within the **Main** layer in Figma.
170
+ The HBS owns exactly the raw Chart coordinate space. Its origin is the Chart layer's `(0, 0)`, and all authored content must remain within the declared Chart width and height unless the design explicitly clips or overflows it.
132
171
 
133
- - Read the **Main** layer dimensions from Figma
134
- - `profile.padding` = 20 units per side
135
- - The `x` and `y` values (drawing area) = Main dimensions minus one padding unit each:
136
- - e.g. Main = 104×255 → `{ x: 84, y: 235 }`
172
+ There is no universal profile padding. Measure padding independently for every page.
137
173
 
138
174
  The `dimensions` property in the JS controller adds padding back on both sides of the **Chart** layer dimensions:
139
175
 
@@ -146,6 +182,20 @@ get dimensions() {
146
182
  },
147
183
  ```
148
184
 
185
+ Padding is layout metadata. Do not apply the same padding again inside the HBS when the layout already owns it.
186
+
187
+ Never introduce a conventional `translate(20,20)`, arbitrary centering transform, or scale. Add an inner translation only when that offset visibly belongs to the Chart layer and is measured from an authorized source.
188
+
189
+ Fix geometry at the layer that owns the error:
190
+
191
+ 1. Chart dimensions
192
+ 2. Global orientation transform
193
+ 3. Major group anchor
194
+ 4. Repeated component geometry
195
+ 5. Individual text placement
196
+
197
+ Do not alter a lower layer to compensate for an error in a higher layer.
198
+
149
199
  ### Data / Labels Convention
150
200
 
151
201
  All data needed for rendering goes in the `labels` property of the JS controller.
@@ -162,7 +212,7 @@ get labels() {
162
212
  }
163
213
  ```
164
214
 
165
- **Where the `score` values come from.** The JSON `score` object fed to a profile is produced by a separate **scoring engine** whose language **varies per test — it may be PHP, Python, or Node.js** — and which usually lives in a sibling repo (e.g. for NEO: `../risloo-docker/services/risloo/app/ScoreNEO93.php` plus a shared `ScoreNEO` trait). **Always ask the user for the actual scoring-source file** rather than assuming the language/path. When a score key's meaning is ambiguous — especially validity flags (is `1` valid or invalid?) — read that source to confirm the encoding instead of guessing; the scoring source also defines the level thresholds (norms) and per-item (reverse) scoring. Each `score` key is matched to a label via `label.eng`.
215
+ **Where the `score` values come from.** The JSON `score` object fed to a profile is produced by a separate **scoring engine** whose language **varies per test — it may be PHP, Python, or Node.js** — and which usually lives in a sibling repo or service. **Always ask the user for the actual scoring-source file** rather than assuming the language/path. When a score key's meaning is ambiguous — especially validity flags (is `1` valid or invalid?) — read that source to confirm the encoding instead of guessing; the scoring source also defines the level thresholds (norms) and per-item (reverse) scoring. Each `score` key is matched to a label via `label.eng`.
166
216
 
167
217
  **The `?? 0` rule.** `Dataset._extractData` (in `src/Profile.js`) maps `mark: score[label.eng] || …`, so a score of `0` (or any falsy value) falls through to `undefined`. Always read marks with `?? 0` in `_calcContext` to avoid `NaN` widths/positions.
168
218
 
@@ -187,18 +237,142 @@ Common helpers used in profile templates (defined under `src/handlebars/helpers/
187
237
 
188
238
  Hard-won knowledge that applies to **every** profile:
189
239
 
190
- - **Alignment is the whole job — measure, never estimate.** These are pixel-precise **infographics** built from SVG, so every element must line up with the design **both horizontally and vertically**. Pull exact coordinates from the SVG (element `x`/`y`; for text-as-paths, the glyph bounding box / the `M…` start) instead of eyeballing. **Horizontal:** decide per label whether it is left-, center-, or right-aligned and set `text-anchor` to match (RTL flips `start`/`end`) — e.g. the right-most axis label's *right edge* sits on the gridline (right-aligned), not centered over it; domain names are right-aligned to a fixed edge with the dash/letter at fixed x's after them. **Vertical:** center row text on the row mid-line with `y=<center> dy=".3em"`, and keep every column of a row on the same baseline. If the chart lives inside an offset frame (NEO93 page 1 = `translate(32, 128)` inside the 800×674 content), transcribe that `translate` exactly, and remember a `filterUnits="userSpaceOnUse"` region is in that same (post-translate) local space. Re-render and eyeball after **every** change.
191
- - **RTL is the base — account for it in EVERY alignment.** The root SVG sets `direction="rtl"` (`views/profiles/layout.hbs`), which flips both horizontal text flow **and** the advance direction of rotated labels. This is the single most common alignment mistake — get it wrong and a label slides *under* a bar or lands a whole run-length away. Two cases:
192
- - **Horizontal `text-anchor`.** Under RTL, `text-anchor="start"` puts the **right** edge at `x` (text grows **left**); `text-anchor="end"` puts the **left** edge at `x` (text grows **right**). So: right-align Persian labels with `start`; a label that must sit to the **right** of an element and grow away from it uses `end`. Inside-bar `%` label → `start`; outside (narrow-bar) label → `end` (mirror `FRHPT93_2.hbs`). BSCT93 vertical total bar: the `%`/ratio labels sit to the **right** of the bar `end`; the `٪۱۰۰` axis label sits to the **left** `start` (using `end` there grows it rightward *under* the bar, which is drawn after and paints over it). If a run of Latin/numbers mis-orders, add `direction="ltr"` on that `<text>`.
193
- - **Rotated (`rotate(-90)`) vertical labels advance DOWN, not up.** Because the base is RTL, a `<text transform="rotate(-90,0,0) translate(tx,ty)">` grows **downward** on screen (its reading run goes top→bottom). So the baseline x = `<group-x> + ty` (glyph bodies extend **left** of it) and the text's **TOP** edge = `<group-y> − tx`, running down for its length. BSCT93 «نمره کل» under group `translate(637,1)` → `ty=22` (baseline x≈659), `tx=-202` (top y≈203, bottom ≈238). Assuming it grows *up* drops it ~a run-length too low.
240
+ ### Alignment and text measurement
241
+
242
+ - **Alignment is the whole job measure, never estimate.** These are pixel-precise **infographics** built from SVG, so every element must line up with the design both horizontally and vertically. Pull exact coordinates from the SVG instead of eyeballing them. Decide per label whether it is left-, center-, or right-aligned, and center row text on the row mid-line with a shared baseline. If the chart lives inside an offset frame, transcribe that `translate` exactly.
243
+ - Identify what a design spacing measurement refers to before implementing it: component bounds, text layout box, glyph bounds, baseline, or visible painted pixels. These are not interchangeable.
244
+ - Measure the two relevant edges from the authoritative SVG. Do not compensate for a spacing error by moving unrelated elements.
245
+ - When several labels share a relationship with a chart origin, derive all their positions from that origin so alternate dimensions remain aligned automatically.
246
+
247
+ Before placing individual `<text>` nodes, identify alignment families: axis values, raw scores, codes, dashes, Persian titles, card values, section headings, and bar labels often share a row, column, baseline, or guide. For each family record:
248
+
249
+ | Field | Meaning |
250
+ |---|---|
251
+ | `axis` | Whether the guide lies on `x` or `y` |
252
+ | `coordinate` | Exact guide coordinate |
253
+ | `alignedPart` | Start edge, end edge, center, or baseline |
254
+ | `layoutBox` | Invisible text-box dimensions |
255
+ | `anchor` | `text-anchor` |
256
+ | `direction` | Actual writing direction |
257
+ | `baseline` | Shared baseline or vertical center |
258
+
259
+ Members of one family do not necessarily use `text-anchor="middle"`. Determine what the design aligns:
260
+
261
+ - If axis numbers end on a gridline, align the text end rather than its center.
262
+ - If codes occupy equal invisible boxes, align the boxes first and align text within each box.
263
+ - If titles begin after a dash, derive their start edge from the shared dash guide.
264
+ - For values of different lengths, align the specified edge or layout box rather than visible glyph centers.
265
+ - For texts in one row, share a baseline or vertical center instead of merely assigning similar `y` values.
266
+
267
+ Identify these families in the upright logical space for globally rotated pages. A logical column may appear as a final horizontal row after rotation. Verify each family with its shortest, longest, and an intermediate member.
268
+
269
+ When a design specifies an invisible code box, treat it as real geometry. Record its origin and dimensions, text alignment, chart-to-box gap, box-to-dash gap, dash-to-title gap, and shared row baseline. Measure gaps from component bounds, not visible glyph edges. Center a multiline label as one block, not as independently positioned lines. Extract a partial when this full contract repeats.
270
+
271
+ ### RTL alignment
272
+
273
+ - The root SVG sets `direction="rtl"` in `views/profiles/layout.hbs`, which affects horizontal text flow and the advance direction of rotated labels.
274
+ - Under RTL, `text-anchor="start"` puts the right edge at `x` and grows text leftward; `text-anchor="end"` puts the left edge at `x` and grows text rightward. Derive `direction` and `text-anchor` together from the intended aligned edge and growth direction; do not reuse an anchor after changing direction.
275
+ - A `<text transform="rotate(-90,0,0) translate(tx,ty)">` advances downward on screen under the inherited RTL direction. Its baseline x is `<group-x> + ty`, and its top edge is `<group-y> − tx`. Measure the intended top and baseline from the SVG rather than assuming the text advances upward.
276
+
277
+ ### Font and bidirectional numeric tokens
278
+
279
+ - Apply the English font class only to semantic Latin identifiers such as codes and abbreviations, including digits embedded in those identifiers.
280
+ - Do not apply the English font class to raw scores, BR values, percentages, axis values, or report numbers unless the design explicitly requests it. Those values should normally remain in the profile's Persian numeric font.
281
+ - Treat mixed-direction tokens such as numbers with percent signs as one visual component. Set `direction`, `unicode-bidi`, anchors, and spacing explicitly; do not rely on source-string order under inherited RTL.
282
+ - After adding whitespace to a mixed-direction token, re-render it. Bidi reordering can change both visual order and anchor behavior.
283
+
284
+ ### Source fidelity and rendering rules
285
+
194
286
  - **`?? 0` on every mark.** See _Data / Labels Convention_ — `_extractData` turns a `0` mark into `undefined`; always guard with `?? 0`.
195
- - **Figma SVG = outlined paths, not text.** A Figma SVG export renders every text run as an outlined `<path>`, never `<text>`. Do **not** transcribe glyph paths. Reconstruct text with real `<text>` elements at the SVG's x/y, and take the actual content/colors from the **HTML** export. Transcribe verbatim only: `rect`, `line`, `linearGradient`, `filter`, and icon `<path>`s.
196
- - **The SVG is the source of truth; the HTML export is only a helper.** Trust the **SVG** for everything authoritative: which elements exist, exact coordinates, and styling — e.g. a divider that looks like a plain solid line in the HTML is actually `stroke-dasharray` *dashed* in the SVG. Use the **HTML** only because (a) SVG text is outlined paths so you can't read it there — the HTML tells you the text content and **where each label goes (and where it doesn't)**, and (b) it's easier to read colors/gradients/structure. If the HTML shows an element the SVG lacks, it's a *disabled Figma layer* (the HTML export still draws disabled layers, with color) — **drop it**. (NEO93 level chips were exactly this: present in HTML, absent in SVG → removed; the level *text* stays, the chip box doesn't.)
287
+ - **Figma SVG = outlined paths, not text.** Do not transcribe glyph paths. Reconstruct text with real `<text>` elements and obtain content from an authorized semantic source such as designer notes, JSON/scoring labels, or HTML when its use is allowed.
288
+ - **Authority is field-specific.** Use the source-role contract above instead of treating one artifact as authoritative for every concern. Inspect every authorized source and surface conflicts.
197
289
  - **Persian digits come from the font.** Emit Western digits in templates (`{{item.mark}}`, `از 192`, `50 ٪`); the `DanaFaNum` font shapes them to Persian. Never hand-convert.
198
- - **Draw order = z-order.** Later elements paint on top. Transcribe in the same order as the Figma SVG (e.g. a shadowed baseline drawn **after** the bars sits in front of them).
199
- - **Custom / zoomed axis.** Gridlines are not always 0–100%. If their positions look "irregular", check for a zoomed range (e.g. NEO93 = 20–80%). Derive the linear map `x = a·pct + b` from two known `(label, x)` pairs and draw the bars on **that** scale, so a bar's end lands exactly on its labeled gridline.
200
- - **Validity / alert-box pattern.** Lay indicators right-to-left: text at `x = ANCHOR − (start + length/2)`, separator dot at `ANCHOR − (start + length + 26)` guarded by `{{#unless last}}`; hide the whole box unless ≥1 indicator is active. Drive it from JS with per-item `{ start, length, last }` (see `FRHPT93.js` / `NEO93.js`).
201
- - **Close with a PNG visual match — your eye finds *where*, the SVG gives *what*.** The final gate before delivery: render the `raw` PNG and, by actually **viewing both images** (Read the two PNGs), compare it against the designer's reference PNG. Match variants — the reference must be a **Chart-layer** export (same as the CLI `raw`), never the full sidebar page. Render with the **same data the reference used** (transcribe the demo values from the HTML into the JSON), or bars land at different positions and every difference is a false alarm. Your vision reliably catches **structural** errors — wrong gradient/color, a label flipped to the wrong side (RTL), a bar whose end misses its gridline, reordered rows, a dropped or extra element — but it is **not a pixel ruler**: never trust it for 1–3px nudges. So when your eye flags a spot, go back to the **SVG**, read the exact coordinate, and fix from that number (eye = *where*, SVG = *what*). Iterate render → view → fix until the two PNGs match; that match is the close of the job. (This complements — never replaces — HTML-vs-output text checking, since outlined/rendered text is too noisy for a visual diff to judge.)
290
+ - **Draw order = z-order.** Later elements paint on top. Preserve the Figma SVG's element order.
291
+
292
+ ### Piecewise score axes
293
+
294
+ Never assume that one coefficient covers a score axis's full domain. Represent the axis as ordered breakpoints or segments. For a segment from score `a` at position `p0` to score `b` at position `p1`:
295
+
296
+ ```js
297
+ position = p0 + ((score - a) * (p1 - p0)) / (b - a);
298
+ ```
299
+
300
+ Select the segment containing the score and clamp only at the domain boundaries. A score exactly equal to a breakpoint must land exactly on that breakpoint's gridline. Verify every breakpoint and the nearest valid value on both sides.
301
+
302
+ ### Exact palettes and source icons
303
+
304
+ - Create semantic palette tokens from exact SVG color values. Every related part of a stateful component—bar, track, label, card body, footer, divider, border, and icon—must derive from the selected theme unless an exception is documented.
305
+ - Do not substitute a visually similar hex value.
306
+ - Use the exact SVG path for design-specific arrows, alerts, and symbols. Do not replace them with Unicode characters, generic icon libraries, or hand-drawn approximations.
307
+
308
+ ### Shared templates and partials
309
+
310
+ - Treat every partial as a parameterized visual component. Do not bake a specific profile's coordinates, palette, labels, thresholds, or variant rules into a shared partial.
311
+ - Express geometry relative to meaningful anchors such as `barX`, `barWidth`, row center, label edge, or component bounds. Avoid unrelated absolute coordinates when the component is shared.
312
+ - Pass visual differences through semantic properties such as `primary`, `accent`, `track`, `border`, `shadow`, and `labelOutside`; do not infer colors from profile names, row indexes, or variant labels.
313
+ - Before changing a shared partial, find every controller and template that consumes it. After the change, render all affected consumers, including at least one unchanged consumer as a regression check.
314
+ - A shared partial must have an explicit input contract: required values, defaults, coordinate system, conditional sections, and ownership of `<defs>` IDs.
315
+
316
+ ### Conditional components
317
+
318
+ - For every conditional component, determine from the design whether hiding it preserves its reserved space, collapses its space, or causes another component to occupy its position.
319
+ - Do not assume independent conditions are mutually exclusive.
320
+ - For components containing a variable number of items, compute item positions and separators from the active items instead of maintaining fixed markup for anticipated combinations.
321
+ - Build validation cases from the component's actual independent conditions and supported item cardinalities. Create a coverage matrix rather than an automatic Cartesian product: one fixture may cover several independent states, and a full product is needed only when states interact visually or geometrically. Name each fixture by the states it exercises.
322
+
323
+ ### SVG effects, clipping, borders, and shadows
324
+
325
+ - Treat `<defs>` as measurable design geometry, not approximate decoration. Reproduce and verify `filterUnits`; filter bounds (`x`, `y`, `width`, `height`); `dx` and `dy`; blur radius; flood color and opacity; primitive order and `in`/`result` wiring; clip/mask geometry; transform coordinate space; and final paint order.
326
+ - Verify both the direction and extent of every shadow against the reference PNG.
327
+ - When a rounded component contains fills or effects, use clipping where required and draw its final border after the clipped content. Confirm that radius, border thickness, corners, and shadow are visually independent.
328
+ - Use unique IDs for gradients, clips, masks, and filters when a partial can appear more than once in one document.
329
+
330
+ ### Boundary-driven rendering tests
331
+
332
+ - Extract every rendering boundary from the design notes, scoring source, and controller logic.
333
+ - For each numeric boundary, render values immediately below, at, and immediately above it, using the nearest valid values for that domain.
334
+ - For every piecewise axis, test each breakpoint and the nearest valid value on both sides.
335
+ - Also cover the domain minimum, maximum, zero when valid, missing input, and out-of-range input if clamping is expected.
336
+ - Test the visual behavior, not only the computed value: bar width, clipping, label placement, anchor, contrast, and overflow.
337
+ - Test fixtures must be derived from the current profile's rules and use scoring-valid raw values. Fixed example values must not become global conventions. Label intentionally presentation-only, scoring-inconsistent fixtures explicitly.
338
+
339
+ ### Visual verification before claiming a fix
340
+
341
+ Do not report a visual issue as fixed based only on code inspection or a successful render command.
342
+
343
+ For every affected page:
344
+
345
+ 1. Render the raw PNG.
346
+ 2. Open and inspect the actual PNG.
347
+ 3. Compare it with the reference at the same orientation.
348
+ 4. Use the PNG to locate the mismatch.
349
+ 5. Return to the SVG or geometry ledger for the exact correction.
350
+ 6. Re-render and inspect again.
351
+
352
+ Validate major geometry before typography:
353
+
354
+ 1. Chart width and height
355
+ 2. Origin and global transform
356
+ 3. Major group extents
357
+ 4. Bar and grid dimensions
358
+ 5. Repeated alignment contracts
359
+ 6. Text baselines and glyph-level adjustments
360
+
361
+ If several local coordinate patches accumulate, stop. Re-establish the coordinate model from the sources instead of continuing to nudge individual elements.
362
+
363
+ ### Pages, variants, and title metadata
364
+
365
+ - Treat pages and presentation variants as independent output dimensions. Enumerate the supported combinations before implementation.
366
+ - Each page context must provide the title suffix required to distinguish that output. Build `titleAppend` from semantic page and variant metadata rather than from template filenames.
367
+ - Validate `titleAppend` in the full SVG/PNG, because it belongs to the injected page header and is not visible in the raw chart.
368
+ - A variant must not change scoring or geometry unless the specification explicitly says so.
369
+
370
+ ### Concurrent-work hygiene
371
+
372
+ - Assume files may change while the task is in progress. Re-read shared files immediately before editing them.
373
+ - Inspect the diff before and after each shared-file modification.
374
+ - Stage explicit intended paths unless the user has expressly requested publishing all workspace changes.
375
+ - Do not overwrite or normalize concurrent changes merely to make the current diff cleaner.
202
376
 
203
377
  ---
204
378
 
@@ -224,7 +398,7 @@ Hard-won knowledge that applies to **every** profile:
224
398
  - `raw` — total/aggregate element
225
399
  - `ticks` — graduation marks on profiles
226
400
  - `s` suffix — denotes arrays (not `Arr`)
227
- - Sample names: uppercase acronym + year (e.g. `BSCT93`, `BEQI93`, `16PF93`)
401
+ - Sample names: uppercase acronym + version/year suffix (for example, `<ACRONYM><SUFFIX>`)
228
402
  - Dataset score structure: `dataset.score = [{ label: { eng, ... }, mark }]`
229
403
 
230
404
  ---
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@baravak/risloo-profile-cli",
3
- "version": "4.70.0",
3
+ "version": "4.77.0",
4
4
  "description": "**Risloo Profile CLI** is a library for creating profiles, reports and sheets for *psychological* samples.",
5
5
  "main": "bin/risloo.js",
6
6
  "publishConfig": {
@@ -3722,7 +3722,7 @@
3722
3722
  {
3723
3723
  "type": "text",
3724
3724
  "label": "marital_status",
3725
- "text": "جنسیت",
3725
+ "text": "وضعیت تأهل",
3726
3726
  "answer": { "type": "select", "options": ["مجرد", "متأهل"] },
3727
3727
  "alias": "marital_status",
3728
3728
  "force": true,
@@ -3720,7 +3720,7 @@
3720
3720
  {
3721
3721
  "type": "text",
3722
3722
  "label": "marital_status",
3723
- "text": "جنسیت",
3723
+ "text": "وضعیت تأهل",
3724
3724
  "answer": { "type": "select", "options": ["مجرد", "متأهل"] },
3725
3725
  "alias": "marital_status",
3726
3726
  "force": true,