diff --git a/app/controllers/wiki_pages_api_controller.rb b/app/controllers/wiki_pages_api_controller.rb index 468767aa3cc..f4c88ddc421 100644 --- a/app/controllers/wiki_pages_api_controller.rb +++ b/app/controllers/wiki_pages_api_controller.rb @@ -354,7 +354,9 @@ class WikiPagesApiController < ApplicationController @wiki = @context.wiki @page = @wiki.build_wiki_page(@current_user, initial_params) if authorized_action(@page, @current_user, :create) - update_params = get_update_params(Set[:title, :body]) + allowed_fields = Set[:title, :body] + allowed_fields << :block_editor_attributes if @context.account.feature_enabled?(:block_editor) + update_params = get_update_params(allowed_fields) assign_todo_date if !update_params.is_a?(Symbol) && @page.update(update_params) && process_front_page log_asset_access(@page, "wiki", @wiki, "participate") @@ -434,6 +436,7 @@ class WikiPagesApiController < ApplicationController if @page.new_record? perform_update = true if authorized_action(@page, @current_user, [:create]) allowed_fields = Set[:title, :body] + allowed_fields << :block_editor_attributes if @context.account.feature_enabled?(:block_editor) elsif authorized_action(@page, @current_user, [:update, :update_content]) perform_update = true allowed_fields = Set[] @@ -639,7 +642,9 @@ class WikiPagesApiController < ApplicationController def get_update_params(allowed_fields = Set[]) # normalize parameters - page_params = params[:wiki_page] ? params[:wiki_page].permit(*%w[title body notify_of_update published front_page editing_roles publish_at]) : {} + wiki_page_params = %w[title body notify_of_update published front_page editing_roles publish_at] + wiki_page_params += [block_editor_attributes: [:time, :version, { blocks: [:id, :type, { data: strong_anything }] }]] if @context.account.feature_enabled?(:block_editor) + page_params = params[:wiki_page] ? params[:wiki_page].permit(*wiki_page_params) : {} if page_params.key?(:published) published_value = page_params.delete(:published) @@ -665,6 +670,10 @@ class WikiPagesApiController < ApplicationController end change_front_page = !!@set_front_page + if page_params.key?(:block_editor_attributes) + page_params[:block_editor_attributes][:root_account_id] = @context.root_account_id + end + # check user permissions rejected_fields = Set[] if @wiki.grants_right?(@current_user, session, :update) @@ -683,6 +692,7 @@ class WikiPagesApiController < ApplicationController unless @page.grants_right?(@current_user, session, :update) allowed_fields << :body + allowed_fields << :block_editor_attributes if @context.account.feature_enabled?(:block_editor) rejected_fields << :title if page_params.include?(:title) && page_params[:title] != @page.title rejected_fields << :front_page if change_front_page && !@wiki.grants_right?(@current_user, session, :update) diff --git a/app/controllers/wiki_pages_controller.rb b/app/controllers/wiki_pages_controller.rb index 5d0152a9201..4d3bbdeffa7 100644 --- a/app/controllers/wiki_pages_controller.rb +++ b/app/controllers/wiki_pages_controller.rb @@ -174,7 +174,8 @@ class WikiPagesController < ApplicationController wiki_page_menu_tools: external_tools_display_hashes(:wiki_page_menu), wiki_index_menu_tools: external_tools_display_hashes(:wiki_index_menu), DISPLAY_SHOW_ALL_LINK: tab_enabled?(context.class::TAB_PAGES, no_render: true) && !@k5_details_view, - CAN_SET_TODO_DATE: context.grants_any_right?(@current_user, session, :manage_content, :manage_course_content_edit) + CAN_SET_TODO_DATE: context.grants_any_right?(@current_user, session, :manage_content, :manage_course_content_edit), + BLOCK_EDITOR: context.account.feature_enabled?(:block_editor) } if Account.site_admin.feature_enabled?(:permanent_page_links) title_availability_path = context.is_a?(Course) ? api_v1_course_page_title_availability_path : api_v1_group_page_title_availability_path diff --git a/app/models/block_editor.rb b/app/models/block_editor.rb new file mode 100644 index 00000000000..3d8a337f196 --- /dev/null +++ b/app/models/block_editor.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +# +# Copyright (C) 2024 - present Instructure, Inc. +# +# This file is part of Canvas. +# +# Canvas is free software: you can redistribute it and/or modify it under +# the terms of the GNU Affero General Public License as published by the Free +# Software Foundation, version 3 of the License. +# +# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +# details. +# +# You should have received a copy of the GNU Affero General Public License along +# with this program. If not, see . +# + +class BlockEditor < ActiveRecord::Base + belongs_to :context, polymorphic: [:wiki_page] + + alias_attribute :version, :editor_version +end diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb index 301ed36754c..e5235b498b8 100644 --- a/app/models/wiki_page.rb +++ b/app/models/wiki_page.rb @@ -59,6 +59,8 @@ class WikiPage < ActiveRecord::Base has_one :master_content_tag, class_name: "MasterCourses::MasterContentTag", inverse_of: :wiki_page has_many :assignment_overrides, dependent: :destroy, inverse_of: :wiki_page has_many :assignment_override_students, dependent: :destroy + has_one :block_editor, as: :context, dependent: :destroy + accepts_nested_attributes_for :block_editor, allow_destroy: true acts_as_url :title, sync_url: true validate :validate_front_page_visibility diff --git a/config/feature_flags/learning_foundations_release_flags.yml b/config/feature_flags/learning_foundations_release_flags.yml index 81c607c738b..cf2623c816a 100644 --- a/config/feature_flags/learning_foundations_release_flags.yml +++ b/config/feature_flags/learning_foundations_release_flags.yml @@ -240,3 +240,11 @@ observer_appointment_groups: state: allowed_on development: state: allowed_on + +block_editor: + applies_to: Account + state: hidden + display_name: Block Editor + description: |- + Enable the new block editor for the rich content editor. + beta: true diff --git a/db/migrate/20240207181908_create_block_editors.rb b/db/migrate/20240207181908_create_block_editors.rb new file mode 100644 index 00000000000..ebdde6ad778 --- /dev/null +++ b/db/migrate/20240207181908_create_block_editors.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +# +# Copyright (C) 2024 - present Instructure, Inc. +# +# This file is part of Canvas. +# +# Canvas is free software: you can redistribute it and/or modify it under +# the terms of the GNU Affero General Public License as published by the Free +# Software Foundation, version 3 of the License. +# +# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +# details. +# +# You should have received a copy of the GNU Affero General Public License along +# with this program. If not, see . +# + +class CreateBlockEditors < ActiveRecord::Migration[7.0] + tag :predeploy + + def change + create_table :block_editors do |t| + t.references :root_account, null: false, foreign_key: { to_table: :accounts }, index: false + t.references :context, polymorphic: true, null: false, index: true + t.bigint :time + t.jsonb :blocks, default: [], null: false + t.string :editor_version + + t.timestamps + end + end +end diff --git a/db/migrate/20240208204317_add_replica_identity_to_block_editors.rb b/db/migrate/20240208204317_add_replica_identity_to_block_editors.rb new file mode 100644 index 00000000000..aca729da8fd --- /dev/null +++ b/db/migrate/20240208204317_add_replica_identity_to_block_editors.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +# +# Copyright (C) 2024 - present Instructure, Inc. +# +# This file is part of Canvas. +# +# Canvas is free software: you can redistribute it and/or modify it under +# the terms of the GNU Affero General Public License as published by the Free +# Software Foundation, version 3 of the License. +# +# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +# details. +# +# You should have received a copy of the GNU Affero General Public License along +# with this program. If not, see . +# + +class AddReplicaIdentityToBlockEditors < ActiveRecord::Migration[7.0] + tag :predeploy + + def change + add_replica_identity "BlockEditor", :root_account_id + end +end diff --git a/spec/apis/v1/wiki_pages_api_spec.rb b/spec/apis/v1/wiki_pages_api_spec.rb index 97c469a89fc..0cb572fc50c 100644 --- a/spec/apis/v1/wiki_pages_api_spec.rb +++ b/spec/apis/v1/wiki_pages_api_spec.rb @@ -130,6 +130,43 @@ describe WikiPagesApiController, type: :request do create_wiki_page(@teacher, { title: "New Page", editing_roles: "public" }, 401) expect(WikiPage.last).to be_nil end + + context "with the block editor" do + context "with the block editor feature flag on" do + before do + Account.default.enable_feature!(:block_editor) + end + + it "succeeds" do + block_editor_attributes = { + time: Time.now.to_i, + blocks: [{ "data" => { "text" => "test" }, "id" => "R0iGYLKhw2", "type" => "paragraph" }], + version: "1.0" + } + create_wiki_page(@teacher, { title: "New Page", block_editor_attributes: }) + expect(WikiPage.last.title).to eq "New Page" + expect(WikiPage.last.block_editor).to be_present + expect(WikiPage.last.block_editor.blocks).to eq([{ "data" => { "text" => "test" }, "id" => "R0iGYLKhw2", "type" => "paragraph" }]) + end + end + + context "with the block editor feature flag off" do + before do + Account.default.disable_feature!(:block_editor) + end + + it "ignores the block_editor_attributes" do + block_editor_attributes = { + time: Time.now.to_i, + blocks: [{ "data" => { "text" => "test" }, "id" => "R0iGYLKhw2", "type" => "paragraph" }], + version: "1.0" + } + create_wiki_page(@teacher, { title: "New Page", block_editor_attributes: }) + expect(WikiPage.last.title).to eq "New Page" + expect(WikiPage.last.block_editor).not_to be_present + end + end + end end context "with the user not having manage_wiki_create permission" do diff --git a/ui/featureBundles.ts b/ui/featureBundles.ts index 7bac570c802..3f895e69719 100644 --- a/ui/featureBundles.ts +++ b/ui/featureBundles.ts @@ -45,6 +45,7 @@ const featureBundles: { available_pronouns_list: () => import('./features/available_pronouns_list/index'), blueprint_course_child: () => import('./features/blueprint_course_child/index'), blueprint_course_master: () => import('./features/blueprint_course_master/index'), + block_editor: () => import('./features/block_editor/index'), brand_configs: () => import('./features/brand_configs/index'), calendar_appointment_group_edit: () => import('./features/calendar_appointment_group_edit/index'), calendar: () => import('./features/calendar/index'), diff --git a/ui/features/block_editor/index.tsx b/ui/features/block_editor/index.tsx new file mode 100644 index 00000000000..b9da1f0cadd --- /dev/null +++ b/ui/features/block_editor/index.tsx @@ -0,0 +1,28 @@ +// @ts-nocheck +/* + * Copyright (C) 2024 - present Instructure, Inc. + * + * This file is part of Canvas. + * + * Canvas is free software: you can redistribute it and/or modify it under + * the terms of the GNU Affero General Public License as published by the Free + * Software Foundation, version 3 of the License. + * + * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + * details. + * + * You should have received a copy of the GNU Affero General Public License along + * with this program. If not, see . + */ + +import React from 'react' +import ReactDOM from 'react-dom' +import ready from '@instructure/ready' + +import {BlockEditor} from '@canvas/block-editor' + +ready(() => { + ReactDOM.render(, document.getElementById('block_editor')) +}) diff --git a/ui/features/block_editor/package.json b/ui/features/block_editor/package.json new file mode 100644 index 00000000000..07b170e2335 --- /dev/null +++ b/ui/features/block_editor/package.json @@ -0,0 +1,6 @@ +{ + "name": "@canvas-features/block_editor", + "private": true, + "version": "0.1.0", + "owner": "LF" +} diff --git a/ui/shared/block-editor/package.json b/ui/shared/block-editor/package.json new file mode 100644 index 00000000000..139b39f00a0 --- /dev/null +++ b/ui/shared/block-editor/package.json @@ -0,0 +1,14 @@ +{ + "name": "@canvas/block-editor", + "private": true, + "version": "1.0.0", + "author": "neme", + "main": "./react/index.tsx", + "dependencies": { + "@editorjs/editorjs": "^2.28.2", + "@editorjs/header": "^2.7.0", + "@editorjs/nested-list": "^1.3.0", + "@editorjs/paragraph": "^2.11.3", + "@editorjs/quote": "^2.5.0" + } +} diff --git a/ui/shared/block-editor/react/BlockEditor.stories.tsx b/ui/shared/block-editor/react/BlockEditor.stories.tsx new file mode 100644 index 00000000000..badb4565b77 --- /dev/null +++ b/ui/shared/block-editor/react/BlockEditor.stories.tsx @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2023 - present Instructure, Inc. + * + * This file is part of Canvas. + * + * Canvas is free software: you can redistribute it and/or modify it under + * the terms of the GNU Affero General Public License as published by the Free + * Software Foundation, version 3 of the License. + * + * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + * details. + * + * You should have received a copy of the GNU Affero General Public License along + * with this program. If not, see . + */ + +import React from 'react' + +import BlockEditor from './BlockEditor' + +export default { + title: 'Examples/Shared/BlockEditor', + component: BlockEditor, +} + +const Template = args => { + return ( + <> + + + ) +} + +export const DefaultEditor = Template.bind({}) diff --git a/ui/shared/block-editor/react/BlockEditor.tsx b/ui/shared/block-editor/react/BlockEditor.tsx new file mode 100644 index 00000000000..1141fe7c7e4 --- /dev/null +++ b/ui/shared/block-editor/react/BlockEditor.tsx @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2023 - present Instructure, Inc. + * + * This file is part of Canvas. + * + * Canvas is free software: you can redistribute it and/or modify it under + * the terms of the GNU Affero General Public License as published by the Free + * Software Foundation, version 3 of the License. + * + * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + * details. + * + * You should have received a copy of the GNU Affero General Public License along + * with this program. If not, see . + */ + +import React, {useRef} from 'react' +import {useScope as useI18nScope} from '@canvas/i18n' +import EditorJS from '@editorjs/editorjs' +import Header from '@editorjs/header' +import NestedList from '@editorjs/nested-list' +import Paragraph from '@editorjs/paragraph' +import Quote from '@editorjs/quote' + +import {View} from '@instructure/ui-view' + +const I18n = useI18nScope('block-editor') + +export default function BlockEditor() { + const editor = useRef(null) + + React.useEffect(() => { + editor.current = new EditorJS({ + holder: 'canvas-block-editor', + tools: { + header: { + class: Header, + inlineToolbar: true, + }, + list: { + class: NestedList, + inlineToolbar: true, + config: { + defaultStyle: 'unordered', + }, + }, + paragraph: { + class: Paragraph, + inlineToolbar: false, + }, + quote: { + class: Quote, + config: { + quotePlaceholder: 'Enter your quote here', + }, + }, + }, + defaultBlock: 'paragraph', + placeholder: I18n.t('Press tab for more options'), + }) + window.block_editor = editor.current + }, []) + + return ( + + +
+ + ) +} diff --git a/ui/shared/block-editor/react/index.tsx b/ui/shared/block-editor/react/index.tsx new file mode 100644 index 00000000000..41fe6b25bdf --- /dev/null +++ b/ui/shared/block-editor/react/index.tsx @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2023 - present Instructure, Inc. + * + * This file is part of Canvas. + * + * Canvas is free software: you can redistribute it and/or modify it under + * the terms of the GNU Affero General Public License as published by the Free + * Software Foundation, version 3 of the License. + * + * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + * details. + * + * You should have received a copy of the GNU Affero General Public License along + * with this program. If not, see . + */ + +import React from 'react' +import ReactDOM from 'react-dom' +import BlockEditor from './BlockEditor' + +export {BlockEditor} + +export default function renderBlockEditorApp(_, elt) { + ReactDOM.render(, elt) +} diff --git a/ui/shared/wiki/backbone/views/WikiPageEditView.jsx b/ui/shared/wiki/backbone/views/WikiPageEditView.jsx index ffb0bbce75f..db00ff93904 100644 --- a/ui/shared/wiki/backbone/views/WikiPageEditView.jsx +++ b/ui/shared/wiki/backbone/views/WikiPageEditView.jsx @@ -19,6 +19,7 @@ import $ from 'jquery' import React from 'react' import ReactDOM from 'react-dom' import RichContentEditor from '@canvas/rce/RichContentEditor' +import {BlockEditor} from '@canvas/block-editor' import template from '../../jst/WikiPageEdit.handlebars' import ValidatedFormView from '@canvas/forms/backbone/views/ValidatedFormView' import WikiPageDeleteDialog from './WikiPageDeleteDialog' @@ -185,7 +186,11 @@ export default class WikiPageEditView extends ValidatedFormView { }) } - RichContentEditor.loadNewEditor(this.$wikiPageBody, {focus: true, manageParent: true}) + if (window.ENV.BLOCK_EDITOR) { + ReactDOM.render(, document.getElementById('block_editor')) + } else { + RichContentEditor.loadNewEditor(this.$wikiPageBody, {focus: true, manageParent: true}) + } this.checkUnsavedOnLeave = true $(window).on('beforeunload', this.onUnload.bind(this)) @@ -291,7 +296,7 @@ export default class WikiPageEditView extends ValidatedFormView { ) } - submit(event) { + async submit(event) { this.checkUnsavedOnLeave = false if (this.reloadPending) { if ( @@ -309,6 +314,13 @@ export default class WikiPageEditView extends ValidatedFormView { return } } + if (window.block_editor) { + let blockEditorData + await window.block_editor.save().then((outputData) => { + blockEditorData = outputData + }) + this.blockEditorData = blockEditorData + } if (this.reloadView != null) { this.reloadView.stopPolling() @@ -349,7 +361,9 @@ export default class WikiPageEditView extends ValidatedFormView { if (page_data.publish_at) { page_data.publish_at = $.unfudgeDateForProfileTimezone(page_data.publish_at) } - + if (this.blockEditorData) { + page_data.block_editor_attributes = this.blockEditorData + } if (this.shouldPublish) page_data.published = true return page_data } diff --git a/ui/shared/wiki/jst/WikiPageEdit.handlebars b/ui/shared/wiki/jst/WikiPageEdit.handlebars index 74f18120d46..a6b000aa0d4 100644 --- a/ui/shared/wiki/jst/WikiPageEdit.handlebars +++ b/ui/shared/wiki/jst/WikiPageEdit.handlebars @@ -17,7 +17,11 @@ {{{body}}} {{else}} - + {{#if ENV.BLOCK_EDITOR}} +
+ {{else}} + + {{/if}} {{/if}} {{#if CAN.EDIT_ROLES}} diff --git a/yarn.lock b/yarn.lock index eb1cf25cf5f..84ef3603893 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1307,6 +1307,21 @@ exec-sh "^0.3.2" minimist "^1.2.0" +"@codexteam/icons@^0.0.2": + version "0.0.2" + resolved "https://registry.yarnpkg.com/@codexteam/icons/-/icons-0.0.2.tgz#9183996a38b75a93506890373a015e3a2a369264" + integrity sha512-KdeKj3TwaTHqM3IXd5YjeJP39PBUZTb+dtHjGlf5+b0VgsxYD4qzsZkb11lzopZbAuDsHaZJmAYQ8LFligIT6Q== + +"@codexteam/icons@^0.0.4": + version "0.0.4" + resolved "https://registry.yarnpkg.com/@codexteam/icons/-/icons-0.0.4.tgz#8b72dcd3f3a1b0d880bdceb2abebd74b46d3ae13" + integrity sha512-V8N/TY2TGyas4wLrPIFq7bcow68b3gu8DfDt1+rrHPtXxcexadKauRJL6eQgfG7Z0LCrN4boLRawR4S9gjIh/Q== + +"@codexteam/icons@^0.0.5": + version "0.0.5" + resolved "https://registry.yarnpkg.com/@codexteam/icons/-/icons-0.0.5.tgz#d17f39b6a0497c6439f57dd42711817a3dd3679c" + integrity sha512-s6H2KXhLz2rgbMZSkRm8dsMJvyUNZsEjxobBEg9ztdrb1B2H3pEzY6iTwI4XUPJWJ3c3qRKwV4TrO3J5jUdoQA== + "@colors/colors@1.5.0": version "1.5.0" resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" @@ -1324,6 +1339,39 @@ resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== +"@editorjs/editorjs@^2.28.2": + version "2.29.0" + resolved "https://registry.yarnpkg.com/@editorjs/editorjs/-/editorjs-2.29.0.tgz#1c9846af19b2afab62a6bb3a815641721c0587f1" + integrity sha512-w2BVboSHokMBd/cAOZn0UU328o3gSZ8XUvFPA2e9+ciIGKILiRSPB70kkNdmhHkuNS3q2px+vdaIFaywBl7wGA== + +"@editorjs/header@^2.7.0": + version "2.8.1" + resolved "https://registry.yarnpkg.com/@editorjs/header/-/header-2.8.1.tgz#ba16f43aaf461aa920c3594bdf0d854c4b5119b9" + integrity sha512-y0HVXRP7m2W617CWo3fsb5HhXmSLaRpb9GzFx0Vkp/HEm9Dz5YO1s8tC7R8JD3MskwoYh7V0hRFQt39io/r6hA== + dependencies: + "@codexteam/icons" "^0.0.5" + +"@editorjs/nested-list@^1.3.0": + version "1.4.2" + resolved "https://registry.yarnpkg.com/@editorjs/nested-list/-/nested-list-1.4.2.tgz#2b47b9c3ee1ce11dec02eae0b176bd4107360847" + integrity sha512-qb1dAoJ+bihqmlR3822TC2GuIxEjTCLTZsZVWNces3uJIZ+W4019G3IJKBt/MOOgz4Evzad/RvUEKwPCPe6YOQ== + dependencies: + "@codexteam/icons" "^0.0.2" + +"@editorjs/paragraph@^2.11.3": + version "2.11.3" + resolved "https://registry.yarnpkg.com/@editorjs/paragraph/-/paragraph-2.11.3.tgz#fb438de863179739f18de7d8851671a0d8447923" + integrity sha512-ON72lhvhgWzPrq4VXpHUeut9bsFeJgVATDeL850FVToOwYHKvdsNpfu0VgxEodhkXgzU/IGl4FzdqC2wd3AJUQ== + dependencies: + "@codexteam/icons" "^0.0.4" + +"@editorjs/quote@^2.5.0": + version "2.6.0" + resolved "https://registry.yarnpkg.com/@editorjs/quote/-/quote-2.6.0.tgz#5ff02b5b3cac1fcea4157c31ac975e3acb3906a8" + integrity sha512-8DiCMBT4n4UDV5bgzvfRH26HmL6YWddGC4+twvjhR+PzX0gwrnY8nFifvro79EeSqxwRtFjjlGnu5I0VTfw5aQ== + dependencies: + "@codexteam/icons" "^0.0.5" + "@emotion/babel-plugin@^11.11.0": version "11.11.0" resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz#c2d872b6a7767a9d176d007f5b31f7d504bb5d6c"