123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647openCmarkittypefile_reader=Fpath.t->(stringoption,[`Msgofstring])result(** The compilation from "pure" markdown cmarkit values to compiled slipshow
values (as extended cmarkit values) is done in several stages. The reason is
that otherwise, the order in which we do thing on a specific node is quite
tricky to get right... Also, I remember CMarkit mappers were limiting in
what they allow to do: You cannot recurse on the children and then
post-process the result
([let to_post_process = Tast_iterator.default_iterator.node my_iterator node
in ...])
The first stage is doing the following:
- Block quotes are turned into Divs,
- [slip-script] code blocks are turned into slip scripts,
- [includes] are included, with the first stage runned on them,
- Images src are relativized,
- Images are turned into audio/video depending on the attributes/extension
- Attributes are suffixed with [-at-unpause]
- BlockS are grouped on divs by [---]
The second stage is doing the following:
- [children:...] attributes are passed to children
The third stage is doing the following:
- [blockquote] attributed elements are turned into block quotes
- [slip] attributed elements are turned into slips
- [slide] attributed elements are turned into slides
- [carousel] attributed elements are turned into carousels
The fourth stage is populating the media files map *)modulePath_entering:sig(** Path are relative to the file we are reading. When we include a file we
need to interpret the path as relative to it.
Since we only have access to fold and maps, but not to "lift maps", we use
some state. *)typetvalmake:unit->tvalin_path:t->Fpath.t->(unit->'a)->'avalrelativize:t->Fpath.t->Fpath.tend=structtypet=Fpath.tStack.tletmake=Stack.createletin_pathpath_stackpf=Stack.pushppath_stack;letres=f()inignore@@Stack.poppath_stack;resletrelativizepath_stackp=letrecdo_lacc=matchlwith[]->acc|p::q->do_q(Fpath.(//)pacc)indo_(path_stack|>Stack.to_seq|>List.of_seq)p|>Fpath.normalizeendmoduleId:sigvalgen:unit->stringend=structleti=ref0letgen()=letres="__slipshow__id__"^string_of_int!iinincri;resendletclassify_imagep=matchFpath.get_extpwith|".3gp"|".mpg"|".mpeg"|".mp4"|".m4v"|".m4p"|".ogv"|".ogg"|".mov"|".webm"->`Video|".aac"|".flac"|".mp3"|".oga"|".wav"->`Audio|".pdf"->`Pdf|".apng"|".avif"|".gif"|".jpeg"|".jpg"|".jpe"|".jig"|".jfif"|".png"|".svg"|".webp"->(* https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types#image_types *)`Image|".draw"->`Draw|_->`Imageletresolve_filepss=matchAsset.Uri.of_stringswith|Links->Asset.Uri.Links|Pathp->Path(Path_entering.relativizepsp)moduleStage1=structletturn_block_quotes_into_divsm((bq,(attrs,meta2)),meta)=letb=Block.Block_quote.blockbqinletb=matchMapper.map_blockmbwithNone->Block.empty|Someb->binletattrs=Mapper.map_attrsmattrsinMapper.ret(Ast.Div((b,(attrs,meta2)),meta))lethandle_slip_scripts_creationm((cb,(attrs,meta)),meta2)=matchBlock.Code_block.info_stringcbwith|None->Mapper.default|Some(info,_)->(matchBlock.Code_block.language_of_info_stringinfowith|Some("slip-script",_)->Mapper.ret(Ast.SlipScript((cb,(Mapper.map_attrsmattrs,meta)),meta2))|_->Mapper.default)lethandle_includesread_filecurrent_pathm(attrs,meta)=match(Attributes.find"include"attrs,Attributes.find"src"attrs)with|Some(_,None),Some(_,Some({v=src;_},_))->(letrelativized_path=Path_entering.relativizecurrent_path(Fpath.vsrc)inmatchread_filerelativized_pathwith|Error(`Msgerr)->Logs.warn(funm->m"Could not read %a: %s"Fpath.pprelativized_patherr);Mapper.default|OkNone->Mapper.default|Ok(Somecontents)->(letmd=Cmarkit.Doc.of_string~heading_auto_ids:true~strict:falsecontentsinPath_entering.in_pathcurrent_path(Fpath.parent(Fpath.vsrc))@@fun()->matchMapper.map_blockm(Doc.blockmd)with|None->Mapper.default|Somemapped_blocks->letattrs=Mapper.map_attrsmattrsinMapper.ret(Ast.Included((mapped_blocks,(attrs,meta)),Meta.none))))|_->Mapper.defaultletget_link_definition(defs:Cmarkit.Label.defs)l=matchInline.Link.reference_definitiondefslwith|Some(Cmarkit.Link_definition.Defld)->Someld|_->Noneletclassify_link_definition(ld:Cmarkit.Link_definition.t)attrs=lethas_attrsx=Cmarkit.Attributes.findxattrs|>Option.is_someinifhas_attrs"video"then`Videoelseifhas_attrs"audio"then`Audioelseifhas_attrs"image"then`Imageelseifhas_attrs"pdf"then`Pdfelseletd,_meta=Cmarkit.Link_definition.destldinmatchFpath.of_stringdwith|Error_->`Image|Okp->classify_imagepletupdate_link_definitioncurrent_pathld=letlabel,layout,defined_label,(dest,meta_dest),title=Link_definition.(labelld,layoutld,defined_labelld,destld,titleld)inleturi=resolve_filecurrent_pathdestinletdest=(Asset.Uri.to_stringuri,meta_dest)in(uri,Link_definition.make~layout~defined_label?label~dest?title())lethandle_image_inliningmdefscurrent_path((l,(attrs,meta2)),meta)=lettext=Inline.Link.textlinlet(let*)xf=matchxwithNone->Mapper.default|Somex->fxinlet*kind,ld,uri=matchget_link_definitiondefslwith|None->None|Some((ld,(attrs_ld,meta2)),meta)->letattrs=Cmarkit.Attributes.merge~base:attrs~new_attrs:attrs_ldinletkind=classify_link_definitionldattrsinletattrs_ld=Mapper.map_attrsmattrs_ldinletdest,ld=update_link_definitioncurrent_pathldinSome(kind,((ld,(attrs_ld,meta2)),meta),dest)inletreference=`Inlineldinletl=Inline.Link.maketextreferenceinletattrs=Mapper.map_attrsmattrsinletorigin=((l,(attrs,meta2)),meta)inmatchkindwith|`Image->Mapper.ret@@Ast.Image{Ast.uri;origin;id=Id.gen()}|`Video->Mapper.ret@@Ast.Video{Ast.uri;origin;id=Id.gen()}|`Audio->Mapper.ret@@Ast.Audio{Ast.uri;origin;id=Id.gen()}|`Draw->Mapper.ret@@Ast.Hand_drawn{Ast.uri;origin;id=Id.gen()}|`Pdf->Mapper.ret@@Ast.Pdf{Ast.uri;origin;id=Id.gen()}lethandle_dash_separated_blocksm(blocks,meta)=letdiv((attrs,am),blocks)=letattrs=Mapper.map_attrsmattrsinletblocks=matchblockswith|[b]->Mapper.map_blockmb|blocks->Mapper.map_blockm@@Block.Blocks(blocks,Meta.none)inmatchblockswith|None->None|Someblocks->Some(Ast.Div((blocks,(attrs,am)),Meta.none))inletfind_biggestblocks=letfind_biggestbiggestblock=letmaxx=functionNone->Somex|Somey->Some(Int.maxxy)inmatchblockwith|Block.Thematic_break((tb,_),_)whenString.for_all(func->c='-')(Block.Thematic_break.layouttb)->max(String.length(Block.Thematic_break.layouttb))biggest|_->biggestinList.fold_leftfind_biggestNoneblocksinletreccollect_until_dash?(first=false)~separator(acc_attrs,acc1)global_accblocks=letadd_to_global_acc(acc_attrs,acc1)global_acc=ifglobal_acc=[]&&acc1=[]&&firstthen[](* We do not add empty first element to give a chance to add attributes to the (new) first element *)else(acc_attrs,List.revacc1)::global_accinmatchblockswith|Block.Thematic_break((tb,tb_attrs),_tb_meta)::restwhenString.equalseparator(Block.Thematic_break.layouttb)->letglobal_acc=add_to_global_acc(acc_attrs,acc1)global_accincollect_until_dash~separator(tb_attrs,[])global_accrest|e::rest->collect_until_dash~separator(acc_attrs,e::acc1)global_accrest|[]->List.rev((acc_attrs,List.revacc1)::global_acc)inmatchfind_biggestblockswith|None->Mapper.default|Somen->letseparator=String.maken'-'inletres=collect_until_dash~first:true~separator((Attributes.empty,Meta.none),[])[]blocksinletres=List.filter_mapdivresinMapper.ret@@Block.Blocks(res,meta)letexecutedefsread_file=letcurrent_path=Path_entering.make()inletblockm=function|Block.Blocksbs->handle_dash_separated_blocksmbs|Block.Block_quotebq->turn_block_quotes_into_divsmbq|Block.Code_blockcb->handle_slip_scripts_creationmcb|Block.Ext_standalone_attributessa->handle_includesread_filecurrent_pathmsa|_->Mapper.defaultinletinlinei=function|Inline.Imageimg->handle_image_inliningidefscurrent_pathimg|_->Mapper.defaultinletattrs=function|`Kv(("up",m),v)->Some(`Kv(("up-at-unpause",m),v))|`Kv(("center",m),v)->Some(`Kv(("center-at-unpause",m),v))|`Kv(("down",m),v)->Some(`Kv(("down-at-unpause",m),v))|`Kv(("exec",m),v)->Some(`Kv(("exec-at-unpause",m),v))|`Kv(("scroll",m),v)->Some(`Kv(("scroll-at-unpause",m),v))|`Kv(("enter",m),v)->Some(`Kv(("enter-at-unpause",m),v))|`Kv(("emph",m),v)->Some(`Kv(("emph-at-unpause",m),v))|`Kv(("focus",m),v)->Some(`Kv(("focus-at-unpause",m),v))|`Kv(("reveal",m),v)->Some(`Kv(("reveal-at-unpause",m),v))|`Kv(("static",m),v)->Some(`Kv(("static-at-unpause",m),v))|`Kv(("unemph",m),v)->Some(`Kv(("unemph-at-unpause",m),v))|`Kv(("unfocus",m),v)->Some(`Kv(("unfocus-at-unpause",m),v))|`Kv(("unreveal",m),v)->Some(`Kv(("unreveal-at-unpause",m),v))|`Kv(("unstatic",m),v)->Some(`Kv(("unstatic-at-unpause",m),v))(* TODO: Improve this (eg by moving it to another phase) *)|`Kv(("children:up",m),v)->Some(`Kv(("children:up-at-unpause",m),v))|`Kv(("children:center",m),v)->Some(`Kv(("children:center-at-unpause",m),v))|`Kv(("children:down",m),v)->Some(`Kv(("children:down-at-unpause",m),v))|`Kv(("children:exec",m),v)->Some(`Kv(("children:exec-at-unpause",m),v))|`Kv(("children:scroll",m),v)->Some(`Kv(("children:scroll-at-unpause",m),v))|`Kv(("children:enter",m),v)->Some(`Kv(("children:enter-at-unpause",m),v))|`Kv(("children:emph",m),v)->Some(`Kv(("children:emph-at-unpause",m),v))|`Kv(("children:focus",m),v)->Some(`Kv(("children:focus-at-unpause",m),v))|`Kv(("children:reveal",m),v)->Some(`Kv(("children:reveal-at-unpause",m),v))|`Kv(("children:static",m),v)->Some(`Kv(("children:static-at-unpause",m),v))|`Kv(("children:unemph",m),v)->Some(`Kv(("children:unemph-at-unpause",m),v))|`Kv(("children:unfocus",m),v)->Some(`Kv(("children:unfocus-at-unpause",m),v))|`Kv(("children:unreveal",m),v)->Some(`Kv(("children:unreveal-at-unpause",m),v))|`Kv(("children:unstatic",m),v)->Some(`Kv(("children:unstatic-at-unpause",m),v))|x->SomexinAst.Mapper.make~block~inline~attrs()letexecutedefsread_filemd=Cmarkit.Mapper.map_doc(executedefsread_file)mdendmoduleStage2=struct(** Get the attributes of a cmarkit node, returns them and the element
stripped of its attributes *)letget_attribute=letno_attrs=(Attributes.empty,Meta.none)infunction(* Standard Cmarkit nodes *)|Block.Blank_line_->None|Block.Block_quote((bq,attrs),meta)->Some(Block.Block_quote((bq,no_attrs),meta),attrs)|Block.Blocks_->None|Block.Code_block((cb,attrs),meta)->Some(Block.Code_block((cb,no_attrs),meta),attrs)|Block.Heading((h,attrs),meta)->Some(Block.Heading((h,no_attrs),meta),attrs)|Block.Html_block((hb,attrs),meta)->Some(Block.Html_block((hb,no_attrs),meta),attrs)|Block.Link_reference_definition_->None|Block.List((l,attrs),meta)->Some(Block.List((l,no_attrs),meta),attrs)|Block.Paragraph((p,attrs),meta)->Some(Block.Paragraph((p,no_attrs),meta),attrs)|Block.Thematic_break((tb,attrs),meta)->Some(Block.Thematic_break((tb,no_attrs),meta),attrs)(* Extension Cmarkit nodes *)|Block.Ext_math_block((mb,attrs),meta)->Some(Block.Ext_math_block((mb,no_attrs),meta),attrs)|Block.Ext_table((table,attrs),meta)->Some(Block.Ext_table((table,no_attrs),meta),attrs)|Block.Ext_footnote_definition_->None|Block.Ext_standalone_attributes_->None|Block.Ext_attribute_definition_->None(* Slipshow nodes *)|Ast.Included((inc,attrs),meta)->Some(Ast.Included((inc,no_attrs),meta),attrs)|Ast.Div((div,attrs),meta)->Some(Ast.Div((div,no_attrs),meta),attrs)|Ast.Slide((slide,attrs),meta)->Logs.err(funm->m"Slides should not appear here, this is an error on slipshow's \
side. Please report!");Some(Ast.Slide((slide,no_attrs),meta),attrs)|Ast.Slip((slip,attrs),meta)->Logs.err(funm->m"Slips should not appear here, this is an error on slipshow's \
side. Please report!");Some(Ast.Slip((slip,no_attrs),meta),attrs)|Ast.SlipScript((slscr,attrs),meta)->Some(Ast.SlipScript((slscr,no_attrs),meta),attrs)|Ast.Carousel((c,attrs),meta)->Some(Ast.Carousel((c,no_attrs),meta),attrs)|_->None(** Get the attributes of a cmarkit node, returns them and the element
stripped of its attributes *)letmerge_attributenew_attrsb=letmergebase=Attributes.merge~base~new_attrs(* Old attributes take precendence over "new" one *)inmatchbwith(* Standard Cmarkit nodes *)|Block.Blank_line_|Block.Blocks_->b|Block.Block_quote((bq,(attrs,meta_a)),meta)->Block.Block_quote((bq,(mergeattrs,meta_a)),meta)|Block.Code_block((cb,(attrs,meta_a)),meta)->Block.Code_block((cb,(mergeattrs,meta_a)),meta)|Block.Heading((h,(attrs,meta_a)),meta)->Block.Heading((h,(mergeattrs,meta_a)),meta)|Block.Html_block((hb,(attrs,meta_a)),meta)->Block.Html_block((hb,(mergeattrs,meta_a)),meta)|Block.Link_reference_definition_->b|Block.List((l,(attrs,meta_a)),meta)->Block.List((l,(mergeattrs,meta_a)),meta)|Block.Paragraph((p,(attrs,meta_a)),meta)->Block.Paragraph((p,(mergeattrs,meta_a)),meta)|Block.Thematic_break((tb,(attrs,meta_a)),meta)->Block.Thematic_break((tb,(mergeattrs,meta_a)),meta)(* Extension Cmarkit nodes *)|Block.Ext_math_block((mb,(attrs,meta_a)),meta)->Block.Ext_math_block((mb,(mergeattrs,meta_a)),meta)|Block.Ext_table((table,(attrs,meta_a)),meta)->Block.Ext_table((table,(mergeattrs,meta_a)),meta)|Block.Ext_footnote_definition_->b|Block.Ext_standalone_attributes_->b|Block.Ext_attribute_definition_->b(* Slipshow nodes *)|Ast.Included((inc,(attrs,meta_a)),meta)->Ast.Included((inc,(mergeattrs,meta_a)),meta)|Ast.Div((div,(attrs,meta_a)),meta)->Ast.Div((div,(mergeattrs,meta_a)),meta)|Ast.Slide((slide,(attrs,meta_a)),meta)->Logs.err(funm->m"Slides should not appear here, this is an error on slipshow's \
side. Please report!");Ast.Slide((slide,(mergeattrs,meta_a)),meta)|Ast.Slip((slip,(attrs,meta_a)),meta)->Logs.err(funm->m"Slips should not appear here, this is an error on slipshow's \
side. Please report!");Ast.Slip((slip,(mergeattrs,meta_a)),meta)|Ast.SlipScript((slscr,(attrs,meta_a)),meta)->Ast.SlipScript((slscr,(mergeattrs,meta_a)),meta)|Ast.Carousel((c,(attrs,meta_a)),meta)->Ast.Carousel((c,(mergeattrs,meta_a)),meta)|_->bletexecute=letblockmc=matchcwith|Ast.Div((Block.Blocks(bs,m_bs),(attrs,m_attrs)),m_div)->letkvs=Attributes.kv_attributesattrsinletrem_prefix~prefixs=ifString.starts_with~prefixsthenSome(String.subs(String.lengthprefix)(String.lengths-String.lengthprefix))elseNoneinletcategorizekey=matchrem_prefix~prefix:"."keywith|Somec->`Classc|None->`Kvkeyinletnew_attrs=List.fold_left(funacc((key,meta),value)->matchrem_prefix~prefix:"children:"keywith|None->acc|Somekey->(match(categorizekey,value)with|`Classc,None->Attributes.add_classacc(c,meta)|`Kvc,_->Attributes.add(c,meta)valueacc|`Classc,Some_->Logs.warn(funm->m"Children classes cannot have a value");Attributes.add(c,meta)valueacc))Attributes.emptykvsinletbs=List.map(merge_attributenew_attrs)bsinletbs=matchMapper.map_blockm(Block.Blocks(bs,m_bs))with|None->Block.Blocks([],m_bs)|Somel->linMapper.ret(Ast.Div((bs,(attrs,m_attrs)),m_div))|_->Mapper.defaultinAst.Mapper.make~block()letexecutemd=Cmarkit.Mapper.map_docexecutemdendmoduleStage3=structletrecextract_titleblock=matchblockwith|Ast.Div((h,attrs),meta)->letblock,title=extract_titlehin(Ast.Div((block,attrs),meta),title)|Block.Heading((h,attrs),_)whenBlock.Heading.levelh=1->(Block.Blocks([],Meta.none),Some(Block.Heading.inlineh,attrs))|Block.Blocks(Block.Heading((h,attrs),_)::blocks,meta)whenBlock.Heading.levelh=1->(Block.Blocks(blocks,meta),Some(Block.Heading.inlineh,attrs))|Block.Blocks((Block.Blank_line_asbl)::blocks,meta)->letblock,title=extract_title(Block.Blocks(blocks,meta))inletblocks=matchblockwith|Block.Blocks(bs,_)->bl::bs|_->bl::[block]in(Block.Blocks(blocks,meta),title)|_->(block,None)letexecute=letblockmc=letmap~may_enterblock(attrs,meta2)=letb=matchMapper.map_blockmblockwith|None->Block.empty|Someb->binletattrs=if(Attributes.mem"no-enter"attrs||Attributes.mem"enter-at-unpause"attrs)||notmay_enterthenattrselseAttributes.add("enter-at-unpause",Meta.none)Noneattrsinletattrs=Mapper.map_attrsmattrsin(b,(attrs,meta2))inmatchStage2.get_attributecwith|None->Mapper.default|Some(block,(attrs,meta2))whenAttributes.mem"blockquote"attrs->letblock,attrs=map~may_enter:falseblock(attrs,meta2)inletblock=Block.Block_quote.makeblockinMapper.ret@@Block.Block_quote((block,attrs),Meta.none)|Some(block,(attrs,meta2))whenAttributes.mem"slide"attrs->letblock,attrs=map~may_enter:trueblock(attrs,meta2)inletblock,title=extract_titleblockinMapper.ret@@Ast.Slide(({content=block;title},attrs),Meta.none)|Some(block,(attrs,meta2))whenAttributes.mem"slip"attrs->letblock,(attrs,meta)=map~may_enter:trueblock(attrs,meta2)inMapper.ret@@Ast.Slip((block,(attrs,meta)),Meta.none)|Some(block,(attrs,meta2))whenAttributes.mem"carousel"attrs->letblock,attrs=map~may_enter:falseblock(attrs,meta2)inletchildren=matchblockwith|Ast.Div((Block.Blocks(l,_),_),_)->l|_->[block]inletchildren=List.filter_map(functionBlock.Blank_line_->None|x->Somex)childreninMapper.ret@@Ast.Carousel((children,attrs),Meta.none)|Some_->Mapper.defaultinAst.Mapper.make~block()letexecutemd=Cmarkit.Mapper.map_docexecutemdendmoduleStage4=structletfpath_map_add_to_listxdatam=letopenFpath.Mapinletadd=functionNone->Some[data]|Somel->Some(data::l)inupdatexaddmletexecute=letblock_f_acc_c=Folder.defaultinletinline_facc=function|Ast.Video{uri=Pathp;id;_}|Ast.Pdf{uri=Pathp;id;_}|Ast.Audio{uri=Pathp;id;_}|Ast.Hand_drawn{uri=Pathp;id;_}|Ast.Image{uri=Pathp;id;_}->Folder.ret@@fpath_map_add_to_listpidacc|_->Folder.defaultinAst.Folder.make~block~inline()letexecute~read_filemd=letasset_map=Cmarkit.Folder.fold_docexecuteFpath.Map.emptymdinletfiles=Fpath.Map.filter_map(funpathused_by->letread_file:file_reader=read_fileinletmode=`Base64inmatchread_filepathwith|Ok(Somecontent)->Some{Ast.Files.content;mode;used_by;path}|OkNone->None|Error(`Msgs)->Logs.warn(funm->m"Could not read file: %a. Considering it as an URL. (%s)"Fpath.pppaths);None)asset_mapin{Ast.doc=md;files}endletof_cmarkit~read_filemd=letdefs=Cmarkit.Doc.defsmdinletmd1=Stage1.executedefsread_filemdinletmd2=Stage2.executemd1inletmd3=Stage3.executemd2inStage4.execute~read_filemd3letcompile~attrs?(read_file=fun_->OkNone)s=letopenCmarkitinletmd=letdoc=Doc.of_string~heading_auto_ids:true~strict:falsesinletbq=Block.Block_quote.make(Doc.blockdoc)inletblock=Block.Block_quote((bq,(attrs,Meta.none)),Meta.none)inDoc.makeblockinof_cmarkit~read_filemdletto_cmarkit=let(let*)xf=Option.bindxfinlet(let+)xf=Option.mapfxinletblockm=function|Ast.Slide(({content;title},_),meta)->lettitle=let*title,attrs=titleinlet+title=Mapper.map_inlinemtitleinBlock.Heading((Block.Heading.make~level:1title,attrs),Meta.none)inlettitle=Option.to_listtitleinletb=matchMapper.map_blockmcontentwith|None->Block.empty|Someb->binMapper.ret(Block.Blocks(title@[b],meta))|Ast.Div((bq,_),meta)|Ast.Slip((bq,_),meta)|Ast.Included((bq,_),meta)->letb=matchMapper.map_blockmbqwithNone->Block.empty|Someb->binMapper.ret(Block.Blocks([b],meta))|Ast.SlipScript_->Mapper.delete|Ast.Carousel((l,_),meta)->`Map(Mapper.map_blockm(Block.Blocks(l,meta)))|_->Mapper.defaultinletinlinem=function|Ast.Video{origin;_}|Ast.Audio{origin;_}|Ast.Pdf{origin;_}|Ast.Hand_drawn{origin;_}|Ast.Image{origin;_}->`Map(Mapper.map_inlinem(Inline.Imageorigin))|_->Mapper.defaultinletattrs=function|`Kv(("up-at-unpause",m),v)->Some(`Kv(("up",m),v))|`Kv(("center-at-unpause",m),v)->Some(`Kv(("center",m),v))|`Kv(("enter-at-unpause",m),v)->Some(`Kv(("enter",m),v))|`Kv(("down-at-unpause",m),v)->Some(`Kv(("down",m),v))|`Kv(("exec-at-unpause",m),v)->Some(`Kv(("exec",m),v))|`Kv(("scroll-at-unpause",m),v)->Some(`Kv(("scroll",m),v))|x->SomexinAst.Mapper.make~block~inline~attrs()letto_cmarkit{Ast.doc=sd;_}=Cmarkit.Mapper.map_docto_cmarkitsd