mirror of
https://github.com/zulip/zulip.git
synced 2025-11-02 04:53:36 +00:00
We've for a while been finding that copy/paste of LaTeX only worked if we had a more JS-heavy copy handler that directly copies the HTML/text using events, rather than just allowing the browser's native copying logic to do its thing. We've discovered the bug was that the `<annotation>` tag in MathML is not rendered in some browsers which results in default copy behavior of the browser breaking KaTeX copy-pasting and quoting by omitting the `<annotation>` in the copied HTML as Chrome and possibly other browsers select only what is visible. From https://www.w3.org/TR/selection-api/ > Chrome 16 dev behaves as you'd expect given its Selection implementation. It refuses to select anything that's not visible, so it's almost always wrong. Opera 11.50 just does nothing in all my tests, as usual. This can be observed in the `paste_html` fixtures present in https://github.com/zulip/zulip/pull/33993#issuecomment-2725417637. We fix this by simply overriding the `display:none` property from the <annotation> tag in the user agent stylesheet, which lets the browser select and ultimately copy it which results in the annotation being present in the `paste_html` and restore the working of KaTeX quoting/copy-pasting. This appears to have no visible impact, since browsers don't actually have a way to display the annotations.
981 lines
28 KiB
CSS
981 lines
28 KiB
CSS
.rendered_markdown {
|
|
& p {
|
|
margin: 0 0 var(--markdown-interelement-space-px);
|
|
}
|
|
|
|
/* The spacing between two paragraphs is double the usual value.
|
|
We coordinate this spacing matches so that it matches the
|
|
spacing between paragraphs in two consecutive 1-line messages. */
|
|
& p + p {
|
|
margin-top: var(--markdown-interelement-doubled-space-px);
|
|
}
|
|
|
|
& ul {
|
|
margin: 0 0 var(--markdown-interelement-space-px) 0;
|
|
margin-inline-start: 3.5ch;
|
|
}
|
|
|
|
& ol {
|
|
margin: 0 0 var(--markdown-interelement-space-px) 0;
|
|
margin-inline-start: 3.5ch;
|
|
}
|
|
|
|
/* We set margin according to the length of the longest list counter. */
|
|
.counter-length-1 {
|
|
margin-inline-start: 2.5ch;
|
|
}
|
|
|
|
.counter-length-2 {
|
|
margin-inline-start: 3.5ch;
|
|
}
|
|
|
|
.counter-length-3 {
|
|
margin-inline-start: 4.5ch;
|
|
}
|
|
|
|
.counter-length-4 {
|
|
margin-inline-start: 5.5ch;
|
|
}
|
|
|
|
.counter-length-5 {
|
|
margin-inline-start: 6.5ch;
|
|
}
|
|
|
|
.counter-length-6 {
|
|
margin-inline-start: 7.5ch;
|
|
}
|
|
|
|
& hr {
|
|
border-bottom: 1px solid hsl(0deg 0% 87%);
|
|
border-top: 1px solid hsl(0deg 0% 87%);
|
|
/* Override Bootstrap with doubled interelement space */
|
|
margin: calc(var(--markdown-interelement-space-px) * 2) 0;
|
|
}
|
|
|
|
/* Headings */
|
|
& h1,
|
|
h2,
|
|
h3,
|
|
h4,
|
|
h5,
|
|
h6 {
|
|
font-weight: 600;
|
|
line-height: 1.4;
|
|
/* Headings take a margin-top because of the pronounced extra
|
|
space they require, but are zeroed out below when they open
|
|
a message. */
|
|
margin-top: 15px;
|
|
margin-bottom: var(--markdown-interelement-space-px);
|
|
}
|
|
|
|
/* Headings: Ensure that messages that start with a heading don't have
|
|
a weirdly blank area at the very start of the message. */
|
|
& h1:first-child,
|
|
h2:first-child,
|
|
h3:first-child,
|
|
h4:first-child,
|
|
h5:first-child,
|
|
h6:first-child {
|
|
margin-top: 0;
|
|
}
|
|
|
|
/* We use a modest progression of heading sizes to make them stand out
|
|
from normal next but avoid taking up too much space. */
|
|
& h1 {
|
|
font-size: 1.4em;
|
|
}
|
|
|
|
& h2 {
|
|
font-size: 1.3em;
|
|
}
|
|
|
|
& h3 {
|
|
font-size: 1.2em;
|
|
}
|
|
|
|
& h4 {
|
|
font-size: 1.1em;
|
|
}
|
|
|
|
& h5 {
|
|
font-size: 1.05em;
|
|
}
|
|
|
|
& h6 {
|
|
font-size: 1em;
|
|
}
|
|
|
|
/* Formatting for blockquotes */
|
|
& blockquote {
|
|
padding-left: 5px;
|
|
margin: 0 0 var(--markdown-interelement-space-px) 10px;
|
|
border-left: 5px solid hsl(0deg 0% 87%);
|
|
}
|
|
|
|
&.rtl blockquote {
|
|
padding-left: unset;
|
|
padding-right: 5px;
|
|
margin-left: unset;
|
|
margin-right: 10px;
|
|
border-left: unset;
|
|
border-right: 5px solid hsl(0deg 0% 87%);
|
|
}
|
|
|
|
/* Formatting for Markdown tables */
|
|
& table {
|
|
padding-right: 10px;
|
|
margin: 0 5px var(--markdown-interelement-space-px);
|
|
width: 99%;
|
|
display: block;
|
|
max-width: fit-content;
|
|
overflow-x: auto;
|
|
white-space: nowrap;
|
|
border-collapse: collapse;
|
|
}
|
|
|
|
& thead {
|
|
background-color: var(--color-background-rendered-markdown-thead);
|
|
}
|
|
|
|
& tr {
|
|
display: table-row;
|
|
vertical-align: inherit;
|
|
}
|
|
|
|
& tr th {
|
|
border: 1px solid var(--color-border-rendered-markdown-table);
|
|
padding: 4px;
|
|
text-align: left;
|
|
}
|
|
|
|
& tr td {
|
|
border: 1px solid var(--color-border-rendered-markdown-table);
|
|
padding: 4px;
|
|
}
|
|
|
|
/* Emoji; sized to be easily understood while not overwhelming text. */
|
|
.emoji {
|
|
/* The box for emoji is allowed to be larger than the size of the
|
|
line-height. */
|
|
height: var(--length-line-oversize-block);
|
|
width: var(--length-line-oversize-block);
|
|
/* A negative top and bottom margin adjustment allows emoji
|
|
to size larger than the size of the line, without disturbing
|
|
the surrounding lines of text. */
|
|
margin: var(--length-line-oversize-block-margin-adjust) auto;
|
|
/* We set the alignment programmatically, as an em value.
|
|
Because the negative margins above are equal, top and bottom,
|
|
this vertical offset value works without adjustment for
|
|
oversize emoji blocks. */
|
|
vertical-align: var(--line-fitted-vertical-align-offset-em);
|
|
}
|
|
|
|
/* Mentions and alert words */
|
|
.user-group-mention,
|
|
.user-mention,
|
|
.topic-mention {
|
|
padding: 0 3px;
|
|
border-radius: 3px;
|
|
white-space: nowrap;
|
|
/* Reduce the font-size to reduce the
|
|
footprint of the background highlight. */
|
|
font-size: 0.95em;
|
|
}
|
|
|
|
.mention-content-wrapper {
|
|
/* Restore the font-size to match the rest
|
|
of the message area. */
|
|
font-size: 1.0526em;
|
|
}
|
|
|
|
.user-mention {
|
|
color: var(--color-text-other-mention);
|
|
background-color: var(--color-background-text-direct-mention);
|
|
|
|
&.user-mention-me {
|
|
color: var(--color-text-self-direct-mention);
|
|
font-weight: 600;
|
|
}
|
|
|
|
&:hover {
|
|
background-color: var(--color-background-text-hover-direct-mention);
|
|
}
|
|
}
|
|
|
|
.user-mention,
|
|
.user-group-mention {
|
|
/* We have to explicitly mention this for compose box preview
|
|
where cursor is set to not-allowed */
|
|
cursor: pointer;
|
|
}
|
|
|
|
/* We show the same cursor as the parent element for `@all`
|
|
mention */
|
|
.user-mention-all {
|
|
cursor: inherit;
|
|
}
|
|
|
|
.user-mention[data-user-id="*"],
|
|
.user-group-mention,
|
|
.topic-mention {
|
|
color: var(--color-text-other-mention);
|
|
background-color: var(--color-background-text-group-mention);
|
|
|
|
&.user-mention-me {
|
|
color: var(--color-text-self-group-mention);
|
|
font-weight: 600;
|
|
}
|
|
}
|
|
|
|
.stream-topic {
|
|
/* Display whitespace within topics. */
|
|
white-space: pre-wrap;
|
|
}
|
|
|
|
.user-group-mention {
|
|
&:hover {
|
|
background-color: var(--color-background-text-hover-group-mention);
|
|
}
|
|
}
|
|
|
|
.alert-word {
|
|
background-color: var(--color-background-alert-word);
|
|
}
|
|
|
|
/* Timestamps */
|
|
& time {
|
|
background: hsl(0deg 0% 93%);
|
|
border-radius: 3px;
|
|
box-shadow: 0 0 0 1px hsl(0deg 0% 80%);
|
|
white-space: nowrap;
|
|
margin: 0 2px;
|
|
/* Reduce the font-size to reduce the
|
|
footprint of the timestamp block. */
|
|
font-size: 0.95em;
|
|
|
|
.timestamp-content-wrapper {
|
|
/* Restore the font-size to match the rest
|
|
of the message area, and apply the layout
|
|
for the icon. */
|
|
font-size: 1.0526em;
|
|
padding: 0 0.2em;
|
|
display: inline-flex;
|
|
align-items: baseline;
|
|
gap: 3px;
|
|
}
|
|
|
|
.markdown-timestamp-icon {
|
|
align-self: center;
|
|
}
|
|
}
|
|
|
|
/* LaTeX styling */
|
|
.katex-display {
|
|
/* KaTeX sometimes overdraws its bounding box by a little, so we
|
|
enlarge its scrolling area by adding 3px of padding to its top
|
|
and bottom. To prevent what will appear as extra whitespace,
|
|
we reduce surrounding margins by the same 3px. */
|
|
padding: 3px 0;
|
|
margin: -3px 0;
|
|
overflow: auto hidden;
|
|
}
|
|
|
|
.katex-mathml annotation {
|
|
/* Because annotations are never displayable, browser user-agent
|
|
stylesheets mark them as not displayable, which has the
|
|
side effect of having them not be included in the HTML
|
|
version of copying the content. Override this, so KaTeX can
|
|
be copy/pasted within Zulip. */
|
|
user-select: all;
|
|
display: inline;
|
|
}
|
|
|
|
.tex-error {
|
|
color: hsl(0deg 0% 50%);
|
|
}
|
|
|
|
/* Spoiler styling */
|
|
.spoiler-block {
|
|
border: hsl(0deg 0% 50%) 1px solid;
|
|
padding: 2px 8px 2px 10px;
|
|
border-radius: 10px;
|
|
/* Space any subsequent Markdown content the same
|
|
distance as adjacent paragraphs are spaced. */
|
|
margin: 0 0 var(--markdown-interelement-doubled-space-px);
|
|
|
|
.spoiler-header {
|
|
/* We use flexbox to display the spoiler message
|
|
and button. */
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 8px 5px;
|
|
font-weight: bold;
|
|
|
|
.spoiler-header-text {
|
|
/* Disallow margin from ordinary rendered-markdown
|
|
elements. The header's vertical space is handled
|
|
independently by padding on .spoiler-header. */
|
|
margin-bottom: 0;
|
|
/* Message grows to push the arrow to the right,
|
|
but shrinks so as to allow long multi-line
|
|
spoiler messages to wrap. */
|
|
flex: 1 1 auto;
|
|
}
|
|
|
|
time {
|
|
/* Time tag pushes the arrow out of view when overflow,
|
|
adding white-space property prevents this by wrapping
|
|
the element */
|
|
white-space: normal;
|
|
}
|
|
}
|
|
|
|
.spoiler-content {
|
|
overflow: hidden;
|
|
border-top: hsl(0deg 0% 50%) 0 solid;
|
|
transition:
|
|
/* stylelint-disable-next-line plugin/no-low-performance-animation-properties */
|
|
height 0.4s ease-in-out,
|
|
border-top 0.4s step-end,
|
|
padding 0.4s step-end; /* stylelint-disable-line plugin/no-low-performance-animation-properties */
|
|
padding: 0;
|
|
height: 0;
|
|
|
|
&.spoiler-content-open {
|
|
border-top: hsl(0deg 0% 50%) 1px solid;
|
|
transition:
|
|
/* stylelint-disable-next-line plugin/no-low-performance-animation-properties */
|
|
height 0.4s ease-in-out,
|
|
border-top 0.4s step-start,
|
|
padding 0.4s step-start; /* stylelint-disable-line plugin/no-low-performance-animation-properties */
|
|
padding: 5px;
|
|
height: auto;
|
|
}
|
|
}
|
|
|
|
.spoiler-button {
|
|
/* Keep the button to a consistent right-hand edge. */
|
|
padding-right: 3px;
|
|
|
|
&:hover .spoiler-arrow {
|
|
&::before,
|
|
&::after {
|
|
background-color: hsl(0deg 0% 50%);
|
|
}
|
|
}
|
|
}
|
|
|
|
.spoiler-arrow {
|
|
float: right;
|
|
width: 15px;
|
|
cursor: pointer;
|
|
transition: transform 0.4s ease;
|
|
transform: rotate(45deg);
|
|
|
|
&::before,
|
|
&::after {
|
|
position: absolute;
|
|
content: "";
|
|
display: inline-block;
|
|
width: 12px;
|
|
height: 3px;
|
|
background-color: hsl(0deg 0% 83%);
|
|
transition: transform 0.4s ease;
|
|
}
|
|
|
|
&::after {
|
|
position: absolute;
|
|
transform: rotate(90deg);
|
|
top: -5px;
|
|
left: 5px;
|
|
}
|
|
|
|
&.spoiler-button-open {
|
|
transform: rotate(45deg) translate(-5px, -5px);
|
|
|
|
&::before {
|
|
transform: translate(10px, 0);
|
|
}
|
|
|
|
&::after {
|
|
transform: rotate(90deg) translate(10px, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* embedded link previews */
|
|
.message_inline_image_title {
|
|
font-weight: bold;
|
|
}
|
|
|
|
.twitter-image,
|
|
.message_inline_image {
|
|
position: relative;
|
|
margin-bottom: var(--markdown-interelement-space-px);
|
|
margin-right: 5px;
|
|
|
|
/* Sizing CSS for inline images requires care, because images load
|
|
asynchronously, and browsers will unfortunately jump your
|
|
scroll position when elements load above the current
|
|
position in the message feed in a way that changes the
|
|
height of elements. (As of March 2022, both Firefox and
|
|
Chrome exhibit this problem, though in Chrome it is pretty
|
|
subtle).
|
|
|
|
We prevent this by setting a fixed height for inline
|
|
previews. 8em is chosen because we don't want images to
|
|
overwhelm conversation in message feeds, as it does in chat
|
|
tools that show images at half-screen height or larger.
|
|
|
|
If there are several images next to each other, we display
|
|
them in a grid format; the same considerations requires
|
|
that either use a scrollable region or set a fixed width
|
|
for images so that the browser statically knows whether
|
|
it'll need to overflow. We choose fixed width here. */
|
|
height: 8em;
|
|
width: 12em;
|
|
|
|
/* Inline image containers also need an inline-block display in order
|
|
to implement the desired grid layout. */
|
|
display: inline-block;
|
|
|
|
/* Set a background for the image; the background will be visible for
|
|
messages whose aspect ratio is different from that of this
|
|
container. */
|
|
border: solid 1px transparent;
|
|
transition: background 0.3s ease;
|
|
background: hsl(0deg 0% 0% / 3%);
|
|
|
|
&:first-child {
|
|
margin-top: var(--markdown-interelement-space-px);
|
|
}
|
|
|
|
&:hover {
|
|
background: hsl(0deg 0% 0% / 15%);
|
|
}
|
|
|
|
& a {
|
|
display: block;
|
|
height: 100%;
|
|
width: 100%;
|
|
}
|
|
|
|
img.image-loading-placeholder {
|
|
content: url("../images/loading/loader-black.svg");
|
|
}
|
|
}
|
|
|
|
&.rtl .twitter-image,
|
|
&.rtl .message_inline_image {
|
|
margin-left: unset;
|
|
margin-right: 5px;
|
|
}
|
|
|
|
/* In browsers that support `:has()`, we pull
|
|
`.rendered_markdown` out of the baseline group
|
|
formed with EDITED/MOVED markers and the
|
|
timestamp when the first child of the rendered
|
|
markdown is a horizontal rule, a media element,
|
|
or KaTeX. */
|
|
&:has(> hr:first-child),
|
|
&:has(> .message_inline_image:first-child),
|
|
&:has(> p:first-child > .katex-display) {
|
|
align-self: center;
|
|
}
|
|
|
|
/* But for browsers that don't support :has(),
|
|
we provide a small layout hack using an
|
|
inline grid. */
|
|
@supports not selector(:has(*)) {
|
|
p:first-child > .katex-display {
|
|
/* KaTeX should take up 100% of the message box,
|
|
so that KaTeX's own CSS for centering still works. */
|
|
width: 100%;
|
|
}
|
|
|
|
p:first-child > .katex-display,
|
|
.message_inline_image {
|
|
/* We'll display this bit of media as
|
|
an inline grid. That will allow us
|
|
to put beneath the image a piece of
|
|
invisible ::before content that we'll
|
|
generate to participate in the
|
|
messagebox-content grid's baseline
|
|
group. */
|
|
display: inline-grid;
|
|
/* We create a grid area called
|
|
"media", so that both the inner
|
|
<a> element and the ::before content
|
|
can sit there. `auto` will take on
|
|
the height and width otherwise assigned
|
|
to the .message_inline_image. Setting
|
|
the min value to 0 on minmax() ensures
|
|
that media larger than those dimensions
|
|
don't cause a grid blowout. */
|
|
grid-template: "media" minmax(0, auto) / minmax(0, auto);
|
|
}
|
|
|
|
p:first-child > .katex-display > .katex,
|
|
.message_inline_image a,
|
|
.message_inline_image video {
|
|
/* We explicitly place the containing
|
|
media element in the thumbnail area. */
|
|
grid-area: media;
|
|
}
|
|
|
|
p:first-child > .katex-display::before,
|
|
.message_inline_image::before {
|
|
/* We generate a single . here to create
|
|
text content enough for a baseline.
|
|
Generated content is not generally
|
|
read aloud to screen readers. */
|
|
content: ".";
|
|
/* We color the generated . transparently,
|
|
so it won't be visible when the media
|
|
doesn't cover the entire area. */
|
|
color: transparent;
|
|
/* And we explicitly place the . in the thumbnail
|
|
area, too. Otherwise, grid would generate a new
|
|
column track for it to sit in. */
|
|
grid-area: media;
|
|
}
|
|
}
|
|
|
|
.twitter-tweet {
|
|
border: 1px solid hsl(0deg 0% 87%);
|
|
padding: 0.5em 0.75em;
|
|
margin-bottom: 0.25em;
|
|
overflow-wrap: anywhere;
|
|
min-height: 48px;
|
|
}
|
|
|
|
.twitter-avatar {
|
|
float: left;
|
|
width: 48px;
|
|
height: 48px;
|
|
margin-right: 0.75em;
|
|
}
|
|
|
|
.message_inline_ref {
|
|
margin-bottom: var(--markdown-interelement-space-px);
|
|
margin-left: 5px;
|
|
height: 50px;
|
|
display: block !important;
|
|
border: none !important;
|
|
}
|
|
|
|
&.rtl .message_inline_ref {
|
|
margin-left: unset;
|
|
margin-right: 5px;
|
|
}
|
|
|
|
.twitter-image img,
|
|
.message_inline_image img,
|
|
.message_inline_ref img {
|
|
/* We use `scale-down` so that images smaller than the container are
|
|
neither scaled up nor cropped to fit. This preserves
|
|
their aspect ratio, which is often helpful. */
|
|
object-fit: scale-down;
|
|
|
|
/* We need to explicitly specify the image dimensions to have
|
|
object-fit work; likely because internally object-fit needs
|
|
to know the frame it is targeting, and images don't default
|
|
to container dimensions. */
|
|
height: 100%;
|
|
width: 100%;
|
|
|
|
float: left;
|
|
margin-right: 10px;
|
|
border-radius: inherit;
|
|
}
|
|
|
|
.message_inline_image img {
|
|
cursor: zoom-in;
|
|
}
|
|
|
|
.youtube-video img,
|
|
.vimeo-video img,
|
|
.embed-video img {
|
|
cursor: pointer;
|
|
}
|
|
|
|
.youtube-video img {
|
|
/* We do this for the sake of increasing
|
|
the size of older YouTube thumbnail
|
|
previews, but there are no ill effects
|
|
on newer preview images. */
|
|
object-fit: contain;
|
|
}
|
|
|
|
&.rtl .twitter-image img,
|
|
&.rtl .message_inline_image img,
|
|
&.rtl .message_inline_ref img {
|
|
float: right;
|
|
margin-right: unset;
|
|
margin-left: 10px;
|
|
}
|
|
|
|
& li .message_inline_image img {
|
|
float: none;
|
|
}
|
|
|
|
.message_inline_video,
|
|
.message_inline_animated_image_still,
|
|
.youtube-video a[data-id] {
|
|
&:hover {
|
|
&::after {
|
|
transform: scale(1);
|
|
}
|
|
}
|
|
|
|
&::after {
|
|
content: "";
|
|
background-image: url("../images/play_button.svg");
|
|
background-position: center center;
|
|
background-repeat: no-repeat;
|
|
/* 32px at 14px/1em */
|
|
background-size: 2.2857em;
|
|
position: absolute;
|
|
/* Match the width to the background size. */
|
|
width: 2.2857em;
|
|
height: 2.2857em;
|
|
/* Center according to those widths; at present,
|
|
thumbnails are 8em tall and 12em wide. So,
|
|
2.2857em / 2 = 1.1429em as the center of the
|
|
play button. We subtract that value from half
|
|
the height (8em/2) to get the `top:` value,
|
|
and then subtract the same from half the width
|
|
(12em/2) to get the `left:` value. */
|
|
top: 2.8571em;
|
|
left: 4.8571em;
|
|
border-radius: 100%;
|
|
transform: scale(0.8);
|
|
transition: transform 0.2s;
|
|
}
|
|
|
|
& video {
|
|
display: block;
|
|
object-fit: contain;
|
|
height: 100%;
|
|
width: 100%;
|
|
}
|
|
}
|
|
|
|
.message_embed {
|
|
display: block;
|
|
position: relative;
|
|
margin: 0 0 var(--markdown-interelement-space-px);
|
|
border: none;
|
|
border-left: 3px solid hsl(0deg 0% 93%);
|
|
height: 80px;
|
|
padding: 5px;
|
|
z-index: 1;
|
|
text-shadow: hsl(0deg 0% 0% / 1%) 0 0 1px;
|
|
|
|
.message_embed_title {
|
|
padding-top: 0;
|
|
/* to remove the spacing that the font has from the top of the container. */
|
|
margin-top: -5px;
|
|
|
|
font-size: 1.2em;
|
|
line-height: normal;
|
|
}
|
|
|
|
.message_embed_description {
|
|
position: relative;
|
|
max-width: 500px;
|
|
margin-top: 3px;
|
|
|
|
/* to put it below the container gradient. */
|
|
z-index: -1;
|
|
}
|
|
|
|
.message_embed_image {
|
|
display: inline-block;
|
|
padding: 5px;
|
|
width: 70px;
|
|
height: 70px;
|
|
background-size: cover;
|
|
background-position: center;
|
|
}
|
|
|
|
.data-container {
|
|
position: relative;
|
|
padding: 0 5px;
|
|
display: inline-block;
|
|
vertical-align: top;
|
|
max-width: calc(100% - 115px);
|
|
max-height: 80px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.data-container::after {
|
|
content: " ";
|
|
position: absolute;
|
|
width: 100%;
|
|
height: 10%;
|
|
bottom: 0;
|
|
|
|
background: linear-gradient(
|
|
0deg,
|
|
var(--color-background-stream-message-content),
|
|
transparent 100%
|
|
);
|
|
}
|
|
}
|
|
|
|
&.rtl .message_embed {
|
|
border-left: unset;
|
|
border-right: 3px solid hsl(0deg 0% 93%);
|
|
}
|
|
|
|
& a {
|
|
color: var(--color-markdown-link);
|
|
text-decoration: none;
|
|
|
|
& code {
|
|
color: var(--color-markdown-code-link);
|
|
}
|
|
|
|
&:hover,
|
|
&:focus {
|
|
color: var(--color-markdown-link-hover);
|
|
text-decoration: underline;
|
|
|
|
& code {
|
|
color: var(--color-markdown-code-link-hover);
|
|
}
|
|
}
|
|
}
|
|
|
|
& pre {
|
|
direction: ltr;
|
|
/* code block text is a bit smaller than normal text */
|
|
font-size: 0.825em;
|
|
line-height: 1.4;
|
|
white-space: pre;
|
|
overflow-x: auto;
|
|
word-break: break-all;
|
|
word-wrap: normal;
|
|
margin: 0 0 var(--markdown-interelement-space-px);
|
|
padding: 5px 7px 3px;
|
|
display: block;
|
|
border-radius: 4px;
|
|
|
|
&:hover .code-buttons-container {
|
|
visibility: visible;
|
|
}
|
|
|
|
/* Hide the code buttons container when the user is
|
|
clicking on the code block other than the buttons.
|
|
This allows the user to select part of the the code
|
|
without the buttons interfering with the selection. */
|
|
&:active .code-buttons-container:not(:hover) {
|
|
visibility: hidden;
|
|
}
|
|
}
|
|
|
|
& pre code {
|
|
font-size: inherit;
|
|
padding: 0;
|
|
white-space: inherit;
|
|
overflow-x: scroll;
|
|
/* Unset to avoid compounding alpha values */
|
|
background-color: unset;
|
|
color: inherit;
|
|
border: 0;
|
|
}
|
|
|
|
& code {
|
|
/* 11.55px when body is the default 14px; this is chosen to be
|
|
slightly above the 11.5px threshold where the height jumps by a
|
|
pixel on most platforms. */
|
|
font-size: 0.825em;
|
|
unicode-bidi: embed;
|
|
direction: ltr;
|
|
white-space: pre-wrap;
|
|
/* 1px at 14px/1em */
|
|
padding: 0.0714em 2px;
|
|
color: var(--color-markdown-code-text);
|
|
background-color: var(--color-markdown-code-background);
|
|
border-radius: 3px;
|
|
}
|
|
|
|
/* Container for buttons inside code blocks. */
|
|
.code-buttons-container {
|
|
/* Break white-space treatment inherited from <pre> */
|
|
white-space: collapse;
|
|
/* Present buttons in a flexbox layout. */
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 3px;
|
|
/* Having absolute positioning here ensures that the element
|
|
doesn't scroll along with the code div in narrow windows */
|
|
position: absolute;
|
|
top: 4px;
|
|
right: 0;
|
|
padding: 0 4px;
|
|
/* Invisible unless <pre> is hovered. */
|
|
visibility: hidden;
|
|
z-index: 0;
|
|
}
|
|
|
|
/* The properties of the code_external_link button are copied from the
|
|
copy-button class in app_components.css. */
|
|
.code_external_link {
|
|
display: flex;
|
|
border-radius: 4px;
|
|
color: var(--color-copy-button);
|
|
/* 2px at 16px/1em */
|
|
padding: 0.125em;
|
|
cursor: pointer;
|
|
|
|
&:hover,
|
|
:focus-visible {
|
|
background-color: var(--color-copy-button-square-bg-hover);
|
|
}
|
|
|
|
&:active {
|
|
background-color: var(--color-copy-button-square-bg-active);
|
|
color: var(--color-copy-button-square-active);
|
|
}
|
|
}
|
|
|
|
.copy_codeblock,
|
|
.code_external_link {
|
|
font-size: 1.1363em;
|
|
border: 1px solid var(--color-copy-button-square-bg-active);
|
|
backdrop-filter: blur(20px);
|
|
}
|
|
}
|
|
|
|
.group_mention,
|
|
.direct_mention {
|
|
& .rendered_markdown pre {
|
|
background-color: var(--color-markdown-pre-background-mentions);
|
|
border-color: var(--color-markdown-pre-border-mentions);
|
|
}
|
|
|
|
& .rendered_markdown code {
|
|
background-color: var(--color-markdown-code-background-mentions);
|
|
}
|
|
|
|
& .rendered_markdown pre code {
|
|
background-color: unset;
|
|
border-color: unset;
|
|
}
|
|
}
|
|
|
|
.preview_content .copy_codeblock {
|
|
/* We avoid displaying copy_codeblock button in previews, because it
|
|
feels odd given that you can just copy-paste the code out of
|
|
the "edit" state. We may change this decision when we add
|
|
menu options for viewing the code in a coding playground. */
|
|
display: none;
|
|
}
|
|
|
|
.informational-overlays .copy_codeblock {
|
|
display: none;
|
|
}
|
|
|
|
.message_edit_history_content .copy_codeblock {
|
|
/* Copy code block button is hidden in edit history, this is done
|
|
because of issues faced in copying code blocks in edit history
|
|
modal. This may be changed later as we decide upon a proper ux
|
|
for displaying edit-history. */
|
|
display: none;
|
|
}
|
|
|
|
.message_edit_history_content .code_external_link {
|
|
right: 5px;
|
|
}
|
|
|
|
.preview_content .code_external_link {
|
|
right: 12px;
|
|
}
|
|
|
|
@media (width < $sm_min) {
|
|
.rendered_markdown .message_embed {
|
|
height: auto;
|
|
|
|
.message_embed_image {
|
|
width: 100%;
|
|
height: 100px;
|
|
}
|
|
|
|
.data-container {
|
|
display: block;
|
|
max-width: 100%;
|
|
margin-top: 10px;
|
|
}
|
|
}
|
|
}
|
|
|
|
.codehilite {
|
|
display: block !important;
|
|
border: none !important;
|
|
background: none !important;
|
|
|
|
/* Set a relative positioning context to more precisely
|
|
position .code-buttons-container. This eliminates
|
|
problems with positioning shifts associated with
|
|
code blocks in spoilers, too. */
|
|
position: relative;
|
|
|
|
& pre {
|
|
color: var(--color-markdown-pre-text);
|
|
/* This is necessary to remove the background color
|
|
set by Pygments. */
|
|
background-color: var(--color-markdown-pre-background);
|
|
border: 1px solid var(--color-markdown-pre-border);
|
|
}
|
|
}
|
|
|
|
/* Both the horizontal scrollbar in <pre/> as well as
|
|
vertical scrollbar in the <textarea/> is styled similarly. */
|
|
.message_edit_form textarea,
|
|
.rendered_markdown pre {
|
|
/* Ensure the horizontal scrollbar is visible on Mac */
|
|
&::-webkit-scrollbar {
|
|
height: 8px;
|
|
width: 10px;
|
|
background-color: hsl(0deg 0% 0% / 5%);
|
|
}
|
|
|
|
&::-webkit-scrollbar-thumb {
|
|
background-color: hsl(0deg 0% 0% / 30%);
|
|
border-radius: 20px;
|
|
cursor: auto;
|
|
transition: background-color 0.2s ease;
|
|
}
|
|
|
|
&::-webkit-scrollbar-thumb:hover {
|
|
background-color: hsl(0deg 0% 0% / 60%);
|
|
}
|
|
}
|
|
|
|
/* Search highlight used in both topics and rendered_markdown */
|
|
.highlight {
|
|
background-color: hsl(51deg 100% 79%);
|
|
}
|
|
|
|
/* For elements where we want to show as much markdown content we can
|
|
in a single line and then hide the overflowing part. */
|
|
.single-line-rendered-markdown {
|
|
/* Any element which can `wrap` in the above defined elements. */
|
|
code,
|
|
.stream-topic {
|
|
white-space: nowrap;
|
|
}
|
|
}
|
|
|
|
.user-mention {
|
|
i.zulip-icon-bot {
|
|
vertical-align: middle;
|
|
position: relative;
|
|
top: -1px;
|
|
padding-left: 0.3em;
|
|
}
|
|
}
|