Skip to content

Commit 3d2bc21

Browse files
sidprasadclaude
andauthored
feat(directives): attribute/tag adopt the shared textStyle block (size + color) (#496)
* feat(directives): attribute/tag adopt the shared textStyle block (size + color) The `attribute` and `tag` directives previously supported only a flat `textSize` field (added in #491). The shared TextStyle abstraction (src/layout/style/text-style.ts) already documented attribute/tag lines as a consumer, but they were never wired to it. Wire them into the shared `textStyle: { size, color }` block — the same shape edgeStyle/atomStyle use — so each attribute/tag line can set its own color as well as size. `textSize` moves to `textStyle.size` as a clean break (no deprecate-and-warn): textSize only shipped in #491, so there is no back-compat burden. Changes: - layoutspec: AttributeDirective/TagDirective carry `textStyle?: TextStyle`, parsed via the shared parseTextStyle; drop normalizeAttrTextSize. - layoutinstance: the per-attribute channel carries the whole TextStyle; getAttributeTextSize -> getAttributeTextStyle; box sizer reads .size. - interfaces/webcolatranslator: attributeSizes -> attributeTextStyles. - webcola-cnd-graph: attribute/tag tspans get font-size from .size and fill from .color (null = inherit the themed label, so dark mode adapts). - spec-editor registry: attribute/tag expose the shared textStyle group. - docs: YAML_SPECIFICATION.md, site/directives.md, site/yaml-reference.md, and the agent-manifest (1.8.0 -> 1.9.0) document size + color. - tests: new attribute-tag-textstyle suite (parse + end-to-end for both directives, incl. sparse color-only and absent cases) — #491 had none. - bump 3.0.1 -> 3.1.0 (minor: additive optional fields). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test(spec-editor): cover attribute/tag textStyle in the no-code builder The no-code editor (SpecEditor/BuilderView) is driven by the registry, so the prior commit's registry change already wires attribute/tag to the shared textStyle group — FieldRenderer renders `kind: 'group'` generically (same path edgeStyle/atomStyle use) and the legacy NoCodeView shim passes params through untouched. Add explicit coverage + fix stale docs: - Assert attribute/tag expose a `textStyle` group field with children [size, color] in the directive registry. - Ingest + round-trip an attribute textStyle block and a tag textStyle block through the codec (parseYamlToState → serializeStateToYaml). - Fix the registry header shape-manifest comment: attribute/tag now show `textStyle?:{size,color}` (they never listed the field). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 8b6c544 commit 3d2bc21

14 files changed

Lines changed: 327 additions & 113 deletions

docs/YAML_SPECIFICATION.md

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -693,7 +693,9 @@ Converts edge relationships into node attributes (displayed as key-value pairs o
693693
field: <field-name> # Required: Relation to convert to attribute
694694
selector: <unary-selector> # Optional: Filter which source atoms apply
695695
filter: <n-ary-selector> # Optional: Filter which tuples to include
696-
textSize: <size> # Optional: small | normal | large (default: normal)
696+
textStyle: # Optional: style the attribute line (shared block)
697+
size: <small|normal|large> # font size relative to the node label (default: normal)
698+
color: <color> # text color (any CSS color)
697699
```
698700

699701
**Fields:**
@@ -703,13 +705,14 @@ Converts edge relationships into node attributes (displayed as key-value pairs o
703705
| `field` | ✅ Yes | string | Name of the relation to display as attribute |
704706
| `selector` | ❌ No | string | Unary selector to filter source atoms |
705707
| `filter` | ❌ No | string | N-ary selector to filter specific tuples |
706-
| `textSize` | ❌ No | `small` \| `normal` \| `large` | Size of this attribute's text, relative to the node label. Default `normal`. |
708+
| `textStyle.size` | ❌ No | `small` \| `normal` \| `large` | Size of this attribute's text, relative to the node label. Default `normal`. |
709+
| `textStyle.color` | ❌ No | string | Text color of this attribute's line (any CSS color). Default inherits the node label color. |
707710

708711
**Behavior:**
709712
- Removes the edge from the graph
710713
- Displays the target value as an attribute on the source node
711714
- Multiple targets become a list
712-
- `textSize` controls the line's font size: `large` renders **bigger** than the node's label, `normal` is the default (smaller than the label), and `small` is smaller still. The node box grows/shrinks to fit.
715+
- `textStyle` is the same shared block edges and atoms use. `size` controls the line's font size: `large` renders **bigger** than the node's label, `normal` is the default (smaller than the label), and `small` is smaller still — the node box grows/shrinks to fit. `color` sets the line's text color (unset = inherit the node's label color, so dark mode still adapts).
713716

714717
**Examples:**
715718

@@ -728,10 +731,10 @@ Converts edge relationships into node attributes (displayed as key-value pairs o
728731
field: status
729732
filter: 'status & (univ -> Active)'
730733
731-
# Emphasize a key attribute — rendered larger than the node label
734+
# Emphasize a key attribute — larger than the node label, in red
732735
- attribute:
733736
field: balance
734-
textSize: large
737+
textStyle: { size: large, color: "#c0392b" }
735738
```
736739

737740
---
@@ -745,7 +748,9 @@ Adds computed attributes to nodes based on selector evaluation. Unlike `attribut
745748
toTag: <unary-selector> # Required: Selector for atoms to receive the tag
746749
name: <attribute-name> # Required: Name of the attribute to display
747750
value: <n-ary-selector> # Required: Selector whose result becomes the value
748-
textSize: <size> # Optional: small | normal | large (default: normal)
751+
textStyle: # Optional: style the tag line (shared block)
752+
size: <small|normal|large> # font size relative to the node label (default: normal)
753+
color: <color> # text color (any CSS color)
749754
```
750755

751756
**Fields:**
@@ -755,13 +760,14 @@ Adds computed attributes to nodes based on selector evaluation. Unlike `attribut
755760
| `toTag` | ✅ Yes | string | Unary selector for atoms that receive this tag |
756761
| `name` | ✅ Yes | string | Attribute name to display |
757762
| `value` | ✅ Yes | string | N-ary selector returning the attribute values |
758-
| `textSize` | ❌ No | `small` \| `normal` \| `large` | Size of this tag's text, relative to the node label. Default `normal`. |
763+
| `textStyle.size` | ❌ No | `small` \| `normal` \| `large` | Size of this tag's text, relative to the node label. Default `normal`. |
764+
| `textStyle.color` | ❌ No | string | Text color of this tag's line (any CSS color). Default inherits the node label color. |
759765

760766
**Behavior:**
761767
- Does NOT remove edges (unlike `attribute`)
762768
- For binary results: displays as `name: value`
763769
- For n-ary results: displays as `name[key1][key2]: value`
764-
- `textSize` controls the line's font size: `large` renders **bigger** than the node's label, `normal` is the default (smaller than the label), and `small` is smaller still.
770+
- `textStyle` is the same shared block edges and atoms use. `size` controls the line's font size: `large` renders **bigger** than the node's label, `normal` is the default (smaller than the label), and `small` is smaller still. `color` sets the line's text color (unset = inherit the node's label color).
765771

766772
**Examples:**
767773

@@ -778,12 +784,12 @@ Adds computed attributes to nodes based on selector evaluation. Unlike `attribut
778784
name: score
779785
value: grades
780786
781-
# De-emphasize a secondary tag — rendered smaller than the default
787+
# De-emphasize a secondary tag — smaller than the default, muted gray
782788
- tag:
783789
toTag: Person
784790
name: id
785791
value: internalId
786-
textSize: small
792+
textStyle: { size: small, color: "#888" }
787793
```
788794

789795
---

docs/agent-manifest.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"description": "Canonical YAML specification for CnD layout",
44
"file": "docs/YAML_SPECIFICATION.md",
55
"cdn_url": "https://cdn.jsdelivr.net/gh/sidprasad/cnd-core@main/docs/YAML_SPECIFICATION.md",
6-
"pinned_example": "https://cdn.jsdelivr.net/gh/sidprasad/cnd-core@v1.8.0/docs/YAML_SPECIFICATION.md",
6+
"pinned_example": "https://cdn.jsdelivr.net/gh/sidprasad/cnd-core@v1.9.0/docs/YAML_SPECIFICATION.md",
77
"content_type": "text/markdown",
8-
"version": "1.8.0"
8+
"version": "1.9.0"
99
}

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "spytial-core",
3-
"version": "3.0.1",
3+
"version": "3.1.0",
44
"description": "A fully client-side application for SpyTial.",
55
"main": "./dist/browser/spytial-core-complete.global.js",
66
"types": "./dist/browser/spytial-core-complete.global.d.ts",

site/directives.md

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -453,22 +453,25 @@ Converts an edge relationship into a **label on the source node**. The edge is r
453453
field: <field-name> # Required
454454
selector: <unary-selector> # Optional
455455
filter: <n-ary-selector> # Optional
456-
textSize: <size> # Optional: small | normal | large
456+
textStyle: # Optional: shared text-style block
457+
size: <small|normal|large> # font size relative to the node label
458+
color: <color> # text color (any CSS color)
457459
```
458460

459461
| Field | Required | Type | Description |
460462
|-------|----------|------|-------------|
461463
| `field` | Yes | string | Relation to display as an attribute |
462464
| `selector` | No | string | Filter by source atom type |
463465
| `filter` | No | string | Filter specific tuples |
464-
| `textSize` | No | `small` \| `normal` \| `large` | Size of the attribute text relative to the node label (default `normal`) |
466+
| `textStyle.size` | No | `small` \| `normal` \| `large` | Size of the attribute text relative to the node label (default `normal`) |
467+
| `textStyle.color` | No | string | Text color of the attribute line (default inherits the node label color) |
465468

466469
### What Happens
467470

468471
- The edge for this field is **removed** from the graph
469472
- The target value appears as `field: value` on the source node
470473
- Multiple targets become a comma-separated list
471-
- `textSize` scales the line's font: `large` is bigger than the node label, `normal` (default) is smaller, `small` smaller still; the node box resizes to fit
474+
- `textStyle` is the same shared block edges and atoms use. `size` scales the line's font (`large` bigger than the node label, `normal` default, `small` smaller still; the node box resizes to fit); `color` sets its text color (unset = inherit the node label color)
472475

473476
### Examples
474477

@@ -524,22 +527,25 @@ Adds computed labels to nodes **without** removing edges. Unlike `attribute`, th
524527
toTag: <unary-selector> # Required
525528
name: <attribute-name> # Required
526529
value: <n-ary-selector> # Required
527-
textSize: <size> # Optional: small | normal | large
530+
textStyle: # Optional: shared text-style block
531+
size: <small|normal|large> # font size relative to the node label
532+
color: <color> # text color (any CSS color)
528533
```
529534

530535
| Field | Required | Type | Description |
531536
|-------|----------|------|-------------|
532537
| `toTag` | Yes | string | Selector for atoms that receive the tag |
533538
| `name` | Yes | string | Label name to display |
534539
| `value` | Yes | string | Selector whose result becomes the value |
535-
| `textSize` | No | `small` \| `normal` \| `large` | Size of the tag text relative to the node label (default `normal`) |
540+
| `textStyle.size` | No | `small` \| `normal` \| `large` | Size of the tag text relative to the node label (default `normal`) |
541+
| `textStyle.color` | No | string | Text color of the tag line (default inherits the node label color) |
536542

537543
### Behavior
538544

539545
- Does **NOT** remove edges (unlike `attribute`)
540546
- For binary results: displays as `name: value`
541547
- For higher-arity results: displays as `name[key1][key2]: value`
542-
- `textSize` scales the line's font: `large` is bigger than the node label, `normal` (default) is smaller, `small` smaller still
548+
- `textStyle` is the same shared block edges and atoms use. `size` scales the line's font (`large` bigger than the node label, `normal` default, `small` smaller still); `color` sets its text color (unset = inherit the node label color)
543549

544550
### Examples
545551

site/yaml-reference.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,11 +108,13 @@ directives:
108108
- attribute:
109109
field: age
110110
selector: Person
111+
textStyle: { size: large, color: "#c0392b" } # optional: shared size + color block
111112
112113
- tag:
113114
toTag: Student
114115
name: grade
115116
value: currentGrade
117+
textStyle: { size: small, color: "#2980b9" }
116118
117119
- icon:
118120
selector: File

src/layout/interfaces.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { Group } from "webcola";
22
import { RelativeOrientationConstraint, CyclicOrientationConstraint, AlignConstraint, GroupByField, GroupBySelector, RelativeDirection } from "./layoutspec";
33
import { EdgeStyle } from "./edge-style";
4-
import { AttrTextSize } from "./text-extent";
54
import type { TextStyle } from "./style/text-style";
65

76
export interface LayoutGroup {
@@ -88,11 +87,12 @@ export interface LayoutNode {
8887
groups?: string[];
8988
attributes?: Record<string, string[]>;
9089
/**
91-
* Per-attribute-key text-size tier (from the `textSize` field of an
92-
* `attribute` or `tag` directive). Keyed by the same attribute key as
93-
* {@link attributes}. Absent keys render at the normal secondary size.
90+
* Per-attribute-key text style (the shared `textStyle` block: size + color)
91+
* from an `attribute` or `tag` directive. Keyed by the same attribute key as
92+
* {@link attributes}. Absent keys render at the normal secondary size with
93+
* the inherited label color.
9494
*/
95-
attributeSizes?: Record<string, AttrTextSize>;
95+
attributeTextStyles?: Record<string, TextStyle>;
9696
/**
9797
* Labels associated with this node from the data instance (e.g., Skolems).
9898
* These are displayed prominently on nodes, typically styled in the node's color.

0 commit comments

Comments
 (0)