├── .editorconfig
├── .github
└── workflows
│ ├── ci.yml
│ ├── publish-crates.yml
│ ├── release.yml
│ └── web.yml
├── .gitignore
├── CHANGELOG.md
├── Cargo.lock
├── Cargo.toml
├── LICENSE-APACHE
├── LICENSE-MIT
├── README.md
├── SECURITY.md
├── assets
└── favicon.ico
├── build.rs
├── docs
├── .gitignore
├── book.toml
└── src
│ ├── SUMMARY.md
│ ├── assets.md
│ ├── building.md
│ ├── cli.md
│ ├── cli
│ ├── build.md
│ ├── dev.md
│ ├── generate.md
│ └── serve.md
│ ├── configuration.md
│ ├── configuration
│ ├── additional-pages.md
│ ├── analytics.md
│ ├── artifacts.md
│ ├── changelog.md
│ ├── funding.md
│ ├── mdbook.md
│ ├── reference.md
│ ├── social.md
│ ├── theme.md
│ ├── theme
│ │ └── previews.md
│ └── workspaces.md
│ ├── contributing.md
│ ├── hosting.md
│ ├── images
│ ├── artifacts-pkgman.png
│ ├── quickstart-1.png
│ ├── quickstart-2.png
│ └── themes
│ │ ├── axodark.png
│ │ ├── axolight.png
│ │ ├── cupcake.png
│ │ ├── dark.png
│ │ ├── hacker.png
│ │ ├── light.jpg
│ │ └── light.png
│ ├── install.md
│ ├── introduction.md
│ ├── quickstart.md
│ └── tips.md
├── flake.lock
├── flake.nix
├── generate-css
├── Cargo.toml
└── src
│ ├── errors.rs
│ └── lib.rs
├── oranda-css
├── css
│ ├── base.css
│ ├── buttons.css
│ ├── components.css
│ ├── main.css
│ ├── pages
│ │ ├── artifacts.css
│ │ ├── changelog.css
│ │ └── workspace_index.css
│ ├── themes
│ │ ├── axo.css
│ │ ├── cupcake.css
│ │ └── hacker.css
│ ├── utilities.css
│ └── variables.css
├── mdbook-theme
│ ├── book.js
│ ├── css
│ │ ├── chrome.css
│ │ ├── general.css
│ │ └── variables.css
│ ├── fonts
│ │ └── fonts.css
│ ├── highlight-js-themes
│ │ └── base16-material.css
│ ├── index.hbs
│ └── oranda-themes
│ │ ├── axo.css
│ │ ├── cupcake.css
│ │ ├── default.css
│ │ └── hacker.css
├── package-lock.json
├── package.json
└── tailwind.config.js
├── oranda.json
├── rust-toolchain.toml
├── src
├── commands
│ ├── build.rs
│ ├── dev.rs
│ ├── generate.rs
│ ├── mod.rs
│ ├── print.rs
│ └── serve.rs
├── config
│ ├── axoproject.rs
│ ├── builds.rs
│ ├── components
│ │ ├── artifacts
│ │ │ ├── mod.rs
│ │ │ └── package_managers.rs
│ │ ├── changelog.rs
│ │ ├── funding.rs
│ │ ├── mdbooks.rs
│ │ └── mod.rs
│ ├── marketing
│ │ ├── analytics.rs
│ │ ├── mod.rs
│ │ └── social.rs
│ ├── mod.rs
│ ├── oranda_config.rs
│ ├── project.rs
│ ├── style.rs
│ └── workspace.rs
├── data
│ ├── artifacts
│ │ ├── inference.rs
│ │ └── mod.rs
│ ├── axodotdev
│ │ └── mod.rs
│ ├── cargo_dist.rs
│ ├── funding.rs
│ ├── github
│ │ └── mod.rs
│ ├── mod.rs
│ ├── release.rs
│ └── workspaces.rs
├── errors.rs
├── formatter.rs
├── generate.rs
├── lib.rs
├── main.rs
├── paths.rs
└── site
│ ├── artifacts
│ └── mod.rs
│ ├── changelog.rs
│ ├── funding
│ └── mod.rs
│ ├── layout
│ ├── css.rs
│ ├── header.rs
│ ├── javascript
│ │ ├── analytics.rs
│ │ ├── artifacts.js
│ │ └── mod.rs
│ └── mod.rs
│ ├── link.rs
│ ├── markdown
│ ├── mod.rs
│ └── syntax_highlight
│ │ ├── MaterialTheme.tmTheme
│ │ ├── TOML.sublime-syntax
│ │ ├── TypeScript.sublime-syntax
│ │ ├── mod.rs
│ │ ├── syntax_themes.rs
│ │ └── syntax_themes.themedump
│ ├── mdbook.rs
│ ├── mod.rs
│ ├── oranda_theme.rs
│ ├── page
│ ├── mod.rs
│ └── source.rs
│ ├── rss.rs
│ ├── templates.rs
│ └── workspace_index.rs
├── templates
├── generate
│ └── web.yml.j2
└── site
│ ├── artifacts.html.j2
│ ├── changelog_index.html.j2
│ ├── changelog_single.html.j2
│ ├── funding.html.j2
│ ├── icons
│ ├── copy.html.j2
│ ├── date.html.j2
│ ├── github.html.j2
│ ├── kofi.html.j2
│ ├── liberapay.html.j2
│ ├── opencollective.html.j2
│ ├── patreon.html.j2
│ ├── rss.html.j2
│ ├── tag.html.j2
│ ├── tidelift.html.j2
│ └── web.html.j2
│ ├── includes
│ ├── changelog_release.html.j2
│ ├── install_widget.html.j2
│ ├── installer_run.html.j2
│ └── nav.html.j2
│ ├── index.html.j2
│ ├── layout.html.j2
│ ├── markdown_page.html.j2
│ └── workspace_index
│ ├── index.html.j2
│ └── layout.html.j2
└── tests
├── autodetect
├── fixtures
│ ├── mod.rs
│ └── project_config.rs
└── mod.rs
├── integration
├── fixtures
│ ├── mod.rs
│ └── oranda_config.rs
└── mod.rs
├── integration_gallery
├── command.rs
├── errors.rs
├── mod.rs
├── oranda_impl.rs
└── repo.rs
├── mod.rs
├── snapshots
├── gal_akaikatana-public.snap
├── gal_axolotlsay-public.snap
├── gal_oranda-public.snap
├── gal_oranda_empty-public.snap
└── gal_oranda_inference-public.snap
└── utils
├── mod.rs
├── snapshots.rs
└── tokio_utils.rs
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 | insert_final_newline = true
6 | indent_size = 2
7 | indent_style = space
8 |
9 | [*.rs]
10 | indent_size = 4
11 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: Rust
2 |
3 | on:
4 | pull_request:
5 | push:
6 | branches:
7 | - main
8 | schedule:
9 | - cron: '11 7 * * 1,4'
10 |
11 | env:
12 | RUSTFLAGS: -Dwarnings
13 |
14 | jobs:
15 | fmt:
16 | runs-on: ubuntu-latest
17 | steps:
18 | - uses: actions/checkout@v3
19 | - uses: dtolnay/rust-toolchain@stable
20 | with:
21 | components: rustfmt
22 | - name: Run cargo fmt
23 | run: |
24 | cargo fmt --all -- --check
25 | clippy:
26 | runs-on: ubuntu-latest
27 | steps:
28 | - uses: actions/checkout@v3
29 | - uses: dtolnay/rust-toolchain@stable
30 | with:
31 | components: clippy
32 | - name: Run cargo clippy
33 | run: |
34 | cargo clippy --workspace --tests --examples
35 | docs:
36 | runs-on: ubuntu-latest
37 | env:
38 | RUSTDOCFLAGS: -Dwarnings
39 | steps:
40 | - uses: actions/checkout@v3
41 | - uses: dtolnay/rust-toolchain@stable
42 | - name: Run cargo doc
43 | run: |
44 | cargo doc --workspace --no-deps
45 | test:
46 | runs-on: ${{ matrix.os }}
47 | strategy:
48 | matrix:
49 | os: [ubuntu-latest, windows-latest, macOS-latest]
50 | steps:
51 | - uses: actions/checkout@v3
52 | - uses: dtolnay/rust-toolchain@stable
53 | - uses: swatinem/rust-cache@v2
54 | - name: Run cargo test
55 | run: |
56 | cargo test --workspace
57 |
--------------------------------------------------------------------------------
/.github/workflows/publish-crates.yml:
--------------------------------------------------------------------------------
1 | # Publishes a release to crates.io
2 | #
3 | # To trigger this:
4 | #
5 | # - go to Actions > PublishRelease
6 | # - click the Run Workflow dropdown in the top-right
7 | # - enter the tag of the release as “Release Tag” (e.g. v0.3.18)
8 | name: PublishCrates
9 |
10 | on:
11 | workflow_call:
12 | inputs:
13 | plan:
14 | required: true
15 | type: string
16 |
17 | jobs:
18 | # publish the current repo state to crates.io
19 | cargo-publish:
20 | runs-on: ubuntu-latest
21 | steps:
22 | - name: Checkout sources
23 | uses: actions/checkout@v4
24 | with:
25 | submodules: recursive
26 | - run: cargo publish -p oranda-generate-css --token ${CRATES_TOKEN}
27 | env:
28 | CRATES_TOKEN: ${{ secrets.CRATES_TOKEN }}
29 | - run: cargo publish -p oranda --token ${CRATES_TOKEN}
30 | env:
31 | CRATES_TOKEN: ${{ secrets.CRATES_TOKEN }}
32 |
--------------------------------------------------------------------------------
/.github/workflows/web.yml:
--------------------------------------------------------------------------------
1 | # Workflow to build your docs with oranda (and mdbook)
2 | # and deploy them to Github Pages
3 | name: Web
4 |
5 | # We're going to push to the gh-pages branch, so we need that permission
6 | permissions:
7 | contents: write
8 |
9 | # What situations do we want to build docs in?
10 | # All of these work independently and can be removed / commented out
11 | # if you don't want oranda/mdbook running in that situation
12 | on:
13 | # Check that a PR didn't break docs!
14 | #
15 | # Note that the "Deploy to Github Pages" step won't run in this mode,
16 | # so this won't have any side-effects. But it will tell you if a PR
17 | # completely broke oranda/mdbook. Sadly we don't provide previews (yet)!
18 | pull_request:
19 |
20 | # Whenever something gets pushed to main, update the docs!
21 | # This is great for getting docs changes live without cutting a full release.
22 | #
23 | # Note that if you're using cargo-dist, this will "race" the Release workflow
24 | # that actually builds the Github Release that oranda tries to read (and
25 | # this will almost certainly complete first). As a result you will publish
26 | # docs for the latest commit but the oranda landing page won't know about
27 | # the latest release. The workflow_run trigger below will properly wait for
28 | # cargo-dist, and so this half-published state will only last for ~10 minutes.
29 | #
30 | # If you only want docs to update with releases, disable this, or change it to
31 | # a "release" branch. You can, of course, also manually trigger a workflow run
32 | # when you want the docs to update.
33 | push:
34 | branches:
35 | - main
36 |
37 | # Whenever a workflow called "Release" completes, update the docs!
38 | #
39 | # If you're using cargo-dist, this is recommended, as it will ensure that
40 | # oranda always sees the latest release right when it's available. Note
41 | # however that Github's UI is wonky when you use workflow_run, and won't
42 | # show this workflow as part of any commit. You have to go to the "actions"
43 | # tab for your repo to see this one running (the gh-pages deploy will also
44 | # only show up there).
45 | workflow_run:
46 | workflows: [ "Release" ]
47 | types:
48 | - completed
49 |
50 | # Alright, let's do it!
51 | jobs:
52 | web:
53 | name: Build and deploy site and docs
54 | runs-on: ubuntu-latest
55 | steps:
56 | # Setup
57 | - uses: actions/checkout@v3
58 | with:
59 | fetch-depth: 0
60 | - uses: dtolnay/rust-toolchain@stable
61 | - uses: swatinem/rust-cache@v2
62 |
63 | # If you use any mdbook plugins, here's the place to install them!
64 |
65 | # Install and run oranda (and mdbook)
66 | # This will write all output to ./public/ (including copying mdbook's output to there)
67 | - name: Install and run oranda
68 | run: |
69 | curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/oranda/releases/download/v0.4.0/oranda-installer.sh | sh
70 | oranda build
71 |
72 | - name: Prepare HTML for link checking
73 | # untitaker/hyperlink supports no site prefixes, move entire site into
74 | # a subfolder
75 | run: mkdir /tmp/public/ && cp -R public /tmp/public/oranda
76 |
77 | - name: Check HTML for broken internal links
78 | uses: untitaker/hyperlink@0.1.29
79 | with:
80 | args: /tmp/public/ --sources docs/
81 |
82 | # Deploy to our gh-pages branch (creating it if it doesn't exist)
83 | # the "public" dir that oranda made above will become the root dir
84 | # of this branch.
85 | #
86 | # Note that once the gh-pages branch exists, you must
87 | # go into repo's settings > pages and set "deploy from branch: gh-pages"
88 | # the other defaults work fine.
89 | - name: Deploy to Github Pages
90 | uses: JamesIves/github-pages-deploy-action@v4.4.1
91 | # ONLY if we're on main (so no PRs or feature branches allowed!)
92 | if: ${{ github.ref == 'refs/heads/main' }}
93 | with:
94 | branch: gh-pages
95 | # Gotta tell the action where to find oranda's output
96 | folder: public
97 | token: ${{ secrets.GITHUB_TOKEN }}
98 | single-commit: true
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | public/
3 | oranda-debug.log
4 | # banish macos to the depths of hell
5 | .DS_Store
6 | .idea/
7 |
8 | # oranda-css
9 | oranda-css/dist
10 | oranda-css/.yarn
11 | # weirdo yarn feature that dumps files everywhere
12 | oranda-css/.pnp*
13 | node_modules
14 |
15 | # nix
16 | result/
17 |
--------------------------------------------------------------------------------
/LICENSE-MIT:
--------------------------------------------------------------------------------
1 | Copyright (c) 2024 Axo Developer Co.
2 |
3 | Permission is hereby granted, free of charge, to any
4 | person obtaining a copy of this software and associated
5 | documentation files (the "Software"), to deal in the
6 | Software without restriction, including without
7 | limitation the rights to use, copy, modify, merge,
8 | publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software
10 | is furnished to do so, subject to the following
11 | conditions:
12 |
13 | The above copyright notice and this permission notice
14 | shall be included in all copies or substantial portions
15 | of the Software.
16 |
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 | DEALINGS IN THE SOFTWARE.
26 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # oranda
4 |
5 |
6 |
7 | > 🎁 generate beautiful landing pages for your projects
8 |
9 | [](https://crates.io/crates/oranda)
10 | [](https://github.com/axodotdev/oranda/actions/workflows/ci.yml)
11 | [](https://github.com/axodotdev/oranda/actions/workflows/release.yml)
12 | [](https://github.com/axodotdev/oranda/actions/workflows/web.yml)
13 |
14 |
15 | `oranda` is an opinionated static-site generator that is designed for developers
16 | who are publishing projects and would like a website but don't want to build
17 | one from scratch.
18 |
19 |
20 |
21 | `oranda` uses `oranda` so you can checkout a live example [here][website]!
22 |
23 | ## Installation
24 |
25 | To install `oranda`, please visit the [`oranda` website][website]- which is generated by
26 | `oranda`!
27 |
28 | [website]: https://axodotdev.github.io/oranda
29 |
30 |
31 |
32 | ## Quickstart
33 |
34 | ```sh
35 | # build your site
36 | > oranda build
37 |
38 | # build your site and start a server that rebuilds on file changes
39 | > oranda dev
40 | ```
41 |
42 | Here's an animated demo:
43 |
44 | 
45 |
46 | ## Configuration
47 |
48 | First of all: `oranda` is designed to work without a configuration file. For a lot of projects,
49 | you can likely just run `oranda build` and get a site that contains a couple of things that
50 | `oranda` was automatically able to glean about your project. That being said, it also supports
51 | a configuration file that allows you to tweak many things about oranda's behaviour.
52 |
53 | If you'd like to configure `oranda`, place an `oranda.json` file in the root of
54 | your project and fill it with the configuration you'd like. Check out the [docs]
55 | to learn more about your configuration options!
56 |
57 | [docs]: https://opensource.axo.dev/oranda/book/configuration.html
58 |
59 | ## Installers: integrating with `cargo-dist`
60 |
61 | `oranda` has first-class integration with [`cargo-dist`], a tool that builds
62 | distributable artifacts for your Rust applications. If you have `cargo-dist`
63 | configured in your project correctly, `oranda` will be able to automatically
64 | tell. Benefits of integrating with `cargo-dist` include:
65 |
66 | - Installer scripts: `cargo-dist` can generate one-line installer scripts, which
67 | `oranda` will display in your generated page
68 | - Guaranteed platform support: `oranda` tries to support as many platforms as it can,
69 | but if you build something with `cargo-dist`, we guarantee it'll show up correctly
70 |
71 | [`cargo-dist`]: https://github.com/axodotdev/cargo-dist
72 |
73 | ## Contributing
74 |
75 | Feel free to open a new issue or pull request if you notice something off or have a new feature
76 | request! We sometimes tag issues with [good first issue] for issues that we think would make
77 | a good learning experience for new contributors.
78 |
79 | For local development on oranda, we also have a [special docs page][contributing-docs] with some tips.
80 |
81 | [good first issue]: https://github.com/axodotdev/oranda/labels/good%20first%20issue
82 | [contributing-docs]: https://opensource.axo.dev/oranda/book/contributing.html
83 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | Axo Developer Co. takes the security of our software products and services seriously. If you believe you have found a security vulnerability in this open source repository, please report the issue to us directly using GitHub private vulnerability reporting or email ashley@axo.dev. If you aren't sure you have found a security vulnerability but have a suspicion or concern, feel free to message anyways; we prefer over-communication :)
2 |
3 | Please do not report security vulnerabilities publicly, such as via GitHub issues, Twitter, or other social media.
4 |
5 | Thanks for helping make software safe for everyone!
6 |
--------------------------------------------------------------------------------
/assets/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axodotdev/oranda/53bdbda1835307ffe11348d4eeb517ccf8a0d2ff/assets/favicon.ico
--------------------------------------------------------------------------------
/build.rs:
--------------------------------------------------------------------------------
1 | use oranda_generate_css::default_css_output_dir;
2 | use std::path::PathBuf;
3 |
4 | fn main() {
5 | // Step 1: Has the user set ORANDA_USE_TAILWIND_BINARY? If so, we set a cfg attribute to build
6 | // the CSS on-demand in the main binary. This is intended to be used by contributors for a faster local
7 | // development cycle.
8 | // Alternatively, a packager can set this for a release build to prebuild the CSS using the
9 | // Tailwind binary.
10 | if std::env::var("ORANDA_USE_TAILWIND_BINARY").is_ok() || cfg!(feature = "build-with-tailwind")
11 | {
12 | if cfg!(debug_assertions) {
13 | println!("cargo:rustc-cfg=css=\"tailwind\"");
14 | } else {
15 | let runtime = tokio::runtime::Builder::new_multi_thread()
16 | .worker_threads(1)
17 | .max_blocking_threads(128)
18 | .enable_all()
19 | .build()
20 | .expect("Initializing Tokio runtime failed");
21 | let _guard = runtime.enter();
22 | oranda_generate_css::build_css(&default_css_output_dir()).unwrap();
23 | println!("cargo:rustc-cfg=css=\"file\"");
24 | }
25 | } else {
26 | // Step 2: Does a CSS file exist at oranda-css/dist/oranda.css? If so, assume the user
27 | // has precompiled oranda CSS, which will cause the main oranda binary to include this
28 | // file.
29 | let path = PathBuf::from("./oranda-css/dist/oranda.css");
30 | if path.exists() {
31 | println!("cargo:rustc-cfg=css=\"file\"");
32 | } else {
33 | // Step 3: The user doesn't have the CSS locally and doesn't want to compile it from
34 | // scratch. In this case, we let the main binary know that it should always pull CSS
35 | // from GitHub releases. This behaviour is intended as a fallback for `cargo install`
36 | // builds.
37 | println!("cargo:rustc-cfg=css=\"fetch\"");
38 | println!("cargo:warning=This build of oranda will pull CSS directly from GitHub releases! This is probably not what you want.");
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/docs/.gitignore:
--------------------------------------------------------------------------------
1 | book
2 |
--------------------------------------------------------------------------------
/docs/book.toml:
--------------------------------------------------------------------------------
1 | [book]
2 | authors = ["axodotdev"]
3 | language = "en"
4 | multilingual = false
5 | src = "src"
6 |
7 | [output.markdown]
8 | [output.html]
9 | edit-url-template = "https://github.com/axodotdev/oranda/edit/main/docs/{path}"
10 | search.use-boolean-and = true
11 |
--------------------------------------------------------------------------------
/docs/src/SUMMARY.md:
--------------------------------------------------------------------------------
1 | # Summary
2 |
3 | - [Introduction](./introduction.md)
4 | - [Install](./install.md)
5 | - [Quickstart](./quickstart.md)
6 | - [Hosting](./hosting.md)
7 | - [Assets](./assets.md)
8 | - [Command Line](./cli.md)
9 | - [build](./cli/build.md)
10 | - [serve](./cli/serve.md)
11 | - [dev](./cli/dev.md)
12 | - [generate](./cli/generate.md)
13 | - [Tips and Tricks](./tips.md)
14 | - [Configuration](./configuration.md)
15 | - [Reference](./configuration/reference.md)
16 | - [Artifacts & `cargo-dist`](./configuration/artifacts.md)
17 | - [Additional Pages](./configuration/additional-pages.md)
18 | - [Analytics](./configuration/analytics.md)
19 | - [Changelogs](./configuration/changelog.md)
20 | - [`mdbook` support](./configuration/mdbook.md)
21 | - [Social](./configuration/social.md)
22 | - [Theming](./configuration/theme.md)
23 | - [Theme Previews](./configuration/theme/previews.md)
24 | - [Funding](./configuration/funding.md)
25 | - [Workspaces](./configuration/workspaces.md)
26 | - [Contributing](./contributing.md)
27 | - [Building oranda](./building.md)
28 |
--------------------------------------------------------------------------------
/docs/src/assets.md:
--------------------------------------------------------------------------------
1 | # Adding static assets
2 |
3 | If you reference static assets in your Markdown, you'll need to place them all inside a directory at the same level as
4 | your project manifest file called `static`. This is because Oranda currently doesn't know about each indidivual asset,
5 | and instead just copies the folder where they're contained.
6 |
7 | In your Markdown, you'll need to refer to the assets in this directory. For example:
8 |
9 | ```md
10 | 
11 | ```
12 |
13 | If you want to use a custom-named directory you can configure this in your `oranda.json`, like so:
14 |
15 | ```json
16 | {
17 | "build": {
18 | "static_dir": "assets"
19 | }
20 | }
21 | ```
22 |
23 | In this case the `assets` directory will be used instead of the default `static` directory.
24 |
--------------------------------------------------------------------------------
/docs/src/building.md:
--------------------------------------------------------------------------------
1 | # Building oranda
2 |
3 | In case you're interested in building oranda from scratch, be it for packaging for a software distribution or
4 | something different, here's some information on things to keep in mind!
5 |
6 | ## Basic setup
7 |
8 | To build oranda from source, you'll need the following:
9 |
10 | - Stable Rust toolchain
11 | - _Optionally_, if you want to pre-compile CSS, Node & npm
12 | - `cargo-dist`, if you want to opt into the same release flags we use in our CI
13 |
14 | oranda can be built from source by running `cargo build --release` (not including the CSS - read below for more).
15 | Since we use `cargo-dist` to build our binaries for the official distribution of oranda, you may want to
16 | run `cargo dist build` instead. This
17 | ensures that you generate the same builds that we do.
18 |
19 | ### The trouble with CSS
20 |
21 | oranda includes some CSS to style pages - we call this oranda-css. This CSS uses [TailwindCSS](https://tailwindcss.com),
22 | and therefore needs to be compiled before it can be included. To figure out _how_ to build and/or include this CSS,
23 | we use a `build.rs` file. This file sets configuration variables for one of these cases:
24 |
25 | > Added in version 0.4.0.
26 |
27 | - You have the environment variable `ORANDA_USE_TAILWIND_BINARY` set. This causes oranda to attempt to download a
28 | `tailwindcss` binary, and build using that.
29 | - If you run this in a development build, the CSS will get built at runtime.
30 | - If you run it in a release build, the CSS will be built as part of `build.rs` and embedded into the resulting
31 | binary.
32 | - If a file exists at `oranda-css/dist/oranda.css`, oranda will inline that file and use it as its _current_ version,
33 | meaning it'll insert it into sites unless the user overrides it with a different version. This means you can prebuild
34 | the CSS using npm, and then run `cargo build` to let it be picked up.
35 | - If neither of these conditions are true, a Cargo build will produce a binary that'll always fetch the CSS from our
36 | GitHub releases. Stricly seen, this is a **worse version of oranda** (because it has to do extra CSS requests), so
37 | we suggest not distributing a binary that was built this way. You can check if a binary was built this way by looking
38 | out for the following log
39 | line: `warning: This build of oranda will pull CSS directly from GitHub releases! This is probably not what you want.`
40 |
41 | > For `cargo install` users: Your `oranda` binary is of the third type in the list above. This is unfortunately a
42 | > shortcoming of Cargo's build pipeline, but if you're fine with using a slightly slower version of
43 | > oranda, `cargo install`
44 | > works fine. If you want a regular binary, check the [install page](./install.md)!
45 |
46 | If you're distributing binaries anywhere, you can either use the Node toolchain, or the Tailwind binary
47 | using `ORANDA_USE_TAILWIND_BINARY`, depending on which is easier/more conformant in your build environment.
48 |
49 | ```sh
50 | # either:
51 | ORANDA_USE_TAILWIND_BINARY=true cargo dist build
52 |
53 | # or:
54 | cd oranda-css
55 | npm run build
56 | cd ..
57 | cargo dist build
58 | ```
59 |
60 |
--------------------------------------------------------------------------------
/docs/src/cli.md:
--------------------------------------------------------------------------------
1 | # Command Line
2 |
3 | Oranda currently has four subcommands that work in similar, but nuanced ways.
4 |
5 | - [`build`](./cli/build.md)
6 | - [`serve`](./cli/serve.md)
7 | - [`dev`](./cli/dev.md)
8 | - [`generate`](./cli/generate.md)
9 |
10 | Oranda supports some common options on each command:
11 |
12 | - `--verbose`. This controls the verbosity level for logs.
13 | - `--output-format`. If you want JSON for processing it with a machine, this is where you'd toggle it.
14 |
--------------------------------------------------------------------------------
/docs/src/cli/build.md:
--------------------------------------------------------------------------------
1 | # `oranda build`
2 |
3 | This command builds your oranda site. You can pass the `--json-only` flag in order for oranda to _only_ build an
4 | `artifacts.json` file that can be read by other tools (or websites) for integration purposes. You can also specify
5 | `--config-path` if your configuration file is not `./oranda.json`, but oranda will still look for an
6 | `oranda-workspace.json` in the current directory.
7 |
--------------------------------------------------------------------------------
/docs/src/cli/dev.md:
--------------------------------------------------------------------------------
1 | # `oranda dev`
2 |
3 | This command basically combined `oranda build` and `oranda serve`, with the added benefit of watching for changes
4 | and recompiling automatically. When you launch, what happens is this:
5 |
6 | 1. Oranda builds your site (unless you told it not to)
7 | 2. Oranda launches a server similar to `oranda serve`
8 | 3. Oranda starts watching its relevant files for changes, and will rerun the build process when something changes
9 |
10 | Oranda's build can have a lot of side-effects (reading/writing files, but also talking to the GitHub API), and as
11 | such, we have to take care to only run the build process when _relevant_ files change. These files are:
12 |
13 | - Your project manifest files (`Cargo.toml`, `package.json`)
14 | - Your oranda configuration file
15 | - Any mdbook source files you may have
16 | - Your readme, and additional files specified in the configuration
17 | - Files immediately relevant to certain components oranda renders (funding, for example)
18 | - Any other paths you give it using `--include-paths`
19 |
20 | This command also supports several options:
21 |
22 | - `--port` to set a custom port for the file server
23 | - `--config-path` to specify a custom path for your oranda config (but oranda will still look for an `oranda-workspace.json`) in your current directory).
24 | - `--no-first-build` to skip the first step mentioned above where oranda builds your site before starting the watch process
25 | - `-i`, `--include-paths` to specify custom paths for oranda to watch
26 |
--------------------------------------------------------------------------------
/docs/src/cli/generate.md:
--------------------------------------------------------------------------------
1 | # `oranda generate`
2 |
3 | > Added in version 0.4.0.
4 |
5 | This command generates files useful for working with oranda. Currently, it only supports one subcommand.
6 |
7 | ## `oranda generate ci`
8 |
9 | Generates a CI file that deploys your site. Supports the following options:
10 |
11 | - `-o, --output-path`: Specify a path for the file to be written to. Default: `.github/workflows/web.yml`
12 | - `-s, --site-path`: Specify a path to your oranda site, in case it's in a subdirectory to your repository
13 | - `--ci`: Specify which CI platform to use. Currently only accepts and defaults to `github`, which deploys to GitHub
14 | Pages using GitHub Actions.
15 |
16 | You can rerun this command to update the CI file based on what we currently recommend as the best workflow, but also
17 | to, for example, update the oranda version that the CI uses (which will always be the oranda version you run
18 | `generate` with).
19 |
--------------------------------------------------------------------------------
/docs/src/cli/serve.md:
--------------------------------------------------------------------------------
1 | # `oranda serve`
2 |
3 | This command launches a small [`axum`][axum]-powered server that serves your generated oranda site.
4 |
5 | Importantly, this does **not** build your site for you. If it can't find a build in the `public/` directory,
6 | it will error and exit. You can set the port for the server to be launched using the `--port` option.
7 |
8 | [axum]: https://cra.tw/axum
9 |
--------------------------------------------------------------------------------
/docs/src/configuration.md:
--------------------------------------------------------------------------------
1 | # Configuration
2 |
3 |
4 | **`oranda` is designed to work with no configuration**- for projects with a
5 | `package.json` or `Cargo.toml`, `oranda` will grab the project metadata it needs
6 | from your project manifest file. It can also infer a lot of the things it wants to
7 | render from your already existing environment.
8 |
9 | If you project has both a `Cargo.toml` and a `package.json` we recommend defining
10 | project metadata fields like `name` in your `oranda.json`.
11 |
12 | ## Manifest file: `oranda.json`
13 |
14 | If you'd like to customize your project you can do so in a `oranda.json` file.
15 |
16 | For example:
17 |
18 | ```json
19 | {
20 | "build": {
21 | "path_prefix": "oranda"
22 | },
23 | "styles": {
24 | "theme": "axodark",
25 | "favicon": "https://www.axo.dev/favicon.ico"
26 | },
27 | "marketing": {
28 | "social": {
29 | "image": "https://www.axo.dev/meta_small.jpeg",
30 | "image_alt": "axo",
31 | "twitter_account": "@axodotdev"
32 | },
33 | "analytics": {
34 | "plausible": {
35 | "domain": "opensource.axo.dev"
36 | }
37 | }
38 | },
39 | "components": {
40 | "changelog": true,
41 | "artifacts": {
42 | "package_managers": {
43 | "preferred": {
44 | "npm": "npm install @axodotdev/oranda --save-dev",
45 | "cargo": "cargo install oranda --locked --profile=dist"
46 | },
47 | "additional": {
48 | "npx": "npx @axodotdev/oranda",
49 | "binstall": "cargo binstall oranda",
50 | "nix-env": "nix-env -i oranda",
51 | "nix flake": "nix profile install github:axodotdev/oranda"
52 | }
53 | }
54 | }
55 | }
56 | }
57 | ```
58 |
59 | > **NOTE:** All paths in `oranda.json` are relative to the `oranda.json` file.
60 |
61 | **See the [configuration reference](./configuration/reference.md) for a detailed explanations of all options!**
62 |
63 | ## Workspace manifest file: `oranda-workspace.json`
64 |
65 | > Added in version 0.3.0.
66 |
67 | oranda supports building multiple sites at once (referred to as building in a "workspace"). To control this behavior,
68 | you can create a `oranda-workspace.json` file inside your workspace root. Running an oranda command will pick up this
69 | file, and build the workspace members accordingly.
70 |
71 | The workspace file supports all other oranda config keys, which will be passed down to each workspace members.
72 |
73 | [Read more about workspaces](configuration/workspaces.md) or [see the workspace reference](./configuration/reference.md#workspace)
74 |
75 | ## Configuration before 0.1.0
76 |
77 | Before version 0.1.0 (the last stable version was/is 0.0.3, the last prerelease was/is 0.1.0-prerelease7), the
78 | configuration format looked like this:
79 |
80 | ```json
81 | {
82 | "name": "oranda",
83 | "description": "generate static sites for your dev tools",
84 | "dist_dir": "oranda_out",
85 | "homepage": "https://oranda.axo.dev",
86 | "static_dir": "static",
87 | "no_header": false,
88 | "readme_path": "dev/README.md",
89 | "repository": "https://github.com/axodotdev/oranda",
90 | "additional_pages": {
91 | "Another page": "dev/additional.md"
92 | },
93 | "favicon": "https://www.axo.dev/favicon.ico",
94 | "analytics": {
95 | "plausible": {
96 | "domain": "tools.axo.dev/oranda"
97 | }
98 | },
99 | "social": {
100 | "image": "https://www.axo.dev/meta_small.jpeg",
101 | "image_alt": "axo",
102 | "twitter_account": "@axodotdev"
103 | },
104 | "artifacts": {
105 | "cargo_dist": true
106 | },
107 | "logo": "assets/oranda.png",
108 | "license": "MIT OR Apache-2.0",
109 | "mdbook": false,
110 | "path_prefix": "oranda",
111 | "styles": {
112 | "theme": "axo_dark"
113 | },
114 | "funding": {
115 | "preferred_funding": "github"
116 | },
117 | "changelog": true
118 | }
119 | ```
120 |
121 |
--------------------------------------------------------------------------------
/docs/src/configuration/additional-pages.md:
--------------------------------------------------------------------------------
1 | # Additional Pages
2 |
3 | If you have extra Markdown files you'd like to link directly as root pages on your generated website, you can
4 | use the `additional_pages` option to list them.
5 |
6 | The option's format is an object with the human-readable page name as keys, and the path to the file as values. Example:
7 |
8 | ```json
9 | {
10 | "build": {
11 | "additional_pages": {
12 | "Another page": "./AnotherFile.md"
13 | }
14 | }
15 | }
16 | ```
17 |
--------------------------------------------------------------------------------
/docs/src/configuration/analytics.md:
--------------------------------------------------------------------------------
1 | # Analytics
2 |
3 | oranda supports automatically inserting the correct analytics snippet your provider into your generated pages.
4 |
5 | Right now we support the following analytics providers:
6 |
7 | - [Google Analytics](https://analytics.google.com/analytics/web/)
8 | - [Plausible](https://plausible.io/)
9 | - [Fathom](https://usefathom.com/)
10 | - [Unami](https://umami.is/)
11 |
12 | To add any of these, add the required configuration under the `analytics` key:
13 |
14 | ### Google Analytics
15 |
16 | ```json
17 | {
18 | "marketing": {
19 | "analytics": {
20 | "google_analytics": {
21 | "tracking_id": "String"
22 | }
23 | }
24 | }
25 | }
26 | ```
27 |
28 | ### Plausible
29 |
30 | ```json
31 | {
32 | "marketing": {
33 | "analytics": {
34 | "plausible": {
35 | "domain": "String",
36 | "script_url": "Optional string for self hosted"
37 | }
38 | }
39 | }
40 | }
41 | ```
42 |
43 | ### Fathom
44 |
45 | ```json
46 | {
47 | "marketing": {
48 | "analytics": {
49 | "fathom": {
50 | "site": "String"
51 | }
52 | }
53 | }
54 | }
55 | ```
56 |
57 | ### Unami
58 |
59 | ```json
60 | {
61 | "marketing": {
62 | "analytics": {
63 | "unami": {
64 | "website": "String",
65 | "script_url": "String"
66 | }
67 | }
68 | }
69 | }
70 | ```
71 |
--------------------------------------------------------------------------------
/docs/src/configuration/artifacts.md:
--------------------------------------------------------------------------------
1 | # Artifacts & `cargo-dist`
2 |
3 | oranda has first-class support for handling releases, including ones generated by [`cargo-dist`].
4 | It can even detect the user's platform and recommend the best installer/archive to use!
5 |
6 | Artifact settings are managed in the `artifacts` key in your oranda config. This is what an example config looks like:
7 |
8 | ```json
9 | {
10 | "components": {
11 | "artifacts": {
12 | "package_managers": {
13 | "preferred": {
14 | "npm": "npm install @axodotdev/oranda --save-dev",
15 | "crates.io": "cargo install oranda --locked --profile=dist",
16 | },
17 | "additional": {
18 | "npx": "npx @axodotdev/oranda",
19 | "binstall": "cargo binstall oranda"
20 | }
21 | }
22 | }
23 | }
24 | }
25 | ```
26 |
27 | ## Enabling cargo-dist
28 |
29 | oranda will automatically attempt to find a `cargo-dist` config in your Cargo.toml. If you want to force disable this,
30 | set `components.artifacts.cargo_dist` to `false`. Once oranda determines that `cargo-dist` support should be enabled,
31 | the following will happen:
32 |
33 | - oranda will attempt to find GitHub releases generated by `cargo-dist`
34 | - A new "Install" page will be generated, containing all artifacts and installers for the latest version
35 | - A section to quickly install the latest release for the user's current platform will be added to the homepage
36 |
37 | ## Enabling arbitrary GitHub release support
38 |
39 | Even if you don't have `cargo-dist` set up, oranda can attempt to glean information about your supported targets and
40 | OSes from your GitHub release artifacts. It will attempt to do this if it can find any releases associated with your
41 | repository. It does this by trying to see if it can recognize any known target triples from your filenames, so for example,
42 | it will recognize `mytool-aarch64-apple-darwin.tar.gz`. If you would like to completely disable this, set
43 | `components.artifacts` to `false` (we may offer a more fine-grained setting for this in the future).
44 |
45 | ## Enabling matching a release to a specific package
46 |
47 | If you have multiple packages being produced by a workspace and need to match a release to a specific package, you can do
48 | so by setting `components.artifacts.match_package_names` to `true`. Upon enabling, while working to determine the latest
49 | release oranda will check if the release tag contains the name of the project being generated. If no match is found
50 | that particular release will be skipped.
51 |
52 | ## Adding package manager installation instructions
53 |
54 | You can add custom installation instructions for package managers or package manager-esque methods using the
55 | `components.artifacts.package_managers` key. These are broken up into two sections:
56 |
57 | - `components.artifacts.package_managers.preferred` - methods that you want to be recommended on the front page install
58 | widget
59 | - `components.artifacts.package_managers.additional` - methods that should only show up on the dedicated "install" page
60 |
61 | All package manager entries are currently treated as "cross-platform", meaning they'll show up in the install widget for
62 | any platform you support. We're aware of this limitation, and will likely expand support for this in the future.
63 |
64 | [`cargo-dist`]: https://opensource.axo.dev/cargo-dist/
65 |
--------------------------------------------------------------------------------
/docs/src/configuration/changelog.md:
--------------------------------------------------------------------------------
1 | # Changelogs
2 |
3 | oranda can generate a separate changelog file from either a local `CHANGELOG.md` file in your repository, or from the body
4 | of GitHub releases. This setting is **enabled** by default, as long as you have a repository set for your project. To disable this
5 | feature, set it to false in the `oranda.json`:
6 |
7 | ```json
8 | {
9 | "components": {
10 | "changelog": false
11 | }
12 | }
13 | ```
14 |
15 | By default, oranda will also generate a `changelog.rss` file which you can plug into RSS readers or other automation!
16 |
17 | ## Controlling where changelogs are read from
18 |
19 | By default, oranda will try to read changelog contents from a file called `CHANGELOG(.md)`. This file needs to be formatted
20 | in such a way that it can be parsed, meaning you'll have to specify consistent headers in your Markdown file, like this:
21 |
22 | ```markdown
23 | # Changelog
24 |
25 | ## 0.1.1 - 2023-04-05
26 |
27 | - Fixed things
28 |
29 | ## 0.1.0 - 2023-04-02
30 |
31 | ### New features
32 |
33 | - Fancy thingie
34 | - Other cool stuff
35 |
36 | ### Fixes
37 |
38 | - Beep booping is now consistent
39 | ```
40 |
41 | If you would like oranda to use the bodies of GitHub releases that it finds instead, set the following option:
42 |
43 | ```json
44 | {
45 | "components": {
46 | "changelog": {
47 | "read_changelog_file": false
48 | }
49 | }
50 | }
51 | ```
52 |
53 | > Even if oranda reads from a local changelog file, it will still try to match those releases to GitHub releases. Make
54 | > sure that both version numbering schemes are the same between your local changelog and GitHub releases.
55 |
56 | For a complete reference of changelog configuration, consult the [reference](./reference.md#componentschangelog)
57 |
58 | ## For workspaces
59 |
60 | If you have a [workspace](./workspaces.md), but you would like to opt-out of changelogs for only some members, you'll need
61 | to add manual overrides in those `oranda.json` files right now.
62 |
--------------------------------------------------------------------------------
/docs/src/configuration/funding.md:
--------------------------------------------------------------------------------
1 | # Funding page
2 |
3 | Oranda has the capability of reading information from your GitHub funding file, and
4 | automatically writing a page based on it. Unless you disable it by setting `components.funding` to `false`
5 | in the oranda config file, oranda will search your project for
6 | a `.github/FUNDING.yml` file, and generate a page based off of it. You can read
7 | more about the format of this file on [GitHub's docs][funding-docs].
8 |
9 | Oranda will display your different sponsor/funding links next to each other, but
10 | if you have a "main" funding option, you can set the following configuration setting:
11 |
12 | ```json
13 | {
14 | "components": {
15 | "funding": {
16 | "preferred_funding": "github"
17 | }
18 | }
19 | }
20 | ```
21 |
22 | Make sure this key corresponds to one of the possible entries in the `FUNDING.yml`
23 | file.
24 |
25 | If you want to display additional information or context, oranda can also include
26 | the contents of a top-level `funding.md` Markdown file. Its contents will be translated
27 | into HTML and displayed on the Funding page as well.
28 |
29 | Both of the YAML and Markdown file paths can be customized as such:
30 |
31 | ```json
32 | {
33 | "components": {
34 | "funding": {
35 | "md_path": "myfunding.md",
36 | "yml_path": "misc/funding.yml"
37 | }
38 | }
39 | }
40 | ```
41 |
42 | > oranda's funding parsing and site generation are currently an experiment into how
43 | to better integrate common funding methods into your tools' websites. If you have
44 | any feedback on how we could do things better, let us know on
45 | [Discord][axodiscord] or [GitHub][newissue]!
46 |
47 | [funding-docs]: https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/displaying-a-sponsor-button-in-your-repository
48 | [axodiscord]: https://discord.com/invite/wVqCRGsb
49 | [newissue]: https://github.com/axodotdev/oranda/issues/new
--------------------------------------------------------------------------------
/docs/src/configuration/mdbook.md:
--------------------------------------------------------------------------------
1 | # mdbook support
2 |
3 | oranda can generate [mdbooks][mdbook] for you. If you've already worked with mdbook, it's as simple as pointing oranda
4 | at your book directory using the `mdbook.path` option:
5 |
6 | ```json
7 | {
8 | "components": {
9 | "mdbook": {
10 | "path": "./docs"
11 | }
12 | }
13 | }
14 | ```
15 |
16 | This will cause oranda to automatically recompile your book for you, which will be served at the `yoursite/book/` URL.
17 | `oranda dev` will also be watching this directory.
18 |
19 | ## mdbook quickstart
20 |
21 | If this is the first time you're working with mdbook, these are the minimal steps you'd need before editing the oranda config.
22 | After you've [installed the mdbook tool][mdbook-install], you can generate a new book scaffold:
23 |
24 | ```sh
25 | mdbook init docs # replace docs with your preferred directory
26 | ```
27 |
28 | You can either use `oranda dev` or `mdbook serve docs/` to have a preview for your mdbook.
29 |
30 | [mdbook]: https://rust-lang.github.io/mdBook/
31 | [mdbook-install]: https://rust-lang.github.io/mdBook/guide/installation.html
32 |
--------------------------------------------------------------------------------
/docs/src/configuration/social.md:
--------------------------------------------------------------------------------
1 | # Social
2 |
3 | In order to help with SEO, there are a couple of options you can use.
4 |
5 | ```json
6 | {
7 | "marketing": {
8 | "social": {
9 | "image": "used as the share image for social media",
10 | "image_alt": "alt for said image",
11 | "twitter_account": "twitter account for the website"
12 | }
13 | }
14 | }
15 | ```
16 |
--------------------------------------------------------------------------------
/docs/src/configuration/theme.md:
--------------------------------------------------------------------------------
1 | # Theming
2 |
3 | ## Predefined themes
4 |
5 | Oranda comes with six default themes:
6 |
7 | - Light
8 | - Dark
9 | - Axo Light (`axo_light` or `axolight`)
10 | - Axo Dark (`axo_dark` or `axodark`)
11 | - Hacker
12 | - Cupcake
13 |
14 | You can change the theme by adding the `styles.theme` key to your `oranda.json`:
15 |
16 | ```json
17 | {
18 | "styles": {
19 | "theme": "hacker"
20 | }
21 | }
22 | ```
23 |
24 | Dark is the default theme.
25 |
26 | ## Customizing Themes
27 |
28 | Themes can be further customized by adding extra CSS.
29 |
30 | Additional CSS can be added using the `styles.additional_css` key.
31 |
32 | ```json
33 | {
34 | "styles": {
35 | "additional_css": ["./local/file.css", "http://www.remote.dev/file.css"]
36 | }
37 | }
38 | ```
39 |
40 | > Note: Remote files will be copied and the copy served locally, so once a link is updated, the site must be regenerated for changes to take effect.
41 |
42 | ### Adding CSS
43 |
44 | Oranda's CSS makes use of [cascade layers](https://css-tricks.com/css-cascade-layers/) to scope CSS and make it simpler to override styles. To override themed styles, say on a `
` element, place it inside a layer called `overrides`.
45 |
46 | ```css
47 | @layer overrides {
48 | p {
49 | color: aquamarine;
50 | }
51 | }
52 | ```
53 |
54 | Alternately, CSS that is not defined within a layer has precedence over all layered CSS, so this will also work.
55 |
56 | ```css
57 | p {
58 | color: aquamarine;
59 | }
60 | ```
61 |
62 | ### Dark vs. Light
63 |
64 | When the `dark` theme is selected, a `dark` class is added to the page, and styles to be applied in dark mode only can include this selector. For instance,
65 |
66 | ```css
67 | .dark p {
68 | color: aquamarine;
69 | }
70 | ```
71 |
72 | Will create paragraphs colored aquamarine in dark mode only.
73 |
74 | ### Adding Classes
75 |
76 | When there are specific elements you would like to add to your pages, these can be added into Markdown files as raw HTML with class selectors that you can target with your CSS.
77 |
78 | ```html
79 |
80 |
81 | ## A Different Kind of Box
82 |
83 |
84 |
An outlined box
85 |
86 | ```
87 |
88 | ```css
89 | .my-border-class {
90 | padding: 1rem;
91 | border: 6px dotted seagreen;
92 | }
93 | ```
94 |
95 | ## Creating a New Theme
96 |
97 | Currently, to create a new theme, you need to follow the directions above in "Customizing Themes" and overwrite the given CSS. We recommend continuing the layer approach and placing overrides in the `overrides` layer and then adding a new named layer for your theme.
98 |
99 | The ability to add a different theme directly will be included in future releases. Following the layers approach will make it simpler to transition your theme.
100 |
--------------------------------------------------------------------------------
/docs/src/configuration/theme/previews.md:
--------------------------------------------------------------------------------
1 | # Theme Previews
2 |
3 | Here you can see what themes look like without having to set up oranda yourself.
4 |
5 | These previews are generated by running oranda on itself.
6 |
7 | ## Dark (default)
8 |
9 | 
10 |
11 | ## Light
12 |
13 | 
14 |
15 | ## Axo Dark
16 |
17 | 
18 |
19 | ## Axo Light
20 |
21 |
22 | 
23 |
24 | ## Hacker
25 |
26 | 
27 |
28 | ## Cupcake
29 |
30 | 
31 |
--------------------------------------------------------------------------------
/docs/src/configuration/workspaces.md:
--------------------------------------------------------------------------------
1 | # Workspaces
2 |
3 | oranda supports building multiple sites at once (referred to as building in a "workspace"). To control this behavior,
4 | you can create a `oranda-workspace.json` file inside your workspace root. Running an oranda command will pick up this
5 | file, and build the workspace members accordingly.
6 |
7 | The reason why this is a separate file, and not part of the `oranda.json` file is to avoid confusing between _nonvirtual_
8 | workspace root members (meaning if a workspace root also contains a site/package of some kind). By putting your workspace
9 | configuration in a separate file, you can still have an oranda site at the same directory level, without any problems.
10 |
11 | > **NOTE**: Workspace functionality will not be enabled if the `oranda-workspace.json` file doesn't exist!
12 |
13 | A workspace configuration file looks something like this:
14 |
15 | ```json
16 | {
17 | "workspace": {
18 | "name": "My Workspace",
19 | "members": [
20 | {
21 | "slug": "projectone",
22 | "path": "./project-one"
23 | },
24 | {
25 | "slug": "project_two",
26 | "path": "./project-two"
27 | }
28 | ]
29 | }
30 | }
31 | ```
32 |
33 | When ran with `oranda build`, this will produce two oranda sites, one at `/projectone`, and one at `/project_two`. oranda
34 | will consider each separate project's `oranda.json` file (should it exist).
35 |
36 | You can additionally pass down keys you'd like to be set for each member project:
37 |
38 | ```json
39 | {
40 | "workspace": {
41 | "name": "My Workspace",
42 | "members": [
43 | {
44 | "slug": "projectone",
45 | "path": "./project-one"
46 | },
47 | {
48 | "slug": "project_two",
49 | "path": "./project-two"
50 | }
51 | ]
52 | },
53 | "styles": {
54 | "theme": "hacker"
55 | }
56 | }
57 | ```
58 |
59 | Individual workspace member configs will still override what's set here, though. Also, _every_ key will be passed down,
60 | including ones that don't make a lot of sense to be the same in multiple projects (for example [package manager](artifacts.md)
61 | configuration).
62 |
63 | Building a workspace will also generate a nice workspace index page that can be used to provide an overview over the
64 | workspace's members, as well as some quick info and metadata.
65 |
--------------------------------------------------------------------------------
/docs/src/contributing.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | Here's some helpful tips for contributing.
4 |
5 | ## Auto-recompiling based on source changes
6 |
7 | If you're working on oranda and you want to rebuild both oranda itself and your preview site when stuff changes,
8 | this is the command to keep in mind (assuming you have `cargo-watch` installed):
9 |
10 | ```shell
11 | cargo watch -x "run dev"
12 | ```
13 |
14 | On some platforms, apparently images also get recompiled and picked up by cargo-watch:
15 |
16 | ```shell
17 | cargo watch -x "run dev" --ignore "*.png" --ignore "*.jpg"
18 | ```
19 |
20 | ## ...plus working on the CSS
21 |
22 | We provide an environment variable (`ORANDA_USE_TAILWIND_BINARY`) that, when set, will cause oranda to
23 | use a prebuilt Tailwind binary to rebuild the CSS on the fly. You can use it like this:
24 |
25 | ```shell
26 | ORANDA_USE_TAILWIND_BINARY=true cargo run dev
27 | # for fish shell:
28 | env ORANDA_USE_TAILWIND_BINARY=true cargo run dev
29 | ```
30 |
31 | (if you don't use this flag, oranda will most likely revert to fetching CSS from the current GitHub release.
32 | Read [this section](./building.md#the-trouble-with-css) in the build documentation for more in-depth info)
33 |
34 | `oranda dev` doesn't automatically reload on CSS changes (because it's meant to be used by users),
35 | but you can include the CSS directory manually like such:
36 |
37 | ```shell
38 | ORANDA_USE_TAILWIND_BINARY=true cargo run dev -i oranda-css/css/
39 | ```
40 |
41 | ## Updating syntax highlighting languages
42 |
43 | We use [syntect] to support syntax highlighting in Markdown code blocks. If you want to add support for a new language
44 | that's not included in syntect's default set of languages or the ones oranda provides, you'll need to extend the
45 | `oranda::site::markdown::syntax_highlight::dump_syntax_themes` function to load your new `.sublime-syntax` file from
46 | disk
47 | and to include it in our syntax set dump. This function, once adjusted, only needs to be ran once manually, by including
48 | it anywhere in the call path of the application (I recommend somewhere close to the top of the build CLI function).
49 |
50 | ### Converting from .tmLanguage
51 |
52 | `syntect` accepts `.sublime-syntax` files, but Sublime Text can also accept `.tmLanguage` (TextMate syntax bundles)
53 | files,
54 | so sometimes we need to convert from one to the other. Thankfully, the Sublime Text editor has a built-in feature for
55 | this.
56 | Here's what you need to do:
57 |
58 | 1. Download and install Sublime Text
59 | 2. In Sublime Text, from the menu, select Tools -> Developer -> New Syntax...
60 | 3. This puts you in your Packages/User folder. Paste your tmLanguage file contents and save as `.tmLanguage`.
61 | 4. Next, you should be able to run Tools -> Developer -> New Syntax from .tmLanguage...
62 | 5. This opens a new tab with the converted output. Save and copy it or paste it into a new file in oranda. Profit!
63 |
64 | [syntect]: https://crates.io/crates/syntect
--------------------------------------------------------------------------------
/docs/src/hosting.md:
--------------------------------------------------------------------------------
1 | # Hosting
2 |
3 | ## On GitHub pages
4 |
5 | > Added in version 0.4.0.
6 |
7 | You can use `oranda generate ci --ci=github` to write a CI file for deploying your main branch to GitHub Pages.
8 |
9 | When hosting on GitHub Pages, depending on the name of your repository, your site may get served at different URLs:
10 |
11 | - If your repo name is `.github.io`, your site will be at that URL
12 | - If you repo name is anything else, it'll be at `.github.io/`.
13 |
14 | For the latter case, you'll need to configure oranda to write its links to your `oranda.json` (more on that on chapter 8) with `` as a prefix:
15 |
16 | ```json
17 | {
18 | "build": {
19 | "path_prefix": "reponame"
20 | }
21 | }
22 | ```
23 |
24 | This will cause, for example, a link to `/changelog/` to be written as `/reponame/changelog/`.
25 |
26 | ## Elsewhere
27 |
28 | oranda is, effectively, a static site generator. It outputs HTML, CSS and JavaScript files. These can all be hosted on a
29 | looooot of different platforms, in fact, too many for us to enumerate here! You can use Vercel, Netlify, any GitHub pages
30 | competitor, or you can plop it on your own server that runs nginx, Apache httpd, Caddy, or anything else!
31 |
32 | You can, in fact, also use the CI generated by `oranda generate ci` linked above and modify it to deploy to different
33 | platforms. If you do, we'd love to hear about it!
34 |
35 | [web.yml]: https://github.com/axodotdev/oranda/blob/main/.github/workflows/web.yml
36 |
--------------------------------------------------------------------------------
/docs/src/images/artifacts-pkgman.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axodotdev/oranda/53bdbda1835307ffe11348d4eeb517ccf8a0d2ff/docs/src/images/artifacts-pkgman.png
--------------------------------------------------------------------------------
/docs/src/images/quickstart-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axodotdev/oranda/53bdbda1835307ffe11348d4eeb517ccf8a0d2ff/docs/src/images/quickstart-1.png
--------------------------------------------------------------------------------
/docs/src/images/quickstart-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axodotdev/oranda/53bdbda1835307ffe11348d4eeb517ccf8a0d2ff/docs/src/images/quickstart-2.png
--------------------------------------------------------------------------------
/docs/src/images/themes/axodark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axodotdev/oranda/53bdbda1835307ffe11348d4eeb517ccf8a0d2ff/docs/src/images/themes/axodark.png
--------------------------------------------------------------------------------
/docs/src/images/themes/axolight.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axodotdev/oranda/53bdbda1835307ffe11348d4eeb517ccf8a0d2ff/docs/src/images/themes/axolight.png
--------------------------------------------------------------------------------
/docs/src/images/themes/cupcake.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axodotdev/oranda/53bdbda1835307ffe11348d4eeb517ccf8a0d2ff/docs/src/images/themes/cupcake.png
--------------------------------------------------------------------------------
/docs/src/images/themes/dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axodotdev/oranda/53bdbda1835307ffe11348d4eeb517ccf8a0d2ff/docs/src/images/themes/dark.png
--------------------------------------------------------------------------------
/docs/src/images/themes/hacker.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axodotdev/oranda/53bdbda1835307ffe11348d4eeb517ccf8a0d2ff/docs/src/images/themes/hacker.png
--------------------------------------------------------------------------------
/docs/src/images/themes/light.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axodotdev/oranda/53bdbda1835307ffe11348d4eeb517ccf8a0d2ff/docs/src/images/themes/light.jpg
--------------------------------------------------------------------------------
/docs/src/images/themes/light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axodotdev/oranda/53bdbda1835307ffe11348d4eeb517ccf8a0d2ff/docs/src/images/themes/light.png
--------------------------------------------------------------------------------
/docs/src/install.md:
--------------------------------------------------------------------------------
1 | # Install
2 |
3 | There's lots of ways to install oranda!
4 |
5 | ## The Quickest Way
6 |
7 | On the [oranda website][website], there's a one-liner command you can execute for your
8 | OS that'll download and install oranda for you, without any further hassle!
9 |
10 | ## Install Prebuilt Binaries With [cargo-binstall]
11 |
12 | ```sh
13 | cargo binstall oranda
14 | ```
15 |
16 | ## Build From Source With Cargo
17 |
18 | ```sh
19 | cargo install oranda --locked --profile=dist
20 | ```
21 |
22 | > `--profile=dist` is the profile we build our shippable binaries with, it's optional.
23 | >
24 | > `--locked` asks Cargo to respect the lockfile, improving build reproducibility at
25 | > the cost of not getting any bugfixes from newer releases of its dependencies.
26 |
27 |
28 | ## Download Prebuilt Binaries From GitHub Releases
29 |
30 | [See the latest release](https://github.com/axodotdev/oranda/releases/latest)!
31 |
32 | ## Install With NPM
33 |
34 | ```sh
35 | npm install @axodotdev/oranda
36 | # alternatively:
37 | npx @axodotdev/oranda build
38 | ```
39 |
40 | ## Install With Nix
41 | oranda is available in [`nixpkgs`](https://github.com/NixOS/nixpkgs/blob/master/pkgs/applications/misc/oranda/default.nix), and also as a nix flake. This installer is currently experimental, so we don't recommend you use it in production workflows.
42 |
43 | On a system with nix installed, you can run
44 | ```sh
45 | nix-env -i oranda
46 | ```
47 |
48 | or to install from GitHub using the flake,
49 | ```sh
50 | nix profile install github:axodotdev/oranda
51 | ```
52 |
53 | [cargo-binstall]:https://github.com/cargo-bins/cargo-binstall
54 | [website]: https://opensource.axo.dev/oranda
55 |
--------------------------------------------------------------------------------
/docs/src/introduction.md:
--------------------------------------------------------------------------------
1 | # Introduction
2 |
3 | Oranda is a tool for generating beautiful landing pages for your projects.
4 |
5 | It can:
6 |
7 | - Automagically generate a webpage based off your project readme file
8 | - Include arbitrary Markdown pages
9 | - Generate `mdbook` books for you
10 | - Show downloadable and installable artifacts, and ways to install them, by integrating with [cargo-dist]
11 | - Provide integration with several web analytics providers
12 |
13 | and more!
14 |
15 | This is the oranda documentation, where we explain how to use the tool in detail. Use the
16 | sidebar to the left to navigate between pages.
17 |
18 | > **Caveat emptor!** oranda is still _beta-quality software_! There may be breaking changes at times, especially
19 | in the configuration format (although we're working hard on stabilizing it)
20 |
21 | [cargo-dist]: https://github.com/axodotdev/cargo-dist
22 |
--------------------------------------------------------------------------------
/docs/src/quickstart.md:
--------------------------------------------------------------------------------
1 | # Quickstart
2 |
3 | After you've [installed](./install.md) oranda, it's time to give it a spin. Make sure you can execute the
4 | `oranda` command, its output should look something like this:
5 |
6 | ```
7 | $ oranda
8 | 🎁 generate beautiful landing pages for your projects
9 |
10 | Usage: oranda [OPTIONS]
11 |
12 | Commands:
13 | build Build an oranda site
14 | dev Start a local development server that recompiles your oranda site if a file changes
15 | serve Start a file server to access your oranda site in a browser
16 | generate Generate infrastructure files for oranda sites
17 | help Print this message or the help of the given subcommand(s)
18 |
19 | Options:
20 | -h, --help
21 | Print help (see a summary with '-h')
22 |
23 | -V, --version
24 | Print version
25 |
26 | GLOBAL OPTIONS:
27 | -v, --verbose
28 | Whether to output more detailed debug information
29 |
30 | --output-format
31 | The format of the output
32 |
33 | [default: human]
34 |
35 | Possible values:
36 | - human: Human-readable output
37 | - json: Machine-readable JSON output
38 | ```
39 |
40 | Since `oranda` is designed to work without configuration, the quickest start is to just run `oranda dev` in an
41 | existing project! This will spawn a web server that serves your site, plus an extra process that watches for
42 | changes in files relevant to `oranda`'s build process.
43 |
44 | > __NOTE__: Prior to version 0.5.0, oranda expects there to be a README.md file in your root directory!
45 |
46 | ## In a Cargo project
47 |
48 | `oranda` integrates with Cargo projects seamlessly. `oranda build` will pick up relevant
49 | metadata from your `Cargo.toml` file automatically, including [`cargo-dist`] configuration,
50 | if you have that set up.
51 |
52 | ## In a Node project
53 |
54 | If you use Node.js, oranda can not only be installed via npm, but also supports reading metadata
55 | from your package manifest file. Additionally, npm scripts make it easy to integrate `oranda` into
56 | your workflows, for example like this:
57 |
58 | ```json
59 | {
60 | "scripts": {
61 | "build:site": "oranda build"
62 | },
63 | "dependencies": {
64 | "@axodotdev/oranda": "~0.3.0"
65 | }
66 | }
67 | ```
68 |
69 | ## Further Steps
70 |
71 | - Explore the [`oranda` configuration options](./configuration.md)
72 | - [Host your site](./hosting.md), on GitHub Pages or elsewhere
73 | - Read the [CLI docs](./cli.md)
74 | - Learn more about [hosting `oranda` sites](./hosting.md)
75 |
76 | [`cargo-dist`]: https://opensource.axo.dev/cargo-dist
77 |
--------------------------------------------------------------------------------
/docs/src/tips.md:
--------------------------------------------------------------------------------
1 | # Tips and Tricks
2 |
3 | ## Hiding the Markdown title
4 |
5 | Oranda breaks out your project's title into its own header, which can be annoying if you've started your own
6 | README.md with something like this:
7 |
8 | ```markdown
9 | # myprojectname
10 |
11 | Blah blah blah etc
12 | ```
13 |
14 | If you build your oranda site like this, the title will appear twice! oranda supports a special class called `oranda-hide`
15 | that you can wrap your title (or whatever you don't want to appear on the page) with, like this:
16 |
17 | ```markdown
18 |
19 |
20 | # myprojectname
21 |
22 |
23 |
24 | Blah blah blah etc
25 | ```
26 |
27 | Keep in mind the line breaks before and after the HTML, otherwise the Markdown parser may not function correctly.
28 |
--------------------------------------------------------------------------------
/flake.lock:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": {
3 | "fenix": {
4 | "inputs": {
5 | "nixpkgs": [
6 | "nixpkgs"
7 | ],
8 | "rust-analyzer-src": "rust-analyzer-src"
9 | },
10 | "locked": {
11 | "lastModified": 1692889293,
12 | "narHash": "sha256-pkQGbmDcHD4To6lZGbPcUynTuceqmt0Le6uUfxijYs4=",
13 | "owner": "nix-community",
14 | "repo": "fenix",
15 | "rev": "d3f055733e5449a31c22d64951300072d84c5f81",
16 | "type": "github"
17 | },
18 | "original": {
19 | "owner": "nix-community",
20 | "repo": "fenix",
21 | "type": "github"
22 | }
23 | },
24 | "flake-utils": {
25 | "inputs": {
26 | "systems": "systems"
27 | },
28 | "locked": {
29 | "lastModified": 1692799911,
30 | "narHash": "sha256-3eihraek4qL744EvQXsK1Ha6C3CR7nnT8X2qWap4RNk=",
31 | "owner": "numtide",
32 | "repo": "flake-utils",
33 | "rev": "f9e7cf818399d17d347f847525c5a5a8032e4e44",
34 | "type": "github"
35 | },
36 | "original": {
37 | "owner": "numtide",
38 | "repo": "flake-utils",
39 | "type": "github"
40 | }
41 | },
42 | "nixpkgs": {
43 | "locked": {
44 | "lastModified": 1692734709,
45 | "narHash": "sha256-SCFnyHCyYjwEmgUsHDDuU0TsbVMKeU1vwkR+r7uS2Rg=",
46 | "owner": "NixOS",
47 | "repo": "nixpkgs",
48 | "rev": "b85ed9dcbf187b909ef7964774f8847d554fab3b",
49 | "type": "github"
50 | },
51 | "original": {
52 | "owner": "NixOS",
53 | "ref": "nixos-unstable",
54 | "repo": "nixpkgs",
55 | "type": "github"
56 | }
57 | },
58 | "root": {
59 | "inputs": {
60 | "fenix": "fenix",
61 | "flake-utils": "flake-utils",
62 | "nixpkgs": "nixpkgs"
63 | }
64 | },
65 | "rust-analyzer-src": {
66 | "flake": false,
67 | "locked": {
68 | "lastModified": 1692775770,
69 | "narHash": "sha256-LwoR5N1JHykSte2Ak+Pj/HjJ9fKy9zMJNEftfBJQkLs=",
70 | "owner": "rust-lang",
71 | "repo": "rust-analyzer",
72 | "rev": "f5b7c60ff7a79bfb3e10f3e98c81b7bb4cb53c68",
73 | "type": "github"
74 | },
75 | "original": {
76 | "owner": "rust-lang",
77 | "ref": "nightly",
78 | "repo": "rust-analyzer",
79 | "type": "github"
80 | }
81 | },
82 | "systems": {
83 | "locked": {
84 | "lastModified": 1681028828,
85 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
86 | "owner": "nix-systems",
87 | "repo": "default",
88 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
89 | "type": "github"
90 | },
91 | "original": {
92 | "owner": "nix-systems",
93 | "repo": "default",
94 | "type": "github"
95 | }
96 | }
97 | },
98 | "root": "root",
99 | "version": 7
100 | }
101 |
--------------------------------------------------------------------------------
/flake.nix:
--------------------------------------------------------------------------------
1 | {
2 | description = "🎁 generate beautiful landing pages for your developer tools";
3 |
4 | inputs = {
5 | nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
6 | flake-utils.url = "github:numtide/flake-utils";
7 | fenix = {
8 | url = "github:nix-community/fenix";
9 | inputs.nixpkgs.follows = "nixpkgs";
10 | };
11 | };
12 |
13 | outputs =
14 | { self
15 | , nixpkgs
16 | , flake-utils
17 | , fenix
18 | , ...
19 | }:
20 | flake-utils.lib.eachDefaultSystem
21 | (system:
22 | let
23 | pkgs = import nixpkgs {
24 | inherit system;
25 | overlays = [
26 | fenix.overlays.default
27 | ];
28 | };
29 |
30 | # Parse the local Cargo.toml so we track the usual rust workflow
31 | cargo_toml = builtins.fromTOML (builtins.readFile ./Cargo.toml);
32 |
33 | package = with pkgs;
34 | rustPlatform.buildRustPackage {
35 | pname = cargo_toml.package.name;
36 | version = cargo_toml.package.version;
37 | src = ./.;
38 | cargoLock = {
39 | lockFile = ./Cargo.lock;
40 | };
41 |
42 | # Don't run checks
43 | doCheck = false;
44 |
45 | # Package metadata
46 | meta = with pkgs.lib; {
47 | description = cargo_toml.package.description;
48 | homepage = cargo_toml.package.repository;
49 | license = with licenses; [ asl20 mit ];
50 | };
51 |
52 | nativeBuildInputs = with pkgs; [
53 | pkg-config
54 | tailwindcss
55 | ];
56 |
57 | buildInputs = with pkgs; ([
58 | bzip2
59 | oniguruma
60 | openssl
61 | xz
62 | zstd
63 | ]
64 | ++ darwinInputs);
65 |
66 | RUSTONIG_SYSTEM_LIBONIG = true;
67 | ZSTD_SYS_USE_PKG_CONFIG = true;
68 | NIX_LDFLAGS = nixLdFlags;
69 | ORANDA_USE_TAILWIND_BINARY = true;
70 | };
71 |
72 | # Darwin-specific build requirements
73 | frameworks = pkgs.darwin.apple_sdk.frameworks;
74 | darwinInputs = with pkgs; (lib.optionals stdenv.isDarwin [ libiconv frameworks.Security ]);
75 | nixLdFlags = with pkgs; (lib.optionalString (stdenv.isDarwin) "-F${frameworks.CoreServices}/Library/Frameworks -framework CoreServices -L${libiconv}/lib");
76 | in
77 | {
78 | packages = rec {
79 | oranda = package;
80 | default = oranda;
81 | };
82 |
83 | devShells = with pkgs; {
84 | default = mkShell {
85 | nativeBuildInputs = package.nativeBuildInputs;
86 |
87 | buildInputs =
88 | [
89 | (fenix.packages.${system}.complete.withComponents [
90 | "cargo"
91 | "clippy"
92 | "rust-src"
93 | "rustc"
94 | "rustfmt"
95 | ])
96 | ]
97 | ++ darwinInputs;
98 |
99 | # Allow rust-analyzer and other tools to see rust src
100 | RUST_SRC_PATH = "${rustPlatform.rustLibSrc}";
101 |
102 | # Fix missing OpenSSL
103 | PKG_CONFIG_PATH = "${pkgs.openssl.dev}/lib/pkgconfig";
104 |
105 | shellHook = ''
106 | export NIX_LDFLAGS="${nixLdFlags}"
107 | '';
108 | };
109 | };
110 | });
111 | }
112 |
--------------------------------------------------------------------------------
/generate-css/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "oranda-generate-css"
3 | version = "0.6.5"
4 | description = "the part of oranda that generates CSS"
5 | repository = "https://github.com/axodotdev/oranda"
6 | homepage = "https://opensource.axo.dev/oranda"
7 | edition = "2021"
8 | authors = ["Axo Developer Co. "]
9 | license = "MIT OR Apache-2.0"
10 |
11 | [dependencies]
12 | directories = "5.0.1"
13 | camino = "1.1.4"
14 | axoasset = "0.4.0"
15 | tracing = "0.1"
16 | tokio = { version = "1.20.1", features = ["full"] }
17 | reqwest = { version = "0.11.13", default-features = false, features = ["json", "rustls-tls"] }
18 | miette = "5.7.0"
19 | thiserror = "1.0.37"
20 |
--------------------------------------------------------------------------------
/generate-css/src/errors.rs:
--------------------------------------------------------------------------------
1 | use miette::Diagnostic;
2 | use thiserror::Error;
3 |
4 | pub type Result = std::result::Result;
5 |
6 | #[derive(Debug, Diagnostic, Error)]
7 | pub enum GenerateCssError {
8 | #[error(transparent)]
9 | Io(#[from] std::io::Error),
10 |
11 | #[error(transparent)]
12 | Reqwest(#[from] reqwest::Error),
13 |
14 | #[error(transparent)]
15 | AxoAsset(#[from] axoasset::AxoassetError),
16 |
17 | #[error(transparent)]
18 | NonUtf8Path(#[from] std::str::Utf8Error),
19 | }
20 |
--------------------------------------------------------------------------------
/oranda-css/css/base.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 |
3 | /* GLOBAL */
4 |
5 | body,
6 | html {
7 | @apply scroll-smooth h-full;
8 | font-family: var(--font-face);
9 | }
10 |
11 | .container {
12 | @apply min-h-full flex flex-col;
13 | }
14 |
15 | .page-body {
16 | @apply grow;
17 | }
18 |
19 | *:focus {
20 | @apply outline outline-offset-4 outline-2;
21 | }
22 |
23 | body {
24 | background-color: var(--bg-color);
25 | color: var(--fg-color);
26 | }
27 |
28 | /* TEXT */
29 |
30 | a {
31 | color: var(--link-color);
32 | @apply hover:underline hover:underline-offset-4;
33 | }
34 |
35 | .title {
36 | @apply text-center text-6xl sm:text-8xl pb-2;
37 | }
38 |
39 | h1 {
40 | @apply heading-1;
41 | }
42 |
43 | h2 {
44 | @apply heading-2;
45 | }
46 |
47 | h2,
48 | h3 {
49 | @apply mt-12 sm:mt-24;
50 | }
51 |
52 | h3 {
53 | @apply heading-3;
54 | }
55 |
56 | h4 {
57 | @apply heading-4;
58 | }
59 |
60 | h5 {
61 | @apply heading-5;
62 | }
63 |
64 | h6 {
65 | @apply heading-6;
66 | }
67 |
68 | p,
69 | table {
70 | @apply text-base sm:text-lg leading-relaxed mb-8;
71 | }
72 |
73 | b,
74 | li {
75 | @apply text-base sm:text-lg leading-relaxed;
76 | }
77 |
78 | /* TABLES */
79 |
80 | table {
81 | @apply my-16;
82 | }
83 |
84 | table th {
85 | @apply text-left uppercase;
86 | padding: 1rem;
87 | }
88 |
89 | table td {
90 | @apply align-top p-4 text-sm font-mono;
91 | }
92 |
93 | table td > code {
94 | @apply text-sm;
95 | }
96 |
97 | table tbody tr {
98 | @apply border-t;
99 | border-color: var(--fg-color);
100 | }
101 |
102 | div.table {
103 | @apply grid grid-cols-4 w-full my-16;
104 | }
105 |
106 | div.table .th {
107 | @apply text-left uppercase font-bold border-t text-lg p-4;
108 | border-color: var(--fg-color);
109 | }
110 |
111 | div.table span:not(.th) {
112 | @apply text-sm font-mono border-t p-4;
113 | border-color: var(--fg-color);
114 | }
115 |
116 | /* LISTS */
117 |
118 | ul,
119 | li {
120 | @apply list-none;
121 | }
122 |
123 | .rendered-markdown ul,
124 | .rendered-markdown li {
125 | @apply list-disc;
126 | }
127 |
128 | li {
129 | @apply sm:ml-8 ml-4 mb-4;
130 | }
131 |
132 | /* CODE */
133 | code {
134 | @apply whitespace-pre-wrap text-base sm:text-lg leading-relaxed mb-4;
135 | color: var(--link-color);
136 | }
137 |
138 | div.table code {
139 | @apply text-sm;
140 | }
141 |
142 | h1 code,
143 | h2 code,
144 | h3 code,
145 | h4 code,
146 | h5 code,
147 | h6 code {
148 | font-size: inherit;
149 | line-height: inherit;
150 | }
151 |
152 | pre {
153 | @apply p-4 overflow-auto my-16;
154 | }
155 |
156 | pre > code {
157 | @apply text-xs sm:text-base;
158 | }
159 |
160 | hr {
161 | @apply text-center my-20 border border-dashed w-64 md:w-96 m-auto;
162 | }
163 |
164 | img {
165 | @apply inline;
166 | }
167 |
168 | /*
169 | In Markdown, you'll want standalone images to live in their own paragraph - which is why we can target only these
170 | standalone images and center them probably.
171 | */
172 | p > img:only-child {
173 | @apply block m-auto;
174 | }
175 |
176 | blockquote {
177 | @apply border-l-2 text-2xl pl-6;
178 | border-color: var(--link-color);
179 | }
180 |
181 | main {
182 | @apply mx-auto my-24 lg:max-w-4xl max-w-[80%];
183 | }
184 |
185 | .github-icon {
186 | @apply w-5 h-5 bg-github-logo;
187 | }
188 |
189 | .dark .github-icon {
190 | @apply bg-github-logo-dark;
191 | }
192 |
193 | .dark .artifacts,
194 | .light .artifacts {
195 | @apply p-8;
196 | }
197 |
198 | .logo {
199 | @apply m-auto block max-w-xs;
200 | }
201 |
202 | .inline-code {
203 | @apply text-center break-all;
204 | }
205 |
--------------------------------------------------------------------------------
/oranda-css/css/buttons.css:
--------------------------------------------------------------------------------
1 | .button {
2 | @apply w-40 min-w-max p-3 rounded text-lg cursor-pointer transition disabled:opacity-60 disabled:cursor-default border-2;
3 | }
4 |
5 | /* A mono-color button */
6 | .button.primary {
7 | @apply border-transparent;
8 | color: var(--fg-color);
9 | background-color: var(--bg-color);
10 | }
11 |
12 | /* A duo-color button that changes when you hover over it. */
13 | .button.secondary {
14 | color: var(--fg-color);
15 | background-color: var(--bg-color);
16 | border-color: var(--fg-color);
17 | }
18 |
19 | .button.secondary:hover {
20 | color: var(--bg-color);
21 | background-color: var(--fg-color);
22 | border-color: var(--bg-color);
23 | }
24 |
25 | select {
26 | color: var(--fg-color);
27 | background-color: var(--bg-color);
28 | }
--------------------------------------------------------------------------------
/oranda-css/css/components.css:
--------------------------------------------------------------------------------
1 | /* FOOTER */
2 |
3 | footer {
4 | @apply flex w-full justify-between px-4 py-2 text-xs items-center shrink grow-0;
5 | background-color: var(--fg-color);
6 | color: var(--bg-color);
7 | }
8 |
9 | /* NAV */
10 |
11 | .nav {
12 | @apply p-0 text-center mb-12;
13 | }
14 |
15 | .nav ul {
16 | @apply p-0 flex flex-wrap gap-6 items-center text-center list-none justify-center;
17 | }
18 |
19 | .nav ul li {
20 | @apply m-0 capitalize;
21 | }
22 |
23 | /* REPO BANNER */
24 |
25 | .repo_banner {
26 | @apply py-1.5;
27 | color: var(--bg-color);
28 | background-color: var(--fg-color);
29 | }
30 |
31 | .repo_banner > a {
32 | @apply flex justify-center gap-2 items-start hover:text-slate-50 text-slate-50 dark:text-axo-black dark:hover:text-axo-black h-[20px] hover:underline hover:underline-offset-1 dark:hover:decoration-axo-black hover:decoration-slate-50;
33 | }
34 |
35 | /* FUNDING */
36 |
37 | .funding-wrapper {
38 | @apply mt-8 flex flex-col items-center;
39 | }
40 |
41 | .funding-list {
42 | @apply my-12 w-full lg:grid grid-cols-2 gap-4;
43 | }
44 |
45 | .funding-list li {
46 | @apply m-0 mb-4;
47 | }
48 |
49 | .funding-list li a {
50 | @apply flex gap-2 items-center;
51 | }
52 |
53 | .funding-list li a:hover button {
54 | @apply text-slate-100 bg-axo-orange-dark border-axo-orange-dark;
55 | color: var(--bg-color);
56 | background-color: var(--fg-color);
57 | border-color: var(--bg-color);
58 | }
59 |
60 | .funding-list .button {
61 | @apply block w-auto mr-2;
62 | }
63 |
64 | .preferred-funding-list {
65 | @apply grid-cols-1;
66 | }
67 |
68 | .preferred-funding-list li a {
69 | @apply flex-col text-4xl font-bold;
70 | }
71 |
72 | .preferred-funding-list svg {
73 | @apply w-12 h-12;
74 | }
75 |
76 | .preferred-funding-list .button {
77 | @apply border-0;
78 | }
--------------------------------------------------------------------------------
/oranda-css/css/main.css:
--------------------------------------------------------------------------------
1 | @import url("https://fonts.googleapis.com/css2?family=Fira+Sans:wght@400;700;900&display=swap");
2 | @import "variables.css";
3 |
4 | @import "base.css";
5 | @import "utilities.css";
6 | @import "buttons.css";
7 | @import "components.css";
8 |
9 | @import "pages/artifacts.css";
10 | @import "pages/changelog.css";
11 | @import "pages/workspace_index.css";
12 |
13 | @import "themes/axo.css";
14 | @import "themes/hacker.css";
15 | @import "themes/cupcake.css";
16 |
--------------------------------------------------------------------------------
/oranda-css/css/pages/artifacts.css:
--------------------------------------------------------------------------------
1 | .package-managers-downloads ul {
2 | @apply my-16;
3 | }
4 |
5 | .package-managers-downloads ul li {
6 | @apply ml-0;
7 | }
8 |
9 | .package-managers-downloads pre {
10 | @apply my-0;
11 | }
12 |
13 | .artifacts {
14 | @apply sm:flex items-center flex-col mb-8 p-0 hidden;
15 | color: var(--highlight-fg-color);
16 | background-color: var(--highlight-bg-color);
17 | }
18 |
19 | .artifacts-table {
20 | @apply block max-w-full overflow-auto;
21 | }
22 |
23 | ul.tabs {
24 | @apply flex border-b-2;
25 | border-color: var(--highlight-fg-color);
26 | }
27 |
28 | ul.tabs li {
29 | @apply hover:cursor-pointer m-0 px-3 py-2 text-base;
30 | }
31 |
32 | ul.tabs li small {
33 | @apply text-xs text-gray-400 block;
34 | }
35 |
36 | ul.tabs li.selected,
37 | ul.tabs li.selected small {
38 | color: var(--highlight-bg-color);
39 | background-color: var(--highlight-fg-color);
40 | }
41 |
42 | .install-content {
43 | @apply max-w-full p-0 m-0;
44 | }
45 |
46 | .detect {
47 | @apply text-center pr-2 md:pr-0;
48 | }
49 |
50 | .detect + a {
51 | @apply block sm:inline my-2 sm:my-0;
52 | }
53 |
54 | .detect .detected-os {
55 | @apply capitalize;
56 | }
57 |
58 | .artifact-header pre {
59 | @apply my-0 mx-auto;
60 | }
61 |
62 | .artifact-header > h4 {
63 | @apply text-center -mb-2 font-bold;
64 | }
65 |
66 | .artifact-header {
67 | @apply max-w-full w-full;
68 | }
69 |
70 | .artifact-header > div:not(.install-code-wrapper) {
71 | @apply md:flex md:gap-4 justify-center text-center md:text-left items-center mt-4;
72 | }
73 |
74 | .backup-download {
75 | @apply hover:no-underline;
76 | }
77 |
78 | .bottom-options {
79 | @apply flex flex-row w-full justify-between items-center;
80 | }
81 | .bottom-options.one {
82 | @apply justify-center;
83 | }
84 |
85 | .install-code-wrapper {
86 | @apply flex items-stretch;
87 | }
88 |
89 | .install-code-wrapper > pre {
90 | @apply flex-grow flex-shrink;
91 | }
92 |
93 | .install-code-wrapper > .button {
94 | @apply w-auto rounded-l-none flex items-center hover:no-underline focus:outline-offset-[-2px];
95 | }
96 |
97 | .install-code-wrapper > .button.copy-clipboard-button {
98 | @apply rounded-none;
99 | }
100 |
101 | .download-wrapper {
102 | @apply flex flex-row justify-center;
103 | }
104 | .button .button-subtitle {
105 | @apply text-xs block;
106 | }
107 |
108 | .published-date {
109 | @apply block mb-2;
110 | }
111 |
112 | .arch {
113 | @apply p-0 m-0 pt-4;
114 | }
115 | .arch .contents {
116 | @apply pt-4;
117 | min-height: 7rem;
118 | }
119 |
120 | .mobile-download {
121 | @apply block sm:hidden mx-auto mb-12;
122 | }
123 |
--------------------------------------------------------------------------------
/oranda-css/css/pages/changelog.css:
--------------------------------------------------------------------------------
1 | .install-code-wrapper > .button svg {
2 | @apply w-6 h-6;
3 | }
4 |
5 | .release-body {
6 | margin-top: 2rem;
7 | word-break: break-word;
8 | }
9 |
10 | .release-body h1 {
11 | @apply heading-2 mt-12;
12 | }
13 | .release-body h2 {
14 | @apply heading-3 mt-12;
15 | }
16 |
17 | .release-body h3 {
18 | @apply heading-4;
19 | }
20 | .release-body h4 {
21 | @apply heading-5;
22 | }
23 | .release-body h5 {
24 | @apply heading-6;
25 | }
26 |
27 | .release-body ul,
28 | .release-body li {
29 | @apply list-disc;
30 | }
31 |
32 | .releases-nav {
33 | @apply top-12 sticky self-start w-max;
34 | }
35 |
36 | .release > h2 {
37 | @apply mt-0;
38 | }
39 |
40 | .release > h2 a {
41 | color: var(--fg-color);
42 | }
43 |
44 | .releases-list {
45 | @apply flex flex-col gap-32;
46 | }
47 |
48 | .releases-wrapper {
49 | @apply md:grid gap-12 relative mt-12;
50 | grid-template-columns: 160px minmax(0, 1fr);
51 | }
52 |
53 | .releases-nav ul {
54 | @apply hidden list-none m-0 md:flex flex-col gap-2 border-l-4 pl-4;
55 | border-color: var(--fg-color);
56 | }
57 |
58 | .releases-nav ul li {
59 | @apply m-0 relative ml-1 text-sm;
60 | }
61 |
62 | .releases-nav ul li:before {
63 | content: "";
64 | @apply h-1 w-4 block absolute top-1/2 -left-5 -translate-y-1/2;
65 | background-color: var(--fg-color);
66 | }
67 |
68 | .releases-nav ul li a {
69 | @apply decoration-transparent underline-offset-2 hover:underline;
70 | color: var(--fg-color);
71 | }
72 |
73 | .release-info {
74 | @apply flex items-center gap-8 text-base;
75 | }
76 |
77 | .prereleases-toggle {
78 | @apply hidden relative md:flex items-center mb-6 w-max;
79 | }
80 |
81 | .prereleases-toggle input {
82 | @apply h-5 w-5 rounded;
83 | color: var(--fg-color);
84 | }
85 |
86 | .prereleases-toggle label {
87 | @apply font-medium ml-3;
88 | }
89 |
90 | .release-info svg {
91 | @apply w-6 h-6;
92 | }
93 |
94 | .release-info > span {
95 | @apply flex gap-2 items-center;
96 | }
97 |
--------------------------------------------------------------------------------
/oranda-css/css/pages/workspace_index.css:
--------------------------------------------------------------------------------
1 | ul.index-grid {
2 | @apply grid lg:grid-cols-2 md:grid-cols-2 gap-8 mt-16 items-stretch;
3 | }
4 |
5 | .index-grid li {
6 | @apply ml-0 border rounded flex flex-col justify-between;
7 | border-color: var(--bg-color);
8 | box-shadow: 0px 0px 0px 8px rgba(0, 0, 0, 0.3);
9 | }
10 |
11 | .index-grid .content {
12 | @apply flex justify-between p-4;
13 | }
14 |
15 | .index-grid .links {
16 | @apply flex w-full border-t divide-x;
17 | }
18 |
19 | .index-grid .links a {
20 | @apply inline-flex items-center space-x-2 w-1/2 px-6 py-4;
21 | }
22 |
23 | .index-grid .content .index-logo {
24 | @apply relative flex-shrink-0 w-20 h-20;
25 | }
26 |
27 | .index-grid li.preferred {
28 | @apply col-span-2;
29 | }
30 |
31 | .index-about h2 {
32 | @apply mt-0;
33 | }
--------------------------------------------------------------------------------
/oranda-css/css/themes/axo.css:
--------------------------------------------------------------------------------
1 | @import url("https://fonts.googleapis.com/css2?family=Comfortaa:wght@400;700&display=swap");
2 |
3 | html.axo {
4 | /* Our theme's own colors */
5 | --highlight-color: rgb(167 139 250);
6 | --axo-orange-color: #F57070;
7 | --axo-pink-color: #FF75C3;
8 |
9 | /* Base Oranda theme variables */
10 | --light-fg-color: #141414;
11 | --light-link-color: var(--axo-pink-color);
12 | --dark-link-color: var(--axo-pink-color);
13 | --light-highlight-bg-color: var(--light-bg-color);
14 | --light-highlight-fg-color: var(--light-fg-color);
15 | --dark-highlight-bg-color: var(--light-fg-color);
16 | --dark-highlight-fg-color: var(--light-bg-color);
17 | --font-face: "Comfortaa", sans-serif;
18 | }
19 |
20 | h1,
21 | h2,
22 | h3,
23 | code {
24 | color: var(--highlight-color);
25 | }
26 |
27 | html.axo .button.primary {
28 | background-color: var(--link-color);
29 | }
30 |
31 | html.axo footer,
32 | html.axo .repo_banner {
33 | @apply axo-gradient;
34 | }
35 |
36 | html.axo h1.title {
37 | @apply axo-gradient-text;
38 | }
39 |
40 | .axo-gradient {
41 | background: -webkit-linear-gradient(
42 | left,
43 | var(--axo-orange-color),
44 | var(--axo-pink-color),
45 | var(--axo-orange-color)
46 | );
47 | background-size: 1600px 200px;
48 | animation-duration: 3s;
49 | animation-name: animation-gradient-title;
50 | animation-iteration-count: infinite;
51 | animation-fill-mode: forwards;
52 | }
53 |
54 | .text-fill-transparent {
55 | -webkit-text-fill-color: transparent;
56 | }
57 |
58 | .axo-gradient-text {
59 | @apply text-fill-transparent axo-gradient;
60 | background-clip: text;
61 | }
62 |
63 | @media (prefers-reduced-motion) {
64 | .axo-gradient {
65 | animation-duration: 0s;
66 | }
67 | }
68 |
69 | @keyframes slide-in {
70 | 0% {
71 | top: -100vh;
72 | }
73 | 100% {
74 | top: 0;
75 | }
76 | }
77 |
78 | @keyframes animation-gradient-title {
79 | 0% {
80 | background-position: 0 1600px;
81 | }
82 | 100% {
83 | background-position: 1600px 0;
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/oranda-css/css/themes/cupcake.css:
--------------------------------------------------------------------------------
1 | @import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap");
2 |
3 | html.cupcake body {
4 | --b1: #faf7f5;
5 | --b2: #dfaff7;
6 | --text: rgba(41, 19, 52, 0.8);
7 | --links: #291334;
8 | --primary: #65c3c8;
9 | --secondary: #291334;
10 | --secondary-100: #210f2a;
11 | --code: #291334;
12 |
13 | font-family: "Inter", sans-serif;
14 | background-color: var(--b1);
15 | color: var(--text);
16 | }
17 |
18 | html.cupcake ::selection {
19 | background-color: var(--b2);
20 | color: var(--code);
21 | -webkit-text-fill-color: var(--code);
22 | }
23 |
24 | html.cupcake .button.primary {
25 | background: var(--secondary);
26 | color: var(--b2);
27 | @apply hover:bg-orange-600 border-transparent no-underline;
28 | }
29 |
30 | html.cupcake .button.primary:hover {
31 | background: var(--secondary-100);
32 | }
33 |
34 | html.cupcake .button.secondary {
35 | border: 1px solid var(--secondary);
36 | color: var(--secondary-100);
37 | }
38 |
39 | html.cupcake .button.secondary:hover {
40 | background: var(--secondary);
41 | color: var(--b2);
42 | }
43 |
44 | html.cupcake h1,
45 | html.cupcake h2,
46 | html.cupcake h3,
47 | html.cupcake h4,
48 | html.cupcake h5,
49 | html.cupcake h6,
50 | html.cupcake p,
51 | html.cupcake table {
52 | color: var(--text);
53 | }
54 |
55 | html.cupcake .title {
56 | color: var(--primary);
57 | }
58 |
59 | html.cupcake a {
60 | color: var(--links);
61 | @apply font-medium underline-offset-4 underline;
62 | }
63 |
64 | html.cupcake a:hover {
65 | color: var(--code);
66 | @apply underline-offset-2;
67 | }
68 |
69 | html.cupcake .axo-gradient {
70 | background: var(--secondary);
71 | }
72 |
73 | html.cupcake .github-icon {
74 | background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='%23dfaff7' d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E");
75 | }
76 |
77 | html.cupcake .repo_banner > a,
78 | html.cupcake footer {
79 | color: var(--b2);
80 | text-decoration: none;
81 | }
82 |
83 | html.cupcake code {
84 | color: var(--code);
85 | @apply font-mono font-medium;
86 | }
87 |
88 | html.cupcake .prereleases-toggle input:checked {
89 | background-color: var(--primary);
90 | }
91 |
92 | html.cupcake .artifacts {
93 | @apply p-8;
94 | }
95 |
96 | html.cupcake .releases-nav ul li a {
97 | color: var(--links);
98 | }
99 |
100 | html.cupcake .releases-nav ul li:before {
101 | @apply bg-gray-300;
102 | }
103 |
104 | html.cupcake .releases-nav ul {
105 | @apply border-l-gray-300;
106 | }
107 |
108 | html.cupcake div.table .th {
109 | color: var(--primary);
110 | }
111 |
--------------------------------------------------------------------------------
/oranda-css/css/themes/hacker.css:
--------------------------------------------------------------------------------
1 | @import url("https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600;700&display=swap");
2 |
3 | html.hacker {
4 | --light-highlight-bg-color: var(--dark-highlight-bg-color);
5 | --light-highlight-fg-color: var(--dark-highlight-fg-color);
6 | --hacker-green: #20c20e;
7 | }
8 |
9 | html.hacker ::selection {
10 | @apply text-axo-black;
11 | background-color: #20c20e;
12 | }
13 |
14 | html.hacker body {
15 | @apply bg-axo-black text-slate-300;
16 | font-family: "IBM Plex Mono", monospace;
17 | }
18 |
19 | html.hacker .button.secondary {
20 | @apply text-slate-300 border-orange-500 hover:bg-orange-500 hover:text-axo-black;
21 | }
22 |
23 | html.hacker h2,
24 | html.hacker h3,
25 | html.hacker h4,
26 | html.hacker h5,
27 | html.hacker h6 {
28 | @apply text-violet-600;
29 | }
30 |
31 | html.hacker .repo_banner > a,
32 | html.hacker footer {
33 | color: var(--light-color);
34 | @apply py-2;
35 | }
36 |
37 | html.hacker p,
38 | html.hacker table {
39 | @apply text-slate-300;
40 | }
41 |
42 | html.hacker .title {
43 | @apply text-left relative inline-block ml-8;
44 | }
45 |
46 | @keyframes blink-animation {
47 | to {
48 | visibility: hidden;
49 | }
50 | }
51 |
52 | html.hacker .title:after {
53 | content: "";
54 | height: 70px;
55 | background: var(--hacker-green);
56 | animation: blink-animation 1s steps(5, start) infinite;
57 | @apply block absolute left-full ml-3 w-4 top-3;
58 | }
59 |
60 | html.hacker .title::before {
61 | content: "> ";
62 | @apply block text-gray-800 text-5xl absolute top-1/2 -translate-y-1/2 -left-8 mt-2;
63 | }
64 |
65 | html.hacker .title,
66 | html.hacker div.table .th,
67 | html.hacker h1 {
68 | color: var(--hacker-green);
69 | }
70 |
71 | html.hacker a {
72 | @apply text-orange-500 hover:decoration-orange-500;
73 | }
74 |
75 | html.hacker .axo-gradient {
76 | background: -webkit-linear-gradient(
77 | left,
78 | var(--hacker-green),
79 | var(--color-green-600),
80 | var(--hacker-green)
81 | );
82 | }
83 |
84 | html.hacker .nav ul {
85 | @apply justify-start;
86 | }
87 |
88 | html.hacker .button.primary {
89 | @apply text-axo-black bg-orange-500 hover:bg-orange-600 border-transparent;
90 | }
91 |
92 | html.hacker .artifact-header > h4 {
93 | @apply text-left;
94 | color: var(--light-color);
95 | }
96 |
97 | html.hacker .releases-nav ul li a {
98 | @apply text-slate-200;
99 | }
100 |
101 | html.hacker .releases-nav ul li:before {
102 | @apply bg-gray-600;
103 | }
104 |
105 | html.hacker .releases-nav ul {
106 | @apply border-l-gray-600;
107 | }
108 |
109 | html.hacker .prereleases-toggle input:checked {
110 | @apply bg-orange-500;
111 | }
112 |
113 | html.hacker .releases-nav ul li a {
114 | @apply hover:decoration-orange-500;
115 | }
116 |
117 | html.hacker .funding-wrapper {
118 | @apply items-start;
119 | }
120 |
121 | html.hacker .artifacts {
122 | @apply p-8;
123 | }
124 |
125 | html.hacker .published-date {
126 | @apply block w-full;
127 | }
128 |
129 | html.hacker .logo {
130 | @apply block m-0;
131 | }
132 |
--------------------------------------------------------------------------------
/oranda-css/css/utilities.css:
--------------------------------------------------------------------------------
1 | @tailwind utilities;
2 |
3 | .oblique {
4 | font-style: oblique;
5 | }
6 |
7 | .well-color {
8 | @apply bg-gray-900;
9 | }
10 |
11 | .oranda-hide {
12 | @apply hidden;
13 | }
14 |
15 | @media (prefers-reduced-motion) {
16 | .axo-gradient {
17 | animation-duration: 0s;
18 | }
19 | }
20 |
21 | .heading-1 {
22 | @apply text-3xl sm:text-6xl leading-tight font-black mb-8;
23 | }
24 |
25 | .heading-2 {
26 | @apply text-2xl sm:text-5xl leading-tight font-bold mb-6;
27 | }
28 |
29 | .heading-3 {
30 | @apply text-2xl sm:text-4xl leading-tight font-bold mb-4;
31 | }
32 |
33 | .heading-4 {
34 | @apply text-2xl sm:text-3xl leading-tight mb-4;
35 | }
36 | .heading-5 {
37 | @apply text-xl sm:text-2xl leading-tight text-slate-700 dark:text-slate-200 font-bold mb-4;
38 | }
39 |
40 | .heading-6 {
41 | @apply text-xl sm:text-xl leading-tight text-slate-800 dark:text-slate-300 font-bold mb-4;
42 | }
43 |
44 | .hidden {
45 | display: none;
46 | }
47 |
48 | .inline-icon > svg {
49 | height: 25px;
50 | width: 25px;
51 | display: inline-block;
52 | }
53 |
--------------------------------------------------------------------------------
/oranda-css/css/variables.css:
--------------------------------------------------------------------------------
1 | :root {
2 | /* Base colors for text and background */
3 | --dark-fg-color: #ffffff;
4 | --light-fg-color: #141414;
5 | --light-bg-color: var(--dark-fg-color);
6 | --dark-bg-color: var(--light-fg-color);
7 | --fg-color: var(--light-fg-color);
8 | --bg-color: var(--light-bg-color);
9 |
10 | /* Link colors */
11 | --light-link-color: #0284c7;
12 | --dark-link-color: #8BB9FE;
13 | --link-color: var(--light-link-color);
14 |
15 | /* Emphasis colors for text and background */
16 | --light-highlight-bg-color: #ededed;
17 | --light-highlight-fg-color: #595959;
18 | --dark-highlight-bg-color: #27272a;
19 | --dark-highlight-fg-color: #ededed;
20 | --highlight-fg-color: var(--light-highlight-fg-color);
21 | --highlight-bg-color: var(--light-highlight-bg-color);
22 | --font-face: "Fira Sans", sans-serif;
23 | }
24 |
25 | :root.dark {
26 | --fg-color: var(--dark-fg-color);
27 | --bg-color: var(--dark-bg-color);
28 | --link-color: var(--dark-link-color);
29 | --highlight-fg-color: var(--dark-highlight-fg-color);
30 | --highlight-bg-color: var(--dark-highlight-bg-color);
31 | }
--------------------------------------------------------------------------------
/oranda-css/mdbook-theme/fonts/fonts.css:
--------------------------------------------------------------------------------
1 | /* fonts used by axo themes, not sure if this is the best way to fetch them */
2 |
3 | @import url("https://fonts.googleapis.com/css2?family=Fira+Sans:wght@400;700&display=swap");
4 | @import url("https://fonts.googleapis.com/css2?family=Comfortaa:wght@400;700&display=swap");
5 | @import url("https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600;700&display=swap");
6 | @import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap");
7 |
8 | /* note that the standard mdbook themes lose their custom fonts by us overriding this */
9 |
--------------------------------------------------------------------------------
/oranda-css/mdbook-theme/oranda-themes/cupcake.css:
--------------------------------------------------------------------------------
1 | .oranda-light {
2 | /*
3 | This part is just defining constants which fringe/oranda-css should probably
4 | be injecting into this file. For now, they're hardcoded.
5 | */
6 | --color-cupcake-white: rgb(250, 247, 245);
7 | --color-cupcake-black: rgb(41, 19, 52);
8 | --color-cupcake-faded-black: rgba(41, 19, 52, 0.8);
9 | --color-cupcake-blue: rgb(101, 195, 200);
10 | --color-cupcake-rouge: rgb(245, 112, 112);
11 |
12 | /*
13 | Here we select which colors/fonts to use for this specific theme.
14 | This first block calls a lot of the shots, most other definitions just
15 | defer to these values.
16 | */
17 | --bg: var(--color-cupcake-white);
18 | --fg: var(--color-cupcake-faded-black);
19 | --well-bg: var(--bg);
20 | --well-bg-highlight: var(--color-cupcake-blue);
21 | --title-fg: var(--color-cupcake-blue);
22 | --subtitle-fg: var(--fg);
23 | --border-color: var(--color-cupcake-rouge);
24 | --main-font: Inter, sans-serif;
25 | --mono-font: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;
26 |
27 | --sidebar-bg: var(--bg);
28 | --sidebar-fg: var(--fg);
29 | --sidebar-non-existant: var(--bg);
30 | --sidebar-active: var(--color-cupcake-faded-black);
31 | --sidebar-spacer: var(--border-color);
32 |
33 | --scrollbar: default;
34 |
35 | --icons: var(--color-cupcake-black);
36 | --icons-hover: var(--color-cupcake-black);
37 |
38 | --links: var(--color-cupcake-black);
39 | --links-hover: var(--color-cupcake-black);
40 |
41 | --inline-code-color: var(--color-cupcake-black);
42 |
43 | --theme-popup-bg: var(--well-bg);
44 | --theme-popup-border: var(--border-color);
45 | --theme-hover: var(--well-bg-highlight);
46 |
47 | --quote-bg: var(--bg);
48 | --quote-border: var(--border-color);
49 |
50 | --table-border-color: var(--border-color);
51 | --table-header-bg: var(--well-bg-highlight);
52 | --table-alternate-bg: var(--well-bg);
53 |
54 | --searchbar-border-color: var(--border-color);
55 | --searchbar-bg: var(--well-bg);
56 | --searchbar-fg: var(--fg);
57 | --searchbar-shadow-color: var(--border-color);
58 | --searchresults-header-fg: var(--title-fg);
59 | --searchresults-border-color: var(--border-color);
60 | --searchresults-li-bg: var(--well-bg);
61 | --search-mark-bg: var(--well-bg-highlight);
62 | }
63 |
--------------------------------------------------------------------------------
/oranda-css/mdbook-theme/oranda-themes/hacker.css:
--------------------------------------------------------------------------------
1 | .oranda-dark {
2 | /*
3 | This part is just defining constants which fringe/oranda-css should probably
4 | be injecting into this file. For now, they're hardcoded.
5 | */
6 | --color-hacker-rouge: rgb(245, 112, 112);
7 | --color-hacker-orange: rgb(249, 115, 22);
8 | --color-hacker-green: rgb(32, 194, 14);
9 | --color-hacker-purple: rgb(124, 58, 237);
10 | --color-hacker-black: rgb(13, 13, 13);
11 | --color-hacker-white: rgb(203, 213, 225);
12 | --color-wellish: rgb(38, 50, 56);
13 | --color-wellish-light: rgb(73, 96, 108);
14 |
15 | /*
16 | Here we select which colors/fonts to use for this specific theme.
17 | This first block calls a lot of the shots, most other definitions just
18 | defer to these values.
19 | */
20 | --bg: var(--color-hacker-black);
21 | --fg: var(--color-hacker-white);
22 | --well-bg: var(--color-wellish);
23 | --well-bg-highlight: var(--color-wellish-light);
24 | --title-fg: var(--color-hacker-green);
25 | --subtitle-fg: var(--color-hacker-purple);
26 | --border-color: var(--color-hacker-orange);
27 | --main-font: IBM Plex Mono, monospace;
28 | --mono-font: IBM Plex Mono, monospace;
29 |
30 | --sidebar-bg: var(--bg);
31 | --sidebar-fg: var(--color-hacker-orange);
32 | --sidebar-non-existant: var(--bg);
33 | --sidebar-active: var(--color-hacker-purple);
34 | --sidebar-spacer: var(--border-color);
35 |
36 | --scrollbar: default;
37 |
38 | --icons: var(--color-hacker-orange);
39 | --icons-hover: var(--color-hacker-orange);
40 |
41 | --links: var(--color-hacker-orange);
42 | --links-hover: var(--color-hacker-orange);
43 |
44 | --inline-code-color: var(--color-hacker-purple);
45 |
46 | --theme-popup-bg: var(--well-bg);
47 | --theme-popup-border: var(--border-color);
48 | --theme-hover: var(--well-bg-highlight);
49 |
50 | --quote-bg: var(--bg);
51 | --quote-border: var(--border-color);
52 |
53 | --table-border-color: var(--border-color);
54 | --table-header-bg: var(--well-bg-highlight);
55 | --table-alternate-bg: var(--well-bg);
56 |
57 | --searchbar-border-color: var(--border-color);
58 | --searchbar-bg: var(--well-bg);
59 | --searchbar-fg: var(--fg);
60 | --searchbar-shadow-color: var(--border-color);
61 | --searchresults-header-fg: var(--title-fg);
62 | --searchresults-border-color: var(--border-color);
63 | --searchresults-li-bg: var(--well-bg);
64 | --search-mark-bg: var(--well-bg-highlight);
65 | }
66 |
--------------------------------------------------------------------------------
/oranda-css/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "scripts": {
4 | "build": "tailwindcss -c tailwind.config.js -i ./css/main.css -o ./dist/oranda.css --minify"
5 | },
6 | "dependencies": {
7 | "@tailwindcss/forms": "^0.5.5",
8 | "@tailwindcss/typography": "^0.5.9",
9 | "tailwindcss": "^3.3.3"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/oranda-css/tailwind.config.js:
--------------------------------------------------------------------------------
1 | const defaultTheme = require("tailwindcss/defaultTheme");
2 | const listStyleType = {
3 | circle: "circle",
4 | square: "square",
5 | "lower-roman": "lower-roman",
6 | "lower-alpha": "lower-alpha",
7 | };
8 |
9 | const maxWidth = {
10 | "prose-lg": "80ch",
11 | };
12 |
13 | const backgroundImage = {
14 | "github-logo": `url("data:image/svg+xml,%3Csvg role='img' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Ctitle%3EGitHub%3C/title%3E%3Cpath fill='#ffffff' d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E")`,
15 | "github-logo-dark": `url("data:image/svg+xml,%3Csvg role='img' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Ctitle%3EGitHub%3C/title%3E%3Cpath fill='#141414' d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E")`,
16 | };
17 | const extend = {
18 | listStyleType,
19 | maxWidth,
20 | backgroundImage,
21 | colors: {
22 | "axo-pink": "hsla(326, 100%, 73%, 1)",
23 | "axo-pink-dark": "hsla(326, 52%, 58%, 1)",
24 | "axo-orange": "hsla(0, 87%, 70%, 1)",
25 | "axo-orange-dark": "hsla(356, 75%, 64%, 1)",
26 | "axo-highlighter": "hsla(51, 100%, 50%, 1)",
27 | "axo-black": "hsla(0, 0%, 8%, 1)",
28 | "axo-light-gray": "hsla(0, 0%, 93%, 1)",
29 | "axo-dark-gray": "hsla(0, 0%, 35%, 1)"
30 | },
31 | };
32 |
33 | const extractColorVars = (themeColors, colorGroup = "") =>
34 | Object.keys(themeColors).reduce((currentVars, colorKey) => {
35 | const value = themeColors[colorKey];
36 | const newVars =
37 | typeof value === "string"
38 | ? { [`--color${colorGroup}-${colorKey}`]: value }
39 | : extractColorVars(value, `-${colorKey}`);
40 |
41 | return { ...currentVars, ...newVars };
42 | }, {});
43 |
44 | const tailwindColorsToCSSVariables = ({ addBase, theme }) =>
45 | addBase({
46 | ":root": extractColorVars(theme("colors")),
47 | });
48 |
49 | module.exports = {
50 | darkMode: "class",
51 | theme: { extend },
52 | plugins: [
53 | require("@tailwindcss/typography"),
54 | require("@tailwindcss/forms"),
55 | tailwindColorsToCSSVariables,
56 | ],
57 | };
58 |
--------------------------------------------------------------------------------
/oranda.json:
--------------------------------------------------------------------------------
1 | {
2 | "build": {
3 | "path_prefix": "oranda"
4 | },
5 | "styles": {
6 | "theme": "axodark",
7 | "favicon": "https://www.axo.dev/favicon.ico"
8 | },
9 | "marketing": {
10 | "social": {
11 | "image": "https://www.axo.dev/meta_small.jpeg",
12 | "image_alt": "axo",
13 | "twitter_account": "@axodotdev"
14 | },
15 | "analytics": {
16 | "plausible": {
17 | "domain": "opensource.axo.dev"
18 | }
19 | }
20 | },
21 | "components": {
22 | "changelog": true,
23 | "artifacts": {
24 | "package_managers": {
25 | "preferred": {
26 | "npm": "npm install @axodotdev/oranda --save-dev"
27 | },
28 | "additional": {
29 | "cargo": "cargo install oranda --locked --profile=dist",
30 | "npx": "npx @axodotdev/oranda",
31 | "binstall": "cargo binstall oranda",
32 | "nix-env": "nix-env -i oranda",
33 | "nix flake": "nix profile install github:axodotdev/oranda"
34 | }
35 | }
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/rust-toolchain.toml:
--------------------------------------------------------------------------------
1 | [toolchain]
2 | channel = "1.78"
3 | components = ["rustc", "cargo", "rust-std", "clippy", "rustfmt"]
4 |
--------------------------------------------------------------------------------
/src/commands/build.rs:
--------------------------------------------------------------------------------
1 | use camino::Utf8PathBuf;
2 | use clap::Parser;
3 |
4 | use oranda::config::Config;
5 |
6 | use oranda::errors::*;
7 | use oranda::site::Site;
8 |
9 | #[derive(Debug, Parser)]
10 | pub struct Build {
11 | /// DO NOT USE: Path to the root dir of the project
12 | ///
13 | /// This flag exists for internal testing. It is incorrectly implemented for actual
14 | /// end-users and will make you very confused and sad.
15 | #[clap(hide = true)]
16 | #[arg(long, default_value = "./")]
17 | project_root: Utf8PathBuf,
18 | /// DO NOT USE: Path to the oranda.json
19 | ///
20 | /// This flag exists for internal testing. It is incorrectly implemented for actual
21 | /// end-users and will make you very confused and sad.
22 | #[clap(hide = true)]
23 | #[arg(long, default_value = "./oranda.json")]
24 | config_path: Utf8PathBuf,
25 | /// Only build the artifacts JSON file (if applicable) and other files that may be used to
26 | /// support it, such as installer source files.
27 | #[arg(long)]
28 | json_only: bool,
29 | }
30 |
31 | impl Build {
32 | pub fn new(project_root: Option, config_path: Option) -> Self {
33 | Build {
34 | project_root: project_root.unwrap_or(Utf8PathBuf::from("./")),
35 | config_path: config_path.unwrap_or(Utf8PathBuf::from("./oranda.json")),
36 | json_only: false,
37 | }
38 | }
39 |
40 | pub fn run(&self) -> Result<()> {
41 | if let Some(config) = Site::get_workspace_config()? {
42 | let sites = Site::build_multi(&config, self.json_only)?;
43 | if config.workspace.generate_index && !self.json_only {
44 | tracing::info!("Building workspace index page...");
45 | let mut member_data = Vec::new();
46 | for site in &sites {
47 | // Unwrap here because `Site::build_multi` always sets `workspace_data = Some(_)`.
48 | // It's only set to `None` on a _single_ page build, which can't happen in this
49 | // code path.
50 | member_data.push(site.workspace_data.clone().unwrap());
51 | }
52 | Site::build_and_write_workspace_index(&config, &member_data)?;
53 | }
54 |
55 | for site in sites {
56 | site.write(None)?;
57 | }
58 | let msg = format!(
59 | "Your site builds are located in `{}`.",
60 | config.build.dist_dir
61 | );
62 | tracing::info!(success = true, "{}", &msg);
63 | } else {
64 | let config = Config::build(&self.config_path)?;
65 | if self.json_only {
66 | Site::build_single_json_only(&config, None)?;
67 | } else {
68 | Site::build_single(&config, None)?.write(Some(&config))?;
69 | }
70 | let msg = format!("Your site build is located in `{}`.", {
71 | config.build.dist_dir
72 | });
73 | tracing::info!(success = true, "{}", &msg);
74 | }
75 | Ok(())
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/commands/generate.rs:
--------------------------------------------------------------------------------
1 | use camino::Utf8PathBuf;
2 | use clap::{Parser, Subcommand, ValueEnum};
3 | use oranda::errors::Result;
4 |
5 | #[derive(Debug, Subcommand)]
6 | pub enum GenerateCommand {
7 | /// Generates a CI file that can be used to deploy your site.
8 | Ci(Ci),
9 | }
10 |
11 | #[derive(Debug, Parser)]
12 | pub struct Ci {
13 | /// What CI to generate a file for.
14 | #[arg(long, default_value_t = CiType::Github)]
15 | #[clap(value_enum)]
16 | ci: CiType,
17 | }
18 |
19 | #[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, ValueEnum)]
20 | pub enum CiType {
21 | /// Deploy to GitHub Pages using GitHub Actions
22 | Github,
23 | }
24 |
25 | #[derive(Debug, Parser)]
26 | pub struct Generate {
27 | /// What type of thing to generate.
28 | #[command(subcommand)]
29 | kind: GenerateCommand,
30 | /// Path to the output file.
31 | #[arg(short, long)]
32 | #[clap(global = true)]
33 | output_path: Option,
34 | /// Path to your oranda site
35 | #[arg(short, long)]
36 | #[clap(global = true)]
37 | site_path: Option,
38 | }
39 |
40 | impl Generate {
41 | pub fn run(&self) -> Result<()> {
42 | let path = self
43 | .output_path
44 | .clone()
45 | .unwrap_or_else(|| self.default_path());
46 | match self.kind {
47 | // TODO: Pass `CiType` in here when we add another one.
48 | GenerateCommand::Ci(_) => oranda::generate::generate_ci(path, &self.site_path)?,
49 | };
50 | Ok(())
51 | }
52 |
53 | fn default_path(&self) -> Utf8PathBuf {
54 | match self.kind {
55 | // TODO: Match on `CiType` when we add another one.
56 | GenerateCommand::Ci(_) => Utf8PathBuf::from(".github/workflows/web.yml"),
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/commands/mod.rs:
--------------------------------------------------------------------------------
1 | mod build;
2 | mod dev;
3 | mod generate;
4 | mod print;
5 | mod serve;
6 |
7 | pub use build::Build;
8 | pub use dev::Dev;
9 | pub use generate::Generate;
10 | pub use print::ConfigSchema;
11 | pub use print::GenerateCss;
12 | pub use serve::Serve;
13 |
--------------------------------------------------------------------------------
/src/commands/print.rs:
--------------------------------------------------------------------------------
1 | use axoasset::LocalAsset;
2 | use camino::Utf8PathBuf;
3 | use clap::Parser;
4 | use oranda::errors::*;
5 | use oranda_generate_css::default_css_output_dir;
6 |
7 | #[derive(Debug, Parser)]
8 | pub struct ConfigSchema {
9 | /// Write the config schema to the named file instead of stdout
10 | #[clap(long)]
11 | pub output: Option,
12 | }
13 |
14 | impl ConfigSchema {
15 | pub fn run(&self) -> Result<()> {
16 | let schema = schemars::schema_for!(oranda::config::OrandaLayer);
17 | let json_schema =
18 | serde_json::to_string_pretty(&schema).expect("failed to stringify schema!?");
19 |
20 | if let Some(output) = &self.output {
21 | let contents = json_schema + "\n";
22 | LocalAsset::write_new(&contents, output)?;
23 | } else {
24 | println!("{json_schema}");
25 | }
26 | Ok(())
27 | }
28 | }
29 |
30 | #[derive(Debug, Parser)]
31 | pub struct GenerateCss {
32 | #[clap(long)]
33 | out_dir: Option,
34 | }
35 |
36 | impl GenerateCss {
37 | pub fn run(&self) -> Result<()> {
38 | let out_dir = self.out_dir.clone().unwrap_or_else(default_css_output_dir);
39 | let out_file = out_dir.join("oranda.css");
40 | oranda_generate_css::build_css(&out_dir)?;
41 | tracing::info!("CSS placed in {out_file}");
42 | Ok(())
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/commands/serve.rs:
--------------------------------------------------------------------------------
1 | use camino::{Utf8Path, Utf8PathBuf};
2 | use std::net::SocketAddr;
3 | use std::sync::mpsc::Receiver;
4 | use std::thread;
5 |
6 | use oranda::config::Config;
7 | use oranda::errors::*;
8 |
9 | use axum::{http::StatusCode, routing::get_service, Router};
10 |
11 | use clap::Parser;
12 | use tower_http::services::ServeDir;
13 | use tower_livereload::LiveReloadLayer;
14 |
15 | #[derive(Debug, Default, Parser)]
16 | pub struct Serve {
17 | #[arg(long, default_value = "7979")]
18 | port: u16,
19 | }
20 |
21 | impl Serve {
22 | pub fn new(port: Option) -> Self {
23 | Serve {
24 | port: port.unwrap_or(7979),
25 | }
26 | }
27 |
28 | pub fn run(&self) -> Result<()> {
29 | let config = Self::build_config()?;
30 | if Utf8Path::new(&config.build.dist_dir).is_dir() {
31 | self.serve(&config.build.dist_dir, &config.build.path_prefix, None)?;
32 | Ok(())
33 | } else {
34 | Err(OrandaError::BuildNotFound {
35 | dist_dir: config.build.dist_dir.to_string(),
36 | })
37 | }
38 | }
39 |
40 | pub fn run_with_livereload(&self, rx: Receiver<()>) -> Result<()> {
41 | let config = Self::build_config()?;
42 | if Utf8Path::new(&config.build.dist_dir).is_dir() {
43 | let livereload = LiveReloadLayer::new();
44 | self.serve(
45 | &config.build.dist_dir,
46 | &config.build.path_prefix,
47 | Some((livereload, rx)),
48 | )?;
49 |
50 | Ok(())
51 | } else {
52 | Err(OrandaError::BuildNotFound {
53 | dist_dir: config.build.dist_dir.to_string(),
54 | })
55 | }
56 | }
57 |
58 | #[tokio::main]
59 | async fn serve(
60 | &self,
61 | dist_dir: &str,
62 | path_prefix: &Option,
63 | livereload: Option<(LiveReloadLayer, Receiver<()>)>,
64 | ) -> Result<()> {
65 | let serve_dir =
66 | get_service(ServeDir::new(dist_dir)).handle_error(|error: std::io::Error| async move {
67 | (
68 | StatusCode::INTERNAL_SERVER_ERROR,
69 | format!("Unhandled internal error: {}", error),
70 | )
71 | });
72 |
73 | let prefix_route = if let Some(prefix) = path_prefix {
74 | format!("/{}", prefix)
75 | } else {
76 | "/".to_string()
77 | };
78 | let mut app = Router::new().nest_service(&prefix_route, serve_dir);
79 | if let Some(livereload) = livereload {
80 | let (livereload, rx) = livereload;
81 | let reloader = livereload.reloader();
82 | app = app.layer(livereload);
83 |
84 | // Because the server will later block this thread, spawn another thread to handle
85 | // reload request messages.
86 | thread::spawn(move || loop {
87 | rx.recv().expect("broken pipe");
88 | reloader.reload();
89 | });
90 | }
91 |
92 | let addr = SocketAddr::from(([127, 0, 0, 1], self.port));
93 | let msg = format!(
94 | "Your project is available at: http://{}/{}",
95 | addr,
96 | path_prefix.as_ref().unwrap_or(&String::new())
97 | );
98 | tracing::info!(success = true, "{}", &msg);
99 | axum::Server::bind(&addr)
100 | .serve(app.into_make_service())
101 | .await
102 | .expect("failed to start server");
103 | Ok(())
104 | }
105 |
106 | fn build_config() -> Result {
107 | let workspace_config_path = &Utf8PathBuf::from("./oranda-workspace.json");
108 | if workspace_config_path.exists() {
109 | Config::build(workspace_config_path)
110 | } else {
111 | Config::build(&Utf8PathBuf::from("./oranda.json"))
112 | }
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/src/config/builds.rs:
--------------------------------------------------------------------------------
1 | use indexmap::IndexMap;
2 | use schemars::JsonSchema;
3 | use serde::{Deserialize, Serialize};
4 |
5 | use super::{ApplyLayer, ApplyOptExt, ApplyValExt};
6 |
7 | #[derive(Debug, Clone)]
8 | /// Information about how the pages should be built (complete version)
9 | pub struct BuildConfig {
10 | /// Relative path to the dir where build output should be placed
11 | pub dist_dir: String,
12 | /// Relative path to a dir full of extra static content
13 | pub static_dir: String,
14 | /// A path fragment to prepend before URLs
15 | ///
16 | /// This allows things like hosting a static site at `axodotdev.github.io/my_project/`
17 | pub path_prefix: Option,
18 | /// Additional pages that should be included in the top level nav.
19 | ///
20 | /// This is a map from page-label to relative paths to pages.
21 | ///
22 | /// We use IndexMap to respect the order the user provided.
23 | pub additional_pages: IndexMap,
24 | }
25 | #[derive(Debug, Serialize, Deserialize, JsonSchema)]
26 | #[serde(deny_unknown_fields)]
27 | /// Information about how the pages of your site should be built
28 | pub struct BuildLayer {
29 | /// Relative path to the dir where build output should be placed
30 | ///
31 | /// This is "./public/" by default
32 | pub dist_dir: Option,
33 | /// Relative path to a dir full of extra static content that should be included in your site
34 | ///
35 | /// (FIXME: explain what paths it ends up at)
36 | ///
37 | /// This is "./static/" by default
38 | pub static_dir: Option,
39 | /// A path fragment to prepend before URLs
40 | ///
41 | /// This allows things like hosting a static site at `axodotdev.github.io/my_project/`
42 | /// (you would set path_prefix = "my_project" for that).
43 | pub path_prefix: Option,
44 | /// Additional pages that should be included in the top level nav.
45 | ///
46 | /// This is a map from page-label to relative paths to (Github Flavored) Markdown files
47 | /// that should be rendered into pages.
48 | ///
49 | /// These pages will be listed in the given order after "home" and before
50 | /// other pages that oranda automatically adds like "install" and "funding".
51 | pub additional_pages: Option>,
52 | }
53 |
54 | impl Default for BuildConfig {
55 | fn default() -> Self {
56 | BuildConfig {
57 | dist_dir: "public".to_owned(),
58 | static_dir: "static".to_owned(),
59 | path_prefix: None,
60 | additional_pages: Default::default(),
61 | }
62 | }
63 | }
64 | impl ApplyLayer for BuildConfig {
65 | type Layer = BuildLayer;
66 | fn apply_layer(&mut self, layer: Self::Layer) {
67 | // This is intentionally written slightly cumbersome to make you update this
68 | let BuildLayer {
69 | dist_dir,
70 | static_dir,
71 | path_prefix,
72 | additional_pages,
73 | } = layer;
74 | self.dist_dir.apply_val(dist_dir);
75 | self.static_dir.apply_val(static_dir);
76 | self.path_prefix.apply_opt(path_prefix);
77 | // In the future this might want to be `extend`
78 | self.additional_pages.apply_val(additional_pages);
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/config/components/artifacts/package_managers.rs:
--------------------------------------------------------------------------------
1 | use indexmap::IndexMap;
2 | use schemars::JsonSchema;
3 | use serde::{Deserialize, Serialize};
4 |
5 | use crate::config::{ApplyLayer, ApplyValExt};
6 |
7 | /// Package managers to display (complete version)
8 | #[derive(Debug, Clone)]
9 | pub struct PackageManagersConfig {
10 | pub preferred: IndexMap,
11 | pub additional: IndexMap,
12 | }
13 | /// Package managers to display
14 | #[derive(Debug, Serialize, Deserialize, JsonSchema)]
15 | #[serde(deny_unknown_fields)]
16 | pub struct PackageManagersLayer {
17 | /// Packages to display in both the install widget and install page
18 | ///
19 | /// See docs for the parent "package_managers" field for details
20 | pub preferred: Option>,
21 | /// Packages to display in just the install page
22 | ///
23 | /// See docs for the parent "package_managers" field for details
24 | pub additional: Option>,
25 | }
26 |
27 | impl Default for PackageManagersConfig {
28 | fn default() -> Self {
29 | PackageManagersConfig {
30 | preferred: IndexMap::default(),
31 | additional: IndexMap::default(),
32 | }
33 | }
34 | }
35 | impl ApplyLayer for PackageManagersConfig {
36 | type Layer = PackageManagersLayer;
37 | fn apply_layer(&mut self, layer: Self::Layer) {
38 | // This is intentionally written slightly cumbersome to make you update this
39 | let PackageManagersLayer {
40 | preferred,
41 | additional,
42 | } = layer;
43 | // In the future these might want to be `extend`
44 | self.preferred.apply_val(preferred);
45 | self.additional.apply_val(additional);
46 | }
47 | }
48 |
49 | impl PackageManagersConfig {
50 | pub fn has(&self, key: &str) -> bool {
51 | self.preferred.contains_key(key) || self.additional.contains_key(key)
52 | }
53 | pub fn has_npm(&self) -> bool {
54 | self.has("npm") || self.has("npx")
55 | }
56 | pub fn is_empty(&self) -> bool {
57 | self.preferred.is_empty() && self.additional.is_empty()
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/config/components/changelog.rs:
--------------------------------------------------------------------------------
1 | use crate::config::{ApplyLayer, ApplyValExt};
2 | use schemars::JsonSchema;
3 | use serde::{Deserialize, Serialize};
4 |
5 | /// Config for tweaking the changelog page generation
6 | #[derive(Debug, Clone)]
7 | pub struct ChangelogConfig {
8 | /// Whether to attempt to read from the local changelog file
9 | pub read_changelog_file: bool,
10 | /// Whether to generate a RSS file
11 | pub rss_feed: bool,
12 | }
13 |
14 | /// The config for generating a separate changelog page
15 | #[derive(Debug, Default, Serialize, Deserialize, JsonSchema)]
16 | pub struct ChangelogLayer {
17 | /// Whether we factor in the local `CHANGELOG.md` file, attempt to parse
18 | /// it, and try and match version headings to release versions that we
19 | /// detect.
20 | pub read_changelog_file: Option,
21 | /// Whether to generate a RSS file under `changelog.rss`.
22 | pub rss_feed: Option,
23 | }
24 |
25 | impl Default for ChangelogConfig {
26 | fn default() -> Self {
27 | ChangelogConfig {
28 | read_changelog_file: true,
29 | rss_feed: true,
30 | }
31 | }
32 | }
33 |
34 | impl ApplyLayer for ChangelogConfig {
35 | type Layer = ChangelogLayer;
36 | fn apply_layer(&mut self, layer: Self::Layer) {
37 | // This is intentionally written slightly cumbersome to make you update this
38 | let ChangelogLayer {
39 | read_changelog_file,
40 | rss_feed,
41 | } = layer;
42 | self.read_changelog_file.apply_val(read_changelog_file);
43 | self.rss_feed.apply_val(rss_feed);
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/config/components/funding.rs:
--------------------------------------------------------------------------------
1 | use camino::Utf8PathBuf;
2 | use schemars::JsonSchema;
3 | use serde::{Deserialize, Serialize};
4 | use std::path::Path;
5 |
6 | use crate::config::{ApplyLayer, ApplyOptExt};
7 | use crate::data::funding::FundingType;
8 | use crate::errors::*;
9 |
10 | /// Config for displaying funding information on your page (complete version)
11 | #[derive(Debug, Clone)]
12 | pub struct FundingConfig {
13 | pub preferred_funding: Option,
14 | pub yml_path: Option,
15 | pub md_path: Option,
16 | }
17 | /// Settings for displaying funding information on your page
18 | #[derive(Debug, Serialize, Deserialize, JsonSchema)]
19 | #[serde(deny_unknown_fields)]
20 | pub struct FundingLayer {
21 | /// A funding method to make larger/focused to encourage over all others
22 | pub preferred_funding: Option,
23 | /// A path to a github-format FUNDING.yml file
24 | ///
25 | /// We parse this out to get a list of funding sources.
26 | ///
27 | /// By default we try to find this at "./.github/FUNDING.yml"
28 | pub yml_path: Option,
29 | /// A relative path to a freeform github-flavor markdown file
30 | /// whose contents will be included on your funding page.
31 | ///
32 | /// By default we try to find this at "./funding.md"
33 | pub md_path: Option,
34 | }
35 |
36 | impl Default for FundingConfig {
37 | fn default() -> Self {
38 | FundingConfig {
39 | preferred_funding: None,
40 | yml_path: None,
41 | md_path: None,
42 | }
43 | }
44 | }
45 | impl ApplyLayer for FundingConfig {
46 | type Layer = FundingLayer;
47 | fn apply_layer(&mut self, layer: Self::Layer) {
48 | // This is intentionally written slightly cumbersome to make you update this
49 | let FundingLayer {
50 | preferred_funding,
51 | yml_path,
52 | md_path,
53 | } = layer;
54 | self.preferred_funding.apply_opt(preferred_funding);
55 | self.yml_path.apply_opt(yml_path);
56 | self.md_path.apply_opt(md_path);
57 | }
58 | }
59 |
60 | impl FundingConfig {
61 | /// If we have a FUNDING.yml file, try to find it. If we fail, we disable funding support.
62 | pub fn find_paths(config: &mut Option, start_dir: &Path) -> Result<()> {
63 | // If this is None, we were force-disabled and shouldn't auto-detect
64 | let Some(this) = config else { return Ok(()) };
65 |
66 | // Try to auto-detect the FUNDING.yml if not specified
67 | if this.yml_path.is_none() {
68 | let default_yml_path =
69 | Utf8PathBuf::from(format!("{}/.github/FUNDING.yml", start_dir.display()));
70 | if default_yml_path.exists() {
71 | this.yml_path = Some(default_yml_path.to_string());
72 | }
73 | }
74 | // Try to auto-detect funding.md if not specified
75 | if this.md_path.is_none() {
76 | let default_md_path = Utf8PathBuf::from(format!("{}/funding.md", start_dir.display()));
77 | if default_md_path.exists() {
78 | this.md_path = Some(default_md_path.to_string());
79 | }
80 | }
81 |
82 | // This is intentionally written slightly cumbersome to make you update this
83 | let FundingConfig {
84 | preferred_funding,
85 | yml_path,
86 | md_path,
87 | } = this;
88 | let cant_find_files = yml_path.is_none() && md_path.is_none();
89 | let has_user_config = preferred_funding.is_some();
90 | if cant_find_files {
91 | // The config is unusable.
92 | //
93 | // * If the user customized stuff, error out because they clearly wanted this to work
94 | // * Otherwise, just disable the feature
95 | if has_user_config {
96 | return Err(OrandaError::FundingConfigInvalid);
97 | } else {
98 | *config = None;
99 | }
100 | }
101 | Ok(())
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/src/config/components/mdbooks.rs:
--------------------------------------------------------------------------------
1 | use schemars::JsonSchema;
2 | use serde::{Deserialize, Serialize};
3 | use std::path::Path;
4 |
5 | use crate::config::{ApplyLayer, ApplyOptExt, ApplyValExt};
6 | use crate::errors::*;
7 |
8 | /// Config for us building and integrating your mdbook (complete version)
9 | #[derive(Debug, Clone)]
10 | pub struct MdBookConfig {
11 | /// Path to the mdbook
12 | ///
13 | /// If not set we will attempt to auto-detect
14 | pub path: Option,
15 | /// Whether to enable the custom oranda/axo theme
16 | pub theme: bool,
17 | }
18 |
19 | /// The config for building and embedding an mdbook on your site
20 | #[derive(Debug, Default, Serialize, Deserialize, JsonSchema)]
21 | #[serde(deny_unknown_fields)]
22 | pub struct MdBookLayer {
23 | /// Path to the mdbook (the directory containing book.toml)
24 | ///
25 | /// If not set we will attempt to auto-detect this by trying
26 | /// "./", "./book/", and "./docs/".
27 | pub path: Option,
28 | /// Whether to enable oranda's customized mdbook theme that unifies
29 | /// with your oranda theme.
30 | ///
31 | /// If enabled we will use mdbook's custom themeing system to overwrite
32 | /// most of the hbs/css/js file mdbook defines to add hooks for our
33 | /// custom themes to be enabled and defaulted on. The existing mdbook
34 | /// themes will still be available and should work normally.
35 | ///
36 | /// Unfortunately this means that `mdbook build` won't produce the same
37 | /// results as `oranda build`. In the future we may introduce a way
38 | /// to "vendor" the changes oranda makes so that `mdbook build` behaves
39 | /// the same. This should be possible because we mostly use officially
40 | /// supported mdbook settings when changing the theme (the only exception
41 | /// being we add an extra css file for our theme's syntax highlighter).
42 | ///
43 | /// Any other mdbook settings should ideally be preserved/respected.
44 | ///
45 | /// If the theme has a paired dark/light variant, that variant will
46 | /// also be made available, although we won't respect mdbook's builtin
47 | /// preferred dark-mode setting, to ensure the rest of your oranda site
48 | /// always looks the same (this may be improved when the rest of oranda
49 | /// gets richer support for light/dark-mode).
50 | ///
51 | /// defaults to true
52 | pub theme: Option,
53 | }
54 |
55 | impl Default for MdBookConfig {
56 | fn default() -> Self {
57 | MdBookConfig {
58 | path: None,
59 | theme: true,
60 | }
61 | }
62 | }
63 | impl ApplyLayer for MdBookConfig {
64 | type Layer = MdBookLayer;
65 | fn apply_layer(&mut self, layer: Self::Layer) {
66 | // This is intentionally written slightly cumbersome to make you update this
67 | let MdBookLayer { path, theme } = layer;
68 | self.path.apply_opt(path);
69 | self.theme.apply_val(theme);
70 | }
71 | }
72 |
73 | impl MdBookConfig {
74 | /// If mdbook is enabled but the path isn't set, we try to find it
75 | ///
76 | /// If we fail, we set mdbook to None to disable it.
77 | pub fn find_paths(config: &mut Option, start_dir: &Path) -> Result<()> {
78 | // If this is None, we were force-disabled and shouldn't auto-detect
79 | let Some(this) = config else {
80 | return Ok(());
81 | };
82 |
83 | if this.path.is_none() {
84 | // Ok time to auto-detect, try these dirs for a book.toml
85 | let possible_paths = vec!["./", "./book/", "./docs/"];
86 | for book_dir in possible_paths {
87 | let book_path = start_dir.join(book_dir).join("book.toml");
88 | if book_path.exists() {
89 | // nice, use it
90 | this.path = Some(book_dir.to_owned());
91 | return Ok(());
92 | }
93 | }
94 | }
95 |
96 | // This is intentionally written slightly cumbersome to make you update this
97 | let MdBookConfig { path, theme } = this;
98 | let cant_find_files = path.is_none();
99 | let has_user_config = *theme != MdBookConfig::default().theme;
100 | if cant_find_files {
101 | // The config is unusable.
102 | //
103 | // * If the user customized stuff, error out because they clearly wanted this to work
104 | // * Otherwise, just disable the feature
105 | if has_user_config {
106 | return Err(OrandaError::MdBookConfigInvalid);
107 | } else {
108 | *config = None;
109 | }
110 | }
111 | Ok(())
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/src/config/marketing/analytics.rs:
--------------------------------------------------------------------------------
1 | use schemars::JsonSchema;
2 | use serde::{Deserialize, Serialize};
3 |
4 | use crate::site::layout::javascript::analytics::{Fathom, Google, Plausible, Umami};
5 |
6 | /// Settings for Analytics
7 | ///
8 | /// Analytics providers are currently mututally exclusive -- you can pick at most one.
9 | #[derive(Debug, Serialize, Deserialize, JsonSchema, Clone)]
10 | #[serde(rename_all = "lowercase")]
11 | pub enum AnalyticsConfig {
12 | /// Use Google Analytics
13 | Google(Google),
14 | /// Use Plausible Analytics
15 | Plausible(Plausible),
16 | /// Use Fathom Analytics
17 | Fathom(Fathom),
18 | /// Use Umami Analytics
19 | Umami(Umami),
20 | }
21 |
--------------------------------------------------------------------------------
/src/config/marketing/mod.rs:
--------------------------------------------------------------------------------
1 | pub use analytics::AnalyticsConfig;
2 | use schemars::JsonSchema;
3 | use serde::{Deserialize, Serialize};
4 | pub use social::{SocialConfig, SocialLayer};
5 |
6 | use super::ApplyLayer;
7 |
8 | mod analytics;
9 | mod social;
10 |
11 | /// Marketing config (complete version)
12 | #[derive(Debug, Clone)]
13 | pub struct MarketingConfig {
14 | /// Analytics
15 | pub analytics: Option,
16 | /// Social media
17 | pub social: SocialConfig,
18 | }
19 | /// Settings for marketing/social/analytics
20 | #[derive(Debug, Serialize, Deserialize, JsonSchema)]
21 | #[serde(deny_unknown_fields)]
22 | pub struct MarketingLayer {
23 | /// Settings for analytics
24 | ///
25 | /// Analytics providers are currently mututally exclusive -- you can pick at most one.
26 | pub analytics: Option,
27 | /// Settings for social media integrations
28 | pub social: Option,
29 | }
30 |
31 | impl Default for MarketingConfig {
32 | fn default() -> Self {
33 | MarketingConfig {
34 | analytics: None,
35 | social: SocialConfig::default(),
36 | }
37 | }
38 | }
39 | impl ApplyLayer for MarketingConfig {
40 | type Layer = MarketingLayer;
41 | fn apply_layer(&mut self, layer: Self::Layer) {
42 | // This is intentionally written slightly cumbersome to make you update this
43 | let MarketingLayer { analytics, social } = layer;
44 |
45 | // FIXME: this is kinda goofy but there's not an obvious thing to do
46 | // if we need to change the enum variant and we care about preserving things.
47 | // So we just clobber the old value no matter what
48 | if let Some(analytics) = analytics {
49 | self.analytics = Some(analytics);
50 | }
51 | self.social.apply_val_layer(social);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/config/marketing/social.rs:
--------------------------------------------------------------------------------
1 | use schemars::JsonSchema;
2 | use serde::{Deserialize, Serialize};
3 |
4 | use crate::config::{ApplyLayer, ApplyOptExt};
5 |
6 | // Social media config (complete version)
7 | #[derive(Debug, Serialize, Clone)]
8 | pub struct SocialConfig {
9 | pub image: Option,
10 | pub image_alt: Option,
11 | pub twitter_account: Option,
12 | }
13 | // Settings for social media integrations
14 | #[derive(Debug, Serialize, Deserialize, JsonSchema)]
15 | #[serde(deny_unknown_fields)]
16 | pub struct SocialLayer {
17 | /// Image to show in link previews
18 | ///
19 | /// FIXME: what format?
20 | pub image: Option,
21 | /// Alt image to show in link previews
22 | ///
23 | /// FIXME: explain the distinction with "image"
24 | ///
25 | /// FIXME: what format?
26 | pub image_alt: Option,
27 | /// Twitter account to show in link previews
28 | ///
29 | /// Example: "@axodotdev"
30 | pub twitter_account: Option,
31 | }
32 |
33 | impl Default for SocialConfig {
34 | fn default() -> Self {
35 | SocialConfig {
36 | image: None,
37 | image_alt: None,
38 | twitter_account: None,
39 | }
40 | }
41 | }
42 | impl ApplyLayer for SocialConfig {
43 | type Layer = SocialLayer;
44 | fn apply_layer(&mut self, layer: Self::Layer) {
45 | // This is intentionally written slightly cumbersome to make you update this
46 | let SocialLayer {
47 | image,
48 | image_alt,
49 | twitter_account,
50 | } = layer;
51 | self.image.apply_opt(image);
52 | self.image_alt.apply_opt(image_alt);
53 | self.twitter_account.apply_opt(twitter_account);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/config/oranda_config.rs:
--------------------------------------------------------------------------------
1 | use axoasset::SourceFile;
2 | use camino::Utf8PathBuf;
3 | use schemars::JsonSchema;
4 | use serde::{Deserialize, Serialize};
5 |
6 | use crate::errors::*;
7 |
8 | use super::{BuildLayer, ComponentLayer, MarketingLayer, ProjectLayer, StyleLayer, WorkspaceLayer};
9 |
10 | /// Configuration for `oranda` (typically stored in oranda.json)
11 | #[derive(Debug, Serialize, Deserialize, JsonSchema)]
12 | #[serde(deny_unknown_fields)]
13 | pub struct OrandaLayer {
14 | /// Info about the project/application you're making a site for
15 | ///
16 | /// All of these values should automatically be sourced from your Cargo.toml or package.json
17 | /// whenever possible. You should only need to set these if you want to override the value.
18 | pub project: Option,
19 | /// Settings for the build/output of the site
20 | pub build: Option,
21 | /// Settings for social/marketing/analytics
22 | pub marketing: Option,
23 | /// Settings for themes/styles of the site
24 | pub styles: Option,
25 | /// Additional optional components
26 | pub components: Option,
27 | /// Workspace configuration
28 | pub workspace: Option,
29 | /// Field that text-editors can use to fetch the schema for this struct
30 | ///
31 | /// We never use this, but we don't want to error out if its set.
32 | #[serde(rename = "$schema")]
33 | pub _schema: Option,
34 | }
35 |
36 | impl OrandaLayer {
37 | pub fn load(config_path: &Utf8PathBuf) -> Result