The Trans Directory
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Update quartz install

aria db1f9dd6 ede4c658

+751 -148
+2
docs/configuration.md
··· 35 35 - `{ provider: 'cabin' }` or `{ provider: 'cabin', host: 'https://cabin.example.com' }` (custom domain): use [Cabin](https://withcabin.com); 36 36 - `{provider: 'clarity', projectId: '<your-clarity-id-code' }`: use [Microsoft clarity](https://clarity.microsoft.com/). The project id can be found on top of the overview page. 37 37 - `{ provider: 'matomo', siteId: '<your-matomo-id-code', host: 'matomo.example.com' }`: use [Matomo](https://matomo.org/), without protocol. 38 + - `{ provider: 'vercel' }`: use [Vercel Web Analytics](https://vercel.com/docs/concepts/analytics). 39 + - `{ provider: 'rybbit', siteId: 'my-rybbit-id' }` (managed) or `{ provider: 'rybbit', siteId: 'my-rybbit-id', host: 'my-rybbit-domain.com' }` (self-hosted) use [Rybbit](https://rybbit.com); 38 40 - `locale`: used for [[i18n]] and date formatting 39 41 - `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. 40 42 - 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`.
+4
docs/features/Docker Support.md
··· 5 5 ```sh 6 6 docker run --rm -itp 8080:8080 -p 3001:3001 -v ./content:/usr/src/app/content $(docker build -q .) 7 7 ``` 8 + 9 + > [!warning] Not to be used for production 10 + > Serve mode is intended for local previews only. 11 + > For production workloads, see the page on [[hosting]].
+1 -1
docs/features/explorer.md
··· 162 162 Component.Explorer({ 163 163 filterFn: (node) => { 164 164 // exclude files with the tag "explorerexclude" 165 - return node.data.tags?.includes("explorerexclude") !== true 165 + return node.data?.tags?.includes("explorerexclude") !== true 166 166 }, 167 167 }) 168 168 ```
+1 -1
docs/features/popover previews.md
··· 8 8 9 9 When [[creating components|creating your own components]], you can include this `popover-hint` class to also include it in the popover. 10 10 11 - Similar to Obsidian, [[quartz layout.png|images referenced using wikilinks]] can also be viewed as popups. 11 + Similar to Obsidian, [[quartz-layout-desktop.png|images referenced using wikilinks]] can also be viewed as popups. 12 12 13 13 ## Configuration 14 14
+1 -1
docs/hosting.md
··· 15 15 ## Cloudflare Pages 16 16 17 17 1. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com/) and select your account. 18 - 2. In Account Home, select **Workers & Pages** > **Create application** > **Pages** > **Connect to Git**. 18 + 2. In Account Home, select **Compute (Workers)** > **Workers & Pages** > **Create application** > **Pages** > **Connect to Git**. 19 19 3. Select the new GitHub repository that you created and, in the **Set up builds and deployments** section, provide the following information: 20 20 21 21 | Configuration option | Value |
-4
docs/plugins/Latex.md
··· 14 14 - `renderEngine`: the engine to use to render LaTeX equations. Can be `"katex"` for [KaTeX](https://katex.org/), `"mathjax"` for [MathJax](https://www.mathjax.org/) [SVG rendering](https://docs.mathjax.org/en/latest/output/svg.html), or `"typst"` for [Typst](https://typst.app/) (a new way to compose LaTeX equation). Defaults to KaTeX. 15 15 - `customMacros`: custom macros for all LaTeX blocks. It takes the form of a key-value pair where the key is a new command name and the value is the expansion of the macro. For example: `{"\\R": "\\mathbb{R}"}` 16 16 17 - > [!note] Typst support 18 - > 19 - > Currently, typst doesn't support inline-math 20 - 21 17 ## API 22 18 23 19 - Category: Transformer
+51
pnpm-lock.yaml
··· 38 38 d3: 39 39 specifier: ^7.9.0 40 40 version: 7.9.0 41 + dotenv: 42 + specifier: ^17.3.1 43 + version: 17.4.2 41 44 esbuild-sass-plugin: 42 45 specifier: ^3.3.1 43 46 version: 3.3.1(esbuild@0.25.10)(sass-embedded@1.92.0) ··· 468 471 resolution: {integrity: sha512-I4RxkXU90cpufazhGPyVujYwfIm9Nk1QDEmiIsaPwdnm013F7RIceaCc87kAH+oUB1ezqEvC6ga4m7MSlqsJvQ==} 469 472 cpu: [arm64] 470 473 os: [linux] 474 + libc: [glibc] 471 475 472 476 '@img/sharp-libvips-linux-arm@1.2.3': 473 477 resolution: {integrity: sha512-x1uE93lyP6wEwGvgAIV0gP6zmaL/a0tGzJs/BIDDG0zeBhMnuUPm7ptxGhUbcGs4okDJrk4nxgrmxpib9g6HpA==} 474 478 cpu: [arm] 475 479 os: [linux] 480 + libc: [glibc] 476 481 477 482 '@img/sharp-libvips-linux-ppc64@1.2.3': 478 483 resolution: {integrity: sha512-Y2T7IsQvJLMCBM+pmPbM3bKT/yYJvVtLJGfCs4Sp95SjvnFIjynbjzsa7dY1fRJX45FTSfDksbTp6AGWudiyCg==} 479 484 cpu: [ppc64] 480 485 os: [linux] 486 + libc: [glibc] 481 487 482 488 '@img/sharp-libvips-linux-s390x@1.2.3': 483 489 resolution: {integrity: sha512-RgWrs/gVU7f+K7P+KeHFaBAJlNkD1nIZuVXdQv6S+fNA6syCcoboNjsV2Pou7zNlVdNQoQUpQTk8SWDHUA3y/w==} 484 490 cpu: [s390x] 485 491 os: [linux] 492 + libc: [glibc] 486 493 487 494 '@img/sharp-libvips-linux-x64@1.2.3': 488 495 resolution: {integrity: sha512-3JU7LmR85K6bBiRzSUc/Ff9JBVIFVvq6bomKE0e63UXGeRw2HPVEjoJke1Yx+iU4rL7/7kUjES4dZ/81Qjhyxg==} 489 496 cpu: [x64] 490 497 os: [linux] 498 + libc: [glibc] 491 499 492 500 '@img/sharp-libvips-linuxmusl-arm64@1.2.3': 493 501 resolution: {integrity: sha512-F9q83RZ8yaCwENw1GieztSfj5msz7GGykG/BA+MOUefvER69K/ubgFHNeSyUu64amHIYKGDs4sRCMzXVj8sEyw==} 494 502 cpu: [arm64] 495 503 os: [linux] 504 + libc: [musl] 496 505 497 506 '@img/sharp-libvips-linuxmusl-x64@1.2.3': 498 507 resolution: {integrity: sha512-U5PUY5jbc45ANM6tSJpsgqmBF/VsL6LnxJmIf11kB7J5DctHgqm0SkuXzVWtIY90GnJxKnC/JT251TDnk1fu/g==} 499 508 cpu: [x64] 500 509 os: [linux] 510 + libc: [musl] 501 511 502 512 '@img/sharp-linux-arm64@0.34.4': 503 513 resolution: {integrity: sha512-YXU1F/mN/Wu786tl72CyJjP/Ngl8mGHN1hST4BGl+hiW5jhCnV2uRVTNOcaYPs73NeT/H8Upm3y9582JVuZHrQ==} 504 514 engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 505 515 cpu: [arm64] 506 516 os: [linux] 517 + libc: [glibc] 507 518 508 519 '@img/sharp-linux-arm@0.34.4': 509 520 resolution: {integrity: sha512-Xyam4mlqM0KkTHYVSuc6wXRmM7LGN0P12li03jAnZ3EJWZqj83+hi8Y9UxZUbxsgsK1qOEwg7O0Bc0LjqQVtxA==} 510 521 engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 511 522 cpu: [arm] 512 523 os: [linux] 524 + libc: [glibc] 513 525 514 526 '@img/sharp-linux-ppc64@0.34.4': 515 527 resolution: {integrity: sha512-F4PDtF4Cy8L8hXA2p3TO6s4aDt93v+LKmpcYFLAVdkkD3hSxZzee0rh6/+94FpAynsuMpLX5h+LRsSG3rIciUQ==} 516 528 engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 517 529 cpu: [ppc64] 518 530 os: [linux] 531 + libc: [glibc] 519 532 520 533 '@img/sharp-linux-s390x@0.34.4': 521 534 resolution: {integrity: sha512-qVrZKE9Bsnzy+myf7lFKvng6bQzhNUAYcVORq2P7bDlvmF6u2sCmK2KyEQEBdYk+u3T01pVsPrkj943T1aJAsw==} 522 535 engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 523 536 cpu: [s390x] 524 537 os: [linux] 538 + libc: [glibc] 525 539 526 540 '@img/sharp-linux-x64@0.34.4': 527 541 resolution: {integrity: sha512-ZfGtcp2xS51iG79c6Vhw9CWqQC8l2Ot8dygxoDoIQPTat/Ov3qAa8qpxSrtAEAJW+UjTXc4yxCjNfxm4h6Xm2A==} 528 542 engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 529 543 cpu: [x64] 530 544 os: [linux] 545 + libc: [glibc] 531 546 532 547 '@img/sharp-linuxmusl-arm64@0.34.4': 533 548 resolution: {integrity: sha512-8hDVvW9eu4yHWnjaOOR8kHVrew1iIX+MUgwxSuH2XyYeNRtLUe4VNioSqbNkB7ZYQJj9rUTT4PyRscyk2PXFKA==} 534 549 engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 535 550 cpu: [arm64] 536 551 os: [linux] 552 + libc: [musl] 537 553 538 554 '@img/sharp-linuxmusl-x64@0.34.4': 539 555 resolution: {integrity: sha512-lU0aA5L8QTlfKjpDCEFOZsTYGn3AEiO6db8W5aQDxj0nQkVrZWmN3ZP9sYKWJdtq3PWPhUNlqehWyXpYDcI9Sg==} 540 556 engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 541 557 cpu: [x64] 542 558 os: [linux] 559 + libc: [musl] 543 560 544 561 '@img/sharp-wasm32@0.34.4': 545 562 resolution: {integrity: sha512-33QL6ZO/qpRyG7woB/HUALz28WnTMI2W1jgX3Nu2bypqLIKx/QKMILLJzJjI+SIbvXdG9fUnmrxR7vbi1sTBeA==} ··· 610 627 engines: {node: '>= 10'} 611 628 cpu: [arm64] 612 629 os: [linux] 630 + libc: [glibc] 613 631 614 632 '@myriaddreamin/typst-ts-node-compiler-linux-arm64-musl@0.6.0': 615 633 resolution: {integrity: sha512-nSokVjKQR0ZH7Jub53q7he89+m72RSbL97exSedkB4OdZAi9tAxGFIgceGJuN5AC+DiNtMmqsPwlJiERUjgPhQ==} 616 634 engines: {node: '>= 10'} 617 635 cpu: [arm64] 618 636 os: [linux] 637 + libc: [musl] 619 638 620 639 '@myriaddreamin/typst-ts-node-compiler-linux-x64-gnu@0.6.0': 621 640 resolution: {integrity: sha512-3Y2ORiYuCTzQkiHSCHWiGuzTBbNvHTB2lCr3DDsZdvTZ2LZMifPwwICN26X3tlnt6GyC3o/ejZBcMnfNqYbdCw==} 622 641 engines: {node: '>= 10'} 623 642 cpu: [x64] 624 643 os: [linux] 644 + libc: [glibc] 625 645 626 646 '@myriaddreamin/typst-ts-node-compiler-linux-x64-musl@0.6.0': 627 647 resolution: {integrity: sha512-b+kTb4vI0sFTkPtIAUE+UqjhZ4kTiAkh4F/2QKnFitAsURlLcRwTcMc9NJm6SXwW1OM0nPj1IGTfUOFpqLOIPQ==} 628 648 engines: {node: '>= 10'} 629 649 cpu: [x64] 630 650 os: [linux] 651 + libc: [musl] 631 652 632 653 '@myriaddreamin/typst-ts-node-compiler-win32-arm64-msvc@0.6.0': 633 654 resolution: {integrity: sha512-04omIPrXSsRKu4XDhj1WZ9uMjdcFcejBGzyOEV351HVDqg5kxgDB32iG3oLySLrzEcbi9WwI5Si46WrW0wh4mA==} ··· 686 707 engines: {node: '>= 10'} 687 708 cpu: [arm64] 688 709 os: [linux] 710 + libc: [glibc] 689 711 690 712 '@napi-rs/simple-git-linux-arm64-musl@0.1.22': 691 713 resolution: {integrity: sha512-MOs7fPyJiU/wqOpKzAOmOpxJ/TZfP4JwmvPad/cXTOWYwwyppMlXFRms3i98EU3HOazI/wMU2Ksfda3+TBluWA==} 692 714 engines: {node: '>= 10'} 693 715 cpu: [arm64] 694 716 os: [linux] 717 + libc: [musl] 695 718 696 719 '@napi-rs/simple-git-linux-ppc64-gnu@0.1.22': 697 720 resolution: {integrity: sha512-L59dR30VBShRUIZ5/cQHU25upNgKS0AMQ7537J6LCIUEFwwXrKORZKJ8ceR+s3Sr/4jempWVvMdjEpFDE4HYww==} 698 721 engines: {node: '>= 10'} 699 722 cpu: [ppc64] 700 723 os: [linux] 724 + libc: [glibc] 701 725 702 726 '@napi-rs/simple-git-linux-s390x-gnu@0.1.22': 703 727 resolution: {integrity: sha512-4FHkPlCSIZUGC6HiADffbe6NVoTBMd65pIwcd40IDbtFKOgFMBA+pWRqKiQ21FERGH16Zed7XHJJoY3jpOqtmQ==} 704 728 engines: {node: '>= 10'} 705 729 cpu: [s390x] 706 730 os: [linux] 731 + libc: [glibc] 707 732 708 733 '@napi-rs/simple-git-linux-x64-gnu@0.1.22': 709 734 resolution: {integrity: sha512-Ei1tM5Ho/dwknF3pOzqkNW9Iv8oFzRxE8uOhrITcdlpxRxVrBVptUF6/0WPdvd7R9747D/q61QG/AVyWsWLFKw==} 710 735 engines: {node: '>= 10'} 711 736 cpu: [x64] 712 737 os: [linux] 738 + libc: [glibc] 713 739 714 740 '@napi-rs/simple-git-linux-x64-musl@0.1.22': 715 741 resolution: {integrity: sha512-zRYxg7it0p3rLyEJYoCoL2PQJNgArVLyNavHW03TFUAYkYi5bxQ/UFNVpgxMaXohr5yu7qCBqeo9j4DWeysalg==} 716 742 engines: {node: '>= 10'} 717 743 cpu: [x64] 718 744 os: [linux] 745 + libc: [musl] 719 746 720 747 '@napi-rs/simple-git-win32-arm64-msvc@0.1.22': 721 748 resolution: {integrity: sha512-XGFR1fj+Y9cWACcovV2Ey/R2xQOZKs8t+7KHPerYdJ4PtjVzGznI4c2EBHXtdOIYvkw7tL5rZ7FN1HJKdD5Quw==} ··· 780 807 engines: {node: '>= 10.0.0'} 781 808 cpu: [arm] 782 809 os: [linux] 810 + libc: [glibc] 783 811 784 812 '@parcel/watcher-linux-arm-musl@2.5.1': 785 813 resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==} 786 814 engines: {node: '>= 10.0.0'} 787 815 cpu: [arm] 788 816 os: [linux] 817 + libc: [musl] 789 818 790 819 '@parcel/watcher-linux-arm64-glibc@2.5.1': 791 820 resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==} 792 821 engines: {node: '>= 10.0.0'} 793 822 cpu: [arm64] 794 823 os: [linux] 824 + libc: [glibc] 795 825 796 826 '@parcel/watcher-linux-arm64-musl@2.5.1': 797 827 resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==} 798 828 engines: {node: '>= 10.0.0'} 799 829 cpu: [arm64] 800 830 os: [linux] 831 + libc: [musl] 801 832 802 833 '@parcel/watcher-linux-x64-glibc@2.5.1': 803 834 resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==} 804 835 engines: {node: '>= 10.0.0'} 805 836 cpu: [x64] 806 837 os: [linux] 838 + libc: [glibc] 807 839 808 840 '@parcel/watcher-linux-x64-musl@2.5.1': 809 841 resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==} 810 842 engines: {node: '>= 10.0.0'} 811 843 cpu: [x64] 812 844 os: [linux] 845 + libc: [musl] 813 846 814 847 '@parcel/watcher-win32-arm64@2.5.1': 815 848 resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==} ··· 1338 1371 devlop@1.1.0: 1339 1372 resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} 1340 1373 1374 + dotenv@17.4.2: 1375 + resolution: {integrity: sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==} 1376 + engines: {node: '>=12'} 1377 + 1341 1378 earcut@3.0.2: 1342 1379 resolution: {integrity: sha512-X7hshQbLyMJ/3RPhyObLARM2sNxxmRALLKx1+NVFFnQ9gKzmCrxm9+uLIAdBcvc8FNLpctqlQ2V6AE92Ol9UDQ==} 1343 1380 ··· 1645 1682 engines: {node: '>= 12.0.0'} 1646 1683 cpu: [arm64] 1647 1684 os: [linux] 1685 + libc: [glibc] 1648 1686 1649 1687 lightningcss-linux-arm64-musl@1.30.2: 1650 1688 resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==} 1651 1689 engines: {node: '>= 12.0.0'} 1652 1690 cpu: [arm64] 1653 1691 os: [linux] 1692 + libc: [musl] 1654 1693 1655 1694 lightningcss-linux-x64-gnu@1.30.2: 1656 1695 resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==} 1657 1696 engines: {node: '>= 12.0.0'} 1658 1697 cpu: [x64] 1659 1698 os: [linux] 1699 + libc: [glibc] 1660 1700 1661 1701 lightningcss-linux-x64-musl@1.30.2: 1662 1702 resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==} 1663 1703 engines: {node: '>= 12.0.0'} 1664 1704 cpu: [x64] 1665 1705 os: [linux] 1706 + libc: [musl] 1666 1707 1667 1708 lightningcss-win32-arm64-msvc@1.30.2: 1668 1709 resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==} ··· 2146 2187 engines: {node: '>=14.0.0'} 2147 2188 cpu: [arm64] 2148 2189 os: [linux] 2190 + libc: glibc 2149 2191 2150 2192 sass-embedded-linux-arm@1.92.0: 2151 2193 resolution: {integrity: sha512-HMjTDjIT8bHwAVd0c8r8QvGxGZwJg3H06/Y5ZiaiwVGiZtYS9jfef6LnrPw8LY5cOG8wm9RZ9AgVqvkL7E2jBQ==} 2152 2194 engines: {node: '>=14.0.0'} 2153 2195 cpu: [arm] 2154 2196 os: [linux] 2197 + libc: glibc 2155 2198 2156 2199 sass-embedded-linux-musl-arm64@1.92.0: 2157 2200 resolution: {integrity: sha512-xYzZDmcPb3BsaD6qlRTqZqtyMOZfGCSKJBZYj2ZRJiKDDr1sqPSIqKx6G8jc1wJAVdvoNp5tzENnCfY7NRkxNA==} 2158 2201 engines: {node: '>=14.0.0'} 2159 2202 cpu: [arm64] 2160 2203 os: [linux] 2204 + libc: musl 2161 2205 2162 2206 sass-embedded-linux-musl-arm@1.92.0: 2163 2207 resolution: {integrity: sha512-qJDCXm379yRT9+8wKSi6nHFCOODTmD6XmE8rqmMozKo6kvCM+Y3sAMlHrT/0+pfzlGh1JSamkoYIo/ODn+LRVA==} 2164 2208 engines: {node: '>=14.0.0'} 2165 2209 cpu: [arm] 2166 2210 os: [linux] 2211 + libc: musl 2167 2212 2168 2213 sass-embedded-linux-musl-riscv64@1.92.0: 2169 2214 resolution: {integrity: sha512-ZD3a6c7YvAjp1lEkKyaQpHc5EuetQ0RU3YoTfjwHiyWwezsuJHZc4hkS7SXWbZNEvi7tc2U1bdt4nSdx9c5Qxw==} 2170 2215 engines: {node: '>=14.0.0'} 2171 2216 cpu: [riscv64] 2172 2217 os: [linux] 2218 + libc: musl 2173 2219 2174 2220 sass-embedded-linux-musl-x64@1.92.0: 2175 2221 resolution: {integrity: sha512-ShivGoEKmpyL57hQB9K+EMEOWOo+LuwH5eIM2T0sRIHW5n28IS6h12R3WEJVf+PYtSi9FKWazy7kzeLefya6fQ==} 2176 2222 engines: {node: '>=14.0.0'} 2177 2223 cpu: [x64] 2178 2224 os: [linux] 2225 + libc: musl 2179 2226 2180 2227 sass-embedded-linux-riscv64@1.92.0: 2181 2228 resolution: {integrity: sha512-CTZF8rMYBS4JsGGFMUwdPExq6DxhONXQv9omKpVmuleRw52Isx37GaMTQg5zSxunS6QfwqCyUysjWXTLe8khHA==} 2182 2229 engines: {node: '>=14.0.0'} 2183 2230 cpu: [riscv64] 2184 2231 os: [linux] 2232 + libc: glibc 2185 2233 2186 2234 sass-embedded-linux-x64@1.92.0: 2187 2235 resolution: {integrity: sha512-jAY4tzhSUUDUYSl0m+GQub/ZpVk00Pn4ybHeUICAYSQj043A9rkag+LSKDGCvC/0MptMM+/HkIDAC06tRY4PeQ==} 2188 2236 engines: {node: '>=14.0.0'} 2189 2237 cpu: [x64] 2190 2238 os: [linux] 2239 + libc: glibc 2191 2240 2192 2241 sass-embedded-unknown-all@1.92.0: 2193 2242 resolution: {integrity: sha512-s0UF1jquqhrxg0dl/0E+L5tCH1zv1ueF+m3VgJukDkDSTW+nb7wpCGcm8csGoSXnP8+dq53jtUXVtt8sPLr8ZQ==} ··· 3396 3445 devlop@1.1.0: 3397 3446 dependencies: 3398 3447 dequal: 2.0.3 3448 + 3449 + dotenv@17.4.2: {} 3399 3450 3400 3451 earcut@3.0.2: {} 3401 3452
+4
pnpm-workspace.yaml
··· 1 + allowBuilds: 2 + '@parcel/watcher': true 3 + esbuild: true 4 + sharp: true
quartz/bootstrap-cli.mjs
+4
quartz/build.ts
··· 143 143 } 144 144 145 145 const watcher = chokidar.watch(".", { 146 + awaitWriteFinish: { stabilityThreshold: 250 }, 146 147 persistent: true, 147 148 cwd: argv.directory, 148 149 ignoreInitial: true, ··· 151 152 const changes: ChangeEvent[] = [] 152 153 watcher 153 154 .on("add", (fp) => { 155 + fp = toPosixPath(fp) 154 156 if (buildData.ignored(fp)) return 155 157 changes.push({ path: fp as FilePath, type: "add" }) 156 158 void rebuild(changes, clientRefresh, buildData) 157 159 }) 158 160 .on("change", (fp) => { 161 + fp = toPosixPath(fp) 159 162 if (buildData.ignored(fp)) return 160 163 changes.push({ path: fp as FilePath, type: "change" }) 161 164 void rebuild(changes, clientRefresh, buildData) 162 165 }) 163 166 .on("unlink", (fp) => { 167 + fp = toPosixPath(fp) 164 168 if (buildData.ignored(fp)) return 165 169 changes.push({ path: fp as FilePath, type: "delete" }) 166 170 void rebuild(changes, clientRefresh, buildData)
+8
quartz/cfg.ts
··· 47 47 host: string 48 48 siteId: string 49 49 } 50 + | { 51 + provider: "vercel" 52 + } 53 + | { 54 + provider: "rybbit" 55 + siteId: string 56 + host?: string 57 + } 50 58 51 59 export interface GlobalConfiguration { 52 60 pageTitle: string
+4 -4
quartz/cli/handlers.js
··· 318 318 319 319 const result = await ctx.rebuild().catch((err) => { 320 320 console.error(`${styleText("red", "Couldn't parse Quartz configuration:")} ${fp}`) 321 - console.log(`Reason: ${styleText("grey", err)}`) 321 + console.log(`Reason: ${styleText("gray", err)}`) 322 322 process.exit(1) 323 323 }) 324 324 release() ··· 395 395 status >= 200 && status < 300 396 396 ? styleText("green", `[${status}]`) 397 397 : styleText("red", `[${status}]`) 398 - console.log(statusString + styleText("grey", ` ${argv.baseDir}${req.url}`)) 398 + console.log(statusString + styleText("gray", ` ${argv.baseDir}${req.url}`)) 399 399 release() 400 400 } 401 401 ··· 406 406 }) 407 407 console.log( 408 408 styleText("yellow", "[302]") + 409 - styleText("grey", ` ${argv.baseDir}${req.url} -> ${newFp}`), 409 + styleText("gray", ` ${argv.baseDir}${req.url} -> ${newFp}`), 410 410 ) 411 411 res.end() 412 412 } ··· 482 482 .on("change", () => build(clientRefresh)) 483 483 .on("unlink", () => build(clientRefresh)) 484 484 485 - console.log(styleText("grey", "hint: exit with ctrl+c")) 485 + console.log(styleText("gray", "hint: exit with ctrl+c")) 486 486 } 487 487 } 488 488
+2 -2
quartz/cli/helpers.js
··· 7 7 export function escapePath(fp) { 8 8 return fp 9 9 .replace(/\\ /g, " ") // unescape spaces 10 - .replace(/^".*"$/, "$1") 11 - .replace(/^'.*"$/, "$1") 10 + .replace(/^"(.*)"$/, "$1") 11 + .replace(/^'(.*)'$/, "$1") 12 12 .trim() 13 13 } 14 14
+1 -1
quartz/components/Search.tsx
··· 20 20 return ( 21 21 <div class={classNames(displayClass, "search")}> 22 22 <button class="search-button"> 23 - <p>{i18n(cfg.locale).components.search.title}</p> 24 23 <svg role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 19.9 19.7"> 25 24 <title>Search</title> 26 25 <g class="search-path" fill="none"> ··· 28 27 <circle cx="8" cy="8" r="7" /> 29 28 </g> 30 29 </svg> 30 + <p>{i18n(cfg.locale).components.search.title}</p> 31 31 </button> 32 32 <div class="search-container"> 33 33 <div class="search-space">
+31 -3
quartz/components/renderPage.tsx
··· 9 9 import { Root, Element, ElementContent } from "hast" 10 10 import { GlobalConfiguration } from "../cfg" 11 11 import { i18n } from "../i18n" 12 + import { styleText } from "util" 12 13 13 14 interface RenderComponents { 14 15 head: QuartzComponent ··· 68 69 cfg: GlobalConfiguration, 69 70 slug: FullSlug, 70 71 componentData: QuartzComponentProps, 72 + visited: Set<FullSlug>, 71 73 ) { 72 74 // process transcludes in componentData 73 75 visit(root, "element", (node, _index, _parent) => { ··· 76 78 if (classNames.includes("transclude")) { 77 79 const inner = node.children[0] as Element 78 80 const transcludeTarget = (inner.properties["data-slug"] ?? slug) as FullSlug 81 + if (visited.has(transcludeTarget)) { 82 + console.warn( 83 + styleText( 84 + "yellow", 85 + `Warning: Skipping circular transclusion: ${slug} -> ${transcludeTarget}`, 86 + ), 87 + ) 88 + node.children = [ 89 + { 90 + type: "element", 91 + tagName: "p", 92 + properties: { style: "color: var(--secondary);" }, 93 + children: [ 94 + { 95 + type: "text", 96 + value: `Circular transclusion detected: ${transcludeTarget}`, 97 + }, 98 + ], 99 + }, 100 + ] 101 + return 102 + } 103 + visited.add(transcludeTarget) 104 + 79 105 const page = componentData.allFiles.find((f) => f.slug === transcludeTarget) 80 106 if (!page) { 81 107 return ··· 196 222 // make a deep copy of the tree so we don't remove the transclusion references 197 223 // for the file cached in contentMap in build.ts 198 224 const root = clone(componentData.tree) as Root 199 - renderTranscludes(root, cfg, slug, componentData) 225 + const visited = new Set<FullSlug>([slug]) 226 + renderTranscludes(root, cfg, slug, componentData, visited) 200 227 201 228 // set componentData.tree to the edited html that has transclusions rendered 202 229 componentData.tree = root ··· 231 258 ) 232 259 233 260 const lang = componentData.fileData.frontmatter?.lang ?? cfg.locale?.split("-")[0] ?? "en" 261 + const direction = i18n(cfg.locale).direction ?? "ltr" 234 262 const doc = ( 235 - <html lang={lang}> 263 + <html lang={lang} dir={direction}> 236 264 <Head {...componentData} /> 237 265 <body data-slug={slug}> 238 266 <div id="quartz-root" class="page"> ··· 266 294 </body> 267 295 {pageResources.js 268 296 .filter((resource) => resource.loadTime === "afterDOMReady") 269 - .map((res) => JSResourceToScriptElement(res))} 297 + .map((res) => JSResourceToScriptElement(res, true))} 270 298 </html> 271 299 ) 272 300
+4
quartz/components/scripts/explorer.inline.ts
··· 111 111 const folderPath = node.slug 112 112 folderContainer.dataset.folderpath = folderPath 113 113 114 + if (currentSlug === folderPath) { 115 + folderContainer.classList.add("active") 116 + } 117 + 114 118 if (opts.folderClickBehavior === "link") { 115 119 // Replace button with link for link behavior 116 120 const button = titleContainer.querySelector(".folder-button") as HTMLElement
+45 -3
quartz/components/scripts/mermaid.inline.ts
··· 29 29 const mouseDownHandler = this.onMouseDown.bind(this) 30 30 const mouseMoveHandler = this.onMouseMove.bind(this) 31 31 const mouseUpHandler = this.onMouseUp.bind(this) 32 + 33 + // Touch drag events 34 + const touchStartHandler = this.onTouchStart.bind(this) 35 + const touchMoveHandler = this.onTouchMove.bind(this) 36 + const touchEndHandler = this.onTouchEnd.bind(this) 37 + 32 38 const resizeHandler = this.resetTransform.bind(this) 33 39 34 40 this.container.addEventListener("mousedown", mouseDownHandler) 35 41 document.addEventListener("mousemove", mouseMoveHandler) 36 42 document.addEventListener("mouseup", mouseUpHandler) 43 + 44 + this.container.addEventListener("touchstart", touchStartHandler, { passive: false }) 45 + document.addEventListener("touchmove", touchMoveHandler, { passive: false }) 46 + document.addEventListener("touchend", touchEndHandler) 47 + 37 48 window.addEventListener("resize", resizeHandler) 38 49 39 50 this.cleanups.push( 40 51 () => this.container.removeEventListener("mousedown", mouseDownHandler), 41 52 () => document.removeEventListener("mousemove", mouseMoveHandler), 42 53 () => document.removeEventListener("mouseup", mouseUpHandler), 54 + () => this.container.removeEventListener("touchstart", touchStartHandler), 55 + () => document.removeEventListener("touchmove", touchMoveHandler), 56 + () => document.removeEventListener("touchend", touchEndHandler), 43 57 () => window.removeEventListener("resize", resizeHandler), 44 58 ) 45 59 } ··· 99 113 this.container.style.cursor = "grab" 100 114 } 101 115 116 + private onTouchStart(e: TouchEvent) { 117 + if (e.touches.length !== 1) return 118 + this.isDragging = true 119 + const touch = e.touches[0] 120 + this.startPan = { x: touch.clientX - this.currentPan.x, y: touch.clientY - this.currentPan.y } 121 + } 122 + 123 + private onTouchMove(e: TouchEvent) { 124 + if (!this.isDragging || e.touches.length !== 1) return 125 + e.preventDefault() // Prevent scrolling 126 + 127 + const touch = e.touches[0] 128 + this.currentPan = { 129 + x: touch.clientX - this.startPan.x, 130 + y: touch.clientY - this.startPan.y, 131 + } 132 + 133 + this.updateTransform() 134 + } 135 + 136 + private onTouchEnd() { 137 + this.isDragging = false 138 + } 139 + 102 140 private zoom(delta: number) { 103 141 const newScale = Math.min(Math.max(this.scale + delta, this.MIN_SCALE), this.MAX_SCALE) 104 142 ··· 120 158 } 121 159 122 160 private resetTransform() { 123 - this.scale = 1 124 161 const svg = this.content.querySelector("svg")! 162 + const rect = svg.getBoundingClientRect() 163 + const width = rect.width / this.scale 164 + const height = rect.height / this.scale 165 + 166 + this.scale = 1 125 167 this.currentPan = { 126 - x: svg.getBoundingClientRect().width / 2, 127 - y: svg.getBoundingClientRect().height / 2, 168 + x: (this.container.clientWidth - width) / 2, 169 + y: (this.container.clientHeight - height) / 2, 128 170 } 129 171 this.updateTransform() 130 172 }
+49 -5
quartz/components/scripts/search.inline.ts
··· 1 - import FlexSearch from "flexsearch" 1 + import FlexSearch, { DefaultDocumentSearchResults } from "flexsearch" 2 2 import { ContentDetails } from "../../plugins/emitters/contentIndex" 3 3 import { registerEscapeHandler, removeAllChildren } from "./util" 4 4 import { FullSlug, normalizeRelativeURLs, resolveRelative } from "../../util/path" ··· 9 9 title: string 10 10 content: string 11 11 tags: string[] 12 + [key: string]: any 12 13 } 13 14 14 15 // Can be expanded with things like "term" in the future 15 16 type SearchType = "basic" | "tags" 16 17 let searchType: SearchType = "basic" 17 18 let currentSearchTerm: string = "" 18 - const encoder = (str: string) => str.toLowerCase().split(/([^a-z]|[^\x00-\x7F])/) 19 + const encoder = (str: string): string[] => { 20 + const tokens: string[] = [] 21 + let bufferStart = -1 22 + let bufferEnd = -1 23 + const lower = str.toLowerCase() 24 + 25 + let i = 0 26 + for (const char of lower) { 27 + const code = char.codePointAt(0)! 28 + 29 + const isCJK = 30 + (code >= 0x3040 && code <= 0x309f) || 31 + (code >= 0x30a0 && code <= 0x30ff) || 32 + (code >= 0x4e00 && code <= 0x9fff) || 33 + (code >= 0xac00 && code <= 0xd7af) || 34 + (code >= 0x20000 && code <= 0x2a6df) 35 + 36 + const isWhitespace = code === 32 || code === 9 || code === 10 || code === 13 37 + 38 + if (isCJK) { 39 + if (bufferStart !== -1) { 40 + tokens.push(lower.slice(bufferStart, bufferEnd)) 41 + bufferStart = -1 42 + } 43 + tokens.push(char) 44 + } else if (isWhitespace) { 45 + if (bufferStart !== -1) { 46 + tokens.push(lower.slice(bufferStart, bufferEnd)) 47 + bufferStart = -1 48 + } 49 + } else { 50 + if (bufferStart === -1) bufferStart = i 51 + bufferEnd = i + char.length 52 + } 53 + 54 + i += char.length 55 + } 56 + 57 + if (bufferStart !== -1) { 58 + tokens.push(lower.slice(bufferStart)) 59 + } 60 + 61 + return tokens 62 + } 63 + 19 64 let index = new FlexSearch.Document<Item>({ 20 - charset: "latin:extra", 21 65 encode: encoder, 22 66 document: { 23 67 id: "id", ··· 397 441 searchLayout.classList.toggle("display-results", currentSearchTerm !== "") 398 442 searchType = currentSearchTerm.startsWith("#") ? "tags" : "basic" 399 443 400 - let searchResults: FlexSearch.SimpleDocumentSearchResultSetUnit[] 444 + let searchResults: DefaultDocumentSearchResults<Item> 401 445 if (searchType === "tags") { 402 446 currentSearchTerm = currentSearchTerm.substring(1).trim() 403 447 const separatorIndex = currentSearchTerm.indexOf(" ") ··· 410 454 // return at least 10000 documents, so it is enough to filter them by tag (implemented in flexsearch) 411 455 limit: Math.max(numSearchResults, 10000), 412 456 index: ["title", "content"], 413 - tag: tag, 457 + tag: { tags: tag }, 414 458 }) 415 459 for (let searchResult of searchResults) { 416 460 searchResult.result = searchResult.result.slice(0, numSearchResults)
+163
quartz/components/scripts/search.test.ts
··· 1 + import test, { describe } from "node:test" 2 + import assert from "node:assert" 3 + 4 + // Inline the encoder function from search.inline.ts for testing 5 + const encoder = (str: string): string[] => { 6 + const tokens: string[] = [] 7 + let bufferStart = -1 8 + let bufferEnd = -1 9 + const lower = str.toLowerCase() 10 + 11 + let i = 0 12 + for (const char of lower) { 13 + const code = char.codePointAt(0)! 14 + 15 + const isCJK = 16 + (code >= 0x3040 && code <= 0x309f) || 17 + (code >= 0x30a0 && code <= 0x30ff) || 18 + (code >= 0x4e00 && code <= 0x9fff) || 19 + (code >= 0xac00 && code <= 0xd7af) || 20 + (code >= 0x20000 && code <= 0x2a6df) 21 + 22 + const isWhitespace = code === 32 || code === 9 || code === 10 || code === 13 23 + 24 + if (isCJK) { 25 + if (bufferStart !== -1) { 26 + tokens.push(lower.slice(bufferStart, bufferEnd)) 27 + bufferStart = -1 28 + } 29 + tokens.push(char) 30 + } else if (isWhitespace) { 31 + if (bufferStart !== -1) { 32 + tokens.push(lower.slice(bufferStart, bufferEnd)) 33 + bufferStart = -1 34 + } 35 + } else { 36 + if (bufferStart === -1) bufferStart = i 37 + bufferEnd = i + char.length 38 + } 39 + 40 + i += char.length 41 + } 42 + 43 + if (bufferStart !== -1) { 44 + tokens.push(lower.slice(bufferStart)) 45 + } 46 + 47 + return tokens 48 + } 49 + 50 + describe("search encoder", () => { 51 + describe("English text", () => { 52 + test("should tokenize simple English words", () => { 53 + const result = encoder("hello world") 54 + assert.deepStrictEqual(result, ["hello", "world"]) 55 + }) 56 + 57 + test("should handle multiple spaces", () => { 58 + const result = encoder("hello world") 59 + assert.deepStrictEqual(result, ["hello", "world"]) 60 + }) 61 + 62 + test("should handle tabs and newlines", () => { 63 + const result = encoder("hello\tworld\ntest") 64 + assert.deepStrictEqual(result, ["hello", "world", "test"]) 65 + }) 66 + 67 + test("should lowercase all text", () => { 68 + const result = encoder("Hello WORLD Test") 69 + assert.deepStrictEqual(result, ["hello", "world", "test"]) 70 + }) 71 + }) 72 + 73 + describe("CJK text", () => { 74 + test("should tokenize Japanese Hiragana character by character", () => { 75 + const result = encoder("こんにちは") 76 + assert.deepStrictEqual(result, ["こ", "ん", "に", "ち", "は"]) 77 + }) 78 + 79 + test("should tokenize Japanese Katakana character by character", () => { 80 + const result = encoder("コントロール") 81 + assert.deepStrictEqual(result, ["コ", "ン", "ト", "ロ", "ー", "ル"]) 82 + }) 83 + 84 + test("should tokenize Japanese Kanji character by character", () => { 85 + const result = encoder("日本語") 86 + assert.deepStrictEqual(result, ["日", "本", "語"]) 87 + }) 88 + 89 + test("should tokenize Korean Hangul character by character", () => { 90 + const result = encoder("안녕하세요") 91 + assert.deepStrictEqual(result, ["안", "녕", "하", "세", "요"]) 92 + }) 93 + 94 + test("should tokenize Chinese characters character by character", () => { 95 + const result = encoder("你好世界") 96 + assert.deepStrictEqual(result, ["你", "好", "世", "界"]) 97 + }) 98 + 99 + test("should handle mixed Hiragana/Katakana/Kanji", () => { 100 + const result = encoder("て以来") 101 + assert.deepStrictEqual(result, ["て", "以", "来"]) 102 + }) 103 + }) 104 + 105 + describe("Mixed CJK and English", () => { 106 + test("should handle Japanese with English words", () => { 107 + const result = encoder("hello 世界") 108 + assert.deepStrictEqual(result, ["hello", "世", "界"]) 109 + }) 110 + 111 + test("should handle English with Japanese words", () => { 112 + const result = encoder("世界 hello world") 113 + assert.deepStrictEqual(result, ["世", "界", "hello", "world"]) 114 + }) 115 + 116 + test("should handle complex mixed content", () => { 117 + const result = encoder("これはtest文章です") 118 + assert.deepStrictEqual(result, ["こ", "れ", "は", "test", "文", "章", "で", "す"]) 119 + }) 120 + 121 + test("should handle mixed Korean and English", () => { 122 + const result = encoder("hello 안녕 world") 123 + assert.deepStrictEqual(result, ["hello", "안", "녕", "world"]) 124 + }) 125 + 126 + test("should handle mixed Chinese and English", () => { 127 + const result = encoder("你好 world") 128 + assert.deepStrictEqual(result, ["你", "好", "world"]) 129 + }) 130 + }) 131 + 132 + describe("Edge cases", () => { 133 + test("should handle empty string", () => { 134 + const result = encoder("") 135 + assert.deepStrictEqual(result, []) 136 + }) 137 + 138 + test("should handle only whitespace", () => { 139 + const result = encoder(" \t\n ") 140 + assert.deepStrictEqual(result, []) 141 + }) 142 + 143 + test("should handle single character", () => { 144 + const result = encoder("a") 145 + assert.deepStrictEqual(result, ["a"]) 146 + }) 147 + 148 + test("should handle single CJK character", () => { 149 + const result = encoder("あ") 150 + assert.deepStrictEqual(result, ["あ"]) 151 + }) 152 + 153 + test("should handle CJK with trailing whitespace", () => { 154 + const result = encoder("日本語 ") 155 + assert.deepStrictEqual(result, ["日", "本", "語"]) 156 + }) 157 + 158 + test("should handle English with trailing whitespace", () => { 159 + const result = encoder("hello ") 160 + assert.deepStrictEqual(result, ["hello"]) 161 + }) 162 + }) 163 + })
+3 -3
quartz/components/scripts/spa.inline.ts
··· 102 102 html.body.appendChild(announcer) 103 103 104 104 // morph body 105 - micromorph(document.body, html.body) 105 + await micromorph(document.body, html.body) 106 106 107 107 // scroll into place and add history 108 108 if (!isBack) { ··· 115 115 } 116 116 117 117 // now, patch head, re-executing scripts 118 - const elementsToRemove = document.head.querySelectorAll(":not([spa-preserve])") 118 + const elementsToRemove = document.head.querySelectorAll(":not([data-persist])") 119 119 elementsToRemove.forEach((el) => el.remove()) 120 - const elementsToAdd = html.head.querySelectorAll(":not([spa-preserve])") 120 + const elementsToAdd = html.head.querySelectorAll(":not([data-persist])") 121 121 elementsToAdd.forEach((el) => document.head.appendChild(el)) 122 122 123 123 // delay setting the url until now
+1 -1
quartz/components/styles/darkmode.scss
··· 5 5 background: none; 6 6 border: none; 7 7 width: 20px; 8 - height: 20px; 8 + height: 32px; 9 9 margin: 0; 10 10 text-align: inherit; 11 11 flex-shrink: 0;
+15 -3
quartz/components/styles/explorer.scss
··· 6 6 & > :not(.sidebar.left:has(.explorer)) { 7 7 transition: transform 300ms ease-in-out; 8 8 } 9 + 9 10 &.lock-scroll > :not(.sidebar.left:has(.explorer)) { 10 11 transform: translateX(100dvw); 11 12 transition: transform 300ms ease-in-out; ··· 33 34 34 35 min-height: 1.2rem; 35 36 flex: 0 1 auto; 37 + 36 38 &.collapsed { 37 39 flex: 0 1 1.2rem; 40 + 38 41 & .fold { 39 42 transform: rotateZ(-90deg); 40 43 } ··· 118 121 list-style: none; 119 122 margin: 0; 120 123 padding: 0; 121 - overscroll-behavior: contain; 124 + 125 + &.explorer-ul { 126 + overscroll-behavior: contain; 127 + } 122 128 123 129 & li > a { 124 130 color: var(--dark); ··· 133 139 } 134 140 135 141 .folder-outer { 142 + visibility: collapse; 136 143 display: grid; 137 144 grid-template-rows: 0fr; 138 - transition: grid-template-rows 0.3s ease-in-out; 145 + transition-property: grid-template-rows, visibility; 146 + transition-duration: 0.3s; 147 + transition-timing-function: ease-in-out; 139 148 } 140 149 141 150 .folder-outer.open { 151 + visibility: visible; 142 152 grid-template-rows: 1fr; 143 153 } 144 154 ··· 265 275 266 276 .mobile-no-scroll { 267 277 @media all and ($mobile) { 268 - overscroll-behavior: none; 278 + .explorer-content > .explorer-ul { 279 + overscroll-behavior: contain; 280 + } 269 281 } 270 282 }
-1
quartz/components/styles/mermaid.inline.scss
··· 65 65 overflow: hidden; 66 66 67 67 & > .mermaid-content { 68 - padding: 2rem; 69 68 position: relative; 70 69 transform-origin: 0 0; 71 70 transition: transform 0.1s ease;
+1 -1
quartz/components/styles/readermode.scss
··· 5 5 background: none; 6 6 border: none; 7 7 width: 20px; 8 - height: 20px; 8 + height: 32px; 9 9 margin: 0; 10 10 text-align: inherit; 11 11 flex-shrink: 0;
+6 -6
quartz/components/styles/search.scss
··· 8 8 } 9 9 10 10 & > .search-button { 11 - background-color: color-mix(in srgb, var(--lightgray) 60%, var(--light)); 12 - border: none; 11 + background-color: transparent; 12 + border: 1px var(--lightgray) solid; 13 13 border-radius: 4px; 14 14 font-family: inherit; 15 15 font-size: inherit; 16 16 height: 2rem; 17 - padding: 0; 17 + padding: 0 1rem 0 0; 18 18 display: flex; 19 19 align-items: center; 20 20 text-align: inherit; 21 21 cursor: pointer; 22 22 white-space: nowrap; 23 23 width: 100%; 24 - justify-content: space-between; 25 24 26 25 & > p { 27 26 display: inline; 28 - padding: 0 1rem; 27 + color: var(--gray); 28 + text-wrap: unset; 29 29 } 30 30 31 31 & svg { ··· 36 36 37 37 .search-path { 38 38 stroke: var(--darkgray); 39 - stroke-width: 2px; 39 + stroke-width: 1.5px; 40 40 transition: stroke 0.5s ease; 41 41 } 42 42 }
+4
quartz/i18n/index.ts
··· 27 27 import fi from "./locales/fi-FI" 28 28 import no from "./locales/nb-NO" 29 29 import id from "./locales/id-ID" 30 + import kk from "./locales/kk-KZ" 31 + import he from "./locales/he-IL" 30 32 31 33 export const TRANSLATIONS = { 32 34 "en-US": enUs, ··· 78 80 "fi-FI": fi, 79 81 "nb-NO": no, 80 82 "id-ID": id, 83 + "kk-KZ": kk, 84 + "he-IL": he, 81 85 } as const 82 86 83 87 export const defaultTranslation = "en-US"
+1
quartz/i18n/locales/ar-SA.ts
··· 5 5 title: "غير معنون", 6 6 description: "لم يتم تقديم أي وصف", 7 7 }, 8 + direction: "rtl" as const, 8 9 components: { 9 10 callout: { 10 11 note: "ملاحظة",
+3 -3
quartz/i18n/locales/de-DE.ts
··· 15 15 success: "Erfolg", 16 16 question: "Frage", 17 17 warning: "Warnung", 18 - failure: "Misserfolg", 18 + failure: "Fehlgeschlagen", 19 19 danger: "Gefahr", 20 20 bug: "Fehler", 21 21 example: "Beispiel", ··· 57 57 title: "Inhaltsverzeichnis", 58 58 }, 59 59 contentMeta: { 60 - readingTime: ({ minutes }) => `${minutes} min read`, 60 + readingTime: ({ minutes }) => `${minutes} Min. Lesezeit`, 61 61 }, 62 62 }, 63 63 pages: { ··· 68 68 error: { 69 69 title: "Nicht gefunden", 70 70 notFound: "Diese Seite ist entweder nicht öffentlich oder existiert nicht.", 71 - home: "Return to Homepage", 71 + home: "Zur Startseite", 72 72 }, 73 73 folderContent: { 74 74 folder: "Ordner",
+1
quartz/i18n/locales/definition.ts
··· 21 21 title: string 22 22 description: string 23 23 } 24 + direction?: "ltr" | "rtl" 24 25 components: { 25 26 callout: CalloutTranslation 26 27 backlinks: {
+1
quartz/i18n/locales/fa-IR.ts
··· 5 5 title: "بدون عنوان", 6 6 description: "توضیح خاصی اضافه نشده است", 7 7 }, 8 + direction: "rtl" as const, 8 9 components: { 9 10 callout: { 10 11 note: "یادداشت",
+88
quartz/i18n/locales/he-IL.ts
··· 1 + import { Translation } from "./definition" 2 + 3 + export default { 4 + propertyDefaults: { 5 + title: "ללא כותרת", 6 + description: "לא סופק תיאור", 7 + }, 8 + direction: "rtl" as const, 9 + components: { 10 + callout: { 11 + note: "הערה", 12 + abstract: "תקציר", 13 + info: "מידע", 14 + todo: "לעשות", 15 + tip: "טיפ", 16 + success: "הצלחה", 17 + question: "שאלה", 18 + warning: "אזהרה", 19 + failure: "כשלון", 20 + danger: "סכנה", 21 + bug: "באג", 22 + example: "דוגמה", 23 + quote: "ציטוט", 24 + }, 25 + backlinks: { 26 + title: "קישורים חוזרים", 27 + noBacklinksFound: "לא נמצאו קישורים חוזרים", 28 + }, 29 + themeToggle: { 30 + lightMode: "מצב בהיר", 31 + darkMode: "מצב כהה", 32 + }, 33 + readerMode: { 34 + title: "מצב קריאה", 35 + }, 36 + explorer: { 37 + title: "סייר", 38 + }, 39 + footer: { 40 + createdWith: "נוצר באמצעות", 41 + }, 42 + graph: { 43 + title: "מבט גרף", 44 + }, 45 + recentNotes: { 46 + title: "הערות אחרונות", 47 + seeRemainingMore: ({ remaining }) => `עיין ב ${remaining} נוספים →`, 48 + }, 49 + transcludes: { 50 + transcludeOf: ({ targetSlug }) => `מצוטט מ ${targetSlug}`, 51 + linkToOriginal: "קישור למקורי", 52 + }, 53 + search: { 54 + title: "חיפוש", 55 + searchBarPlaceholder: "חפשו משהו", 56 + }, 57 + tableOfContents: { 58 + title: "תוכן עניינים", 59 + }, 60 + contentMeta: { 61 + readingTime: ({ minutes }) => `${minutes} דקות קריאה`, 62 + }, 63 + }, 64 + pages: { 65 + rss: { 66 + recentNotes: "הערות אחרונות", 67 + lastFewNotes: ({ count }) => `${count} הערות אחרונות`, 68 + }, 69 + error: { 70 + title: "לא נמצא", 71 + notFound: "העמוד הזה פרטי או לא קיים.", 72 + home: "חזרה לעמוד הבית", 73 + }, 74 + folderContent: { 75 + folder: "תיקייה", 76 + itemsUnderFolder: ({ count }) => 77 + count === 1 ? "פריט אחד תחת תיקייה זו." : `${count} פריטים תחת תיקייה זו.`, 78 + }, 79 + tagContent: { 80 + tag: "תגית", 81 + tagIndex: "מפתח התגיות", 82 + itemsUnderTag: ({ count }) => 83 + count === 1 ? "פריט אחד עם תגית זו." : `${count} פריטים עם תגית זו.`, 84 + showingFirst: ({ count }) => `מראה את ה-${count} תגיות הראשונות.`, 85 + totalTags: ({ count }) => `${count} תגיות נמצאו סך הכל.`, 86 + }, 87 + }, 88 + } as const satisfies Translation
+11 -9
quartz/i18n/locales/it-IT.ts
··· 8 8 components: { 9 9 callout: { 10 10 note: "Nota", 11 - abstract: "Astratto", 11 + abstract: "Abstract", 12 12 info: "Info", 13 13 todo: "Da fare", 14 14 tip: "Consiglio", ··· 17 17 warning: "Attenzione", 18 18 failure: "Errore", 19 19 danger: "Pericolo", 20 - bug: "Bug", 20 + bug: "Problema", 21 21 example: "Esempio", 22 22 quote: "Citazione", 23 23 }, ··· 43 43 }, 44 44 recentNotes: { 45 45 title: "Note recenti", 46 - seeRemainingMore: ({ remaining }) => `Vedi ${remaining} altro →`, 46 + seeRemainingMore: ({ remaining }) => 47 + remaining === 1 ? "Vedi 1 altra →" : `Vedi altre ${remaining} →`, 47 48 }, 48 49 transcludes: { 49 - transcludeOf: ({ targetSlug }) => `Transclusione di ${targetSlug}`, 50 + transcludeOf: ({ targetSlug }) => `Inclusione di ${targetSlug}`, 50 51 linkToOriginal: "Link all'originale", 51 52 }, 52 53 search: { ··· 54 55 searchBarPlaceholder: "Cerca qualcosa", 55 56 }, 56 57 tableOfContents: { 57 - title: "Tabella dei contenuti", 58 + title: "Indice", 58 59 }, 59 60 contentMeta: { 60 - readingTime: ({ minutes }) => `${minutes} minuti`, 61 + readingTime: ({ minutes }) => (minutes === 1 ? "1 minuto" : `${minutes} minuti`), 61 62 }, 62 63 }, 63 64 pages: { 64 65 rss: { 65 66 recentNotes: "Note recenti", 66 - lastFewNotes: ({ count }) => `Ultime ${count} note`, 67 + lastFewNotes: ({ count }) => (count === 1 ? "Ultima nota" : `Ultime ${count} note`), 67 68 }, 68 69 error: { 69 70 title: "Non trovato", ··· 80 81 tagIndex: "Indice etichette", 81 82 itemsUnderTag: ({ count }) => 82 83 count === 1 ? "1 oggetto con questa etichetta." : `${count} oggetti con questa etichetta.`, 83 - showingFirst: ({ count }) => `Prime ${count} etichette.`, 84 - totalTags: ({ count }) => `Trovate ${count} etichette totali.`, 84 + showingFirst: ({ count }) => (count === 1 ? "Prima etichetta." : `Prime ${count} etichette.`), 85 + totalTags: ({ count }) => 86 + count === 1 ? "Trovata 1 etichetta in totale." : `Trovate ${count} etichette totali.`, 85 87 }, 86 88 }, 87 89 } as const satisfies Translation
+87
quartz/i18n/locales/kk-KZ.ts
··· 1 + import { Translation } from "./definition" 2 + 3 + export default { 4 + propertyDefaults: { 5 + title: "Атаусыз", 6 + description: "Сипаттама берілмеген", 7 + }, 8 + components: { 9 + callout: { 10 + note: "Ескерту", 11 + abstract: "Аннотация", 12 + info: "Ақпарат", 13 + todo: "Істеу керек", 14 + tip: "Кеңес", 15 + success: "Сәттілік", 16 + question: "Сұрақ", 17 + warning: "Ескерту", 18 + failure: "Қате", 19 + danger: "Қауіп", 20 + bug: "Қате", 21 + example: "Мысал", 22 + quote: "Дәйексөз", 23 + }, 24 + backlinks: { 25 + title: "Артқа сілтемелер", 26 + noBacklinksFound: "Артқа сілтемелер табылмады", 27 + }, 28 + themeToggle: { 29 + lightMode: "Жарық режимі", 30 + darkMode: "Қараңғы режим", 31 + }, 32 + readerMode: { 33 + title: "Оқу режимі", 34 + }, 35 + explorer: { 36 + title: "Зерттеуші", 37 + }, 38 + footer: { 39 + createdWith: "Құрастырылған құрал:", 40 + }, 41 + graph: { 42 + title: "Граф көрінісі", 43 + }, 44 + recentNotes: { 45 + title: "Соңғы жазбалар", 46 + seeRemainingMore: ({ remaining }) => `Тағы ${remaining} жазбаны қарау →`, 47 + }, 48 + transcludes: { 49 + transcludeOf: ({ targetSlug }) => `${targetSlug} кірістіру`, 50 + linkToOriginal: "Бастапқыға сілтеме", 51 + }, 52 + search: { 53 + title: "Іздеу", 54 + searchBarPlaceholder: "Бірдеңе іздеу", 55 + }, 56 + tableOfContents: { 57 + title: "Мазмұны", 58 + }, 59 + contentMeta: { 60 + readingTime: ({ minutes }) => `${minutes} мин оқу`, 61 + }, 62 + }, 63 + pages: { 64 + rss: { 65 + recentNotes: "Соңғы жазбалар", 66 + lastFewNotes: ({ count }) => `Соңғы ${count} жазба`, 67 + }, 68 + error: { 69 + title: "Табылмады", 70 + notFound: "Бұл бет жеке немесе жоқ болуы мүмкін.", 71 + home: "Басты бетке оралу", 72 + }, 73 + folderContent: { 74 + folder: "Қалта", 75 + itemsUnderFolder: ({ count }) => 76 + count === 1 ? "Бұл қалтада 1 элемент бар." : `Бұл қалтада ${count} элемент бар.`, 77 + }, 78 + tagContent: { 79 + tag: "Тег", 80 + tagIndex: "Тегтер индексі", 81 + itemsUnderTag: ({ count }) => 82 + count === 1 ? "Бұл тегпен 1 элемент." : `Бұл тегпен ${count} элемент.`, 83 + showingFirst: ({ count }) => `Алғашқы ${count} тег көрсетілуде.`, 84 + totalTags: ({ count }) => `Барлығы ${count} тег табылды.`, 85 + }, 86 + }, 87 + } as const satisfies Translation
+37 -39
quartz/i18n/locales/vi-VN.ts
··· 3 3 export default { 4 4 propertyDefaults: { 5 5 title: "Không có tiêu đề", 6 - description: "Không có mô tả được cung cấp", 6 + description: "Không có mô tả", 7 7 }, 8 8 components: { 9 9 callout: { 10 - note: "Ghi Chú", 11 - abstract: "Tóm Tắt", 10 + note: "Ghi chú", 11 + abstract: "Tổng quan", 12 12 info: "Thông tin", 13 - todo: "Cần Làm", 14 - tip: "Gợi Ý", 15 - success: "Thành Công", 16 - question: "Nghi Vấn", 17 - warning: "Cảnh Báo", 18 - failure: "Thất Bại", 19 - danger: "Nguy Hiểm", 13 + todo: "Cần phải làm", 14 + tip: "Gợi ý", 15 + success: "Thành công", 16 + question: "Câu hỏi", 17 + warning: "Cảnh báo", 18 + failure: "Thất bại", 19 + danger: "Nguy hiểm", 20 20 bug: "Lỗi", 21 - example: "Ví Dụ", 22 - quote: "Trích Dẫn", 21 + example: "Ví dụ", 22 + quote: "Trích dẫn", 23 23 }, 24 24 backlinks: { 25 - title: "Liên Kết Ngược", 26 - noBacklinksFound: "Không có liên kết ngược được tìm thấy", 25 + title: "Liên kết ngược", 26 + noBacklinksFound: "Không có liên kết ngược nào", 27 27 }, 28 28 themeToggle: { 29 - lightMode: "Sáng", 30 - darkMode: "Tối", 29 + lightMode: "Chế độ sáng", 30 + darkMode: "Chế độ tối", 31 31 }, 32 32 readerMode: { 33 33 title: "Chế độ đọc", 34 34 }, 35 35 explorer: { 36 - title: "Trong bài này", 36 + title: "Nội dung", 37 37 }, 38 38 footer: { 39 - createdWith: "Được tạo bởi", 39 + createdWith: "Được tạo bằng", 40 40 }, 41 41 graph: { 42 - title: "Biểu Đồ", 42 + title: "Sơ đồ", 43 43 }, 44 44 recentNotes: { 45 - title: "Bài viết gần đây", 46 - seeRemainingMore: ({ remaining }) => `Xem ${remaining} thêm →`, 45 + title: "Ghi chú gần đây", 46 + seeRemainingMore: ({ remaining }) => `Xem thêm ${remaining} ghi chú →`, 47 47 }, 48 48 transcludes: { 49 - transcludeOf: ({ targetSlug }) => `Bao gồm ${targetSlug}`, 50 - linkToOriginal: "Liên Kết Gốc", 49 + transcludeOf: ({ targetSlug }) => `Trích dẫn toàn bộ từ ${targetSlug}`, 50 + linkToOriginal: "Xem trang gốc", 51 51 }, 52 52 search: { 53 - title: "Tìm Kiếm", 53 + title: "Tìm", 54 54 searchBarPlaceholder: "Tìm kiếm thông tin", 55 55 }, 56 56 tableOfContents: { 57 - title: "Bảng Nội Dung", 57 + title: "Mục lục", 58 58 }, 59 59 contentMeta: { 60 - readingTime: ({ minutes }) => `đọc ${minutes} phút`, 60 + readingTime: ({ minutes }) => `${minutes} phút đọc`, 61 61 }, 62 62 }, 63 63 pages: { 64 64 rss: { 65 - recentNotes: "Những bài gần đây", 66 - lastFewNotes: ({ count }) => `${count} Bài gần đây`, 65 + recentNotes: "Ghi chú gần đây", 66 + lastFewNotes: ({ count }) => `${count} Trang gần đây`, 67 67 }, 68 68 error: { 69 - title: "Không Tìm Thấy", 70 - notFound: "Trang này được bảo mật hoặc không tồn tại.", 71 - home: "Trở về trang chủ", 69 + title: "Không tìm thấy", 70 + notFound: "Trang này riêng tư hoặc không tồn tại.", 71 + home: "Về trang chủ", 72 72 }, 73 73 folderContent: { 74 - folder: "Thư Mục", 75 - itemsUnderFolder: ({ count }) => 76 - count === 1 ? "1 mục trong thư mục này." : `${count} mục trong thư mục này.`, 74 + folder: "Thư mục", 75 + itemsUnderFolder: ({ count }) => `Có ${count} trang trong thư mục này.`, 77 76 }, 78 77 tagContent: { 79 78 tag: "Thẻ", 80 - tagIndex: "Thẻ Mục Lục", 81 - itemsUnderTag: ({ count }) => 82 - count === 1 ? "1 mục gắn thẻ này." : `${count} mục gắn thẻ này.`, 83 - showingFirst: ({ count }) => `Hiển thị trước ${count} thẻ.`, 84 - totalTags: ({ count }) => `Tìm thấy ${count} thẻ tổng cộng.`, 79 + tagIndex: "Danh sách thẻ", 80 + itemsUnderTag: ({ count }) => `Có ${count} trang gắn thẻ này.`, 81 + showingFirst: ({ count }) => `Đang hiển thị ${count} trang đầu tiên.`, 82 + totalTags: ({ count }) => `Có tổng cộng ${count} thẻ.`, 85 83 }, 86 84 }, 87 85 } as const satisfies Translation
+13 -8
quartz/plugins/emitters/cname.ts
··· 1 - import { FilePath, joinSegments } from "../../util/path" 2 1 import { QuartzEmitterPlugin } from "../types" 3 - import fs from "fs" 2 + import { write } from "./helpers" 4 3 import { styleText } from "util" 4 + import { FullSlug } from "../../util/path" 5 5 6 6 export function extractDomainFromBaseUrl(baseUrl: string) { 7 7 const url = new URL(`https://${baseUrl}`) ··· 10 10 11 11 export const CNAME: QuartzEmitterPlugin = () => ({ 12 12 name: "CNAME", 13 - async emit({ argv, cfg }) { 14 - if (!cfg.configuration.baseUrl) { 13 + async emit(ctx) { 14 + if (!ctx.cfg.configuration.baseUrl) { 15 15 console.warn( 16 16 styleText("yellow", "CNAME emitter requires `baseUrl` to be set in your configuration"), 17 17 ) 18 18 return [] 19 19 } 20 - const path = joinSegments(argv.output, "CNAME") 21 - const content = extractDomainFromBaseUrl(cfg.configuration.baseUrl) 20 + const content = extractDomainFromBaseUrl(ctx.cfg.configuration.baseUrl) 22 21 if (!content) { 23 22 return [] 24 23 } 25 - await fs.promises.writeFile(path, content) 26 - return [path] as FilePath[] 24 + 25 + const path = await write({ 26 + ctx, 27 + content, 28 + slug: "CNAME" as FullSlug, 29 + ext: "", 30 + }) 31 + return [path] 27 32 }, 28 33 async *partialEmit() {}, 29 34 })
+23
quartz/plugins/emitters/componentResources.ts
··· 228 228 \` 229 229 document.head.appendChild(matomoScript); 230 230 `) 231 + } else if (cfg.analytics?.provider === "vercel") { 232 + /** 233 + * script from {@link https://vercel.com/docs/analytics/quickstart?framework=html#add-the-script-tag-to-your-site|Vercel Docs} 234 + */ 235 + componentResources.beforeDOMLoaded.push(` 236 + window.va = window.va || function () { (window.vaq = window.vaq || []).push(arguments); }; 237 + `) 238 + componentResources.afterDOMLoaded.push(` 239 + const vercelInsightsScript = document.createElement("script") 240 + vercelInsightsScript.src = "/_vercel/insights/script.js" 241 + vercelInsightsScript.defer = true 242 + document.head.appendChild(vercelInsightsScript) 243 + `) 244 + } else if (cfg.analytics?.provider === "rybbit") { 245 + componentResources.afterDOMLoaded.push(` 246 + const rybbitScript = document.createElement("script"); 247 + rybbitScript.src = "${cfg.analytics.host ?? "https://app.rybbit.io"}/api/script.js"; 248 + rybbitScript.setAttribute("data-site-id", "${cfg.analytics.siteId}"); 249 + rybbitScript.async = true; 250 + rybbitScript.defer = true; 251 + 252 + document.head.appendChild(rybbitScript); 253 + `) 231 254 } 232 255 233 256 if (cfg.enableSPA) {
+11 -2
quartz/plugins/transformers/citations.ts
··· 23 23 name: "Citations", 24 24 htmlPlugins(ctx) { 25 25 const plugins: PluggableList = [] 26 - 26 + // per default, rehype-citations only supports en-US 27 + // see: https://github.com/timlrx/rehype-citation/issues/12 28 + // in here there are multiple usable locales: 29 + // https://github.com/citation-style-language/locales 30 + // thus, we optimistically assume there is indeed an appropriate 31 + // locale available and simply create the lang url-string 32 + let lang: string = "en-US" 33 + if (ctx.cfg.configuration.locale !== "en-US") { 34 + lang = `https://raw.githubusercontent.com/citation-style-language/locales/refs/heads/master/locales-${ctx.cfg.configuration.locale}.xml` 35 + } 27 36 // Add rehype-citation to the list of plugins 28 37 plugins.push([ 29 38 rehypeCitation, ··· 32 41 suppressBibliography: opts.suppressBibliography, 33 42 linkCitations: opts.linkCitations, 34 43 csl: opts.csl, 35 - lang: ctx.cfg.configuration.locale ?? "en-US", 44 + lang, 36 45 }, 37 46 ]) 38 47
+2 -1
quartz/plugins/transformers/frontmatter.ts
··· 103 103 const created = coalesceAliases(data, ["created", "date"]) 104 104 if (created) { 105 105 data.created = created 106 - data.modified ||= created // if modified is not set, use created 107 106 } 108 107 109 108 const modified = coalesceAliases(data, [ ··· 113 112 "last-modified", 114 113 ]) 115 114 if (modified) data.modified = modified 115 + data.modified ||= created // if modified is not set, use created 116 + 116 117 const published = coalesceAliases(data, ["published", "publishDate", "date"]) 117 118 if (published) data.published = published 118 119
+16 -5
quartz/plugins/transformers/latex.ts
··· 17 17 typstOptions: TypstOptions 18 18 } 19 19 20 + // mathjax macros 21 + export type Args = boolean | number | string | null 20 22 interface MacroType { 21 - [key: string]: string 23 + [key: string]: string | Args[] 22 24 } 23 25 24 26 export const Latex: QuartzTransformerPlugin<Partial<Options>> = (opts) => { ··· 37 39 case "typst": { 38 40 return [[rehypeTypst, opts?.typstOptions ?? {}]] 39 41 } 42 + default: 40 43 case "mathjax": { 41 - return [[rehypeMathjax, { macros, ...(opts?.mathJaxOptions ?? {}) }]] 42 - } 43 - default: { 44 - return [[rehypeMathjax, { macros, ...(opts?.mathJaxOptions ?? {}) }]] 44 + return [ 45 + [ 46 + rehypeMathjax, 47 + { 48 + ...(opts?.mathJaxOptions ?? {}), 49 + tex: { 50 + ...(opts?.mathJaxOptions?.tex ?? {}), 51 + macros, 52 + }, 53 + }, 54 + ], 55 + ] 45 56 } 46 57 } 47 58 },
+5 -3
quartz/plugins/transformers/links.ts
··· 57 57 ) { 58 58 let dest = node.properties.href as RelativeURL 59 59 const classes = (node.properties.className ?? []) as string[] 60 - const isExternal = isAbsoluteUrl(dest) 60 + const isExternal = isAbsoluteUrl(dest, { httpOnly: false }) 61 61 classes.push(isExternal ? "external" : "internal") 62 62 63 63 if (isExternal && opts.externalLinkIcon) { ··· 99 99 } 100 100 101 101 // don't process external links or intra-document anchors 102 - const isInternal = !(isAbsoluteUrl(dest) || dest.startsWith("#")) 102 + const isInternal = !( 103 + isAbsoluteUrl(dest, { httpOnly: false }) || dest.startsWith("#") 104 + ) 103 105 if (isInternal) { 104 106 dest = node.properties.href = transformLink( 105 107 file.data.slug!, ··· 145 147 node.properties.loading = "lazy" 146 148 } 147 149 148 - if (!isAbsoluteUrl(node.properties.src)) { 150 + if (!isAbsoluteUrl(node.properties.src, { httpOnly: false })) { 149 151 let dest = node.properties.src as RelativeURL 150 152 dest = node.properties.src = transformLink( 151 153 file.data.slug!,
+1 -10
quartz/plugins/transformers/ofm.ts
··· 488 488 { 489 489 data: { hProperties: { className: ["callout-content"] }, hName: "div" }, 490 490 type: "blockquote", 491 - children: [ 492 - { 493 - data: { 494 - hProperties: { className: ["callout-content-inner"] }, 495 - hName: "div", 496 - }, 497 - type: "blockquote", 498 - children: [...calloutContent], 499 - }, 500 - ], 491 + children: [...calloutContent], 501 492 }, 502 493 ] 503 494 }
+6
quartz/plugins/transformers/oxhugofm.ts
··· 1 1 import { QuartzTransformerPlugin } from "../types" 2 + import rehypeRaw from "rehype-raw" 3 + import { PluggableList } from "unified" 2 4 3 5 export interface Options { 4 6 /** Replace {{ relref }} with quartz wikilinks []() */ ··· 101 103 }) 102 104 } 103 105 return src 106 + }, 107 + htmlPlugins() { 108 + const plugins: PluggableList = [rehypeRaw] 109 + return plugins 104 110 }, 105 111 } 106 112 }
+10 -16
quartz/styles/base.scss
··· 9 9 text-size-adjust: none; 10 10 overflow-x: hidden; 11 11 width: 100vw; 12 + 13 + @media all and ($mobile) { 14 + scroll-padding-top: 4rem; 15 + } 12 16 } 13 17 14 18 body { ··· 41 45 .katex, 42 46 .math, 43 47 .typst-doc, 44 - .typst-doc * { 48 + g[class~="typst-text"] { 45 49 color: var(--darkgray); 46 50 fill: var(--darkgray); 47 - hyphens: auto; 51 + overflow-wrap: break-word; 52 + text-wrap: pretty; 48 53 } 49 54 50 - p, 51 - ul, 52 - text, 53 - a, 54 - li, 55 - ol, 56 - ul, 57 - .katex, 58 - .math, 59 - .typst-doc, 60 - .typst-doc * { 61 - overflow-wrap: anywhere; 62 - /* tr and td removed from list of selectors for overflow-wrap, allowing them to use default 'normal' property value */ 55 + path[class~="typst-shape"] { 56 + stroke: var(--darkgray); 63 57 } 64 58 65 59 .math { ··· 225 219 } 226 220 227 221 & .sidebar { 228 - gap: 2rem; 222 + gap: 1.2rem; 229 223 top: 0; 230 224 box-sizing: border-box; 231 225 padding: $topSpacing 2rem 2rem 2rem;
+26 -9
quartz/styles/callouts.scss
··· 11 11 12 12 & > .callout-content { 13 13 display: grid; 14 - transition: grid-template-rows 0.3s ease; 14 + transition: grid-template-rows 0.1s cubic-bezier(0.02, 0.01, 0.47, 1); 15 + overflow: hidden; 15 16 16 - & > .callout-content-inner { 17 - overflow: hidden; 18 - 19 - & > :first-child { 20 - margin-top: 0; 21 - } 17 + & > :first-child { 18 + margin-top: 0; 22 19 } 23 20 } 24 21 ··· 121 118 --callout-icon: var(--callout-icon-quote); 122 119 } 123 120 124 - &.is-collapsed > .callout-title > .fold-callout-icon { 125 - transform: rotateZ(-90deg); 121 + &.is-collapsed { 122 + & > .callout-title > .fold-callout-icon { 123 + transform: rotateZ(-90deg); 124 + } 125 + 126 + .callout-content { 127 + & > * { 128 + transition: 129 + height 0.1s cubic-bezier(0.02, 0.01, 0.47, 1), 130 + margin 0.1s cubic-bezier(0.02, 0.01, 0.47, 1), 131 + padding 0.1s cubic-bezier(0.02, 0.01, 0.47, 1); 132 + overflow-y: clip; 133 + height: 0; 134 + margin-bottom: 0; 135 + margin-top: 0; 136 + padding-bottom: 0; 137 + padding-top: 0; 138 + } 139 + & > :first-child { 140 + margin-top: -1rem; 141 + } 142 + } 126 143 } 127 144 } 128 145
+4 -3
quartz/util/resources.tsx
··· 26 26 export function JSResourceToScriptElement(resource: JSResource, preserve?: boolean): JSX.Element { 27 27 const scriptType = resource.moduleType ?? "application/javascript" 28 28 const spaPreserve = preserve ?? resource.spaPreserve 29 + 29 30 if (resource.contentType === "external") { 30 31 return ( 31 - <script key={resource.src} src={resource.src} type={scriptType} spa-preserve={spaPreserve} /> 32 + <script key={resource.src} src={resource.src} type={scriptType} data-persist={spaPreserve} /> 32 33 ) 33 34 } else { 34 35 const content = resource.script ··· 36 37 <script 37 38 key={randomUUID()} 38 39 type={scriptType} 39 - spa-preserve={spaPreserve} 40 + data-persist={spaPreserve} 40 41 dangerouslySetInnerHTML={{ __html: content }} 41 42 ></script> 42 43 ) ··· 54 55 href={resource.content} 55 56 rel="stylesheet" 56 57 type="text/css" 57 - spa-preserve={spaPreserve} 58 + data-persist={spaPreserve} 58 59 /> 59 60 ) 60 61 }