@orz/ui-markdown 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/LICENSE +29 -0
  2. package/README.md +175 -2
  3. package/dist/attachment-links.d.ts +20 -0
  4. package/dist/attachment-links.d.ts.map +1 -0
  5. package/dist/attachment-links.js +27 -0
  6. package/dist/attachment-links.js.map +1 -0
  7. package/dist/content.d.ts +31 -0
  8. package/dist/content.d.ts.map +1 -0
  9. package/dist/content.js +78 -0
  10. package/dist/content.js.map +1 -0
  11. package/dist/embeds.d.ts +18 -0
  12. package/dist/embeds.d.ts.map +1 -0
  13. package/dist/embeds.js +59 -0
  14. package/dist/embeds.js.map +1 -0
  15. package/dist/external-links.d.ts +23 -0
  16. package/dist/external-links.d.ts.map +1 -0
  17. package/dist/external-links.js +43 -0
  18. package/dist/external-links.js.map +1 -0
  19. package/dist/index.d.ts +7 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +6 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/lib/markdown-processor.d.ts +23 -0
  24. package/dist/lib/markdown-processor.d.ts.map +1 -0
  25. package/dist/lib/markdown-processor.js +35 -0
  26. package/dist/lib/markdown-processor.js.map +1 -0
  27. package/dist/lib/rehype-details.d.ts +13 -0
  28. package/dist/lib/rehype-details.d.ts.map +1 -0
  29. package/dist/lib/rehype-details.js +256 -0
  30. package/dist/lib/rehype-details.js.map +1 -0
  31. package/dist/lib/rehype-source-lines.d.ts +23 -0
  32. package/dist/lib/rehype-source-lines.d.ts.map +1 -0
  33. package/dist/lib/rehype-source-lines.js +77 -0
  34. package/dist/lib/rehype-source-lines.js.map +1 -0
  35. package/dist/lib/remark-frontmatter-table.d.ts +119 -0
  36. package/dist/lib/remark-frontmatter-table.d.ts.map +1 -0
  37. package/dist/lib/remark-frontmatter-table.js +212 -0
  38. package/dist/lib/remark-frontmatter-table.js.map +1 -0
  39. package/dist/lib/remark-rejected-urls.d.ts +23 -0
  40. package/dist/lib/remark-rejected-urls.d.ts.map +1 -0
  41. package/dist/lib/remark-rejected-urls.js +79 -0
  42. package/dist/lib/remark-rejected-urls.js.map +1 -0
  43. package/dist/markdown.d.ts +30 -0
  44. package/dist/markdown.d.ts.map +1 -0
  45. package/dist/markdown.js +146 -0
  46. package/dist/markdown.js.map +1 -0
  47. package/dist/plugin.d.ts +39 -0
  48. package/dist/plugin.d.ts.map +1 -0
  49. package/dist/plugin.js +2 -0
  50. package/dist/plugin.js.map +1 -0
  51. package/package.json +66 -3
  52. package/styles.css +293 -0
package/package.json CHANGED
@@ -1,6 +1,69 @@
1
1
  {
2
2
  "name": "@orz/ui-markdown",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.0",
4
+ "description": "GitHub-style markdown rendering on react-markdown, extended by markdown plugins and drawing code and embeds through @orz/ui-content",
5
+ "license": "BSD-3-Clause",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/orzFly/ui.git",
9
+ "directory": "packages/ui-markdown"
10
+ },
11
+ "type": "module",
12
+ "sideEffects": [
13
+ "**/*.css"
14
+ ],
15
+ "exports": {
16
+ ".": {
17
+ "@orz/source": "./src/index.ts",
18
+ "types": "./dist/index.d.ts",
19
+ "default": "./dist/index.js"
20
+ },
21
+ "./lib/*": {
22
+ "@orz/source": "./src/lib/*.ts",
23
+ "types": "./dist/lib/*.d.ts",
24
+ "default": "./dist/lib/*.js"
25
+ },
26
+ "./styles.css": "./styles.css"
27
+ },
28
+ "files": [
29
+ "dist",
30
+ "styles.css"
31
+ ],
32
+ "publishConfig": {
33
+ "access": "public"
34
+ },
35
+ "peerDependencies": {
36
+ "lucide-react": "^1",
37
+ "react": "^19",
38
+ "react-dom": "^19",
39
+ "@orz/ui": "^0.1.0",
40
+ "@orz/ui-content": "^0.1.0"
41
+ },
42
+ "dependencies": {
43
+ "@types/hast": "^3.0.5",
44
+ "@types/mdast": "^4.0.4",
45
+ "react-markdown": "^10.1.0",
46
+ "remark-frontmatter": "^5.0.0",
47
+ "remark-gfm": "^4.0.1",
48
+ "remark-parse": "^11.0.0",
49
+ "unified": "^11.0.5"
50
+ },
51
+ "devDependencies": {
52
+ "@testing-library/react": "^16.3.2",
53
+ "@types/node": "^22.20.5",
54
+ "@types/react": "^19.2.18",
55
+ "@types/react-dom": "^19.2.4",
56
+ "happy-dom": "^21.0.3",
57
+ "lucide-react": "^1.31.0",
58
+ "react": "^19.2.8",
59
+ "react-dom": "^19.2.8",
60
+ "vitest": "^5.0.5",
61
+ "@orz/ui-content": "0.1.0",
62
+ "@orz/ui": "0.1.0"
63
+ },
64
+ "scripts": {
65
+ "build": "tsc -p tsconfig.build.json",
66
+ "typecheck": "tsc --noEmit",
67
+ "test": "vitest run"
68
+ }
6
69
  }
package/styles.css ADDED
@@ -0,0 +1,293 @@
1
+ @layer components {
2
+ /* GitHub-flavoured markdown typography (T-32). One plain-CSS block —
3
+ theme tokens make it track light/dark for free, and element swaps in
4
+ the renderer (react-markdown `components`) don't need class surgery.
5
+ Sizes follow github.com issue comments (14px base). */
6
+ .markdown-body {
7
+ font-size: 0.875rem;
8
+ line-height: 1.6;
9
+ overflow-wrap: break-word;
10
+ }
11
+ .markdown-body > :first-child {
12
+ margin-top: 0;
13
+ }
14
+ .markdown-body > :last-child {
15
+ margin-bottom: 0;
16
+ }
17
+
18
+ /* .markdown-paragraph is a <p> stand-in for paragraphs carrying a
19
+ block-level embed — block content HTML forbids inside real <p>. */
20
+ .markdown-body p,
21
+ .markdown-body .markdown-paragraph,
22
+ .markdown-body blockquote,
23
+ .markdown-body ul,
24
+ .markdown-body ol,
25
+ .markdown-body table,
26
+ .markdown-body details,
27
+ .markdown-body .markdown-fence,
28
+ .markdown-body pre {
29
+ margin-block: 0 1rem;
30
+ }
31
+
32
+ /* No JS toggle anywhere: native <summary> brings Enter/Space, the `open`
33
+ state and the screen-reader semantics with it. */
34
+ .markdown-body summary {
35
+ display: flex;
36
+ align-items: center;
37
+ gap: 0.4em;
38
+ cursor: pointer;
39
+ font-weight: 600;
40
+ list-style: none;
41
+ }
42
+ .markdown-body summary::-webkit-details-marker {
43
+ display: none;
44
+ }
45
+ /* lucide's `chevron-right`, painted through a mask so it follows the text
46
+ colour into dark mode. */
47
+ .markdown-body summary::before {
48
+ content: "";
49
+ flex: none;
50
+ width: 1em;
51
+ height: 1em;
52
+ background-color: currentColor;
53
+ mask:
54
+ url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m9 18 6-6-6-6'/%3E%3C/svg%3E")
55
+ center / contain no-repeat;
56
+ transition: rotate 120ms ease;
57
+ }
58
+ .markdown-body details[open] > summary::before {
59
+ rotate: 90deg;
60
+ }
61
+ .markdown-body summary > :last-child {
62
+ margin-bottom: 0;
63
+ }
64
+ .markdown-body details > :last-child {
65
+ margin-bottom: 0;
66
+ }
67
+
68
+ .markdown-body h1,
69
+ .markdown-body h2,
70
+ .markdown-body h3,
71
+ .markdown-body h4,
72
+ .markdown-body h5,
73
+ .markdown-body h6 {
74
+ margin-block: 1.5rem 1rem;
75
+ font-weight: 600;
76
+ line-height: 1.25;
77
+ }
78
+ .markdown-body h1,
79
+ .markdown-body h2 {
80
+ padding-bottom: 0.3em;
81
+ border-bottom: 1px solid var(--border);
82
+ }
83
+ .markdown-body h1 {
84
+ font-size: 2em;
85
+ }
86
+ .markdown-body h2 {
87
+ font-size: 1.5em;
88
+ }
89
+ .markdown-body h3 {
90
+ font-size: 1.25em;
91
+ }
92
+ .markdown-body h4 {
93
+ font-size: 1em;
94
+ }
95
+ .markdown-body h5 {
96
+ font-size: 0.875em;
97
+ }
98
+ .markdown-body h6 {
99
+ font-size: 0.85em;
100
+ color: var(--muted-foreground);
101
+ }
102
+
103
+ .markdown-body a {
104
+ text-decoration: underline;
105
+ text-underline-offset: 2px;
106
+ }
107
+ /* What externalLinks sets a target on. Generated rather than written into
108
+ the DOM so a copy of the paragraph does not pick it up; the empty alt
109
+ keeps screen readers from announcing an arrow. Inline-block stops the
110
+ underline running under it. An image link is its own affordance and
111
+ gets no arrow. */
112
+ .markdown-body a[target="_blank"]:not(:has(img))::after {
113
+ content: "↗";
114
+ /* biome-ignore lint/suspicious/noDuplicateProperties: the line above is the fallback where content takes no alt text */
115
+ content: "↗" / "";
116
+ display: inline-block;
117
+ margin-left: 0.1em;
118
+ font-size: 0.8em;
119
+ text-decoration: none;
120
+ }
121
+
122
+ .markdown-body ul,
123
+ .markdown-body ol {
124
+ padding-left: 2em;
125
+ }
126
+ .markdown-body ul {
127
+ list-style-type: disc;
128
+ }
129
+ .markdown-body ol {
130
+ list-style-type: decimal;
131
+ }
132
+ /* Nested lists sit tight under their parent item instead of taking the
133
+ block gap; declared ahead of the deeper marker rules only to keep
134
+ specificity ascending. */
135
+ .markdown-body ul ul,
136
+ .markdown-body ul ol,
137
+ .markdown-body ol ul,
138
+ .markdown-body ol ol {
139
+ margin-block: 0.25em 0;
140
+ }
141
+ .markdown-body ul ul,
142
+ .markdown-body ol ul {
143
+ list-style-type: circle;
144
+ }
145
+ .markdown-body ul ul ul,
146
+ .markdown-body ol ul ul {
147
+ list-style-type: square;
148
+ }
149
+ .markdown-body li + li {
150
+ margin-top: 0.25em;
151
+ }
152
+ /* remark-gfm task lists: the checkbox replaces the marker. */
153
+ .markdown-body li.task-list-item {
154
+ list-style-type: none;
155
+ }
156
+ .markdown-body ul.contains-task-list {
157
+ padding-left: 1.25em;
158
+ }
159
+ .markdown-body .task-list-item input[type="checkbox"] {
160
+ margin-right: 0.4em;
161
+ vertical-align: middle;
162
+ }
163
+
164
+ .markdown-body blockquote {
165
+ padding: 0 1em;
166
+ border-left: 0.25em solid var(--border);
167
+ color: var(--muted-foreground);
168
+ }
169
+
170
+ .markdown-body hr {
171
+ height: 0.25em;
172
+ margin: 1.5rem 0;
173
+ border: 0;
174
+ background-color: var(--border);
175
+ }
176
+
177
+ /* Tables scroll horizontally when wider than their container —
178
+ GitHub's own mechanism (block table, max-content width), which
179
+ needs no wrapper element around the <table>. */
180
+ .markdown-body table {
181
+ display: block;
182
+ width: max-content;
183
+ max-width: 100%;
184
+ overflow-x: auto;
185
+ border-collapse: collapse;
186
+ border-spacing: 0;
187
+ }
188
+ .markdown-body th,
189
+ .markdown-body td {
190
+ padding: 6px 13px;
191
+ border: 1px solid var(--border);
192
+ }
193
+ .markdown-body th {
194
+ font-weight: 600;
195
+ background-color: var(--muted);
196
+ }
197
+ .markdown-body tbody tr:nth-child(2n) {
198
+ background-color: color-mix(in oklab, var(--muted) 50%, transparent);
199
+ }
200
+
201
+ /* A document's frontmatter, as a metadata grid (T-240). It is a `<table>`
202
+ so that anything reading the tree as tables gets one, and it must not
203
+ look like one: the keys are syntax, not a header row, so they get mono at
204
+ small size in the muted tone while the values keep body typography.
205
+
206
+ Grid rather than table layout for one concrete reason: `.markdown-body
207
+ table` above is `width: max-content`, which would let a long value push
208
+ the grid into its own horizontal scrollbar. `minmax(0, 1fr)` on the value
209
+ column caps it at the container instead, and `subgrid` is what still
210
+ aligns every key against every other. */
211
+ .markdown-body table.markdown-frontmatter {
212
+ display: grid;
213
+ grid-template-columns: max-content minmax(0, 1fr);
214
+ width: 100%;
215
+ overflow-x: visible;
216
+ border: 1px solid var(--border);
217
+ border-radius: var(--radius-sm);
218
+ background-color: color-mix(in oklab, var(--muted) 45%, transparent);
219
+ font-size: 0.9375em;
220
+ }
221
+ .markdown-body table.markdown-frontmatter tbody,
222
+ .markdown-body table.markdown-frontmatter tr {
223
+ display: grid;
224
+ grid-column: 1 / -1;
225
+ grid-template-columns: subgrid;
226
+ }
227
+ /* The striping above counts rows; here every row is a field and reads better
228
+ separated by a hairline than by alternating fills. */
229
+ .markdown-body table.markdown-frontmatter tbody tr {
230
+ background-color: transparent;
231
+ }
232
+ .markdown-body table.markdown-frontmatter tr + tr {
233
+ border-top: 1px solid color-mix(in oklab, var(--border) 60%, transparent);
234
+ }
235
+ .markdown-body table.markdown-frontmatter th,
236
+ .markdown-body table.markdown-frontmatter td {
237
+ border: 0;
238
+ padding: 0.2rem 0.6rem;
239
+ text-align: left;
240
+ font-weight: 400;
241
+ background-color: transparent;
242
+ }
243
+ /* `--font-mono` is Tailwind's theme default rather than an @orz/ui token,
244
+ and Tailwind only emits it when something uses it — hence the fallback. */
245
+ .markdown-body table.markdown-frontmatter th {
246
+ font-family: var(--font-mono, ui-monospace, monospace);
247
+ font-size: 0.8125em;
248
+ line-height: 1.7;
249
+ color: var(--muted-foreground);
250
+ white-space: nowrap;
251
+ }
252
+ /* Indentation and line breaks are the whole of what a nested map or a block
253
+ scalar shows, since the value is never parsed (T-240). */
254
+ .markdown-body table.markdown-frontmatter td {
255
+ white-space: pre-wrap;
256
+ }
257
+ /* A field with no key at all — TOML's `[section]`, or a line ahead of the
258
+ first field. Its key cell exists so a reader of the table still sees two
259
+ columns, and spans away here so the row reads as the full-width line it
260
+ is. */
261
+ .markdown-body table.markdown-frontmatter th:empty {
262
+ display: none;
263
+ }
264
+ .markdown-body table.markdown-frontmatter th:empty + td {
265
+ grid-column: 1 / -1;
266
+ }
267
+
268
+ .markdown-body img {
269
+ max-width: 100%;
270
+ }
271
+
272
+ /* Code styling stays intentionally shallow: a registered renderer usually
273
+ replaces the <pre>, so these rules only carry the unrendered fallback
274
+ (T-31). */
275
+ .markdown-body code {
276
+ padding: 0.2em 0.4em;
277
+ border-radius: var(--radius-sm);
278
+ background-color: var(--muted);
279
+ font-size: 0.85em;
280
+ }
281
+ .markdown-body pre {
282
+ padding: 0.75rem;
283
+ border-radius: var(--radius-md);
284
+ background-color: var(--muted);
285
+ overflow-x: auto;
286
+ line-height: 1.45;
287
+ }
288
+ .markdown-body pre code {
289
+ padding: 0;
290
+ border-radius: 0;
291
+ background-color: transparent;
292
+ }
293
+ }