Skip to content

feat(android): TextMate syntax highlighting (closes #35, refs #32)#36

Merged
LeslieOA merged 4 commits into
developfrom
feat/android-highlighting
May 29, 2026
Merged

feat(android): TextMate syntax highlighting (closes #35, refs #32)#36
LeslieOA merged 4 commits into
developfrom
feat/android-highlighting

Conversation

@LeslieOA

Copy link
Copy Markdown
Member

Summary

  • Closes Android: language prop — TextMate syntax highlighting #35. Wires Sora-Editor's `language-textmate` so the cross-platform `language` prop actually highlights on Android (markdown / json / javascript / typescript / html). Plaintext stays unhighlighted, matching iOS/macOS.
  • Lazy one-shot `GrammarRegistry` + `ThemeRegistry` setup in a new `SoraTextMate` object — first `setLanguage` triggers init; never set means no overhead.
  • Color-scheme-before-language ordering enforced (Sora silently no-ops if a `TextMateLanguage` is set against a non-`TextMateColorScheme`).

Assets

VS Code 1.94.0 grammars (MIT) + Dark Modern theme bundled under `android/src/main/assets/textmate/`. ~625KB grammars + 5KB theme; `language-textmate` adds ~1.2MB AAR-uncompressed via the joni regex engine. Justified for a source editor.

Expo plugin extension

`app.plugin.js` grew a `withAppBuildGradle` modifier that injects `coreLibraryDesugaringEnabled` + the `desugar_jdk_libs` dep into `android/app/build.gradle`. Sora's `language-textmate` AAR declares the desugaring requirement; without it the consumer build fails at `checkDebugAarMetadata`.

Test plan

  • `./gradlew :app:assembleDebug` → BUILD SUCCESSFUL after `expo prebuild --platform android` applies the desugar injection.
  • Needs your machine: `npm run android:clean && npm run android:prebuild && npm run android:dev`. Cycle the language chips in `App.android.tsx` and confirm token colours change per language.

🤖 Generated with Claude Code

LeslieOA and others added 4 commits May 13, 2026 19:04
Wire Sora-Editor's TextMate language module so the cross-platform
\`language\` prop (markdown / json / javascript / typescript / html)
actually highlights on Android. Plaintext stays unhighlighted, matching
iOS/macOS behaviour.

Library
- android/build.gradle: add language-textmate dep + core library
  desugaring (joni regex engine needs java.time on minSdk 24).
- android/src/.../SoraTextMate.kt: NEW. Lazy one-shot setup of Sora's
  GrammarRegistry + ThemeRegistry, with a string→scope map and an
  apply() that wires editor.colorScheme + editor.setEditorLanguage.
  Color-scheme-before-language ordering matters — Sora silently no-ops
  if a TextMateLanguage is set against a non-TextMateColorScheme.
- android/src/.../SourceEditorViewManager.kt: setLanguage now delegates
  to SoraTextMate.apply (was no-op).
- android/src/.../SourceEditorView.kt: forward setLanguage from manager.

Assets
- android/src/main/assets/textmate/{markdown,json,javascript,typescript,
  html}/*.tmLanguage.json: grammars from VS Code 1.94.0
  (microsoft/vscode/extensions/<lang>-basics/syntaxes/), MIT-licensed.
- android/src/main/assets/textmate/themes/dark_modern.json: VS Code's
  Dark Modern theme — closest off-the-shelf TextMate theme to the
  iOS/macOS Highlighter palette.
- android/src/main/assets/textmate/languages.json: manifest mapping
  language names to scope + grammar paths, consumed by Sora's
  GrammarRegistry.loadGrammars().

Asset payload: ~625KB grammars + 5KB theme. Joni regex engine adds
~1.2MB AAR-uncompressed via the language-textmate dep. Justified for a
source editor.

Expo config plugin
- app.plugin.js: extend with withAppBuildGradle modifier that injects
  coreLibraryDesugaringEnabled + the desugar_jdk_libs dep into
  android/app/build.gradle. Sora's language-textmate AAR declares the
  desugaring requirement; without it consumer build fails at
  checkDebugAarMetadata. Runs alongside the existing iOS Podfile
  cocoapods-spm injection.

Verified
- ./gradlew :app:assembleDebug → BUILD SUCCESSFUL.
- expo prebuild --platform android applies the desugar injection cleanly.

Not yet verified (needs your machine):
- Highlighting actually renders correctly per language. Test plan: cycle
  through the language chips in App.android.tsx and confirm token
  colours change.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
VS Code's `dark_modern.json` uses JSONC (trailing commas). Sora's
`TMParserJSON` is strict Gson and rejects it at runtime with:

  com.google.gson.stream.MalformedJsonException: Expected name at
  line 129 column 3 path $..widget.border

Swap to `darcula.json` (taken verbatim from sora-editor's sample app —
guaranteed to parse under their parser). Theme is a one-line constant
swap; can revisit when we wire the `theme` prop sub-task in #32.
The single-sample demo masked TextMate highlighting on tab switch — a
JS-flavored blob viewed under e.g. the markdown grammar tokenises as
plain paragraph text, which looks like "lost highlighting" but is
correct grammar behaviour. Mirror App.tsx's per-language SAMPLES record
so each tab loads syntax-appropriate content.

JS-side change only; library code unchanged.
Bring App.android.tsx to functional parity with App.tsx (modulo the
iOS-only @expo/ui glass aesthetic):

- Extract `stripTSTypes`, `wrapJSConsoleHTML`, `wrapMarkdownHTML` into
  `previewHelpers.ts`. Both apps import from there. Pure string-builders,
  no React/RN deps — safe to share.
- App.android.tsx now matches iOS in functionality:
  - Per-language sample text (already landed in the previous commit).
  - Source/Preview segmented toggle in the status bar, gated on
    previewable languages (markdown / html / js / ts).
  - WebView-rendered preview using the shared helpers (markdown via
    marked, HTML rendered as-is, JS executed in-browser, TS stripped
    then executed).
  - Auto-blur the editor when switching to preview.
  - Drop back to source view automatically when switching to a
    non-previewable language.

Skipped intentionally on Android (props are still ViewManager no-ops on
Android until their respective PRs against #32 land):
- lineNumbers toggle UI
- font / theme / contentInsets toolbar pieces
- @expo/ui glass-effect aesthetic — `@expo/ui/jetpack-compose` exposes
  a different component vocabulary (Switch / SegmentedButton / Chip vs
  Toggle / Picker / GlassEffectContainer), so true UI sharing isn't
  possible without a thicker abstraction layer than this PR warrants.
@LeslieOA LeslieOA merged commit 025c31c into develop May 29, 2026
1 of 2 checks passed
LeslieOA added a commit that referenced this pull request May 29, 2026
#32)

Replace the three style/layout no-op stubs in SourceEditorViewManager
with real wiring through SourceEditorView setters. Demo app gains a
matching `#` toggle in the status bar to flip the gutter at runtime.

Library
- SourceEditorView.kt:
  - setFont: maps `{family,size}` → `editor.typefaceText` +
    `editor.setTextSize`. Defaults to MONOSPACE at 14pt to match Apple's
    `monospacedSystemFont(ofSize: 14)` when family/size are absent.
  - setContentInsets: maps `{top,bottom,left,right}` (RN dp) → raw px
    via display-metrics, then `View.setPadding(left,top,right,bottom)`.
    Mirrors UIEdgeInsets semantics — applied to the editor's text
    container.
  - setLineNumbers: `editor.isLineNumberEnabled = value`. Sora ships the
    gutter ON by default; this lets the prop turn it off.
- SourceEditorViewManager.kt: forward each of the three props to the
  view setters. Only `setTheme` remains a no-op; that's the next sub-PR.

Demo
- App.android.tsx: pass `lineNumbers`, `font={size:13}`, and
  `contentInsets={top:12,bottom:12,left:8,right:8}` to SourceEditor —
  matches the iOS App.tsx defaults. Status bar gains a `#` toggle that
  flips lineNumbers state, mirroring iOS's Toggle(systemImage="number").

Verified
- ./gradlew :app:assembleDebug → BUILD SUCCESSFUL
- npm run typecheck clean

Stacked on top of feat/android-highlighting (PR #36); merge that first
to get a clean diff against develop here.
LeslieOA added a commit that referenced this pull request May 29, 2026
#32) (#38)

Replace the three style/layout no-op stubs in SourceEditorViewManager
with real wiring through SourceEditorView setters. Demo app gains a
matching `#` toggle in the status bar to flip the gutter at runtime.

Library
- SourceEditorView.kt:
  - setFont: maps `{family,size}` → `editor.typefaceText` +
    `editor.setTextSize`. Defaults to MONOSPACE at 14pt to match Apple's
    `monospacedSystemFont(ofSize: 14)` when family/size are absent.
  - setContentInsets: maps `{top,bottom,left,right}` (RN dp) → raw px
    via display-metrics, then `View.setPadding(left,top,right,bottom)`.
    Mirrors UIEdgeInsets semantics — applied to the editor's text
    container.
  - setLineNumbers: `editor.isLineNumberEnabled = value`. Sora ships the
    gutter ON by default; this lets the prop turn it off.
- SourceEditorViewManager.kt: forward each of the three props to the
  view setters. Only `setTheme` remains a no-op; that's the next sub-PR.

Demo
- App.android.tsx: pass `lineNumbers`, `font={size:13}`, and
  `contentInsets={top:12,bottom:12,left:8,right:8}` to SourceEditor —
  matches the iOS App.tsx defaults. Status bar gains a `#` toggle that
  flips lineNumbers state, mirroring iOS's Toggle(systemImage="number").

Verified
- ./gradlew :app:assembleDebug → BUILD SUCCESSFUL
- npm run typecheck clean

Stacked on top of feat/android-highlighting (PR #36); merge that first
to get a clean diff against develop here.
LeslieOA added a commit that referenced this pull request May 29, 2026
#44)

After this lands, every code-side and docs-side sub-task on #32 is
done; the tracking issue closes.

README.md
- Platform table: Android → "Shipping (Fabric / New Architecture)" at
  API 24. Drop "Stub only" row.
- Status section: name all three platforms.
- Quick start: clarify cocoapods-spm is iOS-only; macOS uses RN's
  first-party SPM, Android pulls Sora-Editor from Maven Central.
- Plugin snippet: add expo-build-properties android entries
  (minSdkVersion + newArchEnabled).
- Roadmap: drop "Android (TextKit alternative)" — it's done.
- Repo layout: describe android/ proper + the Expo CNG-shared
  example/expo-app/ (was example/ios-app/).
- Add "Running the Android example" section mirroring iOS scripts.
- License section: add Sora-Editor + LGPL boundary note.

docs/installation.md
- Add Android row to deployment-targets table (API 24).
- New "Backing libraries" subsection summarising STTextView vs Sora.
- Scope the SPM bridging section explicitly to iOS / macOS.
- New "Android (Expo CNG)" section: documents the autolinking-from-
  Maven flow + the coreLibraryDesugaring requirement that our config
  plugin auto-injects.
- New "Bare React Native — Android" section with the manual
  build.gradle snippet for non-Expo consumers.
- New "License note for Android" calling out the LGPL inheritance.
- Working examples block: ios-app → expo-app, add Android.

docs/usage.md
- `language` prop note: spell out the Android grammar source (VS Code
  TextMate via Sora) and the HTML caveat (no nested CSS/JS recursion
  on Android, unlike STTextView's attributed text).
- `lineNumbers` note: now applies to all platforms.
- `contentInsets` note: spell out the Android dp→px mapping.

docs/examples.md
- Add Android row to platform table — same expo-app/ project as iOS.
- Refresh layout/toolchain narrative for the Expo-shared model.
- Add android:run script line.

CHANGELOG.md
- Unreleased entry covering #34, #36, #38, #40, #42 plus the
  ios-app→expo-app rename.

Stacked on feat/ci-android (#42); merge order: #36#38#40#42
→ this.
@LeslieOA LeslieOA mentioned this pull request May 29, 2026
7 tasks
LeslieOA added a commit that referenced this pull request May 29, 2026
Wire the `theme` prop on Android. Bundles a light TextMate theme
(quietlight.json from Sora's sample app) alongside the existing
darcula.json. SoraTextMate now loads both at setup and exposes
applyTheme() that flips ThemeRegistry's active scheme + re-applies
TextMateColorScheme on the editor.

- 'light' / 'dark' map to the bundled themes by name.
- 'auto' (and any unknown value) reads
  Configuration.UI_MODE_NIGHT_MASK to pick between them — same
  semantics as the iOS overrideUserInterfaceStyle = .unspecified path.

Refactor: SoraTextMate.apply → applyLanguage / applyTheme, since the
two prop concerns now diverge. Caller code in SourceEditorView updated
accordingly.

Demo: App.android.tsx gains a tiny A/L/D segmented picker in the status
bar to exercise the prop. Initial state is 'auto'.

Verified: ./gradlew :app:assembleDebug → BUILD SUCCESSFUL,
npm run typecheck clean.

Stacked on feat/android-style-props (#38). Merge order: #36#38 →
this.
LeslieOA added a commit that referenced this pull request May 29, 2026
#40)

Wire the `theme` prop on Android. Bundles a light TextMate theme
(quietlight.json from Sora's sample app) alongside the existing
darcula.json. SoraTextMate now loads both at setup and exposes
applyTheme() that flips ThemeRegistry's active scheme + re-applies
TextMateColorScheme on the editor.

- 'light' / 'dark' map to the bundled themes by name.
- 'auto' (and any unknown value) reads
  Configuration.UI_MODE_NIGHT_MASK to pick between them — same
  semantics as the iOS overrideUserInterfaceStyle = .unspecified path.

Refactor: SoraTextMate.apply → applyLanguage / applyTheme, since the
two prop concerns now diverge. Caller code in SourceEditorView updated
accordingly.

Demo: App.android.tsx gains a tiny A/L/D segmented picker in the status
bar to exercise the prop. Initial state is 'auto'.

Verified: ./gradlew :app:assembleDebug → BUILD SUCCESSFUL,
npm run typecheck clean.

Stacked on feat/android-style-props (#38). Merge order: #36#38 →
this.
LeslieOA added a commit that referenced this pull request May 29, 2026
…42)

* feat(android): TextMate syntax highlighting (closes #35, refs #32)

Wire Sora-Editor's TextMate language module so the cross-platform
\`language\` prop (markdown / json / javascript / typescript / html)
actually highlights on Android. Plaintext stays unhighlighted, matching
iOS/macOS behaviour.

Library
- android/build.gradle: add language-textmate dep + core library
  desugaring (joni regex engine needs java.time on minSdk 24).
- android/src/.../SoraTextMate.kt: NEW. Lazy one-shot setup of Sora's
  GrammarRegistry + ThemeRegistry, with a string→scope map and an
  apply() that wires editor.colorScheme + editor.setEditorLanguage.
  Color-scheme-before-language ordering matters — Sora silently no-ops
  if a TextMateLanguage is set against a non-TextMateColorScheme.
- android/src/.../SourceEditorViewManager.kt: setLanguage now delegates
  to SoraTextMate.apply (was no-op).
- android/src/.../SourceEditorView.kt: forward setLanguage from manager.

Assets
- android/src/main/assets/textmate/{markdown,json,javascript,typescript,
  html}/*.tmLanguage.json: grammars from VS Code 1.94.0
  (microsoft/vscode/extensions/<lang>-basics/syntaxes/), MIT-licensed.
- android/src/main/assets/textmate/themes/dark_modern.json: VS Code's
  Dark Modern theme — closest off-the-shelf TextMate theme to the
  iOS/macOS Highlighter palette.
- android/src/main/assets/textmate/languages.json: manifest mapping
  language names to scope + grammar paths, consumed by Sora's
  GrammarRegistry.loadGrammars().

Asset payload: ~625KB grammars + 5KB theme. Joni regex engine adds
~1.2MB AAR-uncompressed via the language-textmate dep. Justified for a
source editor.

Expo config plugin
- app.plugin.js: extend with withAppBuildGradle modifier that injects
  coreLibraryDesugaringEnabled + the desugar_jdk_libs dep into
  android/app/build.gradle. Sora's language-textmate AAR declares the
  desugaring requirement; without it consumer build fails at
  checkDebugAarMetadata. Runs alongside the existing iOS Podfile
  cocoapods-spm injection.

Verified
- ./gradlew :app:assembleDebug → BUILD SUCCESSFUL.
- expo prebuild --platform android applies the desugar injection cleanly.

Not yet verified (needs your machine):
- Highlighting actually renders correctly per language. Test plan: cycle
  through the language chips in App.android.tsx and confirm token
  colours change.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(android): swap dark_modern (JSONC) → darcula (strict JSON)

VS Code's `dark_modern.json` uses JSONC (trailing commas). Sora's
`TMParserJSON` is strict Gson and rejects it at runtime with:

  com.google.gson.stream.MalformedJsonException: Expected name at
  line 129 column 3 path $..widget.border

Swap to `darcula.json` (taken verbatim from sora-editor's sample app —
guaranteed to parse under their parser). Theme is a one-line constant
swap; can revisit when we wire the `theme` prop sub-task in #32.

* fix(android-demo): per-language sample text

The single-sample demo masked TextMate highlighting on tab switch — a
JS-flavored blob viewed under e.g. the markdown grammar tokenises as
plain paragraph text, which looks like "lost highlighting" but is
correct grammar behaviour. Mirror App.tsx's per-language SAMPLES record
so each tab loads syntax-appropriate content.

JS-side change only; library code unchanged.

* refactor(example): share preview helpers, mirror iOS UI on Android

Bring App.android.tsx to functional parity with App.tsx (modulo the
iOS-only @expo/ui glass aesthetic):

- Extract `stripTSTypes`, `wrapJSConsoleHTML`, `wrapMarkdownHTML` into
  `previewHelpers.ts`. Both apps import from there. Pure string-builders,
  no React/RN deps — safe to share.
- App.android.tsx now matches iOS in functionality:
  - Per-language sample text (already landed in the previous commit).
  - Source/Preview segmented toggle in the status bar, gated on
    previewable languages (markdown / html / js / ts).
  - WebView-rendered preview using the shared helpers (markdown via
    marked, HTML rendered as-is, JS executed in-browser, TS stripped
    then executed).
  - Auto-blur the editor when switching to preview.
  - Drop back to source view automatically when switching to a
    non-previewable language.

Skipped intentionally on Android (props are still ViewManager no-ops on
Android until their respective PRs against #32 land):
- lineNumbers toggle UI
- font / theme / contentInsets toolbar pieces
- @expo/ui glass-effect aesthetic — `@expo/ui/jetpack-compose` exposes
  a different component vocabulary (Switch / SegmentedButton / Chip vs
  Toggle / Picker / GlassEffectContainer), so true UI sharing isn't
  possible without a thicker abstraction layer than this PR warrants.

* feat(android): font, contentInsets, lineNumbers props (closes #37, refs #32)

Replace the three style/layout no-op stubs in SourceEditorViewManager
with real wiring through SourceEditorView setters. Demo app gains a
matching `#` toggle in the status bar to flip the gutter at runtime.

Library
- SourceEditorView.kt:
  - setFont: maps `{family,size}` → `editor.typefaceText` +
    `editor.setTextSize`. Defaults to MONOSPACE at 14pt to match Apple's
    `monospacedSystemFont(ofSize: 14)` when family/size are absent.
  - setContentInsets: maps `{top,bottom,left,right}` (RN dp) → raw px
    via display-metrics, then `View.setPadding(left,top,right,bottom)`.
    Mirrors UIEdgeInsets semantics — applied to the editor's text
    container.
  - setLineNumbers: `editor.isLineNumberEnabled = value`. Sora ships the
    gutter ON by default; this lets the prop turn it off.
- SourceEditorViewManager.kt: forward each of the three props to the
  view setters. Only `setTheme` remains a no-op; that's the next sub-PR.

Demo
- App.android.tsx: pass `lineNumbers`, `font={size:13}`, and
  `contentInsets={top:12,bottom:12,left:8,right:8}` to SourceEditor —
  matches the iOS App.tsx defaults. Status bar gains a `#` toggle that
  flips lineNumbers state, mirroring iOS's Toggle(systemImage="number").

Verified
- ./gradlew :app:assembleDebug → BUILD SUCCESSFUL
- npm run typecheck clean

Stacked on top of feat/android-highlighting (PR #36); merge that first
to get a clean diff against develop here.

* feat(android): theme prop — light / dark / auto (closes #39, refs #32)

Wire the `theme` prop on Android. Bundles a light TextMate theme
(quietlight.json from Sora's sample app) alongside the existing
darcula.json. SoraTextMate now loads both at setup and exposes
applyTheme() that flips ThemeRegistry's active scheme + re-applies
TextMateColorScheme on the editor.

- 'light' / 'dark' map to the bundled themes by name.
- 'auto' (and any unknown value) reads
  Configuration.UI_MODE_NIGHT_MASK to pick between them — same
  semantics as the iOS overrideUserInterfaceStyle = .unspecified path.

Refactor: SoraTextMate.apply → applyLanguage / applyTheme, since the
two prop concerns now diverge. Caller code in SourceEditorView updated
accordingly.

Demo: App.android.tsx gains a tiny A/L/D segmented picker in the status
bar to exercise the prop. Initial state is 'auto'.

Verified: ./gradlew :app:assembleDebug → BUILD SUCCESSFUL,
npm run typecheck clean.

Stacked on feat/android-style-props (#38). Merge order: #36#38 →
this.

* ci: add android + macos build jobs, fix iOS paths (closes #41, refs #32)

CI hadn't run since the ios-app → expo-app rename (PR #34) and the
slug change (SourceEditorExampleiOS → SourceEditorExample). Latent
breakage; this catches it up plus adds the two missing platforms.

build-android (NEW)
- ubuntu-latest, JDK 17 (Sora's language-textmate AAR + the gradle
  plugin chain require it).
- android-actions/setup-android@v3 with platforms;android-35 +
  build-tools;35.0.0 to match android/build.gradle's
  compileSdkVersion 35.
- expo prebuild --platform android — runs our app.plugin.js
  withAppBuildGradle modifier, injects coreLibraryDesugaring into
  android/app/build.gradle.
- ./gradlew :app:assembleDebug.

build-macos (NEW — replaces the placeholder comment)
- macos-15, latest-stable Xcode (matches build-ios).
- gem install cocoapods only — macos-app uses RN's first-party
  spm_dependency helper, no cocoapods-spm needed.
- pod install + xcodebuild -workspace MacosApp.xcworkspace.

build-ios (FIX)
- example/ios-app → example/expo-app paths.
- xcworkspace + scheme renamed: SourceEditorExampleiOS →
  SourceEditorExample (driven by app.json name change in PR #34).

typecheck (FIX)
- example/ios-app → example/expo-app path for the example typecheck.

* docs: Android shipping across README + docs/* + CHANGELOG (closes #43) (#44)

After this lands, every code-side and docs-side sub-task on #32 is
done; the tracking issue closes.

README.md
- Platform table: Android → "Shipping (Fabric / New Architecture)" at
  API 24. Drop "Stub only" row.
- Status section: name all three platforms.
- Quick start: clarify cocoapods-spm is iOS-only; macOS uses RN's
  first-party SPM, Android pulls Sora-Editor from Maven Central.
- Plugin snippet: add expo-build-properties android entries
  (minSdkVersion + newArchEnabled).
- Roadmap: drop "Android (TextKit alternative)" — it's done.
- Repo layout: describe android/ proper + the Expo CNG-shared
  example/expo-app/ (was example/ios-app/).
- Add "Running the Android example" section mirroring iOS scripts.
- License section: add Sora-Editor + LGPL boundary note.

docs/installation.md
- Add Android row to deployment-targets table (API 24).
- New "Backing libraries" subsection summarising STTextView vs Sora.
- Scope the SPM bridging section explicitly to iOS / macOS.
- New "Android (Expo CNG)" section: documents the autolinking-from-
  Maven flow + the coreLibraryDesugaring requirement that our config
  plugin auto-injects.
- New "Bare React Native — Android" section with the manual
  build.gradle snippet for non-Expo consumers.
- New "License note for Android" calling out the LGPL inheritance.
- Working examples block: ios-app → expo-app, add Android.

docs/usage.md
- `language` prop note: spell out the Android grammar source (VS Code
  TextMate via Sora) and the HTML caveat (no nested CSS/JS recursion
  on Android, unlike STTextView's attributed text).
- `lineNumbers` note: now applies to all platforms.
- `contentInsets` note: spell out the Android dp→px mapping.

docs/examples.md
- Add Android row to platform table — same expo-app/ project as iOS.
- Refresh layout/toolchain narrative for the Expo-shared model.
- Add android:run script line.

CHANGELOG.md
- Unreleased entry covering #34, #36, #38, #40, #42 plus the
  ios-app→expo-app rename.

Stacked on feat/ci-android (#42); merge order: #36#38#40#42
→ this.

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Android: language prop — TextMate syntax highlighting

1 participant