diff options
Diffstat (limited to '.config/qutebrowser')
117 files changed, 20693 insertions, 0 deletions
diff --git a/.config/qutebrowser/Archive/alias.txt b/.config/qutebrowser/Archive/alias.txt new file mode 100644 index 0000000..a768263 --- /dev/null +++ b/.config/qutebrowser/Archive/alias.txt @@ -0,0 +1,2 @@ + c.aliases = { 'w': 'session-save', 'q': 'quit', 'wq': 'quit --save', 'qtb': 'open https://www.qutebrowser.org/', 'mbt': 'open t https://signin1.bt.com/login/emailloginform', 'Ombt': 'open -t https://signin1.bt.com/login/emailloginform', 'nx': 'open http://next-episode.net/', 'Onx': 'open -t http://next-episode.net/', 'gk': 'open https://gameknot.com/', 'Ogk': 'open -t https://gameknot.com/', 'yt': 'open https://youtube.com', 'Oyt': 'open -t https://youtube.com', 'Y': 'open http://www.cosmos.esa.int/web/xmm-newton/current-calibration-files', 'OY': 'open -t http://www.cosmos.esa.int/web/xmm-newton/current-calibration-files', '1337': 'open https://1337x.to/home', 'O1337': 'open -t https://1337x.to/home', 'ez': 'open https://eztv.ag', 'Oez': 'open -t https://eztv.ag', 'tz': 'open https://extratorrent.ag', 'Otz': 'open -t https://extratorrent.ag', 'tpb': 'open https://thepiratebay.org/', 'Otpb': 'open -t https://thepiratebay.org/', 'Nm': 'open https://netmail.herts.ac.uk/', 'ONm': 'open -t https://netmail.herts.ac.uk/' } + diff --git a/.config/qutebrowser/Archive/search.txt b/.config/qutebrowser/Archive/search.txt new file mode 100644 index 0000000..e91a46c --- /dev/null +++ b/.config/qutebrowser/Archive/search.txt @@ -0,0 +1,2 @@ + c.url.searchengines = { 'DEFAULT': 'https://duckduckgo.com/?q={}', 'aw': 'https://wiki.archlinux.org/?q={}', 'g': 'https://www.google.com/?q={}' } + diff --git a/.config/qutebrowser/autoconfig.yml b/.config/qutebrowser/autoconfig.yml new file mode 100644 index 0000000..b2419ef --- /dev/null +++ b/.config/qutebrowser/autoconfig.yml @@ -0,0 +1,12 @@ +# If a config.py file exists, this file is ignored unless it's explicitly loaded +# via config.load_autoconfig(). For more information, see: +# https://github.com/qutebrowser/qutebrowser/blob/master/doc/help/configuring.asciidoc#loading-autoconfigyml +# DO NOT edit this file by hand, qutebrowser will overwrite it. +# Instead, create a config.py - see :help for details. + +config_version: 2 +settings: + content.javascript.enabled: + '*://*.next-episode.net/*': false + content.notifications: + https://1337x.to: false diff --git a/.config/qutebrowser/bookmarks/urls b/.config/qutebrowser/bookmarks/urls new file mode 100755 index 0000000..1d2c584 --- /dev/null +++ b/.config/qutebrowser/bookmarks/urls @@ -0,0 +1,272 @@ +https://www.ifixit.com/Guide/HP+TouchPad+Battery+Replacement/6082 HP TouchPad Battery Replacement - iFixit +http://opensecuritytraining.info/LifeOfBinaries.html LifeOfBinaries +http://stackoverflow.com/questions/20027990/how-can-i-get-text-section-from-pe-file-using-pefile python - How can I get .text section from PE file using pefile - Stack Overflow +http://what-when-how.com/windows-forensic-analysis/executable-file-analysis-windows-forensic-analysis-part-2/ Executable File Analysis (Windows Forensic Analysis) Part 2 +http://stackoverflow.com/questions/2363483/python-slicing-a-very-large-binary-file Python: slicing a very large binary file - Stack Overflow +http://stackoverflow.com/questions/39312720/python-extract-text-from-struct-or-binary-file Python extract text from Struct, or binary file - Stack Overflow +https://www.poftut.com/use-linux-xxd-command-tutorial-hex-binary-operations-example/ How To Use Linux Xxd Command Tutorial For Hex and Binary Operations With Example? - Poftut +http://stackoverflow.com/questions/28627349/offset-when-reading-binary-file-in-python binaryfiles - Offset when reading binary file in python - Stack Overflow +http://stackoverflow.com/questions/18954596/python-reading-binary-file-with-offsets-and-structs Python - Reading binary file with offsets and structs - Stack Overflow +http://stackoverflow.com/questions/3299213/python-how-can-i-open-a-file-and-specify-the-offset-in-bytes Python - How can I open a file and specify the offset in bytes? - Stack Overflow +http://stackoverflow.com/questions/35883044/python-2-7-11-how-to-read-big-binary-file-from-at-hex-offset Python 2.7.11: How to read (big) binary file from/at hex offset - Stack Overflow +qute://help/commands.html#unbind Commands +https://askubuntu.com/questions/534187/where-is-the-login-screen-wallpaper-for-gdm-stored 14.04 - Where is the login screen wallpaper for GDM stored? - Ask Ubuntu +file://${HOME}/Documents/Personal/bookmarks.html Bookmarks +https://heasarc.gsfc.nasa.gov/ HEASARC: NASA's Archive of Data on Energetic Phenomena +https://netmail.herts.ac.uk/ecp/?rfr=owa&owaparam=modurl%3D0&p=organize/AutomaticReplies.slab automatic replies - Outlook Web App +http://localhost:8889/notebooks/Sandbox/Python/Untitled.ipynb# Untitled +https://ctftime.org/ CTFtime.org / All about CTF (Capture The Flag) +http://overthewire.org/wargames/ OverTheWire: Wargames +https://stackoverflow.com/questions/38580647/how-to-create-a-directory-search-and-replace-with-vimscript-and-fzf vim - How to create a directory search and replace with vimscript and fzf - Stack Overflow +https://superuser.com/questions/1314397/how-to-search-file-contents-grep-ag-rg-using-fzf How to search file *contents* (grep/ag/rg) using FZF? - Super User +https://news.ycombinator.com/item?id=15514589 FZF and RipGrep – Navigate with bash faster than ever before | Hacker News +https://vi.stackexchange.com/questions/14242/how-to-call-the-fzf-ag-command-with-multiple-arguments/14246 vimscript - How to call the fzf Ag command with multiple arguments? - Vi and Vim Stack Exchange +http://eradman.com/entrproject/ entr(1) +http://harmful.cat-v.org/software/ All software sucks +https://suckless.org/rocks/ Stuff that rocks | suckless.org software that sucks less +https://github.com/ranger/ranger/wiki/Official-user-guide Official user guide · ranger/ranger Wiki +https://turses.readthedocs.io/en/latest/user/configuration.html Configuration — turses 0.3.1 documentation +http://vimdoc.sourceforge.net/htmldoc/message.html Vim documentation: message +http://stevelosh.com/blog/2012/10/the-homely-mutt/ The Homely Mutt / Steve Losh +http://www.mutt.org/doc/manual/#sidebar The Mutt E-Mail Client +http://www.samlogic.net/articles/smtp-commands-reference.htm SMTP Commands Reference (covers HELO/EHLO, MAIL, RCPT, DATA, RSET, VRFY, AUTH, STARTTLS etc) +https://medium.com/@insecurity_92477/ssh-shenanigans-the-good-the-bad-and-the-evil-16945ca72596 SSH Shenanigans — The good, the bad, and the evil – PROJECT INSECURITY – Medium +https://dzone.com/articles/ssh-from-the-ground-up SSH From the Ground Up - DZone Performance +http://flask.pocoo.org/ Welcome | Flask (A Python Microframework) +https://netmail.herts.ac.uk/owa/#path=/mail Graffagnino, Vito - Outlook Web App +http://www.therandymon.com/woodnotes/mutt/node24.html Configuring Mutt +http://www.therandymon.com/woodnotes/mutt/using-mutt.html The Woodnotes Guide to the Mutt Email Client +http://www.grameen-info.org/windows-7-professional-product-key-free-for-you/ Windows 7 Professional Product Key Free | Grameen Bank - Bank For The Poor +https://www.itechgyan.com/windows-7-professional-product-key-32-64-bit/ Windows 7 Professional Product Key for 32/64- bit - iTechGyan.com +https://www.thewindowsclub.com/disable-auto-activation-feature-windows-7-8 Disable Automatic Windows Activation popup in Windows 10/8/7 +https://dotfiles.github.io/ GitHub does dotfiles - dotfiles.github.io +https://developer.atlassian.com/blog/2016/02/best-way-to-store-dotfiles-git-bare-repo/ The best way to store your dotfiles: A bare Git repository - Atlassian Developers +https://stackoverflow.com/questions/49658760/convert-json-format-file-to-tsv-using-python convert json format file to tsv using python - Stack Overflow +https://gitlab.com/dwt1/dotfiles/blob/master/.config/vifm/vifmrc .config/vifm/vifmrc · master · Derek Taylor / dotfiles · GitLab +https://guides.fixato.org/weechat/ FiXato's Guide to WeeChat +https://www.linode.com/docs/applications/messaging/using-weechat-for-irc/ Using WeeChat for Internet Relay Chat +https://hugo.md/post/the-perfect-weechat-setup-2/ The Perfect Weechat Setup · Hugo's Blog +https://weechat.org/files/doc/devel/weechat_faq.en.html#irc_ssl_connection WeeChat FAQ (Frequently Asked Questions) +https://forum.xda-developers.com/hp-touchpad/help/how-to-install-android-7-x-hp-touchpad-t3512182/post69927244#post69927244 [ROM GUIDE] How to Install Android 9.x/8.1.x… | HP TouchPad +https://kevin.deldycke.com/2012/08/gmail-backup-mbsync/ Keep a Local Backup of Gmail Thanks to Mbsync | Kevin Deldycke +https://forum.xda-developers.com/showpost.php?p=52922166&postcount=64 [TOOLS] Touchpad Toolbox (updated 2015-02-25) - Post #64 +https://forum.xda-developers.com/hp-touchpad/help/guide-video-how-to-bypass-webos-t3636750 [Guide+Video]How to bypass WebOS activation … | HP TouchPad +https://forum.xda-developers.com/hp-touchpad/development/luneos-webos-ports-t2851450 LuneOS (webOS Ports) | HP TouchPad +https://forum.xda-developers.com/showthread.php?t=1426244 [Guide] Factory condition restoration / Down… | HP TouchPad +https://haveibeenpwned.com/ Have I Been Pwned: Check if your email has been compromised in a data breach +https://forum.xda-developers.com/showthread.php?t=2761381 DebiAndroid [WIP] - Debian native/chroot for… | HP TouchPad +https://indico.ict.inaf.it/event/720/ X-RAY ASTRONOMY 2019 (8-13 September 2019) · INDICO @ INAF (Indico) +https://docs.google.com/forms/d/e/1FAIpQLSf388FvDduVVNT0lLpSz0KrvGjlfuALvv6jnoLBen4bEh26TQ/viewform Chandra/CIAO Workshop, Bologna, Italy, 2019 +https://www.manualslib.com/download/880661/Bmw-318i.html Download BMW 318i Owner's Handbook Manual +https://complaint.resolver.co.uk/#/dashboard Resolver +https://www.moneysavingexpert.com/reclaim/ppi-loan-insurance/#resolver Reclaim PPI for Free: How to claim £1000s - MoneySavingExpert +https://eztv.io/ EZTV - TV Torrents Online Series Download | Official +https://miloserdov.org/?p=1088 Fast and simple method to bypass Captive Portal (hotspot with authorization on the web-interface) - Ethical hacking and penetration testing +https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/cheatsheet-big.png cheatsheet-big.png (3342×2060) +https://jdhao.github.io/2018/12/24/centos_nvim_install_use_guide_en/ A Complete Guide for Installing and Setting up Neovim for Python Development - jdhao's blog +https://stackoverflow.com/questions/54429210/how-do-i-prevent-conda-from-activating-the-base-environment-by-default bash - How do I prevent Conda from activating the base environment by default? - Stack Overflow +https://stackoverflow.com/questions/32611932/how-can-i-switch-using-pip-between-system-and-anaconda python - How can I switch using pip between system and anaconda - Stack Overflow +http://docs.plasmapy.org/en/latest/auto_examples/plot_dispersion_function.html#sphx-glr-download-auto-examples-plot-dispersion-function-py The plasma dispersion function — PlasmaPy 0.2.0 documentation +http://docs.plasmapy.org/en/latest/development/install_dev.html#setting-up-an-environment-for-development Installing PlasmaPy for Development — PlasmaPy 0.2.0 documentation +https://linuxhint.com/arch-linux-docker-tutorial/ Arch Linux Docker Tutorial – Linux Hint +https://stackoverflow.com/questions/45692255/how-make-openvpn-work-with-docker debian - How make openvpn work with docker - Stack Overflow +https://wiki.archlinux.org/index.php/Docker Docker - ArchWiki +https://linuxhint.com/docker_arch_linux/ How to Use Docker on Arch Linux – Linux Hint +https://docs.docker.com/get-started/ Orientation and setup | Docker Documentation +https://news.ycombinator.com/item?id=11071754 I use: git init --bare $HOME/.myconf alias config='/usr/bin/git --git-d... | Hacker News +https://www.atlassian.com/git/tutorials/dotfiles How to store dotfiles | Atlassian Git Tutorial +https://www.cgsecurity.org/wiki/Advanced_Find_ext2_ext3_Backup_SuperBlock Advanced Find ext2 ext3 Backup SuperBlock - CGSecurity +http://stevelosh.com/blog/2012/10/the-homely-mutt/#installing-offlineimap The Homely Mutt / Steve Losh +https://wiki.archlinux.org/index.php/Isync isync - ArchWiki +https://forums.webosnation.com/hp-veer/313841-webos-2-2-4-doctor-veer-t-wr-13.html webOS 2.2.4 Doctor for Veer for AT&T or WR - Page 13 - webOS Nation Forums +https://www.webos-internals.org/ WebOS Internals +http://android-dls.com/wiki/index.php?title=HOWTO:_Unpack%2C_Edit%2C_and_Re-Pack_Boot_Images#Unpacking.2C_Editing.2C_and_Re-Packing_the_images HOWTO: Unpack, Edit, and Re-Pack Boot Images - Android Wiki +https://github.com/webos-internals/meta-doctor/blob/master/scripts/meta-att-touchpad-3.0.4 meta-doctor/meta-att-touchpad-3.0.4 at master · webos-internals/meta-doctor · GitHub +https://www.webos-internals.org/wiki/WebOS_Doctor_Versions WebOS Doctor Versions - WebOS Internals +https://bostonenginerd.com/posts/notmuch-of-a-mail-setup-part-1-mbsync-msmtp-and-systemd/ Notmuch of a mail setup Part 1- mbsync, msmtp and systemd | Assorted Nerdery +https://blog.jpalardy.com/posts/my-best-awk-tricks/ My Best Awk Tricks | Jonathan Palardy's Blog +https://weechat.org/files/doc/devel/weechat_user.en.html#buffers_and_windows WeeChat user’s guide +https://www.irchelp.org/faq/irctutorial.html IRCHelp.org — An IRC Tutorial +http://learn.astropy.org/ Learn Astropy — astropy-tutorials v3.0.dev +https://unix.stackexchange.com/questions/315063/mount-wrong-fs-type-bad-option-bad-superblock ubuntu - mount: wrong fs type, bad option, bad superblock - Unix & Linux Stack Exchange +https://ubuntuforums.org/showthread.php?t=839579 [ubuntu] mount: wrong fs type, bad option, bad superblock on /dev/sdb1 +https://ubuntuforums.org/showthread.php?t=1245536&p=7822694#post7822694 [SOLVED] How to recover from a bad superblock +https://www.cyberciti.biz/tips/surviving-a-linux-filesystem-failures.html Surviving a Linux Filesystem Failures - nixCraft +https://major.io/2010/12/14/mounting-a-raw-partition-file-made-with-dd-or-dd_rescue-in-linux/ Mounting a raw partition file made with dd or dd_rescue in Linux :: major.io +http://www.garloff.de/kurt/linux/ddrescue/ dd_rescue +https://jcutrer.com/howto/how-to-install-youtube-dl-the-easy-way How to install youtube-dl (the easy way) - jcutrer.com +https://www.howtogeek.com/261591/how-to-create-and-run-bash-shell-scripts-on-windows-10/ How to Create and Run Bash Shell Scripts on Windows 10 +https://www.how2shout.com/how-to/install-youtube-dl-linux-macos.html How to install Youtube-dl on Linux and MacOS | H2S Media +https://overthewire.org/wargames/ OverTheWire: Wargames +https://wiki.webos-internals.org/wiki/Application:Navit Application:Navit - WebOS Internals +https://www.linux.com/tutorials/how-fix-mangled-partition-table-linux/ How to Fix a Mangled Partition Table on Linux - Linux.com +http://pragmaticemacs.com/emacs/migrating-from-offlineimap-to-mbsync-for-mu4e/ Migrating from offlineimap to mbsync for mu4e | Pragmatic Emacs +https://emacs-fu.blogspot.com/2012/08/introducing-mu4e-for-email.html emacs-fu: introducing mu4e, an e-mail client for emacs +http://cachestocaches.com/2017/3/complete-guide-email-emacs-using-mu-and-/ A Complete Guide to Email in Emacs using Mu and Mu4e +https://www.emacswiki.org/emacs/mu4e EmacsWiki: mu4e +https://pertuttirestaurant.co.uk/?utm_source=tripadvisor&utm_medium=referral Per Tutti Restaurant | Authentically Italian +https://www.tripadvisor.co.uk/Restaurant_Review-g186306-d732621-Reviews-La_Cosa_Nostra-St_Albans_Hertfordshire_England.html LA COSA NOSTRA, St Albans - Updated 2020 Restaurant Reviews, Photos & Phone Number - Tripadvisor +http://barmeze.co.uk/booking-confirmation.html Bar Meze - St Albans | Reservation confirmation +https://www.tripadvisor.co.uk/Restaurant_Review-g186306-d1742312-Reviews-L_Italiana-St_Albans_Hertfordshire_England.html L'ITALIANA, St Albans - Updated 2020 Restaurant Reviews, Menu & Prices - Tripadvisor +https://www.tripadvisor.co.uk/Restaurant_Review-g186306-d9990284-Reviews-Per_Tutti_St_Albans-St_Albans_Hertfordshire_England.html PER TUTTI ST ALBANS - Updated 2020 Restaurant Reviews, Menu & Prices - Tripadvisor +https://www.tripadvisor.co.uk/Restaurant_Review-g186306-d2318820-Reviews-Alloro-St_Albans_Hertfordshire_England.html ALLORO, St Albans - Updated 2020 Restaurant Reviews, Menu, Prices & Restaurant Reviews - Food Delivery & Takeaway - Tripadvisor +https://pertuttirestaurant.co.uk/book-berkhamsted/ BOOK BERKHAMSTED | Per Tutti Restaurant +https://www.tripadvisor.co.uk/Restaurant_Review-g186306-d11885339-Reviews-Carmelo_s-St_Albans_Hertfordshire_England.html CARMELO'S, St Albans - Updated 2020 Restaurant Reviews, Menu & Prices - Tripadvisor +https://dystroy.org/broot/ Broot +http://www.ss.ncu.edu.tw/~lyu/lecture_files_en/index.html lyu_lecture_ch +http://silas.psfc.mit.edu/introplasma/index.html Introduction to Plasma Physics +https://www.plasma-universe.com/ The Plasma Universe +http://farside.ph.utexas.edu/teaching/plasma/lectures/Plasmahtml.html Plasmahtml +https://spacephysics.ucla.edu/index.html UCLA IGPP Space Physics Exercises +https://www.slickremix.com/docs/get-api-key-for-youtube/# Get API Key for YouTube | SlickRemix +https://courses.edx.org/courses/course-v1:EPFLx+PlasmaIntroductionX+1T_2018/course/ Course | PlasmaIntroductionX | edX +https://www.justpark.com/dashboard/bookings/made/ Bookings | JustPark +https://homepages.spa.umn.edu/~kd/Ast4001-2015/NOTES/n052-saha-bradt.pdf +http://www.astro.wisc.edu/~townsend/resource/teaching/astro-310-F09/solutions-2009-10-05.pdf +http://www.astro.wisc.edu/~townsend/resource/teaching/astro-310-F11/solutions-2011-10-14.pdf +https://people.physics.tamu.edu/belyanin/astr314/lecture8.pdf +http://www.astro.umd.edu/~miller/teaching/astr601/lecture08.pdf +http://www.astro.caltech.edu/~jlc/ay123_2007/save_sol3_2007.pdf +https://leanpub.com/the-tao-of-tmux/read Read The Tao of tmux | Leanpub +https://www.datacamp.com/community/tutorials/tutorial-jupyter-notebook (Tutorial) Jupyter Notebook: The Definitive Guide - DataCamp +https://github.com/jakevdp/PythonDataScienceHandbook GitHub - jakevdp/PythonDataScienceHandbook: Python Data Science Handbook: full text in Jupyter Notebooks +https://jakevdp.github.io/PythonDataScienceHandbook/ Python Data Science Handbook | Python Data Science Handbook +https://irkernel.github.io/installation/#binary-panel Installation · IRkernel +https://nbviewer.jupyter.org/github/jrjohansson/scientific-python-lectures/blob/master/Lecture-4-Matplotlib.ipynb Jupyter Notebook Viewer +https://4dcombat.thinkific.com/courses/4dcombat-online 4D Combat - Online Course +https://www.linode.com/docs/applications/messaging/using-weechat-for-irc/# Using WeeChat for Internet Relay Chat | Linode +https://realpython.com/courses/functional-programming-python/ Functional Programming in Python – Real Python +https://realpython.com/courses/how-to-publish-your-own-python-package-pypi/ How to Publish Your Own Python Package to PyPI – Real Python +https://realpython.com/courses/traditional-face-detection-python/ Traditional Face Detection With Python – Real Python +https://realpython.com/courses/cool-new-features-python-38/ Cool New Features in Python 3.8 – Real Python +https://realpython.com/account/purchases/ Your Purchases – Real Python +https://weechat.org/files/doc/stable/weechat_user.en.html#usage WeeChat user’s guide +https://weechat.org/scripts/ WeeChat :: scripts +https://github.com/GermainZ/weechat-vimode GitHub - GermainZ/weechat-vimode: A WeeChat script that adds vi-like modes, commands and keybindings. +https://github.com/wee-slack/wee-slack GitHub - wee-slack/wee-slack: A WeeChat plugin for Slack.com. Supports threads and reactions, synchronizes read markers, provides typing notification, etc.. +http://nemrod.se/guides/install-rpm-packages-on-arch-linux/ Install RPM packages on Arch Linux | nemrod.se +https://github.com/jupyter/jupyter/wiki/Jupyter-kernels Jupyter kernels · jupyter/jupyter Wiki · GitHub +https://www.docker.com/get-started Get Started with Docker | Docker +https://neovim.io/doc/user/nvim_terminal_emulator.html Nvim documentation: nvim_terminal_emulator +https://neovim.io/doc/user/ Nvim documentation: help +https://github.com/seebye/ueberzug GitHub - seebye/ueberzug: ueberzug is an alternative for w3mimgdisplay +https://github.com/seebye/fmui GitHub - seebye/fmui: fzf mpd user interface +https://hertfordshire.voxcharta.org/?s=aurora Search results for 'aurora' (page 1 of 6)Vox Charta +https://casa.nrao.edu/casa_obtaining.shtml CASA +https://ads.readthedocs.io/en/latest/ The ads Python package — ads 0.12.2 documentation +https://ui.adsabs.harvard.edu/ NASA/ADS +https://wiki.vifm.info/index.php?title=Quickstart_Tutorial Quickstart Tutorial - Vifm Wiki +https://arxiv.org/help/api/index arXiv API | arXiv e-print repository +https://github.com/junegunn/fzf/wiki/examples#search-for-academic-pdfs-by-author-title-journal-institution Examples · junegunn/fzf Wiki +https://www.reddit.com/r/neovim/comments/fu1omj/help_request_getting_deoplete_and_ultisnips_to/ [Help request] Getting deoplete and ultisnips to share completion key. : neovim +https://github.com/junegunn/fzf#preview-window junegunn/fzf: A command-line fuzzy finder +https://blog.thegate.ai/posts/20191113_configuring-neomutt-for-a-local-email-setup/ TheGate.AI | Blog | Creating a local email setup with mbsync + msmtp + neomutt + notmuch. +https://bostonenginerd.com/posts/notmuch-of-a-mail-setup-part-2-notmuch-and-emacs/ Notmuch of mail a setup Part 2 - notmuch and Emacs | Assorted Nerdery +https://notmuchmail.org/mutttips/ mutttips +http://isync.sourceforge.net/ isync: free IMAP and MailDir mailbox synchronizer +https://notmuchmail.org/howto/ howto +https://neomutt.org/feature/notmuch Notmuch - NeoMutt +https://wiki.archlinux.org/index.php/Notmuch Notmuch - ArchWiki +https://guides.nyu.edu/c.php?g=601858&p=4168138 Templates - Getting Started with LaTeX - Research Guides at New York University +https://www.latex-tutorial.com/tutorials/bibtex/ Bibliography in LaTeX with Bibtex/Biblatex +https://www.math.ias.edu/computing/faq/local-latex-style-files Where can I put local latex style files and packages? | IAS School of Mathematics +https://en.wikibooks.org/wiki/LaTeX/Installing_Extra_Packages LaTeX/Installing Extra Packages - Wikibooks, open books for an open world +https://github.com/adsabs/adsabs-dev-api/blob/master/Search_API.ipynb adsabs-dev-api/Search_API.ipynb at master · adsabs/adsabs-dev-api +https://github.com/bibcure/bibcure bibcure/bibcure: Bibcure helps in boring tasks by keeping your bibfile up to date and normalized...also allows you to easily download all papers inside your bibtex +https://arxiv.org/help/api/index#using arXiv API | arXiv e-print repository +https://github.com/junegunn/fzf/wiki/examples#buku Examples · junegunn/fzf Wiki +https://pandoc.org/getting-started.html Pandoc - Getting started with pandoc +https://tmuxcheatsheet.com/ Tmux Cheat Sheet & Quick Reference +https://stackoverflow.com/questions/1550226/python-setup-py-uninstall python setup.py uninstall - Stack Overflow +https://notmuchmail.org/initial_tagging/ initial tagging +https://neomutt.org/guide/configuration.html#mbox-hook Configuration - NeoMutt +http://jazz-the-two-of-us.blogspot.com/ The Sound of Jazz : Download Jazz MP3 Album +http://cazadam.blogspot.com/p/blog-page.html CazAdam: Albums +https://listen-jazz-easily.blogspot.com/2009/01/charlie-parker-very-best.html#links Listen Jazz Easily : Download Jazz Album on Blogspot: Charlie Parker - The Very Best +https://catonmat.net/linux-and-vim-notes My Linux and Vim Notes +https://bestlifeonline.com/hilariously-silly-jokes/ 40 Hilarious Jokes You Can Tell Absolutely Anyone - Silly Jokes +https://null-byte.wonderhowto.com/ Null Byte — The aspiring white-hat hacker/security awareness playground « Null Byte :: WonderHowTo +https://www.alfashop.co.uk/ Home | AlfaShop +https://www.alfashop.co.uk/products/search?utf8=%E2%9C%93&f_search=manual&button= https://www.alfashop.co.uk/products/search?utf8=✓&f_search=manual&button= +https://www.aroc-uk.com/159-register/gallery 159 Register Gallery | Alfa Romeo Owners Club +http://www.ausalfa.com/viewtopic.php?f=4&t=9411#p157448 ausalfa.com • View topic - Installation of after-market head unit into 159/Brera/Spider +https://www.alfaowner.com/threads/head-unit-stereo-upgrade-instructions.931370/ Head Unit / stereo Upgrade Instructions | Alfa Romeo Forum +https://ntmlabs.com/w3m-tricks/ NTM Labs - W3M tricks of the trade +https://github.com/felipesaa/A-vim-like-firefox-like-configuration-for-w3m/blob/master/documentation/functions.txt A-vim-like-firefox-like-configuration-for-w3m/functions.txt at master · felipesaa/A-vim-like-firefox-like-configuration-for-w3m +https://shells.red-pill.eu/ Free Shell Accounts :: the biggest list on the net - Rankings - All Sites +https://sdf.org/ SDF Public Access UNIX System - Free Shell Account and Shell Access +http://www.nyx.net/ Nyx +https://www.chrisatmachine.com/ Christian Chiarulli +https://www.manualslib.com/manual/628233/Alfa-Romeo-159.html ALFA ROMEO 159 OWNER'S MANUAL Pdf Download. +https://www.howtogeek.com/358166/using-android-without-google-a-kind-of-guide/ Using Android without Google: A (Kind of) Guide +https://android.stackexchange.com/questions/91628/how-to-remove-all-google-specific-apps-from-a-rooted-android applications - How to remove all Google-specific apps from a rooted Android? - Android Enthusiasts Stack Exchange +https://www.ducksters.com/jokes/silly.php Jokes for kids: big list of clean silly jokes +https://www.vpscheap.net/pricing.aspx Unmetered SSD and Budget VPS Hosting | VPSCheap.NET +https://www.thinkmoney.co.uk/credit-card/ Credit Cards UK | Apply for a Credit Card Online +https://tablesgenerator.com/ Create LaTeX tables online – TablesGenerator.com +http://detexify.kirelabs.org/classify.html Detexify LaTeX handwritten symbol recognition +https://outlook.office.com/mail/inbox Email - Vito Graffagnino - Outlook +https://www.androidjungles.com/how-to-unlock-bootloader-on-samsung-phones/ How to Unlock Bootloader on Samsung Phones +https://forum.xda-developers.com/galaxy-j5/samsung-galaxy-j5-2016-roms-kernels-recoveries--other-development/rom-tw-wycked-os-v2-0-updated-11-05-2018-t3789168 [ROM TW][7.1.1][J5 2016] Wycked-OS v3.0 Fina… | Samsung Galaxy J5 +https://forum.xda-developers.com/galaxy-j5/samsung-galaxy-j5-2016-roms-kernels-recoveries--other-development/rom-resurrection-remix-j5-2016-t3955181 [ROM][9]Resurrection Remix For J5 2016 | Samsung Galaxy J5 +https://www.getdroidtips.com/samsung-galaxy-j5-2016-stock-firmware/ Samsung Galaxy J5 2016 Stock Firmware Collections +https://forum.xda-developers.com/galaxy-nexus/guide-phone-backup-unlock-root-t1420351 [GUIDE] Full Phone Backup without Unlock or … | +https://appuals.com/fix-downloading-not-turn-off-target/ Fix: Downloading... Do not turn off target - Appuals.com +https://awesomedetect.com/flash-recovery-samsung-j5-2016-nougat-7-1-1/ Flash TWRP recovery in Samsung J5 2016 Nougat 7.1.1 without root – Awesome detect +https://www.droidviews.com/how-to-extract-pit-file-from-samsung-galaxy-devices/ Download Samsung PIT Files and Extract Them on Samsung Devices +https://www.full-repair-firmware.com/2017/10/full-firmware-j510fn.html Full Firmware For Device Samsung Galaxy J5 2016 SM-J510FN +https://www.full-repair-firmware.com/p/pit-files.html Pit Files +https://gearallnews.com/how-to-root-galaxy-j5-2016-sm-j510fn-and-install-twrp/ How To Root Galaxy J5 2016 SM-J510FN and Install TWRP +https://www.androidjungles.com/how-to-boot-into-fastboot-mode-on-samsung-phone/ How to Boot Into Fastboot Mode on Samsung Phone +https://www.androidsage.com/2017/07/12/list-of-samsung-galaxy-country-specific-product-code-csc-and-country-region/ List of Samsung Galaxy CSC Country Specific Product code and region they belong to +https://www.droidwin.com/root-android-devices-without-twrp-recovery/ How to Root Android Devices Without TWRP Recovery +https://rootmygalaxy.net/download-odin-tool-samsung-galaxy-devices-versions/ Download Samsung Odin Flash Tool (All Versions) for Windows +https://www.getdroidtips.com/best-magisk-modules-2020/ Best Magisk Modules You Should Try in 2020 +https://android.gadgethacks.com/how-to/install-microg-replace-google-play-services-prevent-data-mining-0193787/ How to Install MicroG to Replace Google Play Services & Prevent Data Mining « Android :: Gadget Hacks +https://acmarket.net/ ACMarket - Cracked Apps, Games, Mods for Android +https://apkmody.io/apps/titanium-backup-pro Titanium Backup Pro APK 8.4.0.2 (MOD Unlocked) Download +https://microg.org/download.html Download - microG Project +http://hex.ro/wp/blog/removing-gapps-from-cyanogenmod-11/ Removing gapps from Cyanogenmod 11 – and thus goes by another day +https://www.coinbase.com/join/smith_5to1 Buy/Sell cryptocurrency - Coinbase +https://www.vultr.com/features/datacenter-locations/#locations-map Largest Cloud Server Network Available - Vultr.com +https://my.vultr.com/ Log In - Vultr.com +https://stackpointer.io/mobile/android-adb-backup-extract-restore-repack/372/ Android ADB Backup, Extract, Restore - Stack Pointer +https://www.reddit.com/r/linux/comments/13nuda/poor_mans_vpn_with_ssh/ Poor man's VPN with ssh : linux +https://www.reddit.com/r/linux/comments/13nuda/poor_mans_vpn_with_ssh/?sort=new Poor man's VPN with ssh : linux +https://appmaildev.com/en/dkim DKIM Test - DKIM Verify - DKIM Validator +https://www.williamjbowman.com/blog/2015/07/24/setting-up-webdav-caldav-and-carddav-servers/ Setting up WebDAV, CalDAV, and CardDAV servers +https://jonathantutorial.blogspot.com/2014/10/how-to-set-up-radicale.html Jonathan Tutorials: How to set up a private Calendar and Contacts server (Radicale) on Ubuntu 14.04 LTS +https://doom.fandom.com/wiki/Doom_cheat_codes Doom cheat codes | Doom Wiki | Fandom +https://www.reddit.com/r/Piracy/comments/baufql/youtubedl_the_easy_way_on_android/ YouTube-DL the easy way on Android : Piracy +https://wiki.termux.com/wiki/Graphical_Environment Graphical Environment - Termux Wiki +https://www.techncyber.com/2019/08/kali-nethunter-in-termux.html How To use Kali Nethunter in Termux +https://www.reddit.com/r/linux/comments/66fh4f/what_do_you_use_termux_on_android_for/ What do you use Termux on android for? : linux +https://build.nethunter.com/kalifs/kalifs-latest/ Index of /kalifs/kalifs-latest/ +https://github.com/hiway/python-qutescript hiway/python-qutescript: Painless userscripts for qutebrowser. +qute://help/userscripts.html Writing qutebrowser userscripts +https://newsboat.org/releases/2.20.1/docs/newsboat.html#_query_feeds The Newsboat RSS Feedreader +https://hiphish.github.io/blog/2020/05/31/macho-man-command-on-steroids/ HiPhish's Workshop +https://www.verizon.com/support/knowledge-base-221536/ Palm - Factory Data Reset (Powered Off) | Verizon +https://devhints.io/bash Bash scripting cheatsheet +https://forum.xda-developers.com/galaxy-j5/samsung-galaxy-j5-2016-roms-kernels-recoveries--other-development/patcher-dualbootpatcher-j510-t3614376/page7 [PATCHER] DualBootPatcher for J510 - Pg. 7 | Samsung Galaxy J5 +https://new.myhermes.co.uk/track.html#/parcel/8017376759340718/details Track a parcel - MyHermes +https://forum.xda-developers.com/galaxy-j5/samsung-galaxy-j5-2016-roms-kernels-recoveries--other-development/rom-skies-a5ux-port-j510xx-hpm8294-t3586467 | ROM | | 6.0.X - 7.X.X | | J5 2016 all vari… | Samsung Galaxy J5 +https://musicasacra.com/music/latin-settings/ Latin Chant and Choral Music for the Mass | Church Music Association of America +http://docs.adaptivecomputing.com/torque/3-0-5/commands/qdel.php qdel +http://docs.adaptivecomputing.com/maui/commands/mjobctl.php#jobexp docs.adaptivecomputing.com/maui/commands/mjobctl.php#jobexp +http://docs.adaptivecomputing.com/torque/2-5-12/help.htm#topics/commands/qdel.htm qdel +https://leathermissalcovers.co.uk/index.php?route=product/product&path=61&product_id=82&limit=100 zipped leather cover +https://webgefrickel.de/blog/a-modern-mutt-setup-part-two A modern mutt setup — part two | webgefrickel +https://outlook.office.com/mail/options/mail/automaticReplies/automaticRepliesOption Email - Vito Graffagnino - Outlook +http://joshwalawender.github.io/IRAFtutorial/index.html Introduction to IRAF +http://iraf.noao.edu/tutorials/tutorials.html IRAF TUTORIALS AND EXERCISES +https://mkiminki.github.io/notes/irafinstall.html How To Install IRAF | Megan Kiminki +http://iraf.noao.edu/docs/src/pcix/unixsmg-toc.html#TOC UNIX/IRAF Site Manager's Guide +https://karlcordes.com/coreboot-x220/ Karl Cordes - How to flash coreboot on Lenovo X220 +https://leathermissalcovers.co.uk/index.php?route=common/home Leather Missal Covers +https://www.ifixit.com/Device/Lenovo_Thinkpad_X220 Lenovo Thinkpad X220 - iFixit +https://twrp.me/faq/datamedia.html What is a data/media device? +https://forum.xda-developers.com/android/general/guide-degoogle-device-install-microg-t4058743 [GUIDE] DeGoogle any device and install Micr… | Android Development and Hacking +https://herts365.sharepoint.com/sites/Toolkits Toolkits - Home +https://www.jeffgeerling.com/blogs/jeff-geerling/push-your-git-repositories Push your Git repositories to a central server, in a bare repository | Jeff Geerling +https://python-gitlab.readthedocs.io/en/stable/cli.html gitlab CLI usage — python-gitlab 2.5.0 documentation diff --git a/.config/qutebrowser/config.py b/.config/qutebrowser/config.py new file mode 100644 index 0000000..742d6e1 --- /dev/null +++ b/.config/qutebrowser/config.py @@ -0,0 +1,1802 @@ +## Autogenerated config.py ## Documentation: ## qute://help/configuring.html ## qute://help/settings.html +# +# Read ~/.Xresources file for colours (see the help file listed above) +# import subprocess + +# def read_xresources(prefix): + # props = {} + # x=subprocess.run(['xrdb','-query'], stdout=subprocess.PIPE) + # lines = x.stdout.decode().split('\n') + # for line in filter(lambda l : l.startswith(prefix), lines): + # prop, _, value = line.partition(':\t') + # props[prop] = value + # return props + +# xresources = read_xresources('*') +# c.colors.statusbar.normal.bg = xresources['*.background'] + + +## This is here so configs done via the GUI are still loaded. +## Remove it to not load settings done via the GUI. +# config.load_autoconfig() + +## Aliases for commands. The keys of the given dictionary are the +## aliases, while the values are the commands they map to. +## Type: Dict + +c.aliases = {'w':'session-save', 'q':'quit', 'wq':'quit --save', + '1337':'open https://1337x.to/home/', + 'O1337':'open -t https://1337x.to/home/', + 'ads':'open https://ui.adsabs.harvard.edu/classic-form', + 'Oads':'open -t https://ui.adsabs.harvard.edu/classic-form', + 'an':'open https://neonalley.com', + 'Oan':'open -t https://neonalley.com', + 'am':'open https://amazon.co.uk', + 'Oam':'open -t https://amazon.co.uk', + 'bc':'open https://bitchute.com', + 'Obc':'open -t https://bitchute.com', + 'bs':'open https://brittssoulsp.blogspot.com', + 'Obs':'open -t https://brittssoulsp.blogspot.com', + 'bt':'open https://bitsnoop.com', + 'Obt':'open -t https://bitsnoop.com', + 'car':'open https://car.herts.ac.uk', + 'Ocar':'open -t https://car.herts.ac.uk', + 'cluster':'open https://uhhpc.herts.ac.uk/wiki/index.php/Main_Page', + 'Ocluster':'open -t https://uhhpc.herts.ac.uk/wiki/index.php/Main_Page', + 'cm':'open https://churchmilitant.com/ ;; fake-key --global tSH ;; fake-key --global tSH ', + 'Ocm':'open -t https://churchmilitant.com/ ;; fake-key --global tSH ;; fake-key --global tSH ', + 'do':'open https://www.divinumofficium.com/', + 'Odo':'open -t https://www.divinumofficium.com/', + 'eby':'open https://ebay.co.uk', + 'Oeby':'open -t https://ebay.co.uk', + 'epk':'open https://registrar.epik.com', + 'Oepk':'open -t https://registrar.epik.com', + 'ez':'open https://eztv.ag', + 'Oez':'open -t https://eztv.ag', + 'et':'open https://extratorrent.ag/', + 'Oet':'open -t https://extratorrent.ag/', + 'fp':'open https://fmapulse.com', + 'Ofp':'open -t https://fmapulse.com', + 'gh':'open https://geekprank.com/hacker/', + 'Ogh':'open -t https://geekprank.com/hacker/', + 'gfgf':'open https://www.giffgaff.com/auth/login', + 'Ogfgf':'open -t https://www.giffgaff.com/auth/login', + 'gk':'open https://gameknot.com/', + 'Ogk':'open -t https://gameknot.com/', + 'gn':'open https://uhhpc.herts.ac.uk/ganglia/', + 'Ogn':'open -t https://uhhpc.herts.ac.uk/ganglia/', + 'helpdesk':'open https://helpdesk.herts.ac.uk', + 'Ohelpdesk':'open -t https://helpdesk.herts.ac.uk', + 'hertshub':'open https://herts365.sharepoint.com/sites/HertsHub', + 'Ohertshub':'open -t https://herts365.sharepoint.com/sites/HertsHub', + 'hx':'open https://heasarc.gsfc.nasa.gov/', + 'Ohx':'open -t https://heasarc.gsfc.nasa.gov/', + 'ia':'open http://italiaautoparts.co.uk', + 'Oia':'open -t http://italiaautoparts.co.uk', + 'iso':'open https://isohunt.to', + 'Oiso':'open -t https://isohunt.to', + 'll':'open https://go.herts.ac.uk/LiL', + 'Oll':'open -t https://go.herts.ac.uk/LiL', + 'ls':'open https://lukesmith.xyz', + 'Ols':'open -t https://lukesmith.xyz', + 'lms':'open https://lms.org.uk', + 'Olms':'open -t https://lms.org.uk', + 'lt':'open https://www.limetorrents.cc', + 'Olt':'open -t https://www.limetorrents.cc', + 'mbt':'open https://signin1.bt.com/login/emailloginform', + 'Ombt':'open -t https://signin1.bt.com/login/emailloginform', + 'netgear':'open http://2.96.233.114:8080', + 'Onetgear':'open -t http://2.96.233.114:8080', + 'Nm':'open https://outlook.office365.com/', + 'ONm':'open -t https://outlook.office365.com/', + 'nx':'open https://next-episode.net/ ;; fake-key --global tSH ;; fake-key --global tSH ', + 'Onx':'open -t https://next-episode.net/ ;; fake-key --global tSH ;; fake-key --global tSH ', + 'pm':'open https://mail.protonmail.com/login', + 'Opm':'open -t https://mail.protonmail.com/login', + 'pv':'open https://www.pivotce.com/', + 'Opv':'open -t https://www.pivotce.com/', + 'qtb':'open https://www.qutebrowser.org/', + 'Oqtb':'open -t https://www.qutebrowser.org/', + 'qtc':'open https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/cheatsheet-big.png', + 'Oqtc':'open -t https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/cheatsheet-big.png', + 'rb':'open https://www.rarbg.to', + 'Orb':'open -t https://www.rarbg.to', + 'sms':'open https://freesmscode.com', + 'Osms':'open -t https://freesmscode.com', + 'sn':'open https://studynet.herts.ac.uk', + 'Osn':'open -t https://studynet.herts.ac.uk', + 'sp':'open https://stpaulcenter.com', + 'Osp':'open -t https://stpaulcenter.com', + 't1':'open https://uhfinance.t1cloud.com', + 'Ot1':'open -t https://uhfinance.t1cloud.com', + 'tb':'open https://www.torrentbit.net', + 'Otb':'open -t https://www.torrentbit.net', + 'tf':'open https://www.torrentfunk.com', + 'Otf':'open -t https://www.torrentfunk.com', + 'tl':'open https://www.torlock.com', + 'Otl':'open -t https://www.torlock.com', + 'tpb':'open https://thepiratebay.org/', + 'Otpb':'open -t https://thepiratebay.org/', + 'tz':'open https://torrentz2.eu/my', + 'Otz':'open -t https://torrentz2.eu/my', + 'vx':'open https://hertfordshire.voxcharta.org/', + 'Ovx':'open -t https://hertfordshire.voxcharta.org/', + 'vtr':'open https://my.vultr.com/', + 'Ovtr':'open -t https://my.vultr.com/', + 'wn':'open https://www.webosnation.com/', + 'Own':'open -t https://www.webosnation.com/', + 'Y':'open http://www.cosmos.esa.int/web/xmm-newton/current-calibration-files', + 'OY':'open -t http://www.cosmos.esa.int/web/xmm-newton/current-calibration-files', + 'yfy':'open https://yts.am/', + 'Oyfy':'open -t https://yts.am/', + 'ytn':'open https://youtube.com/?disable_polymer=1', + 'Oytn':'open -t https://youtube.com/?disable_polymer=1', + 'yt':'open https://youtube.com/', + 'Oyt':'open -t https://youtube.com/', + 'rtr':'open -t http://192.168.1.254/home.htm ;; fake-key --global tSH '} +# +## Time interval (in milliseconds) between auto-saves of +## config/cookies/etc. +## Type: Int +# c.auto_save.interval = 15000 + +## Always restore open sites when qutebrowser is reopened. +## Type: Bool +c.auto_save.session = True + +## Backend to use to display websites. qutebrowser supports two different +## web rendering engines / backends, QtWebKit and QtWebEngine. QtWebKit +## was discontinued by the Qt project with Qt 5.6, but picked up as a +## well maintained fork: https://github.com/annulen/webkit/wiki - +## qutebrowser only supports the fork. QtWebEngine is Qt's official +## successor to QtWebKit. It's slightly more resource hungry than +## QtWebKit and has a couple of missing features in qutebrowser, but is +## generally the preferred choice. +## Type: String +## Valid values: +## - webengine: Use QtWebEngine (based on Chromium). +## - webkit: Use QtWebKit (based on WebKit, similar to Safari). +# c.backend = 'webengine' + +## This setting can be used to map keys to other keys. When the key used +## as dictionary-key is pressed, the binding for the key used as +## dictionary-value is invoked instead. This is useful for global +## remappings of keys, for example to map Ctrl-[ to Escape. Note that +## when a key is bound (via `bindings.default` or `bindings.commands`), +## the mapping is ignored. +## Type: Dict +# c.bindings.key_mappings = {'<Ctrl-[>': '<Escape>', '<Ctrl-6>': '<Ctrl-^>', '<Ctrl-M>': '<Return>', '<Ctrl-J>': '<Return>', '<Shift-Return>': '<Return>', '<Enter>': '<Return>', '<Shift-Enter>': '<Return>', '<Ctrl-Enter>': '<Ctrl-Return>'} +## Background color of the completion widget category headers. +## Type: QssColor +# c.colors.completion.category.bg = 'qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #888888, stop:1 #505050)' + +## Bottom border color of the completion widget category headers. +## Type: QssColor +# c.colors.completion.category.border.bottom = 'black' + +## Top border color of the completion widget category headers. +## Type: QssColor +# c.colors.completion.category.border.top = 'black' + +## Foreground color of completion widget category headers. +## Type: QtColor +# c.colors.completion.category.fg = 'white' + +## Background color of the completion widget for even rows. +## Type: QssColor +# c.colors.completion.even.bg = '#333333' + +## Text color of the completion widget. May be a single color to use for +## all columns or a list of three colors, one for each column. +## Type: List of QtColor, or QtColor +# c.colors.completion.fg = ['white', 'white', 'white'] + +## Background color of the selected completion item. +## Type: QssColor +# c.colors.completion.item.selected.bg = '#e8c000' + +## Bottom border color of the selected completion item. +## Type: QssColor +# c.colors.completion.item.selected.border.bottom = '#bbbb00' + +## Top border color of the completion widget category headers. +## Type: QssColor +# c.colors.completion.item.selected.border.top = '#bbbb00' + +## Foreground color of the selected completion item. +## Type: QtColor +# c.colors.completion.item.selected.fg = 'black' + +## Foreground color of the matched text in the completion. +## Type: QssColor +# c.colors.completion.match.fg = '#ff4444' + +## Background color of the completion widget for odd rows. +## Type: QssColor +# c.colors.completion.odd.bg = '#444444' + +## Color of the scrollbar in the completion view. +## Type: QssColor +# c.colors.completion.scrollbar.bg = '#333333' + +## Color of the scrollbar handle in the completion view. +## Type: QssColor +# c.colors.completion.scrollbar.fg = 'white' + +## Background color for the download bar. +## Type: QssColor +# c.colors.downloads.bar.bg = 'black' + +## Background color for downloads with errors. +## Type: QtColor +# c.colors.downloads.error.bg = 'red' + +## Foreground color for downloads with errors. +## Type: QtColor +# c.colors.downloads.error.fg = 'white' + +## Color gradient start for download backgrounds. +## Type: QtColor +# c.colors.downloads.start.bg = '#0000aa' + +## Color gradient start for download text. +## Type: QtColor +# c.colors.downloads.start.fg = 'white' + +## Color gradient stop for download backgrounds. +## Type: QtColor +# c.colors.downloads.stop.bg = '#00aa00' + +## Color gradient end for download text. +## Type: QtColor +# c.colors.downloads.stop.fg = 'white' + +## Color gradient interpolation system for download backgrounds. +## Type: ColorSystem +## Valid values: +## - rgb: Interpolate in the RGB color system. +## - hsv: Interpolate in the HSV color system. +## - hsl: Interpolate in the HSL color system. +## - none: Don't show a gradient. +# c.colors.downloads.system.bg = 'rgb' + +## Color gradient interpolation system for download text. +## Type: ColorSystem +## Valid values: +## - rgb: Interpolate in the RGB color system. +## - hsv: Interpolate in the HSV color system. +## - hsl: Interpolate in the HSL color system. +## - none: Don't show a gradient. +# c.colors.downloads.system.fg = 'rgb' + +## Background color for hints. Note that you can use a `rgba(...)` value +## for transparency. +## Type: QssColor +# c.colors.hints.bg = 'qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgba(255, 247, 133, 0.8), stop:1 rgba(255, 197, 66, 0.8))' + +## Font color for hints. +## Type: QssColor +# c.colors.hints.fg = 'black' + +## Font color for the matched part of hints. +## Type: QssColor +# c.colors.hints.match.fg = 'green' + +## Background color of the keyhint widget. +## Type: QssColor +# c.colors.keyhint.bg = 'rgba(0, 0, 0, 80%)' + +## Text color for the keyhint widget. +## Type: QssColor +# c.colors.keyhint.fg = '#FFFFFF' + +## Highlight color for keys to complete the current keychain. +## Type: QssColor +# c.colors.keyhint.suffix.fg = '#FFFF00' + +## Background color of an error message. +## Type: QssColor +# c.colors.messages.error.bg = 'red' + +## Border color of an error message. +## Type: QssColor +# c.colors.messages.error.border = '#bb0000' + +## Foreground color of an error message. +## Type: QssColor +# c.colors.messages.error.fg = 'white' + +## Background color of an info message. +## Type: QssColor +# c.colors.messages.info.bg = 'black' + +## Border color of an info message. +## Type: QssColor +# c.colors.messages.info.border = '#333333' + +## Foreground color of an info message. +## Type: QssColor +# c.colors.messages.info.fg = 'white' + +## Background color of a warning message. +## Type: QssColor +# c.colors.messages.warning.bg = 'darkorange' + +## Border color of a warning message. +## Type: QssColor +# c.colors.messages.warning.border = '#d47300' + +## Foreground color of a warning message. +## Type: QssColor +# c.colors.messages.warning.fg = 'white' + +## Background color for prompts. +## Type: QssColor +# c.colors.prompts.bg = '#444444' + +## Border used around UI elements in prompts. +## Type: String +# c.colors.prompts.border = '1px solid gray' + +## Foreground color for prompts. +## Type: QssColor +# c.colors.prompts.fg = 'white' + +## Background color for the selected item in filename prompts. +## Type: QssColor +# c.colors.prompts.selected.bg = 'grey' + +## Background color of the statusbar in caret mode. +## Type: QssColor +# c.colors.statusbar.caret.bg = 'purple' + +## Foreground color of the statusbar in caret mode. +## Type: QssColor +# c.colors.statusbar.caret.fg = 'white' + +## Background color of the statusbar in caret mode with a selection. +## Type: QssColor +# c.colors.statusbar.caret.selection.bg = '#a12dff' + +## Foreground color of the statusbar in caret mode with a selection. +## Type: QssColor +# c.colors.statusbar.caret.selection.fg = 'white' + +## Background color of the statusbar in command mode. +## Type: QssColor +# c.colors.statusbar.command.bg = 'black' + +## Foreground color of the statusbar in command mode. +## Type: QssColor +# c.colors.statusbar.command.fg = 'white' + +## Background color of the statusbar in private browsing + command mode. +## Type: QssColor +# c.colors.statusbar.command.private.bg = 'grey' + +## Foreground color of the statusbar in private browsing + command mode. +## Type: QssColor +# c.colors.statusbar.command.private.fg = 'white' + +## Background color of the statusbar in insert mode. +## Type: QssColor +# c.colors.statusbar.insert.bg = 'darkgreen' + +## Foreground color of the statusbar in insert mode. +## Type: QssColor +# c.colors.statusbar.insert.fg = 'white' + +## Background color of the statusbar. +## Type: QssColor +# c.colors.statusbar.normal.bg = 'black' + +## Foreground color of the statusbar. +## Type: QssColor +# c.colors.statusbar.normal.fg = 'white' + +## Background color of the statusbar in passthrough mode. +## Type: QssColor +# c.colors.statusbar.passthrough.bg = 'darkblue' + +## Foreground color of the statusbar in passthrough mode. +## Type: QssColor +# c.colors.statusbar.passthrough.fg = 'white' + +## Background color of the statusbar in private browsing mode. +## Type: QssColor +# c.colors.statusbar.private.bg = '#666666' + +## Foreground color of the statusbar in private browsing mode. +## Type: QssColor +# c.colors.statusbar.private.fg = 'white' + +## Background color of the progress bar. +## Type: QssColor +# c.colors.statusbar.progress.bg = 'white' + +## Foreground color of the URL in the statusbar on error. +## Type: QssColor +# c.colors.statusbar.url.error.fg = 'orange' + +## Default foreground color of the URL in the statusbar. +## Type: QssColor +# c.colors.statusbar.url.fg = 'white' + +## Foreground color of the URL in the statusbar for hovered links. +## Type: QssColor +# c.colors.statusbar.url.hover.fg = 'aqua' + +## Foreground color of the URL in the statusbar on successful load +## (http). +## Type: QssColor +# c.colors.statusbar.url.success.http.fg = 'white' + +## Foreground color of the URL in the statusbar on successful load +## (https). +## Type: QssColor +# c.colors.statusbar.url.success.https.fg = 'lime' + +## Foreground color of the URL in the statusbar when there's a warning. +## Type: QssColor +# c.colors.statusbar.url.warn.fg = 'yellow' + +## Background color of the tab bar. +## Type: QtColor +# c.colors.tabs.bar.bg = '#555555' + +## Background color of unselected even tabs. +## Type: QtColor +# c.colors.tabs.even.bg = 'darkgrey' + +## Foreground color of unselected even tabs. +## Type: QtColor +# c.colors.tabs.even.fg = 'white' + +## Color for the tab indicator on errors. +## Type: QtColor +# c.colors.tabs.indicator.error = '#ff0000' + +## Color gradient start for the tab indicator. +## Type: QtColor +# c.colors.tabs.indicator.start = '#0000aa' + +## Color gradient end for the tab indicator. +## Type: QtColor +# c.colors.tabs.indicator.stop = '#00aa00' + +## Color gradient interpolation system for the tab indicator. +## Type: ColorSystem +## Valid values: +## - rgb: Interpolate in the RGB color system. +## - hsv: Interpolate in the HSV color system. +## - hsl: Interpolate in the HSL color system. +## - none: Don't show a gradient. +# c.colors.tabs.indicator.system = 'rgb' + +## Background color of unselected odd tabs. +## Type: QtColor +# c.colors.tabs.odd.bg = 'grey' + +## Foreground color of unselected odd tabs. +## Type: QtColor +# c.colors.tabs.odd.fg = 'white' + +## Background color of selected even tabs. +## Type: QtColor +# c.colors.tabs.selected.even.bg = 'black' + +## Foreground color of selected even tabs. +## Type: QtColor +# c.colors.tabs.selected.even.fg = 'white' + +## Background color of selected odd tabs. +## Type: QtColor +# c.colors.tabs.selected.odd.bg = 'black' + +## Foreground color of selected odd tabs. +## Type: QtColor +# c.colors.tabs.selected.odd.fg = 'white' + +## Background color for webpages if unset (or empty to use the theme's +## color). +## Type: QtColor +# c.colors.webpage.bg = 'white' + +## Number of commands to save in the command history. 0: no history / -1: +## unlimited +## Type: Int +# c.completion.cmd_history_max_items = 100 + +## Delay (in milliseconds) before updating completions after typing a +## character. +## Type: Int +# c.completion.delay = 0 + +## Height (in pixels or as percentage of the window) of the completion. +## Type: PercOrInt +# c.completion.height = '50%' + +## Minimum amount of characters needed to update completions. +## Type: Int +# c.completion.min_chars = 1 + +## Move on to the next part when there's only one possible completion +## left. +## Type: Bool +# c.completion.quick = True + +## Padding (in pixels) of the scrollbar handle in the completion window. +## Type: Int +# c.completion.scrollbar.padding = 2 + +## Width (in pixels) of the scrollbar in the completion window. +## Type: Int +# c.completion.scrollbar.width = 12 + +## When to show the autocompletion window. +## Type: String +## Valid values: +## - always: Whenever a completion is available. +## - auto: Whenever a completion is requested. +## - never: Never. +# c.completion.show = 'always' + +## Shrink the completion to be smaller than the configured size if there +## are no scrollbars. +## Type: Bool +# c.completion.shrink = False + +## Format of timestamps (e.g. for the history completion). +## Type: TimestampTemplate +# c.completion.timestamp_format = '%Y-%m-%d' + +## Execute the best-matching command on a partial match. +## Type: Bool +c.completion.use_best_match = True + +## Number of URLs to show in the web history. 0: no history / -1: +## unlimited +## Type: Int +# c.completion.web_history_max_items = -1 + +## Require a confirmation before quitting the application. +## Type: ConfirmQuit +## Valid values: +## - always: Always show a confirmation. +## - multiple-tabs: Show a confirmation if multiple tabs are opened. +## - downloads: Show a confirmation if downloads are running +## - never: Never show a confirmation. +# c.confirm_quit = ['never'] + +## Enable support for the HTML 5 web application cache feature. An +## application cache acts like an HTTP cache in some sense. For documents +## that use the application cache via JavaScript, the loader engine will +## first ask the application cache for the contents, before hitting the +## network. +## Type: Bool +# c.content.cache.appcache = True + +## Maximum number of pages to hold in the global memory page cache. The +## page cache allows for a nicer user experience when navigating forth or +## back to pages in the forward/back history, by pausing and resuming up +## to _n_ pages. For more information about the feature, please refer to: +## http://webkit.org/blog/427/webkit-page-cache-i-the-basics/ +## Type: Int +# c.content.cache.maximum_pages = 0 + +## Size (in bytes) of the HTTP network cache. Null to use the default +## value. With QtWebEngine, the maximum supported value is 2147483647 (~2 +## GB). +## Type: Int +# c.content.cache.size = None + +## Which cookies to accept. +## Type: String +## Valid values: +## - all: Accept all cookies. +## - no-3rdparty: Accept cookies from the same origin only. +## - no-unknown-3rdparty: Accept cookies from the same origin only, unless a cookie is already set for the domain. +## - never: Don't accept cookies at all. +# c.content.cookies.accept = 'no-3rdparty' + +## Store cookies. Note this option needs a restart with QtWebEngine on Qt +## < 5.9. +## Type: Bool +c.content.cookies.store = True + +## Default encoding to use for websites. The encoding must be a string +## describing an encoding such as _utf-8_, _iso-8859-1_, etc. +## Type: String +# c.content.default_encoding = 'iso-8859-1' + +## Enable extra tools for Web developers. This needs to be enabled for +## `:inspector` to work and also adds an _Inspect_ entry to the context +## menu. For QtWebEngine, see `--enable-webengine-inspector` in +## `qutebrowser --help` instead. +## Type: Bool +## c.content.developer_extras = True + +## Try to pre-fetch DNS entries to speed up browsing. +## Type: Bool +c.content.dns_prefetch = True + +## Expand each subframe to its contents. This will flatten all the frames +## to become one scrollable page. +## Type: Bool +# c.content.frame_flattening = False + +## Allow websites to request geolocations. +## Type: BoolAsk +## Valid values: +## - true +## - false +## - ask +c.content.geolocation = False + +## Value to send in the `Accept-Language` header. +## Type: String +# c.content.headers.accept_language = 'en-US,en' + +## Custom headers for qutebrowser HTTP requests. +## Type: Dict +# c.content.headers.custom = {} + +## Value to send in the `DNT` header. When this is set to true, +## qutebrowser asks websites to not track your identity. If set to null, +## the DNT header is not sent at all. +## Type: Bool +# c.content.headers.do_not_track = True + +## When to send the Referer header. The Referer header tells websites +## from which website you were coming from when visiting them. +## Type: String +## Valid values: +## - always: Always send the Referer. +## - never: Never send the Referer. This is not recommended, as some sites may break. +## - same-domain: Only send the Referer for the same domain. This will still protect your privacy, but shouldn't break any sites. +# c.content.headers.referer = 'same-domain' + +## User agent to send. Unset to send the default. +## Type: String +# c.content.headers.user_agent = None + +## Enable host blocking. +## Type: Bool +# c.content.host_blocking.enabled = True + +## List of URLs of lists which contain hosts to block. The file can be +## in one of the following formats: - An `/etc/hosts`-like file - One +## host per line - A zip-file of any of the above, with either only one +## file, or a file named `hosts` (with any extension). +## Type: List of Url +# c.content.host_blocking.lists = ['https://www.malwaredomainlist.com/hostslist/hosts.txt', 'http://someonewhocares.org/hosts/hosts', 'http://winhelp2002.mvps.org/hosts.zip', 'http://malwaredomains.lehigh.edu/files/justdomains.zip', 'https://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&mimetype=plaintext'] + +## List of domains that should always be loaded, despite being ad- +## blocked. Domains may contain * and ? wildcards and are otherwise +## required to exactly match the requested domain. Local domains are +## always exempt from hostblocking. +## Type: List of String +c.content.host_blocking.whitelist = ['piwik.org','next-episode.net','churchmilitant.com','churchmilitant.tv'] + +## Enable hyperlink auditing (`<a ping>`). +## Type: Bool +# c.content.hyperlink_auditing = False + +## Load images automatically in web pages. +## Type: Bool +# c.content.images = True + +## Show javascript alerts. +## Type: Bool +# c.content.javascript.alert = True + +## Allow JavaScript to read from or write to the clipboard. With +## QtWebEngine, writing the clipboard as response to a user interaction +## is always allowed. +## Type: Bool +# c.content.javascript.can_access_clipboard = False + +## Allow JavaScript to close tabs. +## Type: Bool +# c.content.javascript.can_close_tabs = False + +## Allow JavaScript to open new tabs without user interaction. +## Type: Bool +# c.content.javascript.can_open_tabs_automatically = False + +## Enable JavaScript. +## Type: Bool +c.content.javascript.enabled = True + +## Log levels to use for JavaScript console logging messages. When a +## JavaScript message with the level given in the dictionary key is +## logged, the corresponding dictionary value selects the qutebrowser +## logger to use. On QtWebKit, the "unknown" setting is always used. +## Type: Dict +# c.content.javascript.log = {'unknown': 'debug', 'info': 'debug', 'warning': 'debug', 'error': 'debug'} + +## Use the standard JavaScript modal dialog for `alert()` and +## `confirm()`. +## Type: Bool +# c.content.javascript.modal_dialog = False + +## Show javascript prompts. +## Type: Bool +# c.content.javascript.prompt = True + +## Allow locally loaded documents to access other local URLs. +## Type: Bool +# c.content.local_content_can_access_file_urls = True + +## Allow locally loaded documents to access remote URLs. +## Type: Bool +# c.content.local_content_can_access_remote_urls = False + +## Enable support for HTML 5 local storage and Web SQL. +## Type: Bool +# c.content.local_storage = True + +## Allow websites to record audio/video. +## Type: BoolAsk +## Valid values: +## - true +## - false +## - ask +# c.content.media_capture = 'ask' + +## Netrc-file for HTTP authentication. If unset, `~/.netrc` is used. +## Type: File +# c.content.netrc_file = None + +## Allow websites to show notifications. +## Type: BoolAsk +## Valid values: +## - true +## - false +## - ask +# c.content.notifications = 'ask' + +## Allow pdf.js to view PDF files in the browser. Note that the files can +## still be downloaded by clicking the download button in the pdf.js +## viewer. +## Type: Bool +# c.content.pdfjs = False + +## Enable plugins in Web pages. +## Type: Bool +# c.content.plugins = False vgg +c.content.plugins = True + +## Draw the background color and images also when the page is printed. +## Type: Bool +# c.content.print_element_backgrounds = True + +## Open new windows in private browsing mode which does not record +## visited pages. +## Type: Bool +# c.content.private_browsing = True + +## Proxy to use. In addition to the listed values, you can use a +## `socks://...` or `http://...` URL. +## Type: Proxy +## Valid values: +## - system: Use the system wide proxy. +## - none: Don't use any proxy +# c.content.proxy = 'system' + +## Send DNS requests over the configured proxy. +## Type: Bool +# c.content.proxy_dns_requests = True + +## Validate SSL handshakes. +## Type: BoolAsk +## Valid values: +## - true +## - false +## - ask +# c.content.ssl_strict = 'ask' + +## List of user stylesheet filenames to use. +## Type: List of File, or File +# c.content.user_stylesheets = [] + +## Enable WebGL. +## Type: Bool +# c.content.webgl = True + +## Limit fullscreen to the browser window (does not expand to fill the +## screen). +## Type: Bool +# c.content.windowed_fullscreen = False + +## Monitor load requests for cross-site scripting attempts. Suspicious +## scripts will be blocked and reported in the inspector's JavaScript +## console. Enabling this feature might have an impact on performance. +## Type: Bool +# c.content.xss_auditing = False + +## Directory to save downloads to. If unset, a sensible OS-specific +## default is used. +## Type: Directory +# c.downloads.location.directory = None + +## Prompt the user for the download location. If set to false, +## `downloads.location.directory` will be used. +## Type: Bool +# c.downloads.location.prompt = True + +## Remember the last used download directory. +## Type: Bool +# c.downloads.location.remember = True + +## What to display in the download filename input. +## Type: String +## Valid values: +## - path: Show only the download path. +## - filename: Show only download filename. +## - both: Show download path and filename. +# c.downloads.location.suggestion = 'path' + +## Default program used to open downloads. If null, the default internal +## handler is used. Any `{}` in the string will be expanded to the +## filename, else the filename will be appended. +## Type: String +# c.downloads.open_dispatcher = None + +## Where to show the downloaded files. +## Type: VerticalPosition +## Valid values: +## - top +## - bottom +# c.downloads.position = 'top' + +## Duration (in milliseconds) to wait before removing finished downloads. +## If set to -1, downloads are never removed. +## Type: Int +# c.downloads.remove_finished = -1 + +## Editor (and arguments) to use for the `open-editor` command. The +## following placeholders are defined: * `{file}`: Filename of the file +## to be edited. * `{line}`: Line in which the caret is found in the +## text. * `{column}`: Column in which the caret is found in the text. * +## `{line0}`: Same as `{line}`, but starting from index 0. * `{column0}`: +## Same as `{column}`, but starting from index 0. +## Type: ShellCommand +# c.editor.command = ['gvim', '-f', '{file}', '-c', 'normal {line}G{column0}l'] +c.editor.command = ["st", "-t", "qutebrowserSratchpad", "-g", "86x24+40+60", "-e", "nvim", "-f", "{}"] + +## Encoding to use for the editor. +## Type: Encoding +# c.editor.encoding = 'utf-8' + +## Font used in the completion categories. +## Type: Font +# c.fonts.completion.category = 'bold 10pt monospace' + +## Font used in the completion widget. +## Type: Font +# c.fonts.completion.entry = '10pt monospace' + +## Font used for the debugging console. +## Type: QtFont +# c.fonts.debug_console = '10pt monospace' + +## Font used for the downloadbar. +## Type: Font +# c.fonts.downloads = '10pt monospace' + +## Font used for the hints. +## Type: Font +c.fonts.hints = 'bold 8pt monospace' + +## Font used in the keyhint widget. +## Type: Font +# c.fonts.keyhint = '10pt monospace' + +## Font used for error messages. +## Type: Font +# c.fonts.messages.error = '10pt monospace' + +## Font used for info messages. +## Type: Font +# c.fonts.messages.info = '10pt monospace' + +## Font used for warning messages. +## Type: Font +# c.fonts.messages.warning = '10pt monospace' + +## Default monospace fonts. Whenever "monospace" is used in a font +## setting, it's replaced with the fonts listed here. +## Type: Font +# c.fonts.monospace = '"xos4 Terminus", Terminus, Monospace, "DejaVu Sans Mono", Monaco, "Bitstream Vera Sans Mono", "Andale Mono", "Courier New", Courier, "Liberation Mono", monospace, Fixed, Consolas, Terminal' + +## Font used for prompts. +## Type: Font +# c.fonts.prompts = '10pt sans-serif' + +## Font used in the statusbar. +## Type: Font +# c.fonts.statusbar = '10pt monospace' + +## Font used in the tab bar. +## Type: QtFont +# c.fonts.tabs = '10pt monospace' + +## Font family for cursive fonts. +## Type: FontFamily +# c.fonts.web.family.cursive = '' + +## Font family for fantasy fonts. +## Type: FontFamily +# c.fonts.web.family.fantasy = '' + +## Font family for fixed fonts. +## Type: FontFamily +# c.fonts.web.family.fixed = '' + +## Font family for sans-serif fonts. +## Type: FontFamily +# c.fonts.web.family.sans_serif = '' + +## Font family for serif fonts. +## Type: FontFamily +# c.fonts.web.family.serif = '' + +## Font family for standard fonts. +## Type: FontFamily +# c.fonts.web.family.standard = '' + +## Default font size (in pixels) for regular text. +## Type: Int +# c.fonts.web.size.default = 16 + +## Default font size (in pixels) for fixed-pitch text. +## Type: Int +# c.fonts.web.size.default_fixed = 13 + +## Hard minimum font size (in pixels). +## Type: Int +# c.fonts.web.size.minimum = 0 + +## Minimum logical font size (in pixels) that is applied when zooming +## out. +## Type: Int +# c.fonts.web.size.minimum_logical = 6 + +## When a hint can be automatically followed without pressing Enter. +## Type: String +## Valid values: +## - always: Auto-follow whenever there is only a single hint on a page. +## - unique-match: Auto-follow whenever there is a unique non-empty match in either the hint string (word mode) or filter (number mode). +## - full-match: Follow the hint when the user typed the whole hint (letter, word or number mode) or the element's text (only in number mode). +## - never: The user will always need to press Enter to follow a hint. +# c.hints.auto_follow = 'unique-match' + +## Duration (in milliseconds) to ignore normal-mode key bindings after a +## successful auto-follow. +## Type: Int +# c.hints.auto_follow_timeout = 0 + +## CSS border value for hints. +## Type: String +# c.hints.border = '1px solid #E3BE23' + +## Characters used for hint strings. +## Type: UniqueCharString +# c.hints.chars = 'asdfghjkl' + +## Dictionary file to be used by the word hints. +## Type: File +# c.hints.dictionary = '/usr/share/dict/words' + +## Which implementation to use to find elements to hint. +## Type: String +## Valid values: +## - javascript: Better but slower +## - python: Slightly worse but faster +# c.hints.find_implementation = 'python' + +## Hide unmatched hints in rapid mode. +## Type: Bool +# c.hints.hide_unmatched_rapid_hints = True + +## Minimum number of characters used for hint strings. +## Type: Int +# c.hints.min_chars = 1 + +## Mode to use for hints. +## Type: String +## Valid values: +## - number: Use numeric hints. (In this mode you can also type letters from the hinted element to filter and reduce the number of elements that are hinted.) +## - letter: Use the characters in the `hints.chars` setting. +## - word: Use hints words based on the html elements and the extra words. +c.hints.mode = 'number' + +## Comma-separated list of regular expressions to use for 'next' links. +## Type: List of Regex +# c.hints.next_regexes = ['\\bnext\\b', '\\bmore\\b', '\\bnewer\\b', '\\b[>→≫]\\b', '\\b(>>|»)\\b', '\\bcontinue\\b'] + +## Comma-separated list of regular expressions to use for 'prev' links. +## Type: List of Regex +# c.hints.prev_regexes = ['\\bprev(ious)?\\b', '\\bback\\b', '\\bolder\\b', '\\b[<←≪]\\b', '\\b(<<|«)\\b'] + +## Scatter hint key chains (like Vimium) or not (like dwb). Ignored for +## number hints. +## Type: Bool +# c.hints.scatter = True + +## Make characters in hint strings uppercase. +## Type: Bool +# c.hints.uppercase = False + +## Maximum time (in minutes) between two history items for them to be +## considered being from the same browsing session. Items with less time +## between them are grouped when being displayed in `:history`. Use -1 to +## disable separation. +## Type: Int +# c.history_gap_interval = 30 + +## Which unbound keys to forward to the webview in normal mode. +## Type: String +## Valid values: +## - all: Forward all unbound keys. +## - auto: Forward unbound non-alphanumeric keys. +## - none: Don't forward any keys. +# c.input.forward_unbound_keys = 'auto' + +## Enter insert mode if an editable element is clicked. +## Type: Bool +# c.input.insert_mode.auto_enter = True + +## Leave insert mode if a non-editable element is clicked. +## Type: Bool +# c.input.insert_mode.auto_leave = True + +## Automatically enter insert mode if an editable element is focused +## after loading the page. +## Type: Bool +# c.input.insert_mode.auto_load = False + +## Switch to insert mode when clicking flash and other plugins. +## Type: Bool +# c.input.insert_mode.plugins = False + +## Include hyperlinks in the keyboard focus chain when tabbing. +## Type: Bool +# c.input.links_included_in_focus_chain = True + +## Timeout (in milliseconds) for partially typed key bindings. If the +## current input forms only partial matches, the keystring will be +## cleared after this time. +## Type: Int +# c.input.partial_timeout = 5000 + +## Enable Opera-like mouse rocker gestures. This disables the context +## menu. +## Type: Bool +# c.input.rocker_gestures = False + +## Enable spatial navigation. Spatial navigation consists in the ability +## to navigate between focusable elements in a Web page, such as +## hyperlinks and form controls, by using Left, Right, Up and Down arrow +## keys. For example, if the user presses the Right key, heuristics +## determine whether there is an element he might be trying to reach +## towards the right and which element he probably wants. +## Type: Bool +# c.input.spatial_navigation = False + +## Keychains that shouldn't be shown in the keyhint dialog. Globs are +## supported, so `;*` will blacklist all keychains starting with `;`. Use +## `*` to disable keyhints. +## Type: List of String +# c.keyhint.blacklist = [] + +## Time (in milliseconds) from pressing a key to seeing the keyhint +## dialog. +## Type: Int +# c.keyhint.delay = 500 + +## Rounding radius (in pixels) for the edges of the keyhint dialog. +## Type: Int +# c.keyhint.radius = 6 + +## Duration (in milliseconds) to show messages in the statusbar for. Set +## to 0 to never clear messages. +## Type: Int +# c.messages.timeout = 2000 + +## How to open links in an existing instance if a new one is launched. +## This happens when e.g. opening a link from a terminal. See +## `new_instance_open_target_window` to customize in which window the +## link is opened in. +## Type: String +## Valid values: +## - tab: Open a new tab in the existing window and activate the window. +## - tab-bg: Open a new background tab in the existing window and activate the window. +## - tab-silent: Open a new tab in the existing window without activating the window. +## - tab-bg-silent: Open a new background tab in the existing window without activating the window. +## - window: Open in a new window. +# c.new_instance_open_target = 'tab' + +## Which window to choose when opening links as new tabs. When +## `new_instance_open_target` is not set to `window`, this is ignored. +## Type: String +## Valid values: +## - first-opened: Open new tabs in the first (oldest) opened window. +## - last-opened: Open new tabs in the last (newest) opened window. +## - last-focused: Open new tabs in the most recently focused window. +## - last-visible: Open new tabs in the most recently visible window. +# c.new_instance_open_target_window = 'last-focused' + +## Show a filebrowser in upload/download prompts. +## Type: Bool +# c.prompt.filebrowser = True + +## Rounding radius (in pixels) for the edges of prompts. +## Type: Int +# c.prompt.radius = 8 + +## Additional arguments to pass to Qt, without leading `--`. With +## QtWebEngine, some Chromium arguments (see +## https://peter.sh/experiments/chromium-command-line-switches/ for a +## list) will work. +## Type: List of String +# c.qt.args = [] + +## Force a Qt platform to use. This sets the `QT_QPA_PLATFORM` +## environment variable and is useful to force using the XCB plugin when +## running QtWebEngine on Wayland. +## Type: String +# c.qt.force_platform = None + +## Force software rendering for QtWebEngine. This is needed for +## QtWebEngine to work with Nouveau drivers. +## Type: Bool +# c.qt.force_software_rendering = False + +## Turn on Qt HighDPI scaling. This is equivalent to setting +## QT_AUTO_SCREEN_SCALE_FACTOR=1 in the environment. It's off by default +## as it can cause issues with some bitmap fonts. As an alternative to +## this, it's possible to set font sizes and the `zoom.default` setting. +## Type: Bool +# c.qt.highdpi = False + +## Show a scrollbar. +## Type: Bool +# c.scrolling.bar = False + +## Enable smooth scrolling for web pages. Note smooth scrolling does not +## work with the `:scroll-px` command. +## Type: Bool +# c.scrolling.smooth = False + +## When to find text on a page case-insensitively. +## Type: String +## Valid values: +## - always: Search case-insensitively. +## - never: Search case-sensitively. +## - smart: Search case-sensitively if there are capital characters. +# c.search.ignore_case = 'smart' + +## Find text on a page incrementally, renewing the search for each typed +## character. +## Type: Bool +# c.search.incremental = True + +## Name of the session to save by default. If this is set to null, the +## session which was last loaded is saved. +## Type: SessionName +# c.session.default_name = None + +## Load a restored tab as soon as it takes focus. +## Type: Bool +# c.session.lazy_restore = False + +## Languages to use for spell checking. You can check for available +## languages and install dictionaries using scripts/dictcli.py. Run the +## script with -h/--help for instructions. +## Type: List of String +## Valid values: +## - af-ZA: Afrikaans (South Africa) +## - bg-BG: Bulgarian (Bulgaria) +## - ca-ES: Catalan (Spain) +## - cs-CZ: Czech (Czech Republic) +## - da-DK: Danish (Denmark) +## - de-DE: German (Germany) +## - el-GR: Greek (Greece) +## - en-AU: English (Australia) +## - en-CA: English (Canada) +## - en-GB: English (United Kingdom) +## - en-US: English (United States) +## - es-ES: Spanish (Spain) +## - et-EE: Estonian (Estonia) +## - fa-IR: Farsi (Iran) +## - fo-FO: Faroese (Faroe Islands) +## - fr-FR: French (France) +## - he-IL: Hebrew (Israel) +## - hi-IN: Hindi (India) +## - hr-HR: Croatian (Croatia) +## - hu-HU: Hungarian (Hungary) +## - id-ID: Indonesian (Indonesia) +## - it-IT: Italian (Italy) +## - ko: Korean +## - lt-LT: Lithuanian (Lithuania) +## - lv-LV: Latvian (Latvia) +## - nb-NO: Norwegian (Norway) +## - nl-NL: Dutch (Netherlands) +## - pl-PL: Polish (Poland) +## - pt-BR: Portuguese (Brazil) +## - pt-PT: Portuguese (Portugal) +## - ro-RO: Romanian (Romania) +## - ru-RU: Russian (Russia) +## - sh: Serbo-Croatian +## - sk-SK: Slovak (Slovakia) +## - sl-SI: Slovenian (Slovenia) +## - sq: Albanian +## - sr: Serbian +## - sv-SE: Swedish (Sweden) +## - ta-IN: Tamil (India) +## - tg-TG: Tajik (Tajikistan) +## - tr-TR: Turkish (Turkey) +## - uk-UA: Ukrainian (Ukraine) +## - vi-VN: Vietnamese (Viet Nam) +# c.spellcheck.languages = [] + +## Hide the statusbar unless a message is shown. +## Type: Bool +# c.statusbar.hide = False + +## Padding (in pixels) for the statusbar. +## Type: Padding +# c.statusbar.padding = {'top': 1, 'bottom': 1, 'left': 0, 'right': 0} + +## Position of the status bar. +## Type: VerticalPosition +## Valid values: +## - top +## - bottom +# c.statusbar.position = 'bottom' + +## List of widgets displayed in the statusbar. +## Type: List of String +## Valid values: +## - url: Current page URL. +## - scroll: Percentage of the current page position like `10%`. +## - scroll_raw: Raw percentage of the current page position like `10`. +## - history: Display an arrow when possible to go back/forward in history. +## - tabs: Current active tab, e.g. `2`. +## - keypress: Display pressed keys when composing a vi command. +## - progress: Progress bar for the current page loading. +# c.statusbar.widgets = ['keypress', 'url', 'scroll', 'history', 'tabs', 'progress'] + +## Open new tabs (middleclick/ctrl+click) in the background. +## Type: Bool +# c.tabs.background = False + +## Mouse button with which to close tabs. +## Type: String +## Valid values: +## - right: Close tabs on right-click. +## - middle: Close tabs on middle-click. +## - none: Don't close tabs using the mouse. +# c.tabs.close_mouse_button = 'middle' + +## How to behave when the close mouse button is pressed on the tab bar. +## Type: String +## Valid values: +## - new-tab: Open a new tab. +## - close-current: Close the current tab. +## - close-last: Close the last tab. +## - ignore: Don't do anything. +# c.tabs.close_mouse_button_on_bar = 'new-tab' + +## Scaling factor for favicons in the tab bar. The tab size is unchanged, +## so big favicons also require extra `tabs.padding`. +## Type: Float +# c.tabs.favicons.scale = 1.0 + +## Show favicons in the tab bar. +## Type: Bool +# c.tabs.favicons.show = True + +## Padding (in pixels) for tab indicators. +## Type: Padding +# c.tabs.indicator.padding = {'top': 2, 'bottom': 2, 'left': 0, 'right': 4} + +## Width (in pixels) of the progress indicator (0 to disable). +## Type: Int +# c.tabs.indicator.width = 3 + +## How to behave when the last tab is closed. +## Type: String +## Valid values: +## - ignore: Don't do anything. +## - blank: Load a blank page. +## - startpage: Load the start page. +## - default-page: Load the default page. +## - close: Close the window. +# c.tabs.last_close = 'ignore' + +## When switching tabs, what input mode is applied. +## Type: String +## Valid values: +## - persist: Retain the current mode. +## - restore: Restore previously saved mode. +## - normal: Always revert to normal mode. +# c.tabs.mode_on_change = 'normal' + +## Switch between tabs using the mouse wheel. +## Type: Bool +# c.tabs.mousewheel_switching = True + +## Position of new tabs opened from another tab. +## Type: NewTabPosition +## Valid values: +## - prev: Before the current tab. +## - next: After the current tab. +## - first: At the beginning. +## - last: At the end. +# c.tabs.new_position.related = 'next' + +## Position of new tabs which aren't opened from another tab. +## Type: NewTabPosition +## Valid values: +## - prev: Before the current tab. +## - next: After the current tab. +## - first: At the beginning. +## - last: At the end. +# c.tabs.new_position.unrelated = 'last' + +## Padding (in pixels) around text for tabs. +## Type: Padding +# c.tabs.padding = {'top': 0, 'bottom': 0, 'left': 5, 'right': 5} + +## Shrink pinned tabs down to their contents. +## Type: Bool +# c.tabs.pinned.shrink = True + +## Position of the tab bar. +## Type: Position +## Valid values: +## - top +## - bottom +## - left +## - right +# c.tabs.position = 'top' + +## Which tab to select when the focused tab is removed. +## Type: SelectOnRemove +## Valid values: +## - prev: Select the tab which came before the closed one (left in horizontal, above in vertical). +## - next: Select the tab which came after the closed one (right in horizontal, below in vertical). +## - last-used: Select the previously selected tab. +# c.tabs.select_on_remove = 'next' + +## When to show the tab bar. +## Type: String +## Valid values: +## - always: Always show the tab bar. +## - never: Always hide the tab bar. +## - multiple: Hide the tab bar if only one tab is open. +## - switching: Show the tab bar when switching tabs. +# c.tabs.show = 'always' + +## Duration (in milliseconds) to show the tab bar before hiding it when +## tabs.show is set to 'switching'. +## Type: Int +# c.tabs.show_switching_delay = 800 + +## Open a new window for every tab. +## Type: Bool +# c.tabs.tabs_are_windows = False + +## Alignment of the text inside of tabs. +## Type: TextAlignment +## Valid values: +## - left +## - right +## - center +# c.tabs.title.alignment = 'left' + +## Format to use for the tab title. The following placeholders are +## defined: * `{perc}`: Percentage as a string like `[10%]`. * +## `{perc_raw}`: Raw percentage, e.g. `10`. * `{title}`: Title of the +## current web page. * `{title_sep}`: The string ` - ` if a title is set, +## empty otherwise. * `{index}`: Index of this tab. * `{id}`: Internal +## tab ID of this tab. * `{scroll_pos}`: Page scroll position. * +## `{host}`: Host of the current web page. * `{backend}`: Either +## ''webkit'' or ''webengine'' * `{private}`: Indicates when private mode +## is enabled. * `{current_url}`: URL of the current web page. * +## `{protocol}`: Protocol (http/https/...) of the current web page. +## Type: FormatString +# c.tabs.title.format = '{index}: {title}' + +## Format to use for the tab title for pinned tabs. The same placeholders +## like for `tabs.title.format` are defined. +## Type: FormatString +# c.tabs.title.format_pinned = '{index}' + +## Width (in pixels or as percentage of the window) of the tab bar if +## it's vertical. +## Type: PercOrInt +# c.tabs.width = '20%' + +## Wrap when changing tabs. +## Type: Bool +# c.tabs.wrap = True + +## What search to start when something else than a URL is entered. +## Type: String +## Valid values: +## - naive: Use simple/naive check. +## - dns: Use DNS requests (might be slow!). +## - never: Never search automatically. +# c.url.auto_search = 'naive' + +## Page to open if :open -t/-b/-w is used without URL. Use `about:blank` +## for a blank page. +## Type: FuzzyUrl +# c.url.default_page = 'https://start.duckduckgo.com/' + +## URL segments where `:navigate increment/decrement` will search for a +## number. +## Type: FlagList +## Valid values: +## - host +## - port +## - path +## - query +## - anchor +# c.url.incdec_segments = ['path', 'query'] + +## Search engines which can be used via the address bar. Maps a search +## engine name (such as `DEFAULT`, or `ddg`) to a URL with a `{}` +## placeholder. The placeholder will be replaced by the search term, use +## `{{` and `}}` for literal `{`/`}` signs. The search engine named +## `DEFAULT` is used when `url.auto_search` is turned on and something +## else than a URL was entered to be opened. Other search engines can be +## used by prepending the search engine name to the search term, e.g. +## `:open google qutebrowser`. +## Type: Dict + +c.url.searchengines = { 'DEFAULT':'https://duckduckgo.com/?q={}', + 'aio':'https://www.aiosearch.com/search?q={}', + 'aw':'https://wiki.archlinux.org/?q={}', + 'g':'https://www.google.com/?q={}', + 'git':'https://github.com/search?q={}', + 'imdb':'https://www.imdb.com/find?q={}', + 'red':'https://www.reddit.com/r/{}', + 'ub':'https://www.urbandictionary.com/define.php?term={}', + 'wiki':'https://en.wikipedia.org/wiki/?q={}', + 'yfs':'https://www.yifysubtitles.com/search?q={}', + 'yt':'https://www.youtube.com/results?search_query={}'} + +## Page(s) to open at the start. +## Type: List of FuzzyUrl, or FuzzyUrl +# c.url.start_pages = ['https://start.duckduckgo.com'] + +## URL parameters to strip with `:yank url`. +## Type: List of String +# c.url.yank_ignored_parameters = ['ref', 'utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content'] + +## Hide the window decoration when using wayland. +## Type: Bool +# c.window.hide_wayland_decoration = False + +## Format to use for the window title. The same placeholders like for +## `tabs.title.format` are defined. +## Type: FormatString +# c.window.title_format = '{perc}{title}{title_sep}qutebrowser' + +## Default zoom level. +## Type: Perc +# c.zoom.default = '100%' + +## Available zoom levels. +## Type: List of Perc +# c.zoom.levels = ['25%', '33%', '50%', '67%', '75%', '90%', '100%', '110%', '125%', '150%', '175%', '200%', '250%', '300%', '400%', '500%'] + +## Number of zoom increments to divide the mouse wheel movements to. +## Type: Int +# c.zoom.mouse_divider = 512 + +## Apply the zoom factor on a frame only to the text or to all content. +## Type: Bool +# c.zoom.text_only = False + +## Bindings for normal mode +config.bind(',c', 'hint links spawn mpv --ytdl --ytdl-raw-options=username="v.graffagnino@btinternet.com",password="N9\Ch0n\H@n9" --cookies-file=~/.local/share/qutebrowser/webengine/Cookies {hint-url} --load-unsafe-playlists') +config.bind(',m', 'hint links spawn mpv --ytdl {hint-url} --load-unsafe-playlists') +#config.bind('m', 'hint links spawn mpv --ytdl -slang=en {hint-url} --load-unsafe-playlists') +config.bind('m', '/usr/bin/youtube-dl -x --restrict-filenames --audio-format="mp3" --audio-quality="128k" -o "%(artist)s-%(title)s.%(ext)s" {hint-url}') +config.bind(',M', 'hint links spawn umpv {hint-url}') +config.bind(',t', 'hint links spawn transmission-remote --add {hint-url}') +#config.bind(',y', 'hint links spawn /usr/local/bin/st -e tsp youtube-dl -b -o %(channel)s/%(title)s.%(ext)s {hint-url}') +#config.bind(',y', 'hint links spawn /usr/local/bin/st -e /usr/bin/tsp youtube-dl -b {hint-url}') +config.bind(',y', 'hint links spawn /usr/local/bin/st -e tsp youtube-dl --add-metadata -ic -f best {hint-url}') +config.bind(',Y', 'hint links spawn /usr/local/bin/st -e mpsyt dlurl {hint-url}') +config.bind(',q', 'hint links spawn echo "mpsyt dlurl {hint-url}" >> ~/ytdl.list') +config.bind(',p', 'spawn --userscript qute-pass --dmenu-invocation dmenu') +config.bind('xb', 'config-cycle statusbar.hide') +config.bind('xt', 'config-cycle tabs.show always switching') +config.bind('xx', 'config-cycle statusbar.hide ;; config-cycle tabs.show always switching') +config.bind('xp', 'set-cmd-text :set content.proxy socks://localhost:8765') +config.bind('Xp', 'set-cmd-text :config-unset content.proxy') +#config.bind('ua', 'set-cmd-text :set -u https://accounts.google.com/* content.headers.user_agent "Mozilla/5.0 (X11; Linux x86_64; rv:57.0) Gecko/20100101 Firefox/57.0") + +# config.bind("'", 'enter-mode jump_mark') +# config.bind('+', 'zoom-in') +# config.bind('-', 'zoom-out') +# config.bind('.', 'repeat-command') +# config.bind('/', 'set-cmd-text /') +# config.bind(':', 'set-cmd-text :') +# config.bind(';I', 'hint images tab') +# config.bind(';O', 'hint links fill :open -t -r {hint-url}') +# config.bind(';R', 'hint --rapid links window') +# config.bind(';Y', 'hint links yank-primary') +# config.bind(';b', 'hint all tab-bg') +# config.bind(';d', 'hint links download') +# config.bind(';f', 'hint all tab-fg') +# config.bind(';h', 'hint all hover') +# config.bind(';i', 'hint images') +# config.bind(';o', 'hint links fill :open {hint-url}') +# config.bind(';r', 'hint --rapid links tab-bg') +# config.bind(';t', 'hint inputs') +# config.bind(';y', 'hint links yank') +# config.bind('<Alt-1>', 'tab-focus 1') +# config.bind('<Alt-2>', 'tab-focus 2') +# config.bind('<Alt-3>', 'tab-focus 3') +# config.bind('<Alt-4>', 'tab-focus 4') +# config.bind('<Alt-5>', 'tab-focus 5') +# config.bind('<Alt-6>', 'tab-focus 6') +# config.bind('<Alt-7>', 'tab-focus 7') +# config.bind('<Alt-8>', 'tab-focus 8') +# config.bind('<Alt-9>', 'tab-focus -1') +# config.bind('<Ctrl-A>', 'navigate increment') +# config.bind('<Ctrl-Alt-p>', 'print') +# config.bind('<Ctrl-B>', 'scroll-page 0 -1') +# config.bind('<Ctrl-D>', 'scroll-page 0 0.5') +# config.bind('<Ctrl-F5>', 'reload -f') +# config.bind('<Ctrl-F>', 'scroll-page 0 1') +# config.bind('<Ctrl-N>', 'open -w') +# config.bind('<Ctrl-PgDown>', 'tab-next') +# config.bind('<Ctrl-PgUp>', 'tab-prev') +# config.bind('<Ctrl-Q>', 'quit') +# config.bind('<Ctrl-Return>', 'follow-selected -t') +# config.bind('<Ctrl-Shift-N>', 'open -p') +# config.bind('<Ctrl-Shift-T>', 'undo') +# config.bind('<Ctrl-Shift-W>', 'close') +# config.bind('<Ctrl-T>', 'open -t') +# config.bind('<Ctrl-Tab>', 'tab-focus last') +# config.bind('<Ctrl-U>', 'scroll-page 0 -0.5') +# config.bind('<Ctrl-V>', 'enter-mode passthrough') +# config.bind('<Ctrl-W>', 'tab-close') +# config.bind('<Ctrl-X>', 'navigate decrement') +# config.bind('<Ctrl-^>', 'tab-focus last') +# config.bind('<Ctrl-h>', 'home') +# config.bind('<Ctrl-p>', 'tab-pin') +# config.bind('<Ctrl-s>', 'stop') +# config.bind('<Escape>', 'clear-keychain ;; search ;; fullscreen --leave') +# config.bind('<F11>', 'fullscreen') +# config.bind('<F5>', 'reload') +# config.bind('<Return>', 'follow-selected') +# config.bind('<back>', 'back') +# config.bind('<forward>', 'forward') +# config.bind('=', 'zoom') +# config.bind('?', 'set-cmd-text ?') +# config.bind('@', 'run-macro') +# config.bind('B', 'set-cmd-text -s :quickmark-load -t') +# config.bind('D', 'tab-close -o') +# config.bind('F', 'hint all tab') +# config.bind('G', 'scroll-to-perc') +# config.bind('H', 'back') +config.bind('J', 'tab-prev') +config.bind('K', 'tab-next') +# config.bind('L', 'forward') +# config.bind('M', 'bookmark-add') +# config.bind('N', 'search-prev') +# config.bind('O', 'set-cmd-text -s :open -t') +# config.bind('PP', 'open -t -- {primary}') +# config.bind('Pp', 'open -t -- {clipboard}') +# config.bind('R', 'reload -f') +# config.bind('Sb', 'open qute://bookmarks#bookmarks') +# config.bind('Sh', 'open qute://history') +# config.bind('Sq', 'open qute://bookmarks') +# config.bind('Ss', 'open qute://settings') +# config.bind('T', 'tab-focus') +# config.bind('ZQ', 'quit') +# config.bind('ZZ', 'quit --save') +# config.bind('[[', 'navigate prev') +# config.bind(']]', 'navigate next') +# config.bind('`', 'enter-mode set_mark') +# config.bind('ad', 'download-cancel') +# config.bind('b', 'set-cmd-text -s :quickmark-load') +# config.bind('cd', 'download-clear') +# config.bind('co', 'tab-only') +# config.bind('d', 'tab-close') +# config.bind('f', 'hint') +# config.bind('g$', 'tab-focus -1') +# config.bind('g0', 'tab-focus 1') +# config.bind('gB', 'set-cmd-text -s :bookmark-load -t') +# config.bind('gC', 'tab-clone') +# config.bind('gO', 'set-cmd-text :open -t -r {url:pretty}') +# config.bind('gU', 'navigate up -t') +# config.bind('g^', 'tab-focus 1') +# config.bind('ga', 'open -t') +# config.bind('gb', 'set-cmd-text -s :bookmark-load') +config.bind('gd', 'download') +config.bind('gf', 'view-source') +# config.bind('gg', 'scroll-to-perc 0') +# config.bind('gl', 'tab-move -') +# config.bind('gm', 'tab-move') +# config.bind('go', 'set-cmd-text :open {url:pretty}') +# config.bind('gr', 'tab-move +') +# config.bind('gt', 'set-cmd-text -s :buffer') +# config.bind('gu', 'navigate up') +# config.bind('h', 'scroll left') +# config.bind('i', 'enter-mode insert') +# config.bind('j', 'scroll down') +# config.bind('k', 'scroll up') +# config.bind('l', 'scroll right') +# config.bind('m', 'quickmark-save') +# config.bind('n', 'search-next') +# config.bind('o', 'set-cmd-text -s :open') +# config.bind('pP', 'open -- {primary}') +# config.bind('pp', 'open -- {clipboard}') +# config.bind('q', 'record-macro') +# config.bind('r', 'reload') +# config.bind('sf', 'save') +# config.bind('sk', 'set-cmd-text -s :bind') +# config.bind('sl', 'set-cmd-text -s :set -t') +# config.bind('ss', 'set-cmd-text -s :set') +# config.bind('tPH', 'config-cycle -p -u *://*.{url:host}/* content.plugins ;; reload') +# config.bind('tPh', 'config-cycle -p -u *://{url:host}/* content.plugins ;; reload') +# config.bind('tPu', 'config-cycle -p -u {url} content.plugins ;; reload') +config.bind('tSH', 'config-cycle -p -u *://*.{url:host}/* content.javascript.enabled ;; reload') +config.bind('tSh', 'config-cycle -p -u *://{url:host}/* content.javascript.enabled ;; reload') +config.bind('tSu', 'config-cycle -p -u {url} content.javascript.enabled ;; reload') +# config.bind('th', 'back -t') +# config.bind('tl', 'forward -t') +# config.bind('tpH', 'config-cycle -p -t -u *://*.{url:host}/* content.plugins ;; reload') +# config.bind('tph', 'config-cycle -p -t -u *://{url:host}/* content.plugins ;; reload') +# config.bind('tpu', 'config-cycle -p -t -u {url} content.plugins ;; reload') +# config.bind('tsH', 'config-cycle -p -t -u *://*.{url:host}/* content.javascript.enabled ;; reload') +# config.bind('tsh', 'config-cycle -p -t -u *://{url:host}/* content.javascript.enabled ;; reload') +# config.bind('tsu', 'config-cycle -p -t -u {url} content.javascript.enabled ;; reload') +# config.bind('u', 'undo') +# config.bind('v', 'enter-mode caret') +# config.bind('wB', 'set-cmd-text -s :bookmark-load -w') +# config.bind('wO', 'set-cmd-text :open -w {url:pretty}') +# config.bind('wP', 'open -w -- {primary}') +# config.bind('wb', 'set-cmd-text -s :quickmark-load -w') +# config.bind('wf', 'hint all window') +# config.bind('wh', 'back -w') +# config.bind('wi', 'inspector') +# config.bind('wl', 'forward -w') +# config.bind('wo', 'set-cmd-text -s :open -w') +# config.bind('wp', 'open -w -- {clipboard}') +# config.bind('xO', 'set-cmd-text :open -b -r {url:pretty}') +# config.bind('xo', 'set-cmd-text -s :open -b') +# config.bind('yD', 'yank domain -s') +# config.bind('yP', 'yank pretty-url -s') +# config.bind('yT', 'yank title -s') +# config.bind('yY', 'yank -s') +# config.bind('yd', 'yank domain') +# config.bind('yp', 'yank pretty-url') +# config.bind('yt', 'yank title') +# config.bind('yy', 'yank') +# config.bind('{{', 'navigate prev -t') +# config.bind('}}', 'navigate next -t') + +## Bindings for caret mode +# config.bind('$', 'move-to-end-of-line', mode='caret') +# config.bind('0', 'move-to-start-of-line', mode='caret') +# config.bind('<Ctrl-Space>', 'drop-selection', mode='caret') +# config.bind('<Escape>', 'leave-mode', mode='caret') +# config.bind('<Return>', 'yank selection', mode='caret') +# config.bind('<Space>', 'toggle-selection', mode='caret') +# config.bind('G', 'move-to-end-of-document', mode='caret') +# config.bind('H', 'scroll left', mode='caret') +# config.bind('J', 'scroll down', mode='caret') +# config.bind('K', 'scroll up', mode='caret') +# config.bind('L', 'scroll right', mode='caret') +# config.bind('Y', 'yank selection -s', mode='caret') +# config.bind('[', 'move-to-start-of-prev-block', mode='caret') +# config.bind(']', 'move-to-start-of-next-block', mode='caret') +# config.bind('b', 'move-to-prev-word', mode='caret') +# config.bind('c', 'enter-mode normal', mode='caret') +# config.bind('e', 'move-to-end-of-word', mode='caret') +# config.bind('gg', 'move-to-start-of-document', mode='caret') +# config.bind('h', 'move-to-prev-char', mode='caret') +# config.bind('j', 'move-to-next-line', mode='caret') +# config.bind('k', 'move-to-prev-line', mode='caret') +# config.bind('l', 'move-to-next-char', mode='caret') +# config.bind('v', 'toggle-selection', mode='caret') +# config.bind('w', 'move-to-next-word', mode='caret') +# config.bind('y', 'yank selection', mode='caret') +# config.bind('{', 'move-to-end-of-prev-block', mode='caret') +# config.bind('}', 'move-to-end-of-next-block', mode='caret') + +## Bindings for command mode +# config.bind('<Alt-B>', 'rl-backward-word', mode='command') +# config.bind('<Alt-Backspace>', 'rl-backward-kill-word', mode='command') +# config.bind('<Alt-D>', 'rl-kill-word', mode='command') +# config.bind('<Alt-F>', 'rl-forward-word', mode='command') +# config.bind('<Ctrl-?>', 'rl-delete-char', mode='command') +# config.bind('<Ctrl-A>', 'rl-beginning-of-line', mode='command') +# config.bind('<Ctrl-B>', 'rl-backward-char', mode='command') +# config.bind('<Ctrl-C>', 'completion-item-yank', mode='command') +# config.bind('<Ctrl-D>', 'completion-item-del', mode='command') +# config.bind('<Ctrl-E>', 'rl-end-of-line', mode='command') +# config.bind('<Ctrl-F>', 'rl-forward-char', mode='command') +# config.bind('<Ctrl-H>', 'rl-backward-delete-char', mode='command') +# config.bind('<Ctrl-K>', 'rl-kill-line', mode='command') +# config.bind('<Ctrl-N>', 'command-history-next', mode='command') +# config.bind('<Ctrl-P>', 'command-history-prev', mode='command') +# config.bind('<Ctrl-Return>', 'command-accept --rapid', mode='command') +# config.bind('<Ctrl-Shift-C>', 'completion-item-yank --sel', mode='command') +# config.bind('<Ctrl-Shift-Tab>', 'completion-item-focus prev-category', mode='command') +# config.bind('<Ctrl-Tab>', 'completion-item-focus next-category', mode='command') +# config.bind('<Ctrl-U>', 'rl-unix-line-discard', mode='command') +# config.bind('<Ctrl-W>', 'rl-unix-word-rubout', mode='command') +# config.bind('<Ctrl-Y>', 'rl-yank', mode='command') +# config.bind('<Down>', 'completion-item-focus --history next', mode='command') +# config.bind('<Escape>', 'leave-mode', mode='command') +# config.bind('<Return>', 'command-accept', mode='command') +# config.bind('<Shift-Delete>', 'completion-item-del', mode='command') +# config.bind('<Shift-Tab>', 'completion-item-focus prev', mode='command') +# config.bind('<Tab>', 'completion-item-focus next', mode='command') +# config.bind('<Up>', 'completion-item-focus --history prev', mode='command') + +## Bindings for hint mode +# config.bind('<Ctrl-B>', 'hint all tab-bg', mode='hint') +# config.bind('<Ctrl-F>', 'hint links', mode='hint') +# config.bind('<Ctrl-R>', 'hint --rapid links tab-bg', mode='hint') +# config.bind('<Escape>', 'leave-mode', mode='hint') +# config.bind('<Return>', 'follow-hint', mode='hint') + +## Bindings for insert mode +# config.bind('<Ctrl-E>', 'open-editor', mode='insert') +# config.bind('<Escape>', 'leave-mode', mode='insert') +# config.bind('<Shift-Ins>', 'insert-text {primary}', mode='insert') +config.bind('<Ctrl-i>', 'spawn --userscript qute-keepass -p ~/Documents/20151120.kdbx', mode='insert') + +## Bindings for passthrough mode +# config.bind('<Ctrl-V>', 'leave-mode', mode='passthrough') + +## Bindings for prompt mode +# config.bind('<Alt-B>', 'rl-backward-word', mode='prompt') +# config.bind('<Alt-Backspace>', 'rl-backward-kill-word', mode='prompt') +# config.bind('<Alt-D>', 'rl-kill-word', mode='prompt') +# config.bind('<Alt-F>', 'rl-forward-word', mode='prompt') +# config.bind('<Alt-Shift-Y>', 'prompt-yank --sel', mode='prompt') +# config.bind('<Alt-Y>', 'prompt-yank', mode='prompt') +# config.bind('<Ctrl-?>', 'rl-delete-char', mode='prompt') +# config.bind('<Ctrl-A>', 'rl-beginning-of-line', mode='prompt') +# config.bind('<Ctrl-B>', 'rl-backward-char', mode='prompt') +# config.bind('<Ctrl-E>', 'rl-end-of-line', mode='prompt') +# config.bind('<Ctrl-F>', 'rl-forward-char', mode='prompt') +# config.bind('<Ctrl-H>', 'rl-backward-delete-char', mode='prompt') +# config.bind('<Ctrl-K>', 'rl-kill-line', mode='prompt') +# config.bind('<Ctrl-U>', 'rl-unix-line-discard', mode='prompt') +# config.bind('<Ctrl-W>', 'rl-unix-word-rubout', mode='prompt') +# config.bind('<Ctrl-X>', 'prompt-open-download', mode='prompt') +# config.bind('<Ctrl-Y>', 'rl-yank', mode='prompt') +# config.bind('<Down>', 'prompt-item-focus next', mode='prompt') +# config.bind('<Escape>', 'leave-mode', mode='prompt') +# config.bind('<Return>', 'prompt-accept', mode='prompt') +# config.bind('<Shift-Tab>', 'prompt-item-focus prev', mode='prompt') +# config.bind('<Tab>', 'prompt-item-focus next', mode='prompt') +# config.bind('<Up>', 'prompt-item-focus prev', mode='prompt') + +## Bindings for register mode +# config.bind('<Escape>', 'leave-mode', mode='register') + +## Bindings for yesno mode +# config.bind('<Alt-Shift-Y>', 'prompt-yank --sel', mode='yesno') +# config.bind('<Alt-Y>', 'prompt-yank', mode='yesno') +# config.bind('<Escape>', 'leave-mode', mode='yesno') +# config.bind('<Return>', 'prompt-accept', mode='yesno') +# config.bind('n', 'prompt-accept no', mode='yesno') +# config.bind('y', 'prompt-accept yes', mode='yesno') +config.source('nord-qutebrowser.py') diff --git a/.config/qutebrowser/jupyter-profile/autoconfig.yml b/.config/qutebrowser/jupyter-profile/autoconfig.yml new file mode 100644 index 0000000..bdf84b0 --- /dev/null +++ b/.config/qutebrowser/jupyter-profile/autoconfig.yml @@ -0,0 +1,7 @@ +# DO NOT edit this file by hand, qutebrowser will overwrite it. +# Instead, create a config.py - see :help for details. + +config_version: 2 +settings: + content.javascript.enabled: + '*://*.next-episode.net/*': false diff --git a/.config/qutebrowser/jupyter-profile/config.py b/.config/qutebrowser/jupyter-profile/config.py new file mode 100644 index 0000000..de9b2e7 --- /dev/null +++ b/.config/qutebrowser/jupyter-profile/config.py @@ -0,0 +1,1676 @@ +## Autogenerated config.py +## Documentation: +## qute://help/configuring.html +## qute://help/settings.html +# +# Read ~/.Xresources file for colours (see the help file listed above) +# import subprocess + +# def read_xresources(prefix): + # props = {} + # x=subprocess.run(['xrdb','-query'], stdout=subprocess.PIPE) + # lines = x.stdout.decode().split('\n') + # for line in filter(lambda l : l.startswith(prefix), lines): + # prop, _, value = line.partition(':\t') + # props[prop] = value + # return props + +# xresources = read_xresources('*') +# c.colors.statusbar.normal.bg = xresources['*.background'] + + +## This is here so configs done via the GUI are still loaded. +## Remove it to not load settings done via the GUI. +# config.load_autoconfig() + +## Aliases for commands. The keys of the given dictionary are the +## aliases, while the values are the commands they map to. +## Type: Dict +c.aliases = { 'w':'session-save', 'q':'quit', 'wq':'quit --save', 'qtb':'open https://www.qutebrowser.org/', 'mbt':'open https://signin1.bt.com/login/emailloginform', 'Ombt':'open -t https://signin1.bt.com/login/emailloginform', 'nx':'open https://next-episode.net/', 'Onx':'open -t https://next-episode.net/', 'gk':'open https://gameknot.com/', 'Ogk':'open -t https://gameknot.com/', 'yt':'open https://youtube.com', 'Oyt':'open -t https://youtube.com', 'Y':'open http://www.cosmos.esa.int/web/xmm-newton/current-calibration-files', 'hx':'open https://heasarc.gsfc.nasa.gov/', 'Ohx':'open -t https://heasarc.gsfc.nasa.gov/', 'yfy':'open https://yts.am/', '1337':'open https://1337x.to/home/', 'O1337':'open -t https://1337x.to/home/', 'ez':'open https://eztv.ag', 'Oez':'open -t https://eztv.ag', 'tz':'open https://extratorrent.ag', 'Otz':'open -t https://extratorrent.ag', 'tpb':'open https://thepiratebay.org/', 'Otpb':'open -t https://thepiratebay.org/', 'Nm':'open https://netmail.herts.ac.uk/', 'ONm':'open -t https://netmail.herts.ac.uk/', 'Oan':'open -t https://neonalley.com','Ocm':'open -t https://churchmilitant.com','Osp':'open -t https://stpaulcenter.com'} +# +## Time interval (in milliseconds) between auto-saves of +## config/cookies/etc. +## Type: Int +# c.auto_save.interval = 15000 + +## Always restore open sites when qutebrowser is reopened. +## Type: Bool +c.auto_save.session = True + +## Backend to use to display websites. qutebrowser supports two different +## web rendering engines / backends, QtWebKit and QtWebEngine. QtWebKit +## was discontinued by the Qt project with Qt 5.6, but picked up as a +## well maintained fork: https://github.com/annulen/webkit/wiki - +## qutebrowser only supports the fork. QtWebEngine is Qt's official +## successor to QtWebKit. It's slightly more resource hungry than +## QtWebKit and has a couple of missing features in qutebrowser, but is +## generally the preferred choice. +## Type: String +## Valid values: +## - webengine: Use QtWebEngine (based on Chromium). +## - webkit: Use QtWebKit (based on WebKit, similar to Safari). +# c.backend = 'webengine' + +## This setting can be used to map keys to other keys. When the key used +## as dictionary-key is pressed, the binding for the key used as +## dictionary-value is invoked instead. This is useful for global +## remappings of keys, for example to map Ctrl-[ to Escape. Note that +## when a key is bound (via `bindings.default` or `bindings.commands`), +## the mapping is ignored. +## Type: Dict +# c.bindings.key_mappings = {'<Ctrl-[>': '<Escape>', '<Ctrl-6>': '<Ctrl-^>', '<Ctrl-M>': '<Return>', '<Ctrl-J>': '<Return>', '<Shift-Return>': '<Return>', '<Enter>': '<Return>', '<Shift-Enter>': '<Return>', '<Ctrl-Enter>': '<Ctrl-Return>'} + +## Background color of the completion widget category headers. +## Type: QssColor +# c.colors.completion.category.bg = 'qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #888888, stop:1 #505050)' + +## Bottom border color of the completion widget category headers. +## Type: QssColor +# c.colors.completion.category.border.bottom = 'black' + +## Top border color of the completion widget category headers. +## Type: QssColor +# c.colors.completion.category.border.top = 'black' + +## Foreground color of completion widget category headers. +## Type: QtColor +# c.colors.completion.category.fg = 'white' + +## Background color of the completion widget for even rows. +## Type: QssColor +# c.colors.completion.even.bg = '#333333' + +## Text color of the completion widget. May be a single color to use for +## all columns or a list of three colors, one for each column. +## Type: List of QtColor, or QtColor +# c.colors.completion.fg = ['white', 'white', 'white'] + +## Background color of the selected completion item. +## Type: QssColor +# c.colors.completion.item.selected.bg = '#e8c000' + +## Bottom border color of the selected completion item. +## Type: QssColor +# c.colors.completion.item.selected.border.bottom = '#bbbb00' + +## Top border color of the completion widget category headers. +## Type: QssColor +# c.colors.completion.item.selected.border.top = '#bbbb00' + +## Foreground color of the selected completion item. +## Type: QtColor +# c.colors.completion.item.selected.fg = 'black' + +## Foreground color of the matched text in the completion. +## Type: QssColor +# c.colors.completion.match.fg = '#ff4444' + +## Background color of the completion widget for odd rows. +## Type: QssColor +# c.colors.completion.odd.bg = '#444444' + +## Color of the scrollbar in the completion view. +## Type: QssColor +# c.colors.completion.scrollbar.bg = '#333333' + +## Color of the scrollbar handle in the completion view. +## Type: QssColor +# c.colors.completion.scrollbar.fg = 'white' + +## Background color for the download bar. +## Type: QssColor +# c.colors.downloads.bar.bg = 'black' + +## Background color for downloads with errors. +## Type: QtColor +# c.colors.downloads.error.bg = 'red' + +## Foreground color for downloads with errors. +## Type: QtColor +# c.colors.downloads.error.fg = 'white' + +## Color gradient start for download backgrounds. +## Type: QtColor +# c.colors.downloads.start.bg = '#0000aa' + +## Color gradient start for download text. +## Type: QtColor +# c.colors.downloads.start.fg = 'white' + +## Color gradient stop for download backgrounds. +## Type: QtColor +# c.colors.downloads.stop.bg = '#00aa00' + +## Color gradient end for download text. +## Type: QtColor +# c.colors.downloads.stop.fg = 'white' + +## Color gradient interpolation system for download backgrounds. +## Type: ColorSystem +## Valid values: +## - rgb: Interpolate in the RGB color system. +## - hsv: Interpolate in the HSV color system. +## - hsl: Interpolate in the HSL color system. +## - none: Don't show a gradient. +# c.colors.downloads.system.bg = 'rgb' + +## Color gradient interpolation system for download text. +## Type: ColorSystem +## Valid values: +## - rgb: Interpolate in the RGB color system. +## - hsv: Interpolate in the HSV color system. +## - hsl: Interpolate in the HSL color system. +## - none: Don't show a gradient. +# c.colors.downloads.system.fg = 'rgb' + +## Background color for hints. Note that you can use a `rgba(...)` value +## for transparency. +## Type: QssColor +# c.colors.hints.bg = 'qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgba(255, 247, 133, 0.8), stop:1 rgba(255, 197, 66, 0.8))' + +## Font color for hints. +## Type: QssColor +# c.colors.hints.fg = 'black' + +## Font color for the matched part of hints. +## Type: QssColor +# c.colors.hints.match.fg = 'green' + +## Background color of the keyhint widget. +## Type: QssColor +# c.colors.keyhint.bg = 'rgba(0, 0, 0, 80%)' + +## Text color for the keyhint widget. +## Type: QssColor +# c.colors.keyhint.fg = '#FFFFFF' + +## Highlight color for keys to complete the current keychain. +## Type: QssColor +# c.colors.keyhint.suffix.fg = '#FFFF00' + +## Background color of an error message. +## Type: QssColor +# c.colors.messages.error.bg = 'red' + +## Border color of an error message. +## Type: QssColor +# c.colors.messages.error.border = '#bb0000' + +## Foreground color of an error message. +## Type: QssColor +# c.colors.messages.error.fg = 'white' + +## Background color of an info message. +## Type: QssColor +# c.colors.messages.info.bg = 'black' + +## Border color of an info message. +## Type: QssColor +# c.colors.messages.info.border = '#333333' + +## Foreground color of an info message. +## Type: QssColor +# c.colors.messages.info.fg = 'white' + +## Background color of a warning message. +## Type: QssColor +# c.colors.messages.warning.bg = 'darkorange' + +## Border color of a warning message. +## Type: QssColor +# c.colors.messages.warning.border = '#d47300' + +## Foreground color of a warning message. +## Type: QssColor +# c.colors.messages.warning.fg = 'white' + +## Background color for prompts. +## Type: QssColor +# c.colors.prompts.bg = '#444444' + +## Border used around UI elements in prompts. +## Type: String +# c.colors.prompts.border = '1px solid gray' + +## Foreground color for prompts. +## Type: QssColor +# c.colors.prompts.fg = 'white' + +## Background color for the selected item in filename prompts. +## Type: QssColor +# c.colors.prompts.selected.bg = 'grey' + +## Background color of the statusbar in caret mode. +## Type: QssColor +# c.colors.statusbar.caret.bg = 'purple' + +## Foreground color of the statusbar in caret mode. +## Type: QssColor +# c.colors.statusbar.caret.fg = 'white' + +## Background color of the statusbar in caret mode with a selection. +## Type: QssColor +# c.colors.statusbar.caret.selection.bg = '#a12dff' + +## Foreground color of the statusbar in caret mode with a selection. +## Type: QssColor +# c.colors.statusbar.caret.selection.fg = 'white' + +## Background color of the statusbar in command mode. +## Type: QssColor +# c.colors.statusbar.command.bg = 'black' + +## Foreground color of the statusbar in command mode. +## Type: QssColor +# c.colors.statusbar.command.fg = 'white' + +## Background color of the statusbar in private browsing + command mode. +## Type: QssColor +# c.colors.statusbar.command.private.bg = 'grey' + +## Foreground color of the statusbar in private browsing + command mode. +## Type: QssColor +# c.colors.statusbar.command.private.fg = 'white' + +## Background color of the statusbar in insert mode. +## Type: QssColor +# c.colors.statusbar.insert.bg = 'darkgreen' + +## Foreground color of the statusbar in insert mode. +## Type: QssColor +# c.colors.statusbar.insert.fg = 'white' + +## Background color of the statusbar. +## Type: QssColor +# c.colors.statusbar.normal.bg = 'black' + +## Foreground color of the statusbar. +## Type: QssColor +# c.colors.statusbar.normal.fg = 'white' + +## Background color of the statusbar in passthrough mode. +## Type: QssColor +# c.colors.statusbar.passthrough.bg = 'darkblue' + +## Foreground color of the statusbar in passthrough mode. +## Type: QssColor +# c.colors.statusbar.passthrough.fg = 'white' + +## Background color of the statusbar in private browsing mode. +## Type: QssColor +# c.colors.statusbar.private.bg = '#666666' + +## Foreground color of the statusbar in private browsing mode. +## Type: QssColor +# c.colors.statusbar.private.fg = 'white' + +## Background color of the progress bar. +## Type: QssColor +# c.colors.statusbar.progress.bg = 'white' + +## Foreground color of the URL in the statusbar on error. +## Type: QssColor +# c.colors.statusbar.url.error.fg = 'orange' + +## Default foreground color of the URL in the statusbar. +## Type: QssColor +# c.colors.statusbar.url.fg = 'white' + +## Foreground color of the URL in the statusbar for hovered links. +## Type: QssColor +# c.colors.statusbar.url.hover.fg = 'aqua' + +## Foreground color of the URL in the statusbar on successful load +## (http). +## Type: QssColor +# c.colors.statusbar.url.success.http.fg = 'white' + +## Foreground color of the URL in the statusbar on successful load +## (https). +## Type: QssColor +# c.colors.statusbar.url.success.https.fg = 'lime' + +## Foreground color of the URL in the statusbar when there's a warning. +## Type: QssColor +# c.colors.statusbar.url.warn.fg = 'yellow' + +## Background color of the tab bar. +## Type: QtColor +# c.colors.tabs.bar.bg = '#555555' + +## Background color of unselected even tabs. +## Type: QtColor +# c.colors.tabs.even.bg = 'darkgrey' + +## Foreground color of unselected even tabs. +## Type: QtColor +# c.colors.tabs.even.fg = 'white' + +## Color for the tab indicator on errors. +## Type: QtColor +# c.colors.tabs.indicator.error = '#ff0000' + +## Color gradient start for the tab indicator. +## Type: QtColor +# c.colors.tabs.indicator.start = '#0000aa' + +## Color gradient end for the tab indicator. +## Type: QtColor +# c.colors.tabs.indicator.stop = '#00aa00' + +## Color gradient interpolation system for the tab indicator. +## Type: ColorSystem +## Valid values: +## - rgb: Interpolate in the RGB color system. +## - hsv: Interpolate in the HSV color system. +## - hsl: Interpolate in the HSL color system. +## - none: Don't show a gradient. +# c.colors.tabs.indicator.system = 'rgb' + +## Background color of unselected odd tabs. +## Type: QtColor +# c.colors.tabs.odd.bg = 'grey' + +## Foreground color of unselected odd tabs. +## Type: QtColor +# c.colors.tabs.odd.fg = 'white' + +## Background color of selected even tabs. +## Type: QtColor +# c.colors.tabs.selected.even.bg = 'black' + +## Foreground color of selected even tabs. +## Type: QtColor +# c.colors.tabs.selected.even.fg = 'white' + +## Background color of selected odd tabs. +## Type: QtColor +# c.colors.tabs.selected.odd.bg = 'black' + +## Foreground color of selected odd tabs. +## Type: QtColor +# c.colors.tabs.selected.odd.fg = 'white' + +## Background color for webpages if unset (or empty to use the theme's +## color). +## Type: QtColor +# c.colors.webpage.bg = 'white' + +## Number of commands to save in the command history. 0: no history / -1: +## unlimited +## Type: Int +# c.completion.cmd_history_max_items = 100 + +## Delay (in milliseconds) before updating completions after typing a +## character. +## Type: Int +# c.completion.delay = 0 + +## Height (in pixels or as percentage of the window) of the completion. +## Type: PercOrInt +# c.completion.height = '50%' + +## Minimum amount of characters needed to update completions. +## Type: Int +# c.completion.min_chars = 1 + +## Move on to the next part when there's only one possible completion +## left. +## Type: Bool +# c.completion.quick = True + +## Padding (in pixels) of the scrollbar handle in the completion window. +## Type: Int +# c.completion.scrollbar.padding = 2 + +## Width (in pixels) of the scrollbar in the completion window. +## Type: Int +# c.completion.scrollbar.width = 12 + +## When to show the autocompletion window. +## Type: String +## Valid values: +## - always: Whenever a completion is available. +## - auto: Whenever a completion is requested. +## - never: Never. +# c.completion.show = 'always' + +## Shrink the completion to be smaller than the configured size if there +## are no scrollbars. +## Type: Bool +# c.completion.shrink = False + +## Format of timestamps (e.g. for the history completion). +## Type: TimestampTemplate +# c.completion.timestamp_format = '%Y-%m-%d' + +## Execute the best-matching command on a partial match. +## Type: Bool +c.completion.use_best_match = True + +## Number of URLs to show in the web history. 0: no history / -1: +## unlimited +## Type: Int +# c.completion.web_history_max_items = -1 + +## Require a confirmation before quitting the application. +## Type: ConfirmQuit +## Valid values: +## - always: Always show a confirmation. +## - multiple-tabs: Show a confirmation if multiple tabs are opened. +## - downloads: Show a confirmation if downloads are running +## - never: Never show a confirmation. +# c.confirm_quit = ['never'] + +## Enable support for the HTML 5 web application cache feature. An +## application cache acts like an HTTP cache in some sense. For documents +## that use the application cache via JavaScript, the loader engine will +## first ask the application cache for the contents, before hitting the +## network. +## Type: Bool +# c.content.cache.appcache = True + +## Maximum number of pages to hold in the global memory page cache. The +## page cache allows for a nicer user experience when navigating forth or +## back to pages in the forward/back history, by pausing and resuming up +## to _n_ pages. For more information about the feature, please refer to: +## http://webkit.org/blog/427/webkit-page-cache-i-the-basics/ +## Type: Int +# c.content.cache.maximum_pages = 0 + +## Size (in bytes) of the HTTP network cache. Null to use the default +## value. With QtWebEngine, the maximum supported value is 2147483647 (~2 +## GB). +## Type: Int +# c.content.cache.size = None + +## Which cookies to accept. +## Type: String +## Valid values: +## - all: Accept all cookies. +## - no-3rdparty: Accept cookies from the same origin only. +## - no-unknown-3rdparty: Accept cookies from the same origin only, unless a cookie is already set for the domain. +## - never: Don't accept cookies at all. +# c.content.cookies.accept = 'no-3rdparty' + +## Store cookies. Note this option needs a restart with QtWebEngine on Qt +## < 5.9. +## Type: Bool +c.content.cookies.store = True + +## Default encoding to use for websites. The encoding must be a string +## describing an encoding such as _utf-8_, _iso-8859-1_, etc. +## Type: String +# c.content.default_encoding = 'iso-8859-1' + +## Enable extra tools for Web developers. This needs to be enabled for +## `:inspector` to work and also adds an _Inspect_ entry to the context +## menu. For QtWebEngine, see `--enable-webengine-inspector` in +## `qutebrowser --help` instead. +## Type: Bool +## c.content.developer_extras = True + +## Try to pre-fetch DNS entries to speed up browsing. +## Type: Bool +# c.content.dns_prefetch = True + +## Expand each subframe to its contents. This will flatten all the frames +## to become one scrollable page. +## Type: Bool +# c.content.frame_flattening = False + +## Allow websites to request geolocations. +## Type: BoolAsk +## Valid values: +## - true +## - false +## - ask +# c.content.geolocation = 'ask' + +## Value to send in the `Accept-Language` header. +## Type: String +# c.content.headers.accept_language = 'en-US,en' + +## Custom headers for qutebrowser HTTP requests. +## Type: Dict +# c.content.headers.custom = {} + +## Value to send in the `DNT` header. When this is set to true, +## qutebrowser asks websites to not track your identity. If set to null, +## the DNT header is not sent at all. +## Type: Bool +# c.content.headers.do_not_track = True + +## When to send the Referer header. The Referer header tells websites +## from which website you were coming from when visiting them. +## Type: String +## Valid values: +## - always: Always send the Referer. +## - never: Never send the Referer. This is not recommended, as some sites may break. +## - same-domain: Only send the Referer for the same domain. This will still protect your privacy, but shouldn't break any sites. +# c.content.headers.referer = 'same-domain' + +## User agent to send. Unset to send the default. +## Type: String +# c.content.headers.user_agent = None + +## Enable host blocking. +## Type: Bool +# c.content.host_blocking.enabled = True + +## List of URLs of lists which contain hosts to block. The file can be +## in one of the following formats: - An `/etc/hosts`-like file - One +## host per line - A zip-file of any of the above, with either only one +## file, or a file named `hosts` (with any extension). +## Type: List of Url +# c.content.host_blocking.lists = ['https://www.malwaredomainlist.com/hostslist/hosts.txt', 'http://someonewhocares.org/hosts/hosts', 'http://winhelp2002.mvps.org/hosts.zip', 'http://malwaredomains.lehigh.edu/files/justdomains.zip', 'https://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&mimetype=plaintext'] + +## List of domains that should always be loaded, despite being ad- +## blocked. Domains may contain * and ? wildcards and are otherwise +## required to exactly match the requested domain. Local domains are +## always exempt from hostblocking. +## Type: List of String +c.content.host_blocking.whitelist = ['piwik.org','next-episode.net','churchmilitant.com','churchmilitant.tv'] + +## Enable hyperlink auditing (`<a ping>`). +## Type: Bool +# c.content.hyperlink_auditing = False + +## Load images automatically in web pages. +## Type: Bool +# c.content.images = True + +## Show javascript alerts. +## Type: Bool +# c.content.javascript.alert = True + +## Allow JavaScript to read from or write to the clipboard. With +## QtWebEngine, writing the clipboard as response to a user interaction +## is always allowed. +## Type: Bool +# c.content.javascript.can_access_clipboard = False + +## Allow JavaScript to close tabs. +## Type: Bool +# c.content.javascript.can_close_tabs = False + +## Allow JavaScript to open new tabs without user interaction. +## Type: Bool +# c.content.javascript.can_open_tabs_automatically = False + +## Enable JavaScript. +## Type: Bool +c.content.javascript.enabled = True + +## Log levels to use for JavaScript console logging messages. When a +## JavaScript message with the level given in the dictionary key is +## logged, the corresponding dictionary value selects the qutebrowser +## logger to use. On QtWebKit, the "unknown" setting is always used. +## Type: Dict +# c.content.javascript.log = {'unknown': 'debug', 'info': 'debug', 'warning': 'debug', 'error': 'debug'} + +## Use the standard JavaScript modal dialog for `alert()` and +## `confirm()`. +## Type: Bool +# c.content.javascript.modal_dialog = False + +## Show javascript prompts. +## Type: Bool +# c.content.javascript.prompt = True + +## Allow locally loaded documents to access other local URLs. +## Type: Bool +# c.content.local_content_can_access_file_urls = True + +## Allow locally loaded documents to access remote URLs. +## Type: Bool +# c.content.local_content_can_access_remote_urls = False + +## Enable support for HTML 5 local storage and Web SQL. +## Type: Bool +# c.content.local_storage = True + +## Allow websites to record audio/video. +## Type: BoolAsk +## Valid values: +## - true +## - false +## - ask +# c.content.media_capture = 'ask' + +## Netrc-file for HTTP authentication. If unset, `~/.netrc` is used. +## Type: File +# c.content.netrc_file = None + +## Allow websites to show notifications. +## Type: BoolAsk +## Valid values: +## - true +## - false +## - ask +# c.content.notifications = 'ask' + +## Allow pdf.js to view PDF files in the browser. Note that the files can +## still be downloaded by clicking the download button in the pdf.js +## viewer. +## Type: Bool +# c.content.pdfjs = False + +## Enable plugins in Web pages. +## Type: Bool +# c.content.plugins = False vgg +c.content.plugins = True + +## Draw the background color and images also when the page is printed. +## Type: Bool +# c.content.print_element_backgrounds = True + +## Open new windows in private browsing mode which does not record +## visited pages. +## Type: Bool +# c.content.private_browsing = False + +## Proxy to use. In addition to the listed values, you can use a +## `socks://...` or `http://...` URL. +## Type: Proxy +## Valid values: +## - system: Use the system wide proxy. +## - none: Don't use any proxy +# c.content.proxy = 'system' + +## Send DNS requests over the configured proxy. +## Type: Bool +# c.content.proxy_dns_requests = True + +## Validate SSL handshakes. +## Type: BoolAsk +## Valid values: +## - true +## - false +## - ask +# c.content.ssl_strict = 'ask' + +## List of user stylesheet filenames to use. +## Type: List of File, or File +# c.content.user_stylesheets = [] + +## Enable WebGL. +## Type: Bool +# c.content.webgl = True + +## Limit fullscreen to the browser window (does not expand to fill the +## screen). +## Type: Bool +# c.content.windowed_fullscreen = False + +## Monitor load requests for cross-site scripting attempts. Suspicious +## scripts will be blocked and reported in the inspector's JavaScript +## console. Enabling this feature might have an impact on performance. +## Type: Bool +# c.content.xss_auditing = False + +## Directory to save downloads to. If unset, a sensible OS-specific +## default is used. +## Type: Directory +# c.downloads.location.directory = None + +## Prompt the user for the download location. If set to false, +## `downloads.location.directory` will be used. +## Type: Bool +# c.downloads.location.prompt = True + +## Remember the last used download directory. +## Type: Bool +# c.downloads.location.remember = True + +## What to display in the download filename input. +## Type: String +## Valid values: +## - path: Show only the download path. +## - filename: Show only download filename. +## - both: Show download path and filename. +# c.downloads.location.suggestion = 'path' + +## Default program used to open downloads. If null, the default internal +## handler is used. Any `{}` in the string will be expanded to the +## filename, else the filename will be appended. +## Type: String +# c.downloads.open_dispatcher = None + +## Where to show the downloaded files. +## Type: VerticalPosition +## Valid values: +## - top +## - bottom +# c.downloads.position = 'top' + +## Duration (in milliseconds) to wait before removing finished downloads. +## If set to -1, downloads are never removed. +## Type: Int +# c.downloads.remove_finished = -1 + +## Editor (and arguments) to use for the `open-editor` command. The +## following placeholders are defined: * `{file}`: Filename of the file +## to be edited. * `{line}`: Line in which the caret is found in the +## text. * `{column}`: Column in which the caret is found in the text. * +## `{line0}`: Same as `{line}`, but starting from index 0. * `{column0}`: +## Same as `{column}`, but starting from index 0. +## Type: ShellCommand +# c.editor.command = ['gvim', '-f', '{file}', '-c', 'normal {line}G{column0}l'] +c.editor.command = ["urxvt", "-title", "qutebrowserSratchpad", "-geometry", "86x24+40+60", "-e", "vim", "-f", "{}"] + +## Encoding to use for the editor. +## Type: Encoding +# c.editor.encoding = 'utf-8' + +## Font used in the completion categories. +## Type: Font +# c.fonts.completion.category = 'bold 10pt monospace' + +## Font used in the completion widget. +## Type: Font +# c.fonts.completion.entry = '10pt monospace' + +## Font used for the debugging console. +## Type: QtFont +# c.fonts.debug_console = '10pt monospace' + +## Font used for the downloadbar. +## Type: Font +# c.fonts.downloads = '10pt monospace' + +## Font used for the hints. +## Type: Font +c.fonts.hints = 'bold 8pt monospace' + +## Font used in the keyhint widget. +## Type: Font +# c.fonts.keyhint = '10pt monospace' + +## Font used for error messages. +## Type: Font +# c.fonts.messages.error = '10pt monospace' + +## Font used for info messages. +## Type: Font +# c.fonts.messages.info = '10pt monospace' + +## Font used for warning messages. +## Type: Font +# c.fonts.messages.warning = '10pt monospace' + +## Default monospace fonts. Whenever "monospace" is used in a font +## setting, it's replaced with the fonts listed here. +## Type: Font +# c.fonts.monospace = '"xos4 Terminus", Terminus, Monospace, "DejaVu Sans Mono", Monaco, "Bitstream Vera Sans Mono", "Andale Mono", "Courier New", Courier, "Liberation Mono", monospace, Fixed, Consolas, Terminal' + +## Font used for prompts. +## Type: Font +# c.fonts.prompts = '10pt sans-serif' + +## Font used in the statusbar. +## Type: Font +# c.fonts.statusbar = '10pt monospace' + +## Font used in the tab bar. +## Type: QtFont +# c.fonts.tabs = '10pt monospace' + +## Font family for cursive fonts. +## Type: FontFamily +# c.fonts.web.family.cursive = '' + +## Font family for fantasy fonts. +## Type: FontFamily +# c.fonts.web.family.fantasy = '' + +## Font family for fixed fonts. +## Type: FontFamily +# c.fonts.web.family.fixed = '' + +## Font family for sans-serif fonts. +## Type: FontFamily +# c.fonts.web.family.sans_serif = '' + +## Font family for serif fonts. +## Type: FontFamily +# c.fonts.web.family.serif = '' + +## Font family for standard fonts. +## Type: FontFamily +# c.fonts.web.family.standard = '' + +## Default font size (in pixels) for regular text. +## Type: Int +# c.fonts.web.size.default = 16 + +## Default font size (in pixels) for fixed-pitch text. +## Type: Int +# c.fonts.web.size.default_fixed = 13 + +## Hard minimum font size (in pixels). +## Type: Int +# c.fonts.web.size.minimum = 0 + +## Minimum logical font size (in pixels) that is applied when zooming +## out. +## Type: Int +# c.fonts.web.size.minimum_logical = 6 + +## When a hint can be automatically followed without pressing Enter. +## Type: String +## Valid values: +## - always: Auto-follow whenever there is only a single hint on a page. +## - unique-match: Auto-follow whenever there is a unique non-empty match in either the hint string (word mode) or filter (number mode). +## - full-match: Follow the hint when the user typed the whole hint (letter, word or number mode) or the element's text (only in number mode). +## - never: The user will always need to press Enter to follow a hint. +# c.hints.auto_follow = 'unique-match' + +## Duration (in milliseconds) to ignore normal-mode key bindings after a +## successful auto-follow. +## Type: Int +# c.hints.auto_follow_timeout = 0 + +## CSS border value for hints. +## Type: String +# c.hints.border = '1px solid #E3BE23' + +## Characters used for hint strings. +## Type: UniqueCharString +# c.hints.chars = 'asdfghjkl' + +## Dictionary file to be used by the word hints. +## Type: File +# c.hints.dictionary = '/usr/share/dict/words' + +## Which implementation to use to find elements to hint. +## Type: String +## Valid values: +## - javascript: Better but slower +## - python: Slightly worse but faster +# c.hints.find_implementation = 'python' + +## Hide unmatched hints in rapid mode. +## Type: Bool +# c.hints.hide_unmatched_rapid_hints = True + +## Minimum number of characters used for hint strings. +## Type: Int +# c.hints.min_chars = 1 + +## Mode to use for hints. +## Type: String +## Valid values: +## - number: Use numeric hints. (In this mode you can also type letters from the hinted element to filter and reduce the number of elements that are hinted.) +## - letter: Use the characters in the `hints.chars` setting. +## - word: Use hints words based on the html elements and the extra words. +c.hints.mode = 'number' + +## Comma-separated list of regular expressions to use for 'next' links. +## Type: List of Regex +# c.hints.next_regexes = ['\\bnext\\b', '\\bmore\\b', '\\bnewer\\b', '\\b[>→≫]\\b', '\\b(>>|»)\\b', '\\bcontinue\\b'] + +## Comma-separated list of regular expressions to use for 'prev' links. +## Type: List of Regex +# c.hints.prev_regexes = ['\\bprev(ious)?\\b', '\\bback\\b', '\\bolder\\b', '\\b[<←≪]\\b', '\\b(<<|«)\\b'] + +## Scatter hint key chains (like Vimium) or not (like dwb). Ignored for +## number hints. +## Type: Bool +# c.hints.scatter = True + +## Make characters in hint strings uppercase. +## Type: Bool +# c.hints.uppercase = False + +## Maximum time (in minutes) between two history items for them to be +## considered being from the same browsing session. Items with less time +## between them are grouped when being displayed in `:history`. Use -1 to +## disable separation. +## Type: Int +# c.history_gap_interval = 30 + +## Which unbound keys to forward to the webview in normal mode. +## Type: String +## Valid values: +## - all: Forward all unbound keys. +## - auto: Forward unbound non-alphanumeric keys. +## - none: Don't forward any keys. +# c.input.forward_unbound_keys = 'auto' + +## Enter insert mode if an editable element is clicked. +## Type: Bool +# c.input.insert_mode.auto_enter = True + +## Leave insert mode if a non-editable element is clicked. +## Type: Bool +# c.input.insert_mode.auto_leave = True + +## Automatically enter insert mode if an editable element is focused +## after loading the page. +## Type: Bool +# c.input.insert_mode.auto_load = False + +## Switch to insert mode when clicking flash and other plugins. +## Type: Bool +# c.input.insert_mode.plugins = False + +## Include hyperlinks in the keyboard focus chain when tabbing. +## Type: Bool +# c.input.links_included_in_focus_chain = True + +## Timeout (in milliseconds) for partially typed key bindings. If the +## current input forms only partial matches, the keystring will be +## cleared after this time. +## Type: Int +# c.input.partial_timeout = 5000 + +## Enable Opera-like mouse rocker gestures. This disables the context +## menu. +## Type: Bool +# c.input.rocker_gestures = False + +## Enable spatial navigation. Spatial navigation consists in the ability +## to navigate between focusable elements in a Web page, such as +## hyperlinks and form controls, by using Left, Right, Up and Down arrow +## keys. For example, if the user presses the Right key, heuristics +## determine whether there is an element he might be trying to reach +## towards the right and which element he probably wants. +## Type: Bool +# c.input.spatial_navigation = False + +## Keychains that shouldn't be shown in the keyhint dialog. Globs are +## supported, so `;*` will blacklist all keychains starting with `;`. Use +## `*` to disable keyhints. +## Type: List of String +# c.keyhint.blacklist = [] + +## Time (in milliseconds) from pressing a key to seeing the keyhint +## dialog. +## Type: Int +# c.keyhint.delay = 500 + +## Rounding radius (in pixels) for the edges of the keyhint dialog. +## Type: Int +# c.keyhint.radius = 6 + +## Duration (in milliseconds) to show messages in the statusbar for. Set +## to 0 to never clear messages. +## Type: Int +# c.messages.timeout = 2000 + +## How to open links in an existing instance if a new one is launched. +## This happens when e.g. opening a link from a terminal. See +## `new_instance_open_target_window` to customize in which window the +## link is opened in. +## Type: String +## Valid values: +## - tab: Open a new tab in the existing window and activate the window. +## - tab-bg: Open a new background tab in the existing window and activate the window. +## - tab-silent: Open a new tab in the existing window without activating the window. +## - tab-bg-silent: Open a new background tab in the existing window without activating the window. +## - window: Open in a new window. +# c.new_instance_open_target = 'tab' + +## Which window to choose when opening links as new tabs. When +## `new_instance_open_target` is not set to `window`, this is ignored. +## Type: String +## Valid values: +## - first-opened: Open new tabs in the first (oldest) opened window. +## - last-opened: Open new tabs in the last (newest) opened window. +## - last-focused: Open new tabs in the most recently focused window. +## - last-visible: Open new tabs in the most recently visible window. +# c.new_instance_open_target_window = 'last-focused' + +## Show a filebrowser in upload/download prompts. +## Type: Bool +# c.prompt.filebrowser = True + +## Rounding radius (in pixels) for the edges of prompts. +## Type: Int +# c.prompt.radius = 8 + +## Additional arguments to pass to Qt, without leading `--`. With +## QtWebEngine, some Chromium arguments (see +## https://peter.sh/experiments/chromium-command-line-switches/ for a +## list) will work. +## Type: List of String +# c.qt.args = [] + +## Force a Qt platform to use. This sets the `QT_QPA_PLATFORM` +## environment variable and is useful to force using the XCB plugin when +## running QtWebEngine on Wayland. +## Type: String +# c.qt.force_platform = None + +## Force software rendering for QtWebEngine. This is needed for +## QtWebEngine to work with Nouveau drivers. +## Type: Bool +# c.qt.force_software_rendering = False + +## Turn on Qt HighDPI scaling. This is equivalent to setting +## QT_AUTO_SCREEN_SCALE_FACTOR=1 in the environment. It's off by default +## as it can cause issues with some bitmap fonts. As an alternative to +## this, it's possible to set font sizes and the `zoom.default` setting. +## Type: Bool +# c.qt.highdpi = False + +## Show a scrollbar. +## Type: Bool +# c.scrolling.bar = False + +## Enable smooth scrolling for web pages. Note smooth scrolling does not +## work with the `:scroll-px` command. +## Type: Bool +# c.scrolling.smooth = False + +## When to find text on a page case-insensitively. +## Type: String +## Valid values: +## - always: Search case-insensitively. +## - never: Search case-sensitively. +## - smart: Search case-sensitively if there are capital characters. +# c.search.ignore_case = 'smart' + +## Find text on a page incrementally, renewing the search for each typed +## character. +## Type: Bool +# c.search.incremental = True + +## Name of the session to save by default. If this is set to null, the +## session which was last loaded is saved. +## Type: SessionName +# c.session.default_name = None + +## Load a restored tab as soon as it takes focus. +## Type: Bool +# c.session.lazy_restore = False + +## Languages to use for spell checking. You can check for available +## languages and install dictionaries using scripts/dictcli.py. Run the +## script with -h/--help for instructions. +## Type: List of String +## Valid values: +## - af-ZA: Afrikaans (South Africa) +## - bg-BG: Bulgarian (Bulgaria) +## - ca-ES: Catalan (Spain) +## - cs-CZ: Czech (Czech Republic) +## - da-DK: Danish (Denmark) +## - de-DE: German (Germany) +## - el-GR: Greek (Greece) +## - en-AU: English (Australia) +## - en-CA: English (Canada) +## - en-GB: English (United Kingdom) +## - en-US: English (United States) +## - es-ES: Spanish (Spain) +## - et-EE: Estonian (Estonia) +## - fa-IR: Farsi (Iran) +## - fo-FO: Faroese (Faroe Islands) +## - fr-FR: French (France) +## - he-IL: Hebrew (Israel) +## - hi-IN: Hindi (India) +## - hr-HR: Croatian (Croatia) +## - hu-HU: Hungarian (Hungary) +## - id-ID: Indonesian (Indonesia) +## - it-IT: Italian (Italy) +## - ko: Korean +## - lt-LT: Lithuanian (Lithuania) +## - lv-LV: Latvian (Latvia) +## - nb-NO: Norwegian (Norway) +## - nl-NL: Dutch (Netherlands) +## - pl-PL: Polish (Poland) +## - pt-BR: Portuguese (Brazil) +## - pt-PT: Portuguese (Portugal) +## - ro-RO: Romanian (Romania) +## - ru-RU: Russian (Russia) +## - sh: Serbo-Croatian +## - sk-SK: Slovak (Slovakia) +## - sl-SI: Slovenian (Slovenia) +## - sq: Albanian +## - sr: Serbian +## - sv-SE: Swedish (Sweden) +## - ta-IN: Tamil (India) +## - tg-TG: Tajik (Tajikistan) +## - tr-TR: Turkish (Turkey) +## - uk-UA: Ukrainian (Ukraine) +## - vi-VN: Vietnamese (Viet Nam) +# c.spellcheck.languages = [] + +## Hide the statusbar unless a message is shown. +## Type: Bool +# c.statusbar.hide = False + +## Padding (in pixels) for the statusbar. +## Type: Padding +# c.statusbar.padding = {'top': 1, 'bottom': 1, 'left': 0, 'right': 0} + +## Position of the status bar. +## Type: VerticalPosition +## Valid values: +## - top +## - bottom +# c.statusbar.position = 'bottom' + +## List of widgets displayed in the statusbar. +## Type: List of String +## Valid values: +## - url: Current page URL. +## - scroll: Percentage of the current page position like `10%`. +## - scroll_raw: Raw percentage of the current page position like `10`. +## - history: Display an arrow when possible to go back/forward in history. +## - tabs: Current active tab, e.g. `2`. +## - keypress: Display pressed keys when composing a vi command. +## - progress: Progress bar for the current page loading. +# c.statusbar.widgets = ['keypress', 'url', 'scroll', 'history', 'tabs', 'progress'] + +## Open new tabs (middleclick/ctrl+click) in the background. +## Type: Bool +# c.tabs.background = False + +## Mouse button with which to close tabs. +## Type: String +## Valid values: +## - right: Close tabs on right-click. +## - middle: Close tabs on middle-click. +## - none: Don't close tabs using the mouse. +# c.tabs.close_mouse_button = 'middle' + +## How to behave when the close mouse button is pressed on the tab bar. +## Type: String +## Valid values: +## - new-tab: Open a new tab. +## - close-current: Close the current tab. +## - close-last: Close the last tab. +## - ignore: Don't do anything. +# c.tabs.close_mouse_button_on_bar = 'new-tab' + +## Scaling factor for favicons in the tab bar. The tab size is unchanged, +## so big favicons also require extra `tabs.padding`. +## Type: Float +# c.tabs.favicons.scale = 1.0 + +## Show favicons in the tab bar. +## Type: Bool +# c.tabs.favicons.show = True + +## Padding (in pixels) for tab indicators. +## Type: Padding +# c.tabs.indicator.padding = {'top': 2, 'bottom': 2, 'left': 0, 'right': 4} + +## Width (in pixels) of the progress indicator (0 to disable). +## Type: Int +# c.tabs.indicator.width = 3 + +## How to behave when the last tab is closed. +## Type: String +## Valid values: +## - ignore: Don't do anything. +## - blank: Load a blank page. +## - startpage: Load the start page. +## - default-page: Load the default page. +## - close: Close the window. +# c.tabs.last_close = 'ignore' + +## When switching tabs, what input mode is applied. +## Type: String +## Valid values: +## - persist: Retain the current mode. +## - restore: Restore previously saved mode. +## - normal: Always revert to normal mode. +# c.tabs.mode_on_change = 'normal' + +## Switch between tabs using the mouse wheel. +## Type: Bool +# c.tabs.mousewheel_switching = True + +## Position of new tabs opened from another tab. +## Type: NewTabPosition +## Valid values: +## - prev: Before the current tab. +## - next: After the current tab. +## - first: At the beginning. +## - last: At the end. +# c.tabs.new_position.related = 'next' + +## Position of new tabs which aren't opened from another tab. +## Type: NewTabPosition +## Valid values: +## - prev: Before the current tab. +## - next: After the current tab. +## - first: At the beginning. +## - last: At the end. +# c.tabs.new_position.unrelated = 'last' + +## Padding (in pixels) around text for tabs. +## Type: Padding +# c.tabs.padding = {'top': 0, 'bottom': 0, 'left': 5, 'right': 5} + +## Shrink pinned tabs down to their contents. +## Type: Bool +# c.tabs.pinned.shrink = True + +## Position of the tab bar. +## Type: Position +## Valid values: +## - top +## - bottom +## - left +## - right +# c.tabs.position = 'top' + +## Which tab to select when the focused tab is removed. +## Type: SelectOnRemove +## Valid values: +## - prev: Select the tab which came before the closed one (left in horizontal, above in vertical). +## - next: Select the tab which came after the closed one (right in horizontal, below in vertical). +## - last-used: Select the previously selected tab. +# c.tabs.select_on_remove = 'next' + +## When to show the tab bar. +## Type: String +## Valid values: +## - always: Always show the tab bar. +## - never: Always hide the tab bar. +## - multiple: Hide the tab bar if only one tab is open. +## - switching: Show the tab bar when switching tabs. +# c.tabs.show = 'always' + +## Duration (in milliseconds) to show the tab bar before hiding it when +## tabs.show is set to 'switching'. +## Type: Int +# c.tabs.show_switching_delay = 800 + +## Open a new window for every tab. +## Type: Bool +# c.tabs.tabs_are_windows = False + +## Alignment of the text inside of tabs. +## Type: TextAlignment +## Valid values: +## - left +## - right +## - center +# c.tabs.title.alignment = 'left' + +## Format to use for the tab title. The following placeholders are +## defined: * `{perc}`: Percentage as a string like `[10%]`. * +## `{perc_raw}`: Raw percentage, e.g. `10`. * `{title}`: Title of the +## current web page. * `{title_sep}`: The string ` - ` if a title is set, +## empty otherwise. * `{index}`: Index of this tab. * `{id}`: Internal +## tab ID of this tab. * `{scroll_pos}`: Page scroll position. * +## `{host}`: Host of the current web page. * `{backend}`: Either +## ''webkit'' or ''webengine'' * `{private}`: Indicates when private mode +## is enabled. * `{current_url}`: URL of the current web page. * +## `{protocol}`: Protocol (http/https/...) of the current web page. +## Type: FormatString +# c.tabs.title.format = '{index}: {title}' + +## Format to use for the tab title for pinned tabs. The same placeholders +## like for `tabs.title.format` are defined. +## Type: FormatString +# c.tabs.title.format_pinned = '{index}' + +## Width (in pixels or as percentage of the window) of the tab bar if +## it's vertical. +## Type: PercOrInt +# c.tabs.width = '20%' + +## Wrap when changing tabs. +## Type: Bool +# c.tabs.wrap = True + +## What search to start when something else than a URL is entered. +## Type: String +## Valid values: +## - naive: Use simple/naive check. +## - dns: Use DNS requests (might be slow!). +## - never: Never search automatically. +# c.url.auto_search = 'naive' + +## Page to open if :open -t/-b/-w is used without URL. Use `about:blank` +## for a blank page. +## Type: FuzzyUrl +# c.url.default_page = 'https://start.duckduckgo.com/' + +## URL segments where `:navigate increment/decrement` will search for a +## number. +## Type: FlagList +## Valid values: +## - host +## - port +## - path +## - query +## - anchor +# c.url.incdec_segments = ['path', 'query'] + +## Search engines which can be used via the address bar. Maps a search +## engine name (such as `DEFAULT`, or `ddg`) to a URL with a `{}` +## placeholder. The placeholder will be replaced by the search term, use +## `{{` and `}}` for literal `{`/`}` signs. The search engine named +## `DEFAULT` is used when `url.auto_search` is turned on and something +## else than a URL was entered to be opened. Other search engines can be +## used by prepending the search engine name to the search term, e.g. +## `:open google qutebrowser`. +## Type: Dict + +c.url.searchengines = { 'DEFAULT':'https://duckduckgo.com/?q={}', 'aw':'https://wiki.archlinux.org/?q={}', 'g':'https://www.google.com/?q={}'} + +## Page(s) to open at the start. +## Type: List of FuzzyUrl, or FuzzyUrl +# c.url.start_pages = ['https://start.duckduckgo.com'] + +## URL parameters to strip with `:yank url`. +## Type: List of String +# c.url.yank_ignored_parameters = ['ref', 'utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content'] + +## Hide the window decoration when using wayland. +## Type: Bool +# c.window.hide_wayland_decoration = False + +## Format to use for the window title. The same placeholders like for +## `tabs.title.format` are defined. +## Type: FormatString +# c.window.title_format = '{perc}{title}{title_sep}qutebrowser' + +## Default zoom level. +## Type: Perc +# c.zoom.default = '100%' + +## Available zoom levels. +## Type: List of Perc +# c.zoom.levels = ['25%', '33%', '50%', '67%', '75%', '90%', '100%', '110%', '125%', '150%', '175%', '200%', '250%', '300%', '400%', '500%'] + +## Number of zoom increments to divide the mouse wheel movements to. +## Type: Int +# c.zoom.mouse_divider = 512 + +## Apply the zoom factor on a frame only to the text or to all content. +## Type: Bool +# c.zoom.text_only = False + +## Bindings for normal mode +config.bind(',c', 'hint links spawn mpv --ytdl --ytdl-raw-options=username="v.graffagnino@btinternet.com",password="N9\Ch0n\H@n9" --cookies-file=~/.local/share/qutebrowser/webengine/Cookies --ytdl-raw-options=username=v\.graffagnino\@btinternet\.com,password=N9\\Ch0n\\H\@n9 {hint-url}') +config.bind(',m', 'hint links spawn mpv --ytdl {hint-url} --load-unsafe-playlists') +config.bind('m', 'hint links spawn mpv --ytdl -slang=en {hint-url} --load-unsafe-playlists') +#config.bind('m', 'spawn umpv {url}') +config.bind(',M', 'hint links spawn umpv {hint-url}') +config.bind(',t', 'hint links spawn transmission-remote --add {hint-url}') +config.bind(',y', 'hint links spawn /usr/bin/ts youtube-dl {hint-url}') +config.bind(',Y', 'hint links spawn /usr/bin/urxvt -e mpsyt dlurl {hint-url}') +config.bind(',q', 'hint links spawn echo "mpsyt dlurl {hint-url}" >> ~/ytdl.list') +# config.bind("'", 'enter-mode jump_mark') +# config.bind('+', 'zoom-in') +# config.bind('-', 'zoom-out') +# config.bind('.', 'repeat-command') +# config.bind('/', 'set-cmd-text /') +# config.bind(':', 'set-cmd-text :') +# config.bind(';I', 'hint images tab') +# config.bind(';O', 'hint links fill :open -t -r {hint-url}') +# config.bind(';R', 'hint --rapid links window') +# config.bind(';Y', 'hint links yank-primary') +# config.bind(';b', 'hint all tab-bg') +# config.bind(';d', 'hint links download') +# config.bind(';f', 'hint all tab-fg') +# config.bind(';h', 'hint all hover') +# config.bind(';i', 'hint images') +# config.bind(';o', 'hint links fill :open {hint-url}') +# config.bind(';r', 'hint --rapid links tab-bg') +# config.bind(';t', 'hint inputs') +# config.bind(';y', 'hint links yank') +# config.bind('<Alt-1>', 'tab-focus 1') +# config.bind('<Alt-2>', 'tab-focus 2') +# config.bind('<Alt-3>', 'tab-focus 3') +# config.bind('<Alt-4>', 'tab-focus 4') +# config.bind('<Alt-5>', 'tab-focus 5') +# config.bind('<Alt-6>', 'tab-focus 6') +# config.bind('<Alt-7>', 'tab-focus 7') +# config.bind('<Alt-8>', 'tab-focus 8') +# config.bind('<Alt-9>', 'tab-focus -1') +# config.bind('<Ctrl-A>', 'navigate increment') +# config.bind('<Ctrl-Alt-p>', 'print') +# config.bind('<Ctrl-B>', 'scroll-page 0 -1') +# config.bind('<Ctrl-D>', 'scroll-page 0 0.5') +# config.bind('<Ctrl-F5>', 'reload -f') +# config.bind('<Ctrl-F>', 'scroll-page 0 1') +# config.bind('<Ctrl-N>', 'open -w') +# config.bind('<Ctrl-PgDown>', 'tab-next') +# config.bind('<Ctrl-PgUp>', 'tab-prev') +# config.bind('<Ctrl-Q>', 'quit') +# config.bind('<Ctrl-Return>', 'follow-selected -t') +# config.bind('<Ctrl-Shift-N>', 'open -p') +# config.bind('<Ctrl-Shift-T>', 'undo') +# config.bind('<Ctrl-Shift-W>', 'close') +# config.bind('<Ctrl-T>', 'open -t') +# config.bind('<Ctrl-Tab>', 'tab-focus last') +# config.bind('<Ctrl-U>', 'scroll-page 0 -0.5') +# config.bind('<Ctrl-V>', 'enter-mode passthrough') +# config.bind('<Ctrl-W>', 'tab-close') +# config.bind('<Ctrl-X>', 'navigate decrement') +# config.bind('<Ctrl-^>', 'tab-focus last') +# config.bind('<Ctrl-h>', 'home') +# config.bind('<Ctrl-p>', 'tab-pin') +# config.bind('<Ctrl-s>', 'stop') +# config.bind('<Escape>', 'clear-keychain ;; search ;; fullscreen --leave') +# config.bind('<F11>', 'fullscreen') +# config.bind('<F5>', 'reload') +# config.bind('<Return>', 'follow-selected') +# config.bind('<back>', 'back') +# config.bind('<forward>', 'forward') +# config.bind('=', 'zoom') +# config.bind('?', 'set-cmd-text ?') +# config.bind('@', 'run-macro') +# config.bind('B', 'set-cmd-text -s :quickmark-load -t') +# config.bind('D', 'tab-close -o') +# config.bind('F', 'hint all tab') +# config.bind('G', 'scroll-to-perc') +# config.bind('H', 'back') +config.bind('J', 'tab-prev') +config.bind('K', 'tab-next') +# config.bind('L', 'forward') +# config.bind('M', 'bookmark-add') +# config.bind('N', 'search-prev') +# config.bind('O', 'set-cmd-text -s :open -t') +# config.bind('PP', 'open -t -- {primary}') +# config.bind('Pp', 'open -t -- {clipboard}') +# config.bind('R', 'reload -f') +# config.bind('Sb', 'open qute://bookmarks#bookmarks') +# config.bind('Sh', 'open qute://history') +# config.bind('Sq', 'open qute://bookmarks') +# config.bind('Ss', 'open qute://settings') +# config.bind('T', 'tab-focus') +# config.bind('ZQ', 'quit') +# config.bind('ZZ', 'quit --save') +# config.bind('[[', 'navigate prev') +# config.bind(']]', 'navigate next') +# config.bind('`', 'enter-mode set_mark') +# config.bind('ad', 'download-cancel') +# config.bind('b', 'set-cmd-text -s :quickmark-load') +# config.bind('cd', 'download-clear') +# config.bind('co', 'tab-only') +# config.bind('d', 'tab-close') +# config.bind('f', 'hint') +# config.bind('g$', 'tab-focus -1') +# config.bind('g0', 'tab-focus 1') +# config.bind('gB', 'set-cmd-text -s :bookmark-load -t') +# config.bind('gC', 'tab-clone') +# config.bind('gO', 'set-cmd-text :open -t -r {url:pretty}') +# config.bind('gU', 'navigate up -t') +# config.bind('g^', 'tab-focus 1') +# config.bind('ga', 'open -t') +# config.bind('gb', 'set-cmd-text -s :bookmark-load') +config.bind('gd', 'download') +config.bind('gf', 'view-source') +# config.bind('gg', 'scroll-to-perc 0') +# config.bind('gl', 'tab-move -') +# config.bind('gm', 'tab-move') +# config.bind('go', 'set-cmd-text :open {url:pretty}') +# config.bind('gr', 'tab-move +') +# config.bind('gt', 'set-cmd-text -s :buffer') +# config.bind('gu', 'navigate up') +# config.bind('h', 'scroll left') +# config.bind('i', 'enter-mode insert') +# config.bind('j', 'scroll down') +# config.bind('k', 'scroll up') +# config.bind('l', 'scroll right') +# config.bind('m', 'quickmark-save') +# config.bind('n', 'search-next') +# config.bind('o', 'set-cmd-text -s :open') +# config.bind('pP', 'open -- {primary}') +# config.bind('pp', 'open -- {clipboard}') +# config.bind('q', 'record-macro') +# config.bind('r', 'reload') +# config.bind('sf', 'save') +# config.bind('sk', 'set-cmd-text -s :bind') +# config.bind('sl', 'set-cmd-text -s :set -t') +# config.bind('ss', 'set-cmd-text -s :set') +# config.bind('tPH', 'config-cycle -p -u *://*.{url:host}/* content.plugins ;; reload') +# config.bind('tPh', 'config-cycle -p -u *://{url:host}/* content.plugins ;; reload') +# config.bind('tPu', 'config-cycle -p -u {url} content.plugins ;; reload') +config.bind('tSH', 'config-cycle -p -u *://*.{url:host}/* content.javascript.enabled ;; reload') +config.bind('tSh', 'config-cycle -p -u *://{url:host}/* content.javascript.enabled ;; reload') +config.bind('tSu', 'config-cycle -p -u {url} content.javascript.enabled ;; reload') +# config.bind('th', 'back -t') +# config.bind('tl', 'forward -t') +# config.bind('tpH', 'config-cycle -p -t -u *://*.{url:host}/* content.plugins ;; reload') +# config.bind('tph', 'config-cycle -p -t -u *://{url:host}/* content.plugins ;; reload') +# config.bind('tpu', 'config-cycle -p -t -u {url} content.plugins ;; reload') +# config.bind('tsH', 'config-cycle -p -t -u *://*.{url:host}/* content.javascript.enabled ;; reload') +# config.bind('tsh', 'config-cycle -p -t -u *://{url:host}/* content.javascript.enabled ;; reload') +# config.bind('tsu', 'config-cycle -p -t -u {url} content.javascript.enabled ;; reload') +# config.bind('u', 'undo') +# config.bind('v', 'enter-mode caret') +# config.bind('wB', 'set-cmd-text -s :bookmark-load -w') +# config.bind('wO', 'set-cmd-text :open -w {url:pretty}') +# config.bind('wP', 'open -w -- {primary}') +# config.bind('wb', 'set-cmd-text -s :quickmark-load -w') +# config.bind('wf', 'hint all window') +# config.bind('wh', 'back -w') +# config.bind('wi', 'inspector') +# config.bind('wl', 'forward -w') +# config.bind('wo', 'set-cmd-text -s :open -w') +# config.bind('wp', 'open -w -- {clipboard}') +# config.bind('xO', 'set-cmd-text :open -b -r {url:pretty}') +# config.bind('xo', 'set-cmd-text -s :open -b') +# config.bind('yD', 'yank domain -s') +# config.bind('yP', 'yank pretty-url -s') +# config.bind('yT', 'yank title -s') +# config.bind('yY', 'yank -s') +# config.bind('yd', 'yank domain') +# config.bind('yp', 'yank pretty-url') +# config.bind('yt', 'yank title') +# config.bind('yy', 'yank') +# config.bind('{{', 'navigate prev -t') +# config.bind('}}', 'navigate next -t') + +## Bindings for caret mode +# config.bind('$', 'move-to-end-of-line', mode='caret') +# config.bind('0', 'move-to-start-of-line', mode='caret') +# config.bind('<Ctrl-Space>', 'drop-selection', mode='caret') +# config.bind('<Escape>', 'leave-mode', mode='caret') +# config.bind('<Return>', 'yank selection', mode='caret') +# config.bind('<Space>', 'toggle-selection', mode='caret') +# config.bind('G', 'move-to-end-of-document', mode='caret') +# config.bind('H', 'scroll left', mode='caret') +# config.bind('J', 'scroll down', mode='caret') +# config.bind('K', 'scroll up', mode='caret') +# config.bind('L', 'scroll right', mode='caret') +# config.bind('Y', 'yank selection -s', mode='caret') +# config.bind('[', 'move-to-start-of-prev-block', mode='caret') +# config.bind(']', 'move-to-start-of-next-block', mode='caret') +# config.bind('b', 'move-to-prev-word', mode='caret') +# config.bind('c', 'enter-mode normal', mode='caret') +# config.bind('e', 'move-to-end-of-word', mode='caret') +# config.bind('gg', 'move-to-start-of-document', mode='caret') +# config.bind('h', 'move-to-prev-char', mode='caret') +# config.bind('j', 'move-to-next-line', mode='caret') +# config.bind('k', 'move-to-prev-line', mode='caret') +# config.bind('l', 'move-to-next-char', mode='caret') +# config.bind('v', 'toggle-selection', mode='caret') +# config.bind('w', 'move-to-next-word', mode='caret') +# config.bind('y', 'yank selection', mode='caret') +# config.bind('{', 'move-to-end-of-prev-block', mode='caret') +# config.bind('}', 'move-to-end-of-next-block', mode='caret') + +## Bindings for command mode +# config.bind('<Alt-B>', 'rl-backward-word', mode='command') +# config.bind('<Alt-Backspace>', 'rl-backward-kill-word', mode='command') +# config.bind('<Alt-D>', 'rl-kill-word', mode='command') +# config.bind('<Alt-F>', 'rl-forward-word', mode='command') +# config.bind('<Ctrl-?>', 'rl-delete-char', mode='command') +# config.bind('<Ctrl-A>', 'rl-beginning-of-line', mode='command') +# config.bind('<Ctrl-B>', 'rl-backward-char', mode='command') +# config.bind('<Ctrl-C>', 'completion-item-yank', mode='command') +# config.bind('<Ctrl-D>', 'completion-item-del', mode='command') +# config.bind('<Ctrl-E>', 'rl-end-of-line', mode='command') +# config.bind('<Ctrl-F>', 'rl-forward-char', mode='command') +# config.bind('<Ctrl-H>', 'rl-backward-delete-char', mode='command') +# config.bind('<Ctrl-K>', 'rl-kill-line', mode='command') +# config.bind('<Ctrl-N>', 'command-history-next', mode='command') +# config.bind('<Ctrl-P>', 'command-history-prev', mode='command') +# config.bind('<Ctrl-Return>', 'command-accept --rapid', mode='command') +# config.bind('<Ctrl-Shift-C>', 'completion-item-yank --sel', mode='command') +# config.bind('<Ctrl-Shift-Tab>', 'completion-item-focus prev-category', mode='command') +# config.bind('<Ctrl-Tab>', 'completion-item-focus next-category', mode='command') +# config.bind('<Ctrl-U>', 'rl-unix-line-discard', mode='command') +# config.bind('<Ctrl-W>', 'rl-unix-word-rubout', mode='command') +# config.bind('<Ctrl-Y>', 'rl-yank', mode='command') +# config.bind('<Down>', 'completion-item-focus --history next', mode='command') +# config.bind('<Escape>', 'leave-mode', mode='command') +# config.bind('<Return>', 'command-accept', mode='command') +# config.bind('<Shift-Delete>', 'completion-item-del', mode='command') +# config.bind('<Shift-Tab>', 'completion-item-focus prev', mode='command') +# config.bind('<Tab>', 'completion-item-focus next', mode='command') +# config.bind('<Up>', 'completion-item-focus --history prev', mode='command') + +## Bindings for hint mode +# config.bind('<Ctrl-B>', 'hint all tab-bg', mode='hint') +# config.bind('<Ctrl-F>', 'hint links', mode='hint') +# config.bind('<Ctrl-R>', 'hint --rapid links tab-bg', mode='hint') +# config.bind('<Escape>', 'leave-mode', mode='hint') +# config.bind('<Return>', 'follow-hint', mode='hint') + +## Bindings for insert mode +# config.bind('<Ctrl-E>', 'open-editor', mode='insert') +# config.bind('<Escape>', 'leave-mode', mode='insert') +# config.bind('<Shift-Ins>', 'insert-text {primary}', mode='insert') +config.bind('<Ctrl-i>', 'spawn --userscript qute-keepass -p ~/Documents/20151120.kdbx', mode='insert') + +## Bindings for passthrough mode +# config.bind('<Ctrl-V>', 'leave-mode', mode='passthrough') + +## Bindings for prompt mode +# config.bind('<Alt-B>', 'rl-backward-word', mode='prompt') +# config.bind('<Alt-Backspace>', 'rl-backward-kill-word', mode='prompt') +# config.bind('<Alt-D>', 'rl-kill-word', mode='prompt') +# config.bind('<Alt-F>', 'rl-forward-word', mode='prompt') +# config.bind('<Alt-Shift-Y>', 'prompt-yank --sel', mode='prompt') +# config.bind('<Alt-Y>', 'prompt-yank', mode='prompt') +# config.bind('<Ctrl-?>', 'rl-delete-char', mode='prompt') +# config.bind('<Ctrl-A>', 'rl-beginning-of-line', mode='prompt') +# config.bind('<Ctrl-B>', 'rl-backward-char', mode='prompt') +# config.bind('<Ctrl-E>', 'rl-end-of-line', mode='prompt') +# config.bind('<Ctrl-F>', 'rl-forward-char', mode='prompt') +# config.bind('<Ctrl-H>', 'rl-backward-delete-char', mode='prompt') +# config.bind('<Ctrl-K>', 'rl-kill-line', mode='prompt') +# config.bind('<Ctrl-U>', 'rl-unix-line-discard', mode='prompt') +# config.bind('<Ctrl-W>', 'rl-unix-word-rubout', mode='prompt') +# config.bind('<Ctrl-X>', 'prompt-open-download', mode='prompt') +# config.bind('<Ctrl-Y>', 'rl-yank', mode='prompt') +# config.bind('<Down>', 'prompt-item-focus next', mode='prompt') +# config.bind('<Escape>', 'leave-mode', mode='prompt') +# config.bind('<Return>', 'prompt-accept', mode='prompt') +# config.bind('<Shift-Tab>', 'prompt-item-focus prev', mode='prompt') +# config.bind('<Tab>', 'prompt-item-focus next', mode='prompt') +# config.bind('<Up>', 'prompt-item-focus prev', mode='prompt') + +## Bindings for register mode +# config.bind('<Escape>', 'leave-mode', mode='register') + +## Bindings for yesno mode +# config.bind('<Alt-Shift-Y>', 'prompt-yank --sel', mode='yesno') +# config.bind('<Alt-Y>', 'prompt-yank', mode='yesno') +# config.bind('<Escape>', 'leave-mode', mode='yesno') +# config.bind('<Return>', 'prompt-accept', mode='yesno') +# config.bind('n', 'prompt-accept no', mode='yesno') +# config.bind('y', 'prompt-accept yes', mode='yesno') +config.source('nord-qutebrowser.py') diff --git a/.config/qutebrowser/jupyter-profile/config/bookmarks/urls b/.config/qutebrowser/jupyter-profile/config/bookmarks/urls new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/.config/qutebrowser/jupyter-profile/config/bookmarks/urls diff --git a/.config/qutebrowser/jupyter-profile/config/quickmarks b/.config/qutebrowser/jupyter-profile/config/quickmarks new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/.config/qutebrowser/jupyter-profile/config/quickmarks diff --git a/.config/qutebrowser/jupyter-profile/keys.conf b/.config/qutebrowser/jupyter-profile/keys.conf new file mode 100755 index 0000000..06fc600 --- /dev/null +++ b/.config/qutebrowser/jupyter-profile/keys.conf @@ -0,0 +1,709 @@ +# vim: ft=conf +# +# In this config file, qutebrowser's key bindings are configured. +# The format looks like this: +# +# [keymode] +# +# command +# keychain +# keychain2 +# ... +# +# All blank lines and lines starting with '#' are ignored. +# Inline-comments are not permitted. +# +# keymode is a comma separated list of modes in which the key binding should be +# active. If keymode starts with !, the key binding is active in all modes +# except the listed modes. +# +# For special keys (can't be part of a keychain), enclose them in `<`...`>`. +# For modifiers, you can use either `-` or `+` as delimiters, and these names: +# +# * Control: `Control`, `Ctrl` +# * Meta: `Meta`, `Windows`, `Mod4` +# * Alt: `Alt`, `Mod1` +# * Shift: `Shift` +# +# For simple keys (no `<>`-signs), a capital letter means the key is pressed +# with Shift. For special keys (with `<>`-signs), you need to explicitly add +# `Shift-` to match a key pressed with shift. +# +# Note that default keybindings are always bound, and need to be explicitly +# unbound if you wish to remove them: +# +# <unbound> +# keychain +# keychain2 +# ... + +[!normal] + +leave-mode + <escape> + <ctrl-[> + +[normal] +# Keybindings for normal mode. + +clear-keychain ;; search ;; fullscreen --leave + <escape> + <ctrl-[> + +set-cmd-text -s :open + o + +set-cmd-text :open {url:pretty} + go + +set-cmd-text -s :open -t + O + +set-cmd-text :open -t -i {url:pretty} + gO + +set-cmd-text -s :open -b + xo + +set-cmd-text :open -b -i {url:pretty} + xO + +set-cmd-text -s :open -w + wo + +set-cmd-text :open -w {url:pretty} + wO + +set-cmd-text / + / + +set-cmd-text ? + ? + +set-cmd-text : + : + +open -t + ga + <ctrl-t> + +open -w + <ctrl-n> + +tab-close + d + <ctrl-w> + +tab-close -o + D + +tab-only + co + +tab-focus + T + +tab-move + gm + +tab-move - + gl + +tab-move + + gr + +tab-next + J + <ctrl-pgdown> + +tab-prev + K + <ctrl-pgup> + +tab-clone + gC + +reload + r + <f5> + +reload -f + R + <ctrl-f5> + +back + H + <back> + +back -t + th + +back -w + wh + +forward + L + <forward> + +forward -t + tl + +forward -w + wl + +fullscreen + <f11> + +hint + f + +hint all tab + F + +hint all window + wf + +hint all tab-bg + ;b + +hint all tab-fg + ;f + +hint all hover + ;h + +hint images + ;i + +hint images tab + ;I + +hint links fill :open {hint-url} + ;o + +hint links fill :open -t -i {hint-url} + ;O + +hint links yank + ;y + +hint links yank-primary + ;Y + +hint --rapid links tab-bg + ;r + +hint --rapid links window + ;R + +hint links download + ;d + +hint inputs + ;t + +scroll left + h + +scroll down + j + +scroll up + k + +scroll right + l + +undo + u + <ctrl-shift-t> + +scroll-perc 0 + gg + +scroll-perc + G + +search-next + n + +search-prev + N + +enter-mode insert + i + +enter-mode caret + v + +enter-mode set_mark + ` + +enter-mode jump_mark + ' + +yank + yy + +yank -s + yY + +yank title + yt + +yank title -s + yT + +yank domain + yd + +yank domain -s + yD + +yank pretty-url + yp + +yank pretty-url -s + yP + +open -- {clipboard} + pp + +open -- {primary} + pP + +open -t -- {clipboard} + Pp + +open -t -- {primary} + PP + +open -w -- {clipboard} + wp + +open -w -- {primary} + wP + +set-cmd-text -s :quickmark-load + b + +set-cmd-text -s :quickmark-load -t + B + +set-cmd-text -s :quickmark-load -w + wb + +set-cmd-text -s :bookmark-load + gb + +set-cmd-text -s :bookmark-load -t + gB + +set-cmd-text -s :bookmark-load -w + wB + +save + sf + +set-cmd-text -s :set + ss + +set-cmd-text -s :set -t + sl + +set-cmd-text -s :bind + sk + +zoom-out + - + +zoom-in + + + +zoom + = + +navigate prev + [[ + +navigate next + ]] + +navigate prev -t + {{ + +navigate next -t + }} + +navigate up + gu + +navigate up -t + gU + +navigate increment + <ctrl-a> + +navigate decrement + <ctrl-x> + +inspector + wi + +download + gd + +download-cancel + ad + +download-clear + cd + +view-source + gf + +set-cmd-text -s :buffer + gt + +tab-focus last + <ctrl-tab> + +enter-mode passthrough + <ctrl-v> + +quit + <ctrl-q> + +scroll-page 0 1 + <ctrl-f> + +scroll-page 0 -1 + <ctrl-b> + +scroll-page 0 0.5 + <ctrl-d> + +scroll-page 0 -0.5 + <ctrl-u> + +tab-focus 1 + <alt-1> + +tab-focus 2 + <alt-2> + +tab-focus 3 + <alt-3> + +tab-focus 4 + <alt-4> + +tab-focus 5 + <alt-5> + +tab-focus 6 + <alt-6> + +tab-focus 7 + <alt-7> + +tab-focus 8 + <alt-8> + +tab-focus 9 + <alt-9> + +home + <ctrl-h> + +stop + <ctrl-s> + +print + <ctrl-alt-p> + +open qute:settings + Ss + +follow-selected + <return> + <ctrl-m> + <ctrl-j> + <shift-return> + <enter> + <shift-enter> + +follow-selected -t + <ctrl-return> + <ctrl-enter> + +repeat-command + . + +record-macro + q + +run-macro + @ + +spawn mpv {url} + mpv + m + +hint links spawn umpv {hint-urls} + MPV + +hint links spawn mpv {hint-url} + M + +spawn tsp-ytdl {hint-url} + ;M + +wq + ZZ + +tab-focus -1 + g$ + +tab-pin + <ctrl-p> + +[insert] +# Keybindings for insert mode. +# Since normal keypresses are passed through, only special keys are +# supported in this mode. +# Useful hidden commands to map in this section: +# * `open-editor`: Open a texteditor with the focused field. +# * `paste-primary`: Paste primary selection at cursor position. + +open-editor + <ctrl-e> + +insert-text {primary} + <shift-ins> + +[hint] +# Keybindings for hint mode. +# Since normal keypresses are passed through, only special keys are +# supported in this mode. +# Useful hidden commands to map in this section: +# * `follow-hint`: Follow the currently selected hint. + +follow-hint + <return> + <ctrl-m> + <ctrl-j> + <shift-return> + <enter> + <shift-enter> + +hint --rapid links tab-bg + <ctrl-r> + +hint links + <ctrl-f> + +hint all tab-bg + <ctrl-b> + +[command] +# Keybindings for command mode. +# Since normal keypresses are passed through, only special keys are +# supported in this mode. +# Useful hidden commands to map in this section: +# * `command-history-prev`: Switch to previous command in history. +# * `command-history-next`: Switch to next command in history. +# * `completion-item-focus`: Select another item in completion. +# * `command-accept`: Execute the command currently in the commandline. + +command-history-prev + <ctrl-p> + +command-history-next + <ctrl-n> + +completion-item-focus prev + <shift-tab> + <up> + +completion-item-focus next + <tab> + <down> + +completion-item-focus next-category + <ctrl-tab> + +completion-item-focus prev-category + <ctrl-shift-tab> + +completion-item-del + <ctrl-d> + +command-accept + <return> + <ctrl-m> + <ctrl-j> + <shift-return> + <enter> + <shift-enter> + +[prompt] +# Keybindings for prompts in the status line. +# You can bind normal keys in this mode, but they will be only active +# when a yes/no-prompt is asked. For other prompt modes, you can only +# bind special keys. +# Useful hidden commands to map in this section: +# * `prompt-accept`: Confirm the entered value. +# * `prompt-accept yes`: Answer yes to a yes/no question. +# * `prompt-accept no`: Answer no to a yes/no question. + +prompt-accept + <return> + <ctrl-m> + <ctrl-j> + <shift-return> + <enter> + <shift-enter> + +prompt-accept yes + y + +prompt-accept no + n + +prompt-open-download + <ctrl-x> + +prompt-item-focus prev + <shift-tab> + <up> + +prompt-item-focus next + <tab> + <down> + +[command,prompt] + +rl-backward-char + <ctrl-b> + +rl-forward-char + <ctrl-f> + +rl-backward-word + <alt-b> + +rl-forward-word + <alt-f> + +rl-beginning-of-line + <ctrl-a> + +rl-end-of-line + <ctrl-e> + +rl-unix-line-discard + <ctrl-u> + +rl-kill-line + <ctrl-k> + +rl-kill-word + <alt-d> + +rl-unix-word-rubout + <ctrl-w> + +rl-backward-kill-word + <alt-backspace> + +rl-yank + <ctrl-y> + +rl-delete-char + <ctrl-?> + +rl-backward-delete-char + <ctrl-h> + +[caret] + +toggle-selection + v + <space> + +drop-selection + <ctrl-space> + +enter-mode normal + c + +move-to-next-line + j + +move-to-prev-line + k + +move-to-next-char + l + +move-to-prev-char + h + +move-to-end-of-word + e + +move-to-next-word + w + +move-to-prev-word + b + +move-to-start-of-next-block + ] + +move-to-start-of-prev-block + [ + +move-to-end-of-next-block + } + +move-to-end-of-prev-block + { + +move-to-start-of-line + 0 + +move-to-end-of-line + $ + +move-to-start-of-document + gg + +move-to-end-of-document + G + +yank selection -s + Y + +yank selection + y + <return> + <ctrl-m> + <ctrl-j> + <shift-return> + <enter> + <shift-enter> + +scroll left + H + +scroll down + J + +scroll up + K + +scroll right + L + diff --git a/.config/qutebrowser/jupyter-profile/nord-qutebrowser.py b/.config/qutebrowser/jupyter-profile/nord-qutebrowser.py new file mode 100644 index 0000000..9dd9064 --- /dev/null +++ b/.config/qutebrowser/jupyter-profile/nord-qutebrowser.py @@ -0,0 +1,336 @@ +nord = { + # Polar Night + 'nord0': '#2e3440', + 'nord1': '#3b4252', + 'nord2': '#434c5e', + 'nord3': '#4c566a', + # Snow Storm + 'nord4': '#d8dee9', + 'nord5': '#e5e9f0', + 'nord6': '#eceff4', + # Frost + 'nord7': '#8fbcbb', + 'nord8': '#88c0d0', + 'nord9': '#81a1c1', + 'nord10': '#5e81ac', + # Aurora + 'nord11': '#bf616a', + 'nord12': '#d08770', + 'nord13': '#ebcb8b', + 'nord14': '#a3be8c', + 'nord15': '#b48ead', +} + +## Background color of the completion widget category headers. +## Type: QssColor +c.colors.completion.category.bg = nord['nord0'] + +## Bottom border color of the completion widget category headers. +## Type: QssColor +c.colors.completion.category.border.bottom = nord['nord0'] + +## Top border color of the completion widget category headers. +## Type: QssColor +c.colors.completion.category.border.top = nord['nord0'] + +## Foreground color of completion widget category headers. +## Type: QtColor +c.colors.completion.category.fg = nord['nord5'] + +## Background color of the completion widget for even rows. +## Type: QssColor +c.colors.completion.even.bg = nord['nord1'] + +## Background color of the completion widget for odd rows. +## Type: QssColor +c.colors.completion.odd.bg = nord['nord1'] + +## Text color of the completion widget. +## Type: QtColor +c.colors.completion.fg = nord['nord4'] + +## Background color of the selected completion item. +## Type: QssColor +c.colors.completion.item.selected.bg = nord['nord3'] + +## Bottom border color of the selected completion item. +## Type: QssColor +c.colors.completion.item.selected.border.bottom = nord['nord3'] + +## Top border color of the completion widget category headers. +## Type: QssColor +c.colors.completion.item.selected.border.top = nord['nord3'] + +## Foreground color of the selected completion item. +## Type: QtColor +c.colors.completion.item.selected.fg = nord['nord6'] + +## Foreground color of the matched text in the completion. +## Type: QssColor +c.colors.completion.match.fg = nord['nord13'] + +## Color of the scrollbar in completion view +## Type: QssColor +c.colors.completion.scrollbar.bg = nord['nord1'] + +## Color of the scrollbar handle in completion view. +## Type: QssColor +c.colors.completion.scrollbar.fg = nord['nord5'] + +## Background color for the download bar. +## Type: QssColor +c.colors.downloads.bar.bg = nord['nord0'] + +## Background color for downloads with errors. +## Type: QtColor +c.colors.downloads.error.bg = nord['nord11'] + +## Foreground color for downloads with errors. +## Type: QtColor +c.colors.downloads.error.fg = nord['nord5'] + +## Color gradient stop for download backgrounds. +## Type: QtColor +c.colors.downloads.stop.bg = nord['nord15'] + +## Color gradient interpolation system for download backgrounds. +## Type: ColorSystem +## Valid values: +## - rgb: Interpolate in the RGB color system. +## - hsv: Interpolate in the HSV color system. +## - hsl: Interpolate in the HSL color system. +## - none: Don't show a gradient. +c.colors.downloads.system.bg = 'none' + +## Background color for hints. Note that you can use a `rgba(...)` value +## for transparency. +## Type: QssColor +c.colors.hints.bg = nord['nord13'] + +## Font color for hints. +## Type: QssColor +c.colors.hints.fg = nord['nord0'] + +## Font color for the matched part of hints. +## Type: QssColor +c.colors.hints.match.fg = nord['nord10'] + +## Background color of the keyhint widget. +## Type: QssColor +c.colors.keyhint.bg = nord['nord1'] + +## Text color for the keyhint widget. +## Type: QssColor +c.colors.keyhint.fg = nord['nord5'] + +## Highlight color for keys to complete the current keychain. +## Type: QssColor +c.colors.keyhint.suffix.fg = nord['nord13'] + +## Background color of an error message. +## Type: QssColor +c.colors.messages.error.bg = nord['nord11'] + +## Border color of an error message. +## Type: QssColor +c.colors.messages.error.border = nord['nord11'] + +## Foreground color of an error message. +## Type: QssColor +c.colors.messages.error.fg = nord['nord5'] + +## Background color of an info message. +## Type: QssColor +c.colors.messages.info.bg = nord['nord8'] + +## Border color of an info message. +## Type: QssColor +c.colors.messages.info.border = nord['nord8'] + +## Foreground color an info message. +## Type: QssColor +c.colors.messages.info.fg = nord['nord5'] + +## Background color of a warning message. +## Type: QssColor +c.colors.messages.warning.bg = nord['nord12'] + +## Border color of a warning message. +## Type: QssColor +c.colors.messages.warning.border = nord['nord12'] + +## Foreground color a warning message. +## Type: QssColor +c.colors.messages.warning.fg = nord['nord5'] + +## Background color for prompts. +## Type: QssColor +c.colors.prompts.bg = nord['nord2'] + +# ## Border used around UI elements in prompts. +# ## Type: String +c.colors.prompts.border = '1px solid ' + nord['nord0'] + +## Foreground color for prompts. +## Type: QssColor +c.colors.prompts.fg = nord['nord5'] + +## Background color for the selected item in filename prompts. +## Type: QssColor +c.colors.prompts.selected.bg = nord['nord3'] + +## Background color of the statusbar in caret mode. +## Type: QssColor +c.colors.statusbar.caret.bg = nord['nord15'] + +## Foreground color of the statusbar in caret mode. +## Type: QssColor +c.colors.statusbar.caret.fg = nord['nord5'] + +## Background color of the statusbar in caret mode with a selection. +## Type: QssColor +c.colors.statusbar.caret.selection.bg = nord['nord15'] + +## Foreground color of the statusbar in caret mode with a selection. +## Type: QssColor +c.colors.statusbar.caret.selection.fg = nord['nord5'] + +## Background color of the statusbar in command mode. +## Type: QssColor +c.colors.statusbar.command.bg = nord['nord2'] + +## Foreground color of the statusbar in command mode. +## Type: QssColor +c.colors.statusbar.command.fg = nord['nord5'] + +## Background color of the statusbar in private browsing + command mode. +## Type: QssColor +c.colors.statusbar.command.private.bg = nord['nord2'] + +## Foreground color of the statusbar in private browsing + command mode. +## Type: QssColor +c.colors.statusbar.command.private.fg = nord['nord5'] + +## Background color of the statusbar in insert mode. +## Type: QssColor +c.colors.statusbar.insert.bg = nord['nord14'] + +## Foreground color of the statusbar in insert mode. +## Type: QssColor +c.colors.statusbar.insert.fg = nord['nord1'] + +## Background color of the statusbar. +## Type: QssColor +c.colors.statusbar.normal.bg = nord['nord0'] + +## Foreground color of the statusbar. +## Type: QssColor +c.colors.statusbar.normal.fg = nord['nord5'] + +## Background color of the statusbar in passthrough mode. +## Type: QssColor +c.colors.statusbar.passthrough.bg = nord['nord10'] + +## Foreground color of the statusbar in passthrough mode. +## Type: QssColor +c.colors.statusbar.passthrough.fg = nord['nord5'] + +## Background color of the statusbar in private browsing mode. +## Type: QssColor +c.colors.statusbar.private.bg = nord['nord3'] + +## Foreground color of the statusbar in private browsing mode. +## Type: QssColor +c.colors.statusbar.private.fg = nord['nord5'] + +## Background color of the progress bar. +## Type: QssColor +c.colors.statusbar.progress.bg = nord['nord5'] + +## Foreground color of the URL in the statusbar on error. +## Type: QssColor +c.colors.statusbar.url.error.fg = nord['nord11'] + +## Default foreground color of the URL in the statusbar. +## Type: QssColor +c.colors.statusbar.url.fg = nord['nord5'] + +## Foreground color of the URL in the statusbar for hovered links. +## Type: QssColor +c.colors.statusbar.url.hover.fg = nord['nord8'] + +## Foreground color of the URL in the statusbar on successful load +## (http). +## Type: QssColor +c.colors.statusbar.url.success.http.fg = nord['nord5'] + +## Foreground color of the URL in the statusbar on successful load +## (https). +## Type: QssColor +c.colors.statusbar.url.success.https.fg = nord['nord14'] + +## Foreground color of the URL in the statusbar when there's a warning. +## Type: QssColor +c.colors.statusbar.url.warn.fg = nord['nord12'] + +## Background color of the tab bar. +## Type: QtColor +c.colors.tabs.bar.bg = nord['nord3'] + +## Background color of unselected even tabs. +## Type: QtColor +c.colors.tabs.even.bg = nord['nord3'] + +## Foreground color of unselected even tabs. +## Type: QtColor +c.colors.tabs.even.fg = nord['nord5'] + +## Color for the tab indicator on errors. +## Type: QtColor +c.colors.tabs.indicator.error = nord['nord11'] + +## Color gradient start for the tab indicator. +## Type: QtColor +# c.colors.tabs.indicator.start = nord['violet'] + +## Color gradient end for the tab indicator. +## Type: QtColor +# c.colors.tabs.indicator.stop = nord['orange'] + +## Color gradient interpolation system for the tab indicator. +## Type: ColorSystem +## Valid values: +## - rgb: Interpolate in the RGB color system. +## - hsv: Interpolate in the HSV color system. +## - hsl: Interpolate in the HSL color system. +## - none: Don't show a gradient. +c.colors.tabs.indicator.system = 'none' + +## Background color of unselected odd tabs. +## Type: QtColor +c.colors.tabs.odd.bg = nord['nord3'] + +## Foreground color of unselected odd tabs. +## Type: QtColor +c.colors.tabs.odd.fg = nord['nord5'] + +# ## Background color of selected even tabs. +# ## Type: QtColor +c.colors.tabs.selected.even.bg = nord['nord0'] + +# ## Foreground color of selected even tabs. +# ## Type: QtColor +c.colors.tabs.selected.even.fg = nord['nord5'] + +# ## Background color of selected odd tabs. +# ## Type: QtColor +c.colors.tabs.selected.odd.bg = nord['nord0'] + +# ## Foreground color of selected odd tabs. +# ## Type: QtColor +c.colors.tabs.selected.odd.fg = nord['nord5'] + +## Background color for webpages if unset (or empty to use the theme's +## color) +## Type: QtColor +# c.colors.webpage.bg = 'white' diff --git a/.config/qutebrowser/jupyter-profile/qutebrowser.conf b/.config/qutebrowser/jupyter-profile/qutebrowser.conf new file mode 100755 index 0000000..3fc85a8 --- /dev/null +++ b/.config/qutebrowser/jupyter-profile/qutebrowser.conf @@ -0,0 +1,1612 @@ +# vim: ft=dosini + +# Configfile for qutebrowser. +# +# This configfile is parsed by python's configparser in extended +# interpolation mode. The format is very INI-like, so there are +# categories like [general] with "key = value"-pairs. +# +# Note that you shouldn't add your own comments, as this file is +# regenerated every time the config is saved. +# +# Interpolation looks like ${value} or ${section:value} and will be +# replaced by the respective value. +# +# Some settings will expand environment variables. Note that, since +# interpolation is run first, you will need to escape the $ char as +# described below. +# +# This is the default config, so if you want to remove anything from +# here (as opposed to change/add), for example a key binding, set it to +# an empty value. +# +# You will need to escape the following values: +# - # at the start of the line (at the first position of the key) (\#) +# - $ in a value ($$) + + +# General/miscellaneous options. +[general] + +# ignore-case (IgnoreCase): +# Whether to find text on a page case-insensitively. +# true: Search case-insensitively +# false: Search case-sensitively +# smart: Search case-sensitively if there are capital chars +# Default: smart +ignore-case = smart + +# startpage (List of String): +# The default page(s) to open at the start, separated by commas. +# Default: https://start.duckduckgo.com +startpage = https://start.duckduckgo.com + +# yank-ignored-url-parameters (List of String): +# The URL parameters to strip with :yank url, separated by commas. +# Default: +# ref,utm_source,utm_medium,utm_campaign,utm_term,utm_content +yank-ignored-url-parameters = ref,utm_source,utm_medium,utm_campaign,utm_term,utm_content + +# default-open-dispatcher (String): +# The default program used to open downloads. Set to an empty string +# to use the default internal handler. +# Any {} in the string will be expanded to the filename, else the +# filename will be appended. +# Default: +default-open-dispatcher = + +# default-page (FuzzyUrl): +# The page to open if :open -t/-b/-w is used without URL. Use +# `about:blank` for a blank page. +# Default: ${startpage} +default-page = ${startpage} + +# auto-search (AutoSearch): +# Whether to start a search when something else than a URL is +# entered. +# naive: Use simple/naive check. +# dns: Use DNS requests (might be slow!). +# false: Never search automatically. +# Default: naive +auto-search = naive + +# auto-save-config (Bool): +# Whether to save the config automatically on quit. +# Valid values: true, false +# Default: true +auto-save-config = true + +# auto-save-interval (Int): +# How often (in milliseconds) to auto-save config/cookies/etc. +# Default: 15000 +auto-save-interval = 15000 + +# editor (ShellCommand): +# The editor (and arguments) to use for the `open-editor` command. +# The arguments get split like in a shell, so you can use `"` or `'` +# to quote them. +# `{}` gets replaced by the filename of the file to be edited. +# Default: gvim -f "{}" +editor = gvim -f "{}" + +# editor-encoding (Encoding): +# Encoding to use for editor. +# Default: utf-8 +editor-encoding = utf-8 + +# private-browsing (Bool): +# Open new windows in private browsing mode which does not record +# visited pages. +# Valid values: true, false +# Default: false +private-browsing = true + +# developer-extras (Bool): +# Enable extra tools for Web developers. +# This needs to be enabled for `:inspector` to work and also adds an +# _Inspect_ entry to the context menu. For QtWebEngine, see +# 'qutebrowser --help' instead. +# Valid values: true, false +# Default: false +developer-extras = true + +# print-element-backgrounds (Bool): +# Whether the background color and images are also drawn when the +# page is printed. +# This setting only works with Qt 5.8 or newer when using the +# QtWebEngine backend. +# Valid values: true, false +# Default: true +print-element-backgrounds = true + +# xss-auditing (Bool): +# Whether load requests should be monitored for cross-site scripting +# attempts. +# Suspicious scripts will be blocked and reported in the inspector's +# JavaScript console. Enabling this feature might have an impact on +# performance. +# Valid values: true, false +# Default: false +xss-auditing = false + +# default-encoding (String): +# Default encoding to use for websites. +# The encoding must be a string describing an encoding such as +# _utf-8_, _iso-8859-1_, etc. +# Default: iso-8859-1 +default-encoding = _utf-8_ + +# new-instance-open-target (String): +# How to open links in an existing instance if a new one is +# launched. +# tab: Open a new tab in the existing window and activate the +# window. +# tab-bg: Open a new background tab in the existing window and +# activate the window. +# tab-silent: Open a new tab in the existing window without +# activating the window. +# tab-bg-silent: Open a new background tab in the existing +# window without activating the window. +# window: Open in a new window. +# Default: tab +new-instance-open-target = tab + +# new-instance-open-target.window (String): +# Which window to choose when opening links as new tabs. +# first-opened: Open new tabs in the first (oldest) opened +# window. +# last-opened: Open new tabs in the last (newest) opened window. +# last-focused: Open new tabs in the most recently focused +# window. +# last-visible: Open new tabs in the most recently visible +# window. +# Default: last-focused +new-instance-open-target.window = last-focused + +# log-javascript-console (String): +# How to log javascript console messages. +# none: Don't log messages. +# debug: Log messages with debug level. +# info: Log messages with info level. +# Default: debug +log-javascript-console = debug + +# save-session (Bool): +# Whether to always save the open pages. +# Valid values: true, false +# Default: false +save-session = true + +# session-default-name (SessionName): +# The name of the session to save by default, or empty for the last +# loaded session. +# Default: +session-default-name = + +# url-incdec-segments (FlagList): +# The URL segments where `:navigate increment/decrement` will search +# for a number. +# Valid values: host, path, query, anchor +# Default: path,query +url-incdec-segments = path,query + + +# General options related to the user interface. +[ui] + +# history-session-interval (Int): +# The maximum time in minutes between two history items for them to +# be considered being from the same session. Use -1 to disable +# separation. +# Default: 30 +history-session-interval = 30 + +# zoom-levels (List of Perc): +# The available zoom levels, separated by commas. +# Default: +# 25%,33%,50%,67%,75%,90%,100%,110%,125%,150%,175%,200%,250%,300%,400%,500% +zoom-levels = 25%,33%,50%,67%,75%,90%,100%,110%,125%,150%,175%,200%,250%,300%,400%,500% + +# default-zoom (Perc): +# The default zoom level. +# Default: 100% +default-zoom = 100% + +# downloads-position (VerticalPosition): +# Where to show the downloaded files. +# Valid values: top, bottom +# Default: top +downloads-position = top + +# status-position (VerticalPosition): +# The position of the status bar. +# Valid values: top, bottom +# Default: bottom +status-position = bottom + +# message-timeout (Int): +# Time (in ms) to show messages in the statusbar for. +# Set to 0 to never clear messages. +# Default: 2000 +message-timeout = 2000 + +# message-unfocused (Bool): +# Whether to show messages in unfocused windows. +# Valid values: true, false +# Default: false +message-unfocused = false + +# confirm-quit (ConfirmQuit): +# Whether to confirm quitting the application. +# always: Always show a confirmation. +# multiple-tabs: Show a confirmation if multiple tabs are +# opened. +# downloads: Show a confirmation if downloads are running +# never: Never show a confirmation. +# Default: never +confirm-quit = never + +# zoom-text-only (Bool): +# Whether the zoom factor on a frame applies only to the text or to +# all content. +# Valid values: true, false +# Default: false +zoom-text-only = false + +# frame-flattening (Bool): +# Whether to expand each subframe to its contents. +# This will flatten all the frames to become one scrollable page. +# Valid values: true, false +# Default: false +frame-flattening = false + +# user-stylesheet (File): +# User stylesheet to use (absolute filename or filename relative to +# the config directory). Will expand environment variables. +# Default: +user-stylesheet = + +# hide-scrollbar (Bool): +# Hide the main scrollbar. +# Valid values: true, false +# Default: true +hide-scrollbar = true + +# smooth-scrolling (Bool): +# Whether to enable smooth scrolling for web pages. Note smooth +# scrolling does not work with the :scroll-px command. +# Valid values: true, false +# Default: false +smooth-scrolling = false + +# remove-finished-downloads (Int): +# Number of milliseconds to wait before removing finished downloads. +# Will not be removed if value is -1. +# Default: -1 +remove-finished-downloads = -1 + +# hide-statusbar (Bool): +# Whether to hide the statusbar unless a message is shown. +# Valid values: true, false +# Default: false +hide-statusbar = false + +# statusbar-padding (Padding): +# Padding for statusbar (top, bottom, left, right). +# Default: 1,1,0,0 +statusbar-padding = 1,1,0,0 + +# window-title-format (FormatString): +# The format to use for the window title. The following placeholders +# are defined: +# * `{perc}`: The percentage as a string like `[10%]`. +# * `{perc_raw}`: The raw percentage, e.g. `10` +# * `{title}`: The title of the current web page +# * `{title_sep}`: The string ` - ` if a title is set, empty +# otherwise. +# * `{id}`: The internal window ID of this window. +# * `{scroll_pos}`: The page scroll position. +# * `{host}`: The host of the current web page. +# * `{backend}`: Either 'webkit' or 'webengine' +# * `{private}` : Indicates when private mode is enabled. +# Default: {perc}{title}{title_sep}qutebrowser +window-title-format = {perc}{title}{title_sep}qutebrowser + +# modal-js-dialog (Bool): +# Use standard JavaScript modal dialog for alert() and confirm() +# Valid values: true, false +# Default: false +modal-js-dialog = false + +# hide-wayland-decoration (Bool): +# Hide the window decoration when using wayland (requires restart) +# Valid values: true, false +# Default: false +hide-wayland-decoration = false + +# keyhint-blacklist (List of String): +# Keychains that shouldn't be shown in the keyhint dialog +# Globs are supported, so ';*' will blacklist all keychainsstarting +# with ';'. Use '*' to disable keyhints +# Default: +keyhint-blacklist = + +# keyhint-delay (Int): +# Time from pressing a key to seeing the keyhint dialog (ms) +# Default: 500 +keyhint-delay = 500 + +# prompt-radius (Int): +# The rounding radius for the edges of prompts. +# Default: 8 +prompt-radius = 8 + +# prompt-filebrowser (Bool): +# Show a filebrowser in upload/download prompts. +# Valid values: true, false +# Default: true +prompt-filebrowser = true + + +# Settings related to the network. +[network] + +# do-not-track (Bool): +# Value to send in the `DNT` header. +# Valid values: true, false +# Default: true +do-not-track = true + +# accept-language (String): +# Value to send in the `accept-language` header. +# Default: en-US,en +accept-language = en-US,en;q=0.5 + +# referer-header (String): +# Send the Referer header +# always: Always send. +# never: Never send; this is not recommended, as some sites may +# break. +# same-domain: Only send for the same domain. This will still +# protect your privacy, but shouldn't break any sites. +# Default: same-domain +referer-header = same-domain + +# user-agent (UserAgent): +# User agent to send. Empty to send the default. +# Default: +user-agent = Mozilla/5.0 + +# proxy (Proxy): +# The proxy to use. +# In addition to the listed values, you can use a `socks://...` or +# `http://...` URL. +# system: Use the system wide proxy. +# none: Don't use any proxy +# Default: system +proxy = system + +# proxy-dns-requests (Bool): +# Whether to send DNS requests over the configured proxy. +# Valid values: true, false +# Default: true +proxy-dns-requests = true + +# ssl-strict (BoolAsk): +# Whether to validate SSL handshakes. +# Valid values: true, false, ask +# Default: ask +ssl-strict = ask + +# dns-prefetch (Bool): +# Whether to try to pre-fetch DNS entries to speed up browsing. +# Valid values: true, false +# Default: true +dns-prefetch = true + +# custom-headers (HeaderDict): +# Set custom headers for qutebrowser HTTP requests. +# Default: +custom-headers = + +# netrc-file (File): +# Set location of a netrc-file for HTTP authentication. If empty, +# ~/.netrc is used. +# Default: +netrc-file = + + +# Options related to completion and command history. +[completion] + +# show (String): +# When to show the autocompletion window. +# always: Whenever a completion is available. +# auto: Whenever a completion is requested. +# never: Never. +# Default: always +show = always + +# download-path-suggestion (String): +# What to display in the download filename input. +# path: Show only the download path. +# filename: Show only download filename. +# both: Show download path and filename. +# Default: path +download-path-suggestion = path + +# timestamp-format (TimestampTemplate): +# How to format timestamps (e.g. for history) +# Default: %Y-%m-%d +timestamp-format = %Y-%m-%d + +# height (PercOrInt): +# The height of the completion, in px or as percentage of the +# window. +# Default: 50% +height = 50% + +# cmd-history-max-items (Int): +# How many commands to save in the command history. +# 0: no history / -1: unlimited +# Default: 100 +cmd-history-max-items = 100 + +# web-history-max-items (Int): +# How many URLs to show in the web history. +# 0: no history / -1: unlimited +# Default: 1000 +web-history-max-items = 1000 + +# quick-complete (Bool): +# Whether to move on to the next part when there's only one possible +# completion left. +# Valid values: true, false +# Default: true +quick-complete = true + +# shrink (Bool): +# Whether to shrink the completion to be smaller than the configured +# size if there are no scrollbars. +# Valid values: true, false +# Default: false +shrink = false + +# scrollbar-width (Int): +# Width of the scrollbar in the completion window (in px). +# Default: 12 +scrollbar-width = 12 + +# scrollbar-padding (Int): +# Padding of scrollbar handle in completion window (in px). +# Default: 2 +scrollbar-padding = 2 + + +# Options related to input modes. +[input] + +# timeout (Int): +# Timeout (in milliseconds) for ambiguous key bindings. +# If the current input forms both a complete match and a partial +# match, the complete match will be executed after this time. +# Default: 500 +timeout = 500 + +# partial-timeout (Int): +# Timeout (in milliseconds) for partially typed key bindings. +# If the current input forms only partial matches, the keystring +# will be cleared after this time. +# Default: 5000 +partial-timeout = 5000 + +# insert-mode-on-plugins (Bool): +# Whether to switch to insert mode when clicking flash and other +# plugins. +# Valid values: true, false +# Default: false +insert-mode-on-plugins = false + +# auto-leave-insert-mode (Bool): +# Whether to leave insert mode if a non-editable element is clicked. +# Valid values: true, false +# Default: true +auto-leave-insert-mode = true + +# auto-insert-mode (Bool): +# Whether to automatically enter insert mode if an editable element +# is focused after page load. +# Valid values: true, false +# Default: false +auto-insert-mode = false + +# forward-unbound-keys (String): +# Whether to forward unbound keys to the webview in normal mode. +# all: Forward all unbound keys. +# auto: Forward unbound non-alphanumeric keys. +# none: Don't forward any keys. +# Default: auto +forward-unbound-keys = auto + +# spatial-navigation (Bool): +# Enables or disables the Spatial Navigation feature. +# Spatial navigation consists in the ability to navigate between +# focusable elements in a Web page, such as hyperlinks and form +# controls, by using Left, Right, Up and Down arrow keys. For +# example, if a user presses the Right key, heuristics determine +# whether there is an element he might be trying to reach towards +# the right and which element he probably wants. +# Valid values: true, false +# Default: false +spatial-navigation = false + +# links-included-in-focus-chain (Bool): +# Whether hyperlinks should be included in the keyboard focus chain. +# Valid values: true, false +# Default: true +links-included-in-focus-chain = true + +# rocker-gestures (Bool): +# Whether to enable Opera-like mouse rocker gestures. This disables +# the context menu. +# Valid values: true, false +# Default: false +rocker-gestures = false + +# mouse-zoom-divider (Int): +# How much to divide the mouse wheel movements to translate them +# into zoom increments. +# Default: 512 +mouse-zoom-divider = 512 + + +# Configuration of the tab bar. +[tabs] + +# background-tabs (Bool): +# Whether to open new tabs (middleclick/ctrl+click) in background. +# Valid values: true, false +# Default: false +background-tabs = false + +# select-on-remove (SelectOnRemove): +# Which tab to select when the focused tab is removed. +# prev: Select the tab which came before the closed one (left in +# horizontal, above in vertical). +# next: Select the tab which came after the closed one (right in +# horizontal, below in vertical). +# last-used: Select the previously selected tab. +# Default: next +select-on-remove = next + +# new-tab-position (NewTabPosition): +# How new tabs are positioned. +# prev: Before the current tab. +# next: After the current tab. +# first: At the beginning. +# last: At the end. +# Default: next +new-tab-position = next + +# new-tab-position-explicit (NewTabPosition): +# How new tabs opened explicitly are positioned. +# prev: Before the current tab. +# next: After the current tab. +# first: At the beginning. +# last: At the end. +# Default: last +new-tab-position-explicit = last + +# last-close (String): +# Behavior when the last tab is closed. +# ignore: Don't do anything. +# blank: Load a blank page. +# startpage: Load the start page. +# default-page: Load the default page. +# close: Close the window. +# Default: ignore +last-close = ignore + +# show (String): +# When to show the tab bar +# always: Always show the tab bar. +# never: Always hide the tab bar. +# multiple: Hide the tab bar if only one tab is open. +# switching: Show the tab bar when switching tabs. +# Default: always +show = always + +# show-switching-delay (Int): +# Time to show the tab bar before hiding it when tabs->show is set +# to 'switching'. +# Default: 800 +show-switching-delay = 800 + +# wrap (Bool): +# Whether to wrap when changing tabs. +# Valid values: true, false +# Default: true +wrap = true + +# movable (Bool): +# Whether tabs should be movable. +# Valid values: true, false +# Default: true +movable = true + +# close-mouse-button (String): +# On which mouse button to close tabs. +# right: Close tabs on right-click. +# middle: Close tabs on middle-click. +# none: Don't close tabs using the mouse. +# Default: middle +close-mouse-button = middle + +# position (Position): +# The position of the tab bar. +# Valid values: top, bottom, left, right +# Default: top +position = top + +# show-favicons (Bool): +# Whether to show favicons in the tab bar. +# Valid values: true, false +# Default: true +show-favicons = true + +# favicon-scale (Float): +# Scale for favicons in the tab bar. The tab size is unchanged, so +# big favicons also require extra `tabs->padding`. +# Default: 1.0 +favicon-scale = 1.0 + +# width (PercOrInt): +# The width of the tab bar if it's vertical, in px or as percentage +# of the window. +# Default: 20% +width = 20% + +# pinned-width (Int): +# The width for pinned tabs with a horizontal tabbar, in px. +# Default: 43 +pinned-width = 43 + +# indicator-width (Int): +# Width of the progress indicator (0 to disable). +# Default: 3 +indicator-width = 3 + +# tabs-are-windows (Bool): +# Whether to open windows instead of tabs. +# Valid values: true, false +# Default: false +tabs-are-windows = false + +# title-format (FormatString): +# The format to use for the tab title. The following placeholders +# are defined: +# * `{perc}`: The percentage as a string like `[10%]`. +# * `{perc_raw}`: The raw percentage, e.g. `10` +# * `{title}`: The title of the current web page +# * `{title_sep}`: The string ` - ` if a title is set, empty +# otherwise. +# * `{index}`: The index of this tab. +# * `{id}`: The internal tab ID of this tab. +# * `{scroll_pos}`: The page scroll position. +# * `{host}`: The host of the current web page. +# * `{backend}`: Either 'webkit' or 'webengine' +# * `{private}` : Indicates when private mode is enabled. +# Default: {index}: {title} +title-format = {index}: {title} + +# title-format-pinned (FormatString): +# The format to use for the tab title for pinned tabs. The same +# placeholders like for title-format are defined. +# Default: {index} +title-format-pinned = {index} + +# title-alignment (TextAlignment): +# Alignment of the text inside of tabs +# Valid values: left, right, center +# Default: left +title-alignment = left + +# mousewheel-tab-switching (Bool): +# Switch between tabs using the mouse wheel. +# Valid values: true, false +# Default: true +mousewheel-tab-switching = true + +# padding (Padding): +# Padding for tabs (top, bottom, left, right). +# Default: 0,0,5,5 +padding = 0,0,5,5 + +# indicator-padding (Padding): +# Padding for indicators (top, bottom, left, right). +# Default: 2,2,0,4 +indicator-padding = 2,2,0,4 + + +# Settings related to cache and storage. +[storage] + +# download-directory (Directory): +# The directory to save downloads to. An empty value selects a +# sensible os-specific default. Will expand environment variables. +# Default: +download-directory = + +# prompt-download-directory (Bool): +# Whether to prompt the user for the download location. +# If set to false, 'download-directory' will be used. +# Valid values: true, false +# Default: true +prompt-download-directory = true + +# remember-download-directory (Bool): +# Whether to remember the last used download directory. +# Valid values: true, false +# Default: true +remember-download-directory = true + +# maximum-pages-in-cache (Int): +# The maximum number of pages to hold in the global memory page +# cache. +# The Page Cache allows for a nicer user experience when navigating +# forth or back to pages in the forward/back history, by pausing and +# resuming up to _n_ pages. +# For more information about the feature, please refer to: +# http://webkit.org/blog/427/webkit-page-cache-i-the-basics/ +# Default: 0 +maximum-pages-in-cache = 0 + +# offline-web-application-cache (Bool): +# Whether support for the HTML 5 web application cache feature is +# enabled. +# An application cache acts like an HTTP cache in some sense. For +# documents that use the application cache via JavaScript, the +# loader engine will first ask the application cache for the +# contents, before hitting the network. +# The feature is described in details at: +# http://dev.w3.org/html5/spec/Overview.html#appcache +# Valid values: true, false +# Default: true +offline-web-application-cache = true + +# local-storage (Bool): +# Whether support for HTML 5 local storage and Web SQL is enabled. +# Valid values: true, false +# Default: true +local-storage = true + +# cache-size (Int): +# Size of the HTTP network cache. Empty to use the default value. +# Default: +cache-size = + + +# Loaded plugins/scripts and allowed actions. +[content] + +# allow-images (Bool): +# Whether images are automatically loaded in web pages. +# Valid values: true, false +# Default: true +allow-images = true + +# allow-javascript (Bool): +# Enables or disables the running of JavaScript programs. +# Valid values: true, false +# Default: true +allow-javascript = true + +# allow-plugins (Bool): +# Enables or disables plugins in Web pages. +# Qt plugins with a mimetype such as "application/x-qt-plugin" are +# not affected by this setting. +# Valid values: true, false +# Default: false +allow-plugins = true + +# webgl (Bool): +# Enables or disables WebGL. +# Valid values: true, false +# Default: true +webgl = true + +# hyperlink-auditing (Bool): +# Enable or disable hyperlink auditing (<a ping>). +# Valid values: true, false +# Default: false +hyperlink-auditing = false + +# geolocation (BoolAsk): +# Allow websites to request geolocations. +# Valid values: true, false, ask +# Default: ask +geolocation = false + +# notifications (BoolAsk): +# Allow websites to show notifications. +# Valid values: true, false, ask +# Default: ask +notifications = ask + +# media-capture (BoolAsk): +# Allow websites to record audio/video. +# Valid values: true, false, ask +# Default: ask +media-capture = ask + +# javascript-can-open-windows-automatically (Bool): +# Whether JavaScript programs can open new windows without user +# interaction. +# Valid values: true, false +# Default: false +javascript-can-open-windows-automatically = true + +# javascript-can-close-windows (Bool): +# Whether JavaScript programs can close windows. +# Valid values: true, false +# Default: false +javascript-can-close-windows = true + +# javascript-can-access-clipboard (Bool): +# Whether JavaScript programs can read or write to the clipboard. +# With QtWebEngine, writing the clipboard as response to a user +# interaction is always allowed. +# Valid values: true, false +# Default: false +javascript-can-access-clipboard = true + +# ignore-javascript-prompt (Bool): +# Whether all javascript prompts should be ignored. +# Valid values: true, false +# Default: false +ignore-javascript-prompt = true + +# ignore-javascript-alert (Bool): +# Whether all javascript alerts should be ignored. +# Valid values: true, false +# Default: false +ignore-javascript-alert = true + +# local-content-can-access-remote-urls (Bool): +# Whether locally loaded documents are allowed to access remote +# urls. +# Valid values: true, false +# Default: false +local-content-can-access-remote-urls = false + +# local-content-can-access-file-urls (Bool): +# Whether locally loaded documents are allowed to access other local +# urls. +# Valid values: true, false +# Default: true +local-content-can-access-file-urls = true + +# cookies-accept (String): +# Control which cookies to accept. +# all: Accept all cookies. +# no-3rdparty: Accept cookies from the same origin only. +# no-unknown-3rdparty: Accept cookies from the same origin only, +# unless a cookie is already set for the domain. +# never: Don't accept cookies at all. +# Default: no-3rdparty +cookies-accept = all + +# cookies-store (Bool): +# Whether to store cookies. Note this option needs a restart with +# QtWebEngine on Qt < 5.9. +# Valid values: true, false +# Default: true +cookies-store = true + +# host-block-lists (List of Url): +# List of URLs of lists which contain hosts to block. +# The file can be in one of the following formats: +# - An '/etc/hosts'-like file +# - One host per line +# - A zip-file of any of the above, with either only one file, or a +# file named 'hosts' (with any extension). +# Default: +# https://www.malwaredomainlist.com/hostslist/hosts.txt,http://someonewhocares.org/hosts/hosts,http://winhelp2002.mvps.org/hosts.zip,http://malwaredomains.lehigh.edu/files/justdomains.zip,https://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&mimetype=plaintext +host-block-lists = https://www.malwaredomainlist.com/hostslist/hosts.txt,http://someonewhocares.org/hosts/hosts,http://winhelp2002.mvps.org/hosts.zip,http://malwaredomains.lehigh.edu/files/justdomains.zip,https://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&mimetype=plaintext + +# host-blocking-enabled (Bool): +# Whether host blocking is enabled. +# Valid values: true, false +# Default: true +host-blocking-enabled = false + +# host-blocking-whitelist (List of String): +# List of domains that should always be loaded, despite being +# ad-blocked. +# Domains may contain * and ? wildcards and are otherwise required +# to exactly match the requested domain. +# Local domains are always exempt from hostblocking. +# Default: piwik.org +host-blocking-whitelist = piwik.org,next-episode.net,churchmilitant.tv,load.sumome.com,s7.addthis.com,www.google-analytics.com,ssl.google-analytics.com,referrer.disqus.com,m.addthisedge.com,m.addthis.com,stats.g.doubleclick.net,*.addthis.com,lukesmith.xyz + +# enable-pdfjs (Bool): +# Enable pdf.js to view PDF files in the browser. +# Note that the files can still be downloaded by clicking the +# download button in the pdf.js viewer. +# Valid values: true, false +# Default: false +enable-pdfjs = false + + +# Hinting settings. +[hints] + +# border (String): +# CSS border value for hints. +# Default: 1px solid #E3BE23 +border = 1px solid #E3BE23 + +# mode (String): +# Mode to use for hints. +# number: Use numeric hints. (In this mode you can also type +# letters form the hinted element to filter and reduce the number of +# elements that are hinted.) +# letter: Use the chars in the hints -> chars setting. +# word: Use hints words based on the html elements and the extra +# words. +# Default: letter +mode = number + +# chars (UniqueCharString): +# Chars used for hint strings. +# Default: asdfghjkl +chars = asdfghjkl + +# min-chars (Int): +# Minimum number of chars used for hint strings. +# Default: 1 +min-chars = 1 + +# scatter (Bool): +# Whether to scatter hint key chains (like Vimium) or not (like +# dwb). Ignored for number hints. +# Valid values: true, false +# Default: true +scatter = true + +# uppercase (Bool): +# Make chars in hint strings uppercase. +# Valid values: true, false +# Default: false +uppercase = false + +# dictionary (File): +# The dictionary file to be used by the word hints. +# Default: /usr/share/dict/words +dictionary = /usr/share/dict/words + +# auto-follow (String): +# Controls when a hint can be automatically followed without the +# user pressing Enter. +# always: Auto-follow whenever there is only a single hint on a +# page. +# unique-match: Auto-follow whenever there is a unique non-empty +# match in either the hint string (word mode) or filter (number +# mode). +# full-match: Follow the hint when the user typed the whole hint +# (letter, word or number mode) or the element's text (only in +# number mode). +# never: The user will always need to press Enter to follow a +# hint. +# Default: unique-match +auto-follow = unique-match + +# auto-follow-timeout (Int): +# A timeout (in milliseconds) to inhibit normal-mode key bindings +# after a successful auto-follow. +# Default: 0 +auto-follow-timeout = 0 + +# next-regexes (List of Regex): +# A comma-separated list of regexes to use for 'next' links. +# Default: +# \bnext\b,\bmore\b,\bnewer\b,\b[>→≫]\b,\b(>>|»)\b,\bcontinue\b +next-regexes = \bnext\b,\bmore\b,\bnewer\b,\b[>→≫]\b,\b(>>|»)\b,\bcontinue\b + +# prev-regexes (List of Regex): +# A comma-separated list of regexes to use for 'prev' links. +# Default: \bprev(ious)?\b,\bback\b,\bolder\b,\b[<←≪]\b,\b(<<|«)\b +prev-regexes = \bprev(ious)?\b,\bback\b,\bolder\b,\b[<←≪]\b,\b(<<|«)\b + +# find-implementation (String): +# Which implementation to use to find elements to hint. +# javascript: Better but slower +# python: Slightly worse but faster +# Default: python +find-implementation = python + +# hide-unmatched-rapid-hints (Bool): +# Controls hiding unmatched hints in rapid mode. +# Valid values: true, false +# Default: true +hide-unmatched-rapid-hints = true + + +# Definitions of search engines which can be used via the address bar. +# The searchengine named `DEFAULT` is used when `general -> auto-search` +# is true and something else than a URL was entered to be opened. Other +# search engines can be used by prepending the search engine name to the +# search term, e.g. `:open google qutebrowser`. The string `{}` will be +# replaced by the search term, use `{{` and `}}` for literal `{`/`}` +# signs. +[searchengines] +DEFAULT = https://duckduckgo.com/?q={} + + +# Aliases for commands. +# By default, no aliases are defined. Example which adds a new command +# `:qtb` to open qutebrowsers website: +# `qtb = open https://www.qutebrowser.org/` +[aliases] +qtb = open https://www.qutebrowser.org/ +mbt = open t https://signin1.bt.com/login/emailloginform +Ombt = open -t https://signin1.bt.com/login/emailloginform +nx = open http://next-episode.net/ +Onx = open -t http://next-episode.net/ +gk = open https://gameknot.com/ +Ogk = open -t https://gameknot.com/ +yt = open https://youtube.com +Oyt = open -t https://youtube.com +Y = open http://www.cosmos.esa.int/web/xmm-newton/current-calibration-files +OY = open -t http://www.cosmos.esa.int/web/xmm-newton/current-calibration-files +1337 = open https://1337x.to/home +O1337 = open -t https://1337x.to/home +ez = open https://eztv.ag +Oez = open -t https://eztv.ag +tz = open https://extratorrent.ag +Otz = open -t https://extratorrent.ag +tpb = open https://thepiratebay.org/ +Otpb = open -t https://thepiratebay.org/ +Nm = open https://netmail.herts.ac.uk/owa/auth/logon.aspx?replaceCurrent=1&url=https%3a%2f%2fnetmail.herts.ac.uk%2fowa +ONm = open -t https://netmail.herts.ac.uk/owa/auth/logon.aspx?replaceCurrent=1&url=https%3a%2f%2fnetmail.herts.ac.uk%2fowa + + +# Colors used in the UI. +# A value can be in one of the following format: +# * `#RGB`/`#RRGGBB`/`#RRRGGGBBB`/`#RRRRGGGGBBBB` +# * An SVG color name as specified in http://www.w3.org/TR/SVG/types.html#ColorKeywords[the W3C specification]. +# * transparent (no color) +# * `rgb(r, g, b)` / `rgba(r, g, b, a)` (values 0-255 or percentages) +# * `hsv(h, s, v)` / `hsva(h, s, v, a)` (values 0-255, hue 0-359) +# * A gradient as explained in http://doc.qt.io/qt-5/stylesheet-reference.html#list-of-property-types[the Qt documentation] under ``Gradient''. +# A *.system value determines the color system to use for color +# interpolation between similarly-named *.start and *.stop entries, +# regardless of how they are defined in the options. Valid values are +# 'rgb', 'hsv', and 'hsl'. +# The `hints.*` values are a special case as they're real CSS colors, not Qt-CSS colors. There, for a gradient, you need to use `-webkit-gradient`, see https://www.webkit.org/blog/175/introducing-css-gradients/[the WebKit documentation]. +[colors] + +# completion.fg (QtColor): +# Text color of the completion widget. +# Default: white +completion.fg = white + +# completion.bg (QssColor): +# Background color of the completion widget. +# Default: #333333 +completion.bg = #333333 + +# completion.alternate-bg (QssColor): +# Alternating background color of the completion widget. +# Default: #444444 +completion.alternate-bg = #444444 + +# completion.category.fg (QtColor): +# Foreground color of completion widget category headers. +# Default: white +completion.category.fg = white + +# completion.category.bg (QssColor): +# Background color of the completion widget category headers. +# Default: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #888888, +# stop:1 #505050) +completion.category.bg = qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #888888, stop:1 #505050) + +# completion.category.border.top (QssColor): +# Top border color of the completion widget category headers. +# Default: black +completion.category.border.top = black + +# completion.category.border.bottom (QssColor): +# Bottom border color of the completion widget category headers. +# Default: ${completion.category.border.top} +completion.category.border.bottom = ${completion.category.border.top} + +# completion.item.selected.fg (QtColor): +# Foreground color of the selected completion item. +# Default: black +completion.item.selected.fg = black + +# completion.item.selected.bg (QssColor): +# Background color of the selected completion item. +# Default: #e8c000 +completion.item.selected.bg = #e8c000 + +# completion.item.selected.border.top (QssColor): +# Top border color of the completion widget category headers. +# Default: #bbbb00 +completion.item.selected.border.top = #bbbb00 + +# completion.item.selected.border.bottom (QssColor): +# Bottom border color of the selected completion item. +# Default: ${completion.item.selected.border.top} +completion.item.selected.border.bottom = ${completion.item.selected.border.top} + +# completion.match.fg (QssColor): +# Foreground color of the matched text in the completion. +# Default: #ff4444 +completion.match.fg = #ff4444 + +# completion.scrollbar.fg (QssColor): +# Color of the scrollbar handle in completion view. +# Default: ${completion.fg} +completion.scrollbar.fg = ${completion.fg} + +# completion.scrollbar.bg (QssColor): +# Color of the scrollbar in completion view +# Default: ${completion.bg} +completion.scrollbar.bg = ${completion.bg} + +# statusbar.fg (QssColor): +# Foreground color of the statusbar. +# Default: white +statusbar.fg = white + +# statusbar.bg (QssColor): +# Background color of the statusbar. +# Default: black +statusbar.bg = black + +# statusbar.fg.private (QssColor): +# Foreground color of the statusbar in private browsing mode. +# Default: ${statusbar.fg} +statusbar.fg.private = ${statusbar.fg} + +# statusbar.bg.private (QssColor): +# Background color of the statusbar in private browsing mode. +# Default: #666666 +statusbar.bg.private = #666666 + +# statusbar.fg.insert (QssColor): +# Foreground color of the statusbar in insert mode. +# Default: ${statusbar.fg} +statusbar.fg.insert = ${statusbar.fg} + +# statusbar.bg.insert (QssColor): +# Background color of the statusbar in insert mode. +# Default: darkgreen +statusbar.bg.insert = darkgreen + +# statusbar.fg.command (QssColor): +# Foreground color of the statusbar in command mode. +# Default: ${statusbar.fg} +statusbar.fg.command = ${statusbar.fg} + +# statusbar.bg.command (QssColor): +# Background color of the statusbar in command mode. +# Default: ${statusbar.bg} +statusbar.bg.command = ${statusbar.bg} + +# statusbar.fg.command.private (QssColor): +# Foreground color of the statusbar in private browsing + command +# mode. +# Default: ${statusbar.fg.private} +statusbar.fg.command.private = ${statusbar.fg.private} + +# statusbar.bg.command.private (QssColor): +# Background color of the statusbar in private browsing + command +# mode. +# Default: ${statusbar.bg.private} +statusbar.bg.command.private = ${statusbar.bg.private} + +# statusbar.fg.caret (QssColor): +# Foreground color of the statusbar in caret mode. +# Default: ${statusbar.fg} +statusbar.fg.caret = ${statusbar.fg} + +# statusbar.bg.caret (QssColor): +# Background color of the statusbar in caret mode. +# Default: purple +statusbar.bg.caret = purple + +# statusbar.fg.caret-selection (QssColor): +# Foreground color of the statusbar in caret mode with a selection +# Default: ${statusbar.fg} +statusbar.fg.caret-selection = ${statusbar.fg} + +# statusbar.bg.caret-selection (QssColor): +# Background color of the statusbar in caret mode with a selection +# Default: #a12dff +statusbar.bg.caret-selection = #a12dff + +# statusbar.progress.bg (QssColor): +# Background color of the progress bar. +# Default: white +statusbar.progress.bg = white + +# statusbar.url.fg (QssColor): +# Default foreground color of the URL in the statusbar. +# Default: ${statusbar.fg} +statusbar.url.fg = ${statusbar.fg} + +# statusbar.url.fg.success (QssColor): +# Foreground color of the URL in the statusbar on successful load +# (http). +# Default: white +statusbar.url.fg.success = white + +# statusbar.url.fg.success.https (QssColor): +# Foreground color of the URL in the statusbar on successful load +# (https). +# Default: lime +statusbar.url.fg.success.https = lime + +# statusbar.url.fg.error (QssColor): +# Foreground color of the URL in the statusbar on error. +# Default: orange +statusbar.url.fg.error = orange + +# statusbar.url.fg.warn (QssColor): +# Foreground color of the URL in the statusbar when there's a +# warning. +# Default: yellow +statusbar.url.fg.warn = yellow + +# statusbar.url.fg.hover (QssColor): +# Foreground color of the URL in the statusbar for hovered links. +# Default: aqua +statusbar.url.fg.hover = aqua + +# tabs.fg.odd (QtColor): +# Foreground color of unselected odd tabs. +# Default: white +tabs.fg.odd = white + +# tabs.bg.odd (QtColor): +# Background color of unselected odd tabs. +# Default: grey +tabs.bg.odd = grey + +# tabs.fg.even (QtColor): +# Foreground color of unselected even tabs. +# Default: white +tabs.fg.even = white + +# tabs.bg.even (QtColor): +# Background color of unselected even tabs. +# Default: darkgrey +tabs.bg.even = darkgrey + +# tabs.fg.selected.odd (QtColor): +# Foreground color of selected odd tabs. +# Default: white +tabs.fg.selected.odd = white + +# tabs.bg.selected.odd (QtColor): +# Background color of selected odd tabs. +# Default: black +tabs.bg.selected.odd = black + +# tabs.fg.selected.even (QtColor): +# Foreground color of selected even tabs. +# Default: ${tabs.fg.selected.odd} +tabs.fg.selected.even = ${tabs.fg.selected.odd} + +# tabs.bg.selected.even (QtColor): +# Background color of selected even tabs. +# Default: ${tabs.bg.selected.odd} +tabs.bg.selected.even = ${tabs.bg.selected.odd} + +# tabs.bg.bar (QtColor): +# Background color of the tab bar. +# Default: #555555 +tabs.bg.bar = #555555 + +# tabs.indicator.start (QtColor): +# Color gradient start for the tab indicator. +# Default: #0000aa +tabs.indicator.start = #0000aa + +# tabs.indicator.stop (QtColor): +# Color gradient end for the tab indicator. +# Default: #00aa00 +tabs.indicator.stop = #00aa00 + +# tabs.indicator.error (QtColor): +# Color for the tab indicator on errors.. +# Default: #ff0000 +tabs.indicator.error = #ff0000 + +# tabs.indicator.system (ColorSystem): +# Color gradient interpolation system for the tab indicator. +# rgb: Interpolate in the RGB color system. +# hsv: Interpolate in the HSV color system. +# hsl: Interpolate in the HSL color system. +# none: Don't show a gradient. +# Default: rgb +tabs.indicator.system = rgb + +# hints.fg (QssColor): +# Font color for hints. +# Default: black +hints.fg = black + +# hints.bg (QssColor): +# Background color for hints. Note that you can use a `rgba(...)` +# value for transparency. +# Default: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgba(255, +# 247, 133, 0.8), stop:1 rgba(255, 197, 66, 0.8)) +hints.bg = qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgba(255, 247, 133, 0.8), stop:1 rgba(255, 197, 66, 0.8)) + +# hints.fg.match (QssColor): +# Font color for the matched part of hints. +# Default: green +hints.fg.match = green + +# downloads.bg.bar (QssColor): +# Background color for the download bar. +# Default: black +downloads.bg.bar = black + +# downloads.fg.start (QtColor): +# Color gradient start for download text. +# Default: white +downloads.fg.start = white + +# downloads.bg.start (QtColor): +# Color gradient start for download backgrounds. +# Default: #0000aa +downloads.bg.start = #0000aa + +# downloads.fg.stop (QtColor): +# Color gradient end for download text. +# Default: ${downloads.fg.start} +downloads.fg.stop = ${downloads.fg.start} + +# downloads.bg.stop (QtColor): +# Color gradient stop for download backgrounds. +# Default: #00aa00 +downloads.bg.stop = #00aa00 + +# downloads.fg.system (ColorSystem): +# Color gradient interpolation system for download text. +# rgb: Interpolate in the RGB color system. +# hsv: Interpolate in the HSV color system. +# hsl: Interpolate in the HSL color system. +# none: Don't show a gradient. +# Default: rgb +downloads.fg.system = rgb + +# downloads.bg.system (ColorSystem): +# Color gradient interpolation system for download backgrounds. +# rgb: Interpolate in the RGB color system. +# hsv: Interpolate in the HSV color system. +# hsl: Interpolate in the HSL color system. +# none: Don't show a gradient. +# Default: rgb +downloads.bg.system = rgb + +# downloads.fg.error (QtColor): +# Foreground color for downloads with errors. +# Default: white +downloads.fg.error = white + +# downloads.bg.error (QtColor): +# Background color for downloads with errors. +# Default: red +downloads.bg.error = red + +# webpage.bg (QtColor): +# Background color for webpages if unset (or empty to use the +# theme's color) +# Default: white +webpage.bg = white + +# keyhint.fg (QssColor): +# Text color for the keyhint widget. +# Default: #FFFFFF +keyhint.fg = #FFFFFF + +# keyhint.fg.suffix (CssColor): +# Highlight color for keys to complete the current keychain +# Default: #FFFF00 +keyhint.fg.suffix = #FFFF00 + +# keyhint.bg (QssColor): +# Background color of the keyhint widget. +# Default: rgba(0, 0, 0, 80%) +keyhint.bg = rgba(0, 0, 0, 80%) + +# messages.fg.error (QssColor): +# Foreground color of an error message. +# Default: white +messages.fg.error = white + +# messages.bg.error (QssColor): +# Background color of an error message. +# Default: red +messages.bg.error = red + +# messages.border.error (QssColor): +# Border color of an error message. +# Default: #bb0000 +messages.border.error = #bb0000 + +# messages.fg.warning (QssColor): +# Foreground color a warning message. +# Default: white +messages.fg.warning = white + +# messages.bg.warning (QssColor): +# Background color of a warning message. +# Default: darkorange +messages.bg.warning = darkorange + +# messages.border.warning (QssColor): +# Border color of an error message. +# Default: #d47300 +messages.border.warning = #d47300 + +# messages.fg.info (QssColor): +# Foreground color an info message. +# Default: white +messages.fg.info = white + +# messages.bg.info (QssColor): +# Background color of an info message. +# Default: black +messages.bg.info = black + +# messages.border.info (QssColor): +# Border color of an info message. +# Default: #333333 +messages.border.info = #333333 + +# prompts.fg (QssColor): +# Foreground color for prompts. +# Default: white +prompts.fg = white + +# prompts.bg (QssColor): +# Background color for prompts. +# Default: darkblue +prompts.bg = darkblue + +# prompts.selected.bg (QssColor): +# Background color for the selected item in filename prompts. +# Default: #308cc6 +prompts.selected.bg = #308cc6 + + +# Fonts used for the UI, with optional style/weight/size. +# * Style: `normal`/`italic`/`oblique` +# * Weight: `normal`, `bold`, `100`..`900` +# * Size: _number_ `px`/`pt` +[fonts] + +# _monospace (Font): +# Default monospace fonts. +# Default: xos4 Terminus, Terminus, Monospace, "DejaVu Sans Mono", +# Monaco, "Bitstream Vera Sans Mono", "Andale Mono", "Courier New", +# Courier, "Liberation Mono", monospace, Fixed, Consolas, Terminal +_monospace = Terminus, Monospace, "DejaVu Sans Mono", Monaco, "Bitstream Vera Sans Mono", "Andale Mono", "Courier New", Courier, "Liberation Mono", monospace, Fixed, Consolas, Terminal + +# completion (Font): +# Font used in the completion widget. +# Default: 8pt ${_monospace} +completion = 8pt ${_monospace} + +# completion.category (Font): +# Font used in the completion categories. +# Default: bold ${completion} +completion.category = bold ${completion} + +# tabbar (QtFont): +# Font used in the tab bar. +# Default: 8pt ${_monospace} +tabbar = 8pt ${_monospace} + +# statusbar (Font): +# Font used in the statusbar. +# Default: 8pt ${_monospace} +statusbar = 8pt ${_monospace} + +# downloads (Font): +# Font used for the downloadbar. +# Default: 8pt ${_monospace} +downloads = 8pt ${_monospace} + +# hints (Font): +# Font used for the hints. +# Default: bold 13px ${_monospace} +hints = bold 13px ${_monospace} + +# debug-console (QtFont): +# Font used for the debugging console. +# Default: 8pt ${_monospace} +debug-console = 8pt ${_monospace} + +# web-family-standard (FontFamily): +# Font family for standard fonts. +# Default: +web-family-standard = + +# web-family-fixed (FontFamily): +# Font family for fixed fonts. +# Default: +web-family-fixed = + +# web-family-serif (FontFamily): +# Font family for serif fonts. +# Default: +web-family-serif = + +# web-family-sans-serif (FontFamily): +# Font family for sans-serif fonts. +# Default: +web-family-sans-serif = + +# web-family-cursive (FontFamily): +# Font family for cursive fonts. +# Default: +web-family-cursive = + +# web-family-fantasy (FontFamily): +# Font family for fantasy fonts. +# Default: +web-family-fantasy = + +# web-size-minimum (Int): +# The hard minimum font size. +# Default: 0 +web-size-minimum = 0 + +# web-size-minimum-logical (Int): +# The minimum logical font size that is applied when zooming out. +# Default: 6 +web-size-minimum-logical = 6 + +# web-size-default (Int): +# The default font size for regular text. +# Default: 16 +web-size-default = 16 + +# web-size-default-fixed (Int): +# The default font size for fixed-pitch text. +# Default: 13 +web-size-default-fixed = 13 + +# keyhint (Font): +# Font used in the keyhint widget. +# Default: 8pt ${_monospace} +keyhint = 8pt ${_monospace} + +# messages.error (Font): +# Font used for error messages. +# Default: 8pt ${_monospace} +messages.error = 8pt ${_monospace} + +# messages.warning (Font): +# Font used for warning messages. +# Default: 8pt ${_monospace} +messages.warning = 8pt ${_monospace} + +# messages.info (Font): +# Font used for info messages. +# Default: 8pt ${_monospace} +messages.info = 8pt ${_monospace} + +# prompts (Font): +# Font used for prompts. +# Default: 8pt sans-serif +prompts = 8pt sans-serif diff --git a/.config/qutebrowser/keys.conf b/.config/qutebrowser/keys.conf new file mode 100755 index 0000000..06fc600 --- /dev/null +++ b/.config/qutebrowser/keys.conf @@ -0,0 +1,709 @@ +# vim: ft=conf +# +# In this config file, qutebrowser's key bindings are configured. +# The format looks like this: +# +# [keymode] +# +# command +# keychain +# keychain2 +# ... +# +# All blank lines and lines starting with '#' are ignored. +# Inline-comments are not permitted. +# +# keymode is a comma separated list of modes in which the key binding should be +# active. If keymode starts with !, the key binding is active in all modes +# except the listed modes. +# +# For special keys (can't be part of a keychain), enclose them in `<`...`>`. +# For modifiers, you can use either `-` or `+` as delimiters, and these names: +# +# * Control: `Control`, `Ctrl` +# * Meta: `Meta`, `Windows`, `Mod4` +# * Alt: `Alt`, `Mod1` +# * Shift: `Shift` +# +# For simple keys (no `<>`-signs), a capital letter means the key is pressed +# with Shift. For special keys (with `<>`-signs), you need to explicitly add +# `Shift-` to match a key pressed with shift. +# +# Note that default keybindings are always bound, and need to be explicitly +# unbound if you wish to remove them: +# +# <unbound> +# keychain +# keychain2 +# ... + +[!normal] + +leave-mode + <escape> + <ctrl-[> + +[normal] +# Keybindings for normal mode. + +clear-keychain ;; search ;; fullscreen --leave + <escape> + <ctrl-[> + +set-cmd-text -s :open + o + +set-cmd-text :open {url:pretty} + go + +set-cmd-text -s :open -t + O + +set-cmd-text :open -t -i {url:pretty} + gO + +set-cmd-text -s :open -b + xo + +set-cmd-text :open -b -i {url:pretty} + xO + +set-cmd-text -s :open -w + wo + +set-cmd-text :open -w {url:pretty} + wO + +set-cmd-text / + / + +set-cmd-text ? + ? + +set-cmd-text : + : + +open -t + ga + <ctrl-t> + +open -w + <ctrl-n> + +tab-close + d + <ctrl-w> + +tab-close -o + D + +tab-only + co + +tab-focus + T + +tab-move + gm + +tab-move - + gl + +tab-move + + gr + +tab-next + J + <ctrl-pgdown> + +tab-prev + K + <ctrl-pgup> + +tab-clone + gC + +reload + r + <f5> + +reload -f + R + <ctrl-f5> + +back + H + <back> + +back -t + th + +back -w + wh + +forward + L + <forward> + +forward -t + tl + +forward -w + wl + +fullscreen + <f11> + +hint + f + +hint all tab + F + +hint all window + wf + +hint all tab-bg + ;b + +hint all tab-fg + ;f + +hint all hover + ;h + +hint images + ;i + +hint images tab + ;I + +hint links fill :open {hint-url} + ;o + +hint links fill :open -t -i {hint-url} + ;O + +hint links yank + ;y + +hint links yank-primary + ;Y + +hint --rapid links tab-bg + ;r + +hint --rapid links window + ;R + +hint links download + ;d + +hint inputs + ;t + +scroll left + h + +scroll down + j + +scroll up + k + +scroll right + l + +undo + u + <ctrl-shift-t> + +scroll-perc 0 + gg + +scroll-perc + G + +search-next + n + +search-prev + N + +enter-mode insert + i + +enter-mode caret + v + +enter-mode set_mark + ` + +enter-mode jump_mark + ' + +yank + yy + +yank -s + yY + +yank title + yt + +yank title -s + yT + +yank domain + yd + +yank domain -s + yD + +yank pretty-url + yp + +yank pretty-url -s + yP + +open -- {clipboard} + pp + +open -- {primary} + pP + +open -t -- {clipboard} + Pp + +open -t -- {primary} + PP + +open -w -- {clipboard} + wp + +open -w -- {primary} + wP + +set-cmd-text -s :quickmark-load + b + +set-cmd-text -s :quickmark-load -t + B + +set-cmd-text -s :quickmark-load -w + wb + +set-cmd-text -s :bookmark-load + gb + +set-cmd-text -s :bookmark-load -t + gB + +set-cmd-text -s :bookmark-load -w + wB + +save + sf + +set-cmd-text -s :set + ss + +set-cmd-text -s :set -t + sl + +set-cmd-text -s :bind + sk + +zoom-out + - + +zoom-in + + + +zoom + = + +navigate prev + [[ + +navigate next + ]] + +navigate prev -t + {{ + +navigate next -t + }} + +navigate up + gu + +navigate up -t + gU + +navigate increment + <ctrl-a> + +navigate decrement + <ctrl-x> + +inspector + wi + +download + gd + +download-cancel + ad + +download-clear + cd + +view-source + gf + +set-cmd-text -s :buffer + gt + +tab-focus last + <ctrl-tab> + +enter-mode passthrough + <ctrl-v> + +quit + <ctrl-q> + +scroll-page 0 1 + <ctrl-f> + +scroll-page 0 -1 + <ctrl-b> + +scroll-page 0 0.5 + <ctrl-d> + +scroll-page 0 -0.5 + <ctrl-u> + +tab-focus 1 + <alt-1> + +tab-focus 2 + <alt-2> + +tab-focus 3 + <alt-3> + +tab-focus 4 + <alt-4> + +tab-focus 5 + <alt-5> + +tab-focus 6 + <alt-6> + +tab-focus 7 + <alt-7> + +tab-focus 8 + <alt-8> + +tab-focus 9 + <alt-9> + +home + <ctrl-h> + +stop + <ctrl-s> + +print + <ctrl-alt-p> + +open qute:settings + Ss + +follow-selected + <return> + <ctrl-m> + <ctrl-j> + <shift-return> + <enter> + <shift-enter> + +follow-selected -t + <ctrl-return> + <ctrl-enter> + +repeat-command + . + +record-macro + q + +run-macro + @ + +spawn mpv {url} + mpv + m + +hint links spawn umpv {hint-urls} + MPV + +hint links spawn mpv {hint-url} + M + +spawn tsp-ytdl {hint-url} + ;M + +wq + ZZ + +tab-focus -1 + g$ + +tab-pin + <ctrl-p> + +[insert] +# Keybindings for insert mode. +# Since normal keypresses are passed through, only special keys are +# supported in this mode. +# Useful hidden commands to map in this section: +# * `open-editor`: Open a texteditor with the focused field. +# * `paste-primary`: Paste primary selection at cursor position. + +open-editor + <ctrl-e> + +insert-text {primary} + <shift-ins> + +[hint] +# Keybindings for hint mode. +# Since normal keypresses are passed through, only special keys are +# supported in this mode. +# Useful hidden commands to map in this section: +# * `follow-hint`: Follow the currently selected hint. + +follow-hint + <return> + <ctrl-m> + <ctrl-j> + <shift-return> + <enter> + <shift-enter> + +hint --rapid links tab-bg + <ctrl-r> + +hint links + <ctrl-f> + +hint all tab-bg + <ctrl-b> + +[command] +# Keybindings for command mode. +# Since normal keypresses are passed through, only special keys are +# supported in this mode. +# Useful hidden commands to map in this section: +# * `command-history-prev`: Switch to previous command in history. +# * `command-history-next`: Switch to next command in history. +# * `completion-item-focus`: Select another item in completion. +# * `command-accept`: Execute the command currently in the commandline. + +command-history-prev + <ctrl-p> + +command-history-next + <ctrl-n> + +completion-item-focus prev + <shift-tab> + <up> + +completion-item-focus next + <tab> + <down> + +completion-item-focus next-category + <ctrl-tab> + +completion-item-focus prev-category + <ctrl-shift-tab> + +completion-item-del + <ctrl-d> + +command-accept + <return> + <ctrl-m> + <ctrl-j> + <shift-return> + <enter> + <shift-enter> + +[prompt] +# Keybindings for prompts in the status line. +# You can bind normal keys in this mode, but they will be only active +# when a yes/no-prompt is asked. For other prompt modes, you can only +# bind special keys. +# Useful hidden commands to map in this section: +# * `prompt-accept`: Confirm the entered value. +# * `prompt-accept yes`: Answer yes to a yes/no question. +# * `prompt-accept no`: Answer no to a yes/no question. + +prompt-accept + <return> + <ctrl-m> + <ctrl-j> + <shift-return> + <enter> + <shift-enter> + +prompt-accept yes + y + +prompt-accept no + n + +prompt-open-download + <ctrl-x> + +prompt-item-focus prev + <shift-tab> + <up> + +prompt-item-focus next + <tab> + <down> + +[command,prompt] + +rl-backward-char + <ctrl-b> + +rl-forward-char + <ctrl-f> + +rl-backward-word + <alt-b> + +rl-forward-word + <alt-f> + +rl-beginning-of-line + <ctrl-a> + +rl-end-of-line + <ctrl-e> + +rl-unix-line-discard + <ctrl-u> + +rl-kill-line + <ctrl-k> + +rl-kill-word + <alt-d> + +rl-unix-word-rubout + <ctrl-w> + +rl-backward-kill-word + <alt-backspace> + +rl-yank + <ctrl-y> + +rl-delete-char + <ctrl-?> + +rl-backward-delete-char + <ctrl-h> + +[caret] + +toggle-selection + v + <space> + +drop-selection + <ctrl-space> + +enter-mode normal + c + +move-to-next-line + j + +move-to-prev-line + k + +move-to-next-char + l + +move-to-prev-char + h + +move-to-end-of-word + e + +move-to-next-word + w + +move-to-prev-word + b + +move-to-start-of-next-block + ] + +move-to-start-of-prev-block + [ + +move-to-end-of-next-block + } + +move-to-end-of-prev-block + { + +move-to-start-of-line + 0 + +move-to-end-of-line + $ + +move-to-start-of-document + gg + +move-to-end-of-document + G + +yank selection -s + Y + +yank selection + y + <return> + <ctrl-m> + <ctrl-j> + <shift-return> + <enter> + <shift-enter> + +scroll left + H + +scroll down + J + +scroll up + K + +scroll right + L + diff --git a/.config/qutebrowser/misc/Makefile b/.config/qutebrowser/misc/Makefile new file mode 100644 index 0000000..210480e --- /dev/null +++ b/.config/qutebrowser/misc/Makefile @@ -0,0 +1,33 @@ +PYTHON = python3 +PREFIX = /usr/local +DESTDIR = +ICONSIZES = 16 24 32 48 64 128 256 512 + +SETUPTOOLSOPTIONS = +ifdef DESTDIR +SETUPTOOLSOPTS = --root="$(DESTDIR)" +endif + +.PHONY: install + +doc/qutebrowser.1.html: + a2x -f manpage doc/qutebrowser.1.asciidoc + +install: doc/qutebrowser.1.html + $(PYTHON) setup.py install --prefix="$(PREFIX)" --optimize=1 $(SETUPTOOLSOPTS) + install -Dm644 misc/qutebrowser.appdata.xml \ + "$(DESTDIR)$(PREFIX)/share/metainfo/qutebrowser.appdata.xml" + install -Dm644 doc/qutebrowser.1 \ + "$(DESTDIR)$(PREFIX)/share/man/man1/qutebrowser.1" + install -Dm644 misc/qutebrowser.desktop \ + "$(DESTDIR)$(PREFIX)/share/applications/qutebrowser.desktop" + $(foreach i,$(ICONSIZES),install -Dm644 "icons/qutebrowser-$(i)x$(i).png" \ + "$(DESTDIR)$(PREFIX)/share/icons/hicolor/$(i)x$(i)/apps/qutebrowser.png";) + install -Dm644 icons/qutebrowser.svg \ + "$(DESTDIR)$(PREFIX)/share/icons/hicolor/scalable/apps/qutebrowser.svg" + install -Dm755 -t "$(DESTDIR)$(PREFIX)/share/qutebrowser/userscripts/" \ + $(wildcard misc/userscripts/*) + install -Dm755 -t "$(DESTDIR)$(PREFIX)/share/qutebrowser/scripts/" \ + $(filter-out scripts/__init__.py scripts/__pycache__ scripts/dev \ + scripts/testbrowser scripts/asciidoc2html.py scripts/setupcommon.py \ + scripts/link_pyqt.py,$(wildcard scripts/*)) diff --git a/.config/qutebrowser/misc/apparmor/usr.bin.qutebrowser b/.config/qutebrowser/misc/apparmor/usr.bin.qutebrowser new file mode 100644 index 0000000..b993e00 --- /dev/null +++ b/.config/qutebrowser/misc/apparmor/usr.bin.qutebrowser @@ -0,0 +1,41 @@ +# AppArmor profile for qutebrowser +# Tested on Debian jessie + +#include <tunables/global> + +profile qutebrowser /usr/{local/,}bin/qutebrowser { + + #include <abstractions/base> + #include <abstractions/nameservice> + #include <abstractions/openssl> + #include <abstractions/ssl_certs> + #include <abstractions/audio> + #include <abstractions/fonts> + #include <abstractions/kde> + #include <abstractions/user-download> + #include <abstractions/X> + + capability dac_override, + + /usr/{local/,}bin/ r, + /usr/{local/,}bin/qutebrowser rix, + /usr/bin/python3.? r, + + /usr/lib/python3/ mr, + /usr/lib/python3/** mr, + /usr/lib/python3.?/ r, + /usr/lib/python3.?/** mr, + /usr/local/lib/python3.?/** r, + + /proc/*/mounts r, + owner /tmp/** rwkl, + owner /run/user/*/ rw, + owner /run/user/*/** krw, + + @{HOME}/.config/qutebrowser/** krw, + @{HOME}/.local/share/qutebrowser/** krw, + @{HOME}/.cache/qutebrowser/** krw, + @{HOME}/.gstreamer-0.10/* r, + +} + diff --git a/.config/qutebrowser/misc/cheatsheet.svg b/.config/qutebrowser/misc/cheatsheet.svg new file mode 100644 index 0000000..bb87142 --- /dev/null +++ b/.config/qutebrowser/misc/cheatsheet.svg @@ -0,0 +1,3726 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="1092.2667" + height="682.66669" + id="svg2" + sodipodi:version="0.32" + inkscape:version="0.92.2 5c3e80d, 2017-08-06" + version="1.0" + sodipodi:docname="cheatsheet.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape" + inkscape:export-filename="/home/vav/images/xmonad/xmbindings_lg.png" + inkscape:export-xdpi="112.5" + inkscape:export-ydpi="112.5"> + <defs + id="defs4" /> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + gridtolerance="10000" + guidetolerance="10" + objecttolerance="10" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="1.7536248" + inkscape:cx="430.72917" + inkscape:cy="268.64059" + inkscape:document-units="px" + inkscape:current-layer="layer1" + width="1024px" + height="640px" + showgrid="false" + inkscape:window-width="2560" + inkscape:window-height="1440" + inkscape:window-x="0" + inkscape:window-y="0" + showguides="true" + inkscape:guide-bbox="true" + inkscape:window-maximized="0" + inkscape:snap-text-baseline="true" + inkscape:measure-start="0,0" + inkscape:measure-end="0,0"> + <inkscape:grid + id="GridFromPre046Settings" + type="xygrid" + originx="0" + originy="0" + spacingx="2.1333333" + spacingy="2.1333333" + color="#3f3fff" + empcolor="#3f3fff" + opacity="0.15" + empopacity="0.38" + empspacing="5" /> + </sodipodi:namedview> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1"> + <rect + ry="4.7797003" + y="163.79405" + x="10.666667" + height="64" + width="74.666664" + id="rect3328" + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" /> + <rect + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" + id="rect3330" + width="64" + height="64" + x="96" + y="163.79405" + ry="4.7797003" /> + <rect + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" + id="rect3382" + width="63.461262" + height="64" + x="544.48407" + y="163.79405" + ry="4.7797003" /> + <rect + ry="4.7797003" + y="163.79405" + x="619.1535" + height="64" + width="63.461262" + id="rect3384" + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" /> + <rect + ry="4.7797003" + y="163.79405" + x="469.8172" + height="64" + width="63.461262" + id="rect3386" + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" /> + <rect + ry="4.7797003" + y="163.79405" + x="768.48407" + height="64" + width="63.461262" + id="rect3388" + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" /> + <rect + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" + id="rect3392" + width="63.461262" + height="64" + x="693.82019" + y="163.79405" + ry="4.7797003" /> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="99.751244" + y="187.30594" + id="text3444"><tspan + sodipodi:role="line" + id="tspan3446" + x="99.751244" + y="187.30594" + style="font-size:19.20000076px;line-height:1.25;stroke-width:1.06666672">Q</tspan></text> + <rect + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" + id="rect2739" + width="63.461262" + height="64" + x="320.05182" + y="163.79405" + ry="4.7797003" /> + <text + id="text3448" + y="187.60356" + x="323.15463" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="187.60356" + x="323.15463" + id="tspan3450" + sodipodi:role="line" + style="font-size:19.20000076px;line-height:1.25;stroke-width:1.06666672">R</tspan></text> + <rect + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" + id="rect7301" + width="63.461262" + height="64" + x="171.15358" + y="163.79405" + ry="4.7797003" /> + <path + style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 171.24757,196.86071 c 21.0911,0 42.18218,0 63.27327,0" + id="path7305" + inkscape:connector-curvature="0" /> + <text + id="text3464" + y="187.92427" + x="175.64062" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:monospace;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="187.92427" + x="175.64062" + sodipodi:role="line" + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672" + id="tspan3498">W</tspan></text> + <text + id="text3468" + y="216.29616" + x="99.515625" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="216.29616" + x="99.515625" + id="tspan3470" + sodipodi:role="line" + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">q</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="175.64062" + y="220.43825" + id="text3472"><tspan + sodipodi:role="line" + id="tspan3474" + x="175.64062" + y="220.43825" + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">w</tspan></text> + <path + style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16661239px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 96.637962,195.79405 c 21.265388,0 42.530768,0 63.796148,0" + id="path3476" + inkscape:connector-curvature="0" /> + <g + id="g7271" + transform="translate(0,16.59406)" + style="fill:#eeeeec;fill-opacity:1"> + <rect + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" + id="rect3378" + width="63.461262" + height="64" + x="395.15076" + y="147.2" + ry="4.7797003" /> + <rect + ry="2.4296808" + y="147.2" + x="395.15076" + height="32.533333" + width="63.461262" + id="rect7269" + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" /> + <path + style="font-size:18px;fill:#eeeeec;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.16661239px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 395.24997,179.73333 c 21.26539,0 42.53077,0 63.79615,0" + id="path3484" + inkscape:connector-curvature="0" /> + </g> + <path + style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.1617893px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 469.91423,196.86071 c 21.08993,0 42.17982,0 63.26974,0" + id="path3486" + inkscape:connector-curvature="0" /> + <path + style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16661239px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 544.58331,196.86071 c 21.26538,0 42.53076,0 63.79615,0" + id="path3488" + inkscape:connector-curvature="0" /> + <path + id="path3490" + d="m 619.24757,196.32738 c 21.0911,0 42.18218,0 63.27327,0" + style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + inkscape:connector-curvature="0" /> + <path + id="path3492" + d="m 693.91424,196.86071 c 21.09109,0 42.18218,0 63.27327,0" + style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + inkscape:connector-curvature="0" /> + <path + id="path3494" + d="m 768.58331,196.86071 c 21.26538,0 42.53076,0 63.79615,0" + style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16661239px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + inkscape:connector-curvature="0" /> + <rect + ry="4.7797003" + y="163.79405" + x="245.38516" + height="64" + width="63.461262" + id="rect2735" + style="font-size:18px;fill:#babdb6;fill-opacity:1;stroke:none;stroke-width:1.06666672" /> + <path + id="path2737" + d="m 245.47916,195.19999 c 21.09109,0 42.18217,0 63.27327,0" + style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + inkscape:connector-curvature="0" /> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="248.78169" + y="187.60356" + id="text3502"><tspan + sodipodi:role="line" + id="tspan3504" + x="248.78169" + y="187.60356" + style="font-size:19.20000076px;line-height:1.25;stroke-width:1.06666672">E</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="248.64069" + y="220.16637" + id="text3506"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672" + sodipodi:role="line" + id="tspan3508" + x="248.64069" + y="220.16637">e</tspan></text> + <path + style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 320.14582,195.19999 c 21.0911,0 42.18218,0 63.27327,0" + id="path2741" + inkscape:connector-curvature="0" /> + <text + id="text3510" + y="219.88054" + x="321.97186" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';fill:#0000ff;stroke-width:1.06666672" + y="219.88054" + x="321.97186" + id="tspan3512" + sodipodi:role="line">r</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="399.83853" + y="220.32741" + id="text3532"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672" + sodipodi:role="line" + id="tspan3534" + x="399.83853" + y="220.32741">t</tspan></text> + <text + id="text3536" + y="217.93365" + x="474.22501" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672" + y="217.93365" + x="474.22501" + id="tspan3538" + sodipodi:role="line">y</tspan></text> + <text + id="text3540" + y="219.90315" + x="547.44312" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="219.90315" + x="547.44312" + id="tspan3542" + sodipodi:role="line" + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';fill:#0000ff;stroke-width:1.06666672">u</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="622.33124" + y="220.32741" + id="text3544"><tspan + sodipodi:role="line" + id="tspan3546" + x="622.33124" + y="220.32741" + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">i</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="698.52502" + y="219.88054" + id="text3548"><tspan + sodipodi:role="line" + id="tspan3550" + x="698.52502" + y="219.88054" + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">o</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="772.01251" + y="219.88054" + id="text3552"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672" + sodipodi:role="line" + id="tspan3554" + x="772.01251" + y="219.88054">p</tspan></text> + <text + id="text3556" + y="201.17621" + x="851.88257" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + xml:space="preserve"><tspan + style="font-size:23.46666718px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672px" + y="201.17621" + x="851.88257" + id="tspan3558" + sodipodi:role="line"> </tspan></text> + <rect + ry="4.7797003" + y="238.46074" + x="106.23158" + height="64" + width="64" + id="rect3616" + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" /> + <rect + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" + id="rect3618" + width="63.461262" + height="64" + x="181.38516" + y="238.46074" + ry="4.7797003" /> + <rect + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" + id="rect3620" + width="64.538773" + height="64" + x="255.56491" + y="238.46074" + ry="4.7797003" /> + <rect + ry="4.7797003" + y="238.46074" + x="332.05182" + height="64" + width="63.461262" + id="rect3622" + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" /> + <rect + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" + id="rect3624" + width="63.461262" + height="64" + x="405.38232" + y="238.46074" + ry="4.7797003" /> + <rect + ry="4.7797003" + y="238.46074" + x="554.71564" + height="64" + width="63.461262" + id="rect3626" + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" /> + <rect + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" + id="rect3628" + width="63.461262" + height="64" + x="629.38507" + y="238.46074" + ry="4.7797003" /> + <rect + ry="4.7797003" + y="238.46074" + x="779.1535" + height="64" + width="63.948242" + id="rect3634" + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" /> + <text + id="text3638" + y="262.07977" + x="110.55427" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="262.07977" + x="110.55427" + id="tspan3640" + sodipodi:role="line" + style="font-size:19.20000076px;line-height:1.25;stroke-width:1.06666672">A</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="335.79037" + y="264.53333" + id="text3642"><tspan + sodipodi:role="line" + id="tspan3644" + x="335.79037" + y="264.53333" + style="font-size:19.20000076px;line-height:1.25;stroke-width:1.06666672">F</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:monospace;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="185.38518" + y="261.80792" + id="text3646"><tspan + id="tspan3648" + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672" + sodipodi:role="line" + x="185.38518" + y="261.80792">S</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="110.07188" + y="295.08054" + id="text3650"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672" + sodipodi:role="line" + id="tspan3652" + x="110.07188" + y="295.08054">a</tspan></text> + <text + id="text3654" + y="295.08054" + x="185.54375" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672" + y="295.08054" + x="185.54375" + id="tspan3656" + sodipodi:role="line">s</tspan></text> + <path + id="path3658" + d="m 106.86955,270.46071 c 21.26538,0 42.53076,0 63.79615,0" + style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16661239px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + inkscape:connector-curvature="0" /> + <path + style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 181.47916,270.99405 c 21.09109,0 42.18217,0 63.27327,0" + id="path3660" + inkscape:connector-curvature="0" /> + <path + style="font-size:18px;fill:none;stroke:#000000;stroke-width:0.60877639px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 256.71172,270.08518 c 20.26767,0 40.53532,0 60.80299,0" + id="path3662" + inkscape:connector-curvature="0" /> + <path + id="path3664" + d="m 330.81249,271.52738 c 21.09109,0 42.18218,0 63.27327,0" + style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + inkscape:connector-curvature="0" /> + <path + style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16661239px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 405.48156,270.99405 c 21.26538,0 42.53076,0 63.79615,0" + id="path3666" + inkscape:connector-curvature="0" /> + <g + id="g10542" + transform="matrix(1,0,0,-1,0,653.98815)" + style="fill:#eeeeec;fill-opacity:1"> + <rect + ry="4.7797003" + y="351.5274" + x="480.04877" + height="64" + width="63.461262" + id="rect3630" + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" /> + <rect + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" + id="rect8437" + width="63.461262" + height="33.005924" + x="480.04877" + y="351.5274" + ry="2.4649756" /> + <path + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:#000000;stroke-width:1.1617893px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 480.14581,384.59406 c 21.08993,0 42.17982,0 63.26974,0" + id="path3668" + inkscape:connector-curvature="0" /> + </g> + <path + id="path3670" + d="m 554.81489,271.52738 c 21.26539,0 42.53076,0 63.79615,0" + style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16661239px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + inkscape:connector-curvature="0" /> + <path + style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 629.47916,270.99405 c 21.09109,0 42.18217,0 63.27327,0" + id="path3672" + inkscape:connector-curvature="0" /> + <path + style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 704.14582,271.52738 c 21.0911,0 42.18218,0 63.27327,0" + id="path3674" + inkscape:connector-curvature="0" /> + <path + id="path3678" + d="m 779.24757,270.99405 c 21.0911,0 42.18218,0 63.27327,0" + style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + inkscape:connector-curvature="0" /> + <text + id="text3680" + y="262.07977" + x="259.86594" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="262.07977" + x="259.86594" + id="tspan3682" + sodipodi:role="line" + style="font-size:19.20000076px;line-height:1.25;stroke-width:1.06666672">D</tspan></text> + <text + id="text3684" + y="295.7149" + x="260.63126" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="295.7149" + x="260.63126" + id="tspan3686" + sodipodi:role="line" + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';fill:#0000ff;stroke-width:1.06666672">d</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="335.79037" + y="293.96075" + id="text3688"><tspan + sodipodi:role="line" + id="tspan3690" + x="335.79037" + y="293.96075" + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">f</tspan></text> + <text + id="text3692" + y="293.27176" + x="409.74942" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="293.27176" + x="409.74942" + id="tspan3694" + sodipodi:role="line" + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">g</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="483.63565" + y="292.44882" + id="text3696"><tspan + sodipodi:role="line" + id="tspan3698" + x="483.63565" + y="292.44882" + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';fill:#0000ff;stroke-width:1.06666672">h</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="563.19849" + y="293.46237" + id="text3700"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';fill:#0000ff;stroke-width:1.06666672" + sodipodi:role="line" + id="tspan3702" + x="563.19849" + y="293.46237">j</tspan></text> + <text + id="text3704" + y="298.28571" + x="630.01605" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';fill:#0000ff;stroke-width:1.06666672" + y="298.28571" + x="630.01605" + id="tspan3706" + sodipodi:role="line">k</tspan></text> + <g + id="g10537" + transform="translate(0,-112.47259)" + style="fill:#eeeeec;fill-opacity:1"> + <rect + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" + id="rect8447" + width="63.461262" + height="64" + x="704" + y="350.93332" + ry="4.7797003" /> + <rect + ry="2.3898501" + y="382.93332" + x="704" + height="32" + width="63.461262" + id="rect8449" + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" /> + <path + id="path8451" + d="m 704.33984,383.97137 c 21.08993,0 42.17982,0 63.26974,0" + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:#000000;stroke-width:1.1617893px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + inkscape:connector-curvature="0" /> + </g> + <text + id="text3708" + y="297.45032" + x="708.60419" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';fill:#0000ff;stroke-width:1.06666672" + y="297.45032" + x="708.60419" + id="tspan3710" + sodipodi:role="line">l</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="781.47919" + y="292.20032" + id="text3716"><tspan + sodipodi:role="line" + id="tspan3718" + x="781.47919" + y="292.20032" + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">;</tspan></text> + <rect + ry="4.7797003" + y="238.46074" + x="854.2525" + height="64" + width="63.461262" + id="rect3720" + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" /> + <path + id="path3724" + d="m 854.35172,271.52738 c 21.26539,0 42.53077,0 63.79615,0" + style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16661239px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + inkscape:connector-curvature="0" /> + <rect + style="font-size:18px;fill:#babdb6;fill-opacity:1;stroke:none;stroke-width:1.06666672" + id="rect3722" + width="137.60541" + height="64" + x="929.06122" + y="238.46074" + ry="4.7797003" /> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="858.5" + y="296.23157" + id="text3728"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672" + sodipodi:role="line" + id="tspan3730" + x="858.5" + y="296.23157">'</tspan></text> + <text + id="text3740" + y="279.57574" + x="929.25153" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:'Bitstream Vera Sans';text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="279.57574" + x="929.25153" + id="tspan3742" + sodipodi:role="line" + style="font-size:21.33333397px;line-height:1.25;stroke-width:1.06666672">↲</tspan></text> + <rect + style="font-size:18px;fill:#babdb6;fill-opacity:1;stroke:none;stroke-width:1.06666672" + id="rect3764" + width="64" + height="64" + x="116.89825" + y="313.12741" + ry="4.7797003" /> + <rect + ry="4.7797003" + y="313.12741" + x="192.05182" + height="64" + width="63.461262" + id="rect3766" + style="font-size:18px;fill:#babdb6;fill-opacity:1;stroke:none;stroke-width:1.06666672" /> + <rect + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" + id="rect3770" + width="63.461262" + height="64" + x="341.38516" + y="313.12741" + ry="4.7797003" /> + <rect + ry="4.7797003" + y="313.12741" + x="416.04898" + height="64" + width="63.461262" + id="rect3772" + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" /> + <rect + ry="4.7797003" + y="313.12741" + x="490.71542" + height="64" + width="63.461262" + id="rect3778" + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" /> + <rect + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" + id="rect3780" + width="63.948242" + height="64" + x="789.82019" + y="313.12741" + ry="4.7797003" /> + <text + id="text3796" + y="368.70032" + x="126.76303" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="368.70032" + x="126.76303" + id="tspan3798" + sodipodi:role="line" + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">z</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="199.07387" + y="367.11551" + id="text3800"><tspan + sodipodi:role="line" + id="tspan3802" + x="199.07387" + y="367.11551" + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">x</tspan></text> + <path + style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16661239px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 117.53621,345.12738 c 21.26539,0 42.53077,0 63.79615,0" + id="path3804" + inkscape:connector-curvature="0" /> + <path + id="path3806" + d="m 192.14582,345.66071 c 21.0911,0 42.18218,0 63.27327,0" + style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + inkscape:connector-curvature="0" /> + <rect + ry="4.7797003" + y="313.12741" + x="266.23157" + height="64" + width="64.538773" + id="rect3768" + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" /> + <path + id="path3808" + d="m 267.37839,344.75185 c 20.26767,0 40.53532,0 60.80299,0" + style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.06666672;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + inkscape:connector-curvature="0" /> + <path + style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 341.47916,346.19405 c 21.09109,0 42.18217,0 63.27327,0" + id="path3810" + inkscape:connector-curvature="0" /> + <path + id="path3812" + d="m 416.14822,345.66071 c 21.26539,0 42.53077,0 63.79615,0" + style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16661239px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + inkscape:connector-curvature="0" /> + <path + style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.1617893px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 490.81248,346.19405 c 21.08993,0 42.17982,0 63.26974,0" + id="path3814" + inkscape:connector-curvature="0" /> + <g + id="g7140" + transform="translate(0,16.594076)" + style="fill:#eeeeec;fill-opacity:1"> + <rect + ry="4.7797003" + y="296.53333" + x="565.38226" + height="64" + width="63.461262" + id="rect3774" + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" /> + <rect + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" + id="rect7131" + width="63.461262" + height="32" + x="565.38226" + y="328.53333" + ry="2.3898501" /> + <path + id="path3816" + d="m 565.48156,329.6 c 21.26538,0 42.53076,0 63.79615,0" + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:#000000;stroke-width:1.16661239px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + inkscape:connector-curvature="0" /> + </g> + <g + id="g7162" + transform="translate(0,16.594076)" + style="fill:#babdb6;fill-opacity:1"> + <rect + style="font-size:18px;fill:#babdb6;fill-opacity:1;stroke:none;stroke-width:1.06666672" + id="rect3776" + width="63.461262" + height="64" + x="640.05176" + y="296.53333" + ry="4.7797003" /> + <rect + ry="2.3898501" + y="328.53333" + x="640.05176" + height="32" + width="63.461262" + id="rect7160" + style="font-size:18px;fill:#babdb6;fill-opacity:1;stroke:none;stroke-width:1.06666672" /> + <path + style="font-size:18px;fill:#babdb6;fill-opacity:1;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 640.14582,329.06667 c 21.0911,0 42.18218,0 63.27327,0" + id="path3818" + inkscape:connector-curvature="0" /> + </g> + <path + style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 789.91424,345.66071 c 21.09109,0 42.18218,0 63.27327,0" + id="path3822" + inkscape:connector-curvature="0" /> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="271.65314" + y="368.61978" + id="text3828"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672" + sodipodi:role="line" + id="tspan3830" + x="271.65314" + y="368.61978">c</tspan></text> + <text + id="text3832" + y="368.62741" + x="348.59036" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672" + y="368.62741" + x="348.59036" + id="tspan3834" + sodipodi:role="line">v</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="420.34647" + y="367.93842" + id="text3836"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672" + sodipodi:role="line" + id="tspan3838" + x="420.34647" + y="367.93842">b</tspan></text> + <text + id="text3840" + y="368.05829" + x="494.32407" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';fill:#0000ff;stroke-width:1.06666672" + y="368.05829" + x="494.32407" + id="tspan3842" + sodipodi:role="line">n</tspan></text> + <text + id="text3844" + y="368.12903" + x="573.86517" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="368.12903" + x="573.86517" + id="tspan3846" + sodipodi:role="line" + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">m</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="643.82837" + y="367.86551" + id="text3848"><tspan + sodipodi:role="line" + id="tspan3850" + x="643.82837" + y="367.86551" + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">,</tspan></text> + <g + id="g7167" + transform="translate(74.666662,16.594076)" + style="fill:#eeeeec;fill-opacity:1"> + <rect + ry="4.7797003" + y="296.53333" + x="640.05176" + height="64" + width="63.461262" + id="rect7169" + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" /> + <rect + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" + id="rect7171" + width="63.461262" + height="32" + x="640.05176" + y="328.53333" + ry="2.3898501" /> + <path + id="path7173" + d="m 640.14582,329.06667 c 21.0911,0 42.18218,0 63.27327,0" + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + inkscape:connector-curvature="0" /> + </g> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="718.6156" + y="367.46667" + id="text3852"><tspan + sodipodi:role="line" + id="tspan3854" + x="718.6156" + y="367.46667" + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">.</tspan></text> + <text + id="text3856" + y="367.10361" + x="798.54919" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672" + y="367.10361" + x="798.54919" + id="tspan3858" + sodipodi:role="line">/</tspan></text> + <rect + ry="4.7797003" + y="89.127388" + x="85.333328" + height="64" + width="64" + id="rect3876" + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" /> + <rect + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" + id="rect3878" + width="63.461262" + height="64" + x="160.48691" + y="89.127388" + ry="4.7797003" /> + <rect + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" + id="rect10511" + width="63.461262" + height="64" + x="235.15358" + y="89.127388" + ry="4.7797003" /> + <rect + ry="4.7797003" + y="89.127388" + x="309.82025" + height="64" + width="63.461262" + id="rect3882" + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" /> + <rect + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" + id="rect3884" + width="63.461262" + height="64" + x="384.48407" + y="89.127388" + ry="4.7797003" /> + <rect + ry="4.7797003" + y="89.127388" + x="533.81738" + height="64" + width="63.461262" + id="rect3886" + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" /> + <rect + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" + id="rect3888" + width="63.461262" + height="64" + x="608.48688" + y="89.127388" + ry="4.7797003" /> + <rect + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" + id="rect3890" + width="63.461262" + height="64" + x="459.15051" + y="89.127388" + ry="4.7797003" /> + <rect + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" + id="rect3892" + width="63.461262" + height="64" + x="757.33331" + y="89.127388" + ry="4.7797003" /> + <rect + ry="4.7797003" + y="89.127388" + x="832.48688" + height="64" + width="63.461262" + id="rect3894" + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" /> + <rect + ry="4.7797003" + y="89.127388" + x="683.1535" + height="64" + width="63.461262" + id="rect3896" + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" /> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="88.721916" + y="145.3714" + id="text3910"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672" + sodipodi:role="line" + id="tspan3912" + x="88.721916" + y="145.3714">1</tspan></text> + <text + id="text3914" + y="144.91666" + x="162.87187" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672" + y="144.91666" + x="162.87187" + id="tspan3916" + sodipodi:role="line">2</tspan></text> + <path + id="path3918" + d="m 85.971291,121.12738 c 21.265389,0 42.530769,0 63.796159,0" + style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16661239px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + inkscape:connector-curvature="0" /> + <path + style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 160.58091,121.66071 c 21.09109,0 42.18217,0 63.27327,0" + id="path3920" + inkscape:connector-curvature="0" /> + <path + id="path3924" + d="m 309.91424,122.19405 c 21.09109,0 42.18218,0 63.27327,0" + style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + inkscape:connector-curvature="0" /> + <path + style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16661239px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 384.58331,121.66071 c 21.26538,0 42.53076,0 63.79615,0" + id="path3926" + inkscape:connector-curvature="0" /> + <path + id="path3928" + d="m 459.24756,122.19405 c 21.08993,0 42.17982,0 63.26974,0" + style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.1617893px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + inkscape:connector-curvature="0" /> + <path + id="path3930" + d="m 533.91664,122.19405 c 21.26539,0 42.53076,0 63.79615,0" + style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16661239px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + inkscape:connector-curvature="0" /> + <path + style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 608.58091,121.66071 c 21.09109,0 42.18217,0 63.27327,0" + id="path3932" + inkscape:connector-curvature="0" /> + <path + style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 683.24757,122.19405 c 21.0911,0 42.18218,0 63.27327,0" + id="path3934" + inkscape:connector-curvature="0" /> + <path + style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16661239px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 757.91664,122.19405 c 21.26539,0 42.53076,0 63.79615,0" + id="path3936" + inkscape:connector-curvature="0" /> + <path + id="path3938" + d="m 832.58091,121.66071 c 21.09109,0 42.18217,0 63.27327,0" + style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + inkscape:connector-curvature="0" /> + <path + style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 235.24757,122.19405 c 21.0911,0 42.18218,0 63.27327,0" + id="path10513" + inkscape:connector-curvature="0" /> + <text + id="text3944" + y="144.91666" + x="239.48438" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="144.91666" + x="239.48438" + id="tspan3946" + sodipodi:role="line" + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">3</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="312.26355" + y="145.3714" + id="text3948"><tspan + sodipodi:role="line" + id="tspan3950" + x="312.26355" + y="145.3714" + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">4</tspan></text> + <text + id="text3952" + y="144.79477" + x="386.92606" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="144.79477" + x="386.92606" + id="tspan3954" + sodipodi:role="line" + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">5</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="461.15417" + y="144.91666" + id="text3956"><tspan + sodipodi:role="line" + id="tspan3958" + x="461.15417" + y="144.91666" + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">6</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="536.29688" + y="145.06665" + id="text3960"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672" + sodipodi:role="line" + id="tspan3962" + x="536.29688" + y="145.06665">7</tspan></text> + <text + id="text3964" + y="144.79477" + x="611.03851" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672" + y="144.79477" + x="611.03851" + id="tspan3966" + sodipodi:role="line">8</tspan></text> + <text + id="text3968" + y="144.82977" + x="686.55652" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672" + y="144.82977" + x="686.55652" + id="tspan3970" + sodipodi:role="line">9</tspan></text> + <text + id="text3972" + y="144.57379" + x="760.35419" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="144.57379" + x="760.35419" + id="tspan3974" + sodipodi:role="line" + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">0</tspan></text> + <rect + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" + id="rect3976" + width="63.461262" + height="64" + x="906.71844" + y="89.127388" + ry="4.7797003" /> + <path + style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 906.81249,121.66071 c 21.09109,0 42.18218,0 63.27327,0" + id="path3978" + inkscape:connector-curvature="0" /> + <rect + ry="4.7797003" + y="89.127388" + x="11.153512" + height="64" + width="63.461262" + id="rect3980" + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" /> + <path + id="path3982" + d="m 11.247578,121.66071 c 21.091093,0 42.182176,0 63.273269,0" + style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + inkscape:connector-curvature="0" /> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="840.27075" + y="143.8774" + id="text3988"><tspan + sodipodi:role="line" + id="tspan3990" + x="840.27075" + y="143.8774" + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';fill:#0000ff;stroke-width:1.06666672">-</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="913.50781" + y="144.89095" + id="text3992"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';fill:#0000ff;stroke-width:1.06666672" + sodipodi:role="line" + id="tspan3994" + x="913.50781" + y="144.89095">=</tspan></text> + <rect + ry="4.7797003" + y="89.127388" + x="981.67877" + height="64" + width="95.078056" + id="rect3996" + style="font-size:18px;fill:#babdb6;fill-opacity:1;stroke:none;stroke-width:1.06666672" /> + <text + id="text4004" + y="141.20207" + x="992.40417" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="141.20207" + x="992.40417" + id="tspan4006" + sodipodi:role="line" + style="font-size:13.86666679px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">Backspace</tspan></text> + <text + id="text4977" + y="117.46353" + x="89.259377" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="117.46353" + x="89.259377" + id="tspan4979" + sodipodi:role="line" + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">!</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="162.17561" + y="116.44883" + id="text4981"><tspan + sodipodi:role="line" + id="tspan4983" + x="162.17561" + y="116.44883" + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">@</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="238.91458" + y="117.86665" + id="text4985"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672" + sodipodi:role="line" + id="tspan4987" + x="238.91458" + y="117.86665">#</tspan></text> + <text + id="text4989" + y="117.96072" + x="311.69211" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672" + y="117.96072" + x="311.69211" + id="tspan4991" + sodipodi:role="line">$</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="387.71487" + y="117.27174" + id="text4993"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672" + sodipodi:role="line" + id="tspan4995" + x="387.71487" + y="117.27174">%</tspan></text> + <text + id="text4997" + y="116.44883" + x="461.6011" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672" + y="116.44883" + x="461.6011" + id="tspan4999" + sodipodi:role="line">^</tspan></text> + <text + id="text5001" + y="117.46236" + x="536.96692" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="117.46236" + x="536.96692" + id="tspan5003" + sodipodi:role="line" + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">&</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="609.5968" + y="117.19882" + id="text5005"><tspan + sodipodi:role="line" + id="tspan5007" + x="609.5968" + y="117.19882" + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">*</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="685.03265" + y="116.44881" + id="text5009"><tspan + sodipodi:role="line" + id="tspan5011" + x="685.03265" + y="116.44881" + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">(</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="761.91931" + y="116.12902" + id="text5013"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672" + sodipodi:role="line" + id="tspan5015" + x="761.91931" + y="116.12902">)</tspan></text> + <rect + ry="3.5688117" + y="387.40637" + x="254.19489" + height="47.786243" + width="385.80511" + id="rect5017" + style="font-size:18px;fill:#babdb6;fill-opacity:1;stroke:none;stroke-width:1.06666672" /> + <g + id="g4061" + transform="translate(0,-7.1714294)"> + <text + id="text5021" + y="422.17459" + x="261.68033" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="422.17459" + x="261.68033" + id="tspan5023" + sodipodi:role="line" + style="font-size:13.86666679px;line-height:1.25;stroke-width:1.06666672">Space</tspan></text> + </g> + <text + id="text6971" + y="339.18835" + x="125.368" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="339.18835" + x="125.368" + id="tspan6973" + sodipodi:role="line" + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">Z</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="197.67885" + y="337.60355" + id="text6975"><tspan + sodipodi:role="line" + id="tspan6977" + x="197.67885" + y="337.60355" + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">X</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="271.76355" + y="339.99478" + id="text6979"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672" + sodipodi:role="line" + id="tspan6981" + x="271.76355" + y="339.99478">C</tspan></text> + <text + id="text6983" + y="339.11542" + x="347.19534" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672" + y="339.11542" + x="347.19534" + id="tspan6985" + sodipodi:role="line">V</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="418.95145" + y="338.42645" + id="text6987"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672" + sodipodi:role="line" + id="tspan6989" + x="418.95145" + y="338.42645">B</tspan></text> + <text + id="text6991" + y="338.27698" + x="494.1412" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';fill:#0000ff;stroke-width:1.06666672" + y="338.27698" + x="494.1412" + id="tspan6993" + sodipodi:role="line">N</tspan></text> + <text + id="text6995" + y="338.6171" + x="572.47015" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="338.6171" + x="572.47015" + id="tspan6997" + sodipodi:role="line" + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">M</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="645.10004" + y="338.35355" + id="text6999"><tspan + sodipodi:role="line" + id="tspan7001" + x="645.10004" + y="338.35355" + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672"><</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="723.39313" + y="336.84164" + id="text7003"><tspan + sodipodi:role="line" + id="tspan7005" + x="723.39313" + y="336.84164" + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">></tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="798.75592" + y="337.85516" + id="text7007"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672" + sodipodi:role="line" + id="tspan7009" + x="798.75592" + y="337.85516">?</tspan></text> + <text + id="text7027" + y="264.07922" + x="409.74942" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="264.07922" + x="409.74942" + id="tspan7029" + sodipodi:role="line" + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';fill:#0000ff;stroke-width:1.06666672">G</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="483.63565" + y="263.25632" + id="text7031"><tspan + sodipodi:role="line" + id="tspan7033" + x="483.63565" + y="263.25632" + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';fill:#0000ff;stroke-width:1.06666672">H</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="559.00153" + y="264.26987" + id="text7035"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';fill:#0000ff;stroke-width:1.06666672" + sodipodi:role="line" + id="tspan7037" + x="559.00153" + y="264.26987">J</tspan></text> + <text + id="text7039" + y="263.13974" + x="631.41089" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';fill:#0000ff;stroke-width:1.06666672" + y="263.13974" + x="631.41089" + id="tspan7041" + sodipodi:role="line">K</tspan></text> + <text + id="text7043" + y="264.96075" + x="708.60419" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';fill:#0000ff;stroke-width:1.06666672" + y="264.96075" + x="708.60419" + id="tspan7045" + sodipodi:role="line">L</tspan></text> + <text + id="text7047" + y="263.34451" + x="781.32269" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="263.34451" + x="781.32269" + id="tspan7049" + sodipodi:role="line" + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">:</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="399.83853" + y="187.92427" + id="text7051"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';fill:#0000ff;stroke-width:1.06666672" + sodipodi:role="line" + id="tspan7053" + x="399.83853" + y="187.92427">T</tspan></text> + <text + id="text7055" + y="187.60356" + x="474.54233" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672" + y="187.60356" + x="474.54233" + id="tspan7057" + sodipodi:role="line">Y</tspan></text> + <text + id="text7059" + y="187.3317" + x="548.19391" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="187.3317" + x="548.19391" + id="tspan7061" + sodipodi:role="line" + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">U</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="621.77612" + y="187.60356" + id="text7063"><tspan + sodipodi:role="line" + id="tspan7065" + x="621.77612" + y="187.60356" + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">I</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="698.92633" + y="187.3317" + id="text7067"><tspan + sodipodi:role="line" + id="tspan7069" + x="698.92633" + y="187.3317" + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">O</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="771.70312" + y="187.92427" + id="text7071"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672" + sodipodi:role="line" + id="tspan7073" + x="771.70312" + y="187.92427">P</tspan></text> + <rect + ry="4.7797003" + y="163.79405" + x="842.71564" + height="64" + width="63.461262" + id="rect7075" + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" /> + <rect + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" + id="rect7077" + width="63.461262" + height="64" + x="917.38507" + y="163.79405" + ry="4.7797003" /> + <rect + ry="4.7797003" + y="163.79405" + x="992.05176" + height="64" + width="85.281578" + id="rect7079" + style="font-size:18px;fill:#babdb6;fill-opacity:1;stroke:none;stroke-width:1.06666672" /> + <path + id="path7081" + d="m 842.81489,196.86071 c 21.26539,0 42.53076,0 63.79615,0" + style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16661239px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + inkscape:connector-curvature="0" /> + <path + style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 917.47916,196.32738 c 21.09109,0 42.18217,0 63.27327,0" + id="path7083" + inkscape:connector-curvature="0" /> + <path + style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.34042335px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 992.33815,196.86071 c 28.07395,0 56.14795,0 84.22195,0" + id="path7085" + inkscape:connector-curvature="0" /> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="845.2594" + y="219.39616" + id="text7087"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672" + sodipodi:role="line" + id="tspan7089" + x="845.2594" + y="219.39616">[</tspan></text> + <text + id="text7091" + y="219.39616" + x="919.95624" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672" + y="219.39616" + x="919.95624" + id="tspan7093" + sodipodi:role="line">]</tspan></text> + <text + id="text7095" + y="219.92429" + x="993.08435" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672" + y="219.92429" + x="993.08435" + id="tspan7097" + sodipodi:role="line">\</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="844.91248" + y="186.91489" + id="text7099"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672" + sodipodi:role="line" + id="tspan7101" + x="844.91248" + y="186.91489">{</tspan></text> + <text + id="text7103" + y="186.91489" + x="918.51251" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672" + y="186.91489" + x="918.51251" + id="tspan7105" + sodipodi:role="line">}</tspan></text> + <text + id="text7107" + y="185.39926" + x="993.43127" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672" + y="185.39926" + x="993.43127" + id="tspan7109" + sodipodi:role="line">|</tspan></text> + <flowRoot + xml:space="preserve" + id="flowRoot7111" + style="font-size:12.80000019px;line-height:0.01%;font-family:'DejaVu Sans Mono';stroke-width:1.06666672"><flowRegion + id="flowRegion7113" + style="stroke-width:1.06666672"><rect + id="rect7115" + width="12.929953" + height="17.239937" + x="859.84186" + y="230.24284" + style="font-size:21.33333397px;font-family:'DejaVu Sans Mono';stroke-width:1.13777781" /></flowRegion><flowPara + id="flowPara7117" + style="font-size:21.33333397px;line-height:1.25;stroke-width:1.06666672">'"l</flowPara></flowRoot> <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="857.28125" + y="264.82532" + id="text7119"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672" + sodipodi:role="line" + id="tspan7121" + x="857.28125" + y="264.82532">"</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="20.090919" + y="145.65862" + id="text7123"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672" + sodipodi:role="line" + id="tspan7125" + x="20.090919" + y="145.65862">`</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="18.695885" + y="116.14663" + id="text7127"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672" + sodipodi:role="line" + id="tspan7129" + x="18.695885" + y="116.14663">~</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="338.80719" + y="208.43478" + id="text7245"><tspan + sodipodi:role="line" + x="338.80719" + y="208.43478" + id="tspan7366" + style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan + sodipodi:role="line" + x="338.80719" + y="216.3177" + id="tspan7249" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">reload</tspan></text> + <flowRoot + xml:space="preserve" + id="flowRoot7342" + style="font-size:12.80000019px;line-height:0.01%;font-family:'DejaVu Sans Mono';stroke-width:1.06666672"><flowRegion + id="flowRegion7344" + style="stroke-width:1.06666672"><rect + id="rect7346" + width="17.453804" + height="36.448208" + x="14.546197" + y="174.75179" + style="font-size:13.86666679px;font-family:'DejaVu Sans Mono';stroke-width:1.13777781" /></flowRegion><flowPara + id="flowPara7348" + style="font-size:13.86666679px;line-height:1.25;stroke-width:1.06666672">TabTa</flowPara></flowRoot> <text + id="text7358" + y="199.05951" + x="11.713984" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="199.05951" + x="11.713984" + id="tspan7360" + sodipodi:role="line" + style="font-size:12.80000019px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">Tab</tspan></text> + <rect + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" + id="rect10890" + width="338.15115" + height="41.734409" + x="101.31554" + y="20.999462" + ry="3.116843" /> + <text + id="text7404" + y="49.597778" + x="106.28438" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans';text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="49.597778" + x="106.28438" + id="tspan7406" + sodipodi:role="line" + style="font-size:23.46666718px;line-height:1;stroke-width:1.06666672">qutebrowser default bindings</tspan></text> + <text + id="text9514" + y="279.05405" + x="578.2074" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="279.05405" + x="578.2074" + sodipodi:role="line" + id="tspan9524" + style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan + y="286.93695" + x="578.2074" + sodipodi:role="line" + id="tspan4938" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">scroll</tspan><tspan + y="294.61697" + x="578.2074" + sodipodi:role="line" + id="tspan4936" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">down</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="650.2074" + y="279.24451" + id="text9526"><tspan + sodipodi:role="line" + x="650.2074" + y="279.24451" + id="tspan9530" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan + sodipodi:role="line" + x="650.2074" + y="286.9245" + id="tspan4942" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">scroll</tspan><tspan + id="tspan9532" + sodipodi:role="line" + x="650.2074" + y="294.60449" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">up</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="578.2074" + y="247.81595" + id="text10515"><tspan + sodipodi:role="line" + x="578.2074" + y="247.81595" + id="tspan10517" + style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan + sodipodi:role="line" + x="578.2074" + y="255.69887" + id="tspan10519" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">next</tspan><tspan + id="tspan10521" + sodipodi:role="line" + x="578.2074" + y="263.37888" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">tab</tspan></text> + <text + id="text10523" + y="248.00641" + x="650.2074" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + id="tspan10525" + y="248.00641" + x="650.2074" + sodipodi:role="line" + style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan + id="tspan10527" + y="255.88933" + x="650.2074" + sodipodi:role="line" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">previous</tspan><tspan + y="263.56934" + x="650.2074" + sodipodi:role="line" + id="tspan10529" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">tab</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="501.71222" + y="278.67307" + id="text10547"><tspan + sodipodi:role="line" + x="501.71222" + y="278.67307" + id="tspan10560" + style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan + sodipodi:role="line" + x="501.71222" + y="286.55597" + id="tspan10574" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">scroll</tspan><tspan + sodipodi:role="line" + x="501.71222" + y="294.23599" + id="tspan10562" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">left</tspan></text> + <text + id="text10564" + y="278.67307" + x="724.62445" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="278.67307" + x="724.62445" + sodipodi:role="line" + id="tspan10566" + style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan + id="tspan10568" + y="286.55597" + x="724.62445" + sodipodi:role="line" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">scroll</tspan><tspan + id="tspan10570" + y="294.23599" + x="724.62445" + sodipodi:role="line" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">right</tspan></text> + <text + id="text10564-6" + y="199.11761" + x="715.61145" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + id="tspan10570-9" + y="199.11761" + x="715.61145" + sodipodi:role="line" + style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan + y="207.75761" + x="715.61145" + sodipodi:role="line" + id="tspan4975" + style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan + y="215.64053" + x="715.61145" + sodipodi:role="line" + id="tspan4977" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">open <tspan + style="fill:#ff0000;stroke-width:1.06666672" + id="tspan3697">(6)</tspan></tspan></text> + <text + id="text10564-3" + y="166.45094" + x="714.94476" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + id="tspan10570-6" + y="166.45094" + x="714.94476" + sodipodi:role="line" + style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan + y="174.33386" + x="714.94476" + sodipodi:role="line" + id="tspan4996" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">open in</tspan><tspan + y="182.01385" + x="714.94476" + sodipodi:role="line" + id="tspan4998" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">new tab<tspan + style="fill:#ff0000;stroke-width:1.06666672" + id="tspan3699" /></tspan><tspan + y="189.69386" + x="714.94476" + sodipodi:role="line" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672" + id="tspan3701"><tspan + style="fill:#ff0000;stroke-width:1.06666672" + id="tspan3703">(6)</tspan></tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="282.57269" + y="279.80652" + id="text10547-2"><tspan + sodipodi:role="line" + x="282.57269" + y="279.80652" + id="tspan10560-6" + style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan + sodipodi:role="line" + x="282.57269" + y="287.68942" + id="tspan10562-1" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">close</tspan><tspan + sodipodi:role="line" + x="282.57269" + y="295.36945" + id="tspan5970" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">tab</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="351.18723" + y="272.2189" + id="text10547-2-1"><tspan + sodipodi:role="line" + x="351.18723" + y="272.2189" + id="tspan10560-6-1" + style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan + sodipodi:role="line" + x="351.18723" + y="280.10181" + id="tspan5062" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">hint</tspan><tspan + sodipodi:role="line" + x="351.18723" + y="287.78183" + id="tspan5066" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">(label</tspan><tspan + sodipodi:role="line" + x="351.18723" + y="295.46182" + id="tspan6140" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">links) <tspan + style="font-size:8.53333378px;fill:#ff0000;stroke-width:1.06666672" + id="tspan3723">(8)</tspan></tspan></text> + <text + id="text9514-6" + y="201.01891" + x="564.76721" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="201.01891" + x="564.76721" + sodipodi:role="line" + id="tspan9524-9" + style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan + y="208.90182" + x="564.76721" + sodipodi:role="line" + id="tspan4936-0" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">undo</tspan><tspan + y="216.58182" + x="564.76721" + sodipodi:role="line" + id="tspan5968" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">closing</tspan><tspan + y="224.26183" + x="564.76721" + sodipodi:role="line" + id="tspan5436" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">tab</tspan></text> + <text + id="text9514-4" + y="205.01891" + x="638.90051" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="205.01891" + x="638.90051" + sodipodi:role="line" + id="tspan4936-5" + style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan + y="212.90182" + x="638.90051" + sodipodi:role="line" + id="tspan5132" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">insert-</tspan><tspan + y="220.58182" + x="638.90051" + sodipodi:role="line" + id="tspan5136" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">mode</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="840.11176" + y="108.99891" + id="text3988-2"><tspan + sodipodi:role="line" + id="tspan3990-4" + x="840.11176" + y="108.99891" + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">_</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="913.50781" + y="110.18444" + id="text3992-3"><tspan + style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';fill:#0000ff;stroke-width:1.06666672" + sodipodi:role="line" + id="tspan3994-3" + x="913.50781" + y="110.18444">+</tspan></text> + <text + id="text9514-6-5" + y="129.16545" + x="856.87421" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="129.16545" + x="856.87421" + sodipodi:role="line" + id="tspan9524-9-4" + style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan + y="137.04837" + x="856.87421" + sodipodi:role="line" + id="tspan4936-0-0" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">zoom</tspan><tspan + y="144.72836" + x="856.87421" + sodipodi:role="line" + id="tspan6188" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">out</tspan></text> + <text + id="text9514-6-1" + y="96" + x="930.13336" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="96" + x="930.13336" + sodipodi:role="line" + id="tspan9524-9-1" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan + y="103.68" + x="930.13336" + sodipodi:role="line" + id="tspan4936-0-6" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">zoom</tspan><tspan + y="111.36" + x="930.13336" + sodipodi:role="line" + id="tspan5593" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">in</tspan></text> + <text + id="text9514-6-1-2" + y="133.00302" + x="929.7572" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="133.00302" + x="929.7572" + sodipodi:role="line" + id="tspan9524-9-1-6" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">set</tspan><tspan + y="140.68301" + x="929.7572" + sodipodi:role="line" + id="tspan5252" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">zoom</tspan><tspan + y="148.36302" + x="929.7572" + sodipodi:role="line" + id="tspan4936-0-6-8" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">level</tspan></text> + <rect + ry="4.7797003" + y="14.834106" + x="12.374795" + height="64" + width="63.461262" + id="rect3980-8" + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" /> + <text + id="text7358-4" + y="29.305208" + x="18.544792" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="29.305208" + x="18.544792" + id="tspan7360-3" + sodipodi:role="line" + style="font-size:12.80000019px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">Esc</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="351.18723" + y="242.80072" + id="text10547-2-1-5"><tspan + sodipodi:role="line" + x="351.18723" + y="242.80072" + id="tspan10560-6-1-1" + style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan + sodipodi:role="line" + x="351.18723" + y="250.68364" + id="tspan5062-4" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">hint</tspan><tspan + sodipodi:role="line" + x="351.18723" + y="258.36365" + id="tspan5066-3" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">(new</tspan><tspan + sodipodi:role="line" + x="351.18723" + y="266.04364" + id="tspan6144" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">tab)</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="501.71222" + y="251.41891" + id="text10547-23"><tspan + sodipodi:role="line" + x="501.71222" + y="251.41891" + id="tspan10560-1" + style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan + sodipodi:role="line" + x="501.71222" + y="259.30182" + id="tspan10562-12" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">back <tspan + style="font-size:8.53333378px;fill:#ff0000;stroke-width:1.06666672" + id="tspan3586">(7)</tspan></tspan></text> + <text + id="text10564-6-7" + y="239.34317" + x="724.62445" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + id="tspan10570-9-4" + y="239.34317" + x="724.62445" + sodipodi:role="line" + style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan + y="247.98317" + x="724.62445" + sodipodi:role="line" + id="tspan4975-2" + style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan + y="255.86609" + x="724.62445" + sodipodi:role="line" + id="tspan4977-3" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">forward</tspan><tspan + y="263.54608" + x="724.62445" + sodipodi:role="line" + id="tspan3588" + style="font-size:8.53333378px;line-height:0.89999998;fill:#ff0000;stroke-width:1.06666672">(7)</tspan></text> + <text + id="text10564-6-7-8" + y="347.8989" + x="814.39789" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + id="tspan10570-9-4-6" + y="347.8989" + x="814.39789" + sodipodi:role="line" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan + y="355.57889" + x="814.39789" + sodipodi:role="line" + id="tspan4975-2-8" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan + y="363.25888" + x="814.39789" + sodipodi:role="line" + id="tspan4977-3-1" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">search</tspan></text> + <text + id="text10564-6-7-8-3" + y="238.93333" + x="800" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + id="tspan10570-9-4-6-2" + y="238.93333" + x="800" + sodipodi:role="line" + style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan + y="247.57333" + x="800" + sodipodi:role="line" + id="tspan4975-2-8-1" + style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan + y="255.45625" + x="800" + sodipodi:role="line" + id="tspan4977-3-1-5" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">cmd</tspan><tspan + y="263.13626" + x="800" + sodipodi:role="line" + id="tspan5435" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">mode</tspan></text> + <text + id="text9514-60" + y="353.55225" + x="511.70053" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="353.55225" + x="511.70053" + sodipodi:role="line" + id="tspan9524-92" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan + y="361.23224" + x="511.70053" + sodipodi:role="line" + id="tspan4936-8" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">search</tspan><tspan + y="368.91223" + x="511.70053" + sodipodi:role="line" + id="tspan5466" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">next</tspan></text> + <text + id="text9514-60-7" + y="319.95224" + x="511.99792" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="319.95224" + x="511.99792" + sodipodi:role="line" + id="tspan9524-92-1" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan + y="327.63223" + x="511.99792" + sodipodi:role="line" + id="tspan4936-8-5" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">search</tspan><tspan + y="335.31223" + x="511.99792" + sodipodi:role="line" + id="tspan5466-8" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">prev</tspan></text> + <text + id="text9514-60-8" + y="355.28558" + x="588.79791" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="355.28558" + x="588.79791" + sodipodi:role="line" + id="tspan5524" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">save</tspan><tspan + y="362.96558" + x="588.79791" + sodipodi:role="line" + id="tspan5530" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">quick-</tspan><tspan + y="370.64557" + x="588.79791" + sodipodi:role="line" + id="tspan5532" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">mark</tspan></text> + <text + id="text9514-8" + y="204.21892" + x="490.50494" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="204.21892" + x="490.50494" + sodipodi:role="line" + id="tspan9524-5" + style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan + y="212.10184" + x="490.50494" + sodipodi:role="line" + id="tspan4938-4" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">yank/</tspan><tspan + y="219.78183" + x="490.50494" + sodipodi:role="line" + id="tspan4936-1" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">copy <tspan + style="font-size:8.53333378px;fill:#ff0000;stroke-width:1.06666672" + id="tspan5713">(1)</tspan></tspan></text> + <text + id="text9514-8-9" + y="206.18428" + x="788.66119" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="206.18428" + x="788.66119" + sodipodi:role="line" + id="tspan9524-5-1" + style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan + y="214.0672" + x="788.66119" + sodipodi:role="line" + id="tspan6096" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">paste</tspan><tspan + y="221.74719" + x="788.66119" + sodipodi:role="line" + id="tspan5366" + style="font-size:8.53333378px;line-height:0.89999998;fill:#ff0000;stroke-width:1.06666672">(2)</tspan></text> + <flowRoot + xml:space="preserve" + id="flowRoot5590" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0.01%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"><flowRegion + id="flowRegion5592" + style="font-family:sans-serif;stroke-width:1.06666672px"><rect + id="rect5594" + width="552" + height="56" + x="518.66669" + y="25.333334" + style="font-size:10.66666698px;font-family:sans-serif;stroke-width:1.13777781px" /></flowRegion><flowPara + id="flowPara5596" + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;stroke-width:1.06666672px"> </flowPara></flowRoot> <flowRoot + xml:space="preserve" + id="flowRoot5598" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0.01%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"><flowRegion + id="flowRegion5600" + style="font-family:sans-serif;stroke-width:1.06666672"><rect + id="rect5602" + width="582.66669" + height="61.333332" + x="501.33334" + y="14.933333" + style="font-size:10.66666698px;font-family:sans-serif;stroke-width:1.13777781" /></flowRegion><flowPara + id="flowPara5604" + style="font-size:13.86666679px;line-height:1.25;font-family:sans-serif;stroke-width:1.06666672">Website: https://www.qutebrowser.org/ </flowPara><flowPara + id="flowPara5595" + style="font-size:13.86666679px;line-height:1.25;font-family:sans-serif;stroke-width:1.06666672">IRC: #qutebrowser on Freenode</flowPara><flowPara + id="flowPara5597" + style="font-size:13.86666679px;line-height:1.25;font-family:sans-serif;stroke-width:1.06666672">Mailinglist: qutebrowser@lists.qutebrowser.org</flowPara></flowRoot> <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="425.7652" + y="241.39365" + id="text10547-23-6"><tspan + sodipodi:role="line" + x="425.7652" + y="241.39365" + id="tspan10560-1-3" + style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan + sodipodi:role="line" + x="425.7652" + y="250.03365" + id="tspan10574-1-0" + style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672">s<tspan + style="font-size:8.53333378px;stroke-width:1.06666672" + id="tspan5591">croll to</tspan></tspan><tspan + sodipodi:role="line" + x="425.7652" + y="257.91656" + id="tspan5633" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">bottom/</tspan><tspan + sodipodi:role="line" + x="425.7652" + y="265.59656" + id="tspan5461" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">perc.</tspan><tspan + sodipodi:role="line" + x="425.7652" + y="273.27655" + id="tspan10562-12-5" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672"> </tspan></text> + <text + id="text9514-60-7-7" + y="349.15512" + x="437.06458" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="349.15512" + x="437.06458" + sodipodi:role="line" + id="tspan9524-92-1-3" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan + y="356.83511" + x="437.06458" + sodipodi:role="line" + id="tspan4936-8-5-9" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">load</tspan><tspan + y="364.51511" + x="437.06458" + sodipodi:role="line" + id="tspan5466-8-8" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">quick-</tspan><tspan + y="372.19513" + x="437.06458" + sodipodi:role="line" + id="tspan6184" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">mark <tspan + style="font-size:8.53333378px;fill:#ff0000;stroke-width:1.06666672" + id="tspan3727">(8)</tspan></tspan></text> + <text + id="text9514-60-7-7-0" + y="315.02179" + x="436.65286" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="315.02179" + x="436.65286" + sodipodi:role="line" + id="tspan9524-92-1-3-7" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan + y="322.70178" + x="436.65286" + sodipodi:role="line" + id="tspan4936-8-5-9-8" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">load</tspan><tspan + y="330.38177" + x="436.65286" + sodipodi:role="line" + id="tspan5466-8-8-0" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">quickm.</tspan><tspan + y="338.0618" + x="436.65286" + sodipodi:role="line" + id="tspan6186" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">(tab)</tspan><tspan + y="345.74179" + x="436.65286" + sodipodi:role="line" + id="tspan5689" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672"> </tspan></text> + <flowRoot + xml:space="preserve" + id="flowRoot5691" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0.01%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"><flowRegion + id="flowRegion5693" + style="font-family:sans-serif;stroke-width:1.06666672"><rect + id="rect5695" + width="344" + height="173.33333" + x="18.666666" + y="437.55823" + style="font-family:sans-serif;fill:#000000;stroke-width:1.13777781" /></flowRegion><flowPara + id="flowPara5697" + style="font-weight:bold;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#000000;stroke-width:1.06666672"><flowSpan + style="font-weight:bold;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#ff0000;stroke-width:1.06666672" + id="flowSpan5705">(1)</flowSpan> copying/yanking:</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara5701">yy - copy/yank URL</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara5703">yY - copy URL to selection</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara5707">yt - copy title to clipboard</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara5709">yT - copy title to selection</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara5711"> </flowPara></flowRoot> <flowRoot + xml:space="preserve" + id="flowRoot5691-0" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0.01%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + transform="translate(0,-10)"><flowRegion + id="flowRegion5693-7" + style="font-family:sans-serif;stroke-width:1.06666672"><rect + id="rect5695-0" + width="344" + height="173.33333" + x="19.42783" + y="520.07886" + style="font-family:sans-serif;fill:#000000;stroke-width:1.13777781" /></flowRegion><flowPara + id="flowPara5697-9" + style="font-weight:bold;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#000000;stroke-width:1.06666672"><flowSpan + style="font-weight:bold;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#ff0000;stroke-width:1.06666672" + id="flowSpan5705-5">(2)</flowSpan> pasting:</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara5701-9">pp - open URL from clipboard</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara5703-8">pP - open URL from selection</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara5707-0">Pp - like <flowSpan + style="font-style:italic;font-family:sans-serif;-inkscape-font-specification:'Sans Italic';stroke-width:1.06666672" + id="flowSpan6101">pp</flowSpan>, in new tab</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara5709-3">PP - like <flowSpan + style="font-style:italic;font-family:sans-serif;-inkscape-font-specification:'Sans Italic';stroke-width:1.06666672" + id="flowSpan6103">pP</flowSpan>, in new tab</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara5763">wp - like <flowSpan + style="font-style:italic;font-family:sans-serif;-inkscape-font-specification:'Sans Italic';stroke-width:1.06666672" + id="flowSpan6105">pp</flowSpan>, in new window</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara5765">wP - like <flowSpan + style="font-style:italic;font-family:sans-serif;-inkscape-font-specification:'Sans Italic';stroke-width:1.06666672" + id="flowSpan6107">pP</flowSpan>, in new window</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara5711-1"> </flowPara></flowRoot> <flowRoot + xml:space="preserve" + id="flowRoot5691-0-9" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0.01%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"><flowRegion + id="flowRegion5693-7-0" + style="font-family:sans-serif;stroke-width:1.06666672"><rect + id="rect5695-0-5" + width="344" + height="173.33333" + x="201.3311" + y="437.55823" + style="font-family:sans-serif;fill:#000000;stroke-width:1.13777781" /></flowRegion><flowPara + style="font-weight:bold;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#000000;stroke-width:1.06666672" + id="flowPara5701-9-6"><flowSpan + style="font-weight:bold;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#ff0000;stroke-width:1.06666672" + id="flowSpan5705-5-8">(3)</flowSpan> navigation:</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara5829">[[ - click "previous"-link on page</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara5703-8-2">]] - click "next"-link on page </flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara5707-0-7">{{ - like <flowSpan + style="font-style:italic;font-family:sans-serif;-inkscape-font-specification:'Sans Italic';stroke-width:1.06666672" + id="flowSpan6111">[[</flowSpan>, in new tab</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara5709-3-1">}} - like <flowSpan + style="font-style:italic;font-family:sans-serif;-inkscape-font-specification:'Sans Italic';stroke-width:1.06666672" + id="flowSpan6109">]]</flowSpan>, in new tab</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara5835"><Ctrl-A> - increment no. in URL</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara5837"><Ctrl-X> - decrement no. in URL</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara5841"> </flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara5839" /><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara5711-1-8" /></flowRoot> <text + id="text9514-8-9-0" + y="215.27605" + x="867.20831" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="215.27605" + x="867.20831" + sodipodi:role="line" + id="tspan4936-1-1-9" + style="font-size:8.53333378px;line-height:0.89999998;fill:#ff0000;stroke-width:1.06666672">(3)</tspan></text> + <text + id="text9514-8-9-0-3" + y="184.34271" + x="867.20831" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="184.34271" + x="867.20831" + sodipodi:role="line" + id="tspan4936-1-1-9-7" + style="font-size:8.53333378px;line-height:0.89999998;fill:#ff0000;stroke-width:1.06666672">(3)</tspan></text> + <text + id="text9514-8-9-0-2" + y="215.27605" + x="947.20831" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="215.27605" + x="947.20831" + sodipodi:role="line" + id="tspan4936-1-1-9-5" + style="font-size:8.53333378px;line-height:0.89999998;fill:#ff0000;stroke-width:1.06666672">(3)</tspan></text> + <text + id="text9514-8-9-0-6" + y="184.34271" + x="947.20831" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="184.34271" + x="947.20831" + sodipodi:role="line" + id="tspan4936-1-1-9-2" + style="font-size:8.53333378px;line-height:0.89999998;fill:#ff0000;stroke-width:1.06666672">(3)</tspan></text> + <flowRoot + xml:space="preserve" + id="flowRoot5691-4" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0.01%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"><flowRegion + id="flowRegion5693-9" + style="font-family:sans-serif;stroke-width:1.06666672"><rect + id="rect5695-9" + width="344" + height="173.33333" + x="199.82608" + y="546.74548" + style="font-family:sans-serif;fill:#000000;stroke-width:1.13777781" /></flowRegion><flowPara + id="flowPara5697-3" + style="font-weight:bold;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#000000;stroke-width:1.06666672"><flowSpan + style="font-weight:bold;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#ff0000;stroke-width:1.06666672" + id="flowSpan5705-0">(4)</flowSpan> scrolling:</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara5701-8"><Ctrl-F> - page down</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara5707-9"><Ctrl-B> - page up</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara5962"><Ctrl-D> - half page down</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara5711-7"><Ctrl-U> - half page up</flowPara></flowRoot> <flowRoot + xml:space="preserve" + id="flowRoot5691-4-9" + style="font-style:normal;font-weight:bold;font-size:12.80000019px;line-height:0.01%;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"><flowRegion + id="flowRegion5693-9-1" + style="font-family:sans-serif;stroke-width:1.06666672"><rect + id="rect5695-9-8" + width="344" + height="173.33333" + x="403.53775" + y="437.55823" + style="font-weight:bold;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#000000;stroke-width:1.13777781" /></flowRegion><flowPara + style="font-weight:bold;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#000000;stroke-width:1.06666672" + id="flowPara4171">in prompt mode:</flowPara><flowPara + style="font-weight:normal;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:Sans;fill:#000000;stroke-width:1.06666672" + id="flowPara4175">Enter - accept prompt</flowPara><flowPara + style="font-weight:normal;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:Sans;fill:#000000;stroke-width:1.06666672" + id="flowPara4177">y - answer yes to prompt</flowPara><flowPara + style="font-weight:normal;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:Sans;fill:#000000;stroke-width:1.06666672" + id="flowPara4179">n - answer no to prompt</flowPara><flowPara + style="font-weight:normal;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:Sans;fill:#000000;stroke-width:1.06666672" + id="flowPara6016"> </flowPara></flowRoot> <flowRoot + xml:space="preserve" + id="flowRoot5691-0-9-9" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0.01%;font-family:sans-serif;-inkscape-font-specification:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"><flowRegion + id="flowRegion5693-7-0-2" + style="font-family:sans-serif;stroke-width:1.06666672"><rect + id="rect5695-0-5-6" + width="344" + height="173.33333" + x="403.54816" + y="496.42233" + style="font-style:normal;font-family:sans-serif;-inkscape-font-specification:Sans;fill:#000000;stroke-width:1.13777781" /></flowRegion><flowPara + style="font-style:normal;font-weight:bold;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#000000;stroke-width:1.06666672" + id="flowPara5701-9-6-8"><flowSpan + style="font-style:normal;font-weight:bold;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#ff0000;stroke-width:1.06666672" + id="flowSpan5705-5-8-3">(6)</flowSpan> opening:</flowPara><flowPara + style="font-style:normal;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:Sans;fill:#000000;stroke-width:1.06666672" + id="flowPara5829-1">go - edit & open current URL</flowPara><flowPara + style="font-style:normal;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:Sans;fill:#000000;stroke-width:1.06666672" + id="flowPara5703-8-2-8">gO - like <flowSpan + style="font-style:italic;font-family:sans-serif;-inkscape-font-specification:'Sans Italic';stroke-width:1.06666672" + id="flowSpan6132">go</flowSpan>, in new tab</flowPara><flowPara + style="font-style:normal;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:Sans;fill:#000000;stroke-width:1.06666672" + id="flowPara3581">xO - like <flowSpan + style="font-style:italic;font-family:sans-serif;-inkscape-font-specification:'Sans Italic';stroke-width:1.06666672" + id="flowSpan6134">go</flowSpan>, in bg. tab</flowPara><flowPara + style="font-style:normal;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:Sans;fill:#000000;stroke-width:1.06666672" + id="flowPara5709-3-1-6">xo - open in background tab</flowPara><flowPara + style="font-style:normal;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:Sans;fill:#000000;stroke-width:1.06666672" + id="flowPara5841-1">wo - open in new window</flowPara><flowPara + style="font-style:normal;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:Sans;fill:#000000;stroke-width:1.06666672" + id="flowPara5839-8"> </flowPara><flowPara + style="font-style:normal;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:Sans;fill:#000000;stroke-width:1.06666672" + id="flowPara5711-1-8-7" /></flowRoot> <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="425.7652" + y="272.26849" + id="text10547-23-6-1"><tspan + sodipodi:role="line" + x="425.7652" + y="272.26849" + id="tspan10560-1-3-8" + style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan + sodipodi:role="line" + x="425.7652" + y="280.1514" + id="tspan10574-1-0-7" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">gg: <tspan + style="font-size:8.53333378px;fill:#ff0000;stroke-width:1.06666672" + id="tspan3812">(10)</tspan></tspan><tspan + sodipodi:role="line" + x="425.7652" + y="287.83142" + id="tspan6176" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">scroll</tspan><tspan + sodipodi:role="line" + x="425.7652" + y="295.51141" + id="tspan5633-2" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">to top</tspan><tspan + sodipodi:role="line" + x="425.7652" + y="303.19141" + id="tspan10562-12-5-9" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672"> </tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="26.836851" + y="44.101078" + id="text10547-2-5"><tspan + sodipodi:role="line" + x="26.836851" + y="44.101078" + id="tspan10560-6-4" + style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan + sodipodi:role="line" + x="26.836851" + y="51.983994" + id="tspan5970-5" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">normal</tspan><tspan + sodipodi:role="line" + x="26.836851" + y="59.663994" + id="tspan6219" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">mode</tspan></text> + <flowRoot + xml:space="preserve" + id="flowRoot5691-4-9-3" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0.01%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"><flowRegion + id="flowRegion5693-9-1-7" + style="font-family:sans-serif;stroke-width:1.06666672"><rect + id="rect5695-9-8-7" + width="344" + height="173.33333" + x="404.05209" + y="582.96967" + style="font-family:sans-serif;fill:#000000;stroke-width:1.13777781" /></flowRegion><flowPara + id="flowPara5697-3-7-6" + style="font-weight:bold;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#000000;stroke-width:1.06666672"><flowSpan + style="font-weight:bold;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#ff0000;stroke-width:1.06666672" + id="flowSpan5705-0-4-7">(7)</flowSpan> back/forward:</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara5701-8-5-8"><flowSpan + style="font-family:sans-serif;fill:#0000ff;stroke-width:1.06666672" + id="flowSpan5463">th</flowSpan> - back (in new tab)</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara6016-5"><flowSpan + style="font-family:sans-serif;fill:#0000ff;stroke-width:1.06666672" + id="flowSpan5465">wh</flowSpan> - back (in new window)</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara3628"><flowSpan + style="font-family:sans-serif;fill:#0000ff;stroke-width:1.06666672" + id="flowSpan5467">tl</flowSpan> - forward (in new tab)</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara3630"><flowSpan + style="font-family:sans-serif;fill:#0000ff;stroke-width:1.06666672" + id="flowSpan5469">wl</flowSpan> - forward (in new window)</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara3626"> </flowPara></flowRoot> <text + id="text10564-6-7-8-3-5" + y="270.24557" + x="800" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + id="tspan10570-9-4-6-2-6" + y="270.24557" + x="800" + sodipodi:role="line" + style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan + y="278.88559" + x="800" + sodipodi:role="line" + id="tspan4975-2-8-1-6" + style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan + y="286.76849" + x="800" + sodipodi:role="line" + id="tspan4977-3-1-5-7" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">ext.</tspan><tspan + y="294.44849" + x="800" + sodipodi:role="line" + id="tspan5435-7" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">hints <tspan + style="font-size:8.53333378px;fill:#ff0000;stroke-width:1.06666672" + id="tspan3662">(9)</tspan></tspan></text> + <flowRoot + xml:space="preserve" + id="flowRoot5691-4-9-3-6" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0.01%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"><flowRegion + id="flowRegion5693-9-1-7-3" + style="font-family:sans-serif;stroke-width:1.06666672"><rect + id="rect5695-9-8-7-7" + width="344" + height="173.33333" + x="579.90106" + y="437.54779" + style="font-family:sans-serif;fill:#000000;stroke-width:1.13777781" /></flowRegion><flowPara + id="flowPara5697-3-7-6-8" + style="font-weight:bold;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#ff0000;stroke-width:1.06666672">(8)</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara3626-7">prefix with w - in new window</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara3725"> </flowPara></flowRoot> <flowRoot + xml:space="preserve" + id="flowRoot5691-4-9-3-1" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0.01%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"><flowRegion + id="flowRegion5693-9-1-7-1" + style="font-family:sans-serif;stroke-width:1.06666672"><rect + id="rect5695-9-8-7-5" + width="344" + height="173.33333" + x="579.36774" + y="489.80301" + style="font-family:sans-serif;fill:#000000;stroke-width:1.13777781" /></flowRegion><flowPara + id="flowPara5697-3-7-6-1" + style="font-weight:bold;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#000000;stroke-width:1.06666672"><flowSpan + style="font-weight:bold;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#ff0000;stroke-width:1.06666672" + id="flowSpan5705-0-4-7-6">(9)</flowSpan> extended hint mode:</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara3626-73">;b - open hint in background tab</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara4051">;f - open hint in foreground tab</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara3788">;h - hover over hint (mouse-over)</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara3790">;i - hint images</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara3792">;I - hint images in new tab</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara6096">;t - hint inputs</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara3794">;o - put hinted URL in cmd. line</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara3796">;O - like <flowSpan + style="font-style:italic;font-family:sans-serif;-inkscape-font-specification:'Sans Italic';stroke-width:1.06666672" + id="flowSpan3798">;o</flowSpan>, in new tab</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara3800">;y - yank hinted URL to clipboard</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara3802">;Y - yank hinted URL to selection</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara3804">;r - rapid hinting</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara3806">;R - like <flowSpan + style="font-style:italic;font-family:sans-serif;-inkscape-font-specification:'Sans Italic';stroke-width:1.06666672" + id="flowSpan3810">;r</flowSpan>, in new window</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara3808">;d - download hinted URL</flowPara></flowRoot> <flowRoot + xml:space="preserve" + id="flowRoot5691-4-9-3-6-1" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0.01%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"><flowRegion + id="flowRegion5693-9-1-7-3-5" + style="font-family:sans-serif;stroke-width:1.06666672"><rect + id="rect5695-9-8-7-7-0" + width="165.23355" + height="256.78436" + x="772.63074" + y="437.55823" + style="font-family:sans-serif;fill:#000000;stroke-width:1.13777781" /></flowRegion><flowPara + id="flowPara5697-3-7-6-8-2" + style="font-weight:bold;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#000000;stroke-width:1.06666672"><flowSpan + style="font-weight:bold;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#ff0000;stroke-width:1.06666672" + id="flowSpan3852">(10)</flowSpan> misc. commands:</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara3725-0">gt - switch tabs by name</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara4052"><flowSpan + style="font-family:sans-serif;fill:#0000ff;stroke-width:1.06666672" + id="flowSpan4054">gm/gl/gr</flowSpan> - move tab</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara4056"> (to index/left/right)</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara3858">gC - clone tab </flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara3860">gf - view page source</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara3915">gu - navigate up in URL</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara3917">gU - like <flowSpan + style="font-style:italic;font-family:sans-serif;-inkscape-font-specification:'Sans Italic';stroke-width:1.06666672" + id="flowSpan3923">gu</flowSpan>, in new tab</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara3921">sf - save config</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara3925">ss - set setting</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara3927">sl - set temp. setting</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara3929">sk - bind key</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara3931">Ss - show settings</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara3937">wi - open web inspector</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara4167">gd - download page</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara4169"><flowSpan + style="font-family:sans-serif;fill:#0000ff;stroke-width:1.06666672" + id="flowSpan5438">ad</flowSpan> - cancel download</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara4077">co - close other tabs</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara4081">cd - clear downloads</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara3933"> </flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara3935" /><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara4079" /></flowRoot> <text + id="text9514-8-9-0-8" + y="217.88069" + x="201.04399" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="217.88069" + x="201.04399" + sodipodi:role="line" + id="tspan4936-1-1-9-59" + style="font-size:8.53333378px;line-height:0.89999998;fill:#ff0000;stroke-width:1.06666672">(10)</tspan></text> + <text + id="text9514-8-9-0-8-4" + y="257.17371" + x="208.27393" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="257.17371" + x="208.27393" + sodipodi:role="line" + id="tspan4936-1-1-9-59-8" + style="font-size:8.53333378px;line-height:0.89999998;fill:#ff0000;stroke-width:1.06666672">(10)</tspan></text> + <text + id="text9514-8-9-0-8-7" + y="291.00937" + x="208.27393" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="291.00937" + x="208.27393" + sodipodi:role="line" + id="tspan4936-1-1-9-59-5" + style="font-size:8.53333378px;line-height:0.89999998;fill:#ff0000;stroke-width:1.06666672">(10)</tspan></text> + <flowRoot + xml:space="preserve" + id="flowRoot5691-4-9-3-6-1-2" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0.01%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"><flowRegion + id="flowRegion5693-9-1-7-3-5-2" + style="font-family:sans-serif;stroke-width:1.06666672"><rect + id="rect5695-9-8-7-7-0-9" + width="350.20157" + height="196.89922" + x="915.77972" + y="437.55823" + style="font-family:sans-serif;fill:#000000;stroke-width:1.13777781" /></flowRegion><flowPara + id="flowPara5697-3-7-6-8-2-0" + style="font-weight:bold;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#000000;stroke-width:1.06666672"><flowSpan + style="font-weight:bold;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#ff0000;stroke-width:1.06666672" + id="flowSpan3852-6">(11)</flowSpan> modifier commands:</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara3933-6"><Alt-num> - select tab</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara4128"><Ctrl-Tab> - select prev. tab</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara4130"><Ctrl-V> - passthrough mode</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara4132"><Ctrl-Q> - quit</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara4136"><Ctrl-H> - home</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara4138"><Ctrl-S> - stop loading</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara4140"><Ctrl-Alt-P> - print</flowPara><flowPara + style="font-weight:bold;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#000000;stroke-width:1.06666672" + id="flowPara4142">in insert mode:</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara4144"><Ctrl-E> - open editor</flowPara><flowPara + style="font-weight:bold;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#000000;stroke-width:1.06666672" + id="flowPara4146">in command mode:</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara4148"><Ctrl-P> - prev. history item</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara3935-9"><Ctrl-N> - next history item</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara6189"><Ctrl-D> - delete current item</flowPara></flowRoot> <rect + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" + id="rect3764-9" + width="64" + height="49.059277" + x="179.54729" + y="386.13333" + ry="3.6638854" /> + <rect + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" + id="rect3764-9-3" + width="64" + height="49.059277" + x="51.100777" + y="386.13333" + ry="3.6638854" /> + <rect + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" + id="rect3764-9-1" + width="64" + height="49.059277" + x="654.73492" + y="386.13333" + ry="3.6638854" /> + <rect + style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" + id="rect3764-9-7" + width="64" + height="49.059277" + x="779.16278" + y="386.13333" + ry="3.6638854" /> + <g + id="g4049" + transform="translate(1.4643921,-2.0969564)"> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="66.420761" + y="411.5079" + id="text7358-8"><tspan + style="font-size:12.80000019px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672" + sodipodi:role="line" + id="tspan7360-1" + x="66.420761" + y="411.5079">Ctrl</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="71.803055" + y="426.94736" + id="text9514-8-9-0-8-4-0"><tspan + style="font-size:8.53333378px;line-height:0.89999998;fill:#ff0000;stroke-width:1.06666672" + id="tspan4936-1-1-9-59-8-3" + sodipodi:role="line" + x="71.803055" + y="426.94736">(11)</tspan></text> + </g> + <g + id="g4055" + transform="translate(1.7364258,-12.763623)"> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="198.77023" + y="422.17459" + id="text7358-8-3-8-1"><tspan + style="font-size:12.80000019px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672" + sodipodi:role="line" + id="tspan7360-1-7-0-2" + x="198.77023" + y="422.17459">Alt</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="199.97752" + y="437.61404" + id="text9514-8-9-0-8-4-0-8"><tspan + style="font-size:8.53333378px;line-height:0.89999998;fill:#ff0000;stroke-width:1.06666672" + id="tspan4936-1-1-9-59-8-3-8" + sodipodi:role="line" + x="199.97752" + y="437.61404">(11)</tspan></text> + </g> + <g + id="g4065" + transform="translate(6.0870443,-12.763623)"> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="669.60724" + y="422.17459" + id="text7358-8-3-8"><tspan + style="font-size:12.80000019px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672" + sodipodi:role="line" + id="tspan7360-1-7-0" + x="669.60724" + y="422.17459">Alt</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="670.81451" + y="437.61404" + id="text9514-8-9-0-8-4-0-7"><tspan + style="font-size:8.53333378px;line-height:0.89999998;fill:#ff0000;stroke-width:1.06666672" + id="tspan4936-1-1-9-59-8-3-82" + sodipodi:role="line" + x="670.81451" + y="437.61404">(11)</tspan></text> + </g> + <g + id="g4071" + transform="translate(1.0914714,-12.763623)"> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="794.85565" + y="422.17459" + id="text7358-8-3"><tspan + style="font-size:12.80000019px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672" + sodipodi:role="line" + id="tspan7360-1-7" + x="794.85565" + y="422.17459">Ctrl</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="800.23798" + y="437.61404" + id="text9514-8-9-0-8-4-0-3"><tspan + style="font-size:8.53333378px;line-height:0.89999998;fill:#ff0000;stroke-width:1.06666672" + id="tspan4936-1-1-9-59-8-3-4" + sodipodi:role="line" + x="800.23798" + y="437.61404">(11)</tspan></text> + </g> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="416.72626" + y="171.09129" + id="text7245-1"><tspan + sodipodi:role="line" + x="416.72626" + y="171.09129" + id="tspan7366-3" + style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan + sodipodi:role="line" + x="416.72626" + y="178.97421" + id="tspan7249-4" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">select</tspan><tspan + sodipodi:role="line" + x="416.72626" + y="186.65421" + id="tspan5293" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">tab</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="292.49472" + y="366.05417" + id="text10547-23-6-7"><tspan + sodipodi:role="line" + x="292.49472" + y="366.05417" + id="tspan4052" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">(10)</tspan></text> + <text + id="text10564-6-7-8-0" + y="314.29889" + x="814.39789" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + id="tspan10570-9-4-6-1" + y="314.29889" + x="814.39789" + sodipodi:role="line" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan + y="321.97888" + x="814.39789" + sodipodi:role="line" + id="tspan4975-2-8-7" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan + y="329.65887" + x="814.39789" + sodipodi:role="line" + id="tspan4977-3-1-2" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">search</tspan><tspan + y="337.3389" + x="814.39789" + sodipodi:role="line" + id="tspan5364" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">backw.</tspan></text> + <text + id="text9514-8-9-9" + y="171.64418" + x="788.19946" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="171.64418" + x="788.19946" + sodipodi:role="line" + id="tspan9524-5-1-5" + style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan + y="179.5271" + x="788.19946" + sodipodi:role="line" + id="tspan6096-4" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">paste</tspan><tspan + y="187.20709" + x="788.19946" + sodipodi:role="line" + id="tspan5366-3" + style="font-size:8.53333378px;line-height:0.89999998;fill:#ff0000;stroke-width:1.06666672">(2)</tspan></text> + <text + id="text9514-8-0" + y="171.09129" + x="490.50494" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="171.09129" + x="490.50494" + sodipodi:role="line" + id="tspan9524-5-8" + style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan + y="178.97421" + x="490.50494" + sodipodi:role="line" + id="tspan4938-4-2" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">yank/</tspan><tspan + y="186.65421" + x="490.50494" + sodipodi:role="line" + id="tspan4936-1-1" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">copy <tspan + style="font-size:8.53333378px;fill:#ff0000;stroke-width:1.06666672" + id="tspan5713-7">(1)</tspan></tspan></text> + <text + id="text9514-8-9-0-8-7-7" + y="291.00937" + x="133.7375" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="291.00937" + x="133.7375" + sodipodi:role="line" + id="tspan4936-1-1-9-59-5-6" + style="font-size:8.53333378px;line-height:0.89999998;fill:#ff0000;stroke-width:1.06666672">(10)</tspan></text> + <flowRoot + xml:space="preserve" + id="flowRoot5691-4-9-3-6-6" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0.01%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + transform="translate(0,10)"><flowRegion + id="flowRegion5693-9-1-7-3-8" + style="font-family:sans-serif;stroke-width:1.06666672"><rect + id="rect5695-9-8-7-7-6" + width="344" + height="173.33333" + x="913.12598" + y="622.15717" + style="font-family:sans-serif;fill:#000000;stroke-width:1.13777781" /></flowRegion><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara3626-7-0"><flowSpan + id="flowSpan5520" + style="font-weight:bold;font-size:10.66666698px;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#0000ff;stroke-width:1.06666672">blue keys </flowSpan><flowSpan + style="font-family:sans-serif;fill:#0000ff;stroke-width:1.06666672" + id="flowSpan5528">can be</flowSpan></flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#0000ff;stroke-width:1.06666672" + id="flowPara3725-9">prefixed by a count</flowPara></flowRoot> <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="339.1572" + y="166.24342" + id="text7245-1-7"><tspan + sodipodi:role="line" + x="339.1572" + y="166.24342" + id="tspan7366-3-3" + style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan + sodipodi:role="line" + x="339.1572" + y="174.12634" + id="tspan5293-5" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">reload </tspan><tspan + sodipodi:role="line" + x="339.1572" + y="181.80634" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672" + id="tspan3716">(bypass </tspan><tspan + sodipodi:role="line" + x="339.1572" + y="189.48634" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672" + id="tspan3719">cache)</tspan></text> + <text + id="text9514-60-7-7-0-8" + y="360.58533" + x="365.25357" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="360.58533" + x="365.25357" + sodipodi:role="line" + id="tspan5689-6" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">visual</tspan><tspan + y="368.26532" + x="365.25357" + sodipodi:role="line" + id="tspan4112" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">mode</tspan></text> + <text + id="text10564-5" + y="274.2934" + x="873.4303" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="274.2934" + x="873.4303" + sodipodi:role="line" + id="tspan10566-6" + style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan + id="tspan10570-91" + y="282.1763" + x="873.4303" + sodipodi:role="line" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">jump to</tspan><tspan + y="289.85632" + x="873.4303" + sodipodi:role="line" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672" + id="tspan6066">scroll</tspan><tspan + y="297.53632" + x="873.4303" + sodipodi:role="line" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672" + id="tspan6068">mark</tspan></text> + <text + id="text10564-2" + y="362.50635" + x="731.82947" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + id="tspan10568-0" + y="362.50635" + x="731.82947" + sodipodi:role="line" + style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672">repeat</tspan><tspan + id="tspan10570-93" + y="370.38925" + x="731.82947" + sodipodi:role="line" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">cmd</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="183.06667" + y="97.639633" + id="text7245-1-6"><tspan + sodipodi:role="line" + x="183.06667" + y="97.639633" + id="tspan7366-3-0" + style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan + sodipodi:role="line" + x="183.06667" + y="105.52255" + id="tspan7249-4-6" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">run</tspan><tspan + sodipodi:role="line" + x="183.06667" + y="113.20255" + id="tspan5293-2" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">macro</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="117.44301" + y="203.05061" + id="text7245-1-61"><tspan + sodipodi:role="line" + x="117.44301" + y="203.05061" + id="tspan7366-3-8" + style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan + sodipodi:role="line" + x="117.44301" + y="210.93353" + id="tspan5293-9" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">record</tspan><tspan + sodipodi:role="line" + x="117.44301" + y="218.61353" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672" + id="tspan6136">macro</tspan></text> + <text + id="text10564-5-2" + y="125.17836" + x="37.344757" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="125.17836" + x="37.344757" + sodipodi:role="line" + id="tspan10566-6-0" + style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan + id="tspan10570-91-2" + y="133.06128" + x="37.344757" + sodipodi:role="line" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">set</tspan><tspan + y="140.74127" + x="37.344757" + sodipodi:role="line" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672" + id="tspan6066-3">scroll</tspan><tspan + y="148.42128" + x="37.344757" + sodipodi:role="line" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672" + id="tspan6068-7">mark</tspan></text> + <text + id="text9514-60-8-5" + y="323.89648" + x="590.26257" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="323.89648" + x="590.26257" + sodipodi:role="line" + id="tspan5524-9" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">save</tspan><tspan + y="331.57648" + x="590.26257" + sodipodi:role="line" + id="tspan5530-2" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">book-</tspan><tspan + y="339.25647" + x="590.26257" + sodipodi:role="line" + id="tspan5532-2" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">mark</tspan></text> + <text + id="text10564-5-2-8" + y="200.40416" + x="21.280243" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + xml:space="preserve"><tspan + y="200.40416" + x="21.280243" + sodipodi:role="line" + id="tspan10566-6-0-9" + style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan + id="tspan10570-91-2-7" + y="208.28708" + x="21.280243" + sodipodi:role="line" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">cycle</tspan><tspan + y="215.96707" + x="21.280243" + sodipodi:role="line" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672" + id="tspan6068-7-6">completion</tspan><tspan + y="223.64708" + x="21.280243" + sodipodi:role="line" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672" + id="tspan6220">items</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" + x="417.29486" + y="205.18887" + id="text7245-1-3"><tspan + sodipodi:role="line" + x="417.29486" + y="205.18887" + id="tspan7366-3-6" + style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan + sodipodi:role="line" + x="417.29486" + y="213.07179" + id="tspan5293-53" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">toggle</tspan><tspan + sodipodi:role="line" + x="417.29486" + y="220.75179" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672;fill:#ff0000" + id="tspan6091">(12)</tspan><tspan + sodipodi:role="line" + x="417.29486" + y="225.70012" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672" + id="tspan6087" /><tspan + sodipodi:role="line" + x="417.29486" + y="225.70012" + style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672" + id="tspan6089" /></text> + <flowRoot + transform="translate(-1.2953814,90.2721)" + xml:space="preserve" + id="flowRoot5691-0-5" + style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0.01%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"><flowRegion + id="flowRegion5693-7-6" + style="font-family:sans-serif;stroke-width:1.06666672"><rect + id="rect5695-0-2" + width="344" + height="173.33333" + x="19.42783" + y="520.07886" + style="font-family:sans-serif;fill:#000000;stroke-width:1.13777781" /></flowRegion><flowPara + style="font-weight:bold;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#000000;stroke-width:1.06666672" + id="flowPara5701-9-2"><flowSpan + style="font-weight:bold;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#ff0000;stroke-width:1.06666672" + id="flowSpan5705-5-1">(12)</flowSpan> toggling settings:</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara6196">tsh - toggle scripts for the current host (temporarily)</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara6200">tSh - like <flowSpan + style="font-style:italic" + id="flowSpan6202">tsh</flowSpan>, but permanently</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara6206">tsH/tsu - like <flowSpan + style="font-style:italic" + id="flowSpan6210">tsh</flowSpan>, but including subdomains / with exact URL</flowPara><flowPara + style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672" + id="flowPara6208">tph - toggle plugins</flowPara></flowRoot> </g> +</svg> diff --git a/.config/qutebrowser/misc/qutebrowser.appdata.xml b/.config/qutebrowser/misc/qutebrowser.appdata.xml new file mode 100644 index 0000000..bdab55f --- /dev/null +++ b/.config/qutebrowser/misc/qutebrowser.appdata.xml @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright 2017 suve <veg@svgames.pl> --> +<component type="desktop"> + <id>org.qutebrowser.qutebrowser</id> + <metadata_license>CC-BY-SA-3.0</metadata_license> + <project_license>GPL-3.0</project_license> + <name>qutebrowser</name> + <summary>A keyboard-driven web browser</summary> + <description> + <p> + qutebrowser is a keyboard-focused browser with a minimal GUI. + It was inspired by other browsers/addons like dwb and Vimperator/Pentadactyl, + and is based on Python and PyQt5. + </p> + </description> + <categories> + <category>Network</category> + <category>WebBrowser</category> + </categories> + <provides> + <binary>qutebrowser</binary> + </provides> + <launchable type="desktop-id">qutebrowser.desktop</launchable> + <screenshots> + <screenshot type="default"> + <image>https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/main.png</image> + </screenshot> + <screenshot> + <image>https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/downloads.png</image> + </screenshot> + <screenshot> + <image>https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/completion.png</image> + </screenshot> + <screenshot> + <image>https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/hints.png</image> + </screenshot> + </screenshots> + <url type="homepage">https://www.qutebrowser.org</url> + <url type="faq">https://qutebrowser.org/doc/faq.html</url> + <url type="help">https://qutebrowser.org/doc/help/</url> + <url type="bugtracker">https://github.com/qutebrowser/qutebrowser/issues/</url> + <url type="donation">https://github.com/qutebrowser/qutebrowser#donating</url> + <releases> + <release version="1.3.0" date="2018-05-04"/> + <release version="1.2.1" date="2018-03-14"/> + <release version="1.2.0" date="2018-03-09"/> + </releases> +</component> diff --git a/.config/qutebrowser/misc/qutebrowser.desktop b/.config/qutebrowser/misc/qutebrowser.desktop new file mode 100644 index 0000000..5243b0c --- /dev/null +++ b/.config/qutebrowser/misc/qutebrowser.desktop @@ -0,0 +1,12 @@ +[Desktop Entry] +Name=qutebrowser +GenericName=Web Browser +Comment=A keyboard-driven, vim-like browser based on PyQt5 +Icon=qutebrowser +Type=Application +Categories=Network;WebBrowser; +Exec=qutebrowser %u +Terminal=false +StartupNotify=false +MimeType=text/html;text/xml;application/xhtml+xml;application/xml;application/rdf+xml;image/gif;image/jpeg;image/png;x-scheme-handler/http;x-scheme-handler/https;x-scheme-handler/qute; +Keywords=Browser diff --git a/.config/qutebrowser/misc/qutebrowser.nsi b/.config/qutebrowser/misc/qutebrowser.nsi new file mode 100644 index 0000000..76f459a --- /dev/null +++ b/.config/qutebrowser/misc/qutebrowser.nsi @@ -0,0 +1,80 @@ +Name "qutebrowser"
+
+Unicode true
+RequestExecutionLevel admin
+SetCompressor /solid lzma
+
+!ifdef X64
+ OutFile "..\dist\qutebrowser-${VERSION}-amd64.exe"
+ InstallDir "$ProgramFiles64\qutebrowser"
+!else
+ OutFile "..\dist\qutebrowser-${VERSION}-win32.exe"
+ InstallDir "$ProgramFiles\qutebrowser"
+!endif
+
+;Default installation folder
+
+!include "MUI2.nsh"
+;!include "MultiUser.nsh"
+
+!define MUI_ABORTWARNING
+;!define MULTIUSER_MUI
+;!define MULTIUSER_INSTALLMODE_COMMANDLINE
+!define MUI_ICON "../icons/qutebrowser.ico"
+!define MUI_UNICON "../icons/qutebrowser.ico"
+
+!insertmacro MUI_PAGE_LICENSE "..\LICENSE"
+!insertmacro MUI_PAGE_DIRECTORY
+!insertmacro MUI_PAGE_INSTFILES
+!insertmacro MUI_UNPAGE_CONFIRM
+!insertmacro MUI_UNPAGE_INSTFILES
+
+!insertmacro MUI_LANGUAGE "English"
+
+; depends on admin status
+;SetShellVarContext current
+
+
+Section "Install"
+
+ ; Uninstall old versions
+ ExecWait 'MsiExec.exe /quiet /qn /norestart /X{633F41F9-FE9B-42D1-9CC4-718CBD01EE11}'
+ ExecWait 'MsiExec.exe /quiet /qn /norestart /X{9331D947-AC86-4542-A755-A833429C6E69}'
+ IfFileExists "$INSTDIR\uninst.exe" 0 +2
+ ExecWait "$INSTDIR\uninst.exe /S _?=$INSTDIR"
+ CreateDirectory "$INSTDIR"
+
+ SetOutPath "$INSTDIR"
+
+ !ifdef X64
+ file /r "..\dist\qutebrowser-${VERSION}-x64\*.*"
+ !else
+ file /r "..\dist\qutebrowser-${VERSION}-x86\*.*"
+ !endif
+
+ SetShellVarContext all
+ CreateShortCut "$SMPROGRAMS\qutebrowser.lnk" "$INSTDIR\qutebrowser.exe"
+
+ ;Create uninstaller
+ WriteUninstaller "$INSTDIR\uninst.exe"
+
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\qutebrowser" "DisplayName" "qutebrowser"
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\qutebrowser" "UninstallString" '"$INSTDIR\uninst.exe"'
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\qutebrowser" "QuietUninstallString" '"$INSTDIR\uninst.exe" /S'
+
+SectionEnd
+
+;--------------------------------
+;Uninstaller Section
+
+Section "Uninstall"
+
+ SetShellVarContext all
+ Delete "$SMPROGRAMS\qutebrowser.lnk"
+
+ RMDir /r "$INSTDIR\*.*"
+ RMDir "$INSTDIR"
+
+ DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\qutebrowser"
+
+SectionEnd
diff --git a/.config/qutebrowser/misc/qutebrowser.rcc b/.config/qutebrowser/misc/qutebrowser.rcc new file mode 100644 index 0000000..2f73b37 --- /dev/null +++ b/.config/qutebrowser/misc/qutebrowser.rcc @@ -0,0 +1,13 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource> + <file>icons/qutebrowser-16x16.png</file> + <file>icons/qutebrowser-24x24.png</file> + <file>icons/qutebrowser-32x32.png</file> + <file>icons/qutebrowser-48x48.png</file> + <file>icons/qutebrowser-64x64.png</file> + <file>icons/qutebrowser-96x96.png</file> + <file>icons/qutebrowser-128x128.png</file> + <file>icons/qutebrowser-256x256.png</file> + <file>icons/qutebrowser-512x512.png</file> +</qresource> +</RCC> diff --git a/.config/qutebrowser/misc/qutebrowser.spec b/.config/qutebrowser/misc/qutebrowser.spec new file mode 100644 index 0000000..c886fb0 --- /dev/null +++ b/.config/qutebrowser/misc/qutebrowser.spec @@ -0,0 +1,76 @@ +# -*- mode: python -*- + +import sys +import os + +sys.path.insert(0, os.getcwd()) +from scripts import setupcommon + +block_cipher = None + + +def get_data_files(): + data_files = [ + ('../qutebrowser/html', 'html'), + ('../qutebrowser/img', 'img'), + ('../qutebrowser/javascript', 'javascript'), + ('../qutebrowser/html/doc', 'html/doc'), + ('../qutebrowser/git-commit-id', '.'), + ('../qutebrowser/config/configdata.yml', 'config'), + ] + + # if os.path.exists(os.path.join('qutebrowser', '3rdparty', 'pdfjs')): + # data_files.append(('../qutebrowser/3rdparty/pdfjs', '3rdparty/pdfjs')) + # else: + # print("Warning: excluding pdfjs as it's not present!") + + return data_files + + +setupcommon.write_git_file() + + +if os.name == 'nt': + icon = 'icons/qutebrowser.ico' +elif sys.platform == 'darwin': + icon = 'icons/qutebrowser.icns' +else: + icon = None + + +a = Analysis(['../qutebrowser/__main__.py'], + pathex=['misc'], + binaries=None, + datas=get_data_files(), + hiddenimports=['PyQt5.QtOpenGL', 'PyQt5._QOpenGLFunctions_2_0'], + hookspath=[], + runtime_hooks=[], + excludes=['tkinter'], + win_no_prefer_redirects=False, + win_private_assemblies=False, + cipher=block_cipher) +pyz = PYZ(a.pure, a.zipped_data, + cipher=block_cipher) +exe = EXE(pyz, + a.scripts, + exclude_binaries=True, + name='qutebrowser', + icon=icon, + debug=False, + strip=False, + upx=False, + console=False, + version='misc/file_version_info.txt') +coll = COLLECT(exe, + a.binaries, + a.zipfiles, + a.datas, + strip=False, + upx=False, + name='qutebrowser') + +app = BUNDLE(coll, + name='qutebrowser.app', + icon=icon, + # https://github.com/pyinstaller/pyinstaller/blob/b78bfe530cdc2904f65ce098bdf2de08c9037abb/PyInstaller/hooks/hook-PyQt5.QtWebEngineWidgets.py#L24 + bundle_identifier='org.qt-project.Qt.QtWebEngineCore') diff --git a/.config/qutebrowser/misc/requirements/README.md b/.config/qutebrowser/misc/requirements/README.md new file mode 100644 index 0000000..6ae9862 --- /dev/null +++ b/.config/qutebrowser/misc/requirements/README.md @@ -0,0 +1,20 @@ +This directory contains various `requirements` files which are used by `tox` to +have reproducible tests with pinned versions. + +The files are generated based on unpinned requirements in `*.txt-raw` files. + +Those files can also contain some special commands: + +- Add an additional comment to a line: `#@ comment: <package> <comment here>` +- Filter a line for requirements.io: `#@ filter: <package> <filter>` +- Don't include a package in the output: `#@ ignore: <package>` (or multiple packages) +- Replace a part of a frozen package specification with another: `#@ replace <regex> <replacement>` + +Some examples: + +``` +#@ comment: mypkg blah blub +#@ filter: mypkg != 1.0.0 +#@ ignore: mypkg, otherpkg +#@ replace: foo bar +``` diff --git a/.config/qutebrowser/misc/requirements/requirements-check-manifest.txt b/.config/qutebrowser/misc/requirements/requirements-check-manifest.txt new file mode 100644 index 0000000..c11a3f7 --- /dev/null +++ b/.config/qutebrowser/misc/requirements/requirements-check-manifest.txt @@ -0,0 +1,3 @@ +# This file is automatically generated by scripts/dev/recompile_requirements.py + +check-manifest==0.37 diff --git a/.config/qutebrowser/misc/requirements/requirements-check-manifest.txt-raw b/.config/qutebrowser/misc/requirements/requirements-check-manifest.txt-raw new file mode 100644 index 0000000..dcc0efe --- /dev/null +++ b/.config/qutebrowser/misc/requirements/requirements-check-manifest.txt-raw @@ -0,0 +1 @@ +check-manifest diff --git a/.config/qutebrowser/misc/requirements/requirements-codecov.txt b/.config/qutebrowser/misc/requirements/requirements-codecov.txt new file mode 100644 index 0000000..9fed7b3 --- /dev/null +++ b/.config/qutebrowser/misc/requirements/requirements-codecov.txt @@ -0,0 +1,9 @@ +# This file is automatically generated by scripts/dev/recompile_requirements.py + +certifi==2018.4.16 +chardet==3.0.4 +codecov==2.0.15 +coverage==4.5.1 +idna==2.7 +requests==2.19.1 +urllib3==1.23 diff --git a/.config/qutebrowser/misc/requirements/requirements-codecov.txt-raw b/.config/qutebrowser/misc/requirements/requirements-codecov.txt-raw new file mode 100644 index 0000000..15f1c72 --- /dev/null +++ b/.config/qutebrowser/misc/requirements/requirements-codecov.txt-raw @@ -0,0 +1 @@ +codecov diff --git a/.config/qutebrowser/misc/requirements/requirements-flake8.txt b/.config/qutebrowser/misc/requirements/requirements-flake8.txt new file mode 100644 index 0000000..b4f0045 --- /dev/null +++ b/.config/qutebrowser/misc/requirements/requirements-flake8.txt @@ -0,0 +1,27 @@ +# This file is automatically generated by scripts/dev/recompile_requirements.py + +attrs==18.1.0 +flake8==3.5.0 +flake8-bugbear==18.2.0 +flake8-builtins==1.4.1 # rq.filter: != 1.4.0 +flake8-comprehensions==1.4.1 +flake8-copyright==0.2.0 +flake8-debugger==3.1.0 +flake8-deprecated==1.3 +flake8-docstrings==1.3.0 +flake8-future-import==0.4.5 +flake8-mock==0.3 +flake8-per-file-ignores==0.6 +flake8-polyfill==1.0.2 +flake8-string-format==0.2.3 +flake8-tidy-imports==1.1.0 +flake8-tuple==0.2.13 +mccabe==0.6.1 +pathmatch==0.2.1 +pep8-naming==0.7.0 +pycodestyle==2.3.1 # rq.filter: < 2.4.0 +pydocstyle==2.1.1 +pyflakes==2.0.0 +six==1.11.0 +snowballstemmer==1.2.1 +typing==3.6.4 diff --git a/.config/qutebrowser/misc/requirements/requirements-flake8.txt-raw b/.config/qutebrowser/misc/requirements/requirements-flake8.txt-raw new file mode 100644 index 0000000..7ccbbce --- /dev/null +++ b/.config/qutebrowser/misc/requirements/requirements-flake8.txt-raw @@ -0,0 +1,23 @@ +flake8 +flake8-bugbear +flake8-builtins!=1.4.0 +flake8-comprehensions +flake8-copyright +flake8-debugger +flake8-deprecated +flake8-docstrings +flake8-future-import +flake8-mock +flake8-per-file-ignores +flake8-string-format +flake8-tidy-imports +flake8-tuple +pep8-naming +pydocstyle +pyflakes + +# https://github.com/PyCQA/pycodestyle/issues/741 +#@ filter: pycodestyle < 2.4.0 + +# https://github.com/gforcada/flake8-builtins/issues/36 +#@ filter: flake8-builtins != 1.4.0 diff --git a/.config/qutebrowser/misc/requirements/requirements-pip.txt b/.config/qutebrowser/misc/requirements/requirements-pip.txt new file mode 100644 index 0000000..bf003fc --- /dev/null +++ b/.config/qutebrowser/misc/requirements/requirements-pip.txt @@ -0,0 +1,8 @@ +# This file is automatically generated by scripts/dev/recompile_requirements.py + +appdirs==1.4.3 +packaging==17.1 +pyparsing==2.2.0 +setuptools==40.0.0 +six==1.11.0 +wheel==0.31.1 diff --git a/.config/qutebrowser/misc/requirements/requirements-pyinstaller.txt b/.config/qutebrowser/misc/requirements/requirements-pyinstaller.txt new file mode 100644 index 0000000..e916393 --- /dev/null +++ b/.config/qutebrowser/misc/requirements/requirements-pyinstaller.txt @@ -0,0 +1,7 @@ +# This file is automatically generated by scripts/dev/recompile_requirements.py + +altgraph==0.15 +future==0.16.0 +macholib==1.9 +pefile==2017.11.5 +PyInstaller==3.3.1 diff --git a/.config/qutebrowser/misc/requirements/requirements-pyinstaller.txt-raw b/.config/qutebrowser/misc/requirements/requirements-pyinstaller.txt-raw new file mode 100644 index 0000000..c313980 --- /dev/null +++ b/.config/qutebrowser/misc/requirements/requirements-pyinstaller.txt-raw @@ -0,0 +1 @@ +PyInstaller diff --git a/.config/qutebrowser/misc/requirements/requirements-pylint-master.txt b/.config/qutebrowser/misc/requirements/requirements-pylint-master.txt new file mode 100644 index 0000000..2df0736 --- /dev/null +++ b/.config/qutebrowser/misc/requirements/requirements-pylint-master.txt @@ -0,0 +1,19 @@ +# This file is automatically generated by scripts/dev/recompile_requirements.py + +-e git+https://github.com/PyCQA/astroid.git#egg=astroid +certifi==2018.4.16 +chardet==3.0.4 +github3.py==1.1.0 +idna==2.7 +isort==4.3.4 +lazy-object-proxy==1.3.1 +mccabe==0.6.1 +-e git+https://github.com/PyCQA/pylint.git#egg=pylint +python-dateutil==2.7.3 +./scripts/dev/pylint_checkers +requests==2.19.1 +six==1.11.0 +typed-ast==1.1.0 +uritemplate==3.0.0 +urllib3==1.23 +wrapt==1.10.11 diff --git a/.config/qutebrowser/misc/requirements/requirements-pylint-master.txt-raw b/.config/qutebrowser/misc/requirements/requirements-pylint-master.txt-raw new file mode 100644 index 0000000..405b0ab --- /dev/null +++ b/.config/qutebrowser/misc/requirements/requirements-pylint-master.txt-raw @@ -0,0 +1,11 @@ +-e git+https://github.com/PyCQA/astroid.git#egg=astroid +-e git+https://github.com/PyCQA/pylint.git#egg=pylint +./scripts/dev/pylint_checkers +requests +github3.py + +# remove @commit-id for scm installs +#@ replace: @.*# # + +# fix qute-pylint location +#@ replace: qute-pylint==.* ./scripts/dev/pylint_checkers diff --git a/.config/qutebrowser/misc/requirements/requirements-pylint.txt b/.config/qutebrowser/misc/requirements/requirements-pylint.txt new file mode 100644 index 0000000..e78dfe2 --- /dev/null +++ b/.config/qutebrowser/misc/requirements/requirements-pylint.txt @@ -0,0 +1,19 @@ +# This file is automatically generated by scripts/dev/recompile_requirements.py + +astroid==2.0.1 +certifi==2018.4.16 +chardet==3.0.4 +github3.py==1.1.0 +idna==2.7 +isort==4.3.4 +lazy-object-proxy==1.3.1 +mccabe==0.6.1 +pylint==2.0.1 +python-dateutil==2.7.3 +./scripts/dev/pylint_checkers +requests==2.19.1 +six==1.11.0 +typed-ast==1.1.0 +uritemplate==3.0.0 +urllib3==1.23 +wrapt==1.10.11 diff --git a/.config/qutebrowser/misc/requirements/requirements-pylint.txt-raw b/.config/qutebrowser/misc/requirements/requirements-pylint.txt-raw new file mode 100644 index 0000000..37252ee --- /dev/null +++ b/.config/qutebrowser/misc/requirements/requirements-pylint.txt-raw @@ -0,0 +1,7 @@ +pylint +./scripts/dev/pylint_checkers +requests +github3.py + +# fix qute-pylint location +#@ replace: qute-pylint==.* ./scripts/dev/pylint_checkers diff --git a/.config/qutebrowser/misc/requirements/requirements-pyqt.txt b/.config/qutebrowser/misc/requirements/requirements-pyqt.txt new file mode 100644 index 0000000..2878a55 --- /dev/null +++ b/.config/qutebrowser/misc/requirements/requirements-pyqt.txt @@ -0,0 +1,4 @@ +# This file is automatically generated by scripts/dev/recompile_requirements.py + +PyQt5==5.11.2 +PyQt5-sip==4.19.12 diff --git a/.config/qutebrowser/misc/requirements/requirements-pyqt.txt-raw b/.config/qutebrowser/misc/requirements/requirements-pyqt.txt-raw new file mode 100644 index 0000000..37a69c4 --- /dev/null +++ b/.config/qutebrowser/misc/requirements/requirements-pyqt.txt-raw @@ -0,0 +1 @@ +PyQt5
\ No newline at end of file diff --git a/.config/qutebrowser/misc/requirements/requirements-pyroma.txt b/.config/qutebrowser/misc/requirements/requirements-pyroma.txt new file mode 100644 index 0000000..6afd097 --- /dev/null +++ b/.config/qutebrowser/misc/requirements/requirements-pyroma.txt @@ -0,0 +1,4 @@ +# This file is automatically generated by scripts/dev/recompile_requirements.py + +docutils==0.14 +pyroma==2.3.1 diff --git a/.config/qutebrowser/misc/requirements/requirements-pyroma.txt-raw b/.config/qutebrowser/misc/requirements/requirements-pyroma.txt-raw new file mode 100644 index 0000000..5ddfb65 --- /dev/null +++ b/.config/qutebrowser/misc/requirements/requirements-pyroma.txt-raw @@ -0,0 +1 @@ +pyroma diff --git a/.config/qutebrowser/misc/requirements/requirements-qutebrowser.txt-raw b/.config/qutebrowser/misc/requirements/requirements-qutebrowser.txt-raw new file mode 100644 index 0000000..c66c65b --- /dev/null +++ b/.config/qutebrowser/misc/requirements/requirements-qutebrowser.txt-raw @@ -0,0 +1,7 @@ +Jinja2 +Pygments +pyPEG2 +PyYAML +colorama +cssutils +attrs diff --git a/.config/qutebrowser/misc/requirements/requirements-tests-git.txt b/.config/qutebrowser/misc/requirements/requirements-tests-git.txt new file mode 100644 index 0000000..ce00cd3 --- /dev/null +++ b/.config/qutebrowser/misc/requirements/requirements-tests-git.txt @@ -0,0 +1,38 @@ +bzr+lp:beautifulsoup +git+https://github.com/cherrypy/cheroot.git +hg+https://bitbucket.org/ned/coveragepy +git+https://github.com/micheles/decorator.git +git+https://github.com/pallets/flask.git +git+https://github.com/miracle2k/python-glob2.git +git+https://github.com/HypothesisWorks/hypothesis-python.git +git+https://github.com/pallets/itsdangerous.git +git+https://bitbucket.org/zzzeek/mako.git +git+https://github.com/r1chardj0n3s/parse.git +git+https://github.com/jenisys/parse_type.git +hg+https://bitbucket.org/pytest-dev/py +git+https://github.com/pytest-dev/pytest.git@features +git+https://github.com/pytest-dev/pytest-bdd.git +git+https://github.com/pytest-dev/pytest-cov.git +git+https://github.com/pytest-dev/pytest-faulthandler.git +git+https://github.com/pytest-dev/pytest-instafail.git +git+https://github.com/pytest-dev/pytest-mock.git +git+https://github.com/pytest-dev/pytest-qt.git +git+https://github.com/pytest-dev/pytest-repeat.git +git+https://github.com/pytest-dev/pytest-rerunfailures.git +git+https://github.com/abusalimov/pytest-travis-fold.git +git+https://github.com/The-Compiler/pytest-xvfb.git +hg+https://bitbucket.org/gutworth/six +hg+https://bitbucket.org/jendrikseipp/vulture +git+https://github.com/pallets/werkzeug.git + + +## qutebrowser dependencies + +git+https://github.com/tartley/colorama.git +hg+https://bitbucket.org/cthedot/cssutils +git+https://github.com/pallets/jinja.git +git+https://github.com/pallets/markupsafe.git +hg+http://bitbucket.org/birkenfeld/pygments-main +hg+https://bitbucket.org/fdik/pypeg +git+https://github.com/python-attrs/attrs.git +git+https://github.com/yaml/pyyaml.git diff --git a/.config/qutebrowser/misc/requirements/requirements-tests.txt b/.config/qutebrowser/misc/requirements/requirements-tests.txt new file mode 100644 index 0000000..07b0378 --- /dev/null +++ b/.config/qutebrowser/misc/requirements/requirements-tests.txt @@ -0,0 +1,42 @@ +# This file is automatically generated by scripts/dev/recompile_requirements.py + +atomicwrites==1.1.5 +attrs==18.1.0 +backports.functools-lru-cache==1.5 +beautifulsoup4==4.6.0 +cheroot==6.3.3 +click==6.7 +# colorama==0.3.9 +coverage==4.5.1 +EasyProcess==0.2.3 +fields==5.0.0 +Flask==1.0.2 +glob2==0.6 +hunter==2.0.2 +hypothesis==3.66.6 +itsdangerous==0.24 +# Jinja2==2.10 +Mako==1.0.7 +# MarkupSafe==1.0 +more-itertools==4.2.0 +parse==1.8.4 +parse-type==0.4.2 +pluggy==0.6.0 +py==1.5.4 +py-cpuinfo==4.0.0 +pytest==3.6.3 +pytest-bdd==2.21.0 +pytest-benchmark==3.1.1 +pytest-cov==2.5.1 +pytest-faulthandler==1.5.0 +pytest-instafail==0.4.0 +pytest-mock==1.10.0 +pytest-qt==3.0.0 +pytest-repeat==0.5.0 +pytest-rerunfailures==4.1 +pytest-travis-fold==1.3.0 +pytest-xvfb==1.1.0 +PyVirtualDisplay==0.2.1 +six==1.11.0 +vulture==0.28 +Werkzeug==0.14.1 diff --git a/.config/qutebrowser/misc/requirements/requirements-tests.txt-raw b/.config/qutebrowser/misc/requirements/requirements-tests.txt-raw new file mode 100644 index 0000000..1216899 --- /dev/null +++ b/.config/qutebrowser/misc/requirements/requirements-tests.txt-raw @@ -0,0 +1,21 @@ +beautifulsoup4 +cheroot +coverage +Flask +hunter +hypothesis +pytest +pytest-bdd +pytest-benchmark +pytest-cov +pytest-faulthandler +pytest-instafail +pytest-mock +pytest-qt +pytest-repeat +pytest-rerunfailures +pytest-travis-fold +pytest-xvfb +vulture + +#@ ignore: Jinja2, MarkupSafe, colorama diff --git a/.config/qutebrowser/misc/requirements/requirements-tox.txt b/.config/qutebrowser/misc/requirements/requirements-tox.txt new file mode 100644 index 0000000..e8c660e --- /dev/null +++ b/.config/qutebrowser/misc/requirements/requirements-tox.txt @@ -0,0 +1,9 @@ +# This file is automatically generated by scripts/dev/recompile_requirements.py + +packaging==17.1 +pluggy==0.6.0 +py==1.5.4 +pyparsing==2.2.0 +six==1.11.0 +tox==3.1.2 +virtualenv==16.0.0 diff --git a/.config/qutebrowser/misc/requirements/requirements-tox.txt-raw b/.config/qutebrowser/misc/requirements/requirements-tox.txt-raw new file mode 100644 index 0000000..053148f --- /dev/null +++ b/.config/qutebrowser/misc/requirements/requirements-tox.txt-raw @@ -0,0 +1 @@ +tox diff --git a/.config/qutebrowser/misc/requirements/requirements-vulture.txt b/.config/qutebrowser/misc/requirements/requirements-vulture.txt new file mode 100644 index 0000000..c8a26e8 --- /dev/null +++ b/.config/qutebrowser/misc/requirements/requirements-vulture.txt @@ -0,0 +1,3 @@ +# This file is automatically generated by scripts/dev/recompile_requirements.py + +vulture==0.28 diff --git a/.config/qutebrowser/misc/requirements/requirements-vulture.txt-raw b/.config/qutebrowser/misc/requirements/requirements-vulture.txt-raw new file mode 100644 index 0000000..a10f860 --- /dev/null +++ b/.config/qutebrowser/misc/requirements/requirements-vulture.txt-raw @@ -0,0 +1 @@ +vulture diff --git a/.config/qutebrowser/misc/userscripts/cast b/.config/qutebrowser/misc/userscripts/cast new file mode 100755 index 0000000..f7b64df --- /dev/null +++ b/.config/qutebrowser/misc/userscripts/cast @@ -0,0 +1,156 @@ +#!/usr/bin/env bash +# +# Behaviour +# Userscript for qutebrowser which casts the url passed in $1 to the default +# ChromeCast device in the network using the program `castnow` +# +# Usage +# You can launch the script from qutebrowser as follows: +# spawn --userscript ${PATH_TO_FILE} {url} +# +# Then, you can control the chromecast by launching the simple command +# `castnow` in a shell which will connect to the running castnow instance. +# +# For stopping the script, issue the command `pkill -f castnow` which would +# then let the rest of the userscript execute for cleaning temporary file. +# +# Thanks +# This userscript borrows Thorsten Wißmann's javascript code from his `mpv` +# userscript. +# +# Dependencies +# - castnow, https://github.com/xat/castnow +# +# Author +# Simon Désaulniers <sim.desaulniers@gmail.com> + +if [ -z "$QUTE_FIFO" ] ; then + cat 1>&2 <<EOF +Error: $0 can not be run as a standalone script. + +It is a qutebrowser userscript. In order to use it, call it using +'spawn --userscript' as described in qute://help/userscripts.html +EOF + exit 1 +fi + +msg() { + local cmd="$1" + shift + local msg="$*" + if [ -z "$QUTE_FIFO" ] ; then + echo "$cmd: $msg" >&2 + else + echo "message-$cmd '${msg//\'/\\\'}'" >> "$QUTE_FIFO" + fi +} + +js() { +cat <<EOF + + function descendantOfTagName(child, ancestorTagName) { + // tells whether child has some (proper) ancestor + // with the tag name ancestorTagName + while (child.parentNode != null) { + child = child.parentNode; + if (typeof child.tagName === 'undefined') break; + if (child.tagName.toUpperCase() == ancestorTagName.toUpperCase()) { + return true; + } + } + return false; + } + + var App = {}; + + var all_videos = []; + all_videos.push.apply(all_videos, document.getElementsByTagName("video")); + all_videos.push.apply(all_videos, document.getElementsByTagName("object")); + all_videos.push.apply(all_videos, document.getElementsByTagName("embed")); + App.backup_videos = Array(); + App.all_replacements = Array(); + for (i = 0; i < all_videos.length; i++) { + var video = all_videos[i]; + if (descendantOfTagName(video, "object")) { + // skip tags that are contained in an object, because we hide + // the object anyway. + continue; + } + var replacement = document.createElement("div"); + replacement.innerHTML = " + <p style=\\"margin-bottom: 0.5em\\"> + The video is being cast on your ChromeCast device. + </p> + <p> + In order to restore this particular video + <a style=\\"font-weight: bold; + color: white; + background: transparent; + \\" + onClick=\\"restore_video(this, " + i + ");\\" + href=\\"javascript: restore_video(this, " + i + ")\\" + >click here</a>. + </p> + "; + replacement.style.position = "relative"; + replacement.style.zIndex = "100003000000"; + replacement.style.fontSize = "1rem"; + replacement.style.textAlign = "center"; + replacement.style.verticalAlign = "middle"; + replacement.style.height = "100%"; + replacement.style.background = "#101010"; + replacement.style.color = "white"; + replacement.style.border = "4px dashed #545454"; + replacement.style.padding = "2em"; + replacement.style.margin = "auto"; + App.all_replacements[i] = replacement; + App.backup_videos[i] = video; + video.parentNode.replaceChild(replacement, video); + } + + function restore_video(obj, index) { + obj = App.all_replacements[index]; + video = App.backup_videos[index]; + console.log(video); + obj.parentNode.replaceChild(video, obj); + } + + /** force repainting the video, thanks to: + * http://martinwolf.org/2014/06/10/force-repaint-of-an-element-with-javascript/ + */ + var siteHeader = document.getElementById('header'); + siteHeader.style.display='none'; + siteHeader.offsetHeight; // no need to store this anywhere, the reference is enough + siteHeader.style.display='block'; + +EOF +} + +printjs() { + js | sed 's,//.*$,,' | tr '\n' ' ' +} +echo "jseval -q $(printjs)" >> "$QUTE_FIFO" + +tmpdir=$(mktemp -d) +file_to_cast=${tmpdir}/qutecast +program_=$(command -v castnow) + +if [[ "${program_}" == "" ]]; then + msg error "castnow can't be found..." + exit 1 +fi + +# kill any running instance of castnow +pkill -f "${program_}" + +# start youtube download in stream mode (-o -) into temporary file +youtube-dl -qo - "$1" > "${file_to_cast}" & +ytdl_pid=$! + +msg info "Casting $1" >> "$QUTE_FIFO" +# start castnow in stream mode to cast on ChromeCast +tail -F "${file_to_cast}" | ${program_} - + +# cleanup remaining background process and file on disk +kill ${ytdl_pid} +rm -rf "${tmpdir}" diff --git a/.config/qutebrowser/misc/userscripts/dmenu_qutebrowser b/.config/qutebrowser/misc/userscripts/dmenu_qutebrowser new file mode 100755 index 0000000..82e6d2f --- /dev/null +++ b/.config/qutebrowser/misc/userscripts/dmenu_qutebrowser @@ -0,0 +1,48 @@ +#!/usr/bin/env bash + +# Copyright 2015 Zach-Button <zachrey.button@gmail.com> +# Copyright 2015-2017 Florian Bruhin (The Compiler) <mail@qutebrowser.org> +# +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. + +# Pipes history, quickmarks, and URL into dmenu. +# +# If run from qutebrowser as a userscript, it runs :open on the URL +# If not, it opens a new qutebrowser window at the URL +# +# Ideal for use with tabs_are_windows. Set a hotkey to launch this script, then: +# :bind o spawn --userscript dmenu_qutebrowser +# +# Use the hotkey to open in new tab/window, press 'o' to open URL in current tab/window +# You can simulate "go" by pressing "o<tab>", as the current URL is always first in the list +# +# I personally use "<Mod4>o" to launch this script. For me, my workflow is: +# Default keys Keys with this script +# O <Mod4>o +# o o +# go o<Tab> +# gO gC, then o<Tab> +# (This is unnecessarily long. I use this rarely, feel free to make this script accept parameters.) +# + +[ -z "$QUTE_URL" ] && QUTE_URL='http://google.com' + +url=$(echo "$QUTE_URL" | cat - "$QUTE_CONFIG_DIR/quickmarks" "$QUTE_DATA_DIR/history" | dmenu -l 15 -p qutebrowser) +url=$(echo "$url" | sed -E 's/[^ ]+ +//g' | grep -E "https?:" || echo "$url") + +[ -z "${url// }" ] && exit + +echo "open $url" >> "$QUTE_FIFO" || qutebrowser "$url" diff --git a/.config/qutebrowser/misc/userscripts/format_json b/.config/qutebrowser/misc/userscripts/format_json new file mode 100755 index 0000000..0d476b3 --- /dev/null +++ b/.config/qutebrowser/misc/userscripts/format_json @@ -0,0 +1,42 @@ +#!/bin/sh +set -euo pipefail +# +# Behavior: +# Userscript for qutebrowser which will take the raw JSON text of the current +# page, format it using `jq`, will add syntax highlighting using `pygments`, +# and open the syntax highlighted pretty printed html in a new tab. If the file +# is larger than 10MB then this script will only indent the json and will forego +# syntax highlighting using pygments. +# +# In order to use this script, just start it using `spawn --userscript` from +# qutebrowser. I recommend using an alias, e.g. put this in the +# [alias]-section of qutebrowser.conf: +# +# json = spawn --userscript /path/to/json_format +# +# Note that the color style defaults to monokai, but a different pygments style +# can be passed as the first parameter to the script. A full list of the pygments +# styles can be found at: https://help.farbox.com/pygments.html +# +# Bryan Gilbert, 2017 + +# do not run pygmentize on files larger than this amount of bytes +MAX_SIZE_PRETTIFY=10485760 # 10 MB +# default style to monokai if none is provided +STYLE=${1:-monokai} + +TEMP_FILE="$(mktemp)" +jq . "$QUTE_TEXT" >"$TEMP_FILE" + +# try GNU stat first and then OSX stat if the former fails +FILE_SIZE=$( + stat --printf="%s" "$TEMP_FILE" 2>/dev/null || + stat -f%z "$TEMP_FILE" 2>/dev/null +) +if [ "$FILE_SIZE" -lt "$MAX_SIZE_PRETTIFY" ]; then + pygmentize -l json -f html -O full,style="$STYLE" <"$TEMP_FILE" >"${TEMP_FILE}_" + mv -f "${TEMP_FILE}_" "$TEMP_FILE" +fi + +# send the command to qutebrowser to open the new file containing the formatted json +echo "open -t file://$TEMP_FILE" >> "$QUTE_FIFO" diff --git a/.config/qutebrowser/misc/userscripts/getbib b/.config/qutebrowser/misc/userscripts/getbib new file mode 100755 index 0000000..22af7a8 --- /dev/null +++ b/.config/qutebrowser/misc/userscripts/getbib @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +"""Qutebrowser userscript scraping the current web page for DOIs and downloading +corresponding bibtex information. + +Set the environment variable 'QUTE_BIB_FILEPATH' to indicate the path to +download to. Otherwise, bibtex information is downloaded to '/tmp' and hence +deleted at reboot. + +Installation: see qute://help/userscripts.html + +Inspired by +https://ocefpaf.github.io/python4oceanographers/blog/2014/05/19/doi2bibtex/ +""" + +import os +import sys +import shutil +import re +from collections import Counter +from urllib import parse as url_parse +from urllib import request as url_request + + +FIFO_PATH = os.getenv("QUTE_FIFO") + +def message_fifo(message, level="warning"): + """Send message to qutebrowser FIFO. The level must be one of 'info', + 'warning' (default) or 'error'.""" + with open(FIFO_PATH, "w") as fifo: + fifo.write("message-{} '{}'".format(level, message)) + + +source = os.getenv("QUTE_TEXT") +with open(source) as f: + text = f.read() + +# find DOIs on page using regex +dval = re.compile(r'(10\.(\d)+/([^(\s\>\"\<)])+)') +# https://stackoverflow.com/a/10324802/3865876, too strict +# dval = re.compile(r'\b(10[.][0-9]{4,}(?:[.][0-9]+)*/(?:(?!["&\'<>])\S)+)\b') +dois = dval.findall(text) +dois = Counter(e[0] for e in dois) +try: + doi = dois.most_common(1)[0][0] +except IndexError: + message_fifo("No DOIs found on page") + sys.exit() +message_fifo("Found {} DOIs on page, selecting {}".format(len(dois), doi), + level="info") + +# get bibtex data corresponding to DOI +url = "http://dx.doi.org/" + url_parse.quote(doi) +headers = dict(Accept='text/bibliography; style=bibtex') +request = url_request.Request(url, headers=headers) +response = url_request.urlopen(request) +status_code = response.getcode() +if status_code >= 400: + message_fifo("Request returned {}".format(status_code)) + sys.exit() + +# obtain content and format it +bibtex = response.read().decode("utf-8").strip() +bibtex = bibtex.replace(" ", "\n ", 1).\ + replace("}, ", "},\n ").replace("}}", "}\n}") + +# append to file +bib_filepath = os.getenv("QUTE_BIB_FILEPATH", "/tmp/qute.bib") +with open(bib_filepath, "a") as f: + f.write(bibtex + "\n\n") diff --git a/.config/qutebrowser/misc/userscripts/open_download b/.config/qutebrowser/misc/userscripts/open_download new file mode 100755 index 0000000..8dbb113 --- /dev/null +++ b/.config/qutebrowser/misc/userscripts/open_download @@ -0,0 +1,115 @@ +#!/usr/bin/env bash +# Both standalone script and qutebrowser userscript that opens a rofi menu with +# all files from the download director and opens the selected file. It works +# both as a userscript and a standalone script that is called from outside of +# qutebrowser. +# +# Suggested keybinding (for "show downloads"): +# spawn --userscript ~/.config/qutebrowser/open_download +# sd +# +# Requirements: +# - rofi (in a recent version) +# - xdg-open and xdg-mime +# - You should configure qutebrowser to download files to a single directory +# - It comes in handy if you enable downloads.remove_finished. If you want to +# see the recent downloads, just press "sd". +# +# Thorsten Wißmann, 2015 (thorsten` on freenode) +# Any feedback is welcome! + +set -e + +# open a file from the download directory using rofi +DOWNLOAD_DIR=${DOWNLOAD_DIR:-$QUTE_DOWNLOAD_DIR} +DOWNLOAD_DIR=${DOWNLOAD_DIR:-$HOME/Downloads} +# the name of the rofi command +ROFI_CMD=${ROFI_CMD:-rofi} +ROFI_ARGS=${ROFI_ARGS:-} + +msg() { + local cmd="$1" + shift + local msg="$*" + if [ -z "$QUTE_FIFO" ] ; then + echo "$cmd: $msg" >&2 + else + echo "message-$cmd '${msg//\'/\\\'}'" >> "$QUTE_FIFO" + fi +} +die() { + msg error "$*" + if [ -n "$QUTE_FIFO" ] ; then + # when run as a userscript, the above error message already informs the + # user about the failure, and no additional "userscript exited with status + # 1" is needed. + exit 0; + else + exit 1; + fi +} + +if ! [ -d "$DOWNLOAD_DIR" ] ; then + die "Download directory »$DOWNLOAD_DIR« not found!" +fi +if ! command -v "${ROFI_CMD}" > /dev/null ; then + die "Rofi command »${ROFI_CMD}« not found in PATH!" +fi + +rofi_default_args=( + -monitor -2 # place above window + -location 6 # aligned at the bottom + -width 100 # use full window width + -i + -no-custom + -format i # make rofi return the index + -l 10 + -p 'Open download:' -dmenu + ) + +crop-first-column() { + local maxlength=${1:-40} + local expression='s|^\([^\t]\{0,'"$maxlength"'\}\)[^\t]*\t|\1\t|' + sed "$expression" +} + +ls-files() { + # add the slash at the end of the download dir enforces to follow the + # symlink, if the DOWNLOAD_DIR itself is a symlink + # shellcheck disable=SC2010 + ls -Q --quoting-style escape -h -o -1 -A -t "${DOWNLOAD_DIR}/" \ + | grep '^[-]' \ + | cut -d' ' -f3- \ + | sed 's,^\(.*[^\]\) \(.*\)$,\2\t\1,' \ + | sed 's,\\\(.\),\1,g' +} + +mapfile -t entries < <(ls-files) + +# we need to manually check that there are items, because rofi doesn't show up +# if there are no items and -no-custom is passed to rofi. +if [ "${#entries[@]}" -eq 0 ] ; then + die "Download directory »${DOWNLOAD_DIR}« empty" +fi + +line=$(printf '%s\n' "${entries[@]}" \ + | crop-first-column 55 \ + | column -s $'\t' -t \ + | $ROFI_CMD "${rofi_default_args[@]}" "$ROFI_ARGS") || true +if [ -z "$line" ]; then + exit 0 +fi + +file="${entries[$line]}" +file="${file%%$'\t'*}" +path="$DOWNLOAD_DIR/$file" +filetype=$(xdg-mime query filetype "$path") +application=$(xdg-mime query default "$filetype") + +if [ -z "$application" ] ; then + die "Do not know how to open »$file« of type $filetype" +fi + +msg info "Opening »$file« (of type $filetype) with ${application%.desktop}" + +xdg-open "$path" & diff --git a/.config/qutebrowser/misc/userscripts/openfeeds b/.config/qutebrowser/misc/userscripts/openfeeds new file mode 100755 index 0000000..4a1a942 --- /dev/null +++ b/.config/qutebrowser/misc/userscripts/openfeeds @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# Copyright 2015 jnphilipp <me@jnphilipp.org> +# Copyright 2016-2017 Florian Bruhin (The Compiler) <mail@qutebrowser.org> +# +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. + +# Opens all links to feeds defined in the head of a site +# +# Ideal for use with tabs_are_windows. Set a hotkey to launch this script, then: +# :bind gF spawn --userscript openfeeds +# +# Use the hotkey to open the feeds in new tab/window, press 'gF' to open +# + +import os +import re + +from bs4 import BeautifulSoup +from urllib.parse import urljoin + +with open(os.environ['QUTE_HTML'], 'r') as f: + soup = BeautifulSoup(f) +with open(os.environ['QUTE_FIFO'], 'w') as f: + for link in soup.find_all('link', rel='alternate', type=re.compile(r'application/((rss|rdf|atom)\+)?xml|text/xml')): + f.write('open -t %s\n' % urljoin(os.environ['QUTE_URL'], link.get('href'))) diff --git a/.config/qutebrowser/misc/userscripts/password_fill b/.config/qutebrowser/misc/userscripts/password_fill new file mode 100755 index 0000000..a61a42c --- /dev/null +++ b/.config/qutebrowser/misc/userscripts/password_fill @@ -0,0 +1,381 @@ +#!/usr/bin/env bash +help() { + blink=$'\e[1;31m' reset=$'\e[0m' +cat <<EOF +This script can only be used as a userscript for qutebrowser +2015, Thorsten Wißmann <edu _at_ thorsten-wissmann _dot_ de> +In case of questions or suggestions, do not hesitate to send me an E-Mail or to +directly ask me via IRC (nickname thorsten\`) in #qutebrowser on freenode. + + $blink!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!$reset + WARNING: the passwords are stored in qutebrowser's + debug log reachable via the url qute://log + $blink!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!$reset + +Usage: run as a userscript form qutebrowser, e.g.: + spawn --userscript ~/.config/qutebrowser/password_fill + +Pass backend: (see also passwordstore.org) + This script expects pass to store the credentials of each page in an extra + file, where the filename (or filepath) contains the domain of the respective + page. The first line of the file must contain the password, the login name + must be contained in a later line beginning with "user:", "login:", or + "username:" (configurable by the user_pattern variable). + +Behavior: + It will try to find a username/password entry in the configured backend + (currently only pass) for the current website and will load that pair of + username and password to any form on the current page that has some password + entry field. If multiple entries are found, a zenity menu is offered. + + If no entry is found, then it crops subdomains from the url if at least one + entry is found in the backend. (In that case, it always shows a menu) + +Configuration: + This script loads the bash script ~/.config/qutebrowser/password_fill_rc (if + it exists), so you can change any configuration variable and overwrite any + function you like. + +EOF +} + +set -o errexit +set -o pipefail +shopt -s nocasematch # make regexp matching in bash case insensitive + +if [ -z "$QUTE_FIFO" ] ; then + help + exit +fi + +error() { + local msg="$*" + echo "message-error '${msg//\'/\\\'}'" >> "$QUTE_FIFO" +} +msg() { + local msg="$*" + echo "message-info '${msg//\'/\\\'}'" >> "$QUTE_FIFO" +} +die() { + error "$*" + exit 0 +} + +javascript_escape() { + # print the first argument in an escaped way, such that it can safely + # be used within javascripts double quotes + sed "s,[\\\\'\"],\\\\&,g" <<< "$1" +} + +# ======================================================= # +# CONFIGURATION +# ======================================================= # +# The configuration file is per default located in +# ~/.config/qutebrowser/password_fill_rc and is a bash script that is loaded +# later in the present script. So basically you can replace all of the +# following definitions and make them fit your needs. + +# The following simplifies a URL to the domain (e.g. "wiki.qutebrowser.org") +# which is later used to search the correct entries in the password backend. If +# you e.g. don't want the "www." to be removed or if you want to distinguish +# between different paths on the same domain. + +simplify_url() { + simple_url="${1##*://}" # remove protocol specification + simple_url="${simple_url%%\?*}" # remove GET parameters + simple_url="${simple_url%%/*}" # remove directory path + simple_url="${simple_url%:*}" # remove port + simple_url="${simple_url##www.}" # remove www. subdomain +} + +# no_entries_found() is called if the first query_entries() call did not find +# any matching entries. Multiple implementations are possible: +# The easiest behavior is to quit: +#no_entries_found() { +# if [ 0 -eq "${#files[@]}" ] ; then +# die "No entry found for »$simple_url«" +# fi +#} +# But you could also fill the files array with all entries from your pass db +# if the first db query did not find anything +# no_entries_found() { +# if [ 0 -eq "${#files[@]}" ] ; then +# query_entries "" +# if [ 0 -eq "${#files[@]}" ] ; then +# die "No entry found for »$simple_url«" +# fi +# fi +# } + +# Another behavior is to drop another level of subdomains until search hits +# are found: +no_entries_found() { + while [ 0 -eq "${#files[@]}" ] && [ -n "$simple_url" ]; do + shorter_simple_url=$(sed 's,^[^.]*\.,,' <<< "$simple_url") + if [ "$shorter_simple_url" = "$simple_url" ] ; then + # if no dot, then even remove the top level domain + simple_url="" + query_entries "$simple_url" + break + fi + simple_url="$shorter_simple_url" + query_entries "$simple_url" + #die "No entry found for »$simple_url«" + # enforce menu if we do "fuzzy" matching + menu_if_one_entry=1 + done + if [ 0 -eq "${#files[@]}" ] ; then + die "No entry found for »$simple_url«" + fi +} + +# Backend implementations tell, how the actual password store is accessed. +# Right now, there is only one fully functional password backend, namely for +# the program "pass". +# A password backend consists of three actions: +# - init() initializes backend-specific things and does sanity checks. +# - query_entries() is called with a simplified url and is expected to fill +# the bash array $files with the names of matching password entries. There +# are no requirements how these names should look like. +# - open_entry() is called with some specific entry of the $files array and is +# expected to write the username of that entry to the $username variable and +# the corresponding password to $password + +reset_backend() { + init() { true ; } + query_entries() { true ; } + open_entry() { true ; } +} + +# choose_entry() is expected to choose one entry from the array $files and +# write it to the variable $file. +choose_entry() { + choose_entry_zenity +} + +# The default implementation chooses a random entry from the array. So if there +# are multiple matching entries, multiple calls to this userscript will +# eventually pick the "correct" entry. I.e. if this userscript is bound to +# "zl", the user has to press "zl" until the correct username shows up in the +# login form. +choose_entry_random() { + local nr=${#files[@]} + file="${files[$((RANDOM % nr))]}" + # Warn user, that there might be other matching password entries + if [ "$nr" -gt 1 ] ; then + msg "Picked $file out of $nr entries: ${files[*]}" + fi +} + +# another implementation would be to ask the user via some menu (like rofi or +# dmenu or zenity or even qutebrowser completion in future?) which entry to +# pick +MENU_COMMAND=( head -n 1 ) +# whether to show the menu if there is only one entry in it +menu_if_one_entry=0 +choose_entry_menu() { + local nr=${#files[@]} + if [ "$nr" -eq 1 ] && ! ((menu_if_one_entry)) ; then + file="${files[0]}" + else + file=$( printf '%s\n' "${files[@]}" | "${MENU_COMMAND[@]}" ) + fi +} + +choose_entry_rofi() { + MENU_COMMAND=( rofi -p "qutebrowser> " -dmenu + -mesg $'Pick a password entry for <b>'"${QUTE_URL//&/&}"'</b>' ) + choose_entry_menu || true +} + +choose_entry_zenity() { + MENU_COMMAND=( zenity --list --title "qutebrowser password fill" + --text "Pick the password entry:" + --column "Name" ) + choose_entry_menu || true +} + +choose_entry_zenity_radio() { + zenity_helper() { + awk '{ print $0 ; print $0 }' \ + | zenity --list --radiolist \ + --title "qutebrowser password fill" \ + --text "Pick the password entry:" \ + --column " " --column "Name" + } + MENU_COMMAND=( zenity_helper ) + choose_entry_menu || true +} + +# ======================================================= +# backend: PASS + +# configuration options: +match_filename=1 # whether allowing entry match by filepath +match_line=0 # whether allowing entry match by URL-Pattern in file + # Note: match_line=1 gets very slow, even for small password stores! +match_line_pattern='^url: .*' # applied using grep -iE +user_pattern='^(user|username|login): ' + +GPG_OPTS=( "--quiet" "--yes" "--compress-algo=none" "--no-encrypt-to" ) +GPG="gpg" +export GPG_TTY="${GPG_TTY:-$(tty 2>/dev/null)}" +command -v gpg2 &>/dev/null && GPG="gpg2" +[[ -n $GPG_AGENT_INFO || $GPG == "gpg2" ]] && GPG_OPTS+=( "--batch" "--use-agent" ) + +pass_backend() { + init() { + PREFIX="${PASSWORD_STORE_DIR:-$HOME/.password-store}" + if ! [ -d "$PREFIX" ] ; then + die "Can not open password store dir »$PREFIX«" + fi + } + query_entries() { + local url="$1" + + if ((match_line)) ; then + # add entries with matching URL-tag + while read -r -d "" passfile ; do + if $GPG "${GPG_OPTS[@]}" -d "$passfile" \ + | grep --max-count=1 -iE "${match_line_pattern}${url}" > /dev/null + then + passfile="${passfile#$PREFIX}" + passfile="${passfile#/}" + files+=( "${passfile%.gpg}" ) + fi + done < <(find -L "$PREFIX" -iname '*.gpg' -print0) + fi + if ((match_filename)) ; then + # add entries with matching filepath + while read -r passfile ; do + passfile="${passfile#$PREFIX}" + passfile="${passfile#/}" + files+=( "${passfile%.gpg}" ) + done < <(find -L "$PREFIX" -iname '*.gpg' | grep "$url") + fi + } + open_entry() { + local path="$PREFIX/${1}.gpg" + password="" + local firstline=1 + while read -r line ; do + if ((firstline)) ; then + password="$line" + firstline=0 + else + if [[ $line =~ $user_pattern ]] ; then + # remove the matching prefix "user: " from the beginning of the line + username=${line#${BASH_REMATCH[0]}} + break + fi + fi + done < <($GPG "${GPG_OPTS[@]}" -d "$path" ) + } +} +# ======================================================= + +# ======================================================= +# backend: secret +secret_backend() { + init() { + return + } + query_entries() { + local domain="$1" + while read -r line ; do + if [[ "$line" == "attribute.username = "* ]] ; then + files+=("$domain ${line:21}") + fi + done < <( secret-tool search --unlock --all domain "$domain" 2>&1 ) + } + open_entry() { + local domain="${1%% *}" + username="${1#* }" + password=$(secret-tool lookup domain "$domain" username "$username") + } +} +# ======================================================= + +# load some sane default backend +reset_backend +pass_backend +# load configuration +QUTE_CONFIG_DIR=${QUTE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/qutebrowser/} +PWFILL_CONFIG=${PWFILL_CONFIG:-${QUTE_CONFIG_DIR}/password_fill_rc} +if [ -f "$PWFILL_CONFIG" ] ; then + # shellcheck source=/dev/null + source "$PWFILL_CONFIG" +fi +init + +simplify_url "$QUTE_URL" +query_entries "${simple_url}" +no_entries_found +# remove duplicates +mapfile -t files < <(printf '%s\n' "${files[@]}" | sort | uniq ) +choose_entry +if [ -z "$file" ] ; then + # choose_entry didn't want any of these entries + exit 0 +fi +open_entry "$file" +#username="$(date)" +#password="XYZ" +#msg "$username, ${#password}" + +[ -n "$username" ] || die "Username not set in entry $file" +[ -n "$password" ] || die "Password not set in entry $file" + +js() { +cat <<EOF + function isVisible(elem) { + var style = elem.ownerDocument.defaultView.getComputedStyle(elem, null); + + if (style.getPropertyValue("visibility") !== "visible" || + style.getPropertyValue("display") === "none" || + style.getPropertyValue("opacity") === "0") { + return false; + } + + return elem.offsetWidth > 0 && elem.offsetHeight > 0; + }; + function hasPasswordField(form) { + var inputs = form.getElementsByTagName("input"); + for (var j = 0; j < inputs.length; j++) { + var input = inputs[j]; + if (input.type == "password") { + return true; + } + } + return false; + }; + function loadData2Form (form) { + var inputs = form.getElementsByTagName("input"); + for (var j = 0; j < inputs.length; j++) { + var input = inputs[j]; + if (isVisible(input) && (input.type == "text" || input.type == "email")) { + input.focus(); + input.value = "$(javascript_escape "${username}")"; + input.blur(); + } + if (input.type == "password") { + input.focus(); + input.value = "$(javascript_escape "${password}")"; + input.blur(); + } + } + }; + + var forms = document.getElementsByTagName("form"); + for (i = 0; i < forms.length; i++) { + if (hasPasswordField(forms[i])) { + loadData2Form(forms[i]); + } + } +EOF +} + +printjs() { + js | sed 's,//.*$,,' | tr '\n' ' ' +} +echo "jseval -q $(printjs)" >> "$QUTE_FIFO" diff --git a/.config/qutebrowser/misc/userscripts/qute-keepass b/.config/qutebrowser/misc/userscripts/qute-keepass new file mode 100755 index 0000000..a21ebc9 --- /dev/null +++ b/.config/qutebrowser/misc/userscripts/qute-keepass @@ -0,0 +1,261 @@ +#!/usr/bin/env python3 + +# Copyright 2018 Jay Kamat <jaygkamat@gmail.com> +# +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. + +"""This userscript allows for insertion of usernames and passwords from keepass +databases using pykeepass. Since it is a userscript, it must be run from +qutebrowser. + +A sample invocation of this script is: + +:spawn --userscript qute-keepass -p ~/KeePassFiles/MainDatabase.kdbx + +And a sample binding + +:bind --mode=insert <ctrl-i> spawn --userscript qute-keepass -p ~/KeePassFiles/MainDatabase.kdbx + +-p or --path is a required argument. + +--keyfile-path allows you to specify a keepass keyfile. If you only use a +keyfile, also add --no-password as well. Specifying --no-password without +--keyfile-path will lead to an error. + +login information is inserted using :insert-text and :fake-key <Tab>, which +means you must have a cursor in position before initiating this userscript. If +you do not do this, you will get 'element not editable' errors. + +If keepass takes a while to open the DB, you might want to consider reducing +the number of transform rounds in your database settings. + +Dependencies: pykeepass (in python3), PyQt5. Without pykeepass, you will get an +exit code of 100. + +********************!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!****************** + +WARNING: The login details are viewable as plaintext in qutebrowser's debug log +(qute://log) and could be compromised if you decide to submit a crash report! + +********************!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!****************** + +""" + +# pylint: disable=bad-builtin + +import argparse +import enum +import functools +import os +import shlex +import subprocess +import sys + +from PyQt5.QtCore import QUrl +from PyQt5.QtWidgets import QApplication, QInputDialog, QLineEdit + +try: + import pykeepass +except ImportError as e: + print("pykeepass not found: {}".format(str(e)), file=sys.stderr) + + # Since this is a common error, try to print it to the FIFO if we can. + if 'QUTE_FIFO' in os.environ: + with open(os.environ['QUTE_FIFO'], 'w') as fifo: + fifo.write('message-error "pykeepass failed to be imported."\n') + fifo.flush() + sys.exit(100) + +argument_parser = argparse.ArgumentParser( + description="Fill passwords using keepass.", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=__doc__) +argument_parser.add_argument('url', nargs='?', default=os.getenv('QUTE_URL')) +argument_parser.add_argument('--path', '-p', required=True, + help='Path to the keepass db.') +argument_parser.add_argument('--keyfile-path', '-k', default=None, + help='Path to a keepass keyfile') +argument_parser.add_argument( + '--no-password', action='store_true', + help='Supply if no password is required to unlock this database. ' + 'Only allowed with --keyfile-path') +argument_parser.add_argument( + '--dmenu-invocation', '-d', default='dmenu', + help='Invocation used to execute a dmenu-provider') +argument_parser.add_argument( + '--dmenu-format', '-f', default='{title}: {username}', + help='Format string for keys to display in dmenu.' + ' Must generate a unique string.') +argument_parser.add_argument( + '--no-insert-mode', '-n', dest='insert_mode', action='store_false', + help="Don't automatically enter insert mode") +argument_parser.add_argument( + '--io-encoding', '-i', default='UTF-8', + help='Encoding used to communicate with subprocesses') +group = argument_parser.add_mutually_exclusive_group() +group.add_argument('--username-fill-only', '-e', + action='store_true', help='Only insert username') +group.add_argument('--password-fill-only', '-w', + action='store_true', help='Only insert password') + +CMD_DELAY = 50 + + +class ExitCodes(enum.IntEnum): + """Stores various exit codes groups to use.""" + SUCCESS = 0 + FAILURE = 1 + # 1 is automatically used if Python throws an exception + NO_CANDIDATES = 2 + USER_QUIT = 3 + DB_OPEN_FAIL = 4 + + INTERNAL_ERROR = 10 + + +def qute_command(command): + with open(os.environ['QUTE_FIFO'], 'w') as fifo: + fifo.write(command + '\n') + fifo.flush() + + +def stderr(to_print): + """Extra functionality to echo out errors to qb ui.""" + print(to_print, file=sys.stderr) + qute_command('message-error "{}"'.format(to_print)) + + +def dmenu(items, invocation, encoding): + """Runs dmenu with given arguments.""" + command = shlex.split(invocation) + process = subprocess.run(command, input='\n'.join(items).encode(encoding), + stdout=subprocess.PIPE) + return process.stdout.decode(encoding).strip() + + +def get_password(): + """Get a keepass db password from user.""" + _app = QApplication(sys.argv) + text, ok = QInputDialog.getText( + None, "KeePass DB Password", + "Please enter your KeePass Master Password", + QLineEdit.Password) + if not ok: + stderr('Password Prompt Rejected.') + sys.exit(ExitCodes.USER_QUIT) + return text + + +def find_candidates(args, host): + """Finds candidates that match host""" + file_path = os.path.expanduser(args.path) + + # TODO find a way to keep the db open, so we don't open (and query + # password) it every time + + pw = None + if not args.no_password: + pw = get_password() + + kf = args.keyfile_path + if kf: + kf = os.path.expanduser(kf) + + try: + kp = pykeepass.PyKeePass(file_path, password=pw, keyfile=kf) + except Exception as e: + stderr("There was an error opening the DB: {}".format(str(e))) + + return kp.find_entries(url="{}{}{}".format(".*", host, ".*"), regex=True) + + +def candidate_to_str(args, candidate): + """Turns candidate into a human readable string for dmenu""" + return args.dmenu_format.format(title=candidate.title, + url=candidate.url, + username=candidate.username, + path=candidate.path, + uuid=candidate.uuid) + + +def candidate_to_secret(candidate): + """Turns candidate into a generic (user, password) tuple""" + return (candidate.username, candidate.password) + + +def run(args): + """Runs qute-keepass""" + if not args.url: + argument_parser.print_help() + return ExitCodes.FAILURE + + url_host = QUrl(args.url).host() + + if not url_host: + stderr('{} was not parsed as a valid URL!'.format(args.url)) + return ExitCodes.INTERNAL_ERROR + + # Find candidates matching the host of the given URL + candidates = find_candidates(args, url_host) + if not candidates: + stderr('No candidates for URL {!r} found!'.format(args.url)) + return ExitCodes.NO_CANDIDATES + + # Create a map so we can get turn the resulting string from dmenu back into + # a candidate + candidates_strs = list(map(functools.partial(candidate_to_str, args), + candidates)) + candidates_map = dict(zip(candidates_strs, candidates)) + + if len(candidates) == 1: + selection = candidates.pop() + else: + selection = dmenu(candidates_strs, + args.dmenu_invocation, + args.io_encoding) + + if selection not in candidates_map: + stderr("'{}' was not a valid entry!").format(selection) + return ExitCodes.USER_QUIT + + selection = candidates_map[selection] + + username, password = candidate_to_secret(selection) + + insert_mode = ';; enter-mode insert' if args.insert_mode else '' + if args.username_fill_only: + qute_command('insert-text {}{}'.format(username, insert_mode)) + elif args.password_fill_only: + qute_command('insert-text {}{}'.format(password, insert_mode)) + else: + # Enter username and password using insert-key and fake-key <Tab> + # (which supports more passwords than fake-key only), then switch back + # into insert-mode, so the form can be directly submitted by hitting + # enter afterwards. It dosen't matter when we go into insert mode, but + # the other commands need to be be executed sequentially, so we add + # delays with later. + qute_command('insert-text {} ;;' + 'later {} fake-key <Tab> ;;' + 'later {} insert-text {}{}' + .format(username, CMD_DELAY, + CMD_DELAY * 2, password, insert_mode)) + + return ExitCodes.SUCCESS + + +if __name__ == '__main__': + arguments = argument_parser.parse_args() + sys.exit(run(arguments)) diff --git a/.config/qutebrowser/misc/userscripts/qute-lastpass b/.config/qutebrowser/misc/userscripts/qute-lastpass new file mode 100755 index 0000000..ea88cf8 --- /dev/null +++ b/.config/qutebrowser/misc/userscripts/qute-lastpass @@ -0,0 +1,172 @@ +#!/usr/bin/env python3 + +# Copyright 2017 Chris Braun (cryzed) <cryzed@googlemail.com> +# Adapted for LastPass by Wayne Cheng (welps) <waynethecheng@gmail.com> +# +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published bjy +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. + +""" +Insert login information using lastpass CLI and a dmenu-compatible application (e.g. dmenu, rofi -dmenu, ...). +A short demonstration can be seen here: https://i.imgur.com/zA61NrF.gifv. +""" + +USAGE = """The domain of the site has to be in the name of the LastPass entry, for example: "github.com/cryzed" or +"websites/github.com". The login information is inserted by emulating key events using qutebrowser's fake-key command in this manner: +[USERNAME]<Tab>[PASSWORD], which is compatible with almost all login forms. + +You must log into LastPass CLI using `lpass login <email>` prior to use of this script. The LastPass CLI agent only holds your master password for an hour by default. If you wish to change this, please see `man lpass`. + +To use in qutebrowser, run: `spawn --userscript qute-lastpass` +""" + +EPILOG = """Dependencies: tldextract (Python 3 module), LastPass CLI (1.3 or newer) + +WARNING: The login details are viewable as plaintext in qutebrowser's debug log (qute://log) and might be shared if +you decide to submit a crash report!""" + +import argparse +import enum +import fnmatch +import functools +import os +import re +import shlex +import subprocess +import sys +import json +import tldextract + +argument_parser = argparse.ArgumentParser( + description=__doc__, usage=USAGE, epilog=EPILOG) +argument_parser.add_argument('url', nargs='?', default=os.getenv('QUTE_URL')) +argument_parser.add_argument('--dmenu-invocation', '-d', default='rofi -dmenu', + help='Invocation used to execute a dmenu-provider') +argument_parser.add_argument('--no-insert-mode', '-n', dest='insert_mode', action='store_false', + help="Don't automatically enter insert mode") +argument_parser.add_argument('--io-encoding', '-i', default='UTF-8', + help='Encoding used to communicate with subprocesses') +argument_parser.add_argument('--merge-candidates', '-m', action='store_true', + help='Merge pass candidates for fully-qualified and registered domain name') +group = argument_parser.add_mutually_exclusive_group() +group.add_argument('--username-only', '-e', + action='store_true', help='Only insert username') +group.add_argument('--password-only', '-w', + action='store_true', help='Only insert password') + +stderr = functools.partial(print, file=sys.stderr) + +class ExitCodes(enum.IntEnum): + SUCCESS = 0 + FAILURE = 1 + # 1 is automatically used if Python throws an exception + NO_PASS_CANDIDATES = 2 + COULD_NOT_MATCH_USERNAME = 3 + COULD_NOT_MATCH_PASSWORD = 4 + +def qute_command(command): + with open(os.environ['QUTE_FIFO'], 'w') as fifo: + fifo.write(command + '\n') + fifo.flush() + +def pass_(domain, encoding): + args = ['lpass', 'show', '-x', '-j', '-G', '.*{:s}.*'.format(domain)] + process = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + err = process.stderr.decode(encoding).strip() + if err: + msg = "LastPass CLI returned for {:s} - {:s}".format(domain, err) + stderr(msg) + return '[]' + + out = process.stdout.decode(encoding).strip() + + return out + +def dmenu(items, invocation, encoding): + command = shlex.split(invocation) + process = subprocess.run(command, input='\n'.join( + items).encode(encoding), stdout=subprocess.PIPE) + return process.stdout.decode(encoding).strip() + + +def fake_key_raw(text): + for character in text: + # Escape all characters by default, space requires special handling + sequence = '" "' if character == ' ' else '\{}'.format(character) + qute_command('fake-key {}'.format(sequence)) + + +def main(arguments): + if not arguments.url: + argument_parser.print_help() + return ExitCodes.FAILURE + + extract_result = tldextract.extract(arguments.url) + + # Try to find candidates using targets in the following order: fully-qualified domain name (includes subdomains), + # the registered domain name and finally: the IPv4 address if that's what + # the URL represents + candidates = [] + for target in filter(None, [extract_result.fqdn, extract_result.registered_domain, extract_result.subdomain + extract_result.domain, extract_result.domain, extract_result.ipv4]): + target_candidates = json.loads(pass_(target, arguments.io_encoding)) + if not target_candidates: + continue + + candidates = candidates + target_candidates + if not arguments.merge_candidates: + break + else: + if not candidates: + stderr('No pass candidates for URL {!r} found!'.format( + arguments.url)) + return ExitCodes.NO_PASS_CANDIDATES + + if len(candidates) == 1: + selection = candidates.pop() + else: + choices = ["{:s} | {:s} | {:s} | {:s}".format(c["id"], c["name"], c["url"], c["username"]) for c in candidates] + choice = dmenu(choices, arguments.dmenu_invocation, arguments.io_encoding) + choiceId = choice.split("|")[0].strip() + selection = next((c for (i, c) in enumerate(candidates) if c["id"] == choiceId), None) + + # Nothing was selected, simply return + if not selection: + return ExitCodes.SUCCESS + + username = selection["username"] + password = selection["password"] + + if arguments.username_only: + fake_key_raw(username) + elif arguments.password_only: + fake_key_raw(password) + else: + # Enter username and password using fake-key and <Tab> (which seems to work almost universally), then switch + # back into insert-mode, so the form can be directly submitted by + # hitting enter afterwards + fake_key_raw(username) + qute_command('fake-key <Tab>') + fake_key_raw(password) + + if arguments.insert_mode: + qute_command('enter-mode insert') + + return ExitCodes.SUCCESS + + +if __name__ == '__main__': + arguments = argument_parser.parse_args() + sys.exit(main(arguments)) diff --git a/.config/qutebrowser/misc/userscripts/qute-pass b/.config/qutebrowser/misc/userscripts/qute-pass new file mode 100755 index 0000000..4f79e11 --- /dev/null +++ b/.config/qutebrowser/misc/userscripts/qute-pass @@ -0,0 +1,207 @@ +#!/usr/bin/env python3 + +# Copyright 2017 Chris Braun (cryzed) <cryzed@googlemail.com> +# +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. + +""" +Insert login information using pass and a dmenu-compatible application (e.g. dmenu, rofi -dmenu, ...). A short +demonstration can be seen here: https://i.imgur.com/KN3XuZP.gif. +""" + +USAGE = """The domain of the site has to appear as a segment in the pass path, for example: "github.com/cryzed" or +"websites/github.com". How the username and password are determined is freely configurable using the CLI arguments. The +login information is inserted by emulating key events using qutebrowser's fake-key command in this manner: +[USERNAME]<Tab>[PASSWORD], which is compatible with almost all login forms. + +Suggested bindings similar to Uzbl's `formfiller` script: + + config.bind('<z><l>', 'spawn --userscript qute-pass') + config.bind('<z><u><l>', 'spawn --userscript qute-pass --username-only') + config.bind('<z><p><l>', 'spawn --userscript qute-pass --password-only') + config.bind('<z><o><l>', 'spawn --userscript qute-pass --otp-only') +""" + +EPILOG = """Dependencies: tldextract (Python 3 module), pass, pass-otp (optional). +For issues and feedback please use: https://github.com/cryzed/qutebrowser-userscripts. + +WARNING: The login details are viewable as plaintext in qutebrowser's debug log (qute://log) and might be shared if +you decide to submit a crash report!""" + +import argparse +import enum +import fnmatch +import functools +import os +import re +import shlex +import subprocess +import sys + +import tldextract + +argument_parser = argparse.ArgumentParser(description=__doc__, usage=USAGE, epilog=EPILOG) +argument_parser.add_argument('url', nargs='?', default=os.getenv('QUTE_URL')) +argument_parser.add_argument('--password-store', '-p', default=os.path.expanduser('~/.password-store'), + help='Path to your pass password-store') +argument_parser.add_argument('--username-pattern', '-u', default=r'.*/(.+)', + help='Regular expression that matches the username') +argument_parser.add_argument('--username-target', '-U', choices=['path', 'secret'], default='path', + help='The target for the username regular expression') +argument_parser.add_argument('--password-pattern', '-P', default=r'(.*)', + help='Regular expression that matches the password') +argument_parser.add_argument('--dmenu-invocation', '-d', default='rofi -dmenu', + help='Invocation used to execute a dmenu-provider') +argument_parser.add_argument('--no-insert-mode', '-n', dest='insert_mode', action='store_false', + help="Don't automatically enter insert mode") +argument_parser.add_argument('--io-encoding', '-i', default='UTF-8', + help='Encoding used to communicate with subprocesses') +argument_parser.add_argument('--merge-candidates', '-m', action='store_true', + help='Merge pass candidates for fully-qualified and registered domain name') +group = argument_parser.add_mutually_exclusive_group() +group.add_argument('--username-only', '-e', action='store_true', help='Only insert username') +group.add_argument('--password-only', '-w', action='store_true', help='Only insert password') +group.add_argument('--otp-only', '-o', action='store_true', help='Only insert OTP code') + +stderr = functools.partial(print, file=sys.stderr) + + +class ExitCodes(enum.IntEnum): + SUCCESS = 0 + FAILURE = 1 + # 1 is automatically used if Python throws an exception + NO_PASS_CANDIDATES = 2 + COULD_NOT_MATCH_USERNAME = 3 + COULD_NOT_MATCH_PASSWORD = 4 + + +def qute_command(command): + with open(os.environ['QUTE_FIFO'], 'w') as fifo: + fifo.write(command + '\n') + fifo.flush() + + +def find_pass_candidates(domain, password_store_path): + candidates = [] + for path, directories, file_names in os.walk(password_store_path, followlinks=True): + if directories or domain not in path.split(os.path.sep): + continue + + # Strip password store path prefix to get the relative pass path + pass_path = path[len(password_store_path) + 1:] + secrets = fnmatch.filter(file_names, '*.gpg') + candidates.extend(os.path.join(pass_path, os.path.splitext(secret)[0]) for secret in secrets) + return candidates + + +def _run_pass(command, encoding): + process = subprocess.run(command, stdout=subprocess.PIPE) + return process.stdout.decode(encoding).strip() + + +def pass_(path, encoding): + return _run_pass(['pass', path], encoding) + + +def pass_otp(path, encoding): + return _run_pass(['pass', 'otp', path], encoding) + + +def dmenu(items, invocation, encoding): + command = shlex.split(invocation) + process = subprocess.run(command, input='\n'.join(items).encode(encoding), stdout=subprocess.PIPE) + return process.stdout.decode(encoding).strip() + + +def fake_key_raw(text): + for character in text: + # Escape all characters by default, space requires special handling + sequence = '" "' if character == ' ' else '\{}'.format(character) + qute_command('fake-key {}'.format(sequence)) + + +def main(arguments): + if not arguments.url: + argument_parser.print_help() + return ExitCodes.FAILURE + + extract_result = tldextract.extract(arguments.url) + + # Expand potential ~ in paths, since this script won't be called from a shell that does it for us + password_store_path = os.path.expanduser(arguments.password_store) + + # Try to find candidates using targets in the following order: fully-qualified domain name (includes subdomains), + # the registered domain name and finally: the IPv4 address if that's what the URL represents + candidates = set() + for target in filter(None, [extract_result.fqdn, extract_result.registered_domain, extract_result.ipv4]): + target_candidates = find_pass_candidates(target, password_store_path) + if not target_candidates: + continue + + candidates.update(target_candidates) + if not arguments.merge_candidates: + break + else: + if not candidates: + stderr('No pass candidates for URL {!r} found!'.format(arguments.url)) + return ExitCodes.NO_PASS_CANDIDATES + + selection = candidates.pop() if len(candidates) == 1 else dmenu(sorted(candidates), arguments.dmenu_invocation, + arguments.io_encoding) + # Nothing was selected, simply return + if not selection: + return ExitCodes.SUCCESS + + secret = pass_(selection, arguments.io_encoding) + + # Match username + target = selection if arguments.username_target == 'path' else secret + match = re.match(arguments.username_pattern, target) + if not match: + stderr('Failed to match username pattern on {}!'.format(arguments.username_target)) + return ExitCodes.COULD_NOT_MATCH_USERNAME + username = match.group(1) + + # Match password + match = re.match(arguments.password_pattern, secret) + if not match: + stderr('Failed to match password pattern on secret!') + return ExitCodes.COULD_NOT_MATCH_PASSWORD + password = match.group(1) + + if arguments.username_only: + fake_key_raw(username) + elif arguments.password_only: + fake_key_raw(password) + elif arguments.otp_only: + otp = pass_otp(selection, arguments.io_encoding) + fake_key_raw(otp) + else: + # Enter username and password using fake-key and <Tab> (which seems to work almost universally), then switch + # back into insert-mode, so the form can be directly submitted by hitting enter afterwards + fake_key_raw(username) + qute_command('fake-key <Tab>') + fake_key_raw(password) + + if arguments.insert_mode: + qute_command('enter-mode insert') + + return ExitCodes.SUCCESS + + +if __name__ == '__main__': + arguments = argument_parser.parse_args() + sys.exit(main(arguments)) diff --git a/.config/qutebrowser/misc/userscripts/qutedmenu b/.config/qutebrowser/misc/userscripts/qutedmenu new file mode 100755 index 0000000..de1b8d6 --- /dev/null +++ b/.config/qutebrowser/misc/userscripts/qutedmenu @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +# Handle open -s && open -t with bemenu + +#:bind o spawn --userscript /path/to/userscripts/qutedmenu open +#:bind O spawn --userscript /path/to/userscripts/qutedmenu tab + +# If you would like to set a custom colorscheme/font use these dirs. +# https://github.com/halfwit/dotfiles/blob/master/.config/dmenu/bemenucolors +readonly confdir=${XDG_CONFIG_HOME:-$HOME/.config} + +readonly optsfile=$confdir/dmenu/bemenucolors + +create_menu() { + # Check quickmarks + while read -r url; do + printf -- '%s\n' "$url" + done < "$QUTE_CONFIG_DIR"/quickmarks + + # Next bookmarks + while read -r url _; do + printf -- '%s\n' "$url" + done < "$QUTE_CONFIG_DIR"/bookmarks/urls + + # Finally history + while read -r _ url; do + printf -- '%s\n' "$url" + done < "$QUTE_DATA_DIR"/history + } + +get_selection() { + opts+=(-p qutebrowser) + #create_menu | dmenu -l 10 "${opts[@]}" + create_menu | bemenu -l 10 "${opts[@]}" +} + +# Main +# https://github.com/halfwit/dotfiles/blob/master/.config/dmenu/font +[[ -s $confdir/dmenu/font ]] && read -r font < "$confdir"/dmenu/font + +[[ $font ]] && opts+=(-fn "$font") + +# shellcheck source=/dev/null +[[ -s $optsfile ]] && source "$optsfile" + +url=$(get_selection) +url=${url/*http/http} + +# If no selection is made, exit (escape pressed, e.g.) +[[ ! $url ]] && exit 0 + +case $1 in + open) printf '%s' "open $url" >> "$QUTE_FIFO" || qutebrowser "$url" ;; + tab) printf '%s' "open -t $url" >> "$QUTE_FIFO" || qutebrowser "$url" ;; +esac diff --git a/.config/qutebrowser/misc/userscripts/readability b/.config/qutebrowser/misc/userscripts/readability new file mode 100755 index 0000000..d0ef437 --- /dev/null +++ b/.config/qutebrowser/misc/userscripts/readability @@ -0,0 +1,41 @@ +#!/usr/bin/env python +# +# Executes python-readability on current page and opens the summary as new tab. +# +# Depends on the python-readability package, or its fork: +# +# - https://github.com/buriy/python-readability +# - https://github.com/bookieio/breadability +# +# Usage: +# :spawn --userscript readability +# +from __future__ import absolute_import +import codecs, os + +tmpfile = os.path.join( + os.environ.get('QUTE_DATA_DIR', + os.path.expanduser('~/.local/share/qutebrowser')), + 'userscripts/readability.html') + +if not os.path.exists(os.path.dirname(tmpfile)): + os.makedirs(os.path.dirname(tmpfile)) + +with codecs.open(os.environ['QUTE_HTML'], 'r', 'utf-8') as source: + data = source.read() + + try: + from breadability.readable import Article as reader + doc = reader(data) + content = doc.readable + except ImportError: + from readability import Document + doc = Document(data) + content = doc.summary().replace('<html>', '<html><head><title>%s</title></head>' % doc.title()) + + with codecs.open(tmpfile, 'w', 'utf-8') as target: + target.write('<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />') + target.write(content) + + with open(os.environ['QUTE_FIFO'], 'w') as fifo: + fifo.write('open -t %s' % tmpfile) diff --git a/.config/qutebrowser/misc/userscripts/ripbang b/.config/qutebrowser/misc/userscripts/ripbang new file mode 100755 index 0000000..b35ff77 --- /dev/null +++ b/.config/qutebrowser/misc/userscripts/ripbang @@ -0,0 +1,34 @@ +#!/usr/bin/env python +# +# Adds DuckDuckGo bang as searchengine. +# +# Usage: +# :spawn --userscript ripbang [bang]... +# +# Example: +# :spawn --userscript ripbang amazon maps +# + +from __future__ import print_function +import os, re, requests, sys + +try: + from urllib.parse import unquote +except ImportError: + from urllib import unquote + +for argument in sys.argv[1:]: + bang = '!' + argument + r = requests.get('https://duckduckgo.com/', + params={'q': bang + ' SEARCHTEXT'}) + + searchengine = unquote(re.search("url=[^']+", r.text).group(0)) + searchengine = searchengine.replace('url=', '') + searchengine = searchengine.replace('/l/?kh=-1&uddg=', '') + searchengine = searchengine.replace('SEARCHTEXT', '{}') + + if os.getenv('QUTE_FIFO'): + with open(os.environ['QUTE_FIFO'], 'w') as fifo: + fifo.write('set searchengines %s %s' % (bang, searchengine)) + else: + print('%s %s' % (bang, searchengine)) diff --git a/.config/qutebrowser/misc/userscripts/rss b/.config/qutebrowser/misc/userscripts/rss new file mode 100755 index 0000000..f8feebe --- /dev/null +++ b/.config/qutebrowser/misc/userscripts/rss @@ -0,0 +1,122 @@ +#!/bin/sh + +# Copyright 2016 Jan Verbeek (blyxxyz) <ring@openmailbox.org> +# +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. + +# This script keeps track of URLs in RSS feeds and opens new ones. +# New feeds can be added with ':spawn -u /path/to/userscripts/rss add' or +# ':spawn -u /path/to/userscripts/rss <url>'. +# New items can be opened with ':spawn -u /path/to/userscripts/rss'. +# The script doesn't really parse XML, and searches for things that look like +# item links. It might open things that aren't real links, and it might miss +# real links. + +config_dir="$HOME/.qute-rss" + +add_feed () { + touch "feeds" + if grep -Fq "$1" "feeds"; then + notice "$1 is saved already." + else + printf '%s\n' "$1" >> "feeds" + fi +} + +# Show an error message and exit +fail () { + echo "message-error '$*'" > "$QUTE_FIFO" + exit 1 +} + +# Get a sorted list of item URLs from a RSS feed +get_items () { + $curl "$@" | grep "$text_only" -zo -e '<guid[^<>]*>[^<>]*</guid>' \ + -e '<link[^<>]*>[^<>]*</link>' \ + -e '<link[^<>]*href="[^"]*"' | + grep "$text_only" -o 'http[^<>"]*' | sort | uniq +} + +# Show an info message +notice () { + echo "message-info '$*'" > "$QUTE_FIFO" +} + +# Update a database of a feed and open new URLs +read_items () { + cd read_urls || return 1 + feed_file="$(echo "$1" | tr -d /)" + feed_temp_file="$(mktemp "$feed_file.tmp.XXXXXXXXXX")" + feed_new_items="$(mktemp "$feed_file.new.XXXXXXXXXX")" + get_items "$1" > "$feed_temp_file" + if [ ! -s "$feed_temp_file" ]; then + notice "No items found for $1." + rm "$feed_temp_file" "$feed_new_items" + elif [ ! -f "$feed_file" ]; then + notice "$1 is a new feed. All items will be marked as read." + mv "$feed_temp_file" "$feed_file" + rm "$feed_new_items" + else + sort -o "$feed_file" "$feed_file" + comm -2 -3 "$feed_temp_file" "$feed_file" | tee "$feed_new_items" + cat "$feed_new_items" >> "$feed_file" + sort -o "$feed_file" "$feed_file" + rm "$feed_temp_file" "$feed_new_items" + fi | while read -r item; do + echo "open -t $item" > "$QUTE_FIFO" + done +} + +if [ ! -d "$config_dir/read_urls" ]; then + notice "Creating configuration directory." + mkdir -p "$config_dir/read_urls" +fi + +cd "$config_dir" || exit 1 + +if [ $# != 0 ]; then + for arg in "$@"; do + if [ "$arg" = "add" ]; then + add_feed "$QUTE_URL" + else + add_feed "$arg" + fi + done + exit +fi + +if [ ! -f "feeds" ]; then + fail "Add feeds by running ':spawn -u rss add' or ':spawn -u rss <url>'." +fi + +if curl --version >&-; then + curl="curl -sL" +elif wget --version >&-; then + curl="wget -qO -" +else + fail "Either curl or wget is needed to run this script." +fi + +# Detect GNU grep so we can force it to treat everything as text +if < /dev/null grep --help 2>&1 | grep -q -- -a; then + text_only="-a" +fi + +while read -r feed_url; do + read_items "$feed_url" & +done < "$config_dir/feeds" + +wait diff --git a/.config/qutebrowser/misc/userscripts/taskadd b/.config/qutebrowser/misc/userscripts/taskadd new file mode 100755 index 0000000..36e1c2c --- /dev/null +++ b/.config/qutebrowser/misc/userscripts/taskadd @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +# +# Behavior: +# Userscript for qutebrowser which adds a task to taskwarrior. +# If run as a command (:spawn --userscript taskadd), it creates a new task +# with the description equal to the current page title and annotates it with +# the current page url. Additional arguments are passed along so you can add +# mods to the task (e.g. priority, due date, tags). +# +# Example: +# :spawn --userscript taskadd due:eod pri:H +# +# To enable passing along extra args, I suggest using a mapping like: +# :bind <somekey> set-cmd-text -s :spawn --userscript taskadd +# +# If run from hint mode, it uses the selected hint text as the description +# and the selected hint url as the annotation. +# +# Ryan Roden-Corrent (rcorre), 2016 +# Any feedback is welcome! +# +# For more info on Taskwarrior, see http://taskwarrior.org/ + +# use either the current page title or the hint text as the task description +[[ $QUTE_MODE == 'hints' ]] && title=$QUTE_SELECTED_TEXT || title=$QUTE_TITLE + +# try to add the task and grab the output +if msg="$(task add "$title" "$*" 2>&1)"; then + # annotate the new task with the url, send the output back to the browser + task +LATEST annotate "$QUTE_URL" + echo "message-info '$(echo "$msg" | head -n 1)'" >> "$QUTE_FIFO" +else + echo "message-error '$(echo "$msg" | head -n 1)'" >> "$QUTE_FIFO" +fi diff --git a/.config/qutebrowser/misc/userscripts/tor_identity b/.config/qutebrowser/misc/userscripts/tor_identity new file mode 100755 index 0000000..93b6d41 --- /dev/null +++ b/.config/qutebrowser/misc/userscripts/tor_identity @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# Copyright 2018 jnphilipp <mail@jnphilipp.org> +# +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. + +# Change your tor identity. +# +# Set a hotkey to launch this script, then: +# :bind ti spawn --userscript tor_identity PASSWORD +# +# Use the hotkey to change your tor identity, press 'ti' to change it. +# https://stem.torproject.org/faq.html#how-do-i-request-a-new-identity-from-tor +# + +import os +import sys + +try: + from stem import Signal + from stem.control import Controller +except ImportError: + if os.getenv('QUTE_FIFO'): + with open(os.environ['QUTE_FIFO'], 'w') as f: + f.write('message-error "Failed to import stem."') + else: + print('Failed to import stem.') + + +password = sys.argv[1] +with Controller.from_port(port=9051) as controller: + controller.authenticate(password) + controller.signal(Signal.NEWNYM) + if os.getenv('QUTE_FIFO'): + with open(os.environ['QUTE_FIFO'], 'w') as f: + f.write('message-info "Tor identity changed."') + else: + print('Tor identity changed.') diff --git a/.config/qutebrowser/misc/userscripts/view_in_mpv b/.config/qutebrowser/misc/userscripts/view_in_mpv new file mode 100755 index 0000000..16603bd --- /dev/null +++ b/.config/qutebrowser/misc/userscripts/view_in_mpv @@ -0,0 +1,143 @@ +#!/usr/bin/env bash +# +# Behavior: +# Userscript for qutebrowser which views the current web page in mpv using +# sensible mpv-flags. While viewing the page in MPV, all <video>, <embed>, +# and <object> tags in the original page are temporarily removed. Clicking on +# such a removed video restores the respective video. +# +# In order to use this script, just start it using `spawn --userscript` from +# qutebrowser. I recommend using an alias, e.g. put this in the +# [alias]-section of qutebrowser.conf: +# +# mpv = spawn --userscript /path/to/view_in_mpv +# +# Background: +# Most of my machines are too slow to play youtube videos using html5, but +# they work fine in mpv (and mpv has further advantages like video scaling, +# etc). Of course, I don't want the video to be played (or even to be +# downloaded) twice — in MPV and in qwebkit. So I often close the tab after +# opening it in mpv. However, I actually want to keep the rest of the page +# (comments and video suggestions), i.e. only the videos should disappear +# when mpv is started. And that's precisely what the present script does. +# +# Thorsten Wißmann, 2015 (thorsten` on freenode) +# Any feedback is welcome! + +set -e + +if [ -z "$QUTE_FIFO" ] ; then + cat 1>&2 <<EOF +Error: $0 can not be run as a standalone script. + +It is a qutebrowser userscript. In order to use it, call it using +'spawn --userscript' as described in qute://help/userscripts.html +EOF + exit 1 +fi + +msg() { + local cmd="$1" + shift + local msg="$*" + if [ -z "$QUTE_FIFO" ] ; then + echo "$cmd: $msg" >&2 + else + echo "message-$cmd '${msg//\'/\\\'}'" >> "$QUTE_FIFO" + fi +} + +MPV_COMMAND=${MPV_COMMAND:-mpv} +# Warning: spaces in single flags are not supported +MPV_FLAGS=${MPV_FLAGS:- --force-window --no-terminal --keep-open=yes --ytdl} +IFS=" " read -r -a video_command <<< "$MPV_COMMAND $MPV_FLAGS" + +js() { +cat <<EOF + + function descendantOfTagName(child, ancestorTagName) { + // tells whether child has some (proper) ancestor + // with the tag name ancestorTagName + while (child.parentNode != null) { + child = child.parentNode; + if (typeof child.tagName === 'undefined') break; + if (child.tagName.toUpperCase() == ancestorTagName.toUpperCase()) { + return true; + } + } + return false; + } + + var App = {}; + + var all_videos = []; + all_videos.push.apply(all_videos, document.getElementsByTagName("video")); + all_videos.push.apply(all_videos, document.getElementsByTagName("object")); + all_videos.push.apply(all_videos, document.getElementsByTagName("embed")); + App.backup_videos = Array(); + App.all_replacements = Array(); + for (i = 0; i < all_videos.length; i++) { + var video = all_videos[i]; + if (descendantOfTagName(video, "object")) { + // skip tags that are contained in an object, because we hide + // the object anyway. + continue; + } + var replacement = document.createElement("div"); + replacement.innerHTML = " + <p style=\\"margin-bottom: 0.5em\\"> + Opening page with: + <span style=\\"font-family: monospace;\\">${video_command[*]}</span> + </p> + <p> + In order to restore this particular video + <a style=\\"font-weight: bold; + color: white; + background: transparent; + \\" + onClick=\\"restore_video(this, " + i + ");\\" + href=\\"javascript: restore_video(this, " + i + ")\\" + >click here</a>. + </p> + "; + replacement.style.position = "relative"; + replacement.style.zIndex = "100003000000"; + replacement.style.fontSize = "1rem"; + replacement.style.textAlign = "center"; + replacement.style.verticalAlign = "middle"; + replacement.style.height = "100%"; + replacement.style.background = "#101010"; + replacement.style.color = "white"; + replacement.style.border = "4px dashed #545454"; + replacement.style.padding = "2em"; + replacement.style.margin = "auto"; + App.all_replacements[i] = replacement; + App.backup_videos[i] = video; + video.parentNode.replaceChild(replacement, video); + } + + function restore_video(obj, index) { + obj = App.all_replacements[index]; + video = App.backup_videos[index]; + console.log(video); + obj.parentNode.replaceChild(video, obj); + } + + /** force repainting the video, thanks to: + * http://martinwolf.org/2014/06/10/force-repaint-of-an-element-with-javascript/ + */ + var siteHeader = document.getElementById('header'); + siteHeader.style.display='none'; + siteHeader.offsetHeight; // no need to store this anywhere, the reference is enough + siteHeader.style.display='block'; + +EOF +} + +printjs() { + js | sed 's,//.*$,,' | tr '\n' ' ' +} +echo "jseval -q $(printjs)" >> "$QUTE_FIFO" + +msg info "Opening $QUTE_URL with mpv" +"${video_command[@]}" "$@" "$QUTE_URL" diff --git a/.config/qutebrowser/nord-qutebrowser.py b/.config/qutebrowser/nord-qutebrowser.py new file mode 100644 index 0000000..9dd9064 --- /dev/null +++ b/.config/qutebrowser/nord-qutebrowser.py @@ -0,0 +1,336 @@ +nord = { + # Polar Night + 'nord0': '#2e3440', + 'nord1': '#3b4252', + 'nord2': '#434c5e', + 'nord3': '#4c566a', + # Snow Storm + 'nord4': '#d8dee9', + 'nord5': '#e5e9f0', + 'nord6': '#eceff4', + # Frost + 'nord7': '#8fbcbb', + 'nord8': '#88c0d0', + 'nord9': '#81a1c1', + 'nord10': '#5e81ac', + # Aurora + 'nord11': '#bf616a', + 'nord12': '#d08770', + 'nord13': '#ebcb8b', + 'nord14': '#a3be8c', + 'nord15': '#b48ead', +} + +## Background color of the completion widget category headers. +## Type: QssColor +c.colors.completion.category.bg = nord['nord0'] + +## Bottom border color of the completion widget category headers. +## Type: QssColor +c.colors.completion.category.border.bottom = nord['nord0'] + +## Top border color of the completion widget category headers. +## Type: QssColor +c.colors.completion.category.border.top = nord['nord0'] + +## Foreground color of completion widget category headers. +## Type: QtColor +c.colors.completion.category.fg = nord['nord5'] + +## Background color of the completion widget for even rows. +## Type: QssColor +c.colors.completion.even.bg = nord['nord1'] + +## Background color of the completion widget for odd rows. +## Type: QssColor +c.colors.completion.odd.bg = nord['nord1'] + +## Text color of the completion widget. +## Type: QtColor +c.colors.completion.fg = nord['nord4'] + +## Background color of the selected completion item. +## Type: QssColor +c.colors.completion.item.selected.bg = nord['nord3'] + +## Bottom border color of the selected completion item. +## Type: QssColor +c.colors.completion.item.selected.border.bottom = nord['nord3'] + +## Top border color of the completion widget category headers. +## Type: QssColor +c.colors.completion.item.selected.border.top = nord['nord3'] + +## Foreground color of the selected completion item. +## Type: QtColor +c.colors.completion.item.selected.fg = nord['nord6'] + +## Foreground color of the matched text in the completion. +## Type: QssColor +c.colors.completion.match.fg = nord['nord13'] + +## Color of the scrollbar in completion view +## Type: QssColor +c.colors.completion.scrollbar.bg = nord['nord1'] + +## Color of the scrollbar handle in completion view. +## Type: QssColor +c.colors.completion.scrollbar.fg = nord['nord5'] + +## Background color for the download bar. +## Type: QssColor +c.colors.downloads.bar.bg = nord['nord0'] + +## Background color for downloads with errors. +## Type: QtColor +c.colors.downloads.error.bg = nord['nord11'] + +## Foreground color for downloads with errors. +## Type: QtColor +c.colors.downloads.error.fg = nord['nord5'] + +## Color gradient stop for download backgrounds. +## Type: QtColor +c.colors.downloads.stop.bg = nord['nord15'] + +## Color gradient interpolation system for download backgrounds. +## Type: ColorSystem +## Valid values: +## - rgb: Interpolate in the RGB color system. +## - hsv: Interpolate in the HSV color system. +## - hsl: Interpolate in the HSL color system. +## - none: Don't show a gradient. +c.colors.downloads.system.bg = 'none' + +## Background color for hints. Note that you can use a `rgba(...)` value +## for transparency. +## Type: QssColor +c.colors.hints.bg = nord['nord13'] + +## Font color for hints. +## Type: QssColor +c.colors.hints.fg = nord['nord0'] + +## Font color for the matched part of hints. +## Type: QssColor +c.colors.hints.match.fg = nord['nord10'] + +## Background color of the keyhint widget. +## Type: QssColor +c.colors.keyhint.bg = nord['nord1'] + +## Text color for the keyhint widget. +## Type: QssColor +c.colors.keyhint.fg = nord['nord5'] + +## Highlight color for keys to complete the current keychain. +## Type: QssColor +c.colors.keyhint.suffix.fg = nord['nord13'] + +## Background color of an error message. +## Type: QssColor +c.colors.messages.error.bg = nord['nord11'] + +## Border color of an error message. +## Type: QssColor +c.colors.messages.error.border = nord['nord11'] + +## Foreground color of an error message. +## Type: QssColor +c.colors.messages.error.fg = nord['nord5'] + +## Background color of an info message. +## Type: QssColor +c.colors.messages.info.bg = nord['nord8'] + +## Border color of an info message. +## Type: QssColor +c.colors.messages.info.border = nord['nord8'] + +## Foreground color an info message. +## Type: QssColor +c.colors.messages.info.fg = nord['nord5'] + +## Background color of a warning message. +## Type: QssColor +c.colors.messages.warning.bg = nord['nord12'] + +## Border color of a warning message. +## Type: QssColor +c.colors.messages.warning.border = nord['nord12'] + +## Foreground color a warning message. +## Type: QssColor +c.colors.messages.warning.fg = nord['nord5'] + +## Background color for prompts. +## Type: QssColor +c.colors.prompts.bg = nord['nord2'] + +# ## Border used around UI elements in prompts. +# ## Type: String +c.colors.prompts.border = '1px solid ' + nord['nord0'] + +## Foreground color for prompts. +## Type: QssColor +c.colors.prompts.fg = nord['nord5'] + +## Background color for the selected item in filename prompts. +## Type: QssColor +c.colors.prompts.selected.bg = nord['nord3'] + +## Background color of the statusbar in caret mode. +## Type: QssColor +c.colors.statusbar.caret.bg = nord['nord15'] + +## Foreground color of the statusbar in caret mode. +## Type: QssColor +c.colors.statusbar.caret.fg = nord['nord5'] + +## Background color of the statusbar in caret mode with a selection. +## Type: QssColor +c.colors.statusbar.caret.selection.bg = nord['nord15'] + +## Foreground color of the statusbar in caret mode with a selection. +## Type: QssColor +c.colors.statusbar.caret.selection.fg = nord['nord5'] + +## Background color of the statusbar in command mode. +## Type: QssColor +c.colors.statusbar.command.bg = nord['nord2'] + +## Foreground color of the statusbar in command mode. +## Type: QssColor +c.colors.statusbar.command.fg = nord['nord5'] + +## Background color of the statusbar in private browsing + command mode. +## Type: QssColor +c.colors.statusbar.command.private.bg = nord['nord2'] + +## Foreground color of the statusbar in private browsing + command mode. +## Type: QssColor +c.colors.statusbar.command.private.fg = nord['nord5'] + +## Background color of the statusbar in insert mode. +## Type: QssColor +c.colors.statusbar.insert.bg = nord['nord14'] + +## Foreground color of the statusbar in insert mode. +## Type: QssColor +c.colors.statusbar.insert.fg = nord['nord1'] + +## Background color of the statusbar. +## Type: QssColor +c.colors.statusbar.normal.bg = nord['nord0'] + +## Foreground color of the statusbar. +## Type: QssColor +c.colors.statusbar.normal.fg = nord['nord5'] + +## Background color of the statusbar in passthrough mode. +## Type: QssColor +c.colors.statusbar.passthrough.bg = nord['nord10'] + +## Foreground color of the statusbar in passthrough mode. +## Type: QssColor +c.colors.statusbar.passthrough.fg = nord['nord5'] + +## Background color of the statusbar in private browsing mode. +## Type: QssColor +c.colors.statusbar.private.bg = nord['nord3'] + +## Foreground color of the statusbar in private browsing mode. +## Type: QssColor +c.colors.statusbar.private.fg = nord['nord5'] + +## Background color of the progress bar. +## Type: QssColor +c.colors.statusbar.progress.bg = nord['nord5'] + +## Foreground color of the URL in the statusbar on error. +## Type: QssColor +c.colors.statusbar.url.error.fg = nord['nord11'] + +## Default foreground color of the URL in the statusbar. +## Type: QssColor +c.colors.statusbar.url.fg = nord['nord5'] + +## Foreground color of the URL in the statusbar for hovered links. +## Type: QssColor +c.colors.statusbar.url.hover.fg = nord['nord8'] + +## Foreground color of the URL in the statusbar on successful load +## (http). +## Type: QssColor +c.colors.statusbar.url.success.http.fg = nord['nord5'] + +## Foreground color of the URL in the statusbar on successful load +## (https). +## Type: QssColor +c.colors.statusbar.url.success.https.fg = nord['nord14'] + +## Foreground color of the URL in the statusbar when there's a warning. +## Type: QssColor +c.colors.statusbar.url.warn.fg = nord['nord12'] + +## Background color of the tab bar. +## Type: QtColor +c.colors.tabs.bar.bg = nord['nord3'] + +## Background color of unselected even tabs. +## Type: QtColor +c.colors.tabs.even.bg = nord['nord3'] + +## Foreground color of unselected even tabs. +## Type: QtColor +c.colors.tabs.even.fg = nord['nord5'] + +## Color for the tab indicator on errors. +## Type: QtColor +c.colors.tabs.indicator.error = nord['nord11'] + +## Color gradient start for the tab indicator. +## Type: QtColor +# c.colors.tabs.indicator.start = nord['violet'] + +## Color gradient end for the tab indicator. +## Type: QtColor +# c.colors.tabs.indicator.stop = nord['orange'] + +## Color gradient interpolation system for the tab indicator. +## Type: ColorSystem +## Valid values: +## - rgb: Interpolate in the RGB color system. +## - hsv: Interpolate in the HSV color system. +## - hsl: Interpolate in the HSL color system. +## - none: Don't show a gradient. +c.colors.tabs.indicator.system = 'none' + +## Background color of unselected odd tabs. +## Type: QtColor +c.colors.tabs.odd.bg = nord['nord3'] + +## Foreground color of unselected odd tabs. +## Type: QtColor +c.colors.tabs.odd.fg = nord['nord5'] + +# ## Background color of selected even tabs. +# ## Type: QtColor +c.colors.tabs.selected.even.bg = nord['nord0'] + +# ## Foreground color of selected even tabs. +# ## Type: QtColor +c.colors.tabs.selected.even.fg = nord['nord5'] + +# ## Background color of selected odd tabs. +# ## Type: QtColor +c.colors.tabs.selected.odd.bg = nord['nord0'] + +# ## Foreground color of selected odd tabs. +# ## Type: QtColor +c.colors.tabs.selected.odd.fg = nord['nord5'] + +## Background color for webpages if unset (or empty to use the theme's +## color) +## Type: QtColor +# c.colors.webpage.bg = 'white' diff --git a/.config/qutebrowser/qsettings/QtProject.conf b/.config/qutebrowser/qsettings/QtProject.conf new file mode 100644 index 0000000..8777b99 --- /dev/null +++ b/.config/qutebrowser/qsettings/QtProject.conf @@ -0,0 +1,8 @@ +[FileDialog] +history=file:///home/archlinux/vgg, file:///home/archlinux/vgg/Downloads, file:///home/archlinux/vgg/Sandbox/Python/PlasmaPySandbox, file:///home/archlinux/vgg/Documents/Astro/UoH/Teaching/PlasmaPhysics/Lecture4, file:///home/archlinux/vgg/Requisition/LizzieNoakes +lastVisited=file:///home/archlinux/vgg/Requisition/LizzieNoakes +qtVersion=5.13.2 +shortcuts=file:, file:///home/archlinux/vgg +sidebarWidth=98 +treeViewHeader=@ByteArray(\0\0\0\xff\0\0\0\0\0\0\0\x1\0\0\0\0\0\0\0\0\x1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x1\xec\0\0\0\x4\x1\x1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x64\xff\xff\xff\xff\0\0\0\x81\0\0\0\0\0\0\0\x4\0\0\0\xff\0\0\0\x1\0\0\0\0\0\0\0?\0\0\0\x1\0\0\0\0\0\0\0@\0\0\0\x1\0\0\0\0\0\0\0n\0\0\0\x1\0\0\0\0\0\0\x3\xe8\0\xff\xff\xff\xff) +viewMode=Detail diff --git a/.config/qutebrowser/quickmarks b/.config/qutebrowser/quickmarks new file mode 100755 index 0000000..61cd7db --- /dev/null +++ b/.config/qutebrowser/quickmarks @@ -0,0 +1 @@ +bookmark file://${HOME}/bookmarks.html diff --git a/.config/qutebrowser/qutebrowser.conf b/.config/qutebrowser/qutebrowser.conf new file mode 100755 index 0000000..3fc85a8 --- /dev/null +++ b/.config/qutebrowser/qutebrowser.conf @@ -0,0 +1,1612 @@ +# vim: ft=dosini + +# Configfile for qutebrowser. +# +# This configfile is parsed by python's configparser in extended +# interpolation mode. The format is very INI-like, so there are +# categories like [general] with "key = value"-pairs. +# +# Note that you shouldn't add your own comments, as this file is +# regenerated every time the config is saved. +# +# Interpolation looks like ${value} or ${section:value} and will be +# replaced by the respective value. +# +# Some settings will expand environment variables. Note that, since +# interpolation is run first, you will need to escape the $ char as +# described below. +# +# This is the default config, so if you want to remove anything from +# here (as opposed to change/add), for example a key binding, set it to +# an empty value. +# +# You will need to escape the following values: +# - # at the start of the line (at the first position of the key) (\#) +# - $ in a value ($$) + + +# General/miscellaneous options. +[general] + +# ignore-case (IgnoreCase): +# Whether to find text on a page case-insensitively. +# true: Search case-insensitively +# false: Search case-sensitively +# smart: Search case-sensitively if there are capital chars +# Default: smart +ignore-case = smart + +# startpage (List of String): +# The default page(s) to open at the start, separated by commas. +# Default: https://start.duckduckgo.com +startpage = https://start.duckduckgo.com + +# yank-ignored-url-parameters (List of String): +# The URL parameters to strip with :yank url, separated by commas. +# Default: +# ref,utm_source,utm_medium,utm_campaign,utm_term,utm_content +yank-ignored-url-parameters = ref,utm_source,utm_medium,utm_campaign,utm_term,utm_content + +# default-open-dispatcher (String): +# The default program used to open downloads. Set to an empty string +# to use the default internal handler. +# Any {} in the string will be expanded to the filename, else the +# filename will be appended. +# Default: +default-open-dispatcher = + +# default-page (FuzzyUrl): +# The page to open if :open -t/-b/-w is used without URL. Use +# `about:blank` for a blank page. +# Default: ${startpage} +default-page = ${startpage} + +# auto-search (AutoSearch): +# Whether to start a search when something else than a URL is +# entered. +# naive: Use simple/naive check. +# dns: Use DNS requests (might be slow!). +# false: Never search automatically. +# Default: naive +auto-search = naive + +# auto-save-config (Bool): +# Whether to save the config automatically on quit. +# Valid values: true, false +# Default: true +auto-save-config = true + +# auto-save-interval (Int): +# How often (in milliseconds) to auto-save config/cookies/etc. +# Default: 15000 +auto-save-interval = 15000 + +# editor (ShellCommand): +# The editor (and arguments) to use for the `open-editor` command. +# The arguments get split like in a shell, so you can use `"` or `'` +# to quote them. +# `{}` gets replaced by the filename of the file to be edited. +# Default: gvim -f "{}" +editor = gvim -f "{}" + +# editor-encoding (Encoding): +# Encoding to use for editor. +# Default: utf-8 +editor-encoding = utf-8 + +# private-browsing (Bool): +# Open new windows in private browsing mode which does not record +# visited pages. +# Valid values: true, false +# Default: false +private-browsing = true + +# developer-extras (Bool): +# Enable extra tools for Web developers. +# This needs to be enabled for `:inspector` to work and also adds an +# _Inspect_ entry to the context menu. For QtWebEngine, see +# 'qutebrowser --help' instead. +# Valid values: true, false +# Default: false +developer-extras = true + +# print-element-backgrounds (Bool): +# Whether the background color and images are also drawn when the +# page is printed. +# This setting only works with Qt 5.8 or newer when using the +# QtWebEngine backend. +# Valid values: true, false +# Default: true +print-element-backgrounds = true + +# xss-auditing (Bool): +# Whether load requests should be monitored for cross-site scripting +# attempts. +# Suspicious scripts will be blocked and reported in the inspector's +# JavaScript console. Enabling this feature might have an impact on +# performance. +# Valid values: true, false +# Default: false +xss-auditing = false + +# default-encoding (String): +# Default encoding to use for websites. +# The encoding must be a string describing an encoding such as +# _utf-8_, _iso-8859-1_, etc. +# Default: iso-8859-1 +default-encoding = _utf-8_ + +# new-instance-open-target (String): +# How to open links in an existing instance if a new one is +# launched. +# tab: Open a new tab in the existing window and activate the +# window. +# tab-bg: Open a new background tab in the existing window and +# activate the window. +# tab-silent: Open a new tab in the existing window without +# activating the window. +# tab-bg-silent: Open a new background tab in the existing +# window without activating the window. +# window: Open in a new window. +# Default: tab +new-instance-open-target = tab + +# new-instance-open-target.window (String): +# Which window to choose when opening links as new tabs. +# first-opened: Open new tabs in the first (oldest) opened +# window. +# last-opened: Open new tabs in the last (newest) opened window. +# last-focused: Open new tabs in the most recently focused +# window. +# last-visible: Open new tabs in the most recently visible +# window. +# Default: last-focused +new-instance-open-target.window = last-focused + +# log-javascript-console (String): +# How to log javascript console messages. +# none: Don't log messages. +# debug: Log messages with debug level. +# info: Log messages with info level. +# Default: debug +log-javascript-console = debug + +# save-session (Bool): +# Whether to always save the open pages. +# Valid values: true, false +# Default: false +save-session = true + +# session-default-name (SessionName): +# The name of the session to save by default, or empty for the last +# loaded session. +# Default: +session-default-name = + +# url-incdec-segments (FlagList): +# The URL segments where `:navigate increment/decrement` will search +# for a number. +# Valid values: host, path, query, anchor +# Default: path,query +url-incdec-segments = path,query + + +# General options related to the user interface. +[ui] + +# history-session-interval (Int): +# The maximum time in minutes between two history items for them to +# be considered being from the same session. Use -1 to disable +# separation. +# Default: 30 +history-session-interval = 30 + +# zoom-levels (List of Perc): +# The available zoom levels, separated by commas. +# Default: +# 25%,33%,50%,67%,75%,90%,100%,110%,125%,150%,175%,200%,250%,300%,400%,500% +zoom-levels = 25%,33%,50%,67%,75%,90%,100%,110%,125%,150%,175%,200%,250%,300%,400%,500% + +# default-zoom (Perc): +# The default zoom level. +# Default: 100% +default-zoom = 100% + +# downloads-position (VerticalPosition): +# Where to show the downloaded files. +# Valid values: top, bottom +# Default: top +downloads-position = top + +# status-position (VerticalPosition): +# The position of the status bar. +# Valid values: top, bottom +# Default: bottom +status-position = bottom + +# message-timeout (Int): +# Time (in ms) to show messages in the statusbar for. +# Set to 0 to never clear messages. +# Default: 2000 +message-timeout = 2000 + +# message-unfocused (Bool): +# Whether to show messages in unfocused windows. +# Valid values: true, false +# Default: false +message-unfocused = false + +# confirm-quit (ConfirmQuit): +# Whether to confirm quitting the application. +# always: Always show a confirmation. +# multiple-tabs: Show a confirmation if multiple tabs are +# opened. +# downloads: Show a confirmation if downloads are running +# never: Never show a confirmation. +# Default: never +confirm-quit = never + +# zoom-text-only (Bool): +# Whether the zoom factor on a frame applies only to the text or to +# all content. +# Valid values: true, false +# Default: false +zoom-text-only = false + +# frame-flattening (Bool): +# Whether to expand each subframe to its contents. +# This will flatten all the frames to become one scrollable page. +# Valid values: true, false +# Default: false +frame-flattening = false + +# user-stylesheet (File): +# User stylesheet to use (absolute filename or filename relative to +# the config directory). Will expand environment variables. +# Default: +user-stylesheet = + +# hide-scrollbar (Bool): +# Hide the main scrollbar. +# Valid values: true, false +# Default: true +hide-scrollbar = true + +# smooth-scrolling (Bool): +# Whether to enable smooth scrolling for web pages. Note smooth +# scrolling does not work with the :scroll-px command. +# Valid values: true, false +# Default: false +smooth-scrolling = false + +# remove-finished-downloads (Int): +# Number of milliseconds to wait before removing finished downloads. +# Will not be removed if value is -1. +# Default: -1 +remove-finished-downloads = -1 + +# hide-statusbar (Bool): +# Whether to hide the statusbar unless a message is shown. +# Valid values: true, false +# Default: false +hide-statusbar = false + +# statusbar-padding (Padding): +# Padding for statusbar (top, bottom, left, right). +# Default: 1,1,0,0 +statusbar-padding = 1,1,0,0 + +# window-title-format (FormatString): +# The format to use for the window title. The following placeholders +# are defined: +# * `{perc}`: The percentage as a string like `[10%]`. +# * `{perc_raw}`: The raw percentage, e.g. `10` +# * `{title}`: The title of the current web page +# * `{title_sep}`: The string ` - ` if a title is set, empty +# otherwise. +# * `{id}`: The internal window ID of this window. +# * `{scroll_pos}`: The page scroll position. +# * `{host}`: The host of the current web page. +# * `{backend}`: Either 'webkit' or 'webengine' +# * `{private}` : Indicates when private mode is enabled. +# Default: {perc}{title}{title_sep}qutebrowser +window-title-format = {perc}{title}{title_sep}qutebrowser + +# modal-js-dialog (Bool): +# Use standard JavaScript modal dialog for alert() and confirm() +# Valid values: true, false +# Default: false +modal-js-dialog = false + +# hide-wayland-decoration (Bool): +# Hide the window decoration when using wayland (requires restart) +# Valid values: true, false +# Default: false +hide-wayland-decoration = false + +# keyhint-blacklist (List of String): +# Keychains that shouldn't be shown in the keyhint dialog +# Globs are supported, so ';*' will blacklist all keychainsstarting +# with ';'. Use '*' to disable keyhints +# Default: +keyhint-blacklist = + +# keyhint-delay (Int): +# Time from pressing a key to seeing the keyhint dialog (ms) +# Default: 500 +keyhint-delay = 500 + +# prompt-radius (Int): +# The rounding radius for the edges of prompts. +# Default: 8 +prompt-radius = 8 + +# prompt-filebrowser (Bool): +# Show a filebrowser in upload/download prompts. +# Valid values: true, false +# Default: true +prompt-filebrowser = true + + +# Settings related to the network. +[network] + +# do-not-track (Bool): +# Value to send in the `DNT` header. +# Valid values: true, false +# Default: true +do-not-track = true + +# accept-language (String): +# Value to send in the `accept-language` header. +# Default: en-US,en +accept-language = en-US,en;q=0.5 + +# referer-header (String): +# Send the Referer header +# always: Always send. +# never: Never send; this is not recommended, as some sites may +# break. +# same-domain: Only send for the same domain. This will still +# protect your privacy, but shouldn't break any sites. +# Default: same-domain +referer-header = same-domain + +# user-agent (UserAgent): +# User agent to send. Empty to send the default. +# Default: +user-agent = Mozilla/5.0 + +# proxy (Proxy): +# The proxy to use. +# In addition to the listed values, you can use a `socks://...` or +# `http://...` URL. +# system: Use the system wide proxy. +# none: Don't use any proxy +# Default: system +proxy = system + +# proxy-dns-requests (Bool): +# Whether to send DNS requests over the configured proxy. +# Valid values: true, false +# Default: true +proxy-dns-requests = true + +# ssl-strict (BoolAsk): +# Whether to validate SSL handshakes. +# Valid values: true, false, ask +# Default: ask +ssl-strict = ask + +# dns-prefetch (Bool): +# Whether to try to pre-fetch DNS entries to speed up browsing. +# Valid values: true, false +# Default: true +dns-prefetch = true + +# custom-headers (HeaderDict): +# Set custom headers for qutebrowser HTTP requests. +# Default: +custom-headers = + +# netrc-file (File): +# Set location of a netrc-file for HTTP authentication. If empty, +# ~/.netrc is used. +# Default: +netrc-file = + + +# Options related to completion and command history. +[completion] + +# show (String): +# When to show the autocompletion window. +# always: Whenever a completion is available. +# auto: Whenever a completion is requested. +# never: Never. +# Default: always +show = always + +# download-path-suggestion (String): +# What to display in the download filename input. +# path: Show only the download path. +# filename: Show only download filename. +# both: Show download path and filename. +# Default: path +download-path-suggestion = path + +# timestamp-format (TimestampTemplate): +# How to format timestamps (e.g. for history) +# Default: %Y-%m-%d +timestamp-format = %Y-%m-%d + +# height (PercOrInt): +# The height of the completion, in px or as percentage of the +# window. +# Default: 50% +height = 50% + +# cmd-history-max-items (Int): +# How many commands to save in the command history. +# 0: no history / -1: unlimited +# Default: 100 +cmd-history-max-items = 100 + +# web-history-max-items (Int): +# How many URLs to show in the web history. +# 0: no history / -1: unlimited +# Default: 1000 +web-history-max-items = 1000 + +# quick-complete (Bool): +# Whether to move on to the next part when there's only one possible +# completion left. +# Valid values: true, false +# Default: true +quick-complete = true + +# shrink (Bool): +# Whether to shrink the completion to be smaller than the configured +# size if there are no scrollbars. +# Valid values: true, false +# Default: false +shrink = false + +# scrollbar-width (Int): +# Width of the scrollbar in the completion window (in px). +# Default: 12 +scrollbar-width = 12 + +# scrollbar-padding (Int): +# Padding of scrollbar handle in completion window (in px). +# Default: 2 +scrollbar-padding = 2 + + +# Options related to input modes. +[input] + +# timeout (Int): +# Timeout (in milliseconds) for ambiguous key bindings. +# If the current input forms both a complete match and a partial +# match, the complete match will be executed after this time. +# Default: 500 +timeout = 500 + +# partial-timeout (Int): +# Timeout (in milliseconds) for partially typed key bindings. +# If the current input forms only partial matches, the keystring +# will be cleared after this time. +# Default: 5000 +partial-timeout = 5000 + +# insert-mode-on-plugins (Bool): +# Whether to switch to insert mode when clicking flash and other +# plugins. +# Valid values: true, false +# Default: false +insert-mode-on-plugins = false + +# auto-leave-insert-mode (Bool): +# Whether to leave insert mode if a non-editable element is clicked. +# Valid values: true, false +# Default: true +auto-leave-insert-mode = true + +# auto-insert-mode (Bool): +# Whether to automatically enter insert mode if an editable element +# is focused after page load. +# Valid values: true, false +# Default: false +auto-insert-mode = false + +# forward-unbound-keys (String): +# Whether to forward unbound keys to the webview in normal mode. +# all: Forward all unbound keys. +# auto: Forward unbound non-alphanumeric keys. +# none: Don't forward any keys. +# Default: auto +forward-unbound-keys = auto + +# spatial-navigation (Bool): +# Enables or disables the Spatial Navigation feature. +# Spatial navigation consists in the ability to navigate between +# focusable elements in a Web page, such as hyperlinks and form +# controls, by using Left, Right, Up and Down arrow keys. For +# example, if a user presses the Right key, heuristics determine +# whether there is an element he might be trying to reach towards +# the right and which element he probably wants. +# Valid values: true, false +# Default: false +spatial-navigation = false + +# links-included-in-focus-chain (Bool): +# Whether hyperlinks should be included in the keyboard focus chain. +# Valid values: true, false +# Default: true +links-included-in-focus-chain = true + +# rocker-gestures (Bool): +# Whether to enable Opera-like mouse rocker gestures. This disables +# the context menu. +# Valid values: true, false +# Default: false +rocker-gestures = false + +# mouse-zoom-divider (Int): +# How much to divide the mouse wheel movements to translate them +# into zoom increments. +# Default: 512 +mouse-zoom-divider = 512 + + +# Configuration of the tab bar. +[tabs] + +# background-tabs (Bool): +# Whether to open new tabs (middleclick/ctrl+click) in background. +# Valid values: true, false +# Default: false +background-tabs = false + +# select-on-remove (SelectOnRemove): +# Which tab to select when the focused tab is removed. +# prev: Select the tab which came before the closed one (left in +# horizontal, above in vertical). +# next: Select the tab which came after the closed one (right in +# horizontal, below in vertical). +# last-used: Select the previously selected tab. +# Default: next +select-on-remove = next + +# new-tab-position (NewTabPosition): +# How new tabs are positioned. +# prev: Before the current tab. +# next: After the current tab. +# first: At the beginning. +# last: At the end. +# Default: next +new-tab-position = next + +# new-tab-position-explicit (NewTabPosition): +# How new tabs opened explicitly are positioned. +# prev: Before the current tab. +# next: After the current tab. +# first: At the beginning. +# last: At the end. +# Default: last +new-tab-position-explicit = last + +# last-close (String): +# Behavior when the last tab is closed. +# ignore: Don't do anything. +# blank: Load a blank page. +# startpage: Load the start page. +# default-page: Load the default page. +# close: Close the window. +# Default: ignore +last-close = ignore + +# show (String): +# When to show the tab bar +# always: Always show the tab bar. +# never: Always hide the tab bar. +# multiple: Hide the tab bar if only one tab is open. +# switching: Show the tab bar when switching tabs. +# Default: always +show = always + +# show-switching-delay (Int): +# Time to show the tab bar before hiding it when tabs->show is set +# to 'switching'. +# Default: 800 +show-switching-delay = 800 + +# wrap (Bool): +# Whether to wrap when changing tabs. +# Valid values: true, false +# Default: true +wrap = true + +# movable (Bool): +# Whether tabs should be movable. +# Valid values: true, false +# Default: true +movable = true + +# close-mouse-button (String): +# On which mouse button to close tabs. +# right: Close tabs on right-click. +# middle: Close tabs on middle-click. +# none: Don't close tabs using the mouse. +# Default: middle +close-mouse-button = middle + +# position (Position): +# The position of the tab bar. +# Valid values: top, bottom, left, right +# Default: top +position = top + +# show-favicons (Bool): +# Whether to show favicons in the tab bar. +# Valid values: true, false +# Default: true +show-favicons = true + +# favicon-scale (Float): +# Scale for favicons in the tab bar. The tab size is unchanged, so +# big favicons also require extra `tabs->padding`. +# Default: 1.0 +favicon-scale = 1.0 + +# width (PercOrInt): +# The width of the tab bar if it's vertical, in px or as percentage +# of the window. +# Default: 20% +width = 20% + +# pinned-width (Int): +# The width for pinned tabs with a horizontal tabbar, in px. +# Default: 43 +pinned-width = 43 + +# indicator-width (Int): +# Width of the progress indicator (0 to disable). +# Default: 3 +indicator-width = 3 + +# tabs-are-windows (Bool): +# Whether to open windows instead of tabs. +# Valid values: true, false +# Default: false +tabs-are-windows = false + +# title-format (FormatString): +# The format to use for the tab title. The following placeholders +# are defined: +# * `{perc}`: The percentage as a string like `[10%]`. +# * `{perc_raw}`: The raw percentage, e.g. `10` +# * `{title}`: The title of the current web page +# * `{title_sep}`: The string ` - ` if a title is set, empty +# otherwise. +# * `{index}`: The index of this tab. +# * `{id}`: The internal tab ID of this tab. +# * `{scroll_pos}`: The page scroll position. +# * `{host}`: The host of the current web page. +# * `{backend}`: Either 'webkit' or 'webengine' +# * `{private}` : Indicates when private mode is enabled. +# Default: {index}: {title} +title-format = {index}: {title} + +# title-format-pinned (FormatString): +# The format to use for the tab title for pinned tabs. The same +# placeholders like for title-format are defined. +# Default: {index} +title-format-pinned = {index} + +# title-alignment (TextAlignment): +# Alignment of the text inside of tabs +# Valid values: left, right, center +# Default: left +title-alignment = left + +# mousewheel-tab-switching (Bool): +# Switch between tabs using the mouse wheel. +# Valid values: true, false +# Default: true +mousewheel-tab-switching = true + +# padding (Padding): +# Padding for tabs (top, bottom, left, right). +# Default: 0,0,5,5 +padding = 0,0,5,5 + +# indicator-padding (Padding): +# Padding for indicators (top, bottom, left, right). +# Default: 2,2,0,4 +indicator-padding = 2,2,0,4 + + +# Settings related to cache and storage. +[storage] + +# download-directory (Directory): +# The directory to save downloads to. An empty value selects a +# sensible os-specific default. Will expand environment variables. +# Default: +download-directory = + +# prompt-download-directory (Bool): +# Whether to prompt the user for the download location. +# If set to false, 'download-directory' will be used. +# Valid values: true, false +# Default: true +prompt-download-directory = true + +# remember-download-directory (Bool): +# Whether to remember the last used download directory. +# Valid values: true, false +# Default: true +remember-download-directory = true + +# maximum-pages-in-cache (Int): +# The maximum number of pages to hold in the global memory page +# cache. +# The Page Cache allows for a nicer user experience when navigating +# forth or back to pages in the forward/back history, by pausing and +# resuming up to _n_ pages. +# For more information about the feature, please refer to: +# http://webkit.org/blog/427/webkit-page-cache-i-the-basics/ +# Default: 0 +maximum-pages-in-cache = 0 + +# offline-web-application-cache (Bool): +# Whether support for the HTML 5 web application cache feature is +# enabled. +# An application cache acts like an HTTP cache in some sense. For +# documents that use the application cache via JavaScript, the +# loader engine will first ask the application cache for the +# contents, before hitting the network. +# The feature is described in details at: +# http://dev.w3.org/html5/spec/Overview.html#appcache +# Valid values: true, false +# Default: true +offline-web-application-cache = true + +# local-storage (Bool): +# Whether support for HTML 5 local storage and Web SQL is enabled. +# Valid values: true, false +# Default: true +local-storage = true + +# cache-size (Int): +# Size of the HTTP network cache. Empty to use the default value. +# Default: +cache-size = + + +# Loaded plugins/scripts and allowed actions. +[content] + +# allow-images (Bool): +# Whether images are automatically loaded in web pages. +# Valid values: true, false +# Default: true +allow-images = true + +# allow-javascript (Bool): +# Enables or disables the running of JavaScript programs. +# Valid values: true, false +# Default: true +allow-javascript = true + +# allow-plugins (Bool): +# Enables or disables plugins in Web pages. +# Qt plugins with a mimetype such as "application/x-qt-plugin" are +# not affected by this setting. +# Valid values: true, false +# Default: false +allow-plugins = true + +# webgl (Bool): +# Enables or disables WebGL. +# Valid values: true, false +# Default: true +webgl = true + +# hyperlink-auditing (Bool): +# Enable or disable hyperlink auditing (<a ping>). +# Valid values: true, false +# Default: false +hyperlink-auditing = false + +# geolocation (BoolAsk): +# Allow websites to request geolocations. +# Valid values: true, false, ask +# Default: ask +geolocation = false + +# notifications (BoolAsk): +# Allow websites to show notifications. +# Valid values: true, false, ask +# Default: ask +notifications = ask + +# media-capture (BoolAsk): +# Allow websites to record audio/video. +# Valid values: true, false, ask +# Default: ask +media-capture = ask + +# javascript-can-open-windows-automatically (Bool): +# Whether JavaScript programs can open new windows without user +# interaction. +# Valid values: true, false +# Default: false +javascript-can-open-windows-automatically = true + +# javascript-can-close-windows (Bool): +# Whether JavaScript programs can close windows. +# Valid values: true, false +# Default: false +javascript-can-close-windows = true + +# javascript-can-access-clipboard (Bool): +# Whether JavaScript programs can read or write to the clipboard. +# With QtWebEngine, writing the clipboard as response to a user +# interaction is always allowed. +# Valid values: true, false +# Default: false +javascript-can-access-clipboard = true + +# ignore-javascript-prompt (Bool): +# Whether all javascript prompts should be ignored. +# Valid values: true, false +# Default: false +ignore-javascript-prompt = true + +# ignore-javascript-alert (Bool): +# Whether all javascript alerts should be ignored. +# Valid values: true, false +# Default: false +ignore-javascript-alert = true + +# local-content-can-access-remote-urls (Bool): +# Whether locally loaded documents are allowed to access remote +# urls. +# Valid values: true, false +# Default: false +local-content-can-access-remote-urls = false + +# local-content-can-access-file-urls (Bool): +# Whether locally loaded documents are allowed to access other local +# urls. +# Valid values: true, false +# Default: true +local-content-can-access-file-urls = true + +# cookies-accept (String): +# Control which cookies to accept. +# all: Accept all cookies. +# no-3rdparty: Accept cookies from the same origin only. +# no-unknown-3rdparty: Accept cookies from the same origin only, +# unless a cookie is already set for the domain. +# never: Don't accept cookies at all. +# Default: no-3rdparty +cookies-accept = all + +# cookies-store (Bool): +# Whether to store cookies. Note this option needs a restart with +# QtWebEngine on Qt < 5.9. +# Valid values: true, false +# Default: true +cookies-store = true + +# host-block-lists (List of Url): +# List of URLs of lists which contain hosts to block. +# The file can be in one of the following formats: +# - An '/etc/hosts'-like file +# - One host per line +# - A zip-file of any of the above, with either only one file, or a +# file named 'hosts' (with any extension). +# Default: +# https://www.malwaredomainlist.com/hostslist/hosts.txt,http://someonewhocares.org/hosts/hosts,http://winhelp2002.mvps.org/hosts.zip,http://malwaredomains.lehigh.edu/files/justdomains.zip,https://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&mimetype=plaintext +host-block-lists = https://www.malwaredomainlist.com/hostslist/hosts.txt,http://someonewhocares.org/hosts/hosts,http://winhelp2002.mvps.org/hosts.zip,http://malwaredomains.lehigh.edu/files/justdomains.zip,https://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&mimetype=plaintext + +# host-blocking-enabled (Bool): +# Whether host blocking is enabled. +# Valid values: true, false +# Default: true +host-blocking-enabled = false + +# host-blocking-whitelist (List of String): +# List of domains that should always be loaded, despite being +# ad-blocked. +# Domains may contain * and ? wildcards and are otherwise required +# to exactly match the requested domain. +# Local domains are always exempt from hostblocking. +# Default: piwik.org +host-blocking-whitelist = piwik.org,next-episode.net,churchmilitant.tv,load.sumome.com,s7.addthis.com,www.google-analytics.com,ssl.google-analytics.com,referrer.disqus.com,m.addthisedge.com,m.addthis.com,stats.g.doubleclick.net,*.addthis.com,lukesmith.xyz + +# enable-pdfjs (Bool): +# Enable pdf.js to view PDF files in the browser. +# Note that the files can still be downloaded by clicking the +# download button in the pdf.js viewer. +# Valid values: true, false +# Default: false +enable-pdfjs = false + + +# Hinting settings. +[hints] + +# border (String): +# CSS border value for hints. +# Default: 1px solid #E3BE23 +border = 1px solid #E3BE23 + +# mode (String): +# Mode to use for hints. +# number: Use numeric hints. (In this mode you can also type +# letters form the hinted element to filter and reduce the number of +# elements that are hinted.) +# letter: Use the chars in the hints -> chars setting. +# word: Use hints words based on the html elements and the extra +# words. +# Default: letter +mode = number + +# chars (UniqueCharString): +# Chars used for hint strings. +# Default: asdfghjkl +chars = asdfghjkl + +# min-chars (Int): +# Minimum number of chars used for hint strings. +# Default: 1 +min-chars = 1 + +# scatter (Bool): +# Whether to scatter hint key chains (like Vimium) or not (like +# dwb). Ignored for number hints. +# Valid values: true, false +# Default: true +scatter = true + +# uppercase (Bool): +# Make chars in hint strings uppercase. +# Valid values: true, false +# Default: false +uppercase = false + +# dictionary (File): +# The dictionary file to be used by the word hints. +# Default: /usr/share/dict/words +dictionary = /usr/share/dict/words + +# auto-follow (String): +# Controls when a hint can be automatically followed without the +# user pressing Enter. +# always: Auto-follow whenever there is only a single hint on a +# page. +# unique-match: Auto-follow whenever there is a unique non-empty +# match in either the hint string (word mode) or filter (number +# mode). +# full-match: Follow the hint when the user typed the whole hint +# (letter, word or number mode) or the element's text (only in +# number mode). +# never: The user will always need to press Enter to follow a +# hint. +# Default: unique-match +auto-follow = unique-match + +# auto-follow-timeout (Int): +# A timeout (in milliseconds) to inhibit normal-mode key bindings +# after a successful auto-follow. +# Default: 0 +auto-follow-timeout = 0 + +# next-regexes (List of Regex): +# A comma-separated list of regexes to use for 'next' links. +# Default: +# \bnext\b,\bmore\b,\bnewer\b,\b[>→≫]\b,\b(>>|»)\b,\bcontinue\b +next-regexes = \bnext\b,\bmore\b,\bnewer\b,\b[>→≫]\b,\b(>>|»)\b,\bcontinue\b + +# prev-regexes (List of Regex): +# A comma-separated list of regexes to use for 'prev' links. +# Default: \bprev(ious)?\b,\bback\b,\bolder\b,\b[<←≪]\b,\b(<<|«)\b +prev-regexes = \bprev(ious)?\b,\bback\b,\bolder\b,\b[<←≪]\b,\b(<<|«)\b + +# find-implementation (String): +# Which implementation to use to find elements to hint. +# javascript: Better but slower +# python: Slightly worse but faster +# Default: python +find-implementation = python + +# hide-unmatched-rapid-hints (Bool): +# Controls hiding unmatched hints in rapid mode. +# Valid values: true, false +# Default: true +hide-unmatched-rapid-hints = true + + +# Definitions of search engines which can be used via the address bar. +# The searchengine named `DEFAULT` is used when `general -> auto-search` +# is true and something else than a URL was entered to be opened. Other +# search engines can be used by prepending the search engine name to the +# search term, e.g. `:open google qutebrowser`. The string `{}` will be +# replaced by the search term, use `{{` and `}}` for literal `{`/`}` +# signs. +[searchengines] +DEFAULT = https://duckduckgo.com/?q={} + + +# Aliases for commands. +# By default, no aliases are defined. Example which adds a new command +# `:qtb` to open qutebrowsers website: +# `qtb = open https://www.qutebrowser.org/` +[aliases] +qtb = open https://www.qutebrowser.org/ +mbt = open t https://signin1.bt.com/login/emailloginform +Ombt = open -t https://signin1.bt.com/login/emailloginform +nx = open http://next-episode.net/ +Onx = open -t http://next-episode.net/ +gk = open https://gameknot.com/ +Ogk = open -t https://gameknot.com/ +yt = open https://youtube.com +Oyt = open -t https://youtube.com +Y = open http://www.cosmos.esa.int/web/xmm-newton/current-calibration-files +OY = open -t http://www.cosmos.esa.int/web/xmm-newton/current-calibration-files +1337 = open https://1337x.to/home +O1337 = open -t https://1337x.to/home +ez = open https://eztv.ag +Oez = open -t https://eztv.ag +tz = open https://extratorrent.ag +Otz = open -t https://extratorrent.ag +tpb = open https://thepiratebay.org/ +Otpb = open -t https://thepiratebay.org/ +Nm = open https://netmail.herts.ac.uk/owa/auth/logon.aspx?replaceCurrent=1&url=https%3a%2f%2fnetmail.herts.ac.uk%2fowa +ONm = open -t https://netmail.herts.ac.uk/owa/auth/logon.aspx?replaceCurrent=1&url=https%3a%2f%2fnetmail.herts.ac.uk%2fowa + + +# Colors used in the UI. +# A value can be in one of the following format: +# * `#RGB`/`#RRGGBB`/`#RRRGGGBBB`/`#RRRRGGGGBBBB` +# * An SVG color name as specified in http://www.w3.org/TR/SVG/types.html#ColorKeywords[the W3C specification]. +# * transparent (no color) +# * `rgb(r, g, b)` / `rgba(r, g, b, a)` (values 0-255 or percentages) +# * `hsv(h, s, v)` / `hsva(h, s, v, a)` (values 0-255, hue 0-359) +# * A gradient as explained in http://doc.qt.io/qt-5/stylesheet-reference.html#list-of-property-types[the Qt documentation] under ``Gradient''. +# A *.system value determines the color system to use for color +# interpolation between similarly-named *.start and *.stop entries, +# regardless of how they are defined in the options. Valid values are +# 'rgb', 'hsv', and 'hsl'. +# The `hints.*` values are a special case as they're real CSS colors, not Qt-CSS colors. There, for a gradient, you need to use `-webkit-gradient`, see https://www.webkit.org/blog/175/introducing-css-gradients/[the WebKit documentation]. +[colors] + +# completion.fg (QtColor): +# Text color of the completion widget. +# Default: white +completion.fg = white + +# completion.bg (QssColor): +# Background color of the completion widget. +# Default: #333333 +completion.bg = #333333 + +# completion.alternate-bg (QssColor): +# Alternating background color of the completion widget. +# Default: #444444 +completion.alternate-bg = #444444 + +# completion.category.fg (QtColor): +# Foreground color of completion widget category headers. +# Default: white +completion.category.fg = white + +# completion.category.bg (QssColor): +# Background color of the completion widget category headers. +# Default: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #888888, +# stop:1 #505050) +completion.category.bg = qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #888888, stop:1 #505050) + +# completion.category.border.top (QssColor): +# Top border color of the completion widget category headers. +# Default: black +completion.category.border.top = black + +# completion.category.border.bottom (QssColor): +# Bottom border color of the completion widget category headers. +# Default: ${completion.category.border.top} +completion.category.border.bottom = ${completion.category.border.top} + +# completion.item.selected.fg (QtColor): +# Foreground color of the selected completion item. +# Default: black +completion.item.selected.fg = black + +# completion.item.selected.bg (QssColor): +# Background color of the selected completion item. +# Default: #e8c000 +completion.item.selected.bg = #e8c000 + +# completion.item.selected.border.top (QssColor): +# Top border color of the completion widget category headers. +# Default: #bbbb00 +completion.item.selected.border.top = #bbbb00 + +# completion.item.selected.border.bottom (QssColor): +# Bottom border color of the selected completion item. +# Default: ${completion.item.selected.border.top} +completion.item.selected.border.bottom = ${completion.item.selected.border.top} + +# completion.match.fg (QssColor): +# Foreground color of the matched text in the completion. +# Default: #ff4444 +completion.match.fg = #ff4444 + +# completion.scrollbar.fg (QssColor): +# Color of the scrollbar handle in completion view. +# Default: ${completion.fg} +completion.scrollbar.fg = ${completion.fg} + +# completion.scrollbar.bg (QssColor): +# Color of the scrollbar in completion view +# Default: ${completion.bg} +completion.scrollbar.bg = ${completion.bg} + +# statusbar.fg (QssColor): +# Foreground color of the statusbar. +# Default: white +statusbar.fg = white + +# statusbar.bg (QssColor): +# Background color of the statusbar. +# Default: black +statusbar.bg = black + +# statusbar.fg.private (QssColor): +# Foreground color of the statusbar in private browsing mode. +# Default: ${statusbar.fg} +statusbar.fg.private = ${statusbar.fg} + +# statusbar.bg.private (QssColor): +# Background color of the statusbar in private browsing mode. +# Default: #666666 +statusbar.bg.private = #666666 + +# statusbar.fg.insert (QssColor): +# Foreground color of the statusbar in insert mode. +# Default: ${statusbar.fg} +statusbar.fg.insert = ${statusbar.fg} + +# statusbar.bg.insert (QssColor): +# Background color of the statusbar in insert mode. +# Default: darkgreen +statusbar.bg.insert = darkgreen + +# statusbar.fg.command (QssColor): +# Foreground color of the statusbar in command mode. +# Default: ${statusbar.fg} +statusbar.fg.command = ${statusbar.fg} + +# statusbar.bg.command (QssColor): +# Background color of the statusbar in command mode. +# Default: ${statusbar.bg} +statusbar.bg.command = ${statusbar.bg} + +# statusbar.fg.command.private (QssColor): +# Foreground color of the statusbar in private browsing + command +# mode. +# Default: ${statusbar.fg.private} +statusbar.fg.command.private = ${statusbar.fg.private} + +# statusbar.bg.command.private (QssColor): +# Background color of the statusbar in private browsing + command +# mode. +# Default: ${statusbar.bg.private} +statusbar.bg.command.private = ${statusbar.bg.private} + +# statusbar.fg.caret (QssColor): +# Foreground color of the statusbar in caret mode. +# Default: ${statusbar.fg} +statusbar.fg.caret = ${statusbar.fg} + +# statusbar.bg.caret (QssColor): +# Background color of the statusbar in caret mode. +# Default: purple +statusbar.bg.caret = purple + +# statusbar.fg.caret-selection (QssColor): +# Foreground color of the statusbar in caret mode with a selection +# Default: ${statusbar.fg} +statusbar.fg.caret-selection = ${statusbar.fg} + +# statusbar.bg.caret-selection (QssColor): +# Background color of the statusbar in caret mode with a selection +# Default: #a12dff +statusbar.bg.caret-selection = #a12dff + +# statusbar.progress.bg (QssColor): +# Background color of the progress bar. +# Default: white +statusbar.progress.bg = white + +# statusbar.url.fg (QssColor): +# Default foreground color of the URL in the statusbar. +# Default: ${statusbar.fg} +statusbar.url.fg = ${statusbar.fg} + +# statusbar.url.fg.success (QssColor): +# Foreground color of the URL in the statusbar on successful load +# (http). +# Default: white +statusbar.url.fg.success = white + +# statusbar.url.fg.success.https (QssColor): +# Foreground color of the URL in the statusbar on successful load +# (https). +# Default: lime +statusbar.url.fg.success.https = lime + +# statusbar.url.fg.error (QssColor): +# Foreground color of the URL in the statusbar on error. +# Default: orange +statusbar.url.fg.error = orange + +# statusbar.url.fg.warn (QssColor): +# Foreground color of the URL in the statusbar when there's a +# warning. +# Default: yellow +statusbar.url.fg.warn = yellow + +# statusbar.url.fg.hover (QssColor): +# Foreground color of the URL in the statusbar for hovered links. +# Default: aqua +statusbar.url.fg.hover = aqua + +# tabs.fg.odd (QtColor): +# Foreground color of unselected odd tabs. +# Default: white +tabs.fg.odd = white + +# tabs.bg.odd (QtColor): +# Background color of unselected odd tabs. +# Default: grey +tabs.bg.odd = grey + +# tabs.fg.even (QtColor): +# Foreground color of unselected even tabs. +# Default: white +tabs.fg.even = white + +# tabs.bg.even (QtColor): +# Background color of unselected even tabs. +# Default: darkgrey +tabs.bg.even = darkgrey + +# tabs.fg.selected.odd (QtColor): +# Foreground color of selected odd tabs. +# Default: white +tabs.fg.selected.odd = white + +# tabs.bg.selected.odd (QtColor): +# Background color of selected odd tabs. +# Default: black +tabs.bg.selected.odd = black + +# tabs.fg.selected.even (QtColor): +# Foreground color of selected even tabs. +# Default: ${tabs.fg.selected.odd} +tabs.fg.selected.even = ${tabs.fg.selected.odd} + +# tabs.bg.selected.even (QtColor): +# Background color of selected even tabs. +# Default: ${tabs.bg.selected.odd} +tabs.bg.selected.even = ${tabs.bg.selected.odd} + +# tabs.bg.bar (QtColor): +# Background color of the tab bar. +# Default: #555555 +tabs.bg.bar = #555555 + +# tabs.indicator.start (QtColor): +# Color gradient start for the tab indicator. +# Default: #0000aa +tabs.indicator.start = #0000aa + +# tabs.indicator.stop (QtColor): +# Color gradient end for the tab indicator. +# Default: #00aa00 +tabs.indicator.stop = #00aa00 + +# tabs.indicator.error (QtColor): +# Color for the tab indicator on errors.. +# Default: #ff0000 +tabs.indicator.error = #ff0000 + +# tabs.indicator.system (ColorSystem): +# Color gradient interpolation system for the tab indicator. +# rgb: Interpolate in the RGB color system. +# hsv: Interpolate in the HSV color system. +# hsl: Interpolate in the HSL color system. +# none: Don't show a gradient. +# Default: rgb +tabs.indicator.system = rgb + +# hints.fg (QssColor): +# Font color for hints. +# Default: black +hints.fg = black + +# hints.bg (QssColor): +# Background color for hints. Note that you can use a `rgba(...)` +# value for transparency. +# Default: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgba(255, +# 247, 133, 0.8), stop:1 rgba(255, 197, 66, 0.8)) +hints.bg = qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgba(255, 247, 133, 0.8), stop:1 rgba(255, 197, 66, 0.8)) + +# hints.fg.match (QssColor): +# Font color for the matched part of hints. +# Default: green +hints.fg.match = green + +# downloads.bg.bar (QssColor): +# Background color for the download bar. +# Default: black +downloads.bg.bar = black + +# downloads.fg.start (QtColor): +# Color gradient start for download text. +# Default: white +downloads.fg.start = white + +# downloads.bg.start (QtColor): +# Color gradient start for download backgrounds. +# Default: #0000aa +downloads.bg.start = #0000aa + +# downloads.fg.stop (QtColor): +# Color gradient end for download text. +# Default: ${downloads.fg.start} +downloads.fg.stop = ${downloads.fg.start} + +# downloads.bg.stop (QtColor): +# Color gradient stop for download backgrounds. +# Default: #00aa00 +downloads.bg.stop = #00aa00 + +# downloads.fg.system (ColorSystem): +# Color gradient interpolation system for download text. +# rgb: Interpolate in the RGB color system. +# hsv: Interpolate in the HSV color system. +# hsl: Interpolate in the HSL color system. +# none: Don't show a gradient. +# Default: rgb +downloads.fg.system = rgb + +# downloads.bg.system (ColorSystem): +# Color gradient interpolation system for download backgrounds. +# rgb: Interpolate in the RGB color system. +# hsv: Interpolate in the HSV color system. +# hsl: Interpolate in the HSL color system. +# none: Don't show a gradient. +# Default: rgb +downloads.bg.system = rgb + +# downloads.fg.error (QtColor): +# Foreground color for downloads with errors. +# Default: white +downloads.fg.error = white + +# downloads.bg.error (QtColor): +# Background color for downloads with errors. +# Default: red +downloads.bg.error = red + +# webpage.bg (QtColor): +# Background color for webpages if unset (or empty to use the +# theme's color) +# Default: white +webpage.bg = white + +# keyhint.fg (QssColor): +# Text color for the keyhint widget. +# Default: #FFFFFF +keyhint.fg = #FFFFFF + +# keyhint.fg.suffix (CssColor): +# Highlight color for keys to complete the current keychain +# Default: #FFFF00 +keyhint.fg.suffix = #FFFF00 + +# keyhint.bg (QssColor): +# Background color of the keyhint widget. +# Default: rgba(0, 0, 0, 80%) +keyhint.bg = rgba(0, 0, 0, 80%) + +# messages.fg.error (QssColor): +# Foreground color of an error message. +# Default: white +messages.fg.error = white + +# messages.bg.error (QssColor): +# Background color of an error message. +# Default: red +messages.bg.error = red + +# messages.border.error (QssColor): +# Border color of an error message. +# Default: #bb0000 +messages.border.error = #bb0000 + +# messages.fg.warning (QssColor): +# Foreground color a warning message. +# Default: white +messages.fg.warning = white + +# messages.bg.warning (QssColor): +# Background color of a warning message. +# Default: darkorange +messages.bg.warning = darkorange + +# messages.border.warning (QssColor): +# Border color of an error message. +# Default: #d47300 +messages.border.warning = #d47300 + +# messages.fg.info (QssColor): +# Foreground color an info message. +# Default: white +messages.fg.info = white + +# messages.bg.info (QssColor): +# Background color of an info message. +# Default: black +messages.bg.info = black + +# messages.border.info (QssColor): +# Border color of an info message. +# Default: #333333 +messages.border.info = #333333 + +# prompts.fg (QssColor): +# Foreground color for prompts. +# Default: white +prompts.fg = white + +# prompts.bg (QssColor): +# Background color for prompts. +# Default: darkblue +prompts.bg = darkblue + +# prompts.selected.bg (QssColor): +# Background color for the selected item in filename prompts. +# Default: #308cc6 +prompts.selected.bg = #308cc6 + + +# Fonts used for the UI, with optional style/weight/size. +# * Style: `normal`/`italic`/`oblique` +# * Weight: `normal`, `bold`, `100`..`900` +# * Size: _number_ `px`/`pt` +[fonts] + +# _monospace (Font): +# Default monospace fonts. +# Default: xos4 Terminus, Terminus, Monospace, "DejaVu Sans Mono", +# Monaco, "Bitstream Vera Sans Mono", "Andale Mono", "Courier New", +# Courier, "Liberation Mono", monospace, Fixed, Consolas, Terminal +_monospace = Terminus, Monospace, "DejaVu Sans Mono", Monaco, "Bitstream Vera Sans Mono", "Andale Mono", "Courier New", Courier, "Liberation Mono", monospace, Fixed, Consolas, Terminal + +# completion (Font): +# Font used in the completion widget. +# Default: 8pt ${_monospace} +completion = 8pt ${_monospace} + +# completion.category (Font): +# Font used in the completion categories. +# Default: bold ${completion} +completion.category = bold ${completion} + +# tabbar (QtFont): +# Font used in the tab bar. +# Default: 8pt ${_monospace} +tabbar = 8pt ${_monospace} + +# statusbar (Font): +# Font used in the statusbar. +# Default: 8pt ${_monospace} +statusbar = 8pt ${_monospace} + +# downloads (Font): +# Font used for the downloadbar. +# Default: 8pt ${_monospace} +downloads = 8pt ${_monospace} + +# hints (Font): +# Font used for the hints. +# Default: bold 13px ${_monospace} +hints = bold 13px ${_monospace} + +# debug-console (QtFont): +# Font used for the debugging console. +# Default: 8pt ${_monospace} +debug-console = 8pt ${_monospace} + +# web-family-standard (FontFamily): +# Font family for standard fonts. +# Default: +web-family-standard = + +# web-family-fixed (FontFamily): +# Font family for fixed fonts. +# Default: +web-family-fixed = + +# web-family-serif (FontFamily): +# Font family for serif fonts. +# Default: +web-family-serif = + +# web-family-sans-serif (FontFamily): +# Font family for sans-serif fonts. +# Default: +web-family-sans-serif = + +# web-family-cursive (FontFamily): +# Font family for cursive fonts. +# Default: +web-family-cursive = + +# web-family-fantasy (FontFamily): +# Font family for fantasy fonts. +# Default: +web-family-fantasy = + +# web-size-minimum (Int): +# The hard minimum font size. +# Default: 0 +web-size-minimum = 0 + +# web-size-minimum-logical (Int): +# The minimum logical font size that is applied when zooming out. +# Default: 6 +web-size-minimum-logical = 6 + +# web-size-default (Int): +# The default font size for regular text. +# Default: 16 +web-size-default = 16 + +# web-size-default-fixed (Int): +# The default font size for fixed-pitch text. +# Default: 13 +web-size-default-fixed = 13 + +# keyhint (Font): +# Font used in the keyhint widget. +# Default: 8pt ${_monospace} +keyhint = 8pt ${_monospace} + +# messages.error (Font): +# Font used for error messages. +# Default: 8pt ${_monospace} +messages.error = 8pt ${_monospace} + +# messages.warning (Font): +# Font used for warning messages. +# Default: 8pt ${_monospace} +messages.warning = 8pt ${_monospace} + +# messages.info (Font): +# Font used for info messages. +# Default: 8pt ${_monospace} +messages.info = 8pt ${_monospace} + +# prompts (Font): +# Font used for prompts. +# Default: 8pt sans-serif +prompts = 8pt sans-serif diff --git a/.config/qutebrowser/scripts/__init__.py b/.config/qutebrowser/scripts/__init__.py new file mode 100755 index 0000000..90be1e0 --- /dev/null +++ b/.config/qutebrowser/scripts/__init__.py @@ -0,0 +1,3 @@ +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +"""Various utility scripts.""" diff --git a/.config/qutebrowser/scripts/asciidoc2html.py b/.config/qutebrowser/scripts/asciidoc2html.py new file mode 100755 index 0000000..c4af174 --- /dev/null +++ b/.config/qutebrowser/scripts/asciidoc2html.py @@ -0,0 +1,303 @@ +#!/usr/bin/env python3 +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2014-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org> + +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. + +"""Generate the html documentation based on the asciidoc files.""" + +import re +import os +import os.path +import sys +import subprocess +import glob +import shutil +import tempfile +import argparse +import io + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir)) + +from scripts import utils + + +class AsciiDoc: + + """Abstraction of an asciidoc subprocess.""" + + FILES = ['faq', 'changelog', 'contributing', 'quickstart', 'userscripts'] + + def __init__(self, args): + self._cmd = None + self._args = args + self._homedir = None + self._themedir = None + self._tempdir = None + self._failed = False + + def prepare(self): + """Get the asciidoc command and create the homedir to use.""" + self._cmd = self._get_asciidoc_cmd() + self._homedir = tempfile.mkdtemp() + self._themedir = os.path.join( + self._homedir, '.asciidoc', 'themes', 'qute') + self._tempdir = os.path.join(self._homedir, 'tmp') + os.makedirs(self._tempdir) + os.makedirs(self._themedir) + + def cleanup(self): + """Clean up the temporary home directory for asciidoc.""" + if self._homedir is not None and not self._failed: + shutil.rmtree(self._homedir) + + def build(self): + """Build either the website or the docs.""" + if self._args.website: + self._build_website() + else: + self._build_docs() + self._copy_images() + + def _build_docs(self): + """Render .asciidoc files to .html sites.""" + files = [('doc/{}.asciidoc'.format(f), + 'qutebrowser/html/doc/{}.html'.format(f)) + for f in self.FILES] + for src in glob.glob('doc/help/*.asciidoc'): + name, _ext = os.path.splitext(os.path.basename(src)) + dst = 'qutebrowser/html/doc/{}.html'.format(name) + files.append((src, dst)) + + # patch image links to use local copy + replacements = [ + ("https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/cheatsheet-big.png", + "qute://help/img/cheatsheet-big.png"), + ("https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/cheatsheet-small.png", + "qute://help/img/cheatsheet-small.png") + ] + asciidoc_args = ['-a', 'source-highlighter=pygments'] + + for src, dst in files: + src_basename = os.path.basename(src) + modified_src = os.path.join(self._tempdir, src_basename) + with open(modified_src, 'w', encoding='utf-8') as modified_f, \ + open(src, 'r', encoding='utf-8') as f: + for line in f: + for orig, repl in replacements: + line = line.replace(orig, repl) + modified_f.write(line) + self.call(modified_src, dst, *asciidoc_args) + + def _copy_images(self): + """Copy image files to qutebrowser/html/doc.""" + print("Copying files...") + dst_path = os.path.join('qutebrowser', 'html', 'doc', 'img') + try: + os.mkdir(dst_path) + except FileExistsError: + pass + for filename in ['cheatsheet-big.png', 'cheatsheet-small.png']: + src = os.path.join('doc', 'img', filename) + dst = os.path.join(dst_path, filename) + shutil.copy(src, dst) + + def _build_website_file(self, root, filename): + """Build a single website file.""" + src = os.path.join(root, filename) + src_basename = os.path.basename(src) + parts = [self._args.website[0]] + dirname = os.path.dirname(src) + if dirname: + parts.append(os.path.relpath(os.path.dirname(src))) + parts.append( + os.extsep.join((os.path.splitext(src_basename)[0], + 'html'))) + dst = os.path.join(*parts) + os.makedirs(os.path.dirname(dst), exist_ok=True) + + modified_src = os.path.join(self._tempdir, src_basename) + shutil.copy('www/header.asciidoc', modified_src) + + outfp = io.StringIO() + + with open(modified_src, 'r', encoding='utf-8') as header_file: + header = header_file.read() + header += "\n\n" + + with open(src, 'r', encoding='utf-8') as infp: + outfp.write("\n\n") + hidden = False + found_title = False + title = "" + last_line = "" + + for line in infp: + line = line.rstrip() + if line == '// QUTE_WEB_HIDE': + assert not hidden + hidden = True + elif line == '// QUTE_WEB_HIDE_END': + assert hidden + hidden = False + elif line == "The Compiler <mail@qutebrowser.org>": + continue + elif re.fullmatch(r':\w+:.*', line): + # asciidoc field + continue + + if not found_title: + if re.fullmatch(r'=+', line): + line = line.replace('=', '-') + found_title = True + title = last_line + " | qutebrowser\n" + title += "=" * (len(title) - 1) + elif re.fullmatch(r'= .+', line): + line = '==' + line[1:] + found_title = True + title = last_line + " | qutebrowser\n" + title += "=" * (len(title) - 1) + + if not hidden: + outfp.write(line.replace(".asciidoc[", ".html[") + '\n') + last_line = line + + current_lines = outfp.getvalue() + outfp.close() + + with open(modified_src, 'w+', encoding='utf-8') as final_version: + final_version.write(title + "\n\n" + header + current_lines) + + asciidoc_args = ['--theme=qute', '-a toc', '-a toc-placement=manual', + '-a', 'source-highlighter=pygments'] + self.call(modified_src, dst, *asciidoc_args) + + def _build_website(self): + """Prepare and build the website.""" + theme_file = os.path.abspath(os.path.join('www', 'qute.css')) + shutil.copy(theme_file, self._themedir) + + outdir = self._args.website[0] + + for root, _dirs, files in os.walk(os.getcwd()): + for filename in files: + basename, ext = os.path.splitext(filename) + if (ext != '.asciidoc' or + basename in ['header', 'OpenSans-License']): + continue + self._build_website_file(root, filename) + + copy = {'icons': 'icons', 'doc/img': 'doc/img', 'www/media': 'media/'} + + for src, dest in copy.items(): + full_dest = os.path.join(outdir, dest) + try: + shutil.rmtree(full_dest) + except FileNotFoundError: + pass + shutil.copytree(src, full_dest) + + for dst, link_name in [ + ('README.html', 'index.html'), + (os.path.join('doc', 'quickstart.html'), 'quickstart.html')]: + try: + os.symlink(dst, os.path.join(outdir, link_name)) + except FileExistsError: + pass + + def _get_asciidoc_cmd(self): + """Try to find out what commandline to use to invoke asciidoc.""" + if self._args.asciidoc is not None: + return self._args.asciidoc + + try: + subprocess.run(['asciidoc'], stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL) + except OSError: + pass + else: + return ['asciidoc'] + + try: + subprocess.run(['asciidoc.py'], stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL) + except OSError: + pass + else: + return ['asciidoc.py'] + + raise FileNotFoundError + + def call(self, src, dst, *args): + """Call asciidoc for the given files. + + Args: + src: The source .asciidoc file. + dst: The destination .html file, or None to auto-guess. + *args: Additional arguments passed to asciidoc. + """ + print("Calling asciidoc for {}...".format(os.path.basename(src))) + cmdline = self._cmd[:] + if dst is not None: + cmdline += ['--out-file', dst] + cmdline += args + cmdline.append(src) + try: + env = os.environ.copy() + env['HOME'] = self._homedir + subprocess.run(cmdline, check=True, env=env) + except (subprocess.CalledProcessError, OSError) as e: + self._failed = True + utils.print_col(str(e), 'red') + print("Keeping modified sources in {}.".format(self._homedir)) + sys.exit(1) + + +def main(colors=False): + """Generate html files for the online documentation.""" + utils.change_cwd() + utils.use_color = colors + parser = argparse.ArgumentParser() + parser.add_argument('--website', help="Build website into a given " + "directory.", nargs=1) + parser.add_argument('--asciidoc', help="Full path to python and " + "asciidoc.py. If not given, it's searched in PATH.", + nargs=2, required=False, + metavar=('PYTHON', 'ASCIIDOC')) + args = parser.parse_args() + try: + os.mkdir('qutebrowser/html/doc') + except FileExistsError: + pass + + asciidoc = AsciiDoc(args) + try: + asciidoc.prepare() + except FileNotFoundError: + utils.print_col("Could not find asciidoc! Please install it, or use " + "the --asciidoc argument to point this script to the " + "correct python/asciidoc.py location!", 'red') + sys.exit(1) + + try: + asciidoc.build() + finally: + asciidoc.cleanup() + + +if __name__ == '__main__': + main(colors=True) diff --git a/.config/qutebrowser/scripts/cycle-inputs.js b/.config/qutebrowser/scripts/cycle-inputs.js new file mode 100644 index 0000000..bb667bd --- /dev/null +++ b/.config/qutebrowser/scripts/cycle-inputs.js @@ -0,0 +1,46 @@ +/* Cycle <input> text boxes. + * works with the types defined in 'types'. + * Note: Does not work for <textarea>. + * + * Example keybind: + * CYCLE_INPUTS = "jseval -q -f ~/.config/qutebrowser/cycle-inputs.js" + * config.bind('gi', CYCLE_INPUTS) + * + * By dive on freenode <dave@dawoodfall.net> + */ + +(function() { + "use strict"; + const inputs = document.getElementsByTagName("input"); + const types = /text|password|date|email|month|number|range|search|tel|time|url|week/; + const hidden = /hidden/; + let found = false; + + function ishidden(el) { + return hidden.test(el.attributes.value) || el.offsetParent === null; + } + + for (let i = 0; i < inputs.length; i++) { + if (inputs[i] === document.activeElement) { + for (let k = i + 1; k < inputs.length; k++) { + if (!ishidden(inputs[k]) && types.test(inputs[k].type)) { + inputs[k].focus(); + found = true; + break; + } + } + break; + } + } + + if (!found) { + for (let i = 0; i < inputs.length; i++) { + if (!ishidden(inputs[i]) && types.test(inputs[i].type)) { + inputs[i].focus(); + break; + } + } + } +})(); + +// vim: tw=0 expandtab tabstop=4 softtabstop=4 shiftwidth=4 diff --git a/.config/qutebrowser/scripts/dev/Makefile-dmg b/.config/qutebrowser/scripts/dev/Makefile-dmg new file mode 100644 index 0000000..1cf4cfb --- /dev/null +++ b/.config/qutebrowser/scripts/dev/Makefile-dmg @@ -0,0 +1,71 @@ +# +# Build file for creating DMG files. +# +# The DMG packager looks for a template.dmg.bz2 for using as its +# DMG template. If it doesn't find one, it generates a clean one. +# +# If you create a DMG template, you should make one containing all +# the files listed in $(SOURCE_FILES) below, and arrange everything to suit +# your style. The contents of the files themselves does not matter, so +# they can be empty (they will be overwritten later). +# +# Remko Tronçon +# https://el-tramo.be +# Licensed under the MIT License. See COPYING for details. + + +################################################################################ +# Customizable variables +################################################################################ + +NAME ?= qutebrowser + +SOURCE_DIR ?= . +SOURCE_FILES ?= dist/qutebrowser.app LICENSE + +TEMPLATE_DMG ?= template.dmg +TEMPLATE_SIZE ?= 300m + +################################################################################ +# DMG building. No editing should be needed beyond this point. +################################################################################ + +MASTER_DMG=$(NAME).dmg +WC_DMG=wc.dmg +WC_DIR=wc + +.PHONY: all +all: $(MASTER_DMG) + +$(TEMPLATE_DMG): + @echo + @echo --------------------- Generating empty template -------------------- + mkdir template + hdiutil create -fs HFSX -layout SPUD -size $(TEMPLATE_SIZE) "$(TEMPLATE_DMG)" -srcfolder template -format UDRW -volname "$(NAME)" -quiet + rmdir template + +$(WC_DMG): $(TEMPLATE_DMG) + cp $< $@ + +$(MASTER_DMG): $(WC_DMG) $(addprefix $(SOURCE_DIR)/,$(SOURCE_FILES)) + @echo + @echo --------------------- Creating Disk Image -------------------- + mkdir -p $(WC_DIR) + hdiutil attach "$(WC_DMG)" -noautoopen -quiet -mountpoint "$(WC_DIR)" + for i in $(SOURCE_FILES); do \ + rm -rf "$(WC_DIR)/$$i"; \ + ditto -rsrc "$(SOURCE_DIR)/$$i" "$(WC_DIR)/$${i##*/}"; \ + done + ln -s /Applications $(WC_DIR) + #rm -f "$@" + #hdiutil create -srcfolder "$(WC_DIR)" -format UDZO -imagekey zlib-level=9 "$@" -volname "$(NAME) $(VERSION)" -scrub -quiet + WC_DEV=`hdiutil info | grep "$(WC_DIR)" | grep "Apple_HFS" | awk '{print $$1}'` && \ + hdiutil detach $$WC_DEV -quiet -force + rm -f "$(MASTER_DMG)" + hdiutil convert "$(WC_DMG)" -quiet -format UDZO -imagekey zlib-level=9 -o "$@" + rm -rf $(WC_DIR) + @echo + +.PHONY: clean +clean: + -rm -rf $(TEMPLATE_DMG) $(MASTER_DMG) $(WC_DMG) diff --git a/.config/qutebrowser/scripts/dev/__init__.py b/.config/qutebrowser/scripts/dev/__init__.py new file mode 100644 index 0000000..7dc0433 --- /dev/null +++ b/.config/qutebrowser/scripts/dev/__init__.py @@ -0,0 +1,3 @@ +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +"""Various scripts used for developing qutebrowser.""" diff --git a/.config/qutebrowser/scripts/dev/build_release.py b/.config/qutebrowser/scripts/dev/build_release.py new file mode 100755 index 0000000..254132b --- /dev/null +++ b/.config/qutebrowser/scripts/dev/build_release.py @@ -0,0 +1,419 @@ +#!/usr/bin/env python3 +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2014-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org> +# +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. + +"""Build a new release.""" + + +import os +import os.path +import sys +import time +import glob +import shutil +import plistlib +import subprocess +import argparse +import tarfile +import tempfile +import collections + +try: + import winreg +except ImportError: + pass + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir, + os.pardir)) + +import qutebrowser +from scripts import utils +# from scripts.dev import update_3rdparty + + +def call_script(name, *args, python=sys.executable): + """Call a given shell script. + + Args: + name: The script to call. + *args: The arguments to pass. + python: The python interpreter to use. + """ + path = os.path.join(os.path.dirname(__file__), os.pardir, name) + subprocess.run([python, path] + list(args), check=True) + + +def call_tox(toxenv, *args, python=sys.executable): + """Call tox. + + Args: + toxenv: Which tox environment to use + *args: The arguments to pass. + python: The python interpreter to use. + """ + env = os.environ.copy() + env['PYTHON'] = python + env['PATH'] = os.environ['PATH'] + os.pathsep + os.path.dirname(python) + subprocess.run( + [sys.executable, '-m', 'tox', '-vv', '-e', toxenv] + list(args), + env=env, check=True) + + +def run_asciidoc2html(args): + """Common buildsteps used for all OS'.""" + utils.print_title("Running asciidoc2html.py") + if args.asciidoc is not None: + a2h_args = ['--asciidoc'] + args.asciidoc + else: + a2h_args = [] + call_script('asciidoc2html.py', *a2h_args) + + +def _maybe_remove(path): + """Remove a path if it exists.""" + try: + shutil.rmtree(path) + except FileNotFoundError: + pass + + +def smoke_test(executable): + """Try starting the given qutebrowser executable.""" + subprocess.run([executable, '--no-err-windows', '--nowindow', + '--temp-basedir', 'about:blank', ':later 500 quit'], + check=True) + + +def patch_mac_app(): + """Patch .app to copy missing data and link some libs. + + See https://github.com/pyinstaller/pyinstaller/issues/2276 + """ + app_path = os.path.join('dist', 'qutebrowser.app') + qtwe_core_dir = os.path.join('.tox', 'pyinstaller', 'lib', 'python3.6', + 'site-packages', 'PyQt5', 'Qt', 'lib', + 'QtWebEngineCore.framework') + # Copy QtWebEngineProcess.app + proc_app = 'QtWebEngineProcess.app' + shutil.copytree(os.path.join(qtwe_core_dir, 'Helpers', proc_app), + os.path.join(app_path, 'Contents', 'MacOS', proc_app)) + # Copy resources + for f in glob.glob(os.path.join(qtwe_core_dir, 'Resources', '*')): + dest = os.path.join(app_path, 'Contents', 'Resources') + if os.path.isdir(f): + dir_dest = os.path.join(dest, os.path.basename(f)) + print("Copying directory {} to {}".format(f, dir_dest)) + shutil.copytree(f, dir_dest) + else: + print("Copying {} to {}".format(f, dest)) + shutil.copy(f, dest) + # Link dependencies + for lib in ['QtCore', 'QtWebEngineCore', 'QtQuick', 'QtQml', 'QtNetwork', + 'QtGui', 'QtWebChannel', 'QtPositioning']: + dest = os.path.join(app_path, lib + '.framework', 'Versions', '5') + os.makedirs(dest) + os.symlink(os.path.join(os.pardir, os.pardir, os.pardir, 'Contents', + 'MacOS', lib), + os.path.join(dest, lib)) + # Patch Info.plist - pyinstaller's options are too limiting + plist_path = os.path.join(app_path, 'Contents', 'Info.plist') + with open(plist_path, "rb") as f: + plist_data = plistlib.load(f) + plist_data.update(INFO_PLIST_UPDATES) + with open(plist_path, "wb") as f: + plistlib.dump(plist_data, f) + + +INFO_PLIST_UPDATES = { + 'CFBundleVersion': qutebrowser.__version__, + 'CFBundleShortVersionString': qutebrowser.__version__, + 'NSSupportsAutomaticGraphicsSwitching': True, + 'NSHighResolutionCapable': True, + 'CFBundleURLTypes': [{ + "CFBundleURLName": "http(s) URL", + "CFBundleURLSchemes": ["http", "https"] + }, { + "CFBundleURLName": "local file URL", + "CFBundleURLSchemes": ["file"] + }], + 'CFBundleDocumentTypes': [{ + "CFBundleTypeExtensions": ["html", "htm"], + "CFBundleTypeMIMETypes": ["text/html"], + "CFBundleTypeName": "HTML document", + "CFBundleTypeOSTypes": ["HTML"], + "CFBundleTypeRole": "Viewer", + }, { + "CFBundleTypeExtensions": ["xhtml"], + "CFBundleTypeMIMETypes": ["text/xhtml"], + "CFBundleTypeName": "XHTML document", + "CFBundleTypeRole": "Viewer", + }] +} + + +def build_mac(): + """Build macOS .dmg/.app.""" + utils.print_title("Cleaning up...") + for f in ['wc.dmg', 'template.dmg']: + try: + os.remove(f) + except FileNotFoundError: + pass + for d in ['dist', 'build']: + shutil.rmtree(d, ignore_errors=True) + utils.print_title("Updating 3rdparty content") + # Currently disabled because QtWebEngine has no pdfjs support + # update_3rdparty.run(ace=False, pdfjs=True, fancy_dmg=False) + utils.print_title("Building .app via pyinstaller") + call_tox('pyinstaller', '-r') + utils.print_title("Patching .app") + patch_mac_app() + utils.print_title("Building .dmg") + subprocess.run(['make', '-f', 'scripts/dev/Makefile-dmg'], check=True) + + dmg_name = 'qutebrowser-{}.dmg'.format(qutebrowser.__version__) + os.rename('qutebrowser.dmg', dmg_name) + + utils.print_title("Running smoke test") + + try: + with tempfile.TemporaryDirectory() as tmpdir: + subprocess.run(['hdiutil', 'attach', dmg_name, + '-mountpoint', tmpdir], check=True) + try: + binary = os.path.join(tmpdir, 'qutebrowser.app', 'Contents', + 'MacOS', 'qutebrowser') + smoke_test(binary) + finally: + time.sleep(5) + subprocess.run(['hdiutil', 'detach', tmpdir]) + except PermissionError as e: + print("Failed to remove tempdir: {}".format(e)) + + return [(dmg_name, 'application/x-apple-diskimage', 'macOS .dmg')] + + +def patch_windows(out_dir): + """Copy missing DLLs for windows into the given output.""" + dll_dir = os.path.join('.tox', 'pyinstaller', 'lib', 'site-packages', + 'PyQt5', 'Qt', 'bin') + dlls = ['libEGL.dll', 'libGLESv2.dll', 'libeay32.dll', 'ssleay32.dll'] + for dll in dlls: + shutil.copy(os.path.join(dll_dir, dll), out_dir) + + +def build_windows(): + """Build windows executables/setups.""" + utils.print_title("Updating 3rdparty content") + # Currently disabled because QtWebEngine has no pdfjs support + # update_3rdparty.run(ace=False, pdfjs=True, fancy_dmg=False) + + utils.print_title("Building Windows binaries") + parts = str(sys.version_info.major), str(sys.version_info.minor) + ver = ''.join(parts) + dot_ver = '.'.join(parts) + + # Get python path from registry if possible + try: + reg64_key = winreg.OpenKeyEx(winreg.HKEY_LOCAL_MACHINE, + r'SOFTWARE\Python\PythonCore' + r'\{}\InstallPath'.format(dot_ver)) + python_x64 = winreg.QueryValueEx(reg64_key, 'ExecutablePath')[0] + except FileNotFoundError: + python_x64 = r'C:\Python{}\python.exe'.format(ver) + + out_pyinstaller = os.path.join('dist', 'qutebrowser') + out_64 = os.path.join('dist', + 'qutebrowser-{}-x64'.format(qutebrowser.__version__)) + + artifacts = [] + + from scripts.dev import gen_versioninfo + utils.print_title("Updating VersionInfo file") + gen_versioninfo.main() + + utils.print_title("Running pyinstaller 64bit") + _maybe_remove(out_64) + call_tox('pyinstaller', '-r', python=python_x64) + shutil.move(out_pyinstaller, out_64) + patch_windows(out_64) + + utils.print_title("Building installers") + subprocess.run(['makensis.exe', + '/DX64', + '/DVERSION={}'.format(qutebrowser.__version__), + 'misc/qutebrowser.nsi'], check=True) + + name_64 = 'qutebrowser-{}-amd64.exe'.format(qutebrowser.__version__) + + artifacts += [ + (os.path.join('dist', name_64), + 'application/vnd.microsoft.portable-executable', + 'Windows 64bit installer'), + ] + + utils.print_title("Running 64bit smoke test") + smoke_test(os.path.join(out_64, 'qutebrowser.exe')) + + utils.print_title("Zipping 64bit standalone...") + name = 'qutebrowser-{}-windows-standalone-amd64'.format( + qutebrowser.__version__) + shutil.make_archive(name, 'zip', 'dist', os.path.basename(out_64)) + artifacts.append(('{}.zip'.format(name), + 'application/zip', + 'Windows 64bit standalone')) + + return artifacts + + +def build_sdist(): + """Build an sdist and list the contents.""" + utils.print_title("Building sdist") + + _maybe_remove('dist') + + subprocess.run([sys.executable, 'setup.py', 'sdist'], check=True) + dist_files = os.listdir(os.path.abspath('dist')) + assert len(dist_files) == 1 + + dist_file = os.path.join('dist', dist_files[0]) + subprocess.run(['gpg', '--detach-sign', '-a', dist_file], check=True) + + tar = tarfile.open(dist_file) + by_ext = collections.defaultdict(list) + + for tarinfo in tar.getmembers(): + if not tarinfo.isfile(): + continue + name = os.sep.join(tarinfo.name.split(os.sep)[1:]) + _base, ext = os.path.splitext(name) + by_ext[ext].append(name) + + assert '.pyc' not in by_ext + + utils.print_title("sdist contents") + + for ext, files in sorted(by_ext.items()): + utils.print_subtitle(ext) + print('\n'.join(files)) + + filename = 'qutebrowser-{}.tar.gz'.format(qutebrowser.__version__) + artifacts = [ + (os.path.join('dist', filename), 'application/gzip', 'Source release'), + (os.path.join('dist', filename + '.asc'), 'application/pgp-signature', + 'Source release - PGP signature'), + ] + + return artifacts + + +def test_makefile(): + """Make sure the Makefile works correctly.""" + utils.print_title("Testing makefile") + with tempfile.TemporaryDirectory() as tmpdir: + subprocess.run(['make', '-f', 'misc/Makefile', + 'DESTDIR={}'.format(tmpdir), 'install'], check=True) + + +def read_github_token(): + """Read the GitHub API token from disk.""" + token_file = os.path.join(os.path.expanduser('~'), '.gh_token') + with open(token_file, encoding='ascii') as f: + token = f.read().strip() + return token + + +def github_upload(artifacts, tag): + """Upload the given artifacts to GitHub. + + Args: + artifacts: A list of (filename, mimetype, description) tuples + tag: The name of the release tag + """ + import github3 + utils.print_title("Uploading to github...") + + token = read_github_token() + gh = github3.login(token=token) + repo = gh.repository('qutebrowser', 'qutebrowser') + + release = None # to satisfy pylint + for release in repo.releases(): + if release.tag_name == tag: + break + else: + raise Exception("No release found for {!r}!".format(tag)) + + for filename, mimetype, description in artifacts: + with open(filename, 'rb') as f: + basename = os.path.basename(filename) + asset = release.upload_asset(mimetype, basename, f) + asset.edit(basename, description) + + +def pypi_upload(artifacts): + """Upload the given artifacts to PyPI using twine.""" + filenames = [a[0] for a in artifacts] + subprocess.run(['twine', 'upload'] + filenames, check=True) + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('--asciidoc', help="Full path to python and " + "asciidoc.py. If not given, it's searched in PATH.", + nargs=2, required=False, + metavar=('PYTHON', 'ASCIIDOC')) + parser.add_argument('--upload', help="Tag to upload the release for", + nargs=1, required=False, metavar='TAG') + args = parser.parse_args() + utils.change_cwd() + + upload_to_pypi = False + + if args.upload is not None: + # Fail early when trying to upload without github3 installed + # or without API token + import github3 # pylint: disable=unused-variable + read_github_token() + + run_asciidoc2html(args) + if os.name == 'nt': + artifacts = build_windows() + elif sys.platform == 'darwin': + artifacts = build_mac() + else: + test_makefile() + artifacts = build_sdist() + upload_to_pypi = True + + if args.upload is not None: + utils.print_title("Press enter to release...") + input() + github_upload(artifacts, args.upload[0]) + if upload_to_pypi: + pypi_upload(artifacts) + else: + print() + utils.print_title("Artifacts") + for artifact in artifacts: + print(artifact) + + +if __name__ == '__main__': + main() diff --git a/.config/qutebrowser/scripts/dev/check_coverage.py b/.config/qutebrowser/scripts/dev/check_coverage.py new file mode 100644 index 0000000..32c5afc --- /dev/null +++ b/.config/qutebrowser/scripts/dev/check_coverage.py @@ -0,0 +1,348 @@ +#!/usr/bin/env python3 +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2015-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org> + +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. + +"""Enforce perfect coverage on some files.""" + +import os +import os.path +import sys +import enum +import subprocess +from xml.etree import ElementTree + +import attr + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir, + os.pardir)) + +from scripts import utils as scriptutils +from qutebrowser.utils import utils + + +@attr.s +class Message: + + """A message shown by coverage.py.""" + + typ = attr.ib() + filename = attr.ib() + text = attr.ib() + + +MsgType = enum.Enum('MsgType', 'insufficent_coverage, perfect_file') + + +# A list of (test_file, tested_file) tuples. test_file can be None. +PERFECT_FILES = [ + (None, + 'commands/cmdexc.py'), + ('tests/unit/commands/test_cmdutils.py', + 'commands/cmdutils.py'), + ('tests/unit/commands/test_argparser.py', + 'commands/argparser.py'), + + ('tests/unit/browser/webkit/test_cache.py', + 'browser/webkit/cache.py'), + ('tests/unit/browser/webkit/test_cookies.py', + 'browser/webkit/cookies.py'), + ('tests/unit/browser/test_history.py', + 'browser/history.py'), + ('tests/unit/browser/webkit/http/test_http.py', + 'browser/webkit/http.py'), + ('tests/unit/browser/webkit/http/test_content_disposition.py', + 'browser/webkit/rfc6266.py'), + # ('tests/unit/browser/webkit/test_webkitelem.py', + # 'browser/webkit/webkitelem.py'), + # ('tests/unit/browser/webkit/test_webkitelem.py', + # 'browser/webelem.py'), + ('tests/unit/browser/webkit/network/test_filescheme.py', + 'browser/webkit/network/filescheme.py'), + ('tests/unit/browser/webkit/network/test_networkreply.py', + 'browser/webkit/network/networkreply.py'), + + ('tests/unit/browser/test_signalfilter.py', + 'browser/signalfilter.py'), + (None, + 'browser/webengine/certificateerror.py'), + # ('tests/unit/browser/test_tab.py', + # 'browser/tab.py'), + + ('tests/unit/keyinput/test_basekeyparser.py', + 'keyinput/basekeyparser.py'), + ('tests/unit/keyinput/test_keyutils.py', + 'keyinput/keyutils.py'), + + ('tests/unit/misc/test_autoupdate.py', + 'misc/autoupdate.py'), + ('tests/unit/misc/test_readline.py', + 'misc/readline.py'), + ('tests/unit/misc/test_split.py', + 'misc/split.py'), + ('tests/unit/misc/test_msgbox.py', + 'misc/msgbox.py'), + ('tests/unit/misc/test_checkpyver.py', + 'misc/checkpyver.py'), + ('tests/unit/misc/test_guiprocess.py', + 'misc/guiprocess.py'), + ('tests/unit/misc/test_editor.py', + 'misc/editor.py'), + ('tests/unit/misc/test_cmdhistory.py', + 'misc/cmdhistory.py'), + ('tests/unit/misc/test_ipc.py', + 'misc/ipc.py'), + ('tests/unit/misc/test_keyhints.py', + 'misc/keyhintwidget.py'), + ('tests/unit/misc/test_pastebin.py', + 'misc/pastebin.py'), + (None, + 'misc/objects.py'), + + (None, + 'mainwindow/statusbar/keystring.py'), + ('tests/unit/mainwindow/statusbar/test_percentage.py', + 'mainwindow/statusbar/percentage.py'), + ('tests/unit/mainwindow/statusbar/test_progress.py', + 'mainwindow/statusbar/progress.py'), + ('tests/unit/mainwindow/statusbar/test_tabindex.py', + 'mainwindow/statusbar/tabindex.py'), + ('tests/unit/mainwindow/statusbar/test_textbase.py', + 'mainwindow/statusbar/textbase.py'), + ('tests/unit/mainwindow/statusbar/test_url.py', + 'mainwindow/statusbar/url.py'), + ('tests/unit/mainwindow/statusbar/test_backforward.py', + 'mainwindow/statusbar/backforward.py'), + ('tests/unit/mainwindow/test_messageview.py', + 'mainwindow/messageview.py'), + + ('tests/unit/config/test_config.py', + 'config/config.py'), + ('tests/unit/config/test_configdata.py', + 'config/configdata.py'), + ('tests/unit/config/test_configexc.py', + 'config/configexc.py'), + ('tests/unit/config/test_configfiles.py', + 'config/configfiles.py'), + ('tests/unit/config/test_configtypes.py', + 'config/configtypes.py'), + ('tests/unit/config/test_configinit.py', + 'config/configinit.py'), + ('tests/unit/config/test_configcommands.py', + 'config/configcommands.py'), + ('tests/unit/config/test_configutils.py', + 'config/configutils.py'), + + ('tests/unit/utils/test_qtutils.py', + 'utils/qtutils.py'), + ('tests/unit/utils/test_standarddir.py', + 'utils/standarddir.py'), + ('tests/unit/utils/test_urlutils.py', + 'utils/urlutils.py'), + ('tests/unit/utils/usertypes', + 'utils/usertypes.py'), + ('tests/unit/utils/test_utils.py', + 'utils/utils.py'), + ('tests/unit/utils/test_version.py', + 'utils/version.py'), + ('tests/unit/utils/test_debug.py', + 'utils/debug.py'), + ('tests/unit/utils/test_jinja.py', + 'utils/jinja.py'), + ('tests/unit/utils/test_error.py', + 'utils/error.py'), + ('tests/unit/utils/test_javascript.py', + 'utils/javascript.py'), + ('tests/unit/utils/test_urlmatch.py', + 'utils/urlmatch.py'), + + (None, + 'completion/models/util.py'), + ('tests/unit/completion/test_models.py', + 'completion/models/urlmodel.py'), + ('tests/unit/completion/test_models.py', + 'completion/models/configmodel.py'), + ('tests/unit/completion/test_histcategory.py', + 'completion/models/histcategory.py'), + ('tests/unit/completion/test_listcategory.py', + 'completion/models/listcategory.py'), + + ('tests/unit/browser/webengine/test_spell.py', + 'browser/webengine/spell.py'), + +] + + +# 100% coverage because of end2end tests, but no perfect unit tests yet. +WHITELISTED_FILES = [ + 'browser/webkit/webkitinspector.py', + 'keyinput/macros.py', + 'browser/webkit/webkitelem.py', +] + + +class Skipped(Exception): + + """Exception raised when skipping coverage checks.""" + + def __init__(self, reason): + self.reason = reason + super().__init__("Skipping coverage checks " + reason) + + +def _get_filename(filename): + """Transform the absolute test filenames to relative ones.""" + if os.path.isabs(filename): + basedir = os.path.abspath( + os.path.join(os.path.dirname(__file__), '..', '..')) + common_path = os.path.commonprefix([basedir, filename]) + if common_path: + filename = filename[len(common_path):].lstrip('/') + if filename.startswith('qutebrowser/'): + filename = filename.split('/', maxsplit=1)[1] + + return filename + + +def check(fileobj, perfect_files): + """Main entry point which parses/checks coverage.xml if applicable.""" + if not utils.is_linux: + raise Skipped("on non-Linux system.") + elif '-k' in sys.argv[1:]: + raise Skipped("because -k is given.") + elif '-m' in sys.argv[1:]: + raise Skipped("because -m is given.") + elif '--lf' in sys.argv[1:]: + raise Skipped("because --lf is given.") + + perfect_src_files = [e[1] for e in perfect_files] + + filename_args = [arg for arg in sys.argv[1:] + if arg.startswith('tests' + os.sep)] + filtered_files = [tpl[1] for tpl in perfect_files if tpl[0] in + filename_args] + + if filename_args and not filtered_files: + raise Skipped("because there is nothing to check.") + + tree = ElementTree.parse(fileobj) + classes = tree.getroot().findall('./packages/package/classes/class') + + messages = [] + + for klass in classes: + filename = _get_filename(klass.attrib['filename']) + + line_cov = float(klass.attrib['line-rate']) * 100 + branch_cov = float(klass.attrib['branch-rate']) * 100 + + if filtered_files and filename not in filtered_files: + continue + + assert 0 <= line_cov <= 100, line_cov + assert 0 <= branch_cov <= 100, branch_cov + assert '\\' not in filename, filename + + is_bad = line_cov < 100 or branch_cov < 100 + + if filename in perfect_src_files and is_bad: + text = "{} has {:.2f}% line and {:.2f}% branch coverage!".format( + filename, line_cov, branch_cov) + messages.append(Message(MsgType.insufficent_coverage, filename, + text)) + elif (filename not in perfect_src_files and not is_bad and + filename not in WHITELISTED_FILES): + text = ("{} has 100% coverage but is not in " + "perfect_files!".format(filename)) + messages.append(Message(MsgType.perfect_file, filename, text)) + + return messages + + +def main_check(): + """Check coverage after a test run.""" + try: + with open('coverage.xml', encoding='utf-8') as f: + messages = check(f, PERFECT_FILES) + except Skipped as e: + print(e) + messages = [] + + if messages: + print() + print() + scriptutils.print_title("Coverage check failed") + for msg in messages: + print(msg.text) + print() + filters = ','.join('qutebrowser/' + msg.filename for msg in messages) + subprocess.run([sys.executable, '-m', 'coverage', 'report', + '--show-missing', '--include', filters], check=True) + print() + print("To debug this, run 'tox -e py36-pyqt59-cov' " + "(or py35-pyqt59-cov) locally and check htmlcov/index.html") + print("or check https://codecov.io/github/qutebrowser/qutebrowser") + print() + + if 'CI' in os.environ: + print("Keeping coverage.xml on CI.") + else: + os.remove('coverage.xml') + return 1 if messages else 0 + + +def main_check_all(): + """Check the coverage for all files individually. + + This makes sure the files have 100% coverage without running unrelated + tests. + + This runs pytest with the used executable, so check_coverage.py should be + called with something like ./.tox/py36/bin/python. + """ + for test_file, src_file in PERFECT_FILES: + if test_file is None: + continue + subprocess.run( + [sys.executable, '-m', 'pytest', '--cov', 'qutebrowser', + '--cov-report', 'xml', test_file], check=True) + with open('coverage.xml', encoding='utf-8') as f: + messages = check(f, [(test_file, src_file)]) + os.remove('coverage.xml') + + messages = [msg for msg in messages + if msg.typ == MsgType.insufficent_coverage] + if messages: + for msg in messages: + print(msg.text) + return 1 + else: + print("Check ok!") + return 0 + + +def main(): + scriptutils.change_cwd() + if '--check-all' in sys.argv: + return main_check_all() + else: + return main_check() + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/.config/qutebrowser/scripts/dev/check_doc_changes.py b/.config/qutebrowser/scripts/dev/check_doc_changes.py new file mode 100755 index 0000000..3d90bea --- /dev/null +++ b/.config/qutebrowser/scripts/dev/check_doc_changes.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2016-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org> + +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. + +"""Check if docs changed and output an error if so.""" + +import sys +import subprocess +import os + +code = subprocess.run(['git', '--no-pager', 'diff', + '--exit-code', '--stat']).returncode + +if os.environ.get('TRAVIS_PULL_REQUEST', 'false') != 'false': + if code != 0: + print("Docs changed but ignoring change as we're building a PR") + sys.exit(0) + +if code != 0: + print() + print('The autogenerated docs changed, please run this to update them:') + print(' tox -e docs') + print(' git commit -am "Update docs"') + print() + print('(Or you have uncommitted changes, in which case you can ignore ' + 'this.)') + if 'TRAVIS' in os.environ: + print() + print("travis_fold:start:gitdiff") + subprocess.run(['git', '--no-pager', 'diff']) + print("travis_fold:end:gitdiff") +sys.exit(code) diff --git a/.config/qutebrowser/scripts/dev/ci/travis_backtrace.sh b/.config/qutebrowser/scripts/dev/ci/travis_backtrace.sh new file mode 100644 index 0000000..227dde8 --- /dev/null +++ b/.config/qutebrowser/scripts/dev/ci/travis_backtrace.sh @@ -0,0 +1,18 @@ +#!/bin/bash +# +# Find all possible core files under current directory. Attempt +# to determine exe using file(1) and dump stack trace with gdb. +# + +case $TESTENV in + py3*-pyqt*) + exe=$(readlink -f ".tox/$TESTENV/bin/python") + full= + ;; + *) + echo "Skipping coredump analysis in testenv $TESTENV!" + exit 0 + ;; +esac + +find . \( -name "*.core" -o -name core \) -exec gdb --batch --quiet -ex "thread apply all bt $full" "$exe" {} \; diff --git a/.config/qutebrowser/scripts/dev/ci/travis_install.sh b/.config/qutebrowser/scripts/dev/ci/travis_install.sh new file mode 100644 index 0000000..18f5aa9 --- /dev/null +++ b/.config/qutebrowser/scripts/dev/ci/travis_install.sh @@ -0,0 +1,108 @@ +#!/bin/bash +# vim: ft=sh fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2016-2017 Florian Bruhin (The Compiler) <mail@qutebrowser.org> + +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. + +# Stolen from https://github.com/travis-ci/travis-build/blob/master/lib/travis/build/templates/header.sh +# and adjusted to use ((...)) +travis_retry() { + local ANSI_RED='\033[31;1m' + local ANSI_RESET='\033[0m' + local result=0 + local count=1 + while (( count < 3 )); do + if (( result != 0 )); then + echo -e "\\n${ANSI_RED}The command \"$*\" failed. Retrying, $count of 3.${ANSI_RESET}\\n" >&2 + fi + "$@" + result=$? + (( result == 0 )) && break + count=$(( count + 1 )) + sleep 1 + done + + if (( count > 3 )); then + echo -e "\\n${ANSI_RED}The command \"$*\" failed 3 times.${ANSI_RESET}\\n" >&2 + fi + + return $result +} + +pip_install() { + travis_retry python3 -m pip install "$@" +} + +npm_install() { + # Make sure npm is up-to-date first + travis_retry npm install -g npm + travis_retry npm install -g "$@" +} + +check_pyqt() { + python3 <<EOF +import sys +from PyQt5.QtCore import QT_VERSION_STR, PYQT_VERSION_STR, qVersion +try: + from PyQt.sip import SIP_VERSION_STR +except ModuleNotFoundError: + from sip import SIP_VERSION_STR + +print("Python {}".format(sys.version)) +print("PyQt5 {}".format(PYQT_VERSION_STR)) +print("Qt5 {} (runtime {})".format(QT_VERSION_STR, qVersion())) +print("sip {}".format(SIP_VERSION_STR)) +EOF +} + +set -e + +if [[ $DOCKER ]]; then + exit 0 +elif [[ $TRAVIS_OS_NAME == osx ]]; then + # Disable App Nap + defaults write NSGlobalDomain NSAppSleepDisabled -bool YES + + curl -LO https://bootstrap.pypa.io/get-pip.py + sudo -H python get-pip.py + + brew --version + brew update + brew upgrade python libyaml + brew install qt5 pyqt5 + + pip_install -r misc/requirements/requirements-tox.txt + python3 -m pip --version + tox --version + check_pyqt + exit 0 +fi + +case $TESTENV in + eslint) + npm_install eslint + ;; + shellcheck) + ;; + *) + pip_install pip + pip_install -r misc/requirements/requirements-tox.txt + if [[ $TESTENV == *-cov ]]; then + pip_install -r misc/requirements/requirements-codecov.txt + fi + ;; +esac diff --git a/.config/qutebrowser/scripts/dev/ci/travis_run.sh b/.config/qutebrowser/scripts/dev/ci/travis_run.sh new file mode 100644 index 0000000..55ca7c1 --- /dev/null +++ b/.config/qutebrowser/scripts/dev/ci/travis_run.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +if [[ $DOCKER ]]; then + docker run \ + --privileged \ + -v "$PWD:/outside" \ + -e "QUTE_BDD_WEBENGINE=$QUTE_BDD_WEBENGINE" \ + -e "DOCKER=$DOCKER" \ + -e "CI=$CI" \ + -e "TRAVIS=$TRAVIS" \ + "qutebrowser/travis:$DOCKER" +elif [[ $TESTENV == eslint ]]; then + # Can't run this via tox as we can't easily install tox in the javascript + # travis env + cd qutebrowser/javascript || exit 1 + eslint --color --report-unused-disable-directives . +elif [[ $TESTENV == shellcheck ]]; then + SCRIPTS=$( mktemp ) + find scripts/dev/ -name '*.sh' >"$SCRIPTS" + find misc/userscripts/ -type f -exec grep -lE '[/ ][bd]ash$|[/ ]sh$|[/ ]ksh$' {} + >>"$SCRIPTS" + mapfile -t scripts <"$SCRIPTS" + rm -f "$SCRIPTS" + docker run \ + -v "$PWD:/outside" \ + -w /outside \ + koalaman/shellcheck:latest "${scripts[@]}" +else + args=() + [[ $TRAVIS_OS_NAME == osx ]] && args=('--qute-bdd-webengine' '--no-xvfb' 'tests/unit') + + tox -e "$TESTENV" -- "${args[@]}" +fi diff --git a/.config/qutebrowser/scripts/dev/cleanup.py b/.config/qutebrowser/scripts/dev/cleanup.py new file mode 100755 index 0000000..d1bb84a --- /dev/null +++ b/.config/qutebrowser/scripts/dev/cleanup.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2014-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org> +# +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. + +"""Script to clean up the mess made by Python/setuptools/PyInstaller.""" + +import os +import os.path +import sys +import glob +import shutil +import fnmatch + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir, + os.pardir)) + +from scripts import utils + + +recursive_lint = ('__pycache__', '*.pyc') +lint = ('build', 'dist', 'pkg/pkg', 'pkg/qutebrowser-*.pkg.tar.xz', 'pkg/src', + 'pkg/qutebrowser', 'qutebrowser.egg-info', 'setuptools-*.egg', + 'setuptools-*.zip', 'doc/qutebrowser.asciidoc', 'doc/*.html', + 'doc/qutebrowser.1', 'README.html', 'qutebrowser/html/doc') + + +def remove(path): + """Remove either a file or directory unless --dry-run is given.""" + if os.path.isdir(path): + print("rm -r '{}'".format(path)) + if '--dry-run' not in sys.argv: + shutil.rmtree(path) + else: + print("rm '{}'".format(path)) + if '--dry-run' not in sys.argv: + os.remove(path) + + +def main(): + """Clean up lint in the current dir.""" + utils.change_cwd() + for elem in lint: + for f in glob.glob(elem): + remove(f) + + for root, _dirs, _files in os.walk(os.getcwd()): + path = os.path.basename(root) + if any(fnmatch.fnmatch(path, e) for e in recursive_lint): + remove(root) + + +if __name__ == '__main__': + main() diff --git a/.config/qutebrowser/scripts/dev/download_release.sh b/.config/qutebrowser/scripts/dev/download_release.sh new file mode 100644 index 0000000..207da21 --- /dev/null +++ b/.config/qutebrowser/scripts/dev/download_release.sh @@ -0,0 +1,34 @@ +#!/bin/bash +set -e + +# This script downloads the given release from GitHub so we can mirror it on +# qutebrowser.org. + +tmpdir=$(mktemp -d) +oldpwd=$PWD + +if [[ $# != 1 ]]; then + echo "Usage: $0 <version>" >&2 + exit 1 +fi + +cd "$tmpdir" +mkdir windows + +base="https://github.com/qutebrowser/qutebrowser/releases/download/v$1" + +wget "$base/qutebrowser-$1.tar.gz" +wget "$base/qutebrowser-$1.tar.gz.asc" +wget "$base/qutebrowser-$1.dmg" +wget "$base/qutebrowser_${1}-1_all.deb" + +cd windows +wget "$base/qutebrowser-${1}-amd64.msi" +wget "$base/qutebrowser-${1}-win32.msi" +wget "$base/qutebrowser-${1}-windows-standalone-amd64.zip" +wget "$base/qutebrowser-${1}-windows-standalone-win32.zip" + +dest="/srv/http/qutebrowser/releases/v$1" +cd "$oldpwd" +sudo mv "$tmpdir" "$dest" +sudo chown -R http:http "$dest" diff --git a/.config/qutebrowser/scripts/dev/gen_resources.py b/.config/qutebrowser/scripts/dev/gen_resources.py new file mode 100644 index 0000000..cbfc69b --- /dev/null +++ b/.config/qutebrowser/scripts/dev/gen_resources.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# copyright 2014 florian bruhin (the compiler) <mail@qutebrowser.org> + +# this file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the gnu general public license as published by +# the free software foundation, either version 3 of the license, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but without any warranty; without even the implied warranty of +# merchantability or fitness for a particular purpose. see the +# gnu general public license for more details. +# +# you should have received a copy of the gnu general public license +# along with qutebrowser. if not, see <http://www.gnu.org/licenses/>. + +"""Generate Qt resources based on source files.""" + +import subprocess + +with open('qutebrowser/resources.py', 'w', encoding='utf-8') as f: + subprocess.run(['pyrcc5', 'qutebrowser.rcc'], stdout=f, check=True) diff --git a/.config/qutebrowser/scripts/dev/gen_versioninfo.py b/.config/qutebrowser/scripts/dev/gen_versioninfo.py new file mode 100644 index 0000000..1aa4b64 --- /dev/null +++ b/.config/qutebrowser/scripts/dev/gen_versioninfo.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org> +# +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. + +"""Generate file_version_info.txt for Pyinstaller use with Windows builds.""" + +import os.path +import sys + +# pylint: disable=import-error,no-member,useless-suppression +from PyInstaller.utils.win32 import versioninfo as vs +# pylint: enable=import-error,no-member,useless-suppression + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir, + os.pardir)) + +import qutebrowser +from scripts import utils + + +def main(): + utils.change_cwd() + out_filename = 'misc/file_version_info.txt' + + filevers = qutebrowser.__version_info__ + (0,) + prodvers = qutebrowser.__version_info__ + (0,) + str_filevers = qutebrowser.__version__ + str_prodvers = qutebrowser.__version__ + + comment_text = qutebrowser.__doc__ + copyright_text = qutebrowser.__copyright__ + trademark_text = ("qutebrowser is free software under the GNU General " + "Public License") + + # https://www.science.co.il/language/Locale-codes.php#definitions + # https://msdn.microsoft.com/en-us/library/windows/desktop/dd317756.aspx + en_us = 1033 # 0x0409 + utf_16 = 1200 # 0x04B0 + + ffi = vs.FixedFileInfo(filevers, prodvers) + + kids = [ + vs.StringFileInfo([ + # 0x0409: MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US) + # 0x04B0: codepage 1200 (UTF-16LE) + vs.StringTable('040904B0', [ + vs.StringStruct('Comments', comment_text), + vs.StringStruct('CompanyName', "qutebrowser.org"), + vs.StringStruct('FileDescription', "qutebrowser"), + vs.StringStruct('FileVersion', str_filevers), + vs.StringStruct('InternalName', "qutebrowser"), + vs.StringStruct('LegalCopyright', copyright_text), + vs.StringStruct('LegalTrademarks', trademark_text), + vs.StringStruct('OriginalFilename', "qutebrowser.exe"), + vs.StringStruct('ProductName', "qutebrowser"), + vs.StringStruct('ProductVersion', str_prodvers) + ]), + ]), + vs.VarFileInfo([vs.VarStruct('Translation', [en_us, utf_16])]), + ] + + file_version_info = vs.VSVersionInfo(ffi, kids) + + with open(out_filename, 'w', encoding='utf-8') as f: + f.write(str(file_version_info)) + + +if __name__ == '__main__': + main() diff --git a/.config/qutebrowser/scripts/dev/get_coredumpctl_traces.py b/.config/qutebrowser/scripts/dev/get_coredumpctl_traces.py new file mode 100644 index 0000000..d286d38 --- /dev/null +++ b/.config/qutebrowser/scripts/dev/get_coredumpctl_traces.py @@ -0,0 +1,176 @@ +#!/usr/bin/env python3 +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2015-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org> + +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. + +"""Get qutebrowser crash information and stacktraces from coredumpctl.""" + +import os +import os.path +import sys +import argparse +import subprocess +import tempfile + +import attr + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir, + os.pardir)) + +from scripts import utils + + +@attr.s +class Line: + + """A line in "coredumpctl list".""" + + time = attr.ib() + pid = attr.ib() + uid = attr.ib() + gid = attr.ib() + sig = attr.ib() + present = attr.ib() + exe = attr.ib() + + +def _convert_present(data): + """Convert " "/"*" to True/False for parse_coredumpctl_line.""" + if data == '*': + return True + elif data == ' ': + return False + else: + raise ValueError(data) + + +def parse_coredumpctl_line(line): + """Parse a given string coming from coredumpctl and return a Line object. + + Example input: + Mon 2015-09-28 23:22:24 CEST 10606 1000 1000 11 /usr/bin/python3.4 + """ + fields = { + 'time': (0, 28, str), + 'pid': (29, 35, int), + 'uid': (36, 41, int), + 'gid': (42, 47, int), + 'sig': (48, 51, int), + 'present': (52, 53, _convert_present), + 'exe': (54, None, str), + } + + data = {} + for name, (start, end, converter) in fields.items(): + data[name] = converter(line[start:end]) + return Line(**data) + + +def get_info(pid): + """Get and parse "coredumpctl info" output for the given PID.""" + data = {} + output = subprocess.run(['coredumpctl', 'info', str(pid)], check=True, + stdout=subprocess.PIPE).stdout + output = output.decode('utf-8') + for line in output.split('\n'): + if not line.strip(): + continue + try: + key, value = line.split(':', maxsplit=1) + except ValueError: + # systemd stack output + continue + data[key.strip()] = value.strip() + return data + + +def is_qutebrowser_dump(parsed): + """Check if the given Line is a qutebrowser dump.""" + basename = os.path.basename(parsed.exe) + if basename == 'python' or basename.startswith('python3'): + info = get_info(parsed.pid) + try: + cmdline = info['Command Line'] + except KeyError: + return True + else: + return '-m qutebrowser' in cmdline + else: + return basename == 'qutebrowser' + + +def dump_infos_gdb(parsed): + """Dump all needed infos for the given crash using gdb.""" + with tempfile.TemporaryDirectory() as tempdir: + coredump = os.path.join(tempdir, 'dump') + subprocess.run(['coredumpctl', 'dump', '-o', coredump, + str(parsed.pid)], check=True) + subprocess.run(['gdb', parsed.exe, coredump, + '-ex', 'info threads', + '-ex', 'thread apply all bt full', + '-ex', 'quit'], check=True) + + +def dump_infos(parsed): + """Dump all possible infos for the given crash.""" + if not parsed.present: + info = get_info(parsed.pid) + print("{}: Signal {} with no coredump: {}".format( + parsed.time, info.get('Signal', None), + info.get('Command Line', None))) + else: + print('\n\n\n') + utils.print_title('{} - {}'.format(parsed.time, parsed.pid)) + sys.stdout.flush() + dump_infos_gdb(parsed) + + +def check_prerequisites(): + """Check if coredumpctl/gdb are installed.""" + for binary in ['coredumpctl', 'gdb']: + try: + subprocess.run([binary, '--version'], check=True) + except FileNotFoundError: + print("{} is needed to run this script!".format(binary), + file=sys.stderr) + sys.exit(1) + + +def main(): + check_prerequisites() + + parser = argparse.ArgumentParser() + parser.add_argument('--all', help="Also list crashes without coredumps.", + action='store_true') + args = parser.parse_args() + + coredumps = subprocess.run(['coredumpctl', 'list'], check=True, + stdout=subprocess.PIPE).stdout + lines = coredumps.decode('utf-8').split('\n') + for line in lines[1:]: + if not line.strip(): + continue + parsed = parse_coredumpctl_line(line) + if not parsed.present and not args.all: + continue + if is_qutebrowser_dump(parsed): + dump_infos(parsed) + + +if __name__ == '__main__': + main() diff --git a/.config/qutebrowser/scripts/dev/misc_checks.py b/.config/qutebrowser/scripts/dev/misc_checks.py new file mode 100644 index 0000000..2992464 --- /dev/null +++ b/.config/qutebrowser/scripts/dev/misc_checks.py @@ -0,0 +1,163 @@ +#!/usr/bin/env python3 +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2014-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org> +# +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. + +"""Various small code checkers.""" + +import os +import os.path +import re +import sys +import argparse +import subprocess +import tokenize +import traceback +import collections + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir, + os.pardir)) + +from scripts import utils + + +def _get_files(only_py=False): + """Iterate over all python files and yield filenames.""" + for (dirpath, _dirnames, filenames) in os.walk('.'): + parts = dirpath.split(os.sep) + if len(parts) >= 2: + rootdir = parts[1] + if rootdir.startswith('.') or rootdir == 'htmlcov': + # ignore hidden dirs and htmlcov + continue + + if only_py: + endings = {'.py'} + else: + endings = {'.py', '.asciidoc', '.js', '.feature'} + files = (e for e in filenames if os.path.splitext(e)[1] in endings) + for name in files: + yield os.path.join(dirpath, name) + + +def check_git(): + """Check for uncommitted git files..""" + if not os.path.isdir(".git"): + print("No .git dir, ignoring") + print() + return False + untracked = [] + gitst = subprocess.run(['git', 'status', '--porcelain'], check=True, + stdout=subprocess.PIPE).stdout + gitst = gitst.decode('UTF-8').strip() + for line in gitst.splitlines(): + s, name = line.split(maxsplit=1) + if s == '??' and name != '.venv/': + untracked.append(name) + status = True + if untracked: + status = False + utils.print_col("Untracked files:", 'red') + print('\n'.join(untracked)) + print() + return status + + +def check_spelling(): + """Check commonly misspelled words.""" + # Words which I often misspell + words = {'[Bb]ehaviour', '[Qq]uitted', 'Ll]ikelyhood', '[Ss]ucessfully', + '[Oo]ccur[^rs .]', '[Ss]eperator', '[Ee]xplicitely', + '[Aa]uxillary', '[Aa]ccidentaly', '[Aa]mbigious', '[Ll]oosly', + '[Ii]nitialis', '[Cc]onvienence', '[Ss]imiliar', '[Uu]ncommited', + '[Rr]eproducable', '[Aa]n [Uu]ser', '[Cc]onvienience', + '[Ww]ether', '[Pp]rogramatically', '[Ss]plitted', '[Ee]xitted', + '[Mm]ininum', '[Rr]esett?ed', '[Rr]ecieved', '[Rr]egularily', + '[Uu]nderlaying', '[Ii]nexistant', '[Ee]lipsis', 'commiting', + 'existant', '[Rr]esetted', '[Ss]imilarily', '[Ii]nformations'} + + # Words which look better when splitted, but might need some fine tuning. + words |= {'[Ww]ebelements', '[Mm]ouseevent', '[Kk]eysequence', + '[Nn]ormalmode', '[Ee]ventloops', '[Ss]izehint', + '[Ss]tatemachine', '[Mm]etaobject', '[Ll]ogrecord', + '[Ff]iletype'} + + # Files which should be ignored, e.g. because they come from another + # package + ignored = [ + os.path.join('.', 'scripts', 'dev', 'misc_checks.py'), + os.path.join('.', 'qutebrowser', '3rdparty', 'pdfjs'), + os.path.join('.', 'tests', 'end2end', 'data', 'hints', 'ace', + 'ace.js'), + ] + + seen = collections.defaultdict(list) + try: + ok = True + for fn in _get_files(): + with tokenize.open(fn) as f: + if any(fn.startswith(i) for i in ignored): + continue + for line in f: + for w in words: + if (re.search(w, line) and + fn not in seen[w] and + '# pragma: no spellcheck' not in line): + print('Found "{}" in {}!'.format(w, fn)) + seen[w].append(fn) + ok = False + print() + return ok + except Exception: + traceback.print_exc() + return None + + +def check_vcs_conflict(): + """Check VCS conflict markers.""" + try: + ok = True + for fn in _get_files(only_py=True): + with tokenize.open(fn) as f: + for line in f: + if any(line.startswith(c * 7) for c in '<>=|'): + print("Found conflict marker in {}".format(fn)) + ok = False + print() + return ok + except Exception: + traceback.print_exc() + return None + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('checker', choices=('git', 'vcs', 'spelling'), + help="Which checker to run.") + args = parser.parse_args() + if args.checker == 'git': + ok = check_git() + elif args.checker == 'vcs': + ok = check_vcs_conflict() + elif args.checker == 'spelling': + ok = check_spelling() + return 0 if ok else 1 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/.config/qutebrowser/scripts/dev/pylint_checkers/qute_pylint/__init__.py b/.config/qutebrowser/scripts/dev/pylint_checkers/qute_pylint/__init__.py new file mode 100644 index 0000000..1341a93 --- /dev/null +++ b/.config/qutebrowser/scripts/dev/pylint_checkers/qute_pylint/__init__.py @@ -0,0 +1 @@ +"""Custom pylint checkers.""" diff --git a/.config/qutebrowser/scripts/dev/pylint_checkers/qute_pylint/config.py b/.config/qutebrowser/scripts/dev/pylint_checkers/qute_pylint/config.py new file mode 100644 index 0000000..5aa5250 --- /dev/null +++ b/.config/qutebrowser/scripts/dev/pylint_checkers/qute_pylint/config.py @@ -0,0 +1,84 @@ +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2014-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org> +# +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. + +"""Custom astroid checker for config calls.""" + +import sys +import pathlib + +import yaml +import astroid +from pylint import interfaces, checkers +from pylint.checkers import utils + + +OPTIONS = None +FAILED_LOAD = False + + +class ConfigChecker(checkers.BaseChecker): + + """Custom astroid checker for config calls.""" + + __implements__ = interfaces.IAstroidChecker + name = 'config' + msgs = { + 'E9998': ('%s is no valid config option.', # flake8: disable=S001 + 'bad-config-option', + None), + } + priority = -1 + printed_warning = False + + @utils.check_messages('bad-config-option') + def visit_attribute(self, node): + """Visit a getattr node.""" + # At the end of a config.val.foo.bar chain + if not isinstance(node.parent, astroid.Attribute): + # FIXME:conf do some proper check for this... + node_str = node.as_string() + prefix = 'config.val.' + if node_str.startswith(prefix): + self._check_config(node, node_str[len(prefix):]) + + def _check_config(self, node, name): + """Check that we're accessing proper config options.""" + if FAILED_LOAD: + if not ConfigChecker.printed_warning: + print("[WARN] Could not find configdata.yml. Please run " + "pylint from qutebrowser root.", file=sys.stderr) + print("Skipping some checks...", file=sys.stderr) + ConfigChecker.printed_warning = True + return + if name not in OPTIONS: + self.add_message('bad-config-option', node=node, args=name) + + +def register(linter): + """Register this checker.""" + linter.register_checker(ConfigChecker(linter)) + global OPTIONS + global FAILED_LOAD + yaml_file = pathlib.Path('qutebrowser') / 'config' / 'configdata.yml' + if not yaml_file.exists(): + OPTIONS = None + FAILED_LOAD = True + return + with yaml_file.open(mode='r', encoding='utf-8') as f: + OPTIONS = list(yaml.load(f)) diff --git a/.config/qutebrowser/scripts/dev/pylint_checkers/qute_pylint/modeline.py b/.config/qutebrowser/scripts/dev/pylint_checkers/qute_pylint/modeline.py new file mode 100644 index 0000000..429974c --- /dev/null +++ b/.config/qutebrowser/scripts/dev/pylint_checkers/qute_pylint/modeline.py @@ -0,0 +1,63 @@ +# Copyright 2014-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org> +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. + +"""Checker for vim modelines in files.""" + +import os.path +import contextlib + +from pylint import interfaces, checkers + + +class ModelineChecker(checkers.BaseChecker): + + """Check for vim modelines in files.""" + + __implements__ = interfaces.IRawChecker + + name = 'modeline' + msgs = {'W9002': ('Does not have vim modeline', 'modeline-missing', None), + 'W9003': ('Modeline is invalid', 'invalid-modeline', None), + 'W9004': ('Modeline position is wrong', 'modeline-position', None)} + options = () + priority = -1 + + def process_module(self, node): + """Process the module.""" + if os.path.basename(os.path.splitext(node.file)[0]) == '__init__': + return + max_lineno = 1 + with contextlib.closing(node.stream()) as stream: + for (lineno, line) in enumerate(stream): + if lineno == 1 and line.startswith(b'#!'): + max_lineno += 1 + continue + elif line.startswith(b'# vim:'): + if lineno > max_lineno: + self.add_message('modeline-position', line=lineno) + if (line.rstrip() != b'# vim: ft=python ' + b'fileencoding=utf-8 sts=4 sw=4 et:'): + self.add_message('invalid-modeline', line=lineno) + break + else: + self.add_message('modeline-missing', line=1) + + +def register(linter): + """Register the checker.""" + linter.register_checker(ModelineChecker(linter)) diff --git a/.config/qutebrowser/scripts/dev/pylint_checkers/qute_pylint/openencoding.py b/.config/qutebrowser/scripts/dev/pylint_checkers/qute_pylint/openencoding.py new file mode 100644 index 0000000..f577011 --- /dev/null +++ b/.config/qutebrowser/scripts/dev/pylint_checkers/qute_pylint/openencoding.py @@ -0,0 +1,83 @@ +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2014-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org> + +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. + +"""Make sure open() has an encoding set.""" + +import astroid +from pylint import interfaces, checkers +from pylint.checkers import utils + + +class OpenEncodingChecker(checkers.BaseChecker): + + """Checker to check open() has an encoding set.""" + + __implements__ = interfaces.IAstroidChecker + name = 'open-encoding' + + msgs = { + 'W9400': ('open() called without encoding', 'open-without-encoding', + None), + } + + @utils.check_messages('open-without-encoding') + def visit_call(self, node): + """Visit a Call node.""" + if hasattr(node, 'func'): + infer = utils.safe_infer(node.func) + if infer and infer.root().name == '_io': + if getattr(node.func, 'name', None) in ['open', 'file']: + self._check_open_encoding(node) + + def _check_open_encoding(self, node): + """Check that an open() call always has an encoding set.""" + try: + mode_arg = utils.get_argument_from_call(node, position=1, + keyword='mode') + except utils.NoSuchArgumentError: + mode_arg = None + _encoding = None + try: + _encoding = utils.get_argument_from_call(node, position=2) + except utils.NoSuchArgumentError: + try: + _encoding = utils.get_argument_from_call(node, + keyword='encoding') + except utils.NoSuchArgumentError: + pass + if _encoding is None: + if mode_arg is None: + mode = None + else: + mode = utils.safe_infer(mode_arg) + if mode is not None and not isinstance(mode, astroid.Const): + # We can't say what mode is exactly. + return + if mode is None: + self.add_message('open-without-encoding', node=node) + elif 'b' in getattr(mode, 'value', ''): + # Files opened as binary don't need an encoding. + return + else: + self.add_message('open-without-encoding', node=node) + + +def register(linter): + """Register this checker.""" + linter.register_checker(OpenEncodingChecker(linter)) diff --git a/.config/qutebrowser/scripts/dev/pylint_checkers/qute_pylint/settrace.py b/.config/qutebrowser/scripts/dev/pylint_checkers/qute_pylint/settrace.py new file mode 100644 index 0000000..c82d646 --- /dev/null +++ b/.config/qutebrowser/scripts/dev/pylint_checkers/qute_pylint/settrace.py @@ -0,0 +1,49 @@ +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2014-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org> +# +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. + +"""Custom astroid checker for set_trace calls.""" + +from pylint.interfaces import IAstroidChecker +from pylint.checkers import BaseChecker, utils + + +class SetTraceChecker(BaseChecker): + + """Custom astroid checker for set_trace calls.""" + + __implements__ = IAstroidChecker + name = 'settrace' + msgs = { + 'E9101': ('set_trace call found', 'set-trace', None), + } + priority = -1 + + @utils.check_messages('set-trace') + def visit_call(self, node): + """Visit a Call node.""" + if hasattr(node, 'func'): + infer = utils.safe_infer(node.func) + if infer: + if getattr(node.func, 'name', None) == 'set_trace': + self.add_message('set-trace', node=node) + + +def register(linter): + """Register this checker.""" + linter.register_checker(SetTraceChecker(linter)) diff --git a/.config/qutebrowser/scripts/dev/pylint_checkers/setup.py b/.config/qutebrowser/scripts/dev/pylint_checkers/setup.py new file mode 100644 index 0000000..7833c7d --- /dev/null +++ b/.config/qutebrowser/scripts/dev/pylint_checkers/setup.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 + +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2016-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org> +# +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. + +"""This is only here so we can install those plugins in tox.ini easily.""" + +from setuptools import setup +setup(name='qute_pylint', packages=['qute_pylint']) diff --git a/.config/qutebrowser/scripts/dev/quit_segfault_test.sh b/.config/qutebrowser/scripts/dev/quit_segfault_test.sh new file mode 100755 index 0000000..389f125 --- /dev/null +++ b/.config/qutebrowser/scripts/dev/quit_segfault_test.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +[[ $PWD == */scripts ]] && cd .. + +echo > crash.log +while :; do + exit=0 + while (( exit == 0 )); do + duration=$(( RANDOM % 10000 )) + python3 -m qutebrowser --debug ":later $duration quit" http://www.heise.de/ + exit=$? + done + echo "$(date) $exit $duration" >> crash.log +done diff --git a/.config/qutebrowser/scripts/dev/recompile_requirements.py b/.config/qutebrowser/scripts/dev/recompile_requirements.py new file mode 100644 index 0000000..6e26145 --- /dev/null +++ b/.config/qutebrowser/scripts/dev/recompile_requirements.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python3 +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2016-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org> +# +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. + +"""Script to regenerate requirements files in misc/requirements.""" + +import re +import sys +import os.path +import glob +import subprocess +import tempfile + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir, + os.pardir)) + +from scripts import utils + +REPO_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), + '..', '..') # /scripts/dev -> /scripts -> / +REQ_DIR = os.path.join(REPO_DIR, 'misc', 'requirements') + + +def convert_line(line, comments): + """Convert the given requirement line to place into the output.""" + for pattern, repl in comments['replace'].items(): + line = re.sub(pattern, repl, line) + + pkgname = line.split('=')[0] + + if pkgname in comments['ignore']: + line = '# ' + line + + try: + line += ' # ' + comments['comment'][pkgname] + except KeyError: + pass + + try: + line += ' # rq.filter: {}'.format(comments['filter'][pkgname]) + except KeyError: + pass + + return line + + +def read_comments(fobj): + """Find special comments in the config. + + Args: + fobj: A file object for the config. + + Return: + A dict with the parsed comment data. + """ + comments = { + 'filter': {}, + 'comment': {}, + 'ignore': [], + 'replace': {}, + } + for line in fobj: + if line.startswith('#@'): + command, args = line[2:].split(':', maxsplit=1) + command = command.strip() + args = args.strip() + if command == 'filter': + pkg, filt = args.split(' ', maxsplit=1) + comments['filter'][pkg] = filt + elif command == 'comment': + pkg, comment = args.split(' ', maxsplit=1) + comments['comment'][pkg] = comment + elif command == 'ignore': + comments['ignore'] += args.split(', ') + elif command == 'replace': + pattern, replacement = args.split(' ', maxsplit=1) + comments['replace'][pattern] = replacement + return comments + + +def get_all_names(): + """Get all requirement names based on filenames.""" + for filename in glob.glob(os.path.join(REQ_DIR, 'requirements-*.txt-raw')): + basename = os.path.basename(filename) + yield basename[len('requirements-'):-len('.txt-raw')] + + +def main(): + """Re-compile the given (or all) requirement files.""" + names = sys.argv[1:] if len(sys.argv) > 1 else sorted(get_all_names()) + + for name in names: + utils.print_title(name) + filename = os.path.join(REQ_DIR, + 'requirements-{}.txt-raw'.format(name)) + if name == 'qutebrowser': + outfile = os.path.join(REPO_DIR, 'requirements.txt') + else: + outfile = os.path.join(REQ_DIR, 'requirements-{}.txt'.format(name)) + + with tempfile.TemporaryDirectory() as tmpdir: + pip_bin = os.path.join(tmpdir, 'bin', 'pip') + subprocess.run(['virtualenv', tmpdir], check=True) + subprocess.run([pip_bin, 'install', '-r', filename], check=True) + proc = subprocess.run([pip_bin, 'freeze'], check=True, + stdout=subprocess.PIPE) + reqs = proc.stdout.decode('utf-8') + + with open(filename, 'r', encoding='utf-8') as f: + comments = read_comments(f) + + with open(outfile, 'w', encoding='utf-8') as f: + f.write("# This file is automatically generated by " + "scripts/dev/recompile_requirements.py\n\n") + for line in reqs.splitlines(): + f.write(convert_line(line, comments) + '\n') + + +if __name__ == '__main__': + main() diff --git a/.config/qutebrowser/scripts/dev/run_profile.py b/.config/qutebrowser/scripts/dev/run_profile.py new file mode 100755 index 0000000..93e0b61 --- /dev/null +++ b/.config/qutebrowser/scripts/dev/run_profile.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python3 +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2014-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org> +# +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. + +"""Profile qutebrowser.""" + +import sys +import cProfile +import os.path +import os +import tempfile +import subprocess +import shutil +import argparse +import shlex + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir, + os.pardir)) + +import qutebrowser.qutebrowser + + +def parse_args(): + """Parse commandline arguments. + + Return: + A (namespace, remaining_args) tuple from argparse. + """ + parser = argparse.ArgumentParser() + parser.add_argument('--profile-tool', metavar='TOOL', + action='store', choices=['kcachegrind', 'snakeviz', + 'gprof2dot', 'none'], + default='snakeviz', + help="The tool to use to view the profiling data") + parser.add_argument('--profile-file', metavar='FILE', action='store', + help="The filename to use with --profile-tool=none") + return parser.parse_known_args() + + +def main(): + args, remaining = parse_args() + tempdir = tempfile.mkdtemp() + + if args.profile_tool == 'none': + profilefile = os.path.join(os.getcwd(), args.profile_file) + else: + profilefile = os.path.join(tempdir, 'profile') + + sys.argv = [sys.argv[0]] + remaining + + profiler = cProfile.Profile() + profiler.runcall(qutebrowser.qutebrowser.main) + + # If we have an exception after here, we don't want the qutebrowser + # exception hook to take over. + sys.excepthook = sys.__excepthook__ + profiler.dump_stats(profilefile) + + if args.profile_tool == 'none': + pass + elif args.profile_tool == 'gprof2dot': + # yep, shell=True. I know what I'm doing. + subprocess.run( + 'gprof2dot -f pstats {} | dot -Tpng | feh -F -'.format( + shlex.quote(profilefile)), shell=True) + elif args.profile_tool == 'kcachegrind': + callgraphfile = os.path.join(tempdir, 'callgraph') + subprocess.run(['pyprof2calltree', '-k', '-i', profilefile, + '-o', callgraphfile]) + elif args.profile_tool == 'snakeviz': + subprocess.run(['snakeviz', profilefile]) + + shutil.rmtree(tempdir) + + +if __name__ == '__main__': + main() diff --git a/.config/qutebrowser/scripts/dev/run_pylint_on_tests.py b/.config/qutebrowser/scripts/dev/run_pylint_on_tests.py new file mode 100644 index 0000000..7adf45f --- /dev/null +++ b/.config/qutebrowser/scripts/dev/run_pylint_on_tests.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2014-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org> + +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. + +"""Run pylint on tests. + +This is needed because pylint can't check a folder which isn't a package: +https://bitbucket.org/logilab/pylint/issue/512/ +""" + +import os +import os.path +import sys +import subprocess + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir, + os.pardir)) + +from scripts import utils + + +def main(): + """Main entry point. + + Return: + The pylint exit status. + """ + utils.change_cwd() + files = [] + for dirpath, _dirnames, filenames in os.walk('tests'): + for fn in filenames: + if os.path.splitext(fn)[1] == '.py': + files.append(os.path.join(dirpath, fn)) + + disabled = [ + # pytest fixtures + 'redefined-outer-name', + 'unused-argument', + # things which are okay in tests + 'missing-docstring', + 'protected-access', + 'len-as-condition', + # directories without __init__.py... + 'import-error', + ] + + toxinidir = sys.argv[1] + pythonpath = os.environ.get('PYTHONPATH', '').split(os.pathsep) + [ + toxinidir, + ] + + args = (['--disable={}'.format(','.join(disabled)), + '--ignored-modules=helpers,pytest,PyQt5'] + + sys.argv[2:] + files) + env = os.environ.copy() + env['PYTHONPATH'] = os.pathsep.join(pythonpath) + + ret = subprocess.run(['pylint'] + args, env=env).returncode + return ret + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/.config/qutebrowser/scripts/dev/run_vulture.py b/.config/qutebrowser/scripts/dev/run_vulture.py new file mode 100755 index 0000000..cb19d62 --- /dev/null +++ b/.config/qutebrowser/scripts/dev/run_vulture.py @@ -0,0 +1,196 @@ +#!/usr/bin/env python +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2015-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org> + +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. + +"""Run vulture on the source files and filter out false-positives.""" + +import sys +import os +import re +import tempfile +import inspect +import argparse + +import vulture + +import qutebrowser.app # pylint: disable=unused-import +from qutebrowser.commands import cmdutils +from qutebrowser.utils import utils +from qutebrowser.browser.webkit import rfc6266 +# To run the decorators from there +# pylint: disable=unused-import +from qutebrowser.browser.webkit.network import webkitqutescheme +# pylint: enable=unused-import +from qutebrowser.browser import qutescheme +from qutebrowser.config import configtypes + + +def whitelist_generator(): # noqa + """Generator which yields lines to add to a vulture whitelist.""" + # qutebrowser commands + for cmd in cmdutils.cmd_dict.values(): + yield utils.qualname(cmd.handler) + + # pyPEG2 classes + for name, member in inspect.getmembers(rfc6266, inspect.isclass): + for attr in ['grammar', 'regex']: + if hasattr(member, attr): + yield 'qutebrowser.browser.webkit.rfc6266.{}.{}'.format(name, + attr) + + # PyQt properties + yield 'qutebrowser.mainwindow.statusbar.bar.StatusBar.color_flags' + yield 'qutebrowser.mainwindow.statusbar.url.UrlText.urltype' + + # Not used yet, but soon (or when debugging) + yield 'qutebrowser.utils.debug.log_events' + yield 'qutebrowser.utils.debug.log_signals' + yield 'qutebrowser.utils.debug.qflags_key' + yield 'qutebrowser.utils.qtutils.QtOSError.qt_errno' + yield 'scripts.utils.bg_colors' + + # Qt attributes + yield 'PyQt5.QtWebKit.QWebPage.ErrorPageExtensionReturn().baseUrl' + yield 'PyQt5.QtWebKit.QWebPage.ErrorPageExtensionReturn().content' + yield 'PyQt5.QtWebKit.QWebPage.ErrorPageExtensionReturn().encoding' + yield 'PyQt5.QtWebKit.QWebPage.ErrorPageExtensionReturn().fileNames' + yield 'PyQt5.QtWidgets.QStyleOptionViewItem.backgroundColor' + + ## qute://... handlers + for name in qutescheme._HANDLERS: # pylint: disable=protected-access + name = name.replace('-', '_') + yield 'qutebrowser.browser.qutescheme.qute_' + name + + # Other false-positives + yield 'qutebrowser.completion.models.listcategory.ListCategory().lessThan' + yield 'qutebrowser.utils.jinja.Loader.get_source' + yield 'qutebrowser.utils.log.QtWarningFilter.filter' + yield 'qutebrowser.browser.pdfjs.is_available' + yield 'qutebrowser.misc.guiprocess.spawn_output' + yield 'QEvent.posted' + yield 'log_stack' # from message.py + yield 'propagate' # logging.getLogger('...).propagate = False + # vulture doesn't notice the hasattr() and thus thinks netrc_used is unused + # in NetworkManager.on_authentication_required + yield 'PyQt5.QtNetwork.QNetworkReply.netrc_used' + yield 'qutebrowser.browser.downloads.last_used_directory' + yield 'PaintContext.clip' # from completiondelegate.py + yield 'logging.LogRecord.log_color' # from logging.py + yield 'scripts.utils.use_color' # from asciidoc2html.py + for attr in ['pyeval_output', 'log_clipboard', 'fake_clipboard']: + yield 'qutebrowser.misc.utilcmds.' + attr + + for attr in ['fileno', 'truncate', 'closed', 'readable']: + yield 'qutebrowser.utils.qtutils.PyQIODevice.' + attr + + for attr in ['msgs', 'priority', 'visit_attribute']: + yield 'scripts.dev.pylint_checkers.config.' + attr + for attr in ['visit_call', 'process_module']: + yield 'scripts.dev.pylint_checkers.modeline.' + attr + + for name, _member in inspect.getmembers(configtypes, inspect.isclass): + yield 'qutebrowser.config.configtypes.' + name + yield 'qutebrowser.config.configexc.ConfigErrorDesc.traceback' + yield 'qutebrowser.config.configfiles.ConfigAPI.load_autoconfig' + yield 'types.ModuleType.c' # configfiles:read_config_py + for name in ['configdir', 'datadir']: + yield 'qutebrowser.config.configfiles.ConfigAPI.' + name + + yield 'include_aliases' + + for attr in ['_get_default_metavar_for_optional', + '_get_default_metavar_for_positional', '_metavar_formatter']: + yield 'scripts.dev.src2asciidoc.UsageFormatter.' + attr + + # attrs + yield 'qutebrowser.browser.webkit.network.networkmanager.ProxyId.hostname' + yield 'qutebrowser.command.command.ArgInfo._validate_exclusive' + yield 'scripts.get_coredumpctl_traces.Line.uid' + yield 'scripts.get_coredumpctl_traces.Line.gid' + yield 'scripts.importer.import_moz_places.places.row_factory' + + +def filter_func(item): + """Check if a missing function should be filtered or not. + + Return: + True if the missing function should be filtered/ignored, False + otherwise. + """ + return bool(re.fullmatch(r'[a-z]+[A-Z][a-zA-Z]+', item.name)) + + +def report(items): + """Generate a report based on the given vulture.Item's. + + Based on vulture.Vulture.report, but we can't use that as we can't set the + properties which get used for the items. + """ + output = [] + for item in sorted(items, + key=lambda e: (e.filename.lower(), e.first_lineno)): + output.append(item.get_report()) + return output + + +def run(files): + """Run vulture over the given files.""" + with tempfile.NamedTemporaryFile(mode='w', delete=False) as whitelist_file: + for line in whitelist_generator(): + whitelist_file.write(line + '\n') + + whitelist_file.close() + + vult = vulture.Vulture(verbose=False) + vult.scavenge(files + [whitelist_file.name]) + + os.remove(whitelist_file.name) + + filters = { + 'unused_funcs': filter_func, + 'unused_props': lambda item: False, + 'unused_vars': lambda item: False, + 'unused_attrs': lambda item: False, + } + + items = [] + + for attr, func in filters.items(): + sub_items = getattr(vult, attr) + for item in sub_items: + filtered = func(item) + if not filtered: + items.append(item) + + return report(items) + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('files', nargs='*', default=['qutebrowser', 'scripts', + 'setup.py']) + args = parser.parse_args() + out = run(args.files) + for line in out: + print(line) + sys.exit(bool(out)) + + +if __name__ == '__main__': + main() diff --git a/.config/qutebrowser/scripts/dev/segfault_test.py b/.config/qutebrowser/scripts/dev/segfault_test.py new file mode 100755 index 0000000..aaf495f --- /dev/null +++ b/.config/qutebrowser/scripts/dev/segfault_test.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python3 +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2014-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org> +# +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. + +"""Tester for Qt segfaults with different harfbuzz engines.""" + +import os +import os.path +import signal +import sys +import subprocess + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir, + os.pardir)) + +from scripts import utils + + +SCRIPT = """ +import sys + +from PyQt5.QtCore import QUrl +from PyQt5.QtWidgets import QApplication +from PyQt5.QtWebKitWidgets import QWebView + +def on_load_finished(ok): + if ok: + app.exit(0) + else: + app.exit(1) + +app = QApplication([]) +wv = QWebView() +wv.loadFinished.connect(on_load_finished) +wv.load(QUrl(sys.argv[1])) +#wv.show() +app.exec_() +""" + + +def print_ret(ret): + """Print information about an exit status.""" + if ret == 0: + utils.print_col("success", 'green') + elif ret == -signal.SIGSEGV: + utils.print_col("segfault", 'red') + else: + utils.print_col("error {}".format(ret), 'yellow') + print() + + +def main(): + retvals = [] + if len(sys.argv) < 2: + # pages which previously caused problems + pages = [ + # ANGLE, https://bugreports.qt.io/browse/QTBUG-39723 + ('http://www.binpress.com/', False), + ('http://david.li/flow/', False), + ('https://imzdl.com/', False), + # not reproducible + # https://bugreports.qt.io/browse/QTBUG-39847 + ('http://www.20min.ch/', True), + # HarfBuzz, https://bugreports.qt.io/browse/QTBUG-39278 + ('http://www.the-compiler.org/', True), + ('http://phoronix.com', True), + ('http://twitter.com', True), + # HarfBuzz #2, https://bugreports.qt.io/browse/QTBUG-36099 + ('http://lenta.ru/', True), + # Unknown, https://bugreports.qt.io/browse/QTBUG-41360 + ('http://salt.readthedocs.org/en/latest/topics/pillar/', True), + ] + else: + pages = [(e, True) for e in sys.argv[1:]] + for page, test_harfbuzz in pages: + utils.print_bold("==== {} ====".format(page)) + if test_harfbuzz: + print("With system harfbuzz:") + ret = subprocess.run([sys.executable, '-c', SCRIPT, page]).returncode + print_ret(ret) + retvals.append(ret) + if test_harfbuzz: + print("With QT_HARFBUZZ=old:") + env = dict(os.environ) + env['QT_HARFBUZZ'] = 'old' + ret = subprocess.run([sys.executable, '-c', SCRIPT, page], + env=env).returncode + print_ret(ret) + retvals.append(ret) + print("With QT_HARFBUZZ=new:") + env = dict(os.environ) + env['QT_HARFBUZZ'] = 'new' + ret = subprocess.run([sys.executable, '-c', SCRIPT, page], + env=env).returncode + print_ret(ret) + retvals.append(ret) + if all(r == 0 for r in retvals): + sys.exit(0) + else: + sys.exit(1) + + +if __name__ == '__main__': + main() diff --git a/.config/qutebrowser/scripts/dev/src2asciidoc.py b/.config/qutebrowser/scripts/dev/src2asciidoc.py new file mode 100755 index 0000000..cc00c37 --- /dev/null +++ b/.config/qutebrowser/scripts/dev/src2asciidoc.py @@ -0,0 +1,561 @@ +#!/usr/bin/env python3 +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2014-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org> + +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. + +"""Generate asciidoc source for qutebrowser based on docstrings.""" + +import os +import os.path +import sys +import shutil +import inspect +import subprocess +import tempfile +import argparse + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir, + os.pardir)) + +# We import qutebrowser.app so all @cmdutils-register decorators are run. +import qutebrowser.app +from qutebrowser import qutebrowser, commands +from qutebrowser.commands import cmdutils, argparser +from qutebrowser.config import configdata, configtypes +from qutebrowser.utils import docutils, usertypes +from scripts import asciidoc2html, utils + +FILE_HEADER = """ +// DO NOT EDIT THIS FILE DIRECTLY! +// It is autogenerated by running: +// $ python3 scripts/dev/src2asciidoc.py +// vim: readonly: + +""".lstrip() + + +class UsageFormatter(argparse.HelpFormatter): + + """Patched HelpFormatter to include some asciidoc markup in the usage. + + This does some horrible things, but the alternative would be to reimplement + argparse.HelpFormatter while copying 99% of the code :-/ + """ + + def _format_usage(self, usage, actions, groups, _prefix): + """Override _format_usage to not add the 'usage:' prefix.""" + return super()._format_usage(usage, actions, groups, '') + + def _get_default_metavar_for_optional(self, action): + """Do name transforming when getting metavar.""" + return argparser.arg_name(action.dest.upper()) + + def _get_default_metavar_for_positional(self, action): + """Do name transforming when getting metavar.""" + return argparser.arg_name(action.dest) + + def _metavar_formatter(self, action, default_metavar): + """Override _metavar_formatter to add asciidoc markup to metavars. + + Most code here is copied from Python 3.4's argparse.py. + """ + if action.metavar is not None: + result = "'{}'".format(action.metavar) + elif action.choices is not None: + choice_strs = [str(choice) for choice in action.choices] + result = ('{' + ','.join('*{}*'.format(e) for e in choice_strs) + + '}') + else: + result = "'{}'".format(default_metavar) + + def fmt(tuple_size): + """Format the result according to the tuple size.""" + if isinstance(result, tuple): + return result + else: + return (result, ) * tuple_size + return fmt + + def _format_actions_usage(self, actions, groups): + """Override _format_actions_usage to add asciidoc markup to flags. + + Because argparse.py's _format_actions_usage is very complex, we first + monkey-patch the option strings to include the asciidoc markup, then + run the original method, then undo the patching. + """ + old_option_strings = {} + for action in actions: + old_option_strings[action] = action.option_strings[:] + action.option_strings = ['*{}*'.format(s) + for s in action.option_strings] + ret = super()._format_actions_usage(actions, groups) + for action in actions: + action.option_strings = old_option_strings[action] + return ret + + +def _open_file(name, mode='w'): + """Open a file with a preset newline/encoding mode.""" + return open(name, mode, newline='\n', encoding='utf-8') + + +def _get_cmd_syntax(_name, cmd): + """Get the command syntax for a command. + + We monkey-patch the parser's formatter_class here to use our UsageFormatter + which adds some asciidoc markup. + """ + old_fmt_class = cmd.parser.formatter_class + cmd.parser.formatter_class = UsageFormatter + usage = cmd.parser.format_usage().rstrip() + cmd.parser.formatter_class = old_fmt_class + return usage + + +def _get_command_quickref(cmds): + """Generate the command quick reference.""" + out = [] + out.append('[options="header",width="75%",cols="25%,75%"]') + out.append('|==============') + out.append('|Command|Description') + for name, cmd in cmds: + desc = inspect.getdoc(cmd.handler).splitlines()[0] + out.append('|<<{},{}>>|{}'.format(name, name, desc)) + out.append('|==============') + return '\n'.join(out) + + +def _get_setting_quickref(): + """Generate the settings quick reference.""" + out = [] + out.append('') + out.append('[options="header",width="75%",cols="25%,75%"]') + out.append('|==============') + out.append('|Setting|Description') + for opt in sorted(configdata.DATA.values()): + desc = opt.description.splitlines()[0] + out.append('|<<{},{}>>|{}'.format(opt.name, opt.name, desc)) + out.append('|==============') + return '\n'.join(out) + + +def _get_configtypes(): + """Get configtypes classes to document.""" + predicate = lambda e: ( + inspect.isclass(e) and + # pylint: disable=protected-access + e not in [configtypes.BaseType, configtypes.MappingType, + configtypes._Numeric] and + # pylint: enable=protected-access + issubclass(e, configtypes.BaseType)) + yield from inspect.getmembers(configtypes, predicate) + + +def _get_setting_types_quickref(): + """Generate the setting types quick reference.""" + out = [] + out.append('[[types]]') + out.append('[options="header",width="75%",cols="25%,75%"]') + out.append('|==============') + out.append('|Type|Description') + + for name, typ in _get_configtypes(): + parser = docutils.DocstringParser(typ) + desc = parser.short_desc + if parser.long_desc: + desc += '\n\n' + parser.long_desc + out.append('|{}|{}'.format(name, desc)) + + out.append('|==============') + return '\n'.join(out) + + +def _get_command_doc(name, cmd): + """Generate the documentation for a command.""" + output = ['[[{}]]'.format(name)] + output += ['=== {}'.format(name)] + syntax = _get_cmd_syntax(name, cmd) + if syntax != name: + output.append('Syntax: +:{}+'.format(syntax)) + output.append("") + parser = docutils.DocstringParser(cmd.handler) + output.append(parser.short_desc) + if parser.long_desc: + output.append("") + output.append(parser.long_desc) + + output += list(_get_command_doc_args(cmd, parser)) + output += list(_get_command_doc_count(cmd, parser)) + output += list(_get_command_doc_notes(cmd)) + + output.append("") + output.append("") + return '\n'.join(output) + + +def _get_command_doc_args(cmd, parser): + """Get docs for the arguments of a command. + + Args: + cmd: The Command to get the docs for. + parser: The DocstringParser to use. + + Yield: + Strings which should be added to the docs. + """ + if cmd.pos_args: + yield "" + yield "==== positional arguments" + for arg, name in cmd.pos_args: + try: + yield "* +'{}'+: {}".format(name, parser.arg_descs[arg]) + except KeyError as e: + raise KeyError("No description for arg {} of command " + "'{}'!".format(e, cmd.name)) from e + + if cmd.opt_args: + yield "" + yield "==== optional arguments" + for arg, (long_flag, short_flag) in cmd.opt_args.items(): + try: + yield '* +*{}*+, +*{}*+: {}'.format(short_flag, long_flag, + parser.arg_descs[arg]) + except KeyError as e: + raise KeyError("No description for arg {} of command " + "'{}'!".format(e, cmd.name)) from e + + +def _get_command_doc_count(cmd, parser): + """Get docs for the count of a command. + + Args: + cmd: The Command to get the docs for. + parser: The DocstringParser to use. + + Yield: + Strings which should be added to the docs. + """ + for param in inspect.signature(cmd.handler).parameters.values(): + if cmd.get_arg_info(param).count: + yield "" + yield "==== count" + try: + yield parser.arg_descs[param.name] + except KeyError as e: + raise KeyError("No description for count arg {!r} of command " + "{!r}!".format(param.name, cmd.name)) from e + + +def _get_command_doc_notes(cmd): + """Get docs for the notes of a command. + + Args: + cmd: The Command to get the docs for. + parser: The DocstringParser to use. + + Yield: + Strings which should be added to the docs. + """ + if (cmd.maxsplit is not None or cmd.no_cmd_split or + cmd.no_replace_variables and cmd.name != "spawn"): + yield "" + yield "==== note" + if cmd.maxsplit is not None: + yield ("* This command does not split arguments after the last " + "argument and handles quotes literally.") + if cmd.no_cmd_split: + yield ("* With this command, +;;+ is interpreted literally " + "instead of splitting off a second command.") + if cmd.no_replace_variables and cmd.name != "spawn": + yield r"* This command does not replace variables like +\{url\}+." + + +def _get_action_metavar(action, nargs=1): + """Get the metavar to display for an argparse action. + + Args: + action: The argparse action to get the metavar for. + nargs: The nargs setting for the related argument. + """ + if action.metavar is not None: + if isinstance(action.metavar, str): + elems = [action.metavar] * nargs + else: + elems = action.metavar + return ' '.join("'{}'".format(e) for e in elems) + elif action.choices is not None: + choices = ','.join(str(e) for e in action.choices) + return "'{{{}}}'".format(choices) + else: + return "'{}'".format(action.dest.upper()) + + +def _format_action_args(action): + """Get an argument string based on an argparse action.""" + if action.nargs is None: + return _get_action_metavar(action) + elif action.nargs == '?': + return '[{}]'.format(_get_action_metavar(action)) + elif action.nargs == '*': + return '[{mv} [{mv} ...]]'.format(mv=_get_action_metavar(action)) + elif action.nargs == '+': + return '{mv} [{mv} ...]'.format(mv=_get_action_metavar(action)) + elif action.nargs == '...': + return '...' + else: + return _get_action_metavar(action, nargs=action.nargs) + + +def _format_action(action): + """Get an invocation string/help from an argparse action.""" + if action.help == argparse.SUPPRESS: + return None + if not action.option_strings: + invocation = '*{}*::'.format(_get_action_metavar(action)) + else: + parts = [] + if action.nargs == 0: + # Doesn't take a value, so the syntax is -s, --long + parts += ['*{}*'.format(s) for s in action.option_strings] + else: + # Takes a value, so the syntax is -s ARGS or --long ARGS. + args_string = _format_action_args(action) + for opt in action.option_strings: + parts.append('*{}* {}'.format(opt, args_string)) + invocation = ', '.join(parts) + '::' + return '{}\n {}\n'.format(invocation, action.help) + + +def generate_commands(filename): + """Generate the complete commands section.""" + with _open_file(filename) as f: + f.write(FILE_HEADER) + f.write("= Commands\n\n") + f.write(commands.__doc__) + normal_cmds = [] + other_cmds = [] + debug_cmds = [] + for name, cmd in cmdutils.cmd_dict.items(): + if cmd.deprecated: + continue + if usertypes.KeyMode.normal not in cmd.modes: + other_cmds.append((name, cmd)) + elif cmd.debug: + debug_cmds.append((name, cmd)) + else: + normal_cmds.append((name, cmd)) + normal_cmds.sort() + other_cmds.sort() + debug_cmds.sort() + f.write("\n") + f.write("== Normal commands\n") + f.write(".Quick reference\n") + f.write(_get_command_quickref(normal_cmds) + '\n') + for name, cmd in normal_cmds: + f.write(_get_command_doc(name, cmd)) + f.write("\n") + f.write("== Commands not usable in normal mode\n") + f.write(".Quick reference\n") + f.write(_get_command_quickref(other_cmds) + '\n') + for name, cmd in other_cmds: + f.write(_get_command_doc(name, cmd)) + f.write("\n") + f.write("== Debugging commands\n") + f.write("These commands are mainly intended for debugging. They are " + "hidden if qutebrowser was started without the " + "`--debug`-flag.\n") + f.write("\n") + f.write(".Quick reference\n") + f.write(_get_command_quickref(debug_cmds) + '\n') + for name, cmd in debug_cmds: + f.write(_get_command_doc(name, cmd)) + + +def _generate_setting_backend_info(f, opt): + """Generate backend information for the given option.""" + all_backends = [usertypes.Backend.QtWebKit, usertypes.Backend.QtWebEngine] + if opt.raw_backends is not None: + for name, conditional in sorted(opt.raw_backends.items()): + if conditional is True: + pass + elif conditional is False: + f.write("\nOn {}, this setting is unavailable.\n".format(name)) + else: + f.write("\nOn {}, this setting requires {} or newer.\n" + .format(name, conditional)) + elif opt.backends == all_backends: + pass + elif opt.backends == [usertypes.Backend.QtWebKit]: + f.write("\nThis setting is only available with the QtWebKit " + "backend.\n") + elif opt.backends == [usertypes.Backend.QtWebEngine]: + f.write("\nThis setting is only available with the QtWebEngine " + "backend.\n") + else: + raise ValueError("Invalid value {!r} for opt.backends" + .format(opt.backends)) + + +def _generate_setting_option(f, opt): + """Generate documentation for a single section.""" + f.write("\n") + f.write('[[{}]]'.format(opt.name) + "\n") + f.write("=== {}".format(opt.name) + "\n") + f.write(opt.description + "\n") + if opt.restart: + f.write("This setting requires a restart.\n") + if opt.supports_pattern: + f.write("\nThis setting supports URL patterns.\n") + if opt.no_autoconfig: + f.write("\nThis setting can only be set in config.py.\n") + f.write("\n") + typ = opt.typ.get_name().replace(',', ',') + f.write('Type: <<types,{typ}>>\n'.format(typ=typ)) + f.write("\n") + + valid_values = opt.typ.get_valid_values() + if valid_values is not None and valid_values.generate_docs: + f.write("Valid values:\n") + f.write("\n") + for val in valid_values: + try: + desc = valid_values.descriptions[val] + f.write(" * +{}+: {}".format(val, desc) + "\n") + except KeyError: + f.write(" * +{}+".format(val) + "\n") + f.write("\n") + + f.write("Default: {}\n".format(opt.typ.to_doc(opt.default))) + _generate_setting_backend_info(f, opt) + + +def generate_settings(filename): + """Generate the complete settings section.""" + configdata.init() + with _open_file(filename) as f: + f.write(FILE_HEADER) + f.write("= Setting reference\n\n") + f.write("== All settings\n") + f.write(_get_setting_quickref() + "\n") + for opt in sorted(configdata.DATA.values()): + _generate_setting_option(f, opt) + f.write("\n== Setting types\n") + f.write(_get_setting_types_quickref() + "\n") + + +def _format_block(filename, what, data): + """Format a block in a file. + + The block is delimited by markers like these: + // QUTE_*_START + ... + // QUTE_*_END + + The * part is the part which should be given as 'what'. + + Args: + filename: The file to change. + what: What to change (authors, options, etc.) + data; A list of strings which is the new data. + """ + what = what.upper() + oshandle, tmpname = tempfile.mkstemp() + try: + with _open_file(filename, mode='r') as infile, \ + _open_file(oshandle, mode='w') as temp: + found_start = False + found_end = False + for line in infile: + if line.strip() == '// QUTE_{}_START'.format(what): + temp.write(line) + temp.write(''.join(data)) + found_start = True + elif line.strip() == '// QUTE_{}_END'.format(what.upper()): + temp.write(line) + found_end = True + elif (not found_start) or found_end: + temp.write(line) + if not found_start: + raise Exception("Marker '// QUTE_{}_START' not found in " + "'{}'!".format(what, filename)) + elif not found_end: + raise Exception("Marker '// QUTE_{}_END' not found in " + "'{}'!".format(what, filename)) + except: + os.remove(tmpname) + raise + else: + os.remove(filename) + shutil.move(tmpname, filename) + + +def regenerate_manpage(filename): + """Update manpage OPTIONS using an argparse parser.""" + parser = qutebrowser.get_argparser() + groups = [] + # positionals, optionals and user-defined groups + # pylint: disable=protected-access + for group in parser._action_groups: + groupdata = [] + groupdata.append('=== {}'.format(group.title)) + if group.description is not None: + groupdata.append(group.description) + for action in group._group_actions: + action_data = _format_action(action) + if action_data is not None: + groupdata.append(action_data) + groups.append('\n'.join(groupdata)) + # pylint: enable=protected-access + options = '\n'.join(groups) + # epilog + if parser.epilog is not None: + options += parser.epilog + _format_block(filename, 'options', options) + + +def regenerate_cheatsheet(): + """Generate cheatsheet PNGs based on the SVG.""" + files = [ + ('doc/img/cheatsheet-small.png', 300, 185), + ('doc/img/cheatsheet-big.png', 3342, 2060), + ] + + for filename, x, y in files: + subprocess.run(['inkscape', '-e', filename, '-b', 'white', + '-w', str(x), '-h', str(y), + 'misc/cheatsheet.svg'], check=True) + + +def main(): + """Regenerate all documentation.""" + utils.change_cwd() + print("Generating manpage...") + regenerate_manpage('doc/qutebrowser.1.asciidoc') + print("Generating settings help...") + generate_settings('doc/help/settings.asciidoc') + print("Generating command help...") + generate_commands('doc/help/commands.asciidoc') + if '--cheatsheet' in sys.argv: + print("Regenerating cheatsheet .pngs") + regenerate_cheatsheet() + if '--html' in sys.argv: + asciidoc2html.main() + + +if __name__ == '__main__': + main() diff --git a/.config/qutebrowser/scripts/dev/standardpaths_tester.py b/.config/qutebrowser/scripts/dev/standardpaths_tester.py new file mode 100644 index 0000000..27b8382 --- /dev/null +++ b/.config/qutebrowser/scripts/dev/standardpaths_tester.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2017-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org> +# +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. + +"""Show various QStandardPath paths.""" + +import os +import sys + +from PyQt5.QtCore import (QT_VERSION_STR, PYQT_VERSION_STR, qVersion, + QStandardPaths, QCoreApplication) + + +def print_header(): + """Show system information.""" + print("Python {}".format(sys.version)) + print("os.name: {}".format(os.name)) + print("sys.platform: {}".format(sys.platform)) + print() + + print("Qt {}, compiled {}".format(qVersion(), QT_VERSION_STR)) + print("PyQt {}".format(PYQT_VERSION_STR)) + print() + + +def print_paths(): + """Print all QStandardPaths.StandardLocation members.""" + for name, obj in vars(QStandardPaths).items(): + if isinstance(obj, QStandardPaths.StandardLocation): + location = QStandardPaths.writableLocation(obj) + print("{:25} {}".format(name, location)) + + +def main(): + print_header() + + print("No QApplication") + print("===============") + print() + print_paths() + + app = QCoreApplication(sys.argv) + app.setApplicationName("qapp_name") + + print() + print("With QApplication") + print("=================") + print() + print_paths() + + +if __name__ == '__main__': + main() diff --git a/.config/qutebrowser/scripts/dev/strip_whitespace.sh b/.config/qutebrowser/scripts/dev/strip_whitespace.sh new file mode 100644 index 0000000..ee14278 --- /dev/null +++ b/.config/qutebrowser/scripts/dev/strip_whitespace.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +# Strip trailing whitespace from files in this repo + +find qutebrowser scripts tests \ + -type f \( \ + -name '*.py' -o \ + -name '*.feature' -o \ + -name '*.sh' \ + \) -exec sed -i 's/ \+$//' {} + diff --git a/.config/qutebrowser/scripts/dev/ua_fetch.py b/.config/qutebrowser/scripts/dev/ua_fetch.py new file mode 100755 index 0000000..75ce4c2 --- /dev/null +++ b/.config/qutebrowser/scripts/dev/ua_fetch.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python3 +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2015-2018 lamarpavel +# Copyright 2015-2018 Alexey Nabrodov (Averrin) +# Copyright 2015-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org> +# +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. + + +"""Fetch list of popular user-agents. + +The script is based on a gist posted by github.com/averrin, the output of this +script is formatted to be pasted into configdata.yml +""" + +import requests +from lxml import html # pylint: disable=import-error + + +def fetch(): + """Fetch list of popular user-agents. + + Return: + List of relevant strings. + """ + url = 'https://techblog.willshouse.com/2012/01/03/most-common-user-agents/' + page = requests.get(url) + page = html.fromstring(page.text) + path = '//*[@id="post-2229"]/div[2]/table/tbody' + return page.xpath(path)[0] + + +def filter_list(complete_list, browsers): + """Filter the received list based on a look up table. + + The LUT should be a dictionary of the format {browser: versions}, where + 'browser' is the name of the browser (eg. "Firefox") as string and + 'versions' is a set of different versions of this browser that should be + included when found (eg. {"Linux", "MacOSX"}). This function returns a + dictionary with the same keys as the LUT, but storing lists of tuples + (user_agent, browser_description) as values. + """ + # pylint: disable=too-many-nested-blocks + table = {} + for entry in complete_list: + # Tuple of (user_agent, browser_description) + candidate = (entry[1].text_content(), entry[2].text_content()) + for name in browsers: + found = False + if name.lower() in candidate[1].lower(): + for version in browsers[name]: + if version.lower() in candidate[1].lower(): + if table.get(name) is None: + table[name] = [] + table[name].append(candidate) + browsers[name].remove(version) + found = True + break + if found: + break + return table + + +def add_diversity(table): + """Insert a few additional entries for diversity into the dict. + + (as returned by filter_list()) + """ + table["Obscure"] = [ + ('Mozilla/5.0 (compatible; Googlebot/2.1; ' + '+http://www.google.com/bot.html', + "Google Bot"), + ('Wget/1.16.1 (linux-gnu)', + "wget 1.16.1"), + ('curl/7.40.0', + "curl 7.40.0"), + ('Mozilla/5.0 (Linux; U; Android 7.1.2) AppleWebKit/534.30 ' + '(KHTML, like Gecko) Version/4.0 Mobile Safari/534.30', + "Mobile Generic Android"), + ('Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like ' + 'Gecko', + "IE 11.0 for Desktop Win7 64-bit"), + ] + return table + + +def main(): + """Generate user agent code.""" + fetched = fetch() + lut = { + "Firefox": {"Win", "MacOSX", "Linux", "Android"}, + "Chrome": {"Win", "MacOSX", "Linux"}, + "Safari": {"MacOSX", "iOS"} + } + filtered = filter_list(fetched, lut) + filtered = add_diversity(filtered) + + tab = " " + for browser in ["Firefox", "Safari", "Chrome", "Obscure"]: + for it in filtered[browser]: + print('{}- - "{}"'.format(3 * tab, it[0])) + desc = it[1].replace('\xa0', ' ').replace(' ', ' ') + print("{}- {}".format(4 * tab, desc)) + print("") + + +if __name__ == '__main__': + main() diff --git a/.config/qutebrowser/scripts/dev/update_3rdparty.py b/.config/qutebrowser/scripts/dev/update_3rdparty.py new file mode 100755 index 0000000..c40015d --- /dev/null +++ b/.config/qutebrowser/scripts/dev/update_3rdparty.py @@ -0,0 +1,166 @@ +#!/usr/bin/env python3 +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2015 Daniel Schadt +# Copyright 2016-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org> +# +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. + +"""Update all third-party-modules.""" + +import argparse +import urllib.request +import urllib.error +import shutil +import json +import os +import sys + +sys.path.insert( + 0, os.path.join(os.path.dirname(__file__), os.pardir, os.pardir)) +from scripts import dictcli +from qutebrowser.config import configdata + + +def get_latest_pdfjs_url(): + """Get the URL of the latest pdf.js prebuilt package. + + Returns a (version, url)-tuple. + """ + github_api = 'https://api.github.com' + endpoint = 'repos/mozilla/pdf.js/releases/latest' + request_url = '{}/{}'.format(github_api, endpoint) + with urllib.request.urlopen(request_url) as fp: + data = json.loads(fp.read().decode('utf-8')) + + download_url = data['assets'][0]['browser_download_url'] + version_name = data['name'] + return (version_name, download_url) + + +def update_pdfjs(target_version=None): + """Download and extract the latest pdf.js version. + + If target_version is not None, download the given version instead. + + Args: + target_version: None or version string ('x.y.z') + """ + if target_version is None: + version, url = get_latest_pdfjs_url() + else: + # We need target_version as x.y.z, without the 'v' prefix, though the + # user might give it on the command line + if target_version.startswith('v'): + target_version = target_version[1:] + # version should have the prefix to be consistent with the return value + # of get_latest_pdfjs_url() + version = 'v' + target_version + url = ('https://github.com/mozilla/pdf.js/releases/download/' + 'v{0}/pdfjs-{0}-dist.zip').format(target_version) + + os.chdir(os.path.join(os.path.dirname(os.path.abspath(__file__)), + '..', '..')) + target_path = os.path.join('qutebrowser', '3rdparty', 'pdfjs') + print("=> Downloading pdf.js {}".format(version)) + try: + (archive_path, _headers) = urllib.request.urlretrieve(url) + except urllib.error.HTTPError as error: + print("Could not retrieve pdfjs {}: {}".format(version, error)) + return + if os.path.isdir(target_path): + print("Removing old version in {}".format(target_path)) + shutil.rmtree(target_path) + os.makedirs(target_path) + print("Extracting new version") + with open(archive_path, 'rb') as archive: + shutil.unpack_archive(archive, target_path, 'zip') + urllib.request.urlcleanup() + + +def update_dmg_makefile(): + """Update fancy-dmg Makefile. + + See https://el-tramo.be/blog/fancy-dmg/ + """ + print("Updating fancy-dmg Makefile...") + url = 'https://raw.githubusercontent.com/remko/fancy-dmg/master/Makefile' + target_path = os.path.join('scripts', 'dev', 'Makefile-dmg') + urllib.request.urlretrieve(url, target_path) + urllib.request.urlcleanup() + + +def update_ace(): + """Update ACE. + + See https://ace.c9.io/ and https://github.com/ajaxorg/ace-builds/ + """ + print("Updating ACE...") + url = 'https://raw.githubusercontent.com/ajaxorg/ace-builds/master/src/ace.js' + target_path = os.path.join('tests', 'end2end', 'data', 'hints', 'ace', + 'ace.js') + urllib.request.urlretrieve(url, target_path) + urllib.request.urlcleanup() + + +def test_dicts(): + """Test available dictionaries.""" + configdata.init() + for lang in dictcli.available_languages(): + print('Testing dictionary {}... '.format(lang.code), end='') + lang_url = urllib.parse.urljoin(dictcli.API_URL, lang.remote_path) + request = urllib.request.Request(lang_url, method='HEAD') + response = urllib.request.urlopen(request) + if response.status == 200: + print('OK') + else: + print('ERROR: {}'.format(response.status)) + + +def run(ace=False, pdfjs=True, fancy_dmg=False, pdfjs_version=None, + dicts=False): + """Update components based on the given arguments.""" + if pdfjs: + update_pdfjs(pdfjs_version) + if ace: + update_ace() + if fancy_dmg: + update_dmg_makefile() + if dicts: + test_dicts() + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument( + '--pdfjs', '-p', + help='Specify pdfjs version. If not given, ' + 'the latest version is used.', + required=False, metavar='VERSION') + parser.add_argument('--fancy-dmg', help="Update fancy-dmg Makefile", + action='store_true') + parser.add_argument( + '--dicts', '-d', + help='Test whether all available dictionaries ' + 'can be reached at the remote repository.', + required=False, action='store_true') + args = parser.parse_args() + run(ace=True, pdfjs=True, fancy_dmg=args.fancy_dmg, + pdfjs_version=args.pdfjs, dicts=args.dicts) + + +if __name__ == '__main__': + main() diff --git a/.config/qutebrowser/scripts/dictcli.py b/.config/qutebrowser/scripts/dictcli.py new file mode 100755 index 0000000..4017159 --- /dev/null +++ b/.config/qutebrowser/scripts/dictcli.py @@ -0,0 +1,283 @@ +#!/usr/bin/env python3 +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2017-2018 Michal Siedlaczek <michal.siedlaczek@gmail.com> + +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. + +"""A script installing Hunspell dictionaries. + +Use: python -m scripts.dictcli [-h] {list,update,remove-old,install} ... +""" + +import argparse +import base64 +import json +import os +import sys +import re +import urllib.request + +import attr + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir)) +from qutebrowser.browser.webengine import spell +from qutebrowser.config import configdata +from qutebrowser.utils import standarddir + + +API_URL = 'https://chromium.googlesource.com/chromium/deps/hunspell_dictionaries.git/+/master/' + + +class InvalidLanguageError(Exception): + + """Raised when requesting invalid languages.""" + + def __init__(self, invalid_langs): + msg = 'invalid languages: {}'.format(', '.join(invalid_langs)) + super().__init__(msg) + + +@attr.s +class Language: + + """Dictionary language specs.""" + + code = attr.ib() + name = attr.ib() + remote_filename = attr.ib() + local_filename = attr.ib(default=None) + _file_extension = attr.ib('bdic', init=False) + + def __attrs_post_init__(self): + if self.local_filename is None: + self.local_filename = spell.local_filename(self.code) + + @property + def remote_path(self): + """Resolve the filename with extension the remote dictionary.""" + return '.'.join([self.remote_filename, self._file_extension]) + + @property + def local_path(self): + """Resolve the filename with extension the local dictionary.""" + if self.local_filename is None: + return None + return '.'.join([self.local_filename, self._file_extension]) + + @property + def remote_version(self): + """Resolve the version of the local dictionary.""" + return spell.version(self.remote_path) + + @property + def local_version(self): + """Resolve the version of the local dictionary.""" + local_path = self.local_path + if local_path is None: + return None + return spell.version(local_path) + + +def get_argparser(): + """Get the argparse parser.""" + desc = 'Install and manage Hunspell dictionaries for QtWebEngine.' + parser = argparse.ArgumentParser(prog='dictcli', + description=desc) + subparsers = parser.add_subparsers(help='Command', dest='cmd') + subparsers.required = True + subparsers.add_parser('list', + help='Display the list of available languages.') + subparsers.add_parser('update', + help='Update dictionaries') + subparsers.add_parser('remove-old', + help='Remove old versions of dictionaries.') + + install_parser = subparsers.add_parser('install', + help='Install dictionaries') + install_parser.add_argument('language', + nargs='*', + help="A list of languages to install.") + + return parser + + +def version_str(version): + return '.'.join(str(n) for n in version) + + +def print_list(languages): + """Print the list of available languages.""" + pat = '{:<7}{:<26}{:<8}{:<5}' + print(pat.format('Code', 'Name', 'Version', 'Installed')) + for lang in languages: + remote_version = version_str(lang.remote_version) + local_version = '-' + if lang.local_version is not None: + local_version = version_str(lang.local_version) + if lang.local_version < lang.remote_version: + local_version += ' - update available!' + print(pat.format(lang.code, lang.name, remote_version, local_version)) + + +def valid_languages(): + """Return a mapping from valid language codes to their names.""" + option = configdata.DATA['spellcheck.languages'] + return option.typ.valtype.valid_values.descriptions + + +def parse_entry(entry): + """Parse an entry from the remote API.""" + dict_re = re.compile(r""" + (?P<filename>(?P<code>[a-z]{2}(-[A-Z]{2})?).*)\.bdic + """, re.VERBOSE) + match = dict_re.fullmatch(entry['name']) + if match is not None: + return match.group('code'), match.group('filename') + else: + return None + + +def language_list_from_api(): + """Return a JSON with a list of available languages from Google API.""" + listurl = API_URL + '?format=JSON' + response = urllib.request.urlopen(listurl) + # A special 5-byte prefix must be stripped from the response content + # See: https://github.com/google/gitiles/issues/22 + # https://github.com/google/gitiles/issues/82 + json_content = response.read()[5:] + entries = json.loads(json_content.decode('utf-8'))['entries'] + parsed_entries = [parse_entry(entry) for entry in entries] + return [entry for entry in parsed_entries if entry is not None] + + +def latest_yet(code2file, code, filename): + """Determine whether the latest version so far.""" + if code not in code2file: + return True + return spell.version(code2file[code]) < spell.version(filename) + + +def available_languages(): + """Return a list of Language objects of all available languages.""" + lang_map = valid_languages() + api_list = language_list_from_api() + code2file = {} + for code, filename in api_list: + if latest_yet(code2file, code, filename): + code2file[code] = filename + return [ + Language(code, name, code2file[code]) + for code, name in lang_map.items() + if code in code2file + ] + + +def download_dictionary(url, dest): + """Download a decoded dictionary file.""" + response = urllib.request.urlopen(url) + decoded = base64.decodebytes(response.read()) + with open(dest, 'bw') as dict_file: + dict_file.write(decoded) + + +def filter_languages(languages, selected): + """Filter a list of languages based on an inclusion list. + + Args: + languages: a list of languages to filter + selected: a list of keys to select + """ + filtered_languages = [] + for language in languages: + if language.code in selected: + filtered_languages.append(language) + selected.remove(language.code) + if selected: + raise InvalidLanguageError(selected) + return filtered_languages + + +def install_lang(lang): + """Install a single lang given by the argument.""" + lang_url = API_URL + lang.remote_path + '?format=TEXT' + if not os.path.isdir(spell.dictionary_dir()): + msg = '{} does not exist, creating the directory' + print(msg.format(spell.dictionary_dir())) + os.makedirs(spell.dictionary_dir()) + print('Downloading {}'.format(lang_url)) + dest = os.path.join(spell.dictionary_dir(), lang.remote_path) + download_dictionary(lang_url, dest) + print('Done.') + + +def install(languages): + """Install languages.""" + for lang in languages: + try: + print('Installing {}: {}'.format(lang.code, lang.name)) + install_lang(lang) + except PermissionError as e: + sys.exit(str(e)) + + +def update(languages): + """Update the given languages.""" + installed = [lang for lang in languages if lang.local_version is not None] + for lang in installed: + if lang.local_version < lang.remote_version: + print('Upgrading {} from {} to {}'.format( + lang.code, + version_str(lang.local_version), + version_str(lang.remote_version))) + install_lang(lang) + + +def remove_old(languages): + """Remove old versions of languages.""" + installed = [lang for lang in languages if lang.local_version is not None] + for lang in installed: + local_files = spell.local_files(lang.code) + for old_file in local_files[1:]: + os.remove(os.path.join(spell.dictionary_dir(), old_file)) + + +def main(): + if configdata.DATA is None: + configdata.init() + standarddir.init(None) + + parser = get_argparser() + argv = sys.argv[1:] + args = parser.parse_args(argv) + languages = available_languages() + if args.cmd == 'list': + print_list(languages) + elif args.cmd == 'update': + update(languages) + elif args.cmd == 'remove-old': + remove_old(languages) + elif not args.language: + sys.exit('You must provide a list of languages to install.') + else: + try: + install(filter_languages(languages, args.language)) + except InvalidLanguageError as e: + print(e) + + +if __name__ == '__main__': + main() diff --git a/.config/qutebrowser/scripts/hist_importer.py b/.config/qutebrowser/scripts/hist_importer.py new file mode 100755 index 0000000..914701a --- /dev/null +++ b/.config/qutebrowser/scripts/hist_importer.py @@ -0,0 +1,174 @@ +#!/usr/bin/env python3 +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2017-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org> +# Copyright 2017-2018 Josefson Souza <josefson.br@gmail.com> + +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. + + +"""Tool to import browser history from other browsers.""" + + +import argparse +import sqlite3 +import sys +import os + + +class Error(Exception): + + """Exception for errors in this module.""" + + pass + + +def parse(): + """Parse command line arguments.""" + description = ("This program is meant to extract browser history from your" + " previous browser and import them into qutebrowser.") + epilog = ("Databases:\n\n\tqutebrowser: Is named 'history.sqlite' and can " + "be found at your --basedir. In order to find where your " + "basedir is you can run ':open qute:version' inside qutebrowser." + "\n\n\tFirefox: Is named 'places.sqlite', and can be found at " + "your system's profile folder. Check this link for where it is " + "located: http://kb.mozillazine.org/Profile_folder" + "\n\n\tChrome: Is named 'History', and can be found at the " + "respective User Data Directory. Check this link for where it is" + "located: https://chromium.googlesource.com/chromium/src/+/" + "master/docs/user_data_dir.md\n\n" + "Example: hist_importer.py -b firefox -s /Firefox/Profile/" + "places.sqlite -d /qutebrowser/data/history.sqlite") + parser = argparse.ArgumentParser( + description=description, epilog=epilog, + formatter_class=argparse.RawTextHelpFormatter + ) + parser.add_argument('-b', '--browser', dest='browser', required=True, + type=str, help='Browsers: {firefox, chrome}') + parser.add_argument('-s', '--source', dest='source', required=True, + type=str, help='Source: Full path to the sqlite data' + 'base file from the source browser.') + parser.add_argument('-d', '--dest', dest='dest', required=True, type=str, + help='\nDestination: Full path to the qutebrowser ' + 'sqlite database') + return parser.parse_args() + + +def open_db(data_base): + """Open connection with database.""" + if os.path.isfile(data_base): + return sqlite3.connect(data_base) + raise Error('The file {} does not exist.'.format(data_base)) + + +def extract(source, query): + """Get records from source database. + + Args: + source: File path to the source database where we want to extract the + data from. + query: The query string to be executed in order to retrieve relevant + attributes as (datetime, url, time) from the source database according + to the browser chosen. + """ + try: + conn = open_db(source) + cursor = conn.cursor() + cursor.execute(query) + history = cursor.fetchall() + conn.close() + return history + except sqlite3.OperationalError as op_e: + raise Error('Could not perform queries on the source database: ' + '{}'.format(op_e)) + + +def clean(history): + """Clean up records from source database. + + Receives a list of record and sanityze them in order for them to be + properly imported to qutebrowser. Sanitation requires adding a 4th + attribute 'redirect' which is filled with '0's, and also purging all + records that have a NULL/None datetime attribute. + + Args: + history: List of records (datetime, url, title) from source database. + """ + # replace missing titles with an empty string + for index, record in enumerate(history): + if record[1] is None: + cleaned = list(record) + cleaned[1] = '' + history[index] = tuple(cleaned) + + nulls = [record for record in history if None in record] + for null_record in nulls: + history.remove(null_record) + history = [list(record) for record in history] + for record in history: + record.append('0') + return history + + +def insert_qb(history, dest): + """Insert history into dest database. + + Args: + history: List of records. + dest: File path to the destination database, where history will be + inserted. + """ + conn = open_db(dest) + cursor = conn.cursor() + cursor.executemany( + 'INSERT INTO History (url,title,atime,redirect) VALUES (?,?,?,?)', + history + ) + cursor.execute('DROP TABLE CompletionHistory') + conn.commit() + conn.close() + + +def run(): + """Main control flux of the script.""" + args = parse() + browser = args.browser.lower() + source, dest = args.source, args.dest + query = { + 'firefox': 'select url,title,last_visit_date/1000000 as date ' + 'from moz_places where url like "http%" or url ' + 'like "ftp%" or url like "file://%"', + 'chrome': 'select url,title,last_visit_time/10000000 as date ' + 'from urls', + } + if browser not in query: + raise Error('Sorry, the selected browser: "{}" is not ' + 'supported.'.format(browser)) + else: + history = extract(source, query[browser]) + history = clean(history) + insert_qb(history, dest) + + +def main(): + try: + run() + except Error as e: + sys.exit(str(e)) + + +if __name__ == "__main__": + main() diff --git a/.config/qutebrowser/scripts/hostblock_blame.py b/.config/qutebrowser/scripts/hostblock_blame.py new file mode 100755 index 0000000..2f68d29 --- /dev/null +++ b/.config/qutebrowser/scripts/hostblock_blame.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2014-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org> +# +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. + +"""Check by which hostblock list a host was blocked.""" + +import sys +import io +import os +import os.path +import urllib.request + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir)) +from qutebrowser.browser import adblock +from qutebrowser.config import configdata + + +def main(): + """Check by which hostblock list a host was blocked.""" + if len(sys.argv) != 2: + print("Usage: {} <host>".format(sys.argv[0]), file=sys.stderr) + sys.exit(1) + + configdata.init() + + for url in configdata.DATA['content.host_blocking.lists'].default: + print("checking {}...".format(url)) + raw_file = urllib.request.urlopen(url) + byte_io = io.BytesIO(raw_file.read()) + f = adblock.get_fileobj(byte_io) + for line in f: + line = line.decode('utf-8') + if sys.argv[1] in line: + print("FOUND {} in {}:".format(sys.argv[1], url)) + print(" " + line.rstrip()) + + +if __name__ == '__main__': + main() diff --git a/.config/qutebrowser/scripts/importer.py b/.config/qutebrowser/scripts/importer.py new file mode 100755 index 0000000..eb808a6 --- /dev/null +++ b/.config/qutebrowser/scripts/importer.py @@ -0,0 +1,349 @@ +#!/usr/bin/env python3 +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2014-2018 Claude (longneck) <longneck@scratchbook.ch> +# Copyright 2014-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org> + +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. + + +"""Tool to import data from other browsers. + +Currently importing bookmarks from Netscape Bookmark files and Mozilla +profiles is supported. +""" + + +import argparse +import sqlite3 +import os +import urllib.parse +import json +import string + +browser_default_input_format = { + 'chromium': 'chrome', + 'chrome': 'chrome', + 'ie': 'netscape', + 'firefox': 'mozilla', + 'seamonkey': 'mozilla', + 'palemoon': 'mozilla', +} + + +def main(): + args = get_args() + bookmark_types = [] + output_format = None + input_format = args.input_format + if args.search_output: + bookmark_types = ['search'] + if args.oldconfig: + output_format = 'oldsearch' + else: + output_format = 'search' + else: + if args.bookmark_output: + output_format = 'bookmark' + elif args.quickmark_output: + output_format = 'quickmark' + if args.import_bookmarks: + bookmark_types.append('bookmark') + if args.import_keywords: + bookmark_types.append('keyword') + if not bookmark_types: + bookmark_types = ['bookmark', 'keyword'] + if not output_format: + output_format = 'quickmark' + if not input_format: + if args.browser: + input_format = browser_default_input_format[args.browser] + else: + #default to netscape + input_format = 'netscape' + + import_function = { + 'netscape': import_netscape_bookmarks, + 'mozilla': import_moz_places, + 'chrome': import_chrome, + } + import_function[input_format](args.bookmarks, bookmark_types, + output_format) + + +def get_args(): + """Get the argparse parser.""" + parser = argparse.ArgumentParser( + epilog="To import bookmarks from Chromium, Firefox or IE, " + "export them to HTML in your browsers bookmark manager. ") + parser.add_argument( + 'browser', + help="Which browser? {%(choices)s}", + choices=browser_default_input_format.keys(), + nargs='?', + metavar='browser') + parser.add_argument( + '-i', + '--input-format', + help='Which input format? (overrides browser default; "netscape" if ' + 'neither given)', + choices=set(browser_default_input_format.values()), + required=False) + parser.add_argument( + '-b', + '--bookmark-output', + help="Output in bookmark format.", + action='store_true', + default=False, + required=False) + parser.add_argument( + '-q', + '--quickmark-output', + help="Output in quickmark format (default).", + action='store_true', + default=False, + required=False) + parser.add_argument( + '-s', + '--search-output', + help="Output config.py search engine format (negates -B and -K)", + action='store_true', + default=False, + required=False) + parser.add_argument( + '--oldconfig', + help="Output search engine format for old qutebrowser.conf format", + default=False, + action='store_true', + required=False) + parser.add_argument( + '-B', + '--import-bookmarks', + help="Import plain bookmarks (can be combiend with -K)", + action='store_true', + default=False, + required=False) + parser.add_argument( + '-K', + '--import-keywords', + help="Import keywords (can be combined with -B)", + action='store_true', + default=False, + required=False) + parser.add_argument( + 'bookmarks', + help="Bookmarks file (html format) or " + "profile folder (Mozilla format)") + args = parser.parse_args() + return args + + +def search_escape(url): + """Escape URLs such that preexisting { and } are handled properly. + + Will obviously trash a properly-formatted qutebrowser URL. + """ + return url.replace('{', '{{').replace('}', '}}') + + +def opensearch_convert(url): + """Convert a basic OpenSearch URL into something qutebrowser can use. + + Exceptions: + KeyError: + An unknown and required parameter is present in the URL. This + usually means there's browser/addon specific functionality needed + to build the URL (I'm looking at you and your browser, Google) that + obviously won't be present here. + """ + subst = { + 'searchTerms': '%s', # for proper escaping later + 'language': '*', + 'inputEncoding': 'UTF-8', + 'outputEncoding': 'UTF-8' + } + + # remove optional parameters (even those we don't support) + for param in string.Formatter().parse(url): + if param[1]: + if param[1].endswith('?'): + url = url.replace('{' + param[1] + '}', '') + elif param[2] and param[2].endswith('?'): + url = url.replace('{' + param[1] + ':' + param[2] + '}', '') + return search_escape(url.format(**subst)).replace('%s', '{}') + + +def import_netscape_bookmarks(bookmarks_file, bookmark_types, output_format): + """Import bookmarks from a NETSCAPE-Bookmark-file v1. + + Generated by Chromium, Firefox, IE and possibly more browsers. Not all + export all possible bookmark types: + - Firefox mostly works with everything + - Chrome doesn't support keywords at all; searches are a separate + database + """ + import bs4 + with open(bookmarks_file, encoding='utf-8') as f: + soup = bs4.BeautifulSoup(f, 'html.parser') + bookmark_query = { + 'search': lambda tag: ( + (tag.name == 'a') and + ('shortcuturl' in tag.attrs) and + ('%s' in tag['href'])), + 'keyword': lambda tag: ( + (tag.name == 'a') and + ('shortcuturl' in tag.attrs) and + ('%s' not in tag['href'])), + 'bookmark': lambda tag: ( + (tag.name == 'a') and + ('shortcuturl' not in tag.attrs) and + (tag.string)), + } + output_template = { + 'search': { + 'search': + "c.url.searchengines['{tag[shortcuturl]}'] = " + "'{tag[href]}' #{tag.string}" + }, + 'oldsearch': { + 'search': '{tag[shortcuturl]} = {tag[href]} #{tag.string}', + }, + 'bookmark': { + 'bookmark': '{tag[href]} {tag.string}', + 'keyword': '{tag[href]} {tag.string}' + }, + 'quickmark': { + 'bookmark': '{tag.string} {tag[href]}', + 'keyword': '{tag[shortcuturl]} {tag[href]}' + } + } + bookmarks = [] + for typ in bookmark_types: + tags = soup.findAll(bookmark_query[typ]) + for tag in tags: + if typ == 'search': + tag['href'] = search_escape(tag['href']).replace('%s', '{}') + if tag['href'] not in bookmarks: + bookmarks.append( + output_template[output_format][typ].format(tag=tag)) + for bookmark in bookmarks: + print(bookmark) + + +def import_moz_places(profile, bookmark_types, output_format): + """Import bookmarks from a Mozilla profile's places.sqlite database.""" + place_query = { + 'bookmark': ( + "SELECT DISTINCT moz_bookmarks.title,moz_places.url " + "FROM moz_bookmarks,moz_places " + "WHERE moz_places.id=moz_bookmarks.fk " + "AND moz_places.id NOT IN (SELECT place_id FROM moz_keywords) " + "AND moz_places.url NOT LIKE 'place:%';" + ), # Bookmarks with no keywords assigned + 'keyword': ( + "SELECT moz_keywords.keyword,moz_places.url " + "FROM moz_keywords,moz_places,moz_bookmarks " + "WHERE moz_places.id=moz_bookmarks.fk " + "AND moz_places.id=moz_keywords.place_id " + "AND moz_places.url NOT LIKE '%!%s%' ESCAPE '!';" + ), # Bookmarks with keywords assigned but no %s substitution + 'search': ( + "SELECT moz_keywords.keyword, " + " moz_bookmarks.title, " + " search_conv(moz_places.url) AS url " + "FROM moz_keywords,moz_places,moz_bookmarks " + "WHERE moz_places.id=moz_bookmarks.fk " + "AND moz_places.id=moz_keywords.place_id " + "AND moz_places.url LIKE '%!%s%' ESCAPE '!';" + ) # bookmarks with keyword and %s substitution + } + out_template = { + 'bookmark': { + 'bookmark': '{url} {title}', + 'keyword': '{url} {keyword}' + }, + 'quickmark': { + 'bookmark': '{title} {url}', + 'keyword': '{keyword} {url}' + }, + 'oldsearch': { + 'search': '{keyword} {url} #{title}' + }, + 'search': { + 'search': "c.url.searchengines['{keyword}'] = '{url}' #{title}" + } + } + + def search_conv(url): + return search_escape(url).replace('%s', '{}') + + places = sqlite3.connect(os.path.join(profile, "places.sqlite")) + places.create_function('search_conv', 1, search_conv) + places.row_factory = sqlite3.Row + c = places.cursor() + for typ in bookmark_types: + c.execute(place_query[typ]) + for row in c: + print(out_template[output_format][typ].format(**row)) + + +def import_chrome(profile, bookmark_types, output_format): + """Import bookmarks and search keywords from Chrome-type profiles. + + On Chrome, keywords and search engines are the same thing and handled in + their own database table; bookmarks cannot have associated keywords. This + is why the dictionary lookups here are much simpler. + """ + out_template = { + 'bookmark': '{url} {name}', + 'quickmark': '{name} {url}', + 'search': "c.url.searchengines['{keyword}'] = '{url}'", + 'oldsearch': '{keyword} {url}' + } + + if 'search' in bookmark_types: + webdata = sqlite3.connect(os.path.join(profile, 'Web Data')) + c = webdata.cursor() + c.execute('SELECT keyword,url FROM keywords;') + for keyword, url in c: + try: + url = opensearch_convert(url) + print(out_template[output_format].format( + keyword=keyword, url=url)) + except KeyError: + print('# Unsupported parameter in url for {}; skipping....'. + format(keyword)) + + else: + with open(os.path.join(profile, 'Bookmarks'), encoding='utf-8') as f: + bookmarks = json.load(f) + + def bm_tree_walk(bm, template): + """Recursive function to walk through bookmarks.""" + assert 'type' in bm, bm + if bm['type'] == 'url': + if urllib.parse.urlparse(bm['url']).scheme != 'chrome': + print(template.format(**bm)) + elif bm['type'] == 'folder': + for child in bm['children']: + bm_tree_walk(child, template) + + for root in bookmarks['roots'].values(): + bm_tree_walk(root, out_template[output_format]) + + +if __name__ == '__main__': + main() diff --git a/.config/qutebrowser/scripts/keytester.py b/.config/qutebrowser/scripts/keytester.py new file mode 100755 index 0000000..ee5eb34 --- /dev/null +++ b/.config/qutebrowser/scripts/keytester.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2014-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org> + +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. + +"""Small test script to show key presses. + +Use python3 -m scripts.keytester to launch it. +""" + +from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QHBoxLayout + +from qutebrowser.keyinput import keyutils + + +class KeyWidget(QWidget): + + """Widget displaying key presses.""" + + def __init__(self, parent=None): + super().__init__(parent) + self._layout = QHBoxLayout(self) + self._label = QLabel(text="Waiting for keypress...") + self._layout.addWidget(self._label) + + def keyPressEvent(self, e): + """Show pressed keys.""" + lines = [ + str(keyutils.KeyInfo.from_event(e)), + '', + 'key: 0x{:x}'.format(int(e.key())), + 'modifiers: 0x{:x}'.format(int(e.modifiers())), + 'text: {!r}'.format(e.text()), + ] + self._label.setText('\n'.join(lines)) + + +app = QApplication([]) +w = KeyWidget() +w.show() +app.exec_() diff --git a/.config/qutebrowser/scripts/link_pyqt.py b/.config/qutebrowser/scripts/link_pyqt.py new file mode 100755 index 0000000..ae7eaa6 --- /dev/null +++ b/.config/qutebrowser/scripts/link_pyqt.py @@ -0,0 +1,233 @@ +#!/usr/bin/env python3 +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2014-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org> + +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. + +"""Symlink PyQt into a given virtualenv.""" + +import os +import os.path +import argparse +import shutil +import sys +import subprocess +import tempfile +import filecmp + + +class Error(Exception): + + """Exception raised when linking fails.""" + + pass + + +def run_py(executable, *code): + """Run the given python code with the given executable.""" + if os.name == 'nt' and len(code) > 1: + # Windows can't do newlines in arguments... + oshandle, filename = tempfile.mkstemp() + with os.fdopen(oshandle, 'w') as f: + f.write('\n'.join(code)) + cmd = [executable, filename] + try: + ret = subprocess.run(cmd, universal_newlines=True, check=True, + stdout=subprocess.PIPE).stdout + finally: + os.remove(filename) + else: + cmd = [executable, '-c', '\n'.join(code)] + ret = subprocess.run(cmd, universal_newlines=True, check=True, + stdout=subprocess.PIPE).stdout + return ret.rstrip() + + +def verbose_copy(src, dst, *, follow_symlinks=True): + """Copy function for shutil.copytree which prints copied files.""" + if '-v' in sys.argv: + print('{} -> {}'.format(src, dst)) + shutil.copy(src, dst, follow_symlinks=follow_symlinks) + + +def get_ignored_files(directory, files): + """Get the files which should be ignored for link_pyqt() on Windows.""" + needed_exts = ('.py', '.dll', '.pyd', '.so') + ignored_dirs = ('examples', 'qml', 'uic', 'doc') + filtered = [] + for f in files: + ext = os.path.splitext(f)[1] + full_path = os.path.join(directory, f) + if os.path.isdir(full_path) and f in ignored_dirs: + filtered.append(f) + elif (ext not in needed_exts) and os.path.isfile(full_path): + filtered.append(f) + return filtered + + +def needs_update(source, dest): + """Check if a file to be linked/copied needs to be updated.""" + if os.path.islink(dest): + # No need to delete a link and relink -> skip this + return False + elif os.path.isdir(dest): + diffs = filecmp.dircmp(source, dest) + ignored = get_ignored_files(source, diffs.left_only) + has_new_files = set(ignored) != set(diffs.left_only) + return (has_new_files or diffs.right_only or diffs.common_funny or + diffs.diff_files or diffs.funny_files) + else: + return not filecmp.cmp(source, dest) + + +def get_lib_path(executable, name, required=True): + """Get the path of a python library. + + Args: + executable: The Python executable to use. + name: The name of the library to get the path for. + required: Whether Error should be raised if the lib was not found. + """ + code = [ + 'try:', + ' import {}'.format(name), + 'except ImportError as e:', + ' print("ImportError: " + str(e))', + 'else:', + ' print("path: " + {}.__file__)'.format(name) + ] + output = run_py(executable, *code) + + try: + prefix, data = output.split(': ') + except ValueError: + raise ValueError("Unexpected output: {!r}".format(output)) + + if prefix == 'path': + return data + elif prefix == 'ImportError': + if required: + raise Error("Could not import {} with {}: {}!".format( + name, executable, data)) + else: + return None + else: + raise ValueError("Unexpected output: {!r}".format(output)) + + +def link_pyqt(executable, venv_path): + """Symlink the systemwide PyQt/sip into the venv. + + Args: + executable: The python executable where the source files are present. + venv_path: The path to the virtualenv site-packages. + """ + try: + get_lib_path(executable, 'PyQt5.sip') + except Error: + # There is no PyQt5.sip, so we need to copy the toplevel sip. + sip_file = get_lib_path(executable, 'sip') + else: + # There is a PyQt5.sip, it'll get copied with the PyQt5 dir. + sip_file = None + + sipconfig_file = get_lib_path(executable, 'sipconfig', required=False) + pyqt_dir = os.path.dirname(get_lib_path(executable, 'PyQt5.QtCore')) + + for path in [sip_file, sipconfig_file, pyqt_dir]: + if path is None: + continue + + fn = os.path.basename(path) + dest = os.path.join(venv_path, fn) + + if os.path.exists(dest): + if needs_update(path, dest): + remove(dest) + else: + continue + + copy_or_link(path, dest) + + +def copy_or_link(source, dest): + """Copy or symlink source to dest.""" + if os.name == 'nt': + if os.path.isdir(source): + print('{} -> {}'.format(source, dest)) + shutil.copytree(source, dest, ignore=get_ignored_files, + copy_function=verbose_copy) + else: + print('{} -> {}'.format(source, dest)) + shutil.copy(source, dest) + else: + print('{} -> {}'.format(source, dest)) + os.symlink(source, dest) + + +def remove(filename): + """Remove a given filename, regardless of whether it's a file or dir.""" + if os.path.isdir(filename): + shutil.rmtree(filename) + else: + os.unlink(filename) + + +def get_venv_lib_path(path): + """Get the library path of a virtualenv.""" + subdir = 'Scripts' if os.name == 'nt' else 'bin' + executable = os.path.join(path, subdir, 'python') + return run_py(executable, + 'from distutils.sysconfig import get_python_lib', + 'print(get_python_lib())') + + +def get_tox_syspython(tox_path): + """Get the system python based on a virtualenv created by tox.""" + path = os.path.join(tox_path, '.tox-config1') + with open(path, encoding='ascii') as f: + line = f.readline() + _md5, sys_python = line.rstrip().split(' ', 1) + return sys_python + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('path', help="Base path to the venv.") + parser.add_argument('--tox', help="Add when called via tox.", + action='store_true') + args = parser.parse_args() + + if args.tox: + # Workaround for the lack of negative factors in tox.ini + if 'LINK_PYQT_SKIP' in os.environ: + print('LINK_PYQT_SKIP set, exiting...') + sys.exit(0) + executable = get_tox_syspython(args.path) + else: + executable = sys.executable + + venv_path = get_venv_lib_path(args.path) + link_pyqt(executable, venv_path) + + +if __name__ == '__main__': + try: + main() + except Error as e: + print(str(e), file=sys.stderr) + sys.exit(1) diff --git a/.config/qutebrowser/scripts/open_url_in_instance.sh b/.config/qutebrowser/scripts/open_url_in_instance.sh new file mode 100755 index 0000000..a6ce0ed --- /dev/null +++ b/.config/qutebrowser/scripts/open_url_in_instance.sh @@ -0,0 +1,15 @@ +#!/bin/bash +# initial idea: Florian Bruhin (The-Compiler) +# author: Thore Bödecker (foxxx0) + +_url="$1" +_qb_version='1.0.4' +_proto_version=1 +_ipc_socket="${XDG_RUNTIME_DIR}/qutebrowser/ipc-$(echo -n "$USER" | md5sum | cut -d' ' -f1)" +_qute_bin="/usr/bin/qutebrowser" + +printf '{"args": ["%s"], "target_arg": null, "version": "%s", "protocol_version": %d, "cwd": "%s"}\n' \ + "${_url}" \ + "${_qb_version}" \ + "${_proto_version}" \ + "${PWD}" | socat - UNIX-CONNECT:"${_ipc_socket}" 2>/dev/null || "$_qute_bin" "$@" & diff --git a/.config/qutebrowser/scripts/setupcommon.py b/.config/qutebrowser/scripts/setupcommon.py new file mode 100755 index 0000000..50eabac --- /dev/null +++ b/.config/qutebrowser/scripts/setupcommon.py @@ -0,0 +1,74 @@ +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2014-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org> + +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. + + +"""Data used by setup.py and the PyInstaller qutebrowser.spec.""" + +import sys +import os +import os.path +import subprocess +sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir)) + + +if sys.hexversion >= 0x03000000: + open_file = open +else: + import codecs + open_file = codecs.open + + +BASEDIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), + os.path.pardir) + + +def _git_str(): + """Try to find out git version. + + Return: + string containing the git commit ID and timestamp. + None if there was an error or we're not in a git repo. + """ + if BASEDIR is None: + return None + if not os.path.isdir(os.path.join(BASEDIR, ".git")): + return None + try: + # https://stackoverflow.com/questions/21017300/21017394#21017394 + commit_hash = subprocess.run( + ['git', 'describe', '--match=NeVeRmAtCh', '--always', '--dirty'], + cwd=BASEDIR, check=True, + stdout=subprocess.PIPE).stdout.decode('UTF-8').strip() + date = subprocess.run( + ['git', 'show', '-s', '--format=%ci', 'HEAD'], + cwd=BASEDIR, check=True, + stdout=subprocess.PIPE).stdout.decode('UTF-8').strip() + return '{} ({})'.format(commit_hash, date) + except (subprocess.CalledProcessError, OSError): + return None + + +def write_git_file(): + """Write the git-commit-id file with the current commit.""" + gitstr = _git_str() + if gitstr is None: + gitstr = '' + path = os.path.join(BASEDIR, 'qutebrowser', 'git-commit-id') + with open_file(path, 'w', encoding='ascii') as f: + f.write(gitstr) diff --git a/.config/qutebrowser/scripts/testbrowser/cpp/webengine/main.cpp b/.config/qutebrowser/scripts/testbrowser/cpp/webengine/main.cpp new file mode 100644 index 0000000..311432e --- /dev/null +++ b/.config/qutebrowser/scripts/testbrowser/cpp/webengine/main.cpp @@ -0,0 +1,13 @@ +#include <QApplication> +#include <QWebEngineView> +#include <QUrl> + + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + QWebEngineView view; + view.load(QUrl(argv[1])); + view.show(); + return app.exec(); +} diff --git a/.config/qutebrowser/scripts/testbrowser/cpp/webengine/testbrowser.pro b/.config/qutebrowser/scripts/testbrowser/cpp/webengine/testbrowser.pro new file mode 100644 index 0000000..12a1cf7 --- /dev/null +++ b/.config/qutebrowser/scripts/testbrowser/cpp/webengine/testbrowser.pro @@ -0,0 +1,6 @@ +QT += core widgets webenginewidgets + +TARGET = testbrowser +TEMPLATE = app + +SOURCES += main.cpp diff --git a/.config/qutebrowser/scripts/testbrowser/cpp/webkit/main.cpp b/.config/qutebrowser/scripts/testbrowser/cpp/webkit/main.cpp new file mode 100644 index 0000000..06c3d1a --- /dev/null +++ b/.config/qutebrowser/scripts/testbrowser/cpp/webkit/main.cpp @@ -0,0 +1,13 @@ +#include <QApplication> +#include <QWebView> +#include <QUrl> + + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + QWebView view; + view.load(QUrl(argv[1])); + view.show(); + return app.exec(); +} diff --git a/.config/qutebrowser/scripts/testbrowser/cpp/webkit/testbrowser.pro b/.config/qutebrowser/scripts/testbrowser/cpp/webkit/testbrowser.pro new file mode 100644 index 0000000..59f55dd --- /dev/null +++ b/.config/qutebrowser/scripts/testbrowser/cpp/webkit/testbrowser.pro @@ -0,0 +1,6 @@ +QT += core widgets webkit webkitwidgets + +TARGET = testbrowser +TEMPLATE = app + +SOURCES += main.cpp diff --git a/.config/qutebrowser/scripts/testbrowser/testbrowser_webengine.py b/.config/qutebrowser/scripts/testbrowser/testbrowser_webengine.py new file mode 100755 index 0000000..fdf6728 --- /dev/null +++ b/.config/qutebrowser/scripts/testbrowser/testbrowser_webengine.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2014-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org> +# +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. + +"""Very simple browser for testing purposes.""" + +import sys +import argparse + +from PyQt5.QtCore import QUrl +from PyQt5.QtWidgets import QApplication +from PyQt5.QtWebEngineWidgets import QWebEngineView + + +def parse_args(): + """Parse commandline arguments.""" + parser = argparse.ArgumentParser() + parser.add_argument('url', help='The URL to open') + return parser.parse_known_args()[0] + + +if __name__ == '__main__': + args = parse_args() + app = QApplication(sys.argv) + wv = QWebEngineView() + + wv.loadStarted.connect(lambda: print("Loading started")) + wv.loadProgress.connect(lambda p: print("Loading progress: {}%".format(p))) + wv.loadFinished.connect(lambda: print("Loading finished")) + + wv.load(QUrl.fromUserInput(args.url)) + wv.show() + + app.exec_() diff --git a/.config/qutebrowser/scripts/testbrowser/testbrowser_webkit.py b/.config/qutebrowser/scripts/testbrowser/testbrowser_webkit.py new file mode 100755 index 0000000..73cae08 --- /dev/null +++ b/.config/qutebrowser/scripts/testbrowser/testbrowser_webkit.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2014-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org> +# +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. + +"""Very simple browser for testing purposes.""" + +import sys +import argparse + +from PyQt5.QtCore import QUrl +from PyQt5.QtWidgets import QApplication +from PyQt5.QtWebKit import QWebSettings +from PyQt5.QtWebKitWidgets import QWebView + + +def parse_args(): + """Parse commandline arguments.""" + parser = argparse.ArgumentParser() + parser.add_argument('url', help='The URL to open') + parser.add_argument('--plugins', '-p', help='Enable plugins', + default=False, action='store_true') + return parser.parse_known_args()[0] + + +if __name__ == '__main__': + args = parse_args() + app = QApplication(sys.argv) + wv = QWebView() + + wv.loadStarted.connect(lambda: print("Loading started")) + wv.loadProgress.connect(lambda p: print("Loading progress: {}%".format(p))) + wv.loadFinished.connect(lambda: print("Loading finished")) + + if args.plugins: + wv.settings().setAttribute(QWebSettings.PluginsEnabled, True) + + wv.load(QUrl.fromUserInput(args.url)) + wv.show() + + app.exec_() diff --git a/.config/qutebrowser/scripts/utils.py b/.config/qutebrowser/scripts/utils.py new file mode 100755 index 0000000..9a1a751 --- /dev/null +++ b/.config/qutebrowser/scripts/utils.py @@ -0,0 +1,103 @@ +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2014-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org> + +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. + +"""Utility functions for scripts.""" + +import os +import os.path + + +# Import side-effects are an evil thing, but here it's okay so scripts using +# colors work on Windows as well. +try: + import colorama +except ImportError: + colorama = None +else: + colorama.init() + + +use_color = os.name != 'nt' or colorama + + +fg_colors = { + 'black': 30, + 'red': 31, + 'green': 32, + 'yellow': 33, + 'blue': 34, + 'magenta': 35, + 'cyan': 36, + 'white': 37, + 'reset': 39, +} + + +bg_colors = {name: col + 10 for name, col in fg_colors.items()} + + +term_attributes = { + 'bright': 1, + 'dim': 2, + 'normal': 22, + 'reset': 0, +} + + +def _esc(code): + """Get an ANSI color code based on a color number.""" + return '\033[{}m'.format(code) + + +def print_col(text, color): + """Print a colorized text.""" + if use_color: + fg = _esc(fg_colors[color.lower()]) + reset = _esc(fg_colors['reset']) + print(''.join([fg, text, reset])) + else: + print(text) + + +def print_title(text): + """Print a title.""" + print_col("==================== {} ====================".format(text), + 'yellow') + + +def print_subtitle(text): + """Print a subtitle.""" + print_col("------ {} ------".format(text), 'cyan') + + +def print_bold(text): + """Print a bold text.""" + if use_color: + bold = _esc(term_attributes['bright']) + reset = _esc(term_attributes['reset']) + print(''.join([bold, text, reset])) + else: + print(text) + + +def change_cwd(): + """Change the scripts cwd if it was started inside the script folder.""" + cwd = os.getcwd() + if os.path.split(cwd)[1] == 'scripts': + os.chdir(os.path.join(cwd, os.pardir)) |
