chore: Use markdown-it instead of marked (#6123)
* chore: Use markdown-it instead of marked * Adds styling for markdown rendered content * fixes codeclimate issue * Fixes blockquote styles for widget in darkmode * fix: issue block quote color issue in light mode * fix: issue block quote color issue in light mode * Fixes blockquote color in dark mode * Remove usage of dark mode mixin in user bubble * chore: code clean up --------- Co-authored-by: Nithin David Thomas <1277421+nithindavid@users.noreply.github.com> Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Co-authored-by: iamsivin <iamsivin@gmail.com>
This commit is contained in:
@@ -380,14 +380,6 @@
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
border-left-color: var(--s-300);
|
||||
|
||||
p {
|
||||
color: var(--s-300);
|
||||
}
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
@@ -408,19 +400,8 @@
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
border-left-color: var(--w-100);
|
||||
|
||||
p {
|
||||
color: var(--w-100);
|
||||
}
|
||||
}
|
||||
|
||||
pre code {
|
||||
background: var(--color-background);
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -243,14 +243,14 @@ export default {
|
||||
return this.contentAttributes.translations || {};
|
||||
},
|
||||
displayQuotedButton() {
|
||||
if (!this.isIncoming) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.emailMessageContent.includes('<blockquote')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!this.isIncoming) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
translationsAvailable() {
|
||||
@@ -660,4 +660,71 @@ li.right {
|
||||
.context-menu {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Markdown styling */
|
||||
|
||||
.bubble .text-content {
|
||||
p code {
|
||||
background-color: var(--s-75);
|
||||
display: inline-block;
|
||||
line-height: 1;
|
||||
|
||||
border-radius: var(--border-radius-small);
|
||||
padding: var(--space-smaller);
|
||||
}
|
||||
|
||||
pre {
|
||||
background-color: var(--s-75);
|
||||
border-color: var(--s-75);
|
||||
color: var(--s-800);
|
||||
border-radius: var(--border-radius-normal);
|
||||
padding: var(--space-small);
|
||||
margin-top: var(--space-smaller);
|
||||
margin-bottom: var(--space-small);
|
||||
display: block;
|
||||
line-height: 1.7;
|
||||
white-space: pre-wrap;
|
||||
|
||||
code {
|
||||
background-color: transparent;
|
||||
color: var(--s-800);
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
blockquote {
|
||||
border-left: var(--space-micro) solid var(--s-75);
|
||||
color: var(--s-800);
|
||||
padding: var(--space-smaller) var(--space-small);
|
||||
margin: var(--space-smaller) 0;
|
||||
padding: var(--space-small) var(--space-small) 0 var(--space-normal);
|
||||
}
|
||||
}
|
||||
|
||||
.right .bubble .text-content {
|
||||
p code {
|
||||
background-color: var(--w-600);
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
pre {
|
||||
background-color: var(--w-800);
|
||||
border-color: var(--w-700);
|
||||
color: var(--white);
|
||||
|
||||
code {
|
||||
background-color: transparent;
|
||||
color: var(--white);
|
||||
}
|
||||
}
|
||||
|
||||
blockquote {
|
||||
border-left: var(--space-micro) solid var(--w-400);
|
||||
color: var(--white);
|
||||
|
||||
p {
|
||||
color: var(--w-75);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
<div
|
||||
class="message-text__wrap"
|
||||
:class="{
|
||||
'show--quoted': showQuotedContent,
|
||||
'hide--quoted': !showQuotedContent,
|
||||
'show--quoted': isQuotedContentPresent,
|
||||
'hide--quoted': !isQuotedContentPresent,
|
||||
}"
|
||||
>
|
||||
<div v-if="!isEmail" v-dompurify-html="message" class="text-content" />
|
||||
<letter v-else class="text-content" :html="message" />
|
||||
<button
|
||||
v-if="displayQuotedButton"
|
||||
v-if="showQuoteToggle"
|
||||
class="quoted-text--button"
|
||||
@click="toggleQuotedContent"
|
||||
>
|
||||
@@ -49,6 +49,20 @@ export default {
|
||||
showQuotedContent: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
isQuotedContentPresent() {
|
||||
if (!this.isEmail) {
|
||||
return this.message.includes('<blockquote');
|
||||
}
|
||||
return this.showQuotedContent;
|
||||
},
|
||||
showQuoteToggle() {
|
||||
if (!this.isEmail) {
|
||||
return false;
|
||||
}
|
||||
return this.displayQuotedButton;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
toggleQuotedContent() {
|
||||
this.showQuotedContent = !this.showQuotedContent;
|
||||
|
||||
@@ -1,57 +1,49 @@
|
||||
import { marked } from 'marked';
|
||||
import DOMPurify from 'dompurify';
|
||||
import { escapeHtml, afterSanitizeAttributes } from './HTMLSanitizer';
|
||||
import mila from 'markdown-it-link-attributes';
|
||||
import mentionPlugin from './markdownIt/link';
|
||||
const md = require('markdown-it')({
|
||||
html: false,
|
||||
xhtmlOut: true,
|
||||
breaks: true,
|
||||
langPrefix: 'language-',
|
||||
linkify: true,
|
||||
typographer: true,
|
||||
quotes: '\u201c\u201d\u2018\u2019',
|
||||
maxNesting: 20,
|
||||
})
|
||||
.use(mentionPlugin)
|
||||
.use(mila, {
|
||||
attrs: {
|
||||
class: 'link',
|
||||
rel: 'noreferrer noopener nofollow',
|
||||
target: '_blank',
|
||||
},
|
||||
});
|
||||
|
||||
const TWITTER_USERNAME_REGEX = /(^|[^@\w])@(\w{1,15})\b/g;
|
||||
const TWITTER_USERNAME_REPLACEMENT =
|
||||
'$1<a href="http://twitter.com/$2" target="_blank" rel="noreferrer nofollow noopener">@$2</a>';
|
||||
|
||||
const TWITTER_USERNAME_REPLACEMENT = '$1[@$2](http://twitter.com/$2)';
|
||||
const TWITTER_HASH_REGEX = /(^|\s)#(\w+)/g;
|
||||
const TWITTER_HASH_REPLACEMENT =
|
||||
'$1<a href="https://twitter.com/hashtag/$2" target="_blank" rel="noreferrer nofollow noopener">#$2</a>';
|
||||
|
||||
const USER_MENTIONS_REGEX = /mention:\/\/(user|team)\/(\d+)\/(.+)/gm;
|
||||
const TWITTER_HASH_REPLACEMENT = '$1[#$2](https://twitter.com/hashtag/$2)';
|
||||
|
||||
class MessageFormatter {
|
||||
constructor(message, isATweet = false, isAPrivateNote = false) {
|
||||
this.message = DOMPurify.sanitize(escapeHtml(message || ''));
|
||||
this.message = message || '';
|
||||
this.isAPrivateNote = isAPrivateNote;
|
||||
this.isATweet = isATweet;
|
||||
this.marked = marked;
|
||||
|
||||
const renderer = {
|
||||
heading(text) {
|
||||
return `<strong>${text}</strong>`;
|
||||
},
|
||||
link(url, title, text) {
|
||||
const mentionRegex = new RegExp(USER_MENTIONS_REGEX);
|
||||
if (url.match(mentionRegex)) {
|
||||
return `<span class="prosemirror-mention-node">${text}</span>`;
|
||||
}
|
||||
return `<a rel="noreferrer noopener nofollow" href="${url}" class="link" title="${title ||
|
||||
''}" target="_blank">${text}</a>`;
|
||||
},
|
||||
};
|
||||
this.marked.use({ renderer });
|
||||
}
|
||||
|
||||
formatMessage() {
|
||||
let updatedMessage = this.message;
|
||||
if (this.isATweet && !this.isAPrivateNote) {
|
||||
const withUserName = this.message.replace(
|
||||
updatedMessage = updatedMessage.replace(
|
||||
TWITTER_USERNAME_REGEX,
|
||||
TWITTER_USERNAME_REPLACEMENT
|
||||
);
|
||||
const withHash = withUserName.replace(
|
||||
updatedMessage = updatedMessage.replace(
|
||||
TWITTER_HASH_REGEX,
|
||||
TWITTER_HASH_REPLACEMENT
|
||||
);
|
||||
const markedDownOutput = marked(withHash);
|
||||
return markedDownOutput;
|
||||
}
|
||||
DOMPurify.addHook('afterSanitizeAttributes', afterSanitizeAttributes);
|
||||
return DOMPurify.sanitize(
|
||||
marked(this.message, { breaks: true, gfm: true })
|
||||
);
|
||||
return md.render(updatedMessage);
|
||||
}
|
||||
|
||||
get formattedMessage() {
|
||||
|
||||
69
app/javascript/shared/helpers/markdownIt/link.js
Normal file
69
app/javascript/shared/helpers/markdownIt/link.js
Normal file
@@ -0,0 +1,69 @@
|
||||
// Process [@mention](mention://user/1/Pranav)
|
||||
const USER_MENTIONS_REGEX = /mention:\/\/(user|team)\/(\d+)\/(.+)/gm;
|
||||
|
||||
const buildMentionTokens = () => (state, silent) => {
|
||||
var label;
|
||||
var labelEnd;
|
||||
var labelStart;
|
||||
var pos;
|
||||
var res;
|
||||
var token;
|
||||
var href = '';
|
||||
var max = state.posMax;
|
||||
|
||||
if (state.src.charCodeAt(state.pos) !== 0x5b /* [ */) {
|
||||
return false;
|
||||
}
|
||||
|
||||
labelStart = state.pos + 1;
|
||||
labelEnd = state.md.helpers.parseLinkLabel(state, state.pos, true);
|
||||
|
||||
// parser failed to find ']', so it's not a valid link
|
||||
if (labelEnd < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
label = state.src.slice(labelStart, labelEnd);
|
||||
pos = labelEnd + 1;
|
||||
|
||||
if (pos < max && state.src.charCodeAt(pos) === 0x28 /* ( */) {
|
||||
pos += 1;
|
||||
res = state.md.helpers.parseLinkDestination(state.src, pos, state.posMax);
|
||||
if (res.ok) {
|
||||
href = state.md.normalizeLink(res.str);
|
||||
if (state.md.validateLink(href)) {
|
||||
pos = res.pos;
|
||||
} else {
|
||||
href = '';
|
||||
}
|
||||
}
|
||||
pos += 1;
|
||||
}
|
||||
|
||||
if (!href.match(new RegExp(USER_MENTIONS_REGEX))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!silent) {
|
||||
state.pos = labelStart;
|
||||
state.posMax = labelEnd;
|
||||
|
||||
token = state.push('mention', '');
|
||||
token.href = href;
|
||||
token.content = label;
|
||||
}
|
||||
|
||||
state.pos = pos;
|
||||
state.posMax = max;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const renderMentions = () => (tokens, idx) => {
|
||||
return `<span class="prosemirror-mention-node">${tokens[idx].content}</span>`;
|
||||
};
|
||||
|
||||
export default function mentionPlugin(md) {
|
||||
md.renderer.rules.mention = renderMentions(md);
|
||||
md.inline.ruler.before('link', 'mention', buildMentionTokens(md));
|
||||
}
|
||||
@@ -6,14 +6,14 @@ describe('#MessageFormatter', () => {
|
||||
const message =
|
||||
'Chatwoot is an opensource tool. [Chatwoot](https://www.chatwoot.com)';
|
||||
expect(new MessageFormatter(message).formattedMessage).toMatch(
|
||||
'<p>Chatwoot is an opensource tool. <a title="" class="link" href="https://www.chatwoot.com" rel="noreferrer noopener nofollow" target="_blank">Chatwoot</a></p>'
|
||||
'<p>Chatwoot is an opensource tool. <a href="https://www.chatwoot.com" class="link" rel="noreferrer noopener nofollow" target="_blank">Chatwoot</a></p>'
|
||||
);
|
||||
});
|
||||
it('should format correctly', () => {
|
||||
const message =
|
||||
'Chatwoot is an opensource tool. https://www.chatwoot.com';
|
||||
expect(new MessageFormatter(message).formattedMessage).toMatch(
|
||||
'<p>Chatwoot is an opensource tool. <a title="" class="link" href="https://www.chatwoot.com" rel="noreferrer noopener nofollow" target="_blank">https://www.chatwoot.com</a></p>'
|
||||
'<p>Chatwoot is an opensource tool. <a href="https://www.chatwoot.com" class="link" rel="noreferrer noopener nofollow" target="_blank">https://www.chatwoot.com</a></p>'
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -22,7 +22,8 @@ describe('#MessageFormatter', () => {
|
||||
it('should format correctly', () => {
|
||||
const message = '### opensource \n ## tool';
|
||||
expect(new MessageFormatter(message).formattedMessage).toMatch(
|
||||
'<strong>opensource</strong><strong>tool</strong>'
|
||||
`<h3>opensource</h3>
|
||||
<h2>tool</h2>`
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -39,7 +40,7 @@ describe('#MessageFormatter', () => {
|
||||
expect(
|
||||
new MessageFormatter(message, true, false).formattedMessage
|
||||
).toMatch(
|
||||
'<p><a href="http://twitter.com/chatwootapp" target="_blank" rel="noreferrer nofollow noopener">@chatwootapp</a> is an opensource tool thanks @longnonexistenttwitterusername</p>'
|
||||
'<p><a href="http://twitter.com/chatwootapp" class="link" rel="noreferrer noopener nofollow" target="_blank">@chatwootapp</a> is an opensource tool thanks @longnonexistenttwitterusername</p>'
|
||||
);
|
||||
});
|
||||
|
||||
@@ -48,7 +49,7 @@ describe('#MessageFormatter', () => {
|
||||
expect(
|
||||
new MessageFormatter(message, true, false).formattedMessage
|
||||
).toMatch(
|
||||
'<p><a href="https://twitter.com/hashtag/chatwootapp" target="_blank" rel="noreferrer nofollow noopener">#chatwootapp</a> is an opensource tool</p>'
|
||||
'<p><a href="https://twitter.com/hashtag/chatwootapp" class="link" rel="noreferrer noopener nofollow" target="_blank">#chatwootapp</a> is an opensource tool</p>'
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -90,7 +91,8 @@ describe('#MessageFormatter', () => {
|
||||
const message =
|
||||
'[xssLink](javascript:alert(document.cookie))\n[normalLink](https://google.com)**I am a bold text paragraph**';
|
||||
expect(new MessageFormatter(message).formattedMessage).toMatch(
|
||||
'<p><a title="" class="link" rel="noreferrer noopener nofollow" target="_blank">xssLink</a><br><a title="" class="link" href="https://google.com" rel="noreferrer noopener nofollow" target="_blank">normalLink</a><strong>I am a bold text paragraph</strong></p>'
|
||||
`<p>[xssLink](javascript:alert(document.cookie))<br />
|
||||
<a href="https://google.com" class="link" rel="noreferrer noopener nofollow" target="_blank">normalLink</a><strong>I am a bold text paragraph</strong></p>`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -26,6 +26,9 @@ $space-big: 4rem;
|
||||
$space-jumbo: 5rem;
|
||||
$space-mega: 6.25rem;
|
||||
|
||||
$border-radius-small: 0.1875rem;
|
||||
$border-radius-normal: 0.3125rem;
|
||||
|
||||
// font-weight
|
||||
$font-weight-feather: 100;
|
||||
$font-weight-light: 300;
|
||||
@@ -74,8 +77,16 @@ $line-height: 1;
|
||||
$footer-height: 11.2rem;
|
||||
$header-expanded-height: $space-medium * 10;
|
||||
|
||||
$font-family: 'Inter', -apple-system, system-ui, BlinkMacSystemFont, 'Segoe UI',
|
||||
Roboto, 'Helvetica Neue', Tahoma, Arial, sans-serif;
|
||||
$font-family: 'Inter',
|
||||
-apple-system,
|
||||
system-ui,
|
||||
BlinkMacSystemFont,
|
||||
'Segoe UI',
|
||||
Roboto,
|
||||
'Helvetica Neue',
|
||||
Tahoma,
|
||||
Arial,
|
||||
sans-serif;
|
||||
|
||||
// Break points
|
||||
$break-point-medium: 667px;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.file-uploads .attachment-button+label {
|
||||
.file-uploads .attachment-button + label {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
}
|
||||
|
||||
.agent-message-wrap {
|
||||
+.agent-message-wrap {
|
||||
+ .agent-message-wrap {
|
||||
margin-top: $space-micro;
|
||||
|
||||
.agent-message .chat-bubble {
|
||||
@@ -69,11 +69,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
+.user-message-wrap {
|
||||
+ .user-message-wrap {
|
||||
margin-top: $space-normal;
|
||||
}
|
||||
|
||||
&.has-response+.user-message-wrap {
|
||||
&.has-response + .user-message-wrap {
|
||||
margin-top: $space-micro;
|
||||
|
||||
.chat-bubble {
|
||||
@@ -81,7 +81,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.has-response+.agent-message-wrap {
|
||||
&.has-response + .agent-message-wrap {
|
||||
margin-top: $space-normal;
|
||||
}
|
||||
}
|
||||
@@ -117,7 +117,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.user.has-attachment {
|
||||
.icon-wrap {
|
||||
color: $color-white;
|
||||
@@ -129,7 +128,7 @@
|
||||
}
|
||||
|
||||
.user-message-wrap {
|
||||
+.user-message-wrap {
|
||||
+ .user-message-wrap {
|
||||
margin-top: $space-micro;
|
||||
|
||||
.user-message .chat-bubble {
|
||||
@@ -137,7 +136,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
+.agent-message-wrap {
|
||||
+ .agent-message-wrap {
|
||||
margin-top: $space-normal;
|
||||
}
|
||||
}
|
||||
@@ -147,7 +146,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.unread-messages {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -168,7 +166,7 @@
|
||||
border: 1px solid $color-border-dark;
|
||||
}
|
||||
|
||||
+.chat-bubble-wrap {
|
||||
+ .chat-bubble-wrap {
|
||||
.chat-bubble {
|
||||
border-top-left-radius: $space-smaller;
|
||||
}
|
||||
@@ -190,7 +188,7 @@
|
||||
border-radius: $space-two;
|
||||
}
|
||||
|
||||
+.chat-bubble-wrap {
|
||||
+ .chat-bubble-wrap {
|
||||
.chat-bubble {
|
||||
border-top-right-radius: $space-smaller;
|
||||
}
|
||||
@@ -206,7 +204,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.chat-bubble {
|
||||
@include light-shadow;
|
||||
border-radius: $space-two;
|
||||
@@ -214,6 +211,7 @@
|
||||
display: inline-block;
|
||||
font-size: $font-size-default;
|
||||
line-height: 1.5;
|
||||
max-width: 100%;
|
||||
padding: $space-slab $space-normal;
|
||||
text-align: left;
|
||||
word-break: break-word;
|
||||
@@ -222,7 +220,7 @@
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
>a {
|
||||
> a {
|
||||
color: $color-primary;
|
||||
word-break: break-all;
|
||||
}
|
||||
@@ -234,7 +232,7 @@
|
||||
&.user {
|
||||
border-bottom-right-radius: $space-smaller;
|
||||
|
||||
>a {
|
||||
> a {
|
||||
color: $color-white;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
!isCards && !isOptions && !isForm && !isArticle && !isCards && !isCSAT
|
||||
"
|
||||
class="chat-bubble agent"
|
||||
:class="$dm('bg-white', 'dark:bg-slate-700')"
|
||||
:class="$dm('bg-white', 'dark:bg-slate-700 has-dark-mode')"
|
||||
>
|
||||
<div
|
||||
v-dompurify-html="formatMessage(message, false)"
|
||||
@@ -142,14 +142,3 @@ export default {
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '~widget/assets/scss/variables.scss';
|
||||
|
||||
.chat-bubble .message-content::v-deep pre {
|
||||
background: $color-primary-light;
|
||||
color: $color-body;
|
||||
overflow-y: auto;
|
||||
padding: $space-smaller;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -35,3 +35,56 @@ export default {
|
||||
max-width: 90%;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
@import '~widget/assets/scss/variables.scss';
|
||||
|
||||
.chat-bubble .message-content,
|
||||
.chat-bubble.user {
|
||||
p code {
|
||||
background-color: var(--s-75);
|
||||
display: inline-block;
|
||||
line-height: 1;
|
||||
|
||||
border-radius: $border-radius-small;
|
||||
padding: $space-smaller;
|
||||
}
|
||||
|
||||
pre {
|
||||
overflow-y: auto;
|
||||
background-color: var(--s-75);
|
||||
border-color: var(--s-75);
|
||||
color: var(--s-800);
|
||||
border-radius: $border-radius-normal;
|
||||
padding: $space-small;
|
||||
margin-top: $space-smaller;
|
||||
margin-bottom: $space-small;
|
||||
display: block;
|
||||
line-height: 1.7;
|
||||
white-space: pre-wrap;
|
||||
|
||||
code {
|
||||
background-color: transparent;
|
||||
color: var(--s-800);
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
blockquote {
|
||||
border-left: $space-micro solid var(--s-75);
|
||||
color: var(--s-800);
|
||||
padding: $space-smaller $space-small;
|
||||
margin: $space-smaller 0;
|
||||
padding: $space-small $space-small 0 $space-normal;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.chat-bubble.agent.has-dark-mode {
|
||||
blockquote {
|
||||
border-color: var(--s-200);
|
||||
color: var(--s-50);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -38,10 +38,31 @@ export default {
|
||||
<style lang="scss" scoped>
|
||||
@import '~widget/assets/scss/variables.scss';
|
||||
|
||||
.chat-bubble.user::v-deep pre {
|
||||
background: $color-primary-light;
|
||||
color: $color-body;
|
||||
overflow: auto;
|
||||
padding: $space-smaller;
|
||||
.chat-bubble.user::v-deep {
|
||||
p code {
|
||||
background-color: var(--w-600);
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
pre {
|
||||
background-color: var(--w-800);
|
||||
border-color: var(--w-700);
|
||||
color: var(--white);
|
||||
|
||||
code {
|
||||
background-color: transparent;
|
||||
color: var(--white);
|
||||
}
|
||||
}
|
||||
|
||||
blockquote {
|
||||
border-left: $space-micro solid var(--w-400);
|
||||
background: var(--s-25);
|
||||
border-color: var(--s-200);
|
||||
|
||||
p {
|
||||
color: var(--s-800);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user