cortav  Diff

Differences From Artifact [1e64ee70c7]:

To Artifact [0a87e02f8c]:


    20     20   		lisp = { color = 0x77ff88 };
    21     21   		fortran = { color = 0xff779a };
    22     22   		python = { color = 0xffd277 };
    23     23   		ruby = { color = 0xcdd6ff };
    24     24   	}
    25     25   
    26     26   	local stylesets = {
           27  +		list = [[
           28  +			@counter-style enclosed {
           29  +				system: extends decimal;
           30  +				prefix: "(";
           31  +				suffix: ") ";
           32  +			}
           33  +			ul, ol {
           34  +				padding: 0 1em;
           35  +			}
           36  +			li {
           37  +				padding: 0.1em 0;
           38  +			}
           39  +		]];
           40  +		list_ordered = [[]];
           41  +		list_unordered = [[]];
    27     42   		footnote = [[
    28     43   			div.footnote {
    29         -			font-family: 90%;
    30         -				display: none;
           44  +				font-family: 90%;
    31     45   				grid-template-columns: 1em 1fr min-content;
    32     46   				grid-template-rows: 1fr min-content;
    33     47   				position: fixed;
    34     48   				padding: 1em;
    35         -				background: @tone(0.05);
    36         -				border: black;
           49  +				background: @tone(0.03);
    37     50   				margin:auto;
    38     51   			}
    39         -			div.footnote:target { display:grid; }
    40     52   			@media screen {
    41     53   				div.footnote {
           54  +					display: grid;
    42     55   					left: 10em;
    43     56   					right: 10em;
    44     57   					max-width: calc(@width + 2em);
    45     58   					max-height: 30vw;
    46     59   					bottom: 1em;
           60  +					border: 1px solid black;
           61  +					transform: translateY(200%);
           62  +					transition: 0.4s;
           63  +					z-index: 100;
           64  +				}
           65  +				div.footnote:target {
           66  +					transform: translateY(0%);
           67  +				}
           68  +				#cover {
           69  +					position: fixed;
           70  +					top: 0;
           71  +					left: 0;
           72  +					height: 100vh; width: 100vw;
           73  +					background: linear-gradient(to top,
           74  +						@tone/0.8(-0.07),
           75  +						@tone/0.4(-0.07));
           76  +					opacity: 0%;
           77  +					transition: 1s;
           78  +					pointer-events: none;
           79  +					backdrop-filter: blur(0px);
           80  +				}
           81  +				div.footnote:target ~ #cover {
           82  +					opacity: 100%;
           83  +					pointer-events: all;
           84  +					backdrop-filter: blur(5px);
    47     85   				}
    48     86   			}
    49     87   			@media print {
    50     88   				div.footnote {
           89  +					display: grid;
    51     90   					position: relative;
    52     91   				}
    53     92   				div.footnote:first-of-type {
    54     93   					border-top: 1px solid black;
    55     94   				}
    56     95   			}
    57     96   
    58     97   			div.footnote > a[href="#0"]{
    59     98   				grid-row: 2/3;
    60     99   				grid-column: 3/4;
    61    100   				display: block;
    62         -				padding: 0.2em 0.7em;
    63    101   				text-align: center;
          102  +				padding: 0 0.3em;
    64    103   				text-decoration: none;
    65    104   				background: @tone(0.2);
    66    105   				color: @tone(1);
    67    106   				border: 1px solid black;
    68    107   				margin-top: 0.6em;
          108  +				font-size: 150%;
    69    109   				-webkit-user-select: none;
    70    110   				-ms-user-select: none;
    71    111   				user-select: none;
    72    112   				-webkit-user-drag: none;
    73    113   				user-drag: none;
    74    114   			}
    75    115   			div.footnote > a[href="#0"]:hover {
................................................................................
    90    130   				grid-row: 1/2;
    91    131   				grid-column: 1/2;
    92    132   			}
    93    133   			div.footnote > div.text {
    94    134   				grid-row: 1/2;
    95    135   				grid-column: 2/4;
    96    136   				padding-left: 1em;
    97         -				overflow-y: scroll;
          137  +				overflow-y: auto;
          138  +			}
          139  +			div.footnote > div.text > p:first-child {
          140  +				margin-top: 0;
    98    141   			}
    99    142   		]];
   100    143   		header = [[
   101    144   			body { padding: 0 2.5em !important }
   102    145   			h1,h2,h3,h4,h5,h6 { border-bottom: 1px solid @tone(0.7); }
   103    146   			h1 { font-size: 200%; border-bottom-style: double !important; border-bottom-width: 3px !important; margin: 0em -1em; }
   104    147   			h2 { font-size: 130%; margin: 0em -0.7em; }
................................................................................
   205    248   			section > aside p:first-child {
   206    249   				margin: 0;
   207    250   			}
   208    251         ]];
   209    252   		code = [[
   210    253   			code {
   211    254   				display: inline-block;
   212         -				background: @tone(0.9);
   213         -				color: @bg;
          255  +				background: @tone(-1);
          256  +				color: @tone(0.7);
   214    257   				font-family: monospace;
   215    258   				font-size: 90%;
   216         -				padding: 3px 5px;
          259  +				padding: 2px 5px;
          260  +				user-select: all;
   217    261   			}
   218    262   		]];
   219    263   		var = [[
   220    264   			var {
   221    265   				font-style: italic;
   222    266   				font-family: monospace;
   223    267   				color: @tone(0.7);
          268  +				font-size: 90%;
   224    269   			}
   225    270   			code var {
   226         -				color: @tone(0.25);
          271  +				color: @tone(0.4);
   227    272   			}
   228    273   		]];
   229    274   		math = [[
   230    275   			span.equation {
   231    276   				display: inline-block;
   232    277   				background: @tone(0.08);
   233    278   				color: @tone(2);
................................................................................
   238    283   		abbr = [[
   239    284   			abbr[title] { cursor: help; }
   240    285   		]];
   241    286   		editors_markup = [[]];
   242    287   		block_code_listing = [[
   243    288   			figure.listing {
   244    289   				font-family: monospace;
   245         -				background: @tone(0.05);
   246         -				color: @fg;
          290  +				background: @tone(0.05 20);
          291  +				color: @tone(1 20);
   247    292   				padding: 0;
   248    293   				margin: 0.3em 0;
   249    294   				counter-reset: line-number;
   250    295   				position: relative;
   251         -				border: 1px solid @fg;
          296  +				border: 1px solid @tone(1 20);
   252    297   			}
          298  +			:not(figure.listing) + figure.listing {
          299  +            margin-top: 1em;
          300  +         }
          301  +			figure.listing + :not(figure.listing) {
          302  +				margin-top: 1em;
          303  +         }
   253    304   			figure.listing>div {
   254    305   				white-space: pre-wrap;
   255    306   				tab-size: 3;
   256    307   				-moz-tab-size: 3;
   257    308   				counter-increment: line-number;
   258    309   				text-indent: -2.3em;
   259    310   				margin-left: 2.3em;
   260    311   			}
   261    312   			figure.listing>:is(div,hr)::before {
   262    313   				width: 1.0em;
   263    314   				padding: 0.2em 0.4em;
   264    315   				text-align: right;
   265    316   				display: inline-block;
   266         -				background-color: @tone(0.2);
          317  +				background-color: @tone(0.2 20);
   267    318   				border-right: 1px solid @fg;
   268    319   				content: counter(line-number);
   269    320   				margin-right: 0.3em;
   270    321   			}
   271    322   			figure.listing>hr::before {
   272    323   				color: transparent;
   273    324   				padding-top: 0;
   274    325   				padding-bottom: 0;
   275    326   			}
   276    327   			figure.listing>div::before {
   277         -				color: @fg;
          328  +				color: @tone(1 20);
   278    329   			}
   279    330   			figure.listing>div:last-child::before {
   280    331   				padding-bottom: 0.5em;
   281    332   			}
   282    333   			figure.listing>figcaption:first-child {
   283    334   				border: none;
   284         -				border-bottom: 1px solid @fg;
          335  +				border-bottom: 1px solid @tone(1 20);
   285    336   			}
   286    337   			figure.listing>figcaption::after {
   287    338   				display: block;
   288    339   				float: right;
   289    340   				font-weight: normal;
   290    341   				font-style: italic;
   291    342   				font-size: 70%;
................................................................................
   292    343   				padding-top: 0.3em;
   293    344   			}
   294    345   			figure.listing>figcaption {
   295    346   				font-family: sans-serif;
   296    347   				font-size: 120%;
   297    348   				padding: 0.2em 0.4em;
   298    349   				border: none;
   299         -				color: @tone(2);
          350  +				color: @tone(2 20);
   300    351   			}
   301    352   			figure.listing > hr {
   302    353   				border: none;
   303    354   				margin: 0;
   304    355   				height: 0.7em;
   305    356   				counter-increment: line-number;
   306    357   			}
   307    358   		]];
          359  +		root = [[
          360  +			body {
          361  +				font-size: 16pt;
          362  +				page-break-before: always;
          363  +			}
          364  +			h1 {
          365  +				page-break-before: always;
          366  +			}
          367  +			h1,h2,h3,h4,h5,h6 {
          368  +				page-break-after: avoid;
          369  +			}
          370  +		]];
          371  +	}
          372  +
          373  +	local stylesNeeded = {
          374  +		flags = {};
          375  +		order = {};
   308    376   	}
          377  +	local function addStyle(sty)
          378  +		-- convenience function, also just in case i end up having
          379  +		-- to change the goddamn implementation again
          380  +		if not stylesNeeded.flags[sty] then
          381  +			stylesNeeded.flags[sty] = true
          382  +			table.insert(stylesNeeded.order, sty)
          383  +			return true
          384  +		end
          385  +		return false
          386  +	end
   309    387   
   310         -	local stylesNeeded = {}
          388  +	addStyle 'root'
   311    389   
   312    390   	local render_state_handle = {
   313    391   		doc = doc;
   314    392   		opts = opts;
   315    393   		style_rules = styles; -- use stylesneeded if at all possible
          394  +		style_add = addStyle;
   316    395   		stylesets = stylesets;
   317    396   		stylesets_active = stylesNeeded;
   318    397   		obj_htmlid = getSafeID;
   319    398   		-- remaining fields added later
   320    399   	}
   321    400   
   322    401   	local renderJob = doc:job('render_html', nil, render_state_handle)
................................................................................
   400    479   	local spanparse = function(...)
   401    480   		local s = ct.parse_span(...)
   402    481   		doc.docjob:hook('meddle_span', s)
   403    482   		return s
   404    483   	end
   405    484   
   406    485   	local cssRulesFor = {}
          486  +	function getCSSImageForResource(r)
          487  +		return '' -- TODO
          488  +	end
          489  +
   407    490   	local function getSpanRenderers(procs)
   408    491   		local tag, elt, catenate = procs.tag, procs.elt, procs.catenate
   409    492   		local span_renderers = {}
   410    493   		local plainrdr = getBaseRenderers(tagproc.toTXT, span_renderers)
   411    494   		local htmlSpan = getBaseRenderers(procs, span_renderers).htmlSpan
   412    495   
   413    496   		function span_renderers.format(sp,...)
   414    497   			local tags = { strong = 'strong', emph = 'em', strike = 'del', insert = 'ins', literal = 'code', variable = 'var'}
   415    498   			if sp.style == 'literal' and not opts['fossil-uv'] then
   416         -				stylesNeeded.code = true
          499  +				addStyle 'code'
   417    500   			elseif sp.style == 'strike' or sp.style == 'insert' then
   418         -				stylesNeeded.editors_markup = true
          501  +				addStyle 'editors_markup'
   419    502   			elseif sp.style == 'variable' then
   420         -				stylesNeeded.var = true
          503  +				addStyle 'var'
   421    504   			end
   422    505   			return tag(tags[sp.style],nil,htmlSpan(sp.spans,...))
   423    506   		end
   424    507   
   425    508   		function span_renderers.deref(t,b,s)
   426    509   			local r = b.origin:ref(t.ref)
   427    510   			local name = t.ref
   428    511   			if name:find'%.' then name = name:match '^[^.]*%.(.+)$' end
   429    512   			if type(r) == 'string' then
   430         -				stylesNeeded.abbr = true
          513  +				addStyle 'abbr'
   431    514   				return tag('abbr',{title=r},next(t.spans) and htmlSpan(t.spans,b,s) or name)
   432    515   			end
   433    516   			if r.kind == 'resource' then
   434    517   				local rid = getSafeID(r, 'res-')
   435    518   				if r.class == 'image' then
   436    519   					if not cssRulesFor[r] then
   437    520   						local css = prepcss(string.format([[
   438    521   							section p > .%s {
          522  +								background: %s;
   439    523   							}
   440         -						]], rid))
          524  +						]], rid, getCSSImageForResource(r)))
   441    525   						stylesets[r] = css
   442    526   						cssRulesFor[r] = css
   443         -						stylesNeeded[r] = true
          527  +						addStyle(r)
   444    528   					end
   445         -					return tag('div',{class=rid},catenate{'blaah'})
          529  +					return tag('div',{class=rid},catenate{''})
   446    530   				elseif r.class == 'video' then
   447    531   					local vid = {}
   448    532   					return tag('video',nil,vid)
   449    533   				elseif r.class == 'font' then
   450    534   					b.origin:fail('fonts cannot be instantiated, use %font directive instead')
   451    535   				end
   452    536   			else
................................................................................
   504    588   				b.origin:fail('%s is an object, not a reference', t.ref)
   505    589   			end
   506    590   			local mctx = b.origin:clone()
   507    591   			mctx.invocation = m
   508    592   			return htmlSpan(ct.parse_span(r, mctx),b,s)
   509    593   		end
   510    594   		function span_renderers.math(m,b,s)
   511         -			stylesNeeded.math = true
          595  +			addStyle 'math'
   512    596   			local spans = {}
   513    597   			local function fmt(sp, target)
   514    598   				for i,v in ipairs(sp) do
   515    599   					if type(v) == 'string' then
   516    600   						local x = ct.tool.mathfmt(b.origin, v)
   517    601   						for _,v in ipairs(x) do
   518    602   							table.insert(target, v)
................................................................................
   539    623   			elseif d.crit then
   540    624   				b.origin:fail('critical extension %s unavailable', d.ext)
   541    625   			elseif d.failthru then
   542    626   				return htmlSpan(d.spans, b, s)
   543    627   			end
   544    628   		end
   545    629   		function span_renderers.footnote(f,b,s)
   546         -			stylesNeeded.footnote = true
          630  +			addStyle 'footnote'
   547    631   			local source, sid, ssec = b.origin:ref(f.ref)
   548    632   			local cnc = getSafeID(ssec) .. ' ' .. sid
   549    633   			local fn
   550    634   			if footnotes[cnc] then
   551    635   				fn = footnotes[cnc]
   552    636   			else
   553    637   				footnotecount = footnotecount + 1
................................................................................
   567    651   		local null = function() return catenate{} end
   568    652   
   569    653   		local block_renderers = {
   570    654   			anchor = function(b,s)
   571    655   				return tag('a',{id = getSafeID(b)},null())
   572    656   			end;
   573    657   			paragraph = function(b,s)
   574         -				stylesNeeded.paragraph = true;
          658  +				addStyle 'paragraph'
   575    659   				return tag('p', nil, sr.htmlSpan(b.spans, b, s), b)
   576    660   			end;
   577    661   			directive = function(b,s)
   578    662   				-- deal with renderer directives
   579    663   				local _, cmd, args = b.words(2)
   580    664   				if cmd == 'page-title' then
   581    665   					if not opts.title then doctitle = args end
................................................................................
   582    666   				elseif b.critical then
   583    667   					b.origin:fail('critical HTML renderer directive “%s” not supported', cmd)
   584    668   				end
   585    669   			end;
   586    670   			label = function(b,s)
   587    671   				if ct.sec.is(b.captions) then
   588    672   					if not (opts['fossil-uv'] or opts.snippet) then
   589         -						stylesNeeded.header = true
          673  +						addStyle 'header'
   590    674   					end
   591    675   					local h = math.min(6,math.max(1,b.captions.depth))
   592    676   					return tag(f('h%u',h), nil, sr.htmlSpan(b.spans, b, s), b)
   593    677   				else
   594    678   					-- handle other uses of labels here
   595    679   				end
   596    680   			end;
................................................................................
   606    690   						{align=c.align}, sr.htmlSpan(c.spans, b)))
   607    691   					end
   608    692   					table.insert(tb, tag('tr',nil,catenate(row)))
   609    693   				end
   610    694   				return tag('table',nil,catenate(tb))
   611    695   			end;
   612    696   			listing = function(b,s)
   613         -				stylesNeeded.block_code_listing = true
          697  +				addStyle 'block_code_listing'
   614    698   				local nodes = ss.map(function(l)
   615    699   					if #l > 0 then
   616    700   						return tag('div',nil,sr.htmlSpan(l, b, s))
   617    701   					else
   618    702   						return elt('hr')
   619    703   					end
   620    704   				end, b.lines)
................................................................................
   622    706   					table.insert(nodes,1, tag('figcaption',nil,sr.htmlSpan(b.title)))
   623    707   				end
   624    708   				if b.lang then langsused[b.lang] = true end
   625    709   				return tag('figure', {class='listing', lang=b.lang, id=b.id and getSafeID(b)}, catenate(nodes))
   626    710   			end;
   627    711   			aside = function(b,s)
   628    712   				local bn = {}
   629         -				stylesNeeded.aside = true
          713  +				addStyle 'aside'
   630    714   				if #b.lines == 1 then
   631    715   					bn[1] = sr.htmlSpan(b.lines[1], b, s)
   632    716   				else
   633    717   					for _,v in pairs(b.lines) do
   634    718   						table.insert(bn, tag('p', {}, sr.htmlSpan(v, b, s)))
   635    719   					end
   636    720   				end
................................................................................
   688    772   						tagproc = tagproc.toIR;
   689    773   						astproc = astproc.toIR;
   690    774   					}, block, sec)
   691    775   				end
   692    776   			end
   693    777   			if rd then
   694    778   				if opts['heading-anchors'] and block == sec.heading_node then
   695         -					stylesNeeded.headingAnchors = true
          779  +					addStyle 'headingAnchors'
   696    780   					table.insert(rd.nodes, ' ')
   697    781   					table.insert(rd.nodes, {
   698    782   						tag = 'a';
   699    783   						attrs = {href = '#' .. irs.attrs.id, class='anchor'};
   700    784   						nodes = {type(opts['heading-anchors'])=='string' and opts['heading-anchors'] or '§'};
   701    785   					})
   702    786   				end
................................................................................
   735    819   		for l in fn.source:gmatch('([^\n]*)') do
   736    820   			ct.parse_line(l, fn.origin, ftir)
   737    821   		end
   738    822   		renderBlocks(ftir,body)
   739    823   		local note = tag('div',{class='footnote',id=fn.id}, {
   740    824   			tag('div',{class='number'}, tostring(fn.num)),
   741    825   			tag('div',{class='text'}, body.nodes),
   742         -			tag('a',{href='#0'},'close')
          826  +			tag('a',{href='#0'},'⤫')
   743    827   		})
   744    828   		table.insert(ir, note)
   745    829   	end
          830  +	if next(footnotes) then
          831  +		table.insert(ir, tagproc.toIR.tag('div',{id='cover'},''))
          832  +	end
   746    833   
   747    834   	-- restructure passes
   748    835   	runhook('ir_restructure_pre', ir)
   749    836   
   750    837   	---- list insertion pass
   751    838   	local lists = {}
   752    839   	for _, sec in pairs(ir) do
   753    840   		if sec.tag == 'section' then
   754    841   			local i = 1 while i <= #sec.nodes do local v = sec.nodes[i]
   755    842   				if v.tag == 'li' then
          843  +					addStyle 'list'
   756    844   					local ltag
   757    845   					if v.src.ordered
   758         -						then ltag = 'ol'
   759         -						else ltag = 'ul'
          846  +						then ltag = 'ol' addStyle 'list_ordered'
          847  +						else ltag = 'ul' addStyle 'list_unordered'
   760    848   					end
   761    849   					local last = i>1 and sec.nodes[i-1]
   762    850   					if last and last.embed == 'list' and not (
   763    851   						last.ref[#last.ref].src.depth   == v.src.depth and
   764    852   						last.ref[#last.ref].src.ordered ~= v.src.ordered
   765    853   					) then
   766    854   						-- add to existing list
................................................................................
   900    988   	if opts.width then
   901    989   		table.insert(styles, string.format([[body {padding:0 1em;margin:auto;max-width:%s}]], opts.width))
   902    990   	end
   903    991   	if opts.accent then
   904    992   		table.insert(styles, string.format(':root {--accent:%s}', opts.accent))
   905    993   	end
   906    994   	if opts.accent or (not opts['dark-on-light']) and (not opts['fossil-uv']) then
   907         -		stylesNeeded.accent = true
          995  +		addStyle 'accent'
   908    996   	end
   909    997   
   910    998   
   911         -	for k in pairs(stylesNeeded) do
          999  +	for _,k in pairs(stylesNeeded.order) do
   912   1000   		if not stylesets[k] then ct.exns.unimpl('styleset %s not implemented (!)',  k):throw() end
   913   1001   		table.insert(styles, prepcss(stylesets[k]))
   914   1002   	end
   915   1003   
   916   1004   	local head = {}
   917   1005   	local styletag = ''
   918   1006   	if opts['link-css'] then