@opengeni/react-native 4.2.1-canary.39593846423001 → 4.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/android/build.gradle +15 -0
  2. package/android/src/main/AndroidManifest.xml +1 -0
  3. package/android/src/main/java/dev/opengeni/textselection/OpenGeniTextSelectionModule.kt +274 -0
  4. package/dist/{chunk-D3MFBDVR.js → chunk-2RNMGRTB.js} +3 -3
  5. package/dist/{chunk-D3MFBDVR.js.map → chunk-2RNMGRTB.js.map} +1 -1
  6. package/dist/{chunk-2W74OY6I.js → chunk-FHPMRXIG.js} +16 -2
  7. package/dist/chunk-FHPMRXIG.js.map +1 -0
  8. package/dist/{chunk-63PZ52YJ.js → chunk-QFV5DISV.js} +38 -5
  9. package/dist/chunk-QFV5DISV.js.map +1 -0
  10. package/dist/{chunk-U5L2CUMJ.js → chunk-ZZNZBZWV.js} +3 -3
  11. package/dist/{chunk-U5L2CUMJ.js.map → chunk-ZZNZBZWV.js.map} +1 -1
  12. package/dist/index.js +2 -2
  13. package/dist/timeline/activity.d.ts +3 -1
  14. package/dist/timeline/annotations.d.ts +81 -0
  15. package/dist/timeline/icon-nodes.generated.d.ts +5 -0
  16. package/dist/timeline/index.d.ts +2 -0
  17. package/dist/timeline/index.js +1223 -203
  18. package/dist/timeline/index.js.map +1 -1
  19. package/dist/timeline/markdown.js +5 -2
  20. package/dist/timeline/markdown.js.map +1 -1
  21. package/dist/timeline/message-timeline.d.ts +20 -2
  22. package/dist/timeline/previews.js +2 -2
  23. package/dist/timeline/primitives.d.ts +7 -1
  24. package/dist/timeline/queue-dock.d.ts +4 -1
  25. package/dist/timeline/text-selection.d.ts +44 -0
  26. package/expo-module.config.json +5 -2
  27. package/ios/OpenGeniTextSelectionModule.swift +560 -0
  28. package/package.json +6 -4
  29. package/src/timeline/activity.tsx +76 -21
  30. package/src/timeline/annotations.tsx +877 -0
  31. package/src/timeline/icon-nodes.generated.ts +14 -0
  32. package/src/timeline/index.ts +2 -0
  33. package/src/timeline/markdown.tsx +7 -0
  34. package/src/timeline/message-timeline.tsx +340 -174
  35. package/src/timeline/primitives.tsx +50 -1
  36. package/src/timeline/queue-dock.tsx +36 -0
  37. package/src/timeline/session-screen.tsx +122 -4
  38. package/src/timeline/sheet.tsx +3 -2
  39. package/src/timeline/text-selection.ts +115 -0
  40. package/dist/chunk-2W74OY6I.js.map +0 -1
  41. package/dist/chunk-63PZ52YJ.js.map +0 -1
@@ -0,0 +1,15 @@
1
+ plugins {
2
+ id 'com.android.library'
3
+ id 'expo-module-gradle-plugin'
4
+ }
5
+
6
+ group = 'dev.opengeni'
7
+ version = '1.0.0'
8
+
9
+ android {
10
+ namespace "dev.opengeni.textselection"
11
+ defaultConfig {
12
+ versionCode 1
13
+ versionName '1.0.0'
14
+ }
15
+ }
@@ -0,0 +1 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android" />
@@ -0,0 +1,274 @@
1
+ package dev.opengeni.textselection
2
+
3
+ import android.app.Activity
4
+ import android.os.Build
5
+ import android.text.Selection
6
+ import android.text.Spannable
7
+ import android.view.ActionMode
8
+ import android.view.HapticFeedbackConstants
9
+ import android.view.Menu
10
+ import android.view.MenuItem
11
+ import android.view.View
12
+ import android.view.ViewGroup
13
+ import android.view.Window
14
+ import android.widget.TextView
15
+ import expo.modules.kotlin.functions.Queues
16
+ import expo.modules.kotlin.modules.Module
17
+ import expo.modules.kotlin.modules.ModuleDefinition
18
+
19
+ /**
20
+ * Add note on selected timeline text. Android's selectable text already
21
+ * selects any range with the system handles and menu; inside a view whose
22
+ * test id starts with `og-annotation-source:` and names a source, the menu
23
+ * also offers Add note while JavaScript listens, reporting the selected text
24
+ * and its surroundings.
25
+ */
26
+ class OpenGeniTextSelectionModule : Module() {
27
+ override fun definition() = ModuleDefinition {
28
+ Name("OpenGeniTextSelection")
29
+
30
+ Events("onAnnotate")
31
+
32
+ OnCreate { install() }
33
+
34
+ OnActivityEntersForeground { install() }
35
+
36
+ OnStartObserving {
37
+ TextSelectionState.emit = { event -> sendEvent("onAnnotate", event) }
38
+ }
39
+
40
+ OnStopObserving { TextSelectionState.emit = null }
41
+
42
+ /** The Add note title, localized by JavaScript; null hides Add note. */
43
+ Function("setAnnotateTitle") { title: String? -> TextSelectionState.title = title }
44
+
45
+ /**
46
+ * Where each draft quote is drawn inside a source, in the source view's
47
+ * coordinates (dp): `{ x, top, rects }`, the end of its last glyph, the
48
+ * top of that line's text and one text box per line (flattened x, y,
49
+ * width, height), or an empty map when the quote is not drawn there.
50
+ */
51
+ AsyncFunction("quoteAnchors") { sourceKey: String, queries: List<Map<String, String>> ->
52
+ QuoteAnchors.measure(appContext.currentActivity, sourceKey, queries)
53
+ }.runOnQueue(Queues.MAIN)
54
+ }
55
+
56
+ private fun install() {
57
+ val activity = appContext.currentActivity ?: return
58
+ activity.runOnUiThread {
59
+ val window = activity.window ?: return@runOnUiThread
60
+ val callback = window.callback ?: return@runOnUiThread
61
+ if (callback !is AnnotationWindowCallback) {
62
+ window.callback = AnnotationWindowCallback(callback, activity)
63
+ }
64
+ }
65
+ }
66
+ }
67
+
68
+ internal object TextSelectionState {
69
+ const val SOURCE_PREFIX = "og-annotation-source:"
70
+ var emit: ((Map<String, Any?>) -> Unit)? = null
71
+ var title: String? = null
72
+
73
+ /** The source key of the timeline message `view` sits in, if any. */
74
+ fun sourceKey(view: View): String? {
75
+ var current: View? = view
76
+ while (current != null) {
77
+ val tag = current.tag
78
+ if (tag is String && tag.startsWith(SOURCE_PREFIX)) return tag.removePrefix(SOURCE_PREFIX)
79
+ current = current.parent as? View
80
+ }
81
+ return null
82
+ }
83
+ }
84
+
85
+ internal object QuoteAnchors {
86
+ fun measure(
87
+ activity: Activity?,
88
+ sourceKey: String,
89
+ queries: List<Map<String, String>>,
90
+ ): List<Map<String, Any>> {
91
+ val none = queries.map { emptyMap<String, Any>() }
92
+ val root = activity?.window?.decorView ?: return none
93
+ val source =
94
+ root.findViewWithTag<View>(TextSelectionState.SOURCE_PREFIX + sourceKey) ?: return none
95
+ val texts = mutableListOf<TextView>()
96
+ collect(source, texts)
97
+ return queries.map { query ->
98
+ val quote = query["quote"]?.trim().orEmpty()
99
+ if (quote.isEmpty()) return@map emptyMap<String, Any>()
100
+ var best: Triple<TextView, Int, Int>? = null
101
+ for (view in texts) {
102
+ val string = view.text?.toString() ?: continue
103
+ var index = string.indexOf(quote)
104
+ while (index >= 0) {
105
+ val score =
106
+ commonSuffix(string.substring(0, index), query["before"].orEmpty()) +
107
+ commonPrefix(string.substring(index + quote.length), query["after"].orEmpty())
108
+ if (best == null || score > best.third) best = Triple(view, index, score)
109
+ index = string.indexOf(quote, index + 1)
110
+ }
111
+ }
112
+ best?.let { anchor(it.first, it.second, it.second + quote.length, source) }
113
+ ?: emptyMap()
114
+ }
115
+ }
116
+
117
+ private fun anchor(view: TextView, start: Int, end: Int, source: View): Map<String, Any> {
118
+ val layout = view.layout ?: return emptyMap()
119
+ val string = view.text.toString()
120
+ val density = view.resources.displayMetrics.density
121
+ val viewAt = IntArray(2).also(view::getLocationInWindow)
122
+ val sourceAt = IntArray(2).also(source::getLocationInWindow)
123
+ val dx = (viewAt[0] - sourceAt[0] + view.totalPaddingLeft).toFloat()
124
+ val dy = (viewAt[1] - sourceAt[1] + view.totalPaddingTop).toFloat()
125
+ val metrics = view.paint.fontMetrics
126
+ // The end of the last glyph: measured from its start, since the offset
127
+ // after a line's last character belongs to the next line.
128
+ val endX =
129
+ layout.getPrimaryHorizontal(end - 1) + view.paint.measureText(string, end - 1, end)
130
+ val firstLine = layout.getLineForOffset(start)
131
+ val lastLine = layout.getLineForOffset(end - 1)
132
+ val rects = mutableListOf<Double>()
133
+ for (line in firstLine..lastLine) {
134
+ val left = if (line == firstLine) layout.getPrimaryHorizontal(start) else layout.getLineLeft(line)
135
+ val right = if (line == lastLine) endX else layout.getLineRight(line)
136
+ val baseline = layout.getLineBaseline(line).toFloat()
137
+ val top = baseline + metrics.ascent
138
+ rects += listOf(
139
+ ((dx + left) / density).toDouble(),
140
+ ((dy + top) / density).toDouble(),
141
+ ((right - left) / density).toDouble(),
142
+ ((metrics.descent - metrics.ascent) / density).toDouble(),
143
+ )
144
+ }
145
+ val top = layout.getLineBaseline(lastLine) + metrics.ascent
146
+ return mapOf(
147
+ "x" to ((dx + endX) / density).toDouble(),
148
+ "top" to ((dy + top) / density).toDouble(),
149
+ "rects" to rects,
150
+ )
151
+ }
152
+
153
+ private fun collect(view: View, into: MutableList<TextView>) {
154
+ if (view.visibility != View.VISIBLE) return
155
+ if (view is TextView) {
156
+ into += view
157
+ return
158
+ }
159
+ if (view is ViewGroup) for (index in 0 until view.childCount) collect(view.getChildAt(index), into)
160
+ }
161
+
162
+ private fun commonSuffix(a: String, b: String): Int {
163
+ var count = 0
164
+ while (count < a.length && count < b.length && a[a.length - 1 - count] == b[b.length - 1 - count]) count++
165
+ return count
166
+ }
167
+
168
+ private fun commonPrefix(a: String, b: String): Int {
169
+ var count = 0
170
+ while (count < a.length && count < b.length && a[count] == b[count]) count++
171
+ return count
172
+ }
173
+ }
174
+
175
+ /**
176
+ * Watches the activity's window callback for the floating text-selection menu
177
+ * a selectable text view starts. For a timeline source it attaches Add note
178
+ * through the view's own custom selection callback, which the system menu
179
+ * consults while it builds its items (the window callback cannot change that
180
+ * menu itself: the system builds floating menus from its own copy of the
181
+ * callback). Every call still goes to the original callback unchanged.
182
+ */
183
+ internal class AnnotationWindowCallback(
184
+ private val base: Window.Callback,
185
+ private val activity: Activity,
186
+ ) : Window.Callback by base {
187
+ override fun onWindowStartingActionMode(callback: ActionMode.Callback, type: Int): ActionMode? {
188
+ if (type == ActionMode.TYPE_FLOATING) attach()
189
+ return base.onWindowStartingActionMode(callback, type)
190
+ }
191
+
192
+ override fun onWindowStartingActionMode(callback: ActionMode.Callback): ActionMode? =
193
+ base.onWindowStartingActionMode(callback)
194
+
195
+ private fun attach() {
196
+ val title = TextSelectionState.title ?: return
197
+ if (TextSelectionState.emit == null) return
198
+ // A read-only selectable text view rarely takes focus: find the text view
199
+ // that holds the selection this menu is for.
200
+ val selected =
201
+ (activity.currentFocus as? TextView)?.takeIf { it.isTextSelectable && it.hasSelection() }
202
+ ?: activity.window?.decorView?.let(::selectedTextView)
203
+ ?: return
204
+ val key = TextSelectionState.sourceKey(selected)
205
+ if (key.isNullOrEmpty()) return
206
+ val current = selected.customSelectionActionModeCallback
207
+ val previous = if (current is AnnotateCallback) current.previous else current
208
+ selected.customSelectionActionModeCallback = AnnotateCallback(previous, selected, key, title)
209
+ }
210
+
211
+ private fun selectedTextView(view: View): TextView? {
212
+ if (view.visibility != View.VISIBLE) return null
213
+ if (view is TextView) return view.takeIf { it.isTextSelectable && it.hasSelection() }
214
+ if (view !is ViewGroup) return null
215
+ for (index in 0 until view.childCount) {
216
+ selectedTextView(view.getChildAt(index))?.let { return it }
217
+ }
218
+ return null
219
+ }
220
+ }
221
+
222
+ /**
223
+ * Adds Add note to a text view's selection menu, ahead of the system items,
224
+ * and reports the selection with its surroundings when it is chosen. Any
225
+ * custom callback the view already had keeps running.
226
+ */
227
+ private class AnnotateCallback(
228
+ val previous: ActionMode.Callback?,
229
+ private val view: TextView,
230
+ private val sourceKey: String,
231
+ private val title: String,
232
+ ) : ActionMode.Callback {
233
+ override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
234
+ if (previous?.onCreateActionMode(mode, menu) == false) return false
235
+ if (menu.findItem(ITEM_ID) == null) {
236
+ menu.add(Menu.NONE, ITEM_ID, 0, title).setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM)
237
+ }
238
+ return true
239
+ }
240
+
241
+ override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean =
242
+ previous?.onPrepareActionMode(mode, menu) ?: false
243
+
244
+ override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
245
+ if (item.itemId != ITEM_ID) return previous?.onActionItemClicked(mode, item) ?: false
246
+ val text = view.text
247
+ val start = minOf(view.selectionStart, view.selectionEnd)
248
+ val end = maxOf(view.selectionStart, view.selectionEnd)
249
+ if (start < 0 || end <= start || end > text.length) return true
250
+ TextSelectionState.emit?.invoke(
251
+ mapOf(
252
+ "sourceKey" to sourceKey,
253
+ "quote" to text.subSequence(start, end).toString(),
254
+ "before" to text.subSequence(0, start).toString(),
255
+ "after" to text.subSequence(end, text.length).toString(),
256
+ ),
257
+ )
258
+ view.performHapticFeedback(
259
+ if (Build.VERSION.SDK_INT >= 30) HapticFeedbackConstants.CONFIRM
260
+ else HapticFeedbackConstants.VIRTUAL_KEY,
261
+ )
262
+ mode.finish()
263
+ (view.text as? Spannable)?.let { Selection.removeSelection(it) }
264
+ return true
265
+ }
266
+
267
+ override fun onDestroyActionMode(mode: ActionMode) {
268
+ previous?.onDestroyActionMode(mode)
269
+ }
270
+
271
+ private companion object {
272
+ const val ITEM_ID = 0x4f47414e
273
+ }
274
+ }
@@ -1,4 +1,4 @@
1
- import { useNativeTimelineTheme } from './chunk-2W74OY6I.js';
1
+ import { useNativeTimelineTheme } from './chunk-FHPMRXIG.js';
2
2
  import { resolvePreset, MODE_FRAMES } from 'thinking-orbs/engine';
3
3
  import { useMemo, useState, useEffect } from 'react';
4
4
  import { AppState, AccessibilityInfo } from 'react-native';
@@ -261,5 +261,5 @@ function ThinkingOrb({
261
261
  }
262
262
 
263
263
  export { SECRET_SAVE_TO_OPTIONS, ThinkingOrb, defaultSecretFieldMessages, humanInputAnswerLines, initialSecretDraft, isSecretQuestion, secretInputTraits, secretPatternMismatch, secretSubmission, secretUsernameTraits };
264
- //# sourceMappingURL=chunk-D3MFBDVR.js.map
265
- //# sourceMappingURL=chunk-D3MFBDVR.js.map
264
+ //# sourceMappingURL=chunk-2RNMGRTB.js.map
265
+ //# sourceMappingURL=chunk-2RNMGRTB.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/timeline/secret-field-model.ts","../src/timeline/thinking-orb.tsx"],"names":[],"mappings":"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","file":"chunk-D3MFBDVR.js","sourcesContent":["import type {\n GeneratePasswordSpec,\n HumanInputAnswer,\n HumanInputQuestion,\n HumanInputResponse,\n HumanInputSecretAnswer,\n HumanInputSecretAnswerResult,\n HumanInputSecretSaveTo,\n HumanInputSecretSpec,\n} from \"@opengeni/sdk\";\nimport { type OpenGeniText } from \"@opengeni/react/messages\";\n\n/* ----------------------------------------------------------------------------\n Pure rules for the native secret question: which keyboard and autofill\n traits the field gets, what a submission sends, and how a saved answer\n reads. No React Native imports, so the rules are unit-tested under bun.\n -------------------------------------------------------------------------- */\n\n/**\n * - `current-password` / `new-password`: the OS password manager fills or\n * suggests a password.\n * - `one-time-code`: a code the person just received (number pad, SMS autofill).\n * - `setup-key`: a TOTP seed (`fieldKind: \"totp\"` stores the seed, not a code).\n * - `off`: an API key, token or note; no autofill.\n */\nexport type SecretInputMode =\n | \"current-password\"\n | \"new-password\"\n | \"one-time-code\"\n | \"setup-key\"\n | \"off\";\n\nexport function secretInputMode(spec: HumanInputSecretSpec | null | undefined): SecretInputMode {\n const autocomplete = spec?.autocomplete;\n if (autocomplete === \"one-time-code\") return \"one-time-code\";\n if (autocomplete === \"new-password\") return \"new-password\";\n if (autocomplete === \"current-password\") return \"current-password\";\n if (autocomplete === \"off\") return spec?.fieldKind === \"totp\" ? \"setup-key\" : \"off\";\n if (spec?.fieldKind === \"totp\") return \"setup-key\";\n if (spec?.allowGenerate) return \"new-password\";\n if (spec?.itemType === \"api_key\" || spec?.itemType === \"note\") return \"off\";\n return \"current-password\";\n}\n\n/** The `TextInput` props that make the field secure and autofill-aware. */\nexport type SecretInputTraits = {\n mode: SecretInputMode;\n secureTextEntry: true;\n autoCorrect: false;\n autoCapitalize: \"none\";\n spellCheck: false;\n /** iOS. */\n textContentType: \"password\" | \"newPassword\" | \"oneTimeCode\" | \"none\";\n /** Android autofill hint (RN maps it to the platform constant). */\n autoComplete: \"password\" | \"password-new\" | \"sms-otp\" | \"off\";\n /** Android. */\n importantForAutofill: \"yes\" | \"no\";\n keyboardType: \"default\" | \"number-pad\";\n /** iOS strong-password rules, from the question's generator spec. */\n passwordRules?: string | undefined;\n};\n\nexport function secretInputTraits(\n spec: HumanInputSecretSpec | null | undefined,\n): SecretInputTraits {\n const mode = secretInputMode(spec);\n const base = {\n mode,\n secureTextEntry: true,\n autoCorrect: false,\n autoCapitalize: \"none\",\n spellCheck: false,\n } as const;\n switch (mode) {\n case \"current-password\":\n return {\n ...base,\n textContentType: \"password\",\n autoComplete: \"password\",\n importantForAutofill: \"yes\",\n keyboardType: \"default\",\n };\n case \"new-password\": {\n const passwordRules = spec?.generate ? applePasswordRules(spec.generate) : undefined;\n return {\n ...base,\n textContentType: \"newPassword\",\n autoComplete: \"password-new\",\n importantForAutofill: \"yes\",\n keyboardType: \"default\",\n ...(passwordRules ? { passwordRules } : {}),\n };\n }\n case \"one-time-code\":\n return {\n ...base,\n textContentType: \"oneTimeCode\",\n autoComplete: \"sms-otp\",\n importantForAutofill: \"yes\",\n keyboardType: \"number-pad\",\n };\n case \"setup-key\":\n case \"off\":\n return {\n ...base,\n textContentType: \"none\",\n autoComplete: \"off\",\n importantForAutofill: \"no\",\n keyboardType: \"default\",\n };\n }\n}\n\n/** The username field paired with the password, so the OS saves them together. */\nexport const secretUsernameTraits = {\n textContentType: \"username\",\n autoComplete: \"username\",\n importantForAutofill: \"yes\",\n autoCorrect: false,\n autoCapitalize: \"none\",\n spellCheck: false,\n} as const;\n\n/**\n * Apple password-rules markup for the iOS strong-password suggestion, from the\n * same spec the server generates with (defaults as in `@opengeni/contracts`).\n */\nexport function applePasswordRules(spec: GeneratePasswordSpec): string {\n const rules: string[] = [];\n const lower = spec.lowercase ?? true;\n const upper = spec.uppercase ?? true;\n const digits = spec.digits ?? true;\n const symbols = spec.symbols ?? true;\n const requireEach = spec.requireEachClass ?? true;\n // In the rules grammar a hyphen is literal only first in a class, and \"]\"\n // or a quote cannot appear at all.\n const rawSymbols = spec.symbolSet ?? \"!#%+-.:=?@_\";\n const symbolSet = (rawSymbols.includes(\"-\") ? \"-\" : \"\") + rawSymbols.replace(/[-\\]\"]/gu, \"\");\n const classes = [\n lower ? \"lower\" : null,\n upper ? \"upper\" : null,\n digits ? \"digit\" : null,\n symbols && symbolSet ? `[${symbolSet}]` : null,\n ].filter((entry): entry is string => entry !== null);\n if (requireEach) for (const entry of classes) rules.push(`required: ${entry};`);\n else rules.push(`allowed: ${classes.join(\", \")};`);\n rules.push(`minlength: ${spec.length ?? 24};`);\n return rules.join(\" \");\n}\n\n/** What the person picked for one secret question. The value itself lives in a ref. */\nexport type SecretDraft = {\n generate: boolean;\n username: string;\n saveTo: HumanInputSecretSaveTo;\n};\n\nexport const SECRET_SAVE_TO_OPTIONS: readonly HumanInputSecretSaveTo[] = [\n \"user\",\n \"task\",\n \"workspace\",\n];\n\nexport function initialSecretDraft(spec: HumanInputSecretSpec | null | undefined): SecretDraft {\n return {\n generate: false,\n username: spec?.username?.value ?? \"\",\n saveTo: spec?.saveTo ?? \"user\",\n };\n}\n\nexport function isSecretQuestion(question: Pick<HumanInputQuestion, \"kind\">): boolean {\n return question.kind === \"secret\";\n}\n\n/**\n * The secret half of a submission. Each answered secret question sends an\n * empty `answers` entry and its value (or a generate spec) only in\n * `secretAnswers`, which the API strips before anything is stored.\n */\nexport function secretSubmission(\n questions: readonly HumanInputQuestion[],\n drafts: Readonly<Record<string, SecretDraft | undefined>>,\n values: Readonly<Record<string, string | undefined>>,\n requiredMessage: string,\n): {\n answers: HumanInputAnswer[];\n secretAnswers: HumanInputSecretAnswer[];\n errors: Record<string, string>;\n} {\n const answers: HumanInputAnswer[] = [];\n const secretAnswers: HumanInputSecretAnswer[] = [];\n const errors: Record<string, string> = {};\n for (const question of questions) {\n if (!isSecretQuestion(question)) continue;\n const spec = question.secret;\n const draft = drafts[question.id] ?? initialSecretDraft(spec);\n const value = values[question.id] ?? \"\";\n const supplied = draft.generate || value.length > 0;\n if (!supplied) {\n if (question.required) errors[question.id] = requiredMessage;\n continue;\n }\n const username = draft.username.trim();\n answers.push({ questionId: question.id, values: [] });\n secretAnswers.push({\n questionId: question.id,\n ...(draft.generate ? { generate: spec?.generate ?? {} } : { value }),\n ...(spec?.username && username ? { username } : {}),\n saveTo: draft.saveTo,\n });\n }\n return { answers, secretAnswers, errors };\n}\n\n/**\n * A soft check against the question's `pattern` (anchored, like the HTML\n * attribute). Never blocks; an invalid pattern never warns.\n */\nexport function secretPatternMismatch(pattern: string | null | undefined, value: string): boolean {\n if (!pattern || value.length === 0) return false;\n let expression: RegExp;\n try {\n expression = new RegExp(`^(?:${pattern})$`, \"u\");\n } catch {\n return false;\n }\n return !expression.test(value);\n}\n\nexport type SecretFieldMessages = {\n saveTo: string;\n saveToOption: Record<HumanInputSecretSaveTo, string>;\n vaultHint: (vault: string) => string;\n show: string;\n hide: string;\n generate: string;\n generated: string;\n undo: string;\n looksLike: (example: string) => string;\n patternWarning: string;\n placeholder: Record<SecretInputMode, string>;\n saved: (vaultName: string, itemName: string) => string;\n};\n\nexport const defaultSecretFieldMessages = (t: OpenGeniText): SecretFieldMessages => ({\n saveTo: t(\"native.timeline.secretFieldModel.saveTo\"),\n saveToOption: {\n user: t(\"native.timeline.secretFieldModel.onlyMe\"),\n task: t(\"native.timeline.secretFieldModel.thisTask\"),\n workspace: t(\"native.timeline.secretFieldModel.workspace\"),\n },\n vaultHint: (vault) => t(\"native.timeline.secretFieldModel.savesToTheVaultVault\", { vault }),\n show: t(\"native.timeline.secretFieldModel.showSecret\"),\n hide: t(\"native.timeline.secretFieldModel.hideSecret\"),\n generate: t(\"native.timeline.secretFieldModel.generate\"),\n generated: t(\"native.timeline.secretFieldModel.aStrongPasswordWillBe\"),\n undo: t(\"native.timeline.secretFieldModel.undo\"),\n looksLike: (example) => t(\"native.timeline.secretFieldModel.looksLikeExample\", { example }),\n patternWarning: t(\"native.timeline.secretFieldModel.thisDoesnTMatchThe\"),\n placeholder: {\n \"current-password\": t(\"native.timeline.secretFieldModel.password\"),\n \"new-password\": t(\"native.timeline.secretFieldModel.newPassword\"),\n \"one-time-code\": t(\"native.timeline.secretFieldModel.code\"),\n \"setup-key\": t(\"native.timeline.secretFieldModel.setupKeyOrOtpauthLink\"),\n off: t(\"native.timeline.secretFieldModel.value\"),\n },\n saved: (vaultName, itemName) =>\n t(\"native.timeline.secretFieldModel.savedToVaultnameItemname\", { vaultName, itemName }),\n});\n\n/** The answered chip: where the value went, never the value. */\nexport function secretSavedLabel(\n t: OpenGeniText,\n result: Pick<HumanInputSecretAnswerResult, \"vaultName\" | \"itemName\">,\n messages: Pick<SecretFieldMessages, \"saved\"> = defaultSecretFieldMessages(t),\n): string {\n return messages.saved(result.vaultName, result.itemName);\n}\n\n/**\n * The lines of a settled answer bubble. A secret answer reads as where it was\n * saved (\"Saved to Private › GitHub\"); its value never reaches the timeline.\n */\nexport function humanInputAnswerLines(\n answers: readonly { questionId: string; label: string; values: readonly string[] }[],\n response: HumanInputResponse,\n saved: (vaultName: string, itemName: string) => string,\n multiple: boolean,\n): { questionId: string; text: string; secret: boolean }[] {\n const secrets = new Map(\n response.outcome === \"answered\"\n ? response.answers.flatMap((answer) =>\n answer.secret ? [[answer.questionId, answer.secret] as const] : [],\n )\n : [],\n );\n return answers.map((answer) => {\n const prefix = multiple ? `${answer.label}: ` : \"\";\n const secret = secrets.get(answer.questionId);\n return secret\n ? {\n questionId: answer.questionId,\n text: `${prefix}${saved(secret.vaultName, secret.itemName)}`,\n secret: true,\n }\n : {\n questionId: answer.questionId,\n text: `${prefix}${answer.values.join(\", \")}`,\n secret: false,\n };\n });\n}\n","import { MODE_FRAMES, resolvePreset, type OrbState } from \"thinking-orbs/engine\";\nimport { useEffect, useMemo, useState } from \"react\";\nimport { AccessibilityInfo, AppState } from \"react-native\";\nimport Svg, { Circle, Line } from \"react-native-svg\";\nimport { useNativeTimelineTheme } from \"./theme\";\n\n/** About 30 frames a second: smooth at 64pt without spending a frame budget. */\nconst FRAME_MS = 33;\n\nfunction ink(white: number, alpha: number | undefined, dark: boolean): string {\n // The engine's convention: 0 is the darkest ink on paper, mirrored on dark.\n const level = Math.round((dark ? 1 - Math.min(1, Math.max(0, white)) : white) * 255);\n return `rgba(${level},${level},${level},${alpha ?? 1})`;\n}\n\n/**\n * The web's ThinkingOrb on native: the same `thinking-orbs` geometry (one\n * frame of dots and edges per instant) drawn with SVG. Pauses in the\n * background and holds a still frame under Reduce Motion, as the web does.\n */\nexport function ThinkingOrb({\n state = \"searching\",\n size = 64,\n speed = 0.8,\n displaySize,\n}: {\n state?: OrbState;\n size?: 64 | 20;\n speed?: number;\n /** Draw the tuned preset larger (for example a call screen) without losing sharpness. */\n displaySize?: number;\n}) {\n const theme = useNativeTimelineTheme();\n const dark = theme.scheme === \"dark\";\n const preset = useMemo(() => resolvePreset(state, size), [state, size]);\n const [time, setTime] = useState(0.6);\n const [reduceMotion, setReduceMotion] = useState(false);\n const [active, setActive] = useState(AppState.currentState === \"active\");\n useEffect(() => {\n void AccessibilityInfo.isReduceMotionEnabled().then(setReduceMotion);\n const motion = AccessibilityInfo.addEventListener(\"reduceMotionChanged\", setReduceMotion);\n const app = AppState.addEventListener(\"change\", (next) => setActive(next === \"active\"));\n return () => {\n motion.remove();\n app.remove();\n };\n }, []);\n useEffect(() => {\n if (reduceMotion || !active) return;\n const started = Date.now();\n const timer = setInterval(() => setTime((Date.now() - started) / 1000), FRAME_MS);\n return () => clearInterval(timer);\n }, [active, reduceMotion]);\n const frame = MODE_FRAMES[preset.mode](\n size,\n reduceMotion ? 0.6 : time * preset.speed * speed,\n preset.opts,\n );\n return (\n <Svg\n width={displaySize ?? size}\n height={displaySize ?? size}\n viewBox={`0 0 ${size} ${size}`}\n accessibilityElementsHidden\n importantForAccessibility=\"no\"\n >\n {frame.lines.map((line, index) => (\n <Line\n // Stateless drawing slots retain their keys as coordinates animate.\n // oxlint-disable-next-line react/no-array-index-key\n key={`l${index}`}\n x1={line.x1}\n y1={line.y1}\n x2={line.x2}\n y2={line.y2}\n stroke={ink(line.white, line.a, dark)}\n strokeWidth={line.w}\n />\n ))}\n {frame.dots.map((dot, index) => (\n <Circle\n // Stateless drawing slots retain their keys as coordinates animate.\n // oxlint-disable-next-line react/no-array-index-key\n key={`d${index}`}\n cx={dot.x}\n cy={dot.y}\n r={dot.r}\n fill={ink(dot.white, dot.a, dark)}\n />\n ))}\n </Svg>\n );\n}\n"]}
1
+ {"version":3,"sources":["../src/timeline/secret-field-model.ts","../src/timeline/thinking-orb.tsx"],"names":[],"mappings":"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","file":"chunk-2RNMGRTB.js","sourcesContent":["import type {\n GeneratePasswordSpec,\n HumanInputAnswer,\n HumanInputQuestion,\n HumanInputResponse,\n HumanInputSecretAnswer,\n HumanInputSecretAnswerResult,\n HumanInputSecretSaveTo,\n HumanInputSecretSpec,\n} from \"@opengeni/sdk\";\nimport { type OpenGeniText } from \"@opengeni/react/messages\";\n\n/* ----------------------------------------------------------------------------\n Pure rules for the native secret question: which keyboard and autofill\n traits the field gets, what a submission sends, and how a saved answer\n reads. No React Native imports, so the rules are unit-tested under bun.\n -------------------------------------------------------------------------- */\n\n/**\n * - `current-password` / `new-password`: the OS password manager fills or\n * suggests a password.\n * - `one-time-code`: a code the person just received (number pad, SMS autofill).\n * - `setup-key`: a TOTP seed (`fieldKind: \"totp\"` stores the seed, not a code).\n * - `off`: an API key, token or note; no autofill.\n */\nexport type SecretInputMode =\n | \"current-password\"\n | \"new-password\"\n | \"one-time-code\"\n | \"setup-key\"\n | \"off\";\n\nexport function secretInputMode(spec: HumanInputSecretSpec | null | undefined): SecretInputMode {\n const autocomplete = spec?.autocomplete;\n if (autocomplete === \"one-time-code\") return \"one-time-code\";\n if (autocomplete === \"new-password\") return \"new-password\";\n if (autocomplete === \"current-password\") return \"current-password\";\n if (autocomplete === \"off\") return spec?.fieldKind === \"totp\" ? \"setup-key\" : \"off\";\n if (spec?.fieldKind === \"totp\") return \"setup-key\";\n if (spec?.allowGenerate) return \"new-password\";\n if (spec?.itemType === \"api_key\" || spec?.itemType === \"note\") return \"off\";\n return \"current-password\";\n}\n\n/** The `TextInput` props that make the field secure and autofill-aware. */\nexport type SecretInputTraits = {\n mode: SecretInputMode;\n secureTextEntry: true;\n autoCorrect: false;\n autoCapitalize: \"none\";\n spellCheck: false;\n /** iOS. */\n textContentType: \"password\" | \"newPassword\" | \"oneTimeCode\" | \"none\";\n /** Android autofill hint (RN maps it to the platform constant). */\n autoComplete: \"password\" | \"password-new\" | \"sms-otp\" | \"off\";\n /** Android. */\n importantForAutofill: \"yes\" | \"no\";\n keyboardType: \"default\" | \"number-pad\";\n /** iOS strong-password rules, from the question's generator spec. */\n passwordRules?: string | undefined;\n};\n\nexport function secretInputTraits(\n spec: HumanInputSecretSpec | null | undefined,\n): SecretInputTraits {\n const mode = secretInputMode(spec);\n const base = {\n mode,\n secureTextEntry: true,\n autoCorrect: false,\n autoCapitalize: \"none\",\n spellCheck: false,\n } as const;\n switch (mode) {\n case \"current-password\":\n return {\n ...base,\n textContentType: \"password\",\n autoComplete: \"password\",\n importantForAutofill: \"yes\",\n keyboardType: \"default\",\n };\n case \"new-password\": {\n const passwordRules = spec?.generate ? applePasswordRules(spec.generate) : undefined;\n return {\n ...base,\n textContentType: \"newPassword\",\n autoComplete: \"password-new\",\n importantForAutofill: \"yes\",\n keyboardType: \"default\",\n ...(passwordRules ? { passwordRules } : {}),\n };\n }\n case \"one-time-code\":\n return {\n ...base,\n textContentType: \"oneTimeCode\",\n autoComplete: \"sms-otp\",\n importantForAutofill: \"yes\",\n keyboardType: \"number-pad\",\n };\n case \"setup-key\":\n case \"off\":\n return {\n ...base,\n textContentType: \"none\",\n autoComplete: \"off\",\n importantForAutofill: \"no\",\n keyboardType: \"default\",\n };\n }\n}\n\n/** The username field paired with the password, so the OS saves them together. */\nexport const secretUsernameTraits = {\n textContentType: \"username\",\n autoComplete: \"username\",\n importantForAutofill: \"yes\",\n autoCorrect: false,\n autoCapitalize: \"none\",\n spellCheck: false,\n} as const;\n\n/**\n * Apple password-rules markup for the iOS strong-password suggestion, from the\n * same spec the server generates with (defaults as in `@opengeni/contracts`).\n */\nexport function applePasswordRules(spec: GeneratePasswordSpec): string {\n const rules: string[] = [];\n const lower = spec.lowercase ?? true;\n const upper = spec.uppercase ?? true;\n const digits = spec.digits ?? true;\n const symbols = spec.symbols ?? true;\n const requireEach = spec.requireEachClass ?? true;\n // In the rules grammar a hyphen is literal only first in a class, and \"]\"\n // or a quote cannot appear at all.\n const rawSymbols = spec.symbolSet ?? \"!#%+-.:=?@_\";\n const symbolSet = (rawSymbols.includes(\"-\") ? \"-\" : \"\") + rawSymbols.replace(/[-\\]\"]/gu, \"\");\n const classes = [\n lower ? \"lower\" : null,\n upper ? \"upper\" : null,\n digits ? \"digit\" : null,\n symbols && symbolSet ? `[${symbolSet}]` : null,\n ].filter((entry): entry is string => entry !== null);\n if (requireEach) for (const entry of classes) rules.push(`required: ${entry};`);\n else rules.push(`allowed: ${classes.join(\", \")};`);\n rules.push(`minlength: ${spec.length ?? 24};`);\n return rules.join(\" \");\n}\n\n/** What the person picked for one secret question. The value itself lives in a ref. */\nexport type SecretDraft = {\n generate: boolean;\n username: string;\n saveTo: HumanInputSecretSaveTo;\n};\n\nexport const SECRET_SAVE_TO_OPTIONS: readonly HumanInputSecretSaveTo[] = [\n \"user\",\n \"task\",\n \"workspace\",\n];\n\nexport function initialSecretDraft(spec: HumanInputSecretSpec | null | undefined): SecretDraft {\n return {\n generate: false,\n username: spec?.username?.value ?? \"\",\n saveTo: spec?.saveTo ?? \"user\",\n };\n}\n\nexport function isSecretQuestion(question: Pick<HumanInputQuestion, \"kind\">): boolean {\n return question.kind === \"secret\";\n}\n\n/**\n * The secret half of a submission. Each answered secret question sends an\n * empty `answers` entry and its value (or a generate spec) only in\n * `secretAnswers`, which the API strips before anything is stored.\n */\nexport function secretSubmission(\n questions: readonly HumanInputQuestion[],\n drafts: Readonly<Record<string, SecretDraft | undefined>>,\n values: Readonly<Record<string, string | undefined>>,\n requiredMessage: string,\n): {\n answers: HumanInputAnswer[];\n secretAnswers: HumanInputSecretAnswer[];\n errors: Record<string, string>;\n} {\n const answers: HumanInputAnswer[] = [];\n const secretAnswers: HumanInputSecretAnswer[] = [];\n const errors: Record<string, string> = {};\n for (const question of questions) {\n if (!isSecretQuestion(question)) continue;\n const spec = question.secret;\n const draft = drafts[question.id] ?? initialSecretDraft(spec);\n const value = values[question.id] ?? \"\";\n const supplied = draft.generate || value.length > 0;\n if (!supplied) {\n if (question.required) errors[question.id] = requiredMessage;\n continue;\n }\n const username = draft.username.trim();\n answers.push({ questionId: question.id, values: [] });\n secretAnswers.push({\n questionId: question.id,\n ...(draft.generate ? { generate: spec?.generate ?? {} } : { value }),\n ...(spec?.username && username ? { username } : {}),\n saveTo: draft.saveTo,\n });\n }\n return { answers, secretAnswers, errors };\n}\n\n/**\n * A soft check against the question's `pattern` (anchored, like the HTML\n * attribute). Never blocks; an invalid pattern never warns.\n */\nexport function secretPatternMismatch(pattern: string | null | undefined, value: string): boolean {\n if (!pattern || value.length === 0) return false;\n let expression: RegExp;\n try {\n expression = new RegExp(`^(?:${pattern})$`, \"u\");\n } catch {\n return false;\n }\n return !expression.test(value);\n}\n\nexport type SecretFieldMessages = {\n saveTo: string;\n saveToOption: Record<HumanInputSecretSaveTo, string>;\n vaultHint: (vault: string) => string;\n show: string;\n hide: string;\n generate: string;\n generated: string;\n undo: string;\n looksLike: (example: string) => string;\n patternWarning: string;\n placeholder: Record<SecretInputMode, string>;\n saved: (vaultName: string, itemName: string) => string;\n};\n\nexport const defaultSecretFieldMessages = (t: OpenGeniText): SecretFieldMessages => ({\n saveTo: t(\"native.timeline.secretFieldModel.saveTo\"),\n saveToOption: {\n user: t(\"native.timeline.secretFieldModel.onlyMe\"),\n task: t(\"native.timeline.secretFieldModel.thisTask\"),\n workspace: t(\"native.timeline.secretFieldModel.workspace\"),\n },\n vaultHint: (vault) => t(\"native.timeline.secretFieldModel.savesToTheVaultVault\", { vault }),\n show: t(\"native.timeline.secretFieldModel.showSecret\"),\n hide: t(\"native.timeline.secretFieldModel.hideSecret\"),\n generate: t(\"native.timeline.secretFieldModel.generate\"),\n generated: t(\"native.timeline.secretFieldModel.aStrongPasswordWillBe\"),\n undo: t(\"native.timeline.secretFieldModel.undo\"),\n looksLike: (example) => t(\"native.timeline.secretFieldModel.looksLikeExample\", { example }),\n patternWarning: t(\"native.timeline.secretFieldModel.thisDoesnTMatchThe\"),\n placeholder: {\n \"current-password\": t(\"native.timeline.secretFieldModel.password\"),\n \"new-password\": t(\"native.timeline.secretFieldModel.newPassword\"),\n \"one-time-code\": t(\"native.timeline.secretFieldModel.code\"),\n \"setup-key\": t(\"native.timeline.secretFieldModel.setupKeyOrOtpauthLink\"),\n off: t(\"native.timeline.secretFieldModel.value\"),\n },\n saved: (vaultName, itemName) =>\n t(\"native.timeline.secretFieldModel.savedToVaultnameItemname\", { vaultName, itemName }),\n});\n\n/** The answered chip: where the value went, never the value. */\nexport function secretSavedLabel(\n t: OpenGeniText,\n result: Pick<HumanInputSecretAnswerResult, \"vaultName\" | \"itemName\">,\n messages: Pick<SecretFieldMessages, \"saved\"> = defaultSecretFieldMessages(t),\n): string {\n return messages.saved(result.vaultName, result.itemName);\n}\n\n/**\n * The lines of a settled answer bubble. A secret answer reads as where it was\n * saved (\"Saved to Private › GitHub\"); its value never reaches the timeline.\n */\nexport function humanInputAnswerLines(\n answers: readonly { questionId: string; label: string; values: readonly string[] }[],\n response: HumanInputResponse,\n saved: (vaultName: string, itemName: string) => string,\n multiple: boolean,\n): { questionId: string; text: string; secret: boolean }[] {\n const secrets = new Map(\n response.outcome === \"answered\"\n ? response.answers.flatMap((answer) =>\n answer.secret ? [[answer.questionId, answer.secret] as const] : [],\n )\n : [],\n );\n return answers.map((answer) => {\n const prefix = multiple ? `${answer.label}: ` : \"\";\n const secret = secrets.get(answer.questionId);\n return secret\n ? {\n questionId: answer.questionId,\n text: `${prefix}${saved(secret.vaultName, secret.itemName)}`,\n secret: true,\n }\n : {\n questionId: answer.questionId,\n text: `${prefix}${answer.values.join(\", \")}`,\n secret: false,\n };\n });\n}\n","import { MODE_FRAMES, resolvePreset, type OrbState } from \"thinking-orbs/engine\";\nimport { useEffect, useMemo, useState } from \"react\";\nimport { AccessibilityInfo, AppState } from \"react-native\";\nimport Svg, { Circle, Line } from \"react-native-svg\";\nimport { useNativeTimelineTheme } from \"./theme\";\n\n/** About 30 frames a second: smooth at 64pt without spending a frame budget. */\nconst FRAME_MS = 33;\n\nfunction ink(white: number, alpha: number | undefined, dark: boolean): string {\n // The engine's convention: 0 is the darkest ink on paper, mirrored on dark.\n const level = Math.round((dark ? 1 - Math.min(1, Math.max(0, white)) : white) * 255);\n return `rgba(${level},${level},${level},${alpha ?? 1})`;\n}\n\n/**\n * The web's ThinkingOrb on native: the same `thinking-orbs` geometry (one\n * frame of dots and edges per instant) drawn with SVG. Pauses in the\n * background and holds a still frame under Reduce Motion, as the web does.\n */\nexport function ThinkingOrb({\n state = \"searching\",\n size = 64,\n speed = 0.8,\n displaySize,\n}: {\n state?: OrbState;\n size?: 64 | 20;\n speed?: number;\n /** Draw the tuned preset larger (for example a call screen) without losing sharpness. */\n displaySize?: number;\n}) {\n const theme = useNativeTimelineTheme();\n const dark = theme.scheme === \"dark\";\n const preset = useMemo(() => resolvePreset(state, size), [state, size]);\n const [time, setTime] = useState(0.6);\n const [reduceMotion, setReduceMotion] = useState(false);\n const [active, setActive] = useState(AppState.currentState === \"active\");\n useEffect(() => {\n void AccessibilityInfo.isReduceMotionEnabled().then(setReduceMotion);\n const motion = AccessibilityInfo.addEventListener(\"reduceMotionChanged\", setReduceMotion);\n const app = AppState.addEventListener(\"change\", (next) => setActive(next === \"active\"));\n return () => {\n motion.remove();\n app.remove();\n };\n }, []);\n useEffect(() => {\n if (reduceMotion || !active) return;\n const started = Date.now();\n const timer = setInterval(() => setTime((Date.now() - started) / 1000), FRAME_MS);\n return () => clearInterval(timer);\n }, [active, reduceMotion]);\n const frame = MODE_FRAMES[preset.mode](\n size,\n reduceMotion ? 0.6 : time * preset.speed * speed,\n preset.opts,\n );\n return (\n <Svg\n width={displaySize ?? size}\n height={displaySize ?? size}\n viewBox={`0 0 ${size} ${size}`}\n accessibilityElementsHidden\n importantForAccessibility=\"no\"\n >\n {frame.lines.map((line, index) => (\n <Line\n // Stateless drawing slots retain their keys as coordinates animate.\n // oxlint-disable-next-line react/no-array-index-key\n key={`l${index}`}\n x1={line.x1}\n y1={line.y1}\n x2={line.x2}\n y2={line.y2}\n stroke={ink(line.white, line.a, dark)}\n strokeWidth={line.w}\n />\n ))}\n {frame.dots.map((dot, index) => (\n <Circle\n // Stateless drawing slots retain their keys as coordinates animate.\n // oxlint-disable-next-line react/no-array-index-key\n key={`d${index}`}\n cx={dot.x}\n cy={dot.y}\n r={dot.r}\n fill={ink(dot.white, dot.a, dark)}\n />\n ))}\n </Svg>\n );\n}\n"]}
@@ -973,6 +973,20 @@ var iconNodes = {
973
973
  }
974
974
  ]
975
975
  ],
976
+ quote: [
977
+ [
978
+ "path",
979
+ {
980
+ d: "M16 3a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2 1 1 0 0 1 1 1v1a2 2 0 0 1-2 2 1 1 0 0 0-1 1v2a1 1 0 0 0 1 1 6 6 0 0 0 6-6V5a2 2 0 0 0-2-2z"
981
+ }
982
+ ],
983
+ [
984
+ "path",
985
+ {
986
+ d: "M5 3a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2 1 1 0 0 1 1 1v1a2 2 0 0 1-2 2 1 1 0 0 0-1 1v2a1 1 0 0 0 1 1 6 6 0 0 0 6-6V5a2 2 0 0 0-2-2z"
987
+ }
988
+ ]
989
+ ],
976
990
  search: [
977
991
  [
978
992
  "path",
@@ -2270,5 +2284,5 @@ function fontStyle(theme, weight = 400, face = "sans") {
2270
2284
  var MONO_FALLBACK = "Menlo";
2271
2285
 
2272
2286
  export { Icon, MONO_FALLBACK, NativeTimelineThemeProvider, createNativeTimelineTheme, fontStyle, insetSurface, useNativeTimelineTheme, webColorsDark, webColorsLight };
2273
- //# sourceMappingURL=chunk-2W74OY6I.js.map
2274
- //# sourceMappingURL=chunk-2W74OY6I.js.map
2287
+ //# sourceMappingURL=chunk-FHPMRXIG.js.map
2288
+ //# sourceMappingURL=chunk-FHPMRXIG.js.map