@@ -404,8 +404,48 @@ h1,h2,h3,h4,h5,h6 { page-break-after: avoid; } ]]; + linkBlock = [[ + a[href].link { + position: relative; + display: block; + padding: .5em; + padding-right: 1.5em; + border: 1px solid @tone(0.2 30); + background: @tone(0.05 30); + font-size: 1.1em; + margin: 0 1em; + text-decoration: none; + color: @tone(0.8 30); + } + a[href].link + a[href].link { + margin-top: -1px; + } + a[href].link:hover { + border-color: @tone(0.3 30); + background: @tone(0.2 30); + color: @tone(0.95 30); + } + a[href].link:hover + a[href].link { + margin-top: 0; + border-top: none; + } + a[href].link::after { + display: block; + position: absolute; + right: .5em; + content: "→"; + top: 50%; + margin-left: 1em; + font-size: 1.8em; + transform: translateY(-50%); + color: @tone(0.3 30); + } + a[href].link:hover::after { + color: @tone(0.7 30); + } + ]]; } local stylesNeeded = { flags = {}; @@ -450,8 +490,58 @@ local runhook = function(h, ...) return renderJob:hook(h, render_state_handle, ...) end + + local function htmlURI(uri) + local family = uri:canfetch() + if family == 'file' then + if uri.namespace == 'localhost' then + -- emit an actual file url + return 'file://' .. uri:construct('path','frag') + elseif uri.namespace == nil then + -- this is gonna be tricky. first we establish the location + -- of the CWD/asset base relative to the output file (if any; + -- assume equivalent otherwise) then express the difference + -- as a directory prefix. + -- jk tho for now we just emit the path+frag sadlol TODO + if uri.path == nil and uri.frag then + -- file:#sec links to #sec within the current document + return uri:part 'frag' + else + return uri:construct('path','frag') + end + else + b.origin:fail('file: URI namespace must be empty or “localhost” for HTML links; others are not meaningful (offending URI: “%s”)', uri.raw) + end + elseif family == 'http' then + local sc = 'http' + if uri.class[1] == 'https' or uri.class[2] == 'tls' then + sc = 'https' + end + if uri.namespace == nil and uri.auth == nil and uri.svc == nil then + -- omit the scheme so we can use a relative path + return uri:construct('path','query','frag') + else + uri.class = {sc} + return tostring(uri) + end + else return tostring(uri) end + end + + local function idLink(id,b) + local dest_o, _, dest_s = b.origin:ref(id) + if dest_o == nil then + -- link is to the section itself + return '#' .. getSafeID(dest_s) + else + if type(dest_o) == 'table' then + return '#' .. getSafeID(dest_o) + else -- URI in reference + return htmlURI(ss.uri(dest_o)) + end + end + end local tagproc do local html_open = function(t,attrs) if attrs then @@ -614,50 +704,10 @@ return htmlSpan(v.spans, b, s) end function span_renderers.link(sp,b,s) - local dest_o, _, dest_s = b.origin:ref(sp.ref) - local href - if dest_o == nil then - -- link is to the section itself - href = '#' .. getSafeID(dest_s) - else --- if sp.addr then href = sp.addr else - if type(dest_o) == 'table' then - href = '#' .. getSafeID(dest_o) - else -- URI in reference - local uri = ss.uri(dest_o) - if uri.class[1] == 'file' - or uri.class[1] == 'asset' then - if uri.namespace == 'localhost' then - -- emit an actual file url - href = 'file://' .. uri:construct('path','frag') - elseif uri.namespace == nil then - -- this is gonna be tricky. first we establish the location - -- of the CWD/asset base relative to the output file (if any; - -- assume equivalent otherwise) then express the difference - -- as a directory prefix. - -- jk tho for now we just emit the path+frag sadlol TODO - href = uri:construct('path','frag') - else - b.origin:fail('file: URI namespace must be empty or “localhost” for HTML links; others are not meaningful (offending URI: “%s”)', dest_o) - end - elseif uri:canfetch() == 'http' then - local sc = 'http' - if uri.class[1] == 'https' or uri.class[2] == 'tls' then - sc = 'https' - end - if uri.namespace == nil and uri.auth == nil and uri.svc == nil then - -- omit the scheme so we can use a relative path - href = uri:construct('path','query','frag') - else - uri.class = {sc} - href = tostring(uri) - end - else href = tostring(uri) end - end - end - return tag('a',{href=href},next(sp.spans) and htmlSpan(sp.spans,b,s) or href) + local href = idLink(sp.ref,b) + return tag('a',{href=href}, next(sp.spans) and htmlSpan(sp.spans,b,s) or href) end span_renderers['line-break'] = function(sp,b,s) return elt('br') @@ -776,8 +826,20 @@ end; ['list-item'] = function(b,s) return tag('li', nil, sr.htmlSpan(b.spans, b, s), b) end; + link = function(b,s) + addStyle 'linkBlock' + local href + if b.uri then + href = htmlURI(b.uri) + elseif b.ref then + href = idLink(b.ref, b) + end + local sp = sr.htmlSpan(b.spans, b, s) + return tag('div', {}, + catenate{tag('a',{class='link', href=href},sp)}) + end; table = function(b,s) local tb = {} for i, r in ipairs(b.rows) do local row = {} @@ -866,18 +928,8 @@ if b.rsrc then obj = b.rsrc else obj = b.origin:ref(b.ref) end - local function htmlURI(u) - local family = u:canfetch() - if family == 'file' or - (family == 'http' and u.namespace == nil) then - -- TODO asset: - return u.path - else - return tostring(u) - end - end local function uriForSource(s) if s.mode == 'link' or s.mode == 'auto' then return htmlURI(s.uri) elseif s.mode == 'embed' then