Add HOCON playground with interactive tutorials#5
Merged
Conversation
…lick types)
Four lessons closing out actions topic. Each builds on the previous so
the user grows a working click handler from "just send a message" to
"different left/right reactions".
- 16-click-message: click { actions { message } } - introduces the
three-level nesting (click -> actions -> action-type) with the
simplest possible action
- 17-click-command: swap message for command - same nesting, different
action type. Mentions @p selector and the long-form { cmd, as }
variant for player-side execution
- 18-multiple-actions: stack multiple action types in one actions block;
notes the gotcha that since actions is an object you can't have two
`message` keys (point users at the bulk action for that)
- 19-click-types: split click.actions into click.left.actions /
click.right.actions; classic shop pattern (left=buy, right=inspect)
Each starter carries the previous lesson's state forward so the
incremental build is visible in the editor. Shape-checks assert the
specific path the lesson teaches; users are free to leave or remove
prior content as they like.
15-count-cooldown -> 16-click-message. 19-click-types is the new tail
until batch 6 (templates).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When the same path is assigned twice and both old and new values are
objects, the resolver now merges them recursively instead of overwriting.
Matches Lightbend HOCON's documented behavior:
defaults { a = 1 }
defaults { b = 2 }
-> { defaults: { a: 1, b: 2 } }
Last-wins still applies for any non-object collision (scalar over scalar,
scalar over object, object over scalar) - same as before.
Localized to setPath() in resolve.ts + a new private mergeTrees() helper.
8 tests in resolve.test.ts cover: simple two-block merge, three-block,
field-overlap last-wins inside the merge, recursive nested merge, both
non-object overwrite directions, scalar dup unchanged, dotted-key merge.
Self-contained change - revert via `git revert HEAD` if it causes any
regression in real configs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`a += [x]` now concatenates `[x]` onto an existing array at `a` (or
creates `[x]` if `a` is unset), instead of silently overwriting. Matches
the HOCON spec's desugaring `a += b == a = ${?a} [b]` for the literal-
array case.
Wired e.append from the parser through buildTree -> setPath. The append
branch handles three cases:
- existing is an array Node: concat items
- existing is undefined: treat as first assignment
- existing is something else: fall back to overwrite (predictable, even
if HOCON spec would error)
Substitution-valued appends (`items += ${shared}`) take the fall-back
path; supporting them needs deferred resolution and isn't required for
the templates lessons. Documented inline.
6 new tests cover the common scenarios + regression on plain `=`.
Self-contained: revert via `git revert HEAD` if needed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`{ slot = 0, ${defaults} }` now merges the resolved defaults object's
fields into the enclosing object instead of erroring on the substitution
token. Foundational for the templates topic's DRY patterns.
Implementation:
- types.ts: Entry gains optional `spread?: boolean`. When true, `value`
is a substitution Node and `path` is `[]`.
- parser.ts: parseEntry detects `substitution` token at entry position
(where a key path would otherwise start), emits a spread Entry.
- resolve.ts: buildTree stashes spreads on a Symbol-keyed slot of the
Tree (so Object.keys() doesn't see them). resolveSubsDeep evaluates
spreads FIRST, Object.assigns each into the output, then processes
own keys - so explicit fields override spread fields on collision.
- scope-ranges.ts: walkEntries skips spread entries (they don't add a
key path to index).
- unknown-keys.ts: already early-skips empty-path entries, no change.
Semantic note: HOCON spec uses strict declaration-order for merge
precedence. We use "spreads-first, own-keys-win" because the typical
templates intent ("apply defaults, then override") works regardless of
where the user puts `${ref}` in the literal. Documented inline.
7 tests: simple merge, override-wins, items-array DRY, multiple spreads
compose, spread of non-object is no-op, spread of unknown ref is
graceful, no spurious parse errors.
Touches 4 files - revert via `git revert HEAD` cleanly removes the
feature without affecting the merge or += commits before it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three lessons that exercise the parser features added in the three
preceding commits.
- 20-include: regex check (`include "..."`), explicit caveat in the
intro that the playground warns because there's no real file system;
syntax check still passes
- 21-array-append: builds on lesson 10's two-item menu pattern but uses
`+=` to append; shape-check verifies BOTH items survive
- 22-object-merge: two `defaults { ... }` blocks merging into one
resolved object; cross-references the same rule firing on `include`
for the base+override pattern
19-click-types -> 20-include. 22-object-merge is the new tail until
batch 7 (defaults-merge / optional-subs / placeholders).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… placeholders)
Three lessons closing out the templates topic.
- 23-defaults-merge: classic DRY pattern - define `defaults { ... }` and
spread `${defaults}` into each item via the inline-spread feature
(commit aeb73db). Shape-check verifies both items inherit material AND
the second item's own `name` overrides the default (own-keys-win).
- 24-optional-subs: `${?var}` form silently absent when missing.
Regex-checked because the lesson teaches the LITERAL form, not just
the resolved value (writing `subtitle = null` would also resolve to
null but wouldn't teach the feature).
- 25-placeholders: contrasts `${var}` (HOCON parse-time) with `%name%`
(PlaceholderAPI runtime); shape `matches` checks the literal text
survives into the resolved value (the parser doesn't touch it).
22-object-merge -> 23-defaults-merge. 25-placeholders is the new tail
until batch 8 (conditions: rules + bindings).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Final lesson batch. Closes the curriculum at 29 lessons across 6 topics.
- 26-rules-permission: introduces `rules { ... }` as a click-gating
block sibling of `actions`; lists the most common rule kinds
(permission, money, levels, gamemode, world). Notes AND-semantics for
multi-rule blocks.
- 27-bindings: per-player visual variants via `bindings = [{ rules,
props }]`. Cross-references the previous lesson on rule structure;
introduces "first match wins" tier patterns.
- 28-rules-world: rules also work at item level (gates visibility,
not actions). Combines a world rule with the inherited click handler
from earlier; mentions OR-via-bindings and the if/js rules for
advanced cases.
25-placeholders -> 26-rules-permission. 28-rules-world is the final
lesson (next: null).
The full 29-lesson tutorial: 8 basics + 4 menu + 4 items + 4 actions +
6 templates + 3 conditions.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Lessons can now reference external sources (HOCON spec, Bukkit docs, PlaceholderAPI registry) as proper hyperlinks instead of bare URLs. Implementation: - renderInline pulls code spans out via SOH/STX-delimited placeholders before running other transforms, then restores them with re-escaped contents at the end. This is necessary for ordering: the link regex would otherwise match `[text](url)` patterns *inside* code spans where the user wants the literal markdown form to display. - Only http/https URLs render as anchors; anything else (javascript:, relative paths, etc.) collapses to href="#" so untrusted lesson contributors can't inject XSS. - Anchors get target="_blank" rel="noopener noreferrer" so the target page can't reach back into the playground via window.opener. 5 new tests cover: http link, https link, javascript: neutralised, relative path neutralised, and `[text](url)` inside backticks staying literal. 333/333 total. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…values
The lesson previously implied durations were a generic standard. They're
HOCON's own format, defined in the spec under "Duration format" - not
ISO 8601 and not Java's Duration.parse syntax.
Updated the intro (en + ru) to:
- name the format ("HOCON's own duration format") with a link to the
authoritative section in the lightbend/config spec
- clarify that suffixes are lowercase and case-sensitive
- list the supported short suffixes (ns/us/ms/s/m/h/d) since they're
what users will type 95% of the time
Uses the markdown link support added in the previous commit.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…r default The browser-default `<a>` color (vivid #0000EE / purple visited) jars against the rest of the muted dark palette. Switched to GitHub-style sky blue for dark theme (#79c0ff) and the standard medium blue for light theme (#0969da). Underline is dotted-feel via reduced opacity text-decoration-color; becomes solid on hover so links stay visibly clickable without screaming. Scoped to .pg-tutorial a so the rest of the chrome (header, panels) keeps its own conventions. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The goal field in a lesson was injected as a raw text node, so backticks in the goal showed up as literal characters instead of <code> spans: "Цель: Добавь три ключа: \`size\` (число 3), \`cooldown\` (длительность 5s)" is what the user actually saw, instead of the intended formatted line. Fix: export renderInlineMd from markdown.ts (same code/bold/link pipeline as the paragraph form, just without the <p> wrap), and use it in TutorialPanel.buildHead's goal block via innerHTML on a span. The new helper is sandboxed to the same XSS protections as renderMd (http(s) link allowlist, html escape on every text segment), so feeding user content through innerHTML stays safe. 4 new tests cover code/bold/link/escape via the new entry point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Both lessons asked the user to write custom keys (closeOnClick, cooldown, tags, meta) at the menu-root level, which triggered "Unknown key in menu-root scope" warnings the moment the user reached the goal. The warnings made the lesson feel broken even when the goal was satisfied. 03-values: drop the boolean primitive from the goal (no top-level bool key exists in the AbstractMenus menu-root schema). Goal now teaches number + duration with `size` and `updateInterval` - both real menu-root fields. The intro still teaches all three primitives and points at where booleans actually live (firework, bindings, potion settings). 04-lists-vs-objects: swap the synthetic `tags` / `meta` for the real menu-root keys `metaList` (list) and `activators` (object with `command` inside). Both are valid AbstractMenus configuration; the lesson keeps its pedagogical point about list-vs-object syntax without producing warnings. en + ru intros, goals, asserts, and hints all updated. 337/337 tests pass. Note: 5 other lessons (05/06/07/22/23) still trigger menu-root warnings because they intentionally define custom variables (`defaults`, `itemName`, `closeAction`). That's a separate issue - solving it properly needs a substitution-aware unknown-key check, not per-lesson edits. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Lesson intros and hints already used triple-backtick fences for multi-line code samples (e.g. the verbose-vs-dotted comparison in 05-dotted-keys), but renderMd had no handler for them - the fences showed up as literal characters and the contents got rendered as plain paragraphs (with backticks visible). Implementation: - Pre-stash fenced regions before paragraph splitting via an ETX (0x03) sentinel built with String.fromCharCode (keeps the source file plain ASCII instead of relying on raw control bytes that some editors mangle). - Each fence becomes a single token regardless of how many blank lines it contains, so multi-paragraph code samples survive the split intact. - Filter empty blocks introduced by the stash padding so we don't emit spurious <p></p> around fences. - Same FENCE_MARK trick applied to the existing inline-code STASH_OPEN / STASH_CLOSE constants for consistency. - Fence contents are HTML-escaped; bold/code/link transforms do NOT run inside, so a code sample showing markdown syntax stays literal. - Optional language tag after opening fence (```hocon, ```ts) is parsed and ignored - room for a syntax-highlighting upgrade later. CSS: .pg-tutorial pre / pre code styled separately from inline code - its own padding, scroll on overflow, no rounded pill background on the inner code element. 6 new tests cover: single fence, language tag, blank-line preservation, HTML escaping, paragraph+fence+paragraph ordering, no inline transforms inside fences. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous version asked the user to write `name = ${itemName}` at the
top level, where `name` is not a recognised menu-root key - it's an item
field. Result: a spurious "Unknown key `name` in menu-root scope" warning
even after the goal was satisfied correctly.
Restructured so the substitution is consumed inside an item:
itemName = "Diamond Sword"
items = [
{ slot = 4, material = DIAMOND_SWORD, name = ${itemName} }
]
Now `name` lives in the item scope where it's catalog-known. The custom
`itemName` at menu-root still warns (intentional substitution variable;
the broader fix for that warning class needs a substitution-aware
unknown-key check, separate work).
Both en + ru intros, goals, asserts, and hints updated. 84/84 tutorial
tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Catalog KeyDef.description was hardcoded English, so users on the ru locale saw English text in autocomplete tooltips and hover-docs even when the rest of the UI was Russian. - catalog/i18n.ts: describeKeyDef(def) helper that looks up `<scope>.<name>` in a per-locale dictionary; falls back to the canonical English description if the key isn't translated. Auto-loads catalog/translations/<lang>.ts via import.meta.glob - dropping a new file there registers the locale (same convention as the UI i18n). - catalog/translations/ru.ts: seeded ~70 high-frequency keys covering menu-root + item + click + actions + rules + binding + activators. Partial translation is fine; the helper falls back per-key. - cm/hover-docs.ts and cm/catalog-source.ts swapped def.description for describeKeyDef(def). Two single-line changes. - 4 tests: en default, ru hits seeded key, ru falls back on unseeded, switch-back-to-en works. Community PRs adding new locales just need to add a translations file + optionally add the locale code to the language selector's LANG_LABELS. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Same class of fix as lesson 06: the previous version assigned `click =
${closeAction}` at the top level, where `click` is not a recognised
menu-root key (it's an item field), so the user got an "Unknown key
`click` in menu-root scope" warning even after satisfying the goal.
Restructured to use a real shop close-button shape:
closeBtnAction { closeMenu = true }
items = [
{ slot = 8, material = BARRIER, name = "&cClose"
click { actions = ${closeBtnAction} }
}
]
Now `click` lives in the item scope where it's catalog-known, and the
referenced object is `actions`-shaped (matching click.actions, also
catalog-known) - resolves without warnings on the recognised keys.
The custom `closeBtnAction` at menu-root still warns; that's the same
substitution-variable issue the previous fix flagged for the broader
substitution-aware unknown-key check.
Bonus: the new shape is closer to a real menu config than the synthetic
`{ close = true }` had been - users coming from real configs will
recognise the pattern.
Both en + ru intros, goals, asserts, and hints updated. 84/84 tutorial
tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…nown
Both lessons used a made-up key purely to demonstrate a HOCON syntactic
form, but the synthetic key tripped the unknown-key validator and
produced a confusing warning at the menu-root level.
- 02-separators: `meta` -> `closeActions`. Both keys are objects and the
pedagogical point (drop the `=` before `{`) is identical, but
`closeActions` is a real menu-root key and renders without warning.
- 24-optional-subs: `subtitle` -> `title`. The lesson teaches `${?var}`,
which only requires the LHS to be an assignable key. Switching to
`title` (catalog-known menu-root field) clears the warning while
keeping the demo intact. Adjusted the intro and template-include
example to use `customTitle` consistently.
en + ru both updated. 347/347 tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
\`defaults\` is a community convention rather than a hard AbstractMenus
field, but it's the canonical name for "shared item template I substitute
into each item via \${defaults}". The DRY-pattern lessons (05-dotted-keys,
22-object-merge, 23-defaults-merge) all use it and were getting spurious
"Unknown key \`defaults\` in menu-root scope" warnings every time the
user reached the goal.
- catalog/keys.ts: added entry with valueType: 'object' and
childrenScope: 'item' so its children also validate against item-scope
(matching how it's actually used as an item template).
- catalog/translations/ru.ts: matching ru description.
Audit after this + the lesson 02/24 swap commit: 5 of the 7 lessons that
previously warned now resolve cleanly. Only 06 and 07 still warn, and
intentionally - they teach the user to define a *custom* substitution
variable (itemName, closeBtnAction); the proper fix is a separate
substitution-aware unknown-key check that excludes any key referenced
from \${name} elsewhere in the doc.
347/347 tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Lesson intros and hints carry inline HOCON examples in triple-backtick
fences, but they rendered as flat text - everything in one foreground
colour, no contrast between keys/strings/operators. Hard to read at a
glance, especially next to the editor which IS highlighted.
- highlight-hocon.ts: thin wrapper around the existing cm/hocon-tokenizer.
Walks each line, emits HTML where every non-null token is wrapped in
`<span class="cm-tk-${type}">`. Always HTML-escapes; safe for innerHTML.
- markdown.ts: when a fenced block has no lang or `hocon` lang, send the
content through highlightHocon; otherwise plain escape (so ```bash etc.
fall through cleanly). Lang is captured per-fence via an updated stash
regex.
- playground.css: respec the .cm-tk-* palette under `.pg-tutorial pre`
so the same colours apply outside the editor's `.cm-editor` scope.
Both dark + light themes covered.
- 5 new highlight-hocon tests: classes appear, html escapes, comments and
substitutions get their own classes, multi-line preserved.
- markdown tests adjusted: tests that probe structural rendering now use
```text to opt out of highlighting; new tests verify hocon and explicit
`hocon` lang produce the expected token classes; ```bash falls through.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mass reformat of every lesson's intro fenced blocks, hints, and starter
templates so any HOCON object with two or more keys lands on multiple
lines, one entry per line. One-key objects keep the compact inline form.
Mixed cases (some inline, some on own lines) are normalized to one-per-
line to remove the visual jaggedness.
Done via a one-shot Python script (kept as a scratch in /tmp, not
committed): walks each .json, splits inner content by top-level commas /
newlines / semicolons (skipping strings, ${...} substitutions, and
#//-comments), expands when count >= 2, then re-anchors every line via
a port of formatHocon's bracket-depth indenter so previously partial
formatting comes out consistent.
24 lesson files touched. The transform is idempotent: re-running the
script produces no further diff. Manual spot-checks on the trickier
lessons (07-variables-object, 23-defaults-merge, 17-click-command,
12-lore, 28-rules-world) confirm comments inside fences are preserved
verbatim and nested array indentation is correct.
354/354 tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The intro showed a code example with `\${closeBtnAction}` then said
"When `\${name}` resolves to an object..." - the `\${name}` placeholder
was never introduced in this lesson and reads as if a different variable
is being discussed. Renamed both the en and ru sentence to refer to
`\${closeBtnAction}` directly so the prose tracks the example.
Lessons 06 and 24 also use `\${name}` but as the FIRST mention of the
substitution syntax (introducing the pattern itself before any concrete
example), so they're left as-is.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Lessons now live as 01-introduction.json through 29-rules-world.json, matching the 1-based "Lesson N / Total" counter the user sees in the tutorial UI. Previously the file IDs ran 00..28 while the position counter ran 1..29, so users referencing lesson "11" in conversation were actually on file 10-slots.json - confusing. Mass rename via a one-shot Python script (kept in /tmp): for each .json in lessons/, increments the leading 2-digit number, rewrites `id` and `next` fields, remaps any cross-references in intro/hints text (02-separators -> 03-separators, 22-object-merge -> 23-object-merge), and updates lessons.test.ts + TutorialController.test.ts hardcoded IDs. Anyone with progress saved under old IDs lands on the first lesson via the existing stale-id fallback in TutorialController.enter() (commit b5e52c4) - no broken state. Obsidian spec also updated to match. 361/361 tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…opup
Two related polish items.
1. Markdown image syntax `` in lesson intros and hints renders
to an `<img loading="lazy">`. URL allowlist accepts http(s) absolute
plus same-origin paths starting with `/` (so static assets in
public/img/... can be referenced); javascript:, data:, and bare
relatives all collapse to no output (or `#` for links). Image
substitution runs before the link substitution to avoid `![alt]
(url)` matching the link regex with a stray leading `!`.
playground.css: .pg-tutorial img gets max-width: 100%, lazy load,
subtle border so images sit cleanly inside the panel.
2. Lesson 11-slots intro (en + ru) now embeds /docs/img/items_slots_id.png
right under the ascii grid - a real visual reference for the slot
numbering instead of just text.
3. The "All lessons" popup now prefixes each row with its 1-based
global position ("1. Introduction", "11. Slot positions", ...). Helps
users locate themselves at a glance and matches the "Lesson N / Total"
counter shown in the panel header.
5 new tests cover image rendering / safety + the numbering prefix.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Lessons can now reference the docs wiki via `[label](wiki:concept)`. At render time the concept is resolved against a registry and the active locale, producing a real `/docs/<lang>/.../#anchor` href. Russian anchors are percent-encoded for href correctness. - tutorial/wiki-links.ts: registry seeded with 11 concepts (click, slot, items, item-format, actions, rules, variables, placeholders, activators, hocon, templates). Per-locale URLs; missing locale falls back to en. Adding more concepts is one entry per item. - markdown.ts: link substitution detects the `wiki:` scheme, calls resolveWikiUrl, falls back to `#` for unknown concepts (so the broken link is visible). Same `target="_blank" rel="noopener noreferrer"` as every other lesson link - all playground links open in a new tab so the tutorial state isn't lost. - 8 new tests cover registry resolution + the markdown integration. - 5 lessons get example wiki links seeded in their intros (en + ru): 10-items-array, 11-slots, 12-name-and-material, 17-click-message, 26-placeholders, 27-rules-permission. The pattern is the same for every other lesson - extend incrementally. 369/369 tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Picks up where the previous wiki-links commit left off. Audited every
lesson, picked the most-relevant concept per intro, added the link.
Registry changes (wiki-links.ts):
- Fixed click anchor: `#click-handling` -> `#click-processing` (the real
heading slug in menu-structure.md). Same for the ru anchor.
- Removed the made-up `slot` and `items` anchors that didn't exist in
the docs; pointed them at real headings (`#buttons` for items, `#slot`
on item-format for slot positions).
- Added new concepts wired to actual section anchors: `size` (menu
properties), `display-rules`, `bindings`, `display` (item-format -
name/lore/material/flags), `mechanics` (enchantments), `cooldown`
(slot+cooldown section).
Lessons newly linked (en + ru):
- 09-size: "menu" -> wiki:size
- 13-lore: "lore" -> wiki:display
- 14-enchantments:"enchantments" -> wiki:mechanics
- 15-flags: "flags" -> wiki:display
- 16-count-cooldown:"item" -> wiki:cooldown
- 18-click-command:"command action" -> wiki:actions
- 19-multiple-actions:"actions" -> wiki:actions
- 20-click-types: "left/right/middle/..." -> wiki:click
- 21-include: "include" -> wiki:hocon
- 24-defaults-merge:"DRY trick" -> wiki:templates
- 25-optional-subs:"${name}" -> wiki:variables
- 28-bindings: "bindings" -> wiki:bindings
- 29-rules-world: "rules" -> wiki:rules + "visibility" -> wiki:display-rules
369/369 tests pass. Anchor strings verified against the actual
section headings in src/content/docs/{en,ru}/general/menu-structure.md
and item-format.md.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Audited every standalone wiki page (actions/rules/variables/placeholders/
activators/hocon/templates) for sub-headings and added per-anchor
registry entries where they map naturally to lesson concepts.
Registry additions (all anchors verified against the actual headings in
src/content/docs/{en,ru}/...):
- action.message, action.command, action.sound, action.teleport,
action.delay, action.bulk - per-action anchors. actions.md uses English
headings in BOTH locales (## Message / ## Command), so slugs are
identical for en and ru.
- templates.include - the "Templates in separate file" section, which
is what the include lesson actually wants to point at.
- Pointed top-level entries at meaningful section anchors instead of
page roots: rules -> #all-rules, placeholders -> #built-in-placeholders,
activators -> #all-activators, hocon -> #basic-hocon-syntax,
templates -> #templates-basics. Per-locale slugs differ for the
Russian-headed sections.
Lessons updated:
- 17-click-message: "message", "command", "sound" each get their own
per-action anchor (was just one wiki:actions for the whole list).
- 18-click-command: wiki:actions -> wiki:action.command.
- 21-include: wiki:hocon -> wiki:templates.include (more relevant target).
Added a sanity test in wiki-links.test.ts that scans every shipped
lesson's intro and hints for `wiki:concept` refs and asserts each one
exists in the registry. Catches typos and registry drift on the next
commit instead of in the browser.
370/370 tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
New in-browser HOCON editor at
/playground/with autocomplete, live diagnostics, and a resolved-JSON preview. Runs entirely in the browser, no backend.Tutorial mode walks through 28 lessons covering values, items, actions, templates, and conditions. Lesson links jump to the matching docs page.
EN/RU UI, lesson content, and diagnostic messages. Share a config via URL, recent edits are kept in local history per mode.