@meetreeve/capacitor-bridge 0.1.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.
@@ -0,0 +1,523 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/react/index.ts
31
+ var react_exports = {};
32
+ __export(react_exports, {
33
+ BugReportButton: () => BugReportButton,
34
+ CapacitorProvider: () => CapacitorProvider,
35
+ useCapacitor: () => useCapacitor,
36
+ useSafeArea: () => useSafeArea
37
+ });
38
+ module.exports = __toCommonJS(react_exports);
39
+
40
+ // src/react/CapacitorProvider.tsx
41
+ var React = __toESM(require("react"), 1);
42
+
43
+ // src/core/platform.ts
44
+ function getCapacitor() {
45
+ if (typeof window === "undefined") return null;
46
+ const cap = window.Capacitor;
47
+ if (!cap || typeof cap.isNativePlatform !== "function") return null;
48
+ return cap;
49
+ }
50
+ function getPlatform() {
51
+ const cap = getCapacitor();
52
+ if (!cap) return "web";
53
+ const p = cap.getPlatform();
54
+ return p === "ios" ? "ios" : p === "android" ? "android" : "web";
55
+ }
56
+ function isNativePlatform() {
57
+ return getPlatform() !== "web";
58
+ }
59
+
60
+ // src/core/safe-area.ts
61
+ var ZERO = { top: 0, right: 0, bottom: 0, left: 0 };
62
+ function getSafeAreaInsets() {
63
+ if (typeof window === "undefined" || typeof document === "undefined") {
64
+ return ZERO;
65
+ }
66
+ const root = document.documentElement;
67
+ const styles = window.getComputedStyle(root);
68
+ const read = (side) => {
69
+ const v = styles.getPropertyValue(`--safe-area-inset-${side}`).trim();
70
+ const n = parseFloat(v);
71
+ return Number.isFinite(n) ? n : 0;
72
+ };
73
+ return {
74
+ top: read("top"),
75
+ right: read("right"),
76
+ bottom: read("bottom"),
77
+ left: read("left")
78
+ };
79
+ }
80
+ function installSafeAreaVars() {
81
+ if (typeof document === "undefined") return;
82
+ const TAG = "reeve-capacitor-bridge-safe-area";
83
+ if (document.querySelector(`style[data-${TAG}]`)) return;
84
+ const style = document.createElement("style");
85
+ style.setAttribute(`data-${TAG}`, "1");
86
+ style.textContent = `
87
+ :root {
88
+ --safe-area-inset-top: env(safe-area-inset-top, 0px);
89
+ --safe-area-inset-right: env(safe-area-inset-right, 0px);
90
+ --safe-area-inset-bottom: env(safe-area-inset-bottom, 0px);
91
+ --safe-area-inset-left: env(safe-area-inset-left, 0px);
92
+ }
93
+ `;
94
+ document.head.appendChild(style);
95
+ }
96
+
97
+ // src/core/splash.ts
98
+ function getSplashPlugin() {
99
+ if (typeof window === "undefined") return null;
100
+ const cap = window.Capacitor;
101
+ return cap?.Plugins?.SplashScreen ?? null;
102
+ }
103
+ var GLOBAL_KEY = /* @__PURE__ */ Symbol.for("@meetreeve/capacitor-bridge/splash@1");
104
+ function getGlobalState() {
105
+ const g = globalThis;
106
+ if (!g[GLOBAL_KEY]) g[GLOBAL_KEY] = { done: null, pending: null };
107
+ return g[GLOBAL_KEY];
108
+ }
109
+ async function dismissSplash(fadeOutDuration = 250) {
110
+ if (!isNativePlatform()) return;
111
+ const state = getGlobalState();
112
+ if (state.done) return state.done;
113
+ if (state.pending) return state.pending;
114
+ const plugin = getSplashPlugin();
115
+ if (!plugin) return;
116
+ const attempt = (async () => {
117
+ try {
118
+ await plugin.hide({ fadeOutDuration });
119
+ state.done = Promise.resolve();
120
+ } catch (err) {
121
+ state.pending = null;
122
+ throw err;
123
+ } finally {
124
+ if (state.done) state.pending = null;
125
+ }
126
+ })();
127
+ state.pending = attempt;
128
+ return attempt.catch(() => {
129
+ });
130
+ }
131
+
132
+ // src/react/CapacitorProvider.tsx
133
+ var import_jsx_runtime = require("react/jsx-runtime");
134
+ var FALLBACK_VALUE = {
135
+ platform: "web",
136
+ isNative: false,
137
+ isIOS: false,
138
+ isAndroid: false,
139
+ dismissSplash: async () => {
140
+ }
141
+ };
142
+ var CapacitorContext = React.createContext(FALLBACK_VALUE);
143
+ function CapacitorProvider({
144
+ children,
145
+ autoDismissSplash = true
146
+ }) {
147
+ const value = React.useMemo(() => {
148
+ const platform = getPlatform();
149
+ return {
150
+ platform,
151
+ isNative: platform !== "web",
152
+ isIOS: platform === "ios",
153
+ isAndroid: platform === "android",
154
+ dismissSplash
155
+ };
156
+ }, []);
157
+ React.useEffect(() => {
158
+ installSafeAreaVars();
159
+ if (typeof document !== "undefined") {
160
+ document.documentElement.setAttribute("data-platform", value.platform);
161
+ }
162
+ if (autoDismissSplash) {
163
+ void dismissSplash();
164
+ }
165
+ }, [autoDismissSplash, value.platform]);
166
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CapacitorContext.Provider, { value, children });
167
+ }
168
+ function useCapacitor() {
169
+ return React.useContext(CapacitorContext);
170
+ }
171
+
172
+ // src/react/useSafeArea.ts
173
+ var React2 = __toESM(require("react"), 1);
174
+ function useSafeArea() {
175
+ const [insets, setInsets] = React2.useState(() => getSafeAreaInsets());
176
+ React2.useEffect(() => {
177
+ if (typeof window === "undefined") return;
178
+ const update = () => setInsets(getSafeAreaInsets());
179
+ window.addEventListener("orientationchange", update);
180
+ window.addEventListener("resize", update);
181
+ return () => {
182
+ window.removeEventListener("orientationchange", update);
183
+ window.removeEventListener("resize", update);
184
+ };
185
+ }, []);
186
+ return insets;
187
+ }
188
+
189
+ // src/react/BugReportButton.tsx
190
+ var React3 = __toESM(require("react"), 1);
191
+
192
+ // src/bugreport/index.ts
193
+ var GLOBAL_KEY2 = /* @__PURE__ */ Symbol.for("@meetreeve/capacitor-bridge/bugreport@1");
194
+ function getGlobalState2() {
195
+ const g = globalThis;
196
+ if (!g[GLOBAL_KEY2]) g[GLOBAL_KEY2] = { config: null };
197
+ return g[GLOBAL_KEY2];
198
+ }
199
+ function getBugReportEnvironment() {
200
+ const config = getGlobalState2().config;
201
+ if (typeof window === "undefined") {
202
+ return {
203
+ platform: getPlatform(),
204
+ userAgent: "",
205
+ currentUrl: "",
206
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
207
+ };
208
+ }
209
+ return {
210
+ platform: getPlatform(),
211
+ userAgent: window.navigator?.userAgent ?? "",
212
+ appVersion: config?.appVersion,
213
+ buildNumber: config?.buildNumber,
214
+ currentUrl: window.location?.href ?? "",
215
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
216
+ };
217
+ }
218
+ async function submitBugReport(payload) {
219
+ const config = getGlobalState2().config;
220
+ if (!config) {
221
+ throw new Error(
222
+ "[@meetreeve/capacitor-bridge/bugreport] submitBugReport called without configureBugReport({endpoint})."
223
+ );
224
+ }
225
+ const submission = {
226
+ ...payload,
227
+ environment: getBugReportEnvironment()
228
+ };
229
+ const headers = {
230
+ "Content-Type": "application/json"
231
+ };
232
+ if (config.headers) {
233
+ const extraHeaders = await config.headers();
234
+ Object.assign(headers, extraHeaders);
235
+ }
236
+ const response = await fetch(config.endpoint, {
237
+ method: "POST",
238
+ headers,
239
+ body: JSON.stringify(submission),
240
+ credentials: config.credentials ?? "omit"
241
+ });
242
+ if (!response.ok) {
243
+ throw new Error(
244
+ `[@meetreeve/capacitor-bridge/bugreport] bug-report submission failed: ${response.status} ${response.statusText}`
245
+ );
246
+ }
247
+ }
248
+ async function captureScreenshot() {
249
+ if (typeof window === "undefined") return null;
250
+ const cap = window.Capacitor;
251
+ const plugin = cap?.Plugins?.Screenshot;
252
+ if (plugin) {
253
+ try {
254
+ const { base64 } = await plugin.take();
255
+ return `data:image/png;base64,${base64}`;
256
+ } catch {
257
+ }
258
+ }
259
+ return null;
260
+ }
261
+
262
+ // src/react/BugReportButton.tsx
263
+ var import_jsx_runtime2 = require("react/jsx-runtime");
264
+ var initialState = {
265
+ open: false,
266
+ message: "",
267
+ screenshot: null,
268
+ capturing: false,
269
+ submitting: false,
270
+ error: null
271
+ };
272
+ function BugReportButton({
273
+ userIdentifier,
274
+ renderTrigger,
275
+ extra,
276
+ onSubmitted
277
+ }) {
278
+ const [state, setState] = React3.useState(initialState);
279
+ const triggerRef = React3.useRef(null);
280
+ const textareaRef = React3.useRef(null);
281
+ const previouslyFocusedRef = React3.useRef(null);
282
+ const open = React3.useCallback(() => {
283
+ previouslyFocusedRef.current = document.activeElement;
284
+ setState({ ...initialState, open: true });
285
+ }, []);
286
+ const close = React3.useCallback(() => {
287
+ setState(initialState);
288
+ const prev = previouslyFocusedRef.current;
289
+ if (prev && typeof prev.focus === "function") {
290
+ prev.focus();
291
+ }
292
+ }, []);
293
+ const captureScreen = React3.useCallback(async () => {
294
+ setState((s) => ({ ...s, capturing: true, error: null }));
295
+ try {
296
+ const img = await captureScreenshot();
297
+ setState((s) => ({ ...s, screenshot: img, capturing: false }));
298
+ } catch (err) {
299
+ setState((s) => ({
300
+ ...s,
301
+ capturing: false,
302
+ error: err instanceof Error ? err.message : String(err)
303
+ }));
304
+ }
305
+ }, []);
306
+ React3.useEffect(() => {
307
+ if (!state.open) return;
308
+ textareaRef.current?.focus();
309
+ const onKey = (e) => {
310
+ if (e.key === "Escape" && !state.submitting) {
311
+ e.stopPropagation();
312
+ close();
313
+ }
314
+ };
315
+ document.addEventListener("keydown", onKey);
316
+ return () => document.removeEventListener("keydown", onKey);
317
+ }, [state.open, state.submitting, close]);
318
+ const submit = React3.useCallback(async () => {
319
+ if (!state.message.trim()) return;
320
+ setState((s) => ({ ...s, submitting: true, error: null }));
321
+ const payload = {
322
+ message: state.message,
323
+ userIdentifier,
324
+ extra
325
+ };
326
+ if (state.screenshot) payload.screenshot = state.screenshot;
327
+ try {
328
+ await submitBugReport(payload);
329
+ setState(initialState);
330
+ onSubmitted?.();
331
+ } catch (err) {
332
+ setState((s) => ({
333
+ ...s,
334
+ submitting: false,
335
+ error: err instanceof Error ? err.message : String(err)
336
+ }));
337
+ }
338
+ }, [state.message, state.screenshot, userIdentifier, extra, onSubmitted]);
339
+ const trigger = renderTrigger ? renderTrigger(open) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
340
+ "button",
341
+ {
342
+ ref: triggerRef,
343
+ type: "button",
344
+ onClick: open,
345
+ style: {
346
+ position: "fixed",
347
+ bottom: "calc(env(safe-area-inset-bottom, 0px) + 16px)",
348
+ right: "calc(env(safe-area-inset-right, 0px) + 16px)",
349
+ zIndex: 9999,
350
+ padding: "10px 14px",
351
+ background: "rgba(0,0,0,0.78)",
352
+ color: "white",
353
+ border: "none",
354
+ borderRadius: "999px",
355
+ fontSize: 14,
356
+ fontWeight: 500,
357
+ boxShadow: "0 4px 12px rgba(0,0,0,0.25)",
358
+ cursor: "pointer"
359
+ },
360
+ "aria-label": "Report a bug",
361
+ children: "Report bug"
362
+ }
363
+ );
364
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
365
+ trigger,
366
+ state.open && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
367
+ "div",
368
+ {
369
+ role: "dialog",
370
+ "aria-modal": "true",
371
+ "aria-label": "Report a bug",
372
+ style: {
373
+ position: "fixed",
374
+ inset: 0,
375
+ zIndex: 1e4,
376
+ background: "rgba(0,0,0,0.55)",
377
+ display: "flex",
378
+ alignItems: "flex-end",
379
+ justifyContent: "center"
380
+ },
381
+ onClick: (e) => {
382
+ if (e.target === e.currentTarget && !state.submitting) close();
383
+ },
384
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
385
+ "div",
386
+ {
387
+ style: {
388
+ width: "100%",
389
+ maxWidth: 460,
390
+ background: "white",
391
+ color: "#111",
392
+ borderTopLeftRadius: 16,
393
+ borderTopRightRadius: 16,
394
+ padding: "20px 20px calc(env(safe-area-inset-bottom, 0px) + 20px)",
395
+ boxSizing: "border-box"
396
+ },
397
+ children: [
398
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h2", { style: { margin: "0 0 12px", fontSize: 18 }, children: "Report a bug" }),
399
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
400
+ "textarea",
401
+ {
402
+ ref: textareaRef,
403
+ value: state.message,
404
+ onChange: (e) => setState((s) => ({ ...s, message: e.target.value })),
405
+ placeholder: "What broke? What were you doing when it broke?",
406
+ rows: 4,
407
+ style: {
408
+ width: "100%",
409
+ boxSizing: "border-box",
410
+ padding: 10,
411
+ fontSize: 15,
412
+ border: "1px solid #ddd",
413
+ borderRadius: 8,
414
+ resize: "vertical"
415
+ },
416
+ disabled: state.submitting
417
+ }
418
+ ),
419
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
420
+ "div",
421
+ {
422
+ style: {
423
+ marginTop: 10,
424
+ display: "flex",
425
+ gap: 8,
426
+ alignItems: "center"
427
+ },
428
+ children: [
429
+ state.screenshot ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
430
+ "img",
431
+ {
432
+ src: state.screenshot,
433
+ alt: "screenshot",
434
+ style: {
435
+ width: 56,
436
+ height: 56,
437
+ borderRadius: 6,
438
+ objectFit: "cover",
439
+ border: "1px solid #eee"
440
+ }
441
+ }
442
+ ) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
443
+ "button",
444
+ {
445
+ type: "button",
446
+ onClick: captureScreen,
447
+ disabled: state.capturing || state.submitting,
448
+ style: {
449
+ padding: "6px 10px",
450
+ fontSize: 13,
451
+ border: "1px solid #ccc",
452
+ borderRadius: 6,
453
+ background: "white",
454
+ cursor: "pointer"
455
+ },
456
+ children: state.capturing ? "Capturing\u2026" : "Attach screenshot"
457
+ }
458
+ ),
459
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { flex: 1 } }),
460
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
461
+ "button",
462
+ {
463
+ type: "button",
464
+ onClick: close,
465
+ disabled: state.submitting,
466
+ style: {
467
+ padding: "8px 14px",
468
+ fontSize: 14,
469
+ border: "1px solid #ccc",
470
+ borderRadius: 6,
471
+ background: "white",
472
+ cursor: "pointer"
473
+ },
474
+ children: "Cancel"
475
+ }
476
+ ),
477
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
478
+ "button",
479
+ {
480
+ type: "button",
481
+ onClick: submit,
482
+ disabled: !state.message.trim() || state.submitting,
483
+ style: {
484
+ padding: "8px 14px",
485
+ fontSize: 14,
486
+ border: "none",
487
+ borderRadius: 6,
488
+ background: state.message.trim() ? "#111" : "#bbb",
489
+ color: "white",
490
+ cursor: state.message.trim() ? "pointer" : "default"
491
+ },
492
+ children: state.submitting ? "Sending\u2026" : "Send"
493
+ }
494
+ )
495
+ ]
496
+ }
497
+ ),
498
+ state.error && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
499
+ "div",
500
+ {
501
+ role: "alert",
502
+ style: {
503
+ marginTop: 12,
504
+ color: "#b00020",
505
+ fontSize: 13
506
+ },
507
+ children: state.error
508
+ }
509
+ )
510
+ ]
511
+ }
512
+ )
513
+ }
514
+ )
515
+ ] });
516
+ }
517
+ // Annotate the CommonJS export names for ESM import in node:
518
+ 0 && (module.exports = {
519
+ BugReportButton,
520
+ CapacitorProvider,
521
+ useCapacitor,
522
+ useSafeArea
523
+ });
@@ -0,0 +1,68 @@
1
+ import * as React from 'react';
2
+ import { P as Platform, S as SafeAreaInsets } from '../safe-area-B67yGQOn.cjs';
3
+
4
+ /**
5
+ * <CapacitorProvider> is the one-line install for consumer apps. Mount it at
6
+ * the top of the tree and every child gets useCapacitor() with platform info,
7
+ * safe-area insets, and a stable dismissSplash() callback.
8
+ *
9
+ * On mount the provider also installs the --safe-area-inset-* CSS vars and
10
+ * marks the document body with a data-platform="ios|android|web" attribute
11
+ * so CSS can target native chrome without JS.
12
+ */
13
+
14
+ interface CapacitorContextValue {
15
+ platform: Platform;
16
+ isNative: boolean;
17
+ isIOS: boolean;
18
+ isAndroid: boolean;
19
+ dismissSplash: (fadeOutDuration?: number) => Promise<void>;
20
+ }
21
+ interface CapacitorProviderProps {
22
+ children: React.ReactNode;
23
+ /**
24
+ * Dismiss the native splash screen automatically on first child render.
25
+ * Defaults to true — consumer apps almost always want this; set false if
26
+ * the splash should stay visible until some later signal (e.g. after auth
27
+ * bootstrap).
28
+ */
29
+ autoDismissSplash?: boolean;
30
+ }
31
+ declare function CapacitorProvider({ children, autoDismissSplash, }: CapacitorProviderProps): React.ReactElement;
32
+ declare function useCapacitor(): CapacitorContextValue;
33
+
34
+ /**
35
+ * useSafeArea returns the current safe-area inset values and re-reads them
36
+ * on orientation change. Consumer components inset their layout with these
37
+ * numbers when they can't (or don't want to) use the env() CSS vars directly
38
+ * — e.g. computing a virtual list's first-row offset.
39
+ */
40
+
41
+ declare function useSafeArea(): SafeAreaInsets;
42
+
43
+ /**
44
+ * <BugReportButton> — TestFlight bug-report widget React component.
45
+ *
46
+ * Drop it once in a Capacitor consumer app (typically a floating button in
47
+ * a beta-only nav surface) and beta testers get a one-tap path to send a
48
+ * message + optional screenshot + environment context to the consumer's
49
+ * backend (which fans out to Slack, Sentry, etc.).
50
+ *
51
+ * Visual chrome is intentionally tiny — consumers wrap this in their own
52
+ * styled trigger if they want. The default appearance is a small floating
53
+ * pill in the bottom-right with a "Bug" label.
54
+ */
55
+
56
+ interface BugReportButtonProps {
57
+ /** Subscriber / user id, attached to every submission. */
58
+ userIdentifier?: string;
59
+ /** Override the default floating-pill chrome with your own. */
60
+ renderTrigger?: (open: () => void) => React.ReactNode;
61
+ /** Extra metadata merged into every submission. */
62
+ extra?: Record<string, unknown>;
63
+ /** Called after a successful submission — close UI, show toast, etc. */
64
+ onSubmitted?: () => void;
65
+ }
66
+ declare function BugReportButton({ userIdentifier, renderTrigger, extra, onSubmitted, }: BugReportButtonProps): React.ReactElement;
67
+
68
+ export { BugReportButton, type BugReportButtonProps, type CapacitorContextValue, CapacitorProvider, useCapacitor, useSafeArea };
@@ -0,0 +1,68 @@
1
+ import * as React from 'react';
2
+ import { P as Platform, S as SafeAreaInsets } from '../safe-area-B67yGQOn.js';
3
+
4
+ /**
5
+ * <CapacitorProvider> is the one-line install for consumer apps. Mount it at
6
+ * the top of the tree and every child gets useCapacitor() with platform info,
7
+ * safe-area insets, and a stable dismissSplash() callback.
8
+ *
9
+ * On mount the provider also installs the --safe-area-inset-* CSS vars and
10
+ * marks the document body with a data-platform="ios|android|web" attribute
11
+ * so CSS can target native chrome without JS.
12
+ */
13
+
14
+ interface CapacitorContextValue {
15
+ platform: Platform;
16
+ isNative: boolean;
17
+ isIOS: boolean;
18
+ isAndroid: boolean;
19
+ dismissSplash: (fadeOutDuration?: number) => Promise<void>;
20
+ }
21
+ interface CapacitorProviderProps {
22
+ children: React.ReactNode;
23
+ /**
24
+ * Dismiss the native splash screen automatically on first child render.
25
+ * Defaults to true — consumer apps almost always want this; set false if
26
+ * the splash should stay visible until some later signal (e.g. after auth
27
+ * bootstrap).
28
+ */
29
+ autoDismissSplash?: boolean;
30
+ }
31
+ declare function CapacitorProvider({ children, autoDismissSplash, }: CapacitorProviderProps): React.ReactElement;
32
+ declare function useCapacitor(): CapacitorContextValue;
33
+
34
+ /**
35
+ * useSafeArea returns the current safe-area inset values and re-reads them
36
+ * on orientation change. Consumer components inset their layout with these
37
+ * numbers when they can't (or don't want to) use the env() CSS vars directly
38
+ * — e.g. computing a virtual list's first-row offset.
39
+ */
40
+
41
+ declare function useSafeArea(): SafeAreaInsets;
42
+
43
+ /**
44
+ * <BugReportButton> — TestFlight bug-report widget React component.
45
+ *
46
+ * Drop it once in a Capacitor consumer app (typically a floating button in
47
+ * a beta-only nav surface) and beta testers get a one-tap path to send a
48
+ * message + optional screenshot + environment context to the consumer's
49
+ * backend (which fans out to Slack, Sentry, etc.).
50
+ *
51
+ * Visual chrome is intentionally tiny — consumers wrap this in their own
52
+ * styled trigger if they want. The default appearance is a small floating
53
+ * pill in the bottom-right with a "Bug" label.
54
+ */
55
+
56
+ interface BugReportButtonProps {
57
+ /** Subscriber / user id, attached to every submission. */
58
+ userIdentifier?: string;
59
+ /** Override the default floating-pill chrome with your own. */
60
+ renderTrigger?: (open: () => void) => React.ReactNode;
61
+ /** Extra metadata merged into every submission. */
62
+ extra?: Record<string, unknown>;
63
+ /** Called after a successful submission — close UI, show toast, etc. */
64
+ onSubmitted?: () => void;
65
+ }
66
+ declare function BugReportButton({ userIdentifier, renderTrigger, extra, onSubmitted, }: BugReportButtonProps): React.ReactElement;
67
+
68
+ export { BugReportButton, type BugReportButtonProps, type CapacitorContextValue, CapacitorProvider, useCapacitor, useSafeArea };