Skip to content

Commit 59872dc

Browse files
author
Mark Riechers
authored
Merge pull request #9 from MarkOnFire/claude/add-suggested-tools-dl9GU
Data-driven suggested tools with markdown source and GitHub Action
2 parents 73c067c + fdbdfff commit 59872dc

File tree

6 files changed

+202
-2
lines changed

6 files changed

+202
-2
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: Build suggested tools
2+
3+
on:
4+
push:
5+
paths:
6+
- 'suggested-tools.md'
7+
workflow_dispatch:
8+
9+
permissions:
10+
contents: write
11+
12+
jobs:
13+
build:
14+
runs-on: ubuntu-latest
15+
steps:
16+
- uses: actions/checkout@v4
17+
18+
- name: Build suggested tools HTML
19+
run: node scripts/build-suggested-tools.js
20+
21+
- name: Commit changes (if any)
22+
run: |
23+
git config user.name "github-actions[bot]"
24+
git config user.email "github-actions[bot]@users.noreply.github.com"
25+
git diff --quiet && exit 0
26+
git add index.html formatter/index.html og-image/index.html
27+
git commit -m "Rebuild suggested tools from suggested-tools.md"
28+
git push

formatter/index.html

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,9 +265,12 @@ <h2>Live Preview</h2>
265265
<a href="../" rel="noopener">All Tools</a> &middot;
266266
<a href="https://validator.w3.org/" target="_blank" rel="noopener">W3C Validator</a> &middot;
267267
<a href="https://caniuse.com/" target="_blank" rel="noopener">Can I Use</a> &middot;
268+
<!-- SUGGESTED_TOOLS_START -->
268269
<a href="https://validator.w3.org/feed/" target="_blank" rel="noopener">W3C Feed Validator</a> &middot;
269270
<a href="https://www.castfeedvalidator.com/" target="_blank" rel="noopener">Cast Feed Validator</a> &middot;
270-
<a href="https://embedresponsively.com/" target="_blank" rel="noopener">Embed Responsively</a>
271+
<a href="https://embedresponsively.com/" target="_blank" rel="noopener">Embed Responsively</a> &middot;
272+
<a href="https://chatgpt.com/g/g-6821f526808c81918e50e06207c3f359-image-alt-writer-2" target="_blank" rel="noopener">Image Alt Writer 2</a>
273+
<!-- SUGGESTED_TOOLS_END -->
271274
</p>
272275
<p class="footer-note">
273276
All processing happens in your browser — nothing is sent to a server.

index.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,14 +281,17 @@ <h2 class="tool-name">HTML Formatter &amp; Tidy</h2>
281281

282282
</div>
283283

284+
<!-- SUGGESTED_TOOLS_START -->
284285
<section class="suggested-tools">
285286
<h2 class="section-heading">Suggested Tools</h2>
286287
<ul class="suggested-list">
287288
<li><a href="https://www.castfeedvalidator.com/" target="_blank" rel="noopener">Cast Feed Validator <span class="external-icon" aria-hidden="true">&#8599;</span></a></li>
288289
<li><a href="https://validator.w3.org/feed/" target="_blank" rel="noopener">W3C Feed Validator <span class="external-icon" aria-hidden="true">&#8599;</span></a></li>
289290
<li><a href="https://embedresponsively.com/" target="_blank" rel="noopener">Embed Responsively <span class="external-icon" aria-hidden="true">&#8599;</span></a></li>
291+
<li><a href="https://chatgpt.com/g/g-6821f526808c81918e50e06207c3f359-image-alt-writer-2" target="_blank" rel="noopener">Image Alt Writer 2 <span class="external-icon" aria-hidden="true">&#8599;</span></a></li>
290292
</ul>
291293
</section>
294+
<!-- SUGGESTED_TOOLS_END -->
292295
</main>
293296

294297
<footer>

og-image/index.html

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,10 @@ <h2>Suggested Meta Tags</h2>
252252
<strong>Related tools:</strong>
253253
<a href="../formatter/">HTML Formatter</a> ·
254254
<a href="../">All Tools</a> ·
255-
<a href="https://embedresponsively.com/" target="_blank" rel="noopener">Embed Responsively</a>
255+
<!-- SUGGESTED_TOOLS_START -->
256+
<a href="https://embedresponsively.com/" target="_blank" rel="noopener">Embed Responsively</a> &middot;
257+
<a href="https://chatgpt.com/g/g-6821f526808c81918e50e06207c3f359-image-alt-writer-2" target="_blank" rel="noopener">Image Alt Writer 2</a>
258+
<!-- SUGGESTED_TOOLS_END -->
256259
</p>
257260
<p>
258261
<strong>Platform debug tools:</strong>

scripts/build-suggested-tools.js

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
#!/usr/bin/env node
2+
/**
3+
* Reads suggested-tools.md and injects generated HTML into each page.
4+
*
5+
* Each page must contain marker comments:
6+
* <!-- SUGGESTED_TOOLS_START --> ... <!-- SUGGESTED_TOOLS_END -->
7+
*
8+
* The home page gets a standalone <section>, while tool sub-pages get
9+
* inline links appended to their existing "Related tools:" footer line.
10+
*/
11+
12+
const fs = require('fs');
13+
const path = require('path');
14+
15+
const ROOT = path.resolve(__dirname, '..');
16+
const MD_PATH = path.join(ROOT, 'suggested-tools.md');
17+
18+
// Map section names in the markdown to their HTML file paths and render mode.
19+
const PAGE_MAP = {
20+
home: { file: 'index.html', mode: 'section' },
21+
formatter: { file: 'formatter/index.html', mode: 'inline' },
22+
'og-image': { file: 'og-image/index.html', mode: 'inline' },
23+
};
24+
25+
// ---------------------------------------------------------------------------
26+
// Parse the markdown
27+
// ---------------------------------------------------------------------------
28+
function parseMarkdown(src) {
29+
const sections = {};
30+
let current = null;
31+
32+
for (const raw of src.split('\n')) {
33+
const line = raw.trim();
34+
35+
// H2 heading = page key
36+
const h2 = line.match(/^##\s+(.+)$/);
37+
if (h2) {
38+
current = h2[1].trim().toLowerCase();
39+
sections[current] = [];
40+
continue;
41+
}
42+
43+
// Markdown link inside a list item
44+
if (current && /^-\s+\[/.test(line)) {
45+
const match = line.match(/\[([^\]]+)\]\(([^)]+)\)/);
46+
if (match) {
47+
sections[current].push({ name: match[1], url: match[2] });
48+
}
49+
}
50+
}
51+
52+
return sections;
53+
}
54+
55+
// ---------------------------------------------------------------------------
56+
// Render HTML for each mode
57+
// ---------------------------------------------------------------------------
58+
function renderSection(tools) {
59+
if (tools.length === 0) return '';
60+
const items = tools
61+
.map(t =>
62+
` <li><a href="${t.url}" target="_blank" rel="noopener">${t.name} <span class="external-icon" aria-hidden="true">&#8599;</span></a></li>`
63+
)
64+
.join('\n');
65+
return [
66+
' <section class="suggested-tools">',
67+
' <h2 class="section-heading">Suggested Tools</h2>',
68+
' <ul class="suggested-list">',
69+
items,
70+
' </ul>',
71+
' </section>',
72+
].join('\n');
73+
}
74+
75+
function renderInline(tools) {
76+
if (tools.length === 0) return '';
77+
return tools
78+
.map(t =>
79+
` <a href="${t.url}" target="_blank" rel="noopener">${t.name}</a>`
80+
)
81+
.join(' &middot;\n');
82+
}
83+
84+
// ---------------------------------------------------------------------------
85+
// Inject between markers
86+
// ---------------------------------------------------------------------------
87+
const START = '<!-- SUGGESTED_TOOLS_START -->';
88+
const END = '<!-- SUGGESTED_TOOLS_END -->';
89+
90+
function inject(html, rendered) {
91+
const startIdx = html.indexOf(START);
92+
const endIdx = html.indexOf(END);
93+
if (startIdx === -1 || endIdx === -1) {
94+
return null; // markers not found
95+
}
96+
return (
97+
html.slice(0, startIdx + START.length) +
98+
'\n' + rendered + '\n' +
99+
html.slice(endIdx)
100+
);
101+
}
102+
103+
// ---------------------------------------------------------------------------
104+
// Main
105+
// ---------------------------------------------------------------------------
106+
const md = fs.readFileSync(MD_PATH, 'utf-8');
107+
const sections = parseMarkdown(md);
108+
109+
let changed = 0;
110+
111+
for (const [key, config] of Object.entries(PAGE_MAP)) {
112+
const tools = sections[key];
113+
if (!tools || tools.length === 0) {
114+
console.log(` skip ${config.file} (no tools listed under "${key}")`);
115+
continue;
116+
}
117+
118+
const filePath = path.join(ROOT, config.file);
119+
const html = fs.readFileSync(filePath, 'utf-8');
120+
121+
const rendered = config.mode === 'section'
122+
? renderSection(tools)
123+
: renderInline(tools);
124+
125+
const result = inject(html, rendered);
126+
if (result === null) {
127+
console.error(` ERROR ${config.file}: missing ${START} / ${END} markers`);
128+
process.exit(1);
129+
}
130+
131+
fs.writeFileSync(filePath, result, 'utf-8');
132+
console.log(` wrote ${config.file} (${tools.length} tools)`);
133+
changed++;
134+
}
135+
136+
console.log(`\nDone — updated ${changed} file(s).`);

suggested-tools.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Suggested Tools
2+
3+
External tools listed here are automatically added to the "Suggested Tools"
4+
sections across the site. Run `node scripts/build-suggested-tools.js` or
5+
push to trigger the GitHub Action to rebuild.
6+
7+
Each `##` section maps to a page (`home`, `formatter`, `og-image`).
8+
Add a standard markdown link to include a tool on that page.
9+
10+
## home
11+
12+
- [Cast Feed Validator](https://www.castfeedvalidator.com/)
13+
- [W3C Feed Validator](https://validator.w3.org/feed/)
14+
- [Embed Responsively](https://embedresponsively.com/)
15+
- [Image Alt Writer 2](https://chatgpt.com/g/g-6821f526808c81918e50e06207c3f359-image-alt-writer-2)
16+
17+
## formatter
18+
19+
- [W3C Feed Validator](https://validator.w3.org/feed/)
20+
- [Cast Feed Validator](https://www.castfeedvalidator.com/)
21+
- [Embed Responsively](https://embedresponsively.com/)
22+
- [Image Alt Writer 2](https://chatgpt.com/g/g-6821f526808c81918e50e06207c3f359-image-alt-writer-2)
23+
24+
## og-image
25+
26+
- [Embed Responsively](https://embedresponsively.com/)
27+
- [Image Alt Writer 2](https://chatgpt.com/g/g-6821f526808c81918e50e06207c3f359-image-alt-writer-2)

0 commit comments

Comments
 (0)