@chromamark/renderer 0.2.1 → 0.3.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.
package/README.md CHANGED
@@ -24,6 +24,25 @@ import chromamark, { render, createRenderer } from '@chromamark/renderer';
24
24
  | `default` (`chromamark`) | markdown-it plugin: `new MarkdownIt().use(chromamark, options?)`. |
25
25
  | `render(src, options?)` | Convenience: ChromaMark string → HTML fragment. |
26
26
  | `createRenderer(options?)`| A `markdown-it` instance preconfigured with ChromaMark. |
27
+ | `renderAnsi(src, opts?)` | ChromaMark string → ANSI-styled text for a terminal. |
28
+ | `lint(src, opts?)` | Check for common mistakes → array of `{ line, column, rule, … }`. |
29
+
30
+ ### Terminal rendering
31
+
32
+ `renderAnsi` walks the same parse into a TTY: tones become ANSI colors, pills
33
+ become bracketed icon chips (`[✓ PASS]`), blocks get a colored left bar, and
34
+ meters draw a unicode bar. It degrades to plain, icon-annotated text when color
35
+ is off.
36
+
37
+ ```js
38
+ import { renderAnsi } from '@chromamark/renderer';
39
+
40
+ process.stdout.write(renderAnsi('::: success\nAll good [!ok healthy]\n:::'));
41
+ // options: { color: 'auto' | 'always' | 'never', width?: number }
42
+ ```
43
+
44
+ Color defaults to `'auto'` — on when stdout is a TTY and [`NO_COLOR`](https://no-color.org)
45
+ is unset. The `colorEnabled(option, env?, isTTY?)` helper is exported too.
27
46
 
28
47
  ### Options
29
48
 
@@ -5929,13 +5929,52 @@ function makeRule2(enabled) {
5929
5929
  if (silent) return true;
5930
5930
  let nextLine = startLine;
5931
5931
  let autoClosed = false;
5932
+ let fenceCh = 0;
5933
+ let fenceLen = 0;
5932
5934
  for (; ; ) {
5933
5935
  nextLine++;
5934
5936
  if (nextLine >= endLine) break;
5935
5937
  const lstart = state.bMarks[nextLine] + state.tShift[nextLine];
5936
5938
  const lmax = state.eMarks[nextLine];
5937
- if (state.src.charCodeAt(lstart) !== MARKER) continue;
5938
- if (state.sCount[nextLine] - state.blkIndent >= 4) continue;
5939
+ const indent = state.sCount[nextLine] - state.blkIndent;
5940
+ if (fenceCh) {
5941
+ if (indent < 4 && state.src.charCodeAt(lstart) === fenceCh) {
5942
+ let q = lstart;
5943
+ while (q < lmax && state.src.charCodeAt(q) === fenceCh) q++;
5944
+ if (q - lstart >= fenceLen) {
5945
+ let r = q;
5946
+ while (r < lmax && (state.src.charCodeAt(r) === 32 || state.src.charCodeAt(r) === 9)) r++;
5947
+ if (r >= lmax) {
5948
+ fenceCh = 0;
5949
+ fenceLen = 0;
5950
+ }
5951
+ }
5952
+ }
5953
+ continue;
5954
+ }
5955
+ const openCh = state.src.charCodeAt(lstart);
5956
+ if (indent < 4 && (openCh === 96 || openCh === 126)) {
5957
+ let q = lstart;
5958
+ while (q < lmax && state.src.charCodeAt(q) === openCh) q++;
5959
+ if (q - lstart >= 3) {
5960
+ let ok = true;
5961
+ if (openCh === 96) {
5962
+ for (let r = q; r < lmax; r++) {
5963
+ if (state.src.charCodeAt(r) === 96) {
5964
+ ok = false;
5965
+ break;
5966
+ }
5967
+ }
5968
+ }
5969
+ if (ok) {
5970
+ fenceCh = openCh;
5971
+ fenceLen = q - lstart;
5972
+ continue;
5973
+ }
5974
+ }
5975
+ }
5976
+ if (openCh !== MARKER) continue;
5977
+ if (indent >= 4) continue;
5939
5978
  const closeLen = fenceLength(state.src, lstart, lmax);
5940
5979
  if (closeLen < openLen) continue;
5941
5980
  let p = lstart + closeLen;
@@ -6015,6 +6054,24 @@ function containerPlugin(md, enabled) {
6015
6054
  }
6016
6055
  return html + "</dl>";
6017
6056
  };
6057
+ md.core.ruler.push("cm_sanitize_bodies", (state) => {
6058
+ let depth = 0;
6059
+ for (const token of state.tokens) {
6060
+ if (token.type === "cm_container_open") {
6061
+ depth++;
6062
+ } else if (token.type === "cm_container_close") {
6063
+ depth--;
6064
+ } else if (depth > 0) {
6065
+ if (token.type === "html_block") {
6066
+ token.content = esc(token.content);
6067
+ } else if (token.type === "inline" && token.children) {
6068
+ for (const child of token.children) {
6069
+ if (child.type === "html_inline") child.content = esc(child.content);
6070
+ }
6071
+ }
6072
+ }
6073
+ }
6074
+ });
6018
6075
  }
6019
6076
 
6020
6077
  // src/index.js
@@ -6081,16 +6138,25 @@ function injectTheme(doc) {
6081
6138
  (d.head || d.documentElement).appendChild(style);
6082
6139
  }
6083
6140
  function dedent(text2) {
6084
- const lines = text2.replace(/\t/g, " ").replace(/\r/g, "").split("\n");
6141
+ const lines = text2.replace(/\r/g, "").split("\n");
6085
6142
  while (lines.length && lines[0].trim() === "") lines.shift();
6086
6143
  while (lines.length && lines[lines.length - 1].trim() === "") lines.pop();
6087
- let min = Infinity;
6144
+ let prefix = null;
6088
6145
  for (const line of lines) {
6089
6146
  if (!line.trim()) continue;
6090
- min = Math.min(min, line.match(/^ */)[0].length);
6147
+ const lead = line.match(/^[ \t]*/)[0];
6148
+ if (prefix === null) {
6149
+ prefix = lead;
6150
+ continue;
6151
+ }
6152
+ let i = 0;
6153
+ const lim = Math.min(prefix.length, lead.length);
6154
+ while (i < lim && prefix[i] === lead[i]) i++;
6155
+ prefix = prefix.slice(0, i);
6156
+ if (prefix === "") break;
6091
6157
  }
6092
- if (!Number.isFinite(min)) min = 0;
6093
- return lines.map((line) => line.slice(min)).join("\n");
6158
+ const cut = prefix ? prefix.length : 0;
6159
+ return lines.map((line) => line.slice(cut)).join("\n");
6094
6160
  }
6095
6161
  function resolve(target) {
6096
6162
  return typeof target === "string" ? document.querySelector(target) : target;