Skip to content

Commit 16b747c

Browse files
feat: BROS-496: New Markdown tag to improve layouts (#8572)
Co-authored-by: robot-ci-heartex <robot-ci-heartex@users.noreply.github.com> Co-authored-by: hlomzik <hlomzik@users.noreply.github.com>
1 parent 4487d49 commit 16b747c

File tree

10 files changed

+1041
-153
lines changed

10 files changed

+1041
-153
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
### Parameters
2+
3+
| Param | Type | Description |
4+
| --- | --- | --- |
5+
| value | <code>string</code> | Markdown text content, either static text or field name in task data (e.g., $markdown_field) |
6+
| [style] | <code>string</code> | CSS style string |
7+
| [className] | <code>string</code> | Class name of the CSS style to apply |
8+
| [idAttr] | <code>string</code> | Unique ID attribute to use in CSS |
9+
| [visibleWhen] | <code>region-selected</code> \| <code>choice-selected</code> \| <code>no-region-selected</code> \| <code>choice-unselected</code> | Control visibility of the content |
10+
| [whenTagName] | <code>string</code> | Use with `visibleWhen`. Narrow down visibility by tag name |
11+
| [whenLabelValue] | <code>string</code> | Use with `visibleWhen="region-selected"`. Narrow down visibility by label value |
12+
| [whenChoiceValue] | <code>string</code> | Use with `visibleWhen` and `whenTagName`. Narrow down visibility by choice value |
13+

docs/source/tags/markdown.md

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
---
2+
title: Markdown
3+
type: tags
4+
order: 504
5+
meta_title: Markdown Tag for Rendering Markdown Text
6+
meta_description: Customize Label Studio with the Markdown tag to display formatted markdown text content for machine learning and data science projects.
7+
---
8+
9+
The `Markdown` tag is used to display markdown-formatted text content on the labeling interface. Use this tag to provide rich text instructions, descriptions, or content with formatting support including headers, bold text, lists, links, and more.
10+
11+
Also that's the simplest way to display auxiliary text content on the labeling interface.
12+
13+
Use with the following data types: Text content in Markdown format.
14+
15+
{% insertmd includes/tags/view.md %}
16+
17+
### Example
18+
19+
Display static markdown instructions on the labeling interface:
20+
21+
```html
22+
<View>
23+
<Markdown>
24+
## Instructions
25+
26+
Please **carefully** read the following text and mark all entities.
27+
28+
- Look for **person names**
29+
- Look for **organization names**
30+
- Look for **locations**
31+
32+
> Remember to be thorough in your analysis.
33+
</Markdown>
34+
<Text name="text" value="$text" />
35+
</View>
36+
```
37+
38+
Indents are important in markdown, so it's advised to keep markdown content unindented.
39+
40+
### Example
41+
42+
Display markdown content from task data:
43+
44+
```html
45+
<View>
46+
<Markdown value="$markdown_description" />
47+
<Text name="text" value="$text" />
48+
</View>
49+
```
50+
51+
**Example task data:**
52+
53+
```json
54+
{
55+
"markdown_description": "## Analysis Task\n\nPlease analyze the following text for sentiment:\n\n- **Positive** - Shows satisfaction or approval\n- **Negative** - Shows dissatisfaction or criticism\n- **Neutral** - Shows no particular sentiment",
56+
"text": "The product was amazing and I loved it!"
57+
}
58+
```
59+
60+
### Example
61+
62+
Display styled markdown content:
63+
64+
```html
65+
<View>
66+
<Markdown
67+
value="$content"
68+
style="background: #f5f5f5; padding: 15px; border-radius: 8px; border-left: 4px solid #007bff;" />
69+
</View>
70+
```
71+
72+
### Supported Markdown Features
73+
74+
The Markdown tag supports standard Markdown syntax including:
75+
76+
- **Headers** - `# ## ### ####` etc.
77+
- **Bold and italic** - `**bold**` and `*italic*`
78+
- **Lists** - Ordered (`1. item`) and unordered (`- item`)
79+
- **Links** - `[link text](URL)`
80+
- **Code** - Inline `` `code` `` and code blocks with ```
81+
- **Blockquotes** - `> quoted text`
82+
- **Line breaks** - Double line breaks create new paragraphs

docs/source/tags/style.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: Style
33
type: tags
4-
order: 504
4+
order: 505
55
meta_title: Style Tag to use CSS Styles
66
meta_description: Customize Label Studio with CSS styles to modify the labeling interface for machine learning and data science projects.
77
---

docs/source/tags/view.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: View
33
type: tags
4-
order: 505
4+
order: 506
55
meta_title: View Tag for Defining How Blocks are Displayed
66
meta_description: Customize how blocks are displayed on the labeling interface in Label Studio for machine learning and data science projects.
77
---

web/libs/core/src/lib/utils/schema/tags.json

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3011,6 +3011,60 @@
30113011
}
30123012
}
30133013
},
3014+
"Markdown": {
3015+
"name": "Markdown",
3016+
"description": "The `Markdown` element is used to display markdown-formatted text content.",
3017+
"attrs": {
3018+
"value": {
3019+
"name": "value",
3020+
"description": "Markdown text content, either static text or field name in task data (e.g., $markdown_field)",
3021+
"type": "string",
3022+
"required": true
3023+
},
3024+
"style": {
3025+
"name": "style",
3026+
"description": "CSS style string",
3027+
"type": "string",
3028+
"required": false
3029+
},
3030+
"className": {
3031+
"name": "className",
3032+
"description": "Class name of the CSS style to apply",
3033+
"type": "string",
3034+
"required": false
3035+
},
3036+
"idAttr": {
3037+
"name": "idAttr",
3038+
"description": "Unique ID attribute to use in CSS",
3039+
"type": "string",
3040+
"required": false
3041+
},
3042+
"visibleWhen": {
3043+
"name": "visibleWhen",
3044+
"description": "Control visibility of the content",
3045+
"type": ["region-selected", "choice-selected", "no-region-selected", "choice-unselected"],
3046+
"required": false
3047+
},
3048+
"whenTagName": {
3049+
"name": "whenTagName",
3050+
"description": "Use with `visibleWhen`. Narrow down visibility by tag name",
3051+
"type": "string",
3052+
"required": false
3053+
},
3054+
"whenLabelValue": {
3055+
"name": "whenLabelValue",
3056+
"description": "Use with `visibleWhen=\"region-selected\"`. Narrow down visibility by label value",
3057+
"type": "string",
3058+
"required": false
3059+
},
3060+
"whenChoiceValue": {
3061+
"name": "whenChoiceValue",
3062+
"description": "Use with `visibleWhen` and `whenTagName`. Narrow down visibility by choice value",
3063+
"type": "string",
3064+
"required": false
3065+
}
3066+
}
3067+
},
30143068
"Style": {
30153069
"name": "Style",
30163070
"description": "The `Style` tag is used in combination with the View tag to apply custom CSS properties to the labeling interface. See the [CSS Reference](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference) on the MDN page for a full list of available properties that you can reference. You can also adjust default Label Studio CSS classes. Use the browser developer tools to inspect the element on the UI and locate the class name, then specify that class name in the `Style` tag.",
@@ -3132,6 +3186,7 @@
31323186
"Collapse",
31333187
"Filter",
31343188
"Header",
3189+
"Markdown",
31353190
"Style",
31363191
"View"
31373192
]
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
import { observer } from "mobx-react";
2+
import { types } from "mobx-state-tree";
3+
import ReactMarkdown from "react-markdown";
4+
5+
import Registry from "../../core/Registry";
6+
import Tree from "../../core/Tree";
7+
import ProcessAttrsMixin from "../../mixins/ProcessAttrs";
8+
import VisibilityMixin from "../../mixins/Visibility";
9+
import { AnnotationMixin } from "../../mixins/AnnotationMixin";
10+
import { parseValue } from "../../utils/data";
11+
import { guidGenerator } from "../../utils/unique";
12+
13+
// Custom markdown components with Tailwind styling
14+
const markdownComponents = {
15+
// Headings
16+
h1: ({ children }) => <h1 className="text-display-small font-bold mb-wide mt-wider">{children}</h1>,
17+
h2: ({ children }) => <h2 className="text-headline-large font-bold mb-base mt-wide">{children}</h2>,
18+
h3: ({ children }) => <h3 className="text-headline-medium font-semibold mb-base mt-wide">{children}</h3>,
19+
h4: ({ children }) => <h4 className="text-headline-small font-semibold mb-tight mt-base">{children}</h4>,
20+
h5: ({ children }) => <h5 className="text-title-large font-semibold mb-tight mt-base">{children}</h5>,
21+
h6: ({ children }) => <h6 className="text-title-medium font-semibold mb-tight mt-base">{children}</h6>,
22+
23+
// Paragraphs
24+
p: ({ children }) => <p className="text-body-medium mb-base leading-body-medium">{children}</p>,
25+
26+
// Lists
27+
ul: ({ children }) => <ul className="list-disc pl-base mb-base space-y-tighter">{children}</ul>,
28+
ol: ({ children }) => <ol className="list-decimal pl-base mb-base space-y-tighter">{children}</ol>,
29+
li: ({ children }) => <li className="text-body-medium pl-tight ml-base">{children}</li>,
30+
31+
// Code
32+
code: ({ inline, children, ...props }) => {
33+
return inline ? (
34+
<code
35+
className="bg-neutral-emphasis text-neutral-content px-tighter py-tightest rounded-smallest font-mono text-body-smaller"
36+
{...props}
37+
>
38+
{children}
39+
</code>
40+
) : (
41+
<code className="font-mono text-body-small" {...props}>
42+
{children}
43+
</code>
44+
);
45+
},
46+
pre: ({ children }) => (
47+
<pre className="bg-neutral-surface-inset border border-neutral-border rounded-small p-base mb-base overflow-x-auto">
48+
{children}
49+
</pre>
50+
),
51+
52+
// Blockquotes
53+
blockquote: ({ children }) => (
54+
<blockquote className="border-l-4 border-primary-border pl-base ml-base mb-base italic text-neutral-content-subtle">
55+
{children}
56+
</blockquote>
57+
),
58+
59+
// Links
60+
a: ({ children, href, ...props }) => (
61+
<a href={href} className="text-primary-content hover:text-primary-content-hover underline" {...props}>
62+
{children}
63+
</a>
64+
),
65+
66+
// Horizontal rule
67+
hr: () => <hr className="border-neutral-border my-wide" />,
68+
69+
// Tables
70+
table: ({ children }) => (
71+
<div className="overflow-x-auto mb-base">
72+
<table className="min-w-full border-collapse border border-neutral-border">{children}</table>
73+
</div>
74+
),
75+
thead: ({ children }) => <thead className="bg-neutral-surface">{children}</thead>,
76+
tbody: ({ children }) => <tbody>{children}</tbody>,
77+
tr: ({ children }) => <tr className="border-b border-neutral-border">{children}</tr>,
78+
th: ({ children }) => (
79+
<th className="border border-neutral-border px-base py-tight text-left font-semibold text-body-medium">
80+
{children}
81+
</th>
82+
),
83+
td: ({ children }) => <td className="border border-neutral-border px-base py-tight text-body-medium">{children}</td>,
84+
85+
// Strong and emphasis
86+
strong: ({ children }) => <strong className="font-bold">{children}</strong>,
87+
em: ({ children }) => <em className="italic">{children}</em>,
88+
89+
// Strikethrough
90+
del: ({ children }) => <del className="line-through text-neutral-content-subtle">{children}</del>,
91+
};
92+
93+
/**
94+
* The `Markdown` element is used to display markdown-formatted text content.
95+
* @example
96+
* <!-- Display markdown from task data -->
97+
* <View>
98+
* <Markdown value="$markdown_text"/>
99+
* </View>
100+
* @example
101+
* <!-- Display static markdown content; indents mark code blocks, so avoid them -->
102+
* <View>
103+
* <Markdown>
104+
* ## Instructions
105+
*
106+
* Please **carefully** read the following text and mark all entities.
107+
* </Markdown>
108+
* </View>
109+
* @example
110+
* <!-- Display markdown with custom styling -->
111+
* <View>
112+
* <Markdown value="$description" style="background: #f5f5f5; padding: 10px; border-radius: 4px;"/>
113+
* </View>
114+
* @name Markdown
115+
* @meta_title Markdown Tag for Rendering Markdown Text
116+
* @meta_description Customize Label Studio with the Markdown tag to display formatted markdown text content for machine learning and data science projects.
117+
* @param {string} value - Markdown text content, either static text or field name in task data (e.g., $markdown_field)
118+
* @param {string} [style] - CSS style string
119+
* @param {string} [className] - Class name of the CSS style to apply
120+
* @param {string} [idAttr] - Unique ID attribute to use in CSS
121+
* @param {region-selected|choice-selected|no-region-selected|choice-unselected} [visibleWhen] Control visibility of the content
122+
* @param {string} [whenTagName] Use with `visibleWhen`. Narrow down visibility by tag name
123+
* @param {string} [whenLabelValue] Use with `visibleWhen="region-selected"`. Narrow down visibility by label value
124+
* @param {string} [whenChoiceValue] Use with `visibleWhen` and `whenTagName`. Narrow down visibility by choice value
125+
*/
126+
const Model = types
127+
.model({
128+
id: types.optional(types.identifier, guidGenerator),
129+
type: "markdown",
130+
value: types.optional(types.string, ""),
131+
_value: types.optional(types.string, ""),
132+
classname: types.optional(types.string, ""),
133+
style: types.maybeNull(types.string),
134+
idattr: types.optional(types.string, ""),
135+
})
136+
.actions((self) => ({
137+
updateValue(store) {
138+
const value = parseValue(self.value, store?.task?.dataObj ?? {});
139+
140+
// cut CDATA
141+
self._value = value.replace(/^\s*<!\[CDATA\[|\]\]>\s*$/g, "");
142+
},
143+
}));
144+
145+
const MarkdownModel = types.compose("MarkdownModel", ProcessAttrsMixin, VisibilityMixin, AnnotationMixin, Model);
146+
147+
const HtxMarkdown = observer(({ item }) => {
148+
const style = item.style ? Tree.cssConverter(item.style) : {};
149+
150+
if (item.isVisible === false) {
151+
style.display = "none";
152+
}
153+
154+
return (
155+
<div id={item.idattr} className={item.classname} style={style}>
156+
<ReactMarkdown components={markdownComponents}>{item._value || ""}</ReactMarkdown>
157+
</div>
158+
);
159+
});
160+
161+
Registry.addTag("markdown", MarkdownModel, HtxMarkdown);
162+
163+
export { HtxMarkdown, MarkdownModel };

web/libs/editor/src/tags/visual/View.jsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ const Model = types
7878
children: Types.unionArray([
7979
"view",
8080
"header",
81+
"markdown",
8182
"labels",
8283
"label",
8384
"table",
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { CollapseModel } from "./Collapse";
22
import { DialogModel } from "./Dialog";
33
import { HeaderModel } from "./Header";
4+
import { MarkdownModel } from "./Markdown";
45
import { ViewModel } from "./View";
56
import { StyleModel } from "./Style";
67
import { FilterModel } from "./Filter";
78

8-
export { CollapseModel, DialogModel, HeaderModel, ViewModel, StyleModel, FilterModel };
9+
export { CollapseModel, DialogModel, HeaderModel, MarkdownModel, ViewModel, StyleModel, FilterModel };

web/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@
105105
"react-joyride": "^2.9.3",
106106
"react-konva": "^18.2.10",
107107
"react-konva-utils": "^0.2.0",
108+
"react-markdown": "^10.1.0",
108109
"react-router": "^5.2.0",
109110
"react-router-dom": "^5.2.0",
110111
"react-singleton-hook": "^3.1.1",

0 commit comments

Comments
 (0)