Skip to content

Commit 3fe566e

Browse files
committed
Highlight emotion keywords in spec table
1 parent e0c79cd commit 3fe566e

File tree

2 files changed

+200
-1
lines changed

2 files changed

+200
-1
lines changed

scripts/build-site.mjs

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,20 @@ const readmeUrl = `${repoUrl}/blob/main/README.md`;
1818
const licenseUrl = `${repoUrl}/blob/main/LICENSE`;
1919
const socialImageUrl = `${siteUrl}social-card.svg`;
2020
const siteName = "TPS Format Specification";
21+
const emotionStyles = {
22+
warm: { colorLabel: "Orange" },
23+
concerned: { colorLabel: "Red" },
24+
focused: { colorLabel: "Green" },
25+
motivational: { colorLabel: "Purple" },
26+
neutral: { colorLabel: "Blue" },
27+
urgent: { colorLabel: "Bright Red" },
28+
happy: { colorLabel: "Yellow" },
29+
excited: { colorLabel: "Pink" },
30+
sad: { colorLabel: "Indigo" },
31+
calm: { colorLabel: "Teal" },
32+
energetic: { colorLabel: "Orange-Red" },
33+
professional: { colorLabel: "Navy" }
34+
};
2135

2236
const readme = await readFile(readmePath, "utf8");
2337
const styles = await readFile(stylesPath, "utf8");
@@ -47,7 +61,7 @@ const title = extractTitle(tokens) ?? "TPS Format Specification";
4761
const summary = extractSummary(tokens) ?? "Markdown-based teleprompter scripts with timing, pacing, emotion, and styling metadata.";
4862
const sections = extractSections(tokens);
4963
const stats = buildStats(readme, sections);
50-
const articleHtml = md.renderer.render(trimTitle(tokens), md.options, {});
64+
const articleHtml = enhanceArticleHtml(md.renderer.render(trimTitle(tokens), md.options, {}));
5165
const heroTitle = buildHeroTitle(title);
5266
const quickAnswers = buildQuickAnswers(summary);
5367
const keywords = buildKeywords();
@@ -265,6 +279,38 @@ function buildStats(markdown, sectionList) {
265279
};
266280
}
267281

282+
function enhanceArticleHtml(html) {
283+
return decorateEmotionTable(html);
284+
}
285+
286+
function decorateEmotionTable(html) {
287+
const sectionPattern = /(<h3 id="emotions-case-insensitive"[\s\S]*?<\/h3>\s*)(<table>[\s\S]*?<\/table>)/;
288+
289+
return html.replace(sectionPattern, (match, headingHtml, tableHtml) => {
290+
let enhancedTable = tableHtml.replace("<table>", '<table class="emotion-table">');
291+
292+
for (const [keyword, { colorLabel }] of Object.entries(emotionStyles)) {
293+
const escapedKeyword = escapeRegExp(keyword);
294+
const escapedColorLabel = escapeRegExp(colorLabel);
295+
const rowPattern = new RegExp(
296+
`<tr>\\s*<td><code>${escapedKeyword}<\\/code><\\/td>\\s*<td>${escapedColorLabel}<\\/td>\\s*<td>([^<]+)<\\/td>\\s*<td>([\\s\\S]*?)<\\/td>\\s*<\\/tr>`
297+
);
298+
299+
enhancedTable = enhancedTable.replace(
300+
rowPattern,
301+
`<tr class="emotion-row emotion-row-${keyword}">
302+
<td class="emotion-keyword-cell"><code class="emotion-token">${keyword}</code></td>
303+
<td class="emotion-color-cell"><span class="emotion-swatch-wrap"><span class="emotion-swatch" aria-hidden="true"></span><span class="emotion-color-name">${colorLabel}</span></span></td>
304+
<td class="emotion-emoji-cell"><span class="emotion-emoji" aria-hidden="true">$1</span></td>
305+
<td>$2</td>
306+
</tr>`
307+
);
308+
}
309+
310+
return `${headingHtml}${enhancedTable}`;
311+
});
312+
}
313+
268314
function trimTitle(tokenList) {
269315
if (
270316
tokenList[0]?.type === "heading_open" &&
@@ -496,3 +542,7 @@ function escapeHtml(value) {
496542
.replaceAll('"', "&quot;")
497543
.replaceAll("'", "&#39;");
498544
}
545+
546+
function escapeRegExp(value) {
547+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
548+
}

website/site.css

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,155 @@ pre code {
722722
border-bottom: 0;
723723
}
724724

725+
.markdown-body .emotion-table {
726+
border-color: rgba(153, 179, 210, 0.18);
727+
}
728+
729+
.markdown-body .emotion-table tbody tr {
730+
transition:
731+
transform 160ms ease,
732+
background-color 160ms ease;
733+
}
734+
735+
.markdown-body .emotion-table tbody tr:hover {
736+
transform: translateY(-1px);
737+
}
738+
739+
.markdown-body .emotion-row td {
740+
vertical-align: middle;
741+
}
742+
743+
.markdown-body .emotion-row td:first-child {
744+
box-shadow: inset 3px 0 0 var(--emotion-color);
745+
}
746+
747+
.markdown-body .emotion-row td:last-child {
748+
color: var(--text);
749+
}
750+
751+
.markdown-body .emotion-row:hover td {
752+
background:
753+
linear-gradient(90deg, var(--emotion-soft), rgba(14, 28, 45, 0.16) 72%),
754+
rgba(14, 28, 45, 0.18);
755+
}
756+
757+
.markdown-body .emotion-keyword-cell,
758+
.markdown-body .emotion-color-cell,
759+
.markdown-body .emotion-emoji-cell {
760+
white-space: nowrap;
761+
}
762+
763+
.markdown-body .emotion-token {
764+
display: inline-flex;
765+
align-items: center;
766+
justify-content: center;
767+
min-width: 7.75rem;
768+
padding: 0.45rem 0.82rem;
769+
border: 1px solid color-mix(in srgb, var(--emotion-color) 42%, transparent);
770+
border-radius: 999px;
771+
background: var(--emotion-soft);
772+
color: var(--emotion-color);
773+
font-size: 0.8rem;
774+
font-weight: 800;
775+
letter-spacing: 0.08em;
776+
text-transform: uppercase;
777+
}
778+
779+
.markdown-body .emotion-swatch-wrap {
780+
display: inline-flex;
781+
align-items: center;
782+
gap: 0.72rem;
783+
font-weight: 800;
784+
color: var(--text);
785+
}
786+
787+
.markdown-body .emotion-swatch {
788+
width: 0.9rem;
789+
height: 0.9rem;
790+
border-radius: 999px;
791+
background: var(--emotion-color);
792+
box-shadow:
793+
0 0 0 4px var(--emotion-soft),
794+
0 0 18px rgba(255, 255, 255, 0.08);
795+
}
796+
797+
.markdown-body .emotion-color-name {
798+
color: var(--emotion-color);
799+
font-weight: 800;
800+
}
801+
802+
.markdown-body .emotion-emoji {
803+
display: inline-grid;
804+
place-items: center;
805+
width: 2.4rem;
806+
height: 2.4rem;
807+
border: 1px solid rgba(153, 179, 210, 0.14);
808+
border-radius: 16px;
809+
background: var(--emotion-soft);
810+
font-size: 1.25rem;
811+
line-height: 1;
812+
}
813+
814+
.markdown-body .emotion-row-warm {
815+
--emotion-color: #ffb55c;
816+
--emotion-soft: rgba(255, 181, 92, 0.16);
817+
}
818+
819+
.markdown-body .emotion-row-concerned {
820+
--emotion-color: #ff7282;
821+
--emotion-soft: rgba(255, 114, 130, 0.16);
822+
}
823+
824+
.markdown-body .emotion-row-focused {
825+
--emotion-color: #62d893;
826+
--emotion-soft: rgba(98, 216, 147, 0.16);
827+
}
828+
829+
.markdown-body .emotion-row-motivational {
830+
--emotion-color: #c291ff;
831+
--emotion-soft: rgba(194, 145, 255, 0.16);
832+
}
833+
834+
.markdown-body .emotion-row-neutral {
835+
--emotion-color: #78caff;
836+
--emotion-soft: rgba(120, 202, 255, 0.16);
837+
}
838+
839+
.markdown-body .emotion-row-urgent {
840+
--emotion-color: #ff5252;
841+
--emotion-soft: rgba(255, 82, 82, 0.16);
842+
}
843+
844+
.markdown-body .emotion-row-happy {
845+
--emotion-color: #ffd76b;
846+
--emotion-soft: rgba(255, 215, 107, 0.16);
847+
}
848+
849+
.markdown-body .emotion-row-excited {
850+
--emotion-color: #ff94d8;
851+
--emotion-soft: rgba(255, 148, 216, 0.16);
852+
}
853+
854+
.markdown-body .emotion-row-sad {
855+
--emotion-color: #97a4ff;
856+
--emotion-soft: rgba(151, 164, 255, 0.16);
857+
}
858+
859+
.markdown-body .emotion-row-calm {
860+
--emotion-color: #5fd8d0;
861+
--emotion-soft: rgba(95, 216, 208, 0.16);
862+
}
863+
864+
.markdown-body .emotion-row-energetic {
865+
--emotion-color: #ff874f;
866+
--emotion-soft: rgba(255, 135, 79, 0.16);
867+
}
868+
869+
.markdown-body .emotion-row-professional {
870+
--emotion-color: #89a8d8;
871+
--emotion-soft: rgba(137, 168, 216, 0.16);
872+
}
873+
725874
.markdown-body pre {
726875
position: relative;
727876
padding: 2.6rem 1.3rem 1.25rem;

0 commit comments

Comments
 (0)