@retor/react-native 0.4.5 → 0.5.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 CHANGED
@@ -111,24 +111,43 @@ function MyLineCard({ line }: { line: RetorLine }) {
111
111
 
112
112
  ## Notes
113
113
 
114
- `<AddNoteSheet>` triggers when you call `useAddNote().open(tagId?)` — for example from a "+" button on the active tag in your `LineTagList`. It collects text + private/public, then calls `onNoteSubmit` on the parent `<Viewer>`. **Persistence is your responsibility** — store the note however you like, then re-pass updated notes back to Retor via `<Notes>`.
114
+ `<AddNoteSheet>` triggers when you call `useAddNote().open(tagId?)`. It collects text + private/public, then either:
115
+ - calls `onNoteSubmit` on the parent `<Viewer>` (if set) — **persistence is your responsibility**
116
+ - or falls back to persisting via Retor's own backend (Convex) when no `onNoteSubmit` is provided
117
+
118
+ Re-pass saved notes back to the 3D scene via `<Notes>`.
119
+
120
+ ### Note fields for proper rendering
121
+
122
+ When passing notes via `<Notes>`, each note should include:
123
+
124
+ | Field | Required | Purpose |
125
+ |-------|----------|---------|
126
+ | `_id` | yes | Unique identifier |
127
+ | `name` | yes | The note text (displayed in the tag list and 3D scene) |
128
+ | `position` | yes | `{ x, y, z }` — where the note sits on the line |
129
+ | `objectId` | yes | Set to the `lineId` so the note associates with the correct line |
130
+ | `progress` | yes | `0..1` position along the line (from the submit payload) — used for sort order and scroll-to |
131
+ | `avatarUrl` | recommended | Profile image URL — renders as a circular avatar in the tag pill and list item |
132
+ | `authorName` | recommended | Display name — used as initial-letter fallback when no `avatarUrl` |
133
+ | `tagType` | recommended | Set to `"icon"` for the standard note pill appearance |
134
+ | `userId` | for deletion | The note author's user ID — compared against `Viewer.userId` to show the delete button |
135
+
136
+ ### Creating + deleting notes
115
137
 
116
138
  ```tsx
117
139
  import { useState } from "react";
118
- import { Viewer, Hud, ProjectSheet, LineDetailSheet, AddNoteSheet, Notes, useAddNote, type RetorTag } from "@retor/react-native";
119
-
120
- function AddButton() {
121
- const { open } = useAddNote();
122
- return <Pressable onPress={() => open()}><Text>+</Text></Pressable>;
123
- }
140
+ import { Viewer, Hud, ProjectSheet, LineDetailSheet, AddNoteSheet, Notes, type RetorTag } from "@retor/react-native";
124
141
 
125
142
  export default function Scene() {
126
143
  const [notes, setNotes] = useState<RetorTag[]>([]);
144
+ const currentUserId = "user_abc"; // your auth system's user ID
127
145
 
128
146
  return (
129
147
  <Viewer
130
148
  projectId="abc123"
131
- onNoteSubmit={({ text, isPrivate, tagId, lineId, position }) => {
149
+ userId={currentUserId}
150
+ onNoteSubmit={({ text, isPrivate, lineId, position, progress }) => {
132
151
  if (!position) return;
133
152
  setNotes((prev) => [
134
153
  ...prev,
@@ -136,24 +155,24 @@ export default function Scene() {
136
155
  _id: `note-${Date.now()}`,
137
156
  name: text,
138
157
  position,
158
+ progress,
139
159
  objectId: lineId ?? undefined,
160
+ tagType: "icon",
161
+ avatarUrl: "https://example.com/avatar.jpg",
162
+ authorName: "Jane",
163
+ userId: currentUserId,
140
164
  },
141
165
  ]);
142
166
  }}
167
+ onNoteDelete={(noteId) => {
168
+ setNotes((prev) => prev.filter((n) => n._id !== noteId));
169
+ // also delete from your backend
170
+ }}
143
171
  >
144
172
  <Notes notes={notes} />
145
173
  <Hud>
146
174
  <ProjectSheet />
147
- <LineDetailSheet>
148
- <LineTagList>
149
- {(tag, isActive) => (
150
- <View style={{ flexDirection: "row", padding: 12 }}>
151
- <Text style={{ flex: 1, color: isActive ? "white" : "gray" }}>{tag.name}</Text>
152
- {isActive && <AddButton />}
153
- </View>
154
- )}
155
- </LineTagList>
156
- </LineDetailSheet>
175
+ <LineDetailSheet />
157
176
  <AddNoteSheet />
158
177
  </Hud>
159
178
  </Viewer>
@@ -161,6 +180,12 @@ export default function Scene() {
161
180
  }
162
181
  ```
163
182
 
183
+ When `onNoteSubmit` is **not** provided, the SDK sends the note to Retor's backend automatically (using the signed-in Clerk session inside the WebView). No `<Notes>` re-injection needed in that case.
184
+
185
+ When a user deletes a note, the SDK:
186
+ 1. Optimistically removes it from the local tag list
187
+ 2. Fires `onNoteDelete(noteId)` so you can delete from your backend
188
+
164
189
  ## Hooks
165
190
 
166
191
  All hooks read from the bridge context provided by the parent `<Viewer>`.
@@ -173,7 +198,7 @@ All hooks read from the bridge context provided by the parent `<Viewer>`.
173
198
  | `useLineProgress()` | `{ progress, closestTagId }` |
174
199
  | `useAutoplay()` | `{ isPlaying, toggle, play, pause }` |
175
200
  | `useAddNote()` | `{ isOpen, tagId, open, close, submit }` |
176
- | `useViewer()` | Imperative controls (`openLine`, `exitLine`, `scrollToTag`, ...) |
201
+ | `useViewer()` | Imperative controls (`openLine`, `exitLine`, `scrollToTag`, `scrollToProgress`, ...) |
177
202
 
178
203
  ## Imperative API
179
204
 
package/dist/index.d.mts CHANGED
@@ -25,6 +25,13 @@ interface RetorTag {
25
25
  authorName?: string;
26
26
  /** ID of the user who created this note. Compared against `Viewer.userId` to show the delete button. */
27
27
  userId?: string;
28
+ /** Absolute elevation in meters (above the GPS reference's elevation datum, typically MSL).
29
+ * Null if the project isn't georeferenced or has too few reference points (<4 non-coplanar). */
30
+ elevationAboveSeaLevel?: number | null;
31
+ /** Cumulative distance along the parent line from start to this tag, in meters.
32
+ * Uses the same arc-length lookup as `LineProgressPayload.distanceFromStart`, so the
33
+ * values are directly comparable. Null if the project isn't georeferenced. */
34
+ distanceFromStart?: number | null;
28
35
  }
29
36
  interface RetorLine {
30
37
  _id: string;
@@ -36,6 +43,8 @@ interface RetorLine {
36
43
  scrollType?: "track" | "observe";
37
44
  closed?: boolean;
38
45
  notesSupported?: boolean;
46
+ /** User-defined key/value fields set in the line editor. */
47
+ metadata?: Record<string, string | number | boolean>;
39
48
  tags: RetorTag[];
40
49
  }
41
50
  interface RetorProject {
@@ -60,6 +69,25 @@ interface LineProgressPayload {
60
69
  /** Distance along the line from the start.
61
70
  * In meters if GPS is configured on the project, else project-local units. */
62
71
  distanceFromStart?: number | null;
72
+ /** Pitch of the line tangent above the ground plane at the current point, in degrees.
73
+ * Positive = uphill, negative = downhill. Null if the curve isn't ready. */
74
+ pitchDeg?: number | null;
75
+ /** Absolute elevation in meters at the current point. Requires a georeferenced
76
+ * project (coord tags with lat/lon/elevation); null otherwise. */
77
+ elevationM?: number | null;
78
+ /** Cursor latitude in degrees. Null if not georeferenced. */
79
+ lat?: number | null;
80
+ /** Cursor longitude in degrees. Null if not georeferenced. */
81
+ lon?: number | null;
82
+ /** Elevation difference (meters) between the cursor and the line's first control tag.
83
+ * Positive = above start, negative = below. Null if not georeferenced. */
84
+ elevationGainM?: number | null;
85
+ /** Length in meters of the split-section the cursor is currently inside. */
86
+ sectionLengthM?: number | null;
87
+ /** 1-based index of the current split-section (1 if the line has no splits). */
88
+ sectionIndex?: number | null;
89
+ /** 0..1 progress of the cursor within the current section. */
90
+ sectionProgress?: number | null;
63
91
  }
64
92
  interface ViewerHandle {
65
93
  openLine: (lineId: string) => void;
@@ -140,6 +168,14 @@ declare function useLineProgress(): {
140
168
  z: number;
141
169
  } | null;
142
170
  distanceFromStart: number | null;
171
+ pitchDeg: number | null;
172
+ elevationM: number | null;
173
+ lat: number | null;
174
+ lon: number | null;
175
+ elevationGainM: number | null;
176
+ sectionLengthM: number | null;
177
+ sectionIndex: number | null;
178
+ sectionProgress: number | null;
143
179
  };
144
180
  /**
145
181
  * Returns the autoplay state and controls.
package/dist/index.d.ts CHANGED
@@ -25,6 +25,13 @@ interface RetorTag {
25
25
  authorName?: string;
26
26
  /** ID of the user who created this note. Compared against `Viewer.userId` to show the delete button. */
27
27
  userId?: string;
28
+ /** Absolute elevation in meters (above the GPS reference's elevation datum, typically MSL).
29
+ * Null if the project isn't georeferenced or has too few reference points (<4 non-coplanar). */
30
+ elevationAboveSeaLevel?: number | null;
31
+ /** Cumulative distance along the parent line from start to this tag, in meters.
32
+ * Uses the same arc-length lookup as `LineProgressPayload.distanceFromStart`, so the
33
+ * values are directly comparable. Null if the project isn't georeferenced. */
34
+ distanceFromStart?: number | null;
28
35
  }
29
36
  interface RetorLine {
30
37
  _id: string;
@@ -36,6 +43,8 @@ interface RetorLine {
36
43
  scrollType?: "track" | "observe";
37
44
  closed?: boolean;
38
45
  notesSupported?: boolean;
46
+ /** User-defined key/value fields set in the line editor. */
47
+ metadata?: Record<string, string | number | boolean>;
39
48
  tags: RetorTag[];
40
49
  }
41
50
  interface RetorProject {
@@ -60,6 +69,25 @@ interface LineProgressPayload {
60
69
  /** Distance along the line from the start.
61
70
  * In meters if GPS is configured on the project, else project-local units. */
62
71
  distanceFromStart?: number | null;
72
+ /** Pitch of the line tangent above the ground plane at the current point, in degrees.
73
+ * Positive = uphill, negative = downhill. Null if the curve isn't ready. */
74
+ pitchDeg?: number | null;
75
+ /** Absolute elevation in meters at the current point. Requires a georeferenced
76
+ * project (coord tags with lat/lon/elevation); null otherwise. */
77
+ elevationM?: number | null;
78
+ /** Cursor latitude in degrees. Null if not georeferenced. */
79
+ lat?: number | null;
80
+ /** Cursor longitude in degrees. Null if not georeferenced. */
81
+ lon?: number | null;
82
+ /** Elevation difference (meters) between the cursor and the line's first control tag.
83
+ * Positive = above start, negative = below. Null if not georeferenced. */
84
+ elevationGainM?: number | null;
85
+ /** Length in meters of the split-section the cursor is currently inside. */
86
+ sectionLengthM?: number | null;
87
+ /** 1-based index of the current split-section (1 if the line has no splits). */
88
+ sectionIndex?: number | null;
89
+ /** 0..1 progress of the cursor within the current section. */
90
+ sectionProgress?: number | null;
63
91
  }
64
92
  interface ViewerHandle {
65
93
  openLine: (lineId: string) => void;
@@ -140,6 +168,14 @@ declare function useLineProgress(): {
140
168
  z: number;
141
169
  } | null;
142
170
  distanceFromStart: number | null;
171
+ pitchDeg: number | null;
172
+ elevationM: number | null;
173
+ lat: number | null;
174
+ lon: number | null;
175
+ elevationGainM: number | null;
176
+ sectionLengthM: number | null;
177
+ sectionIndex: number | null;
178
+ sectionProgress: number | null;
143
179
  };
144
180
  /**
145
181
  * Returns the autoplay state and controls.
package/dist/index.js CHANGED
@@ -59,6 +59,14 @@ var RetorProgressContext = (0, import_react.createContext)({
59
59
  closestTagId: null,
60
60
  targetPosition: null,
61
61
  distanceFromStart: null,
62
+ pitchDeg: null,
63
+ elevationM: null,
64
+ lat: null,
65
+ lon: null,
66
+ elevationGainM: null,
67
+ sectionLengthM: null,
68
+ sectionIndex: null,
69
+ sectionProgress: null,
62
70
  isPlaying: false
63
71
  });
64
72
  function noop() {
@@ -103,10 +111,49 @@ function useActiveLine() {
103
111
  return useRetorBridge().activeLine;
104
112
  }
105
113
  function useLineProgress() {
106
- const { progress, closestTagId, targetPosition, distanceFromStart } = useRetorProgress();
114
+ const {
115
+ progress,
116
+ closestTagId,
117
+ targetPosition,
118
+ distanceFromStart,
119
+ pitchDeg,
120
+ elevationM,
121
+ lat,
122
+ lon,
123
+ elevationGainM,
124
+ sectionLengthM,
125
+ sectionIndex,
126
+ sectionProgress
127
+ } = useRetorProgress();
107
128
  return (0, import_react.useMemo)(
108
- () => ({ progress, closestTagId, targetPosition, distanceFromStart }),
109
- [progress, closestTagId, targetPosition, distanceFromStart]
129
+ () => ({
130
+ progress,
131
+ closestTagId,
132
+ targetPosition,
133
+ distanceFromStart,
134
+ pitchDeg,
135
+ elevationM,
136
+ lat,
137
+ lon,
138
+ elevationGainM,
139
+ sectionLengthM,
140
+ sectionIndex,
141
+ sectionProgress
142
+ }),
143
+ [
144
+ progress,
145
+ closestTagId,
146
+ targetPosition,
147
+ distanceFromStart,
148
+ pitchDeg,
149
+ elevationM,
150
+ lat,
151
+ lon,
152
+ elevationGainM,
153
+ sectionLengthM,
154
+ sectionIndex,
155
+ sectionProgress
156
+ ]
110
157
  );
111
158
  }
112
159
  function useAutoplay() {
@@ -218,6 +265,14 @@ var Viewer = (0, import_react2.forwardRef)(function Viewer2({ projectId, id = "d
218
265
  const [progress, setProgress] = (0, import_react2.useState)(0);
219
266
  const [targetPosition, setTargetPosition] = (0, import_react2.useState)(null);
220
267
  const [distanceFromStart, setDistanceFromStart] = (0, import_react2.useState)(null);
268
+ const [pitchDeg, setPitchDeg] = (0, import_react2.useState)(null);
269
+ const [elevationM, setElevationM] = (0, import_react2.useState)(null);
270
+ const [lat, setLat] = (0, import_react2.useState)(null);
271
+ const [lon, setLon] = (0, import_react2.useState)(null);
272
+ const [elevationGainM, setElevationGainM] = (0, import_react2.useState)(null);
273
+ const [sectionLengthM, setSectionLengthM] = (0, import_react2.useState)(null);
274
+ const [sectionIndex, setSectionIndex] = (0, import_react2.useState)(null);
275
+ const [sectionProgress, setSectionProgress] = (0, import_react2.useState)(null);
221
276
  const [isPlaying, setIsPlaying] = (0, import_react2.useState)(false);
222
277
  const [isAddNoteOpen, setIsAddNoteOpen] = (0, import_react2.useState)(false);
223
278
  const [addNoteTagId, setAddNoteTagId] = (0, import_react2.useState)(null);
@@ -366,6 +421,14 @@ var Viewer = (0, import_react2.forwardRef)(function Viewer2({ projectId, id = "d
366
421
  setClosestTagId(payload.closestTagId);
367
422
  setTargetPosition(payload.targetPosition ?? null);
368
423
  setDistanceFromStart(payload.distanceFromStart ?? null);
424
+ setPitchDeg(payload.pitchDeg ?? null);
425
+ setElevationM(payload.elevationM ?? null);
426
+ setLat(payload.lat ?? null);
427
+ setLon(payload.lon ?? null);
428
+ setElevationGainM(payload.elevationGainM ?? null);
429
+ setSectionLengthM(payload.sectionLengthM ?? null);
430
+ setSectionIndex(payload.sectionIndex ?? null);
431
+ setSectionProgress(payload.sectionProgress ?? null);
369
432
  onLineProgress?.(payload);
370
433
  break;
371
434
  }
@@ -413,8 +476,36 @@ var Viewer = (0, import_react2.forwardRef)(function Viewer2({ projectId, id = "d
413
476
  [project, lines, activeLineId, activeLine, lineNotes, externalNotes, userId, isAddNoteOpen, addNoteTagId, controls, openAddNote, closeAddNote, submitNote, deleteNote]
414
477
  );
415
478
  const progressCtx = (0, import_react2.useMemo)(
416
- () => ({ progress, closestTagId, targetPosition, distanceFromStart, isPlaying }),
417
- [progress, closestTagId, targetPosition, distanceFromStart, isPlaying]
479
+ () => ({
480
+ progress,
481
+ closestTagId,
482
+ targetPosition,
483
+ distanceFromStart,
484
+ pitchDeg,
485
+ elevationM,
486
+ lat,
487
+ lon,
488
+ elevationGainM,
489
+ sectionLengthM,
490
+ sectionIndex,
491
+ sectionProgress,
492
+ isPlaying
493
+ }),
494
+ [
495
+ progress,
496
+ closestTagId,
497
+ targetPosition,
498
+ distanceFromStart,
499
+ pitchDeg,
500
+ elevationM,
501
+ lat,
502
+ lon,
503
+ elevationGainM,
504
+ sectionLengthM,
505
+ sectionIndex,
506
+ sectionProgress,
507
+ isPlaying
508
+ ]
418
509
  );
419
510
  return /* @__PURE__ */ import_react2.default.createElement(RetorBridgeProvider, { value: ctxValue }, /* @__PURE__ */ import_react2.default.createElement(RetorProgressProvider, { value: progressCtx }, /* @__PURE__ */ import_react2.default.createElement(import_react_native.View, { style: [styles.root, style] }, /* @__PURE__ */ import_react2.default.createElement(
420
511
  import_react_native_webview.WebView,
@@ -667,7 +758,7 @@ function LineDetailSheet({ snapPoints = ["35%", "75%"], topInset, renderHeader,
667
758
  backgroundComponent: BlurBackground,
668
759
  topInset: effectiveTopInset
669
760
  },
670
- activeLine && (children ?? /* @__PURE__ */ import_react6.default.createElement(DefaultLineTagList, { listHeader: header }))
761
+ activeLine && (children ? /* @__PURE__ */ import_react6.default.createElement(import_bottom_sheet3.BottomSheetScrollView, { contentContainerStyle: styles3.customContent }, children) : /* @__PURE__ */ import_react6.default.createElement(DefaultLineTagList, { listHeader: header }))
671
762
  );
672
763
  }
673
764
  function DefaultHeader({
@@ -792,6 +883,7 @@ function DefaultTagItem({ tag, isActive }) {
792
883
  }
793
884
  var styles3 = import_react_native5.StyleSheet.create({
794
885
  handle: { backgroundColor: "rgba(255,255,255,0.3)" },
886
+ customContent: { paddingBottom: 120 },
795
887
  header: {
796
888
  flexDirection: "row",
797
889
  alignItems: "flex-start",
package/dist/index.mjs CHANGED
@@ -6,6 +6,14 @@ var RetorProgressContext = createContext({
6
6
  closestTagId: null,
7
7
  targetPosition: null,
8
8
  distanceFromStart: null,
9
+ pitchDeg: null,
10
+ elevationM: null,
11
+ lat: null,
12
+ lon: null,
13
+ elevationGainM: null,
14
+ sectionLengthM: null,
15
+ sectionIndex: null,
16
+ sectionProgress: null,
9
17
  isPlaying: false
10
18
  });
11
19
  function noop() {
@@ -50,10 +58,49 @@ function useActiveLine() {
50
58
  return useRetorBridge().activeLine;
51
59
  }
52
60
  function useLineProgress() {
53
- const { progress, closestTagId, targetPosition, distanceFromStart } = useRetorProgress();
61
+ const {
62
+ progress,
63
+ closestTagId,
64
+ targetPosition,
65
+ distanceFromStart,
66
+ pitchDeg,
67
+ elevationM,
68
+ lat,
69
+ lon,
70
+ elevationGainM,
71
+ sectionLengthM,
72
+ sectionIndex,
73
+ sectionProgress
74
+ } = useRetorProgress();
54
75
  return useMemo(
55
- () => ({ progress, closestTagId, targetPosition, distanceFromStart }),
56
- [progress, closestTagId, targetPosition, distanceFromStart]
76
+ () => ({
77
+ progress,
78
+ closestTagId,
79
+ targetPosition,
80
+ distanceFromStart,
81
+ pitchDeg,
82
+ elevationM,
83
+ lat,
84
+ lon,
85
+ elevationGainM,
86
+ sectionLengthM,
87
+ sectionIndex,
88
+ sectionProgress
89
+ }),
90
+ [
91
+ progress,
92
+ closestTagId,
93
+ targetPosition,
94
+ distanceFromStart,
95
+ pitchDeg,
96
+ elevationM,
97
+ lat,
98
+ lon,
99
+ elevationGainM,
100
+ sectionLengthM,
101
+ sectionIndex,
102
+ sectionProgress
103
+ ]
57
104
  );
58
105
  }
59
106
  function useAutoplay() {
@@ -174,6 +221,14 @@ var Viewer = forwardRef(function Viewer2({ projectId, id = "default", baseUrl =
174
221
  const [progress, setProgress] = useState(0);
175
222
  const [targetPosition, setTargetPosition] = useState(null);
176
223
  const [distanceFromStart, setDistanceFromStart] = useState(null);
224
+ const [pitchDeg, setPitchDeg] = useState(null);
225
+ const [elevationM, setElevationM] = useState(null);
226
+ const [lat, setLat] = useState(null);
227
+ const [lon, setLon] = useState(null);
228
+ const [elevationGainM, setElevationGainM] = useState(null);
229
+ const [sectionLengthM, setSectionLengthM] = useState(null);
230
+ const [sectionIndex, setSectionIndex] = useState(null);
231
+ const [sectionProgress, setSectionProgress] = useState(null);
177
232
  const [isPlaying, setIsPlaying] = useState(false);
178
233
  const [isAddNoteOpen, setIsAddNoteOpen] = useState(false);
179
234
  const [addNoteTagId, setAddNoteTagId] = useState(null);
@@ -322,6 +377,14 @@ var Viewer = forwardRef(function Viewer2({ projectId, id = "default", baseUrl =
322
377
  setClosestTagId(payload.closestTagId);
323
378
  setTargetPosition(payload.targetPosition ?? null);
324
379
  setDistanceFromStart(payload.distanceFromStart ?? null);
380
+ setPitchDeg(payload.pitchDeg ?? null);
381
+ setElevationM(payload.elevationM ?? null);
382
+ setLat(payload.lat ?? null);
383
+ setLon(payload.lon ?? null);
384
+ setElevationGainM(payload.elevationGainM ?? null);
385
+ setSectionLengthM(payload.sectionLengthM ?? null);
386
+ setSectionIndex(payload.sectionIndex ?? null);
387
+ setSectionProgress(payload.sectionProgress ?? null);
325
388
  onLineProgress?.(payload);
326
389
  break;
327
390
  }
@@ -369,8 +432,36 @@ var Viewer = forwardRef(function Viewer2({ projectId, id = "default", baseUrl =
369
432
  [project, lines, activeLineId, activeLine, lineNotes, externalNotes, userId, isAddNoteOpen, addNoteTagId, controls, openAddNote, closeAddNote, submitNote, deleteNote]
370
433
  );
371
434
  const progressCtx = useMemo2(
372
- () => ({ progress, closestTagId, targetPosition, distanceFromStart, isPlaying }),
373
- [progress, closestTagId, targetPosition, distanceFromStart, isPlaying]
435
+ () => ({
436
+ progress,
437
+ closestTagId,
438
+ targetPosition,
439
+ distanceFromStart,
440
+ pitchDeg,
441
+ elevationM,
442
+ lat,
443
+ lon,
444
+ elevationGainM,
445
+ sectionLengthM,
446
+ sectionIndex,
447
+ sectionProgress,
448
+ isPlaying
449
+ }),
450
+ [
451
+ progress,
452
+ closestTagId,
453
+ targetPosition,
454
+ distanceFromStart,
455
+ pitchDeg,
456
+ elevationM,
457
+ lat,
458
+ lon,
459
+ elevationGainM,
460
+ sectionLengthM,
461
+ sectionIndex,
462
+ sectionProgress,
463
+ isPlaying
464
+ ]
374
465
  );
375
466
  return /* @__PURE__ */ React2.createElement(RetorBridgeProvider, { value: ctxValue }, /* @__PURE__ */ React2.createElement(RetorProgressProvider, { value: progressCtx }, /* @__PURE__ */ React2.createElement(View, { style: [styles.root, style] }, /* @__PURE__ */ React2.createElement(
376
467
  WebView,
@@ -628,7 +719,7 @@ function LineDetailSheet({ snapPoints = ["35%", "75%"], topInset, renderHeader,
628
719
  backgroundComponent: BlurBackground,
629
720
  topInset: effectiveTopInset
630
721
  },
631
- activeLine && (children ?? /* @__PURE__ */ React6.createElement(DefaultLineTagList, { listHeader: header }))
722
+ activeLine && (children ? /* @__PURE__ */ React6.createElement(BottomSheetScrollView, { contentContainerStyle: styles3.customContent }, children) : /* @__PURE__ */ React6.createElement(DefaultLineTagList, { listHeader: header }))
632
723
  );
633
724
  }
634
725
  function DefaultHeader({
@@ -753,6 +844,7 @@ function DefaultTagItem({ tag, isActive }) {
753
844
  }
754
845
  var styles3 = StyleSheet5.create({
755
846
  handle: { backgroundColor: "rgba(255,255,255,0.3)" },
847
+ customContent: { paddingBottom: 120 },
756
848
  header: {
757
849
  flexDirection: "row",
758
850
  alignItems: "flex-start",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@retor/react-native",
3
- "version": "0.4.5",
3
+ "version": "0.5.0",
4
4
  "description": "React Native SDK for embedding Retor 3D experiences",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -17,6 +17,12 @@
17
17
  "README.md",
18
18
  "LICENSE"
19
19
  ],
20
+ "scripts": {
21
+ "build": "tsup src/index.tsx --format cjs,esm --dts --clean --external react --external react-native --external react-native-webview --external @gorhom/bottom-sheet --external react-native-gesture-handler --external react-native-reanimated --external react-native-svg --external lucide-react-native --external expo-blur",
22
+ "dev": "tsup src/index.tsx --format cjs,esm --dts --watch --external react --external react-native --external react-native-webview --external @gorhom/bottom-sheet --external react-native-gesture-handler --external react-native-reanimated --external react-native-svg --external lucide-react-native --external expo-blur",
23
+ "typecheck": "tsc --noEmit",
24
+ "prepublishOnly": "pnpm build"
25
+ },
20
26
  "keywords": [
21
27
  "retor",
22
28
  "3d",
@@ -55,9 +61,5 @@
55
61
  "tsup": "^8.0.0",
56
62
  "typescript": "^5.4.0"
57
63
  },
58
- "scripts": {
59
- "build": "tsup src/index.tsx --format cjs,esm --dts --clean --external react --external react-native --external react-native-webview --external @gorhom/bottom-sheet --external react-native-gesture-handler --external react-native-reanimated --external react-native-svg --external lucide-react-native --external expo-blur",
60
- "dev": "tsup src/index.tsx --format cjs,esm --dts --watch --external react --external react-native --external react-native-webview --external @gorhom/bottom-sheet --external react-native-gesture-handler --external react-native-reanimated --external react-native-svg --external lucide-react-native --external expo-blur",
61
- "typecheck": "tsc --noEmit"
62
- }
63
- }
64
+ "packageManager": "pnpm@9.12.3+sha512.cce0f9de9c5a7c95bef944169cc5dfe8741abfb145078c0d508b868056848a87c81e626246cb60967cbd7fd29a6c062ef73ff840d96b3c86c40ac92cf4a813ee"
65
+ }