@fulate/ui 1.0.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/README.md +199 -0
- package/dist/authored-path.d.ts +28 -0
- package/dist/bitmap.d.ts +19 -0
- package/dist/circle.d.ts +23 -0
- package/dist/connection/commands.d.ts +27 -0
- package/dist/connection/endpoint-relation-index.d.ts +27 -0
- package/dist/connection/index.d.ts +4 -0
- package/dist/connection/types.d.ts +37 -0
- package/dist/connection-port-decoration.d.ts +27 -0
- package/dist/editor/capability.d.ts +150 -0
- package/dist/editor/frame-flush.d.ts +39 -0
- package/dist/editor/shape.d.ts +38 -0
- package/dist/editor/transform.d.ts +8 -0
- package/dist/floating.d.ts +9 -0
- package/dist/group.d.ts +44 -0
- package/dist/image.d.ts +52 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.js +5956 -0
- package/dist/line/base.d.ts +142 -0
- package/dist/line/helpers/decoration.d.ts +4 -0
- package/dist/line/index.d.ts +3 -0
- package/dist/line/straight-primitives.d.ts +8 -0
- package/dist/line/straight.d.ts +19 -0
- package/dist/line-tree/history-delta.d.ts +22 -0
- package/dist/line-tree/index.d.ts +74 -0
- package/dist/line-tree/topology.d.ts +39 -0
- package/dist/line-tree/transform.d.ts +54 -0
- package/dist/line-tree/types.d.ts +46 -0
- package/dist/paint/circle.d.ts +3 -0
- package/dist/point-controls.d.ts +6 -0
- package/dist/polygon.d.ts +52 -0
- package/dist/rectangle.d.ts +27 -0
- package/dist/ripple.d.ts +29 -0
- package/dist/scrollview.d.ts +68 -0
- package/dist/text/block.d.ts +31 -0
- package/dist/text/editing.d.ts +16 -0
- package/dist/text/index.d.ts +149 -0
- package/dist/text/layout.d.ts +101 -0
- package/dist/text/paint.d.ts +59 -0
- package/dist/text/style.d.ts +13 -0
- package/dist/triangle.d.ts +20 -0
- package/dist/vector-path.d.ts +58 -0
- package/dist/worker-surface-protocol.d.ts +26 -0
- package/dist/worker-surface-worker.d.ts +25 -0
- package/dist/worker-surface-worker.js +109 -0
- package/dist/worker-surface.d.ts +23 -0
- package/dist/workspace.d.ts +44 -0
- package/package.json +33 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { PointView } from "@fulate/share";
|
|
2
|
+
import type { Text } from "./index";
|
|
3
|
+
import type { ContentEditingConsumer } from "../editor/capability";
|
|
4
|
+
declare const SELECTION_COLOR = "rgba(66, 133, 244, 0.3)";
|
|
5
|
+
export { SELECTION_COLOR };
|
|
6
|
+
export declare function getCaretLocalXY(self: Text, caretIndex: number): {
|
|
7
|
+
x: number;
|
|
8
|
+
y: number;
|
|
9
|
+
h: number;
|
|
10
|
+
};
|
|
11
|
+
export declare function syncEditingText(self: Text, value: string): void;
|
|
12
|
+
export declare function enterEditing(self: Text, initialClickLocal?: PointView, consumer?: ContentEditingConsumer | null): void;
|
|
13
|
+
export declare function requestEditingFinish(self: Text): void;
|
|
14
|
+
export declare function closeEditing(self: Text): void;
|
|
15
|
+
export declare const exitEditing: typeof requestEditingFinish;
|
|
16
|
+
export declare function setupClickToEdit(self: Text): (() => void) | null;
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { ChangeImpact, type ElementTransformClass, type GeometrySnapshot, type Layer, type PropertyChanges, type Root, type ShadowOption } from "@fulate/core";
|
|
2
|
+
import { Rectangle, RectangleProperties, RectangleTransform, type RectangleOption } from "../rectangle";
|
|
3
|
+
import { type BackgroundColor, type PointView, type Rect } from "@fulate/share";
|
|
4
|
+
import { type TextLineBoxMetrics } from "./paint";
|
|
5
|
+
import { TEXT_STYLE_DEFAULTS, type TextStyleKey, type TextStyleConfig, type ResolvedTextStyle, buildFontString } from "./style";
|
|
6
|
+
import { type ContentEditingConsumer, type ContentEditingSession, type EditorCapabilityToken } from "../editor/capability";
|
|
7
|
+
export type { TextStyleConfig, TextStyleKey, ResolvedTextStyle };
|
|
8
|
+
export { buildFontString, TEXT_STYLE_DEFAULTS };
|
|
9
|
+
export { measureTextRun } from "./paint";
|
|
10
|
+
export type { TextLineBoxMetrics, TextRunMetrics } from "./paint";
|
|
11
|
+
/**
|
|
12
|
+
* Text uses its intrinsic text size when width/height are omitted. Supplying a
|
|
13
|
+
* width constrains wrapping; `wordWrap`, `ellipsis`, and `overflow` are
|
|
14
|
+
* independent. By default text does not wrap, truncate, or clip.
|
|
15
|
+
*/
|
|
16
|
+
export interface TextOption<T = Text> extends RectangleOption<T> {
|
|
17
|
+
text?: string;
|
|
18
|
+
fontSize?: number;
|
|
19
|
+
fontFamily?: string;
|
|
20
|
+
fontWeight?: string | number;
|
|
21
|
+
fontStyle?: string;
|
|
22
|
+
color?: BackgroundColor;
|
|
23
|
+
textAlign?: "left" | "center" | "right";
|
|
24
|
+
textBaseline?: CanvasTextBaseline;
|
|
25
|
+
verticalAlign?: "top" | "middle" | "bottom";
|
|
26
|
+
underline?: boolean;
|
|
27
|
+
strikethrough?: boolean;
|
|
28
|
+
lineHeight?: number;
|
|
29
|
+
wordWrap?: boolean;
|
|
30
|
+
/** 单行或最后可见行放不下时是否绘制省略号;与 `wordWrap`、`overflow` 独立。 */
|
|
31
|
+
ellipsis?: boolean;
|
|
32
|
+
maxLines?: number;
|
|
33
|
+
editable?: boolean;
|
|
34
|
+
placeholder?: string;
|
|
35
|
+
placeholderColor?: string;
|
|
36
|
+
letterSpacing?: number;
|
|
37
|
+
textStrokeColor?: BackgroundColor;
|
|
38
|
+
textStrokeWidth?: number;
|
|
39
|
+
textShadow?: ShadowOption | null;
|
|
40
|
+
}
|
|
41
|
+
export declare class TextProperties extends RectangleProperties {
|
|
42
|
+
text: string;
|
|
43
|
+
fontSize?: number;
|
|
44
|
+
fontFamily?: string;
|
|
45
|
+
fontWeight?: string | number;
|
|
46
|
+
fontStyle?: string;
|
|
47
|
+
color?: BackgroundColor;
|
|
48
|
+
textAlign?: "left" | "center" | "right";
|
|
49
|
+
textBaseline?: CanvasTextBaseline;
|
|
50
|
+
verticalAlign?: "top" | "middle" | "bottom";
|
|
51
|
+
underline?: boolean;
|
|
52
|
+
strikethrough?: boolean;
|
|
53
|
+
lineHeight?: number;
|
|
54
|
+
wordWrap?: boolean;
|
|
55
|
+
ellipsis: boolean;
|
|
56
|
+
maxLines?: number;
|
|
57
|
+
editable: boolean;
|
|
58
|
+
placeholder: string;
|
|
59
|
+
placeholderColor: string;
|
|
60
|
+
letterSpacing?: number;
|
|
61
|
+
textStrokeColor?: BackgroundColor;
|
|
62
|
+
textStrokeWidth?: number;
|
|
63
|
+
textShadow?: Readonly<ShadowOption> | null;
|
|
64
|
+
constructor(options?: TextOption);
|
|
65
|
+
getImpact(changes: PropertyChanges): ChangeImpact;
|
|
66
|
+
}
|
|
67
|
+
export declare class TextTransform<E extends Text = Text> extends RectangleTransform<E> {
|
|
68
|
+
protected resolveFitSize(): void;
|
|
69
|
+
protected buildVisualBounds(snapshot: GeometrySnapshot): Readonly<Rect> | null;
|
|
70
|
+
protected buildVisualOutline(snapshot: GeometrySnapshot): PointView[];
|
|
71
|
+
}
|
|
72
|
+
export declare class Text<P extends TextProperties = TextProperties, O extends TextOption<any> = TextOption<any>> extends Rectangle<P, O> {
|
|
73
|
+
readonly type = "text";
|
|
74
|
+
get text(): string;
|
|
75
|
+
get fontSize(): number;
|
|
76
|
+
get fontFamily(): string;
|
|
77
|
+
get fontWeight(): string | number;
|
|
78
|
+
get fontStyle(): string;
|
|
79
|
+
get color(): BackgroundColor;
|
|
80
|
+
get textAlign(): "left" | "center" | "right";
|
|
81
|
+
get textBaseline(): CanvasTextBaseline;
|
|
82
|
+
get verticalAlign(): "top" | "bottom" | "middle";
|
|
83
|
+
get underline(): boolean;
|
|
84
|
+
get strikethrough(): boolean;
|
|
85
|
+
get lineHeight(): number;
|
|
86
|
+
get wordWrap(): boolean;
|
|
87
|
+
get ellipsis(): boolean;
|
|
88
|
+
get maxLines(): number;
|
|
89
|
+
get editable(): boolean;
|
|
90
|
+
get placeholder(): string;
|
|
91
|
+
get placeholderColor(): string;
|
|
92
|
+
get letterSpacing(): number;
|
|
93
|
+
get textStrokeColor(): BackgroundColor;
|
|
94
|
+
get textStrokeWidth(): number;
|
|
95
|
+
get textShadow(): Readonly<ShadowOption>;
|
|
96
|
+
get isEditing(): boolean;
|
|
97
|
+
textarea: HTMLTextAreaElement | null;
|
|
98
|
+
editingConsumer: ContentEditingConsumer | null;
|
|
99
|
+
editAbort: AbortController | null;
|
|
100
|
+
editSubscriptionRemove: (() => void) | null;
|
|
101
|
+
caretVisible: boolean;
|
|
102
|
+
blinkTimer: ReturnType<typeof setInterval> | null;
|
|
103
|
+
composing: boolean;
|
|
104
|
+
scrollX: number;
|
|
105
|
+
selAnchor: number;
|
|
106
|
+
private clickToEditRemove;
|
|
107
|
+
private skinTextCommitRemove;
|
|
108
|
+
private textMeasurementRemove;
|
|
109
|
+
/** One resolved style snapshot; authored and Root skin changes invalidate it. */
|
|
110
|
+
private resolvedTextStyleCache;
|
|
111
|
+
private textLineBoxMetricsCache;
|
|
112
|
+
private readonly textBlock;
|
|
113
|
+
constructor(options?: NoInfer<O>, properties?: P, Transform?: ElementTransformClass);
|
|
114
|
+
getEditorCapability<T>(token: EditorCapabilityToken<T>): T | undefined;
|
|
115
|
+
beginContentEditing(consumer: ContentEditingConsumer): ContentEditingSession | undefined;
|
|
116
|
+
get fontString(): string;
|
|
117
|
+
getResolvedTextStyle(): ResolvedTextStyle;
|
|
118
|
+
getFontString(style?: Required<TextStyleConfig>): string;
|
|
119
|
+
get textLayout(): import("./layout").TextLayoutSnapshot;
|
|
120
|
+
/** @internal Shared by TextRow layout and the final Text painter. */
|
|
121
|
+
getTextLineBoxMetrics(text?: string): TextLineBoxMetrics;
|
|
122
|
+
/** @internal Alphabetic baseline relative to this Text box. */
|
|
123
|
+
getTextBlockFirstAlphabeticBaseline(): number;
|
|
124
|
+
/** @internal The editor frame follows the Text box, not visible overflow. */
|
|
125
|
+
getSelectionFrameBounds(): Readonly<Rect> | null;
|
|
126
|
+
get lines(): string[];
|
|
127
|
+
get lineCharOffsets(): readonly number[];
|
|
128
|
+
get textHeight(): number;
|
|
129
|
+
protected getLayoutWidth(): number;
|
|
130
|
+
protected getResolvedHeight(): number;
|
|
131
|
+
syncTextLayout(): void;
|
|
132
|
+
/** 布局 owner 提交 Text 自身的 intrinsic size;派生布局可覆写为只采用其 resolved frame。 */
|
|
133
|
+
protected commitTextLayoutSize(width: number | undefined, height: number | undefined): void;
|
|
134
|
+
/** @internal Shared by Yoga Span and the canvas Text owner. */
|
|
135
|
+
measureTextLayout(width: number, height?: number): import("./layout").TextLayoutMetrics;
|
|
136
|
+
/** @internal TextTransform 消费现有 Text layout owner 的 visual bounds。 */
|
|
137
|
+
getVisibleTextBounds(): Rect | null;
|
|
138
|
+
enterEditing(initialClickLocal?: PointView): void;
|
|
139
|
+
exitEditing(): void;
|
|
140
|
+
setEditingText(value: string): void;
|
|
141
|
+
protected onPropertiesChanged(current: P, changes: PropertyChanges): void;
|
|
142
|
+
protected onActivate(root: Root | null, layer: Layer | null): void;
|
|
143
|
+
protected onDeactivate(root: Root | null, layer: Layer | null): void;
|
|
144
|
+
private readonly handleTextMeasurementChanged;
|
|
145
|
+
/** @internal Layout owners such as Span use this to invalidate Yoga. */
|
|
146
|
+
protected onTextLayoutInvalidated(): void;
|
|
147
|
+
protected paintTextContent(ctx: CanvasRenderingContext2D, offsetY?: number): void;
|
|
148
|
+
protected paintContent(ctx: CanvasRenderingContext2D): void;
|
|
149
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { type LayoutLine } from "@chenglou/pretext";
|
|
2
|
+
export declare function subscribeTextMeasurementChanges(listener: () => void): () => boolean;
|
|
3
|
+
export interface TextLayoutLine {
|
|
4
|
+
readonly text: string;
|
|
5
|
+
readonly width: number;
|
|
6
|
+
readonly ellipsized: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface TextLayoutOptions {
|
|
9
|
+
width: number;
|
|
10
|
+
height?: number;
|
|
11
|
+
lineHeight: number;
|
|
12
|
+
wordWrap: boolean;
|
|
13
|
+
maxLines?: number;
|
|
14
|
+
overflow: "hidden" | "visible";
|
|
15
|
+
/** 单行宽度溢出时是否使用同一测量引擎省略。 */
|
|
16
|
+
ellipsis?: boolean;
|
|
17
|
+
}
|
|
18
|
+
export interface TextLayoutMetrics {
|
|
19
|
+
readonly lineCount: number;
|
|
20
|
+
readonly textHeight: number;
|
|
21
|
+
readonly naturalWidth: number;
|
|
22
|
+
readonly maxLineWidth: number;
|
|
23
|
+
readonly truncated: boolean;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Return the painted alphabetic baseline relative to the Canvas draw Y.
|
|
27
|
+
* The same shared Canvas context and font string also own Text width metrics.
|
|
28
|
+
*/
|
|
29
|
+
export declare function measureTextAlphabeticBaselineOffset(text: string, font: string, textBaseline: CanvasTextBaseline): number;
|
|
30
|
+
/**
|
|
31
|
+
* Measure one text run through the same engine used by Text and Span.
|
|
32
|
+
* `white-space: pre-wrap` keeps the caller's spaces and hard breaks intact.
|
|
33
|
+
*/
|
|
34
|
+
export declare function measureTextWidth(text: string, font: string, letterSpacing?: number): number;
|
|
35
|
+
/** Fit a single run and append an ellipsis only when it does not fit. */
|
|
36
|
+
export declare function fitTextWithEllipsis(text: string, maxWidth: number, font: string, letterSpacing?: number): string;
|
|
37
|
+
declare class PreparedTextLayout {
|
|
38
|
+
readonly text: string;
|
|
39
|
+
readonly font: string;
|
|
40
|
+
readonly letterSpacing: number;
|
|
41
|
+
readonly normalizedText: string;
|
|
42
|
+
readonly naturalWidth: number;
|
|
43
|
+
private readonly plainText;
|
|
44
|
+
private prepared;
|
|
45
|
+
constructor(text: string, font: string, letterSpacing: number);
|
|
46
|
+
measure(options: TextLayoutOptions): TextLayoutMetrics;
|
|
47
|
+
private getFirstLineRange;
|
|
48
|
+
layout(options: TextLayoutOptions): TextLayoutSnapshot;
|
|
49
|
+
private getRichPrepared;
|
|
50
|
+
}
|
|
51
|
+
type SnapshotLine = LayoutLine & {
|
|
52
|
+
ellipsized: boolean;
|
|
53
|
+
};
|
|
54
|
+
interface TextLineSourceRange {
|
|
55
|
+
readonly sourceStart: number;
|
|
56
|
+
readonly sourceEnd: number;
|
|
57
|
+
}
|
|
58
|
+
export declare class TextLayoutSnapshot {
|
|
59
|
+
readonly normalizedText: string;
|
|
60
|
+
private readonly font;
|
|
61
|
+
private readonly letterSpacing;
|
|
62
|
+
readonly lineHeight: number;
|
|
63
|
+
readonly naturalWidth: number;
|
|
64
|
+
readonly rawLineCount: number;
|
|
65
|
+
readonly truncated: boolean;
|
|
66
|
+
readonly maxLineWidth: number;
|
|
67
|
+
readonly textHeight: number;
|
|
68
|
+
private readonly lineCaretMetrics;
|
|
69
|
+
private readonly lineSourceRanges;
|
|
70
|
+
private readonly preparedSegments;
|
|
71
|
+
private readonly layoutLines;
|
|
72
|
+
private segmentStarts;
|
|
73
|
+
private segmentBoundaries;
|
|
74
|
+
private lineCharOffsetsCache;
|
|
75
|
+
constructor(normalizedText: string, font: string, letterSpacing: number, lineHeight: number, naturalWidth: number, preparedSegments: readonly string[], rawLineCount: number, truncated: boolean, lines: readonly SnapshotLine[], maxLineWidth: number);
|
|
76
|
+
get lines(): readonly TextLayoutLine[];
|
|
77
|
+
get lineCharOffsets(): readonly number[];
|
|
78
|
+
static empty(): TextLayoutSnapshot;
|
|
79
|
+
getLineSourceRange(index: number): TextLineSourceRange;
|
|
80
|
+
getLineIndex(offset: number): number;
|
|
81
|
+
getLineX(index: number, width: number, textAlign: string): number;
|
|
82
|
+
getBlockVerticalOffset(height: number, verticalAlign: string, contentHeight?: number): number;
|
|
83
|
+
getCaretLocalXY(caretIndex: number, width: number, height: number, textAlign: string, verticalAlign: string, fontSize: number): {
|
|
84
|
+
x: number;
|
|
85
|
+
y: number;
|
|
86
|
+
h: number;
|
|
87
|
+
};
|
|
88
|
+
hitTestCaret(lineIndex: number, localX: number, width: number, textAlign: string): number;
|
|
89
|
+
measureLineRange(index: number, start: number, end: number): {
|
|
90
|
+
x: number;
|
|
91
|
+
width: number;
|
|
92
|
+
};
|
|
93
|
+
private getCaretX;
|
|
94
|
+
private getBoundaryX;
|
|
95
|
+
private getLineCaretMetrics;
|
|
96
|
+
private cursorOffset;
|
|
97
|
+
private ensureSourceIndex;
|
|
98
|
+
}
|
|
99
|
+
export declare function prepareTextLayout(text: string, font: string, letterSpacing?: number): PreparedTextLayout;
|
|
100
|
+
export type PreparedTextLayoutHandle = PreparedTextLayout;
|
|
101
|
+
export {};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { type TextLayoutSnapshot } from "./layout";
|
|
2
|
+
import type { Text } from "./index";
|
|
3
|
+
import { type ResolvedTextStyle } from "./style";
|
|
4
|
+
import type { ShadowOption, TextStyleConfig } from "@fulate/core";
|
|
5
|
+
export declare function resolveColorFill(ctx: CanvasRenderingContext2D, color: ResolvedTextStyle["color"], w: number, h: number): string | CanvasGradient;
|
|
6
|
+
export declare function drawTextLine(ctx: CanvasRenderingContext2D, line: string, x: number, y: number, font: string, letterSpacing: number, doFill: boolean, doStroke: boolean): void;
|
|
7
|
+
export interface TextDecorationOptions {
|
|
8
|
+
x: number;
|
|
9
|
+
y: number;
|
|
10
|
+
width: number;
|
|
11
|
+
fontSize: number;
|
|
12
|
+
textBaseline: string;
|
|
13
|
+
underline?: boolean;
|
|
14
|
+
strikethrough?: boolean;
|
|
15
|
+
color: string;
|
|
16
|
+
thickness?: number;
|
|
17
|
+
}
|
|
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
|
+
export declare function applyTextShadow(ctx: CanvasRenderingContext2D, shadow: ShadowOption): void;
|
|
30
|
+
export declare function clearTextShadow(ctx: CanvasRenderingContext2D): void;
|
|
31
|
+
export interface TextRunMetrics {
|
|
32
|
+
/** CSS font shorthand used for measurement and later painting. */
|
|
33
|
+
readonly font: string;
|
|
34
|
+
/** Painted width; empty text is always `0`. */
|
|
35
|
+
readonly width: number;
|
|
36
|
+
/** Line-box height in logical pixels. */
|
|
37
|
+
readonly lineHeight: number;
|
|
38
|
+
/** Canvas draw Y relative to the run's line-box top. */
|
|
39
|
+
readonly drawY: number;
|
|
40
|
+
/** Alphabetic baseline relative to the run's line-box top. */
|
|
41
|
+
readonly alphabeticBaseline: number;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Measure one native Canvas text run with Fulate's resolved font and line-box
|
|
45
|
+
* semantics. This performs one `measureText()` call and writes the run's font,
|
|
46
|
+
* baseline, and letter spacing to the supplied context without saving or
|
|
47
|
+
* restoring it. Empty text measures a space for baseline data but reports zero
|
|
48
|
+
* width.
|
|
49
|
+
*/
|
|
50
|
+
export declare function measureTextRun(ctx: CanvasRenderingContext2D, text: string, style: Readonly<Required<TextStyleConfig>>, fontSize?: number, fontWeight?: string | number): TextRunMetrics;
|
|
51
|
+
export interface TextLineBoxMetrics {
|
|
52
|
+
readonly alphabeticBaseline: number;
|
|
53
|
+
readonly height: number;
|
|
54
|
+
}
|
|
55
|
+
/** Measure the line box produced by the existing Text painter. */
|
|
56
|
+
export declare function measureTextLineBoxMetrics(text: string, style: ResolvedTextStyle): TextLineBoxMetrics;
|
|
57
|
+
/** 绘制一份已排版文字块;Text 与内部组合共同使用。 */
|
|
58
|
+
export declare function paintTextBlock(ctx: CanvasRenderingContext2D, layout: TextLayoutSnapshot, style: ResolvedTextStyle, width: number, height: number): void;
|
|
59
|
+
export declare function paintTextContent(self: Text, ctx: CanvasRenderingContext2D, offsetY?: number): void;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { TextStyleConfig } from "@fulate/core";
|
|
2
|
+
export type { TextStyleConfig };
|
|
3
|
+
export type ResolvedTextStyle = Required<TextStyleConfig>;
|
|
4
|
+
export declare const TEXT_STYLE_DEFAULTS: Required<TextStyleConfig>;
|
|
5
|
+
export declare const TEXT_STYLE_KEYS: readonly TextStyleKey[];
|
|
6
|
+
export type TextStyleKey = keyof TextStyleConfig;
|
|
7
|
+
export declare function buildFontString(style: {
|
|
8
|
+
fontStyle?: string;
|
|
9
|
+
fontWeight?: string | number;
|
|
10
|
+
fontSize?: number;
|
|
11
|
+
fontFamily?: string;
|
|
12
|
+
}, fontSize?: number, fontWeight?: string | number): string;
|
|
13
|
+
export declare function resolveTextStyle(instance: Record<string, any>, defaults: Record<string, any>): ResolvedTextStyle;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type Edge, type PointView } from "@fulate/share";
|
|
2
|
+
import { type GeometrySnapshot } from "@fulate/core";
|
|
3
|
+
import { EditorShape, EditorShapeTransform } from "./editor/shape";
|
|
4
|
+
export declare class TriangleTransform<E extends Triangle = Triangle> extends EditorShapeTransform<E> {
|
|
5
|
+
protected buildVisualOutline(snapshot: GeometrySnapshot): PointView[];
|
|
6
|
+
protected buildGeometrySnapshot(revision: number, layoutReferenceBounds?: Readonly<import("@fulate/share").Rect>): GeometrySnapshot;
|
|
7
|
+
}
|
|
8
|
+
export declare class Triangle extends EditorShape {
|
|
9
|
+
readonly type = "triangle";
|
|
10
|
+
constructor(options?: {});
|
|
11
|
+
protected buildPath(ctx: CanvasRenderingContext2D): void;
|
|
12
|
+
getEdgeSegment(edge: Edge): {
|
|
13
|
+
start: PointView;
|
|
14
|
+
end: PointView;
|
|
15
|
+
};
|
|
16
|
+
protected getDefaultConnectionPortSchema(): {
|
|
17
|
+
id: string;
|
|
18
|
+
localPosition: (el: any) => any;
|
|
19
|
+
}[];
|
|
20
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { PointView } from "@fulate/share";
|
|
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
|
+
import { EditorShape, EditorShapeTransform } from "./editor/shape";
|
|
4
|
+
import { type EditorCapabilityToken, type PointControlDescriptor, type PointEditRequest, type PointEditingScope, type PointOwnerEdit } from "./editor/capability";
|
|
5
|
+
/** Sketch/SVG-style creation intent; commands never enter live Properties. */
|
|
6
|
+
export type VectorPathCommand = Readonly<{
|
|
7
|
+
type: "M" | "L";
|
|
8
|
+
point: PointView;
|
|
9
|
+
}> | Readonly<{
|
|
10
|
+
type: "C";
|
|
11
|
+
control1: PointView;
|
|
12
|
+
control2: PointView;
|
|
13
|
+
point: PointView;
|
|
14
|
+
}> | Readonly<{
|
|
15
|
+
type: "Z";
|
|
16
|
+
}>;
|
|
17
|
+
export interface VectorPathGeometry {
|
|
18
|
+
readonly subpaths: readonly AuthoredSubpath[];
|
|
19
|
+
}
|
|
20
|
+
export interface VectorPathOption extends ShapeOption<VectorPath> {
|
|
21
|
+
/** 普通创建传 commands;restore 直接传 canonical geometry。 */
|
|
22
|
+
path?: readonly VectorPathCommand[] | VectorPathGeometry;
|
|
23
|
+
}
|
|
24
|
+
type VectorPathPatch = ShapeOptionPatch<VectorPathOption>;
|
|
25
|
+
interface VectorPathStateOption extends Omit<VectorPathOption, "path"> {
|
|
26
|
+
path?: VectorPathGeometry;
|
|
27
|
+
}
|
|
28
|
+
export declare class VectorPathProperties extends ShapeProperties {
|
|
29
|
+
path: VectorPathGeometry;
|
|
30
|
+
constructor(options?: VectorPathStateOption);
|
|
31
|
+
getImpact(changes: PropertyChanges): ChangeImpact;
|
|
32
|
+
}
|
|
33
|
+
export declare class VectorPathTransform<E extends VectorPath = VectorPath> extends EditorShapeTransform<E> {
|
|
34
|
+
protected buildGeometrySnapshot(revision: number): GeometrySnapshot;
|
|
35
|
+
}
|
|
36
|
+
export declare class VectorPath extends EditorShape<VectorPathProperties, VectorPathOption> {
|
|
37
|
+
readonly type = "vectorPath";
|
|
38
|
+
get path(): VectorPathGeometry;
|
|
39
|
+
constructor(options?: VectorPathOption);
|
|
40
|
+
protected resolveOptions(options: VectorPathPatch): Partial<Omit<VectorPathOption, "key" | "children">>;
|
|
41
|
+
getEditorCapability<T>(token: EditorCapabilityToken<T>): T | undefined;
|
|
42
|
+
resolvePointScope(worldPoint: PointView): {
|
|
43
|
+
readonly kind: "owner";
|
|
44
|
+
};
|
|
45
|
+
hasPointScope(scope: PointEditingScope): scope is {
|
|
46
|
+
readonly kind: "owner";
|
|
47
|
+
};
|
|
48
|
+
getPointControls(_scope: PointEditingScope): readonly PointControlDescriptor[];
|
|
49
|
+
getPointScopeOutline(_scope: PointEditingScope): any;
|
|
50
|
+
resolvePointEdit(request: PointEditRequest): PointOwnerEdit | null;
|
|
51
|
+
private findAnchor;
|
|
52
|
+
private findSegment;
|
|
53
|
+
private ownerPathChange;
|
|
54
|
+
captureTransformBaseline(): ElementTransformBaseline<VectorPathGeometry>;
|
|
55
|
+
resolveTransformChange(baseline: Readonly<ElementTransformBaseline<VectorPathGeometry>>, intent: Readonly<TransformIntent | ReparentTransformIntent>): OwnerPropertyChange;
|
|
56
|
+
protected buildPath(ctx: CanvasRenderingContext2D): void;
|
|
57
|
+
}
|
|
58
|
+
export {};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export type WorkerSurfaceRequest<Initialize, Create, Message> = {
|
|
2
|
+
type: "initialize";
|
|
3
|
+
input: Initialize;
|
|
4
|
+
} | {
|
|
5
|
+
type: "create";
|
|
6
|
+
surfaceId: number;
|
|
7
|
+
input: Create;
|
|
8
|
+
} | {
|
|
9
|
+
type: "message";
|
|
10
|
+
surfaceId: number;
|
|
11
|
+
input: Message;
|
|
12
|
+
} | {
|
|
13
|
+
type: "destroy";
|
|
14
|
+
surfaceId: number;
|
|
15
|
+
};
|
|
16
|
+
export type WorkerSurfaceResponse<Output = never> = {
|
|
17
|
+
type: "ready";
|
|
18
|
+
} | {
|
|
19
|
+
type: "frame";
|
|
20
|
+
surfaceId: number;
|
|
21
|
+
bitmap: ImageBitmap;
|
|
22
|
+
} | {
|
|
23
|
+
type: "output";
|
|
24
|
+
surfaceId: number;
|
|
25
|
+
output: Output;
|
|
26
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface WorkerSurface<Message> {
|
|
2
|
+
receive(message: Message): void;
|
|
3
|
+
dispose(): void;
|
|
4
|
+
}
|
|
5
|
+
export interface WorkerSurfaceFactory<Initialize, Create, Message, Runtime, Output = never> {
|
|
6
|
+
initialize(input: Initialize): Runtime | Promise<Runtime>;
|
|
7
|
+
create(runtime: Runtime, input: Create, emitFrame: (bitmap: ImageBitmap) => void, emitOutput: (output: Output) => void): WorkerSurface<Message>;
|
|
8
|
+
}
|
|
9
|
+
/** Installs the shared typed lifecycle in one Worker realm. */
|
|
10
|
+
export declare function runWorkerSurfaceHost<Initialize, Create, Message, Runtime, Output = never>(factory: WorkerSurfaceFactory<Initialize, Create, Message, Runtime, Output>): void;
|
|
11
|
+
/** Coalesces renderer callbacks to one current transferable bitmap frame. */
|
|
12
|
+
export declare class BitmapFrameEmitter {
|
|
13
|
+
private readonly getCanvas;
|
|
14
|
+
private readonly emit;
|
|
15
|
+
private pending;
|
|
16
|
+
private queued;
|
|
17
|
+
private paused;
|
|
18
|
+
private active;
|
|
19
|
+
constructor(getCanvas: () => OffscreenCanvas, emit: (bitmap: ImageBitmap) => void);
|
|
20
|
+
request(): void;
|
|
21
|
+
pause(): void;
|
|
22
|
+
resume(): void;
|
|
23
|
+
dispose(): void;
|
|
24
|
+
private flush;
|
|
25
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
//#region packages/ui/src/worker-surface-worker.ts
|
|
2
|
+
/** Installs the shared typed lifecycle in one Worker realm. */
|
|
3
|
+
function runWorkerSurfaceHost(factory) {
|
|
4
|
+
const scope = self;
|
|
5
|
+
const surfaces = /* @__PURE__ */ new Map();
|
|
6
|
+
let runtime;
|
|
7
|
+
scope.onmessage = async (event) => {
|
|
8
|
+
const message = event.data;
|
|
9
|
+
switch (message.type) {
|
|
10
|
+
case "initialize":
|
|
11
|
+
runtime = await factory.initialize(message.input);
|
|
12
|
+
scope.postMessage({ type: "ready" });
|
|
13
|
+
break;
|
|
14
|
+
case "create": {
|
|
15
|
+
const { surfaceId } = message;
|
|
16
|
+
const entry = { surface: null };
|
|
17
|
+
surfaces.set(surfaceId, entry);
|
|
18
|
+
try {
|
|
19
|
+
entry.surface = factory.create(runtime, message.input, (bitmap) => {
|
|
20
|
+
if (surfaces.get(surfaceId) !== entry) {
|
|
21
|
+
bitmap.close();
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const response = {
|
|
25
|
+
type: "frame",
|
|
26
|
+
surfaceId,
|
|
27
|
+
bitmap
|
|
28
|
+
};
|
|
29
|
+
scope.postMessage(response, [bitmap]);
|
|
30
|
+
}, (output) => {
|
|
31
|
+
if (surfaces.get(surfaceId) !== entry) return;
|
|
32
|
+
const response = {
|
|
33
|
+
type: "output",
|
|
34
|
+
surfaceId,
|
|
35
|
+
output
|
|
36
|
+
};
|
|
37
|
+
scope.postMessage(response);
|
|
38
|
+
});
|
|
39
|
+
} catch (error) {
|
|
40
|
+
surfaces.delete(surfaceId);
|
|
41
|
+
throw error;
|
|
42
|
+
}
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
case "message":
|
|
46
|
+
surfaces.get(message.surfaceId).surface.receive(message.input);
|
|
47
|
+
break;
|
|
48
|
+
case "destroy": {
|
|
49
|
+
const entry = surfaces.get(message.surfaceId);
|
|
50
|
+
surfaces.delete(message.surfaceId);
|
|
51
|
+
entry.surface.dispose();
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/** Coalesces renderer callbacks to one current transferable bitmap frame. */
|
|
58
|
+
var BitmapFrameEmitter = class {
|
|
59
|
+
pending = null;
|
|
60
|
+
queued = false;
|
|
61
|
+
paused = false;
|
|
62
|
+
active = true;
|
|
63
|
+
constructor(getCanvas, emit) {
|
|
64
|
+
this.getCanvas = getCanvas;
|
|
65
|
+
this.emit = emit;
|
|
66
|
+
}
|
|
67
|
+
request() {
|
|
68
|
+
if (!this.active || this.paused) return;
|
|
69
|
+
this.queued = true;
|
|
70
|
+
this.flush();
|
|
71
|
+
}
|
|
72
|
+
pause() {
|
|
73
|
+
if (!this.active || this.paused) return;
|
|
74
|
+
this.paused = true;
|
|
75
|
+
this.queued = false;
|
|
76
|
+
}
|
|
77
|
+
resume() {
|
|
78
|
+
if (!this.active || !this.paused) return;
|
|
79
|
+
this.paused = false;
|
|
80
|
+
this.request();
|
|
81
|
+
}
|
|
82
|
+
dispose() {
|
|
83
|
+
this.active = false;
|
|
84
|
+
this.queued = false;
|
|
85
|
+
}
|
|
86
|
+
flush() {
|
|
87
|
+
if (!this.active || this.paused || this.pending || !this.queued) return;
|
|
88
|
+
this.queued = false;
|
|
89
|
+
const pending = createImageBitmap(this.getCanvas());
|
|
90
|
+
this.pending = pending;
|
|
91
|
+
pending.then((bitmap) => {
|
|
92
|
+
if (!this.active || this.paused || this.pending !== pending || this.queued) {
|
|
93
|
+
bitmap.close();
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
this.emit(bitmap);
|
|
98
|
+
} catch (error) {
|
|
99
|
+
bitmap.close();
|
|
100
|
+
throw error;
|
|
101
|
+
}
|
|
102
|
+
}).finally(() => {
|
|
103
|
+
if (this.pending === pending) this.pending = null;
|
|
104
|
+
if (this.active && !this.paused && this.queued) this.flush();
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
//#endregion
|
|
109
|
+
export { BitmapFrameEmitter, runWorkerSurfaceHost };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface WorkerSurfacePoolOptions<Initialize> {
|
|
2
|
+
readonly size: number;
|
|
3
|
+
readonly initialize: Initialize;
|
|
4
|
+
readonly createWorker: () => Worker;
|
|
5
|
+
}
|
|
6
|
+
export interface WorkerSurfaceHandle<Message> {
|
|
7
|
+
post(message: Message): void;
|
|
8
|
+
dispose(): void;
|
|
9
|
+
}
|
|
10
|
+
type FrameReceiver = (bitmap: ImageBitmap) => void;
|
|
11
|
+
type OutputReceiver<Output> = (output: Output) => void;
|
|
12
|
+
/** Pins long-lived bitmap surfaces to a fixed set of homogeneous Workers. */
|
|
13
|
+
export declare class WorkerSurfacePool<Initialize, Create, Message, Output = never> {
|
|
14
|
+
private slots;
|
|
15
|
+
private entries;
|
|
16
|
+
private nextSurfaceId;
|
|
17
|
+
constructor(options: WorkerSurfacePoolOptions<Initialize>);
|
|
18
|
+
create(input: Create, onFrame: FrameReceiver, onOutput?: OutputReceiver<Output>): WorkerSurfaceHandle<Message>;
|
|
19
|
+
dispose(): void;
|
|
20
|
+
private pickWorker;
|
|
21
|
+
private receive;
|
|
22
|
+
}
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { type ElementJSON, type Layer, type Root, type Viewport, type ViewportInsets } from "@fulate/core";
|
|
2
|
+
import { Image, ImageProperties, type ImageOption } from "./image";
|
|
3
|
+
/** @internal 当前 Root 中为 Workspace 自动聚焦提供屏幕边距的唯一 owner。 */
|
|
4
|
+
export type WorkspaceFocusInsetsSource = () => number | ViewportInsets;
|
|
5
|
+
/** @internal Workspace 与高层 screen-space overlay 共享的 Root context key。 */
|
|
6
|
+
export declare const workspaceFocusInsetsKey: unique symbol;
|
|
7
|
+
/** Workspace 只接受自身配置,不接受任何 children。 */
|
|
8
|
+
export interface WorkspaceOption extends Omit<ImageOption<Workspace>, "children" | "image" | "resize"> {
|
|
9
|
+
/** 首次进入 Root 时是否在首个 Canvas paint 前无动画聚焦,默认 `true`。 */
|
|
10
|
+
fit?: boolean;
|
|
11
|
+
/** Root 尺寸变化后是否无动画重新聚焦工作区,默认 `true`。 */
|
|
12
|
+
fitOnResize?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export type WorkspaceJSON = Omit<ElementJSON<WorkspaceOption>, "children">;
|
|
15
|
+
/** Workspace 的自有 authored 属性。 */
|
|
16
|
+
export declare class WorkspaceProperties extends ImageProperties {
|
|
17
|
+
/** 首次进入 Root 时是否在首个 Canvas paint 前执行一次无动画聚焦。 */
|
|
18
|
+
fit: boolean;
|
|
19
|
+
/** Root 尺寸变化后是否执行无动画聚焦。 */
|
|
20
|
+
fitOnResize: boolean;
|
|
21
|
+
constructor(options?: WorkspaceOption);
|
|
22
|
+
}
|
|
23
|
+
/** Options forwarded unchanged to the owning Root viewport. */
|
|
24
|
+
export type WorkspaceFitOptions = NonNullable<Parameters<Viewport["focus"]>[1]>;
|
|
25
|
+
/** Fits one active Workspace into its owning Root viewport. */
|
|
26
|
+
export declare function fitWorkspace(workspace: Workspace, options?: WorkspaceFitOptions): Promise<void>;
|
|
27
|
+
/** Workspace 是不可选择的背景板,支持颜色、图片及无动画的首次与 resize fit。 */
|
|
28
|
+
export declare class Workspace extends Image<WorkspaceProperties, WorkspaceOption> {
|
|
29
|
+
readonly type = "workspace";
|
|
30
|
+
private didFocus;
|
|
31
|
+
private resizeRequested;
|
|
32
|
+
private pendingResizeFit;
|
|
33
|
+
private removeInitialFitObserver;
|
|
34
|
+
private removeResizeListener;
|
|
35
|
+
constructor(options?: WorkspaceOption);
|
|
36
|
+
get fit(): boolean;
|
|
37
|
+
get fitOnResize(): boolean;
|
|
38
|
+
/** Workspace 只导出自己的配置,不导出任何子节点。 */
|
|
39
|
+
toJSON(): WorkspaceJSON;
|
|
40
|
+
protected onDeactivate(root: Root | null, layer: Layer | null): void;
|
|
41
|
+
protected onActivate(root: Root | null, layer: Layer | null): void;
|
|
42
|
+
private focusInsets;
|
|
43
|
+
private scheduleResizeFit;
|
|
44
|
+
}
|