@formicoidea/labre-core 0.57.0 → 0.59.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.
Files changed (178) hide show
  1. package/architecture/code.c4 +94 -65
  2. package/architecture/domain.c4 +2510 -0
  3. package/architecture/likec4.config.json +3 -3
  4. package/dist/_pkgs/affine-block-attachment/edgeless-clipboard-config.d.ts +3 -2
  5. package/dist/_pkgs/affine-block-attachment/edgeless-clipboard-config.js +18 -13
  6. package/dist/_pkgs/affine-block-callout/adapters/markdown.js +4 -1
  7. package/dist/_pkgs/affine-block-code/adapters/html.js +1 -1
  8. package/dist/_pkgs/affine-block-code/adapters/markdown/preprocessor.js +13 -1
  9. package/dist/_pkgs/affine-block-code/adapters/notion-html.js +1 -1
  10. package/dist/_pkgs/affine-block-code/code-block.d.ts +17 -2
  11. package/dist/_pkgs/affine-block-code/code-block.js +28 -23
  12. package/dist/_pkgs/affine-block-code/code-toolbar/components/lang-button.js +17 -4
  13. package/dist/_pkgs/affine-block-code/code-toolbar/components/preview-button.js +1 -1
  14. package/dist/_pkgs/affine-block-data-view/data-source.js +12 -5
  15. package/dist/_pkgs/affine-block-edgeless-text/edgeless-clipboard-config.d.ts +3 -2
  16. package/dist/_pkgs/affine-block-edgeless-text/edgeless-clipboard-config.js +8 -5
  17. package/dist/_pkgs/affine-block-image/asset-picker.d.ts +23 -2
  18. package/dist/_pkgs/affine-block-image/asset-picker.js +52 -12
  19. package/dist/_pkgs/affine-block-image/configs/replace-action.d.ts +8 -5
  20. package/dist/_pkgs/affine-block-image/configs/replace-action.js +8 -8
  21. package/dist/_pkgs/affine-block-image/configs/toolbar.js +2 -1
  22. package/dist/_pkgs/affine-block-image/crop/crop-controller.d.ts +15 -0
  23. package/dist/_pkgs/affine-block-image/crop/crop-controller.js +40 -2
  24. package/dist/_pkgs/affine-block-image/crop/crop-math.d.ts +22 -0
  25. package/dist/_pkgs/affine-block-image/crop/crop-math.js +55 -2
  26. package/dist/_pkgs/affine-block-image/crop/crop-overlay.d.ts +17 -3
  27. package/dist/_pkgs/affine-block-image/crop/crop-overlay.js +64 -6
  28. package/dist/_pkgs/affine-block-image/edgeless-clipboard-config.d.ts +3 -2
  29. package/dist/_pkgs/affine-block-image/edgeless-clipboard-config.js +20 -14
  30. package/dist/_pkgs/affine-block-image/image-block.js +1 -0
  31. package/dist/_pkgs/affine-block-image/image-edgeless-block.js +3 -0
  32. package/dist/_pkgs/affine-block-image/index.d.ts +2 -0
  33. package/dist/_pkgs/affine-block-image/index.js +2 -0
  34. package/dist/_pkgs/affine-block-image/replace/commands.d.ts +68 -0
  35. package/dist/_pkgs/affine-block-image/replace/commands.js +200 -0
  36. package/dist/_pkgs/affine-block-image/replace/replace-model.d.ts +44 -0
  37. package/dist/_pkgs/affine-block-image/replace/replace-model.js +168 -0
  38. package/dist/_pkgs/affine-block-image/replace/toolbar.d.ts +21 -0
  39. package/dist/_pkgs/affine-block-image/replace/toolbar.js +159 -0
  40. package/dist/_pkgs/affine-block-image/translations.d.ts +15 -2
  41. package/dist/_pkgs/affine-block-image/translations.js +35 -2
  42. package/dist/_pkgs/affine-block-image/utils.d.ts +7 -0
  43. package/dist/_pkgs/affine-block-image/utils.js +13 -0
  44. package/dist/_pkgs/affine-block-image/view.js +7 -0
  45. package/dist/_pkgs/affine-block-note/components/edgeless-page-block-title.d.ts +14 -5
  46. package/dist/_pkgs/affine-block-note/components/edgeless-page-block-title.js +18 -11
  47. package/dist/_pkgs/affine-block-note/config.d.ts +9 -2
  48. package/dist/_pkgs/affine-block-note/edgeless-clipboard-config.d.ts +3 -2
  49. package/dist/_pkgs/affine-block-note/edgeless-clipboard-config.js +8 -5
  50. package/dist/_pkgs/affine-block-paragraph/adapters/markdown.js +11 -0
  51. package/dist/_pkgs/affine-block-root/commands/invisible-characters-commands.d.ts +17 -0
  52. package/dist/_pkgs/affine-block-root/commands/invisible-characters-commands.js +123 -0
  53. package/dist/_pkgs/affine-block-root/edgeless/clipboard/command.js +82 -65
  54. package/dist/_pkgs/affine-block-root/edgeless/edgeless-keyboard.js +2 -0
  55. package/dist/_pkgs/affine-block-root/index.d.ts +1 -0
  56. package/dist/_pkgs/affine-block-root/index.js +1 -0
  57. package/dist/_pkgs/affine-block-root/translations.d.ts +6 -0
  58. package/dist/_pkgs/affine-block-root/translations.js +10 -0
  59. package/dist/_pkgs/affine-block-root/view.js +6 -1
  60. package/dist/_pkgs/affine-block-surface/commands/index.d.ts +1 -1
  61. package/dist/_pkgs/affine-block-surface/commands/index.js +1 -1
  62. package/dist/_pkgs/affine-block-surface/commands/reassociate-connectors.d.ts +9 -0
  63. package/dist/_pkgs/affine-block-surface/commands/reassociate-connectors.js +21 -12
  64. package/dist/_pkgs/affine-block-surface/extensions/clipboard-config.d.ts +33 -0
  65. package/dist/_pkgs/affine-block-surface/extensions/clipboard-config.js +53 -0
  66. package/dist/_pkgs/affine-block-surface/extensions/reading-toolbar.js +12 -12
  67. package/dist/_pkgs/affine-block-surface/framework-background/background-view.js +2 -1
  68. package/dist/_pkgs/affine-block-surface/framework-background/band-widths.d.ts +104 -0
  69. package/dist/_pkgs/affine-block-surface/framework-background/band-widths.js +156 -0
  70. package/dist/_pkgs/affine-block-surface/framework-background/def.d.ts +87 -4
  71. package/dist/_pkgs/affine-block-surface/framework-background/def.js +26 -7
  72. package/dist/_pkgs/affine-block-surface/framework-background/ellipsis.d.ts +58 -0
  73. package/dist/_pkgs/affine-block-surface/framework-background/ellipsis.js +110 -0
  74. package/dist/_pkgs/affine-block-surface/framework-background/index.d.ts +3 -0
  75. package/dist/_pkgs/affine-block-surface/framework-background/index.js +3 -0
  76. package/dist/_pkgs/affine-block-surface/framework-background/labels.d.ts +12 -2
  77. package/dist/_pkgs/affine-block-surface/framework-background/labels.js +29 -5
  78. package/dist/_pkgs/affine-block-surface/framework-background/measure-text.d.ts +14 -0
  79. package/dist/_pkgs/affine-block-surface/framework-background/measure-text.js +31 -0
  80. package/dist/_pkgs/affine-block-surface/framework-background/renderer.d.ts +14 -1
  81. package/dist/_pkgs/affine-block-surface/framework-background/renderer.js +84 -31
  82. package/dist/_pkgs/affine-block-table/adapters/html.js +26 -14
  83. package/dist/_pkgs/affine-block-table/adapters/utils.d.ts +1 -0
  84. package/dist/_pkgs/affine-block-table/adapters/utils.js +23 -5
  85. package/dist/_pkgs/affine-block-table/header-menu.d.ts +13 -0
  86. package/dist/_pkgs/affine-block-table/header-menu.js +25 -0
  87. package/dist/_pkgs/affine-block-table/selection-controller.d.ts +15 -0
  88. package/dist/_pkgs/affine-block-table/selection-controller.js +62 -0
  89. package/dist/_pkgs/affine-block-table/table-cell-css.d.ts +7 -0
  90. package/dist/_pkgs/affine-block-table/table-cell-css.js +10 -0
  91. package/dist/_pkgs/affine-block-table/table-cell.d.ts +8 -0
  92. package/dist/_pkgs/affine-block-table/table-cell.js +35 -3
  93. package/dist/_pkgs/affine-block-table/table-data-manager.d.ts +14 -0
  94. package/dist/_pkgs/affine-block-table/table-data-manager.js +44 -3
  95. package/dist/_pkgs/affine-block-table/translations.d.ts +14 -0
  96. package/dist/_pkgs/affine-block-table/translations.js +22 -0
  97. package/dist/_pkgs/affine-components/highlight-dropdown-menu/highlight-duotone-icon.js +8 -8
  98. package/dist/_pkgs/affine-components/highlight-dropdown-menu/text-duotone-icon.js +11 -11
  99. package/dist/_pkgs/affine-fragment-doc-title/title-band/title-band.d.ts +3 -1
  100. package/dist/_pkgs/affine-fragment-doc-title/title-band/title-band.js +15 -2
  101. package/dist/_pkgs/affine-gfx-mindmap/toolbar/mindmap-tool-button.d.ts +2 -2
  102. package/dist/_pkgs/affine-gfx-template/builtin/other.d.ts +4 -0
  103. package/dist/_pkgs/affine-gfx-template/builtin/other.js +1 -1
  104. package/dist/_pkgs/affine-gfx-template/builtin/slides/build.d.ts +81 -0
  105. package/dist/_pkgs/affine-gfx-template/builtin/slides/build.js +276 -0
  106. package/dist/_pkgs/affine-gfx-template/builtin/slides/consts.d.ts +126 -0
  107. package/dist/_pkgs/affine-gfx-template/builtin/slides/consts.js +101 -0
  108. package/dist/_pkgs/affine-gfx-template/builtin/slides/index.d.ts +8 -0
  109. package/dist/_pkgs/affine-gfx-template/builtin/slides/index.js +27 -0
  110. package/dist/_pkgs/affine-gfx-template/builtin/slides/masks.d.ts +83 -0
  111. package/dist/_pkgs/affine-gfx-template/builtin/slides/masks.js +674 -0
  112. package/dist/_pkgs/affine-gfx-template/contribute.js +12 -17
  113. package/dist/_pkgs/affine-gfx-template/make-snapshot.d.ts +14 -2
  114. package/dist/_pkgs/affine-gfx-template/make-snapshot.js +6 -2
  115. package/dist/_pkgs/affine-gfx-template/services/template-middlewares.d.ts +12 -0
  116. package/dist/_pkgs/affine-gfx-template/services/template-middlewares.js +48 -0
  117. package/dist/_pkgs/affine-gfx-template/services/template.js +2 -1
  118. package/dist/_pkgs/affine-gfx-template/toolbar/builtin-templates.d.ts +15 -1
  119. package/dist/_pkgs/affine-gfx-template/toolbar/builtin-templates.js +33 -37
  120. package/dist/_pkgs/affine-gfx-template/toolbar/template-type.d.ts +6 -0
  121. package/dist/_pkgs/affine-gfx-template/translations.d.ts +94 -0
  122. package/dist/_pkgs/affine-gfx-template/translations.js +441 -2
  123. package/dist/_pkgs/affine-model/blocks/table/table-model.d.ts +4 -0
  124. package/dist/_pkgs/affine-model/blocks/table/table-model.js +4 -0
  125. package/dist/_pkgs/affine-model/elements/bpmn/node.d.ts +46 -3
  126. package/dist/_pkgs/affine-model/elements/bpmn/node.js +47 -4
  127. package/dist/_pkgs/affine-model/elements/bpmn/pool.d.ts +20 -0
  128. package/dist/_pkgs/affine-model/elements/bpmn/pool.js +33 -4
  129. package/dist/_pkgs/affine-shared/services/edit-props-store.d.ts +3 -16
  130. package/dist/_pkgs/affine-shared/services/edit-props-store.js +16 -16
  131. package/dist/_pkgs/affine-shared/services/editor-setting-service.d.ts +3 -0
  132. package/dist/_pkgs/affine-shared/services/editor-setting-service.js +4 -0
  133. package/dist/_pkgs/affine-shared/services/index.d.ts +1 -0
  134. package/dist/_pkgs/affine-shared/services/index.js +1 -0
  135. package/dist/_pkgs/affine-shared/services/invisible-characters.d.ts +33 -0
  136. package/dist/_pkgs/affine-shared/services/invisible-characters.js +63 -0
  137. package/dist/_pkgs/affine-shared/services/link-preview-service/link-preview-cache.d.ts +0 -9
  138. package/dist/_pkgs/affine-shared/services/link-preview-service/link-preview-cache.js +3 -9
  139. package/dist/_pkgs/affine-shared/services/telemetry-service/lifecycle.d.ts +26 -0
  140. package/dist/_pkgs/affine-shared/services/telemetry-service/telemetry-service.d.ts +4 -3
  141. package/dist/_pkgs/affine-shared/services/telemetry-service/types.d.ts +11 -0
  142. package/dist/_pkgs/affine-shared/utils/index.d.ts +1 -0
  143. package/dist/_pkgs/affine-shared/utils/index.js +1 -0
  144. package/dist/_pkgs/affine-shared/utils/invisible-characters.d.ts +42 -0
  145. package/dist/_pkgs/affine-shared/utils/invisible-characters.js +81 -0
  146. package/dist/_pkgs/affine-widget-edgeless-zoom-toolbar/index.js +26 -26
  147. package/dist/_pkgs/affine-widget-edgeless-zoom-toolbar/zoom-toolbar.js +13 -2
  148. package/dist/_pkgs/affine-widget-keyboard-toolbar/icons.js +98 -98
  149. package/dist/_pkgs/affine-widget-page-dragging-area/index.js +10 -10
  150. package/dist/_pkgs/affine-widget-viewport-overlay/index.js +13 -13
  151. package/dist/_pkgs/data-view/core/utils/menu-title.js +17 -17
  152. package/dist/_pkgs/data-view/view-presets/table/pc/header/column-header.js +6 -0
  153. package/dist/_pkgs/data-view/view-presets/table/pc/table-view-ui-logic.js +5 -2
  154. package/dist/_pkgs/std/clipboard/clipboard.d.ts +15 -1
  155. package/dist/_pkgs/std/clipboard/clipboard.js +18 -0
  156. package/dist/_pkgs/std/inline/components/embed-gap.js +1 -1
  157. package/dist/_pkgs/std/inline/components/v-line.js +1 -1
  158. package/dist/_pkgs/std/inline/components/v-text.d.ts +15 -2
  159. package/dist/_pkgs/std/inline/components/v-text.js +147 -15
  160. package/dist/_pkgs/std/inline/consts.d.ts +5 -0
  161. package/dist/_pkgs/std/inline/consts.js +5 -0
  162. package/dist/_pkgs/std/inline/utils/index.d.ts +1 -0
  163. package/dist/_pkgs/std/inline/utils/index.js +1 -0
  164. package/dist/_pkgs/std/inline/utils/invisible-characters.d.ts +57 -0
  165. package/dist/_pkgs/std/inline/utils/invisible-characters.js +162 -0
  166. package/dist/_pkgs/std/inline/utils/point-conversion.js +3 -3
  167. package/dist/_pkgs/std/inline/utils/text.d.ts +8 -0
  168. package/dist/_pkgs/std/inline/utils/text.js +11 -2
  169. package/dist/_pkgs/store/model/store/crud.d.ts +28 -0
  170. package/dist/_pkgs/store/model/store/crud.js +97 -30
  171. package/dist/_pkgs/store/model/store/query.js +3 -2
  172. package/dist/_pkgs/store/model/store/store.d.ts +21 -0
  173. package/dist/_pkgs/store/model/store/store.js +88 -21
  174. package/dist/_pkgs/store/transformer/transformer.d.ts +46 -0
  175. package/dist/_pkgs/store/transformer/transformer.js +72 -0
  176. package/dist/commands.js +6 -1
  177. package/dist/translations.js +2 -0
  178. package/package.json +3 -4
@@ -1,11 +1,12 @@
1
1
  import { ExportToHtmlIcon, ExportToMarkdownIcon } from '@blocksuite/icons/lit';
2
2
  import { ViewExtensionProvider, } from '../affine-ext-loader/index.js';
3
3
  import { NoteBlockSchema } from '../affine-model/index.js';
4
- import { ToolbarModuleExtension, ViewportElementExtension, } from '../affine-shared/services/index.js';
4
+ import { InvisibleCharacters, ToolbarModuleExtension, ViewportElementExtension, } from '../affine-shared/services/index.js';
5
5
  import { BlockFlavourIdentifier, BlockViewExtension, FlavourExtension, CommandExtension, ShortcutKeymapExtension, } from '../std/index.js';
6
6
  import { literal } from 'lit/static-html.js';
7
7
  import { PageClipboard, ReadOnlyClipboard } from './clipboard/index.js';
8
8
  import { copyLinkCommands } from './commands/copy-link-commands.js';
9
+ import { invisibleCharactersCommands } from './commands/invisible-characters-commands.js';
9
10
  import { pageExportCommands } from './commands/page-export-commands.js';
10
11
  import { pivotCommands } from './commands/pivot-commands.js';
11
12
  import { readingCommands } from './commands/reading-commands.js';
@@ -74,6 +75,7 @@ export class RootViewExtension extends ViewExtensionProvider {
74
75
  ...tagCommands,
75
76
  ...readingCommands,
76
77
  ...copyLinkCommands,
78
+ ...invisibleCharactersCommands,
77
79
  ]),
78
80
  // The page as Markdown or HTML (ADR 0034, amendment of 2026-10-14):
79
81
  // view actions of both views, never gated — the page's own text.
@@ -82,6 +84,9 @@ export class RootViewExtension extends ViewExtensionProvider {
82
84
  'page.export.html': ExportToHtmlIcon(),
83
85
  }),
84
86
  ShortcutKeymapExtension('global'),
87
+ // "Show special characters", this viewer's display choice, read by
88
+ // every `v-text` under the host; both views, previews included.
89
+ InvisibleCharacters,
85
90
  ToolbarModuleExtension({
86
91
  id: BlockFlavourIdentifier(NoteBlockSchema.model.flavour),
87
92
  config: builtinToolbarConfig,
@@ -1,2 +1,2 @@
1
1
  export { autoArrangeElementsCommand, autoResizeElementsCommand, } from './auto-align.js';
2
- export { reassociateConnectorsCommand } from './reassociate-connectors.js';
2
+ export { reassociateConnectors, reassociateConnectorsCommand, } from './reassociate-connectors.js';
@@ -1,2 +1,2 @@
1
1
  export { autoArrangeElementsCommand, autoResizeElementsCommand, } from './auto-align.js';
2
- export { reassociateConnectorsCommand } from './reassociate-connectors.js';
2
+ export { reassociateConnectors, reassociateConnectorsCommand, } from './reassociate-connectors.js';
@@ -1,4 +1,13 @@
1
1
  import type { Command } from '../../std/index.js';
2
+ import type { SurfaceBlockModel } from '../surface-model.js';
3
+ /**
4
+ * Re-point every connector end bound to `oldId` at `newId`, keeping the rest
5
+ * of the endpoint (its `position`). The caller owns the transaction and the
6
+ * undo step: a model converted into another (a bookmark into an embed, a
7
+ * shape into an image, ADR 0044) re-points its connectors before the old one
8
+ * is deleted.
9
+ */
10
+ export declare function reassociateConnectors(surface: Pick<SurfaceBlockModel, 'getConnectors' | 'updateElement'>, oldId: string, newId: string): void;
2
11
  /**
3
12
  * Re-associate bindings for block that have been converted.
4
13
  *
@@ -1,18 +1,12 @@
1
1
  import { GfxControllerIdentifier } from '../../std/gfx/index.js';
2
2
  /**
3
- * Re-associate bindings for block that have been converted.
4
- *
5
- * @param oldId - the old block id
6
- * @param newId - the new block id
3
+ * Re-point every connector end bound to `oldId` at `newId`, keeping the rest
4
+ * of the endpoint (its `position`). The caller owns the transaction and the
5
+ * undo step: a model converted into another (a bookmark into an embed, a
6
+ * shape into an image, ADR 0044) re-points its connectors before the old one
7
+ * is deleted.
7
8
  */
8
- export const reassociateConnectorsCommand = (ctx, next) => {
9
- const { oldId, newId } = ctx;
10
- const gfx = ctx.std.get(GfxControllerIdentifier);
11
- if (!oldId || !newId || !gfx.surface) {
12
- next();
13
- return;
14
- }
15
- const surface = gfx.surface;
9
+ export function reassociateConnectors(surface, oldId, newId) {
16
10
  const connectors = surface.getConnectors(oldId);
17
11
  for (const { id, source, target } of connectors) {
18
12
  if (source.id === oldId) {
@@ -32,5 +26,20 @@ export const reassociateConnectorsCommand = (ctx, next) => {
32
26
  });
33
27
  }
34
28
  }
29
+ }
30
+ /**
31
+ * Re-associate bindings for block that have been converted.
32
+ *
33
+ * @param oldId - the old block id
34
+ * @param newId - the new block id
35
+ */
36
+ export const reassociateConnectorsCommand = (ctx, next) => {
37
+ const { oldId, newId } = ctx;
38
+ const gfx = ctx.std.get(GfxControllerIdentifier);
39
+ if (!oldId || !newId || !gfx.surface) {
40
+ next();
41
+ return;
42
+ }
43
+ reassociateConnectors(gfx.surface, oldId, newId);
35
44
  next();
36
45
  };
@@ -15,6 +15,15 @@ export type ClipboardConfigCreationContext = {
15
15
  */
16
16
  newPresentationIndexes: Map<string, string>;
17
17
  };
18
+ /**
19
+ * A block a paste is ready to write (`EdgelessClipboardConfig.prepare`).
20
+ * `write` is synchronous for a config that follows the contract; a Promise is
21
+ * the legacy `createBlock` behind the default `prepare`.
22
+ */
23
+ export interface PreparedClipboardBlock {
24
+ write(): string | null | Promise<string | null>;
25
+ afterWrite?(): void;
26
+ }
18
27
  export declare const EdgelessClipboardConfigIdentifier: import("../../global/di/types.js").ServiceIdentifier<EdgelessClipboardConfig> & (<U extends EdgelessClipboardConfig = EdgelessClipboardConfig>(variant: import("../../global/di/types.js").ServiceVariant) => import("../../global/di/types.js").ServiceIdentifier<U>);
19
28
  export declare abstract class EdgelessClipboardConfig extends Extension {
20
29
  readonly std: BlockStdScope;
@@ -23,6 +32,30 @@ export declare abstract class EdgelessClipboardConfig extends Extension {
23
32
  get surface(): import("../surface-block.js").SurfaceBlockComponent | null;
24
33
  get crud(): import("./crud-extension.js").EdgelessCRUDExtension;
25
34
  onBlockSnapshotPaste: (snapshot: BlockSnapshot, doc: Store, parent?: string, index?: number) => Promise<string | null>;
35
+ /**
36
+ * `onBlockSnapshotPaste` in two halves, for `prepare`: the snapshot is
37
+ * converted now, the blocks are written by `write`, and the import
38
+ * middlewares run in `afterWrite`.
39
+ */
40
+ prepareBlockSnapshotPaste: (snapshot: BlockSnapshot, doc: Store, parent?: string, index?: number) => Promise<PreparedClipboardBlock | null>;
26
41
  abstract createBlock(snapshot: BlockSnapshot, context: ClipboardConfigCreationContext): string | null | Promise<string | null>;
42
+ /**
43
+ * The paste's two-phase contract (ADR 0042): everything slow (a blob fetch,
44
+ * a snapshot conversion) happens here and writes nothing; the returned
45
+ * `write` adds the block synchronously, because the paste calls every
46
+ * block's `write` and creates every canvas element in ONE transaction, which
47
+ * is one undo step and one update for a peer. `afterWrite` runs in the
48
+ * paste's second transaction, once every pasted model exists.
49
+ *
50
+ * The default defers to `createBlock`, so a config written before the
51
+ * contract keeps working; an async `createBlock` then still writes after
52
+ * the paste's transaction and may cost its own undo step.
53
+ */
54
+ prepare(snapshot: BlockSnapshot, context: ClipboardConfigCreationContext): PreparedClipboardBlock | null | Promise<PreparedClipboardBlock | null>;
55
+ /**
56
+ * `createBlock` for a config that implements `prepare`: prepares, then
57
+ * writes in one transaction of its own.
58
+ */
59
+ protected createPreparedBlock(snapshot: BlockSnapshot, context: ClipboardConfigCreationContext): Promise<string | null>;
27
60
  static setup(di: Container): void;
28
61
  }
@@ -17,6 +17,26 @@ export class EdgelessClipboardConfig extends Extension {
17
17
  const block = await this.std.clipboard.pasteBlockSnapshot(snapshot, doc, parent, index);
18
18
  return block?.id ?? null;
19
19
  };
20
+ /**
21
+ * `onBlockSnapshotPaste` in two halves, for `prepare`: the snapshot is
22
+ * converted now, the blocks are written by `write`, and the import
23
+ * middlewares run in `afterWrite`.
24
+ */
25
+ this.prepareBlockSnapshotPaste = async (snapshot, doc, parent, index) => {
26
+ applyCreationLayer(this.std, snapshot.props);
27
+ const prepared = await this.std.clipboard.prepareBlockSnapshot(snapshot, parent, index);
28
+ if (!prepared)
29
+ return null;
30
+ let flush;
31
+ return {
32
+ write: () => {
33
+ const inserted = prepared.insert(doc);
34
+ flush = inserted?.flushAfterImport;
35
+ return inserted?.id ?? null;
36
+ },
37
+ afterWrite: () => flush?.(),
38
+ };
39
+ };
20
40
  }
21
41
  get surface() {
22
42
  return getSurfaceComponent(this.std);
@@ -24,6 +44,39 @@ export class EdgelessClipboardConfig extends Extension {
24
44
  get crud() {
25
45
  return this.std.get(EdgelessCRUDIdentifier);
26
46
  }
47
+ /**
48
+ * The paste's two-phase contract (ADR 0042): everything slow (a blob fetch,
49
+ * a snapshot conversion) happens here and writes nothing; the returned
50
+ * `write` adds the block synchronously, because the paste calls every
51
+ * block's `write` and creates every canvas element in ONE transaction, which
52
+ * is one undo step and one update for a peer. `afterWrite` runs in the
53
+ * paste's second transaction, once every pasted model exists.
54
+ *
55
+ * The default defers to `createBlock`, so a config written before the
56
+ * contract keeps working; an async `createBlock` then still writes after
57
+ * the paste's transaction and may cost its own undo step.
58
+ */
59
+ prepare(snapshot, context) {
60
+ return { write: () => this.createBlock(snapshot, context) };
61
+ }
62
+ /**
63
+ * `createBlock` for a config that implements `prepare`: prepares, then
64
+ * writes in one transaction of its own.
65
+ */
66
+ async createPreparedBlock(snapshot, context) {
67
+ const prepared = await this.prepare(snapshot, context);
68
+ if (!prepared)
69
+ return null;
70
+ const result = {
71
+ id: null,
72
+ };
73
+ this.std.store.transact(() => {
74
+ result.id = prepared.write();
75
+ });
76
+ const id = await result.id;
77
+ prepared.afterWrite?.();
78
+ return id;
79
+ }
27
80
  static setup(di) {
28
81
  if (!this.key) {
29
82
  throw new BlockSuiteError(BlockSuiteError.ErrorCode.ValueNotExists, 'Key is not defined in the EdgelessClipboardConfig');
@@ -58,18 +58,18 @@ const READ_LABEL_FALLBACK = 'Read this component';
58
58
  * Drawn inline like its neighbours: this package does not depend on
59
59
  * `@blocksuite/icons` and one glyph is not worth a new dependency.
60
60
  */
61
- const ReadIcon = html `<svg
62
- width="20"
63
- height="20"
64
- viewBox="0 0 24 24"
65
- fill="none"
66
- stroke="currentColor"
67
- stroke-width="2"
68
- stroke-linecap="round"
69
- stroke-linejoin="round"
70
- >
71
- <path d="M2 12s3.5-6 10-6 10 6 10 6-3.5 6-10 6-10-6-10-6z" />
72
- <circle cx="12" cy="12" r="3" />
61
+ const ReadIcon = html `<svg
62
+ width="20"
63
+ height="20"
64
+ viewBox="0 0 24 24"
65
+ fill="none"
66
+ stroke="currentColor"
67
+ stroke-width="2"
68
+ stroke-linecap="round"
69
+ stroke-linejoin="round"
70
+ >
71
+ <path d="M2 12s3.5-6 10-6 10 6 10 6-3.5 6-10 6-10-6-10-6z" />
72
+ <circle cx="12" cy="12" r="3" />
73
73
  </svg>`;
74
74
  /** The command this module drives. Spelled once. */
75
75
  const ELEMENT_READ_ID = 'element.read';
@@ -3,6 +3,7 @@ import { rotatePoint } from '../../global/gfx/index.js';
3
3
  import { GfxElementModelView } from '../../std/gfx/index.js';
4
4
  import { EdgelessCRUDIdentifier } from '../extensions/crud-extension.js';
5
5
  import { backgroundLabelHits, hitTestBackgroundLabel } from './labels.js';
6
+ import { measureBackgroundText } from './measure-text.js';
6
7
  /**
7
8
  * The rename gesture every framework background shares: double-click a label,
8
9
  * type, Enter or click away.
@@ -179,7 +180,7 @@ export class FrameworkBackgroundView extends GfxElementModelView {
179
180
  */
180
181
  export class DeclaredBackgroundView extends FrameworkBackgroundView {
181
182
  labelAt(lx, ly, w, h) {
182
- const hit = hitTestBackgroundLabel(backgroundLabelHits(this.def, this.model, w, h, this.gfx.std.getOptional(TranslationProvider)), lx, ly);
183
+ const hit = hitTestBackgroundLabel(backgroundLabelHits(this.def, this.model, w, h, this.gfx.std.getOptional(TranslationProvider), measureBackgroundText), lx, ly);
183
184
  return hit ? { prop: hit.prop, text: hit.text } : null;
184
185
  }
185
186
  }
@@ -0,0 +1,104 @@
1
+ import type { TranslationService } from '../../affine-shared/services/index.js';
2
+ import type { BackgroundInset, BackgroundTextDef, BackgroundTextStyle, FrameworkBackgroundDef } from './def.js';
3
+ import type { BackgroundModelLike } from './labels.js';
4
+ /**
5
+ * How thick a background's bands are, once their labels have been measured.
6
+ *
7
+ * A band whose label declares `fit: 'wrap'` is as thick as its declaration says
8
+ * OR as thick as its wrapped words need, whichever is more (PO, 2026-10-27: a
9
+ * BPMN pool's title band grows with its name instead of cutting it). That is a
10
+ * function of measured text, so it cannot live in the declaration, and it must
11
+ * not be computed twice: the renderer paints the band, the label hit boxes
12
+ * (`labels.ts`) and a framework's own hit tests (`gfx/bpmn/src/pool-hit.ts`)
13
+ * aim at it, and the three disagreeing by a unit is a double-click that lands
14
+ * on the flow area beside the name it was aimed at. {@link backgroundBandWidths}
15
+ * is the one resolver they all read.
16
+ *
17
+ * Nothing is written to the document. The stored geometry is the declared one,
18
+ * and so is the plot the SEMANTIC readers use (facts, audit, rules, exports):
19
+ * where an artefact sits in a frame must not move because a title was renamed.
20
+ * Only the chrome — what is painted and what is aimed at — reads the grown
21
+ * widths.
22
+ *
23
+ * Kept beside `def.ts` rather than in it because it needs the label resolution
24
+ * of `labels.ts`, which itself reads `def.ts`.
25
+ */
26
+ /** Width of `text` in model units when drawn in the CSS `font`. */
27
+ export type BackgroundTextMeasure = (text: string, font: string) => number;
28
+ /**
29
+ * The line height of a wrapped label, as a multiple of its font size: the CSS
30
+ * `normal` line height. Not the 1.25 the slides use — at 1.25 a single 13-unit
31
+ * lane name would need 24.25 units and widen the BPMN pool's 24-unit lane strip
32
+ * by a quarter unit for a name that fits it; a band only grows for a SECOND
33
+ * line.
34
+ */
35
+ export declare const BACKGROUND_WRAP_LINE_HEIGHT = 1.2;
36
+ /** The family a declaration that names none falls back to. */
37
+ export declare const DEFAULT_BACKGROUND_FONT_FAMILY = "Inter, sans-serif";
38
+ /** The CSS font a background text is drawn in. */
39
+ export declare function backgroundFont(style: BackgroundTextStyle, fallbackFamily?: string): string;
40
+ /** Distance between two wrapped lines of this style, in model units. */
41
+ export declare function backgroundWrapLineHeight(style: BackgroundTextStyle): number;
42
+ /**
43
+ * The room a fitted declared text has: from its anchor to the card's edges
44
+ * along the direction it reads, on the side(s) its alignment grows towards,
45
+ * less {@link BACKGROUND_TEXT_FIT_PADDING} at each end. A vertical text reads
46
+ * bottom-to-top, so `left` grows UP from its anchor.
47
+ */
48
+ export declare function backgroundTextRoom(ax: number, ay: number, align: BackgroundTextDef['align'], vertical: boolean, w: number, h: number): number;
49
+ /**
50
+ * A rough width with no canvas at all: 0.55 × the font size per code point, the
51
+ * ratio `svg-sketch.ts` estimates with. The default of every headless caller
52
+ * (this file is reachable from the `./interchange` entry, which loads no DOM);
53
+ * a caller with a document passes `measureBackgroundText` (`measure-text.ts`),
54
+ * which measures with the canvas's own metrics.
55
+ */
56
+ export declare function estimateBackgroundText(text: string, font: string): number;
57
+ /** A side band's label once wrapped: what to draw and where. */
58
+ export interface BackgroundWrappedLabel {
59
+ lines: readonly string[];
60
+ /**
61
+ * Model units to add to the label's anchor, resolved against the GROWN plot,
62
+ * so it keeps its offset from the middle of its band: half the growth, back
63
+ * towards the card edge.
64
+ */
65
+ shift: readonly [number, number];
66
+ }
67
+ /** The bands of one instance, sized by what they hold. */
68
+ export interface BackgroundBandLayout {
69
+ /** The margins, each side band grown to what its wrapped label needs. */
70
+ margin: BackgroundInset;
71
+ /**
72
+ * Thickness of every instance-zone strip — the declared width or the one the
73
+ * most demanding wrapped name needs. `undefined` without a strip.
74
+ */
75
+ zoneBandWidth: number | undefined;
76
+ /** Each wrapped side-band label, by its declaration. */
77
+ labels: ReadonlyMap<BackgroundTextDef, BackgroundWrappedLabel>;
78
+ /** Each wrapped instance-zone name, by reported zone id. */
79
+ zoneLines: ReadonlyMap<string, readonly string[]>;
80
+ }
81
+ /**
82
+ * THE resolver of band thickness. Pure in its inputs: the declaration, the
83
+ * instance's props, its size, and how to measure text — the renderer passes its
84
+ * context, a hit test outside a paint `measureBackgroundText` (the same metrics
85
+ * from an offscreen canvas), and a caller with no DOM gets the estimate.
86
+ *
87
+ * - a SIDE band whose label wraps is `max(declared margin, lines × line height
88
+ * + 2 × padding)` thick, the lines broken over the room the label has along
89
+ * the band (`backgroundTextRoom`, from its anchor in the declared plot);
90
+ * - every INSTANCE-ZONE strip is one thickness: the declared width, or the
91
+ * most any zone's wrapped name needs, each name broken over its own zone's
92
+ * length less the padding. A lane with a long name widens every lane's strip,
93
+ * so the leading edges stay aligned.
94
+ *
95
+ * A declaration with no `'wrap'` anywhere gets its declared numbers back
96
+ * without a single measurement, so the frameworks that never asked for this
97
+ * pay nothing for it.
98
+ *
99
+ * ponytail: a text's room is measured against the DECLARED plot, so a
100
+ * declaration wrapping both a left and a top band would size each without the
101
+ * other's growth. Nothing declares two; upgrade by resolving the sides in turn
102
+ * against the plot resolved so far, the day one does.
103
+ */
104
+ export declare function backgroundBandWidths(def: FrameworkBackgroundDef, model: BackgroundModelLike, w: number, h: number, measure?: BackgroundTextMeasure, translation?: TranslationService | null): BackgroundBandLayout;
@@ -0,0 +1,156 @@
1
+ import { backgroundInstanceZones, backgroundPlot, backgroundPoint, DEFAULT_BACKGROUND_WRAP_MAX_LINES, } from './def.js';
2
+ import { BACKGROUND_TEXT_FIT_PADDING, wrapToWidth } from './ellipsis.js';
3
+ import { backgroundInVariant, backgroundLabelText, backgroundVisible, } from './labels.js';
4
+ /**
5
+ * The line height of a wrapped label, as a multiple of its font size: the CSS
6
+ * `normal` line height. Not the 1.25 the slides use — at 1.25 a single 13-unit
7
+ * lane name would need 24.25 units and widen the BPMN pool's 24-unit lane strip
8
+ * by a quarter unit for a name that fits it; a band only grows for a SECOND
9
+ * line.
10
+ */
11
+ export const BACKGROUND_WRAP_LINE_HEIGHT = 1.2;
12
+ /** The family a declaration that names none falls back to. */
13
+ export const DEFAULT_BACKGROUND_FONT_FAMILY = 'Inter, sans-serif';
14
+ /** The CSS font a background text is drawn in. */
15
+ export function backgroundFont(style, fallbackFamily = DEFAULT_BACKGROUND_FONT_FAMILY) {
16
+ const italic = style.italic ? 'italic ' : '';
17
+ const weight = style.weight ? `${style.weight} ` : '';
18
+ return `${italic}${weight}${style.size}px ${style.family ?? fallbackFamily}`;
19
+ }
20
+ /** Distance between two wrapped lines of this style, in model units. */
21
+ export function backgroundWrapLineHeight(style) {
22
+ return style.size * BACKGROUND_WRAP_LINE_HEIGHT;
23
+ }
24
+ /**
25
+ * The room a fitted declared text has: from its anchor to the card's edges
26
+ * along the direction it reads, on the side(s) its alignment grows towards,
27
+ * less {@link BACKGROUND_TEXT_FIT_PADDING} at each end. A vertical text reads
28
+ * bottom-to-top, so `left` grows UP from its anchor.
29
+ */
30
+ export function backgroundTextRoom(ax, ay, align, vertical, w, h) {
31
+ const pad = BACKGROUND_TEXT_FIT_PADDING;
32
+ if (vertical) {
33
+ const up = ay - pad;
34
+ const down = h - ay - pad;
35
+ const side = align ?? 'center';
36
+ if (side === 'left')
37
+ return up;
38
+ if (side === 'right')
39
+ return down;
40
+ return 2 * Math.min(up, down);
41
+ }
42
+ const leftward = ax - pad;
43
+ const rightward = w - ax - pad;
44
+ const side = align ?? 'left';
45
+ if (side === 'left')
46
+ return rightward;
47
+ if (side === 'right')
48
+ return leftward;
49
+ return 2 * Math.min(leftward, rightward);
50
+ }
51
+ /** Rough advance per code point, as a fraction of the font size. */
52
+ const ESTIMATED_ADVANCE = 0.55;
53
+ /**
54
+ * A rough width with no canvas at all: 0.55 × the font size per code point, the
55
+ * ratio `svg-sketch.ts` estimates with. The default of every headless caller
56
+ * (this file is reachable from the `./interchange` entry, which loads no DOM);
57
+ * a caller with a document passes `measureBackgroundText` (`measure-text.ts`),
58
+ * which measures with the canvas's own metrics.
59
+ */
60
+ export function estimateBackgroundText(text, font) {
61
+ const size = Number(/(\d+(?:\.\d+)?)px/.exec(font)?.[1]) || 12;
62
+ return Array.from(text).length * size * ESTIMATED_ADVANCE;
63
+ }
64
+ const NO_LABELS = new Map();
65
+ const NO_ZONE_LINES = new Map();
66
+ /**
67
+ * THE resolver of band thickness. Pure in its inputs: the declaration, the
68
+ * instance's props, its size, and how to measure text — the renderer passes its
69
+ * context, a hit test outside a paint `measureBackgroundText` (the same metrics
70
+ * from an offscreen canvas), and a caller with no DOM gets the estimate.
71
+ *
72
+ * - a SIDE band whose label wraps is `max(declared margin, lines × line height
73
+ * + 2 × padding)` thick, the lines broken over the room the label has along
74
+ * the band (`backgroundTextRoom`, from its anchor in the declared plot);
75
+ * - every INSTANCE-ZONE strip is one thickness: the declared width, or the
76
+ * most any zone's wrapped name needs, each name broken over its own zone's
77
+ * length less the padding. A lane with a long name widens every lane's strip,
78
+ * so the leading edges stay aligned.
79
+ *
80
+ * A declaration with no `'wrap'` anywhere gets its declared numbers back
81
+ * without a single measurement, so the frameworks that never asked for this
82
+ * pay nothing for it.
83
+ *
84
+ * ponytail: a text's room is measured against the DECLARED plot, so a
85
+ * declaration wrapping both a left and a top band would size each without the
86
+ * other's growth. Nothing declares two; upgrade by resolving the sides in turn
87
+ * against the plot resolved so far, the day one does.
88
+ */
89
+ export function backgroundBandWidths(def, model, w, h, measure = estimateBackgroundText, translation) {
90
+ const declared = def.geometry.margin;
91
+ const zoneLabel = def.instanceZones?.label;
92
+ const wrapsBand = (def.chrome?.sideBands ?? []).some(band => band.label?.fit === 'wrap');
93
+ const wrapsZones = zoneLabel?.band !== undefined && zoneLabel.fit === 'wrap';
94
+ if (!wrapsBand && !wrapsZones) {
95
+ return {
96
+ margin: declared,
97
+ zoneBandWidth: zoneLabel?.band?.width,
98
+ labels: NO_LABELS,
99
+ zoneLines: NO_ZONE_LINES,
100
+ };
101
+ }
102
+ const family = def.chrome?.fontFamily ?? DEFAULT_BACKGROUND_FONT_FAMILY;
103
+ const pad = BACKGROUND_TEXT_FIT_PADDING;
104
+ const measurer = (font) => ({
105
+ measureText: (text) => ({ width: measure(text, font) }),
106
+ });
107
+ const thicknessFor = (lines, style) => lines > 0 ? lines * backgroundWrapLineHeight(style) + 2 * pad : 0;
108
+ const margin = { ...declared };
109
+ const wrapped = [];
110
+ const lines = new Map();
111
+ if (wrapsBand) {
112
+ const plot = backgroundPlot(def, w, h);
113
+ for (const band of def.chrome?.sideBands ?? []) {
114
+ const label = band.label;
115
+ if (label?.fit !== 'wrap')
116
+ continue;
117
+ // A label the instance does not paint holds nothing open.
118
+ if (!backgroundInVariant(def, label.variants, model))
119
+ continue;
120
+ if (!backgroundVisible(label.visibleProp, model))
121
+ continue;
122
+ const vertical = label.vertical === true;
123
+ const [ax, ay] = backgroundPoint(label.anchor, plot);
124
+ const shown = wrapToWidth(measurer(backgroundFont(label.style, family)), backgroundLabelText(label, model, translation), backgroundTextRoom(ax, ay, label.align, vertical, w, h), label.maxLines ?? DEFAULT_BACKGROUND_WRAP_MAX_LINES);
125
+ lines.set(label, shown);
126
+ wrapped.push({ label, side: band.side });
127
+ margin[band.side] = Math.max(margin[band.side], thicknessFor(shown.length, label.style));
128
+ }
129
+ }
130
+ const labels = new Map();
131
+ for (const { label, side } of wrapped) {
132
+ const back = (declared[side] - margin[side]) / 2;
133
+ labels.set(label, {
134
+ lines: lines.get(label) ?? [],
135
+ shift: side === 'left' ? [back, 0] : [0, back],
136
+ });
137
+ }
138
+ let zoneBandWidth = zoneLabel?.band?.width;
139
+ const zoneLines = new Map();
140
+ if (wrapsZones && zoneLabel && zoneBandWidth !== undefined) {
141
+ const plot = backgroundPlot(def, w, h, margin);
142
+ const stackY = def.instanceZones?.stack === 'y';
143
+ const ctx = measurer(backgroundFont(zoneLabel.style, family));
144
+ for (const zone of backgroundInstanceZones(def, model)) {
145
+ if (zone.name === undefined || zone.name === '')
146
+ continue;
147
+ const length = stackY
148
+ ? zone.rect.h * plot.height
149
+ : zone.rect.w * plot.width;
150
+ const shown = wrapToWidth(ctx, zone.name, length - 2 * pad, zoneLabel.maxLines ?? DEFAULT_BACKGROUND_WRAP_MAX_LINES);
151
+ zoneLines.set(zone.id, shown);
152
+ zoneBandWidth = Math.max(zoneBandWidth, thicknessFor(shown.length, zoneLabel.style));
153
+ }
154
+ }
155
+ return { margin, zoneBandWidth, labels, zoneLines };
156
+ }
@@ -122,7 +122,41 @@ export interface BackgroundTextDef extends BackgroundLabelSource {
122
122
  vertical?: boolean;
123
123
  /** Model prop gating this text. Absent means always drawn. */
124
124
  visibleProp?: string;
125
+ /**
126
+ * What happens to words longer than the room they are written in.
127
+ *
128
+ * Absent — what every declaration written before this existed says — draws
129
+ * the words whole, however far they run. `'ellipsis'` cuts them to the room
130
+ * between the anchor and the card's edges along the text's direction, minus
131
+ * a small padding, ending with "…" (`ellipsis.ts`). Opt-in, because the
132
+ * frameworks that do not ask for it have exact-drawing tests and labels that
133
+ * are meant to overhang.
134
+ *
135
+ * `'wrap'` breaks the words into lines over the same room and stacks them
136
+ * ACROSS the text's direction, at most {@link maxLines}, the last one cut
137
+ * with "…" if the words still do not fit. It is honoured on a SIDE BAND's
138
+ * label ({@link BackgroundSideBandDef}), whose band then grows to hold the
139
+ * lines (`backgroundBandWidths`); any other text has no band to grow and
140
+ * treats `'wrap'` as `'ellipsis'`. Named consumer: the BPMN participant name,
141
+ * written up its band, which thickens as the name needs more lines (PO,
142
+ * 2026-10-27) rather than cutting it as PR #532 did.
143
+ *
144
+ * Append-only, like every enum-like value a declaration states.
145
+ */
146
+ fit?: 'ellipsis' | 'wrap';
147
+ /**
148
+ * Under `fit: 'wrap'`, the most lines the words may take before the last one
149
+ * is cut with "…". Defaults to {@link DEFAULT_BACKGROUND_WRAP_MAX_LINES}.
150
+ * Ignored by every other fit.
151
+ */
152
+ maxLines?: number;
125
153
  }
154
+ /**
155
+ * How many lines a `fit: 'wrap'` label takes when its declaration names no
156
+ * `maxLines`: enough for a sentence-long name, few enough that a pasted
157
+ * paragraph does not turn a title band into a column.
158
+ */
159
+ export declare const DEFAULT_BACKGROUND_WRAP_MAX_LINES = 3;
126
160
  /** One graduation on an axis. */
127
161
  export interface BackgroundTickDef {
128
162
  /** Position along the axis, as a ratio of the plot span it runs on. */
@@ -261,7 +295,10 @@ export interface BackgroundInstanceZoneItem {
261
295
  * wrong.
262
296
  */
263
297
  export interface BackgroundInstanceZoneBandDef {
264
- /** Strip thickness in FIXED model units — across the stacking direction. */
298
+ /**
299
+ * Strip thickness in FIXED model units — across the stacking direction. The
300
+ * MINIMUM thickness under a `fit: 'wrap'` label, which may grow it.
301
+ */
265
302
  width: number;
266
303
  /** The line between the strip and the rest of the zone. Absent means none. */
267
304
  divider?: BackgroundStroke;
@@ -329,6 +366,28 @@ export interface BackgroundInstanceZonesDef {
329
366
  dy?: number;
330
367
  /** Present = band placement. */
331
368
  band?: BackgroundInstanceZoneBandDef;
369
+ /**
370
+ * `'ellipsis'` cuts a name longer than its room — the strip's length under
371
+ * a band, the zone's width from the inset under the corner — and ends it
372
+ * with "…", as {@link BackgroundTextDef.fit} does. Absent draws the name
373
+ * whole.
374
+ *
375
+ * `'wrap'` breaks a name into lines stacked across the strip, at most
376
+ * {@link maxLines}, and every strip of the instance grows to the SAME
377
+ * thickness: the one its most demanding name needs, never less than
378
+ * {@link BackgroundInstanceZoneBandDef.width} (`backgroundBandWidths`). One
379
+ * thickness, because strips of different widths would stagger the lanes'
380
+ * leading edges and read as lanes nested at different depths. Band
381
+ * placement only: the corner placement has no strip to grow and treats
382
+ * `'wrap'` as `'ellipsis'`. Named consumer: the BPMN lane title (PO,
383
+ * 2026-10-27).
384
+ */
385
+ fit?: 'ellipsis' | 'wrap';
386
+ /**
387
+ * Under `fit: 'wrap'`, the most lines a name may take. Defaults to
388
+ * {@link DEFAULT_BACKGROUND_WRAP_MAX_LINES}.
389
+ */
390
+ maxLines?: number;
332
391
  };
333
392
  /**
334
393
  * Prefix of the reported zone ids: `'lane'` reports `lane:sales`.
@@ -390,6 +449,17 @@ export interface BackgroundSurfaceDef {
390
449
  * and one day will not. An element smaller than its own margin clamps the band
391
450
  * to the element, which is the only case where the two can disagree.
392
451
  *
452
+ * ## A band that grows with its label
453
+ *
454
+ * Under a `fit: 'wrap'` label the declared margin is the MINIMUM thickness:
455
+ * the band grows to `lines × line height + 2 × padding` when the words need
456
+ * more lines than it holds, and the plot gives up the extra room with it.
457
+ * Derived at paint time from the measured words, never stored — the one
458
+ * resolver is `backgroundBandWidths`, and the renderer, the label hit boxes
459
+ * and a framework's own hit tests all read the margin it returns. The label
460
+ * keeps its offset from the MIDDLE of the band, so a label centred in the
461
+ * declared band stays centred in the grown one.
462
+ *
393
463
  * A band is part of the CARD, not a layer over it: it is painted over the
394
464
  * card's fill and under its border, so the frame keeps outlining the whole
395
465
  * element. Its {@link label} is a text like any other, drawn with the rest of
@@ -499,8 +569,17 @@ export interface FrameworkBackgroundDef {
499
569
  * fill, no border, no radius.
500
570
  */
501
571
  export declare const DEFAULT_BACKGROUND_SURFACE: BackgroundSurfaceDef;
502
- /** The plot rectangle of a background of size `w × h`, in element-local units. */
503
- export declare function backgroundPlot(def: FrameworkBackgroundDef, w: number, h: number): {
572
+ /**
573
+ * The plot rectangle of a background of size `w × h`, in element-local units.
574
+ *
575
+ * `margin` defaults to the DECLARED one. The painted chrome — the renderer, the
576
+ * label hit boxes, a framework's own hit tests — passes the margin
577
+ * `backgroundBandWidths` resolved instead, which a wrapped band label may have
578
+ * grown. The semantic readers (facts, audit, rules, interchange exports) keep
579
+ * the declared plot: where an artefact sits in a frame is a question about the
580
+ * frame, and its answer must not change because somebody renamed it.
581
+ */
582
+ export declare function backgroundPlot(def: FrameworkBackgroundDef, w: number, h: number, margin?: BackgroundInset): {
504
583
  x0: number;
505
584
  y0: number;
506
585
  x1: number;
@@ -592,8 +671,12 @@ export interface BackgroundRect {
592
671
  * Clamped to the plot, the way a side band clamps to an element narrower than
593
672
  * its own margin ({@link BackgroundSideBandDef}) — the same degenerate case,
594
673
  * and the same answer.
674
+ *
675
+ * `bandWidth` defaults to the DECLARED strip width; the painted chrome passes
676
+ * the one `backgroundBandWidths` resolved, which a wrapped name may have grown
677
+ * — and which is the same for every zone of the instance.
595
678
  */
596
- export declare function backgroundInstanceZoneBand(def: FrameworkBackgroundDef, zone: BackgroundInstanceZone, plot: ReturnType<typeof backgroundPlot>): BackgroundRect | null;
679
+ export declare function backgroundInstanceZoneBand(def: FrameworkBackgroundDef, zone: BackgroundInstanceZone, plot: ReturnType<typeof backgroundPlot>, bandWidth?: number): BackgroundRect | null;
597
680
  /**
598
681
  * Whether the resize handles are offered for this element.
599
682
  *