# Markup Guidelines This file documents author-facing Ink tag conventions. The active parser normalizes tags into structured `StoryTag` objects before they reach the UI. ## Implemented Tag Forms Use bracket tags for titles, filenames, and longer text: ```ink #chapter[Eibenreith] #image[statue.png](square) #music[Kaiserpunk Waltz.mp3](crossfade, loop, lead=8) #sfx[church-bells.ogg](max=8, fade) #score[You reached an ending.] #achievement[First Steps] #alert[Try examining the room.] ``` Use colon tags for short identifiers, categories, and choice keys: ```ink #action:movement #key:l #sort:last #gated:noble ``` Bare flags are accepted as tags with no value: ```ink #optional ``` ## Right-Page Glossary Notes Glossary notes are story tags scoped to the paragraph/block they belong to. They affect only the right-page story rendering, never choice text or command history. ```ink The conductor points toward Eibenreith. #gloss[Eibenreith](A fictional alpine town in the Kaiserpunk setting.) ``` The bracket value is the visible term to find. The parenthesized value is the note shown on hover/focus. The renderer marks every matching instance of the term in the same right-page block. The tag is not displayed and is not sent to TTS. Avoid raw Ink control characters in the explanation; `|`, `{`, and `}` must be escaped in Ink as `\|`, `\{`, and `\}` if they are needed literally. ## TTS Reading Instructions TTS instruction tags are story tags scoped to the paragraph/block they belong to. They are not rendered, and they are only sent to TTS providers that support per-request reading instructions. Currently this means OpenAI with `gpt-4o-mini-tts`. ```ink „Ich habe nichts gesehen“, sagt Viktor. #tts[Read softly, with controlled unease.] ``` The default form omits a provider and is the preferred authoring style. Providers that support instructions may consume it; providers that do not support instructions silently ignore it. Provider-specific instructions are only needed when two providers should receive different direction, or when an instruction must be hidden from all but one provider. They use the tag parameter position: ```ink „Ich habe nichts gesehen“, sagt Viktor. #tts[openai](Read softly, with controlled unease.) ``` The shorthand `#tts-openai[...]` is also accepted. `#tts(...)` is equivalent to providerless `#tts[...]` if parentheses read better in a local context. `tts-1` and `tts-1-hd` ignore these instructions because the OpenAI speech endpoint only supports the `instructions` request parameter for `gpt-4o-mini-tts`. Keep instructions short and describe performance rather than content. OpenAI's TTS guide recommends using `gpt-4o-mini-tts` when you need controllable delivery; useful instruction targets include tone, emotional range, intonation, speaking speed, accent, impressions, and whispering. Good examples: ```ink #tts[Speak with restrained concern and a slower pace.] #tts[Whisper the line with controlled urgency.] #tts-openai[Use a dry, formal tone; avoid melodrama.] ``` Avoid repeating the full dialogue in the instruction. Put the words to be spoken in the story text, and use `#tts` only to describe how the provider should read that block. ## Choice Metadata Choice tags are placed on the Ink choice they belong to: ```ink * [__Schaue__: Aus dem Fenster.] #action:orientation #key:l ``` Implemented choice metadata: - `#key:x`: reserves keyboard key `X` for the choice. - `#letter[x]`: older equivalent for reserving keyboard key `X`. - `#action:group` or `#action[group]`: assigns the choice to an invisible action group. The current UI renders all choices in one visible list. Choices are first grouped by `#action` in the order each new action group appears in the authored choice list. Choices inside each group are randomized. Choices without `#action` form one final unlabelled group shown after all tagged groups. Explicit keys are assigned before automatic keys; choices without explicit keys receive `1` through `0`, then `A` through `Z` in final visible order while skipping explicit keys. `#optional` choices are displayed italic. Grouping columns, `#gated[...]`, and `#sort[...]` are documented authoring conventions or future metadata, not fully implemented UI behavior yet. ## Popup And End-State Tags These tags may appear as Ink global tags, paragraph tags, or empty tag-only lines. They are dispatched through the same tag channel as media tags. ```ink #score[You reached the quiet ending.] #error[The story ended unexpectedly.] #achievement[First Steps] #alert[Try examining objects before using them.] ``` - `#score[...]`: intended ending. When the turn reaches `inputMode: end`, the UI shows a localized ending popup with the tag value as the optional message. - `#error[...]`: unrecoverable ending. The UI shows a localized error popup with the tag value as the optional message. The Ink engine emits this automatically if Ink runs out of content without an explicit `#score[...]` or `#error[...]`. - `#achievement[...]`: queued localized achievement popup while the game continues. - `#alert[...]`: queued localized player hint/tutorial popup while the game continues. ## Existing Media And Structure Tags ```ink #chapter[Title] #section #textblock #image[filename.png](landscape) #image[filename.png](portrait pause=2) #image[filename.png](square lead=1.5) #music[track.mp3](crossfade, loop, lead=4) #sfx[file.ogg](max=8 fade fade-duration=2) ``` Asset filenames resolve relative to the configured image, music, and sound folders.