diff --git a/.blade.yml b/.blade.yml
deleted file mode 100644
index ef231c707..000000000
--- a/.blade.yml
+++ /dev/null
@@ -1,40 +0,0 @@
-load_paths:
- - test/src
- - test/vendor
- - assets
- - polyfills
- - src
-
-logical_paths:
- - trix.js
- - test.js
- - trix.css
-
-build:
- logical_paths:
- - trix.js
- - trix-core.js
- - trix.css
- path: dist
- js_compressor: uglifier
-
-plugins:
- sauce_labs:
- browsers:
- Google Chrome:
- os: Mac, Windows
- version: -2
- Firefox:
- os: Mac, Windows
- version: -2
- Safari:
- platform: Mac
- version: -3
- Microsoft Edge:
- version: -2
- Internet Explorer:
- version: 11
- iPhone:
- version: [9.2, 8.4]
- Motorola Droid 4 Emulator:
- version: [5.1, 4.4]
diff --git a/.eslintrc b/.eslintrc
new file mode 100644
index 000000000..c8a112d41
--- /dev/null
+++ b/.eslintrc
@@ -0,0 +1,47 @@
+{
+ "parser": "babel-eslint",
+ "extends": "eslint:recommended",
+ "rules": {
+ "array-bracket-spacing": ["error", "always"],
+ "block-spacing": ["error", "always"],
+ "camelcase": ["error"],
+ "comma-spacing": ["error"],
+ "curly": ["error", "multi-line"],
+ "dot-notation": ["error"],
+ "eol-last": ["error"],
+ "getter-return": ["error"],
+ "id-length": ["error", { "properties": "never", "exceptions": ["_", "i", "j", "n"] }],
+ "keyword-spacing": ["error"],
+ "no-extra-parens": ["error"],
+ "no-multi-spaces": ["error", { "exceptions": { "VariableDeclarator": true } }],
+ "no-multiple-empty-lines": ["error", { "max": 2 }],
+ "no-restricted-globals": ["error", "event"],
+ "no-trailing-spaces": ["error"],
+ "no-unused-vars": ["error", { "vars": "all", "args": "none" }],
+ "no-var": ["error"],
+ "object-curly-spacing": ["error", "always"],
+ "prefer-const": ["error"],
+ "quotes": ["error", "double"],
+ "semi": ["error", "never"],
+ "sort-imports": ["error", { "ignoreDeclarationSort": true }]
+ },
+ "ignorePatterns": ["dist/**/*.js", "**/vendor/**/*.js", "action_text-trix/**/*.js"],
+ "globals": {
+ "after": true,
+ "getComposition": true,
+ "getDocument": true,
+ "getEditor": true,
+ "getEditorController": true,
+ "getEditorElement": true,
+ "getSelectionManager": true,
+ "getToolbarElement": true,
+ "QUnit": true,
+ "rangy": true,
+ "Trix": true
+ },
+ "env": {
+ "browser": true,
+ "node": true,
+ "es6": true
+ }
+}
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 000000000..274472510
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,41 @@
+version: 2
+
+updates:
+ - package-ecosystem: github-actions
+ directory: "/"
+ groups:
+ github-actions:
+ patterns:
+ - "*"
+ schedule:
+ interval: weekly
+ cooldown:
+ default-days: 7
+
+ - package-ecosystem: npm
+ directory: "/"
+ groups:
+ npm:
+ patterns:
+ - "*"
+ schedule:
+ interval: weekly
+ cooldown:
+ semver-major-days: 7
+ semver-minor-days: 3
+ semver-patch-days: 2
+ default-days: 7
+
+ - package-ecosystem: bundler
+ directory: "/action_text-trix"
+ groups:
+ bundler:
+ patterns:
+ - "*"
+ schedule:
+ interval: weekly
+ cooldown:
+ semver-major-days: 7
+ semver-minor-days: 3
+ semver-patch-days: 2
+ default-days: 7
diff --git a/.github/stale.yml b/.github/stale.yml
new file mode 100644
index 000000000..dc472ee1a
--- /dev/null
+++ b/.github/stale.yml
@@ -0,0 +1,23 @@
+# Number of days of inactivity before an issue becomes stale
+daysUntilStale: 90
+# Number of days of inactivity before a stale issue is closed
+daysUntilClose: 7
+# Issues with these labels will never be considered stale
+exemptLabels:
+ - bug
+ - enhancement
+ - pinned
+ - security
+ - WIP
+# Label to use when marking an issue as stale
+staleLabel: stale
+# Comment to post when marking an issue as stale. Set to `false` to disable
+markComment: >
+ This issue has been automatically marked as stale after 90 days of inactivity.
+ It will be closed if no further activity occurs.
+pulls:
+ markComment: >
+ This pull request has been automatically marked as stale after 90 days of inactivity.
+ It will be closed if no further activity occurs.
+# Comment to post when closing a stale issue. Set to `false` to disable
+closeComment: false
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 000000000..8c5f29682
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,175 @@
+name: CI
+
+concurrency:
+ group: "${{github.workflow}}-${{github.ref}}"
+ cancel-in-progress: true
+
+on:
+ workflow_dispatch:
+ push:
+ branches: [ main ]
+ pull_request:
+ types: [opened, synchronize]
+ branches: [ '*' ]
+
+permissions: {}
+
+env:
+ SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }}
+ SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }}
+ SAUCE_REGION: us
+ SAUCE_TUNNEL_IDENTIFIER: trix-${{ github.run_id }}
+
+jobs:
+ lint-actions:
+ name: GitHub Actions audit
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+
+ steps:
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
+
+ - name: Run actionlint
+ uses: rhysd/actionlint@914e7df21a07ef503a81201c76d2b11c789d3fca # v1.7.12
+
+ - name: Run zizmor
+ uses: zizmorcore/zizmor-action@b1d7e1fb5de872772f31590499237e7cce841e8e # v0.5.3
+ with:
+ advanced-security: false
+
+ build:
+ name: Browser tests
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ steps:
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
+ - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
+ with:
+ node-version: 18
+ cache: "yarn"
+ - name: Install Dependencies
+ run: yarn install --frozen-lockfile
+ - name: Start Sauce Connect
+ if: ${{ env.SAUCE_ACCESS_KEY != '' }}
+ uses: saucelabs/sauce-connect-action@cb88b508c6f9ff4d84490093733315dbd55de022 # v3
+ with:
+ username: ${{ env.SAUCE_USERNAME }}
+ accessKey: ${{ env.SAUCE_ACCESS_KEY }}
+ tunnelName: ${{ env.SAUCE_TUNNEL_IDENTIFIER }}
+ region: ${{ env.SAUCE_REGION }}
+ proxyLocalhost: allow
+ - name: Install Playwright Browsers
+ if: ${{ env.SAUCE_ACCESS_KEY == '' }}
+ run: npx playwright install --with-deps chromium
+ - run: bin/ci
+ - name: Fail when generated npm changes are not checked-in
+ run: |
+ git update-index --refresh && git diff-index --quiet HEAD --
+
+ rails-tests:
+ name: Downstream Rails integration tests
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ steps:
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
+ - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
+ with:
+ node-version: 18
+ cache: "yarn"
+ - uses: ruby/setup-ruby-pkgs@2233d39c1315c667a2970436418b520a6300124e # v1.33.5
+ with:
+ ruby-version: "3.4"
+ apt-get: libvips-tools
+ - name: Install Dependencies
+ run: yarn install --frozen-lockfile
+ - name: Packaging
+ run: yarn build
+ - name: Clone Rails
+ run: git clone --depth=1 https://github.com/rails/rails
+ - name: Configure Rails
+ run: |
+ cd rails
+ yarn install --frozen-lockfile
+ bundle add action_text-trix --path ".."
+ bundle show --paths action_text-trix
+ - name: Action Text tests
+ env:
+ RACK_ENV: test # see https://github.com/rails/rails/issues/56563
+ run: |
+ cd rails/actiontext
+ bundle exec rake test test:system
+ action_text-trix:
+ name: Action Text tests
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - ruby: "3.2"
+ rails_branch: 7-2-stable
+ - ruby: "3.3"
+ rails_branch: 7-2-stable
+ - ruby: "3.4"
+ rails_branch: 7-2-stable
+
+ - ruby: "3.2"
+ rails_branch: 8-0-stable
+ - ruby: "3.3"
+ rails_branch: 8-0-stable
+ - ruby: "3.4"
+ rails_branch: 8-0-stable
+
+ - ruby: "3.2"
+ rails_branch: 8-1-stable
+ - ruby: "3.3"
+ rails_branch: 8-1-stable
+ - ruby: "3.4"
+ rails_branch: 8-1-stable
+ - ruby: "4.0"
+ rails_branch: 8-1-stable
+
+ - ruby: "3.3"
+ rails_branch: main
+ experimental: true
+ - ruby: "3.4"
+ rails_branch: main
+ experimental: true
+ - ruby: "4.0"
+ rails_branch: main
+ experimental: true
+
+ - ruby: head
+ rails_branch: main
+ experimental: true
+ steps:
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
+ - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
+ with:
+ node-version: 18
+ cache: "yarn"
+ - uses: ruby/setup-ruby@7372622e62b60b3cb750dcd2b9e32c247ffec26a # v1.302.0
+ env:
+ RAILS_BRANCH: ${{ matrix.rails_branch }}
+ with:
+ working-directory: "./action_text-trix"
+ ruby-version: ${{ matrix.ruby }}
+ bundler-cache: true
+ - name: Run tests
+ env:
+ RAILS_BRANCH: ${{ matrix.rails_branch }}
+ continue-on-error: ${{ matrix.experimental || false }}
+ working-directory: "./action_text-trix"
+ run: bin/rails test:all
diff --git a/.gitignore b/.gitignore
index 46bc1c30e..9ad4f258e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
-/.rbenv-vars
+yarn-error.log
+package-lock.json
+/dist
/node_modules
/tmp
diff --git a/.node-version b/.node-version
new file mode 100644
index 000000000..02c8b485e
--- /dev/null
+++ b/.node-version
@@ -0,0 +1 @@
+18.18.0
diff --git a/.npmignore b/.npmignore
new file mode 100644
index 000000000..00a6d7509
--- /dev/null
+++ b/.npmignore
@@ -0,0 +1,7 @@
+.DS_Store
+/node_modules
+/.github
+/bin
+/assets
+yarn-error.log
+yarn.lock
diff --git a/.ruby-version b/.ruby-version
deleted file mode 100644
index eca07e4c1..000000000
--- a/.ruby-version
+++ /dev/null
@@ -1 +0,0 @@
-2.1.2
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 46766e0d6..000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-language: ruby
-cache: bundler
-script: bin/ci
-branches:
- only:
- - master
diff --git a/CONDUCT.md b/CODE_OF_CONDUCT.md
similarity index 97%
rename from CONDUCT.md
rename to CODE_OF_CONDUCT.md
index 2b97d72e4..2f7a5edbc 100644
--- a/CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -55,7 +55,7 @@ further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
-reported by contacting the project team at [INSERT EMAIL ADDRESS]. All
+reported by contacting the [project maintainers](#project-maintainers). All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
diff --git a/Gemfile b/Gemfile
deleted file mode 100644
index c37768df1..000000000
--- a/Gemfile
+++ /dev/null
@@ -1,17 +0,0 @@
-source 'https://rubygems.org'
-
-gem 'rake'
-gem 'sprockets'
-gem 'coffee-script'
-gem 'coffee-script-source', '~> 1.9.1'
-gem 'eco'
-gem 'sass'
-gem 'uglifier'
-
-gem 'blade'
-gem 'blade-sauce_labs_plugin'
-# Lock to 1.1.1 until the fix for https://github.com/faye/faye/issues/394 is released
-gem 'faye', '1.1.1'
-
-gem 'github_api'
-gem 'aws-sdk'
diff --git a/Gemfile.lock b/Gemfile.lock
deleted file mode 100644
index 103f02b28..000000000
--- a/Gemfile.lock
+++ /dev/null
@@ -1,150 +0,0 @@
-GEM
- remote: https://rubygems.org/
- specs:
- activesupport (4.2.6)
- i18n (~> 0.7)
- json (~> 1.7, >= 1.7.7)
- minitest (~> 5.1)
- thread_safe (~> 0.3, >= 0.3.4)
- tzinfo (~> 1.1)
- addressable (2.4.0)
- aws-sdk (2.2.8)
- aws-sdk-resources (= 2.2.8)
- aws-sdk-core (2.2.8)
- jmespath (~> 1.0)
- aws-sdk-resources (2.2.8)
- aws-sdk-core (= 2.2.8)
- blade (0.5.6)
- activesupport (>= 3.0.0)
- blade-qunit_adapter (~> 1.20.0)
- coffee-script
- coffee-script-source
- curses (~> 1.0.0)
- eventmachine
- faye
- sprockets (>= 3.0)
- sprockets-export (~> 0.9.1)
- thin (>= 1.6.0)
- thor (~> 0.19.1)
- useragent (~> 0.16.7)
- blade-qunit_adapter (1.20.0)
- blade-sauce_labs_plugin (0.5.2)
- childprocess
- faraday
- selenium-webdriver
- childprocess (0.5.9)
- ffi (~> 1.0, >= 1.0.11)
- coffee-script (2.4.1)
- coffee-script-source
- execjs
- coffee-script-source (1.9.3)
- concurrent-ruby (1.0.2)
- cookiejar (0.3.3)
- curses (1.0.2)
- daemons (1.2.3)
- descendants_tracker (0.0.4)
- thread_safe (~> 0.3, >= 0.3.1)
- eco (1.0.0)
- coffee-script
- eco-source
- execjs
- eco-source (1.1.0.rc.1)
- em-http-request (1.1.4)
- addressable (>= 2.3.4)
- cookiejar (!= 0.3.1)
- em-socksify (>= 0.3)
- eventmachine (>= 1.0.3)
- http_parser.rb (>= 0.6.0)
- em-socksify (0.3.1)
- eventmachine (>= 1.0.0.beta.4)
- eventmachine (1.2.0.1)
- execjs (2.7.0)
- faraday (0.9.2)
- multipart-post (>= 1.2, < 3)
- faye (1.1.1)
- cookiejar (>= 0.3.0)
- em-http-request (>= 0.3.0)
- eventmachine (>= 0.12.0)
- faye-websocket (>= 0.9.1)
- multi_json (>= 1.0.0)
- rack (>= 1.0.0)
- websocket-driver (>= 0.5.1)
- faye-websocket (0.10.4)
- eventmachine (>= 0.12.0)
- websocket-driver (>= 0.5.1)
- ffi (1.9.10)
- github_api (0.13.0)
- addressable (~> 2.3)
- descendants_tracker (~> 0.0.4)
- faraday (~> 0.8, < 0.10)
- hashie (>= 3.4)
- multi_json (>= 1.7.5, < 2.0)
- nokogiri (~> 1.6.6)
- oauth2
- hashie (3.4.3)
- http_parser.rb (0.6.0)
- i18n (0.7.0)
- jmespath (1.1.3)
- json (1.8.3)
- jwt (1.5.2)
- mini_portile2 (2.0.0)
- minitest (5.9.0)
- multi_json (1.12.1)
- multi_xml (0.5.5)
- multipart-post (2.0.0)
- nokogiri (1.6.7)
- mini_portile2 (~> 2.0.0.rc2)
- oauth2 (1.0.0)
- faraday (>= 0.8, < 0.10)
- jwt (~> 1.0)
- multi_json (~> 1.3)
- multi_xml (~> 0.5)
- rack (~> 1.2)
- rack (1.6.4)
- rake (10.0.4)
- rubyzip (1.2.0)
- sass (3.4.3)
- selenium-webdriver (2.53.1)
- childprocess (~> 0.5)
- rubyzip (~> 1.0)
- websocket (~> 1.0)
- sprockets (3.6.0)
- concurrent-ruby (~> 1.0)
- rack (> 1, < 3)
- sprockets-export (0.9.1)
- thin (1.7.0)
- daemons (~> 1.0, >= 1.0.9)
- eventmachine (~> 1.0, >= 1.0.4)
- rack (>= 1, < 3)
- thor (0.19.1)
- thread_safe (0.3.5)
- tzinfo (1.2.2)
- thread_safe (~> 0.1)
- uglifier (2.5.1)
- execjs (>= 0.3.0)
- json (>= 1.8.0)
- useragent (0.16.7)
- websocket (1.2.3)
- websocket-driver (0.6.4)
- websocket-extensions (>= 0.1.0)
- websocket-extensions (0.1.2)
-
-PLATFORMS
- ruby
-
-DEPENDENCIES
- aws-sdk
- blade
- blade-sauce_labs_plugin
- coffee-script
- coffee-script-source (~> 1.9.1)
- eco
- faye (= 1.1.1)
- github_api
- rake
- sass
- sprockets
- uglifier
-
-BUNDLED WITH
- 1.10.6
diff --git a/LICENSE b/LICENSE
index a4910677e..ba437271f 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2015 Basecamp, LLC
+Copyright (c) 37signals, LLC
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
diff --git a/README.md b/README.md
index 0805c4df7..323f98e7d 100644
--- a/README.md
+++ b/README.md
@@ -3,39 +3,49 @@
**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 [Basecamp](https://basecamp.com/), the creators of [Ruby on Rails](http://rubyonrails.org/). Millions of people trust their text to Basecamp, and we built Trix to give them the best possible editing experience. See Trix in action in the [all-new Basecamp 3](https://basecamp.com/3-is-coming).
+Trix is an open-source project from [37signals](https://37signals.com), the creators of [Ruby on Rails](http://rubyonrails.org/). 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](https://basecamp.com).
### Different By Design
-Most WYSIWYG editors are 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](https://blog.whatwg.org/the-road-to-html-5-contenteditable#history) and copied by other browsers.
+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](https://blog.whatwg.org/the-road-to-html-5-contenteditable#history) and copied by other browsers.
-Because these APIs were never 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.
+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 sidesteps 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.
+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.
-### Built for the Modern Web
+This is the approach that all modern, production ready, WYSIWYG editors now take.
-Trix supports all evergreen, self-updating desktop and mobile browsers.
+### Built on Web standards
-
+Trix supports all evergreen, self-updating desktop and mobile browsers.