@fulate/ui 1.0.6 → 1.0.8
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/README.md +12 -10
- package/dist/authored-path.d.ts +3 -3
- package/dist/editor/capability.d.ts +16 -1
- package/dist/editor/shape.d.ts +18 -3
- package/dist/editor/transform.d.ts +1 -1
- package/dist/floating.d.ts +4 -6
- package/dist/group.d.ts +28 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +262 -85
- package/dist/line/base.d.ts +1 -1
- package/dist/line-tree/index.d.ts +2 -2
- package/dist/line-tree/topology.d.ts +3 -3
- package/dist/line-tree/transform.d.ts +4 -0
- package/dist/polygon.d.ts +1 -1
- package/dist/text/layout.d.ts +0 -2
- package/dist/text/paint.d.ts +0 -10
- package/dist/vector-path.d.ts +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -133,8 +133,9 @@ Rectangle、Triangle及其他Shape省略`backgroundColor`/`borderColor`时读取
|
|
|
133
133
|
`skin.lineStroke`。显式值始终优先,Shape的显式`null`关闭绘制,显式字段清回`undefined`后立即重新
|
|
134
134
|
继承Skin。Skin改变只触发现有Root/Layer重绘,不改写element、branch topology或History。
|
|
135
135
|
|
|
136
|
-
LineTree整体resize
|
|
137
|
-
world pose;包括反向scale在内,只有目标anchor改变world位置,其他points
|
|
136
|
+
LineTree整体resize把全部拓扑点按完整仿射写回 authored topology。随后Point移动或插入anchor时在
|
|
137
|
+
同一owner change内保留当前world pose;包括反向scale在内,只有目标anchor改变world位置,其他points
|
|
138
|
+
保持不动。
|
|
138
139
|
|
|
139
140
|
## Text 尺寸与显示
|
|
140
141
|
|
|
@@ -234,14 +235,15 @@ const behavior = {
|
|
|
234
235
|
};
|
|
235
236
|
```
|
|
236
237
|
|
|
237
|
-
`OwnerDirect` 用于不绘制单选 frame、首次位移可直接提升 owner drag 的自定义对象;Group 使用
|
|
238
|
-
`DirectChild` dive,普通 Shape/Line 使用 `Standard`。Transform provider 在 gesture start 捕获 frozen
|
|
239
|
-
reference 与领域 owned state,每帧只纯解析最终 owner change;frame-authored Shape、pose-authored
|
|
240
|
-
Group 与 authored-geometry Line/Polygon/VectorPath 共用 Core reference-transform primitive,结构化
|
|
241
|
-
authored value 不为 caller isolation clone。Group 的稳定 reference 由创建时的 width/height/origin 与
|
|
242
|
-
pose 拥有;当前 content bounds 是只读 local derived view。Line/LineTree 的
|
|
243
|
-
固定world paint padding不贡献ancestor content bounds,但仍进入各自render coverage
|
|
244
|
-
|
|
238
|
+
`OwnerDirect` 用于不绘制单选 frame、首次位移可直接提升 owner drag 的自定义对象;Group 使用
|
|
239
|
+
`DirectChild` dive,普通 Shape/Line 使用 `Standard`。Transform provider 在 gesture start 捕获 frozen
|
|
240
|
+
reference 与领域 owned state,每帧只纯解析最终 owner change;frame-authored Shape、pose-authored
|
|
241
|
+
Group 与 authored-geometry Line/Polygon/VectorPath 共用 Core reference-transform primitive,结构化
|
|
242
|
+
authored value 不为 caller isolation clone。Group 的稳定 reference 由创建时的 width/height/origin 与
|
|
243
|
+
pose 拥有;当前 content bounds 是只读 local derived view。Line/LineTree 的
|
|
244
|
+
固定world paint padding不贡献ancestor content bounds,但仍进入各自render coverage。普通Group
|
|
245
|
+
move/rotate只写自身pose;Group或多选resize/flip递归写入子元素的frame或矢量geometry。group/ungroup
|
|
246
|
+
只转换 local pose,保留 width/path/contour/topology、effect 与 relation 的值和引用。
|
|
245
247
|
|
|
246
248
|
`selectable:false`只限制Editor交互选择,并对该owner分支生效:普通点击、marquee、Select All与
|
|
247
249
|
Group DirectChild下钻都不能进入该分支。公开`Select.select(elements)`和History selection replay由
|
package/dist/authored-path.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createAuthoredLineSubpath, type AnchorId, type AuthoredSubpath, type SegmentId } from "@fulate/core";
|
|
2
|
-
import type
|
|
2
|
+
import { type PointView, type Rect } from "@fulate/share";
|
|
3
3
|
export { createAuthoredLineSubpath };
|
|
4
4
|
/** Replace one authored anchor point while retaining its record and all IDs. */
|
|
5
5
|
export declare function replaceAuthoredAnchorPoint(subpath: AuthoredSubpath, anchorId: AnchorId, point: PointView): AuthoredSubpath;
|
|
@@ -22,7 +22,7 @@ export declare function insertAuthoredLineAnchor(subpath: AuthoredSubpath, segme
|
|
|
22
22
|
export declare function replaceAuthoredCubicHandle(subpath: AuthoredSubpath, segmentId: SegmentId, role: "cubic-control-1" | "cubic-control-2", point: PointView): AuthoredSubpath;
|
|
23
23
|
/** Delete one anchor and merge its predecessor directly to its successor. */
|
|
24
24
|
export declare function removeAuthoredAnchor(subpath: AuthoredSubpath, anchorId: AnchorId, minimumAnchorCount?: number): AuthoredSubpath;
|
|
25
|
-
/**
|
|
26
|
-
export declare function
|
|
25
|
+
/** Transform coordinates while retaining every subpath, anchor, segment, and record ID. */
|
|
26
|
+
export declare function transformAuthoredSubpath(subpath: AuthoredSubpath, matrix: DOMMatrixReadOnly): AuthoredSubpath;
|
|
27
27
|
/** Read bounds directly from authored anchor coordinates. */
|
|
28
28
|
export declare function getAuthoredSubpathBounds(subpath: AuthoredSubpath): Readonly<Rect> | null;
|
|
@@ -12,6 +12,11 @@ export interface EditorCapabilityHost {
|
|
|
12
12
|
}
|
|
13
13
|
/** 创建一个不携带字符串 key 或元数据的 capability token。 */
|
|
14
14
|
export declare function defineEditorCapability<T>(): EditorCapabilityToken<T>;
|
|
15
|
+
/**
|
|
16
|
+
* @internal Match one token and check that its provider implements the capability.
|
|
17
|
+
* TypeScript cannot narrow the requested generic from the opaque token identity.
|
|
18
|
+
*/
|
|
19
|
+
export declare function provideEditorCapability<T, Capability>(token: EditorCapabilityToken<T>, supported: EditorCapabilityToken<Capability>, provider: NoInfer<Capability>): T | undefined;
|
|
15
20
|
/** 判断元素是否直接提供 capability 查询入口。 */
|
|
16
21
|
export declare function isEditorCapabilityHost(element: Element): element is Element & EditorCapabilityHost;
|
|
17
22
|
/** @internal 执行一次窄 host 判断和一次 direct capability 查询。 */
|
|
@@ -47,7 +52,11 @@ export interface TransformInteractionCapability<Owned = unknown> {
|
|
|
47
52
|
/** Optional authored/layout bounds used by the editor transform frame. */
|
|
48
53
|
getSelectionFrameBounds?(): Readonly<Rect> | null;
|
|
49
54
|
captureTransformBaseline(): ElementTransformBaseline<Owned>;
|
|
50
|
-
|
|
55
|
+
/**
|
|
56
|
+
* Resolves every owner change for this transform. Composite owners may return
|
|
57
|
+
* multiple child changes; the editor commits the complete result in one batch.
|
|
58
|
+
*/
|
|
59
|
+
resolveTransformChange(baseline: Readonly<ElementTransformBaseline<Owned>>, intent: Readonly<TransformIntent | ReparentTransformIntent>): readonly OwnerPropertyChange[];
|
|
51
60
|
}
|
|
52
61
|
export interface SnapSourceCapability {
|
|
53
62
|
getWorldSnapPointsView(): readonly PointView[];
|
|
@@ -146,5 +155,11 @@ export declare const SelectionBehaviorCapabilityToken: EditorCapabilityToken<Sel
|
|
|
146
155
|
export declare const TransformInteractionCapabilityToken: EditorCapabilityToken<TransformInteractionCapability<unknown>>;
|
|
147
156
|
export declare const SnapSourceCapabilityToken: EditorCapabilityToken<SnapSourceCapability>;
|
|
148
157
|
export declare const PointEditingCapabilityToken: EditorCapabilityToken<PointEditingCapability>;
|
|
158
|
+
/**
|
|
159
|
+
* Resolve the local rectangle used as an editor selection frame. An owner
|
|
160
|
+
* supplied frame is authoritative; owners without one retain a deterministic
|
|
161
|
+
* local visual/layout fallback.
|
|
162
|
+
*/
|
|
163
|
+
export declare function resolveSelectionFrameBounds(element: Element, capability?: TransformInteractionCapability): Readonly<Rect>;
|
|
149
164
|
export declare const ContentEditingCapabilityToken: EditorCapabilityToken<ContentEditingCapability>;
|
|
150
165
|
export {};
|
package/dist/editor/shape.d.ts
CHANGED
|
@@ -1,9 +1,23 @@
|
|
|
1
1
|
import { type Layer, type PropertyChanges, type Root, Shape, ShapeProperties, ShapeTransform, type ConnectionPortConfiguration, type ElementOptionPatch, type ElementTransformBaseline, type ReparentTransformIntent, type ShapeOption, type TransformIntent } from "@fulate/core";
|
|
2
|
-
import { Bound } from "@fulate/share";
|
|
2
|
+
import { Bound, type PointView, type Rect } from "@fulate/share";
|
|
3
3
|
import { SelectionBehaviorMode, SelectionDiveMode, SelectionSnapMode, type EditorCapabilityToken, type SelectionBehaviorCapability, type SnapSourceCapability, type TransformInteractionCapability } from "./capability";
|
|
4
|
+
export interface WorldRectPointsViewCache {
|
|
5
|
+
source: Readonly<Rect> | null;
|
|
6
|
+
a: number;
|
|
7
|
+
b: number;
|
|
8
|
+
c: number;
|
|
9
|
+
d: number;
|
|
10
|
+
e: number;
|
|
11
|
+
f: number;
|
|
12
|
+
points: readonly PointView[] | null;
|
|
13
|
+
}
|
|
14
|
+
export declare function createWorldRectPointsViewCache(): WorldRectPointsViewCache;
|
|
15
|
+
/** Project one stable local frame to a borrowed world-point view. */
|
|
16
|
+
export declare function getWorldRectPointsView(cache: WorldRectPointsViewCache, frame: Readonly<Rect>, matrix: DOMMatrixReadOnly): readonly PointView[];
|
|
4
17
|
/** UI frame-authored Shape 系列共享的 direct Editor provider。 */
|
|
5
18
|
export declare abstract class EditorShape<P extends ShapeProperties = ShapeProperties, O extends ShapeOption<any> = ShapeOption<any>> extends Shape<P, O> implements SelectionBehaviorCapability, TransformInteractionCapability, SnapSourceCapability {
|
|
6
19
|
private connectionPortDecoration;
|
|
20
|
+
private readonly worldSnapPointsCache;
|
|
7
21
|
getEditorCapability<T>(token: EditorCapabilityToken<T>): T | undefined;
|
|
8
22
|
getSelectionBehavior(): {
|
|
9
23
|
readonly mode: SelectionBehaviorMode.Standard;
|
|
@@ -17,11 +31,12 @@ export declare abstract class EditorShape<P extends ShapeProperties = ShapePrope
|
|
|
17
31
|
readonly rotationPivot: "adjustable";
|
|
18
32
|
};
|
|
19
33
|
captureTransformBaseline(): ElementTransformBaseline;
|
|
20
|
-
resolveTransformChange(baseline: Readonly<ElementTransformBaseline>, intent: Readonly<TransformIntent | ReparentTransformIntent>): import("@fulate/core").OwnerPropertyChange;
|
|
34
|
+
resolveTransformChange(baseline: Readonly<ElementTransformBaseline>, intent: Readonly<TransformIntent | ReparentTransformIntent>): readonly import("@fulate/core").OwnerPropertyChange[];
|
|
35
|
+
getSelectionFrameBounds(): Readonly<Rect> | null;
|
|
21
36
|
setOptions(options?: ElementOptionPatch<O>): this;
|
|
22
37
|
replacePorts(value: ConnectionPortConfiguration): this;
|
|
23
38
|
restorePropertySnapshot(snapshot: Record<string, unknown>): this;
|
|
24
|
-
getWorldSnapPointsView(): readonly
|
|
39
|
+
getWorldSnapPointsView(): readonly PointView[];
|
|
25
40
|
paint(ctx?: CanvasRenderingContext2D): void;
|
|
26
41
|
/** 绘制宿主本体;`paint()` 随后统一绘制 custom Port decoration。 */
|
|
27
42
|
protected paintHost(ctx: CanvasRenderingContext2D): void;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Element, type ElementOptionPatch, type ElementTransformBaseline, type OwnerPropertyChange, type ReparentTransformIntent, type TransformIntent } from "@fulate/core";
|
|
2
2
|
import { type Rect } from "@fulate/share";
|
|
3
3
|
/** @internal frame-authored provider 共用的纯 resolver。 */
|
|
4
|
-
export declare function resolveFrameTransformChange(owner: Element, baseline: Readonly<ElementTransformBaseline>, intent: Readonly<TransformIntent | ReparentTransformIntent>): OwnerPropertyChange;
|
|
4
|
+
export declare function resolveFrameTransformChange(owner: Element, baseline: Readonly<ElementTransformBaseline>, intent: Readonly<TransformIntent | ReparentTransformIntent>): readonly OwnerPropertyChange[];
|
|
5
5
|
/** @internal 捕获一次 owner reference;structured ownedState 直接保留原引用。 */
|
|
6
6
|
export declare function captureElementTransformBaseline<Owned>(owner: Element, writeMode: "frame-authored" | "authored-geometry" | "pose-authored", ownedState: Owned): ElementTransformBaseline<Owned>;
|
|
7
7
|
/** @internal Geometry reference 变化时保持当前 world pose 的最小 frame patch。 */
|
package/dist/floating.d.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import { Element
|
|
2
|
-
import { type ComputePositionConfig
|
|
3
|
-
export {
|
|
4
|
-
export type {
|
|
1
|
+
import { Element } from "@fulate/core";
|
|
2
|
+
import { type ComputePositionConfig } from "@floating-ui/core";
|
|
3
|
+
export { flip, offset, shift } from "@floating-ui/core";
|
|
4
|
+
export type { Placement } from "@floating-ui/core";
|
|
5
5
|
export type ComputePositionOptions = Omit<ComputePositionConfig, "platform" | "strategy">;
|
|
6
|
-
/** Creates a Floating UI platform backed by one Fulate Root's world space. */
|
|
7
|
-
export declare function createFloatingPlatform(root: Root): Platform;
|
|
8
6
|
/** Computes world-space coordinates without mutating either element. */
|
|
9
7
|
export declare function computePosition(reference: Element, floating: Element, options?: ComputePositionOptions): Promise<import("@floating-ui/core").ComputePositionReturn>;
|
package/dist/group.d.ts
CHANGED
|
@@ -1,19 +1,36 @@
|
|
|
1
|
-
import { Element, ElementTransform, type BaseElementOption, type ElementTransformBaseline, type ReparentTransformIntent, type TransformIntent } from "@fulate/core";
|
|
2
|
-
import { Bound, type PointView } from "@fulate/share";
|
|
3
|
-
import { SelectionBehaviorMode, SelectionDiveMode, SelectionSnapMode, type EditorCapabilityToken } from "./editor/capability";
|
|
1
|
+
import { ChangeImpact, Element, ElementTransform, type BaseElementOption, type ElementTransformBaseline, type ReparentTransformIntent, type TransformIntent } from "@fulate/core";
|
|
2
|
+
import { Bound, type BoundingBox, type PointView } from "@fulate/share";
|
|
3
|
+
import { SelectionBehaviorMode, SelectionDiveMode, SelectionSnapMode, type EditorCapabilityToken, type TransformInteractionCapability } from "./editor/capability";
|
|
4
|
+
interface GroupTransformChildBaseline {
|
|
5
|
+
readonly capability: TransformInteractionCapability;
|
|
6
|
+
readonly baseline: ReturnType<TransformInteractionCapability["captureTransformBaseline"]>;
|
|
7
|
+
}
|
|
8
|
+
interface GroupTransformOwnedState {
|
|
9
|
+
readonly children: readonly GroupTransformChildBaseline[];
|
|
10
|
+
}
|
|
4
11
|
export interface GroupOption extends BaseElementOption<Group> {
|
|
5
12
|
}
|
|
6
13
|
export declare class GroupTransform<E extends Group = Group> extends ElementTransform<E> {
|
|
7
14
|
private localContentBounds;
|
|
15
|
+
private localSelectionFrameBounds;
|
|
8
16
|
protected onChildrenChanged(): void;
|
|
9
17
|
protected onDescendantDirty(_child: ElementTransform, affectsContentBounds: boolean): void;
|
|
10
18
|
private invalidateContentBounds;
|
|
19
|
+
invalidate(impact: ChangeImpact, previousBounds?: Readonly<BoundingBox> | null): void;
|
|
11
20
|
/** @internal Group-local canonical content subtree bounds. */
|
|
12
21
|
getLocalContentBoundsView(): Readonly<Bound> | null;
|
|
22
|
+
/** @internal Group-local selection frame derived from child owner frames. */
|
|
23
|
+
getLocalSelectionFrameBoundsView(): Readonly<Bound> | null;
|
|
13
24
|
/** Group 是 content bounds owner;ancestor 只投影这份 local view。 */
|
|
14
25
|
protected collectContentBoundsInReference(parentMatrix: DOMMatrixReadOnly, _referenceWorldInverse: DOMMatrixReadOnly): Bound;
|
|
15
26
|
/** 只合并真实 content members;不贡献 content bounds 的 branch 返回 null。 */
|
|
16
27
|
protected buildLocalContentBounds(): Bound | null;
|
|
28
|
+
/**
|
|
29
|
+
* Build the frame used by selection/transform/snap. Text-like owners expose
|
|
30
|
+
* authored frame bounds that intentionally exclude paint overflow; owners
|
|
31
|
+
* without that capability retain an explicit content/visual fallback.
|
|
32
|
+
*/
|
|
33
|
+
protected buildLocalSelectionFrameBounds(): Bound | null;
|
|
17
34
|
protected buildLocalHitBounds(): Readonly<Bound>;
|
|
18
35
|
protected containsLocalHitPoint(point: PointView): boolean;
|
|
19
36
|
protected buildVisualBounds(): Readonly<Bound>;
|
|
@@ -25,6 +42,8 @@ export declare class GroupTransform<E extends Group = Group> extends ElementTran
|
|
|
25
42
|
}
|
|
26
43
|
export declare class Group extends Element {
|
|
27
44
|
readonly type = "group";
|
|
45
|
+
private readonly worldSnapPointsCache;
|
|
46
|
+
readonly transform: GroupTransform<this>;
|
|
28
47
|
constructor(options?: GroupOption);
|
|
29
48
|
getEditorCapability<T>(token: EditorCapabilityToken<T>): T | undefined;
|
|
30
49
|
getSelectionBehavior(): {
|
|
@@ -38,7 +57,11 @@ export declare class Group extends Element {
|
|
|
38
57
|
readonly rotate: true;
|
|
39
58
|
readonly rotationPivot: "adjustable";
|
|
40
59
|
};
|
|
41
|
-
captureTransformBaseline(): ElementTransformBaseline<
|
|
42
|
-
|
|
60
|
+
captureTransformBaseline(): ElementTransformBaseline<{
|
|
61
|
+
children: GroupTransformChildBaseline[];
|
|
62
|
+
}>;
|
|
63
|
+
resolveTransformChange(baseline: Readonly<ElementTransformBaseline<GroupTransformOwnedState>>, intent: Readonly<TransformIntent | ReparentTransformIntent>): readonly import("@fulate/core").OwnerPropertyChange[];
|
|
43
64
|
getWorldSnapPointsView(): readonly PointView[];
|
|
65
|
+
getSelectionFrameBounds(): Readonly<Bound>;
|
|
44
66
|
}
|
|
67
|
+
export {};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { measureTextWidth,
|
|
1
|
+
export { measureTextWidth, subscribeTextMeasurementChanges } from "./text/layout";
|
|
2
2
|
export type { TextLayoutLine, TextLayoutMetrics, TextLayoutOptions, TextLayoutSnapshot } from "./text/layout";
|
|
3
3
|
export { measureTextRun } from "./text/paint";
|
|
4
4
|
export { paintTextBlock } from "./text/paint";
|
|
@@ -22,11 +22,11 @@ export { WorkerSurfacePool } from "./worker-surface";
|
|
|
22
22
|
export type { WorkerSurfaceHandle, WorkerSurfacePoolOptions } from "./worker-surface";
|
|
23
23
|
export { fitWorkspace, workspaceFocusInsetsKey, Workspace, WorkspaceProperties } from "./workspace";
|
|
24
24
|
export type { WorkspaceFocusInsetsSource, WorkspaceFitOptions, WorkspaceJSON, WorkspaceOption } from "./workspace";
|
|
25
|
-
export {
|
|
26
|
-
export type { ComputePositionOptions,
|
|
25
|
+
export { computePosition, flip, offset, shift } from "./floating";
|
|
26
|
+
export type { ComputePositionOptions, Placement } from "./floating";
|
|
27
27
|
export { Group, GroupTransform } from "./group";
|
|
28
28
|
export type { GroupOption } from "./group";
|
|
29
|
-
export { ContentEditingCapabilityToken, PointEditingCapabilityToken, SelectionBehaviorMode, SelectionBehaviorCapabilityToken, SelectionDiveMode, SelectionSnapMode, SnapSourceCapabilityToken, TransformInteractionCapabilityToken, defineEditorCapability, getEditorCapability, isEditorCapabilityHost } from "./editor/capability";
|
|
29
|
+
export { ContentEditingCapabilityToken, PointEditingCapabilityToken, SelectionBehaviorMode, SelectionBehaviorCapabilityToken, SelectionDiveMode, SelectionSnapMode, SnapSourceCapabilityToken, TransformInteractionCapabilityToken, defineEditorCapability, getEditorCapability, isEditorCapabilityHost, resolveSelectionFrameBounds } from "./editor/capability";
|
|
30
30
|
export type { ContentEditingCapability, ContentEditingConsumer, ContentEditingSession, EditorCapabilityHost, EditorCapabilityToken, PointControlDescriptor, PointEditRequest, PointEditTarget, PointEditingCapability, PointEditingScope, PointOwnerEdit, SelectionBehavior, SelectionBehaviorCapability, SnapSourceCapability, TransformInteractionCapability, TransformInteractionPolicy } from "./editor/capability";
|
|
31
31
|
export { EditorFrameFlush, getEditorFrameFlush } from "./editor/frame-flush";
|
|
32
32
|
export type { EditorAuthoredGesture, EditorOverlayClient, EditorSpatialClient } from "./editor/frame-flush";
|
|
@@ -37,7 +37,7 @@ export type { RippleOption } from "./ripple";
|
|
|
37
37
|
export { BaseLine, LineTransform, Line } from "./line/index";
|
|
38
38
|
export { connectionPortAccepts, connectEndpoint, disconnectEndpoint, getEndpointRelationIndex, hasConnectionPortCapacity, isEndpointRelationSource, resolveConnectionPortChanges, resolveConnectEndpointChange, resolveDisconnectEndpointChanges, EndpointRelationIndex } from "./connection";
|
|
39
39
|
export type { BindableEndpoint, EndpointConnectionRole, EndpointRef, EndpointRelation, EndpointRelationBinding, EndpointRelationSource } from "./connection";
|
|
40
|
-
export { LineTree, LineTreeProperties, LineTreeTransform, EMPTY_LINE_TREE_TOPOLOGY, DEFAULT_LINE_TREE_BRANCH_STYLE, addLineTreeBranchAtTail, createLineTreeTopology, getLineTreeBranch, getLineTreeBindableEndpoints, getLineTreeEndpointRelationBindings, getLineTreeJunctionAnchorIds, getLineTreePoint, getLineTreePointReferenceCounts, insertLineTreePoint, moveLineTreePoint, removeLineTreeBranch, removeLineTreePoint, remapLineTreeRelations,
|
|
40
|
+
export { LineTree, LineTreeProperties, LineTreeTransform, EMPTY_LINE_TREE_TOPOLOGY, DEFAULT_LINE_TREE_BRANCH_STYLE, addLineTreeBranchAtTail, createLineTreeTopology, getLineTreeBranch, getLineTreeBindableEndpoints, getLineTreeEndpointRelationBindings, getLineTreeJunctionAnchorIds, getLineTreePoint, getLineTreePointReferenceCounts, insertLineTreePoint, moveLineTreePoint, removeLineTreeBranch, removeLineTreePoint, remapLineTreeRelations, transformLineTreeTopology, updateLineTreeBranchStyle } from "./line-tree";
|
|
41
41
|
export type { LineTreeBranch, LineTreeBranchRemoval, LineTreeBranchTailSource, LineTreeBranchStyle, LineTreeBranchVertex, LineTreeOption, LineTreePoint, LineTreeTopology } from "./line-tree";
|
|
42
42
|
export { applyLineTreeTopologyDelta, createLineTreeTopologyDelta } from "./line-tree/history-delta";
|
|
43
43
|
export type { LineTreeTopologyDelta } from "./line-tree/history-delta";
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { clearCache, layoutNextLineRange, layoutWithLines, materializeLineRange, measureLineStats, measureNaturalWidth, prepareWithSegments } from "@chenglou/pretext";
|
|
2
|
-
import { ChangeImpact, CustomEvent, Element, ElementProperties, ElementTransform, SKIN_DEFAULTS, Shape, ShapeProperties, ShapeTransform, createAuthoredLineSubpath, createGeometrySnapshot, createLineSubpath, createPathLocalId, evaluatePathSegmentPoint, getElementConnectionPort, getElementConnectionPortPoint, isReparentTransformIntent, resolveAuthoredSubpath, resolveReferenceTransform, resolveReferenceTransformTarget, splitPathSegment } from "@fulate/core";
|
|
2
|
+
import { ChangeImpact, CustomEvent, Element, ElementProperties, ElementTransform, SKIN_DEFAULTS, Shape, ShapeProperties, ShapeTransform, createAuthoredLineSubpath, createGeometrySnapshot, createLineSubpath, createPathLocalId, evaluatePathSegmentPoint, getElementConnectionPort, getElementConnectionPortPoint, isReparentTransformIntent, resolveAuthoredGeometryFramePatch, resolveAuthoredSubpath, resolveReferenceTransform, resolveReferenceTransformTarget, splitPathSegment } from "@fulate/core";
|
|
3
3
|
import { clamp, isFunction, isNil, isNull, isString, isUndefined } from "lodash-es";
|
|
4
|
-
import { Bound, Intersection, createCanvasGradient, createPointView, isGradient, isPointWithinDistance, rectEdgePosition, transformPoint } from "@fulate/share";
|
|
5
|
-
import {
|
|
4
|
+
import { Bound, Intersection, createCanvasGradient, createPointView, isGradient, isIdentityMatrix, isPointWithinDistance, rectEdgePosition, transformPoint } from "@fulate/share";
|
|
5
|
+
import { computePosition as computePosition$1, flip, offset, shift } from "@floating-ui/core";
|
|
6
6
|
import { Easing, Tween } from "@tweenjs/tween.js";
|
|
7
7
|
//#region packages/ui/src/text/layout.ts
|
|
8
8
|
var graphemeSegmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
@@ -136,10 +136,6 @@ function ellipsizeText(text, maxWidth, font, letterSpacing, force) {
|
|
|
136
136
|
width: measure(value)
|
|
137
137
|
};
|
|
138
138
|
}
|
|
139
|
-
/** Fit a single run and append an ellipsis only when it does not fit. */
|
|
140
|
-
function fitTextWithEllipsis(text, maxWidth, font, letterSpacing = 0) {
|
|
141
|
-
return ellipsizeText(text, maxWidth, font, letterSpacing, false).text;
|
|
142
|
-
}
|
|
143
139
|
var PreparedTextLayout = class {
|
|
144
140
|
normalizedText;
|
|
145
141
|
naturalWidth;
|
|
@@ -530,7 +526,7 @@ function updateTextareaPosition(self) {
|
|
|
530
526
|
if (!root) return;
|
|
531
527
|
const { scale, x: vx, y: vy } = root.viewport;
|
|
532
528
|
self.transform.bubbleUpdateTransform();
|
|
533
|
-
const m = self.transform.
|
|
529
|
+
const m = self.transform.getWorldMatrixView();
|
|
534
530
|
const style = self.getResolvedTextStyle();
|
|
535
531
|
const { x: cx, y: cy, h } = getCaretLocalXY(self, ta.selectionStart ?? self.text.length);
|
|
536
532
|
const screenX = (m.a * cx + m.c * cy + m.e) * scale + vx;
|
|
@@ -1124,8 +1120,9 @@ var ConnectionPortDecoration = class {
|
|
|
1124
1120
|
traceCirclePath(ctx, position, DOT_RADIUS);
|
|
1125
1121
|
ctx.fill();
|
|
1126
1122
|
}
|
|
1123
|
+
const worldMatrix = entries.some(({ block }) => block) ? this.host.transform.getWorldMatrixView() : void 0;
|
|
1127
1124
|
for (const entry of entries) {
|
|
1128
|
-
const placement = this.resolveLabelPlacement(entry);
|
|
1125
|
+
const placement = this.resolveLabelPlacement(entry, worldMatrix);
|
|
1129
1126
|
if (!placement) continue;
|
|
1130
1127
|
const { block } = entry;
|
|
1131
1128
|
if (!block) continue;
|
|
@@ -1156,7 +1153,7 @@ var ConnectionPortDecoration = class {
|
|
|
1156
1153
|
height: DOT_RADIUS * 2
|
|
1157
1154
|
});
|
|
1158
1155
|
bounds = bounds ? bounds.merge(dotBounds) : dotBounds;
|
|
1159
|
-
const placement = this.resolveLabelPlacement(entry);
|
|
1156
|
+
const placement = this.resolveLabelPlacement(entry, worldMatrix);
|
|
1160
1157
|
if (!placement?.entry.block) continue;
|
|
1161
1158
|
const textBounds = getTextBlockVisualBounds(placement.entry.block.layout, placement.style, placement.width, placement.height);
|
|
1162
1159
|
if (!textBounds) continue;
|
|
@@ -1182,7 +1179,7 @@ var ConnectionPortDecoration = class {
|
|
|
1182
1179
|
}
|
|
1183
1180
|
return this.entries;
|
|
1184
1181
|
}
|
|
1185
|
-
resolveLabelPlacement(entry) {
|
|
1182
|
+
resolveLabelPlacement(entry, worldMatrix) {
|
|
1186
1183
|
const { slot, block } = entry;
|
|
1187
1184
|
if (!block) return null;
|
|
1188
1185
|
const definition = slot.definition;
|
|
@@ -1194,7 +1191,7 @@ var ConnectionPortDecoration = class {
|
|
|
1194
1191
|
if (width === 0) return null;
|
|
1195
1192
|
const localAlign = slot.edge === "left" ? "right" : slot.edge === "right" ? "left" : "center";
|
|
1196
1193
|
const upright = definition.keepLabelUpright !== false;
|
|
1197
|
-
const worldNormal = upright ? transformVector(
|
|
1194
|
+
const worldNormal = upright ? transformVector(worldMatrix, nx, ny) : null;
|
|
1198
1195
|
const align = worldNormal ? Math.abs(worldNormal.x) >= Math.abs(worldNormal.y) ? worldNormal.x >= 0 ? "left" : "right" : "center" : localAlign;
|
|
1199
1196
|
const style = this.getStyle(entry, align);
|
|
1200
1197
|
const lineHeight = style.fontSize * style.lineHeight;
|
|
@@ -1235,7 +1232,7 @@ var ConnectionPortDecoration = class {
|
|
|
1235
1232
|
width,
|
|
1236
1233
|
height
|
|
1237
1234
|
};
|
|
1238
|
-
const uprightMatrix = upright ? this.buildUprightMatrix(position, nx, ny, width, height) : null;
|
|
1235
|
+
const uprightMatrix = upright ? this.buildUprightMatrix(position, nx, ny, width, height, worldMatrix) : null;
|
|
1239
1236
|
if (upright && !uprightMatrix) return null;
|
|
1240
1237
|
return {
|
|
1241
1238
|
entry,
|
|
@@ -1264,8 +1261,7 @@ var ConnectionPortDecoration = class {
|
|
|
1264
1261
|
entry.styles[align] = style;
|
|
1265
1262
|
return style;
|
|
1266
1263
|
}
|
|
1267
|
-
buildUprightMatrix(position, nx, ny, width, height) {
|
|
1268
|
-
const matrix = this.host.transform.getWorldMatrixView();
|
|
1264
|
+
buildUprightMatrix(position, nx, ny, width, height, matrix) {
|
|
1269
1265
|
const worldPosition = transformPoint(matrix, position);
|
|
1270
1266
|
const normal = transformVector(matrix, nx, ny);
|
|
1271
1267
|
const normalLength = Math.hypot(normal.x, normal.y);
|
|
@@ -1576,9 +1572,13 @@ function resolveConnectionPortChanges(root, changes) {
|
|
|
1576
1572
|
if (Object.is(owner.ports, next)) continue;
|
|
1577
1573
|
const nextIds = new Set(next === false ? [] : next === true ? owner.resolveConnectionPorts(true).map(({ ref }) => ref.portId) : next.map(({ id }) => id));
|
|
1578
1574
|
index ??= getEndpointRelationIndex(root);
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1575
|
+
const previousPortIds = owner.ports === false ? [] : owner.ports === true ? owner.resolveConnectionPorts(true).map(({ ref }) => ref.portId) : owner.ports.map(({ id }) => id);
|
|
1576
|
+
for (const portId of previousPortIds) {
|
|
1577
|
+
if (nextIds.has(portId)) continue;
|
|
1578
|
+
const targeting = index.relationsTargetingPort({
|
|
1579
|
+
elementKey: owner.key,
|
|
1580
|
+
portId
|
|
1581
|
+
});
|
|
1582
1582
|
if (targeting.length > 0) (sources ??= []).push(...targeting);
|
|
1583
1583
|
}
|
|
1584
1584
|
}
|
|
@@ -1604,6 +1604,13 @@ function commitConnectionPortPatch(owner, patch) {
|
|
|
1604
1604
|
function defineEditorCapability() {
|
|
1605
1605
|
return {};
|
|
1606
1606
|
}
|
|
1607
|
+
/**
|
|
1608
|
+
* @internal Match one token and check that its provider implements the capability.
|
|
1609
|
+
* TypeScript cannot narrow the requested generic from the opaque token identity.
|
|
1610
|
+
*/
|
|
1611
|
+
function provideEditorCapability(token, supported, provider) {
|
|
1612
|
+
return token === supported ? provider : void 0;
|
|
1613
|
+
}
|
|
1607
1614
|
/** 判断元素是否直接提供 capability 查询入口。 */
|
|
1608
1615
|
function isEditorCapabilityHost(element) {
|
|
1609
1616
|
return isFunction(element.getEditorCapability);
|
|
@@ -1631,6 +1638,14 @@ var SelectionBehaviorCapabilityToken = defineEditorCapability();
|
|
|
1631
1638
|
var TransformInteractionCapabilityToken = defineEditorCapability();
|
|
1632
1639
|
var SnapSourceCapabilityToken = defineEditorCapability();
|
|
1633
1640
|
var PointEditingCapabilityToken = defineEditorCapability();
|
|
1641
|
+
/**
|
|
1642
|
+
* Resolve the local rectangle used as an editor selection frame. An owner
|
|
1643
|
+
* supplied frame is authoritative; owners without one retain a deterministic
|
|
1644
|
+
* local visual/layout fallback.
|
|
1645
|
+
*/
|
|
1646
|
+
function resolveSelectionFrameBounds(element, capability) {
|
|
1647
|
+
return capability?.getSelectionFrameBounds?.() ?? element.transform.getLocalVisualBoundsView() ?? element.transform.getLayoutReferenceBoundsView();
|
|
1648
|
+
}
|
|
1634
1649
|
var ContentEditingCapabilityToken = defineEditorCapability();
|
|
1635
1650
|
//#endregion
|
|
1636
1651
|
//#region packages/ui/src/editor/transform.ts
|
|
@@ -1664,10 +1679,10 @@ function readElementTransformBaseline(owner, writeMode, ownedState) {
|
|
|
1664
1679
|
}
|
|
1665
1680
|
/** @internal frame-authored provider 共用的纯 resolver。 */
|
|
1666
1681
|
function resolveFrameTransformChange(owner, baseline, intent) {
|
|
1667
|
-
return {
|
|
1682
|
+
return [{
|
|
1668
1683
|
owner,
|
|
1669
1684
|
patch: resolveReferenceTransform(baseline.reference, intent).framePatch
|
|
1670
|
-
};
|
|
1685
|
+
}];
|
|
1671
1686
|
}
|
|
1672
1687
|
/** @internal 捕获一次 owner reference;structured ownedState 直接保留原引用。 */
|
|
1673
1688
|
function captureElementTransformBaseline(owner, writeMode, ownedState) {
|
|
@@ -1703,11 +1718,58 @@ var FRAME_TRANSFORM_POLICY = {
|
|
|
1703
1718
|
rotate: true,
|
|
1704
1719
|
rotationPivot: "adjustable"
|
|
1705
1720
|
};
|
|
1721
|
+
function getRectCorners(rect) {
|
|
1722
|
+
const right = rect.left + rect.width;
|
|
1723
|
+
const bottom = rect.top + rect.height;
|
|
1724
|
+
return [
|
|
1725
|
+
{
|
|
1726
|
+
x: rect.left,
|
|
1727
|
+
y: rect.top
|
|
1728
|
+
},
|
|
1729
|
+
{
|
|
1730
|
+
x: right,
|
|
1731
|
+
y: rect.top
|
|
1732
|
+
},
|
|
1733
|
+
{
|
|
1734
|
+
x: right,
|
|
1735
|
+
y: bottom
|
|
1736
|
+
},
|
|
1737
|
+
{
|
|
1738
|
+
x: rect.left,
|
|
1739
|
+
y: bottom
|
|
1740
|
+
}
|
|
1741
|
+
];
|
|
1742
|
+
}
|
|
1743
|
+
function createWorldRectPointsViewCache() {
|
|
1744
|
+
return {
|
|
1745
|
+
source: null,
|
|
1746
|
+
a: NaN,
|
|
1747
|
+
b: NaN,
|
|
1748
|
+
c: NaN,
|
|
1749
|
+
d: NaN,
|
|
1750
|
+
e: NaN,
|
|
1751
|
+
f: NaN,
|
|
1752
|
+
points: null
|
|
1753
|
+
};
|
|
1754
|
+
}
|
|
1755
|
+
/** Project one stable local frame to a borrowed world-point view. */
|
|
1756
|
+
function getWorldRectPointsView(cache, frame, matrix) {
|
|
1757
|
+
if (cache.source === frame && cache.points && cache.a === matrix.a && cache.b === matrix.b && cache.c === matrix.c && cache.d === matrix.d && cache.e === matrix.e && cache.f === matrix.f) return cache.points;
|
|
1758
|
+
cache.source = frame;
|
|
1759
|
+
cache.a = matrix.a;
|
|
1760
|
+
cache.b = matrix.b;
|
|
1761
|
+
cache.c = matrix.c;
|
|
1762
|
+
cache.d = matrix.d;
|
|
1763
|
+
cache.e = matrix.e;
|
|
1764
|
+
cache.f = matrix.f;
|
|
1765
|
+
return cache.points = getRectCorners(frame).map((point) => transformPoint(matrix, point));
|
|
1766
|
+
}
|
|
1706
1767
|
/** UI frame-authored Shape 系列共享的 direct Editor provider。 */
|
|
1707
|
-
var EditorShape = class extends Shape {
|
|
1768
|
+
var EditorShape = class EditorShape extends Shape {
|
|
1708
1769
|
connectionPortDecoration = null;
|
|
1770
|
+
worldSnapPointsCache = createWorldRectPointsViewCache();
|
|
1709
1771
|
getEditorCapability(token) {
|
|
1710
|
-
return token
|
|
1772
|
+
return provideEditorCapability(token, SelectionBehaviorCapabilityToken, this) ?? provideEditorCapability(token, TransformInteractionCapabilityToken, this) ?? provideEditorCapability(token, SnapSourceCapabilityToken, this);
|
|
1711
1773
|
}
|
|
1712
1774
|
getSelectionBehavior() {
|
|
1713
1775
|
return STANDARD_SELECTION;
|
|
@@ -1721,12 +1783,16 @@ var EditorShape = class extends Shape {
|
|
|
1721
1783
|
resolveTransformChange(baseline, intent) {
|
|
1722
1784
|
return resolveFrameTransformChange(this, baseline, intent);
|
|
1723
1785
|
}
|
|
1786
|
+
getSelectionFrameBounds() {
|
|
1787
|
+
return null;
|
|
1788
|
+
}
|
|
1724
1789
|
setOptions(options) {
|
|
1725
1790
|
if (!options) return this;
|
|
1726
1791
|
if (commitConnectionPortPatch(this, options)) return this;
|
|
1727
1792
|
return super.setOptions(options);
|
|
1728
1793
|
}
|
|
1729
1794
|
replacePorts(value) {
|
|
1795
|
+
if (this.ports === value && EditorShape.getConfiguredPropertyValue(this, "ports") === value) return this;
|
|
1730
1796
|
return this.setOptions({ ports: value });
|
|
1731
1797
|
}
|
|
1732
1798
|
restorePropertySnapshot(snapshot) {
|
|
@@ -1734,6 +1800,8 @@ var EditorShape = class extends Shape {
|
|
|
1734
1800
|
return super.restorePropertySnapshot(snapshot);
|
|
1735
1801
|
}
|
|
1736
1802
|
getWorldSnapPointsView() {
|
|
1803
|
+
const frame = this.getSelectionFrameBounds();
|
|
1804
|
+
if (frame) return getWorldRectPointsView(this.worldSnapPointsCache, frame, this.transform.getWorldMatrixView());
|
|
1737
1805
|
return this.transform.getWorldVisualPointsView();
|
|
1738
1806
|
}
|
|
1739
1807
|
paint(ctx = this.layer.ctx) {
|
|
@@ -2254,24 +2322,18 @@ function removeAuthoredAnchor(subpath, anchorId, minimumAnchorCount = 2) {
|
|
|
2254
2322
|
anchors
|
|
2255
2323
|
};
|
|
2256
2324
|
}
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
y: point.y * scaleY
|
|
2261
|
-
};
|
|
2262
|
-
}
|
|
2263
|
-
/** Scale coordinates while retaining every subpath, anchor, segment, and record ID. */
|
|
2264
|
-
function scaleAuthoredSubpath(subpath, scaleX, scaleY) {
|
|
2265
|
-
if (scaleX === 1 && scaleY === 1) return subpath;
|
|
2325
|
+
/** Transform coordinates while retaining every subpath, anchor, segment, and record ID. */
|
|
2326
|
+
function transformAuthoredSubpath(subpath, matrix) {
|
|
2327
|
+
if (isIdentityMatrix(matrix)) return subpath;
|
|
2266
2328
|
return {
|
|
2267
2329
|
...subpath,
|
|
2268
2330
|
anchors: subpath.anchors.map((anchor) => {
|
|
2269
|
-
const point =
|
|
2331
|
+
const point = transformPoint(matrix, anchor.point);
|
|
2270
2332
|
return "control1" in anchor ? {
|
|
2271
2333
|
...anchor,
|
|
2272
2334
|
point,
|
|
2273
|
-
control1:
|
|
2274
|
-
control2:
|
|
2335
|
+
control1: transformPoint(matrix, anchor.control1),
|
|
2336
|
+
control2: transformPoint(matrix, anchor.control2)
|
|
2275
2337
|
} : {
|
|
2276
2338
|
...anchor,
|
|
2277
2339
|
point
|
|
@@ -2410,7 +2472,7 @@ var Polygon = class extends EditorShape {
|
|
|
2410
2472
|
};
|
|
2411
2473
|
}
|
|
2412
2474
|
getEditorCapability(token) {
|
|
2413
|
-
return token
|
|
2475
|
+
return provideEditorCapability(token, PointEditingCapabilityToken, this) ?? super.getEditorCapability(token);
|
|
2414
2476
|
}
|
|
2415
2477
|
resolvePointScope(worldPoint) {
|
|
2416
2478
|
return this.transform.hasPointHint(worldPoint) ? { kind: "owner" } : null;
|
|
@@ -2476,19 +2538,25 @@ var Polygon = class extends EditorShape {
|
|
|
2476
2538
|
}
|
|
2477
2539
|
resolveTransformChange(baseline, intent) {
|
|
2478
2540
|
const transform = resolveReferenceTransform(baseline.reference, intent);
|
|
2479
|
-
const contour =
|
|
2541
|
+
const contour = transformAuthoredSubpath(baseline.ownedState, transform.authoredLocalDelta);
|
|
2480
2542
|
const bounds = contour === baseline.ownedState || contour.anchors.length < 2 ? null : getAuthoredSubpathBounds(contour);
|
|
2481
|
-
|
|
2543
|
+
const framePatch = bounds && intent.bakeGeometry && !isReparentTransformIntent(intent) ? resolveAuthoredGeometryFramePatch(baseline.reference, transform, bounds) : transform.framePatch;
|
|
2544
|
+
const restoredFrameSize = intent.bakeGeometry && !isReparentTransformIntent(intent) && contour === baseline.ownedState ? {
|
|
2545
|
+
width: baseline.reference.frame.width,
|
|
2546
|
+
height: baseline.reference.frame.height
|
|
2547
|
+
} : {};
|
|
2548
|
+
return [{
|
|
2482
2549
|
owner: this,
|
|
2483
2550
|
patch: {
|
|
2484
|
-
...
|
|
2551
|
+
...framePatch,
|
|
2552
|
+
...restoredFrameSize,
|
|
2485
2553
|
contour,
|
|
2486
2554
|
...bounds ? {
|
|
2487
2555
|
width: bounds.width || 1,
|
|
2488
2556
|
height: bounds.height || 1
|
|
2489
2557
|
} : {}
|
|
2490
2558
|
}
|
|
2491
|
-
};
|
|
2559
|
+
}];
|
|
2492
2560
|
}
|
|
2493
2561
|
buildPath(ctx) {
|
|
2494
2562
|
if (this.contour.anchors.length < 3) {
|
|
@@ -2713,7 +2781,7 @@ var VectorPath = class extends EditorShape {
|
|
|
2713
2781
|
};
|
|
2714
2782
|
}
|
|
2715
2783
|
getEditorCapability(token) {
|
|
2716
|
-
return token
|
|
2784
|
+
return provideEditorCapability(token, PointEditingCapabilityToken, this) ?? super.getEditorCapability(token);
|
|
2717
2785
|
}
|
|
2718
2786
|
resolvePointScope(worldPoint) {
|
|
2719
2787
|
return this.transform.hasPointHint(worldPoint) ? { kind: "owner" } : null;
|
|
@@ -2814,21 +2882,25 @@ var VectorPath = class extends EditorShape {
|
|
|
2814
2882
|
}
|
|
2815
2883
|
resolveTransformChange(baseline, intent) {
|
|
2816
2884
|
const transform = resolveReferenceTransform(baseline.reference, intent);
|
|
2817
|
-
const
|
|
2818
|
-
const scaleY = transform.authoredLocalDelta.d;
|
|
2819
|
-
const path = scaleX === 1 && scaleY === 1 ? baseline.ownedState : { subpaths: baseline.ownedState.subpaths.map((subpath) => scaleAuthoredSubpath(subpath, scaleX, scaleY)) };
|
|
2885
|
+
const path = isIdentityMatrix(transform.authoredLocalDelta) ? baseline.ownedState : { subpaths: baseline.ownedState.subpaths.map((subpath) => transformAuthoredSubpath(subpath, transform.authoredLocalDelta)) };
|
|
2820
2886
|
const bounds = path === baseline.ownedState ? null : createVectorPathSnapshot("", 0, path).layoutReferenceBounds;
|
|
2821
|
-
|
|
2887
|
+
const framePatch = bounds && intent.bakeGeometry && !isReparentTransformIntent(intent) ? resolveAuthoredGeometryFramePatch(baseline.reference, transform, bounds) : transform.framePatch;
|
|
2888
|
+
const restoredFrameSize = intent.bakeGeometry && !isReparentTransformIntent(intent) && path === baseline.ownedState ? {
|
|
2889
|
+
width: baseline.reference.frame.width,
|
|
2890
|
+
height: baseline.reference.frame.height
|
|
2891
|
+
} : {};
|
|
2892
|
+
return [{
|
|
2822
2893
|
owner: this,
|
|
2823
2894
|
patch: {
|
|
2824
|
-
...
|
|
2895
|
+
...framePatch,
|
|
2896
|
+
...restoredFrameSize,
|
|
2825
2897
|
path,
|
|
2826
2898
|
...bounds ? {
|
|
2827
2899
|
width: bounds.width || 1,
|
|
2828
2900
|
height: bounds.height || 1
|
|
2829
2901
|
} : {}
|
|
2830
2902
|
}
|
|
2831
|
-
};
|
|
2903
|
+
}];
|
|
2832
2904
|
}
|
|
2833
2905
|
buildPath(ctx) {
|
|
2834
2906
|
this.buildResolvedPath(ctx);
|
|
@@ -3031,7 +3103,7 @@ var Text = class extends Rectangle {
|
|
|
3031
3103
|
super(options, properties, Transform);
|
|
3032
3104
|
}
|
|
3033
3105
|
getEditorCapability(token) {
|
|
3034
|
-
return token
|
|
3106
|
+
return provideEditorCapability(token, ContentEditingCapabilityToken, this) ?? super.getEditorCapability(token);
|
|
3035
3107
|
}
|
|
3036
3108
|
beginContentEditing(consumer) {
|
|
3037
3109
|
if (!this.editable || this.isEditing) return;
|
|
@@ -3108,6 +3180,7 @@ var Text = class extends Rectangle {
|
|
|
3108
3180
|
const style = this.getResolvedTextStyle();
|
|
3109
3181
|
const layoutWidth = this.getLayoutWidth();
|
|
3110
3182
|
const resolvedHeight = this.getResolvedHeight();
|
|
3183
|
+
const previousLayout = this.textBlock.layout;
|
|
3111
3184
|
const layout = this.textBlock.sync({
|
|
3112
3185
|
text: this.text,
|
|
3113
3186
|
style,
|
|
@@ -3118,6 +3191,7 @@ var Text = class extends Rectangle {
|
|
|
3118
3191
|
});
|
|
3119
3192
|
const width = layoutWidth ?? layout.naturalWidth;
|
|
3120
3193
|
this.commitTextLayoutSize(isUndefined(this.propertyState.width) ? width : void 0, isUndefined(this.propertyState.height) ? resolvedHeight ?? layout.textHeight : void 0);
|
|
3194
|
+
if (layout !== previousLayout) this.transform.invalidateLayoutBounds();
|
|
3121
3195
|
}
|
|
3122
3196
|
/** 布局 owner 提交 Text 自身的 intrinsic size;派生布局可覆写为只采用其 resolved frame。 */
|
|
3123
3197
|
commitTextLayoutSize(width, height) {
|
|
@@ -3683,7 +3757,7 @@ function isRect(value) {
|
|
|
3683
3757
|
return typeof rect.x === "number" && typeof rect.y === "number" && typeof rect.width === "number" && typeof rect.height === "number";
|
|
3684
3758
|
}
|
|
3685
3759
|
function getDimensions(element) {
|
|
3686
|
-
const bounds = element.transform.
|
|
3760
|
+
const bounds = element.transform.getWorldVisualBoundsView();
|
|
3687
3761
|
return {
|
|
3688
3762
|
width: element.width ?? bounds.width,
|
|
3689
3763
|
height: element.height ?? bounds.height
|
|
@@ -3691,15 +3765,15 @@ function getDimensions(element) {
|
|
|
3691
3765
|
}
|
|
3692
3766
|
function getClippingRect(root, boundary, rootBoundary) {
|
|
3693
3767
|
if (isRect(boundary)) return boundary;
|
|
3694
|
-
if (boundary instanceof Element) return toRect(boundary.transform.
|
|
3768
|
+
if (boundary instanceof Element) return toRect(boundary.transform.getWorldVisualBoundsView());
|
|
3695
3769
|
if (isRect(rootBoundary)) return rootBoundary;
|
|
3696
|
-
return toRect(root.viewport.
|
|
3770
|
+
return toRect(root.viewport.getWorldRectView());
|
|
3697
3771
|
}
|
|
3698
3772
|
/** Creates a Floating UI platform backed by one Fulate Root's world space. */
|
|
3699
3773
|
function createFloatingPlatform(root) {
|
|
3700
3774
|
return {
|
|
3701
3775
|
getElementRects: ({ reference, floating }) => ({
|
|
3702
|
-
reference: toRect(reference.transform.
|
|
3776
|
+
reference: toRect(reference.transform.getWorldVisualBoundsView()),
|
|
3703
3777
|
floating: {
|
|
3704
3778
|
x: 0,
|
|
3705
3779
|
y: 0,
|
|
@@ -3725,23 +3799,57 @@ function computePosition(reference, floating, options = {}) {
|
|
|
3725
3799
|
}
|
|
3726
3800
|
//#endregion
|
|
3727
3801
|
//#region packages/ui/src/group.ts
|
|
3802
|
+
function rectCorners(rect) {
|
|
3803
|
+
const right = rect.left + rect.width;
|
|
3804
|
+
const bottom = rect.top + rect.height;
|
|
3805
|
+
return [
|
|
3806
|
+
{
|
|
3807
|
+
x: rect.left,
|
|
3808
|
+
y: rect.top
|
|
3809
|
+
},
|
|
3810
|
+
{
|
|
3811
|
+
x: right,
|
|
3812
|
+
y: rect.top
|
|
3813
|
+
},
|
|
3814
|
+
{
|
|
3815
|
+
x: right,
|
|
3816
|
+
y: bottom
|
|
3817
|
+
},
|
|
3818
|
+
{
|
|
3819
|
+
x: rect.left,
|
|
3820
|
+
y: bottom
|
|
3821
|
+
}
|
|
3822
|
+
];
|
|
3823
|
+
}
|
|
3728
3824
|
var GroupTransform = class extends ElementTransform {
|
|
3729
3825
|
localContentBounds;
|
|
3826
|
+
localSelectionFrameBounds;
|
|
3730
3827
|
onChildrenChanged() {
|
|
3731
3828
|
this.invalidateContentBounds();
|
|
3732
3829
|
}
|
|
3733
3830
|
onDescendantDirty(_child, affectsContentBounds) {
|
|
3734
|
-
if (affectsContentBounds) this.invalidateContentBounds();
|
|
3831
|
+
if (affectsContentBounds && (this.localContentBounds !== void 0 || this.localSelectionFrameBounds !== void 0)) this.invalidateContentBounds();
|
|
3735
3832
|
}
|
|
3736
3833
|
invalidateContentBounds() {
|
|
3737
|
-
this.localContentBounds = void 0;
|
|
3738
3834
|
this.invalidate(ChangeImpact.VisualBounds | ChangeImpact.HitBounds);
|
|
3739
3835
|
}
|
|
3836
|
+
invalidate(impact, previousBounds) {
|
|
3837
|
+
if (impact & (ChangeImpact.LocalGeometry | ChangeImpact.VisualBounds | ChangeImpact.HitBounds)) {
|
|
3838
|
+
this.localContentBounds = void 0;
|
|
3839
|
+
this.localSelectionFrameBounds = void 0;
|
|
3840
|
+
}
|
|
3841
|
+
super.invalidate(impact, previousBounds);
|
|
3842
|
+
}
|
|
3740
3843
|
/** @internal Group-local canonical content subtree bounds. */
|
|
3741
3844
|
getLocalContentBoundsView() {
|
|
3742
3845
|
if (this.localContentBounds !== void 0) return this.localContentBounds;
|
|
3743
3846
|
return this.localContentBounds = this.buildLocalContentBounds();
|
|
3744
3847
|
}
|
|
3848
|
+
/** @internal Group-local selection frame derived from child owner frames. */
|
|
3849
|
+
getLocalSelectionFrameBoundsView() {
|
|
3850
|
+
if (this.localSelectionFrameBounds !== void 0) return this.localSelectionFrameBounds;
|
|
3851
|
+
return this.localSelectionFrameBounds = this.buildLocalSelectionFrameBounds();
|
|
3852
|
+
}
|
|
3745
3853
|
/** Group 是 content bounds owner;ancestor 只投影这份 local view。 */
|
|
3746
3854
|
collectContentBoundsInReference(parentMatrix, _referenceWorldInverse) {
|
|
3747
3855
|
if (!this.element.visible) return null;
|
|
@@ -3769,6 +3877,33 @@ var GroupTransform = class extends ElementTransform {
|
|
|
3769
3877
|
bounds.maxY = Math.min(bounds.maxY, clip.top + clip.height);
|
|
3770
3878
|
return bounds.minX < bounds.maxX && bounds.minY < bounds.maxY ? bounds : null;
|
|
3771
3879
|
}
|
|
3880
|
+
/**
|
|
3881
|
+
* Build the frame used by selection/transform/snap. Text-like owners expose
|
|
3882
|
+
* authored frame bounds that intentionally exclude paint overflow; owners
|
|
3883
|
+
* without that capability retain an explicit content/visual fallback.
|
|
3884
|
+
*/
|
|
3885
|
+
buildLocalSelectionFrameBounds() {
|
|
3886
|
+
const childWorldInverse = this.getChildWorldInverseMatrixView();
|
|
3887
|
+
let bounds = null;
|
|
3888
|
+
for (const child of this.element.children) {
|
|
3889
|
+
if (!child.visible) continue;
|
|
3890
|
+
const localFrame = getEditorCapability(child, TransformInteractionCapabilityToken)?.getSelectionFrameBounds?.();
|
|
3891
|
+
const points = localFrame ? rectCorners(localFrame).map((point) => transformPoint(childWorldInverse, transformPoint(child.transform.getWorldMatrixView(), point))) : (() => {
|
|
3892
|
+
const content = child.transform.getContentBoundsInParentView();
|
|
3893
|
+
return content ? rectCorners(content) : [];
|
|
3894
|
+
})();
|
|
3895
|
+
if (points.length === 0) continue;
|
|
3896
|
+
const childBounds = Bound.fromPoints(points);
|
|
3897
|
+
bounds = bounds ? bounds.merge(childBounds) : childBounds;
|
|
3898
|
+
}
|
|
3899
|
+
const clip = bounds && this.element.getChildClipRect();
|
|
3900
|
+
if (!bounds || !clip) return bounds;
|
|
3901
|
+
bounds.minX = Math.max(bounds.minX, clip.left);
|
|
3902
|
+
bounds.minY = Math.max(bounds.minY, clip.top);
|
|
3903
|
+
bounds.maxX = Math.min(bounds.maxX, clip.left + clip.width);
|
|
3904
|
+
bounds.maxY = Math.min(bounds.maxY, clip.top + clip.height);
|
|
3905
|
+
return bounds.minX < bounds.maxX && bounds.minY < bounds.maxY ? bounds : null;
|
|
3906
|
+
}
|
|
3772
3907
|
buildLocalHitBounds() {
|
|
3773
3908
|
return this.getLocalContentBoundsView();
|
|
3774
3909
|
}
|
|
@@ -3802,16 +3937,18 @@ var GroupTransform = class extends ElementTransform {
|
|
|
3802
3937
|
}
|
|
3803
3938
|
dispose() {
|
|
3804
3939
|
this.localContentBounds = void 0;
|
|
3940
|
+
this.localSelectionFrameBounds = void 0;
|
|
3805
3941
|
super.dispose();
|
|
3806
3942
|
}
|
|
3807
3943
|
};
|
|
3808
3944
|
var Group = class extends Element {
|
|
3809
3945
|
type = "group";
|
|
3946
|
+
worldSnapPointsCache = createWorldRectPointsViewCache();
|
|
3810
3947
|
constructor(options = {}) {
|
|
3811
3948
|
super(options, new ElementProperties(options), GroupTransform);
|
|
3812
3949
|
}
|
|
3813
3950
|
getEditorCapability(token) {
|
|
3814
|
-
return token
|
|
3951
|
+
return provideEditorCapability(token, SelectionBehaviorCapabilityToken, this) ?? provideEditorCapability(token, TransformInteractionCapabilityToken, this) ?? provideEditorCapability(token, SnapSourceCapabilityToken, this);
|
|
3815
3952
|
}
|
|
3816
3953
|
getSelectionBehavior() {
|
|
3817
3954
|
return {
|
|
@@ -3829,14 +3966,29 @@ var Group = class extends Element {
|
|
|
3829
3966
|
};
|
|
3830
3967
|
}
|
|
3831
3968
|
captureTransformBaseline() {
|
|
3832
|
-
|
|
3969
|
+
const children = [];
|
|
3970
|
+
for (const child of this.children) {
|
|
3971
|
+
const capability = getEditorCapability(child, TransformInteractionCapabilityToken);
|
|
3972
|
+
if (!capability) continue;
|
|
3973
|
+
children.push({
|
|
3974
|
+
capability,
|
|
3975
|
+
baseline: capability.captureTransformBaseline()
|
|
3976
|
+
});
|
|
3977
|
+
}
|
|
3978
|
+
return captureElementTransformBaseline(this, "pose-authored", { children });
|
|
3833
3979
|
}
|
|
3834
3980
|
resolveTransformChange(baseline, intent) {
|
|
3981
|
+
if (intent.bakeGeometry && !isReparentTransformIntent(intent)) return baseline.ownedState.children.flatMap(({ capability, baseline }) => capability.resolveTransformChange(baseline, intent));
|
|
3835
3982
|
return resolveFrameTransformChange(this, baseline, intent);
|
|
3836
3983
|
}
|
|
3837
3984
|
getWorldSnapPointsView() {
|
|
3985
|
+
const frame = this.transform.getLocalSelectionFrameBoundsView();
|
|
3986
|
+
if (frame) return getWorldRectPointsView(this.worldSnapPointsCache, frame, this.transform.getWorldMatrixView());
|
|
3838
3987
|
return this.transform.getWorldVisualPointsView();
|
|
3839
3988
|
}
|
|
3989
|
+
getSelectionFrameBounds() {
|
|
3990
|
+
return this.transform.getLocalSelectionFrameBoundsView();
|
|
3991
|
+
}
|
|
3840
3992
|
};
|
|
3841
3993
|
//#endregion
|
|
3842
3994
|
//#region packages/ui/src/editor/frame-flush.ts
|
|
@@ -4697,7 +4849,7 @@ var BaseLine = class extends Element {
|
|
|
4697
4849
|
return bindings;
|
|
4698
4850
|
}
|
|
4699
4851
|
getEditorCapability(token) {
|
|
4700
|
-
return token
|
|
4852
|
+
return provideEditorCapability(token, SelectionBehaviorCapabilityToken, this) ?? provideEditorCapability(token, TransformInteractionCapabilityToken, this) ?? provideEditorCapability(token, SnapSourceCapabilityToken, this);
|
|
4701
4853
|
}
|
|
4702
4854
|
getSelectionBehavior() {
|
|
4703
4855
|
return {
|
|
@@ -4748,16 +4900,19 @@ var BaseLine = class extends Element {
|
|
|
4748
4900
|
}
|
|
4749
4901
|
resolveTransformChange(baseline, intent) {
|
|
4750
4902
|
const effective = isReparentTransformIntent(intent) ? void 0 : baseline.ownedState.effective;
|
|
4751
|
-
const
|
|
4752
|
-
const
|
|
4753
|
-
|
|
4903
|
+
const reference = effective?.reference ?? baseline.reference;
|
|
4904
|
+
const transform = resolveReferenceTransform(reference, intent);
|
|
4905
|
+
const path = transformAuthoredSubpath(effective?.path ?? baseline.ownedState.path, transform.authoredLocalDelta);
|
|
4906
|
+
const bounds = intent.bakeGeometry && !isReparentTransformIntent(intent) && path.anchors.length > 1 ? getAuthoredSubpathBounds(path) : null;
|
|
4907
|
+
const framePatch = bounds ? resolveAuthoredGeometryFramePatch(reference, transform, bounds) : transform.framePatch;
|
|
4908
|
+
return [{
|
|
4754
4909
|
owner: this,
|
|
4755
4910
|
patch: {
|
|
4756
|
-
...
|
|
4911
|
+
...framePatch,
|
|
4757
4912
|
path,
|
|
4758
4913
|
...effective?.relationPatch ? { relations: effective.relationPatch } : {}
|
|
4759
4914
|
}
|
|
4760
|
-
};
|
|
4915
|
+
}];
|
|
4761
4916
|
}
|
|
4762
4917
|
resolveOptions(options) {
|
|
4763
4918
|
const resolved = resolveLinePatch(options);
|
|
@@ -4981,7 +5136,7 @@ var Line = class extends BaseLine {
|
|
|
4981
5136
|
super(options);
|
|
4982
5137
|
}
|
|
4983
5138
|
getEditorCapability(token) {
|
|
4984
|
-
return token
|
|
5139
|
+
return provideEditorCapability(token, PointEditingCapabilityToken, this) ?? super.getEditorCapability(token);
|
|
4985
5140
|
}
|
|
4986
5141
|
resolvePointScope(worldPoint) {
|
|
4987
5142
|
return this.transform.hasPointHint(worldPoint) ? { kind: "owner" } : null;
|
|
@@ -5177,11 +5332,6 @@ function getLineTreeBindableEndpoints(topology, elementKey) {
|
|
|
5177
5332
|
anchorId
|
|
5178
5333
|
} }));
|
|
5179
5334
|
}
|
|
5180
|
-
function getLineTreeEndpointRole(topology, anchorId) {
|
|
5181
|
-
if (!getLineTreePoint(topology, anchorId)) return null;
|
|
5182
|
-
if (getLineTreePointDegrees(topology).get(anchorId) !== 1) return null;
|
|
5183
|
-
return (topology.rootSubpathId === void 0 ? void 0 : getLineTreeBranch(topology, topology.rootSubpathId))?.vertices[0]?.anchorId === anchorId ? "output" : "input";
|
|
5184
|
-
}
|
|
5185
5335
|
function moveLineTreePoint(topology, anchorId, point, relation) {
|
|
5186
5336
|
const index = topology.points.findIndex((candidate) => candidate.anchorId === anchorId);
|
|
5187
5337
|
const current = topology.points[index];
|
|
@@ -5331,16 +5481,13 @@ function updateLineTreeBranchStyle(topology, subpathId, style) {
|
|
|
5331
5481
|
branches
|
|
5332
5482
|
};
|
|
5333
5483
|
}
|
|
5334
|
-
function
|
|
5335
|
-
if (
|
|
5484
|
+
function transformLineTreeTopology(topology, matrix) {
|
|
5485
|
+
if (isIdentityMatrix(matrix)) return topology;
|
|
5336
5486
|
return {
|
|
5337
5487
|
...topology,
|
|
5338
5488
|
points: topology.points.map((point) => ({
|
|
5339
5489
|
...point,
|
|
5340
|
-
point:
|
|
5341
|
-
x: point.point.x * scaleX,
|
|
5342
|
-
y: point.point.y * scaleY
|
|
5343
|
-
}
|
|
5490
|
+
point: transformPoint(matrix, point.point)
|
|
5344
5491
|
}))
|
|
5345
5492
|
};
|
|
5346
5493
|
}
|
|
@@ -5378,12 +5525,14 @@ var LineTreeTransform = class extends ElementTransform {
|
|
|
5378
5525
|
topologySource = null;
|
|
5379
5526
|
pointById = /* @__PURE__ */ new Map();
|
|
5380
5527
|
branchById = /* @__PURE__ */ new Map();
|
|
5528
|
+
endpointRoleById;
|
|
5381
5529
|
junctionAnchorIds = [];
|
|
5382
5530
|
junctionAnchorIdSet = /* @__PURE__ */ new Set();
|
|
5383
5531
|
layoutBounds = null;
|
|
5384
5532
|
resolvedPointById = /* @__PURE__ */ new Map();
|
|
5385
5533
|
worldGeometrySource = null;
|
|
5386
5534
|
worldBranchPoints = [];
|
|
5535
|
+
worldBranchPointsById = /* @__PURE__ */ new Map();
|
|
5387
5536
|
worldPointById = /* @__PURE__ */ new Map();
|
|
5388
5537
|
worldSnapPoints = [];
|
|
5389
5538
|
worldMatrixA = NaN;
|
|
@@ -5398,10 +5547,22 @@ var LineTreeTransform = class extends ElementTransform {
|
|
|
5398
5547
|
this.topologySource = topology;
|
|
5399
5548
|
this.pointById = new Map(topology.points.map((point) => [point.anchorId, point]));
|
|
5400
5549
|
this.branchById = new Map(topology.branches.map((branch) => [branch.subpathId, branch]));
|
|
5550
|
+
this.endpointRoleById = void 0;
|
|
5401
5551
|
this.junctionAnchorIds = getLineTreeJunctionAnchorIds(topology);
|
|
5402
5552
|
this.junctionAnchorIdSet = new Set(this.junctionAnchorIds);
|
|
5403
5553
|
this.layoutBounds = getLineTreeTopologyBounds(topology);
|
|
5404
5554
|
}
|
|
5555
|
+
buildEndpointRoles() {
|
|
5556
|
+
const topology = this.element.topology;
|
|
5557
|
+
const degrees = getLineTreePointDegrees(topology);
|
|
5558
|
+
const rootAnchorId = topology.rootSubpathId === void 0 ? void 0 : this.branchById.get(topology.rootSubpathId)?.vertices[0]?.anchorId;
|
|
5559
|
+
const roles = /* @__PURE__ */ new Map();
|
|
5560
|
+
for (const point of topology.points) {
|
|
5561
|
+
if (degrees.get(point.anchorId) !== 1) continue;
|
|
5562
|
+
roles.set(point.anchorId, point.anchorId === rootAnchorId ? "output" : "input");
|
|
5563
|
+
}
|
|
5564
|
+
return roles;
|
|
5565
|
+
}
|
|
5405
5566
|
getPoint(anchorId) {
|
|
5406
5567
|
this.ensureTopologyViews();
|
|
5407
5568
|
return this.pointById.get(anchorId);
|
|
@@ -5410,6 +5571,11 @@ var LineTreeTransform = class extends ElementTransform {
|
|
|
5410
5571
|
this.ensureTopologyViews();
|
|
5411
5572
|
return this.branchById.get(subpathId);
|
|
5412
5573
|
}
|
|
5574
|
+
getEndpointRole(anchorId) {
|
|
5575
|
+
this.ensureTopologyViews();
|
|
5576
|
+
this.endpointRoleById ??= this.buildEndpointRoles();
|
|
5577
|
+
return this.endpointRoleById.get(anchorId) ?? null;
|
|
5578
|
+
}
|
|
5413
5579
|
getResolvedPoint(anchorId) {
|
|
5414
5580
|
this.getGeometrySnapshot();
|
|
5415
5581
|
return this.resolvedPointById.get(anchorId);
|
|
@@ -5527,14 +5693,17 @@ var LineTreeTransform = class extends ElementTransform {
|
|
|
5527
5693
|
if (vertex.anchorId !== void 0) this.worldPointById.set(vertex.anchorId, world);
|
|
5528
5694
|
return world;
|
|
5529
5695
|
}));
|
|
5696
|
+
this.worldBranchPointsById = /* @__PURE__ */ new Map();
|
|
5697
|
+
snapshot.subpaths.forEach((subpath, index) => {
|
|
5698
|
+
if (subpath.subpathId !== void 0) this.worldBranchPointsById.set(subpath.subpathId, this.worldBranchPoints[index]);
|
|
5699
|
+
});
|
|
5530
5700
|
this.worldSnapPoints = this.element.topology.points.map((point) => {
|
|
5531
5701
|
return this.worldPointById.get(point.anchorId);
|
|
5532
5702
|
});
|
|
5533
5703
|
}
|
|
5534
5704
|
getWorldBranchPointsView(subpathId) {
|
|
5535
5705
|
this.updateWorldPoints();
|
|
5536
|
-
|
|
5537
|
-
return this.worldBranchPoints[index];
|
|
5706
|
+
return this.worldBranchPointsById.get(subpathId);
|
|
5538
5707
|
}
|
|
5539
5708
|
getWorldPointView(anchorId) {
|
|
5540
5709
|
this.updateWorldPoints();
|
|
@@ -5639,7 +5808,7 @@ var LineTree = class extends Element {
|
|
|
5639
5808
|
return this;
|
|
5640
5809
|
}
|
|
5641
5810
|
getEditorCapability(token) {
|
|
5642
|
-
return token
|
|
5811
|
+
return provideEditorCapability(token, SelectionBehaviorCapabilityToken, this) ?? provideEditorCapability(token, TransformInteractionCapabilityToken, this) ?? provideEditorCapability(token, SnapSourceCapabilityToken, this) ?? provideEditorCapability(token, PointEditingCapabilityToken, this);
|
|
5643
5812
|
}
|
|
5644
5813
|
getSelectionBehavior() {
|
|
5645
5814
|
return {
|
|
@@ -5717,20 +5886,28 @@ var LineTree = class extends Element {
|
|
|
5717
5886
|
}
|
|
5718
5887
|
resolveTransformChange(baseline, intent) {
|
|
5719
5888
|
const effective = isReparentTransformIntent(intent) ? void 0 : baseline.ownedState.effective;
|
|
5720
|
-
const
|
|
5721
|
-
|
|
5889
|
+
const bakeGeometry = intent.bakeGeometry && !isReparentTransformIntent(intent);
|
|
5890
|
+
const reference = effective?.reference ?? (bakeGeometry ? {
|
|
5891
|
+
...baseline.reference,
|
|
5892
|
+
writeMode: "authored-geometry"
|
|
5893
|
+
} : baseline.reference);
|
|
5894
|
+
const transform = resolveReferenceTransform(reference, intent);
|
|
5895
|
+
const topology = bakeGeometry ? transformLineTreeTopology(effective?.topology ?? baseline.ownedState.topology, transform.authoredLocalDelta) : effective ? transformLineTreeTopology(effective.topology, transform.authoredLocalDelta) : void 0;
|
|
5896
|
+
const bounds = bakeGeometry && topology ? getLineTreeTopologyBounds(topology) : null;
|
|
5897
|
+
const framePatch = bounds ? resolveAuthoredGeometryFramePatch(reference, transform, bounds) : transform.framePatch;
|
|
5898
|
+
return [{
|
|
5722
5899
|
owner: this,
|
|
5723
5900
|
patch: {
|
|
5724
|
-
...
|
|
5725
|
-
...
|
|
5901
|
+
...framePatch,
|
|
5902
|
+
...topology ? { topology } : {}
|
|
5726
5903
|
}
|
|
5727
|
-
};
|
|
5904
|
+
}];
|
|
5728
5905
|
}
|
|
5729
5906
|
getWorldSnapPointsView() {
|
|
5730
5907
|
return this.transform.getWorldSnapPointsView();
|
|
5731
5908
|
}
|
|
5732
5909
|
getEndpointRole(anchorId) {
|
|
5733
|
-
return
|
|
5910
|
+
return this.transform.getEndpointRole(anchorId);
|
|
5734
5911
|
}
|
|
5735
5912
|
getEndpointRelation(anchorId) {
|
|
5736
5913
|
return this.transform.getPoint(anchorId)?.relation;
|
|
@@ -5751,7 +5928,7 @@ var LineTree = class extends Element {
|
|
|
5751
5928
|
resolveDisconnectEndpointChange(anchorIds, localPositions) {
|
|
5752
5929
|
let topology = this.topology;
|
|
5753
5930
|
for (const anchorId of new Set(anchorIds)) {
|
|
5754
|
-
if (!
|
|
5931
|
+
if (!this.transform.getPoint(anchorId)?.relation) continue;
|
|
5755
5932
|
topology = moveLineTreePoint(topology, anchorId, localPositions?.get(anchorId) ?? this.transform.getResolvedPoint(anchorId).point, void 0);
|
|
5756
5933
|
}
|
|
5757
5934
|
return topology === this.topology ? null : {
|
|
@@ -5836,7 +6013,7 @@ var LineTree = class extends Element {
|
|
|
5836
6013
|
};
|
|
5837
6014
|
}
|
|
5838
6015
|
getBranch(subpathId) {
|
|
5839
|
-
return
|
|
6016
|
+
return this.transform.getBranch(subpathId);
|
|
5840
6017
|
}
|
|
5841
6018
|
resolveSubpathAtWorldPoint(point, preferredSubpathId) {
|
|
5842
6019
|
return this.transform.resolveSubpathAtWorldPoint(point, preferredSubpathId);
|
|
@@ -5970,4 +6147,4 @@ function applyLineTreeTopologyDelta(topology, delta, direction) {
|
|
|
5970
6147
|
};
|
|
5971
6148
|
}
|
|
5972
6149
|
//#endregion
|
|
5973
|
-
export { BaseLine, Bitmap, Circle, CircleTransform, ContentEditingCapabilityToken, DEFAULT_LINE_TREE_BRANCH_STYLE, EMPTY_LINE_TREE_TOPOLOGY, EditorFrameFlush, EndpointRelationIndex, Group, GroupTransform, Image, ImageProperties, ImageTransform, Line, LineTransform, LineTree, LineTreeProperties, LineTreeTransform, PointEditingCapabilityToken, Polygon, PolygonTransform, Rectangle, RectangleProperties, RectangleTransform, RippleOverlay, ScrollView, ScrollViewTransform, SelectionBehaviorCapabilityToken, SelectionBehaviorMode, SelectionDiveMode, SelectionSnapMode, SnapSourceCapabilityToken, Text, TextBlock, TextProperties, TextTransform, TransformInteractionCapabilityToken, Triangle, TriangleTransform, VectorPath, VectorPathProperties, VectorPathTransform, WorkerSurfacePool, Workspace, WorkspaceProperties, addLineTreeBranchAtTail, applyLineTreeTopologyDelta,
|
|
6150
|
+
export { BaseLine, Bitmap, Circle, CircleTransform, ContentEditingCapabilityToken, DEFAULT_LINE_TREE_BRANCH_STYLE, EMPTY_LINE_TREE_TOPOLOGY, EditorFrameFlush, EndpointRelationIndex, Group, GroupTransform, Image, ImageProperties, ImageTransform, Line, LineTransform, LineTree, LineTreeProperties, LineTreeTransform, PointEditingCapabilityToken, Polygon, PolygonTransform, Rectangle, RectangleProperties, RectangleTransform, RippleOverlay, ScrollView, ScrollViewTransform, SelectionBehaviorCapabilityToken, SelectionBehaviorMode, SelectionDiveMode, SelectionSnapMode, SnapSourceCapabilityToken, Text, TextBlock, TextProperties, TextTransform, TransformInteractionCapabilityToken, Triangle, TriangleTransform, VectorPath, VectorPathProperties, VectorPathTransform, WorkerSurfacePool, Workspace, WorkspaceProperties, addLineTreeBranchAtTail, applyLineTreeTopologyDelta, computePosition, connectEndpoint, connectionPortAccepts, createLineTreeTopology, createLineTreeTopologyDelta, defineEditorCapability, disconnectEndpoint, fitWorkspace, flip, getEditorCapability, getEditorFrameFlush, getEndpointRelationIndex, getLineTreeBindableEndpoints, getLineTreeBranch, getLineTreeEndpointRelationBindings, getLineTreeJunctionAnchorIds, getLineTreePoint, getLineTreePointReferenceCounts, getTextBlockVisualBounds, hasConnectionPortCapacity, insertLineTreePoint, isEditorCapabilityHost, isEndpointRelationSource, measureTextRun, measureTextWidth, moveLineTreePoint, offset, paintTextBlock, remapLineTreeRelations, removeLineTreeBranch, removeLineTreePoint, resolveConnectEndpointChange, resolveConnectionPortChanges, resolveDisconnectEndpointChanges, resolveSelectionFrameBounds, resolveTextStyle, shift, subscribeTextMeasurementChanges, transformLineTreeTopology, updateLineTreeBranchStyle, workspaceFocusInsetsKey };
|
package/dist/line/base.d.ts
CHANGED
|
@@ -110,7 +110,7 @@ export declare abstract class BaseLine extends Element<LineProperties, LineOptio
|
|
|
110
110
|
};
|
|
111
111
|
protected resolvePointPathPatch(path: AuthoredSubpath): LinePatch;
|
|
112
112
|
captureTransformBaseline(): ElementTransformBaseline<LineTransformOwnedState>;
|
|
113
|
-
resolveTransformChange(baseline: Readonly<ElementTransformBaseline<LineTransformOwnedState>>, intent: Readonly<TransformIntent | ReparentTransformIntent>): OwnerPropertyChange;
|
|
113
|
+
resolveTransformChange(baseline: Readonly<ElementTransformBaseline<LineTransformOwnedState>>, intent: Readonly<TransformIntent | ReparentTransformIntent>): readonly OwnerPropertyChange[];
|
|
114
114
|
protected resolveOptions(options: LinePatch): LinePatch;
|
|
115
115
|
get headAnchor(): AuthoredPathAnchor;
|
|
116
116
|
get tailAnchor(): AuthoredPathAnchor;
|
|
@@ -45,7 +45,7 @@ export declare class LineTree extends Element<LineTreeProperties, LineTreeOption
|
|
|
45
45
|
private materializeEffectiveTopology;
|
|
46
46
|
private preservePoseForTopology;
|
|
47
47
|
captureTransformBaseline(): ElementTransformBaseline<LineTreeTransformOwnedState>;
|
|
48
|
-
resolveTransformChange(baseline: Readonly<ElementTransformBaseline<LineTreeTransformOwnedState>>, intent: Readonly<TransformIntent | ReparentTransformIntent>): OwnerPropertyChange;
|
|
48
|
+
resolveTransformChange(baseline: Readonly<ElementTransformBaseline<LineTreeTransformOwnedState>>, intent: Readonly<TransformIntent | ReparentTransformIntent>): readonly OwnerPropertyChange[];
|
|
49
49
|
getWorldSnapPointsView(): readonly PointView[];
|
|
50
50
|
getEndpointRole(anchorId: AnchorId): "input" | "output";
|
|
51
51
|
getEndpointRelation(anchorId: AnchorId): EndpointRelation;
|
|
@@ -70,5 +70,5 @@ export declare class LineTree extends Element<LineTreeProperties, LineTreeOption
|
|
|
70
70
|
protected onDeactivate(root: Root | null, layer: Layer | null): void;
|
|
71
71
|
}
|
|
72
72
|
export { LineTreeTransform } from "./transform";
|
|
73
|
-
export { EMPTY_LINE_TREE_TOPOLOGY, DEFAULT_LINE_TREE_BRANCH_STYLE, addLineTreeBranchAtTail, createLineTreeTopology, getLineTreeBranch, getLineTreeBindableEndpoints, getLineTreeEndpointRelationBindings, getLineTreeEndpointRole, getLineTreeJunctionAnchorIds, getLineTreePoint, getLineTreePointReferenceCounts, insertLineTreePoint, moveLineTreePoint, removeLineTreeBranch, removeLineTreePoint, remapLineTreeRelations,
|
|
73
|
+
export { EMPTY_LINE_TREE_TOPOLOGY, DEFAULT_LINE_TREE_BRANCH_STYLE, addLineTreeBranchAtTail, createLineTreeTopology, getLineTreeBranch, getLineTreeBindableEndpoints, getLineTreeEndpointRelationBindings, getLineTreeEndpointRole, getLineTreeJunctionAnchorIds, getLineTreePoint, getLineTreePointReferenceCounts, insertLineTreePoint, moveLineTreePoint, removeLineTreeBranch, removeLineTreePoint, remapLineTreeRelations, transformLineTreeTopology, updateLineTreeBranchStyle } from "./topology";
|
|
74
74
|
export type { LineTreeBranch, LineTreeBranchRemoval, LineTreeBranchTailSource, LineTreeBranchStyle, LineTreeBranchVertex, LineTreeOption, LineTreePoint, LineTreeTopology } from "./types";
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { type AnchorId, type SegmentId, type SubpathId } from "@fulate/core";
|
|
2
|
-
import type
|
|
3
|
-
import type { Rect } from "@fulate/share";
|
|
2
|
+
import { type PointView, type Rect } from "@fulate/share";
|
|
4
3
|
import type { LineEndpointRelationPatch } from "../line/base";
|
|
5
4
|
import type { BindableEndpoint, EndpointRelation, EndpointRelationBinding } from "../connection/types";
|
|
6
5
|
import type { LineTreeBranch, LineTreeBranchRemoval, LineTreeBranchStyle, LineTreePoint, LineTreeTopology } from "./types";
|
|
@@ -12,6 +11,7 @@ export declare function getLineTreePoint(topology: LineTreeTopology, anchorId: A
|
|
|
12
11
|
export declare function getLineTreeBranch(topology: LineTreeTopology, subpathId: SubpathId): LineTreeBranch;
|
|
13
12
|
export declare function getLineTreePointReferenceCounts(topology: LineTreeTopology): Map<AnchorId, number>;
|
|
14
13
|
export declare function getLineTreeJunctionAnchorIds(topology: LineTreeTopology): AnchorId[];
|
|
14
|
+
export declare function getLineTreePointDegrees(topology: LineTreeTopology): Map<AnchorId, number>;
|
|
15
15
|
export declare function getLineTreeEndpointRelationBindings(topology: LineTreeTopology, elementKey: string): readonly EndpointRelationBinding[];
|
|
16
16
|
export declare function getLineTreeBindableEndpoints(topology: LineTreeTopology, elementKey: string): readonly BindableEndpoint[];
|
|
17
17
|
export declare function getLineTreeEndpointRole(topology: LineTreeTopology, anchorId: AnchorId): "input" | "output";
|
|
@@ -35,5 +35,5 @@ export declare function addLineTreeBranchAtTail(topology: LineTreeTopology, pare
|
|
|
35
35
|
};
|
|
36
36
|
export declare function removeLineTreeBranch(topology: LineTreeTopology, subpathId: SubpathId): LineTreeBranchRemoval;
|
|
37
37
|
export declare function updateLineTreeBranchStyle(topology: LineTreeTopology, subpathId: SubpathId, style: LineTreeBranchStyle): LineTreeTopology;
|
|
38
|
-
export declare function
|
|
38
|
+
export declare function transformLineTreeTopology(topology: LineTreeTopology, matrix: DOMMatrixReadOnly): LineTreeTopology;
|
|
39
39
|
export declare function remapLineTreeRelations(topology: LineTreeTopology, keyMap: ReadonlyMap<string, string>): LineTreeTopology;
|
|
@@ -6,12 +6,14 @@ export declare class LineTreeTransform<E extends LineTree = LineTree> extends El
|
|
|
6
6
|
private topologySource;
|
|
7
7
|
private pointById;
|
|
8
8
|
private branchById;
|
|
9
|
+
private endpointRoleById;
|
|
9
10
|
private junctionAnchorIds;
|
|
10
11
|
private junctionAnchorIdSet;
|
|
11
12
|
private layoutBounds;
|
|
12
13
|
private resolvedPointById;
|
|
13
14
|
private worldGeometrySource;
|
|
14
15
|
private worldBranchPoints;
|
|
16
|
+
private worldBranchPointsById;
|
|
15
17
|
private worldPointById;
|
|
16
18
|
private worldSnapPoints;
|
|
17
19
|
private worldMatrixA;
|
|
@@ -21,8 +23,10 @@ export declare class LineTreeTransform<E extends LineTree = LineTree> extends El
|
|
|
21
23
|
private worldMatrixE;
|
|
22
24
|
private worldMatrixF;
|
|
23
25
|
private ensureTopologyViews;
|
|
26
|
+
private buildEndpointRoles;
|
|
24
27
|
getPoint(anchorId: AnchorId): LineTreePoint;
|
|
25
28
|
getBranch(subpathId: SubpathId): LineTreeBranch;
|
|
29
|
+
getEndpointRole(anchorId: AnchorId): "input" | "output";
|
|
26
30
|
getResolvedPoint(anchorId: AnchorId): LineTreePoint;
|
|
27
31
|
getJunctionAnchorIds(): readonly AnchorId[];
|
|
28
32
|
isJunctionAnchor(anchorId: AnchorId): boolean;
|
package/dist/polygon.d.ts
CHANGED
|
@@ -37,7 +37,7 @@ export declare class Polygon extends EditorShape<PolygonProperties, PolygonOptio
|
|
|
37
37
|
getPointScopeOutline(_scope: PointEditingScope): any;
|
|
38
38
|
resolvePointEdit(request: PointEditRequest): PointOwnerEdit | null;
|
|
39
39
|
captureTransformBaseline(): ElementTransformBaseline<AuthoredSubpath<import("@fulate/core").AuthoredPathAnchor>>;
|
|
40
|
-
resolveTransformChange(baseline: Readonly<ElementTransformBaseline<AuthoredSubpath>>, intent: Readonly<TransformIntent | ReparentTransformIntent>): OwnerPropertyChange;
|
|
40
|
+
resolveTransformChange(baseline: Readonly<ElementTransformBaseline<AuthoredSubpath>>, intent: Readonly<TransformIntent | ReparentTransformIntent>): readonly OwnerPropertyChange[];
|
|
41
41
|
protected buildPath(ctx: CanvasRenderingContext2D): void;
|
|
42
42
|
protected buildBorderPath(ctx: CanvasRenderingContext2D): void;
|
|
43
43
|
getEdgeSegment(edge: Edge): {
|
package/dist/text/layout.d.ts
CHANGED
|
@@ -38,8 +38,6 @@ export declare function measureTextAlphabeticBaselineOffset(text: string, font:
|
|
|
38
38
|
* `white-space: pre-wrap` keeps the caller's spaces and hard breaks intact.
|
|
39
39
|
*/
|
|
40
40
|
export declare function measureTextWidth(text: string, font: string, letterSpacing?: number): number;
|
|
41
|
-
/** Fit a single run and append an ellipsis only when it does not fit. */
|
|
42
|
-
export declare function fitTextWithEllipsis(text: string, maxWidth: number, font: string, letterSpacing?: number): string;
|
|
43
41
|
declare class PreparedTextLayout {
|
|
44
42
|
readonly text: string;
|
|
45
43
|
readonly font: string;
|
package/dist/text/paint.d.ts
CHANGED
|
@@ -16,16 +16,6 @@ export interface TextDecorationOptions {
|
|
|
16
16
|
thickness?: number;
|
|
17
17
|
}
|
|
18
18
|
export declare function drawTextDecoration(ctx: CanvasRenderingContext2D, opts: TextDecorationOptions): void;
|
|
19
|
-
export interface TextStrokeOptions {
|
|
20
|
-
line: string;
|
|
21
|
-
x: number;
|
|
22
|
-
y: number;
|
|
23
|
-
font: string;
|
|
24
|
-
letterSpacing: number;
|
|
25
|
-
strokeColor: string | CanvasGradient;
|
|
26
|
-
strokeWidth: number;
|
|
27
|
-
}
|
|
28
|
-
export declare function drawTextWithStroke(ctx: CanvasRenderingContext2D, opts: TextStrokeOptions): void;
|
|
29
19
|
export declare function applyTextShadow(ctx: CanvasRenderingContext2D, shadow: ShadowOption): void;
|
|
30
20
|
export declare function clearTextShadow(ctx: CanvasRenderingContext2D): void;
|
|
31
21
|
export interface TextRunMetrics {
|
package/dist/vector-path.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type PointView } from "@fulate/share";
|
|
2
2
|
import { ChangeImpact, ShapeProperties, type AuthoredSubpath, type GeometrySnapshot, type OwnerPropertyChange, type PropertyChanges, type ReparentTransformIntent, type ShapeOption, type ShapeOptionPatch, type ElementTransformBaseline, type TransformIntent } from "@fulate/core";
|
|
3
3
|
import { EditorShape, EditorShapeTransform } from "./editor/shape";
|
|
4
4
|
import { type EditorCapabilityToken, type PointControlDescriptor, type PointEditRequest, type PointEditingScope, type PointOwnerEdit } from "./editor/capability";
|
|
@@ -52,7 +52,7 @@ export declare class VectorPath extends EditorShape<VectorPathProperties, Vector
|
|
|
52
52
|
private findSegment;
|
|
53
53
|
private ownerPathChange;
|
|
54
54
|
captureTransformBaseline(): ElementTransformBaseline<VectorPathGeometry>;
|
|
55
|
-
resolveTransformChange(baseline: Readonly<ElementTransformBaseline<VectorPathGeometry>>, intent: Readonly<TransformIntent | ReparentTransformIntent>): OwnerPropertyChange;
|
|
55
|
+
resolveTransformChange(baseline: Readonly<ElementTransformBaseline<VectorPathGeometry>>, intent: Readonly<TransformIntent | ReparentTransformIntent>): readonly OwnerPropertyChange[];
|
|
56
56
|
protected buildPath(ctx: CanvasRenderingContext2D): void;
|
|
57
57
|
}
|
|
58
58
|
export {};
|