input coloring

This commit is contained in:
Ian Gulliver
2026-10-17 08:53:21 -07:00
parent 3e537c827d
commit b1eb595af1
8 changed files with 171 additions and 15 deletions
+41 -4
View File
@@ -245,10 +245,47 @@ function cells(line) {
}
function table(rows, opts) {
const [head, ...body] = rows;
return h("div", { class: "md-table" }, h("table", {}, h("thead", {}, h("tr", {}, ...head.map((c) => h("th", {}, ...inline(c, opts))))), h("tbody", {}, ...body.map((r) => h("tr", {}, ...r.map((c) => h("td", {}, ...inline(c, opts))))))));
return h("div", { class: "md-table" }, h("table", {}, h("thead", {}, h("tr", {}, ...head.map((c) => h("th", markClass(c), ...inline(c, opts))))), h("tbody", {}, ...body.map((r) => h("tr", {}, ...r.map((c) => h("td", markClass(c), ...inline(c, opts))))))));
}
const marks = {
"✓": "ok",
"✔": "ok",
"✔️": "ok",
"✅": "ok",
"☑": "ok",
"☑️": "ok",
"~": "warn",
"≈": "warn",
"〜": "warn",
"⚠": "warn",
"⚠️": "warn",
"🟡": "warn",
x: "danger",
X: "danger",
"×": "danger",
"✗": "danger",
"✘": "danger",
"✕": "danger",
"✖": "danger",
"✖️": "danger",
"❌": "danger",
};
function markOf(src) {
return marks[src.trim().replace(/^(\*\*|__)(.*)\1$/, "$2").trim()];
}
function markClass(cell) {
const mark = markOf(cell);
return mark ? { class: `md-mark ${mark}` } : {};
}
const inlineToken = /`([^`]+)`|\*\*(.+?)\*\*|__(.+?)__|\*([^*\s][^*]*?)\*|(?<![\w])_([^_\s][^_]*?)_(?![\w])|!\[([^\]]*)\]\((?:<([^>]+)>|([^)]+))\)|\[([^\]]+)\]\(([^)\s]+)\)|(https?:\/\/[^\s<>()]+[^\s<>().,;:!?'"])/;
function inline(src, opts) {
const mark = markOf(src);
if (mark) {
return [h("span", { class: `md-mark ${mark}` }, ...spans(src, opts))];
}
return spans(src, opts);
}
function spans(src, opts) {
const out = [];
let rest = src;
while (rest.length > 0) {
@@ -265,16 +302,16 @@ function inline(src, opts) {
out.push(h("code", {}, code));
}
else if (strong1 !== undefined || strong2 !== undefined) {
out.push(h("strong", {}, ...inline(strong1 ?? strong2, opts)));
out.push(h("strong", {}, ...spans(strong1 ?? strong2, opts)));
}
else if (em1 !== undefined || em2 !== undefined) {
out.push(h("em", {}, ...inline(em1 ?? em2, opts)));
out.push(h("em", {}, ...spans(em1 ?? em2, opts)));
}
else if (imgAlt !== undefined) {
out.push(image((imgAngled ?? imgSrc).trim(), imgAlt, opts.taskId));
}
else if (linkText !== undefined) {
out.push(link(linkHref, inline(linkText, opts)));
out.push(link(linkHref, spans(linkText, opts)));
}
else if (bare !== undefined) {
out.push(link(bare, [document.createTextNode(bare)]));
+1 -1
View File
File diff suppressed because one or more lines are too long
+46 -5
View File
@@ -283,16 +283,57 @@ function table(rows: string[][], opts: Options): HTMLElement {
h(
"table",
{},
h("thead", {}, h("tr", {}, ...head!.map((c) => h("th", {}, ...inline(c, opts))))),
h("tbody", {}, ...body.map((r) => h("tr", {}, ...r.map((c) => h("td", {}, ...inline(c, opts)))))),
h("thead", {}, h("tr", {}, ...head!.map((c) => h("th", markClass(c), ...inline(c, opts))))),
h("tbody", {}, ...body.map((r) => h("tr", {}, ...r.map((c) => h("td", markClass(c), ...inline(c, opts)))))),
),
);
}
const marks: Record<string, string> = {
"✓": "ok",
"✔": "ok",
"✔️": "ok",
"✅": "ok",
"☑": "ok",
"☑️": "ok",
"~": "warn",
"≈": "warn",
"〜": "warn",
"⚠": "warn",
"⚠️": "warn",
"🟡": "warn",
x: "danger",
X: "danger",
"×": "danger",
"✗": "danger",
"✘": "danger",
"✕": "danger",
"✖": "danger",
"✖️": "danger",
"❌": "danger",
};
function markOf(src: string): string | undefined {
return marks[src.trim().replace(/^(\*\*|__)(.*)\1$/, "$2").trim()];
}
function markClass(cell: string): { class?: string } {
const mark = markOf(cell);
return mark ? { class: `md-mark ${mark}` } : {};
}
const inlineToken =
/`([^`]+)`|\*\*(.+?)\*\*|__(.+?)__|\*([^*\s][^*]*?)\*|(?<![\w])_([^_\s][^_]*?)_(?![\w])|!\[([^\]]*)\]\((?:<([^>]+)>|([^)]+))\)|\[([^\]]+)\]\(([^)\s]+)\)|(https?:\/\/[^\s<>()]+[^\s<>().,;:!?'"])/;
function inline(src: string, opts: Options): Node[] {
const mark = markOf(src);
if (mark) {
return [h("span", { class: `md-mark ${mark}` }, ...spans(src, opts))];
}
return spans(src, opts);
}
function spans(src: string, opts: Options): Node[] {
const out: Node[] = [];
let rest = src;
while (rest.length > 0) {
@@ -308,13 +349,13 @@ function inline(src: string, opts: Options): Node[] {
if (code !== undefined) {
out.push(h("code", {}, code));
} else if (strong1 !== undefined || strong2 !== undefined) {
out.push(h("strong", {}, ...inline(strong1 ?? strong2!, opts)));
out.push(h("strong", {}, ...spans(strong1 ?? strong2!, opts)));
} else if (em1 !== undefined || em2 !== undefined) {
out.push(h("em", {}, ...inline(em1 ?? em2!, opts)));
out.push(h("em", {}, ...spans(em1 ?? em2!, opts)));
} else if (imgAlt !== undefined) {
out.push(image((imgAngled ?? imgSrc!).trim(), imgAlt, opts.taskId));
} else if (linkText !== undefined) {
out.push(link(linkHref!, inline(linkText, opts)));
out.push(link(linkHref!, spans(linkText, opts)));
} else if (bare !== undefined) {
out.push(link(bare, [document.createTextNode(bare)]));
}
+24
View File
@@ -850,6 +850,30 @@ button.compact.danger {
background: var(--surface);
}
.md-mark.ok {
color: var(--ok);
}
.md-mark.warn {
color: var(--warn);
}
.md-mark.danger {
color: var(--danger);
}
.md .md-mark.ok:is(td, th) {
background: var(--ok-tint);
}
.md .md-mark.warn:is(td, th) {
background: var(--warn-tint);
}
.md .md-mark.danger:is(td, th) {
background: var(--danger-tint);
}
.msg.user {
align-self: flex-end;
max-width: 85%;