@formicoidea/labre-framework-c4 0.40.0 → 0.42.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.
@@ -0,0 +1,33 @@
1
+ import type { FrameworkPalette, NamedPalette } from '@formicoidea/labre-core/components/color-picker';
2
+ import type { ChromeWording } from '@formicoidea/labre-core/shared/services';
3
+ /**
4
+ * The C4 swatches' own names, resolved by `resolvePaletteLabel`
5
+ * (`@labre/affine-components/color-picker`) through each swatch's own
6
+ * {@link NamedPalette.labelWording} — the same framework-agnostic mechanism
7
+ * Wardley and EDGY use (`WARDLEY_PALETTE_WORDINGS`), so nothing here touches
8
+ * `packages/affine/model` (RED ZONE) or another framework's package.
9
+ *
10
+ * Exported so `../translations.ts` can list them under source `chrome`: a
11
+ * swatch's name is toolbar chrome, re-rendered on every locale switch, never
12
+ * seeded into a document.
13
+ */
14
+ export declare const C4_PALETTE_WORDING_PERSON: ChromeWording;
15
+ export declare const C4_PALETTE_WORDING_SYSTEM: ChromeWording;
16
+ export declare const C4_PALETTE_WORDING_CONTAINER: ChromeWording;
17
+ export declare const C4_PALETTE_WORDING_COMPONENT: ChromeWording;
18
+ export declare const C4_PALETTE_WORDING_EXTERNAL: ChromeWording;
19
+ export declare const C4_PALETTE_WORDING_RELATIONSHIP: ChromeWording;
20
+ export declare const C4_PALETTE_WORDINGS: readonly ChromeWording[];
21
+ /**
22
+ * From the default editor palette we keep ONLY the neutrals — the historical
23
+ * colours say nothing in this notation.
24
+ */
25
+ export declare const C4_PALETTE_LIST: NamedPalette[];
26
+ /**
27
+ * C4's page of the colour pickers' carousel (`docs/adr/0027`). Registered from
28
+ * the FLAG-GATED view extension, because offering hues is TOOLING: a diagram
29
+ * drawn while the flag was on keeps every colour it was painted with when the
30
+ * flag goes off — the shelf that offers more of them is what goes away
31
+ * (`docs/adr/0009`).
32
+ */
33
+ export declare const C4_FRAMEWORK_PALETTE: FrameworkPalette;
@@ -0,0 +1,114 @@
1
+ import { neutralPalettes } from '@formicoidea/labre-core/gfx/shape';
2
+ import { NODE_PALETTE, RELATIONSHIP_STROKE } from '../consts.js';
3
+ /**
4
+ * The C4 swatches' own names, resolved by `resolvePaletteLabel`
5
+ * (`@labre/affine-components/color-picker`) through each swatch's own
6
+ * {@link NamedPalette.labelWording} — the same framework-agnostic mechanism
7
+ * Wardley and EDGY use (`WARDLEY_PALETTE_WORDINGS`), so nothing here touches
8
+ * `packages/affine/model` (RED ZONE) or another framework's package.
9
+ *
10
+ * Exported so `../translations.ts` can list them under source `chrome`: a
11
+ * swatch's name is toolbar chrome, re-rendered on every locale switch, never
12
+ * seeded into a document.
13
+ */
14
+ export const C4_PALETTE_WORDING_PERSON = [
15
+ 'com.labre.c4.palette.person-blue',
16
+ 'Person blue',
17
+ ];
18
+ export const C4_PALETTE_WORDING_SYSTEM = [
19
+ 'com.labre.c4.palette.system-blue',
20
+ 'System blue',
21
+ ];
22
+ export const C4_PALETTE_WORDING_CONTAINER = [
23
+ 'com.labre.c4.palette.container-blue',
24
+ 'Container blue',
25
+ ];
26
+ export const C4_PALETTE_WORDING_COMPONENT = [
27
+ 'com.labre.c4.palette.component-blue',
28
+ 'Component blue',
29
+ ];
30
+ export const C4_PALETTE_WORDING_EXTERNAL = [
31
+ 'com.labre.c4.palette.external-grey',
32
+ 'External grey',
33
+ ];
34
+ export const C4_PALETTE_WORDING_RELATIONSHIP = [
35
+ 'com.labre.c4.palette.relationship-grey',
36
+ 'Relationship grey',
37
+ ];
38
+ export const C4_PALETTE_WORDINGS = [
39
+ C4_PALETTE_WORDING_PERSON,
40
+ C4_PALETTE_WORDING_SYSTEM,
41
+ C4_PALETTE_WORDING_CONTAINER,
42
+ C4_PALETTE_WORDING_COMPONENT,
43
+ C4_PALETTE_WORDING_EXTERNAL,
44
+ C4_PALETTE_WORDING_RELATIONSHIP,
45
+ ];
46
+ /**
47
+ * The C4 **level ladder**, surfaced as ready-made swatches.
48
+ *
49
+ * C4's stencil IS a colour code (see `../consts.ts`): the four levels are four
50
+ * blues running lighter as they go in — person, software system, container,
51
+ * component — anything outside the diagram's scope is one grey, and everything
52
+ * that is not an element (the relationship arrow, the boundary frame) is drawn
53
+ * in a second, darker one.
54
+ *
55
+ * Every value is read off {@link NODE_PALETTE} and {@link RELATIONSHIP_STROKE}
56
+ * rather than restated, so the shelf and the notation cannot drift apart. They
57
+ * are SHORTCUTS, never constraints: no rule of this pack reads a colour.
58
+ */
59
+ const C4_PALETTES = [
60
+ {
61
+ key: 'Person blue',
62
+ value: NODE_PALETTE.person.fill,
63
+ labelWording: C4_PALETTE_WORDING_PERSON,
64
+ },
65
+ {
66
+ key: 'System blue',
67
+ value: NODE_PALETTE.system.fill,
68
+ labelWording: C4_PALETTE_WORDING_SYSTEM,
69
+ },
70
+ {
71
+ key: 'Container blue',
72
+ value: NODE_PALETTE.container.fill,
73
+ labelWording: C4_PALETTE_WORDING_CONTAINER,
74
+ },
75
+ {
76
+ key: 'Component blue',
77
+ value: NODE_PALETTE.component.fill,
78
+ labelWording: C4_PALETTE_WORDING_COMPONENT,
79
+ },
80
+ // One grey for both external kinds, because the stencil paints them the same:
81
+ // "outside the scope of this diagram" is one statement.
82
+ {
83
+ key: 'External grey',
84
+ value: NODE_PALETTE['system-ext'].fill,
85
+ labelWording: C4_PALETTE_WORDING_EXTERNAL,
86
+ },
87
+ // The grey of the relationship arrow — and of the boundary frame, which the
88
+ // stencil draws in the very same `#444444`.
89
+ {
90
+ key: 'Relationship grey',
91
+ value: RELATIONSHIP_STROKE,
92
+ labelWording: C4_PALETTE_WORDING_RELATIONSHIP,
93
+ },
94
+ ];
95
+ /**
96
+ * From the default editor palette we keep ONLY the neutrals — the historical
97
+ * colours say nothing in this notation.
98
+ */
99
+ export const C4_PALETTE_LIST = [
100
+ ...C4_PALETTES,
101
+ ...neutralPalettes(),
102
+ ];
103
+ /**
104
+ * C4's page of the colour pickers' carousel (`docs/adr/0027`). Registered from
105
+ * the FLAG-GATED view extension, because offering hues is TOOLING: a diagram
106
+ * drawn while the flag was on keeps every colour it was painted with when the
107
+ * flag goes off — the shelf that offers more of them is what goes away
108
+ * (`docs/adr/0009`).
109
+ */
110
+ export const C4_FRAMEWORK_PALETTE = {
111
+ framework: 'c4',
112
+ labelWording: ['com.labre.framework.c4', 'C4 model'],
113
+ palettes: C4_PALETTE_LIST,
114
+ };
@@ -1,11 +1,60 @@
1
1
  import { collectTranslationKeys, commandCategoryTranslationEntries, commandTranslationEntries, mergeTranslationEntries, } from '@formicoidea/labre-core/std';
2
2
  import { C4_BOARD_BACKGROUND, C4_BOUNDARY_BACKGROUND } from './background.js';
3
- import { c4Commands } from './commands.js';
3
+ import { c4Commands, C4_LEGEND_SECTION_WORDINGS } from './commands.js';
4
+ import { BOUNDARY_LABEL, boundaryLabelKey, DESCRIPTION_PLACEHOLDER, DESCRIPTION_PLACEHOLDER_KEY, NODE_LABEL, nodeLabelKey, } from './consts.js';
4
5
  import { C4_BOARD_LEVEL_MENU } from './levels.js';
5
6
  import { C4_PROFILES } from './profiles.js';
6
7
  import { C4_READINGS } from './reading.js';
7
8
  import { C4_ROLES } from './roles.js';
8
9
  import { C4_RULES } from './rules.js';
10
+ import { C4_PALETTE_WORDINGS } from './toolbar/palette.js';
11
+ import { C4_TYPE_LINE_WORDINGS } from './type-line.js';
12
+ /**
13
+ * The captions a placed component and a placed boundary are seeded with — the
14
+ * fallback IS `NODE_LABEL[kind]` / `BOUNDARY_LABEL[variant]`, never restated,
15
+ * mirroring BPMN's `seedEntries` exactly. The type line's own bracketed word
16
+ * and the technology placeholder ARE here too (`C4_TYPE_LINE_WORDINGS`),
17
+ * resolved at placement (`createC4Node`, `actions.ts`) and rebuilt through the
18
+ * same seam on every edit commit (`C4TypeLineWatcher`).
19
+ */
20
+ const seedEntries = () => [
21
+ ...Object.entries(NODE_LABEL).map(([kind, label]) => ({
22
+ key: nodeLabelKey(kind),
23
+ fallback: label,
24
+ source: 'seed',
25
+ })),
26
+ ...Object.entries(BOUNDARY_LABEL).map(([variant, label]) => ({
27
+ key: boundaryLabelKey(variant),
28
+ fallback: label,
29
+ source: 'seed',
30
+ })),
31
+ {
32
+ key: DESCRIPTION_PLACEHOLDER_KEY,
33
+ fallback: DESCRIPTION_PLACEHOLDER,
34
+ source: 'seed',
35
+ },
36
+ ...C4_TYPE_LINE_WORDINGS.map(([key, fallback]) => ({
37
+ key,
38
+ fallback,
39
+ source: 'seed',
40
+ })),
41
+ ];
42
+ /**
43
+ * The carousel swatches' own names ({@link C4_PALETTE_WORDINGS}) — chrome,
44
+ * re-rendered by the colour picker on every locale switch, never seeded into
45
+ * a document. Same treatment as Wardley's and EDGY's.
46
+ */
47
+ const paletteEntries = () => C4_PALETTE_WORDINGS.map(([key, fallback]) => ({
48
+ key,
49
+ fallback,
50
+ source: 'chrome',
51
+ }));
52
+ /** The auto-legend's own section titles ({@link C4_LEGEND_SECTION_WORDINGS}). */
53
+ const legendSectionEntries = () => C4_LEGEND_SECTION_WORDINGS.map(([key, fallback]) => ({
54
+ key,
55
+ fallback,
56
+ source: 'chrome',
57
+ }));
9
58
  /**
10
59
  * THIS framework's contribution to the translation-key manifest — every
11
60
  * `com.labre.*` key C4 can hand to `TranslationProvider.t`, derived from the
@@ -40,7 +89,7 @@ commandCategoryTranslationEntries(c4Commands), collectTranslationKeys('role', C4
40
89
  // nothing is restated in `affine/all`, so a key added to the picker
41
90
  // reaches a host's catalogue by construction.
42
91
  C4_BOARD_LEVEL_MENU,
43
- ]),
92
+ ]), seedEntries(), legendSectionEntries(), paletteEntries(),
44
93
  // AFTER the two above, and the order is load-bearing: a rule carries its
45
94
  // framework's `roles` and, for `c4.person-in-boundary`, the boundary's own
46
95
  // declaration, so walking the rules reaches keys those two lists already
@@ -1,4 +1,6 @@
1
1
  import type { C4NodeKind } from '@formicoidea/labre-core/model';
2
+ import { type ChromeWording } from '@formicoidea/labre-core/shared/services';
3
+ import type { BlockStdScope } from '@formicoidea/labre-core/std';
2
4
  /**
3
5
  * The middle tier of a C4 element's label — `[Person]`, `[Software System]`,
4
6
  * `[Container: Java]` — SEMI-DERIVED: the word comes from the kind, the
@@ -41,6 +43,7 @@ import type { C4NodeKind } from '@formicoidea/labre-core/model';
41
43
  * without a word to announce it fails the build here.
42
44
  */
43
45
  export declare const C4_TYPE_WORD: Record<C4NodeKind, string>;
46
+ export declare const C4_TYPE_WORD_KEY: Record<C4NodeKind, string>;
44
47
  /**
45
48
  * The type line as it is drawn, brackets included.
46
49
  *
@@ -49,10 +52,12 @@ export declare const C4_TYPE_WORD: Record<C4NodeKind, string>;
49
52
  * produce a dangling `[Container: ]`. Whitespace runs collapse for the same
50
53
  * reason they do in the mermaid sanitizer: the line is one line.
51
54
  *
52
- * Pure, total and `std`-free, so the renderer, the exporter and a test can all
53
- * ask the same question and get the same answer.
55
+ * Pure and total; `std` is OPTIONAL and, when given, resolves the bracketed
56
+ * word through the host's catalogue ({@link typeWord}) — the renderer, the
57
+ * exporter and a test can all still ask the same question with no `std` at
58
+ * all and get the same English answer they always have.
54
59
  */
55
- export declare function c4TypeLine(kind: C4NodeKind, technology?: string): string;
60
+ export declare function c4TypeLine(kind: C4NodeKind, technology?: string, std?: BlockStdScope): string;
56
61
  /**
57
62
  * The technology an author stated in a type line, whatever shape they left it
58
63
  * in — `''` when they stated none.
@@ -70,8 +75,12 @@ export declare function c4TypeLine(kind: C4NodeKind, technology?: string): strin
70
75
  * https://x/docs → https://x/docs a colon that is NOT a prefix
71
76
  * ```
72
77
  *
73
- * Pure and `std`-free, so the commit hook, the exporter and a test all read the
74
- * same line the same way.
78
+ * Pure and total; `std` is OPTIONAL, exactly like {@link c4TypeLine} —
79
+ * without it this reads a line the same way it always has, and with it the
80
+ * vocabulary the `<word>:` prefix is matched against ALSO recognises the
81
+ * host's own resolved word ({@link knownTypeWords}), so a line seeded or
82
+ * normalized in a translated host still gives back the right technology
83
+ * rather than the whole line.
75
84
  *
76
85
  * It deliberately does NOT know about the creation placeholder: a node created
77
86
  * and never touched carries `[Container: technology]`, and reading that as "no
@@ -80,7 +89,7 @@ export declare function c4TypeLine(kind: C4NodeKind, technology?: string): strin
80
89
  * placeholder is a question for whoever asks what the element STATES, which is
81
90
  * the exporter, and it is answered there.
82
91
  */
83
- export declare function technologyOfTypeLine(text: string | null | undefined): string;
92
+ export declare function technologyOfTypeLine(text: string | null | undefined, std?: BlockStdScope): string;
84
93
  /**
85
94
  * A type line as it must be STORED, whatever was typed into it.
86
95
  *
@@ -91,8 +100,14 @@ export declare function technologyOfTypeLine(text: string | null | undefined): s
91
100
  *
92
101
  * Idempotent by construction — `normalize(normalize(x)) === normalize(x)` —
93
102
  * which is what lets it run on every commit without a guard.
103
+ *
104
+ * `std` is OPTIONAL and, when given, is threaded to both halves: the
105
+ * technology is read back recognising the host's own word
106
+ * ({@link technologyOfTypeLine}) and the line is rebuilt with it
107
+ * ({@link c4TypeLine}) — see `C4TypeLineWatcher`, the one real caller that
108
+ * has an `std` to give it.
94
109
  */
95
- export declare function normalizeC4TypeLine(kind: C4NodeKind, rawText: string | null | undefined): string;
110
+ export declare function normalizeC4TypeLine(kind: C4NodeKind, rawText: string | null | undefined, std?: BlockStdScope): string;
96
111
  /**
97
112
  * The word a fresh type line prompts the author with, in the technology's slot.
98
113
  *
@@ -101,6 +116,19 @@ export declare function normalizeC4TypeLine(kind: C4NodeKind, rawText: string |
101
116
  * {@link C4_TYPE_PLACEHOLDER}.
102
117
  */
103
118
  export declare const TYPE_TECHNOLOGY_PLACEHOLDER = "technology";
119
+ /**
120
+ * {@link TYPE_TECHNOLOGY_PLACEHOLDER}'s own key — see the long comment on
121
+ * {@link C4_TYPE_WORD_KEY} for why this lot's shipped French proposal keeps
122
+ * it identical to English.
123
+ */
124
+ export declare const C4_TECHNOLOGY_PLACEHOLDER_KEY = "com.labre.c4.type.technology-placeholder";
125
+ /**
126
+ * Every wording this file writes onto the canvas, for `translations.ts`'s
127
+ * manifest contribution — the four distinct type words plus the technology
128
+ * prompt, all `seed` (written into the document once, at placement or at a
129
+ * commit the watcher normalizes).
130
+ */
131
+ export declare const C4_TYPE_LINE_WORDINGS: readonly ChromeWording[];
104
132
  /**
105
133
  * Which kinds announce a technology in their type line AT ALL.
106
134
  *
@@ -131,6 +159,15 @@ export declare const C4_TYPE_TAKES_TECHNOLOGY: Record<C4NodeKind, boolean>;
131
159
  * with a technology literally called "technology".
132
160
  */
133
161
  export declare const C4_TYPE_PLACEHOLDER: Record<C4NodeKind, string>;
162
+ /**
163
+ * {@link C4_TYPE_PLACEHOLDER}, resolved through `std` when given — the
164
+ * placement site (`createC4Node`, `actions.ts`) seeds a fresh element with
165
+ * this rather than the English-only constant, so a component drawn in a
166
+ * translated host starts in that language, like every other seed
167
+ * (`docs/adr/0023`). With no `std` this returns exactly
168
+ * `C4_TYPE_PLACEHOLDER[kind]`.
169
+ */
170
+ export declare function c4TypePlaceholder(kind: C4NodeKind, std?: BlockStdScope): string;
134
171
  /**
135
172
  * The type line a component should carry once its shape has morphed from `from`
136
173
  * to `to` — or `null` when the line is the AUTHOR's and must not be touched.
@@ -172,4 +209,4 @@ export declare const C4_TYPE_PLACEHOLDER: Record<C4NodeKind, string>;
172
209
  *
173
210
  * Pure, total and `std`-free, like everything else in this file.
174
211
  */
175
- export declare function c4MorphedTypeLine(from: C4NodeKind, to: C4NodeKind, rawText: string | null | undefined): string | null;
212
+ export declare function c4MorphedTypeLine(from: C4NodeKind, to: C4NodeKind, rawText: string | null | undefined, std?: BlockStdScope): string | null;
package/dist/type-line.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { translateKey, } from '@formicoidea/labre-core/shared/services';
1
2
  /**
2
3
  * The middle tier of a C4 element's label — `[Person]`, `[Software System]`,
3
4
  * `[Container: Java]` — SEMI-DERIVED: the word comes from the kind, the
@@ -50,6 +51,51 @@ export const C4_TYPE_WORD = {
50
51
  browser: 'Container',
51
52
  component: 'Component',
52
53
  };
54
+ /**
55
+ * {@link C4_TYPE_WORD}'s own i18n key, one per DISTINCT word (four kinds
56
+ * share `Container`, two share `Person`, two share `Software System`, one
57
+ * word each carries one key).
58
+ *
59
+ * The PO's own glossary (`brief-i18n.md`) names "Software System",
60
+ * "Container" and "Component" franglais — the French proposal IS the English
61
+ * word — and this lot extends the same choice to "Person" and to
62
+ * {@link TYPE_TECHNOLOGY_PLACEHOLDER}, for a reason beyond consistency: this
63
+ * whole file's comparisons (`technologyOfTypeLine`, `C4_TYPE_PLACEHOLDER`,
64
+ * `c4StatedTechnology` in `component.ts`) read the STORED text back against
65
+ * these very words, and `c4StatedTechnology` runs from the `std`-free exporter
66
+ * (`export.ts`, ADR 0012 P3) — it cannot ask a host's catalogue at all. Keeping
67
+ * the shipped French wording IDENTICAL to English is what keeps that
68
+ * comparison correct on a document seeded in French. A host that overrides
69
+ * the catalogue with a genuinely different word regains the risk the
70
+ * `DESCRIPTION_PLACEHOLDER_KEY` comment in `consts.ts` already names for the
71
+ * description tier.
72
+ */
73
+ // Named constants rather than the key literals inline: `manifest.unit.spec.ts`
74
+ // reads two adjacent single-quoted literals in one argument list as a
75
+ // key/fallback PAIR, with no anchor on `translateKey(` — and a `Record`
76
+ // spelling each key out beside the NEXT (quoted, hyphenated) property name
77
+ // below it reads exactly like one. Referencing an identifier instead of
78
+ // repeating the literal sidesteps the false pairing.
79
+ const PERSON_KEY = 'com.labre.c4.type.person';
80
+ const SYSTEM_KEY = 'com.labre.c4.type.system';
81
+ const CONTAINER_KEY = 'com.labre.c4.type.container';
82
+ const COMPONENT_KEY = 'com.labre.c4.type.component';
83
+ export const C4_TYPE_WORD_KEY = {
84
+ person: PERSON_KEY,
85
+ 'person-ext': PERSON_KEY,
86
+ system: SYSTEM_KEY,
87
+ 'system-ext': SYSTEM_KEY,
88
+ container: CONTAINER_KEY,
89
+ database: CONTAINER_KEY,
90
+ mobile: CONTAINER_KEY,
91
+ browser: CONTAINER_KEY,
92
+ component: COMPONENT_KEY,
93
+ };
94
+ /** The word an `std` (when given) resolves {@link kind}'s bracket to. */
95
+ function typeWord(kind, std) {
96
+ const english = C4_TYPE_WORD[kind];
97
+ return std ? translateKey(std, C4_TYPE_WORD_KEY[kind], english) : english;
98
+ }
53
99
  /**
54
100
  * The type line as it is drawn, brackets included.
55
101
  *
@@ -58,11 +104,13 @@ export const C4_TYPE_WORD = {
58
104
  * produce a dangling `[Container: ]`. Whitespace runs collapse for the same
59
105
  * reason they do in the mermaid sanitizer: the line is one line.
60
106
  *
61
- * Pure, total and `std`-free, so the renderer, the exporter and a test can all
62
- * ask the same question and get the same answer.
107
+ * Pure and total; `std` is OPTIONAL and, when given, resolves the bracketed
108
+ * word through the host's catalogue ({@link typeWord}) — the renderer, the
109
+ * exporter and a test can all still ask the same question with no `std` at
110
+ * all and get the same English answer they always have.
63
111
  */
64
- export function c4TypeLine(kind, technology) {
65
- const word = C4_TYPE_WORD[kind];
112
+ export function c4TypeLine(kind, technology, std) {
113
+ const word = typeWord(kind, std);
66
114
  const techn = (technology ?? '').replaceAll(/\s+/g, ' ').trim();
67
115
  return techn ? `[${word}: ${techn}]` : `[${word}]`;
68
116
  }
@@ -82,6 +130,22 @@ export function c4TypeLine(kind, technology) {
82
130
  * instead can only ever strip a word the notation itself writes.
83
131
  */
84
132
  const KNOWN_TYPE_WORDS = new Set(Object.values(C4_TYPE_WORD).map(word => word.toLowerCase()));
133
+ /**
134
+ * {@link KNOWN_TYPE_WORDS}, plus the host-resolved word of every kind when
135
+ * `std` is given — so a line seeded in a translated host (`c4TypeLine(kind,
136
+ * …, std)` at placement, or after a commit the watcher rebuilt) is still
137
+ * recognised as carrying THE NOTATION'S word and not a technology typed on
138
+ * its own. With no `std` this is exactly {@link KNOWN_TYPE_WORDS}.
139
+ */
140
+ function knownTypeWords(std) {
141
+ if (!std)
142
+ return KNOWN_TYPE_WORDS;
143
+ const words = new Set(KNOWN_TYPE_WORDS);
144
+ for (const kind of Object.keys(C4_TYPE_WORD)) {
145
+ words.add(typeWord(kind, std).toLowerCase());
146
+ }
147
+ return words;
148
+ }
85
149
  /**
86
150
  * The technology an author stated in a type line, whatever shape they left it
87
151
  * in — `''` when they stated none.
@@ -99,8 +163,12 @@ const KNOWN_TYPE_WORDS = new Set(Object.values(C4_TYPE_WORD).map(word => word.to
99
163
  * https://x/docs → https://x/docs a colon that is NOT a prefix
100
164
  * ```
101
165
  *
102
- * Pure and `std`-free, so the commit hook, the exporter and a test all read the
103
- * same line the same way.
166
+ * Pure and total; `std` is OPTIONAL, exactly like {@link c4TypeLine} —
167
+ * without it this reads a line the same way it always has, and with it the
168
+ * vocabulary the `<word>:` prefix is matched against ALSO recognises the
169
+ * host's own resolved word ({@link knownTypeWords}), so a line seeded or
170
+ * normalized in a translated host still gives back the right technology
171
+ * rather than the whole line.
104
172
  *
105
173
  * It deliberately does NOT know about the creation placeholder: a node created
106
174
  * and never touched carries `[Container: technology]`, and reading that as "no
@@ -109,18 +177,19 @@ const KNOWN_TYPE_WORDS = new Set(Object.values(C4_TYPE_WORD).map(word => word.to
109
177
  * placeholder is a question for whoever asks what the element STATES, which is
110
178
  * the exporter, and it is answered there.
111
179
  */
112
- export function technologyOfTypeLine(text) {
180
+ export function technologyOfTypeLine(text, std) {
113
181
  const flat = (text ?? '')
114
182
  .replaceAll(/[[\]]/g, ' ')
115
183
  .replaceAll(/\s+/g, ' ')
116
184
  .trim();
117
185
  const colon = flat.indexOf(':');
186
+ const words = knownTypeWords(std);
118
187
  // No colon at all: the line is either the bare notation word — which states no
119
188
  // technology — or it is the technology, typed on its own.
120
189
  if (colon < 0)
121
- return KNOWN_TYPE_WORDS.has(flat.toLowerCase()) ? '' : flat;
190
+ return words.has(flat.toLowerCase()) ? '' : flat;
122
191
  const head = flat.slice(0, colon).trim().toLowerCase();
123
- if (!KNOWN_TYPE_WORDS.has(head))
192
+ if (!words.has(head))
124
193
  return flat;
125
194
  return flat.slice(colon + 1).trim();
126
195
  }
@@ -134,9 +203,15 @@ export function technologyOfTypeLine(text) {
134
203
  *
135
204
  * Idempotent by construction — `normalize(normalize(x)) === normalize(x)` —
136
205
  * which is what lets it run on every commit without a guard.
206
+ *
207
+ * `std` is OPTIONAL and, when given, is threaded to both halves: the
208
+ * technology is read back recognising the host's own word
209
+ * ({@link technologyOfTypeLine}) and the line is rebuilt with it
210
+ * ({@link c4TypeLine}) — see `C4TypeLineWatcher`, the one real caller that
211
+ * has an `std` to give it.
137
212
  */
138
- export function normalizeC4TypeLine(kind, rawText) {
139
- return c4TypeLine(kind, technologyOfTypeLine(rawText));
213
+ export function normalizeC4TypeLine(kind, rawText, std) {
214
+ return c4TypeLine(kind, technologyOfTypeLine(rawText, std), std);
140
215
  }
141
216
  /* ── What a fresh element says before anybody writes on it ─────────────── */
142
217
  /**
@@ -147,6 +222,31 @@ export function normalizeC4TypeLine(kind, rawText) {
147
222
  * {@link C4_TYPE_PLACEHOLDER}.
148
223
  */
149
224
  export const TYPE_TECHNOLOGY_PLACEHOLDER = 'technology';
225
+ /**
226
+ * {@link TYPE_TECHNOLOGY_PLACEHOLDER}'s own key — see the long comment on
227
+ * {@link C4_TYPE_WORD_KEY} for why this lot's shipped French proposal keeps
228
+ * it identical to English.
229
+ */
230
+ export const C4_TECHNOLOGY_PLACEHOLDER_KEY = 'com.labre.c4.type.technology-placeholder';
231
+ /** The word an `std` (when given) resolves the technology prompt to. */
232
+ function technologyPlaceholderWord(std) {
233
+ return std
234
+ ? translateKey(std, C4_TECHNOLOGY_PLACEHOLDER_KEY, TYPE_TECHNOLOGY_PLACEHOLDER)
235
+ : TYPE_TECHNOLOGY_PLACEHOLDER;
236
+ }
237
+ /**
238
+ * Every wording this file writes onto the canvas, for `translations.ts`'s
239
+ * manifest contribution — the four distinct type words plus the technology
240
+ * prompt, all `seed` (written into the document once, at placement or at a
241
+ * commit the watcher normalizes).
242
+ */
243
+ export const C4_TYPE_LINE_WORDINGS = [
244
+ [C4_TYPE_WORD_KEY.person, C4_TYPE_WORD.person],
245
+ [C4_TYPE_WORD_KEY.system, C4_TYPE_WORD.system],
246
+ [C4_TYPE_WORD_KEY.container, C4_TYPE_WORD.container],
247
+ [C4_TYPE_WORD_KEY.component, C4_TYPE_WORD.component],
248
+ [C4_TECHNOLOGY_PLACEHOLDER_KEY, TYPE_TECHNOLOGY_PLACEHOLDER],
249
+ ];
150
250
  /**
151
251
  * Which kinds announce a technology in their type line AT ALL.
152
252
  *
@@ -190,6 +290,17 @@ export const C4_TYPE_PLACEHOLDER = Object.fromEntries(Object.keys(C4_TYPE_WORD).
190
290
  kind,
191
291
  c4TypeLine(kind, C4_TYPE_TAKES_TECHNOLOGY[kind] ? TYPE_TECHNOLOGY_PLACEHOLDER : undefined),
192
292
  ]));
293
+ /**
294
+ * {@link C4_TYPE_PLACEHOLDER}, resolved through `std` when given — the
295
+ * placement site (`createC4Node`, `actions.ts`) seeds a fresh element with
296
+ * this rather than the English-only constant, so a component drawn in a
297
+ * translated host starts in that language, like every other seed
298
+ * (`docs/adr/0023`). With no `std` this returns exactly
299
+ * `C4_TYPE_PLACEHOLDER[kind]`.
300
+ */
301
+ export function c4TypePlaceholder(kind, std) {
302
+ return c4TypeLine(kind, C4_TYPE_TAKES_TECHNOLOGY[kind] ? technologyPlaceholderWord(std) : undefined, std);
303
+ }
193
304
  /* ── What the line becomes when the shape becomes something else ───────── */
194
305
  /**
195
306
  * The type line a component should carry once its shape has morphed from `from`
@@ -232,13 +343,19 @@ export const C4_TYPE_PLACEHOLDER = Object.fromEntries(Object.keys(C4_TYPE_WORD).
232
343
  *
233
344
  * Pure, total and `std`-free, like everything else in this file.
234
345
  */
235
- export function c4MorphedTypeLine(from, to, rawText) {
346
+ export function c4MorphedTypeLine(from, to, rawText, std) {
236
347
  const text = (rawText ?? '').trim();
237
348
  if (!text)
238
349
  return null;
239
- if (text === C4_TYPE_PLACEHOLDER[from])
240
- return C4_TYPE_PLACEHOLDER[to];
241
- if (text !== normalizeC4TypeLine(from, text))
350
+ // The untouched prompt, in EITHER language: the English literal (a document
351
+ // seeded before these keys existed, or with no host) or the host's own
352
+ // resolved wording (`c4TypePlaceholder`, which equals the English constant
353
+ // when `std` is absent) — see the module docstring.
354
+ if (text === C4_TYPE_PLACEHOLDER[from] ||
355
+ text === c4TypePlaceholder(from, std)) {
356
+ return c4TypePlaceholder(to, std);
357
+ }
358
+ if (text !== normalizeC4TypeLine(from, text, std))
242
359
  return null;
243
- return c4TypeLine(to, technologyOfTypeLine(text));
360
+ return c4TypeLine(to, technologyOfTypeLine(text, std), std);
244
361
  }
package/dist/view.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { FrameworkBackgroundInteractionExtension, InterchangeExtension, morphToolbarConfig, ReadingProfileExtension, ValidationProfileExtension, ValidationRuleExtension, } from '@formicoidea/labre-core/blocks/surface';
2
+ import { FrameworkPaletteExtension } from '@formicoidea/labre-core/components/color-picker';
2
3
  import { ViewExtensionProvider, } from '@formicoidea/labre-core/ext-loader';
3
4
  import { TemplateCategoryExtension } from '@formicoidea/labre-core/gfx/template';
4
5
  import { ToolbarModuleExtension, toolbarModuleKey, } from '@formicoidea/labre-core/shared/services';
@@ -20,6 +21,7 @@ import { C4_ROLES } from './roles.js';
20
21
  import { C4_RULES } from './rules.js';
21
22
  import { c4TemplateCategory } from './templates.js';
22
23
  import { c4BoardToolbarExtension, c4BoardToolingToolbarExtension, } from './toolbar/config.js';
24
+ import { C4_FRAMEWORK_PALETTE } from './toolbar/palette.js';
23
25
  import { c4SeniorTool } from './toolbar/senior-tool.js';
24
26
  /**
25
27
  * C4 rendering — ALWAYS registered, independent of any flag. Boards, boundaries
@@ -112,6 +114,11 @@ export class C4ViewExtension extends ViewExtensionProvider {
112
114
  context.register(ReadingProfileExtension(reading));
113
115
  }
114
116
  context.register(c4SeniorTool);
117
+ // C4's page of the colour pickers' carousel (`docs/adr/0027`). Here,
118
+ // beside the senior tool, because offering hues is TOOLING — the colours
119
+ // already painted on a stored diagram are content and do not move
120
+ // (`docs/adr/0009`).
121
+ context.register(FrameworkPaletteExtension(C4_FRAMEWORK_PALETTE));
115
122
  // The Templates-panel category — tooling, so it goes with the flag (#244).
116
123
  context.register(TemplateCategoryExtension(c4TemplateCategory));
117
124
  context.register(CommandExtension(c4Commands, c4CommandIcons));
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@formicoidea/labre-framework-c4",
3
3
  "description": "Labre c4 framework for @formicoidea/labre-core.",
4
- "version": "0.40.0",
4
+ "version": "0.42.0",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "author": "lajola",
@@ -31,8 +31,8 @@
31
31
  "dist"
32
32
  ],
33
33
  "dependencies": {
34
- "@formicoidea/labre-core": "0.40.0",
35
- "@formicoidea/labre-ddd-shared": "0.40.0",
34
+ "@formicoidea/labre-core": "0.42.0",
35
+ "@formicoidea/labre-ddd-shared": "0.42.0",
36
36
  "lit": "^3.2.0"
37
37
  }
38
38
  }
package/dist/legend.d.ts DELETED
@@ -1,37 +0,0 @@
1
- import { type AutoLegendSpec } from '@formicoidea/labre-ddd-shared';
2
- /**
3
- * What the C4 board's automatic legend can say — a TABLE, and nothing else: the
4
- * scan, the placement and the box are `createAutoLegend`'s job, shared with the
5
- * three DDD boards.
6
- *
7
- * Every row is DERIVED twice over: its WORDING from the role vocabulary's own
8
- * `labelFallback` ({@link roleLabel}), and its COLOUR from the very palette the
9
- * creation site paints with ({@link NODE_PALETTE}). Neither is restated here, so
10
- * renaming a role renames its legend row and restyling the pack restyles its
11
- * swatches — which is what keeps a legend a description of the board rather than
12
- * a second opinion about it.
13
- *
14
- * ## Why `exact` on the container, and only there
15
- *
16
- * `c4:database` specialises `c4:container`, so an inclusive entry on the parent
17
- * would put a "Container" row — with the container's blue square — on a board
18
- * carrying nothing but cylinders. The row would name a shape that is nowhere on
19
- * the diagram. EDGY's four base kinds hit this first and `AutoLegendEntry.exact`
20
- * exists for it: base and specialisation become two separate statements, each
21
- * listed when it is the thing the user actually drew.
22
- *
23
- * The other four element roles are childless, so `exact` would change nothing
24
- * for them and is left off — the flag marks the places where the distinction is
25
- * real.
26
- *
27
- * ## What is missing, and why
28
- *
29
- * `mobile` and `browser` get no row of their own: they carry `c4:container`,
30
- * because a phone app and a single-page app are containers with a picture
31
- * (`roles.ts`). Detection is by role and only by role, so a board of nothing but
32
- * mobile apps lists "Container" — which is the honest answer about what the
33
- * boxes MEAN. `person-ext` and `system-ext` collapse the same way onto person
34
- * and system, for the same reason: the grey says "out of scope", not "a
35
- * different sort of thing".
36
- */
37
- export declare const C4_AUTO_LEGEND: AutoLegendSpec;