diff --git a/packages/core/src/perception/dom-trimmer.ts b/packages/core/src/perception/dom-trimmer.ts
index 6ec5bd2..1406ed0 100644
--- a/packages/core/src/perception/dom-trimmer.ts
+++ b/packages/core/src/perception/dom-trimmer.ts
@@ -266,7 +266,8 @@ function walkIOS(node: any, result: TrimmedNode[], parentContext: string = ''):
const isFocused = node['@_hasFocus'] === 'true';
const isSelected = node['@_selected'] === 'true';
- const editableTypes = ['TextField', 'SecureTextField', 'TextEditor', 'SearchField'];
+ // XCUITest exposes UIKit/RN multiline inputs (including KRN composers) as TextView.
+ const editableTypes = ['TextField', 'TextView', 'SecureTextField', 'TextEditor', 'SearchField'];
const editable = editableTypes.some((t) => tag.includes(t));
const clickable =
isAccessible ||
diff --git a/packages/core/src/perception/ios-parser.ts b/packages/core/src/perception/ios-parser.ts
index d6ee2fe..8fa2848 100644
--- a/packages/core/src/perception/ios-parser.ts
+++ b/packages/core/src/perception/ios-parser.ts
@@ -17,7 +17,8 @@ function shortTypeName(xcuiType: string): string {
/** Check if an iOS element type is typically editable */
function isEditableType(typeName: string): boolean {
- const editableTypes = ['TextField', 'SecureTextField', 'TextEditor', 'SearchField'];
+ // XCUITest exposes UIKit/RN multiline inputs (including KRN composers) as TextView.
+ const editableTypes = ['TextField', 'TextView', 'SecureTextField', 'TextEditor', 'SearchField'];
return editableTypes.some((t) => typeName.includes(t));
}
diff --git a/tests/flow/ios-textview-editable.test.ts b/tests/flow/ios-textview-editable.test.ts
new file mode 100644
index 0000000..729020c
--- /dev/null
+++ b/tests/flow/ios-textview-editable.test.ts
@@ -0,0 +1,54 @@
+import { describe, expect, test } from 'vitest';
+import { resolveEditableForTarget } from '@appclaw/core/flow/run-yaml-flow';
+import { trimDOM } from '@appclaw/core/perception/dom-trimmer';
+import { parseIOSPageSource } from '@appclaw/core/perception/ios-parser';
+
+const krnComposerSource = `
+
+
+
+
+`;
+
+describe('iOS XCUIElementTypeTextView inputs', () => {
+ test('page-source parser exposes a TextView as an editable type target', () => {
+ const elements = parseIOSPageSource(krnComposerSource);
+ const input = elements.find((element) => element.id === 'kitchen-composer-text-input');
+
+ expect(input).toMatchObject({
+ type: 'TextView',
+ editable: true,
+ action: 'type',
+ });
+ expect(resolveEditableForTarget(elements, 'kitchen-composer-text-input').el).toBe(input);
+ });
+
+ test('DOM trimmer marks a TextView editable and includes it in editableCount', () => {
+ const result = trimDOM(krnComposerSource, 'ios');
+
+ expect(result.editableCount).toBe(1);
+ expect(result.xml).toContain('