@formicoidea/labre-core 0.35.0 → 0.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_pkgs/affine-block-surface/extensions/reading-toolbar.js +12 -12
- package/dist/_pkgs/affine-block-surface/extensions/reading-widget.d.ts +25 -13
- package/dist/_pkgs/affine-block-surface/extensions/reading-widget.js +58 -24
- package/dist/_pkgs/affine-block-surface/extensions/reading.d.ts +83 -7
- package/dist/_pkgs/affine-block-surface/extensions/reading.js +41 -14
- package/dist/_pkgs/affine-block-surface/extensions/tags-toolbar.js +7 -0
- package/dist/_pkgs/affine-components/highlight-dropdown-menu/highlight-duotone-icon.js +8 -8
- package/dist/_pkgs/affine-components/highlight-dropdown-menu/text-duotone-icon.js +11 -11
- package/dist/_pkgs/affine-components/icons/text.js +426 -426
- package/dist/_pkgs/affine-components/toolbar/menu-button.js +13 -0
- package/dist/_pkgs/affine-gfx-shape/element-view.d.ts +42 -1
- package/dist/_pkgs/affine-gfx-shape/element-view.js +54 -0
- package/dist/_pkgs/affine-gfx-shape/toolbar/index.d.ts +1 -0
- package/dist/_pkgs/affine-gfx-shape/toolbar/index.js +1 -0
- package/dist/_pkgs/affine-gfx-shape/toolbar/palette-color-action.d.ts +25 -0
- package/dist/_pkgs/affine-gfx-shape/toolbar/palette-color-action.js +123 -0
- package/dist/_pkgs/affine-model/elements/wardley/node.d.ts +21 -6
- package/dist/_pkgs/affine-model/elements/wardley/node.js +20 -5
- package/dist/_pkgs/affine-shared/services/telemetry-service/block-lifecycle-watcher.d.ts +16 -1
- package/dist/_pkgs/affine-shared/services/telemetry-service/block-lifecycle-watcher.js +27 -2
- package/dist/_pkgs/affine-widget-edgeless-auto-connect/index.js +124 -124
- package/dist/_pkgs/affine-widget-edgeless-zoom-toolbar/index.js +26 -26
- package/dist/_pkgs/affine-widget-keyboard-toolbar/icons.js +98 -98
- package/dist/_pkgs/affine-widget-page-dragging-area/index.js +10 -10
- package/dist/_pkgs/affine-widget-viewport-overlay/index.js +13 -13
- package/dist/_pkgs/data-view/core/group-by/renderer/boolean-group.js +11 -11
- package/dist/_pkgs/data-view/core/utils/menu-title.js +17 -17
- package/dist/_pkgs/data-view/view-presets/table/pc-virtual/group/top/group-title.js +18 -18
- package/dist/_pkgs/std/extension/command-registry.d.ts +15 -8
- package/dist/extensions/command-telemetry.js +4 -0
- package/dist/translations.js +8 -2
- package/package.json +1 -1
|
@@ -247,6 +247,19 @@ export class EditorMenuAction extends LitElement {
|
|
|
247
247
|
pointer-events: none;
|
|
248
248
|
}
|
|
249
249
|
|
|
250
|
+
/*
|
|
251
|
+
* ...unless the row is a TOGGLE rather than one of N. The rule above reads
|
|
252
|
+
* a selected row as inert, which is right for a single choice — re-picking
|
|
253
|
+
* the option already in force is a no-op, so swallowing the click costs
|
|
254
|
+
* nothing. A toggle is the opposite: re-clicking the value in force is the
|
|
255
|
+
* ONLY way to un-pick it (the qualification dropdown's tag values), and an
|
|
256
|
+
* inert row leaves the user with a tag that cannot be removed. Such a row
|
|
257
|
+
* opts the pointer back in through data-toggle.
|
|
258
|
+
*/
|
|
259
|
+
:host([data-selected][data-toggle]) {
|
|
260
|
+
pointer-events: auto;
|
|
261
|
+
}
|
|
262
|
+
|
|
250
263
|
/*
|
|
251
264
|
* An OPTION row: one of N choices, the one in force carrying
|
|
252
265
|
* data-selected. Opt-in through data-option, because a plain command row is
|
|
@@ -1,7 +1,24 @@
|
|
|
1
1
|
import { ShapeElementModel } from '../affine-model/index.js';
|
|
2
2
|
import { GfxElementModelView } from '../std/gfx/index.js';
|
|
3
3
|
import { PolygonVertexEditingOverlay } from './overlay/polygon-vertex-editing-overlay.js';
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* The view every shape gets, and the base a framework NODE view extends when
|
|
6
|
+
* its model is a {@link ShapeElementModel} subclass.
|
|
7
|
+
*
|
|
8
|
+
* Generic over the model, with the plain shape as the default, so
|
|
9
|
+
* `WardleyNodeView extends ShapeElementView<WardleyNodeElementModel>` reads its
|
|
10
|
+
* own `kind` off `this.model` without a cast. Every existing call site names no
|
|
11
|
+
* type argument and is unchanged.
|
|
12
|
+
*
|
|
13
|
+
* A framework node that extends this inherits the polygon VERTEX EDITOR, which
|
|
14
|
+
* is the whole reason to: {@link enterVertexEditingMode} is what the toolbar's
|
|
15
|
+
* "Edit vertices" action calls, and the action checks `view instanceof
|
|
16
|
+
* ShapeElementView` — a node view outside this hierarchy makes that button
|
|
17
|
+
* silently do nothing. What such a subclass usually does NOT want is the other
|
|
18
|
+
* thing wired here, the double-click-to-edit; {@link editsTextOnDblClick} is
|
|
19
|
+
* the one line it has to say so.
|
|
20
|
+
*/
|
|
21
|
+
export declare class ShapeElementView<T extends ShapeElementModel = ShapeElementModel> extends GfxElementModelView<T> {
|
|
5
22
|
static type: string;
|
|
6
23
|
/** Overlay for polygon vertex editing (only active for polygon shapes). */
|
|
7
24
|
private _vertexEditingOverlay;
|
|
@@ -16,6 +33,17 @@ export declare class ShapeElementView extends GfxElementModelView<ShapeElementMo
|
|
|
16
33
|
* visually correct on every drag frame.
|
|
17
34
|
*/
|
|
18
35
|
private _syncConnectorPaths;
|
|
36
|
+
/**
|
|
37
|
+
* Whether a double-click on THIS element opens its own inner-text editor.
|
|
38
|
+
*
|
|
39
|
+
* True for every plain shape, which is what this editor has always done: a
|
|
40
|
+
* shape is a box you write in. It is a HOOK rather than a constant because a
|
|
41
|
+
* framework node is often not one — its name may be a text element grouped
|
|
42
|
+
* beside it, or the element may be a glyph's own wiring that must carry no
|
|
43
|
+
* words at all — and a subclass that overrode `onCreated` to dodge this one
|
|
44
|
+
* wiring would lose the vertex editor set up beside it.
|
|
45
|
+
*/
|
|
46
|
+
protected editsTextOnDblClick(): boolean;
|
|
19
47
|
private _initDblClickToEdit;
|
|
20
48
|
/**
|
|
21
49
|
* Whether a vertex is currently being dragged.
|
|
@@ -61,6 +89,19 @@ export declare class ShapeElementView extends GfxElementModelView<ShapeElementMo
|
|
|
61
89
|
* The overlay is shown when a polygon is selected and removed when
|
|
62
90
|
* deselected or when a non-polygon shape is selected.
|
|
63
91
|
*/
|
|
92
|
+
/**
|
|
93
|
+
* Whether THIS element's corners may be moved by hand.
|
|
94
|
+
*
|
|
95
|
+
* True for every plain polygon. The companion of {@link editsTextOnDblClick},
|
|
96
|
+
* and a hook for the same reason: a framework polygon's outline is often the
|
|
97
|
+
* NOTATION rather than a drawing — a Wardley accelerator points right and a
|
|
98
|
+
* decelerator points left, and that direction is the whole of what separates
|
|
99
|
+
* them — so hovering one must not offer a handle that can only turn a
|
|
100
|
+
* statement into a blob. Declining here skips the selection overlay, the
|
|
101
|
+
* pointer wiring and {@link enterVertexEditingMode} together, which is the
|
|
102
|
+
* point: a half-wired overlay is worse than none.
|
|
103
|
+
*/
|
|
104
|
+
protected editsVerticesOnSelection(): boolean;
|
|
64
105
|
private _initPolygonVertexEditing;
|
|
65
106
|
private _ensureVertexEditingOverlay;
|
|
66
107
|
private _removeVertexEditingOverlay;
|
|
@@ -6,6 +6,23 @@ import { GfxElementModelView, GfxViewInteractionExtension, } from '../std/gfx/in
|
|
|
6
6
|
import { normalizeShapeBound } from './element-renderer/index.js';
|
|
7
7
|
import { PolygonVertexEditingOverlay } from './overlay/polygon-vertex-editing-overlay.js';
|
|
8
8
|
import { mountShapeTextEditor } from './text/edgeless-shape-text-editor.js';
|
|
9
|
+
/**
|
|
10
|
+
* The view every shape gets, and the base a framework NODE view extends when
|
|
11
|
+
* its model is a {@link ShapeElementModel} subclass.
|
|
12
|
+
*
|
|
13
|
+
* Generic over the model, with the plain shape as the default, so
|
|
14
|
+
* `WardleyNodeView extends ShapeElementView<WardleyNodeElementModel>` reads its
|
|
15
|
+
* own `kind` off `this.model` without a cast. Every existing call site names no
|
|
16
|
+
* type argument and is unchanged.
|
|
17
|
+
*
|
|
18
|
+
* A framework node that extends this inherits the polygon VERTEX EDITOR, which
|
|
19
|
+
* is the whole reason to: {@link enterVertexEditingMode} is what the toolbar's
|
|
20
|
+
* "Edit vertices" action calls, and the action checks `view instanceof
|
|
21
|
+
* ShapeElementView` — a node view outside this hierarchy makes that button
|
|
22
|
+
* silently do nothing. What such a subclass usually does NOT want is the other
|
|
23
|
+
* thing wired here, the double-click-to-edit; {@link editsTextOnDblClick} is
|
|
24
|
+
* the one line it has to say so.
|
|
25
|
+
*/
|
|
9
26
|
export class ShapeElementView extends GfxElementModelView {
|
|
10
27
|
constructor() {
|
|
11
28
|
super(...arguments);
|
|
@@ -78,8 +95,23 @@ export class ShapeElementView extends GfxElementModelView {
|
|
|
78
95
|
ConnectorPathGenerator.updatePath(connector, null, elementGetter);
|
|
79
96
|
}
|
|
80
97
|
}
|
|
98
|
+
/**
|
|
99
|
+
* Whether a double-click on THIS element opens its own inner-text editor.
|
|
100
|
+
*
|
|
101
|
+
* True for every plain shape, which is what this editor has always done: a
|
|
102
|
+
* shape is a box you write in. It is a HOOK rather than a constant because a
|
|
103
|
+
* framework node is often not one — its name may be a text element grouped
|
|
104
|
+
* beside it, or the element may be a glyph's own wiring that must carry no
|
|
105
|
+
* words at all — and a subclass that overrode `onCreated` to dodge this one
|
|
106
|
+
* wiring would lose the vertex editor set up beside it.
|
|
107
|
+
*/
|
|
108
|
+
editsTextOnDblClick() {
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
81
111
|
_initDblClickToEdit() {
|
|
82
112
|
this.on('dblclick', () => {
|
|
113
|
+
if (!this.editsTextOnDblClick())
|
|
114
|
+
return;
|
|
83
115
|
const edgeless = this.std.view.getBlock(this.std.store.root.id);
|
|
84
116
|
if (edgeless &&
|
|
85
117
|
!this.model.isLocked() &&
|
|
@@ -333,9 +365,26 @@ export class ShapeElementView extends GfxElementModelView {
|
|
|
333
365
|
* The overlay is shown when a polygon is selected and removed when
|
|
334
366
|
* deselected or when a non-polygon shape is selected.
|
|
335
367
|
*/
|
|
368
|
+
/**
|
|
369
|
+
* Whether THIS element's corners may be moved by hand.
|
|
370
|
+
*
|
|
371
|
+
* True for every plain polygon. The companion of {@link editsTextOnDblClick},
|
|
372
|
+
* and a hook for the same reason: a framework polygon's outline is often the
|
|
373
|
+
* NOTATION rather than a drawing — a Wardley accelerator points right and a
|
|
374
|
+
* decelerator points left, and that direction is the whole of what separates
|
|
375
|
+
* them — so hovering one must not offer a handle that can only turn a
|
|
376
|
+
* statement into a blob. Declining here skips the selection overlay, the
|
|
377
|
+
* pointer wiring and {@link enterVertexEditingMode} together, which is the
|
|
378
|
+
* point: a half-wired overlay is worse than none.
|
|
379
|
+
*/
|
|
380
|
+
editsVerticesOnSelection() {
|
|
381
|
+
return true;
|
|
382
|
+
}
|
|
336
383
|
_initPolygonVertexEditing() {
|
|
337
384
|
if (this.model.shapeType !== ShapeType.Polygon)
|
|
338
385
|
return;
|
|
386
|
+
if (!this.editsVerticesOnSelection())
|
|
387
|
+
return;
|
|
339
388
|
// Subscribe to selection changes to manage overlay lifecycle
|
|
340
389
|
this.disposable.add(this.gfx.selection.slots.updated.subscribe(() => {
|
|
341
390
|
const selectedIds = this.gfx.selection.selectedIds;
|
|
@@ -481,6 +530,11 @@ export class ShapeElementView extends GfxElementModelView {
|
|
|
481
530
|
enterVertexEditingMode() {
|
|
482
531
|
if (this.model.shapeType !== ShapeType.Polygon)
|
|
483
532
|
return;
|
|
533
|
+
// The same hook the selection wiring reads, asked again here: this is the
|
|
534
|
+
// other door into the mode, and an element that declined the wiring would
|
|
535
|
+
// otherwise get an overlay with no pointer handlers behind it.
|
|
536
|
+
if (!this.editsVerticesOnSelection())
|
|
537
|
+
return;
|
|
484
538
|
this._ensureVertexEditingOverlay();
|
|
485
539
|
this._enterVertexEditingMode();
|
|
486
540
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type Color, type Palette, ShapeElementModel } from '../../affine-model/index.js';
|
|
2
|
+
import type { ToolbarAction } from '../../affine-shared/services/index.js';
|
|
3
|
+
export declare function neutralPalettes(): Palette[];
|
|
4
|
+
/**
|
|
5
|
+
* A fill / stroke colour picker identical to the shape one, seeded with the
|
|
6
|
+
* caller's swatches (`.palettes`) instead of the default editor palette.
|
|
7
|
+
*
|
|
8
|
+
* Frameworks whose nodes are {@link ShapeElementModel} subclasses (EDGY,
|
|
9
|
+
* Wardley, …) all want the same picker with their own colours in front of it,
|
|
10
|
+
* so the ~100 lines that wire `edgeless-shape-color-picker` to the models live
|
|
11
|
+
* here once rather than being copied per framework.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* The last word on what a picked swatch actually WRITES as a model's fill.
|
|
15
|
+
*
|
|
16
|
+
* The identity by default, which is what a colour picker means everywhere. It
|
|
17
|
+
* exists because a swatch is a hue and some elements are washes: a Wardley
|
|
18
|
+
* ZONE is drawn over the map it groups, so its fill carries an alpha, and a
|
|
19
|
+
* picked swatch that replaced `#c6dbfc99` with an opaque `#5b9cf6` would hide
|
|
20
|
+
* the very map the zone is there to annotate. A framework that has such
|
|
21
|
+
* elements says so here — per model, because the same picker serves its
|
|
22
|
+
* washes and its opaque artefacts.
|
|
23
|
+
*/
|
|
24
|
+
export type FillColorFor = (model: ShapeElementModel, value: Color) => Color;
|
|
25
|
+
export declare function paletteColorAction(id: string, palettes: Palette[], fillColorFor?: FillColorFor): ToolbarAction;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { EdgelessCRUDIdentifier } from '../../affine-block-surface/index.js';
|
|
2
|
+
import { packColor, } from '../../affine-components/color-picker/index.js';
|
|
3
|
+
import { DefaultTheme, isTransparent, LineWidth, resolveColor, ShapeElementModel, StrokeStyle, } from '../../affine-model/index.js';
|
|
4
|
+
import { getMostCommonValue } from '../../affine-shared/utils/index.js';
|
|
5
|
+
import { html } from 'lit';
|
|
6
|
+
/**
|
|
7
|
+
* The neutrals of the default editor palette: greys, white, black, transparent.
|
|
8
|
+
*
|
|
9
|
+
* A framework that seeds the picker with its OWN swatches drops the historical
|
|
10
|
+
* editor colours — they say nothing in that notation — but keeps the neutrals,
|
|
11
|
+
* which every drawing needs and no notation owns.
|
|
12
|
+
*/
|
|
13
|
+
const NEUTRAL_KEY = /grey|gray|white|black|transparent/i;
|
|
14
|
+
export function neutralPalettes() {
|
|
15
|
+
return DefaultTheme.Palettes.filter(p => NEUTRAL_KEY.test(p.key));
|
|
16
|
+
}
|
|
17
|
+
// Mirror of the shape color action's text-color rule.
|
|
18
|
+
function getTextColor(fillColor) {
|
|
19
|
+
if (fillColor === DefaultTheme.black)
|
|
20
|
+
return DefaultTheme.white;
|
|
21
|
+
if (fillColor === DefaultTheme.white)
|
|
22
|
+
return DefaultTheme.black;
|
|
23
|
+
return DefaultTheme.shapeTextColor;
|
|
24
|
+
}
|
|
25
|
+
export function paletteColorAction(id, palettes, fillColorFor = (_model, value) => value) {
|
|
26
|
+
return {
|
|
27
|
+
id,
|
|
28
|
+
when(ctx) {
|
|
29
|
+
return ctx.getSurfaceModelsByType(ShapeElementModel).length > 0;
|
|
30
|
+
},
|
|
31
|
+
content(ctx) {
|
|
32
|
+
const models = ctx.getSurfaceModelsByType(ShapeElementModel);
|
|
33
|
+
if (!models.length)
|
|
34
|
+
return null;
|
|
35
|
+
const enableCustomColor = ctx.features.getFlag('enable_color_picker');
|
|
36
|
+
const theme = ctx.theme.edgeless$.value;
|
|
37
|
+
const firstModel = models[0];
|
|
38
|
+
const originalFillColor = firstModel.fillColor;
|
|
39
|
+
const originalStrokeColor = firstModel.strokeColor;
|
|
40
|
+
const mapped = models.map(({ filled, fillColor, strokeColor, strokeWidth, strokeStyle }) => ({
|
|
41
|
+
fillColor: filled
|
|
42
|
+
? resolveColor(fillColor, theme)
|
|
43
|
+
: DefaultTheme.transparent,
|
|
44
|
+
strokeColor: resolveColor(strokeColor, theme),
|
|
45
|
+
strokeWidth,
|
|
46
|
+
strokeStyle,
|
|
47
|
+
}));
|
|
48
|
+
const fillColor = getMostCommonValue(mapped, 'fillColor') ??
|
|
49
|
+
resolveColor(DefaultTheme.shapeFillColor, theme);
|
|
50
|
+
const strokeColor = getMostCommonValue(mapped, 'strokeColor') ??
|
|
51
|
+
resolveColor(DefaultTheme.shapeStrokeColor, theme);
|
|
52
|
+
const strokeWidth = getMostCommonValue(mapped, 'strokeWidth') ?? LineWidth.Four;
|
|
53
|
+
const strokeStyle = getMostCommonValue(mapped, 'strokeStyle') ?? StrokeStyle.Solid;
|
|
54
|
+
const pickColorWrapper = (field, pickCallback) => (e) => {
|
|
55
|
+
e.stopPropagation();
|
|
56
|
+
switch (e.detail.type) {
|
|
57
|
+
case 'pick':
|
|
58
|
+
pickCallback(e.detail.detail);
|
|
59
|
+
break;
|
|
60
|
+
case 'start':
|
|
61
|
+
ctx.store.captureSync();
|
|
62
|
+
models.forEach(model => model.stash(field));
|
|
63
|
+
break;
|
|
64
|
+
case 'end':
|
|
65
|
+
ctx.store.transact(() => {
|
|
66
|
+
models.forEach(model => model.pop(field));
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
const onPickFillColor = pickColorWrapper('fillColor', palette => {
|
|
71
|
+
const value = palette.value;
|
|
72
|
+
const filled = isTransparent(value);
|
|
73
|
+
const crud = ctx.std.get(EdgelessCRUDIdentifier);
|
|
74
|
+
models.forEach(model => {
|
|
75
|
+
// Per model, so a selection mixing a wash and an opaque artefact
|
|
76
|
+
// gets the right fill on each — and `packColor` runs on what the
|
|
77
|
+
// hook returned, since that is the value the document stores.
|
|
78
|
+
const props = packColor('fillColor', fillColorFor(model, value));
|
|
79
|
+
if (filled && !model.filled) {
|
|
80
|
+
const color = getTextColor(value);
|
|
81
|
+
Object.assign(props, { filled, color });
|
|
82
|
+
}
|
|
83
|
+
crud.updateElement(model.id, props);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
const onPickStrokeColor = pickColorWrapper('strokeColor', palette => {
|
|
87
|
+
const props = packColor('strokeColor', palette.value);
|
|
88
|
+
const crud = ctx.std.get(EdgelessCRUDIdentifier);
|
|
89
|
+
models.forEach(model => crud.updateElement(model.id, props));
|
|
90
|
+
});
|
|
91
|
+
const onPickStrokeStyle = (e) => {
|
|
92
|
+
e.stopPropagation();
|
|
93
|
+
const { type, value } = e.detail;
|
|
94
|
+
const crud = ctx.std.get(EdgelessCRUDIdentifier);
|
|
95
|
+
const props = type === 'size'
|
|
96
|
+
? { strokeWidth: value }
|
|
97
|
+
: { strokeStyle: value };
|
|
98
|
+
for (const model of models) {
|
|
99
|
+
crud.updateElement(model.id, props);
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
return html `
|
|
103
|
+
<edgeless-shape-color-picker
|
|
104
|
+
@pickFillColor=${onPickFillColor}
|
|
105
|
+
@pickStrokeColor=${onPickStrokeColor}
|
|
106
|
+
@pickStrokeStyle=${onPickStrokeStyle}
|
|
107
|
+
.palettes=${palettes}
|
|
108
|
+
.payload=${{
|
|
109
|
+
fillColor,
|
|
110
|
+
strokeColor,
|
|
111
|
+
strokeWidth,
|
|
112
|
+
strokeStyle,
|
|
113
|
+
originalFillColor,
|
|
114
|
+
originalStrokeColor,
|
|
115
|
+
theme,
|
|
116
|
+
enableCustomColor,
|
|
117
|
+
}}
|
|
118
|
+
>
|
|
119
|
+
</edgeless-shape-color-picker>
|
|
120
|
+
`;
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
}
|
|
@@ -1,16 +1,31 @@
|
|
|
1
1
|
import { ShapeElementModel } from '../shape/index.js';
|
|
2
|
-
export type WardleyNodeKind = 'component' | 'anchor' | 'pipeline' | 'handle' | 'market' | 'ecosystem' | 'method';
|
|
2
|
+
export type WardleyNodeKind = 'component' | 'anchor' | 'pipeline' | 'handle' | 'market' | 'ecosystem' | 'method' | 'porter' | 'accelerator' | 'decelerator' | 'area';
|
|
3
3
|
/**
|
|
4
4
|
* A Wardley map node. Extends {@link ShapeElementModel} (a native ellipse) so it
|
|
5
5
|
* inherits ALL shape behaviour — editable stroke width / colors, native resize,
|
|
6
6
|
* center connector anchor, the shape context toolbar — for free. `kind`
|
|
7
7
|
* discriminates the plain `component`, the `anchor` (which the renderer
|
|
8
8
|
* decorates with an inscribed person glyph), the two pieces of a pipeline (the
|
|
9
|
-
* `pipeline` body + its square `handle`), the `market` outer circle,
|
|
10
|
-
* `ecosystem` grey backing disk
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
9
|
+
* `pipeline` body + its square `handle`), the `market` outer circle, the
|
|
10
|
+
* `ecosystem` grey backing disk, the `porter` circle — a Porter's-forces
|
|
11
|
+
* glyph, which is the one kind that is NOT a link in the value chain: it marks
|
|
12
|
+
* an external competition force acting on the map — and the `accelerator` /
|
|
13
|
+
* `decelerator` arrows, which are not links in the chain either: they annotate
|
|
14
|
+
* the CLIMATE, saying that something is speeding evolution up or slowing it
|
|
15
|
+
* down. Those two are the first kinds drawn as a native POLYGON rather than an
|
|
16
|
+
* ellipse or a rect — and the `area` is the third: a translucent ZONE of the
|
|
17
|
+
* map, drawn as a rect or as a polygon over the components it groups, and the
|
|
18
|
+
* only kind whose two shapes are the same kind (the `shapeType` says which).
|
|
19
|
+
* Composite nodes (pipeline / market / ecosystem / porter / accelerator /
|
|
20
|
+
* decelerator) are built by grouping several of these + native connectors + a
|
|
21
|
+
* text label. The text label is a SEPARATE native text element grouped with the
|
|
22
|
+
* node, not stored here — the `porter` and the `area` being the exceptions that
|
|
23
|
+
* prove it, since the porter's one letter and the area's name are the shape's
|
|
24
|
+
* own inner text.
|
|
25
|
+
*
|
|
26
|
+
* `kind` is ADDITIVE by construction: a value is only ever appended, so a
|
|
27
|
+
* document written before a value was appended carries none of it and opens
|
|
28
|
+
* unchanged. `area` is the most recent, and needs no migration.
|
|
14
29
|
*/
|
|
15
30
|
export declare class WardleyNodeElementModel extends ShapeElementModel {
|
|
16
31
|
get type(): string;
|
|
@@ -40,11 +40,26 @@ import { ShapeElementModel } from '../shape/index.js';
|
|
|
40
40
|
* center connector anchor, the shape context toolbar — for free. `kind`
|
|
41
41
|
* discriminates the plain `component`, the `anchor` (which the renderer
|
|
42
42
|
* decorates with an inscribed person glyph), the two pieces of a pipeline (the
|
|
43
|
-
* `pipeline` body + its square `handle`), the `market` outer circle,
|
|
44
|
-
* `ecosystem` grey backing disk
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
43
|
+
* `pipeline` body + its square `handle`), the `market` outer circle, the
|
|
44
|
+
* `ecosystem` grey backing disk, the `porter` circle — a Porter's-forces
|
|
45
|
+
* glyph, which is the one kind that is NOT a link in the value chain: it marks
|
|
46
|
+
* an external competition force acting on the map — and the `accelerator` /
|
|
47
|
+
* `decelerator` arrows, which are not links in the chain either: they annotate
|
|
48
|
+
* the CLIMATE, saying that something is speeding evolution up or slowing it
|
|
49
|
+
* down. Those two are the first kinds drawn as a native POLYGON rather than an
|
|
50
|
+
* ellipse or a rect — and the `area` is the third: a translucent ZONE of the
|
|
51
|
+
* map, drawn as a rect or as a polygon over the components it groups, and the
|
|
52
|
+
* only kind whose two shapes are the same kind (the `shapeType` says which).
|
|
53
|
+
* Composite nodes (pipeline / market / ecosystem / porter / accelerator /
|
|
54
|
+
* decelerator) are built by grouping several of these + native connectors + a
|
|
55
|
+
* text label. The text label is a SEPARATE native text element grouped with the
|
|
56
|
+
* node, not stored here — the `porter` and the `area` being the exceptions that
|
|
57
|
+
* prove it, since the porter's one letter and the area's name are the shape's
|
|
58
|
+
* own inner text.
|
|
59
|
+
*
|
|
60
|
+
* `kind` is ADDITIVE by construction: a value is only ever appended, so a
|
|
61
|
+
* document written before a value was appended carries none of it and opens
|
|
62
|
+
* unchanged. `area` is the most recent, and needs no migration.
|
|
48
63
|
*/
|
|
49
64
|
let WardleyNodeElementModel = (() => {
|
|
50
65
|
let _classSuper = ShapeElementModel;
|
|
@@ -10,6 +10,20 @@ export declare const ABANDON_WINDOW_MS = 15000;
|
|
|
10
10
|
* BlockUsageDuration when it closes).
|
|
11
11
|
*/
|
|
12
12
|
export declare const IDLE_GAP_MS = 30000;
|
|
13
|
+
/**
|
|
14
|
+
* The flavours whose lifecycle is reported. Canvas blocks only: the surface
|
|
15
|
+
* (every framework element mutation lands on it), the cards placed on it and
|
|
16
|
+
* the media that can be dropped there.
|
|
17
|
+
*
|
|
18
|
+
* Prose is deliberately out (PO decision, 2026-09-03). Per-paragraph edit
|
|
19
|
+
* sessions are the volume driver of the whole taxonomy and answer no product
|
|
20
|
+
* question the document itself cannot answer later — which documents carry
|
|
21
|
+
* prose next to a map is a corpus query, not a telemetry one. What evaporates
|
|
22
|
+
* if not captured is behaviour on the canvas: hesitation, abandonment, time
|
|
23
|
+
* spent — so that is what stays on the bus. Reversible in one line.
|
|
24
|
+
*/
|
|
25
|
+
export declare const CANVAS_FLAVOURS: ReadonlySet<string>;
|
|
26
|
+
export declare function isCanvasFlavour(flavour: string): boolean;
|
|
13
27
|
/**
|
|
14
28
|
* The subset of the store `blockUpdated` payload the tracker relies on —
|
|
15
29
|
* structurally compatible with `Store['slots']['blockUpdated']` payloads.
|
|
@@ -29,7 +43,8 @@ export type BlockLifecyclePayload = {
|
|
|
29
43
|
* BlockCreated is intentionally NOT emitted here — it stays a UI-intent
|
|
30
44
|
* event emitted at insertion sites (slash menu, toolbars), to avoid double
|
|
31
45
|
* counting. Remote and initial-load mutations are ignored (`isLocal` only):
|
|
32
|
-
* the taxonomy describes user intent, not store mechanics.
|
|
46
|
+
* the taxonomy describes user intent, not store mechanics. Non-canvas
|
|
47
|
+
* flavours are ignored too — see {@link CANVAS_FLAVOURS}.
|
|
33
48
|
*
|
|
34
49
|
* Pure logic, injectable clock — see the unit tests.
|
|
35
50
|
*/
|
|
@@ -10,6 +10,30 @@ export const ABANDON_WINDOW_MS = 15_000;
|
|
|
10
10
|
* BlockUsageDuration when it closes).
|
|
11
11
|
*/
|
|
12
12
|
export const IDLE_GAP_MS = 30_000;
|
|
13
|
+
/**
|
|
14
|
+
* The flavours whose lifecycle is reported. Canvas blocks only: the surface
|
|
15
|
+
* (every framework element mutation lands on it), the cards placed on it and
|
|
16
|
+
* the media that can be dropped there.
|
|
17
|
+
*
|
|
18
|
+
* Prose is deliberately out (PO decision, 2026-09-03). Per-paragraph edit
|
|
19
|
+
* sessions are the volume driver of the whole taxonomy and answer no product
|
|
20
|
+
* question the document itself cannot answer later — which documents carry
|
|
21
|
+
* prose next to a map is a corpus query, not a telemetry one. What evaporates
|
|
22
|
+
* if not captured is behaviour on the canvas: hesitation, abandonment, time
|
|
23
|
+
* spent — so that is what stays on the bus. Reversible in one line.
|
|
24
|
+
*/
|
|
25
|
+
export const CANVAS_FLAVOURS = new Set([
|
|
26
|
+
'affine:surface',
|
|
27
|
+
'affine:note',
|
|
28
|
+
'affine:frame',
|
|
29
|
+
'affine:edgeless-text',
|
|
30
|
+
'affine:image',
|
|
31
|
+
'affine:attachment',
|
|
32
|
+
'affine:bookmark',
|
|
33
|
+
]);
|
|
34
|
+
export function isCanvasFlavour(flavour) {
|
|
35
|
+
return CANVAS_FLAVOURS.has(flavour) || flavour.startsWith('affine:embed-');
|
|
36
|
+
}
|
|
13
37
|
/**
|
|
14
38
|
* Turns raw store mutations into the block lifecycle taxonomy (see README):
|
|
15
39
|
* BlockEdited / BlockDeleted / BlockAbandoned / BlockUsageDuration.
|
|
@@ -17,7 +41,8 @@ export const IDLE_GAP_MS = 30_000;
|
|
|
17
41
|
* BlockCreated is intentionally NOT emitted here — it stays a UI-intent
|
|
18
42
|
* event emitted at insertion sites (slash menu, toolbars), to avoid double
|
|
19
43
|
* counting. Remote and initial-load mutations are ignored (`isLocal` only):
|
|
20
|
-
* the taxonomy describes user intent, not store mechanics.
|
|
44
|
+
* the taxonomy describes user intent, not store mechanics. Non-canvas
|
|
45
|
+
* flavours are ignored too — see {@link CANVAS_FLAVOURS}.
|
|
21
46
|
*
|
|
22
47
|
* Pure logic, injectable clock — see the unit tests.
|
|
23
48
|
*/
|
|
@@ -31,7 +56,7 @@ export class BlockLifecycleTracker {
|
|
|
31
56
|
this._sessions = new Map();
|
|
32
57
|
}
|
|
33
58
|
handle(payload) {
|
|
34
|
-
if (!payload.isLocal)
|
|
59
|
+
if (!payload.isLocal || !isCanvasFlavour(payload.flavour))
|
|
35
60
|
return;
|
|
36
61
|
const t = this._now();
|
|
37
62
|
const { id, flavour } = payload;
|