Merge pull request #160 from list-jonas/main

This commit is contained in:
mr. m 2025-04-16 17:12:05 +02:00 committed by GitHub
commit 5e6431f711
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
382 changed files with 3243 additions and 155473 deletions

3
.eslintrc.json Normal file
View file

@ -0,0 +1,3 @@
{
"extends": ["next/core-web-vitals", "next/typescript"]
}

1
.gitattributes vendored
View file

@ -1 +0,0 @@
* text=auto eol=lf

View file

@ -1,40 +0,0 @@
---
name: Bug report
about: Something about Quartz isn't working the way you expect
title: ""
labels: bug
assignees: ""
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots and Source**
If applicable, add screenshots to help explain your problem.
You can help speed up fixing the problem by either
1. providing a simple reproduction
2. linking to your Quartz repository where the problem can be observed
**Desktop (please complete the following information):**
- Quartz Version: [e.g. v4.1.2]
- `node` Version: [e.g. v18.16]
- `npm` version: [e.g. v10.1.0]
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
**Additional context**
Add any other context about the problem here.

View file

@ -1,19 +0,0 @@
---
name: Feature request
about: Suggest an idea or improvement for Quartz
title: ""
labels: enhancement
assignees: ""
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View file

@ -1,6 +0,0 @@
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"

View file

@ -1,53 +0,0 @@
name: Deploy Quartz site to GitHub Pages
on:
push:
branches:
- main
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: "pages"
cancel-in-progress: false
jobs:
build:
runs-on: ubuntu-22.04
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
- name: Install dependencies
run: npm ci
- name: Build Quartz
run: npx quartz build
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: public
deploy:
needs: build
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4

91
.github/workflows/nextjs.yml vendored Normal file
View file

@ -0,0 +1,91 @@
# Sample workflow for building and deploying a Next.js site to GitHub Pages
#
# To get started with Next.js see: https://nextjs.org/docs/getting-started
#
name: Deploy Next.js site to Pages
on:
# Runs on pushes targeting the default branch
push:
branches: ["main"]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: "pages"
cancel-in-progress: false
jobs:
# Build job
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Detect package manager
id: detect-package-manager
run: |
if [ -f "${{ github.workspace }}/package.json" ]; then
echo "manager=npm" >> $GITHUB_OUTPUT
echo "command=install" >> $GITHUB_OUTPUT
echo "runner=npx --no-install" >> $GITHUB_OUTPUT
exit 0
else
echo "Unable to determine package manager"
exit 1
fi
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: "20"
cache: ${{ steps.detect-package-manager.outputs.manager }}
cache-dependency-path: "package.json"
check-latest: true
- name: Setup Pages
uses: actions/configure-pages@v5
# with:
# Automatically inject basePath in your Next.js configuration file and disable
# server side image optimization (https://nextjs.org/docs/api-reference/next/image#unoptimized).
#
# You may remove this line if you want to manage the configuration yourself.
# static_site_generator: next
- name: Restore cache
uses: actions/cache@v4
with:
path: |
.next/cache
node_modules
# Generate a new cache whenever packages or source files change.
key: ${{ runner.os }}-nextjs-${{ hashFiles('package.json') }}
# If source files changed but packages didn't, rebuild from a prior cache.
restore-keys: |
${{ runner.os }}-nextjs-
- name: Install dependencies
run: ${{ steps.detect-package-manager.outputs.manager }} ${{ steps.detect-package-manager.outputs.command }}
- name: Build with Next.js
run: ${{ steps.detect-package-manager.outputs.runner }} next build
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: ./out
# Deployment job
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4

34
.gitignore vendored
View file

@ -1,8 +1,30 @@
# deps
/node_modules
.idea
public/
quartz/.quartz-cache
package-lock.json
# generated content
.contentlayer
.content-collections
.source
# test & build
/coverage
/.next/
/out/
/build
*.tsbuildinfo
# misc
.DS_Store
workspace.json
.idea/discord.xml
content/.obsidian/core-plugins.json
*.pem
/.pnp
.pnp.js
npm-debug.log*
yarn-debug.log*
yarn-error.log*
**/.obsidian
# others
.env*.local
.vercel
next-env.d.ts

0
.idea/.gitignore generated vendored
View file

14
.idea/discord.xml generated
View file

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DiscordProjectSettings">
<option name="show" value="ASK" />
<option name="description" value="" />
<option name="applicationTheme" value="default" />
<option name="iconsTheme" value="default" />
<option name="button1Title" value="" />
<option name="button1Url" value="" />
<option name="button2Title" value="" />
<option name="button2Url" value="" />
<option name="customApplicationId" value="" />
</component>
</project>

12
.idea/docs.iml generated
View file

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
<excludeFolder url="file://$MODULE_DIR$/temp" />
<excludeFolder url="file://$MODULE_DIR$/tmp" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View file

@ -1,20 +0,0 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="PyPep8NamingInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<option name="ignoredErrors">
<list>
<option value="N806" />
<option value="N802" />
</list>
</option>
</inspection_tool>
<inspection_tool class="PyUnresolvedReferencesInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoredIdentifiers">
<list>
<option value="tkmacosx.widgets.button.Button.config" />
</list>
</option>
</inspection_tool>
</profile>
</component>

8
.idea/modules.xml generated
View file

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/docs.iml" filepath="$PROJECT_DIR$/.idea/docs.iml" />
</modules>
</component>
</project>

6
.idea/prettier.xml generated
View file

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PrettierConfiguration">
<option name="myConfigurationMode" value="AUTOMATIC" />
</component>
</project>

6
.idea/vcs.xml generated
View file

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View file

@ -1 +0,0 @@
v20.9.0

1
.npmrc
View file

@ -1 +0,0 @@
engine-strict=true

View file

@ -1,3 +0,0 @@
public
node_modules
.quartz-cache

View file

@ -1,7 +0,0 @@
{
"printWidth": 100,
"quoteProps": "as-needed",
"trailingComma": "all",
"tabWidth": 2,
"semi": false
}

View file

@ -1,11 +0,0 @@
FROM node:20-slim as builder
WORKDIR /usr/src/app
COPY package.json .
COPY package-lock.json* .
RUN npm ci
FROM node:20-slim
WORKDIR /usr/src/app
COPY --from=builder /usr/src/app/ /usr/src/app/
COPY . .
CMD ["npx", "quartz", "build", "--serve"]

View file

@ -1,25 +1,28 @@
<div align="center">
<picture>
<img src="./logo.png" width="128px">
<img src="./public/icon.svg" width="128px">
</picture>
</div>
<h1 align="center">
Zen Browser Docs website
</h1>
# Zen Browser Docs
Welcome to **Zen Browsers Documentation Repo!**
[![Status badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fuptime.zen-browser.app%2Fshield-badges%2Fstatus.json&style=for-the-badge)](https://uptime.zen-browser.app)
## Contributing
## 🛠️ Built With
- [Next.js](https://nextjs.org/) - React framework
- [Fumadocs](https://fumadocs.vercel.app/) - Documentation framework
- MDX - Markdown syntax
This site is built with [Quartz 4.0](https://quartz.jzhao.xyz/). Site content is written in Markdown format located in `content`.
If you would like to contribute see: [Getting Started with Documentation Contributions](https://docs.zen-browser.app/contribute/docs)
## 🤝 Contributing
We welcome contributions to help improve Zen Browser's documentation! Please follow these guidelines:
1. Head over to **https://docs.zen-browser.app/contribute/docs** for full guidelines.
2. Make sure your changes align with our content style and technical accuracy.
3. Open a pull request—our team will review and merge it promptly.
For local preview:
Thanks for helping us make Zen Browser even better!
```bash
npm i
# for preview
npx quartz build --serve
```
## 🙏 Acknowledgments
- Thanks to all our contributors
- Special thanks to the Fumadocs and Next.js teams

1663
bun.lock Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,4 +0,0 @@
{
"livePreview": false,
"alwaysUpdateLinks": true
}

View file

@ -1,4 +0,0 @@
{
"cssTheme": "Catppuccin",
"showRibbon": false
}

View file

@ -1 +0,0 @@
["obsidian-style-settings", "emoji-shortcodes", "callout-manager", "obsidian-emoji-toolbar"]

View file

@ -1,30 +0,0 @@
{
"file-explorer": true,
"global-search": true,
"switcher": true,
"graph": true,
"backlink": true,
"canvas": true,
"outgoing-link": true,
"tag-pane": true,
"properties": false,
"page-preview": true,
"daily-notes": true,
"templates": true,
"note-composer": true,
"command-palette": true,
"slash-command": false,
"editor-status": true,
"bookmarks": true,
"markdown-importer": false,
"zk-prefixer": false,
"random-note": false,
"outline": true,
"word-count": true,
"slides": false,
"audio-recorder": false,
"workspaces": false,
"file-recovery": true,
"publish": false,
"sync": false
}

View file

@ -1,22 +0,0 @@
{
"collapse-filter": true,
"search": "",
"showTags": false,
"showAttachments": false,
"hideUnresolved": false,
"showOrphans": true,
"collapse-color-groups": true,
"colorGroups": [],
"collapse-display": true,
"showArrow": false,
"textFadeMultiplier": 0,
"nodeSizeMultiplier": 1,
"lineSizeMultiplier": 1,
"collapse-forces": true,
"centerStrength": 0.518713248970312,
"repelStrength": 10,
"linkStrength": 1,
"linkDistance": 250,
"scale": 1.942784106682881,
"close": false
}

View file

@ -1,8 +0,0 @@
{
"callout-manager:manage-callouts": [
{
"modifiers": ["Mod", "Shift"],
"key": "C"
}
]
}

View file

@ -1,11 +0,0 @@
{
"callouts": {
"custom": [],
"settings": {}
},
"calloutDetection": {
"obsidian": true,
"theme": true,
"snippet": true
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,10 +0,0 @@
{
"id": "callout-manager",
"version": "1.1.0",
"description": "Easily create and customize callouts.",
"author": "eth-p",
"authorUrl": "https://github.com/eth-p",
"name": "Callout Manager",
"minAppVersion": "1.0.0",
"isDesktopOnly": false
}

View file

@ -1,388 +0,0 @@
.calloutmanager-latest-changes {
padding: 0.75em 0;
border-top: 1px solid var(--background-modifier-border);
}
.calloutmanager-latest-changes .calloutmanager-changelog-section > :first-child {
margin-top: 0;
}
.calloutmanager-latest-changes .calloutmanager-changelog-section > :last-child {
margin-bottom: 0;
}
.calloutmanager-latest-changes .callout {
background: none;
}
.mod-sidebar-layout .calloutmanager-setting-tab.vertical-tab-content {
position: relative;
padding: 0 !important;
display: flex;
flex-direction: column;
overflow-y: initial;
}
.mod-sidebar-layout .calloutmanager-setting-tab.vertical-tab-content {
--cm-setting-tab-controls-margin: calc(var(--size-4-12) - (var(--size-4-2) - var(--size-4-1)));
}
body.is-phone .mod-sidebar-layout .calloutmanager-setting-tab.vertical-tab-content {
--cm-setting-tab-controls-margin: var(--size-4-2);
}
.calloutmanager-setting-tab-header {
display: flex;
align-items: center;
padding-top: var(--size-4-1);
padding-bottom: var(--size-4-1);
padding-right: var(--size-4-2);
border-bottom: 1px solid var(--background-modifier-border);
background-color: var(--background-secondary);
}
body.is-phone .calloutmanager-setting-tab-header {
background-color: var(--background-primary);
}
.calloutmanager-setting-tab-nav {
display: flex;
align-items: center;
justify-content: center;
min-width: var(--size-4-12);
min-height: calc(var(--size-4-2) + var(--input-height));
}
.calloutmanager-setting-tab-nav button {
padding: var(--size-4-1) var(--size-4-2);
box-shadow: none;
}
body.is-mobile .calloutmanager-setting-tab-nav {
padding: var(--size-4-2);
}
body.is-phone .calloutmanager-setting-tab-nav,
body.is-phone .calloutmanager-setting-tab-nav button {
height: 100%;
min-width: unset;
}
.calloutmanager-setting-tab-controls {
flex: 3 3;
display: flex;
align-items: center;
justify-content: end;
gap: var(--size-4-2);
padding-left: var(--cm-setting-tab-controls-margin);
}
.calloutmanager-setting-tab-controls > *:not(.modal-close-button) {
display: contents;
}
.calloutmanager-setting-tab-controls > *:not(.modal-close-button) > input[type="text"] {
flex: 1 1 auto;
}
.calloutmanager-setting-tab-controls .modal-close-button {
flex: 0 0 auto;
position: static;
left: unset;
top: 0;
right: 0;
bottom: 0;
}
body.is-phone .calloutmanager-setting-tab-controls .modal-close-button {
display: none;
}
.calloutmanager-setting-tab-title {
flex: 1 1 auto;
flex-wrap: nowrap;
}
.calloutmanager-setting-tab-title h2,
.calloutmanager-setting-tab-title h3 {
margin: 0;
word-break: keep-all;
}
.calloutmanager-setting-tab-title h3 {
font-size: var(--font-ui-small);
}
body:not(.is-phone) .calloutmanager-setting-tab-title h3 {
font-size: 0.8em;
}
body.is-phone .calloutmanager-setting-tab-title h2:has(+ h3) {
display: none;
}
.calloutmanager-setting-tab-viewport {
flex: 1 2 auto;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}
.calloutmanager-setting-tab-content {
flex: 1 1 auto;
}
.calloutmanager-setting-tab-content body:not(.is-phone) {
min-height: 100%;
}
.calloutmanager-callout-resolver {
display: none !important;
}
.calloutmanager-search-error {
width: 60%;
}
body.is-phone .calloutmanager-search-error {
width: 100%;
}
.calloutmanager-search-error code {
word-break: break-all;
color: var(--text-accent);
}
.calloutmanager-search-error-suggestions {
color: var(--text-muted);
}
.calloutmanager-preview-container-with-button {
--calloutmanager-callout-edit-buttons-size: calc(var(--input-height) + 2 * var(--size-4-3));
display: grid;
grid-template-columns: 1fr var(--calloutmanager-callout-edit-buttons-size) var(
--calloutmanager-callout-edit-buttons-size
);
align-items: center;
gap: var(--size-4-2);
}
body.is-phone .calloutmanager-preview-container-with-button {
--calloutmanager-callout-edit-buttons-size: var(--input-height);
}
.calloutmanager-preview-container-with-button > button {
width: var(--calloutmanager-callout-edit-buttons-size);
height: 100%;
}
body:not(.is-phone) .calloutmanager-preview-container-with-button > button {
display: block;
padding: 0 !important;
}
.calloutmanager-changelog > *:not(:first-child) {
margin-top: 1em;
}
.calloutmanager-changelog details > summary::marker {
color: var(--text-faint);
}
.calloutmanager-changelog details[open] > summary::marker {
color: var(--text-muted);
}
.calloutmanager-changelog details .calloutmanager-changelog-section {
border-bottom: 1px solid var(--background-modifier-border);
margin-bottom: 1.25em;
padding-bottom: 1.25em;
}
.calloutmanager-changelog details .calloutmanager-changelog-section > :last-child {
margin-bottom: 0;
}
.calloutmanager-changelog
details:not([data-current-version="true"])
.calloutmanager-changelog-heading {
color: var(--text-muted);
}
.calloutmanager-changelog-section .callout {
--callout-padding: 0.5em;
}
.calloutmanager-changelog-section .callout > .callout-content {
margin-left: calc(18px + 0.25em);
}
.calloutmanager-changelog-section .callout > .callout-content > :first-child {
margin-top: 0;
}
.calloutmanager-changelog-section .callout > .callout-content > :last-child {
margin-bottom: 0;
}
.callout[data-calloutmanager-changelog-callout="new"] {
--callout-icon: lucide-plus;
--callout-color: 30, 160, 30;
}
.theme-dark .callout[data-calloutmanager-changelog-callout="new"] {
--callout-color: 60, 250, 60;
}
.callout[data-calloutmanager-changelog-callout="fix"] {
--callout-icon: lucide-wrench;
--callout-color: 128, 128, 128;
}
.theme-dark .callout[data-calloutmanager-changelog-callout="fix"] {
--callout-color: 180, 180, 180;
}
.callout[data-calloutmanager-changelog-callout="change"] {
--callout-icon: lucide-edit-3;
--callout-color: 10, 170, 210;
}
.theme-dark .callout[data-calloutmanager-changelog-callout="change"] {
--callout-color: 60, 157, 210;
}
.calloutmanager-changelog-heading {
display: inline;
font-weight: bold;
}
.calloutmanager-centerbox {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.calloutmanager-pane button[disabled] {
box-shadow: none;
background-color: var(--interactive-normal);
}
.calloutmanager-pane button[disabled]:hover {
background-color: var(--interactive-normal);
cursor: not-allowed;
}
.calloutmanager-pane input[type="color"][disabled] {
cursor: not-allowed;
}
.calloutmanager-pane input:invalid:not(:placeholder-shown),
.calloutmanager-pane input.invalid {
border-color: var(--text-error);
}
body.is-phone .calloutmanager-pane input[type="color"]::-webkit-color-swatch {
border-radius: var(--button-radius);
border: red 2px solid;
border: 1px solid var(--checkbox-border-color);
}
.calloutmanager-pane .clickable-icon.mod-warning {
color: var(--text-error);
background: rgba(0, 0, 0, 0);
}
.calloutmanager-pane .clickable-icon.mod-warning:hover {
color: var(--text-error);
background: rgba(0, 0, 0, 0);
}
.calloutmanager-pane input[type="text"].mod-error {
border-color: var(--text-error);
}
body.is-phone .calloutmanager-setting-tab-content .setting-item-control button.clickable-icon,
body.is-phone .calloutmanager-setting-tab-controls button.clickable-icon {
width: var(--button-height);
}
body.is-phone .calloutmanager-setting-tab-content .setting-item-control button.clickable-icon {
border: 1px solid var(--checkbox-border-color);
}
.callout.calloutmanager-preview {
mix-blend-mode: unset !important;
margin: 0 !important;
}
.calloutmanager-preview-container {
margin-top: 0.5em;
margin-bottom: 0.5em;
}
.calloutmanager-edit-callout-section {
border-top: 1px solid var(--background-modifier-border);
padding-top: var(--size-4-3);
padding-bottom: var(--size-4-6);
}
.calloutmanager-edit-callout-section:empty {
display: none;
}
.calloutmanager-edit-callout-section--noborder {
border-top: none;
}
.calloutmanager-edit-callout-section .setting-item .setting-item-description p {
margin: 0;
}
.calloutmanager-edit-callout-section h2 {
margin-bottom: 0.3em;
}
.calloutmanager-edit-callout-section h2 + p {
margin-top: 0;
}
.calloutmanager-edit-callout-appearance .setting-item {
border-top: none;
padding-top: 0.375em;
}
.calloutmanager-edit-callout-appearance .setting-item:has(+ .setting-item) {
padding-bottom: 0.375em;
}
body.is-phone .calloutmanager-edit-callout-appearance .setting-item:has(+ .setting-item) {
margin-bottom: 0.7em;
}
.calloutmanager-edit-callout-appearance-json pre {
border: rgba(var(--background-modifier-border)) 1px solid;
border-radius: var(--callout-radius);
padding: var(--size-4-2);
background: var(--background-primary-alt);
overflow-x: auto;
margin: 0;
}
.calloutmanager-edit-callout-appearance-reset {
width: 100%;
}
.calloutmanager-edit-callout-preview {
padding-bottom: var(--size-4-8);
min-height: 14em;
}
body.is-mobile .calloutmanager-edit-callout-preview {
min-height: 35vh;
}
.calloutmanager-preview-editor {
resize: vertical;
width: 100%;
min-height: 6em;
margin-top: var(--size-4-3);
background: rgba(0, 0, 0, 0);
font-size: var(--font-text-size);
font-family: var(--font-text);
line-height: var(--line-height-normal);
}
.calloutmanager-edit-callout-info {
color: var(--text-muted);
}
.calloutmanager-edit-callout--invalid-color {
color: var(--text-error);
}
.calloutmanager-edit-callout--callout-color {
color: rgb(var(--resolved-callout-color));
}
.calloutmanager-edit-callout--callout-id,
.calloutmanager-edit-callout--callout-icon,
.calloutmanager-edit-callout--callout-source {
color: var(--text-normal);
}
:root {
--calloutmanager-reset-button-disabled-opacity: 0.3;
}
.calloutmanager-reset-button:is(.is-disabled, [disabled]) {
opacity: var(--calloutmanager-reset-button-disabled-opacity);
}
.calloutmanager-reset-button:is(.is-disabled, [disabled]):hover {
background-color: rgba(0, 0, 0, 0);
}
.calloutmanager-reset-button:is(.is-disabled, [disabled]):active {
color: var(--icon-color);
}
:root {
--calloutmanager-icon-picker-size: 100px;
--calloutmanager-icon-picker-gap: 8px;
--calloutmanager-icon-picker-icon-size: 2.5em;
--calloutmanager-icon-picker-id-size: 0.75em;
}
.calloutmanager-icon-picker {
display: grid;
grid-template-columns: repeat(auto-fill, var(--calloutmanager-icon-picker-size));
grid-auto-rows: var(--calloutmanager-icon-picker-size);
gap: var(--calloutmanager-icon-picker-gap);
justify-content: center;
}
.calloutmanager-icon-picker .calloutmanager-icon-preview {
height: 100%;
--calloutmanager-icon-preview-icon-size: var(--calloutmanager-icon-picker-icon-size);
--calloutmanager-icon-preview-id-size: var(--calloutmanager-icon-picker-id-size);
}
:root {
--calloutmanager-icon-preview-icon-size: 1em;
--calloutmanager-icon-preview-id-size: 0.8em;
}
.calloutmanager-icon-preview {
position: relative;
height: unset;
min-height: 3em;
display: flex;
flex-direction: column;
}
.calloutmanager-icon-preview--icon {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, calc(-50% - 0.5em));
--icon-size: var(--calloutmanager-icon-picker-icon-size);
}
.calloutmanager-icon-preview--id {
width: 100%;
margin-top: auto;
white-space: normal;
word-break: break-word;
hyphens: manual;
font-size: var(--calloutmanager-icon-picker-id-size);
}

View file

@ -1,16 +0,0 @@
{
"immediateReplace": true,
"suggester": true,
"historyPriority": true,
"historyLimit": 100,
"history": [
":smile:",
":package:",
":apple:",
":penguin:",
":window:",
":older_man:",
":rocket:",
":closed_lock_with_key:"
]
}

File diff suppressed because one or more lines are too long

View file

@ -1,11 +0,0 @@
{
"id": "emoji-shortcodes",
"name": "Emoji Shortcodes",
"version": "2.2.0",
"minAppVersion": "1.0.0",
"description": "This Plugin enables the use of Markdown Emoji Shortcodes :smile:",
"author": "phibr0",
"authorUrl": "https://github.com/phibr0",
"isDesktopOnly": false,
"fundingUrl": "https://ko-fi.com/phibr0"
}

View file

@ -1,31 +0,0 @@
a[href="https://ko-fi.com/phibr0"] > img
{
height: 3em;
}
a[href="https://ko-fi.com/phibr0"]
{
transform: translate(0, 5%);
}
.ES-suggester-container {
display: flex;
place-content: space-between;
}
.ES-shortcode {
margin-right: 8px;
}
.ES-suggestion-item {
border-top: solid var(--background-secondary) 1px;
padding-left: 10px;
}
.ES-sub-setting {
padding-left: 2em;
}
.ES-sub-setting + .ES-sub-setting {
padding-left: 0;
margin-left: 2em;
}

File diff suppressed because one or more lines are too long

View file

@ -1,9 +0,0 @@
{
"id": "obsidian-emoji-toolbar",
"name": "Emoji Toolbar",
"version": "0.4.1",
"description": "Quickly search for and insert emojis into your notes.",
"author": "oliveryh",
"authorUrl": "https://github.com/oliveryh/obsidian-emoji-toolbar",
"isDesktopOnly": false
}

View file

@ -1,542 +0,0 @@
img.emoji {
height: 1em;
width: 1em;
margin: 0 0.05em 0 0.1em;
vertical-align: -0.1em;
display: inline-block;
}
.emoji-mart,
.emoji-mart * {
box-sizing: border-box;
line-height: 1.15;
}
.emoji-mart {
font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", sans-serif;
font-size: 16px;
display: inline-block;
color: #222427;
border: 1px solid #d9d9d9;
border-radius: 5px;
background: #fff;
}
.emoji-mart .emoji-mart-emoji {
padding: 6px !important;
}
.emoji-mart-bar {
border: 0 solid #d9d9d9;
}
.emoji-mart-bar:first-child {
border-bottom-width: 1px;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
}
.emoji-mart-bar:last-child {
border-top-width: 1px;
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
}
.emoji-mart-anchors {
display: flex;
flex-direction: row;
justify-content: space-between;
padding: 0 6px;
line-height: 0;
}
.emoji-mart-anchor {
position: relative;
display: block;
flex: 1 1 auto;
color: #858585;
text-align: center;
padding: 12px 4px !important;
overflow: hidden;
transition: color 0.1s ease-out;
margin: 0 !important;
box-shadow: none !important;
background: none !important;
border: none !important;
}
.emoji-mart-anchor:focus {
outline: 0;
}
.emoji-mart-anchor:hover,
.emoji-mart-anchor:focus,
.emoji-mart-anchor-selected {
color: #464646;
}
.emoji-mart-anchor-selected .emoji-mart-anchor-bar {
bottom: 0;
}
.emoji-mart-anchor-bar {
position: absolute;
bottom: -3px;
left: 0;
width: 100%;
height: 3px;
background-color: #464646;
}
.emoji-mart-anchors i {
display: inline-block;
width: 100%;
max-width: 22px;
}
.emoji-mart-anchors svg,
.emoji-mart-anchors img {
fill: currentColor;
height: 18px;
width: 18px;
}
.emoji-mart-scroll {
overflow-y: scroll;
overflow-x: hidden;
height: 270px;
padding: 0 6px 6px 6px;
will-change: transform; /* avoids "repaints on scroll" in mobile Chrome */
}
.emoji-mart-search {
margin-top: 6px;
padding: 0 6px;
position: relative;
}
.emoji-mart-search input {
font-size: 16px;
display: block;
width: 100%;
padding: 5px 25px 6px 10px;
border-radius: 5px;
border: 1px solid #d9d9d9;
outline: 0;
}
.emoji-mart-search input,
.emoji-mart-search input::-webkit-search-decoration,
.emoji-mart-search input::-webkit-search-cancel-button,
.emoji-mart-search input::-webkit-search-results-button,
.emoji-mart-search input::-webkit-search-results-decoration {
/* remove webkit/blink styles for <input type="search">
* via https://stackoverflow.com/a/9422689 */
-webkit-appearance: none;
}
.emoji-mart-search-icon {
position: absolute;
top: 7px;
right: 11px;
z-index: 2;
padding: 2px 5px 1px;
border: none !important;
box-shadow: none !important;
background: none !important;
}
.emoji-mart-category .emoji-mart-emoji span {
z-index: 1;
position: relative;
text-align: center;
cursor: default;
}
.emoji-mart-category .emoji-mart-emoji:focus {
outline: 0;
}
.emoji-mart-category .emoji-mart-emoji:hover:before,
.emoji-mart-category .emoji-mart-emoji:focus:before {
z-index: 0;
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: #c5c5c5;
border-radius: 100%;
}
.emoji-mart-category-label {
z-index: 2;
position: relative;
position: -webkit-sticky;
position: sticky;
top: 0;
}
.emoji-mart-category-label span {
display: block;
width: 100%;
font-weight: 500;
padding: 5px 6px;
background-color: #fff;
background-color: rgba(255, 255, 255, 0.95);
}
.emoji-mart-category-list {
border-spacing: 0;
margin: 0;
padding: 0;
}
.emoji-mart-category-list td {
margin: 0;
padding: 0;
}
.emoji-mart-emoji {
position: relative;
display: inline-block;
font-size: 0;
margin: 0 !important;
padding: 0 !important;
border: none !important;
background: none !important;
box-shadow: none !important;
}
.emoji-mart-emoji-native {
font-family:
"Segoe UI Emoji", "Segoe UI Symbol", "Segoe UI", "Apple Color Emoji", "Twemoji Mozilla",
"Noto Color Emoji", "Android Emoji";
}
.emoji-mart-no-results {
font-size: 14px;
text-align: center;
padding-top: 70px;
color: #858585;
}
.emoji-mart-no-results-img {
display: block;
margin-left: auto;
margin-right: auto;
width: 50%;
}
.emoji-mart-no-results .emoji-mart-category-label {
display: none;
}
.emoji-mart-no-results .emoji-mart-no-results-label {
margin-top: 0.2em;
}
.emoji-mart-no-results .emoji-mart-emoji:hover:before {
content: none;
}
.emoji-mart-preview {
position: relative;
height: 70px;
}
.emoji-mart-preview-emoji,
.emoji-mart-preview-data,
.emoji-mart-preview-skins {
position: absolute;
top: 50%;
transform: translateY(-50%);
}
.emoji-mart-preview-emoji {
left: 12px;
}
.emoji-mart-preview-data {
left: 68px;
right: 12px;
word-break: break-all;
}
.emoji-mart-preview-skins {
right: 30px;
text-align: right;
}
.emoji-mart-preview-skins.custom {
right: 10px;
text-align: right;
}
.emoji-mart-preview-name {
font-size: 14px;
}
.emoji-mart-preview-shortname {
font-size: 12px;
color: #888;
}
.emoji-mart-preview-shortname + .emoji-mart-preview-shortname,
.emoji-mart-preview-shortname + .emoji-mart-preview-emoticon,
.emoji-mart-preview-emoticon + .emoji-mart-preview-emoticon {
margin-left: 0.5em;
}
.emoji-mart-preview-emoticon {
font-size: 11px;
color: #bbb;
}
.emoji-mart-title span {
display: inline-block;
vertical-align: middle;
}
.emoji-mart-title .emoji-mart-emoji {
padding: 0;
}
.emoji-mart-title-label {
color: #999a9c;
font-size: 26px;
font-weight: 300;
}
.emoji-mart-skin-swatches {
font-size: 0;
padding: 2px 0;
border: 1px solid #d9d9d9;
border-radius: 12px;
background-color: #fff;
}
.emoji-mart-skin-swatches.custom {
font-size: 0;
border: none;
background-color: #fff;
}
.emoji-mart-skin-swatches.opened .emoji-mart-skin-swatch {
width: 16px;
padding: 0 2px;
}
.emoji-mart-skin-swatches.opened .emoji-mart-skin-swatch.selected:after {
opacity: 0.75;
}
.emoji-mart-skin-swatch {
display: inline-block;
width: 0;
vertical-align: middle;
transition-property: width, padding;
transition-duration: 0.125s;
transition-timing-function: ease-out;
}
.emoji-mart-skin-swatch:nth-child(1) {
transition-delay: 0s;
}
.emoji-mart-skin-swatch:nth-child(2) {
transition-delay: 0.03s;
}
.emoji-mart-skin-swatch:nth-child(3) {
transition-delay: 0.06s;
}
.emoji-mart-skin-swatch:nth-child(4) {
transition-delay: 0.09s;
}
.emoji-mart-skin-swatch:nth-child(5) {
transition-delay: 0.12s;
}
.emoji-mart-skin-swatch:nth-child(6) {
transition-delay: 0.15s;
}
.emoji-mart-skin-swatch.selected {
position: relative;
width: 16px;
padding: 0 2px;
}
.emoji-mart-skin-swatch.selected:after {
content: "";
position: absolute;
top: 50%;
left: 50%;
width: 4px;
height: 4px;
margin: -2px 0 0 -2px;
background-color: #fff;
border-radius: 100%;
pointer-events: none;
opacity: 0;
transition: opacity 0.2s ease-out;
}
.emoji-mart-skin-swatch.custom {
display: inline-block;
width: 0;
height: 38px;
overflow: hidden;
vertical-align: middle;
transition-property: width, height;
transition-duration: 0.125s;
transition-timing-function: ease-out;
cursor: default;
}
.emoji-mart-skin-swatch.custom.selected {
position: relative;
width: 36px;
height: 38px;
padding: 0 2px 0 0;
}
.emoji-mart-skin-swatch.custom.selected:after {
content: "";
width: 0;
height: 0;
}
.emoji-mart-skin-swatches.custom .emoji-mart-skin-swatch.custom:hover {
background-color: #f4f4f4;
border-radius: 10%;
}
.emoji-mart-skin-swatches.custom.opened .emoji-mart-skin-swatch.custom {
width: 36px;
height: 38px;
padding: 0 2px 0 0;
}
.emoji-mart-skin-swatches.custom.opened .emoji-mart-skin-swatch.custom.selected:after {
opacity: 0.75;
}
.emoji-mart-skin-text.opened {
display: inline-block;
vertical-align: middle;
text-align: left;
color: #888;
font-size: 11px;
padding: 5px 2px;
width: 95px;
height: 40px;
border-radius: 10%;
background-color: #fff;
}
.emoji-mart-skin {
display: inline-block;
width: 100%;
padding-top: 100%;
max-width: 12px;
border-radius: 100%;
}
.emoji-mart-skin-tone-1 {
background-color: #ffc93a;
}
.emoji-mart-skin-tone-2 {
background-color: #fadcbc;
}
.emoji-mart-skin-tone-3 {
background-color: #e0bb95;
}
.emoji-mart-skin-tone-4 {
background-color: #bf8f68;
}
.emoji-mart-skin-tone-5 {
background-color: #9b643d;
}
.emoji-mart-skin-tone-6 {
background-color: #594539;
}
/* For screenreaders only, via https://stackoverflow.com/a/19758620 */
.emoji-mart-sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
}
/*
* Dark mode styles
*/
.emoji-mart-dark {
color: #fff;
border-color: #555453;
background-color: #222;
}
.emoji-mart-dark .emoji-mart-bar {
border-color: #555453;
}
.emoji-mart-dark .emoji-mart-search input {
color: #fff;
border-color: #555453;
background-color: #2f2f2f;
}
.emoji-mart-dark .emoji-mart-search-icon svg {
fill: #fff;
}
.emoji-mart-category .emoji-mart-emoji {
background-color: unset !important;
}
.emoji-mart-anchor {
background-color: unset !important;
}
.emoji-mart-search-icon {
background-color: unset !important;
}
.emoji-mart-dark .emoji-mart-category .emoji-mart-emoji:hover:before,
.emoji-mart-dark .emoji-mart-category .emoji-mart-emoji:focus:before {
background-color: #888;
}
.emoji-mart-dark .emoji-mart-category-label span {
background-color: #222;
color: #fff;
}
.emoji-mart-dark .emoji-mart-skin-swatches {
border-color: #555453;
background-color: #222;
}
.emoji-mart-dark .emoji-mart-anchor:hover,
.emoji-mart-dark .emoji-mart-anchor:focus,
.emoji-mart-dark .emoji-mart-anchor-selected {
color: #bfbfbf;
}
#emoji-modal {
padding: 0px;
min-width: unset;
width: unset !important;
}
#emoji-modal > .modal-content {
margin-top: 0px;
}
#emoji-modal > button {
background-color: unset;
border: 0px !important;
box-shadow: 0px !important;
}
#emoji-modal > .modal-close-button {
visibility: hidden;
}

View file

@ -1,3 +0,0 @@
{
"minimal-advanced@@styled-scrollbars": true
}

File diff suppressed because it is too large Load diff

View file

@ -1,10 +0,0 @@
{
"id": "obsidian-style-settings",
"name": "Style Settings",
"version": "1.0.8",
"minAppVersion": "0.11.5",
"description": "Offers controls for adjusting theme, plugin, and snippet CSS variables.",
"author": "mgmeyers",
"authorUrl": "https://github.com/mgmeyers/obsidian-style-settings",
"isDesktopOnly": false
}

View file

@ -1,646 +0,0 @@
.style-settings-container .pcr-app {
display: none;
}
.style-settings-container .pcr-app.visible {
display: flex;
}
.pcr-app .pcr-swatches > button {
padding: 0;
}
.pickr .pcr-button {
margin-right: 0;
}
.themed-color-wrapper > div {
background: var(--background-primary);
padding: 10px;
display: flex;
align-items: center;
border-radius: 4px;
}
.themed-color-wrapper > div + div {
margin-top: 6px;
}
.themed-color-wrapper button {
display: block;
}
.themed-color-wrapper .pickr-reset > button {
margin: 0 0 0 10px;
padding: 9px;
line-height: 1;
}
.themed-color-wrapper .pickr-reset > button > svg {
display: block;
}
.style-settings-heading {
cursor: pointer;
margin-bottom: 18px;
padding-bottom: 6px;
border-bottom: 1px solid var(--background-modifier-border);
}
.style-settings-heading[data-level="0"] {
margin-bottom: 26px;
}
.style-settings-container {
padding-bottom: 16px;
}
.style-settings-heading[data-level="0"] + .style-settings-container {
padding-left: 34px;
}
.style-settings-heading.is-collapsed {
margin-bottom: 0;
}
.style-settings-heading.is-collapsed + .style-settings-container {
display: none;
}
.style-settings-collapse-indicator {
color: var(--text-faint);
display: inline-block;
margin-right: 8px;
position: relative;
top: -1px;
}
.style-settings-heading[data-level="0"]
+ .style-settings-container
.style-settings-collapse-indicator {
margin-left: -17px;
}
.style-settings-collapse-indicator > svg {
height: 9px;
width: 9px;
}
.style-settings-heading.is-collapsed .style-settings-collapse-indicator > svg {
transform: rotate(-90deg);
}
.style-settings-filter-result-count {
color: var(--text-faint);
line-height: var(--line-height-tight);
margin-inline: var(--size-4-2);
}
.style-settings-error {
font-size: 14px;
border-radius: 6px;
background: rgba(var(--background-modifier-error-rgb), 0.2);
color: var(--text-error);
padding: 10px;
margin-bottom: 1rem;
}
.style-settings-error-name {
font-weight: bold;
margin-bottom: 5px;
}
.style-settings-error-desc {
white-space: pre;
}
.style-settings-empty {
font-size: 14px;
background: var(--background-secondary);
padding: 10px;
}
.style-settings-empty-name {
font-weight: bold;
margin-bottom: 5px;
}
.style-settings-import-input {
width: 0.1px;
height: 0.1px;
opacity: 0;
overflow: hidden;
position: absolute;
z-index: -1;
}
.style-settings-import-label {
cursor: pointer;
color: var(--text-accent);
text-decoration: underline;
}
.style-settings-import-label:hover {
color: var(--text-accent-hover);
}
.style-settings-export,
.style-settings-import {
display: inline-block;
margin-right: 10px;
}
.style-settings-copy,
.style-settings-download {
position: relative;
display: inline-block;
margin-left: 10px;
}
.style-settings-copy:before {
color: var(--interactive-success);
content: "✓";
position: absolute;
left: -18px;
font-weight: bold;
opacity: 0;
transition: 150ms opacity ease-in-out;
}
.style-settings-copy.success:before {
opacity: 1;
}
.modal-style-settings {
height: 70vh;
display: flex;
flex-direction: column;
}
.modal-style-settings .modal-content {
flex-grow: 1;
margin: 0;
display: flex;
flex-direction: column;
}
.modal-style-settings textarea {
display: block;
width: 100%;
height: 100%;
font-family: var(--font-monospace) !important;
font-size: 12px;
white-space: pre;
overflow-wrap: normal;
overflow-x: scroll;
margin-bottom: 5px;
}
.modal-style-settings .setting-item {
align-items: flex-start;
}
.modal-style-settings button {
margin: 10px 0 0;
}
.style-settings-import-error {
display: none;
color: var(--text-error);
}
.style-settings-import-error.active {
display: block;
}
.view-content .style-settings-container .setting-item:not(.setting-item-heading) {
flex-direction: column;
align-items: flex-start;
}
.view-content
.style-settings-container
.setting-item:not(.setting-item-heading)
.setting-item-control {
padding-top: 0.5em;
}
.view-content
.style-settings-container
.setting-item:not(.setting-item-heading)
.themed-color-wrapper {
display: flex;
}
.style-settings-ref {
position: absolute;
width: 0 !important;
height: 0 !important;
pointer-events: none;
}
.style-settings-info-text .style-settings-markdown :first-child {
margin-top: 0;
}
.style-settings-info-text .style-settings-markdown :last-child {
margin-bottom: 0;
} /*! Pickr 1.8.4 MIT | https://github.com/Simonwep/pickr */
.pickr {
position: relative;
overflow: visible;
transform: translateY(0);
}
.pickr * {
box-sizing: border-box;
outline: none;
border: none;
-webkit-appearance: none;
}
.pickr .pcr-button {
position: relative;
height: 2em;
width: 2em;
padding: 0.5em;
cursor: pointer;
font-family:
-apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Helvetica Neue", Arial, sans-serif;
border-radius: 0.15em;
background: url('data:image/svg+xml;utf8, <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" stroke="%2342445A" stroke-width="5px" stroke-linecap="round"><path d="M45,45L5,5"></path><path d="M45,5L5,45"></path></svg>')
no-repeat center;
background-size: 0;
transition: all 0.3s;
}
.pickr .pcr-button::before {
position: absolute;
content: "";
top: 0;
left: 0;
width: 100%;
height: 100%;
background: url('data:image/svg+xml;utf8, <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2 2"><path fill="white" d="M1,0H2V1H1V0ZM0,1H1V2H0V1Z"/><path fill="gray" d="M0,0H1V1H0V0ZM1,1H2V2H1V1Z"/></svg>');
background-size: 0.5em;
border-radius: 0.15em;
z-index: -1;
}
.pickr .pcr-button::before {
z-index: initial;
}
.pickr .pcr-button::after {
position: absolute;
content: "";
top: 0;
left: 0;
height: 100%;
width: 100%;
transition: background 0.3s;
background: var(--pcr-color);
border-radius: 0.15em;
}
.pickr .pcr-button.clear {
background-size: 70%;
}
.pickr .pcr-button.clear::before {
opacity: 0;
}
.pickr .pcr-button.clear:focus {
box-shadow:
0 0 0 1px rgba(255, 255, 255, 0.85),
0 0 0 3px var(--pcr-color);
}
.pickr .pcr-button.disabled {
cursor: not-allowed;
}
.pickr *,
.pcr-app * {
box-sizing: border-box;
outline: none;
border: none;
-webkit-appearance: none;
}
.pickr input:focus,
.pickr input.pcr-active,
.pickr button:focus,
.pickr button.pcr-active,
.pcr-app input:focus,
.pcr-app input.pcr-active,
.pcr-app button:focus,
.pcr-app button.pcr-active {
box-shadow:
0 0 0 1px rgba(255, 255, 255, 0.85),
0 0 0 3px var(--pcr-color);
}
.pickr .pcr-palette,
.pickr .pcr-slider,
.pcr-app .pcr-palette,
.pcr-app .pcr-slider {
transition: box-shadow 0.3s;
}
.pickr .pcr-palette:focus,
.pickr .pcr-slider:focus,
.pcr-app .pcr-palette:focus,
.pcr-app .pcr-slider:focus {
box-shadow:
0 0 0 1px rgba(255, 255, 255, 0.85),
0 0 0 3px rgba(0, 0, 0, 0.25);
}
.pcr-app {
position: fixed;
display: flex;
flex-direction: column;
z-index: 10000;
border-radius: 0.1em;
background: #fff;
opacity: 0;
visibility: hidden;
transition:
opacity 0.3s,
visibility 0s 0.3s;
font-family:
-apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Helvetica Neue", Arial, sans-serif;
box-shadow:
0 0.15em 1.5em 0 rgba(0, 0, 0, 0.1),
0 0 1em 0 rgba(0, 0, 0, 0.03);
left: 0;
top: 0;
}
.pcr-app.visible {
transition: opacity 0.3s;
visibility: visible;
opacity: 1;
}
.pcr-app .pcr-swatches {
display: flex;
flex-wrap: wrap;
margin-top: 0.75em;
}
.pcr-app .pcr-swatches.pcr-last {
margin: 0;
}
@supports (display: grid) {
.pcr-app .pcr-swatches {
display: grid;
align-items: center;
grid-template-columns: repeat(auto-fit, 1.75em);
}
}
.pcr-app .pcr-swatches > button {
font-size: 1em;
position: relative;
width: calc(1.75em - 5px);
height: calc(1.75em - 5px);
border-radius: 0.15em;
cursor: pointer;
margin: 2.5px;
flex-shrink: 0;
justify-self: center;
transition: all 0.15s;
overflow: hidden;
background: transparent;
z-index: 1;
}
.pcr-app .pcr-swatches > button::before {
position: absolute;
content: "";
top: 0;
left: 0;
width: 100%;
height: 100%;
background: url('data:image/svg+xml;utf8, <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2 2"><path fill="white" d="M1,0H2V1H1V0ZM0,1H1V2H0V1Z"/><path fill="gray" d="M0,0H1V1H0V0ZM1,1H2V2H1V1Z"/></svg>');
background-size: 6px;
border-radius: 0.15em;
z-index: -1;
}
.pcr-app .pcr-swatches > button::after {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: var(--pcr-color);
border: 1px solid rgba(0, 0, 0, 0.05);
border-radius: 0.15em;
box-sizing: border-box;
}
.pcr-app .pcr-swatches > button:hover {
filter: brightness(1.05);
}
.pcr-app .pcr-swatches > button:not(.pcr-active) {
box-shadow: none;
}
.pcr-app .pcr-interaction {
display: flex;
flex-wrap: wrap;
align-items: center;
margin: 0 -0.2em 0 -0.2em;
}
.pcr-app .pcr-interaction > * {
margin: 0 0.2em;
}
.pcr-app .pcr-interaction input {
letter-spacing: 0.07em;
font-size: 0.75em;
text-align: center;
cursor: pointer;
color: #75797e;
background: #f1f3f4;
border-radius: 0.15em;
transition: all 0.15s;
padding: 0.45em 0.5em;
margin-top: 0.75em;
}
.pcr-app .pcr-interaction input:hover {
filter: brightness(0.975);
}
.pcr-app .pcr-interaction input:focus {
box-shadow:
0 0 0 1px rgba(255, 255, 255, 0.85),
0 0 0 3px rgba(66, 133, 244, 0.75);
}
.pcr-app .pcr-interaction .pcr-result {
color: #75797e;
text-align: left;
flex: 1 1 8em;
min-width: 8em;
transition: all 0.2s;
border-radius: 0.15em;
background: #f1f3f4;
cursor: text;
}
.pcr-app .pcr-interaction .pcr-result::-moz-selection {
background: #4285f4;
color: #fff;
}
.pcr-app .pcr-interaction .pcr-result::selection {
background: #4285f4;
color: #fff;
}
.pcr-app .pcr-interaction .pcr-type.active {
color: #fff;
background: #4285f4;
}
.pcr-app .pcr-interaction .pcr-save,
.pcr-app .pcr-interaction .pcr-cancel,
.pcr-app .pcr-interaction .pcr-clear {
color: #fff;
width: auto;
}
.pcr-app .pcr-interaction .pcr-save,
.pcr-app .pcr-interaction .pcr-cancel,
.pcr-app .pcr-interaction .pcr-clear {
color: #fff;
}
.pcr-app .pcr-interaction .pcr-save:hover,
.pcr-app .pcr-interaction .pcr-cancel:hover,
.pcr-app .pcr-interaction .pcr-clear:hover {
filter: brightness(0.925);
}
.pcr-app .pcr-interaction .pcr-save {
background: #4285f4;
}
.pcr-app .pcr-interaction .pcr-clear,
.pcr-app .pcr-interaction .pcr-cancel {
background: #f44250;
}
.pcr-app .pcr-interaction .pcr-clear:focus,
.pcr-app .pcr-interaction .pcr-cancel:focus {
box-shadow:
0 0 0 1px rgba(255, 255, 255, 0.85),
0 0 0 3px rgba(244, 66, 80, 0.75);
}
.pcr-app .pcr-selection .pcr-picker {
position: absolute;
height: 18px;
width: 18px;
border: 2px solid #fff;
border-radius: 100%;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.pcr-app .pcr-selection .pcr-color-palette,
.pcr-app .pcr-selection .pcr-color-chooser,
.pcr-app .pcr-selection .pcr-color-opacity {
position: relative;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
display: flex;
flex-direction: column;
cursor: grab;
cursor: -webkit-grab;
}
.pcr-app .pcr-selection .pcr-color-palette:active,
.pcr-app .pcr-selection .pcr-color-chooser:active,
.pcr-app .pcr-selection .pcr-color-opacity:active {
cursor: grabbing;
cursor: -webkit-grabbing;
}
.pcr-app[data-theme="nano"] {
width: 14.25em;
max-width: 95vw;
}
.pcr-app[data-theme="nano"] .pcr-swatches {
margin-top: 0.6em;
padding: 0 0.6em;
}
.pcr-app[data-theme="nano"] .pcr-interaction {
padding: 0 0.6em 0.6em 0.6em;
}
.pcr-app[data-theme="nano"] .pcr-selection {
display: grid;
grid-gap: 0.6em;
grid-template-columns: 1fr 4fr;
grid-template-rows: 5fr auto auto;
align-items: center;
height: 10.5em;
width: 100%;
align-self: flex-start;
}
.pcr-app[data-theme="nano"] .pcr-selection .pcr-color-preview {
grid-area: 2 / 1 / 4 / 1;
height: 100%;
width: 100%;
display: flex;
flex-direction: row;
justify-content: center;
margin-left: 0.6em;
}
.pcr-app[data-theme="nano"] .pcr-selection .pcr-color-preview .pcr-last-color {
display: none;
}
.pcr-app[data-theme="nano"] .pcr-selection .pcr-color-preview .pcr-current-color {
position: relative;
background: var(--pcr-color);
width: 2em;
height: 2em;
border-radius: 50em;
overflow: hidden;
}
.pcr-app[data-theme="nano"] .pcr-selection .pcr-color-preview .pcr-current-color::before {
position: absolute;
content: "";
top: 0;
left: 0;
width: 100%;
height: 100%;
background: url('data:image/svg+xml;utf8, <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2 2"><path fill="white" d="M1,0H2V1H1V0ZM0,1H1V2H0V1Z"/><path fill="gray" d="M0,0H1V1H0V0ZM1,1H2V2H1V1Z"/></svg>');
background-size: 0.5em;
border-radius: 0.15em;
z-index: -1;
}
.pcr-app[data-theme="nano"] .pcr-selection .pcr-color-palette {
grid-area: 1 / 1 / 2 / 3;
width: 100%;
height: 100%;
z-index: 1;
}
.pcr-app[data-theme="nano"] .pcr-selection .pcr-color-palette .pcr-palette {
border-radius: 0.15em;
width: 100%;
height: 100%;
}
.pcr-app[data-theme="nano"] .pcr-selection .pcr-color-palette .pcr-palette::before {
position: absolute;
content: "";
top: 0;
left: 0;
width: 100%;
height: 100%;
background: url('data:image/svg+xml;utf8, <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2 2"><path fill="white" d="M1,0H2V1H1V0ZM0,1H1V2H0V1Z"/><path fill="gray" d="M0,0H1V1H0V0ZM1,1H2V2H1V1Z"/></svg>');
background-size: 0.5em;
border-radius: 0.15em;
z-index: -1;
}
.pcr-app[data-theme="nano"] .pcr-selection .pcr-color-chooser {
grid-area: 2 / 2 / 2 / 2;
}
.pcr-app[data-theme="nano"] .pcr-selection .pcr-color-opacity {
grid-area: 3 / 2 / 3 / 2;
}
.pcr-app[data-theme="nano"] .pcr-selection .pcr-color-chooser,
.pcr-app[data-theme="nano"] .pcr-selection .pcr-color-opacity {
height: 0.5em;
margin: 0 0.6em;
}
.pcr-app[data-theme="nano"] .pcr-selection .pcr-color-chooser .pcr-picker,
.pcr-app[data-theme="nano"] .pcr-selection .pcr-color-opacity .pcr-picker {
top: 50%;
transform: translateY(-50%);
}
.pcr-app[data-theme="nano"] .pcr-selection .pcr-color-chooser .pcr-slider,
.pcr-app[data-theme="nano"] .pcr-selection .pcr-color-opacity .pcr-slider {
flex-grow: 1;
border-radius: 50em;
}
.pcr-app[data-theme="nano"] .pcr-selection .pcr-color-chooser .pcr-slider {
background: linear-gradient(to right, red, #ff0, lime, cyan, blue, #f0f, red);
}
.pcr-app[data-theme="nano"] .pcr-selection .pcr-color-opacity .pcr-slider {
background:
linear-gradient(to right, transparent, black),
url('data:image/svg+xml;utf8, <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2 2"><path fill="white" d="M1,0H2V1H1V0ZM0,1H1V2H0V1Z"/><path fill="gray" d="M0,0H1V1H0V0ZM1,1H2V2H1V1Z"/></svg>');
background-size: 100%, 0.25em;
}

View file

@ -1,7 +0,0 @@
{
"name": "Catppuccin",
"version": "0.4.24",
"minAppVersion": "1.0.0",
"author": "Marshall Beckrich",
"authorUrl": "https://github.com/catppuccin/obsidian"
}

File diff suppressed because one or more lines are too long

View file

@ -1,12 +0,0 @@
{
"types": {
"aliases": "aliases",
"cssclasses": "multitext",
"tags": "tags",
"draft": "checkbox",
"date": "date",
"modified": "date",
"created": "date",
"lastmod": "date"
}
}

View file

@ -1,4 +0,0 @@
---
title: Contribution Guides 🌟
draft: false
---

View file

@ -1,23 +1,34 @@
---
title: Performance Benchmarks 📊
lastmod: 2024-10-15
title: Benchmarks
icon: ChartNoAxesCombined
---
import { Callout } from 'fumadocs-ui/components/callout';
These benchmarks were performed on a device with the following specifications:
- Windows 11
- Intel® Core i5-13600K Processor
> [!warning]
> These benchmarks only focus on specific performance aspects of a browser, and your personal results may vary drastically depending on your hardware.
> This page is not a definitive indicator of overall browser speed.
<Callout type="warn">
These benchmarks only focus on specific performance aspects of a browser, and your personal results may vary drastically depending on your hardware.
This page is not a definitive indicator of overall browser speed.
</Callout>
> [!notes] Where are the latest versions?
> This page is maintained by [NOCanoa](https://github.com/NOCanoa). These benchmarks are only updated when he is available.
<Callout title="Where are the latest versions?">
This page is maintained by [NOCanoa](https://github.com/NOCanoa). These benchmarks are only updated when he is available.
</Callout>
## [Browserbench.org](https://browserbench.org/)
## Browserbench.org
### [Speedometer 3.0](https://browserbench.org/Speedometer3.0/)
<Callout type="none">
[Browserbench.org website](https://browserbench.org/)
</Callout>
### Speedometer 3.0
<Callout type="none">
[Speedometer 3.0 benchmark](https://browserbench.org/Speedometer3.0/)
</Callout>
| Version | Score |
| ------------------- | ----- |
@ -46,7 +57,11 @@ xychart-beta
line [21.2,20.8,21.5,21.1,21.8,21.7,21.6,25.5,25.6,25.4,25.6,25.4]
```
### [JetStream 2.2](https://browserbench.org/JetStream/)
### JetStream 2.2
<Callout type="none">
[JetStream 2.2 benchmark](https://browserbench.org/JetStream/)
</Callout>
| Version | Score |
| -------------- | ----- |
@ -65,7 +80,11 @@ xychart-beta
line [228,239,238,237,240]
```
### [MotionMark](https://browserbench.org/MotionMark1.3.1/)
### MotionMark
<Callout type="none">
[MotionMark benchmark](https://browserbench.org/MotionMark1.3.1/)
</Callout>
| Version | Score |
| -------------- | ----- |
@ -84,42 +103,27 @@ xychart-beta
line [1464,1562,1538,1527,1515]
```
<style>
/* Add spacing between each details section */
details {
margin-bottom: 20px; /* Adjust this value to increase or decrease spacing */
}
---
/* Style the summary to make it stand out */
summary {
font-size: 1.25em;
font-weight: bold;
cursor: pointer; /* Changes cursor to pointer when hovering over summary */
}
Archived Benchmarks (Basemark Web 3.0)
### Basemark Web 3.0
/* Add spacing between the summary and the content */
details > summary + * {
margin-top: 10px;
}
</style>
<details>
<summary>Archived Benchmarks (Basemark Web 3.0)</summary>
## [Basemark Web 3.0](https://web.basemark.com/)
<Callout type="none">
[Basemark Web 3.0 benchmark](https://web.basemark.com/)
</Callout>
| Version | Score | CSS | HTML5 | Page Responsiveness |
| ------------------ | ------- | ------ | ------ | ------------------- | --- |
| 1.0.0-a.X | --- | --- | --- | --- | --- |
| 1.0.0-a.34-opt | 1920 | 59% | 91% | 91% | 76% |
| 1.0.0-a.33-opt | 1957 | 59% | 91% | 91% | 76% |
| 1.0.0-a.32-opt | 1732 | 59% | 91% | 90% | 76% |
| 1.0.0-a.15-opt | 2141 | 59% | 91% | 90% | 76% |
| 1.0.0-a.13-opt | 1658 | 59% | 91% | 90% | 76% |
| 1.0.0-a.12-opt | 1874 | 59% | 91% | 91% | 76% |
| 1.0.0-a.11-opt | 1678 | 59% | 91% | 91% | 76% |
| 1.0.0-a.10 | 1660 | 59% | 91% | 91% | 76% |
| 1.0.0-a.9 | 470 | --- | --- | --- | --- |
| ------------------ | ------- | ------ | ------ | ------------------- |
| 1.0.0-a.X | --- | --- | --- | --- |
| 1.0.0-a.34-opt | 1920 | 59% | 91% | 91% |
| 1.0.0-a.33-opt | 1957 | 59% | 91% | 91% |
| 1.0.0-a.32-opt | 1732 | 59% | 91% | 90% |
| 1.0.0-a.15-opt | 2141 | 59% | 91% | 90% |
| 1.0.0-a.13-opt | 1658 | 59% | 91% | 90% |
| 1.0.0-a.12-opt | 1874 | 59% | 91% | 91% |
| 1.0.0-a.11-opt | 1678 | 59% | 91% | 91% |
| 1.0.0-a.10 | 1660 | 59% | 91% | 91% |
| 1.0.0-a.9 | 470 | | | |
| 1.0.0-a.8 | 446.74 | 59% | 91% | 96% |
| 1.0.0-a.7 | 1964.43 | 59% | 91% | 91% |
| 1.0.0-a.6 | 1747.98 | 59% | 91% | 91% |
@ -136,5 +140,3 @@ xychart-beta
bar [475.52, 470.49, 1747.98, 1964.43, 446.74, 470, 1660.89, 1678.49, 1874.49, 1658.87, 2141.63, 1732, 1957, 1920]
line [475.52, 470.49, 1747.98, 1964.43, 446.74, 470, 1660.89, 1678.49, 1874.49, 1658.87, 2141.63, 1732, 1957, 1920]
```
</details>

View file

@ -1,7 +1,8 @@
---
title: Code of Conduct
draft: false
lastmod: 2024-08-24
description: >-
This is the code of conduct for the community. It outlines the expectations
for behavior and how to report unacceptable behavior.
---
## Our Pledge

View file

@ -1,12 +1,14 @@
---
title: Contributing to Zen Browser
draft: false
lastmod: 2025-02-07
title: Contributing
description: Contributing to Zen Browser
---
import { GlobeIcon, BookAIcon, BookIcon } from 'lucide-react'
import { GithubInfo } from 'fumadocs-ui/components/github-info';
Thank you for considering contributing to Zen Browser! We appreciate your time and effort in improving this project. The following is a set of guidelines for contributing to Zen Browser. These guidelines are intended to make it easier for you to get involved.
### Types of Contributions
## Types of Contributions
We welcome a wide range of contributions, including but not limited to:
@ -16,13 +18,16 @@ We welcome a wide range of contributions, including but not limited to:
- **Code Refactoring**: Clean up the code to improve readability, performance, or maintainability.
- **UI/UX Enhancements**: Improve the user interface or user experience of Zen Browser.
### Getting Started
## Getting Started
To help you get started with contributing, we have created separate guides for each repository:
- [[www | Getting Started with Zen's Homepage Development]]
- [[docs | Getting Started with Documentation Contributions]]
- [[translation | Getting Started with Translations]]
<Cards>
<Card title="www" icon={<GlobeIcon />} description="Getting Started with Zen's Homepage Development" href="/contribute/www" />
<Card title="docs" icon={<BookIcon />} description="Getting Started with Documentation Contributions" href="/contribute/docs" />
<Card title="translation" icon={<BookAIcon />} description="Getting Started with Translations" href="/contribute/translation" />
</Cards>
Please follow the appropriate guide based on the repository you want to contribute to.
@ -30,17 +35,18 @@ Please follow the appropriate guide based on the repository you want to contribu
If you find a bug, please open an issue and describe the problem in detail. Include steps to reproduce the bug, the expected behavior, and any relevant information about your environment. Please verify that the bug has not been reported already.
> [!important]
> Open the issue in it's corresponding GitHub repository:
>
> - [Desktop Browser App](https://github.com/zen-browser/desktop/issues)
> - [Zen's Custom Mods](https://github.com/zen-browser/theme-store)
> - [Zen's Homepage Website](https://github.com/zen-browser/www)
> - [This documentation Website](https://github.com/zen-browser/docs)
<Callout type="info">
Open the issue in it's corresponding GitHub repository:
- [Desktop Browser App](https://github.com/zen-browser/desktop/issues)
- [Zen's Custom Mods](https://github.com/zen-browser/theme-store)
- [Zen's Homepage Website](https://github.com/zen-browser/www)
- [This documentation Website](https://github.com/zen-browser/docs)
</Callout>
### Suggesting Features
![[discuss.png]]
![Discuss](/assets/contribute/discuss.png)
We welcome suggestions for new features or improvements to existing ones. To suggest a feature, please start a new Github discussion in the Ideas category.
_Use the correct Github Repository based on the list above_

View file

@ -1,19 +1,18 @@
---
title: Getting Started with Documentation Contributions
draft: false
lastmod: 2024-08-26
title: Docs
description: Getting started with contributing to the documentation for Zen Browser.
---
This guide will help you get started with contributing to the documentation for Zen Browser. The documentation is crucial for helping users and developers understand and use the project effectively. We use Quartz v4 for generating the static documentation site, and we recommend using Obsidian as your Markdown editor for making contributions.
This guide will help you get started with contributing to the documentation for Zen Browser. The documentation is crucial for helping users and developers understand and use the project effectively. We use Fumadocs for generating the static documentation site.
## Prerequisites
Before you begin, ensure you have the following tools installed:
- [**Git**](https://git-scm.com/): Version control system to clone the repository and manage your code.
- [**Node.js**](https://nodejs.org/): Required for building the Quartz v4 site.
- [**Node.js**](https://nodejs.org/): Required for building the NextJS site.
- [**npm**](https://www.npmjs.com/): Node package manager, which comes with Node.js.
- [**Obsidian**](https://obsidian.md/): A powerful Markdown editor recommended for editing and organizing documentation. _(Optional)_
- **IDE of your choice**: You can use any text editor or IDE of your choice.
## 1. Fork the Repository
@ -41,21 +40,21 @@ npm install
This command installs all the necessary packages listed in the `package.json` file.
## 4. Open the Project in Obsidian
## 4. Open the Project in IDE
Open the cloned repository folder in Obsidian to begin editing:
Open the cloned repository folder in your IDE to begin editing:
1. Open Obsidian.
2. Click on "Open folder as vault" and select the folder containing the cloned repository.
3. You can now browse, edit, and add new Markdown files to the project.
1. Open IDE.
2. Select "Open Folder" or "File > Open Folder".
3. Navigate to the cloned repository folder.
## 5. Make Your Changes
You can now start editing the documentation. The project structure is as follows:
- **`content/`** - contains the Markdown files for the documentation.
- **`assets/`** - contains images and other static assets used in the documentation.
- **`data/`** - contains any additional data files needed for the documentation.
- **`content/docs/`** - contains the Markdown files for the documentation.
- **`public/assets/`** - contains images and other static assets used in the documentation.
- _**`src/`** - contains the source code for the documentation site. No changes are required here._
### Writing Guidelines
@ -69,10 +68,14 @@ You can now start editing the documentation. The project structure is as follows
To preview the documentation site locally, run the following command:
```bash
npx quartz build --serve
npm run dev
# or
pnpm dev
# or
yarn dev
```
This command starts a local server and opens the documentation site in your default web browser. The site will automatically reload whenever you make changes to the Markdown files.
This command starts a local server running on `http://localhost:3000` that you can access from your browser. The site will automatically reload whenever you make changes to the Markdown files.
## 7. Commit and Push Your Changes
@ -104,9 +107,9 @@ Your pull request will be reviewed by the maintainers, and you may be asked to m
## Additional Resources
- [Zen Browser Documentation Repository](https://github.com/zen-browser/docs)
- [Quartz v4 Documentation](https://quartz.jzhao.xyz/)
- [[CONTRIBUTING| Contribution Guidelines]]
- [[CODE_OF_CONDUCT | Code of Conduct]]
- [Fumadocs Documentation](https://fumadocs.vercel.app/docs/ui)
- [Contribution Guidelines](/contribute/contributing)
- [Code of Conduct](/contribute/code-of-conduct)
---

View file

@ -0,0 +1,8 @@
{
"title": "Contribute",
"icon": "GitMerge",
"pages": [
"contributing",
"..."
]
}

View file

@ -1,13 +1,13 @@
---
title: Getting Started with Translations
draft: false
lastmod: 2024-08-24
title: Translations
description: How to contribute to the translations for Zen Browser.
---
Thank you for your interest in contributing to the translations for Zen Browser! Ensuring that Zen Browser is accessible to users around the world is a key priority, and your contributions help make this possible. This guide will walk you through the process of getting started with translating Zen Browser using Crowdin.
> [!note]
> If you want to translate a language that is not currently available in the Crowdin project, please reach out to the developers on Discord. Well be happy to add it for you!
<Callout type="info">
If you want to translate a language that is not currently available in the Crowdin project, please reach out to the developers on Discord. Well be happy to add it for you!
</Callout>
## Prerequisites
@ -57,8 +57,8 @@ Crowdin allows you to track the progress of the translation project and stay upd
- [Zen Browser Translation Project on Crowdin](https://crowdin.com/project/zen-browser)
- [Crowdin Documentation](https://support.crowdin.com/)
- [[CONTRIBUTING| Contribution Guidelines]]
- [[CODE_OF_CONDUCT | Code of Conduct]]
- [Contribution Guidelines](/contribute/contributing)
- [Code of Conduct](/contribute/code-of-conduct)
---

View file

@ -1,7 +1,6 @@
---
title: Getting Started with Zen's Homepage Development
draft: false
lastmod: 2024-08-24
title: Homepage
description: Learn how to set up and contribute to the development of Zen Browser's homepage.
---
This guide will walk you through the steps required to set up and contribute to the development of Zen Browser's homepage. Whether you're fixing bugs, adding new features, or enhancing the design, this guide will help you get started.
@ -104,8 +103,8 @@ Your pull request will be reviewed by the maintainers, and you may be asked to m
## Additional Resources
- [Zen Browser Homepage Repository](https://github.com/zen-browser/www)
- [[CONTRIBUTING| Contribution Guidelines]]
- [[CODE_OF_CONDUCT | Code of Conduct]]
- [Contribution Guidelines](/contribute/contributing)
- [Code of Conduct](/contribute/code-of-conduct)
---

View file

@ -1,10 +1,15 @@
---
title: Frequently Asked Questions ❓
lastmod: 2025-01-10
title: FAQ
icon: CircleHelp
---
import { Callout } from 'fumadocs-ui/components/callout';
import { InlineTOC } from 'fumadocs-ui/components/inline-toc';
Welcome to the Zen Browser FAQ section! Here, you'll find answers to common questions and helpful tips to enhance your experience with Zen Browser. If your question isn't covered here, feel free to explore our community forums [r/zen_browser](https://www.reddit.com/r/zen_browser) or reach out to the support team.
<InlineTOC items={toc} />
## How can I use horizontal tabs?
Zen Browser will not support horizontal tabs in the near future. The decision to focus on **Vertical Tabs** is a core design choice, with the entire Zen Browser experience built around this concept. This approach is intended to maximize screen space and improve navigation, making vertical tabs an essential part of Zen's philosophy.
@ -15,30 +20,34 @@ At the moment, our team does not have the time or resources to develop Android o
## Why can't Zen Browser play DRM-protected content?
> [!important] This only affects Microsoft Windows and MacOS
<Callout type="warn">
This only affects Microsoft Windows and MacOS
</Callout>
> [!question]- What is DRM?
> [Digital Rights Management](https://wikipedia.org/wiki/Digital_rights_management) (DRM) is a technology used to control how digital content, such as videos and music, can be accessed and used. DRM is commonly used by streaming services to protect copyrighted content. When you try to play DRM-protected content, the website verifies if the necessary DRM software is available on your browser. Most browsers use [**Widevine**](https://www.widevine.com), a DRM technology developed by Google, to facilitate this.
<Callout type="none" title="What is DRM?">
[Digital Rights Management](https://wikipedia.org/wiki/Digital_rights_management) (DRM) is a technology used to control how digital content, such as videos and music, can be accessed and used. DRM is commonly used by streaming services to protect copyrighted content. When you try to play DRM-protected content, the website verifies if the necessary DRM software is available on your browser. Most browsers use [**Widevine**](https://www.widevine.com), a DRM technology developed by Google, to facilitate this.
</Callout>
Zen Browser currently lacks DRM-support, because it does not have a Widevine license. Acquiring such a license requires the payment of large fees (at least $5,000). Acquiring this license is financially unresponsible for the developer of Zen. This means that DRM-protected media cannot be played in Zen Browser for the foreseeable future.
We have to also consider that in order to be able to apply for this license, Zen must be a part of a company with size at least as big as Mozilla or Brave.
> [!question]- Which Services Are Affected?
> Due to the lack of DRM support, you will not be able to stream content from the following services in Zen Browser:
>
> - **HBO Max**
> - **Netflix**
> - **Spotify**
> - **Disney+**
> - **Amazon Prime Video**
> - **Apple Music**
> - **Google Play Movies & TV**
> - **And possible other services that use DRM not listed here**
<Callout type="none" title="Which Services Are Affected?">
Due to the lack of DRM support, you will not be able to stream content from the following services in Zen Browser:
- **HBO Max**
- **Netflix**
- **Spotify**
- **Disney+**
- **Amazon Prime Video**
- **Apple Music**
- **Google Play Movies & TV**
- **And possible other services that use DRM not listed here**
</Callout>
> [!info] Alternative solutions
>
> - Use a browser that has a Widevine license, such as [**Mozilla Firefox**](https://www.mozilla.org/firefox/), when streaming DRM-protected content.
> - Use the native desktop app for the service you want to use
<Callout type="info" title="Alternative solutions">
- Use a browser that has a Widevine license, such as [**Mozilla Firefox**](https://www.mozilla.org/firefox/), when streaming DRM-protected content.
- Use the native desktop app for the service you want to use
</Callout>
## How do I know Zen is safe?
@ -55,7 +64,9 @@ Your support helps the team maintain and enhance Zen Browser for everyone!
## How do I use the Split View feature?
> [!hint] Use shortcuts to perform Split View actions faster!
<Callout type="info" title="Tip">
Use shortcuts to perform Split View actions faster!
</Callout>
1. Select multiple tabs by left-clicking them while holding the `Ctrl` key, or left-click 2 tabs while holding the `Shift` key to select all tabs in between
2. Right click a tab, and select `Split x Tabs`
@ -73,7 +84,9 @@ Once this setting is enabled, you can hover your mouse over the tab bar and use
## Where do report problems and bugs?
> [!caution] New features are not bugs. Please see [Where do I recommend features?](#where-do-i-recommend-features) below
<Callout type="info">
New features are not bugs. Please see [Where do I recommend features?](#where-do-i-recommend-features) below
</Callout>
If you want report an issue or a bug with the browser, you can do so on the browser's GitHub page. Before submitting your request, it's mandatory to check if the issue has already been reported. You can do this by searching through existing issues on the [GitHub issues page](https://github.com/zen-browser/desktop/issues).

View file

@ -1,17 +1,17 @@
---
title: 1Password Integration Fix
draft: false
lastmod: 2024-10-27
title: 1Password Integration
description: How to integrate 1Password Desktop App with Zen Browser
---
This [[guides/index|guide]] is designed to help you integrate [1Password Desktop App](https://1password.com/downloads) with Zen Browser, for a more **straight forward workflow** when accessing your credentials using this password manager browser extension.
This Guide is designed to help you integrate [1Password Desktop App](https://1password.com/downloads) with Zen Browser, for a more **straight forward workflow** when accessing your credentials using this password manager browser extension.
> [!important]
> This guide only applies for **Linux** and **MacOS** users.
>
> **Windows** users can still use the Browser Extension without integration with the Desktop App
>
> See: [Adding another trusted browser - 1Password](https://support.1password.com/1password-browser-connection-security/#adding-another-trusted-browser)
<Callout type="warn">
This guide only applies for **Linux** and **MacOS** users.
**Windows** users can still use the Browser Extension without integration with the Desktop App
See: [Adding another trusted browser - 1Password](https://support.1password.com/1password-browser-connection-security/#adding-another-trusted-browser)
</Callout>
1Password browser integrations follows a [list of well-known/trusted browser](https://support.1password.com/1password-browser-connection-security/), with this integration account information and encryption keys are transferred using this connection to allow the 1Password app and browser extension to share your vaults and lock state and allowing you to unlock your Browser Extension Vault with [bio-metric](https://en.wikipedia.org/wiki/Biometrics) data.
@ -58,13 +58,14 @@ Sources:
In MacOS you can use the Graphical Interface of the Desktop app to add Zen Browser to the trusted browsers list.
#### 1. Go into the 1Password desktop app and open Settings.
1. Go into the 1Password desktop app and open Settings.
#### 2. In the Browser tab, click "Add Browser".
2. In the Browser tab, click "Add Browser".
#### 3. In your Applications folder, find and add "Zen Browser", then authorize 1Password when prompted.
3. In your Applications folder, find and add "Zen Browser", then authorize 1Password when prompted.
![[macos-settings-4.png]]
![macos settings](/assets/1password/macos-settings-4.png)
> [!todo]
> If you would like to contribute with Screenshots for steps 1, 2 and 3 send me a message in our [Discord Server](https://discord.gg/zen-browser) **@mr. docs**
<Callout type="info">
If you would like to contribute with Screenshots for steps 1, 2 and 3 send me a message in our [Discord Server](https://discord.gg/zen-browser) **@mr. docs**
</Callout>

View file

@ -1,7 +1,7 @@
---
title: Building Zen Browser 📦
lastmod: 2025-04-03
title: Building Zen Browser
---
import { Accordion, Accordions } from 'fumadocs-ui/components/accordion';
We've taken the time to make building Zen Browser as easy as possible, independent of your operating system or technical knowledge.
@ -17,10 +17,13 @@ The following resources are essential for a successful build. Without them, you
- 7-Zip ([Download here](https://www.7-zip.org/download.html)) Used to extract Firefox source archives.
- sccache ([Download here](https://github.com/mozilla/sccache/releases)) A caching tool that speeds up rebuilds by storing compiled objects.
> **Note:** If you're using Windows, ensure that all the basic software requirements are added to the `PATH` variable.
<Callout>
If you're using Windows, ensure that all the basic software requirements are added to the `PATH` variable.
</Callout>
> [!failure]
> We cannot provide support if a build fails. Please understand this before proceeding with the following steps.
<Callout type="warn">
We cannot provide support if a build fails. Please understand this before proceeding with the following steps.
</Callout>
## Step 1: Clone the Project
@ -94,7 +97,6 @@ npm start
This command launches the browser, allowing you to see your changes in action.
---
---
### Common Build Errors & Fixes
@ -130,6 +132,4 @@ This command launches the browser, allowing you to see your changes in action.
> Perform a clean rebuild:
> ```sh
> npm run reset-ff && npm run init && npm run build
> ```
---
> ```

View file

@ -1,8 +1,6 @@
---
title: Why have optimized builds been removed?
aliases:
- Choose between the generic and optimized build
lastmod: 2024-12-23
title: Optimized builds
description: Why have optimized builds been removed?
---
For Windows and Linux, Zen Browser used to have the option to download optimized builds. These builds utilized AVX2 instructions to improve perfomance. These builds have been removed because of the following reasons:

View file

@ -1,20 +1,23 @@
---
title: Live Editing Zen Theme
lastmod: 2024-09-17
description: Learn how to live edit the appearance of Zen Browser by editing the userChrome.css file.
---
This [[guides/index|guide]] will help you customize the appearance of Zen Browser by live editing the `userChrome.css` file. Follow the steps below to start customizing your browser's theme.
import { Callout } from 'fumadocs-ui/components/callout';
This Guide will help you customize the appearance of Zen Browser by live editing the `userChrome.css` file. Follow the steps below to start customizing your browser's theme.
## Step 1: Access the Profile Folder
<Callout type="warn">
On the Flatpak version of Zen, the profile folder will be located at `~/.var/app/app.zen_browser.zen/.zen`.
</Callout>
1. Open Zen Browser.
2. Type `about:support` in the address bar and press Enter.
3. Look for the **Application Basics** section.
4. Click on **Open Profile Folder**. This will open the folder where Zen Browser stores your user data.
> [!warning]
> On the Flatpak version of Zen, the profile folder will be located at `~/.var/app/app.zen_browser.zen/.zen`.
## Step 2: Create the `chrome` Folder
1. In the Profile Folder, create a new folder and name it `chrome`.
@ -23,34 +26,38 @@ This [[guides/index|guide]] will help you customize the appearance of Zen Browse
## Step 3: Open Style Editor in Zen Browser
<Callout type="warn" title="How do I enable the Browser Developer Tools?">
After Zen Browser version `1.0.0-a.31` the Browser Developer Tools is <strong>disabled</strong> by default for security.
1. Open the `about:config` page. This page contains advanced settings for the browser.
2. Search for `devtools.debugger.remote-enabled`
3. Toggle the setting to `true` by double-clicking on it
</Callout>
1. In Zen Browser, press `Ctrl + Shift + Alt + I` to open the Developer Tools.
2. Navigate to the **Style Editor** tab.
3. In the filter/search bar, type `userChrome` to locate the `userChrome.css` file you created earlier.
> [!warning] How do I enable the Browser Developer Tools?
> After Zen Browser version `1.0.0-a.31` the Browser Developer Tools is **disabled** by default for security.
>
> 1. Open the `about:config` page. This page contains advanced settings for the browser.
> 2. Search for `devtools.debugger.remote-enabled`
> 3. Toggle the setting to `true` by double-clicking on it
## Step 4: Edit the `userChrome.css` File
<Callout type="warn">
If a style does not apply as expected, try adding the `!important` keyword at the end of the CSS rule. This forces the browser to apply the style regardless of any other existing styles.
</Callout>
<Callout type="info" title="Tip">
If you wish to edit pop-ups or menus that automatically hide, be sure to enable the `Disable Popup Auto-Hide` option from the <em>Browser Toolbox</em> settings menu ( ⋯ button )
</Callout>
1. The `userChrome.css` file should now be visible in the Style Editor.
2. You can start editing the file directly within the Style Editor.
- **Note:** You can use the **Inspect** button ![[inspect.png]] to hover over and select elements on the page. This allows you to learn about the `id`, `class`, or other attributes of elements, which you can then target in your `userChrome.css` file.
![inspect button](/assets/live-editing/inspect.png)
- **Note:** You can use the **Inspect** button to hover over and select elements on the page. This allows you to learn about the `id`, `class`, or other attributes of elements, which you can then target in your `userChrome.css` file.
3. To apply your changes, save the file by clicking **Save** or by pressing `Ctrl + S`.
> [!warning]
> If a style does not apply as expected, try adding the `!important` keyword at the end of the CSS rule. This forces the browser to apply the style regardless of any other existing styles.
> [!tip]
> If you wish to edit pop-ups or menus that automatically hide, be sure to enable the `Disable Popup Auto-Hide` option from the _Browser Toolbox_ settings menu ( ⋯ button )
Any changes you make to the `userChrome.css` file will be applied immediately to Zen Browser.
Use this file to customize various UI elements, such as colors, fonts, and the layout.
You can use this guide to help you [[themes-marketplace| create your Zen theme and publish it.]]
You can use this guide to help you [create your Zen theme and publish it.](/themes-store/themes-marketplace)
---

View file

@ -1,9 +1,11 @@
---
title: Managing Firefox Profiles
lastmod: 2024-08-23
description: Learn how to manage Firefox profiles effectively, preserving key elements of your browsing experience.
---
This [[guides/index|guide]] will give you a comprehensive understanding of Firefox profiles, helping you manage them effectively even in the most challenging situations. By following this guide, you'll learn how to preserve key elements of your browsing experience, including bookmarks, history, passwords, and more.
import { Callout } from 'fumadocs-ui/components/callout';
This Guide will give you a comprehensive understanding of Firefox profiles, helping you manage them effectively even in the most challenging situations. By following this guide, you'll learn how to preserve key elements of your browsing experience, including bookmarks, history, passwords, and more.
## Goal
@ -21,14 +23,20 @@ This guide will help you:
### 1. Open Your Current Profile Folder
<Callout type="danger" title="Turn Off Firefox">
This step is crucial to avoid corruption, as Firefox continuously reads and writes data while running.
</Callout>
1. Go to `about:support` in Firefox.
2. Under the "Application Basics" section, click on "Open Folder" next to "Profile Folder."
> [!IMPORTANT] **Turn Off Firefox**
> This step is crucial to avoid corruption, as Firefox continuously reads and writes data while running.
### 2. Copy Essential Files
<Callout type="info" title="Optional Files">
- **storage folder**: If you want to keep add-on customizations (this may not work 100% of the time).
- **chrome folder**: If you want to retain your interface customizations.
</Callout>
After turning off Firefox, copy the following files from your profile folder:
- **places.sqlite**: Contains bookmarks and history.
@ -39,13 +47,12 @@ After turning off Firefox, copy the following files from your profile folder:
- **sessionCheckpoints.json + sessionstore.jsonlz4**: Saves your currently open tabs.
- **prefs.js**: Contains your `about:config` settings.
> [!NOTE] **Optional Files**
>
> - **storage folder**: If you want to keep add-on customizations (this may not work 100% of the time).
> - **chrome folder**: If you want to retain your interface customizations.
### 3. Create and Set Up a New Profile
<Callout type="warn" title="Incompatibility Error">
If Firefox opens with an incompatibility error after pasting the files, go to the new profile folder and move the `compatibility.ini` file somewhere else.
</Callout>
1. Go to `about:profiles` in Firefox.
2. Click on "Create a New Profile."
3. Select a folder to store the new profile.
@ -54,10 +61,6 @@ After turning off Firefox, copy the following files from your profile folder:
6. **Turn off Firefox**.
7. Paste the files you copied earlier into the new profile folder.
> [!WARNING] **Incompatibility Error**
>
> If Firefox opens with an incompatibility error after pasting the files, go to the new profile folder and move the `compatibility.ini` file somewhere else.
### 4. Final Step: Set as Default Profile
After ensuring everything works correctly, go back to `about:profiles` and set the newly created profile as the default. This will make it your main profile moving forward.

View file

@ -0,0 +1,4 @@
{
"name": "Guides",
"icon": "Bookmark"
}

69
content/docs/index.mdx Normal file
View file

@ -0,0 +1,69 @@
---
title: Documentation
description: Welcome to Zen Browser's documentation
icon: BookOpen
---
import {
SpeechIcon,
BookIcon,
WrenchIcon,
PaletteIcon,
HelpCircleIcon,
BarChartIcon,
HeartIcon,
ShieldIcon
} from 'lucide-react';
Welcome to **Zen Browser's Documentation!** Here, you'll find everything you need to get the most out of your browsing experience. Dive in to explore how Zen can make your browsing more secure, private, and efficient.
<Cards cols={2}>
<Card
icon={<BookIcon />}
title="User Manual"
href="/user-manual"
description="Complete user guide and features"
/>
<Card
icon={<WrenchIcon />}
title="Guides"
href="/guides/live-editing"
description="Tutorials and how-to guides"
/>
<Card
icon={<PaletteIcon />}
title="Mods Registry"
href="/themes-store/themes-marketplace"
description="Custom themes and modifications"
/>
<Card
icon={<HelpCircleIcon />}
title="FAQ"
href="/faq"
description="Frequently asked questions"
/>
<Card
icon={<BarChartIcon />}
title="Benchmarks"
href="/benchmarks"
description="Performance comparisons"
/>
<Card
icon={<ShieldIcon />}
title="Security"
href="/security"
description="Security features and protocols"
/>
<Card
icon={<SpeechIcon />}
title="Code of Conduct"
href="/contribute/code-of-conduct"
description="Community guidelines"
/>
<Card
icon={<HeartIcon />}
title="Contribute"
href="/contribute/contributing"
description="How to contribute to the project"
/>
</Cards>

16
content/docs/meta.json Normal file
View file

@ -0,0 +1,16 @@
{
"title": "Zen",
"root": true,
"pages": [
"---Introduction---",
"index",
"faq",
"security",
"benchmarks",
"---Getting Started---",
"...",
"---Developers---",
"contribute",
"themes-store"
]
}

View file

@ -1,14 +1,14 @@
---
title: Privacy & Security 🔐
aliases:
- Privacy & Security 🔐
title: Privacy & Security
description: At Zen Browser, your online safety and privacy are our top priorities. We've implemented a range of security features to ensure you're protected while browsing.
lastmod: 2024-09-18
icon: ShieldCheck
---
import { Callout } from 'fumadocs-ui/components/callout';
> [!important] Password, Cookies, and Cache Management
> All passwords, cookies, and cache in Zen Browser are **managed by Firefox**. This means your saved passwords are **automatically encrypted**, providing an extra layer of security to keep your login credentials safe. Cookies and cache are also handled according to Firefox's strict privacy policies, ensuring your data is stored securely and only accessible to you.
> For more information visit [Firefox Privacy and Security](https://support.mozilla.org/es/products/firefox/privacy-and-security)
<Callout title="Password, Cookies, and Cache Management">
All passwords, cookies, and cache in Zen Browser are **managed by Firefox**. This means your saved passwords are **automatically encrypted**, providing an extra layer of security to keep your login credentials safe. Cookies and cache are also handled according to Firefox's strict privacy policies, ensuring your data is stored securely and only accessible to you.
For more information visit [Firefox Privacy and Security](https://support.mozilla.org/es/products/firefox/privacy-and-security)
</Callout>
## Adjusting Security Settings
@ -21,8 +21,9 @@ Zen Browser lets you customize privacy and security settings to improve your bro
## Security settings
> [!success] Latest Firefox Version
> Zen Browser is built on the **latest version of Firefox**, one of the most secure browsers available today. This means you benefit from all of Firefox's security patches and updates as soon as they are released, keeping you safe from known vulnerabilities.
<Callout type="info" title="Latest Firefox Version">
Zen Browser is built on the **latest version of Firefox**, one of the most secure browsers available today. This means you benefit from all of Firefox's security patches and updates as soon as they are released, keeping you safe from known vulnerabilities.
</Callout>
### 1. OCSP Enabled
@ -32,7 +33,9 @@ Zen Browser uses **OCSP (Online Certificate Status Protocol)** to check the vali
**HTTPS Only Mode** ensures that you connect securely to websites by using the HTTPS version whenever possible. HTTPS encrypts the communication between you and the website, protecting your data from being intercepted by hackers.
> [!info] You can enable HTTPS-Only Mode in **all windows** or **private windows only**.
<Callout type="info">
You can enable HTTPS-Only Mode in **all windows** or **private windows only**.
</Callout>
For more information visit [HTTPS-Only Mode in Firefox](https://support.mozilla.org/en-US/kb/https-only-prefs)
@ -54,13 +57,17 @@ Zen Browser treats insecure SSL connections (those that don't meet modern securi
To protect your privacy, Zen Browser includes **tracking protection**. This feature automatically blocks websites and advertisers from tracking your online activity, making it harder for third parties to collect your data and show you targeted ads.
> [!info] You can choose from three levels of Tracking Protection: **Standard**, **Strict**, or **Custom**.
<Callout type="info">
You can choose from three levels of Tracking Protection: **Standard**, **Strict**, or **Custom**.
</Callout>
### 2. DNS over HTTPS
Zen Browser allows the users to enable DNS over HTTPS, which **sends your request for a domain name through an encrypted connection**, providing a secure DNS and making it harder for others to see which website youre about to access.
> [!info] You can choose from three protection levels: **Default Protection**, **Increased Protection**, or **Max Protection**.
<Callout type="info">
You can choose from three protection levels: **Default Protection**, **Increased Protection**, or **Max Protection**.
</Callout>
---

View file

@ -0,0 +1,4 @@
{
"title": "Mods Registry",
"icon": "SwatchBook"
}

View file

@ -1,8 +1,9 @@
---
title: Mods Registry Preferences ⚙️
lastmod: 2025-02-07
title: Preferences
---
import { Callout } from 'fumadocs-ui/components/callout';
The `preferences.json` file allows mod developers to define custom preferences that control the behavior and appearance of mods in the Zen Browser. Each preference is defined with a `property`, a `label`, a `type`, and optionally `options` (for dropdown preferences), `defaultValue`, `placeholder` (to configure preference placeholder) and `disabledOn` (to disable property on selected OS). The `preferences.json` file contains a list of these preference objects at its root.
## Preferences fields
@ -156,8 +157,7 @@ For example: `e.g: 10px`
---
<details>
<summary><font weight="bold" size=4.75><b>See Full Example</b></font></summary>
See Full Example
Below is a full example of what a `preferences.json` file might look like with multiple preference objects in its root. Each object represents a preference defined for a mod:
@ -202,7 +202,6 @@ In this example:
- Optionally, each object defines either a `defaultValue`, `disabledOn` or `placeholder`.
- Dropdown preferences have to include an `options` field, with each option having a `label` and a `value`.
</details>
---
@ -243,10 +242,12 @@ You can also have negative conditions
### Dropdown Preferences
> [!attention] > `property` fields defined in `preferences.json` using the `"dropdown"` type will have one key difference when used in your mods CSS: **dots (`.`) in the `property` name are replaced with hyphens (`-`)**.
>
> E.g. `mod.mymod.background_color` becomes `mod-mymod-background_color` in the CSS file.
> This transformation ensures that the property can be used as an attribute selector or inside a media query.
<Callout type="warn" title="Attention">
`property` fields defined in `preferences.json` using the `"dropdown"` type will have one key difference when used in your mod's CSS: <strong>dots (`.`) in the `property` name are replaced with hyphens (`-`)</strong>.
<br/><br/>
E.g. `mod.mymod.background_color` becomes `mod-mymod-background_color` in the CSS file.
This transformation ensures that the property can be used as an attribute selector or inside a media query.
</Callout>
For dropdown preferences, you can detect the selected value using the `:has(){:css}` CSS pseudo-class, which applies styles based on the selected attribute and value in the DOM.
@ -293,8 +294,7 @@ In this example:
---
<details>
<summary><font weight="bold" size=4.75><b>See Full Example</b></font></summary>
See Full Example
Suppose your `preferences.json` file includes these two preferences:
@ -351,16 +351,17 @@ This allows users to:
- Toggle dark mode on/off using the checkbox.
- Select a background color from the dropdown, which dynamically changes the background and text colors based on the user's choice.
</details>
### String Preferences
String preferences can be detected in your CSS using the `var(--property)` operator. The preference property is saved at `:root` level.
> [!attention] > `property` fields defined in `preferences.json` using the `"string"` type will have one key difference when used in your mods CSS: **dots (`.`) in the `property` name are replaced with hyphens (`-`)**.
>
> E.g. `mod.mymod.background_color` becomes `mod-mymod-background_color` in the CSS file.
> This transformation ensures that the property can be used as an attribute selector or inside a media query.
<Callout type="warn" title="Attention">
`property` fields defined in `preferences.json` using the `"string"` type will have one key difference when used in your mod's CSS: <strong>dots (`.`) in the `property` name are replaced with hyphens (`-`)</strong>.
<br/><br/>
E.g. `mod.mymod.background_color` becomes `mod-mymod-background_color` in the CSS file.
This transformation ensures that the property can be used as an attribute selector or inside a media query.
</Callout>
For example, if you have a preference to enable dark mode in your mod:

View file

@ -1,8 +1,9 @@
---
title: Mods Registry Submission Guidelines 📋
lastmod: 2025-02-07
title: Submission Guidelines
---
import { Callout } from 'fumadocs-ui/components/callout';
If you are a [mod developer] and would like to submit your mod, please follow these guidelines:
1. **Mod requirements**:
@ -19,7 +20,7 @@ If you are a [mod developer] and would like to submit your mod, please follow th
- Your mod's screenshot must be a `PNG` with a size of `600x400` (it can be resized after upload).
- Your mod must contain a valid `README` describing the mod and how to use it.
- If your mod has any preferences values, they must be set in the `preferences` text area as a `JSON` object.
- See how preferences work [here](themes-store/themes-marketplace-preferences.md).
- See how preferences work [here](./themes-marketplace-preferences.mdx).
3. **Mod Submission**:
@ -32,5 +33,6 @@ If you are a [mod developer] and would like to submit your mod, please follow th
- If you would like to update your mod, please create an issue [here](https://github.com/zen-browser/theme-store/issues/new)
- Please explain the changes you have made.
> [!info]
> Mods are automatically updated and generated by the bot. If your mod is not approved, you will receive a message with the reason why it was not approved.
<Callout type="info">
Mods are automatically updated and generated by the bot. If your mod is not approved, you will receive a message with the reason why it was not approved.
</Callout>

View file

@ -1,6 +1,5 @@
---
title: Information about Mods Registry
lastmod: 2025-02-07
title: Information
---
The Mods Registry is a place where you can find and install mods for Zen Browser.

View file

@ -0,0 +1,10 @@
---
title: History & Browsing Sessions
---
{/* TODO
## long press(/click?) on the back or forward button to see and navigate history of the same tab
## use history sidebar (ctrl + h) and history section in main menu to open previously opened sites
## restore previous windows (include "open previous windows and tabs" option in Settings > General; and then show how to do it via the main menu, as well as keyboard shortcuts)
## restore previous tabs (via main menu, via tab context menu, as well as keyboard shortcuts)
## What if you lose your tabs when opening Zen?
*/}

View file

@ -0,0 +1,3 @@
---
title: Sidebar
---

View file

@ -0,0 +1,72 @@
---
title: Bookmarks in Zen
---
Zen, as a fork of Firefox, inherits its webpage bookmarking system from the Gecko engine, with some of that extra Zen touch added on top. Zen offers two vertical tab layouts: **Single toolbar layout**, which integrates a compact address bar into the vertical tabs toolbar, and **Multiple toolbars layout**, featuring a traditional, full-size address bar in a separate horizontal toolbar. This guide covers the basics of creating and managing bookmarks, tailored to your chosen Zen layout.
## How do I bookmark a page
To bookmark a page, find and click on the bookmark icon in the address bar. A pop up dialog will allow you to name and move your bookmark.
{/* TODO: insert video of popup dialog */}
<Callout type="info" title="Tips:">
Did you know Zen offers Workspaces specific bookmarks?
</Callout>
In **Multiple toolbars layout**, you will find the bookmark icon exposed on the right of your address bar, while in **Single toolbar layout**, you must first expand your compact address bar by clicking on it then find the icon there.
{/* TODO: **Single toolbar layout**
*insert video*
**Multiple toolbars layout**
*insert video* */}
<Callout type="info" title="Tips:">
While you could use your mouse to click the bookmark icon, we recommend using the keyboard shortcut `Ctrl/Cmd + D` for bookmarking, especially in **Single toolbar layout**.
</Callout>
Alternatively, you can bookmark a single tab by right-clicking it and selecting `"Bookmark Tab..."` from the context menu, which opens a detailed bookmarking dialog with options for *tagging* and *keywords*.
To bookmark multiple or all open tabs, select them in the vertical tabs toolbar, right-click, and choose `"Bookmark Tabs..."` from the context menu. This will bookmark the selected tabs into a new bookmark folder.
## How do I find and manage my bookmarks
You can access, edit, organize and delete your bookmarks in a variety of ways in Zen.
### Bookmarks Toolbar and Bookmarks Menu
Taken from the default behavior of Gecko, Zen offers 3 locations (or, groups) for bookmarks:
- **Bookmarks Toolbar**: This can be considered a public location for bookmarks, displayed in the browser's [chrome](https://developer.mozilla.org/en-US/docs/Glossary/Chrome). You typically find it beneath the main browser toolbar, which is featured in **Multiple toolbars layout** in Zen, while in **Single toolbar layout**, hovering your cursor to the top edge will display the hidden Bookmarks Toolbar, next to your window controls. To toggle the visibility of your Bookmarks Toolbar, use the shortcut `Ctrl/Cmd + Shift + B`
{/* TODO: *insert video* */}
<Callout type="info" title="Tips:">
If you want to fully hide the Bookmarks Toolbar, you can change these settings in `about:config`
- `browser.toolbars.bookmarks.visibility = 'never'`: Never show your Bookmarks, revert by changing value to `'always'`.
- `zen.view.experimental-no-window-controls = 'true'`: disable hovering window controls in **Single toolbar layout**, effectively remove access to the Bookmarks Toolbar.
</Callout>
- **Bookmarks Menu**: This can be considered a private location for bookmarks, only accessible via opening the Bookmarks Sidebar or the Bookmarks Library.
- **Other Bookmarks**: Location for your miscellaneous unorganized bookmarks. Other Bookmarks will show up at the end of the Bookmarks Toolbar but you can choose to hide it by right clicking and disable `"Show Other Bookmarks"`.
### Bookmarks Sidebar
Your bookmarks are also avaialbe via what is known as the [Firefox Sidebar](https://support.mozilla.org/kb/use-firefox-sidebar-access-bookmarks-history-synced). The Sidebar can be opened by adding a Sidebar button to your controls, or preferably by using the shortcut `Ctr/Cmd + B` to open the Bookmarks Sidebar. You can find all of your bookmarks here including entries from both Bookmarks Toolbar and Bookmarks Menu, in the form of a tree structure explorer with access to a searching function at the top.
{/* TODO: *insert video/image* */}
### Bookmarks Library
The Firefox Library is a Bookmarks/History/Download Manager. You can access the Library by the shortcut `Ctrl/Cmd + Shift + O`. While you can manage your bookmarks here in the same fashion as the Sidebar with extra details, you should only open the Library for the purpose of Importing and Backing up Zen's data.
*insert library image*
<Callout>
_All shortcuts can be modified via `Settings > Keyboard Shortcuts`._
</Callout>
Learn more about Bookmarks in Mozilla Support: https://support.mozilla.org/kb/bookmarks-firefox

View file

@ -0,0 +1,38 @@
---
title: Compact Mode
description: Minimalistic interface for focused browsing
---
Compact Mode is one of Zen's main feature that let you hide all browser toolbars and give wider view for the website you're currently visit.
You can activate this feature by right click on empty area on the `toolbar > "Compact Mode" > "Enable compact mode"`, or use `Alt + Ctrl/Cmd + C` keyboard shortcut.
{
<div align="center">
<video width="100%" loop autoPlay>
<source src="/assets/user-manual/compact-mode/compact-mode.webm" />
Your browser does not support the video tag.
</video>
</div>
}
In Single Toolbar mode, activating Compact Mode will hide the tab sidebar. You can access tab sidebar by hovering the side edge of the browser (based on whether `"Tabs on the right"` is activated or not).
In Multiple Toolbar or Collapsed Toolbar mode, you can choose which bar to hide. These options are accessible below "Compact Mode" > "Enable compact mode" when right clicking the toolbar:
- **Hide sidebar**: Hide the tab sidebar, accessible when hovering side edge of the browser.
- **Hide toolbar**: Hide the top toolbar, accessible when hovering top edge of the browser.
- **Hide both**: Hide both top toolbar and tab sidebar.
<div align="center">
<img src="/assets/user-manual/compact-mode/enable-compact-mode.png" alt="Compact Mode" width="500" />
</div>
You can also use these extra shortcuts to show the hidden bars, suitable for heavy keyboard users. Unlike usual hovering gesture, showing sidebar/toolbar using these shortcuts is done persistently, until you pressed the shortcut again to hide it.
- **Toggle Floating Sidebar**: `Alt + Ctrl/Cmd + S` - Show the tab sidebar for all toolbar modes
- **Toggle Floating Toolbar**: `Alt + Ctrl/Cmd + W` - Show the top toolbar for Multiple & Collapsed Toolbar mode
<Callout>
_All shortcuts can be modified via `Settings > Keyboard Shortcuts`._
</Callout>

View file

@ -0,0 +1,51 @@
---
title: Extensions
description: Get to know how extensions work in Zen
---
Extensions are a small software piece that enhance and personalize a browser by adding or modifying browser function and features. Example of commonly installed extensions includes ad blockers, easy reader mode, privacy and tracking managers, media downloaders, password managers, and tweaks for commonly used websites.
Since Zen is based on Mozilla Firefox, you can install extensions from the same source: https://addons.mozilla.org.
![Mozilla Add-ons](/assets/user-manual/extensions/mozilla-addons.png)
<Callout> Due to fundamental difference on how Zen does theming, Firefox themes are not supported in Zen. </Callout>
<Callout title="Difference between Extensions and Zen Mods"> Extensions are made with HTML, CSS and Javascript. Depending on the permissions you enabled when installing, extensions generally affect websites you visit. Meanwhile, Zen Mods are made with CSS and only affect the browser UI. </Callout>
### Manage Extensions using Extensions Button
When you launch Zen, there will be an Extension button (with jigsaw icon) in the top bar. It will show a list of enabled extensions, where you can see which permissions the extensions have for the site you're visiting. You can click the gear icon beside each extensions to perform some common actions like Pin to Toolbar, Manage Extension to access extension settings, Remove Extension, or [Report Extension to Mozilla](https://support.mozilla.org/en-US/kb/reporting-extensions-and-themes-abuse).
![Extensions Button](/assets/user-manual/extensions/extensions-button.png)
If you haven't installed any extensions yet, clicking the Extensions button will take you to Add-ons Manager page, showing extension recommendations curated by Firefox.
### Manage Extensions from Add-ons Manager
Add-Ons Manager is a page that primarily let you see extension details, manage its preferences, assign shortcuts, disable and remove extensions. You can access Add-Ons Manager by:
- Open the extension button > Click `Manage extensions`.
- Open the main menu > Click "Add-ons and themes".
- Press the default keyboard shortcut `Ctrl/Cmd + Shift + A`.
Select "Extensions" and you can see the list of your installed extensions. You can click on each extensions name to expand the details, or click toggle in each extensions to disable or enable it.
![Add-Ons Manager](/assets/user-manual/extensions/add-ons-manager.png)
The expanded page of individual extensions includes:
- Extension details, with link to homepage and extension ratings in addons.mozilla.org
- Toggle to allow automatic updates.
- Toggle to allow extension run in Private Windows (off by default).
- Toggle to allow extension run on sites restricted by Mozilla (off by default).
- Accessing list of permissions required by the extension, as well as optional permissions toggle for added functionality.
- Additional Preferences tab (availability varies across extensions). Most extensions has their own preference page, that can be accessed from its extension toolbar button.
![Extension Details](/assets/user-manual/extensions/extension-details.png)
### Manage Extension Shortcuts
You can perform some actions related to extensions faster with keyboard shortcuts. Some extensions already has preconfigured shortcut fields, but you can also set your own custom shortcuts, depend on the extension support itself. You can access it by opening `Add-Ons Manager` > `Extensions` > `open Gear menu` > choose `Manage Extension Shortcuts`.
![Manage Extension Shortcuts](/assets/user-manual/extensions/manage-extension-shortcuts.png)
---
You can read more support articles related to extensions: [Add-ons, extensions, and themes | Support Mozilla](https://support.mozilla.org/en-US/topics/add-ons-extensions-and-themes/firefox)

View file

@ -0,0 +1,25 @@
---
title: Glance
description: Preview websites on top of your current tab
---
Zen's Glance lets you preview websites on top of your current tab, without fully switching to it. By default, you can create a Glance view by holding `Alt` as trigger key when clicking a link in a regular tab. In Essentials and pinned tabs, Glance will be automatically created when clicking a link outside current website, without having to pressing the trigger key.
{
<div align="center">
<video width="100%" loop autoPlay>
<source src="/assets/user-manual/glance/glance.webm" />
Your browser does not support the video tag.
</video>
</div>
}
Once Glance appeared, there's three buttons on its top left side:
- Close button to close the view (can also be done by clicking outside the Glance area).
- Expand button to move the website into a new tab.
- Split button to add the website as a splitted tab.
You can disable/enable Glance and change the trigger method (from `Alt + Click` to `Ctrl + Click` or `Shift + Click`) by opening `Settings` > `Look and Feel` > `Glance`.
With Glance, you can take a quick look of websites, move on if you're done with the site, and go back to your previous surfing activities easily.

View file

@ -0,0 +1,70 @@
---
title: User Manual
---
import { Callout } from 'fumadocs-ui/components/callout';
<Callout type='none'>
This user manual is still in early stages of development and incomplete
</Callout>
Welcome to the Zen Browser User Manual! This guide is designed to provide you with a comprehensive overview of Zen Browser's features, settings, and customization options. This manual will help you navigate the essential aspects of Zen Browser and optimize your browsing experience.
<Callout type="warn" title="Important Notice">
Zen Browser is under active and rapid development. Some content in this manual may become outdated as the browser evolves. Please check the last modification date below the title to confirm you have the most recent information. If you find any outdated sections, please feel free to report them by opening an issue on our [GitHub repository](https://github.com/zen-browser/docs/issues).
</Callout>
<Cards>
<Card
title="URL Bar"
description="Smart navigation with persistent input memory"
href="/user-manual/urlbar"
/>
<Card
title="Workspaces"
description="Organize tabs by projects with custom containers"
href="/user-manual/workspaces"
/>
<Card
title="Compact Mode"
description="Minimalistic interface for focused browsing"
href="/user-manual/compact-mode"
/>
<Card
title="Extensions"
description="Customize Zen Browser with extensions"
href="/user-manual/extensions"
/>
<Card
title="Glance"
description="Preview websites on top of your current tab"
href="/user-manual/glance"
/>
<Card
title="Bookmarks"
description="Managing bookmarks in Zen"
href="/user-manual/bookmarks"
/>
<Card
title="Picture-in-Picture"
description="Watch videos in a separate window"
href="/user-manual/pip"
/>
{/* <Card
title="History"
description="Managing browsing history in Zen"
href="/user-manual/history"
/> */}
{/* <Card
title="Sidebar"
description="Using Firefox Sidebar in Zen"
href="/user-manual/sidebar"
/> */}
<Card
title="Web Panel (deprecated)"
description="Side panel for quick access to web apps"
href="/user-manual/webpanel"
/>
</Cards>
Thank you for using Zen Browser, and happy browsing!

View file

@ -0,0 +1,8 @@
{
"title": "User Manual",
"icon": "BookText",
"pages": [
"...",
"webpanel"
]
}

View file

@ -0,0 +1,141 @@
---
title: Picture-in-Picture
description: Watch videos in a separate window
---
With **Picture-in-Picture (PiP)** in **Zen**, you can pop videos out of webpages into a clean, always-on-top floating window. Whether you're working, browsing, or just casually scrolling, PiP keeps your video visible without disrupting your flow.
## How to Use Picture-in-Picture
**Stay in flow while watching.**
### 1. On-Screen Video Toggle
When you hover over a supported video, a **Zen PiP toggle** will appear.
Click it to launch the video in a floating window.
![PIP toggle](/assets/user-manual/pip/open-toggle.png)
- Right-click the toggle to move it to the left or right side.
- The toggle is hidden when the video is in full-screen mode.
### 2. Address Bar Icon
On pages with a single video, Zen displays a PiP icon in the **address bar**.
![Toolbar Toggle](/assets/user-manual/pip/toolbar-toggle.png)
- If there are multiple videos or none, the icon remains hidden.
### 3. Right-Click Menu
You can also launch PiP by **right-clicking** on the video.
- Some platforms like YouTube use custom menus.
In that case, use **Shift + Right-Click** or **double right-click** to access Zens native menu.
![PIP context menu](/assets/user-manual/pip/pip-context-menu.png)
### 4. **Auto-Play in Mini Player**
When you switch tabs while a video is playing, Zen quietly tucks the video into a floating player at the bottom right.
Enable it by setting the property below in about:config to TRUE
`media.videocontrols.picture-in-picture.enable-when-switching-tabs.enabled`
### 5. **Resize**
Hover over any corner of the mini player and drag to adjust its size—just how you like it.
## Keyboard Shortcuts
| Action | Shortcut |
|------------------------------|-----------------------------|
| Launch/Close PiP | `Ctrl + Shift + ]` |
| Mute / Unmute | `Ctrl + ↓ / Ctrl + ↑` |
| Volume Control | `↑ / ↓` |
| Seek 5s Back / Forward | `← / →` |
| Seek 10% Back / Forward | `Ctrl + ← / Ctrl + →` |
| Jump to Start / End | `Home / End` |
| Pause / Play | `Space` |
| Fullscreen Toggle | `Double-click` or `F` |
| Close PiP Window | `Ctrl + W` |
## How to Disable Picture-in-Picture
### Option 1: From the Player
- Hover over the Picture-in-Picture Player
- Click on the [x] button
![Close pip](/assets/user-manual/pip/close.png)
### Option 2: From Zen Settings
- Go to **Settings → General → Browsing**
- Uncheck **"Enable picture-in-picture video controls"**
You can always re-enable it later.
![Disable pip](/assets/user-manual/pip/disable.png)
### Option 3: From the Toggle
- Hover over a video and right-click the PiP toggle.
- Select **"Hide Picture-in-Picture Toggle"**.
### Option 4: From Configuration Page
If youd like to disable Picture-in-Picture functionality entirely, you can do so via Zens advanced configuration page:
1. In the address bar, type `about:config` and press Enter.
2. Accept any warning that appears.
3. Search for the setting: `media.videocontrols.picture-in-picture.enabled`.
4. Set it to **false** to disable Picture-in-Picture across Zen.
You can always re-enable it by returning to this setting and switching it back to **true**.
<Callout title="Why You Might Not See the Toggle">
- The video is shorter than 45 seconds or has no audio.
- Its in full-screen mode.
- The site may restrict PiP, but Zen gives you the option to override this via **"Enable Anyway"** when available.
</Callout>
## Subtitles & Captions
Turn on captions **before** activating PiP to see them in the floating window.
Zen supports captions on major platforms including:
- YouTube
- Netflix
- Disney+
- Amazon Prime Video
- Funimation
- Dailymotion
- Khan Academy
- BBC
- Washington Post
- Nebula
- Tubi
- Hotstar
- SonyLIV
- Any site using **WebVTT** (e.g. Coursera, Twitter)
More platforms are being added.
## Media Player
**Audio, always within reach.**
![Media Player](/assets/user-manual/pip/media-player.png)
- **Automatic Playback Controls**
When audio is playing in a background tab, Zen adds a subtle media controller to the bottom of your sidebar.
{/* TODO: enter video */}
- **Simple Dismissal**
To hide the Media Player, just click the “X” on the controller. Zen will take the hint.
{/* TODO: enter video */}

View file

@ -0,0 +1,40 @@
---
title: Split View
description: Open multiple tabs side by side easily
---
Zen's Split View lets you view up to 4 tabs side by side, so you can compare information or multitask easily.
![Split View](/assets/user-manual/split-view/split-view.png)
### Creating split view
You can create split view simply by open one tab, drag another tab from sidebar to left or right side of it, and drop once the split indicator placeholder is shown. You can also right click on a link and choose `"Split link in new tab"`.
{ /* TODO: insert video of these sequences:
- dragging a tab to split it with another tab
- click new tab button, insert url, load it, and drag it into the existing split.
- right click a link from one of the tabs > click "split link in new tab"
*/}
### Using split wiew
When split view is enabled, the active tab will have an overlay on its top side, containing two buttons: **Drag Handle** `:::` button and **Unsplit Tab** `` button.
{ /* TODO: `insert gif/video of dragging splitted tab and then pressing button to unsplit it` */}
- With **Drag Handle** `:::` button, you can move a splitted tab to various directions (left, bottom, right, or top side of another tabs).
- Meanwhile, clicking **Unsplit Tab** `` button will remove the tab and expand it outside the previous split view.
- Currently, you can **unsplit all tabs within a split view** by pressing `Alt + Ctrl/Cmd + U` shortcut.
### Toggle Split View shortcuts
Zen also provide keyboard shortcuts to help you rearrange the split tabs automatically into a horizontal, vertical, or grid layout.
- **Toggle Split View Horizontal**: `Alt + Ctrl/Cmd + H`
- **Toggle Split View Vertical**: `Alt + Ctrl/Cmd + V`
- **Toggle Split View Grid**: `Alt + Ctrl/Cmd + G`
{ /* TODO: insert gif/video of horizontal, vertical, and grid toggling split view here */}
You can also press one of the Toggle Split View shortcuts to split current tab with the tab below it.
> All shortcuts can be modified via `Settings > Keyboard Shortcuts`.

View file

@ -1,23 +1,21 @@
---
title: Zen URL bar
aliases:
- URL bar
---
{
<div align="center">
<video width="100%" loop autoplay>
<source src="/assets/user-manual/urlbar/vid.mov" type="video/mp4">
<video width="100%" loop autoPlay>
<source src="/assets/user-manual/urlbar/vid.mov" type="video/mp4" />
Your browser does not support the video tag.
</video>
</div>
## Zen URL bar
}
Zen Browsers **URL bar** is a powerful tool that helps you navigate the web quickly and efficiently. There's no need to open a new tab or window to search the web—simply type your query into the URL bar and hit Enter to search. The URL bar also supports direct navigation to websites, so you can type a URL and press Enter to visit the site directly.
- Can be disabled in settings or `about:config` (`zen.urlbar.replace-newtab`)
### How does it work?
### How does it work?
- When trying to open a new tab, the search bar will appear. This allows you to navigate faster and more efficiently by being able to type out the address or getting auto-completed without having a change in the view.
- If the newtab urlbar is closed but you've typed something. The text is remembered unless the URL or tab has been changed.

View file

@ -0,0 +1,17 @@
---
title: Web Panel (deprecated)
---
<Callout type="warn" title="Important Notice">
Since the release of version [1.11b](https://zen-browser.app/release-notes/#1.11b), **Web Panel** has been indefinitely **removed** from Zen due to a discovered [security vulnerability](https://bugzilla.mozilla.org/show_bug.cgi?id=1935985) that could allow websites in web panels to bypass sandboxing protections, potentially enabling malicious code execution on your system.
We understand that this change may cause inconvenience to our current user base relying on Web Panel, and we sincerely apologize for the disruption. We do plan to bring Web Panel back in the future once we address these issues and ensure its stable and secure.
</Callout>
<div align="center">
<img src="/assets/user-manual/webpanel/webpanel.png" alt="Webpanel" width="200"/>
</div>
The **Zen Web Panel** brings your favorite web apps—like chats, notes, or to-do lists—right into your browser window for quick, side-by-side multitasking. Instead of switching tabs, you can keep essentials within reach, making it easy to reference or interact without losing focus on your main task.
With options to float or pin these panels alongside your browsing tabs, the Sidebar is perfect for keeping tools like messaging or music accessible. Planned upgrades will even add notification badges for real-time updates, creating a streamlined and productive workspace within Zen.

View file

@ -0,0 +1,37 @@
---
title: Workspaces
---
<div align="center">
![Workspace](/assets/user-manual/workspaces/workspaces.png)
</div>
Zen Browsers **workspaces** feature is your go-to tool for organizing tabs seamlessly by tasks, projects, or themes. Think of each workspace as a focused area where you can group related tabs and quickly switch between sets—ideal for juggling work, personal tasks, or study sessions without cluttering your tab bar.
You can make each workspace your own by adding **default container tabs** to keep accounts or projects isolated within one workspace, preserving privacy and making navigation easy. Customize each workspace with unique icons and names, so its a breeze to find what you need.
Perfect for power users, workspaces bring the flexibility of multiple browser windows into one streamlined experience, complete with shortcuts to switch between them in an instant. Organize, focus, and explore your tabs with Zen Browsers workspaces for a truly efficient browsing experience.
### Container Tabs / Multi-Account Containers
Container Tabs is a feature derived from Firefox that provide separate cookie sessions within the same browser profile. With Container Tabs, you can log in with multiple accounts on the same sites without having to log out/in multiple times.
By default, there are four containers provided by Firefox: **Personal**, **Work**, **Banking**, and **Shopping**. You can manage, remove, or add new containers from `"Settings" > "General" > "Container Tabs"`. You can choose between 9 colors and 13 icons to customize or create your own containers.
![Container Tabs](/assets/user-manual/workspaces/container-tabs.png)
Browsing sessions with Zen in regular tabs are automatically classified as No Container. You can browse sites using container tabs by:
- Right click `"New Tab"` button and choose container to open
- Right click on `existing tabs > "Open in New Container Tab" > choose container to open`
- Right click on `links > "Open Link in New Container Tab" > choose container to open`
- After the sites is opened in a container tab, you can log in again with different account credentials.
By assigning containers to Workspaces in Zen, you can devote a workspace for certain usage of accounts without affecting your current login session (registered in regular/No Container tabs.)
If you assign one container for each workspaces and open a container tab outside of the assigned workspaces, you can make it automatically moved to the intended workspaces with checking `"Switch to workspace where container is set as default when opening container tabs"` option in `"Settings" > "Tab Management" > "Workspaces"`.
![Switch to workspace where container is set as default when opening container tabs](/assets/user-manual/workspaces/switch-to-workspace-when-opening-container-tabs.png)
**Limitation**: Currently Container Tabs separate your cookies/browsing sessions, but it doesn't separate your browsing history and extensions.
Learn more about Multi-Account Containers in Mozilla Support: https://support.mozilla.org/kb/containers

View file

@ -1,84 +0,0 @@
---
title: Download Hub
draft: false
aliases:
- Download Hub
lastmod: 2025-01-16
---
This [[guides/index|guide]] is designed to help you easily find and download the version of Zen Browser that best fits your operating system and hardware. Whether you're using Windows, Linux, or macOS - you'll find the build for you, along with portable and installer options to suit your needs.
> [!faq]- What happened to Generic and Optimized builds?
> ![[generic-optimized]]
# Stable
## Windows 🪟
- ### x86_64 💽
- [Installer 🚀](https://github.com/zen-browser/desktop/releases/latest/download/zen.installer.exe)
- ### arm64 💻
- [Installer 🚀](https://github.com/zen-browser/desktop/releases/latest/download/zen.installer-arm64.exe)
## Linux 🐧
- ### x86_64 💽
- [AppImage 🚀](https://github.com/zen-browser/desktop/releases/latest/download/zen-x86_64.AppImage)
- [Portable 📦 `tar.bz2`](https://github.com/zen-browser/desktop/releases/latest/download/zen.linux-x86_64.tar.bz2)
- ### aarch64 💻
- [AppImage 🚀](https://github.com/zen-browser/desktop/releases/latest/download/zen-aarch64.AppImage)
- [Portable 📦 `tar.bz2`](https://github.com/zen-browser/desktop/releases/latest/download/zen.linux-aarch64.tar.bz2)
- ### Package Managers
- [Arch User Repository 📂 `AUR`](https://aur.archlinux.org/packages/zen-browser-bin)
- [Pacstall `comunity`](https://pacstall.dev/packages/zen-browser-bin)
- [Flatpak 🌐](https://flathub.org/apps/io.github.zen_browser.zen)
```bash
flatpak install flathub app.zen_browser.zen
```
## MacOS 🍎
- ### aarch64 (M-series chips) 💻
- [DMG File 🗂️](https://github.com/zen-browser/desktop/releases/latest/download/zen.macos-aarch64.dmg)
- ### x86_64 (Intel chips) 💽
- [DMG File 🗂️](https://github.com/zen-browser/desktop/releases/latest/download/zen.macos-x86_64.dmg)
# Twilight
> [!caution] This is an automated daily build for testing purposes, so expect some bugs on this version.
## Windows 🪟
- ### x86_64 💽
- [Installer 🚀](https://github.com/zen-browser/desktop/releases/download/twilight/zen.installer.exe)
- [Portable 📦 `zip`](https://github.com/zen-browser/desktop/releases/download/twilight/zen.win-x86_64.zip)
- ### arm64 💻
- [Installer 🚀](https://github.com/zen-browser/desktop/releases/download/twilight/zen.installer-arm64.exe)
- [Portable 📦 `zip`](https://github.com/zen-browser/desktop/releases/download/twilight/zen.win-arm64.zip)
## Linux 🐧
- ### x86_64 💽
- [AppImage 🚀](https://github.com/zen-browser/desktop/releases/download/twilight/zen-x86_64.AppImage)
- [Portable 📦 `tar.bz2`](https://github.com/zen-browser/desktop/releases/download/twilight/zen.linux-x86_64.tar.bz2)
- ### aarch64 💻
- [AppImage 🚀](https://github.com/zen-browser/desktop/releases/download/twilight/zen-aarch64.AppImage)
- [Portable 📦 `tar.bz2`](https://github.com/zen-browser/desktop/releases/download/twilight/zen.linux-aarch64.tar.bz2)
## MacOS 🍎
- ### aarch64 (M-series chips) 💻
- [DMG File 🗂️](https://github.com/zen-browser/desktop/releases/download/twilight/zen.macos-aarch64.dmg)
- ### x86_64 (Intel chips) 💽
- [DMG File 🗂️](https://github.com/zen-browser/desktop/releases/download/twilight/zen.macos-x86_64.dmg)

View file

@ -1,3 +0,0 @@
---
title: Guides 🛠️
---

View file

@ -1,17 +0,0 @@
---
title: Welcome to Zen Browser's Documentation
lastmod: 2024-09-11
---
Welcome to **Zen Browser's Documentation!** Here, you'll find everything you need to get the most out of your browsing experience. Dive in to explore how Zen can make your browsing more secure, private, and efficient.
## Index
- #### [[building|Building Zen Browser 📦]]
- #### [[user-manual/index|User Manual 📖]]
- #### [[guides/index|Guides 🛠️]]
- #### [[themes-store/index|Mods Registry 🎨]]
- #### [[faq | Frequently Asked Questions ❓]]
- #### [[benchmarks | Performance Benchmarks 📊]]
- #### [[contribute/index | Contribution Guides 🌟]]
- #### [[security|Security 🔐]]

View file

@ -1,3 +0,0 @@
---
title: Mods Registry 🎨
---

View file

@ -1,18 +0,0 @@
---
title: User Manual 📖
aliases:
- User Manual 📖
- User Manual
---
> [!danger]
> This user manual is still in early stages of development and incomplete
Welcome to the Zen Browser User Manual! This guide is designed to provide you with a comprehensive overview of Zen Browser's features, settings, and customization options. This manual will help you navigate the essential aspects of Zen Browser and optimize your browsing experience.
> [!important] **Important Notice**
> Zen Browser is under active and rapid development. Some content in this manual may become outdated as the browser evolves. Please check the last modification date below the title to confirm you have the most recent information. If you find any outdated sections, please feel free to report them by opening an issue on our [GitHub repository](https://github.com/zen-browser/docs/issues).
Thank you for using Zen Browser, and happy browsing!
# Manuals

View file

@ -1,13 +0,0 @@
---
title: Web Panel
aliases:
- Web Panel
---
<div align="center">
<img src="/assets/user-manual/webpanel/webpanel.png" width="45%">
</div>
The **Zen Web Panel** brings your favorite web apps—like chats, notes, or to-do lists—right into your browser window for quick, side-by-side multitasking. Instead of switching tabs, you can keep essentials within reach, making it easy to reference or interact without losing focus on your main task.
With options to float or pin these panels alongside your browsing tabs, the Sidebar is perfect for keeping tools like messaging or music accessible. Planned upgrades will even add notification badges for real-time updates, creating a streamlined and productive workspace within Zen.

View file

@ -1,15 +0,0 @@
---
title: Workspaces
aliases:
- Workspaces
---
<div align="center">
<img src="/assets/user-manual/workspaces/workspaces.png">
</div>
Zen Browsers **workspaces** feature is your go-to tool for organizing tabs seamlessly by tasks, projects, or themes. Think of each workspace as a focused area where you can group related tabs and quickly switch between sets—ideal for juggling work, personal tasks, or study sessions without cluttering your tab bar.
You can make each workspace your own by adding **default container tabs** to keep accounts or projects isolated within one workspace, preserving privacy and making navigation easy. Customize each workspace with unique icons and names, so its a breeze to find what you need.
Perfect for power users, workspaces bring the flexibility of multiple browser windows into one streamlined experience, complete with shortcuts to switch between them in an instant. Organize, focus, and explore your tabs with Zen Browsers workspaces for a truly efficient browsing experience.

View file

@ -1,52 +0,0 @@
---
title: Architecture
---
Quartz is a static site generator. How does it work?
This question is best answered by tracing what happens when a user (you!) runs `npx quartz build` in the command line:
## On the server
1. After running `npx quartz build`, npm will look at `package.json` to find the `bin` entry for `quartz` which points at `./quartz/bootstrap-cli.mjs`.
2. This file has a [shebang](<https://en.wikipedia.org/wiki/Shebang_(Unix)>) line at the top which tells npm to execute it using Node.
3. `bootstrap-cli.mjs` is responsible for a few things:
1. Parsing the command-line arguments using [yargs](http://yargs.js.org/).
2. Transpiling and bundling the rest of Quartz (which is in Typescript) to regular JavaScript using [esbuild](https://esbuild.github.io/). The `esbuild` configuration here is slightly special as it also handles `.scss` file imports using [esbuild-sass-plugin v2](https://www.npmjs.com/package/esbuild-sass-plugin). Additionally, we bundle 'inline' client-side scripts (any `.inline.ts` file) that components declare using a custom `esbuild` plugin that runs another instance of `esbuild` which bundles for the browser instead of `node`. Modules of both types are imported as plain text.
3. Running the local preview server if `--serve` is set. This starts two servers:
1. A WebSocket server on port 3001 to handle hot-reload signals. This tracks all inbound connections and sends a 'rebuild' message a server-side change is detected (either content or configuration).
2. An HTTP file-server on a user defined port (normally 8080) to serve the actual website files.
4. If the `--serve` flag is set, it also starts a file watcher to detect source-code changes (e.g. anything that is `.ts`, `.tsx`, `.scss`, or packager files). On a change, we rebuild the module (step 2 above) using esbuild's [rebuild API](https://esbuild.github.io/api/#rebuild) which drastically reduces the build times.
5. After transpiling the main Quartz build module (`quartz/build.ts`), we write it to a cache file `.quartz-cache/transpiled-build.mjs` and then dynamically import this using `await import(cacheFile)`. However, we need to be pretty smart about how to bust Node's [import cache](https://github.com/nodejs/modules/issues/307) so we add a random query string to fake Node into thinking it's a new module. This does, however, cause memory leaks so we just hope that the user doesn't hot-reload their configuration too many times in a single session :)) (it leaks about ~350kB memory on each reload). After importing the module, we then invoke it, passing in the command line arguments we parsed earlier along with a callback function to signal the client to refresh.
4. In `build.ts`, we start by installing source map support manually to account for the query string cache busting hack we introduced earlier. Then, we start processing content:
1. Clean the output directory.
2. Recursively glob all files in the `content` folder, respecting the `.gitignore`.
3. Parse the Markdown files.
1. Quartz detects the number of threads available and chooses to spawn worker threads if there are >128 pieces of content to parse (rough heuristic). If it needs to spawn workers, it will invoke esbuild again to transpile the worker script `quartz/worker.ts`. Then, a work-stealing [workerpool](https://www.npmjs.com/package/workerpool) is then created and batches of 128 files are assigned to workers.
2. Each worker (or just the main thread if there is no concurrency) creates a [unified](https://github.com/unifiedjs/unified) parser based off of the plugins defined in the [[configuration]].
3. Parsing has three steps:
1. Read the file into a [vfile](https://github.com/vfile/vfile).
2. Applied plugin-defined text transformations over the content.
3. Slugify the file path and store it in the data for the file. See the page on [[paths]] for more details about how path logic works in Quartz (spoiler: its complicated).
4. Markdown parsing using [remark-parse](https://www.npmjs.com/package/remark-parse) (text to [mdast](https://github.com/syntax-tree/mdast)).
5. Apply plugin-defined Markdown-to-Markdown transformations.
6. Convert Markdown into HTML using [remark-rehype](https://github.com/remarkjs/remark-rehype) ([mdast](https://github.com/syntax-tree/mdast) to [hast](https://github.com/syntax-tree/hast)).
7. Apply plugin-defined HTML-to-HTML transformations.
4. Filter out unwanted content using plugins.
5. Emit files using plugins.
1. Gather all the static resources (e.g. external CSS, JS modules, etc.) each emitter plugin declares.
2. Emitters that emit HTML files do a bit of extra work here as they need to transform the [hast](https://github.com/syntax-tree/hast) produced in the parse step to JSX. This is done using [hast-util-to-jsx-runtime](https://github.com/syntax-tree/hast-util-to-jsx-runtime) with the [Preact](https://preactjs.com/) runtime. Finally, the JSX is rendered to HTML using [preact-render-to-string](https://github.com/preactjs/preact-render-to-string) which statically renders the JSX to HTML (i.e. doesn't care about `useState`, `useEffect`, or any other React/Preact interactive bits). Here, we also do a bunch of fun stuff like assemble the page [[layout]] from `quartz.layout.ts`, assemble all the inline scripts that actually get shipped to the client, and all the transpiled styles. The bulk of this logic can be found in `quartz/components/renderPage.tsx`. Other fun things of note:
1. CSS is minified and transformed using [Lightning CSS](https://github.com/parcel-bundler/lightningcss) to add vendor prefixes and do syntax lowering.
2. Scripts are split into `beforeDOMLoaded` and `afterDOMLoaded` and are inserted in the `<head>` and `<body>` respectively.
3. Finally, each emitter plugin is responsible for emitting and writing it's own emitted files to disk.
6. If the `--serve` flag was detected, we also set up another file watcher to detect content changes (only `.md` files). We keep a content map that tracks the parsed AST and plugin data for each slug and update this on file changes. Newly added or modified paths are rebuilt and added to the content map. Then, all the filters and emitters are run over the resulting content map. This file watcher is debounced with a threshold of 250ms. On success, we send a client refresh signal using the passed in callback function.
## On the client
1. The browser opens a Quartz page and loads the HTML. The `<head>` also links to page styles (emitted to `public/index.css`) and page-critical JS (emitted to `public/prescript.js`)
2. Then, once the body is loaded, the browser loads the non-critical JS (emitted to `public/postscript.js`)
3. Once the page is done loading, the page will then dispatch a custom synthetic browser event `"nav"`. This is used so client-side scripts declared by components can 'setup' anything that requires access to the page DOM.
1. If the [[SPA Routing|enableSPA option]] is enabled in the [[configuration]], this `"nav"` event is also fired on any client-navigation to allow for components to unregister and reregister any event handlers and state.
2. If it's not, we wire up the `"nav"` event to just be fired a single time after page load to allow for consistency across how state is setup across both SPA and non-SPA contexts.
The architecture and design of the plugin system was intentionally left pretty vague here as this is described in much more depth in the guide on [[making plugins|making your own plugin]].

View file

@ -1,234 +0,0 @@
---
title: Creating your own Quartz components
---
> [!warning]
> This guide assumes you have experience writing JavaScript and are familiar with TypeScript.
Normally on the web, we write layout code using HTML which looks something like the following:
```html
<article>
<h1>An article header</h1>
<p>Some content</p>
</article>
```
This piece of HTML represents an article with a leading header that says "An article header" and a paragraph that contains the text "Some content". This is combined with CSS to style the page and JavaScript to add interactivity.
However, HTML doesn't let you create reusable templates. If you wanted to create a new page, you would need to copy and paste the above snippet and edit the header and content yourself. This isn't great if we have a lot of content on our site that shares a lot of similar layout. The smart people who created React also had similar complaints and invented the concept of Components -- JavaScript functions that return JSX -- to solve the code duplication problem.
In effect, components allow you to write a JavaScript function that takes some data and produces HTML as an output. **While Quartz doesn't use React, it uses the same component concept to allow you to easily express layout templates in your Quartz site.**
## An Example Component
### Constructor
Component files are written in `.tsx` files that live in the `quartz/components` folder. These are re-exported in `quartz/components/index.ts` so you can use them in layouts and other components more easily.
Each component file should have a default export that satisfies the `QuartzComponentConstructor` function signature. It's a function that takes in a single optional parameter `opts` and returns a Quartz Component. The type of the parameters `opts` is defined by the interface `Options` which you as the component creator also decide.
In your component, you can use the values from the configuration option to change the rendering behaviour inside of your component. For example, the component in the code snippet below will not render if the `favouriteNumber` option is below 0.
```tsx {11-17}
interface Options {
favouriteNumber: number
}
const defaultOptions: Options = {
favouriteNumber: 42,
}
export default ((userOpts?: Options) => {
const opts = { ...userOpts, ...defaultOpts }
function YourComponent(props: QuartzComponentProps) {
if (opts.favouriteNumber < 0) {
return null
}
return <p>My favourite number is {opts.favouriteNumber}</p>
}
return YourComponent
}) satisfies QuartzComponentConstructor
```
### Props
The Quartz component itself (lines 11-17 highlighted above) looks like a React component. It takes in properties (sometimes called [props](https://react.dev/learn/passing-props-to-a-component)) and returns JSX.
All Quartz components accept the same set of props:
```tsx title="quartz/components/types.ts"
// simplified for sake of demonstration
export type QuartzComponentProps = {
fileData: QuartzPluginData
cfg: GlobalConfiguration
tree: Node<QuartzPluginData>
allFiles: QuartzPluginData[]
displayClass?: "mobile-only" | "desktop-only"
}
```
- `fileData`: Any metadata [[making plugins|plugins]] may have added to the current page.
- `fileData.slug`: slug of the current page.
- `fileData.frontmatter`: any frontmatter parsed.
- `cfg`: The `configuration` field in `quartz.config.ts`.
- `tree`: the resulting [HTML AST](https://github.com/syntax-tree/hast) after processing and transforming the file. This is useful if you'd like to render the content using [hast-util-to-jsx-runtime](https://github.com/syntax-tree/hast-util-to-jsx-runtime) (you can find an example of this in `quartz/components/pages/Content.tsx`).
- `allFiles`: Metadata for all files that have been parsed. Useful for doing page listings or figuring out the overall site structure.
- `displayClass`: a utility class that indicates a preference from the user about how to render it in a mobile or desktop setting. Helpful if you want to conditionally hide a component on mobile or desktop.
### Styling
Quartz components can also define a `.css` property on the actual function component which will get picked up by Quartz. This is expected to be a CSS string which can either be inlined or imported from a `.scss` file.
Note that inlined styles **must** be plain vanilla CSS:
```tsx {6-10} title="quartz/components/YourComponent.tsx"
export default (() => {
function YourComponent() {
return <p class="red-text">Example Component</p>
}
YourComponent.css = `
p.red-text {
color: red;
}
`
return YourComponent
}) satisfies QuartzComponentConstructor
```
Imported styles, however, can be from SCSS files:
```tsx {1-2,9} title="quartz/components/YourComponent.tsx"
// assuming your stylesheet is in quartz/components/styles/YourComponent.scss
import styles from "./styles/YourComponent.scss"
export default (() => {
function YourComponent() {
return <p>Example Component</p>
}
YourComponent.css = styles
return YourComponent
}) satisfies QuartzComponentConstructor
```
> [!warning]
> Quartz does not use CSS modules so any styles you declare here apply _globally_. If you only want it to apply to your component, make sure you use specific class names and selectors.
### Scripts and Interactivity
What about interactivity? Suppose you want to add an-click handler for example. Like the `.css` property on the component, you can also declare `.beforeDOMLoaded` and `.afterDOMLoaded` properties that are strings that contain the script.
```tsx title="quartz/components/YourComponent.tsx"
export default (() => {
function YourComponent() {
return <button id="btn">Click me</button>
}
YourComponent.beforeDOMLoaded = `
console.log("hello from before the page loads!")
`
YourComponent.afterDOMLoaded = `
document.getElementById('btn').onclick = () => {
alert('button clicked!')
}
`
return YourComponent
}) satisfies QuartzComponentConstructor
```
> [!hint]
> For those coming from React, Quartz components are different from React components in that it only uses JSX for templating and layout. Hooks like `useEffect`, `useState`, etc. are not rendered and other properties that accept functions like `onClick` handlers will not work. Instead, do it using a regular JS script that modifies the DOM element directly.
As the names suggest, the `.beforeDOMLoaded` scripts are executed _before_ the page is done loading so it doesn't have access to any elements on the page. This is mostly used to prefetch any critical data.
The `.afterDOMLoaded` script executes once the page has been completely loaded. This is a good place to setup anything that should last for the duration of a site visit (e.g. getting something saved from local storage).
If you need to create an `afterDOMLoaded` script that depends on _page specific_ elements that may change when navigating to a new page, you can listen for the `"nav"` event that gets fired whenever a page loads (which may happen on navigation if [[SPA Routing]] is enabled).
```ts
document.addEventListener("nav", () => {
// do page specific logic here
// e.g. attach event listeners
const toggleSwitch = document.querySelector("#switch") as HTMLInputElement
toggleSwitch.addEventListener("change", switchTheme)
window.addCleanup(() => toggleSwitch.removeEventListener("change", switchTheme))
})
```
It is best practice to track any event handlers via `window.addCleanup` to prevent memory leaks.
This will get called on page navigation.
#### Importing Code
Of course, it isn't always practical (nor desired!) to write your code as a string literal in the component.
Quartz supports importing component code through `.inline.ts` files.
```tsx title="quartz/components/YourComponent.tsx"
// @ts-ignore: typescript doesn't know about our inline bundling system
// so we need to silence the error
import script from "./scripts/graph.inline"
export default (() => {
function YourComponent() {
return <button id="btn">Click me</button>
}
YourComponent.afterDOMLoaded = script
return YourComponent
}) satisfies QuartzComponentConstructor
```
```ts title="quartz/components/scripts/graph.inline.ts"
// any imports here are bundled for the browser
import * as d3 from "d3"
document.getElementById("btn").onclick = () => {
alert("button clicked!")
}
```
Additionally, like what is shown in the example above, you can import packages in `.inline.ts` files. This will be bundled by Quartz and included in the actual script.
### Using a Component
After creating your custom component, re-export it in `quartz/components/index.ts`:
```ts title="quartz/components/index.ts" {4,10}
import ArticleTitle from "./ArticleTitle"
import Content from "./pages/Content"
import Darkmode from "./Darkmode"
import YourComponent from "./YourComponent"
export { ArticleTitle, Content, Darkmode, YourComponent }
```
Then, you can use it like any other component in `quartz.layout.ts` via `Component.YourComponent()`. See the [[configuration#Layout|layout]] section for more details.
As Quartz components are just functions that return React components, you can compositionally use them in other Quartz components.
```tsx title="quartz/components/AnotherComponent.tsx"
import YourComponent from "./YourComponent"
export default (() => {
function AnotherComponent(props: QuartzComponentProps) {
return (
<div>
<p>It's nested!</p>
<YourComponent {...props} />
</div>
)
}
return AnotherComponent
}) satisfies QuartzComponentConstructor
```
> [!hint]
> Look in `quartz/components` for more examples of components in Quartz as reference for your own components!

View file

@ -1,3 +0,0 @@
---
title: "Advanced"
---

View file

@ -1,303 +0,0 @@
---
title: Making your own plugins
---
> [!warning]
> This part of the documentation will assume you have working knowledge in TypeScript and will include code snippets that describe the interface of what Quartz plugins should look like.
Quartz's plugins are a series of transformations over content. This is illustrated in the diagram of the processing pipeline below:
![[quartz transform pipeline.png]]
All plugins are defined as a function that takes in a single parameter for options `type OptionType = object | undefined` and return an object that corresponds to the type of plugin it is.
```ts
type OptionType = object | undefined
type QuartzPlugin<Options extends OptionType = undefined> = (opts?: Options) => QuartzPluginInstance
type QuartzPluginInstance =
| QuartzTransformerPluginInstance
| QuartzFilterPluginInstance
| QuartzEmitterPluginInstance
```
The following sections will go into detail for what methods can be implemented for each plugin type. Before we do that, let's clarify a few more ambiguous types:
- `BuildCtx` is defined in `quartz/ctx.ts`. It consists of
- `argv`: The command line arguments passed to the Quartz [[build]] command
- `cfg`: The full Quartz [[configuration]]
- `allSlugs`: a list of all the valid content slugs (see [[paths]] for more information on what a `ServerSlug` is)
- `StaticResources` is defined in `quartz/resources.tsx`. It consists of
- `css`: a list of URLs for stylesheets that should be loaded
- `js`: a list of scripts that should be loaded. A script is described with the `JSResource` type which is also defined in `quartz/resources.tsx`. It allows you to define a load time (either before or after the DOM has been loaded), whether it should be a module, and either the source URL or the inline content of the script.
## Transformers
Transformers **map** over content, taking a Markdown file and outputting modified content or adding metadata to the file itself.
```ts
export type QuartzTransformerPluginInstance = {
name: string
textTransform?: (ctx: BuildCtx, src: string | Buffer) => string | Buffer
markdownPlugins?: (ctx: BuildCtx) => PluggableList
htmlPlugins?: (ctx: BuildCtx) => PluggableList
externalResources?: (ctx: BuildCtx) => Partial<StaticResources>
}
```
All transformer plugins must define at least a `name` field to register the plugin and a few optional functions that allow you to hook into various parts of transforming a single Markdown file.
- `textTransform` performs a text-to-text transformation _before_ a file is parsed into the [Markdown AST](https://github.com/syntax-tree/mdast).
- `markdownPlugins` defines a list of [remark plugins](https://github.com/remarkjs/remark/blob/main/doc/plugins.md). `remark` is a tool that transforms Markdown to Markdown in a structured way.
- `htmlPlugins` defines a list of [rehype plugins](https://github.com/rehypejs/rehype/blob/main/doc/plugins.md). Similar to how `remark` works, `rehype` is a tool that transforms HTML to HTML in a structured way.
- `externalResources` defines any external resources the plugin may need to load on the client-side for it to work properly.
Normally for both `remark` and `rehype`, you can find existing plugins that you can use to . If you'd like to create your own `remark` or `rehype` plugin, checkout the [guide to creating a plugin](https://unifiedjs.com/learn/guide/create-a-plugin/) using `unified` (the underlying AST parser and transformer library).
A good example of a transformer plugin that borrows from the `remark` and `rehype` ecosystems is the [[plugins/Latex|Latex]] plugin:
```ts title="quartz/plugins/transformers/latex.ts"
import remarkMath from "remark-math"
import rehypeKatex from "rehype-katex"
import rehypeMathjax from "rehype-mathjax/svg"
import { QuartzTransformerPlugin } from "../types"
interface Options {
renderEngine: "katex" | "mathjax"
}
export const Latex: QuartzTransformerPlugin<Options> = (opts?: Options) => {
const engine = opts?.renderEngine ?? "katex"
return {
name: "Latex",
markdownPlugins() {
return [remarkMath]
},
htmlPlugins() {
if (engine === "katex") {
// if you need to pass options into a plugin, you
// can use a tuple of [plugin, options]
return [[rehypeKatex, { output: "html" }]]
} else {
return [rehypeMathjax]
}
},
externalResources() {
if (engine === "katex") {
return {
css: [
// base css
"https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.9/katex.min.css",
],
js: [
{
// fix copy behaviour: https://github.com/KaTeX/KaTeX/blob/main/contrib/copy-tex/README.md
src: "https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.9/contrib/copy-tex.min.js",
loadTime: "afterDOMReady",
contentType: "external",
},
],
}
} else {
return {}
}
},
}
}
```
Another common thing that transformer plugins will do is parse a file and add extra data for that file:
```ts
export const AddWordCount: QuartzTransformerPlugin = () => {
return {
name: "AddWordCount",
markdownPlugins() {
return [
() => {
return (tree, file) => {
// tree is an `mdast` root element
// file is a `vfile`
const text = file.value
const words = text.split(" ").length
file.data.wordcount = words
}
},
]
},
}
}
// tell typescript about our custom data fields we are adding
// other plugins will then also be aware of this data field
declare module "vfile" {
interface DataMap {
wordcount: number
}
}
```
Finally, you can also perform transformations over Markdown or HTML ASTs using the `visit` function from the `unist-util-visit` package or the `findAndReplace` function from the `mdast-util-find-and-replace` package.
```ts
export const TextTransforms: QuartzTransformerPlugin = () => {
return {
name: "TextTransforms",
markdownPlugins() {
return [() => {
return (tree, file) => {
// replace _text_ with the italics version
findAndReplace(tree, /_(.+)_/, (_value: string, ...capture: string[]) => {
// inner is the text inside of the () of the regex
const [inner] = capture
// return an mdast node
// https://github.com/syntax-tree/mdast
return {
type: "emphasis",
children: [{ type: 'text', value: inner }]
}
})
// remove all links (replace with just the link content)
// match by 'type' field on an mdast node
// https://github.com/syntax-tree/mdast#link in this example
visit(tree, "link", (link: Link) => {
return {
type: "paragraph"
children: [{ type: 'text', value: link.title }]
}
})
}
}]
}
}
}
```
All transformer plugins can be found under `quartz/plugins/transformers`. If you decide to write your own transformer plugin, don't forget to re-export it under `quartz/plugins/transformers/index.ts`
A parting word: transformer plugins are quite complex so don't worry if you don't get them right away. Take a look at the built in transformers and see how they operate over content to get a better sense for how to accomplish what you are trying to do.
## Filters
Filters **filter** content, taking the output of all the transformers and determining what files to actually keep and what to discard.
```ts
export type QuartzFilterPlugin<Options extends OptionType = undefined> = (
opts?: Options,
) => QuartzFilterPluginInstance
export type QuartzFilterPluginInstance = {
name: string
shouldPublish(ctx: BuildCtx, content: ProcessedContent): boolean
}
```
A filter plugin must define a `name` field and a `shouldPublish` function that takes in a piece of content that has been processed by all the transformers and returns a `true` or `false` depending on whether it should be passed to the emitter plugins or not.
For example, here is the built-in plugin for removing drafts:
```ts title="quartz/plugins/filters/draft.ts"
import { QuartzFilterPlugin } from "../types"
export const RemoveDrafts: QuartzFilterPlugin<{}> = () => ({
name: "RemoveDrafts",
shouldPublish(_ctx, [_tree, vfile]) {
// uses frontmatter parsed from transformers
const draftFlag: boolean = vfile.data?.frontmatter?.draft ?? false
return !draftFlag
},
})
```
## Emitters
Emitters **reduce** over content, taking in a list of all the transformed and filtered content and creating output files.
```ts
export type QuartzEmitterPlugin<Options extends OptionType = undefined> = (
opts?: Options,
) => QuartzEmitterPluginInstance
export type QuartzEmitterPluginInstance = {
name: string
emit(ctx: BuildCtx, content: ProcessedContent[], resources: StaticResources): Promise<FilePath[]>
getQuartzComponents(ctx: BuildCtx): QuartzComponent[]
}
```
An emitter plugin must define a `name` field, an `emit` function, and a `getQuartzComponents` function. `emit` is responsible for looking at all the parsed and filtered content and then appropriately creating files and returning a list of paths to files the plugin created.
Creating new files can be done via regular Node [fs module](https://nodejs.org/api/fs.html) (i.e. `fs.cp` or `fs.writeFile`) or via the `write` function in `quartz/plugins/emitters/helpers.ts` if you are creating files that contain text. `write` has the following signature:
```ts
export type WriteOptions = (data: {
// the build context
ctx: BuildCtx
// the name of the file to emit (not including the file extension)
slug: ServerSlug
// the file extension
ext: `.${string}` | ""
// the file content to add
content: string
}) => Promise<FilePath>
```
This is a thin wrapper around writing to the appropriate output folder and ensuring that intermediate directories exist. If you choose to use the native Node `fs` APIs, ensure you emit to the `argv.output` folder as well.
If you are creating an emitter plugin that needs to render components, there are three more things to be aware of:
- Your component should use `getQuartzComponents` to declare a list of `QuartzComponents` that it uses to construct the page. See the page on [[creating components]] for more information.
- You can use the `renderPage` function defined in `quartz/components/renderPage.tsx` to render Quartz components into HTML.
- If you need to render an HTML AST to JSX, you can use the `htmlToJsx` function from `quartz/util/jsx.ts`. An example of this can be found in `quartz/components/pages/Content.tsx`.
For example, the following is a simplified version of the content page plugin that renders every single page.
```tsx title="quartz/plugins/emitters/contentPage.tsx"
export const ContentPage: QuartzEmitterPlugin = () => {
// construct the layout
const layout: FullPageLayout = {
...sharedPageComponents,
...defaultContentPageLayout,
pageBody: Content(),
}
const { head, header, beforeBody, pageBody, afterBody, left, right, footer } = layout
return {
name: "ContentPage",
getQuartzComponents() {
return [head, ...header, ...beforeBody, pageBody, ...afterBody, ...left, ...right, footer]
},
async emit(ctx, content, resources, emit): Promise<FilePath[]> {
const cfg = ctx.cfg.configuration
const fps: FilePath[] = []
const allFiles = content.map((c) => c[1].data)
for (const [tree, file] of content) {
const slug = canonicalizeServer(file.data.slug!)
const externalResources = pageResources(slug, resources)
const componentData: QuartzComponentProps = {
fileData: file.data,
externalResources,
cfg,
children: [],
tree,
allFiles,
}
const content = renderPage(cfg, slug, componentData, opts, externalResources)
const fp = await emit({
content,
slug: file.data.slug!,
ext: ".html",
})
fps.push(fp)
}
return fps
},
}
}
```
Note that it takes in a `FullPageLayout` as the options. It's made by combining a `SharedLayout` and a `PageLayout` both of which are provided through the `quartz.layout.ts` file.
> [!hint]
> Look in `quartz/plugins` for more examples of plugins in Quartz as reference for your own plugins!

View file

@ -1,51 +0,0 @@
---
title: Paths in Quartz
---
Paths are pretty complex to reason about because, especially for a static site generator, they can come from so many places.
A full file path to a piece of content? Also a path. What about a slug for a piece of content? Yet another path.
It would be silly to type these all as `string` and call it a day as it's pretty common to accidentally mistake one type of path for another. Unfortunately, TypeScript does not have [nominal types](https://en.wikipedia.org/wiki/Nominal_type_system) for type aliases meaning even if you made custom types of a server-side slug or a client-slug slug, you can still accidentally assign one to another and TypeScript wouldn't catch it.
Luckily, we can mimic nominal typing using [brands](https://www.typescriptlang.org/play#example/nominal-typing).
```typescript
// instead of
type FullSlug = string
// we do
type FullSlug = string & { __brand: "full" }
// that way, the following will fail typechecking
const slug: FullSlug = "some random string"
```
While this prevents most typing mistakes _within_ our nominal typing system (e.g. mistaking a server slug for a client slug), it doesn't prevent us from _accidentally_ mistaking a string for a client slug when we forcibly cast it.
Thus, we still need to be careful when casting from a string to one of these nominal types in the 'entrypoints', illustrated with hexagon shapes in the diagram below.
The following diagram draws the relationships between all the path sources, nominal path types, and what functions in `quartz/path.ts` convert between them.
```mermaid
graph LR
Browser{{Browser}} --> Window{{Body}} & LinkElement{{Link Element}}
Window --"getFullSlug()"--> FullSlug[Full Slug]
LinkElement --".href"--> Relative[Relative URL]
FullSlug --"simplifySlug()" --> SimpleSlug[Simple Slug]
SimpleSlug --"pathToRoot()"--> Relative
SimpleSlug --"resolveRelative()" --> Relative
MD{{Markdown File}} --> FilePath{{File Path}} & Links[Markdown links]
Links --"transformLink()"--> Relative
FilePath --"slugifyFilePath()"--> FullSlug[Full Slug]
style FullSlug stroke-width:4px
```
Here are the main types of slugs with a rough description of each type of path:
- `FilePath`: a real file path to a file on disk. Cannot be relative and must have a file extension.
- `FullSlug`: cannot be relative and may not have leading or trailing slashes. It can have `index` as it's last segment. Use this wherever possible is it's the most 'general' interpretation of a slug.
- `SimpleSlug`: cannot be relative and shouldn't have `/index` as an ending or a file extension. It _can_ however have a trailing slash to indicate a folder path.
- `RelativeURL`: must start with `.` or `..` to indicate it's a relative URL. Shouldn't have `/index` as an ending or a file extension but can contain a trailing slash.
To get a clearer picture of how these relate to each other, take a look at the path tests in `quartz/util/path.test.ts`.

View file

@ -1,44 +0,0 @@
---
title: Authoring Content
---
All of the content in your Quartz should go in the `/content` folder. The content for the home page of your Quartz lives in `content/index.md`. If you've [[index#🪴 Get Started|setup Quartz]] already, this folder should already be initialized. Any Markdown in this folder will get processed by Quartz.
It is recommended that you use [Obsidian](https://obsidian.md/) as a way to edit and maintain your Quartz. It comes with a nice editor and graphical interface to preview, edit, and link your local files and attachments.
Got everything setup? Let's [[build]] and preview your Quartz locally!
## Syntax
As Quartz uses Markdown files as the main way of writing content, it fully supports Markdown syntax. By default, Quartz also ships with a few syntax extensions like [Github Flavored Markdown](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax) (footnotes, strikethrough, tables, tasklists) and [Obsidian Flavored Markdown](https://help.obsidian.md/Editing+and+formatting/Obsidian+Flavored+Markdown) ([[callouts]], [[wikilinks]]).
Additionally, Quartz also allows you to specify additional metadata in your notes called **frontmatter**.
```md title="content/note.md"
---
title: Example Title
draft: false
tags:
- example-tag
---
The rest of your content lives here. You can use **Markdown** here :)
```
Some common frontmatter fields that are natively supported by Quartz:
- `title`: Title of the page. If it isn't provided, Quartz will use the name of the file as the title.
- `description`: Description of the page used for link previews.
- `aliases`: Other names for this note. This is a list of strings.
- `tags`: Tags for this note.
- `draft`: Whether to publish the page or not. This is one way to make [[private pages|pages private]] in Quartz.
- `date`: A string representing the day the note was published. Normally uses `YYYY-MM-DD` format.
## Syncing your Content
When your Quartz is at a point you're happy with, you can save your changes to GitHub.
First, make sure you've [[setting up your GitHub repository|already setup your GitHub repository]] and then do `npx quartz sync`.
## Customization
Frontmatter parsing for `title`, `tags`, `aliases` and `cssclasses` is a functionality of the [[Frontmatter]] plugin, `date` is handled by the [[CreatedModifiedDate]] plugin and `description` by the [[Description]] plugin. See the plugin pages for customization options.

View file

@ -1,23 +0,0 @@
---
title: "Building your Quartz"
---
Once you've [[index#🪴 Get Started|initialized]] Quartz, let's see what it looks like locally:
```bash
npx quartz build --serve
```
This will start a local web server to run your Quartz on your computer. Open a web browser and visit `http://localhost:8080/` to view it.
> [!hint] Flags and options
> For full help options, you can run `npx quartz build --help`.
>
> Most of these have sensible defaults but you can override them if you have a custom setup:
>
> - `-d` or `--directory`: the content folder. This is normally just `content`
> - `-v` or `--verbose`: print out extra logging information
> - `-o` or `--output`: the output folder. This is normally just `public`
> - `--serve`: run a local hot-reloading server to preview your Quartz
> - `--port`: what port to run the local preview server on
> - `--concurrency`: how many threads to use to parse notes

View file

@ -1,108 +0,0 @@
---
title: Configuration
---
Quartz is meant to be extremely configurable, even if you don't know any coding. Most of the configuration you should need can be done by just editing `quartz.config.ts` or changing [[layout|the layout]] in `quartz.layout.ts`.
> [!tip]
> If you edit Quartz configuration using a text-editor that has TypeScript language support like VSCode, it will warn you when you you've made an error in your configuration, helping you avoid configuration mistakes!
The configuration of Quartz can be broken down into two main parts:
```ts title="quartz.config.ts"
const config: QuartzConfig = {
configuration: { ... },
plugins: { ... },
}
```
## General Configuration
This part of the configuration concerns anything that can affect the whole site. The following is a list breaking down all the things you can configure:
- `pageTitle`: title of the site. This is also used when generating the [[RSS Feed]] for your site.
- `enableSPA`: whether to enable [[SPA Routing]] on your site.
- `enablePopovers`: whether to enable [[popover previews]] on your site.
- `analytics`: what to use for analytics on your site. Values can be
- `null`: don't use analytics;
- `{ provider: 'google', tagId: '<your-google-tag>' }`: use Google Analytics;
- `{ provider: 'plausible' }` (managed) or `{ provider: 'plausible', host: '<your-plausible-host>' }` (self-hosted): use [Plausible](https://plausible.io/);
- `{ provider: 'umami', host: '<your-umami-host>', websiteId: '<your-umami-website-id>' }`: use [Umami](https://umami.is/);
- `{ provider: 'goatcounter', websiteId: 'my-goatcounter-id' }` (managed) or `{ provider: 'goatcounter', websiteId: 'my-goatcounter-id', host: 'my-goatcounter-domain.com', scriptSrc: 'https://my-url.to/counter.js' }` (self-hosted) use [GoatCounter](https://goatcounter.com);
- `{ provider: 'posthog', apiKey: '<your-posthog-project-apiKey>', host: '<your-posthog-host>' }`: use [Posthog](https://posthog.com/);
- `{ provider: 'tinylytics', siteId: '<your-site-id>' }`: use [Tinylytics](https://tinylytics.app/);
- `{ provider: 'cabin' }` or `{ provider: 'cabin', host: 'https://cabin.example.com' }` (custom domain): use [Cabin](https://withcabin.com);
- `locale`: used for [[i18n]] and date formatting
- `baseUrl`: this is used for sitemaps and RSS feeds that require an absolute URL to know where the canonical 'home' of your site lives. This is normally the deployed URL of your site (e.g. `quartz.jzhao.xyz` for this site). Do not include the protocol (i.e. `https://`) or any leading or trailing slashes.
- This should also include the subpath if you are [[hosting]] on GitHub pages without a custom domain. For example, if my repository is `jackyzha0/quartz`, GitHub pages would deploy to `https://jackyzha0.github.io/quartz` and the `baseUrl` would be `jackyzha0.github.io/quartz`.
- Note that Quartz 4 will avoid using this as much as possible and use relative URLs whenever it can to make sure your site works no matter _where_ you end up actually deploying it.
- `ignorePatterns`: a list of [glob](<https://en.wikipedia.org/wiki/Glob_(programming)>) patterns that Quartz should ignore and not search through when looking for files inside the `content` folder. See [[private pages]] for more details.
- `defaultDateType`: whether to use created, modified, or published as the default date to display on pages and page listings.
- `theme`: configure how the site looks.
- `cdnCaching`: If `true` (default), use Google CDN to cache the fonts. This will generally will be faster. Disable (`false`) this if you want Quartz to download the fonts to be self-contained.
- `typography`: what fonts to use. Any font available on [Google Fonts](https://fonts.google.com/) works here.
- `header`: Font to use for headers
- `code`: Font for inline and block quotes.
- `body`: Font for everything
- `colors`: controls the theming of the site.
- `light`: page background
- `lightgray`: borders
- `gray`: graph links, heavier borders
- `darkgray`: body text
- `dark`: header text and icons
- `secondary`: link colour, current [[graph view|graph]] node
- `tertiary`: hover states and visited [[graph view|graph]] nodes
- `highlight`: internal link background, highlighted text, [[syntax highlighting|highlighted lines of code]]
- `textHighlight`: markdown highlighted text background
## Plugins
You can think of Quartz plugins as a series of transformations over content.
![[quartz transform pipeline.png]]
```ts title="quartz.config.ts"
plugins: {
transformers: [...],
filters: [...],
emitters: [...],
}
```
- [[tags/plugin/transformer|Transformers]] **map** over content (e.g. parsing frontmatter, generating a description)
- [[tags/plugin/filter|Filters]] **filter** content (e.g. filtering out drafts)
- [[tags/plugin/emitter|Emitters]] **reduce** over content (e.g. creating an RSS feed or pages that list all files with a specific tag)
You can customize the behaviour of Quartz by adding, removing and reordering plugins in the `transformers`, `filters` and `emitters` fields.
> [!note]
> Each node is modified by every transformer _in order_. Some transformers are position sensitive, so you may need to pay particular attention to whether they need to come before or after certain other plugins.
You should take care to add the plugin to the right entry corresponding to its plugin type. For example, to add the [[ExplicitPublish]] plugin (a [[tags/plugin/filter|Filter]]), you would add the following line:
```ts title="quartz.config.ts"
filters: [
...
Plugin.ExplicitPublish(),
...
],
```
To remove a plugin, you should remove all occurrences of it in the `quartz.config.ts`.
To customize plugins further, some plugins may also have their own configuration settings that you can pass in. If you do not pass in a configuration, the plugin will use its default settings.
For example, the [[plugins/Latex|Latex]] plugin allows you to pass in a field specifying the `renderEngine` to choose between Katex and MathJax.
```ts title="quartz.config.ts"
transformers: [
Plugin.FrontMatter(), // use default options
Plugin.Latex({ renderEngine: "katex" }), // set some custom options
]
```
Some plugins are included by default in the[ `quartz.config.ts`](https://github.com/jackyzha0/quartz/blob/v4/quartz.config.ts), but there are more available.
You can see a list of all plugins and their configuration options [[tags/plugin|here]].
If you'd like to make your own plugins, see the [[making plugins|making custom plugins]] guide.

View file

@ -1,7 +0,0 @@
Quartz comes shipped with a Docker image that will allow you to preview your Quartz locally without installing Node.
You can run the below one-liner to run Quartz in Docker.
```sh
docker run --rm -itp 8080:8080 $(docker build -q .)
```

View file

@ -1,82 +0,0 @@
---
title: LaTeX
tags:
- feature/transformer
---
Quartz uses [Katex](https://katex.org/) by default to typeset both inline and block math expressions at build time.
## Syntax
### Block Math
Block math can be rendered by delimiting math expression with `$$`.
```
$$
f(x) = \int_{-\infty}^\infty
f\hat(\xi),e^{2 \pi i \xi x}
\,d\xi
$$
```
$$
f(x) = \int_{-\infty}^\infty
f\hat(\xi),e^{2 \pi i \xi x}
\,d\xi
$$
$$
\begin{aligned}
a &= b + c \\ &= e + f \\
\end{aligned}
$$
$$
\begin{bmatrix}
1 & 2 & 3 \\
a & b & c
\end{bmatrix}
$$
$$
\begin{array}{rll}
E \psi &= H\psi & \text{Expanding the Hamiltonian Operator} \\
&= -\frac{\hbar^2}{2m}\frac{\partial^2}{\partial x^2} \psi + \frac{1}{2}m\omega x^2 \psi & \text{Using the ansatz $\psi(x) = e^{-kx^2}f(x)$, hoping to cancel the $x^2$ term} \\
&= -\frac{\hbar^2}{2m} [4k^2x^2f(x)+2(-2kx)f'(x) + f''(x)]e^{-kx^2} + \frac{1}{2}m\omega x^2 f(x)e^{-kx^2} &\text{Removing the $e^{-kx^2}$ term from both sides} \\
& \Downarrow \\
Ef(x) &= -\frac{\hbar^2}{2m} [4k^2x^2f(x)-4kxf'(x) + f''(x)] + \frac{1}{2}m\omega x^2 f(x) & \text{Choosing $k=\frac{im}{2}\sqrt{\frac{\omega}{\hbar}}$ to cancel the $x^2$ term, via $-\frac{\hbar^2}{2m}4k^2=\frac{1}{2}m \omega$} \\
&= -\frac{\hbar^2}{2m} [-4kxf'(x) + f''(x)] \\
\end{array}
$$
> [!warn]
> Due to limitations in the [underlying parsing library](https://github.com/remarkjs/remark-math), block math in Quartz requires the `$$` delimiters to be on newlines like above.
### Inline Math
Similarly, inline math can be rendered by delimiting math expression with a single `$`. For example, `$e^{i\pi} = -1$` produces $e^{i\pi} = -1$
### Escaping symbols
There will be cases where you may have more than one `$` in a paragraph at once which may accidentally trigger MathJax/Katex.
To get around this, you can escape the dollar sign by doing `\$` instead.
For example:
- Incorrect: `I have $1 and you have $2` produces I have $1 and you have $2
- Correct: `I have \$1 and you have \$2` produces I have \$1 and you have \$2
### Using mhchem
Add the following import to the top of `quartz/plugins/transformers/latex.ts` (before all the other
imports):
```ts title="quartz/plugins/transformers/latex.ts"
import "katex/contrib/mhchem"
```
## Customization
Latex parsing is a functionality of the [[plugins/Latex|Latex]] plugin. See the plugin page for customization options.

View file

@ -1,34 +0,0 @@
---
title: "Mermaid Diagrams"
tags:
- feature/transformer
---
Quartz supports Mermaid which allows you to add diagrams and charts to your notes. Mermaid supports a range of diagrams, such as [flow charts](https://mermaid.js.org/syntax/flowchart.html), [sequence diagrams](https://mermaid.js.org/syntax/sequenceDiagram.html), and [timelines](https://mermaid.js.org/syntax/timeline.html). This is enabled as a part of [[Obsidian compatibility]] and can be configured and enabled/disabled from that plugin.
By default, Quartz will render Mermaid diagrams to match the site theme.
> [!warning]
> Wondering why Mermaid diagrams may not be showing up even if you have them enabled? You may need to reorder your plugins so that [[ObsidianFlavoredMarkdown]] is _after_ [[SyntaxHighlighting]].
## Syntax
To add a Mermaid diagram, create a mermaid code block.
````
```mermaid
sequenceDiagram
Alice->>+John: Hello John, how are you?
Alice->>+John: John, can you hear me?
John-->>-Alice: Hi Alice, I can hear you!
John-->>-Alice: I feel great!
```
````
```mermaid
sequenceDiagram
Alice->>+John: Hello John, how are you?
Alice->>+John: John, can you hear me?
John-->>-Alice: Hi Alice, I can hear you!
John-->>-Alice: I feel great!
```

View file

@ -1,17 +0,0 @@
---
title: "Obsidian Compatibility"
tags:
- feature/transformer
---
Quartz was originally designed as a tool to publish Obsidian vaults as websites. Even as the scope of Quartz has widened over time, it hasn't lost the ability to seamlessly interoperate with Obsidian.
By default, Quartz ships with the [[ObsidianFlavoredMarkdown]] plugin, which is a transformer plugin that adds support for [Obsidian Flavored Markdown](https://help.obsidian.md/Editing+and+formatting/Obsidian+Flavored+Markdown). This includes support for features like [[wikilinks]] and [[Mermaid diagrams]].
It also ships with support for [frontmatter parsing](https://help.obsidian.md/Editing+and+formatting/Properties) with the same fields that Obsidian uses through the [[Frontmatter]] transformer plugin.
Finally, Quartz also provides [[CrawlLinks]] plugin, which allows you to customize Quartz's link resolution behaviour to match Obsidian.
## Configuration
This functionality is provided by the [[ObsidianFlavoredMarkdown]], [[Frontmatter]] and [[CrawlLinks]] plugins. See the plugin pages for customization options.

Some files were not shown because too many files have changed in this diff Show more