diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 9718e770..bb674844 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,5 +1,5 @@
.apk-add: &apk-add
-- "apk add go-task diffutils"
+- "apk add go-task diffutils gitlab_ci_log_section"
.disable-hainish: &disable-hainish
- "rm -f .env.development"
.cache-ruby: &cache-ruby
@@ -30,13 +30,18 @@ assets:
- *cache-node
- *cache-task
before_script:
+ - "gitlab_ci_log_section --name git --header=\"Configuring git\""
- "git config --global user.email \"${GIT_USER_EMAIL:-$GITLAB_USER_EMAIL}\""
- "git config --global user.name \"${GIT_USER_NAME:-$GITLAB_USER_NAME}\""
- "git remote set-url --push origin \"https://${GITLAB_USERNAME}:${GITLAB_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git\""
+ - "gitlab_ci_log_section --name git --end"
+ - "gitlab_ci_log_section --name apk --header=\"Installing dependencies\""
- "apk add brotli"
- *apk-add
- *disable-hainish
+ - "gitlab_ci_log_section --name apk --end"
script:
+ - "gitlab_ci_log_section --name assets --header=\"Building\""
- "go-task assets"
after_script:
- "git add public && git commit -m \"ci: assets [skip ci]\""
diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss
index 11c44d90..4d1d0848 100644
--- a/app/assets/stylesheets/application.scss
+++ b/app/assets/stylesheets/application.scss
@@ -68,6 +68,25 @@ $sizes: (
--background: #{$black};
--color: #{$cyan};
}
+
+ .btn-secondary {
+ background-color: $white;
+ color: $black;
+ border: none;
+
+ &:hover {
+ color: $black;
+ background-color: $cyan;
+ }
+
+ &:active {
+ background-color: $cyan;
+ }
+
+ &:focus {
+ box-shadow: 0 0 0 0.2rem $cyan;
+ }
+ }
}
// TODO: Encontrar la forma de generar esto desde los locales de Rails
diff --git a/app/controllers/active_storage/direct_uploads_controller_decorator.rb b/app/controllers/active_storage/direct_uploads_controller_decorator.rb
index c62dae2a..94070882 100644
--- a/app/controllers/active_storage/direct_uploads_controller_decorator.rb
+++ b/app/controllers/active_storage/direct_uploads_controller_decorator.rb
@@ -18,8 +18,9 @@ module ActiveStorage
# para que puedan propagarse correctamente a través de todo el
# stack.
def blob_args
- params.require(:blob).permit(:filename, :byte_size, :checksum, :content_type, metadata: {}).to_h.symbolize_keys.tap do |ba|
- ba[:filename] = ba[:filename].unicode_normalize
+ params.require(:blob).permit(:filename, :byte_size, :checksum, :content_type,
+ metadata: {}).to_h.symbolize_keys.tap do |ba|
+ ba[:filename] = ba[:filename].unicode_normalize.sub(/\A_+/, '')
end
end
end
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index bd7b8c4b..b76238a0 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -11,6 +11,7 @@ class ApplicationController < ActionController::Base
before_action :configure_permitted_parameters, if: :devise_controller?
before_action :notify_unconfirmed_email, unless: :devise_controller?
around_action :set_locale
+ after_action :store_location!
before_action do
Rack::MiniProfiler.authorize_request if current_usuarie&.email&.ends_with?('@' + ENV.fetch('SUTTY', 'sutty.nl'))
@@ -106,7 +107,17 @@ class ApplicationController < ActionController::Base
def after_sign_in_path_for(resource)
session[:locale] = nil
- sites_path
+ super
+ end
+
+ # Guardar la ubicación para que devise redirija a donde íbamos, a
+ # menos que estemos recibiendo información o intentando ingresar.
+ def store_location!
+ return if request.xhr?
+ return unless request.request_method_symbol == :GET
+ return if devise_controller? && !is_a?(Devise::RegistrationsController) && params[:action] != 'edit'
+
+ session[:usuarie_return_to] = request.fullpath
end
end
diff --git a/app/controllers/sites_controller.rb b/app/controllers/sites_controller.rb
index bec42b39..e911daac 100644
--- a/app/controllers/sites_controller.rb
+++ b/app/controllers/sites_controller.rb
@@ -15,6 +15,19 @@ class SitesController < ApplicationController
fresh_when @sites
end
+ # Genera la caja del estado para HTMX
+ def status
+ authorize site
+
+ render('sites/status', layout: false) if stale? site
+ end
+
+ def button
+ authorize site
+
+ render('sites/build', layout: false)
+ end
+
# No tenemos propiedades de un sitio aún, así que vamos al listado de
# artículos
def show
diff --git a/app/javascript/etc/htmx_abort.js b/app/javascript/etc/htmx_abort.js
new file mode 100644
index 00000000..308d0315
--- /dev/null
+++ b/app/javascript/etc/htmx_abort.js
@@ -0,0 +1,7 @@
+// Cancela las peticiones pendientes de htmx para todos los elementos al
+// cambiar de página.
+document.addEventListener("turbolinks:click", () => {
+ for (const hx of document.querySelectorAll("[hx-get]")) {
+ window.htmx.trigger(hx, "htmx:abort");
+ }
+});
diff --git a/app/javascript/etc/index.js b/app/javascript/etc/index.js
index d4b9f7a3..9ee6a95a 100644
--- a/app/javascript/etc/index.js
+++ b/app/javascript/etc/index.js
@@ -7,3 +7,4 @@ import './timezone'
import './turbolinks-anchors'
import './validation'
import './new_editor'
+import './htmx_abort'
diff --git a/app/javascript/packs/application.js b/app/javascript/packs/application.js
index 9cbc30bf..e10e2b5d 100644
--- a/app/javascript/packs/application.js
+++ b/app/javascript/packs/application.js
@@ -9,9 +9,16 @@ try {
host: window.env.PANEL_URL
});
+ const ignoredErrors = ["htmx:afterRequest", "htmx:sendAbort"];
+
console.originalError = console.error;
console.error = (...e) => {
- window.airbrake.notify(e.join(" "));
+ const msg = e.join(" ");
+
+ if (!ignoredErrors.some(x => msg.includes(x))) {
+ window.airbrake.notify(e.join(" "));
+ }
+
return console.originalError(...e);
};
} catch(e) {
@@ -33,3 +40,5 @@ import 'chartkick/chart.js'
Rails.start()
Turbolinks.start()
ActiveStorage.start()
+
+window.htmx = require('htmx.org/dist/htmx.js')
diff --git a/app/jobs/gitlab_notifier_job.rb b/app/jobs/gitlab_notifier_job.rb
index 308adfc7..77e865a3 100644
--- a/app/jobs/gitlab_notifier_job.rb
+++ b/app/jobs/gitlab_notifier_job.rb
@@ -105,7 +105,7 @@ class GitlabNotifierJob < ApplicationJob
def title
@title ||= ''.dup.tap do |t|
t << "[#{exception.class}] " unless javascript?
- t << exception.message
+ t << exception.message[0..200]
t << " [#{issue_data[:count]}]"
end
end
diff --git a/app/lib/action_dispatch/http/uploaded_file_decorator.rb b/app/lib/action_dispatch/http/uploaded_file_decorator.rb
index 0bdebdc0..7b28a0db 100644
--- a/app/lib/action_dispatch/http/uploaded_file_decorator.rb
+++ b/app/lib/action_dispatch/http/uploaded_file_decorator.rb
@@ -11,7 +11,7 @@ module ActionDispatch
# Devolver el nombre de archivo con caracteres unicode
# normalizados
def original_filename
- @original_filename.unicode_normalize
+ @original_filename.unicode_normalize.sub(/\A_+/, '')
end
end
end
diff --git a/app/lib/jekyll/readers/data_reader_decorator.rb b/app/lib/jekyll/readers/data_reader_decorator.rb
index 9fed7ac7..2a2a8fc2 100644
--- a/app/lib/jekyll/readers/data_reader_decorator.rb
+++ b/app/lib/jekyll/readers/data_reader_decorator.rb
@@ -14,6 +14,8 @@ module Jekyll
extend ActiveSupport::Concern
included do
+ DATA_EXTENSIONS = %w[.yaml .yml .json .csv .tsv].freeze
+
def read_data_to(dir, data)
return unless File.directory?(dir) && !@entry_filter.symlink?(dir)
@@ -24,7 +26,7 @@ module Jekyll
if File.directory?(path)
read_data_to(path, data[sanitize_filename(entry)] = {})
- else
+ elsif DATA_EXTENSIONS.include?(File.extname(entry))
key = sanitize_filename(File.basename(entry, ".*"))
data[key] = read_data_file(path)
end
diff --git a/app/models/deploy_distributed_press.rb b/app/models/deploy_distributed_press.rb
index da8fe209..678d7083 100644
--- a/app/models/deploy_distributed_press.rb
+++ b/app/models/deploy_distributed_press.rb
@@ -87,7 +87,7 @@ class DeployDistributedPress < Deploy
# @return [Array]
def gateway_urls
remote_info.dig(:distributed_press, :links)&.values&.map do |protocol|
- [ protocol[:link], protocol[:gateway] ]
+ [protocol[:link]]
end&.flatten&.compact&.select do |link|
link.include? '://'
end || []
@@ -128,6 +128,8 @@ class DeployDistributedPress < Deploy
#
# @return [nil]
def create_remote_site!
+ self.hostname ||= site.hostname
+
created_site = site_client.create(create_site)
self.remote_site_id = created_site[:id]
diff --git a/app/models/metadata_path.rb b/app/models/metadata_path.rb
index 95fc7dbb..17085e07 100644
--- a/app/models/metadata_path.rb
+++ b/app/models/metadata_path.rb
@@ -6,7 +6,7 @@ class MetadataPath < MetadataTemplate
#
# @return [String]
def default_value
- File.join(site.path, "_#{lang}", "#{date}-#{slug}#{ext}")
+ File.join(site.path, "_#{lang}", "#{limited_name}#{ext}")
end
# La ruta del archivo según Jekyll
@@ -46,4 +46,12 @@ class MetadataPath < MetadataTemplate
def date
post.date.value.strftime('%F')
end
+
+ # Limita el nombre de archivo a 255 bytes, de forma que siempre
+ # podemos guardarlo
+ #
+ # @return [String]
+ def limited_name
+ "#{date}-#{slug}".mb_chars.limit(255 - ext.length)
+ end
end
diff --git a/app/models/site.rb b/app/models/site.rb
index a9cb3652..ffa4e836 100644
--- a/app/models/site.rb
+++ b/app/models/site.rb
@@ -387,8 +387,10 @@ class Site < ApplicationRecord
end
def reload
- super
- reload_jekyll!
+ super.tap do |s|
+ reload_jekyll!
+ end
+ self
end
def configuration
@@ -578,7 +580,7 @@ class Site < ApplicationRecord
if !gems_installed? || gemfile_updated? || gemfile_lock_updated?
deploy_local.bundle
touch
- ::File.touch(gemfile_path)
+ FileUtils.touch(gemfile_path)
end
end
diff --git a/app/models/site/repository.rb b/app/models/site/repository.rb
index acbf6553..c7056eaa 100644
--- a/app/models/site/repository.rb
+++ b/app/models/site/repository.rb
@@ -169,6 +169,12 @@ class Site
git_sh('git', 'lfs', 'push', remote.name, default_branch)
end
+ # Hace limpieza de LFS
+ def lfs_cleanup
+ git_sh("git", "lfs", "prune")
+ git_sh("git", "lfs", "dedup")
+ end
+
private
# @deprecated
diff --git a/app/policies/site_policy.rb b/app/policies/site_policy.rb
index 2ca96256..ce56a2e7 100644
--- a/app/policies/site_policy.rb
+++ b/app/policies/site_policy.rb
@@ -14,6 +14,10 @@ class SitePolicy
true
end
+ def status?
+ true
+ end
+
# Puede ver la versión privada del sitio?
def private?
edit? && site.deploys.find_by_type('DeployPrivate')
@@ -57,6 +61,10 @@ class SitePolicy
show? && usuarie?
end
+ def button?
+ show?
+ end
+
def enqueue?
build?
end
diff --git a/app/services/cleanup_service.rb b/app/services/cleanup_service.rb
index 28f6f860..e53fbcb4 100644
--- a/app/services/cleanup_service.rb
+++ b/app/services/cleanup_service.rb
@@ -31,7 +31,7 @@ class CleanupService
site.deploys.find_each(&:cleanup!)
site.repository.gc
- lfs_cleanup
+ site.repository.lfs_cleanup
site.touch
end
end
@@ -46,14 +46,8 @@ class CleanupService
Rails.logger.info "Limpiando repositorio git de #{site.name}"
site.repository.gc
- lfs_cleanup
+ site.repository.lfs_cleanup
site.touch
end
end
-
- private
- def lfs_cleanup
- site.repository.git_sh("git", "lfs", "prune")
- site.repository.git_sh("git", "lfs", "dedup")
- end
end
diff --git a/app/views/devise/shared/_links.haml b/app/views/devise/shared/_links.haml
index b4b89175..5d5c0b67 100644
--- a/app/views/devise/shared/_links.haml
+++ b/app/views/devise/shared/_links.haml
@@ -4,12 +4,12 @@
- if controller_name != 'sessions'
= link_to t('.sign_in'), new_session_path(resource_name, params: locale),
- class: 'btn btn-lg btn-block btn-success'
+ class: 'btn btn-lg btn-block btn-secondary'
%br/
- if devise_mapping.registerable? && controller_name != 'registrations'
= link_to t('.sign_up'), new_registration_path(resource_name, params: locale),
- class: 'btn btn-lg btn-block btn-success'
+ class: 'btn btn-lg btn-block btn-secondary'
%br/
- if devise_mapping.recoverable?
diff --git a/app/views/sites/_build.haml b/app/views/sites/_build.haml
index b0961e31..8db4d370 100644
--- a/app/views/sites/_build.haml
+++ b/app/views/sites/_build.haml
@@ -1,9 +1,10 @@
- if policy(site).build?
- = form_tag site_enqueue_path(site),
- method: :post,
- class: 'form-inline inline' do
- = submit_tag site.enqueued? ? t('sites.enqueued') : t('sites.enqueue'),
- class: "btn btn-secondary #{local_assigns[:class]}",
- title: site.enqueued? ? t('help.sites.enqueued') : t('help.sites.enqueue'),
- data: { disable_with: t('sites.enqueued') },
- disabled: site.enqueued?
+ %div{ 'hx-get': site_button_path(site, class: local_assigns[:class]), 'hx-trigger': 'every 10s', 'hx-swap': 'outerHTML' }
+ = form_tag site_enqueue_path(site),
+ method: :post,
+ class: 'form-inline inline' do
+ = submit_tag site.enqueued? ? t('sites.enqueued') : t('sites.enqueue'),
+ class: "btn btn-secondary #{local_assigns[:class]}",
+ title: site.enqueued? ? t('help.sites.enqueued') : t('help.sites.enqueue'),
+ data: { disable_with: t('sites.enqueued') },
+ disabled: !site.waiting?
diff --git a/app/views/sites/_status.haml b/app/views/sites/_status.haml
index 6a610e73..a3dfd4ad 100644
--- a/app/views/sites/_status.haml
+++ b/app/views/sites/_status.haml
@@ -1,21 +1,24 @@
-- link = nil
-- if site.not_published_yet?
- - message = t('.not_published_yet')
-- elsif site.awaiting_publication?
- - message = t('.awaiting_publication')
-- elsif site.building?
- - if site.average_publication_time_calculable?
- - average_building_time = site.average_publication_time
- - elsif !site.similar_sites?
- - average_building_time = 60
+- cache site do
+ - link = nil
+ - if site.not_published_yet?
+ - message = t('.not_published_yet')
+ - elsif site.awaiting_publication?
+ - message = t('.awaiting_publication')
+ - elsif site.building?
+ - if site.average_publication_time_calculable?
+ - average_building_time = site.average_publication_time
+ - elsif !site.similar_sites?
+ - average_building_time = 60
+ - else
+ - average_building_time = site.average_publication_time_for_similar_sites
+
+ - average_publication_time_human = distance_of_time_in_words average_building_time
+ - message = t('.building', average_time: average_publication_time_human, seconds: average_building_time)
- else
- - average_building_time = site.average_publication_time_for_similar_sites
+ - message = t('.available')
+ - link = true
- - average_publication_time_human = distance_of_time_in_words average_building_time
- - message = t('.building', average_time: average_publication_time_human, seconds: average_building_time)
-- else
- - message = t('.available')
- - link = true
-
-= render 'bootstrap/alert' do
- = link_to_if link, message.html_safe, site_build_stats_path(site), class: 'alert-link'
+ -# TODO: Calcular cada cuánto sería óptimo recargar
+ %div{ 'hx-get': site_status_path(site), 'hx-trigger': 'every 10s', 'hx-swap': 'outerHTML' }
+ = render 'bootstrap/alert' do
+ = link_to_if link, message.html_safe, site_build_stats_path(site), class: 'alert-link'
diff --git a/app/views/sites/build.haml b/app/views/sites/build.haml
new file mode 100644
index 00000000..c2becec0
--- /dev/null
+++ b/app/views/sites/build.haml
@@ -0,0 +1 @@
+= render 'sites/build', site: @site, class: params.permit(:class)[:class]
diff --git a/app/views/sites/status.haml b/app/views/sites/status.haml
new file mode 100644
index 00000000..3d9793a5
--- /dev/null
+++ b/app/views/sites/status.haml
@@ -0,0 +1 @@
+= render 'sites/status', site: @site
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 1c9f4576..5e9a2377 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -582,7 +582,7 @@ en:
static_file_migration: 'File migration'
find_and_replace: 'Search and replace'
status:
- building: "Your site is building, refresh this page in ."
+ building: "Your site is building, it will be ready in ."
not_published_yet: "Your site is being published for the first time, please wait up to 1 minute..."
available: "Your site is available! Click here to find all the different ways to visit it."
awaiting_publication: "There are unpublished changes. Click the button below and wait a moment to find them on your site."
diff --git a/config/locales/es.yml b/config/locales/es.yml
index a96d5e60..a07b3799 100644
--- a/config/locales/es.yml
+++ b/config/locales/es.yml
@@ -586,7 +586,7 @@ es:
static_file_migration: 'Migración de archivos'
find_and_replace: 'Búsqueda y reemplazo'
status:
- building: "Tu sitio se está publicando, recargá esta página en ."
+ building: "Tu sitio se está publicando, estará listo en ."
not_published_yet: "Tu sitio se está publicando por primera vez, por favor espera hasta un minuto..."
available: "¡Tu sitio está disponible! Cliqueá aquí para encontrar todas las formas en que podés visitarlo."
awaiting_publication: "Hay cambios sin publicar, cliqueá el botón debajo y espera un momento para encontrarlos en tu sitio."
diff --git a/config/routes.rb b/config/routes.rb
index 4d43d66a..9d5c974a 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -52,6 +52,9 @@ Rails.application.routes.draw do
get 'pull', to: 'sites#fetch'
post 'pull', to: 'sites#merge'
+ get 'status', to: 'sites#status'
+ get 'button', to: 'sites#button'
+
# Gestionar usuaries
get 'usuaries/invite', to: 'usuaries#invite'
post 'usuaries/invite', to: 'usuaries#send_invitations'
diff --git a/package.json b/package.json
index 7901ad41..74ff491a 100644
--- a/package.json
+++ b/package.json
@@ -13,7 +13,7 @@
"@rails/activestorage": "^6.1.3-1",
"@rails/ujs": "^6.1.3-1",
"@rails/webpacker": "5.4.4",
- "@suttyweb/editor": "^0.1.25",
+ "@suttyweb/editor": "^0.1.27",
"babel-loader": "^8.2.2",
"chart.js": "^3.5.1",
"chartkick": "^4.0.5",
@@ -21,6 +21,7 @@
"commonmark": "^0.29.0",
"fork-awesome": "^1.1.7",
"fork-ts-checker-webpack-plugin": "^6.1.0",
+ "htmx.org": "^1.9.11",
"input-map": "git+https://0xacab.org/sutty/input-map.git",
"input-tag": "git+https://0xacab.org/sutty/input-tag.git",
"leaflet": "^1.7.1",
diff --git a/yarn.lock b/yarn.lock
index 0c52b9d3..829b7ed1 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1821,6 +1821,26 @@
resolved "https://registry.npmjs.org/@csstools/convert-colors/-/convert-colors-1.4.0.tgz"
integrity sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw==
+"@floating-ui/core@^1.0.0":
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.6.0.tgz#fa41b87812a16bf123122bf945946bae3fdf7fc1"
+ integrity sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==
+ dependencies:
+ "@floating-ui/utils" "^0.2.1"
+
+"@floating-ui/dom@^1.5.1":
+ version "1.6.3"
+ resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.6.3.tgz#954e46c1dd3ad48e49db9ada7218b0985cee75ef"
+ integrity sha512-RnDthu3mzPlQ31Ss/BTwQ1zjzIhr3lk1gZB1OC56h/1vEtaXkESrOqL5fQVMfXpwGtRwX+YsZBdyHtJMQnkArw==
+ dependencies:
+ "@floating-ui/core" "^1.0.0"
+ "@floating-ui/utils" "^0.2.0"
+
+"@floating-ui/utils@^0.2.0", "@floating-ui/utils@^0.2.1":
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.1.tgz#16308cea045f0fc777b6ff20a9f25474dd8293d2"
+ integrity sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==
+
"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2":
version "0.3.3"
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098"
@@ -1955,11 +1975,13 @@
resolved "https://registry.npmjs.org/@stimulus/webpack-helpers/-/webpack-helpers-1.1.1.tgz"
integrity sha512-XOkqSw53N9072FLHvpLM25PIwy+ndkSSbnTtjKuyzsv8K5yfkFB2rv68jU1pzqYa9FZLcvZWP4yazC0V38dx9A==
-"@suttyweb/editor@^0.1.25":
- version "0.1.25"
- resolved "https://registry.yarnpkg.com/@suttyweb/editor/-/editor-0.1.25.tgz#37b38560642a49b24383473543c28be943695f9f"
- integrity sha512-fxOO9LpdntWzgNZch4cZB6QL0u+jEw0NqsNahKcGBbiJaS0GNGLRrT2LUd/Djc6O8HWkQguPLcquVT5eHq2h9g==
+"@suttyweb/editor@^0.1.27":
+ version "0.1.27"
+ resolved "https://registry.yarnpkg.com/@suttyweb/editor/-/editor-0.1.27.tgz#9415a0b767e72dbe4fbf42ce87e62fb8f5125c31"
+ integrity sha512-Ts9TZtGiRIaHm+ffVBRl+/nuVcANWZNtFsrGacoajgEsagaIyA1cq8qjiNpPoM5ne9vTba3cAaLP04V/uEIhBw==
dependencies:
+ "@floating-ui/dom" "^1.5.1"
+ linkifyjs "^4.1.1"
prosemirror-svelte-nodeview "^1.0.2"
"@types/caseless@*":
@@ -4548,6 +4570,11 @@ html-entities@^1.3.1:
resolved "https://registry.npmjs.org/html-entities/-/html-entities-1.4.0.tgz"
integrity sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==
+htmx.org@^1.9.11:
+ version "1.9.11"
+ resolved "https://registry.yarnpkg.com/htmx.org/-/htmx.org-1.9.11.tgz#00192041ee682d6ca7146d0fbd901169ffe72d87"
+ integrity sha512-WlVuICn8dfNOOgYmdYzYG8zSnP3++AdHkMHooQAzGZObWpVXYathpz/I37ycF4zikR6YduzfCvEcxk20JkIUsw==
+
http-deceiver@^1.2.7:
version "1.2.7"
resolved "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz"
@@ -5190,6 +5217,11 @@ linkify-it@^2.0.0:
dependencies:
uc.micro "^1.0.1"
+linkifyjs@^4.1.1:
+ version "4.1.3"
+ resolved "https://registry.yarnpkg.com/linkifyjs/-/linkifyjs-4.1.3.tgz#0edbc346428a7390a23ea2e5939f76112c9ae07f"
+ integrity sha512-auMesunaJ8yfkHvK4gfg1K0SaKX/6Wn9g2Aac/NwX+l5VdmFZzo/hdPGxEOETj+ryRa4/fiOPjeeKURSAJx1sg==
+
loader-runner@^2.4.0:
version "2.4.0"
resolved "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz"