Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .github/workflows/build-suggested-tools.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Build suggested tools

on:
push:
paths:
- 'suggested-tools.md'
workflow_dispatch:

permissions:
contents: write

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Build suggested tools HTML
run: node scripts/build-suggested-tools.js

- name: Commit changes (if any)
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git diff --quiet && exit 0
git add index.html formatter/index.html og-image/index.html
git commit -m "Rebuild suggested tools from suggested-tools.md"
git push
5 changes: 4 additions & 1 deletion formatter/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -265,9 +265,12 @@ <h2>Live Preview</h2>
<a href="../" rel="noopener">All Tools</a> &middot;
<a href="https://validator.w3.org/" target="_blank" rel="noopener">W3C Validator</a> &middot;
<a href="https://caniuse.com/" target="_blank" rel="noopener">Can I Use</a> &middot;
<!-- SUGGESTED_TOOLS_START -->
<a href="https://validator.w3.org/feed/" target="_blank" rel="noopener">W3C Feed Validator</a> &middot;
<a href="https://www.castfeedvalidator.com/" target="_blank" rel="noopener">Cast Feed Validator</a> &middot;
<a href="https://embedresponsively.com/" target="_blank" rel="noopener">Embed Responsively</a>
<a href="https://embedresponsively.com/" target="_blank" rel="noopener">Embed Responsively</a> &middot;
<a href="https://chatgpt.com/g/g-6821f526808c81918e50e06207c3f359-image-alt-writer-2" target="_blank" rel="noopener">Image Alt Writer 2</a>
<!-- SUGGESTED_TOOLS_END -->
</p>
<p class="footer-note">
All processing happens in your browser — nothing is sent to a server.
Expand Down
3 changes: 3 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -310,14 +310,17 @@ <h2 class="tool-name">HTML Formatter &amp; Tidy</h2>

</div>

<!-- SUGGESTED_TOOLS_START -->
<section class="suggested-tools">
<h2 class="section-heading">Suggested Tools</h2>
<ul class="suggested-list">
<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>
<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>
<li><a href="https://embedresponsively.com/" target="_blank" rel="noopener">Embed Responsively <span class="external-icon" aria-hidden="true">&#8599;</span></a></li>
<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>
</ul>
</section>
<!-- SUGGESTED_TOOLS_END -->
</main>

<footer>
Expand Down
5 changes: 4 additions & 1 deletion og-image/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,10 @@ <h2>Suggested Meta Tags</h2>
<strong>Related tools:</strong>
<a href="../formatter/">HTML Formatter</a> ·
<a href="../">All Tools</a> ·
<a href="https://embedresponsively.com/" target="_blank" rel="noopener">Embed Responsively</a>
<!-- SUGGESTED_TOOLS_START -->
<a href="https://embedresponsively.com/" target="_blank" rel="noopener">Embed Responsively</a> &middot;
<a href="https://chatgpt.com/g/g-6821f526808c81918e50e06207c3f359-image-alt-writer-2" target="_blank" rel="noopener">Image Alt Writer 2</a>
<!-- SUGGESTED_TOOLS_END -->
</p>
<p>
<strong>Platform debug tools:</strong>
Expand Down
136 changes: 136 additions & 0 deletions scripts/build-suggested-tools.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
#!/usr/bin/env node
/**
* Reads suggested-tools.md and injects generated HTML into each page.
*
* Each page must contain marker comments:
* <!-- SUGGESTED_TOOLS_START --> ... <!-- SUGGESTED_TOOLS_END -->
*
* The home page gets a standalone <section>, while tool sub-pages get
* inline links appended to their existing "Related tools:" footer line.
*/

const fs = require('fs');
const path = require('path');

const ROOT = path.resolve(__dirname, '..');
const MD_PATH = path.join(ROOT, 'suggested-tools.md');

// Map section names in the markdown to their HTML file paths and render mode.
const PAGE_MAP = {
home: { file: 'index.html', mode: 'section' },
formatter: { file: 'formatter/index.html', mode: 'inline' },
'og-image': { file: 'og-image/index.html', mode: 'inline' },
};

// ---------------------------------------------------------------------------
// Parse the markdown
// ---------------------------------------------------------------------------
function parseMarkdown(src) {
const sections = {};
let current = null;

for (const raw of src.split('\n')) {
const line = raw.trim();

// H2 heading = page key
const h2 = line.match(/^##\s+(.+)$/);
if (h2) {
current = h2[1].trim().toLowerCase();
sections[current] = [];
continue;
}

// Markdown link inside a list item
if (current && /^-\s+\[/.test(line)) {
const match = line.match(/\[([^\]]+)\]\(([^)]+)\)/);
if (match) {
sections[current].push({ name: match[1], url: match[2] });
}
}
}

return sections;
}

// ---------------------------------------------------------------------------
// Render HTML for each mode
// ---------------------------------------------------------------------------
function renderSection(tools) {
if (tools.length === 0) return '';
const items = tools
.map(t =>
` <li><a href="${t.url}" target="_blank" rel="noopener">${t.name} <span class="external-icon" aria-hidden="true">&#8599;</span></a></li>`
)
.join('\n');
return [
' <section class="suggested-tools">',
' <h2 class="section-heading">Suggested Tools</h2>',
' <ul class="suggested-list">',
items,
' </ul>',
' </section>',
].join('\n');
}

function renderInline(tools) {
if (tools.length === 0) return '';
return tools
.map(t =>
` <a href="${t.url}" target="_blank" rel="noopener">${t.name}</a>`
)
.join(' &middot;\n');
}

// ---------------------------------------------------------------------------
// Inject between markers
// ---------------------------------------------------------------------------
const START = '<!-- SUGGESTED_TOOLS_START -->';
const END = '<!-- SUGGESTED_TOOLS_END -->';

function inject(html, rendered) {
const startIdx = html.indexOf(START);
const endIdx = html.indexOf(END);
if (startIdx === -1 || endIdx === -1) {
return null; // markers not found
}
return (
html.slice(0, startIdx + START.length) +
'\n' + rendered + '\n' +
html.slice(endIdx)
);
}

// ---------------------------------------------------------------------------
// Main
// ---------------------------------------------------------------------------
const md = fs.readFileSync(MD_PATH, 'utf-8');
const sections = parseMarkdown(md);

let changed = 0;

for (const [key, config] of Object.entries(PAGE_MAP)) {
const tools = sections[key];
if (!tools || tools.length === 0) {
console.log(` skip ${config.file} (no tools listed under "${key}")`);
continue;
}

const filePath = path.join(ROOT, config.file);
const html = fs.readFileSync(filePath, 'utf-8');

const rendered = config.mode === 'section'
? renderSection(tools)
: renderInline(tools);

const result = inject(html, rendered);
if (result === null) {
console.error(` ERROR ${config.file}: missing ${START} / ${END} markers`);
process.exit(1);
}

fs.writeFileSync(filePath, result, 'utf-8');
console.log(` wrote ${config.file} (${tools.length} tools)`);
changed++;
}

console.log(`\nDone — updated ${changed} file(s).`);
27 changes: 27 additions & 0 deletions suggested-tools.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Suggested Tools

External tools listed here are automatically added to the "Suggested Tools"
sections across the site. Run `node scripts/build-suggested-tools.js` or
push to trigger the GitHub Action to rebuild.

Each `##` section maps to a page (`home`, `formatter`, `og-image`).
Add a standard markdown link to include a tool on that page.

## home

- [Cast Feed Validator](https://www.castfeedvalidator.com/)
- [W3C Feed Validator](https://validator.w3.org/feed/)
- [Embed Responsively](https://embedresponsively.com/)
- [Image Alt Writer 2](https://chatgpt.com/g/g-6821f526808c81918e50e06207c3f359-image-alt-writer-2)

## formatter

- [W3C Feed Validator](https://validator.w3.org/feed/)
- [Cast Feed Validator](https://www.castfeedvalidator.com/)
- [Embed Responsively](https://embedresponsively.com/)
- [Image Alt Writer 2](https://chatgpt.com/g/g-6821f526808c81918e50e06207c3f359-image-alt-writer-2)

## og-image

- [Embed Responsively](https://embedresponsively.com/)
- [Image Alt Writer 2](https://chatgpt.com/g/g-6821f526808c81918e50e06207c3f359-image-alt-writer-2)