MCPcopy
hub / github.com/basecamp/trix

github.com/basecamp/trix @v2.1.19 sqlite

repository ↗ · DeepWiki ↗ · release v2.1.19 ↗
2,421 symbols 6,980 edges 190 files 7 documented · 0%
README

Trix

A Rich Text Editor for Everyday Writing

Compose beautifully formatted text in your web application. Trix is a WYSIWYG editor for writing messages, comments, articles, and lists—the simple documents most web apps are made of. It features a sophisticated document model, support for embedded attachments, and outputs terse and consistent HTML.

Trix is an open-source project from 37signals, the creators of Ruby on Rails. Millions of people trust their text to us, and we built Trix to give them the best possible editing experience. See Trix in action in Basecamp.

Different By Design

When Trix was designed in 2014, most WYSIWYG editors were wrappers around HTML’s contenteditable and execCommand APIs, designed by Microsoft to support live editing of web pages in Internet Explorer 5.5, and eventually reverse-engineered and copied by other browsers.

Because these APIs were not fully specified or documented, and because WYSIWYG HTML editors are enormous in scope, each browser’s implementation has its own set of bugs and quirks, and JavaScript developers are left to resolve the inconsistencies.

Trix sidestepped these inconsistencies by treating contenteditable as an I/O device: when input makes its way to the editor, Trix converts that input into an editing operation on its internal document model, then re-renders that document back into the editor. This gives Trix complete control over what happens after every keystroke, and avoids the need to use execCommand at all.

This is the approach that all modern, production ready, WYSIWYG editors now take.

Built on Web standards

Trix supports all evergreen, self-updating desktop and mobile browsers.

Trix is built with established web standards, notably Custom Elements, Element Internals, Mutation Observer, and Promises.

Getting Started

Trix comes bundled in ESM and UMD formats and works with any asset packaging system.

The easiest way to start with Trix is including it from an npm CDN in the <head> of your page:

<head>
  …
  <link rel="stylesheet" type="text/css" href="https://unpkg.com/trix@2.0.8/dist/trix.css">
  <script type="text/javascript" src="https://unpkg.com/trix@2.0.8/dist/trix.umd.min.js"></script>
</head>

trix.css includes default styles for the Trix toolbar, editor, and attachments. Skip this file if you prefer to define these styles yourself.

Alternatively, you can install the npm package and import it in your application:

import Trix from "trix"

document.addEventListener("trix-before-initialize", () => {
  // Change Trix.config if you need
})

Creating an Editor

Place an empty <trix-editor></trix-editor> tag on the page. Trix will automatically insert a separate <trix-toolbar> before the editor.

Like an HTML <textarea>, <trix-editor> accepts autofocus and placeholder attributes. Unlike a <textarea>, <trix-editor> automatically expands vertically to fit its contents.

Creating a Toolbar

Trix automatically will create a toolbar for you and attach it right before the <trix-editor> element. If you'd like to place the toolbar in a different place you can use the toolbar attribute:

<main>
  <trix-toolbar id="my_toolbar"></trix-toolbar>





  <trix-editor toolbar="my_toolbar" input="my_input"></trix-editor>
</main>

To change the toolbar without modifying Trix, you can overwrite the Trix.config.toolbar.getDefaultHTML() function. The default toolbar HTML is in config/toolbar.js. Trix uses data attributes to determine how to respond to a toolbar button click.

Toggle Attribute

With data-trix-attribute="<attribute name>", you can add an attribute to the current selection. For example, to apply bold styling to the selected text the button is:

<button type="button" class="bold" data-trix-attribute="bold" data-trix-key="b"></button>

Trix will determine that a range of text is selected and will apply the formatting defined in Trix.config.textAttributes (found in config/text_attributes.js).

data-trix-key="b" tells Trix that this attribute should be applied when you use meta+b.

If the attribute is defined in Trix.config.blockAttributes, Trix will apply the attribute to the current block of text.

<button type="button" class="quote" data-trix-attribute="quote"></button>

Clicking the quote button toggles whether the block should be rendered with <blockquote>.

Integrating with Element Internals

Trix will integrate <trix-editor> elements with forms depending on the browser's support for Element Internals. If there is a need to disable support for ElementInternals, set Trix.elements.TrixEditorElement.formAssociated = false:

import Trix from "trix"

Trix.elements.TrixEditorElement.formAssociated = false

When Trix is configured to be compatible with ElementInternals, it is also capable of functioning without an <input type="hidden"> element. To configure a <trix-editor> element to skip creating its <input type="hidden">, set the element's willCreateInput = false:

addEventListener("before-trix-initialize", (event) => {
  const trixEditor = event.target

  trixEditor.willCreateInput = false
})

[!NOTE] Trix will always use an associated <input type="hidden"> element when the [input] attribute is set. To migrate to <input>-free support, set willCreateInput = false, then render the <trix-editor> without the [input] attribute.

[!WARNING] In the absence of an <input type="hidden"> element, the <trix-editor> element's value will not be included in <form> element submissions unless it is rendered with a [name] attribute. Set the [name] attribute to the same value that the <input type="hidden"> element would have.

Invoking Internal Trix Actions

Internal actions are defined in controllers/editor_controller.js and consist of:

  • undo
  • redo
  • link
  • increaseBlockLevel
  • decreaseBlockLevel
<button type="button" class="block-level decrease" data-trix-action="decreaseBlockLevel"></button>

Invoking External Custom Actions

If you want to add a button to the toolbar and have it invoke an external action, you can prefix your action name with x-. For example, if I want to print a log statement any time my new button is clicked, I would set by button's data attribute to be data-trix-action="x-log"

<button id="log-button" type="button" data-trix-action="x-log"></button>

To respond to the action, listen for trix-action-invoke. The event's target property returns a reference to the <trix-editor> element, its invokingElement property returns a reference to the <button> element, and its actionName property returns the value of the [data-trix-action] attribute. Use the value of the actionName property to detect which external action was invoked.

document.addEventListener("trix-action-invoke", function(event) {
  const { target, invokingElement, actionName } = event

  if (actionName === "x-log") {
    console.log(`Custom ${actionName} invoked from ${invokingElement.id} button on ${target.id} trix-editor`)
  }
})

Integrating With Forms

To submit the contents of a <trix-editor> with a form, first define a hidden input field in the form and assign it an id. Then reference that id in the editor’s input attribute.

<form …>
  <input id="x" type="hidden" name="content">
  <trix-editor input="x"></trix-editor>
</form>

Trix will automatically update the value of the hidden input field with each change to the editor.

Populating With Stored Content

To populate a <trix-editor> with stored content, include that content in the associated input element’s value attribute.

<form …>
  <input id="x" value="Editor content goes here" type="hidden" name="content">
  <trix-editor input="x"></trix-editor>
</form>

Use an associated input element to initially populate an editor. When an associated input element is absent, Trix will safely sanitize then load any HTML content inside a <trix-editor>…</trix-editor> tag.

<form …>
  <trix-editor>Editor content goes here</trix-editor>
</form>

[!WARNING] When a <trix-editor> element initially connects with both HTML content and an associated input element, Trix will always disregard the HTML content and load its initial content from the associated input element.

Validating the Editor

Out of the box, <trix-editor> elements support browsers' built-in Constraint validation. When rendered with the required attribute, editors will be invalid when they're completely empty. For example, consider the following HTML:

<input id="x" value="" type="hidden" name="content">
<trix-editor input="x" required></trix-editor>

Since the <trix-editor> element is [required], it is invalid when its value is empty:

const editor = document.querySelector("trix-editor")

editor.validity.valid        // => false
editor.validity.valueMissing // => true
editor.matches(":valid")     // => false
editor.matches(":invalid")   // => true

editor.value = "A value that isn't empty"

editor.validity.valid         // => true
editor.validity.valueMissing  // => false
editor.matches(":valid")      // => true
editor.matches(":invalid")    // => false

In addition to the built-in [required] attribute, <trix-editor> elements support custom validation through their setCustomValidity method. For example, consider the following HTML:

<input id="x" value="" type="hidden" name="content">
<trix-editor input="x"></trix-editor>

Custom validation can occur at any time. For example, validation can occur after a trix-change event fired after the editor's contents change:

addEventListener("trix-change", (event) => {
  const editorElement = event.target
  const trixDocument = editorElement.editor.getDocument()
  const isValid = (trixDocument) => {
    // determine the validity based on your custom criteria
    return true
  }

  if (isValid(trixDocument)) {
    editorElement.setCustomValidity("")
  } else {
    editorElement.setCustomValidity("The document is not valid.")
  }
}

Disabling the Editor

To disable the <trix-editor>, render it with the [disabled] attribute:

<trix-editor disabled></trix-editor>

Disabled editors are not editable, cannot receive focus, and their values will be ignored when their related <form> element is submitted.

To change whether or not an editor is disabled, either toggle the [disabled] attribute or assign a boolean to the .disabled property:

<trix-editor id="editor" disabled></trix-editor>

<script>
  const editor = document.getElementById("editor")

  editor.toggleAttribute("disabled", false)
  editor.disabled = true
</script>

When disabled, the editor will match the :disabled CSS pseudo-class.

Providing an Accessible Name

Like other form controls, <trix-editor> elements should have an accessible name. The <trix-editor> element integrates with <label> elements. It supports two styles of integrating with <label> elements:

  1. render the <trix-editor> element with an [id] attribute that the <label> element references through its [for] attribute:
<label for="editor">Editor</label>
<trix-editor id="editor"></trix-editor>
  1. render the <trix-editor> element as a child of the <label> element:
<trix-toolbar id="editor-toolbar"></trix-toolbar>
<label>
  Editor

  <trix-editor toolbar="editor-toolbar"></trix-editor>
</label>

[!WARNING] When rendering the <trix-editor> element as a child of the <label> element, explicitly render the corresponding <trix-toolbar> element outside of the <label> element.

In addition to integrating with <label> elements, <trix-editor> elements support [aria-label] and [aria-labelledby] attributes.

Styling Formatted Content

To ensure what you see when you edit is what you see when you save, use a CSS class name to scope styles for Trix formatted content. Apply this class name to your <trix-editor> element, and to a containing element when you render stored Trix content for display in your application.

<trix-editor class="trix-content"></trix-editor>


Stored content here


The default trix.css file includes styles for basic formatted content—including bulleted and numbered lists, code blocks, and block quotes—under the class name trix-content. We encourage you to use these styles as a starting point by copying them into your application’s CSS with a different class name.

Storing Attached Files

Trix automatically accepts files dragged or pasted into an editor and inserts them as attachments in the document. Each attachment is considered pending until you store it remotely and provide Trix with

Core symbols most depended-on inside this repo

typeCharacters
called by 143
src/test/test_helpers/input_helpers.js
expectDocument
called by 133
src/test/test_helpers/assertions.js
clickToolbarButton
called by 128
src/test/test_helpers/toolbar_helpers.js
triggerEvent
called by 104
src/test/test_helpers/event_helpers.js
nextFrame
called by 104
src/test/test_helpers/timing_helpers.js
getBlockAtIndex
called by 78
src/trix/models/document.js
makeElement
called by 57
src/trix/core/helpers/dom.js
testGroup
called by 56
src/test/test_helpers/test_helpers.js

Shape

Method 1,591
Function 605
Class 225

Languages

TypeScript100%

Modules by API surface

action_text-trix/app/assets/javascripts/trix.js880 symbols
src/trix/models/composition.js86 symbols
src/trix/controllers/level_2_input_controller.js84 symbols
src/trix/controllers/editor_controller.js80 symbols
src/trix/elements/trix_editor_element.js76 symbols
src/trix/models/document.js71 symbols
src/trix/controllers/level_0_input_controller.js53 symbols
src/trix/models/block.js52 symbols
src/trix/models/text.js47 symbols
src/trix/models/html_parser.js43 symbols
src/trix/models/editor.js41 symbols
src/trix/models/attachment.js39 symbols

Used by 1 indexed graphs manifest dependencies, hub-wide

Dependencies from manifests, versioned

@babel/core7.16.0 · 1×
@babel/preset-env7.16.4 · 1×
@rollup/plugin-babel5.3.0 · 1×
@rollup/plugin-commonjs22.0.2 · 1×
@rollup/plugin-json4.1.0 · 1×
@rollup/plugin-node-resolve13.3.0 · 1×
@web/dev-server0.1.34 · 1×
@web/test-runner0.20.2 · 1×
@web/test-runner-playwright0.11.1 · 1×
@web/test-runner-webdriver0.9.0 · 1×
babel-eslint10.1.0 · 1×
chokidar4.0.2 · 1×

For agents

$ claude mcp add trix \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact