# Harlowe - the default [Twine 2](https://github.com/klembot/twinejs) story format. Documentation is at http://twine2.neocities.org/. See below for compilation instructions. ### 3.2.0 changes (unreleased): #### Bugfixes * The bulleted list, numbered list and heading markup has been fixed to permit multi-line markup (such as multi-line hooks) within it. So, among other things, hooks' opening `[` and closing `]` can now be on the same line as a bulleted list item, numbered list item, or heading. * Fixed a long-standing bug involving the interaction between temp variables and the `(show:)` and `(link-show:)` macros. The previous behaviour, which was not my intention at all, meant that hooks shown with `(show:)` could only access temp variables available at the `(show:)` macro's location, rather than the hook's location (which is now the current behaviour). The same applies to `(link-show:)`, as well as to the new `(rerun:)` macro (below). * Fixed a long-standing bug where continuous ranges for arrays, such as `(a: 1,2)'s 4thlasttolast`, wouldn't work correctly. (What that example should do is provide the entire array, as is consistent with Python.) * Fixed a long-standing bug where `(click: ?Passage)` and `(click: ?Sidebar)` just flat-out didn't work at all. * Fixed a long-standing bug where, due to an operator precedence conflict, writing something like `$a's stops's (1)` would cause a Javascript error. * Fixed a long-standing bug where lambdas would produce an incorrect duplicate-name error if the temp variables used with their clauses contained capital letters. * Fixed a long-standing bug where hooks that had `(transition:)` transitions would restart their transition animations whenever the containing passage finished transitioning in. Previously, the only way to overcome this was to make the passage transition using `(transition-arrive:"instant")`. * Fixed a long-standing bug where you couldn't use the column markup to create empty columns by placing two column markup lines in succession, without an intervening blank line. * Fixed a long-standing bug where strings containing HookName syntax (such as `"?pear"`) were considered identical to actual hooknames (such as `?pear`). This was NOT intended behaviour and was not documented, and as such, certain design patterns that involved constructing HookName strings programmatically (such as `(replace:"?" + $name)`) will no longer work. * Fixed a bug where the default CSS for `(click: ?Page)` (a blue border around the page) wasn't visible. (Now, an `::after` pseudo-element is created for the enchantment, so that the border displays above all the page content.) * Fixed a bug where typing `is not an` instead of `is not a` (such as in `$wallet is not an array`) would cause an error. * Now, trying to access the `0th` or `0thlast` value in an array or string produces an error. * Now, `(mouseover:)` and `(mouseout:)` should work correctly with ?Page, ?Passage, and ?Sidebar. * Now, `(hook:)` works correctly when given to `(enchant:)`. * Fixed a bug where `(for:)` would emit infinite loop errors if 50 or more elements were given to it. * Fixed a long-standing bug where clicking the sidebar "undo" and "redo" icons would cause `(click:?Page)` in the preceding or following passages to automatically fire, even though you didn't actually click them. * Fixed a long-standing bug where block elements (like `(align:)` enchanted hooks) weren't transitioning in correctly when their passage appeared. * Significantly improved the performance of macros like `(enchant:)` and `(click:)` when they target a hook's `chars` or `lines`, such as by `(enchant: ?passage's hooks, $changer)`. * Fixed a bug where an `(enchant:)` given `?passage's chars` would crash Harlowe if the passage began with whitespace characters. * Fixed a bug where `(enchant:)` given a link changer such as `(link:)` or `(click:)` would cause a Javascript error. It now causes no error, but it does nothing. (The documentation that formerly cited this as an example usage has been changed.) * Fixed a bug where `(enchant:)` given `?passage's lines` would crash Harlowe if the passage contained zero line breaks. * Fixed a bug where `(enchant:)` given `?passage's lines` would often cause text nodes within lines, such as the text of a `(link:)` link, to be transplanted out of any elements containing them. * Fixed a bug where `(hover-style:)` combined with a link changer such as `(link:)` would cause the specified hover style, after the link was clicked, to permanently persist on the hook. * Fixed a bug where `(hover-style:)` couldn't actually override the default hover colour for links of any kind, due to a CSS conflict. * Fixed a bug where certain changers, like `(for:)`, caused a crash when attached to a command (like a `(print:)` macro, or a passage link). * Fixed a bug where closing an `(alert:)`, `(confirm:)` or `(prompt:)` dialog box when there was an `(event:)` hook in the passage would cause a crash. * Fixed a bug where trying to use datanames with certain unusual types of data (changers, commands, datatypes) would give a bad Javascript error message instead of the intended error message. * Fixed a bug where the CSS used to position the new pure HTML dialogs didn't work on certain old browser versions. * Now, consecutive line breaks (which Harlowe reduces the cumulative height of) at the start of a passage are no longer the wrong height while that passage transitions in. (To handle this, consecutive line breaks are now made into `` elements instead of `
` elements.) * Now, `(dropdown:)` explicitly uses the background of its containing hook, instead of being transparent, which the Windows version of Chrome displays as white, regardless of text colour. * Now, align, horizontal rule, and column syntax in the editor no longer occupies two lines unexpectedly. * `(save-game:)` can now save a greater range of variable data. Formerly, only changers, arrays, datamaps, datasets, booleans, strings and numbers could be stored in variables when you use `(save-game:)` - other values, such as commands, colours, gradients, or lambdas, would cause an error. Now, it should work with every kind of supported Harlowe value (i.e. those mentioned in the documentation) except user-created commands created with `(output:)` (see below). But, this means that save data from stories made in 3.1.0 is no longer compatible with 3.2.0. * Improved an error message that could appear if you erroneously put the spread `...` syntax inside parentheses, such as `(...$arr)`. * Altered the error message that appears when you don't give macros "enough values", which wouldn't properly exclude optional values. * Fixed some error messages involving accessing an invalid data value of a string, which would refer to the string as "an array". * Fixed a long-standing bug that caused error messages for certain invalid operators (such as unsupported SugarCube operators) to be replaced with an incorrect message. * Fixed a couple of crashes under Internet Explorer 10. #### Alterations * With great humility, I've decided to change the behaviour regarding how temp variables interact with nested hooks. Formerly, temp variables created in an outer hook (or the outermost level of the passage) could be accessed in inner hooks, they couldn't be directly modified, because `(set:)`, `(put:)` and `(move:)` would instead create identically-named inner variables specific to that hook. I had originally planned to add a special kind of syntax to let inner hooks modify outer hooks' temp variables, such as `outer _a` – but, I've since accepted that this behaviour is too close to people's intuitions to require extra syntax to enable, and the existing behaviour too restrictive, especially regarding its interaction with `(if:)`. So now, using `(set:)` on a temp variable in a nested hook which shares a name with an outer temp variable will instead modify the outer temp variable. This change brings Harlowe temp variables closer in behaviour to SugarCube's temp variables – though, since SugarCube doesn't have a notion of "hooks", the notion of temp variables only being accessible in the hook in which they were first set is still unique to Harlowe. * It should be noted that the behaviour of `(for:)`'s temp variables is unchanged - this change will still leave any same-named temp variables outside of the hook unaltered by whatever happens inside the hook. * For a long time, the interaction between `(link:)` (as well as `(link-reveal:)` and `(link-repeat:)`) and other changers combined with it has been terribly inconsistent, with some changers applying to just the link, others to just the revealed hook, and most of them applying to both. I've decided after much heart-rending that I need to change this to a single hard-and-fast rule: changers combined with `(link:)` (and the others) will only apply to the revealed hook, NOT the link. This is to allow `(t8n:)`, `(box:)`, `(live:)` and other hook-specific changers to interact with `(link:)` as expected, and also to make `(link:)` links more consistent with `(click:)` links. * This also means that some unusual combinations of `(link:)` have different, more intuitive behaviour. For instance, combining `(link:)` with itself (such as by `(link:"A")+(link:"B")`) causes the first link to reveal the second link, which then, when clicked, reveals the attached hook. Also, `(for:)` combined with `(link:)` will now repeat the hook for the correct number of iterations. * Due to the above alteration, the link changers `(link:)`, `(link-reveal:)` and `(link-repeat:)` (as well as the `(click:)`, `(mouseover:)` and `(mouseout:)` changers) now accept an optional changer as their second value. This is used to style just the link, leaving the revealed hook unchanged. * The behaviour of `(text-colour:)` regarding links has been changed. Formerly, `(text-colour:)` couldn't change the colour of links inside hooks unless it was used with `(enchant:)` and wasn't used with `?passage` or `?page`. This meant that changing the colour of individual links was, unintuitively, not possible without `(enchant:)`. Now, a simpler rule is in place: `(text-colour:)` will always change links' colours, unless it's used with `(enchant: ?passage)` or `(enchant:?page)`. (This exception means that all of `(enchant:)`'s behaviour is unchanged with this release.) * The behaviour for multiple `(click:)` macros affecting the same hook (such as in `|A>[B] (click: ?A)[1] (click: ?A)[2]`) has changed to be slightly more intuitive: formerly, as you clicked the hook, the last `(click:)` would activate first (so, `[2]` then `[1]`). Now, they activate from first to last. This also applies to `(mouseover:)` and `(mouseout:)`. * Now, the text input box in `(prompt:)` is wider, and is auto-focused when the dialog appears, allowing the player to type into it without having to click it. * `(hook:)` now gives an error if it's given an empty string. * Now, `contains`, `is in` and `is not in` will emit an error if it's used to check if a string contains a non-string value, such as a number or boolean. This brings it into consistency with a few other operations, like trying to set a string's `1st` to something other than a single character. * Slightly altered the behaviour of `matches` when both sides are datatypes - now, identical datatypes will match each other, instead of only matching if one side is `datatype`. * The `(str-repeated:)` and `(repeated:)` macros now no longer error if the given number of repetitions is 0. (They will return empty strings and arrays, respectively.) * Now, `?hook's links` will contain the hook itself, if it's a link, or has been given a link changer or enchantment. * Now, pressing Return or Enter in a `(prompt:)` text input box should submit the text, as if "OK" was clicked. * Now, `(enchant:)` can correctly apply `(transition:)` transitions to existing hooks and text. Enchanting an existing hook with a transition will cause it to animate immediately, even if it was already visible - so, it's best used at the top level of passages, and in headers or footers. * Now, `(enchant:)` can also accept a 'via' lambda as its second argument, to apply a slightly different changer to each hook or text - `(enchant: ?passage's lines, via (text-color:(hsl: pos * 10, 1, 0.5)))` uses the new "pos" identifier (see below) to give each line of the passage a different colour. * Now, `(alert:)`, `(confirm:)` and `(prompt:)` accept optional strings for their dialogs' link text (which are "Cancel" and "OK" if no strings are provided). * Macros and expressions (such as bare variables) inside the strings given to `(alert:)`, `(confirm:)` and `(prompt:)` will now run. This wasn't strictly a bug, but it was inconsistent given that markup is available in them. * The default CSS for `(mouseover:)` and `(mouseout:)` (a dotted gray border and translucent cyan border, respectively) has been brightened slightly to be more visible. Also, it has been altered to work correctly with the new `(box:)` macro. * Reworded the error message produced by trying to get an array element that's outside the array's length (such as `(a: 1,2)'s 5th`). * Removed unused, undocumented CSS for the following (also unused) elements: ``, ``, ``, ``, ``, ``, ``, ``, ``, ``, ``, ``, ``, and ``. The CSS applied to these was mostly functionally identical to their corresponding `(textstyle:)` styling. * The `(text-style:)` styles "rumble" and "shudder" no longer need to set `display:inline-block` on the hook. * The shadow of the `(text-style:)` style "emboss" has been slightly adjusted. * Now, attaching `(transition:)` to a passage link instead of `(transition-depart:)` or `(transition-arrive:)` will cause an error instead of doing nothing. * Now, when Harlowe detects that a hook containing block elements is being transitioned, the `` element will be given `display:block`. This is to fix a number of transition animation bugs, though it may cause certain structures to animate differently during transition. * `(mouseover:)`, `(mouseout:)` and their related macros, which use mouse-hovering input that isn't possible on touch devices, will now fall back to simply being activated by clicks/touches on touch devices. * After much fretting and fussing, I've decided to un-deprecate `(subarray:)` and `(substring:)`, because my intended subsequence syntax - `$a's 1stTo2ndlast` and `$a's (range: $b, $c)` - has a not-uncommon edge case where it fails - when `$c` in the preceding example is negative - and I've abandoned plans to alter it to accommodate this case. This un-deprecation changes nothing about how they behaved, but the documentation has been rewritten to include them. * Slightly adjusted the animation of `(text-style:"rumble")` and `(text-style:"shudder")` so that the text shifts position from its centre, rather than its left edge. * `(all-pass:)` can now be shortened to `(pass:)`. This alias is meant for cases when you only want to check a single value, like in `(pass: $noLetterE, "Gadsby")`, rather than a sequence of values. * `(background:)` can now be shortened to `(bg:)`. * `(text-rotate:)` is now aliased to `(text-rotate-z:)`, for consistency with the new macros below. * `(alert:)` has been renamed to `(dialog:)`, retaining the original name `(alert:)` as an alias for it. Moreover, it can now accept any amount of link text, as well as a bound variable to set to the text of whichever link you click. Also, changers like `(t8n:)` can now be attached to the `(dialog:)` macro. * `(rgb:)` now accepts fractional values for the R, G, and B components. * Made the syntax highlighting dark mode colours 50% brighter. * Markup that's inside the verbatim markup will no longer be syntax-highlighted as if it was outside it. #### Additions ##### Coding * Added negated versions of several operators. You may now write `is not in`, `is not a`, `does not contain` and `does not match` as more intelligible negations of `is in`, `contains` and `matches`. * Added the `pos` identifier, which is used in lambdas to provide the position of the data value (from those passed into the macro) that the lambda is currently processing. `(altered: via it + pos, 0,0,5,0,0)` equals `(a:1,2,8,4,5)`. * Added `2bind`, a "two-way bind" variation of `bind` which causes the `(cycling-link:)` and `(dropdown:)` macros, as well as the new `(seq-link:)`, `(checkbox:)` and `(input-box:)` macros, to automatically match the current value of the bound variable, and update itself whenever another macro changes the variable. * Datatypes can now be converted into "spread datatypes" using `...`, such as by writing `...str`. This allows the datatype, inside arrays or string patterns (see below), to match zero or more values of that type. `(a: ...str)` can match `(a:"Oat")`, `(a:"Oat","Wheat")` or `(a:"Oat","Wheat","Maize")`. * Added the HookName data name `visited` (as in `?passage's visited`), which allows you to select links that point to visited passages, and change the unique colour these links have. * Colours now have an alpha `a` data value, containing the alpha value given to the `(hsl:)` and `(rgb:)` macros - `(hsl: 130, 1, 0.5, 0.2)'s a` is 0.2. * Colours now have an `lch` data value, which contains a datamap of LCH color space values for that colour (corresponding to the numbers given to the new `(lch:)` macro). Because LCH's values conflict with HSL's, the LCH values are inside this datamap instead of directly accessible from the colour itself. * `(text-style:)` now lets you provide multiple style names, as a shortcut to combining multiple changers. `(text-style:"italic","outline")` is the same as `(text-style:"italic")+(text-style:"outline")`. * As an error-checking feature, you can now force your story's variables to only ever hold certain datatypes, so that data of the wrong type can't be set to them. `(set: num-type $funds to 0)` will, for the rest of the story, cause an error to occur if a non-number is ever put into $funds, such as by `(set: $funds to "200")`. This not only works with plain datatypes, but with complex data structures, using the `matches` operator's rules: `(set: (a:str,str)-type $b to (a:'1','1'))` will cause an error to occur if any data that doesn't match `(a:str,str)` is put into $b. You can use this syntax with temp variables and the new `(unpack:)` macro, too. * Additionally, you can force a variable to remain constant throughout the story using the new `const` datatype. `(set: const-type $robotText to (font:"Courier New"))` specifies that any further changes to $robotText should cause an error. * Lambdas' temp variables may now optionally have types as well, such as by writing `each str-type _name where _name contains "el"`. This checks that the correct types of data are given to the lambda. * Arrays and strings now have a `random` data name, which retrieves a random value from these structures. `(a:5,8,9)'s random` produces 5, 8 or 9. This works well with the `(move:)` macro, allowing you to randomly move values out of an array without necessarily needing to use `(shuffled:)`. * Arrays and strings now have `start` and `end` data names, which are designed to be used with `is` and `matches`. You can check if a string or array begins or ends with a certain other string or subarray by writing, for instance, `start of "Gossamer" is "Goss"`. * Added the `lambda` and `macro` datatypes (see below), and the `boolean` datatype can now be shortened to `bool`. * Added the following special datatypes: * `even`, which matches only even numbers. * `odd`, which matches only odd numbers. * `integer` (alias `int`) which matches only whole numbers. * `alphanumeric` (alias `alnum`), which matches strings that only contain letters and numbers. * `digit`, which matches strings that only contain digits. * `whitespace`, which matches strings that only contain whitespace. * `lowercase` and `uppercase`, which match single lowercase or uppercase characters. * `anycase`, which matches any character which has different lowercase or uppercase forms. * `linebreak`, which matches only a linebreak character. * `empty`, which matches only empty arrays, strings, datamaps and datasets. * Added a `transparent` built-in colour value. ##### Macros ###### Markup * Added unclosed collapsing whitespace markup `{=` (`{` followed by any number of `=`) to match the unclosed hook markup. ###### Metadata * Added some new macros, `(storylet:)` and `(open-storylets:)`, to support "storylets", an alternative way to link between groups of passages that's preferable for writing non-linear "episodic" interactive fiction. Instead of writing direct links between each episode, you instead write a requirement at the start of each episode, specifying (using a 'when' lambda) when would be the best time to let the player visit the passages. An example is `(storylet: when $season is "spring")`. Then, when you want the player to go to an episode, you use macros like `(open-storylets:)` to get a list of which storylet passages are available right now, and create links or other structures from there. Thanks to Emily Short for popularising the "storylet" design pattern. * Added an `(urgency:)` macro, which, when used alongside `(storylet:)`, alters the order in which `(open-storylets:)` sorts this passage, placing it higher than storylets with a lower "urgency" value. * Added an `(exclusivity:)` macro, which, when used alongside `(storylet:)`, causes it to be the only passage available in `(open-storylets:)` whenever it's open. Use this for very plot-critical storylets that mustn't be missed. For multiple passages with `(exclusivity:)`, the highest exclusivity values override lower ones. * Added a `(metadata:)` macro, which, when placed in a passage, adds values to the `(passage:)` datamap for that passage, allowing you to store any arbitrary data on it for your own use. This takes the same values as `(dm:)` - string names and data values in alternation. ###### Colours * Added an `(lch:)` macro (alias `(lcha:)`), an alternative colour creation macro (comparable to `(hsl:)`) that uses the LCH colour space (which is the CIELAB colour space, familiar to graphic designers, but expressed radially using a HSL-style hue angle). * Added a `(complement:)` macro, which takes a colour and produces its complement by rotating its LCH hue by 180 degrees. * Added a `(palette:)` macro, designed for rapid prototyping, which produces a four-colour palette based on a given colour, for use with `(text-colour:)`, `(background:)` and `(enchant:)`. * Added `(stripes:)`, a variant of `(gradient:)` which creates a striped background (for use with `(background:)`) when given a given stripe width, angle, and colours. ###### Values * Added a `(trunc:)` macro, which is similar to `(round:)`, but rounds toward zero, "truncating" the fractional component. It is named after the Excel function that performs the same rounding. * Added a `(plural:)` macro, which takes a number and a string, and combines them, automatically pluralising the string if the number isn't 1. `(plural:2,"duck")` produces `"2 ducks"`. Pluralisation is performed by adding "s" - a second string may be given which specifies a more correct plural form of the word (such as `(plural:4,"elf","elves")`). * Added a `(str-nth:)` macro (alias `(string-nth)`) which takes a number and produces the English ordinal of that number (such as `"2nd"` for `2`, and `"-41st"` for `-41`). ###### Data Structures * Added a `(permutations:)` macro, which, when given a sequence of data, produces an array containing all possible arrangements of that data, in arrays. * Added a `(source:)` macro, which can turn any data value into its source code representation. `(source: (text-style:"bold") + (click: ?hat's 1st))` produces the string `'(text-style:"bold")+(click:?hat's 1st)'`. * Added a `(rotated-to:)` macro, a variation of `(rotated:)` which, rather than rotating the values by a given number, takes a lambda and rotates them until the first one that matches the lambda is at the front. `(rotated-to:where it > 3, 1,2,3,4)` produces `(a:4,1,2,3)`. * Added `(unpack:)`, a variation of `(put:)` used for assigning multiple values from an array, datamap or string into multiple variables at once. You can now write `(unpack: (a:1,2,(a:3)) into (a:$x, $y, (a:$z)))`, and the array on the left will overwrite the array on the right, causing $x to become 1, $y to become 2, and $z to become 3, without needing to write multiple `(set:)` statements. You may also put values or datatypes at positions in the right side, such as in `(put: (a:1,2,3) into (a:1,2,$x))`, to check that the right side indeed has matching values at those positions, and to cause an error if they do not. * The `(move:)` macro now also supports unpacking, by providing it with the same kind of statements as `(unpack:)`. * Added `(split:)` (also known as `(splitted:)` to emphasise that it has an adjectival name) which takes a string or string pattern (see below) and uses it as a separator value with which to split up the other string given to it, producing an array of substrings. * Added `(joined:)`, which joins together the strings given to it, using the first string as a separator value. `(joined:"! ", "Liberty", "Equality", "Fraternity", "")` produces the string `"Liberty! Equality! Fraternity!"`. * Added `(trimmed:)`, a macro that removes instances of the given string pattern from the start and end of the given string (defaulting to whitespace if no pattern was given). ###### Datatypes * Added several macros that, when used together, let you construct custom string datatypes called "string patterns" that match very specific kinds of strings. These are roughly comparable to regular expressions in other programming languages. * `(p:)` takes a sequence of strings or string datatypes, and produces a datatype that only matches strings that match the entire sequence, in order. * `(p-either:)` takes one or more strings or string datatypes, and produces a datatype that only matches strings that match any one of the values. * `(p-opt:)` is a variation of `(p:)` that optionally matches the sequence - it matches strings that match the sequence, or are empty. * `(p-many:)` is a variation of `(p:)` that matches strings that match the sequence many times. You can specify the minimum and/or maximum amount of times the string can match the sequence. * `(p-ins:)` is a case-insensitive version of `(p:)`, which also treats any sub-patterns given to it as case-insensitive. * You can also use the preceding macros with `(unpack:)`. For instance, `(unpack: "Dr. Iris Cornea" into (p: (p-opt:"Dr. "), (p: str-type _firstName, whitespace, str-type _lastName)-type _fullName))` creates three variables, _firstName, _lastName and _fullName, from a single string. * Added `(datatype:)` and `(datapattern)` macros. `(datatype:)` produces the datatype that matches the given value, if it exists. `(datapattern)` is a variation that, when given arrays or datamaps, produces a data pattern that can be used with the `matches` operator, among other things, to check if the structure of one value matches the other. `(datapattern: (passage:))` produces `(dm:"name",str,"source",str,"tags",(a:str))`. ###### Changers * Added a variation of the `(enchant:)` macro, called `(change:)`, which simply applies the given changers once, without creating an ongoing effect that causes additional inserted text to be affected by it. * Added a changer variation of `(enchant:)`, `(enchant-in:)`, which restricts the effect of the enchantment to just the attached hook. This also lets you deploy (enchant:) effects alongside other changers in variables. Note that `(enchant-in:)` cannot be supplied to itself or `(enchant:)`. * Added some shorthands for using `(enchant-in:)` with certain common hook names: `(link-style:)` is a shorthand for using `(enchant-in:)` with `?Link`, `(line-style:)` is a shorthand for using `(enchant-in:)` with `?Page's lines`, and `(char-style:)` is a shorthand for using `(enchant-in:)` with `?Page's chars`. Because the Harlowe code for enchanting just the lines or characters within a hook is messy with the latter two constructions (you can't say "this hook's lines", for instance, and instead must specify the equivalent of "the page's lines which are in this hook") these macros have been created as a more intuitive alternative. Their naming is in keeping with the precedent set by `(hover-style:)`. * Added a `(text-size:)` style changer macro (also known as `(size:)`) that scales the attached hook's font size and line height by the given multiplier. * Added an `(opacity:)` style changer macro, which allows the CSS opacity of hooks to be altered. This also allows sidebar icons' opacity to be altered, using (change:) and other tools. * Added a `(transition-delay:)` macro (also known as `(t8n-delay:)`) which adds an initial delay to transitions before they begin animating. This can only enchant hooks, not links. * Added a `(transition-skip:)` macro (also known as `(t8n-skip:)`) which, when included with a transition, allows the player to speed up the transition by a given number of milliseconds per frame, by holding down any keyboard key, mouse button or touching the screen. * Added a `(collapsed:)` changer macro, which causes the attached hook's whitespace to collapse, as if by the `{` and `}` collapsing whitespace markup. This can also be used with `(enchant:)`. * Added a `(verbatim:)` changer macro (alias `(v6m:)`), which causes the attached hook or command to be printed verbatim, as if by the verbatim markup. * Added a `(text-indent:)` changer macro, which applies a leading indent to the attached hook, and can be applied to each line in the passage using, for example, `(enchant:?passage's lines, (text-indent:12))`. * Added a `(box:)` changer macro, which turns the attached hook into a box with given width and horizontal margins, a height scaling with window height, and a scroll bar if its contained prose exceeds its height. * Added a `(float-box:)` changer macro, a variant of `(box:)` which turns the hook into a separate pane floating on the window, using `position:fixed`. These have an explicit vertical position as well as horizontal position. * Added a `(text-rotate-x:)` and `(text-rotate-y:)`, which are 3D versions of `(text-rotate:)`, rotating the hook around the X or Y axis, making it appear to lean into the page with perspective. * Added the changer macros `(border:)`, `(border-size:)`, and `(border-colour:)` (aliases `(b4r:)`, `(b4r-size:)`, `(b4r-colour:)`) which add and adjust CSS borders for hooks. `(border:)` will automatically make the attached hook have `display:inline-block`, so that it remains rectangular and the border can properly enclose it. * Added a `(corner-radius:)` macro, which rounds the corners of the hook (using the CSS "border-radius" property, which, despite its name, works on elements without borders). It will also add padding, proportional to the amount of corner rounding, so that the corners don't encroach on the inner text. * Added a `(button:)` macro, which is designed especially for links. It is essentially a convenient shorthand for `(align:"=><=")+(box:"X")+(border:"solid")+(css:"padding:0px")+(corner-radius:16)`. * The following `(text-style:)` styles have been added: * "double-underline", "wavy-underline", "double-strike" and "wavy-strike", which are variants of "underline" and "strike". Be aware these will not work in Internet Explorer. * "fidget", which jolts the hook by one pixel in cardinal directions pseudorandomly. * "buoy" and "sway", which are slow, gentle movement animations to serve as counterparts to "rumble" and "shudder". * The following `(transition:)` transitions have been added: * "zoom", which makes the transitioning entity zoom in or out from the mouse cursor's position (or the last place the touch device was touched). * "fade-up", "fade-down", "fade-left", and "fade-right", which are combinations of "dissolve" with gentler sliding transitions. * "fade", which is an alias for "dissolve" (for consistency with the above, as well as the "fade-in-out" text style). * Added `(append-with:)`, `(prepend-with:)` and `(replace-with:)`, changers which modify the hook by adding a given string of code to the start or end of it, or replace it entirely. This is intended for use with things like dialogue tags or punctuation that's commonly used throughout the story. * Added two debugging changers, `(test-true:)` and `(test-false:)`, inspired by Chapbook's `ifalways` and `ifnever` mods, that ignore all of the values given to them (as well as their data types) and instead act like `(if:true)` or `(if:false)`, respectively. As with their inspirations, these are designed for quick debugging checks. ###### Commands * Added an `(input-box:)` macro, which places a `