ebta-workflow-prod 4.1.3 → 4.1.4

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ebta-workflow-prod",
3
3
  "private": false,
4
- "version": "4.1.3",
4
+ "version": "4.1.4",
5
5
  "description": "",
6
6
  "license": "UNLICENSED",
7
7
  "author": "",
@@ -32,8 +32,8 @@
32
32
  "@fseehawer/react-circular-slider": "^3.2.7",
33
33
  "@mantine/core": "^7.16.0",
34
34
  "@mantine/hooks": "^9.5.1",
35
- "@mui/icons-material": "^5.17.1",
36
35
  "@mui/base": "5.0.0-beta.70",
36
+ "@mui/icons-material": "^5.17.1",
37
37
  "@mui/material": "^5.17.1",
38
38
  "@mui/x-charts": "^7.6.2",
39
39
  "@turf/turf": "^6.5.0",
@@ -44,6 +44,7 @@
44
44
  "axios": "^1.7.7",
45
45
  "dayjs": "^1.11.23",
46
46
  "html2canvas": "^1.4.1",
47
+ "marzipano": "^0.10.2",
47
48
  "pannellum-react-update": "^1.0.0",
48
49
  "pdf-lib": "^1.17.1",
49
50
  "qrcode": "^1.5.4",
@@ -0,0 +1,239 @@
1
+ import type { IActivityHandler } from "@vertigis/workflow";
2
+
3
+ import Polygon from "@arcgis/core/geometry/Polygon";
4
+ import SpatialReference from "@arcgis/core/geometry/SpatialReference";
5
+
6
+
7
+ /** An interface that defines the inputs of the activity. */
8
+ interface SimplifyGeometryInputs {
9
+ /**
10
+ * @displayName Geometry
11
+ * @description The geometry to be simplified.
12
+ * @required
13
+ */
14
+ geometry: Polygon;
15
+
16
+ /**
17
+ * @displayName Vertex Limit
18
+ * @description The maximum number of vertices allowed in the simplified geometry.
19
+ */
20
+ vertlimit?: number;
21
+ }
22
+
23
+ /** An interface that defines the outputs of the activity. */
24
+ interface SimplifyGeometryOutputs {
25
+ /**
26
+ * @description The result of the activity.
27
+ */
28
+ result: Polygon;
29
+ }
30
+
31
+ /**
32
+ * @displayName SimplifyGeometry
33
+ * @category Custom Activities
34
+ * @description Simplify geometry - covering the whole geometry and increase minimum extra area, under required vertex amount.
35
+ */
36
+ export default class SimplifyGeometryActivity implements IActivityHandler {
37
+ /** Perform the execution logic of the activity. */
38
+
39
+ execute(inputs: SimplifyGeometryInputs): SimplifyGeometryOutputs {
40
+
41
+ // Clone the whole result so we never touch the original $permitFeature.result
42
+ let simplifyresult = inputs.geometry;
43
+ let limit = inputs.vertlimit || 30;
44
+
45
+ let ring = simplifyresult.rings[0];
46
+
47
+ if (ring.length > limit) {
48
+ ring = coveringSimplify(ring, limit);
49
+
50
+ simplifyresult.rings[0] = ring;
51
+ }
52
+
53
+ return { result: simplifyresult };
54
+
55
+
56
+
57
+
58
+ // ---- geometry helpers ----
59
+ function cross(o, a, b) {
60
+ return (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]);
61
+ }
62
+ function triangleArea(a, b, c) {
63
+ return Math.abs(cross(a, b, c)) / 2;
64
+ }
65
+ function signedArea(points) {
66
+ var area = 0, n = points.length;
67
+ for (var i = 0; i < n; i++) {
68
+ var p1 = points[i], p2 = points[(i + 1) % n];
69
+ area += p1[0] * p2[1] - p2[0] * p1[1];
70
+ }
71
+ return area / 2;
72
+ }
73
+
74
+ // orientation: >0 CCW turn, <0 CW turn, 0 collinear
75
+ function orient(a, b, c) {
76
+ var v = (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]);
77
+ if (v > 1e-9) return 1;
78
+ if (v < -1e-9) return -1;
79
+ return 0;
80
+ }
81
+
82
+ // true only for a PROPER crossing (interior intersection), not shared endpoints/touching
83
+ function properlyIntersects(p1, p2, p3, p4) {
84
+ var d1 = orient(p3, p4, p1);
85
+ var d2 = orient(p3, p4, p2);
86
+ var d3 = orient(p1, p2, p3);
87
+ var d4 = orient(p1, p2, p4);
88
+ return (d1 !== d2 && d1 !== 0 && d2 !== 0) && (d3 !== d4 && d3 !== 0 && d4 !== 0);
89
+ }
90
+
91
+ // does chording out vertex i (prev->next) cross any OTHER edge of
92
+ // the polygon? Required check — without it, concave removal can
93
+ // fold the polygon over itself into a self-intersecting bowtie on
94
+ // complex shapes (forks, interchanges, multiple concavities).
95
+ function chordIsSafe(pts, i) {
96
+ var n = pts.length;
97
+ var prev = pts[(i - 1 + n) % n];
98
+ var next = pts[(i + 1) % n];
99
+
100
+ for (var k = 0; k < n; k++) {
101
+ var a = pts[k];
102
+ var b = pts[(k + 1) % n];
103
+ if (k === (i - 1 + n) % n || k === i) continue; // the two edges being replaced
104
+ if (properlyIntersects(prev, next, a, b)) return false;
105
+ }
106
+ return true;
107
+ }
108
+
109
+ // Remove reflex (concave) vertices, cheapest-area first, but only
110
+ // when the resulting chord doesn't self-intersect the polygon.
111
+ // Chording a safe concave notch always ADDS area outside the
112
+ // original shape, so coverage is preserved automatically.
113
+ function removeReflexVertices(points, maxVertices) {
114
+ var pts = points.slice();
115
+ var orientSign = signedArea(pts) >= 0 ? 1 : -1;
116
+
117
+ var progress = true;
118
+ while (pts.length > maxVertices && progress) {
119
+ progress = false;
120
+ var n = pts.length;
121
+ var candidates = [];
122
+
123
+ for (var i = 0; i < n; i++) {
124
+ var prev = pts[(i - 1 + n) % n];
125
+ var curr = pts[i];
126
+ var next = pts[(i + 1) % n];
127
+ var c = cross(prev, curr, next);
128
+ var isReflexOrFlat = c * orientSign <= 1e-9;
129
+ if (isReflexOrFlat) {
130
+ candidates.push({ idx: i, area: triangleArea(prev, curr, next) });
131
+ }
132
+ }
133
+
134
+ candidates.sort(function (a, b) { return a.area - b.area; });
135
+
136
+ for (var ci = 0; ci < candidates.length; ci++) {
137
+ var idx = candidates[ci].idx;
138
+ if (chordIsSafe(pts, idx)) {
139
+ pts.splice(idx, 1);
140
+ progress = true;
141
+ break; // recompute fresh each removal since indices shift
142
+ }
143
+ }
144
+ }
145
+
146
+ return pts;
147
+ }
148
+
149
+ // Infinite-line intersection of (p1,p2) and (p3,p4).
150
+ function lineIntersection(p1, p2, p3, p4) {
151
+ var x1 = p1[0], y1 = p1[1], x2 = p2[0], y2 = p2[1];
152
+ var x3 = p3[0], y3 = p3[1], x4 = p4[0], y4 = p4[1];
153
+ var denom = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
154
+ if (Math.abs(denom) < 1e-9) return [(x2 + x3) / 2, (y2 + y3) / 2];
155
+ var pxNum = (x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4);
156
+ var pyNum = (x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4);
157
+ return [pxNum / denom, pyNum / denom];
158
+ }
159
+
160
+ // Standard monotone-chain convex hull — guaranteed simple and
161
+ // guaranteed to cover any input point set, regardless of how tangled
162
+ // the remaining shape is. Used as a safe fallback.
163
+ function convexHull(points) {
164
+ var sorted = points.slice().sort(function (a, b) { return a[0] === b[0] ? a[1] - b[1] : a[0] - b[0]; });
165
+ var pts = [];
166
+ for (var i = 0; i < sorted.length; i++) {
167
+ var p = sorted[i], prev = pts[pts.length - 1];
168
+ if (!prev || prev[0] !== p[0] || prev[1] !== p[1]) pts.push(p);
169
+ }
170
+ var n = pts.length;
171
+ if (n < 3) return pts.slice();
172
+ var lower = [];
173
+ for (var i2 = 0; i2 < n; i2++) {
174
+ while (lower.length >= 2 && cross(lower[lower.length - 2], lower[lower.length - 1], pts[i2]) <= 0) lower.pop();
175
+ lower.push(pts[i2]);
176
+ }
177
+ var upper = [];
178
+ for (var i3 = n - 1; i3 >= 0; i3--) {
179
+ while (upper.length >= 2 && cross(upper[upper.length - 2], upper[upper.length - 1], pts[i3]) <= 0) upper.pop();
180
+ upper.push(pts[i3]);
181
+ }
182
+ lower.pop(); upper.pop();
183
+ return lower.concat(upper);
184
+ }
185
+
186
+ // Reduce an already-convex polygon to maxVertices by selecting
187
+ // maxVertices of its edges (evenly spaced) as infinite support lines
188
+ // and intersecting consecutive ones. Always contains the input.
189
+ function reduceConvexBySupportLines(points, maxVertices) {
190
+ var n = points.length;
191
+ if (n <= maxVertices) return points.slice();
192
+ var chosen = [];
193
+ for (var k = 0; k < maxVertices; k++) chosen.push(Math.floor((k * n) / maxVertices));
194
+ var newPts = [];
195
+ for (var k2 = 0; k2 < maxVertices; k2++) {
196
+ var iPrevEdge = chosen[(k2 - 1 + maxVertices) % maxVertices];
197
+ var iCurrEdge = chosen[k2];
198
+ var a1 = points[iPrevEdge], a2 = points[(iPrevEdge + 1) % n];
199
+ var b1 = points[iCurrEdge], b2 = points[(iCurrEdge + 1) % n];
200
+ newPts.push(lineIntersection(a1, a2, b1, b2));
201
+ }
202
+ return newPts;
203
+ }
204
+
205
+ // Simplify a ring to at most maxVertices points, guaranteeing the
206
+ // result is both SIMPLE (no self-intersections) and always covers
207
+ // (contains) the original shape.
208
+ function coveringSimplify(ring, maxVertices) {
209
+ var first = ring[0], last = ring[ring.length - 1];
210
+ var isClosed = first[0] === last[0] && first[1] === last[1];
211
+ var open = isClosed ? ring.slice(0, ring.length - 1) : ring.slice();
212
+ var budgetOpen = isClosed ? maxVertices - 1 : maxVertices;
213
+
214
+ if (open.length <= budgetOpen) return ring.slice();
215
+
216
+ var afterReflex = removeReflexVertices(open, budgetOpen);
217
+
218
+ var result;
219
+ if (afterReflex.length > budgetOpen) {
220
+ // Couldn't safely get all the way there via reflex removal
221
+ // (or the remainder is genuinely convex) — fall back to the
222
+ // convex hull, which is always simple and always covers.
223
+ var hull = convexHull(afterReflex);
224
+ result = hull.length > budgetOpen
225
+ ? reduceConvexBySupportLines(hull, budgetOpen)
226
+ : hull;
227
+ } else {
228
+ result = afterReflex;
229
+ }
230
+
231
+ return isClosed ? result.concat([result[0]]) : result;
232
+ }
233
+
234
+
235
+ return {
236
+ result: simplifyresult
237
+ };
238
+ }
239
+ }
@@ -4,8 +4,9 @@ import { FormElementProps, FormElementRegistration } from "@vertigis/workflow";
4
4
  import { ColorInput, ColorPicker } from '@mantine/core';
5
5
  type ColorFormat = "hex" | "rgba" | "rgb" | "hsl" | "hsla";
6
6
 
7
- import "@mantine/core/styles.css";
8
-
7
+ //import "@mantine/core/styles.css";
8
+ //Causing shrink
9
+ import "../styles/mantine-scoped.css";
9
10
  import Color from "@arcgis/core/Color";
10
11
 
11
12
 
@@ -79,6 +80,7 @@ function CsColorPickerV2(props: CsColorPickerV2Props): React.ReactElement {
79
80
  };
80
81
 
81
82
  return (
83
+ <div className="vertigis-mantine-scope">
82
84
  <MantineProvider withStaticClasses withGlobalClasses classNamesPrefix="ebta-mantime">
83
85
  {componentType === "ColorInput" ? (
84
86
  <ColorInput
@@ -105,6 +107,7 @@ function CsColorPickerV2(props: CsColorPickerV2Props): React.ReactElement {
105
107
  />
106
108
  )}
107
109
  </MantineProvider>
110
+ </div>
108
111
  );
109
112
  }
110
113
 
@@ -0,0 +1,225 @@
1
+ import * as React from "react";
2
+ import { FormElementProps, FormElementRegistration } from "@vertigis/workflow";
3
+ import Marzipano from "marzipano";
4
+
5
+
6
+ /**
7
+ * The generic type argument provided to `FormElementProps<T>` controls the type
8
+ * of `props.value` and `props.setValue(value)`. If your element doesn't need a
9
+ * `value`, you can omit the type argument.
10
+ *
11
+ * You can also declare additional public properties of your element by adding
12
+ * properties to this interface. The properties will be managed by the Workflow
13
+ * runtime, and passed in as `props`. You can update the properties by using
14
+ * `props.setProperty(key, value)`.
15
+ */
16
+ interface PanovideoPlayerProps extends FormElementProps<string> {}
17
+
18
+ class VideoAsset extends (Marzipano as any).util.Emitter {
19
+ private _element: HTMLVideoElement;
20
+
21
+ constructor(videoElement: HTMLVideoElement) {
22
+ super();
23
+ this._element = videoElement;
24
+ // Marzipano polls isDynamic()/emits('change') to know when to redraw.
25
+ this._element.addEventListener("timeupdate", () => {
26
+ this.emit("change");
27
+ });
28
+ }
29
+
30
+ element(): HTMLVideoElement {
31
+ return this._element;
32
+ }
33
+
34
+ width(): number {
35
+ return this._element.videoWidth;
36
+ }
37
+
38
+ height(): number {
39
+ return this._element.videoHeight;
40
+ }
41
+
42
+ isDynamic(): boolean {
43
+ return true;
44
+ }
45
+ }
46
+
47
+ /**
48
+ * A Workflow element built using React.
49
+ * @displayName PanovideoPlayer
50
+ * @description Marzipano library panoramic video player
51
+ * @param props The props that will be provided by the Workflow runtime.
52
+ */
53
+ function PanovideoPlayer(props: PanovideoPlayerProps): React.ReactElement {
54
+ const { setValue, value } = props;
55
+
56
+ const containerRef = React.useRef<HTMLDivElement | null>(null);
57
+ const videoElRef = React.useRef<HTMLVideoElement | null>(null);
58
+ const viewerRef = React.useRef<any>(null);
59
+ const [isPlaying, setIsPlaying] = React.useState(false);
60
+ const [error, setError] = React.useState<string | null>(null);
61
+
62
+ // (Re)builds the Marzipano viewer whenever the video URL (props.value) changes.
63
+ React.useEffect(() => {
64
+ if (!value || !containerRef.current) {
65
+ return undefined;
66
+ }
67
+
68
+ setError(null);
69
+
70
+ // Create a hidden video element to drive the panorama. We don't
71
+ // attach it to the DOM directly - Marzipano reads frames from it
72
+ // and draws them into its own WebGL canvas.
73
+ const videoEl = document.createElement("video");
74
+ videoEl.crossOrigin = "anonymous";
75
+ videoEl.loop = true;
76
+ videoEl.muted = true; // required for autoplay in most browsers
77
+ videoEl.playsInline = true;
78
+ videoEl.src = value;
79
+ videoElRef.current = videoEl;
80
+
81
+ let viewer: any;
82
+ let cancelled = false;
83
+
84
+ const onLoadedMetadata = () => {
85
+ if (cancelled || !containerRef.current) {
86
+ return;
87
+ }
88
+
89
+ try {
90
+ viewer = new (Marzipano as any).Viewer(containerRef.current);
91
+ viewerRef.current = viewer;
92
+
93
+ const asset = new VideoAsset(videoEl);
94
+ const source = (Marzipano as any).SingleAssetSource.fromAsset(asset);
95
+
96
+ const geometry = new (Marzipano as any).EquirectGeometry([
97
+ { width: videoEl.videoWidth || 1920 },
98
+ ]);
99
+
100
+ const limiter = (Marzipano as any).RectilinearView.limit.traditional(
101
+ 1024,
102
+ (100 * Math.PI) / 180
103
+ );
104
+ const view = new (Marzipano as any).RectilinearView(
105
+ { yaw: 0, pitch: 0, fov: Math.PI / 2 },
106
+ limiter
107
+ );
108
+
109
+ const scene = viewer.createScene({
110
+ source,
111
+ geometry,
112
+ view,
113
+ pinFirstLevel: true,
114
+ });
115
+
116
+ scene.switchTo();
117
+
118
+ videoEl
119
+ .play()
120
+ .then(() => setIsPlaying(true))
121
+ .catch(() => {
122
+ // Autoplay was blocked - user needs to tap play.
123
+ setIsPlaying(false);
124
+ });
125
+ } catch (e) {
126
+ setError(
127
+ e instanceof Error ? e.message : "Failed to initialize panoramic video."
128
+ );
129
+ }
130
+ };
131
+
132
+ const onVideoError = () => {
133
+ if (!cancelled) {
134
+ setError("Unable to load video from the provided URL.");
135
+ }
136
+ };
137
+
138
+ videoEl.addEventListener("loadedmetadata", onLoadedMetadata);
139
+ videoEl.addEventListener("error", onVideoError);
140
+
141
+ return () => {
142
+ cancelled = true;
143
+ videoEl.removeEventListener("loadedmetadata", onLoadedMetadata);
144
+ videoEl.removeEventListener("error", onVideoError);
145
+ videoEl.pause();
146
+ videoEl.removeAttribute("src");
147
+ videoEl.load();
148
+
149
+ if (viewerRef.current) {
150
+ try {
151
+ // Not all Marzipano versions expose destroy(); guard defensively.
152
+ viewerRef.current.destroy?.();
153
+ } catch {
154
+ // no-op
155
+ }
156
+ viewerRef.current = null;
157
+ }
158
+
159
+ if (containerRef.current) {
160
+ containerRef.current.innerHTML = "";
161
+ }
162
+ };
163
+ }, [value]);
164
+
165
+ const togglePlayback = () => {
166
+ const videoEl = videoElRef.current;
167
+ if (!videoEl) {
168
+ return;
169
+ }
170
+ if (videoEl.paused) {
171
+ videoEl.play().then(() => setIsPlaying(true));
172
+ } else {
173
+ videoEl.pause();
174
+ setIsPlaying(false);
175
+ }
176
+ };
177
+
178
+ return (
179
+ <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
180
+ <input
181
+ type="text"
182
+ placeholder="Enter 360° equirectangular video URL"
183
+ onChange={(event) => setValue(event.currentTarget.value)}
184
+ value={value ?? ""}
185
+ style={{ width: "100%" }}
186
+ />
187
+
188
+ {error && (
189
+ <div style={{ color: "red", fontSize: 12 }}>{error}</div>
190
+ )}
191
+
192
+ {value && (
193
+ <div style={{ position: "relative" }}>
194
+ <div
195
+ ref={containerRef}
196
+ style={{
197
+ width: "100%",
198
+ height: 400,
199
+ background: "#000",
200
+ }}
201
+ />
202
+ <button
203
+ type="button"
204
+ onClick={togglePlayback}
205
+ style={{
206
+ position: "absolute",
207
+ bottom: 8,
208
+ left: 8,
209
+ }}
210
+ >
211
+ {isPlaying ? "Pause" : "Play"}
212
+ </button>
213
+ </div>
214
+ )}
215
+ </div>
216
+ );
217
+ }
218
+
219
+ const PanovideoPlayerElementRegistration: FormElementRegistration<PanovideoPlayerProps> = {
220
+ component: PanovideoPlayer,
221
+ getInitialProperties: () => ({ value: "" }),
222
+ id: "PanovideoPlayer",
223
+ };
224
+
225
+ export default PanovideoPlayerElementRegistration;
@@ -2,7 +2,7 @@ import * as React from "react";
2
2
  import { FormElementProps, FormElementRegistration } from "@vertigis/workflow";
3
3
  import { Timeline as MainTimeLine, Text } from '@mantine/core';
4
4
  import { MantineProvider } from '@mantine/core';
5
-
5
+ import "../styles/mantine-scoped.css";
6
6
 
7
7
  /**
8
8
  * The generic type argument provided to `FormElementProps<T>` controls the type
@@ -38,30 +38,33 @@ interface TimelineProps extends FormElementProps<string> {
38
38
  * @param props The props that will be provided by the Workflow runtime.
39
39
  */
40
40
  function Timeline(props: TimelineProps): React.ReactElement {
41
- const { itemDetails,iconSize,lineWidth,active,color,reverseActive } = props;
41
+ const { itemDetails, iconSize, lineWidth, active, color, reverseActive } = props;
42
42
  iconSize * 0.6
43
43
  return (
44
- <MantineProvider>
45
- <MainTimeLine active={active} bulletSize={iconSize} lineWidth={lineWidth} color={color} reverseActive={reverseActive}>
44
+ <div className="vertigis-mantine-scope">
45
+ <MantineProvider>
46
+ <MainTimeLine active={active} bulletSize={iconSize} lineWidth={lineWidth} color={color} reverseActive={reverseActive}>
47
+
48
+ {itemDetails.map((item, index) => {
49
+
50
+ return (
51
+
52
+ <MainTimeLine.Item
53
+ key={index}
54
+ title={item.title}
55
+ lineVariant={item.lineVariant}
56
+ >
57
+
58
+ {item.description ? <Text c="dimmed" size="sm">{item.description}</Text> : undefined}
59
+ {item.time ? <Text size="xs" mt={4}>{item.time}</Text> : undefined}
46
60
 
47
- {itemDetails.map((item, index) => {
48
-
49
- return (
50
-
51
- <MainTimeLine.Item
52
- key={index}
53
- title={item.title}
54
- lineVariant={item.lineVariant}
55
- >
56
-
57
- {item.description ? <Text c="dimmed" size="sm">{item.description}</Text> : undefined}
58
- {item.time ? <Text size="xs" mt={4}>{item.time}</Text> : undefined}
59
-
60
- </MainTimeLine.Item>
61
- )})}
62
- </MainTimeLine>
61
+ </MainTimeLine.Item>
62
+ )
63
+ })}
64
+ </MainTimeLine>
63
65
 
64
- </MantineProvider>
66
+ </MantineProvider>
67
+ </div>
65
68
  );
66
69
  }
67
70
 
@@ -72,7 +75,7 @@ const TimelineElementRegistration: FormElementRegistration<TimelineProps> = {
72
75
  lineWidth: 1,
73
76
  iconSize: 40,
74
77
  active: 2,
75
- color:'var(--buttonIcon)',
78
+ color: 'var(--buttonIcon)',
76
79
  reverseActive: true,
77
80
  itemDetails: [
78
81
  {
package/src/index.ts CHANGED
@@ -18,6 +18,7 @@ export { default as HTMLextractorActivity } from "./activities/HTMLextractor";
18
18
  export { default as AttributeFilterElementRegistration } from "./elements/AttributeFilter"
19
19
  export { default as ItemSearcher_V2DEVRegistration } from "./elements/ItemSearcher_v2";
20
20
  export { default as Viewer360Registration } from "./elements/Viewer360";
21
+ export { default as PanovideoPlayerRegistration} from "./elements/PanovideoPlayer";
21
22
  export { default as TableRadioGroupElementRegistration } from "./elements/TableRadioGoup";
22
23
  export { MuiSingleToggleButtonGroupElementRegistration, MuiMultipleToggleButtonGroupElementRegistration } from "./elements/ToggleButtonGroup";
23
24
 
@@ -40,3 +41,6 @@ export { default as DrapGeometryActivity } from "./activities/DrapGeometry";
40
41
  export { default as SampleActivity } from "./activities/Sample";
41
42
  export { default as SampleInputRegistration } from "./elements/SampleInput";
42
43
 
44
+
45
+
46
+ export { default as SimplifyGeometryActivity } from "./activities/SimplifyGeometry";
@@ -0,0 +1,3 @@
1
+ .vertigis-mantine-scope {
2
+ @import "@mantine/core/styles.css";
3
+ }