Commit Graph
547 Commits
Author SHA1 Message Date
tiennm99 0bf2521918 feat: docs, changelog, expanded examples for v0.1.0 (Phase 8)
- docs/config.md: full params reference (required + optional + per-post
  frontmatter); explains why theme defaults don't deep-merge into
  consumer sites
- docs/data-schemas.md: profile.yaml + projects.yaml field tables
- docs/customization.md: layout overrides, design token override via
  custom CSS, font self-hosting, per-page color overrides, kill switches
- docs/migrating-from-stack.md: submodule swap, drop list, Stack-to-tsuki
  param mapping, frontmatter compat, gotchas (heading id changes break
  inbound anchor links)
- CHANGELOG.md: complete 0.1.0 entry covering all phases
- README.md: drop "under construction"; add full params snippet, search
  + comments + browser support sections, links to all docs
- exampleSite/data/projects.yaml: +tsuki, +penny-pincher (6 total)

Screenshots, miti99 migration, and v0.1.0 tag are user-driven actions
recorded in phase-08 plan.
2026-05-07 22:20:44 +07:00
tiennm99 80dd906827 feat: Pagefind search and Giscus comments (Phase 7)
- search/list.html: Pagefind UI container + module init with full vi
  i18n strings; loads /pagefind/pagefind-ui.{css,js} from generated dir
- search-button.html in header, gated by params.search.enable
- comments.html for Giscus, opt-in via params.comments.giscus.enable;
  reads repo/repoId/category/categoryId/mapping/theme/lang from params
- giscus-theme.js: MutationObserver on data-theme, postMessage iframe
  on toggle so Giscus theme stays in sync with site
- search.css: header-actions row, search button, Pagefind UI tokens
  mapped to tsuki design tokens
- comments.css: min-block-size on iframe to prevent layout shift
- exampleSite/hugo.yaml: commented giscus params block as docs
- i18n/vi.yml: Pagefind UI strings (clear, loadMore, zeroResults...)

Both features no-op when disabled. CSS bundle 3.5 KB gz (budget 15).
2026-05-07 22:03:32 +07:00
tiennm99 49107f6164 feat: visual layer with vi typography, dark mode, view transitions (Phase 6)
- 9 CSS files (tokens, reset, typography, layout, components, home,
  archive, toc, view-transitions) bundled via resources.Concat
- 3 ES module JS files (theme-toggle, code-copy, toc-active); toc-active
  loaded only on long posts
- @view-transition: navigation auto for cross-page morph (CSS-only)
- TOC partial gated by WordCount>400 and Params.toc!=false; sticky on
  wide viewports, framed block on narrow
- render-heading.html adds cosmetic anchor links
- Hugo asset pipeline: resources.Concat | minify | fingerprint (no
  SCSS, no PostCSS, no Node)
- i18n/vi.yml extended with prev/next/posts/archiveEmpty/toggleTheme
- Bundle sizes: CSS 3.3 KB gz (budget 15), JS 0.8 KB gz (budget 8)

Config: github-ascii heading IDs, :contentbasename permalink token for
clean ASCII URLs, pagination.pagerSize migration (Hugo 0.128+).
2026-05-07 21:27:46 +07:00
tiennm99 8a368471f3 feat: posts, taxonomies, archive (Phase 5)
- post/list.html paginated section list with post-card
- pagination.html partial (renders only when >1 page)
- _default/taxonomy.html for term lists (/tags/, /categories/) with counts
- taxonomy/term.html for single term pages (paginated posts)
- archives/list.html + archive-group.html: year/month grouped flat list
- exampleSite: 4 new posts across 2025-2026, 2 categories, 4 tags, archive index

Modern Hugo 0.146+ template naming: term.html resolved via taxonomy/
path (Hugo 0.154 lookup ignores _default/term.html).
2026-05-07 21:10:28 +07:00
tiennm99 a9b858bd9e feat: homepage personal portfolio (Phase 4)
- data/profile.yaml schema: name, handle, tagline, avatar, bio, links
- data/projects.yaml schema: featured projects with title, tagline, repo, demo, image, tags
- _partials/home/hero.html: avatar + name + tagline + bio + social links
- _partials/home/projects.html: data-driven project grid
- _partials/home/recent-posts.html: configurable count, view-all link
- home.html composes hero → projects → recent-posts
- assets/icons: github, mail, rss (Lucide-style)
- exampleSite fixtures: profile, 4 projects, 5 SVG placeholder images
2026-05-07 20:53:06 +07:00
tiennm99 a6b1b6ab55 feat: core layouts and partials (Phase 3)
- baseof.html with named blocks (head_extra, body_class, main, scripts)
- single.html with data-pagefind-body wrapper
- list.html using post-card partial
- home.html placeholder (Phase 4 fills hero/projects/recent-posts)
- 404.html with vi i18n
- _partials: head (full meta + OG + Twitter Card + RSS autodiscovery + theme-flash script), header, nav, footer, meta, post-card, icon
- Sample post fixture in exampleSite to validate render

ci: bump GitHub Actions to Node 24-compatible versions

- actions/checkout v4 → v6
- actions/setup-node v4 → v6, node 20 → 22
- actions/upload-pages-artifact v3 → v5
- actions/deploy-pages v4 → v5
2026-05-07 20:45:31 +07:00
tiennm99 046a1622e6 ci: skip Pagefind step when no HTML pages exist yet 2026-05-07 20:29:33 +07:00
tiennm99 8edfa80acc build: add package-lock.json for npm cache in CI 2026-05-07 20:28:23 +07:00
tiennm99 94500a8e3d feat: initial theme scaffold
- Repo metadata: theme.toml, README, Apache-2.0 license
- Hugo defaults: vi language, post permalinks, taxonomies, paginate, TOC config
- exampleSite skeleton with themesDir wiring
- CI: Hugo build + Pagefind index + Pages deploy
- Pagefind devDependency in package.json (Dependabot weekly)
- vi i18n strings, default archetype
- Empty layouts/_partials/_markup/_shortcodes directories ready for Phase 3
2026-05-07 20:27:07 +07:00
tiennm99 5d36f5966c Initial commit 2026-05-07 20:25:16 +07:00
github-actions[bot] c083129bc9 chore(projects): refresh projects list 2026-10-04 02:34:26 +07:00
tiennm99 93798350bc docs(referral): add Hermes Agent Cloud referral 2026-10-03 09:30:40 +07:00
github-actions[bot] 8eb701f110 chore(projects): refresh projects list 2026-10-03 04:04:47 +07:00
tiennm99 c5efe95cac docs(referral): refresh current deals and offers 2026-10-02 10:45:26 +07:00
tiennm99 d464b5df8e docs(referral): remove ended OpenCode Go referral 2026-10-02 09:57:16 +07:00
tiennm99 b5a80e3ab6 docs(referral): remove BigModel.cn and Claude Code guest pass entries 2026-10-02 09:00:02 +07:00
github-actions[bot] afd1d56ca1 chore(projects): refresh projects list 2026-10-02 04:30:01 +07:00
github-actions[bot] 2550fbe79d chore(projects): refresh projects list 2026-10-01 04:04:46 +07:00
github-actions[bot] 58261a01cb chore(projects): refresh projects list 2026-09-30 17:07:37 +07:00
github-actions[bot] 1a68d064d5 chore(projects): refresh projects list 2026-09-30 16:58:10 +07:00
github-actions[bot] 470b0a143d chore(projects): refresh projects list 2026-09-30 16:37:51 +07:00
github-actions[bot] c1cb997311 chore(projects): refresh projects list 2026-09-30 16:22:09 +07:00
github-actions[bot] f03bb0f287 chore(projects): refresh projects list 2026-09-30 04:07:31 +07:00
github-actions[bot] b1174df458 chore(projects): refresh projects list 2026-09-29 05:18:57 +07:00
tiennm99 318098d384 docs(newsletter): restore original source titles on article headings 2026-09-28 09:33:15 +07:00
tiennm99 8574eee45e docs(newsletter): mark dead links, fix headings and tags 2026-09-28 09:25:58 +07:00
github-actions[bot] c84fccf0e3 chore(projects): refresh projects list 2026-09-28 02:54:30 +07:00
tiennm99 c0ddcb17b8 docs(newsletter): rewrite newsletters with Opus 5.5 2026-09-27 21:47:31 +07:00
tiennm99 574a830e03 feat(newsletter): protect code, verdicts and author notes in rewrites
protected-lines now keeps fenced code blocks (re-indent allowed) and
**Đánh giá:** verdict lines, and leaves summary sub-headings and short
italic key-point labels rewritable. The rewrite skill skips posts
without the AI-Assisted tag.
2026-09-27 21:47:31 +07:00
tiennm99 5804366438 feat(newsletter): add mt-rewrite-newsletter skill and protected-lines guard 2026-09-27 21:47:30 +07:00
github-actions[bot] 4710af385f chore(projects): refresh projects list 2026-09-27 02:29:20 +07:00
github-actions[bot] a53a9f5e07 chore(projects): refresh projects list 2026-09-26 03:10:28 +07:00
github-actions[bot] ba76a170fc chore(projects): refresh projects list 2026-09-25 03:12:20 +07:00
github-actions[bot] 5e6a84b9a4 chore(projects): refresh projects list 2026-09-24 02:54:13 +07:00
github-actions[bot] 21dcce6067 chore(projects): refresh projects list 2026-09-23 02:57:16 +07:00
github-actions[bot] e5f28ef941 chore(projects): refresh projects list 2026-09-22 03:41:18 +07:00
github-actions[bot] 76dd8c9622 chore(projects): refresh projects list 2026-09-21 02:09:31 +07:00
github-actions[bot] 20c4e27dc2 chore(projects): refresh projects list 2026-09-20 02:02:07 +07:00
github-actions[bot] 377b2dab6a chore(projects): refresh projects list 2026-09-19 02:16:08 +07:00
tiennm99 50ef688ce8 chore: drop Netlify
The site is built and deployed to Cloudflare Pages by hugo.yml and served by
Cloudflare; netlify.toml was referenced nowhere else and no Netlify build runs,
so the file only pinned toolchain versions for a deploy path that no longer
exists. No _redirects or _headers accompanied it.
2026-09-18 16:34:13 +07:00
tiennm99 20301d2d88 chore: check out LF line endings everywhere
The repository normalises to LF in the index either way; the working tree was
being converted to CRLF on checkout, which is wrong for a repo worked on from
Linux. It also silently breaks the newsletter parity suite, which compares
committed golden files byte for byte against engine stdout — the reason
scripts/newsletter needed an explicit LF override until now. That override and
the *.sh one are both redundant against an LF default.
2026-09-18 16:33:41 +07:00
tiennm99 7588c6566f docs(plans): record the post-push verification results
Actions deploy and the projects workflow both pass without Go. Netlify is
recorded as not applicable: the site deploys to Cloudflare Pages and netlify.toml
is referenced nowhere else, so no Netlify build exists to observe.
2026-09-18 16:28:07 +07:00
github-actions[bot] 486732d78c chore(projects): refresh projects list 2026-09-18 16:27:11 +07:00
tiennm99 19e297d25d docs(plans): record the JavaScript migration, parity, and review
Adds the migration plan and its phase files, the parity report, and the code
review.

Annotates the two records this work supersedes rather than rewriting them: the
2026-09-18 language-choice research, whose first recommendation was to keep the
engine in Go and was overridden the same day, and the 2026-08-18 plan that
produced that engine. Both bodies are left as written, since erasing them would
destroy the reasoning a future reader needs.

Six success criteria stay unticked with the reason inline: they need a push, a
GitHub token, or a Codex run to verify.
2026-09-18 16:24:56 +07:00
tiennm99 a95dfe3d05 ci: drop the Go toolchain from Actions and Netlify
Nothing in the repository needs Go any more. Kept as its own commit so a bisect
can attribute a Hugo build break to this change rather than to the engine
deletion.

Node setup and the conditional npm ci stay: the lockfile exists, so the
dependencies the engine now needs are installed if a workflow ever invokes it.
2026-09-18 16:24:49 +07:00
tiennm99 a56168b119 chore(newsletter): delete the Go engine
Parity is proven and every call site now invokes the JavaScript engine, so the
Go implementation has no callers. It remains recoverable from history.

The newsletter engine was Go's only consumer here: the Hugo theme is vendored
under themes/ and the site configuration declares no module block, so nothing
else needs a toolchain. Dropping go.mod also disposes of a latent defect, since
it declared a newer Go version than either build environment pinned and only
worked because the toolchain was fetched on demand.

The Go patterns in .gitignore go with it.
2026-09-18 16:24:42 +07:00
tiennm99 d0bf8bd347 refactor(skills): invoke the Node engine and centralise the command reference
Switches every call site to `node scripts/newsletter` and, in the same pass,
fixes three structural problems in the skill layer.

The seven subcommands were spelled out in five files; they are now enumerated
once in docs/newsletter/engine-commands.md, and every other mention is a link
except where a skill inlines the one or two commands it actually invokes. The
shared post mechanics move out of mt-add-url's directory into docs/newsletter/,
so no skill owns another skill's documentation and both runtimes reach it by a
repository-relative path. The Codex adapters become symlinks to their canonical
counterparts, which removes the parallel frontmatter that could drift.

Two skills are renamed for a uniform mt-<verb>-<object> scheme: mt-add-post
becomes mt-add-article, since it adds an article to a post, and mt-webfetch
becomes mt-fetch-url. The other four already conformed. Note that $mt-add-post
and mt-webfetch no longer resolve; no alias directories are left behind, because
two names for one skill is the duplication this change removes.

Trigger wording in the frontmatter descriptions is left alone apart from the
renamed tokens and the corrected fetch-tier summary: those strings are how the
runtimes decide whether to invoke a skill.

Setup documentation now states the npm ci prerequisite. The engine has
dependencies, so unlike its predecessor it does not run on a bare clone.
2026-09-18 16:24:34 +07:00
tiennm99 d07f48d69e test(newsletter): add a golden-output parity suite
Captures the Go engine's stdout for 26 deterministic cases, commits those
outputs as fixtures, and asserts the JavaScript engine reproduces them through
the real CLI surface, exit codes included. This is both the migration's proof of
correctness and the regression net the engine never had.

Every fixture was generated from the Go engine rather than written by hand, and
reviewed before commit so no existing bug became a permanent contract. Each
behaviour that a JavaScript-idiomatic rewrite would silently break has a case
that goes red when the naive implementation is reintroduced; verified by
mutating the engine and re-running.

Content-scanning commands are pointed at a fixture tree through the subprocess
working directory. Both engines already resolve content/post from the process
CWD, so neither needed a test-only configuration knob, and the suite is proven
isolated from the real content by renaming it away and re-running.

Cases that depend on live upstreams are opt-in behind NEWSLETTER_NET=1: a
publication's feed window moves, and a suite that goes red on someone else's
outage teaches people to ignore red. The deterministic tier reaches the network
only through .invalid hosts, which never resolve, so it runs offline in about
two seconds.

The golden files are compared byte for byte, so scripts/newsletter is pinned to
LF; the repository otherwise checks out CRLF, which would break every comparison
on a fresh clone.
2026-09-18 16:24:16 +07:00
tiennm99 fb292d444d feat(newsletter): port the engine to JavaScript
Translates the seven-subcommand engine to Node ESM, one module per former Go
file, invoked as `node scripts/newsletter <command>` from the repo root. The Go
implementation stays in place for now so parity can be measured against it.

Hand-rolled HTML and XML regexes give way to cheerio, which removes the manual
string surgery in caption extraction and covers RSS and sitemap XML through
xmlMode without a second parser. fetch-via-defuddle gains a local extraction
stage ahead of the defuddle.md proxy; the proxy stays, because fetching from a
third IP is the whole point when this machine's IP is the blocked one. Both
stages now emit the same YAML frontmatter plus body, and an extraction that
looks like a bot challenge counts as a local failure so the proxy still runs.

Behaviour is preserved where it is load-bearing rather than where it is merely
idiomatic: query strings are rebuilt by string surgery so surviving parameters
keep their original order and encoding, duplicate detection stays an index loop
with byte-offset boundary checks so a prefix of a stored URL is not a false
match, empty optional fields are omitted rather than emitted as "", a deep-crawl
miss reports cutoff as null rather than dropping the key, bullet filtering counts
code points, tag counts keep their six-column alignment, and a malformed percent
sequence falls back to the raw substring.

The printer lives in its own module so a command module never imports the
dispatcher: index.js runs main() at module scope, so that cycle would execute
the CLI as a side effect of any import. Missing dependencies report one
actionable line instead of a module-resolution stack trace, and a reader that
closes early exits quietly instead of raising EPIPE.
2026-09-18 16:23:58 +07:00
tiennm99 a396c1fb40 refactor(scripts): unify module extensions on .js
The root package.json already declares "type": "module", so a .js file here is
ESM and the .mjs suffix only restates what the manifest states. Renames the three
GitHub Actions scripts and follows every reference, including the six JSDoc
import("...") type annotations that would otherwise degrade to any without
throwing.
2026-09-18 16:23:36 +07:00