././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1629188395.0680406 sphinx_inline_tabs-2023.4.21/.flake80000644000000000000000000000007014106670453014011 0ustar00[flake8] max-line-length = 88 extend-ignore = E203,E501 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1629188395.0681183 sphinx_inline_tabs-2023.4.21/.gitignore0000644000000000000000000000005514106670453014631 0ustar00# Python stuff .nox /build /dist __pycache__ ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1629188395.0681958 sphinx_inline_tabs-2023.4.21/.isort.cfg0000644000000000000000000000003114106670453014532 0ustar00[settings] profile=black ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1680043734.1631093 sphinx_inline_tabs-2023.4.21/.pre-commit-config.yaml0000644000000000000000000000173214410667326017130 0ustar00repos: - repo: https://github.com/psf/black rev: 23.1.0 hooks: - id: black - repo: https://github.com/PyCQA/isort rev: 5.12.0 hooks: - id: isort - repo: https://github.com/PyCQA/flake8 rev: 6.0.0 hooks: - id: flake8 - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.4.0 hooks: - id: check-builtin-literals - id: check-added-large-files - id: check-case-conflict - id: check-toml - id: check-yaml - id: debug-statements - id: end-of-file-fixer - id: forbid-new-submodules - id: trailing-whitespace - repo: https://github.com/PyCQA/pydocstyle.git rev: 6.3.0 hooks: - id: pydocstyle exclude: noxfile\.py|docs/conf\.py - repo: https://github.com/asottile/blacken-docs rev: 1.13.0 hooks: - id: blacken-docs - repo: https://github.com/pre-commit/mirrors-prettier rev: v3.0.0-alpha.6 hooks: - id: prettier ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1629188395.068378 sphinx_inline_tabs-2023.4.21/.readthedocs.yml0000644000000000000000000000027414106670453015732 0ustar00version: 2 build: image: latest formats: [pdf] sphinx: configuration: docs/conf.py python: version: 3.8 install: - method: pip path: . extra_requirements: [doc] ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1629188395.0684874 sphinx_inline_tabs-2023.4.21/CODE_OF_CONDUCT.md0000644000000000000000000001213414106670453015441 0ustar00# Contributor Covenant Code of Conduct ## Our Pledge We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. ## Our Standards Examples of behavior that contributes to a positive environment for our community include: - Demonstrating empathy and kindness toward other people - Being respectful of differing opinions, viewpoints, and experiences - Giving and gracefully accepting constructive feedback - Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience - Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: - The use of sexualized language or imagery, and sexual attention or advances of any kind - Trolling, insulting or derogatory comments, and personal or political attacks - Public or private harassment - Publishing others' private information, such as a physical or email address, without their explicit permission - Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. ## Scope This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at conduct@pradyunsg.me. All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. ## Enforcement Guidelines Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: ### 1. Correction **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. **Consequence**: A written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. ### 2. Warning **Community Impact**: A violation through a single incident or series of actions. **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. ### 3. Temporary Ban **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within the community. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations. ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1629188395.0685852 sphinx_inline_tabs-2023.4.21/LICENSE0000644000000000000000000000206614106670453013652 0ustar00Copyright (c) 2020 Pradyun Gedam Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1640176473.3414385 sphinx_inline_tabs-2023.4.21/README.md0000644000000000000000000000230114160615531014110 0ustar00# Sphinx Inline Tabs ![demo image](https://raw.githubusercontent.com/pradyunsg/sphinx-inline-tabs/main/docs/_static/demo.png) Add inline tabbed content to your Sphinx documentation. ## Installation This project is available on PyPI, and can be installed using pip: ``` pip install sphinx-inline-tabs ``` You'll also want to add the extension to `extensions` in `conf.py`: ```python extensions = [ ..., "sphinx_inline_tabs", ..., ] ``` ## Features - **Elegant design**: Small footprint in the markup and generated website, while looking good. - **Configurable**: All the colors can be configured using CSS variables. - **Synchronisation**: Tabs with the same label all switch with a single click. - **Works without JavaScript**: JavaScript is not required for the basics, only for synchronisation. ## Contributing sphinx-inline-tabs is a volunteer maintained open source project, and we welcome contributions of all forms. The [Code of Conduct](CODE_OF_CONDUCT.md) applies within all community spaces. If you are not familiar with our Code of Conduct policy, take a minute to read the policy before starting with your first contribution. ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1629188395.069045 sphinx_inline_tabs-2023.4.21/docs/_static/demo.png0000644000000000000000000007761214106670453016666 0ustar00PNG  IHDRArBiCCPICC Profile(c``H,(aa``+) rwRR` ` \\TQk .Ȭ 3V ~{GXIL(+%8H䂢 [nEg; >V d_3S'@Nx:j/pp*$D;Teg(8C(U3/YOGȀ՟(v !zSbYr {>10V 30w20/H,J;KqSϲ {000-4^AVeXIfMM*iDAASCIIScreenshotciTXtXML:com.adobe.xmp 833 Screenshot 284 ,@IDATxUoHO! -BJj*MA *`^@" E)EzI$$!>=f'ognv3wnzn9-1 h˴I:LpRTD@D@D@D@D@ڊn%VD@D@D@D@D@Jʀ@[V٭ĊH Rh+R*X)A*" " " " " mE@JP[e+" " " " " %He@D@D@D@D@DH jVbE@D@D@D@D@ v칵8o {Մ-(" " " " "̹4#" " " " " L@JP3&" " " " "pRT43)A͜;@ H j8Ry(" " " " "5s(n" " " " "  ' %H@3̹4#" " " " " L@JP3&" " " " "pRT43)A͜;@ H j8Ry(" " " " "5s(n" " " " "  ' %H@3̹4#" " " " " L@JP3&" " " " "pM5}~)ۃܹsw\KD@D@D@D@Dn.{O8;g W6;|pauz/tYxf?;9rX8;t=|ٱkavyF뮿m&G({I?|G-Y61cFog)" " " K.%<'i6Gp3?|7Aaѽ&Ӓ$I:ՊyҀ;|4>={vU.3f4&i1cfEJÓ" " " "ЌT 5' '.T ~jWߚNpfϚ:s0hᙗfioW_vS[[;>4)saaa=CWZ$~ӛg0aw֭[x饉AAÀJ /L3g d_~9w;v a9a5V .2uZ?rˆ#>}5zIZv~ðak2,f[/}ul{7 S&缞~|eֆ͛g_X}ՀY)&ٮ[9 'D~'5~a͑ +,>~4S[b sύ+q=?.̛;/ >$5jdXiܝ@W(p ?'Lzu^`DV^~pq_ޞ1߷up 3#sӫ[C}yN8{S E:GW+,Rpvn> /ܕw1|@zN=9̞3\ø/I= lfȦ{FE1rǞ W^uS>s1' W]}}X{Zay%hMyej8看3RPP"nҗ\%vDn wޒofC-<7jp1G;!ri2~o (S(G(Ӧ.j70:̻%/4_ԫWϰ~{W[TP6XׁWzID@D@D@EThynw9̶v'f^.F}©G-s_ 9t)LS^6ZW8[n6+Ûf}n]KjMZkGE-_یƍ{1̙3'DikwV?t  }<<;XJr~auԼ0e}w֯Y[7GDs]^-vaSF؂+ d)-d.6r+D3j?6<>~ g.O0'l^k[С_^cLJW^)l澪À:–'f>6dȂ<6lxĽE94}{[H' Qw!;Kk0qR!7G<#+b~L0Fnea5m3UF2.f!ХJ#z95Eh/.l?r̎$p>Y϶3Qkk $`a}3Jwf(B~N^ xb<+)e$z7vгzȐml4iܤ/4Ŋf*V>('Yeg²=ޛ`ex/~Y flnG.|-` g:߻0y6]|&*LQ=۾X2n@a ,<wޙ g3X5o2(U|i(^$ael 0xnn0b !VzI_WM:tǭEy׬Dk/8!U}\" " " "t%g 2sػ}fbp)҆e7&s٠.mv3B+a Fز_Q݈CM+>D[>+BXf%l-hnp%ocYmV8+7Z?qXim³ύ @x̌w7ɧ]Gi#l7}Oꫭ0.V Х+A|LO?:۬/XdcVYiiYXY”};•"nZ䘵Gkglu 3 #F5`xzYY~t_D@D@DMtC MTO٥VڪQ L]?Ҝ0rMu1IS+muN^W1>ۡ_cqj(>夈r=~tR^ *x9{OD@D@D@D+ 4ԕ :v+*AM@o[ * " " " " " C@JPb*" " " " "RQ^)AW@H jDy!" " " " ":N^)" " " " "  %@:y4@" " " " " C@JPb*" " " " "RQ^)AW@tKLGu6} mV%TD@D@D@D@@+._ST.9hu5zbJʀ@[V٭ĊH Rh+R*X)A*" " " " " mE@JP[e+" " " " " %He@D@D@D@D@DH jVbE@D@D@D@D@ )AmJ " %[2 " " " " "VUv+" " " " " RTD@D@D@D@D@ڊn%VD@D@D@D@D@Jʀ@[V٭Ċ,5JٳC$MF9sf?" " " " "u)AN/N:)\{7lHjba=%U(0J+4˖n +Bx[*ފ,:?)p  +r/DÕ^{[k`k k r/?5\3 :tţ;wnx뭷¼y)Z=!pwawg}vXݛg}6|+_ g}{tYfe-R{Kz㏇+2|6߈#%" " " " "Lä͙3'laUV ?°aYo|U / 'pB۷o5kV@qZ~]w߻uV]uxwV\qonخww߾jD>;pн{'|27.a^x!jwީrx?Ͱmc7tSx|8;찃sXF!>lMdՁ<_:r->?#.yXVq۝ y_k9={セ=Oq{0~m/ٯ_+,f>}|&Oa{ԩ/b9r|8+ 4(kر/wǗ&l vky>.(ٸL2% wWN)" " " "P kSN>\ve߳qf„ .Jl𘘢C%sN;̘1]ˆn|CJos=)) f!C?mYbM_W)=)Il:?cczǔ]|}j,+W_?|r@C}9$lAbuI_A9Í7ޘ}=!__qλ9[oubo떔~ZIϞ=/O9]89 鶭qb?oHBD@D@D@D,$(2 lU{'Nt7MjGb[Nx;[H17/̟??V;bx<+mQ.JmKl$җTe t$z^o +4ۦ把x1=S;? /ٹuY_ַ`$ ҹO(U{rU+O?tBv۹Ri+J$A6/bgѲVK"yGn=\"k+U-y_ys饗h'~+z}yD%(oYTSt!" " " "P@JP(2mK80=K^s<>d5apɌY+qӟQpᗬrO,q#?l33+"EpҤIV5_}*'PVYgWW>я)qU.='l`el%hnV~(rU+fMΕ ض;^D FdgWi%ȶQ|-~ mR[/˅裏z>)%T+K0 8wSDQďS^ӯ]eZwcv ^VxVyr: Σ#+oyE@D@D@D@j$P`e]SO6:yVD`0x ~`.[ =P;QQ Ql Su^Χ{pt/2`g- PaV[ixcoڴiue g>yn E^]4܍jaWRTP"p϶ֹK>"X8DVHoވB>/u-" " " "P@JParZ%%gH^  G5=gz3W]uU_R_0lU?nG %3߂ẄoBrib;`zC恕 U;+<(iTJ,fK`V e3U"ב[ yf "UB,PFPJrieu*hf%hg|? u3?]c,`;i1bёp6 E^4}3 7բ,+ů;z=su7b͊S_)5SBnhJ$ْɽDxfZ=c2kUIsްB>~Py]D@D@D@څ@7rSz)|نNx+ ~96x~cGnfʋ amJCEX\h,^1[;Y%)²U} >UV%lOvFq%C#Z" ERҒ;v'%!*%mַoȶ?*D@D@D@D@چJ(!Grh&YZ+5@3L@wsؚ8f8ġ@ KNE@D@D@D@D@Z@õ~v% %]s^6% %M3^v% %]s^6% %M3^v% %]s^6% %M3^v% %]s^6% %M3^v% %]s^6% %M3^v% %]s^6% %M3^v% %]s^6% %M3^v% %]s^6% %M3^v% %]s^6% %M3^v% %]s^6% %M3^v%4JЬY΃ٳg$I=?~+G_q@Vhl0Y׈0sFxӒ~J{] nǿR:{f%K/ ^xa׿<>ꫯ^5nk|ͪn+9=ztc=*9Yr-ӧO8쳛"s +BG㗍{;E;[n6ϧ~x?uc(WgQe;F/ɲ$êL'Cgۘc5Zwey9mڴ"LlaV wJw#^.F3cƌ/})92e+7_ f*ĻEe|s oVz+j,>uqDžw10[u5jT;vlݻwI=zp%h.R\sMӟ~WpQI'[n9[l|ꩧgouYHWyR:)TPN9%#Uӧ^zyޭցsz=7o{骄J:蠒|)Wgs=avζѵZq9YfhġmLѱ[ZGzw4j倉5\3 :(ֆ1_(ya]vR{S.2\_leR;s=ͥۯz`x|wy}[?bb1J3{nn.pWvo5'|g*8_U#SNt𹸥i%~{ݥuYO{@oT4Zq-n ,mҞ9]#-ʎÓcm_jnCĿ}MY׬u6H}bʔ)%<3!nP|&ZdE^;+"K%}?R<[D!=PoƏo#<{|AA뮻~5yn0anHq_7nqE_;}ҤI*ng4N;gGq;f$qD1ϪJrϗu֙2r@yʄ <,evVHB7_m>/Wxo/̾fٜu+>3&ΕVM"/h_{w]}J\Ҏt#np/9S$-u8#׼l >*{]ΖufGa4LŰiH< Q6몫3 +R*R'EV e\F[@S ⋞|Iy$n1A..B=}:h;}'{i maf%2{Z*1~6PI: Tz]/:Y.Lҝ[tTQ&|ާZڗ{\'>\"g%_Ib"'8qԡrRK}7rӏy1[4_Ϟ=/糣|?d;0}O~j6Ui.VaZsҿ*uҽ{Ķa Bg>dUWM[AI~{gsn]lɇ>=ܓY$C 7l*E~<o߾~g٫qwgK~;k4Ζԃlg{7k=ۖl)3_ɗeҘ1c!#駟XGw?ma?S]O,,첉,qN9&/(|?7|9nckݯ׭:i;C>~5qh(G7ܝ/<'}{I:;1Elad?m[r'zꫯz?|nx.JL| _X˂"䅅_1^W_aӦPgc $7Z&?ovKqqQG%믿~ɽju_uVgѾ>~W}_z5% zf8|egwPwe#v&6; m/g.FmT16YeUALՒo}[foRPtN@`d;rd.lbG#GDCIC;P3x+*MlC٣dM6Ilߋ aveKSlPlU.!MqŢqWrIz 8g Fa}$ƏEeVA>2z4Qq6K.@#!Wؐ?ā H, Bp$IauY 30B`dl<"qqҎQƳB~.1ʗͺe߭TgJP0:[״/L Fv6:/!&d8uUx?˹-V,CV2ojz} ?)Q&S[f6׿_>((& "E?+uE/m@>fW~_JD1q;|LYt' eD@̬fVPFN~\ecE!%YCpD?m-:?<^ $b³?ARi@U>ybfo[v(dg<92R4N%dU4pN=`4?:=ee2' ጲYPFxE: >OQ643xQR8uU׬2cU/# DV6{%a6GwK=/ªy3DxΖPɳeg֬#Ew mV1At$&JP0:[%c~E'+Q+AEVz;{]mrPmm90ȡQJY6$R t/Uk'y0ha~%*V#-,l \5&iQ\T ?&p16fh:S|2h]"lh[PD>O v[Ǩu}llkoIJHr[,1 &Pl>яb#WI Bʊmt> R>d^3 /7Q *~ (LT'k5ѦU~W7.jH__)ܮ_u8kkk5*%`9ΖRU&7Xо?`~m6xcߓw[ `67F h>~ےga/*-| re`3a~4g_)|m12g8;4?YS)kYg=.~[5NY+]l[Gl05gg3%^ bO~lߐ2I{}Ӕk~7L[0RؖKn>%q>GY.q6Ǧx??ì%OPO+ d(6 2M@뭷^0Dž5zG~d6gSˬd oҬg EZA_lRv=rJ0)S/ܐѴYL`%{˯~)[xRFѰ(^Vi"EFX+]mJ^Uɷ%tesy{oocl>SF]3E/PltO>}kV0#ΌVkNrVӾ+Xn4\S.=ghWic[Xk}, u`Io۾ڊ6L"GE"c@i[}CNt !s)H_*/$_wl5`%ΊԹc9#1Nq~֔p7ؑc?PFKs]j}}~UOnȯ> { c["hxmx=K3E -ξKdfcF7E>mV&JWT$Ażln!{??>+zm[?q-i@a?Xm(J+6k~*/&U& FFCd ˟:jTfEC E~g(m%\`~Oi3Vt`,b.Wwl vP3x BVmJYe {}-[&:+2U.n< B74:S~^4|:].?i/T(cD*M8E`>>.^Ơpqܶ%W 1Qa%8u>ՄmF h[Pl3Qmu/Ѧع V̻rQ}mhi=J= )Ww5+r[,1¦-f$ևjeY6ZEm(l{=U`+sk8 ܻW"}}3;] lĶrK:Da&A| 33C_}fkN LC\M~_"-fê`U#<26W͏sͶ,2sw/81+h[|.;CU YibbQ&Z3mv`A-wT?殱FWA AS:fv$ _GWز{:p䶞ZE9s΋t#wʙ '[U]_sgq "|Bv/ع-f"/t!;m&0c塳i1JNPF ;YQ#j/A~`& YG"?CjK4\=/g= s jH6x^ HY GFd@Nr_(70ȆxU!^ #*4f ؾX7gg( j ,Bv6W(| lG2;'>laBg@\FK|+B~{O9OVdYf ?gH[J,װ8hʽ^o;F€f *l<ؒFy lY}0kP5LF4SP~ J+A(8؊Ɍ)3͚2Yx3,şUfV`v@b\T:Hd3 ^+EZ)|cfL( f)ZT+ ϶lgܑȟ3)e2d\l.XdF(FLe3?}5+(:vW-ɯ47!.+((^ʬjv'wTxE./3RQ;VRi`z`5m|>,^岣>NȒA9q*[g:Ò_9S:aKYKxE9ᮞ8Uk˒ptJ 8)t>̎`5YE~hmF?9ӑИ"ѱ1 ; >?%K-qGcAYbN;d0e?Š-q=x7;ʺ_xR8/ԡ!<%~W%Z;| ~ޟ"ߋ_Ɖ&(7yČV)0H,.[I;H:ߥȧc̊%qwUetJ~ttz}Bf:W|gPDYf2UtŸ#/wGqD2_o\+O %CK$(?v9R4%YH%C>XcƾRW )gXy@q`pewayߢEPNYgC gAeOw+]0s*TGY!mcps$ JmeŐ l;'q&Jc mmm gϬKw.?3K_ "m! #9{c;f 7e#)1rѶq>P츬iګѿm?3; |lӶVkKk NDܙv_X;AI,qn)gyC6`1VAܝi5؉{UfK~'~˭OYg`) taRRDWD+T"IPrP~qZbd4ytPY 0ckI( ѣ{Q->}>YG_xpz#gY$k78|'y /|o3~{VgW9s +~Wu[A#0=E%L [mUѣGxGK{g8ߝpXc5›o^\{p)^kz%3%/!h[ow}8,@Ư7w\@X2cƌo5^体N:)L6-O rHɳL<9ᮻb{F?/\vea„ s ;sg?Νې௵Za5WL1^s5СCk_`eQ6!Ywi4ᒔ徖8|ӟG5jToEǹӧO$6w|/"nʽ^rIxaIKa@W^ye谙cp,2˸RU'|2|K_ p9>kϟ' ¸q&NojC#￿3xkFRL1"jC/N_;$h%N_|e {Xve+/J_h=h ˖&EI l}׿w1 6W/92lgϞeݾ'3oo}-2Oݯj~;pfO{CJ6lq3tYmJk[Nvەtw`KIG2~&a;ֿ e]Bo۳կz[n/0h ݃[;(+YaۥpC:6t㚮Ilxg/R 믿~XuUkc2l~9{̾{auI1֤{,za6 zJ_6lpᇧ('_L~f"u}7zQpQ>aԁZ[ƗM?q2eJbTD";A]CJ-*gOٮC:h3"ĉ|~҆U Q_uwBQ7iBRY]RN;^VKV/ !Vs=:M[_mvJ3o:GKGmn->o~] BdvHWF?E7 򊷥?iiǘ/̳(+oҤI'X$*=?3w7BZ7q s]w]$p}p -(?[Q|)9ʍpQζ #? C( lJ}2ޢ2&ɮˎeEwijQ>EiXYl@bbbHl9=dbUS^N8!5 /{6۞d뭷N_]{;=w7鍅'ksLbfGS8N>;HL8so߾u\}щ5| x۶ԍ b<]tQbJ]} _H< >r>/zӟ4_z׭SMlVb[<\۪kb gV6&UD|$cu0ɇ>!wN;%6Xu$ͿozX/ ~,s+rb\wrQG%Jb~|?/bN8p`b[rbMrgrzE2"0x+60Kl)~Zi <;+,}5y衇6OE_CS=QLZH?좘[ m ()Id E۰|19?ʼMK[m$_i_~V4%/ٗXFMx}ʺ'Ix|O|=\z/U[5IyCZm7uM',qZͭq~ǔ;"-?t9ԧYl5I+&vF<,뢼RpO9Jvmx?i+M|3^&X*}jw:[1Mneg?/>ض{ݠg~zbSWz/<Κ65XJLIX)QyYY|Z] 1l}@`6&fH`،Kb3>J%w|Y\xaMl'aB; ixE `@k|' l Aq  >w`HLwr46W.w擁 hKlׯhQN=!x/{m;%6 `;[i2@^%@lV)3&մ،rb3fi\IogWr ;9\,C9/']Cye*N;/2wswęk3xjWl&f;s:t&NPa/_~e2E:Hm +e?7J e<*y%w "[M)چ7?范Ц>컗 [pߋƻ\T(cw{sx` q,>0"`X|ls@6&~'iBP? t9#'"m.mKa\ߩOܖ_8^0f (RQ(+?((%jMI^0‘juzI2eNzE4*sThx?YS1Nw'whDy$̠Oڏ~k_{#*ZoA9SC&c܂h(7sv󜲎3)Rx%"LjVf`fK%頒xA%3|ϯQ0c.`w Tz%Nną!+̄0 1Cq 8J_J:K`%Jю;/ɀC:8[4( Y3G?m[KIcN:W@Q b_VOt zc  3+)۞+') 2heJ^Ŏ8_I :3dJ(ET^ bҁdW5rVIL0bzHj4#_SAc|  G[_Q+چev]nKq"ag|q52y'=,ZO)E~շxHDys( Y.uv eUrlgw>e5fE/dӡkh5 ͭNYh=am/HoOUV9b[j2}f5pb ﱵ6MQgيQ5{ώdopNam}klt>8؀?{8m̟G?+D!TgGدoc46qaO2g3 Gydmn[m~`xR euF;cm\ͭLȲZSR'hu~<&J|mBm˳9xe^aˈl%povk!=R y.Ԭq3ߘQ839$$񯒻WW0(B8+txf%v{^3'n7OcYN lj@3*A6k~_f6 ~ ,0`7G 'x l; ^0H`&-V\@=Fvޯ@Y;y:y^kA .lE"4o9^0^^yZ]~1xpWE@(CH6\璭W4ޕl诗G !`>g[|bAH?2re3nJt#E\w\_~7j&LQiW8r}& G;CDoQPh1`=3]lvYS0a(AW]uDUΒ4V'1ieֹ|@-muY)N|.s4^ )E ,:&d^ ay* Y1`mĚ KnyD`?̸Q-R> ~vVxdzQ!* VZ!*&̈x0P-L%3|F|BC'L3B ":Zc KY300cˊCrH2(I19u8ի)+ DcP̪mpGf8$z8wh~7e@~&v@\47e:VY wggE fY1&&X3.W{s/ Y-/;VG[ߩ,bv8;[o^BIFy/򱜰ҌCG)`@mēսFKzZoƄ˩"WvJ("¤^ 2"ږL[L7bb4+q &;蝽Xrf0SYۃ^[$f-`6B`v4+t*,Ӂ*̺tm/}f1ّ ΋+@tfxwg@z¶x/IaEyA=F?)gx-l[ e&$0kflEO S(ZH2/y饗8eo/`U*S%/–'c(e- v PO+AE*_K~^|zLpfmJ*mX'U/vƻeX$@oq;nKtS$݌冉 3q+Vn:6SOJ0"0qH~о!,¬h+:^0-RbuN"Dfa2Q23k4@ 6TQF3yw4 fpÀ RH%6ha(AeyT_DlTNh$DnDk|kFc4l N(tSӄb-tY%l+VʆPhH8>i 1>߹;吲gw0X*Ag@ { ^'lj%%$}..f-WϹ+AC!\kLJ E5z !;ޅXROW(/ y}`F=J9%fmFʀSY)چr!n=nk̇)o7#EK-ofR+ACb|br:¤!;BيP,e[N/(7U[u)o;kPZ)-Ԣ_s(3 mx%i<> &(lEZ%?fA֟"amNGD6l /o7$l_}9GE]*3 l /+qkPΰMtg^hk:j󊴹N>Ε(ap +ޏmj%nEy6Ezڙ]jsO6yn [}ZG,6eqI"h#*4A"K 50o60 m*i^ jd,ufUH[ NY,uTD`) ,a4kg)H " ,׭CC ̀KD] `zV$pA 'RQ%؎:%v^=|D^ls2@7⏍vqt@ BO@g(D@D@D@D@D@Z,EUD@D@D@D@D~Rg(D@D@D@D@D@Z,EUD@D@D@D@D~Rg(D@D@D@D@D@Z,EUD@D@D@D@D~Rg(D@D@D@D@D@Z,EUD@D@D@D@D~Rg(D@D@D@D@D@Z,EUD@D@D@D@D~Rg(D@D@D@D@D@Z,EUD@D@D@D@D~Rg(D@D@D@D@D@Z,EUD@D@D@D@D~Rg(D@D@D@D@D@Z,EUD@D@D@D@D~Rg(D@D@D@D@D@Z,EUD@D@D@D@D~Rg(D@D@D@D@D@Z,EUD@D@D@D@D~Rg(D@D@D@D@D@Z,EUD@D@D@D@D~Rg(D@D@D@D@D@Z,EUD@D@D@D@D~Rg(D@D@D@D@D@Z,EUD@D@D@D@D~Rg(D@D@D@D@D@Z,EUD@D@D@D@D~Rg(D@D@D@D@D@Z,EUD@D@D@D@D~Rg(D@D@D@D@D@Z,EUD@D@D@D@D~Rg(D@D@D@D@D@Z,EUD@D@D@D@D~Rg(D@D@D@D@D@Z@)AoMBf"0wpǝ(GGKS/WVəa+s-m9\f͚~v?rpavfi{cdva ]rMBMQ;~[nlcƌ J7iN8{o/XZ}`0չᆻ[.a{/M+%I0uګ`+v|i|8{\fxwiLz4c̊,W[m'E@D@D@D t4kN]3WW ^}k~8G 'hJGkA+vOh7 +,Lw懭T9BxY ú#z_н6>}z~?0L|yrgîlu^zib`?h0`R ̙3_ݎB3{NXc²{LƏ),ܲa䈡O>ixzzcR.v=>0l~2˄*BFx~ o>a[޽}ͷ”ɯ9fy)85V_5VɺoVvN %Ѣle cd~Xs ˧_}0VXB`szFJsϏ Ç ;3XA] }þ_p~ԓ=Å<bd=–[nkƋlg4Y,l{pU903'N|pׇG͟PJДWsι8L1#n )&}Y/UbGp-V[m;dÏ/xӭ\sCɡ"gv+W2"{#$OD@D@DNo/XVjKmg Vtr ;l7pЊa)L3g'o}.}+@BwZ68en8駯di1kqVI5z{]]b3E+ӵ콿ﷅ-lɆ"V&OVyN;nY+>(Ӧ.j70:̻%/4_ԫWϰ~{W[TP6XׁWzID@D@D@EThyֶ!m{[9f}|fxvœ;m7=|=Ȍ5ikƣz>7+iVߥ_IZ:eFl|TthܸÜ9sBTgJc?ﺃ!]wmO˓&)SZ: :8M!+!'t\ƒRmc+׏#44n훆Ѭ= 8~ lQ&=޷Kymzek|CxO=o1ejZ^[XlT CdҴY)ʚ/X! `g#Ƨķ˓gm Q+ g\]=;>Ja6U |M890!CVK>lF)}\dsf%hDōBwʗc!2aBnFZ7?VyF V~y90aG2 jl;gV6e\"" " " BTX±pVƽ<Ƿ. FPX Z.># p>!+BJ=; _xr oX B}،=2;~EkɊҧwk+rQZFӼ=(Cvn,dפI u&}/V4wTA9*;̎޹:\FguO i]:Ͳ}3rԀE7dM%io =m *L=/M?tˆ-\o[@Vx H;LwU {UV*>?{BYUٴM6 Fla7[O[9kiuCIxQ[pͪ M6‹Reg)" " " MMKWP9e3d0% O+CLuDtfcz7 `籙am: LEwG+<1|)6=}Ňra+]wQQbE !$qmc0m  ~sU6 :p˭wڌ N1GJ@7rڣ܌53`AX]<{߬?VO/" " "&١&>Ř~~u5z6zc1 mVUٌbf/bemS&nIQ ~YhL %Q} 6l˂`$w#bҲYkH"3& ,uxqO @@.IP.. @Ժ$gP  @% % @Z ? @$AZn]ÌqxW#@ @"59~$<;';}}%=z4hoo2'~裏<>}Xti޽{*  @(@M'A 1qhjlX_c%ۯ]6b>JX"R̆ /$h…jժBO @*$PIPKSK_^9|+WΓ駟Ɩ-[n++RիWǾ}V+ @ Pf&AۿW=wU兘8wչq?C 7gq$n-qe\|Af^jϳx=_{}L{rZ,sxOo/i[%{fu__~eر#fϞ]*~Μ93/_s̉tWԖb:6m* @,<t|͇ƇbVĸqqckKcWc餸uſ8^KwEϱ,f|xbηo @bT8w,kIJ, bkKхFkKkt-ڤDhois &_}~m۶-+Θ1PU?~||Bznʔ)uָkO8 @$P$09qg|0_x9BHyʹkgλx1:vDJJmwu֭Yȫ_z/+754S./66}~BJ҂'OX=E]jD @(p._=B1:툣ۏ/Uj^1~f۳?Ygi+ |ᄊ!@ @`* [{PUxXpY:i/8rHܹBUi!K.dX @$Az8w?絝W+Fs\8tt ۥ%csS'* @HԵ+[`E*/pQ=(h/n}1vߙ=1iҤ馛bݺu ;_|Ef^ڧNv>  @$A .XsFdz?,nO]Zz_&Dzin ~Z6; E̬h]a+<8:+?#Dwwwv*?S'ť{7+B~RT @W2G tl[uDӳ'o;眲lmoXxqu]ow7o,ȗ @\]'V5kdčf͊KF  @,P$(-0^tZNSӑsԩ#m @YÝ; @"pJߗ @t, @  @J@TWm @H| @+IP]M @  @ @$Au5K @$w @ȕ544 @ P#Qr%A-w @ԕHr[8~xlb  @ PR J Pcc:9s'A%{  @\ T= @q @1' sSj@ @q @1' sSj@ @q @1' sSj@ @q @1' sSj@ @q @1' sSj@ @q @1'?E16:CIENDB`././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1629188395.069147 sphinx_inline_tabs-2023.4.21/docs/conf.py0000644000000000000000000000312214106670453015066 0ustar00# Configuration file for the Sphinx documentation builder. # # Full list of options can be found in the Sphinx documentation: # https://www.sphinx-doc.org/en/master/usage/configuration.html # # -- sys.path preparation ---------------------------------------------------- # import sys from pathlib import Path sys.path.append(str(Path(__file__).parent / "demo")) # # -- Project information ----------------------------------------------------- # project = "Sphinx Inline Tabs" copyright = "2020, Pradyun Gedam" author = "Pradyun Gedam" # # -- General configuration --------------------------------------------------- # extensions = [ "sphinx.ext.autodoc", "sphinx.ext.extlinks", "sphinx.ext.intersphinx", "sphinx.ext.todo", "sphinx.ext.viewcode", "sphinx_inline_tabs", "myst_parser", ] templates_path = ["_templates"] # # -- Options for extlinks ---------------------------------------------------- # extlinks = { "pypi": ("https://pypi.org/project/%s/", ""), } # # -- Options for intersphinx ------------------------------------------------- # intersphinx_mapping = { "python": ("https://docs.python.org/3", None), "sphinx": ("https://www.sphinx-doc.org/", None), } # # -- Options for TODOs ------------------------------------------------------- # todo_include_todos = True # # -- Options for Markdown files ---------------------------------------------- # myst_admonition_enable = True myst_deflist_enable = True myst_heading_anchors = 3 # # -- Options for HTML output ------------------------------------------------- # html_theme = "furo" html_title = project ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1629188395.0692303 sphinx_inline_tabs-2023.4.21/docs/index.md0000644000000000000000000000166414106670453015231 0ustar00# Sphinx Inline Tabs ```{include} ../README.md :start-after: :end-before: ``` ## Demo ```{tab} One One is an interesting number. ``` ```{tab} Two Two is the even prime. ``` ```{tab} Three Three is an odd prime. ``` ```{tab} Four Four is not a perfect number. ``` This is text after the tabs, which seems to flow right through, which avoids breaking the flow of the document. And, if JavaScript is enabled, the next set of tabs will be synchronised. ````{tab} One ``` print(1) ``` ```` ````{tab} Two ``` print(2) ``` ```` ````{tab} Three ``` print(3) ``` ```` ````{tab} Four ``` print(4) ``` ```` To know more, take a look at the [Usage](usage) documentation of this project. ```{toctree} :hidden: usage ``` ```{toctree} :caption: Useful Links :hidden: PyPI page GitHub Repository ``` ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1640177078.5717716 sphinx_inline_tabs-2023.4.21/docs/usage.md0000644000000000000000000000720314160616667015230 0ustar00# Usage ## Basics Tabs are created, using a `tab` directive. Consecutive `tab` directives create a single set of tabs. ```rst This is text before the tabs. .. tab:: One One is an interesting number. .. tab:: Two Two is the even prime. .. tab:: Three Three is an odd prime. .. tab:: Four Four is not a perfect number. This is text after the tabs, which seems to flow right through, which avoids breaking the flow of the document. ``` This is text before the tabs. ```{tab} One One is an interesting number. ``` ```{tab} Two Two is the even prime. ``` ```{tab} Three Three is an odd prime. ``` ```{tab} Four Four is not a perfect number. ``` This is text after the tabs, which seems to flow right through. There is no visual difference for content that's within a tab vs outside it -- adding tabs doesn't disrupt the flow of the document. ## Multiple Tab Sets It is possible to start a new set from a tab, by having some content between sets or by providing `:new-set:` option to the `tab` directive. ```rst .. tab:: One One is an interesting number. .. tab:: Two Two is the even prime. This will break the tab set! .. tab:: Three Three is an odd prime. .. tab:: Four Four is not a perfect number. .. tab:: Five :new-set: Five is a nice number. .. tab:: Six Six is also nice. ``` ```{eval-rst} .. tab:: One One is an interesting number. .. tab:: Two Two is the even prime. This will break the tab set! .. tab:: Three Three is an odd prime. .. tab:: Four Four is not a perfect number. .. tab:: Five :new-set: Five is a nice number. .. tab:: Six Six is also nice. ``` ## Rich tab labels This is only possible with Markdown, as far as I know. ```` ```{tab} Three Three is an odd prime. ``` ```{tab} Four (equal to $2^2$) Four is not a perfect number. ``` ```` ```{tab} Three Three is an odd prime. ``` ```{tab} Four (equal to $2^2$) Four is not a perfect number. ``` ## Code Tabs The first code block in a tab content will "join" with the tabs, making things fairly clean for language snippets and OS-based command suggestions. ````{tab} Python ```python print("Hello World!") ``` It's pretty simple! ```` ````{tab} C++ ```cpp #include int main() { std::cout << "Hello World!" << std::endl; } ``` More code, but it works too! ```` ````{tab} Text ```none Hello World! ``` Why not. ```` ## Synchronisation Tabs across multiple sets are synchronised according to the label, unconditionally. This requires JavaScript to be enabled on the end user's browser and, thus, should be considered a progressive enhancement. ```{hint} Nearly all usage of tabbed content in documentation is based on something about the user which stays consistent throughout the reading (like their OS, or preferred language etc). That's why this behaviour is unconditional. ``` ````{tab} Windows ```console $ py -m pip install sphinx ``` ```` ````{tab} Unix (MacOS / Linux) ```console $ python -m pip install sphinx ``` ```` ````{tab} Windows :new-set: ```console $ make.bat html ``` ```` ````{tab} Unix (MacOS / Linux) ```console $ make html ``` ```` ## Nesting ```{versionadded} 2020.04.11.beta8 ``` Tabs can be nested, allowing the creation of decision trees made with tabs. Nested tabs are also synchronised. ````{tab} Windows ```{tab} Command prompt `cmd.exe` ``` ```{tab} Powershell `ps2.exe` ``` ```` ````{tab} Unix (MacOS / Linux) As can be seen on the other tab, the tab sets will "join" when there's no text between different levels of tabs. Adding text between them will space them out. ```{tab} Bash `bash` ``` ```{tab} Zsh `zsh` ``` ```{tab} Fish `fish` ``` ```{tab} Powershell `ps2` ``` ```` ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1682108629.4820662 sphinx_inline_tabs-2023.4.21/noxfile.py0000644000000000000000000001122614420570325014655 0ustar00"""Development automation """ import datetime import glob import os import tempfile import nox PACKAGE_NAME = "sphinx_inline_tabs" nox.options.sessions = ["lint", "test"] # # Helpers # def _install_this_project_with_flit(session, *, extras=None, editable=False): session.install("flit") args = [] if extras: args.append("--extras") args.append(",".join(extras)) if editable: args.append("--pth-file" if os.name == "nt" else "--symlink") session.run("flit", "install", "--deps=production", *args, silent=True) # # Development Sessions # @nox.session(name="docs-live") def docs_live(session): if session.posargs: docs_dir = session.posargs[0] additional_dependencies = session.posargs[1:] else: docs_dir = "docs/" additional_dependencies = () _install_this_project_with_flit(session, extras=["doc"], editable=True) session.install("sphinx-autobuild", *additional_dependencies) with tempfile.TemporaryDirectory() as destination: session.run( "sphinx-autobuild", # for sphinx-autobuild "--port=0", "--watch=src/", "--open-browser", # for sphinx "-b=dirhtml", "-a", docs_dir, destination, ) @nox.session def docs(session): _install_this_project_with_flit(session, extras=["doc"], editable=False) # Generate documentation into `build/docs` session.run("sphinx-build", "-b", "dirhtml", "-v", "docs/", "build/docs") @nox.session def lint(session): session.install("pre-commit") args = list(session.posargs) args.append("--all-files") if "CI" in os.environ: args.append("--show-diff-on-failure") session.run("pre-commit", "run", "--all-files", *args) @nox.session def test(session): _install_this_project_with_flit(session, extras=["test"]) args = session.posargs or ["-n", "auto", "--cov", PACKAGE_NAME] session.run("pytest", *args) def get_release_versions(version_file): marker = "__version__ = " with open(version_file) as f: for line in f: if line.startswith(marker): version = line[len(marker) + 1 : -2] last_release_date, last_release_number_str = version.split(".dev") last_release_number = int(last_release_number_str) break else: raise RuntimeError("Could not find current version.") today = datetime.date.today() today_date = today.strftime("%Y.%m.%d") if last_release_date.startswith(today_date): release_version = today_date else: release_version = today.strftime(f"%Y.%m.%d.{last_release_number}") next_version = today.strftime(f"%Y.%m.%d.dev{last_release_number+1}") return release_version, next_version @nox.session def release(session): version_file = f"src/{PACKAGE_NAME}/__init__.py" allowed_upstreams = [ f"git@github.com:pradyunsg/{PACKAGE_NAME.replace('_', '-')}.git" ] release_version, next_version = get_release_versions(version_file) session.install("flit", "twine", "release-helper") # Sanity Checks session.run("release-helper", "version-check-validity", release_version) session.run("release-helper", "version-check-validity", next_version) session.run("release-helper", "directory-check-empty", "dist") session.run("release-helper", "git-check-branch", "main") session.run("release-helper", "git-check-clean") session.run("release-helper", "git-check-tag", release_version, "--does-not-exist") session.run("release-helper", "git-check-remote", "origin", *allowed_upstreams) # Prepare release commit session.run("release-helper", "version-bump", version_file, release_version) session.run("git", "add", version_file, external=True) session.run( "git", "commit", "-m", f"Prepare release: {release_version}", external=True ) # Build the package session.run("flit", "build") session.run("twine", "check", *glob.glob("dist/*")) # Tag the commit session.run( # fmt: off "git", "tag", release_version, "-m", f"Release {release_version}", "-s", external=True, # fmt: on ) # Prepare back-to-development commit session.run("release-helper", "version-bump", version_file, next_version) session.run("git", "add", version_file, external=True) session.run("git", "commit", "-m", "Back to development", external=True) # Push the commits and tag. session.run("git", "push", "origin", "main", release_version, external=True) # Upload the distributions. session.run("twine", "upload", *glob.glob("dist/*")) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1629188395.0695143 sphinx_inline_tabs-2023.4.21/pyproject.toml0000644000000000000000000000072214106670453015556 0ustar00[build-system] requires = ["flit_core >=2,<4"] build-backend = "flit_core.buildapi" [tool.flit.metadata] module = "sphinx_inline_tabs" author = "Pradyun Gedam" author-email = "mail@pradyunsg.me" home-page = "https://github.com/pradyunsg/sphinx-inline-tabs" description-file = "README.md" requires-python = ">=3.8" requires = ["sphinx >= 3"] [tool.flit.metadata.requires-extra] test = [ "pytest", "pytest-cov", "pytest-xdist", ] doc = ["myst-parser", "furo"] ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1682108710.0718868 sphinx_inline_tabs-2023.4.21/src/sphinx_inline_tabs/__init__.py0000644000000000000000000000173214420570446021424 0ustar00"""Add inline tabbed content to your Sphinx documentation.""" import os __version__ = "2023.04.21" __all__ = ["setup"] def setup(app): """Entry point for sphinx theming.""" app.require_sphinx("3.0") # We do imports from Sphinx, after validating the Sphinx version from ._impl import TabDirective, TabHtmlTransform, _TabInput, _TabLabel app.add_directive("tab", TabDirective) app.add_post_transform(TabHtmlTransform) app.add_node(_TabInput, html=(_TabInput.visit, _TabInput.depart)) app.add_node(_TabLabel, html=(_TabLabel.visit, _TabLabel.depart)) # Include our static assets static_dir = os.path.join(os.path.dirname(__file__), "static") app.connect( "builder-inited", (lambda app: app.config.html_static_path.append(static_dir)) ) app.add_js_file("tabs.js") app.add_css_file("tabs.css") return { "parallel_read_safe": True, "parallel_write_safe": True, "version": __version__, } ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1680043664.8244846 sphinx_inline_tabs-2023.4.21/src/sphinx_inline_tabs/_impl.py0000644000000000000000000001405014410667221020757 0ustar00"""The actual implementation.""" import itertools from typing import List from docutils import nodes from docutils.parsers.rst import directives from sphinx.transforms.post_transforms import SphinxPostTransform from sphinx.util.docutils import SphinxDirective from sphinx.util.nodes import NodeMatcher class TabContainer(nodes.container): """The initial tree-node for holding tab content.""" class _GeneralHTMLTagElement(nodes.Element, nodes.General): @staticmethod def visit(translator, node): attributes = node.attributes.copy() # Nobody needs this crap. attributes.pop("ids") attributes.pop("classes") attributes.pop("names") attributes.pop("dupnames") attributes.pop("backrefs") text = translator.starttag(node, node.tagname, **attributes) translator.body.append(text.strip()) @staticmethod def depart(translator, node): if node.endtag: translator.body.append(f"") class _TabInput(_GeneralHTMLTagElement): tagname = "input" endtag = False class _TabLabel(_GeneralHTMLTagElement): tagname = "label" endtag = True class TabDirective(SphinxDirective): """Tabbed content in Sphinx documentation.""" required_arguments = 1 # directive takes a single argument. final_argument_whitespace = True # this allows that argument to contain spaces. has_content = True option_spec = { "new-set": directives.flag, } def run(self): """Parse a tabs directive.""" self.assert_has_content() container = TabContainer("", type="tab", new_set="new-set" in self.options) self.set_source_info(container) # Handle the label (non-plain-text variants allowed) textnodes, messages = self.state.inline_text(self.arguments[0], self.lineno) # The signature of this object is: # __init__(self, rawsource='', text='', *children, **attributes) # # We want to directly populate the children here. label = nodes.label("", "", *textnodes) # Handle the content content = nodes.container("", is_div=True, classes=["tab-content"]) self.state.nested_parse(self.content, self.content_offset, content) container += label container += content return [container] class TabHtmlTransform(SphinxPostTransform): """Transform output of TabDirective into usable chunks.""" default_priority = 200 formats = ["html"] def run(self): """Locate and replace `TabContainer`s.""" self.stack = [] # type: List[List[TabContainer]] self.counter = itertools.count(start=0, step=1) matcher = NodeMatcher(TabContainer) for node in self.document.traverse(matcher): # type: TabContainer self._process_one_node(node) while self.stack: tab_set = self.stack.pop() self.finalize_set(tab_set, next(self.counter)) def _process_one_node(self, node: TabContainer): # There is no existing tab set. Let's start a new one. if not self.stack: self.stack.append([node]) return # There should never be an empty "current" tab set. assert self.stack[-1] close_till = None append = False for tab_set in reversed(self.stack[:]): last_node = tab_set[-1] # Is this node a direct child of the last node in this tab-set? is_child = node in last_node.children[1] if is_child: close_till = tab_set append = False break # Is this node a sibling of the last node in this tab-set? is_sibling = ( node.parent == last_node.parent # same parent # immediately after the previous node and node.parent.index(last_node) + 1 == node.parent.index(node) ) if is_sibling: close_till = tab_set append = True break # Close all tab sets as required. if close_till is not None: while self.stack[-1] != close_till: self.finalize_set(self.stack.pop(), next(self.counter)) else: while self.stack: self.finalize_set(self.stack.pop(), next(self.counter)) # Start a new tab set, as required or if requested. if append and not node["new_set"]: assert self.stack self.stack[-1].append(node) else: self.stack.append([node]) def finalize_set(self, tab_set: List[TabContainer], set_counter: int): """Add these TabContainers as a single-set-of-tabs.""" assert tab_set parent = tab_set[0].parent container = nodes.container("", is_div=True, classes=["tab-set"]) container.parent = parent tab_set_name = f"tab-set--{set_counter}" node_counter = 0 for node in tab_set: node_counter += 1 tab_id = tab_set_name + f"-input--{node_counter}" title, content = node.children # , for storing state in radio boxes. input_node = _TabInput( type="radio", ids=[tab_id], name=tab_set_name, classes=["tab-input"] ) #