From 4cc6afcf8032962ff58ab3e40077dff07e7666c0 Mon Sep 17 00:00:00 2001 From: wenchy Date: Thu, 11 Jun 2026 21:01:22 +0800 Subject: [PATCH 1/8] feat: add new skill tableau-release-html --- .claude/skills/tableau-release-html/SKILL.md | 272 ++++++++++++++++ .../references/design-system.md | 305 ++++++++++++++++++ README.md | 4 + 3 files changed, 581 insertions(+) create mode 100644 .claude/skills/tableau-release-html/SKILL.md create mode 100644 .claude/skills/tableau-release-html/references/design-system.md diff --git a/.claude/skills/tableau-release-html/SKILL.md b/.claude/skills/tableau-release-html/SKILL.md new file mode 100644 index 000000000..d905df9e1 --- /dev/null +++ b/.claude/skills/tableau-release-html/SKILL.md @@ -0,0 +1,272 @@ +--- +name: tableau-release-html +description: > + Generate a complete, beautiful styled HTML release notes page for a Tableau Go library + release (github.com/tableauio/tableau). Produces a self-contained light-theme HTML file + with sticky nav, animated hero, semantic color sections (Features / Bug Fixes / Breaking / + Deprecated / Dependencies / Examples), GitHub-light code blocks, Excel spreadsheet demo + tables, and scroll-reveal animations. Use this skill whenever the user asks to generate, + create, or update a Tableau release page, release notes HTML, or release poster — + even if they just say "make the release page for v0.17.0" or "new release, same style". +--- + +# Tableau Release HTML Generator + +Produce a polished, self-contained HTML release notes page for a Tableau Go library release. +Everything you need is inside this skill — read `references/design-system.md` (bundled here) +for the complete CSS block and all component HTML patterns before writing any code. + +--- + +## Step 1 — Gather release content + +If the user hasn't provided details, ask for (or infer from the GitHub releases page): + +| Field | Example | +|-------|---------| +| Version | `v0.17.0` | +| Release date | `July 15, 2026` | +| Features | List of titles + short descriptions + key proto/YAML snippets | +| Bug fixes | List of short fix descriptions (9–15 typical) | +| Breaking changes | Items requiring migration, with before/after code | +| Deprecated items | Anything deprecated, with notes | +| Dependency updates | Package name + old version → new version | +| Spreadsheet examples | Excel-style demos for key features | + +You can also read the GitHub release tag using `gh release view vX.Y.Z --repo tableauio/tableau` +or inspect commit history to fill in details automatically. + +--- + +## Step 2 — Plan the sections + +Standard section order (omit sections with no content): + +``` +01 New Features green (#16a34a) +02 Bug Fixes blue (#2563eb) +03 Breaking Changes red (#dc2626) +04 Deprecated amber (#d97706) +05 Dependency Updates slate (#6b7280) +06 Spreadsheet Examples violet (#7c3aed) +``` + +--- + +## Step 3 — Generate the HTML + +Output a single self-contained HTML file named `release-vX.Y.Z-light.html`. + +Read `references/design-system.md` now for the complete CSS block and component HTML patterns +before writing any code. This is critical — the classes, colors, and structures must match +exactly for the page to look correct. + +### Page skeleton + +```html + + + + + + Tableau vX.Y.Z — Release Notes + + + + + + + + + + + + + + + + + + +``` + +### Key generation rules + +**Nav** — always version-specific: +- Left: logo (22px) + "Tableau" bold + version badge (grey pill) +- Right: nav links matching the sections present + GitHub icon SVG link to the release tag + +**Hero** — five animated elements: +1. `hero-anim-0`: brand row (logo 28px + "Tableau" + `|` + version badge + `|` + green released-pill with pulse dot) +2. `hero-anim-1`: "RELEASE NOTES" eyebrow (JetBrains Mono, 10.5px, 0.3em letter-spacing, `#9ca3af`) +3. `hero-anim-2`: giant version `vX.Y.Z` using `.hero-version` +4. `hero-anim-3`: descriptive subtitle (list the top 3–4 headline features, muted `#6b7280`, max-width 580px) +5. `hero-anim-4`: stat chips row (`.stat-chip .chip-green/blue/amber/violet`) + `|` + tech tags + +Background: CSS grid (`::before`) + ghosted watermark version text + `deco-sheet-wrap` (3D spreadsheet table, absolute positioned bottom-right). + +**Section structure** — each major section: +```html +
+
+
+ 01 +

New Features

+
+ +
+
+``` + +**Feature cards** — grid layout, `border-t-2` accent: +```html +
+
+
+ NEW + TOOLING +
+

Feature Title

+

Description…

+ +
+
+``` + +**Bug fix rows** — inside a `.card-base rounded-2xl`: +```html +
+
+ +
+

Fix title

+

Brief description.

+
+ PARSER +
+ +
+``` + +**Breaking change cards** — `.breaking-card` with red top border: +```html +
+
BREAKING
+

Change title

+

Description…

+ +
+``` + +**Before/after panels**: +```html +
+
+
✗ Before (vOLD)
+
...old code...
+
+
+
✓ After (vNEW)
+
...new code...
+
+
+``` + +**Code blocks** — GitHub light style: +```html +
+
▸ filename.go
+
+ // comment + func Example() error { + // highlighted new line + } +
+
+``` +Syntax color classes: `c-key` (red `#cf222e`), `c-str` (dark blue `#0a3069`), `c-typ` (brown `#953800`), `c-ann` (purple `#8250df`), `c-num` (blue `#0550ae`), `c-val` (green `#116329`), `c-fld` (default `#24292f`), `c-com` (grey `#6e7781`). +Highlight classes: `c-hl` (green bg, good), `c-warn` (yellow bg, caution), `c-add` (green line), `c-del` (red line). + +**Excel spreadsheet tables** — always use 3-header-row convention: +```html +
▸ SheetName.xlsx — SheetTab
+
+ + + + + + + ... + ... + ... + ... + ... + +
ABC
1FieldName
2map<uint32,Msg>
3Note text
41
5
+
+``` + +**Footer**: +```html + +``` + +**Scroll-reveal script** — include verbatim at bottom of ``: +```html + +``` + +--- + +## Step 4 — Output + +Save the file as `release-vX.Y.Z-light.html` in the current working directory (or wherever +the user specifies — ask if unclear). + +After writing, take a Playwright screenshot to verify the rendered look. Locate `node_modules/playwright` +in the project directory and adapt the paths accordingly: +```js +const {chromium} = require('playwright'); // or path to local node_modules +(async () => { + const browser = await chromium.launch(); + const page = await browser.newPage(); + await page.setViewportSize({width:1440, height:900}); + await page.goto('file://').catch(()=>{}); + await page.waitForTimeout(2800); + await page.screenshot({path:'preview-vX.Y.Z.png', fullPage:false}); + await browser.close(); +})(); +``` + +Show the screenshot to the user and ask if any changes are needed. + +--- + +## Design system quick reference + +See `references/design-system.md` for: +- Complete ` +``` + +--- + +## Semantic color matrix + +### Sections +| Section | Number | CSS class | Accent colour | Title colour | +|---------|--------|-----------|---------------|--------------| +| New Features | 01 | `sec-feat` | `#16a34a` green | `#16a34a` | +| Bug Fixes | 02 | `sec-fix` | `#2563eb` blue | `#1d4ed8` | +| Breaking Changes | 03 | `sec-break` | `#dc2626` red | `#b91c1c` | +| Deprecated | 04 | `sec-dep` | `#d97706` amber | `#92400e` | +| Dependencies | 05 | `sec-white-next-gray` | `#6b7280` slate | `#6b7280` | +| Examples | 06 | `sec-gray` | `#7c3aed` violet | default | + +### Tags (`.tag` base class) +| Class | Use | +|-------|-----| +| `tag-new` / `tag-option` | New feature, new option — green | +| `tag-breaking` | Breaking change — red | +| `tag-tooling` | Tooling / parser / generator — blue | +| `tag-enhanced` | Enhancement / improvement — violet | +| `tag-arch` | Architecture / config — amber | + +### Stat chips (`.stat-chip` base class) +| Class | Use | +|-------|-----| +| `chip-green` | Feature count | +| `chip-blue` | Bug fix count | +| `chip-amber` | Breaking change count (red theme) | +| `chip-violet` | Deprecated count (amber theme) | + +### Demo badges (`.demo-badge` base class) +| Class | Use | +|-------|-----| +| `db-new` | New feature demo — green | +| `db-fix` | Bug fix demo — blue | +| `db-breaking` | Breaking change demo — red | +| `db-dep` | Deprecated demo — amber | + +--- + +## GitHub icon SVG (for nav) + +```html + + + +``` + +--- + +## Hero decorative spreadsheet (copy as-is, update cell content for the release) + +```html +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ABCDEFG
1IDNameLevelHPAttackTypeValidate
2map<uint32,Hero>stringint32int32int32enum<HeroType>string|{validate:...
3Hero IDHero nameHero levelMax HPBase attackHero classCEL rule
41Arthur50500120warrior
52Merlin4538090mage
63Lancelot55620150knight
+
+``` + +--- + +## Tableau protobuf type system quick reference + +Row 2 of every Excel table uses these type strings: + +| Type string | Meaning | +|-------------|---------| +| `map` | Map keyed by uint32, value is MsgName proto | +| `[MsgName]` | Repeated/list of MsgName | +| `[]{.MsgName}` | Horizontal list struct (union) | +| `string` / `int32` / `bool` / `float` | Scalar proto types | +| `enum` | Enum reference | +| `string\|{validate:"rule"}` | Scalar + CEL validation prop | +| `string\|{validate_complex:"rule"}` | Complex CEL validation | +| `uint32\|{key:true}` | Primary key field | +| `uint32\|{optional:true}` | Optional field | +| `uint32\|{refer:"Sheet.Field"}` | Cross-sheet reference | diff --git a/README.md b/README.md index abe148a7c..6c27ff94b 100644 --- a/README.md +++ b/README.md @@ -46,3 +46,7 @@ assets/js/ # scripts (entry: index.js) static/ # files served as-is at the URL root .github/workflows/ # CI: lint + build + GitHub Pages deploy ``` + +## Skills + +- [tableau-release-html](.claude/skills/tableau-release-html) From 7fcd1eddf7388d75a11557e555120433a55f4ef2 Mon Sep 17 00:00:00 2001 From: wenchy Date: Thu, 11 Jun 2026 21:17:24 +0800 Subject: [PATCH 2/8] feat(release): add Loader v0.6.0 release page; document release flow in CLAUDE.md --- CLAUDE.md | 48 ++ content/en/release/loader-v0-6-0.md | 12 + content/zh/release/loader-v0-6-0.md | 12 + static/release/loader-v0-6-0.html | 657 ++++++++++++++++++++++++++++ 4 files changed, 729 insertions(+) create mode 100644 content/en/release/loader-v0-6-0.md create mode 100644 content/zh/release/loader-v0-6-0.md create mode 100644 static/release/loader-v0-6-0.html diff --git a/CLAUDE.md b/CLAUDE.md index b603e81b5..7ed30cb97 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -66,3 +66,51 @@ SCSS lives in `assets/scss/` (entry `app.scss`); JS in `assets/js/` (entry `inde **GitHub Pages** (see `.github/workflows/deploy-github.yml`) — on push to `master`, runs `npm install`, `npm run lint:markdown`, `npm run build`, then publishes `./public` via `peaceiris/actions-gh-pages@v3`. CI pins Node 24. Treat upstream Doks files (delivered via `node_modules/@hyas/doks`) as read-only; override by creating same-path files in this repo's `layouts/`, `assets/`, etc. + +## Adding a release page + +The Release section (`/release/`) lists release entries from `content//release/` as Bootstrap cards. Each entry is a `.md` page; the iframe field in front-matter determines whether it renders the markdown body or embeds a standalone HTML report. + +### Two flavors + +**Markdown release** — author release notes as plain Hugo markdown: + +1. Create `content/en/release/.md` and `content/zh/release/.md` with parallel content. +2. Use the same front-matter shape as `docs/` (`title`, `description`, `lead`, `date`, `lastmod`, `weight`, `toc`, …). +3. The page renders in the blog-style centered article layout with an optional right-side TOC. + +**HTML release** — embed a pre-built standalone HTML report: + +1. Place the artifact at `static/release/.html`. It is served as-is at `/release/.html` (no theme wrapping). For multi-repo disambiguation (e.g., the `loader` repo and the `tableau` repo can both reach v0.6.0), prefix the slug with the repo name: `loader-v0-6-0.html`, `tableau-v0-16-0.html`. +2. Inside the artifact, wrap the brand link with `target="_top"` so navigation escapes the iframe. +3. Create stub markdown files `content/en/release/.md` and `content/zh/release/.md` with the docs front-matter plus an extra field: + + ```yaml + --- + title: "Loader v0.6.0" + description: "Loader v0.6.0 release report." + lead: "One-line summary of the headline features." + date: 2026-06-11T04:17:48+00:00 + lastmod: 2026-06-11T04:17:48+00:00 + draft: false + images: [] + weight: 1610 + toc: false + iframe: "/release/loader-v0-6-0.html" + --- + ``` + + The body is empty; the iframe renders inside a full-width frame with breadcrumb + open-in-tab + fullscreen-toggle action bar. Reading time on the card is computed by reading the artifact's prose at build time (see `layouts/partials/main/release-meta.html`). + +### Generating the HTML report + +The `tableau-release-html` skill (in `.claude/skills/tableau-release-html/`) generates the styled standalone HTML for a Tableau-family release from a GitHub release tag. Pipeline: + +1. Invoke the skill with the repo + version: `gh release view --repo ` is used internally to fetch the release body. +2. The skill produces a self-contained HTML file (Inter + JetBrains Mono fonts via Google Fonts CDN, Tailwind via CDN, all CSS inline) following the design system in `.claude/skills/tableau-release-html/references/design-system.md`. +3. Move the output to `static/release/.html`. +4. Author the en + zh markdown stubs as above. + +### Sort order + +The card list uses `weight` ascending — lower weight sorts to the top. Convention: newer releases use lower weights (e.g., v0.16.0 = 1600, v0.15.0 = 1700). For multi-repo entries, choose weights that preserve the desired chronological order across repos. diff --git a/content/en/release/loader-v0-6-0.md b/content/en/release/loader-v0-6-0.md new file mode 100644 index 000000000..b5b5138c7 --- /dev/null +++ b/content/en/release/loader-v0-6-0.md @@ -0,0 +1,12 @@ +--- +title: "Loader v0.6.0" +description: "Loader v0.6.0 release report." +lead: "C# loader plugin, Protobuf Editions (2023/2024) support, cross-language patch system, and a Windows CI overhaul." +date: 2026-06-11T04:17:48+00:00 +lastmod: 2026-06-11T04:17:48+00:00 +draft: false +images: [] +weight: 1610 +toc: false +iframe: "/release/loader-v0-6-0.html" +--- diff --git a/content/zh/release/loader-v0-6-0.md b/content/zh/release/loader-v0-6-0.md new file mode 100644 index 000000000..e5751f87c --- /dev/null +++ b/content/zh/release/loader-v0-6-0.md @@ -0,0 +1,12 @@ +--- +title: "Loader v0.6.0" +description: "Loader v0.6.0 发布报告。" +lead: "C# loader 插件、Protobuf Editions (2023/2024) 支持、跨语言 patch 系统,以及 Windows CI 改造。" +date: 2026-06-11T04:17:48+00:00 +lastmod: 2026-06-11T04:17:48+00:00 +draft: false +images: [] +weight: 1610 +toc: false +iframe: "/release/loader-v0-6-0.html" +--- diff --git a/static/release/loader-v0-6-0.html b/static/release/loader-v0-6-0.html new file mode 100644 index 000000000..509b13dfe --- /dev/null +++ b/static/release/loader-v0-6-0.html @@ -0,0 +1,657 @@ + + + + + + Tableau Loader v0.6.0 — Release Notes + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ABCDEF
1IDNameLoaderLangIndexPatch
2map<uint32,Item>stringenum<Loader>string[Index]bool
3Item IDDisplay nameTarget loaderLanguageIndexesPatch on
41HerogoGoby_nametrue
52HerocppC++by_nametrue
63HerocsharpC#by_nametrue
+
+ +
+ + + +
+
+ Tableau + Tableau Loader + | + v0.6.0 + | + + + Released June 11, 2026 + +
+ +

RELEASE NOTES

+ +

v0.6.0

+ +

+ C# loader plugin, Protobuf Editions (2023/2024) support, cross-language patch system, and a Windows CI overhaul. +

+ +
+ + 6 Features + ○ 6 Bug Fixes + ⟲ 3 Refactors + | + Go · C++ · C# · Python · Protobuf Editions +
+
+
+ + +
+
+ + +
+
+
+ 01 +

New Features

+
+ +
+ + +
+
+ NEW + PLUGIN +
+

C# Loader Plugin

+

+ First-class C# code generation alongside Go, C++, and Python. Generates strongly-typed accessors, indexes, and OrderedMap collections from your protobuf schema. +

+
+
▸ Hero.cs (generated)
+
+public class HeroMgr { + public Hero Get(uint id); + public OrderedMap All(); + public Hero FindByName(string name); +} +
+
+
+ + +
+
+ NEW + PROTOBUF +
+

Protobuf Editions 2023/2024

+

+ Full support for the new Protobuf Editions model. Generated code now compiles cleanly under edition = "2023" and "2024", with a modernized build toolchain. +

+
+
▸ schema.proto
+
+// proto2 / proto3 still supported +// new: editions 2023, 2024 +edition = "2024"; +package tableau; +message Hero { ... } +
+
+
+ + +
+
+ NEW + CROSS-LANG +
+

Cross-Language Patch Support

+

+ Unified patch loading across Go, C++, C#, and Python. Real test suites verify byte-for-byte parity. A Windows-CI overhaul ensures every PR runs the full matrix. +

+
+
▸ patch matrix
+
+// patch.json applied identically across languages +✓ go loader/patch_test.go PASS +✓ cpp loader/patch_test.cpp PASS +✓ csharp Loader.Tests/Patch.cs PASS +✓ python loader/test_patch.py PASS +
+
+
+ + +
+
+ NEW + WINDOWS +
+

Windows Build Setup

+

+ One-command Windows environment bootstrap. prepare.bat installs CMake 3.x, sets up vcpkg, and prompts for admin where needed. +

+
+
▸ prepare.bat
+
+:: From a fresh Windows checkout +> prepare.bat + → Installing CMake 3.x ... + → Setting up vcpkg ... + ✓ Build environment ready +
+
+
+ + +
+
+ NEW + LOGGING +
+

Protobuf Log Handler

+

+ Structured logging output as Protobuf messages, suitable for downstream pipelines. Bundled with improved code generation for log fields and CI toolchain upgrades. +

+
+
▸ usage
+
+loader.SetLogHandler(func(e *pb.LogEntry) { + // structured proto, easy to ship + sink.Write(e) +}) +
+
+
+ + +
+
+ NEW + GO 1.26 +
+

Generic Tree Map (Go 1.26)

+

+ The internal tree-map index uses Go 1.26's self-referential generics, eliminating runtime type assertions and giving you compile-time-checked nested map access. +

+
+
▸ treemap.go
+
+type TreeMap[K comparable, V any] struct { + children map[K]*TreeMap[K, V] + value V +} +
+
+
+ +
+
+
+ + +
+
+
+ 02 +

Bug Fixes

+
+ +
+
+ +
+

C++ GetLastLoadedTime no longer marked inline

+

Removing the keyword fixes ODR violations when the symbol is referenced from multiple translation units.

+
+ CPP +
+ +
+ +
+

Template includes use original names; OrderedMap accessors adjusted

+

Generated headers now reference the original proto file names instead of munged variants, and OrderedMap iteration helpers were corrected.

+
+ TEMPLATE +
+ +
+ +
+

Windows prepare.bat handles CMake 3.x and admin elevation

+

The bootstrap script now installs the correct CMake major version and prompts for admin rights when required.

+
+ WINDOWS +
+ +
+ +
+

Field-name case conversion + cross-platform generation order

+

Generated output is now byte-identical regardless of build platform. Diff-friendly artifacts unblock reproducible builds.

+
+ CODEGEN +
+ +
+ +
+

Index parser skips oneof fields when walking level messages

+

Level traversal no longer descends into oneof branches, removing spurious "field not found" errors.

+
+ INDEX +
+ +
+ +
+

Index field in list with upper maps now resolves correctly

+

Fixes a path-resolution bug where index fields nested under both a list and a parent map produced wrong key sequences.

+
+ INDEX +
+
+
+
+ + +
+
+
+ 03 +

Dependencies & Toolchain

+
+ +

+ Major build-system modernization. The protobuf submodule was removed in favor of vcpkg-managed dependencies, the Go diff library was swapped, and CI now runs buf for proto generation. +

+ +
+ + vcpkg (replaces protobuf submodule) + - protobuf submodule (removed) + + go-udiff (extracted to pkg/udiff) + - go-difflib (replaced) + + buf (CI proto generation) + + Protobuf Editions 2023/2024 + + Go 1.26 (self-refer generics) + + Recursive submodule checkout in CI +
+ +
+ Migration note: if you previously built from a checkout that pulled the bundled protobuf submodule, run prepare.bat (Windows) or ./prepare.sh (Linux/macOS) once after upgrading to bootstrap vcpkg-managed dependencies. +
+
+
+ + +
+
+
+ 04 +

Examples

+
+ + +
+ FIX · INDEX +

Indexes through list-under-map paths

+

+ Before v0.6.0, an index defined on a field nested inside a list that itself sits under a parent map generated an incorrect key path. v0.6.0 walks the parent map context correctly and emits stable lookups. +

+ +

▸ HeroSquads.xlsx — Squad

+
+ + + + + + + + + + + + + +
ABCD
1SquadIDMembersHeroIDRole
2map<uint32,Squad>[Member]uint32|{index:"ByHero"}string
3Squad IDMember listIndex by heroMember role
4101001tank
5101002healer
6111001dps
+
+ +
+
+
✗ Before (v0.5.0)
+
+// FindByHero(1001) returned only the first squad's match
+// upper map context dropped during index build
+[]Member{ {SquadID:10, HeroID:1001} } +
+
+
+
✓ After (v0.6.0)
+
+// FindByHero(1001) returns all matching members
+// across every squad in the parent map
+[]Member{
+ {SquadID:10, HeroID:1001, Role:"tank"},
+ {SquadID:11, HeroID:1001, Role:"dps"},
+} +
+
+
+
+ + +
+ NEW · MULTI-LANG +

Same data, four languages

+

+ From a single protobuf schema, v0.6.0 generates idiomatic loader code for Go, C++, C#, and Python. All four use the same patch system, the same indexes, and ship with parity tests. +

+ +
+
+
▸ main.go
+
+m, _ := loader.New("./conf") +hero := m.Hero().Get(1001) +
+
+
+
▸ main.cpp
+
+tableau::Loader m("./conf"); +auto* hero = m.Hero().Get(1001); +
+
+
+
▸ Program.cs
+
+var m = new Loader("./conf"); +var hero = m.Hero.Get(1001); +
+
+
+
▸ main.py
+
+m = Loader("./conf") +hero = m.hero.get(1001) +
+
+
+
+
+
+ + + + + + + + From 8ccffcdbe07d39a02ac0c3c1bdfaa9286345b2a5 Mon Sep 17 00:00:00 2001 From: wenchy Date: Fri, 12 Jun 2026 13:03:07 +0800 Subject: [PATCH 3/8] feat(release): rewrite Loader v0.6.0 page from deep loader-repo analysis --- static/release/loader-v0-6-0.html | 385 ++++++++++++++++++------------ 1 file changed, 231 insertions(+), 154 deletions(-) diff --git a/static/release/loader-v0-6-0.html b/static/release/loader-v0-6-0.html index 509b13dfe..d554169c6 100644 --- a/static/release/loader-v0-6-0.html +++ b/static/release/loader-v0-6-0.html @@ -32,22 +32,18 @@ html{scroll-behavior:smooth;} body{background:var(--bg);font-family:'Inter',system-ui,sans-serif;color:var(--text);-webkit-font-smoothing:antialiased;padding-top:0;} - /* Typography */ .hero-version{font-weight:900;font-size:clamp(60px,10vw,130px);line-height:.92;letter-spacing:-4px;color:#111827;} .hero-anim-0{animation:fadeInUp .7s ease .02s both;} .sec-title{font-weight:800;font-size:clamp(28px,4.5vw,50px);line-height:1.06;color:var(--text);} .sec-num{font-family:'JetBrains Mono',monospace;font-size:11px;font-weight:600;letter-spacing:.12em;text-transform:uppercase;} - /* Nav */ .hero-section{background:#ffffff;} .nav-link{font-size:13.5px;font-weight:500;color:#6b7280;text-decoration:none;transition:color .15s;} .nav-link:hover{color:#111827;} - /* Cards */ .card-base{background:var(--card);border:1px solid #e5e7eb;box-shadow:0 1px 3px rgba(0,0,0,0.05),0 4px 16px rgba(0,0,0,0.07);} .feat-card{background:var(--card);border:1px solid var(--border);box-shadow:0 1px 3px rgba(0,0,0,.04),0 4px 16px rgba(0,0,0,.05);transition:transform .22s,box-shadow .22s;} .feat-card:hover{transform:translateY(-3px);box-shadow:0 4px 8px rgba(0,0,0,.06),0 16px 40px rgba(0,0,0,.09);} - /* Animations */ @keyframes fadeInUp{from{opacity:0;transform:translateY(24px)}to{opacity:1;transform:none}} @keyframes bounceY{0%,100%{transform:translateY(0) translateX(-50%)}50%{transform:translateY(7px) translateX(-50%)}} @keyframes pulseDot{0%,100%{opacity:1;transform:scale(1)}50%{opacity:.35;transform:scale(.72)}} @@ -63,7 +59,6 @@ .rd1{transition-delay:.07s}.rd2{transition-delay:.14s}.rd3{transition-delay:.21s} .rd4{transition-delay:.28s}.rd5{transition-delay:.35s}.rd6{transition-delay:.42s} - /* Chips & Tags */ .stat-chip{display:inline-flex;align-items:center;gap:6px;padding:8px 18px;border-radius:8px;font-family:'JetBrains Mono',monospace;font-size:12.5px;font-weight:600;} .chip-green{background:#f0fdf4;color:#16a34a;border:1px solid #bbf7d0;} .chip-blue{background:#eff6ff;color:#2563eb;border:1px solid #bfdbfe;} @@ -82,12 +77,10 @@ .db-dep{background:#fffbeb;color:#d97706;border:1px solid #fde68a;} .icode-o{font-family:'JetBrains Mono',monospace;font-size:11.5px;background:#f0fdf4;color:#16a34a;border:1px solid #bbf7d0;padding:1px 6px;border-radius:4px;} - /* Fix rows */ .fix-row{border-bottom:1px solid #f3f4f6;transition:background .12s;} .fix-row:last-child{border-bottom:none;} .fix-row:hover{background:#f9fafb;} - /* Code blocks */ .code-blk{background:#fff;border:1px solid #e5e7eb;border-radius:8px;overflow:hidden;box-shadow:0 1px 3px rgba(0,0,0,0.04),0 4px 12px rgba(0,0,0,0.05);} .code-blk-hd{padding:9px 16px;background:#f9fafb;border-bottom:1px solid #e5e7eb;font-family:'JetBrains Mono',monospace;font-size:11px;color:#57606a;letter-spacing:.04em;} .code-blk-hd::before,.demo-out-hd::before{display:none;} @@ -100,12 +93,10 @@ .c-hl{background:#e6ffec;display:block;border-left:2px solid #2da44e;padding-left:14px;margin-left:-16px;margin-right:-16px;padding-right:16px;} .c-warn{background:#fff8c5;display:block;border-left:2px solid #d29922;padding-left:14px;margin-left:-16px;margin-right:-16px;padding-right:16px;} - /* Demo output */ .demo-out{border:1px solid #e5e7eb;border-radius:8px;overflow:hidden;margin-top:16px;box-shadow:0 1px 3px rgba(0,0,0,0.04),0 4px 12px rgba(0,0,0,0.05);} .demo-out-hd{padding:9px 16px;background:#f9fafb;border-bottom:1px solid #e5e7eb;font-family:'JetBrains Mono',monospace;font-size:11px;color:#57606a;letter-spacing:.04em;} .demo-out-bd{padding:14px 16px;background:#fff;font-family:'JetBrains Mono',monospace;font-size:12px;line-height:1.8;overflow-x:auto;color:#24292f;} - /* SBS panels */ .sbs-wrap{display:grid;grid-template-columns:1fr 1fr;gap:16px;} @media(max-width:720px){.sbs-wrap{grid-template-columns:1fr;}} .sbs-panel{border-radius:10px;overflow:hidden;box-shadow:0 1px 3px rgba(0,0,0,0.04),0 2px 8px rgba(0,0,0,0.06);} @@ -117,11 +108,9 @@ .sbs-bd-after{background:#e6ffec;border:1px solid #abdfba;border-top:none;color:#0d3b1f;} .ok{color:#116329;}.bad{color:#82071e;text-decoration:line-through;}.warn{color:#953800;}.dim{color:#6e7781;} - /* Callout */ .callout{background:#eff6ff;border:1px solid #bfdbfe;border-left:3px solid #2563eb;padding:11px 16px;border-radius:0 8px 8px 0;font-size:13.5px;color:#1e40af;line-height:1.65;} .callout code{font-family:'JetBrains Mono',monospace;font-size:11px;background:rgba(37,99,235,0.1);color:#1e40af;padding:1px 5px;border-radius:3px;} - /* Excel */ .xl-scroll{overflow-x:auto;border:1px solid #d0d7de;border-radius:6px;box-shadow:0 1px 3px rgba(0,0,0,0.05),0 4px 16px rgba(0,0,0,0.08);} .xl{border-collapse:collapse;font-family:'JetBrains Mono',monospace;font-size:11.5px;background:#fff;min-width:380px;width:100%;} .xl thead th{background:#DDE4EE;color:#4A5878;border:1px solid #B8C4D4;text-align:center;padding:3px 8px;font-size:10.5px;font-weight:600;user-select:none;white-space:nowrap;} @@ -135,15 +124,12 @@ .xl tr.xr-meta td{background:#F0EEF8;color:#4A3880;} .xl-label{font-family:'JetBrains Mono',monospace;font-size:11px;color:#A2A2A2;letter-spacing:.05em;margin-bottom:6px;} - /* Dependency tags */ .dep-tag{background:#fff;border:1px solid rgba(0,0,0,.1);padding:7px 15px;border-radius:6px;font-family:'JetBrains Mono',monospace;font-size:12px;color:#626060;transition:all .15s;cursor:default;box-shadow:0 1px 2px rgba(0,0,0,0.04);} .dep-tag-new{background:#fff7ed;border-color:rgba(249,115,22,0.3);color:#ea580c;} - /* Demo blocks */ .demo-block{padding-bottom:56px;margin-bottom:56px;border-bottom:1px solid #f3f4f6;} .demo-block:last-child{border-bottom:none;padding-bottom:0;margin-bottom:0;} - /* Breaking cards */ .breaking-card{background:#fff;border-radius:16px;padding:32px;border:1px solid rgba(0,0,0,.09);border-left:4px solid #dc2626;box-shadow:0 1px 3px rgba(220,38,38,0.05),0 4px 14px rgba(220,38,38,0.08);} @media(max-width:640px){ @@ -151,7 +137,6 @@ .sec-title{font-size:clamp(24px,8vw,38px);} } - /* Hero grid background */ .hero-section::before{ content:''; position:absolute; inset:0; pointer-events:none; z-index:0; background-image: @@ -160,7 +145,6 @@ background-size:80px 28px; } - /* Decorative spreadsheet */ .deco-sheet-wrap{position:absolute;bottom:-30px;right:-24px;pointer-events:none;user-select:none;z-index:1;opacity:0.10;transform:perspective(1100px) rotateY(-14deg) rotateX(5deg);transform-origin:right bottom;transition:opacity .3s;} .hero-section:hover .deco-sheet-wrap{opacity:0.16;} .deco-sheet{border-collapse:collapse;font-family:'JetBrains Mono',monospace;font-size:11.5px;background:#fff;border:2px solid #9AAAB8;box-shadow:0 8px 40px rgba(0,0,0,0.18);} @@ -175,7 +159,6 @@ .deco-sheet td.key{background:#FFFDE8;} .deco-sheet td.sel,.deco-sheet th.sel{background:rgba(217,90,39,0.15)!important;outline:2px solid rgba(217,90,39,0.5);outline-offset:-2px;} - /* Section tints */ .sec-feat{background:linear-gradient(180deg,#fafffe 0%,#fafffe 90%,#f0fdf8 100%);} .sec-fix{background:linear-gradient(180deg,#f0f8ff 0%,#f0f8ff 90%,#ffffff 100%);} .sec-break{background:linear-gradient(180deg,#fffafa 0%,#fffafa 90%,#fff5f5 100%);} @@ -200,7 +183,7 @@
-
@@ -272,21 +254,20 @@

v0.6.0

-

- C# loader plugin, Protobuf Editions (2023/2024) support, cross-language patch system, and a Windows CI overhaul. +

+ First-class C# loader plugin, Protobuf Editions 2023/2024 across all three plugins, a unified make.py build pipeline replacing init.bat / init.sh / prepare.bat, and vcpkg-managed protobuf with a quarterly-pinned matrix.

- + 6 Features + + 7 Features ○ 6 Bug Fixes ⟲ 3 Refactors | - Go · C++ · C# · Python · Protobuf Editions + Go · C++17 · C# (Unity 2022.3 LTS / .NET 8) · Editions 2023/2024 · Go 1.26
-
@@ -300,7 +281,6 @@

New Features

-
NEW @@ -308,124 +288,150 @@

New Features

C# Loader Plugin

- First-class C# code generation alongside Go, C++, and Python. Generates strongly-typed accessors, indexes, and OrderedMap collections from your protobuf schema. + New protoc-gen-csharp-tableau-loader plugin. Targets Unity 2022.3 LTS / .NET 8, generates *.pc.cs. Same Hub / Messager / Load API surface as Go & C++, including patch loading, MessagerOptions, custom ReadFunc/LoadFunc, and a thread-safe Atomic<T>-backed Hub.

-
▸ Hero.cs (generated)
+
▸ Program.cs
-public class HeroMgr { - public Hero Get(uint id); - public OrderedMap All(); - public Hero FindByName(string name); -} +var hub = new Tableau.Hub(); +var opts = new Tableau.Load.Options { + IgnoreUnknownFields = true, + PatchDirs = new() { "./patch" }, +}; +hub.Load("./conf", Format.JSON, opts); +var conf = hub.GetItemConf();
-
NEW - PROTOBUF + EDITIONS
-

Protobuf Editions 2023/2024

+

Protobuf Editions 2023 & 2024

- Full support for the new Protobuf Editions model. Generated code now compiles cleanly under edition = "2023" and "2024", with a modernized build toolchain. + All three plugins (Go / C++ / C#) emit code that compiles cleanly under edition = "2023" and "2024" alongside proto2 / proto3. The build toolchain in init.sh / init.bat was modernized to install a matching protoc.

▸ schema.proto
// proto2 / proto3 still supported -// new: editions 2023, 2024 +// new: Editions 2023 & 2024 edition = "2024"; -package tableau; -message Hero { ... } +package protoconf; +message ItemConf { ... }
-
NEW - CROSS-LANG + PATCH · TESTS
-

Cross-Language Patch Support

+

Cross-Language Patch + Real Test Suites

- Unified patch loading across Go, C++, C#, and Python. Real test suites verify byte-for-byte parity. A Windows-CI overhaul ensures every PR runs the full matrix. + Three real test suites — Go (testing), C++ (gtest), C# (xUnit) — exercise the same patch scenarios with golden-file parity. Standalone main.go / main.cpp / Program.cs demos were retired.

▸ patch matrix
-// patch.json applied identically across languages -✓ go loader/patch_test.go PASS -✓ cpp loader/patch_test.cpp PASS -✓ csharp Loader.Tests/Patch.cs PASS -✓ python loader/test_patch.py PASS +✓ go main_test.go::Test_Patch_* PASS +✓ cpp tests/patch_test.cpp PatchConf_* PASS +✓ csharp tests/PatchTests.cs PatchConf_* PASS +// All three load the same testdata/conf + testdata/patchconf +// and compare to testdata/patchresult/ goldens.
-
NEW - WINDOWS + BUILD
-

Windows Build Setup

+

Unified make.py Pipeline

- One-command Windows environment bootstrap. prepare.bat installs CMake 3.x, sets up vcpkg, and prompts for admin where needed. + One Python 3.10+ stdlib-only entry point replaces init.sh, init.bat, and prepare.bat. Subcommands: setup, generate, build, test, clean, env. CI runs the same script as developers.

-
▸ prepare.bat
+
▸ shell
-:: From a fresh Windows checkout -> prepare.bat - → Installing CMake 3.x ... - → Setting up vcpkg ... - ✓ Build environment ready +$ python3 make.py setup --lang all +$ python3 make.py test --lang go +$ python3 make.py test --lang cpp # vcpkg-installed protobuf +$ python3 make.py test --lang csharp -k HubTests +$ python3 make.py test --lang cpp --protobuf-version 3.21.12
-
NEW - LOGGING + DEV ENV
-

Protobuf Log Handler

+

Devcontainer & buf Codegen

- Structured logging output as Protobuf messages, suitable for downstream pipelines. Bundled with improved code generation for log fields and CI toolchain upgrades. + Reproducible dev environment via .devcontainer/: VS Code → "Reopen in Container" gives the same Go, buf, vcpkg, and protobuf versions used in CI. buf generate replaces the per-language gen.sh / gen.bat scripts in every test workspace.

-
▸ usage
+
▸ .devcontainer/versions.env
-loader.SetLogHandler(func(e *pb.LogEntry) { - // structured proto, easy to ship - sink.Write(e) -}) +GO_VERSION=1.24.0 +BUF_VERSION=1.67.0 +DOTNET_VERSION=8.0 +DEFAULT_VARIANT=modern +MODERN_PROTOBUF_VERSION=6.33.4 +LEGACY_V3_PROTOBUF_VERSION=3.21.12
-
+
+ NEW + LOGGING +
+

Dual-Mode Protobuf Log Handler (C++)

+

+ The C++ runtime now installs a Tableau log sink on libprotobuf — routing [libprotobuf] messages into the Tableau logger. Compile-time TABLEAU_PB_LOG_LEGACY picks legacy SetLogHandler (protobuf ≤ 3.x) or modern absl::LogSink (protobuf ≥ 4.x with Abseil). +

+
+
▸ util.pc.cc
+
+#if TABLEAU_PB_LOG_LEGACY + // SetLogHandler(ProtobufLogHandler) on protobuf 3.x +#else + // class ProtobufAbslLogSink : public absl::LogSink { + // void Send(const absl::LogEntry& entry) override; + // }; +#endif +ATOM_LOGGER_CALL(tableau::log::DefaultLogger(), lvl, "[libprotobuf %s:%d] %s", ...); +
+
+
+ +
NEW GO 1.26
-

Generic Tree Map (Go 1.26)

+

Self-Referential Generics for TreeMap

- The internal tree-map index uses Go 1.26's self-referential generics, eliminating runtime type assertions and giving you compile-time-checked nested map access. + pkg/treemap/redblacktree now uses self-referential generics on Go ≥ 1.26 (Lesser[T Lesser[T]]), with a //go:build !go1.26 fallback to keep older toolchains compiling. Tightens type safety for FindIter, UpperBound, LowerBound.

-
▸ treemap.go
+
▸ pkg/treemap/redblacktree/lesser_go126.go
-type TreeMap[K comparable, V any] struct { - children map[K]*TreeMap[K, V] - value V -} +//go:build go1.26 +package redblacktree + +type Lesser[T Lesser[T]] interface { + comparable + Less(other T) bool +}
@@ -443,93 +449,114 @@

Bug Fixes

+
-

C++ GetLastLoadedTime no longer marked inline

-

Removing the keyword fixes ODR violations when the symbol is referenced from multiple translation units.

+

Index field in a list under upper maps now resolves correctly (#158)

+

Reworked LevelMessage to track MapDepth + a new LeveledContainerDepth(). List levels under a parent map now generate the correct number of leveled finders (FindItem1, FindItem2, …). Adds Fruit6Conf.json + 543 lines of index_test.go.

- CPP + INDEX
-

Template includes use original names; OrderedMap accessors adjusted

-

Generated headers now reference the original proto file names instead of munged variants, and OrderedMap iteration helpers were corrected.

+

Index parser skips oneof branches when walking levels (#156)

+

Three-line fix in internal/index/descriptor.go: when iterating a message's fields, descriptors with ContainingOneof() != nil are skipped, removing spurious "field not found" errors caused by oneof name conflicts.

- TEMPLATE + INDEX
-

Windows prepare.bat handles CMake 3.x and admin elevation

-

The bootstrap script now installs the correct CMake major version and prompts for admin rights when required.

+

Field-name case conversion + cross-platform generation order (#153)

+

New underscoresToCamelCase helper unifies property naming across all three plugins; introduces internal/xproto.SplitShards so generation order is identical on Linux / macOS / Windows. New StrcaseConf regression suite covers UserID, V2Ray, XCoordinate, etc.

- WINDOWS + CODEGEN
-

Field-name case conversion + cross-platform generation order

-

Generated output is now byte-identical regardless of build platform. Diff-friendly artifacts unblock reproducible builds.

+

Windows prepare.bat CMake 3.x install + admin elevation prompt (#152, #154)

+

Bootstrap script now winget-installs the right CMake major version and re-launches itself elevated when needed. Subsequent commits absorbed it into make.py setup.

- CODEGEN + WINDOWS
-

Index parser skips oneof fields when walking level messages

-

Level traversal no longer descends into oneof branches, removing spurious "field not found" errors.

+

Templated includes use original proto names; OrderedMap accessors adjusted (#151)

+

Generated C++ hub.pc.cc / hub_shard.pc.cc templates emit the original (non-snake-cased) include names, and the OrderedMap accessor in C++ & C# generators only fires when there's a top-level map.

- INDEX + TEMPLATE
-

Index field in list with upper maps now resolves correctly

-

Fixes a path-resolution bug where index fields nested under both a list and a parent map produced wrong key sequences.

+

C++ GetLastLoadedTime no longer marked inline (#146)

+

The inline keyword on a non-trivial accessor caused ODR violations when the symbol was referenced from multiple translation units; removing it makes the link-time behavior portable.

- INDEX + CPP
+
- +
03 -

Dependencies & Toolchain

+

Toolchain & Dependencies

-

- Major build-system modernization. The protobuf submodule was removed in favor of vcpkg-managed dependencies, the Go diff library was swapped, and CI now runs buf for proto generation. +

+ The biggest non-feature change in v0.6.0 is build-system modernization. The bundled third_party/_submodules/protobuf submodule was removed in favor of vcpkg-managed protobuf with a quarterly-pinned VCPKG_BASELINE_COMMIT. Two variants ship out of the box:

-
- + vcpkg (replaces protobuf submodule) - - protobuf submodule (removed) +
+
+

DEFAULT_VARIANT=modern

+

protobuf 6.33.4

+

vcpkg snapshot pinned to 56bb2411… (tip of 2026.04.27).

+
+
+

DEFAULT_VARIANT=legacy_v3

+

protobuf 3.21.12 (Linux-only smoke)

+

vcpkg snapshot from 2023-01-15 (6245ce44…).

+
+
+ +
+ + vcpkg-managed protobuf + - third_party/_submodules/protobuf (removed) + go-udiff (extracted to pkg/udiff) - - go-difflib (replaced) - + buf (CI proto generation) - + Protobuf Editions 2023/2024 - + Go 1.26 (self-refer generics) - + Recursive submodule checkout in CI + - pmezard/go-difflib (replaced) + + buf v1.67.0 (CI proto generation) + + Editions 2023 / 2024 + + Go 1.24 (devcontainer) · Go 1.26 build tag + + .NET 8.0 SDK + + tableau v0.15.1 + + submodule recursive checkout in CI
-
- Migration note: if you previously built from a checkout that pulled the bundled protobuf submodule, run prepare.bat (Windows) or ./prepare.sh (Linux/macOS) once after upgrading to bootstrap vcpkg-managed dependencies. +
+ Migration note. If you previously cloned with --recurse-submodules for the bundled protobuf, drop that flag and run python3 make.py setup --lang all (or open the repo in the devcontainer). The script bootstraps vcpkg at VCPKG_BASELINE_COMMIT, installs Go / buf / .NET pinned to .devcontainer/versions.env, and is idempotent. +
+ +
+ Heads-up. init.bat, init.sh, and the original prepare.bat were deleted. If your CI or local scripts call them, switch to python3 make.py.
- +
@@ -537,28 +564,27 @@

Dependencies & Toolchain

Examples

-
- FIX · INDEX -

Indexes through list-under-map paths

+ FIX · INDEX (#158) +

Index on a list nested under a parent map

- Before v0.6.0, an index defined on a field nested inside a list that itself sits under a parent map generated an incorrect key path. v0.6.0 walks the parent map context correctly and emits stable lookups. + Consider Fruit6Conf: an outer map keyed by FruitType whose values contain a list of items, with the index Price<ID> and ordered index Price<ID>@OrderedFruit. v0.5.0 generated the wrong number of leveled containers because MapDepth alone didn't capture "list under map". v0.6.0 introduces LeveledContainerDepth(), which adds 1 for non-map levels that still need the parent map's container.

-

▸ HeroSquads.xlsx — Squad

+

▸ Index.xlsx — Fruit6Conf

- + - - - - - - + + + + + +
ABCDABC
1SquadIDMembersHeroIDRole
2map<uint32,Squad>[Member]uint32|{index:"ByHero"}string
3Squad IDMember listIndex by heroMember role
4101001tank
5101002healer
6111001dps
1FruitTypeIDPrice
2map<int32,Fruit>[Item]int32
3Outer map keyList of items (vertical)Indexed by Price<ID>
4199
5179
6299
@@ -567,64 +593,114 @@

Indexes through list-u
✗ Before (v0.5.0)
-// FindByHero(1001) returned only the first squad's match
-// upper map context dropped during index build
-[]Member{ {SquadID:10, HeroID:1001} } +// list under map → 0 leveled finders generated
+// FindItem1(fruitType, price) absent
+items := mgr.FindItem(99) // returns only 1 fruit type
✓ After (v0.6.0)
-// FindByHero(1001) returns all matching members
-// across every squad in the parent map
-[]Member{
- {SquadID:10, HeroID:1001, Role:"tank"},
- {SquadID:11, HeroID:1001, Role:"dps"},
-} +// LCD = MapDepth + 1 → 1 leveled finder
+items := mgr.FindItem1(/*fruitType*/ 1, 99)
+// scoped to the parent map key

- +
+ FIX · ONEOF (#156) +

Oneof branches no longer break level walking

+

+ When a level message embeds a oneof, the parser previously tried to resolve oneof case fields by name and conflicted with regular fields. The fix skips any descriptor whose ContainingOneof() is non-nil. +

+
+
▸ internal/index/descriptor.go (parseCols)
+
+for i := 0; i < md.Fields().Len(); i++ { + fd := md.Fields().Get(i) + if fd.ContainingOneof() != nil { + continue // skip oneof case fields + } + // ... existing logic +} +
+
+
+
NEW · MULTI-LANG -

Same data, four languages

+

One schema, three loaders, identical patch tests

- From a single protobuf schema, v0.6.0 generates idiomatic loader code for Go, C++, C#, and Python. All four use the same patch system, the same indexes, and ship with parity tests. + From a single .proto set, v0.6.0 generates idiomatic Go, C++17, and C# loaders — and three real test suites (testing, gtest, xUnit) running the same PatchConf scenarios against shared testdata/ goldens.

-
+
-
▸ main.go
+
▸ go: main_test.go
-m, _ := loader.New("./conf") -hero := m.Hero().Get(1001) +func prepareHub(t *testing.T) *hub.MyHub { + h := hub.NewMyHub() + h.Load("../testdata/conf/", + format.JSON, + load.IgnoreUnknownFields()) + return h +}
+
-
▸ main.cpp
+
▸ cpp: tests/patch_test.cpp
-tableau::Loader m("./conf"); -auto* hero = m.Hero().Get(1001); +TEST_F(PatchTest, RecursivePatchConf) { + auto opts = NewOptions(); + opts->patch_dirs = { + test::TestPaths::PatchConf().string()}; + Hub::Instance().Load(...); +}
+
-
▸ Program.cs
+
▸ csharp: PatchTests.cs
-var m = new Loader("./conf"); -var hero = m.Hero.Get(1001); +[Fact] +public void PatchReplaceConf() { + var opts = new Load.Options{ + PatchDirs = new(){TestPaths.PatchConfDir}}; + var hub = new Hub(); + hub.Load(...); +}
-
-
▸ main.py
-
-m = Loader("./conf") -hero = m.hero.get(1001) -
+
+
+ +
+ NEW · BUILD +

A single Python script for the whole flow

+

+ make.py uses Python 3.10 stdlib only. It mounts vcpkg, sources vcvarsall.bat per-subprocess on Windows (your shell PATH/INCLUDE/LIB are never mutated), and is itself unit-tested via test_make.py + testing-make.yml. +

+
+
▸ shell — typical contributor flow
+
+# one-time host toolchain (no-op inside devcontainer) +$ python3 make.py setup --lang all + +# per-language +$ python3 make.py test --lang go -k Test_ActivityConf_OrderedMap +$ python3 make.py test --lang cpp --cxx-std 20 --cxx-compiler clang +$ python3 make.py test --lang cpp --protobuf-version 3.21.12 +$ python3 make.py test --lang csharp -k HubTests + +# diagnostic JSON (paths, versions, env probes) +$ python3 make.py env
+
@@ -635,6 +711,7 @@

Same data, four langua
v0.6.0 · Released June 11, 2026 · github.com/tableauio/loader
From 46e0bbcced92c98041ddb55c4d5302f54dfc37cb Mon Sep 17 00:00:00 2001 From: wenchy Date: Fri, 12 Jun 2026 13:53:38 +0800 Subject: [PATCH 4/8] fix(release): tint section titles with their tag accent colour --- .claude/skills/tableau-release-html/SKILL.md | 16 ++++++++++++++-- .../references/design-system.md | 19 +++++++++++-------- static/release/loader-v0-6-0.html | 4 ++-- 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/.claude/skills/tableau-release-html/SKILL.md b/.claude/skills/tableau-release-html/SKILL.md index d905df9e1..6dbdb4af1 100644 --- a/.claude/skills/tableau-release-html/SKILL.md +++ b/.claude/skills/tableau-release-html/SKILL.md @@ -105,19 +105,31 @@ exactly for the page to look correct. Background: CSS grid (`::before`) + ghosted watermark version text + `deco-sheet-wrap` (3D spreadsheet table, absolute positioned bottom-right). -**Section structure** — each major section: +**Section structure** — each major section. The `sec-num` and the `sec-title` MUST share the section's accent colour (the same colour as the section's tag style). This colour pairing is non-negotiable — never leave the title at the default dark. + ```html
01 -

New Features

+

New Features

``` +**Section accent colour matrix** (identical to `tag-*` colours — see `references/design-system.md` for the full matrix): + +| Section | `sec-num` + `sec-title` colour | Matching tag | +|---------|--------------------------------|--------------| +| New Features | `#16a34a` (green) | `tag-new`, `tag-option` | +| Bug Fixes | `#1d4ed8` (blue, deep) — pair with section background `sec-fix` | `tag-tooling` | +| Breaking Changes | `#dc2626` (red) | `tag-breaking` | +| Deprecated | `#d97706` (amber) | `tag-arch` | +| Dependencies | `#6b7280` (slate) — body title may use `#374151` for contrast | n/a | +| Examples | `#7c3aed` (violet) | `tag-enhanced` | + **Feature cards** — grid layout, `border-t-2` accent: ```html
diff --git a/.claude/skills/tableau-release-html/references/design-system.md b/.claude/skills/tableau-release-html/references/design-system.md index 1cc35becb..742e87614 100644 --- a/.claude/skills/tableau-release-html/references/design-system.md +++ b/.claude/skills/tableau-release-html/references/design-system.md @@ -195,14 +195,17 @@ Then use the component colour matrix to apply the right classes. ## Semantic color matrix ### Sections -| Section | Number | CSS class | Accent colour | Title colour | -|---------|--------|-----------|---------------|--------------| -| New Features | 01 | `sec-feat` | `#16a34a` green | `#16a34a` | -| Bug Fixes | 02 | `sec-fix` | `#2563eb` blue | `#1d4ed8` | -| Breaking Changes | 03 | `sec-break` | `#dc2626` red | `#b91c1c` | -| Deprecated | 04 | `sec-dep` | `#d97706` amber | `#92400e` | -| Dependencies | 05 | `sec-white-next-gray` | `#6b7280` slate | `#6b7280` | -| Examples | 06 | `sec-gray` | `#7c3aed` violet | default | + +The `sec-num` AND `sec-title` of every section MUST be set to the accent colour below — the same colour as the section's matching tag. Title left at the default dark is a bug. + +| Section | Number | CSS class | `sec-num` + `sec-title` colour | +|---------|--------|-----------|--------------------------------| +| New Features | 01 | `sec-feat` | `#16a34a` (green) | +| Bug Fixes | 02 | `sec-fix` | `#1d4ed8` (deep blue — readable on the light-blue background) | +| Breaking Changes | 03 | `sec-break` | `#dc2626` (red, or `#b91c1c` for slightly darker) | +| Deprecated | 04 | `sec-dep` | `#d97706` (amber, or `#92400e` for darker) | +| Dependencies | 05 | `sec-white-next-gray` | `#6b7280` (slate) — body title may use `#374151` for contrast | +| Examples | 06 | `sec-gray` | `#7c3aed` (violet) | ### Tags (`.tag` base class) | Class | Use | diff --git a/static/release/loader-v0-6-0.html b/static/release/loader-v0-6-0.html index d554169c6..8dac48583 100644 --- a/static/release/loader-v0-6-0.html +++ b/static/release/loader-v0-6-0.html @@ -276,7 +276,7 @@

v0.
01 -

New Features

+

New Features

@@ -561,7 +561,7 @@

Toolchain & Dependencies

04 -

Examples

+

Examples

From 2a1b2a79641d813c86b99af998c6861f0735a023 Mon Sep 17 00:00:00 2001 From: wenchy Date: Fri, 12 Jun 2026 14:08:38 +0800 Subject: [PATCH 5/8] fix(dev): drop --poll, rely on fsnotify (cleaner Ctrl-C, less CPU) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6062c25a9..03d315539 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "init": "shx rm -rf .git && git init -b main", "create": "exec-bin node_modules/.bin/hugo/hugo new", "prestart": "npm run clean", - "start": "exec-bin node_modules/.bin/hugo/hugo server --bind=0.0.0.0 --disableFastRender --poll 700ms --forceSyncStatic --navigateToChanged", + "start": "exec-bin node_modules/.bin/hugo/hugo server --bind=0.0.0.0 --disableFastRender --forceSyncStatic --navigateToChanged", "prebuild": "npm run clean", "build": "exec-bin node_modules/.bin/hugo/hugo --gc --minify", "build:preview": "npm run build -D -F", From 237c90a13aa33c8088288c8798adb683479d4db9 Mon Sep 17 00:00:00 2001 From: wenchy Date: Fri, 12 Jun 2026 14:16:00 +0800 Subject: [PATCH 6/8] fix: tableau version --- config/_default/params.toml | 2 +- content/en/docs/basics/enum.md | 2 +- content/zh/docs/basics/enum.md | 2 +- static/release/loader-v0-6-0.html | 2904 ++++++++++++++++++++++------- 4 files changed, 2226 insertions(+), 684 deletions(-) diff --git a/config/_default/params.toml b/config/_default/params.toml index be1c379df..07bfef176 100644 --- a/config/_default/params.toml +++ b/config/_default/params.toml @@ -10,7 +10,7 @@ description = "Tableau can convert Excel/CSV/XML/YAML to multiple formats: JSON, # docsVersion = "0.3" ## Tableauc version for download links -tableaucVersion = "v0.15.1" +tableaucVersion = "v0.16.0" ## Open Graph images = ["tableau-logo.svg"] diff --git a/content/en/docs/basics/enum.md b/content/en/docs/basics/enum.md index d38d2377a..f64209228 100644 --- a/content/en/docs/basics/enum.md +++ b/content/en/docs/basics/enum.md @@ -16,7 +16,7 @@ The tableau parser accepts three enum value forms: 1. enum value **name**. 2. enum value **number**. - 3. enum value **alias**. It is another name in English, Chinese, or any other language, which can be specified by [tableau.evalue](https://github.com/tableauio/tableau/blob/v0.15.1/proto/tableau/protobuf/tableau.proto#L39) by extending [google.protobuf.EnumValueOptions](https://github.com/protocolbuffers/protobuf/blob/v34.0/src/google/protobuf/descriptor.proto#L904). + 3. enum value **alias**. It is another name in English, Chinese, or any other language, which can be specified by [tableau.evalue](https://github.com/tableauio/tableau/blob/v0.16.0/proto/tableau/protobuf/tableau.proto#L39) by extending [google.protobuf.EnumValueOptions](https://github.com/protocolbuffers/protobuf/blob/v34.0/src/google/protobuf/descriptor.proto#L904). For example, enum type `FruitType` in `common.proto` is defined as: diff --git a/content/zh/docs/basics/enum.md b/content/zh/docs/basics/enum.md index 7dd21458c..9265ecc7d 100644 --- a/content/zh/docs/basics/enum.md +++ b/content/zh/docs/basics/enum.md @@ -16,7 +16,7 @@ tableau 解析器支持三种枚举值形式: 1. 枚举值**名称**(name)。 2. 枚举值**编号**(number)。 - 3. 枚举值**别名**(alias)。别名可以是英文、中文或其他任意语言,通过 [tableau.evalue](https://github.com/tableauio/tableau/blob/v0.15.1/proto/tableau/protobuf/tableau.proto#L39) 扩展 [google.protobuf.EnumValueOptions](https://github.com/protocolbuffers/protobuf/blob/v34.0/src/google/protobuf/descriptor.proto#L904) 来指定。 + 3. 枚举值**别名**(alias)。别名可以是英文、中文或其他任意语言,通过 [tableau.evalue](https://github.com/tableauio/tableau/blob/v0.16.0/proto/tableau/protobuf/tableau.proto#L39) 扩展 [google.protobuf.EnumValueOptions](https://github.com/protocolbuffers/protobuf/blob/v34.0/src/google/protobuf/descriptor.proto#L904) 来指定。 例如,`common.proto` 中定义的枚举类型 `FruitType`: diff --git a/static/release/loader-v0-6-0.html b/static/release/loader-v0-6-0.html index 8dac48583..49d0697a3 100644 --- a/static/release/loader-v0-6-0.html +++ b/static/release/loader-v0-6-0.html @@ -1,734 +1,2276 @@ - + - - - - Tableau Loader v0.6.0 — Release Notes - - - - - + - - - - - - - - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ABCDEF
1FruitTypeIDPriceNameTagStock
2map<int32,Fruit>map<int32,Item>int32stringstringint32
3Outer keyInner keyIndexed fieldDisplayTagStock
4110099Applefresh120
5110179Pearfresh80
6220099Berryfrozen40
-
- -
- - -
-
- Tableau - Tableau Loader - | - v0.6.0 - | - - - Released June 11, 2026 - + }; + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ABCDEF
1FruitTypeIDPriceNameTagStock
2map<int32,Fruit>map<int32,Item>int32stringstringint32
3Outer keyInner keyIndexed fieldDisplayTagStock
4110099Applefresh120
5110179Pearfresh80
6220099Berryfrozen40
-

RELEASE NOTES

- -

v0.6.0

+
+ -

- First-class C# loader plugin, Protobuf Editions 2023/2024 across all three plugins, a unified make.py build pipeline replacing init.bat / init.sh / prepare.bat, and vcpkg-managed protobuf with a quarterly-pinned matrix. -

+
+
+ Tableau + Tableau Loader + | + v0.6.0 + | + + + Released June 11, 2026 + +
-
- + 7 Features - ○ 6 Bug Fixes - ⟲ 3 Refactors - | - Go · C++17 · C# (Unity 2022.3 LTS / .NET 8) · Editions 2023/2024 · Go 1.26 -
-
-
- -
-
- - -
-
-
- 01 -

New Features

-
- -
- -
-
- NEW - PLUGIN -
-

C# Loader Plugin

-

- New protoc-gen-csharp-tableau-loader plugin. Targets Unity 2022.3 LTS / .NET 8, generates *.pc.cs. Same Hub / Messager / Load API surface as Go & C++, including patch loading, MessagerOptions, custom ReadFunc/LoadFunc, and a thread-safe Atomic<T>-backed Hub. -

-
-
▸ Program.cs
-
-var hub = new Tableau.Hub(); -var opts = new Tableau.Load.Options { - IgnoreUnknownFields = true, - PatchDirs = new() { "./patch" }, -}; -hub.Load("./conf", Format.JSON, opts); -var conf = hub.GetItemConf(); +

+ RELEASE NOTES +

+ +

+ v0.6.0 +

+ +

+ First-class C# loader plugin, Protobuf Editions 2023/2024 across all + three plugins, a unified + make.py + build pipeline replacing init.bat / init.sh / prepare.bat, and + vcpkg-managed protobuf with a quarterly-pinned matrix. +

+ +
+ + 7 Features + ○ 6 Bug Fixes + ⟲ 3 Refactors + | + Go · C++17 · C# (Unity 2022.3 LTS / .NET 8) · Editions 2023/2024 + · Go 1.26
-
-
- NEW - EDITIONS +
+ ↓ +
+
+ + +
+
+
+ 01 +

New Features

-

Protobuf Editions 2023 & 2024

-

- All three plugins (Go / C++ / C#) emit code that compiles cleanly under edition = "2023" and "2024" alongside proto2 / proto3. The build toolchain in init.sh / init.bat was modernized to install a matching protoc. -

-
-
▸ schema.proto
-
-// proto2 / proto3 still supported -// new: Editions 2023 & 2024 -edition = "2024"; -package protoconf; -message ItemConf { ... } + +
+
+
+ NEW + PLUGIN +
+

+ C# Loader Plugin +

+

+ New + protoc-gen-csharp-tableau-loader + plugin. Targets Unity 2022.3 LTS / .NET 8, + generates *.pc.cs. Same Hub / + Messager / Load API surface as Go & C++, including patch + loading, MessagerOptions, custom + ReadFunc/LoadFunc, and a thread-safe + Atomic<T>-backed Hub. +

+
+
▸ Program.cs
+
+ var hub = + new + Tableau.Hub(); + var opts = + new + Tableau.Load.Options { + + IgnoreUnknownFields = + true, + + PatchDirs = + new() { + "./patch" }, + }; + hub.Load("./conf", Format.JSON, opts); + var conf = hub.GetItemConf(); +
+
-
-
-
-
- NEW - PATCH · TESTS -
-

Cross-Language Patch + Real Test Suites

-

- Three real test suites — Go (testing), C++ (gtest), C# (xUnit) — exercise the same patch scenarios with golden-file parity. Standalone main.go / main.cpp / Program.cs demos were retired. -

-
-
▸ patch matrix
-
-✓ go main_test.go::Test_Patch_* PASS -✓ cpp tests/patch_test.cpp PatchConf_* PASS -✓ csharp tests/PatchTests.cs PatchConf_* PASS -// All three load the same testdata/conf + testdata/patchconf -// and compare to testdata/patchresult/ goldens. +
+
+ NEW + EDITIONS +
+

+ Protobuf Editions 2023 & 2024 +

+

+ All three plugins (Go / C++ / C#) emit code that compiles cleanly + under edition = "2023" and + "2024" alongside proto2 / proto3. The + build toolchain in init.sh / + init.bat was modernized to install a + matching protoc. +

+
+
▸ schema.proto
+
+ // proto2 / proto3 still supported + // new: Editions 2023 & 2024 + edition = + "2024"; + package protoconf; + message + ItemConf { ... } +
+
-
-
-
-
- NEW - BUILD -
-

Unified make.py Pipeline

-

- One Python 3.10+ stdlib-only entry point replaces init.sh, init.bat, and prepare.bat. Subcommands: setup, generate, build, test, clean, env. CI runs the same script as developers. -

-
-
▸ shell
-
-$ python3 make.py setup --lang all -$ python3 make.py test --lang go -$ python3 make.py test --lang cpp # vcpkg-installed protobuf -$ python3 make.py test --lang csharp -k HubTests -$ python3 make.py test --lang cpp --protobuf-version 3.21.12 +
+
+ NEW + PATCH · TESTS +
+

+ Cross-Language Patch + Real Test Suites +

+

+ Three real test suites — Go (testing), C++ (gtest), C# (xUnit) — exercise the same patch scenarios with golden-file parity. + Standalone main.go / + main.cpp / + Program.cs demos were retired. +

+
+
▸ patch matrix
+
+ ✓ go main_test.go::Test_Patch_* PASS + ✓ cpp tests/patch_test.cpp PatchConf_* PASS + ✓ csharp tests/PatchTests.cs PatchConf_* PASS + // All three load the same testdata/conf + + testdata/patchconf + // and compare to testdata/patchresult/ goldens. +
+
-
-
-
-
- NEW - DEV ENV -
-

Devcontainer & buf Codegen

-

- Reproducible dev environment via .devcontainer/: VS Code → "Reopen in Container" gives the same Go, buf, vcpkg, and protobuf versions used in CI. buf generate replaces the per-language gen.sh / gen.bat scripts in every test workspace. -

-
-
▸ .devcontainer/versions.env
-
-GO_VERSION=1.24.0 -BUF_VERSION=1.67.0 -DOTNET_VERSION=8.0 -DEFAULT_VARIANT=modern -MODERN_PROTOBUF_VERSION=6.33.4 -LEGACY_V3_PROTOBUF_VERSION=3.21.12 +
+
+ NEW + BUILD +
+

+ Unified make.py Pipeline +

+

+ One Python 3.10+ stdlib-only entry point replaces + init.sh, + init.bat, and + prepare.bat. Subcommands: + setup, + generate, + build, + test, + clean, + env. CI runs the same script as + developers. +

+
+
▸ shell
+
+ $ python3 make.py setup --lang + all + $ python3 make.py test --lang + go + $ python3 make.py test --lang cpp + # vcpkg-installed protobuf + $ python3 make.py test --lang + csharp -k HubTests + $ python3 make.py test --lang cpp + --protobuf-version 3.21.12 +
+
-
-
-
-
- NEW - LOGGING -
-

Dual-Mode Protobuf Log Handler (C++)

-

- The C++ runtime now installs a Tableau log sink on libprotobuf — routing [libprotobuf] messages into the Tableau logger. Compile-time TABLEAU_PB_LOG_LEGACY picks legacy SetLogHandler (protobuf ≤ 3.x) or modern absl::LogSink (protobuf ≥ 4.x with Abseil). -

-
-
▸ util.pc.cc
-
-#if TABLEAU_PB_LOG_LEGACY - // SetLogHandler(ProtobufLogHandler) on protobuf 3.x -#else - // class ProtobufAbslLogSink : public absl::LogSink { - // void Send(const absl::LogEntry& entry) override; - // }; -#endif -ATOM_LOGGER_CALL(tableau::log::DefaultLogger(), lvl, "[libprotobuf %s:%d] %s", ...); +
+
+ NEW + DEV ENV +
+

+ Devcontainer & buf Codegen +

+

+ Reproducible dev environment via + .devcontainer/: VS Code → "Reopen in + Container" gives the same Go, buf, + vcpkg, and protobuf versions used in CI. + buf generate replaces the + per-language gen.sh / + gen.bat scripts in every test + workspace. +

+
+
▸ .devcontainer/versions.env
+
+ GO_VERSION=1.24.0 + BUF_VERSION=1.67.0 + DOTNET_VERSION=8.0 + DEFAULT_VARIANT=modern + MODERN_PROTOBUF_VERSION=6.33.4 + LEGACY_V3_PROTOBUF_VERSION=3.21.12 +
+
-
-
-
-
- NEW - GO 1.26 -
-

Self-Referential Generics for TreeMap

-

- pkg/treemap/redblacktree now uses self-referential generics on Go ≥ 1.26 (Lesser[T Lesser[T]]), with a //go:build !go1.26 fallback to keep older toolchains compiling. Tightens type safety for FindIter, UpperBound, LowerBound. -

-
-
▸ pkg/treemap/redblacktree/lesser_go126.go
-
-//go:build go1.26 -package redblacktree - -type Lesser[T Lesser[T]] interface { - comparable - Less(other T) bool -} +
+
+ NEW + LOGGING +
+

+ Dual-Mode Protobuf Log Handler (C++) +

+

+ The C++ runtime now installs a Tableau log sink on libprotobuf — + routing [libprotobuf] messages into + the Tableau logger. Compile-time + TABLEAU_PB_LOG_LEGACY picks legacy + SetLogHandler (protobuf ≤ 3.x) or + modern absl::LogSink (protobuf ≥ 4.x + with Abseil). +

+
+
▸ util.pc.cc
+
+ #if TABLEAU_PB_LOG_LEGACY + + // SetLogHandler(ProtobufLogHandler) on protobuf 3.x + #else + + // class ProtobufAbslLogSink : public absl::LogSink { + + // void Send(const absl::LogEntry& entry) override; + // }; + #endif + ATOM_LOGGER_CALL(tableau::log::DefaultLogger(), lvl, "[libprotobuf %s:%d] %s", + ...); +
+
-
-
-
-
-
- - -
-
-
- 02 -

Bug Fixes

-
- -
- -
- -
-

Index field in a list under upper maps now resolves correctly (#158)

-

Reworked LevelMessage to track MapDepth + a new LeveledContainerDepth(). List levels under a parent map now generate the correct number of leveled finders (FindItem1, FindItem2, …). Adds Fruit6Conf.json + 543 lines of index_test.go.

+
+
+ NEW + GO 1.26 +
+

+ Self-Referential Generics for TreeMap +

+

+ pkg/treemap/redblacktree now uses + self-referential generics on Go ≥ 1.26 (Lesser[T Lesser[T]]), with a + //go:build !go1.26 fallback to keep + older toolchains compiling. Tightens type safety for + FindIter, + UpperBound, + LowerBound. +

+
+
+ ▸ pkg/treemap/redblacktree/lesser_go126.go +
+
+ //go:build go1.26 + package redblacktree + + type + Lesser[T + Lesser[T]] + interface { + comparable + + Less(other T) + bool + } +
+
+
- INDEX
- -
- -
-

Index parser skips oneof branches when walking levels (#156)

-

Three-line fix in internal/index/descriptor.go: when iterating a message's fields, descriptors with ContainingOneof() != nil are skipped, removing spurious "field not found" errors caused by oneof name conflicts.

+
+ + +
+
+
+ 02 +

Bug Fixes

- INDEX -
-
- -
-

Field-name case conversion + cross-platform generation order (#153)

-

New underscoresToCamelCase helper unifies property naming across all three plugins; introduces internal/xproto.SplitShards so generation order is identical on Linux / macOS / Windows. New StrcaseConf regression suite covers UserID, V2Ray, XCoordinate, etc.

+
+
+ +
+

+ Index field in a list under upper maps now resolves correctly + (#158) +

+

+ Reworked + LevelMessage + to track + MapDepth + + a new + LeveledContainerDepth(). List levels under a parent map now generate the correct + number of leveled finders (FindItem1, + FindItem2, …). Adds Fruit6Conf.json + 543 + lines of index_test.go. +

+
+ INDEX +
+ +
+ +
+

+ Index parser skips + oneof + branches when walking levels + (#156) +

+

+ Three-line fix in + internal/index/descriptor.go: when iterating a message's fields, descriptors with + ContainingOneof() != nil + are skipped, removing spurious "field not found" errors caused + by oneof name conflicts. +

+
+ INDEX +
+ +
+ +
+

+ Field-name case conversion + cross-platform generation order + (#153) +

+

+ New + underscoresToCamelCase + helper unifies property naming across all three plugins; + introduces + internal/xproto.SplitShards + so generation order is identical on Linux / macOS / Windows. New + StrcaseConf regression suite covers + UserID, + V2Ray, + XCoordinate, etc. +

+
+ CODEGEN +
+ +
+ +
+

+ Windows + prepare.bat + CMake 3.x install + admin elevation prompt + (#152, #154) +

+

+ Bootstrap script now winget-installs the right CMake major + version and re-launches itself elevated when needed. Subsequent + commits absorbed it into + make.py setup. +

+
+ WINDOWS +
+ +
+ +
+

+ Templated includes use original proto names; OrderedMap + accessors adjusted + (#151) +

+

+ Generated C++ + hub.pc.cc + / + hub_shard.pc.cc + templates emit the original (non-snake-cased) include names, and + the OrderedMap accessor in C++ & C# generators only fires + when there's a top-level map. +

+
+ TEMPLATE +
+ +
+ +
+

+ C++ + GetLastLoadedTime + no longer marked + inline + (#146) +

+

+ The inline keyword on a non-trivial + accessor caused ODR violations when the symbol was referenced + from multiple translation units; removing it makes the link-time + behavior portable. +

+
+ CPP +
- CODEGEN
+
+ + +
+
+
+ 03 +

+ Toolchain & Dependencies +

+
-
- -
-

Windows prepare.bat CMake 3.x install + admin elevation prompt (#152, #154)

-

Bootstrap script now winget-installs the right CMake major version and re-launches itself elevated when needed. Subsequent commits absorbed it into make.py setup.

+

+ The biggest non-feature change in v0.6.0 is build-system + modernization. The bundled + third_party/_submodules/protobuf + submodule was removed in favor of vcpkg-managed + protobuf with a quarterly-pinned + VCPKG_BASELINE_COMMIT. Two variants ship + out of the box: +

+ +
+
+

+ DEFAULT_VARIANT=modern +

+

+ protobuf 6.33.4 +

+

+ vcpkg snapshot pinned to + 56bb2411… (tip of + 2026.04.27). +

+
+
+

+ DEFAULT_VARIANT=legacy_v3 +

+

+ protobuf 3.21.12 (Linux-only smoke) +

+

+ vcpkg snapshot from 2023-01-15 (6245ce44…). +

+
- WINDOWS -
-
- -
-

Templated includes use original proto names; OrderedMap accessors adjusted (#151)

-

Generated C++ hub.pc.cc / hub_shard.pc.cc templates emit the original (non-snake-cased) include names, and the OrderedMap accessor in C++ & C# generators only fires when there's a top-level map.

+
+ + vcpkg-managed protobuf + - third_party/_submodules/protobuf (removed) + + go-udiff (extracted to pkg/udiff) + - pmezard/go-difflib (replaced) + + buf v1.67.0 (CI proto generation) + + Editions 2023 / 2024 + + Go 1.24 (devcontainer) · Go 1.26 build tag + + .NET 8.0 SDK + + tableau v0.16.0 + + submodule recursive checkout in CI
- TEMPLATE -
-
- -
-

C++ GetLastLoadedTime no longer marked inline (#146)

-

The inline keyword on a non-trivial accessor caused ODR violations when the symbol was referenced from multiple translation units; removing it makes the link-time behavior portable.

+
+ Migration note. If you + previously cloned with --recurse-submodules for the + bundled protobuf, drop that flag and run + python3 make.py setup --lang all (or open the repo in the + devcontainer). The script bootstraps vcpkg at + VCPKG_BASELINE_COMMIT, installs Go / buf / .NET pinned to + .devcontainer/versions.env, and is idempotent.
- CPP -
-
-
-
- - -
-
-
- 03 -

Toolchain & Dependencies

-
- -

- The biggest non-feature change in v0.6.0 is build-system modernization. The bundled third_party/_submodules/protobuf submodule was removed in favor of vcpkg-managed protobuf with a quarterly-pinned VCPKG_BASELINE_COMMIT. Two variants ship out of the box: -

- -
-
-

DEFAULT_VARIANT=modern

-

protobuf 6.33.4

-

vcpkg snapshot pinned to 56bb2411… (tip of 2026.04.27).

-
-
-

DEFAULT_VARIANT=legacy_v3

-

protobuf 3.21.12 (Linux-only smoke)

-

vcpkg snapshot from 2023-01-15 (6245ce44…).

-
-
- -
- + vcpkg-managed protobuf - - third_party/_submodules/protobuf (removed) - + go-udiff (extracted to pkg/udiff) - - pmezard/go-difflib (replaced) - + buf v1.67.0 (CI proto generation) - + Editions 2023 / 2024 - + Go 1.24 (devcontainer) · Go 1.26 build tag - + .NET 8.0 SDK - + tableau v0.15.1 - + submodule recursive checkout in CI -
- -
- Migration note. If you previously cloned with --recurse-submodules for the bundled protobuf, drop that flag and run python3 make.py setup --lang all (or open the repo in the devcontainer). The script bootstraps vcpkg at VCPKG_BASELINE_COMMIT, installs Go / buf / .NET pinned to .devcontainer/versions.env, and is idempotent. -
- -
- Heads-up. init.bat, init.sh, and the original prepare.bat were deleted. If your CI or local scripts call them, switch to python3 make.py. -
-
-
- - -
-
-
- 04 -

Examples

-
- -
- FIX · INDEX (#158) -

Index on a list nested under a parent map

-

- Consider Fruit6Conf: an outer map keyed by FruitType whose values contain a list of items, with the index Price<ID> and ordered index Price<ID>@OrderedFruit. v0.5.0 generated the wrong number of leveled containers because MapDepth alone didn't capture "list under map". v0.6.0 introduces LeveledContainerDepth(), which adds 1 for non-map levels that still need the parent map's container. -

- -

▸ Index.xlsx — Fruit6Conf

-
- - - - - - - - - - - - - -
ABC
1FruitTypeIDPrice
2map<int32,Fruit>[Item]int32
3Outer map keyList of items (vertical)Indexed by Price<ID>
4199
5179
6299
+
+ Heads-up. + init.bat, init.sh, and the original + prepare.bat were deleted. If your CI or local scripts + call them, switch to python3 make.py. +
+
+ + +
+
+
+ 04 +

Examples

+
-
-
-
✗ Before (v0.5.0)
-
-// list under map → 0 leveled finders generated
-// FindItem1(fruitType, price) absent
-items := mgr.FindItem(99) // returns only 1 fruit type +
+ FIX · INDEX (#158) +

+ Index on a list nested under a parent map +

+

+ Consider Fruit6Conf: an outer map keyed + by + FruitType + whose values contain a list of items, with the index + Price<ID> + and ordered index + Price<ID>@OrderedFruit. v0.5.0 generated the wrong number of leveled containers because + MapDepth + alone didn't capture "list under map". v0.6.0 introduces + LeveledContainerDepth(), which adds 1 for non-map levels that still need the parent map's + container. +

+ +

▸ Index.xlsx — Fruit6Conf

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ABC
1FruitTypeIDPrice
2map<int32,Fruit>[Item]int32
3Outer map keyList of items (vertical)Indexed by Price<ID>
4199
5179
6299
-
-
-
✓ After (v0.6.0)
-
-// LCD = MapDepth + 1 → 1 leveled finder
-items := mgr.FindItem1(/*fruitType*/ 1, 99)
-// scoped to the parent map key + +
+
+
✗ Before (v0.5.0)
+
+ // list under map → 0 leveled finders generated
+ // FindItem1(fruitType, price) absent
+ items := mgr.FindItem(99) // returns only 1 fruit type +
+
+
+
✓ After (v0.6.0)
+
+ // LCD = MapDepth + 1 → 1 leveled finder
+ items := mgr.FindItem1(/*fruitType*/ + 1, 99)
+ // scoped to the parent map key +
+
-
-
- -
- FIX · ONEOF (#156) -

Oneof branches no longer break level walking

-

- When a level message embeds a oneof, the parser previously tried to resolve oneof case fields by name and conflicted with regular fields. The fix skips any descriptor whose ContainingOneof() is non-nil. -

-
-
▸ internal/index/descriptor.go (parseCols)
-
-for i := 0; i < md.Fields().Len(); i++ { - fd := md.Fields().Get(i) - if fd.ContainingOneof() != nil { - continue // skip oneof case fields - } - // ... existing logic -} -
-
-
- -
- NEW · MULTI-LANG -

One schema, three loaders, identical patch tests

-

- From a single .proto set, v0.6.0 generates idiomatic Go, C++17, and C# loaders — and three real test suites (testing, gtest, xUnit) running the same PatchConf scenarios against shared testdata/ goldens. -

- -
-
-
▸ go: main_test.go
-
-func prepareHub(t *testing.T) *hub.MyHub { - h := hub.NewMyHub() - h.Load("../testdata/conf/", - format.JSON, - load.IgnoreUnknownFields()) - return h -} + +
+ FIX · ONEOF (#156) +

+ Oneof branches no longer break level walking +

+

+ When a level message embeds a + oneof, the parser previously tried to resolve oneof case fields by name + and conflicted with regular fields. The fix skips any descriptor + whose + ContainingOneof() + is non-nil. +

+
+
+ ▸ internal/index/descriptor.go (parseCols) +
+
+ for i := + 0; i < md.Fields().Len(); i++ { + + fd := md.Fields().Get(i) + + if fd.ContainingOneof() != nil { + + continue + // skip oneof case fields + } + // ... existing logic + } +
-
-
▸ cpp: tests/patch_test.cpp
-
-TEST_F(PatchTest, RecursivePatchConf) { - auto opts = NewOptions(); - opts->patch_dirs = { - test::TestPaths::PatchConf().string()}; - Hub::Instance().Load(...); -} +
+ NEW · MULTI-LANG +

+ One schema, three loaders, identical patch tests +

+

+ From a single .proto set, v0.6.0 + generates idiomatic Go, C++17, and C# loaders — and three real test + suites (testing, + gtest, + xUnit) running the same + PatchConf scenarios against shared + testdata/ goldens. +

+ +
+
+
▸ go: main_test.go
+
+ func + prepareHub(t *testing.T) *hub.MyHub { + h := hub.NewMyHub() + + h.Load("../testdata/conf/", + + format.JSON, + + load.IgnoreUnknownFields()) + return h + } +
+
+ +
+
▸ cpp: tests/patch_test.cpp
+
+ TEST_F(PatchTest, + RecursivePatchConf) { + + auto opts = + NewOptions(); + opts->patch_dirs = { + + test::TestPaths::PatchConf().string()}; + + Hub::Instance().Load(...); + } +
+
+ +
+
▸ csharp: PatchTests.cs
+
+ [Fact] + public void + PatchReplaceConf() { + + var opts = + new + Load.Options{ + + PatchDirs = + new(){TestPaths.PatchConfDir}}; + + var hub = + new + Hub(); + hub.Load(...); + } +
+
-
-
▸ csharp: PatchTests.cs
-
-[Fact] -public void PatchReplaceConf() { - var opts = new Load.Options{ - PatchDirs = new(){TestPaths.PatchConfDir}}; - var hub = new Hub(); - hub.Load(...); -} +
+ NEW · BUILD +

+ A single Python script for the whole flow +

+

+ make.py + uses Python 3.10 stdlib only. It mounts vcpkg, sources + vcvarsall.bat + per-subprocess on Windows (your shell PATH/INCLUDE/LIB are never + mutated), and is itself unit-tested via + test_make.py + + + testing-make.yml. +

+
+
▸ shell — typical contributor flow
+
+ # one-time host toolchain (no-op inside devcontainer) + $ python3 make.py setup --lang + all + + # per-language + $ python3 make.py test --lang go -k + Test_ActivityConf_OrderedMap + $ python3 make.py test --lang cpp + --cxx-std 20 --cxx-compiler clang + $ python3 make.py test --lang cpp + --protobuf-version 3.21.12 + $ python3 make.py test --lang csharp + -k HubTests + + # diagnostic JSON (paths, versions, env probes) + $ python3 make.py env +
-
- -
- NEW · BUILD -

A single Python script for the whole flow

-

- make.py uses Python 3.10 stdlib only. It mounts vcpkg, sources vcvarsall.bat per-subprocess on Windows (your shell PATH/INCLUDE/LIB are never mutated), and is itself unit-tested via test_make.py + testing-make.yml. -

-
-
▸ shell — typical contributor flow
-
-# one-time host toolchain (no-op inside devcontainer) -$ python3 make.py setup --lang all - -# per-language -$ python3 make.py test --lang go -k Test_ActivityConf_OrderedMap -$ python3 make.py test --lang cpp --cxx-std 20 --cxx-compiler clang -$ python3 make.py test --lang cpp --protobuf-version 3.21.12 -$ python3 make.py test --lang csharp -k HubTests - -# diagnostic JSON (paths, versions, env probes) -$ python3 make.py env +
+ + +
- -
-
- - - - - - - + + + + From f166e380e663fb71da8f9fcf00fa7d454dfa5990 Mon Sep 17 00:00:00 2001 From: wenchy Date: Fri, 12 Jun 2026 15:06:02 +0800 Subject: [PATCH 7/8] fix(release): code blocks use
 with literal
 newlines and per-block scroll

---
 static/release/loader-v0-6-0.html | 444 ++++++++++--------------------
 1 file changed, 142 insertions(+), 302 deletions(-)

diff --git a/static/release/loader-v0-6-0.html b/static/release/loader-v0-6-0.html
index 49d0697a3..688fd4cbc 100644
--- a/static/release/loader-v0-6-0.html
+++ b/static/release/loader-v0-6-0.html
@@ -352,12 +352,53 @@
         display: none;
       }
       .code-blk-bd {
+        padding: 0;
+        background: #fff;
+      }
+      .code-blk-bd > pre {
+        margin: 0;
         padding: 14px 16px;
         font-family: "JetBrains Mono", monospace;
         font-size: 12px;
         line-height: 1.8;
-        overflow-x: auto;
         color: #24292f;
+        overflow-x: auto;
+        white-space: pre;
+        tab-size: 2;
+      }
+      .code-blk-bd > pre > code {
+        font-family: inherit;
+        background: none;
+        padding: 0;
+        white-space: pre;
+        display: block;
+        min-width: max-content;
+      }
+      .code-blk-bd > pre > code .c-del,
+      .code-blk-bd > pre > code .c-add,
+      .code-blk-bd > pre > code .c-com,
+      .code-blk-bd > pre > code .c-inf,
+      .code-blk-bd > pre > code .c-hl,
+      .code-blk-bd > pre > code .c-warn,
+      .demo-out-bd > pre > code .c-com,
+      .demo-out-bd > pre > code .c-inf {
+        display: inline;
+        margin: 0;
+        padding: 0;
+        border-left: 0;
+      }
+      .code-blk-bd > pre > code .c-del,
+      .code-blk-bd > pre > code .c-add,
+      .code-blk-bd > pre > code .c-com,
+      .code-blk-bd > pre > code .c-inf,
+      .code-blk-bd > pre > code .c-hl,
+      .code-blk-bd > pre > code .c-warn,
+      .demo-out-bd > pre > code .c-com,
+      .demo-out-bd > pre > code .c-inf {
+        display: inline;
+        margin: 0;
+        padding: 0;
+        border-left: 0;
       }
       .c-del {
         color: #82071e;
@@ -440,13 +481,27 @@
         letter-spacing: 0.04em;
       }
       .demo-out-bd {
-        padding: 14px 16px;
+        padding: 0;
         background: #fff;
+      }
+      .demo-out-bd > pre {
+        margin: 0;
+        padding: 14px 16px;
         font-family: "JetBrains Mono", monospace;
         font-size: 12px;
         line-height: 1.8;
-        overflow-x: auto;
         color: #24292f;
+        overflow-x: auto;
+        white-space: pre;
+        tab-size: 2;
+      }
+      .demo-out-bd > pre > code {
+        font-family: inherit;
+        background: none;
+        padding: 0;
+        white-space: pre;
+        display: block;
+        min-width: max-content;
       }
 
       .sbs-wrap {
@@ -1145,42 +1200,13 @@ 

▸ Program.cs
-
- var hub = - new - Tableau.Hub(); - var opts = - new - Tableau.Load.Options { - - IgnoreUnknownFields = - true, - - PatchDirs = - new() { - "./patch" }, - }; - hub.Load("./conf", Format.JSON, opts); - var conf = hub.GetItemConf(); -
+
var hub  = new Tableau.Hub();
+var opts = new Tableau.Load.Options {
+    IgnoreUnknownFields = true,
+    PatchDirs           = new() { "./patch" },
+};
+hub.Load("./conf", Format.JSON, opts);
+var conf = hub.GetItemConf();

@@ -1205,19 +1231,11 @@

▸ schema.proto
-
- // proto2 / proto3 still supported - // new: Editions 2023 & 2024 - edition = - "2024"; - package protoconf; - message - ItemConf { ... } -
+
// proto2 / proto3 still supported
+// new: Editions 2023 & 2024
+edition = "2024";
+package protoconf;
+message ItemConf { ... }

@@ -1244,24 +1262,11 @@

▸ patch matrix
-
- ✓ go main_test.go::Test_Patch_* PASS - ✓ cpp tests/patch_test.cpp PatchConf_* PASS - ✓ csharp tests/PatchTests.cs PatchConf_* PASS - // All three load the same testdata/conf + - testdata/patchconf - // and compare to testdata/patchresult/ goldens. -
+
✓ go     main_test.go::Test_Patch_*           PASS
+✓ cpp    tests/patch_test.cpp PatchConf_*     PASS
+✓ csharp tests/PatchTests.cs PatchConf_*       PASS
+// All three load the same testdata/conf + testdata/patchconf
+// and compare to testdata/patchresult/ goldens.

@@ -1291,28 +1296,11 @@

▸ shell
-
- $ python3 make.py setup --lang - all - $ python3 make.py test --lang - go - $ python3 make.py test --lang cpp - # vcpkg-installed protobuf - $ python3 make.py test --lang - csharp -k HubTests - $ python3 make.py test --lang cpp - --protobuf-version 3.21.12 -
+
$ python3 make.py setup --lang all
+$ python3 make.py test  --lang go
+$ python3 make.py test  --lang cpp     # vcpkg-installed protobuf
+$ python3 make.py test  --lang csharp -k HubTests
+$ python3 make.py test  --lang cpp     --protobuf-version 3.21.12

@@ -1339,41 +1327,12 @@

▸ .devcontainer/versions.env
-
- GO_VERSION=1.24.0 - BUF_VERSION=1.67.0 - DOTNET_VERSION=8.0 - DEFAULT_VARIANT=modern - MODERN_PROTOBUF_VERSION=6.33.4 - LEGACY_V3_PROTOBUF_VERSION=3.21.12 -
+
GO_VERSION=1.24.0
+BUF_VERSION=1.67.0
+DOTNET_VERSION=8.0
+DEFAULT_VARIANT=modern
+MODERN_PROTOBUF_VERSION=6.33.4
+LEGACY_V3_PROTOBUF_VERSION=3.21.12

@@ -1399,30 +1358,15 @@

▸ util.pc.cc
-
- #if TABLEAU_PB_LOG_LEGACY - - // SetLogHandler(ProtobufLogHandler) on protobuf 3.x - #else - - // class ProtobufAbslLogSink : public absl::LogSink { - - // void Send(const absl::LogEntry& entry) override; - // }; - #endif - ATOM_LOGGER_CALL(tableau::log::DefaultLogger(), lvl, "[libprotobuf %s:%d] %s", - ...); -
+
#if TABLEAU_PB_LOG_LEGACY
+  // SetLogHandler(ProtobufLogHandler) on protobuf 3.x
+#else
+  // class ProtobufAbslLogSink : public absl::LogSink {
+  //   void Send(const absl::LogEntry& entry) override;
+  // };
+#endif
+ATOM_LOGGER_CALL(tableau::log::DefaultLogger(), lvl,
+                 "[libprotobuf %s:%d] %s", ...);
@@ -1452,23 +1396,14 @@

▸ pkg/treemap/redblacktree/lesser_go126.go
-
- //go:build go1.26 - package redblacktree - - type - Lesser[T - Lesser[T]] - interface { - comparable - - Less(other T) - bool - } -
+
//go:build go1.26
+
+package redblacktree
+
+type Lesser[T Lesser[T]] interface {
+    comparable
+    Less(other T) bool
+}
@@ -1980,31 +1915,13 @@

▸ internal/index/descriptor.go (parseCols)
-
- for i := - 0; i < md.Fields().Len(); i++ { - - fd := md.Fields().Get(i) - - if fd.ContainingOneof() != nil { - - continue - // skip oneof case fields - } - // ... existing logic - } -
+
for i := 0; i < md.Fields().Len(); i++ {
+    fd := md.Fields().Get(i)
+    if fd.ContainingOneof() != nil {
+        continue  // skip oneof case fields
+    }
+    // ... existing logic
+}
@@ -2029,87 +1946,35 @@

▸ go: main_test.go
-
- func - prepareHub(t *testing.T) *hub.MyHub { - h := hub.NewMyHub() - - h.Load("../testdata/conf/", - - format.JSON, - - load.IgnoreUnknownFields()) - return h - } -
+
func prepareHub(t *testing.T) *hub.MyHub {
+    h := hub.NewMyHub()
+    h.Load("../testdata/conf/",
+        format.JSON,
+        load.IgnoreUnknownFields())
+    return h
+}
▸ cpp: tests/patch_test.cpp
-
- TEST_F(PatchTest, - RecursivePatchConf) { - - auto opts = - NewOptions(); - opts->patch_dirs = { - - test::TestPaths::PatchConf().string()}; - - Hub::Instance().Load(...); - } -
+
TEST_F(PatchTest, RecursivePatchConf) {
+    auto opts = NewOptions();
+    opts->patch_dirs = {
+        test::TestPaths::PatchConf().string()};
+    Hub::Instance().Load(...);
+}
▸ csharp: PatchTests.cs
-
- [Fact] - public void - PatchReplaceConf() { - - var opts = - new - Load.Options{ - - PatchDirs = - new(){TestPaths.PatchConfDir}}; - - var hub = - new - Hub(); - hub.Load(...); - } -
+
[Fact]
+public void PatchReplaceConf() {
+    var opts = new Load.Options {
+        PatchDirs = new() { TestPaths.PatchConfDir }
+    };
+    var hub = new Hub();
+    hub.Load(...);
+}
@@ -2170,42 +2035,17 @@

▸ shell — typical contributor flow
-
- # one-time host toolchain (no-op inside devcontainer) - $ python3 make.py setup --lang - all - - # per-language - $ python3 make.py test --lang go -k - Test_ActivityConf_OrderedMap - $ python3 make.py test --lang cpp - --cxx-std 20 --cxx-compiler clang - $ python3 make.py test --lang cpp - --protobuf-version 3.21.12 - $ python3 make.py test --lang csharp - -k HubTests - - # diagnostic JSON (paths, versions, env probes) - $ python3 make.py env -
+
# one-time host toolchain (no-op inside devcontainer)
+$ python3 make.py setup --lang all
+
+# per-language
+$ python3 make.py test --lang go     -k Test_ActivityConf_OrderedMap
+$ python3 make.py test --lang cpp    --cxx-std 20 --cxx-compiler clang
+$ python3 make.py test --lang cpp    --protobuf-version 3.21.12
+$ python3 make.py test --lang csharp -k HubTests
+
+# diagnostic JSON (paths, versions, env probes)
+$ python3 make.py env
From 0e125cb55f6c75d8e395f6b72bf3e4aa913c539e Mon Sep 17 00:00:00 2001 From: wenchy Date: Fri, 12 Jun 2026 15:20:27 +0800 Subject: [PATCH 8/8] chore: improve skill --- .claude/skills/tableau-release-html/SKILL.md | 17 +++++++------ .../references/design-system.md | 24 +++++++++++++++---- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/.claude/skills/tableau-release-html/SKILL.md b/.claude/skills/tableau-release-html/SKILL.md index 6dbdb4af1..4dae5e350 100644 --- a/.claude/skills/tableau-release-html/SKILL.md +++ b/.claude/skills/tableau-release-html/SKILL.md @@ -184,18 +184,21 @@ Background: CSS grid (`::before`) + ghosted watermark version text + `deco-sheet ``` -**Code blocks** — GitHub light style: +**Code blocks** — GitHub light style. **Always wrap the body in `
` with literal newlines** (one source line per line). Do NOT use one inline `` per line — Prettier and other formatters collapse whitespace inside `
` flow content but preserve it inside `
`, so this is the only formatter-proof pattern. Long lines must scroll horizontally inside the block (CSS already sets `overflow-x: auto` on `.code-blk-bd > pre`); never let long lines wrap or push the surrounding card wider.
+
 ```html
 
▸ filename.go
-
- // comment - func Example() error { - // highlighted new line - } -
+
// comment
+func Example() error {
+  // highlighted new line
+}
``` + +The same rule applies to `.demo-out-bd` blocks: wrap content in `
...
`. + +**Important.** The `
` open and close tags MUST sit immediately adjacent to their content — no whitespace between `` and the first character, no whitespace between the last character and ``. The very first character after `` becomes column 0; any leading newline or indent will show up as a blank line in the rendered output.
 Syntax color classes: `c-key` (red `#cf222e`), `c-str` (dark blue `#0a3069`), `c-typ` (brown `#953800`), `c-ann` (purple `#8250df`), `c-num` (blue `#0550ae`), `c-val` (green `#116329`), `c-fld` (default `#24292f`), `c-com` (grey `#6e7781`).
 Highlight classes: `c-hl` (green bg, good), `c-warn` (yellow bg, caution), `c-add` (green line), `c-del` (red line).
 
diff --git a/.claude/skills/tableau-release-html/references/design-system.md b/.claude/skills/tableau-release-html/references/design-system.md
index 742e87614..f927f5b71 100644
--- a/.claude/skills/tableau-release-html/references/design-system.md
+++ b/.claude/skills/tableau-release-html/references/design-system.md
@@ -91,11 +91,25 @@ Then use the component colour matrix to apply the right classes.
   .fix-row:last-child{border-bottom:none;}
   .fix-row:hover{background:#f9fafb;}
 
-  /* Code blocks — GitHub light */
+  /* Code blocks — GitHub light. Body MUST be `
...
` so + Prettier preserves whitespace verbatim. Long lines scroll horizontally + inside the block; they never wrap or push the surrounding card wider. */ .code-blk{background:#fff;border:1px solid #e5e7eb;border-radius:8px;overflow:hidden;box-shadow:0 1px 3px rgba(0,0,0,0.04),0 4px 12px rgba(0,0,0,0.05);} .code-blk-hd{padding:9px 16px;background:#f9fafb;border-bottom:1px solid #e5e7eb;font-family:'JetBrains Mono',monospace;font-size:11px;color:#57606a;letter-spacing:.04em;} .code-blk-hd::before,.demo-out-hd::before{display:none;} - .code-blk-bd{padding:14px 16px;font-family:'JetBrains Mono',monospace;font-size:12px;line-height:1.8;overflow-x:auto;color:#24292f;} + .code-blk-bd{padding:0;background:#fff;} + .code-blk-bd > pre{margin:0;padding:14px 16px;font-family:'JetBrains Mono',monospace;font-size:12px;line-height:1.8;color:#24292f;overflow-x:auto;white-space:pre;tab-size:2;} + .code-blk-bd > pre > code{font-family:inherit;background:none;padding:0;white-space:pre;display:block;min-width:max-content;} + /* Inside
, syntax-color spans are inline (the literal newline does the line break). */
+  .code-blk-bd > pre > code .c-del,
+  .code-blk-bd > pre > code .c-add,
+  .code-blk-bd > pre > code .c-com,
+  .code-blk-bd > pre > code .c-inf,
+  .code-blk-bd > pre > code .c-hl,
+  .code-blk-bd > pre > code .c-warn,
+  .demo-out-bd > pre > code .c-com,
+  .demo-out-bd > pre > code .c-inf{display:inline;margin:0;padding:0;border-left:0;}
+  /* Block-level highlight defaults (used outside pre/code, kept for compatibility). */
   .c-del{color:#82071e;display:block;background:#ffebe9;margin:0 -16px;padding:0 16px;}
   .c-add{color:#116329;display:block;background:#e6ffec;margin:0 -16px;padding:0 16px;}
   .c-com{color:#6e7781;display:block;}.c-inf{color:#0550ae;display:block;}
@@ -104,10 +118,12 @@ Then use the component colour matrix to apply the right classes.
   .c-hl{background:#e6ffec;display:block;border-left:2px solid #2da44e;padding-left:14px;margin-left:-16px;margin-right:-16px;padding-right:16px;}
   .c-warn{background:#fff8c5;display:block;border-left:2px solid #d29922;padding-left:14px;margin-left:-16px;margin-right:-16px;padding-right:16px;}
 
-  /* Demo output blocks */
+  /* Demo output blocks — same `
` rule as code blocks. */
   .demo-out{border:1px solid #e5e7eb;border-radius:8px;overflow:hidden;margin-top:16px;box-shadow:0 1px 3px rgba(0,0,0,0.04),0 4px 12px rgba(0,0,0,0.05);}
   .demo-out-hd{padding:9px 16px;background:#f9fafb;border-bottom:1px solid #e5e7eb;font-family:'JetBrains Mono',monospace;font-size:11px;color:#57606a;letter-spacing:.04em;}
-  .demo-out-bd{padding:14px 16px;background:#fff;font-family:'JetBrains Mono',monospace;font-size:12px;line-height:1.8;overflow-x:auto;color:#24292f;}
+  .demo-out-bd{padding:0;background:#fff;}
+  .demo-out-bd > pre{margin:0;padding:14px 16px;font-family:'JetBrains Mono',monospace;font-size:12px;line-height:1.8;color:#24292f;overflow-x:auto;white-space:pre;tab-size:2;}
+  .demo-out-bd > pre > code{font-family:inherit;background:none;padding:0;white-space:pre;display:block;min-width:max-content;}
 
   /* SBS panels */
   .sbs-wrap{display:grid;grid-template-columns:1fr 1fr;gap:16px;}