Index: cortav.ct ================================================================== --- cortav.ct +++ cortav.ct @@ -106,26 +106,26 @@ def-tab-enc: in encodings without tab characters, a definition is opened by a line beginning with two blanks, and continued by a line beginning with four blanks. def-ex: [*open a new reference]: [`[!\\t][$key]: [$value]] [*continue a reference]: [`[!\\t\\t][$value]] * [*quotation] ([`<]): a line of the form [`<[$name]> [$quote]] denotes an utterance by [$name]. * [*blockquote] ([`>]): alternate blockquote syntax. can be nested by repeating the [`>] character. -* [*subtitle/caption] ([`\--]): attaches a subtitle to the previous header, or caption to the previous object +* [*subtitle/caption] (["--]): attaches a subtitle to the previous header, or caption to the previous object * [*embed] ([`&]): embeds a referenced object. can be used to show images or repeat previously defined objects like lists or tables, optionally with a caption. an embed line can be followed immediately by a sequence of [*definitions] in the same way that resource definitions can, to override resource properties on a per-instance basis. note that only presentation-related properties like [$desc] can be meaningful overridden, as embed does not trigger a re-render of the parse tree; if you want to override e.g. context variables, use a multiline macro invocation instead. ** [`&[$image]] embeds an image or other block-level object. [!image] can be a reference with a url or file path, or it can be an embed section (e.g. for SVG files) ***[`&myimg All that remained of the unfortunate blood magic pageant contestants and audience (police photo)] ** [`&-[$ident] [$styled-text]] embeds a closed disclosure element containing the text of the named object (a nonprinting section or cortav resource should usually be used to store the content; it can also name an image or video, of course). in interactive outputs, this will display as a block which can be clicked on to view the full contents of the referenced object [$ident]; if [$styled-text] is present, it overrides the title of the section you are embedding (if any). in static outputs, the disclosure object will display as an enclosed box with [$styled-text] as the title text *** [`&-ex-a Prosecution Exhibit A (GRAPHIC CONTENT)] ** [`&+[$section] [$styled-text]] is like the above, but the disclosure element is open by default * [`$[$macro] [$arg1]|[$arg2]|[$argn]…] invokes a block-level macro with the supplied arguments, and can be followed by a property override definition list the same way embed and resource lines can. note that while both [`$[$id]] and [`&[$id]] can be used to instantiate resources of type [`text/x.cortav], there is a critical difference: [`$[$id]] renders out the sub-document separately each time it is named, allowing for parameter expansion and for context variables to be overridden for each invocation. by contrast, [`&[$id]] can only insert copies of the same render; no parameters can be passed and context variables will be expanded to their value at the time the resource was defined. ** [`$mymacro arg 1|arg 2|arg 3] -* [*horizontal rule] ([`\---]): inserts a horizontal rule or other context break; does not end the section. must be followed by newline. underlines can also be used in place of dashes ([`___], [`-_-], [`__-__-__] etc), as can horizontal unicode box drawing characters ([`─ ━ ┈] etc). -* [*page break] ([`\^^]): for formats that support pagination, like EPUB or HTML (when printed), indicates that the rest of the current page should be blank. for formats that do not, extra margins will be inserted. does not create a new section -* [*page rule] ([`\^-^]): inserts a page break for formats that support them, and a horizontal rule for formats that do not. does not create a new section. comprised of any number of horizontal rule characters surrounded by a pair of carets (e.g. [`^-^] [`^_^] [`^----^] [`^__--^] [`^┈┈┈┈┈^]) +* [*horizontal rule] (["---]): inserts a horizontal rule or other context break; does not end the section. must be followed by newline. underlines can also be used in place of dashes ([`___], [`-_-], [`__-__-__] etc), as can horizontal unicode box drawing characters ([`─ ━ ┈] etc). +* [*page break] (["^^]): for formats that support pagination, like EPUB or HTML (when printed), indicates that the rest of the current page should be blank. for formats that do not, extra margins will be inserted. does not create a new section +* [*page rule] (["^-^]): inserts a page break for formats that support them, and a horizontal rule for formats that do not. does not create a new section. comprised of any number of horizontal rule characters surrounded by a pair of carets (e.g. [`^-^] [`^_^] [`^----^] [`^__--^] [`^┈┈┈┈┈^]) * [*table cells] ([`+ |]): see [>ex.tab table examples]. * [*equations] ([`=]): block-level equations can be inserted with the [`=] sequence * [*cross-references] ([`=>] [`⇒]): inserts a block-level link. has two forms for the sake of gemtext compatibility. [$styled-text] is a descriptive text of the destination. especially useful for menus and gemtext output. -** the cortav syntax is [`=>[$ident] [$styled-text]], where [$ident] is an identifier; links to the same destination as [` \[>[$ident] [$styled-text]\]] would +** the cortav syntax is [`=>[$ident] [$styled-text]], where [$ident] is an identifier; links to the same destination as [`\[>[$ident] [$styled-text]\]] would ** the compatibility syntax is [`=> [$uri] [$styled-text]] (note the space before [$uri]!). instead of taking an identifier for an object in the document, it directly accepts a URI. note that this is not formally equivalent to gemtext's link syntax, which also allows paths in place of URIs; [`cortav] does not. the gemtext line ["=> /somewhere] would need to be expressed as ["=> file:/somewhere], and ["=> /somewhere?key=val] as ["http:/somewhere?key=val] (or ["gemini:/somewhere?key=val], if the result is to be served over a gemini server). * [*empty lines] (that is, lines consisting of nothing but whitespace) constitute a [!break], which terminates multiline objects that do not have a dedicated termination sequence, for example lists and asides. ##onspans styled text most blocks contain a sequence of spans. these spans are produced by interpreting a stream of [*styled-text] following the control sequence. styled-text is a sequence of codepoints potentially interspersed with escapes. an escape is formed by an open square bracket [`\[] followed by a [*span control sequence], and arguments for that sequence like more styled-text. escapes can be nested. @@ -136,13 +136,13 @@ * literal {obj `|styled-text}: indicates that its text is a reference to a literal sequence of characters or other discrete token. generally rendered in monospace * variable {obj $|styled-text}: indicates to the reader that its text is a placeholder, rather than a literal representation. generally rendered in italic monospace, ideally of a different color * underline {obj _|styled-text}: underlines the text. use sparingly on text intended for webpages -- underlined text [!is] distinct from links, but underlining non-links is still a violation of convention. * strikeout {obj ~|styled-text}: indicates that its text should be struck through or otherwise indicated for deletion * insertion {obj +|styled-text}: indicates that its text should be indicated as a new addition to the text body. -** consider using a macro definition [`\edit: [~[#1]][+[#2]]] to save typing if you are doing editing work -* link [` \[>[$ref] [$styled-text]\]]: produces a hyperlink or cross-reference denoted by [$ref], which may be either a URL specified with a reference or the name of an object like an image or section elsewhere in the document. the unicode characters [`→] and [`🔗] can also be used instead of [`>] to denote a link. -* footnote {span ^|ref|[$styled-text]}: annotates the text with a defined footnote. in interactive output media [`\[^citations.qtheo Quantum Theosophy: A Neophyte's Catechism\]] will insert a link with the text [`Quantum Theosophy: A Neophyte's Catechism] that, when clicked, causes a footnote to pop up on the screen. for static output media, the text will simply have a superscript integer after it denoting where the footnote is to be found. +** consider using a macro definition ["edit: [~[#1]][+[#2]]] to save typing if you are doing editing work +* link [`\[>[$ref] [$styled-text]\]]: produces a hyperlink or cross-reference denoted by [$ref], which may be either a URL specified with a reference or the name of an object like an image or section elsewhere in the document. the unicode characters [`→] and [`🔗] can also be used instead of [`>] to denote a link. +* footnote {span ^|[$ref]|[$styled-text]}: annotates the text with a defined footnote. in interactive output media ["[^citations.qtheo Quantum Theosophy: A Neophyte's Catechism]] will insert a link with the text [`Quantum Theosophy: A Neophyte's Catechism] that, when clicked, causes a footnote to pop up on the screen. for static output media, the text will simply have a superscript integer after it denoting where the footnote is to be found. [$ref] can be the ID of a reference, in which case the reference value is parsed as [`cortav] markup to form the body of the footnote; it can also be the ID of a resource, which can be of any MIME type compatible with the current renderer, as as [`text/x.cortav], [`text/plain], or [`image/png]. * superscript {obj '|[$styled-text]} * subscript {obj ,|[$styled-text]} * raw {obj \\ |[$raw-text]}: causes all characters within to be interpreted literally, without expansion. the only special characters are square brackets, which must have a matching closing bracket, and backslashes. * raw literal [` \["[$raw-text]\]]: shorthand for a raw inside a literal, that is ["[`[\\…]]] * macro [` \{[$name] [$arguments]}]: invokes a [>ex.mac macro] inline, specified with a reference. if the result of macro expansion contains newlines, they will be treated as line breaks, rather than paragraph breaks as they would be in a multiline context. @@ -647,14 +647,14 @@ %% context variables are useful because they inherit from the enclosing context %% thus, we can exploit resource syntax to create templates with default values @agent { - + CODENAME :| [#1] - + CIVILIAN IDENTITY :| [#civil] + + CODENAME :| [#1] + + CIVILIAN IDENTITY :| [#civil] + RULES of ENGAGEMENT :| [#roe] - + DANGER LEVEL :| [#danger] + + DANGER LEVEL :| [#danger] } .civil: (unknown) .roe: Monitor; do not engage .danger: (unknown) @@ -674,13 +674,13 @@ | rakewyrm:| hirvag | hi phang | nache umwelinde | | eat :| fese | dzia | rotechqa | and now the other way around! -+:english :| honor | -+:ranuir :| tef | -+:zia ţai :| pang | ++:english :| honor | ++:ranuir :| tef | ++:zia ţai :| pang | +:thalishte:| mbecheve | ~~~ ##extns extensions the cortav specification also specifies a number of extensions that do not have to be supported for a renderer to be compliant. the extension mechanism supports the following directives. @@ -754,11 +754,11 @@ ###lua lua renderers with a lua interpreter available can evaluate lua code: * [`%lua use [!file]]: evaluates [$file] and makes its definitions available * [`\[%lua raw [!script]\]]: evaluates [$script] and emits the string it returns (if any) in raw span context. -* [` \[%lua exp [!script]\]]: evaluates [$script] and emits the string it returns (if any) in expanded span context. +* [`\[%lua exp [!script]\]]: evaluates [$script] and emits the string it returns (if any) in expanded span context. * [`%lua raw [!script]]: evaluates [$script] and emits the string array it returns (if any) in raw block context. * [`%lua exp [!script]]: evaluates [$script] and emits the string array it returns (if any) in expanded block context. the interpreter should provide a [`cortav] table with the objects: * [`ctx]: contains context variables @@ -776,12 +776,12 @@ * [`%[*ts] word [$scope word] ([$styled-text])]: indicates a codeword clearance that must be present for the text to render. if styled-text is present, this will be used to render the name of the codeword instead of [$word]. * [`%[*when] ts level [$level]] * [`%[*when] ts word [$word]] [`ts] enables the spans: -* [` \[🔒#[$level] [$styled-text]\]]: redacts the span if the security level is below that specified. -* [` \[🔒.[$word] [$styled-text]\]]: redacts the span if the specified codeword clearance is not enabled. +* [`\[🔒#[$level] [$styled-text]\]]: redacts the span if the security level is below that specified. +* [`\[🔒.[$word] [$styled-text]\]]: redacts the span if the specified codeword clearance is not enabled. (the padlock emoji is shorthand for [`%[*ts]].) [`ts] redacts spans securely; that is, they are simply replaced with an indicator that they have been redacted, without visually leaking the length of the redacted text. redacted sections are simply omitted. ~~~#ts-example example [cortav] ~~~ @@ -947,11 +947,11 @@ corran: http://ʞ.cc/fic/spirals/society tengwar: https://en.wikipedia.org/wiki/Tengwar ###refimpl-switches switches [`cortav.lua] offers various switches to control its behavior. -+ long + short + function + ++ long + short + function + | [`--out [$file]] :|:[`-o]:| sets the output file (default stdout) | | [`--log [$file]] :|:[`-l]:| sets the log file (default stderr) | | [`--define [$var] [$val]] :|:[`-d]:| sets the context variable [$var] to [$val] | | [`--mode-set [$mode]] :|:[`-y]:| activates the [>refimpl-mode mode] with ID [!mode] | [`--mode-clear [$mode]] :|:[`-n]:| disables the mode with ID [!mode] | Index: cortav.lua ================================================================== --- cortav.lua +++ cortav.lua @@ -740,32 +740,29 @@ ct.spanctls = { {seq = '!', parse = formatter 'emph'}; {seq = '*', parse = formatter 'strong'}; {seq = '~', parse = formatter 'strike'}; {seq = '+', parse = formatter 'insert'}; - {seq = '"', parse = rawcode}; - -- deprecated - {seq = '`\\', parse = rawcode}; - {seq = '\\\\', parse = rawcode}; {seq = '\\', parse = function(s, c) -- raw return { kind = 'raw'; spans = {s}; origin = c:clone(); } end}; {seq = '`', parse = formatter 'literal'}; + {seq = '"', parse = rawcode}; {seq = '$', parse = formatter 'variable'}; - {seq = '^', parse = function(s,c) --footnotes - local r, t = s:match '^([^%s]+)%s*(.-)$' + {seq = '^', parse = function(s, c) + -- TODO support for footnote sections + local fn, t = s:match '^([^%s]+)%s*(.-)$' return { kind = 'footnote'; - ref = r; - spans = ct.parse_span(t, c); + spans = (t and t~='') and ct.parse_span(t, c) or {}; + ref = fn; origin = c:clone(); } - -- TODO support for footnote sections end}; {seq = '=', parse = function(s,c) --math mode local tx = { ['%*'] = '×'; ['/'] = '÷'; @@ -791,19 +788,10 @@ local r, t = s:match '^([^%s]+)%s*(.-)$' return { kind = 'deref'; spans = (t and t ~= "") and ct.parse_span(t, c) or {}; ref = r; - origin = c:clone(); - } - end}; - {seq = '^', parse = function(s, c) - local fn, t = s:match '^([^%s]+)%s*(.-)$' - return { - kind = 'footnote'; - spans = (t and t~='') and ct.parse_span(t, c) or {}; - ref = fn; origin = c:clone(); } end}; {seq = '>', parse = insert_link}; {seq = '→', parse = insert_link}; Index: desk/cortav.xml ================================================================== --- desk/cortav.xml +++ desk/cortav.xml @@ -8,14 +8,18 @@ ! NOTE: the Kate syntax engine cannot capture all the syntactic properties of Cortav. we do the best we can, but note the following important discrepancies: 1) the inline resource syntax allows a wide range of complex - brackets pairs such as ; the Kate syntax only + brackets pairs such as ; the Kate syntax only accounts for the pair { } --> - + uses needs inhibits @@ -160,11 +164,10 @@ - Index: ext/toc.lua ================================================================== --- ext/toc.lua +++ ext/toc.lua @@ -223,17 +223,21 @@ local ent = tag('li',nil, catenate{tag('a', {href='#'..getSafeID(sec)}, sr.htmlSpan(sec.heading_node.spans, sec.heading_node, sec))}) if secptr.depth > #stack then local n = {tag = 'ol', attrs={}, nodes={ent}} - table.insert(top().nodes[#top().nodes].nodes, n) + if top() then + table.insert(top().nodes[#top().nodes].nodes, n) + end table.insert(stack, n) else if secptr.depth < #stack then for j=#stack,secptr.depth+1,-1 do stack[j] = nil end end - table.insert(top().nodes, ent) + if top() then + table.insert(top().nodes, ent) + end end -- now we need to assemble a list of items within the -- section worthy of an entry on their own. currently -- this is only anchors created with %toc mark|name Index: render/html.lua ================================================================== --- render/html.lua +++ render/html.lua @@ -57,10 +57,21 @@ } ]]; list_ordered = [[]]; list_unordered = [[]]; footnote = [[ + @media screen { + a[href].fnref { + text-decoration-style: dashed; + color: @tone(0.7 45); + text-decoration-color: @tone/0.4(0.7 45); + } + a[href]:hover.fnref { + color: @tone(0.9 45); + text-decoration-color: @tone/0.7(0.7 45); + } + } aside.footnote { font-family: 90%; grid-template-columns: 1em 1fr min-content; grid-template-rows: 1fr min-content; position: fixed; @@ -100,10 +111,16 @@ aside.footnote:target ~ #cover { opacity: 100%; pointer-events: all; backdrop-filter: blur(5px); } + } + @media screen and (max-width: calc(@width + 20em)) { + aside.footnote { + left: 1em; + right: 1em; + } } @media print { aside.footnote { display: grid; position: relative; @@ -147,17 +164,18 @@ aside.footnote > div.number { text-align:right; grid-row: 1/2; grid-column: 1/2; } - aside.footnote > div.text { + aside.footnote > .text { grid-row: 1/2; grid-column: 2/4; padding-left: 1em; overflow-y: auto; + margin-top: 0; } - aside.footnote > div.text > p:first-child { + aside.footnote > .text > :first-child { margin-top: 0; } ]]; header = [[ body { padding: 0 2.5em !important } @@ -763,10 +781,11 @@ local linkattr = {} if opts.epub then linkattr['epub:type'] = 'noteref' else addStyle 'footnote' + linkattr.class = 'fnref' end local source, sid, ssec = b.origin:ref(f.ref) local cnc = getSafeID(ssec) .. ' ' .. sid local fn if footnotes[cnc] then @@ -895,12 +914,12 @@ end if next(secnodes) then if doc.secorder[2] then --#secs>1? -- only wrap in a section if >1 section table.insert(ir, tag('section', - {id = getSafeID(sec)}, - secnodes)) + {id = getSafeID(sec)}, + secnodes)) else ir = secnodes end end end @@ -1191,16 +1210,37 @@ fnsorted[fn.num] = fn end for _, fn in ipairs(fnsorted) do local tag = tagproc.toIR.tag - local body = {nodes={}} - local ftir = {} - for l in fn.source:gmatch('([^\n]*)') do - ct.parse_line(l, fn.origin, ftir) + local body + if type(fn.source) == 'table' then + if fn.source.kind == 'resource' then + local fake_embed = { + kind = 'embed'; + rsrc = fn.source; + origin = fn.origin; + mode = 'inline'; + } + local rendered = astproc.toIR.block_renderers.embed( + fake_embed, fn.origin.sec + ) + if not rendered then + fn.origin:fail('unacceptable resource mime type “%s” for footnote target “%s”', fn.source.mime, fn.source.id or '(anonymous)') + end + body = rendered + else + fn.origin:fail('footnote span links to block “%s” of unacceptable kind “%s”', fn.source.kind) + end + else + body = {tag='div',nodes={}} + local ftir = {} + for l in fn.source:gmatch('([^\n]*)') do + ct.parse_line(l, fn.origin, ftir) + end + renderBlocks(ftir,body) end - renderBlocks(ftir,body) local fattr = {id=fn.id} if opts.epub then ---UUUUUUGHHH local npfx = string.format('(%u) ', fn.num) if next(body.nodes) then @@ -1217,19 +1257,28 @@ break end until false else - body.nodes[1] = {tag='p',attrs={},nodes={npfx}} + if body.tag == 'div' then + body.nodes[1] = {tag='p',attrs={},nodes={npfx}} + elseif body.tag == 'pre' then + body.nodes[1] = npfx .. body.nodes[1] + else + body = {tag='div', nodes = {npfx, body}} + end end fattr['epub:type'] = 'footnote' else fattr.class = 'footnote' end + body.attrs = body.attrs or {} + body.attrs.class = 'text' local note = tag('aside', fattr, opts.epub and body.nodes or { tag('div',{class='number'}, tostring(fn.num)), - tag('div',{class='text'}, body.nodes), + body, +-- tag('div',{class='text'}, body.nodes), tag('a',{href='#0'},'⤫') }) table.insert(ir, note) end end