@formicoidea/labre-core 0.33.0 → 0.34.1

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 (100) hide show
  1. package/dist/_pkgs/affine-block-attachment/attachment-block.js +2 -2
  2. package/dist/_pkgs/affine-block-attachment/configs/toolbar.d.ts +2 -2
  3. package/dist/_pkgs/affine-block-attachment/configs/toolbar.js +9 -7
  4. package/dist/_pkgs/affine-block-bookmark/configs/toolbar.js +12 -12
  5. package/dist/_pkgs/affine-block-code/code-block.js +2 -2
  6. package/dist/_pkgs/affine-block-code/code-toolbar/config.js +6 -0
  7. package/dist/_pkgs/affine-block-database/database-block.js +2 -2
  8. package/dist/_pkgs/affine-block-embed/common/render-linked-doc.js +3 -3
  9. package/dist/_pkgs/affine-block-embed/configs/toolbar.js +11 -11
  10. package/dist/_pkgs/affine-block-embed/embed-html-block/configs/toolbar.js +5 -5
  11. package/dist/_pkgs/affine-block-embed/embed-iframe-block/configs/toolbar.d.ts +7 -7
  12. package/dist/_pkgs/affine-block-embed/embed-iframe-block/configs/toolbar.js +12 -12
  13. package/dist/_pkgs/affine-block-embed/embed-iframe-block/embed-iframe-block.js +2 -2
  14. package/dist/_pkgs/affine-block-embed-doc/embed-linked-doc-block/configs/toolbar.js +9 -9
  15. package/dist/_pkgs/affine-block-embed-doc/embed-linked-doc-block/embed-linked-doc-block.js +4 -4
  16. package/dist/_pkgs/affine-block-embed-doc/embed-synced-doc-block/components/embed-synced-doc-card.js +5 -4
  17. package/dist/_pkgs/affine-block-embed-doc/embed-synced-doc-block/configs/toolbar.js +9 -9
  18. package/dist/_pkgs/affine-block-frame/frame-toolbar.js +2 -2
  19. package/dist/_pkgs/affine-block-image/configs/toolbar.js +4 -4
  20. package/dist/_pkgs/affine-block-note/configs/toolbar.js +2 -2
  21. package/dist/_pkgs/affine-block-root/configs/toolbar.d.ts +6 -4
  22. package/dist/_pkgs/affine-block-root/configs/toolbar.js +6 -6
  23. package/dist/_pkgs/affine-block-root/edgeless/configs/toolbar/misc.d.ts +10 -10
  24. package/dist/_pkgs/affine-block-root/edgeless/configs/toolbar/misc.js +4 -4
  25. package/dist/_pkgs/affine-block-root/edgeless/configs/toolbar/more.d.ts +7 -7
  26. package/dist/_pkgs/affine-block-root/edgeless/configs/toolbar/more.js +8 -8
  27. package/dist/_pkgs/affine-block-root/edgeless/edgeless-root-block.js +3 -1
  28. package/dist/_pkgs/affine-block-surface/extensions/interchange-import.js +15 -4
  29. package/dist/_pkgs/affine-block-surface/extensions/interchange.d.ts +17 -0
  30. package/dist/_pkgs/affine-block-surface/extensions/spotlight.d.ts +21 -0
  31. package/dist/_pkgs/affine-block-surface/extensions/spotlight.js +17 -3
  32. package/dist/_pkgs/affine-block-surface/index.d.ts +1 -0
  33. package/dist/_pkgs/affine-block-surface/index.js +1 -0
  34. package/dist/_pkgs/affine-block-surface/renderer/element-highlight-overlay.d.ts +17 -0
  35. package/dist/_pkgs/affine-block-surface/renderer/element-highlight-overlay.js +66 -0
  36. package/dist/_pkgs/affine-block-surface/view.js +5 -0
  37. package/dist/_pkgs/affine-block-surface-ref/configs/toolbar.js +4 -4
  38. package/dist/_pkgs/affine-components/highlight-dropdown-menu/highlight-duotone-icon.js +8 -8
  39. package/dist/_pkgs/affine-components/highlight-dropdown-menu/text-duotone-icon.js +11 -11
  40. package/dist/_pkgs/affine-components/icons/text.js +426 -426
  41. package/dist/_pkgs/affine-components/open-doc-dropdown-menu/dropdown-menu.d.ts +2 -0
  42. package/dist/_pkgs/affine-components/resource/resource.js +2 -0
  43. package/dist/_pkgs/affine-components/view-dropdown-menu/dropdown-menu.js +27 -14
  44. package/dist/_pkgs/affine-inline-link/link-node/configs/toolbar.d.ts +4 -4
  45. package/dist/_pkgs/affine-inline-link/link-node/configs/toolbar.js +6 -6
  46. package/dist/_pkgs/affine-inline-reference/reference-node/configs/toolbar.d.ts +4 -4
  47. package/dist/_pkgs/affine-inline-reference/reference-node/configs/toolbar.js +6 -6
  48. package/dist/_pkgs/affine-model/elements/bpmn/pool.d.ts +61 -0
  49. package/dist/_pkgs/affine-model/elements/bpmn/pool.js +93 -0
  50. package/dist/_pkgs/affine-model/elements/cynefin/cynefin.d.ts +7 -2
  51. package/dist/_pkgs/affine-model/elements/cynefin/cynefin.js +9 -4
  52. package/dist/_pkgs/affine-model/elements/edgy/board.d.ts +7 -2
  53. package/dist/_pkgs/affine-model/elements/edgy/board.js +9 -4
  54. package/dist/_pkgs/affine-model/elements/edgy/facets.d.ts +20 -3
  55. package/dist/_pkgs/affine-model/elements/edgy/facets.js +13 -4
  56. package/dist/_pkgs/affine-model/elements/estuarine/estuarine.d.ts +7 -2
  57. package/dist/_pkgs/affine-model/elements/estuarine/estuarine.js +9 -4
  58. package/dist/_pkgs/affine-model/elements/framework-background/background.d.ts +7 -2
  59. package/dist/_pkgs/affine-model/elements/framework-background/background.js +9 -4
  60. package/dist/_pkgs/affine-model/elements/framework-background/hit-test.d.ts +48 -0
  61. package/dist/_pkgs/affine-model/elements/framework-background/hit-test.js +55 -0
  62. package/dist/_pkgs/affine-model/elements/framework-background/index.d.ts +1 -0
  63. package/dist/_pkgs/affine-model/elements/framework-background/index.js +1 -0
  64. package/dist/_pkgs/affine-shared/services/edit-props-store.d.ts +11 -0
  65. package/dist/_pkgs/affine-shared/services/edit-props-store.js +15 -0
  66. package/dist/_pkgs/affine-shared/services/toolbar-service/action.d.ts +32 -0
  67. package/dist/_pkgs/affine-shared/services/toolbar-service/action.js +16 -0
  68. package/dist/_pkgs/affine-shared/services/toolbar-service/flags.d.ts +10 -0
  69. package/dist/_pkgs/affine-shared/services/toolbar-service/flags.js +32 -1
  70. package/dist/_pkgs/affine-shared/services/translation-service/chrome.d.ts +106 -0
  71. package/dist/_pkgs/affine-shared/services/translation-service/chrome.js +201 -0
  72. package/dist/_pkgs/affine-shared/services/translation-service/index.d.ts +1 -0
  73. package/dist/_pkgs/affine-shared/services/translation-service/index.js +1 -0
  74. package/dist/_pkgs/affine-widget-edgeless-toolbar/catalogue/catalogue-groups.d.ts +11 -16
  75. package/dist/_pkgs/affine-widget-edgeless-toolbar/catalogue/catalogue-groups.js +11 -28
  76. package/dist/_pkgs/affine-widget-keyboard-toolbar/config.js +2 -1
  77. package/dist/_pkgs/affine-widget-keyboard-toolbar/icons.js +98 -98
  78. package/dist/_pkgs/affine-widget-slash-menu/config.js +2 -1
  79. package/dist/_pkgs/affine-widget-toolbar/toolbar.js +5 -0
  80. package/dist/_pkgs/affine-widget-toolbar/utils.d.ts +19 -2
  81. package/dist/_pkgs/affine-widget-toolbar/utils.js +22 -7
  82. package/dist/_pkgs/data-view/core/group-by/renderer/boolean-group.js +11 -11
  83. package/dist/_pkgs/data-view/core/utils/menu-title.js +17 -17
  84. package/dist/_pkgs/data-view/view-presets/table/pc-virtual/group/top/group-title.js +18 -18
  85. package/dist/_pkgs/std/extension/command-registry.d.ts +36 -0
  86. package/dist/_pkgs/std/extension/command-registry.js +16 -0
  87. package/dist/_pkgs/std/extension/translation-manifest.d.ts +46 -1
  88. package/dist/_pkgs/std/extension/translation-manifest.js +58 -0
  89. package/dist/_pkgs/std/gfx/highlight.d.ts +84 -0
  90. package/dist/_pkgs/std/gfx/highlight.js +112 -0
  91. package/dist/_pkgs/std/gfx/index.d.ts +1 -0
  92. package/dist/_pkgs/std/gfx/index.js +1 -0
  93. package/dist/_pkgs/std/gfx/interactivity/gfx-view-event-handler.js +17 -3
  94. package/dist/_pkgs/std/scope/std-scope.d.ts +2 -1
  95. package/dist/_pkgs/std/scope/std-scope.js +2 -0
  96. package/dist/_pkgs/std/utils/gfx.js +2 -0
  97. package/dist/shortcuts.d.ts +20 -6
  98. package/dist/shortcuts.js +7 -9
  99. package/dist/translations.js +25 -18
  100. package/package.json +13 -1
@@ -32,8 +32,9 @@ var __runInitializers = (this && this.__runInitializers) || function (thisArg, i
32
32
  }
33
33
  return useValue ? value : void 0;
34
34
  };
35
- import { Bound, getPointsFromBoundWithRotation, linePolygonIntersects, pointInPolygon, polygonNearestPoint, } from '../../../global/gfx/index.js';
35
+ import { Bound, getPointsFromBoundWithRotation, linePolygonIntersects, polygonNearestPoint, } from '../../../global/gfx/index.js';
36
36
  import { field, GfxPrimitiveElementModel } from '../../../std/gfx/index.js';
37
+ import { backgroundIncludesPoint } from '../framework-background/index.js';
37
38
  /**
38
39
  * A static "Estuarine framework" map: the 2D energy (e, vertical, double-headed)
39
40
  * / time (t, horizontal) space with the three reference curves — Liminal (green),
@@ -101,9 +102,13 @@ let EstuarineElementModel = (() => {
101
102
  getNearestPoint(point) {
102
103
  return polygonNearestPoint(Bound.deserialize(this.xywh).points, point);
103
104
  }
104
- includesPoint(x, y) {
105
- const points = getPointsFromBoundWithRotation(this);
106
- return pointInPolygon([x, y], points);
105
+ /**
106
+ * Picked by its BORDER band — see {@link backgroundIncludesPoint}. The map is
107
+ * a space constraint nodes are dropped into, so the clicks inside it are
108
+ * theirs.
109
+ */
110
+ includesPoint(x, y, options) {
111
+ return backgroundIncludesPoint(this, x, y, options);
107
112
  }
108
113
  #resizeEnabled_accessor_storage = __runInitializers(this, _resizeEnabled_initializers, true);
109
114
  get resizeEnabled() { return this.#resizeEnabled_accessor_storage; }
@@ -1,6 +1,6 @@
1
1
  import type { IVec } from '../../../global/gfx/index.js';
2
2
  import { Bound } from '../../../global/gfx/index.js';
3
- import type { BaseElementProps } from '../../../std/gfx/index.js';
3
+ import type { BaseElementProps, PointTestOptions } from '../../../std/gfx/index.js';
4
4
  import { GfxPrimitiveElementModel } from '../../../std/gfx/index.js';
5
5
  /**
6
6
  * The props every framework background carries, whatever it draws.
@@ -38,5 +38,10 @@ export declare abstract class FrameworkBackgroundElementModel<Props extends Base
38
38
  containsBound(bounds: Bound): boolean;
39
39
  getLineIntersections(start: IVec, end: IVec): import("../../../global/gfx/index.js").PointLocation[] | null;
40
40
  getNearestPoint(point: IVec): IVec;
41
- includesPoint(x: number, y: number): boolean;
41
+ /**
42
+ * Picked by its BORDER band, not by its whole area — see
43
+ * {@link backgroundIncludesPoint} for the why and for what a caller asking
44
+ * about the visible extent (`ignoreTransparent: false`) still gets.
45
+ */
46
+ includesPoint(x: number, y: number, options?: PointTestOptions): boolean;
42
47
  }
@@ -1,5 +1,6 @@
1
- import { Bound, getPointsFromBoundWithRotation, linePolygonIntersects, pointInPolygon, polygonNearestPoint, } from '../../../global/gfx/index.js';
1
+ import { Bound, getPointsFromBoundWithRotation, linePolygonIntersects, polygonNearestPoint, } from '../../../global/gfx/index.js';
2
2
  import { GfxPrimitiveElementModel } from '../../../std/gfx/index.js';
3
+ import { backgroundIncludesPoint } from './hit-test.js';
3
4
  /**
4
5
  * The MODEL half of the framework-background primitive (PF2.1).
5
6
  *
@@ -35,8 +36,12 @@ export class FrameworkBackgroundElementModel extends GfxPrimitiveElementModel {
35
36
  getNearestPoint(point) {
36
37
  return polygonNearestPoint(Bound.deserialize(this.xywh).points, point);
37
38
  }
38
- includesPoint(x, y) {
39
- const points = getPointsFromBoundWithRotation(this);
40
- return pointInPolygon([x, y], points);
39
+ /**
40
+ * Picked by its BORDER band, not by its whole area — see
41
+ * {@link backgroundIncludesPoint} for the why and for what a caller asking
42
+ * about the visible extent (`ignoreTransparent: false`) still gets.
43
+ */
44
+ includesPoint(x, y, options) {
45
+ return backgroundIncludesPoint(this, x, y, options);
41
46
  }
42
47
  }
@@ -0,0 +1,48 @@
1
+ import type { IBound } from '../../../global/gfx/index.js';
2
+ import type { PointTestOptions } from '../../../std/gfx/index.js';
3
+ /**
4
+ * The width of the band a background is picked by, in SCREEN pixels.
5
+ *
6
+ * Ten is what every caller on the picking path already asks for
7
+ * (`getElementByPoint`, the default tool, the gfx view event handler all pass
8
+ * `hitThreshold: 10`); naming it here gives the one caller that passes nothing
9
+ * — the frame manager, which calls `includesPoint(x, y, {})` — the same band
10
+ * instead of a shape's 1-pixel default.
11
+ */
12
+ export declare const BACKGROUND_BORDER_HIT_THRESHOLD = 10;
13
+ /**
14
+ * Whether a point picks a framework background — THE hit test every background
15
+ * in the library shares.
16
+ *
17
+ * ## A background is picked by its border, not by its area
18
+ *
19
+ * A background is a passive canvas: the user drops nodes onto it and works on
20
+ * THEM. A hit test over the whole rectangle makes the backdrop compete with its
21
+ * own content for every click — it wins outright when it sits above in the
22
+ * paint order (`getElementByPoint` keeps the topmost hit), and it fills every
23
+ * gap when it sits below (the interior of an unfilled shape, the space beside a
24
+ * small node). Both were reported from the same recette (issue #194).
25
+ *
26
+ * So the band IS the affordance: within `hitThreshold` screen pixels of the
27
+ * (rotated) outline, and nowhere else. The threshold is divided by the zoom for
28
+ * the reason every shape divides it — a border band has to stay the same width
29
+ * under the pointer whatever the viewport is doing.
30
+ *
31
+ * ## The interior still answers "is the pointer on this element"
32
+ *
33
+ * `ignoreTransparent: false` is the existing way a caller says it is asking
34
+ * about the element's visible EXTENT rather than about what a click would pick
35
+ * — an unfilled shape answers the same two ways for the same reason
36
+ * (`rect.includesPoint`). Exactly two callers pass it: the default tool, which
37
+ * uses it to decide whether a press on an ALREADY-SELECTED element starts a
38
+ * move, and the connector manager, which never sees a background at all
39
+ * (`connectable` is false). So a selected background keeps being dragged from
40
+ * anywhere inside it, and only picking changed.
41
+ *
42
+ * ## What did NOT change
43
+ *
44
+ * `containsBound` / `intersectsBound` (the lasso), `getNearestPoint` and
45
+ * `getLineIntersections` are untouched: a rubber band that had to graze the
46
+ * outline would be a second regression dressed as a fix.
47
+ */
48
+ export declare function backgroundIncludesPoint(bounds: IBound, x: number, y: number, options?: PointTestOptions): boolean;
@@ -0,0 +1,55 @@
1
+ import { getPointsFromBoundWithRotation, pointInPolygon, pointOnPolygonStoke, } from '../../../global/gfx/index.js';
2
+ /**
3
+ * The width of the band a background is picked by, in SCREEN pixels.
4
+ *
5
+ * Ten is what every caller on the picking path already asks for
6
+ * (`getElementByPoint`, the default tool, the gfx view event handler all pass
7
+ * `hitThreshold: 10`); naming it here gives the one caller that passes nothing
8
+ * — the frame manager, which calls `includesPoint(x, y, {})` — the same band
9
+ * instead of a shape's 1-pixel default.
10
+ */
11
+ export const BACKGROUND_BORDER_HIT_THRESHOLD = 10;
12
+ /**
13
+ * Whether a point picks a framework background — THE hit test every background
14
+ * in the library shares.
15
+ *
16
+ * ## A background is picked by its border, not by its area
17
+ *
18
+ * A background is a passive canvas: the user drops nodes onto it and works on
19
+ * THEM. A hit test over the whole rectangle makes the backdrop compete with its
20
+ * own content for every click — it wins outright when it sits above in the
21
+ * paint order (`getElementByPoint` keeps the topmost hit), and it fills every
22
+ * gap when it sits below (the interior of an unfilled shape, the space beside a
23
+ * small node). Both were reported from the same recette (issue #194).
24
+ *
25
+ * So the band IS the affordance: within `hitThreshold` screen pixels of the
26
+ * (rotated) outline, and nowhere else. The threshold is divided by the zoom for
27
+ * the reason every shape divides it — a border band has to stay the same width
28
+ * under the pointer whatever the viewport is doing.
29
+ *
30
+ * ## The interior still answers "is the pointer on this element"
31
+ *
32
+ * `ignoreTransparent: false` is the existing way a caller says it is asking
33
+ * about the element's visible EXTENT rather than about what a click would pick
34
+ * — an unfilled shape answers the same two ways for the same reason
35
+ * (`rect.includesPoint`). Exactly two callers pass it: the default tool, which
36
+ * uses it to decide whether a press on an ALREADY-SELECTED element starts a
37
+ * move, and the connector manager, which never sees a background at all
38
+ * (`connectable` is false). So a selected background keeps being dragged from
39
+ * anywhere inside it, and only picking changed.
40
+ *
41
+ * ## What did NOT change
42
+ *
43
+ * `containsBound` / `intersectsBound` (the lasso), `getNearestPoint` and
44
+ * `getLineIntersections` are untouched: a rubber band that had to graze the
45
+ * outline would be a second regression dressed as a fix.
46
+ */
47
+ export function backgroundIncludesPoint(bounds, x, y, options) {
48
+ const point = [x, y];
49
+ const points = getPointsFromBoundWithRotation(bounds);
50
+ const threshold = (options?.hitThreshold ?? BACKGROUND_BORDER_HIT_THRESHOLD) /
51
+ (options?.zoom ?? 1);
52
+ if (pointOnPolygonStoke(point, points, threshold))
53
+ return true;
54
+ return options?.ignoreTransparent === false && pointInPolygon(point, points);
55
+ }
@@ -1 +1,2 @@
1
1
  export * from './background.js';
2
+ export * from './hit-test.js';
@@ -1 +1,2 @@
1
1
  export * from './background.js';
2
+ export * from './hit-test.js';
@@ -405,6 +405,17 @@ export declare class EditPropsStore extends LifeCycleWatcher {
405
405
  dispose(): void;
406
406
  getStorage<T extends StoragePropsKey>(key: T): StorageProps[T] | null;
407
407
  recordLastProps(key: LastPropsKey, props: Partial<LastProps[LastPropsKey]>): void;
408
+ /**
409
+ * Remember the edgeless viewport of the current editing session, so the next
410
+ * mount of this doc reopens where the user left it.
411
+ *
412
+ * The storage key is shared by every mount of the same doc, so a read-only
413
+ * mount — an embedded preview, a mini map — must not overwrite what the full
414
+ * editor remembered: a preview is not an editing session. Readonly is read
415
+ * here, at save time (the edgeless root block saves on unmount), so a
416
+ * session that turns read-only before unmounting stops writing too.
417
+ */
418
+ saveViewport(viewport: StorageProps['viewport']): void;
408
419
  setStorage<T extends StoragePropsKey>(key: T, value: StorageProps[T]): void;
409
420
  unmounted(): void;
410
421
  }
@@ -156,6 +156,21 @@ export class EditPropsStore extends LifeCycleWatcher {
156
156
  const nextProps = mergeWith(clonedeep(innerProps), { [key]: overrideProps }, customizer);
157
157
  this.innerProps$.value = OptionalPropsSchema.parse(nextProps);
158
158
  }
159
+ /**
160
+ * Remember the edgeless viewport of the current editing session, so the next
161
+ * mount of this doc reopens where the user left it.
162
+ *
163
+ * The storage key is shared by every mount of the same doc, so a read-only
164
+ * mount — an embedded preview, a mini map — must not overwrite what the full
165
+ * editor remembered: a preview is not an editing session. Readonly is read
166
+ * here, at save time (the edgeless root block saves on unmount), so a
167
+ * session that turns read-only before unmounting stops writing too.
168
+ */
169
+ saveViewport(viewport) {
170
+ if (this.std.store.readonly)
171
+ return;
172
+ this.setStorage('viewport', viewport);
173
+ }
159
174
  setStorage(key, value) {
160
175
  const oldValue = this.getStorage(key);
161
176
  this._getStorage(key).setItem(this._getStorageKey(key), JSON.stringify(value));
@@ -1,4 +1,6 @@
1
+ import type { BlockStdScope } from '../../../std/index.js';
1
2
  import type { TemplateResult } from 'lit';
3
+ import { type ChromeWording } from '../translation-service/index.js';
2
4
  import type { ToolbarContext } from './context.js';
3
5
  export declare enum ActionPlacement {
4
6
  Start = 0,
@@ -25,9 +27,28 @@ type ActionBase = {
25
27
  };
26
28
  export type ToolbarAction = ActionBase & {
27
29
  label?: string;
30
+ /**
31
+ * The same word as {@link label}, said as an i18n key with its English
32
+ * default — resolved against the host's catalogue when the row is built, and
33
+ * written over `label` there (`combine`, in the toolbar widget).
34
+ *
35
+ * It exists because `label` is a VALUE and the translation seam needs `std`:
36
+ * a literal here is the one wording a host cannot override, which is what
37
+ * left a fully translated editor saying "Duplicate" and "Card view" in
38
+ * English (#183). Declaring the pair keeps a call site one line — the shape a
39
+ * static label already had — instead of pushing every entry that has words
40
+ * into a `generate`.
41
+ *
42
+ * Wordings are declared once in `@labre/affine-shared/services`
43
+ * (`CHROME_WORDINGS`), which is also what the key manifest walks — a pair
44
+ * spelt inline at a call site would be a key no host is ever offered.
45
+ */
46
+ labelWording?: ChromeWording;
28
47
  showLabel?: boolean;
29
48
  icon?: TemplateResult;
30
49
  tooltip?: string | TemplateResult;
50
+ /** {@link labelWording}, for the tooltip. Written over `tooltip`. */
51
+ tooltipWording?: ChromeWording;
31
52
  variant?: 'destructive';
32
53
  disabled?: ((cx: ToolbarContext) => boolean) | boolean;
33
54
  content?: ((cx: ToolbarContext) => TemplateResult | null) | (TemplateResult | null);
@@ -45,4 +66,15 @@ export type ToolbarActionGroupGenerator = ActionBase & {
45
66
  };
46
67
  export type ToolbarGenericAction = ToolbarAction | ToolbarActionGenerator | ToolbarActionGroup | ToolbarActionGroupGenerator;
47
68
  export type ToolbarActions<T extends ActionBase = ToolbarGenericAction> = T[];
69
+ /**
70
+ * What an entry SAYS, whether it declared a wording or a literal label.
71
+ *
72
+ * The row itself never needs this — `combine` resolves `labelWording` onto
73
+ * `label` before anything is drawn. It exists for the entries a widget renders
74
+ * ITSELF out of the raw declarations, which is exactly what the view switcher
75
+ * does: `content(ctx)` hands its own children to `affine-view-dropdown-menu`,
76
+ * so those never pass through `combine` and would otherwise be the one control
77
+ * in the editor left saying "Card view" in English (#183).
78
+ */
79
+ export declare function toolbarActionLabel(std: BlockStdScope, action: ToolbarAction): string;
48
80
  export {};
@@ -1,3 +1,4 @@
1
+ import { translateKey } from '../translation-service/index.js';
1
2
  export var ActionPlacement;
2
3
  (function (ActionPlacement) {
3
4
  ActionPlacement[ActionPlacement["Start"] = 0] = "Start";
@@ -5,3 +6,18 @@ export var ActionPlacement;
5
6
  ActionPlacement[ActionPlacement["End"] = 2] = "End";
6
7
  ActionPlacement[ActionPlacement["More"] = 4] = "More";
7
8
  })(ActionPlacement || (ActionPlacement = {}));
9
+ /**
10
+ * What an entry SAYS, whether it declared a wording or a literal label.
11
+ *
12
+ * The row itself never needs this — `combine` resolves `labelWording` onto
13
+ * `label` before anything is drawn. It exists for the entries a widget renders
14
+ * ITSELF out of the raw declarations, which is exactly what the view switcher
15
+ * does: `content(ctx)` hands its own children to `affine-view-dropdown-menu`,
16
+ * so those never pass through `combine` and would otherwise be the one control
17
+ * in the editor left saying "Card view" in English (#183).
18
+ */
19
+ export function toolbarActionLabel(std, action) {
20
+ return action.labelWording
21
+ ? translateKey(std, ...action.labelWording)
22
+ : (action.label ?? '');
23
+ }
@@ -9,11 +9,21 @@ export declare enum Flag {
9
9
  Accepting = 64
10
10
  }
11
11
  export declare class Flags {
12
+ #private;
12
13
  value$: import("@preact/signals-core").Signal<Flag>;
14
+ get revision(): number;
15
+ get revision$(): import("@preact/signals-core").Signal<number>;
13
16
  get value(): Flag;
14
17
  toggle(flag: Flag, activated: boolean): void;
15
18
  check(flag: Flag, value?: Flag): boolean;
16
19
  contains(flag: number, value?: Flag): boolean;
20
+ /**
21
+ * Forces subscribers to re-run while keeping `flag` activated.
22
+ *
23
+ * The revision bump is what actually notifies; the batch keeps both writes
24
+ * in a single notification so the toolbar never sees a transient
25
+ * {@link Flag.None} frame.
26
+ */
17
27
  refresh(flag: Flag): void;
18
28
  reset(): void;
19
29
  hide(): void;
@@ -16,6 +16,30 @@ export var Flag;
16
16
  export class Flags {
17
17
  constructor() {
18
18
  this.value$ = signal(Flag.None);
19
+ /**
20
+ * Bumped on every {@link Flags.refresh} call.
21
+ *
22
+ * `refresh` intentionally leaves `value$` unchanged, so it cannot be used as
23
+ * a repaint channel on its own: since `@preact/signals-core` 1.14 a `batch`
24
+ * that ends on its initial value notifies nobody. Subscribers that must
25
+ * re-run on a forced refresh have to read this revision counter as well.
26
+ */
27
+ this.#revision$ = signal(0);
28
+ }
29
+ /**
30
+ * Bumped on every {@link Flags.refresh} call.
31
+ *
32
+ * `refresh` intentionally leaves `value$` unchanged, so it cannot be used as
33
+ * a repaint channel on its own: since `@preact/signals-core` 1.14 a `batch`
34
+ * that ends on its initial value notifies nobody. Subscribers that must
35
+ * re-run on a forced refresh have to read this revision counter as well.
36
+ */
37
+ #revision$;
38
+ get revision() {
39
+ return this.#revision$.peek();
40
+ }
41
+ get revision$() {
42
+ return this.#revision$;
19
43
  }
20
44
  get value() {
21
45
  return this.value$.peek();
@@ -33,10 +57,17 @@ export class Flags {
33
57
  contains(flag, value = this.value) {
34
58
  return (flag & value) !== Flag.None;
35
59
  }
60
+ /**
61
+ * Forces subscribers to re-run while keeping `flag` activated.
62
+ *
63
+ * The revision bump is what actually notifies; the batch keeps both writes
64
+ * in a single notification so the toolbar never sees a transient
65
+ * {@link Flag.None} frame.
66
+ */
36
67
  refresh(flag) {
37
68
  batch(() => {
38
- this.toggle(flag, false);
39
69
  this.toggle(flag, true);
70
+ this.#revision$.value++;
40
71
  });
41
72
  }
42
73
  reset() {
@@ -0,0 +1,106 @@
1
+ /**
2
+ * The editor's own chrome wordings, as `[key, fallback]` pairs.
3
+ *
4
+ * ## Why they are a table and not forty literals
5
+ *
6
+ * "Copy", "Delete", "Card view" are the SAME word on a dozen toolbars — the
7
+ * bookmark's, the image's, the embed's, the linked doc's — and a literal per
8
+ * call site is a wording a host has to translate a dozen times and can get
9
+ * twelve different answers for. One pair per word, imported where it is
10
+ * rendered, is what makes the manifest's promise ("every key the library can
11
+ * ask for") describe a list a human can actually word.
12
+ *
13
+ * ## The tuple shape is load-bearing
14
+ *
15
+ * `translateKey(std, ...COPY)` spreads into `(std, key, fallback)`, so a call
16
+ * site names the wording once and cannot pair the wrong fallback with the right
17
+ * key. It is also what
18
+ * `packages/affine/all/src/__tests__/translations/manifest.unit.spec.ts` reads:
19
+ * the two adjacent literals below are the pair its drift check confirms the
20
+ * manifest against, which is why the wordings live in literals here rather than
21
+ * being derived from anything.
22
+ *
23
+ * Chrome, so every one of them ships an English default: a standalone
24
+ * playground with no `TranslationProvider` registered must read exactly as it
25
+ * did before these keys existed.
26
+ */
27
+ /** A wording the library can ask the host for: its key, and its English default. */
28
+ export type ChromeWording = readonly [key: string, fallback: string];
29
+ /**
30
+ * The transient messages the editor puts on screen after a gesture.
31
+ *
32
+ * They were the last English strings left in a fully translated host (#182):
33
+ * silent while nobody injected a `NotificationProvider`, and English in the
34
+ * middle of a French UI the moment somebody did.
35
+ */
36
+ export declare const TOAST_COPIED_TO_CLIPBOARD: ChromeWording;
37
+ export declare const TOAST_LINKED_DOC_CREATED: ChromeWording;
38
+ export declare const TOAST_NOTE_REMOVED_FROM_PAGE: ChromeWording;
39
+ export declare const TOAST_FRAME_INSERTED_INTO_PAGE: ChromeWording;
40
+ export declare const TOAST_NO_LINK_FOUND: ChromeWording;
41
+ export declare const TOOLBAR_BRING_TO_FRONT: ChromeWording;
42
+ export declare const TOOLBAR_SEND_TO_BACK: ChromeWording;
43
+ export declare const TOOLBAR_COPY: ChromeWording;
44
+ export declare const TOOLBAR_DUPLICATE: ChromeWording;
45
+ export declare const TOOLBAR_DELETE: ChromeWording;
46
+ export declare const TOOLBAR_LOCK: ChromeWording;
47
+ export declare const TOOLBAR_MORE: ChromeWording;
48
+ export declare const TOOLBAR_LINK: ChromeWording;
49
+ export declare const TOOLBAR_CREATE_LINKED_DOC: ChromeWording;
50
+ export declare const TOOLBAR_DRAW_CONNECTOR: ChromeWording;
51
+ /**
52
+ * The four wordings of the view switcher — the control that decides whether a
53
+ * link is drawn as words, as a card or as the document itself.
54
+ *
55
+ * One set for every block that offers it (bookmark, attachment, embed, linked
56
+ * doc, synced doc, iframe, the inline link and the inline reference): they are
57
+ * one control with one vocabulary, and a host that had to word "Card view"
58
+ * eight times would end up with eight wordings of it.
59
+ */
60
+ export declare const TOOLBAR_SWITCH_VIEW: ChromeWording;
61
+ export declare const TOOLBAR_INLINE_VIEW: ChromeWording;
62
+ export declare const TOOLBAR_CARD_VIEW: ChromeWording;
63
+ export declare const TOOLBAR_EMBED_VIEW: ChromeWording;
64
+ /**
65
+ * The four states a linked-doc card can be in with nothing to show: deleted,
66
+ * unreadable, empty, and — for the SYNCED card, which frames a page rather
67
+ * than a doc — empty in its own words.
68
+ *
69
+ * All four and not only the two the recette caught (#183): they are one
70
+ * sentence rendered by one ternary, and translating half of it would leave a
71
+ * card that changes language when the document it points at goes missing.
72
+ */
73
+ export declare const LINKED_DOC_DELETED: ChromeWording;
74
+ export declare const LINKED_DOC_FAILED: ChromeWording;
75
+ export declare const LINKED_DOC_EMPTY_PREVIEW: ChromeWording;
76
+ export declare const SYNCED_DOC_EMPTY_PREVIEW: ChromeWording;
77
+ /**
78
+ * The resize toggle, which is the one entry EVERY framework board carries —
79
+ * Wardley, BPMN, C4, EDGY, Cynefin, Estuarine and the two DDD boards all
80
+ * register the same always-on button (`docs/adr/0009`: a stored board must stay
81
+ * usable with its framework switched off).
82
+ *
83
+ * Declared here rather than once per framework because it is not a framework's
84
+ * own word: it names a behaviour of the generic frame primitive, and eight
85
+ * copies of it in eight manifests would be eight keys for one tooltip.
86
+ */
87
+ export declare const BOARD_RESIZE_TOGGLE: ChromeWording;
88
+ /**
89
+ * The legend button, in the two wordings the boards actually use: the notation
90
+ * boards say "notation", Wardley says "components".
91
+ *
92
+ * Two keys and not one interpolated sentence, for the reason the interchange
93
+ * counts already ran on — the seam has no interpolation, so the smallest honest
94
+ * unit is the whole sentence.
95
+ */
96
+ export declare const BOARD_LEGEND_NOTATION: ChromeWording;
97
+ export declare const BOARD_LEGEND_COMPONENTS: ChromeWording;
98
+ /**
99
+ * Every wording declared above, in declaration order.
100
+ *
101
+ * The manifest (`@labre/affine/translations`) walks this instead of restating
102
+ * the pairs, so a wording added here reaches a host's catalogue with no second
103
+ * edit — the same "declared data, not restated data" rule the roles, the rules
104
+ * and the commands already follow.
105
+ */
106
+ export declare const CHROME_WORDINGS: readonly ChromeWording[];