diff --git a/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/TipTapEditor.vue b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/TipTapEditor.vue index 405f7654de..321b40743d 100644 --- a/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/TipTapEditor.vue +++ b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/TipTapEditor.vue @@ -1,8 +1,71 @@ @@ -10,23 +73,79 @@ + + + diff --git a/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/image/ImageNodeView.vue b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/image/ImageNodeView.vue new file mode 100644 index 0000000000..0e16eb11d4 --- /dev/null +++ b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/image/ImageNodeView.vue @@ -0,0 +1,279 @@ + + + + + + + diff --git a/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/image/ImageUploadModal.vue b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/image/ImageUploadModal.vue new file mode 100644 index 0000000000..1d5cc39ddb --- /dev/null +++ b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/image/ImageUploadModal.vue @@ -0,0 +1,511 @@ + + + + + + + diff --git a/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/link/LinkBubbleMenu.vue b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/link/LinkBubbleMenu.vue new file mode 100644 index 0000000000..4746dd2174 --- /dev/null +++ b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/link/LinkBubbleMenu.vue @@ -0,0 +1,172 @@ + + + + + + + diff --git a/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/link/LinkEditor.vue b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/link/LinkEditor.vue new file mode 100644 index 0000000000..2171a8cf84 --- /dev/null +++ b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/link/LinkEditor.vue @@ -0,0 +1,256 @@ + + + + + + + diff --git a/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/math/FormulasMenu.vue b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/math/FormulasMenu.vue new file mode 100644 index 0000000000..aa5683bdae --- /dev/null +++ b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/math/FormulasMenu.vue @@ -0,0 +1,421 @@ + + + + + + + diff --git a/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/math/FormulasStrings.js b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/math/FormulasStrings.js new file mode 100644 index 0000000000..65b9d2df03 --- /dev/null +++ b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/math/FormulasStrings.js @@ -0,0 +1,744 @@ +import { createTranslator } from 'shared/i18n'; + +const NAMESPACE = 'FormulasStrings'; + +const MESSAGES = { + // Category titles + formulasCategory: { + message: 'Formulas', + context: 'Category title for formula symbols', + }, + linesCategory: { + message: 'Lines', + context: 'Category title for line symbols', + }, + basicCategory: { + message: 'Basic', + context: 'Category title for basic math symbols', + }, + advancedCategory: { + message: 'Advanced', + context: 'Category title for advanced math symbols', + }, + logicCategory: { + message: 'Logic', + context: 'Category title for logic symbols', + }, + geometryCategory: { + message: 'Geometry', + context: 'Category title for geometry symbols', + }, + setsCategory: { + message: 'Sets', + context: 'Category title for set theory symbols', + }, + directionalCategory: { + message: 'Directional', + context: 'Category title for directional symbols', + }, + charactersCategory: { + message: 'Characters', + context: 'Category title for Greek characters', + }, + miscellaneousCategory: { + message: 'Miscellaneous', + context: 'Category title for miscellaneous symbols', + }, + + // Formula symbols + superscript: { + message: 'Superscript', + context: 'Title for superscript symbol', + }, + subscript: { + message: 'Subscript', + context: 'Title for subscript symbol', + }, + fraction: { + message: 'Fraction', + context: 'Title for fraction symbol', + }, + binomialCoefficient: { + message: 'Binomial coefficient', + context: 'Title for binomial coefficient symbol', + }, + sum: { + message: 'Sum', + context: 'Title for sum symbol', + }, + product: { + message: 'Product', + context: 'Title for product symbol', + }, + coproduct: { + message: 'Coproduct', + context: 'Title for coproduct symbol', + }, + integral: { + message: 'Integral', + context: 'Title for integral symbol', + }, + + // Lines symbols + squareRoot: { + message: 'Square root', + context: 'Title for square root symbol', + }, + bar: { + message: 'Bar', + context: 'Title for bar symbol', + }, + underline: { + message: 'Underline', + context: 'Title for underline symbol', + }, + leftArrow: { + message: 'Left arrow', + context: 'Title for left arrow symbol', + }, + rightArrow: { + message: 'Right arrow', + context: 'Title for right arrow symbol', + }, + vector: { + message: 'Vector', + context: 'Title for vector symbol', + }, + + // Basic symbols + addition: { + message: 'Addition', + context: 'Title for addition symbol', + }, + subtraction: { + message: 'Subtraction', + context: 'Title for subtraction symbol', + }, + multiplication: { + message: 'Multiplication', + context: 'Title for multiplication symbol', + }, + division: { + message: 'Division', + context: 'Title for division symbol', + }, + dot: { + message: 'Dot', + context: 'Title for dot symbol', + }, + negation: { + message: 'Negation', + context: 'Title for negation symbol', + }, + plusMinus: { + message: 'Plus-minus', + context: 'Title for plus-minus symbol', + }, + minusPlus: { + message: 'Minus-plus', + context: 'Title for minus-plus symbol', + }, + doesNotEqual: { + message: 'Does not equal', + context: 'Title for does not equal symbol', + }, + approximately: { + message: 'Approximately', + context: 'Title for approximately symbol', + }, + proportional: { + message: 'Proportional', + context: 'Title for proportional symbol', + }, + definition: { + message: 'Definition', + context: 'Title for definition symbol', + }, + greaterThan: { + message: 'Greater than', + context: 'Title for greater than symbol', + }, + greaterThanOrEqual: { + message: 'Greater than or equal to', + context: 'Title for greater than or equal symbol', + }, + notGreaterThan: { + message: 'Not greater than', + context: 'Title for not greater than symbol', + }, + significantlyGreaterThan: { + message: 'Significantly greater than', + context: 'Title for significantly greater than symbol', + }, + lessThan: { + message: 'Less than', + context: 'Title for less than symbol', + }, + lessThanOrEqual: { + message: 'Less than or equal to', + context: 'Title for less than or equal symbol', + }, + notLessThan: { + message: 'Not less than', + context: 'Title for not less than symbol', + }, + significantlyLessThan: { + message: 'Significantly less than', + context: 'Title for significantly less than symbol', + }, + leftCeiling: { + message: 'Left ceiling', + context: 'Title for left ceiling symbol', + }, + leftFloor: { + message: 'Left floor', + context: 'Title for left floor symbol', + }, + rightCeiling: { + message: 'Right ceiling', + context: 'Title for right ceiling symbol', + }, + rightFloor: { + message: 'Right floor', + context: 'Title for right floor symbol', + }, + + // Advanced symbols + tensorProduct: { + message: 'Tensor product', + context: 'Title for tensor product symbol', + }, + contourIntegral: { + message: 'Contour integral', + context: 'Title for contour integral symbol', + }, + nabla: { + message: 'Nabla', + context: 'Title for nabla symbol', + }, + conjugate: { + message: 'Conjugate', + context: 'Title for conjugate symbol', + }, + conjugateTranspose: { + message: 'Conjugate transpose', + context: 'Title for conjugate transpose symbol', + }, + partial: { + message: 'Partial', + context: 'Title for partial symbol', + }, + wedgeProduct: { + message: 'Wedge product', + context: 'Title for wedge product symbol', + }, + infinity: { + message: 'Infinity', + context: 'Title for infinity symbol', + }, + topElement: { + message: 'Top element', + context: 'Title for top element symbol', + }, + reducibleTo: { + message: 'Reducible to', + context: 'Title for reducible to symbol', + }, + nondominatedBy: { + message: 'Nondominated by', + context: 'Title for nondominated by symbol', + }, + + // Logic symbols + and: { + message: 'And', + context: 'Title for and symbol', + }, + or: { + message: 'Or', + context: 'Title for or symbol', + }, + ifAndOnlyIf: { + message: 'If and only if', + context: 'Title for if and only if symbol', + }, + entails: { + message: 'Entails', + context: 'Title for entails symbol', + }, + implies: { + message: 'Implies', + context: 'Title for implies symbol', + }, + givenThat: { + message: 'Given that/Such that', + context: 'Title for given that symbol', + }, + exists: { + message: 'Exists', + context: 'Title for exists symbol', + }, + forAll: { + message: 'For all', + context: 'Title for for all symbol', + }, + because: { + message: 'Because', + context: 'Title for because symbol', + }, + therefore: { + message: 'Therefore', + context: 'Title for therefore symbol', + }, + qed: { + message: 'QED', + context: 'Title for QED symbol', + }, + exclusiveOr: { + message: 'Exclusive or', + context: 'Title for exclusive or symbol', + }, + + // Geometry symbols + degrees: { + message: 'Degrees', + context: 'Title for degrees symbol', + }, + angle: { + message: 'Angle', + context: 'Title for angle symbol', + }, + measuredAngle: { + message: 'Measured angle', + context: 'Title for measured angle symbol', + }, + parallel: { + message: 'Parallel', + context: 'Title for parallel symbol', + }, + perpendicular: { + message: 'Perpendicular', + context: 'Title for perpendicular symbol', + }, + incomparableTo: { + message: 'Incomparable to', + context: 'Title for incomparable to symbol', + }, + similarTo: { + message: 'Similar to', + context: 'Title for similar to symbol', + }, + similarOrEqual: { + message: 'Similar or equal to', + context: 'Title for similar or equal symbol', + }, + congruentTo: { + message: 'Congruent to', + context: 'Title for congruent to symbol', + }, + + // Sets symbols + ellipsis: { + message: 'Ellipsis', + context: 'Title for ellipsis symbol', + }, + ellipsisVertical: { + message: 'Ellipsis (vertical)', + context: 'Title for vertical ellipsis symbol', + }, + ellipsisCentered: { + message: 'Ellipsis (centered)', + context: 'Title for centered ellipsis symbol', + }, + ellipsisDiagonal: { + message: 'Ellipsis (diagonal)', + context: 'Title for diagonal ellipsis symbol', + }, + cardinality: { + message: 'Cardinality', + context: 'Title for cardinality symbol', + }, + intersection: { + message: 'Intersection', + context: 'Title for intersection symbol', + }, + union: { + message: 'Union', + context: 'Title for union symbol', + }, + emptySet: { + message: 'Empty set', + context: 'Title for empty set symbol', + }, + in: { + message: 'In', + context: 'Title for in symbol', + }, + notIn: { + message: 'Not in', + context: 'Title for not in symbol', + }, + contains: { + message: 'Contains', + context: 'Title for contains symbol', + }, + symmetricDifference: { + message: 'Symmetric difference', + context: 'Title for symmetric difference symbol', + }, + setDifference: { + message: 'Set difference', + context: 'Title for set difference symbol', + }, + subset: { + message: 'Subset', + context: 'Title for subset symbol', + }, + subsetOrEqual: { + message: 'Subset or equal', + context: 'Title for subset or equal symbol', + }, + notSubsetOrEqual: { + message: 'Not a subset or equal', + context: 'Title for not subset or equal symbol', + }, + superset: { + message: 'Superset', + context: 'Title for superset symbol', + }, + supersetOrEqual: { + message: 'Superset or equal', + context: 'Title for superset or equal symbol', + }, + notSupersetOrEqual: { + message: 'Not a superset or equal', + context: 'Title for not superset or equal symbol', + }, + wreathProduct: { + message: 'Wreath product', + context: 'Title for wreath product symbol', + }, + naturalJoin: { + message: 'Natural join', + context: 'Title for natural join symbol', + }, + + // Directional symbols + down: { + message: 'Down', + context: 'Title for down arrow symbol', + }, + downDouble: { + message: 'Down (double)', + context: 'Title for double down arrow symbol', + }, + triangleDown: { + message: 'Triangle down', + context: 'Title for triangle down symbol', + }, + up: { + message: 'Up', + context: 'Title for up arrow symbol', + }, + upDouble: { + message: 'Up (double)', + context: 'Title for double up arrow symbol', + }, + triangleUp: { + message: 'Triangle up', + context: 'Title for triangle up symbol', + }, + upDown: { + message: 'Up-down', + context: 'Title for up-down arrow symbol', + }, + upDownDouble: { + message: 'Up-down (double)', + context: 'Title for double up-down arrow symbol', + }, + left: { + message: 'Left', + context: 'Title for left arrow symbol', + }, + leftDouble: { + message: 'Left (double)', + context: 'Title for double left arrow symbol', + }, + leftLong: { + message: 'Left (long)', + context: 'Title for long left arrow symbol', + }, + leftLongDouble: { + message: 'Left (long, double)', + context: 'Title for long double left arrow symbol', + }, + leftHooked: { + message: 'Left (hooked)', + context: 'Title for hooked left arrow symbol', + }, + leftHarpoonDown: { + message: 'Left (harpoon down)', + context: 'Title for left harpoon down symbol', + }, + leftHarpoonUp: { + message: 'Left (harpoon up)', + context: 'Title for left harpoon up symbol', + }, + right: { + message: 'Right', + context: 'Title for right arrow symbol', + }, + rightDouble: { + message: 'Right (double)', + context: 'Title for double right arrow symbol', + }, + rightLong: { + message: 'Right (long)', + context: 'Title for long right arrow symbol', + }, + rightLongDouble: { + message: 'Right (long, double)', + context: 'Title for long double right arrow symbol', + }, + rightHooked: { + message: 'Right (hooked)', + context: 'Title for hooked right arrow symbol', + }, + rightHarpoonDown: { + message: 'Right (harpoon down)', + context: 'Title for right harpoon down symbol', + }, + rightHarpoonUp: { + message: 'Right (harpoon up)', + context: 'Title for right harpoon up symbol', + }, + leftRight: { + message: 'Left-right', + context: 'Title for left-right arrow symbol', + }, + leftRightDouble: { + message: 'Left-right (double)', + context: 'Title for double left-right arrow symbol', + }, + leftRightLong: { + message: 'Left-right (long)', + context: 'Title for long left-right arrow symbol', + }, + leftRightLongDouble: { + message: 'Left-right (long, double)', + context: 'Title for long double left-right arrow symbol', + }, + northeast: { + message: 'Northeast', + context: 'Title for northeast arrow symbol', + }, + northwest: { + message: 'Northwest', + context: 'Title for northwest arrow symbol', + }, + southeast: { + message: 'Southeast', + context: 'Title for southeast arrow symbol', + }, + southwest: { + message: 'Southwest', + context: 'Title for southwest arrow symbol', + }, + + // Greek characters + alpha: { + message: 'alpha', + context: 'Title for alpha character', + }, + beta: { + message: 'beta', + context: 'Title for beta character', + }, + chi: { + message: 'chi', + context: 'Title for chi character', + }, + delta: { + message: 'delta', + context: 'Title for delta character', + }, + deltaCapital: { + message: 'Delta', + context: 'Title for capital Delta character', + }, + digamma: { + message: 'digamma', + context: 'Title for digamma character', + }, + ell: { + message: 'ell', + context: 'Title for ell character', + }, + epsilon: { + message: 'epsilon', + context: 'Title for epsilon character', + }, + eta: { + message: 'eta', + context: 'Title for eta character', + }, + gamma: { + message: 'gamma', + context: 'Title for gamma character', + }, + gammaCapital: { + message: 'Gamma', + context: 'Title for capital Gamma character', + }, + planckConstant: { + message: "Planck's constant", + context: 'Title for Planck constant character', + }, + iota: { + message: 'iota', + context: 'Title for iota character', + }, + kappa: { + message: 'kappa', + context: 'Title for kappa character', + }, + lambda: { + message: 'lambda', + context: 'Title for lambda character', + }, + lambdaCapital: { + message: 'Lambda', + context: 'Title for capital Lambda character', + }, + mu: { + message: 'mu', + context: 'Title for mu character', + }, + nu: { + message: 'nu', + context: 'Title for nu character', + }, + omega: { + message: 'omega', + context: 'Title for omega character', + }, + omegaCapital: { + message: 'Omega', + context: 'Title for capital Omega character', + }, + phi: { + message: 'phi', + context: 'Title for phi character', + }, + phiCapital: { + message: 'Phi', + context: 'Title for capital Phi character', + }, + pi: { + message: 'pi', + context: 'Title for pi character', + }, + piCapital: { + message: 'Pi', + context: 'Title for capital Pi character', + }, + psi: { + message: 'psi', + context: 'Title for psi character', + }, + psiCapital: { + message: 'Psi', + context: 'Title for capital Psi character', + }, + rho: { + message: 'rho', + context: 'Title for rho character', + }, + sigma: { + message: 'sigma', + context: 'Title for sigma character', + }, + sigmaCapital: { + message: 'Sigma', + context: 'Title for capital Sigma character', + }, + tau: { + message: 'tau', + context: 'Title for tau character', + }, + theta: { + message: 'theta', + context: 'Title for theta character', + }, + thetaCapital: { + message: 'Theta', + context: 'Title for capital Theta character', + }, + upsilon: { + message: 'upsilon', + context: 'Title for upsilon character', + }, + upsilonCapital: { + message: 'Upsilon', + context: 'Title for capital Upsilon character', + }, + pWeierstrass: { + message: 'P', + context: 'Title for Weierstrass P character', + }, + xi: { + message: 'xi', + context: 'Title for xi character', + }, + xiCapital: { + message: 'Xi', + context: 'Title for capital Xi character', + }, + zeta: { + message: 'zeta', + context: 'Title for zeta character', + }, + + // Miscellaneous symbols + circle: { + message: 'Circle', + context: 'Title for circle symbol', + }, + club: { + message: 'Club', + context: 'Title for club symbol', + }, + diamond: { + message: 'Diamond', + context: 'Title for diamond symbol', + }, + heart: { + message: 'Heart', + context: 'Title for heart symbol', + }, + spade: { + message: 'Spade', + context: 'Title for spade symbol', + }, + flat: { + message: 'Flat', + context: 'Title for flat symbol', + }, + natural: { + message: 'Natural', + context: 'Title for natural symbol', + }, + sharp: { + message: 'Sharp', + context: 'Title for sharp symbol', + }, +}; + +let FormulasStrings = null; + +export function getFormulasStrings() { + if (!FormulasStrings) { + FormulasStrings = createTranslator(NAMESPACE, MESSAGES); + } + return FormulasStrings; +} diff --git a/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/math/MathNodeView.vue b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/math/MathNodeView.vue new file mode 100644 index 0000000000..8dc4dc6df5 --- /dev/null +++ b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/math/MathNodeView.vue @@ -0,0 +1,90 @@ + + + + + + + + + + diff --git a/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/math/symbols.json b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/math/symbols.json new file mode 100644 index 0000000000..b1d683de1a --- /dev/null +++ b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/math/symbols.json @@ -0,0 +1,277 @@ +[ + { + "titleKey": "formulasCategory", + "symbols": [ + { + "key": "^", + "preview": "{a}^{b}", + "titleKey": "superscript", + "template": "\\placeholder{}^{\\placeholder{}}" + }, + { + "key": "_", + "preview": "{a}_{b}", + "titleKey": "subscript", + "template": "\\placeholder{}_{\\placeholder{}}" + }, + { + "key": "\\frac", + "preview": "\\frac{a}{b}", + "titleKey": "fraction", + "template": "\\frac{\\placeholder{}}{\\placeholder{}}" + }, + { + "key": "\\binom", + "preview": "(\\binom{_b}{^a})", + "titleKey": "binomialCoefficient", + "template": "\\binom{\\placeholder{}}{\\placeholder{}}" + }, + { + "key": "\\sum", + "preview": "\\sum_{i=0}^{n}x_{i}", + "titleKey": "sum", + "template": "\\sum_{\\placeholder{}}^{\\placeholder{}}\\placeholder{}" + }, + { + "key": "\\prod", + "preview": "\\prod_b^a", + "titleKey": "product", + "template": "\\prod_{\\placeholder{}}^{\\placeholder{}}\\placeholder{}" + }, + { + "key": "\\coprod", + "preview": "\\coprod_b^a", + "titleKey": "coproduct", + "template": "\\coprod_{\\placeholder{}}^{\\placeholder{}}\\placeholder{}" + }, + { + "key": "", + "preview": "\\int_{a}^{b}f\\left(x\r)\\,dx", + "titleKey": "integral", + "template": "\\int_{\\placeholder{}}^{\\placeholder{}}\\placeholder{}\\,d\\placeholder{}" + } + ] + }, + { + "titleKey": "linesCategory", + "symbols": [ + { "key": "\\sqrt", "preview": "\\sqrt{abc}", "titleKey": "squareRoot" }, + { "key": "\\bar", "preview": "\\overline{abc}", "titleKey": "bar" }, + { "key": "\\underline", "preview": "\\underline{abc}", "titleKey": "underline" }, + { "key": "\\overleftarrow", "preview": "\\overleftarrow{abc}", "titleKey": "leftArrow" }, + { "key": "\\overrightarrow", "preview": "\\overrightarrow{abc}", "titleKey": "rightArrow" }, + { "key": "\\vec", "preview": "\\vec{v}", "titleKey": "vector" } + ] + }, + { + "titleKey": "basicCategory", + "symbols": [ + { "key": "+", "preview": "+", "titleKey": "addition" }, + { "key": "-", "preview": "−", "titleKey": "subtraction" }, + { "key": "\\times", "preview": "×", "titleKey": "multiplication" }, + { "key": "\\div", "preview": "÷", "titleKey": "division" }, + { "key": "\\cdot", "preview": "·", "titleKey": "dot" }, + { "key": "\\neg", "preview": "¬", "titleKey": "negation" }, + { "key": "\\pm", "preview": "±", "titleKey": "plusMinus" }, + { "key": "\\mp", "preview": "∓", "titleKey": "minusPlus" }, + { "key": "\\neq", "preview": "≠", "titleKey": "doesNotEqual" }, + { "key": "\\approx", "preview": "≈", "titleKey": "approximately" }, + { "key": "\\propto", "preview": "∝", "titleKey": "proportional" }, + { "key": "\\doteq", "preview": "≐", "titleKey": "definition" }, + { "key": "\\gt", "preview": ">", "titleKey": "greaterThan" }, + { "key": "\\ge", "preview": "≥", "titleKey": "greaterThanOrEqual" }, + { "key": "\\ngtr", "preview": "≯", "titleKey": "notGreaterThan" }, + { "key": "\\gg", "preview": "≫", "titleKey": "significantlyGreaterThan" }, + { "key": "\\lt", "preview": "<", "titleKey": "lessThan" }, + { "key": "\\le", "preview": "≤", "titleKey": "lessThanOrEqual" }, + { "key": "\\nless", "preview": "≮", "titleKey": "notLessThan" }, + { "key": "\\ll", "preview": "≪", "titleKey": "significantlyLessThan" }, + { "key": "\\lceil", "preview": "⌈", "titleKey": "leftCeiling" }, + { "key": "\\lfloor", "preview": "⌊", "titleKey": "leftFloor" }, + { "key": "\\rceil", "preview": "⌉", "titleKey": "rightCeiling" }, + { "key": "\\rfloor", "preview": "⌋", "titleKey": "rightFloor" } + ] + }, + { + "titleKey": "advancedCategory", + "symbols": [ + { "key": "\\bigotimes", "preview": "⨂", "titleKey": "tensorProduct" }, + { "key": "\\otimes", "preview": "⊗", "titleKey": "tensorProduct" }, + { "key": "\\oint", "preview": "∮", "titleKey": "contourIntegral" }, + { "key": "\\nabla", "preview": "∇", "titleKey": "nabla" }, + { "key": "\\ast", "preview": "*", "titleKey": "conjugate" }, + { "key": "\\dagger", "preview": "†", "titleKey": "conjugateTranspose" }, + { "key": "\\partial", "preview": "∂", "titleKey": "partial" }, + { "key": "\\wedge", "preview": "∧", "titleKey": "wedgeProduct" }, + { "key": "\\infty", "preview": "∞", "titleKey": "infinity" }, + { "key": "\\top", "preview": "⊤", "titleKey": "topElement" }, + { "key": "\\prec", "preview": "≺", "titleKey": "reducibleTo" }, + { "key": "\\succ", "preview": "≻", "titleKey": "nondominatedBy" } + ] + }, + { + "titleKey": "logicCategory", + "symbols": [ + { "key": "\\land", "preview": "∧", "titleKey": "and" }, + { "key": "\\lor", "preview": "∨", "titleKey": "or" }, + { "key": "\\iff", "preview": "⟺", "titleKey": "ifAndOnlyIf" }, + { "key": "\\models", "preview": "⊨", "titleKey": "entails" }, + { "key": "\\vdash", "preview": "⊢", "titleKey": "implies" }, + { "key": "\\vert", "preview": "|", "titleKey": "givenThat" }, + { "key": "\\exists", "preview": "∃", "titleKey": "exists" }, + { "key": "\\forall", "preview": "∀", "titleKey": "forAll" }, + { "key": "\\because", "preview": "∵", "titleKey": "because" }, + { "key": "\\therefore", "preview": "∴", "titleKey": "therefore" }, + { "key": "\\square", "preview": "□", "titleKey": "qed" }, + { "key": "\\bigoplus", "preview": "⨁", "titleKey": "exclusiveOr" }, + { "key": "\\oplus", "preview": "⊕", "titleKey": "exclusiveOr" } + ] + }, + { + "titleKey": "geometryCategory", + "symbols": [ + { "key": "\\circ", "preview": "∘", "titleKey": "degrees" }, + { "key": "\\angle", "preview": "∠", "titleKey": "angle" }, + { "key": "\\measuredangle", "preview": "∡", "titleKey": "measuredAngle" }, + { "key": "\\parallel", "preview": "∥", "titleKey": "parallel" }, + { "key": "\\perp", "preview": "⊥", "titleKey": "perpendicular" }, + { "key": "\\nparallel", "preview": "∦", "titleKey": "incomparableTo" }, + { "key": "\\sim", "preview": "∼", "titleKey": "similarTo" }, + { "key": "\\simeq", "preview": "≃", "titleKey": "similarOrEqual" }, + { "key": "\\cong", "preview": "≅", "titleKey": "congruentTo" }, + { "key": "\\equiv", "preview": "≡", "titleKey": "congruentTo" } + ] + }, + { + "titleKey": "setsCategory", + "symbols": [ + { "key": "\\dots", "preview": "…", "titleKey": "ellipsis" }, + { "key": "\\vdots", "preview": "⋮", "titleKey": "ellipsisVertical" }, + { "key": "\\cdots", "preview": "⋯", "titleKey": "ellipsisCentered" }, + { "key": "\\ddots", "preview": "⋱", "titleKey": "ellipsisDiagonal" }, + { "key": "\\aleph", "preview": "ℵ", "titleKey": "cardinality" }, + { "key": "\\bigcap", "preview": "⋂", "titleKey": "intersection" }, + { "key": "\\cap", "preview": "∩", "titleKey": "intersection" }, + { "key": "\\sqcap", "preview": "⊓", "titleKey": "intersection" }, + { "key": "\\bigcup", "preview": "⋃", "titleKey": "union" }, + { "key": "\\cup", "preview": "∪", "titleKey": "union" }, + { "key": "\\sqcup", "preview": "⊔", "titleKey": "union" }, + { "key": "\\emptyset", "preview": "∅", "titleKey": "emptySet" }, + { "key": "\\in", "preview": "∈", "titleKey": "in" }, + { "key": "\\notin", "preview": "∉", "titleKey": "notIn" }, + { "key": "\\ni", "preview": "∋", "titleKey": "contains" }, + { "key": "\\ominus", "preview": "⊖", "titleKey": "symmetricDifference" }, + { "key": "\\setminus", "preview": "∖", "titleKey": "setDifference" }, + { "key": "\\subset", "preview": "⊂", "titleKey": "subset" }, + { "key": "\\sqsubset", "preview": "⊏", "titleKey": "subset" }, + { "key": "\\subseteq", "preview": "⊆", "titleKey": "subsetOrEqual" }, + { "key": "\\sqsubseteq", "preview": "⊑", "titleKey": "subsetOrEqual" }, + { "key": "\\nsubseteq", "preview": "⊈", "titleKey": "notSubsetOrEqual" }, + { "key": "\\supset", "preview": "⊃", "titleKey": "superset" }, + { "key": "\\sqsupset", "preview": "⊐", "titleKey": "superset" }, + { "key": "\\supseteq", "preview": "⊇", "titleKey": "supersetOrEqual" }, + { "key": "\\sqsupseteq", "preview": "⊒", "titleKey": "supersetOrEqual" }, + { "key": "\\nsupseteq", "preview": "⊉", "titleKey": "notSupersetOrEqual" }, + { "key": "\\wr", "preview": "≀", "titleKey": "wreathProduct" }, + { "key": "\\bowtie", "preview": "⋈", "titleKey": "naturalJoin" } + ] + }, + { + "titleKey": "directionalCategory", + "symbols": [ + { "key": "\\downarrow", "preview": "↓", "titleKey": "down" }, + { "key": "\\Downarrow", "preview": "⇓", "titleKey": "downDouble" }, + { "key": "\\bigtriangledown", "preview": "▽", "titleKey": "triangleDown" }, + { "key": "\\uparrow", "preview": "↑", "titleKey": "up" }, + { "key": "\\Uparrow", "preview": "⇑", "titleKey": "upDouble" }, + { "key": "\\bigtriangleup", "preview": "△", "titleKey": "triangleUp" }, + { "key": "\\updownarrow", "preview": "↕", "titleKey": "upDown" }, + { "key": "\\Updownarrow", "preview": "⇕", "titleKey": "upDownDouble" }, + { "key": "\\leftarrow", "preview": "←", "titleKey": "left" }, + { "key": "\\Leftarrow", "preview": "⇐", "titleKey": "leftDouble" }, + { "key": "\\longleftarrow", "preview": "⟵", "titleKey": "leftLong" }, + { "key": "\\Longleftarrow", "preview": "⟸", "titleKey": "leftLongDouble" }, + { "key": "\\hookleftarrow", "preview": "↩", "titleKey": "leftHooked" }, + { "key": "\\leftharpoondown", "preview": "↽", "titleKey": "leftHarpoonDown" }, + { "key": "\\leftharpoondup", "preview": "↼", "titleKey": "leftHarpoonUp" }, + { "key": "\\rightarrow", "preview": "→", "titleKey": "right" }, + { "key": "\\Rightarrow", "preview": "⇒", "titleKey": "rightDouble" }, + { "key": "\\longrightarrow", "preview": "⟶", "titleKey": "rightLong" }, + { "key": "\\Longrightarrow", "preview": "⟹", "titleKey": "rightLongDouble" }, + { "key": "\\hookrightarrow", "preview": "↪", "titleKey": "rightHooked" }, + { "key": "\\rightharpoondown", "preview": "⇁", "titleKey": "rightHarpoonDown" }, + { "key": "\\rightharpoondup", "preview": "⇀", "titleKey": "rightHarpoonUp" }, + { "key": "\\leftrightarrow", "preview": "↔", "titleKey": "leftRight" }, + { "key": "\\Leftrightarrow", "preview": "⇔", "titleKey": "leftRightDouble" }, + { "key": "\\longleftrightarrow", "preview": "⟷", "titleKey": "leftRightLong" }, + { "key": "\\Longleftrightarrow", "preview": "⟺", "titleKey": "leftRightLongDouble" }, + { "key": "\\nearrow", "preview": "↗", "titleKey": "northeast" }, + { "key": "\\nwarrow", "preview": "↖", "titleKey": "northwest" }, + { "key": "\\searrow", "preview": "↘", "titleKey": "southeast" }, + { "key": "\\swarrow", "preview": "↙", "titleKey": "southwest" } + ] + }, + { + "titleKey": "charactersCategory", + "symbols": [ + { "key": "\\alpha", "preview": "α", "titleKey": "alpha" }, + { "key": "\\beta", "preview": "β", "titleKey": "beta" }, + { "key": "\\chi", "preview": "χ", "titleKey": "chi" }, + { "key": "\\Delta", "preview": "Δ", "titleKey": "deltaCapital" }, + { "key": "\\delta", "preview": "δ", "titleKey": "delta" }, + { "key": "\\digamma", "preview": "ϝ", "titleKey": "digamma" }, + { "key": "\\ell", "preview": "ℓ", "titleKey": "ell" }, + { "key": "\\epsilon", "preview": "ϵ", "titleKey": "epsilon" }, + { "key": "\\varepsilon", "preview": "ε", "titleKey": "epsilon" }, + { "key": "\\eta", "preview": "η", "titleKey": "eta" }, + { "key": "\\Gamma", "preview": "Γ", "titleKey": "gammaCapital" }, + { "key": "\\gamma", "preview": "γ", "titleKey": "gamma" }, + { "key": "\\hbar", "preview": "ℏ", "titleKey": "planckConstant" }, + { "key": "\\iota", "preview": "ι", "titleKey": "iota" }, + { "key": "\\kappa", "preview": "κ", "titleKey": "kappa" }, + { "key": "\\varkappa", "preview": "ϰ", "titleKey": "kappa" }, + { "key": "\\Lambda", "preview": "Λ", "titleKey": "lambdaCapital" }, + { "key": "\\lambda", "preview": "λ", "titleKey": "lambda" }, + { "key": "\\mu", "preview": "μ", "titleKey": "mu" }, + { "key": "\\nu", "preview": "ν", "titleKey": "nu" }, + { "key": "\\Omega", "preview": "Ω", "titleKey": "omegaCapital" }, + { "key": "\\omega", "preview": "ω", "titleKey": "omega" }, + { "key": "\\Phi", "preview": "Φ", "titleKey": "phiCapital" }, + { "key": "\\phi", "preview": "ϕ", "titleKey": "phi" }, + { "key": "\\varphi", "preview": "φ", "titleKey": "phi" }, + { "key": "\\Pi", "preview": "Π", "titleKey": "piCapital" }, + { "key": "\\pi", "preview": "π", "titleKey": "pi" }, + { "key": "\\varpi", "preview": "ϖ", "titleKey": "pi" }, + { "key": "\\Psi", "preview": "Ψ", "titleKey": "psiCapital" }, + { "key": "\\psi", "preview": "ψ", "titleKey": "psi" }, + { "key": "\\rho", "preview": "ρ", "titleKey": "rho" }, + { "key": "\\varrho", "preview": "ϱ", "titleKey": "rho" }, + { "key": "\\Sigma", "preview": "Σ", "titleKey": "sigmaCapital" }, + { "key": "\\sigma", "preview": "σ", "titleKey": "sigma" }, + { "key": "\\varsigma", "preview": "ς", "titleKey": "sigma" }, + { "key": "\\tau", "preview": "τ", "titleKey": "tau" }, + { "key": "\\Theta", "preview": "Θ", "titleKey": "thetaCapital" }, + { "key": "\\theta", "preview": "θ", "titleKey": "theta" }, + { "key": "\\vartheta", "preview": "ϑ", "titleKey": "theta" }, + { "key": "\\Upsilon", "preview": "Υ", "titleKey": "upsilonCapital" }, + { "key": "\\upsilon", "preview": "υ", "titleKey": "upsilon" }, + { "key": "\\wp", "preview": "℘", "titleKey": "pWeierstrass" }, + { "key": "\\Xi", "preview": "Ξ", "titleKey": "xiCapital" }, + { "key": "\\xi", "preview": "ξ", "titleKey": "xi" }, + { "key": "\\zeta", "preview": "ζ", "titleKey": "zeta" } + ] + }, + { + "titleKey": "miscellaneousCategory", + "symbols": [ + { "key": "\\bigcirc", "preview": "◯", "titleKey": "circle" }, + { "key": "\\clubsuit", "preview": "♣", "titleKey": "club" }, + { "key": "\\diamondsuit", "preview": "♢", "titleKey": "diamond" }, + { "key": "\\heartsuit", "preview": "♡", "titleKey": "heart" }, + { "key": "\\spadesuit", "preview": "♠", "titleKey": "spade" }, + { "key": "\\flat", "preview": "♭", "titleKey": "flat" }, + { "key": "\\natural", "preview": "♮", "titleKey": "natural" }, + { "key": "\\sharp", "preview": "♯", "titleKey": "sharp" } + ] + } +] diff --git a/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/toolbar/FormatDropdown.vue b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/toolbar/FormatDropdown.vue index a55ecd43ec..a3906153cd 100644 --- a/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/toolbar/FormatDropdown.vue +++ b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/toolbar/FormatDropdown.vue @@ -126,7 +126,6 @@ case ' ': event.preventDefault(); applyFormat(format); - toggleDropdown(); break; case 'ArrowDown': event.preventDefault(); @@ -235,7 +234,6 @@ background: #e6e6e6; border-radius: 4px; outline: 2px solid #0097f2; - outline-offset: 2px; } .dropdown-icon { @@ -269,7 +267,6 @@ 'Segoe UI', sans-serif; font-size: 14px; - font-weight: 400; line-height: 140%; color: #000000; cursor: pointer; diff --git a/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/toolbar/PasteDropdown.vue b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/toolbar/PasteDropdown.vue index d8072183a3..ad5e27c398 100644 --- a/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/toolbar/PasteDropdown.vue +++ b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/toolbar/PasteDropdown.vue @@ -259,7 +259,6 @@ background: #e6e6e6; border-radius: 4px; outline: 2px solid #0097f2; - outline-offset: 2px; } .toolbar-icon { @@ -304,7 +303,6 @@ background: #e6e6e6; border-radius: 4px; outline: 2px solid #0097f2; - outline-offset: 2px; } .dropdown-arrow { @@ -343,7 +341,6 @@ 'Segoe UI', sans-serif; font-size: 14px; - font-weight: 400; line-height: 140%; color: #000000; cursor: pointer; diff --git a/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/toolbar/ToolbarButton.vue b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/toolbar/ToolbarButton.vue index 97af38d3ec..406facb8ac 100644 --- a/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/toolbar/ToolbarButton.vue +++ b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/toolbar/ToolbarButton.vue @@ -41,9 +41,9 @@ return props.icon; }); - const handleClick = () => { + const handleClick = event => { if (props.isAvailable) { - emit('click'); + emit('click', event); } }; @@ -51,7 +51,7 @@ // Handle Enter and Space keys if ((event.key === 'Enter' || event.key === ' ') && props.isAvailable) { event.preventDefault(); - emit('click'); + emit('click', event); } }; @@ -129,12 +129,11 @@ background: #e6e6e6; border-radius: 4px; outline: 2px solid #0097f2; - outline-offset: 2px; } .toolbar-icon { - width: 19px; - height: 19px; + width: 21px; + height: 21px; opacity: 0.7; } diff --git a/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/toolbar/ToolbarDivider.vue b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/toolbar/ToolbarDivider.vue index 57917ffa58..25f9782a92 100644 --- a/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/toolbar/ToolbarDivider.vue +++ b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/components/toolbar/ToolbarDivider.vue @@ -21,7 +21,7 @@ .toolbar-divider { width: 1px; height: 20px; - margin: 0 4px; + margin: 0 3px; background: #dee2e6; } diff --git a/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/composables/useEditor.js b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/composables/useEditor.js index 95375cbda0..47b1b48501 100644 --- a/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/composables/useEditor.js +++ b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/composables/useEditor.js @@ -1,8 +1,14 @@ -import { ref, onMounted, onUnmounted } from 'vue'; +import { ref, onUnmounted } from 'vue'; import { Editor } from '@tiptap/vue-2'; import StarterKitExtension from '@tiptap/starter-kit'; import UnderlineExtension from '@tiptap/extension-underline'; +import { Superscript } from '@tiptap/extension-superscript'; +import { Subscript } from '@tiptap/extension-subscript'; import { Small } from '../extensions/SmallTextExtension'; +import { Image } from '../extensions/Image'; +import { CodeBlockSyntaxHighlight } from '../extensions/CodeBlockSyntaxHighlight'; +import { CustomLink } from '../extensions/Link'; +import { Math } from '../extensions/Math'; export function useEditor() { const editor = ref(null); @@ -10,7 +16,20 @@ export function useEditor() { const initializeEditor = () => { editor.value = new Editor({ - extensions: [StarterKitExtension, UnderlineExtension, Small], + extensions: [ + StarterKitExtension.configure({ + codeBlock: false, // Disable default code block to use the extended version + link: false, // Disable default link to use the custom link extension + }), + UnderlineExtension, + CodeBlockSyntaxHighlight, + Small, + Superscript, + Subscript, + Image, + CustomLink, // Use our custom Link extension + Math, + ], content: '

', editorProps: { attributes: { @@ -31,10 +50,6 @@ export function useEditor() { } }; - onMounted(() => { - initializeEditor(); - }); - onUnmounted(() => { destroyEditor(); }); diff --git a/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/composables/useFocusTrap.js b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/composables/useFocusTrap.js new file mode 100644 index 0000000000..5907b11dfb --- /dev/null +++ b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/composables/useFocusTrap.js @@ -0,0 +1,57 @@ +import { onMounted, onUnmounted, nextTick } from 'vue'; + +/** + * A composable that traps focus within a specified element. + * @param {import('vue').Ref} rootElRef + * - A ref to the root element that should contain focus. + */ +export function useFocusTrap(rootElRef) { + const getFocusableElements = () => { + if (!rootElRef.value) return []; + const elements = rootElRef.value.querySelectorAll( + 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])', + ); + // Return only visible and enabled elements + return Array.from(elements).filter(el => !el.disabled && el.offsetParent !== null); + }; + + const handleFocusTrap = event => { + if (event.key !== 'Tab') return; + + const focusableElements = getFocusableElements(); + if (focusableElements.length === 0) return; + + const firstElement = focusableElements[0]; + const lastElement = focusableElements[focusableElements.length - 1]; + + if (event.shiftKey) { + // When Shift+Tab is pressed on the first element, loop to the last + if (document.activeElement === firstElement) { + event.preventDefault(); + lastElement.focus(); + } + } else { + // When Tab is pressed on the last element, loop to the first + if (document.activeElement === lastElement) { + event.preventDefault(); + firstElement.focus(); + } + } + }; + + onMounted(() => { + // When the component mounts, focus the first interactive element + nextTick(() => { + const focusableElements = getFocusableElements(); + if (focusableElements.length > 0) { + focusableElements[0].focus(); + } + }); + + document.addEventListener('keydown', handleFocusTrap); + }); + + onUnmounted(() => { + document.removeEventListener('keydown', handleFocusTrap); + }); +} diff --git a/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/composables/useImageHandling.js b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/composables/useImageHandling.js new file mode 100644 index 0000000000..68154a72b2 --- /dev/null +++ b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/composables/useImageHandling.js @@ -0,0 +1,126 @@ +import { ref, onMounted, onUnmounted, watch } from 'vue'; + +export function useImageHandling(editor) { + const modalMode = ref(null); // 'create' or 'edit' + const modalInitialData = ref({}); + const popoverStyle = ref({}); + const editingNodePos = ref(null); + const isModalCentered = ref(false); + + const setPopoverPosition = targetElement => { + isModalCentered.value = false; + const rect = targetElement.getBoundingClientRect(); + popoverStyle.value = { + position: 'fixed', + top: `${rect.bottom + 5}px`, + left: `${rect.right}px`, + transform: 'translateX(-100%)', + }; + }; + + const setCenteredPosition = () => { + isModalCentered.value = true; + popoverStyle.value = { + position: 'fixed', + top: '50%', + left: '50%', + transform: 'translate(-50%, -50%)', + }; + }; + + const openCreateModal = ({ file = null, targetElement = null } = {}) => { + if (targetElement) { + setPopoverPosition(targetElement); + } else { + setCenteredPosition(); + } + modalInitialData.value = { file }; + modalMode.value = 'create'; + }; + + const openEditModal = ({ pos, attrs }) => { + setCenteredPosition(); + editingNodePos.value = pos; + modalInitialData.value = { ...attrs }; + modalMode.value = 'edit'; + }; + + const closeModal = () => { + modalMode.value = null; + modalInitialData.value = {}; + editingNodePos.value = null; + isModalCentered.value = false; + }; + + const handleInsert = async data => { + if (!data.src || !editor?.value) return; + editor.value.chain().focus().setImage(data).run(); + closeModal(); + }; + + const handleUpdate = newAttrs => { + if (editingNodePos.value !== null && editor?.value) { + editor.value + .chain() + .focus() + .updateAttributes('image', newAttrs) + .setNodeSelection(editingNodePos.value) + .run(); + } + closeModal(); + }; + + const handleRemove = () => { + if (editingNodePos.value !== null && editor?.value) { + const node = editor.value.state.doc.nodeAt(editingNodePos.value); + if (node) { + editor.value + .chain() + .focus() + .deleteRange({ from: editingNodePos.value, to: editingNodePos.value + node.nodeSize }) + .run(); + } + } + closeModal(); + }; + + watch(modalMode, mode => { + const handler = event => { + const popover = document.querySelector('.image-upload-modal'); + if (popover && !popover.contains(event.target)) { + closeModal(); + } + }; + if (mode) { + setTimeout(() => { + document.addEventListener('mousedown', handler, true); + }, 0); + } else { + document.removeEventListener('mousedown', handler, true); + } + }); + + onMounted(() => { + if (editor?.value) { + editor.value.on('open-image-editor', openEditModal); + } + }); + + onUnmounted(() => { + if (editor?.value) { + editor.value.off('open-image-editor', openEditModal); + } + }); + + return { + modalMode, + modalInitialData, + popoverStyle, + isModalCentered, + openCreateModal, + closeModal, + handleInsert, + handleUpdate, + handleRemove, + }; +} diff --git a/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/composables/useLinkHandling.js b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/composables/useLinkHandling.js new file mode 100644 index 0000000000..fa0e6a3d00 --- /dev/null +++ b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/composables/useLinkHandling.js @@ -0,0 +1,164 @@ +import { ref, watch, onUnmounted } from 'vue'; + +export function useLinkHandling(editor) { + const isEditorOpen = ref(false); + const isBubbleMenuOpen = ref(false); + const popoverStyle = ref({}); + const editorInitialState = ref({ text: '', href: '' }); + const editorMode = ref('create'); + const savedSelection = ref(null); + + const calculatePosition = () => { + if (!editor.value) return {}; + const { view } = editor.value; + const { from } = view.state.selection; + const coords = view.coordsAtPos(from); + return { + position: 'fixed', + left: `${coords.left}px`, + top: `${coords.bottom + 8}px`, + zIndex: 1001, + }; + }; + + const openLinkEditor = (mode = 'create') => { + if (!editor.value) return; + + closeBubbleMenu(); // Ensure bubble menu is closed + + const { state } = editor.value; + const { from, to, empty } = state.selection; + + popoverStyle.value = calculatePosition(); + editorMode.value = mode; + + if (mode === 'edit') { + const linkAttrs = editor.value.getAttributes('link'); + if (!linkAttrs.href) { + closeLinkEditor(); + return; + } + editor.value.chain().focus().extendMarkRange('link').run(); + const newSelection = editor.value.state.selection; + savedSelection.value = { from: newSelection.from, to: newSelection.to }; + editorInitialState.value = { + href: linkAttrs.href, + text: state.doc.textBetween(newSelection.from, newSelection.to, ' '), + }; + } else { + savedSelection.value = { from, to }; + editorInitialState.value = { + href: '', + text: empty ? '' : state.doc.textBetween(from, to, ' '), + }; + } + + isEditorOpen.value = true; + }; + + const closeLinkEditor = () => { + isEditorOpen.value = false; + savedSelection.value = null; + editorMode.value = 'create'; + }; + + const openBubbleMenu = () => { + if (isEditorOpen.value) return; + popoverStyle.value = calculatePosition(); + isBubbleMenuOpen.value = true; + }; + + const closeBubbleMenu = () => { + isBubbleMenuOpen.value = false; + }; + + const closeAll = () => { + closeLinkEditor(); + closeBubbleMenu(); + }; + + const saveLink = ({ text, href }) => { + if (!editor.value || !savedSelection.value) { + return closeAll(); + } + + const { from, to } = savedSelection.value; + const tr = editor.value.state.tr; + const linkMark = editor.value.state.schema.marks.link.create({ href }); + tr.replaceWith(from, to, editor.value.state.schema.text(text, [linkMark])); + editor.value.view.dispatch(tr); + + closeAll(); + }; + + const removeLink = () => { + editor.value?.chain().focus().extendMarkRange('link').unsetLink().run(); + closeAll(); + }; + + const handleSelectionUpdate = () => { + if (editor.value.isActive('link')) { + openBubbleMenu(); + } else { + closeBubbleMenu(); + } + }; + + watch([isEditorOpen, isBubbleMenuOpen], ([editorIsOpen, bubbleIsOpen]) => { + const isOpen = editorIsOpen || bubbleIsOpen; + const clickHandler = event => { + const popover = + document.querySelector('.link-editor-popover') || + document.querySelector('.link-bubble-menu'); + if (popover && !popover.contains(event.target)) { + closeAll(); + } + }; + const scrollHandler = () => closeAll(); + + if (isOpen) { + setTimeout(() => { + document.addEventListener('mousedown', clickHandler, true); + document.addEventListener('scroll', scrollHandler, true); + }, 0); + } else { + document.removeEventListener('mousedown', clickHandler, true); + document.removeEventListener('scroll', scrollHandler, true); + } + }); + + watch( + () => editor.value, + (newEditor, oldEditor) => { + if (oldEditor) { + oldEditor.off('open-link-editor', openLinkEditor); + oldEditor.off('selectionUpdate', handleSelectionUpdate); + } + if (newEditor) { + newEditor.on('open-link-editor', openLinkEditor); + newEditor.on('selectionUpdate', handleSelectionUpdate); + } + }, + { immediate: true }, + ); + + onUnmounted(() => { + if (editor?.value) { + editor.value.off('open-link-editor', openLinkEditor); + editor.value.off('selectionUpdate', handleSelectionUpdate); + } + }); + + return { + isEditorOpen, + isBubbleMenuOpen, + popoverStyle, + editorInitialState, + editorMode, + openLinkEditor, + closeLinkEditor, + closeBubbleMenu, + saveLink, + removeLink, + }; +} diff --git a/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/composables/useMathHandling.js b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/composables/useMathHandling.js new file mode 100644 index 0000000000..ab51f39895 --- /dev/null +++ b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/composables/useMathHandling.js @@ -0,0 +1,128 @@ +import { ref, onUnmounted, watch } from 'vue'; + +export function useMathHandling(editor) { + // State for the modal + const isMathModalOpen = ref(false); + const mathModalMode = ref('create'); // 'create' or 'edit' + const mathModalInitialLatex = ref(''); + const editingMathNodePos = ref(null); + const popoverStyle = ref({}); + const isModalCentered = ref(false); + + const setPopoverPosition = targetElement => { + isModalCentered.value = false; + const rect = targetElement.getBoundingClientRect(); + popoverStyle.value = { + position: 'fixed', + top: `${rect.bottom + 5}px`, + left: `${rect.right}px`, + transform: 'translateX(-100%)', + }; + }; + + const setCenteredPosition = () => { + isModalCentered.value = true; + popoverStyle.value = { + position: 'fixed', + top: '50%', + left: '50%', + transform: 'translate(-50%, -50%)', + }; + }; + + const openCreateMathModal = ({ targetElement = null } = {}) => { + mathModalMode.value = 'create'; + mathModalInitialLatex.value = ''; + editingMathNodePos.value = null; + + if (targetElement) { + setPopoverPosition(targetElement); + } else { + setCenteredPosition(); + } + + isMathModalOpen.value = true; + }; + + const openEditMathModal = ({ pos, latex }) => { + mathModalMode.value = 'edit'; + mathModalInitialLatex.value = latex; + editingMathNodePos.value = pos; + setCenteredPosition(); // Edit mode stays centered + isMathModalOpen.value = true; + }; + + const closeMathModal = () => { + isMathModalOpen.value = false; + isModalCentered.value = false; + }; + + const handleSaveMath = newLatex => { + if (!editor.value) return; + + if (mathModalMode.value === 'create') { + editor.value.chain().focus().setMath({ latex: newLatex }).run(); + } else if (editingMathNodePos.value !== null) { + // For editing - use direct transaction approach + const { state, view } = editor.value; + const pos = editingMathNodePos.value; + const node = state.doc.nodeAt(pos); + + if (node && node.type.name === 'math') { + const tr = state.tr.setNodeMarkup(pos, null, { latex: newLatex }); + view.dispatch(tr); + editor.value.commands.focus(); + } + } + closeMathModal(); + }; + + // Watch for when the editor becomes available and set up event listeners + watch( + () => editor.value, + (newEditor, oldEditor) => { + if (oldEditor) { + oldEditor.off('open-math-editor', openEditMathModal); + } + if (newEditor) { + newEditor.on('open-math-editor', openEditMathModal); + } + }, + { immediate: true }, + ); + + // Close modal when clicking outside + watch(isMathModalOpen, isOpen => { + const handler = event => { + const modalElement = document.querySelector('.formulas-menu'); + if (modalElement && !modalElement.contains(event.target)) { + closeMathModal(); + } + }; + + if (isOpen) { + setTimeout(() => { + document.addEventListener('mousedown', handler, true); + }, 0); + } else { + document.removeEventListener('mousedown', handler, true); + } + }); + + onUnmounted(() => { + if (editor.value) { + editor.value.off('open-math-editor', openEditMathModal); + } + }); + + return { + isMathModalOpen, + mathModalMode, + mathModalInitialLatex, + popoverStyle, + isModalCentered, + openCreateMathModal, + closeMathModal, + handleSaveMath, + }; +} diff --git a/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/composables/useMathLiveLocale.js b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/composables/useMathLiveLocale.js new file mode 100644 index 0000000000..7a307d4dbb --- /dev/null +++ b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/composables/useMathLiveLocale.js @@ -0,0 +1,26 @@ +import { watch } from 'vue'; + +export function useMathLiveLocale(locale) { + const configureMathLiveLocale = newLocale => { + if (typeof window !== 'undefined' && window.MathfieldElement) { + window.MathfieldElement.locale = newLocale; + + if (window.mathVirtualKeyboard) { + window.mathVirtualKeyboard.locale = newLocale; + } + } + }; + + // Watch for locale changes + watch( + locale, + newLocale => { + configureMathLiveLocale(newLocale); + }, + { immediate: true }, + ); + + return { + configureMathLiveLocale, + }; +} diff --git a/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/composables/useToolbarActions.js b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/composables/useToolbarActions.js index 1eeacf871b..56a9018de0 100644 --- a/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/composables/useToolbarActions.js +++ b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/composables/useToolbarActions.js @@ -61,38 +61,76 @@ export function useToolbarActions() { // Copy with formatting const handleCopy = async () => { - if (editor.value) { - const { state } = editor.value; - const { from, to } = state.selection; + if (!editor.value) return; - if (from === to) return; // No selection + const { state } = editor.value; + const { from, to } = state.selection; - // Get selected text - const selectedText = state.doc.textBetween(from, to, '\n'); + if (from === to) return; // No selection - // Use browser's native selection to get the actual selected HTML - const selection = window.getSelection(); - let selectedHtml = ''; + // Get selected text for plain text fallback + const selectedText = state.doc.textBetween(from, to, '\n'); + try { + // Get HTML directly from the current selection + const selection = window.getSelection(); if (selection.rangeCount > 0) { const range = selection.getRangeAt(0); - const contents = range.cloneContents(); - const tempDiv = document.createElement('div'); - tempDiv.appendChild(contents); - selectedHtml = tempDiv.innerHTML; - } + const container = document.createElement('div'); + container.appendChild(range.cloneContents()); + + // Process math elements specifically + const mathFields = container.querySelectorAll('math-field'); + mathFields.forEach(mathField => { + const latex = mathField.getAttribute('value') || ''; + const span = document.createElement('span'); + span.className = 'math-inline'; + span.setAttribute('data-latex', latex); + span.textContent = `\\(${latex}\\)`; + mathField.parentNode.replaceChild(span, mathField); + }); + + // Remove elements marked with data-copy-ignore + const copyIgnoreElements = container.querySelectorAll('[data-copy-ignore]'); + copyIgnoreElements.forEach(el => el.remove()); + + // Remove Vue-specific attributes and wrapper divs + const allElements = container.querySelectorAll('*'); + allElements.forEach(el => { + // Remove Vue data attributes + Array.from(el.attributes).forEach(attr => { + if ( + attr.name.startsWith('data-v-') || + attr.name === 'data-node-view-wrapper' || + attr.name === 'contenteditable' || + attr.name === 'tabindex' || + attr.name === 'data-copy-ignore' + ) { + el.removeAttribute(attr.name); + } + }); + + // Remove wrapper divs + if (el.classList.contains('math-node-wrapper')) { + while (el.firstChild) { + el.parentNode.insertBefore(el.firstChild, el); + } + el.remove(); + } + }); + + const cleanHtml = container.innerHTML; - try { await navigator.clipboard.write([ new ClipboardItem({ - 'text/html': new Blob([selectedHtml], { type: 'text/html' }), + 'text/html': new Blob([cleanHtml], { type: 'text/html' }), 'text/plain': new Blob([selectedText], { type: 'text/plain' }), }), ]); - } catch (err) { - // Fallback to plain text - await navigator.clipboard.writeText(selectedText); } + } catch (err) { + // Fallback to plain text + await navigator.clipboard.writeText(selectedText); } }; @@ -142,19 +180,23 @@ export function useToolbarActions() { }; const handleSubscript = () => { - // TipTap subscript logic will be added here + if (editor?.value) { + editor.value.chain().focus().toggleSubscript().run(); + } }; const handleSuperscript = () => { - // TipTap superscript logic will be added here + if (editor?.value) { + editor.value.chain().focus().toggleSuperscript().run(); + } }; const handleInsertImage = () => { - // TipTap insert image logic will be added here + // placeholder }; const handleInsertLink = () => { - // TipTap insert link logic will be added here + // placeholder }; const handleMath = () => { @@ -162,7 +204,9 @@ export function useToolbarActions() { }; const handleCodeBlock = () => { - // TipTap code block logic may be added here + if (editor?.value) { + editor.value.chain().focus().toggleCodeBlock().run(); + } }; const handleFormatChange = format => { @@ -191,12 +235,34 @@ export function useToolbarActions() { } }; + const handleClearFormat = () => { + if (editor?.value) { + editor.value.chain().focus().unsetAllMarks().run(); + } + }; + // Helper function to check if a mark is active const isMarkActive = markName => { return editor?.value?.isActive(markName) || false; }; - // Helper function to check if a button is clickable + // Helper function to check if any of the main marks are active + const hasClearableMark = () => { + if (!editor?.value) return false; + const { from, to, empty } = editor.value.state.selection; + if (empty) { + return false; + } + + let hasMarks = false; + editor.value.state.doc.nodesBetween(from, to, node => { + if (node.marks.length > 0) { + hasMarks = true; + } + }); + return hasMarks; + }; + const isButtonAvailable = action => { if (!editor?.value) return false; @@ -205,8 +271,10 @@ export function useToolbarActions() { return editor.value.can().undo(); case 'redo': return editor.value.can().redo(); + case 'removeFormat': + return hasClearableMark(); default: - return true; // Default to true for other actions + return true; } }; @@ -282,13 +350,17 @@ export function useToolbarActions() { name: 'subscript', title: subscript$(), icon: require('../../assets/icon-subscript.svg'), + rtlIcon: require('../../assets/icon-subscriptRTL.svg'), handler: handleSubscript, + isActive: isMarkActive('subscript'), }, { name: 'superscript', title: superscript$(), icon: require('../../assets/icon-superscript.svg'), + rtlIcon: require('../../assets/icon-superscriptRTL.svg'), handler: handleSuperscript, + isActive: isMarkActive('superscript'), }, ]); @@ -303,6 +375,7 @@ export function useToolbarActions() { name: 'link', title: insertLink$(), icon: require('../../assets/icon-link.svg'), + isActive: isMarkActive('link'), handler: handleInsertLink, }, { @@ -316,6 +389,7 @@ export function useToolbarActions() { title: codeBlock$(), icon: require('../../assets/icon-codeblock.svg'), handler: handleCodeBlock, + isActive: isMarkActive('codeBlock'), }, ]); @@ -339,6 +413,8 @@ export function useToolbarActions() { handleMath, handleCodeBlock, handleFormatChange, + handleClearFormat, + canClearFormat: computed(() => isButtonAvailable('removeFormat')), // Action arrays historyActions, diff --git a/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/extensions/CodeBlockSyntaxHighlight.js b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/extensions/CodeBlockSyntaxHighlight.js new file mode 100644 index 0000000000..ae13fae18b --- /dev/null +++ b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/extensions/CodeBlockSyntaxHighlight.js @@ -0,0 +1,12 @@ +import TiptapCodeBlockLowlight from '@tiptap/extension-code-block-lowlight'; + +import { createLowlight, common } from 'lowlight'; + +const lowlight = createLowlight(common); + +export const CodeBlockSyntaxHighlight = TiptapCodeBlockLowlight.configure({ + lowlight, + HTMLAttributes: { + spellcheck: 'false', + }, +}); diff --git a/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/extensions/Image.js b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/extensions/Image.js new file mode 100644 index 0000000000..6b75c2eae1 --- /dev/null +++ b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/extensions/Image.js @@ -0,0 +1,47 @@ +import { Node, mergeAttributes } from '@tiptap/core'; +import { VueNodeViewRenderer } from '@tiptap/vue-2'; +import ImageNodeView from '../components/image/ImageNodeView.vue'; + +export const Image = Node.create({ + name: 'image', + + group: 'block', + + inline: false, + + allowBase64: true, + + addAttributes() { + return { + src: { default: null }, + alt: { default: null }, + width: { default: null }, + height: { default: null }, + }; + }, + + parseHTML() { + return [{ tag: 'img[src]' }]; + }, + + renderHTML({ HTMLAttributes }) { + return ['img', mergeAttributes(HTMLAttributes)]; + }, + + addNodeView() { + return VueNodeViewRenderer(ImageNodeView); + }, + + addCommands() { + return { + setImage: + options => + ({ commands }) => { + return commands.insertContent({ + type: this.name, + attrs: options, + }); + }, + }; + }, +}); diff --git a/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/extensions/Link.js b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/extensions/Link.js new file mode 100644 index 0000000000..8a76237bdf --- /dev/null +++ b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/extensions/Link.js @@ -0,0 +1,24 @@ +/* We specifically want to control the click behavior to show our custom bubble menu instead of +immediately navigating to the link's URL */ + +import { Link } from '@tiptap/extension-link'; + +export const CustomLink = Link.extend({ + inclusive: false, + + addOptions() { + return { + ...this.parent?.(), + openOnClick: false, + }; + }, + + addKeyboardShortcuts() { + return { + 'Mod-k': () => { + this.editor.emit('open-link-editor'); + return true; + }, + }; + }, +}); diff --git a/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/extensions/Math.js b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/extensions/Math.js new file mode 100644 index 0000000000..73546c81e2 --- /dev/null +++ b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/extensions/Math.js @@ -0,0 +1,49 @@ +import { Node, mergeAttributes } from '@tiptap/core'; +import { VueNodeViewRenderer } from '@tiptap/vue-2'; +import MathNodeView from '../components/math/MathNodeView.vue'; + +export const Math = Node.create({ + name: 'math', + group: 'inline', + inline: true, + + addAttributes() { + return { + latex: { + default: null, + parseHTML: element => element.getAttribute('data-latex'), + renderHTML: attributes => { + if (!attributes.latex) { + return {}; + } + return { 'data-latex': attributes.latex }; + }, + }, + }; + }, + + parseHTML() { + return [{ tag: 'span[data-latex]' }]; + }, + + renderHTML({ HTMLAttributes }) { + return ['span', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes)]; + }, + + addNodeView() { + return VueNodeViewRenderer(MathNodeView); + }, + + addCommands() { + return { + setMath: + attributes => + ({ commands }) => { + return commands.insertContent({ + type: this.name, + attrs: attributes, + }); + }, + }; + }, +}); diff --git a/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/services/imageService.js b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/services/imageService.js new file mode 100644 index 0000000000..2577312a80 --- /dev/null +++ b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/TipTapEditor/services/imageService.js @@ -0,0 +1,85 @@ +import { getTipTapEditorStrings } from '../TipTapEditorStrings'; + +const MAX_FILE_SIZE_MB = 10; // I think I need review on this value, I just picked what seemed reasonable +export const ACCEPTED_MIME_TYPES = [ + 'image/png', + 'image/jpeg', + 'image/jpg', + 'image/webp', + 'image/svg+xml', +]; + +const { noFileProvided$, invalidFileType$, fileTooLarge$, fileSizeUnit$, failedToProcessImage$ } = + getTipTapEditorStrings(); + +/** + * Validates a file based on type and size. + * @param {File} file - The file to validate. + * @returns {{isValid: boolean, error?: string}} + */ +export function validateFile(file) { + if (!file) { + return { isValid: false, error: noFileProvided$ }; + } + if (!ACCEPTED_MIME_TYPES.includes(file.type)) { + return { + isValid: false, + error: invalidFileType$ + ACCEPTED_MIME_TYPES.join(', '), + }; + } + if (file.size > MAX_FILE_SIZE_MB * 1024 * 1024) { + return { + isValid: false, + error: fileTooLarge$ + MAX_FILE_SIZE_MB + fileSizeUnit$, + }; + } + return { isValid: true }; +} + +/** + * Reads a file and returns it as a Data URL. + * @param {File} file - The image file. + * @returns {Promise} A promise that resolves with the Data URL. + */ +function readFileAsDataURL(file) { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onload = () => resolve(reader.result); + reader.onerror = error => reject(error); + reader.readAsDataURL(file); + }); +} + +/** + * Gets the natural dimensions of an image from its source. + * @param {string} src - The image source (e.g., a Data URL). + * @returns {Promise<{width: number, height: number}>} + */ +export function getImageDimensions(src) { + return new Promise((resolve, reject) => { + const img = new window.Image(); + img.onload = () => resolve({ width: img.width, height: img.height }); + img.onerror = error => reject(error); + img.src = src; + }); +} + +/** + * Fully processes a file: validates, reads, and gets dimensions. + * @param {File} file - The file to process. + * @returns {Promise<{src: string, width: number, height: number, file: File}>} + */ +export async function processFile(file) { + const validation = validateFile(file); + if (!validation.isValid) { + throw new Error(validation.error); + } + + try { + const src = await readFileAsDataURL(file); + const { width, height } = await getImageDimensions(src); + return { src, width, height, file }; + } catch (error) { + throw new Error(failedToProcessImage$); + } +} diff --git a/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/assets/icon-clearFormat.svg b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/assets/icon-clearFormat.svg new file mode 100644 index 0000000000..dbb054ca6e --- /dev/null +++ b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/assets/icon-clearFormat.svg @@ -0,0 +1,3 @@ + + + diff --git a/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/assets/icon-edit.svg b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/assets/icon-edit.svg new file mode 100644 index 0000000000..63c2ca84f5 --- /dev/null +++ b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/assets/icon-edit.svg @@ -0,0 +1,3 @@ + + + diff --git a/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/assets/icon-linkOff.svg b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/assets/icon-linkOff.svg new file mode 100644 index 0000000000..3518cdca07 --- /dev/null +++ b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/assets/icon-linkOff.svg @@ -0,0 +1,3 @@ + + + diff --git a/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/assets/icon-subscriptRTL.svg b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/assets/icon-subscriptRTL.svg new file mode 100644 index 0000000000..f24cea89a9 --- /dev/null +++ b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/assets/icon-subscriptRTL.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/assets/icon-superscriptRTL.svg b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/assets/icon-superscriptRTL.svg new file mode 100644 index 0000000000..eeafffef79 --- /dev/null +++ b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/assets/icon-superscriptRTL.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/assets/icon-trash.svg b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/assets/icon-trash.svg new file mode 100644 index 0000000000..1e6141d04b --- /dev/null +++ b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/assets/icon-trash.svg @@ -0,0 +1,3 @@ + + + diff --git a/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/assets/styles/code-theme-dark.css b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/assets/styles/code-theme-dark.css new file mode 100644 index 0000000000..b25a3038d9 --- /dev/null +++ b/contentcuration/contentcuration/frontend/shared/views/TipTapEditor/assets/styles/code-theme-dark.css @@ -0,0 +1,80 @@ +/* +https://github.com/microsoft/vscode/blob/main/extensions/theme-defaults/themes/dark_modern.json +*/ + +.ProseMirror pre { + padding: 1rem 1.25rem; + font-family: 'Consolas', 'Courier New', monospace; + color: #d4d4d4; + white-space: pre-wrap; + background: #1e1e1e; + border-radius: 0.5rem; +} + +.ProseMirror pre code { + display: block; + padding: 0; + overflow-x: auto; + font-size: 0.9rem; + color: inherit; + background: none; +} + +/* Comments */ +.ProseMirror .hljs-comment, +.ProseMirror .hljs-quote { + color: #6a9955; +} + +/* Keywords (const, let, if, return, etc.) & HTML Tags */ +.ProseMirror .hljs-keyword, +.ProseMirror .hljs-selector-tag, +.ProseMirror .hljs-tag .hljs-name { + color: #569cd6; +} + +/* Storage type (const) & Built-ins */ +.ProseMirror .hljs-storage.type, +.ProseMirror .hljs-built_in { + color: #c586c0; +} + +/* Strings */ +.ProseMirror .hljs-string, +.ProseMirror .hljs-symbol { + color: #ce9178; +} + +/* Numbers & Booleans */ +.ProseMirror .hljs-number, +.ProseMirror .hljs-literal { + color: #b5cea8; +} + +/* Function & Class Names */ +.ProseMirror .hljs-title.function_, +.ProseMirror .hljs-title.class_ { + color: #dcdcaa; +} + +/* HTML Attributes (class, id, etc.) & Variables/Parameters */ +.ProseMirror .hljs-attr, +.ProseMirror .hljs-variable, +.ProseMirror .hljs-params, +.ProseMirror .hljs-template-variable { + color: #9cdcfe; +} + +/* RegEx */ +.ProseMirror .hljs-regexp { + color: #d16969; +} + +/* Special Emphasis */ +.ProseMirror .hljs-emphasis { + font-style: italic; +} + +.ProseMirror .hljs-strong { + font-weight: bold; +} diff --git a/jest_config/jest.conf.js b/jest_config/jest.conf.js index 06b73f2a3c..20987fa663 100644 --- a/jest_config/jest.conf.js +++ b/jest_config/jest.conf.js @@ -18,6 +18,7 @@ module.exports = { 'shared/client': path.resolve(__dirname, './globalMocks/client.js'), 'shared/urls': path.resolve(__dirname, './globalMocks/urls.js'), '^dexie$': require.resolve('dexie'), + '^mathlive$': 'identity-obj-proxy', }, testEnvironment: 'jsdom', testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'], @@ -28,7 +29,9 @@ module.exports = { '^.+\\.js$': '/node_modules/babel-jest', '.*\\.(vue)$': '/node_modules/vue-jest', }, - transformIgnorePatterns: ['/node_modules/.pnpm/(?!(vuetify|epubjs|kolibri-design-system|kolibri-constants|axios)@)'], + transformIgnorePatterns: [ + '/node_modules/.pnpm/(?!(vuetify|epubjs|kolibri-design-system|kolibri-constants|axios|lowlight|@tiptap|tiptap|prosemirror-.*|unified|unist-.*|hast-.*|bail|trough|vfile.*|remark-.*|rehype-.*|mdast-.*|devlop))', + ], snapshotSerializers: ['/node_modules/jest-serializer-vue'], setupFilesAfterEnv: ['/jest_config/setup.js'], coverageDirectory: '/coverage', diff --git a/package.json b/package.json index 072263e08b..d6f1092fb8 100644 --- a/package.json +++ b/package.json @@ -55,6 +55,10 @@ "dependencies": { "@sentry/vue": "^7.112.2", "@tiptap/core": "^2.14.0", + "@tiptap/extension-code-block-lowlight": "^2.23.0", + "@tiptap/extension-link": "^2.23.1", + "@tiptap/extension-subscript": "^2.14.0", + "@tiptap/extension-superscript": "^2.14.0", "@tiptap/extension-underline": "^2.14.0", "@tiptap/starter-kit": "^2.13.0", "@tiptap/vue-2": "^2.13.0", @@ -69,6 +73,7 @@ "dexie-observable": "3.0.0-beta.11", "epubjs": "^0.3.89", "file-saver": "^2.0.2", + "highlight.js": "^11.11.1", "html2canvas": "^1.0.0-rc.5", "i18n-iso-countries": "^7.11.3", "intl": "1.2.5", @@ -78,7 +83,9 @@ "kolibri-constants": "^0.2.12", "kolibri-design-system": "5.2.0", "lodash": "^4.17.21", + "lowlight": "^3.3.0", "material-icons": "0.3.1", + "mathlive": "^0.105.3", "mutex-js": "^1.1.5", "node-vibrant": "^3.1.6", "pako": "^2.1.0", @@ -119,6 +126,7 @@ "babel-jest": "^29.7.0", "babel-loader": "^9.2.1", "circular-dependency-plugin": "^5.2.0", + "copy-webpack-plugin": "^13.0.0", "css-loader": "7.1.2", "eslint": "^8.57.0", "eslint-config-prettier": "^10.0.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 38287974df..e290aa9f14 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,16 +13,28 @@ importers: version: 7.120.3(vue@2.7.16) '@tiptap/core': specifier: ^2.14.0 - version: 2.22.3(@tiptap/pm@2.22.3) + version: 2.23.1(@tiptap/pm@2.23.1) + '@tiptap/extension-code-block-lowlight': + specifier: ^2.23.0 + version: 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/extension-code-block@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1)(highlight.js@11.11.1)(lowlight@3.3.0) + '@tiptap/extension-link': + specifier: ^2.23.1 + version: 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1) + '@tiptap/extension-subscript': + specifier: ^2.14.0 + version: 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1)) + '@tiptap/extension-superscript': + specifier: ^2.14.0 + version: 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1)) '@tiptap/extension-underline': specifier: ^2.14.0 - version: 2.22.3(@tiptap/core@2.22.3(@tiptap/pm@2.22.3)) + version: 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1)) '@tiptap/starter-kit': specifier: ^2.13.0 - version: 2.22.3 + version: 2.23.1 '@tiptap/vue-2': specifier: ^2.13.0 - version: 2.22.3(@tiptap/core@2.22.3(@tiptap/pm@2.22.3))(@tiptap/pm@2.22.3)(vue@2.7.16) + version: 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1)(vue@2.7.16) '@toast-ui/editor': specifier: ^2.3.1 version: 2.5.4 @@ -56,6 +68,9 @@ importers: file-saver: specifier: ^2.0.2 version: 2.0.5 + highlight.js: + specifier: ^11.11.1 + version: 11.11.1 html2canvas: specifier: ^1.0.0-rc.5 version: 1.4.1 @@ -83,9 +98,15 @@ importers: lodash: specifier: ^4.17.21 version: 4.17.21 + lowlight: + specifier: ^3.3.0 + version: 3.3.0 material-icons: specifier: 0.3.1 version: 0.3.1 + mathlive: + specifier: ^0.105.3 + version: 0.105.3 mutex-js: specifier: ^1.1.5 version: 1.1.5 @@ -197,13 +218,16 @@ importers: version: 29.7.0(@babel/core@7.28.0) babel-loader: specifier: ^9.2.1 - version: 9.2.1(@babel/core@7.28.0)(webpack@5.99.7) + version: 9.2.1(@babel/core@7.28.0)(webpack@5.99.9) circular-dependency-plugin: specifier: ^5.2.0 - version: 5.2.2(webpack@5.99.7) + version: 5.2.2(webpack@5.99.9) + copy-webpack-plugin: + specifier: ^13.0.0 + version: 13.0.0(webpack@5.99.9) css-loader: specifier: 7.1.2 - version: 7.1.2(webpack@5.99.7) + version: 7.1.2(webpack@5.99.9) eslint: specifier: ^8.57.0 version: 8.57.1 @@ -212,13 +236,13 @@ importers: version: 10.1.5(eslint@8.57.1) eslint-import-resolver-webpack: specifier: 0.13.8 - version: 0.13.8(eslint-plugin-import@2.32.0)(webpack@5.99.7) + version: 0.13.8(eslint-plugin-import@2.32.0)(webpack@5.99.9) eslint-plugin-import: specifier: ^2.31.0 version: 2.32.0(eslint-import-resolver-webpack@0.13.8)(eslint@8.57.1) eslint-plugin-jest: specifier: ^28.11.0 - version: 28.14.0(eslint@8.57.1)(jest@29.7.0(@types/node@24.0.4))(typescript@5.8.3) + version: 28.14.0(eslint@8.57.1)(jest@29.7.0(@types/node@24.0.10))(typescript@5.8.3) eslint-plugin-jest-dom: specifier: ^5.5.0 version: 5.5.0(@testing-library/dom@9.3.4)(eslint@8.57.1) @@ -233,7 +257,7 @@ importers: version: 5.0.2 file-loader: specifier: ^6.2.0 - version: 6.2.0(webpack@5.99.7) + version: 6.2.0(webpack@5.99.9) flush-promises: specifier: ^1.0.2 version: 1.0.2 @@ -242,10 +266,10 @@ importers: version: 3.0.0 jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@24.0.4) + version: 29.7.0(@types/node@24.0.10) jest-cli: specifier: ^29.7.0 - version: 29.7.0(@types/node@24.0.4) + version: 29.7.0(@types/node@24.0.10) jest-each: specifier: ^29.7.0 version: 29.7.0 @@ -257,13 +281,13 @@ importers: version: 3.1.0 kolibri-format: specifier: 1.0.1 - version: 1.0.1(@testing-library/dom@9.3.4)(eslint-import-resolver-webpack@0.13.8)(jest@29.7.0(@types/node@24.0.4))(postcss@8.5.6)(typescript@5.8.3) + version: 1.0.1(@testing-library/dom@9.3.4)(eslint-import-resolver-webpack@0.13.8)(jest@29.7.0(@types/node@24.0.10))(postcss@8.5.6)(typescript@5.8.3) kolibri-tools: specifier: 0.18.2 - version: 0.18.2(@testing-library/dom@9.3.4)(@types/node@24.0.4)(ejs@3.1.10)(eslint-import-resolver-webpack@0.13.8)(file-loader@6.2.0(webpack@5.99.7))(postcss@8.5.6)(typescript@5.8.3)(vue@2.7.16) + version: 0.18.2(@testing-library/dom@9.3.4)(@types/node@24.0.10)(ejs@3.1.10)(eslint-import-resolver-webpack@0.13.8)(file-loader@6.2.0(webpack@5.99.9))(postcss@8.5.6)(typescript@5.8.3)(vue@2.7.16) mini-css-extract-plugin: specifier: ^2.8.1 - version: 2.9.2(webpack@5.99.7) + version: 2.9.2(webpack@5.99.9) node-sass: specifier: 9.0.0 version: 9.0.0 @@ -278,7 +302,7 @@ importers: version: 6.0.0(postcss@8.5.6) postcss-loader: specifier: ^8.1.1 - version: 8.1.1(postcss@8.5.6)(typescript@5.8.3)(webpack@5.99.7) + version: 8.1.1(postcss@8.5.6)(typescript@5.8.3)(webpack@5.99.9) postcss-sass: specifier: ^0.5.0 version: 0.5.0 @@ -287,10 +311,10 @@ importers: version: 4.0.9(postcss@8.5.6) sass-loader: specifier: 16.0.5 - version: 16.0.5(node-sass@9.0.0)(webpack@5.99.7) + version: 16.0.5(node-sass@9.0.0)(webpack@5.99.9) style-loader: specifier: ^4.0.0 - version: 4.0.0(webpack@5.99.7) + version: 4.0.0(webpack@5.99.9) stylelint: specifier: ^15.11.0 version: 15.11.0(typescript@5.8.3) @@ -323,13 +347,13 @@ importers: version: 0.63.0 stylus-loader: specifier: ^8.1.0 - version: 8.1.1(stylus@0.63.0)(webpack@5.99.7) + version: 8.1.1(stylus@0.63.0)(webpack@5.99.9) vue-jest: specifier: ^3.0.7 version: 3.0.7(babel-core@7.0.0-bridge.0(@babel/core@7.28.0))(vue-template-compiler@2.7.16)(vue@2.7.16) vue-loader: specifier: 15.11.1 - version: 15.11.1(babel-core@7.0.0-bridge.0(@babel/core@7.28.0))(css-loader@7.1.2(webpack@5.99.7))(ejs@3.1.10)(lodash@4.17.21)(vue-template-compiler@2.7.16)(webpack@5.99.7) + version: 15.11.1(babel-core@7.0.0-bridge.0(@babel/core@7.28.0))(css-loader@7.1.2(webpack@5.99.9))(ejs@3.1.10)(lodash@4.17.21)(vue-template-compiler@2.7.16)(webpack@5.99.9) vue-style-loader: specifier: ^4.1.3 version: 4.1.3 @@ -341,19 +365,19 @@ importers: version: 4.1.0 webpack: specifier: ^5.97.1 - version: 5.99.7(webpack-cli@6.0.1) + version: 5.99.9(webpack-cli@6.0.1) webpack-cli: specifier: ^6.0.1 - version: 6.0.1(webpack-dev-server@5.2.2)(webpack@5.99.7) + version: 6.0.1(webpack-dev-server@5.2.2)(webpack@5.99.9) webpack-dev-server: specifier: ^5.2.2 - version: 5.2.2(webpack-cli@6.0.1)(webpack@5.99.7) + version: 5.2.2(webpack-cli@6.0.1)(webpack@5.99.9) webpack-merge: specifier: ^6.0.1 version: 6.0.1 workbox-webpack-plugin: specifier: ^7.3.0 - version: 7.3.0(@types/babel__core@7.20.5)(webpack@5.99.7) + version: 7.3.0(@types/babel__core@7.20.5)(webpack@5.99.9) packages: @@ -927,8 +951,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-typescript@7.27.1': - resolution: {integrity: sha512-Q5sT5+O4QUebHdbwKedFBEwRLb02zJ7r4A5Gg2hUoLuU3FjdMcyqcywqUrLCaDsFCxzokf7u9kuy7qz51YUuAg==} + '@babel/plugin-transform-typescript@7.28.0': + resolution: {integrity: sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1009,6 +1033,10 @@ packages: '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + '@cortex-js/compute-engine@0.28.0': + resolution: {integrity: sha512-kGs74P4KVNLSqu+iVhLSAvodScZdVGoZI2kOsUjnBEFCFjlPJ1Nj5TpBz/4nwPT+viguB+g7VseXsmcxWRx23Q==} + engines: {node: '>=21.7.3', npm: '>=10.5.0'} + '@csstools/css-parser-algorithms@2.7.1': resolution: {integrity: sha512-2SJS42gxmACHgikc1WGesXLIT8d/q2l0UFM7TaEeIzdFCE/FPMtTiizcPGGJtlPo2xuQzY09OhrLTzRxqJqwGw==} engines: {node: ^14 || ^16 || >=18} @@ -1212,18 +1240,12 @@ packages: resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} - '@jridgewell/source-map@0.3.6': - resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==} - - '@jridgewell/sourcemap-codec@1.5.0': - resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + '@jridgewell/source-map@0.3.10': + resolution: {integrity: sha512-0pPkgz9dY+bijgistcTTJ5mR+ocqRXLuhXHYdzoMmmoJ2C9S46RCm2GMUbatPEUK9Yjy26IrAy8D/M00lLkv+Q==} '@jridgewell/sourcemap-codec@1.5.4': resolution: {integrity: sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==} - '@jridgewell/trace-mapping@0.3.25': - resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} - '@jridgewell/trace-mapping@0.3.29': resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==} @@ -1342,8 +1364,8 @@ packages: '@rtsao/scc@1.1.0': resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} - '@rushstack/eslint-patch@1.11.0': - resolution: {integrity: sha512-zxnHvoMQVqewTJr/W4pKjF0bMGiKJv1WX7bSrkl46Hg0QjESbzBROWK0Wg4RphzSOS5Jiy7eFimmM3UgMrMZbQ==} + '@rushstack/eslint-patch@1.12.0': + resolution: {integrity: sha512-5EwMtOqvJMMa3HbmxLlF74e+3/HhwBTMcvt3nqVJgGCozO6hzIPOBlwm8mGVNR9SN2IJpxSnlxczyDjcn7qIyw==} '@sentry-internal/feedback@7.120.3': resolution: {integrity: sha512-ewJJIQ0mbsOX6jfiVFvqMjokxNtgP3dNwUv+4nenN+iJJPQsM6a0ocro3iscxwVdbkjw5hY3BUV2ICI5Q0UWoA==} @@ -1420,136 +1442,161 @@ packages: vue: ^2.6.10 vue-template-compiler: ^2.6.10 - '@tiptap/core@2.22.3': - resolution: {integrity: sha512-czyBPXZG/ZFyObZEF1kyusGf58Ai3X8TnaxlUUn3gqLLWPy0idXZg85NETCidzi/gAxWxL9j6Pcy+zwS4pbZYQ==} + '@tiptap/core@2.23.1': + resolution: {integrity: sha512-EURGKGsEPrwxvOPi9gA+BsczvsECJNV+xgTAGWHmEtU4YJ0AulYrCX3b7FK+aiduVhThIHDoG/Mmvmb/HPLRhQ==} peerDependencies: '@tiptap/pm': ^2.7.0 - '@tiptap/extension-blockquote@2.22.3': - resolution: {integrity: sha512-HvTXvqeGaANg0owk0Xxkgyc4lJMO5CZES2Lc3JJp8u5kV+HZIwd78eJ7fbKBMtkpKb4zOk4xQsHQ/TuhghJaeA==} + '@tiptap/extension-blockquote@2.23.1': + resolution: {integrity: sha512-GI3s+uFU88LWRaDG20Z9yIu2av3Usn8kw2lkm2ntwX1K6/mQBS/zkGhWr/FSwWOlMtTzYFxF4Ttb0e+hn67A/A==} peerDependencies: '@tiptap/core': ^2.7.0 - '@tiptap/extension-bold@2.22.3': - resolution: {integrity: sha512-J3GxKwijD42eqCwU1SS7PK5aSgnp0wgQDetLz9izAD0RQBrKj5WZA13GnPoTTlzLU4qwjcPRV+6mvF+llH6b6A==} + '@tiptap/extension-bold@2.23.1': + resolution: {integrity: sha512-OM4RxuZeOqpYRN1G/YpXSE8tZ3sVtT2XlO3qKa74qf+htWz8W3x4X0oQCrHrRTDSAA1wbmeZU3QghAIHnbvP/A==} peerDependencies: '@tiptap/core': ^2.7.0 - '@tiptap/extension-bubble-menu@2.22.3': - resolution: {integrity: sha512-8iQLNrRf3iBPKqI3dQnfvMxMfgp6y9TAbO803LihvzbIGqBaX264ES7fHtoyFIIeVjy2xFruVsTZCZofWTupGg==} + '@tiptap/extension-bubble-menu@2.23.1': + resolution: {integrity: sha512-tupuvrlZMTziVZXJuCVjLwllUnux/an9BtTYHpoRyLX9Hg0v7Kh39k9x58zJaoW8Q/Oc/qxPhbJpyOqhE1rLeg==} peerDependencies: '@tiptap/core': ^2.7.0 '@tiptap/pm': ^2.7.0 - '@tiptap/extension-bullet-list@2.22.3': - resolution: {integrity: sha512-SYvLIxqmuV0kTj4/3ZFlnZ1fr9Y233qX00BKuIpGnczeFsWQmzBJo8vGm3d1IlKPCQN+jTRtDdDE1aSum8Kv2w==} + '@tiptap/extension-bullet-list@2.23.1': + resolution: {integrity: sha512-0g9U42m+boLJZP3x9KoJHDCp9WD5abaVdqNbTg9sFPDNsepb7Zaeu8AEB+yZLP/fuTI1I4ko6qkdr3UaaIYcmA==} peerDependencies: '@tiptap/core': ^2.7.0 - '@tiptap/extension-code-block@2.22.3': - resolution: {integrity: sha512-twPCBpb/ygNixlSBAXgvfo+t56Ucpb8lvPDiZn+cH8OjmmO0ayBoSfSrjKWgaEWGPcXBrFAfsBRbYHyoHj7pXg==} + '@tiptap/extension-code-block-lowlight@2.23.1': + resolution: {integrity: sha512-EQwLyO1zKViSXcwdsnzurWOdVS1W3oiNuFnkxxSGtU8hlRbkDP4+dY8c/jRE2XejfJuxz7zY7iGg594d8+5/SA==} peerDependencies: '@tiptap/core': ^2.7.0 + '@tiptap/extension-code-block': ^2.7.0 '@tiptap/pm': ^2.7.0 + highlight.js: ^11 + lowlight: ^2 || ^3 - '@tiptap/extension-code@2.22.3': - resolution: {integrity: sha512-s+W6jHezq+n9cC40xZ3hZF6cGGSl+fBELik1b2x8+cb0WoIlqmcdWin1dgeMNrWlRZUw1aD2DNwy/PdXI5vn2g==} + '@tiptap/extension-code-block@2.23.1': + resolution: {integrity: sha512-eYzJVUR13BhSE/TYAMZihGBId+XiwhnTPqGcSFo+zx89It/vxwDLvAUn0PReMNI7ULKPTw8orUt2fVKSarb2DQ==} peerDependencies: '@tiptap/core': ^2.7.0 + '@tiptap/pm': ^2.7.0 - '@tiptap/extension-document@2.22.3': - resolution: {integrity: sha512-7MnILbhRZRyROlMUgyntzRZ/EZlqNB8fO761RNjJxR2WMb49R4yc04fz7/+f/QH/hwxoS13bKfsNUDAsDxA5Aw==} + '@tiptap/extension-code@2.23.1': + resolution: {integrity: sha512-3IOdE40m0UTR2+UXui69o/apLtutAbtzfgmMxD6q0qlRvVqz99QEfk9RPHDNlUqJtYCL4TD+sj7UclBsDdgVXA==} peerDependencies: '@tiptap/core': ^2.7.0 - '@tiptap/extension-dropcursor@2.22.3': - resolution: {integrity: sha512-yQxSfTWjdUQS+bh6KiNLR9KIMsn1SElzycQe4XE+0eoaetapGtKqxfwkTbbQdNgQOU5wQG1KOda221mnPvkpAA==} + '@tiptap/extension-document@2.23.1': + resolution: {integrity: sha512-2nkIkGVsaMJkpd024E6vXK+5XNz8VOVWp/pM6bbXpuv0HnGPrfLdh4ruuFc+xTQ3WPOmpSu8ygtujt4I1o9/6g==} + peerDependencies: + '@tiptap/core': ^2.7.0 + + '@tiptap/extension-dropcursor@2.23.1': + resolution: {integrity: sha512-GyVp+o/RVrKlLdrQvtIpJGphFGogiPjcPCkAFcrfY1vDY1EYxfVZELC96gG1mUT1BO8FUD3hmbpkWi9l8/6O4A==} peerDependencies: '@tiptap/core': ^2.7.0 '@tiptap/pm': ^2.7.0 - '@tiptap/extension-floating-menu@2.22.3': - resolution: {integrity: sha512-GeJRRdulxpwsshxzBkpOf/xJkLD2fa+49o+3FqRCmrm7AioC8oUcZZmzuzjLj5a3ZNGKPuJ9xxDkYWUjH4tE1g==} + '@tiptap/extension-floating-menu@2.23.1': + resolution: {integrity: sha512-GMWkpH+p/OUOk1Y5UGOnKuHSDEVBN7DhYIJiWt5g9LK/mpPeuqoCmQg3RQDgjtZXb74SlxLK2pS/3YcAnemdfQ==} peerDependencies: '@tiptap/core': ^2.7.0 '@tiptap/pm': ^2.7.0 - '@tiptap/extension-gapcursor@2.22.3': - resolution: {integrity: sha512-6Q8TLL4PVGcZLn27eQazCC+be8LP8uzuz5Z5e4TpIeswPAju49cerQOdEGNFKkuYv/FelWIhXNtkWFMf4eSmyw==} + '@tiptap/extension-gapcursor@2.23.1': + resolution: {integrity: sha512-iP+TiFIGZEbOvYAs04pI14mLI4xqbt64Da91TgMF1FNZUrG+9eWKjqbcHLQREuK3Qnjn5f0DI4nOBv61FlnPmA==} peerDependencies: '@tiptap/core': ^2.7.0 '@tiptap/pm': ^2.7.0 - '@tiptap/extension-hard-break@2.22.3': - resolution: {integrity: sha512-tbEji/V4Za3UhxYwB36amYhyonwe5j66iYTNRWzgjNixjrcGDbWk6cfaF9jMAgPgIDBmmtQLJY+moKskwgpnZg==} + '@tiptap/extension-hard-break@2.23.1': + resolution: {integrity: sha512-YF66EVxnBxt1bHPx6fUUSSXK1Vg+/9baJ0AfJ12hCSPCgSjUclRuNmWIH5ikVfByOmPV1xlrN9wryLoSEBcNRQ==} peerDependencies: '@tiptap/core': ^2.7.0 - '@tiptap/extension-heading@2.22.3': - resolution: {integrity: sha512-+MexJD+kXtNwMDbNTFa7jCFipx1DqAdT+n9GgInqebAN9bK+CWjC+SskzZNRqeMrQ0Er7QTsi6YC09M+74sevA==} + '@tiptap/extension-heading@2.23.1': + resolution: {integrity: sha512-5BPoli9wudiAOgSyK8309jyRhFyu5vd02lNChfpHwxUudzIJ/L+0E6FcwrDcw+yXh23cx7F5SSjtFQ7AobxlDQ==} peerDependencies: '@tiptap/core': ^2.7.0 - '@tiptap/extension-history@2.22.3': - resolution: {integrity: sha512-F9sC45zPw7vbjKrwSKuSLZ0ODyc/X3bGPeCa6HYLEHKfgqsdt2v2fQLvxjpmlwO2ZMrnkBkg76KDxHfVyrZ2zQ==} + '@tiptap/extension-history@2.23.1': + resolution: {integrity: sha512-1rp2CRjM+P58oGEgeUUDSk0ch67ngIGbGJOOjiBGKU9GIVhI2j4uSwsYTAa9qYMjMUI6IyH1xJpsY2hLKcBOtg==} peerDependencies: '@tiptap/core': ^2.7.0 '@tiptap/pm': ^2.7.0 - '@tiptap/extension-horizontal-rule@2.22.3': - resolution: {integrity: sha512-3GvY798p9pCXUBbCebIdSmi1q80l7VZz/B6NN4uUMQ9iwxWopd8yaZ0O7xx2hM2UBzPEtY3M4FAhhpYUTXNFgQ==} + '@tiptap/extension-horizontal-rule@2.23.1': + resolution: {integrity: sha512-uHEF0jpmhtgAxjKw8/s5ipEeTnu99f9RVMGAlmcthJ5Fx9TzH0MvtH4dtBNEu5MXC7+0bNsnncWo125AAbCohg==} peerDependencies: '@tiptap/core': ^2.7.0 '@tiptap/pm': ^2.7.0 - '@tiptap/extension-italic@2.22.3': - resolution: {integrity: sha512-W/rQDo7qFL7MfwfaYEcdtbk862fOmBv30qIEwVdqElBye7BFJYKtRuWBzNbG2BwKanjwMbVc/tBXF5W1sqfT7Q==} + '@tiptap/extension-italic@2.23.1': + resolution: {integrity: sha512-a+cPzffaC/1AKMmZ1Ka6l81xmTgcalf8NXfBuFCUTf5r7uI9NIgXnLo9hg+jR9F4K+bwhC4/UbMvQQzAjh0c0A==} peerDependencies: '@tiptap/core': ^2.7.0 - '@tiptap/extension-list-item@2.22.3': - resolution: {integrity: sha512-B7Fze+eM1sYbGOZtDDAwAivnj1ow2wN5RqaQPC1la3wdTK4Wgp7bdzGjvUbrN6gp3zMFCEWlqP2toc/mRAHCtA==} + '@tiptap/extension-link@2.23.1': + resolution: {integrity: sha512-zMD0V8djkvwRYACzd8EvFXXNLQH5poJt6aHC9/8uest6njRhlrRjSjwG5oa+xHW4A76XfAH0A5BPj6ZxZnAUQg==} peerDependencies: '@tiptap/core': ^2.7.0 + '@tiptap/pm': ^2.7.0 - '@tiptap/extension-ordered-list@2.22.3': - resolution: {integrity: sha512-pHGkuZhV/uAAHI9vzk/lpAkbdpMT4wUR1FI17/GE3zNrogfzx0VopCQrXq4+sQVsLUW4I6Cj6VeBjm9wB6qlIw==} + '@tiptap/extension-list-item@2.23.1': + resolution: {integrity: sha512-wVrRp6KAiyjFVFGmn+ojisP64Bsd+ZPdqQBYVbebBx1skZeW0uhG60d7vUkWHi0gCgxHZDfvDbXpfnOD0INRWw==} peerDependencies: '@tiptap/core': ^2.7.0 - '@tiptap/extension-paragraph@2.22.3': - resolution: {integrity: sha512-TYvgS7CweNFo/xVxsKWSt0wnm46Y8OtsfDSjnLbSC4Pj4ZNa6PU3zpvDTW+UxYakr+8zIPvI2WgLBkyTHq6oQA==} + '@tiptap/extension-ordered-list@2.23.1': + resolution: {integrity: sha512-Zp+qognyNgoaJ9bxkBwIuWJEnQ67RdsHXzv3YOdeGRbkUhd8LT6OL7P0mAuNbMBU8MwHxyJ7C7NsyzwzuVbFzA==} peerDependencies: '@tiptap/core': ^2.7.0 - '@tiptap/extension-strike@2.22.3': - resolution: {integrity: sha512-I+s2Csw2cTHae2vFJiojnHK+NnQjDr6441mSlAd+e7kEly1kjZ4g7J+JMj02ajNQhr/ob8/hb5r6EdIyv2xtoA==} + '@tiptap/extension-paragraph@2.23.1': + resolution: {integrity: sha512-LLEPizt1ALE7Ek6prlJ1uhoUCT8C/a3PdZpCh3DshM1L3Kv9TENlaJL2GhFl8SVUCwHmWHvXg30+4tIRFBedaQ==} peerDependencies: '@tiptap/core': ^2.7.0 - '@tiptap/extension-text-style@2.22.3': - resolution: {integrity: sha512-M3FLOUPcO8fR+rM97mR2gQ54KFkdlAUQtEPKQpO1f312gtcVdBNxgq0WgqTnBY7thWLyqQSKiAsL6y88+JddSA==} + '@tiptap/extension-strike@2.23.1': + resolution: {integrity: sha512-hAT9peYkKezRGp/EcPQKtyYQT+2XGUbb26toTr9XIBQIeQCuCpT+FirPrDMrMVWPwcJt7Rv+AzoVjDuBs9wE0A==} peerDependencies: '@tiptap/core': ^2.7.0 - '@tiptap/extension-text@2.22.3': - resolution: {integrity: sha512-07cymWkPTfq6nuum88Yf90YYArbowed8nNiu0Tw3jCvwpzf9J9TDaovT+LAKuSKtrOsnNpFB/9IqUwFxZepOGw==} + '@tiptap/extension-subscript@2.23.1': + resolution: {integrity: sha512-LFUwe90E90f38aES6ka0Jg7kUG3WTMq3M+7qRu6skEx4+izVB6ub5RTvA56trQlWefWiYeJZptf8xfIKdcwGSw==} peerDependencies: '@tiptap/core': ^2.7.0 - '@tiptap/extension-underline@2.22.3': - resolution: {integrity: sha512-floLjh1UbQ2pKgdwfw7qCAJ5VojvH1uqj7xW2RCv79aWYUuJCPD6UBpaBOt/jv7gXDJJ9EeV3m2Hga49CXBrEQ==} + '@tiptap/extension-superscript@2.23.1': + resolution: {integrity: sha512-us1p+AZD6B3+eWuhO83WP3Kd9MBSApOh5c4S9MEavmmQxHAvFk7sv/tuyQPgBQNxvNnpV5z0mfFEtqiMrCo22A==} peerDependencies: '@tiptap/core': ^2.7.0 - '@tiptap/pm@2.22.3': - resolution: {integrity: sha512-uWPeIScnpQVCYdTnL140XgcvbT1qH288CstMJ6S0Y11lC5PclPK9CxfAipsqgWWrIK7yatxKUVCg6TzfG9zpmA==} + '@tiptap/extension-text-style@2.23.1': + resolution: {integrity: sha512-fZn1GePlL27pUFfKXKoRZo4L4pZP9dUjNNiS/eltLpbi/SenJ15UKhAoHtN1KQvNGJsWkYN49FjnnltU8qvQ+Q==} + peerDependencies: + '@tiptap/core': ^2.7.0 + + '@tiptap/extension-text@2.23.1': + resolution: {integrity: sha512-XK0D/eyS1Vm5yUrCtkS0AfgyKLJqpi8nJivCOux/JLhhC4x87R1+mI8NoFDYZJ5ic/afREPSBB8jORqOi0qIHg==} + peerDependencies: + '@tiptap/core': ^2.7.0 + + '@tiptap/extension-underline@2.23.1': + resolution: {integrity: sha512-MTG+VlGStXD3uj7iPzZU8aJrqxoQyX45WX6xTouezaZzh/NQtTyVWwJqNGE7fsMhxirpJ+at0IZmqlDTjAhEDQ==} + peerDependencies: + '@tiptap/core': ^2.7.0 + + '@tiptap/pm@2.23.1': + resolution: {integrity: sha512-iAx4rP0k4Xd9Ywh+Gpaz5IWfY2CYRpiwVXWekTHLlNRFtrVIWVpMxaQr2mvRU2g0Ca6rz5w3KzkHBMqrI3dIBA==} - '@tiptap/starter-kit@2.22.3': - resolution: {integrity: sha512-GkvheaR2ORnHJ9g9R6xIT38w2uppGja/iAIrXLZ9vY1QuR+0cya/ZZ5vKU6r9C2PeyBs3aKYxRD1/j3HDhuGXw==} + '@tiptap/starter-kit@2.23.1': + resolution: {integrity: sha512-rrImwzJbKSHoFa+WdNU4I0evXcMiQ4yRm737sxvNJwYItT6fXIxrbRT7nJDmtYu2TflcfT1KklEnSrzz1hhYRw==} - '@tiptap/vue-2@2.22.3': - resolution: {integrity: sha512-x2sv+3JbupXTdoBvgTMLexi7VLbXgDE0FoJssjfa5FsZqDw+qO1e0DG2/sUKuGCbDVTT0vm8ac/7sfgTS4WXvg==} + '@tiptap/vue-2@2.23.1': + resolution: {integrity: sha512-AgbBm0PjYj+amuZZ4fZui3hrBexg1YVTw6ppDhZJL5pyY2PeTo4ag4tRoj+Z/CPS0Cxj3hC6OHgxrMdr6rYeCw==} peerDependencies: '@tiptap/core': ^2.7.0 '@tiptap/pm': ^2.7.0 @@ -1624,6 +1671,9 @@ packages: '@types/graceful-fs@4.1.9': resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} + '@types/hast@3.0.4': + resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + '@types/http-errors@2.0.5': resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==} @@ -1655,8 +1705,8 @@ packages: resolution: {integrity: sha512-tJxahnjm9dEI1X+hQSC5f2BSd/coZaqbIl1m3TCl0q9SVuC52XcXfV0XmoCU1+PmjyucuVITwoTnN8OlTbEXXA==} deprecated: This is a stub types definition for localforage (https://github.com/localForage/localForage). localforage provides its own type definitions, so you don't need @types/localforage installed! - '@types/lodash@4.17.18': - resolution: {integrity: sha512-KJ65INaxqxmU6EoCiJmRPZC9H9RVWCRd349tXM2M3O5NA7cY6YL7c0bHAHQ93NOfTObEQ004kd2QVHs/r0+m4g==} + '@types/lodash@4.17.20': + resolution: {integrity: sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==} '@types/markdown-it@14.1.2': resolution: {integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==} @@ -1679,8 +1729,8 @@ packages: '@types/node@16.9.1': resolution: {integrity: sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g==} - '@types/node@24.0.4': - resolution: {integrity: sha512-ulyqAkrhnuNq9pB76DRBTkcS6YsmDALy6Ua63V8OhrOBgbcYt6IOdzpw5P1+dyRIyMerzLkeYWBeOXPpA9GMAA==} + '@types/node@24.0.10': + resolution: {integrity: sha512-ENHwaH+JIRTDIEEbDK6QSQntAYGtbvdDXnMXnZaZ6k13Du1dPMmprkEHIL7ok2Wl2aZevetwTAb5S+7yIF+enA==} '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} @@ -1727,6 +1777,9 @@ packages: '@types/trusted-types@2.0.7': resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + '@types/web-bluetooth@0.0.20': resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==} @@ -1739,41 +1792,41 @@ packages: '@types/yargs@17.0.33': resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==} - '@typescript-eslint/project-service@8.35.0': - resolution: {integrity: sha512-41xatqRwWZuhUMF/aZm2fcUsOFKNcG28xqRSS6ZVr9BVJtGExosLAm5A1OxTjRMagx8nJqva+P5zNIGt8RIgbQ==} + '@typescript-eslint/project-service@8.35.1': + resolution: {integrity: sha512-VYxn/5LOpVxADAuP3NrnxxHYfzVtQzLKeldIhDhzC8UHaiQvYlXvKuVho1qLduFbJjjy5U5bkGwa3rUGUb1Q6Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/scope-manager@8.35.0': - resolution: {integrity: sha512-+AgL5+mcoLxl1vGjwNfiWq5fLDZM1TmTPYs2UkyHfFhgERxBbqHlNjRzhThJqz+ktBqTChRYY6zwbMwy0591AA==} + '@typescript-eslint/scope-manager@8.35.1': + resolution: {integrity: sha512-s/Bpd4i7ht2934nG+UoSPlYXd08KYz3bmjLEb7Ye1UVob0d1ENiT3lY8bsCmik4RqfSbPw9xJJHbugpPpP5JUg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.35.0': - resolution: {integrity: sha512-04k/7247kZzFraweuEirmvUj+W3bJLI9fX6fbo1Qm2YykuBvEhRTPl8tcxlYO8kZZW+HIXfkZNoasVb8EV4jpA==} + '@typescript-eslint/tsconfig-utils@8.35.1': + resolution: {integrity: sha512-K5/U9VmT9dTHoNowWZpz+/TObS3xqC5h0xAIjXPw+MNcKV9qg6eSatEnmeAwkjHijhACH0/N7bkhKvbt1+DXWQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/types@8.35.0': - resolution: {integrity: sha512-0mYH3emanku0vHw2aRLNGqe7EXh9WHEhi7kZzscrMDf6IIRUQ5Jk4wp1QrledE/36KtdZrVfKnE32eZCf/vaVQ==} + '@typescript-eslint/types@8.35.1': + resolution: {integrity: sha512-q/O04vVnKHfrrhNAscndAn1tuQhIkwqnaW+eu5waD5IPts2eX1dgJxgqcPx5BX109/qAz7IG6VrEPTOYKCNfRQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.35.0': - resolution: {integrity: sha512-F+BhnaBemgu1Qf8oHrxyw14wq6vbL8xwWKKMwTMwYIRmFFY/1n/9T/jpbobZL8vp7QyEUcC6xGrnAO4ua8Kp7w==} + '@typescript-eslint/typescript-estree@8.35.1': + resolution: {integrity: sha512-Vvpuvj4tBxIka7cPs6Y1uvM7gJgdF5Uu9F+mBJBPY4MhvjrjWGK4H0lVgLJd/8PWZ23FTqsaJaLEkBCFUk8Y9g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/utils@8.35.0': - resolution: {integrity: sha512-nqoMu7WWM7ki5tPgLVsmPM8CkqtoPUG6xXGeefM5t4x3XumOEKMoUZPdi+7F+/EotukN4R9OWdmDxN80fqoZeg==} + '@typescript-eslint/utils@8.35.1': + resolution: {integrity: sha512-lhnwatFmOFcazAsUm3ZnZFpXSxiwoa1Lj50HphnDe1Et01NF4+hrdXONSUHIcbVu2eFb1bAf+5yjXkGVkXBKAQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/visitor-keys@8.35.0': - resolution: {integrity: sha512-zTh2+1Y8ZpmeQaQVIc/ZZxsx8UzgKJyNg1PTvjzC7WMhPSVS8bfDX34k1SrwOf016qd5RU3az2UxUNue3IfQ5g==} + '@typescript-eslint/visitor-keys@8.35.1': + resolution: {integrity: sha512-VRwixir4zBWCSTP/ljEo091lbpypz57PoeAQ9imjG+vbeof9LplljsL1mos4ccG6H9IjfrVGM359RozUnuFhpw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@ungap/structured-clone@1.3.0': @@ -2560,6 +2613,10 @@ packages: commondir@1.0.1: resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} + complex-esm@2.1.1-esm1: + resolution: {integrity: sha512-IShBEWHILB9s7MnfyevqNGxV0A1cfcSnewL/4uPFiSxkcQL4Mm3FxJ0pXMtCXuWLjYz3lRRyk6OfkeDZcjD6nw==} + engines: {node: '>=16.14.2', npm: '>=8.5.0'} + compressible@2.0.18: resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} engines: {node: '>= 0.6'} @@ -2781,8 +2838,14 @@ packages: resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} engines: {node: '>= 0.6'} - core-js-compat@3.44.0: - resolution: {integrity: sha512-JepmAj2zfl6ogy34qfWtcE7nHKAJnKsQFRn++scjVS2bZFllwptzw61BZcZFYBPpUznLfAvh0LGhxKppk04ClA==} + copy-webpack-plugin@13.0.0: + resolution: {integrity: sha512-FgR/h5a6hzJqATDGd9YG41SeDViH+0bkHn6WNXCi5zKAZkeESeSxLySSsFLHqLEVCh0E+rITmCf0dusXWYukeQ==} + engines: {node: '>= 18.12.0'} + peerDependencies: + webpack: ^5.1.0 + + core-js-compat@3.43.0: + resolution: {integrity: sha512-2GML2ZsCc5LR7hZYz4AXmjQw8zuy2T//2QntwdnpuYI7jteT6GVYJL7F6C2C57R7gSYrcqVW3lAALefdbhBLDA==} core-js@2.6.12: resolution: {integrity: sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==} @@ -2896,8 +2959,8 @@ packages: lightningcss: optional: true - css-select@5.1.0: - resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} + css-select@5.2.2: + resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} css-tree@2.2.1: resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==} @@ -2911,8 +2974,8 @@ packages: resolution: {integrity: sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} - css-what@6.1.0: - resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} + css-what@6.2.2: + resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} engines: {node: '>= 6'} css.escape@1.5.1: @@ -3106,6 +3169,10 @@ packages: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + destroy@1.2.0: resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} @@ -3117,6 +3184,9 @@ packages: detect-node@2.1.0: resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + dexie-observable@3.0.0-beta.11: resolution: {integrity: sha512-mKwSYy54Kj73Njp3Rs+GoldLxyDJK1LF7niBkZxSenGAlBCXB5RxafH2Mg6Ay0B1pLlDYvJCWH31s88/Uz0WSg==} peerDependencies: @@ -3206,8 +3276,8 @@ packages: engines: {node: '>=0.10.0'} hasBin: true - electron-to-chromium@1.5.174: - resolution: {integrity: sha512-HE43yYdUUiJVjewV2A9EP8o89Kb4AqMKplMQP2IxEPUws1Etu/ZkdsgUDabUZ/WmbP4ZbvJDOcunvbBUPPIfmw==} + electron-to-chromium@1.5.178: + resolution: {integrity: sha512-wObbz/ar3Bc6e4X5vf0iO8xTN8YAjN/tgiAOJLr7yjYFtP9wAjq8Mb5h0yn6kResir+VYx2DXBj9NNobs0ETSA==} emittery@0.13.1: resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} @@ -3605,6 +3675,14 @@ packages: fb-watchman@2.0.2: resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} + fdir@6.4.6: + resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + file-entry-cache@6.0.1: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} @@ -3689,8 +3767,8 @@ packages: flatted@3.3.3: resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} - flow-parser@0.274.0: - resolution: {integrity: sha512-pwmoGcWUSyamhv1DWhSFBROYqBfVJ/XAjqAdcrKSaisn5vI5dQ0Ni0Lawrp03TE2ZPC69vseFdeWkYlpCAqxtw==} + flow-parser@0.274.2: + resolution: {integrity: sha512-kCjoA1h5j+Ttu/9fekY9XzeKPG8SvNtxigiCkezmDIOlcKr+d9LysczrPylEeSYINE3sLlX45W5vT2CroD6sWA==} engines: {node: '>=0.4.0'} flush-promises@1.0.2: @@ -3971,6 +4049,10 @@ packages: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} hasBin: true + highlight.js@11.11.1: + resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==} + engines: {node: '>=12.0.0'} + hosted-git-info@2.8.9: resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} @@ -4814,6 +4896,9 @@ packages: linkify-it@5.0.0: resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} + linkifyjs@4.3.1: + resolution: {integrity: sha512-DRSlB9DKVW04c4SUdGvKK5FR6be45lTU9M76JnngqPeeGDqPwYc0zdUErtsNVMtxPXgUWV4HbXbnC4sNyBxkYg==} + load-bmfont@1.4.2: resolution: {integrity: sha512-qElWkmjW9Oq1F9EI5Gt7aD9zcdHb9spJCW1L/dmPf7KzCCEJxq8nhHz5eCgI9aMf7vrG/wyaCqdsI+Iy9ZTlog==} @@ -4897,6 +4982,9 @@ packages: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true + lowlight@3.3.0: + resolution: {integrity: sha512-0JNhgFoPvP6U6lE/UdVsSq99tn6DhjjpAj5MxG49ewd2mOBVtwWYIT8ClyABhq198aXXODMU6Ox8DrGy/CpTZQ==} + lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} @@ -4965,6 +5053,9 @@ packages: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} + mathlive@0.105.3: + resolution: {integrity: sha512-eEnJIlRm1ga18ymY79di5Iuc161CzHs3PTXIg8WUHeEt/jpxFHs2CPVYRNxAzOo+5t4S7lA+HDretW4i5dsTmg==} + mathml-tag-names@2.1.3: resolution: {integrity: sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==} @@ -4977,8 +5068,8 @@ packages: mdn-data@2.12.2: resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==} - mdn-data@2.21.0: - resolution: {integrity: sha512-+ZKPQezM5vYJIkCxaC+4DTnRrVZR1CgsKLu5zsQERQx6Tea8Y+wMx5A24rq8A8NepCeatIQufVAekKNgiBMsGQ==} + mdn-data@2.22.0: + resolution: {integrity: sha512-IwcXqjPl4kgZbQH1WLb97/Pjt7XwPDSHnXsjENPhwx68bBf/Dw1/PwM8paXnFq974V1d8qP4o9ViVQqZuPAlCw==} mdurl@2.0.0: resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} @@ -5861,8 +5952,8 @@ packages: engines: {node: '>=10.13.0'} hasBin: true - prettier@3.6.1: - resolution: {integrity: sha512-5xGWRa90Sp2+x1dQtNpIpeOQpTDBs9cZDmA/qs2vDNN2i18PdapqY7CmBeyLlMuGqXJRIOPaCaVZTLNQRWUH/A==} + prettier@3.6.2: + resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} engines: {node: '>=14'} hasBin: true @@ -6928,6 +7019,10 @@ packages: tinycolor2@1.6.0: resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==} + tinyglobby@0.2.14: + resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} + engines: {node: '>=12.0.0'} + tippy.js@4.3.5: resolution: {integrity: sha512-NDq3efte8nGK6BOJ1dDN1/WelAwfmh3UtIYXXck6+SxLzbIQNZE/cmRSnwScZ/FyiKdIcvFHvYUgqmoGx8CcyA==} @@ -7414,8 +7509,8 @@ packages: resolution: {integrity: sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==} engines: {node: '>=10.13.0'} - webpack@5.99.7: - resolution: {integrity: sha512-CNqKBRMQjwcmKR0idID5va1qlhrqVUKpovi+Ec79ksW8ux7iS1+A6VqzfZXgVYCFRKl7XL5ap3ZoMpwBJxcg0w==} + webpack@5.99.9: + resolution: {integrity: sha512-brOPwM3JnmOa+7kd3NsmOUOwbDAj8FT9xDsG3IW0MgbN9yZV7Oi/s/+MNQ/EcSMqw7qfoRyXPoeEWT8zLVdVGg==} engines: {node: '>=10.13.0'} hasBin: true peerDependencies: @@ -7559,8 +7654,8 @@ packages: resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - ws@8.18.2: - resolution: {integrity: sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==} + ws@8.18.3: + resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -8295,7 +8390,7 @@ snapshots: '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-typescript@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-typescript@7.28.0(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 '@babel/helper-annotate-as-pure': 7.27.3 @@ -8400,7 +8495,7 @@ snapshots: babel-plugin-polyfill-corejs2: 0.4.14(@babel/core@7.28.0) babel-plugin-polyfill-corejs3: 0.13.0(@babel/core@7.28.0) babel-plugin-polyfill-regenerator: 0.6.5(@babel/core@7.28.0) - core-js-compat: 3.44.0 + core-js-compat: 3.43.0 semver: 6.3.1 transitivePeerDependencies: - supports-color @@ -8426,7 +8521,7 @@ snapshots: '@babel/helper-validator-option': 7.27.1 '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.0) '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-typescript': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-typescript': 7.28.0(@babel/core@7.28.0) transitivePeerDependencies: - supports-color @@ -8470,6 +8565,11 @@ snapshots: '@bcoe/v8-coverage@0.2.3': {} + '@cortex-js/compute-engine@0.28.0': + dependencies: + complex-esm: 2.1.1-esm1 + decimal.js: 10.5.0 + '@csstools/css-parser-algorithms@2.7.1(@csstools/css-tokenizer@2.4.1)': dependencies: '@csstools/css-tokenizer': 2.4.1 @@ -8570,7 +8670,7 @@ snapshots: '@jest/console@29.7.0': dependencies: '@jest/types': 29.6.3 - '@types/node': 24.0.4 + '@types/node': 24.0.10 chalk: 4.1.2 jest-message-util: 29.7.0 jest-util: 29.7.0 @@ -8583,14 +8683,14 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 24.0.4 + '@types/node': 24.0.10 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.9.0 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@24.0.4) + jest-config: 29.7.0(@types/node@24.0.10) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -8615,7 +8715,7 @@ snapshots: dependencies: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 24.0.4 + '@types/node': 24.0.10 jest-mock: 29.7.0 '@jest/expect-utils@29.7.0': @@ -8633,7 +8733,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@sinonjs/fake-timers': 10.3.0 - '@types/node': 24.0.4 + '@types/node': 24.0.10 jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -8655,7 +8755,7 @@ snapshots: '@jest/transform': 29.7.0 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.29 - '@types/node': 24.0.4 + '@types/node': 24.0.10 chalk: 4.1.2 collect-v8-coverage: 1.0.2 exit: 0.1.2 @@ -8704,7 +8804,7 @@ snapshots: dependencies: '@babel/core': 7.28.0 '@jest/types': 29.6.3 - '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/trace-mapping': 0.3.29 babel-plugin-istanbul: 6.1.1 chalk: 4.1.2 convert-source-map: 2.0.0 @@ -8725,7 +8825,7 @@ snapshots: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 24.0.4 + '@types/node': 24.0.10 '@types/yargs': 17.0.33 chalk: 4.1.2 @@ -8816,20 +8916,13 @@ snapshots: '@jridgewell/resolve-uri@3.1.2': {} - '@jridgewell/source-map@0.3.6': + '@jridgewell/source-map@0.3.10': dependencies: '@jridgewell/gen-mapping': 0.3.12 - '@jridgewell/trace-mapping': 0.3.25 - - '@jridgewell/sourcemap-codec@1.5.0': {} + '@jridgewell/trace-mapping': 0.3.29 '@jridgewell/sourcemap-codec@1.5.4': {} - '@jridgewell/trace-mapping@0.3.25': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.0 - '@jridgewell/trace-mapping@0.3.29': dependencies: '@jridgewell/resolve-uri': 3.1.2 @@ -8946,7 +9039,7 @@ snapshots: '@rtsao/scc@1.1.0': {} - '@rushstack/eslint-patch@1.11.0': {} + '@rushstack/eslint-patch@1.12.0': {} '@sentry-internal/feedback@7.120.3': dependencies: @@ -9061,108 +9154,130 @@ snapshots: vue: 2.7.16 vue-template-compiler: 2.7.16 - '@tiptap/core@2.22.3(@tiptap/pm@2.22.3)': + '@tiptap/core@2.23.1(@tiptap/pm@2.23.1)': dependencies: - '@tiptap/pm': 2.22.3 + '@tiptap/pm': 2.23.1 - '@tiptap/extension-blockquote@2.22.3(@tiptap/core@2.22.3(@tiptap/pm@2.22.3))': + '@tiptap/extension-blockquote@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))': dependencies: - '@tiptap/core': 2.22.3(@tiptap/pm@2.22.3) + '@tiptap/core': 2.23.1(@tiptap/pm@2.23.1) - '@tiptap/extension-bold@2.22.3(@tiptap/core@2.22.3(@tiptap/pm@2.22.3))': + '@tiptap/extension-bold@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))': dependencies: - '@tiptap/core': 2.22.3(@tiptap/pm@2.22.3) + '@tiptap/core': 2.23.1(@tiptap/pm@2.23.1) - '@tiptap/extension-bubble-menu@2.22.3(@tiptap/core@2.22.3(@tiptap/pm@2.22.3))(@tiptap/pm@2.22.3)': + '@tiptap/extension-bubble-menu@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1)': dependencies: - '@tiptap/core': 2.22.3(@tiptap/pm@2.22.3) - '@tiptap/pm': 2.22.3 + '@tiptap/core': 2.23.1(@tiptap/pm@2.23.1) + '@tiptap/pm': 2.23.1 tippy.js: 6.3.7 - '@tiptap/extension-bullet-list@2.22.3(@tiptap/core@2.22.3(@tiptap/pm@2.22.3))': + '@tiptap/extension-bullet-list@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))': + dependencies: + '@tiptap/core': 2.23.1(@tiptap/pm@2.23.1) + + '@tiptap/extension-code-block-lowlight@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/extension-code-block@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1)(highlight.js@11.11.1)(lowlight@3.3.0)': dependencies: - '@tiptap/core': 2.22.3(@tiptap/pm@2.22.3) + '@tiptap/core': 2.23.1(@tiptap/pm@2.23.1) + '@tiptap/extension-code-block': 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1) + '@tiptap/pm': 2.23.1 + highlight.js: 11.11.1 + lowlight: 3.3.0 - '@tiptap/extension-code-block@2.22.3(@tiptap/core@2.22.3(@tiptap/pm@2.22.3))(@tiptap/pm@2.22.3)': + '@tiptap/extension-code-block@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1)': dependencies: - '@tiptap/core': 2.22.3(@tiptap/pm@2.22.3) - '@tiptap/pm': 2.22.3 + '@tiptap/core': 2.23.1(@tiptap/pm@2.23.1) + '@tiptap/pm': 2.23.1 - '@tiptap/extension-code@2.22.3(@tiptap/core@2.22.3(@tiptap/pm@2.22.3))': + '@tiptap/extension-code@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))': dependencies: - '@tiptap/core': 2.22.3(@tiptap/pm@2.22.3) + '@tiptap/core': 2.23.1(@tiptap/pm@2.23.1) - '@tiptap/extension-document@2.22.3(@tiptap/core@2.22.3(@tiptap/pm@2.22.3))': + '@tiptap/extension-document@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))': dependencies: - '@tiptap/core': 2.22.3(@tiptap/pm@2.22.3) + '@tiptap/core': 2.23.1(@tiptap/pm@2.23.1) - '@tiptap/extension-dropcursor@2.22.3(@tiptap/core@2.22.3(@tiptap/pm@2.22.3))(@tiptap/pm@2.22.3)': + '@tiptap/extension-dropcursor@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1)': dependencies: - '@tiptap/core': 2.22.3(@tiptap/pm@2.22.3) - '@tiptap/pm': 2.22.3 + '@tiptap/core': 2.23.1(@tiptap/pm@2.23.1) + '@tiptap/pm': 2.23.1 - '@tiptap/extension-floating-menu@2.22.3(@tiptap/core@2.22.3(@tiptap/pm@2.22.3))(@tiptap/pm@2.22.3)': + '@tiptap/extension-floating-menu@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1)': dependencies: - '@tiptap/core': 2.22.3(@tiptap/pm@2.22.3) - '@tiptap/pm': 2.22.3 + '@tiptap/core': 2.23.1(@tiptap/pm@2.23.1) + '@tiptap/pm': 2.23.1 tippy.js: 6.3.7 - '@tiptap/extension-gapcursor@2.22.3(@tiptap/core@2.22.3(@tiptap/pm@2.22.3))(@tiptap/pm@2.22.3)': + '@tiptap/extension-gapcursor@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1)': + dependencies: + '@tiptap/core': 2.23.1(@tiptap/pm@2.23.1) + '@tiptap/pm': 2.23.1 + + '@tiptap/extension-hard-break@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))': dependencies: - '@tiptap/core': 2.22.3(@tiptap/pm@2.22.3) - '@tiptap/pm': 2.22.3 + '@tiptap/core': 2.23.1(@tiptap/pm@2.23.1) - '@tiptap/extension-hard-break@2.22.3(@tiptap/core@2.22.3(@tiptap/pm@2.22.3))': + '@tiptap/extension-heading@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))': dependencies: - '@tiptap/core': 2.22.3(@tiptap/pm@2.22.3) + '@tiptap/core': 2.23.1(@tiptap/pm@2.23.1) - '@tiptap/extension-heading@2.22.3(@tiptap/core@2.22.3(@tiptap/pm@2.22.3))': + '@tiptap/extension-history@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1)': dependencies: - '@tiptap/core': 2.22.3(@tiptap/pm@2.22.3) + '@tiptap/core': 2.23.1(@tiptap/pm@2.23.1) + '@tiptap/pm': 2.23.1 - '@tiptap/extension-history@2.22.3(@tiptap/core@2.22.3(@tiptap/pm@2.22.3))(@tiptap/pm@2.22.3)': + '@tiptap/extension-horizontal-rule@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1)': dependencies: - '@tiptap/core': 2.22.3(@tiptap/pm@2.22.3) - '@tiptap/pm': 2.22.3 + '@tiptap/core': 2.23.1(@tiptap/pm@2.23.1) + '@tiptap/pm': 2.23.1 - '@tiptap/extension-horizontal-rule@2.22.3(@tiptap/core@2.22.3(@tiptap/pm@2.22.3))(@tiptap/pm@2.22.3)': + '@tiptap/extension-italic@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))': dependencies: - '@tiptap/core': 2.22.3(@tiptap/pm@2.22.3) - '@tiptap/pm': 2.22.3 + '@tiptap/core': 2.23.1(@tiptap/pm@2.23.1) - '@tiptap/extension-italic@2.22.3(@tiptap/core@2.22.3(@tiptap/pm@2.22.3))': + '@tiptap/extension-link@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1)': dependencies: - '@tiptap/core': 2.22.3(@tiptap/pm@2.22.3) + '@tiptap/core': 2.23.1(@tiptap/pm@2.23.1) + '@tiptap/pm': 2.23.1 + linkifyjs: 4.3.1 - '@tiptap/extension-list-item@2.22.3(@tiptap/core@2.22.3(@tiptap/pm@2.22.3))': + '@tiptap/extension-list-item@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))': dependencies: - '@tiptap/core': 2.22.3(@tiptap/pm@2.22.3) + '@tiptap/core': 2.23.1(@tiptap/pm@2.23.1) - '@tiptap/extension-ordered-list@2.22.3(@tiptap/core@2.22.3(@tiptap/pm@2.22.3))': + '@tiptap/extension-ordered-list@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))': dependencies: - '@tiptap/core': 2.22.3(@tiptap/pm@2.22.3) + '@tiptap/core': 2.23.1(@tiptap/pm@2.23.1) - '@tiptap/extension-paragraph@2.22.3(@tiptap/core@2.22.3(@tiptap/pm@2.22.3))': + '@tiptap/extension-paragraph@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))': dependencies: - '@tiptap/core': 2.22.3(@tiptap/pm@2.22.3) + '@tiptap/core': 2.23.1(@tiptap/pm@2.23.1) - '@tiptap/extension-strike@2.22.3(@tiptap/core@2.22.3(@tiptap/pm@2.22.3))': + '@tiptap/extension-strike@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))': dependencies: - '@tiptap/core': 2.22.3(@tiptap/pm@2.22.3) + '@tiptap/core': 2.23.1(@tiptap/pm@2.23.1) - '@tiptap/extension-text-style@2.22.3(@tiptap/core@2.22.3(@tiptap/pm@2.22.3))': + '@tiptap/extension-subscript@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))': dependencies: - '@tiptap/core': 2.22.3(@tiptap/pm@2.22.3) + '@tiptap/core': 2.23.1(@tiptap/pm@2.23.1) - '@tiptap/extension-text@2.22.3(@tiptap/core@2.22.3(@tiptap/pm@2.22.3))': + '@tiptap/extension-superscript@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))': dependencies: - '@tiptap/core': 2.22.3(@tiptap/pm@2.22.3) + '@tiptap/core': 2.23.1(@tiptap/pm@2.23.1) - '@tiptap/extension-underline@2.22.3(@tiptap/core@2.22.3(@tiptap/pm@2.22.3))': + '@tiptap/extension-text-style@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))': dependencies: - '@tiptap/core': 2.22.3(@tiptap/pm@2.22.3) + '@tiptap/core': 2.23.1(@tiptap/pm@2.23.1) - '@tiptap/pm@2.22.3': + '@tiptap/extension-text@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))': + dependencies: + '@tiptap/core': 2.23.1(@tiptap/pm@2.23.1) + + '@tiptap/extension-underline@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))': + dependencies: + '@tiptap/core': 2.23.1(@tiptap/pm@2.23.1) + + '@tiptap/pm@2.23.1': dependencies: prosemirror-changeset: 2.3.1 prosemirror-collab: 1.3.1 @@ -9183,36 +9298,36 @@ snapshots: prosemirror-transform: 1.10.4 prosemirror-view: 1.40.0 - '@tiptap/starter-kit@2.22.3': - dependencies: - '@tiptap/core': 2.22.3(@tiptap/pm@2.22.3) - '@tiptap/extension-blockquote': 2.22.3(@tiptap/core@2.22.3(@tiptap/pm@2.22.3)) - '@tiptap/extension-bold': 2.22.3(@tiptap/core@2.22.3(@tiptap/pm@2.22.3)) - '@tiptap/extension-bullet-list': 2.22.3(@tiptap/core@2.22.3(@tiptap/pm@2.22.3)) - '@tiptap/extension-code': 2.22.3(@tiptap/core@2.22.3(@tiptap/pm@2.22.3)) - '@tiptap/extension-code-block': 2.22.3(@tiptap/core@2.22.3(@tiptap/pm@2.22.3))(@tiptap/pm@2.22.3) - '@tiptap/extension-document': 2.22.3(@tiptap/core@2.22.3(@tiptap/pm@2.22.3)) - '@tiptap/extension-dropcursor': 2.22.3(@tiptap/core@2.22.3(@tiptap/pm@2.22.3))(@tiptap/pm@2.22.3) - '@tiptap/extension-gapcursor': 2.22.3(@tiptap/core@2.22.3(@tiptap/pm@2.22.3))(@tiptap/pm@2.22.3) - '@tiptap/extension-hard-break': 2.22.3(@tiptap/core@2.22.3(@tiptap/pm@2.22.3)) - '@tiptap/extension-heading': 2.22.3(@tiptap/core@2.22.3(@tiptap/pm@2.22.3)) - '@tiptap/extension-history': 2.22.3(@tiptap/core@2.22.3(@tiptap/pm@2.22.3))(@tiptap/pm@2.22.3) - '@tiptap/extension-horizontal-rule': 2.22.3(@tiptap/core@2.22.3(@tiptap/pm@2.22.3))(@tiptap/pm@2.22.3) - '@tiptap/extension-italic': 2.22.3(@tiptap/core@2.22.3(@tiptap/pm@2.22.3)) - '@tiptap/extension-list-item': 2.22.3(@tiptap/core@2.22.3(@tiptap/pm@2.22.3)) - '@tiptap/extension-ordered-list': 2.22.3(@tiptap/core@2.22.3(@tiptap/pm@2.22.3)) - '@tiptap/extension-paragraph': 2.22.3(@tiptap/core@2.22.3(@tiptap/pm@2.22.3)) - '@tiptap/extension-strike': 2.22.3(@tiptap/core@2.22.3(@tiptap/pm@2.22.3)) - '@tiptap/extension-text': 2.22.3(@tiptap/core@2.22.3(@tiptap/pm@2.22.3)) - '@tiptap/extension-text-style': 2.22.3(@tiptap/core@2.22.3(@tiptap/pm@2.22.3)) - '@tiptap/pm': 2.22.3 - - '@tiptap/vue-2@2.22.3(@tiptap/core@2.22.3(@tiptap/pm@2.22.3))(@tiptap/pm@2.22.3)(vue@2.7.16)': - dependencies: - '@tiptap/core': 2.22.3(@tiptap/pm@2.22.3) - '@tiptap/extension-bubble-menu': 2.22.3(@tiptap/core@2.22.3(@tiptap/pm@2.22.3))(@tiptap/pm@2.22.3) - '@tiptap/extension-floating-menu': 2.22.3(@tiptap/core@2.22.3(@tiptap/pm@2.22.3))(@tiptap/pm@2.22.3) - '@tiptap/pm': 2.22.3 + '@tiptap/starter-kit@2.23.1': + dependencies: + '@tiptap/core': 2.23.1(@tiptap/pm@2.23.1) + '@tiptap/extension-blockquote': 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1)) + '@tiptap/extension-bold': 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1)) + '@tiptap/extension-bullet-list': 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1)) + '@tiptap/extension-code': 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1)) + '@tiptap/extension-code-block': 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1) + '@tiptap/extension-document': 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1)) + '@tiptap/extension-dropcursor': 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1) + '@tiptap/extension-gapcursor': 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1) + '@tiptap/extension-hard-break': 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1)) + '@tiptap/extension-heading': 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1)) + '@tiptap/extension-history': 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1) + '@tiptap/extension-horizontal-rule': 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1) + '@tiptap/extension-italic': 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1)) + '@tiptap/extension-list-item': 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1)) + '@tiptap/extension-ordered-list': 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1)) + '@tiptap/extension-paragraph': 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1)) + '@tiptap/extension-strike': 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1)) + '@tiptap/extension-text': 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1)) + '@tiptap/extension-text-style': 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1)) + '@tiptap/pm': 2.23.1 + + '@tiptap/vue-2@2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1)(vue@2.7.16)': + dependencies: + '@tiptap/core': 2.23.1(@tiptap/pm@2.23.1) + '@tiptap/extension-bubble-menu': 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1) + '@tiptap/extension-floating-menu': 2.23.1(@tiptap/core@2.23.1(@tiptap/pm@2.23.1))(@tiptap/pm@2.23.1) + '@tiptap/pm': 2.23.1 vue: 2.7.16 vue-ts-types: 1.6.2(vue@2.7.16) @@ -9255,11 +9370,11 @@ snapshots: '@types/body-parser@1.19.6': dependencies: '@types/connect': 3.4.38 - '@types/node': 24.0.4 + '@types/node': 24.0.10 '@types/bonjour@3.5.13': dependencies: - '@types/node': 24.0.4 + '@types/node': 24.0.10 '@types/codemirror@0.0.71': dependencies: @@ -9268,11 +9383,11 @@ snapshots: '@types/connect-history-api-fallback@1.5.4': dependencies: '@types/express-serve-static-core': 4.19.6 - '@types/node': 24.0.4 + '@types/node': 24.0.10 '@types/connect@3.4.38': dependencies: - '@types/node': 24.0.4 + '@types/node': 24.0.10 '@types/eslint-scope@3.7.7': dependencies: @@ -9290,7 +9405,7 @@ snapshots: '@types/express-serve-static-core@4.19.6': dependencies: - '@types/node': 24.0.4 + '@types/node': 24.0.10 '@types/qs': 6.14.0 '@types/range-parser': 1.2.7 '@types/send': 0.17.5 @@ -9304,13 +9419,17 @@ snapshots: '@types/graceful-fs@4.1.9': dependencies: - '@types/node': 24.0.4 + '@types/node': 24.0.10 + + '@types/hast@3.0.4': + dependencies: + '@types/unist': 3.0.3 '@types/http-errors@2.0.5': {} '@types/http-proxy@1.17.16': dependencies: - '@types/node': 24.0.4 + '@types/node': 24.0.10 '@types/istanbul-lib-coverage@2.0.6': {} @@ -9324,7 +9443,7 @@ snapshots: '@types/jsdom@20.0.1': dependencies: - '@types/node': 24.0.4 + '@types/node': 24.0.10 '@types/tough-cookie': 4.0.5 parse5: 7.3.0 @@ -9338,7 +9457,7 @@ snapshots: dependencies: localforage: 1.10.0 - '@types/lodash@4.17.18': {} + '@types/lodash@4.17.20': {} '@types/markdown-it@14.1.2': dependencies: @@ -9353,13 +9472,13 @@ snapshots: '@types/node-forge@1.3.11': dependencies: - '@types/node': 24.0.4 + '@types/node': 24.0.10 '@types/node@10.17.60': {} '@types/node@16.9.1': {} - '@types/node@24.0.4': + '@types/node@24.0.10': dependencies: undici-types: 7.8.0 @@ -9376,7 +9495,7 @@ snapshots: '@types/send@0.17.5': dependencies: '@types/mime': 1.3.5 - '@types/node': 24.0.4 + '@types/node': 24.0.10 '@types/serve-index@1.9.4': dependencies: @@ -9385,12 +9504,12 @@ snapshots: '@types/serve-static@1.15.8': dependencies: '@types/http-errors': 2.0.5 - '@types/node': 24.0.4 + '@types/node': 24.0.10 '@types/send': 0.17.5 '@types/sockjs@0.3.36': dependencies: - '@types/node': 24.0.4 + '@types/node': 24.0.10 '@types/stack-utils@2.0.3': {} @@ -9406,11 +9525,13 @@ snapshots: '@types/trusted-types@2.0.7': {} + '@types/unist@3.0.3': {} + '@types/web-bluetooth@0.0.20': {} '@types/ws@8.18.1': dependencies: - '@types/node': 24.0.4 + '@types/node': 24.0.10 '@types/yargs-parser@21.0.3': {} @@ -9418,32 +9539,32 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.3 - '@typescript-eslint/project-service@8.35.0(typescript@5.8.3)': + '@typescript-eslint/project-service@8.35.1(typescript@5.8.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.35.0(typescript@5.8.3) - '@typescript-eslint/types': 8.35.0 + '@typescript-eslint/tsconfig-utils': 8.35.1(typescript@5.8.3) + '@typescript-eslint/types': 8.35.1 debug: 4.4.1 typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.35.0': + '@typescript-eslint/scope-manager@8.35.1': dependencies: - '@typescript-eslint/types': 8.35.0 - '@typescript-eslint/visitor-keys': 8.35.0 + '@typescript-eslint/types': 8.35.1 + '@typescript-eslint/visitor-keys': 8.35.1 - '@typescript-eslint/tsconfig-utils@8.35.0(typescript@5.8.3)': + '@typescript-eslint/tsconfig-utils@8.35.1(typescript@5.8.3)': dependencies: typescript: 5.8.3 - '@typescript-eslint/types@8.35.0': {} + '@typescript-eslint/types@8.35.1': {} - '@typescript-eslint/typescript-estree@8.35.0(typescript@5.8.3)': + '@typescript-eslint/typescript-estree@8.35.1(typescript@5.8.3)': dependencies: - '@typescript-eslint/project-service': 8.35.0(typescript@5.8.3) - '@typescript-eslint/tsconfig-utils': 8.35.0(typescript@5.8.3) - '@typescript-eslint/types': 8.35.0 - '@typescript-eslint/visitor-keys': 8.35.0 + '@typescript-eslint/project-service': 8.35.1(typescript@5.8.3) + '@typescript-eslint/tsconfig-utils': 8.35.1(typescript@5.8.3) + '@typescript-eslint/types': 8.35.1 + '@typescript-eslint/visitor-keys': 8.35.1 debug: 4.4.1 fast-glob: 3.3.3 is-glob: 4.0.3 @@ -9454,20 +9575,20 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.35.0(eslint@8.57.1)(typescript@5.8.3)': + '@typescript-eslint/utils@8.35.1(eslint@8.57.1)(typescript@5.8.3)': dependencies: '@eslint-community/eslint-utils': 4.7.0(eslint@8.57.1) - '@typescript-eslint/scope-manager': 8.35.0 - '@typescript-eslint/types': 8.35.0 - '@typescript-eslint/typescript-estree': 8.35.0(typescript@5.8.3) + '@typescript-eslint/scope-manager': 8.35.1 + '@typescript-eslint/types': 8.35.1 + '@typescript-eslint/typescript-estree': 8.35.1(typescript@5.8.3) eslint: 8.57.1 typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.35.0': + '@typescript-eslint/visitor-keys@8.35.1': dependencies: - '@typescript-eslint/types': 8.35.0 + '@typescript-eslint/types': 8.35.1 eslint-visitor-keys: 4.2.1 '@ungap/structured-clone@1.3.0': {} @@ -9650,22 +9771,22 @@ snapshots: '@webassemblyjs/ast': 1.14.1 '@xtuc/long': 4.2.2 - '@webpack-cli/configtest@3.0.1(webpack-cli@6.0.1)(webpack@5.99.7)': + '@webpack-cli/configtest@3.0.1(webpack-cli@6.0.1)(webpack@5.99.9)': dependencies: - webpack: 5.99.7(webpack-cli@6.0.1) - webpack-cli: 6.0.1(webpack-dev-server@5.2.2)(webpack@5.99.7) + webpack: 5.99.9(webpack-cli@6.0.1) + webpack-cli: 6.0.1(webpack-dev-server@5.2.2)(webpack@5.99.9) - '@webpack-cli/info@3.0.1(webpack-cli@6.0.1)(webpack@5.99.7)': + '@webpack-cli/info@3.0.1(webpack-cli@6.0.1)(webpack@5.99.9)': dependencies: - webpack: 5.99.7(webpack-cli@6.0.1) - webpack-cli: 6.0.1(webpack-dev-server@5.2.2)(webpack@5.99.7) + webpack: 5.99.9(webpack-cli@6.0.1) + webpack-cli: 6.0.1(webpack-dev-server@5.2.2)(webpack@5.99.9) - '@webpack-cli/serve@3.0.1(webpack-cli@6.0.1)(webpack-dev-server@5.2.2)(webpack@5.99.7)': + '@webpack-cli/serve@3.0.1(webpack-cli@6.0.1)(webpack-dev-server@5.2.2)(webpack@5.99.9)': dependencies: - webpack: 5.99.7(webpack-cli@6.0.1) - webpack-cli: 6.0.1(webpack-dev-server@5.2.2)(webpack@5.99.7) + webpack: 5.99.9(webpack-cli@6.0.1) + webpack-cli: 6.0.1(webpack-dev-server@5.2.2)(webpack@5.99.9) optionalDependencies: - webpack-dev-server: 5.2.2(webpack-cli@6.0.1)(webpack@5.99.7) + webpack-dev-server: 5.2.2(webpack-cli@6.0.1)(webpack@5.99.9) '@xmldom/xmldom@0.7.13': {} @@ -9971,18 +10092,18 @@ snapshots: transitivePeerDependencies: - supports-color - babel-loader@10.0.0(@babel/core@7.28.0)(webpack@5.99.7): + babel-loader@10.0.0(@babel/core@7.28.0)(webpack@5.99.9): dependencies: '@babel/core': 7.28.0 find-up: 5.0.0 - webpack: 5.99.7(webpack-cli@6.0.1) + webpack: 5.99.9(webpack-cli@6.0.1) - babel-loader@9.2.1(@babel/core@7.28.0)(webpack@5.99.7): + babel-loader@9.2.1(@babel/core@7.28.0)(webpack@5.99.9): dependencies: '@babel/core': 7.28.0 find-cache-dir: 4.0.0 schema-utils: 4.3.2 - webpack: 5.99.7(webpack-cli@6.0.1) + webpack: 5.99.9(webpack-cli@6.0.1) babel-messages@6.23.0: dependencies: @@ -10018,7 +10139,7 @@ snapshots: dependencies: '@babel/core': 7.28.0 '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.28.0) - core-js-compat: 3.44.0 + core-js-compat: 3.43.0 transitivePeerDependencies: - supports-color @@ -10197,7 +10318,7 @@ snapshots: browserslist@4.25.1: dependencies: caniuse-lite: 1.0.30001726 - electron-to-chromium: 1.5.174 + electron-to-chromium: 1.5.178 node-releases: 2.0.19 update-browserslist-db: 1.1.3(browserslist@4.25.1) @@ -10388,9 +10509,9 @@ snapshots: ci-info@3.9.0: {} - circular-dependency-plugin@5.2.2(webpack@5.99.7): + circular-dependency-plugin@5.2.2(webpack@5.99.9): dependencies: - webpack: 5.99.7(webpack-cli@6.0.1) + webpack: 5.99.9(webpack-cli@6.0.1) cjs-module-lexer@1.4.3: {} @@ -10472,6 +10593,8 @@ snapshots: commondir@1.0.1: {} + complex-esm@2.1.1-esm1: {} + compressible@2.0.18: dependencies: mime-db: 1.54.0 @@ -10533,7 +10656,16 @@ snapshots: cookie@0.7.2: {} - core-js-compat@3.44.0: + copy-webpack-plugin@13.0.0(webpack@5.99.9): + dependencies: + glob-parent: 6.0.2 + normalize-path: 3.0.0 + schema-utils: 4.3.2 + serialize-javascript: 6.0.2 + tinyglobby: 0.2.14 + webpack: 5.99.9(webpack-cli@6.0.1) + + core-js-compat@3.43.0: dependencies: browserslist: 4.25.1 @@ -10565,13 +10697,13 @@ snapshots: crc-32@1.2.2: {} - create-jest@29.7.0(@types/node@24.0.4): + create-jest@29.7.0(@types/node@24.0.10): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@24.0.4) + jest-config: 29.7.0(@types/node@24.0.10) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -10615,7 +10747,7 @@ snapshots: dependencies: utrie: 1.0.2 - css-loader@7.1.2(webpack@5.99.7): + css-loader@7.1.2(webpack@5.99.9): dependencies: icss-utils: 5.1.0(postcss@8.5.6) postcss: 8.5.6 @@ -10626,22 +10758,22 @@ snapshots: postcss-value-parser: 4.2.0 semver: 7.7.2 optionalDependencies: - webpack: 5.99.7(webpack-cli@6.0.1) + webpack: 5.99.9(webpack-cli@6.0.1) - css-minimizer-webpack-plugin@7.0.2(webpack@5.99.7): + css-minimizer-webpack-plugin@7.0.2(webpack@5.99.9): dependencies: - '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/trace-mapping': 0.3.29 cssnano: 7.0.7(postcss@8.5.6) jest-worker: 29.7.0 postcss: 8.5.6 schema-utils: 4.3.2 serialize-javascript: 6.0.2 - webpack: 5.99.7(webpack-cli@6.0.1) + webpack: 5.99.9(webpack-cli@6.0.1) - css-select@5.1.0: + css-select@5.2.2: dependencies: boolbase: 1.0.0 - css-what: 6.1.0 + css-what: 6.2.2 domhandler: 5.0.3 domutils: 3.2.2 nth-check: 2.1.1 @@ -10661,7 +10793,7 @@ snapshots: mdn-data: 2.12.2 source-map-js: 1.2.1 - css-what@6.1.0: {} + css-what@6.2.2: {} css.escape@1.5.1: {} @@ -10874,12 +11006,18 @@ snapshots: depd@2.0.0: {} + dequal@2.0.3: {} + destroy@1.2.0: {} detect-newline@3.1.0: {} detect-node@2.1.0: {} + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + dexie-observable@3.0.0-beta.11(dexie@3.2.7): dependencies: dexie: 3.2.7 @@ -10964,7 +11102,7 @@ snapshots: dependencies: jake: 10.9.2 - electron-to-chromium@1.5.174: {} + electron-to-chromium@1.5.178: {} emittery@0.13.1: {} @@ -11180,7 +11318,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.32.0)(webpack@5.99.7): + eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.32.0)(webpack@5.99.9): dependencies: array.prototype.find: 2.2.3 debug: 3.2.7 @@ -11194,7 +11332,7 @@ snapshots: lodash: 4.17.21 resolve: 2.0.0-next.5 semver: 5.7.2 - webpack: 5.99.7(webpack-cli@6.0.1) + webpack: 5.99.9(webpack-cli@6.0.1) transitivePeerDependencies: - supports-color @@ -11204,7 +11342,7 @@ snapshots: optionalDependencies: eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-webpack: 0.13.8(eslint-plugin-import@2.32.0)(webpack@5.99.7) + eslint-import-resolver-webpack: 0.13.8(eslint-plugin-import@2.32.0)(webpack@5.99.9) transitivePeerDependencies: - supports-color @@ -11243,12 +11381,12 @@ snapshots: optionalDependencies: '@testing-library/dom': 9.3.4 - eslint-plugin-jest@28.14.0(eslint@8.57.1)(jest@29.7.0(@types/node@24.0.4))(typescript@5.8.3): + eslint-plugin-jest@28.14.0(eslint@8.57.1)(jest@29.7.0(@types/node@24.0.10))(typescript@5.8.3): dependencies: - '@typescript-eslint/utils': 8.35.0(eslint@8.57.1)(typescript@5.8.3) + '@typescript-eslint/utils': 8.35.1(eslint@8.57.1)(typescript@5.8.3) eslint: 8.57.1 optionalDependencies: - jest: 29.7.0(@types/node@24.0.4) + jest: 29.7.0(@types/node@24.0.10) transitivePeerDependencies: - supports-color - typescript @@ -11573,6 +11711,10 @@ snapshots: dependencies: bser: 2.1.1 + fdir@6.4.6(picomatch@4.0.2): + optionalDependencies: + picomatch: 4.0.2 + file-entry-cache@6.0.1: dependencies: flat-cache: 3.2.0 @@ -11581,11 +11723,11 @@ snapshots: dependencies: flat-cache: 3.2.0 - file-loader@6.2.0(webpack@5.99.7): + file-loader@6.2.0(webpack@5.99.9): dependencies: loader-utils: 2.0.4 schema-utils: 3.3.0 - webpack: 5.99.7(webpack-cli@6.0.1) + webpack: 5.99.9(webpack-cli@6.0.1) file-saver@2.0.1: {} @@ -11677,7 +11819,7 @@ snapshots: flatted@3.3.3: {} - flow-parser@0.274.0: {} + flow-parser@0.274.2: {} flush-promises@1.0.2: {} @@ -11960,6 +12102,8 @@ snapshots: he@1.2.0: {} + highlight.js@11.11.1: {} + hosted-git-info@2.8.9: {} hosted-git-info@4.1.0: @@ -12438,7 +12582,7 @@ snapshots: '@jest/expect': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 24.0.4 + '@types/node': 24.0.10 chalk: 4.1.2 co: 4.6.0 dedent: 1.6.0 @@ -12458,16 +12602,16 @@ snapshots: - babel-plugin-macros - supports-color - jest-cli@29.7.0(@types/node@24.0.4): + jest-cli@29.7.0(@types/node@24.0.10): dependencies: '@jest/core': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@24.0.4) + create-jest: 29.7.0(@types/node@24.0.10) exit: 0.1.2 import-local: 3.2.0 - jest-config: 29.7.0(@types/node@24.0.4) + jest-config: 29.7.0(@types/node@24.0.10) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -12477,7 +12621,7 @@ snapshots: - supports-color - ts-node - jest-config@29.7.0(@types/node@24.0.4): + jest-config@29.7.0(@types/node@24.0.10): dependencies: '@babel/core': 7.28.0 '@jest/test-sequencer': 29.7.0 @@ -12502,7 +12646,7 @@ snapshots: slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - '@types/node': 24.0.4 + '@types/node': 24.0.10 transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -12532,7 +12676,7 @@ snapshots: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 '@types/jsdom': 20.0.1 - '@types/node': 24.0.4 + '@types/node': 24.0.10 jest-mock: 29.7.0 jest-util: 29.7.0 jsdom: 20.0.3 @@ -12546,7 +12690,7 @@ snapshots: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 24.0.4 + '@types/node': 24.0.10 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -12556,7 +12700,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.9 - '@types/node': 24.0.4 + '@types/node': 24.0.10 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -12595,7 +12739,7 @@ snapshots: jest-mock@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 24.0.4 + '@types/node': 24.0.10 jest-util: 29.7.0 jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): @@ -12630,7 +12774,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 24.0.4 + '@types/node': 24.0.10 chalk: 4.1.2 emittery: 0.13.1 graceful-fs: 4.2.11 @@ -12658,7 +12802,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 24.0.4 + '@types/node': 24.0.10 chalk: 4.1.2 cjs-module-lexer: 1.4.3 collect-v8-coverage: 1.0.2 @@ -12708,7 +12852,7 @@ snapshots: jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 24.0.4 + '@types/node': 24.0.10 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -12727,7 +12871,7 @@ snapshots: dependencies: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 24.0.4 + '@types/node': 24.0.10 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -12736,23 +12880,23 @@ snapshots: jest-worker@27.5.1: dependencies: - '@types/node': 24.0.4 + '@types/node': 24.0.10 merge-stream: 2.0.0 supports-color: 8.1.1 jest-worker@29.7.0: dependencies: - '@types/node': 24.0.4 + '@types/node': 24.0.10 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 - jest@29.7.0(@types/node@24.0.4): + jest@29.7.0(@types/node@24.0.10): dependencies: '@jest/core': 29.7.0 '@jest/types': 29.6.3 import-local: 3.2.0 - jest-cli: 29.7.0(@types/node@24.0.4) + jest-cli: 29.7.0(@types/node@24.0.10) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -12808,7 +12952,7 @@ snapshots: '@babel/preset-flow': 7.27.1(@babel/core@7.28.0) '@babel/preset-typescript': 7.27.1(@babel/core@7.28.0) '@babel/register': 7.27.1(@babel/core@7.28.0) - flow-parser: 0.274.0 + flow-parser: 0.274.2 graceful-fs: 4.2.11 micromatch: 4.0.8 neo-async: 2.6.2 @@ -12847,7 +12991,7 @@ snapshots: whatwg-encoding: 2.0.0 whatwg-mimetype: 3.0.0 whatwg-url: 11.0.0 - ws: 8.18.2 + ws: 8.18.3 xml-name-validator: 4.0.0 transitivePeerDependencies: - bufferutil @@ -12991,14 +13135,14 @@ snapshots: vue2-teleport: 1.1.4 xstate: 4.38.3 - kolibri-format@1.0.1(@testing-library/dom@9.3.4)(eslint-import-resolver-webpack@0.13.8)(jest@29.7.0(@types/node@24.0.4))(postcss@8.5.6)(typescript@5.8.3): + kolibri-format@1.0.1(@testing-library/dom@9.3.4)(eslint-import-resolver-webpack@0.13.8)(jest@29.7.0(@types/node@24.0.10))(postcss@8.5.6)(typescript@5.8.3): dependencies: chalk: 4.1.2 commander: 13.1.0 eslint: 8.57.1 eslint-config-prettier: 10.1.5(eslint@8.57.1) eslint-plugin-import: 2.32.0(eslint-import-resolver-webpack@0.13.8)(eslint@8.57.1) - eslint-plugin-jest: 28.14.0(eslint@8.57.1)(jest@29.7.0(@types/node@24.0.4))(typescript@5.8.3) + eslint-plugin-jest: 28.14.0(eslint@8.57.1)(jest@29.7.0(@types/node@24.0.10))(typescript@5.8.3) eslint-plugin-jest-dom: 5.5.0(@testing-library/dom@9.3.4)(eslint@8.57.1) eslint-plugin-kolibri: 0.18.0 eslint-plugin-vue: 9.33.0(eslint@8.57.1) @@ -13008,7 +13152,7 @@ snapshots: postcss-less: 6.0.0(postcss@8.5.6) postcss-sass: 0.5.0 postcss-scss: 4.0.9(postcss@8.5.6) - prettier: 3.6.1 + prettier: 3.6.2 stylelint: 15.11.0(typescript@5.8.3) stylelint-config-html: 1.1.0(postcss-html@1.8.0)(stylelint@15.11.0(typescript@5.8.3)) stylelint-config-prettier: 9.0.5(stylelint@15.11.0(typescript@5.8.3)) @@ -13034,13 +13178,13 @@ snapshots: chalk: 4.1.2 loglevel: 1.9.2 - kolibri-tools@0.18.2(@testing-library/dom@9.3.4)(@types/node@24.0.4)(ejs@3.1.10)(eslint-import-resolver-webpack@0.13.8)(file-loader@6.2.0(webpack@5.99.7))(postcss@8.5.6)(typescript@5.8.3)(vue@2.7.16): + kolibri-tools@0.18.2(@testing-library/dom@9.3.4)(@types/node@24.0.10)(ejs@3.1.10)(eslint-import-resolver-webpack@0.13.8)(file-loader@6.2.0(webpack@5.99.9))(postcss@8.5.6)(typescript@5.8.3)(vue@2.7.16): dependencies: '@babel/core': 7.28.0 '@babel/plugin-syntax-import-assertions': 7.27.1(@babel/core@7.28.0) '@babel/plugin-transform-runtime': 7.28.0(@babel/core@7.28.0) '@babel/preset-env': 7.28.0(@babel/core@7.28.0) - '@rushstack/eslint-patch': 1.11.0 + '@rushstack/eslint-patch': 1.12.0 '@testing-library/jest-dom': 6.6.3 '@testing-library/user-event': 14.6.1(@testing-library/dom@9.3.4) '@testing-library/vue': 5.9.0(vue-template-compiler@2.7.16)(vue@2.7.16) @@ -13049,15 +13193,15 @@ snapshots: autoprefixer: 10.4.21(postcss@8.5.6) babel-core: 7.0.0-bridge.0(@babel/core@7.28.0) babel-jest: 29.7.0(@babel/core@7.28.0) - babel-loader: 10.0.0(@babel/core@7.28.0)(webpack@5.99.7) + babel-loader: 10.0.0(@babel/core@7.28.0)(webpack@5.99.9) browserslist-config-kolibri: 0.18.0 chalk: 4.1.2 check-node-version: 4.2.1 cli-table: 0.3.11 commander: 13.1.0 core-js: 3.43.0 - css-loader: 7.1.2(webpack@5.99.7) - css-minimizer-webpack-plugin: 7.0.2(webpack@5.99.7) + css-loader: 7.1.2(webpack@5.99.9) + css-minimizer-webpack-plugin: 7.0.2(webpack@5.99.9) csv-parse: 5.6.0 csv-writer: 1.6.0 del: 6.1.1 @@ -13065,39 +13209,39 @@ snapshots: express: 5.1.0 fast-glob: 3.3.3 ini: 5.0.0 - jest: 29.7.0(@types/node@24.0.4) + jest: 29.7.0(@types/node@24.0.10) jest-environment-jsdom: 29.7.0 jest-serializer-vue: 3.1.0 jscodeshift: 17.3.0(@babel/preset-env@7.28.0(@babel/core@7.28.0)) kolibri: 0.18.0 - kolibri-format: 1.0.1(@testing-library/dom@9.3.4)(eslint-import-resolver-webpack@0.13.8)(jest@29.7.0(@types/node@24.0.4))(postcss@8.5.6)(typescript@5.8.3) + kolibri-format: 1.0.1(@testing-library/dom@9.3.4)(eslint-import-resolver-webpack@0.13.8)(jest@29.7.0(@types/node@24.0.10))(postcss@8.5.6)(typescript@5.8.3) launch-editor-middleware: 2.10.0 lodash: 4.17.21 - mini-css-extract-plugin: 2.9.2(webpack@5.99.7) + mini-css-extract-plugin: 2.9.2(webpack@5.99.9) node-sass: 9.0.0 - postcss-loader: 8.1.1(postcss@8.5.6)(typescript@5.8.3)(webpack@5.99.7) + postcss-loader: 8.1.1(postcss@8.5.6)(typescript@5.8.3)(webpack@5.99.9) process: 0.11.10 query-ast: 1.0.5 readline-sync: 1.4.10 recast: 0.23.11 rewire: 6.0.0 rtlcss: 4.3.0 - sass-loader: 16.0.5(node-sass@9.0.0)(webpack@5.99.7) + sass-loader: 16.0.5(node-sass@9.0.0)(webpack@5.99.9) semver: 7.7.2 strip-ansi: 6.0.1 - style-loader: 4.0.0(webpack@5.99.7) + style-loader: 4.0.0(webpack@5.99.9) temp: 0.8.4 - terser-webpack-plugin: 5.3.14(webpack@5.99.7) + terser-webpack-plugin: 5.3.14(webpack@5.99.9) toml: 3.0.0 - url-loader: 4.1.1(file-loader@6.2.0(webpack@5.99.7))(webpack@5.99.7) + url-loader: 4.1.1(file-loader@6.2.0(webpack@5.99.9))(webpack@5.99.9) vue-jest: 3.0.7(babel-core@7.0.0-bridge.0(@babel/core@7.28.0))(vue-template-compiler@2.7.16)(vue@2.7.16) - vue-loader: 15.11.1(babel-core@7.0.0-bridge.0(@babel/core@7.28.0))(css-loader@7.1.2(webpack@5.99.7))(ejs@3.1.10)(lodash@4.17.21)(vue-template-compiler@2.7.16)(webpack@5.99.7) + vue-loader: 15.11.1(babel-core@7.0.0-bridge.0(@babel/core@7.28.0))(css-loader@7.1.2(webpack@5.99.9))(ejs@3.1.10)(lodash@4.17.21)(vue-template-compiler@2.7.16)(webpack@5.99.9) vue-sfc-descriptor-to-string: 1.0.0 vue-style-loader: 4.1.3 vue-template-compiler: 2.7.16 - webpack: 5.99.7(webpack-cli@6.0.1) - webpack-cli: 6.0.1(webpack-dev-server@5.2.2)(webpack@5.99.7) - webpack-dev-server: 5.2.2(webpack-cli@6.0.1)(webpack@5.99.7) + webpack: 5.99.9(webpack-cli@6.0.1) + webpack-cli: 6.0.1(webpack-dev-server@5.2.2)(webpack@5.99.9) + webpack-dev-server: 5.2.2(webpack-cli@6.0.1)(webpack@5.99.9) webpack-merge: 6.0.1 transitivePeerDependencies: - '@parcel/css' @@ -13246,6 +13390,8 @@ snapshots: dependencies: uc.micro: 2.1.0 + linkifyjs@4.3.1: {} + load-bmfont@1.4.2: dependencies: buffer-equal: 0.0.1 @@ -13331,6 +13477,12 @@ snapshots: dependencies: js-tokens: 4.0.0 + lowlight@3.3.0: + dependencies: + '@types/hast': 3.0.4 + devlop: 1.1.0 + highlight.js: 11.11.1 + lru-cache@10.4.3: {} lru-cache@4.1.5: @@ -13432,6 +13584,10 @@ snapshots: math-intrinsics@1.1.0: {} + mathlive@0.105.3: + dependencies: + '@cortex-js/compute-engine': 0.28.0 + mathml-tag-names@2.1.3: {} mdn-data@2.0.28: {} @@ -13440,7 +13596,7 @@ snapshots: mdn-data@2.12.2: {} - mdn-data@2.21.0: {} + mdn-data@2.22.0: {} mdurl@2.0.0: {} @@ -13530,11 +13686,11 @@ snapshots: min-indent@1.0.1: {} - mini-css-extract-plugin@2.9.2(webpack@5.99.7): + mini-css-extract-plugin@2.9.2(webpack@5.99.9): dependencies: schema-utils: 4.3.2 tapable: 2.2.2 - webpack: 5.99.7(webpack-cli@6.0.1) + webpack: 5.99.9(webpack-cli@6.0.1) minimalistic-assert@1.0.1: {} @@ -13705,7 +13861,7 @@ snapshots: '@jimp/custom': 0.16.13 '@jimp/plugin-resize': 0.16.13(@jimp/custom@0.16.13) '@jimp/types': 0.16.13(@jimp/custom@0.16.13) - '@types/lodash': 4.17.18 + '@types/lodash': 4.17.20 '@types/node': 10.17.60 lodash: 4.17.21 url: 0.11.4 @@ -14100,14 +14256,14 @@ snapshots: dependencies: postcss: 8.5.6 - postcss-loader@8.1.1(postcss@8.5.6)(typescript@5.8.3)(webpack@5.99.7): + postcss-loader@8.1.1(postcss@8.5.6)(typescript@5.8.3)(webpack@5.99.9): dependencies: cosmiconfig: 9.0.0(typescript@5.8.3) jiti: 1.21.7 postcss: 8.5.6 semver: 7.7.2 optionalDependencies: - webpack: 5.99.7(webpack-cli@6.0.1) + webpack: 5.99.9(webpack-cli@6.0.1) transitivePeerDependencies: - typescript @@ -14295,7 +14451,7 @@ snapshots: prettier@2.8.8: optional: true - prettier@3.6.1: {} + prettier@3.6.2: {} pretty-bytes@5.6.0: {} @@ -14807,12 +14963,12 @@ snapshots: scss-tokenizer: 0.4.3 yargs: 17.7.2 - sass-loader@16.0.5(node-sass@9.0.0)(webpack@5.99.7): + sass-loader@16.0.5(node-sass@9.0.0)(webpack@5.99.9): dependencies: neo-async: 2.6.2 optionalDependencies: node-sass: 9.0.0 - webpack: 5.99.7(webpack-cli@6.0.1) + webpack: 5.99.9(webpack-cli@6.0.1) sax@1.3.0: {} @@ -15313,9 +15469,9 @@ snapshots: '@tokenizer/token': 0.3.0 peek-readable: 4.1.0 - style-loader@4.0.0(webpack@5.99.7): + style-loader@4.0.0(webpack@5.99.9): dependencies: - webpack: 5.99.7(webpack-cli@6.0.1) + webpack: 5.99.9(webpack-cli@6.0.1) style-search@0.1.0: {} @@ -15393,7 +15549,7 @@ snapshots: css-tree: 3.1.0 is-plain-object: 5.0.0 known-css-properties: 0.36.0 - mdn-data: 2.21.0 + mdn-data: 2.22.0 postcss-media-query-parser: 0.2.3 postcss-resolve-nested-selector: 0.1.6 postcss-selector-parser: 7.1.0 @@ -15446,13 +15602,13 @@ snapshots: - supports-color - typescript - stylus-loader@8.1.1(stylus@0.63.0)(webpack@5.99.7): + stylus-loader@8.1.1(stylus@0.63.0)(webpack@5.99.9): dependencies: fast-glob: 3.3.3 normalize-path: 3.0.0 stylus: 0.63.0 optionalDependencies: - webpack: 5.99.7(webpack-cli@6.0.1) + webpack: 5.99.9(webpack-cli@6.0.1) stylus@0.63.0: dependencies: @@ -15491,9 +15647,9 @@ snapshots: dependencies: '@trysound/sax': 0.2.0 commander: 7.2.0 - css-select: 5.1.0 + css-select: 5.2.2 css-tree: 2.3.1 - css-what: 6.1.0 + css-what: 6.2.2 csso: 5.0.5 picocolors: 1.1.1 @@ -15533,18 +15689,18 @@ snapshots: type-fest: 0.16.0 unique-string: 2.0.0 - terser-webpack-plugin@5.3.14(webpack@5.99.7): + terser-webpack-plugin@5.3.14(webpack@5.99.9): dependencies: - '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/trace-mapping': 0.3.29 jest-worker: 27.5.1 schema-utils: 4.3.2 serialize-javascript: 6.0.2 terser: 5.43.1 - webpack: 5.99.7(webpack-cli@6.0.1) + webpack: 5.99.9(webpack-cli@6.0.1) terser@5.43.1: dependencies: - '@jridgewell/source-map': 0.3.6 + '@jridgewell/source-map': 0.3.10 acorn: 8.15.0 commander: 2.20.3 source-map-support: 0.5.21 @@ -15573,6 +15729,11 @@ snapshots: tinycolor2@1.6.0: {} + tinyglobby@0.2.14: + dependencies: + fdir: 6.4.6(picomatch@4.0.2) + picomatch: 4.0.2 + tippy.js@4.3.5: dependencies: popper.js: 1.16.1 @@ -15796,14 +15957,14 @@ snapshots: urix@0.1.0: {} - url-loader@4.1.1(file-loader@6.2.0(webpack@5.99.7))(webpack@5.99.7): + url-loader@4.1.1(file-loader@6.2.0(webpack@5.99.9))(webpack@5.99.9): dependencies: loader-utils: 2.0.4 mime-types: 2.1.35 schema-utils: 3.3.0 - webpack: 5.99.7(webpack-cli@6.0.1) + webpack: 5.99.9(webpack-cli@6.0.1) optionalDependencies: - file-loader: 6.2.0(webpack@5.99.7) + file-loader: 6.2.0(webpack@5.99.9) url-parse@1.5.10: dependencies: @@ -15907,15 +16068,15 @@ snapshots: transitivePeerDependencies: - supports-color - vue-loader@15.11.1(babel-core@7.0.0-bridge.0(@babel/core@7.28.0))(css-loader@7.1.2(webpack@5.99.7))(ejs@3.1.10)(lodash@4.17.21)(vue-template-compiler@2.7.16)(webpack@5.99.7): + vue-loader@15.11.1(babel-core@7.0.0-bridge.0(@babel/core@7.28.0))(css-loader@7.1.2(webpack@5.99.9))(ejs@3.1.10)(lodash@4.17.21)(vue-template-compiler@2.7.16)(webpack@5.99.9): dependencies: '@vue/component-compiler-utils': 3.3.0(babel-core@7.0.0-bridge.0(@babel/core@7.28.0))(ejs@3.1.10)(lodash@4.17.21) - css-loader: 7.1.2(webpack@5.99.7) + css-loader: 7.1.2(webpack@5.99.9) hash-sum: 1.0.2 loader-utils: 1.4.2 vue-hot-reload-api: 2.3.4 vue-style-loader: 4.1.3 - webpack: 5.99.7(webpack-cli@6.0.1) + webpack: 5.99.9(webpack-cli@6.0.1) optionalDependencies: vue-template-compiler: 2.7.16 transitivePeerDependencies: @@ -16045,12 +16206,12 @@ snapshots: webidl-conversions@7.0.0: {} - webpack-cli@6.0.1(webpack-dev-server@5.2.2)(webpack@5.99.7): + webpack-cli@6.0.1(webpack-dev-server@5.2.2)(webpack@5.99.9): dependencies: '@discoveryjs/json-ext': 0.6.3 - '@webpack-cli/configtest': 3.0.1(webpack-cli@6.0.1)(webpack@5.99.7) - '@webpack-cli/info': 3.0.1(webpack-cli@6.0.1)(webpack@5.99.7) - '@webpack-cli/serve': 3.0.1(webpack-cli@6.0.1)(webpack-dev-server@5.2.2)(webpack@5.99.7) + '@webpack-cli/configtest': 3.0.1(webpack-cli@6.0.1)(webpack@5.99.9) + '@webpack-cli/info': 3.0.1(webpack-cli@6.0.1)(webpack@5.99.9) + '@webpack-cli/serve': 3.0.1(webpack-cli@6.0.1)(webpack-dev-server@5.2.2)(webpack@5.99.9) colorette: 2.0.20 commander: 12.1.0 cross-spawn: 7.0.6 @@ -16059,12 +16220,12 @@ snapshots: import-local: 3.2.0 interpret: 3.1.1 rechoir: 0.8.0 - webpack: 5.99.7(webpack-cli@6.0.1) + webpack: 5.99.9(webpack-cli@6.0.1) webpack-merge: 6.0.1 optionalDependencies: - webpack-dev-server: 5.2.2(webpack-cli@6.0.1)(webpack@5.99.7) + webpack-dev-server: 5.2.2(webpack-cli@6.0.1)(webpack@5.99.9) - webpack-dev-middleware@7.4.2(webpack@5.99.7): + webpack-dev-middleware@7.4.2(webpack@5.99.9): dependencies: colorette: 2.0.20 memfs: 4.17.2 @@ -16073,9 +16234,9 @@ snapshots: range-parser: 1.2.1 schema-utils: 4.3.2 optionalDependencies: - webpack: 5.99.7(webpack-cli@6.0.1) + webpack: 5.99.9(webpack-cli@6.0.1) - webpack-dev-server@5.2.2(webpack-cli@6.0.1)(webpack@5.99.7): + webpack-dev-server@5.2.2(webpack-cli@6.0.1)(webpack@5.99.9): dependencies: '@types/bonjour': 3.5.13 '@types/connect-history-api-fallback': 1.5.4 @@ -16103,11 +16264,11 @@ snapshots: serve-index: 1.9.1 sockjs: 0.3.24 spdy: 4.0.2 - webpack-dev-middleware: 7.4.2(webpack@5.99.7) - ws: 8.18.2 + webpack-dev-middleware: 7.4.2(webpack@5.99.9) + ws: 8.18.3 optionalDependencies: - webpack: 5.99.7(webpack-cli@6.0.1) - webpack-cli: 6.0.1(webpack-dev-server@5.2.2)(webpack@5.99.7) + webpack: 5.99.9(webpack-cli@6.0.1) + webpack-cli: 6.0.1(webpack-dev-server@5.2.2)(webpack@5.99.9) transitivePeerDependencies: - bufferutil - debug @@ -16127,7 +16288,7 @@ snapshots: webpack-sources@3.3.3: {} - webpack@5.99.7(webpack-cli@6.0.1): + webpack@5.99.9(webpack-cli@6.0.1): dependencies: '@types/eslint-scope': 3.7.7 '@types/estree': 1.0.8 @@ -16150,11 +16311,11 @@ snapshots: neo-async: 2.6.2 schema-utils: 4.3.2 tapable: 2.2.2 - terser-webpack-plugin: 5.3.14(webpack@5.99.7) + terser-webpack-plugin: 5.3.14(webpack@5.99.9) watchpack: 2.4.4 webpack-sources: 3.3.3 optionalDependencies: - webpack-cli: 6.0.1(webpack-dev-server@5.2.2)(webpack@5.99.7) + webpack-cli: 6.0.1(webpack-dev-server@5.2.2)(webpack@5.99.9) transitivePeerDependencies: - '@swc/core' - esbuild @@ -16355,12 +16516,12 @@ snapshots: workbox-sw@7.3.0: {} - workbox-webpack-plugin@7.3.0(@types/babel__core@7.20.5)(webpack@5.99.7): + workbox-webpack-plugin@7.3.0(@types/babel__core@7.20.5)(webpack@5.99.9): dependencies: fast-json-stable-stringify: 2.1.0 pretty-bytes: 5.6.0 upath: 1.2.0 - webpack: 5.99.7(webpack-cli@6.0.1) + webpack: 5.99.9(webpack-cli@6.0.1) webpack-sources: 1.4.3 workbox-build: 7.3.0(@types/babel__core@7.20.5) transitivePeerDependencies: @@ -16396,7 +16557,7 @@ snapshots: imurmurhash: 0.1.4 signal-exit: 4.1.0 - ws@8.18.2: {} + ws@8.18.3: {} xhr@2.6.0: dependencies: diff --git a/webpack.config.js b/webpack.config.js index cca2abe8c2..def4a8687e 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -16,6 +16,8 @@ const WebpackRTLPlugin = require('kolibri-tools/lib/webpackRtlPlugin'); const { InjectManifest } = require('workbox-webpack-plugin'); +const CopyPlugin = require('copy-webpack-plugin'); + // Function to detect if running in WSL function isWSL() { try { @@ -167,6 +169,14 @@ module.exports = (env = {}) => { new WebpackRTLPlugin({ minify: false, }), + new CopyPlugin({ + patterns: [ + { + from: path.resolve(__dirname, 'node_modules/mathlive/fonts'), + to: path.join(bundleOutputDir, 'fonts'), + }, + ], + }), new CircularDependencyPlugin({ // exclude detection of files based on a RegExp exclude: /a\.js|node_modules/,