@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.
- package/android/build.gradle +15 -0
- package/android/src/main/AndroidManifest.xml +1 -0
- package/android/src/main/java/dev/opengeni/textselection/OpenGeniTextSelectionModule.kt +274 -0
- package/dist/{chunk-D3MFBDVR.js → chunk-2RNMGRTB.js} +3 -3
- package/dist/{chunk-D3MFBDVR.js.map → chunk-2RNMGRTB.js.map} +1 -1
- package/dist/{chunk-2W74OY6I.js → chunk-FHPMRXIG.js} +16 -2
- package/dist/chunk-FHPMRXIG.js.map +1 -0
- package/dist/{chunk-63PZ52YJ.js → chunk-QFV5DISV.js} +38 -5
- package/dist/chunk-QFV5DISV.js.map +1 -0
- package/dist/{chunk-U5L2CUMJ.js → chunk-ZZNZBZWV.js} +3 -3
- package/dist/{chunk-U5L2CUMJ.js.map → chunk-ZZNZBZWV.js.map} +1 -1
- package/dist/index.js +2 -2
- package/dist/timeline/activity.d.ts +3 -1
- package/dist/timeline/annotations.d.ts +81 -0
- package/dist/timeline/icon-nodes.generated.d.ts +5 -0
- package/dist/timeline/index.d.ts +2 -0
- package/dist/timeline/index.js +1223 -203
- package/dist/timeline/index.js.map +1 -1
- package/dist/timeline/markdown.js +5 -2
- package/dist/timeline/markdown.js.map +1 -1
- package/dist/timeline/message-timeline.d.ts +20 -2
- package/dist/timeline/previews.js +2 -2
- package/dist/timeline/primitives.d.ts +7 -1
- package/dist/timeline/queue-dock.d.ts +4 -1
- package/dist/timeline/text-selection.d.ts +44 -0
- package/expo-module.config.json +5 -2
- package/ios/OpenGeniTextSelectionModule.swift +560 -0
- package/package.json +6 -4
- package/src/timeline/activity.tsx +76 -21
- package/src/timeline/annotations.tsx +877 -0
- package/src/timeline/icon-nodes.generated.ts +14 -0
- package/src/timeline/index.ts +2 -0
- package/src/timeline/markdown.tsx +7 -0
- package/src/timeline/message-timeline.tsx +340 -174
- package/src/timeline/primitives.tsx +50 -1
- package/src/timeline/queue-dock.tsx +36 -0
- package/src/timeline/session-screen.tsx +122 -4
- package/src/timeline/sheet.tsx +3 -2
- package/src/timeline/text-selection.ts +115 -0
- package/dist/chunk-2W74OY6I.js.map +0 -1
- package/dist/chunk-63PZ52YJ.js.map +0 -1
|
@@ -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-
|
|
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-
|
|
265
|
-
//# sourceMappingURL=chunk-
|
|
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-
|
|
2274
|
-
//# sourceMappingURL=chunk-
|
|
2287
|
+
//# sourceMappingURL=chunk-FHPMRXIG.js.map
|
|
2288
|
+
//# sourceMappingURL=chunk-FHPMRXIG.js.map
|