Init project

This commit is contained in:
2026-01-11 16:19:42 +01:00
commit df59325836
380 changed files with 33805 additions and 0 deletions

17
.editorconfig Normal file
View File

@@ -0,0 +1,17 @@
# editorconfig.org
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[{compose.yaml,compose.*.yaml}]
indent_size = 2
[*.md]
trim_trailing_whitespace = false

47
.env Normal file
View File

@@ -0,0 +1,47 @@
# In all environments, the following files are loaded if they exist,
# the latter taking precedence over the former:
#
# * .env contains default values for the environment variables needed by the app
# * .env.local uncommitted file with local overrides
# * .env.$APP_ENV committed environment-specific defaults
# * .env.$APP_ENV.local uncommitted environment-specific overrides
#
# Real environment variables win over .env files.
#
# DO NOT DEFINE PRODUCTION SECRETS IN THIS FILE NOR IN ANY OTHER COMMITTED FILES.
# https://symfony.com/doc/current/configuration/secrets.html
#
# Run "composer dump-env prod" to compile .env files for production use (requires symfony/flex >=1.2).
# https://symfony.com/doc/current/best_practices.html#use-environment-variables-for-infrastructure-configuration
###> symfony/framework-bundle ###
APP_ENV=dev
APP_SECRET=
###< symfony/framework-bundle ###
###> symfony/routing ###
# Configure how to generate URLs in non-HTTP contexts, such as CLI commands.
# See https://symfony.com/doc/current/routing.html#generating-urls-in-commands
DEFAULT_URI=http://localhost
###< symfony/routing ###
###> doctrine/doctrine-bundle ###
# Format described at https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url
# IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml
#
# DATABASE_URL="sqlite:///%kernel.project_dir%/var/data_%kernel.environment%.db"
# DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=8.0.32&charset=utf8mb4"
# DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=10.11.2-MariaDB&charset=utf8mb4"
DATABASE_URL="postgresql://app:!ChangeMe!@127.0.0.1:5432/app?serverVersion=16&charset=utf8"
###< doctrine/doctrine-bundle ###
###> symfony/messenger ###
# Choose one of the transports below
# MESSENGER_TRANSPORT_DSN=amqp://guest:guest@localhost:5672/%2f/messages
# MESSENGER_TRANSPORT_DSN=redis://localhost:6379/messages
MESSENGER_TRANSPORT_DSN=doctrine://default?auto_setup=0
###< symfony/messenger ###
###> symfony/mailer ###
MAILER_DSN=null://null
###< symfony/mailer ###

4
.env.dev Normal file
View File

@@ -0,0 +1,4 @@
###> symfony/framework-bundle ###
APP_SECRET=a67d1dd72d40ab0515ec1158403570b6
###< symfony/framework-bundle ###

3
.env.test Normal file
View File

@@ -0,0 +1,3 @@
# define your env variables for the test env here
KERNEL_CLASS='App\Kernel'
APP_SECRET='$ecretf0rt3st'

24
.gitignore vendored Normal file
View File

@@ -0,0 +1,24 @@
###> symfony/framework-bundle ###
/.env.local
/.env.local.php
/.env.*.local
/config/secrets/prod/prod.decrypt.private.php
/public/bundles/
/var/
/vendor/
###< symfony/framework-bundle ###
###> phpunit/phpunit ###
/phpunit.xml
/.phpunit.cache/
###< phpunit/phpunit ###
###> symfony/webpack-encore-bundle ###
/node_modules/
/public/build/
npm-debug.log
yarn-error.log
###< symfony/webpack-encore-bundle ###
/.vscode
/.zed

14
.prettierrc Normal file
View File

@@ -0,0 +1,14 @@
{
"plugins": ["@zackad/prettier-plugin-twig"],
"tabWidth": 2,
"useTabs": false,
"overrides": [
{
"files": ["*.jsonc"],
"options": {
"parser": "json",
"trailingComma": "none"
}
}
]
}

12
assets/app.js Normal file
View File

@@ -0,0 +1,12 @@
import "./stimulus_bootstrap.js";
/*
* Welcome to your app's main JavaScript file!
*
* We recommend including the built version of this JavaScript file
* (and its CSS file) in your base layout (base.html.twig).
*/
// any CSS you import will output into a single css file (app.css in this case)
import "./styles/app.css";
import "./js/ckeditor-init.js";
import "./styles/css/ckeditor5.css";

15
assets/controllers.json Normal file
View File

@@ -0,0 +1,15 @@
{
"controllers": {
"@symfony/ux-turbo": {
"turbo-core": {
"enabled": true,
"fetch": "eager"
},
"mercure-turbo-stream": {
"enabled": false,
"fetch": "eager"
}
}
},
"entrypoints": []
}

View File

@@ -0,0 +1,15 @@
// assets/controllers/ckeditor5_controller.js
import { Controller } from "@hotwired/stimulus";
import EnhancedEditor from "../js/ckeditor-init.js";
export default class extends Controller {
connect() {
this.editor = EnhancedEditor.create(this.element)
.then(editor => (this.editor = editor))
.catch(error => console.error(error));
}
disconnect() {
this.editor.destroy().catch(error => console.error(error));
}
}

View File

@@ -0,0 +1,64 @@
import { Controller } from "@hotwired/stimulus";
export default class extends Controller {
static targets = [
"loaderWrapper",
"loaderProgressBar",
"list",
"imagesWrapper",
];
connect() {
if (!this.hasListTarget || !this.hasImagesWrapperTarget) {
return;
}
const images = Array.from(this.listTarget.querySelectorAll("img"));
// 1⃣ avant chargement → tout cacher
this.loaderWrapperTarget.classList.add("hidden");
this.imagesWrapperTarget.style.opacity = 0;
if (images.length > 0) {
this.trackImages(images);
}
}
trackImages(images) {
let loadedCount = 0;
// 2⃣ début du chargement → afficher le loader
this.loaderWrapperTarget.classList.remove("hidden");
this.loaderProgressBarTarget.style.width = "0%";
images.forEach((img) => {
if (img.complete) {
loadedCount++;
this.updateProgress(loadedCount, images.length);
} else {
img.addEventListener("load", () => {
loadedCount++;
this.updateProgress(loadedCount, images.length);
});
img.addEventListener("error", () => {
loadedCount++;
this.updateProgress(loadedCount, images.length);
});
}
});
}
updateProgress(loadedCount, total) {
const progress = (loadedCount / total) * 100;
this.loaderProgressBarTarget.style.width = `${progress}%`;
if (loadedCount === total) {
// 3⃣ chargement terminé → masquer le loader + afficher les images
setTimeout(() => {
this.loaderWrapperTarget.classList.add("hidden");
this.imagesWrapperTarget.style.transition = "opacity .3s ease";
this.imagesWrapperTarget.style.opacity = 1;
}, 200);
}
}
}

View File

@@ -0,0 +1,81 @@
const nameCheck = /^[-_a-zA-Z0-9]{4,22}$/;
const tokenCheck = /^[-_/+a-zA-Z0-9]{24,}$/;
// Generate and double-submit a CSRF token in a form field and a cookie, as defined by Symfony's SameOriginCsrfTokenManager
// Use `form.requestSubmit()` to ensure that the submit event is triggered. Using `form.submit()` will not trigger the event
// and thus this event-listener will not be executed.
document.addEventListener('submit', function (event) {
generateCsrfToken(event.target);
}, true);
// When @hotwired/turbo handles form submissions, send the CSRF token in a header in addition to a cookie
// The `framework.csrf_protection.check_header` config option needs to be enabled for the header to be checked
document.addEventListener('turbo:submit-start', function (event) {
const h = generateCsrfHeaders(event.detail.formSubmission.formElement);
Object.keys(h).map(function (k) {
event.detail.formSubmission.fetchRequest.headers[k] = h[k];
});
});
// When @hotwired/turbo handles form submissions, remove the CSRF cookie once a form has been submitted
document.addEventListener('turbo:submit-end', function (event) {
removeCsrfToken(event.detail.formSubmission.formElement);
});
export function generateCsrfToken (formElement) {
const csrfField = formElement.querySelector('input[data-controller="csrf-protection"], input[name="_csrf_token"]');
if (!csrfField) {
return;
}
let csrfCookie = csrfField.getAttribute('data-csrf-protection-cookie-value');
let csrfToken = csrfField.value;
if (!csrfCookie && nameCheck.test(csrfToken)) {
csrfField.setAttribute('data-csrf-protection-cookie-value', csrfCookie = csrfToken);
csrfField.defaultValue = csrfToken = btoa(String.fromCharCode.apply(null, (window.crypto || window.msCrypto).getRandomValues(new Uint8Array(18))));
}
csrfField.dispatchEvent(new Event('change', { bubbles: true }));
if (csrfCookie && tokenCheck.test(csrfToken)) {
const cookie = csrfCookie + '_' + csrfToken + '=' + csrfCookie + '; path=/; samesite=strict';
document.cookie = window.location.protocol === 'https:' ? '__Host-' + cookie + '; secure' : cookie;
}
}
export function generateCsrfHeaders (formElement) {
const headers = {};
const csrfField = formElement.querySelector('input[data-controller="csrf-protection"], input[name="_csrf_token"]');
if (!csrfField) {
return headers;
}
const csrfCookie = csrfField.getAttribute('data-csrf-protection-cookie-value');
if (tokenCheck.test(csrfField.value) && nameCheck.test(csrfCookie)) {
headers[csrfCookie] = csrfField.value;
}
return headers;
}
export function removeCsrfToken (formElement) {
const csrfField = formElement.querySelector('input[data-controller="csrf-protection"], input[name="_csrf_token"]');
if (!csrfField) {
return;
}
const csrfCookie = csrfField.getAttribute('data-csrf-protection-cookie-value');
if (tokenCheck.test(csrfField.value) && nameCheck.test(csrfCookie)) {
const cookie = csrfCookie + '_' + csrfField.value + '=0; path=/; samesite=strict; max-age=0';
document.cookie = window.location.protocol === 'https:' ? '__Host-' + cookie + '; secure' : cookie;
}
}
/* stimulusFetch: 'lazy' */
export default 'csrf-protection-controller';

View File

@@ -0,0 +1,28 @@
import { Controller } from "@hotwired/stimulus";
export default class extends Controller {
connect() {
let usersTable = document.getElementById("users-table");
if (!usersTable) return;
let toggleAllCheckbox = usersTable.querySelector(
"thead input[type='checkbox']",
);
let checkboxes = [
...usersTable.querySelectorAll("tbody input[type='checkbox']"),
];
toggleAllCheckbox.addEventListener("change", (event) => {
checkboxes.forEach((checkbox) => {
checkbox.checked = event.target.checked;
});
});
checkboxes.forEach((checkbox) => {
checkbox.addEventListener("change", () => {
let allChecked = checkboxes.every((checkbox) => checkbox.checked);
let someChecked = checkboxes.some((checkbox) => checkbox.checked);
toggleAllCheckbox.checked = someChecked;
toggleAllCheckbox.indeterminate = someChecked && !allChecked;
});
});
}
}

View File

@@ -0,0 +1,16 @@
import { Controller } from '@hotwired/stimulus';
/*
* This is an example Stimulus controller!
*
* Any element with a data-controller="hello" attribute will cause
* this controller to be executed. The name "hello" comes from the filename:
* hello_controller.js -> "hello"
*
* Delete this file or adapt it for your use!
*/
export default class extends Controller {
connect() {
this.element.textContent = 'Hello Stimulus! Edit me in assets/controllers/hello_controller.js';
}
}

View File

@@ -0,0 +1,26 @@
import { Controller } from "@hotwired/stimulus";
export default class extends Controller {
static targets = ["dialog", "form", "token"];
open(event) {
event.preventDefault();
const button = event.currentTarget;
// Récupère l'URL et le token depuis le bouton
const url = button.dataset.url;
const csrfToken = button.dataset.token;
// Remplit le formulaire de la modale
this.formTarget.action = url;
this.tokenTarget.value = csrfToken;
this.dialogTarget.classList.remove("hidden");
this.dialogTarget.classList.add("flex");
}
close() {
this.dialogTarget.classList.add("hidden");
this.dialogTarget.classList.remove("flex");
}
}

View File

@@ -0,0 +1,66 @@
import { Controller } from "@hotwired/stimulus";
export default class extends Controller {
static targets = ["input", "preview", "loader"];
connect() {
this.element.addEventListener("dragover", this.dragOver.bind(this));
this.element.addEventListener("dragleave", this.dragLeave.bind(this));
this.element.addEventListener("drop", this.drop.bind(this));
this.inputTarget.addEventListener("change", (event) => {
this.handleFiles(this.inputTarget.files);
});
}
dragOver(event) {
event.preventDefault();
this.element.classList.add("border-amber-600");
}
dragLeave(event) {
event.preventDefault();
this.element.classList.remove("border-amber-600");
}
drop(event) {
event.preventDefault();
this.element.classList.remove("border-amber-600");
this.handleFiles(event.dataTransfer.files);
}
handleFiles(files) {
const formData = new FormData();
this.previewTarget.innerHTML = "";
Array.from(files).forEach((file) => {
formData.append("file-upload[]", file);
const reader = new FileReader();
reader.onload = (e) => {
const img = document.createElement("img");
img.src = e.target.result;
img.className = "h-24 w-24 object-cover rounded border";
this.previewTarget.appendChild(img);
};
reader.readAsDataURL(file);
});
this.uploadFiles(formData);
}
async uploadFiles(formData) {
this.loaderTarget.style.display = "block";
try {
await fetch("/admin/image/upload", {
method: "POST",
body: formData,
});
} catch (err) {
console.error("Upload error:", err);
} finally {
this.loaderTarget.style.display = "none";
}
}
}

View File

@@ -0,0 +1,23 @@
import { Controller } from "@hotwired/stimulus";
export default class extends Controller {
static targets = ["dialog", "form", "token"];
open(event) {
const button = event.currentTarget;
const url = button.dataset.url;
const token = button.dataset.token;
// Remplit le formulaire avec les bonnes données
this.formTarget.action = url;
this.tokenTarget.value = token;
this.dialogTarget.classList.remove("hidden");
this.dialogTarget.classList.add("flex");
}
close() {
this.dialogTarget.classList.add("hidden");
this.dialogTarget.classList.remove("flex");
}
}

View File

@@ -0,0 +1,165 @@
import { Controller } from "@hotwired/stimulus";
import Sortable from "sortablejs";
export default class extends Controller {
static targets = [
"input",
"preview",
"list",
"card",
"checkbox",
"checkIcon",
"overlay",
"count",
];
sortable = null;
connect() {
this.restoreInitialSelection();
this.enableSortable();
this.updateCount();
}
// ---------------------------------------------------------------------
// 1) Restaurer auto la sélection existante (images déjà liées)
// ---------------------------------------------------------------------
restoreInitialSelection() {
this.previewTarget.innerHTML = "";
const selectedIds = [
...document.querySelectorAll('input[name="selectedImages[]"]'),
].map((input) => input.value);
// coche les bonnes cases dans la modale
this.checkboxTargets.forEach((checkbox) => {
if (selectedIds.includes(checkbox.value)) {
checkbox.checked = true;
this.updateCard(checkbox.closest("label"), true);
}
});
// Ajoute dans le preview
selectedIds.forEach((id) => {
const card = this.cardTargets.find((c) => c.dataset.id === id);
if (!card) return;
const img = document.createElement("img");
img.src = card.dataset.url;
img.dataset.id = id;
img.className = "w-20 h-20 rounded object-cover cursor-move";
this.previewTarget.appendChild(img);
});
}
// ---------------------------------------------------------------------
// 2) Sélection dans la modale
// ---------------------------------------------------------------------
cardClick(event) {
const card = event.currentTarget;
const checkbox = card.querySelector("input[type='checkbox']");
checkbox.checked = !checkbox.checked;
this.updateCard(card, checkbox.checked);
this.updateCount();
}
toggleCheckbox(event) {
const checkbox = event.currentTarget;
const card = checkbox.closest("label");
this.updateCard(card, checkbox.checked);
this.updateCount();
}
updateCard(card, checked) {
const icon = card.querySelector(
"[data-news--edit-image-selector-target='checkIcon']",
);
const overlay = card.querySelector(
"[data-news--edit-image-selector-target='overlay']",
);
if (icon) icon.classList.toggle("hidden", !checked);
if (overlay) overlay.classList.toggle("hidden", !checked);
card.classList.toggle("ring-4", checked);
card.classList.toggle("ring-amber-500", checked);
}
updateCount() {
const total = this.checkboxTargets.filter((c) => c.checked).length;
if (this.hasCountTarget) {
this.countTarget.textContent = `${total} sélectionnée(s)`;
}
}
// ---------------------------------------------------------------------
// 3) Validation depuis la modale → update preview + ordre
// ---------------------------------------------------------------------
validate() {
const selected = this.checkboxTargets
.filter((c) => c.checked)
.map((c) => c.value);
// 🔥 RESET COMPLET
this.previewTarget.innerHTML = "";
// 🔁 RECONSTRUCTION PROPRE
selected.forEach((id) => {
const card = this.cardTargets.find((c) => c.dataset.id === id);
if (!card) return;
const img = document.createElement("img");
img.src = card.dataset.url;
img.dataset.id = id;
img.className = "w-20 h-20 rounded object-cover cursor-move";
this.previewTarget.appendChild(img);
});
this.enableSortable();
this.updateOrder();
document
.querySelector("[command='close'][commandfor='edit-news-image-dialog']")
.click();
}
// ---------------------------------------------------------------------
// 4) Drag & drop
// ---------------------------------------------------------------------
enableSortable() {
if (this.sortable) this.sortable.destroy();
this.sortable = Sortable.create(this.previewTarget, {
animation: 150,
ghostClass: "opacity-40",
onSort: () => this.updateOrder(),
});
}
updateOrder() {
const form = this.previewTarget.closest("form");
// supprime uniquement les inputs dynamiques
form
.querySelectorAll(
'input[name="selectedImages[]"]:not([data-permanent="true"])',
)
.forEach((i) => i.remove());
// recréer les inputs cachés
Array.from(this.previewTarget.children).forEach((img) => {
const hidden = document.createElement("input");
hidden.type = "hidden";
hidden.name = "selectedImages[]";
hidden.value = img.dataset.id;
form.appendChild(hidden);
});
}
}

View File

@@ -0,0 +1,126 @@
import { Controller } from "@hotwired/stimulus";
import Sortable from "sortablejs";
export default class extends Controller {
static targets = [
"container",
"input",
"preview",
"list",
"card",
"checkbox",
"checkIcon",
"count",
];
sortable = null;
// ------------------------------------------------------------
// Sélection normale
// ------------------------------------------------------------
cardClick(event) {
const card = event.currentTarget;
const checkbox = card.querySelector("input[type='checkbox']");
checkbox.checked = !checkbox.checked;
this.updateCard(card, checkbox.checked);
this.updateCount();
}
toggleCheckbox(event) {
const checkbox = event.currentTarget;
const card = checkbox.closest("label");
this.updateCard(card, checkbox.checked);
this.updateCount();
}
updateCard(card, checked) {
const icon = card.querySelector(
"[data-news--image-selector-target='checkIcon']",
);
const overlay = card.querySelector(
"[data-news--image-selector-target='overlay']",
);
icon.classList.toggle("hidden", !checked);
overlay.classList.toggle("hidden", !checked);
card.classList.toggle("ring-4", checked);
card.classList.toggle("ring-amber-500", checked);
}
updateCount() {
const total = this.checkboxTargets.filter((c) => c.checked).length;
if (this.hasCountTarget) {
this.countTarget.textContent = `${total} sélectionnée(s)`;
}
}
// ------------------------------------------------------------
// Validation → création du preview + activation du drag&drop
// ------------------------------------------------------------
validate() {
const selected = this.checkboxTargets
.filter((c) => c.checked)
.map((c) => c.value);
// 🔥 RESET TOTAL DU PREVIEW
this.previewTarget.innerHTML = "";
// 🔁 RECONSTRUCTION À PARTIR DE LA SOURCE DE VÉRITÉ
selected.forEach((id) => {
const card = this.cardTargets.find((c) => c.dataset.id === id);
if (!card) return;
const img = document.createElement("img");
img.src = card.dataset.url;
img.dataset.id = id;
img.className = "w-20 h-20 rounded object-cover cursor-move";
this.previewTarget.appendChild(img);
});
this.enableSortable();
this.updateOrder();
// ferme la modale
document.querySelector("[command='close'][commandfor='dialog']").click();
}
// ------------------------------------------------------------
// SortableJS (drag & drop)
// ------------------------------------------------------------
enableSortable() {
if (this.sortable) {
this.sortable.destroy(); // reset si déjà actif
}
this.sortable = Sortable.create(this.previewTarget, {
animation: 150,
ghostClass: "opacity-40",
onSort: () => {
this.updateOrder();
},
});
}
updateOrder() {
// Supprime les anciennes valeurs
const container = this.previewTarget.closest("form");
container
.querySelectorAll('input[name="selectedImages[]"]')
.forEach((i) => i.remove());
// Crée un input caché par image dans l'ordre
Array.from(this.previewTarget.children).forEach((img) => {
const hidden = document.createElement("input");
hidden.type = "hidden";
hidden.name = "selectedImages[]";
hidden.value = img.dataset.id;
container.appendChild(hidden);
});
}
}

View File

@@ -0,0 +1,33 @@
import Sortable from "sortablejs";
import { Controller } from "@hotwired/stimulus";
export default class extends Controller {
static values = {
url: String,
};
static targets = ["item"];
connect() {
this.sortable = new Sortable(this.element, {
animation: 150,
handle: '[data-news--sortable-target="handle"]',
onEnd: this.reorder.bind(this),
});
}
reorder() {
const order = this.itemTargets.map((el, index) => ({
id: el.dataset.id,
position: index + 1,
}));
fetch(this.urlValue, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ order }),
});
}
}

View File

@@ -0,0 +1,36 @@
// assets/controllers/news_carroussel_controller.js
import { Controller } from "@hotwired/stimulus";
import Swiper from "swiper";
import { Navigation, Pagination } from "swiper/modules";
import "swiper/css";
import "swiper/css/navigation";
import "swiper/css/pagination";
export default class extends Controller {
connect() {
this.swiper = new Swiper(this.element, {
modules: [Navigation, Pagination],
slidesPerView: 1,
spaceBetween: 24,
loop: true,
pagination: {
el: ".swiper-pagination",
clickable: true,
dynamicBullets: true,
},
navigation: {
nextEl: ".swiper-button-next",
prevEl: ".swiper-button-prev",
},
});
}
disconnect() {
if (this.swiper) {
this.swiper.destroy();
}
}
}

View File

@@ -0,0 +1,8 @@
import { Controller } from "@hotwired/stimulus";
export default class extends Controller {
connect() {
const year = new Date().getFullYear();
this.element.innerHTML = `&copy; ${year} Arts-ticule, Tous droits réservés.`;
}
}

View File

@@ -0,0 +1,55 @@
Software License Agreement
==========================
**CKEditor&nbsp;5** (https://github.com/ckeditor/ckeditor5)<br>
Copyright (c) 20032025, [CKSource Holding sp. z o.o.](https://cksource.com) All rights reserved.
Licensed under a dual-license model, this software is available under:
* the [GNU General Public License Version 2 or later](https://www.gnu.org/licenses/gpl.html) (see COPYING.GPL),
* or commercial license terms from CKSource Holding sp. z o.o.
For more information, see: [https://ckeditor.com/legal/ckeditor-licensing-options](https://ckeditor.com/legal/ckeditor-licensing-options).
If you are using CKEditor under commercial terms, you are free to remove the COPYING.GPL file with the full copy of a GPL license.
Sources of Intellectual Property Included in CKEditor&nbsp;5
------------------------------------------------------------
Where not otherwise indicated, all CKEditor&nbsp;5 content is authored by CKSource engineers and consists of CKSource-owned intellectual property. In some specific instances, CKEditor&nbsp;5 will incorporate work done by developers outside of CKSource with their express permission.
The following libraries are included in CKEditor&nbsp;5 under the [ISC license](https://opensource.org/licenses/ISC):
* hast-util-from-dom - Copyright (c) Keith McKnight <keith@mcknig.ht>.
* rehype-dom-parse - Copyright (c) 2018 Keith McKnight <keith@mcknig.ht>.
* rehype-dom-stringify - Copyright (c) 2018 Keith McKnight <keith@mcknig.ht>.
The following libraries are included in CKEditor&nbsp;5 under the [MIT license](https://opensource.org/licenses/MIT):
* @types/color-convert - Copyright (c) Microsoft Corporation.
* @types/hast - Copyright (c) Microsoft Corporation.
* blurhash - Copyright (c) 2018 Wolt Enterprises.
* color-convert - Copyright (c) 2011-2016 Heather Arthur <fayearthur@gmail.com> and Copyright (c) 2016-2021 Josh Junon <josh@junon.me>.
* color-parse - Copyright (c) 2015 Dmitry Ivanov.
* emojibase-data - Copyright (c) 2017-2019 Miles Johnson.
* es-toolkit - Copyright (c) 2024 Viva Republica, Inc.
* fuzzysort - Copyright (c) 2018 Stephen Kamenar.
* hast-util-to-html - Copyright (c) Titus Wormer <tituswormer@gmail.com>.
* hast-util-to-mdast - Copyright (c) Titus Wormer <tituswormer@gmail.com> and Copyright (c) Seth Vincent <sethvincent@gmail.com>.
* hastscript - Copyright (c) Titus Wormer <tituswormer@gmail.com>.
* is-emoji-supported - Copyright (c) 2016-2020 Koala Interactive, Inc.
* Regular expression for URL validation - Copyright (c) 2010-2018 Diego Perini.
* rehype-remark - Copyright (c) Titus Wormer <tituswormer@gmail.com>.
* remark-breaks - Copyright (c) 2017 Titus Wormer <tituswormer@gmail.com>.
* remark-gfm - Copyright (c) Titus Wormer <tituswormer@gmail.com>.
* remark-parse - Copyright (c) 2014 Titus Wormer <tituswormer@gmail.com>.
* remark-rehype - Copyright (c) Titus Wormer <tituswormer@gmail.com>.
* remark-stringify - Copyright (c) 2014 Titus Wormer <tituswormer@gmail.com>.
* unified - Copyright (c) 2015 Titus Wormer <tituswormer@gmail.com>.
* unist-util-visit - Copyright (c) 2015 Titus Wormer <tituswormer@gmail.com>.
* vanilla-colorful - Copyright (c) 2020 Serhii Kulykov <iamkulykov@gmail.com>.
Trademarks
----------
**CKEditor** is a trademark of [CKSource Holding sp. z o.o.](https://cksource.com) All other brand and product names are trademarks, registered trademarks or service marks of their respective holders.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
/**
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
*/
import type { Translations } from '@ckeditor/ckeditor5-utils';
declare const translations: Translations;
export default translations;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
/**
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
*/
import type { Translations } from '@ckeditor/ckeditor5-utils';
declare const translations: Translations;
export default translations;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
/**
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
*/
import type { Translations } from '@ckeditor/ckeditor5-utils';
declare const translations: Translations;
export default translations;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
/**
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
*/
import type { Translations } from '@ckeditor/ckeditor5-utils';
declare const translations: Translations;
export default translations;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
/**
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
*/
import type { Translations } from '@ckeditor/ckeditor5-utils';
declare const translations: Translations;
export default translations;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
/**
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
*/
import type { Translations } from '@ckeditor/ckeditor5-utils';
declare const translations: Translations;
export default translations;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
/**
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
*/
import type { Translations } from '@ckeditor/ckeditor5-utils';
declare const translations: Translations;
export default translations;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
/**
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
*/
import type { Translations } from '@ckeditor/ckeditor5-utils';
declare const translations: Translations;
export default translations;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
/**
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
*/
import type { Translations } from '@ckeditor/ckeditor5-utils';
declare const translations: Translations;
export default translations;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
/**
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
*/
import type { Translations } from '@ckeditor/ckeditor5-utils';
declare const translations: Translations;
export default translations;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
/**
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
*/
import type { Translations } from '@ckeditor/ckeditor5-utils';
declare const translations: Translations;
export default translations;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
/**
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
*/
import type { Translations } from '@ckeditor/ckeditor5-utils';
declare const translations: Translations;
export default translations;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
/**
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
*/
import type { Translations } from '@ckeditor/ckeditor5-utils';
declare const translations: Translations;
export default translations;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
/**
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
*/
import type { Translations } from '@ckeditor/ckeditor5-utils';
declare const translations: Translations;
export default translations;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
/**
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
*/
import type { Translations } from '@ckeditor/ckeditor5-utils';
declare const translations: Translations;
export default translations;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
/**
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
*/
import type { Translations } from '@ckeditor/ckeditor5-utils';
declare const translations: Translations;
export default translations;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
/**
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
*/
import type { Translations } from '@ckeditor/ckeditor5-utils';
declare const translations: Translations;
export default translations;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
/**
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
*/
import type { Translations } from '@ckeditor/ckeditor5-utils';
declare const translations: Translations;
export default translations;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
/**
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
*/
import type { Translations } from '@ckeditor/ckeditor5-utils';
declare const translations: Translations;
export default translations;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
/**
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
*/
import type { Translations } from '@ckeditor/ckeditor5-utils';
declare const translations: Translations;
export default translations;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
/**
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
*/
import type { Translations } from '@ckeditor/ckeditor5-utils';
declare const translations: Translations;
export default translations;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
/**
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
*/
import type { Translations } from '@ckeditor/ckeditor5-utils';
declare const translations: Translations;
export default translations;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
/**
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
*/
import type { Translations } from '@ckeditor/ckeditor5-utils';
declare const translations: Translations;
export default translations;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
/**
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
*/
import type { Translations } from '@ckeditor/ckeditor5-utils';
declare const translations: Translations;
export default translations;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
/**
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
*/
import type { Translations } from '@ckeditor/ckeditor5-utils';
declare const translations: Translations;
export default translations;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
/**
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
*/
import type { Translations } from '@ckeditor/ckeditor5-utils';
declare const translations: Translations;
export default translations;

Some files were not shown because too many files have changed in this diff Show More