@buoy-gg/shared-ui 7.0.21 → 7.0.23
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/lib/commonjs/JsModal.js +39 -16
- package/lib/commonjs/dataViewer/JsonStringNotice.js +59 -0
- package/lib/commonjs/dataViewer/ThemedSplitView.js +20 -1
- package/lib/commonjs/dataViewer/TimestampDeltaBar.js +96 -0
- package/lib/commonjs/dataViewer/index.js +52 -0
- package/lib/commonjs/dataViewer/jsonStringValue.js +94 -0
- package/lib/commonjs/dataViewer/timestampDiff.js +146 -0
- package/lib/commonjs/hooks/safe-area-impl.js +1 -1
- package/lib/commonjs/index.js +64 -1
- package/lib/commonjs/license/FeatureGate.js +43 -10
- package/lib/commonjs/license/FreeAccountPrompt.js +181 -0
- package/lib/commonjs/license/index.js +57 -42
- package/lib/commonjs/license/licenseModule.js +99 -0
- package/lib/commonjs/license/tierLimits.js +165 -0
- package/lib/commonjs/ui/backgrounds/BackgroundSwitcher.js +180 -0
- package/lib/commonjs/ui/backgrounds/README.md +115 -0
- package/lib/commonjs/ui/backgrounds/ToolBackground.js +74 -0
- package/lib/commonjs/ui/backgrounds/backgroundStore.js +116 -0
- package/lib/commonjs/ui/backgrounds/index.js +70 -0
- package/lib/commonjs/ui/backgrounds/primitives.js +333 -0
- package/lib/commonjs/ui/backgrounds/registry.js +112 -0
- package/lib/commonjs/ui/backgrounds/rng.js +72 -0
- package/lib/commonjs/ui/backgrounds/types.js +1 -0
- package/lib/commonjs/ui/backgrounds/variants/deepField.js +351 -0
- package/lib/commonjs/ui/backgrounds/variants/lattice.js +579 -0
- package/lib/commonjs/ui/backgrounds/variants/starfield.js +210 -0
- package/lib/commonjs/ui/index.js +15 -3
- package/lib/commonjs/utils/devServer.js +62 -0
- package/lib/commonjs/utils/index.js +13 -0
- package/lib/commonjs/utils/renderCaptureRegistry.js +32 -3
- package/lib/commonjs/utils/safeExpoRouter.js +27 -2
- package/lib/module/JsModal.js +39 -16
- package/lib/module/dataViewer/JsonStringNotice.js +54 -0
- package/lib/module/dataViewer/ThemedSplitView.js +20 -1
- package/lib/module/dataViewer/TimestampDeltaBar.js +91 -0
- package/lib/module/dataViewer/index.js +4 -0
- package/lib/module/dataViewer/jsonStringValue.js +89 -0
- package/lib/module/dataViewer/timestampDiff.js +139 -0
- package/lib/module/hooks/safe-area-impl.js +1 -1
- package/lib/module/index.js +5 -3
- package/lib/module/license/FeatureGate.js +43 -10
- package/lib/module/license/FreeAccountPrompt.js +176 -0
- package/lib/module/license/index.js +5 -40
- package/lib/module/license/licenseModule.js +92 -0
- package/lib/module/license/tierLimits.js +158 -0
- package/lib/module/ui/backgrounds/BackgroundSwitcher.js +176 -0
- package/lib/module/ui/backgrounds/README.md +115 -0
- package/lib/module/ui/backgrounds/ToolBackground.js +70 -0
- package/lib/module/ui/backgrounds/backgroundStore.js +111 -0
- package/lib/module/ui/backgrounds/index.js +9 -0
- package/lib/module/ui/backgrounds/primitives.js +328 -0
- package/lib/module/ui/backgrounds/registry.js +105 -0
- package/lib/module/ui/backgrounds/rng.js +65 -0
- package/lib/module/ui/backgrounds/types.js +1 -0
- package/lib/module/ui/backgrounds/variants/deepField.js +347 -0
- package/lib/module/ui/backgrounds/variants/lattice.js +575 -0
- package/lib/module/ui/backgrounds/variants/starfield.js +206 -0
- package/lib/module/ui/index.js +3 -0
- package/lib/module/utils/devServer.js +58 -0
- package/lib/module/utils/index.js +2 -1
- package/lib/module/utils/renderCaptureRegistry.js +32 -3
- package/lib/module/utils/safeExpoRouter.js +26 -2
- package/lib/typescript/commonjs/JsModal.d.ts.map +1 -1
- package/lib/typescript/commonjs/dataViewer/JsonStringNotice.d.ts +6 -0
- package/lib/typescript/commonjs/dataViewer/JsonStringNotice.d.ts.map +1 -0
- package/lib/typescript/commonjs/dataViewer/ThemedSplitView.d.ts.map +1 -1
- package/lib/typescript/commonjs/dataViewer/TimestampDeltaBar.d.ts +7 -0
- package/lib/typescript/commonjs/dataViewer/TimestampDeltaBar.d.ts.map +1 -0
- package/lib/typescript/commonjs/dataViewer/index.d.ts +6 -0
- package/lib/typescript/commonjs/dataViewer/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/dataViewer/jsonStringValue.d.ts +25 -0
- package/lib/typescript/commonjs/dataViewer/jsonStringValue.d.ts.map +1 -0
- package/lib/typescript/commonjs/dataViewer/timestampDiff.d.ts +55 -0
- package/lib/typescript/commonjs/dataViewer/timestampDiff.d.ts.map +1 -0
- package/lib/typescript/commonjs/hooks/safe-area-impl.d.ts +1 -1
- package/lib/typescript/commonjs/index.d.ts +4 -3
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/license/FeatureGate.d.ts +19 -3
- package/lib/typescript/commonjs/license/FeatureGate.d.ts.map +1 -1
- package/lib/typescript/commonjs/license/FreeAccountPrompt.d.ts +23 -0
- package/lib/typescript/commonjs/license/FreeAccountPrompt.d.ts.map +1 -0
- package/lib/typescript/commonjs/license/index.d.ts +6 -15
- package/lib/typescript/commonjs/license/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/license/licenseModule.d.ts +55 -0
- package/lib/typescript/commonjs/license/licenseModule.d.ts.map +1 -0
- package/lib/typescript/commonjs/license/tierLimits.d.ts +143 -0
- package/lib/typescript/commonjs/license/tierLimits.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/backgrounds/BackgroundSwitcher.d.ts +13 -0
- package/lib/typescript/commonjs/ui/backgrounds/BackgroundSwitcher.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/backgrounds/ToolBackground.d.ts +30 -0
- package/lib/typescript/commonjs/ui/backgrounds/ToolBackground.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/backgrounds/backgroundStore.d.ts +43 -0
- package/lib/typescript/commonjs/ui/backgrounds/backgroundStore.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/backgrounds/index.d.ts +7 -0
- package/lib/typescript/commonjs/ui/backgrounds/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/backgrounds/primitives.d.ts +106 -0
- package/lib/typescript/commonjs/ui/backgrounds/primitives.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/backgrounds/registry.d.ts +28 -0
- package/lib/typescript/commonjs/ui/backgrounds/registry.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/backgrounds/rng.d.ts +27 -0
- package/lib/typescript/commonjs/ui/backgrounds/rng.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/backgrounds/types.d.ts +14 -0
- package/lib/typescript/commonjs/ui/backgrounds/types.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/backgrounds/variants/deepField.d.ts +65 -0
- package/lib/typescript/commonjs/ui/backgrounds/variants/deepField.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/backgrounds/variants/lattice.d.ts +33 -0
- package/lib/typescript/commonjs/ui/backgrounds/variants/lattice.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/backgrounds/variants/starfield.d.ts +12 -0
- package/lib/typescript/commonjs/ui/backgrounds/variants/starfield.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/index.d.ts +1 -0
- package/lib/typescript/commonjs/ui/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/utils/devServer.d.ts +24 -0
- package/lib/typescript/commonjs/utils/devServer.d.ts.map +1 -0
- package/lib/typescript/commonjs/utils/index.d.ts +3 -2
- package/lib/typescript/commonjs/utils/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/utils/renderCaptureRegistry.d.ts +52 -2
- package/lib/typescript/commonjs/utils/renderCaptureRegistry.d.ts.map +1 -1
- package/lib/typescript/commonjs/utils/safeExpoRouter.d.ts +23 -0
- package/lib/typescript/commonjs/utils/safeExpoRouter.d.ts.map +1 -1
- package/lib/typescript/module/JsModal.d.ts.map +1 -1
- package/lib/typescript/module/dataViewer/JsonStringNotice.d.ts +6 -0
- package/lib/typescript/module/dataViewer/JsonStringNotice.d.ts.map +1 -0
- package/lib/typescript/module/dataViewer/ThemedSplitView.d.ts.map +1 -1
- package/lib/typescript/module/dataViewer/TimestampDeltaBar.d.ts +7 -0
- package/lib/typescript/module/dataViewer/TimestampDeltaBar.d.ts.map +1 -0
- package/lib/typescript/module/dataViewer/index.d.ts +6 -0
- package/lib/typescript/module/dataViewer/index.d.ts.map +1 -1
- package/lib/typescript/module/dataViewer/jsonStringValue.d.ts +25 -0
- package/lib/typescript/module/dataViewer/jsonStringValue.d.ts.map +1 -0
- package/lib/typescript/module/dataViewer/timestampDiff.d.ts +55 -0
- package/lib/typescript/module/dataViewer/timestampDiff.d.ts.map +1 -0
- package/lib/typescript/module/hooks/safe-area-impl.d.ts +1 -1
- package/lib/typescript/module/index.d.ts +4 -3
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/license/FeatureGate.d.ts +19 -3
- package/lib/typescript/module/license/FeatureGate.d.ts.map +1 -1
- package/lib/typescript/module/license/FreeAccountPrompt.d.ts +23 -0
- package/lib/typescript/module/license/FreeAccountPrompt.d.ts.map +1 -0
- package/lib/typescript/module/license/index.d.ts +6 -15
- package/lib/typescript/module/license/index.d.ts.map +1 -1
- package/lib/typescript/module/license/licenseModule.d.ts +55 -0
- package/lib/typescript/module/license/licenseModule.d.ts.map +1 -0
- package/lib/typescript/module/license/tierLimits.d.ts +143 -0
- package/lib/typescript/module/license/tierLimits.d.ts.map +1 -0
- package/lib/typescript/module/ui/backgrounds/BackgroundSwitcher.d.ts +13 -0
- package/lib/typescript/module/ui/backgrounds/BackgroundSwitcher.d.ts.map +1 -0
- package/lib/typescript/module/ui/backgrounds/ToolBackground.d.ts +30 -0
- package/lib/typescript/module/ui/backgrounds/ToolBackground.d.ts.map +1 -0
- package/lib/typescript/module/ui/backgrounds/backgroundStore.d.ts +43 -0
- package/lib/typescript/module/ui/backgrounds/backgroundStore.d.ts.map +1 -0
- package/lib/typescript/module/ui/backgrounds/index.d.ts +7 -0
- package/lib/typescript/module/ui/backgrounds/index.d.ts.map +1 -0
- package/lib/typescript/module/ui/backgrounds/primitives.d.ts +106 -0
- package/lib/typescript/module/ui/backgrounds/primitives.d.ts.map +1 -0
- package/lib/typescript/module/ui/backgrounds/registry.d.ts +28 -0
- package/lib/typescript/module/ui/backgrounds/registry.d.ts.map +1 -0
- package/lib/typescript/module/ui/backgrounds/rng.d.ts +27 -0
- package/lib/typescript/module/ui/backgrounds/rng.d.ts.map +1 -0
- package/lib/typescript/module/ui/backgrounds/types.d.ts +14 -0
- package/lib/typescript/module/ui/backgrounds/types.d.ts.map +1 -0
- package/lib/typescript/module/ui/backgrounds/variants/deepField.d.ts +65 -0
- package/lib/typescript/module/ui/backgrounds/variants/deepField.d.ts.map +1 -0
- package/lib/typescript/module/ui/backgrounds/variants/lattice.d.ts +33 -0
- package/lib/typescript/module/ui/backgrounds/variants/lattice.d.ts.map +1 -0
- package/lib/typescript/module/ui/backgrounds/variants/starfield.d.ts +12 -0
- package/lib/typescript/module/ui/backgrounds/variants/starfield.d.ts.map +1 -0
- package/lib/typescript/module/ui/index.d.ts +1 -0
- package/lib/typescript/module/ui/index.d.ts.map +1 -1
- package/lib/typescript/module/utils/devServer.d.ts +24 -0
- package/lib/typescript/module/utils/devServer.d.ts.map +1 -0
- package/lib/typescript/module/utils/index.d.ts +3 -2
- package/lib/typescript/module/utils/index.d.ts.map +1 -1
- package/lib/typescript/module/utils/renderCaptureRegistry.d.ts +52 -2
- package/lib/typescript/module/utils/renderCaptureRegistry.d.ts.map +1 -1
- package/lib/typescript/module/utils/safeExpoRouter.d.ts +23 -0
- package/lib/typescript/module/utils/safeExpoRouter.d.ts.map +1 -1
- package/package.json +4 -4
- package/scripts/detect-safe-area.js +1 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* TimestampDeltaBar — reads out the timestamp pairs in a diff.
|
|
5
|
+
*
|
|
6
|
+
* Sits under the diff summary and translates `1787085897165 → 1787012175875`
|
|
7
|
+
* into "20h 29m earlier", with both instants spelled out. The raw values stay
|
|
8
|
+
* on the rows below: this annotates the diff, it never replaces it.
|
|
9
|
+
*
|
|
10
|
+
* Renders nothing when no side of the diff is confidently a timestamp — see
|
|
11
|
+
* timestampDiff.ts for how narrow "confidently" is.
|
|
12
|
+
*/
|
|
13
|
+
import { memo } from "react";
|
|
14
|
+
import { View, Text, StyleSheet } from "react-native";
|
|
15
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
16
|
+
export const TimestampDeltaBar = /*#__PURE__*/memo(function TimestampDeltaBar({
|
|
17
|
+
deltas,
|
|
18
|
+
theme
|
|
19
|
+
}) {
|
|
20
|
+
if (deltas.length === 0) return null;
|
|
21
|
+
return /*#__PURE__*/_jsx(View, {
|
|
22
|
+
style: [styles.container, {
|
|
23
|
+
backgroundColor: theme.summaryBackground,
|
|
24
|
+
borderTopColor: theme.borderColor
|
|
25
|
+
}],
|
|
26
|
+
children: deltas.map(delta => /*#__PURE__*/_jsxs(View, {
|
|
27
|
+
style: styles.row,
|
|
28
|
+
children: [/*#__PURE__*/_jsx(Text, {
|
|
29
|
+
style: [styles.glyph, {
|
|
30
|
+
color: theme.summaryModifiedText
|
|
31
|
+
}],
|
|
32
|
+
children: "\u25F7"
|
|
33
|
+
}), /*#__PURE__*/_jsxs(View, {
|
|
34
|
+
style: styles.text,
|
|
35
|
+
children: [/*#__PURE__*/_jsxs(Text, {
|
|
36
|
+
style: styles.headline,
|
|
37
|
+
children: [delta.path ? /*#__PURE__*/_jsxs(Text, {
|
|
38
|
+
style: [styles.path, {
|
|
39
|
+
color: theme.lineNumberText
|
|
40
|
+
}],
|
|
41
|
+
children: [delta.path, " "]
|
|
42
|
+
}) : null, /*#__PURE__*/_jsx(Text, {
|
|
43
|
+
style: {
|
|
44
|
+
color: theme.summaryModifiedText
|
|
45
|
+
},
|
|
46
|
+
children: delta.deltaLabel
|
|
47
|
+
})]
|
|
48
|
+
}), /*#__PURE__*/_jsxs(Text, {
|
|
49
|
+
style: [styles.instants, {
|
|
50
|
+
color: theme.lineNumberText
|
|
51
|
+
}],
|
|
52
|
+
children: [delta.fromLabel, " \u2192 ", delta.toLabel]
|
|
53
|
+
})]
|
|
54
|
+
})]
|
|
55
|
+
}, delta.path ?? "__value__"))
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
const styles = StyleSheet.create({
|
|
59
|
+
container: {
|
|
60
|
+
borderTopWidth: StyleSheet.hairlineWidth,
|
|
61
|
+
paddingHorizontal: 10,
|
|
62
|
+
paddingVertical: 7,
|
|
63
|
+
gap: 6
|
|
64
|
+
},
|
|
65
|
+
row: {
|
|
66
|
+
flexDirection: "row",
|
|
67
|
+
alignItems: "flex-start",
|
|
68
|
+
gap: 7
|
|
69
|
+
},
|
|
70
|
+
glyph: {
|
|
71
|
+
fontSize: 12,
|
|
72
|
+
lineHeight: 15
|
|
73
|
+
},
|
|
74
|
+
text: {
|
|
75
|
+
flex: 1,
|
|
76
|
+
minWidth: 0
|
|
77
|
+
},
|
|
78
|
+
headline: {
|
|
79
|
+
fontSize: 12,
|
|
80
|
+
fontWeight: "600",
|
|
81
|
+
lineHeight: 15
|
|
82
|
+
},
|
|
83
|
+
path: {
|
|
84
|
+
fontWeight: "400"
|
|
85
|
+
},
|
|
86
|
+
instants: {
|
|
87
|
+
fontSize: 11,
|
|
88
|
+
marginTop: 1,
|
|
89
|
+
fontVariant: ["tabular-nums"]
|
|
90
|
+
}
|
|
91
|
+
});
|
|
@@ -33,6 +33,10 @@ export { DataTreeActionDock } from "./DataTreeActionDock.js";
|
|
|
33
33
|
// Diff viewer components
|
|
34
34
|
export { SplitDiffViewer } from "./SplitDiffViewer.js";
|
|
35
35
|
export { DiffSummary } from "./DiffSummary.js";
|
|
36
|
+
export { TimestampDeltaBar } from "./TimestampDeltaBar.js";
|
|
37
|
+
export { JsonStringNotice } from "./JsonStringNotice.js";
|
|
38
|
+
export { decodeJsonStringPair, parseJsonContainer } from "./jsonStringValue.js";
|
|
39
|
+
export { detectTimestamp, describeTimestampDeltas, formatDuration, formatInstant } from "./timestampDiff.js";
|
|
36
40
|
// Diff utilities and themes
|
|
37
41
|
export { computeLineDiff, DiffType, ABSENT_VALUE, isAbsent } from "./lineDiff.js";
|
|
38
42
|
export { diffThemes, gitClassicTheme, devToolsDefaultTheme } from "./diffThemes.js";
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* JSON-encoded string values in diffs.
|
|
5
|
+
*
|
|
6
|
+
* Every device storage backend stores STRINGS, so apps write
|
|
7
|
+
* `JSON.stringify(["pikachu"])` and the stored value is the six-character
|
|
8
|
+
* string `["pikachu"]`. Rendering that through the diff's own
|
|
9
|
+
* `JSON.stringify` escapes it a second time and you get `"[\"pikachu\"]"` on
|
|
10
|
+
* one unreadable line — and a real change (one entry added to a list of
|
|
11
|
+
* twenty) becomes a single modified line you can't read.
|
|
12
|
+
*
|
|
13
|
+
* So: when a side is a JSON-encoded OBJECT OR ARRAY, diff the decoded value
|
|
14
|
+
* and say so on screen. The label is the whole contract — the store really
|
|
15
|
+
* does hold a string, and a viewer that quietly showed an array without
|
|
16
|
+
* mentioning it would be lying about the shape of the data.
|
|
17
|
+
*
|
|
18
|
+
* Scalars are deliberately NOT decoded. Turning the stored string `"5"` into
|
|
19
|
+
* the number 5, or `"true"` into a boolean, changes the apparent type while
|
|
20
|
+
* adding no readability — the exact misrepresentation this is trying to
|
|
21
|
+
* avoid.
|
|
22
|
+
*/
|
|
23
|
+
import { isAbsent } from "./lineDiff.js";
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Parse a string that encodes a JSON object or array. Returns a wrapper so a
|
|
27
|
+
* successfully decoded `null`-ish payload is still distinguishable from "not
|
|
28
|
+
* decodable" (the prefix check makes that unreachable today, but callers
|
|
29
|
+
* shouldn't have to know that).
|
|
30
|
+
*/
|
|
31
|
+
export function parseJsonContainer(value) {
|
|
32
|
+
if (typeof value !== "string") return null;
|
|
33
|
+
const trimmed = value.trim();
|
|
34
|
+
// Cheap gate before JSON.parse: only containers qualify, and this keeps a
|
|
35
|
+
// try/catch off the path for every ordinary string in a diff.
|
|
36
|
+
if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) return null;
|
|
37
|
+
try {
|
|
38
|
+
const parsed = JSON.parse(trimmed);
|
|
39
|
+
if (typeof parsed !== "object" || parsed === null) return null;
|
|
40
|
+
return {
|
|
41
|
+
value: parsed
|
|
42
|
+
};
|
|
43
|
+
} catch {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/** A side that carries no value at all — an add or a delete, not a change. */
|
|
48
|
+
function isMissing(value) {
|
|
49
|
+
return isAbsent(value) || value === undefined || value === null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Decode both sides of a diff when they are JSON-encoded containers.
|
|
54
|
+
*
|
|
55
|
+
* One side alone is decoded ONLY when the other side is missing entirely
|
|
56
|
+
* (a key being added or removed). A JSON string on one side and an ordinary
|
|
57
|
+
* value on the other is a change of shape, and decoding just one half would
|
|
58
|
+
* present it as something it isn't.
|
|
59
|
+
*/
|
|
60
|
+
export function decodeJsonStringPair(left, right) {
|
|
61
|
+
const decodedLeft = parseJsonContainer(left);
|
|
62
|
+
const decodedRight = parseJsonContainer(right);
|
|
63
|
+
if (decodedLeft && decodedRight) {
|
|
64
|
+
return {
|
|
65
|
+
left: decodedLeft.value,
|
|
66
|
+
right: decodedRight.value,
|
|
67
|
+
decoded: true
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
if (decodedLeft && isMissing(right)) {
|
|
71
|
+
return {
|
|
72
|
+
left: decodedLeft.value,
|
|
73
|
+
right,
|
|
74
|
+
decoded: true
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
if (decodedRight && isMissing(left)) {
|
|
78
|
+
return {
|
|
79
|
+
left,
|
|
80
|
+
right: decodedRight.value,
|
|
81
|
+
decoded: true
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
left,
|
|
86
|
+
right,
|
|
87
|
+
decoded: false
|
|
88
|
+
};
|
|
89
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Timestamp detection for diff views.
|
|
5
|
+
*
|
|
6
|
+
* `1787085897165 → 1787012175875` is unreadable. It is also the single most
|
|
7
|
+
* common diff in a devtool: `lastSyncTimestamp`, `expiresAt`, `updatedAt`,
|
|
8
|
+
* every cache stamp. This turns that pair into "20h 29m earlier" plus both
|
|
9
|
+
* instants spelled out, WITHOUT hiding the raw values — a devtool that
|
|
10
|
+
* silently reinterprets what is stored is worse than one that says nothing.
|
|
11
|
+
*
|
|
12
|
+
* DETECTION IS DELIBERATELY NARROW, because the failure mode is confidently
|
|
13
|
+
* mislabelling an id or a counter as a date:
|
|
14
|
+
* - Epoch milliseconds only in [2001-09-09, 2100), i.e. 13 digits.
|
|
15
|
+
* - Epoch seconds only in the same window, i.e. 10 digits.
|
|
16
|
+
* The two ranges cannot overlap, so a value is never ambiguous.
|
|
17
|
+
* - ISO 8601 strings that both match the shape AND parse.
|
|
18
|
+
* - BOTH sides must be the same kind. An ISO string against an epoch number
|
|
19
|
+
* is a representation change, not a time delta, and reporting a gap there
|
|
20
|
+
* would bury the more interesting fact.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/** Units we recognize. A pair must agree on one of them. */
|
|
24
|
+
|
|
25
|
+
/** 2001-09-09 — below this, a 13-digit-ish number is not a plausible stamp. */
|
|
26
|
+
const MS_MIN = 1_000_000_000_000;
|
|
27
|
+
/** 2100-01-01. */
|
|
28
|
+
const MS_MAX = 4_102_444_800_000;
|
|
29
|
+
const S_MIN = 1_000_000_000;
|
|
30
|
+
const S_MAX = 4_102_444_800;
|
|
31
|
+
|
|
32
|
+
/** Date, or date-time with optional seconds/fraction/zone. */
|
|
33
|
+
const ISO_RE = /^\d{4}-\d{2}-\d{2}(?:[T ]\d{2}:\d{2}(?::\d{2}(?:\.\d{1,9})?)?(?:Z|[+-]\d{2}:?\d{2})?)?$/;
|
|
34
|
+
function fromNumber(n) {
|
|
35
|
+
if (!Number.isInteger(n)) return null;
|
|
36
|
+
if (n >= MS_MIN && n < MS_MAX) return {
|
|
37
|
+
kind: "epoch-ms",
|
|
38
|
+
epochMs: n
|
|
39
|
+
};
|
|
40
|
+
if (n >= S_MIN && n < S_MAX) return {
|
|
41
|
+
kind: "epoch-s",
|
|
42
|
+
epochMs: n * 1000
|
|
43
|
+
};
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Read one value as an instant, or null when it isn't confidently a time.
|
|
49
|
+
* Numeric strings count: storage backends hand back everything as strings.
|
|
50
|
+
*/
|
|
51
|
+
export function detectTimestamp(value) {
|
|
52
|
+
if (value instanceof Date) {
|
|
53
|
+
const ms = value.getTime();
|
|
54
|
+
return Number.isFinite(ms) ? {
|
|
55
|
+
kind: "iso",
|
|
56
|
+
epochMs: ms
|
|
57
|
+
} : null;
|
|
58
|
+
}
|
|
59
|
+
if (typeof value === "number") return fromNumber(value);
|
|
60
|
+
if (typeof value !== "string") return null;
|
|
61
|
+
const trimmed = value.trim();
|
|
62
|
+
if (trimmed === "") return null;
|
|
63
|
+
if (/^\d+$/.test(trimmed)) return fromNumber(Number(trimmed));
|
|
64
|
+
if (!ISO_RE.test(trimmed)) return null;
|
|
65
|
+
const parsed = Date.parse(trimmed);
|
|
66
|
+
return Number.isFinite(parsed) ? {
|
|
67
|
+
kind: "iso",
|
|
68
|
+
epochMs: parsed
|
|
69
|
+
} : null;
|
|
70
|
+
}
|
|
71
|
+
function two(n) {
|
|
72
|
+
return n < 10 ? `0${n}` : String(n);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Local wall-clock, fixed format — a devtool wants it sortable, not chatty. */
|
|
76
|
+
export function formatInstant(epochMs) {
|
|
77
|
+
const d = new Date(epochMs);
|
|
78
|
+
return `${d.getFullYear()}-${two(d.getMonth() + 1)}-${two(d.getDate())} ` + `${two(d.getHours())}:${two(d.getMinutes())}:${two(d.getSeconds())}`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Coarsest two units that still carry the magnitude: "3d 4h", "5m 30s". */
|
|
82
|
+
export function formatDuration(ms) {
|
|
83
|
+
const abs = Math.abs(ms);
|
|
84
|
+
if (abs < 1000) return `${abs}ms`;
|
|
85
|
+
const seconds = Math.floor(abs / 1000);
|
|
86
|
+
if (seconds < 60) return `${seconds}s`;
|
|
87
|
+
const minutes = Math.floor(seconds / 60);
|
|
88
|
+
const restSeconds = seconds % 60;
|
|
89
|
+
if (minutes < 60) return restSeconds ? `${minutes}m ${restSeconds}s` : `${minutes}m`;
|
|
90
|
+
const hours = Math.floor(minutes / 60);
|
|
91
|
+
const restMinutes = minutes % 60;
|
|
92
|
+
if (hours < 24) return restMinutes ? `${hours}h ${restMinutes}m` : `${hours}h`;
|
|
93
|
+
const days = Math.floor(hours / 24);
|
|
94
|
+
const restHours = hours % 24;
|
|
95
|
+
return restHours ? `${days}d ${restHours}h` : `${days}d`;
|
|
96
|
+
}
|
|
97
|
+
function pair(oldValue, newValue, path) {
|
|
98
|
+
const before = detectTimestamp(oldValue);
|
|
99
|
+
const after = detectTimestamp(newValue);
|
|
100
|
+
if (!before || !after || before.kind !== after.kind) return null;
|
|
101
|
+
const deltaMs = after.epochMs - before.epochMs;
|
|
102
|
+
return {
|
|
103
|
+
path,
|
|
104
|
+
kind: before.kind,
|
|
105
|
+
deltaMs,
|
|
106
|
+
deltaLabel: deltaMs === 0 ? "same instant" : `${formatDuration(deltaMs)} ${deltaMs < 0 ? "earlier" : "later"}`,
|
|
107
|
+
fromLabel: formatInstant(before.epochMs),
|
|
108
|
+
toLabel: formatInstant(after.epochMs)
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function isPlainObject(value) {
|
|
112
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Date);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Beyond a handful the bar stops being a summary and becomes the diff. */
|
|
116
|
+
const MAX_DELTAS = 6;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Every timestamp pair worth explaining in a diff of these two values.
|
|
120
|
+
*
|
|
121
|
+
* Scalars give at most one entry. Plain objects are inspected ONE level deep
|
|
122
|
+
* — enough for the `{ updatedAt, expiresAt }` shape that dominates, without
|
|
123
|
+
* walking a whole document to hunt for dates.
|
|
124
|
+
*/
|
|
125
|
+
export function describeTimestampDeltas(oldValue, newValue) {
|
|
126
|
+
const scalar = pair(oldValue, newValue);
|
|
127
|
+
if (scalar) return [scalar];
|
|
128
|
+
if (!isPlainObject(oldValue) || !isPlainObject(newValue)) return [];
|
|
129
|
+
const out = [];
|
|
130
|
+
for (const key of Object.keys(oldValue)) {
|
|
131
|
+
if (out.length >= MAX_DELTAS) break;
|
|
132
|
+
if (!(key in newValue)) continue;
|
|
133
|
+
// Unchanged stamps are noise: the point is explaining what MOVED.
|
|
134
|
+
if (Object.is(oldValue[key], newValue[key])) continue;
|
|
135
|
+
const entry = pair(oldValue[key], newValue[key], key);
|
|
136
|
+
if (entry) out.push(entry);
|
|
137
|
+
}
|
|
138
|
+
return out;
|
|
139
|
+
}
|
package/lib/module/index.js
CHANGED
|
@@ -33,13 +33,15 @@ registerStateSnapshotProvider, getStateSnapshotProviders, getStateSnapshotProvid
|
|
|
33
33
|
superJsonSerialize, superJsonDeserialize,
|
|
34
34
|
// JS-bundle reload (core's `app` adapter + perf-monitor's batch runner)
|
|
35
35
|
reloadApp, scheduleReloadApp, resolveReloadStrategy, REMOTE_RELOAD_DELAY_MS,
|
|
36
|
+
// Dev-server origin — how a remote driver recovers an app that crashed too
|
|
37
|
+
// hard to reload itself
|
|
38
|
+
getDevServerOrigin,
|
|
36
39
|
// Subscribable base class for self-managing listeners
|
|
37
40
|
Subscribable,
|
|
38
41
|
// Subscriber count notifier for cross-package notifications
|
|
39
42
|
subscriberCountNotifier, subscribeToSubscriberCountChanges, notifySubscriberCountChange,
|
|
40
43
|
// Safe expo-router wrappers (falls back to no-ops on RN CLI)
|
|
41
|
-
useSafeRouter, useSafePathname, useSafeSegments, useSafeGlobalSearchParams, getSafeRouter, getSafeCurrentPathname, isExpoRouterAvailable } from "./utils/index.js";
|
|
42
|
-
|
|
44
|
+
useSafeRouter, useSafePathname, useSafeSegments, useSafeGlobalSearchParams, getSafeRouter, getSafeCurrentPathname, getSafeCurrentRoute, isExpoRouterAvailable } from "./utils/index.js";
|
|
43
45
|
// Also export formatting utils
|
|
44
46
|
export * from "./utils/formatting/index.js";
|
|
45
47
|
export * from "./utils/time/index.js";
|
|
@@ -80,4 +82,4 @@ export { HintsProvider, useHintsDisabled, useHintsContext } from "./context/inde
|
|
|
80
82
|
export * from "./history/index.js";
|
|
81
83
|
|
|
82
84
|
// License/Feature gating exports
|
|
83
|
-
export { FeatureGate, UpgradePrompt, useFeatureGate, ProFeatureBanner, UpgradeModal, LicenseEntryModal, ProBadge, ProUpgradeModal, getUseIsPro, getUseLicense } from "./license/index.js";
|
|
85
|
+
export { FeatureGate, UpgradePrompt, useFeatureGate, ProFeatureBanner, UpgradeModal, LicenseEntryModal, ProBadge, ProUpgradeModal, FreeAccountPrompt, LOGIN_COMMAND, getUseIsPro, getUseLicense, getUseTier, TIER_LIMITS, useLimit, resolveEffectiveTier, freeKeyRaises } from "./license/index.js";
|
|
@@ -12,6 +12,9 @@ import { gameUIColors, buoyColors } from "../ui/gameUI/constants/gameUIColors.js
|
|
|
12
12
|
import { LockIcon, X, Zap, Shield, Clock, Check } from "../icons/lucide-icons.js";
|
|
13
13
|
import { LicenseEntryModal } from "./LicenseEntryModal.js";
|
|
14
14
|
import { openPricing, isWeb } from "./openPricing.js";
|
|
15
|
+
import { FreeAccountPrompt } from "./FreeAccountPrompt.js";
|
|
16
|
+
import { getUseTier } from "./licenseModule.js";
|
|
17
|
+
import { freeKeyRaises } from "./tierLimits.js";
|
|
15
18
|
import { useIsPro, useLicense, useProAccess } from "@buoy-gg/license";
|
|
16
19
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
17
20
|
// Weekend Pass branding — a distinct violet so it never reads as the gold PRO.
|
|
@@ -49,14 +52,19 @@ export const ProBadge = ({
|
|
|
49
52
|
export const ProUpgradeModal = ({
|
|
50
53
|
visible,
|
|
51
54
|
onClose,
|
|
52
|
-
featureName = "This feature"
|
|
55
|
+
featureName = "This feature",
|
|
56
|
+
limitKey
|
|
53
57
|
}) => {
|
|
54
58
|
// Simple wrapper around UpgradeModal with minimal description and no benefits
|
|
55
59
|
return /*#__PURE__*/_jsx(UpgradeModal, {
|
|
56
60
|
visible: visible,
|
|
57
61
|
onClose: onClose,
|
|
58
|
-
featureName: featureName
|
|
59
|
-
|
|
62
|
+
featureName: featureName
|
|
63
|
+
// Only meaningful when this upsell is about a LIMIT; without it the
|
|
64
|
+
// description below (which promises Pro) is the accurate message.
|
|
65
|
+
,
|
|
66
|
+
limitKey: limitKey,
|
|
67
|
+
description: limitKey ? undefined : `${featureName} requires a Pro subscription.`
|
|
60
68
|
});
|
|
61
69
|
};
|
|
62
70
|
/**
|
|
@@ -66,9 +74,13 @@ export const ProUpgradeModal = ({
|
|
|
66
74
|
export const UpgradePrompt = ({
|
|
67
75
|
featureName,
|
|
68
76
|
onUpgradePress,
|
|
69
|
-
compact = false
|
|
77
|
+
compact = false,
|
|
78
|
+
limitKey
|
|
70
79
|
}) => {
|
|
71
80
|
const [showLicenseModal, setShowLicenseModal] = useState(false);
|
|
81
|
+
// Only offer the free key when it would actually raise this limit — see
|
|
82
|
+
// UpgradeModalProps.limitKey.
|
|
83
|
+
const isAnonymous = getUseTier()() === "anonymous" && limitKey != null && freeKeyRaises(limitKey);
|
|
72
84
|
const handlePress = useCallback(() => {
|
|
73
85
|
if (onUpgradePress) {
|
|
74
86
|
onUpgradePress();
|
|
@@ -96,7 +108,11 @@ export const UpgradePrompt = ({
|
|
|
96
108
|
style: styles.compactText,
|
|
97
109
|
children: "PRO"
|
|
98
110
|
})]
|
|
99
|
-
}), /*#__PURE__*/_jsx(
|
|
111
|
+
}), isAnonymous ? /*#__PURE__*/_jsx(FreeAccountPrompt, {
|
|
112
|
+
visible: showLicenseModal,
|
|
113
|
+
onClose: handleCloseModal,
|
|
114
|
+
featureName: featureName
|
|
115
|
+
}) : /*#__PURE__*/_jsx(LicenseEntryModal, {
|
|
100
116
|
visible: showLicenseModal,
|
|
101
117
|
onClose: handleCloseModal
|
|
102
118
|
})]
|
|
@@ -113,23 +129,27 @@ export const UpgradePrompt = ({
|
|
|
113
129
|
})
|
|
114
130
|
}), /*#__PURE__*/_jsx(Text, {
|
|
115
131
|
style: styles.title,
|
|
116
|
-
children: "Pro Feature"
|
|
132
|
+
children: isAnonymous ? "Get a free key" : "Pro Feature"
|
|
117
133
|
}), featureName && /*#__PURE__*/_jsx(Text, {
|
|
118
134
|
style: styles.featureName,
|
|
119
135
|
children: featureName
|
|
120
136
|
}), /*#__PURE__*/_jsx(Text, {
|
|
121
137
|
style: styles.description,
|
|
122
|
-
children: "Upgrade to React Buoy Pro to unlock this feature and more."
|
|
138
|
+
children: isAnonymous ? "Buoy works without an account, with a small slice of each tool. A free key opens it up." : "Upgrade to React Buoy Pro to unlock this feature and more."
|
|
123
139
|
}), /*#__PURE__*/_jsx(TouchableOpacity, {
|
|
124
140
|
style: styles.upgradeButton,
|
|
125
141
|
onPress: handlePress,
|
|
126
142
|
activeOpacity: 0.8,
|
|
127
143
|
children: /*#__PURE__*/_jsx(Text, {
|
|
128
144
|
style: styles.upgradeButtonText,
|
|
129
|
-
children: "Upgrade to Pro"
|
|
145
|
+
children: isAnonymous ? "Get a free key" : "Upgrade to Pro"
|
|
130
146
|
})
|
|
131
147
|
})]
|
|
132
|
-
}), /*#__PURE__*/_jsx(
|
|
148
|
+
}), isAnonymous ? /*#__PURE__*/_jsx(FreeAccountPrompt, {
|
|
149
|
+
visible: showLicenseModal,
|
|
150
|
+
onClose: handleCloseModal,
|
|
151
|
+
featureName: featureName
|
|
152
|
+
}) : /*#__PURE__*/_jsx(LicenseEntryModal, {
|
|
133
153
|
visible: showLicenseModal,
|
|
134
154
|
onClose: handleCloseModal
|
|
135
155
|
})]
|
|
@@ -308,8 +328,14 @@ export const UpgradeModal = ({
|
|
|
308
328
|
onClose,
|
|
309
329
|
featureName = "This Feature",
|
|
310
330
|
description,
|
|
311
|
-
benefits
|
|
331
|
+
benefits,
|
|
332
|
+
limitKey
|
|
312
333
|
}) => {
|
|
334
|
+
// Anonymous users get a different ask — but ONLY when a free key would
|
|
335
|
+
// actually fix what they just hit. Everything else here is Pro-exclusive,
|
|
336
|
+
// where a free key changes nothing and offering one would be dishonest.
|
|
337
|
+
const tier = getUseTier()();
|
|
338
|
+
const offerFreeKey = tier === "anonymous" && limitKey != null && freeKeyRaises(limitKey);
|
|
313
339
|
const hasBenefits = benefits && benefits.length > 0;
|
|
314
340
|
const [showLicenseModal, setShowLicenseModal] = useState(false);
|
|
315
341
|
const license = useLicense();
|
|
@@ -332,6 +358,13 @@ export const UpgradeModal = ({
|
|
|
332
358
|
onClose();
|
|
333
359
|
}, [onClose]);
|
|
334
360
|
if (!visible) return null;
|
|
361
|
+
if (offerFreeKey) {
|
|
362
|
+
return /*#__PURE__*/_jsx(FreeAccountPrompt, {
|
|
363
|
+
visible: visible,
|
|
364
|
+
onClose: onClose,
|
|
365
|
+
featureName: featureName === "This Feature" ? undefined : featureName
|
|
366
|
+
});
|
|
367
|
+
}
|
|
335
368
|
return /*#__PURE__*/_jsxs(_Fragment, {
|
|
336
369
|
children: [/*#__PURE__*/_jsx(Modal, {
|
|
337
370
|
visible: visible && !showLicenseModal,
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The upsell an ANONYMOUS user sees — someone with no key at all.
|
|
5
|
+
*
|
|
6
|
+
* Deliberately not the Pro modal. Their next step is free and takes about
|
|
7
|
+
* thirty seconds, so asking them for money would be asking the wrong question
|
|
8
|
+
* at the wrong moment, and would suppress exactly the signup this exists to
|
|
9
|
+
* produce. Pro is still offered, as a secondary action.
|
|
10
|
+
*
|
|
11
|
+
* The command is shown inline and copyable rather than linked, because there is
|
|
12
|
+
* no grace period: someone who just watched their limits shrink needs the fix
|
|
13
|
+
* in front of them, not a link to a page that has a link to a command.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import React, { useCallback, useEffect, useRef, useState } from "react";
|
|
17
|
+
import { Modal, StyleSheet, Text, TouchableOpacity } from "react-native";
|
|
18
|
+
import { buoyColors } from "../ui/gameUI/constants/gameUIColors.js";
|
|
19
|
+
import { Check, Copy, X } from "../icons/lucide-icons.js";
|
|
20
|
+
import { copyToClipboard } from "../clipboard/index.js";
|
|
21
|
+
import { openPricing } from "./openPricing.js";
|
|
22
|
+
|
|
23
|
+
/** The one thing a reader has to be able to act on. */
|
|
24
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
25
|
+
export const LOGIN_COMMAND = "npx buoy login";
|
|
26
|
+
export const FreeAccountPrompt = ({
|
|
27
|
+
visible,
|
|
28
|
+
onClose,
|
|
29
|
+
featureName
|
|
30
|
+
}) => {
|
|
31
|
+
const [copied, setCopied] = useState(false);
|
|
32
|
+
const resetTimer = useRef(null);
|
|
33
|
+
useEffect(() => () => {
|
|
34
|
+
if (resetTimer.current) clearTimeout(resetTimer.current);
|
|
35
|
+
}, []);
|
|
36
|
+
const handleCopy = useCallback(() => {
|
|
37
|
+
void copyToClipboard(LOGIN_COMMAND).then(ok => {
|
|
38
|
+
if (!ok) return;
|
|
39
|
+
setCopied(true);
|
|
40
|
+
if (resetTimer.current) clearTimeout(resetTimer.current);
|
|
41
|
+
resetTimer.current = setTimeout(() => setCopied(false), 1500);
|
|
42
|
+
});
|
|
43
|
+
}, []);
|
|
44
|
+
if (!visible) return null;
|
|
45
|
+
return /*#__PURE__*/_jsx(Modal, {
|
|
46
|
+
visible: visible,
|
|
47
|
+
transparent: true,
|
|
48
|
+
animationType: "fade",
|
|
49
|
+
onRequestClose: onClose,
|
|
50
|
+
children: /*#__PURE__*/_jsx(TouchableOpacity, {
|
|
51
|
+
style: styles.overlay,
|
|
52
|
+
onPress: onClose,
|
|
53
|
+
activeOpacity: 1,
|
|
54
|
+
children: /*#__PURE__*/_jsxs(TouchableOpacity, {
|
|
55
|
+
style: styles.card,
|
|
56
|
+
activeOpacity: 1,
|
|
57
|
+
onPress: () => {},
|
|
58
|
+
children: [/*#__PURE__*/_jsx(TouchableOpacity, {
|
|
59
|
+
style: styles.close,
|
|
60
|
+
onPress: onClose,
|
|
61
|
+
activeOpacity: 0.7,
|
|
62
|
+
children: /*#__PURE__*/_jsx(X, {
|
|
63
|
+
size: 18,
|
|
64
|
+
color: buoyColors.textMuted
|
|
65
|
+
})
|
|
66
|
+
}), /*#__PURE__*/_jsx(Text, {
|
|
67
|
+
style: styles.title,
|
|
68
|
+
children: featureName ? `More ${featureName}` : "See more"
|
|
69
|
+
}), /*#__PURE__*/_jsx(Text, {
|
|
70
|
+
style: styles.subtitle,
|
|
71
|
+
children: "Buoy runs without an account \u2014 you just get a small slice of each tool. A free key opens it up."
|
|
72
|
+
}), /*#__PURE__*/_jsxs(TouchableOpacity, {
|
|
73
|
+
style: styles.command,
|
|
74
|
+
onPress: handleCopy,
|
|
75
|
+
activeOpacity: 0.7,
|
|
76
|
+
accessibilityLabel: `Copy ${LOGIN_COMMAND}`,
|
|
77
|
+
children: [/*#__PURE__*/_jsx(Text, {
|
|
78
|
+
style: styles.commandText,
|
|
79
|
+
children: LOGIN_COMMAND
|
|
80
|
+
}), copied ? /*#__PURE__*/_jsx(Check, {
|
|
81
|
+
size: 14,
|
|
82
|
+
color: buoyColors.success
|
|
83
|
+
}) : /*#__PURE__*/_jsx(Copy, {
|
|
84
|
+
size: 14,
|
|
85
|
+
color: buoyColors.textMuted
|
|
86
|
+
})]
|
|
87
|
+
}), /*#__PURE__*/_jsx(Text, {
|
|
88
|
+
style: styles.reassure,
|
|
89
|
+
children: "Free forever. No card. About thirty seconds."
|
|
90
|
+
}), /*#__PURE__*/_jsx(TouchableOpacity, {
|
|
91
|
+
style: styles.secondary,
|
|
92
|
+
onPress: () => openPricing(),
|
|
93
|
+
activeOpacity: 0.8,
|
|
94
|
+
children: /*#__PURE__*/_jsx(Text, {
|
|
95
|
+
style: styles.secondaryText,
|
|
96
|
+
children: "Or see Pro"
|
|
97
|
+
})
|
|
98
|
+
})]
|
|
99
|
+
})
|
|
100
|
+
})
|
|
101
|
+
});
|
|
102
|
+
};
|
|
103
|
+
const styles = StyleSheet.create({
|
|
104
|
+
overlay: {
|
|
105
|
+
flex: 1,
|
|
106
|
+
backgroundColor: "rgba(0, 0, 0, 0.75)",
|
|
107
|
+
alignItems: "center",
|
|
108
|
+
justifyContent: "center",
|
|
109
|
+
padding: 20
|
|
110
|
+
},
|
|
111
|
+
card: {
|
|
112
|
+
backgroundColor: buoyColors.card,
|
|
113
|
+
borderRadius: 16,
|
|
114
|
+
borderWidth: 1,
|
|
115
|
+
borderColor: buoyColors.primary + "60",
|
|
116
|
+
padding: 24,
|
|
117
|
+
maxWidth: 340,
|
|
118
|
+
width: "100%",
|
|
119
|
+
position: "relative"
|
|
120
|
+
},
|
|
121
|
+
close: {
|
|
122
|
+
position: "absolute",
|
|
123
|
+
top: 12,
|
|
124
|
+
right: 12,
|
|
125
|
+
padding: 6,
|
|
126
|
+
zIndex: 1
|
|
127
|
+
},
|
|
128
|
+
title: {
|
|
129
|
+
fontSize: 20,
|
|
130
|
+
fontWeight: "700",
|
|
131
|
+
color: buoyColors.text,
|
|
132
|
+
textAlign: "center",
|
|
133
|
+
marginBottom: 8
|
|
134
|
+
},
|
|
135
|
+
subtitle: {
|
|
136
|
+
fontSize: 13,
|
|
137
|
+
lineHeight: 19,
|
|
138
|
+
color: buoyColors.textMuted,
|
|
139
|
+
textAlign: "center",
|
|
140
|
+
marginBottom: 20
|
|
141
|
+
},
|
|
142
|
+
command: {
|
|
143
|
+
flexDirection: "row",
|
|
144
|
+
alignItems: "center",
|
|
145
|
+
justifyContent: "space-between",
|
|
146
|
+
backgroundColor: buoyColors.base,
|
|
147
|
+
borderRadius: 8,
|
|
148
|
+
borderWidth: 1,
|
|
149
|
+
borderColor: buoyColors.border,
|
|
150
|
+
paddingHorizontal: 14,
|
|
151
|
+
paddingVertical: 12,
|
|
152
|
+
gap: 10
|
|
153
|
+
},
|
|
154
|
+
commandText: {
|
|
155
|
+
fontSize: 14,
|
|
156
|
+
fontWeight: "600",
|
|
157
|
+
color: buoyColors.text,
|
|
158
|
+
fontFamily: "monospace"
|
|
159
|
+
},
|
|
160
|
+
reassure: {
|
|
161
|
+
fontSize: 12,
|
|
162
|
+
color: buoyColors.textMuted,
|
|
163
|
+
textAlign: "center",
|
|
164
|
+
marginTop: 10
|
|
165
|
+
},
|
|
166
|
+
secondary: {
|
|
167
|
+
marginTop: 18,
|
|
168
|
+
paddingVertical: 10,
|
|
169
|
+
alignItems: "center"
|
|
170
|
+
},
|
|
171
|
+
secondaryText: {
|
|
172
|
+
fontSize: 13,
|
|
173
|
+
fontWeight: "600",
|
|
174
|
+
color: buoyColors.textMuted
|
|
175
|
+
}
|
|
176
|
+
});
|