@duskmoon-dev/components 0.3.0 → 0.3.1

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.
package/README.md CHANGED
@@ -1,18 +1,26 @@
1
1
  # @duskmoon-dev/components
2
2
 
3
- The core React component library for Duskmoon.
3
+ The DuskMoon React component package.
4
4
 
5
5
  ## Installation
6
6
 
7
7
  ```bash
8
- bun add @duskmoon-dev/components
8
+ bun add @duskmoon-dev/components @duskmoon-dev/core react react-dom
9
+ ```
10
+
11
+ Import the stylesheet once in your app:
12
+
13
+ ```tsx
14
+ import "@duskmoon-dev/components/styles.css";
9
15
  ```
10
16
 
11
17
  ## Features
12
18
 
13
- - **TypeScript First**: Written entirely in TypeScript for excellent developer experience.
14
- - **Optimized for Next.js**: Compatible with Next.js 15 and React 19.
15
- - **Modular Exports**: Import only what you need.
19
+ - **React 19 ready**: Components and examples target the current React runtime.
20
+ - **TypeScript first**: Public props and helper APIs ship with declarations.
21
+ - **Modular exports**: Import from the root package or from component subpaths.
22
+ - **DuskMoon styling**: Component classes and CSS are backed by
23
+ `@duskmoon-dev/core`.
16
24
 
17
25
  ## Usage
18
26
 
@@ -25,6 +33,23 @@ export default function MyComponent() {
25
33
  }
26
34
  ```
27
35
 
36
+ Root imports are also supported:
37
+
38
+ ```tsx
39
+ import { Button, DmTable, theme } from "@duskmoon-dev/components";
40
+ ```
41
+
42
+ ## Public Surface
43
+
44
+ - 71 standard components such as `Button`, `Table`, `Modal`, `Select`, and
45
+ `Typography`.
46
+ - 21 DuskMoon workflow components such as `DmLayout`, `DmSearch`, `DmTable`,
47
+ `DmProTable`, and `DmToolbar`.
48
+ - 13 infrastructure exports including `theme`, `version`,
49
+ `unstableSetRender`, `GetProps`, `GetRef`, and DuskMoon theme helpers.
50
+ - Component subpath exports follow `@duskmoon-dev/components/{component-id}`,
51
+ for example `@duskmoon-dev/components/date-picker`.
52
+
28
53
  ## Development
29
54
 
30
55
  ```bash
@@ -36,4 +61,7 @@ bun run test
36
61
 
37
62
  # Typecheck
38
63
  bun run typecheck
64
+
65
+ # Check parity manifest coverage
66
+ bun run parity:components
39
67
  ```
@@ -88,5 +88,6 @@ export * from "./transfer";
88
88
  export * from "./tree";
89
89
  export * from "./tree-select";
90
90
  export * from "./typography";
91
+ export * from "./markdown";
91
92
  export * from "./upload";
92
93
  export * from "./watermark";
@@ -2577,6 +2577,18 @@ function getTypographyLinkClasses({
2577
2577
  }) {
2578
2578
  return cn(typographyLinkClass, status && typographyStatusClasses[status], disabled && typographyDisabledClass, ellipsis && typographyEllipsisClass, className);
2579
2579
  }
2580
+ // src/classes/markdown.ts
2581
+ var markdownBaseClass = "markdown-body";
2582
+ var markdownFrontMatterClass = "dm-front-matter";
2583
+ var markdownColorCodeClass = "dm-color-code";
2584
+ var markdownColorChipClass = "dm-color-chip";
2585
+ var markdownColorChipSwatchClass = "dm-color-chip-swatch";
2586
+ function getMarkdownClasses({
2587
+ variant,
2588
+ className
2589
+ }) {
2590
+ return cn(markdownBaseClass, variant && `${markdownBaseClass}-${variant}`, className);
2591
+ }
2580
2592
  // src/classes/upload.ts
2581
2593
  var uploadBaseClass = "file-upload";
2582
2594
  var uploadTriggerClass = "file-upload-trigger";
@@ -3061,6 +3073,11 @@ export {
3061
3073
  mentionsNoOptionsClass,
3062
3074
  mentionsDropdownClass,
3063
3075
  mentionsBaseClass,
3076
+ markdownFrontMatterClass,
3077
+ markdownColorCodeClass,
3078
+ markdownColorChipSwatchClass,
3079
+ markdownColorChipClass,
3080
+ markdownBaseClass,
3064
3081
  listSizeClasses,
3065
3082
  listItemTrailingClass,
3066
3083
  listItemTextClass,
@@ -3195,6 +3212,7 @@ export {
3195
3212
  getMentionsOptionClasses,
3196
3213
  getMentionsDropdownClasses,
3197
3214
  getMentionsClasses,
3215
+ getMarkdownClasses,
3198
3216
  getListItemClasses,
3199
3217
  getListClasses,
3200
3218
  getLayoutSiderClasses,
@@ -0,0 +1,9 @@
1
+ export declare const markdownBaseClass = "markdown-body";
2
+ export declare const markdownFrontMatterClass = "dm-front-matter";
3
+ export declare const markdownColorCodeClass = "dm-color-code";
4
+ export declare const markdownColorChipClass = "dm-color-chip";
5
+ export declare const markdownColorChipSwatchClass = "dm-color-chip-swatch";
6
+ export declare function getMarkdownClasses({ variant, className, }: {
7
+ variant?: string;
8
+ className?: string;
9
+ }): string;
@@ -0,0 +1,3 @@
1
+ import React from "react";
2
+ import type { MarkdownProps } from "./Markdown.types";
3
+ export declare const Markdown: React.ForwardRefExoticComponent<Omit<MarkdownProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,14 @@
1
+ import type { ComponentProps } from "react";
2
+ export type MarkdownFrontMatterMode = "render" | "hidden" | "disabled";
3
+ export interface MarkdownProps extends Omit<ComponentProps<"div">, "children"> {
4
+ /** Markdown source to render. */
5
+ markdown?: string;
6
+ /** Convert soft line endings to `<br>` elements. @default true */
7
+ breaks?: boolean;
8
+ /** Add a visual swatch to inline code containing a valid CSS color. @default true */
9
+ colorChips?: boolean;
10
+ /** Control handling of YAML front matter at the beginning of the source. @default "render" */
11
+ frontMatter?: MarkdownFrontMatterMode;
12
+ /** Optional color variant used to build the `markdown-body-{variant}` class. */
13
+ variant?: string;
14
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./Markdown";
2
+ export type * from "./Markdown.types";
@@ -0,0 +1,171 @@
1
+ "use client";
2
+
3
+ // src/components/markdown/Markdown.tsx
4
+ import { forwardRef } from "react";
5
+ import ReactMarkdown from "react-markdown";
6
+ import remarkBreaks from "remark-breaks";
7
+ import remarkGfm from "remark-gfm";
8
+
9
+ // ../../node_modules/.bun/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs
10
+ function r(e) {
11
+ var t, f, n = "";
12
+ if (typeof e == "string" || typeof e == "number")
13
+ n += e;
14
+ else if (typeof e == "object")
15
+ if (Array.isArray(e)) {
16
+ var o = e.length;
17
+ for (t = 0;t < o; t++)
18
+ e[t] && (f = r(e[t])) && (n && (n += " "), n += f);
19
+ } else
20
+ for (f in e)
21
+ e[f] && (n && (n += " "), n += f);
22
+ return n;
23
+ }
24
+ function clsx() {
25
+ for (var e, t, f = 0, n = "", o = arguments.length;f < o; f++)
26
+ (e = arguments[f]) && (t = r(e)) && (n && (n += " "), n += t);
27
+ return n;
28
+ }
29
+
30
+ // src/utils/cn.ts
31
+ function cn(...inputs) {
32
+ return clsx(inputs);
33
+ }
34
+ // src/classes/markdown.ts
35
+ var markdownBaseClass = "markdown-body";
36
+ var markdownFrontMatterClass = "dm-front-matter";
37
+ var markdownColorCodeClass = "dm-color-code";
38
+ var markdownColorChipClass = "dm-color-chip";
39
+ var markdownColorChipSwatchClass = "dm-color-chip-swatch";
40
+ function getMarkdownClasses({
41
+ variant,
42
+ className
43
+ }) {
44
+ return cn(markdownBaseClass, variant && `${markdownBaseClass}-${variant}`, className);
45
+ }
46
+
47
+ // src/components/markdown/Markdown.tsx
48
+ import { jsx, jsxs } from "react/jsx-runtime";
49
+ function splitFrontMatter(markdown) {
50
+ const source = markdown.startsWith("\uFEFF") ? markdown.slice(1) : markdown;
51
+ const lines = source.split(/\r?\n/);
52
+ if (lines.length < 2 || lines[0] !== "---")
53
+ return;
54
+ for (let index = 1;index < lines.length; index += 1) {
55
+ if (lines[index] === "---" || lines[index] === "...") {
56
+ return {
57
+ source: lines.slice(1, index).join(`
58
+ `),
59
+ body: lines.slice(index + 1).join(`
60
+ `)
61
+ };
62
+ }
63
+ }
64
+ return;
65
+ }
66
+ function numberInRange(value, minimum, maximum) {
67
+ if (!/^[+-]?(?:\d+(?:\.\d+)?|\.\d+)$/.test(value))
68
+ return false;
69
+ const number = Number(value);
70
+ return Number.isFinite(number) && number >= minimum && number <= maximum;
71
+ }
72
+ function percentage(value) {
73
+ return value.endsWith("%") && numberInRange(value.slice(0, -1), 0, 100);
74
+ }
75
+ function alpha(value) {
76
+ return percentage(value) || numberInRange(value, 0, 1);
77
+ }
78
+ function isCssColor(value) {
79
+ if (!value || Array.from(value).some((character) => character.charCodeAt(0) > 127) || value.trim() !== value) {
80
+ return false;
81
+ }
82
+ if (/^#[\da-f]{3,4}(?:[\da-f]{2}){0,2}$/i.test(value))
83
+ return true;
84
+ const match = /^(rgba?|hsla?)\((.*)\)$/i.exec(value);
85
+ if (!match || match[2].includes("(") || match[2].includes(")"))
86
+ return false;
87
+ const name = match[1].toLowerCase();
88
+ const parts = match[2].split(",").map((part) => part.trim());
89
+ const hasAlpha = name.endsWith("a");
90
+ if (parts.length !== (hasAlpha ? 4 : 3))
91
+ return false;
92
+ if (name.startsWith("rgb")) {
93
+ const channels = parts.slice(0, 3);
94
+ const allPercentages = channels.every(percentage);
95
+ const allNumbers = channels.every((part) => numberInRange(part, 0, 255));
96
+ return (allPercentages || allNumbers) && (!hasAlpha || alpha(parts[3]));
97
+ }
98
+ const hue = parts[0].replace(/deg$/i, "");
99
+ return numberInRange(hue, 0, 360) && percentage(parts[1]) && percentage(parts[2]) && (!hasAlpha || alpha(parts[3]));
100
+ }
101
+ function markdownSource(markdown, mode) {
102
+ if (mode === "disabled")
103
+ return { body: markdown, source: undefined };
104
+ const frontMatter = splitFrontMatter(markdown);
105
+ return frontMatter ?? { body: markdown, source: undefined };
106
+ }
107
+ var Markdown = forwardRef(function Markdown2({
108
+ markdown = "",
109
+ breaks = true,
110
+ colorChips = true,
111
+ frontMatter = "render",
112
+ variant,
113
+ className,
114
+ ...props
115
+ }, ref) {
116
+ const source = markdownSource(markdown, frontMatter);
117
+ const components = colorChips ? {
118
+ code({ children, className: codeClassName, node, ...codeProps }) {
119
+ const value = String(children).replace(/\n$/, "");
120
+ const isInline = node?.position?.start.line === node?.position?.end.line;
121
+ if (!isInline || !isCssColor(value)) {
122
+ return /* @__PURE__ */ jsx("code", {
123
+ className: codeClassName,
124
+ ...codeProps,
125
+ children
126
+ });
127
+ }
128
+ return /* @__PURE__ */ jsxs("code", {
129
+ className: markdownColorCodeClass,
130
+ ...codeProps,
131
+ children: [
132
+ children,
133
+ /* @__PURE__ */ jsx("span", {
134
+ className: markdownColorChipClass,
135
+ role: "img",
136
+ "aria-label": `Color ${value}`,
137
+ children: /* @__PURE__ */ jsx("span", {
138
+ className: markdownColorChipSwatchClass,
139
+ style: { backgroundColor: value },
140
+ "aria-hidden": "true"
141
+ })
142
+ })
143
+ ]
144
+ });
145
+ }
146
+ } : undefined;
147
+ return /* @__PURE__ */ jsxs("div", {
148
+ ref,
149
+ className: getMarkdownClasses({ variant, className }),
150
+ ...props,
151
+ children: [
152
+ frontMatter === "render" && source.source !== undefined ? /* @__PURE__ */ jsx("div", {
153
+ className: markdownFrontMatterClass,
154
+ children: /* @__PURE__ */ jsx("pre", {
155
+ children: /* @__PURE__ */ jsx("code", {
156
+ className: "language-yaml",
157
+ children: source.source
158
+ })
159
+ })
160
+ }) : null,
161
+ /* @__PURE__ */ jsx(ReactMarkdown, {
162
+ remarkPlugins: breaks ? [remarkGfm, remarkBreaks] : [remarkGfm],
163
+ components,
164
+ children: source.body
165
+ })
166
+ ]
167
+ });
168
+ });
169
+ export {
170
+ Markdown
171
+ };
@@ -1 +1 @@
1
- export declare const version = "0.3.0";
1
+ export declare const version = "0.3.1";
package/dist/index.d.ts CHANGED
@@ -95,5 +95,6 @@ export * from "./components/transfer";
95
95
  export * from "./components/tree";
96
96
  export * from "./components/tree-select";
97
97
  export * from "./components/typography";
98
+ export * from "./components/markdown";
98
99
  export * from "./components/upload";
99
100
  export * from "./components/watermark";
package/dist/index.js CHANGED
@@ -79,7 +79,7 @@ var ThemeInitScript = () => {
79
79
  import { useCallback as useCallback2, useState as useState2 } from "react";
80
80
 
81
81
  // src/generated/version.ts
82
- var version = "0.3.0";
82
+ var version = "0.3.1";
83
83
 
84
84
  // src/infrastructure.ts
85
85
  var theme = {
@@ -17498,8 +17498,149 @@ var Typography = Object.assign(TypographyRoot, {
17498
17498
  Paragraph,
17499
17499
  Link
17500
17500
  });
17501
+ // src/components/markdown/Markdown.tsx
17502
+ import { forwardRef as forwardRef78 } from "react";
17503
+ import ReactMarkdown from "react-markdown";
17504
+ import remarkBreaks from "remark-breaks";
17505
+ import remarkGfm from "remark-gfm";
17506
+
17507
+ // src/classes/markdown.ts
17508
+ var markdownBaseClass = "markdown-body";
17509
+ var markdownFrontMatterClass = "dm-front-matter";
17510
+ var markdownColorCodeClass = "dm-color-code";
17511
+ var markdownColorChipClass = "dm-color-chip";
17512
+ var markdownColorChipSwatchClass = "dm-color-chip-swatch";
17513
+ function getMarkdownClasses({
17514
+ variant,
17515
+ className
17516
+ }) {
17517
+ return cn(markdownBaseClass, variant && `${markdownBaseClass}-${variant}`, className);
17518
+ }
17519
+
17520
+ // src/components/markdown/Markdown.tsx
17521
+ import { jsx as jsx92, jsxs as jsxs73 } from "react/jsx-runtime";
17522
+ function splitFrontMatter(markdown) {
17523
+ const source = markdown.startsWith("\uFEFF") ? markdown.slice(1) : markdown;
17524
+ const lines = source.split(/\r?\n/);
17525
+ if (lines.length < 2 || lines[0] !== "---")
17526
+ return;
17527
+ for (let index = 1;index < lines.length; index += 1) {
17528
+ if (lines[index] === "---" || lines[index] === "...") {
17529
+ return {
17530
+ source: lines.slice(1, index).join(`
17531
+ `),
17532
+ body: lines.slice(index + 1).join(`
17533
+ `)
17534
+ };
17535
+ }
17536
+ }
17537
+ return;
17538
+ }
17539
+ function numberInRange(value, minimum, maximum) {
17540
+ if (!/^[+-]?(?:\d+(?:\.\d+)?|\.\d+)$/.test(value))
17541
+ return false;
17542
+ const number = Number(value);
17543
+ return Number.isFinite(number) && number >= minimum && number <= maximum;
17544
+ }
17545
+ function percentage(value) {
17546
+ return value.endsWith("%") && numberInRange(value.slice(0, -1), 0, 100);
17547
+ }
17548
+ function alpha(value) {
17549
+ return percentage(value) || numberInRange(value, 0, 1);
17550
+ }
17551
+ function isCssColor(value) {
17552
+ if (!value || Array.from(value).some((character) => character.charCodeAt(0) > 127) || value.trim() !== value) {
17553
+ return false;
17554
+ }
17555
+ if (/^#[\da-f]{3,4}(?:[\da-f]{2}){0,2}$/i.test(value))
17556
+ return true;
17557
+ const match = /^(rgba?|hsla?)\((.*)\)$/i.exec(value);
17558
+ if (!match || match[2].includes("(") || match[2].includes(")"))
17559
+ return false;
17560
+ const name = match[1].toLowerCase();
17561
+ const parts = match[2].split(",").map((part) => part.trim());
17562
+ const hasAlpha = name.endsWith("a");
17563
+ if (parts.length !== (hasAlpha ? 4 : 3))
17564
+ return false;
17565
+ if (name.startsWith("rgb")) {
17566
+ const channels = parts.slice(0, 3);
17567
+ const allPercentages = channels.every(percentage);
17568
+ const allNumbers = channels.every((part) => numberInRange(part, 0, 255));
17569
+ return (allPercentages || allNumbers) && (!hasAlpha || alpha(parts[3]));
17570
+ }
17571
+ const hue = parts[0].replace(/deg$/i, "");
17572
+ return numberInRange(hue, 0, 360) && percentage(parts[1]) && percentage(parts[2]) && (!hasAlpha || alpha(parts[3]));
17573
+ }
17574
+ function markdownSource(markdown, mode) {
17575
+ if (mode === "disabled")
17576
+ return { body: markdown, source: undefined };
17577
+ const frontMatter = splitFrontMatter(markdown);
17578
+ return frontMatter ?? { body: markdown, source: undefined };
17579
+ }
17580
+ var Markdown = forwardRef78(function Markdown2({
17581
+ markdown = "",
17582
+ breaks = true,
17583
+ colorChips = true,
17584
+ frontMatter = "render",
17585
+ variant,
17586
+ className,
17587
+ ...props
17588
+ }, ref) {
17589
+ const source = markdownSource(markdown, frontMatter);
17590
+ const components = colorChips ? {
17591
+ code({ children, className: codeClassName, node, ...codeProps }) {
17592
+ const value = String(children).replace(/\n$/, "");
17593
+ const isInline = node?.position?.start.line === node?.position?.end.line;
17594
+ if (!isInline || !isCssColor(value)) {
17595
+ return /* @__PURE__ */ jsx92("code", {
17596
+ className: codeClassName,
17597
+ ...codeProps,
17598
+ children
17599
+ });
17600
+ }
17601
+ return /* @__PURE__ */ jsxs73("code", {
17602
+ className: markdownColorCodeClass,
17603
+ ...codeProps,
17604
+ children: [
17605
+ children,
17606
+ /* @__PURE__ */ jsx92("span", {
17607
+ className: markdownColorChipClass,
17608
+ role: "img",
17609
+ "aria-label": `Color ${value}`,
17610
+ children: /* @__PURE__ */ jsx92("span", {
17611
+ className: markdownColorChipSwatchClass,
17612
+ style: { backgroundColor: value },
17613
+ "aria-hidden": "true"
17614
+ })
17615
+ })
17616
+ ]
17617
+ });
17618
+ }
17619
+ } : undefined;
17620
+ return /* @__PURE__ */ jsxs73("div", {
17621
+ ref,
17622
+ className: getMarkdownClasses({ variant, className }),
17623
+ ...props,
17624
+ children: [
17625
+ frontMatter === "render" && source.source !== undefined ? /* @__PURE__ */ jsx92("div", {
17626
+ className: markdownFrontMatterClass,
17627
+ children: /* @__PURE__ */ jsx92("pre", {
17628
+ children: /* @__PURE__ */ jsx92("code", {
17629
+ className: "language-yaml",
17630
+ children: source.source
17631
+ })
17632
+ })
17633
+ }) : null,
17634
+ /* @__PURE__ */ jsx92(ReactMarkdown, {
17635
+ remarkPlugins: breaks ? [remarkGfm, remarkBreaks] : [remarkGfm],
17636
+ components,
17637
+ children: source.body
17638
+ })
17639
+ ]
17640
+ });
17641
+ });
17501
17642
  // src/components/upload/Upload.tsx
17502
- import React84, { forwardRef as forwardRef78, useLayoutEffect as useLayoutEffect3, useRef as useRef19, useState as useState57 } from "react";
17643
+ import React85, { forwardRef as forwardRef79, useLayoutEffect as useLayoutEffect3, useRef as useRef19, useState as useState57 } from "react";
17503
17644
 
17504
17645
  // src/classes/upload.ts
17505
17646
  var uploadBaseClass = "file-upload";
@@ -17544,7 +17685,7 @@ function getUploadItemClasses({
17544
17685
  }
17545
17686
 
17546
17687
  // src/components/upload/Upload.tsx
17547
- import { jsx as jsx92, jsxs as jsxs73 } from "react/jsx-runtime";
17688
+ import { jsx as jsx93, jsxs as jsxs74 } from "react/jsx-runtime";
17548
17689
  var LIST_IGNORE = Symbol("Upload.LIST_IGNORE");
17549
17690
  var uidSeed = 0;
17550
17691
  function nextUid() {
@@ -17711,12 +17852,12 @@ function UploadRoot({
17711
17852
  }
17712
17853
  }
17713
17854
  function renderTrigger() {
17714
- const trigger = children ?? /* @__PURE__ */ jsx92("button", {
17855
+ const trigger = children ?? /* @__PURE__ */ jsx93("button", {
17715
17856
  type: "button",
17716
17857
  children: "Upload"
17717
17858
  });
17718
17859
  if (dragger) {
17719
- return /* @__PURE__ */ jsx92("div", {
17860
+ return /* @__PURE__ */ jsx93("div", {
17720
17861
  className: uploadDropzoneClass,
17721
17862
  role: "button",
17722
17863
  tabIndex: disabled ? -1 : 0,
@@ -17747,8 +17888,8 @@ function UploadRoot({
17747
17888
  children: trigger
17748
17889
  });
17749
17890
  }
17750
- if (React84.isValidElement(trigger)) {
17751
- return React84.cloneElement(trigger, {
17891
+ if (React85.isValidElement(trigger)) {
17892
+ return React85.cloneElement(trigger, {
17752
17893
  className: joinClasses(uploadTriggerClass, trigger.props.className),
17753
17894
  onClick: (event) => {
17754
17895
  trigger.props.onClick?.(event);
@@ -17756,7 +17897,7 @@ function UploadRoot({
17756
17897
  }
17757
17898
  });
17758
17899
  }
17759
- return /* @__PURE__ */ jsx92("button", {
17900
+ return /* @__PURE__ */ jsx93("button", {
17760
17901
  type: "button",
17761
17902
  className: uploadTriggerClass,
17762
17903
  disabled,
@@ -17764,7 +17905,7 @@ function UploadRoot({
17764
17905
  children: trigger
17765
17906
  });
17766
17907
  }
17767
- return /* @__PURE__ */ jsxs73("div", {
17908
+ return /* @__PURE__ */ jsxs74("div", {
17768
17909
  ...props,
17769
17910
  ref,
17770
17911
  className: getUploadClasses({
@@ -17774,7 +17915,7 @@ function UploadRoot({
17774
17915
  className
17775
17916
  }),
17776
17917
  children: [
17777
- /* @__PURE__ */ jsx92("input", {
17918
+ /* @__PURE__ */ jsx93("input", {
17778
17919
  ref: inputRef,
17779
17920
  className: uploadInputClass,
17780
17921
  type: "file",
@@ -17788,37 +17929,37 @@ function UploadRoot({
17788
17929
  }
17789
17930
  }),
17790
17931
  renderTrigger(),
17791
- mergedFileList.length > 0 ? /* @__PURE__ */ jsx92("div", {
17932
+ mergedFileList.length > 0 ? /* @__PURE__ */ jsx93("div", {
17792
17933
  className: getUploadListClasses({ listType }),
17793
17934
  children: mergedFileList.map((file) => {
17794
17935
  const size = formatSize(file.size);
17795
- return /* @__PURE__ */ jsxs73("div", {
17936
+ return /* @__PURE__ */ jsxs74("div", {
17796
17937
  className: getUploadItemClasses({ status: file.status }),
17797
17938
  children: [
17798
- /* @__PURE__ */ jsxs73("div", {
17939
+ /* @__PURE__ */ jsxs74("div", {
17799
17940
  className: uploadItemInfoClass,
17800
17941
  children: [
17801
- /* @__PURE__ */ jsx92("button", {
17942
+ /* @__PURE__ */ jsx93("button", {
17802
17943
  type: "button",
17803
17944
  className: uploadPreviewClass,
17804
17945
  disabled,
17805
17946
  onClick: () => onPreview?.(file),
17806
- children: /* @__PURE__ */ jsx92("span", {
17947
+ children: /* @__PURE__ */ jsx93("span", {
17807
17948
  className: uploadItemNameClass,
17808
17949
  children: file.name
17809
17950
  })
17810
17951
  }),
17811
- size ? /* @__PURE__ */ jsx92("span", {
17952
+ size ? /* @__PURE__ */ jsx93("span", {
17812
17953
  className: uploadItemSizeClass,
17813
17954
  children: size
17814
17955
  }) : null,
17815
- typeof file.percent === "number" ? /* @__PURE__ */ jsx92("div", {
17956
+ typeof file.percent === "number" ? /* @__PURE__ */ jsx93("div", {
17816
17957
  className: uploadProgressClass,
17817
17958
  role: "progressbar",
17818
17959
  "aria-valuemin": 0,
17819
17960
  "aria-valuemax": 100,
17820
17961
  "aria-valuenow": file.percent,
17821
- children: /* @__PURE__ */ jsx92("div", {
17962
+ children: /* @__PURE__ */ jsx93("div", {
17822
17963
  className: uploadProgressBarClass,
17823
17964
  style: {
17824
17965
  width: `${Math.max(0, Math.min(100, file.percent))}%`
@@ -17827,9 +17968,9 @@ function UploadRoot({
17827
17968
  }) : null
17828
17969
  ]
17829
17970
  }),
17830
- /* @__PURE__ */ jsx92("div", {
17971
+ /* @__PURE__ */ jsx93("div", {
17831
17972
  className: uploadItemActionsClass,
17832
- children: /* @__PURE__ */ jsx92("button", {
17973
+ children: /* @__PURE__ */ jsx93("button", {
17833
17974
  type: "button",
17834
17975
  className: uploadRemoveClass,
17835
17976
  disabled,
@@ -17845,9 +17986,9 @@ function UploadRoot({
17845
17986
  ]
17846
17987
  });
17847
17988
  }
17848
- var UploadFrame = forwardRef78(UploadRoot);
17989
+ var UploadFrame = forwardRef79(UploadRoot);
17849
17990
  UploadFrame.displayName = "Upload";
17850
- var Dragger = forwardRef78((props, ref) => /* @__PURE__ */ jsx92(UploadFrame, {
17991
+ var Dragger = forwardRef79((props, ref) => /* @__PURE__ */ jsx93(UploadFrame, {
17851
17992
  ...props,
17852
17993
  ref,
17853
17994
  dragger: true
@@ -17857,7 +17998,7 @@ var Upload = UploadFrame;
17857
17998
  Upload.Dragger = Dragger;
17858
17999
  Upload.LIST_IGNORE = LIST_IGNORE;
17859
18000
  // src/components/watermark/Watermark.tsx
17860
- import { forwardRef as forwardRef79, useMemo as useMemo36 } from "react";
18001
+ import { forwardRef as forwardRef80, useMemo as useMemo36 } from "react";
17861
18002
 
17862
18003
  // src/classes/watermark.ts
17863
18004
  var watermarkBaseClass = "watermark";
@@ -17867,7 +18008,7 @@ function getWatermarkClasses({ className }) {
17867
18008
  }
17868
18009
 
17869
18010
  // src/components/watermark/Watermark.tsx
17870
- import { jsx as jsx93, jsxs as jsxs74 } from "react/jsx-runtime";
18011
+ import { jsx as jsx94, jsxs as jsxs75 } from "react/jsx-runtime";
17871
18012
  function encodeSvg(svg) {
17872
18013
  return `url("data:image/svg+xml,${encodeURIComponent(svg)}")`;
17873
18014
  }
@@ -17889,7 +18030,7 @@ function createWatermarkSvg({
17889
18030
  }
17890
18031
  return `<svg xmlns="http://www.w3.org/2000/svg" width="${viewWidth}" height="${viewHeight}" viewBox="0 0 ${viewWidth} ${viewHeight}"><text x="50%" y="50%" dominant-baseline="middle" text-anchor="middle" fill="${color}" font-size="${fontSize}" font-family="${font?.fontFamily ?? "sans-serif"}" font-weight="${font?.fontWeight ?? 400}" font-style="${font?.fontStyle ?? "normal"}" transform="rotate(${rotate ?? -22} ${viewWidth / 2} ${viewHeight / 2})">${text}</text></svg>`;
17891
18032
  }
17892
- var Watermark = forwardRef79(({
18033
+ var Watermark = forwardRef80(({
17893
18034
  children,
17894
18035
  className,
17895
18036
  content,
@@ -17906,17 +18047,17 @@ var Watermark = forwardRef79(({
17906
18047
  ...props
17907
18048
  }, ref) => {
17908
18049
  const backgroundImage = useMemo36(() => encodeSvg(createWatermarkSvg({ content, image, width, height, rotate, font })), [content, font, height, image, rotate, width]);
17909
- return /* @__PURE__ */ jsxs74("div", {
18050
+ return /* @__PURE__ */ jsxs75("div", {
17910
18051
  ...props,
17911
18052
  ref,
17912
18053
  className: getWatermarkClasses({ className }),
17913
18054
  style: { position: "relative", ...style },
17914
18055
  children: [
17915
- /* @__PURE__ */ jsx93("div", {
18056
+ /* @__PURE__ */ jsx94("div", {
17916
18057
  className: watermarkContentClass,
17917
18058
  children
17918
18059
  }),
17919
- /* @__PURE__ */ jsx93("div", {
18060
+ /* @__PURE__ */ jsx94("div", {
17920
18061
  "aria-hidden": "true",
17921
18062
  "data-watermark-source": image ?? (typeof content === "string" || typeof content === "number" ? String(content) : undefined),
17922
18063
  "data-inherit": inherit || undefined,
@@ -18027,6 +18168,7 @@ export {
18027
18168
  MessageHolder,
18028
18169
  Menu,
18029
18170
  Mentions,
18171
+ Markdown,
18030
18172
  List,
18031
18173
  Link,
18032
18174
  Layout,
@@ -4,7 +4,7 @@
4
4
  import { useCallback, useState } from "react";
5
5
 
6
6
  // src/generated/version.ts
7
- var version = "0.3.0";
7
+ var version = "0.3.1";
8
8
 
9
9
  // src/infrastructure.ts
10
10
  var theme = {
package/dist/styles.css CHANGED
@@ -23760,6 +23760,49 @@ html {
23760
23760
  color: var(--color-on-surface-variant);
23761
23761
  font-weight: 700;
23762
23762
  }
23763
+
23764
+ .dm-front-matter {
23765
+ margin-block-end: 1rem;
23766
+ }
23767
+
23768
+ .dm-color-code {
23769
+ display: inline-flex;
23770
+ align-items: center;
23771
+ gap: 0.35em;
23772
+ white-space: nowrap;
23773
+ vertical-align: middle;
23774
+ }
23775
+
23776
+ .dm-color-chip {
23777
+ position: relative;
23778
+ display: inline-block;
23779
+ flex: 0 0 auto;
23780
+ width: 1em;
23781
+ height: 1em;
23782
+ overflow: hidden;
23783
+ border: 1px solid
23784
+ color-mix(
23785
+ in oklch,
23786
+ var(--color-base-content, #111827) 56%,
23787
+ var(--color-surface, #ffffff)
23788
+ );
23789
+ border-radius: 3px;
23790
+ background-color: var(--color-surface, #ffffff);
23791
+ background-image: conic-gradient(
23792
+ color-mix(in oklch, var(--color-base-content, #111827) 22%, transparent)
23793
+ 25%,
23794
+ transparent 0 50%,
23795
+ color-mix(in oklch, var(--color-base-content, #111827) 22%, transparent) 0
23796
+ 75%,
23797
+ transparent 0
23798
+ );
23799
+ background-size: 6px 6px;
23800
+ }
23801
+
23802
+ .dm-color-chip-swatch {
23803
+ position: absolute;
23804
+ inset: 0;
23805
+ }
23763
23806
  }
23764
23807
 
23765
23808
  @layer components {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@duskmoon-dev/components",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -383,6 +383,10 @@
383
383
  "import": "./dist/components/typography/index.js",
384
384
  "types": "./dist/components/typography/index.d.ts"
385
385
  },
386
+ "./markdown": {
387
+ "import": "./dist/components/markdown/index.js",
388
+ "types": "./dist/components/markdown/index.d.ts"
389
+ },
386
390
  "./upload": {
387
391
  "import": "./dist/components/upload/index.js",
388
392
  "types": "./dist/components/upload/index.d.ts"
@@ -445,7 +449,10 @@
445
449
  "react-dom": ">=19.0.0"
446
450
  },
447
451
  "dependencies": {
448
- "clsx": "^2.1.1"
452
+ "clsx": "^2.1.1",
453
+ "react-markdown": "^10.1.0",
454
+ "remark-breaks": "^4.0.0",
455
+ "remark-gfm": "^4.0.1"
449
456
  },
450
457
  "devDependencies": {
451
458
  "@duskmoon-dev/core": "1.17.0",