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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ Releases: push a `v*` tag matching package.json; CI builds
`site/index.html` is the hqterm.sh landing page and `site/docs.html` is
https://hqterm.sh/docs; `install.sh` is served at https://hqterm.sh/install and
`install.ps1` at https://hqterm.sh/install.ps1. Publish them with
`scripts/deploy-site.sh`, which bakes the pit.world network footer into every
page and rsyncs the lot to dev2's `/var/www/hqterm.sh`.
`scripts/deploy-site.sh`, which rsyncs the pages and their shared Profullstack
footer to dev2's `/var/www/hqterm.sh`.

MIT.
24 changes: 4 additions & 20 deletions scripts/deploy-site.sh
Original file line number Diff line number Diff line change
@@ -1,38 +1,22 @@
#!/bin/sh
# Publish hqterm.sh: site/ plus the two installers, with the pit.world network
# footer baked in as the last thing in <body> of every page.
# Publish hqterm.sh: site/ plus the two installers. Each HTML page carries the
# shared Profullstack footer and refreshes it from the published template.
#
# scripts/deploy-site.sh build dist/site and rsync it to dev2
# scripts/deploy-site.sh --build build dist/site only
#
# hqterm.sh is plain files behind nginx (no server code), so the footer is
# fetched here, at deploy time, not per request. If pit.world is slow (3 s) or
# down, the pages ship without it rather than failing the deploy.
# hqterm.sh is plain files behind nginx (no server code); its pages include a
# server-rendered fallback and load the shared template in the browser.
set -eu
cd "$(dirname "$0")/.."

OUT=dist/site
DEST=${DEST:-anthony@dev2.profullstack.com:/var/www/hqterm.sh/}
PIT_URL='https://pit.world/snippets/footer.html?site=hqterm.sh'

rm -rf "$OUT"
mkdir -p "$OUT"
cp -R site/. "$OUT/"
cp install.sh install.ps1 "$OUT/"

PIT=$(curl -fsS -m 3 "$PIT_URL" 2>/dev/null || true)
case "$PIT" in
*data-pit-footer*)
export PIT
for f in "$OUT"/*.html; do
# Before the LAST </body> only.
perl -0pi -e 's{</body>(?![\s\S]*</body>)}{$ENV{PIT}\n</body>}' "$f"
done
echo "pit footer baked into $(ls "$OUT"/*.html | wc -l) page(s)"
;;
*) echo "pit footer unavailable; publishing without it" >&2 ;;
esac

[ "${1:-}" = "--build" ] && exit 0
# Explicit modes: a restrictive local umask (007) would otherwise publish
# 0770/0660 files that nginx cannot read, and every page 500s.
Expand Down
53 changes: 53 additions & 0 deletions site/docs.html
Original file line number Diff line number Diff line change
Expand Up @@ -240,5 +240,58 @@ <h2 id="uninstall">Update and uninstall</h2>
<span class="c"># Windows: Remove-Item ~\.local\bin\hqsh.exe, then remove that folder from your user PATH</span></code></pre>
</main>
</div>
<div data-profullstack-footer data-site="https://hqterm.sh/" data-since="2026" data-tagline="MIT" data-links='[{"label":"Home","href":"/"},{"label":"GitHub","href":"https://github.com/profullstack/hqterm"},{"label":"hqsh","href":"https://github.com/profullstack/hqsh"}]' data-badges='[{"src":"https://logicsrc.com/openicon/set/hq/svg/built-indie.svg","href":"https://built.indie.pit.world/for?site=https://hqterm.sh/","alt":"Find this site on built.indie","width":32,"height":32},{"src":"https://logicsrc.com/openicon/set/hq/svg/pwamart.svg","href":"https://pwamart.com/for?site=https://hqterm.sh/","alt":"Find this site on PwaMart","width":32,"height":32}]'><footer class="pfs-footer" data-pfs-footer="0.6.10"><style>.pfs-footer{--pfs-footer-muted:color-mix(in srgb,currentColor 62%,transparent);--pfs-footer-gap:.5rem 1.25rem;--pfs-footer-size:.875rem;border-top:1px solid color-mix(in srgb,currentColor 14%,transparent);font-size:var(--pfs-footer-size);line-height:1.5;color:inherit}.pfs-footer__inner{max-width:72rem;margin:0 auto;padding:.85rem 1rem;display:flex;flex-direction:column;align-items:center;gap:.4rem;text-align:center}.pfs-footer a{color:var(--pfs-footer-muted);text-decoration:none}.pfs-footer a:hover,.pfs-footer a:focus-visible{color:inherit;text-decoration:underline}.pfs-footer__links{display:flex;flex-wrap:wrap;justify-content:center;gap:.35rem 1rem}.pfs-footer__ring{display:flex;flex-wrap:wrap;justify-content:center;align-items:center;gap:.4rem}.pfs-footer__badges{display:flex;flex-wrap:wrap;justify-content:center;align-items:center;gap:.4rem}.pfs-footer a.pfs-footer__badge{display:inline-block;line-height:0}.pfs-footer__badge img{display:block;width:200px;height:44px;max-width:100%}.pfs-footer__badge--site img{width:auto;height:auto}.pfs-footer__badge--publisher img{width:32px;height:32px;max-width:none;object-fit:contain}.pfs-footer__badge--square img{width:32px;height:32px;max-width:none;object-fit:contain}.pfs-footer__news{display:flex;justify-content:center}.pfs-footer__sub{display:flex;align-items:center;gap:.35rem;margin:0;flex-wrap:wrap;justify-content:center}.pfs-footer__sub input[type=email]{font:inherit;font-size:.8125rem;color:inherit;background:transparent;width:13.5rem;max-width:62vw;padding:.2rem .5rem;border:1px solid color-mix(in srgb,currentColor 25%,transparent);border-radius:.35rem}.pfs-footer__sub input[type=email]::placeholder{color:var(--pfs-footer-muted);opacity:1}.pfs-footer__sub input[type=email]:focus-visible{outline:2px solid color-mix(in srgb,currentColor 45%,transparent);outline-offset:1px}.pfs-footer__sub button{font:inherit;font-size:.8125rem;color:var(--pfs-footer-muted);background:transparent;padding:.2rem .6rem;border:1px solid color-mix(in srgb,currentColor 25%,transparent);border-radius:.35rem;cursor:pointer}.pfs-footer__sub button:hover,.pfs-footer__sub button:focus-visible{color:inherit}.pfs-footer__hp{position:absolute!important;left:-9999px!important;width:1px;height:1px;opacity:0}.pfs-footer__sub+.pfs-footer__sublink{display:none}.pfs-footer__msg{flex-basis:100%;text-align:center;font-size:.8125rem;color:var(--pfs-footer-muted)}.pfs-footer__msg:empty{display:none}.pfs-footer__msg[data-state=ok]{color:inherit}.pfs-footer__msg[data-state=error]{color:#d92d20;color:light-dark(#b42318,#f97066)}.pfs-footer__copy{margin:0;color:var(--pfs-footer-muted)}.pfs-footer__copy a{color:inherit}.pfs-footer__tagline{display:inline-block;white-space:nowrap}.pfs-footer__brand{display:inline-flex;align-items:center;justify-content:center;vertical-align:middle;margin-inline:.35rem;padding:2px 4px;border-radius:3px;background:#202124;line-height:0}.pfs-footer__brand img{display:block;width:20px;height:11px;max-width:none}</style><div class="pfs-footer__inner"><nav class="pfs-footer__links" aria-label="Footer"><a href="/">Home</a><a href="https://github.com/profullstack/hqterm">GitHub</a><a href="https://github.com/profullstack/hqsh">hqsh</a><a href="https://pit.world/pro">Find a pro</a><a href="https://profullstack.com/terms">Terms</a><a href="https://profullstack.com/privacy">Privacy</a><a href="https://profullstack.com/media">Media</a><a href="https://github.com/profullstack" rel="noopener noreferrer">GitHub</a><a href="https://discord.gg/U7dEXfBA3s" rel="noopener noreferrer">Discord</a><a href="https://x.com/profullstackinc" rel="noopener noreferrer">X</a><a href="https://luma.com/profullstack" rel="noopener noreferrer">Events</a><a href="https://luma.com/mergenlaunch" rel="noopener noreferrer">Register for Event</a><a href="https://pairux.com/@profullstack" rel="noopener noreferrer">PairUX</a><a href="https://pairux.com/@moshcoding" rel="noopener noreferrer">Moshcoding</a></nav><nav class="pfs-footer__links" aria-label="Profullstack products"><a href="https://profullstack.com/stack/build">Agentic site builder</a><a href="https://crawlproof.com/surveys">CrawlProof Surveys</a></nav><nav class="webring pfs-footer__ring" aria-label="Profullstack webring"><a href="https://rssamplifier.com/ring/profullstack/previous?from=https%3A%2F%2Fhqterm.sh%2F" rel="prev" title="Previous site">&lt;&lt;</a><a href="https://rssamplifier.com/ring/profullstack">Profullstack</a><a href="https://rssamplifier.com/ring/profullstack/next?from=https%3A%2F%2Fhqterm.sh%2F" rel="next" title="Next site">&gt;&gt;</a></nav><div class="pfs-footer__news"><form class="pfs-footer__sub" action="https://pit.world/subscribe" method="post" autocomplete="on"><input type="hidden" name="site" value="hqterm.sh"><label for="pfs-footer-email">Email</label><input id="pfs-footer-email" type="email" name="email" required maxlength="254" autocomplete="email" placeholder="Email for our newsletter" aria-label="Email address for the Profullstack newsletter"><input class="pfs-footer__hp" name="website" type="text" tabindex="-1" autocomplete="off" aria-hidden="true"><button type="submit">Subscribe</button><span class="pfs-footer__msg" role="status" aria-live="polite"></span><script>(function wireSubscribe(form) {
if (!form || form.getAttribute('data-pfs-wired')) return;
form.setAttribute('data-pfs-wired', '1');
var msg = form.querySelector('.pfs-footer__msg');
var button = form.querySelector('button');
var reasons = {
invalid: "that doesn't look like an email address",
undeliverable: "that address's domain can't receive email",
failed: 'the confirmation email could not be sent, try again later',
};
function say(text, state) {
if (!msg) return;
msg.textContent = text;
msg.setAttribute('data-state', state);
}
form.addEventListener('submit', function (event) {
if (typeof fetch !== 'function') return; // let the plain POST go
event.preventDefault();
var data = new FormData(form);
var email = String(data.get('email') || '').trim();
// The honeypot: a bot that fills it gets the normal answer and nothing is sent.
if (data.get('website')) return say('Email confirmation sent. Check your inbox.', 'ok');
if (button) button.disabled = true;
say('Sending…', 'busy');
fetch(new URL('/api/subscribe', form.action).href, {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ email: email, site: data.get('site') }),
})
.then(function (res) {
return res
.json()
.catch(function () {
return {};
})
.then(function (body) {
if (res.ok && body.ok) {
say('Email confirmation sent to ' + email + '. Check your inbox.', 'ok');
form.reset();
} else {
say('Failed: ' + (body.error || reasons[body.status] || 'pit.world answered ' + res.status), 'error');
}
});
})
.catch(function (err) {
say('Failed: could not reach pit.world (' + ((err && err.message) || err) + ')', 'error');
})
.then(function () {
if (button) button.disabled = false;
});
});
})(document.currentScript.parentNode)</script></form><a class="pfs-footer__sublink" href="https://pit.world/subscribe?site=https://hqterm.sh/">Newsletter</a></div><div class="pfs-footer__badges"><a class="pfs-footer__badge pfs-footer__badge--publisher" href="https://built.indie.pit.world/for?site=https://hqterm.sh/"><img src="https://logicsrc.com/openicon/set/hq/svg/built-indie.svg" alt="Find this site on built.indie" width="32" height="32" loading="lazy"></a><a class="pfs-footer__badge pfs-footer__badge--publisher" href="https://pwamart.com/for?site=https://hqterm.sh/"><img src="https://logicsrc.com/openicon/set/hq/svg/pwamart.svg" alt="Find this site on PwaMart" width="32" height="32" loading="lazy"></a></div><p class="pfs-footer__copy"><a href="tel:+18885264640">Phone: +1-888-526-4640</a></p><p class="pfs-footer__copy">&copy; 2026 <a class="pfs-footer__brand" href="https://profullstack.com/" title="Profullstack, Inc."><img src="https://profullstack.com/assets/logo.svg" alt="" width="20" height="11" loading="lazy"></a> <a href="https://profullstack.com/">Profullstack, Inc.</a> <span class="pfs-footer__tagline">&middot; MIT</span></p></div></footer></div>
<script type="module" src="https://cdn.jsdelivr.net/npm/@profullstack/footer@latest/src/embed.js"></script>
</body>
</html>
Loading
Loading