Search page titles, headings and text across the docs.

Packages

On this page

@canvist/lynx

canvist’s rich-text editor for Lynx: the native iOS and Android CanvistViews as a <canvist-editor> element, not a web view. Typing goes through each platform’s own text input, and the Rust engine lays out and paints every line. Commands, queries, and documents are typed from the extensions you register, with the same API as @canvist/web and @canvist/react-native.

import { CanvistEditor } from "@canvist/lynx";

<CanvistEditor
	content={{ format: "markdown", input: "# Notes" }}
	onUpdate={(editor) => save(editor.export("markdown"))}
	accessibility-label="Note"
	style={{ flex: 1 }}
/>;

Needs ReactLynx, Lynx 4.1, iOS 17, and Android 7 (API 24).

A toolbar that follows the selection

import {
	bold,
	CanvistView,
	heading,
	useCanvistEditor,
	useEditorState,
} from "@canvist/lynx";

function Notes() {
	const { editor, props } = useCanvistEditor({ extensions: [bold, heading] });
	const isBold = useEditorState(editor, (e) => e.query.isBold(), false);

	return (
		<view style={{ flex: 1 }}>
			{/* `ignore-focus` keeps the keyboard up while you tap the toolbar. */}
			<view ignore-focus bindtap={() => editor?.commands.toggleBold()}>
				<text>{isBold ? "Unbold" : "Bold"}</text>
			</view>
			<CanvistView {...props} style={{ flex: 1 }} />
		</view>
	);
}

The editor has the same calls as on React Native: commands, can, query, run, ask, setContent, export, toJson, selection, select, insertText, undo, redo, focus, blur, and subscribe. They are synchronous, and failures throw a CanvistError with a stable code.

Install

The native editors aren’t on a package registry yet, so build them from the repository, and register the element and its native module in your host app.

iOS. Build the engine with platforms/apple/build.sh, add both pods to your Podfile, and register the module on your LynxConfig. The element registers itself as <canvist-editor>:

pod "Canvist", path: "path/to/canvist/platforms/apple"
pod "CanvistLynx", path: "path/to/canvist/packages/canvist-lynx"
let config = LynxConfig(provider: provider)
config.register(CanvistLynxModule.self)

Android. Publish canvist-android to Maven Local with platforms/android/build.sh and ./gradlew :canvist:publishToMavenLocal, include packages/canvist-lynx/android as a Gradle project, and register both pieces:

LynxEnv.inst().init(application, null, null, null)
CanvistLynx.registerModule()

val lynxView = LynxViewBuilder()
	.setTemplateProvider(provider)
	.addBehavior(CanvistLynx.behavior())
	.build(context)

Lynx knows neither the safe area nor the keyboard, so size your LynxView to fit between them, as examples/lynx does.

Development
npm test                               # rstest: the typed editor, the element's attributes and events
npx tsc --noEmit                       # types, including the type-level tests
../../examples/lynx/e2e/test.sh ios    # the example on a simulator, driven by Maestro
../../examples/lynx/e2e/test.sh android

The build keeps the JSX (dist/*.jsx) for the app’s ReactLynx compiler, as Lynx libraries do. The native module answers with a JSON envelope rather than throwing, because Lynx reports native exceptions to the host rather than to JavaScript.

Docs