├── .watchmanconfig
├── example
├── .watchmanconfig
├── jest.config.js
├── .bundle
│ └── config
├── .eslintrc.js
├── app.json
├── .prettierrc.js
├── android
│ ├── app
│ │ ├── debug.keystore
│ │ ├── src
│ │ │ ├── main
│ │ │ │ ├── res
│ │ │ │ │ ├── values
│ │ │ │ │ │ ├── strings.xml
│ │ │ │ │ │ └── styles.xml
│ │ │ │ │ ├── mipmap-hdpi
│ │ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ │ ├── mipmap-mdpi
│ │ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ │ ├── mipmap-xhdpi
│ │ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ │ └── drawable
│ │ │ │ │ │ └── rn_edit_text_material.xml
│ │ │ │ ├── AndroidManifest.xml
│ │ │ │ └── java
│ │ │ │ │ └── com
│ │ │ │ │ └── nitrotextexample
│ │ │ │ │ ├── MainActivity.kt
│ │ │ │ │ └── MainApplication.kt
│ │ │ └── debug
│ │ │ │ └── AndroidManifest.xml
│ │ ├── proguard-rules.pro
│ │ └── build.gradle
│ ├── gradle
│ │ └── wrapper
│ │ │ ├── gradle-wrapper.jar
│ │ │ └── gradle-wrapper.properties
│ ├── settings.gradle
│ ├── build.gradle
│ ├── gradle.properties
│ └── gradlew.bat
├── ios
│ ├── NitroTextExample
│ │ ├── Images.xcassets
│ │ │ ├── Contents.json
│ │ │ └── AppIcon.appiconset
│ │ │ │ └── Contents.json
│ │ ├── PrivacyInfo.xcprivacy
│ │ ├── AppDelegate.swift
│ │ ├── Info.plist
│ │ └── LaunchScreen.storyboard
│ ├── NitroTextExample.xcworkspace
│ │ └── contents.xcworkspacedata
│ ├── .xcode.env
│ ├── Podfile
│ └── NitroTextExample.xcodeproj
│ │ └── xcshareddata
│ │ └── xcschemes
│ │ └── NitroTextExample.xcscheme
├── index.js
├── tsconfig.json
├── react-native.config.js
├── metro.config.js
├── babel.config.js
├── Gemfile
├── .gitignore
├── package.json
├── src
│ └── App.tsx
├── Gemfile.lock
└── README.md
├── bunfig.toml
├── nitrogen
└── generated
│ ├── .gitattributes
│ ├── ios
│ ├── c++
│ │ └── HybridNitroTextSpecSwift.cpp
│ ├── NitroTextAutolinking.mm
│ ├── swift
│ │ ├── Renderer.swift
│ │ ├── FontStyle.swift
│ │ ├── EllipsizeMode.swift
│ │ ├── TextTransform.swift
│ │ ├── TextDecorationStyle.swift
│ │ ├── TextAlign.swift
│ │ ├── LineBreakStrategyIOS.swift
│ │ ├── TextDecorationLine.swift
│ │ ├── TextLayoutEvent.swift
│ │ ├── Func_void.swift
│ │ ├── MenuItem.swift
│ │ ├── Func_void_TextLayoutEvent.swift
│ │ ├── DynamicTypeRamp.swift
│ │ ├── FontWeight.swift
│ │ ├── TextLayout.swift
│ │ └── HybridNitroTextSpec.swift
│ ├── NitroTextAutolinking.swift
│ ├── NitroText+autolinking.rb
│ ├── NitroText-Swift-Cxx-Bridge.cpp
│ └── NitroText-Swift-Cxx-Umbrella.hpp
│ └── shared
│ ├── json
│ └── NitroTextConfig.json
│ └── c++
│ ├── TextLayoutEvent.hpp
│ ├── MenuItem.hpp
│ ├── Renderer.hpp
│ ├── FontStyle.hpp
│ ├── EllipsizeMode.hpp
│ ├── TextTransform.hpp
│ ├── TextDecorationStyle.hpp
│ ├── LineBreakStrategyIOS.hpp
│ ├── TextAlign.hpp
│ ├── TextDecorationLine.hpp
│ └── TextLayout.hpp
├── src
├── index.ts
├── constants
│ ├── index.ts
│ └── css.ts
├── renderers.ts
├── renderers
│ ├── index.ts
│ ├── types.ts
│ └── utils.ts
├── types.ts
├── specs
│ └── nitro-text.nitro.ts
└── nitro-text.tsx
├── babel.config.js
├── ios
├── Bridge.h
├── HybridNitroTextComponentOverride.mm
├── NitroTextImpl+Paragraph.swift
├── NitroTextImpl+Fragment.swift
└── NitroTextImpl+Attributes.swift
├── react-native.config.js
├── cpp
├── NitroTextUtil.cpp
├── NitroTextUtil.hpp
├── NitroTextComponentDescriptor.hpp
├── NitroTextShadowNode.hpp
├── NitroTextComponentDescriptor.cpp
└── NitroTextLogger.hpp
├── jest.config.js
├── nitro.json
├── .github
├── pull_request_template.md
├── ISSUE_TEMPLATE
│ ├── question.yml
│ ├── feature_request.yml
│ └── bug_report.yml
├── dependabot.yml
└── workflows
│ ├── release.yml
│ ├── android-build.yml
│ └── ios-build.yml
├── NitroText.podspec
├── tsconfig.json
├── .gitignore
├── LICENSE
├── release.config.cjs
├── eslint.config.mjs
├── package.json
└── README.md
/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/example/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/bunfig.toml:
--------------------------------------------------------------------------------
1 | [install]
2 | linker = "hoisted"
3 |
--------------------------------------------------------------------------------
/nitrogen/generated/.gitattributes:
--------------------------------------------------------------------------------
1 | ** linguist-generated=true
2 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './nitro-text'
2 | export * from './types'
3 |
--------------------------------------------------------------------------------
/src/constants/index.ts:
--------------------------------------------------------------------------------
1 | export * from './html'
2 | export * from './css'
3 |
--------------------------------------------------------------------------------
/example/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | preset: 'react-native',
3 | };
4 |
--------------------------------------------------------------------------------
/example/.bundle/config:
--------------------------------------------------------------------------------
1 | BUNDLE_PATH: "vendor/bundle"
2 | BUNDLE_FORCE_RUBY_PLATFORM: 1
3 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ['module:@react-native/babel-preset'],
3 | }
4 |
--------------------------------------------------------------------------------
/example/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | extends: '@react-native',
4 | };
5 |
--------------------------------------------------------------------------------
/example/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "NitroTextExample",
3 | "displayName": "NitroTextExample"
4 | }
5 |
--------------------------------------------------------------------------------
/example/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | arrowParens: 'avoid',
3 | singleQuote: true,
4 | trailingComma: 'all',
5 | };
6 |
--------------------------------------------------------------------------------
/ios/Bridge.h:
--------------------------------------------------------------------------------
1 | //
2 | // Bridge.h
3 | // nitro-text
4 | //
5 | // Created by Patrick Kabwe on 9/1/2025
6 | //
7 |
8 | #pragma once
9 |
--------------------------------------------------------------------------------
/src/renderers.ts:
--------------------------------------------------------------------------------
1 | export { renderStringChildren } from './renderers/index'
2 | export type { RenderResult } from './renderers/index'
3 |
--------------------------------------------------------------------------------
/example/android/app/debug.keystore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/patrickkabwe/react-native-nitro-text/HEAD/example/android/app/debug.keystore
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | NitroTextExample
3 |
4 |
--------------------------------------------------------------------------------
/example/ios/NitroTextExample/Images.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/example/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/patrickkabwe/react-native-nitro-text/HEAD/example/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/patrickkabwe/react-native-nitro-text/HEAD/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/patrickkabwe/react-native-nitro-text/HEAD/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/patrickkabwe/react-native-nitro-text/HEAD/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/patrickkabwe/react-native-nitro-text/HEAD/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/patrickkabwe/react-native-nitro-text/HEAD/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/patrickkabwe/react-native-nitro-text/HEAD/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/patrickkabwe/react-native-nitro-text/HEAD/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/patrickkabwe/react-native-nitro-text/HEAD/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/patrickkabwe/react-native-nitro-text/HEAD/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/patrickkabwe/react-native-nitro-text/HEAD/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/react-native.config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @type {import('@react-native-community/cli-types').DependencyConfig}
3 | */
4 | module.exports = {
5 | dependency: {
6 | autolinkTransitiveDependencies: true,
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/example/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @format
3 | */
4 |
5 | import { AppRegistry } from 'react-native';
6 | import App from './src/App';
7 | import { name as appName } from './app.json';
8 |
9 | AppRegistry.registerComponent(appName, () => App);
10 |
--------------------------------------------------------------------------------
/cpp/NitroTextUtil.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // NitroTextUtil.cpp
3 | // Utility functions for NitroText
4 | //
5 |
6 | #include "NitroTextUtil.hpp"
7 |
8 | // Currently only contains macros, so this file is mostly empty.
9 | // Kept for future utility functions if needed.
10 |
11 |
--------------------------------------------------------------------------------
/example/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/example/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@react-native/typescript-config",
3 | "include": ["**/*.ts", "**/*.tsx"],
4 | "exclude": ["**/node_modules", "**/Pods"],
5 | "compilerOptions": {
6 | "strict": true,
7 | "baseUrl": ".",
8 | "paths": {
9 | "react-native-nitro-text": ["../src"]
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/example/ios/NitroTextExample.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | preset: 'react-native',
3 | testMatch: ['**/__tests__/**/*.ts', '**/__tests__/**/*.tsx', '**/*.test.ts', '**/*.test.tsx'],
4 | transformIgnorePatterns: [
5 | 'node_modules/(?!(react-native|@react-native|react-native-nitro-modules|@react-native-community|@react-native/js-polyfills)/)',
6 | ],
7 | }
8 |
9 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/example/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
9 |
--------------------------------------------------------------------------------
/example/android/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement { includeBuild("../../node_modules/@react-native/gradle-plugin") }
2 | plugins { id("com.facebook.react.settings") }
3 | extensions.configure(com.facebook.react.ReactSettingsExtension){ ex -> ex.autolinkLibrariesFromCommand() }
4 | rootProject.name = 'NitroTextExample'
5 | include ':app'
6 | includeBuild('../../node_modules/@react-native/gradle-plugin')
7 |
--------------------------------------------------------------------------------
/nitrogen/generated/ios/c++/HybridNitroTextSpecSwift.cpp:
--------------------------------------------------------------------------------
1 | ///
2 | /// HybridNitroTextSpecSwift.cpp
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | #include "HybridNitroTextSpecSwift.hpp"
9 |
10 | namespace margelo::nitro::nitrotext {
11 | } // namespace margelo::nitro::nitrotext
12 |
--------------------------------------------------------------------------------
/example/react-native.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const pkg = require('../package.json')
3 |
4 | /**
5 | * @type {import('@react-native-community/cli-types').Config}
6 | */
7 | module.exports = {
8 | project: {
9 | ios: {
10 | automaticPodsInstallation: true,
11 | },
12 | },
13 | dependencies: {
14 | [pkg.name]: {
15 | root: path.join(__dirname, '..'),
16 | },
17 | },
18 | }
19 |
--------------------------------------------------------------------------------
/example/metro.config.js:
--------------------------------------------------------------------------------
1 | const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
2 | const path = require('path');
3 | const root = path.resolve(__dirname, '..');
4 |
5 | /**
6 | * Metro configuration
7 | * https://facebook.github.io/metro/docs/configuration
8 | *
9 | * @type {import('metro-config').MetroConfig}
10 | */
11 | const config = {
12 | watchFolders: [root],
13 | };
14 |
15 | module.exports = mergeConfig(getDefaultConfig(__dirname), config);
--------------------------------------------------------------------------------
/nitro.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://nitro.margelo.com/nitro.schema.json",
3 | "cxxNamespace": [
4 | "nitrotext"
5 | ],
6 | "ios": {
7 | "iosModuleName": "NitroText"
8 | },
9 | "android": {
10 | "androidNamespace": [
11 | "nitrotext"
12 | ],
13 | "androidCxxLibName": "NitroText"
14 | },
15 | "autolinking": {
16 | "NitroText": {
17 | "swift": "HybridNitroText"
18 | }
19 | },
20 | "ignorePaths": [
21 | "**/node_modules"
22 | ]
23 | }
--------------------------------------------------------------------------------
/src/constants/css.ts:
--------------------------------------------------------------------------------
1 | import type { Fragment } from '../types'
2 |
3 | export const FRAGMENT_STYLE_KEYS: (keyof Fragment)[] = [
4 | 'selectionColor',
5 | 'fontSize',
6 | 'fontWeight',
7 | 'fontColor',
8 | 'fragmentBackgroundColor',
9 | 'fontStyle',
10 | 'fontFamily',
11 | 'lineHeight',
12 | 'letterSpacing',
13 | 'textAlign',
14 | 'textTransform',
15 | 'textDecorationLine',
16 | 'textDecorationColor',
17 | 'textDecorationStyle',
18 | 'linkUrl',
19 | ]
20 |
--------------------------------------------------------------------------------
/example/android/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
--------------------------------------------------------------------------------
/example/babel.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const pak = require('../package.json');
3 |
4 | module.exports = api => {
5 | api.cache(true);
6 | return {
7 | presets: ['module:@react-native/babel-preset'],
8 | plugins: [
9 | [
10 | 'module-resolver',
11 | {
12 | extensions: ['.js', '.ts', '.json', '.jsx', '.tsx'],
13 | alias: {
14 | [pak.name]: path.join(__dirname, '../', pak.source),
15 | },
16 | },
17 | ],
18 | ],
19 | };
20 | };
--------------------------------------------------------------------------------
/example/ios/.xcode.env:
--------------------------------------------------------------------------------
1 | # This `.xcode.env` file is versioned and is used to source the environment
2 | # used when running script phases inside Xcode.
3 | # To customize your local environment, you can create an `.xcode.env.local`
4 | # file that is not versioned.
5 |
6 | # NODE_BINARY variable contains the PATH to the node executable.
7 | #
8 | # Customize the NODE_BINARY variable here.
9 | # For example, to use nvm with brew, add the following line
10 | # . "$(brew --prefix nvm)/nvm.sh" --no-use
11 | export NODE_BINARY=$(command -v node)
12 |
--------------------------------------------------------------------------------
/example/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | # You may use http://rbenv.org/ or https://rvm.io/ to install and use this version
4 | ruby ">= 2.6.10"
5 |
6 | # Exclude problematic versions of cocoapods and activesupport that causes build failures.
7 | gem 'cocoapods', '>= 1.13', '!= 1.15.0', '!= 1.15.1'
8 | gem 'activesupport', '>= 6.1.7.5', '!= 7.1.0'
9 | gem 'xcodeproj', '< 1.28.0'
10 | gem 'concurrent-ruby', '< 1.3.6'
11 |
12 | # Ruby 3.4.0 has removed some libraries from the standard library.
13 | gem 'bigdecimal'
14 | gem 'logger'
15 | gem 'benchmark'
16 | gem 'mutex_m'
17 |
--------------------------------------------------------------------------------
/example/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext {
3 | buildToolsVersion = "36.0.0"
4 | minSdkVersion = 24
5 | compileSdkVersion = 36
6 | targetSdkVersion = 36
7 | ndkVersion = "27.1.12297006"
8 | kotlinVersion = "2.1.20"
9 | }
10 | repositories {
11 | google()
12 | mavenCentral()
13 | }
14 | dependencies {
15 | classpath("com.android.tools.build:gradle")
16 | classpath("com.facebook.react:react-native-gradle-plugin")
17 | classpath("org.jetbrains.kotlin:kotlin-gradle-plugin")
18 | }
19 | }
20 |
21 | apply plugin: "com.facebook.react.rootproject"
22 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | # Pull Request
2 |
3 | ## Description
4 |
5 | Brief description of what this PR does.
6 |
7 | ## Type of Change
8 |
9 | - [ ] Bug fix
10 | - [ ] New feature
11 | - [ ] Breaking change
12 | - [ ] Documentation update
13 | - [ ] Other
14 |
15 | ## Testing
16 |
17 | - [ ] Tested on iOS
18 | - [ ] Tested on Android
19 | - [ ] Added/updated tests
20 |
21 | ## Checklist
22 |
23 | - [ ] Code follows project style guidelines
24 | - [ ] Self-review completed
25 | - [ ] Documentation updated (if needed)
26 | - [ ] No breaking changes (or clearly documented)
27 |
28 | ## Additional Notes
29 |
30 | Any additional information for reviewers.
31 |
--------------------------------------------------------------------------------
/nitrogen/generated/ios/NitroTextAutolinking.mm:
--------------------------------------------------------------------------------
1 | ///
2 | /// NitroTextAutolinking.mm
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | #import
9 | #import
10 | #import "NitroText-Swift-Cxx-Umbrella.hpp"
11 | #import
12 |
13 | #include "HybridNitroTextSpecSwift.hpp"
14 |
15 | @interface NitroTextAutolinking : NSObject
16 | @end
17 |
18 | @implementation NitroTextAutolinking
19 |
20 | + (void) load {
21 | using namespace margelo::nitro;
22 | using namespace margelo::nitro::nitrotext;
23 |
24 | HybridObjectRegistry::registerHybridObjectConstructor(
25 | "NitroText",
26 | []() -> std::shared_ptr {
27 | std::shared_ptr hybridObject = NitroText::NitroTextAutolinking::createNitroText();
28 | return hybridObject;
29 | }
30 | );
31 | }
32 |
33 | @end
34 |
--------------------------------------------------------------------------------
/NitroText.podspec:
--------------------------------------------------------------------------------
1 | require "json"
2 |
3 | package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4 |
5 | Pod::Spec.new do |s|
6 | s.name = "NitroText"
7 | s.version = package["version"]
8 | s.summary = package["description"]
9 | s.homepage = package["homepage"]
10 | s.license = package["license"]
11 | s.authors = package["author"]
12 |
13 | s.platforms = { :ios => min_ios_version_supported, :visionos => 1.0 }
14 | s.source = { :git => "https://github.com/patrickkabwe/react-native-nitro-text.git", :tag => "#{s.version}" }
15 |
16 | s.source_files = [
17 | # Implementation (Swift)
18 | "ios/**/*.{swift}",
19 | # Autolinking/Registration (Objective-C++)
20 | "ios/**/*.{m,mm,hpp}",
21 | # Implementation (C++ objects)
22 | "cpp/**/*.{hpp,cpp}",
23 | ]
24 |
25 | load 'nitrogen/generated/ios/NitroText+autolinking.rb'
26 | add_nitrogen_files(s)
27 |
28 | s.dependency 'React-jsi'
29 | s.dependency 'React-callinvoker'
30 | install_modules_dependencies(s)
31 | end
32 |
--------------------------------------------------------------------------------
/nitrogen/generated/ios/swift/Renderer.swift:
--------------------------------------------------------------------------------
1 | ///
2 | /// Renderer.swift
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | /**
9 | * Represents the JS union `Renderer`, backed by a C++ enum.
10 | */
11 | public typealias Renderer = margelo.nitro.nitrotext.Renderer
12 |
13 | public extension Renderer {
14 | /**
15 | * Get a Renderer for the given String value, or
16 | * return `nil` if the given value was invalid/unknown.
17 | */
18 | init?(fromString string: String) {
19 | switch string {
20 | case "html":
21 | self = .html
22 | case "plaintext":
23 | self = .plaintext
24 | default:
25 | return nil
26 | }
27 | }
28 |
29 | /**
30 | * Get the String value this Renderer represents.
31 | */
32 | var stringValue: String {
33 | switch self {
34 | case .html:
35 | return "html"
36 | case .plaintext:
37 | return "plaintext"
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/cpp/NitroTextUtil.hpp:
--------------------------------------------------------------------------------
1 | //
2 | // NitroTextUtil.hpp
3 | // Utility macros for React Native version checking
4 | //
5 |
6 | #pragma once
7 |
8 | #if __has_include()
9 | #include
10 | #endif
11 |
12 | /**
13 | * @spec RN_VERSION_AT_LEAST
14 | * @brief Checks if React Native version is >= (major.minor)
15 | * @param major Major version number (e.g., 0, 1, 2)
16 | * @param minor Minor version number (e.g., 81, 82, 83)
17 | * @return Non-zero if version >= (major.minor), 0 otherwise
18 | *
19 | * @example
20 | * ```cpp
21 | * #if RN_VERSION_AT_LEAST(0, 81)
22 | * // Code for RN >= 0.81
23 | * #endif
24 | * ```
25 | */
26 | #if defined(REACT_NATIVE_VERSION_MAJOR)
27 | #define RN_VERSION_AT_LEAST(major, minor) \
28 | ((REACT_NATIVE_VERSION_MAJOR > (major)) || \
29 | (REACT_NATIVE_VERSION_MAJOR == (major) && REACT_NATIVE_VERSION_MINOR >= (minor)))
30 | #else
31 | #define RN_VERSION_AT_LEAST(major, minor) 0
32 | #endif
33 |
34 |
--------------------------------------------------------------------------------
/nitrogen/generated/ios/NitroTextAutolinking.swift:
--------------------------------------------------------------------------------
1 | ///
2 | /// NitroTextAutolinking.swift
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | public final class NitroTextAutolinking {
9 | public typealias bridge = margelo.nitro.nitrotext.bridge.swift
10 |
11 | /**
12 | * Creates an instance of a Swift class that implements `HybridNitroTextSpec`,
13 | * and wraps it in a Swift class that can directly interop with C++ (`HybridNitroTextSpec_cxx`)
14 | *
15 | * This is generated by Nitrogen and will initialize the class specified
16 | * in the `"autolinking"` property of `nitro.json` (in this case, `HybridNitroText`).
17 | */
18 | public static func createNitroText() -> bridge.std__shared_ptr_HybridNitroTextSpec_ {
19 | let hybridObject = HybridNitroText()
20 | return { () -> bridge.std__shared_ptr_HybridNitroTextSpec_ in
21 | let __cxxWrapped = hybridObject.getCxxWrapper()
22 | return __cxxWrapped.getCxxPart()
23 | }()
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "allowUnreachableCode": false,
5 | "allowUnusedLabels": false,
6 | "esModuleInterop": true,
7 | "forceConsistentCasingInFileNames": true,
8 | "jsx": "react",
9 | "lib": ["esnext"],
10 | "module": "esnext",
11 | "moduleResolution": "node",
12 | "noEmit": false,
13 | "noFallthroughCasesInSwitch": true,
14 | "noImplicitReturns": true,
15 | "noImplicitUseStrict": false,
16 | "noStrictGenericChecks": false,
17 | "noUncheckedIndexedAccess": true,
18 | "noUnusedLocals": true,
19 | "noUnusedParameters": true,
20 | "resolveJsonModule": true,
21 | "skipLibCheck": true,
22 | "strict": true,
23 | "target": "esnext",
24 | "verbatimModuleSyntax": true
25 | },
26 | "exclude": [
27 | "**/node_modules",
28 | "**/lib",
29 | "**/.eslintrc.js",
30 | "**/.prettierrc.js",
31 | "**/jest.config.js",
32 | "**/babel.config.js",
33 | "**/metro.config.js",
34 | "**/tsconfig.json"
35 | ],
36 | "include": ["src/**/*", "nitrogen/**/*.json"]
37 | }
38 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # XDE
6 | .expo/
7 |
8 | # VSCode
9 | .vscode/
10 | jsconfig.json
11 |
12 | # Xcode
13 | #
14 | build/
15 | *.pbxuser
16 | !default.pbxuser
17 | *.mode1v3
18 | !default.mode1v3
19 | *.mode2v3
20 | !default.mode2v3
21 | *.perspectivev3
22 | !default.perspectivev3
23 | xcuserdata
24 | *.xccheckout
25 | *.moved-aside
26 | DerivedData
27 | *.hmap
28 | *.ipa
29 | *.xcuserstate
30 | project.xcworkspace
31 |
32 | # Android/IJ
33 | #
34 | .classpath
35 | .cxx
36 | .gradle
37 | .idea
38 | .project
39 | .settings
40 | local.properties
41 | android.iml
42 |
43 | # Cocoapods
44 | #
45 | example/ios/Pods
46 |
47 | # Ruby
48 | example/vendor/
49 |
50 | # node.js
51 | #
52 | node_modules/
53 | npm-debug.log
54 | yarn-debug.log
55 | yarn-error.log
56 |
57 | # BUCK
58 | buck-out/
59 | \.buckd/
60 | android/app/libs
61 | android/keystores/debug.keystore
62 |
63 | # Yarn
64 | .yarn/*
65 | !.yarn/patches
66 | !.yarn/plugins
67 | !.yarn/releases
68 | !.yarn/sdks
69 | !.yarn/versions
70 | .kotlin
71 |
72 | # Expo
73 | .expo/
74 |
75 | # generated by bob
76 | lib/
77 | tsconfig.tsbuildinfo
--------------------------------------------------------------------------------
/src/renderers/index.ts:
--------------------------------------------------------------------------------
1 | import type { Fragment, Renderer } from '../types'
2 | import type { RenderResult } from './types'
3 | import { HTMLRenderer } from './html'
4 | import {
5 | appendText,
6 | createState,
7 | finalizeState,
8 | trimTrailingNewlines,
9 | } from './utils'
10 |
11 | export function renderStringChildren(
12 | input: string,
13 | renderer: Renderer,
14 | baseFragment: Partial = {}
15 | ): RenderResult {
16 | const text = input ?? ''
17 | switch (renderer) {
18 | case 'html':
19 | return HTMLRenderer.render(text, baseFragment)
20 | case 'plaintext':
21 | default:
22 | return renderPlain(text, baseFragment)
23 | }
24 | }
25 |
26 | function renderPlain(text: string, base: Partial): RenderResult {
27 | const state = createState()
28 | appendText(state, text, base)
29 | trimTrailingNewlines(state)
30 | return finalizeState(state)
31 | }
32 |
33 | export { HTMLRenderer } from './html'
34 | export { CSSProcessor } from './css-processor'
35 | export type { RenderResult, Stylesheet, AppliedStyle } from './types'
36 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/question.yml:
--------------------------------------------------------------------------------
1 | name: ❓ Question
2 | description: Ask a question or get help
3 | title: '[Question]: '
4 | labels: ['question']
5 | assignees: []
6 |
7 | body:
8 | - type: textarea
9 | id: question
10 | attributes:
11 | label: What's your question?
12 | description: What would you like to know?
13 | placeholder: Your question here...
14 | validations:
15 | required: true
16 |
17 | - type: textarea
18 | id: tried
19 | attributes:
20 | label: What have you tried?
21 | description: What approaches have you already tried?
22 | placeholder: Describe what you've tried so far...
23 |
24 | - type: textarea
25 | id: code
26 | attributes:
27 | label: Code (if applicable)
28 | description: Relevant code sample
29 | placeholder: |
30 | ```tsx
31 | // Your code here
32 | ```
33 | render: tsx
34 |
35 | - type: checkboxes
36 | id: checklist
37 | attributes:
38 | label: Checklist
39 | options:
40 | - label: I have searched existing issues and discussions
41 | required: true
42 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 Patrick Kabwe
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/example/ios/NitroTextExample/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "scale" : "2x",
6 | "size" : "20x20"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "scale" : "3x",
11 | "size" : "20x20"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "scale" : "2x",
16 | "size" : "29x29"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "scale" : "3x",
21 | "size" : "29x29"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "scale" : "2x",
26 | "size" : "40x40"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "scale" : "3x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "scale" : "2x",
36 | "size" : "60x60"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "scale" : "3x",
41 | "size" : "60x60"
42 | },
43 | {
44 | "idiom" : "ios-marketing",
45 | "scale" : "1x",
46 | "size" : "1024x1024"
47 | }
48 | ],
49 | "info" : {
50 | "author" : "xcode",
51 | "version" : 1
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/example/ios/NitroTextExample/PrivacyInfo.xcprivacy:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | NSPrivacyAccessedAPITypes
6 |
7 |
8 | NSPrivacyAccessedAPIType
9 | NSPrivacyAccessedAPICategoryFileTimestamp
10 | NSPrivacyAccessedAPITypeReasons
11 |
12 | C617.1
13 |
14 |
15 |
16 | NSPrivacyAccessedAPIType
17 | NSPrivacyAccessedAPICategoryUserDefaults
18 | NSPrivacyAccessedAPITypeReasons
19 |
20 | CA92.1
21 |
22 |
23 |
24 | NSPrivacyAccessedAPIType
25 | NSPrivacyAccessedAPICategorySystemBootTime
26 | NSPrivacyAccessedAPITypeReasons
27 |
28 | 35F9.1
29 |
30 |
31 |
32 | NSPrivacyCollectedDataTypes
33 |
34 | NSPrivacyTracking
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/nitrogen/generated/ios/swift/FontStyle.swift:
--------------------------------------------------------------------------------
1 | ///
2 | /// FontStyle.swift
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | /**
9 | * Represents the JS union `FontStyle`, backed by a C++ enum.
10 | */
11 | public typealias FontStyle = margelo.nitro.nitrotext.FontStyle
12 |
13 | public extension FontStyle {
14 | /**
15 | * Get a FontStyle for the given String value, or
16 | * return `nil` if the given value was invalid/unknown.
17 | */
18 | init?(fromString string: String) {
19 | switch string {
20 | case "normal":
21 | self = .normal
22 | case "italic":
23 | self = .italic
24 | case "oblique":
25 | self = .oblique
26 | default:
27 | return nil
28 | }
29 | }
30 |
31 | /**
32 | * Get the String value this FontStyle represents.
33 | */
34 | var stringValue: String {
35 | switch self {
36 | case .normal:
37 | return "normal"
38 | case .italic:
39 | return "italic"
40 | case .oblique:
41 | return "oblique"
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/example/ios/Podfile:
--------------------------------------------------------------------------------
1 | # Resolve react_native_pods.rb with node to allow for hoisting
2 | require Pod::Executable.execute_command('node', ['-p',
3 | 'require.resolve(
4 | "react-native/scripts/react_native_pods.rb",
5 | {paths: [process.argv[1]]},
6 | )', __dir__]).strip
7 |
8 | platform :ios, min_ios_version_supported
9 | prepare_react_native_project!
10 |
11 | linkage = ENV['USE_FRAMEWORKS']
12 | if linkage != nil
13 | Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green
14 | use_frameworks! :linkage => linkage.to_sym
15 | end
16 |
17 | target 'NitroTextExample' do
18 | config = use_native_modules!
19 |
20 | use_react_native!(
21 | :path => config[:reactNativePath],
22 | # An absolute path to your application root.
23 | :app_path => "#{Pod::Config.instance.installation_root}/.."
24 | )
25 |
26 | post_install do |installer|
27 | # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202
28 | react_native_post_install(
29 | installer,
30 | config[:reactNativePath],
31 | :mac_catalyst_enabled => false,
32 | # :ccache_enabled => true
33 | )
34 | end
35 | end
36 |
--------------------------------------------------------------------------------
/nitrogen/generated/shared/json/NitroTextConfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "uiViewClassName": "NitroText",
3 | "supportsRawText": false,
4 | "bubblingEventTypes": {},
5 | "directEventTypes": {},
6 | "validAttributes": {
7 | "fragments": true,
8 | "renderer": true,
9 | "selectable": true,
10 | "allowFontScaling": true,
11 | "ellipsizeMode": true,
12 | "numberOfLines": true,
13 | "lineBreakStrategyIOS": true,
14 | "dynamicTypeRamp": true,
15 | "maxFontSizeMultiplier": true,
16 | "adjustsFontSizeToFit": true,
17 | "minimumFontScale": true,
18 | "menus": true,
19 | "onTextLayout": true,
20 | "onPress": true,
21 | "onPressIn": true,
22 | "onPressOut": true,
23 | "text": true,
24 | "selectionColor": true,
25 | "fontSize": true,
26 | "fontWeight": true,
27 | "fontColor": true,
28 | "fragmentBackgroundColor": true,
29 | "fontStyle": true,
30 | "fontFamily": true,
31 | "lineHeight": true,
32 | "letterSpacing": true,
33 | "textAlign": true,
34 | "textTransform": true,
35 | "textDecorationLine": true,
36 | "textDecorationColor": true,
37 | "textDecorationStyle": true,
38 | "hybridRef": true
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/cpp/NitroTextComponentDescriptor.hpp:
--------------------------------------------------------------------------------
1 | //
2 | // NitroTextComponentDescriptor.hpp
3 | // Custom, non-generated ComponentDescriptor for NitroText
4 | //
5 |
6 | #pragma once
7 |
8 | #include "../cpp/NitroTextShadowNode.hpp"
9 | #include
10 |
11 | namespace margelo::nitro::nitrotext::views {
12 |
13 | /**
14 | * The Component Descriptor for the "NitroText" View.
15 | */
16 | class NitroTextComponentDescriptor final: public react::ConcreteComponentDescriptor {
17 | public:
18 | NitroTextComponentDescriptor(const react::ComponentDescriptorParameters& parameters);
19 |
20 | public:
21 | /**
22 | * A faster path for cloning props - reuses the caching logic from `HybridNitroTextProps`.
23 | */
24 | std::shared_ptr cloneProps(const react::PropsParserContext& context,
25 | const std::shared_ptr& props,
26 | react::RawProps rawProps) const override;
27 |
28 | void adopt(react::ShadowNode& shadowNode) const override;
29 | };
30 |
31 | } // namespace margelo::nitro::nitrotext::views
32 |
--------------------------------------------------------------------------------
/src/renderers/types.ts:
--------------------------------------------------------------------------------
1 | import type { Fragment } from '../types'
2 |
3 | export type RenderResult = {
4 | fragments: Fragment[]
5 | text: string
6 | }
7 |
8 | export type AppendState = {
9 | fragments: Fragment[]
10 | plainText: string
11 | }
12 |
13 | export type ElementNode = {
14 | type: 'element'
15 | tag: string
16 | attrs: Record
17 | children: Node[]
18 | }
19 |
20 | export type TextNode = {
21 | type: 'text'
22 | content: string
23 | }
24 |
25 | export type Node = ElementNode | TextNode
26 |
27 | export type Stylesheet = {
28 | tag: Map>>
29 | className: Map>>
30 | id: Map>>
31 | }
32 |
33 | export type AppliedStyle = {
34 | fragment?: Partial
35 | hidden: boolean
36 | suppressNewlines: boolean
37 | }
38 |
39 | export type WalkContext = {
40 | style: Partial
41 | preformatted: boolean
42 | }
43 |
44 | export type ListStackItem = {
45 | type: 'ul' | 'ol'
46 | index: number
47 | }
48 |
49 | export type WalkState = {
50 | stylesheet: Stylesheet
51 | state: AppendState
52 | listStack: ListStackItem[]
53 | }
54 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | enable-beta-ecosystems: true
3 | updates:
4 | - package-ecosystem: 'github-actions'
5 | directory: '/'
6 | schedule:
7 | interval: 'daily'
8 | labels:
9 | - 'dependencies'
10 |
11 | - package-ecosystem: 'gradle'
12 | directories:
13 | - '/android/'
14 | - '/example/android/'
15 | schedule:
16 | interval: 'daily'
17 | labels:
18 | - 'nitro-core'
19 | - 'nitrogen'
20 | - 'dependencies'
21 | - 'kotlin'
22 |
23 | - package-ecosystem: 'bundler'
24 | directory: '/example/'
25 | schedule:
26 | interval: 'daily'
27 | labels:
28 | - 'dependencies'
29 | - 'ruby'
30 |
31 | - package-ecosystem: 'npm'
32 | directories:
33 | - '/example/'
34 | - '/'
35 | schedule:
36 | interval: 'daily'
37 | labels:
38 | - 'nitro-core'
39 | - 'dependencies'
40 | - 'typescript'
41 | - 'nitrogen'
42 |
43 | groups:
44 | react-native-cli:
45 | patterns:
46 | - '@react-native-community/cli*'
47 | babel:
48 | patterns:
49 | - '@babel/*'
50 | react-native:
51 | patterns:
52 | - '@react-native/*'
53 |
--------------------------------------------------------------------------------
/example/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
15 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.yml:
--------------------------------------------------------------------------------
1 | name: ✨ Feature Request
2 | description: Suggest a new feature or enhancement
3 | title: '[Feature]: '
4 | labels: ['enhancement']
5 | assignees: []
6 |
7 | body:
8 | - type: textarea
9 | id: description
10 | attributes:
11 | label: What would you like?
12 | description: A clear description of the feature
13 | placeholder: Describe the feature...
14 | validations:
15 | required: true
16 |
17 | - type: textarea
18 | id: motivation
19 | attributes:
20 | label: Why is this needed?
21 | description: What problem does this solve?
22 | placeholder: Explain why this would be useful...
23 |
24 | - type: textarea
25 | id: code
26 | attributes:
27 | label: Code example
28 | description: How would you like to use this feature?
29 | placeholder: |
30 | ```tsx
31 | Example
32 | ```
33 | render: tsx
34 |
35 | - type: checkboxes
36 | id: checklist
37 | attributes:
38 | label: Checklist
39 | options:
40 | - label: I have searched existing issues
41 | required: true
42 | - label: I have provided a clear use case
43 | required: true
44 |
--------------------------------------------------------------------------------
/nitrogen/generated/ios/swift/EllipsizeMode.swift:
--------------------------------------------------------------------------------
1 | ///
2 | /// EllipsizeMode.swift
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | /**
9 | * Represents the JS union `EllipsizeMode`, backed by a C++ enum.
10 | */
11 | public typealias EllipsizeMode = margelo.nitro.nitrotext.EllipsizeMode
12 |
13 | public extension EllipsizeMode {
14 | /**
15 | * Get a EllipsizeMode for the given String value, or
16 | * return `nil` if the given value was invalid/unknown.
17 | */
18 | init?(fromString string: String) {
19 | switch string {
20 | case "head":
21 | self = .head
22 | case "middle":
23 | self = .middle
24 | case "tail":
25 | self = .tail
26 | case "clip":
27 | self = .clip
28 | default:
29 | return nil
30 | }
31 | }
32 |
33 | /**
34 | * Get the String value this EllipsizeMode represents.
35 | */
36 | var stringValue: String {
37 | switch self {
38 | case .head:
39 | return "head"
40 | case .middle:
41 | return "middle"
42 | case .tail:
43 | return "tail"
44 | case .clip:
45 | return "clip"
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/example/android/app/src/main/java/com/nitrotextexample/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.nitrotextexample
2 |
3 | import android.os.Bundle
4 | import com.facebook.react.ReactActivity
5 | import com.facebook.react.ReactActivityDelegate
6 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled
7 | import com.facebook.react.defaults.DefaultReactActivityDelegate
8 | import com.swmansion.rnscreens.fragment.restoration.RNScreensFragmentFactory
9 |
10 | class MainActivity : ReactActivity() {
11 |
12 | /**
13 | * Returns the name of the main component registered from JavaScript. This is used to schedule
14 | * rendering of the component.
15 | */
16 | override fun getMainComponentName(): String = "NitroTextExample"
17 |
18 | override fun onCreate(savedInstanceState: Bundle?) {
19 | supportFragmentManager.fragmentFactory = RNScreensFragmentFactory()
20 | super.onCreate(savedInstanceState)
21 | }
22 |
23 | /**
24 | * Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate]
25 | * which allows you to enable New Architecture with a single boolean flags [fabricEnabled]
26 | */
27 | override fun createReactActivityDelegate(): ReactActivityDelegate =
28 | DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled)
29 | }
30 |
--------------------------------------------------------------------------------
/nitrogen/generated/ios/swift/TextTransform.swift:
--------------------------------------------------------------------------------
1 | ///
2 | /// TextTransform.swift
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | /**
9 | * Represents the JS union `TextTransform`, backed by a C++ enum.
10 | */
11 | public typealias TextTransform = margelo.nitro.nitrotext.TextTransform
12 |
13 | public extension TextTransform {
14 | /**
15 | * Get a TextTransform for the given String value, or
16 | * return `nil` if the given value was invalid/unknown.
17 | */
18 | init?(fromString string: String) {
19 | switch string {
20 | case "none":
21 | self = .none
22 | case "uppercase":
23 | self = .uppercase
24 | case "lowercase":
25 | self = .lowercase
26 | case "capitalize":
27 | self = .capitalize
28 | default:
29 | return nil
30 | }
31 | }
32 |
33 | /**
34 | * Get the String value this TextTransform represents.
35 | */
36 | var stringValue: String {
37 | switch self {
38 | case .none:
39 | return "none"
40 | case .uppercase:
41 | return "uppercase"
42 | case .lowercase:
43 | return "lowercase"
44 | case .capitalize:
45 | return "capitalize"
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/nitrogen/generated/ios/swift/TextDecorationStyle.swift:
--------------------------------------------------------------------------------
1 | ///
2 | /// TextDecorationStyle.swift
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | /**
9 | * Represents the JS union `TextDecorationStyle`, backed by a C++ enum.
10 | */
11 | public typealias TextDecorationStyle = margelo.nitro.nitrotext.TextDecorationStyle
12 |
13 | public extension TextDecorationStyle {
14 | /**
15 | * Get a TextDecorationStyle for the given String value, or
16 | * return `nil` if the given value was invalid/unknown.
17 | */
18 | init?(fromString string: String) {
19 | switch string {
20 | case "solid":
21 | self = .solid
22 | case "double":
23 | self = .double
24 | case "dotted":
25 | self = .dotted
26 | case "dashed":
27 | self = .dashed
28 | default:
29 | return nil
30 | }
31 | }
32 |
33 | /**
34 | * Get the String value this TextDecorationStyle represents.
35 | */
36 | var stringValue: String {
37 | switch self {
38 | case .solid:
39 | return "solid"
40 | case .double:
41 | return "double"
42 | case .dotted:
43 | return "dotted"
44 | case .dashed:
45 | return "dashed"
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/nitrogen/generated/ios/swift/TextAlign.swift:
--------------------------------------------------------------------------------
1 | ///
2 | /// TextAlign.swift
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | /**
9 | * Represents the JS union `TextAlign`, backed by a C++ enum.
10 | */
11 | public typealias TextAlign = margelo.nitro.nitrotext.TextAlign
12 |
13 | public extension TextAlign {
14 | /**
15 | * Get a TextAlign for the given String value, or
16 | * return `nil` if the given value was invalid/unknown.
17 | */
18 | init?(fromString string: String) {
19 | switch string {
20 | case "auto":
21 | self = .auto
22 | case "left":
23 | self = .left
24 | case "right":
25 | self = .right
26 | case "center":
27 | self = .center
28 | case "justify":
29 | self = .justify
30 | default:
31 | return nil
32 | }
33 | }
34 |
35 | /**
36 | * Get the String value this TextAlign represents.
37 | */
38 | var stringValue: String {
39 | switch self {
40 | case .auto:
41 | return "auto"
42 | case .left:
43 | return "left"
44 | case .right:
45 | return "right"
46 | case .center:
47 | return "center"
48 | case .justify:
49 | return "justify"
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/nitrogen/generated/ios/swift/LineBreakStrategyIOS.swift:
--------------------------------------------------------------------------------
1 | ///
2 | /// LineBreakStrategyIOS.swift
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | /**
9 | * Represents the JS union `LineBreakStrategyIOS`, backed by a C++ enum.
10 | */
11 | public typealias LineBreakStrategyIOS = margelo.nitro.nitrotext.LineBreakStrategyIOS
12 |
13 | public extension LineBreakStrategyIOS {
14 | /**
15 | * Get a LineBreakStrategyIOS for the given String value, or
16 | * return `nil` if the given value was invalid/unknown.
17 | */
18 | init?(fromString string: String) {
19 | switch string {
20 | case "none":
21 | self = .none
22 | case "standard":
23 | self = .standard
24 | case "hangul-word":
25 | self = .hangulWord
26 | case "push-out":
27 | self = .pushOut
28 | default:
29 | return nil
30 | }
31 | }
32 |
33 | /**
34 | * Get the String value this LineBreakStrategyIOS represents.
35 | */
36 | var stringValue: String {
37 | switch self {
38 | case .none:
39 | return "none"
40 | case .standard:
41 | return "standard"
42 | case .hangulWord:
43 | return "hangul-word"
44 | case .pushOut:
45 | return "push-out"
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/example/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # Xcode
6 | #
7 | build/
8 | *.pbxuser
9 | !default.pbxuser
10 | *.mode1v3
11 | !default.mode1v3
12 | *.mode2v3
13 | !default.mode2v3
14 | *.perspectivev3
15 | !default.perspectivev3
16 | xcuserdata
17 | *.xccheckout
18 | *.moved-aside
19 | DerivedData
20 | *.hmap
21 | *.ipa
22 | *.xcuserstate
23 | **/.xcode.env.local
24 |
25 | # Android/IntelliJ
26 | #
27 | build/
28 | .idea
29 | .gradle
30 | local.properties
31 | *.iml
32 | *.hprof
33 | .cxx/
34 | *.keystore
35 | !debug.keystore
36 | .kotlin/
37 |
38 | # node.js
39 | #
40 | node_modules/
41 | npm-debug.log
42 | yarn-error.log
43 |
44 | # fastlane
45 | #
46 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
47 | # screenshots whenever they are needed.
48 | # For more information about the recommended setup visit:
49 | # https://docs.fastlane.tools/best-practices/source-control/
50 |
51 | **/fastlane/report.xml
52 | **/fastlane/Preview.html
53 | **/fastlane/screenshots
54 | **/fastlane/test_output
55 |
56 | # Bundle artifact
57 | *.jsbundle
58 |
59 | # Ruby / CocoaPods
60 | **/Pods/
61 | /vendor/bundle/
62 |
63 | # Temporary files created by Metro to check the health of the file watcher
64 | .metro-health-check*
65 |
66 | # testing
67 | /coverage
68 |
69 | # Yarn
70 | .yarn/*
71 | !.yarn/patches
72 | !.yarn/plugins
73 | !.yarn/releases
74 | !.yarn/sdks
75 | !.yarn/versions
76 |
--------------------------------------------------------------------------------
/example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-nitro-text-example",
3 | "version": "0.0.1",
4 | "private": true,
5 | "scripts": {
6 | "android": "react-native run-android",
7 | "ios": "react-native run-ios --simulator='iPhone 16'",
8 | "lint": "eslint .",
9 | "start": "react-native start --reset-cache",
10 | "test": "jest",
11 | "pod": "bundle install && bundle exec pod install --project-directory=ios"
12 | },
13 | "dependencies": {
14 | "@react-navigation/bottom-tabs": "^7.8.5",
15 | "@react-navigation/native": "^7.1.20",
16 | "react": "19.1.0",
17 | "react-native": "0.81.4",
18 | "react-native-nitro-modules": "^0.31.9",
19 | "react-native-safe-area-context": "^5.5.2",
20 | "react-native-screens": "^4.18.0"
21 | },
22 | "devDependencies": {
23 | "@babel/core": "^7.25.2",
24 | "@babel/preset-env": "^7.25.3",
25 | "@babel/runtime": "^7.25.0",
26 | "@react-native-community/cli": "20.0.0",
27 | "@react-native-community/cli-platform-android": "20.0.0",
28 | "@react-native-community/cli-platform-ios": "20.0.0",
29 | "@react-native/babel-preset": "0.81.4",
30 | "@react-native/eslint-config": "0.81.4",
31 | "@react-native/metro-config": "0.81.4",
32 | "@react-native/typescript-config": "0.81.4",
33 | "@types/jest": "^29.5.13",
34 | "babel-plugin-module-resolver": "^5.0.2"
35 | },
36 | "engines": {
37 | "node": ">=20"
38 | }
39 | }
--------------------------------------------------------------------------------
/nitrogen/generated/ios/swift/TextDecorationLine.swift:
--------------------------------------------------------------------------------
1 | ///
2 | /// TextDecorationLine.swift
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | /**
9 | * Represents the JS union `TextDecorationLine`, backed by a C++ enum.
10 | */
11 | public typealias TextDecorationLine = margelo.nitro.nitrotext.TextDecorationLine
12 |
13 | public extension TextDecorationLine {
14 | /**
15 | * Get a TextDecorationLine for the given String value, or
16 | * return `nil` if the given value was invalid/unknown.
17 | */
18 | init?(fromString string: String) {
19 | switch string {
20 | case "none":
21 | self = .none
22 | case "underline":
23 | self = .underline
24 | case "line-through":
25 | self = .lineThrough
26 | case "underline line-through":
27 | self = .underlineLineThrough
28 | default:
29 | return nil
30 | }
31 | }
32 |
33 | /**
34 | * Get the String value this TextDecorationLine represents.
35 | */
36 | var stringValue: String {
37 | switch self {
38 | case .none:
39 | return "none"
40 | case .underline:
41 | return "underline"
42 | case .lineThrough:
43 | return "line-through"
44 | case .underlineLineThrough:
45 | return "underline line-through"
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/nitrogen/generated/ios/swift/TextLayoutEvent.swift:
--------------------------------------------------------------------------------
1 | ///
2 | /// TextLayoutEvent.swift
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | import Foundation
9 | import NitroModules
10 |
11 | /**
12 | * Represents an instance of `TextLayoutEvent`, backed by a C++ struct.
13 | */
14 | public typealias TextLayoutEvent = margelo.nitro.nitrotext.TextLayoutEvent
15 |
16 | public extension TextLayoutEvent {
17 | private typealias bridge = margelo.nitro.nitrotext.bridge.swift
18 |
19 | /**
20 | * Create a new instance of `TextLayoutEvent`.
21 | */
22 | init(lines: [TextLayout]) {
23 | self.init({ () -> bridge.std__vector_TextLayout_ in
24 | var __vector = bridge.create_std__vector_TextLayout_(lines.count)
25 | for __item in lines {
26 | __vector.push_back(__item)
27 | }
28 | return __vector
29 | }())
30 | }
31 |
32 | var lines: [TextLayout] {
33 | @inline(__always)
34 | get {
35 | return self.__lines.map({ __item in __item })
36 | }
37 | @inline(__always)
38 | set {
39 | self.__lines = { () -> bridge.std__vector_TextLayout_ in
40 | var __vector = bridge.create_std__vector_TextLayout_(newValue.count)
41 | for __item in newValue {
42 | __vector.push_back(__item)
43 | }
44 | return __vector
45 | }()
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/example/ios/NitroTextExample/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import React
3 | import React_RCTAppDelegate
4 | import ReactAppDependencyProvider
5 |
6 | @main
7 | class AppDelegate: UIResponder, UIApplicationDelegate {
8 | var window: UIWindow?
9 |
10 | var reactNativeDelegate: ReactNativeDelegate?
11 | var reactNativeFactory: RCTReactNativeFactory?
12 |
13 | func application(
14 | _ application: UIApplication,
15 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
16 | ) -> Bool {
17 | let delegate = ReactNativeDelegate()
18 | let factory = RCTReactNativeFactory(delegate: delegate)
19 | delegate.dependencyProvider = RCTAppDependencyProvider()
20 |
21 | reactNativeDelegate = delegate
22 | reactNativeFactory = factory
23 |
24 | window = UIWindow(frame: UIScreen.main.bounds)
25 |
26 | factory.startReactNative(
27 | withModuleName: "NitroTextExample",
28 | in: window,
29 | launchOptions: launchOptions
30 | )
31 |
32 | return true
33 | }
34 | }
35 |
36 | class ReactNativeDelegate: RCTDefaultReactNativeFactoryDelegate {
37 | override func sourceURL(for bridge: RCTBridge) -> URL? {
38 | self.bundleURL()
39 | }
40 |
41 | override func bundleURL() -> URL? {
42 | #if DEBUG
43 | RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index")
44 | #else
45 | Bundle.main.url(forResource: "main", withExtension: "jsbundle")
46 | #endif
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/nitrogen/generated/ios/swift/Func_void.swift:
--------------------------------------------------------------------------------
1 | ///
2 | /// Func_void.swift
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | import Foundation
9 | import NitroModules
10 |
11 | /**
12 | * Wraps a Swift `() -> Void` as a class.
13 | * This class can be used from C++, e.g. to wrap the Swift closure as a `std::function`.
14 | */
15 | public final class Func_void {
16 | public typealias bridge = margelo.nitro.nitrotext.bridge.swift
17 |
18 | private let closure: () -> Void
19 |
20 | public init(_ closure: @escaping () -> Void) {
21 | self.closure = closure
22 | }
23 |
24 | @inline(__always)
25 | public func call() -> Void {
26 | self.closure()
27 | }
28 |
29 | /**
30 | * Casts this instance to a retained unsafe raw pointer.
31 | * This acquires one additional strong reference on the object!
32 | */
33 | @inline(__always)
34 | public func toUnsafe() -> UnsafeMutableRawPointer {
35 | return Unmanaged.passRetained(self).toOpaque()
36 | }
37 |
38 | /**
39 | * Casts an unsafe pointer to a `Func_void`.
40 | * The pointer has to be a retained opaque `Unmanaged`.
41 | * This removes one strong reference from the object!
42 | */
43 | @inline(__always)
44 | public static func fromUnsafe(_ pointer: UnsafeMutableRawPointer) -> Func_void {
45 | return Unmanaged.fromOpaque(pointer).takeRetainedValue()
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.yml:
--------------------------------------------------------------------------------
1 | name: 🐛 Bug Report
2 | description: Report a bug or unexpected behavior
3 | title: '[Bug]: '
4 | labels: ['bug']
5 | assignees: []
6 |
7 | body:
8 | - type: textarea
9 | id: description
10 | attributes:
11 | label: What happened?
12 | description: A clear description of the bug
13 | placeholder: Describe the bug...
14 | validations:
15 | required: true
16 |
17 | - type: textarea
18 | id: steps
19 | attributes:
20 | label: Steps to reproduce
21 | description: How can we reproduce this?
22 | placeholder: |
23 | 1. Go to '...'
24 | 2. Click '....'
25 | 3. See error
26 | validations:
27 | required: true
28 |
29 | - type: textarea
30 | id: code
31 | attributes:
32 | label: Code sample
33 | description: Minimal code that reproduces the issue
34 | placeholder: |
35 | ```tsx
36 | Your code here
37 | ```
38 | render: tsx
39 |
40 | - type: input
41 | id: environment
42 | attributes:
43 | label: Environment
44 | description: React Native version, platform, device
45 | placeholder: e.g., RN 0.81.4, iOS, iPhone 15 Pro
46 |
47 | - type: checkboxes
48 | id: checklist
49 | attributes:
50 | label: Checklist
51 | options:
52 | - label: I have searched existing issues
53 | required: true
54 | - label: I have provided a minimal reproduction case
55 | required: true
56 |
--------------------------------------------------------------------------------
/example/android/app/src/main/java/com/nitrotextexample/MainApplication.kt:
--------------------------------------------------------------------------------
1 | package com.nitrotextexample
2 |
3 | import android.app.Application
4 | import com.facebook.react.PackageList
5 | import com.facebook.react.ReactApplication
6 | import com.facebook.react.ReactHost
7 | import com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative
8 | import com.facebook.react.ReactNativeHost
9 | import com.facebook.react.ReactPackage
10 | import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
11 | import com.facebook.react.defaults.DefaultReactNativeHost
12 |
13 | class MainApplication : Application(), ReactApplication {
14 |
15 | override val reactNativeHost: ReactNativeHost =
16 | object : DefaultReactNativeHost(this) {
17 | override fun getPackages(): List =
18 | PackageList(this).packages.apply {
19 | // Packages that cannot be autolinked yet can be added manually here, for example:
20 | // add(MyReactNativePackage())
21 | }
22 |
23 | override fun getJSMainModuleName(): String = "index"
24 |
25 | override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG
26 |
27 | override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
28 | override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED
29 | }
30 |
31 | override val reactHost: ReactHost
32 | get() = getDefaultReactHost(applicationContext, reactNativeHost)
33 |
34 | override fun onCreate() {
35 | super.onCreate()
36 | loadReactNative(this)
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | workflow_dispatch:
5 |
6 | permissions:
7 | contents: read
8 |
9 | concurrency:
10 | group: ${{ github.workflow }}-${{ github.ref }}
11 | cancel-in-progress: true
12 |
13 | jobs:
14 | release:
15 | name: Release
16 | runs-on: ubuntu-latest
17 | permissions:
18 | contents: write
19 | issues: write
20 | pull-requests: write
21 | id-token: write
22 | steps:
23 | - name: Checkout
24 | uses: actions/checkout@v6
25 | with:
26 | fetch-depth: 0
27 | - name: Setup Bun.js
28 | uses: oven-sh/setup-bun@v2
29 | with:
30 | bun-version: latest
31 | - name: Cache bun dependencies
32 | id: bun-cache
33 | uses: actions/cache@v4
34 | with:
35 | path: ~/.bun/install/cache
36 | key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }}
37 | restore-keys: |
38 | ${{ runner.os }}-bun-
39 |
40 | - name: Install npm dependencies (bun)
41 | run: bun install
42 |
43 | - name: Build lib
44 | run: bun run build
45 |
46 | - name: Release
47 | env:
48 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
49 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
50 | NPM_CONFIG_PROVENANCE: true
51 | GIT_AUTHOR_NAME: ${{ github.actor }}
52 | GIT_AUTHOR_EMAIL: "${{ github.actor }}@users.noreply.github.com"
53 | GIT_COMMITTER_NAME: ${{ github.actor }}
54 | GIT_COMMITTER_EMAIL: "${{ github.actor }}@users.noreply.github.com"
55 | run: bun release
56 |
--------------------------------------------------------------------------------
/nitrogen/generated/ios/swift/MenuItem.swift:
--------------------------------------------------------------------------------
1 | ///
2 | /// MenuItem.swift
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | import Foundation
9 | import NitroModules
10 |
11 | /**
12 | * Represents an instance of `MenuItem`, backed by a C++ struct.
13 | */
14 | public typealias MenuItem = margelo.nitro.nitrotext.MenuItem
15 |
16 | public extension MenuItem {
17 | private typealias bridge = margelo.nitro.nitrotext.bridge.swift
18 |
19 | /**
20 | * Create a new instance of `MenuItem`.
21 | */
22 | init(title: String, action: @escaping () -> Void) {
23 | self.init(std.string(title), { () -> bridge.Func_void in
24 | let __closureWrapper = Func_void(action)
25 | return bridge.create_Func_void(__closureWrapper.toUnsafe())
26 | }())
27 | }
28 |
29 | var title: String {
30 | @inline(__always)
31 | get {
32 | return String(self.__title)
33 | }
34 | @inline(__always)
35 | set {
36 | self.__title = std.string(newValue)
37 | }
38 | }
39 |
40 | var action: () -> Void {
41 | @inline(__always)
42 | get {
43 | return { () -> () -> Void in
44 | let __wrappedFunction = bridge.wrap_Func_void(self.__action)
45 | return { () -> Void in
46 | __wrappedFunction.call()
47 | }
48 | }()
49 | }
50 | @inline(__always)
51 | set {
52 | self.__action = { () -> bridge.Func_void in
53 | let __closureWrapper = Func_void(newValue)
54 | return bridge.create_Func_void(__closureWrapper.toUnsafe())
55 | }()
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/nitrogen/generated/ios/swift/Func_void_TextLayoutEvent.swift:
--------------------------------------------------------------------------------
1 | ///
2 | /// Func_void_TextLayoutEvent.swift
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | import Foundation
9 | import NitroModules
10 |
11 | /**
12 | * Wraps a Swift `(_ layout: TextLayoutEvent) -> Void` as a class.
13 | * This class can be used from C++, e.g. to wrap the Swift closure as a `std::function`.
14 | */
15 | public final class Func_void_TextLayoutEvent {
16 | public typealias bridge = margelo.nitro.nitrotext.bridge.swift
17 |
18 | private let closure: (_ layout: TextLayoutEvent) -> Void
19 |
20 | public init(_ closure: @escaping (_ layout: TextLayoutEvent) -> Void) {
21 | self.closure = closure
22 | }
23 |
24 | @inline(__always)
25 | public func call(layout: TextLayoutEvent) -> Void {
26 | self.closure(layout)
27 | }
28 |
29 | /**
30 | * Casts this instance to a retained unsafe raw pointer.
31 | * This acquires one additional strong reference on the object!
32 | */
33 | @inline(__always)
34 | public func toUnsafe() -> UnsafeMutableRawPointer {
35 | return Unmanaged.passRetained(self).toOpaque()
36 | }
37 |
38 | /**
39 | * Casts an unsafe pointer to a `Func_void_TextLayoutEvent`.
40 | * The pointer has to be a retained opaque `Unmanaged`.
41 | * This removes one strong reference from the object!
42 | */
43 | @inline(__always)
44 | public static func fromUnsafe(_ pointer: UnsafeMutableRawPointer) -> Func_void_TextLayoutEvent {
45 | return Unmanaged.fromOpaque(pointer).takeRetainedValue()
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/example/ios/NitroTextExample/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | NitroTextExample
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | $(MARKETING_VERSION)
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | $(CURRENT_PROJECT_VERSION)
25 | LSRequiresIPhoneOS
26 |
27 | NSAppTransportSecurity
28 |
29 | NSAllowsArbitraryLoads
30 |
31 | NSAllowsLocalNetworking
32 |
33 |
34 | NSLocationWhenInUseUsageDescription
35 |
36 | RCTNewArchEnabled
37 |
38 | UILaunchStoryboardName
39 | LaunchScreen
40 | UIRequiredDeviceCapabilities
41 |
42 | arm64
43 |
44 | UISupportedInterfaceOrientations
45 |
46 | UIInterfaceOrientationPortrait
47 | UIInterfaceOrientationLandscapeLeft
48 | UIInterfaceOrientationLandscapeRight
49 |
50 | UIViewControllerBasedStatusBarAppearance
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/example/src/App.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Text } from 'react-native';
3 | import { NavigationContainer } from '@react-navigation/native';
4 | import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
5 | import { PlainTextScreen } from './screens/PlainTextScreen';
6 | import { HtmlScreen } from './screens/HtmlScreen';
7 | import { PerformanceScreen } from './screens/PerformanceScreen';
8 |
9 | const Tab = createBottomTabNavigator();
10 |
11 | function MyTabs() {
12 | return (
13 |
20 | (
26 | 📄
27 | ),
28 | }}
29 | />
30 | (
36 | 🌐
37 | ),
38 | }}
39 | />
40 | (
46 | ⚡
47 | ),
48 | }}
49 | />
50 |
51 | );
52 | }
53 |
54 | export default function App() {
55 | return (
56 |
57 |
58 |
59 | );
60 | }
61 |
--------------------------------------------------------------------------------
/cpp/NitroTextShadowNode.hpp:
--------------------------------------------------------------------------------
1 | //
2 | // NitroTextShadowNode.hpp
3 | // Custom, non-generated ShadowNode for NitroText
4 | //
5 |
6 | #pragma once
7 |
8 | #include "HybridNitroTextComponent.hpp"
9 |
10 | #include
11 | #include
12 |
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 |
19 | namespace margelo::nitro::nitrotext::views {
20 |
21 | /**
22 | * The Shadow Node for the "NitroText" View.
23 | * Mark as a Leaf + Measurable Yoga node so Fabric queries the ShadowNode for
24 | * size. (We measure cross-platform in C++ using TextLayoutManager, like
25 | * Paragraph.)
26 | */
27 | class NitroTextShadowNode final
28 | : public react::ConcreteViewShadowNode<
29 | HybridNitroTextComponentName,
30 | HybridNitroTextProps,
31 | react::ViewEventEmitter,
32 | HybridNitroTextState> {
33 | public:
34 | using ConcreteViewShadowNode::ConcreteViewShadowNode;
35 |
36 | static react::ShadowNodeTraits BaseTraits();
37 |
38 | void setTextLayoutManager(
39 | std::shared_ptr tlm);
40 |
41 | protected:
42 | react::Size measureContent(
43 | const react::LayoutContext &layoutContext,
44 | const react::LayoutConstraints &layoutConstraints) const override;
45 |
46 | react::Float baseline(const react::LayoutContext &layoutContext,
47 | react::Size size) const override;
48 |
49 | private:
50 | std::shared_ptr textLayoutManager_;
51 | };
52 |
53 | } // namespace margelo::nitro::nitrotext::views
54 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable/rn_edit_text_material.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
22 |
23 |
24 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/nitrogen/generated/ios/swift/DynamicTypeRamp.swift:
--------------------------------------------------------------------------------
1 | ///
2 | /// DynamicTypeRamp.swift
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | /**
9 | * Represents the JS union `DynamicTypeRamp`, backed by a C++ enum.
10 | */
11 | public typealias DynamicTypeRamp = margelo.nitro.nitrotext.DynamicTypeRamp
12 |
13 | public extension DynamicTypeRamp {
14 | /**
15 | * Get a DynamicTypeRamp for the given String value, or
16 | * return `nil` if the given value was invalid/unknown.
17 | */
18 | init?(fromString string: String) {
19 | switch string {
20 | case "caption2":
21 | self = .caption2
22 | case "caption1":
23 | self = .caption1
24 | case "footnote":
25 | self = .footnote
26 | case "subheadline":
27 | self = .subheadline
28 | case "callout":
29 | self = .callout
30 | case "body":
31 | self = .body
32 | case "headline":
33 | self = .headline
34 | case "title3":
35 | self = .title3
36 | case "title2":
37 | self = .title2
38 | case "title1":
39 | self = .title1
40 | case "largeTitle":
41 | self = .largetitle
42 | default:
43 | return nil
44 | }
45 | }
46 |
47 | /**
48 | * Get the String value this DynamicTypeRamp represents.
49 | */
50 | var stringValue: String {
51 | switch self {
52 | case .caption2:
53 | return "caption2"
54 | case .caption1:
55 | return "caption1"
56 | case .footnote:
57 | return "footnote"
58 | case .subheadline:
59 | return "subheadline"
60 | case .callout:
61 | return "callout"
62 | case .body:
63 | return "body"
64 | case .headline:
65 | return "headline"
66 | case .title3:
67 | return "title3"
68 | case .title2:
69 | return "title2"
70 | case .title1:
71 | return "title1"
72 | case .largetitle:
73 | return "largeTitle"
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/example/android/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx512m -XX:MaxMetaspaceSize=256m
13 | org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
19 |
20 | # AndroidX package structure to make it clearer which packages are bundled with the
21 | # Android operating system, and which are packaged with your app's APK
22 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
23 | android.useAndroidX=true
24 |
25 | # Use this property to specify which architecture you want to build.
26 | # You can also override it from the CLI using
27 | # ./gradlew -PreactNativeArchitectures=x86_64
28 | reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
29 |
30 | # Use this property to enable support to the new architecture.
31 | # This will allow you to use TurboModules and the Fabric render in
32 | # your application. You should enable this flag either if you want
33 | # to write custom TurboModules/Fabric components OR use libraries that
34 | # are providing them.
35 | newArchEnabled=true
36 |
37 | # Use this property to enable or disable the Hermes JS engine.
38 | # If set to false, you will be using JSC instead.
39 | hermesEnabled=true
40 |
41 | # Use this property to enable edge-to-edge display support.
42 | # This allows your app to draw behind system bars for an immersive UI.
43 | # Note: Only works with ReactActivity and should not be used with custom Activity.
44 | edgeToEdgeEnabled=false
45 |
--------------------------------------------------------------------------------
/nitrogen/generated/ios/swift/FontWeight.swift:
--------------------------------------------------------------------------------
1 | ///
2 | /// FontWeight.swift
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | /**
9 | * Represents the JS union `FontWeight`, backed by a C++ enum.
10 | */
11 | public typealias FontWeight = margelo.nitro.nitrotext.FontWeight
12 |
13 | public extension FontWeight {
14 | /**
15 | * Get a FontWeight for the given String value, or
16 | * return `nil` if the given value was invalid/unknown.
17 | */
18 | init?(fromString string: String) {
19 | switch string {
20 | case "normal":
21 | self = .normal
22 | case "bold":
23 | self = .bold
24 | case "ultralight":
25 | self = .ultralight
26 | case "thin":
27 | self = .thin
28 | case "light":
29 | self = .light
30 | case "medium":
31 | self = .medium
32 | case "regular":
33 | self = .regular
34 | case "semibold":
35 | self = .semibold
36 | case "condensedBold":
37 | self = .condensedbold
38 | case "condensed":
39 | self = .condensed
40 | case "heavy":
41 | self = .heavy
42 | case "black":
43 | self = .black
44 | default:
45 | return nil
46 | }
47 | }
48 |
49 | /**
50 | * Get the String value this FontWeight represents.
51 | */
52 | var stringValue: String {
53 | switch self {
54 | case .normal:
55 | return "normal"
56 | case .bold:
57 | return "bold"
58 | case .ultralight:
59 | return "ultralight"
60 | case .thin:
61 | return "thin"
62 | case .light:
63 | return "light"
64 | case .medium:
65 | return "medium"
66 | case .regular:
67 | return "regular"
68 | case .semibold:
69 | return "semibold"
70 | case .condensedbold:
71 | return "condensedBold"
72 | case .condensed:
73 | return "condensed"
74 | case .heavy:
75 | return "heavy"
76 | case .black:
77 | return "black"
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/ios/HybridNitroTextComponentOverride.mm:
--------------------------------------------------------------------------------
1 | //
2 | // NitroTextComponentOverride.mm
3 | // Override ComponentDescriptor and ComponentView behavior for the generated view class
4 | // without introducing a new ComponentView class.
5 | //
6 |
7 | #import
8 | #import
9 | #import
10 | #import
11 |
12 | #import "NitroTextComponentDescriptor.hpp"
13 |
14 | // Forward-declare the generated view class; we don't import generated headers here.
15 | @interface HybridNitroTextComponent : RCTViewComponentView
16 | @end
17 |
18 | using namespace facebook;
19 | using namespace margelo::nitro::nitrotext::views;
20 |
21 | @interface HybridNitroTextComponent (ComponentDescriptorOverride)
22 | @end
23 |
24 | @implementation HybridNitroTextComponent (ComponentDescriptorOverride)
25 |
26 | + (void)load
27 | {
28 | NSLog(@"[NitroText] ComponentDescriptorOverride Step 1: +load - Re-registering HybridNitroTextComponent");
29 |
30 | // Re-register THIS class so the factory picks up our overridden methods below.
31 | // This MUST happen first to ensure our overrides are respected.
32 | // Without this, the generated class's methods would be used instead.
33 | [[RCTComponentViewFactory currentComponentViewFactory] registerComponentViewClass:self];
34 | }
35 |
36 | + (react::ComponentDescriptorProvider)componentDescriptorProvider
37 | {
38 | NSLog(@"[NitroText] ComponentDescriptorOverride Step 2: Providing custom ComponentDescriptorProvider");
39 |
40 | // Return our custom descriptor (which uses our custom ShadowNode).
41 | // This is critical for proper layout and measurement.
42 | return react::concreteComponentDescriptorProvider();
43 | }
44 |
45 | + (BOOL)shouldBeRecycled
46 | {
47 | NSLog(@"[NitroText] ComponentOverride Step 3: Disabling component recycling");
48 |
49 | // Disable component recycling for NitroText to ensure proper cleanup and re-initialization.
50 | // This prevents recycling which was causing issues with attributed text state.
51 | return NO;
52 | }
53 |
54 |
55 | @end
56 |
57 |
--------------------------------------------------------------------------------
/release.config.cjs:
--------------------------------------------------------------------------------
1 | const rules = [
2 | { type: 'feat', release: 'minor', title: '✨ Features' },
3 | { type: 'fix', release: 'patch', title: '🐛 Bug Fixes' },
4 | { type: 'perf', release: 'patch', title: '💨 Performance Improvements' },
5 | { type: 'refactor', release: 'patch', title: '🔄 Code Refactors' },
6 | { type: 'docs', release: 'patch', title: '📚 Documentation' },
7 | { type: 'chore', release: 'patch', title: '🛠️ Other changes' },
8 | ]
9 |
10 | const sortMap = Object.fromEntries(
11 | rules.map((rule, index) => [rule.title, index])
12 | )
13 |
14 | /**
15 | * @type {import('semantic-release').GlobalConfig}
16 | */
17 | module.exports = {
18 | branches: ['main', { name: 'next', prerelease: 'next' }],
19 | plugins: [
20 | [
21 | '@semantic-release/commit-analyzer',
22 | {
23 | preset: 'conventionalcommits',
24 | releaseRules: [
25 | { breaking: true, release: 'major' },
26 | { revert: true, release: 'patch' },
27 | ].concat(rules.map(({ type, release }) => ({ type, release }))),
28 | },
29 | ],
30 | [
31 | '@semantic-release/release-notes-generator',
32 | {
33 | preset: 'conventionalcommits',
34 | presetConfig: {
35 | types: rules.map(({ type, title }) => ({
36 | type,
37 | section: title,
38 | })),
39 | },
40 | writerOpts: {
41 | commitGroupsSort: (a, z) =>
42 | sortMap[a.title] - sortMap[z.title],
43 | },
44 | },
45 | ],
46 | [
47 | '@semantic-release/changelog',
48 | {
49 | changelogFile: 'CHANGELOG.md',
50 | },
51 | ],
52 | '@semantic-release/npm',
53 | '@semantic-release/github',
54 | [
55 | '@semantic-release/git',
56 | {
57 | assets: ['package.json', 'CHANGELOG.md', 'example/package.json'],
58 | },
59 | ],
60 | ],
61 | }
62 |
--------------------------------------------------------------------------------
/nitrogen/generated/ios/NitroText+autolinking.rb:
--------------------------------------------------------------------------------
1 | #
2 | # NitroText+autolinking.rb
3 | # This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | # https://github.com/mrousavy/nitro
5 | # Copyright © 2025 Marc Rousavy @ Margelo
6 | #
7 |
8 | # This is a Ruby script that adds all files generated by Nitrogen
9 | # to the given podspec.
10 | #
11 | # To use it, add this to your .podspec:
12 | # ```ruby
13 | # Pod::Spec.new do |spec|
14 | # # ...
15 | #
16 | # # Add all files generated by Nitrogen
17 | # load 'nitrogen/generated/ios/NitroText+autolinking.rb'
18 | # add_nitrogen_files(spec)
19 | # end
20 | # ```
21 |
22 | def add_nitrogen_files(spec)
23 | Pod::UI.puts "[NitroModules] 🔥 NitroText is boosted by nitro!"
24 |
25 | spec.dependency "NitroModules"
26 |
27 | current_source_files = Array(spec.attributes_hash['source_files'])
28 | spec.source_files = current_source_files + [
29 | # Generated cross-platform specs
30 | "nitrogen/generated/shared/**/*.{h,hpp,c,cpp,swift}",
31 | # Generated bridges for the cross-platform specs
32 | "nitrogen/generated/ios/**/*.{h,hpp,c,cpp,mm,swift}",
33 | ]
34 |
35 | current_public_header_files = Array(spec.attributes_hash['public_header_files'])
36 | spec.public_header_files = current_public_header_files + [
37 | # Generated specs
38 | "nitrogen/generated/shared/**/*.{h,hpp}",
39 | # Swift to C++ bridging helpers
40 | "nitrogen/generated/ios/NitroText-Swift-Cxx-Bridge.hpp"
41 | ]
42 |
43 | current_private_header_files = Array(spec.attributes_hash['private_header_files'])
44 | spec.private_header_files = current_private_header_files + [
45 | # iOS specific specs
46 | "nitrogen/generated/ios/c++/**/*.{h,hpp}",
47 | # Views are framework-specific and should be private
48 | "nitrogen/generated/shared/**/views/**/*"
49 | ]
50 |
51 | current_pod_target_xcconfig = spec.attributes_hash['pod_target_xcconfig'] || {}
52 | spec.pod_target_xcconfig = current_pod_target_xcconfig.merge({
53 | # Use C++ 20
54 | "CLANG_CXX_LANGUAGE_STANDARD" => "c++20",
55 | # Enables C++ <-> Swift interop (by default it's only C)
56 | "SWIFT_OBJC_INTEROP_MODE" => "objcxx",
57 | # Enables stricter modular headers
58 | "DEFINES_MODULE" => "YES",
59 | })
60 | end
61 |
--------------------------------------------------------------------------------
/ios/NitroTextImpl+Paragraph.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NitroTextImpl+Paragraph.swift
3 | // Pods
4 | //
5 | // Created by Patrick Kabwe on 14/09/2025.
6 | //
7 |
8 | import UIKit
9 |
10 | struct ParagraphKey: Hashable {
11 | let lineHeight: CGFloat
12 | let alignmentRaw: Int
13 | let strategyRaw: UInt
14 | }
15 |
16 | extension NitroTextImpl {
17 |
18 | func makeParagraphStyle(for fragment: Fragment) -> NSParagraphStyle {
19 | let textAlignment: NSTextAlignment = {
20 | if let align = fragment.textAlign {
21 | switch align {
22 | case .center: return .center
23 | case .right: return .right
24 | case .justify: return .justified
25 | case .left: return .left
26 | case .auto: return .natural
27 | }
28 | }
29 | return currentTextAlignment
30 | }()
31 |
32 | var _lineHeight: CGFloat = 0
33 | if let lineHeight = fragment.lineHeight, lineHeight > 0 {
34 | let baseSize: CGFloat = fragment.fontSize.map({ CGFloat($0) })
35 | ?? nitroTextView?.font?.pointSize
36 | ?? 14.0
37 | let scale = allowFontScaling ? getScaleFactor(requestedSize: baseSize) : 1.0
38 | _lineHeight = CGFloat(lineHeight) * scale
39 | }
40 |
41 | let strategyRaw: UInt = {
42 | if #available(iOS 14.0, *) {
43 | return currentLineBreakStrategy.rawValue
44 | }
45 | return 0
46 | }()
47 |
48 | let key = ParagraphKey(
49 | lineHeight: _lineHeight,
50 | alignmentRaw: Int(textAlignment.rawValue),
51 | strategyRaw: strategyRaw
52 | )
53 | if let cached = paragraphStyleCache[key] { return cached }
54 |
55 | let para = NSMutableParagraphStyle()
56 | if _lineHeight > 0 {
57 | para.minimumLineHeight = _lineHeight
58 | para.maximumLineHeight = _lineHeight
59 | }
60 | para.alignment = textAlignment
61 | if #available(iOS 14.0, *) {
62 | para.lineBreakStrategy = currentLineBreakStrategy
63 | }
64 |
65 | let immutable = para.copy() as! NSParagraphStyle
66 | paragraphStyleCache[key] = immutable
67 | return immutable
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/.github/workflows/android-build.yml:
--------------------------------------------------------------------------------
1 | name: Build Android
2 |
3 | permissions:
4 | contents: read
5 |
6 | on:
7 | push:
8 | branches:
9 | - main
10 | paths:
11 | - '.github/workflows/android-build.yml'
12 | - 'example/android/**'
13 | - 'nitrogen/generated/shared/**'
14 | - 'nitrogen/generated/android/**'
15 | - 'cpp/**'
16 | - 'android/**'
17 | - '**/bun.lock'
18 | - '**/react-native.config.js'
19 | - '**/nitro.json'
20 | pull_request:
21 | paths:
22 | - '.github/workflows/android-build.yml'
23 | - 'example/android/**'
24 | - '**/nitrogen/generated/shared/**'
25 | - '**/nitrogen/generated/android/**'
26 | - 'cpp/**'
27 | - 'android/**'
28 | - '**/bun.lock'
29 | - '**/react-native.config.js'
30 | - '**/nitro.json'
31 | workflow_dispatch:
32 |
33 | concurrency:
34 | group: ${{ github.workflow }}-${{ github.ref }}
35 | cancel-in-progress: true
36 |
37 | jobs:
38 | build:
39 | name: Build Android Example App (${{ matrix.arch }})
40 | runs-on: ubuntu-latest
41 | strategy:
42 | fail-fast: false
43 | matrix:
44 | arch: [new, old]
45 | steps:
46 | - uses: actions/checkout@v6
47 | - uses: oven-sh/setup-bun@v2
48 |
49 | - name: Install dependencies (bun)
50 | run: bun install
51 |
52 | - name: Disable new architecture in gradle.properties
53 | if: matrix.arch == 'old'
54 | run: sed -i "s/newArchEnabled=true/newArchEnabled=false/g" example/android/gradle.properties
55 |
56 | - name: Setup JDK 17
57 | uses: actions/setup-java@v5
58 | with:
59 | distribution: 'zulu'
60 | java-version: '17'
61 | cache: 'gradle'
62 |
63 | - name: Cache Gradle
64 | uses: actions/cache@v4
65 | with:
66 | path: |
67 | ~/.gradle/caches
68 | ~/.gradle/wrapper
69 | key: ${{ runner.os }}-gradle-${{ hashFiles('example/android/**/*.gradle*') }}
70 | restore-keys: |
71 | ${{ runner.os }}-gradle-
72 |
73 | - name: Run Gradle build
74 | working-directory: example/android
75 | run: ./gradlew assembleDebug --no-daemon --build-cache
76 |
77 | - name: Stop Gradle daemon
78 | working-directory: example/android
79 | run: ./gradlew --stop
80 |
--------------------------------------------------------------------------------
/cpp/NitroTextComponentDescriptor.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // NitroTextComponentDescriptor.cpp
3 | // Shared implementation for custom ComponentDescriptor
4 | //
5 |
6 | #include "NitroTextComponentDescriptor.hpp"
7 | #include
8 |
9 | using namespace facebook;
10 | using namespace margelo::nitro::nitrotext::views;
11 |
12 | NitroTextComponentDescriptor::NitroTextComponentDescriptor(const react::ComponentDescriptorParameters& parameters)
13 | : ConcreteComponentDescriptor(parameters,
14 | react::RawPropsParser(/* enableJsiParser */ true)) {}
15 |
16 | std::shared_ptr NitroTextComponentDescriptor::cloneProps(const react::PropsParserContext& context,
17 | const std::shared_ptr& props,
18 | react::RawProps rawProps) const {
19 | // 1. Prepare raw props parser
20 | rawProps.parse(rawPropsParser_);
21 | // 2. Copy props with Nitro's cached copy constructor
22 | return NitroTextShadowNode::Props(context, /* & */ rawProps, props);
23 | }
24 |
25 | void NitroTextComponentDescriptor::adopt(react::ShadowNode& shadowNode) const {
26 | // Always call base adopt first.
27 | ConcreteComponentDescriptor::adopt(shadowNode);
28 |
29 | auto& concreteShadowNode = dynamic_cast(shadowNode);
30 |
31 | #ifdef ANDROID
32 | // On Android, wrap props into state for JNI roundtrip.
33 | const HybridNitroTextProps& props = concreteShadowNode.getConcreteProps();
34 | HybridNitroTextState state;
35 | state.setProps(props);
36 | concreteShadowNode.setStateData(std::move(state));
37 | #endif
38 |
39 | // Inject TextLayoutManager so measurement works on Fabric (iOS/macOS/etc.).
40 | // Construct directly with the descriptor's ContextContainer.
41 | if (auto contextContainer = this->getContextContainer()) {
42 | auto textLayoutManager = std::make_shared(contextContainer);
43 | concreteShadowNode.setTextLayoutManager(textLayoutManager);
44 | } else {
45 | auto textLayoutManager = std::make_shared(nullptr);
46 | concreteShadowNode.setTextLayoutManager(textLayoutManager);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/nitrogen/generated/ios/NitroText-Swift-Cxx-Bridge.cpp:
--------------------------------------------------------------------------------
1 | ///
2 | /// NitroText-Swift-Cxx-Bridge.cpp
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | #include "NitroText-Swift-Cxx-Bridge.hpp"
9 |
10 | // Include C++ implementation defined types
11 | #include "HybridNitroTextSpecSwift.hpp"
12 | #include "NitroText-Swift-Cxx-Umbrella.hpp"
13 | #include
14 |
15 | namespace margelo::nitro::nitrotext::bridge::swift {
16 |
17 | // pragma MARK: std::function
18 | Func_void create_Func_void(void* NON_NULL swiftClosureWrapper) noexcept {
19 | auto swiftClosure = NitroText::Func_void::fromUnsafe(swiftClosureWrapper);
20 | return [swiftClosure = std::move(swiftClosure)]() mutable -> void {
21 | swiftClosure.call();
22 | };
23 | }
24 |
25 | // pragma MARK: std::function
26 | Func_void_TextLayoutEvent create_Func_void_TextLayoutEvent(void* NON_NULL swiftClosureWrapper) noexcept {
27 | auto swiftClosure = NitroText::Func_void_TextLayoutEvent::fromUnsafe(swiftClosureWrapper);
28 | return [swiftClosure = std::move(swiftClosure)](const TextLayoutEvent& layout) mutable -> void {
29 | swiftClosure.call(layout);
30 | };
31 | }
32 |
33 | // pragma MARK: std::shared_ptr
34 | std::shared_ptr create_std__shared_ptr_HybridNitroTextSpec_(void* NON_NULL swiftUnsafePointer) noexcept {
35 | NitroText::HybridNitroTextSpec_cxx swiftPart = NitroText::HybridNitroTextSpec_cxx::fromUnsafe(swiftUnsafePointer);
36 | return std::make_shared(swiftPart);
37 | }
38 | void* NON_NULL get_std__shared_ptr_HybridNitroTextSpec_(std__shared_ptr_HybridNitroTextSpec_ cppType) {
39 | std::shared_ptr swiftWrapper = std::dynamic_pointer_cast(cppType);
40 | #ifdef NITRO_DEBUG
41 | if (swiftWrapper == nullptr) [[unlikely]] {
42 | throw std::runtime_error("Class \"HybridNitroTextSpec\" is not implemented in Swift!");
43 | }
44 | #endif
45 | NitroText::HybridNitroTextSpec_cxx& swiftPart = swiftWrapper->getSwiftPart();
46 | return swiftPart.toUnsafe();
47 | }
48 |
49 | } // namespace margelo::nitro::nitrotext::bridge::swift
50 |
--------------------------------------------------------------------------------
/cpp/NitroTextLogger.hpp:
--------------------------------------------------------------------------------
1 | //
2 | // NitroTextLogger.hpp
3 | //
4 | //
5 |
6 | #pragma once
7 |
8 | #include
9 | #include
10 | #include
11 |
12 | // Check if we're in debug mode
13 | #ifndef NDEBUG
14 | #define NITRO_TEXT_DEBUG_LOGGING 1
15 | #else
16 | #define NITRO_TEXT_DEBUG_LOGGING 0
17 | #endif
18 |
19 | namespace margelo::nitro::nitrotext::logger {
20 |
21 | /**
22 | * @brief Logs an informational message
23 | * @param message The message to log
24 | * @param tag Optional tag identifier for categorization
25 | *
26 | * Only logs in debug builds to avoid performance overhead in production
27 | */
28 | inline void info(
29 | const std::string& message,
30 | const std::string& tag = "") {
31 | #if NITRO_TEXT_DEBUG_LOGGING
32 | std::ostringstream oss;
33 | oss << "[NitroText]";
34 | if (!tag.empty()) {
35 | oss << " [" << tag << "]";
36 | }
37 | oss << " " << message;
38 | std::cout << oss.str() << std::endl;
39 | #else
40 | // No-op in release builds
41 | (void)message;
42 | (void)tag;
43 | #endif
44 | }
45 |
46 | /**
47 | * @brief Logs a warning message
48 | * @param message The warning message to log
49 | * @param tag Optional tag identifier for categorization
50 | *
51 | * Only logs in debug builds to avoid performance overhead in production
52 | */
53 | inline void warn(
54 | const std::string& message,
55 | const std::string& tag = "") {
56 | #if NITRO_TEXT_DEBUG_LOGGING
57 | std::ostringstream oss;
58 | oss << "[NitroText] [WARN]";
59 | if (!tag.empty()) {
60 | oss << " [" << tag << "]";
61 | }
62 | oss << " " << message;
63 | std::cerr << oss.str() << std::endl;
64 | #else
65 | // No-op in release builds
66 | (void)message;
67 | (void)tag;
68 | #endif
69 | }
70 |
71 | /**
72 | * @brief Logs an error message
73 | * @param message The error message to log
74 | * @param tag Optional tag identifier for categorization
75 | *
76 | * Only logs in debug builds to avoid performance overhead in production
77 | */
78 | inline void error(
79 | const std::string& message,
80 | const std::string& tag = "") {
81 | #if NITRO_TEXT_DEBUG_LOGGING
82 | std::ostringstream oss;
83 | oss << "[NitroText] [ERROR]";
84 | if (!tag.empty()) {
85 | oss << " [" << tag << "]";
86 | }
87 | oss << " " << message;
88 | std::cerr << oss.str() << std::endl;
89 | #else
90 | // No-op in release builds
91 | (void)message;
92 | (void)tag;
93 | #endif
94 | }
95 |
96 | } // namespace margelo::nitro::nitrotext::logger
97 |
--------------------------------------------------------------------------------
/eslint.config.mjs:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "eslint/config";
2 | import eslint from '@eslint/js';
3 | import tseslint from 'typescript-eslint';
4 | import react from 'eslint-plugin-react';
5 | import reactHooks from 'eslint-plugin-react-hooks';
6 | import reactNative from 'eslint-plugin-react-native';
7 | import prettierPlugin from 'eslint-plugin-prettier';
8 | import prettierConfig from 'eslint-config-prettier';
9 | import nitroModulesPlugin from 'eslint-plugin-nitro-modules';
10 |
11 | export default defineConfig([
12 | eslint.configs.recommended,
13 | nitroModulesPlugin.configs.recommended,
14 | ...tseslint.configs.recommended,
15 | prettierConfig,
16 | {
17 | files: ['src/**/*.{js,jsx,ts,tsx}'],
18 | languageOptions: {
19 | ecmaVersion: 2024,
20 | sourceType: 'module',
21 | parserOptions: {
22 | ecmaFeatures: {
23 | jsx: true,
24 | },
25 | },
26 | },
27 | plugins: {
28 | react,
29 | 'react-hooks': reactHooks,
30 | 'react-native': reactNative,
31 | prettier: prettierPlugin
32 | },
33 | rules: {
34 | ...react.configs['jsx-runtime'].rules,
35 | ...reactHooks.configs.recommended.rules,
36 | 'react/react-in-jsx-scope': 'off',
37 | 'react/prop-types': 'off',
38 | '@typescript-eslint/no-explicit-any': 'warn',
39 | "@typescript-eslint/no-empty-object-type": "off",
40 | "@typescript-eslint/no-require-imports": "off",
41 | '@typescript-eslint/no-unused-vars': [
42 | 'warn',
43 | {
44 | argsIgnorePattern: '^_',
45 | varsIgnorePattern: '^_',
46 | },
47 | ],
48 | 'react-hooks/rules-of-hooks': 'error',
49 | 'react-hooks/exhaustive-deps': 'warn',
50 |
51 | 'prettier/prettier': [
52 | 'warn',
53 | {
54 | "quoteProps": "consistent",
55 | "singleQuote": true,
56 | "tabWidth": 3,
57 | "trailingComma": "es5",
58 | "useTabs": false,
59 | "semi": false
60 | },
61 | ],
62 | },
63 | settings: {
64 | react: {
65 | version: 'detect',
66 | },
67 | },
68 | },
69 | {
70 | ignores: [
71 | 'node_modules/**',
72 | 'lib/**',
73 | 'build/**',
74 | 'example/**',
75 | 'nitrogen/generated/**',
76 | 'android/**',
77 | 'ios/**',
78 | 'cpp/**',
79 | '*.config.js',
80 | '*.config.mjs',
81 | '*.config.cjs',
82 | '*.podspec',
83 | ],
84 | },
85 | ]);
86 |
--------------------------------------------------------------------------------
/nitrogen/generated/ios/swift/TextLayout.swift:
--------------------------------------------------------------------------------
1 | ///
2 | /// TextLayout.swift
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | import Foundation
9 | import NitroModules
10 |
11 | /**
12 | * Represents an instance of `TextLayout`, backed by a C++ struct.
13 | */
14 | public typealias TextLayout = margelo.nitro.nitrotext.TextLayout
15 |
16 | public extension TextLayout {
17 | private typealias bridge = margelo.nitro.nitrotext.bridge.swift
18 |
19 | /**
20 | * Create a new instance of `TextLayout`.
21 | */
22 | init(text: String, x: Double, y: Double, width: Double, height: Double, descender: Double, capHeight: Double, ascender: Double, xHeight: Double) {
23 | self.init(std.string(text), x, y, width, height, descender, capHeight, ascender, xHeight)
24 | }
25 |
26 | var text: String {
27 | @inline(__always)
28 | get {
29 | return String(self.__text)
30 | }
31 | @inline(__always)
32 | set {
33 | self.__text = std.string(newValue)
34 | }
35 | }
36 |
37 | var x: Double {
38 | @inline(__always)
39 | get {
40 | return self.__x
41 | }
42 | @inline(__always)
43 | set {
44 | self.__x = newValue
45 | }
46 | }
47 |
48 | var y: Double {
49 | @inline(__always)
50 | get {
51 | return self.__y
52 | }
53 | @inline(__always)
54 | set {
55 | self.__y = newValue
56 | }
57 | }
58 |
59 | var width: Double {
60 | @inline(__always)
61 | get {
62 | return self.__width
63 | }
64 | @inline(__always)
65 | set {
66 | self.__width = newValue
67 | }
68 | }
69 |
70 | var height: Double {
71 | @inline(__always)
72 | get {
73 | return self.__height
74 | }
75 | @inline(__always)
76 | set {
77 | self.__height = newValue
78 | }
79 | }
80 |
81 | var descender: Double {
82 | @inline(__always)
83 | get {
84 | return self.__descender
85 | }
86 | @inline(__always)
87 | set {
88 | self.__descender = newValue
89 | }
90 | }
91 |
92 | var capHeight: Double {
93 | @inline(__always)
94 | get {
95 | return self.__capHeight
96 | }
97 | @inline(__always)
98 | set {
99 | self.__capHeight = newValue
100 | }
101 | }
102 |
103 | var ascender: Double {
104 | @inline(__always)
105 | get {
106 | return self.__ascender
107 | }
108 | @inline(__always)
109 | set {
110 | self.__ascender = newValue
111 | }
112 | }
113 |
114 | var xHeight: Double {
115 | @inline(__always)
116 | get {
117 | return self.__xHeight
118 | }
119 | @inline(__always)
120 | set {
121 | self.__xHeight = newValue
122 | }
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/nitrogen/generated/shared/c++/TextLayoutEvent.hpp:
--------------------------------------------------------------------------------
1 | ///
2 | /// TextLayoutEvent.hpp
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | #pragma once
9 |
10 | #if __has_include()
11 | #include
12 | #else
13 | #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
14 | #endif
15 | #if __has_include()
16 | #include
17 | #else
18 | #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
19 | #endif
20 | #if __has_include()
21 | #include
22 | #else
23 | #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
24 | #endif
25 |
26 | // Forward declaration of `TextLayout` to properly resolve imports.
27 | namespace margelo::nitro::nitrotext { struct TextLayout; }
28 |
29 | #include "TextLayout.hpp"
30 | #include
31 |
32 | namespace margelo::nitro::nitrotext {
33 |
34 | /**
35 | * A struct which can be represented as a JavaScript object (TextLayoutEvent).
36 | */
37 | struct TextLayoutEvent {
38 | public:
39 | std::vector lines SWIFT_PRIVATE;
40 |
41 | public:
42 | TextLayoutEvent() = default;
43 | explicit TextLayoutEvent(std::vector lines): lines(lines) {}
44 | };
45 |
46 | } // namespace margelo::nitro::nitrotext
47 |
48 | namespace margelo::nitro {
49 |
50 | // C++ TextLayoutEvent <> JS TextLayoutEvent (object)
51 | template <>
52 | struct JSIConverter final {
53 | static inline margelo::nitro::nitrotext::TextLayoutEvent fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
54 | jsi::Object obj = arg.asObject(runtime);
55 | return margelo::nitro::nitrotext::TextLayoutEvent(
56 | JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, "lines"))
57 | );
58 | }
59 | static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::nitrotext::TextLayoutEvent& arg) {
60 | jsi::Object obj(runtime);
61 | obj.setProperty(runtime, "lines", JSIConverter>::toJSI(runtime, arg.lines));
62 | return obj;
63 | }
64 | static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
65 | if (!value.isObject()) {
66 | return false;
67 | }
68 | jsi::Object obj = value.getObject(runtime);
69 | if (!nitro::isPlainObject(runtime, obj)) {
70 | return false;
71 | }
72 | if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, "lines"))) return false;
73 | return true;
74 | }
75 | };
76 |
77 | } // namespace margelo::nitro
78 |
--------------------------------------------------------------------------------
/nitrogen/generated/shared/c++/MenuItem.hpp:
--------------------------------------------------------------------------------
1 | ///
2 | /// MenuItem.hpp
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | #pragma once
9 |
10 | #if __has_include()
11 | #include
12 | #else
13 | #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
14 | #endif
15 | #if __has_include()
16 | #include
17 | #else
18 | #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
19 | #endif
20 | #if __has_include()
21 | #include
22 | #else
23 | #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
24 | #endif
25 |
26 |
27 |
28 | #include
29 | #include
30 |
31 | namespace margelo::nitro::nitrotext {
32 |
33 | /**
34 | * A struct which can be represented as a JavaScript object (MenuItem).
35 | */
36 | struct MenuItem {
37 | public:
38 | std::string title SWIFT_PRIVATE;
39 | std::function action SWIFT_PRIVATE;
40 |
41 | public:
42 | MenuItem() = default;
43 | explicit MenuItem(std::string title, std::function action): title(title), action(action) {}
44 | };
45 |
46 | } // namespace margelo::nitro::nitrotext
47 |
48 | namespace margelo::nitro {
49 |
50 | // C++ MenuItem <> JS MenuItem (object)
51 | template <>
52 | struct JSIConverter final {
53 | static inline margelo::nitro::nitrotext::MenuItem fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
54 | jsi::Object obj = arg.asObject(runtime);
55 | return margelo::nitro::nitrotext::MenuItem(
56 | JSIConverter::fromJSI(runtime, obj.getProperty(runtime, "title")),
57 | JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, "action"))
58 | );
59 | }
60 | static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::nitrotext::MenuItem& arg) {
61 | jsi::Object obj(runtime);
62 | obj.setProperty(runtime, "title", JSIConverter::toJSI(runtime, arg.title));
63 | obj.setProperty(runtime, "action", JSIConverter>::toJSI(runtime, arg.action));
64 | return obj;
65 | }
66 | static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
67 | if (!value.isObject()) {
68 | return false;
69 | }
70 | jsi::Object obj = value.getObject(runtime);
71 | if (!nitro::isPlainObject(runtime, obj)) {
72 | return false;
73 | }
74 | if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, "title"))) return false;
75 | if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, "action"))) return false;
76 | return true;
77 | }
78 | };
79 |
80 | } // namespace margelo::nitro
81 |
--------------------------------------------------------------------------------
/nitrogen/generated/shared/c++/Renderer.hpp:
--------------------------------------------------------------------------------
1 | ///
2 | /// Renderer.hpp
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | #pragma once
9 |
10 | #if __has_include()
11 | #include
12 | #else
13 | #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
14 | #endif
15 | #if __has_include()
16 | #include
17 | #else
18 | #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
19 | #endif
20 | #if __has_include()
21 | #include
22 | #else
23 | #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
24 | #endif
25 |
26 | namespace margelo::nitro::nitrotext {
27 |
28 | /**
29 | * An enum which can be represented as a JavaScript union (Renderer).
30 | */
31 | enum class Renderer {
32 | HTML SWIFT_NAME(html) = 0,
33 | PLAINTEXT SWIFT_NAME(plaintext) = 1,
34 | } CLOSED_ENUM;
35 |
36 | } // namespace margelo::nitro::nitrotext
37 |
38 | namespace margelo::nitro {
39 |
40 | // C++ Renderer <> JS Renderer (union)
41 | template <>
42 | struct JSIConverter final {
43 | static inline margelo::nitro::nitrotext::Renderer fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
44 | std::string unionValue = JSIConverter::fromJSI(runtime, arg);
45 | switch (hashString(unionValue.c_str(), unionValue.size())) {
46 | case hashString("html"): return margelo::nitro::nitrotext::Renderer::HTML;
47 | case hashString("plaintext"): return margelo::nitro::nitrotext::Renderer::PLAINTEXT;
48 | default: [[unlikely]]
49 | throw std::invalid_argument("Cannot convert \"" + unionValue + "\" to enum Renderer - invalid value!");
50 | }
51 | }
52 | static inline jsi::Value toJSI(jsi::Runtime& runtime, margelo::nitro::nitrotext::Renderer arg) {
53 | switch (arg) {
54 | case margelo::nitro::nitrotext::Renderer::HTML: return JSIConverter::toJSI(runtime, "html");
55 | case margelo::nitro::nitrotext::Renderer::PLAINTEXT: return JSIConverter::toJSI(runtime, "plaintext");
56 | default: [[unlikely]]
57 | throw std::invalid_argument("Cannot convert Renderer to JS - invalid value: "
58 | + std::to_string(static_cast(arg)) + "!");
59 | }
60 | }
61 | static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
62 | if (!value.isString()) {
63 | return false;
64 | }
65 | std::string unionValue = JSIConverter::fromJSI(runtime, value);
66 | switch (hashString(unionValue.c_str(), unionValue.size())) {
67 | case hashString("html"):
68 | case hashString("plaintext"):
69 | return true;
70 | default:
71 | return false;
72 | }
73 | }
74 | };
75 |
76 | } // namespace margelo::nitro
77 |
--------------------------------------------------------------------------------
/nitrogen/generated/ios/swift/HybridNitroTextSpec.swift:
--------------------------------------------------------------------------------
1 | ///
2 | /// HybridNitroTextSpec.swift
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | import Foundation
9 | import NitroModules
10 |
11 | /// See ``HybridNitroTextSpec``
12 | public protocol HybridNitroTextSpec_protocol: HybridObject, HybridView {
13 | // Properties
14 | var fragments: [Fragment]? { get set }
15 | var renderer: Renderer? { get set }
16 | var selectable: Bool? { get set }
17 | var allowFontScaling: Bool? { get set }
18 | var ellipsizeMode: EllipsizeMode? { get set }
19 | var numberOfLines: Double? { get set }
20 | var lineBreakStrategyIOS: LineBreakStrategyIOS? { get set }
21 | var dynamicTypeRamp: DynamicTypeRamp? { get set }
22 | var maxFontSizeMultiplier: Double? { get set }
23 | var adjustsFontSizeToFit: Bool? { get set }
24 | var minimumFontScale: Double? { get set }
25 | var menus: [MenuItem]? { get set }
26 | var onTextLayout: ((_ layout: TextLayoutEvent) -> Void)? { get set }
27 | var onPress: (() -> Void)? { get set }
28 | var onPressIn: (() -> Void)? { get set }
29 | var onPressOut: (() -> Void)? { get set }
30 | var text: String? { get set }
31 | var selectionColor: String? { get set }
32 | var fontSize: Double? { get set }
33 | var fontWeight: FontWeight? { get set }
34 | var fontColor: String? { get set }
35 | var fragmentBackgroundColor: String? { get set }
36 | var fontStyle: FontStyle? { get set }
37 | var fontFamily: String? { get set }
38 | var lineHeight: Double? { get set }
39 | var letterSpacing: Double? { get set }
40 | var textAlign: TextAlign? { get set }
41 | var textTransform: TextTransform? { get set }
42 | var textDecorationLine: TextDecorationLine? { get set }
43 | var textDecorationColor: String? { get set }
44 | var textDecorationStyle: TextDecorationStyle? { get set }
45 |
46 | // Methods
47 |
48 | }
49 |
50 | public extension HybridNitroTextSpec_protocol {
51 | /// Default implementation of ``HybridObject.toString``
52 | func toString() -> String {
53 | return "[HybridObject NitroText]"
54 | }
55 | }
56 |
57 | /// See ``HybridNitroTextSpec``
58 | open class HybridNitroTextSpec_base {
59 | private weak var cxxWrapper: HybridNitroTextSpec_cxx? = nil
60 | public init() { }
61 | public func getCxxWrapper() -> HybridNitroTextSpec_cxx {
62 | #if DEBUG
63 | guard self is HybridNitroTextSpec else {
64 | fatalError("`self` is not a `HybridNitroTextSpec`! Did you accidentally inherit from `HybridNitroTextSpec_base` instead of `HybridNitroTextSpec`?")
65 | }
66 | #endif
67 | if let cxxWrapper = self.cxxWrapper {
68 | return cxxWrapper
69 | } else {
70 | let cxxWrapper = HybridNitroTextSpec_cxx(self as! HybridNitroTextSpec)
71 | self.cxxWrapper = cxxWrapper
72 | return cxxWrapper
73 | }
74 | }
75 | }
76 |
77 | /**
78 | * A Swift base-protocol representing the NitroText HybridObject.
79 | * Implement this protocol to create Swift-based instances of NitroText.
80 | * ```swift
81 | * class HybridNitroText : HybridNitroTextSpec {
82 | * // ...
83 | * }
84 | * ```
85 | */
86 | public typealias HybridNitroTextSpec = HybridNitroTextSpec_protocol & HybridNitroTextSpec_base
87 |
--------------------------------------------------------------------------------
/.github/workflows/ios-build.yml:
--------------------------------------------------------------------------------
1 | name: Build iOS
2 |
3 | permissions:
4 | contents: read
5 |
6 | on:
7 | push:
8 | branches:
9 | - main
10 | paths:
11 | - '.github/workflows/ios-build.yml'
12 | - 'example/ios/**'
13 | - 'example/Gemfile'
14 | - 'example/Gemfile.lock'
15 | - '**/nitrogen/generated/shared/**'
16 | - '**/nitrogen/generated/ios/**'
17 | - 'cpp/**'
18 | - 'ios/**'
19 | - '**/Podfile.lock'
20 | - '**/bun.lock'
21 | - '**/*.podspec'
22 | - '**/react-native.config.js'
23 | - '**/nitro.json'
24 | pull_request:
25 | paths:
26 | - '.github/workflows/ios-build.yml'
27 | - 'example/ios/**'
28 | - 'example/Gemfile'
29 | - 'example/Gemfile.lock'
30 | - '**/nitrogen/generated/shared/**'
31 | - '**/nitrogen/generated/ios/**'
32 | - 'cpp/**'
33 | - 'ios/**'
34 | - '**/Podfile.lock'
35 | - '**/bun.lock'
36 | - '**/*.podspec'
37 | - '**/react-native.config.js'
38 | - '**/nitro.json'
39 | workflow_dispatch:
40 |
41 | env:
42 | USE_CCACHE: 1
43 |
44 | concurrency:
45 | group: ${{ github.workflow }}-${{ github.ref }}
46 | cancel-in-progress: true
47 |
48 | jobs:
49 | build:
50 | name: Build iOS Example App (${{ matrix.arch }})
51 | runs-on: macOS-15
52 | strategy:
53 | fail-fast: false
54 | matrix:
55 | arch: [new, old]
56 | steps:
57 | - uses: actions/checkout@v6
58 | - uses: oven-sh/setup-bun@v2
59 | - name: Setup Xcode
60 | uses: maxim-lobanov/setup-xcode@v1
61 | with:
62 | xcode-version: 16.4
63 |
64 | - name: Install dependencies (bun)
65 | run: bun install
66 |
67 | - name: Disable new architecture in Podfile
68 | if: matrix.arch == 'old'
69 | run: sed -i "" "s/ENV\['RCT_NEW_ARCH_ENABLED'\] = '1'/ENV['RCT_NEW_ARCH_ENABLED'] = '0'/g" example/ios/Podfile
70 |
71 | - name: Setup Ruby (bundle)
72 | uses: ruby/setup-ruby@v1
73 | with:
74 | ruby-version: '3.2'
75 | bundler-cache: true
76 | working-directory: example/ios
77 |
78 | - name: Install xcpretty
79 | run: gem install xcpretty
80 |
81 | # - name: Cache CocoaPods
82 | # uses: actions/cache@v4
83 | # with:
84 | # path: example/ios/Pods
85 | # key: ${{ runner.os }}-pods-${{ hashFiles('example/ios/Podfile') }}
86 | # restore-keys: |
87 | # ${{ runner.os }}-pods-
88 |
89 | - name: Install Pods
90 | working-directory: example/ios
91 | run: pod install
92 |
93 | - name: Build App
94 | working-directory: example/ios
95 | run: |
96 | set -o pipefail && xcodebuild \
97 | CC=clang CPLUSPLUS=clang++ LD=clang LDPLUSPLUS=clang++ \
98 | -derivedDataPath build -UseModernBuildSystem=YES \
99 | -workspace NitroTextExample.xcworkspace \
100 | -scheme NitroTextExample \
101 | -sdk iphonesimulator \
102 | -configuration Debug \
103 | -destination 'platform=iOS Simulator,name=iPhone 16' \
104 | build \
105 | CODE_SIGNING_ALLOWED=NO | xcpretty
106 |
--------------------------------------------------------------------------------
/nitrogen/generated/shared/c++/FontStyle.hpp:
--------------------------------------------------------------------------------
1 | ///
2 | /// FontStyle.hpp
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | #pragma once
9 |
10 | #if __has_include()
11 | #include
12 | #else
13 | #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
14 | #endif
15 | #if __has_include()
16 | #include
17 | #else
18 | #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
19 | #endif
20 | #if __has_include()
21 | #include
22 | #else
23 | #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
24 | #endif
25 |
26 | namespace margelo::nitro::nitrotext {
27 |
28 | /**
29 | * An enum which can be represented as a JavaScript union (FontStyle).
30 | */
31 | enum class FontStyle {
32 | NORMAL SWIFT_NAME(normal) = 0,
33 | ITALIC SWIFT_NAME(italic) = 1,
34 | OBLIQUE SWIFT_NAME(oblique) = 2,
35 | } CLOSED_ENUM;
36 |
37 | } // namespace margelo::nitro::nitrotext
38 |
39 | namespace margelo::nitro {
40 |
41 | // C++ FontStyle <> JS FontStyle (union)
42 | template <>
43 | struct JSIConverter final {
44 | static inline margelo::nitro::nitrotext::FontStyle fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
45 | std::string unionValue = JSIConverter::fromJSI(runtime, arg);
46 | switch (hashString(unionValue.c_str(), unionValue.size())) {
47 | case hashString("normal"): return margelo::nitro::nitrotext::FontStyle::NORMAL;
48 | case hashString("italic"): return margelo::nitro::nitrotext::FontStyle::ITALIC;
49 | case hashString("oblique"): return margelo::nitro::nitrotext::FontStyle::OBLIQUE;
50 | default: [[unlikely]]
51 | throw std::invalid_argument("Cannot convert \"" + unionValue + "\" to enum FontStyle - invalid value!");
52 | }
53 | }
54 | static inline jsi::Value toJSI(jsi::Runtime& runtime, margelo::nitro::nitrotext::FontStyle arg) {
55 | switch (arg) {
56 | case margelo::nitro::nitrotext::FontStyle::NORMAL: return JSIConverter::toJSI(runtime, "normal");
57 | case margelo::nitro::nitrotext::FontStyle::ITALIC: return JSIConverter::toJSI(runtime, "italic");
58 | case margelo::nitro::nitrotext::FontStyle::OBLIQUE: return JSIConverter::toJSI(runtime, "oblique");
59 | default: [[unlikely]]
60 | throw std::invalid_argument("Cannot convert FontStyle to JS - invalid value: "
61 | + std::to_string(static_cast(arg)) + "!");
62 | }
63 | }
64 | static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
65 | if (!value.isString()) {
66 | return false;
67 | }
68 | std::string unionValue = JSIConverter::fromJSI(runtime, value);
69 | switch (hashString(unionValue.c_str(), unionValue.size())) {
70 | case hashString("normal"):
71 | case hashString("italic"):
72 | case hashString("oblique"):
73 | return true;
74 | default:
75 | return false;
76 | }
77 | }
78 | };
79 |
80 | } // namespace margelo::nitro
81 |
--------------------------------------------------------------------------------
/example/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: https://rubygems.org/
3 | specs:
4 | CFPropertyList (3.0.7)
5 | base64
6 | nkf
7 | rexml
8 | activesupport (7.2.3)
9 | base64
10 | benchmark (>= 0.3)
11 | bigdecimal
12 | concurrent-ruby (~> 1.0, >= 1.3.1)
13 | connection_pool (>= 2.2.5)
14 | drb
15 | i18n (>= 1.6, < 2)
16 | logger (>= 1.4.2)
17 | minitest (>= 5.1)
18 | securerandom (>= 0.3)
19 | tzinfo (~> 2.0, >= 2.0.5)
20 | addressable (2.8.7)
21 | public_suffix (>= 2.0.2, < 7.0)
22 | algoliasearch (1.27.5)
23 | httpclient (~> 2.8, >= 2.8.3)
24 | json (>= 1.5.1)
25 | atomos (0.1.3)
26 | base64 (0.3.0)
27 | benchmark (0.5.0)
28 | bigdecimal (3.3.1)
29 | claide (1.1.0)
30 | cocoapods (1.16.2)
31 | addressable (~> 2.8)
32 | claide (>= 1.0.2, < 2.0)
33 | cocoapods-core (= 1.16.2)
34 | cocoapods-deintegrate (>= 1.0.3, < 2.0)
35 | cocoapods-downloader (>= 2.1, < 3.0)
36 | cocoapods-plugins (>= 1.0.0, < 2.0)
37 | cocoapods-search (>= 1.0.0, < 2.0)
38 | cocoapods-trunk (>= 1.6.0, < 2.0)
39 | cocoapods-try (>= 1.1.0, < 2.0)
40 | colored2 (~> 3.1)
41 | escape (~> 0.0.4)
42 | fourflusher (>= 2.3.0, < 3.0)
43 | gh_inspector (~> 1.0)
44 | molinillo (~> 0.8.0)
45 | nap (~> 1.0)
46 | ruby-macho (>= 2.3.0, < 3.0)
47 | xcodeproj (>= 1.27.0, < 2.0)
48 | cocoapods-core (1.16.2)
49 | activesupport (>= 5.0, < 8)
50 | addressable (~> 2.8)
51 | algoliasearch (~> 1.0)
52 | concurrent-ruby (~> 1.1)
53 | fuzzy_match (~> 2.0.4)
54 | nap (~> 1.0)
55 | netrc (~> 0.11)
56 | public_suffix (~> 4.0)
57 | typhoeus (~> 1.0)
58 | cocoapods-deintegrate (1.0.5)
59 | cocoapods-downloader (2.1)
60 | cocoapods-plugins (1.0.0)
61 | nap
62 | cocoapods-search (1.0.1)
63 | cocoapods-trunk (1.6.0)
64 | nap (>= 0.8, < 2.0)
65 | netrc (~> 0.11)
66 | cocoapods-try (1.2.0)
67 | colored2 (3.1.2)
68 | concurrent-ruby (1.3.5)
69 | connection_pool (2.5.4)
70 | drb (2.2.3)
71 | escape (0.0.4)
72 | ethon (0.15.0)
73 | ffi (>= 1.15.0)
74 | ffi (1.17.2)
75 | fourflusher (2.3.1)
76 | fuzzy_match (2.0.4)
77 | gh_inspector (1.1.3)
78 | httpclient (2.9.0)
79 | mutex_m
80 | i18n (1.14.7)
81 | concurrent-ruby (~> 1.0)
82 | json (2.13.2)
83 | logger (1.7.0)
84 | minitest (5.26.0)
85 | molinillo (0.8.0)
86 | mutex_m (0.3.0)
87 | nanaimo (0.4.0)
88 | nap (1.1.0)
89 | netrc (0.11.0)
90 | nkf (0.2.0)
91 | public_suffix (4.0.7)
92 | rexml (3.4.4)
93 | ruby-macho (2.5.1)
94 | securerandom (0.4.1)
95 | typhoeus (1.5.0)
96 | ethon (>= 0.9.0, < 0.16.0)
97 | tzinfo (2.0.6)
98 | concurrent-ruby (~> 1.0)
99 | xcodeproj (1.27.0)
100 | CFPropertyList (>= 2.3.3, < 4.0)
101 | atomos (~> 0.1.3)
102 | claide (>= 1.0.2, < 2.0)
103 | colored2 (~> 3.1)
104 | nanaimo (~> 0.4.0)
105 | rexml (>= 3.3.6, < 4.0)
106 |
107 | PLATFORMS
108 | ruby
109 |
110 | DEPENDENCIES
111 | activesupport (>= 6.1.7.5, != 7.1.0)
112 | benchmark
113 | bigdecimal
114 | cocoapods (>= 1.13, != 1.15.1, != 1.15.0)
115 | concurrent-ruby (< 1.3.6)
116 | logger
117 | mutex_m
118 | xcodeproj (< 1.28.0)
119 |
120 | RUBY VERSION
121 | ruby 3.3.6p108
122 |
123 | BUNDLED WITH
124 | 2.5.22
125 |
--------------------------------------------------------------------------------
/example/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @REM Copyright (c) Meta Platforms, Inc. and affiliates.
2 | @REM
3 | @REM This source code is licensed under the MIT license found in the
4 | @REM LICENSE file in the root directory of this source tree.
5 |
6 | @rem
7 | @rem Copyright 2015 the original author or authors.
8 | @rem
9 | @rem Licensed under the Apache License, Version 2.0 (the "License");
10 | @rem you may not use this file except in compliance with the License.
11 | @rem You may obtain a copy of the License at
12 | @rem
13 | @rem https://www.apache.org/licenses/LICENSE-2.0
14 | @rem
15 | @rem Unless required by applicable law or agreed to in writing, software
16 | @rem distributed under the License is distributed on an "AS IS" BASIS,
17 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 | @rem See the License for the specific language governing permissions and
19 | @rem limitations under the License.
20 | @rem
21 | @rem SPDX-License-Identifier: Apache-2.0
22 | @rem
23 |
24 | @if "%DEBUG%"=="" @echo off
25 | @rem ##########################################################################
26 | @rem
27 | @rem Gradle startup script for Windows
28 | @rem
29 | @rem ##########################################################################
30 |
31 | @rem Set local scope for the variables with windows NT shell
32 | if "%OS%"=="Windows_NT" setlocal
33 |
34 | set DIRNAME=%~dp0
35 | if "%DIRNAME%"=="" set DIRNAME=.
36 | @rem This is normally unused
37 | set APP_BASE_NAME=%~n0
38 | set APP_HOME=%DIRNAME%
39 |
40 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
41 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
42 |
43 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
44 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
45 |
46 | @rem Find java.exe
47 | if defined JAVA_HOME goto findJavaFromJavaHome
48 |
49 | set JAVA_EXE=java.exe
50 | %JAVA_EXE% -version >NUL 2>&1
51 | if %ERRORLEVEL% equ 0 goto execute
52 |
53 | echo. 1>&2
54 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
55 | echo. 1>&2
56 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
57 | echo location of your Java installation. 1>&2
58 |
59 | goto fail
60 |
61 | :findJavaFromJavaHome
62 | set JAVA_HOME=%JAVA_HOME:"=%
63 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
64 |
65 | if exist "%JAVA_EXE%" goto execute
66 |
67 | echo. 1>&2
68 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
69 | echo. 1>&2
70 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
71 | echo location of your Java installation. 1>&2
72 |
73 | goto fail
74 |
75 | :execute
76 | @rem Setup the command line
77 |
78 | set CLASSPATH=
79 |
80 |
81 | @rem Execute Gradle
82 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
83 |
84 | :end
85 | @rem End local scope for the variables with windows NT shell
86 | if %ERRORLEVEL% equ 0 goto mainEnd
87 |
88 | :fail
89 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
90 | rem the _cmd.exe /c_ return code!
91 | set EXIT_CODE=%ERRORLEVEL%
92 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
93 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
94 | exit /b %EXIT_CODE%
95 |
96 | :mainEnd
97 | if "%OS%"=="Windows_NT" endlocal
98 |
99 | :omega
100 |
--------------------------------------------------------------------------------
/example/ios/NitroTextExample.xcodeproj/xcshareddata/xcschemes/NitroTextExample.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
53 |
55 |
61 |
62 |
63 |
64 |
70 |
72 |
78 |
79 |
80 |
81 |
83 |
84 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/nitrogen/generated/shared/c++/EllipsizeMode.hpp:
--------------------------------------------------------------------------------
1 | ///
2 | /// EllipsizeMode.hpp
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | #pragma once
9 |
10 | #if __has_include()
11 | #include
12 | #else
13 | #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
14 | #endif
15 | #if __has_include()
16 | #include
17 | #else
18 | #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
19 | #endif
20 | #if __has_include()
21 | #include
22 | #else
23 | #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
24 | #endif
25 |
26 | namespace margelo::nitro::nitrotext {
27 |
28 | /**
29 | * An enum which can be represented as a JavaScript union (EllipsizeMode).
30 | */
31 | enum class EllipsizeMode {
32 | HEAD SWIFT_NAME(head) = 0,
33 | MIDDLE SWIFT_NAME(middle) = 1,
34 | TAIL SWIFT_NAME(tail) = 2,
35 | CLIP SWIFT_NAME(clip) = 3,
36 | } CLOSED_ENUM;
37 |
38 | } // namespace margelo::nitro::nitrotext
39 |
40 | namespace margelo::nitro {
41 |
42 | // C++ EllipsizeMode <> JS EllipsizeMode (union)
43 | template <>
44 | struct JSIConverter final {
45 | static inline margelo::nitro::nitrotext::EllipsizeMode fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
46 | std::string unionValue = JSIConverter::fromJSI(runtime, arg);
47 | switch (hashString(unionValue.c_str(), unionValue.size())) {
48 | case hashString("head"): return margelo::nitro::nitrotext::EllipsizeMode::HEAD;
49 | case hashString("middle"): return margelo::nitro::nitrotext::EllipsizeMode::MIDDLE;
50 | case hashString("tail"): return margelo::nitro::nitrotext::EllipsizeMode::TAIL;
51 | case hashString("clip"): return margelo::nitro::nitrotext::EllipsizeMode::CLIP;
52 | default: [[unlikely]]
53 | throw std::invalid_argument("Cannot convert \"" + unionValue + "\" to enum EllipsizeMode - invalid value!");
54 | }
55 | }
56 | static inline jsi::Value toJSI(jsi::Runtime& runtime, margelo::nitro::nitrotext::EllipsizeMode arg) {
57 | switch (arg) {
58 | case margelo::nitro::nitrotext::EllipsizeMode::HEAD: return JSIConverter::toJSI(runtime, "head");
59 | case margelo::nitro::nitrotext::EllipsizeMode::MIDDLE: return JSIConverter::toJSI(runtime, "middle");
60 | case margelo::nitro::nitrotext::EllipsizeMode::TAIL: return JSIConverter::toJSI(runtime, "tail");
61 | case margelo::nitro::nitrotext::EllipsizeMode::CLIP: return JSIConverter::toJSI(runtime, "clip");
62 | default: [[unlikely]]
63 | throw std::invalid_argument("Cannot convert EllipsizeMode to JS - invalid value: "
64 | + std::to_string(static_cast(arg)) + "!");
65 | }
66 | }
67 | static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
68 | if (!value.isString()) {
69 | return false;
70 | }
71 | std::string unionValue = JSIConverter::fromJSI(runtime, value);
72 | switch (hashString(unionValue.c_str(), unionValue.size())) {
73 | case hashString("head"):
74 | case hashString("middle"):
75 | case hashString("tail"):
76 | case hashString("clip"):
77 | return true;
78 | default:
79 | return false;
80 | }
81 | }
82 | };
83 |
84 | } // namespace margelo::nitro
85 |
--------------------------------------------------------------------------------
/nitrogen/generated/shared/c++/TextTransform.hpp:
--------------------------------------------------------------------------------
1 | ///
2 | /// TextTransform.hpp
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | #pragma once
9 |
10 | #if __has_include()
11 | #include
12 | #else
13 | #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
14 | #endif
15 | #if __has_include()
16 | #include
17 | #else
18 | #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
19 | #endif
20 | #if __has_include()
21 | #include
22 | #else
23 | #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
24 | #endif
25 |
26 | namespace margelo::nitro::nitrotext {
27 |
28 | /**
29 | * An enum which can be represented as a JavaScript union (TextTransform).
30 | */
31 | enum class TextTransform {
32 | NONE SWIFT_NAME(none) = 0,
33 | UPPERCASE SWIFT_NAME(uppercase) = 1,
34 | LOWERCASE SWIFT_NAME(lowercase) = 2,
35 | CAPITALIZE SWIFT_NAME(capitalize) = 3,
36 | } CLOSED_ENUM;
37 |
38 | } // namespace margelo::nitro::nitrotext
39 |
40 | namespace margelo::nitro {
41 |
42 | // C++ TextTransform <> JS TextTransform (union)
43 | template <>
44 | struct JSIConverter final {
45 | static inline margelo::nitro::nitrotext::TextTransform fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
46 | std::string unionValue = JSIConverter::fromJSI(runtime, arg);
47 | switch (hashString(unionValue.c_str(), unionValue.size())) {
48 | case hashString("none"): return margelo::nitro::nitrotext::TextTransform::NONE;
49 | case hashString("uppercase"): return margelo::nitro::nitrotext::TextTransform::UPPERCASE;
50 | case hashString("lowercase"): return margelo::nitro::nitrotext::TextTransform::LOWERCASE;
51 | case hashString("capitalize"): return margelo::nitro::nitrotext::TextTransform::CAPITALIZE;
52 | default: [[unlikely]]
53 | throw std::invalid_argument("Cannot convert \"" + unionValue + "\" to enum TextTransform - invalid value!");
54 | }
55 | }
56 | static inline jsi::Value toJSI(jsi::Runtime& runtime, margelo::nitro::nitrotext::TextTransform arg) {
57 | switch (arg) {
58 | case margelo::nitro::nitrotext::TextTransform::NONE: return JSIConverter::toJSI(runtime, "none");
59 | case margelo::nitro::nitrotext::TextTransform::UPPERCASE: return JSIConverter::toJSI(runtime, "uppercase");
60 | case margelo::nitro::nitrotext::TextTransform::LOWERCASE: return JSIConverter::toJSI(runtime, "lowercase");
61 | case margelo::nitro::nitrotext::TextTransform::CAPITALIZE: return JSIConverter::toJSI(runtime, "capitalize");
62 | default: [[unlikely]]
63 | throw std::invalid_argument("Cannot convert TextTransform to JS - invalid value: "
64 | + std::to_string(static_cast(arg)) + "!");
65 | }
66 | }
67 | static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
68 | if (!value.isString()) {
69 | return false;
70 | }
71 | std::string unionValue = JSIConverter::fromJSI(runtime, value);
72 | switch (hashString(unionValue.c_str(), unionValue.size())) {
73 | case hashString("none"):
74 | case hashString("uppercase"):
75 | case hashString("lowercase"):
76 | case hashString("capitalize"):
77 | return true;
78 | default:
79 | return false;
80 | }
81 | }
82 | };
83 |
84 | } // namespace margelo::nitro
85 |
--------------------------------------------------------------------------------
/nitrogen/generated/shared/c++/TextDecorationStyle.hpp:
--------------------------------------------------------------------------------
1 | ///
2 | /// TextDecorationStyle.hpp
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | #pragma once
9 |
10 | #if __has_include()
11 | #include
12 | #else
13 | #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
14 | #endif
15 | #if __has_include()
16 | #include
17 | #else
18 | #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
19 | #endif
20 | #if __has_include()
21 | #include
22 | #else
23 | #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
24 | #endif
25 |
26 | namespace margelo::nitro::nitrotext {
27 |
28 | /**
29 | * An enum which can be represented as a JavaScript union (TextDecorationStyle).
30 | */
31 | enum class TextDecorationStyle {
32 | SOLID SWIFT_NAME(solid) = 0,
33 | DOUBLE SWIFT_NAME(double) = 1,
34 | DOTTED SWIFT_NAME(dotted) = 2,
35 | DASHED SWIFT_NAME(dashed) = 3,
36 | } CLOSED_ENUM;
37 |
38 | } // namespace margelo::nitro::nitrotext
39 |
40 | namespace margelo::nitro {
41 |
42 | // C++ TextDecorationStyle <> JS TextDecorationStyle (union)
43 | template <>
44 | struct JSIConverter final {
45 | static inline margelo::nitro::nitrotext::TextDecorationStyle fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
46 | std::string unionValue = JSIConverter::fromJSI(runtime, arg);
47 | switch (hashString(unionValue.c_str(), unionValue.size())) {
48 | case hashString("solid"): return margelo::nitro::nitrotext::TextDecorationStyle::SOLID;
49 | case hashString("double"): return margelo::nitro::nitrotext::TextDecorationStyle::DOUBLE;
50 | case hashString("dotted"): return margelo::nitro::nitrotext::TextDecorationStyle::DOTTED;
51 | case hashString("dashed"): return margelo::nitro::nitrotext::TextDecorationStyle::DASHED;
52 | default: [[unlikely]]
53 | throw std::invalid_argument("Cannot convert \"" + unionValue + "\" to enum TextDecorationStyle - invalid value!");
54 | }
55 | }
56 | static inline jsi::Value toJSI(jsi::Runtime& runtime, margelo::nitro::nitrotext::TextDecorationStyle arg) {
57 | switch (arg) {
58 | case margelo::nitro::nitrotext::TextDecorationStyle::SOLID: return JSIConverter::toJSI(runtime, "solid");
59 | case margelo::nitro::nitrotext::TextDecorationStyle::DOUBLE: return JSIConverter::toJSI(runtime, "double");
60 | case margelo::nitro::nitrotext::TextDecorationStyle::DOTTED: return JSIConverter::toJSI(runtime, "dotted");
61 | case margelo::nitro::nitrotext::TextDecorationStyle::DASHED: return JSIConverter::toJSI(runtime, "dashed");
62 | default: [[unlikely]]
63 | throw std::invalid_argument("Cannot convert TextDecorationStyle to JS - invalid value: "
64 | + std::to_string(static_cast(arg)) + "!");
65 | }
66 | }
67 | static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
68 | if (!value.isString()) {
69 | return false;
70 | }
71 | std::string unionValue = JSIConverter::fromJSI(runtime, value);
72 | switch (hashString(unionValue.c_str(), unionValue.size())) {
73 | case hashString("solid"):
74 | case hashString("double"):
75 | case hashString("dotted"):
76 | case hashString("dashed"):
77 | return true;
78 | default:
79 | return false;
80 | }
81 | }
82 | };
83 |
84 | } // namespace margelo::nitro
85 |
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 | This is a new [**React Native**](https://reactnative.dev) project, bootstrapped using [`@react-native-community/cli`](https://github.com/react-native-community/cli).
2 |
3 | # Getting Started
4 |
5 | > **Note**: Make sure you have completed the [Set Up Your Environment](https://reactnative.dev/docs/set-up-your-environment) guide before proceeding.
6 |
7 | ## Step 1: Start Metro
8 |
9 | First, you will need to run **Metro**, the JavaScript build tool for React Native.
10 |
11 | To start the Metro dev server, run the following command from the root of your React Native project:
12 |
13 | ```sh
14 | # Using npm
15 | npm start
16 |
17 | # OR using Yarn
18 | yarn start
19 | ```
20 |
21 | ## Step 2: Build and run your app
22 |
23 | With Metro running, open a new terminal window/pane from the root of your React Native project, and use one of the following commands to build and run your Android or iOS app:
24 |
25 | ### Android
26 |
27 | ```sh
28 | # Using npm
29 | npm run android
30 |
31 | # OR using Yarn
32 | yarn android
33 | ```
34 |
35 | ### iOS
36 |
37 | For iOS, remember to install CocoaPods dependencies (this only needs to be run on first clone or after updating native deps).
38 |
39 | The first time you create a new project, run the Ruby bundler to install CocoaPods itself:
40 |
41 | ```sh
42 | bundle install
43 | ```
44 |
45 | Then, and every time you update your native dependencies, run:
46 |
47 | ```sh
48 | bundle exec pod install
49 | ```
50 |
51 | For more information, please visit [CocoaPods Getting Started guide](https://guides.cocoapods.org/using/getting-started.html).
52 |
53 | ```sh
54 | # Using npm
55 | npm run ios
56 |
57 | # OR using Yarn
58 | yarn ios
59 | ```
60 |
61 | If everything is set up correctly, you should see your new app running in the Android Emulator, iOS Simulator, or your connected device.
62 |
63 | This is one way to run your app — you can also build it directly from Android Studio or Xcode.
64 |
65 | ## Step 3: Modify your app
66 |
67 | Now that you have successfully run the app, let's make changes!
68 |
69 | Open `App.tsx` in your text editor of choice and make some changes. When you save, your app will automatically update and reflect these changes — this is powered by [Fast Refresh](https://reactnative.dev/docs/fast-refresh).
70 |
71 | When you want to forcefully reload, for example to reset the state of your app, you can perform a full reload:
72 |
73 | - **Android**: Press the R key twice or select **"Reload"** from the **Dev Menu**, accessed via Ctrl + M (Windows/Linux) or Cmd ⌘ + M (macOS).
74 | - **iOS**: Press R in iOS Simulator.
75 |
76 | ## Congratulations! :tada:
77 |
78 | You've successfully run and modified your React Native App. :partying_face:
79 |
80 | ### Now what?
81 |
82 | - If you want to add this new React Native code to an existing application, check out the [Integration guide](https://reactnative.dev/docs/integration-with-existing-apps).
83 | - If you're curious to learn more about React Native, check out the [docs](https://reactnative.dev/docs/getting-started).
84 |
85 | # Troubleshooting
86 |
87 | If you're having issues getting the above steps to work, see the [Troubleshooting](https://reactnative.dev/docs/troubleshooting) page.
88 |
89 | # Learn More
90 |
91 | To learn more about React Native, take a look at the following resources:
92 |
93 | - [React Native Website](https://reactnative.dev) - learn more about React Native.
94 | - [Getting Started](https://reactnative.dev/docs/environment-setup) - an **overview** of React Native and how setup your environment.
95 | - [Learn the Basics](https://reactnative.dev/docs/getting-started) - a **guided tour** of the React Native **basics**.
96 | - [Blog](https://reactnative.dev/blog) - read the latest official React Native **Blog** posts.
97 | - [`@facebook/react-native`](https://github.com/facebook/react-native) - the Open Source; GitHub **repository** for React Native.
98 |
--------------------------------------------------------------------------------
/nitrogen/generated/shared/c++/LineBreakStrategyIOS.hpp:
--------------------------------------------------------------------------------
1 | ///
2 | /// LineBreakStrategyIOS.hpp
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | #pragma once
9 |
10 | #if __has_include()
11 | #include
12 | #else
13 | #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
14 | #endif
15 | #if __has_include()
16 | #include
17 | #else
18 | #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
19 | #endif
20 | #if __has_include()
21 | #include
22 | #else
23 | #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
24 | #endif
25 |
26 | namespace margelo::nitro::nitrotext {
27 |
28 | /**
29 | * An enum which can be represented as a JavaScript union (LineBreakStrategyIOS).
30 | */
31 | enum class LineBreakStrategyIOS {
32 | NONE SWIFT_NAME(none) = 0,
33 | STANDARD SWIFT_NAME(standard) = 1,
34 | HANGUL_WORD SWIFT_NAME(hangulWord) = 2,
35 | PUSH_OUT SWIFT_NAME(pushOut) = 3,
36 | } CLOSED_ENUM;
37 |
38 | } // namespace margelo::nitro::nitrotext
39 |
40 | namespace margelo::nitro {
41 |
42 | // C++ LineBreakStrategyIOS <> JS LineBreakStrategyIOS (union)
43 | template <>
44 | struct JSIConverter final {
45 | static inline margelo::nitro::nitrotext::LineBreakStrategyIOS fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
46 | std::string unionValue = JSIConverter::fromJSI(runtime, arg);
47 | switch (hashString(unionValue.c_str(), unionValue.size())) {
48 | case hashString("none"): return margelo::nitro::nitrotext::LineBreakStrategyIOS::NONE;
49 | case hashString("standard"): return margelo::nitro::nitrotext::LineBreakStrategyIOS::STANDARD;
50 | case hashString("hangul-word"): return margelo::nitro::nitrotext::LineBreakStrategyIOS::HANGUL_WORD;
51 | case hashString("push-out"): return margelo::nitro::nitrotext::LineBreakStrategyIOS::PUSH_OUT;
52 | default: [[unlikely]]
53 | throw std::invalid_argument("Cannot convert \"" + unionValue + "\" to enum LineBreakStrategyIOS - invalid value!");
54 | }
55 | }
56 | static inline jsi::Value toJSI(jsi::Runtime& runtime, margelo::nitro::nitrotext::LineBreakStrategyIOS arg) {
57 | switch (arg) {
58 | case margelo::nitro::nitrotext::LineBreakStrategyIOS::NONE: return JSIConverter::toJSI(runtime, "none");
59 | case margelo::nitro::nitrotext::LineBreakStrategyIOS::STANDARD: return JSIConverter::toJSI(runtime, "standard");
60 | case margelo::nitro::nitrotext::LineBreakStrategyIOS::HANGUL_WORD: return JSIConverter::toJSI(runtime, "hangul-word");
61 | case margelo::nitro::nitrotext::LineBreakStrategyIOS::PUSH_OUT: return JSIConverter::toJSI(runtime, "push-out");
62 | default: [[unlikely]]
63 | throw std::invalid_argument("Cannot convert LineBreakStrategyIOS to JS - invalid value: "
64 | + std::to_string(static_cast(arg)) + "!");
65 | }
66 | }
67 | static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
68 | if (!value.isString()) {
69 | return false;
70 | }
71 | std::string unionValue = JSIConverter::fromJSI(runtime, value);
72 | switch (hashString(unionValue.c_str(), unionValue.size())) {
73 | case hashString("none"):
74 | case hashString("standard"):
75 | case hashString("hangul-word"):
76 | case hashString("push-out"):
77 | return true;
78 | default:
79 | return false;
80 | }
81 | }
82 | };
83 |
84 | } // namespace margelo::nitro
85 |
--------------------------------------------------------------------------------
/nitrogen/generated/shared/c++/TextAlign.hpp:
--------------------------------------------------------------------------------
1 | ///
2 | /// TextAlign.hpp
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | #pragma once
9 |
10 | #if __has_include()
11 | #include
12 | #else
13 | #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
14 | #endif
15 | #if __has_include()
16 | #include
17 | #else
18 | #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
19 | #endif
20 | #if __has_include()
21 | #include
22 | #else
23 | #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
24 | #endif
25 |
26 | namespace margelo::nitro::nitrotext {
27 |
28 | /**
29 | * An enum which can be represented as a JavaScript union (TextAlign).
30 | */
31 | enum class TextAlign {
32 | AUTO SWIFT_NAME(auto) = 0,
33 | LEFT SWIFT_NAME(left) = 1,
34 | RIGHT SWIFT_NAME(right) = 2,
35 | CENTER SWIFT_NAME(center) = 3,
36 | JUSTIFY SWIFT_NAME(justify) = 4,
37 | } CLOSED_ENUM;
38 |
39 | } // namespace margelo::nitro::nitrotext
40 |
41 | namespace margelo::nitro {
42 |
43 | // C++ TextAlign <> JS TextAlign (union)
44 | template <>
45 | struct JSIConverter final {
46 | static inline margelo::nitro::nitrotext::TextAlign fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
47 | std::string unionValue = JSIConverter::fromJSI(runtime, arg);
48 | switch (hashString(unionValue.c_str(), unionValue.size())) {
49 | case hashString("auto"): return margelo::nitro::nitrotext::TextAlign::AUTO;
50 | case hashString("left"): return margelo::nitro::nitrotext::TextAlign::LEFT;
51 | case hashString("right"): return margelo::nitro::nitrotext::TextAlign::RIGHT;
52 | case hashString("center"): return margelo::nitro::nitrotext::TextAlign::CENTER;
53 | case hashString("justify"): return margelo::nitro::nitrotext::TextAlign::JUSTIFY;
54 | default: [[unlikely]]
55 | throw std::invalid_argument("Cannot convert \"" + unionValue + "\" to enum TextAlign - invalid value!");
56 | }
57 | }
58 | static inline jsi::Value toJSI(jsi::Runtime& runtime, margelo::nitro::nitrotext::TextAlign arg) {
59 | switch (arg) {
60 | case margelo::nitro::nitrotext::TextAlign::AUTO: return JSIConverter::toJSI(runtime, "auto");
61 | case margelo::nitro::nitrotext::TextAlign::LEFT: return JSIConverter::toJSI(runtime, "left");
62 | case margelo::nitro::nitrotext::TextAlign::RIGHT: return JSIConverter::toJSI(runtime, "right");
63 | case margelo::nitro::nitrotext::TextAlign::CENTER: return JSIConverter::toJSI(runtime, "center");
64 | case margelo::nitro::nitrotext::TextAlign::JUSTIFY: return JSIConverter::toJSI(runtime, "justify");
65 | default: [[unlikely]]
66 | throw std::invalid_argument("Cannot convert TextAlign to JS - invalid value: "
67 | + std::to_string(static_cast(arg)) + "!");
68 | }
69 | }
70 | static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
71 | if (!value.isString()) {
72 | return false;
73 | }
74 | std::string unionValue = JSIConverter::fromJSI(runtime, value);
75 | switch (hashString(unionValue.c_str(), unionValue.size())) {
76 | case hashString("auto"):
77 | case hashString("left"):
78 | case hashString("right"):
79 | case hashString("center"):
80 | case hashString("justify"):
81 | return true;
82 | default:
83 | return false;
84 | }
85 | }
86 | };
87 |
88 | } // namespace margelo::nitro
89 |
--------------------------------------------------------------------------------
/nitrogen/generated/shared/c++/TextDecorationLine.hpp:
--------------------------------------------------------------------------------
1 | ///
2 | /// TextDecorationLine.hpp
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | #pragma once
9 |
10 | #if __has_include()
11 | #include
12 | #else
13 | #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
14 | #endif
15 | #if __has_include()
16 | #include
17 | #else
18 | #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
19 | #endif
20 | #if __has_include()
21 | #include
22 | #else
23 | #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
24 | #endif
25 |
26 | namespace margelo::nitro::nitrotext {
27 |
28 | /**
29 | * An enum which can be represented as a JavaScript union (TextDecorationLine).
30 | */
31 | enum class TextDecorationLine {
32 | NONE SWIFT_NAME(none) = 0,
33 | UNDERLINE SWIFT_NAME(underline) = 1,
34 | LINE_THROUGH SWIFT_NAME(lineThrough) = 2,
35 | UNDERLINE_LINE_THROUGH SWIFT_NAME(underlineLineThrough) = 3,
36 | } CLOSED_ENUM;
37 |
38 | } // namespace margelo::nitro::nitrotext
39 |
40 | namespace margelo::nitro {
41 |
42 | // C++ TextDecorationLine <> JS TextDecorationLine (union)
43 | template <>
44 | struct JSIConverter final {
45 | static inline margelo::nitro::nitrotext::TextDecorationLine fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
46 | std::string unionValue = JSIConverter::fromJSI(runtime, arg);
47 | switch (hashString(unionValue.c_str(), unionValue.size())) {
48 | case hashString("none"): return margelo::nitro::nitrotext::TextDecorationLine::NONE;
49 | case hashString("underline"): return margelo::nitro::nitrotext::TextDecorationLine::UNDERLINE;
50 | case hashString("line-through"): return margelo::nitro::nitrotext::TextDecorationLine::LINE_THROUGH;
51 | case hashString("underline line-through"): return margelo::nitro::nitrotext::TextDecorationLine::UNDERLINE_LINE_THROUGH;
52 | default: [[unlikely]]
53 | throw std::invalid_argument("Cannot convert \"" + unionValue + "\" to enum TextDecorationLine - invalid value!");
54 | }
55 | }
56 | static inline jsi::Value toJSI(jsi::Runtime& runtime, margelo::nitro::nitrotext::TextDecorationLine arg) {
57 | switch (arg) {
58 | case margelo::nitro::nitrotext::TextDecorationLine::NONE: return JSIConverter::toJSI(runtime, "none");
59 | case margelo::nitro::nitrotext::TextDecorationLine::UNDERLINE: return JSIConverter::toJSI(runtime, "underline");
60 | case margelo::nitro::nitrotext::TextDecorationLine::LINE_THROUGH: return JSIConverter::toJSI(runtime, "line-through");
61 | case margelo::nitro::nitrotext::TextDecorationLine::UNDERLINE_LINE_THROUGH: return JSIConverter::toJSI(runtime, "underline line-through");
62 | default: [[unlikely]]
63 | throw std::invalid_argument("Cannot convert TextDecorationLine to JS - invalid value: "
64 | + std::to_string(static_cast(arg)) + "!");
65 | }
66 | }
67 | static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
68 | if (!value.isString()) {
69 | return false;
70 | }
71 | std::string unionValue = JSIConverter::fromJSI(runtime, value);
72 | switch (hashString(unionValue.c_str(), unionValue.size())) {
73 | case hashString("none"):
74 | case hashString("underline"):
75 | case hashString("line-through"):
76 | case hashString("underline line-through"):
77 | return true;
78 | default:
79 | return false;
80 | }
81 | }
82 | };
83 |
84 | } // namespace margelo::nitro
85 |
--------------------------------------------------------------------------------
/src/types.ts:
--------------------------------------------------------------------------------
1 | type TextAlign = 'auto' | 'left' | 'right' | 'center' | 'justify'
2 | type TextTransform = 'none' | 'uppercase' | 'lowercase' | 'capitalize'
3 | export type EllipsizeMode = 'head' | 'middle' | 'tail' | 'clip'
4 | export type LineBreakStrategyIOS =
5 | | 'none'
6 | | 'standard'
7 | | 'hangul-word'
8 | | 'push-out'
9 | export type DynamicTypeRamp =
10 | | 'caption2'
11 | | 'caption1'
12 | | 'footnote'
13 | | 'subheadline'
14 | | 'callout'
15 | | 'body'
16 | | 'headline'
17 | | 'title3'
18 | | 'title2'
19 | | 'title1'
20 | | 'largeTitle'
21 |
22 | // '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900' - Nitro does not support these
23 | type FontWeight =
24 | | 'normal'
25 | | 'bold'
26 | | 'ultralight'
27 | | 'thin'
28 | | 'light'
29 | | 'medium'
30 | | 'regular'
31 | | 'semibold'
32 | | 'condensedBold'
33 | | 'condensed'
34 | | 'heavy'
35 | | 'black'
36 | type FontStyle = 'normal' | 'italic' | 'oblique'
37 |
38 | type TextDecorationLine =
39 | | 'none'
40 | | 'underline'
41 | | 'line-through'
42 | | 'underline line-through'
43 | type TextDecorationStyle = 'solid' | 'double' | 'dotted' | 'dashed'
44 |
45 | export type TextLayout = {
46 | text: string
47 | x: number
48 | y: number
49 | width: number
50 | height: number
51 | descender: number
52 | capHeight: number
53 | ascender: number
54 | xHeight: number
55 | }
56 |
57 | export type TextLayoutEvent = {
58 | lines: Array
59 | }
60 |
61 | export type Fragment = {
62 | /**
63 | * The text of the text.
64 | */
65 | text?: string
66 |
67 | /**
68 | * iOS: Color for text selection highlight/caret.
69 | */
70 | selectionColor?: string
71 |
72 | /**
73 | * The font size of the text.
74 | */
75 | fontSize?: number
76 |
77 | /**
78 | * The font weight of the text.
79 | */
80 | fontWeight?: FontWeight
81 |
82 | /**
83 | * The font color of the text.
84 | */
85 | fontColor?: string
86 |
87 | /**
88 | * Background highlight behind this text fragment.
89 | * Mirrors React Native Text's `backgroundColor` when applied to nested runs.
90 | * Named differently to avoid clashing with view style `backgroundColor`.
91 | */
92 | fragmentBackgroundColor?: string
93 |
94 | /**
95 | * The font style of the text (italic, normal).
96 | */
97 | fontStyle?: FontStyle
98 |
99 | /**
100 | * Custom font family for this fragment.
101 | * Note: Currently applied as a top-level font on iOS NitroText.
102 | */
103 | fontFamily?: string
104 |
105 | /**
106 | * The line height of the text.
107 | */
108 | lineHeight?: number
109 |
110 | /**
111 | * Additional space between letters (kerning), in points.
112 | * Matches React Native Text's `letterSpacing` on iOS.
113 | */
114 | letterSpacing?: number
115 |
116 | /**
117 | * Horizontal text alignment applied to the whole block.
118 | */
119 | textAlign?: TextAlign
120 | /**
121 | * Applies text transform to the content.
122 | */
123 | textTransform?: TextTransform
124 |
125 | /**
126 | * Text decoration for underline/strikethrough.
127 | * Mirrors RN Text's `textDecorationLine`.
128 | */
129 | textDecorationLine?: TextDecorationLine
130 |
131 | /**
132 | * Text decoration color.
133 | */
134 | textDecorationColor?: string
135 |
136 | /**
137 | * Text decoration style (solid, double, dotted, dashed).
138 | */
139 | textDecorationStyle?: TextDecorationStyle
140 |
141 | /**
142 | * Link URL (href attribute from tag).
143 | * When present, this fragment represents clickable link text.
144 | */
145 | linkUrl?: string
146 | }
147 |
148 | /**
149 | * A menu item for the selection menu.
150 | */
151 | export type MenuItem = {
152 | title: string
153 | action: () => void
154 | }
155 |
156 | /**
157 | * Supported renderers for rich text parsing.
158 | */
159 | export type Renderer = 'html' | 'plaintext'
160 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-nitro-text",
3 | "version": "1.1.0",
4 | "description": "A Text component that is much richer and performant for both iOS and Android.",
5 | "main": "./lib/commonjs/index.js",
6 | "module": "./lib/module/index.js",
7 | "types": "./lib/typescript/src/index.d.ts",
8 | "react-native": "src/index",
9 | "source": "src/index",
10 | "scripts": {
11 | "typecheck": "tsc --noEmit",
12 | "lint": "eslint src",
13 | "lint:fix": "eslint src --fix",
14 | "format": "prettier --write \"src/**/*.{js,jsx,ts,tsx,json,md}\"",
15 | "format:check": "prettier --check \"src/**/*.{js,jsx,ts,tsx,json,md}\"",
16 | "clean": "git clean -dfX",
17 | "release": "semantic-release",
18 | "build": "bun run typecheck && bob build",
19 | "codegen": "nitrogen --logLevel=\"debug\" && bun run build",
20 | "postcodegen": "bun --cwd example pod",
21 | "test": "jest"
22 | },
23 | "keywords": [
24 | "react-native",
25 | "react-native-nitro-text"
26 | ],
27 | "files": [
28 | "src",
29 | "react-native.config.js",
30 | "lib",
31 | "nitrogen",
32 | "cpp",
33 | "nitro.json",
34 | "android/build.gradle",
35 | "android/fix-prefab.gradle",
36 | "android/gradle.properties",
37 | "android/CMakeLists.txt",
38 | "android/src",
39 | "ios/**/*.h",
40 | "ios/**/*.hpp",
41 | "ios/**/*.m",
42 | "ios/**/*.mm",
43 | "ios/**/*.cpp",
44 | "ios/**/*.swift",
45 | "app.plugin.js",
46 | "*.podspec",
47 | "README.md"
48 | ],
49 | "workspaces": [
50 | "example"
51 | ],
52 | "repository": "https://github.com/patrickkabwe/react-native-nitro-text.git",
53 | "author": "Patrick Kabwe",
54 | "license": "MIT",
55 | "bugs": "https://github.com/patrickkabwe/react-native-nitro-text/issues",
56 | "homepage": "https://github.com/patrickkabwe/react-native-nitro-text#readme",
57 | "publishConfig": {
58 | "access": "public",
59 | "registry": "https://registry.npmjs.org/"
60 | },
61 | "devDependencies": {
62 | "@eslint/js": "^9.38.0",
63 | "@jamesacarr/eslint-formatter-github-actions": "^0.2.0",
64 | "@react-native/eslint-config": "0.81.4",
65 | "@semantic-release/changelog": "^6.0.3",
66 | "@semantic-release/git": "^10.0.1",
67 | "@types/jest": "^29.5.12",
68 | "@types/react": "19.1.0",
69 | "@typescript-eslint/eslint-plugin": "^8.46.2",
70 | "@typescript-eslint/parser": "^8.46.2",
71 | "conventional-changelog-conventionalcommits": "^9.1.0",
72 | "eslint": "^9.38.0",
73 | "eslint-config-prettier": "^10.1.8",
74 | "eslint-plugin-prettier": "^5.5.4",
75 | "eslint-plugin-react": "^7.37.5",
76 | "eslint-plugin-react-hooks": "^7.0.1",
77 | "eslint-plugin-react-native": "^5.0.0",
78 | "nitrogen": "^0.31.9",
79 | "prettier": "^3.6.2",
80 | "react": "19.1.0",
81 | "react-native": "0.81.4",
82 | "react-native-builder-bob": "^0.37.0",
83 | "react-native-nitro-modules": "^0.31.9",
84 | "semantic-release": "^24.2.8",
85 | "typescript": "^5.8.3",
86 | "typescript-eslint": "^8.46.2",
87 | "jest": "^29.7.0"
88 | },
89 | "peerDependencies": {
90 | "react": "*",
91 | "react-native": "*",
92 | "react-native-nitro-modules": "*"
93 | },
94 | "prettier": {
95 | "quoteProps": "consistent",
96 | "singleQuote": true,
97 | "tabWidth": 3,
98 | "trailingComma": "es5",
99 | "useTabs": false,
100 | "semi": false
101 | },
102 | "react-native-builder-bob": {
103 | "source": "src",
104 | "output": "lib",
105 | "targets": [
106 | "commonjs",
107 | "module",
108 | [
109 | "typescript",
110 | {
111 | "project": "tsconfig.json"
112 | }
113 | ]
114 | ]
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/nitrogen/generated/ios/NitroText-Swift-Cxx-Umbrella.hpp:
--------------------------------------------------------------------------------
1 | ///
2 | /// NitroText-Swift-Cxx-Umbrella.hpp
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | #pragma once
9 |
10 | // Forward declarations of C++ defined types
11 | // Forward declaration of `DynamicTypeRamp` to properly resolve imports.
12 | namespace margelo::nitro::nitrotext { enum class DynamicTypeRamp; }
13 | // Forward declaration of `EllipsizeMode` to properly resolve imports.
14 | namespace margelo::nitro::nitrotext { enum class EllipsizeMode; }
15 | // Forward declaration of `FontStyle` to properly resolve imports.
16 | namespace margelo::nitro::nitrotext { enum class FontStyle; }
17 | // Forward declaration of `FontWeight` to properly resolve imports.
18 | namespace margelo::nitro::nitrotext { enum class FontWeight; }
19 | // Forward declaration of `Fragment` to properly resolve imports.
20 | namespace margelo::nitro::nitrotext { struct Fragment; }
21 | // Forward declaration of `HybridNitroTextSpec` to properly resolve imports.
22 | namespace margelo::nitro::nitrotext { class HybridNitroTextSpec; }
23 | // Forward declaration of `LineBreakStrategyIOS` to properly resolve imports.
24 | namespace margelo::nitro::nitrotext { enum class LineBreakStrategyIOS; }
25 | // Forward declaration of `MenuItem` to properly resolve imports.
26 | namespace margelo::nitro::nitrotext { struct MenuItem; }
27 | // Forward declaration of `Renderer` to properly resolve imports.
28 | namespace margelo::nitro::nitrotext { enum class Renderer; }
29 | // Forward declaration of `TextAlign` to properly resolve imports.
30 | namespace margelo::nitro::nitrotext { enum class TextAlign; }
31 | // Forward declaration of `TextDecorationLine` to properly resolve imports.
32 | namespace margelo::nitro::nitrotext { enum class TextDecorationLine; }
33 | // Forward declaration of `TextDecorationStyle` to properly resolve imports.
34 | namespace margelo::nitro::nitrotext { enum class TextDecorationStyle; }
35 | // Forward declaration of `TextLayoutEvent` to properly resolve imports.
36 | namespace margelo::nitro::nitrotext { struct TextLayoutEvent; }
37 | // Forward declaration of `TextLayout` to properly resolve imports.
38 | namespace margelo::nitro::nitrotext { struct TextLayout; }
39 | // Forward declaration of `TextTransform` to properly resolve imports.
40 | namespace margelo::nitro::nitrotext { enum class TextTransform; }
41 |
42 | // Include C++ defined types
43 | #include "DynamicTypeRamp.hpp"
44 | #include "EllipsizeMode.hpp"
45 | #include "FontStyle.hpp"
46 | #include "FontWeight.hpp"
47 | #include "Fragment.hpp"
48 | #include "HybridNitroTextSpec.hpp"
49 | #include "LineBreakStrategyIOS.hpp"
50 | #include "MenuItem.hpp"
51 | #include "Renderer.hpp"
52 | #include "TextAlign.hpp"
53 | #include "TextDecorationLine.hpp"
54 | #include "TextDecorationStyle.hpp"
55 | #include "TextLayout.hpp"
56 | #include "TextLayoutEvent.hpp"
57 | #include "TextTransform.hpp"
58 | #include
59 | #include
60 | #include
61 | #include
62 | #include
63 |
64 | // C++ helpers for Swift
65 | #include "NitroText-Swift-Cxx-Bridge.hpp"
66 |
67 | // Common C++ types used in Swift
68 | #include
69 | #include
70 | #include
71 | #include
72 |
73 | // Forward declarations of Swift defined types
74 | // Forward declaration of `HybridNitroTextSpec_cxx` to properly resolve imports.
75 | namespace NitroText { class HybridNitroTextSpec_cxx; }
76 |
77 | // Include Swift defined types
78 | #if __has_include("NitroText-Swift.h")
79 | // This header is generated by Xcode/Swift on every app build.
80 | // If it cannot be found, make sure the Swift module's name (= podspec name) is actually "NitroText".
81 | #include "NitroText-Swift.h"
82 | // Same as above, but used when building with frameworks (`use_frameworks`)
83 | #elif __has_include()
84 | #include
85 | #else
86 | #error NitroText's autogenerated Swift header cannot be found! Make sure the Swift module's name (= podspec name) is actually "NitroText", and try building the app first.
87 | #endif
88 |
--------------------------------------------------------------------------------
/example/ios/NitroTextExample/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
24 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/src/specs/nitro-text.nitro.ts:
--------------------------------------------------------------------------------
1 | import type {
2 | HybridView,
3 | HybridViewMethods,
4 | HybridViewProps,
5 | } from 'react-native-nitro-modules'
6 | import type {
7 | DynamicTypeRamp,
8 | EllipsizeMode,
9 | Fragment,
10 | LineBreakStrategyIOS,
11 | TextLayoutEvent,
12 | MenuItem,
13 | Renderer,
14 | } from '../types'
15 |
16 | export interface NitroTextProps
17 | extends HybridViewProps,
18 | Omit {
19 | /**
20 | * The fragments of the text.
21 | */
22 | fragments?: Fragment[]
23 |
24 | /**
25 | * Renderer for parsing rich text content from string children.
26 | * When specified, the string children are parsed by a purpose-built, zero-allocation parser:
27 | * - 'html': Parses HTML tags, inline CSS styles, `
51 | *
52 | *
53 | * Bold and red from stylesheet
54 | * Title text
55 | *
56 | *