diff --git a/app/controllers/resources_controller.rb b/app/controllers/resources_controller.rb
index 3586b3ce1..6bba8048b 100644
--- a/app/controllers/resources_controller.rb
+++ b/app/controllers/resources_controller.rb
@@ -35,6 +35,12 @@ def show
render :show
end
+ def rhino_text
+ @resource = Resource.find(resource_id_param).decorate
+ load_forms
+ render :show_test
+ end
+
def create
@resource = current_user.resources.build(resource_params)
if @resource.save
@@ -118,12 +124,12 @@ def resource_id_param
def resource_params
params.require(:resource).permit(
- :text, :kind, :male, :female, :title, :featured, :inactive, :url,
+ :text, :rhino_text, :kind, :male, :female, :title, :featured, :inactive, :url,
:agency, :author, :filemaker_code, :windows_type_id, :ordering,
main_image_attributes: [:id, :file, :_destroy],
gallery_images_attributes: [:id, :file, :_destroy],
categorizable_items_attributes: [:id, :category_id, :_destroy], category_ids: [],
- sectorable_items_attributes: [:id, :sector_id, :is_leader, :_destroy], sector_ids: [],
+ sectorable_items_attributes: [:id, :sector_id, :is_leader, :_destroy], sector_ids: []
)
end
diff --git a/app/frontend/javascript/application.js b/app/frontend/javascript/application.js
index 562d92c85..dfd48449c 100644
--- a/app/frontend/javascript/application.js
+++ b/app/frontend/javascript/application.js
@@ -1,3 +1,7 @@
import "@hotwired/turbo-rails";
+import "@rails/actiontext";
+import "rhino-editor";
+import "rhino-editor/exports/styles/trix.css";
import "./controllers";
+import "./rhino/extend-editor.js";
diff --git a/app/frontend/javascript/controllers/index.js b/app/frontend/javascript/controllers/index.js
index af3655451..93119ebff 100644
--- a/app/frontend/javascript/controllers/index.js
+++ b/app/frontend/javascript/controllers/index.js
@@ -29,3 +29,6 @@ application.register("tabs", TabsController)
import TimeframeController from "./timeframe_controller"
application.register("timeframe", TimeframeController)
+
+import RhinoSourceController from "./rhino_source_controller"
+application.register("rhino-source", RhinoSourceController)
diff --git a/app/frontend/javascript/controllers/rhino_source_controller.js b/app/frontend/javascript/controllers/rhino_source_controller.js
new file mode 100644
index 000000000..88679e718
--- /dev/null
+++ b/app/frontend/javascript/controllers/rhino_source_controller.js
@@ -0,0 +1,35 @@
+import { Controller } from "@hotwired/stimulus"
+
+// Connects to data-controller="rhino-source"
+
+export default class extends Controller {
+ static targets = ["modal", "textarea"]
+
+ connect() {
+ this.modalTarget.classList.add("hidden")
+ this._editor = null
+ }
+
+ registerEditor(editor) {
+ this._editor = editor
+ }
+
+ show(event) {
+ event?.preventDefault()
+ if (!this._editor) return
+ this.textareaTarget.value = this._editor.getHTML()
+ this.modalTarget.classList.remove("hidden")
+ }
+
+ hide(event) {
+ event?.preventDefault()
+ this.modalTarget.classList.add("hidden")
+ }
+
+ save(event) {
+ event.preventDefault()
+ if (!this._editor) return
+ this._editor.chain().focus().setContent(this.textareaTarget.value).run()
+ this.hide()
+ }
+}
diff --git a/app/frontend/javascript/rhino/custom-editor.js b/app/frontend/javascript/rhino/custom-editor.js
new file mode 100644
index 000000000..b5e03d6cd
--- /dev/null
+++ b/app/frontend/javascript/rhino/custom-editor.js
@@ -0,0 +1,439 @@
+// This custom editor
+// extends the default tiptap editor to have a toolbar
+// with table editing buttons in it.
+
+import { html } from "lit"
+import "rhino-editor/exports/styles/trix.css"
+import { TipTapEditor } from "rhino-editor/exports/elements/tip-tap-editor.js"
+import * as table_icons from "./table-icons.js"
+import * as table_translations from "./table-translations.js"
+import { application } from "../controllers/application"
+
+class CustomEditor extends TipTapEditor {
+
+ renderToolbar() {
+
+ if (this.readonly) return html``;
+
+ return html`
+
+
+ ${this.renderToolbarStart()}
+
+
+
+ ${this.renderBoldButton()}
+
+
+
+
+ ${this.renderItalicButton()}
+
+
+
+
+ ${this.renderStrikeButton()}
+
+
+
+
+ ${this.renderLinkButton()}
+
+
+
+ ${this.renderAlignmentButtons()}
+
+
+
+ ${this.renderHeadingButton()}
+
+
+
+
+ ${this.renderBlockquoteButton()}
+
+
+
+
+ ${this.renderCodeBlockButton()}
+
+
+
+
+ ${this.renderBulletListButton()}
+
+
+
+
+
+ ${this.renderOrderedListButton()}
+
+
+
+
+ ${this.renderDecreaseIndentation()}
+
+
+
+ ${this.renderIncreaseIndentation()}
+
+
+
+
+ ${this.renderTableButton()}
+
+
+
+ ${this.renderAttachmentButton()}
+
+
+
+
+
+
+
+
+
+
+ ${this.renderUndoButton()}
+
+
+
+
+ ${this.renderRedoButton()}
+
+
+ ${this.renderToolbarEnd()}
+
+
+ ${this.renderTableMenu()}
+
+ `;
+ }
+
+ renderTableButton() {
+ const tableEnabled = true; // Boolean(this.editor?.commands.setAttachment);
+
+ if (!tableEnabled) return html``;
+
+ const isDisabled = this.editor == null;
+ return html`
+
+ `;
+ }
+ renderTableMenu() {
+ if (!this.editor || !this.editor.isActive('table')) {
+ return html``;
+ }
+ return html`
+
+
+
+
+
+
+
+
+
+
+
+
+ `;
+ }
+
+ renderAlignmentButtons() {
+ if (!this.editor) return html``;
+
+ const alignmentOptions = [
+ { name: 'left', icon: '⬅️' },
+ { name: 'center', icon: '↔️' },
+ { name: 'right', icon: '➡️' },
+ { name: 'justify', icon: '⏹', style: 'margin-inline-end:1rem;' },
+ ];
+
+ const canAlign = ['paragraph', 'heading'].some(type => this.editor.isActive(type));
+ if (!canAlign) return html``;
+
+ return html`
+ ${alignmentOptions.map(
+ align => html`
+
+ `
+ )}
+ `;
+ }
+}
+
+CustomEditor.define("custom-rhino-editor")
diff --git a/app/frontend/javascript/rhino/extend-editor.js b/app/frontend/javascript/rhino/extend-editor.js
new file mode 100644
index 000000000..3fd6a6b53
--- /dev/null
+++ b/app/frontend/javascript/rhino/extend-editor.js
@@ -0,0 +1,26 @@
+import "./custom-editor.js"
+import { Table } from '@tiptap/extension-table'
+import { TableCell } from '@tiptap/extension-table-cell'
+import { TableHeader } from '@tiptap/extension-table-header'
+import { TableRow } from '@tiptap/extension-table-row'
+import Youtube from '@tiptap/extension-youtube'
+import TextAlign from '@tiptap/extension-text-align'
+
+function extendRhinoEditor(event) {
+ const rhinoEditor = event.target
+ if (!rhinoEditor) return
+
+ rhinoEditor.addExtensions(
+ Table,
+ TableRow,
+ TableHeader,
+ TableCell,
+ Youtube.configure({ nocookie: true }),
+ TextAlign.configure({
+ types: ['heading', 'paragraph'],
+ })
+ )
+}
+
+document.addEventListener("rhino-before-initialize", extendRhinoEditor)
+
diff --git a/app/frontend/javascript/rhino/table-icons.js b/app/frontend/javascript/rhino/table-icons.js
new file mode 100644
index 000000000..8355ffa79
--- /dev/null
+++ b/app/frontend/javascript/rhino/table-icons.js
@@ -0,0 +1,88 @@
+import { html, svg } from "lit";
+
+function toSvg(path, size = 24) {
+ return html`
+
+ `
+}
+
+export const insertTable = toSvg(
+ svg``
+);
+
+export const deleteTable = toSvg(
+ svg``
+);
+
+export const addColumnBefore = toSvg(
+ svg``
+);
+
+export const addColumnAfter = toSvg(
+ svg``
+);
+
+export const deleteColumn = toSvg(
+ svg``
+);
+
+export const addRowBefore = toSvg(
+ svg``
+);
+
+export const addRowAfter = toSvg(
+ svg``
+);
+
+export const deleteRow = toSvg(
+ svg``
+);
+
+export const mergeCells = toSvg(
+ svg``
+);
+
+export const splitCells = toSvg(
+ svg``
+);
+
+export const toggleHeaderColumn = toSvg(
+ svg``
+);
+
+export const toggleHeaderRow = toSvg(
+ svg``
+);
+
+export const toggleHeaderCell = toSvg(
+ svg``
+);
+
+export const mergeOrSplit = toSvg(
+ svg``
+);
+
+export const setCellAttribute = toSvg(
+ svg``
+);
+
+export const fixTables = toSvg(
+ svg``
+);
+
+export const goToNextCell = toSvg(
+ svg``
+);
+export const goToPreviousCell = toSvg(
+ svg``
+);
diff --git a/app/frontend/javascript/rhino/table-translations.js b/app/frontend/javascript/rhino/table-translations.js
new file mode 100644
index 000000000..5b52c45c4
--- /dev/null
+++ b/app/frontend/javascript/rhino/table-translations.js
@@ -0,0 +1,18 @@
+export const insertTable = "Insert a Table";
+export const deleteTable = "Delete a Table";
+export const addColumnBefore = "Add A Column Before";
+export const addColumnAfter = "Add a Column After";
+export const deleteColumn = "Delete a Column";
+export const addRowBefore = "Add a Row Before";
+export const addRowAfter = "Add a Row After";
+export const deleteRow = "Delete a Row";
+export const mergeCells = "Merge Cells";
+export const splitCells = "Split Cells";
+export const toggleHeaderColumn = "Toggle Header Column";
+export const toggleHeaderRow = "Toggle Header Row";
+export const toggleHeaderCell = "Toggle Header Cell";
+export const mergeOrSplit = "Merge or Split Cells";
+export const setCellAttribute = "Set Cell Attribute";
+export const fixTables = "Fix Tables";
+export const goToNextCell = "Go To Next Cell";
+export const goToPreviousCell = "Go To Previous Cell";
diff --git a/app/frontend/stylesheets/application.tailwind.css b/app/frontend/stylesheets/application.tailwind.css
index dfdeb639b..0e5f6fd00 100644
--- a/app/frontend/stylesheets/application.tailwind.css
+++ b/app/frontend/stylesheets/application.tailwind.css
@@ -82,3 +82,59 @@
.is-active {
@apply text-primary border-b-2 border-primary font-bold bg-gray-100;
}
+
+
+custom-rhino-editor table {
+ border-collapse: collapse;
+ margin: 0;
+ overflow: hidden;
+ table-layout: fixed;
+ width: 100%;
+}
+custom-rhino-editor table td, custom-rhino-editor table th {
+ border: 2px solid #ced4da;
+ box-sizing: border-box;
+ min-width: 1em;
+ padding: 3px 5px;
+ position: relative;
+ vertical-align: top;
+}
+custom-rhino-editor table td > *, custom-rhino-editor table th > * {
+ margin-bottom: 0;
+}
+custom-rhino-editor table th {
+ background-color: #f1f3f5;
+ font-weight: bold;
+ text-align: left;
+}
+custom-rhino-editor table .selectedCell:after {
+ background: rgba(200, 200, 255, 0.4);
+ content: "";
+ left: 0;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ pointer-events: none;
+ position: absolute;
+ z-index: 2;
+}
+custom-rhino-editor table .column-resize-handle {
+ background-color: #adf;
+ bottom: -2px;
+ position: absolute;
+ right: -2px;
+ pointer-events: none;
+ top: 0;
+ width: 4px;
+}
+custom-rhino-editor table p {
+ margin: 0;
+}
+custom-rhino-editor a {
+ @apply underline text-blue-600 hover:text-blue-800;
+}
+.tableWrapper {
+ padding: 1rem 0;
+ overflow-x: auto;
+
+}
diff --git a/app/helpers/rhino_editor_helper.rb b/app/helpers/rhino_editor_helper.rb
new file mode 100644
index 000000000..10428ff62
--- /dev/null
+++ b/app/helpers/rhino_editor_helper.rb
@@ -0,0 +1,43 @@
+module RhinoEditorHelper
+ # custom rhino editor with stimulus controller attached to edit raw source html
+ def rhino_editor(form, base_attribute_name)
+ rhino_attr = :"rhino_#{base_attribute_name}"
+ field_id = form.field_id(rhino_attr)
+ value = form.object.public_send(rhino_attr)
+
+ hidden = form.hidden_field(
+ rhino_attr,
+ id: field_id,
+ value: value.respond_to?(:to_trix_html) ? value.to_trix_html : value
+ )
+
+ modal = content_tag(:div, data: {rhino_source_target: "modal"}, class: "hidden fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50") do
+ content_tag(:div, class: "bg-white p-4 rounded shadow-lg w-3/4 max-w-2xl") do
+ safe_join([
+ content_tag(:h2, "Edit HTML", class: "text-lg font-bold mb-2"),
+ content_tag(:textarea, nil, data: {rhino_source_target: "textarea"}, class: "w-full h-64 p-2 border rounded prose"),
+ content_tag(:div, class: "mt-2 text-right") do
+ safe_join([
+ content_tag(:button, "Cancel", data: {action: "click->rhino-source#hide"}, class: "mr-2 px-4 py-2 bg-gray-200 rounded"),
+ content_tag(:button, "Save", data: {action: "click->rhino-source#save"}, class: "px-4 py-2 bg-blue-600 text-white rounded")
+ ])
+ end
+ ])
+ end
+ end
+
+ editor = content_tag(
+ :"custom-rhino-editor",
+ nil,
+ input: field_id,
+ data: {
+ blob_url_template: rails_service_blob_url(":signed_id", ":filename"),
+ direct_upload_url: rails_direct_uploads_url
+ }
+ )
+
+ content_tag(:div, data: {controller: "rhino-source"}) do
+ safe_join([modal, hidden, editor])
+ end
+ end
+end
diff --git a/app/models/resource.rb b/app/models/resource.rb
index 5802d20e6..886d77bbf 100644
--- a/app/models/resource.rb
+++ b/app/models/resource.rb
@@ -4,6 +4,8 @@ class Resource < ApplicationRecord
PUBLISHED_KINDS = ["Handout", "Scholarship", "Template", "Toolkit", "Form"]
KINDS = PUBLISHED_KINDS + ["Resource", "Story"]
+ has_rich_text :rhino_text
+
belongs_to :user
belongs_to :workshop, optional: true
belongs_to :windows_type, optional: true
diff --git a/app/services/rich_text_migrator.rb b/app/services/rich_text_migrator.rb
new file mode 100644
index 000000000..ec8ad4a73
--- /dev/null
+++ b/app/services/rich_text_migrator.rb
@@ -0,0 +1,90 @@
+class RichTextMigrator
+ include ActionText::ContentHelper
+
+ PLACEHOLDER_TEXT = "image not found"
+
+ def initialize(record, old_column)
+ @record = record
+ @old_column = old_column.to_s
+ @new_column = "rhino_#{@old_column}"
+ @blobs_by_key = index_resource_blobs
+ end
+
+ def migrate!
+ html = @record.public_send(@old_column)
+ return if html.blank?
+
+ sanitized_html = sanitize_html(html)
+
+ @record.assign_attributes(@new_column => ActionText::Content.new(sanitized_html))
+
+ @record.save(validate: false)
+ end
+
+ private
+
+ def sanitize_html(html)
+ sanitized = ActionController::Base.helpers.sanitize(
+ html,
+ tags: allowed_tags,
+ attributes: allowed_attributes
+ )
+
+ convert_images_to_attachments(sanitized)
+ end
+
+ def allowed_tags
+ ActionText::ContentHelper.allowed_tags
+ end
+
+ def allowed_attributes
+ ActionText::ContentHelper.allowed_attributes
+ end
+
+ def convert_images_to_attachments(html)
+ fragment = Nokogiri::HTML::DocumentFragment.parse(html)
+
+ fragment.css("img").each do |img|
+ aws_key = extract_aws_key(img["src"])
+ blob = @blobs_by_key[aws_key]
+
+ if blob
+ attachment = ActionText::Attachment.from_attachable(blob)
+ img.replace(attachment.to_html)
+ else
+ img.replace(placeholder_node(img["src"]))
+ end
+ end
+
+ fragment.to_html
+ end
+
+ def index_resource_blobs
+ @record.images
+ .includes(file_attachment: :blob)
+ .map(&:file) # get ActiveStorage::Attached::One
+ .map(&:blob)
+ .compact # remove nil blobs
+ .index_by(&:aws_key)
+ end
+
+ def extract_aws_key(src)
+ return if src.blank?
+
+ uri = URI.parse(src)
+ uri.path
+ .sub(%r{^/}, "") # remove leading slash
+ .split("?")
+ .first
+ rescue URI::InvalidURIError
+ nil
+ end
+
+ def placeholder_node(src = nil)
+ text = %([#{PLACEHOLDER_TEXT}#{": #{src}" if src.present?}])
+
+ Nokogiri::HTML::DocumentFragment.parse(
+ "#{ERB::Util.html_escape(text)}"
+ )
+ end
+end
diff --git a/app/views/active_storage/blobs/_blob.html.erb b/app/views/active_storage/blobs/_blob.html.erb
new file mode 100644
index 000000000..49ba357dd
--- /dev/null
+++ b/app/views/active_storage/blobs/_blob.html.erb
@@ -0,0 +1,14 @@
+ attachment--<%= blob.filename.extension %>">
+ <% if blob.representable? %>
+ <%= image_tag blob.representation(resize_to_limit: local_assigns[:in_gallery] ? [ 800, 600 ] : [ 1024, 768 ]) %>
+ <% end %>
+
+
+ <% if caption = blob.try(:caption) %>
+ <%= caption %>
+ <% else %>
+ <%= blob.filename %>
+ <%= number_to_human_size blob.byte_size %>
+ <% end %>
+
+
diff --git a/app/views/layouts/action_text/contents/_content.html.erb b/app/views/layouts/action_text/contents/_content.html.erb
new file mode 100644
index 000000000..9fa0cc4c4
--- /dev/null
+++ b/app/views/layouts/action_text/contents/_content.html.erb
@@ -0,0 +1 @@
+
<%= link_to "Delete", @resource, class: "btn btn-danger-outline", method: :delete, data: { confirm: "Are you sure?" } %>
diff --git a/app/views/resources/show_test.html.erb b/app/views/resources/show_test.html.erb
new file mode 100644
index 000000000..5493fe36c
--- /dev/null
+++ b/app/views/resources/show_test.html.erb
@@ -0,0 +1,101 @@
+
+
+
+
+
+
+ <%= render "bookmarks/editable_bookmark_button", resource: @resource.object %>
+
+
+ <% if current_user.super_user? %>
+ <%= link_to("Edit", edit_resource_path(@resource),
+ class: "admin-only bg-blue-100 btn btn-primary-outline") %>
+ <% end %>
+
+ <%= link_to ("" +
+ " Download ").html_safe,
+ resource_download_path(@resource),
+ class: "btn btn-utility" if @resource.download_attachment&.file&.attached? %>
+
+
+
+
+
+
<%= title_with_badges(@resource, font_size: "text-3xl") %>
+
+
+
+ <% unless @resource.toolkit_and_form? %>
+
+ <%= @resource.author %>
+ <% unless @resource.story? %>
+ ">
+ <%= @resource.display_date %>
+
+ <% end %>
+
+ <% end %>
+
+
+
+
+ <% if @resource.main_image&.file&.attached? %>
+
+ <%= image_tag @resource.main_image.file,
+ class: "w-full rounded-lg shadow-sm border border-gray-200",
+ alt: @resource.title %>
+
+ <% end %>
+
+
+ <% if @resource.gallery_images.any? { |img| img.persisted? && img.file.attached? } %>
+
+
+ <% @resource.gallery_images.each_with_index do |gallery_image, idx| %>
+ <% if gallery_image.file&.attached? %>
+ <%= image_tag url_for(gallery_image.file),
+ alt: "Gallery Image #{idx + 1}",
+ data: { file_preview_target: "preview" },
+ class: "w-32 h-32 object-cover border border-gray-300 shadow-sm" %>
+ <% end %>
+ <% end %>
+
+
+ <% end %>
+
+
+
+ <% if @resource.url.present? %>
+
+ <%= link_to @resource.url, @resource.url, target: "_blank",
+ rel: "noopener noreferrer",
+ class: "text-blue-600 hover:underline break-words" %>
+
+ <% end %>
+
+
+ <%= @resource.rhino_text %>
+
+
+ <% if @resource.attachments.any? %>
+
+
Attachment:
+
+ <% @resource.attachments.each do |attachment| %>
+ <% if attachment.file&.attached? %>
+ -
+ <%= link_to ("" +
+ " #{ attachment.file_file_name } ").html_safe,
+ resource_download_path(@resource,
+ attachment_id: attachment.id),
+ class: "btn btn-utility" if attachment&.file&.attached? %>
+
+ <% end %>
+ <% end %>
+
+
+ <% end %>
+
+
+
+
diff --git a/config/initializers/action_text.rb b/config/initializers/action_text.rb
new file mode 100644
index 000000000..5ded161b0
--- /dev/null
+++ b/config/initializers/action_text.rb
@@ -0,0 +1,5 @@
+default_allowed_tags = Class.new.include(ActionText::ContentHelper).new.sanitizer_allowed_tags
+ActionText::ContentHelper.allowed_tags = default_allowed_tags.merge(%w[iframe table colgroup col thead tbody tfoot tr th td])
+
+default_allowed_attributes = Class.new.include(ActionText::ContentHelper).new.sanitizer_allowed_attributes
+ActionText::ContentHelper.allowed_attributes = default_allowed_attributes.merge(%w[style colspan rowspan cellpadding cellspacing width height align valign])
diff --git a/config/routes.rb b/config/routes.rb
index 86e3917e6..7a3b3ead6 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -67,6 +67,9 @@
resources :reports
resources :resources do
get :download
+ member do
+ get :rhino_text
+ end
collection do
post :search
end
diff --git a/db/migrate/20251217163623_create_action_text_tables.action_text.rb b/db/migrate/20251217163623_create_action_text_tables.action_text.rb
new file mode 100644
index 000000000..c1c0a9d8c
--- /dev/null
+++ b/db/migrate/20251217163623_create_action_text_tables.action_text.rb
@@ -0,0 +1,26 @@
+# This migration comes from action_text (originally 20180528164100)
+class CreateActionTextTables < ActiveRecord::Migration[6.0]
+ def change
+ # Use Active Record's configured type for primary and foreign keys
+ primary_key_type, foreign_key_type = primary_and_foreign_key_types
+
+ create_table :action_text_rich_texts, id: primary_key_type do |t|
+ t.string :name, null: false
+ t.text :body, size: :long
+ t.references :record, null: false, polymorphic: true, index: false, type: foreign_key_type
+
+ t.timestamps
+
+ t.index [ :record_type, :record_id, :name ], name: "index_action_text_rich_texts_uniqueness", unique: true
+ end
+ end
+
+ private
+ def primary_and_foreign_key_types
+ config = Rails.configuration.generators
+ setting = config.options[config.orm][:primary_key_type]
+ primary_key_type = setting || :primary_key
+ foreign_key_type = setting || :bigint
+ [ primary_key_type, foreign_key_type ]
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 99d3cf784..66369a750 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,17 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema[8.1].define(version: 2025_12_04_024120) do
+ActiveRecord::Schema[8.1].define(version: 2025_12_17_163623) do
+ create_table "action_text_rich_texts", charset: "utf8mb3", force: :cascade do |t|
+ t.text "body", size: :long
+ t.datetime "created_at", null: false
+ t.string "name", null: false
+ t.bigint "record_id", null: false
+ t.string "record_type", null: false
+ t.datetime "updated_at", null: false
+ t.index ["record_type", "record_id", "name"], name: "index_action_text_rich_texts_uniqueness", unique: true
+ end
+
create_table "active_storage_attachments", charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t|
t.bigint "blob_id", null: false
t.datetime "created_at", precision: nil, null: false
diff --git a/package-lock.json b/package-lock.json
index 82ce43410..7b1282793 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,11 +8,21 @@
"@fortawesome/fontawesome-free": "^7.0.1",
"@hotwired/stimulus": "^3.2.2",
"@hotwired/turbo-rails": "^8.0.18",
+ "@rails/actiontext": "^8.1.100",
"@rails/request.js": "^0.0.12",
"@tailwindcss/vite": "^4.1.13",
+ "@tiptap/extension-table": "^3.13.0",
+ "@tiptap/extension-table-cell": "^3.13.0",
+ "@tiptap/extension-table-header": "^3.13.0",
+ "@tiptap/extension-table-row": "^3.13.0",
+ "@tiptap/extension-text-align": "^3.13.0",
+ "@tiptap/extension-youtube": "^3.13.0",
+ "from": "^0.1.7",
+ "rhino-editor": "^0.18.0",
"sortablejs": "^1.15.6",
"swiper": "^12.0.3",
"tailwindcss": "^4.1.13",
+ "trix": "^2.1.15",
"vite-plugin-rails": "^0.5.0"
},
"devDependencies": {
@@ -366,6 +376,31 @@
"node": ">=12"
}
},
+ "node_modules/@floating-ui/core": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz",
+ "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/utils": "^0.2.10"
+ }
+ },
+ "node_modules/@floating-ui/dom": {
+ "version": "1.7.4",
+ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz",
+ "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/core": "^1.7.3",
+ "@floating-ui/utils": "^0.2.10"
+ }
+ },
+ "node_modules/@floating-ui/utils": {
+ "version": "0.2.10",
+ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz",
+ "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==",
+ "license": "MIT"
+ },
"node_modules/@fortawesome/fontawesome-free": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-7.0.1.tgz",
@@ -457,6 +492,21 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
+ "node_modules/@lit-labs/ssr-dom-shim": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.4.0.tgz",
+ "integrity": "sha512-ficsEARKnmmW5njugNYKipTm4SFnbik7CXtoencDZzmzo/dQ+2Q0bgkzJuoJP20Aj0F+izzJjOqsnkd6F/o1bw==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@lit/reactive-element": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-2.1.1.tgz",
+ "integrity": "sha512-N+dm5PAYdQ8e6UlywyyrgI2t++wFGXfHx+dSJ1oBrg6FAxUj40jId++EaRm80MKX5JnlH1sBsyZ5h0bcZKemCg==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@lit-labs/ssr-dom-shim": "^1.4.0"
+ }
+ },
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -489,18 +539,51 @@
"node": ">= 8"
}
},
+ "node_modules/@open-wc/dedupe-mixin": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@open-wc/dedupe-mixin/-/dedupe-mixin-1.4.0.tgz",
+ "integrity": "sha512-Sj7gKl1TLcDbF7B6KUhtvr+1UCxdhMbNY5KxdU5IfMFWqL8oy1ZeAcCANjoB1TL0AJTcPmcCFsCbHf8X2jGDUA==",
+ "license": "MIT"
+ },
"node_modules/@rails/actioncable": {
"version": "8.0.300",
"resolved": "https://registry.npmjs.org/@rails/actioncable/-/actioncable-8.0.300.tgz",
"integrity": "sha512-X+jxLnyYciTciEeM9crFFsR6DCodCsnoQIzv4hEST6Lx1rEBEjNQbBopnyDT4gr7lBeHJNfb6fEcvZuWFxUSQg==",
"license": "MIT"
},
+ "node_modules/@rails/actiontext": {
+ "version": "8.1.100",
+ "resolved": "https://registry.npmjs.org/@rails/actiontext/-/actiontext-8.1.100.tgz",
+ "integrity": "sha512-gnLK1HgGCmmVGY40hCiAJ4RwGALwRmiRzqyht6fiM/hrQnul278CqCg9jwV2IQOo7+jMbktGx2O9luGUzayQIA==",
+ "license": "MIT",
+ "dependencies": {
+ "@rails/activestorage": ">= 8.1.0-alpha"
+ },
+ "peerDependencies": {
+ "trix": "^2.0.0"
+ }
+ },
+ "node_modules/@rails/activestorage": {
+ "version": "8.1.100",
+ "resolved": "https://registry.npmjs.org/@rails/activestorage/-/activestorage-8.1.100.tgz",
+ "integrity": "sha512-rlZg23KYT7JFYXl1tj1WD9nm/yvtUvZT6pzNMVX+HaOjPyGq8xpEv1ADrrhawkbMktKxRj3xDVZ3d/qy0akHvw==",
+ "license": "MIT",
+ "dependencies": {
+ "spark-md5": "^3.0.1"
+ }
+ },
"node_modules/@rails/request.js": {
"version": "0.0.12",
"resolved": "https://registry.npmjs.org/@rails/request.js/-/request.js-0.0.12.tgz",
"integrity": "sha512-g3//JBja1s04Zflj7IoMLQuXza9i4ZvtLmm0r0dMwh1QQUs6rL2iKUOGGyERfLsd81SnXC5ucfVV//rtsDlEEA==",
"license": "MIT"
},
+ "node_modules/@remirror/core-constants": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@remirror/core-constants/-/core-constants-3.0.0.tgz",
+ "integrity": "sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==",
+ "license": "MIT"
+ },
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.50.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.50.1.tgz",
@@ -1028,11 +1111,536 @@
"vite": "^5.2.0 || ^6 || ^7"
}
},
+ "node_modules/@tiptap/core": {
+ "version": "3.13.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-3.13.0.tgz",
+ "integrity": "sha512-iUelgiTMgPVMpY5ZqASUpk8mC8HuR9FWKaDzK27w9oWip9tuB54Z8mePTxNcQaSPb6ErzEaC8x8egrRt7OsdGQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/pm": "^3.13.0"
+ }
+ },
+ "node_modules/@tiptap/extension-blockquote": {
+ "version": "3.13.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-3.13.0.tgz",
+ "integrity": "sha512-K1z/PAIIwEmiWbzrP//4cC7iG1TZknDlF1yb42G7qkx2S2X4P0NiqX7sKOej3yqrPjKjGwPujLMSuDnCF87QkQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/core": "^3.13.0"
+ }
+ },
+ "node_modules/@tiptap/extension-bold": {
+ "version": "3.13.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-3.13.0.tgz",
+ "integrity": "sha512-VYiDN9EEwR6ShaDLclG8mphkb/wlIzqfk7hxaKboq1G+NSDj8PcaSI9hldKKtTCLeaSNu6UR5nkdu/YHdzYWTw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/core": "^3.13.0"
+ }
+ },
+ "node_modules/@tiptap/extension-bullet-list": {
+ "version": "3.13.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-3.13.0.tgz",
+ "integrity": "sha512-fFQmmEUoPzRGiQJ/KKutG35ZX21GE+1UCDo8Q6PoWH7Al9lex47nvyeU1BiDYOhcTKgIaJRtEH5lInsOsRJcSA==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/extension-list": "^3.13.0"
+ }
+ },
+ "node_modules/@tiptap/extension-code": {
+ "version": "3.13.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-3.13.0.tgz",
+ "integrity": "sha512-sF5raBni6iSVpXWvwJCAcOXw5/kZ+djDHx1YSGWhopm4+fsj0xW7GvVO+VTwiFjZGKSw+K5NeAxzcQTJZd3Vhw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/core": "^3.13.0"
+ }
+ },
+ "node_modules/@tiptap/extension-code-block": {
+ "version": "3.13.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-3.13.0.tgz",
+ "integrity": "sha512-kIwfQ4iqootsWg9e74iYJK54/YMIj6ahUxEltjZRML5z/h4gTDcQt2eTpnEC8yjDjHeUVOR94zH9auCySyk9CQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/core": "^3.13.0",
+ "@tiptap/pm": "^3.13.0"
+ }
+ },
+ "node_modules/@tiptap/extension-document": {
+ "version": "3.13.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-3.13.0.tgz",
+ "integrity": "sha512-RjU7hTJwjKXIdY57o/Pc+Yr8swLkrwT7PBQ/m+LCX5oO/V2wYoWCjoBYnK5KSHrWlNy/aLzC33BvLeqZZ9nzlQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/core": "^3.13.0"
+ }
+ },
+ "node_modules/@tiptap/extension-dropcursor": {
+ "version": "3.13.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-3.13.0.tgz",
+ "integrity": "sha512-m7GPT3c/83ni+bbU8c+3dpNa8ug+aQ4phNB1Q52VQG3oTonDJnZS7WCtn3lB/Hi1LqoqMtEHwhepU2eD+JeXqQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/extensions": "^3.13.0"
+ }
+ },
+ "node_modules/@tiptap/extension-focus": {
+ "version": "3.13.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-focus/-/extension-focus-3.13.0.tgz",
+ "integrity": "sha512-UXeJcreVqXRLz1f4MX/KmeKCicwOWilCAc1I81s0GVV6kgdqgUodnqRSvQZBpbSZ+vicG+gmYWXOa0S3CurEGg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/extensions": "^3.13.0"
+ }
+ },
+ "node_modules/@tiptap/extension-gapcursor": {
+ "version": "3.13.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-3.13.0.tgz",
+ "integrity": "sha512-KVxjQKkd964nin+1IdM2Dvej/Jy4JTMcMgq5seusUhJ9T9P8F9s2D5Iefwgkps3OCzub/aF+eAsZe+1P5KSIgA==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/extensions": "^3.13.0"
+ }
+ },
+ "node_modules/@tiptap/extension-hard-break": {
+ "version": "3.13.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-3.13.0.tgz",
+ "integrity": "sha512-nH1OBaO+/pakhu+P1jF208mPgB70IKlrR/9d46RMYoYbqJTNf4KVLx5lHAOHytIhjcNg+MjyTfJWfkK+dyCCyg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/core": "^3.13.0"
+ }
+ },
+ "node_modules/@tiptap/extension-heading": {
+ "version": "3.13.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-3.13.0.tgz",
+ "integrity": "sha512-8VKWX8waYPtUWN97J89em9fOtxNteh6pvUEd0htcOAtoxjt2uZjbW5N4lKyWhNKifZBrVhH2Cc2NUPuftCVgxw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/core": "^3.13.0"
+ }
+ },
+ "node_modules/@tiptap/extension-horizontal-rule": {
+ "version": "3.13.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-3.13.0.tgz",
+ "integrity": "sha512-ZUFyORtjj22ib8ykbxRhWFQOTZjNKqOsMQjaAGof30cuD2DN5J5pMz7Haj2fFRtLpugWYH+f0Mi+WumQXC3hCw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/core": "^3.13.0",
+ "@tiptap/pm": "^3.13.0"
+ }
+ },
+ "node_modules/@tiptap/extension-image": {
+ "version": "3.13.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-image/-/extension-image-3.13.0.tgz",
+ "integrity": "sha512-223uzLUkIa1rkK7aQK3AcIXe6LbCtmnpVb7sY5OEp+LpSaSPyXwyrZ4A0EO1o98qXG68/0B2OqMntFtA9c5Fbw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/core": "^3.13.0"
+ }
+ },
+ "node_modules/@tiptap/extension-italic": {
+ "version": "3.13.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-3.13.0.tgz",
+ "integrity": "sha512-XbVTgmzk1kgUMTirA6AGdLTcKHUvEJoh3R4qMdPtwwygEOe7sBuvKuLtF6AwUtpnOM+Y3tfWUTNEDWv9AcEdww==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/core": "^3.13.0"
+ }
+ },
+ "node_modules/@tiptap/extension-link": {
+ "version": "3.13.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-link/-/extension-link-3.13.0.tgz",
+ "integrity": "sha512-LuFPJ5GoL12GHW4A+USsj60O90pLcwUPdvEUSWewl9USyG6gnLnY/j5ZOXPYH7LiwYW8+lhq7ABwrDF2PKyBbA==",
+ "license": "MIT",
+ "dependencies": {
+ "linkifyjs": "^4.3.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/core": "^3.13.0",
+ "@tiptap/pm": "^3.13.0"
+ }
+ },
+ "node_modules/@tiptap/extension-list": {
+ "version": "3.13.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-list/-/extension-list-3.13.0.tgz",
+ "integrity": "sha512-MMFH0jQ4LeCPkJJFyZ77kt6eM/vcKujvTbMzW1xSHCIEA6s4lEcx9QdZMPpfmnOvTzeoVKR4nsu2t2qT9ZXzAw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/core": "^3.13.0",
+ "@tiptap/pm": "^3.13.0"
+ }
+ },
+ "node_modules/@tiptap/extension-list-item": {
+ "version": "3.13.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-3.13.0.tgz",
+ "integrity": "sha512-63NbcS/XeQP2jcdDEnEAE3rjJICDj8y1SN1h/MsJmSt1LusnEo8WQ2ub86QELO6XnD3M04V03cY6Knf6I5mTkw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/extension-list": "^3.13.0"
+ }
+ },
+ "node_modules/@tiptap/extension-list-keymap": {
+ "version": "3.13.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-list-keymap/-/extension-list-keymap-3.13.0.tgz",
+ "integrity": "sha512-P+HtIa1iwosb1feFc8B/9MN5EAwzS+/dZ0UH0CTF2E4wnp5Z9OMxKl1IYjfiCwHzZrU5Let+S/maOvJR/EmV0g==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/extension-list": "^3.13.0"
+ }
+ },
+ "node_modules/@tiptap/extension-ordered-list": {
+ "version": "3.13.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-3.13.0.tgz",
+ "integrity": "sha512-QuDyLzuK/3vCvx9GeKhgvHWrGECBzmJyAx6gli2HY+Iil7XicbfltV4nvhIxgxzpx3LDHLKzJN9pBi+2MzX60g==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/extension-list": "^3.13.0"
+ }
+ },
+ "node_modules/@tiptap/extension-paragraph": {
+ "version": "3.13.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-3.13.0.tgz",
+ "integrity": "sha512-9csQde1i0yeZI5oQQ9e1GYNtGL2JcC2d8Fwtw9FsGC8yz2W0h+Fmk+3bc2kobbtO5LGqupSc1fKM8fAg5rSRDg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/core": "^3.13.0"
+ }
+ },
+ "node_modules/@tiptap/extension-placeholder": {
+ "version": "3.13.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-placeholder/-/extension-placeholder-3.13.0.tgz",
+ "integrity": "sha512-Au4ktRBraQktX9gjSzGWyJV6kPof7+kOhzE8ej+rOMjIrHbx3DCHy1CJWftSO9BbqIyonjsFmm4nE+vjzZ3Z5Q==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/extensions": "^3.13.0"
+ }
+ },
+ "node_modules/@tiptap/extension-strike": {
+ "version": "3.13.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-3.13.0.tgz",
+ "integrity": "sha512-VHhWNqTAMOfrC48m2FcPIZB0nhl6XHQviAV16SBc+EFznKNv9tQUsqQrnuQ2y6ZVfqq5UxvZ3hKF/JlN/Ff7xw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/core": "^3.13.0"
+ }
+ },
+ "node_modules/@tiptap/extension-table": {
+ "version": "3.13.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-table/-/extension-table-3.13.0.tgz",
+ "integrity": "sha512-LcH9KE4QBUJ6IPwt1Uo5iU7zatFjUUvXbctIu2fKQ9nqJ7nNSFxRhkNyporVFkTWYH7/rb0qMoF1VxSUGefG5w==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/core": "^3.13.0",
+ "@tiptap/pm": "^3.13.0"
+ }
+ },
+ "node_modules/@tiptap/extension-table-cell": {
+ "version": "3.13.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-table-cell/-/extension-table-cell-3.13.0.tgz",
+ "integrity": "sha512-dBOPeLe2NqymZI8reS2yZRIcgfIYHudCq4lDWZBJ5NiMvoy0b/fVe1qYSP4EcfBQMpfgAP0E05bMB3C7p1kmkw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/extension-table": "^3.13.0"
+ }
+ },
+ "node_modules/@tiptap/extension-table-header": {
+ "version": "3.13.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-table-header/-/extension-table-header-3.13.0.tgz",
+ "integrity": "sha512-ZA5lhcek0VlKupqj3WFgE6bj5vNRmxcpDqjYc6RWaxuva4dYfEQSqdlG+3a53Sr53le0grfBpolM8zJFCVYrcw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/extension-table": "^3.13.0"
+ }
+ },
+ "node_modules/@tiptap/extension-table-row": {
+ "version": "3.13.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-table-row/-/extension-table-row-3.13.0.tgz",
+ "integrity": "sha512-FdsMTIBNleZtWJEhXE/PBxkB8L9YJtnKs7YZNesLKqLn6/sWZl+X5e5SAAN/N3DLOEAwNCa/m0C3A1DWGNz/tw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/extension-table": "^3.13.0"
+ }
+ },
+ "node_modules/@tiptap/extension-text": {
+ "version": "3.13.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-3.13.0.tgz",
+ "integrity": "sha512-VcZIna93rixw7hRkHGCxDbL3kvJWi80vIT25a2pXg0WP1e7Pi3nBYvZIL4SQtkbBCji9EHrbZx3p8nNPzfazYw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/core": "^3.13.0"
+ }
+ },
+ "node_modules/@tiptap/extension-text-align": {
+ "version": "3.13.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-text-align/-/extension-text-align-3.13.0.tgz",
+ "integrity": "sha512-hebIus9tdXWb+AmhO+LTeUxZLdb0tqwdeaL/0wYxJQR5DeCTlJe6huXacMD/BkmnlEpRhxzQH0FrmXAd0d4Wgg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/core": "^3.13.0"
+ }
+ },
+ "node_modules/@tiptap/extension-underline": {
+ "version": "3.13.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-3.13.0.tgz",
+ "integrity": "sha512-VDQi+UYw0tFnfghpthJTFmtJ3yx90kXeDwFvhmT8G+O+si5VmP05xYDBYBmYCix5jqKigJxEASiBL0gYOgMDEg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/core": "^3.13.0"
+ }
+ },
+ "node_modules/@tiptap/extension-youtube": {
+ "version": "3.13.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-youtube/-/extension-youtube-3.13.0.tgz",
+ "integrity": "sha512-KG6NtdpK9VheacN4imjhDJ8G1V2tJZd/LRr+QID8dYEMQe9vKbqIXgA0kO91t+f+iH8NwgNPmi3a/BQogvoqJg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/core": "^3.13.0"
+ }
+ },
+ "node_modules/@tiptap/extensions": {
+ "version": "3.13.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extensions/-/extensions-3.13.0.tgz",
+ "integrity": "sha512-i7O0ptSibEtTy+2PIPsNKEvhTvMaFJg1W4Oxfnbuxvaigs7cJV9Q0lwDUcc7CPsNw2T1+44wcxg431CzTvdYoA==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/core": "^3.13.0",
+ "@tiptap/pm": "^3.13.0"
+ }
+ },
+ "node_modules/@tiptap/pm": {
+ "version": "3.13.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-3.13.0.tgz",
+ "integrity": "sha512-WKR4ucALq+lwx0WJZW17CspeTpXorbIOpvKv5mulZica6QxqfMhn8n1IXCkDws/mCoLRx4Drk5d377tIjFNsvQ==",
+ "license": "MIT",
+ "dependencies": {
+ "prosemirror-changeset": "^2.3.0",
+ "prosemirror-collab": "^1.3.1",
+ "prosemirror-commands": "^1.6.2",
+ "prosemirror-dropcursor": "^1.8.1",
+ "prosemirror-gapcursor": "^1.3.2",
+ "prosemirror-history": "^1.4.1",
+ "prosemirror-inputrules": "^1.4.0",
+ "prosemirror-keymap": "^1.2.2",
+ "prosemirror-markdown": "^1.13.1",
+ "prosemirror-menu": "^1.2.4",
+ "prosemirror-model": "^1.24.1",
+ "prosemirror-schema-basic": "^1.2.3",
+ "prosemirror-schema-list": "^1.5.0",
+ "prosemirror-state": "^1.4.3",
+ "prosemirror-tables": "^1.6.4",
+ "prosemirror-trailing-node": "^3.0.0",
+ "prosemirror-transform": "^1.10.2",
+ "prosemirror-view": "^1.38.1"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ }
+ },
+ "node_modules/@tiptap/starter-kit": {
+ "version": "3.13.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-3.13.0.tgz",
+ "integrity": "sha512-Ojn6sRub04CRuyQ+9wqN62JUOMv+rG1vXhc2s6DCBCpu28lkCMMW+vTe7kXJcEdbot82+5swPbERw9vohswFzg==",
+ "license": "MIT",
+ "dependencies": {
+ "@tiptap/core": "^3.13.0",
+ "@tiptap/extension-blockquote": "^3.13.0",
+ "@tiptap/extension-bold": "^3.13.0",
+ "@tiptap/extension-bullet-list": "^3.13.0",
+ "@tiptap/extension-code": "^3.13.0",
+ "@tiptap/extension-code-block": "^3.13.0",
+ "@tiptap/extension-document": "^3.13.0",
+ "@tiptap/extension-dropcursor": "^3.13.0",
+ "@tiptap/extension-gapcursor": "^3.13.0",
+ "@tiptap/extension-hard-break": "^3.13.0",
+ "@tiptap/extension-heading": "^3.13.0",
+ "@tiptap/extension-horizontal-rule": "^3.13.0",
+ "@tiptap/extension-italic": "^3.13.0",
+ "@tiptap/extension-link": "^3.13.0",
+ "@tiptap/extension-list": "^3.13.0",
+ "@tiptap/extension-list-item": "^3.13.0",
+ "@tiptap/extension-list-keymap": "^3.13.0",
+ "@tiptap/extension-ordered-list": "^3.13.0",
+ "@tiptap/extension-paragraph": "^3.13.0",
+ "@tiptap/extension-strike": "^3.13.0",
+ "@tiptap/extension-text": "^3.13.0",
+ "@tiptap/extension-underline": "^3.13.0",
+ "@tiptap/extensions": "^3.13.0",
+ "@tiptap/pm": "^3.13.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ }
+ },
"node_modules/@types/estree": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="
},
+ "node_modules/@types/linkify-it": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz",
+ "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==",
+ "license": "MIT"
+ },
+ "node_modules/@types/markdown-it": {
+ "version": "14.1.2",
+ "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz",
+ "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/linkify-it": "^5",
+ "@types/mdurl": "^2"
+ }
+ },
+ "node_modules/@types/mdurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz",
+ "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==",
+ "license": "MIT"
+ },
+ "node_modules/@types/trusted-types": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
+ "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
+ "license": "MIT"
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "license": "Python-2.0"
+ },
"node_modules/braces": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
@@ -1053,6 +1661,12 @@
"node": ">=18"
}
},
+ "node_modules/crelt": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz",
+ "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==",
+ "license": "MIT"
+ },
"node_modules/cssesc": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
@@ -1091,6 +1705,15 @@
"node": ">=8"
}
},
+ "node_modules/dompurify": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.1.tgz",
+ "integrity": "sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==",
+ "license": "(MPL-2.0 OR Apache-2.0)",
+ "optionalDependencies": {
+ "@types/trusted-types": "^2.0.7"
+ }
+ },
"node_modules/enhanced-resolve": {
"version": "5.18.3",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz",
@@ -1104,6 +1727,18 @@
"node": ">=10.13.0"
}
},
+ "node_modules/entities": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+ "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
"node_modules/esbuild": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
@@ -1141,6 +1776,18 @@
"@esbuild/win32-x64": "0.21.5"
}
},
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/fast-glob": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
@@ -1175,6 +1822,18 @@
"node": ">=8"
}
},
+ "node_modules/form-associated-helpers": {
+ "version": "0.0.10",
+ "resolved": "https://registry.npmjs.org/form-associated-helpers/-/form-associated-helpers-0.0.10.tgz",
+ "integrity": "sha512-ClR8Y8muq6EPsmUZsPf+iWUWvRpRzSRGm/ESb5pbhf1PsRSph5cWjpvBlHrNeaBwTGPC1R0kFjAb5kR96ZQe+Q==",
+ "license": "MIT"
+ },
+ "node_modules/from": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz",
+ "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==",
+ "license": "MIT"
+ },
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@@ -1469,6 +2128,52 @@
"url": "https://opencollective.com/parcel"
}
},
+ "node_modules/linkify-it": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz",
+ "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==",
+ "license": "MIT",
+ "dependencies": {
+ "uc.micro": "^2.0.0"
+ }
+ },
+ "node_modules/linkifyjs": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-4.3.2.tgz",
+ "integrity": "sha512-NT1CJtq3hHIreOianA8aSXn6Cw0JzYOuDQbOrSPe7gqFnCpKP++MQe3ODgO3oh2GJFORkAAdqredOa60z63GbA==",
+ "license": "MIT"
+ },
+ "node_modules/lit": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/lit/-/lit-3.3.1.tgz",
+ "integrity": "sha512-Ksr/8L3PTapbdXJCk+EJVB78jDodUMaP54gD24W186zGRARvwrsPfS60wae/SSCTCNZVPd1chXqio1qHQmu4NA==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@lit/reactive-element": "^2.1.0",
+ "lit-element": "^4.2.0",
+ "lit-html": "^3.3.0"
+ }
+ },
+ "node_modules/lit-element": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-4.2.1.tgz",
+ "integrity": "sha512-WGAWRGzirAgyphK2urmYOV72tlvnxw7YfyLDgQ+OZnM9vQQBQnumQ7jUJe6unEzwGU3ahFOjuz1iz1jjrpCPuw==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@lit-labs/ssr-dom-shim": "^1.4.0",
+ "@lit/reactive-element": "^2.1.0",
+ "lit-html": "^3.3.0"
+ }
+ },
+ "node_modules/lit-html": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.3.1.tgz",
+ "integrity": "sha512-S9hbyDu/vs1qNrithiNyeyv64c9yqiW9l+DBgI18fL+MTvOtWoFR0FWiyq1TxaYef5wNlpEmzlXoBlZEO+WjoA==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@types/trusted-types": "^2.0.2"
+ }
+ },
"node_modules/magic-string": {
"version": "0.30.19",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz",
@@ -1478,6 +2183,29 @@
"@jridgewell/sourcemap-codec": "^1.5.5"
}
},
+ "node_modules/markdown-it": {
+ "version": "14.1.0",
+ "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz",
+ "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==",
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1",
+ "entities": "^4.4.0",
+ "linkify-it": "^5.0.0",
+ "mdurl": "^2.0.0",
+ "punycode.js": "^2.3.1",
+ "uc.micro": "^2.1.0"
+ },
+ "bin": {
+ "markdown-it": "bin/markdown-it.mjs"
+ }
+ },
+ "node_modules/mdurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz",
+ "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==",
+ "license": "MIT"
+ },
"node_modules/merge2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@@ -1541,6 +2269,12 @@
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
+ "node_modules/orderedmap": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-2.1.1.tgz",
+ "integrity": "sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==",
+ "license": "MIT"
+ },
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@@ -1598,6 +2332,232 @@
"node": ">=4"
}
},
+ "node_modules/prosemirror-changeset": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/prosemirror-changeset/-/prosemirror-changeset-2.3.1.tgz",
+ "integrity": "sha512-j0kORIBm8ayJNl3zQvD1TTPHJX3g042et6y/KQhZhnPrruO8exkTgG8X+NRpj7kIyMMEx74Xb3DyMIBtO0IKkQ==",
+ "license": "MIT",
+ "dependencies": {
+ "prosemirror-transform": "^1.0.0"
+ }
+ },
+ "node_modules/prosemirror-codemark": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/prosemirror-codemark/-/prosemirror-codemark-0.4.2.tgz",
+ "integrity": "sha512-4n+PnGQToa/vTjn0OiivUvE8/moLtguUAfry8UA4Q8p47MhqT2Qpf2zBLustX5Upi4mSp3z1ZYBqLLovZC6abA==",
+ "license": "MIT",
+ "peerDependencies": {
+ "prosemirror-inputrules": "^1.2.0",
+ "prosemirror-model": "^1.18.1",
+ "prosemirror-state": "^1.4.1",
+ "prosemirror-view": "^1.26.2"
+ }
+ },
+ "node_modules/prosemirror-collab": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/prosemirror-collab/-/prosemirror-collab-1.3.1.tgz",
+ "integrity": "sha512-4SnynYR9TTYaQVXd/ieUvsVV4PDMBzrq2xPUWutHivDuOshZXqQ5rGbZM84HEaXKbLdItse7weMGOUdDVcLKEQ==",
+ "license": "MIT",
+ "dependencies": {
+ "prosemirror-state": "^1.0.0"
+ }
+ },
+ "node_modules/prosemirror-commands": {
+ "version": "1.7.1",
+ "resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.7.1.tgz",
+ "integrity": "sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w==",
+ "license": "MIT",
+ "dependencies": {
+ "prosemirror-model": "^1.0.0",
+ "prosemirror-state": "^1.0.0",
+ "prosemirror-transform": "^1.10.2"
+ }
+ },
+ "node_modules/prosemirror-dropcursor": {
+ "version": "1.8.2",
+ "resolved": "https://registry.npmjs.org/prosemirror-dropcursor/-/prosemirror-dropcursor-1.8.2.tgz",
+ "integrity": "sha512-CCk6Gyx9+Tt2sbYk5NK0nB1ukHi2ryaRgadV/LvyNuO3ena1payM2z6Cg0vO1ebK8cxbzo41ku2DE5Axj1Zuiw==",
+ "license": "MIT",
+ "dependencies": {
+ "prosemirror-state": "^1.0.0",
+ "prosemirror-transform": "^1.1.0",
+ "prosemirror-view": "^1.1.0"
+ }
+ },
+ "node_modules/prosemirror-gapcursor": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/prosemirror-gapcursor/-/prosemirror-gapcursor-1.4.0.tgz",
+ "integrity": "sha512-z00qvurSdCEWUIulij/isHaqu4uLS8r/Fi61IbjdIPJEonQgggbJsLnstW7Lgdk4zQ68/yr6B6bf7sJXowIgdQ==",
+ "license": "MIT",
+ "dependencies": {
+ "prosemirror-keymap": "^1.0.0",
+ "prosemirror-model": "^1.0.0",
+ "prosemirror-state": "^1.0.0",
+ "prosemirror-view": "^1.0.0"
+ }
+ },
+ "node_modules/prosemirror-history": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.5.0.tgz",
+ "integrity": "sha512-zlzTiH01eKA55UAf1MEjtssJeHnGxO0j4K4Dpx+gnmX9n+SHNlDqI2oO1Kv1iPN5B1dm5fsljCfqKF9nFL6HRg==",
+ "license": "MIT",
+ "dependencies": {
+ "prosemirror-state": "^1.2.2",
+ "prosemirror-transform": "^1.0.0",
+ "prosemirror-view": "^1.31.0",
+ "rope-sequence": "^1.3.0"
+ }
+ },
+ "node_modules/prosemirror-inputrules": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/prosemirror-inputrules/-/prosemirror-inputrules-1.5.1.tgz",
+ "integrity": "sha512-7wj4uMjKaXWAQ1CDgxNzNtR9AlsuwzHfdFH1ygEHA2KHF2DOEaXl1CJfNPAKCg9qNEh4rum975QLaCiQPyY6Fw==",
+ "license": "MIT",
+ "dependencies": {
+ "prosemirror-state": "^1.0.0",
+ "prosemirror-transform": "^1.0.0"
+ }
+ },
+ "node_modules/prosemirror-keymap": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.2.3.tgz",
+ "integrity": "sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==",
+ "license": "MIT",
+ "dependencies": {
+ "prosemirror-state": "^1.0.0",
+ "w3c-keyname": "^2.2.0"
+ }
+ },
+ "node_modules/prosemirror-markdown": {
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/prosemirror-markdown/-/prosemirror-markdown-1.13.2.tgz",
+ "integrity": "sha512-FPD9rHPdA9fqzNmIIDhhnYQ6WgNoSWX9StUZ8LEKapaXU9i6XgykaHKhp6XMyXlOWetmaFgGDS/nu/w9/vUc5g==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/markdown-it": "^14.0.0",
+ "markdown-it": "^14.0.0",
+ "prosemirror-model": "^1.25.0"
+ }
+ },
+ "node_modules/prosemirror-menu": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/prosemirror-menu/-/prosemirror-menu-1.2.5.tgz",
+ "integrity": "sha512-qwXzynnpBIeg1D7BAtjOusR+81xCp53j7iWu/IargiRZqRjGIlQuu1f3jFi+ehrHhWMLoyOQTSRx/IWZJqOYtQ==",
+ "license": "MIT",
+ "dependencies": {
+ "crelt": "^1.0.0",
+ "prosemirror-commands": "^1.0.0",
+ "prosemirror-history": "^1.0.0",
+ "prosemirror-state": "^1.0.0"
+ }
+ },
+ "node_modules/prosemirror-model": {
+ "version": "1.25.4",
+ "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.4.tgz",
+ "integrity": "sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA==",
+ "license": "MIT",
+ "dependencies": {
+ "orderedmap": "^2.0.0"
+ }
+ },
+ "node_modules/prosemirror-schema-basic": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/prosemirror-schema-basic/-/prosemirror-schema-basic-1.2.4.tgz",
+ "integrity": "sha512-ELxP4TlX3yr2v5rM7Sb70SqStq5NvI15c0j9j/gjsrO5vaw+fnnpovCLEGIcpeGfifkuqJwl4fon6b+KdrODYQ==",
+ "license": "MIT",
+ "dependencies": {
+ "prosemirror-model": "^1.25.0"
+ }
+ },
+ "node_modules/prosemirror-schema-list": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.5.1.tgz",
+ "integrity": "sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q==",
+ "license": "MIT",
+ "dependencies": {
+ "prosemirror-model": "^1.0.0",
+ "prosemirror-state": "^1.0.0",
+ "prosemirror-transform": "^1.7.3"
+ }
+ },
+ "node_modules/prosemirror-state": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.4.tgz",
+ "integrity": "sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==",
+ "license": "MIT",
+ "dependencies": {
+ "prosemirror-model": "^1.0.0",
+ "prosemirror-transform": "^1.0.0",
+ "prosemirror-view": "^1.27.0"
+ }
+ },
+ "node_modules/prosemirror-tables": {
+ "version": "1.8.3",
+ "resolved": "https://registry.npmjs.org/prosemirror-tables/-/prosemirror-tables-1.8.3.tgz",
+ "integrity": "sha512-wbqCR/RlRPRe41a4LFtmhKElzBEfBTdtAYWNIGHM6X2e24NN/MTNUKyXjjphfAfdQce37Kh/5yf765mLPYDe7Q==",
+ "license": "MIT",
+ "dependencies": {
+ "prosemirror-keymap": "^1.2.3",
+ "prosemirror-model": "^1.25.4",
+ "prosemirror-state": "^1.4.4",
+ "prosemirror-transform": "^1.10.5",
+ "prosemirror-view": "^1.41.4"
+ }
+ },
+ "node_modules/prosemirror-trailing-node": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/prosemirror-trailing-node/-/prosemirror-trailing-node-3.0.0.tgz",
+ "integrity": "sha512-xiun5/3q0w5eRnGYfNlW1uU9W6x5MoFKWwq/0TIRgt09lv7Hcser2QYV8t4muXbEr+Fwo0geYn79Xs4GKywrRQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@remirror/core-constants": "3.0.0",
+ "escape-string-regexp": "^4.0.0"
+ },
+ "peerDependencies": {
+ "prosemirror-model": "^1.22.1",
+ "prosemirror-state": "^1.4.2",
+ "prosemirror-view": "^1.33.8"
+ }
+ },
+ "node_modules/prosemirror-transform": {
+ "version": "1.10.5",
+ "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.10.5.tgz",
+ "integrity": "sha512-RPDQCxIDhIBb1o36xxwsaeAvivO8VLJcgBtzmOwQ64bMtsVFh5SSuJ6dWSxO1UsHTiTXPCgQm3PDJt7p6IOLbw==",
+ "license": "MIT",
+ "dependencies": {
+ "prosemirror-model": "^1.21.0"
+ }
+ },
+ "node_modules/prosemirror-utils": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/prosemirror-utils/-/prosemirror-utils-1.2.2.tgz",
+ "integrity": "sha512-7a2MPf99oCW8/587rQYI1/snX71Ban40+apr1hLkY8TmU9YXd7JeR6QsmktcTisJURO3WRjxIia4lTMsYgZVOw==",
+ "license": "Apache-2.0",
+ "peerDependencies": {
+ "prosemirror-model": "^1.19.2",
+ "prosemirror-state": "^1.4.3"
+ }
+ },
+ "node_modules/prosemirror-view": {
+ "version": "1.41.4",
+ "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.41.4.tgz",
+ "integrity": "sha512-WkKgnyjNncri03Gjaz3IFWvCAE94XoiEgvtr0/r2Xw7R8/IjK3sKLSiDoCHWcsXSAinVaKlGRZDvMCsF1kbzjA==",
+ "license": "MIT",
+ "dependencies": {
+ "prosemirror-model": "^1.20.0",
+ "prosemirror-state": "^1.0.0",
+ "prosemirror-transform": "^1.1.0"
+ }
+ },
+ "node_modules/punycode.js": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz",
+ "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/queue-microtask": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
@@ -1626,6 +2586,44 @@
"node": ">=0.10.0"
}
},
+ "node_modules/rhino-editor": {
+ "version": "0.18.0",
+ "resolved": "https://registry.npmjs.org/rhino-editor/-/rhino-editor-0.18.0.tgz",
+ "integrity": "sha512-5n9VHb7dfeSIDuc7aJUWc+7EmRNCMvha32yV/hvL89vjXliIDvENSy+C4808HsvRrHsbJpMBXs+36MTDL2fBTQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@rails/activestorage": "^8.0.201",
+ "@tiptap/core": "^3.4.0",
+ "@tiptap/extension-code": "^3.4.0",
+ "@tiptap/extension-code-block": "^3.4.0",
+ "@tiptap/extension-focus": "^3.4.0",
+ "@tiptap/extension-image": "^3.4.0",
+ "@tiptap/extension-link": "^3.4.0",
+ "@tiptap/extension-placeholder": "^3.4.0",
+ "@tiptap/extension-strike": "^3.4.0",
+ "@tiptap/pm": "^3.4.0",
+ "@tiptap/starter-kit": "^3.4.0",
+ "linkifyjs": "^4.1.3",
+ "lit": "^3.3.1",
+ "prosemirror-codemark": "^0.4.2",
+ "prosemirror-utils": "^1.2.2",
+ "prosemirror-view": "^1.40.1",
+ "role-components": "^3.1.0",
+ "tslib": "^2.8.1"
+ }
+ },
+ "node_modules/role-components": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/role-components/-/role-components-3.1.0.tgz",
+ "integrity": "sha512-d0vq9H4Hqe24fGzd79s+e/X/GzNz+OI5dsThsn1cNaix+VKfr/I+UbkErZ8G8lWEqWe+ULOTa25/t5oRng2xkQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/dom": "^1.6.5",
+ "form-associated-helpers": "^0.0.10",
+ "lit": "^3",
+ "web-component-define": "^2.0.11"
+ }
+ },
"node_modules/rollup": {
"version": "4.50.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.50.1.tgz",
@@ -1676,6 +2674,12 @@
"rollup": ">=2.0.0"
}
},
+ "node_modules/rope-sequence": {
+ "version": "1.3.4",
+ "resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.4.tgz",
+ "integrity": "sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==",
+ "license": "MIT"
+ },
"node_modules/run-parallel": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
@@ -1712,6 +2716,12 @@
"node": ">=0.10.0"
}
},
+ "node_modules/spark-md5": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/spark-md5/-/spark-md5-3.0.2.tgz",
+ "integrity": "sha512-wcFzz9cDfbuqe0FZzfi2or1sgyIrsDwmPwfZC4hiNidPdPINjeUwNfv5kldczoEAcjl9Y1L3SM7Uz2PUEQzxQw==",
+ "license": "(WTFPL OR MIT)"
+ },
"node_modules/stimulus-vite-helpers": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/stimulus-vite-helpers/-/stimulus-vite-helpers-3.1.0.tgz",
@@ -1782,6 +2792,27 @@
"node": ">=8.0"
}
},
+ "node_modules/trix": {
+ "version": "2.1.15",
+ "resolved": "https://registry.npmjs.org/trix/-/trix-2.1.15.tgz",
+ "integrity": "sha512-LoaXWczdTUV8+3Box92B9b1iaDVbxD14dYemZRxi3PwY+AuDm97BUJV2aHLBUFPuDABhxp0wzcbf0CxHCVmXiw==",
+ "license": "MIT",
+ "dependencies": {
+ "dompurify": "^3.2.5"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "license": "0BSD"
+ },
+ "node_modules/uc.micro": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz",
+ "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==",
+ "license": "MIT"
+ },
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@@ -1908,6 +2939,31 @@
"stimulus-vite-helpers": "^3.0.0"
}
},
+ "node_modules/w3c-keyname": {
+ "version": "2.2.8",
+ "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
+ "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==",
+ "license": "MIT"
+ },
+ "node_modules/web-component-define": {
+ "version": "2.0.11",
+ "resolved": "https://registry.npmjs.org/web-component-define/-/web-component-define-2.0.11.tgz",
+ "integrity": "sha512-oMOngNGL5k55NyaQNnPz0UnV5QtPuESvSuxpBgmHHfNjb8sgVm4akAFq7HO4I5bhGHX8Of+0XnUZ3whBo0chgw==",
+ "license": "MIT",
+ "dependencies": {
+ "@lit/reactive-element": "^1.6.1",
+ "@open-wc/dedupe-mixin": "^1.3.1"
+ }
+ },
+ "node_modules/web-component-define/node_modules/@lit/reactive-element": {
+ "version": "1.6.3",
+ "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-1.6.3.tgz",
+ "integrity": "sha512-QuTgnG52Poic7uM1AN5yJ09QMe0O28e10XzSvWDz02TJiiKee4stsiownEIadWm8nYzyDAyT+gKzUoZmiWQtsQ==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@lit-labs/ssr-dom-shim": "^1.0.0"
+ }
+ },
"node_modules/yallist": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz",
diff --git a/package.json b/package.json
index fdeedf59f..6217ab8a1 100644
--- a/package.json
+++ b/package.json
@@ -8,11 +8,21 @@
"@fortawesome/fontawesome-free": "^7.0.1",
"@hotwired/stimulus": "^3.2.2",
"@hotwired/turbo-rails": "^8.0.18",
+ "@rails/actiontext": "^8.1.100",
"@rails/request.js": "^0.0.12",
"@tailwindcss/vite": "^4.1.13",
+ "@tiptap/extension-table": "^3.13.0",
+ "@tiptap/extension-table-cell": "^3.13.0",
+ "@tiptap/extension-table-header": "^3.13.0",
+ "@tiptap/extension-table-row": "^3.13.0",
+ "@tiptap/extension-text-align": "^3.13.0",
+ "@tiptap/extension-youtube": "^3.13.0",
+ "from": "^0.1.7",
+ "rhino-editor": "^0.18.0",
"sortablejs": "^1.15.6",
"swiper": "^12.0.3",
"tailwindcss": "^4.1.13",
+ "trix": "^2.1.15",
"vite-plugin-rails": "^0.5.0"
}
}