@colixsystems/widget-sdk 0.149.1 → 0.150.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/README.md +48 -5
- package/dist/contract.cjs +27 -6
- package/dist/contract.js +27 -6
- package/dist/emoji-data.js +1758 -0
- package/dist/emoji-picker-tokens.js +68 -0
- package/dist/emoji-picker-view.js +193 -0
- package/dist/emoji-picker.js +14 -0
- package/dist/emoji-picker.native.js +16 -0
- package/dist/emoji-reactions.js +167 -0
- package/dist/index.js +24 -0
- package/dist/index.native.js +23 -0
- package/dist/linter.cjs +21 -9
- package/dist/linter.js +21 -9
- package/dist/theme-components.cjs +9 -4
- package/dist/theme-components.js +9 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -72,7 +72,44 @@ See the design reference for the full architecture: [`docs/architecture/widget-m
|
|
|
72
72
|
|
|
73
73
|
## Status
|
|
74
74
|
|
|
75
|
-
`v0.
|
|
75
|
+
`v0.150.1` — pre-publish. The package surface (types, function names, export paths) is the v1 contract; runtime behaviour for some hooks is stubbed (each hook documents what's wired and what isn't). It is **not yet published to npm**.
|
|
76
|
+
|
|
77
|
+
### What's new in 0.150.1 (contract 1.118.0 — unchanged)
|
|
78
|
+
|
|
79
|
+
**A capability's fallback belongs to the web build, not to native (sc-8381).** The barcode guidance here, in the in-Studio hooks reference and in both AI agents' prompts told you to gate the scan button on `supported` **and** keep a manual-entry path — unconditionally, while the reason given was a browser-only gap. `supported` is always `true` on the Expo export, so that read shipped a redundant "or type the code" field on every phone: the exact typing the scanner exists to remove. The rule is now scoped to the host that actually fails the gate.
|
|
80
|
+
|
|
81
|
+
- **Gate the substitute on the same flag that gates the button.** Where `supported` is `false` — the web Player in Safari and Firefox, which ship no `BarcodeDetector` — render the typed field instead of the scan button. Where it is `true`, the capability is the whole input. Nothing about the API changed; only the guidance did.
|
|
82
|
+
- **A native `catch` is an error state, not a web substitute.** The `react-native-nfc-manager` notes said to render "the same fallback from the `catch` that `widget.web.jsx` uses". A workspace that has not switched the NFC device capability on in Publish settings is a **misconfiguration** the widget should name, not a reason to park a permanent manual-entry field beside the tap target.
|
|
83
|
+
- **The web half is still required.** This narrows where a *substitute* belongs; it does not relax the parity rule. A native-only capability still ships a real way in on `widget.web.jsx` — a typed code, a QR scan, a non-sensor path — and still degrades visibly rather than rendering blank.
|
|
84
|
+
|
|
85
|
+
### What's new in 0.150.0 (contract 1.118.0)
|
|
86
|
+
|
|
87
|
+
**New primitive `<EmojiPicker>` — pick an emoji without leaving for the OS keyboard (sc-8104).** Typed emoji always worked: a plain `<TextInput>` stores and renders them on both hosts, and nothing about that changes. What was missing was an in-app way to *choose* one — for a message composer, or to react to something.
|
|
88
|
+
|
|
89
|
+
```jsx
|
|
90
|
+
const [open, setOpen] = useState(false);
|
|
91
|
+
const [recents, setRecents] = useState([]);
|
|
92
|
+
|
|
93
|
+
<EmojiPicker
|
|
94
|
+
visible={open}
|
|
95
|
+
recents={recents}
|
|
96
|
+
onRecentsChange={setRecents}
|
|
97
|
+
onRequestClose={() => setOpen(false)}
|
|
98
|
+
onSelect={(char) => {
|
|
99
|
+
const { value } = insertAtCaret(draft, char, selection);
|
|
100
|
+
setDraft(value);
|
|
101
|
+
setOpen(false);
|
|
102
|
+
}}
|
|
103
|
+
/>
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
It presents inside `<Overlay>`, so it escapes the widget's clipping box on both hosts, and ships a curated Unicode set (categories, search, recents) as plain data — no emoji dependency to vet. `onSelect` hands you the character; `recents` / `onRecentsChange` let you persist the picks wherever your widget keeps state. Pair it with `insertAtCaret(value, char, selection)` to land the emoji mid-draft instead of appending it.
|
|
107
|
+
|
|
108
|
+
**Also new: a shared reaction model.** `groupReactions(rows, { typeField, userField, userId })` turns reaction rows into per-emoji pill groups — one row per (target, user, emoji), de-duplicated per user — and `readReactionEmoji(value)` maps a stored value to its emoji, ignoring anything it does not recognise. Widgets storing reactions should group through these rather than rolling their own, so pills behave identically wherever they appear.
|
|
109
|
+
|
|
110
|
+
### What's new in 0.149.0 (contract 1.117.0)
|
|
111
|
+
|
|
112
|
+
**Six text roles on the theme, so every widget speaks one typographic voice (sc-8058).** See the theme section above for the roles and how to read them.
|
|
76
113
|
|
|
77
114
|
### What's new in 0.148.0 (contract 1.116.0)
|
|
78
115
|
|
|
@@ -274,7 +311,7 @@ Also tightened in this release: the `publicGrant` flags are now type-checked. Th
|
|
|
274
311
|
**NFC tags are vetted, native-only (`react-native-nfc-manager`).** A widget can now read and write NFC tags on the Expo export — NDEF over `NfcManager.requestTechnology(NfcTech.Ndef)`, plus the raw technologies a badge, asset tag or transit card uses: `NfcTech.IsoDep` on both hosts, `NfcTech.MifareClassic` on Android, `NfcTech.MifareIOS` / `FelicaIOS` / `Iso15693IOS` on iOS. (There is no `NfcTech.FeliCa` — check `index.d.ts` before naming one.) This is the last common "tap a physical thing" input the allowlist was missing; barcode and QR already had `useBarcodeScanner()`.
|
|
275
312
|
|
|
276
313
|
- **Native-only, and the browser is why.** Web NFC ships in Chrome on Android alone — Safari, Firefox and every desktop browser omit it — so there is no web build to pair with. Put the import in `widget.native.jsx` and give `widget.web.jsx` a real way in: a typed code field, or the SDK's `useBarcodeScanner()` QR path. The linter's `import-platform-mismatch` enforces the split.
|
|
277
|
-
- **Opt-in per workspace — and `isSupported()` cannot see that.** The export claims the iOS NFC entitlement and the Android `NFC` permission only when the publisher enables the **NFC tags** device capability in Publish settings. `isSupported()` probes the **device** (`readingAvailable` on iOS, `hasSystemFeature` on Android), not the gate: on a capable phone in an app that never asked, it returns `true` and the read then fails — Android throws a `SecurityException` from `registerTagEvent`, iOS cannot open the reader session. So wrap `start()` / `requestTechnology()` in a `try` and render
|
|
314
|
+
- **Opt-in per workspace — and `isSupported()` cannot see that.** The export claims the iOS NFC entitlement and the Android `NFC` permission only when the publisher enables the **NFC tags** device capability in Publish settings. `isSupported()` probes the **device** (`readingAvailable` on iOS, `hasSystemFeature` on Android), not the gate: on a capable phone in an app that never asked, it returns `true` and the read then fails — Android throws a `SecurityException` from `registerTagEvent`, iOS cannot open the reader session. So wrap `start()` / `requestTechnology()` in a `try` and render a clear **error state** from the `catch` — name what is missing and say the capability has to be switched on in Publish settings. That `catch` is an error path, **not** the web build's typed-code substitute: do not give `widget.native.jsx` a permanent manual-entry field beside the tap target, because in a correctly-configured app the radio works and the field is the typing the tag was meant to replace.
|
|
278
315
|
- **Clean up the session.** `cancelTechnologyRequest()` in your effect cleanup; an abandoned request leaves the reader sheet up on iOS.
|
|
279
316
|
- **It is a write capability too.** Read with `getNdefMessage()`; write with `writeNdefMessage(bytes)`, `formatNdef(bytes)` (Android only, for a blank tag) and `transceive(bytes)` on the `IsoDep` / `NfcA` / `NfcV` handlers. One capability covers both — Android expresses read and write through the single `android.permission.NFC` — so the workspace owner who enables it is consenting to every widget in the workspace being able to rewrite the tags their users tap. Raw ISO7816 `transceive` on iOS additionally needs `select-identifiers` on the entitlement, which the export does not declare; NDEF read and write work on both hosts.
|
|
280
317
|
- **`makeReadOnly()` is irreversible.** It permanently locks the tag — no later write will ever succeed, on any device, by any app. Call it only where the feature genuinely wants a one-time-programmable tag, and never as a cleanup step after writing.
|
|
@@ -353,13 +390,19 @@ const hit = await scan(); // { value, format } | null
|
|
|
353
390
|
|
|
354
391
|
It is deliberately **one-shot** rather than a start/stop subscription — to read several codes, call `scan()` again. There is no loop to leave running and no camera to forget to release.
|
|
355
392
|
|
|
356
|
-
**Gate the button on `supported`, and
|
|
393
|
+
**Gate the button on `supported`, and keep the manual-entry path for the host that fails that gate.** `BarcodeDetector` is Chromium-only today, so the web Player reports `supported: false` in Safari and Firefox — a genuine browser gap, not a missing feature, and there the scanner must not be the only way to enter a code. The Expo export always reports `true`, so do **not** mirror the typed field onto native: on the phone the scanner is the whole input, and the twin field is the typing the scan was meant to remove. Gate the substitute on the same flag that gates the button:
|
|
357
394
|
|
|
358
395
|
```jsx
|
|
359
|
-
|
|
360
|
-
|
|
396
|
+
{supported
|
|
397
|
+
? <Pressable onPress={scan}><Text>Scan</Text></Pressable>
|
|
398
|
+
: <TextInput value={code} onChangeText={setCode} placeholder="Enter the code" />}
|
|
399
|
+
{/* On the phone there is no typed field to fall back to, so a denied
|
|
400
|
+
camera has to say so rather than leave a button that does nothing. */}
|
|
401
|
+
{error ? <Text>Camera access is off — turn it on in Settings to scan.</Text> : null}
|
|
361
402
|
```
|
|
362
403
|
|
|
404
|
+
That error line is not optional on native. `supported` is `true` there, so the scan button is the only way in — a `PERMISSION_DENIED` the widget swallows leaves the user tapping a control that silently does nothing.
|
|
405
|
+
|
|
363
406
|
**Host-brokered, not a vetted import.** Like the camera and speech-to-text, your widget never imports the native module: `expo-camera` is pinned by the Expo export only, so a widget that does not scan gains no native dependency and nothing new enters widget bundles.
|
|
364
407
|
|
|
365
408
|
### What's new in 0.128.0 (contract 1.99.0 — unchanged)
|
package/dist/contract.cjs
CHANGED
|
@@ -1860,9 +1860,12 @@ const HOOKS = [
|
|
|
1860
1860
|
"PERMISSION_DENIED | UNSUPPORTED | INTERNAL. `value` is the decoded text; `format` is a lowercase symbology name " +
|
|
1861
1861
|
"(qr_code, code_128, ean_13, …) — treat it as a hint, not a promise, because the two hosts detect different sets. " +
|
|
1862
1862
|
"It is deliberately ONE-SHOT rather than a start/stop subscription: to scan several codes, call scan() again. " +
|
|
1863
|
-
"reset() clears the last result and error. ALWAYS check `supported` before rendering a scan button and give the " +
|
|
1864
|
-
"widget a manual-entry path — the
|
|
1865
|
-
"Firefox today),
|
|
1863
|
+
"reset() clears the last result and error. ALWAYS check `supported` before rendering a scan button, and give the " +
|
|
1864
|
+
"widget a manual-entry path WHERE THAT GUARD FAILS — which is the WEB Player, wherever the browser ships no " +
|
|
1865
|
+
"BarcodeDetector (Safari and Firefox today), a real browser gap rather than a missing feature. The Expo export " +
|
|
1866
|
+
"ALWAYS reports true, so do NOT pair the scan button with a typed-code twin there: on the phone the scanner is the " +
|
|
1867
|
+
"whole input, and a redundant 'or type it in' field is the manual step the capability exists to remove. " +
|
|
1868
|
+
"options: { formats } is a HINT narrowing the " +
|
|
1866
1869
|
"symbologies to look for. The Expo export scans via expo-camera; the web Player via BarcodeDetector over the same " +
|
|
1867
1870
|
"getUserMedia preview useCamera() uses. The camera is released on every exit path, including a dismissal.",
|
|
1868
1871
|
returnShape: {
|
|
@@ -2079,6 +2082,15 @@ const PRIMITIVES = [
|
|
|
2079
2082
|
rnComponent: null,
|
|
2080
2083
|
docsUrl: null,
|
|
2081
2084
|
},
|
|
2085
|
+
// sc-8104 — the in-app emoji picker. Typed emoji always worked; what this
|
|
2086
|
+
// replaces is being forced out to the OS keyboard to find one.
|
|
2087
|
+
{
|
|
2088
|
+
name: "EmojiPicker",
|
|
2089
|
+
description:
|
|
2090
|
+
"In-app emoji picker. `<EmojiPicker visible={open} onRequestClose={() => setOpen(false)} onSelect={(char) => insert(char)} recents={recents} onRecentsChange={setRecents} />`. THE way to let an app user pick an emoji without leaving for the OS keyboard — for a message/post composer, and for starting a reaction. It presents inside the SDK `<Overlay>`, so it escapes the clipping box of the widget on both hosts, and it ships a curated Unicode set WITH the SDK (categories, search, recents) so no emoji dependency is needed. Props: `visible` (boolean), `onSelect` (`(char: string) => void`, fired with the chosen emoji character), `onRequestClose` (wire it or the picker cannot be dismissed), `recents` (string[] of previously picked characters) and `onRecentsChange` (persist them where the widget keeps its own state), plus `accessibilityLabel` and `searchPlaceholder`. To insert into a composer at the caret rather than appending, pair it with `insertAtCaret(value, char, selection)`. Typed emoji need nothing: a plain `TextInput` already stores and renders them.",
|
|
2091
|
+
rnComponent: null,
|
|
2092
|
+
docsUrl: null,
|
|
2093
|
+
},
|
|
2082
2094
|
// sc-6970 — formatted content, rendered. Widgets have no HTML path on either
|
|
2083
2095
|
// host, so this is the ONE way bold, headings and lists reach a reader.
|
|
2084
2096
|
{
|
|
@@ -2625,7 +2637,8 @@ const WIDGET_CONTEXT_SHAPE = {
|
|
|
2625
2637
|
"BarcodeError (.code PERMISSION_DENIED | UNSUPPORTED | INTERNAL). Its formats hint and its format output BOTH speak " +
|
|
2626
2638
|
"BarcodeDetector names (qr_code, code_128, ean_13, ...) on both hosts; the Expo export maps them to expo-camera own " +
|
|
2627
2639
|
"vocabulary internally and DROPS a name it cannot map. isSupported() is false wherever the browser ships no " +
|
|
2628
|
-
"BarcodeDetector (Safari, Firefox), so a
|
|
2640
|
+
"BarcodeDetector (Safari, Firefox), so a WEB build must offer manual entry beside a scan button; the Expo export " +
|
|
2641
|
+
"always reports true and takes the scanner alone — no typed-code twin on native. " +
|
|
2629
2642
|
"sc-6450 — the geolocation background-watch members are NATIVE-ONLY and opt-in per app: the web Player and an export that " +
|
|
2630
2643
|
"did not opt in both report isBackgroundSupported() false, and the Expo export backs it with expo-location + " +
|
|
2631
2644
|
"expo-task-manager. subscribeBackgroundPositions and subscribeBackgroundWatchState are plain subscriptions — they start no " +
|
|
@@ -2955,7 +2968,7 @@ const VETTED_IMPORTS = [
|
|
|
2955
2968
|
platforms: ["native"],
|
|
2956
2969
|
category: "system",
|
|
2957
2970
|
description:
|
|
2958
|
-
"Read and write NFC tags on the Expo export: NDEF records via NfcManager.requestTechnology(NfcTech.Ndef), plus the raw technologies a badge, asset tag or transit card uses - NfcTech.IsoDep on both hosts, NfcTech.MifareClassic on Android, NfcTech.MifareIOS / FelicaIOS / Iso15693IOS on iOS (there is no NfcTech.FeliCa; check index.d.ts before naming one). Read with ndefHandler.getNdefMessage(); WRITE with writeNdefMessage(bytes), formatNdef(bytes) (Android only, for a blank tag) and transceive(bytes) on the IsoDep / NfcA / NfcV handlers. makeReadOnly() PERMANENTLY locks a tag - it cannot be undone and no later write will ever succeed, so call it only where the brief actually asks for a one-time-programmable tag, never as a tidy-up step. Raw ISO7816 transceive on iOS additionally needs select-identifiers on the entitlement, which this export does not declare - NDEF read and write work on both hosts. Always cancelTechnologyRequest() in a cleanup - an abandoned session leaves the reader sheet up on iOS. Native-only because no browser hands the Player a reader: Web NFC exists in Chrome on Android alone (Safari, Firefox and every desktop browser omit it), so there is no web build to pair with. Author it in widget.native.jsx and give widget.web.jsx a real way in instead - a typed code field, or the SDK's useBarcodeScanner() QR path. The radio is ALSO opt-in per workspace: the export claims the iOS NFC entitlement and the Android NFC permission only when the publisher enables the 'nfc' device capability in Publish settings. isSupported() does NOT see that gate - it probes the DEVICE (readingAvailable on iOS, hasSystemFeature on Android), so on a capable phone in an app that never asked it returns true and the read then fails: Android throws a SecurityException from registerTagEvent, iOS cannot open the reader session. So wrap start() / requestTechnology() in a try and render
|
|
2971
|
+
"Read and write NFC tags on the Expo export: NDEF records via NfcManager.requestTechnology(NfcTech.Ndef), plus the raw technologies a badge, asset tag or transit card uses - NfcTech.IsoDep on both hosts, NfcTech.MifareClassic on Android, NfcTech.MifareIOS / FelicaIOS / Iso15693IOS on iOS (there is no NfcTech.FeliCa; check index.d.ts before naming one). Read with ndefHandler.getNdefMessage(); WRITE with writeNdefMessage(bytes), formatNdef(bytes) (Android only, for a blank tag) and transceive(bytes) on the IsoDep / NfcA / NfcV handlers. makeReadOnly() PERMANENTLY locks a tag - it cannot be undone and no later write will ever succeed, so call it only where the brief actually asks for a one-time-programmable tag, never as a tidy-up step. Raw ISO7816 transceive on iOS additionally needs select-identifiers on the entitlement, which this export does not declare - NDEF read and write work on both hosts. Always cancelTechnologyRequest() in a cleanup - an abandoned session leaves the reader sheet up on iOS. Native-only because no browser hands the Player a reader: Web NFC exists in Chrome on Android alone (Safari, Firefox and every desktop browser omit it), so there is no web build to pair with. Author it in widget.native.jsx and give widget.web.jsx a real way in instead - a typed code field, or the SDK's useBarcodeScanner() QR path. The radio is ALSO opt-in per workspace: the export claims the iOS NFC entitlement and the Android NFC permission only when the publisher enables the 'nfc' device capability in Publish settings. isSupported() does NOT see that gate - it probes the DEVICE (readingAvailable on iOS, hasSystemFeature on Android), so on a capable phone in an app that never asked it returns true and the read then fails: Android throws a SecurityException from registerTagEvent, iOS cannot open the reader session. So wrap start() / requestTechnology() in a try and render a clear ERROR STATE from the catch - name what is missing and say the maker must switch 'nfc' on in Publish settings. That catch is an ERROR path, NOT the web build's typed-code substitute: do not give widget.native.jsx a permanent manual-entry field beside the tap target, because in a correctly-configured app the radio works and the field is the typing the tag was meant to replace. Pinned at 3.17.2, the current stable release (4.x is still beta); a community package, so Expo bundledNativeModules has no version opinion on it.",
|
|
2959
2972
|
},
|
|
2960
2973
|
{
|
|
2961
2974
|
specifier: "decimal.js",
|
|
@@ -4243,7 +4256,15 @@ const CONTRACT = deepFreeze({
|
|
|
4243
4256
|
// hosts' shared textStyle module), not a theme token. Additive: no export
|
|
4244
4257
|
// changed signature, and a theme carrying no `roles` renders on the seeded
|
|
4245
4258
|
// defaults exactly as before.
|
|
4246
|
-
|
|
4259
|
+
// 1.118.0: additive (sc-8104) — the `EmojiPicker` primitive, plus the emoji
|
|
4260
|
+
// set and reaction model it ships with. Typed emoji always worked; there
|
|
4261
|
+
// was no in-app way to PICK one, so every emoji meant a trip to the OS
|
|
4262
|
+
// keyboard. The curated Unicode set ships as plain string data rather than
|
|
4263
|
+
// a vetted dependency. `groupReactions` / `readReactionEmoji` are shared
|
|
4264
|
+
// because Newsfeed and Chat both render per-emoji pills over a Reactions
|
|
4265
|
+
// table, and the legacy slug mapping (`like` -> emoji) must not be
|
|
4266
|
+
// re-derived per widget.
|
|
4267
|
+
version: "1.118.0",
|
|
4247
4268
|
sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
|
|
4248
4269
|
hooks: HOOKS,
|
|
4249
4270
|
primitives: PRIMITIVES,
|
package/dist/contract.js
CHANGED
|
@@ -1860,9 +1860,12 @@ const HOOKS = [
|
|
|
1860
1860
|
"PERMISSION_DENIED | UNSUPPORTED | INTERNAL. `value` is the decoded text; `format` is a lowercase symbology name " +
|
|
1861
1861
|
"(qr_code, code_128, ean_13, …) — treat it as a hint, not a promise, because the two hosts detect different sets. " +
|
|
1862
1862
|
"It is deliberately ONE-SHOT rather than a start/stop subscription: to scan several codes, call scan() again. " +
|
|
1863
|
-
"reset() clears the last result and error. ALWAYS check `supported` before rendering a scan button and give the " +
|
|
1864
|
-
"widget a manual-entry path — the
|
|
1865
|
-
"Firefox today),
|
|
1863
|
+
"reset() clears the last result and error. ALWAYS check `supported` before rendering a scan button, and give the " +
|
|
1864
|
+
"widget a manual-entry path WHERE THAT GUARD FAILS — which is the WEB Player, wherever the browser ships no " +
|
|
1865
|
+
"BarcodeDetector (Safari and Firefox today), a real browser gap rather than a missing feature. The Expo export " +
|
|
1866
|
+
"ALWAYS reports true, so do NOT pair the scan button with a typed-code twin there: on the phone the scanner is the " +
|
|
1867
|
+
"whole input, and a redundant 'or type it in' field is the manual step the capability exists to remove. " +
|
|
1868
|
+
"options: { formats } is a HINT narrowing the " +
|
|
1866
1869
|
"symbologies to look for. The Expo export scans via expo-camera; the web Player via BarcodeDetector over the same " +
|
|
1867
1870
|
"getUserMedia preview useCamera() uses. The camera is released on every exit path, including a dismissal.",
|
|
1868
1871
|
returnShape: {
|
|
@@ -2079,6 +2082,15 @@ const PRIMITIVES = [
|
|
|
2079
2082
|
rnComponent: null,
|
|
2080
2083
|
docsUrl: null,
|
|
2081
2084
|
},
|
|
2085
|
+
// sc-8104 — the in-app emoji picker. Typed emoji always worked; what this
|
|
2086
|
+
// replaces is being forced out to the OS keyboard to find one.
|
|
2087
|
+
{
|
|
2088
|
+
name: "EmojiPicker",
|
|
2089
|
+
description:
|
|
2090
|
+
"In-app emoji picker. `<EmojiPicker visible={open} onRequestClose={() => setOpen(false)} onSelect={(char) => insert(char)} recents={recents} onRecentsChange={setRecents} />`. THE way to let an app user pick an emoji without leaving for the OS keyboard — for a message/post composer, and for starting a reaction. It presents inside the SDK `<Overlay>`, so it escapes the clipping box of the widget on both hosts, and it ships a curated Unicode set WITH the SDK (categories, search, recents) so no emoji dependency is needed. Props: `visible` (boolean), `onSelect` (`(char: string) => void`, fired with the chosen emoji character), `onRequestClose` (wire it or the picker cannot be dismissed), `recents` (string[] of previously picked characters) and `onRecentsChange` (persist them where the widget keeps its own state), plus `accessibilityLabel` and `searchPlaceholder`. To insert into a composer at the caret rather than appending, pair it with `insertAtCaret(value, char, selection)`. Typed emoji need nothing: a plain `TextInput` already stores and renders them.",
|
|
2091
|
+
rnComponent: null,
|
|
2092
|
+
docsUrl: null,
|
|
2093
|
+
},
|
|
2082
2094
|
// sc-6970 — formatted content, rendered. Widgets have no HTML path on either
|
|
2083
2095
|
// host, so this is the ONE way bold, headings and lists reach a reader.
|
|
2084
2096
|
{
|
|
@@ -2621,7 +2633,8 @@ const WIDGET_CONTEXT_SHAPE = {
|
|
|
2621
2633
|
"BarcodeError (.code PERMISSION_DENIED | UNSUPPORTED | INTERNAL). Its formats hint and its format output BOTH speak " +
|
|
2622
2634
|
"BarcodeDetector names (qr_code, code_128, ean_13, ...) on both hosts; the Expo export maps them to expo-camera own " +
|
|
2623
2635
|
"vocabulary internally and DROPS a name it cannot map. isSupported() is false wherever the browser ships no " +
|
|
2624
|
-
"BarcodeDetector (Safari, Firefox), so a
|
|
2636
|
+
"BarcodeDetector (Safari, Firefox), so a WEB build must offer manual entry beside a scan button; the Expo export " +
|
|
2637
|
+
"always reports true and takes the scanner alone — no typed-code twin on native. " +
|
|
2625
2638
|
"sc-6450 — the geolocation background-watch members are NATIVE-ONLY and opt-in per app: the web Player and an export that " +
|
|
2626
2639
|
"did not opt in both report isBackgroundSupported() false, and the Expo export backs it with expo-location + " +
|
|
2627
2640
|
"expo-task-manager. subscribeBackgroundPositions and subscribeBackgroundWatchState are plain subscriptions — they start no " +
|
|
@@ -2951,7 +2964,7 @@ const VETTED_IMPORTS = [
|
|
|
2951
2964
|
platforms: ["native"],
|
|
2952
2965
|
category: "system",
|
|
2953
2966
|
description:
|
|
2954
|
-
"Read and write NFC tags on the Expo export: NDEF records via NfcManager.requestTechnology(NfcTech.Ndef), plus the raw technologies a badge, asset tag or transit card uses - NfcTech.IsoDep on both hosts, NfcTech.MifareClassic on Android, NfcTech.MifareIOS / FelicaIOS / Iso15693IOS on iOS (there is no NfcTech.FeliCa; check index.d.ts before naming one). Read with ndefHandler.getNdefMessage(); WRITE with writeNdefMessage(bytes), formatNdef(bytes) (Android only, for a blank tag) and transceive(bytes) on the IsoDep / NfcA / NfcV handlers. makeReadOnly() PERMANENTLY locks a tag - it cannot be undone and no later write will ever succeed, so call it only where the brief actually asks for a one-time-programmable tag, never as a tidy-up step. Raw ISO7816 transceive on iOS additionally needs select-identifiers on the entitlement, which this export does not declare - NDEF read and write work on both hosts. Always cancelTechnologyRequest() in a cleanup - an abandoned session leaves the reader sheet up on iOS. Native-only because no browser hands the Player a reader: Web NFC exists in Chrome on Android alone (Safari, Firefox and every desktop browser omit it), so there is no web build to pair with. Author it in widget.native.jsx and give widget.web.jsx a real way in instead - a typed code field, or the SDK's useBarcodeScanner() QR path. The radio is ALSO opt-in per workspace: the export claims the iOS NFC entitlement and the Android NFC permission only when the publisher enables the 'nfc' device capability in Publish settings. isSupported() does NOT see that gate - it probes the DEVICE (readingAvailable on iOS, hasSystemFeature on Android), so on a capable phone in an app that never asked it returns true and the read then fails: Android throws a SecurityException from registerTagEvent, iOS cannot open the reader session. So wrap start() / requestTechnology() in a try and render
|
|
2967
|
+
"Read and write NFC tags on the Expo export: NDEF records via NfcManager.requestTechnology(NfcTech.Ndef), plus the raw technologies a badge, asset tag or transit card uses - NfcTech.IsoDep on both hosts, NfcTech.MifareClassic on Android, NfcTech.MifareIOS / FelicaIOS / Iso15693IOS on iOS (there is no NfcTech.FeliCa; check index.d.ts before naming one). Read with ndefHandler.getNdefMessage(); WRITE with writeNdefMessage(bytes), formatNdef(bytes) (Android only, for a blank tag) and transceive(bytes) on the IsoDep / NfcA / NfcV handlers. makeReadOnly() PERMANENTLY locks a tag - it cannot be undone and no later write will ever succeed, so call it only where the brief actually asks for a one-time-programmable tag, never as a tidy-up step. Raw ISO7816 transceive on iOS additionally needs select-identifiers on the entitlement, which this export does not declare - NDEF read and write work on both hosts. Always cancelTechnologyRequest() in a cleanup - an abandoned session leaves the reader sheet up on iOS. Native-only because no browser hands the Player a reader: Web NFC exists in Chrome on Android alone (Safari, Firefox and every desktop browser omit it), so there is no web build to pair with. Author it in widget.native.jsx and give widget.web.jsx a real way in instead - a typed code field, or the SDK's useBarcodeScanner() QR path. The radio is ALSO opt-in per workspace: the export claims the iOS NFC entitlement and the Android NFC permission only when the publisher enables the 'nfc' device capability in Publish settings. isSupported() does NOT see that gate - it probes the DEVICE (readingAvailable on iOS, hasSystemFeature on Android), so on a capable phone in an app that never asked it returns true and the read then fails: Android throws a SecurityException from registerTagEvent, iOS cannot open the reader session. So wrap start() / requestTechnology() in a try and render a clear ERROR STATE from the catch - name what is missing and say the maker must switch 'nfc' on in Publish settings. That catch is an ERROR path, NOT the web build's typed-code substitute: do not give widget.native.jsx a permanent manual-entry field beside the tap target, because in a correctly-configured app the radio works and the field is the typing the tag was meant to replace. Pinned at 3.17.2, the current stable release (4.x is still beta); a community package, so Expo bundledNativeModules has no version opinion on it.",
|
|
2955
2968
|
},
|
|
2956
2969
|
{
|
|
2957
2970
|
specifier: "decimal.js",
|
|
@@ -4239,7 +4252,15 @@ const CONTRACT = deepFreeze({
|
|
|
4239
4252
|
// hosts' shared textStyle module), not a theme token. Additive: no export
|
|
4240
4253
|
// changed signature, and a theme carrying no `roles` renders on the seeded
|
|
4241
4254
|
// defaults exactly as before.
|
|
4242
|
-
|
|
4255
|
+
// 1.118.0: additive (sc-8104) — the `EmojiPicker` primitive, plus the emoji
|
|
4256
|
+
// set and reaction model it ships with. Typed emoji always worked; there
|
|
4257
|
+
// was no in-app way to PICK one, so every emoji meant a trip to the OS
|
|
4258
|
+
// keyboard. The curated Unicode set ships as plain string data rather than
|
|
4259
|
+
// a vetted dependency. `groupReactions` / `readReactionEmoji` are shared
|
|
4260
|
+
// because Newsfeed and Chat both render per-emoji pills over a Reactions
|
|
4261
|
+
// table, and the legacy slug mapping (`like` -> emoji) must not be
|
|
4262
|
+
// re-derived per widget.
|
|
4263
|
+
version: "1.118.0",
|
|
4243
4264
|
sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
|
|
4244
4265
|
hooks: HOOKS,
|
|
4245
4266
|
primitives: PRIMITIVES,
|