diff --git a/CHANGELOG.md b/CHANGELOG.md index dce5f7d..247c31a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,19 @@ Pre-1.0, the JS API may evolve between minor versions. See the README's roadmap. ## [Unreleased] +### Added + +- **Android support.** Bare RN Fabric component wrapping [Sora-Editor](https://github.com/Rosemoe/sora-editor) 0.23.6 (LGPL-2.1, consumed dynamically as a Gradle `implementation` AAR). Targets react-native 0.81 New Architecture, minSdk 24. ([#34](https://github.com/workspace-sh/react-native-source-editor/pull/34), closes [#33](https://github.com/workspace-sh/react-native-source-editor/issues/33)) +- **TextMate syntax highlighting on Android** for the existing six languages (markdown / json / javascript / typescript / html). VS Code 1.94.0 grammars + `darcula.json` theme bundled as Android assets. ([#36](https://github.com/workspace-sh/react-native-source-editor/pull/36), closes [#35](https://github.com/workspace-sh/react-native-source-editor/issues/35)) +- **`font`, `contentInsets`, `lineNumbers` on Android.** `font` → Sora `typefaceText` + `textSize`; `contentInsets` → `View.setPadding` with dp→px conversion; `lineNumbers` → `editor.isLineNumberEnabled`. ([#38](https://github.com/workspace-sh/react-native-source-editor/pull/38), closes [#37](https://github.com/workspace-sh/react-native-source-editor/issues/37)) +- **`theme` prop on Android.** `light` / `dark` map to bundled TextMate themes; `auto` reads `Configuration.UI_MODE_NIGHT_MASK`. ([#40](https://github.com/workspace-sh/react-native-source-editor/pull/40), closes [#39](https://github.com/workspace-sh/react-native-source-editor/issues/39)) +- **Expo config plugin: Android support.** `app.plugin.js` now also injects `coreLibraryDesugaring` into `android/app/build.gradle` (Sora's TextMate AAR requires it on minSdk < 26). Same plugin entry covers both platforms. ([#36](https://github.com/workspace-sh/react-native-source-editor/pull/36)) +- **CI: Android + macOS jobs.** GitHub Actions now builds all three platforms on every PR. ([#42](https://github.com/workspace-sh/react-native-source-editor/pull/42), closes [#41](https://github.com/workspace-sh/react-native-source-editor/issues/41)) + +### Changed + +- **`example/ios-app/` → `example/expo-app/`.** Same Expo CNG project now serves both iOS and Android — Expo prebuild generates `ios/` and `android/` from the same source. The bare-RN `example/macos-app/` stays separate (Expo doesn't support macOS). ([#34](https://github.com/workspace-sh/react-native-source-editor/pull/34)) + ## [0.1.0] — 2026-05-10 First milestone tag. Both target platforms shipping; library not yet on npm (install via local path until v1.0). diff --git a/README.md b/README.md index d290901..3181dba 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # react-native-source-editor -A native source editor component for React Native, built as a Fabric component wrapping [STTextView](https://github.com/krzyzanowskim/STTextView) on iOS and macOS. Designed to drop into both Expo CNG apps (via the included config plugin) and bare React Native / react-native-macos hosts. +A native source editor component for React Native. Fabric component wrapping [STTextView](https://github.com/krzyzanowskim/STTextView) on iOS and macOS, and [Sora-Editor](https://github.com/Rosemoe/sora-editor) on Android. Designed to drop into both Expo CNG apps (via the included config plugin) and bare React Native / react-native-macos hosts. ## Status -iOS and macOS both ship today on the New Architecture. iOS targets Expo CNG (with the bundled config plugin); macOS targets bare react-native-macos 0.81. Pre-1.0, expect the JS API to evolve. +iOS, macOS, and Android all ship today on the New Architecture. iOS and Android target Expo CNG (with the bundled config plugin); macOS targets bare react-native-macos 0.81. Pre-1.0, expect the JS API to evolve. Project tracking lives on the [project board](https://github.com/orgs/workspace-sh/projects/2). @@ -14,22 +14,24 @@ Project tracking lives on the [project board](https://github.com/orgs/workspace- | --- | --- | --- | | iOS | Shipping (Fabric / New Architecture) | 16.0 | | macOS | Shipping (Fabric / New Architecture) | 14.0 | +| Android | Shipping (Fabric / New Architecture) | API 24 | | iPadOS | Roadmap | — | -| Android | Stub only | — | | Windows | Roadmap | — | | Linux | Roadmap | — | | Web | Out of scope for v1 | — | -iOS / macOS minimums are STTextView's floors, not ours. +iOS / macOS minimums are STTextView's floors, Android's is Sora-Editor's — not ours. ## Quick start ```sh # Library is not yet on npm — install via local path while v1 is in flight npm install /path/to/react-native-source-editor -gem install cocoapods-spm # required: STTextView is SPM-only +gem install cocoapods-spm # iOS only: STTextView is SPM-only on Apple platforms ``` +(macOS and Android skip `cocoapods-spm` — macOS uses RN's first-party `spm_dependency` helper; Android pulls Sora-Editor from Maven Central.) + ```tsx import SourceEditor from '@workspace-sh/react-native-source-editor'; @@ -45,14 +47,15 @@ export default function Editor() { } ``` -For Expo CNG apps, register the plugin in `app.json` so `cocoapods-spm` + the STTextView SPM package get injected into the Podfile during `expo prebuild`: +For Expo CNG apps, register the plugin in `app.json` so the iOS Podfile gets `cocoapods-spm` + the STTextView SPM package, and the Android `app/build.gradle` gets `coreLibraryDesugaring` (Sora-Editor's TextMate AAR requires it on minSdk < 26): ```json { "plugins": [ "@workspace-sh/react-native-source-editor", ["expo-build-properties", { - "ios": { "deploymentTarget": "16.0", "useFrameworks": "static" } + "ios": { "deploymentTarget": "16.0", "useFrameworks": "static" }, + "android": { "minSdkVersion": 24, "newArchEnabled": true } }] ] } @@ -66,9 +69,9 @@ Bare-RN hosts: see [docs/installation.md](docs/installation.md) for the manual ` ## Roadmap -- **v0.x (now)** — bare RN Fabric library, iOS + macOS shipping. JS API: text, selection, font, theme, language (markdown / json / js / ts / html), `lineNumbers` gutter toggle, `contentInsets`, imperative `focus`/`blur`. +- **v0.x (now)** — bare RN Fabric library, iOS + macOS + Android shipping. JS API: text, selection, font, theme, language (markdown / json / js / ts / html), `lineNumbers` gutter toggle, `contentInsets`, imperative `focus`/`blur`. - **v1.0** — expanded font customisation, npm publish under `@workspace-sh/react-native-source-editor`. -- **Post-1.0** — iPadOS polish, Android (TextKit alternative), Windows, Linux, Web. +- **Post-1.0** — iPadOS polish, Windows, Linux, Web. ## Contributing @@ -78,9 +81,10 @@ Contributions and bug reports are welcome. - `src/` — TypeScript surface. `SourceEditor.tsx` is the high-level wrapper; `SourceEditorView.tsx` exposes the lower-level codegen Fabric component. - `ios/` — Native sources, shared across iOS + macOS. `SourceEditor.{h,mm}` is the Fabric Obj-C++ wrapper; `SourceEditorImpl.swift` and `Highlighter.swift` are the Swift impl (UIKit/AppKit branched via `#if os(iOS)` / `#elseif os(macOS)`). `ReactNativeSourceEditor.h` is the framework's umbrella anchor. -- `example/ios-app/` — Expo SDK 55 host. Uses Expo CNG, owned by `expo prebuild` — never run `pod install` here manually. +- `android/` — Kotlin Fabric component wrapping Sora-Editor. `SourceEditorPackage.kt` (autolinked entry), `SourceEditorViewManager.kt` (codegen-driven), `SourceEditorView.kt` (Sora `CodeEditor` host), `SoraTextMate.kt` (grammar + theme registry), `events/*.kt`. TextMate grammar JSONs and themes live under `src/main/assets/textmate/`. +- `example/expo-app/` — Expo SDK 55 host serving both iOS and Android. Uses Expo CNG, owned by `expo prebuild` — never run `pod install` (iOS) or hand-edit `android/` here manually. - `example/macos-app/` — react-native-macos 0.81 host. Standard bare-RN toolchain (`pod install`, `react-native run-macos`). Wires STTextView via RN's first-party `spm_dependency` helper rather than the third-party `cocoapods-spm` plugin used on iOS; see the app's [README](example/macos-app/README.md). -- `app.plugin.js` — Expo config plugin. Injects `cocoapods-spm` + the `STTextView` SPM package into the consumer's `Podfile` during `expo prebuild` (iOS path only). +- `app.plugin.js` — Expo config plugin. Injects `cocoapods-spm` + the `STTextView` SPM package into the iOS Podfile and `coreLibraryDesugaring` into the Android `app/build.gradle` during `expo prebuild`. - `ReactNativeSourceEditor.podspec` — root podspec; bare RN libraries put it here so autolinking finds it. ### Development setup @@ -94,13 +98,25 @@ npm run typecheck ```sh npm run ios:plugin # one-time: gem install cocoapods-spm -npm run ios:install # one-time: install example/ios-app deps +npm run ios:install # one-time: install example/expo-app deps npm run ios:run # build + launch on iOS simulator npm run ios:dev # concurrently: clean Metro + run:ios ``` Other scripts: `ios:start`, `ios:clear`, `ios:run:device`, `ios:run:device:release`, `ios:clean` (wipes generated `ios/`), `ios:prebuild`. Pod install is owned by `expo run:ios` — there is intentionally no `ios:pods` script. +### Running the Android example + +Same Expo project as iOS — different `android:*` script set: + +```sh +npm run android:install # one-time: install example/expo-app deps +npm run android:run # prebuild + gradle build + launch on emulator/device +npm run android:dev # concurrently: clean Metro + run-android +``` + +Other scripts: `android:start`, `android:clear`, `android:run:device`, `android:clean` (wipes generated `android/`), `android:prebuild`. Requires JDK 17 + an Android SDK with API 35 + an emulator or device. + ### Running the macOS example ```sh @@ -116,10 +132,12 @@ Other scripts: `macos:start`, `macos:clear`, `macos:run`, `macos:clean` (wipes ` - Branch off `develop`; PRs target `develop`. `main` mirrors the latest release. - One PR per project-board issue. Reference the issue in the PR description. - Squash-merge with the PR title as the commit subject. -- CI runs typecheck + iOS build via `.github/workflows/ci.yml`. +- CI runs typecheck + iOS + Android + macOS builds via `.github/workflows/ci.yml`. ## License [MIT](LICENSE) © Leslie Owusu-Appiah. -Built on top of [STTextView](https://github.com/krzyzanowskim/STTextView) by [Marcin Krzyzanowski](https://github.com/krzyzanowskim) (BSD-2-Clause). Without it, this library would not exist. +Built on top of [STTextView](https://github.com/krzyzanowskim/STTextView) by [Marcin Krzyzanowski](https://github.com/krzyzanowskim) (BSD-2-Clause) on Apple platforms, and [Sora-Editor](https://github.com/Rosemoe/sora-editor) by [Rosemoe](https://github.com/Rosemoe) (LGPL-2.1) on Android. Without them, this library would not exist. + +The Sora-Editor dependency is consumed dynamically as a Gradle `implementation` AAR (no source modification, no static linking) — the standard pattern for LGPL compliance in MIT-licensed downstream apps. If you fork the AAR or relink statically, you take on the LGPL relinking obligations yourself. diff --git a/docs/examples.md b/docs/examples.md index 9c83681..ecac4d0 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -4,16 +4,20 @@ Each example is a runnable React Native app under [`example/`](../example/). | Platform | App | Toolchain | Status | | --- | --- | --- | --- | -| iOS | [`example/ios-app/`](../example/ios-app/) | Expo SDK 55 (CNG) | Shipping | +| iOS | [`example/expo-app/`](../example/expo-app/) | Expo SDK 55 (CNG) | Shipping | +| Android | [`example/expo-app/`](../example/expo-app/) | Expo SDK 55 (CNG) | Shipping | | macOS | [`example/macos-app/`](../example/macos-app/) | react-native-macos 0.81 (bare RN) | Shipping | -Each app demonstrates the editor in a split-pane layout with markdown on one side and TypeScript on the other, alongside standard RN components (`Switch`, `Button`, `SafeAreaView`) so the integration story is visible. +iOS and Android share the same Expo CNG project (`expo-app/`) — Expo prebuild generates `ios/` and `android/` from the same `App.tsx` / `App.android.tsx` pair. The macOS example lives in its own bare-RN-macos project because Expo doesn't support the platform. -The two examples deliberately use different toolchains so the library is exercised through both the Expo CNG path (config plugin injects the Podfile mods) and the bare-RN path (hand-edited Podfile, manual `pod install`). +Each app demonstrates the editor with a multi-language tab picker, syntax highlighting per language, and a Source/Preview toggle (Markdown / HTML / JS / TS) backed by an in-page WebView. + +The two project layouts deliberately exercise different toolchains so the library is verified through both the Expo CNG path (config plugin injects the Podfile + Gradle mods) and the bare-RN path (hand-edited Podfile, manual `pod install`). See each app's `README.md` for prerequisites and run instructions, or use the root-level scripts: ```sh -npm run ios:run # iOS example via Expo -npm run macos:dev # macOS example via bare react-native-macos +npm run ios:run # iOS via Expo CNG +npm run android:run # Android via Expo CNG (same project as iOS) +npm run macos:dev # macOS via bare react-native-macos ``` diff --git a/docs/installation.md b/docs/installation.md index ba3f61d..9a10e4f 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -14,10 +14,16 @@ Once v1 ships, it will be published as `@workspace-sh/react-native-source-editor | --- | --- | | iOS | 16.0 | | macOS | 14.0 | +| Android | API 24 (Android 7.0) | -These are STTextView's floors. Bump your app's targets accordingly. +iOS / macOS minimums are STTextView's floors; Android's is Sora-Editor's. Bump your app's targets accordingly. -## Swift Package Manager bridging +## Backing libraries + +- **iOS / macOS**: [STTextView](https://github.com/krzyzanowskim/STTextView) (Swift / SPM-only). +- **Android**: [Sora-Editor](https://github.com/Rosemoe/sora-editor) (Kotlin, Maven Central). + +## Swift Package Manager bridging (iOS / macOS) STTextView is distributed only via SPM. The two supported toolchains use different bridges, picked by an env var the library podspec reads: @@ -107,7 +113,49 @@ target 'YourApp' do end ``` +## Android (Expo CNG) + +No SPM, no extra gem — Sora-Editor comes from Maven Central, autolinked via the standard React Native gradle plugin. + +The same Expo config plugin entry that handles iOS also injects `coreLibraryDesugaring` into `android/app/build.gradle`. Sora's `language-textmate` AAR declares a desugaring requirement (its [Joni](https://github.com/jruby/joni) regex engine uses `java.time` on `minSdk < 26`); without it, your Android build fails at `checkDebugAarMetadata`. + +```json +{ + "plugins": [ + "@workspace-sh/react-native-source-editor", + ["expo-build-properties", { + "ios": { "deploymentTarget": "16.0", "useFrameworks": "static" }, + "android": { "minSdkVersion": 24, "newArchEnabled": true } + }] + ] +} +``` + +Build with `expo run:android`. Same as iOS, **don't hand-edit the generated `android/` directory** — Expo CNG owns it and your changes will be overwritten on the next prebuild. + +### Bare React Native — Android + +If you're not on Expo CNG, your `android/app/build.gradle` needs the same desugaring config that the plugin injects. Add to the `android { ... }` block: + +```groovy +compileOptions { + coreLibraryDesugaringEnabled true + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 +} +``` + +…and to the `dependencies { ... }` block: + +```groovy +coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.5' +``` + +### License note for Android + +Sora-Editor is **LGPL-2.1**. We consume it dynamically as a Gradle `implementation` AAR — no source modification, no static linking — which is the standard pattern that lets MIT-licensed downstream apps depend on this library without inheriting LGPL relinking obligations on their own code. **Don't fork the AAR or relink statically** unless you're prepared to take on those obligations yourself. + ## Working examples -- iOS (Expo CNG): [`example/ios-app/`](../example/ios-app/) — runnable with `npm run ios:run` from the repo root. +- iOS + Android (Expo CNG): [`example/expo-app/`](../example/expo-app/) — runnable with `npm run ios:run` and `npm run android:run` from the repo root. - macOS (bare RN-macos): [`example/macos-app/`](../example/macos-app/) — runnable with `npm run macos:dev` from the repo root. diff --git a/docs/usage.md b/docs/usage.md index 27a82cc..44c267e 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -35,9 +35,9 @@ export default function Editor() { | `editable` | `boolean` | `true` | Disables editing when `false`. | | `font` | `{ family?: string; size?: number }` | system mono @ 14 | Falls back to monospaced system font if `family` is missing or unresolvable. | | `theme` | `'light' \| 'dark' \| 'auto'` | `'auto'` | `auto` follows system appearance. | -| `language` | `'plaintext' \| 'markdown' \| 'json' \| 'javascript' \| 'typescript' \| 'html'` | `'plaintext'` | Syntax highlighting via attributed text. `html` highlights nested CSS (in `