Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-03-04 12:13:07 +00:00
parent 9fc9084df3
commit 7150920cea
73 changed files with 947 additions and 1152 deletions

View file

@ -48,54 +48,48 @@ import Video from './nodes/video';
// from GFM should have a node or mark here.
// The GFM-to-HTML-to-GFM cycle is tested in spec/features/markdown/copy_as_gfm_spec.rb.
export default [
new Doc(),
new Paragraph(),
new Text(),
export default {
nodes: [
Doc(),
Paragraph(),
Text(),
new Blockquote(),
new CodeBlock(),
new HardBreak(),
new Heading({ maxLevel: 6 }),
new HorizontalRule(),
new Image(),
Blockquote(),
CodeBlock(),
HardBreak(),
Heading(),
HorizontalRule(),
Image(),
new Table(),
new TableHead(),
new TableBody(),
new TableHeaderRow(),
new TableRow(),
new TableCell(),
Table(),
TableHead(),
TableBody(),
TableHeaderRow(),
TableRow(),
TableCell(),
new Emoji(),
new Reference(),
Emoji(),
Reference(),
new TableOfContents(),
new Video(),
new Audio(),
TableOfContents(),
Video(),
Audio(),
new BulletList(),
new OrderedList(),
new ListItem(),
BulletList(),
OrderedList(),
ListItem(),
new DescriptionList(),
new DescriptionTerm(),
new DescriptionDetails(),
DescriptionList(),
DescriptionTerm(),
DescriptionDetails(),
new TaskList(),
new OrderedTaskList(),
new TaskListItem(),
TaskList(),
OrderedTaskList(),
TaskListItem(),
new Summary(),
new Details(),
Summary(),
Details(),
],
new Bold(),
new Italic(),
new Strike(),
new InlineDiff(),
new Link(),
new Code(),
new MathMark(),
new InlineHTML(),
];
marks: [Bold(), Italic(), Strike(), InlineDiff(), Link(), Code(), MathMark(), InlineHTML()],
};

View file

@ -1,11 +1,17 @@
/* eslint-disable class-methods-use-this */
import { Bold as BaseBold } from 'tiptap-extensions';
import { defaultMarkdownSerializer } from '~/lib/prosemirror_markdown_serializer';
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
export default class Bold extends BaseBold {
get toMarkdown() {
return defaultMarkdownSerializer.marks.strong;
}
}
export default () => {
return {
name: 'bold',
schema: {
parseDOM: [
{
tag: 'strong',
},
],
toDOM: () => ['strong', 0],
},
toMarkdown: defaultMarkdownSerializer.marks.strong,
};
};

View file

@ -1,11 +1,12 @@
/* eslint-disable class-methods-use-this */
import { Code as BaseCode } from 'tiptap-extensions';
import { defaultMarkdownSerializer } from '~/lib/prosemirror_markdown_serializer';
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
export default class Code extends BaseCode {
get toMarkdown() {
return defaultMarkdownSerializer.marks.code;
}
}
export default () => ({
name: 'code',
schema: {
excludes: '_',
parseDOM: [{ tag: 'code' }],
toDOM: () => ['code', 0],
},
toMarkdown: defaultMarkdownSerializer.marks.code,
});

View file

@ -1,41 +1,29 @@
/* eslint-disable class-methods-use-this */
import { Mark } from 'tiptap';
// Transforms generated HTML back to GFM for Banzai::Filter::InlineDiffFilter
export default class InlineDiff extends Mark {
get name() {
return 'inline_diff';
}
get schema() {
return {
attrs: {
addition: {
default: true,
},
export default () => ({
name: 'inline_diff',
schema: {
attrs: {
addition: {
default: true,
},
parseDOM: [
{ tag: 'span.idiff.addition', attrs: { addition: true } },
{ tag: 'span.idiff.deletion', attrs: { addition: false } },
],
toDOM: (node) => [
'span',
{ class: `idiff left right ${node.attrs.addition ? 'addition' : 'deletion'}` },
0,
],
};
}
get toMarkdown() {
return {
mixable: true,
open(state, mark) {
return mark.attrs.addition ? '{+' : '{-';
},
close(state, mark) {
return mark.attrs.addition ? '+}' : '-}';
},
};
}
}
},
parseDOM: [
{ tag: 'span.idiff.addition', attrs: { addition: true } },
{ tag: 'span.idiff.deletion', attrs: { addition: false } },
],
toDOM: (node) => [
'span',
{ class: `idiff left right ${node.attrs.addition ? 'addition' : 'deletion'}` },
0,
],
},
toMarkdown: {
mixable: true,
open(_, mark) {
return mark.attrs.addition ? '{+' : '{-';
},
close(_, mark) {
return mark.attrs.addition ? '+}' : '-}';
},
},
});

View file

@ -1,46 +1,35 @@
/* eslint-disable class-methods-use-this */
import { escape } from 'lodash';
import { Mark } from 'tiptap';
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
export default class InlineHTML extends Mark {
get name() {
return 'inline_html';
}
get schema() {
return {
excludes: '',
attrs: {
tag: {},
title: { default: null },
export default () => ({
name: 'inline_html',
schema: {
excludes: '',
attrs: {
tag: {},
title: { default: null },
},
parseDOM: [
{
tag: 'sup, sub, kbd, q, samp, var',
getAttrs: (el) => ({ tag: el.nodeName.toLowerCase() }),
},
parseDOM: [
{
tag: 'sup, sub, kbd, q, samp, var',
getAttrs: (el) => ({ tag: el.nodeName.toLowerCase() }),
},
{
tag: 'abbr',
getAttrs: (el) => ({ tag: 'abbr', title: el.getAttribute('title') }),
},
],
toDOM: (node) => [node.attrs.tag, { title: node.attrs.title }, 0],
};
}
get toMarkdown() {
return {
mixable: true,
open(state, mark) {
return `<${mark.attrs.tag}${
mark.attrs.title ? ` title="${state.esc(escape(mark.attrs.title))}"` : ''
}>`;
{
tag: 'abbr',
getAttrs: (el) => ({ tag: 'abbr', title: el.getAttribute('title') }),
},
close(state, mark) {
return `</${mark.attrs.tag}>`;
},
};
}
}
],
toDOM: (node) => [node.attrs.tag, { title: node.attrs.title }, 0],
},
toMarkdown: {
mixable: true,
open(state, mark) {
return `<${mark.attrs.tag}${
mark.attrs.title ? ` title="${state.esc(escape(mark.attrs.title))}"` : ''
}>`;
},
close(_, mark) {
return `</${mark.attrs.tag}>`;
},
},
});

View file

@ -1,11 +1,11 @@
/* eslint-disable class-methods-use-this */
import { Italic as BaseItalic } from 'tiptap-extensions';
import { defaultMarkdownSerializer } from '~/lib/prosemirror_markdown_serializer';
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
export default class Italic extends BaseItalic {
get toMarkdown() {
return defaultMarkdownSerializer.marks.em;
}
}
export default () => ({
name: 'italic',
schema: {
parseDOM: [{ tag: 'em' }],
toDOM: () => ['em', 0],
},
toMarkdown: defaultMarkdownSerializer.marks.em,
});

View file

@ -1,21 +1,47 @@
/* eslint-disable class-methods-use-this */
import { Link as BaseLink } from 'tiptap-extensions';
import { defaultMarkdownSerializer } from '~/lib/prosemirror_markdown_serializer';
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
export default class Link extends BaseLink {
get toMarkdown() {
return {
mixable: true,
open(state, mark, parent, index) {
const open = defaultMarkdownSerializer.marks.link.open(state, mark, parent, index);
return open === '<' ? '' : open;
export default () => ({
name: 'link',
schema: {
attrs: {
href: {
default: null,
},
close(state, mark, parent, index) {
const close = defaultMarkdownSerializer.marks.link.close(state, mark, parent, index);
return close === '>' ? '' : close;
target: {
default: null,
},
};
}
}
},
inclusive: false,
parseDOM: [
{
tag: 'a[href]',
getAttrs: (dom) => ({
href: dom.getAttribute('href'),
target: dom.getAttribute('target'),
}),
},
],
toDOM: (node) => [
'a',
{
...node.attrs,
// eslint-disable-next-line @gitlab/require-i18n-strings
rel: 'noopener noreferrer nofollow',
target: node.attrs.target,
},
0,
],
},
toMarkdown: {
mixable: true,
open(state, mark, parent, index) {
const open = defaultMarkdownSerializer.marks.link.open(state, mark, parent, index);
return open === '<' ? '' : open;
},
close(state, mark, parent, index) {
const close = defaultMarkdownSerializer.marks.link.close(state, mark, parent, index);
return close === '>' ? '' : close;
},
},
});

View file

@ -1,42 +1,31 @@
/* eslint-disable class-methods-use-this */
import { Mark } from 'tiptap';
import { defaultMarkdownSerializer } from '~/lib/prosemirror_markdown_serializer';
import { HIGHER_PARSE_RULE_PRIORITY } from '../constants';
// Transforms generated HTML back to GFM for Banzai::Filter::MathFilter
export default class MathMark extends Mark {
get name() {
return 'math';
}
get schema() {
return {
parseDOM: [
// Matches HTML generated by Banzai::Filter::MathFilter
{
tag: 'code.code.math[data-math-style=inline]',
priority: HIGHER_PARSE_RULE_PRIORITY,
},
// Matches HTML after being transformed by app/assets/javascripts/behaviors/markdown/render_math.js
{
tag: 'span.katex',
contentElement: 'annotation[encoding="application/x-tex"]',
},
],
toDOM: () => ['code', { class: 'code math', 'data-math-style': 'inline' }, 0],
};
}
get toMarkdown() {
return {
escape: false,
open(state, mark, parent, index) {
return `$${defaultMarkdownSerializer.marks.code.open(state, mark, parent, index)}`;
export default () => ({
name: 'math',
schema: {
parseDOM: [
// Matches HTML generated by Banzai::Filter::MathFilter
{
tag: 'code.code.math[data-math-style=inline]',
priority: HIGHER_PARSE_RULE_PRIORITY,
},
close(state, mark, parent, index) {
return `${defaultMarkdownSerializer.marks.code.close(state, mark, parent, index)}$`;
// Matches HTML after being transformed by app/assets/javascripts/behaviors/markdown/render_math.js
{
tag: 'span.katex',
contentElement: 'annotation[encoding="application/x-tex"]',
},
};
}
}
],
toDOM: () => ['code', { class: 'code math', 'data-math-style': 'inline' }, 0],
},
toMarkdown: {
escape: false,
open(state, mark, parent, index) {
return `$${defaultMarkdownSerializer.marks.code.open(state, mark, parent, index)}`;
},
close(state, mark, parent, index) {
return `${defaultMarkdownSerializer.marks.code.close(state, mark, parent, index)}$`;
},
},
});

View file

@ -1,15 +1,18 @@
/* eslint-disable class-methods-use-this */
import { Strike as BaseStrike } from 'tiptap-extensions';
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
export default class Strike extends BaseStrike {
get toMarkdown() {
return {
open: '~~',
close: '~~',
mixable: true,
expelEnclosingWhitespace: true,
};
}
}
export default () => ({
name: 'strike',
schema: {
parseDOM: [
{
tag: 'del',
},
],
toDOM: () => ['s', 0],
},
toMarkdown: {
open: '~~',
close: '~~',
mixable: true,
expelEnclosingWhitespace: true,
},
});

View file

@ -1,9 +1,4 @@
import Playable from './playable';
import playable from './playable';
// Transforms generated HTML back to GFM for Banzai::Filter::AudioLinkFilter
export default class Audio extends Playable {
constructor() {
super();
this.mediaType = 'audio';
}
}
export default () => playable({ mediaType: 'audio' });

View file

@ -1,13 +1,19 @@
/* eslint-disable class-methods-use-this */
import { Blockquote as BaseBlockquote } from 'tiptap-extensions';
import { defaultMarkdownSerializer } from '~/lib/prosemirror_markdown_serializer';
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
export default class Blockquote extends BaseBlockquote {
export default () => ({
name: 'blockquote',
schema: {
content: 'block*',
group: 'block',
defining: true,
draggable: false,
parseDOM: [{ tag: 'blockquote' }],
toDOM: () => ['blockquote', 0],
},
toMarkdown(state, node) {
if (!node.childCount) return;
defaultMarkdownSerializer.nodes.blockquote(state, node);
}
}
},
});

View file

@ -1,11 +1,15 @@
/* eslint-disable class-methods-use-this */
import { BulletList as BaseBulletList } from 'tiptap-extensions';
import { defaultMarkdownSerializer } from '~/lib/prosemirror_markdown_serializer';
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
export default class BulletList extends BaseBulletList {
export default () => ({
name: 'bullet_list',
schema: {
content: 'list_item+',
group: 'block',
parseDOM: [{ tag: 'ul' }],
toDOM: () => ['ul', 0],
},
toMarkdown(state, node) {
defaultMarkdownSerializer.nodes.bullet_list(state, node);
}
}
},
});

View file

@ -1,7 +1,3 @@
/* eslint-disable class-methods-use-this */
import { CodeBlock as BaseCodeBlock } from 'tiptap-extensions';
const PLAINTEXT_LANG = 'plaintext';
// Transforms generated HTML back to GFM for:
@ -9,68 +5,67 @@ const PLAINTEXT_LANG = 'plaintext';
// - Banzai::Filter::MathFilter
// - Banzai::Filter::MermaidFilter
// - Banzai::Filter::SuggestionFilter
export default class CodeBlock extends BaseCodeBlock {
get schema() {
return {
content: 'text*',
marks: '',
group: 'block',
code: true,
defining: true,
attrs: {
lang: { default: PLAINTEXT_LANG },
},
parseDOM: [
// Matches HTML generated by Banzai::Filter::SyntaxHighlightFilter, Banzai::Filter::MathFilter, Banzai::Filter::MermaidFilter, or Banzai::Filter::SuggestionFilter
{
tag: 'pre.code.highlight',
preserveWhitespace: 'full',
getAttrs: (el) => {
const lang = el.getAttribute('lang');
if (!lang || lang === '') return {};
export default () => ({
name: 'code_block',
schema: {
content: 'text*',
marks: '',
group: 'block',
code: true,
defining: true,
attrs: {
lang: { default: PLAINTEXT_LANG },
},
parseDOM: [
// Matches HTML generated by Banzai::Filter::SyntaxHighlightFilter, Banzai::Filter::MathFilter, Banzai::Filter::MermaidFilter, or Banzai::Filter::SuggestionFilter
{
tag: 'pre.code.highlight',
preserveWhitespace: 'full',
getAttrs: (el) => {
const lang = el.getAttribute('lang');
if (!lang || lang === '') return {};
return { lang };
},
return { lang };
},
// Matches HTML generated by Banzai::Filter::MathFilter,
// after being transformed by app/assets/javascripts/behaviors/markdown/render_math.js
{
tag: 'span.katex-display',
preserveWhitespace: 'full',
contentElement: 'annotation[encoding="application/x-tex"]',
attrs: { lang: 'math' },
},
// Matches HTML generated by Banzai::Filter::MermaidFilter,
// after being transformed by app/assets/javascripts/behaviors/markdown/render_mermaid.js
{
tag: 'svg.mermaid',
preserveWhitespace: 'full',
contentElement: 'text.source',
attrs: { lang: 'mermaid' },
},
// Matches HTML generated by Banzai::Filter::SuggestionFilter,
// after being transformed by app/assets/javascripts/vue_shared/components/markdown/suggestions.vue
{
tag: '.md-suggestion',
skip: true,
},
{
tag: '.md-suggestion-header',
ignore: true,
},
{
tag: '.md-suggestion-diff',
preserveWhitespace: 'full',
getContent: (el, schema) =>
[...el.querySelectorAll('.line_content.new span')].map((span) =>
schema.text(span.innerText),
),
attrs: { lang: 'suggestion' },
},
],
toDOM: (node) => ['pre', { class: 'code highlight', lang: node.attrs.lang }, ['code', 0]],
};
}
},
// Matches HTML generated by Banzai::Filter::MathFilter,
// after being transformed by app/assets/javascripts/behaviors/markdown/render_math.js
{
tag: 'span.katex-display',
preserveWhitespace: 'full',
contentElement: 'annotation[encoding="application/x-tex"]',
attrs: { lang: 'math' },
},
// Matches HTML generated by Banzai::Filter::MermaidFilter,
// after being transformed by app/assets/javascripts/behaviors/markdown/render_mermaid.js
{
tag: 'svg.mermaid',
preserveWhitespace: 'full',
contentElement: 'text.source',
attrs: { lang: 'mermaid' },
},
// Matches HTML generated by Banzai::Filter::SuggestionFilter,
// after being transformed by app/assets/javascripts/vue_shared/components/markdown/suggestions.vue
{
tag: '.md-suggestion',
skip: true,
},
{
tag: '.md-suggestion-header',
ignore: true,
},
{
tag: '.md-suggestion-diff',
preserveWhitespace: 'full',
getContent: (el, schema) =>
[...el.querySelectorAll('.line_content.new span')].map((span) =>
schema.text(span.innerText),
),
attrs: { lang: 'suggestion' },
},
],
toDOM: (node) => ['pre', { class: 'code highlight', lang: node.attrs.lang }, ['code', 0]],
},
toMarkdown(state, node) {
if (!node.childCount) return;
@ -95,5 +90,5 @@ export default class CodeBlock extends BaseCodeBlock {
state.write('```');
state.closeBlock(node);
}
}
},
});

View file

@ -1,22 +1,14 @@
/* eslint-disable class-methods-use-this */
import { Node } from 'tiptap';
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
export default class DescriptionDetails extends Node {
get name() {
return 'description_details';
}
export default () => ({
name: 'description_details',
get schema() {
return {
content: 'text*',
marks: '',
defining: true,
parseDOM: [{ tag: 'dd' }],
toDOM: () => ['dd', 0],
};
}
schema: {
content: 'text*',
marks: '',
defining: true,
parseDOM: [{ tag: 'dd' }],
toDOM: () => ['dd', 0],
},
toMarkdown(state, node) {
state.flushClose(1);
@ -24,5 +16,5 @@ export default class DescriptionDetails extends Node {
state.text(node.textContent, false);
state.write('</dd>');
state.closeBlock(node);
}
}
},
});

View file

@ -1,21 +1,12 @@
/* eslint-disable class-methods-use-this */
import { Node } from 'tiptap';
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
export default class DescriptionList extends Node {
get name() {
return 'description_list';
}
get schema() {
return {
content: '(description_term+ description_details+)+',
group: 'block',
parseDOM: [{ tag: 'dl' }],
toDOM: () => ['dl', 0],
};
}
export default () => ({
name: 'description_list',
schema: {
content: '(description_term+ description_details+)+',
group: 'block',
parseDOM: [{ tag: 'dl' }],
toDOM: () => ['dl', 0],
},
toMarkdown(state, node) {
state.write('<dl>\n');
@ -24,5 +15,5 @@ export default class DescriptionList extends Node {
state.ensureNewLine();
state.write('</dl>');
state.closeBlock(node);
}
}
},
});

View file

@ -1,28 +1,18 @@
/* eslint-disable class-methods-use-this */
import { Node } from 'tiptap';
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
export default class DescriptionTerm extends Node {
get name() {
return 'description_term';
}
get schema() {
return {
content: 'text*',
marks: '',
defining: true,
parseDOM: [{ tag: 'dt' }],
toDOM: () => ['dt', 0],
};
}
export default () => ({
name: 'description_term',
schema: {
content: 'text*',
marks: '',
defining: true,
parseDOM: [{ tag: 'dt' }],
toDOM: () => ['dt', 0],
},
toMarkdown(state, node) {
state.flushClose(state.closed && state.closed.type === node.type ? 1 : 2);
state.write('<dt>');
state.text(node.textContent, false);
state.write('</dt>');
state.closeBlock(node);
}
}
},
});

View file

@ -1,22 +1,12 @@
/* eslint-disable class-methods-use-this */
import { Node } from 'tiptap';
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
export default class Details extends Node {
get name() {
return 'details';
}
get schema() {
return {
content: 'summary block*',
group: 'block',
parseDOM: [{ tag: 'details' }],
toDOM: () => ['details', { open: true, onclick: 'return false', tabindex: '-1' }, 0],
};
}
export default () => ({
name: 'details',
schema: {
content: 'summary block*',
group: 'block',
parseDOM: [{ tag: 'details' }],
toDOM: () => ['details', { open: true, onclick: 'return false', tabindex: '-1' }, 0],
},
toMarkdown(state, node) {
state.write('<details>\n');
state.renderContent(node);
@ -24,5 +14,5 @@ export default class Details extends Node {
state.ensureNewLine();
state.write('</details>');
state.closeBlock(node);
}
}
},
});

View file

@ -1,15 +1,6 @@
/* eslint-disable class-methods-use-this */
import { Node } from 'tiptap';
export default class Doc extends Node {
get name() {
return 'doc';
}
get schema() {
return {
content: 'block+',
};
}
}
export default () => ({
name: 'doc',
schema: {
content: 'block+',
},
});

View file

@ -1,53 +1,43 @@
/* eslint-disable class-methods-use-this */
import { Node } from 'tiptap';
// Transforms generated HTML back to GFM for Banzai::Filter::EmojiFilter
export default class Emoji extends Node {
get name() {
return 'emoji';
}
get schema() {
return {
inline: true,
group: 'inline',
attrs: {
name: {},
title: {},
moji: {},
export default () => ({
name: 'emoji',
schema: {
inline: true,
group: 'inline',
attrs: {
name: {},
title: {},
moji: {},
},
parseDOM: [
{
tag: 'gl-emoji',
getAttrs: (el) => ({
name: el.dataset.name,
title: el.getAttribute('title'),
moji: el.textContent,
}),
},
parseDOM: [
{
tag: 'gl-emoji',
getAttrs: (el) => ({
name: el.dataset.name,
title: el.getAttribute('title'),
moji: el.textContent,
}),
},
{
tag: 'img.emoji',
getAttrs: (el) => {
const name = el.getAttribute('title').replace(/^:|:$/g, '');
{
tag: 'img.emoji',
getAttrs: (el) => {
const name = el.getAttribute('title').replace(/^:|:$/g, '');
return {
name,
title: name,
moji: name,
};
},
return {
name,
title: name,
moji: name,
};
},
],
toDOM: (node) => [
'gl-emoji',
{ 'data-name': node.attrs.name, title: node.attrs.title },
node.attrs.moji,
],
};
}
},
],
toDOM: (node) => [
'gl-emoji',
{ 'data-name': node.attrs.name, title: node.attrs.title },
node.attrs.moji,
],
},
toMarkdown(state, node) {
state.write(`:${node.attrs.name}:`);
}
}
},
});

View file

@ -1,10 +1,14 @@
/* eslint-disable class-methods-use-this */
import { HardBreak as BaseHardBreak } from 'tiptap-extensions';
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
export default class HardBreak extends BaseHardBreak {
export default () => ({
name: 'hard_break',
schema: {
inline: true,
group: 'inline',
selectable: false,
parseDOM: [{ tag: 'br' }],
toDOM: () => ['br'],
},
toMarkdown(state) {
if (!state.atBlank()) state.write(' \n');
}
}
},
});

View file

@ -1,13 +1,27 @@
/* eslint-disable class-methods-use-this */
import { Heading as BaseHeading } from 'tiptap-extensions';
import { defaultMarkdownSerializer } from '~/lib/prosemirror_markdown_serializer';
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
export default class Heading extends BaseHeading {
export default ({ levels = [1, 2, 3, 4, 5, 6] } = {}) => ({
name: 'heading',
schema: {
attrs: {
level: {
default: 1,
},
},
content: 'inline*',
group: 'block',
defining: true,
draggable: false,
parseDOM: levels.map((level) => ({
tag: `h${level}`,
attrs: { level },
})),
toDOM: (node) => [`h${node.attrs.level}`, 0],
},
toMarkdown(state, node) {
if (!node.childCount) return;
defaultMarkdownSerializer.nodes.heading(state, node);
}
}
},
});

View file

@ -1,11 +1,14 @@
/* eslint-disable class-methods-use-this */
import { HorizontalRule as BaseHorizontalRule } from 'tiptap-extensions';
import { defaultMarkdownSerializer } from '~/lib/prosemirror_markdown_serializer';
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
export default class HorizontalRule extends BaseHorizontalRule {
export default () => ({
name: 'horizontal_rule',
schema: {
group: 'block',
parseDOM: [{ tag: 'hr' }],
toDOM: () => ['hr'],
},
toMarkdown(state, node) {
defaultMarkdownSerializer.nodes.horizontal_rule(state, node);
}
}
},
});

View file

@ -1,53 +1,48 @@
/* eslint-disable class-methods-use-this */
import { Image as BaseImage } from 'tiptap-extensions';
import { placeholderImage } from '~/lazy_loader';
import { defaultMarkdownSerializer } from '~/lib/prosemirror_markdown_serializer';
import { HIGHER_PARSE_RULE_PRIORITY } from '../constants';
export default class Image extends BaseImage {
get schema() {
return {
attrs: {
src: {},
alt: {
default: null,
},
title: {
default: null,
export default () => ({
name: 'image',
schema: {
attrs: {
src: {},
alt: {
default: null,
},
title: {
default: null,
},
},
group: 'inline',
inline: true,
draggable: true,
parseDOM: [
// Matches HTML generated by Banzai::Filter::ImageLinkFilter
{
tag: 'a.no-attachment-icon',
priority: HIGHER_PARSE_RULE_PRIORITY,
skip: true,
},
// Matches HTML generated by Banzai::Filter::ImageLazyLoadFilter
{
tag: 'img[src]:not(.emoji)',
getAttrs: (el) => {
const imageSrc = el.src;
const imageUrl =
imageSrc && imageSrc !== placeholderImage ? imageSrc : el.dataset.src || '';
return {
src: imageUrl,
title: el.getAttribute('title'),
alt: el.getAttribute('alt'),
};
},
},
group: 'inline',
inline: true,
draggable: true,
parseDOM: [
// Matches HTML generated by Banzai::Filter::ImageLinkFilter
{
tag: 'a.no-attachment-icon',
priority: HIGHER_PARSE_RULE_PRIORITY,
skip: true,
},
// Matches HTML generated by Banzai::Filter::ImageLazyLoadFilter
{
tag: 'img[src]:not(.emoji)',
getAttrs: (el) => {
const imageSrc = el.src;
const imageUrl =
imageSrc && imageSrc !== placeholderImage ? imageSrc : el.dataset.src || '';
return {
src: imageUrl,
title: el.getAttribute('title'),
alt: el.getAttribute('alt'),
};
},
},
],
toDOM: (node) => ['img', node.attrs],
};
}
],
toDOM: (node) => ['img', node.attrs],
},
toMarkdown(state, node) {
defaultMarkdownSerializer.nodes.image(state, node);
}
}
},
});

View file

@ -1,11 +1,16 @@
/* eslint-disable class-methods-use-this */
import { ListItem as BaseListItem } from 'tiptap-extensions';
import { defaultMarkdownSerializer } from '~/lib/prosemirror_markdown_serializer';
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
export default class ListItem extends BaseListItem {
export default () => ({
name: 'list_item',
schema: {
content: 'paragraph block*',
defining: true,
draggable: false,
parseDOM: [{ tag: 'li' }],
toDOM: () => ['li', 0],
},
toMarkdown(state, node) {
defaultMarkdownSerializer.nodes.list_item(state, node);
}
}
},
});

View file

@ -1,10 +1,25 @@
/* eslint-disable class-methods-use-this */
import { OrderedList as BaseOrderedList } from 'tiptap-extensions';
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
export default class OrderedList extends BaseOrderedList {
export default () => ({
name: 'ordered_list',
schema: {
attrs: {
order: {
default: 1,
},
},
content: 'list_item+',
group: 'block',
parseDOM: [
{
tag: 'ol',
getAttrs: (dom) => ({
order: dom.hasAttribute('start') ? dom.getAttribute('start') + 1 : 1,
}),
},
],
toDOM: (node) => (node.attrs.order === 1 ? ['ol', 0] : ['ol', { start: node.attrs.order }, 0]),
},
toMarkdown(state, node) {
state.renderList(node, ' ', () => '1. ');
}
}
},
});

View file

@ -1,29 +1,21 @@
/* eslint-disable class-methods-use-this */
import { Node } from 'tiptap';
import { HIGHER_PARSE_RULE_PRIORITY } from '../constants';
// Transforms generated HTML back to GFM for Banzai::Filter::TaskListFilter
export default class OrderedTaskList extends Node {
get name() {
return 'ordered_task_list';
}
get schema() {
return {
group: 'block',
content: '(task_list_item|list_item)+',
parseDOM: [
{
priority: HIGHER_PARSE_RULE_PRIORITY,
tag: 'ol.task-list',
},
],
toDOM: () => ['ol', { class: 'task-list' }, 0],
};
}
export default () => ({
name: 'ordered_task_list',
schema: {
group: 'block',
content: '(task_list_item|list_item)+',
parseDOM: [
{
priority: HIGHER_PARSE_RULE_PRIORITY,
tag: 'ol.task-list',
},
],
toDOM: () => ['ol', { class: 'task-list' }, 0],
},
toMarkdown(state, node) {
state.renderList(node, ' ', () => '1. ');
}
}
},
});

View file

@ -1,24 +1,15 @@
/* eslint-disable class-methods-use-this */
import { Node } from 'tiptap';
import { defaultMarkdownSerializer } from '~/lib/prosemirror_markdown_serializer';
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
export default class Paragraph extends Node {
get name() {
return 'paragraph';
}
get schema() {
return {
content: 'inline*',
group: 'block',
parseDOM: [{ tag: 'p' }],
toDOM: () => ['p', 0],
};
}
export default () => ({
name: 'paragraph',
schema: {
content: 'inline*',
group: 'block',
parseDOM: [{ tag: 'p' }],
toDOM: () => ['p', 0],
},
toMarkdown(state, node) {
defaultMarkdownSerializer.nodes.paragraph(state, node);
}
}
},
});

View file

@ -1,7 +1,3 @@
/* eslint-disable class-methods-use-this */
/* eslint-disable @gitlab/require-i18n-strings */
import { Node } from 'tiptap';
import { defaultMarkdownSerializer } from '~/lib/prosemirror_markdown_serializer';
/**
@ -10,62 +6,51 @@ import { defaultMarkdownSerializer } from '~/lib/prosemirror_markdown_serializer
* the `mediaType` property in their constructors.
* @abstract
*/
export default class Playable extends Node {
constructor() {
super();
this.mediaType = '';
this.extraElementAttrs = {};
}
get name() {
return this.mediaType;
}
get schema() {
const attrs = {
src: {},
alt: {
default: null,
},
};
const parseDOM = [
export default ({ mediaType, extraElementAttrs = {} }) => {
const attrs = {
src: {},
alt: {
default: null,
},
};
const parseDOM = [
{
// eslint-disable-next-line @gitlab/require-i18n-strings
tag: `.${mediaType}-container`,
getAttrs: (el) => ({
src: el.querySelector(mediaType).src,
alt: el.querySelector(mediaType).dataset.title,
}),
},
];
const toDOM = (node) => [
'span',
{ class: `media-container ${mediaType}-container` },
[
mediaType,
{
tag: `.${this.mediaType}-container`,
getAttrs: (el) => ({
src: el.querySelector(this.mediaType).src,
alt: el.querySelector(this.mediaType).dataset.title,
}),
src: node.attrs.src,
controls: true,
'data-setup': '{}',
'data-title': node.attrs.alt,
...extraElementAttrs,
},
];
],
['a', { href: node.attrs.src }, node.attrs.alt],
];
const toDOM = (node) => [
'span',
{ class: `media-container ${this.mediaType}-container` },
[
this.mediaType,
{
src: node.attrs.src,
controls: true,
'data-setup': '{}',
'data-title': node.attrs.alt,
...this.extraElementAttrs,
},
],
['a', { href: node.attrs.src }, node.attrs.alt],
];
return {
return {
name: mediaType,
schema: {
attrs,
group: 'inline',
inline: true,
draggable: true,
parseDOM,
toDOM,
};
}
toMarkdown(state, node) {
defaultMarkdownSerializer.nodes.image(state, node);
}
}
},
toMarkdown(state, node) {
defaultMarkdownSerializer.nodes.image(state, node);
},
};
};

View file

@ -1,53 +1,44 @@
/* eslint-disable class-methods-use-this */
import { Node } from 'tiptap';
import { HIGHER_PARSE_RULE_PRIORITY } from '../constants';
// Transforms generated HTML back to GFM for Banzai::Filter::ReferenceFilter and subclasses
export default class Reference extends Node {
get name() {
return 'reference';
}
get schema() {
return {
inline: true,
group: 'inline',
atom: true,
attrs: {
className: {},
referenceType: {},
originalText: { default: null },
href: {},
text: {},
export default () => ({
name: 'reference',
schema: {
inline: true,
group: 'inline',
atom: true,
attrs: {
className: {},
referenceType: {},
originalText: { default: null },
href: {},
text: {},
},
parseDOM: [
{
tag: 'a.gfm:not([data-link=true])',
priority: HIGHER_PARSE_RULE_PRIORITY,
getAttrs: (el) => ({
className: el.className,
referenceType: el.dataset.referenceType,
originalText: el.dataset.original,
href: el.getAttribute('href'),
text: el.textContent,
}),
},
parseDOM: [
{
tag: 'a.gfm:not([data-link=true])',
priority: HIGHER_PARSE_RULE_PRIORITY,
getAttrs: (el) => ({
className: el.className,
referenceType: el.dataset.referenceType,
originalText: el.dataset.original,
href: el.getAttribute('href'),
text: el.textContent,
}),
},
],
toDOM: (node) => [
'a',
{
class: node.attrs.className,
href: node.attrs.href,
'data-reference-type': node.attrs.referenceType,
'data-original': node.attrs.originalText,
},
node.attrs.text,
],
};
}
],
toDOM: (node) => [
'a',
{
class: node.attrs.className,
href: node.attrs.href,
'data-reference-type': node.attrs.referenceType,
'data-original': node.attrs.originalText,
},
node.attrs.text,
],
},
toMarkdown(state, node) {
state.write(node.attrs.originalText || node.attrs.text);
}
}
},
});

View file

@ -1,27 +1,17 @@
/* eslint-disable class-methods-use-this */
import { Node } from 'tiptap';
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
export default class Summary extends Node {
get name() {
return 'summary';
}
get schema() {
return {
content: 'text*',
marks: '',
defining: true,
parseDOM: [{ tag: 'summary' }],
toDOM: () => ['summary', 0],
};
}
export default () => ({
name: 'summary',
schema: {
content: 'text*',
marks: '',
defining: true,
parseDOM: [{ tag: 'summary' }],
toDOM: () => ['summary', 0],
},
toMarkdown(state, node) {
state.write('<summary>');
state.text(node.textContent, false);
state.write('</summary>');
state.closeBlock(node);
}
}
},
});

View file

@ -1,25 +1,15 @@
/* eslint-disable class-methods-use-this */
import { Node } from 'tiptap';
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
export default class Table extends Node {
get name() {
return 'table';
}
get schema() {
return {
content: 'table_head table_body',
group: 'block',
isolating: true,
parseDOM: [{ tag: 'table' }],
toDOM: () => ['table', 0],
};
}
export default () => ({
name: 'table',
schema: {
content: 'table_head table_body',
group: 'block',
isolating: true,
parseDOM: [{ tag: 'table' }],
toDOM: () => ['table', 0],
},
toMarkdown(state, node) {
state.renderContent(node);
state.closeBlock(node);
}
}
},
});

View file

@ -1,24 +1,14 @@
/* eslint-disable class-methods-use-this */
import { Node } from 'tiptap';
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
export default class TableBody extends Node {
get name() {
return 'table_body';
}
get schema() {
return {
content: 'table_row+',
parseDOM: [{ tag: 'tbody' }],
toDOM: () => ['tbody', 0],
};
}
toMarkdown(state, node) {
export default () => ({
name: 'table_body',
schema: {
content: 'table_row+',
parseDOM: [{ tag: 'tbody' }],
toDOM: () => ['tbody', 0],
},
toMarkdown: (state, node) => {
state.flushClose(1);
state.renderContent(node);
state.closeBlock(node);
}
}
},
});

View file

@ -1,35 +1,25 @@
/* eslint-disable class-methods-use-this */
import { Node } from 'tiptap';
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
export default class TableCell extends Node {
get name() {
return 'table_cell';
}
get schema() {
return {
attrs: {
header: { default: false },
align: { default: null },
export default () => ({
name: 'table_cell',
schema: {
attrs: {
header: { default: false },
align: { default: null },
},
content: 'inline*',
isolating: true,
parseDOM: [
{
tag: 'td, th',
getAttrs: (el) => ({
header: el.tagName === 'TH',
align: el.getAttribute('align') || el.style.textAlign,
}),
},
content: 'inline*',
isolating: true,
parseDOM: [
{
tag: 'td, th',
getAttrs: (el) => ({
header: el.tagName === 'TH',
align: el.getAttribute('align') || el.style.textAlign,
}),
},
],
toDOM: (node) => [node.attrs.header ? 'th' : 'td', { align: node.attrs.align }, 0],
};
}
toMarkdown(state, node) {
],
toDOM: (node) => [node.attrs.header ? 'th' : 'td', { align: node.attrs.align }, 0],
},
toMarkdown: (state, node) => {
state.renderInline(node);
}
}
},
});

View file

@ -1,24 +1,14 @@
/* eslint-disable class-methods-use-this */
import { Node } from 'tiptap';
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
export default class TableHead extends Node {
get name() {
return 'table_head';
}
get schema() {
return {
content: 'table_header_row',
parseDOM: [{ tag: 'thead' }],
toDOM: () => ['thead', 0],
};
}
toMarkdown(state, node) {
export default () => ({
name: 'table_head',
schema: {
content: 'table_header_row',
parseDOM: [{ tag: 'thead' }],
toDOM: () => ['thead', 0],
},
toMarkdown: (state, node) => {
state.flushClose(1);
state.renderContent(node);
state.closeBlock(node);
}
}
},
});

View file

@ -1,31 +1,23 @@
/* eslint-disable class-methods-use-this */
import { HIGHER_PARSE_RULE_PRIORITY } from '../constants';
import TableRow from './table_row';
const CENTER_ALIGN = 'center';
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
export default class TableHeaderRow extends TableRow {
get name() {
return 'table_header_row';
}
get schema() {
return {
content: 'table_cell+',
parseDOM: [
{
tag: 'thead tr',
priority: HIGHER_PARSE_RULE_PRIORITY,
},
],
toDOM: () => ['tr', 0],
};
}
toMarkdown(state, node) {
const cellWidths = super.toMarkdown(state, node);
export default () => ({
name: 'table_header_row',
schema: {
content: 'table_cell+',
parseDOM: [
{
tag: 'thead tr',
priority: HIGHER_PARSE_RULE_PRIORITY,
},
],
toDOM: () => ['tr', 0],
},
toMarkdown: (state, node) => {
const cellWidths = TableRow().toMarkdown(state, node);
state.flushClose(1);
@ -40,5 +32,5 @@ export default class TableHeaderRow extends TableRow {
state.write('|');
state.closeBlock(node);
}
}
},
});

View file

@ -1,35 +1,26 @@
/* eslint-disable class-methods-use-this */
import { Node } from 'tiptap';
import { __ } from '~/locale';
import { HIGHER_PARSE_RULE_PRIORITY } from '../constants';
// Transforms generated HTML back to GFM for Banzai::Filter::TableOfContentsFilter
export default class TableOfContents extends Node {
get name() {
return 'table_of_contents';
}
get schema() {
return {
group: 'block',
atom: true,
parseDOM: [
{
tag: 'ul.section-nav',
priority: HIGHER_PARSE_RULE_PRIORITY,
},
{
tag: 'p.table-of-contents',
priority: HIGHER_PARSE_RULE_PRIORITY,
},
],
toDOM: () => ['p', { class: 'table-of-contents' }, __('Table of Contents')],
};
}
toMarkdown(state, node) {
export default () => ({
name: 'table_of_contents',
schema: {
group: 'block',
atom: true,
parseDOM: [
{
tag: 'ul.section-nav',
priority: HIGHER_PARSE_RULE_PRIORITY,
},
{
tag: 'p.table-of-contents',
priority: HIGHER_PARSE_RULE_PRIORITY,
},
],
toDOM: () => ['p', { class: 'table-of-contents' }, __('Table of Contents')],
},
toMarkdown: (state, node) => {
state.write('[[_TOC_]]');
state.closeBlock(node);
}
}
},
});

View file

@ -1,22 +1,12 @@
/* eslint-disable class-methods-use-this */
import { Node } from 'tiptap';
// Transforms generated HTML back to GFM for Banzai::Filter::MarkdownFilter
export default class TableRow extends Node {
get name() {
return 'table_row';
}
get schema() {
return {
content: 'table_cell+',
parseDOM: [{ tag: 'tr' }],
toDOM: () => ['tr', 0],
};
}
toMarkdown(state, node) {
export default () => ({
name: 'table_row',
schema: {
content: 'table_cell+',
parseDOM: [{ tag: 'tr' }],
toDOM: () => ['tr', 0],
},
toMarkdown: (state, node) => {
const cellWidths = [];
state.flushClose(1);
@ -34,5 +24,5 @@ export default class TableRow extends Node {
state.closeBlock(node);
return cellWidths;
}
}
},
});

View file

@ -1,29 +1,20 @@
/* eslint-disable class-methods-use-this */
import { Node } from 'tiptap';
import { HIGHER_PARSE_RULE_PRIORITY } from '../constants';
// Transforms generated HTML back to GFM for Banzai::Filter::TaskListFilter
export default class TaskList extends Node {
get name() {
return 'task_list';
}
get schema() {
return {
group: 'block',
content: '(task_list_item|list_item)+',
parseDOM: [
{
priority: HIGHER_PARSE_RULE_PRIORITY,
tag: 'ul.task-list',
},
],
toDOM: () => ['ul', { class: 'task-list' }, 0],
};
}
export default () => ({
name: 'task_list',
schema: {
group: 'block',
content: '(task_list_item|list_item)+',
parseDOM: [
{
priority: HIGHER_PARSE_RULE_PRIORITY,
tag: 'ul.task-list',
},
],
toDOM: () => ['ul', { class: 'task-list' }, 0],
},
toMarkdown(state, node) {
state.renderList(node, ' ', () => '* ');
}
}
},
});

View file

@ -1,50 +1,38 @@
/* eslint-disable class-methods-use-this */
import { Node } from 'tiptap';
import { HIGHER_PARSE_RULE_PRIORITY } from '../constants';
// Transforms generated HTML back to GFM for Banzai::Filter::TaskListFilter
export default class TaskListItem extends Node {
get name() {
return 'task_list_item';
}
get schema() {
return {
attrs: {
done: {
default: false,
export default () => ({
name: 'task_list_item',
schema: {
attrs: {
done: {
default: false,
},
},
defining: true,
draggable: false,
content: 'paragraph block*',
parseDOM: [
{
priority: HIGHER_PARSE_RULE_PRIORITY,
tag: 'li.task-list-item',
getAttrs: (el) => {
const checkbox = el.querySelector('input[type=checkbox].task-list-item-checkbox');
return { done: checkbox && checkbox.checked };
},
},
defining: true,
draggable: false,
content: 'paragraph block*',
parseDOM: [
{
priority: HIGHER_PARSE_RULE_PRIORITY,
tag: 'li.task-list-item',
getAttrs: (el) => {
const checkbox = el.querySelector('input[type=checkbox].task-list-item-checkbox');
return { done: checkbox && checkbox.checked };
},
},
],
toDOM(node) {
return [
'li',
{ class: 'task-list-item' },
[
'input',
{ type: 'checkbox', class: 'task-list-item-checkbox', checked: node.attrs.done },
],
['div', { class: 'todo-content' }, 0],
];
},
};
}
],
toDOM(node) {
return [
'li',
{ class: 'task-list-item' },
['input', { type: 'checkbox', class: 'task-list-item-checkbox', checked: node.attrs.done }],
['div', { class: 'todo-content' }, 0],
];
},
},
toMarkdown(state, node) {
state.write(`[${node.attrs.done ? 'x' : ' '}] `);
state.renderContent(node);
}
}
},
});

View file

@ -1,20 +1,11 @@
/* eslint-disable class-methods-use-this */
import { Node } from 'tiptap';
import { defaultMarkdownSerializer } from '~/lib/prosemirror_markdown_serializer';
export default class Text extends Node {
get name() {
return 'text';
}
get schema() {
return {
group: 'inline',
};
}
export default () => ({
name: 'text',
schema: {
group: 'inline',
},
toMarkdown(state, node) {
defaultMarkdownSerializer.nodes.text(state, node);
}
}
},
});

View file

@ -1,10 +1,4 @@
import Playable from './playable';
import playable from './playable';
// Transforms generated HTML back to GFM for Banzai::Filter::VideoLinkFilter
export default class Video extends Playable {
constructor() {
super();
this.mediaType = 'video';
this.extraElementAttrs = { width: '400' };
}
}
export default () => playable({ mediaType: 'video', extraElementAttrs: { width: '400' } });

View file

@ -1,24 +1,20 @@
import { Schema } from 'prosemirror-model';
import editorExtensions from './editor_extensions';
const nodes = editorExtensions
.filter((extension) => extension.type === 'node')
.reduce(
(ns, { name, schema }) => ({
...ns,
[name]: schema,
}),
{},
);
const nodes = editorExtensions.nodes.reduce(
(ns, { name, schema }) => ({
...ns,
[name]: schema,
}),
{},
);
const marks = editorExtensions
.filter((extension) => extension.type === 'mark')
.reduce(
(ms, { name, schema }) => ({
...ms,
[name]: schema,
}),
{},
);
const marks = editorExtensions.marks.reduce(
(ms, { name, schema }) => ({
...ms,
[name]: schema,
}),
{},
);
export default new Schema({ nodes, marks });

View file

@ -1,24 +1,20 @@
import { MarkdownSerializer } from '~/lib/prosemirror_markdown_serializer';
import editorExtensions from './editor_extensions';
const nodes = editorExtensions
.filter((extension) => extension.type === 'node')
.reduce(
(ns, { name, toMarkdown }) => ({
...ns,
[name]: toMarkdown,
}),
{},
);
const nodes = editorExtensions.nodes.reduce(
(ns, { name, toMarkdown }) => ({
...ns,
[name]: toMarkdown,
}),
{},
);
const marks = editorExtensions
.filter((extension) => extension.type === 'mark')
.reduce(
(ms, { name, toMarkdown }) => ({
...ms,
[name]: toMarkdown,
}),
{},
);
const marks = editorExtensions.marks.reduce(
(ms, { name, toMarkdown }) => ({
...ms,
[name]: toMarkdown,
}),
{},
);
export default new MarkdownSerializer(nodes, marks);

View file

@ -129,7 +129,7 @@ module DropdownsHelper
end
def dropdown_loading
spinner = loading_icon(container: true, size: "md", css_class: "gl-mt-7")
spinner = gl_loading_icon(size: "md", css_class: "gl-mt-7")
content_tag(:div, spinner, class: "dropdown-loading")
end
end

View file

@ -49,13 +49,39 @@ module IconsHelper
end
end
def loading_icon(container: false, color: 'orange', size: 'sm', css_class: nil)
css_classes = ['gl-spinner', "gl-spinner-#{color}", "gl-spinner-#{size}"]
css_classes << "#{css_class}" unless css_class.blank?
# Creates a GitLab UI loading icon/spinner.
#
# Examples:
# # Default
# gl_loading_icon
#
# # Sizes
# gl_loading_icon(size: 'md')
# gl_loading_icon(size: 'lg')
# gl_loading_icon(size: 'xl')
#
# # Colors
# gl_loading_icon(color: 'light')
#
# # Block/Inline
# gl_loading_icon(inline: true)
#
# # Custom classes
# gl_loading_icon(css_class: "foo-bar")
#
# See also https://gitlab-org.gitlab.io/gitlab-ui/?path=/story/base-loading-icon--default
def gl_loading_icon(inline: false, color: 'dark', size: 'sm', css_class: nil)
spinner = content_tag(:span, "", {
class: %[gl-spinner gl-spinner-#{color} gl-spinner-#{size} gl-vertical-align-text-bottom!],
aria: { label: _('Loading') }
})
spinner = content_tag(:span, "", { class: css_classes.join(' '), aria: { label: _('Loading') } })
container == true ? content_tag(:div, spinner, { class: 'gl-spinner-container' }) : spinner
container_classes = ['gl-spinner-container']
container_classes << css_class unless css_class.blank?
content_tag(inline ? :span : :div, spinner, {
class: container_classes,
role: 'status'
})
end
def external_snippet_icon(name)

View file

@ -12,8 +12,7 @@
.info-well.gl-display-none.gl-sm-display-flex.project-last-commit.gl-flex-direction-column
#js-last-commit.gl-m-auto
.gl-spinner-container.m-auto
= loading_icon(size: 'md', color: 'dark', css_class: 'align-text-bottom')
= gl_loading_icon(size: 'md')
#js-code-owners
- if is_project_overview

View file

@ -21,8 +21,7 @@
project_path: @project.full_path,
target_branch: project.empty_repo? ? ref : @ref,
original_branch: @ref } }
.gl-spinner-container
= loading_icon(size: 'md')
= gl_loading_icon(size: 'md')
- else
%article.file-holder
= render 'projects/blob/header', blob: blob

View file

@ -1,4 +1,4 @@
= loading_icon(css_class: "gl-vertical-align-text-bottom mr-1")
= gl_loading_icon(inline: true, css_class: "gl-mr-2!")
= s_('Pipelines|Validating GitLab CI configuration…')
= link_to _('Learn more'), help_page_path('ci/yaml/index')

View file

@ -1,2 +1 @@
.text-center.gl-mt-4.gl-mb-3
= loading_icon(size: "md", css_class: "qa-spinner")
= gl_loading_icon(size: "md", css_class: "qa-spinner gl-my-4")

View file

@ -1,2 +1,2 @@
= loading_icon(css_class: "gl-vertical-align-text-bottom")
= gl_loading_icon(inline: true)
= _("Analyzing file…")

View file

@ -1,4 +1,4 @@
= loading_icon(css_class: "gl-vertical-align-text-bottom mr-1")
= gl_loading_icon(inline: true, css_class: "mr-1")
= _('Metrics Dashboard YAML definition') + '…'
= link_to _('Learn more'), help_page_path('operations/metrics/dashboards/yaml.md')

View file

@ -1,4 +1,4 @@
= loading_icon(css_class: "gl-vertical-align-text-bottom gl-mr-1")
= gl_loading_icon(inline: true, css_class: "gl-mr-1")
Validating Route Map…
= link_to 'Learn more', help_page_path('ci/environments/index.md', anchor: 'go-from-source-files-to-public-pages')

View file

@ -1,3 +1,2 @@
.file-content#js-sketch-viewer{ data: { endpoint: blob_raw_path } }
.text-center.gl-mt-4.gl-mb-3.js-loading-icon
= loading_icon(size: "md")
= gl_loading_icon(size: "md", css_class: "gl-my-4 js-loading-icon")

View file

@ -1,6 +1,6 @@
.file-content.is-stl-loading
.text-center#js-stl-viewer{ data: { endpoint: blob_raw_path } }
= loading_icon(size: "md", css_class: "gl-mt-4 gl-mb-3")
= gl_loading_icon(size: "md", css_class: "gl-my-4")
.text-center.gl-mt-3.gl-mb-3.stl-controls
.btn-group
%button.gl-button.btn.btn-default.btn-sm.js-material-changer{ data: { type: 'wireframe' } }

View file

@ -34,5 +34,4 @@
%div{ id: dom_id(@project) }
%ol#commits-list.list-unstyled.content_list
= render 'commits', project: @project, ref: @ref
.loading.hide
= loading_icon(size: "lg")
= gl_loading_icon(size: 'lg', css_class: 'loading hide')

View file

@ -23,5 +23,4 @@
= _('There are no matching files')
%p.text-secondary
= _('Try using a different search term to find the file you are looking for.')
.text-center.gl-mt-3.loading
= loading_icon(size: 'md')
= gl_loading_icon(size: 'md', css_class: 'gl-mt-3 loading')

View file

@ -4,7 +4,7 @@
.save-project-loader
.center
%h2
= loading_icon
= gl_loading_icon(inline: true)
= import_in_progress_title
- if !has_ci_cd_only_params? && @project.external_import?
%p.monospace git clone --bare #{@project.safe_import_url}

View file

@ -18,7 +18,7 @@
.flash-text
.loading-metrics.js-loading-custom-metrics
%p.m-3
= loading_icon(css_class: 'metrics-load-spinner')
= gl_loading_icon(inline: true, css_class: 'metrics-load-spinner')
= s_('PrometheusService|Finding custom metrics...')
.empty-metrics.hidden.js-empty-custom-metrics
%p.text-tertiary.m-3.js-no-active-integration-text.hidden

View file

@ -16,7 +16,7 @@
.card-body
.loading-metrics.js-loading-metrics
%p.m-3
= loading_icon(css_class: 'metrics-load-spinner')
= gl_loading_icon(inline: true, css_class: 'metrics-load-spinner')
= s_('PrometheusService|Finding and configuring metrics...')
.empty-metrics.hidden.js-empty-metrics
%p.text-tertiary.m-3

View file

@ -1,7 +1,7 @@
- if any_projects?(@projects)
.project-item-select-holder.btn-group.gl-ml-auto.gl-mr-auto.gl-relative.gl-overflow-hidden{ class: 'gl-display-flex!' }
%a.btn.gl-button.btn-confirm.js-new-project-item-link.block-truncated.qa-new-project-item-link{ href: '', data: { label: local_assigns[:label], type: local_assigns[:type] }, class: "gl-m-0!" }
= loading_icon(color: 'light')
= gl_loading_icon(inline: true, color: 'light')
= project_select_tag :project_path, class: "project-item-select gl-absolute! gl-visibility-hidden", data: { include_groups: local_assigns[:include_groups], order_by: 'last_activity_at', relative_path: local_assigns[:path], with_shared: local_assigns[:with_shared], include_projects_in_subgroups: local_assigns[:include_projects_in_subgroups] }, with_feature_enabled: local_assigns[:with_feature_enabled]
%button.btn.dropdown-toggle.btn-confirm.btn-md.gl-button.gl-dropdown-toggle.dropdown-toggle-split.new-project-item-select-button.qa-new-project-item-select-button.gl-p-0.gl-w-100{ class: "gl-m-0!", 'aria-label': _('Toggle project select') }
= sprite_icon('chevron-down')

View file

@ -50,7 +50,7 @@
// Fallback while content is loading
.title.hide-collapsed
= _('Time tracking')
= loading_icon(css_class: 'gl-vertical-align-text-bottom')
= gl_loading_icon(inline: true)
- if issuable_sidebar.has_key?(:due_date)
#js-due-date-entry-point
@ -109,8 +109,8 @@
= dropdown_loading
= dropdown_footer add_content_class: true do
%button.gl-button.btn.btn-confirm.sidebar-move-issue-confirmation-button.js-move-issue-confirmation-button{ type: 'button', disabled: true }
= gl_loading_icon(inline: true, css_class: 'sidebar-move-issue-confirmation-loading-icon gl-mr-2')
= _('Move')
= loading_icon(css_class: 'gl-vertical-align-text-bottom sidebar-move-issue-confirmation-loading-icon')
-# haml-lint:disable InlineJavaScript
%script.js-sidebar-options{ type: "application/json" }= issuable_sidebar_options(issuable_sidebar).to_json.html_safe

View file

@ -7,7 +7,7 @@
directly_invite_members: can_admin_project_member?(@project) } }
.title.hide-collapsed
= _('Assignee')
= loading_icon(css_class: 'gl-vertical-align-text-bottom')
= gl_loading_icon(inline: true)
.js-sidebar-assignee-data.selectbox.hide-collapsed
- if assignees.none?

View file

@ -3,7 +3,7 @@
#js-vue-sidebar-reviewers{ data: { field: issuable_type, signed_in: signed_in } }
.title.hide-collapsed
= _('Reviewer')
= loading_icon(css_class: 'gl-vertical-align-text-bottom')
= gl_loading_icon(inline: true)
.selectbox.hide-collapsed
- if reviewers.none?

View file

@ -18,7 +18,7 @@
%span.attaching-file-message
-# Populated by app/assets/javascripts/dropzone_input.js
%span.uploading-progress 0%
= loading_icon(css_class: 'align-text-bottom gl-mr-2')
= gl_loading_icon(inline: true, css_class: 'gl-mr-2')
%span.uploading-error-container.hide
%span.uploading-error-icon

View file

@ -1,8 +0,0 @@
---
name: rate_limit_user_by_id_endpoint
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73069
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/348796
milestone: '14.6'
type: development
group: group::optimize
default_enabled: false

View file

@ -771,6 +771,26 @@ group.require_two_factor_authentication=false
group.save
```
### Check and toggle a feature for all projects in a group
```ruby
projects = Group.find_by_name('_group_name').projects
projects.each do |p|
state = p.<feature-name>?
if state
puts "#{p.name} has <feature-name> already enabled. Skipping..."
else
puts "#{p.name} didn't have <feature-name> enabled. Enabling..."
p.project_feature.update!(builds_access_level: ProjectFeature::PRIVATE)
end
end
```
To find features that can be toggled, run `pp p.project_feature`.
Available permission levels are listed in
[concerns/featurable.rb](https://gitlab.com/gitlab-org/gitlab/blob/master/app/models/concerns/featurable.rb).
## Authentication
### Re-enable standard web sign-in form

View file

@ -11104,7 +11104,6 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="groupistemporarystorageincreaseenabled"></a>`isTemporaryStorageIncreaseEnabled` | [`Boolean!`](#boolean) | Status of the temporary storage increase. |
| <a id="grouplfsenabled"></a>`lfsEnabled` | [`Boolean`](#boolean) | Indicates if Large File Storage (LFS) is enabled for namespace. |
| <a id="groupmentionsdisabled"></a>`mentionsDisabled` | [`Boolean`](#boolean) | Indicates if a group is disabled from getting mentioned. |
| <a id="groupmergerequestviolations"></a>`mergeRequestViolations` | [`ComplianceViolationConnection`](#complianceviolationconnection) | Compliance violations reported on merge requests merged within the group. Available only when feature flag `compliance_violations_graphql_type` is enabled. This flag is disabled by default, because the feature is experimental and is subject to change without notice. (see [Connections](#connections)) |
| <a id="groupname"></a>`name` | [`String!`](#string) | Name of the namespace. |
| <a id="grouporganizations"></a>`organizations` | [`CustomerRelationsOrganizationConnection`](#customerrelationsorganizationconnection) | Find organizations of this group. (see [Connections](#connections)) |
| <a id="grouppackagesettings"></a>`packageSettings` | [`PackageSettings`](#packagesettings) | Package settings for the namespace. |
@ -11449,6 +11448,23 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="grouplabelsonlygrouplabels"></a>`onlyGroupLabels` | [`Boolean`](#boolean) | Include only group level labels. |
| <a id="grouplabelssearchterm"></a>`searchTerm` | [`String`](#string) | Search term to find labels with. |
##### `Group.mergeRequestViolations`
Compliance violations reported on merge requests merged within the group. Available only when feature flag `compliance_violations_graphql_type` is enabled. This flag is disabled by default, because the feature is experimental and is subject to change without notice.
Returns [`ComplianceViolationConnection`](#complianceviolationconnection).
This field returns a [connection](#connections). It accepts the
four standard [pagination arguments](#connection-pagination-arguments):
`before: String`, `after: String`, `first: Int`, `last: Int`.
###### Arguments
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="groupmergerequestviolationsfilters"></a>`filters` | [`ComplianceViolationInput`](#complianceviolationinput) | Filters applied when retrieving compliance violations. |
| <a id="groupmergerequestviolationssort"></a>`sort` | [`ComplianceViolationSort`](#complianceviolationsort) | List compliance violations by sort order. |
##### `Group.mergeRequests`
Merge requests for projects in this group.
@ -17156,6 +17172,21 @@ Severity of the compliance violation.
| <a id="complianceviolationseveritylow"></a>`LOW` | Low severity. |
| <a id="complianceviolationseveritymedium"></a>`MEDIUM` | Medium severity. |
### `ComplianceViolationSort`
Compliance violation sort values.
| Value | Description |
| ----- | ----------- |
| <a id="complianceviolationsortmerged_at_asc"></a>`MERGED_AT_ASC` | Date merged in ascending order, further sorted by ID in ascending order. |
| <a id="complianceviolationsortmerged_at_desc"></a>`MERGED_AT_DESC` | Date merged in descending order, further sorted by ID in descending order. |
| <a id="complianceviolationsortmerge_request_title_asc"></a>`MERGE_REQUEST_TITLE_ASC` | Merge request title in ascending order, further sorted by ID in ascending order. |
| <a id="complianceviolationsortmerge_request_title_desc"></a>`MERGE_REQUEST_TITLE_DESC` | Merge request title in descending order, further sorted by ID in descending order. |
| <a id="complianceviolationsortseverity_level_asc"></a>`SEVERITY_LEVEL_ASC` | Severity in ascending order, further sorted by ID in ascending order. |
| <a id="complianceviolationsortseverity_level_desc"></a>`SEVERITY_LEVEL_DESC` | Severity in descending order, further sorted by ID in descending order. |
| <a id="complianceviolationsortviolation_reason_asc"></a>`VIOLATION_REASON_ASC` | Violation reason in ascending order, further sorted by ID in ascending order. |
| <a id="complianceviolationsortviolation_reason_desc"></a>`VIOLATION_REASON_DESC` | Violation reason in descending order, further sorted by ID in descending order. |
### `ConanMetadatumFileTypeEnum`
Conan file types.
@ -19744,6 +19775,16 @@ Field that are available while modifying the custom mapping attributes for an HT
| <a id="complianceframeworkinputname"></a>`name` | [`String`](#string) | New name for the compliance framework. |
| <a id="complianceframeworkinputpipelineconfigurationfullpath"></a>`pipelineConfigurationFullPath` | [`String`](#string) | Full path of the compliance pipeline configuration stored in a project repository, such as `.gitlab/.compliance-gitlab-ci.yml@compliance/hipaa` **(ULTIMATE)**. |
### `ComplianceViolationInput`
#### Arguments
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="complianceviolationinputmergedafter"></a>`mergedAfter` | [`Date`](#date) | Merged date of merge requests merged after a compliance violation was created. |
| <a id="complianceviolationinputmergedbefore"></a>`mergedBefore` | [`Date`](#date) | Merged date of merge requests merged before a compliance violation was created. |
| <a id="complianceviolationinputprojectids"></a>`projectIds` | [`[ProjectID!]`](#projectid) | Filter compliance violations by project. |
### `DastProfileCadenceInput`
Represents DAST Profile Cadence.

View file

@ -88,50 +88,36 @@ Please use the following function inside JS to render an icon:
### Usage in HAML/Rails
To insert a loading spinner in HAML or Rails use the `loading_icon` helper:
To insert a loading spinner in HAML or Rails use the `gl_loading_icon` helper:
```haml
= loading_icon
= gl_loading_icon
```
You can include one or more of the following properties with the `loading_icon` helper, as demonstrated
You can include one or more of the following properties with the `gl_loading_icon` helper, as demonstrated
by the examples that follow:
- `container` (optional): wraps the loading icon in a container, which centers the loading icon using the `text-center` CSS property.
- `color` (optional): either `orange` (default), `light`, or `dark`.
- `inline` (optional): uses in an inline element if `true`, otherwise, a block element (default), with the spinner centered.
- `color` (optional): either `dark` (default) or `light`.
- `size` (optional): either `sm` (default), `md`, `lg`, or `xl`.
- `css_class` (optional): defaults to an empty string, but can be used for utility classes to fine-tune alignment or spacing.
- `css_class` (optional): defaults to nothing, but can be used for utility classes to fine-tune alignment or spacing.
**Example 1:**
The following HAML expression generates a loading icon's markup and
centers the icon by wrapping it in a `gl-spinner-container` element.
centers the icon.
```haml
= loading_icon(container: true)
```
**Output from example 1:**
```html
<div class="gl-spinner-container">
<span class="gl-spinner gl-spinner-orange gl-spinner-sm" aria-label="Loading"></span>
</div>
= gl_loading_icon
```
**Example 2:**
The following HAML expression generates a loading icon's markup
The following HAML expression generates an inline loading icon's markup
with a custom size. It also appends a margin utility class.
```haml
= loading_icon(size: 'lg', css_class: 'gl-mr-2')
```
**Output from example 2:**
```html
<span class="gl-spinner gl-spinner-orange gl-spinner-lg gl-mr-2" aria-label="Loading"></span>
= gl_loading_icon(inline: true, size: 'lg', css_class: 'gl-mr-2')
```
### Usage in Vue

View file

@ -142,13 +142,11 @@ module API
get ":id", feature_category: :users do
forbidden!('Not authorized!') unless current_user
if Feature.enabled?(:rate_limit_user_by_id_endpoint, type: :development)
unless current_user.admin?
check_rate_limit!(:users_get_by_id,
scope: current_user,
users_allowlist: Gitlab::CurrentSettings.current_application_settings.users_get_by_id_limit_allowlist
)
end
unless current_user.admin?
check_rate_limit!(:users_get_by_id,
scope: current_user,
users_allowlist: Gitlab::CurrentSettings.current_application_settings.users_get_by_id_limit_allowlist
)
end
user = User.find_by(id: params[:id])

View file

@ -175,8 +175,6 @@
"three-orbit-controls": "^82.1.0",
"three-stl-loader": "^1.0.4",
"timeago.js": "^4.0.2",
"tiptap": "^1.32.2",
"tiptap-extensions": "^1.35.2",
"url-loader": "^4.1.1",
"uuid": "8.1.0",
"visibilityjs": "^1.2.4",

View file

@ -231,23 +231,33 @@ RSpec.describe IconsHelper do
end
end
describe 'loading_icon' do
it 'returns span with gl-spinner class and default configuration' do
expect(loading_icon.to_s)
.to eq '<span class="gl-spinner gl-spinner-orange gl-spinner-sm" aria-label="Loading"></span>'
describe 'gl_loading_icon' do
it 'returns the default spinner markup' do
expect(gl_loading_icon.to_s)
.to eq '<div class="gl-spinner-container" role="status"><span class="gl-spinner gl-spinner-dark gl-spinner-sm gl-vertical-align-text-bottom!" aria-label="Loading"></span></div>'
end
context 'when css_class is provided' do
it 'appends css_class to gl-spinner element' do
expect(loading_icon(css_class: 'gl-mr-2').to_s)
.to eq '<span class="gl-spinner gl-spinner-orange gl-spinner-sm gl-mr-2" aria-label="Loading"></span>'
it 'appends css_class to container element' do
expect(gl_loading_icon(css_class: 'gl-mr-2').to_s).to match 'gl-spinner-container gl-mr-2'
end
end
context 'when container is true' do
it 'creates a container that has the gl-spinner-container class selector' do
expect(loading_icon(container: true).to_s)
.to eq '<div class="gl-spinner-container"><span class="gl-spinner gl-spinner-orange gl-spinner-sm" aria-label="Loading"></span></div>'
context 'when size is provided' do
it 'sets the size class' do
expect(gl_loading_icon(size: 'xl').to_s).to match 'gl-spinner-xl'
end
end
context 'when color is provided' do
it 'sets the color class' do
expect(gl_loading_icon(color: 'light').to_s).to match 'gl-spinner-light'
end
end
context 'when inline is true' do
it 'creates an inline container' do
expect(gl_loading_icon(inline: true).to_s).to start_with '<span class="gl-spinner-container"'
end
end
end

View file

@ -649,20 +649,6 @@ RSpec.describe API::Users do
expect(response).to have_gitlab_http_status(:ok)
end
end
context 'when feature flag is disabled' do
before do
stub_feature_flags(rate_limit_user_by_id_endpoint: false)
end
it 'does not throttle the request' do
expect(Gitlab::ApplicationRateLimiter).not_to receive(:throttled?)
get api("/users/#{user.id}", user)
expect(response).to have_gitlab_http_status(:ok)
end
end
end
context 'when job title is present' do

View file

@ -8096,7 +8096,7 @@ lowercase-keys@^2.0.0:
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479"
integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==
lowlight@^1.17.0, lowlight@^1.20.0:
lowlight@^1.20.0:
version "1.20.0"
resolved "https://registry.yarnpkg.com/lowlight/-/lowlight-1.20.0.tgz#ddb197d33462ad0d93bf19d17b6c301aa3941888"
integrity sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==
@ -9584,14 +9584,7 @@ prop-types@^15.7.2:
object-assign "^4.1.1"
react-is "^16.8.1"
prosemirror-collab@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/prosemirror-collab/-/prosemirror-collab-1.2.2.tgz#8d2c0e82779cfef5d051154bd0836428bd6d9c4a"
integrity sha512-tBnHKMLgy5Qmx9MYVcLfs3pAyjtcqYYDd9kp3y+LSiQzkhMQDfZSV3NXWe4Gsly32adSef173BvObwfoSQL5MA==
dependencies:
prosemirror-state "^1.0.0"
prosemirror-commands@^1.1.4, prosemirror-commands@^1.2.1:
prosemirror-commands@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/prosemirror-commands/-/prosemirror-commands-1.2.1.tgz#eae0cb714df695260659b78ff5d201d3a037e50d"
integrity sha512-S/IkpXfpuLFsRynC2HQ5iYROUPiZskKS1+ClcWycGJvj4HMb/mVfeEkQrixYxgTl96EAh+RZQNWPC06GZXk5tQ==
@ -9600,7 +9593,7 @@ prosemirror-commands@^1.1.4, prosemirror-commands@^1.2.1:
prosemirror-state "^1.0.0"
prosemirror-transform "^1.0.0"
prosemirror-dropcursor@^1.3.2, prosemirror-dropcursor@^1.4.0:
prosemirror-dropcursor@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/prosemirror-dropcursor/-/prosemirror-dropcursor-1.4.0.tgz#91a859d4ee79c99b1c0ba6ee61c093b195c0d9f0"
integrity sha512-6+YwTjmqDwlA/Dm+5wK67ezgqgjA/MhSDgaNxKUzH97SmeuWFXyLeDRxxOPZeSo7yTxcDGUCWTEjmQZsVBuMrQ==
@ -9609,7 +9602,7 @@ prosemirror-dropcursor@^1.3.2, prosemirror-dropcursor@^1.4.0:
prosemirror-transform "^1.1.0"
prosemirror-view "^1.1.0"
prosemirror-gapcursor@^1.1.5, prosemirror-gapcursor@^1.2.1:
prosemirror-gapcursor@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/prosemirror-gapcursor/-/prosemirror-gapcursor-1.2.1.tgz#02365e1bcc1ad25d390b0fb7f0e94a7fc173ad75"
integrity sha512-PHa9lj27iM/g4C46gxVzsefuXVfy/LrGQH4QjMRht7VDBgw77iWYWn8ZHMWSFkwtr9jQEuxI5gccHHHwWG80nw==
@ -9619,7 +9612,7 @@ prosemirror-gapcursor@^1.1.5, prosemirror-gapcursor@^1.2.1:
prosemirror-state "^1.0.0"
prosemirror-view "^1.0.0"
prosemirror-history@^1.1.3, prosemirror-history@^1.2.0:
prosemirror-history@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/prosemirror-history/-/prosemirror-history-1.2.0.tgz#04cc4df8d2f7b2a46651a2780de191ada6d465ea"
integrity sha512-B9v9xtf4fYbKxQwIr+3wtTDNLDZcmMMmGiI3TAPShnUzvo+Rmv1GiUrsQChY1meetHl7rhML2cppF3FTs7f7UQ==
@ -9628,15 +9621,7 @@ prosemirror-history@^1.1.3, prosemirror-history@^1.2.0:
prosemirror-transform "^1.0.0"
rope-sequence "^1.3.0"
prosemirror-inputrules@^1.1.2, prosemirror-inputrules@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/prosemirror-inputrules/-/prosemirror-inputrules-1.1.3.tgz#93f9199ca02473259c30d7e352e4c14022d54638"
integrity sha512-ZaHCLyBtvbyIHv0f5p6boQTIJjlD6o2NPZiEaZWT2DA+j591zS29QQEMT4lBqwcLW3qRSf7ZvoKNbf05YrsStw==
dependencies:
prosemirror-state "^1.0.0"
prosemirror-transform "^1.0.0"
prosemirror-keymap@^1.0.0, prosemirror-keymap@^1.1.2, prosemirror-keymap@^1.1.4, prosemirror-keymap@^1.1.5:
prosemirror-keymap@^1.0.0, prosemirror-keymap@^1.1.2, prosemirror-keymap@^1.1.5:
version "1.1.5"
resolved "https://registry.yarnpkg.com/prosemirror-keymap/-/prosemirror-keymap-1.1.5.tgz#b5984c7d30f5c75956c853126c54e9e624c0327b"
integrity sha512-8SZgPH3K+GLsHL2wKuwBD9rxhsbnVBTwpHCO4VUO5GmqUQlxd/2GtBVWTsyLq4Dp3N9nGgPd3+lZFKUDuVp+Vw==
@ -9652,7 +9637,7 @@ prosemirror-markdown@1.7.1:
markdown-it "^12.0.0"
prosemirror-model "^1.0.0"
prosemirror-model@^1.0.0, prosemirror-model@^1.13.1, prosemirror-model@^1.16.0, prosemirror-model@^1.16.1, prosemirror-model@^1.2.0, prosemirror-model@^1.8.1:
prosemirror-model@^1.0.0, prosemirror-model@^1.16.0, prosemirror-model@^1.16.1, prosemirror-model@^1.2.0, prosemirror-model@^1.8.1:
version "1.16.1"
resolved "https://registry.yarnpkg.com/prosemirror-model/-/prosemirror-model-1.16.1.tgz#fb388270bc9609b66298d6a7e15d0cc1d6c61253"
integrity sha512-r1/w0HDU40TtkXp0DyKBnFPYwd8FSlUSJmGCGFv4DeynfeSlyQF2FD0RQbVEMOe6P3PpUSXM6LZBV7W/YNZ4mA==
@ -9666,7 +9651,7 @@ prosemirror-schema-basic@^1.0.0, prosemirror-schema-basic@^1.1.2:
dependencies:
prosemirror-model "^1.2.0"
prosemirror-schema-list@^1.0.0, prosemirror-schema-list@^1.1.4, prosemirror-schema-list@^1.1.6:
prosemirror-schema-list@^1.0.0, prosemirror-schema-list@^1.1.6:
version "1.1.6"
resolved "https://registry.yarnpkg.com/prosemirror-schema-list/-/prosemirror-schema-list-1.1.6.tgz#c3e13fe2f74750e4a53ff88d798dc0c4ccca6707"
integrity sha512-aFGEdaCWmJzouZ8DwedmvSsL50JpRkqhQ6tcpThwJONVVmCgI36LJHtoQ4VGZbusMavaBhXXr33zyD2IVsTlkw==
@ -9674,7 +9659,7 @@ prosemirror-schema-list@^1.0.0, prosemirror-schema-list@^1.1.4, prosemirror-sche
prosemirror-model "^1.0.0"
prosemirror-transform "^1.0.0"
prosemirror-state@^1.0.0, prosemirror-state@^1.2.2, prosemirror-state@^1.3.1, prosemirror-state@^1.3.3, prosemirror-state@^1.3.4:
prosemirror-state@^1.0.0, prosemirror-state@^1.2.2, prosemirror-state@^1.3.1, prosemirror-state@^1.3.4:
version "1.3.4"
resolved "https://registry.yarnpkg.com/prosemirror-state/-/prosemirror-state-1.3.4.tgz#4c6b52628216e753fc901c6d2bfd84ce109e8952"
integrity sha512-Xkkrpd1y/TQ6HKzN3agsQIGRcLckUMA9u3j207L04mt8ToRgpGeyhbVv0HI7omDORIBHjR29b7AwlATFFf2GLA==
@ -9702,14 +9687,14 @@ prosemirror-test-builder@^1.0.5:
prosemirror-schema-basic "^1.0.0"
prosemirror-schema-list "^1.0.0"
prosemirror-transform@^1.0.0, prosemirror-transform@^1.1.0, prosemirror-transform@^1.2.1, prosemirror-transform@^1.2.8, prosemirror-transform@^1.3.3:
prosemirror-transform@^1.0.0, prosemirror-transform@^1.1.0, prosemirror-transform@^1.2.1, prosemirror-transform@^1.3.3:
version "1.3.3"
resolved "https://registry.yarnpkg.com/prosemirror-transform/-/prosemirror-transform-1.3.3.tgz#5f6712b0577a119cc418686fe7588b6dd9b7464d"
integrity sha512-9NLVXy1Sfa2G6qPqhWMkEvwQQMTw7OyTqOZbJaGQWsCeH3hH5Cw+c5eNaLM1Uu75EyKLsEZhJ93XpHJBa6RX8A==
dependencies:
prosemirror-model "^1.0.0"
prosemirror-view@^1.0.0, prosemirror-view@^1.1.0, prosemirror-view@^1.13.3, prosemirror-view@^1.16.5, prosemirror-view@^1.23.6, prosemirror-view@^1.23.7:
prosemirror-view@^1.0.0, prosemirror-view@^1.1.0, prosemirror-view@^1.13.3, prosemirror-view@^1.23.6, prosemirror-view@^1.23.7:
version "1.23.7"
resolved "https://registry.yarnpkg.com/prosemirror-view/-/prosemirror-view-1.23.7.tgz#f003af94445ef456e397c18cf4bb995e7072097f"
integrity sha512-ugY+g/4UI2Ree1zzdvbyQWF2KpbFa7kxKMLHaEJcxiPaErnZiD5wZFqIgFinc7fY2v/QM3DLnJ++2I45ULRdrg==
@ -11335,61 +11320,6 @@ tippy.js@^6.3.7:
dependencies:
"@popperjs/core" "^2.9.0"
tiptap-commands@^1.17.1:
version "1.17.1"
resolved "https://registry.yarnpkg.com/tiptap-commands/-/tiptap-commands-1.17.1.tgz#a8974a26d87db57b2fd4fc56a552520c69e43a4a"
integrity sha512-CyGvMD/c6fNer5LThWGtrVMXHAqHn93ivGQpqJ58x3HNZFuoIiF9QTWXAiWbY/4QrG0ANYHKCSe9n5afickTqw==
dependencies:
prosemirror-commands "^1.1.4"
prosemirror-inputrules "^1.1.2"
prosemirror-model "^1.13.1"
prosemirror-schema-list "^1.1.4"
prosemirror-state "^1.3.3"
prosemirror-tables "^1.1.1"
tiptap-utils "^1.13.1"
tiptap-extensions@^1.35.2:
version "1.35.2"
resolved "https://registry.yarnpkg.com/tiptap-extensions/-/tiptap-extensions-1.35.2.tgz#83dd6ee703ae8c83b58c7608f97253fcc4f1a94c"
integrity sha512-TIMbHVJe0/3aVeTeCmqGbatDkfxduPYFOffNCmuKR+h6oQNzTu6rLVhRzoNqktfxIoi/b44SiDPorTjSN72dCw==
dependencies:
lowlight "^1.17.0"
prosemirror-collab "^1.2.2"
prosemirror-history "^1.1.3"
prosemirror-model "^1.13.1"
prosemirror-state "^1.3.3"
prosemirror-tables "^1.1.1"
prosemirror-transform "^1.2.8"
prosemirror-view "^1.16.5"
tiptap "^1.32.2"
tiptap-commands "^1.17.1"
tiptap-utils "^1.13.1"
tiptap-utils@^1.13.1:
version "1.13.1"
resolved "https://registry.yarnpkg.com/tiptap-utils/-/tiptap-utils-1.13.1.tgz#f2150ded432465d66aa03a5ab333803415cddd20"
integrity sha512-RoCvMfkdu7fp9u7nsRr1OgsYU8RFjoHKHEKpx075rJ9X0t+j5Vxah9n6QzTTr4yjvcavq22WO2flFacm36zYtA==
dependencies:
prosemirror-model "^1.13.1"
prosemirror-state "^1.3.3"
prosemirror-tables "^1.1.1"
tiptap@^1.32.2:
version "1.32.2"
resolved "https://registry.yarnpkg.com/tiptap/-/tiptap-1.32.2.tgz#cd6259e853652bfc6860758ff44ebb695d5edd1c"
integrity sha512-5IwVj8nGo8y5V3jbdtoEd7xNUsi8Q0N6WV2Nfs70olqz3fldXkiImBrDhZJ4Anx8vhyP6PIBttrg0prFVmwIvw==
dependencies:
prosemirror-commands "^1.1.4"
prosemirror-dropcursor "^1.3.2"
prosemirror-gapcursor "^1.1.5"
prosemirror-inputrules "^1.1.3"
prosemirror-keymap "^1.1.4"
prosemirror-model "^1.13.1"
prosemirror-state "^1.3.3"
prosemirror-view "^1.16.5"
tiptap-commands "^1.17.1"
tiptap-utils "^1.13.1"
tmpl@1.0.x:
version "1.0.4"
resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1"