Instance customization - MadLadSquad/YouyinWeb GitHub Wiki
There are two layers of customization for a self-hosted instance:
-
Project configuration in
uvproj.yaml(andTranslations/ui18n-config.yaml) — branding, canonical URL and the external service URLs. These are substituted into the source at build time by the UVKBuildTool, so they apply to both the HTML templates and the JS. -
Runtime tuning constants scattered across a few JS files in
CONSTANT BLOCKcomment fences — grading, animation timings, writer sizing and the character-database download behaviour.
The variables: section of uvproj.yaml is the single source of truth for branding and URLs. Each var/val pair is substituted wherever the template token {{ <var> }} appears in any processed file.
| Variable | Default | What it controls |
|---|---|---|
trademark |
Youyin |
The product/brand name. Substituted via {{ trademark }} in the HTML chrome (page titles, Open Graph/Twitter meta) and via the {brand} placeholder inside translation strings. |
cannonical_url |
https://youyin.madladsquad.com |
The deployed site's base URL. CI rewrites ./ links to this and it feeds the canonical/OG/sitemap URLs. |
marketplace_url |
https://github.com/MadLadSquad/YouyinPublicDeckRepository |
Human-facing link to the public deck repository. |
marketplace_cdn_url |
https://cdn.jsdelivr.net/gh/MadLadSquad/YouyinPublicDeckRepository@latest |
jsDelivr base the marketplace page fetches deck lists/decks from (MARKETPLACE_CDN in marketplace.js, also pre-cached by sw.js). |
char_data_url |
https://cdn.jsdelivr.net/gh/MadLadSquad/hanzi-writer-data-youyin@latest |
jsDelivr base for the character stroke database. Builds CHARACTER_MANIFEST_URL and CHARACTER_CHUNK_URL_BASE in character-database.js. |
additional_cdn_hosts |
none |
Extra hosts to add to the service worker's pre-cache list (sw.js). The literal string none disables the block. |
Because the brand appears in two different substitution engines, renaming requires editing two files:
-
uvproj.yaml→trademark— used by the build's{{ trademark }}token (HTML chrome, meta tags). -
Translations/ui18n-config.yaml→brand— the literal name the translation engine substitutes for the{brand}placeholder inside localized strings. It must be a plain literal (the i18n engine is single-pass, so a nested{{ }}token would not resolve).
Never hardcode the name inside a translation string or template — always write {brand} / {{ trademark }} so a rename stays a two-line change. Other {name}-style placeholders (e.g. {streak}) are filled at runtime by JS, not at build time.
These live inside // --- CONSTANT BLOCK EDIT IF RUNNING ON A CUSTOM SYSTEM --- fences. They are no longer all in one file — each block sits next to the code that uses it.
window.MAX_KNOWLEDGE_LEVEL = 4;
window.HOUR_UNIX = 3600000; // ms in an hour
window.MINUTE_UNIX = 60000;
window.SECOND_UNIX = 1000;window.MAX_POINTS_ON_CHARACTER = 0.05;
window.ADD_POINTS_ON_ERROR_3_4 = 0.0375; // 3/4 of 0.05
window.ADD_POINTS_ON_ERROR_1_2 = 0.025; // 1/2 of 0.05
window.ADD_POINTS_ON_ERROR_1_4 = 0.0125; // 1/4 of 0.05
// Hard cap on cards (and, separately, phrases) revised per play session. Larger
// decks are shuffled and only the first entries are drawn, so each session is a
// random subset of at most this many cards and this many phrases.
window.MAX_SESSION_REVISION_ITEMS = 8;
window.WRITER_SLEEP_AFTER_COMPLETE = 1200; // ms to admire a finished character
window.WRITER_FLY_TO_COUNTER_DURATION = 650; // ms of the "fly into the counter" animation
window.WRITER_SHOW_HINT_ON_ERRORS = 3; // misses before a hint at normal levels
window.WRITER_SHOW_HINT_ON_ERRORS_LVL_3 = 1; // misses before a hint at the advanced levelwindow.CARD_WRITER_SIZE = 100; // single-character widget
window.PHRASE_CARD_WRITER_SIZE = 50; // per-character widget inside a phrase card
window.CARD_WRITER_STROKE_ANIMATION_SPEED = 1.25;
window.CARD_WRITER_DELAY_BETWEEN_STROKES = 50;
window.WRITER_PADDING = 5;The writer colours (WRITER_RADICAL_COLOUR, WRITER_STROKE_COLOUR, WRITER_OUTLINE_COLOUR) are not constants here — they are owned by scripts/data/theme.js and set from the active theme's accent/stroke/outline colours.
window.CARD_DEFAULT_CHARACTER = "是";
window.CARD_DEFAULT_PREVIEW_NAME = "Preview Name";window.CHARACTER_MANIFEST_URL = "{{ char_data_url }}/character-map-chunks.json";
window.CHARACTER_CHUNK_URL_BASE = "{{ char_data_url }}/character-map-chunks/character-map-full-";
window.CHARACTER_CHUNK_BATCH_SIZE = 5; // chunks fetched per batch
window.CHARACTER_CHUNK_COOLDOWN_MS = 300; // pause between batches
window.CHARACTER_FETCH_RETRIES = 4; // retries per manifest/chunk fetch
window.CHARACTER_FETCH_RETRY_DELAY_MS = 600;The two URLs are derived from the char_data_url build variable — to point at a different stroke dataset, change char_data_url in uvproj.yaml rather than editing these lines.
Messing with the grading settings may lead to logical breakages in the application. Setting MAX_KNOWLEDGE_LEVEL above 4 will simply raise the upper points limit without changing functionality; lowering it disables the advanced levels.