Skip to content

Commit ff90514

Browse files
author
TechStack Global
committed
feat(seo): add cluster identity signal to hardware series pages
1 parent 34f73ee commit ff90514

9 files changed

+123
-0
lines changed

add_cluster_signal.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import os
2+
import re
3+
4+
files_to_update = [
5+
"posts/best-remote-work-setup-2026.html",
6+
"posts/budget-laptops-under-1000.html",
7+
"posts/best-laptops-for-students-2026.html",
8+
"posts/best-premium-laptop-for-work-2026.html",
9+
"posts/do-you-need-thunderbolt-dock.html",
10+
"posts/is-a-4k-monitor-worth-it.html",
11+
"posts/is-samsung-990-pro-worth-it.html",
12+
]
13+
14+
cluster_signal = '\n<p class="cluster-signal" style="font-style: italic; font-size: 0.92rem; color: #a1a1aa; margin-top: -0.5rem; margin-bottom: 1.5rem;">This guide is part of our <a href="/posts/best-remote-work-setup-2026.html" style="color: var(--accent); text-decoration: underline;">Remote Work Hardware Series</a>.</p>'
15+
16+
for file_path in files_to_update:
17+
if not os.path.exists(file_path):
18+
print(f"Missing: {file_path}")
19+
continue
20+
21+
with open(file_path, "r", encoding="utf-8") as f:
22+
content = f.read()
23+
24+
if "cluster-signal" in content:
25+
print(f"Already injected in: {file_path}")
26+
continue
27+
28+
# Regex to find <p class="post-meta">...</p> and insert right after it.
29+
# We account for the fact that some pages might not have it exactly matching.
30+
# We can also look for <span class="badge">...</span> and <h1 class="post-title">...</h1>
31+
32+
# We will look for <p class="post-meta">....</p>
33+
pattern = r'(<p class="post-meta">.*?</p>)'
34+
35+
# In budget-laptops-under-1000.html, there's no post-meta!
36+
# Let's check where to inject if post-meta is missing.
37+
if re.search(pattern, content):
38+
new_content = re.sub(pattern, r'\1' + cluster_signal, content, count=1)
39+
else:
40+
# If no post-meta, insert after post-title
41+
pattern_title = r'(<h1 class="post-title">.*?</h1>)'
42+
new_content = re.sub(pattern_title, r'\1' + cluster_signal, content, count=1)
43+
44+
if new_content != content:
45+
with open(file_path, "w", encoding="utf-8") as f:
46+
f.write(new_content)
47+
print(f"Injected in: {file_path}")
48+
else:
49+
print(f"Failed to inject in: {file_path}")

crawl_depth_test.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import os
2+
from bs4 import BeautifulSoup
3+
from collections import deque
4+
import urllib.parse
5+
6+
def clean_link(link):
7+
if not link:
8+
return None
9+
link = link.split('#')[0]
10+
link = link.split('?')[0]
11+
if link.startswith('http') or link.startswith('mailto:') or link.startswith('tel:'):
12+
return None
13+
return link
14+
15+
def build_graph(root_dir='.'):
16+
graph = {}
17+
for root, dirs, files in os.walk(root_dir):
18+
if '.git' in root or '.venv' in root:
19+
continue
20+
for file in files:
21+
if file.endswith('.html'):
22+
abs_path = os.path.abspath(os.path.join(root, file))
23+
rel_path = os.path.relpath(abs_path, root_dir).replace('\\', '/')
24+
graph[rel_path] = []
25+
with open(abs_path, 'r', encoding='utf-8') as f:
26+
soup = BeautifulSoup(f.read(), 'html.parser')
27+
for a in soup.find_all('a', href=True):
28+
link = clean_link(a['href'])
29+
if not link:
30+
continue
31+
target_abs = os.path.normpath(os.path.join(root, link))
32+
target_rel = os.path.relpath(target_abs, root_dir).replace('\\', '/')
33+
if os.path.exists(target_abs) and target_rel.endswith('.html'):
34+
graph[rel_path].append(target_rel)
35+
return graph
36+
37+
def check_depth(graph, start='index.html'):
38+
if start not in graph:
39+
print(f"Start node {start} not found.")
40+
return
41+
42+
distances = {start: 0}
43+
queue = deque([start])
44+
45+
while queue:
46+
current = queue.popleft()
47+
for neighbor in graph.get(current, []):
48+
if neighbor not in distances:
49+
distances[neighbor] = distances[current] + 1
50+
queue.append(neighbor)
51+
52+
unreachable = [node for node in graph if node not in distances]
53+
54+
print("=== CRAWL DEPTH REPORT (FROM index.html) ===")
55+
for node, dist in sorted(distances.items(), key=lambda x: x[1]):
56+
print(f"Depth {dist}: {node}")
57+
58+
print("\n=== UNREACHABLE (ORPHAN) HTML PAGES ===")
59+
if unreachable:
60+
for u in unreachable:
61+
print(f"- {u}")
62+
else:
63+
print("None! All HTML pages are reachable.")
64+
65+
if __name__ == '__main__':
66+
graph = build_graph()
67+
check_depth(graph)

posts/best-laptops-for-students-2026.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
</div>
3838
<span class="badge">Student Tech Hub</span>
3939
<h1 class="post-title">Best Laptops for Students 2026: From Budget to Premium</h1>
40+
<p class="cluster-signal" style="font-style: italic; font-size: 0.92rem; color: #a1a1aa; margin-top: -0.5rem; margin-bottom: 1.5rem;">This guide is part of our <a href="/posts/best-remote-work-setup-2026.html" style="color: var(--accent); text-decoration: underline;">Remote Work Hardware Series</a>.</p>
4041
<div class="tldr-verdict glass-card" style="padding: 1.5rem; border-left: 4px solid var( accent); background: rgba(0,255,157,0.05); margin-bottom: 2rem;">
4142
<strong>Quick Summary:</strong> Choosing a student laptop in 2026 is about balancing 12+ hour battery
4243
life with the ability to handle AI-integrated coursework. For most students, the <strong>MacBook Air

posts/best-premium-laptop-for-work-2026.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ <h1 class="post-title">Best Premium Laptop for Work in 2026: MacBook vs Dell vs
8585
</p>
8686

8787
<p class="post-meta">Published on <span id="date">February 27, 2026</span> • 7 min read</p>
88+
<p class="cluster-signal" style="font-style: italic; font-size: 0.92rem; color: #a1a1aa; margin-top: -0.5rem; margin-bottom: 1.5rem;">This guide is part of our <a href="/posts/best-remote-work-setup-2026.html" style="color: var(--accent); text-decoration: underline;">Remote Work Hardware Series</a>.</p>
8889
<p>Choosing a reliable laptop used to mean picking whatever felt sturdy and had a decent enough keyboard.
8990
But today, the demands of remote work, software development, and digital creation require a machine that
9091
doesn't just survive the workday it actively enhances your workflow.</p>

posts/best-remote-work-setup-2026.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
<span class="badge">Ecosystem Hub</span>
7676
<h1 class="post-title">The Best Remote Work Setup Formula for 2026</h1>
7777
<p class="post-meta">Published on <span id="date">February 27, 2026</span></p>
78+
<p class="cluster-signal" style="font-style: italic; font-size: 0.92rem; color: #a1a1aa; margin-top: -0.5rem; margin-bottom: 1.5rem;">This guide is part of our <a href="/posts/best-remote-work-setup-2026.html" style="color: var(--accent); text-decoration: underline;">Remote Work Hardware Series</a>.</p>
7879
<p>Welcome to the ultimate guide for building a seamless, hyper-productive home office. You don’t need
7980
dozens of gimmicky gadgets to be productive. You need a concise, powerful core stack that "just works."
8081
</p>

posts/budget-laptops-under-1000.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
</div>
5454
<span class="badge">Budget Picks 2026</span>
5555
<h1 class="post-title">Best Budget Laptops Under $1000: Real Value for Real Work</h1>
56+
<p class="cluster-signal" style="font-style: italic; font-size: 0.92rem; color: #a1a1aa; margin-top: -0.5rem; margin-bottom: 1.5rem;">This guide is part of our <a href="/posts/best-remote-work-setup-2026.html" style="color: var(--accent); text-decoration: underline;">Remote Work Hardware Series</a>.</p>
5657
<div class="tldr-verdict glass-card"
5758
style="padding: 1.5rem; border-left: 4px solid var( accent); background: rgba(0,255,157,0.05); margin-bottom: 2rem;">
5859
<strong>TL;DR:</strong> You don't need to spend $2,000 for a pro-level experience. The <strong>MacBook

posts/do-you-need-thunderbolt-dock.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
<span class="badge">Buying Guide</span>
8787
<h1 class="post-title">Do You Really Need a Thunderbolt Dock for Remote Work?</h1>
8888
<p class="post-meta">Published on <span id="date">February 27, 2026</span> • 6 min read</p>
89+
<p class="cluster-signal" style="font-style: italic; font-size: 0.92rem; color: #a1a1aa; margin-top: -0.5rem; margin-bottom: 1.5rem;">This guide is part of our <a href="/posts/best-remote-work-setup-2026.html" style="color: var(--accent); text-decoration: underline;">Remote Work Hardware Series</a>.</p>
8990

9091
<!-- Internal: top related links -->
9192
<p class="internal-top-links" style="margin-top:0.8rem;font-size:0.95rem;">

posts/is-a-4k-monitor-worth-it.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ <h1 class="post-title">Is a 4K Monitor Worth It for Remote Work in 2026?</h1>
7878
</p>
7979

8080
<p class="post-meta">Published on <span id="date">February 27, 2026</span> • 6 min read</p>
81+
<p class="cluster-signal" style="font-style: italic; font-size: 0.92rem; color: #a1a1aa; margin-top: -0.5rem; margin-bottom: 1.5rem;">This guide is part of our <a href="/posts/best-remote-work-setup-2026.html" style="color: var(--accent); text-decoration: underline;">Remote Work Hardware Series</a>.</p>
8182
<p>If you've spent the last few years hunching over a 14-inch laptop screen, or squinting at a decade-old
8283
1080p monitor, you've probably felt the daily fatigue setting in. You might be considering an upgrade as
8384
part of your <a href="best-remote-work-setup-2026.html" style="color:var( accent)">Remote Work

posts/is-samsung-990-pro-worth-it.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ <h1 class="post-title">Is Samsung 990 PRO Worth It? Gen4 SSD vs Standard NVMe Ex
9696
</p>
9797

9898
<p class="post-meta">Published on <span id="date">February 27, 2026</span> • 5 min read</p>
99+
<p class="cluster-signal" style="font-style: italic; font-size: 0.92rem; color: #a1a1aa; margin-top: -0.5rem; margin-bottom: 1.5rem;">This guide is part of our <a href="/posts/best-remote-work-setup-2026.html" style="color: var(--accent); text-decoration: underline;">Remote Work Hardware Series</a>.</p>
99100
<p>If you've been looking to upgrade your computer’s storage, you’ve probably stared at the <a
100101
href="samsung-990-pro-ssd-review.html" style="color:var( accent)">Samsung 990 PRO</a> and wondered:
101102
"Is it really worth paying a premium for extreme speeds?"</p>

0 commit comments

Comments
 (0)