@twick/2d 0.13.0 → 0.14.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. package/LICENSE +21 -0
  2. package/editor/editor/tsconfig.build.tsbuildinfo +1 -1
  3. package/lib/components/Audio.js +3 -3
  4. package/lib/components/Img.js +23 -23
  5. package/lib/components/Line.js +31 -31
  6. package/lib/components/Media.d.ts +1 -1
  7. package/lib/components/Media.d.ts.map +1 -1
  8. package/lib/components/Media.js +26 -26
  9. package/lib/components/Spline.js +25 -25
  10. package/lib/components/Video.js +3 -3
  11. package/lib/tsconfig.build.tsbuildinfo +1 -1
  12. package/package.json +5 -4
  13. package/src/editor/NodeInspectorConfig.tsx +76 -76
  14. package/src/editor/PreviewOverlayConfig.tsx +67 -67
  15. package/src/editor/Provider.tsx +93 -93
  16. package/src/editor/SceneGraphTabConfig.tsx +81 -81
  17. package/src/editor/icons/CircleIcon.tsx +7 -7
  18. package/src/editor/icons/CodeBlockIcon.tsx +8 -8
  19. package/src/editor/icons/CurveIcon.tsx +7 -7
  20. package/src/editor/icons/GridIcon.tsx +7 -7
  21. package/src/editor/icons/IconMap.ts +35 -35
  22. package/src/editor/icons/ImgIcon.tsx +8 -8
  23. package/src/editor/icons/LayoutIcon.tsx +9 -9
  24. package/src/editor/icons/LineIcon.tsx +7 -7
  25. package/src/editor/icons/NodeIcon.tsx +7 -7
  26. package/src/editor/icons/RayIcon.tsx +7 -7
  27. package/src/editor/icons/RectIcon.tsx +7 -7
  28. package/src/editor/icons/ShapeIcon.tsx +7 -7
  29. package/src/editor/icons/TxtIcon.tsx +8 -8
  30. package/src/editor/icons/VideoIcon.tsx +7 -7
  31. package/src/editor/icons/View2DIcon.tsx +10 -10
  32. package/src/editor/index.ts +17 -17
  33. package/src/editor/tree/DetachedRoot.tsx +23 -23
  34. package/src/editor/tree/NodeElement.tsx +74 -74
  35. package/src/editor/tree/TreeElement.tsx +72 -72
  36. package/src/editor/tree/TreeRoot.tsx +10 -10
  37. package/src/editor/tree/ViewRoot.tsx +20 -20
  38. package/src/editor/tree/index.module.scss +38 -38
  39. package/src/editor/tree/index.ts +3 -3
  40. package/src/editor/tsconfig.build.json +5 -5
  41. package/src/editor/tsconfig.json +12 -12
  42. package/src/editor/tsdoc.json +4 -4
  43. package/src/editor/vite-env.d.ts +1 -1
  44. package/src/lib/code/CodeCursor.ts +445 -445
  45. package/src/lib/code/CodeDiffer.ts +78 -78
  46. package/src/lib/code/CodeFragment.ts +97 -97
  47. package/src/lib/code/CodeHighlighter.ts +75 -75
  48. package/src/lib/code/CodeMetrics.ts +47 -47
  49. package/src/lib/code/CodeRange.test.ts +74 -74
  50. package/src/lib/code/CodeRange.ts +216 -216
  51. package/src/lib/code/CodeScope.ts +101 -101
  52. package/src/lib/code/CodeSelection.ts +24 -24
  53. package/src/lib/code/CodeSignal.ts +327 -327
  54. package/src/lib/code/CodeTokenizer.ts +54 -54
  55. package/src/lib/code/DefaultHighlightStyle.ts +98 -98
  56. package/src/lib/code/LezerHighlighter.ts +113 -113
  57. package/src/lib/code/diff.test.ts +311 -311
  58. package/src/lib/code/diff.ts +319 -319
  59. package/src/lib/code/extractRange.ts +126 -126
  60. package/src/lib/code/index.ts +13 -13
  61. package/src/lib/components/Audio.ts +168 -168
  62. package/src/lib/components/Bezier.ts +105 -105
  63. package/src/lib/components/Circle.ts +266 -266
  64. package/src/lib/components/Code.ts +526 -526
  65. package/src/lib/components/CodeBlock.ts +576 -576
  66. package/src/lib/components/CubicBezier.ts +112 -112
  67. package/src/lib/components/Curve.ts +455 -455
  68. package/src/lib/components/Grid.ts +135 -135
  69. package/src/lib/components/Icon.ts +96 -96
  70. package/src/lib/components/Img.ts +319 -319
  71. package/src/lib/components/Knot.ts +157 -157
  72. package/src/lib/components/Latex.ts +122 -122
  73. package/src/lib/components/Layout.ts +1092 -1092
  74. package/src/lib/components/Line.ts +429 -429
  75. package/src/lib/components/Media.ts +576 -576
  76. package/src/lib/components/Node.ts +1940 -1940
  77. package/src/lib/components/Path.ts +137 -137
  78. package/src/lib/components/Polygon.ts +171 -171
  79. package/src/lib/components/QuadBezier.ts +100 -100
  80. package/src/lib/components/Ray.ts +125 -125
  81. package/src/lib/components/Rect.ts +187 -187
  82. package/src/lib/components/Rive.ts +156 -156
  83. package/src/lib/components/SVG.ts +797 -797
  84. package/src/lib/components/Shape.ts +143 -143
  85. package/src/lib/components/Spline.ts +344 -344
  86. package/src/lib/components/Txt.test.tsx +81 -81
  87. package/src/lib/components/Txt.ts +203 -203
  88. package/src/lib/components/TxtLeaf.ts +205 -205
  89. package/src/lib/components/Video.ts +461 -461
  90. package/src/lib/components/View2D.ts +98 -98
  91. package/src/lib/components/__tests__/children.test.tsx +142 -142
  92. package/src/lib/components/__tests__/clone.test.tsx +126 -126
  93. package/src/lib/components/__tests__/generatorTest.ts +28 -28
  94. package/src/lib/components/__tests__/mockScene2D.ts +45 -45
  95. package/src/lib/components/__tests__/query.test.tsx +122 -122
  96. package/src/lib/components/__tests__/state.test.tsx +60 -60
  97. package/src/lib/components/index.ts +28 -28
  98. package/src/lib/components/types.ts +35 -35
  99. package/src/lib/curves/ArcSegment.ts +159 -159
  100. package/src/lib/curves/CircleSegment.ts +77 -77
  101. package/src/lib/curves/CubicBezierSegment.ts +78 -78
  102. package/src/lib/curves/CurveDrawingInfo.ts +11 -11
  103. package/src/lib/curves/CurvePoint.ts +15 -15
  104. package/src/lib/curves/CurveProfile.ts +7 -7
  105. package/src/lib/curves/KnotInfo.ts +10 -10
  106. package/src/lib/curves/LineSegment.ts +62 -62
  107. package/src/lib/curves/Polynomial.ts +355 -355
  108. package/src/lib/curves/Polynomial2D.ts +62 -62
  109. package/src/lib/curves/PolynomialSegment.ts +124 -124
  110. package/src/lib/curves/QuadBezierSegment.ts +64 -64
  111. package/src/lib/curves/Segment.ts +17 -17
  112. package/src/lib/curves/UniformPolynomialCurveSampler.ts +94 -94
  113. package/src/lib/curves/createCurveProfileLerp.ts +471 -471
  114. package/src/lib/curves/getBezierSplineProfile.ts +223 -223
  115. package/src/lib/curves/getCircleProfile.ts +86 -86
  116. package/src/lib/curves/getPathProfile.ts +178 -178
  117. package/src/lib/curves/getPointAtDistance.ts +21 -21
  118. package/src/lib/curves/getPolylineProfile.test.ts +21 -21
  119. package/src/lib/curves/getPolylineProfile.ts +89 -89
  120. package/src/lib/curves/getRectProfile.ts +139 -139
  121. package/src/lib/curves/index.ts +16 -16
  122. package/src/lib/decorators/canvasStyleSignal.ts +16 -16
  123. package/src/lib/decorators/colorSignal.ts +9 -9
  124. package/src/lib/decorators/compound.ts +72 -72
  125. package/src/lib/decorators/computed.ts +18 -18
  126. package/src/lib/decorators/defaultStyle.ts +18 -18
  127. package/src/lib/decorators/filtersSignal.ts +136 -136
  128. package/src/lib/decorators/index.ts +10 -10
  129. package/src/lib/decorators/initializers.ts +32 -32
  130. package/src/lib/decorators/nodeName.ts +13 -13
  131. package/src/lib/decorators/signal.test.ts +90 -90
  132. package/src/lib/decorators/signal.ts +345 -345
  133. package/src/lib/decorators/spacingSignal.ts +15 -15
  134. package/src/lib/decorators/vector2Signal.ts +30 -30
  135. package/src/lib/globals.d.ts +2 -2
  136. package/src/lib/index.ts +8 -8
  137. package/src/lib/jsx-dev-runtime.ts +2 -2
  138. package/src/lib/jsx-runtime.ts +46 -46
  139. package/src/lib/parse-svg-path.d.ts +14 -14
  140. package/src/lib/partials/Filter.ts +180 -180
  141. package/src/lib/partials/Gradient.ts +102 -102
  142. package/src/lib/partials/Pattern.ts +34 -34
  143. package/src/lib/partials/ShaderConfig.ts +117 -117
  144. package/src/lib/partials/index.ts +4 -4
  145. package/src/lib/partials/types.ts +58 -58
  146. package/src/lib/scenes/Scene2D.ts +242 -242
  147. package/src/lib/scenes/index.ts +3 -3
  148. package/src/lib/scenes/makeScene2D.ts +16 -16
  149. package/src/lib/scenes/useScene2D.ts +6 -6
  150. package/src/lib/tsconfig.build.json +5 -5
  151. package/src/lib/tsconfig.json +10 -10
  152. package/src/lib/tsdoc.json +4 -4
  153. package/src/lib/utils/CanvasUtils.ts +306 -306
  154. package/src/lib/utils/diff.test.ts +453 -453
  155. package/src/lib/utils/diff.ts +148 -148
  156. package/src/lib/utils/index.ts +2 -2
  157. package/src/lib/utils/is.ts +11 -11
  158. package/src/lib/utils/makeSignalExtensions.ts +30 -30
  159. package/src/lib/utils/video/declarations.d.ts +1 -1
  160. package/src/lib/utils/video/ffmpeg-client.ts +50 -50
  161. package/src/lib/utils/video/mp4-parser-manager.ts +72 -72
  162. package/src/lib/utils/video/parser/index.ts +1 -1
  163. package/src/lib/utils/video/parser/parser.ts +257 -257
  164. package/src/lib/utils/video/parser/sampler.ts +72 -72
  165. package/src/lib/utils/video/parser/segment.ts +302 -302
  166. package/src/lib/utils/video/parser/sink.ts +29 -29
  167. package/src/lib/utils/video/parser/utils.ts +31 -31
  168. package/src/tsconfig.base.json +19 -19
  169. package/src/tsconfig.build.json +8 -8
  170. package/src/tsconfig.json +5 -5
  171. package/tsconfig.project.json +7 -7
@@ -1,157 +1,157 @@
1
- import type {
2
- PossibleVector2,
3
- Signal,
4
- SignalValue,
5
- Vector2Signal,
6
- } from '@twick/core';
7
- import {transformVectorAsPoint, Vector2} from '@twick/core';
8
- import type {KnotInfo} from '../curves';
9
- import {
10
- cloneable,
11
- compound,
12
- computed,
13
- initial,
14
- parser,
15
- signal,
16
- wrapper,
17
- } from '../decorators';
18
- import type {NodeProps} from './Node';
19
- import {Node} from './Node';
20
-
21
- export interface KnotProps extends NodeProps {
22
- /**
23
- * {@inheritDoc Knot.startHandle}
24
- */
25
- startHandle?: SignalValue<PossibleVector2>;
26
- /**
27
- * {@inheritDoc Knot.endHandle}
28
- */
29
- endHandle?: SignalValue<PossibleVector2>;
30
- /**
31
- * {@inheritDoc Knot.auto}
32
- */
33
- auto?: SignalValue<PossibleKnotAuto>;
34
- startHandleAuto?: SignalValue<number>;
35
- endHandleAuto?: SignalValue<number>;
36
- }
37
-
38
- export type KnotAuto = {startHandle: number; endHandle: number};
39
- export type PossibleKnotAuto = KnotAuto | number | [number, number];
40
- export type KnotAutoSignal<TOwner> = Signal<
41
- PossibleKnotAuto,
42
- KnotAuto,
43
- TOwner
44
- > & {
45
- endHandle: Signal<number, number, TOwner>;
46
- startHandle: Signal<number, number, TOwner>;
47
- };
48
-
49
- /**
50
- * A node representing a knot of a {@link Spline}.
51
- */
52
- export class Knot extends Node {
53
- /**
54
- * The position of the knot's start handle. The position is provided relative
55
- * to the knot's position.
56
- *
57
- * @remarks
58
- * By default, the position of the start handle will be the mirrored position
59
- * of the {@link endHandle}.
60
- *
61
- * If neither an end handle nor a start handle is provided, the positions of
62
- * the handles gets calculated automatically to create smooth curve through
63
- * the knot. The smoothness of the resulting curve can be controlled via the
64
- * {@link Spline.smoothness} property.
65
- *
66
- * It is also possible to blend between a user-defined position and the
67
- * auto-calculated position by using the {@link auto} property.
68
- *
69
- * @defaultValue Mirrored position of the endHandle.
70
- */
71
- @wrapper(Vector2)
72
- @signal()
73
- public declare readonly startHandle: Vector2Signal<this>;
74
-
75
- /**
76
- * The position of the knot's end handle. The position is provided relative
77
- * to the knot's position.
78
- *
79
- * @remarks
80
- * By default, the position of the end handle will be the mirrored position
81
- * of the {@link startHandle}.
82
- *
83
- * If neither an end handle nor a start handle is provided, the positions of
84
- * the handles gets calculated automatically to create smooth curve through
85
- * the knot. The smoothness of the resulting curve can be controlled via the
86
- * {@link Spline.smoothness} property.
87
- *
88
- * It is also possible to blend between a user-defined position and the
89
- * auto-calculated position by using the {@link auto} property.
90
- *
91
- * @defaultValue Mirrored position of the startHandle.
92
- */
93
- @wrapper(Vector2)
94
- @signal()
95
- public declare readonly endHandle: Vector2Signal<this>;
96
-
97
- /**
98
- * How much to blend between the user-provided handles and the auto-calculated
99
- * handles.
100
- *
101
- * @remarks
102
- * This property has no effect if no explicit handles are provided for the
103
- * knot.
104
- *
105
- * @defaultValue 0
106
- */
107
- @cloneable(false)
108
- @initial(() => ({startHandle: 0, endHandle: 0}))
109
- @parser((value: PossibleKnotAuto) => {
110
- if (typeof value === 'object' && !Array.isArray(value)) {
111
- return value;
112
- }
113
- if (typeof value === 'number') {
114
- value = [value, value];
115
- }
116
- return {startHandle: value[0], endHandle: value[1]};
117
- })
118
- @compound({startHandle: 'startHandleAuto', endHandle: 'endHandleAuto'})
119
- public declare readonly auto: KnotAutoSignal<this>;
120
- public get startHandleAuto() {
121
- return this.auto.startHandle;
122
- }
123
- public get endHandleAuto() {
124
- return this.auto.endHandle;
125
- }
126
-
127
- public constructor(props: KnotProps) {
128
- super(
129
- props.startHandle === undefined && props.endHandle === undefined
130
- ? {auto: 1, ...props}
131
- : props,
132
- );
133
- }
134
-
135
- @computed()
136
- public points(): KnotInfo {
137
- const hasExplicitHandles =
138
- !this.startHandle.isInitial() || !this.endHandle.isInitial();
139
- const startHandle = hasExplicitHandles ? this.startHandle() : Vector2.zero;
140
- const endHandle = hasExplicitHandles ? this.endHandle() : Vector2.zero;
141
-
142
- return {
143
- position: this.position(),
144
- startHandle: transformVectorAsPoint(startHandle, this.localToParent()),
145
- endHandle: transformVectorAsPoint(endHandle, this.localToParent()),
146
- auto: {start: this.startHandleAuto(), end: this.endHandleAuto()},
147
- };
148
- }
149
-
150
- private getDefaultEndHandle() {
151
- return this.startHandle().flipped;
152
- }
153
-
154
- private getDefaultStartHandle() {
155
- return this.endHandle().flipped;
156
- }
157
- }
1
+ import type {
2
+ PossibleVector2,
3
+ Signal,
4
+ SignalValue,
5
+ Vector2Signal,
6
+ } from '@twick/core';
7
+ import {transformVectorAsPoint, Vector2} from '@twick/core';
8
+ import type {KnotInfo} from '../curves';
9
+ import {
10
+ cloneable,
11
+ compound,
12
+ computed,
13
+ initial,
14
+ parser,
15
+ signal,
16
+ wrapper,
17
+ } from '../decorators';
18
+ import type {NodeProps} from './Node';
19
+ import {Node} from './Node';
20
+
21
+ export interface KnotProps extends NodeProps {
22
+ /**
23
+ * {@inheritDoc Knot.startHandle}
24
+ */
25
+ startHandle?: SignalValue<PossibleVector2>;
26
+ /**
27
+ * {@inheritDoc Knot.endHandle}
28
+ */
29
+ endHandle?: SignalValue<PossibleVector2>;
30
+ /**
31
+ * {@inheritDoc Knot.auto}
32
+ */
33
+ auto?: SignalValue<PossibleKnotAuto>;
34
+ startHandleAuto?: SignalValue<number>;
35
+ endHandleAuto?: SignalValue<number>;
36
+ }
37
+
38
+ export type KnotAuto = {startHandle: number; endHandle: number};
39
+ export type PossibleKnotAuto = KnotAuto | number | [number, number];
40
+ export type KnotAutoSignal<TOwner> = Signal<
41
+ PossibleKnotAuto,
42
+ KnotAuto,
43
+ TOwner
44
+ > & {
45
+ endHandle: Signal<number, number, TOwner>;
46
+ startHandle: Signal<number, number, TOwner>;
47
+ };
48
+
49
+ /**
50
+ * A node representing a knot of a {@link Spline}.
51
+ */
52
+ export class Knot extends Node {
53
+ /**
54
+ * The position of the knot's start handle. The position is provided relative
55
+ * to the knot's position.
56
+ *
57
+ * @remarks
58
+ * By default, the position of the start handle will be the mirrored position
59
+ * of the {@link endHandle}.
60
+ *
61
+ * If neither an end handle nor a start handle is provided, the positions of
62
+ * the handles gets calculated automatically to create smooth curve through
63
+ * the knot. The smoothness of the resulting curve can be controlled via the
64
+ * {@link Spline.smoothness} property.
65
+ *
66
+ * It is also possible to blend between a user-defined position and the
67
+ * auto-calculated position by using the {@link auto} property.
68
+ *
69
+ * @defaultValue Mirrored position of the endHandle.
70
+ */
71
+ @wrapper(Vector2)
72
+ @signal()
73
+ public declare readonly startHandle: Vector2Signal<this>;
74
+
75
+ /**
76
+ * The position of the knot's end handle. The position is provided relative
77
+ * to the knot's position.
78
+ *
79
+ * @remarks
80
+ * By default, the position of the end handle will be the mirrored position
81
+ * of the {@link startHandle}.
82
+ *
83
+ * If neither an end handle nor a start handle is provided, the positions of
84
+ * the handles gets calculated automatically to create smooth curve through
85
+ * the knot. The smoothness of the resulting curve can be controlled via the
86
+ * {@link Spline.smoothness} property.
87
+ *
88
+ * It is also possible to blend between a user-defined position and the
89
+ * auto-calculated position by using the {@link auto} property.
90
+ *
91
+ * @defaultValue Mirrored position of the startHandle.
92
+ */
93
+ @wrapper(Vector2)
94
+ @signal()
95
+ public declare readonly endHandle: Vector2Signal<this>;
96
+
97
+ /**
98
+ * How much to blend between the user-provided handles and the auto-calculated
99
+ * handles.
100
+ *
101
+ * @remarks
102
+ * This property has no effect if no explicit handles are provided for the
103
+ * knot.
104
+ *
105
+ * @defaultValue 0
106
+ */
107
+ @cloneable(false)
108
+ @initial(() => ({startHandle: 0, endHandle: 0}))
109
+ @parser((value: PossibleKnotAuto) => {
110
+ if (typeof value === 'object' && !Array.isArray(value)) {
111
+ return value;
112
+ }
113
+ if (typeof value === 'number') {
114
+ value = [value, value];
115
+ }
116
+ return {startHandle: value[0], endHandle: value[1]};
117
+ })
118
+ @compound({startHandle: 'startHandleAuto', endHandle: 'endHandleAuto'})
119
+ public declare readonly auto: KnotAutoSignal<this>;
120
+ public get startHandleAuto() {
121
+ return this.auto.startHandle;
122
+ }
123
+ public get endHandleAuto() {
124
+ return this.auto.endHandle;
125
+ }
126
+
127
+ public constructor(props: KnotProps) {
128
+ super(
129
+ props.startHandle === undefined && props.endHandle === undefined
130
+ ? {auto: 1, ...props}
131
+ : props,
132
+ );
133
+ }
134
+
135
+ @computed()
136
+ public points(): KnotInfo {
137
+ const hasExplicitHandles =
138
+ !this.startHandle.isInitial() || !this.endHandle.isInitial();
139
+ const startHandle = hasExplicitHandles ? this.startHandle() : Vector2.zero;
140
+ const endHandle = hasExplicitHandles ? this.endHandle() : Vector2.zero;
141
+
142
+ return {
143
+ position: this.position(),
144
+ startHandle: transformVectorAsPoint(startHandle, this.localToParent()),
145
+ endHandle: transformVectorAsPoint(endHandle, this.localToParent()),
146
+ auto: {start: this.startHandleAuto(), end: this.endHandleAuto()},
147
+ };
148
+ }
149
+
150
+ private getDefaultEndHandle() {
151
+ return this.startHandle().flipped;
152
+ }
153
+
154
+ private getDefaultStartHandle() {
155
+ return this.endHandle().flipped;
156
+ }
157
+ }
@@ -1,122 +1,122 @@
1
- import type {SignalValue, SimpleSignal} from '@twick/core';
2
- import {DependencyContext, useLogger} from '@twick/core';
3
- import type {LiteAdaptor} from 'mathjax-full/js/adaptors/liteAdaptor';
4
- import {liteAdaptor} from 'mathjax-full/js/adaptors/liteAdaptor';
5
- import type {MathDocument} from 'mathjax-full/js/core/MathDocument';
6
- import {RegisterHTMLHandler} from 'mathjax-full/js/handlers/html';
7
- import {TeX} from 'mathjax-full/js/input/tex';
8
- import {AllPackages} from 'mathjax-full/js/input/tex/AllPackages';
9
- import {mathjax} from 'mathjax-full/js/mathjax';
10
- import {SVG} from 'mathjax-full/js/output/svg';
11
- import type {OptionList} from 'mathjax-full/js/util/Options';
12
- import {initial, signal} from '../decorators';
13
- import type {ImgProps} from './Img';
14
- import {Img} from './Img';
15
-
16
- export interface LatexProps extends ImgProps {
17
- tex?: SignalValue<string>;
18
- renderProps?: SignalValue<OptionList>;
19
- }
20
-
21
- /**
22
- * A node for rendering equations with LaTeX.
23
- *
24
- * @preview
25
- * ```tsx editor
26
- * import {Latex, makeScene2D} from '@twick/2d';
27
- *
28
- * export default makeScene2D(function* (view) {
29
- * view.add(
30
- * <Latex
31
- * // Note how this uses \color to set the color.
32
- * tex="{\color{white} ax^2+bx+c=0 \implies x=\frac{-b \pm \sqrt{b^2-4ac}}{2a}}"
33
- * width={600} // height and width can calculate based on each other
34
- * />,
35
- * );
36
- * });
37
- * ```
38
- */
39
- export class Latex extends Img {
40
- private static svgContentsPool: Record<string, string> = {};
41
- private static mathJaxInitialized = false;
42
- private static adaptor: LiteAdaptor;
43
- private static jaxDocument: MathDocument<unknown, unknown, unknown>;
44
-
45
- private static initializeMathJax() {
46
- if (this.mathJaxInitialized) {
47
- return;
48
- }
49
- this.adaptor = liteAdaptor();
50
- RegisterHTMLHandler(this.adaptor);
51
- this.jaxDocument = mathjax.document('', {
52
- // eslint-disable-next-line @typescript-eslint/naming-convention
53
- InputJax: new TeX({packages: AllPackages}),
54
- // eslint-disable-next-line @typescript-eslint/naming-convention
55
- OutputJax: new SVG({fontCache: 'local'}),
56
- });
57
- this.mathJaxInitialized = true;
58
- }
59
-
60
- private readonly imageElement = document.createElement('img');
61
-
62
- @initial({})
63
- @signal()
64
- public declare readonly options: SimpleSignal<OptionList, this>;
65
-
66
- @signal()
67
- public declare readonly tex: SimpleSignal<string, this>;
68
-
69
- public constructor(props: LatexProps) {
70
- super({...props, src: null});
71
- Latex.initializeMathJax();
72
- }
73
-
74
- protected override image(): HTMLImageElement {
75
- // Render props may change the look of the TeX, so we need to cache both
76
- // source and render props together.
77
- const src = `${this.tex()}::${JSON.stringify(this.options())}`;
78
- if (Latex.svgContentsPool[src]) {
79
- this.imageElement.src = Latex.svgContentsPool[src];
80
- if (!this.imageElement.complete) {
81
- DependencyContext.collectPromise(
82
- new Promise((resolve, reject) => {
83
- this.imageElement.addEventListener('load', resolve);
84
- this.imageElement.addEventListener('error', reject);
85
- }),
86
- );
87
- }
88
- return this.imageElement;
89
- }
90
-
91
- // Convert to TeX, look for any errors
92
- const tex = this.tex();
93
- const svg = Latex.adaptor.innerHTML(
94
- Latex.jaxDocument.convert(tex, this.options()) as any,
95
- );
96
- if (svg.includes('data-mjx-error')) {
97
- const errors = svg.match(/data-mjx-error="(.*?)"/);
98
- if (errors && errors.length > 0) {
99
- useLogger().error(`Invalid MathJax: ${errors[1]}`);
100
- }
101
- }
102
-
103
- // Encode to raw base64 image format
104
- const text = `data:image/svg+xml;base64,${btoa(
105
- `<?xml version="1.0" encoding="UTF-8" standalone="no" ?>\n${svg}`,
106
- )}`;
107
- Latex.svgContentsPool[src] = text;
108
- const image = document.createElement('img');
109
- image.src = text;
110
- image.src = text;
111
- if (!image.complete) {
112
- DependencyContext.collectPromise(
113
- new Promise((resolve, reject) => {
114
- image.addEventListener('load', resolve);
115
- image.addEventListener('error', reject);
116
- }),
117
- );
118
- }
119
-
120
- return image;
121
- }
122
- }
1
+ import type {SignalValue, SimpleSignal} from '@twick/core';
2
+ import {DependencyContext, useLogger} from '@twick/core';
3
+ import type {LiteAdaptor} from 'mathjax-full/js/adaptors/liteAdaptor';
4
+ import {liteAdaptor} from 'mathjax-full/js/adaptors/liteAdaptor';
5
+ import type {MathDocument} from 'mathjax-full/js/core/MathDocument';
6
+ import {RegisterHTMLHandler} from 'mathjax-full/js/handlers/html';
7
+ import {TeX} from 'mathjax-full/js/input/tex';
8
+ import {AllPackages} from 'mathjax-full/js/input/tex/AllPackages';
9
+ import {mathjax} from 'mathjax-full/js/mathjax';
10
+ import {SVG} from 'mathjax-full/js/output/svg';
11
+ import type {OptionList} from 'mathjax-full/js/util/Options';
12
+ import {initial, signal} from '../decorators';
13
+ import type {ImgProps} from './Img';
14
+ import {Img} from './Img';
15
+
16
+ export interface LatexProps extends ImgProps {
17
+ tex?: SignalValue<string>;
18
+ renderProps?: SignalValue<OptionList>;
19
+ }
20
+
21
+ /**
22
+ * A node for rendering equations with LaTeX.
23
+ *
24
+ * @preview
25
+ * ```tsx editor
26
+ * import {Latex, makeScene2D} from '@twick/2d';
27
+ *
28
+ * export default makeScene2D(function* (view) {
29
+ * view.add(
30
+ * <Latex
31
+ * // Note how this uses \color to set the color.
32
+ * tex="{\color{white} ax^2+bx+c=0 \implies x=\frac{-b \pm \sqrt{b^2-4ac}}{2a}}"
33
+ * width={600} // height and width can calculate based on each other
34
+ * />,
35
+ * );
36
+ * });
37
+ * ```
38
+ */
39
+ export class Latex extends Img {
40
+ private static svgContentsPool: Record<string, string> = {};
41
+ private static mathJaxInitialized = false;
42
+ private static adaptor: LiteAdaptor;
43
+ private static jaxDocument: MathDocument<unknown, unknown, unknown>;
44
+
45
+ private static initializeMathJax() {
46
+ if (this.mathJaxInitialized) {
47
+ return;
48
+ }
49
+ this.adaptor = liteAdaptor();
50
+ RegisterHTMLHandler(this.adaptor);
51
+ this.jaxDocument = mathjax.document('', {
52
+ // eslint-disable-next-line @typescript-eslint/naming-convention
53
+ InputJax: new TeX({packages: AllPackages}),
54
+ // eslint-disable-next-line @typescript-eslint/naming-convention
55
+ OutputJax: new SVG({fontCache: 'local'}),
56
+ });
57
+ this.mathJaxInitialized = true;
58
+ }
59
+
60
+ private readonly imageElement = document.createElement('img');
61
+
62
+ @initial({})
63
+ @signal()
64
+ public declare readonly options: SimpleSignal<OptionList, this>;
65
+
66
+ @signal()
67
+ public declare readonly tex: SimpleSignal<string, this>;
68
+
69
+ public constructor(props: LatexProps) {
70
+ super({...props, src: null});
71
+ Latex.initializeMathJax();
72
+ }
73
+
74
+ protected override image(): HTMLImageElement {
75
+ // Render props may change the look of the TeX, so we need to cache both
76
+ // source and render props together.
77
+ const src = `${this.tex()}::${JSON.stringify(this.options())}`;
78
+ if (Latex.svgContentsPool[src]) {
79
+ this.imageElement.src = Latex.svgContentsPool[src];
80
+ if (!this.imageElement.complete) {
81
+ DependencyContext.collectPromise(
82
+ new Promise((resolve, reject) => {
83
+ this.imageElement.addEventListener('load', resolve);
84
+ this.imageElement.addEventListener('error', reject);
85
+ }),
86
+ );
87
+ }
88
+ return this.imageElement;
89
+ }
90
+
91
+ // Convert to TeX, look for any errors
92
+ const tex = this.tex();
93
+ const svg = Latex.adaptor.innerHTML(
94
+ Latex.jaxDocument.convert(tex, this.options()) as any,
95
+ );
96
+ if (svg.includes('data-mjx-error')) {
97
+ const errors = svg.match(/data-mjx-error="(.*?)"/);
98
+ if (errors && errors.length > 0) {
99
+ useLogger().error(`Invalid MathJax: ${errors[1]}`);
100
+ }
101
+ }
102
+
103
+ // Encode to raw base64 image format
104
+ const text = `data:image/svg+xml;base64,${btoa(
105
+ `<?xml version="1.0" encoding="UTF-8" standalone="no" ?>\n${svg}`,
106
+ )}`;
107
+ Latex.svgContentsPool[src] = text;
108
+ const image = document.createElement('img');
109
+ image.src = text;
110
+ image.src = text;
111
+ if (!image.complete) {
112
+ DependencyContext.collectPromise(
113
+ new Promise((resolve, reject) => {
114
+ image.addEventListener('load', resolve);
115
+ image.addEventListener('error', reject);
116
+ }),
117
+ );
118
+ }
119
+
120
+ return image;
121
+ }
122
+ }