@sakura-ui/sakura-ui 0.5.3 → 0.5.5

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 (40) hide show
  1. package/package.json +4 -4
  2. package/packages/core/package.json +1 -1
  3. package/packages/core/src/components/Link.tsx +17 -0
  4. package/packages/core/tests/Link.test.tsx +41 -0
  5. package/packages/forms/.turbo/turbo-build.log +2 -2
  6. package/packages/helper/.turbo/turbo-build.log +2 -2
  7. package/packages/markdown/.turbo/turbo-build.log +5 -5
  8. package/packages/markdown/README.md +17 -1
  9. package/packages/markdown/dist/index.cjs.js +22 -22
  10. package/packages/markdown/dist/index.es.js +369 -335
  11. package/packages/markdown/dist/types/components/Markdown.d.ts.map +1 -1
  12. package/packages/markdown/dist/types/decorate.d.ts +2 -5
  13. package/packages/markdown/dist/types/decorate.d.ts.map +1 -1
  14. package/packages/markdown/dist/types/marked/attributes.d.ts +4 -7
  15. package/packages/markdown/dist/types/marked/attributes.d.ts.map +1 -1
  16. package/packages/markdown/dist/types/marked/directives/image.d.ts +3 -0
  17. package/packages/markdown/dist/types/marked/directives/image.d.ts.map +1 -0
  18. package/packages/markdown/dist/types/marked/directives/index.d.ts.map +1 -1
  19. package/packages/markdown/dist/types/marked/html.d.ts.map +1 -1
  20. package/packages/markdown/dist/types/marked/registry.d.ts.map +1 -1
  21. package/packages/markdown/dist/types/marked/renderer.d.ts +4 -6
  22. package/packages/markdown/dist/types/marked/renderer.d.ts.map +1 -1
  23. package/packages/markdown/dist/types/marked/tokenizer.d.ts.map +1 -1
  24. package/packages/markdown/dist/types/sanitize.d.ts.map +1 -1
  25. package/packages/markdown/package.json +1 -1
  26. package/packages/markdown/src/components/Markdown.tsx +12 -6
  27. package/packages/markdown/src/decorate.ts +23 -16
  28. package/packages/markdown/src/marked/attributes.ts +4 -7
  29. package/packages/markdown/src/marked/directives/card.ts +2 -2
  30. package/packages/markdown/src/marked/directives/image.ts +29 -0
  31. package/packages/markdown/src/marked/directives/index.ts +2 -0
  32. package/packages/markdown/src/marked/html.ts +17 -4
  33. package/packages/markdown/src/marked/registry.ts +3 -5
  34. package/packages/markdown/src/marked/renderer.ts +7 -9
  35. package/packages/markdown/src/marked/tokenizer.ts +5 -7
  36. package/packages/markdown/src/sanitize.ts +9 -2
  37. package/packages/markdown/tests/__snapshots__/Markdown.test.tsx.snap +61 -61
  38. package/packages/markdown/tests/image.test.ts +64 -0
  39. package/packages/markdown/tests/sanitize.test.ts +17 -0
  40. package/packages/tailwind-theme-plugin/.turbo/turbo-build.log +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"Markdown.d.ts","sourceRoot":"","sources":["../../../src/components/Markdown.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EAAE,KAAK,WAAW,EAA6B,MAAM,WAAW,CAAA;AASvE,UAAU,QAAQ;IAChB,KAAK,EAAE,WAAW,EAAE,CAAA;CACrB;AAED,eAAO,MAAM,eAAe,GAAI,WAAW,QAAQ,sBAelD,CAAA;AAED,yBAAiB,QAAQ,CAAC;IACxB,UAAiB,KAAK;QACpB,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,OAAO,CAAC,EAAE,OAAO,CAAA;QACjB,oDAAoD;QACpD,YAAY,CAAC,EAAE,MAAM,CAAA;QACrB,0DAA0D;QAC1D,WAAW,CAAC,EAAE,MAAM,CAAA;QACpB,QAAQ,EAAE,MAAM,CAAA;KACjB;CACF;AAED,eAAO,MAAM,QAAQ,GAAI,4DAMtB,QAAQ,CAAC,KAAK,sBA4ChB,CAAA"}
1
+ {"version":3,"file":"Markdown.d.ts","sourceRoot":"","sources":["../../../src/components/Markdown.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EAAE,KAAK,WAAW,EAA6B,MAAM,WAAW,CAAA;AASvE,UAAU,QAAQ;IAChB,KAAK,EAAE,WAAW,EAAE,CAAA;CACrB;AAED,eAAO,MAAM,eAAe,GAAI,WAAW,QAAQ,sBAelD,CAAA;AAED,yBAAiB,QAAQ,CAAC;IACxB,UAAiB,KAAK;QACpB,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,OAAO,CAAC,EAAE,OAAO,CAAA;QACjB,oDAAoD;QACpD,YAAY,CAAC,EAAE,MAAM,CAAA;QACrB,0DAA0D;QAC1D,WAAW,CAAC,EAAE,MAAM,CAAA;QACpB,QAAQ,EAAE,MAAM,CAAA;KACjB;CACF;AAED,eAAO,MAAM,QAAQ,GAAI,4DAMtB,QAAQ,CAAC,KAAK,sBAkDhB,CAAA"}
@@ -1,11 +1,8 @@
1
1
  /**
2
2
  * Applies the look of the design system to the finished document.
3
3
  *
4
- * This runs over the sanitised DOM rather than inside the markdown renderer so
5
- * that a table written as raw HTML 56 of the guide's documents do is styled
6
- * the same as one written with pipes. The pipeline this replaces had the same
7
- * property, because rehype-raw turned the raw HTML into real nodes before the
8
- * tag to component mapping ran.
4
+ * Runs over the sanitised DOM rather than inside the markdown renderer, so that
5
+ * a table written as raw HTML is styled the same as one written with pipes.
9
6
  */
10
7
  export type HeadingItem = {
11
8
  id?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"decorate.d.ts","sourceRoot":"","sources":["../../src/decorate.ts"],"names":[],"mappings":"AAIA;;;;;;;;GAQG;AAEH,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,WAAW,EAAE,CAAA;CACzB,CAAA;AAsDD,MAAM,MAAM,eAAe,GAAG;IAC5B,YAAY,EAAE,MAAM,CAAA;IACpB,WAAW,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,EAAE,WAAW,EAAE,CAAA;CACxB,CAAA;AAID,eAAO,MAAM,QAAQ,GACnB,MAAM,gBAAgB,GAAG,OAAO,EAChC,UAAU,QAAQ,EAClB,SAAS,eAAe,KACvB,cA2EF,CAAA"}
1
+ {"version":3,"file":"decorate.d.ts","sourceRoot":"","sources":["../../src/decorate.ts"],"names":[],"mappings":"AAIA;;;;;GAKG;AAEH,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,WAAW,EAAE,CAAA;CACzB,CAAA;AAoED,MAAM,MAAM,eAAe,GAAG;IAC5B,YAAY,EAAE,MAAM,CAAA;IACpB,WAAW,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,EAAE,WAAW,EAAE,CAAA;CACxB,CAAA;AAID,eAAO,MAAM,QAAQ,GACnB,MAAM,gBAAgB,GAAG,OAAO,EAChC,UAAU,QAAQ,EAClB,SAAS,eAAe,KACvB,cAuEF,CAAA"}
@@ -2,14 +2,11 @@ export type Attrs = Record<string, string>;
2
2
  /**
3
3
  * Where `{#name}` is kept, apart from anything written as `key=value`.
4
4
  *
5
- * A sigil says something about the element itself and means the same on every
6
- * directive; an attribute is the directive's own, and each reads the ones it
7
- * knows. Keeping them in one place would let a directive that wanted an
8
- * attribute called `id` take the anchor's place, which is how the video on a
9
- * youtube embed came to be called `id` and stayed that way.
10
- *
5
+ * A sigil describes the element and means the same on every directive; an
6
+ * attribute belongs to whichever directive reads it. Held apart so that a
7
+ * directive wanting an attribute called `id` cannot take the anchor's place.
11
8
  * A key beginning with `#` cannot be written as `key=value`, since an attribute
12
- * name has to start with a letter, so there is nowhere for the two to meet.
9
+ * name has to start with a letter.
13
10
  */
14
11
  export declare const ANCHOR = "#id";
15
12
  /** Parses the inside of `{...}`: `key=value key2="値 2" #id .cls` */
@@ -1 +1 @@
1
- {"version":3,"file":"attributes.d.ts","sourceRoot":"","sources":["../../../src/marked/attributes.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;AAE1C;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,MAAM,QAAQ,CAAA;AAE3B,oEAAoE;AACpE,eAAO,MAAM,UAAU,GAAI,MAAM,MAAM,GAAG,IAAI,KAAG,KAkBhD,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,aAAa,GACxB,KAAK,MAAM,EACX,OAAO,MAAM,EACb,MAAM,MAAM,EACZ,OAAO,MAAM,KACZ;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,IAkBnC,CAAA;AAED,mFAAmF;AACnF,eAAO,MAAM,QAAQ,GACnB,KAAK,MAAM,EACX,GAAG,MAAM,KACR;IAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,KAAK,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAcnD,CAAA"}
1
+ {"version":3,"file":"attributes.d.ts","sourceRoot":"","sources":["../../../src/marked/attributes.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;AAE1C;;;;;;;;GAQG;AACH,eAAO,MAAM,MAAM,QAAQ,CAAA;AAE3B,oEAAoE;AACpE,eAAO,MAAM,UAAU,GAAI,MAAM,MAAM,GAAG,IAAI,KAAG,KAkBhD,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,aAAa,GACxB,KAAK,MAAM,EACX,OAAO,MAAM,EACb,MAAM,MAAM,EACZ,OAAO,MAAM,KACZ;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,IAkBnC,CAAA;AAED,mFAAmF;AACnF,eAAO,MAAM,QAAQ,GACnB,KAAK,MAAM,EACX,GAAG,MAAM,KACR;IAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,KAAK,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAcnD,CAAA"}
@@ -0,0 +1,3 @@
1
+ import type { DirectiveRenderer } from './context';
2
+ export declare const imageRenderers: Record<string, DirectiveRenderer>;
3
+ //# sourceMappingURL=image.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../../../../src/marked/directives/image.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAmBlD,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAQ5D,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/marked/directives/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAclD;;;GAGG;AACH,eAAO,MAAM,YAAY,GAAI,MAAM,MAAM,KAAG,iBAAiB,GAAG,SACN,CAAA;AAE1D,YAAY,EACV,gBAAgB,EAChB,iBAAiB,EACjB,MAAM,EACN,eAAe,EAChB,MAAM,WAAW,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/marked/directives/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAgBlD;;;GAGG;AACH,eAAO,MAAM,YAAY,GAAI,MAAM,MAAM,KAAG,iBAAiB,GAAG,SACN,CAAA;AAE1D,YAAY,EACV,gBAAgB,EAChB,iBAAiB,EACjB,MAAM,EACN,eAAe,EAChB,MAAM,WAAW,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../../src/marked/html.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,UAAU,GAAI,OAAO,OAAO,KAAG,MACe,CAAA;AAgB3D;;;GAGG;AACH,eAAO,MAAM,QAAQ,GAAI,MAAM,MAAM,GAAG,SAAS,KAAG,MAAM,GAAG,SAS5D,CAAA;AAED,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,IAAI,CAAA;AAE3D,2DAA2D;AAC3D,eAAO,MAAM,WAAW,GAAI,OAAO,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,KAAG,MAIlD,CAAA;AAEb;;;GAGG;AACH,eAAO,MAAM,UAAU,GAAI,GAAG,QAAQ,CAAC,MAAM,GAAG,SAAS,GAAG,KAAK,CAAC,EAAE,KAAG,MACE,CAAA"}
1
+ {"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../../src/marked/html.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,UAAU,GAAI,OAAO,OAAO,KAAG,MACe,CAAA;AAgB3D;;;GAGG;AACH,eAAO,MAAM,QAAQ,GAAI,MAAM,MAAM,GAAG,SAAS,KAAG,MAAM,GAAG,SAS5D,CAAA;AAED,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,IAAI,CAAA;AAS3D,2DAA2D;AAC3D,eAAO,MAAM,WAAW,GAAI,OAAO,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,KAAG,MAUlD,CAAA;AAEb;;;GAGG;AACH,eAAO,MAAM,UAAU,GAAI,GAAG,QAAQ,CAAC,MAAM,GAAG,SAAS,GAAG,KAAK,CAAC,EAAE,KAAG,MACE,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/marked/registry.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,SAAS,cAAc,CAAA;AACpC,eAAO,MAAM,IAAI,SAAS,CAAA;AAC1B,eAAO,MAAM,IAAI,SAAS,CAAA;AAE1B,MAAM,MAAM,aAAa,GAAG,OAAO,SAAS,GAAG,OAAO,IAAI,GAAG,OAAO,IAAI,CAAA;AAyBxE,+EAA+E;AAC/E,eAAO,MAAM,OAAO,QAAiC,CAAA;AAErD,eAAO,MAAM,OAAO,GAAI,MAAM,MAAM,EAAE,MAAM,aAAa,KAAG,OACmB,CAAA"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/marked/registry.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,SAAS,cAAc,CAAA;AACpC,eAAO,MAAM,IAAI,SAAS,CAAA;AAC1B,eAAO,MAAM,IAAI,SAAS,CAAA;AAE1B,MAAM,MAAM,aAAa,GAAG,OAAO,SAAS,GAAG,OAAO,IAAI,GAAG,OAAO,IAAI,CAAA;AAuBxE,+EAA+E;AAC/E,eAAO,MAAM,OAAO,QAAiC,CAAA;AAErD,eAAO,MAAM,OAAO,GAAI,MAAM,MAAM,EAAE,MAAM,aAAa,KAAG,OACmB,CAAA"}
@@ -1,13 +1,11 @@
1
1
  import type { DirectiveToken } from './tokenizer';
2
2
  import { type Parser, type RendererOptions } from './directives';
3
3
  /**
4
- * Builds what a directive renderer is given, then hands the token to the one
5
- * that knows it. The renderers themselves live in `directives/`, a file to a
6
- * feature: `card.ts` holds the card and everything written inside one, because
7
- * a title has to know whether the card gave it an href.
4
+ * Builds the context, then hands the token to the renderer that knows its name.
5
+ * The renderers live in `directives/`, a file to a feature, so that a card and
6
+ * everything written inside one stay together.
8
7
  *
9
- * A name with no renderer falls through to its own children, which is what a
10
- * directive the registry allows but nobody has drawn yet looks like.
8
+ * A name with no renderer falls through to its own children.
11
9
  */
12
10
  export declare function directiveRenderer(this: {
13
11
  parser: Parser;
@@ -1 +1 @@
1
- {"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../../src/marked/renderer.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AACjD,OAAO,EAGL,KAAK,MAAM,EACX,KAAK,eAAe,EACrB,MAAM,cAAc,CAAA;AAErB;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,EACxB,KAAK,EAAE,cAAc,EACrB,OAAO,EAAE,eAAe,GACvB,MAAM,CAyBR;AAED,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA"}
1
+ {"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../../src/marked/renderer.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AACjD,OAAO,EAGL,KAAK,MAAM,EACX,KAAK,eAAe,EACrB,MAAM,cAAc,CAAA;AAErB;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,EACxB,KAAK,EAAE,cAAc,EACrB,OAAO,EAAE,eAAe,GACvB,MAAM,CAyBR;AAED,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"tokenizer.d.ts","sourceRoot":"","sources":["../../../src/marked/tokenizer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC3C,OAAO,EAAE,KAAK,KAAK,EAAY,MAAM,cAAc,CAAA;AACnD,OAAO,EAAa,KAAK,aAAa,EAAuB,MAAM,YAAY,CAAA;AAE/E,MAAM,WAAW,cAAe,SAAQ,MAAM,CAAC,OAAO;IACpD,IAAI,EAAE,WAAW,CAAA;IACjB,IAAI,EAAE,aAAa,CAAA;IACnB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,KAAK,CAAA;IACZ,MAAM,EAAE,KAAK,EAAE,CAAA;IACf,GAAG,EAAE,MAAM,CAAA;IACX,4EAA4E;IAC5E,SAAS,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;IAC9C,yEAAyE;IACzE,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,8DAA8D;IAC9D,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AA4BD;;;;;GAKG;AACH,eAAO,MAAM,UAAU,GAAI,KAAK,MAAM,KAAG,MAAM,GAAG,SAGjD,CAAA;AAED,eAAO,MAAM,WAAW,GAAI,KAAK,MAAM,KAAG,MAAM,GAAG,SAGlD,CAAA;AA+CD,wBAAgB,cAAc,CAC5B,IAAI,EAAE;IACJ,KAAK,EAAE;QACL,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,KAAK,EAAE,CAAA;QACrC,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,IAAI,CAAA;KAC/C,CAAA;CACF,EACD,GAAG,EAAE,MAAM,GACV,cAAc,GAAG,SAAS,CAsC5B;AAED,wBAAgB,eAAe,CAC7B,IAAI,EAAE;IAAE,KAAK,EAAE;QAAE,YAAY,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,KAAK,EAAE,CAAA;KAAE,CAAA;CAAE,EAC3D,GAAG,EAAE,MAAM,GACV,cAAc,GAAG,SAAS,CAiB5B"}
1
+ {"version":3,"file":"tokenizer.d.ts","sourceRoot":"","sources":["../../../src/marked/tokenizer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC3C,OAAO,EAAE,KAAK,KAAK,EAAY,MAAM,cAAc,CAAA;AACnD,OAAO,EAAa,KAAK,aAAa,EAAuB,MAAM,YAAY,CAAA;AAE/E,MAAM,WAAW,cAAe,SAAQ,MAAM,CAAC,OAAO;IACpD,IAAI,EAAE,WAAW,CAAA;IACjB,IAAI,EAAE,aAAa,CAAA;IACnB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,KAAK,CAAA;IACZ,MAAM,EAAE,KAAK,EAAE,CAAA;IACf,GAAG,EAAE,MAAM,CAAA;IACX,4EAA4E;IAC5E,SAAS,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;IAC9C,yEAAyE;IACzE,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,8DAA8D;IAC9D,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AA4BD;;;;;GAKG;AACH,eAAO,MAAM,UAAU,GAAI,KAAK,MAAM,KAAG,MAAM,GAAG,SAGjD,CAAA;AAED,eAAO,MAAM,WAAW,GAAI,KAAK,MAAM,KAAG,MAAM,GAAG,SAGlD,CAAA;AA6CD,wBAAgB,cAAc,CAC5B,IAAI,EAAE;IACJ,KAAK,EAAE;QACL,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,KAAK,EAAE,CAAA;QACrC,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,IAAI,CAAA;KAC/C,CAAA;CACF,EACD,GAAG,EAAE,MAAM,GACV,cAAc,GAAG,SAAS,CAsC5B;AAED,wBAAgB,eAAe,CAC7B,IAAI,EAAE;IAAE,KAAK,EAAE;QAAE,YAAY,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,KAAK,EAAE,CAAA;KAAE,CAAA;CAAE,EAC3D,GAAG,EAAE,MAAM,GACV,cAAc,GAAG,SAAS,CAiB5B"}
@@ -1 +1 @@
1
- {"version":3,"file":"sanitize.d.ts","sourceRoot":"","sources":["../../src/sanitize.ts"],"names":[],"mappings":"AAkLA;;;;GAIG;AACH,eAAO,MAAM,QAAQ,GAAI,MAAM,MAAM,KAAG,gBAQvC,CAAA;AAED,eAAO,MAAM,SAAS;yBAlGM,MAAM,KAAG,MAAM;;CAkGqB,CAAA"}
1
+ {"version":3,"file":"sanitize.d.ts","sourceRoot":"","sources":["../../src/sanitize.ts"],"names":[],"mappings":"AAyLA;;;;GAIG;AACH,eAAO,MAAM,QAAQ,GAAI,MAAM,MAAM,KAAG,gBAQvC,CAAA;AAED,eAAO,MAAM,SAAS;yBAzGM,MAAM,KAAG,MAAM;;CAyGqB,CAAA"}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sakura-ui/markdown",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "description": "",
5
5
  "keywords": [
6
6
  "markdown"
@@ -75,10 +75,8 @@ export const Markdown = ({
75
75
  {showToc && headings.length > 0 && (
76
76
  <nav className="rounded-3xl p-10 bg-yellow-50">
77
77
  {/*
78
- A plain h2 rather than core's H2. That one is built for a heading in
79
- a document: it is sized to be read as one, and carries the space
80
- that separates it from the text above it. This is the label on a
81
- box, at the top of it, with nothing above to separate it from.
78
+ The label on a box, not a heading in the document, so it does not
79
+ use core's H2 and its heading size and spacing.
82
80
  */}
83
81
  <h2 className="mb-4 font-bold text-h-xs-m sm:text-h-xs">
84
82
  {tocTitle}
@@ -88,8 +86,16 @@ export const Markdown = ({
88
86
  </Ul>
89
87
  </nav>
90
88
  )}
91
- {/* The markup is sanitised in src/sanitize.ts before it gets here. */}
92
- <div dangerouslySetInnerHTML={{ __html: html }} />
89
+ {/*
90
+ The document's blocks are children of this element, not of the wrapper,
91
+ so the space between them is set here.
92
+
93
+ The markup is sanitised in src/sanitize.ts before it gets here.
94
+ */}
95
+ <div
96
+ className="flex flex-col gap-8"
97
+ dangerouslySetInnerHTML={{ __html: html }}
98
+ />
93
99
  </div>
94
100
  )
95
101
  }
@@ -5,11 +5,8 @@ import { classNames } from './marked/html'
5
5
  /**
6
6
  * Applies the look of the design system to the finished document.
7
7
  *
8
- * This runs over the sanitised DOM rather than inside the markdown renderer so
9
- * that a table written as raw HTML 56 of the guide's documents do is styled
10
- * the same as one written with pipes. The pipeline this replaces had the same
11
- * property, because rehype-raw turned the raw HTML into real nodes before the
12
- * tag to component mapping ran.
8
+ * Runs over the sanitised DOM rather than inside the markdown renderer, so that
9
+ * a table written as raw HTML is styled the same as one written with pipes.
13
10
  */
14
11
 
15
12
  export type HeadingItem = {
@@ -41,7 +38,21 @@ const CLASS_BY_TAG: Record<string, string> = {
41
38
 
42
39
  /** Elements a directive rendered are already dressed; leave them be. */
43
40
  const isDirectiveOutput = (element: Element) =>
44
- element.hasAttribute('data-sakura')
41
+ element.hasAttribute('data-styled')
42
+
43
+ /**
44
+ * A link that opens elsewhere must not hand the opener to the page it opens.
45
+ * Merged with what the document asked for rather than replacing it, so a
46
+ * `rel="nofollow"` does not take `noopener` away with it.
47
+ */
48
+ const externalRel = (rel: string | null): string =>
49
+ Array.from(
50
+ new Set([
51
+ 'noopener',
52
+ 'noreferrer',
53
+ ...(rel ?? '').split(/\s+/).filter(Boolean)
54
+ ])
55
+ ).join(' ')
45
56
 
46
57
  const addClass = (element: Element, value: string) => {
47
58
  const merged = classNames(element.getAttribute('class') ?? '', value)
@@ -56,10 +67,10 @@ const HEADINGS = new Set(['h1', 'h2', 'h3', 'h4', 'h5', 'h6'])
56
67
  * the Icon component does.
57
68
  */
58
69
  const appendNewTabIcon = (anchor: Element, document: Document) => {
59
- if (anchor.querySelector('[data-sakura-new-tab]')) return
70
+ if (anchor.querySelector('[data-new-tab]')) return
60
71
  const glyph = document.createElement('span')
61
72
  glyph.setAttribute('aria-hidden', 'true')
62
- glyph.setAttribute('data-sakura-new-tab', 'true')
73
+ glyph.setAttribute('data-new-tab', 'true')
63
74
  glyph.setAttribute(
64
75
  'class',
65
76
  classNames(styles.iconSizeStyle[16], styles.iconStyle, 'ml-0.5')
@@ -113,9 +124,7 @@ export const decorate = (
113
124
  const tag = element.tagName.toLowerCase()
114
125
 
115
126
  // One id, generated once, for headings from markdown and from raw HTML
116
- // alike. The pipeline this replaces generated them in two passes that
117
- // disagreed whenever a heading held an image, raw HTML or a directive, and
118
- // the table of contents then linked to anchors that were not there.
127
+ // alike, so the table of contents and the body cannot disagree.
119
128
  if (HEADINGS.has(tag)) {
120
129
  const text = element.textContent ?? ''
121
130
  const asked = element.getAttribute('id')
@@ -136,9 +145,7 @@ export const decorate = (
136
145
  element.getAttribute('target') === '_blank'
137
146
  ) {
138
147
  element.setAttribute('target', '_blank')
139
- // The content writes target="_blank" 41 times and rel not once, which
140
- // leaves the opened page holding a handle on this one.
141
- element.setAttribute('rel', 'noopener noreferrer')
148
+ element.setAttribute('rel', externalRel(element.getAttribute('rel')))
142
149
  appendNewTabIcon(element, document)
143
150
  }
144
151
  }
@@ -150,10 +157,10 @@ export const decorate = (
150
157
  // A wide table scrolls inside its own box rather than pushing the page.
151
158
  for (const table of Array.from(root.querySelectorAll('table'))) {
152
159
  const parent = table.parentElement
153
- if (parent?.dataset?.sakuraTableContainer === 'true') continue
160
+ if (parent?.dataset?.tableContainer === 'true') continue
154
161
  const container = document.createElement('div')
155
162
  container.setAttribute('class', classNames(styles.overflowContainerStyle))
156
- container.dataset.sakuraTableContainer = 'true'
163
+ container.dataset.tableContainer = 'true'
157
164
  table.replaceWith(container)
158
165
  container.appendChild(table)
159
166
  }
@@ -6,14 +6,11 @@ export type Attrs = Record<string, string>
6
6
  /**
7
7
  * Where `{#name}` is kept, apart from anything written as `key=value`.
8
8
  *
9
- * A sigil says something about the element itself and means the same on every
10
- * directive; an attribute is the directive's own, and each reads the ones it
11
- * knows. Keeping them in one place would let a directive that wanted an
12
- * attribute called `id` take the anchor's place, which is how the video on a
13
- * youtube embed came to be called `id` and stayed that way.
14
- *
9
+ * A sigil describes the element and means the same on every directive; an
10
+ * attribute belongs to whichever directive reads it. Held apart so that a
11
+ * directive wanting an attribute called `id` cannot take the anchor's place.
15
12
  * A key beginning with `#` cannot be written as `key=value`, since an attribute
16
- * name has to start with a letter, so there is nowhere for the two to meet.
13
+ * name has to start with a letter.
17
14
  */
18
15
  export const ANCHOR = '#id'
19
16
 
@@ -12,8 +12,8 @@ const iconHtml = (name: string, altText: string): string =>
12
12
 
13
13
  export const cardRenderers: Record<string, DirectiveRenderer> = {
14
14
  card: ({ token, attrs, root, body, nl }) => {
15
- // The tokenizer withholds linked from a card with no title, which has no
16
- // link to give and so must not look as though it had one.
15
+ // Set by the tokenizer, and only when the card has a title to put the
16
+ // link on. A card without one must not look as though it were clickable.
17
17
  const isLink = Boolean(token.linked)
18
18
  const cls = classNames(
19
19
  styles.cardStyle,
@@ -0,0 +1,29 @@
1
+ import { classNames, cleanUrl } from '../html'
2
+ import type { DirectiveRenderer } from './context'
3
+
4
+ /**
5
+ * `width` and `height` are pixels. `max-width` goes with a width so that the
6
+ * image shrinks inside its column on a narrow screen rather than overflowing
7
+ * it; with a height alone there is nothing to overflow, since the width
8
+ * follows the proportions.
9
+ */
10
+ const sizeStyle = (
11
+ width: string | undefined,
12
+ height: string | undefined
13
+ ): string | undefined => {
14
+ const rules: string[] = []
15
+ if (width) rules.push(`width:${width}px`)
16
+ if (height) rules.push(`height:${height}px`)
17
+ if (width) rules.push('max-width:100%')
18
+ return rules.length > 0 ? rules.join(';') : undefined
19
+ }
20
+
21
+ export const imageRenderers: Record<string, DirectiveRenderer> = {
22
+ img: ({ attrs, root, nl }) =>
23
+ `<img${root({
24
+ class: classNames(attrs.class),
25
+ src: cleanUrl(attrs.src),
26
+ alt: attrs.alt ?? '',
27
+ style: sizeStyle(attrs.width, attrs.height)
28
+ })}>${nl}`
29
+ }
@@ -2,6 +2,7 @@ import { GRID_RE } from '../registry'
2
2
  import { cardRenderers } from './card'
3
3
  import type { DirectiveRenderer } from './context'
4
4
  import { faqRenderers } from './faq'
5
+ import { imageRenderers } from './image'
5
6
  import { cellRenderers, grid } from './grid'
6
7
  import { linkButtonRenderers } from './linkButton'
7
8
  import { youtubeRenderers } from './youtube'
@@ -10,6 +11,7 @@ const BY_NAME: Record<string, DirectiveRenderer> = {
10
11
  ...cardRenderers,
11
12
  ...cellRenderers,
12
13
  ...faqRenderers,
14
+ ...imageRenderers,
13
15
  ...youtubeRenderers,
14
16
  ...linkButtonRenderers
15
17
  }
@@ -31,19 +31,32 @@ export const cleanUrl = (href: string | undefined): string | undefined => {
31
31
  if (href == null || href === '') return undefined
32
32
  const trimmed = stripControl(String(href)).trim()
33
33
  if (/^(?:javascript|vbscript|file):/i.test(trimmed)) return undefined
34
- // Images written as data: used to be kept here and dropped nowhere, since
35
- // DOMPurify allows the scheme on an img whatever ALLOWED_URI_REGEXP says. The
36
- // content has no use for it, so both this and the sanitiser refuse it now.
34
+ // Refused here and in the sanitiser both: DOMPurify allows the scheme on an
35
+ // img whatever ALLOWED_URI_REGEXP says, so this is the only place a directive
36
+ // writing one is turned back.
37
37
  if (/^data:/i.test(trimmed)) return undefined
38
38
  return trimmed
39
39
  }
40
40
 
41
41
  export type AttrValue = string | boolean | undefined | null
42
42
 
43
+ /**
44
+ * Attributes where an empty value says something, and so is written out. On an
45
+ * image an empty alt marks it as decoration; leaving the attribute off instead
46
+ * makes a screen reader read the file name.
47
+ */
48
+ const KEEP_WHEN_EMPTY = new Set(['alt'])
49
+
43
50
  /** `{a: 'x', b: true, c: undefined}` becomes ` a="x" b` */
44
51
  export const attrsToHtml = (attrs: Record<string, AttrValue>): string =>
45
52
  Object.entries(attrs)
46
- .filter(([, v]) => v !== undefined && v !== null && v !== false && v !== '')
53
+ .filter(
54
+ ([k, v]) =>
55
+ v !== undefined &&
56
+ v !== null &&
57
+ v !== false &&
58
+ (v !== '' || KEEP_WHEN_EMPTY.has(k))
59
+ )
47
60
  .map(([k, v]) => (v === true ? ` ${k}` : ` ${k}="${escapeHtml(v)}"`))
48
61
  .join('')
49
62
 
@@ -6,11 +6,8 @@ export type DirectiveKind = typeof CONTAINER | typeof LEAF | typeof TEXT
6
6
 
7
7
  /**
8
8
  * The whitelist. A name that is not here is not a directive, and the text
9
- * carrying it is left alone.
10
- *
11
- * The remark pipeline this replaces turned any name into a tag of the same
12
- * name, so ordinary prose lost characters: "HH:MM" rendered as "HH", and
13
- * "chat:write" as "chat".
9
+ * carrying it is left alone — which is what keeps "HH:MM" and "chat:write"
10
+ * intact in the prose.
14
11
  */
15
12
  const STATIC: Record<string, DirectiveKind[]> = {
16
13
  card: [CONTAINER],
@@ -23,6 +20,7 @@ const STATIC: Record<string, DirectiveKind[]> = {
23
20
  'faq-a': [LEAF, CONTAINER],
24
21
  cell: [CONTAINER],
25
22
  'cell-img': [LEAF],
23
+ img: [LEAF],
26
24
  youtube: [LEAF, CONTAINER],
27
25
  'link-button': [TEXT]
28
26
  }
@@ -10,13 +10,11 @@ import {
10
10
  } from './directives'
11
11
 
12
12
  /**
13
- * Builds what a directive renderer is given, then hands the token to the one
14
- * that knows it. The renderers themselves live in `directives/`, a file to a
15
- * feature: `card.ts` holds the card and everything written inside one, because
16
- * a title has to know whether the card gave it an href.
13
+ * Builds the context, then hands the token to the renderer that knows its name.
14
+ * The renderers live in `directives/`, a file to a feature, so that a card and
15
+ * everything written inside one stay together.
17
16
  *
18
- * A name with no renderer falls through to its own children, which is what a
19
- * directive the registry allows but nobody has drawn yet looks like.
17
+ * A name with no renderer falls through to its own children.
20
18
  */
21
19
  export function directiveRenderer(
22
20
  this: { parser: Parser },
@@ -26,10 +24,10 @@ export function directiveRenderer(
26
24
  const { name, kind, attrs, tokens } = token
27
25
  const nl = kind === TEXT ? '' : '\n'
28
26
 
29
- // Marks what the directive dressed itself, so that the pass which styles the
30
- // rest of the document leaves it alone.
27
+ // Marks the element as dressed, so that the pass which styles the rest of
28
+ // the document leaves it alone. The value names the directive it came from.
31
29
  const own: DirectiveContext['own'] = (values) =>
32
- attrsToHtml({ ...values, 'data-sakura': name })
30
+ attrsToHtml({ ...values, 'data-styled': name })
33
31
 
34
32
  const context: DirectiveContext = {
35
33
  token,
@@ -60,14 +60,12 @@ export const inlineStart = (src: string): number | undefined => {
60
60
  }
61
61
 
62
62
  /**
63
- * A grid of cards is a list. The cards are things of one kind, and how many
64
- * there are is part of what the page says; a grid holding anything else, or
65
- * cards and something else together, is a layout — prose beside a figure is not
66
- * two of something. Nothing is written to ask for this, the way nothing is
67
- * written to ask a run of "- " for a <ul>.
63
+ * A grid of cards is a list: the cards are things of one kind, and how many
64
+ * there are is part of what the page says. A grid holding anything else, or
65
+ * cards mixed with something else, is a layout.
68
66
  *
69
- * Marked here rather than worked out while rendering, because by then the
70
- * children have become one string and the boundaries between them are gone.
67
+ * Marked here rather than while rendering, because by then the children are one
68
+ * string with no boundaries between them.
71
69
  */
72
70
  const markCardList = (token: DirectiveToken): void => {
73
71
  if (!/^grid-cols-\d+$/.test(token.name)) return
@@ -168,9 +168,16 @@ const purifier = (): DOMPurify => {
168
168
  element.removeAttribute('style')
169
169
  }
170
170
  }
171
- // A link that opens elsewhere must not hand the opener over with it.
171
+ // A link that opens elsewhere must not hand the opener over with it. What
172
+ // the document asked for is kept alongside rather than replaced.
172
173
  if (element.getAttribute('target') === '_blank') {
173
- element.setAttribute('rel', 'noopener noreferrer')
174
+ const asked = (element.getAttribute('rel') ?? '').split(/\s+/)
175
+ element.setAttribute(
176
+ 'rel',
177
+ Array.from(
178
+ new Set(['noopener', 'noreferrer', ...asked.filter(Boolean)])
179
+ ).join(' ')
180
+ )
174
181
  }
175
182
  })
176
183
  return instance