@astryxdesign/core 0.6.1 → 0.6.2
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/CHANGELOG.md +33 -0
- package/dist/DateRangeInput/DateRangeInput.d.ts +3 -0
- package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
- package/dist/DateRangeInput/DateRangeInput.js +16 -9
- package/dist/Dialog/DialogHeader.d.ts +1 -1
- package/dist/Dialog/DialogHeader.d.ts.map +1 -1
- package/dist/Dialog/DialogHeader.js +10 -7
- package/dist/FileInput/FileInput.d.ts.map +1 -1
- package/dist/FileInput/FileInput.js +9 -3
- package/dist/Markdown/Markdown.d.ts +10 -2
- package/dist/Markdown/Markdown.d.ts.map +1 -1
- package/dist/Markdown/Markdown.js +58 -14
- package/dist/Markdown/index.d.ts +1 -1
- package/dist/Markdown/index.d.ts.map +1 -1
- package/dist/Markdown/parser.d.ts +126 -12
- package/dist/Markdown/parser.d.ts.map +1 -1
- package/dist/Markdown/parser.js +369 -34
- package/dist/Markdown/utils.d.ts +1 -1
- package/dist/Markdown/utils.d.ts.map +1 -1
- package/dist/Slider/Slider.d.ts.map +1 -1
- package/dist/Slider/Slider.js +5 -2
- package/dist/Spinner/Spinner.d.ts +1 -1
- package/dist/Spinner/Spinner.d.ts.map +1 -1
- package/dist/Spinner/Spinner.js +23 -15
- package/dist/astryx.css +2 -1
- package/locales/en.json +16 -0
- package/locales/pseudo.json +12 -0
- package/package.json +6 -4
- package/scripts/agent-doc-state.mjs +1 -1
- package/src/DateRangeInput/DateRangeInput.doc.mjs +35 -7
- package/src/DateRangeInput/DateRangeInput.spec.md +203 -0
- package/src/DateRangeInput/DateRangeInput.test.tsx +100 -4
- package/src/DateRangeInput/DateRangeInput.tsx +29 -20
- package/src/Dialog/Dialog.doc.mjs +3 -0
- package/src/Dialog/Dialog.spec.md +1 -1
- package/src/Dialog/DialogHeader.doc.mjs +38 -0
- package/src/Dialog/DialogHeader.test.tsx +49 -0
- package/src/Dialog/DialogHeader.tsx +23 -4
- package/src/Dialog/modules/DialogHeader.spec.md +152 -0
- package/src/FileInput/FileInput.doc.mjs +2 -0
- package/src/FileInput/FileInput.spec.md +199 -0
- package/src/FileInput/FileInput.test.tsx +14 -0
- package/src/FileInput/FileInput.tsx +13 -3
- package/src/Markdown/Markdown.doc.mjs +167 -42
- package/src/Markdown/Markdown.public.test.ts +157 -0
- package/src/Markdown/Markdown.spec.md +149 -70
- package/src/Markdown/Markdown.test.tsx +107 -3
- package/src/Markdown/Markdown.tsx +116 -35
- package/src/Markdown/incremental.test.ts +175 -7
- package/src/Markdown/index.ts +6 -0
- package/src/Markdown/parser.perf.test.ts +3 -1
- package/src/Markdown/parser.test.ts +122 -0
- package/src/Markdown/parser.ts +609 -81
- package/src/Markdown/utils.ts +6 -0
- package/src/Slider/Slider.doc.mjs +16 -0
- package/src/Slider/Slider.spec.md +61 -47
- package/src/Slider/Slider.test.tsx +18 -0
- package/src/Slider/Slider.tsx +12 -6
- package/src/Spinner/Spinner.doc.mjs +6 -3
- package/src/Spinner/Spinner.test.tsx +37 -0
- package/src/Spinner/Spinner.tsx +31 -14
- package/src/theme/derivedVarRegistry.test.ts +6 -4
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
---
|
|
2
|
+
schema_version: 3
|
|
3
|
+
template_version: 4
|
|
4
|
+
kind: component
|
|
5
|
+
id: component:FileInput
|
|
6
|
+
authority: current
|
|
7
|
+
archive_reason: null
|
|
8
|
+
superseded_by: null
|
|
9
|
+
approved_by: cixzhang
|
|
10
|
+
approved_at: 2026-09-14
|
|
11
|
+
owners: [cixzhang, imdreamrunner]
|
|
12
|
+
review_triggers: [theming]
|
|
13
|
+
verified_by:
|
|
14
|
+
[
|
|
15
|
+
packages/core/src/FileInput/FileInput.test.tsx,
|
|
16
|
+
packages/core/src/theme/themingTargets.test.ts,
|
|
17
|
+
scripts/check-knowledge.mjs,
|
|
18
|
+
]
|
|
19
|
+
modules: []
|
|
20
|
+
families: [family:input-fields]
|
|
21
|
+
design_specs: []
|
|
22
|
+
architecture: [architecture:component-theming-surface]
|
|
23
|
+
contributing: []
|
|
24
|
+
system_specs: []
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
# FileInput component contract
|
|
28
|
+
|
|
29
|
+
## Intent
|
|
30
|
+
|
|
31
|
+
FileInput presents a labelled file-selection field in compact input or dropzone
|
|
32
|
+
form. This contract records its current consumer anatomy and approves separate theme
|
|
33
|
+
ownership for the upload affordance that FileInput paints through Icon.
|
|
34
|
+
|
|
35
|
+
## Compatibility and migration
|
|
36
|
+
|
|
37
|
+
- Released default preserved: `yes`
|
|
38
|
+
- Compatibility class: additive public theming target; no existing target,
|
|
39
|
+
runtime default, DOM, prop, interaction, or accessibility behavior changes
|
|
40
|
+
- Controlled/uncontrolled behavior: unchanged; FileInput remains controlled
|
|
41
|
+
- Migration decision: `component:FileInput/DEC-1`
|
|
42
|
+
|
|
43
|
+
Consumer migration instructions belong in consumer docs and release notes.
|
|
44
|
+
|
|
45
|
+
## Ownership boundary
|
|
46
|
+
|
|
47
|
+
**Owns**
|
|
48
|
+
|
|
49
|
+
- The visible file-selection surface and its input/dropzone mode.
|
|
50
|
+
- Whether, where, and at what default size the upload affordance appears.
|
|
51
|
+
- Reflecting FileInput's mode on its locally owned theme targets.
|
|
52
|
+
|
|
53
|
+
**Does not own / non-goals**
|
|
54
|
+
|
|
55
|
+
- The upload artwork or Icon's base color, size, and accessibility semantics —
|
|
56
|
+
owned by `component:Icon`.
|
|
57
|
+
- Label, description, clear-control, and validation-message presentation — owned
|
|
58
|
+
by `component:Field` and `component:FieldStatus`.
|
|
59
|
+
- Loading-indicator presentation — owned by `component:Spinner`.
|
|
60
|
+
- A new prop, variant, icon slot, or custom property.
|
|
61
|
+
|
|
62
|
+
## Public concepts
|
|
63
|
+
|
|
64
|
+
No consumer prop changes. The `file-input-icon` target gives themes a
|
|
65
|
+
same-element seam for the Upload icon and reflects the existing `mode` axis. The
|
|
66
|
+
existing `file-input` target remains on the visible selection surface and keeps
|
|
67
|
+
its `mode` and `status` axes.
|
|
68
|
+
|
|
69
|
+
## Behavioral and layout contract
|
|
70
|
+
|
|
71
|
+
| ID | Candidate invariant | Basis | Review state |
|
|
72
|
+
| --- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------- | ------------------------------- |
|
|
73
|
+
| FR1 | The visible selection surface carries `file-input` and reflects the existing `mode` and resolved status. | Current source, public docs, and focused tests | Verified current behavior |
|
|
74
|
+
| FR2 | When not loading, input mode renders an upload affordance at the small Icon size. Dropzone mode renders it at the medium Icon size only while no file is selected. | Current source and focused tests | Verified current behavior |
|
|
75
|
+
| FR3 | Icon owns the rendered glyph's base size, color, and accessibility semantics; FileInput owns the affordance's mode-dependent placement and default size. | Current composition and component boundaries | Verified current composition |
|
|
76
|
+
| FR4 | The rendered upload affordance MUST carry `file-input-icon` with the existing `mode` reflected, so a theme can restyle the glyph box without structural selectors or changing every Icon that uses the same artwork. | Owner-approved target contract | Approved additive contract |
|
|
77
|
+
| FR5 | Adding the target MUST NOT change the default artwork, computed layout, interaction, file-selection behavior, accessible name, or decorative Icon semantics. | Compatibility policy and focused regression tests | Required compatibility behavior |
|
|
78
|
+
|
|
79
|
+
### Allowed variation
|
|
80
|
+
|
|
81
|
+
- **AV1 — Theme paint.** A theme may change standard visual
|
|
82
|
+
properties such as the upload glyph's size or color through
|
|
83
|
+
`file-input-icon`; FileInput still owns whether and where the affordance renders.
|
|
84
|
+
- **AV2 — Artwork.** Icon registry and future icon-slot decisions may change the
|
|
85
|
+
artwork without changing this CSS target's ownership of the painted glyph box.
|
|
86
|
+
|
|
87
|
+
### Representative states
|
|
88
|
+
|
|
89
|
+
| State | Required invariant | Allowed variation |
|
|
90
|
+
| ------------------------ | ------------------------------------------------------------------------ | --------------------------------------- |
|
|
91
|
+
| Input, empty or selected | Small upload affordance renders on the input surface when not loading. | Files, placeholder, status, theme paint |
|
|
92
|
+
| Dropzone, empty | Medium upload affordance renders above the placeholder when not loading. | Drag state, placeholder, theme paint |
|
|
93
|
+
| Dropzone, selected | File names replace the upload affordance. | File names and status |
|
|
94
|
+
| Loading | Spinner replaces the upload affordance. | Mode and loading presentation |
|
|
95
|
+
|
|
96
|
+
### Transformation and precedence order
|
|
97
|
+
|
|
98
|
+
- **ORD1 — Content selection.** Resolve loading and selected-file state, choose
|
|
99
|
+
input or dropzone content, then render the mode-sized upload affordance only in
|
|
100
|
+
the states recorded by FR2.
|
|
101
|
+
- **ORD2 — Theme composition.** Icon applies its base size and color, then the
|
|
102
|
+
same-element FileInput target participates in the existing theme layer and
|
|
103
|
+
standard Icon styling merge order.
|
|
104
|
+
|
|
105
|
+
### Performance and resources
|
|
106
|
+
|
|
107
|
+
- **PR1 — No new work.** The additive target performs no measurement, listener,
|
|
108
|
+
observer, state update, or additional render pass.
|
|
109
|
+
|
|
110
|
+
## Accessibility contract
|
|
111
|
+
|
|
112
|
+
The upload affordance remains decorative. The existing focusable file-selection
|
|
113
|
+
trigger, label, description, required/invalid state, disabled explanation, and
|
|
114
|
+
selection announcements remain unchanged.
|
|
115
|
+
|
|
116
|
+
## Design relationships
|
|
117
|
+
|
|
118
|
+
| Anatomy or state | Design requirement | Representation authority | Hierarchy role | Component contract |
|
|
119
|
+
| ---------------- | ----------------------------------------------------------------------------------- | ---------------------------------------- | -------------- | ------------------ |
|
|
120
|
+
| Drop zone | Presents the visible file-selection surface in input or dropzone form. | Current source and public docs | Prominent | FR1 |
|
|
121
|
+
| Upload icon | Hints at the upload action and changes default size with the selected mode. | Current source and owner-approved target | Supporting | FR2, FR3, FR4 |
|
|
122
|
+
| Shared feedback | Uses Field, FieldStatus, and Spinner for labels, validation, and loading treatment. | Current shared composition | Supporting | FR5 |
|
|
123
|
+
|
|
124
|
+
### Theming anatomy
|
|
125
|
+
|
|
126
|
+
<!-- anatomy-theming:v1 -->
|
|
127
|
+
|
|
128
|
+
```json
|
|
129
|
+
{
|
|
130
|
+
"Label": {
|
|
131
|
+
"delegatesTo": {"owner": "component:Field", "target": "field-label"}
|
|
132
|
+
},
|
|
133
|
+
"Description": {
|
|
134
|
+
"none": {
|
|
135
|
+
"reason": "unsettled: No current public target reaches the stable Description; future exposure still needs an owner decision"
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
"Drop zone": {"target": "file-input"},
|
|
139
|
+
"Upload icon": {"target": "file-input-icon"},
|
|
140
|
+
"Placeholder": {"inherits": "file-input"},
|
|
141
|
+
"File name display": {"inherits": "file-input"},
|
|
142
|
+
"Clear button": {
|
|
143
|
+
"delegatesTo": {
|
|
144
|
+
"owner": "component:Field",
|
|
145
|
+
"target": "input-clear-button"
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
"Spinner": {
|
|
149
|
+
"delegatesTo": {"owner": "component:Spinner", "target": "spinner"}
|
|
150
|
+
},
|
|
151
|
+
"Status message": {
|
|
152
|
+
"delegatesTo": {
|
|
153
|
+
"owner": "component:FieldStatus",
|
|
154
|
+
"target": "field-status"
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
The `file-input-icon` disposition records the approved target state. Icon still
|
|
161
|
+
owns the general `icon` target and base glyph semantics; FileInput's narrower
|
|
162
|
+
target owns only this stable upload position and its existing mode distinction.
|
|
163
|
+
|
|
164
|
+
## Family and system relationships
|
|
165
|
+
|
|
166
|
+
- `family:input-fields` owns shared labelled-field and validation behavior.
|
|
167
|
+
- `architecture:component-theming-surface` owns target qualification, anatomy
|
|
168
|
+
mapping, and the requirement that public targets sit on stable painted parts.
|
|
169
|
+
- Field, FieldStatus, Icon, and Spinner retain their existing public target
|
|
170
|
+
contracts when composed by FileInput.
|
|
171
|
+
|
|
172
|
+
## Verification map
|
|
173
|
+
|
|
174
|
+
| Contract | Verification | Representative states | Mutation or failure expectation | Audit section |
|
|
175
|
+
| ------------------- | ----------------------------------------------------------------- | -------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | ------------------------- |
|
|
176
|
+
| FR1, FR2 | `FileInput.test.tsx` rendering and target suites | Input/dropzone; empty/selected/loading | Moving the root target or changing when/at what size the affordance renders breaks focused assertions. | `audit:FileInput/theming` |
|
|
177
|
+
| FR3, FR4, FR5 | `FileInput.test.tsx`, `themingTargets.test.ts`, probe-theme check | Both modes and same-element Icon target | Missing the target, reflecting the wrong mode, or moving it off the glyph fails source/docs/probe coverage. | `audit:FileInput/theming` |
|
|
178
|
+
| Theming anatomy map | `scripts/check-knowledge.mjs` | Nine anatomy entries and two locally owned targets | Missing, extra, prefixed, stale, or unclaimed current mappings fail validation. | `audit:FileInput/anatomy` |
|
|
179
|
+
|
|
180
|
+
## Decision log
|
|
181
|
+
|
|
182
|
+
### DEC-1 — Upload icon is stable FileInput theme anatomy
|
|
183
|
+
|
|
184
|
+
**Reference:** `component:FileInput/DEC-1`
|
|
185
|
+
**Decider:** cixzhang, 2026-09-14
|
|
186
|
+
|
|
187
|
+
The upload icon is a stable, consumer-recognizable FileInput affordance whose
|
|
188
|
+
mode-dependent placement and default size belong to FileInput. It receives the
|
|
189
|
+
`file-input-icon` target on the same Icon element that paints the glyph, while
|
|
190
|
+
Icon retains its general target and base glyph semantics.
|
|
191
|
+
|
|
192
|
+
## Open questions
|
|
193
|
+
|
|
194
|
+
None.
|
|
195
|
+
|
|
196
|
+
## Content boundary
|
|
197
|
+
|
|
198
|
+
This file does not duplicate consumer prop tables, examples, implementation
|
|
199
|
+
steps, or shared-component contracts. It links to their owners.
|
|
@@ -119,6 +119,20 @@ describe('FileInput', () => {
|
|
|
119
119
|
expect(screen.getByText('Drop here')).toBeInTheDocument();
|
|
120
120
|
});
|
|
121
121
|
|
|
122
|
+
it.each([
|
|
123
|
+
{mode: 'input' as const, size: 'sm'},
|
|
124
|
+
{mode: 'dropzone' as const, size: 'md'},
|
|
125
|
+
])('exposes the upload icon as a $mode theme target', ({mode, size}) => {
|
|
126
|
+
render(
|
|
127
|
+
<FileInput label="Upload" mode={mode} value={null} onChange={() => {}} />,
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
const icon = document.querySelector('.astryx-file-input-icon');
|
|
131
|
+
expect(icon).toHaveClass('astryx-icon');
|
|
132
|
+
expect(icon).toHaveAttribute('data-mode', mode);
|
|
133
|
+
expect(icon).toHaveAttribute('data-size', size);
|
|
134
|
+
});
|
|
135
|
+
|
|
122
136
|
it('displays selected file name', () => {
|
|
123
137
|
const file = createFile('report.pdf', 1024, 'application/pdf');
|
|
124
138
|
render(<FileInput label="Document" value={file} onChange={() => {}} />);
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
/**
|
|
6
6
|
* @file FileInput.tsx
|
|
7
7
|
* @input Uses React, useId, Field, Icon, Spinner, VisuallyHidden
|
|
8
|
-
* @output Exports FileInput component,
|
|
8
|
+
* @output Exports FileInput component, public types, and its root/icon theme targets
|
|
9
9
|
* @position Core implementation; consumed by index.ts, tested by FileInput.test.tsx
|
|
10
10
|
*
|
|
11
11
|
* SYNC: When modified, update these files to stay in sync:
|
|
@@ -707,7 +707,12 @@ export function FileInput({
|
|
|
707
707
|
}
|
|
708
708
|
return (
|
|
709
709
|
<>
|
|
710
|
-
<Icon
|
|
710
|
+
<Icon
|
|
711
|
+
icon="arrowUp"
|
|
712
|
+
size="md"
|
|
713
|
+
color="secondary"
|
|
714
|
+
{...themeProps('file-input-icon', {mode})}
|
|
715
|
+
/>
|
|
711
716
|
<span {...stylex.props(styles.placeholderText)}>
|
|
712
717
|
{isDragOver ? t('@astryx.fileInput.dropHint') : displayPlaceholder}
|
|
713
718
|
</span>
|
|
@@ -728,7 +733,12 @@ export function FileInput({
|
|
|
728
733
|
}
|
|
729
734
|
return (
|
|
730
735
|
<>
|
|
731
|
-
<Icon
|
|
736
|
+
<Icon
|
|
737
|
+
icon="arrowUp"
|
|
738
|
+
size="sm"
|
|
739
|
+
color="secondary"
|
|
740
|
+
{...themeProps('file-input-icon', {mode})}
|
|
741
|
+
/>
|
|
732
742
|
<span
|
|
733
743
|
{...stylex.props(
|
|
734
744
|
hasFiles ? styles.fileNameText : styles.placeholderText,
|
|
@@ -5,8 +5,7 @@ const anatomy = [
|
|
|
5
5
|
{
|
|
6
6
|
name: 'Document',
|
|
7
7
|
required: true,
|
|
8
|
-
description:
|
|
9
|
-
'Root container for block or inline Markdown content.',
|
|
8
|
+
description: 'Root container for block or inline Markdown content.',
|
|
10
9
|
},
|
|
11
10
|
{
|
|
12
11
|
name: 'Heading',
|
|
@@ -41,7 +40,8 @@ const anatomy = [
|
|
|
41
40
|
{
|
|
42
41
|
name: 'Table',
|
|
43
42
|
required: false,
|
|
44
|
-
description:
|
|
43
|
+
description:
|
|
44
|
+
'Scrollable table block rendered from Markdown rows and columns.',
|
|
45
45
|
},
|
|
46
46
|
{
|
|
47
47
|
name: 'Divider',
|
|
@@ -138,14 +138,14 @@ export const docs = {
|
|
|
138
138
|
name: 'contentAlign',
|
|
139
139
|
type: "'start' | 'center'",
|
|
140
140
|
description:
|
|
141
|
-
|
|
141
|
+
'Alignment of prose content within the container when contentWidth is narrower than the available space.',
|
|
142
142
|
default: "'start'",
|
|
143
143
|
},
|
|
144
144
|
{
|
|
145
145
|
name: 'inlinePlugins',
|
|
146
146
|
type: 'MarkdownInlinePlugin[]',
|
|
147
147
|
description:
|
|
148
|
-
'Transforms regex matches in parsed text nodes into custom inline React elements. Use for
|
|
148
|
+
'Transforms regex matches in parsed text nodes into custom inline React elements. Use for prefixed identifiers, mentions, and other shorthand patterns. Inline code, fenced code blocks, and math are unaffected.',
|
|
149
149
|
},
|
|
150
150
|
{
|
|
151
151
|
name: 'autolink',
|
|
@@ -156,7 +156,8 @@ export const docs = {
|
|
|
156
156
|
{
|
|
157
157
|
name: 'components',
|
|
158
158
|
type: 'MarkdownComponents',
|
|
159
|
-
description:
|
|
159
|
+
description:
|
|
160
|
+
'Custom React component overrides for rendered Markdown elements (code, inlineCode, math, link, heading, paragraph, image, blockquote, hr, citation). Providing math enables `$…$` inline and `$$…$$` display parsing and receives `{value, display}`; omit it when dollar text should stay literal.',
|
|
160
161
|
},
|
|
161
162
|
{
|
|
162
163
|
name: 'xstyle',
|
|
@@ -184,7 +185,8 @@ export const docs = {
|
|
|
184
185
|
],
|
|
185
186
|
playground: {
|
|
186
187
|
defaults: {
|
|
187
|
-
children:
|
|
188
|
+
children:
|
|
189
|
+
"## Getting Started\n\nInstall the package:\n\n```bash\nnpm install @astryxdesign/core\n```\n\nThen import and use any component:\n\n```tsx\nimport {Button} from '@astryxdesign/core/Button';\n```\n\n**Bold**, *italic*, and `inline code` all work.",
|
|
188
190
|
},
|
|
189
191
|
},
|
|
190
192
|
theming: {
|
|
@@ -229,11 +231,41 @@ export const docs = {
|
|
|
229
231
|
description:
|
|
230
232
|
'Renders a markdown string as Astryx-styled components. Use Markdown for user-generated content, AI responses, and documentation; it handles headings, lists, tables, code blocks, and citations with consistent styling.',
|
|
231
233
|
bestPractices: [
|
|
232
|
-
{
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
234
|
+
{
|
|
235
|
+
guidance: true,
|
|
236
|
+
description:
|
|
237
|
+
'Set headingLevelStart to match the page hierarchy, e.g. start at 3 if the markdown sits inside an h2 section.',
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
guidance: true,
|
|
241
|
+
description:
|
|
242
|
+
'Use contentWidth to keep prose at a readable line length in wide layouts.',
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
guidance: true,
|
|
246
|
+
description:
|
|
247
|
+
'Use inlinePlugins for prefixed identifiers, mentions, and other prose-only shorthand instead of preprocessing the markdown string.',
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
guidance: true,
|
|
251
|
+
description:
|
|
252
|
+
'Provide components.math only for documents that use dollar-delimited math. The renderer owns typesetting and accessible output; Astryx passes the expression as text and never executes raw HTML.',
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
guidance: true,
|
|
256
|
+
description:
|
|
257
|
+
'For direct parsing, use MathParseOptions and handle InlineNodeWithMath or BlockNodeWithMath. Incremental math parsing also uses createIncrementalState<true>() and IncrementalParseState<true>; default calls and ParseOptions annotations keep the legacy unions.',
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
guidance: true,
|
|
261
|
+
description:
|
|
262
|
+
'Pair with Outline and useOutlineFromMarkdown for section navigation: headings render generated id attributes that match the outline item ids, so hash links scroll to their target.',
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
guidance: false,
|
|
266
|
+
description:
|
|
267
|
+
'Use Markdown for hand-authored layouts; use Text and Heading directly when you control the content.',
|
|
268
|
+
},
|
|
237
269
|
],
|
|
238
270
|
},
|
|
239
271
|
examples: [
|
|
@@ -259,23 +291,38 @@ import {Text} from '@astryxdesign/core/Text';
|
|
|
259
291
|
`,
|
|
260
292
|
},
|
|
261
293
|
{
|
|
262
|
-
label: '
|
|
294
|
+
label: 'Entity links',
|
|
263
295
|
code: `
|
|
264
296
|
import {Link} from '@astryxdesign/core/Link';
|
|
265
297
|
|
|
266
|
-
const
|
|
298
|
+
const entityPlugins = [
|
|
267
299
|
{
|
|
268
300
|
pattern: /\\b([A-Z][A-Z0-9]+-\\d+)\\b/g,
|
|
269
301
|
render: (match, key) => (
|
|
270
|
-
<Link key={key} href={\`/
|
|
302
|
+
<Link key={key} href={\`/entities/\${match[1]}\`}>
|
|
271
303
|
{match[0]}
|
|
272
304
|
</Link>
|
|
273
305
|
),
|
|
274
306
|
},
|
|
275
307
|
];
|
|
276
308
|
|
|
277
|
-
<Markdown inlinePlugins={
|
|
278
|
-
{'
|
|
309
|
+
<Markdown inlinePlugins={entityPlugins}>
|
|
310
|
+
{'See DOC-2048. Inline code stays plain: \`DOC-9999\`.'}
|
|
311
|
+
</Markdown>;
|
|
312
|
+
`,
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
label: 'Math renderer',
|
|
316
|
+
code: `
|
|
317
|
+
import {BlockMath, InlineMath} from 'react-katex';
|
|
318
|
+
|
|
319
|
+
function MathExpression({value, display}) {
|
|
320
|
+
const Component = display === 'block' ? BlockMath : InlineMath;
|
|
321
|
+
return <Component math={value} />;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
<Markdown components={{math: MathExpression}}>
|
|
325
|
+
{'Inline $x_1 + y$ and display math:\\n\\n$$\\n\\\\sum_i x_i\\n$$'}
|
|
279
326
|
</Markdown>;
|
|
280
327
|
`,
|
|
281
328
|
},
|
|
@@ -315,8 +362,7 @@ export const docsZh = {
|
|
|
315
362
|
{
|
|
316
363
|
name: 'isStreaming',
|
|
317
364
|
type: 'boolean',
|
|
318
|
-
description:
|
|
319
|
-
'启用流式模式,使用增量解析和淡入动画处理分块文本。',
|
|
365
|
+
description: '启用流式模式,使用增量解析和淡入动画处理分块文本。',
|
|
320
366
|
default: 'false',
|
|
321
367
|
},
|
|
322
368
|
{
|
|
@@ -348,14 +394,14 @@ export const docsZh = {
|
|
|
348
394
|
name: 'contentAlign',
|
|
349
395
|
type: "'start' | 'center'",
|
|
350
396
|
description:
|
|
351
|
-
|
|
397
|
+
'当 contentWidth 小于可用空间时,正文内容在容器内的对齐方式。',
|
|
352
398
|
default: "'start'",
|
|
353
399
|
},
|
|
354
400
|
{
|
|
355
401
|
name: 'inlinePlugins',
|
|
356
402
|
type: 'MarkdownInlinePlugin[]',
|
|
357
403
|
description:
|
|
358
|
-
'将已解析文本节点中的正则匹配转换为自定义内联 React
|
|
404
|
+
'将已解析文本节点中的正则匹配转换为自定义内联 React 元素。适用于带前缀的标识符、用户提及等简写模式。内联代码、围栏代码块和数学表达式不受影响。',
|
|
359
405
|
},
|
|
360
406
|
{
|
|
361
407
|
name: 'autolink',
|
|
@@ -363,6 +409,12 @@ export const docsZh = {
|
|
|
363
409
|
description:
|
|
364
410
|
"可选的裸 URL 和电子邮箱自动链接。设为 'gfm' 启用 GitHub Flavored Markdown 自动链接规则:裸 https?://、www.、<scheme:url>、<email> 以及 user@host 都会变成链接。末尾句末标点和不平衡的末尾右括号会被排除;代码块、现有链接和图片替代文本内部的匹配会被跳过。默认为关闭。",
|
|
365
411
|
},
|
|
412
|
+
{
|
|
413
|
+
name: 'components',
|
|
414
|
+
type: 'MarkdownComponents',
|
|
415
|
+
description:
|
|
416
|
+
'用于覆盖 Markdown 渲染元素的自定义 React 组件(code、inlineCode、math、link、heading、paragraph、image、blockquote、hr、citation)。提供 math 会启用 `$…$` 行内数学和 `$$…$$` 块级数学解析,并接收 `{value, display}`;不提供时美元符号保持原样。',
|
|
417
|
+
},
|
|
366
418
|
{
|
|
367
419
|
name: 'xstyle',
|
|
368
420
|
type: 'StyleXStyles',
|
|
@@ -372,12 +424,14 @@ export const docsZh = {
|
|
|
372
424
|
{
|
|
373
425
|
name: 'className',
|
|
374
426
|
type: 'string',
|
|
375
|
-
description:
|
|
427
|
+
description:
|
|
428
|
+
'根元素的 CSS 类名。建议使用 xstyle,className 适用于非 StyleX 系统集成。',
|
|
376
429
|
},
|
|
377
430
|
{
|
|
378
431
|
name: 'style',
|
|
379
432
|
type: 'CSSProperties',
|
|
380
|
-
description:
|
|
433
|
+
description:
|
|
434
|
+
'根元素的内联样式。建议使用 xstyle,内联样式会绕过 StyleX 优化。',
|
|
381
435
|
},
|
|
382
436
|
{
|
|
383
437
|
name: 'data-testid',
|
|
@@ -443,11 +497,41 @@ export const docsZh = {
|
|
|
443
497
|
description:
|
|
444
498
|
'Renders a markdown string as Astryx-styled components. Use Markdown for user-generated content, AI responses, and documentation; it handles headings, lists, tables, code blocks, and citations with consistent styling.',
|
|
445
499
|
bestPractices: [
|
|
446
|
-
{
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
500
|
+
{
|
|
501
|
+
guidance: true,
|
|
502
|
+
description:
|
|
503
|
+
'Set headingLevelStart to match the page hierarchy, e.g. start at 3 if the markdown sits inside an h2 section.',
|
|
504
|
+
},
|
|
505
|
+
{
|
|
506
|
+
guidance: true,
|
|
507
|
+
description:
|
|
508
|
+
'Use contentWidth to keep prose at a readable line length in wide layouts.',
|
|
509
|
+
},
|
|
510
|
+
{
|
|
511
|
+
guidance: true,
|
|
512
|
+
description:
|
|
513
|
+
'Use inlinePlugins for prefixed identifiers, mentions, and other prose-only shorthand instead of preprocessing the markdown string.',
|
|
514
|
+
},
|
|
515
|
+
{
|
|
516
|
+
guidance: true,
|
|
517
|
+
description:
|
|
518
|
+
'Provide components.math only for documents that use dollar-delimited math. The renderer owns typesetting and accessible output; Astryx passes the expression as text and never executes raw HTML.',
|
|
519
|
+
},
|
|
520
|
+
{
|
|
521
|
+
guidance: true,
|
|
522
|
+
description:
|
|
523
|
+
'For direct parsing, use MathParseOptions and handle InlineNodeWithMath or BlockNodeWithMath. Incremental math parsing also uses createIncrementalState<true>() and IncrementalParseState<true>; default calls and ParseOptions annotations keep the legacy unions.',
|
|
524
|
+
},
|
|
525
|
+
{
|
|
526
|
+
guidance: true,
|
|
527
|
+
description:
|
|
528
|
+
'Pair with Outline and useOutlineFromMarkdown for section navigation: headings render generated id attributes that match the outline item ids, so hash links scroll to their target.',
|
|
529
|
+
},
|
|
530
|
+
{
|
|
531
|
+
guidance: false,
|
|
532
|
+
description:
|
|
533
|
+
'Use Markdown for hand-authored layouts; use Text and Heading directly when you control the content.',
|
|
534
|
+
},
|
|
451
535
|
],
|
|
452
536
|
},
|
|
453
537
|
};
|
|
@@ -460,25 +544,66 @@ export const docsDense = {
|
|
|
460
544
|
description:
|
|
461
545
|
'Renders a markdown string as Astryx-styled components. Use Markdown for user-generated content, AI responses, and documentation; it handles headings, lists, tables, code blocks, and citations with consistent styling.',
|
|
462
546
|
bestPractices: [
|
|
463
|
-
{
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
547
|
+
{
|
|
548
|
+
guidance: true,
|
|
549
|
+
description:
|
|
550
|
+
'Set headingLevelStart to match the page hierarchy, e.g. start at 3 if the markdown sits inside an h2 section.',
|
|
551
|
+
},
|
|
552
|
+
{
|
|
553
|
+
guidance: true,
|
|
554
|
+
description:
|
|
555
|
+
'Use contentWidth to keep prose at a readable line length in wide layouts.',
|
|
556
|
+
},
|
|
557
|
+
{
|
|
558
|
+
guidance: true,
|
|
559
|
+
description:
|
|
560
|
+
'Use inlinePlugins for prefixed identifiers, mentions, and other prose-only shorthand instead of preprocessing the markdown string.',
|
|
561
|
+
},
|
|
562
|
+
{
|
|
563
|
+
guidance: true,
|
|
564
|
+
description:
|
|
565
|
+
'Provide components.math only for documents that use dollar-delimited math; the renderer owns typesetting and accessible output.',
|
|
566
|
+
},
|
|
567
|
+
{
|
|
568
|
+
guidance: true,
|
|
569
|
+
description:
|
|
570
|
+
'Direct math parser calls use MathParseOptions and the explicit WithMath node unions; incremental calls also use createIncrementalState<true>() and IncrementalParseState<true>. Default calls keep the legacy unions.',
|
|
571
|
+
},
|
|
572
|
+
{
|
|
573
|
+
guidance: true,
|
|
574
|
+
description:
|
|
575
|
+
'Headings render id attributes matching useOutlineFromMarkdown ids; pair with Outline for hash navigation.',
|
|
576
|
+
},
|
|
577
|
+
{
|
|
578
|
+
guidance: false,
|
|
579
|
+
description:
|
|
580
|
+
'Use Markdown for hand-authored layouts; use Text and Heading directly when you control the content.',
|
|
581
|
+
},
|
|
468
582
|
],
|
|
469
583
|
},
|
|
470
584
|
propDescriptions: {
|
|
471
585
|
children: 'markdown string',
|
|
472
586
|
density: "Block spacing. 'default'|'compact'. Default: 'default'.",
|
|
473
|
-
headingLevelStart:
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
587
|
+
headingLevelStart:
|
|
588
|
+
'Maps # to this heading level (1-6). Clamped to h6. Default: 1.',
|
|
589
|
+
isStreaming:
|
|
590
|
+
'Incremental parse + fade-in for streamed chunks. Default: false.',
|
|
591
|
+
onLinkClick:
|
|
592
|
+
'(href, event) => void|false. Return false prevents navigation.',
|
|
593
|
+
sources:
|
|
594
|
+
'Record<string, MarkdownSource>. Citation sources by ID. [id]/【id】 markers render as chips.',
|
|
595
|
+
citationStyle:
|
|
596
|
+
"'label'|'number'. label=chip w/ title+icon, number=compact badge. Default: 'label'.",
|
|
597
|
+
contentWidth:
|
|
598
|
+
'number|string. Max width for prose (headings, paragraphs, lists). Tables/code unconstrained.',
|
|
599
|
+
contentAlign:
|
|
600
|
+
"'start'|'center'. Prose alignment when contentWidth < container. Default: 'start'.",
|
|
601
|
+
inlinePlugins:
|
|
602
|
+
'MarkdownInlinePlugin[]. Regex matches in text nodes -> custom inline React elements. Skips inline/fenced code and math.',
|
|
603
|
+
autolink:
|
|
604
|
+
"'gfm'. Opt-in GFM autolinking: bare URLs (https?://, www.), <scheme:url>, <email>, user@host. Skips code, code blocks, existing links. Default: off.",
|
|
605
|
+
components:
|
|
606
|
+
'MarkdownComponents. Custom renderers; math({value, display}) opts into $…$/$$…$$ parsing. Renderer owns output and accessibility.',
|
|
482
607
|
xstyle: 'stylex.create() for layout (margins, sizing).',
|
|
483
608
|
className: 'CSS class. Prefer xstyle.',
|
|
484
609
|
style: 'Inline styles. Prefer xstyle.',
|