@formicoidea/labre-framework-c4 0.34.1 → 0.35.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.
@@ -19,14 +19,35 @@ import type { FrameworkBackgroundDef } from '@formicoidea/labre-core/blocks/surf
19
19
  * `c4:board` is what a rule frames its subjects against, and the title is what
20
20
  * says which of the four levels this particular sheet is drawing.
21
21
  *
22
- * ## Why the title is declared as a zone label
22
+ * ## Why the title is declared as a BAND label
23
23
  *
24
24
  * The primitive knows three places words can come from: a side band's label, a
25
- * zone's label and an axis' title (`backgroundTexts`). A board has no band and
26
- * no axis, so a single full-plot zone — no fill, no tint, nothing painted but
27
- * its name — is what carries the title. That keeps it on the ONE walk both the
28
- * renderer and the hit tester use, which is what makes the words the user
29
- * double-clicks the same words they see (`C4BoardView`).
25
+ * zone's label and an axis' title (`backgroundTexts`). The title used to be a
26
+ * zone's, on the reasoning that a board has no band — a single full-plot zone
27
+ * that painted nothing and existed only to carry a name.
28
+ *
29
+ * It has a band now, and the band is the honest carrier. The deep top margin
30
+ * was ALWAYS the header strip; painting it says so, and once it is painted the
31
+ * name written in it is the band's name in exactly the sense the BPMN pool's
32
+ * participant name is. Nothing else moved: `margin.top` is the same number
33
+ * under its own name (`C4_BOARD_TITLE_BAND_HEIGHT`, declared beside the model
34
+ * that hit-tests against it), the anchor is the same anchor, and the words land
35
+ * on the same pixels — they are simply reached through the ONE walk the
36
+ * renderer and the hit tester share, one entry earlier in it.
37
+ *
38
+ * The zone went with the label it carried. A zone that tints nothing and names
39
+ * nothing is a region the audit reports and no reader can see; the board's one
40
+ * region is the board, which is what the frame already says.
41
+ *
42
+ * ## Why the band is painted at all
43
+ *
44
+ * Because a background is picked by its border and by nothing else (issue
45
+ * #194), and the recette of 02/09/2026 found the consequence: the title had
46
+ * become unreachable. A single click on it selected nothing, and the only thing
47
+ * still answering a double-click was the tight box of the drawn glyphs. The
48
+ * carve-out that fixes it is in the model (`C4BoardElementModel.includesPoint`)
49
+ * — and a strip that is clickable has to be VISIBLE, or it is a target nobody
50
+ * can aim at. So the band is painted for the same reason it is picked.
30
51
  */
31
52
  export declare const C4_BOARD_BACKGROUND: FrameworkBackgroundDef;
32
53
  /**
@@ -1,4 +1,4 @@
1
- import { BOARD_BORDER_WIDTH, BOARD_CARD_BORDER, BOARD_CARD_FILL, BOARD_CORNER_RADIUS, BOARD_MARGIN, BOARD_REF_HEIGHT, BOARD_REF_WIDTH, BOARD_TITLE_COLOR, BOARD_TITLE_FONT_SIZE, BOARD_TITLE_MARGIN, BOUNDARY_CORNER_RADIUS, BOUNDARY_DASH, BOUNDARY_MARGIN, BOUNDARY_NAME_COLOR, BOUNDARY_NAME_FONT_SIZE, BOUNDARY_NAME_INSET, BOUNDARY_REF_HEIGHT, BOUNDARY_REF_WIDTH, BOUNDARY_STROKE, BOUNDARY_TYPE_FONT_SIZE, BOUNDARY_TYPE_STEP, BOUNDARY_WIDTH, FONT_FAMILY, } from './consts.js';
1
+ import { BOARD_BAND_FILL, BOARD_BORDER_WIDTH, BOARD_CARD_BORDER, BOARD_CARD_FILL, BOARD_CORNER_RADIUS, BOARD_MARGIN, BOARD_REF_HEIGHT, BOARD_REF_WIDTH, BOARD_TITLE_COLOR, BOARD_TITLE_FONT_SIZE, BOUNDARY_CORNER_RADIUS, BOUNDARY_DASH, BOUNDARY_MARGIN, BOUNDARY_NAME_COLOR, BOUNDARY_NAME_FONT_SIZE, BOUNDARY_NAME_INSET, BOUNDARY_REF_HEIGHT, BOUNDARY_REF_WIDTH, BOUNDARY_STROKE, BOUNDARY_TYPE_FONT_SIZE, BOUNDARY_TYPE_STEP, BOUNDARY_WIDTH, C4_BOARD_TITLE_BAND_HEIGHT, FONT_FAMILY, } from './consts.js';
2
2
  import { C4_ROLE } from './roles.js';
3
3
  /**
4
4
  * The two C4 frames, DECLARED (the `FrameworkBackgroundDef` primitive).
@@ -21,14 +21,35 @@ import { C4_ROLE } from './roles.js';
21
21
  * `c4:board` is what a rule frames its subjects against, and the title is what
22
22
  * says which of the four levels this particular sheet is drawing.
23
23
  *
24
- * ## Why the title is declared as a zone label
24
+ * ## Why the title is declared as a BAND label
25
25
  *
26
26
  * The primitive knows three places words can come from: a side band's label, a
27
- * zone's label and an axis' title (`backgroundTexts`). A board has no band and
28
- * no axis, so a single full-plot zone — no fill, no tint, nothing painted but
29
- * its name — is what carries the title. That keeps it on the ONE walk both the
30
- * renderer and the hit tester use, which is what makes the words the user
31
- * double-clicks the same words they see (`C4BoardView`).
27
+ * zone's label and an axis' title (`backgroundTexts`). The title used to be a
28
+ * zone's, on the reasoning that a board has no band — a single full-plot zone
29
+ * that painted nothing and existed only to carry a name.
30
+ *
31
+ * It has a band now, and the band is the honest carrier. The deep top margin
32
+ * was ALWAYS the header strip; painting it says so, and once it is painted the
33
+ * name written in it is the band's name in exactly the sense the BPMN pool's
34
+ * participant name is. Nothing else moved: `margin.top` is the same number
35
+ * under its own name (`C4_BOARD_TITLE_BAND_HEIGHT`, declared beside the model
36
+ * that hit-tests against it), the anchor is the same anchor, and the words land
37
+ * on the same pixels — they are simply reached through the ONE walk the
38
+ * renderer and the hit tester share, one entry earlier in it.
39
+ *
40
+ * The zone went with the label it carried. A zone that tints nothing and names
41
+ * nothing is a region the audit reports and no reader can see; the board's one
42
+ * region is the board, which is what the frame already says.
43
+ *
44
+ * ## Why the band is painted at all
45
+ *
46
+ * Because a background is picked by its border and by nothing else (issue
47
+ * #194), and the recette of 02/09/2026 found the consequence: the title had
48
+ * become unreachable. A single click on it selected nothing, and the only thing
49
+ * still answering a double-click was the tight box of the drawn glyphs. The
50
+ * carve-out that fixes it is in the model (`C4BoardElementModel.includesPoint`)
51
+ * — and a strip that is clickable has to be VISIBLE, or it is a target nobody
52
+ * can aim at. So the band is painted for the same reason it is picked.
32
53
  */
33
54
  export const C4_BOARD_BACKGROUND = {
34
55
  type: 'c4Board',
@@ -41,44 +62,21 @@ export const C4_BOARD_BACKGROUND = {
41
62
  height: BOARD_REF_HEIGHT,
42
63
  lockAspectRatio: false,
43
64
  resizable: true,
44
- // Only the top margin is deep: that is where the title is written.
65
+ // Only the top margin is deep: it IS the title band, and the band's height
66
+ // is the margin it covers rather than a second number beside it.
45
67
  margin: {
46
- top: BOARD_TITLE_MARGIN,
68
+ top: C4_BOARD_TITLE_BAND_HEIGHT,
47
69
  right: BOARD_MARGIN,
48
70
  bottom: BOARD_MARGIN,
49
71
  left: BOARD_MARGIN,
50
72
  },
51
73
  },
52
- zones: [
53
- {
54
- id: 'title',
55
- // The whole plot, and nothing painted over it: this zone exists to carry
56
- // a label, not to tint a region. Reported by the audit as one zone
57
- // covering the board, which is the honest answer — a C4 board has exactly
58
- // one region and it is the board.
59
- rect: { x: 0, y: 0, w: 1, h: 1 },
60
- label: {
61
- id: 'name',
62
- // The user's own words, and only those: a diagram is titled by whoever
63
- // draws it, so there is no vocabulary to fall back to and no `labelKey`
64
- // to declare. Same call the BPMN pool's participant name makes.
65
- prop: 'name',
66
- // `y: 0` is the top of the plot; a negative `dy` walks back UP into the
67
- // title margin, where the words are written.
68
- anchor: { x: 0, y: 0, dy: -BOARD_TITLE_MARGIN / 3 },
69
- style: {
70
- size: BOARD_TITLE_FONT_SIZE,
71
- weight: 600,
72
- color: '@title',
73
- },
74
- },
75
- },
76
- ],
77
74
  chrome: {
78
75
  fontFamily: FONT_FAMILY,
79
76
  palette: {
80
77
  card: BOARD_CARD_FILL,
81
78
  cardBorder: BOARD_CARD_BORDER,
79
+ band: BOARD_BAND_FILL,
82
80
  title: BOARD_TITLE_COLOR,
83
81
  },
84
82
  surface: {
@@ -89,6 +87,36 @@ export const C4_BOARD_BACKGROUND = {
89
87
  radius: BOARD_CORNER_RADIUS,
90
88
  },
91
89
  },
90
+ sideBands: [
91
+ {
92
+ // The header strip, across the top: the C4 title is written top-left,
93
+ // so the band a reader sees is the sheet's masthead and not a gutter.
94
+ // The pool's is on the left because a participant name runs up its
95
+ // side; the direction is the notation's, the mechanism is the same one.
96
+ side: 'top',
97
+ fill: '@band',
98
+ // The card's own line, so the strip reads as the frame continued rather
99
+ // than as a different KIND of rule drawn across the sheet.
100
+ divider: { color: '@cardBorder', width: BOARD_BORDER_WIDTH },
101
+ label: {
102
+ id: 'name',
103
+ // The user's own words, and only those: a diagram is titled by whoever
104
+ // draws it, so there is no vocabulary to fall back to and no `labelKey`
105
+ // to declare. Same call the BPMN pool's participant name makes.
106
+ prop: 'name',
107
+ // `y: 0` is the top of the plot, i.e. the band's INNER edge; a
108
+ // negative `dy` walks back UP into the band, where the words are
109
+ // written. Unchanged from the day the title was a zone's label — the
110
+ // anchor is in plot ratios either way, so the title has not moved.
111
+ anchor: { x: 0, y: 0, dy: -C4_BOARD_TITLE_BAND_HEIGHT / 3 },
112
+ style: {
113
+ size: BOARD_TITLE_FONT_SIZE,
114
+ weight: 600,
115
+ color: '@title',
116
+ },
117
+ },
118
+ },
119
+ ],
92
120
  },
93
121
  };
94
122
  /* ── The boundary ──────────────────────────────────────────────────────── */
@@ -0,0 +1,42 @@
1
+ import type { BackgroundRect } from '@formicoidea/labre-core/blocks/surface';
2
+ /**
3
+ * Where the C4 board's title band is, in ELEMENT-LOCAL model units.
4
+ *
5
+ * Pure, and lifted out of `element-view.ts` so the answer can be asserted
6
+ * without an editor, a viewport or a canvas around it — the same split
7
+ * `pool-hit.ts` makes for the BPMN pool, and for the same reason.
8
+ *
9
+ * The rectangle is derived from `backgroundPlot`, the very function the renderer
10
+ * paints the band from: nothing here restates a coordinate, so the strip a user
11
+ * aims at cannot drift away from the strip they can see.
12
+ *
13
+ * ponytail: a ROTATED board is not accounted for — the caller converts by
14
+ * subtraction (and, in `C4FrameView`, by de-rotating about the centre first),
15
+ * so every box here assumes an upright board. Same reserve `pool-hit.ts`
16
+ * documents.
17
+ */
18
+ /** What this file needs of a board: its box. */
19
+ export interface C4BoardGeometry {
20
+ deserializedXYWH: readonly number[];
21
+ }
22
+ /**
23
+ * The board's title band, or `null` when there is no room for one.
24
+ *
25
+ * The band IS the top margin, full width, clamped to a board shorter than its
26
+ * own header — the degenerate case the renderer clamps and the model's own
27
+ * carve-out clamps too.
28
+ */
29
+ export declare function c4BoardTitleBand(model: C4BoardGeometry): BackgroundRect | null;
30
+ /**
31
+ * Whether an element-local point is in the board's title band.
32
+ *
33
+ * The WHOLE band, not the tight box of the drawn glyphs. A header strip you may
34
+ * only double-click the eleven characters of is a target that lies about where
35
+ * it is — the same call the pool's participant band makes (`bpmnInPoolTitleBand`).
36
+ *
37
+ * No zoom-grown target here, unlike the pool's 28-unit strip: growing this one
38
+ * would put the rename zone outside the band the MODEL lets a click select, and
39
+ * "what is painted is what is picked" is worth more than the pixels a 56-unit
40
+ * header would gain at 0.2 zoom.
41
+ */
42
+ export declare function c4InBoardTitleBand(model: C4BoardGeometry, local: readonly [number, number]): boolean;
@@ -0,0 +1,41 @@
1
+ import { backgroundPlot } from '@formicoidea/labre-core/blocks/surface';
2
+ import { C4_BOARD_BACKGROUND } from './background.js';
3
+ /**
4
+ * The board's title band, or `null` when there is no room for one.
5
+ *
6
+ * The band IS the top margin, full width, clamped to a board shorter than its
7
+ * own header — the degenerate case the renderer clamps and the model's own
8
+ * carve-out clamps too.
9
+ */
10
+ export function c4BoardTitleBand(model) {
11
+ const [, , w, h] = model.deserializedXYWH;
12
+ if (!(w > 0) || !(h > 0))
13
+ return null;
14
+ const plot = backgroundPlot(C4_BOARD_BACKGROUND, w, h);
15
+ const height = Math.min(plot.y0, h);
16
+ if (!(height > 0))
17
+ return null;
18
+ return { x: 0, y: 0, w, h: height };
19
+ }
20
+ /**
21
+ * Whether an element-local point is in the board's title band.
22
+ *
23
+ * The WHOLE band, not the tight box of the drawn glyphs. A header strip you may
24
+ * only double-click the eleven characters of is a target that lies about where
25
+ * it is — the same call the pool's participant band makes (`bpmnInPoolTitleBand`).
26
+ *
27
+ * No zoom-grown target here, unlike the pool's 28-unit strip: growing this one
28
+ * would put the rename zone outside the band the MODEL lets a click select, and
29
+ * "what is painted is what is picked" is worth more than the pixels a 56-unit
30
+ * header would gain at 0.2 zoom.
31
+ */
32
+ export function c4InBoardTitleBand(model, local) {
33
+ const band = c4BoardTitleBand(model);
34
+ if (!band)
35
+ return false;
36
+ const [lx, ly] = local;
37
+ return (lx >= band.x &&
38
+ lx <= band.x + band.w &&
39
+ ly >= band.y &&
40
+ ly <= band.y + band.h);
41
+ }
package/dist/consts.d.ts CHANGED
@@ -254,18 +254,38 @@ export declare const BOARD_BORDER_WIDTH = 1.5;
254
254
  export declare const BOARD_CORNER_RADIUS = 12;
255
255
  export declare const BOARD_TITLE_FONT_SIZE = 20;
256
256
  export declare const BOARD_TITLE_COLOR = "#262626";
257
+ /**
258
+ * The header strip's own tint — a shade of the card, not a colour of its own.
259
+ *
260
+ * Sober on purpose, and the same intent as the pool's participant band
261
+ * (`POOL_BAND_FILL`, `#f4f4f5`): the band has to be SEEN, because a strip a user
262
+ * cannot see is a target they cannot aim at, and it must not read as a second
263
+ * object sitting on the sheet. A hair lighter than the pool's, which is what it
264
+ * takes to stay quiet under a 20-unit title rather than under a 13-unit one.
265
+ */
266
+ export declare const BOARD_BAND_FILL = "#f7f8fa";
257
267
  /**
258
268
  * The size a fresh board is created at, and the room its furniture takes.
259
269
  *
260
270
  * The Context Map board's own numbers: a C4 diagram is the same kind of object —
261
271
  * a sheet you spread out and add boxes to as the system is discovered — so it
262
272
  * starts at the same size and grows the same way. The top margin is deeper than
263
- * the other three because that is where the title is written.
273
+ * the other three because that is where the title is written — and, since the
274
+ * title band, it IS the title band (`C4_BOARD_TITLE_BAND_HEIGHT`, re-exported
275
+ * below from the model that owns it).
264
276
  */
265
277
  export declare const BOARD_REF_WIDTH = 1400;
266
278
  export declare const BOARD_REF_HEIGHT = 900;
267
279
  export declare const BOARD_MARGIN = 24;
268
- export declare const BOARD_TITLE_MARGIN = 56;
280
+ /**
281
+ * The board's top margin lives in `@labre/affine-model`, beside the board model
282
+ * that hit-tests against it (a board is clickable by its title band as well as
283
+ * by its border — the same carve-out the BPMN pool makes, issues #194 / #197).
284
+ * It is re-exported here so this file stays the one place a reader looks for a
285
+ * board's metrics, and so the declaration keeps reading it from where it always
286
+ * did — under its own name, since a margin that is a painted band is a band.
287
+ */
288
+ export { C4_BOARD_TITLE_BAND_HEIGHT } from '@formicoidea/labre-core/model';
269
289
  /**
270
290
  * The boundary's frame and its name, at the stencil's own values ×2 (`.st20`
271
291
  * and `.st8` in the reference model).
package/dist/consts.js CHANGED
@@ -299,18 +299,38 @@ export const BOARD_BORDER_WIDTH = 1.5;
299
299
  export const BOARD_CORNER_RADIUS = 12;
300
300
  export const BOARD_TITLE_FONT_SIZE = 20;
301
301
  export const BOARD_TITLE_COLOR = '#262626';
302
+ /**
303
+ * The header strip's own tint — a shade of the card, not a colour of its own.
304
+ *
305
+ * Sober on purpose, and the same intent as the pool's participant band
306
+ * (`POOL_BAND_FILL`, `#f4f4f5`): the band has to be SEEN, because a strip a user
307
+ * cannot see is a target they cannot aim at, and it must not read as a second
308
+ * object sitting on the sheet. A hair lighter than the pool's, which is what it
309
+ * takes to stay quiet under a 20-unit title rather than under a 13-unit one.
310
+ */
311
+ export const BOARD_BAND_FILL = '#f7f8fa';
302
312
  /**
303
313
  * The size a fresh board is created at, and the room its furniture takes.
304
314
  *
305
315
  * The Context Map board's own numbers: a C4 diagram is the same kind of object —
306
316
  * a sheet you spread out and add boxes to as the system is discovered — so it
307
317
  * starts at the same size and grows the same way. The top margin is deeper than
308
- * the other three because that is where the title is written.
318
+ * the other three because that is where the title is written — and, since the
319
+ * title band, it IS the title band (`C4_BOARD_TITLE_BAND_HEIGHT`, re-exported
320
+ * below from the model that owns it).
309
321
  */
310
322
  export const BOARD_REF_WIDTH = 1400;
311
323
  export const BOARD_REF_HEIGHT = 900;
312
324
  export const BOARD_MARGIN = 24;
313
- export const BOARD_TITLE_MARGIN = 56;
325
+ /**
326
+ * The board's top margin lives in `@labre/affine-model`, beside the board model
327
+ * that hit-tests against it (a board is clickable by its title band as well as
328
+ * by its border — the same carve-out the BPMN pool makes, issues #194 / #197).
329
+ * It is re-exported here so this file stays the one place a reader looks for a
330
+ * board's metrics, and so the declaration keeps reading it from where it always
331
+ * did — under its own name, since a margin that is a painted band is a band.
332
+ */
333
+ export { C4_BOARD_TITLE_BAND_HEIGHT } from '@formicoidea/labre-core/model';
314
334
  /* ── The boundary ──────────────────────────────────────────────────────── */
315
335
  /**
316
336
  * The boundary's frame and its name, at the stencil's own values ×2 (`.st20`
@@ -1,4 +1,4 @@
1
- import type { FrameworkBackgroundDef } from '@formicoidea/labre-core/blocks/surface';
1
+ import type { BackgroundLabelHit, FrameworkBackgroundDef } from '@formicoidea/labre-core/blocks/surface';
2
2
  import type { C4BoardElementModel, C4BoundaryElementModel } from '@formicoidea/labre-core/model';
3
3
  import type { EditorHost } from '@formicoidea/labre-core/std';
4
4
  import type { PointTestOptions } from '@formicoidea/labre-core/std/gfx';
@@ -33,6 +33,20 @@ declare abstract class C4FrameView<T extends C4BoardElementModel | C4BoundaryEle
33
33
  onDestroyed(): void;
34
34
  /** The editable name label under a MODEL-space point, or null. */
35
35
  private _labelAt;
36
+ /**
37
+ * Which region of the frame aims at the name, given an ELEMENT-LOCAL point.
38
+ *
39
+ * The DRAWN WORDS by default — the box `backgroundLabelHits` derives from the
40
+ * very anchor the renderer paints at, so a label can never be drawn in one
41
+ * place and clicked in another. That is the right answer for a name written
42
+ * INSIDE the plot, over the diagram, where anything wider would swallow
43
+ * clicks meant for the elements around it: the C4 boundary's name sits in its
44
+ * bottom-left corner, and this is its whole story.
45
+ *
46
+ * A frame whose name has a strip of its own overrides this and returns the
47
+ * strip — see {@link C4BoardView}.
48
+ */
49
+ protected _nameAt(hits: readonly BackgroundLabelHit[], lx: number, ly: number): BackgroundLabelHit | null;
36
50
  /**
37
51
  * A frame is SELECTED by its border (`FrameworkBackgroundElementModel`, issue
38
52
  * #194) — but the name written on its card must still receive the
@@ -42,6 +56,11 @@ declare abstract class C4FrameView<T extends C4BoardElementModel | C4BoundaryEle
42
56
  * asks the MODEL, and a framework declares its own gesture zones beside the
43
57
  * code that draws them. Without this the name becomes unrenameable; without
44
58
  * the model change the card swallows every click meant for the nodes on it.
59
+ *
60
+ * The BOARD's band is in both answers — the model picks it and this widens
61
+ * nothing over it — so on that frame the fallback below only ever fires for a
62
+ * point the model already took. It is the BOUNDARY that still needs it: its
63
+ * name is written inside the plot, where nothing is selectable.
45
64
  */
46
65
  includesPoint(x: number, y: number, options: PointTestOptions, host: EditorHost): boolean;
47
66
  private _onDblClick;
@@ -53,10 +72,25 @@ declare abstract class C4FrameView<T extends C4BoardElementModel | C4BoundaryEle
53
72
  private _openEditor;
54
73
  private _closeEditor;
55
74
  }
56
- /** The sheet a C4 diagram is drawn on. Double-click its title to rename it. */
75
+ /**
76
+ * The sheet a C4 diagram is drawn on. Double-click ANYWHERE in its title band
77
+ * to rename it.
78
+ *
79
+ * The band and not the words, which is the whole of what this subclass changes.
80
+ * A header strip you may only double-click the eleven characters of is a target
81
+ * that lies about where it is — the same call the BPMN pool's participant band
82
+ * makes, and now the same geometry: the strip that is painted is the strip a
83
+ * single click selects (`C4BoardElementModel.includesPoint`) and the strip a
84
+ * double-click renames.
85
+ *
86
+ * `hits` still supplies the WORDS the editor opens on, so a board showing its
87
+ * default title opens on that title rather than on an empty box — only the
88
+ * region grew.
89
+ */
57
90
  export declare class C4BoardView extends C4FrameView<C4BoardElementModel> {
58
91
  static type: string;
59
92
  protected get def(): FrameworkBackgroundDef;
93
+ protected _nameAt(hits: readonly BackgroundLabelHit[], lx: number, ly: number): BackgroundLabelHit | null;
60
94
  }
61
95
  /** The dashed frame drawn round part of one. Double-click its name to rename. */
62
96
  export declare class C4BoundaryView extends C4FrameView<C4BoundaryElementModel> {
@@ -3,6 +3,7 @@ import { TranslationProvider } from '@formicoidea/labre-core/shared/services';
3
3
  import { rotatePoint } from '@formicoidea/labre-core/global/gfx';
4
4
  import { GfxElementModelView } from '@formicoidea/labre-core/std/gfx';
5
5
  import { C4_BOARD_BACKGROUND, C4_BOUNDARY_BACKGROUND } from './background.js';
6
+ import { c4InBoardTitleBand } from './board-hit.js';
6
7
  /**
7
8
  * The one gesture the two C4 frames carry: a double-click on the name edits it
8
9
  * in place.
@@ -51,7 +52,24 @@ class C4FrameView extends GfxElementModelView {
51
52
  lx = ux - bx;
52
53
  ly = uy - by;
53
54
  }
54
- const hit = hitTestBackgroundLabel(backgroundLabelHits(this.def, this.model, w, h, this.gfx.std.getOptional(TranslationProvider)), lx, ly);
55
+ const hits = backgroundLabelHits(this.def, this.model, w, h, this.gfx.std.getOptional(TranslationProvider));
56
+ return this._nameAt(hits, lx, ly);
57
+ }
58
+ /**
59
+ * Which region of the frame aims at the name, given an ELEMENT-LOCAL point.
60
+ *
61
+ * The DRAWN WORDS by default — the box `backgroundLabelHits` derives from the
62
+ * very anchor the renderer paints at, so a label can never be drawn in one
63
+ * place and clicked in another. That is the right answer for a name written
64
+ * INSIDE the plot, over the diagram, where anything wider would swallow
65
+ * clicks meant for the elements around it: the C4 boundary's name sits in its
66
+ * bottom-left corner, and this is its whole story.
67
+ *
68
+ * A frame whose name has a strip of its own overrides this and returns the
69
+ * strip — see {@link C4BoardView}.
70
+ */
71
+ _nameAt(hits, lx, ly) {
72
+ const hit = hitTestBackgroundLabel(hits, lx, ly);
55
73
  return hit && hit.prop === 'name' ? hit : null;
56
74
  }
57
75
  /**
@@ -63,6 +81,11 @@ class C4FrameView extends GfxElementModelView {
63
81
  * asks the MODEL, and a framework declares its own gesture zones beside the
64
82
  * code that draws them. Without this the name becomes unrenameable; without
65
83
  * the model change the card swallows every click meant for the nodes on it.
84
+ *
85
+ * The BOARD's band is in both answers — the model picks it and this widens
86
+ * nothing over it — so on that frame the fallback below only ever fires for a
87
+ * point the model already took. It is the BOUNDARY that still needs it: its
88
+ * name is written inside the plot, where nothing is selectable.
66
89
  */
67
90
  includesPoint(x, y, options, host) {
68
91
  if (super.includesPoint(x, y, options, host))
@@ -150,12 +173,32 @@ class C4FrameView extends GfxElementModelView {
150
173
  }
151
174
  }
152
175
  }
153
- /** The sheet a C4 diagram is drawn on. Double-click its title to rename it. */
176
+ /**
177
+ * The sheet a C4 diagram is drawn on. Double-click ANYWHERE in its title band
178
+ * to rename it.
179
+ *
180
+ * The band and not the words, which is the whole of what this subclass changes.
181
+ * A header strip you may only double-click the eleven characters of is a target
182
+ * that lies about where it is — the same call the BPMN pool's participant band
183
+ * makes, and now the same geometry: the strip that is painted is the strip a
184
+ * single click selects (`C4BoardElementModel.includesPoint`) and the strip a
185
+ * double-click renames.
186
+ *
187
+ * `hits` still supplies the WORDS the editor opens on, so a board showing its
188
+ * default title opens on that title rather than on an empty box — only the
189
+ * region grew.
190
+ */
154
191
  export class C4BoardView extends C4FrameView {
155
192
  static { this.type = 'c4Board'; }
156
193
  get def() {
157
194
  return C4_BOARD_BACKGROUND;
158
195
  }
196
+ _nameAt(hits, lx, ly) {
197
+ const name = hits.find(hit => hit.prop === 'name');
198
+ if (!name)
199
+ return null;
200
+ return c4InBoardTitleBand(this.model, [lx, ly]) ? name : null;
201
+ }
159
202
  }
160
203
  /** The dashed frame drawn round part of one. Double-click its name to rename. */
161
204
  export class C4BoundaryView extends C4FrameView {
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { C4_BOARD_BACKGROUND, C4_BOUNDARY_BACKGROUND } from './background.js';
2
- export { BOUNDARY_LABEL, type C4NodePaint, DESCRIPTION_PLACEHOLDER, NODE_LABEL, NODE_PALETTE, NODE_RADIUS, NODE_SIZE, NODE_STROKE_WIDTH, RELATIONSHIP_STROKE, RELATIONSHIP_WIDTH, } from './consts.js';
2
+ export { BOUNDARY_LABEL, C4_BOARD_TITLE_BAND_HEIGHT, type C4NodePaint, DESCRIPTION_PLACEHOLDER, NODE_LABEL, NODE_PALETTE, NODE_RADIUS, NODE_SIZE, NODE_STROKE_WIDTH, RELATIONSHIP_STROKE, RELATIONSHIP_WIDTH, } from './consts.js';
3
3
  export { C4_TYPE_PLACEHOLDER, C4_TYPE_TAKES_TECHNOLOGY, C4_TYPE_WORD, c4MorphedTypeLine, c4TypeLine, normalizeC4TypeLine, technologyOfTypeLine, TYPE_TECHNOLOGY_PLACEHOLDER, } from './type-line.js';
4
4
  export { c4MorphClears, c4MorphProps, c4NodeProps, GLYPH_BODY_KINDS, } from './presets.js';
5
5
  export { C4_MORPH_FAMILIES, C4_MORPH_SPEC, c4MorphedTitle, c4NodeOfComponent, } from './morph.js';
package/dist/index.js CHANGED
@@ -3,7 +3,11 @@
3
3
  // on why a framework declares rather than draws). Exported so a host — and the
4
4
  // audit's own consumers — can answer those questions without a canvas.
5
5
  export { C4_BOARD_BACKGROUND, C4_BOUNDARY_BACKGROUND } from './background.js';
6
- export { BOUNDARY_LABEL, DESCRIPTION_PLACEHOLDER, NODE_LABEL, NODE_PALETTE, NODE_RADIUS, NODE_SIZE, NODE_STROKE_WIDTH, RELATIONSHIP_STROKE, RELATIONSHIP_WIDTH, } from './consts.js';
6
+ export { BOUNDARY_LABEL,
7
+ // The header strip's height, owned by the model that hit-tests against it and
8
+ // re-exported here with the rest of a board's metrics: a host laying a board
9
+ // out needs to know how much of the top of the sheet is furniture.
10
+ C4_BOARD_TITLE_BAND_HEIGHT, DESCRIPTION_PLACEHOLDER, NODE_LABEL, NODE_PALETTE, NODE_RADIUS, NODE_SIZE, NODE_STROKE_WIDTH, RELATIONSHIP_STROKE, RELATIONSHIP_WIDTH, } from './consts.js';
7
11
  // The middle tier of an element's label, SEMI-DERIVED: the word from the kind,
8
12
  // the technology from the author. Pure and total over the nine kinds and over
9
13
  // every string an author can type, so a host, a rule or a reading can name an
@@ -39,6 +39,11 @@ const ChevronDownIcon = html `<svg
39
39
  >
40
40
  <path d="M6 9l6 6 6-6" />
41
41
  </svg>`;
42
+ /**
43
+ * The tick on the level in force — inline, this package carrying no icon
44
+ * dependency. Rendered at the END of the row and only when selected, the row
45
+ * itself wearing `data-option` for the native geometry and the primary colour.
46
+ */
42
47
  const CheckIcon = html `<svg
43
48
  width="20"
44
49
  height="20"
@@ -291,6 +296,7 @@ const levelPickerAction = {
291
296
  const options = C4_BOARD_LEVEL_MENU.options.map(option => {
292
297
  const selected = option.level === board.level;
293
298
  return html `<editor-menu-action
299
+ data-option
294
300
  data-testid="c4-level-option"
295
301
  data-level=${option.level ?? 'none'}
296
302
  data-selected=${selected ? 'true' : nothing}
@@ -298,8 +304,8 @@ const levelPickerAction = {
298
304
  aria-pressed=${selected}
299
305
  @click=${() => pickLevel(ctx, option)}
300
306
  >
301
- ${selected ? CheckIcon : html `<span style="width: 20px;"></span>`}
302
307
  <span class="label">${wordsFor(option)}</span>
308
+ ${selected ? CheckIcon : nothing}
303
309
  </editor-menu-action>`;
304
310
  });
305
311
  return html `<editor-menu-button
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.34.1",
4
+ "version": "0.35.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.34.1",
35
- "@formicoidea/labre-ddd-shared": "0.34.1",
34
+ "@formicoidea/labre-core": "0.35.0",
35
+ "@formicoidea/labre-ddd-shared": "0.35.0",
36
36
  "lit": "^3.2.0"
37
37
  }
38
38
  }