Repository: slab/quill Branch: main Commit: 539cbffd0a13 Files: 260 Total size: 968.9 KB Directory structure: gitextract_t0yo5mmz/ ├── .eslintignore ├── .github/ │ ├── CODE_OF_CONDUCT.md │ ├── CONTRIBUTING.md │ ├── DEVELOPMENT.md │ ├── ISSUE_TEMPLATE.md │ ├── release.yml │ └── workflows/ │ ├── _test.yml │ ├── changelog.yml │ ├── label.yml │ ├── main.yml │ ├── pull-request.yml │ └── release.yml ├── .gitignore ├── .npmignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── package.json ├── packages/ │ ├── quill/ │ │ ├── .eslintrc.json │ │ ├── .gitignore │ │ ├── LICENSE │ │ ├── README.md │ │ ├── babel.config.cjs │ │ ├── package.json │ │ ├── playwright.config.ts │ │ ├── scripts/ │ │ │ ├── babel-svg-inline-import.cjs │ │ │ └── build │ │ ├── src/ │ │ │ ├── assets/ │ │ │ │ ├── base.styl │ │ │ │ ├── bubble/ │ │ │ │ │ ├── toolbar.styl │ │ │ │ │ └── tooltip.styl │ │ │ │ ├── bubble.styl │ │ │ │ ├── core.styl │ │ │ │ ├── snow/ │ │ │ │ │ ├── toolbar.styl │ │ │ │ │ └── tooltip.styl │ │ │ │ └── snow.styl │ │ │ ├── blots/ │ │ │ │ ├── block.ts │ │ │ │ ├── break.ts │ │ │ │ ├── container.ts │ │ │ │ ├── cursor.ts │ │ │ │ ├── embed.ts │ │ │ │ ├── inline.ts │ │ │ │ ├── scroll.ts │ │ │ │ └── text.ts │ │ │ ├── core/ │ │ │ │ ├── composition.ts │ │ │ │ ├── editor.ts │ │ │ │ ├── emitter.ts │ │ │ │ ├── instances.ts │ │ │ │ ├── logger.ts │ │ │ │ ├── module.ts │ │ │ │ ├── quill.ts │ │ │ │ ├── selection.ts │ │ │ │ ├── theme.ts │ │ │ │ └── utils/ │ │ │ │ ├── createRegistryWithFormats.ts │ │ │ │ └── scrollRectIntoView.ts │ │ │ ├── core.ts │ │ │ ├── formats/ │ │ │ │ ├── align.ts │ │ │ │ ├── background.ts │ │ │ │ ├── blockquote.ts │ │ │ │ ├── bold.ts │ │ │ │ ├── code.ts │ │ │ │ ├── color.ts │ │ │ │ ├── direction.ts │ │ │ │ ├── font.ts │ │ │ │ ├── formula.ts │ │ │ │ ├── header.ts │ │ │ │ ├── image.ts │ │ │ │ ├── indent.ts │ │ │ │ ├── italic.ts │ │ │ │ ├── link.ts │ │ │ │ ├── list.ts │ │ │ │ ├── script.ts │ │ │ │ ├── size.ts │ │ │ │ ├── strike.ts │ │ │ │ ├── table.ts │ │ │ │ ├── underline.ts │ │ │ │ └── video.ts │ │ │ ├── modules/ │ │ │ │ ├── clipboard.ts │ │ │ │ ├── history.ts │ │ │ │ ├── input.ts │ │ │ │ ├── keyboard.ts │ │ │ │ ├── normalizeExternalHTML/ │ │ │ │ │ ├── index.ts │ │ │ │ │ └── normalizers/ │ │ │ │ │ ├── googleDocs.ts │ │ │ │ │ └── msWord.ts │ │ │ │ ├── syntax.ts │ │ │ │ ├── table.ts │ │ │ │ ├── tableEmbed.ts │ │ │ │ ├── toolbar.ts │ │ │ │ ├── uiNode.ts │ │ │ │ └── uploader.ts │ │ │ ├── quill.ts │ │ │ ├── themes/ │ │ │ │ ├── base.ts │ │ │ │ ├── bubble.ts │ │ │ │ └── snow.ts │ │ │ ├── types.d.ts │ │ │ └── ui/ │ │ │ ├── color-picker.ts │ │ │ ├── icon-picker.ts │ │ │ ├── icons.ts │ │ │ ├── picker.ts │ │ │ └── tooltip.ts │ │ ├── test/ │ │ │ ├── e2e/ │ │ │ │ ├── __dev_server__/ │ │ │ │ │ ├── index.html │ │ │ │ │ └── webpack.config.cjs │ │ │ │ ├── fixtures/ │ │ │ │ │ ├── Clipboard.ts │ │ │ │ │ ├── Composition.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── utils/ │ │ │ │ │ └── Locker.ts │ │ │ │ ├── full.spec.ts │ │ │ │ ├── history.spec.ts │ │ │ │ ├── list.spec.ts │ │ │ │ ├── pageobjects/ │ │ │ │ │ └── EditorPage.ts │ │ │ │ ├── replaceSelection.spec.ts │ │ │ │ └── utils/ │ │ │ │ └── index.ts │ │ │ ├── fuzz/ │ │ │ │ ├── __helpers__/ │ │ │ │ │ └── utils.ts │ │ │ │ ├── editor.spec.ts │ │ │ │ ├── tableEmbed.spec.ts │ │ │ │ └── vitest.config.ts │ │ │ ├── types/ │ │ │ │ └── quill.test-d.ts │ │ │ └── unit/ │ │ │ ├── __helpers__/ │ │ │ │ ├── cleanup.ts │ │ │ │ ├── expect.ts │ │ │ │ ├── factory.ts │ │ │ │ ├── utils.ts │ │ │ │ └── vitest.d.ts │ │ │ ├── blots/ │ │ │ │ ├── block-embed.spec.ts │ │ │ │ ├── block.spec.ts │ │ │ │ ├── inline.spec.ts │ │ │ │ └── scroll.spec.ts │ │ │ ├── core/ │ │ │ │ ├── composition.spec.ts │ │ │ │ ├── editor.spec.ts │ │ │ │ ├── emitter.spec.ts │ │ │ │ ├── quill.spec.ts │ │ │ │ ├── selection.spec.ts │ │ │ │ └── utils/ │ │ │ │ └── createRegistryWithFormats.spec.ts │ │ │ ├── formats/ │ │ │ │ ├── align.spec.ts │ │ │ │ ├── bold.spec.ts │ │ │ │ ├── code.spec.ts │ │ │ │ ├── color.spec.ts │ │ │ │ ├── header.spec.ts │ │ │ │ ├── indent.spec.ts │ │ │ │ ├── link.spec.ts │ │ │ │ ├── list.spec.ts │ │ │ │ ├── script.spec.ts │ │ │ │ └── table.spec.ts │ │ │ ├── modules/ │ │ │ │ ├── clipboard.spec.ts │ │ │ │ ├── history.spec.ts │ │ │ │ ├── keyboard.spec.ts │ │ │ │ ├── normalizeExternalHTML/ │ │ │ │ │ └── normalizers/ │ │ │ │ │ ├── googleDocs.spec.ts │ │ │ │ │ └── msWord.spec.ts │ │ │ │ ├── syntax.spec.ts │ │ │ │ ├── table.spec.ts │ │ │ │ ├── tableEmbed.spec.ts │ │ │ │ ├── toolbar.spec.ts │ │ │ │ └── uiNode.spec.ts │ │ │ ├── theme/ │ │ │ │ └── base/ │ │ │ │ └── tooltip.spec.ts │ │ │ ├── ui/ │ │ │ │ └── picker.spec.ts │ │ │ └── vitest.config.ts │ │ ├── tsconfig.json │ │ ├── webpack.common.cjs │ │ └── webpack.config.cjs │ └── website/ │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── content/ │ │ ├── blog/ │ │ │ ├── a-new-delta.mdx │ │ │ ├── an-official-cdn-for-quill.mdx │ │ │ ├── announcing-quill-1-0.mdx │ │ │ ├── are-we-there-yet-to-1-0.mdx │ │ │ ├── quill-1-0-beta-release.mdx │ │ │ ├── quill-1-0-release-candidate-released.mdx │ │ │ ├── quill-v0-19-no-more-iframes.mdx │ │ │ ├── the-road-to-1-0.mdx │ │ │ ├── the-state-of-quill-and-2-0.mdx │ │ │ └── upgrading-to-rich-text-deltas.mdx │ │ └── docs/ │ │ ├── api.mdx │ │ ├── configuration.mdx │ │ ├── customization/ │ │ │ ├── registries.mdx │ │ │ └── themes.mdx │ │ ├── customization.mdx │ │ ├── delta.mdx │ │ ├── formats.mdx │ │ ├── guides/ │ │ │ ├── building-a-custom-module.mdx │ │ │ ├── cloning-medium-with-parchment.js │ │ │ ├── cloning-medium-with-parchment.mdx │ │ │ └── designing-the-delta-format.mdx │ │ ├── installation.mdx │ │ ├── modules/ │ │ │ ├── clipboard.mdx │ │ │ ├── history.mdx │ │ │ ├── keyboard.mdx │ │ │ ├── syntax.mdx │ │ │ └── toolbar.mdx │ │ ├── modules.mdx │ │ ├── quickstart.mdx │ │ ├── upgrading-to-2-0.mdx │ │ └── why-quill.mdx │ ├── env.js │ ├── next.config.mjs │ ├── package.json │ ├── public/ │ │ ├── CNAME │ │ └── robots.txt │ └── src/ │ ├── components/ │ │ ├── ActiveLink.jsx │ │ ├── ClickOutsideHandler.jsx │ │ ├── Editor.jsx │ │ ├── GitHub.jsx │ │ ├── GitHub.module.scss │ │ ├── Header.jsx │ │ ├── Header.module.scss │ │ ├── Heading.jsx │ │ ├── Hint.jsx │ │ ├── Hint.module.scss │ │ ├── Layout.jsx │ │ ├── Link.jsx │ │ ├── MDX.jsx │ │ ├── NoSSR.jsx │ │ ├── OpenSource.jsx │ │ ├── OpenSource.module.scss │ │ ├── PlaygroundLayout.jsx │ │ ├── PlaygroundLayout.module.scss │ │ ├── PostLayout.jsx │ │ ├── PostLayout.module.scss │ │ ├── SEO.jsx │ │ ├── Sandpack.jsx │ │ └── Sandpack.module.scss │ ├── data/ │ │ ├── api.tsx │ │ ├── docs.tsx │ │ └── playground.tsx │ ├── pages/ │ │ ├── 404.jsx │ │ ├── _app.jsx │ │ ├── _document.jsx │ │ ├── base.css │ │ ├── docs/ │ │ │ └── [...id].jsx │ │ ├── docs.jsx │ │ ├── index.jsx │ │ ├── playground/ │ │ │ ├── [...id].jsx │ │ │ └── [...id].module.scss │ │ ├── playground.jsx │ │ ├── standalone/ │ │ │ ├── bubble.mdx │ │ │ ├── full.mdx │ │ │ ├── snow.mdx │ │ │ └── stress.mdx │ │ ├── styles.scss │ │ └── variables.scss │ ├── playground/ │ │ ├── custom-formats/ │ │ │ ├── index.css │ │ │ ├── index.html │ │ │ ├── index.js │ │ │ └── playground.json │ │ ├── form/ │ │ │ ├── index.css │ │ │ ├── index.html │ │ │ ├── index.js │ │ │ └── playground.json │ │ ├── react/ │ │ │ ├── App.js │ │ │ ├── Editor.js │ │ │ ├── playground.json │ │ │ └── styles.css │ │ └── snow/ │ │ ├── index.html │ │ ├── index.js │ │ └── playground.json │ └── utils/ │ ├── flattenData.js │ ├── replaceCDN.js │ └── slug.js ├── scripts/ │ ├── changelog.mjs │ ├── release.js │ └── utils/ │ └── configGit.mjs └── tsconfig.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .eslintignore ================================================ dist/ scripts/ ================================================ FILE: .github/CODE_OF_CONDUCT.md ================================================ # Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be 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 conduct@quilljs.com. 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. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ ================================================ FILE: .github/CONTRIBUTING.md ================================================ # Contributing The best way to contribute is to help others in the Quill community. This includes: - Reporting new [bugs](https://github.com/slab/quill/labels/bug) or adding details to existing ones - Reproducing [unconfirmed bugs](https://github.com/slab/quill/labels/needs%20reproduction) - Quick typo fix or documentation improvement [Pull Requests](#pull-requests) - Participating in [discussions](https://github.com/slab/quill/discussions) After becoming familiar with Quill and the codebase, likely through using Quill yourself and making some of the above contributions, you may choose to take on a bigger commitment by: - Helping fix [bugs](https://github.com/slab/quill/labels/bug) - Implementing new [features](https://github.com/slab/quill/labels/feature) - Publishing guides, tutorials, and examples - Supporting Quill in other ecosystems (Angular, React, etc) Note that if you are going to be making significant contributions, you should first open a [discussion](https://github.com/slab/quill/discussions) to ensure your work aligns with the project's goals and direction. ## Questions If you have a question, it is best to ask on [Discussions](https://github.com/slab/quill/discussions) under the Q&A category. ## Bug Reports Search through [Github Issues](https://github.com/slab/quill/issues) to see if the bug has already been reported. If so, please comment with any additional information. New bug reports must include: 1. Detailed description of faulty behavior 2. Steps for reproduction or failing test case 3. Expected and actual behaviors 4. Platforms (OS **and** browser combination) affected 5. Version of Quill Lacking reports it may be autoclosed with a link to these instructions. ## Feature Requests Search through [Github Issues](https://github.com/slab/quill/labels/feature) to see if someone has already suggested the feature. If so, please provide support with a [reaction](https://github.com/blog/2119-add-reactions-to-pull-requests-issues-and-comments) and add your own use case. To open a new feature request, please include: 1. A detailed description of the feature 2. Why this feature belongs in Quill core, instead of your own application logic 3. Background of where and how you are using Quill 4. The use case that would be enabled or improved for your product, if the feature was implemented Features are prioritized based on real world users and use cases, not theoretically useful additions for other unknown users. Lacking feature requests may be autoclosed with a link to this section. The more complete and compelling the request, the more likely it will ultimately be implemented. Garnering community support will help as well! ## Pull Requests Please check to make sure your plans fall within Quill's scope. This often means opening up a [discussion](https://github.com/slab/quill/labels/discussion). Non-code Pull Requests such as typo fixes or documentation improvements are highly encouraged and are often accepted immediately. Pull Requests modifying public facing interfaces or APIs, including backwards compatible additions, will undergo the most scrutiny, and will almost certainly require a proper discussion of the motivation and merits beforehand. Simply increasing code complexity is a cost not to be taken lightly. Pull requests must: 1. Be forked off the [main](https://github.com/slab/quill/tree/main) branch. 2. Pass the linter and conform to existing coding styles. 3. Commits are [squashed](https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Squashing-Commits) to minimally coherent units of changes. 4. Are accompanied by tests covering the new feature or demonstrating the bug for fixes. 5. Serve a single atomic purpose (add one feature or fix one bug). 6. Introduce only changes that further the PR's singular purpose (ex. do not tweak an unrelated config along with adding your feature). 7. Not break any existing unit or end to end tests. **Important:** By issuing a Pull Request you agree to allow the project owners to license your work under the terms of the [License](https://github.com/slab/quill/blob/master/LICENSE). ================================================ FILE: .github/DEVELOPMENT.md ================================================ # Development This repo is a monorepo powered by npm's official [workspace feature](https://docs.npmjs.com/cli/v10/using-npm/workspaces). It contains the following packages: ### quill This is the Quill library. It's written in [TypeScript](https://www.typescriptlang.org/), and use [Webpack](https://webpack.js.org/) as the bundler. It uses [Vitest](https://vitest.dev) for unit testing, and [Playwright](https://playwright.dev/) for E2E testing. ### website It's Quill's website (hosted at [quilljs.com](https://quilljs.com/)). It's built with [Next.js](https://nextjs.org/). ## Setup To prepare your local environment for development, ensure you have Node.js installed. The repo uses npm, and doesn't support Yarn and pnpm. Install the necessary dependencies with the command below: ```shell npm install ``` Start the development environment using: ```shell npm start ``` This command starts two services: - Quill's webpack dev server - Website's Next.js dev server These servers dynamically build and serve the latest copy of the source code. Access the running website at [localhost:9000](http://localhost:9000/). By default, the website will use your local Quill build, that includes all the examples in the website. This convenient setup allows for seamless development and ensures changes to Quill do not disrupt the website's content. If you need to modify only the website's code, start the website with `npm start -w website``. This makes the website use the latest CDN version. ## Testing To run the unit tests in watch mode, run: npm run test:unit -w quill To execute the E2E tests, run: npm run test:e2e -w quill ## Workflow A standard development workflow involves: 1. `npm start` - to run development services 2. [localhost:9000/standalone/snow](http://localhost:9000/standalone/snow) - to interactively develop and test an isolated example 3. `npm run test:unit -w quill` - to run unit tests 4. If everything is working, run the E2E tests ================================================ FILE: .github/ISSUE_TEMPLATE.md ================================================ Please describe the a concise description and fill out the details below. It will help others efficiently understand your request and get to an answer instead of repeated back and forth. Providing a [minimal, complete and verifiable example](https://stackoverflow.com/help/mcve) will further increase your chances that someone can help. **Steps for Reproduction** 1. Visit [quilljs.com, jsfiddle.net, codepen.io] 2. Step Two 3. Step Three **Expected behavior**: **Actual behavior**: **Platforms**: Include browser, operating system and respective versions **Version**: Run `Quill.version` to find out ================================================ FILE: .github/release.yml ================================================ changelog: exclude: authors: - quill-bot categories: - title: Bug Fixes 🛠 labels: - change:bugfix - title: New Features 🎉 labels: - change:feature - title: Documentation 📚 labels: - change:documentation - title: Other Changes labels: - "*" ================================================ FILE: .github/workflows/_test.yml ================================================ name: Tests on: workflow_call: jobs: e2e: name: E2E Tests timeout-minutes: 60 runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: node-version: 20 - name: Install dependencies run: npm ci - name: Install Playwright Browsers run: npx playwright install --with-deps working-directory: packages/quill - name: Run Playwright tests uses: coactions/setup-xvfb@v1 with: run: npm run test:e2e -- --headed working-directory: packages/quill fuzz: name: Fuzz Tests runs-on: ubuntu-latest steps: - name: Git checkout uses: actions/checkout@v4 - name: Use Node.js uses: actions/setup-node@v4 with: node-version: 20 - run: npm ci env: PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: 1 - run: npm run test:fuzz -w quill unit: name: Unit Tests runs-on: ubuntu-latest strategy: fail-fast: false matrix: browser: [chromium, webkit, firefox] steps: - name: Git checkout uses: actions/checkout@v3 - name: Use Node.js uses: actions/setup-node@v3 with: node-version: 20 - run: npm ci - run: npx playwright install --with-deps - run: npm run lint - run: npm run test:unit -w quill || npm run test:unit -w quill || npm run test:unit -w quill env: BROWSER: ${{ matrix.browser }} ================================================ FILE: .github/workflows/changelog.yml ================================================ name: Generate Changelog on: release: types: [published, created] workflow_dispatch: {} jobs: changelog: runs-on: ubuntu-latest steps: - name: Git checkout uses: actions/checkout@v4 - name: Use Node.js uses: actions/setup-node@v4 with: node-version: 20 - run: npm ci - run: node ./scripts/changelog.mjs env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ================================================ FILE: .github/workflows/label.yml ================================================ name: Pull Requests on: pull_request: types: [opened, labeled, unlabeled, synchronize] jobs: label: runs-on: ubuntu-latest permissions: issues: write pull-requests: write steps: - uses: mheap/github-action-required-labels@v5 with: mode: exactly count: 1 labels: | change:bugfix change:feature change:documentation change:chore change:refactor add_comment: false ================================================ FILE: .github/workflows/main.yml ================================================ name: Main on: push: branches: [main] concurrency: group: main-build cancel-in-progress: true jobs: test: uses: ./.github/workflows/_test.yml ================================================ FILE: .github/workflows/pull-request.yml ================================================ name: Pull Requests on: pull_request: branches: [main] jobs: test: uses: ./.github/workflows/_test.yml ================================================ FILE: .github/workflows/release.yml ================================================ name: Release on: workflow_dispatch: inputs: version: description: 'npm version. Examples: "2.0.0", "2.0.0-beta.0". To deploy an experimental version, type "experimental".' default: "experimental" required: true dry-run: description: "Only create a tarball, do not publish to npm or create a release on GitHub." type: boolean default: true required: true permissions: contents: write jobs: test: uses: ./.github/workflows/_test.yml release: runs-on: ubuntu-latest needs: test steps: - name: Git checkout uses: actions/checkout@v4 - name: Use Node.js uses: actions/setup-node@v4 with: node-version: 20 - run: npm ci - run: ./scripts/release.js --version ${{ github.event.inputs.version }} ${{ github.event.inputs.dry-run == 'true' && '--dry-run' || '' }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - name: Archive npm package tarball uses: actions/upload-artifact@v4 with: name: npm path: | packages/quill/dist/*.tgz ================================================ FILE: .gitignore ================================================ .* !.eslintrc.json !.eslintignore !.npmignore !.gitignore !.github node_modules test-results/ playwright-report/ ================================================ FILE: .npmignore ================================================ *.ts !*.d.ts .* .github .vscode docs test tsconfig.json ================================================ FILE: CHANGELOG.md ================================================ # v2.0.2 (2024-05-13) ## What's Changed ### Bug Fixes 🛠 - Fix typing errors for Quill.register by [@hzgotb](https://github.com/hzgotb) in https://github.com/slab/quill/pull/4127 - Fix event source when deleting a link with shortcuts by [@luin](https://github.com/luin) in https://github.com/slab/quill/pull/4200 - Avoid side effects for Enter/Backspace when composing in Safari by [@luin](https://github.com/luin) in https://github.com/slab/quill/pull/4201 - Ignore pasting images when image format is disallowed by [@luin](https://github.com/luin) in https://github.com/slab/quill/pull/4202 [All changes](https://github.com/slab/quill/releases/tag/v2.0.2) # v2.0.1 (2024-05-01) ## Bug Fixes - Prevent overriding of theme's default toolbar settings mistakenly [#4120](https://github.com/slab/quill/pull/4120) - Improve typings for methods that return a Delta [#4136](https://github.com/slab/quill/pull/4136) - Fix toolbar icons for h3-h6 [#4131](https://github.com/slab/quill/pull/4131) [All changes](https://github.com/slab/quill/releases/tag/v2.0.1) # v2.0.0 (2024-04-17) We are thrilled to announce the release of Quill 2.0! Please check out the [announcement post](https://slab.com/blog/announcing-quill-2-0/). ## Major Improvements - Quill is now a valid ESM package for better ecosystem (e.g. bundlers) and tree-shaking support - Nested Quill support [#3590](https://github.com/slab/quill/pull/3590) - Improved IME and spell corrector support [#3807](https://github.com/slab/quill/pull/3807) - Semantic cleanups for TEXT_CHANGE event [#3778](https://github.com/slab/quill/pull/3778) - **History**: Record selection in history module [#3823](https://github.com/slab/quill/pull/3823) - Auto detect scrolling container [#3840](https://github.com/slab/quill/pull/3840) - **Clipboard**: Improve support for pasting from Google Docs and Microsoft Word ## Performance Improvements Quill 2.0 includes many performance optimizations, the most important of which is the improved rendering speed for large content. - Improve inserting performance [#3815](https://github.com/slab/quill/pull/3815) - Avoid fetching selections when possible [#3538](https://github.com/slab/quill/pull/3538) - No need to setContents when container is empty [#3539](https://github.com/slab/quill/pull/3539) ## Code Modernization - Migrated to TypeScript - Provided official TypeScript declarations - Migrated to Vitest for unit testing - Migrated to Playwright for E2E testing - Migrated website to Gatsby [All changes](https://github.com/slab/quill/releases/tag/v2.0.0) # v2.0.0-rc.5 (2024-04-04) - **Clipboard** Add support for Quill v1 list attributes - Fix overload declarations for `quill.formatText()` and other methods - Expose Bounds type for getBounds() - Expose Range type - Allow ref for insertBefore to be null [All changes](https://github.com/slab/quill/releases/tag/v2.0.0-rc.5) # v2.0.0-rc.4 (2024-03-24) - Include source maps for Parchment - **Clipboard** Support pasting links copied from iOS share sheets - Fix config parsing where undefined values were kept - Expose types for Quill options - Remove empty .css.js files generated by bundlers [All changes](https://github.com/slab/quill/releases/tag/v2.0.0-rc.4) # v2.0.0-rc.3 (2024-03-16) - Fix `Quill#getSemanticHTML()` for list items - Remove unnecessary Firefox workaround - **Clipboard** Fix redundant newlines when pasting from external sources - Add `formats` option for specifying allowed formats [All changes](https://github.com/slab/quill/releases/tag/v2.0.0-rc.3) # v2.0.0-rc.2 (2024-02-15) - Fix toolbar button state not updated in some cases - Narrower `BubbleTheme.tooltip` type - Fix `Selection#getBounds()` when starting range at end of text node - Improve compatibility with esbuild [All changes](https://github.com/slab/quill/releases/tag/v2.0.0-rc.2) # v2.0.0-rc.1 (2024-02-12) - Remove unnecessary lodash usages. [All changes](https://github.com/slab/quill/releases/tag/v2.0.0-rc.1) # v2.0.0-rc.0 (2024-02-03) - **Clipboard** Convert newlines between inline elements to a space. - **Clipboard** Avoid generating unsupported formats on paste. - **Clipboard** Improve support for pasting from Google Docs and Microsoft Word. - **Clipboard** Ignore whitespace between pasted empty paragraphs. - **Syntax** Support highlight.js v10 and v11. [All changes](https://github.com/slab/quill/releases/tag/v2.0.0-rc.0) # v2.0.0-beta.2 (2024-01-30) - Fix IME not working correctly in Safari. - **Clipboard** Support paste as plain text. - Fix `Quill.getText()` not respecting `length` parameter. - **History** Fix redo shortcut not working on Linux and Windows. [All changes](https://github.com/slab/quill/releases/tag/v2.0.0-beta.2) # v2.0.0-beta.1 (2024-01-21) - Fix syntax label from "Javascript" to "JavaScript". - Fix typing errors for emitter. - Inline SVG images for easier bundler setup. - Improve typing for Registry. [All changes](https://github.com/slab/quill/releases/tag/v2.0.0-beta.1) # v2.0.0-beta.0 (2023-12-08) In the upcoming 2.0 release, Quill has been significantly modernized. Leveraging the latest browser-supported APIs, Quill now delivers a more efficient and reliable editing experience. ## Major Improvements - Nested Quill support [#3590](https://github.com/slab/quill/pull/3590) - Improved IME and spell corrector support [#3807](https://github.com/slab/quill/pull/3807) - Semantic cleanups for TEXT_CHANGE event [#3778](https://github.com/slab/quill/pull/3778) - **History**: Record selection in history module [#3823](https://github.com/slab/quill/pull/3823) - Auto detect scrolling container [#3840](https://github.com/slab/quill/pull/3840) ## Performance Improvements Quill 2.0 includes many performance optimizations, the most important of which is the improved rendering speed for large content. - Improve inserting performance [#3815](https://github.com/slab/quill/pull/3815) - Avoid fetching selections when possible [#3538](https://github.com/slab/quill/pull/3538) - No need to setContents when container is empty [#3539](https://github.com/slab/quill/pull/3539) ## Code Modernization - Migrated to TypeScript - Provided official TypeScript declarations - Migrated to Vitest for unit testing - Migrated to Playwright for E2E testing - Migrated website to Gatsby [All changes](https://github.com/slab/quill/releases/tag/v2.0.0-beta.0) # v1.3.7 (2019-09-09) Security related bug fixes. - https://app.snyk.io/vuln/npm:extend:20180424 - https://www.npmjs.com/advisories/1039 Thank you [@danfuzz](https://github.com/danfuzz), [@danielw93](https://github.com/danielw93), [@jonathanlloyd](https://github.com/jonathanlloyd), and [@k-sai-kiranmayee](https://github.com/k-sai-kiranmayee) for your contributions to this release. [All changes](https://github.com/slab/quill/releases/tag/v1.3.7) # v1.3.6 (2018-03-12) - Make picker accessible [#1999](https://github.com/slab/quill/pull/1999) - Fix Japanese composition in Chrome 65 [#2009](https://github.com/slab/quill/issues/2009) Thanks to [@berylw](https://github.com/berylw) and [@erinsinger93](https://github.com/erinsinger93) for contributions to this release! [All changes](https://github.com/slab/quill/releases/tag/v1.3.6) # v1.3.5 (2018-01-22) - Fix indent preservation of a checked checklist item [#1818](https://github.com/slab/quill/issues/1818) - added as a shortcut to trigger bullet list formatting [#1819](https://github.com/slab/quill/pull/1819) - Fix pasting text-align styles [#1873](https://github.com/slab/quill/issues/1873) - Fix cursor position after dangerouslyPasteHTML [#1886](https://github.com/slab/quill/issues/1886) - Fix value of history stack in text-change handler [#1906](https://github.com/slab/quill/pull/1906) - Workaround for Webkit locking up when navigating around images using hotkeys [#1910](https://github.com/slab/quill/issues/1910) Thank you [@araruna](https://github.com/araruna), [@bryanrsmith](https://github.com/bryanrsmith), [@haugstrup](https://github.com/haugstrup), [@icylace](https://github.com/icylace), [@leimig](https://github.com/leimig), [@LFDM](https://github.com/LFDM), [@nikparo](https://github.com/nikparo), [@rafpaf](https://github.com/rafpaf) and [@vk2sky](https://github.com/vk2sky) for your contributions to this release. [All changes](https://github.com/slab/quill/releases/tag/v1.3.5) # v1.3.4 (2017-11-06) - Loosen dependency specification [#1748](https://github.com/slab/quill/issues/1748) - Loosen list autofill constraint [#1749](https://github.com/slab/quill/issues/1749) Thanks to [@danfuzz](https://github.com/danfuzz) and [@SoftVision-CarmenFat](https://github.com/SoftVision-CarmenFat) for contributions to this release! [All changes](https://github.com/slab/quill/releases/tag/v1.3.4) # v1.3.3 (2017-10-09) - Fix `getFormat` with no parameters while editor is not focused [#1548](https://github.com/slab/quill/issues/1548) - Remove automatic highlighting across embeds [#1691](https://github.com/slab/quill/issues/1691) - Support checking checklist on mobile [#1693](https://github.com/slab/quill/pull/1711) - Fix list creation keyboard shortcuts [#1723](https://github.com/slab/quill/issues/1723) - Show KaTex rendering errors [#1738](https://github.com/slab/quill/pull/1738) Thank you [@altschuler](https://github.com/altschuler), [@arrocke](https://github.com/arrocke), [@guillaumepotier](https://github.com/guillaumepotier), [@sferoze](https://github.com/sferoze) and [@volser](https://github.com/volser) for your contributions to this release. [All changes](https://github.com/slab/quill/releases/tag/v1.3.3) # v1.3.2 (2017-09-04) - Pasting into code block should always paste as code [#1624](https://github.com/slab/quill/issues/1624) - Fix removing embed selection when arrow keys change selection [#1633](https://github.com/slab/quill/issues/1633) - Fix selection restoration after image insertion [#1649](https://github.com/slab/quill/issues/1649) - Fix selection-change firing after dragging off screen [#1654](https://github.com/slab/quill/issues/1654) - Fix placeholder text spacing [#1677](https://github.com/slab/quill/issues/1677) Thanks to [@abramz](https://github.com/abramz), [@amitm02](https://github.com/amitm02), [@eamodio](https://github.com/eamodio), [@HWliao](https://github.com/HWliao), [@mmitis](https://github.com/mmitis), [@nelsonpecora](https://github.com/nelsonpecora), [@nipunjain87](https://github.com/nipunjain87), and [@ValueBerry](https://github.com/ValueBerry) for contributions to this release! [All changes](https://github.com/slab/quill/releases/tag/v1.3.2) # v1.3.1 (2017-08-07) - Fix placeholder when emptying text [#1594](https://github.com/slab/quill/issues/1594) - Fix inserting newline after header [#1616](https://github.com/slab/quill/issues/1616) Thank you [@Natim](https://github.com/Natim) and [@stephenLYao](https://github.com/stephenLYao) for your contributions to this release. [All changes](https://github.com/slab/quill/releases/tag/v1.3.1) # v1.3.0 (2017-07-17) Add `matchVisual` [configuration](https://quilljs.com/docs/modules/clipboard/#configuration) to Clipboard. - Use DOM API to determine selected `` without themes [#997](https://github.com/slab/quill/issues/997) - Link `code` icon to `code-block` [#998](https://github.com/slab/quill/issues/998) - Fix undo stack when at size limit [#1001](https://github.com/slab/quill/pull/1001) - Fix bug where `formatLine` did not ignore inline formats [8a7190](https://github.com/quilljs/parchment/commit/8a71905b2dd02d003edb02a15fdc727b26914e49) Thanks to [@dropfen](https://github.com/dropfen), [@evansolomon](https://github.com/evansolomon), [@hallaathrad](https://github.com/hallaathrad), [@janyksteenbeek](https://github.com/janyksteenbeek), [@jackmu95](https://github.com/jackmu95), [@marktron](https://github.com/marktron), [@mcat-ee](https://github.com/mcat-ee), [@unhammer](https://github.com/unhammer), and [@zeke](https://github.com/zeke) for contributions to this release! [All changes](https://github.com/slab/quill/releases/tag/v1.0.6) # v1.0.4 (2016-09-19) - Fix bubble theme defaults [#963](https://github.com/slab/quill/issues/963) - Fix browsers modifying inline nesting order [#971](https://github.com/slab/quill/issues/971) - Do not fire selection-change event on paste [#974](https://github.com/slab/quill/issues/974) - Support alt attribute in images [#975](https://github.com/slab/quill/issues/975) - Deprecate `pasteHTML` for removal in Quill 2.0 [#981](https://github.com/slab/quill/issues/981) Thank you [jackmu95](https://github.com/jackmu95), [kristeehan](https://github.com/kristeehan), [ruffle1986](https://github.com/ruffle1986), [sergop321](https://github.com/sergop321), [sferoze](https://github.com/sferoze), and [sijad](https://github.com/sijad) for contributions to this release. [All changes](https://github.com/slab/quill/releases/tag/v1.0.4) # v1.0.3 (2016-09-07) - Fix [#928](https://github.com/slab/quill/issues/928) Thank you [@scottmessinger](https://github.com/scottmessinger) for the bug report. [All changes](https://github.com/slab/quill/releases/tag/v1.0.3) # v1.0.2 (2016-09-07) - Fix building quill.core.js [docs #11](https://github.com/quilljs/quilljs.github.io/issues/11) - Fix regression of [#793](https://github.com/slab/quill/issues/793) Thanks to [@eamodio](https://github.com/eamodio) and [@neandrake](https://github.com/neandrake) for their contributions to this release. [All changes](https://github.com/slab/quill/releases/tag/v1.0.2) # v1.0.0 (2016-09-06) Quill 1.0 is released! Read the [official announcement](https://quilljs.com/blog/announcing-quill-1-0/). [All changes](https://github.com/slab/quill/releases/tag/v1.0.0) # v1.0.0-rc.4 (2016-08-31) Fix one important bug [fdd920](https://github.com/slab/quill/commit/fdd920250c05403ed9e5d6d86826a00167ba0b09) [All changes](https://github.com/slab/quill/releases/tag/v1.0.0-rc.4) # v1.0.0-rc.3 (2016-08-29) A few bug fixes, one with with possibly significant implications. See the [issue #889](https://github.com/slab/quill/issues/889) and [commit fix](https://github.com/slab/quill/commit/be24c62a6234818548658fcb5e1935a0c07b4eb7) for more details. ### Bug Fixes - Fix indenting beyond first level with toolbar [#882](https://github.com/slab/quill/issues/882) - Fix toolbar font/size display on Safari [#884](https://github.com/slab/quill/issues/884) - Fix pasting from Gmail from on different browser [#886](https://github.com/slab/quill/issues/886) - Fix undo/redo consistency [#889](https://github.com/slab/quill/issues/889) - Fix null error when selecting all on Firefox [#891](https://github.com/slab/quill/issues/891) - Fix merging keyboard options twice [#897](https://github.com/slab/quill/issues/897) Thank you [@benbro](https://github.com/benbro), [@cgilboy](https://github.com/cgilboy), [@cutteroid](https://github.com/cutteroid), and [@routman](https://github.com/routman) for contributions to this release! [All changes](https://github.com/slab/quill/releases/tag/v1.0.0-rc.3) # v1.0.0-rc.2 (2016-08-23) A few bug fixes, including one significant [one](https://github.com/slab/quill/issues/883) ### Bug Fixes - Fix icon picker rendering in MS Edge [#877](https://github.com/slab/quill/issues/877) - Add back minified build to release [#881](https://github.com/slab/quill/issues/881) - Fix optimized change calculation with preformatted text [#883](https://github.com/slab/quill/issues/883) Thanks to [benbro](https://github.com/benbro), [cutteroid](https://github.com/cutteroid), and [CapTec](https://github.com/CapTec) for their contributions to this release. [All changes](https://github.com/slab/quill/releases/tag/v1.0.0-rc.2) # v1.0.0-rc.1 (2016-08-23) A few bug fixes and performance improvements. ### Features - Source maps now available from CDN for minified build ### Bug Fixes - Fix scroll interaction between two Quill editors [#855](https://github.com/slab/quill/issues/855) - Fix scroll on paste [#856](https://github.com/slab/quill/issues/856) - Fix native iOS tooltip formatting [#862](https://github.com/slab/quill/issues/862) - Remove comments from pasting from Word [#872](https://github.com/slab/quill/issues/872) - Fix indent at all supported indent levels [#873](https://github.com/slab/quill/issues/873) - Fix indent interaction with text direction [#874](https://github.com/slab/quill/issues/874) Thank you [@benbro](https://github.com/benbro), [@fernandogmar](https://github.com/fernandogmar), [@sachinrekhi](https://github.com/sachinrekhi), [@sferoze](https://github.com/sferoze), and [@stalniy](https://github.com/stalniy) for contributions to this release! [All changes](https://github.com/slab/quill/releases/tag/v1.0.0-rc.1) # v1.0.0-rc.0 (2016-08-18) Take a look at [Quill 1.0 Release Candidate](https://quilljs.com/blog/quill-1-0-release-candidate-released/) for more details. ### Updates - Going forward the minimal stylesheet build will be named quill.core.css, instead of quill.css ### Bug Fixes - Fix identifying ordered and bulletd lists [#846](https://github.com/slab/quill/issues/846) [#847](https://github.com/slab/quill/issues/847) - Fix bullet interaction with text direction [#848](https://github.com/slab/quill/issues/848) A huge thank you to all contributors to through the beta! Special thanks goes to [@benbro](https://github.com/benbro) and [@sachinrekhi](https://github.com/sachinrekhi) who together submitted submitted almost 50 Issues and Pull Requests! - [@abejdaniels](https://github.com/abejdaniels) - [@anovi](https://github.com/anovi) - [@benbro](https://github.com/benbro) - [@bram2w](https://github.com/bram2w) - [@brynjagr](https://github.com/brynjagr) - [@CapTec](https://github.com/CapTec) - [@Cinamonas](https://github.com/Cinamonas) - [@clemmy](https://github.com/clemmy) - [@crisbeto](https://github.com/crisbeto) - [@cutteroid](https://github.com/cutteroid) - [@DadaMonad](https://github.com/DadaMonad) - [@davelozier](https://github.com/davelozier) - [@emanuelbsilva](https://github.com/emanuelbsilva) - [@ersommer](https://github.com/ersommer) - [@fernandogmar](https://github.com/fernandogmar) - [@george-norris-salesforce](https://github.com/george-norris-salesforce) - [@jackmu95](https://github.com/jackmu95) - [@jasonmng](https://github.com/jasonmng) - [@jbrowning](https://github.com/jbrowning) - [@jonnolen](https://github.com/jonnolen) - [@KameSama](https://github.com/KameSama) - [@kei-ito](https://github.com/kei-ito) - [@kylebragger](https://github.com/kylebragger) - [@LucVanPelt](https://github.com/LucVanPelt) - [@lukechapman](https://github.com/lukechapman) - [@micimize](https://github.com/micimize) - [@mmorearty](https://github.com/mmorearty) - [@mshamaiev-intel471](https://github.com/mshamaiev-intel471) - [@quentez](https://github.com/quentez) - [@sachinrekhi](https://github.com/sachinrekhi) - [@sagacitysite](https://github.com/sagacitysite) - [@saw](https://github.com/saw) - [@stalniy](https://github.com/stalniy) - [@tOgg1](https://github.com/tOgg1) - [@u9520107](https://github.com/u9520107) - [@WriterStat](https://github.com/WriterStat) [All changes](https://github.com/slab/quill/releases/tag/v1.0.0-rc.0) # v1.0.0-beta.11 (2016-08-03) Fixed some regressive bugs from previous release. ### Bug Fixes - Fix activating more than one format before typing [#841](https://github.com/slab/quill/issues/841) - Run default matchers before before user defined ones [#843](https://github.com/slab/quill/issues/843) - Fix merging theme configurations [#844](https://github.com/slab/quill/issues/844), [#845](https://github.com/slab/quill/issues/845) Thanks [benbro](https://github.com/benbro), [jackmu95](https://github.com/jackmu95), and [george-norris-salesforce](https://github.com/george-norris-salesforce) for the bug reports. [All changes](https://github.com/slab/quill/releases/tag/v1.0.0-beta.11) # v1.0.0-beta.10 (2016-08-03) Lots of bug fixes and performance improvements. ### Breaking Changes - Keyboard handler format in initial [configuration](beta.quilljs.com/docs/modules/keyboard/) has changed. `addBinding` is overloaded to be backwards compatible. ### Bug Fixes - Preserve last bullet on paste [#696](https://github.com/slab/quill/issues/696) - Fix getBounds calculation for lists [#765](https://github.com/slab/quill/issues/765) - Escape quotes in font value [#769](https://github.com/slab/quill/issues/769) - Fix spacing calculation on paste [#797](https://github.com/slab/quill/issues/797) - Fix Snow tooltip label [#798](https://github.com/slab/quill/issues/798) - Fix link tooltip showing up on long click [#799](https://github.com/slab/quill/issues/799) - Fix entering code block in IE and Firefox [#803](https://github.com/slab/quill/issues/803) - Fix opening image dialog on Firefox [#805](https://github.com/slab/quill/issues/805) - Fix focus loss on updateContents [#809](https://github.com/slab/quill/issues/809) - Reset toolbar of blur [#810](https://github.com/slab/quill/issues/810) - Fix cursor position calculation on delete [#811](https://github.com/slab/quill/issues/811) - Fix highlighting across different alignment values [#815](https://github.com/slab/quill/issues/815) - Allow default active button [#816](https://github.com/slab/quill/issues/816) - Fix deleting last character of formatted text on Firefox [#824](https://github.com/slab/quill/issues/824) - Fix Youtube regex [#826](https://github.com/slab/quill/pull/826) - Fix missing imports when Quill not global [#836](https://github.com/slab/quill/pull/836) Thanks to [benbro](https://github.com/benbro), [clemmy](https://github.com/clemmy), [crisbeto](https://github.com/crisbeto), [cutteroid](https://github.com/cutteroid), [jackmu95](https://github.com/jackmu95), [kylebragger](https://github.com/kylebragger), [sachinrekhi](https://github.com/sachinrekhi), [stalniy](https://github.com/stalniy), and [tOgg1](https://github.com/tOgg1) for their contributions to this release. [All changes](https://github.com/slab/quill/releases/tag/v1.0.0-beta.10) # v1.0.0-beta.9 (2016-07-19) Potentially the final beta before a release candidate, if no major issues are discovered. ### Breaking Changes - No longer expose `ui/link-tooltip` through `import` as implementation is now Snow specific - Significant refactoring of `ui/tooltip` - Syntax module now autodetects language, instead of defaulting to Javascript ### Features - Formula and video insertion UI added to Snow and Bubble themes ### Bug Fixes - Fix toolbar active state after backspacing to previous line [#730](https://github.com/slab/quill/issues/730) - User selection is now preserved various API calls [#731](https://github.com/slab/quill/issues/731) - Fix long click on link-tooltip [#747](https://github.com/slab/quill/issues/747) - Fix ordered list and text-align right interaction [#784](https://github.com/slab/quill/issues/784) - Fix toggling code block off [#789](https://github.com/slab/quill/issues/789) - Scroll position is now automatically preserved between editor blur and focus Thank you [@benbro](https://github.com/benbro), [@KameSama](https://github.com/KameSama), and [@sachinrekhi](https://github.com/sachinrekhi) for contributions to this release! [All changes](https://github.com/slab/quill/releases/tag/v1.0.0-beta.9) # v1.0.0-beta.8 (2016-07-08) Weekly beta preview release. The editor is almost ready for release candidacy but a couple cycles will be spent on the Snow and Bubble interfaces. ### Work in Progress Image insertion is being reworked in the provided Snow and Bubble themes. The old image-tooltip has been removed in favor of a simpler and native interaction. By default clicking the image icon on the toolbar will open the OS file picker to convert and that into a base64 image. This will allow for a more natural hook to upload to a remote server instead. Some changes to the link tooltip is also being made to accommodate formula and video insertion, currently only available through the API. ### Breaking Changes - Image tooltip UI has been removed, see above - Code blocks now use a single `
` tag, instead of one per line [#723](https://github.com/slab/quill/issues/723)

### Bug Fixes

- Fix multiline syntax highlighting [#723](https://github.com/slab/quill/issues/723)
- Keep pickers open on api text-change [#734](https://github.com/slab/quill/issues/734)
- Emit correct source for text-change [#760](https://github.com/slab/quill/issues/760)
- Emit correct parameters in selection-change [#762](https://github.com/slab/quill/issues/762)
- Fix error redoing line insertion [#767](https://github.com/slab/quill/issues/767)
- Better emitted Deltas for text-change [#768](https://github.com/slab/quill/issues/768)
- Better Array.prototype.find polyfill for IE11 [#776](https://github.com/slab/quill/issues/776)
- Fix Parchment errors in replacing text [#779](https://github.com/slab/quill/issues/779) [#783](https://github.com/slab/quill/issues/783)
- Fix align button active state [#780](https://github.com/slab/quill/issues/780)
- Fix format text on falsy value [#782](https://github.com/slab/quill/issues/782)
- Use native cut [#785](https://github.com/slab/quill/issues/785)
- Fix inializing document where last line is formatted [#786](https://github.com/slab/quill/issues/786)

Thanks to [benbro](https://github.com/benbro), [bram2w](https://github.com/bram2w), [clemmy](https://github.com/clemmy), [DadaMonad](https://github.com/DadaMonad), [ersommer](https://github.com/ersommer), [michaeljosephrosenthal](https://github.com/michaeljosephrosenthal), [mmorearty](https://github.com/mmorearty), [mshamaiev-intel471](https://github.com/mshamaiev-intel471), and [sachinrekhi](https://github.com/sachinrekhi) for their contributions to this release.

[All changes](https://github.com/slab/quill/releases/tag/v1.0.0-beta.8)

# v1.0.0-beta.6 (2016-06-21)

Weekly beta preview release.

### Features

- Pickers can now be used and is styled in Bubble theme

### Bug Fixes

- Fix editing within formula [#702](https://github.com/slab/quill/issues/702)
- Fix adding new line when deleting across lists [#741](https://github.com/slab/quill/issues/741)
- Fix placeholder when default block tag is changed [#743](https://github.com/slab/quill/issues/743)
- Keep Bubble tooltip open on format [#744](https://github.com/slab/quill/issues/744)
- Fix format loss when copying from Quill [#748](https://github.com/slab/quill/issues/748) [#750](https://github.com/slab/quill/issues/750)
- Break long lines in Firefox [#751](https://github.com/slab/quill/issues/751)
- Fix cursor position being off after formatting and typing quickly [#752](https://github.com/slab/quill/issues/752)
- Remove image resizing handles on Firefox [#753](https://github.com/slab/quill/issues/753)
- Fix removing blockquote on initialization [#754](https://github.com/slab/quill/issues/754)
- Fix adding blank lines on initialization [#756](https://github.com/slab/quill/issues/756)

Thank you [abejdaniels](https://github.com/abejdaniels), [benbro](https://github.com/benbro), [davelozier](https://github.com/davelozier), [fernandogmar](https://github.com/fernandogmar), [KameSama](https://github.com/KameSama), and [WriterStat](https://github.com/WriterStat) for contributions to this release.

[All changes](https://github.com/slab/quill/releases/tag/v1.0.0-beta.6)

# v1.0.0-beta.5 (2016-06-14)

Weekly beta preview release.

### Features

- Add blur() [#726](https://github.com/slab/quill/pull/726)

### Bug Fixes

- Fix null error [#728](https://github.com/slab/quill/issues/728)
- Fix building with Node v6 [#732](https://github.com/slab/quill/issues/732)
- Ensure button type for supplied buttons [#733](https://github.com/slab/quill/issues/733)
- Fix line break pasting on Firefox [#735](https://github.com/slab/quill/issues/735)
- Fix 'user' source on API calls [#739](https://github.com/slab/quill/issues/739)

Thanks to [benbro](https://github.com/benbro), [lukechapman](https://github.com/lukechapman), [sachinrekhi](https://github.com/sachinrekhi), and [saw](https://github.com/saw) for their contributions to this release.

[All changes](https://github.com/slab/quill/releases/tag/v1.0.0-beta.5)

# v1.0.0-beta.4 (2016-06-03)

Weekly beta preview release.

### Breaking Changes

- Headers no longer generates id attribute [#700](https://github.com/slab/quill/issues/700)
- Add Control+Y hotkey on Windows [#705](https://github.com/slab/quill/issues/705)
- BlockEmbed Blots are now length 1 and represented in a Delta the same as an inline embed
  - value() used to return object and newline, newline is now removed
  - formats used to be attributed on the newline character, it is now attributed on the object

### Features

- Enter on empty and indented list removes indent [#707](https://github.com/slab/quill/issues/707)
- Allow base64 images to be inserted via APIs [#721](https://github.com/slab/quill/issues/721)

### Bug Fixes

- Fix typing after clearing inline format [#703](https://github.com/slab/quill/issues/703)
- Correctly position Bubble tooltip when selecting multiple lines [#706](https://github.com/slab/quill/issues/706)
- Fix typing after link format [#708](https://github.com/slab/quill/issues/708)
- Fix loss of selection on using link tooltip [#709](https://github.com/slab/quill/issues/709)
- Fix `setSelection(null)` [#722](https://github.com/slab/quill/issues/722)

Thank you [@benbro](https://github.com/benbro), [@brynjagr](https://github.com/brynjagr), and [@sachinrekhi](https://github.com/sachinrekhi) for contributions to this release.

[All changes](https://github.com/slab/quill/releases/tag/v1.0.0-beta.4)

# v1.0.0-beta.3 (2016-05-25)

# 1.0.0-beta.3

Weekly beta preview release.

### Breaking Changes

- Keyboard was incorrectly using `metaKey` to refer to the control key on Windows. It now correctly refers to the Window key and `shortKey` has been added to refer the common platform specific modifier for hotkeys (metaKey for Mac, ctrlKey for Windows/Linux)
- Formula is now a module, since it uses KaTeX

### Features

- Picker now uses text from original `
`} files={{ 'index.js': ` const quill = new Quill('#editor', { theme: 'snow' }); quill.on(Quill.events.TEXT_CHANGE, update); const playground = document.querySelector('#playground'); update(); function formatDelta(delta) { return \`
\${JSON.stringify(delta.ops, null, 2)}
\`; } function update(delta) { const contents = quill.getContents(); let html = \`

contents

\${formatDelta(contents)}\` if (delta) { html = \`\${html}

change

\${formatDelta(delta)}\`; } playground.innerHTML = html; } ` }} /> ================================================ FILE: packages/website/content/docs/formats.mdx ================================================ --- title: Formats --- Quill supports a number of formats, both in UI controls and API calls. By default, all formats are enabled and allowed in a Quill editor. They can be configured with the [formats](/docs/configuration/#formats) option. This is separate from adding a control in the [Toolbar](/docs/modules/toolbar/). For example, you can configure Quill to allow bolded content to be pasted into an editor that has no bold button in the toolbar.
`, }} /> Standalone #### Inline - Background Color - `background` - Bold - `bold` - Color - `color` - Font - `font` - Inline Code - `code` - Italic - `italic` - Link - `link` - Size - `size` - Strikethrough - `strike` - Superscript/Subscript - `script` - Underline - `underline` #### Block - Blockquote - `blockquote` - Header - `header` - Indent - `indent` - List - `list` - Text Alignment - `align` - Text Direction - `direction` - Code Block - `code-block` #### Embeds - Formula - `formula` (requires [KaTeX](https://katex.org/)) - Image - `image` - Video - `video` ================================================ FILE: packages/website/content/docs/guides/building-a-custom-module.mdx ================================================ --- title: Building a Custom Module --- Quill's core strength as an editor is its rich API and powerful customization capabilities. As you implement functionality on top of Quill's API, it may be convenient to organize this as a module. For the purpose of this guide, we will walk through one way to build a word counter module, a commonly found feature in many word processors. Internally modules are how much of Quill's functionality is organized. You can overwrite these default [modules](/docs/modules/) by implementing your own and registering it with the same name. ### Counting Words At its core a word counter simply counts the number of words in the editor and displays this value in some UI. Thus we need to: 1. Listen for text changes in Quill. 1. Count the number of words. 1. Display this value. Let's jump straight in with a complete example!
0
`, 'index.css': ` #editor { border: 1px solid #ccc; } #counter { border: 1px solid #ccc; border-width: 0px 1px 1px 1px; color: #aaa; padding: 5px 15px; text-align: right; } `, 'index.js': ` function Counter(quill, options) { const container = document.querySelector('#counter'); quill.on(Quill.events.TEXT_CHANGE, () => { const text = quill.getText(); // There are a couple issues with counting words // this way but we'll fix these later container.innerText = text.split(/\\s+/).length; }); } Quill.register('modules/counter', Counter); // We can now initialize Quill with something like this: const quill = new Quill('#editor', { modules: { counter: true } }); ` }} /> That's all it takes to add a custom module to Quill! A function can be [registered](/docs/api/#quillregistermodule/) as a module and it will be passed the corresponding Quill editor object along with any options. ### Using Options Modules are passed an options object that can be used to fine tune the desired behavior. We can use this to accept a selector for the counter container instead of a hard-coded string. Let's also customize the counter to either count words or characters:
0
`, 'index.css': ` #editor { border: 1px solid #ccc; } #counter { border: 1px solid #ccc; border-width: 0px 1px 1px 1px; color: #aaa; padding: 5px 15px; text-align: right; } `, 'index.js': ` function Counter(quill, options) { const container = document.querySelector(options.container); quill.on(Quill.events.TEXT_CHANGE, () => { const text = quill.getText(); if (options.unit === 'word') { container.innerText = text.split(/\\s+/).length + ' words'; } else { container.innerText = text.length + ' characters'; } }); } Quill.register('modules/counter', Counter); // We can now initialize Quill with something like this: const quill = new Quill('#editor', { modules: { counter: { container: '#counter', unit: 'word' } } }); ` }} /> ### Constructors Since any function can be registered as a Quill module, we could have implemented our counter as an ES5 constructor or ES6 class. This allows us to access and utilize the module directly.
0
`, 'index.css': ` #editor { border: 1px solid #ccc; } #counter { border: 1px solid #ccc; border-width: 0px 1px 1px 1px; color: #aaa; padding: 5px 15px; text-align: right; } `, 'index.js': ` class Counter { constructor(quill, options) { const container = document.querySelector(options.container); quill.on(Quill.events.TEXT_CHANGE, () => { const text = quill.getText(); if (options.unit === 'word') { container.innerText = text.split(/\\s+/).length + ' words'; } else { container.innerText = text.length + ' characters'; } }); } calculate() { const text = this.quill.getText(); return this.options.unit === 'word' ? text.split(/\\s+/).length : text.length; } } Quill.register('modules/counter', Counter); // We can now initialize Quill with something like this: const quill = new Quill('#editor', { modules: { counter: { container: '#counter', unit: 'word' } } }); ` }} /> ### Wrapping It All Up Now let's polish off the module in ES6 and fix a few pesky bugs. That's all there is to it!
0
`, 'index.css': ` #editor { border: 1px solid #ccc; } #counter { border: 1px solid #ccc; border-width: 0px 1px 1px 1px; color: #aaa; padding: 5px 15px; text-align: right; } `, 'index.js': ` class Counter { constructor(quill, options) { this.quill = quill; this.options = options; this.container = document.querySelector(options.container); quill.on(Quill.events.TEXT_CHANGE, this.update.bind(this)); } calculate() { const text = this.quill.getText(); if (this.options.unit === 'word') { const trimmed = text.trim(); // Splitting empty text returns a non-empty array return trimmed.length > 0 ? trimmed.split(/\\s+/).length : 0; } else { return text.length; } } update() { const length = this.calculate(); let label = this.options.unit; if (length !== 1) { label += 's'; } this.container.innerText = \`\${length} \${label}\`; } } Quill.register('modules/counter', Counter); // We can now initialize Quill with something like this: const quill = new Quill('#editor', { modules: { counter: { container: '#counter', unit: 'word' } } }); ` }} /> ================================================ FILE: packages/website/content/docs/guides/cloning-medium-with-parchment.js ================================================ export const externalResources = [ 'https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css', 'https://fonts.googleapis.com/css?family=Open+Sans%3A300,400,600,700', 'https://platform.twitter.com/widgets.js', ]; export const basicHTML = `
`; export const html = ` ${basicHTML}
Tell your story...
`; export const basicCSS = ` #editor { border: 1px solid #ccc; font-family: 'Open Sans', Helvetica, sans-serif; font-size: 1.2em; height: 180px; margin: 0 auto; width: 450px; } #tooltip-controls, #sidebar-controls { text-align: center; } button { background: transparent; border: none; cursor: pointer; display: inline-block; font-size: 18px; padding: 0; height: 32px; width: 32px; text-align: center; } button:active, button:focus { outline: none; } `; export const boldBlot = ` const Inline = Quill.import('blots/inline'); class BoldBlot extends Inline { static blotName = 'bold'; static tagName = 'strong'; } Quill.register(BoldBlot); `; export const italicBlot = ` const Inline = Quill.import('blots/inline'); class ItalicBlot extends Inline { static blotName = 'italic'; static tagName = 'em'; } Quill.register(ItalicBlot); `; export const linkBlot = ` const Inline = Quill.import('blots/inline'); class LinkBlot extends Inline { static blotName = 'link'; static tagName = 'a'; static create(url) { let node = super.create(); // Sanitize url if desired node.setAttribute('href', url); // Okay to set other non-format related attributes node.setAttribute('target', '_blank'); return node; } static formats(node) { // We will only be called with a node already // determined to be a Link blot, so we do // not need to check ourselves return node.getAttribute('href'); } } Quill.register(LinkBlot); `; export const blockquoteBlot = ` const Block = Quill.import('blots/block'); class BlockquoteBlot extends Block { static blotName = 'blockquote'; static tagName = 'blockquote'; } Quill.register(BlockquoteBlot); `; export const headerBlot = ` const Block = Quill.import('blots/block'); class HeaderBlot extends Block { static blotName = 'header'; static tagName = ['h1', 'h2']; } Quill.register(HeaderBlot); `; export const cssWithBlockquoteAndHeader = ` ${basicCSS} #editor h1 + p, #editor h2 + p { margin-top: 0.5em; } #editor blockquote { border-left: 4px solid #111; padding-left: 1em; } `; export const dividerBlot = ` const BlockEmbed = Quill.import('blots/block/embed'); class DividerBlot extends BlockEmbed { static blotName = 'divider'; static tagName = 'hr'; } Quill.register(DividerBlot); `; export const imageBlot = ` const BlockEmbed = Quill.import('blots/block/embed'); class ImageBlot extends BlockEmbed { static blotName = 'image'; static tagName = 'img'; static create(value) { let node = super.create(); node.setAttribute('alt', value.alt); node.setAttribute('src', value.url); return node; } static value(node) { return { alt: node.getAttribute('alt'), url: node.getAttribute('src') }; } } Quill.register(ImageBlot); `; export const videoBlot = ` const BlockEmbed = Quill.import('blots/block/embed'); class VideoBlot extends BlockEmbed { static blotName = 'video'; static tagName = 'iframe'; static create(url) { let node = super.create(); node.setAttribute('src', url); node.setAttribute('frameborder', '0'); node.setAttribute('allowfullscreen', true); return node; } static formats(node) { let format = {}; if (node.hasAttribute('height')) { format.height = node.getAttribute('height'); } if (node.hasAttribute('width')) { format.width = node.getAttribute('width'); } return format; } static value(node) { return node.getAttribute('src'); } format(name, value) { if (name === 'height' || name === 'width') { if (value) { this.domNode.setAttribute(name, value); } else { this.domNode.removeAttribute(name, value); } } else { super.format(name, value); } } } Quill.register(VideoBlot); `; export const tweetBlot = ` const BlockEmbed = Quill.import('blots/block/embed'); class TweetBlot extends BlockEmbed { static blotName = 'tweet'; static tagName = 'div'; static className = 'tweet'; static create(id) { let node = super.create(); node.dataset.id = id; twttr.widgets.createTweet(id, node); return node; } static value(domNode) { return domNode.dataset.id; } } Quill.register(TweetBlot); `; ================================================ FILE: packages/website/content/docs/guides/cloning-medium-with-parchment.mdx ================================================ --- title: Cloning Medium with Parchment --- To provide a consistent editing experience, you need both consistent data and predictable behaviors. The DOM unfortunately lacks both of these. The solution for modern editors is to maintain their own document model to represent their contents. [Parchment](https://github.com/quilljs/parchment/) is that solution for Quill. It is organized in its own codebase with its own API layer. Through Parchment you can customize the content and formats Quill recognizes, or add entirely new ones. In this guide, we will use the building blocks provided by Parchment and Quill to replicate the editor on Medium. We will start with the bare bones of Quill, without any themes, extraneous modules, or formats. At this basic level, Quill only understands plain text. But by the end of this guide, links, videos, and even tweets will be understood. ## Groundwork Let's start without even using Quill, with just a textarea and button, hooked up to a dummy event listener. We'll use jQuery for convenience throughout this guide, but neither Quill nor Parchment depends on this. We'll also add some basic styling, with the help of [Google Fonts](https://fonts.google.com/) and [Font Awesome](https://fontawesome.io/). None of this has anything to do with Quill or Parchment, so we'll move through quickly. Tell your story... `, 'styles.css': ` #editor { display: block; font-family: 'Open Sans', Helvetica, sans-serif; font-size: 1.2em; height: 180px; margin: 0 auto; width: 450px; } #tooltip-controls, #sidebar-controls { text-align: center; } button { background: transparent; border: none; cursor: pointer; display: inline-block; font-size: 18px; padding: 0; height: 32px; width: 32px; text-align: center; } button:active, button:focus { outline: none; } `, 'index.js': ` document.querySelectorAll('button').forEach((button) => { button.addEventListener('click', () => { alert('Click!'); }); }); ` }} /> ## Adding Quill Core Next, we'll replace the textarea with Quill core, absent of themes, formats and extraneous modules. Open up your developer console to inspect the demo while you type into the editor. You can see the basic building blocks of a Parchment document at work. { button.addEventListener('click', () => { alert('Click!'); }); }); const quill = new Quill('#editor'); ` }} /> Like the DOM, a Parchment document is a tree. Its nodes, called Blots, are an abstraction over DOM Nodes. A few blots are already defined for us: Scroll, Block, Inline, Text and Break. As you type, a Text blot is synchronized with the corresponding DOM Text node; enters are handled by creating a new Block blot. In Parchment, Blots that can have children must have at least one child, so empty Blocks are filled with a Break blot. This makes handling leaves simple and predictable. All this is organized under a root Scroll blot. You cannot observe an Inline blot by just typing at this point since it does not contribute meaningful structure or formatting to the document. A valid Quill document must be canonical and compact. There is only one valid DOM tree that can represent a given document, and that DOM tree contains the minimal number of nodes. Since `

Text

` and `

Text

` represent the same content, the former is invalid and it is part of Quill's optimization process to unwrap the ``. Similarly, once we add formatting, `

Test

` and `

Test

` are also invalid, as they are not the most compact representation. Because of these constraints, **Quill cannot support arbitrary DOM trees and HTML changes**. But as we will see, the consistency and predicability this structure provides enables us to easily build rich editing experiences. ## Basic Formatting We mentioned earlier that an Inline does not contribute formatting. This is the exception, rather than the rule, made for the base Inline class. The base Block blot works the same way for block level elements. To implement bold and italics, we need only to inherit from Inline, set the `blotName` and `tagName`, and register it with Quill. For a compelete reference of the signatures of inherited and static methods and variables, take a look at [Parchment](https://github.com/quilljs/parchment/). ```js const Inline = Quill.import('blots/inline'); class BoldBlot extends Inline { static blotName = 'bold'; static tagName = 'strong'; } class ItalicBlot extends Inline { static blotName = 'italic'; static tagName = 'em'; } Quill.register(BoldBlot); Quill.register(ItalicBlot); ``` We follow Medium's example here in using `strong` and `em` tags but you could just as well use `b` and `i` tags. The name of the blot will be used as the name of the format by Quill. By registering our blots, we can now use Quill's full API on our new formats: ```js Quill.register(BoldBlot); Quill.register(ItalicBlot); const quill = new Quill('#editor'); quill.insertText(0, 'Test', { bold: true }); quill.formatText(0, 4, 'italic', true); // If we named our italic blot "myitalic", we would call // quill.formatText(0, 4, 'myitalic', true); ``` Let's get rid of our dummy button handler and hook up the bold and italic buttons to Quill's [`format()`](/docs/api/#format). We will hardcode `true` to always add formatting for simplicity. In your application, you can use [`getFormat()`](/docs/api/#getformat) to retrieve the current formatting over a arbitrary range to decide whether to add or remove a format. The [Toolbar](/docs/modules/toolbar/) module implements this for Quill, and we will not reimplement it here. Open your developer console and try out Quill's [APIs](/docs/api) on your new bold and italic formats! Make sure to set the context to the correct CodePen iframe to be able to access the `quill` variable in the demo. { document.querySelector(selector).addEventListener('click', callback); }; onClick('#bold-button', () => { quill.format('bold', true); }); onClick('#italic-button', () => { quill.format('italic', true); }); const quill = new Quill('#editor'); ` }} /> Note that if you apply both bold and italic to some text, regardless of what order you do so, Quill wraps the `` tag outside of the `` tag, in a consistent order. ## Links Links are slightly more complicated, since we need more than a boolean to store the link url. This affects our Link blot in two ways: creation and format retrieval. We will represent the url as a string value, but we could easily do so in other ways, such as an object with a url key, allowing for other key/value pairs to be set and define a link. We will demonstrate this later with [images](#images). ```js class LinkBlot extends Inline { static blotName = 'link'; static tagName = 'a'; static create(value) { const node = super.create(); // Sanitize url value if desired node.setAttribute('href', value); // Okay to set other non-format related attributes // These are invisible to Parchment so must be static node.setAttribute('target', '_blank'); return node; } static formats(node) { // We will only be called with a node already // determined to be a Link blot, so we do // not need to check ourselves return node.getAttribute('href'); } } Quill.register(LinkBlot); ``` Now we can hook our link button up to a fancy `prompt`, again to keep things simple, before passing to Quill's `format()`. { document.querySelector(selector).addEventListener('click', callback); }; onClick('#bold-button', () => { quill.format('bold', true); }); onClick('#italic-button', () => { quill.format('italic', true); }); onClick('#link-button', () => { const value = prompt('Enter link URL'); quill.format('link', value); }); const quill = new Quill('#editor'); ` }} /> ## Blockquote and Headers Blockquotes are implemented the same way as Bold blots, except we will inherit from Block, the base block level Blot. While Inline blots can be nested, Block blots cannot. Instead of wrapping, Block blots replace one another when applied to the same text range. ```js const Block = Quill.import('blots/block'); class BlockquoteBlot extends Block { static blotName = 'blockquote'; static tagName = 'blockquote'; } ``` Headers are implemented exactly the same way, with only one difference: it can be represented by more than one DOM element. The value of the format by default becomes the tagName, instead of just `true`. We can customize this by extending `formats()`, similar to how we did so for [links](#links). ```js class HeaderBlot extends Block { static blotName = 'header'; // Medium only supports two header sizes, so we will only demonstrate two, // but we could easily just add more tags into this array static tagName = ['H1', 'H2']; static formats(node) { return HeaderBlot.tagName.indexOf(node.tagName) + 1; } } ``` Let's hook these new blots up to their respective buttons and add some CSS for the `
` tag. { document.querySelector(selector).addEventListener('click', callback); }; onClick('#bold-button', () => { quill.format('bold', true); }); onClick('#italic-button', () => { quill.format('italic', true); }); onClick('#link-button', () => { const value = prompt('Enter link URL'); quill.format('link', value); }); onClick('#blockquote-button', () => { quill.format('blockquote', true); }); onClick('#header-1-button', () => { quill.format('header', 1); }); onClick('#header-2-button', () => { quill.format('header', 2); }); const quill = new Quill('#editor'); ` }} /> Try setting some text to H1, and in your console, run `quill.getContents()`. You will see our custom static `formats()` function at work. Make sure to set the context to the correct CodePen iframe to be able to access the `quill` variable in the demo. ## Dividers Now let's implement our first leaf Blot. While our previous Blot examples contribute formatting and implement `format()`, leaf Blots contribute content and implement `value()`. Leaf Blots can either be Text or Embed Blots, so our section divider will be an Embed. Once created, Embed Blots' value is immutable, requiring deletion and reinsertion to change the content at that location. Our methodology is similar to before, except we inherit from a BlockEmbed. Embed also exists under `blots/embed`, but is meant for inline level blots. We want the block level implementation instead for dividers. ```js const BlockEmbed = Quill.import('blots/block/embed'); class DividerBlot extends BlockEmbed { static blotName = 'divider'; static tagName = 'hr'; } ``` Our click handler calls [`insertEmbed()`](/docs/api/#insertembed), which does not as conveniently determine, save, and restore the user selection for us like [`format()`](/docs/api/#format) does, so we have to do a little more work to preserve selection ourselves. In addition, when we try to insert a BlockEmbed in the middle of the Block, Quill splits the Block for us. To make this behavior more clear, we will explicitly split the block oursevles by inserting a newline before inserting the divider. Take a look at the Babel tab in the CodePen for specifics. { document.querySelector(selector).addEventListener('click', callback); }; onClick('#bold-button', () => { quill.format('bold', true); }); onClick('#italic-button', () => { quill.format('italic', true); }); onClick('#link-button', () => { const value = prompt('Enter link URL'); quill.format('link', value); }); onClick('#blockquote-button', () => { quill.format('blockquote', true); }); onClick('#header-1-button', () => { quill.format('header', 1); }); onClick('#header-2-button', () => { quill.format('header', 2); }); onClick('#divider-button', () => { const range = quill.getSelection(true); quill.insertText(range.index, '\\n', Quill.sources.USER); quill.insertEmbed(range.index + 1, 'divider', true, Quill.sources.USER); quill.setSelection(range.index + 2, Quill.sources.SILENT); }); const quill = new Quill('#editor'); ` }} /> ## Images Images can be added with what we learned building the [Link](#links) and [Divider](#divider) blots. We will use an object for the value to show how this is supported. Our button handler to insert images will use a static value, so we are not distracted by tooltip UI code irrelevant to [Parchment](https://github.com/quilljs/parchment/), the focus of this guide. ```js const BlockEmbed = Quill.import('blots/block/embed'); class ImageBlot extends BlockEmbed { static blotName = 'image'; static tagName = 'img'; static create(value) { const node = super.create(); node.setAttribute('alt', value.alt); node.setAttribute('src', value.url); return node; } static value(node) { return { alt: node.getAttribute('alt'), url: node.getAttribute('src') }; } } ``` { document.querySelector(selector).addEventListener('click', callback); }; onClick('#bold-button', () => { quill.format('bold', true); }); onClick('#italic-button', () => { quill.format('italic', true); }); onClick('#link-button', () => { const value = prompt('Enter link URL'); quill.format('link', value); }); onClick('#blockquote-button', () => { quill.format('blockquote', true); }); onClick('#header-1-button', () => { quill.format('header', 1); }); onClick('#header-2-button', () => { quill.format('header', 2); }); onClick('#divider-button', () => { const range = quill.getSelection(true); quill.insertText(range.index, '\\n', Quill.sources.USER); quill.insertEmbed(range.index + 1, 'divider', true, Quill.sources.USER); quill.setSelection(range.index + 2, Quill.sources.SILENT); }); onClick('#image-button', () => { const range = quill.getSelection(true); quill.insertText(range.index, '\\n', Quill.sources.USER); quill.insertEmbed(range.index + 1, 'image', { alt: 'Quill Cloud', url: 'https://quilljs.com/0.20/assets/images/cloud.png' }, Quill.sources.USER); quill.setSelection(range.index + 2, Quill.sources.SILENT); }); const quill = new Quill('#editor'); ` }} /> ## Videos We will implement videos in a similar way as we did [images](#images). We could use the HTML5 `