diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 7cedf2d7..4c740519 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -10,6 +10,7 @@ PODS: - hermes-engine (0.14.0): - hermes-engine/Pre-built (= 0.14.0) - hermes-engine/Pre-built (0.14.0) + - iosMath (0.9.4) - llama-rn (0.12.0-rc.9): - boost - DoubleConversion @@ -2797,6 +2798,35 @@ PODS: - React-perflogger (= 0.83.1) - React-utils (= 0.83.1) - SocketRocket + - ReactNativeEnrichedMarkdown (0.5.0): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - iosMath (~> 0.9) + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-jsi + - React-NativeModulesApple + - React-RCTFabric + - React-renderercss + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - SocketRocket + - Yoga - RNCAsyncStorage (2.2.0): - boost - DoubleConversion @@ -3368,6 +3398,7 @@ DEPENDENCIES: - ReactAppDependencyProvider (from `build/generated/ios/ReactAppDependencyProvider`) - ReactCodegen (from `build/generated/ios/ReactCodegen`) - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`) + - ReactNativeEnrichedMarkdown (from `../node_modules/react-native-enriched-markdown`) - "RNCAsyncStorage (from `../node_modules/@react-native-async-storage/async-storage`)" - RNDeviceInfo (from `../node_modules/react-native-device-info`) - RNFS (from `../node_modules/react-native-fs`) @@ -3386,6 +3417,7 @@ DEPENDENCIES: SPEC REPOS: trunk: + - iosMath - lottie-ios - SocketRocket - SSZipArchive @@ -3566,6 +3598,8 @@ EXTERNAL SOURCES: :path: build/generated/ios/ReactCodegen ReactCommon: :path: "../node_modules/react-native/ReactCommon" + ReactNativeEnrichedMarkdown: + :path: "../node_modules/react-native-enriched-markdown" RNCAsyncStorage: :path: "../node_modules/@react-native-async-storage/async-storage" RNDeviceInfo: @@ -3604,6 +3638,7 @@ SPEC CHECKSUMS: fmt: a40bb5bd0294ea969aaaba240a927bd33d878cdd glog: 5683914934d5b6e4240e497e0f4a3b42d1854183 hermes-engine: 8c6be38f94b3bf8b864981980e64e55f08e467ec + iosMath: f7a6cbadf9d836d2149c2a84c435b1effc244cba llama-rn: 796fa53f37f89e2c77cd6c462ad1172ee96d4c80 lottie-ios: a881093fab623c467d3bce374367755c272bdd59 lottie-react-native: 691b8363e8c591fb78a78254ff2517258891456b @@ -3684,6 +3719,7 @@ SPEC CHECKSUMS: ReactAppDependencyProvider: 0eb286cc274abb059ee601b862ebddac2e681d01 ReactCodegen: 3d48510bcef445f6403c0004047d4d9cbb915435 ReactCommon: ac934cb340aee91282ecd6f273a26d24d4c55cae + ReactNativeEnrichedMarkdown: bacee50e468b43a7bd133a28e3c777c29af914c4 RNCAsyncStorage: 29f0230e1a25f36c20b05f65e2eb8958d6526e82 RNDeviceInfo: 36d7f232bfe7c9b5c494cb7793230424ed32c388 RNFS: 89de7d7f4c0f6bafa05343c578f61118c8282ed8 diff --git a/package-lock.json b/package-lock.json index 8571ba17..949f35f7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,6 +32,7 @@ "react": "19.2.0", "react-native": "0.83.1", "react-native-device-info": "^15.0.1", + "react-native-enriched-markdown": "^0.5.0", "react-native-fs": "^2.20.0", "react-native-gesture-handler": "^2.30.0", "react-native-haptic-feedback": "^2.3.3", @@ -2252,229 +2253,6 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/@expo/config-plugins": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-2.0.4.tgz", - "integrity": "sha512-JGt/X2tFr7H8KBQrKfbGo9hmCubQraMxq5sj3bqDdKmDOLcE1a/EDCP9g0U4GHsa425J8VDIkQUHYz3h3ndEXQ==", - "license": "MIT", - "dependencies": { - "@expo/config-types": "^41.0.0", - "@expo/json-file": "8.2.30", - "@expo/plist": "0.0.13", - "debug": "^4.3.1", - "find-up": "~5.0.0", - "fs-extra": "9.0.0", - "getenv": "^1.0.0", - "glob": "7.1.6", - "resolve-from": "^5.0.0", - "slash": "^3.0.0", - "xcode": "^3.0.1", - "xml2js": "^0.4.23" - } - }, - "node_modules/@expo/config-plugins/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@expo/config-plugins/node_modules/fs-extra": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.0.tgz", - "integrity": "sha512-pmEYSk3vYsG/bF651KPUXZ+hvjpgWYw/Gc7W9NFUe3ZVLczKKWIij3IKpOrQcdw4TILtibFslZ0UmR8Vvzig4g==", - "license": "MIT", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^1.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@expo/config-plugins/node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@expo/config-plugins/node_modules/jsonfile": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", - "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/@expo/config-plugins/node_modules/jsonfile/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@expo/config-plugins/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@expo/config-plugins/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@expo/config-plugins/node_modules/universalify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", - "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==", - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@expo/config-types": { - "version": "41.0.0", - "resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-41.0.0.tgz", - "integrity": "sha512-Ax0pHuY5OQaSrzplOkT9DdpdmNzaVDnq9VySb4Ujq7UJ4U4jriLy8u93W98zunOXpcu0iiKubPsqD6lCiq0pig==", - "license": "MIT" - }, - "node_modules/@expo/json-file": { - "version": "8.2.30", - "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-8.2.30.tgz", - "integrity": "sha512-vrgGyPEXBoFI5NY70IegusCSoSVIFV3T3ry4tjJg1MFQKTUlR7E0r+8g8XR6qC705rc2PawaZQjqXMAVtV6s2A==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "~7.10.4", - "fs-extra": "9.0.0", - "json5": "^1.0.1", - "write-file-atomic": "^2.3.0" - } - }, - "node_modules/@expo/json-file/node_modules/@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", - "license": "MIT", - "dependencies": { - "@babel/highlight": "^7.10.4" - } - }, - "node_modules/@expo/json-file/node_modules/fs-extra": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.0.tgz", - "integrity": "sha512-pmEYSk3vYsG/bF651KPUXZ+hvjpgWYw/Gc7W9NFUe3ZVLczKKWIij3IKpOrQcdw4TILtibFslZ0UmR8Vvzig4g==", - "license": "MIT", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^1.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@expo/json-file/node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "license": "MIT", - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/@expo/json-file/node_modules/jsonfile": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", - "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/@expo/json-file/node_modules/jsonfile/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@expo/json-file/node_modules/universalify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", - "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==", - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@expo/json-file/node_modules/write-file-atomic": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", - "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", - "license": "ISC", - "dependencies": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" - } - }, - "node_modules/@expo/plist": { - "version": "0.0.13", - "resolved": "https://registry.npmjs.org/@expo/plist/-/plist-0.0.13.tgz", - "integrity": "sha512-zGPSq9OrCn7lWvwLLHLpHUUq2E40KptUFXn53xyZXPViI0k9lbApcR9KlonQZ95C+ELsf0BQ3gRficwK92Ivcw==", - "license": "MIT", - "dependencies": { - "base64-js": "^1.2.3", - "xmlbuilder": "^14.0.0", - "xmldom": "~0.5.0" - } - }, "node_modules/@floating-ui/core": { "version": "1.7.4", "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.4.tgz", @@ -3536,6 +3314,224 @@ "react-native": ">= 0.60.2" } }, + "node_modules/@react-native-voice/voice/node_modules/@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "license": "MIT", + "dependencies": { + "@babel/highlight": "^7.10.4" + } + }, + "node_modules/@react-native-voice/voice/node_modules/@expo/config-plugins": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-2.0.4.tgz", + "integrity": "sha512-JGt/X2tFr7H8KBQrKfbGo9hmCubQraMxq5sj3bqDdKmDOLcE1a/EDCP9g0U4GHsa425J8VDIkQUHYz3h3ndEXQ==", + "license": "MIT", + "dependencies": { + "@expo/config-types": "^41.0.0", + "@expo/json-file": "8.2.30", + "@expo/plist": "0.0.13", + "debug": "^4.3.1", + "find-up": "~5.0.0", + "fs-extra": "9.0.0", + "getenv": "^1.0.0", + "glob": "7.1.6", + "resolve-from": "^5.0.0", + "slash": "^3.0.0", + "xcode": "^3.0.1", + "xml2js": "^0.4.23" + } + }, + "node_modules/@react-native-voice/voice/node_modules/@expo/config-types": { + "version": "41.0.0", + "resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-41.0.0.tgz", + "integrity": "sha512-Ax0pHuY5OQaSrzplOkT9DdpdmNzaVDnq9VySb4Ujq7UJ4U4jriLy8u93W98zunOXpcu0iiKubPsqD6lCiq0pig==", + "license": "MIT" + }, + "node_modules/@react-native-voice/voice/node_modules/@expo/json-file": { + "version": "8.2.30", + "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-8.2.30.tgz", + "integrity": "sha512-vrgGyPEXBoFI5NY70IegusCSoSVIFV3T3ry4tjJg1MFQKTUlR7E0r+8g8XR6qC705rc2PawaZQjqXMAVtV6s2A==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "~7.10.4", + "fs-extra": "9.0.0", + "json5": "^1.0.1", + "write-file-atomic": "^2.3.0" + } + }, + "node_modules/@react-native-voice/voice/node_modules/@expo/plist": { + "version": "0.0.13", + "resolved": "https://registry.npmjs.org/@expo/plist/-/plist-0.0.13.tgz", + "integrity": "sha512-zGPSq9OrCn7lWvwLLHLpHUUq2E40KptUFXn53xyZXPViI0k9lbApcR9KlonQZ95C+ELsf0BQ3gRficwK92Ivcw==", + "license": "MIT", + "dependencies": { + "base64-js": "^1.2.3", + "xmlbuilder": "^14.0.0", + "xmldom": "~0.5.0" + } + }, + "node_modules/@react-native-voice/voice/node_modules/brace-expansion": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@react-native-voice/voice/node_modules/fs-extra": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.0.tgz", + "integrity": "sha512-pmEYSk3vYsG/bF651KPUXZ+hvjpgWYw/Gc7W9NFUe3ZVLczKKWIij3IKpOrQcdw4TILtibFslZ0UmR8Vvzig4g==", + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^1.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@react-native-voice/voice/node_modules/getenv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/getenv/-/getenv-1.0.0.tgz", + "integrity": "sha512-7yetJWqbS9sbn0vIfliPsFgoXMKn/YMF+Wuiog97x+urnSRRRZ7xB+uVkwGKzRgq9CDFfMQnE9ruL5DHv9c6Xg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@react-native-voice/voice/node_modules/glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@react-native-voice/voice/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/@react-native-voice/voice/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@react-native-voice/voice/node_modules/jsonfile/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@react-native-voice/voice/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@react-native-voice/voice/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@react-native-voice/voice/node_modules/universalify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", + "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@react-native-voice/voice/node_modules/write-file-atomic": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", + "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", + "license": "ISC", + "dependencies": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + }, + "node_modules/@react-native-voice/voice/node_modules/xml2js": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", + "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/@react-native-voice/voice/node_modules/xml2js/node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@react-native-voice/voice/node_modules/xmlbuilder": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-14.0.0.tgz", + "integrity": "sha512-ts+B2rSe4fIckR6iquDjsKbQFK2NlUk6iG5nf14mDEyldgoc2nEKZ3jZWMPTxGQwVgToSjt6VGIho1H8/fNFTg==", + "license": "MIT", + "engines": { + "node": ">=8.0" + } + }, "node_modules/@react-native/assets-registry": { "version": "0.83.1", "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.83.1.tgz", @@ -7799,15 +7795,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/getenv": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/getenv/-/getenv-1.0.0.tgz", - "integrity": "sha512-7yetJWqbS9sbn0vIfliPsFgoXMKn/YMF+Wuiog97x+urnSRRRZ7xB+uVkwGKzRgq9CDFfMQnE9ruL5DHv9c6Xg==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -11857,15 +11844,6 @@ "node": ">=10.4.0" } }, - "node_modules/plist/node_modules/xmlbuilder": { - "version": "15.1.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", - "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", - "license": "MIT", - "engines": { - "node": ">=8.0" - } - }, "node_modules/popmotion": { "version": "11.0.3", "resolved": "https://registry.npmjs.org/popmotion/-/popmotion-11.0.3.tgz", @@ -12233,6 +12211,31 @@ "react-native": "*" } }, + "node_modules/react-native-enriched-markdown": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/react-native-enriched-markdown/-/react-native-enriched-markdown-0.5.0.tgz", + "integrity": "sha512-CYihNlcg8IjsrVbmypDjFHsDF6Z6vYO0yraw6CkR30SDsg99J6fcBrOkyWOItO8PIDD21jw8yD1eYP/0LBOCdA==", + "license": "MIT", + "workspaces": [ + "apps/example", + "apps/macos-example", + "apps/web-example" + ], + "peerDependencies": { + "@expo/config-plugins": ">=50.0.0", + "katex": ">=0.16.0", + "react": "*", + "react-native": "*" + }, + "peerDependenciesMeta": { + "@expo/config-plugins": { + "optional": true + }, + "katex": { + "optional": true + } + } + }, "node_modules/react-native-fit-image": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/react-native-fit-image/-/react-native-fit-image-1.5.5.tgz", @@ -13044,9 +13047,9 @@ "license": "MIT" }, "node_modules/sax": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.4.tgz", - "integrity": "sha512-1n3r/tGXO6b6VXMdFT54SHzT9ytu9yr7TaELowdYpMqY/Ao7EnlQGmAQ1+RatX7Tkkdm6hONI2owqNx2aZj5Sw==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", + "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", "license": "BlueOak-1.0.0", "engines": { "node": ">=11.0.0" @@ -14607,32 +14610,10 @@ "node": ">=10.0.0" } }, - "node_modules/xml2js": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", - "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", - "license": "MIT", - "dependencies": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/xml2js/node_modules/xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", - "license": "MIT", - "engines": { - "node": ">=4.0" - } - }, "node_modules/xmlbuilder": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-14.0.0.tgz", - "integrity": "sha512-ts+B2rSe4fIckR6iquDjsKbQFK2NlUk6iG5nf14mDEyldgoc2nEKZ3jZWMPTxGQwVgToSjt6VGIho1H8/fNFTg==", + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", + "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", "license": "MIT", "engines": { "node": ">=8.0" diff --git a/package.json b/package.json index 5c1ffe0a..ac8ac085 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "react": "19.2.0", "react-native": "0.83.1", "react-native-device-info": "^15.0.1", + "react-native-enriched-markdown": "^0.5.0", "react-native-fs": "^2.20.0", "react-native-gesture-handler": "^2.30.0", "react-native-haptic-feedback": "^2.3.3", diff --git a/src/components/ChatMessage/components/ActionMenuSheet.tsx b/src/components/ChatMessage/components/ActionMenuSheet.tsx index 1f380fe2..4b56c0dd 100644 --- a/src/components/ChatMessage/components/ActionMenuSheet.tsx +++ b/src/components/ChatMessage/components/ActionMenuSheet.tsx @@ -10,12 +10,11 @@ interface ActionMenuSheetProps { onClose: () => void; isUser: boolean; canEdit: boolean; - canRetry: boolean; canGenerateImage: boolean; styles: any; onCopy: () => void; onEdit: () => void; - onRetry: () => void; + onRetry?: () => void; onGenerateImage: () => void; } @@ -24,7 +23,6 @@ export function ActionMenuSheet({ onClose, isUser, canEdit, - canRetry, canGenerateImage, styles, onCopy, @@ -64,7 +62,7 @@ export function ActionMenuSheet({ )} - {canRetry && ( + {!!onRetry && ( ) { + useEffect(() => { + logger.log( + `[Structure] MessageContent mounted — role=${isUser ? 'user' : 'assistant'} isThinking=${isThinking} isStreaming=${isStreaming} contentLen=${content?.length ?? 0} hasThinking=${!!parsedContent.thinking} hasResponse=${!!parsedContent.response}` + ); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + if (isThinking) { return ( @@ -71,7 +79,17 @@ export function MessageContent({ ); } return ( - + { + logger.log('[Touch] assistant bubble View — onStartShouldSetResponder fired (returning false so children can claim)'); + return false; + }} + onMoveShouldSetResponder={() => { + logger.log('[Touch] assistant bubble View — onMoveShouldSetResponder fired'); + return false; + }} + > {parsedContent.response} {isStreaming && } diff --git a/src/components/ChatMessage/index.tsx b/src/components/ChatMessage/index.tsx index d80310b7..3ebf9838 100644 --- a/src/components/ChatMessage/index.tsx +++ b/src/components/ChatMessage/index.tsx @@ -230,12 +230,6 @@ export const ChatMessage: React.FC = ({ setIsEditing(false); }; - const handleLongPress = () => { - if (!showActions || isStreaming) return; - triggerHaptic('impactMedium'); - setShowActionMenu(true); - }; - const handleGenerateImage = () => { const source = isUser ? message.content : parsedContent.response; onGenerateImage?.(source.trim().slice(0, 500)); @@ -252,15 +246,12 @@ export const ChatMessage: React.FC = ({ onToggle={() => setShowThinking(!showThinking)} styles={styles} colors={colors} />; } const messageBody = ( - {hasAttachments && ( @@ -296,24 +287,22 @@ export const ChatMessage: React.FC = ({ {showGenerationDetails && !isUser && message.generationMeta && ( )} - + ); return ( <> {animateEntry ? {messageBody} : messageBody} - setShowActionMenu(false)} isUser={isUser} canEdit={!!onEdit} - canRetry={!!onRetry} canGenerateImage={canGenerateImage && !!onGenerateImage} styles={styles} onCopy={handleCopy} onEdit={handleEdit} - onRetry={handleRetry} + onRetry={onRetry ? handleRetry : undefined} onGenerateImage={handleGenerateImage} /> { + Clipboard.setString(code); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + }, [code]); -/** Custom link rule that constrains the Pressable wrapper width */ -function createLinkRule(onPress: (url: string) => void) { - return (node: any, renderChildren: any, _parent: any) => ( - onPress(node.attributes?.href ?? '')} - > - {renderChildren} - + return ( + + + {language} + + + + {copied ? 'Copied' : 'Copy'} + + + + + {code} + + ); } +const codeBlockStyles = StyleSheet.create({ + container: { + borderRadius: 6, + borderWidth: 1, + marginVertical: SPACING.sm, + overflow: 'hidden', + }, + header: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + paddingHorizontal: SPACING.md, + paddingVertical: 6, + borderBottomWidth: StyleSheet.hairlineWidth, + }, + lang: { + fontSize: 11, + fontFamily: FONTS.mono, + textTransform: 'uppercase', + }, + copyBtn: { + flexDirection: 'row', + alignItems: 'center', + gap: 4, + }, + copyLabel: { + fontSize: 11, + }, + code: { + fontSize: 12, + lineHeight: 18, + padding: SPACING.md, + }, +}); + +type Segment = + | { type: 'markdown'; content: string } + | { type: 'code'; language: string; code: string }; + +function splitSegments(text: string): Segment[] { + const segments: Segment[] = []; + const fence = /^```([^\n]*)\n([\s\S]*?)^```/gm; + let lastIndex = 0; + let match: RegExpExecArray | null; + + while ((match = fence.exec(text)) !== null) { + if (match.index > lastIndex) { + const md = text.slice(lastIndex, match.index).trim(); + if (md) segments.push({ type: 'markdown', content: md }); + } + segments.push({ type: 'code', language: match[1].trim(), code: match[2] }); + lastIndex = match.index + match[0].length; + } + + if (lastIndex < text.length) { + const md = text.slice(lastIndex).trim(); + if (md) segments.push({ type: 'markdown', content: md }); + } + + return segments; +} + interface MarkdownTextProps { children: string; dimmed?: boolean; @@ -39,162 +107,139 @@ interface MarkdownTextProps { export function MarkdownText({ children, dimmed }: MarkdownTextProps) { const { colors } = useTheme(); - const markdownStyles = useMemo( - () => createMarkdownStyles(colors, dimmed), - [colors, dimmed], - ); - const handleLinkPress = useCallback((url: string) => { - Linking.openURL(url); - return false; - }, []); + const markdownStyle = useMemo(() => createMarkdownStyle(colors, dimmed), [colors, dimmed]); - const processed = useMemo(() => preprocessMarkdown(children), [children]); - const rules = useMemo(() => ({ link: createLinkRule(handleLinkPress) }), [handleLinkPress]); + const segments = useMemo(() => splitSegments(preprocessMarkdown(children)), [children]); + + const handleLinkPress = useCallback((event: { url: string }) => { + logger.log(`[MarkdownText] link pressed: ${event.url}`); + Linking.openURL(event.url); + }, []); return ( - - {processed} - + + {segments.map((seg, i) => + seg.type === 'code' ? ( + + ) : ( + + ) + )} + ); } -function createMarkdownStyles(colors: ThemeColors, dimmed?: boolean) { +function createMarkdownStyle(colors: ThemeColors, dimmed?: boolean) { const textColor = dimmed ? colors.textSecondary : colors.text; return { - body: { - ...TYPOGRAPHY.body, + paragraph: { + fontSize: TYPOGRAPHY.body.fontSize, + fontFamily: TYPOGRAPHY.body.fontFamily, color: textColor, lineHeight: 20, - flexShrink: 1, + marginBottom: SPACING.sm, }, - heading1: { - ...TYPOGRAPHY.h2, - fontWeight: '600' as const, + h1: { + fontSize: 20, + fontFamily: FONTS.mono, color: textColor, marginTop: SPACING.sm, marginBottom: SPACING.xs, }, - heading2: { - ...TYPOGRAPHY.h2, + h2: { + fontSize: TYPOGRAPHY.h2.fontSize, + fontFamily: FONTS.mono, color: textColor, marginTop: SPACING.sm, marginBottom: SPACING.xs, }, - heading3: { - ...TYPOGRAPHY.h3, - fontWeight: '600' as const, + h3: { + fontSize: TYPOGRAPHY.h3.fontSize, + fontFamily: FONTS.mono, + color: textColor, + marginTop: SPACING.xs, + marginBottom: 2, + }, + h4: { + fontSize: TYPOGRAPHY.h3.fontSize, + fontFamily: FONTS.mono, + color: textColor, + marginTop: SPACING.xs, + marginBottom: 2, + }, + h5: { + fontSize: 12, + fontFamily: FONTS.mono, color: textColor, marginTop: SPACING.xs, marginBottom: 2, }, - heading4: { - ...TYPOGRAPHY.h3, + h6: { + fontSize: 11, + fontFamily: FONTS.mono, color: textColor, marginTop: SPACING.xs, marginBottom: 2, }, strong: { - fontWeight: '700' as const, + fontWeight: 'bold' as const, }, em: { fontStyle: 'italic' as const, }, - s: { - textDecorationLine: 'line-through' as const, - }, - code_inline: { + code: { fontFamily: FONTS.mono, fontSize: 13, - backgroundColor: colors.surfaceLight, color: colors.primary, - paddingHorizontal: 4, - paddingVertical: 1, - borderRadius: 3, - // Override default border - borderWidth: 0, - }, - fence: { - fontFamily: FONTS.mono, - fontSize: 12, backgroundColor: colors.surfaceLight, - color: textColor, - borderRadius: 6, - padding: SPACING.md, - marginVertical: SPACING.sm, - borderWidth: 0, }, - code_block: { + codeBlock: { fontFamily: FONTS.mono, fontSize: 12, - backgroundColor: colors.surfaceLight, color: textColor, + backgroundColor: colors.surfaceLight, + borderColor: colors.border, + borderWidth: 1, borderRadius: 6, padding: SPACING.md, - marginVertical: SPACING.sm, - borderWidth: 0, + marginBottom: SPACING.sm, }, blockquote: { - borderLeftWidth: 3, - borderLeftColor: colors.primary, - paddingLeft: SPACING.md, - marginLeft: 0, - marginVertical: SPACING.sm, + borderColor: colors.primary, + borderWidth: 3, backgroundColor: colors.surfaceLight, - borderRadius: 0, - paddingVertical: SPACING.xs, + marginBottom: SPACING.sm, }, - bullet_list: { - marginVertical: SPACING.xs, + list: { + fontSize: TYPOGRAPHY.body.fontSize, + fontFamily: TYPOGRAPHY.body.fontFamily, + color: textColor, + lineHeight: 20, + marginBottom: SPACING.xs, }, - ordered_list: { - marginVertical: SPACING.xs, + link: { + color: colors.primary, + underline: true, }, - list_item: { - marginVertical: 4, + thematicBreak: { + color: colors.border, + height: 1, + marginTop: SPACING.md, + marginBottom: SPACING.md, }, - // Tables table: { - borderWidth: 1, borderColor: colors.border, + borderWidth: 1, borderRadius: 4, - marginVertical: SPACING.sm, - }, - thead: { - backgroundColor: colors.surfaceLight, - }, - th: { - padding: SPACING.sm, - borderWidth: 0.5, - borderColor: colors.border, - fontWeight: '600' as const, - }, - td: { - padding: SPACING.sm, - borderWidth: 0.5, - borderColor: colors.border, - }, - tr: { - borderBottomWidth: 0.5, - borderColor: colors.border, - }, - hr: { - backgroundColor: colors.border, - height: 1, - marginVertical: SPACING.md, - }, - link: { - color: colors.primary, - textDecorationLine: 'underline' as const, - }, - paragraph: { - marginTop: 0, marginBottom: SPACING.sm, }, - // Image (unlikely in LLM text but handle gracefully) - image: { - borderRadius: 6, - }, }; } diff --git a/src/screens/ChatScreen/ChatScreenComponents.tsx b/src/screens/ChatScreen/ChatScreenComponents.tsx index 080027aa..f3c184fb 100644 --- a/src/screens/ChatScreen/ChatScreenComponents.tsx +++ b/src/screens/ChatScreen/ChatScreenComponents.tsx @@ -113,8 +113,9 @@ export const ChatHeader: React.FC<{ setShowModelSelector: (v: boolean) => void; setShowSettingsPanel: (v: boolean) => void; setShowProjectSelector: (v: boolean) => void; + setShowDebugLogs: (v: boolean) => void; isRemote?: boolean; -}> = ({ styles, colors, activeConversation, activeModel, activeModelName, activeImageModel, activeProject, navigation, setShowModelSelector, setShowSettingsPanel, setShowProjectSelector, isRemote }) => ( +}> = ({ styles, colors, activeConversation, activeModel, activeModelName, activeImageModel, activeProject, navigation, setShowModelSelector, setShowSettingsPanel, setShowProjectSelector, setShowDebugLogs, isRemote }) => ( navigation.goBack()}> @@ -149,6 +150,9 @@ export const ChatHeader: React.FC<{ + setShowDebugLogs(true)} testID="debug-logs-icon"> + + setShowSettingsPanel(true)} testID="chat-settings-icon"> diff --git a/src/screens/ChatScreen/index.tsx b/src/screens/ChatScreen/index.tsx index 27c5475b..bb349479 100644 --- a/src/screens/ChatScreen/index.tsx +++ b/src/screens/ChatScreen/index.tsx @@ -16,6 +16,7 @@ import { MessageRenderer } from './MessageRenderer'; import { NoModelScreen, LoadingScreen, ChatHeader } from './ChatScreenComponents'; import { ChatModalSection } from './ChatModalSection'; import { ChatMessageArea } from './ChatMessageArea'; +import { DebugLogsScreen } from '../../components/DebugLogsScreen'; function countConversationImages(conv: Conversation | undefined): number { return (conv?.messages || []).reduce((n: number, m: Message) => @@ -31,6 +32,7 @@ export const ChatScreen: React.FC = () => { const pendingNextRef = useRef(null); const [sharePromptVisible, setSharePromptVisible] = useState(false); + const [showDebugLogs, setShowDebugLogs] = useState(false); useEffect(() => subscribeSharePrompt(() => setSharePromptVisible(true)), []); // Only ONE AttachStep mounted at a time to avoid waypoint dots/lines. // chatSpotlight controls which index is active (3, 12, 15, or 16). @@ -184,6 +186,7 @@ export const ChatScreen: React.FC = () => { setShowModelSelector={chat.setShowModelSelector} setShowSettingsPanel={chat.setShowSettingsPanel} setShowProjectSelector={chat.setShowProjectSelector} + setShowDebugLogs={setShowDebugLogs} isRemote={chat.activeModelInfo?.isRemote} /> { {alertEl} setSharePromptVisible(false)} /> + {showDebugLogs && setShowDebugLogs(false)} />} ); };