@formicoidea/labre-framework-ddd-core-domain 0.32.0 → 0.33.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.
package/dist/rules.js ADDED
@@ -0,0 +1,206 @@
1
+ import { CORE_DOMAIN_BACKGROUND, CORE_DOMAIN_LEGEND_TONES, } from './core-domain/background.js';
2
+ import { CORE_DOMAIN_ROLE, CORE_DOMAIN_ROLES } from './roles.js';
3
+ /**
4
+ * Core Domain Chart validation rules.
5
+ *
6
+ * DATA owned by the framework, versioned per rule: the engine
7
+ * (`@labre/affine-block-surface`) knows how to evaluate a FAMILY, never a
8
+ * concrete rule. Adding a rule is adding an entry to this array.
9
+ *
10
+ * Registered from the flag-gated `DddCoreDomainViewExtension`, so switching the
11
+ * `ddd-core-domain` flag off removes the rules with the rest of the tooling —
12
+ * charts already drawn keep rendering, they simply stop being checked (see
13
+ * `docs/adr/0009`).
14
+ *
15
+ * Every rule is `warning` or `audit`: nothing downstream implements a blocking
16
+ * level — no gesture is refused anywhere in this library — so shipping one
17
+ * would be data claiming an effect that does not exist.
18
+ *
19
+ * ## Two rules that are NOT here (PO, 26/08/2026)
20
+ *
21
+ * `suspect-supporting` (a supporting sub-domain drawn at high complexity) and
22
+ * `complex-generic` (a generic one drawn at high complexity) were cut from v1:
23
+ * both hang on the WORDING of the chart's regions — what exactly the middle
24
+ * band claims — and the PO has not settled it. They are two `element-in-zone`
25
+ * entries the day it is, and nothing else moves.
26
+ */
27
+ /**
28
+ * **C1** — an outsourced sub-domain has no business in the Core quadrant.
29
+ *
30
+ * The one thing a Core Domain Chart is FOR: the core is what the organisation
31
+ * must own, because it is what differentiates it. A sub-domain marked
32
+ * "outsourced / purchased" plotted in the core is not a placement mistake — it
33
+ * is a strategy contradiction, and the chart exists to make it visible.
34
+ *
35
+ * Written on `core-domain:subdomain-outsourced` alone: the four other dot
36
+ * presets are perfectly at home there.
37
+ *
38
+ * ## What it stays silent about
39
+ *
40
+ * A dot off every chart (a sketch on blank canvas), and — this is the reason
41
+ * the zones name their variant — a dot on a chart turned to its MIGRATION
42
+ * reading, where there is no Core quadrant to be in. Judging one against a
43
+ * region the user cannot see would be indicting them over a frame turned to its
44
+ * other page.
45
+ */
46
+ const outsourcedCore = {
47
+ id: 'core-domain.outsourced-core',
48
+ framework: 'core-domain',
49
+ family: 'element-in-zone',
50
+ severity: 'warning',
51
+ appliesTo: CORE_DOMAIN_ROLE.outsourced,
52
+ roles: CORE_DOMAIN_ROLES,
53
+ messageKey: 'com.labre.core-domain.validation.outsourced-core',
54
+ messageFallback: 'This outsourced sub-domain sits in the Core quadrant.',
55
+ suggestionKey: 'com.labre.core-domain.validation.outsourced-core.suggestion',
56
+ suggestionFallback: 'The core is what the organisation must own — move it out of the Core quadrant, or reclassify it as a big bet.',
57
+ version: 1,
58
+ provenance: {
59
+ source: 'recommendation',
60
+ reference: 'Core Domain Charts (DDD) — the one question the chart exists to ask',
61
+ },
62
+ backgroundRole: CORE_DOMAIN_ROLE.chart,
63
+ // The frame's own declaration, carried as data exactly like `roles` is: it is
64
+ // where the Core quadrant's rectangle is written, and the engine reads it
65
+ // rather than knowing anything about this chart.
66
+ background: CORE_DOMAIN_BACKGROUND,
67
+ inZone: { zoneIds: ['core'], expect: 'outside' },
68
+ };
69
+ /**
70
+ * **C2** — a movement runs from a current position to a future one.
71
+ *
72
+ * The grammar of the arrow, and the whole of it: `bc-current` _is moving to_
73
+ * `bc-future`. An arrow drawn the other way round, or between two current
74
+ * positions, says something the chart has no reading for.
75
+ *
76
+ * Self-loops are a finding too: an arrow whose two ends are the same dot is a
77
+ * context moving to where it already is.
78
+ *
79
+ * ## The three silences, and why each one matters
80
+ *
81
+ * - a **free connector** (no role) is not a claim — the user is annotating;
82
+ * - a movement with a **free end** relates nothing;
83
+ * - a movement onto **anything outside the alphabet** — a sub-domain preset, a
84
+ * neutral shape, a sticky from another framework — is a SKETCH. The alphabet
85
+ * of this rule is `bc-current` / `bc-future` and nothing else, so a movement
86
+ * drawn from a big bet to a platform is silent rather than indicted. That is
87
+ * the hard requirement of the family, and the reason it is shippable.
88
+ *
89
+ * No `forbidDuplicate`: two movements drawn between the same two contexts are
90
+ * two dated statements about the same journey, which is a legitimate way to use
91
+ * this chart.
92
+ */
93
+ const malformedMovement = {
94
+ id: 'core-domain.malformed-movement',
95
+ framework: 'core-domain',
96
+ family: 'relation-endpoints',
97
+ severity: 'warning',
98
+ // No `appliesTo`: the subject of this rule is a RELATION, and the role that
99
+ // names it is declared where the family reads it — naming one of the three
100
+ // indicted elements here would be data that lies.
101
+ roles: CORE_DOMAIN_ROLES,
102
+ messageKey: 'com.labre.core-domain.validation.malformed-movement',
103
+ messageFallback: 'This movement does not run from a current position to a future one.',
104
+ suggestionKey: 'com.labre.core-domain.validation.malformed-movement.suggestion',
105
+ suggestionFallback: 'A movement reads "is moving to" — draw it from the context where it stands today to the future position, or reverse it.',
106
+ version: 1,
107
+ provenance: {
108
+ source: 'recommendation',
109
+ reference: 'Core Domain Charts (DDD) — the grammar of the movement arrow',
110
+ },
111
+ backgroundRole: CORE_DOMAIN_ROLE.chart,
112
+ endpoints: {
113
+ edgeRole: CORE_DOMAIN_ROLE.movement,
114
+ allowed: [
115
+ {
116
+ source: CORE_DOMAIN_ROLE.bcCurrent,
117
+ edge: CORE_DOMAIN_ROLE.movement,
118
+ target: CORE_DOMAIN_ROLE.bcFuture,
119
+ },
120
+ ],
121
+ forbidSelfLoop: true,
122
+ selfLoop: {
123
+ messageKey: 'com.labre.core-domain.validation.movement-self-loop',
124
+ messageFallback: 'This movement points back at its own starting position.',
125
+ suggestionKey: 'com.labre.core-domain.validation.movement-self-loop.suggestion',
126
+ suggestionFallback: 'Drop the far end on the future position, or delete the arrow.',
127
+ },
128
+ },
129
+ };
130
+ /**
131
+ * **C3** — sub-domains must not sit on top of each other.
132
+ *
133
+ * A readability rule, not a semantic one: a chart where two dots overlap hides
134
+ * one of them, and a hidden sub-domain is a sub-domain nobody discusses. Written
135
+ * on the PARENT role, so all five presets are covered for free.
136
+ *
137
+ * `minPenetration: 6` — the dot is 26 units across, so six units is roughly a
138
+ * quarter of one. Two dots grazing each other while somebody arranges a cluster
139
+ * are silent; one drawn over another is not.
140
+ */
141
+ const overlappingArtefacts = {
142
+ id: 'core-domain.overlapping-artefacts',
143
+ framework: 'core-domain',
144
+ family: 'no-overlap',
145
+ severity: 'warning',
146
+ // No `appliesTo`: the subjects of a `no-overlap` rule are its PAIRS.
147
+ roles: CORE_DOMAIN_ROLES,
148
+ messageKey: 'com.labre.core-domain.validation.overlapping-artefacts',
149
+ messageFallback: 'These two sub-domains overlap and hide each other.',
150
+ suggestionKey: 'com.labre.core-domain.validation.overlapping-artefacts.suggestion',
151
+ suggestionFallback: 'Move one of them aside.',
152
+ version: 1,
153
+ provenance: {
154
+ source: 'labre-convention',
155
+ reference: 'Labre readability convention — no Core Domain Chart rule speaks of overlapping ink',
156
+ },
157
+ // Not a frame the rule measures against — an overlap is an overlap wherever
158
+ // it happens — but the chart a finding is ATTRIBUTED to, so the arbitration
159
+ // "ignore this rule on the whole chart" has one chart to be written on.
160
+ backgroundRole: CORE_DOMAIN_ROLE.chart,
161
+ overlap: [[CORE_DOMAIN_ROLE.subdomain, CORE_DOMAIN_ROLE.subdomain]],
162
+ minPenetration: 6,
163
+ };
164
+ /**
165
+ * **C4** — a sub-domain is drawn in one of the five legend colours.
166
+ *
167
+ * The chart's notation IS its five colours: the legend the toolbar inserts
168
+ * lists them, and a dot recoloured by hand out of the shape palette silently
169
+ * stops meaning anything. Judged on the tone FAMILY, never on the hex, so every
170
+ * legitimate shade the shape toolbar can produce still reads as its colour.
171
+ *
172
+ * `audit`, on purpose and in every profile: recolouring a dot is a normal thing
173
+ * to do while thinking, and the day it matters is the day somebody reads the
174
+ * chart back — which is what a conformance report is for. The finding reaches
175
+ * `violations$`; the canvas says nothing.
176
+ *
177
+ * The sanctioned tones are named, not restated: `CORE_DOMAIN_LEGEND_TONES` is
178
+ * the list of palette entries the declaration built from `CD_SUBDOMAINS`, so
179
+ * restyling the notation restyles the convention with it, in one place.
180
+ */
181
+ const offLegendColour = {
182
+ id: 'core-domain.off-legend-colour',
183
+ framework: 'core-domain',
184
+ family: 'tone-convention',
185
+ severity: 'audit',
186
+ appliesTo: CORE_DOMAIN_ROLE.subdomain,
187
+ roles: CORE_DOMAIN_ROLES,
188
+ messageKey: 'com.labre.core-domain.validation.off-legend-colour',
189
+ messageFallback: 'This sub-domain is not drawn in one of the legend colours.',
190
+ suggestionKey: 'com.labre.core-domain.validation.off-legend-colour.suggestion',
191
+ suggestionFallback: 'The chart reads by its notation — put the dot back on one of the five legend colours, or add the new one to the legend.',
192
+ version: 1,
193
+ provenance: {
194
+ source: 'labre-convention',
195
+ reference: 'Labre convention — the legend this toolbar inserts declares the palette',
196
+ },
197
+ backgroundRole: CORE_DOMAIN_ROLE.chart,
198
+ background: CORE_DOMAIN_BACKGROUND,
199
+ tone: { palette: CORE_DOMAIN_LEGEND_TONES },
200
+ };
201
+ export const CORE_DOMAIN_RULES = [
202
+ outsourcedCore,
203
+ malformedMovement,
204
+ overlappingArtefacts,
205
+ offLegendColour,
206
+ ];
@@ -2,5 +2,6 @@ import { DddSeniorButtonBase } from '@formicoidea/labre-ddd-shared';
2
2
  export declare class EdgelessDddCoreDomainSeniorButton extends DddSeniorButtonBase {
3
3
  protected menuTag: "edgeless-ddd-core-domain-menu";
4
4
  protected label: string;
5
+ protected labelKey: string;
5
6
  protected icon: import("lit-html").TemplateResult<2>;
6
7
  }
@@ -4,6 +4,7 @@ export class EdgelessDddCoreDomainSeniorButton extends DddSeniorButtonBase {
4
4
  super(...arguments);
5
5
  this.menuTag = 'edgeless-ddd-core-domain-menu';
6
6
  this.label = 'Core Domain Chart';
7
+ this.labelKey = 'com.labre.framework.ddd-core-domain';
7
8
  this.icon = coreDomainToolbarIcon;
8
9
  }
9
10
  }
@@ -3,6 +3,7 @@ import { html } from 'lit';
3
3
  /** Independent senior tool — Core Domain Chart. */
4
4
  export const coreDomainSeniorTool = SeniorToolExtension('ddd-core-domain', ({ block }) => ({
5
5
  name: 'Core Domain Chart',
6
+ labelKey: 'com.labre.framework.ddd-core-domain',
6
7
  content: html `<edgeless-ddd-core-domain-senior-button
7
8
  .edgeless=${block}
8
9
  ></edgeless-ddd-core-domain-senior-button>`,
@@ -1,14 +1,14 @@
1
1
  import { type TranslationKeyManifestEntry } from '@formicoidea/labre-core/std';
2
2
  /**
3
- * THIS framework's contribution to the translation-key manifest.
3
+ * THIS framework's contribution to the translation-key manifest — every
4
+ * `com.labre.*` key the Core Domain Chart can hand to `TranslationProvider.t`,
5
+ * derived from the very declarations the editor registers.
4
6
  *
5
- * Its command labels and descriptions are built from a TEMPLATE, so the
6
- * concrete keys exist nowhere but in the declarations themselves and the
7
- * core manifest could not restate them even if it wanted to. The
8
- * contribution therefore ships WITH the framework: in the bundled
9
- * distribution `@formicoidea/labre-framework-ddd-core-domain` carries it, and a host
10
- * composes it into its catalogue exactly as it already composes
11
- * `coreDomainCommands` into the command registry. See
12
- * `packages/affine/all/src/translations.ts`.
7
+ * It lives HERE, not in `@labre/affine/translations`, because the bundled
8
+ * distribution splits the library along exactly this line:
9
+ * `@formicoidea/labre-core` is the editor MINUS the frameworks, and a host that
10
+ * installs `@formicoidea/labre-framework-ddd-core-domain` composes this export
11
+ * into the catalogue it builds the same sentence that already holds for
12
+ * `coreDomainCommands`.
13
13
  */
14
14
  export declare const coreDomainTranslationEntries: TranslationKeyManifestEntry[];
@@ -1,15 +1,20 @@
1
- import { commandTranslationEntries, } from '@formicoidea/labre-core/std';
1
+ import { collectTranslationKeys, commandTranslationEntries, mergeTranslationEntries, } from '@formicoidea/labre-core/std';
2
2
  import { coreDomainCommands } from './commands.js';
3
+ import { CORE_DOMAIN_BACKGROUND } from './core-domain/background.js';
4
+ import { CORE_DOMAIN_NUDGES } from './nudges.js';
5
+ import { CORE_DOMAIN_PROFILES } from './profiles.js';
6
+ import { CORE_DOMAIN_ROLES } from './roles.js';
7
+ import { CORE_DOMAIN_RULES } from './rules.js';
3
8
  /**
4
- * THIS framework's contribution to the translation-key manifest.
9
+ * THIS framework's contribution to the translation-key manifest — every
10
+ * `com.labre.*` key the Core Domain Chart can hand to `TranslationProvider.t`,
11
+ * derived from the very declarations the editor registers.
5
12
  *
6
- * Its command labels and descriptions are built from a TEMPLATE, so the
7
- * concrete keys exist nowhere but in the declarations themselves and the
8
- * core manifest could not restate them even if it wanted to. The
9
- * contribution therefore ships WITH the framework: in the bundled
10
- * distribution `@formicoidea/labre-framework-ddd-core-domain` carries it, and a host
11
- * composes it into its catalogue exactly as it already composes
12
- * `coreDomainCommands` into the command registry. See
13
- * `packages/affine/all/src/translations.ts`.
13
+ * It lives HERE, not in `@labre/affine/translations`, because the bundled
14
+ * distribution splits the library along exactly this line:
15
+ * `@formicoidea/labre-core` is the editor MINUS the frameworks, and a host that
16
+ * installs `@formicoidea/labre-framework-ddd-core-domain` composes this export
17
+ * into the catalogue it builds the same sentence that already holds for
18
+ * `coreDomainCommands`.
14
19
  */
15
- export const coreDomainTranslationEntries = commandTranslationEntries(coreDomainCommands);
20
+ export const coreDomainTranslationEntries = mergeTranslationEntries(commandTranslationEntries(coreDomainCommands), collectTranslationKeys('role', CORE_DOMAIN_ROLES), collectTranslationKeys('background', CORE_DOMAIN_BACKGROUND), collectTranslationKeys('rule', CORE_DOMAIN_RULES), collectTranslationKeys('nudge', CORE_DOMAIN_NUDGES), collectTranslationKeys('profile', CORE_DOMAIN_PROFILES));
package/dist/view.d.ts CHANGED
@@ -11,7 +11,9 @@ export declare class DddCoreDomainRenderViewExtension extends ViewExtensionProvi
11
11
  setup(context: ViewExtensionContext): void;
12
12
  }
13
13
  /**
14
- * Core Domain Chart senior button — independently flag-gated (`ddd-core-domain`).
14
+ * Core Domain Chart senior button — independently flag-gated (`ddd-core-domain`)
15
+ * — and, beside it, the validation tooling: the rules, the profiles and the
16
+ * work-quality checklist.
15
17
  *
16
18
  * Note: its Templates-panel category is registered by the aggregate package's
17
19
  * {@link DddTemplatesViewExtension} (gated by `ddd-templates`), so templates
package/dist/view.js CHANGED
@@ -1,10 +1,17 @@
1
+ import { QualityNudgeExtension, validationToolbarConfig, ValidationProfileExtension, ValidationRuleExtension, } from '@formicoidea/labre-core/blocks/surface';
1
2
  import { ViewExtensionProvider, } from '@formicoidea/labre-core/ext-loader';
2
- import { CommandExtension } from '@formicoidea/labre-core/std';
3
+ import { ToolbarModuleExtension } from '@formicoidea/labre-core/shared/services';
4
+ import { BlockFlavourIdentifier, CommandExtension } from '@formicoidea/labre-core/std';
5
+ import { RoleVocabularyExtension } from '@formicoidea/labre-core/std/gfx';
3
6
  import { coreDomainCommandIcons, coreDomainCommands } from './commands.js';
4
7
  import { CoreDomainRendererExtension } from './core-domain/element-renderer.js';
5
- import { CoreDomainInteraction, CoreDomainView } from './core-domain/element-view.js';
8
+ import { CoreDomainInteraction, CoreDomainView, } from './core-domain/element-view.js';
6
9
  import { coreDomainToolbarExtension } from './core-domain/toolbar-config.js';
7
10
  import { coreDomainEffects } from './effects.js';
11
+ import { CORE_DOMAIN_NUDGES } from './nudges.js';
12
+ import { CORE_DOMAIN_PROFILES } from './profiles.js';
13
+ import { CORE_DOMAIN_ROLES } from './roles.js';
14
+ import { CORE_DOMAIN_RULES } from './rules.js';
8
15
  import { coreDomainSeniorTool } from './toolbar/senior-tool.js';
9
16
  /**
10
17
  * Core Domain Chart rendering — ALWAYS registered, independent of any flag.
@@ -22,6 +29,13 @@ export class DddCoreDomainRenderViewExtension extends ViewExtensionProvider {
22
29
  super.setup(context);
23
30
  context.register(CoreDomainView);
24
31
  context.register(CoreDomainRendererExtension);
32
+ // The role VOCABULARY, always on. A role is written in the document, not in
33
+ // the tooling: the direction reveal of a typed movement and the inversion
34
+ // command read this, and they have to keep working on a chart drawn while
35
+ // the flag was on and opened while it is off (`docs/adr/0009`,
36
+ // `docs/adr/0010`). The rules that JUDGE those roles stay in the
37
+ // flag-gated extension below.
38
+ context.register(RoleVocabularyExtension(CORE_DOMAIN_ROLES));
25
39
  if (this.isEdgeless(context.scope)) {
26
40
  context.register(CoreDomainInteraction);
27
41
  context.register(coreDomainToolbarExtension);
@@ -29,7 +43,9 @@ export class DddCoreDomainRenderViewExtension extends ViewExtensionProvider {
29
43
  }
30
44
  }
31
45
  /**
32
- * Core Domain Chart senior button — independently flag-gated (`ddd-core-domain`).
46
+ * Core Domain Chart senior button — independently flag-gated (`ddd-core-domain`)
47
+ * — and, beside it, the validation tooling: the rules, the profiles and the
48
+ * work-quality checklist.
33
49
  *
34
50
  * Note: its Templates-panel category is registered by the aggregate package's
35
51
  * {@link DddTemplatesViewExtension} (gated by `ddd-templates`), so templates
@@ -47,6 +63,20 @@ export class DddCoreDomainViewExtension extends ViewExtensionProvider {
47
63
  setup(context) {
48
64
  super.setup(context);
49
65
  if (this.isEdgeless(context.scope)) {
66
+ context.register(ValidationRuleExtension(CORE_DOMAIN_RULES));
67
+ context.register(ValidationProfileExtension(CORE_DOMAIN_PROFILES));
68
+ context.register(QualityNudgeExtension(CORE_DOMAIN_NUDGES));
69
+ // The Validation dropdown on a selected chart's contextual toolbar. A
70
+ // SECOND module on the same element, through the `custom:` flavour slot:
71
+ // `coreDomainToolbarExtension` is registered always-on because a stored
72
+ // chart must keep its legend action, while choosing how hard to check it
73
+ // is tooling and belongs here. The config itself names no framework — it
74
+ // reads roles and profiles — so it is the very same object Wardley
75
+ // registers on its own flavour.
76
+ context.register(ToolbarModuleExtension({
77
+ id: BlockFlavourIdentifier('custom:affine:surface:coreDomain'),
78
+ config: validationToolbarConfig,
79
+ }));
50
80
  context.register(coreDomainSeniorTool);
51
81
  context.register(CommandExtension(coreDomainCommands, coreDomainCommandIcons));
52
82
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@formicoidea/labre-framework-ddd-core-domain",
3
3
  "description": "Labre ddd-core-domain framework for @formicoidea/labre-core.",
4
- "version": "0.32.0",
4
+ "version": "0.33.0",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "author": "lajola",
@@ -27,8 +27,8 @@
27
27
  "dist"
28
28
  ],
29
29
  "dependencies": {
30
- "@formicoidea/labre-core": "0.32.0",
31
- "@formicoidea/labre-ddd-shared": "0.32.0",
30
+ "@formicoidea/labre-core": "0.33.0",
31
+ "@formicoidea/labre-ddd-shared": "0.33.0",
32
32
  "lit": "^3.2.0"
33
33
  }
34
34
  }
@@ -1,32 +0,0 @@
1
- /**
2
- * Visual constants for the Core Domain Chart background (DDD Crew). Authored in
3
- * a fixed reference space (900 × 820) and scaled uniformly to the element bounds
4
- * by the renderer. Reproduces the supplied template: two axes (Complexity ×
5
- * Business differentiation), Low/High ticks and the translucent Generic /
6
- * Supporting / Core zone bands. The "Notation" legend is a separate prefab group.
7
- */
8
- export declare const REF_W = 900;
9
- export declare const REF_H = 820;
10
- export declare const COLORS: {
11
- readonly axis: "#000000";
12
- readonly zoneLabel: "#ffffff";
13
- readonly tick: "#777777";
14
- readonly title: "#000000";
15
- };
16
- /** Translucent zone bands (drawn at 0.6 alpha), back-to-front. */
17
- export declare const ZONES: ReadonlyArray<{
18
- x: number;
19
- y: number;
20
- w: number;
21
- h: number;
22
- fill: string;
23
- }>;
24
- /** Zone names (centred): [text, x, y, fontSize]. */
25
- export declare const ZONE_LABELS: ReadonlyArray<readonly [string, number, number, number]>;
26
- /** Axis frame: origin (ox, oy) bottom-left, up to `top`, right to `right`. */
27
- export declare const AXIS: {
28
- readonly ox: 60;
29
- readonly oy: 770;
30
- readonly top: 24;
31
- readonly right: 846;
32
- };
@@ -1,30 +0,0 @@
1
- /**
2
- * Visual constants for the Core Domain Chart background (DDD Crew). Authored in
3
- * a fixed reference space (900 × 820) and scaled uniformly to the element bounds
4
- * by the renderer. Reproduces the supplied template: two axes (Complexity ×
5
- * Business differentiation), Low/High ticks and the translucent Generic /
6
- * Supporting / Core zone bands. The "Notation" legend is a separate prefab group.
7
- */
8
- export const REF_W = 900;
9
- export const REF_H = 820;
10
- export const COLORS = {
11
- axis: '#000000',
12
- zoneLabel: '#ffffff',
13
- tick: '#777777',
14
- title: '#000000',
15
- };
16
- /** Translucent zone bands (drawn at 0.6 alpha), back-to-front. */
17
- export const ZONES = [
18
- { x: 70, y: 30, w: 150, h: 720, fill: '#b3b3b3' },
19
- { x: 220, y: 30, w: 220, h: 720, fill: '#9933ff' },
20
- { x: 440, y: 30, w: 400, h: 360, fill: '#4d9900' },
21
- { x: 440, y: 390, w: 400, h: 360, fill: '#9933ff' },
22
- ];
23
- /** Zone names (centred): [text, x, y, fontSize]. */
24
- export const ZONE_LABELS = [
25
- ['Core', 640, 214, 26],
26
- ['Supporting', 340, 474, 20],
27
- ['Generic', 150, 474, 20],
28
- ];
29
- /** Axis frame: origin (ox, oy) bottom-left, up to `top`, right to `right`. */
30
- export const AXIS = { ox: 60, oy: 770, top: 24, right: 846 };