Add support for red text and highlighted text in markdown
This commit is contained in:
parent
dcad48ec90
commit
b23e278729
|
@ -60,8 +60,58 @@
|
|||
// No-op
|
||||
}
|
||||
|
||||
addDefs() {
|
||||
// No-op
|
||||
addDefs(builder, textBuilder) {
|
||||
// Thanks, https://stackoverflow.com/a/12263962/1180785
|
||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=603157
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=917766
|
||||
textBuilder('highlight', () => this.svg.el('filter')
|
||||
.add(
|
||||
// Morph makes characters consistent
|
||||
this.svg.el('feMorphology').attrs({
|
||||
'in': 'SourceAlpha',
|
||||
'operator': 'dilate',
|
||||
'radius': '4',
|
||||
}),
|
||||
// Blur+thresh makes edges smooth
|
||||
this.svg.el('feGaussianBlur').attrs({
|
||||
'edgeMode': 'none',
|
||||
'stdDeviation': '3, 1.5',
|
||||
}),
|
||||
this.svg.el('feComponentTransfer').add(
|
||||
this.svg.el('feFuncA').attrs({
|
||||
'intercept': -70,
|
||||
'slope': 100,
|
||||
'type': 'linear',
|
||||
})
|
||||
),
|
||||
// Add colour
|
||||
this.svg.el('feComponentTransfer').add(
|
||||
this.svg.el('feFuncR').attrs({
|
||||
'intercept': 1,
|
||||
'slope': 0,
|
||||
'type': 'linear',
|
||||
}),
|
||||
this.svg.el('feFuncG').attrs({
|
||||
'intercept': 0.9375,
|
||||
'slope': 0,
|
||||
'type': 'linear',
|
||||
}),
|
||||
this.svg.el('feFuncB').attrs({
|
||||
'intercept': 0.09375,
|
||||
'slope': 0,
|
||||
'type': 'linear',
|
||||
}),
|
||||
this.svg.el('feFuncA').attrs({
|
||||
'slope': 0.7,
|
||||
'type': 'linear',
|
||||
})
|
||||
),
|
||||
// Draw text on top
|
||||
this.svg.el('feMerge').add(
|
||||
this.svg.el('feMergeNode'),
|
||||
this.svg.el('feMergeNode').attr('in', 'SourceGraphic')
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
getTitleAttrs() {
|
||||
|
@ -3837,7 +3887,7 @@
|
|||
start: /"/y,
|
||||
},
|
||||
{baseToken: {v: '...'}, start: /\.\.\./y},
|
||||
{end: /(?=[ \t\r\n:+~\-*!<>,])|$/y, start: /(?=[^ \t\r\n:+~\-*!<>,])/y},
|
||||
{end: /(?=[ \t\r\n:+~\-*!<,])|$/y, start: /(?=[^ \t\r\n:+~\-*!<,])/y},
|
||||
{
|
||||
end: /(?=[^~\-<>x])|[-~]x|[<>](?=x)|$/y,
|
||||
includeEnd: true,
|
||||
|
@ -4109,28 +4159,36 @@
|
|||
const STYLES = [
|
||||
{
|
||||
attrs: {'font-style': 'italic'},
|
||||
begin: /[\s_~`]\*(?=\S)/g,
|
||||
end: /\S\*(?=[\s_~`])/g,
|
||||
begin: {matcher: /[\s_~`>]\*(?=\S)/g, skip: 1},
|
||||
end: {matcher: /\S\*(?=[\s_~`<])/g, skip: 1},
|
||||
}, {
|
||||
attrs: {'font-style': 'italic'},
|
||||
begin: /[\s*~`]_(?=\S)/g,
|
||||
end: /\S_(?=[\s*~`])/g,
|
||||
begin: {matcher: /[\s*~`>]_(?=\S)/g, skip: 1},
|
||||
end: {matcher: /\S_(?=[\s*~`<])/g, skip: 1},
|
||||
}, {
|
||||
attrs: {'font-weight': 'bolder'},
|
||||
begin: /[\s_~`]\*\*(?=\S)/g,
|
||||
end: /\S\*\*(?=[\s_~`])/g,
|
||||
begin: {matcher: /[\s_~`>]\*\*(?=\S)/g, skip: 1},
|
||||
end: {matcher: /\S\*\*(?=[\s_~`<])/g, skip: 1},
|
||||
}, {
|
||||
attrs: {'font-weight': 'bolder'},
|
||||
begin: /[\s*~`]__(?=\S)/g,
|
||||
end: /\S__(?=[\s*~`])/g,
|
||||
begin: {matcher: /[\s*~`>]__(?=\S)/g, skip: 1},
|
||||
end: {matcher: /\S__(?=[\s*~`<])/g, skip: 1},
|
||||
}, {
|
||||
attrs: {'text-decoration': 'line-through'},
|
||||
begin: /[\s_*`]~(?=\S)/g,
|
||||
end: /\S~(?=[\s_*`])/g,
|
||||
begin: {matcher: /[\s_*`>]~(?=\S)/g, skip: 1},
|
||||
end: {matcher: /\S~(?=[\s_*`<])/g, skip: 1},
|
||||
}, {
|
||||
attrs: {'font-family': 'Courier New,Liberation Mono,monospace'},
|
||||
begin: /[\s_*~.]`(?=\S)/g,
|
||||
end: /\S`(?=[\s_*~.])/g,
|
||||
begin: {matcher: /[\s_*~.>]`(?=\S)/g, skip: 1},
|
||||
end: {matcher: /\S`(?=[\s_*~.<])/g, skip: 1},
|
||||
}, {
|
||||
attrs: {'fill': '#DD0000'},
|
||||
begin: {matcher: /<red>/g, skip: 0},
|
||||
end: {matcher: /<\/red>/g, skip: 0},
|
||||
}, {
|
||||
attrs: {'filter': 'highlight'},
|
||||
begin: {matcher: /<highlight>/g, skip: 0},
|
||||
end: {matcher: /<\/highlight>/g, skip: 0},
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -4142,19 +4200,20 @@
|
|||
|
||||
STYLES.forEach(({begin, end}, ind) => {
|
||||
const search = active[ind] ? end : begin;
|
||||
search.lastIndex = p;
|
||||
const m = search.exec(virtLine);
|
||||
if(m && (
|
||||
m.index < bestStart ||
|
||||
(m.index === bestStart && search.lastIndex > bestEnd)
|
||||
)) {
|
||||
search.matcher.lastIndex = p + 1 - search.skip;
|
||||
const m = search.matcher.exec(virtLine);
|
||||
const beginInd = m ? (m.index + search.skip) : Number.POSITIVE_INFINITY;
|
||||
if(
|
||||
beginInd < bestStart ||
|
||||
(beginInd === bestStart && search.matcher.lastIndex > bestEnd)
|
||||
) {
|
||||
styleIndex = ind;
|
||||
bestStart = m.index;
|
||||
bestEnd = search.lastIndex;
|
||||
bestStart = beginInd;
|
||||
bestEnd = search.matcher.lastIndex;
|
||||
}
|
||||
});
|
||||
|
||||
return {end: bestEnd - 1, start: bestStart, styleIndex};
|
||||
return {end: bestEnd - 1, start: bestStart - 1, styleIndex};
|
||||
}
|
||||
|
||||
function combineAttrs(activeCount, active) {
|
||||
|
@ -6776,7 +6835,9 @@
|
|||
}
|
||||
|
||||
detach() {
|
||||
if(this.element.parentNode) {
|
||||
this.element.parentNode.removeChild(this.element);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
@ -6830,11 +6891,14 @@
|
|||
throw new Error('Invalid formatted text line: ' + formattedLine);
|
||||
}
|
||||
formattedLine.forEach(({text, attrs}) => {
|
||||
let element = text;
|
||||
if(attrs) {
|
||||
node.add(svg.el('tspan').attrs(attrs).add(text));
|
||||
} else {
|
||||
node.add(text);
|
||||
element = svg.el('tspan').attrs(attrs).add(text);
|
||||
if(attrs.filter) {
|
||||
element.attr('filter', svg.getTextFilter(attrs.filter));
|
||||
}
|
||||
}
|
||||
node.add(element);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -7282,12 +7346,40 @@
|
|||
this.dom = domWrapper;
|
||||
this.body = this.el('svg').attr('xmlns', NS).attr('version', '1.1');
|
||||
const fn = (textSizerFactory || defaultTextSizerFactory);
|
||||
this.textFilters = new Map();
|
||||
this.textSizer = new TextSizerWrapper(fn(this));
|
||||
|
||||
this.txt = this.txt.bind(this);
|
||||
this.el = this.el.bind(this);
|
||||
}
|
||||
|
||||
resetTextFilters() {
|
||||
this.textFilters.clear();
|
||||
}
|
||||
|
||||
registerTextFilter(name, id) {
|
||||
this.textFilters.set(name, {id, used: false});
|
||||
}
|
||||
|
||||
getTextFilter(name) {
|
||||
const filter = this.textFilters.get(name);
|
||||
if(!filter) {
|
||||
return 'none';
|
||||
}
|
||||
filter.used = true;
|
||||
return 'url(#' + filter.id + ')';
|
||||
}
|
||||
|
||||
getUsedTextFilterNames() {
|
||||
const used = [];
|
||||
for(const [name, filter] of this.textFilters) {
|
||||
if(filter.used) {
|
||||
used.push(name);
|
||||
}
|
||||
}
|
||||
return used;
|
||||
}
|
||||
|
||||
linearGradient(attrs, stops) {
|
||||
return this.el('linearGradient')
|
||||
.attrs(attrs)
|
||||
|
@ -7508,6 +7600,7 @@
|
|||
this.components = components || getComponents();
|
||||
this.svg = new SVG(new DOMWrapper(document), textSizerFactory);
|
||||
this.knownThemeDefs = new Set();
|
||||
this.knownTextFilterDefs = new Map();
|
||||
this.knownDefs = new Set();
|
||||
this.highlights = new Map();
|
||||
this.collapsed = new Set();
|
||||
|
@ -7524,6 +7617,7 @@
|
|||
this.prepareMeasurementsStage.bind(this);
|
||||
this.renderStage = this.renderStage.bind(this);
|
||||
this.addThemeDef = this.addThemeDef.bind(this);
|
||||
this.addThemeTextDef = this.addThemeTextDef.bind(this);
|
||||
this.addDef = this.addDef.bind(this);
|
||||
}
|
||||
|
||||
|
@ -7582,6 +7676,15 @@
|
|||
return namespacedName;
|
||||
}
|
||||
|
||||
addThemeTextDef(name, generator) {
|
||||
const namespacedName = this.namespace + name;
|
||||
if(!this.knownTextFilterDefs.has(name)) {
|
||||
const def = generator().attr('id', namespacedName);
|
||||
this.knownTextFilterDefs.set(name, def);
|
||||
}
|
||||
this.svg.registerTextFilter(name, namespacedName);
|
||||
}
|
||||
|
||||
addDef(name, generator) {
|
||||
let nm = name;
|
||||
let gen = generator;
|
||||
|
@ -7963,6 +8066,7 @@
|
|||
_reset(theme) {
|
||||
if(theme) {
|
||||
this.knownThemeDefs.clear();
|
||||
this.knownTextFilterDefs.clear();
|
||||
this.themeDefs.empty();
|
||||
}
|
||||
|
||||
|
@ -8060,7 +8164,11 @@
|
|||
this._reset(themeChanged);
|
||||
|
||||
this.metaCode.nodeValue = sequence.meta.code;
|
||||
this.theme.addDefs(this.addThemeDef);
|
||||
this.svg.resetTextFilters();
|
||||
this.theme.addDefs(this.addThemeDef, this.addThemeTextDef);
|
||||
for(const def of this.knownTextFilterDefs.values()) {
|
||||
def.detach();
|
||||
}
|
||||
|
||||
this.title.set({
|
||||
attrs: Object.assign({
|
||||
|
@ -8094,6 +8202,10 @@
|
|||
sequence.stages.forEach(this.renderStage);
|
||||
const bottomY = this.checkAgentRange(['[', ']'], this.currentY);
|
||||
|
||||
this.svg.getUsedTextFilterNames().forEach((name) => {
|
||||
this.themeDefs.add(this.knownTextFilterDefs.get(name));
|
||||
});
|
||||
|
||||
const stagesHeight = Math.max(bottomY - this.theme.actionMargin, 0);
|
||||
this.updateBounds(stagesHeight);
|
||||
|
||||
|
@ -8962,7 +9074,7 @@
|
|||
this.random.reset();
|
||||
}
|
||||
|
||||
addDefs(builder) {
|
||||
addDefs(builder, textBuilder) {
|
||||
builder('sketch_font', () => {
|
||||
const style = this.svg.el('style', null);
|
||||
// Font must be embedded for exporting as SVG / PNG
|
||||
|
@ -8974,6 +9086,8 @@
|
|||
);
|
||||
return style;
|
||||
});
|
||||
|
||||
super.addDefs(builder, textBuilder);
|
||||
}
|
||||
|
||||
vary(range, centre = 0) {
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -60,8 +60,58 @@
|
|||
// No-op
|
||||
}
|
||||
|
||||
addDefs() {
|
||||
// No-op
|
||||
addDefs(builder, textBuilder) {
|
||||
// Thanks, https://stackoverflow.com/a/12263962/1180785
|
||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=603157
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=917766
|
||||
textBuilder('highlight', () => this.svg.el('filter')
|
||||
.add(
|
||||
// Morph makes characters consistent
|
||||
this.svg.el('feMorphology').attrs({
|
||||
'in': 'SourceAlpha',
|
||||
'operator': 'dilate',
|
||||
'radius': '4',
|
||||
}),
|
||||
// Blur+thresh makes edges smooth
|
||||
this.svg.el('feGaussianBlur').attrs({
|
||||
'edgeMode': 'none',
|
||||
'stdDeviation': '3, 1.5',
|
||||
}),
|
||||
this.svg.el('feComponentTransfer').add(
|
||||
this.svg.el('feFuncA').attrs({
|
||||
'intercept': -70,
|
||||
'slope': 100,
|
||||
'type': 'linear',
|
||||
})
|
||||
),
|
||||
// Add colour
|
||||
this.svg.el('feComponentTransfer').add(
|
||||
this.svg.el('feFuncR').attrs({
|
||||
'intercept': 1,
|
||||
'slope': 0,
|
||||
'type': 'linear',
|
||||
}),
|
||||
this.svg.el('feFuncG').attrs({
|
||||
'intercept': 0.9375,
|
||||
'slope': 0,
|
||||
'type': 'linear',
|
||||
}),
|
||||
this.svg.el('feFuncB').attrs({
|
||||
'intercept': 0.09375,
|
||||
'slope': 0,
|
||||
'type': 'linear',
|
||||
}),
|
||||
this.svg.el('feFuncA').attrs({
|
||||
'slope': 0.7,
|
||||
'type': 'linear',
|
||||
})
|
||||
),
|
||||
// Draw text on top
|
||||
this.svg.el('feMerge').add(
|
||||
this.svg.el('feMergeNode'),
|
||||
this.svg.el('feMergeNode').attr('in', 'SourceGraphic')
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
getTitleAttrs() {
|
||||
|
@ -3837,7 +3887,7 @@
|
|||
start: /"/y,
|
||||
},
|
||||
{baseToken: {v: '...'}, start: /\.\.\./y},
|
||||
{end: /(?=[ \t\r\n:+~\-*!<>,])|$/y, start: /(?=[^ \t\r\n:+~\-*!<>,])/y},
|
||||
{end: /(?=[ \t\r\n:+~\-*!<,])|$/y, start: /(?=[^ \t\r\n:+~\-*!<,])/y},
|
||||
{
|
||||
end: /(?=[^~\-<>x])|[-~]x|[<>](?=x)|$/y,
|
||||
includeEnd: true,
|
||||
|
@ -4109,28 +4159,36 @@
|
|||
const STYLES = [
|
||||
{
|
||||
attrs: {'font-style': 'italic'},
|
||||
begin: /[\s_~`]\*(?=\S)/g,
|
||||
end: /\S\*(?=[\s_~`])/g,
|
||||
begin: {matcher: /[\s_~`>]\*(?=\S)/g, skip: 1},
|
||||
end: {matcher: /\S\*(?=[\s_~`<])/g, skip: 1},
|
||||
}, {
|
||||
attrs: {'font-style': 'italic'},
|
||||
begin: /[\s*~`]_(?=\S)/g,
|
||||
end: /\S_(?=[\s*~`])/g,
|
||||
begin: {matcher: /[\s*~`>]_(?=\S)/g, skip: 1},
|
||||
end: {matcher: /\S_(?=[\s*~`<])/g, skip: 1},
|
||||
}, {
|
||||
attrs: {'font-weight': 'bolder'},
|
||||
begin: /[\s_~`]\*\*(?=\S)/g,
|
||||
end: /\S\*\*(?=[\s_~`])/g,
|
||||
begin: {matcher: /[\s_~`>]\*\*(?=\S)/g, skip: 1},
|
||||
end: {matcher: /\S\*\*(?=[\s_~`<])/g, skip: 1},
|
||||
}, {
|
||||
attrs: {'font-weight': 'bolder'},
|
||||
begin: /[\s*~`]__(?=\S)/g,
|
||||
end: /\S__(?=[\s*~`])/g,
|
||||
begin: {matcher: /[\s*~`>]__(?=\S)/g, skip: 1},
|
||||
end: {matcher: /\S__(?=[\s*~`<])/g, skip: 1},
|
||||
}, {
|
||||
attrs: {'text-decoration': 'line-through'},
|
||||
begin: /[\s_*`]~(?=\S)/g,
|
||||
end: /\S~(?=[\s_*`])/g,
|
||||
begin: {matcher: /[\s_*`>]~(?=\S)/g, skip: 1},
|
||||
end: {matcher: /\S~(?=[\s_*`<])/g, skip: 1},
|
||||
}, {
|
||||
attrs: {'font-family': 'Courier New,Liberation Mono,monospace'},
|
||||
begin: /[\s_*~.]`(?=\S)/g,
|
||||
end: /\S`(?=[\s_*~.])/g,
|
||||
begin: {matcher: /[\s_*~.>]`(?=\S)/g, skip: 1},
|
||||
end: {matcher: /\S`(?=[\s_*~.<])/g, skip: 1},
|
||||
}, {
|
||||
attrs: {'fill': '#DD0000'},
|
||||
begin: {matcher: /<red>/g, skip: 0},
|
||||
end: {matcher: /<\/red>/g, skip: 0},
|
||||
}, {
|
||||
attrs: {'filter': 'highlight'},
|
||||
begin: {matcher: /<highlight>/g, skip: 0},
|
||||
end: {matcher: /<\/highlight>/g, skip: 0},
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -4142,19 +4200,20 @@
|
|||
|
||||
STYLES.forEach(({begin, end}, ind) => {
|
||||
const search = active[ind] ? end : begin;
|
||||
search.lastIndex = p;
|
||||
const m = search.exec(virtLine);
|
||||
if(m && (
|
||||
m.index < bestStart ||
|
||||
(m.index === bestStart && search.lastIndex > bestEnd)
|
||||
)) {
|
||||
search.matcher.lastIndex = p + 1 - search.skip;
|
||||
const m = search.matcher.exec(virtLine);
|
||||
const beginInd = m ? (m.index + search.skip) : Number.POSITIVE_INFINITY;
|
||||
if(
|
||||
beginInd < bestStart ||
|
||||
(beginInd === bestStart && search.matcher.lastIndex > bestEnd)
|
||||
) {
|
||||
styleIndex = ind;
|
||||
bestStart = m.index;
|
||||
bestEnd = search.lastIndex;
|
||||
bestStart = beginInd;
|
||||
bestEnd = search.matcher.lastIndex;
|
||||
}
|
||||
});
|
||||
|
||||
return {end: bestEnd - 1, start: bestStart, styleIndex};
|
||||
return {end: bestEnd - 1, start: bestStart - 1, styleIndex};
|
||||
}
|
||||
|
||||
function combineAttrs(activeCount, active) {
|
||||
|
@ -6776,7 +6835,9 @@
|
|||
}
|
||||
|
||||
detach() {
|
||||
if(this.element.parentNode) {
|
||||
this.element.parentNode.removeChild(this.element);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
@ -6830,11 +6891,14 @@
|
|||
throw new Error('Invalid formatted text line: ' + formattedLine);
|
||||
}
|
||||
formattedLine.forEach(({text, attrs}) => {
|
||||
let element = text;
|
||||
if(attrs) {
|
||||
node.add(svg.el('tspan').attrs(attrs).add(text));
|
||||
} else {
|
||||
node.add(text);
|
||||
element = svg.el('tspan').attrs(attrs).add(text);
|
||||
if(attrs.filter) {
|
||||
element.attr('filter', svg.getTextFilter(attrs.filter));
|
||||
}
|
||||
}
|
||||
node.add(element);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -7282,12 +7346,40 @@
|
|||
this.dom = domWrapper;
|
||||
this.body = this.el('svg').attr('xmlns', NS).attr('version', '1.1');
|
||||
const fn = (textSizerFactory || defaultTextSizerFactory);
|
||||
this.textFilters = new Map();
|
||||
this.textSizer = new TextSizerWrapper(fn(this));
|
||||
|
||||
this.txt = this.txt.bind(this);
|
||||
this.el = this.el.bind(this);
|
||||
}
|
||||
|
||||
resetTextFilters() {
|
||||
this.textFilters.clear();
|
||||
}
|
||||
|
||||
registerTextFilter(name, id) {
|
||||
this.textFilters.set(name, {id, used: false});
|
||||
}
|
||||
|
||||
getTextFilter(name) {
|
||||
const filter = this.textFilters.get(name);
|
||||
if(!filter) {
|
||||
return 'none';
|
||||
}
|
||||
filter.used = true;
|
||||
return 'url(#' + filter.id + ')';
|
||||
}
|
||||
|
||||
getUsedTextFilterNames() {
|
||||
const used = [];
|
||||
for(const [name, filter] of this.textFilters) {
|
||||
if(filter.used) {
|
||||
used.push(name);
|
||||
}
|
||||
}
|
||||
return used;
|
||||
}
|
||||
|
||||
linearGradient(attrs, stops) {
|
||||
return this.el('linearGradient')
|
||||
.attrs(attrs)
|
||||
|
@ -7508,6 +7600,7 @@
|
|||
this.components = components || getComponents();
|
||||
this.svg = new SVG(new DOMWrapper(document), textSizerFactory);
|
||||
this.knownThemeDefs = new Set();
|
||||
this.knownTextFilterDefs = new Map();
|
||||
this.knownDefs = new Set();
|
||||
this.highlights = new Map();
|
||||
this.collapsed = new Set();
|
||||
|
@ -7524,6 +7617,7 @@
|
|||
this.prepareMeasurementsStage.bind(this);
|
||||
this.renderStage = this.renderStage.bind(this);
|
||||
this.addThemeDef = this.addThemeDef.bind(this);
|
||||
this.addThemeTextDef = this.addThemeTextDef.bind(this);
|
||||
this.addDef = this.addDef.bind(this);
|
||||
}
|
||||
|
||||
|
@ -7582,6 +7676,15 @@
|
|||
return namespacedName;
|
||||
}
|
||||
|
||||
addThemeTextDef(name, generator) {
|
||||
const namespacedName = this.namespace + name;
|
||||
if(!this.knownTextFilterDefs.has(name)) {
|
||||
const def = generator().attr('id', namespacedName);
|
||||
this.knownTextFilterDefs.set(name, def);
|
||||
}
|
||||
this.svg.registerTextFilter(name, namespacedName);
|
||||
}
|
||||
|
||||
addDef(name, generator) {
|
||||
let nm = name;
|
||||
let gen = generator;
|
||||
|
@ -7963,6 +8066,7 @@
|
|||
_reset(theme) {
|
||||
if(theme) {
|
||||
this.knownThemeDefs.clear();
|
||||
this.knownTextFilterDefs.clear();
|
||||
this.themeDefs.empty();
|
||||
}
|
||||
|
||||
|
@ -8060,7 +8164,11 @@
|
|||
this._reset(themeChanged);
|
||||
|
||||
this.metaCode.nodeValue = sequence.meta.code;
|
||||
this.theme.addDefs(this.addThemeDef);
|
||||
this.svg.resetTextFilters();
|
||||
this.theme.addDefs(this.addThemeDef, this.addThemeTextDef);
|
||||
for(const def of this.knownTextFilterDefs.values()) {
|
||||
def.detach();
|
||||
}
|
||||
|
||||
this.title.set({
|
||||
attrs: Object.assign({
|
||||
|
@ -8094,6 +8202,10 @@
|
|||
sequence.stages.forEach(this.renderStage);
|
||||
const bottomY = this.checkAgentRange(['[', ']'], this.currentY);
|
||||
|
||||
this.svg.getUsedTextFilterNames().forEach((name) => {
|
||||
this.themeDefs.add(this.knownTextFilterDefs.get(name));
|
||||
});
|
||||
|
||||
const stagesHeight = Math.max(bottomY - this.theme.actionMargin, 0);
|
||||
this.updateBounds(stagesHeight);
|
||||
|
||||
|
@ -8962,7 +9074,7 @@
|
|||
this.random.reset();
|
||||
}
|
||||
|
||||
addDefs(builder) {
|
||||
addDefs(builder, textBuilder) {
|
||||
builder('sketch_font', () => {
|
||||
const style = this.svg.el('style', null);
|
||||
// Font must be embedded for exporting as SVG / PNG
|
||||
|
@ -8974,6 +9086,8 @@
|
|||
);
|
||||
return style;
|
||||
});
|
||||
|
||||
super.addDefs(builder, textBuilder);
|
||||
}
|
||||
|
||||
vary(range, centre = 0) {
|
||||
|
|
|
@ -164,7 +164,9 @@ class WrappedElement {
|
|||
}
|
||||
|
||||
detach() {
|
||||
if(this.element.parentNode) {
|
||||
this.element.parentNode.removeChild(this.element);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -171,4 +171,11 @@ describe('SequenceDiagram', () => {
|
|||
|
||||
expect(content).toContain('<g class="region collapsed"');
|
||||
});
|
||||
|
||||
it('includes text filters if used', () => {
|
||||
const diagram = makeDiagram('title <highlight>foo</highlight>');
|
||||
const content = getSimplifiedContent(diagram);
|
||||
|
||||
expect(content).toContain('<filter id="highlight"');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,28 +1,36 @@
|
|||
const STYLES = [
|
||||
{
|
||||
attrs: {'font-style': 'italic'},
|
||||
begin: /[\s_~`]\*(?=\S)/g,
|
||||
end: /\S\*(?=[\s_~`])/g,
|
||||
begin: {matcher: /[\s_~`>]\*(?=\S)/g, skip: 1},
|
||||
end: {matcher: /\S\*(?=[\s_~`<])/g, skip: 1},
|
||||
}, {
|
||||
attrs: {'font-style': 'italic'},
|
||||
begin: /[\s*~`]_(?=\S)/g,
|
||||
end: /\S_(?=[\s*~`])/g,
|
||||
begin: {matcher: /[\s*~`>]_(?=\S)/g, skip: 1},
|
||||
end: {matcher: /\S_(?=[\s*~`<])/g, skip: 1},
|
||||
}, {
|
||||
attrs: {'font-weight': 'bolder'},
|
||||
begin: /[\s_~`]\*\*(?=\S)/g,
|
||||
end: /\S\*\*(?=[\s_~`])/g,
|
||||
begin: {matcher: /[\s_~`>]\*\*(?=\S)/g, skip: 1},
|
||||
end: {matcher: /\S\*\*(?=[\s_~`<])/g, skip: 1},
|
||||
}, {
|
||||
attrs: {'font-weight': 'bolder'},
|
||||
begin: /[\s*~`]__(?=\S)/g,
|
||||
end: /\S__(?=[\s*~`])/g,
|
||||
begin: {matcher: /[\s*~`>]__(?=\S)/g, skip: 1},
|
||||
end: {matcher: /\S__(?=[\s*~`<])/g, skip: 1},
|
||||
}, {
|
||||
attrs: {'text-decoration': 'line-through'},
|
||||
begin: /[\s_*`]~(?=\S)/g,
|
||||
end: /\S~(?=[\s_*`])/g,
|
||||
begin: {matcher: /[\s_*`>]~(?=\S)/g, skip: 1},
|
||||
end: {matcher: /\S~(?=[\s_*`<])/g, skip: 1},
|
||||
}, {
|
||||
attrs: {'font-family': 'Courier New,Liberation Mono,monospace'},
|
||||
begin: /[\s_*~.]`(?=\S)/g,
|
||||
end: /\S`(?=[\s_*~.])/g,
|
||||
begin: {matcher: /[\s_*~.>]`(?=\S)/g, skip: 1},
|
||||
end: {matcher: /\S`(?=[\s_*~.<])/g, skip: 1},
|
||||
}, {
|
||||
attrs: {'fill': '#DD0000'},
|
||||
begin: {matcher: /<red>/g, skip: 0},
|
||||
end: {matcher: /<\/red>/g, skip: 0},
|
||||
}, {
|
||||
attrs: {'filter': 'highlight'},
|
||||
begin: {matcher: /<highlight>/g, skip: 0},
|
||||
end: {matcher: /<\/highlight>/g, skip: 0},
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -34,19 +42,20 @@ function findNext(line, p, active) {
|
|||
|
||||
STYLES.forEach(({begin, end}, ind) => {
|
||||
const search = active[ind] ? end : begin;
|
||||
search.lastIndex = p;
|
||||
const m = search.exec(virtLine);
|
||||
if(m && (
|
||||
m.index < bestStart ||
|
||||
(m.index === bestStart && search.lastIndex > bestEnd)
|
||||
)) {
|
||||
search.matcher.lastIndex = p + 1 - search.skip;
|
||||
const m = search.matcher.exec(virtLine);
|
||||
const beginInd = m ? (m.index + search.skip) : Number.POSITIVE_INFINITY;
|
||||
if(
|
||||
beginInd < bestStart ||
|
||||
(beginInd === bestStart && search.matcher.lastIndex > bestEnd)
|
||||
) {
|
||||
styleIndex = ind;
|
||||
bestStart = m.index;
|
||||
bestEnd = search.lastIndex;
|
||||
bestStart = beginInd;
|
||||
bestEnd = search.matcher.lastIndex;
|
||||
}
|
||||
});
|
||||
|
||||
return {end: bestEnd - 1, start: bestStart, styleIndex};
|
||||
return {end: bestEnd - 1, start: bestStart - 1, styleIndex};
|
||||
}
|
||||
|
||||
function combineAttrs(activeCount, active) {
|
||||
|
|
|
@ -117,6 +117,26 @@ describe('Markdown Parser', () => {
|
|||
]]);
|
||||
});
|
||||
|
||||
it('recognises red styling', () => {
|
||||
const formatted = parser('a <red>b</red> c');
|
||||
|
||||
expect(formatted).toEqual([[
|
||||
{attrs: null, text: 'a '},
|
||||
{attrs: {'fill': '#DD0000'}, text: 'b'},
|
||||
{attrs: null, text: ' c'},
|
||||
]]);
|
||||
});
|
||||
|
||||
it('recognises highlight styling', () => {
|
||||
const formatted = parser('a <highlight>b</highlight> c');
|
||||
|
||||
expect(formatted).toEqual([[
|
||||
{attrs: null, text: 'a '},
|
||||
{attrs: {'filter': 'highlight'}, text: 'b'},
|
||||
{attrs: null, text: ' c'},
|
||||
]]);
|
||||
});
|
||||
|
||||
it('allows dots around monospace styling', () => {
|
||||
const formatted = parser('a.`b`.c');
|
||||
|
||||
|
@ -140,6 +160,18 @@ describe('Markdown Parser', () => {
|
|||
]]);
|
||||
});
|
||||
|
||||
it('recognises heavily combined styling', () => {
|
||||
const formatted = parser('**_`abc`_**');
|
||||
|
||||
expect(formatted).toEqual([[
|
||||
{attrs: {
|
||||
'font-family': MONO_FONT,
|
||||
'font-style': 'italic',
|
||||
'font-weight': 'bolder',
|
||||
}, text: 'abc'},
|
||||
]]);
|
||||
});
|
||||
|
||||
it('allows complex interactions between styles', () => {
|
||||
const formatted = parser('_a **b_ ~c~**');
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ const TOKENS = [
|
|||
start: /"/y,
|
||||
},
|
||||
{baseToken: {v: '...'}, start: /\.\.\./y},
|
||||
{end: /(?=[ \t\r\n:+~\-*!<>,])|$/y, start: /(?=[^ \t\r\n:+~\-*!<>,])/y},
|
||||
{end: /(?=[ \t\r\n:+~\-*!<,])|$/y, start: /(?=[^ \t\r\n:+~\-*!<,])/y},
|
||||
{
|
||||
end: /(?=[^~\-<>x])|[-~]x|[<>](?=x)|$/y,
|
||||
includeEnd: true,
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
/* eslint-disable max-statements */
|
||||
|
||||
import Tokeniser from './Tokeniser.mjs';
|
||||
|
||||
describe('Sequence Tokeniser', () => {
|
||||
|
@ -89,6 +91,20 @@ describe('Sequence Tokeniser', () => {
|
|||
]);
|
||||
});
|
||||
|
||||
it('accepts XML tag-like syntax', () => {
|
||||
const input = 'abc <def> >> ghi <<';
|
||||
const tokens = tokeniser.tokenise(input);
|
||||
|
||||
expect(tokens).toEqual([
|
||||
token({s: '', v: 'abc'}),
|
||||
token({s: ' ', v: '<'}),
|
||||
token({s: '', v: 'def>'}),
|
||||
token({s: ' ', v: '>>'}),
|
||||
token({s: ' ', v: 'ghi'}),
|
||||
token({s: ' ', v: '<<'}),
|
||||
]);
|
||||
});
|
||||
|
||||
it('stores line numbers', () => {
|
||||
const input = 'foo bar\nbaz';
|
||||
const tokens = tokeniser.tokenise(input);
|
||||
|
|
|
@ -76,6 +76,7 @@ export default class Renderer extends EventObject {
|
|||
this.components = components || getComponents();
|
||||
this.svg = new SVG(new DOMWrapper(document), textSizerFactory);
|
||||
this.knownThemeDefs = new Set();
|
||||
this.knownTextFilterDefs = new Map();
|
||||
this.knownDefs = new Set();
|
||||
this.highlights = new Map();
|
||||
this.collapsed = new Set();
|
||||
|
@ -92,6 +93,7 @@ export default class Renderer extends EventObject {
|
|||
this.prepareMeasurementsStage.bind(this);
|
||||
this.renderStage = this.renderStage.bind(this);
|
||||
this.addThemeDef = this.addThemeDef.bind(this);
|
||||
this.addThemeTextDef = this.addThemeTextDef.bind(this);
|
||||
this.addDef = this.addDef.bind(this);
|
||||
}
|
||||
|
||||
|
@ -150,6 +152,15 @@ export default class Renderer extends EventObject {
|
|||
return namespacedName;
|
||||
}
|
||||
|
||||
addThemeTextDef(name, generator) {
|
||||
const namespacedName = this.namespace + name;
|
||||
if(!this.knownTextFilterDefs.has(name)) {
|
||||
const def = generator().attr('id', namespacedName);
|
||||
this.knownTextFilterDefs.set(name, def);
|
||||
}
|
||||
this.svg.registerTextFilter(name, namespacedName);
|
||||
}
|
||||
|
||||
addDef(name, generator) {
|
||||
let nm = name;
|
||||
let gen = generator;
|
||||
|
@ -531,6 +542,7 @@ export default class Renderer extends EventObject {
|
|||
_reset(theme) {
|
||||
if(theme) {
|
||||
this.knownThemeDefs.clear();
|
||||
this.knownTextFilterDefs.clear();
|
||||
this.themeDefs.empty();
|
||||
}
|
||||
|
||||
|
@ -628,7 +640,11 @@ export default class Renderer extends EventObject {
|
|||
this._reset(themeChanged);
|
||||
|
||||
this.metaCode.nodeValue = sequence.meta.code;
|
||||
this.theme.addDefs(this.addThemeDef);
|
||||
this.svg.resetTextFilters();
|
||||
this.theme.addDefs(this.addThemeDef, this.addThemeTextDef);
|
||||
for(const def of this.knownTextFilterDefs.values()) {
|
||||
def.detach();
|
||||
}
|
||||
|
||||
this.title.set({
|
||||
attrs: Object.assign({
|
||||
|
@ -662,6 +678,10 @@ export default class Renderer extends EventObject {
|
|||
sequence.stages.forEach(this.renderStage);
|
||||
const bottomY = this.checkAgentRange(['[', ']'], this.currentY);
|
||||
|
||||
this.svg.getUsedTextFilterNames().forEach((name) => {
|
||||
this.themeDefs.add(this.knownTextFilterDefs.get(name));
|
||||
});
|
||||
|
||||
const stagesHeight = Math.max(bottomY - this.theme.actionMargin, 0);
|
||||
this.updateBounds(stagesHeight);
|
||||
|
||||
|
|
|
@ -57,8 +57,58 @@ export default class BaseTheme {
|
|||
// No-op
|
||||
}
|
||||
|
||||
addDefs() {
|
||||
// No-op
|
||||
addDefs(builder, textBuilder) {
|
||||
// Thanks, https://stackoverflow.com/a/12263962/1180785
|
||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=603157
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=917766
|
||||
textBuilder('highlight', () => this.svg.el('filter')
|
||||
.add(
|
||||
// Morph makes characters consistent
|
||||
this.svg.el('feMorphology').attrs({
|
||||
'in': 'SourceAlpha',
|
||||
'operator': 'dilate',
|
||||
'radius': '4',
|
||||
}),
|
||||
// Blur+thresh makes edges smooth
|
||||
this.svg.el('feGaussianBlur').attrs({
|
||||
'edgeMode': 'none',
|
||||
'stdDeviation': '3, 1.5',
|
||||
}),
|
||||
this.svg.el('feComponentTransfer').add(
|
||||
this.svg.el('feFuncA').attrs({
|
||||
'intercept': -70,
|
||||
'slope': 100,
|
||||
'type': 'linear',
|
||||
})
|
||||
),
|
||||
// Add colour
|
||||
this.svg.el('feComponentTransfer').add(
|
||||
this.svg.el('feFuncR').attrs({
|
||||
'intercept': 1,
|
||||
'slope': 0,
|
||||
'type': 'linear',
|
||||
}),
|
||||
this.svg.el('feFuncG').attrs({
|
||||
'intercept': 0.9375,
|
||||
'slope': 0,
|
||||
'type': 'linear',
|
||||
}),
|
||||
this.svg.el('feFuncB').attrs({
|
||||
'intercept': 0.09375,
|
||||
'slope': 0,
|
||||
'type': 'linear',
|
||||
}),
|
||||
this.svg.el('feFuncA').attrs({
|
||||
'slope': 0.7,
|
||||
'type': 'linear',
|
||||
})
|
||||
),
|
||||
// Draw text on top
|
||||
this.svg.el('feMerge').add(
|
||||
this.svg.el('feMergeNode'),
|
||||
this.svg.el('feMergeNode').attr('in', 'SourceGraphic')
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
getTitleAttrs() {
|
||||
|
|
|
@ -350,7 +350,7 @@ export default class SketchTheme extends BaseTheme {
|
|||
this.random.reset();
|
||||
}
|
||||
|
||||
addDefs(builder) {
|
||||
addDefs(builder, textBuilder) {
|
||||
builder('sketch_font', () => {
|
||||
const style = this.svg.el('style', null);
|
||||
// Font must be embedded for exporting as SVG / PNG
|
||||
|
@ -362,6 +362,8 @@ export default class SketchTheme extends BaseTheme {
|
|||
);
|
||||
return style;
|
||||
});
|
||||
|
||||
super.addDefs(builder, textBuilder);
|
||||
}
|
||||
|
||||
vary(range, centre = 0) {
|
||||
|
|
|
@ -189,12 +189,40 @@ export default class SVG {
|
|||
this.dom = domWrapper;
|
||||
this.body = this.el('svg').attr('xmlns', NS).attr('version', '1.1');
|
||||
const fn = (textSizerFactory || defaultTextSizerFactory);
|
||||
this.textFilters = new Map();
|
||||
this.textSizer = new TextSizerWrapper(fn(this));
|
||||
|
||||
this.txt = this.txt.bind(this);
|
||||
this.el = this.el.bind(this);
|
||||
}
|
||||
|
||||
resetTextFilters() {
|
||||
this.textFilters.clear();
|
||||
}
|
||||
|
||||
registerTextFilter(name, id) {
|
||||
this.textFilters.set(name, {id, used: false});
|
||||
}
|
||||
|
||||
getTextFilter(name) {
|
||||
const filter = this.textFilters.get(name);
|
||||
if(!filter) {
|
||||
return 'none';
|
||||
}
|
||||
filter.used = true;
|
||||
return 'url(#' + filter.id + ')';
|
||||
}
|
||||
|
||||
getUsedTextFilterNames() {
|
||||
const used = [];
|
||||
for(const [name, filter] of this.textFilters) {
|
||||
if(filter.used) {
|
||||
used.push(name);
|
||||
}
|
||||
}
|
||||
return used;
|
||||
}
|
||||
|
||||
linearGradient(attrs, stops) {
|
||||
return this.el('linearGradient')
|
||||
.attrs(attrs)
|
||||
|
|
|
@ -15,11 +15,14 @@ function populateSvgTextLine(svg, node, formattedLine) {
|
|||
throw new Error('Invalid formatted text line: ' + formattedLine);
|
||||
}
|
||||
formattedLine.forEach(({text, attrs}) => {
|
||||
let element = text;
|
||||
if(attrs) {
|
||||
node.add(svg.el('tspan').attrs(attrs).add(text));
|
||||
} else {
|
||||
node.add(text);
|
||||
element = svg.el('tspan').attrs(attrs).add(text);
|
||||
if(attrs.filter) {
|
||||
element.attr('filter', svg.getTextFilter(attrs.filter));
|
||||
}
|
||||
}
|
||||
node.add(element);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -76,6 +76,17 @@ describe('SVGTextBlock', () => {
|
|||
.toEqual('foo<tspan zig="zag">bar</tspan>');
|
||||
});
|
||||
|
||||
it('converts filter attributes using the registered filters', () => {
|
||||
svg.registerTextFilter('foo', 'local-foobar');
|
||||
block.set({formatted: [[
|
||||
{text: 'foo'},
|
||||
{attrs: {'filter': 'foo'}, text: 'bar'},
|
||||
]]});
|
||||
|
||||
expect(hold.childNodes[0].innerHTML)
|
||||
.toEqual('foo<tspan filter="url(#local-foobar)">bar</tspan>');
|
||||
});
|
||||
|
||||
it('re-uses text nodes when possible, adding more if needed', () => {
|
||||
block.set({formatted: [[{text: 'foo'}], [{text: 'bar'}]]});
|
||||
const [line0, line1] = hold.childNodes;
|
||||
|
|
|
@ -461,9 +461,20 @@ ImageRegion.loadURL = function(url, size = {}) {
|
|||
};
|
||||
|
||||
ImageRegion.loadSVG = function(svg, size = {}) {
|
||||
const blob = new Blob([svg], {type: 'image/svg+xml'});
|
||||
// Safari workaround:
|
||||
// Tweak SVG size directly rather than resizing image to keep quality high
|
||||
const resolution = size.resolution || 1;
|
||||
const svgCode = svg.replace(
|
||||
/<svg width="([^"]+)" height="([^"]+)"/,
|
||||
(m, w, h) => (
|
||||
'<svg width="' + (w * resolution) +
|
||||
'" height="' + (h * resolution) + '"'
|
||||
)
|
||||
);
|
||||
|
||||
const blob = new Blob([svgCode], {type: 'image/svg+xml'});
|
||||
const url = URL.createObjectURL(blob);
|
||||
return ImageRegion.loadURL(url, size)
|
||||
return ImageRegion.loadURL(url, Object.assign({}, size, {resolution: 1}))
|
||||
.then((region) => {
|
||||
URL.revokeObjectURL(url);
|
||||
return region;
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
<svg width="86.02510070800781" height="98" xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="-43.012550354003906 -93 86.02510070800781 98"><metadata>title "Foo **Bar**
|
||||
<svg width="96.69921875" height="124" xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="-48.349609375 -119 96.69921875 124"><metadata>title "Foo **Bar**
|
||||
_Baz_ `Zig`
|
||||
**_`Zag`_**"</metadata><defs></defs><defs><mask id="R0FullMask" maskUnits="userSpaceOnUse"><rect fill="#FFFFFF" height="98" width="86.02510070800781" x="-43.012550354003906" y="-93"></rect></mask><mask id="R0LineMask" maskUnits="userSpaceOnUse"><rect fill="#FFFFFF" height="98" width="86.02510070800781" x="-43.012550354003906" y="-93"></rect></mask></defs><g></g><g><text x="0" font-family="Helvetica,Arial,Liberation Sans,sans-serif" font-size="20" line-height="1.3" text-anchor="middle" class="title" y="-68">Foo <tspan font-weight="bolder">Bar</tspan></text><text x="0" font-family="Helvetica,Arial,Liberation Sans,sans-serif" font-size="20" line-height="1.3" text-anchor="middle" class="title" y="-42"><tspan font-style="italic">Baz</tspan> <tspan font-family="Courier New,Liberation Mono,monospace">Zig</tspan></text><text x="0" font-family="Helvetica,Arial,Liberation Sans,sans-serif" font-size="20" line-height="1.3" text-anchor="middle" class="title" y="-16"><tspan font-style="italic" font-weight="bolder" font-family="Courier New,Liberation Mono,monospace">Zag</tspan></text></g><g></g><g mask="url(#R0FullMask)"><g mask="url(#R0LineMask)"></g><g></g><g></g></g></svg>
|
||||
**_`Zag`_**
|
||||
<highlight>Back</highlight> <red>Text</red>"
|
||||
</metadata><defs><filter id="R0highlight"><feMorphology in="SourceAlpha" operator="dilate" radius="4"></feMorphology><feGaussianBlur edgeMode="none" stdDeviation="3, 1.5"></feGaussianBlur><feComponentTransfer><feFuncA intercept="-70" slope="100" type="linear"></feFuncA></feComponentTransfer><feComponentTransfer><feFuncR intercept="1" slope="0" type="linear"></feFuncR><feFuncG intercept="0.9375" slope="0" type="linear"></feFuncG><feFuncB intercept="0.09375" slope="0" type="linear"></feFuncB><feFuncA slope="0.7" type="linear"></feFuncA></feComponentTransfer><feMerge><feMergeNode></feMergeNode><feMergeNode in="SourceGraphic"></feMergeNode></feMerge></filter></defs><defs><mask id="R0FullMask" maskUnits="userSpaceOnUse"><rect fill="#FFFFFF" height="124" width="96.69921875" x="-48.349609375" y="-119"></rect></mask><mask id="R0LineMask" maskUnits="userSpaceOnUse"><rect fill="#FFFFFF" height="124" width="96.69921875" x="-48.349609375" y="-119"></rect></mask></defs><g></g><g><text x="0" class="title" font-family="Helvetica,Arial,Liberation Sans,sans-serif" font-size="20" line-height="1.3" text-anchor="middle" y="-94">Foo <tspan font-weight="bolder">Bar</tspan></text><text x="0" class="title" font-family="Helvetica,Arial,Liberation Sans,sans-serif" font-size="20" line-height="1.3" text-anchor="middle" y="-68"><tspan font-style="italic">Baz</tspan> <tspan font-family="Courier New,Liberation Mono,monospace">Zig</tspan></text><text x="0" class="title" font-family="Helvetica,Arial,Liberation Sans,sans-serif" font-size="20" line-height="1.3" text-anchor="middle" y="-42"><tspan font-style="italic" font-weight="bolder" font-family="Courier New,Liberation Mono,monospace">Zag</tspan></text><text x="0" class="title" font-family="Helvetica,Arial,Liberation Sans,sans-serif" font-size="20" line-height="1.3" text-anchor="middle" y="-16"><tspan filter="url(#R0highlight)">Back</tspan> <tspan fill="#DD0000">Text</tspan></text></g><g></g><g mask="url(#R0FullMask)"><g mask="url(#R0LineMask)"></g><g></g><g></g></g></svg>
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 2.2 KiB |
|
@ -266,6 +266,16 @@
|
|||
preview: 'A -> B: `mono`',
|
||||
title: 'Monospace markdown',
|
||||
},
|
||||
{
|
||||
code: '<red>{text}</red>',
|
||||
preview: 'A -> B: <red>red</red>',
|
||||
title: 'Red markdown',
|
||||
},
|
||||
{
|
||||
code: '<highlight>{text}</highlight>',
|
||||
preview: 'A -> B: <highlight>highlight</highlight>',
|
||||
title: 'Highlight markdown',
|
||||
},
|
||||
{
|
||||
code: '{Agent} is red',
|
||||
preview: 'headers box\nA is red\nbegin A',
|
||||
|
@ -479,7 +489,9 @@
|
|||
}
|
||||
|
||||
detach() {
|
||||
if(this.element.parentNode) {
|
||||
this.element.parentNode.removeChild(this.element);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -263,6 +263,16 @@ export default [
|
|||
preview: 'A -> B: `mono`',
|
||||
title: 'Monospace markdown',
|
||||
},
|
||||
{
|
||||
code: '<red>{text}</red>',
|
||||
preview: 'A -> B: <red>red</red>',
|
||||
title: 'Red markdown',
|
||||
},
|
||||
{
|
||||
code: '<highlight>{text}</highlight>',
|
||||
preview: 'A -> B: <highlight>highlight</highlight>',
|
||||
title: 'Highlight markdown',
|
||||
},
|
||||
{
|
||||
code: '{Agent} is red',
|
||||
preview: 'headers box\nA is red\nbegin A',
|
||||
|
|
Loading…
Reference in New Issue