mirror of
				https://github.com/zulip/zulip.git
				synced 2025-10-31 12:03:46 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			448 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			448 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| # Message formatting
 | |
| 
 | |
| Zulip supports an extended version of Markdown for messages, as well as
 | |
| some HTML level special behavior. The Zulip help center article on [message
 | |
| formatting](/help/format-your-message-using-markdown) is the primary
 | |
| documentation for Zulip's markup features. This article is currently a
 | |
| changelog for updates to these features.
 | |
| 
 | |
| The [render a message](/api/render-message) endpoint can be used to get
 | |
| the current HTML version of any Markdown syntax for message content.
 | |
| 
 | |
| ## Code blocks
 | |
| 
 | |
| **Changes**: As of Zulip 4.0 (feature level 33), [code blocks][help-code]
 | |
| can have a `data-code-language` attribute attached to the outer HTML
 | |
| `div` element, which records the programming language that was selected
 | |
| for syntax highlighting. This field is used in the
 | |
| [playgrounds][help-playgrounds] feature for code blocks.
 | |
| 
 | |
| ## Global times
 | |
| 
 | |
| **Changes**: In Zulip 3.0 (feature level 8), added [global time
 | |
| mentions][help-global-time] to supported Markdown message formatting
 | |
| features.
 | |
| 
 | |
| ## Links to channels, topics, and messages
 | |
| 
 | |
| Zulip's markup supports special readable Markdown syntax for [linking
 | |
| to channels, topics, and messages](/help/link-to-a-message-or-conversation).
 | |
| 
 | |
| Sample HTML formats are as follows:
 | |
| ``` html
 | |
| <!-- Syntax: #**announce** -->
 | |
| <a class="stream" data-stream-id="9"
 | |
|   href="/#narrow/channel/9-announce">
 | |
|  #announce
 | |
| </a>
 | |
| 
 | |
| <!-- Syntax: #**announce>Zulip updates** -->
 | |
| <a class="stream-topic" data-stream-id="9"
 | |
|   href="/#narrow/channel/9-announce/topic/Zulip.20updates/with/214">
 | |
|  #announce > Zulip updates
 | |
| </a>
 | |
| 
 | |
| <!-- Syntax: #**announce>Zulip updates**
 | |
|      Generated only if topic had no messages or the link was rendered
 | |
|      before Zulip 10.0 (feature level 347) -->
 | |
| <a class="stream-topic" data-stream-id="9"
 | |
|   href="/#narrow/channel/9-announce/topic/Zulip.20updates">
 | |
|  #announce > Zulip updates
 | |
| </a>
 | |
| 
 | |
| <!-- Syntax: #**announce>Zulip updates@214** -->
 | |
| <a class="message-link"
 | |
|   href="/#narrow/channel/9-announce/topic/Zulip.20updates/near/214">
 | |
|  #announce > Zulip updates @ 💬
 | |
| </a>
 | |
| ```
 | |
| 
 | |
| The `near` and `with` operators are documented in more detail in the
 | |
| [search and URL documentation](/api/construct-narrow). When rendering
 | |
| topic links with the `with` operator, the code doing the rendering may
 | |
| pick the ID arbitrarily among messages accessible to the client and/or
 | |
| acting user at the time of rendering. Currently, the server chooses
 | |
| the message ID to use for `with` operators as the latest message ID in
 | |
| the topic accessible to the user who wrote the message.
 | |
| 
 | |
| The older stream/topic link elements include a `data-stream-id`, which
 | |
| historically was used in order to display the current channel name if
 | |
| the channel had been renamed. That field is **deprecated**, because
 | |
| displaying an updated value for the most common forms of this syntax
 | |
| requires parsing the URL to get the topic to use anyway.
 | |
| 
 | |
| When a topic is an empty string, it is replaced with
 | |
| `realm_empty_topic_display_name` found in the [`POST /register`](/api/register-queue)
 | |
| response and wrapped with the `<em>` tag.
 | |
| 
 | |
| Sample HTML formats with `"realm_empty_topic_display_name": "general chat"`
 | |
| are as follows:
 | |
| ```html
 | |
| <!-- Syntax: #**announce>** -->
 | |
| <a class="stream-topic" data-stream-id="9"
 | |
|   href="/#narrow/channel/9-announce/topic/with/214">
 | |
|  #announce > <em>general chat</em>
 | |
| </a>
 | |
| 
 | |
| <!-- Syntax: #**announce>**
 | |
|      Generated only if topic had no messages or the link was rendered
 | |
|      before Zulip 10.0 (feature level 347) -->
 | |
| <a class="stream-topic" data-stream-id="9"
 | |
|   href="/#narrow/channel/9-announce/topic/">
 | |
|  #announce > <em>general chat</em>
 | |
| </a>
 | |
| 
 | |
| <!-- Syntax: #**announce>@214** -->
 | |
| <a class="message-link"
 | |
|   href="/#narrow/channel/9-announce/topic//near/214">
 | |
|  #announce > <em>general chat</em> @ 💬
 | |
| </a>
 | |
| ```
 | |
| 
 | |
| **Changes**: In Zulip 11.0 (feature level 400), the server switched
 | |
| its strategy for `with` URL construction to choose the latest
 | |
| accessible message ID in a topic. Previously, it used the oldest.
 | |
| 
 | |
| Before Zulip 10.0 (feature level 347), the `with` field
 | |
| was never used in topic link URLs generated by the server; the markup
 | |
| currently used only for empty topics was used for all topic links.
 | |
| 
 | |
| Before Zulip 10.0 (feature level 346), empty string
 | |
| was not a valid topic name in syntaxes for linking to topics and
 | |
| messages.
 | |
| 
 | |
| In Zulip 10.0 (feature level 319), added Markdown syntax
 | |
| for linking to a specific message in a conversation. Declared the
 | |
| `data-stream-id` field to be deprecated as detailed above.
 | |
| 
 | |
| In Zulip 11.0 (feature level 383), clients can decide what
 | |
| channel view a.stream channel link elements take you to -- i.e.,
 | |
| the href for those is the default behavior of the link that also
 | |
| encodes the channel alongside the data-stream-id field, but clients
 | |
| can override that default based on `web_channel_default_view` setting.
 | |
| 
 | |
| ## Image previews
 | |
| 
 | |
| When a Zulip message is sent linking to an uploaded image, Zulip will
 | |
| generate an image preview element with the following format.
 | |
| 
 | |
| ``` html
 | |
| <div class="message_inline_image">
 | |
|     <a href="/user_uploads/path/to/image.png" title="image.png">
 | |
|         <img data-original-dimensions="1920x1080"
 | |
|           data-original-content-type="image/png"
 | |
|           src="/user_uploads/thumbnail/path/to/image.png/840x560.webp">
 | |
|     </a>
 | |
| </div>
 | |
| ```
 | |
| 
 | |
| If the server has not yet generated thumbnails for the image yet at
 | |
| the time the message is sent, the `img` element will be a temporary
 | |
| loading indicator image and have the `image-loading-placeholder`
 | |
| class, which clients can use to identify loading indicators and
 | |
| replace them with a more native loading indicator element if
 | |
| desired. For example:
 | |
| 
 | |
| ``` html
 | |
| <div class="message_inline_image">
 | |
|     <a href="/user_uploads/path/to/image.png" title="image.png">
 | |
|         <img class="image-loading-placeholder"
 | |
|           data-original-dimensions="1920x1080"
 | |
|           data-original-content-type="image/png"
 | |
|           src="/path/to/spinner.png">
 | |
|     </a>
 | |
| </div>
 | |
| ```
 | |
| 
 | |
| Once the server has a working thumbnail, such messages will be updated
 | |
| via an `update_message` event, with the `rendering_only: true` flag
 | |
| (telling clients not to adjust message edit history), with appropriate
 | |
| adjusted `rendered_content`. A client should process those events by
 | |
| just using the updated rendering. If thumbnailing failed, the same
 | |
| type of event will edit the message's rendered form to remove the
 | |
| image preview element, so no special client-side logic should be
 | |
| required to process such errors.
 | |
| 
 | |
| Note that in the uncommon situation that the thumbnailing system is
 | |
| backlogged, an individual message containing multiple image previews
 | |
| may be re-rendered multiple times as each image finishes thumbnailing
 | |
| and triggers a message update.
 | |
| 
 | |
| Clients are recommended to do the following when processing image
 | |
| previews:
 | |
| 
 | |
| - Clients that would like to use the image's aspect ratio to lay out
 | |
|   one or more images in the message feed may use the
 | |
|   `data-original-dimensions` attribute, which is present even if the
 | |
|   image is a placeholder spinner.  This attribute encodes the
 | |
|   dimensions of the original image as `{width}x{height}`.  These
 | |
|   dimensions are for the image as rendered, _after_ any EXIF rotation
 | |
|   and mirroring has been applied.
 | |
| - If the client would like to control the thumbnail resolution used,
 | |
|   it can replace the final section of the URL (`840x560.webp` in the
 | |
|   example above) with the `name` of its preferred format from the set
 | |
|   of supported formats provided by the server in the
 | |
|   `server_thumbnail_formats` portion of the `register`
 | |
|   response. Clients should not make any assumptions about what format
 | |
|   the server will use as the "default" thumbnail resolution, as it may
 | |
|   change over time.
 | |
| - Download button type elements should provide the original image
 | |
|   (encoded via the `href` of the containing `a` tag).
 | |
| - The content-type of the original image is provided on a
 | |
|   `data-original-content-type` attribute, so clients can decide if
 | |
|   they are capable of rendering the original image.
 | |
| - For images whose formats which are not widely-accepted by browsers
 | |
|   (e.g., HEIC and TIFF), the image may contain a
 | |
|   `data-transcoded-image` attribute, which specifies a high-resolution
 | |
|   thumbnail format which clients may use instead of the original
 | |
|   image.
 | |
| - Lightbox elements for viewing an image should be designed to
 | |
|   immediately display any already-downloaded thumbnail while fetching
 | |
|   the original-quality image or an appropriate higher-quality
 | |
|   thumbnail from the server, to be transparently swapped in once it is
 | |
|   available. Clients that would like to size the lightbox based on the
 | |
|   size of the original image can use the `data-original-dimensions`
 | |
|   attribute, as described above.
 | |
| - Animated images will have a `data-animated` attribute on the `img`
 | |
|   tag. As detailed in `server_thumbnail_formats`, both animated and
 | |
|   still images are available for clients to use, depending on their
 | |
|   preference. See, for example, the [web setting][help-previews]
 | |
|   to control whether animated images are autoplayed in the message
 | |
|   feed.
 | |
| - Clients should not assume that the requested format is the format
 | |
|   that they will receive; in rare cases where the client has an
 | |
|   out-of-date list of `server_thumbnail_formats`, the server will
 | |
|   provide an approximation of the client's requested format.  Because
 | |
|   of this, clients should not assume that the pixel dimensions or file
 | |
|   format match what they requested.
 | |
| - No other processing of the URLs is recommended.
 | |
| 
 | |
| **Changes**: In Zulip 10.0 (feature level 336), added
 | |
| `data-original-content-type` attribute to convey the type of the
 | |
| original image, and optional `data-transcoded-image` attribute for
 | |
| images with formats which are not widely supported by browsers.
 | |
| 
 | |
| **Changes**: In Zulip 9.2 (feature levels 278-279, and 287+), added
 | |
| `data-original-dimensions` to the `image-loading-placeholder` spinner
 | |
| images, containing the dimensions of the original image.
 | |
| 
 | |
| In Zulip 9.0 (feature level 276), added `data-original-dimensions`
 | |
| attribute to images that have been thumbnailed, containing the
 | |
| dimensions of the full-size version of the image. Thumbnailing itself
 | |
| was reintroduced at feature level 275.
 | |
| 
 | |
| Previously, with the exception of Zulip servers that used the beta
 | |
| Thumbor-based implementation years ago, all image previews in Zulip
 | |
| messages were not thumbnailed; the `a` tag and the `img` tag would both
 | |
| point to the original image.
 | |
| 
 | |
| Clients that correctly implement the current API should handle
 | |
| Thumbor-based older thumbnails correctly, as long as they do not
 | |
| assume that `data-original-dimensions` is present. Clients should not
 | |
| assume that messages sent prior to the introduction of thumbnailing
 | |
| have been re-rendered to use the new format or have thumbnails
 | |
| available.
 | |
| 
 | |
| ## Video embeddings and previews
 | |
| 
 | |
| When a Zulip message is sent linking to an uploaded video, Zulip may
 | |
| generate a video preview element with the following format.
 | |
| 
 | |
| 
 | |
| ``` html
 | |
| <div class="message_inline_image message_inline_video">
 | |
|   <a href="/user_uploads/path/to/video.mp4">
 | |
|     <video preload="metadata" src="/user_uploads/path/to/video.mp4">
 | |
|     </video>
 | |
|   </a>
 | |
| </div>
 | |
| ```
 | |
| 
 | |
| ## Audio Players
 | |
| 
 | |
| When the Markdown media syntax is used with an uploaded file with an
 | |
| audio `Content-Type`, Zulip will generate an HTML5 `<audio>` player
 | |
| element. Supported MIME types are currently `audio/aac`, `audio/flac`,
 | |
| `audio/mpeg`, and `audio/wav`.
 | |
| 
 | |
| For example, `[file.mp3](/user_uploads/path/to/file.mp3)` renders as:
 | |
| 
 | |
| ``` html
 | |
| <audio controls preload="metadata"
 | |
|   src="/user_uploads/path/to/file.mp3" title="file.mp3">
 | |
| </audio>
 | |
| ```
 | |
| 
 | |
| If the Zulip server has rewritten the URL of the audio file, it will
 | |
| provide the URL in a `data-original-url` parameter. The Zulip server
 | |
| does this for all non-uploaded file audio URLs.
 | |
| 
 | |
| ``` html
 | |
| <audio controls preload="metadata"
 | |
|   data-original-url="https://example.com/path/to/original/file.mp3"
 | |
|   src="https://zulipcdn.example.com/path/to/playable/file.mp3" title="file.mp3">
 | |
| </audio>
 | |
| ```
 | |
| 
 | |
| Clients that cannot render an audio player are recommended to convert
 | |
| audio elements into a link to the original URL.
 | |
| 
 | |
| The Zulip server does not validate whether uploaded files with an
 | |
| audio `Content-Type` are actually playable.
 | |
| 
 | |
| **Changes**: New in Zulip 11.0 (feature level 405).
 | |
| 
 | |
| ## Mentions and silent mentions
 | |
| 
 | |
| Zulip markup supports [mentioning](/help/mention-a-user-or-group)
 | |
| users, user groups, and a few special "wildcard" mentions (the three
 | |
| spellings of a channel wildcard mention: `@**all**`, `@**everyone**`,
 | |
| `@**channel**` and the topic wildcard mention `@**topic**`).
 | |
| 
 | |
| Mentions result in a message being highlighted for the target user(s),
 | |
| both in the UI and in notifications, and may also result in the target
 | |
| user(s) following the conversation, [depending on their
 | |
| settings](/help/follow-a-topic#follow-topics-where-you-are-mentioned).
 | |
| 
 | |
| Silent mentions of users or groups have none of those side effects,
 | |
| but nonetheless uniquely identify the user or group
 | |
| identified. (There's no such thing as a silent wildcard mention).
 | |
| 
 | |
| Permissions for mentioning users work as follows:
 | |
| 
 | |
| - Any user can mention any other user, though mentions by [muted
 | |
| users](/help/mute-a-user) are automatically marked as read and thus do
 | |
| not trigger notifications or otherwise get highlighted like unread
 | |
| mentions.
 | |
| 
 | |
| - Wildcard mentions are permitted except where [organization-level
 | |
| restrictions](/help/restrict-wildcard-mentions) apply.
 | |
| 
 | |
| - User groups can be mentioned if and only if the acting user is in
 | |
| the `can_mention_group` group for that group. All user groups can be
 | |
| silently mentioned by any user.
 | |
| 
 | |
| - System groups, when (silently) mentioned, should be displayed using
 | |
| their description, not their `role:nobody` style API names; see the
 | |
| main [system group
 | |
| documentation](/api/group-setting-values#system-groups) for
 | |
| details. System groups can only be silently mentioned right now,
 | |
| because they happen to all use the empty `Nobody` group for
 | |
| `can_mention_group`; clients should just use `can_mention_group` to
 | |
| determine which groups to offer in typeahead in similar contexts.
 | |
| 
 | |
| - Requests to send or edit a message that are impermissible due to
 | |
| including a mention where the acting user does not have permission to
 | |
| mention the target will return an error. Mention syntax that does not
 | |
| correspond to a real user or group is ignored.
 | |
| 
 | |
| Sample markup for `@**Example User**`:
 | |
| 
 | |
| ``` html
 | |
| <span class="user-mention" data-user-id="31">@Example User</span>
 | |
| ```
 | |
| 
 | |
| Sample markup for `@_**Example User**`:
 | |
| 
 | |
| ``` html
 | |
| <span class="user-mention silent" data-user-id="31">Example User</span>
 | |
| ```
 | |
| 
 | |
| Sample markup for `@**topic**`:
 | |
| 
 | |
| ``` html
 | |
| <span class="topic-mention">@topic</span>
 | |
| ```
 | |
| 
 | |
| Sample markup for `@**channel**`:
 | |
| 
 | |
| ``` html
 | |
| <span class="user-mention channel-wildcard-mention"
 | |
|   data-user-id="*">@channel</span>
 | |
| ```
 | |
| 
 | |
| Sample markup for `@*support*`, assuming "support" is a valid group:
 | |
| ``` html
 | |
| <span class="user-group-mention"
 | |
|   data-user-group-id="17">@support</span>
 | |
| ```
 | |
| 
 | |
| Sample markup for `@_*support*`, assuming "support" is a valid group:
 | |
| ``` html
 | |
| <span class="user-group-mention silent"
 | |
|   data-user-group-id="17">support</span>
 | |
| ```
 | |
| 
 | |
| Sample markup for `@_*role:administrators*`:
 | |
| ``` html
 | |
| <span class="user-group-mention silent"
 | |
|   data-user-group-id="5">Administrators</span>
 | |
| ```
 | |
| 
 | |
| When processing mentions, clients should look up the user or group
 | |
| referenced by ID, and update the textual name for the mention to the
 | |
| current name for the user or group with that ID. Note that for system
 | |
| groups, this requires special logic to look up the user-facing name
 | |
| for that group; see [system
 | |
| groups](/api/group-setting-values#system-groups) for details.
 | |
| 
 | |
| **Changes**: Prior to Zulip 10.0 (feature level 333), it was not
 | |
| possible to silently mention [system
 | |
| groups](/api/group-setting-values#system-groups).
 | |
| 
 | |
| In Zulip 9.0 (feature level 247), `channel` was added to the supported
 | |
| [wildcard][help-mention-all] options used in the
 | |
| [mentions][help-mentions] Markdown message formatting feature.
 | |
| 
 | |
| ## Spoilers
 | |
| 
 | |
| **Changes**: In Zulip 3.0 (feature level 15), added
 | |
| [spoilers][help-spoilers] to supported Markdown message formatting
 | |
| features.
 | |
| 
 | |
| ## Removed features
 | |
| 
 | |
| ### Removed legacy Dropbox link preview markup
 | |
| 
 | |
| In Zulip 11.0 (feature level 395), the Zulip server stopped generating
 | |
| legacy Dropbox link previews. Dropbox links are now previewed just
 | |
| like standard Zulip image/link previews. However, some legacy Dropbox
 | |
| previews may exist in existing messages.
 | |
| 
 | |
| Clients are recommended to prune these previews from message HTML;
 | |
| since they always appear after the actual link, there is no loss of
 | |
| information/functionality. They can be recognized via the classes
 | |
| `message_inline_ref`, `message_inline_image_desc`, and
 | |
| `message_inline_image_title`:
 | |
| 
 | |
| ``` html
 | |
| <div class="message_inline_ref">
 | |
|     <a href="https://www.dropbox.com/sh/cm39k9e04z7fhim/AAAII5NK-9daee3FcF41anEua?dl=" title="Saves">
 | |
|         <img src="/path/to/folder_dropbox.png">
 | |
|     </a>
 | |
|     <div><div class="message_inline_image_title">Saves</div>
 | |
|         <desc class="message_inline_image_desc"></desc>
 | |
|     </div>
 | |
| </div>
 | |
| ```
 | |
| 
 | |
| ### Removed legacy avatar markup
 | |
| 
 | |
| In Zulip 4.0 (feature level 24), the rarely used `!avatar()`
 | |
| and `!gravatar()` markup syntax, which was never documented and had an
 | |
| inconsistent syntax, were removed.
 | |
| 
 | |
| ## Related articles
 | |
| 
 | |
| * [Markdown formatting](/help/format-your-message-using-markdown)
 | |
| * [Send a message](/api/send-message)
 | |
| * [Render a message](/api/render-message)
 | |
| 
 | |
| [help-code]: /help/code-blocks
 | |
| [help-playgrounds]: /help/code-blocks#code-playgrounds
 | |
| [help-spoilers]: /help/spoilers
 | |
| [help-global-time]: /help/global-times
 | |
| [help-mentions]: /help/mention-a-user-or-group
 | |
| [help-mention-all]: /help/mention-a-user-or-group#mention-everyone-on-a-channel
 | |
| [help-previews]: /help/image-video-and-website-previews#configure-how-animated-images-are-played
 |