Skip to content
Anjunar/ DOCS

Scala JS Ember

A headless-first rich-text editor engine for Scala.js. One immutable document drives server-rendered HTML and a hydrated editor in the browser; there is no contenteditable command layer underneath.

1.0.3Current release
Scala.jsRuns on
Scala 3.3Language version
MITLicense

Installation

Modules are published one by one. A minimal rich-text editor needs the kernel, the rich-text profile, the HTML contract and the view.

sbt
enablePlugins(ScalaJSPlugin) libraryDependencies ++= Seq( "com.anjunar" %% "scalajs-ember-core" % "1.0.3", "com.anjunar" %% "scalajs-ember-rich-text" % "1.0.3", "com.anjunar" %% "scalajs-ember-html" % "1.0.3", "com.anjunar" %% "scalajs-ember-ui" % "1.0.3" )

Edit it

This editor was rendered on the server and hydrated in your browser. Type, format and undo; the inspector below shows the same document in four forms.

Writing with Ember

Ember keeps one immutable document. Every keystroke becomes a transaction, and every view is derived from it.

  • Select a word and press Ctrl+B

  • Turn this line into a heading with the toolbar

  • Undo it again with Ctrl+Z

Markdown, JSON and HTML below are read from the same document, live.

session.dispatch(RichText.ToggleMark, StandardMarks.Strong)
56 words · 26 nodes
# Writing with Ember Ember keeps one **immutable document**. Every keystroke becomes a *transaction*, and every view is derived from it. - Select a word and press **Ctrl+B** - Turn this line into a heading with the toolbar - Undo it again with **Ctrl+Z** > Markdown, JSON and HTML below are read from the same document, live. ```scala session.dispatch(RichText.ToggleMark, StandardMarks.Strong) ```

The smallest program

Resolve the extensions, build an empty document, open a session, mount the view and dispatch a command.

scala
import ember.editor.core.* import ember.editor.richtext.* import ember.editor.standard.ParagraphSupport import ember.editor.ui.DocumentView import ui.core.render.DomCursor import org.scalajs.dom val generator = NodeIdGenerator.sequential() val resolved = ExtensionResolver.resolve(Vector(RichText(generator))).getOrElse(sys.error("extensions")) val document = RichText.emptyDocument(resolved.schema, generator).getOrElse(sys.error("document")) val session = EditorSession.create(document, resolved, resolved.sessionConfig()).getOrElse(sys.error("session")) val view = DocumentView.mount(session, DomCursor.root(dom.document.getElementById("root")), ParagraphSupport.views) session.update(_.setSelection(RichText.caretAtStart(session.document))) session.dispatch(RichText.InsertText, "Hello, Ember") session.dispatch(RichText.ToggleMark, StandardMarks.Strong)
01 / Document

Immutable and always valid

A Document only comes from Document.build and already satisfies its invariants. Holding one means nothing more needs checking.

02 / Transaction

Every change is one commit

Commands turn intent into transactions. A transaction yields the new document, a change set and a position mapping in one step.

03 / View

Rendered once, claimed by the browser

The same semantics render the page on the server and the editable surface in the browser. Hydration claims the page instead of rebuilding it.

Contents

From the document to the browser

The model pages explain what an edit is; the format pages how a document travels; the browser pages how it becomes editable.