Either get a tarball (nyxt-<version>-source-with-submodules.tar.xz) from a
tagged release, or clone as a git repository:
mkdir -p ~/common-lisp
git clone --recurse-submodules https://github.com/atlas-engineer/nyxt ~/common-lisp/nyxtNyxt is written in Common Lisp. Currently, we only target one of its implementations - SBCL.
Nyxt also depends on Common Lisp libraries. These are bundled in the tarball
mentioned above or fetched as Git submodules (under ./_build).
Note for advanced users: the single source of truth for CL libraries is dictated
by the Git submodules. Any Nyxt build that deviates from it is considered
unofficial. See environment variable NYXT_SUBMODULES defined in the makefile
to override the default behavior.
Nyxt is designed to be web engine agnostic so its dependencies vary.
Using the latest WebKitGTK version is advised for security concerns. The oldest version that supports all features is 2.36.
The packages that provide the following shared objects are required:
- libwebkit2gtk-4.1.so
- libgobject-2.0.so
- libgirepository-1.0.so
- libglib-2.0.so
- libgthread-2.0.so
- libgio-2.0.so
- libcairo.so
- libpango-1.0.so
- libpangocairo-1.0.so
- libgdk_pixbuf-2.0.so
- libgdk-3.so
- libgtk-3.so
To improve media stream it is recommended to install gst-libav and the
following plugins:
- gst-plugins-bad
- gst-plugins-base
- gst-plugins-good
- gst-plugins-ugly
Experimental support for Electron. Further documentation soon.
The packages that provide the following shared objects are required:
- libssl.so.3
- libcrypto.so.3
- libfixposix.so.3
- libsqlite3.so
Additionally, the following packages:
- xclip
- when using X system;
- wl-clipboard
- when using Wayland;
- enchant
- spellchecking (optional).
Lisp favors incremental program development meaning that you make some changes and compile them. In other words, there’s no need to compile the whole codebase or even restart the program.
The typical Common Lisp IDE is SLIME (or its fork SLY), which requires being comfortable with Emacs. Add the snippet below to Emacs’ init file.
(setq slime-lisp-implementations
'((nyxt ("sbcl" "--dynamic-space-size 3072")
:env ("CL_SOURCE_REGISTRY=~/common-lisp//:~/common-lisp/nyxt/_build//"))))Start the REPL by issuing M-- M-x sly RET nyxt RET and evaluate:
(asdf:load-system :nyxt/gi-gtk)
(nyxt:start)Note that:
- ASDF must be configured to find the required systems;
cffimust be configured to find the required shared objects by setting env varLD_LIBRARY_PATHorcffi:*foreign-library-directories*.
It is recommended to restart the Lisp image before and after running the tests since some of them are stateful:
(asdf:test-system :nyxt/gi-gtk)Nyxt uses the Make build system. Run make to display the documentation or
see the Makefile for more details.
Nyxt is a joint effort and we welcome contributors! You can find tasks on our issue tracker to suit your interests and skills. Please fork the project and open a pull request (PR) on GitHub to undergo the reviewing process. Refer to the branch management section for more detailed information.
Please resist the temptation of discussing changes without drafting its implementation. Currently, we value pragmatism over creativity.
Feel free to contact us at any point if you need guidance.
- To learn Common Lisp, see https://nyxt-browser.com/learn-lisp;
- Open up an issue on GitHub;
- Find Nyxt on Libera IRC:
#nyxt - Nyxt’s Discord;
Ensure to isolate commits containing whitespace changes (including indentation) or code movements as to avoid noise in the diffs.
Regarding commit messages, we follow the convention of prefixing the title with
the basename when there’s a single modified file. For instance, for changes in
source/mode/blocker.lisp the commit message would look as per below:
mode/blocker: Short description of the change Further explanation.
Nyxt uses the following branches:
masterfor development;<feature-branches>for working on particular features;<integer>-seriesto backport commits corresponding to specific major versions.
Branch off from the target branch and rebase onto it right before merging as to avoid merge conflicts.
A commit is said to be atomic when it builds and starts Nyxt successfully. At
times, for the sake of readability, it is wise to break the changes down to
smaller non-atomic commits. In that case, a merge commit is required (use merge
option no-ff). This guarantees that running git bisect with option
--first-parent only picks atomic commits, which streamlines the process.
Those with commit access may push trivial changes directly to the target branch.
The usual style guides by Norvig & Pitman’s Tutorial on Good Lisp Programming Style and Google Common Lisp Style Guide are advised.
For symbol naming conventions, see https://www.cliki.net/Naming+conventions.
Some of our conventions include:
- Prefer
firstandrestovercarandcdr, respectively. - Use
define-classinstead ofdefclass. - Use
nyxt:define-packagefor Nyxt-related pacakges. Notice that it features default imports (e.g.export-always) and package nicknames (e.g.alex,sera, etc.). Preferuiop:define-packagefor general purpose packages. - Export using
export-alwaysnext to the symbol definition. This helps prevent exports to go out-of-sync, or catch typos. Unlikeexport,export-alwayssaves you from surprises upon recompilation. - When sensible, declaim the function types using
->. Note that there is then no need to mention the type of the arguments and the return value in the docstring. - Use the
maybeandmaybe*types instead of(or null ...)and(or null (array * (0)) ...), respectively. - Use the
list-oftype for typed lists. - Use
funcall*to not error when function does not exist. - Prefer classes over structs.
- Classes should be usable with just a
make-instance. - Slots classes should be formatted in the following way:
(slot-name
slot-value
...
:documentation "Foo.")When slot-value is the only parameter specified then:
(slot-name slot-value)customize-instanceis reserved for end users. Useinitialize-instance :afterorslot-unboundto initialize the slots. Set up the rest of the class incustomize-instance :after. Bear in mind that anything in this last method won’t be customizable for the end user.- Almost all files should be handled via the
nfileslibrary. (setf SLOT-WRITER) :afteris reserved for “watchers”, i.e. handlers that are run whenever the slot is set. The:aroundmethod is not used by watchers, and thus the watcher may be overridden.- We use the
%foo%naming convention for special local variables. - We suffix predicates with
-p. Unlike the usual convention, we always use a dash (i.e.foo-poverfoop). - Prefer the term
urloveruri. - URLs should be of type
quri:uri. If you need to manipulate a URL string, call iturl-string. In case the value contains a URL, but is notquri:url, useurl-designatorand itsurlmethod to normalize intoquri:uri. - Paths should be of type
cl:pathname. Useuiop:native-namestringto “send” to OS-facing functions,uiop:ensure-pathnameto “receive” from OS-facing functions or to “trunamize”. - Prefer
handler-bindoverhandler-case: when running from the REPL, this triggers the debugger with a full stacktrace; when running the Nyxt binary, all conditions are caught anyway. - Do not handle the
Tcondition, this may break everything. Handleerror,serious-condition, or exceptionallycondition(for instance if you do not control the called code, and some libraries subclassconditioninstead oferror). - Dummy variables are called
_. - Prefer American spelling.
- Construct
define-commandrequires a short one-line docstring without newlines. - Name keyword function parameters as follows
&key (var default-value var-supplied-p).