capacitor-sora-editor 0.0.2 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/android/build.gradle +32 -32
  2. package/android/src/main/AndroidManifest.xml +6 -9
  3. package/android/src/main/java/com/abc15018045126/capacitor/soraeditor/SoraEditorPlugin.kt +227 -0
  4. package/android/src/main/java/com/{github/soraeditor/capacitor/EditorActivity.kt → abc15018045126/capacitor/soraeditor/compose/ComposeEditorActivity.kt} +8 -7
  5. package/android/src/main/java/com/{github/soraeditor/capacitor → abc15018045126/capacitor/soraeditor/compose}/EditorViewModel.kt +15 -4
  6. package/android/src/main/java/com/{github/soraeditor/capacitor → abc15018045126/capacitor/soraeditor/compose}/ui/EditorScreen.kt +17 -5
  7. package/android/src/main/java/com/abc15018045126/capacitor/soraeditor/compose/ui/SoraEditorWrapper.kt +104 -0
  8. package/android/src/main/java/com/{github/soraeditor/capacitor → abc15018045126/capacitor/soraeditor/compose}/ui/theme/Theme.kt +1 -1
  9. package/capacitor-sora-editor-1.0.0.tgz +0 -0
  10. package/dist/esm/definitions.d.ts +39 -0
  11. package/dist/esm/definitions.d.ts.map +1 -0
  12. package/dist/esm/index.d.ts +1 -0
  13. package/dist/esm/index.d.ts.map +1 -0
  14. package/dist/esm/index.js.map +1 -1
  15. package/dist/esm/web.d.ts +21 -1
  16. package/dist/esm/web.d.ts.map +1 -0
  17. package/dist/esm/web.js +25 -2
  18. package/dist/esm/web.js.map +1 -1
  19. package/dist/plugin.cjs.js +25 -2
  20. package/dist/plugin.cjs.js.map +1 -1
  21. package/dist/plugin.js +25 -2
  22. package/dist/plugin.js.map +1 -1
  23. package/package.json +11 -60
  24. package/rollup.config.mjs +22 -0
  25. package/src/definitions.ts +32 -0
  26. package/src/index.ts +9 -0
  27. package/src/web.ts +32 -0
  28. package/tsconfig.json +26 -0
  29. package/README.md +0 -110
  30. package/android/src/main/java/com/github/soraeditor/capacitor/SoraEditorPlugin.kt +0 -33
  31. package/android/src/main/res/values/styles.xml +0 -6
@@ -1,12 +1,18 @@
1
+ ext {
2
+ compileSdkVersion = project.hasProperty('compileSdkVersion') ? project.compileSdkVersion : 35
3
+ minSdkVersion = project.hasProperty('minSdkVersion') ? project.minSdkVersion : 26
4
+ targetSdkVersion = project.hasProperty('targetSdkVersion') ? project.targetSdkVersion : 35
5
+ }
6
+
1
7
  buildscript {
2
8
  repositories {
3
9
  google()
4
10
  mavenCentral()
5
11
  }
6
12
  dependencies {
7
- classpath 'com.android.tools.build:gradle:8.2.1'
8
- classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.22'
9
- classpath 'org.jetbrains.kotlin:compose-compiler-gradle-plugin:2.0.0'
13
+ classpath 'com.android.tools.build:gradle:8.7.2'
14
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:2.1.0"
15
+ classpath "org.jetbrains.kotlin:compose-compiler-gradle-plugin:2.1.0"
10
16
  }
11
17
  }
12
18
 
@@ -15,31 +21,30 @@ apply plugin: 'kotlin-android'
15
21
  apply plugin: 'org.jetbrains.kotlin.plugin.compose'
16
22
 
17
23
  android {
18
- namespace "com.github.soraeditor.capacitor"
19
- compileSdk 34
24
+ namespace "com.abc15018045126.capacitor.soraeditor"
25
+ compileSdk project.ext.compileSdkVersion
20
26
 
21
27
  defaultConfig {
22
- minSdkVersion 22
23
- targetSdkVersion 34
24
- versionCode 1
25
- versionName "1.0"
26
- testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
27
- consumerProguardFiles "consumer-rules.pro"
28
+ minSdk project.ext.minSdkVersion
29
+ targetSdk project.ext.targetSdkVersion
28
30
  }
29
31
 
30
- buildTypes {
31
- release {
32
- minifyEnabled false
33
- proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
34
- }
35
- }
36
32
  compileOptions {
37
33
  sourceCompatibility JavaVersion.VERSION_21
38
34
  targetCompatibility JavaVersion.VERSION_21
39
35
  }
36
+
40
37
  kotlinOptions {
41
38
  jvmTarget = '21'
42
39
  }
40
+
41
+ buildTypes {
42
+ release {
43
+ minifyEnabled false
44
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
45
+ }
46
+ }
47
+
43
48
  buildFeatures {
44
49
  compose true
45
50
  }
@@ -53,25 +58,20 @@ repositories {
53
58
  dependencies {
54
59
  implementation fileTree(dir: 'libs', include: ['*.jar'])
55
60
  implementation project(':capacitor-android')
56
- implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
57
- implementation "androidx.core:core-ktx:1.12.0"
61
+ implementation "androidx.appcompat:appcompat:1.7.0"
62
+ implementation "androidx.core:core-ktx:1.15.0"
58
63
 
59
- // Sora Editor
60
- implementation 'io.github.Rosemoe.sora-editor:editor:0.24.4'
61
- implementation 'io.github.Rosemoe.sora-editor:language-textmate:0.24.4'
64
+ // Correct Maven coordinates for Sora Editor
65
+ implementation 'io.github.rosemoe:editor:0.24.4'
66
+ implementation 'io.github.rosemoe:language-textmate:0.24.4'
62
67
 
63
- // Jetpack Compose
64
- def composeBom = platform('androidx.compose:compose-bom:2024.02.00')
68
+ // Compose
69
+ def composeBom = platform('androidx.compose:compose-bom:2024.12.01')
65
70
  implementation composeBom
66
71
  implementation 'androidx.compose.ui:ui'
67
72
  implementation 'androidx.compose.material3:material3'
68
73
  implementation 'androidx.compose.material:material-icons-extended'
69
- implementation 'androidx.activity:activity-compose:1.8.2'
70
- implementation 'androidx.compose.ui:ui-tooling-preview'
71
- implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.7.0'
72
- implementation 'androidx.lifecycle:lifecycle-runtime-compose:2.7.0'
73
-
74
- testImplementation "junit:junit:4.13.2"
75
- androidTestImplementation "androidx.test.ext:junit:1.1.5"
76
- androidTestImplementation "androidx.test.espresso:espresso-core:3.5.1"
74
+ implementation 'androidx.activity:activity-compose:1.10.0'
75
+ implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.8.7'
76
+ implementation 'androidx.lifecycle:lifecycle-runtime-compose:2.8.7'
77
77
  }
@@ -1,13 +1,10 @@
1
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
2
- package="com.github.soraeditor.capacitor">
3
-
4
- <!-- Permissions can be added here if needed, e.g. READ_EXTERNAL_STORAGE -->
5
-
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
6
3
  <application>
7
4
  <activity
8
- android:name=".EditorActivity"
9
- android:theme="@style/Theme.AppCompat.NoActionBar"
10
- android:exported="true">
11
- </activity>
5
+ android:name="com.abc15018045126.capacitor.soraeditor.compose.ComposeEditorActivity"
6
+ android:theme="@style/Theme.AppCompat.Light.NoActionBar"
7
+ android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density"
8
+ android:exported="false" />
12
9
  </application>
13
10
  </manifest>
@@ -0,0 +1,227 @@
1
+ package com.abc15018045126.capacitor.soraeditor
2
+
3
+ import android.graphics.Color
4
+ import android.graphics.Typeface
5
+ import android.view.View
6
+ import android.widget.FrameLayout
7
+ import com.getcapacitor.JSObject
8
+ import com.getcapacitor.Plugin
9
+ import com.getcapacitor.PluginCall
10
+ import com.getcapacitor.PluginMethod
11
+ import com.getcapacitor.annotation.CapacitorPlugin
12
+ import io.github.rosemoe.sora.widget.CodeEditor
13
+ import io.github.rosemoe.sora.widget.schemes.EditorColorScheme
14
+
15
+ @CapacitorPlugin(name = "SoraEditor")
16
+ class SoraEditorPlugin : Plugin() {
17
+
18
+ private var editor: CodeEditor? = null
19
+
20
+ @PluginMethod
21
+ fun start(call: PluginCall) {
22
+ val content = call.getString("content") ?: ""
23
+ val topMargin = call.getInt("top") ?: 0
24
+ val leftMargin = call.getInt("left") ?: 0
25
+ val rightMargin = call.getInt("right") ?: 0
26
+ val bottomMargin = call.getInt("bottom") ?: 0
27
+ val width = call.getInt("width") ?: -1 // -1 = MATCH_PARENT
28
+ val height = call.getInt("height") ?: -1
29
+ val fontSize = call.getFloat("fontSize") ?: 18f
30
+ val showLineNumbers = call.getBoolean("showLineNumbers") ?: true
31
+ val wordWrap = call.getBoolean("wordWrap") ?: false
32
+ val editable = call.getBoolean("editable") ?: true
33
+ val bgColorStr = call.getString("backgroundColor") // e.g. "#FFFFFF"
34
+
35
+ activity.runOnUiThread {
36
+ if (editor == null) {
37
+ editor = CodeEditor(context).apply {
38
+ setTextSize(fontSize)
39
+ setTypefaceText(Typeface.MONOSPACE)
40
+ isLineNumberEnabled = showLineNumbers
41
+ isWordwrap = wordWrap
42
+ setEditable(editable)
43
+
44
+ var startX = 0f
45
+ var startY = 0f
46
+ var startTime = 0L
47
+
48
+ setOnTouchListener { _, event ->
49
+ when (event.action) {
50
+ android.view.MotionEvent.ACTION_DOWN -> {
51
+ startX = event.x
52
+ startY = event.y
53
+ startTime = System.currentTimeMillis()
54
+ }
55
+ android.view.MotionEvent.ACTION_UP -> {
56
+ val duration = System.currentTimeMillis() - startTime
57
+ val dist = Math.sqrt(Math.pow((event.x - startX).toDouble(), 2.0) + Math.pow((event.y - startY).toDouble(), 2.0))
58
+ if (duration < 300 && dist < 20) {
59
+ notifyListeners("onEditorClick", JSObject())
60
+ }
61
+ }
62
+ }
63
+ false // Allow editor to handle the event too
64
+ }
65
+ subscribeEvent(io.github.rosemoe.sora.event.ContentChangeEvent::class.java) { event, _ ->
66
+ notifyListeners("onContentChange", JSObject())
67
+ }
68
+ }
69
+
70
+ val params = FrameLayout.LayoutParams(
71
+ FrameLayout.LayoutParams.MATCH_PARENT,
72
+ FrameLayout.LayoutParams.MATCH_PARENT
73
+ )
74
+ activity.addContentView(editor, params)
75
+ }
76
+
77
+ editor!!.isLineNumberEnabled = showLineNumbers
78
+ editor!!.isWordwrap = wordWrap
79
+ editor!!.setEditable(editable)
80
+
81
+ if (bgColorStr != null) {
82
+ try {
83
+ val color = Color.parseColor(bgColorStr)
84
+ val r = Color.red(color)
85
+ val g = Color.green(color)
86
+ val b = Color.blue(color)
87
+ val luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255.0
88
+
89
+ editor!!.colorScheme.setColor(EditorColorScheme.WHOLE_BACKGROUND, color)
90
+ editor!!.colorScheme.setColor(EditorColorScheme.LINE_NUMBER_BACKGROUND, color)
91
+
92
+ if (luminance < 0.5) {
93
+ editor!!.colorScheme.setColor(EditorColorScheme.TEXT_NORMAL, Color.WHITE)
94
+ editor!!.colorScheme.setColor(EditorColorScheme.LINE_NUMBER, Color.GRAY)
95
+ } else {
96
+ editor!!.colorScheme.setColor(EditorColorScheme.TEXT_NORMAL, Color.BLACK)
97
+ editor!!.colorScheme.setColor(EditorColorScheme.LINE_NUMBER, Color.DKGRAY)
98
+ }
99
+ } catch (e: Exception) {}
100
+ }
101
+
102
+ val density = context.resources.displayMetrics.density
103
+ val params = editor!!.layoutParams as FrameLayout.LayoutParams
104
+ params.topMargin = (topMargin * density).toInt()
105
+ params.leftMargin = (leftMargin * density).toInt()
106
+ params.rightMargin = (rightMargin * density).toInt()
107
+ params.bottomMargin = (bottomMargin * density).toInt()
108
+ if (width >= 0) params.width = (width * density).toInt() else params.width = FrameLayout.LayoutParams.MATCH_PARENT
109
+ if (height >= 0) params.height = (height * density).toInt() else params.height = FrameLayout.LayoutParams.MATCH_PARENT
110
+
111
+ editor!!.layoutParams = params
112
+
113
+ if (call.hasOption("content")) {
114
+ val currentText = editor!!.text.toString()
115
+ if (currentText != content) {
116
+ editor!!.setText(content)
117
+ }
118
+ }
119
+
120
+ editor!!.setTextSize(fontSize)
121
+ editor!!.visibility = View.VISIBLE
122
+ editor!!.bringToFront()
123
+
124
+ if (call.hasOption("selectionLine") || call.hasOption("selectionColumn")) {
125
+ val l = call.getInt("selectionLine") ?: 0
126
+ val c = call.getInt("selectionColumn") ?: 0
127
+ editor!!.postDelayed({
128
+ try {
129
+ editor!!.setSelection(l, c)
130
+ editor!!.ensureSelectionVisible()
131
+ } catch(e: Exception){
132
+ }
133
+ }, 150)
134
+ }
135
+ }
136
+ call.resolve()
137
+ }
138
+
139
+ @PluginMethod
140
+ fun close(call: PluginCall) {
141
+ activity.runOnUiThread {
142
+ editor?.visibility = View.GONE
143
+ }
144
+ call.resolve()
145
+ }
146
+
147
+ @PluginMethod
148
+ fun getText(call: PluginCall) {
149
+ val ret = JSObject()
150
+ activity.runOnUiThread {
151
+ ret.put("content", editor?.text?.toString() ?: "")
152
+ call.resolve(ret)
153
+ }
154
+ }
155
+
156
+ @PluginMethod
157
+ fun getSelection(call: PluginCall) {
158
+ val ret = JSObject()
159
+ activity.runOnUiThread {
160
+ if (editor != null) {
161
+ val cursor = editor!!.cursor
162
+ ret.put("line", cursor.leftLine)
163
+ ret.put("column", cursor.leftColumn)
164
+ }
165
+ call.resolve(ret)
166
+ }
167
+ }
168
+
169
+ @PluginMethod
170
+ fun setText(call: PluginCall) {
171
+ val content = call.getString("content") ?: ""
172
+ activity.runOnUiThread {
173
+ editor?.setText(content)
174
+ }
175
+ call.resolve()
176
+ }
177
+
178
+ @PluginMethod
179
+ fun setSelection(call: PluginCall) {
180
+ val line = call.getInt("line") ?: 0
181
+ val column = call.getInt("column") ?: 0
182
+ activity.runOnUiThread {
183
+ editor?.post {
184
+ try {
185
+ editor?.setSelection(line, column)
186
+ editor?.ensureSelectionVisible()
187
+ } catch (e: Exception) {
188
+ e.printStackTrace()
189
+ }
190
+ }
191
+ }
192
+ call.resolve()
193
+ }
194
+
195
+ @PluginMethod
196
+ fun undo(call: PluginCall) {
197
+ activity.runOnUiThread {
198
+ editor?.undo()
199
+ }
200
+ call.resolve()
201
+ }
202
+
203
+ @PluginMethod
204
+ fun redo(call: PluginCall) {
205
+ activity.runOnUiThread {
206
+ editor?.redo()
207
+ }
208
+ call.resolve()
209
+ }
210
+
211
+ @PluginMethod
212
+ fun openEditor(call: PluginCall) {
213
+ val filePath = call.getString("filePath") ?: ""
214
+ val autoFocus = call.getBoolean("autoFocus") ?: false
215
+ if (filePath.isEmpty()) {
216
+ call.reject("File path is required")
217
+ return
218
+ }
219
+ activity.runOnUiThread {
220
+ val intent = android.content.Intent(context, com.abc15018045126.capacitor.soraeditor.compose.ComposeEditorActivity::class.java)
221
+ intent.putExtra("FILE_PATH", filePath)
222
+ intent.putExtra("AUTO_FOCUS", autoFocus)
223
+ activity.startActivity(intent)
224
+ call.resolve()
225
+ }
226
+ }
227
+ }
@@ -1,4 +1,4 @@
1
- package com.github.soraeditor.capacitor
1
+ package com.abc15018045126.capacitor.soraeditor.compose
2
2
 
3
3
  import android.os.Bundle
4
4
  import androidx.activity.ComponentActivity
@@ -9,10 +9,10 @@ import androidx.compose.material3.*
9
9
  import androidx.compose.runtime.*
10
10
  import androidx.compose.ui.Modifier
11
11
  import androidx.compose.ui.platform.LocalContext
12
- import com.github.soraeditor.capacitor.ui.EditorScreen
13
- import com.github.soraeditor.capacitor.ui.theme.NotesTheme
12
+ import com.abc15018045126.capacitor.soraeditor.compose.ui.EditorScreen
13
+ import com.abc15018045126.capacitor.soraeditor.compose.ui.theme.NotesTheme
14
14
 
15
- class EditorActivity : ComponentActivity() {
15
+ class ComposeEditorActivity : ComponentActivity() {
16
16
  private val viewModel: EditorViewModel by viewModels()
17
17
 
18
18
  override fun onCreate(savedInstanceState: Bundle?) {
@@ -20,12 +20,13 @@ class EditorActivity : ComponentActivity() {
20
20
 
21
21
  // Get file path from intent
22
22
  val filePath = intent.getStringExtra("FILE_PATH") ?: ""
23
- android.util.Log.d("EditorActivity", "onCreate with filePath: $filePath")
23
+ val autoFocus = intent.getBooleanExtra("AUTO_FOCUS", false)
24
+ android.util.Log.d("ComposeEditorActivity", "onCreate with filePath: $filePath, autoFocus: $autoFocus")
24
25
 
25
26
  if (filePath.isNotEmpty()) {
26
- viewModel.loadFile(this, filePath)
27
+ viewModel.loadFile(this, filePath, autoFocus)
27
28
  } else {
28
- android.util.Log.e("EditorActivity", "No file path provided")
29
+ android.util.Log.e("ComposeEditorActivity", "No file path provided")
29
30
  }
30
31
 
31
32
  setContent {
@@ -1,4 +1,4 @@
1
- package com.github.soraeditor.capacitor
1
+ package com.abc15018045126.capacitor.soraeditor.compose
2
2
 
3
3
  import android.content.Context
4
4
  import androidx.lifecycle.ViewModel
@@ -44,13 +44,16 @@ data class EditorUiState(
44
44
  val uiColor: String = "#F5F5F5",
45
45
  val tocColor: String = "#FFFFFF",
46
46
  val searchColor: String = "#F5F5F5",
47
- val menuColor: String = "#FFFFFF"
47
+ val menuColor: String = "#FFFFFF",
48
+ val shouldAutoFocus: Boolean = false
48
49
  )
49
50
 
50
51
  class EditorViewModel : ViewModel() {
51
52
  private val _uiState = MutableStateFlow(EditorUiState())
52
53
  val uiState: StateFlow<EditorUiState> = _uiState.asStateFlow()
53
54
 
55
+ // ... existing loadFile method ...
56
+
54
57
  fun setCursorPosition(pos: Int, line: Int, col: Int) {
55
58
  _uiState.update { it.copy(
56
59
  currentCursorPos = pos,
@@ -59,7 +62,7 @@ class EditorViewModel : ViewModel() {
59
62
  ) }
60
63
  }
61
64
 
62
- fun loadFile(context: Context, filePath: String) {
65
+ fun loadFile(context: Context, filePath: String, autoFocus: Boolean = false) {
63
66
  viewModelScope.launch {
64
67
  try {
65
68
  // Convert URI to actual path if needed
@@ -83,7 +86,8 @@ class EditorViewModel : ViewModel() {
83
86
  filePath = actualPath,
84
87
  fileName = file.name,
85
88
  originalContent = content,
86
- isModified = false
89
+ isModified = false,
90
+ shouldAutoFocus = autoFocus
87
91
  )
88
92
  } else {
89
93
  android.util.Log.e("EditorViewModel", "File does not exist: $actualPath")
@@ -140,6 +144,13 @@ class EditorViewModel : ViewModel() {
140
144
  val contentToSave = _uiState.value.content
141
145
  val path = _uiState.value.filePath
142
146
 
147
+ // Should ideally be IO, but for now we keep it simple or use runBlocking for exit safety?
148
+ // Better to just write. File writeText is blocking but OK for background thread.
149
+ // But we are in ViewModel, often main thread call.
150
+ // Let's protect against main thread blocking for large files?
151
+ // But for saveOnExit (onPause) we need synchronous or strictly ordered.
152
+ // For auto-save we are effectively inside a coroutine (from queueAutoSave).
153
+
143
154
  android.util.Log.d("EditorViewModel", "Saving file to: $path, content length: ${contentToSave.length}")
144
155
 
145
156
  val file = File(path)
@@ -1,4 +1,4 @@
1
- package com.github.soraeditor.capacitor.ui
1
+ package com.abc15018045126.capacitor.soraeditor.compose.ui
2
2
 
3
3
  import android.graphics.Typeface
4
4
  import android.view.View
@@ -27,8 +27,8 @@ import androidx.compose.ui.input.pointer.pointerInput
27
27
  import androidx.compose.foundation.gestures.detectDragGestures
28
28
  import androidx.compose.foundation.border
29
29
  import kotlinx.coroutines.launch
30
- import com.github.soraeditor.capacitor.EditorUiState
31
- import com.github.soraeditor.capacitor.EditorViewModel
30
+ import com.abc15018045126.capacitor.soraeditor.compose.EditorUiState
31
+ import com.abc15018045126.capacitor.soraeditor.compose.EditorViewModel
32
32
  import io.github.rosemoe.sora.widget.CodeEditor
33
33
  import io.github.rosemoe.sora.widget.schemes.EditorColorScheme
34
34
 
@@ -182,7 +182,8 @@ fun SoraEditorView(
182
182
  control: EditorControl? = null,
183
183
  onSearchMatchesChange: (Int, Int) -> Unit = { _, _ -> },
184
184
  onScroll: () -> Unit = {},
185
- onTap: () -> Unit = {}
185
+ onTap: () -> Unit = {},
186
+ autoFocus: Boolean = false
186
187
  ) {
187
188
  var editorInstance by remember { mutableStateOf<CodeEditor?>(null) }
188
189
 
@@ -280,6 +281,16 @@ fun SoraEditorView(
280
281
 
281
282
  editorInstance = this
282
283
  control?.attach(this)
284
+
285
+ // Auto-focus if requested (for new notes)
286
+ if (autoFocus) {
287
+ postDelayed({
288
+ requestFocus()
289
+ // Show keyboard with SHOW_FORCED to ensure it appears
290
+ val imm = context.getSystemService(android.content.Context.INPUT_METHOD_SERVICE) as? android.view.inputmethod.InputMethodManager
291
+ imm?.showSoftInput(this, android.view.inputmethod.InputMethodManager.SHOW_FORCED)
292
+ }, 200) // Delay to ensure view is fully laid out
293
+ }
283
294
  }
284
295
  },
285
296
  update = { view ->
@@ -483,7 +494,8 @@ fun EditorScreen(
483
494
  control = editorControl,
484
495
  onSearchMatchesChange = { current, total -> viewModel.setMatchResults(current, total) },
485
496
  onScroll = { if (uiState.isReadOnly && uiState.showToolbar) viewModel.setShowToolbar(false) },
486
- onTap = { if (uiState.isReadOnly) viewModel.toggleToolbar() }
497
+ onTap = { if (uiState.isReadOnly) viewModel.toggleToolbar() },
498
+ autoFocus = uiState.shouldAutoFocus
487
499
  )
488
500
  }
489
501
 
@@ -0,0 +1,104 @@
1
+ package com.abc15018045126.capacitor.soraeditor.compose.ui
2
+
3
+ import android.graphics.Color
4
+ import android.graphics.Typeface
5
+ import androidx.compose.foundation.layout.*
6
+ import androidx.compose.runtime.*
7
+ import androidx.compose.ui.Modifier
8
+ import androidx.compose.ui.viewinterop.AndroidView
9
+ import io.github.rosemoe.sora.widget.CodeEditor
10
+ import io.github.rosemoe.sora.widget.schemes.EditorColorScheme
11
+
12
+ @Composable
13
+ fun SoraEditorView(
14
+ content: String,
15
+ onContentChange: (String) -> Unit,
16
+ fontSize: Float = 18f,
17
+ showLineNumbers: Boolean = true,
18
+ wordWrap: Boolean = false,
19
+ editable: Boolean = true,
20
+ backgroundColor: String = "#FFFFFF",
21
+ modifier: Modifier = Modifier,
22
+ onUndo: () -> Unit = {},
23
+ onRedo: () -> Unit = {}
24
+ ) {
25
+ var editorInstance by remember { mutableStateOf<CodeEditor?>(null) }
26
+
27
+ // Expose undo/redo functions
28
+ LaunchedEffect(editorInstance) {
29
+ editorInstance?.let { editor ->
30
+ // Store reference for external calls
31
+ }
32
+ }
33
+
34
+ AndroidView(
35
+ factory = { context ->
36
+ CodeEditor(context).apply {
37
+ setTextSize(fontSize)
38
+ setTypefaceText(Typeface.MONOSPACE)
39
+ isLineNumberEnabled = showLineNumbers
40
+ isWordwrap = wordWrap
41
+ setEditable(editable)
42
+ setText(content)
43
+
44
+ // Set background color
45
+ try {
46
+ val color = Color.parseColor(backgroundColor)
47
+ val r = Color.red(color)
48
+ val g = Color.green(color)
49
+ val b = Color.blue(color)
50
+ val luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255.0
51
+
52
+ colorScheme.setColor(EditorColorScheme.WHOLE_BACKGROUND, color)
53
+ colorScheme.setColor(EditorColorScheme.LINE_NUMBER_BACKGROUND, color)
54
+
55
+ if (luminance < 0.5) {
56
+ colorScheme.setColor(EditorColorScheme.TEXT_NORMAL, Color.WHITE)
57
+ colorScheme.setColor(EditorColorScheme.LINE_NUMBER, Color.GRAY)
58
+ } else {
59
+ colorScheme.setColor(EditorColorScheme.TEXT_NORMAL, Color.BLACK)
60
+ colorScheme.setColor(EditorColorScheme.LINE_NUMBER, Color.DKGRAY)
61
+ }
62
+ } catch (e: Exception) {
63
+ e.printStackTrace()
64
+ }
65
+
66
+ editorInstance = this
67
+ }
68
+ },
69
+ update = { view ->
70
+ view.setTextSize(fontSize)
71
+ view.isLineNumberEnabled = showLineNumbers
72
+ view.isWordwrap = wordWrap
73
+ view.setEditable(editable)
74
+
75
+ // Only update text if it's different to avoid cursor jump
76
+ if (view.text.toString() != content) {
77
+ view.setText(content)
78
+ }
79
+
80
+ // Update background color
81
+ try {
82
+ val color = Color.parseColor(backgroundColor)
83
+ val r = Color.red(color)
84
+ val g = Color.green(color)
85
+ val b = Color.blue(color)
86
+ val luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255.0
87
+
88
+ view.colorScheme.setColor(EditorColorScheme.WHOLE_BACKGROUND, color)
89
+ view.colorScheme.setColor(EditorColorScheme.LINE_NUMBER_BACKGROUND, color)
90
+
91
+ if (luminance < 0.5) {
92
+ view.colorScheme.setColor(EditorColorScheme.TEXT_NORMAL, Color.WHITE)
93
+ view.colorScheme.setColor(EditorColorScheme.LINE_NUMBER, Color.GRAY)
94
+ } else {
95
+ view.colorScheme.setColor(EditorColorScheme.TEXT_NORMAL, Color.BLACK)
96
+ view.colorScheme.setColor(EditorColorScheme.LINE_NUMBER, Color.DKGRAY)
97
+ }
98
+ } catch (e: Exception) {
99
+ e.printStackTrace()
100
+ }
101
+ },
102
+ modifier = modifier.fillMaxSize()
103
+ )
104
+ }
@@ -1,4 +1,4 @@
1
- package com.github.soraeditor.capacitor.ui.theme
1
+ package com.abc15018045126.capacitor.soraeditor.compose.ui.theme
2
2
 
3
3
  import androidx.compose.foundation.isSystemInDarkTheme
4
4
  import androidx.compose.material3.*
Binary file
@@ -1,5 +1,44 @@
1
+ import type { PluginListenerHandle } from '@capacitor/core';
1
2
  export interface SoraEditorPlugin {
3
+ start(options: SoraStartOptions): Promise<void>;
4
+ close(): Promise<void>;
5
+ getText(): Promise<{
6
+ content: string;
7
+ }>;
8
+ setText(options: {
9
+ content: string;
10
+ }): Promise<void>;
11
+ getSelection(): Promise<{
12
+ line: number;
13
+ column: number;
14
+ }>;
15
+ setSelection(options: {
16
+ line: number;
17
+ column: number;
18
+ }): Promise<void>;
19
+ undo(): Promise<void>;
20
+ redo(): Promise<void>;
2
21
  openEditor(options: {
3
22
  filePath: string;
23
+ autoFocus?: boolean;
4
24
  }): Promise<void>;
25
+ addListener(eventName: 'onEditorClick', listenerFunc: () => void): Promise<PluginListenerHandle>;
26
+ addListener(eventName: 'onContentChange', listenerFunc: () => void): Promise<PluginListenerHandle>;
27
+ }
28
+ export interface SoraStartOptions {
29
+ content?: string;
30
+ top?: number;
31
+ left?: number;
32
+ right?: number;
33
+ bottom?: number;
34
+ width?: number;
35
+ height?: number;
36
+ fontSize?: number;
37
+ showLineNumbers?: boolean;
38
+ wordWrap?: boolean;
39
+ editable?: boolean;
40
+ backgroundColor?: string;
41
+ selectionLine?: number;
42
+ selectionColumn?: number;
5
43
  }
44
+ //# sourceMappingURL=definitions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"definitions.d.ts","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAE5D,MAAM,WAAW,gBAAgB;IAC7B,KAAK,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,OAAO,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxC,OAAO,CAAC,OAAO,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,YAAY,IAAI,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC1D,YAAY,CAAC,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,UAAU,CAAC,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9E,WAAW,CAAC,SAAS,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACjG,WAAW,CAAC,SAAS,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;CACtG;AAED,MAAM,WAAW,gBAAgB;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC5B"}
@@ -2,3 +2,4 @@ import type { SoraEditorPlugin } from './definitions';
2
2
  declare const SoraEditor: SoraEditorPlugin;
3
3
  export * from './definitions';
4
4
  export { SoraEditor };
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEtD,QAAA,MAAM,UAAU,kBAEd,CAAC;AAEH,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,UAAU,EAAE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAIjD,MAAM,UAAU,GAAG,cAAc,CAAmB,YAAY,EAAE;IAC9D,GAAG,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,aAAa,EAAE,CAAC;CAC9D,CAAC,CAAC;AAEH,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,UAAU,EAAE,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGjD,MAAM,UAAU,GAAG,cAAc,CAAmB,YAAY,EAAE;IAC9D,GAAG,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,aAAa,EAAE,CAAC;CAC9D,CAAC,CAAC;AAEH,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,UAAU,EAAE,CAAC"}
package/dist/esm/web.d.ts CHANGED
@@ -1,7 +1,27 @@
1
1
  import { WebPlugin } from '@capacitor/core';
2
- import type { SoraEditorPlugin } from './definitions';
2
+ import type { SoraEditorPlugin, SoraStartOptions } from './definitions';
3
3
  export declare class SoraEditorWeb extends WebPlugin implements SoraEditorPlugin {
4
+ start(options: SoraStartOptions): Promise<void>;
5
+ close(): Promise<void>;
6
+ getText(): Promise<{
7
+ content: string;
8
+ }>;
9
+ setText(options: {
10
+ content: string;
11
+ }): Promise<void>;
12
+ getSelection(): Promise<{
13
+ line: number;
14
+ column: number;
15
+ }>;
16
+ setSelection(options: {
17
+ line: number;
18
+ column: number;
19
+ }): Promise<void>;
20
+ undo(): Promise<void>;
21
+ redo(): Promise<void>;
4
22
  openEditor(options: {
5
23
  filePath: string;
24
+ autoFocus?: boolean;
6
25
  }): Promise<void>;
7
26
  }
27
+ //# sourceMappingURL=web.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web.d.ts","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAExE,qBAAa,aAAc,SAAQ,SAAU,YAAW,gBAAgB;IAC9D,KAAK,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAG/C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAGtB,OAAO,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAGvC,OAAO,CAAC,OAAO,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAGpD,YAAY,IAAI,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAGzD,YAAY,CAAC,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAGtE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAGrB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAGrB,UAAU,CAAC,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAGtF"}
package/dist/esm/web.js CHANGED
@@ -1,8 +1,31 @@
1
1
  import { WebPlugin } from '@capacitor/core';
2
2
  export class SoraEditorWeb extends WebPlugin {
3
+ async start(options) {
4
+ console.log('SoraEditor start', options);
5
+ }
6
+ async close() {
7
+ console.log('SoraEditor close');
8
+ }
9
+ async getText() {
10
+ return { content: '' };
11
+ }
12
+ async setText(options) {
13
+ console.log('SoraEditor setText', options);
14
+ }
15
+ async getSelection() {
16
+ return { line: 0, column: 0 };
17
+ }
18
+ async setSelection(options) {
19
+ console.log('SoraEditor setSelection', options);
20
+ }
21
+ async undo() {
22
+ console.log('SoraEditor undo');
23
+ }
24
+ async redo() {
25
+ console.log('SoraEditor redo');
26
+ }
3
27
  async openEditor(options) {
4
- console.log('openEditor', options);
5
- console.warn('SoraEditor is implemented for Android only');
28
+ console.log('SoraEditor openEditor', options);
6
29
  }
7
30
  }
8
31
  //# sourceMappingURL=web.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAG5C,MAAM,OAAO,aAAc,SAAQ,SAAS;IACxC,KAAK,CAAC,UAAU,CAAC,OAA6B;QAC1C,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QACnC,OAAO,CAAC,IAAI,CAAC,4CAA4C,CAAC,CAAC;IAC/D,CAAC;CACJ"}
1
+ {"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAG5C,MAAM,OAAO,aAAc,SAAQ,SAAS;IACxC,KAAK,CAAC,KAAK,CAAC,OAAyB;QACjC,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,OAAO,CAAC,CAAC;IAC7C,CAAC;IACD,KAAK,CAAC,KAAK;QACP,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;IACpC,CAAC;IACD,KAAK,CAAC,OAAO;QACT,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IAC3B,CAAC;IACD,KAAK,CAAC,OAAO,CAAC,OAA4B;QACtC,OAAO,CAAC,GAAG,CAAC,oBAAoB,EAAE,OAAO,CAAC,CAAC;IAC/C,CAAC;IACD,KAAK,CAAC,YAAY;QACd,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;IAClC,CAAC;IACD,KAAK,CAAC,YAAY,CAAC,OAAyC;QACxD,OAAO,CAAC,GAAG,CAAC,yBAAyB,EAAE,OAAO,CAAC,CAAC;IACpD,CAAC;IACD,KAAK,CAAC,IAAI;QACN,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IACnC,CAAC;IACD,KAAK,CAAC,IAAI;QACN,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IACnC,CAAC;IACD,KAAK,CAAC,UAAU,CAAC,OAAkD;QAC/D,OAAO,CAAC,GAAG,CAAC,uBAAuB,EAAE,OAAO,CAAC,CAAC;IAClD,CAAC;CACJ"}
@@ -7,9 +7,32 @@ const SoraEditor = core.registerPlugin('SoraEditor', {
7
7
  });
8
8
 
9
9
  class SoraEditorWeb extends core.WebPlugin {
10
+ async start(options) {
11
+ console.log('SoraEditor start', options);
12
+ }
13
+ async close() {
14
+ console.log('SoraEditor close');
15
+ }
16
+ async getText() {
17
+ return { content: '' };
18
+ }
19
+ async setText(options) {
20
+ console.log('SoraEditor setText', options);
21
+ }
22
+ async getSelection() {
23
+ return { line: 0, column: 0 };
24
+ }
25
+ async setSelection(options) {
26
+ console.log('SoraEditor setSelection', options);
27
+ }
28
+ async undo() {
29
+ console.log('SoraEditor undo');
30
+ }
31
+ async redo() {
32
+ console.log('SoraEditor redo');
33
+ }
10
34
  async openEditor(options) {
11
- console.log('openEditor', options);
12
- console.warn('SoraEditor is implemented for Android only');
35
+ console.log('SoraEditor openEditor', options);
13
36
  }
14
37
  }
15
38
 
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.cjs.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst SoraEditor = registerPlugin('SoraEditor', {\n web: () => import('./web').then(m => new m.SoraEditorWeb()),\n});\nexport * from './definitions';\nexport { SoraEditor };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nexport class SoraEditorWeb extends WebPlugin {\n async openEditor(options) {\n console.log('openEditor', options);\n console.warn('SoraEditor is implemented for Android only');\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":";;;;AACK,MAAC,UAAU,GAAGA,mBAAc,CAAC,YAAY,EAAE;AAChD,IAAI,GAAG,EAAE,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,aAAa,EAAE,CAAC;AAC/D,CAAC;;ACFM,MAAM,aAAa,SAASC,cAAS,CAAC;AAC7C,IAAI,MAAM,UAAU,CAAC,OAAO,EAAE;AAC9B,QAAQ,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,OAAO,CAAC;AAC1C,QAAQ,OAAO,CAAC,IAAI,CAAC,4CAA4C,CAAC;AAClE,IAAI;AACJ;;;;;;;;;"}
1
+ {"version":3,"file":"plugin.cjs.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst SoraEditor = registerPlugin('SoraEditor', {\n web: () => import('./web').then(m => new m.SoraEditorWeb()),\n});\nexport * from './definitions';\nexport { SoraEditor };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nexport class SoraEditorWeb extends WebPlugin {\n async start(options) {\n console.log('SoraEditor start', options);\n }\n async close() {\n console.log('SoraEditor close');\n }\n async getText() {\n return { content: '' };\n }\n async setText(options) {\n console.log('SoraEditor setText', options);\n }\n async getSelection() {\n return { line: 0, column: 0 };\n }\n async setSelection(options) {\n console.log('SoraEditor setSelection', options);\n }\n async undo() {\n console.log('SoraEditor undo');\n }\n async redo() {\n console.log('SoraEditor redo');\n }\n async openEditor(options) {\n console.log('SoraEditor openEditor', options);\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":";;;;AACK,MAAC,UAAU,GAAGA,mBAAc,CAAC,YAAY,EAAE;AAChD,IAAI,GAAG,EAAE,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,aAAa,EAAE,CAAC;AAC/D,CAAC;;ACFM,MAAM,aAAa,SAASC,cAAS,CAAC;AAC7C,IAAI,MAAM,KAAK,CAAC,OAAO,EAAE;AACzB,QAAQ,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,OAAO,CAAC;AAChD,IAAI;AACJ,IAAI,MAAM,KAAK,GAAG;AAClB,QAAQ,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC;AACvC,IAAI;AACJ,IAAI,MAAM,OAAO,GAAG;AACpB,QAAQ,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE;AAC9B,IAAI;AACJ,IAAI,MAAM,OAAO,CAAC,OAAO,EAAE;AAC3B,QAAQ,OAAO,CAAC,GAAG,CAAC,oBAAoB,EAAE,OAAO,CAAC;AAClD,IAAI;AACJ,IAAI,MAAM,YAAY,GAAG;AACzB,QAAQ,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE;AACrC,IAAI;AACJ,IAAI,MAAM,YAAY,CAAC,OAAO,EAAE;AAChC,QAAQ,OAAO,CAAC,GAAG,CAAC,yBAAyB,EAAE,OAAO,CAAC;AACvD,IAAI;AACJ,IAAI,MAAM,IAAI,GAAG;AACjB,QAAQ,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;AACtC,IAAI;AACJ,IAAI,MAAM,IAAI,GAAG;AACjB,QAAQ,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;AACtC,IAAI;AACJ,IAAI,MAAM,UAAU,CAAC,OAAO,EAAE;AAC9B,QAAQ,OAAO,CAAC,GAAG,CAAC,uBAAuB,EAAE,OAAO,CAAC;AACrD,IAAI;AACJ;;;;;;;;;"}
package/dist/plugin.js CHANGED
@@ -6,9 +6,32 @@ var capacitorSoraEditor = (function (exports, core) {
6
6
  });
7
7
 
8
8
  class SoraEditorWeb extends core.WebPlugin {
9
+ async start(options) {
10
+ console.log('SoraEditor start', options);
11
+ }
12
+ async close() {
13
+ console.log('SoraEditor close');
14
+ }
15
+ async getText() {
16
+ return { content: '' };
17
+ }
18
+ async setText(options) {
19
+ console.log('SoraEditor setText', options);
20
+ }
21
+ async getSelection() {
22
+ return { line: 0, column: 0 };
23
+ }
24
+ async setSelection(options) {
25
+ console.log('SoraEditor setSelection', options);
26
+ }
27
+ async undo() {
28
+ console.log('SoraEditor undo');
29
+ }
30
+ async redo() {
31
+ console.log('SoraEditor redo');
32
+ }
9
33
  async openEditor(options) {
10
- console.log('openEditor', options);
11
- console.warn('SoraEditor is implemented for Android only');
34
+ console.log('SoraEditor openEditor', options);
12
35
  }
13
36
  }
14
37
 
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst SoraEditor = registerPlugin('SoraEditor', {\n web: () => import('./web').then(m => new m.SoraEditorWeb()),\n});\nexport * from './definitions';\nexport { SoraEditor };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nexport class SoraEditorWeb extends WebPlugin {\n async openEditor(options) {\n console.log('openEditor', options);\n console.warn('SoraEditor is implemented for Android only');\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":";;;AACK,UAAC,UAAU,GAAGA,mBAAc,CAAC,YAAY,EAAE;IAChD,IAAI,GAAG,EAAE,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,aAAa,EAAE,CAAC;IAC/D,CAAC;;ICFM,MAAM,aAAa,SAASC,cAAS,CAAC;IAC7C,IAAI,MAAM,UAAU,CAAC,OAAO,EAAE;IAC9B,QAAQ,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,OAAO,CAAC;IAC1C,QAAQ,OAAO,CAAC,IAAI,CAAC,4CAA4C,CAAC;IAClE,IAAI;IACJ;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"plugin.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst SoraEditor = registerPlugin('SoraEditor', {\n web: () => import('./web').then(m => new m.SoraEditorWeb()),\n});\nexport * from './definitions';\nexport { SoraEditor };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nexport class SoraEditorWeb extends WebPlugin {\n async start(options) {\n console.log('SoraEditor start', options);\n }\n async close() {\n console.log('SoraEditor close');\n }\n async getText() {\n return { content: '' };\n }\n async setText(options) {\n console.log('SoraEditor setText', options);\n }\n async getSelection() {\n return { line: 0, column: 0 };\n }\n async setSelection(options) {\n console.log('SoraEditor setSelection', options);\n }\n async undo() {\n console.log('SoraEditor undo');\n }\n async redo() {\n console.log('SoraEditor redo');\n }\n async openEditor(options) {\n console.log('SoraEditor openEditor', options);\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":";;;AACK,UAAC,UAAU,GAAGA,mBAAc,CAAC,YAAY,EAAE;IAChD,IAAI,GAAG,EAAE,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,aAAa,EAAE,CAAC;IAC/D,CAAC;;ICFM,MAAM,aAAa,SAASC,cAAS,CAAC;IAC7C,IAAI,MAAM,KAAK,CAAC,OAAO,EAAE;IACzB,QAAQ,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,OAAO,CAAC;IAChD,IAAI;IACJ,IAAI,MAAM,KAAK,GAAG;IAClB,QAAQ,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC;IACvC,IAAI;IACJ,IAAI,MAAM,OAAO,GAAG;IACpB,QAAQ,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE;IAC9B,IAAI;IACJ,IAAI,MAAM,OAAO,CAAC,OAAO,EAAE;IAC3B,QAAQ,OAAO,CAAC,GAAG,CAAC,oBAAoB,EAAE,OAAO,CAAC;IAClD,IAAI;IACJ,IAAI,MAAM,YAAY,GAAG;IACzB,QAAQ,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE;IACrC,IAAI;IACJ,IAAI,MAAM,YAAY,CAAC,OAAO,EAAE;IAChC,QAAQ,OAAO,CAAC,GAAG,CAAC,yBAAyB,EAAE,OAAO,CAAC;IACvD,IAAI;IACJ,IAAI,MAAM,IAAI,GAAG;IACjB,QAAQ,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;IACtC,IAAI;IACJ,IAAI,MAAM,IAAI,GAAG;IACjB,QAAQ,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;IACtC,IAAI;IACJ,IAAI,MAAM,UAAU,CAAC,OAAO,EAAE;IAC9B,QAAQ,OAAO,CAAC,GAAG,CAAC,uBAAuB,EAAE,OAAO,CAAC;IACrD,IAAI;IACJ;;;;;;;;;;;;;;;"}
package/package.json CHANGED
@@ -1,76 +1,27 @@
1
1
  {
2
2
  "name": "capacitor-sora-editor",
3
- "version": "0.0.2",
4
- "description": "Capacitor plugin for Sora Editor",
5
- "main": "dist/plugin.cjs.js",
3
+ "version": "1.0.0",
4
+ "description": "SoraEditor plugin for Capacitor",
5
+ "main": "dist/plugin.js",
6
6
  "module": "dist/esm/index.js",
7
7
  "types": "dist/esm/index.d.ts",
8
- "unpkg": "dist/plugin.js",
9
- "files": [
10
- "dist/",
11
- "ios/",
12
- "android/",
13
- "CapacitorSoraEditor.podspec"
14
- ],
15
- "author": "",
16
- "license": "MIT",
17
- "repository": {
18
- "type": "git",
19
- "url": "git+https://github.com/abc15018045126/notes.git"
20
- },
21
- "bugs": {
22
- "url": "https://github.com/abc15018045126/notes/issues"
23
- },
24
- "keywords": [
25
- "capacitor",
26
- "plugin",
27
- "native"
28
- ],
29
8
  "scripts": {
30
- "verify": "npm run verify:ios && npm run verify:android && npm run verify:web",
31
- "verify:ios": "cd ios && pod install && xcodebuild -workspace Plugin.xcworkspace -scheme Plugin -destination generic/platform=iOS && cd ..",
32
- "verify:android": "cd android && ./gradlew clean build test && cd ..",
33
- "verify:web": "npm run build",
34
- "lint": "npm run eslint && npm run prettier -- --check && npm run swiftlint -- lint",
35
- "fmt": "npm run eslint -- --fix && npm run prettier -- --write && npm run swiftlint -- --fix --format",
36
- "eslint": "eslint . --ext ts",
37
- "prettier": "prettier \"**/*.{css,html,ts,js,java}\"",
38
- "swiftlint": "node-swiftlint",
39
- "docgen": "docgen --api SoraEditorPlugin --output-readme README.md --output-json dist/docs.json",
40
- "build": "npm run clean && tsc && rollup -c rollup.config.mjs",
41
- "clean": "rimraf dist",
42
- "prepublishOnly": "npm run build"
9
+ "build": "tsc && rollup -c rollup.config.mjs"
43
10
  },
11
+ "author": "abc15018045126",
12
+ "license": "MIT",
44
13
  "devDependencies": {
45
- "@capacitor/android": "^6.0.0",
46
- "@capacitor/core": "^6.0.0",
47
- "@capacitor/docgen": "^0.2.1",
48
- "@capacitor/ios": "^6.0.0",
49
- "@ionic/eslint-config": "^0.3.0",
50
- "@ionic/prettier-config": "^2.0.0",
51
- "@ionic/swiftlint-config": "^1.1.2",
52
- "eslint": "^7.11.0",
53
- "prettier": "^2.4.0",
54
- "prettier-plugin-java": "~1.0.2",
55
- "rimraf": "^3.0.2",
56
- "rollup": "^2.32.0",
57
- "swiftlint": "^1.0.1",
58
- "typescript": "^5.9.3"
14
+ "@capacitor/android": "^8.0.0",
15
+ "@capacitor/core": "^8.0.0",
16
+ "rollup": "^4.0.0",
17
+ "typescript": "^5.0.0"
59
18
  },
60
19
  "peerDependencies": {
61
- "@capacitor/core": ">=6.0.0"
62
- },
63
- "prettier": "@ionic/prettier-config",
64
- "swiftlint": "@ionic/swiftlint-config",
65
- "eslintConfig": {
66
- "extends": "@ionic/eslint-config/recommended"
20
+ "@capacitor/core": "^8.0.0"
67
21
  },
68
22
  "capacitor": {
69
23
  "android": {
70
24
  "src": "android"
71
- },
72
- "ios": {
73
- "src": "ios"
74
25
  }
75
26
  }
76
27
  }
@@ -0,0 +1,22 @@
1
+ export default {
2
+ input: 'dist/esm/index.js',
3
+ output: [
4
+ {
5
+ file: 'dist/plugin.js',
6
+ format: 'iife',
7
+ name: 'capacitorSoraEditor',
8
+ globals: {
9
+ '@capacitor/core': 'capacitorExports',
10
+ },
11
+ sourcemap: true,
12
+ inlineDynamicImports: true,
13
+ },
14
+ {
15
+ file: 'dist/plugin.cjs.js',
16
+ format: 'cjs',
17
+ sourcemap: true,
18
+ inlineDynamicImports: true,
19
+ },
20
+ ],
21
+ external: ['@capacitor/core'],
22
+ };
@@ -0,0 +1,32 @@
1
+ import type { PluginListenerHandle } from '@capacitor/core';
2
+
3
+ export interface SoraEditorPlugin {
4
+ start(options: SoraStartOptions): Promise<void>;
5
+ close(): Promise<void>;
6
+ getText(): Promise<{ content: string }>;
7
+ setText(options: { content: string }): Promise<void>;
8
+ getSelection(): Promise<{ line: number; column: number }>;
9
+ setSelection(options: { line: number; column: number }): Promise<void>;
10
+ undo(): Promise<void>;
11
+ redo(): Promise<void>;
12
+ openEditor(options: { filePath: string; autoFocus?: boolean }): Promise<void>;
13
+ addListener(eventName: 'onEditorClick', listenerFunc: () => void): Promise<PluginListenerHandle>;
14
+ addListener(eventName: 'onContentChange', listenerFunc: () => void): Promise<PluginListenerHandle>;
15
+ }
16
+
17
+ export interface SoraStartOptions {
18
+ content?: string;
19
+ top?: number;
20
+ left?: number;
21
+ right?: number;
22
+ bottom?: number;
23
+ width?: number;
24
+ height?: number;
25
+ fontSize?: number;
26
+ showLineNumbers?: boolean;
27
+ wordWrap?: boolean;
28
+ editable?: boolean;
29
+ backgroundColor?: string;
30
+ selectionLine?: number;
31
+ selectionColumn?: number;
32
+ }
package/src/index.ts ADDED
@@ -0,0 +1,9 @@
1
+ import { registerPlugin } from '@capacitor/core';
2
+ import type { SoraEditorPlugin } from './definitions';
3
+
4
+ const SoraEditor = registerPlugin<SoraEditorPlugin>('SoraEditor', {
5
+ web: () => import('./web').then(m => new m.SoraEditorWeb()),
6
+ });
7
+
8
+ export * from './definitions';
9
+ export { SoraEditor };
package/src/web.ts ADDED
@@ -0,0 +1,32 @@
1
+ import { WebPlugin } from '@capacitor/core';
2
+ import type { SoraEditorPlugin, SoraStartOptions } from './definitions';
3
+
4
+ export class SoraEditorWeb extends WebPlugin implements SoraEditorPlugin {
5
+ async start(options: SoraStartOptions): Promise<void> {
6
+ console.log('SoraEditor start', options);
7
+ }
8
+ async close(): Promise<void> {
9
+ console.log('SoraEditor close');
10
+ }
11
+ async getText(): Promise<{ content: string }> {
12
+ return { content: '' };
13
+ }
14
+ async setText(options: { content: string }): Promise<void> {
15
+ console.log('SoraEditor setText', options);
16
+ }
17
+ async getSelection(): Promise<{ line: number; column: number }> {
18
+ return { line: 0, column: 0 };
19
+ }
20
+ async setSelection(options: { line: number; column: number }): Promise<void> {
21
+ console.log('SoraEditor setSelection', options);
22
+ }
23
+ async undo(): Promise<void> {
24
+ console.log('SoraEditor undo');
25
+ }
26
+ async redo(): Promise<void> {
27
+ console.log('SoraEditor redo');
28
+ }
29
+ async openEditor(options: { filePath: string; autoFocus?: boolean }): Promise<void> {
30
+ console.log('SoraEditor openEditor', options);
31
+ }
32
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "ESNext",
5
+ "lib": [
6
+ "ES2020",
7
+ "DOM"
8
+ ],
9
+ "declaration": true,
10
+ "declarationMap": true,
11
+ "sourceMap": true,
12
+ "outDir": "./dist/esm",
13
+ "moduleResolution": "node",
14
+ "esModuleInterop": true,
15
+ "skipLibCheck": true,
16
+ "forceConsistentCasingInFileNames": true,
17
+ "strict": true
18
+ },
19
+ "include": [
20
+ "src/**/*"
21
+ ],
22
+ "exclude": [
23
+ "node_modules",
24
+ "dist"
25
+ ]
26
+ }
package/README.md DELETED
@@ -1,110 +0,0 @@
1
- # Capacitor Sora Editor Plugin
2
-
3
- A powerful, high-performance code editor for Capacitor Android applications, built with [Sora Editor](https://github.com/Rosemoe/sora-editor) and **Jetpack Compose**.
4
-
5
- This plugin provides a full-featured native code editor experience within your hybrid app, significantly outperforming web-based editors (like Monaco or CodeMirror in a WebView) on mobile devices, especially for large files.
6
-
7
- ## 🚀 Features
8
-
9
- * **High Performance**: Native rendering ensures smooth typing and scrolling, even for large files (thousands of lines).
10
- * **Jetpack Compose UI**: Modern, clean, and customizable Material Design 3 interface.
11
- * **Rich Editing Tools**:
12
- * Undo / Redo
13
- * Search & Replace (with Regex support)
14
- * Go to Line
15
- * Real-time Line & Column counters
16
- * **Productivity Features**:
17
- * **Table of Contents (TOC)**: Automatically generates a TOC based on content (chapters or line chunks).
18
- * **Symbol Bar**: Quick access to common programming symbols above the keyboard.
19
- * **Read-Only Mode**: Toggle between browsing and editing.
20
- * **Auto-Save**: Securely saves changes to the local filesystem.
21
- * **Smart Renaming**: Automatically detects "new" files and proposes a rename based on the first line of text upon exit.
22
- * **Customization**:
23
- * Toggle Line Numbers
24
- * Toggle Word Wrap
25
- * Adjust Font Size
26
- * System Dark/Light Theme support
27
- * Customizable colors for background, UI, etc.
28
-
29
- ## 🛠 Tech Stack & Requirements
30
-
31
- * **Capacitor**: ^6.0.0
32
- * **Android**:
33
- * `minSdkVersion`: 22+
34
- * `compileSdkVersion`: 34+
35
- * Kotlin: 1.9.0+
36
- * Jetpack Compose enabled
37
- * **Sora Editor**: 0.24.4
38
-
39
- ## 📦 Installation
40
-
41
- ```bash
42
- npm install capacitor-sora-editor
43
- npx cap sync
44
- ```
45
-
46
- ## 🔧 Android Configuration
47
-
48
- Since this plugin allows file editing, you generally deal with local files. Ensure your app asks for necessary storage permissions if accessing files outside your app's sandbox.
49
-
50
- No special initialization is required in `MainActivity.java` for Capacitor 3+ as plugins are auto-detected.
51
-
52
- However, ensure your `variables.gradle` or project `build.gradle` meets the minimum Kotlin and SDK versions.
53
-
54
- ## 📖 Usage
55
-
56
- ### 1. Import the Plugin
57
-
58
- ```typescript
59
- import { SoraEditor } from 'capacitor-sora-editor';
60
- ```
61
-
62
- ### 2. Open the Editor
63
-
64
- The plugin works by launching a native Android Activity on top of your current WebView. You trigger it by passing the absolute URI of the file you want to edit.
65
-
66
- ```typescript
67
- const openNativeEditor = async () => {
68
- try {
69
- // You need a valid "file://" URI to a local file.
70
- // Use @capacitor/filesystem to get the URI if needed.
71
- const fileUri = "file:///data/user/0/com.your.app/files/Documents/my_script.js";
72
-
73
- await SoraEditor.openEditor({
74
- filePath: fileUri
75
- });
76
-
77
- console.log("Editor closed");
78
- // You might want to reload the file content in your web app here
79
- // as the native editor has modified it.
80
- } catch (error) {
81
- console.error("Failed to open editor", error);
82
- }
83
- };
84
- ```
85
-
86
- ### 3. File Handling & Lifecycle
87
-
88
- * **Opening**: When `openEditor` is called, the Native Activity starts. The implementation reads the file from the disk directly.
89
- * **Saving**: The editor handles saving internally. It auto-saves on typing (debounced) and performs a final save/rename check when the user exits (presses Back).
90
- * **Return**: When the user presses the Back button in the Native UI, the Activity finishes, and the Promise returned by `openEditor` resolves. Development flow usually involves reloading the file content in your JS layer after the promise resolves.
91
-
92
- ## 🎨 UI & Customization
93
-
94
- The editor includes a built-in "Settings" screen (accessible via the "More" menu in the top-right corner) where the user can configure:
95
-
96
- * Font Size
97
- * Theme Colors
98
- * Line Numbers / Word Wrap
99
- * Auto-save behavior
100
-
101
- These settings are persisted natively in `SharedPreferences` by the plugin.
102
-
103
- ## ⚠️ Notes
104
-
105
- * **Android Only**: This plugin currently implements the native editor **only for Android**. Calling it on iOS or Web will print a warning and do nothing (or throw an error based on configuration).
106
- * **Context**: This was extracted from a personal note-taking app "Squircle CE", designed for high-performance offline text editing.
107
-
108
- ## 📄 License
109
-
110
- MIT
@@ -1,33 +0,0 @@
1
- package com.github.soraeditor.capacitor
2
-
3
- import android.content.Intent
4
- import com.getcapacitor.JSObject
5
- import com.getcapacitor.Plugin
6
- import com.getcapacitor.PluginCall
7
- import com.getcapacitor.PluginMethod
8
- import com.getcapacitor.annotation.CapacitorPlugin
9
-
10
- @CapacitorPlugin(name = "SoraEditor")
11
- class SoraEditorPlugin : Plugin() {
12
-
13
- @PluginMethod
14
- fun openEditor(call: PluginCall) {
15
- val filePath = call.getString("filePath") ?: ""
16
-
17
- android.util.Log.d("SoraEditorPlugin", "openEditor called with filePath: $filePath")
18
-
19
- if (filePath.isEmpty()) {
20
- android.util.Log.e("SoraEditorPlugin", "File path is empty")
21
- call.reject("File path is required")
22
- return
23
- }
24
-
25
- activity.runOnUiThread {
26
- val intent = Intent(context, EditorActivity::class.java)
27
- intent.putExtra("FILE_PATH", filePath)
28
- android.util.Log.d("SoraEditorPlugin", "Starting EditorActivity")
29
- activity.startActivity(intent)
30
- call.resolve()
31
- }
32
- }
33
- }
@@ -1,6 +0,0 @@
1
- <resources>
2
- <style name="AppTheme.NoActionBar" parent="Theme.AppCompat.NoActionBar">
3
- <item name="windowActionBar">false</item>
4
- <item name="windowNoTitle">true</item>
5
- </style>
6
- </resources>